From 78ed81a334dab56aa7a876792a473d67d4359c25 Mon Sep 17 00:00:00 2001 From: cvs2svn Date: Thu, 28 Oct 1993 02:38:37 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.0'. Sprout from unlabeled-1.1.1 1993-07-31 01:10:24 UTC Andrew Moore 'adding GNU dc ("desk calculator")' Cherrypick from master 1993-10-25 21:09:32 UTC Rod Grimes 'It makes little since to rebuild the whatis database before ALL of the': CONTRIB.386BSD CONTRIB.FreeBSD FixLinks Makefile RELNOTES.FreeBSD bin/Makefile bin/cat/cat.c bin/chmod/chmod.c bin/cp/cp.c bin/cp/extern.h bin/cp/path.c bin/csh/Makefile bin/csh/csh.1 bin/csh/dir.c bin/date/date.1 bin/date/date.c bin/dd/Makefile bin/dd/args.c bin/dd/conv.c bin/dd/conv_tab.c bin/dd/dd.1 bin/dd/dd.c bin/dd/dd.h bin/dd/extern.h bin/dd/misc.c bin/dd/position.c bin/df/Makefile bin/df/df.1 bin/df/df.c bin/df/getbsize.c bin/domainname/Makefile bin/domainname/domainname.1 bin/domainname/domainname.c bin/ed/Makefile bin/ed/POSIX bin/ed/README bin/ed/buf.c bin/ed/ed.1 bin/ed/ed.c bin/ed/ed.h bin/ed/re.c bin/ed/test/Makefile bin/ed/test/README bin/ed/test/bang1.d bin/ed/test/bang1.err bin/ed/test/bang1.r bin/ed/test/bang1.t bin/ed/test/bang2.d bin/ed/test/bang2.err bin/ed/test/bang2.r bin/ed/test/bang2.t bin/ed/test/g3.d bin/ed/test/g3.r bin/ed/test/g3.t bin/ed/test/g4.d bin/ed/test/g4.r bin/ed/test/g4.t bin/ed/test/nl.err bin/ed/test/nl1.d bin/ed/test/nl1.r bin/ed/test/nl1.t bin/ed/test/nl2.d bin/ed/test/nl2.r bin/ed/test/nl2.t bin/expr/Makefile bin/expr/expr.1 bin/expr/expr.y bin/kill/kill.1 bin/kill/kill.c bin/ls/cmp.c bin/ls/ls.1 bin/ls/ls.c bin/ls/ls.h bin/ls/print.c bin/ls/util.c bin/mkdir/mkdir.1 bin/mkdir/mkdir.c bin/ps/devname.c bin/ps/keyword.c bin/ps/ps.1 bin/ps/ps.c bin/pwd/pwd.1 bin/rcp/Makefile bin/rm/rm.c bin/rmail/Makefile bin/rmail/rmail.8 bin/rmdir/rmdir.1 bin/rmdir/rmdir.c bin/sh/Makefile bin/sh/TOUR bin/sh/b.c bin/sh/bltin/bltin.h bin/sh/bltin/echo.1 bin/sh/bltin/echo.c bin/sh/builtins bin/sh/cd.c bin/sh/dirent.c bin/sh/errmsg.c bin/sh/errmsg.h bin/sh/error.c bin/sh/error.h bin/sh/eval.c bin/sh/eval.h bin/sh/exec.c bin/sh/exec.h bin/sh/expand.c bin/sh/expand.h bin/sh/funcs/cmv bin/sh/funcs/dirs bin/sh/funcs/kill bin/sh/funcs/login bin/sh/funcs/newgrp bin/sh/funcs/popd bin/sh/funcs/pushd bin/sh/funcs/suspend bin/sh/init.h bin/sh/input.c bin/sh/input.h bin/sh/jobs.c bin/sh/jobs.h bin/sh/machdep.h bin/sh/mail.c bin/sh/mail.h bin/sh/main.c bin/sh/main.h bin/sh/memalloc.c bin/sh/memalloc.h bin/sh/miscbltin.c bin/sh/mkbuiltins bin/sh/mkinit.c bin/sh/mknodes.c bin/sh/mksignames.c bin/sh/mksyntax.c bin/sh/mktokens bin/sh/mt bin/sh/mystring.c bin/sh/mystring.h bin/sh/nodes.c.pat bin/sh/nodetypes bin/sh/options.c bin/sh/options.h bin/sh/output.c bin/sh/output.h bin/sh/parser.c bin/sh/parser.h bin/sh/redir.c bin/sh/redir.h bin/sh/sh.1 bin/sh/shell.h bin/sh/show.c bin/sh/trap.c bin/sh/trap.h bin/sh/var.c bin/sh/var.h bin/stty/key.c bin/stty/stty.c bin/sync/Makefile bin/test/Makefile bin/test/TEST.csh bin/test/binary_op bin/test/mkops bin/test/operators.c bin/test/operators.h bin/test/test.1 bin/test/test.c bin/test/unary_op contrib/Makefile contrib/Makefile.inc contrib/tcpdump/Makefile.inc contrib/tcpdump/tcpdump/Makefile contrib/tcpdump/tcpdump/interface.h contrib/tcpdump/tcpdump/print-nfs.c contrib/tcpdump/tcpdump/print-sunrpc.c contrib/tcpdump/tcpdump/print-udp.c contrib/tcpdump/tcpslice/Makefile etc/Makefile etc/aliases etc/crontab etc/csh.login etc/daily etc/etc.i386/MAKEDEV etc/etc.i386/disktab etc/etc.i386/floppy.install_notes etc/etc.i386/inst1.install etc/etc.i386/inst1.profile etc/etc.i386/inst2.install etc/etc.i386/inst2.profile etc/etc.i386/install_notes etc/etc.i386/kc.profile etc/gettytab etc/group etc/host.conf etc/inetd.conf etc/master.passwd etc/monthly etc/motd etc/mtree/BSD.local.dist etc/mtree/BSD.root.dist etc/mtree/BSD.usr.dist etc/mtree/BSD.var.dist etc/myname etc/netstart etc/phones etc/printcap etc/rc etc/rc.local etc/remote etc/root/dot.cshrc etc/root/dot.login etc/root/dot.profile etc/rpc etc/security etc/services etc/ttys etc/weekly games/Makefile games/arithmetic/Makefile games/atc/Makefile games/backgammon/backgammon/Makefile games/banner/Makefile games/battlestar/Makefile games/bcd/Makefile games/caesar/Makefile games/canfield/canfield/Makefile games/canfield/canfield/canfield.6 games/cribbage/Makefile games/dm/Makefile games/factor/Makefile games/fish/Makefile games/fortune/Makefile games/fortune/datfiles/Makefile games/fortune/fortune/Makefile games/fortune/strfile/Makefile games/hack/Makefile games/hangman/Makefile games/larn/Makefile games/mille/Makefile games/monop/Makefile games/monop/monop.6 games/monop/roll.c games/number/Makefile games/pom/Makefile games/rain/Makefile games/robots/Makefile games/robots/robots.6 games/rogue/Makefile games/rogue/rogue.6 games/rogue/score.c games/sail/Makefile games/sail/player.h games/snake/snake/Makefile games/snake/snake/snake.6 games/snake/snscore/snscore.c games/trek/Makefile games/worm/Makefile games/worms/Makefile games/wump/Makefile gnu/COPYING.LIB gnu/Makefile gnu/Makefile.inc gnu/games/chess/Makefile gnu/games/chess/chess.6 gnu/lib/libg++/COPYING gnu/lib/libg++/Makefile gnu/lib/libg++/g++-include/AVLMap.ccP gnu/lib/libg++/g++-include/AVLMap.hP gnu/lib/libg++/g++-include/AVLSet.ccP gnu/lib/libg++/g++-include/AVLSet.hP gnu/lib/libg++/g++-include/AVec.ccP gnu/lib/libg++/g++-include/AVec.hP gnu/lib/libg++/g++-include/BSTSet.ccP gnu/lib/libg++/g++-include/BSTSet.hP gnu/lib/libg++/g++-include/Bag.ccP gnu/lib/libg++/g++-include/Bag.hP gnu/lib/libg++/g++-include/CHBag.ccP gnu/lib/libg++/g++-include/CHBag.hP gnu/lib/libg++/g++-include/CHMap.ccP gnu/lib/libg++/g++-include/CHMap.hP gnu/lib/libg++/g++-include/CHNode.ccP gnu/lib/libg++/g++-include/CHNode.hP gnu/lib/libg++/g++-include/CHSet.ccP gnu/lib/libg++/g++-include/CHSet.hP gnu/lib/libg++/g++-include/DLDeque.ccP gnu/lib/libg++/g++-include/DLDeque.hP gnu/lib/libg++/g++-include/DLList.ccP gnu/lib/libg++/g++-include/DLList.hP gnu/lib/libg++/g++-include/Deque.ccP gnu/lib/libg++/g++-include/Deque.hP gnu/lib/libg++/g++-include/FPQueue.ccP gnu/lib/libg++/g++-include/FPQueue.hP gnu/lib/libg++/g++-include/FPStack.ccP gnu/lib/libg++/g++-include/FPStack.hP gnu/lib/libg++/g++-include/FPlex.ccP gnu/lib/libg++/g++-include/FPlex.hP gnu/lib/libg++/g++-include/List.ccP gnu/lib/libg++/g++-include/List.hP gnu/lib/libg++/g++-include/MPlex.ccP gnu/lib/libg++/g++-include/MPlex.hP gnu/lib/libg++/g++-include/Map.ccP gnu/lib/libg++/g++-include/Map.hP gnu/lib/libg++/g++-include/OSLBag.ccP gnu/lib/libg++/g++-include/OSLBag.hP gnu/lib/libg++/g++-include/OSLSet.ccP gnu/lib/libg++/g++-include/OSLSet.hP gnu/lib/libg++/g++-include/OXPBag.ccP gnu/lib/libg++/g++-include/OXPBag.hP gnu/lib/libg++/g++-include/OXPSet.ccP gnu/lib/libg++/g++-include/OXPSet.hP gnu/lib/libg++/g++-include/PHPQ.ccP gnu/lib/libg++/g++-include/PHPQ.hP gnu/lib/libg++/g++-include/PQ.ccP gnu/lib/libg++/g++-include/PQ.hP gnu/lib/libg++/g++-include/PSList.hP gnu/lib/libg++/g++-include/PVec.hP gnu/lib/libg++/g++-include/Plex.ccP gnu/lib/libg++/g++-include/Plex.hP gnu/lib/libg++/g++-include/Queue.ccP gnu/lib/libg++/g++-include/Queue.hP gnu/lib/libg++/g++-include/RAVLMap.ccP gnu/lib/libg++/g++-include/RAVLMap.hP gnu/lib/libg++/g++-include/RPlex.ccP gnu/lib/libg++/g++-include/RPlex.hP gnu/lib/libg++/g++-include/SLBag.ccP gnu/lib/libg++/g++-include/SLBag.hP gnu/lib/libg++/g++-include/SLList.ccP gnu/lib/libg++/g++-include/SLList.hP gnu/lib/libg++/g++-include/SLQueue.ccP gnu/lib/libg++/g++-include/SLQueue.hP gnu/lib/libg++/g++-include/SLSet.ccP gnu/lib/libg++/g++-include/SLSet.hP gnu/lib/libg++/g++-include/SLStack.ccP gnu/lib/libg++/g++-include/SLStack.hP gnu/lib/libg++/g++-include/Set.ccP gnu/lib/libg++/g++-include/Set.hP gnu/lib/libg++/g++-include/SkipBag.ccP gnu/lib/libg++/g++-include/SkipBag.hP gnu/lib/libg++/g++-include/SkipMap.ccP gnu/lib/libg++/g++-include/SkipMap.hP gnu/lib/libg++/g++-include/SkipSet.ccP gnu/lib/libg++/g++-include/SkipSet.hP gnu/lib/libg++/g++-include/SplayBag.ccP gnu/lib/libg++/g++-include/SplayBag.hP gnu/lib/libg++/g++-include/SplayMap.ccP gnu/lib/libg++/g++-include/SplayMap.hP gnu/lib/libg++/g++-include/SplayNode.ccP gnu/lib/libg++/g++-include/SplayNode.hP gnu/lib/libg++/g++-include/SplayPQ.ccP gnu/lib/libg++/g++-include/SplayPQ.hP gnu/lib/libg++/g++-include/SplaySet.ccP gnu/lib/libg++/g++-include/SplaySet.hP gnu/lib/libg++/g++-include/Stack.ccP gnu/lib/libg++/g++-include/Stack.hP gnu/lib/libg++/g++-include/VHBag.ccP gnu/lib/libg++/g++-include/VHBag.hP gnu/lib/libg++/g++-include/VHMap.ccP gnu/lib/libg++/g++-include/VHMap.hP gnu/lib/libg++/g++-include/VHSet.ccP gnu/lib/libg++/g++-include/VHSet.hP gnu/lib/libg++/g++-include/VOHSet.ccP gnu/lib/libg++/g++-include/VOHSet.hP gnu/lib/libg++/g++-include/VQueue.ccP gnu/lib/libg++/g++-include/VQueue.hP gnu/lib/libg++/g++-include/VStack.ccP gnu/lib/libg++/g++-include/VStack.hP gnu/lib/libg++/g++-include/Vec.ccP gnu/lib/libg++/g++-include/Vec.hP gnu/lib/libg++/g++-include/XPBag.ccP gnu/lib/libg++/g++-include/XPBag.hP gnu/lib/libg++/g++-include/XPDeque.ccP gnu/lib/libg++/g++-include/XPDeque.hP gnu/lib/libg++/g++-include/XPPQ.ccP gnu/lib/libg++/g++-include/XPPQ.hP gnu/lib/libg++/g++-include/XPQueue.ccP gnu/lib/libg++/g++-include/XPQueue.hP gnu/lib/libg++/g++-include/XPSet.ccP gnu/lib/libg++/g++-include/XPSet.hP gnu/lib/libg++/g++-include/XPStack.ccP gnu/lib/libg++/g++-include/XPStack.hP gnu/lib/libg++/g++-include/XPlex.ccP gnu/lib/libg++/g++-include/XPlex.hP gnu/lib/libg++/g++-include/assert.h gnu/lib/libg++/g++-include/bstring.h gnu/lib/libg++/g++-include/ctype.h gnu/lib/libg++/g++-include/curses.h gnu/lib/libg++/g++-include/defs.hP gnu/lib/libg++/g++-include/dir.h gnu/lib/libg++/g++-include/dirent.h gnu/lib/libg++/g++-include/errno.h gnu/lib/libg++/g++-include/fcntl.h gnu/lib/libg++/g++-include/gen/AVLMap.ccP gnu/lib/libg++/g++-include/gen/AVLMap.hP gnu/lib/libg++/g++-include/gen/AVLSet.ccP gnu/lib/libg++/g++-include/gen/AVLSet.hP gnu/lib/libg++/g++-include/gen/AVec.ccP gnu/lib/libg++/g++-include/gen/AVec.hP gnu/lib/libg++/g++-include/gen/BSTSet.ccP gnu/lib/libg++/g++-include/gen/BSTSet.hP gnu/lib/libg++/g++-include/gen/Bag.ccP gnu/lib/libg++/g++-include/gen/Bag.hP gnu/lib/libg++/g++-include/gen/CHBag.ccP gnu/lib/libg++/g++-include/gen/CHBag.hP gnu/lib/libg++/g++-include/gen/CHMap.ccP gnu/lib/libg++/g++-include/gen/CHMap.hP gnu/lib/libg++/g++-include/gen/CHNode.ccP gnu/lib/libg++/g++-include/gen/CHNode.hP gnu/lib/libg++/g++-include/gen/CHSet.ccP gnu/lib/libg++/g++-include/gen/CHSet.hP gnu/lib/libg++/g++-include/gen/DLDeque.ccP gnu/lib/libg++/g++-include/gen/DLDeque.hP gnu/lib/libg++/g++-include/gen/DLList.ccP gnu/lib/libg++/g++-include/gen/DLList.hP gnu/lib/libg++/g++-include/gen/Deque.ccP gnu/lib/libg++/g++-include/gen/Deque.hP gnu/lib/libg++/g++-include/gen/FPQueue.ccP gnu/lib/libg++/g++-include/gen/FPQueue.hP gnu/lib/libg++/g++-include/gen/FPStack.ccP gnu/lib/libg++/g++-include/gen/FPStack.hP gnu/lib/libg++/g++-include/gen/FPlex.ccP gnu/lib/libg++/g++-include/gen/FPlex.hP gnu/lib/libg++/g++-include/gen/List.ccP gnu/lib/libg++/g++-include/gen/List.hP gnu/lib/libg++/g++-include/gen/MPlex.ccP gnu/lib/libg++/g++-include/gen/MPlex.hP gnu/lib/libg++/g++-include/gen/Map.ccP gnu/lib/libg++/g++-include/gen/Map.hP gnu/lib/libg++/g++-include/gen/OSLBag.ccP gnu/lib/libg++/g++-include/gen/OSLBag.hP gnu/lib/libg++/g++-include/gen/OSLSet.ccP gnu/lib/libg++/g++-include/gen/OSLSet.hP gnu/lib/libg++/g++-include/gen/OXPBag.ccP gnu/lib/libg++/g++-include/gen/OXPBag.hP gnu/lib/libg++/g++-include/gen/OXPSet.ccP gnu/lib/libg++/g++-include/gen/OXPSet.hP gnu/lib/libg++/g++-include/gen/PHPQ.ccP gnu/lib/libg++/g++-include/gen/PHPQ.hP gnu/lib/libg++/g++-include/gen/PQ.ccP gnu/lib/libg++/g++-include/gen/PQ.hP gnu/lib/libg++/g++-include/gen/PSList.hP gnu/lib/libg++/g++-include/gen/PVec.hP gnu/lib/libg++/g++-include/gen/Plex.ccP gnu/lib/libg++/g++-include/gen/Plex.hP gnu/lib/libg++/g++-include/gen/Queue.ccP gnu/lib/libg++/g++-include/gen/Queue.hP gnu/lib/libg++/g++-include/gen/RAVLMap.ccP gnu/lib/libg++/g++-include/gen/RAVLMap.hP gnu/lib/libg++/g++-include/gen/RPlex.ccP gnu/lib/libg++/g++-include/gen/RPlex.hP gnu/lib/libg++/g++-include/gen/SLBag.ccP gnu/lib/libg++/g++-include/gen/SLBag.hP gnu/lib/libg++/g++-include/gen/SLList.ccP gnu/lib/libg++/g++-include/gen/SLList.hP gnu/lib/libg++/g++-include/gen/SLQueue.ccP gnu/lib/libg++/g++-include/gen/SLQueue.hP gnu/lib/libg++/g++-include/gen/SLSet.ccP gnu/lib/libg++/g++-include/gen/SLSet.hP gnu/lib/libg++/g++-include/gen/SLStack.ccP gnu/lib/libg++/g++-include/gen/SLStack.hP gnu/lib/libg++/g++-include/gen/Set.ccP gnu/lib/libg++/g++-include/gen/Set.hP gnu/lib/libg++/g++-include/gen/SkipBag.ccP gnu/lib/libg++/g++-include/gen/SkipBag.hP gnu/lib/libg++/g++-include/gen/SkipMap.ccP gnu/lib/libg++/g++-include/gen/SkipMap.hP gnu/lib/libg++/g++-include/gen/SkipSet.ccP gnu/lib/libg++/g++-include/gen/SkipSet.hP gnu/lib/libg++/g++-include/gen/SplayBag.ccP gnu/lib/libg++/g++-include/gen/SplayBag.hP gnu/lib/libg++/g++-include/gen/SplayMap.ccP gnu/lib/libg++/g++-include/gen/SplayMap.hP gnu/lib/libg++/g++-include/gen/SplayNode.ccP gnu/lib/libg++/g++-include/gen/SplayNode.hP gnu/lib/libg++/g++-include/gen/SplayPQ.ccP gnu/lib/libg++/g++-include/gen/SplayPQ.hP gnu/lib/libg++/g++-include/gen/SplaySet.ccP gnu/lib/libg++/g++-include/gen/SplaySet.hP gnu/lib/libg++/g++-include/gen/Stack.ccP gnu/lib/libg++/g++-include/gen/Stack.hP gnu/lib/libg++/g++-include/gen/VHBag.ccP gnu/lib/libg++/g++-include/gen/VHBag.hP gnu/lib/libg++/g++-include/gen/VHMap.ccP gnu/lib/libg++/g++-include/gen/VHMap.hP gnu/lib/libg++/g++-include/gen/VHSet.ccP gnu/lib/libg++/g++-include/gen/VHSet.hP gnu/lib/libg++/g++-include/gen/VOHSet.ccP gnu/lib/libg++/g++-include/gen/VOHSet.hP gnu/lib/libg++/g++-include/gen/VQueue.ccP gnu/lib/libg++/g++-include/gen/VQueue.hP gnu/lib/libg++/g++-include/gen/VStack.ccP gnu/lib/libg++/g++-include/gen/VStack.hP gnu/lib/libg++/g++-include/gen/Vec.ccP gnu/lib/libg++/g++-include/gen/Vec.hP gnu/lib/libg++/g++-include/gen/XPBag.ccP gnu/lib/libg++/g++-include/gen/XPBag.hP gnu/lib/libg++/g++-include/gen/XPDeque.ccP gnu/lib/libg++/g++-include/gen/XPDeque.hP gnu/lib/libg++/g++-include/gen/XPPQ.ccP gnu/lib/libg++/g++-include/gen/XPPQ.hP gnu/lib/libg++/g++-include/gen/XPQueue.ccP gnu/lib/libg++/g++-include/gen/XPQueue.hP gnu/lib/libg++/g++-include/gen/XPSet.ccP gnu/lib/libg++/g++-include/gen/XPSet.hP gnu/lib/libg++/g++-include/gen/XPStack.ccP gnu/lib/libg++/g++-include/gen/XPStack.hP gnu/lib/libg++/g++-include/gen/XPlex.ccP gnu/lib/libg++/g++-include/gen/XPlex.hP gnu/lib/libg++/g++-include/gen/defs.hP gnu/lib/libg++/g++-include/gen/intSList.hP gnu/lib/libg++/g++-include/gen/intVec.hP gnu/lib/libg++/g++-include/grp.h gnu/lib/libg++/g++-include/intSList.hP gnu/lib/libg++/g++-include/intVec.hP gnu/lib/libg++/g++-include/math.h gnu/lib/libg++/g++-include/memory.h gnu/lib/libg++/g++-include/netdb.h gnu/lib/libg++/g++-include/pwd.h gnu/lib/libg++/g++-include/setjmp.h gnu/lib/libg++/g++-include/signal.h gnu/lib/libg++/g++-include/stdarg.h gnu/lib/libg++/g++-include/stddef.h gnu/lib/libg++/g++-include/stdio.h gnu/lib/libg++/g++-include/stdlib.h gnu/lib/libg++/g++-include/string.h gnu/lib/libg++/g++-include/strings.h gnu/lib/libg++/g++-include/sys/dir.h gnu/lib/libg++/g++-include/sys/fcntl.h gnu/lib/libg++/g++-include/sys/file.h gnu/lib/libg++/g++-include/sys/mman.h gnu/lib/libg++/g++-include/sys/param.h gnu/lib/libg++/g++-include/sys/resource.h gnu/lib/libg++/g++-include/sys/select.h gnu/lib/libg++/g++-include/sys/signal.h gnu/lib/libg++/g++-include/sys/socket.h gnu/lib/libg++/g++-include/sys/stat.h gnu/lib/libg++/g++-include/sys/time.h gnu/lib/libg++/g++-include/sys/times.h gnu/lib/libg++/g++-include/sys/types.h gnu/lib/libg++/g++-include/sys/wait.h gnu/lib/libg++/g++-include/time.h gnu/lib/libg++/g++-include/unistd.h gnu/lib/libg++/g++-include/values.h gnu/lib/libg++/genclass/Makefile gnu/lib/libg++/genclass/genclass.sh gnu/lib/libg++/iostream/PlotFile.C gnu/lib/libg++/iostream/PlotFile.h gnu/lib/libg++/iostream/SFile.C gnu/lib/libg++/iostream/SFile.h gnu/lib/libg++/iostream/editbuf.C gnu/lib/libg++/iostream/editbuf.h gnu/lib/libg++/iostream/filebuf.C gnu/lib/libg++/iostream/floatconv.C gnu/lib/libg++/iostream/floatio.h gnu/lib/libg++/iostream/fstream.C gnu/lib/libg++/iostream/fstream.h gnu/lib/libg++/iostream/igetline.C gnu/lib/libg++/iostream/igetsb.C gnu/lib/libg++/iostream/indstream.C gnu/lib/libg++/iostream/indstream.h gnu/lib/libg++/iostream/iomanip.C gnu/lib/libg++/iostream/iomanip.h gnu/lib/libg++/iostream/ioprivate.h gnu/lib/libg++/iostream/iostream.C gnu/lib/libg++/iostream/iostream.h gnu/lib/libg++/iostream/makebuf.C gnu/lib/libg++/iostream/outfloat.C gnu/lib/libg++/iostream/parsestream.C gnu/lib/libg++/iostream/parsestream.h gnu/lib/libg++/iostream/procbuf.C gnu/lib/libg++/iostream/procbuf.h gnu/lib/libg++/iostream/sbufvform.C gnu/lib/libg++/iostream/sbufvscan.C gnu/lib/libg++/iostream/sgetline.C gnu/lib/libg++/iostream/stdiostream.C gnu/lib/libg++/iostream/stdiostream.h gnu/lib/libg++/iostream/stdstrbufs.C gnu/lib/libg++/iostream/stdstreams.C gnu/lib/libg++/iostream/stream.C gnu/lib/libg++/iostream/stream.h gnu/lib/libg++/iostream/streambuf.C gnu/lib/libg++/iostream/streambuf.h gnu/lib/libg++/iostream/strstream.C gnu/lib/libg++/iostream/strstream.h gnu/lib/libg++/libg++/ACG.cc gnu/lib/libg++/libg++/ACG.h gnu/lib/libg++/libg++/AllocRing.cc gnu/lib/libg++/libg++/AllocRing.h gnu/lib/libg++/libg++/Binomial.cc gnu/lib/libg++/libg++/Binomial.h gnu/lib/libg++/libg++/BitSet.cc gnu/lib/libg++/libg++/BitSet.h gnu/lib/libg++/libg++/BitString.cc gnu/lib/libg++/libg++/BitString.h gnu/lib/libg++/libg++/Complex.cc gnu/lib/libg++/libg++/Complex.h gnu/lib/libg++/libg++/CursesW.cc gnu/lib/libg++/libg++/CursesW.h gnu/lib/libg++/libg++/DLList.cc gnu/lib/libg++/libg++/DLList.h gnu/lib/libg++/libg++/DiscUnif.cc gnu/lib/libg++/libg++/DiscUnif.h gnu/lib/libg++/libg++/Erlang.cc gnu/lib/libg++/libg++/Erlang.h gnu/lib/libg++/libg++/Fix.cc gnu/lib/libg++/libg++/Fix.h gnu/lib/libg++/libg++/Fix16.cc gnu/lib/libg++/libg++/Fix16.h gnu/lib/libg++/libg++/Fix24.cc gnu/lib/libg++/libg++/Fix24.h gnu/lib/libg++/libg++/Geom.cc gnu/lib/libg++/libg++/Geom.h gnu/lib/libg++/libg++/GetOpt.cc gnu/lib/libg++/libg++/GetOpt.h gnu/lib/libg++/libg++/HypGeom.cc gnu/lib/libg++/libg++/HypGeom.h gnu/lib/libg++/libg++/Incremental.h gnu/lib/libg++/libg++/Integer.cc gnu/lib/libg++/libg++/Integer.h gnu/lib/libg++/libg++/LogNorm.cc gnu/lib/libg++/libg++/LogNorm.h gnu/lib/libg++/libg++/MLCG.cc gnu/lib/libg++/libg++/MLCG.h gnu/lib/libg++/libg++/Makefile gnu/lib/libg++/libg++/NegExp.cc gnu/lib/libg++/libg++/NegExp.h gnu/lib/libg++/libg++/Normal.cc gnu/lib/libg++/libg++/Normal.h gnu/lib/libg++/libg++/Obstack.cc gnu/lib/libg++/libg++/Obstack.h gnu/lib/libg++/libg++/Pix.h gnu/lib/libg++/libg++/Poisson.cc gnu/lib/libg++/libg++/Poisson.h gnu/lib/libg++/libg++/RNG.cc gnu/lib/libg++/libg++/RNG.h gnu/lib/libg++/libg++/Random.h gnu/lib/libg++/libg++/Rational.cc gnu/lib/libg++/libg++/Rational.h gnu/lib/libg++/libg++/Regex.cc gnu/lib/libg++/libg++/Regex.h gnu/lib/libg++/libg++/RndInt.h gnu/lib/libg++/libg++/SLList.cc gnu/lib/libg++/libg++/SLList.h gnu/lib/libg++/libg++/SmplHist.cc gnu/lib/libg++/libg++/SmplHist.h gnu/lib/libg++/libg++/SmplStat.cc gnu/lib/libg++/libg++/SmplStat.h gnu/lib/libg++/libg++/String.cc gnu/lib/libg++/libg++/String.h gnu/lib/libg++/libg++/Uniform.cc gnu/lib/libg++/libg++/Uniform.h gnu/lib/libg++/libg++/Weibull.cc gnu/lib/libg++/libg++/Weibull.h gnu/lib/libg++/libg++/_G_config.h gnu/lib/libg++/libg++/bool.h gnu/lib/libg++/libg++/builtin.h gnu/lib/libg++/libg++/chr.cc gnu/lib/libg++/libg++/compare.h gnu/lib/libg++/libg++/complex.h gnu/lib/libg++/libg++/dtoa.cc gnu/lib/libg++/libg++/error.cc gnu/lib/libg++/libg++/fmtq.cc gnu/lib/libg++/libg++/gcd.cc gnu/lib/libg++/libg++/generic.h gnu/lib/libg++/libg++/getpagesize.h gnu/lib/libg++/libg++/hash.cc gnu/lib/libg++/libg++/lg.cc gnu/lib/libg++/libg++/libc.h gnu/lib/libg++/libg++/math-68881.h gnu/lib/libg++/libg++/minmax.h gnu/lib/libg++/libg++/new.cc gnu/lib/libg++/libg++/new.h gnu/lib/libg++/libg++/osfcn.h gnu/lib/libg++/libg++/pow.cc gnu/lib/libg++/libg++/regex.cc gnu/lib/libg++/libg++/regex.h gnu/lib/libg++/libg++/sqrt.cc gnu/lib/libg++/libg++/std.h gnu/lib/libg++/libg++/str.cc gnu/lib/libg++/libg++/strclass.h gnu/lib/libg++/libg++/swap.h gnu/lib/libg++/libg++/timer.cc gnu/lib/libg++/libg++/typemacros.h gnu/lib/libg++/libiberty/config.h gnu/lib/libg++/libiberty/insque.c gnu/lib/libg++/libiberty/strerror.c gnu/lib/libg++/libiberty/strsignal.c gnu/lib/libmalloc/COPYING.LIB gnu/lib/libmalloc/ChangeLog gnu/lib/libmalloc/Makefile gnu/lib/libmalloc/Makefile.gnu gnu/lib/libmalloc/OChangeLog gnu/lib/libmalloc/README gnu/lib/libmalloc/VERSION gnu/lib/libmalloc/calloc.c gnu/lib/libmalloc/cfree.c gnu/lib/libmalloc/getpagesize.h gnu/lib/libmalloc/gmalloc-head.c gnu/lib/libmalloc/malloc.c gnu/lib/libmalloc/malloc.h gnu/lib/libmalloc/mcheck.c gnu/lib/libmalloc/mem-limits.h gnu/lib/libmalloc/memalign.c gnu/lib/libmalloc/morecore.c gnu/lib/libmalloc/mstats.c gnu/lib/libmalloc/mtrace.awk gnu/lib/libmalloc/mtrace.c gnu/lib/libmalloc/ralloc.c gnu/lib/libmalloc/valloc.c gnu/lib/libmalloc/vm-limit.c gnu/lib/libregex/AUTHORS gnu/lib/libregex/COPYING gnu/lib/libregex/ChangeLog gnu/lib/libregex/INSTALL gnu/lib/libregex/Makefile gnu/lib/libregex/Makefile.gnu gnu/lib/libregex/Makefile.in gnu/lib/libregex/NEWS gnu/lib/libregex/README gnu/lib/libregex/VERSION gnu/lib/libregex/config.status gnu/lib/libregex/configure gnu/lib/libregex/configure.in gnu/lib/libregex/doc/Makefile gnu/lib/libregex/doc/Makefile.in gnu/lib/libregex/doc/include.awk gnu/lib/libregex/doc/regex.aux gnu/lib/libregex/doc/regex.cps gnu/lib/libregex/doc/regex.info gnu/lib/libregex/doc/regex.texi gnu/lib/libregex/doc/texinfo.tex gnu/lib/libregex/doc/xregex.texi gnu/lib/libregex/regex.c gnu/lib/libregex/regex.h gnu/lib/libregex/test/ChangeLog gnu/lib/libregex/test/Makefile gnu/lib/libregex/test/Makefile.in gnu/lib/libregex/test/TAGS gnu/lib/libregex/test/alloca.c gnu/lib/libregex/test/bsd-interf.c gnu/lib/libregex/test/debugmalloc.c gnu/lib/libregex/test/emacsmalloc.c gnu/lib/libregex/test/fileregex.c gnu/lib/libregex/test/g++malloc.c gnu/lib/libregex/test/getpagesize.h gnu/lib/libregex/test/iregex.c gnu/lib/libregex/test/main.c gnu/lib/libregex/test/malloc-test.c gnu/lib/libregex/test/other.c gnu/lib/libregex/test/printchar.c gnu/lib/libregex/test/psx-basic.c gnu/lib/libregex/test/psx-extend.c gnu/lib/libregex/test/psx-generic.c gnu/lib/libregex/test/psx-group.c gnu/lib/libregex/test/psx-interf.c gnu/lib/libregex/test/psx-interv.c gnu/lib/libregex/test/regexcpp.sed gnu/lib/libregex/test/syntax.skel gnu/lib/libregex/test/test.c gnu/lib/libregex/test/test.h gnu/lib/libregex/test/tregress.c gnu/lib/libregex/test/upcase.c gnu/lib/libregex/test/xmalloc.c gnu/libexec/uucp/COPYING gnu/libexec/uucp/ChangeLog gnu/libexec/uucp/Makefile gnu/libexec/uucp/Makefile.inc gnu/libexec/uucp/README gnu/libexec/uucp/TODO gnu/libexec/uucp/VERSION gnu/libexec/uucp/common_sources/chat.c gnu/libexec/uucp/common_sources/conf.h gnu/libexec/uucp/common_sources/conn.c gnu/libexec/uucp/common_sources/conn.h gnu/libexec/uucp/common_sources/copy.c gnu/libexec/uucp/common_sources/cu.h gnu/libexec/uucp/common_sources/getopt.h gnu/libexec/uucp/common_sources/log.c gnu/libexec/uucp/common_sources/policy.h gnu/libexec/uucp/common_sources/prot.c gnu/libexec/uucp/common_sources/prot.h gnu/libexec/uucp/common_sources/sysdep.h gnu/libexec/uucp/common_sources/system.h gnu/libexec/uucp/common_sources/tcp.c gnu/libexec/uucp/common_sources/tli.c gnu/libexec/uucp/common_sources/trans.h gnu/libexec/uucp/common_sources/util.c gnu/libexec/uucp/common_sources/uuconf.h gnu/libexec/uucp/common_sources/uucp.h gnu/libexec/uucp/common_sources/uudefs.h gnu/libexec/uucp/contrib/Dial.Hayes gnu/libexec/uucp/contrib/Hangup.Hayes gnu/libexec/uucp/contrib/Login.LAT gnu/libexec/uucp/contrib/Login.PortSel gnu/libexec/uucp/contrib/Login.VMS gnu/libexec/uucp/contrib/Makefile.uurt gnu/libexec/uucp/contrib/Makefile.xchat gnu/libexec/uucp/contrib/README gnu/libexec/uucp/contrib/README-UURATE gnu/libexec/uucp/contrib/README-XCHAT gnu/libexec/uucp/contrib/savelog.man gnu/libexec/uucp/contrib/savelog.sh gnu/libexec/uucp/contrib/stats.sh gnu/libexec/uucp/contrib/tstout.c gnu/libexec/uucp/contrib/uuclean gnu/libexec/uucp/contrib/uuq.sh gnu/libexec/uucp/contrib/uurate.c gnu/libexec/uucp/contrib/uurate.man gnu/libexec/uucp/contrib/uureroute gnu/libexec/uucp/contrib/uusnap.c gnu/libexec/uucp/contrib/uutraf gnu/libexec/uucp/contrib/uutry gnu/libexec/uucp/contrib/xc-conf.h-dist gnu/libexec/uucp/contrib/xchat.c gnu/libexec/uucp/contrib/xchat.man gnu/libexec/uucp/cu/Makefile gnu/libexec/uucp/cu/cu.1 gnu/libexec/uucp/cu/cu.c gnu/libexec/uucp/libunix/MANIFEST gnu/libexec/uucp/libunix/Makefile gnu/libexec/uucp/libunix/access.c gnu/libexec/uucp/libunix/addbas.c gnu/libexec/uucp/libunix/app3.c gnu/libexec/uucp/libunix/app4.c gnu/libexec/uucp/libunix/basnam.c gnu/libexec/uucp/libunix/bytfre.c gnu/libexec/uucp/libunix/chmod.c gnu/libexec/uucp/libunix/cohtty.c gnu/libexec/uucp/libunix/cusub.c gnu/libexec/uucp/libunix/cwd.c gnu/libexec/uucp/libunix/detach.c gnu/libexec/uucp/libunix/dirent.c gnu/libexec/uucp/libunix/dup2.c gnu/libexec/uucp/libunix/efopen.c gnu/libexec/uucp/libunix/epopen.c gnu/libexec/uucp/libunix/exists.c gnu/libexec/uucp/libunix/filnam.c gnu/libexec/uucp/libunix/fsusg.c gnu/libexec/uucp/libunix/fsusg.h gnu/libexec/uucp/libunix/ftw.c gnu/libexec/uucp/libunix/getcwd.c gnu/libexec/uucp/libunix/indir.c gnu/libexec/uucp/libunix/init.c gnu/libexec/uucp/libunix/isdir.c gnu/libexec/uucp/libunix/isfork.c gnu/libexec/uucp/libunix/iswait.c gnu/libexec/uucp/libunix/jobid.c gnu/libexec/uucp/libunix/lcksys.c gnu/libexec/uucp/libunix/link.c gnu/libexec/uucp/libunix/locfil.c gnu/libexec/uucp/libunix/lock.c gnu/libexec/uucp/libunix/loctim.c gnu/libexec/uucp/libunix/mail.c gnu/libexec/uucp/libunix/mkdir.c gnu/libexec/uucp/libunix/mkdirs.c gnu/libexec/uucp/libunix/mode.c gnu/libexec/uucp/libunix/move.c gnu/libexec/uucp/libunix/opensr.c gnu/libexec/uucp/libunix/pause.c gnu/libexec/uucp/libunix/picksb.c gnu/libexec/uucp/libunix/portnm.c gnu/libexec/uucp/libunix/proctm.c gnu/libexec/uucp/libunix/recep.c gnu/libexec/uucp/libunix/remove.c gnu/libexec/uucp/libunix/rename.c gnu/libexec/uucp/libunix/rmdir.c gnu/libexec/uucp/libunix/run.c gnu/libexec/uucp/libunix/seq.c gnu/libexec/uucp/libunix/serial.c gnu/libexec/uucp/libunix/signal.c gnu/libexec/uucp/libunix/sindir.c gnu/libexec/uucp/libunix/size.c gnu/libexec/uucp/libunix/sleep.c gnu/libexec/uucp/libunix/spawn.c gnu/libexec/uucp/libunix/splcmd.c gnu/libexec/uucp/libunix/splnam.c gnu/libexec/uucp/libunix/spool.c gnu/libexec/uucp/libunix/srmdir.c gnu/libexec/uucp/libunix/statsb.c gnu/libexec/uucp/libunix/status.c gnu/libexec/uucp/libunix/strerr.c gnu/libexec/uucp/libunix/time.c gnu/libexec/uucp/libunix/tmpfil.c gnu/libexec/uucp/libunix/trunc.c gnu/libexec/uucp/libunix/uacces.c gnu/libexec/uucp/libunix/ufopen.c gnu/libexec/uucp/libunix/ultspl.c gnu/libexec/uucp/libunix/unknwn.c gnu/libexec/uucp/libunix/uuto.c gnu/libexec/uucp/libunix/walk.c gnu/libexec/uucp/libunix/wldcrd.c gnu/libexec/uucp/libunix/work.c gnu/libexec/uucp/libunix/xqtfil.c gnu/libexec/uucp/libunix/xqtsub.c gnu/libexec/uucp/libuuconf/COPYING.LIB gnu/libexec/uucp/libuuconf/MANIFEST gnu/libexec/uucp/libuuconf/Makefile gnu/libexec/uucp/libuuconf/README gnu/libexec/uucp/libuuconf/addblk.c gnu/libexec/uucp/libuuconf/addstr.c gnu/libexec/uucp/libuuconf/allblk.c gnu/libexec/uucp/libuuconf/alloc.c gnu/libexec/uucp/libuuconf/alloc.h gnu/libexec/uucp/libuuconf/base.c gnu/libexec/uucp/libuuconf/bool.c gnu/libexec/uucp/libuuconf/callin.c gnu/libexec/uucp/libuuconf/calout.c gnu/libexec/uucp/libuuconf/chatc.c gnu/libexec/uucp/libuuconf/cmdarg.c gnu/libexec/uucp/libuuconf/cmdfil.c gnu/libexec/uucp/libuuconf/cmdlin.c gnu/libexec/uucp/libuuconf/debfil.c gnu/libexec/uucp/libuuconf/deblev.c gnu/libexec/uucp/libuuconf/diacod.c gnu/libexec/uucp/libuuconf/dial.c gnu/libexec/uucp/libuuconf/diasub.c gnu/libexec/uucp/libuuconf/dnams.c gnu/libexec/uucp/libuuconf/errno.c gnu/libexec/uucp/libuuconf/errstr.c gnu/libexec/uucp/libuuconf/filnam.c gnu/libexec/uucp/libuuconf/freblk.c gnu/libexec/uucp/libuuconf/fredia.c gnu/libexec/uucp/libuuconf/free.c gnu/libexec/uucp/libuuconf/freprt.c gnu/libexec/uucp/libuuconf/fresys.c gnu/libexec/uucp/libuuconf/grdcmp.c gnu/libexec/uucp/libuuconf/hdial.c gnu/libexec/uucp/libuuconf/hdnams.c gnu/libexec/uucp/libuuconf/hinit.c gnu/libexec/uucp/libuuconf/hlocnm.c gnu/libexec/uucp/libuuconf/hport.c gnu/libexec/uucp/libuuconf/hrmunk.c gnu/libexec/uucp/libuuconf/hsinfo.c gnu/libexec/uucp/libuuconf/hsnams.c gnu/libexec/uucp/libuuconf/hsys.c gnu/libexec/uucp/libuuconf/hunk.c gnu/libexec/uucp/libuuconf/iniglb.c gnu/libexec/uucp/libuuconf/init.c gnu/libexec/uucp/libuuconf/int.c gnu/libexec/uucp/libuuconf/lckdir.c gnu/libexec/uucp/libuuconf/lineno.c gnu/libexec/uucp/libuuconf/llocnm.c gnu/libexec/uucp/libuuconf/local.c gnu/libexec/uucp/libuuconf/locnm.c gnu/libexec/uucp/libuuconf/logfil.c gnu/libexec/uucp/libuuconf/maxuxq.c gnu/libexec/uucp/libuuconf/mrgblk.c gnu/libexec/uucp/libuuconf/paramc.c gnu/libexec/uucp/libuuconf/port.c gnu/libexec/uucp/libuuconf/prtsub.c gnu/libexec/uucp/libuuconf/pubdir.c gnu/libexec/uucp/libuuconf/rdlocs.c gnu/libexec/uucp/libuuconf/rdperm.c gnu/libexec/uucp/libuuconf/reliab.c gnu/libexec/uucp/libuuconf/remunk.c gnu/libexec/uucp/libuuconf/sinfo.c gnu/libexec/uucp/libuuconf/snams.c gnu/libexec/uucp/libuuconf/split.c gnu/libexec/uucp/libuuconf/spool.c gnu/libexec/uucp/libuuconf/stafil.c gnu/libexec/uucp/libuuconf/syshdr.h gnu/libexec/uucp/libuuconf/syssub.c gnu/libexec/uucp/libuuconf/tcalou.c gnu/libexec/uucp/libuuconf/tdial.c gnu/libexec/uucp/libuuconf/tdialc.c gnu/libexec/uucp/libuuconf/tdnams.c gnu/libexec/uucp/libuuconf/tgcmp.c gnu/libexec/uucp/libuuconf/thread.c gnu/libexec/uucp/libuuconf/time.c gnu/libexec/uucp/libuuconf/tinit.c gnu/libexec/uucp/libuuconf/tlocnm.c gnu/libexec/uucp/libuuconf/tport.c gnu/libexec/uucp/libuuconf/tportc.c gnu/libexec/uucp/libuuconf/tsinfo.c gnu/libexec/uucp/libuuconf/tsnams.c gnu/libexec/uucp/libuuconf/tsys.c gnu/libexec/uucp/libuuconf/tval.c gnu/libexec/uucp/libuuconf/ugtlin.c gnu/libexec/uucp/libuuconf/unk.c gnu/libexec/uucp/libuuconf/uucnfi.h gnu/libexec/uucp/libuuconf/val.c gnu/libexec/uucp/libuuconf/vinit.c gnu/libexec/uucp/libuuconf/vport.c gnu/libexec/uucp/libuuconf/vsinfo.c gnu/libexec/uucp/libuuconf/vsnams.c gnu/libexec/uucp/libuuconf/vsys.c gnu/libexec/uucp/libuucp/MANIFEST gnu/libexec/uucp/libuucp/Makefile gnu/libexec/uucp/libuucp/bsrch.c gnu/libexec/uucp/libuucp/buffer.c gnu/libexec/uucp/libuucp/bzero.c gnu/libexec/uucp/libuucp/crc.c gnu/libexec/uucp/libuucp/debug.c gnu/libexec/uucp/libuucp/escape.c gnu/libexec/uucp/libuucp/getlin.c gnu/libexec/uucp/libuucp/getop1.c gnu/libexec/uucp/libuucp/getopt.c gnu/libexec/uucp/libuucp/memchr.c gnu/libexec/uucp/libuucp/memcmp.c gnu/libexec/uucp/libuucp/memcpy.c gnu/libexec/uucp/libuucp/parse.c gnu/libexec/uucp/libuucp/spool.c gnu/libexec/uucp/libuucp/status.c gnu/libexec/uucp/libuucp/strcas.c gnu/libexec/uucp/libuucp/strchr.c gnu/libexec/uucp/libuucp/strdup.c gnu/libexec/uucp/libuucp/strncs.c gnu/libexec/uucp/libuucp/strrch.c gnu/libexec/uucp/libuucp/strstr.c gnu/libexec/uucp/libuucp/strtol.c gnu/libexec/uucp/libuucp/xfree.c gnu/libexec/uucp/libuucp/xmall.c gnu/libexec/uucp/libuucp/xreall.c gnu/libexec/uucp/sample/Makefile gnu/libexec/uucp/sample/call gnu/libexec/uucp/sample/config gnu/libexec/uucp/sample/dial gnu/libexec/uucp/sample/dialcode gnu/libexec/uucp/sample/passwd gnu/libexec/uucp/sample/port gnu/libexec/uucp/sample/sys1 gnu/libexec/uucp/sample/sys2 gnu/libexec/uucp/tstuu.c gnu/libexec/uucp/uuchk/Makefile gnu/libexec/uucp/uuchk/uuchk.c gnu/libexec/uucp/uucico/Makefile gnu/libexec/uucp/uucico/prote.c gnu/libexec/uucp/uucico/protf.c gnu/libexec/uucp/uucico/protg.c gnu/libexec/uucp/uucico/proti.c gnu/libexec/uucp/uucico/protj.c gnu/libexec/uucp/uucico/prott.c gnu/libexec/uucp/uucico/protz.c gnu/libexec/uucp/uucico/rec.c gnu/libexec/uucp/uucico/send.c gnu/libexec/uucp/uucico/time.c gnu/libexec/uucp/uucico/trans.c gnu/libexec/uucp/uucico/uucico.8 gnu/libexec/uucp/uucico/uucico.c gnu/libexec/uucp/uucico/xcmd.c gnu/libexec/uucp/uuconv/Makefile gnu/libexec/uucp/uuconv/uuconv.c gnu/libexec/uucp/uucp/Makefile gnu/libexec/uucp/uucp/uucp.1 gnu/libexec/uucp/uucp/uucp.c gnu/libexec/uucp/uulog/Makefile gnu/libexec/uucp/uulog/uulog.c gnu/libexec/uucp/uuname/Makefile gnu/libexec/uucp/uuname/uuname.c gnu/libexec/uucp/uupick/Makefile gnu/libexec/uucp/uupick/uupick.c gnu/libexec/uucp/uusched/Makefile gnu/libexec/uucp/uusched/uusched.in gnu/libexec/uucp/uustat/Makefile gnu/libexec/uucp/uustat/uustat.1 gnu/libexec/uucp/uustat/uustat.c gnu/libexec/uucp/uuto/Makefile gnu/libexec/uucp/uuto/uuto.in gnu/libexec/uucp/uux/Makefile gnu/libexec/uucp/uux/uux.1 gnu/libexec/uucp/uux/uux.c gnu/libexec/uucp/uuxqt/Makefile gnu/libexec/uucp/uuxqt/uuxqt.8 gnu/libexec/uucp/uuxqt/uuxqt.c gnu/usr.bin/as/COPYING gnu/usr.bin/as/ChangeLog gnu/usr.bin/as/Makefile gnu/usr.bin/as/Makefile.gnu gnu/usr.bin/as/NOTES gnu/usr.bin/as/README.gnu gnu/usr.bin/as/app.c gnu/usr.bin/as/append.c gnu/usr.bin/as/as.1 gnu/usr.bin/as/as.c gnu/usr.bin/as/as.h gnu/usr.bin/as/atof-generic.c gnu/usr.bin/as/bignum-copy.c gnu/usr.bin/as/bignum.h gnu/usr.bin/as/config/Makefile.i386 gnu/usr.bin/as/config/a.out.gnu.h gnu/usr.bin/as/config/atof-ieee.c gnu/usr.bin/as/config/i386-opcode.h gnu/usr.bin/as/config/i386.c gnu/usr.bin/as/config/i386.h gnu/usr.bin/as/expr.c gnu/usr.bin/as/expr.h gnu/usr.bin/as/flonum-const.c gnu/usr.bin/as/flonum-copy.c gnu/usr.bin/as/flonum-mult.c gnu/usr.bin/as/flonum.h gnu/usr.bin/as/frags.c gnu/usr.bin/as/frags.h gnu/usr.bin/as/hash.c gnu/usr.bin/as/hash.h gnu/usr.bin/as/hex-value.c gnu/usr.bin/as/input-file.c gnu/usr.bin/as/input-file.h gnu/usr.bin/as/input-scrub.c gnu/usr.bin/as/md.h gnu/usr.bin/as/messages.c gnu/usr.bin/as/objrecdef.h gnu/usr.bin/as/obstack.c gnu/usr.bin/as/obstack.h gnu/usr.bin/as/output-file.c gnu/usr.bin/as/read.c gnu/usr.bin/as/read.h gnu/usr.bin/as/struc-symbol.h gnu/usr.bin/as/subsegs.c gnu/usr.bin/as/subsegs.h gnu/usr.bin/as/symbols.c gnu/usr.bin/as/symbols.h gnu/usr.bin/as/version.c gnu/usr.bin/as/write.c gnu/usr.bin/as/write.h gnu/usr.bin/as/xmalloc.c gnu/usr.bin/as/xrealloc.c gnu/usr.bin/awk/Makefile gnu/usr.bin/bc/Makefile gnu/usr.bin/bc/bc.1 gnu/usr.bin/cc/Makefile gnu/usr.bin/cc/TODO gnu/usr.bin/cc/cc/Makefile gnu/usr.bin/cc/cc/g++.script gnu/usr.bin/cc/cc/gcc.c gnu/usr.bin/cc/cc1/Makefile gnu/usr.bin/cc/cc1/c-decl.c gnu/usr.bin/cc/cc1/c-iterate.c gnu/usr.bin/cc/cc1/c-typeck.c gnu/usr.bin/cc/cc1obj/Makefile gnu/usr.bin/cc/cc1obj/objc-act.c gnu/usr.bin/cc/cc1plus/Makefile gnu/usr.bin/cc/cc1plus/cp-decl.c gnu/usr.bin/cc/cc1plus/cp-search.c gnu/usr.bin/cc/cc1plus/cp-typeck.c gnu/usr.bin/cc/cpp/Makefile gnu/usr.bin/cc/cpp/cccp.c gnu/usr.bin/cc/cpp/gcpp.1 gnu/usr.bin/cc/lib/Makefile gnu/usr.bin/cc/lib/aux-output.c gnu/usr.bin/cc/lib/basic-block.h gnu/usr.bin/cc/lib/c-common.c gnu/usr.bin/cc/lib/c-lex.h gnu/usr.bin/cc/lib/c-parse.h gnu/usr.bin/cc/lib/c-tree.h gnu/usr.bin/cc/lib/caller-save.c gnu/usr.bin/cc/lib/calls.c gnu/usr.bin/cc/lib/combine.c gnu/usr.bin/cc/lib/conditions.h gnu/usr.bin/cc/lib/config.h gnu/usr.bin/cc/lib/convert.c gnu/usr.bin/cc/lib/convert.h gnu/usr.bin/cc/lib/cse.c gnu/usr.bin/cc/lib/dbxout.c gnu/usr.bin/cc/lib/defaults.h gnu/usr.bin/cc/lib/dwarfout.c gnu/usr.bin/cc/lib/emit-rtl.c gnu/usr.bin/cc/lib/explow.c gnu/usr.bin/cc/lib/expmed.c gnu/usr.bin/cc/lib/expr.c gnu/usr.bin/cc/lib/expr.h gnu/usr.bin/cc/lib/final.c gnu/usr.bin/cc/lib/flags.h gnu/usr.bin/cc/lib/flow.c gnu/usr.bin/cc/lib/fold-const.c gnu/usr.bin/cc/lib/function.c gnu/usr.bin/cc/lib/function.h gnu/usr.bin/cc/lib/gbl-ctors.h gnu/usr.bin/cc/lib/getpwd.c gnu/usr.bin/cc/lib/glimits.h gnu/usr.bin/cc/lib/global.c gnu/usr.bin/cc/lib/gstddef.h gnu/usr.bin/cc/lib/gvarargs.h gnu/usr.bin/cc/lib/hard-reg-set.h gnu/usr.bin/cc/lib/i386/bsd.h gnu/usr.bin/cc/lib/i386/gas.h gnu/usr.bin/cc/lib/i386/gstabs.h gnu/usr.bin/cc/lib/i386/i386.h gnu/usr.bin/cc/lib/i386/perform.h gnu/usr.bin/cc/lib/i386/unix.h gnu/usr.bin/cc/lib/input.h gnu/usr.bin/cc/lib/insn-attr.h gnu/usr.bin/cc/lib/insn-attrtab.c gnu/usr.bin/cc/lib/insn-codes.h gnu/usr.bin/cc/lib/insn-config.h gnu/usr.bin/cc/lib/insn-emit.c gnu/usr.bin/cc/lib/insn-extract.c gnu/usr.bin/cc/lib/insn-flags.h gnu/usr.bin/cc/lib/insn-opinit.c gnu/usr.bin/cc/lib/insn-output.c gnu/usr.bin/cc/lib/insn-peep.c gnu/usr.bin/cc/lib/insn-recog.c gnu/usr.bin/cc/lib/integrate.c gnu/usr.bin/cc/lib/integrate.h gnu/usr.bin/cc/lib/jump.c gnu/usr.bin/cc/lib/lib.mk gnu/usr.bin/cc/lib/local-alloc.c gnu/usr.bin/cc/lib/longlong.h gnu/usr.bin/cc/lib/loop.c gnu/usr.bin/cc/lib/loop.h gnu/usr.bin/cc/lib/machmode.def gnu/usr.bin/cc/lib/machmode.h gnu/usr.bin/cc/lib/obstack.c gnu/usr.bin/cc/lib/obstack.h gnu/usr.bin/cc/lib/optabs.c gnu/usr.bin/cc/lib/output.h gnu/usr.bin/cc/lib/print-rtl.c gnu/usr.bin/cc/lib/print-tree.c gnu/usr.bin/cc/lib/real.c gnu/usr.bin/cc/lib/real.h gnu/usr.bin/cc/lib/recog.c gnu/usr.bin/cc/lib/recog.h gnu/usr.bin/cc/lib/reg-stack.c gnu/usr.bin/cc/lib/regclass.c gnu/usr.bin/cc/lib/regs.h gnu/usr.bin/cc/lib/reload.c gnu/usr.bin/cc/lib/reload.h gnu/usr.bin/cc/lib/reload1.c gnu/usr.bin/cc/lib/reorg.c gnu/usr.bin/cc/lib/rtl.c gnu/usr.bin/cc/lib/rtl.def gnu/usr.bin/cc/lib/rtl.h gnu/usr.bin/cc/lib/rtlanal.c gnu/usr.bin/cc/lib/sched.c gnu/usr.bin/cc/lib/sdbout.c gnu/usr.bin/cc/lib/stmt.c gnu/usr.bin/cc/lib/stor-layout.c gnu/usr.bin/cc/lib/stupid.c gnu/usr.bin/cc/lib/tconfig.h gnu/usr.bin/cc/lib/tm.h gnu/usr.bin/cc/lib/toplev.c gnu/usr.bin/cc/lib/tree.c gnu/usr.bin/cc/lib/tree.def gnu/usr.bin/cc/lib/tree.h gnu/usr.bin/cc/lib/typeclass.h gnu/usr.bin/cc/lib/unroll.c gnu/usr.bin/cc/lib/varasm.c gnu/usr.bin/cc/lib/version.c gnu/usr.bin/cc/lib/xcoffout.c gnu/usr.bin/cc/libgcc/Makefile gnu/usr.bin/cc/libgcc/_eprintf.c gnu/usr.bin/cc/libgcc/_fixunsdfdi.c gnu/usr.bin/cc/libgcc/_new_handler.c gnu/usr.bin/cc/libobjc/Makefile gnu/usr.bin/cc/libobjc/sendmsg.c gnu/usr.bin/cpio/COPYING gnu/usr.bin/cpio/COPYING.LIB gnu/usr.bin/cpio/ChangeLog gnu/usr.bin/cpio/Makefile gnu/usr.bin/cpio/NEWS gnu/usr.bin/cpio/README gnu/usr.bin/cpio/alloca.c gnu/usr.bin/cpio/copyin.c gnu/usr.bin/cpio/copyout.c gnu/usr.bin/cpio/copypass.c gnu/usr.bin/cpio/cpio.1 gnu/usr.bin/cpio/cpio.h gnu/usr.bin/cpio/cpiohdr.h gnu/usr.bin/cpio/defer.c gnu/usr.bin/cpio/defer.h gnu/usr.bin/cpio/dirname.c gnu/usr.bin/cpio/dstring.c gnu/usr.bin/cpio/dstring.h gnu/usr.bin/cpio/error.c gnu/usr.bin/cpio/extern.h gnu/usr.bin/cpio/filemode.c gnu/usr.bin/cpio/filetypes.h gnu/usr.bin/cpio/fnmatch.c gnu/usr.bin/cpio/fnmatch.h gnu/usr.bin/cpio/getopt.c gnu/usr.bin/cpio/getopt.h gnu/usr.bin/cpio/getopt1.c gnu/usr.bin/cpio/global.c gnu/usr.bin/cpio/idcache.c gnu/usr.bin/cpio/main.c gnu/usr.bin/cpio/makepath.c gnu/usr.bin/cpio/rmt.h gnu/usr.bin/cpio/rtapelib.c gnu/usr.bin/cpio/stripslash.c gnu/usr.bin/cpio/system.h gnu/usr.bin/cpio/tar.c gnu/usr.bin/cpio/tar.h gnu/usr.bin/cpio/tarhdr.h gnu/usr.bin/cpio/tcexparg.c gnu/usr.bin/cpio/userspec.c gnu/usr.bin/cpio/util.c gnu/usr.bin/cpio/version.c gnu/usr.bin/cpio/xmalloc.c gnu/usr.bin/cpio/xstrdup.c gnu/usr.bin/cvs/cvs/Makefile gnu/usr.bin/cvs/cvs/import.c gnu/usr.bin/cvs/cvs/update.c gnu/usr.bin/cvs/lib/Makefile gnu/usr.bin/cvs/mkmodules/Makefile gnu/usr.bin/dc/Makefile gnu/usr.bin/dc/dc.1 gnu/usr.bin/diff/COPYING gnu/usr.bin/diff/diff3.c gnu/usr.bin/diff/sdiff.c gnu/usr.bin/diff3/Makefile gnu/usr.bin/gdb/COPYING gnu/usr.bin/gdb/ChangeLog gnu/usr.bin/gdb/Gdbinit gnu/usr.bin/gdb/Makefile gnu/usr.bin/gdb/Makefile.dist gnu/usr.bin/gdb/Projects gnu/usr.bin/gdb/README.gnu gnu/usr.bin/gdb/XGdbinit.samp gnu/usr.bin/gdb/Xgdb.ad gnu/usr.bin/gdb/blockframe.c gnu/usr.bin/gdb/breakpoint.c gnu/usr.bin/gdb/command.c gnu/usr.bin/gdb/command.h gnu/usr.bin/gdb/config/Makefile.i386 gnu/usr.bin/gdb/config/default-dep.c gnu/usr.bin/gdb/config/i386-dep.c gnu/usr.bin/gdb/config/i386-pinsn.c gnu/usr.bin/gdb/config/i386bsd-dep.c gnu/usr.bin/gdb/config/m-i386-sv32.h gnu/usr.bin/gdb/config/m-i386.h gnu/usr.bin/gdb/config/m-i386bsd.h gnu/usr.bin/gdb/config/m-i386g-sv32.h gnu/usr.bin/gdb/config/m-i386gas.h gnu/usr.bin/gdb/copying.c gnu/usr.bin/gdb/core.c gnu/usr.bin/gdb/cplus-dem.c gnu/usr.bin/gdb/dbxread.c gnu/usr.bin/gdb/defs.h gnu/usr.bin/gdb/environ.c gnu/usr.bin/gdb/environ.h gnu/usr.bin/gdb/eval.c gnu/usr.bin/gdb/expprint.c gnu/usr.bin/gdb/expread.y gnu/usr.bin/gdb/expression.h gnu/usr.bin/gdb/findvar.c gnu/usr.bin/gdb/frame.h gnu/usr.bin/gdb/gdb.1 gnu/usr.bin/gdb/getpagesize.h gnu/usr.bin/gdb/infcmd.c gnu/usr.bin/gdb/inferior.h gnu/usr.bin/gdb/inflow.c gnu/usr.bin/gdb/infrun.c gnu/usr.bin/gdb/kgdb_proto.h gnu/usr.bin/gdb/main.c gnu/usr.bin/gdb/ngdb.i386/Makefile gnu/usr.bin/gdb/obstack.c gnu/usr.bin/gdb/obstack.h gnu/usr.bin/gdb/printcmd.c gnu/usr.bin/gdb/readline/ChangeLog gnu/usr.bin/gdb/readline/Makefile.gnu gnu/usr.bin/gdb/readline/chardefs.h gnu/usr.bin/gdb/readline/emacs_keymap.c gnu/usr.bin/gdb/readline/funmap.c gnu/usr.bin/gdb/readline/history.c gnu/usr.bin/gdb/readline/history.h gnu/usr.bin/gdb/readline/keymaps.c gnu/usr.bin/gdb/readline/keymaps.h gnu/usr.bin/gdb/readline/readline.c gnu/usr.bin/gdb/readline/readline.h gnu/usr.bin/gdb/readline/vi_keymap.c gnu/usr.bin/gdb/readline/vi_mode.c gnu/usr.bin/gdb/regex.c gnu/usr.bin/gdb/regex.h gnu/usr.bin/gdb/remote-sl.c gnu/usr.bin/gdb/remote.c gnu/usr.bin/gdb/source.c gnu/usr.bin/gdb/stab.def gnu/usr.bin/gdb/stack.c gnu/usr.bin/gdb/symmisc.c gnu/usr.bin/gdb/symseg.h gnu/usr.bin/gdb/symtab.c gnu/usr.bin/gdb/symtab.h gnu/usr.bin/gdb/utils.c gnu/usr.bin/gdb/valarith.c gnu/usr.bin/gdb/valops.c gnu/usr.bin/gdb/valprint.c gnu/usr.bin/gdb/value.h gnu/usr.bin/gdb/values.c gnu/usr.bin/gdb/version.c gnu/usr.bin/gdb/wait.h gnu/usr.bin/gdb/xgdb/Makefile gnu/usr.bin/gdb/xgdb/xgdb.c gnu/usr.bin/grep/AUTHORS gnu/usr.bin/grep/Makefile gnu/usr.bin/grep/NEWS gnu/usr.bin/grep/PROJECTS gnu/usr.bin/grep/README gnu/usr.bin/grep/dfa.c gnu/usr.bin/grep/dfa.h gnu/usr.bin/grep/getopt.c gnu/usr.bin/grep/getopt.h gnu/usr.bin/grep/getpagesize.h gnu/usr.bin/grep/grep.1 gnu/usr.bin/grep/grep.c gnu/usr.bin/grep/grep.h gnu/usr.bin/grep/kwset.c gnu/usr.bin/grep/kwset.h gnu/usr.bin/grep/obstack.c gnu/usr.bin/grep/obstack.h gnu/usr.bin/grep/regex.c gnu/usr.bin/grep/regex.h gnu/usr.bin/grep/search.c gnu/usr.bin/grep/tests/check.sh gnu/usr.bin/grep/tests/scriptgen.awk gnu/usr.bin/grep/tests/spencer.tests gnu/usr.bin/groff/Makefile gnu/usr.bin/groff/Makefile.cfg gnu/usr.bin/groff/afmtodit/Makefile gnu/usr.bin/groff/devices/Makefile.dev gnu/usr.bin/groff/grog/Makefile gnu/usr.bin/groff/indxbib/Makefile gnu/usr.bin/groff/libdriver/Makefile gnu/usr.bin/groff/man/Makefile gnu/usr.bin/groff/mm/Makefile gnu/usr.bin/groff/nroff/Makefile gnu/usr.bin/groff/tmac/Makefile gnu/usr.bin/groff/troff/Makefile gnu/usr.bin/groff/xditview/Makefile gnu/usr.bin/gzip/ChangeLog gnu/usr.bin/gzip/Makefile gnu/usr.bin/gzip/NEWS gnu/usr.bin/gzip/README gnu/usr.bin/gzip/THANKS gnu/usr.bin/gzip/TODO gnu/usr.bin/gzip/algorithm.doc gnu/usr.bin/gzip/bits.c gnu/usr.bin/gzip/crypt.c gnu/usr.bin/gzip/deflate.c gnu/usr.bin/gzip/getopt.c gnu/usr.bin/gzip/getopt.h gnu/usr.bin/gzip/gzexe gnu/usr.bin/gzip/gzexe.1 gnu/usr.bin/gzip/gzip.1 gnu/usr.bin/gzip/gzip.c gnu/usr.bin/gzip/gzip.h gnu/usr.bin/gzip/inflate.c gnu/usr.bin/gzip/lzw.c gnu/usr.bin/gzip/match.S gnu/usr.bin/gzip/revision.h gnu/usr.bin/gzip/tailor.h gnu/usr.bin/gzip/trees.c gnu/usr.bin/gzip/unlzh.c gnu/usr.bin/gzip/unlzw.c gnu/usr.bin/gzip/unpack.c gnu/usr.bin/gzip/unzip.c gnu/usr.bin/gzip/util.c gnu/usr.bin/gzip/zdiff gnu/usr.bin/gzip/zdiff.1 gnu/usr.bin/gzip/zforce gnu/usr.bin/gzip/zforce.1 gnu/usr.bin/gzip/zgrep gnu/usr.bin/gzip/zgrep.1 gnu/usr.bin/gzip/zip.c gnu/usr.bin/gzip/zmore gnu/usr.bin/gzip/zmore.1 gnu/usr.bin/gzip/znew gnu/usr.bin/gzip/znew.1 gnu/usr.bin/ld/Makefile gnu/usr.bin/ld/cplus-dem.c gnu/usr.bin/ld/ld.c gnu/usr.bin/ld/symseg.h gnu/usr.bin/man/Makefile gnu/usr.bin/man/Makefile.inc gnu/usr.bin/man/apropos/Makefile gnu/usr.bin/man/catman/Makefile gnu/usr.bin/man/catman/catman gnu/usr.bin/man/lib/Makefile gnu/usr.bin/man/lib/config.h_dist gnu/usr.bin/man/lib/util.c gnu/usr.bin/man/makewhatis/Makefile gnu/usr.bin/man/makewhatis/makewhatis.sh gnu/usr.bin/man/man/Makefile gnu/usr.bin/man/man/man.c gnu/usr.bin/man/manpath/Makefile gnu/usr.bin/man/whatis/Makefile gnu/usr.bin/patch/common.h gnu/usr.bin/pr/system.h gnu/usr.bin/rcs/Makefile gnu/usr.bin/rcs/Makefile.inc gnu/usr.bin/rcs/ci/Makefile gnu/usr.bin/rcs/co/Makefile gnu/usr.bin/rcs/ident/Makefile gnu/usr.bin/rcs/lib/Makefile gnu/usr.bin/rcs/lib/rcslex.c gnu/usr.bin/rcs/merge/Makefile gnu/usr.bin/rcs/rcs/Makefile gnu/usr.bin/rcs/rcsclean/Makefile gnu/usr.bin/rcs/rcsdiff/Makefile gnu/usr.bin/rcs/rcsfreeze/Makefile gnu/usr.bin/rcs/rcsmerge/Makefile gnu/usr.bin/rcs/rlog/Makefile gnu/usr.bin/rcs/rlog/rlog.c gnu/usr.bin/sdiff/Makefile gnu/usr.bin/sort/system.h gnu/usr.bin/tar/Makefile include/Makefile include/a.out.h include/arpa/inet.h include/arpa/nameser.h include/assert.h include/ctype.h include/db.h include/dirent.h include/err.h include/fnmatch.h include/fts.h include/glob.h include/math.h include/mpool.h include/ndbm.h include/netdb.h include/pwd.h include/rpcsvc/Makefile include/rpcsvc/bootparam_prot.x include/rpcsvc/klm_prot.x include/rpcsvc/mount.x include/rpcsvc/nfs_prot.x include/rpcsvc/nlm_prot.x include/rpcsvc/rex.x include/rpcsvc/rnusers.x include/rpcsvc/rquota.x include/rpcsvc/rstat.x include/rpcsvc/rwall.x include/rpcsvc/sm_inter.x include/rpcsvc/spray.x include/rpcsvc/yp.x include/rpcsvc/yp_prot.h include/rpcsvc/ypclnt.h include/rpcsvc/yppasswd.x include/setjmp.h include/stab.h include/stdio.h include/stdlib.h include/unistd.h include/vis.h lib/Makefile lib/csu.i386/Makefile lib/csu.i386/crt0.c lib/libc/compat-43/Makefile.inc lib/libc/db/Makefile.inc lib/libc/db/PORT/Makefile lib/libc/db/PORT/README lib/libc/db/PORT/clib/memmove.c lib/libc/db/PORT/clib/mktemp.c lib/libc/db/PORT/clib/realloc.c lib/libc/db/PORT/clib/snprintf.c lib/libc/db/PORT/include/cdefs.h lib/libc/db/PORT/include/compat.h lib/libc/db/PORT/include/db.h lib/libc/db/PORT/include/mpool.h lib/libc/db/PORT/include/ndbm.h lib/libc/db/VERSION lib/libc/db/btree/Makefile.inc lib/libc/db/btree/bt_close.c lib/libc/db/btree/bt_conv.c lib/libc/db/btree/bt_debug.c lib/libc/db/btree/bt_delete.c lib/libc/db/btree/bt_get.c lib/libc/db/btree/bt_open.c lib/libc/db/btree/bt_overflow.c lib/libc/db/btree/bt_page.c lib/libc/db/btree/bt_put.c lib/libc/db/btree/bt_search.c lib/libc/db/btree/bt_seq.c lib/libc/db/btree/bt_split.c lib/libc/db/btree/bt_stack.c lib/libc/db/btree/bt_utils.c lib/libc/db/btree/btree.h lib/libc/db/btree/extern.h lib/libc/db/db/Makefile.inc lib/libc/db/db/db.c lib/libc/db/doc/btree.3.ps lib/libc/db/doc/dbopen.3.ps lib/libc/db/doc/hash.3.ps lib/libc/db/doc/hash.ps lib/libc/db/doc/mpool.3.ps lib/libc/db/doc/recno.3.ps lib/libc/db/hash/Makefile.inc lib/libc/db/hash/README lib/libc/db/hash/extern.h lib/libc/db/hash/hash.c lib/libc/db/hash/hash.h lib/libc/db/hash/hash_bigkey.c lib/libc/db/hash/hash_buf.c lib/libc/db/hash/hash_func.c lib/libc/db/hash/hash_log2.c lib/libc/db/hash/hash_page.c lib/libc/db/hash/hsearch.c lib/libc/db/hash/ndbm.c lib/libc/db/hash/page.h lib/libc/db/hash/search.h lib/libc/db/man/Makefile.inc lib/libc/db/man/btree.3 lib/libc/db/man/dbopen.3 lib/libc/db/man/hash.3 lib/libc/db/man/mpool.3 lib/libc/db/man/recno.3 lib/libc/db/mpool/Makefile.inc lib/libc/db/mpool/README lib/libc/db/mpool/mpool.c lib/libc/db/recno/Makefile.inc lib/libc/db/recno/extern.h lib/libc/db/recno/rec_close.c lib/libc/db/recno/rec_delete.c lib/libc/db/recno/rec_get.c lib/libc/db/recno/rec_open.c lib/libc/db/recno/rec_put.c lib/libc/db/recno/rec_search.c lib/libc/db/recno/rec_seq.c lib/libc/db/recno/rec_utils.c lib/libc/db/recno/recno.h lib/libc/db/test/Makefile lib/libc/db/test/README lib/libc/db/test/btree.tests/main.c lib/libc/db/test/dbtest.c lib/libc/db/test/hash.tests/driver2.c lib/libc/db/test/hash.tests/makedb.sh lib/libc/db/test/hash.tests/tcreat3.c lib/libc/db/test/hash.tests/tdel.c lib/libc/db/test/hash.tests/testit lib/libc/db/test/hash.tests/thash4.c lib/libc/db/test/hash.tests/tread2.c lib/libc/db/test/hash.tests/tseq.c lib/libc/db/test/hash.tests/tverify.c lib/libc/db/test/run.test lib/libc/gen/Makefile.inc lib/libc/gen/README.crypt lib/libc/gen/assert.c lib/libc/gen/crypt.c lib/libc/gen/directory.c lib/libc/gen/err.3 lib/libc/gen/err.c lib/libc/gen/fnmatch.3 lib/libc/gen/fnmatch.c lib/libc/gen/fts.3 lib/libc/gen/fts.c lib/libc/gen/getcap.3 lib/libc/gen/getcap.c lib/libc/gen/getcwd.c lib/libc/gen/getgrent.3 lib/libc/gen/getpwent.c lib/libc/gen/getusershell.c lib/libc/gen/glob.3 lib/libc/gen/glob.c lib/libc/gen/infinity.c lib/libc/gen/insque.3 lib/libc/gen/insque.c lib/libc/gen/setjmp.3 lib/libc/gen/shmat.c lib/libc/gen/shmctl.c lib/libc/gen/shmdt.c lib/libc/gen/shmget.c lib/libc/gen/signal.c lib/libc/gen/syslog.c lib/libc/gen/tcsendbreak.3 lib/libc/gen/ttyname.c lib/libc/gen/utime.3 lib/libc/gen/utime.c lib/libc/gen/vis.c lib/libc/i386/DEFS.h lib/libc/i386/SYS.h lib/libc/i386/gen/_setjmp.s lib/libc/i386/gen/fixdfsi.s lib/libc/i386/gen/fixunsdfsi.s lib/libc/i386/gen/ldexp.c lib/libc/i386/gen/setjmp.s lib/libc/i386/gen/sigsetjmp.s lib/libc/i386/stdlib/abs.s lib/libc/i386/stdlib/div.s lib/libc/i386/stdlib/labs.s lib/libc/i386/stdlib/ldiv.s lib/libc/i386/string/bcmp.s lib/libc/i386/string/bcopy.s lib/libc/i386/string/bzero.s lib/libc/i386/string/ffs.s lib/libc/i386/string/index.s lib/libc/i386/string/memchr.s lib/libc/i386/string/memcmp.s lib/libc/i386/string/memmove.s lib/libc/i386/string/memset.s lib/libc/i386/string/rindex.s lib/libc/i386/string/strcat.s lib/libc/i386/string/strchr.s lib/libc/i386/string/strcmp.s lib/libc/i386/string/strcpy.s lib/libc/i386/string/strlen.s lib/libc/i386/string/strncmp.s lib/libc/i386/string/strrchr.s lib/libc/i386/sys/syscall.s lib/libc/net/Makefile.inc lib/libc/net/gethostnamadr.c lib/libc/net/inet_addr.c lib/libc/stdio/Makefile.inc lib/libc/stdio/fgetpos.c lib/libc/stdio/fread.c lib/libc/stdio/local.h lib/libc/stdio/makebuf.c lib/libc/stdio/scanf.3 lib/libc/stdio/setvbuf.c lib/libc/stdio/sscanf.c lib/libc/stdio/tempnam.c lib/libc/stdio/tmpnam.c lib/libc/stdio/vfprintf.c lib/libc/stdio/vfscanf.c lib/libc/stdlib/Makefile.inc lib/libc/stdlib/_rand48.c lib/libc/stdlib/atof.c lib/libc/stdlib/drand48.c lib/libc/stdlib/erand48.c lib/libc/stdlib/jrand48.c lib/libc/stdlib/lcong48.c lib/libc/stdlib/lrand48.c lib/libc/stdlib/mrand48.c lib/libc/stdlib/nrand48.c lib/libc/stdlib/rand48.3 lib/libc/stdlib/rand48.h lib/libc/stdlib/seed48.c lib/libc/stdlib/srand48.c lib/libc/stdlib/strtod.c lib/libc/string/Makefile.inc lib/libc/string/strftime.3 lib/libc/string/strftime.c lib/libc/sys/Makefile.inc lib/libc/sys/execve.2 lib/libc/sys/getdomainname.2 lib/libc/sys/getpgrp.2 lib/libc/sys/reboot.2 lib/libc/sys/uname.2 lib/libcrypt/Makefile lib/libcrypt/auth-proto.h lib/libcrypt/auth.c lib/libcrypt/auth.h lib/libcrypt/crypt.c lib/libcrypt/enc-proto.h lib/libcrypt/enc_des.c lib/libcrypt/encrypt.c lib/libcrypt/encrypt.h lib/libcrypt/kerberos.c lib/libcrypt/kerberos5.c lib/libcrypt/key-proto.h lib/libcrypt/krb_des.c lib/libcrypt/misc-proto.h lib/libcrypt/misc.c lib/libcrypt/misc.h lib/libcurses/Makefile lib/libcurses/addbytes.c lib/libcurses/addch.c lib/libcurses/addstr.c lib/libcurses/box.c lib/libcurses/clrtobot.c lib/libcurses/clrtoeol.c lib/libcurses/cr_put.c lib/libcurses/curses.h lib/libcurses/delch.c lib/libcurses/deleteln.c lib/libcurses/erase.c lib/libcurses/getch.c lib/libcurses/getstr.c lib/libcurses/insch.c lib/libcurses/insertln.c lib/libcurses/newwin.c lib/libcurses/overlay.c lib/libcurses/overwrite.c lib/libcurses/printw.c lib/libcurses/refresh.c lib/libm/Makefile lib/libm/common/sincos.c lib/libm/common_source/erf.c lib/libm/common_source/exp.c lib/libm/common_source/floor.c lib/libm/common_source/gamma.c lib/libm/common_source/j0.c lib/libm/common_source/j1.c lib/libm/common_source/jn.c lib/libm/common_source/lgamma.3 lib/libm/common_source/lgamma.c lib/libm/common_source/log.c lib/libm/common_source/mathimpl.h lib/libm/common_source/pow.c lib/librpc/Makefile lib/librpc/man/Makefile lib/librpc/man/man1/Makefile lib/librpc/man/man3/Makefile lib/librpc/man/man5/Makefile lib/librpc/man/man8/Makefile lib/librpc/rpc/Makefile lib/librpc/rpc/auth.h lib/librpc/rpc/auth_none.c lib/librpc/rpc/auth_unix.c lib/librpc/rpc/auth_unix.h lib/librpc/rpc/authunix_prot.c lib/librpc/rpc/bindresvport.c lib/librpc/rpc/clnt.h lib/librpc/rpc/clnt_generic.c lib/librpc/rpc/clnt_perror.c lib/librpc/rpc/clnt_raw.c lib/librpc/rpc/clnt_simple.c lib/librpc/rpc/clnt_tcp.c lib/librpc/rpc/clnt_udp.c lib/librpc/rpc/get_myaddress.c lib/librpc/rpc/getrpcent.c lib/librpc/rpc/getrpcport.c lib/librpc/rpc/pmap_clnt.c lib/librpc/rpc/pmap_clnt.h lib/librpc/rpc/pmap_getmaps.c lib/librpc/rpc/pmap_getport.c lib/librpc/rpc/pmap_prot.c lib/librpc/rpc/pmap_prot.h lib/librpc/rpc/pmap_prot2.c lib/librpc/rpc/pmap_rmt.c lib/librpc/rpc/pmap_rmt.h lib/librpc/rpc/rpc.h lib/librpc/rpc/rpc_callmsg.c lib/librpc/rpc/rpc_commondata.c lib/librpc/rpc/rpc_dtablesize.c lib/librpc/rpc/rpc_msg.h lib/librpc/rpc/rpc_prot.c lib/librpc/rpc/svc.c lib/librpc/rpc/svc.h lib/librpc/rpc/svc_auth.c lib/librpc/rpc/svc_auth.h lib/librpc/rpc/svc_auth_unix.c lib/librpc/rpc/svc_raw.c lib/librpc/rpc/svc_run.c lib/librpc/rpc/svc_simple.c lib/librpc/rpc/svc_tcp.c lib/librpc/rpc/svc_udp.c lib/librpc/rpc/types.h lib/librpc/rpc/xdr.c lib/librpc/rpc/xdr.h lib/librpc/rpc/xdr_array.c lib/librpc/rpc/xdr_float.c lib/librpc/rpc/xdr_mem.c lib/librpc/rpc/xdr_rec.c lib/librpc/rpc/xdr_reference.c lib/librpc/rpc/xdr_stdio.c lib/librpcsvc/Makefile lib/libtelnet/Makefile lib/libterm/Makefile lib/libutil/Makefile libexec/Makefile libexec/bugfiler/Makefile libexec/bugfiler/bug.h libexec/bugfiler/bugformat libexec/bugfiler/pathnames.h libexec/bugfiler/sendbug.1 libexec/bugfiler/sendbug.sh libexec/comsat/Makefile libexec/crond/Makefile libexec/elvispreserve/Makefile libexec/elvispreserve/elvispreserve.8 libexec/elvispreserve/elvispreserve.c libexec/elvispreserve/prsvunix.c libexec/fingerd/Makefile libexec/ftpd/Makefile libexec/ftpd/ftpd.c libexec/getty/Makefile libexec/getty/gettytab.c libexec/getty/main.c libexec/getty/subr.c libexec/getty/ttys.5 libexec/mail.local/Makefile libexec/makekey/Makefile libexec/pppd/Makefile libexec/pppd/args.h libexec/pppd/callout.h libexec/pppd/chap.c libexec/pppd/chap.h libexec/pppd/fsm.c libexec/pppd/fsm.h libexec/pppd/ipcp.c libexec/pppd/ipcp.h libexec/pppd/lcp.c libexec/pppd/lcp.h libexec/pppd/logwtmp.c libexec/pppd/magic.c libexec/pppd/magic.h libexec/pppd/main.c libexec/pppd/md5.c libexec/pppd/md5.h libexec/pppd/md5driver.c libexec/pppd/patchlevel.h libexec/pppd/pathnames.h libexec/pppd/pppd.8 libexec/pppd/pppd.h libexec/pppd/upap.c libexec/pppd/upap.h libexec/rexecd/Makefile libexec/rlogind/Makefile libexec/rpc.rstatd/Makefile libexec/rpc.rstatd/rpc.rstatd.8 libexec/rpc.rstatd/rstat_proc.c libexec/rpc.rstatd/rstatd.c libexec/rpc.rusersd/Makefile libexec/rpc.rusersd/rpc.rusersd.8 libexec/rpc.rusersd/rusers_proc.c libexec/rpc.rusersd/rusersd.c libexec/rpc.rwalld/Makefile libexec/rpc.rwalld/rpc.rwalld.8 libexec/rpc.rwalld/rwalld.c libexec/rshd/Makefile libexec/talkd/Makefile libexec/telnetd/Makefile libexec/tftpd/Makefile libexec/tftpd/tftpd.c libexec/uucpd/Makefile sbin/Makefile sbin/XNSrouted/Makefile sbin/XNSrouted/XNSrouted.8 sbin/XNSrouted/af.c sbin/XNSrouted/af.h sbin/XNSrouted/defs.h sbin/XNSrouted/if.c sbin/XNSrouted/input.c sbin/XNSrouted/interface.h sbin/XNSrouted/main.c sbin/XNSrouted/output.c sbin/XNSrouted/protocol.h sbin/XNSrouted/startup.c sbin/XNSrouted/table.h sbin/XNSrouted/tables.c sbin/XNSrouted/timer.c sbin/XNSrouted/tools/query.c sbin/XNSrouted/trace.c sbin/XNSrouted/trace.h sbin/badsect/Makefile sbin/disklabel/Makefile sbin/disklabel/disklabel.5 sbin/disklabel/disklabel.c sbin/dmesg/Makefile sbin/dmesg/dmesg.8 sbin/dump/Makefile sbin/dump/dump.8 sbin/dump/dump.h sbin/dump/dumpitime.c sbin/dump/dumpmain.c sbin/dump/dumpoptr.c sbin/dump/dumprmt.c sbin/dump/dumptape.c sbin/dump/dumptraverse.c sbin/dump/pathnames.h sbin/dump/rdump.8 sbin/dump/unctime.c sbin/dumpfs/Makefile sbin/fastboot/Makefile sbin/fdisk/Makefile sbin/fdisk/fdisk.8 sbin/fsck/Makefile sbin/fsck/dir.c sbin/fsck/fsck.8 sbin/fsck/fsck.h sbin/fsck/inode.c sbin/fsck/main.c sbin/fsck/pass1.c sbin/fsck/pass1b.c sbin/fsck/pass2.c sbin/fsck/pass3.c sbin/fsck/pass4.c sbin/fsck/pass5.c sbin/fsck/preen.c sbin/fsck/setup.c sbin/fsck/utilities.c sbin/halt/Makefile sbin/ifconfig/Makefile sbin/ifconfig/ifconfig.c sbin/init.bsdi/Makefile sbin/init.bsdi/README sbin/init.bsdi/init.8 sbin/init.bsdi/init.c sbin/init.bsdi/pathnames.h sbin/init.chmr/Makefile sbin/init.chmr/README sbin/init.chmr/cf_defs.h sbin/init.chmr/cf_table.c sbin/init.chmr/configure.c sbin/init.chmr/fake_syslog.c sbin/init.chmr/init.8 sbin/init.chmr/init.c sbin/init.chmr/init.conf sbin/init.chmr/init.h sbin/init.chmr/libutil.h sbin/init.chmr/prototypes.h sbin/init.chmr/ttytab.c sbin/init.chmr/utils.c sbin/init/Makefile sbin/init/init.c sbin/mknod/Makefile sbin/mount/Makefile sbin/mount/mount.8 sbin/mount/mount.c sbin/mount_isofs/Makefile sbin/mount_isofs/mount_isofs.8 sbin/mount_isofs/mount_isofs.c sbin/mountd/Makefile sbin/newfs/Makefile sbin/newfs/newfs.c sbin/nfsd/Makefile sbin/nfsiod/Makefile sbin/ping/Makefile sbin/ping/ping.c sbin/quotacheck/Makefile sbin/reboot/Makefile sbin/reboot/reboot_i386.8 sbin/restore/Makefile sbin/route/Makefile sbin/route/route.c sbin/routed/Makefile sbin/savecore/Makefile sbin/shutdown/Makefile sbin/slattach/Makefile sbin/slattach/slattach.8 sbin/slattach/slattach.c sbin/st/Makefile sbin/st/st.8 sbin/st/st.c sbin/swapon/Makefile sbin/tunefs/Makefile sbin/umount/umount.c share/Makefile share/doc/Makefile share/doc/ps1/04.pascal/Makefile share/doc/ps1/04.pascal/csfix.c share/doc/ps1/06.sysman/Makefile share/doc/ps1/06.sysman/a.t share/doc/ps1/07.ipctut/Makefile share/doc/ps1/07.ipctut/dgramread.c share/doc/ps1/07.ipctut/dgramsend.c share/doc/ps1/07.ipctut/pipe.c share/doc/ps1/07.ipctut/socketpair.c share/doc/ps1/07.ipctut/strchkread.c share/doc/ps1/07.ipctut/streamread.c share/doc/ps1/07.ipctut/streamwrite.c share/doc/ps1/07.ipctut/udgramread.c share/doc/ps1/07.ipctut/udgramsend.c share/doc/ps1/07.ipctut/ustreamread.c share/doc/ps1/07.ipctut/ustreamwrite.c share/doc/ps1/08.ipc/5.t share/doc/ps1/08.ipc/Makefile share/doc/ps1/13.rcs/Makefile share/doc/ps1/13.rcs/man/Makefile share/doc/ps1/13.rcs/man/ci.1 share/doc/ps1/13.rcs/man/co.1 share/doc/ps1/13.rcs/man/ident.1 share/doc/ps1/13.rcs/man/merge.1 share/doc/ps1/13.rcs/man/rcs.1 share/doc/ps1/13.rcs/man/rcsdiff.1 share/doc/ps1/13.rcs/man/rcsfile.5 share/doc/ps1/13.rcs/man/rcsintro.1 share/doc/ps1/13.rcs/man/rcsmerge.1 share/doc/ps1/13.rcs/man/rlog.1 share/doc/ps1/13.rcs/man/sccstorcs.1 share/doc/ps1/13.rcs/rcs.ms share/doc/ps1/14.sccs/Makefile share/doc/ps1/18.curses/Makefile share/doc/ps1/18.curses/appen.B share/doc/ps1/18.curses/appen.C share/doc/ps1/18.curses/doc.III share/doc/ps1/18.curses/intro.0 share/doc/ps1/18.curses/intro.1 share/doc/ps1/18.curses/intro.2 share/doc/ps1/18.curses/intro.3 share/doc/ps1/18.curses/intro.4 share/doc/ps1/18.curses/intro.5 share/doc/ps1/18.curses/life.c share/doc/ps1/18.curses/macros share/doc/ps1/18.curses/twinkle1.c share/doc/ps1/18.curses/twinkle2.c share/doc/ps1/18.curses/win_st.c share/doc/ps1/Makefile share/doc/smm/01.setup/Makefile share/doc/smm/01.setup/common/4.t share/doc/smm/01.setup/common/renohints.t share/doc/smm/01.setup/tahoe/1.t share/doc/smm/01.setup/tahoe/Makefile share/doc/smm/01.setup/vax/Makefile share/doc/smm/02.config/6.t share/doc/smm/02.config/Makefile share/doc/smm/04.quotas/Makefile share/doc/smm/05.fsck/Makefile share/doc/smm/06.lpd/Makefile share/doc/smm/07.sendmailop/Makefile share/doc/smm/08.timedop/Makefile share/doc/smm/10.newsop/Makefile share/doc/smm/11.named/Makefile share/doc/smm/14.fastfs/Makefile share/doc/smm/15.net/7.t share/doc/smm/15.net/Makefile share/doc/smm/15.net/a.t share/doc/smm/16.sendmail/Makefile share/doc/smm/20.termdesc/Makefile share/doc/smm/22.timed/Makefile share/doc/smm/Makefile share/doc/usd/04.csh/Makefile share/doc/usd/04.csh/csh.1 share/doc/usd/04.csh/csh.2 share/doc/usd/04.csh/csh.3 share/doc/usd/04.csh/csh.4 share/doc/usd/04.csh/csh.a share/doc/usd/07.Mail/Makefile share/doc/usd/09.newsread/Makefile share/doc/usd/10.etiq/Makefile share/doc/usd/11.notes/0.long share/doc/usd/11.notes/2.1 share/doc/usd/11.notes/2.2 share/doc/usd/11.notes/3.1 share/doc/usd/11.notes/3.2 share/doc/usd/11.notes/4.0 share/doc/usd/11.notes/4.1 share/doc/usd/11.notes/4.2 share/doc/usd/11.notes/4.3 share/doc/usd/11.notes/4.4 share/doc/usd/11.notes/4.5 share/doc/usd/11.notes/4.6 share/doc/usd/11.notes/4.7 share/doc/usd/11.notes/Makefile share/doc/usd/14.edit/Makefile share/doc/usd/14.edit/edittut.ms share/doc/usd/15.vi/Makefile share/doc/usd/15.vi/vi.chars share/doc/usd/15.vi/vi.in share/doc/usd/15.vi/vi.summary share/doc/usd/16.ex/Makefile share/doc/usd/21.msdiffs/Makefile share/doc/usd/22.memacros/Makefile share/doc/usd/23.meref/Makefile share/doc/usd/33.rogue/Makefile share/doc/usd/34.trek/Makefile share/doc/usd/Makefile share/man/Makefile share/man/man1/Makefile share/man/man3/Makefile share/man/man3/fpgetround.3 share/man/man4/Makefile share/man/man4/ddb.4 share/man/man4/man4.i386/Makefile share/man/man4/man4.i386/com.4 share/man/man4/man4.i386/keyboard.4 share/man/man4/man4.i386/lpa.4 share/man/man4/man4.i386/lpt.4 share/man/man4/man4.i386/mse.4 share/man/man4/man4.i386/npx.4 share/man/man4/man4.i386/screen.4 share/man/man4/man4.i386/sio.4 share/man/man4/sd.4 share/man/man4/st.4 share/man/man4/termios.4 share/man/man5/Makefile share/man/man5/core.5 share/man/man7/Makefile share/man/man7/hier.7 share/man/man7/mdoc.samples.7 share/man/man8/Makefile share/man/man8/man8.i386/Makefile share/me/Makefile share/me/footnote.me share/me/me.7 share/misc/zipcodes share/mk/bsd.README share/mk/bsd.doc.mk share/mk/bsd.lib.mk share/mk/bsd.man.mk share/mk/bsd.own.mk share/mk/bsd.prog.mk share/mk/bsd.subdir.mk share/mk/sys.mk share/syscons/Makefile share/syscons/fonts/Makefile share/syscons/fonts/alt-8x14 share/syscons/fonts/alt-8x16 share/syscons/fonts/alt-8x8 share/syscons/fonts/alt8x16.fnt share/syscons/fonts/altc-8x16 share/syscons/fonts/cp850-8x14 share/syscons/fonts/cp850-8x16 share/syscons/fonts/cp850-8x8 share/syscons/fonts/cp865-8x14 share/syscons/fonts/cp865-8x16 share/syscons/fonts/cp865-8x8 share/syscons/fonts/iso-8x14 share/syscons/fonts/iso-8x16 share/syscons/fonts/iso-8x8 share/syscons/fonts/koi8-8x14 share/syscons/fonts/koi8-8x16 share/syscons/fonts/koi8-8x8 share/syscons/fonts/koi8c-8x16 share/syscons/keymaps/Makefile share/syscons/keymaps/danish.cp865 share/syscons/keymaps/danish.iso share/syscons/keymaps/fff share/syscons/keymaps/german.cp850 share/syscons/keymaps/german.iso share/syscons/keymaps/mkkbdfil.c share/syscons/keymaps/ru.koi8.map share/syscons/keymaps/rus.koi8.map share/syscons/keymaps/swedish.cp850 share/syscons/keymaps/swedish.iso share/syscons/keymaps/uk.cp850 share/syscons/keymaps/uk.iso share/syscons/keymaps/us.iso share/syscons/scrnmaps/Makefile share/syscons/scrnmaps/koi82alt.scr share/termcap/Makefile share/termcap/reorder share/tmac/Makefile share/tmac/doc-syms share/tmac/tmac.andoc share/zoneinfo/Makefile share/zoneinfo/datfiles/russia sys/ROADMAP sys/compile/.keep_me sys/conf/files sys/conf/newvers.sh sys/conf/nfsswapvmunix.c sys/conf/param.c sys/ddb/db_access.c sys/ddb/db_access.h sys/ddb/db_aout.c sys/ddb/db_break.c sys/ddb/db_break.h sys/ddb/db_command.c sys/ddb/db_command.h sys/ddb/db_examine.c sys/ddb/db_expr.c sys/ddb/db_input.c sys/ddb/db_lex.c sys/ddb/db_lex.h sys/ddb/db_output.c sys/ddb/db_output.h sys/ddb/db_print.c sys/ddb/db_run.c sys/ddb/db_sym.c sys/ddb/db_sym.h sys/ddb/db_trap.c sys/ddb/db_variables.c sys/ddb/db_variables.h sys/ddb/db_watch.c sys/ddb/db_watch.h sys/ddb/db_write_cmd.c sys/i386/Makefile sys/i386/boot/Makefile sys/i386/boot/README.386BSD sys/i386/boot/README.MACH sys/i386/boot/asm.S sys/i386/boot/asm.h sys/i386/boot/bios.S sys/i386/boot/boot.c sys/i386/boot/boot.h sys/i386/boot/boot2.S sys/i386/boot/disk.c sys/i386/boot/io.c sys/i386/boot/rmaouthdr sys/i386/boot/start.S sys/i386/boot/sys.c sys/i386/boot/table.c sys/i386/conf/GENERICAH sys/i386/conf/GENERICBT sys/i386/conf/LINT sys/i386/conf/Makefile.i386 sys/i386/conf/SYSCONS sys/i386/conf/devices.i386 sys/i386/conf/files.i386 sys/i386/doc/config_options.doc sys/i386/doc/ed.relnotes sys/i386/doc/sound.doc sys/i386/doc/vm_layout.doc sys/i386/i386/autoconf.c sys/i386/i386/conf.c sys/i386/i386/cons.c sys/i386/i386/cons.h sys/i386/i386/db_disasm.c sys/i386/i386/db_interface.c sys/i386/i386/db_trace.c sys/i386/i386/dkbad.c sys/i386/i386/genassym.c sys/i386/i386/in_cksum.c sys/i386/i386/locore.s sys/i386/i386/machdep.c sys/i386/i386/math_emu.h sys/i386/i386/math_emulate.c sys/i386/i386/mem.c sys/i386/i386/microtime.s sys/i386/i386/ns_cksum.c sys/i386/i386/pmap.c sys/i386/i386/swapgeneric.c sys/i386/i386/symbols.raw sys/i386/i386/sys_machdep.c sys/i386/i386/trap.c sys/i386/i386/vm_machdep.c sys/i386/include/ansi.h sys/i386/include/console.h sys/i386/include/cpu.h sys/i386/include/cpufunc.h sys/i386/include/cputypes.h sys/i386/include/db_machdep.h sys/i386/include/dkio.h sys/i386/include/eflags.h sys/i386/include/endian.h sys/i386/include/float.h sys/i386/include/floatingpoint.h sys/i386/include/frame.h sys/i386/include/ioctl_pc.h sys/i386/include/limits.h sys/i386/include/mtpr.h sys/i386/include/npx.h sys/i386/include/param.h sys/i386/include/pc/display.h sys/i386/include/pc/msdos.h sys/i386/include/pcb.h sys/i386/include/pio.h sys/i386/include/pmap.h sys/i386/include/proc.h sys/i386/include/psl.h sys/i386/include/pte.h sys/i386/include/reg.h sys/i386/include/segments.h sys/i386/include/soundcard.h sys/i386/include/specialreg.h sys/i386/include/stdarg.h sys/i386/include/sysarch.h sys/i386/include/trap.h sys/i386/include/tss.h sys/i386/include/types.h sys/i386/include/vmparam.h sys/i386/isa/TODO sys/i386/isa/aha1742.c sys/i386/isa/bt742a.c sys/i386/isa/clock.c sys/i386/isa/com.c sys/i386/isa/comreg.h sys/i386/isa/debug.h sys/i386/isa/fd.c sys/i386/isa/fdreg.h sys/i386/isa/ic/i8042.h sys/i386/isa/ic/i8237.h sys/i386/isa/ic/i82586.h sys/i386/isa/ic/nec765.h sys/i386/isa/ic/ns16450.h sys/i386/isa/ic/ns16550.h sys/i386/isa/icu.h sys/i386/isa/icu.s sys/i386/isa/if_ed.c sys/i386/isa/if_edreg.h sys/i386/isa/if_ie.c sys/i386/isa/if_iereg.h sys/i386/isa/if_is.c sys/i386/isa/if_isreg.h sys/i386/isa/isa.c sys/i386/isa/isa.h sys/i386/isa/isa_device.h sys/i386/isa/iso8859.font sys/i386/isa/kbd.h sys/i386/isa/kbdtables.h sys/i386/isa/lpa.c sys/i386/isa/lpt.c sys/i386/isa/lptreg.h sys/i386/isa/mcd.c sys/i386/isa/mcdreg.h sys/i386/isa/mse.c sys/i386/isa/npx.c sys/i386/isa/pccons.c sys/i386/isa/rtc.h sys/i386/isa/sio.c sys/i386/isa/sound/COPYING sys/i386/isa/sound/HOWTO_MIDI sys/i386/isa/sound/README sys/i386/isa/sound/RELNOTES sys/i386/isa/sound/RELNOTES.Linux sys/i386/isa/sound/adlib_card.c sys/i386/isa/sound/audio.c sys/i386/isa/sound/debug.h sys/i386/isa/sound/dev_table.c sys/i386/isa/sound/dev_table.h sys/i386/isa/sound/dmabuf.c sys/i386/isa/sound/dsp.c sys/i386/isa/sound/finetune.h sys/i386/isa/sound/gus_card.c sys/i386/isa/sound/gus_hw.h sys/i386/isa/sound/gus_midi.c sys/i386/isa/sound/gus_vol.c sys/i386/isa/sound/gus_wave.c sys/i386/isa/sound/gustest/Makefile sys/i386/isa/sound/gustest/Readme sys/i386/isa/sound/gustest/gmidi.h sys/i386/isa/sound/gustest/gmod.c sys/i386/isa/sound/gustest/gpatinfo.c sys/i386/isa/sound/gustest/gusload.c sys/i386/isa/sound/gustest/midithru.c sys/i386/isa/sound/gustest/pmtest.c sys/i386/isa/sound/local.h sys/i386/isa/sound/midi.c sys/i386/isa/sound/midibuf.c sys/i386/isa/sound/mpu401.c sys/i386/isa/sound/opl3.c sys/i386/isa/sound/opl3.h sys/i386/isa/sound/os.h sys/i386/isa/sound/pas.h sys/i386/isa/sound/pas2_card.c sys/i386/isa/sound/pas2_midi.c sys/i386/isa/sound/pas2_mixer.c sys/i386/isa/sound/pas2_pcm.c sys/i386/isa/sound/patmgr.c sys/i386/isa/sound/pro_midi.c sys/i386/isa/sound/sb_card.c sys/i386/isa/sound/sb_dsp.c sys/i386/isa/sound/sequencer.c sys/i386/isa/sound/sound_calls.h sys/i386/isa/sound/sound_config.h sys/i386/isa/sound/soundcard.c sys/i386/isa/sound/soundcard.h sys/i386/isa/sound/tuning.h sys/i386/isa/sound/ulaw.h sys/i386/isa/sound/ultrasound.h sys/i386/isa/spkr.c sys/i386/isa/syscons.c sys/i386/isa/timerreg.h sys/i386/isa/ultra14f.c sys/i386/isa/vector.s sys/i386/isa/wd.c sys/i386/isa/wdreg.h sys/i386/isa/wt.c sys/i386/isa/wtreg.h sys/i386/stand/Makefile sys/i386/stand/as.c sys/i386/stand/asbootblk.c sys/i386/stand/bmap.c sys/i386/stand/boot.c sys/i386/stand/breadxx.c sys/i386/stand/cga.c sys/i386/stand/fd.c sys/i386/stand/fdbootblk.c sys/i386/stand/fs.c sys/i386/stand/kbd.c sys/i386/stand/prf.c sys/i386/stand/saio.h sys/i386/stand/srt0.c sys/i386/stand/trimhd.c sys/i386/stand/wd.c sys/i386/stand/wdbootblk.c sys/isofs/TODO sys/isofs/iso.h sys/isofs/iso_rrip.h sys/isofs/isofs_bmap.c sys/isofs/isofs_lookup.c sys/isofs/isofs_node.c sys/isofs/isofs_node.h sys/isofs/isofs_rrip.c sys/isofs/isofs_rrip.h sys/isofs/isofs_util.c sys/isofs/isofs_vfsops.c sys/isofs/isofs_vnops.c sys/kern/Makefile sys/kern/dead_vnops.c sys/kern/fifo_vnops.c sys/kern/init_main.c sys/kern/init_sysent.c sys/kern/kern__physio.c sys/kern/kern_acct.c sys/kern/kern_clock.c sys/kern/kern_descrip.c sys/kern/kern_execve.c sys/kern/kern_exit.c sys/kern/kern_fork.c sys/kern/kern_kinfo.c sys/kern/kern_ktrace.c sys/kern/kern_malloc.c sys/kern/kern_proc.c sys/kern/kern_prot.c sys/kern/kern_resource.c sys/kern/kern_sig.c sys/kern/kern_subr.c sys/kern/kern_synch.c sys/kern/kern_time.c sys/kern/kern_xxx.c sys/kern/makesyscalls.sh sys/kern/spec_vnops.c sys/kern/subr_log.c sys/kern/subr_mcount.c sys/kern/subr_prf.c sys/kern/subr_rlist.c sys/kern/subr_xxx.c sys/kern/sys_generic.c sys/kern/sys_process.c sys/kern/sys_socket.c sys/kern/syscalls.c sys/kern/syscalls.master sys/kern/sysv_shm.c sys/kern/tty.c sys/kern/tty_compat.c sys/kern/tty_conf.c sys/kern/tty_pty.c sys/kern/tty_ring.c sys/kern/tty_tb.c sys/kern/tty_tty.c sys/kern/uipc_domain.c sys/kern/uipc_mbuf.c sys/kern/uipc_proto.c sys/kern/uipc_socket.c sys/kern/uipc_socket2.c sys/kern/uipc_syscalls.c sys/kern/uipc_usrreq.c sys/kern/vfs__bio.c sys/kern/vfs_bio.old.c sys/kern/vfs_cache.c sys/kern/vfs_conf.c sys/kern/vfs_lookup.c sys/kern/vfs_subr.c sys/kern/vfs_syscalls.c sys/kern/vfs_vnops.c sys/net/af.c sys/net/af.h sys/net/bpf.c sys/net/bpf.h sys/net/bpf_filter.c sys/net/bpfdesc.h sys/net/if.c sys/net/if.h sys/net/if_arp.h sys/net/if_dl.h sys/net/if_ethersubr.c sys/net/if_llc.h sys/net/if_loop.c sys/net/if_ppp.c sys/net/if_ppp.h sys/net/if_sl.c sys/net/if_slvar.h sys/net/if_tun.c sys/net/if_types.h sys/net/netisr.h sys/net/ppp.h sys/net/radix.c sys/net/radix.h sys/net/raw_cb.c sys/net/raw_cb.h sys/net/raw_usrreq.c sys/net/route.c sys/net/route.h sys/net/rtsock.c sys/net/slcompress.c sys/net/slcompress.h sys/net/slip.h sys/netccitt/README.hdlc sys/netccitt/README.packet sys/netccitt/ccitt_proto.c sys/netccitt/hd_debug.c sys/netccitt/hd_input.c sys/netccitt/hd_output.c sys/netccitt/hd_subr.c sys/netccitt/hd_timer.c sys/netccitt/hd_var.h sys/netccitt/hdlc.h sys/netccitt/if_x25subr.c sys/netccitt/pk.h sys/netccitt/pk_acct.c sys/netccitt/pk_debug.c sys/netccitt/pk_input.c sys/netccitt/pk_output.c sys/netccitt/pk_subr.c sys/netccitt/pk_timer.c sys/netccitt/pk_usrreq.c sys/netccitt/pk_var.h sys/netccitt/x25.h sys/netccitt/x25acct.h sys/netccitt/x25err.h sys/netinet/icmp_var.h sys/netinet/if_ether.c sys/netinet/if_ether.h sys/netinet/in.c sys/netinet/in.h sys/netinet/in_cksum.c sys/netinet/in_pcb.c sys/netinet/in_pcb.h sys/netinet/in_proto.c sys/netinet/in_systm.h sys/netinet/in_var.h sys/netinet/ip.h sys/netinet/ip_icmp.c sys/netinet/ip_icmp.h sys/netinet/ip_input.c sys/netinet/ip_output.c sys/netinet/ip_var.h sys/netinet/raw_ip.c sys/netinet/tcp.h sys/netinet/tcp_debug.c sys/netinet/tcp_debug.h sys/netinet/tcp_fsm.h sys/netinet/tcp_input.c sys/netinet/tcp_output.c sys/netinet/tcp_seq.h sys/netinet/tcp_subr.c sys/netinet/tcp_timer.c sys/netinet/tcp_timer.h sys/netinet/tcp_usrreq.c sys/netinet/tcp_var.h sys/netinet/tcpip.h sys/netinet/udp.h sys/netinet/udp_usrreq.c sys/netinet/udp_var.h sys/netiso/argo_debug.h sys/netiso/clnl.h sys/netiso/clnp.h sys/netiso/clnp_debug.c sys/netiso/clnp_er.c sys/netiso/clnp_frag.c sys/netiso/clnp_input.c sys/netiso/clnp_options.c sys/netiso/clnp_output.c sys/netiso/clnp_raw.c sys/netiso/clnp_stat.h sys/netiso/clnp_subr.c sys/netiso/clnp_timer.c sys/netiso/cltp_usrreq.c sys/netiso/cltp_var.h sys/netiso/cons.h sys/netiso/cons_pcb.h sys/netiso/eonvar.h sys/netiso/esis.c sys/netiso/esis.h sys/netiso/if_cons.c sys/netiso/if_eon.c sys/netiso/iso.c sys/netiso/iso.h sys/netiso/iso_chksum.c sys/netiso/iso_errno.h sys/netiso/iso_map.h sys/netiso/iso_pcb.c sys/netiso/iso_pcb.h sys/netiso/iso_proto.c sys/netiso/iso_snpac.c sys/netiso/iso_snpac.h sys/netiso/iso_var.h sys/netiso/tp.trans sys/netiso/tp_astring.c sys/netiso/tp_clnp.h sys/netiso/tp_cons.c sys/netiso/tp_driver.c sys/netiso/tp_emit.c sys/netiso/tp_events.h sys/netiso/tp_inet.c sys/netiso/tp_input.c sys/netiso/tp_ip.h sys/netiso/tp_iso.c sys/netiso/tp_meas.c sys/netiso/tp_meas.h sys/netiso/tp_output.c sys/netiso/tp_param.h sys/netiso/tp_pcb.c sys/netiso/tp_pcb.h sys/netiso/tp_seq.h sys/netiso/tp_stat.h sys/netiso/tp_states.h sys/netiso/tp_states.init sys/netiso/tp_subr.c sys/netiso/tp_subr2.c sys/netiso/tp_timer.c sys/netiso/tp_timer.h sys/netiso/tp_tpdu.h sys/netiso/tp_trace.c sys/netiso/tp_trace.h sys/netiso/tp_user.h sys/netiso/tp_usrreq.c sys/netiso/xebec/Makefile sys/netiso/xebec/debug.h sys/netiso/xebec/llparse.c sys/netiso/xebec/llparse.h sys/netiso/xebec/llscan.c sys/netiso/xebec/main.c sys/netiso/xebec/main.h sys/netiso/xebec/malloc.c sys/netiso/xebec/malloc.h sys/netiso/xebec/procs.c sys/netiso/xebec/procs.h sys/netiso/xebec/putdriver.c sys/netiso/xebec/sets.c sys/netiso/xebec/sets.h sys/netiso/xebec/test.trans sys/netiso/xebec/test_def.h sys/netiso/xebec/xebec.bnf sys/netiso/xebec/xebec.c sys/netiso/xebec/xebec.h sys/netns/idp.h sys/netns/idp_usrreq.c sys/netns/idp_var.h sys/netns/ns.c sys/netns/ns.h sys/netns/ns_error.c sys/netns/ns_error.h sys/netns/ns_if.h sys/netns/ns_input.c sys/netns/ns_ip.c sys/netns/ns_output.c sys/netns/ns_pcb.c sys/netns/ns_pcb.h sys/netns/ns_proto.c sys/netns/sp.h sys/netns/spidp.h sys/netns/spp_debug.c sys/netns/spp_debug.h sys/netns/spp_timer.h sys/netns/spp_usrreq.c sys/netns/spp_var.h sys/nfs/nfs.h sys/nfs/nfs_bio.c sys/nfs/nfs_node.c sys/nfs/nfs_serv.c sys/nfs/nfs_socket.c sys/nfs/nfs_srvcache.c sys/nfs/nfs_subs.c sys/nfs/nfs_syscalls.c sys/nfs/nfs_vfsops.c sys/nfs/nfs_vnops.c sys/nfs/nfscompress.h sys/nfs/nfsdiskless.h sys/nfs/nfsiom.h sys/nfs/nfsm_subs.h sys/nfs/nfsmount.h sys/nfs/nfsnode.h sys/nfs/nfsrvcache.h sys/nfs/nfsv2.h sys/nfs/rpcv2.h sys/nfs/xdr_subs.h sys/pcfs/bootsect.h sys/pcfs/bpb.h sys/pcfs/denode.h sys/pcfs/direntry.h sys/pcfs/fat.h sys/pcfs/pcfs_conv.c sys/pcfs/pcfs_denode.c sys/pcfs/pcfs_fat.c sys/pcfs/pcfs_lookup.c sys/pcfs/pcfs_vfsops.c sys/pcfs/pcfs_vnops.c sys/pcfs/pcfsmount.h sys/scsi/README sys/scsi/cd.c sys/scsi/ch.c sys/scsi/scsi_all.h sys/scsi/scsi_cd.h sys/scsi/scsi_changer.h sys/scsi/scsi_disk.h sys/scsi/scsi_generic.h sys/scsi/scsi_tape.h sys/scsi/scsiconf.c sys/scsi/scsiconf.h sys/scsi/sd.c sys/scsi/sg.c sys/scsi/st.c sys/stand/cat.c sys/stand/copy.c sys/stand/dev.c sys/stand/ls.c sys/stand/printf.c sys/stand/saerrno.h sys/stand/saioctl.h sys/stand/stat.c sys/sys/acct.h sys/sys/asm.h sys/sys/buf.h sys/sys/callout.h sys/sys/cdefs.h sys/sys/cdio.h sys/sys/chio.h sys/sys/clist.h sys/sys/conf.h sys/sys/dir.h sys/sys/disklabel.h sys/sys/dkbad.h sys/sys/dkstat.h sys/sys/dmap.h sys/sys/domain.h sys/sys/errno.h sys/sys/exec.h sys/sys/fcntl.h sys/sys/fifo.h sys/sys/file.h sys/sys/filedesc.h sys/sys/filio.h sys/sys/gprof.h sys/sys/ieeefp.h sys/sys/ioctl.h sys/sys/ioctl_compat.h sys/sys/ipc.h sys/sys/kernel.h sys/sys/kinfo.h sys/sys/kinfo_proc.h sys/sys/ktrace.h sys/sys/malloc.h sys/sys/map.h sys/sys/mapmem.h sys/sys/mbuf.h sys/sys/mman.h sys/sys/mount.h sys/sys/msgbuf.h sys/sys/mtio.h sys/sys/namei.h sys/sys/param.h sys/sys/proc.h sys/sys/protosw.h sys/sys/ptrace.h sys/sys/reboot.h sys/sys/resource.h sys/sys/resourcevar.h sys/sys/rlist.h sys/sys/sgio.h sys/sys/shm.h sys/sys/signal.h sys/sys/signalvar.h sys/sys/socket.h sys/sys/socketvar.h sys/sys/specdev.h sys/sys/spkr.h sys/sys/stat.h sys/sys/syscall.h sys/sys/syslimits.h sys/sys/syslog.h sys/sys/systm.h sys/sys/tablet.h sys/sys/termios.h sys/sys/time.h sys/sys/timeb.h sys/sys/times.h sys/sys/tprintf.h sys/sys/trace.h sys/sys/tty.h sys/sys/ttychars.h sys/sys/ttydefaults.h sys/sys/ttydev.h sys/sys/types.h sys/sys/ucred.h sys/sys/uio.h sys/sys/un.h sys/sys/unistd.h sys/sys/unpcb.h sys/sys/user.h sys/sys/utsname.h sys/sys/vadvise.h sys/sys/vcmd.h sys/sys/vlimit.h sys/sys/vmmeter.h sys/sys/vnode.h sys/sys/vsio.h sys/sys/vtimes.h sys/sys/wait.h sys/ufs/dinode.h sys/ufs/dir.h sys/ufs/fs.h sys/ufs/inode.h sys/ufs/lockf.h sys/ufs/mfs_vfsops.c sys/ufs/mfs_vnops.c sys/ufs/mfsiom.h sys/ufs/mfsnode.h sys/ufs/quota.h sys/ufs/ufs_alloc.c sys/ufs/ufs_bmap.c sys/ufs/ufs_disksubr.c sys/ufs/ufs_inode.c sys/ufs/ufs_lockf.c sys/ufs/ufs_lookup.c sys/ufs/ufs_quota.c sys/ufs/ufs_subr.c sys/ufs/ufs_tables.c sys/ufs/ufs_vfsops.c sys/ufs/ufs_vnops.c sys/ufs/ufsmount.h sys/vm/device_pager.c sys/vm/device_pager.h sys/vm/kern_lock.c sys/vm/lock.h sys/vm/pmap.h sys/vm/queue.h sys/vm/swap_pager.c sys/vm/swap_pager.h sys/vm/vm.h sys/vm/vm_fault.c sys/vm/vm_glue.c sys/vm/vm_inherit.h sys/vm/vm_init.c sys/vm/vm_kern.c sys/vm/vm_kern.h sys/vm/vm_map.c sys/vm/vm_map.h sys/vm/vm_meter.c sys/vm/vm_mmap.c sys/vm/vm_object.c sys/vm/vm_object.h sys/vm/vm_page.c sys/vm/vm_page.h sys/vm/vm_pageout.c sys/vm/vm_pageout.h sys/vm/vm_pager.c sys/vm/vm_pager.h sys/vm/vm_param.h sys/vm/vm_prot.h sys/vm/vm_statistics.h sys/vm/vm_swap.c sys/vm/vm_unix.c sys/vm/vm_user.c sys/vm/vm_user.h sys/vm/vnode_pager.c sys/vm/vnode_pager.h usr.bin/Makefile usr.bin/ar/Makefile usr.bin/ar/ar.5 usr.bin/bdes/bdes.c usr.bin/chat/Example usr.bin/chat/Makefile usr.bin/chat/README usr.bin/chat/chat.c usr.bin/chat/fix-cua usr.bin/chat/ppp-off usr.bin/chat/ppp-on usr.bin/chat/unlock usr.bin/checknr/checknr.1 usr.bin/cksum/Makefile usr.bin/cksum/cksum.1 usr.bin/cksum/cksum.c usr.bin/cksum/crc.c usr.bin/cksum/extern.h usr.bin/cksum/print.c usr.bin/cksum/sum1.c usr.bin/cksum/sum2.c usr.bin/col/col.c usr.bin/compress/Makefile usr.bin/crontab/Makefile usr.bin/crontab/crontab.5 usr.bin/du/Makefile usr.bin/du/du.1 usr.bin/du/du.c usr.bin/elvis/Makefile usr.bin/elvis/blk.c usr.bin/elvis/cmd1.c usr.bin/elvis/cmd2.c usr.bin/elvis/config.h usr.bin/elvis/ctype.c usr.bin/elvis/curses.c usr.bin/elvis/cut.c usr.bin/elvis/doc/Makedoc usr.bin/elvis/doc/Makefile usr.bin/elvis/doc/cflags.ms usr.bin/elvis/doc/col usr.bin/elvis/doc/ctags.man usr.bin/elvis/doc/differ.ms usr.bin/elvis/doc/elvis.man usr.bin/elvis/doc/elvprsv.man usr.bin/elvis/doc/elvrec.man usr.bin/elvis/doc/environ.ms usr.bin/elvis/doc/ex.ms usr.bin/elvis/doc/fmt.man usr.bin/elvis/doc/index.ms usr.bin/elvis/doc/internal.ms usr.bin/elvis/doc/makefile.ms usr.bin/elvis/doc/options.ms usr.bin/elvis/doc/question.ms usr.bin/elvis/doc/ref.man usr.bin/elvis/doc/regexp.ms usr.bin/elvis/doc/termcap.ms usr.bin/elvis/doc/title.ms usr.bin/elvis/doc/ver.ms usr.bin/elvis/doc/versions.ms usr.bin/elvis/doc/visual.ms usr.bin/elvis/elvis.1 usr.bin/elvis/ex.c usr.bin/elvis/input.c usr.bin/elvis/main.c usr.bin/elvis/misc.c usr.bin/elvis/modify.c usr.bin/elvis/move1.c usr.bin/elvis/move2.c usr.bin/elvis/move3.c usr.bin/elvis/move4.c usr.bin/elvis/move5.c usr.bin/elvis/opts.c usr.bin/elvis/recycle.c usr.bin/elvis/redraw.c usr.bin/elvis/regexp.c usr.bin/elvis/regexp.h usr.bin/elvis/regsub.c usr.bin/elvis/system.c usr.bin/elvis/tio.c usr.bin/elvis/tmp.c usr.bin/elvis/unix.c usr.bin/elvis/vars.c usr.bin/elvis/vcmd.c usr.bin/elvis/vi.c usr.bin/elvis/vi.h usr.bin/elvisrecover/Makefile usr.bin/elvisrecover/elvisrecover.c usr.bin/env/Makefile usr.bin/env/env.1 usr.bin/env/env.c usr.bin/file/LEGAL.NOTICE usr.bin/file/MAINT usr.bin/file/Makefile usr.bin/file/PORTING usr.bin/file/README usr.bin/file/apprentice.c usr.bin/file/ascmagic.c usr.bin/file/compress.c usr.bin/file/file.1 usr.bin/file/file.c usr.bin/file/file.h usr.bin/file/fsmagic.c usr.bin/file/is_tar.c usr.bin/file/magdir/Header usr.bin/file/magdir/Localstuff usr.bin/file/magdir/alliant usr.bin/file/magdir/apl usr.bin/file/magdir/ar usr.bin/file/magdir/att3b usr.bin/file/magdir/audio usr.bin/file/magdir/blit usr.bin/file/magdir/bsdi usr.bin/file/magdir/c-lang usr.bin/file/magdir/clipper usr.bin/file/magdir/commands usr.bin/file/magdir/compress usr.bin/file/magdir/convex usr.bin/file/magdir/cpio usr.bin/file/magdir/diamond usr.bin/file/magdir/dump usr.bin/file/magdir/elf usr.bin/file/magdir/encore usr.bin/file/magdir/floppy.raw usr.bin/file/magdir/frame usr.bin/file/magdir/freebsd usr.bin/file/magdir/hp usr.bin/file/magdir/ibm370 usr.bin/file/magdir/ibm6000 usr.bin/file/magdir/iff usr.bin/file/magdir/images usr.bin/file/magdir/intel usr.bin/file/magdir/interleaf usr.bin/file/magdir/iris usr.bin/file/magdir/ispell usr.bin/file/magdir/lex usr.bin/file/magdir/lif usr.bin/file/magdir/linux usr.bin/file/magdir/magic usr.bin/file/magdir/microsoft usr.bin/file/magdir/mips usr.bin/file/magdir/mirage usr.bin/file/magdir/mkid usr.bin/file/magdir/mmdf usr.bin/file/magdir/motorola usr.bin/file/magdir/ms-dos usr.bin/file/magdir/ncr usr.bin/file/magdir/netbsd usr.bin/file/magdir/news usr.bin/file/magdir/pbm usr.bin/file/magdir/pdp usr.bin/file/magdir/pgp usr.bin/file/magdir/pkgadd usr.bin/file/magdir/plus5 usr.bin/file/magdir/postscript usr.bin/file/magdir/psdbms usr.bin/file/magdir/pyramid usr.bin/file/magdir/sc usr.bin/file/magdir/sendmail usr.bin/file/magdir/sequent usr.bin/file/magdir/sgml usr.bin/file/magdir/softquad usr.bin/file/magdir/sun usr.bin/file/magdir/sunraster usr.bin/file/magdir/terminfo usr.bin/file/magdir/tex usr.bin/file/magdir/troff usr.bin/file/magdir/typeset usr.bin/file/magdir/unknown usr.bin/file/magdir/uuencode usr.bin/file/magdir/varied.out usr.bin/file/magdir/vax usr.bin/file/magdir/visx usr.bin/file/magdir/x11 usr.bin/file/magdir/zilog usr.bin/file/magic.5 usr.bin/file/names.h usr.bin/file/patchlevel.h usr.bin/file/print.c usr.bin/file/softmagic.c usr.bin/file/tar.h usr.bin/file/tst/Makefile usr.bin/find/Makefile usr.bin/find/extern.h usr.bin/find/find.1 usr.bin/find/find.c usr.bin/find/find.h usr.bin/find/function.c usr.bin/find/ls.c usr.bin/find/main.c usr.bin/find/misc.c usr.bin/find/operator.c usr.bin/find/option.c usr.bin/fstat/fstat.1 usr.bin/fstat/fstat.c usr.bin/ftp/ftp.c usr.bin/getopt/Makefile usr.bin/getopt/README usr.bin/getopt/getopt.1 usr.bin/getopt/getopt.c usr.bin/groups/groups.1 usr.bin/head/head.1 usr.bin/head/head.c usr.bin/hexdump/Makefile usr.bin/join/Makefile usr.bin/join/join.1 usr.bin/join/join.c usr.bin/lastcomm/lastcomm.c usr.bin/lex/COPYING usr.bin/lex/Makefile usr.bin/lex/ccl.c usr.bin/lex/dfa.c usr.bin/lex/ecs.c usr.bin/lex/flexdef.h usr.bin/lex/flexdoc.1 usr.bin/lex/gen.c usr.bin/lex/initscan.c usr.bin/lex/lex.1 usr.bin/lex/lex.skel usr.bin/lex/lib/Makefile usr.bin/lex/lib/libmain.c usr.bin/lex/main.c usr.bin/lex/misc.c usr.bin/lex/nfa.c usr.bin/lex/parse.y usr.bin/lex/scan.l usr.bin/lex/sym.c usr.bin/lex/tblcmp.c usr.bin/lex/yylex.c usr.bin/locate/locate/locate.c usr.bin/locate/locate/updatedb.sh usr.bin/lock/Makefile usr.bin/login/Makefile usr.bin/login/klogin.c usr.bin/login/login.c usr.bin/lorder/Makefile usr.bin/m4/Makefile usr.bin/m4/TEST/ack.m4 usr.bin/m4/TEST/hanoi.m4 usr.bin/m4/TEST/hash.m4 usr.bin/m4/TEST/sqroot.m4 usr.bin/m4/TEST/string.m4 usr.bin/m4/TEST/test.m4 usr.bin/m4/expr.c usr.bin/m4/extr.h usr.bin/m4/int2str.c usr.bin/m4/look.c usr.bin/m4/m4.1 usr.bin/m4/main.c usr.bin/m4/mdef.h usr.bin/m4/misc.c usr.bin/m4/ourlims.h usr.bin/m4/serv.c usr.bin/mail/misc/mail.rc usr.bin/make/Makefile.dist usr.bin/make/main.c usr.bin/mkdep/Makefile usr.bin/mkdep/mkdep.gcc.sh usr.bin/mkdep/mkdep.sh usr.bin/mkfifo/mkfifo.1 usr.bin/mkfifo/mkfifo.c usr.bin/more/Makefile usr.bin/more/ch.c usr.bin/more/command.c usr.bin/more/decode.c usr.bin/more/filename.c usr.bin/more/help.c usr.bin/more/input.c usr.bin/more/less.h usr.bin/more/line.c usr.bin/more/linenum.c usr.bin/more/main.c usr.bin/more/more.1 usr.bin/more/more.help usr.bin/more/option.c usr.bin/more/os.c usr.bin/more/output.c usr.bin/more/pathnames.h usr.bin/more/position.c usr.bin/more/prim.c usr.bin/more/screen.c usr.bin/more/signal.c usr.bin/more/tags.c usr.bin/more/ttyin.c usr.bin/msgs/msgs.1 usr.bin/msgs/msgs.c usr.bin/mt/mt.1 usr.bin/mt/mt.c usr.bin/netstat/Makefile usr.bin/netstat/iso.c usr.bin/netstat/netstat.1 usr.bin/netstat/route.c usr.bin/netstat/unix.c usr.bin/nfsstat/nfsstat.1 usr.bin/nohup/nohup.1 usr.bin/nohup/nohup.c usr.bin/pagesize/pagesize.1 usr.bin/passwd/Makefile usr.bin/passwd/local_passwd.c usr.bin/printenv/Makefile usr.bin/printenv/printenv.1 usr.bin/ranlib/Makefile usr.bin/ranlib/ranlib.5 usr.bin/rdist/server.c usr.bin/renice/Makefile usr.bin/rev/Makefile usr.bin/rev/rev.1 usr.bin/rev/rev.c usr.bin/rlogin/Makefile usr.bin/rlogin/rlogin.c usr.bin/rpcgen/Makefile usr.bin/rpcgen/rpc_clntout.c usr.bin/rpcgen/rpc_cout.c usr.bin/rpcgen/rpc_hout.c usr.bin/rpcgen/rpc_main.c usr.bin/rpcgen/rpc_parse.c usr.bin/rpcgen/rpc_parse.h usr.bin/rpcgen/rpc_scan.c usr.bin/rpcgen/rpc_scan.h usr.bin/rpcgen/rpc_svcout.c usr.bin/rpcgen/rpc_util.c usr.bin/rpcgen/rpc_util.h usr.bin/rpcgen/rpcgen.1 usr.bin/rpcinfo/Makefile usr.bin/rpcinfo/rpcinfo.8 usr.bin/rpcinfo/rpcinfo.c usr.bin/rsh/Makefile usr.bin/rup/Makefile usr.bin/rup/rup.1 usr.bin/rup/rup.c usr.bin/rusers/Makefile usr.bin/rusers/rusers.1 usr.bin/rusers/rusers.c usr.bin/rwall/Makefile usr.bin/rwall/rwall.1 usr.bin/rwall/rwall.c usr.bin/sed/Makefile usr.bin/sed/POSIX usr.bin/sed/compile.c usr.bin/sed/compile.test usr.bin/sed/defs.h usr.bin/sed/extern.h usr.bin/sed/hanoi.sed usr.bin/sed/main.c usr.bin/sed/math.sed usr.bin/sed/misc.c usr.bin/sed/process.c usr.bin/sed/sed.1 usr.bin/sed/sed.test usr.bin/shar/Makefile usr.bin/showmount/Makefile usr.bin/size/size.1 usr.bin/size/size.c usr.bin/strip/Makefile usr.bin/strip/strip.1 usr.bin/strip/strip.c usr.bin/su/Makefile usr.bin/su/su.c usr.bin/symorder/symorder.1 usr.bin/syscons/Makefile usr.bin/syscons/syscons.1 usr.bin/syscons/syscons.c usr.bin/syscons/syscons.h usr.bin/tail/Makefile usr.bin/tail/extern.h usr.bin/tail/forward.c usr.bin/tail/misc.c usr.bin/tail/read.c usr.bin/tail/reverse.c usr.bin/tail/tail.1 usr.bin/tail/tail.c usr.bin/telnet/Makefile usr.bin/telnet/commands.c usr.bin/tftp/main.c usr.bin/tftp/tftp.c usr.bin/tip/Makefile usr.bin/tip/aculib/courier.c usr.bin/tip/aculib/hayes.c usr.bin/tip/remcap.c usr.bin/tip/tip.1 usr.bin/tip/tipout.c usr.bin/tn3270/Makefile usr.bin/tn3270/Makefile.inc usr.bin/tn3270/mset/Makefile usr.bin/tn3270/mset/map3270.5 usr.bin/tn3270/mset/mset.1 usr.bin/tn3270/tn3270/Makefile usr.bin/tn3270/tn3270/tn3270.1 usr.bin/tn3270/tools/Makefile usr.bin/tn3270/tools/mkastods/Makefile usr.bin/tn3270/tools/mkastods/mkastods.c usr.bin/tn3270/tools/mkastosc/Makefile usr.bin/tn3270/tools/mkastosc/mkastosc.c usr.bin/tn3270/tools/mkdctype/Makefile usr.bin/tn3270/tools/mkdctype/ectype.c usr.bin/tn3270/tools/mkdctype/ectype.h usr.bin/tn3270/tools/mkdctype/mkdctype.c usr.bin/tn3270/tools/mkdstoas/Makefile usr.bin/tn3270/tools/mkdstoas/mkdstoas.c usr.bin/tn3270/tools/mkhits/Makefile usr.bin/tn3270/tools/mkhits/dohits.c usr.bin/tn3270/tools/mkhits/dohits.h usr.bin/tn3270/tools/mkhits/mkhits.c usr.bin/touch/touch.1 usr.bin/touch/touch.c usr.bin/tr/Makefile usr.bin/tr/extern.h usr.bin/tr/str.c usr.bin/tr/tr.1 usr.bin/tr/tr.c usr.bin/tset/set.c usr.bin/tset/term.c usr.bin/tset/tset.1 usr.bin/tset/tset.c usr.bin/uname/Makefile usr.bin/uname/uname.1 usr.bin/uname/uname.c usr.bin/uniq/uniq.1 usr.bin/uniq/uniq.c usr.bin/uuencode/Makefile usr.bin/vacation/vacation.c usr.bin/vgrind/Makefile usr.bin/vgrind/vgrind.1 usr.bin/vgrind/vgrind.sh usr.bin/vgrind/vgrindefs.c usr.bin/vmstat/Makefile usr.bin/vmstat/vmstat.8 usr.bin/vmstat/vmstat.c usr.bin/w/Makefile usr.bin/w/uptime.1 usr.bin/w/w.c usr.bin/whereis/whereis.c usr.bin/which/Makefile usr.bin/window/Makefile usr.bin/window/wwinit.c usr.bin/xargs/xargs.1 usr.bin/xargs/xargs.c usr.bin/xinstall/Makefile usr.bin/yacc/Makefile usr.bin/yacc/NO_WARRANTY usr.bin/yacc/README usr.bin/yacc/closure.c usr.bin/yacc/defs.h usr.bin/yacc/error.c usr.bin/yacc/lalr.c usr.bin/yacc/lr0.c usr.bin/yacc/main.c usr.bin/yacc/mkpar.c usr.bin/yacc/output.c usr.bin/yacc/reader.c usr.bin/yacc/skeleton.c usr.bin/yacc/symtab.c usr.bin/yacc/test/error.tab.c usr.bin/yacc/test/error.tab.h usr.bin/yacc/test/ftp.tab.c usr.bin/yacc/test/ftp.y usr.bin/yacc/verbose.c usr.bin/yacc/warshall.c usr.bin/yacc/yacc.1 usr.sbin/Makefile usr.sbin/XNSrouted/Makefile usr.sbin/XNSrouted/XNSrouted.8 usr.sbin/XNSrouted/af.c usr.sbin/XNSrouted/af.h usr.sbin/XNSrouted/defs.h usr.sbin/XNSrouted/if.c usr.sbin/XNSrouted/input.c usr.sbin/XNSrouted/interface.h usr.sbin/XNSrouted/main.c usr.sbin/XNSrouted/output.c usr.sbin/XNSrouted/protocol.h usr.sbin/XNSrouted/startup.c usr.sbin/XNSrouted/table.h usr.sbin/XNSrouted/tables.c usr.sbin/XNSrouted/timer.c usr.sbin/XNSrouted/tools/query.c usr.sbin/XNSrouted/trace.c usr.sbin/XNSrouted/trace.h usr.sbin/arp/Makefile usr.sbin/arp/arp.8 usr.sbin/bad144/Makefile usr.sbin/bad144/bad144.8 usr.sbin/bad144/bad144.c usr.sbin/chown/Makefile usr.sbin/chroot/Makefile usr.sbin/config/Makefile usr.sbin/config/config.8 usr.sbin/config/config.h usr.sbin/config/config.y usr.sbin/config/lang.l usr.sbin/config/main.c usr.sbin/config/mkglue.c usr.sbin/config/mkioconf.c usr.sbin/config/mkmakefile.c usr.sbin/config/mkswapconf.c usr.sbin/dbsym/Makefile usr.sbin/dbsym/dbsym.c usr.sbin/dev_mkdb/Makefile usr.sbin/dev_mkdb/dev_mkdb.c usr.sbin/diskpart/Makefile usr.sbin/diskpart/diskpart.c usr.sbin/edquota/Makefile usr.sbin/flcopy/Makefile usr.sbin/gettable/Makefile usr.sbin/gettable/gettable.c usr.sbin/htable/Makefile usr.sbin/inetd/Makefile usr.sbin/inetd/inetd.8 usr.sbin/inetd/inetd.c usr.sbin/inetd/pathnames.h usr.sbin/iostat/Makefile usr.sbin/iostat/iostat.8 usr.sbin/kgmon/Makefile usr.sbin/kgmon/kgmon.8 usr.sbin/kvm_mkdb/Makefile usr.sbin/kvm_mkdb/kvm_mkdb.8 usr.sbin/kvm_mkdb/kvm_mkdb.c usr.sbin/kvm_mkdb/nlist.c usr.sbin/lpr/lpc/Makefile usr.sbin/lpr/lpd/Makefile usr.sbin/lpr/lpd/printjob.c usr.sbin/lpr/pac/Makefile usr.sbin/mtree/Makefile usr.sbin/mtree/compare.c usr.sbin/mtree/create.c usr.sbin/mtree/extern.h usr.sbin/mtree/misc.c usr.sbin/mtree/mtree.8 usr.sbin/mtree/mtree.c usr.sbin/mtree/mtree.h usr.sbin/mtree/spec.c usr.sbin/mtree/verify.c usr.sbin/named/Makefile usr.sbin/named/tools/nslookup/Makefile usr.sbin/portmap/Makefile usr.sbin/pppstats/Makefile usr.sbin/pppstats/pppstats.c usr.sbin/pwd_mkdb/Makefile usr.sbin/pwd_mkdb/pwd_mkdb.8 usr.sbin/pwd_mkdb/pwd_mkdb.c usr.sbin/quotaon/Makefile usr.sbin/repquota/Makefile usr.sbin/rmt/Makefile usr.sbin/routed/Makefile usr.sbin/rwhod/Makefile usr.sbin/rwhod/rwhod.8 usr.sbin/rwhod/rwhod.c usr.sbin/sendmail/CHANGES-R5-R8 usr.sbin/sendmail/Makefile usr.sbin/sendmail/READ_ME usr.sbin/sendmail/RELEASE_NOTES usr.sbin/sendmail/cf/README usr.sbin/sendmail/cf/cf/Makefile usr.sbin/sendmail/cf/cf/alpha.mc usr.sbin/sendmail/cf/cf/auspex.mc usr.sbin/sendmail/cf/cf/boat-anchor.mc usr.sbin/sendmail/cf/cf/chez.mc usr.sbin/sendmail/cf/cf/cogsci.mc usr.sbin/sendmail/cf/cf/cs-exposed.mc usr.sbin/sendmail/cf/cf/cs-hidden.mc usr.sbin/sendmail/cf/cf/freefall.mc usr.sbin/sendmail/cf/cf/hpux-cs-exposed.mc usr.sbin/sendmail/cf/cf/hpux-cs-hidden.mc usr.sbin/sendmail/cf/cf/knecht.mc usr.sbin/sendmail/cf/cf/mail.cs.mc usr.sbin/sendmail/cf/cf/mail.eecs.mc usr.sbin/sendmail/cf/cf/python.mc usr.sbin/sendmail/cf/cf/s2k.mc usr.sbin/sendmail/cf/cf/sun-lamp.mc usr.sbin/sendmail/cf/cf/sunos3.5-cs-exposed.mc usr.sbin/sendmail/cf/cf/sunos3.5-cs-hidden.mc usr.sbin/sendmail/cf/cf/sunos4.1-cs-exposed.mc usr.sbin/sendmail/cf/cf/sunos4.1-cs-hidden.mc usr.sbin/sendmail/cf/cf/tcpproto.mc usr.sbin/sendmail/cf/cf/ucbarpa.mc usr.sbin/sendmail/cf/cf/ucbvax.mc usr.sbin/sendmail/cf/cf/udb.mc usr.sbin/sendmail/cf/cf/ultrix4.1-cs-exposed.mc usr.sbin/sendmail/cf/cf/ultrix4.1-cs-hidden.mc usr.sbin/sendmail/cf/cf/uucpproto.mc usr.sbin/sendmail/cf/cf/vangogh.mc usr.sbin/sendmail/cf/domain/Berkeley.m4 usr.sbin/sendmail/cf/domain/cs.exposed.m4 usr.sbin/sendmail/cf/domain/cs.hidden.m4 usr.sbin/sendmail/cf/domain/eecs.hidden.m4 usr.sbin/sendmail/cf/domain/s2k.m4 usr.sbin/sendmail/cf/feature/allmasquerade.m4 usr.sbin/sendmail/cf/feature/always_add_domain.m4 usr.sbin/sendmail/cf/feature/bitdomain.m4 usr.sbin/sendmail/cf/feature/domaintable.m4 usr.sbin/sendmail/cf/feature/mailertable.m4 usr.sbin/sendmail/cf/feature/nocanonify.m4 usr.sbin/sendmail/cf/feature/notsticky.m4 usr.sbin/sendmail/cf/feature/nouucp.m4 usr.sbin/sendmail/cf/feature/redirect.m4 usr.sbin/sendmail/cf/feature/use_cw_file.m4 usr.sbin/sendmail/cf/feature/uucpdomain.m4 usr.sbin/sendmail/cf/hack/cssubdomain.m4 usr.sbin/sendmail/cf/m4/cf.m4 usr.sbin/sendmail/cf/m4/proto.m4 usr.sbin/sendmail/cf/m4/version.m4 usr.sbin/sendmail/cf/mailer/fax.m4 usr.sbin/sendmail/cf/mailer/local.m4 usr.sbin/sendmail/cf/mailer/smtp.m4 usr.sbin/sendmail/cf/mailer/usenet.m4 usr.sbin/sendmail/cf/mailer/uucp.m4 usr.sbin/sendmail/cf/ostype/aix3.m4 usr.sbin/sendmail/cf/ostype/bsd4.3.m4 usr.sbin/sendmail/cf/ostype/bsd4.4.m4 usr.sbin/sendmail/cf/ostype/hpux.m4 usr.sbin/sendmail/cf/ostype/irix.m4 usr.sbin/sendmail/cf/ostype/osf1.m4 usr.sbin/sendmail/cf/ostype/riscos4.5.m4 usr.sbin/sendmail/cf/ostype/solaris2.1.m4 usr.sbin/sendmail/cf/ostype/sunos3.5.m4 usr.sbin/sendmail/cf/ostype/sunos4.1.m4 usr.sbin/sendmail/cf/ostype/ultrix4.1.m4 usr.sbin/sendmail/cf/sh/makeinfo.sh usr.sbin/sendmail/cf/siteconfig/uucp.cogsci.m4 usr.sbin/sendmail/cf/siteconfig/uucp.old.arpa.m4 usr.sbin/sendmail/cf/siteconfig/uucp.ucbarpa.m4 usr.sbin/sendmail/cf/siteconfig/uucp.ucbvax.m4 usr.sbin/sendmail/contrib/README usr.sbin/sendmail/contrib/bitdomain.c usr.sbin/sendmail/contrib/expn.pl usr.sbin/sendmail/contrib/mmuegel usr.sbin/sendmail/contrib/rcpt-streaming usr.sbin/sendmail/contrib/xla/README usr.sbin/sendmail/contrib/xla/xla.c usr.sbin/sendmail/doc/intro/intro.me usr.sbin/sendmail/doc/intro/intro.ps usr.sbin/sendmail/doc/op/op.me usr.sbin/sendmail/doc/op/op.ps usr.sbin/sendmail/doc/usenix/usenix.me usr.sbin/sendmail/doc/usenix/usenix.ps usr.sbin/sendmail/mailstats/Makefile usr.sbin/sendmail/mailstats/mailstats.c usr.sbin/sendmail/makemap/Makefile usr.sbin/sendmail/makemap/makemap.8 usr.sbin/sendmail/makemap/makemap.c usr.sbin/sendmail/praliases/Makefile usr.sbin/sendmail/praliases/praliases.c usr.sbin/sendmail/rmail/Makefile usr.sbin/sendmail/rmail/rmail.8 usr.sbin/sendmail/rmail/rmail.c usr.sbin/sendmail/src/Makefile usr.sbin/sendmail/src/Makefile.AIX usr.sbin/sendmail/src/Makefile.HPUX usr.sbin/sendmail/src/Makefile.IRIX usr.sbin/sendmail/src/Makefile.OSF1 usr.sbin/sendmail/src/Makefile.Solaris usr.sbin/sendmail/src/Makefile.SunOS usr.sbin/sendmail/src/Makefile.ULTRIX usr.sbin/sendmail/src/Makefile.Utah usr.sbin/sendmail/src/Makefile.dist usr.sbin/sendmail/src/READ_ME usr.sbin/sendmail/src/TRACEFLAGS usr.sbin/sendmail/src/alias.c usr.sbin/sendmail/src/aliases usr.sbin/sendmail/src/aliases.5 usr.sbin/sendmail/src/arpadate.c usr.sbin/sendmail/src/cdefs.h usr.sbin/sendmail/src/clock.c usr.sbin/sendmail/src/collect.c usr.sbin/sendmail/src/conf.c usr.sbin/sendmail/src/conf.h usr.sbin/sendmail/src/convtime.c usr.sbin/sendmail/src/daemon.c usr.sbin/sendmail/src/deliver.c usr.sbin/sendmail/src/domain.c usr.sbin/sendmail/src/envelope.c usr.sbin/sendmail/src/err.c usr.sbin/sendmail/src/headers.c usr.sbin/sendmail/src/macro.c usr.sbin/sendmail/src/mailstats.h usr.sbin/sendmail/src/main.c usr.sbin/sendmail/src/map.c usr.sbin/sendmail/src/mci.c usr.sbin/sendmail/src/newaliases.1 usr.sbin/sendmail/src/parseaddr.c usr.sbin/sendmail/src/pathnames.h usr.sbin/sendmail/src/queue.c usr.sbin/sendmail/src/readcf.c usr.sbin/sendmail/src/recipient.c usr.sbin/sendmail/src/savemail.c usr.sbin/sendmail/src/sendmail.8 usr.sbin/sendmail/src/sendmail.h usr.sbin/sendmail/src/sendmail.hf usr.sbin/sendmail/src/srvrsmtp.c usr.sbin/sendmail/src/stab.c usr.sbin/sendmail/src/stats.c usr.sbin/sendmail/src/sysexits.c usr.sbin/sendmail/src/sysexits.h usr.sbin/sendmail/src/trace.c usr.sbin/sendmail/src/udb.c usr.sbin/sendmail/src/useful.h usr.sbin/sendmail/src/usersmtp.c usr.sbin/sendmail/src/util.c usr.sbin/sendmail/src/version.c usr.sbin/sliplogin/Makefile usr.sbin/swapinfo/Makefile usr.sbin/swapinfo/swapinfo.c usr.sbin/syslogd/Makefile usr.sbin/timed/timed/Makefile usr.sbin/timed/timedc/Makefile usr.sbin/traceroute/Makefile usr.sbin/trpt/Makefile usr.sbin/trpt/trpt.8 usr.sbin/trsp/Makefile usr.sbin/trsp/trsp.8 usr.sbin/update/Makefile usr.sbin/vipw/Makefile usr.sbin/vipw/vipw.8 Cherrypick from master 1993-10-28 02:38:36 UTC Rod Grimes 'Fix for 1542C support, it turns out that the 0x8 extended bios flag is only': share/termcap/termcap.src sys/i386/isa/aha1542.c Delete: BUGNFIX.FORM CAREWARE.INFO CONTRIB.LIST REGISTRATION SOFTWARE.FORM bin/ed/regex.c bin/ed/regex.h bin/ed/test/!1.d bin/ed/test/!1.err bin/ed/test/!1.r bin/ed/test/!1.t bin/ed/test/!2.d bin/ed/test/!2.err bin/ed/test/!2.r bin/ed/test/!2.t bin/expr/TODO bin/test/COPYING etc/man.conf games/hack/hack.onames.h gnu/games/chess/Xchess/xchess gnu/lib/libg++/ChangeLog gnu/lib/libg++/Makefile.gnu gnu/lib/libg++/README.gnu gnu/lib/libg++/g++-include/ACG.h gnu/lib/libg++/g++-include/AllocRing.h gnu/lib/libg++/g++-include/Binomial.h gnu/lib/libg++/g++-include/BitSet.h gnu/lib/libg++/g++-include/BitString.h gnu/lib/libg++/g++-include/Complex.h gnu/lib/libg++/g++-include/CursesW.h gnu/lib/libg++/g++-include/DiscUnif.h gnu/lib/libg++/g++-include/Erlang.h gnu/lib/libg++/g++-include/File.h gnu/lib/libg++/g++-include/Filebuf.h gnu/lib/libg++/g++-include/Fix.h gnu/lib/libg++/g++-include/Fix16.h gnu/lib/libg++/g++-include/Fix24.h gnu/lib/libg++/g++-include/Fmodes.h gnu/lib/libg++/g++-include/Geom.h gnu/lib/libg++/g++-include/GetOpt.h gnu/lib/libg++/g++-include/HypGeom.h gnu/lib/libg++/g++-include/Incremental.h gnu/lib/libg++/g++-include/Integer.h gnu/lib/libg++/g++-include/LogNorm.h gnu/lib/libg++/g++-include/MLCG.h gnu/lib/libg++/g++-include/Makefile gnu/lib/libg++/g++-include/NegExp.h gnu/lib/libg++/g++-include/Normal.h gnu/lib/libg++/g++-include/Obstack.h gnu/lib/libg++/g++-include/Pix.h gnu/lib/libg++/g++-include/PlotFile.h gnu/lib/libg++/g++-include/Poisson.h gnu/lib/libg++/g++-include/RNG.h gnu/lib/libg++/g++-include/Random.h gnu/lib/libg++/g++-include/Rational.h gnu/lib/libg++/g++-include/Regex.h gnu/lib/libg++/g++-include/RndInt.h gnu/lib/libg++/g++-include/SFile.h gnu/lib/libg++/g++-include/SmplHist.h gnu/lib/libg++/g++-include/SmplStat.h gnu/lib/libg++/g++-include/String.h gnu/lib/libg++/g++-include/Uniform.h gnu/lib/libg++/g++-include/Weibull.h gnu/lib/libg++/g++-include/abs.h gnu/lib/libg++/g++-include/bool.h gnu/lib/libg++/g++-include/builtin.h gnu/lib/libg++/g++-include/compare.h gnu/lib/libg++/g++-include/complex.h gnu/lib/libg++/g++-include/file.h gnu/lib/libg++/g++-include/filebuf.h gnu/lib/libg++/g++-include/generic.h gnu/lib/libg++/g++-include/istream.h gnu/lib/libg++/g++-include/malloc.h gnu/lib/libg++/g++-include/max.h gnu/lib/libg++/g++-include/min.h gnu/lib/libg++/g++-include/minmax.h gnu/lib/libg++/g++-include/new.h gnu/lib/libg++/g++-include/open.h gnu/lib/libg++/g++-include/osfcn.h gnu/lib/libg++/g++-include/ostream.h gnu/lib/libg++/g++-include/regex.h gnu/lib/libg++/g++-include/std.h gnu/lib/libg++/g++-include/strclass.h gnu/lib/libg++/g++-include/stream.h gnu/lib/libg++/g++-include/streambuf.h gnu/lib/libg++/g++-include/swap.h gnu/lib/libg++/genclass/genclass.1 gnu/lib/libg++/libg++/EH.cc gnu/lib/libg++/libg++/EH2.c gnu/lib/libg++/libg++/File.cc gnu/lib/libg++/libg++/Filebuf.cc gnu/lib/libg++/libg++/Makefile.gnu gnu/lib/libg++/libg++/PlotFile.cc gnu/lib/libg++/libg++/SFile.cc gnu/lib/libg++/libg++/Sample.cc gnu/lib/libg++/libg++/ctype.cc gnu/lib/libg++/libg++/curses.cc gnu/lib/libg++/libg++/delete.cc gnu/lib/libg++/libg++/filebuf.cc gnu/lib/libg++/libg++/form.cc gnu/lib/libg++/libg++/g++.order gnu/lib/libg++/libg++/gnulib3.c gnu/lib/libg++/libg++/istream.cc gnu/lib/libg++/libg++/itoa.cc gnu/lib/libg++/libg++/malloc.c gnu/lib/libg++/libg++/max.cc gnu/lib/libg++/libg++/min.cc gnu/lib/libg++/libg++/open.cc gnu/lib/libg++/libg++/ostream.cc gnu/lib/libg++/libg++/std.cc gnu/lib/libg++/libg++/streambuf.cc gnu/lib/libg++/libg++/xyzzy.cc gnu/usr.bin/cc/Makefile.inc gnu/usr.bin/cc/common/aux-output.c gnu/usr.bin/cc/common/basic-block.h gnu/usr.bin/cc/common/c-common.c gnu/usr.bin/cc/common/c-lex.h gnu/usr.bin/cc/common/c-parse.h gnu/usr.bin/cc/common/c-tree.h gnu/usr.bin/cc/common/caller-save.c gnu/usr.bin/cc/common/calls.c gnu/usr.bin/cc/common/combine.c gnu/usr.bin/cc/common/conditions.h gnu/usr.bin/cc/common/config.h gnu/usr.bin/cc/common/convert.c gnu/usr.bin/cc/common/convert.h gnu/usr.bin/cc/common/cse.c gnu/usr.bin/cc/common/dbxout.c gnu/usr.bin/cc/common/defaults.h gnu/usr.bin/cc/common/dwarfout.c gnu/usr.bin/cc/common/emit-rtl.c gnu/usr.bin/cc/common/explow.c gnu/usr.bin/cc/common/expmed.c gnu/usr.bin/cc/common/expr.c gnu/usr.bin/cc/common/expr.h gnu/usr.bin/cc/common/final.c gnu/usr.bin/cc/common/flags.h gnu/usr.bin/cc/common/flow.c gnu/usr.bin/cc/common/fold-const.c gnu/usr.bin/cc/common/function.c gnu/usr.bin/cc/common/function.h gnu/usr.bin/cc/common/gbl-ctors.h gnu/usr.bin/cc/common/getpwd.c gnu/usr.bin/cc/common/glimits.h gnu/usr.bin/cc/common/global.c gnu/usr.bin/cc/common/gstddef.h gnu/usr.bin/cc/common/gvarargs.h gnu/usr.bin/cc/common/hard-reg-set.h gnu/usr.bin/cc/common/i386/bsd.h gnu/usr.bin/cc/common/i386/gas.h gnu/usr.bin/cc/common/i386/gstabs.h gnu/usr.bin/cc/common/i386/i386.h gnu/usr.bin/cc/common/i386/perform.h gnu/usr.bin/cc/common/i386/unix.h gnu/usr.bin/cc/common/input.h gnu/usr.bin/cc/common/insn-attr.h gnu/usr.bin/cc/common/insn-attrtab.c gnu/usr.bin/cc/common/insn-codes.h gnu/usr.bin/cc/common/insn-config.h gnu/usr.bin/cc/common/insn-emit.c gnu/usr.bin/cc/common/insn-extract.c gnu/usr.bin/cc/common/insn-flags.h gnu/usr.bin/cc/common/insn-opinit.c gnu/usr.bin/cc/common/insn-output.c gnu/usr.bin/cc/common/insn-peep.c gnu/usr.bin/cc/common/insn-recog.c gnu/usr.bin/cc/common/integrate.c gnu/usr.bin/cc/common/integrate.h gnu/usr.bin/cc/common/jump.c gnu/usr.bin/cc/common/local-alloc.c gnu/usr.bin/cc/common/longlong.h gnu/usr.bin/cc/common/loop.c gnu/usr.bin/cc/common/loop.h gnu/usr.bin/cc/common/machmode.def gnu/usr.bin/cc/common/machmode.h gnu/usr.bin/cc/common/obstack.c gnu/usr.bin/cc/common/obstack.h gnu/usr.bin/cc/common/optabs.c gnu/usr.bin/cc/common/output.h gnu/usr.bin/cc/common/print-rtl.c gnu/usr.bin/cc/common/print-tree.c gnu/usr.bin/cc/common/real.c gnu/usr.bin/cc/common/real.h gnu/usr.bin/cc/common/recog.c gnu/usr.bin/cc/common/recog.h gnu/usr.bin/cc/common/reg-stack.c gnu/usr.bin/cc/common/regclass.c gnu/usr.bin/cc/common/regs.h gnu/usr.bin/cc/common/reload.c gnu/usr.bin/cc/common/reload.h gnu/usr.bin/cc/common/reload1.c gnu/usr.bin/cc/common/reorg.c gnu/usr.bin/cc/common/rtl.c gnu/usr.bin/cc/common/rtl.def gnu/usr.bin/cc/common/rtl.h gnu/usr.bin/cc/common/rtlanal.c gnu/usr.bin/cc/common/sched.c gnu/usr.bin/cc/common/sdbout.c gnu/usr.bin/cc/common/stmt.c gnu/usr.bin/cc/common/stor-layout.c gnu/usr.bin/cc/common/stupid.c gnu/usr.bin/cc/common/tconfig.h gnu/usr.bin/cc/common/tm.h gnu/usr.bin/cc/common/toplev.c gnu/usr.bin/cc/common/tree.c gnu/usr.bin/cc/common/tree.def gnu/usr.bin/cc/common/tree.h gnu/usr.bin/cc/common/typeclass.h gnu/usr.bin/cc/common/unroll.c gnu/usr.bin/cc/common/varasm.c gnu/usr.bin/cc/common/version.c gnu/usr.bin/cc/common/xcoffout.c gnu/usr.bin/cc/cpp/cpp.1 gnu/usr.bin/cvs/lib/Makefile.in gnu/usr.bin/cvs/mkmodules/xxx gnu/usr.bin/diff/fnmatch.h gnu/usr.bin/diff3/COPYING gnu/usr.bin/diff3/diff3.c gnu/usr.bin/diff3/getopt.c gnu/usr.bin/diff3/getopt.h gnu/usr.bin/diff3/getopt1.c gnu/usr.bin/diff3/system.h gnu/usr.bin/diff3/version.c gnu/usr.bin/gcc1/Makefile gnu/usr.bin/gcc1/Makefile.symlinks gnu/usr.bin/gcc1/cc/Makefile gnu/usr.bin/gcc1/cc/cc.1 gnu/usr.bin/gcc1/cc/cc.c gnu/usr.bin/gcc1/cc1/.dbxinit gnu/usr.bin/gcc1/cc1/.gdbinit gnu/usr.bin/gcc1/cc1/COPYING gnu/usr.bin/gcc1/cc1/Makefile gnu/usr.bin/gcc1/cc1/Makefile.gnu gnu/usr.bin/gcc1/cc1/PROBLEMS gnu/usr.bin/gcc1/cc1/PROJECTS gnu/usr.bin/gcc1/cc1/README gnu/usr.bin/gcc1/cc1/assert.h gnu/usr.bin/gcc1/cc1/basic-block.h gnu/usr.bin/gcc1/cc1/c-convert.c gnu/usr.bin/gcc1/cc1/c-decl.c gnu/usr.bin/gcc1/cc1/c-parse.gperf gnu/usr.bin/gcc1/cc1/c-parse.h gnu/usr.bin/gcc1/cc1/c-parse.y gnu/usr.bin/gcc1/cc1/c-tree.h gnu/usr.bin/gcc1/cc1/c-typeck.c gnu/usr.bin/gcc1/cc1/caller-save.c gnu/usr.bin/gcc1/cc1/combine.c gnu/usr.bin/gcc1/cc1/conditions.h gnu/usr.bin/gcc1/cc1/config/i386.md gnu/usr.bin/gcc1/cc1/config/out-i386.c gnu/usr.bin/gcc1/cc1/config/tm-att386.h gnu/usr.bin/gcc1/cc1/config/tm-bsd386.h gnu/usr.bin/gcc1/cc1/config/tm-compaq.h gnu/usr.bin/gcc1/cc1/config/tm-i386.h gnu/usr.bin/gcc1/cc1/config/tm-i386b.h gnu/usr.bin/gcc1/cc1/config/tm-i386b.h.save gnu/usr.bin/gcc1/cc1/config/tm-i386gas.h gnu/usr.bin/gcc1/cc1/config/tm-i386v.h gnu/usr.bin/gcc1/cc1/config/tm-i386v4.h gnu/usr.bin/gcc1/cc1/config/tm-i386vgas.h gnu/usr.bin/gcc1/cc1/config/xm-i386.h gnu/usr.bin/gcc1/cc1/config/xm-i386v.h gnu/usr.bin/gcc1/cc1/cse.c gnu/usr.bin/gcc1/cc1/dbxout.c gnu/usr.bin/gcc1/cc1/emit-rtl.c gnu/usr.bin/gcc1/cc1/explow.c gnu/usr.bin/gcc1/cc1/expmed.c gnu/usr.bin/gcc1/cc1/expr.c gnu/usr.bin/gcc1/cc1/expr.h gnu/usr.bin/gcc1/cc1/final.c gnu/usr.bin/gcc1/cc1/flags.h gnu/usr.bin/gcc1/cc1/flow.c gnu/usr.bin/gcc1/cc1/fold-const.c gnu/usr.bin/gcc1/cc1/gdbfiles.h gnu/usr.bin/gcc1/cc1/gencodes.c gnu/usr.bin/gcc1/cc1/genconfig.c gnu/usr.bin/gcc1/cc1/genemit.c gnu/usr.bin/gcc1/cc1/genextract.c gnu/usr.bin/gcc1/cc1/genflags.c gnu/usr.bin/gcc1/cc1/genoutput.c gnu/usr.bin/gcc1/cc1/genpeep.c gnu/usr.bin/gcc1/cc1/genrecog.c gnu/usr.bin/gcc1/cc1/global-alloc.c gnu/usr.bin/gcc1/cc1/gstdarg.h gnu/usr.bin/gcc1/cc1/gvarargs.h gnu/usr.bin/gcc1/cc1/hard-reg-set.h gnu/usr.bin/gcc1/cc1/input.h gnu/usr.bin/gcc1/cc1/integrate.c gnu/usr.bin/gcc1/cc1/jump.c gnu/usr.bin/gcc1/cc1/limits.h gnu/usr.bin/gcc1/cc1/local-alloc.c gnu/usr.bin/gcc1/cc1/loop.c gnu/usr.bin/gcc1/cc1/machmode.def gnu/usr.bin/gcc1/cc1/math-68881.h gnu/usr.bin/gcc1/cc1/move-if-change gnu/usr.bin/gcc1/cc1/obstack.c gnu/usr.bin/gcc1/cc1/obstack.h gnu/usr.bin/gcc1/cc1/optabs.c gnu/usr.bin/gcc1/cc1/output.h gnu/usr.bin/gcc1/cc1/print-tree.c gnu/usr.bin/gcc1/cc1/proto.h gnu/usr.bin/gcc1/cc1/real.h gnu/usr.bin/gcc1/cc1/recog.c gnu/usr.bin/gcc1/cc1/recog.h gnu/usr.bin/gcc1/cc1/regclass.c gnu/usr.bin/gcc1/cc1/regs.h gnu/usr.bin/gcc1/cc1/reload.c gnu/usr.bin/gcc1/cc1/reload.h gnu/usr.bin/gcc1/cc1/reload1.c gnu/usr.bin/gcc1/cc1/rtl.c gnu/usr.bin/gcc1/cc1/rtl.def gnu/usr.bin/gcc1/cc1/rtl.h gnu/usr.bin/gcc1/cc1/rtlanal.c gnu/usr.bin/gcc1/cc1/sdbout.c gnu/usr.bin/gcc1/cc1/stab.def gnu/usr.bin/gcc1/cc1/stab.h gnu/usr.bin/gcc1/cc1/stmt.c gnu/usr.bin/gcc1/cc1/stor-layout.c gnu/usr.bin/gcc1/cc1/stupid.c gnu/usr.bin/gcc1/cc1/symout.c gnu/usr.bin/gcc1/cc1/symseg.h gnu/usr.bin/gcc1/cc1/toplev.c gnu/usr.bin/gcc1/cc1/tree.c gnu/usr.bin/gcc1/cc1/tree.def gnu/usr.bin/gcc1/cc1/tree.h gnu/usr.bin/gcc1/cc1/typeclass.h gnu/usr.bin/gcc1/cc1/varasm.c gnu/usr.bin/gcc1/cc1/version.c gnu/usr.bin/gcc1/cpp/Makefile gnu/usr.bin/gcc1/cpp/cexp.y gnu/usr.bin/gcc1/cpp/cpp.c gnu/usr.bin/gcc1/cpp/usr.bin.cpp.sh gnu/usr.bin/gcc1/gnulib/DIST/gnulib.c gnu/usr.bin/gcc1/gnulib/DIST/gnulib2.c gnu/usr.bin/gcc1/gnulib/Makefile gnu/usr.bin/gcc1/gnulib/g++/builtin_New.c gnu/usr.bin/gcc1/gnulib/g++/builtin_del.c gnu/usr.bin/gcc1/gnulib/g++/builtin_new.c gnu/usr.bin/gcc1/gnulib/g++/eprintf.c gnu/usr.bin/gcc1/gnulib/i386/DEFS.h gnu/usr.bin/gcc1/gnulib/i386/Makefile.machine gnu/usr.bin/gcc1/gnulib/i386/fixdfsi.s gnu/usr.bin/gcc1/gnulib/i386/fixunsdfsi.s gnu/usr.bin/gcc1/gnulib/longlong/adddi3.c gnu/usr.bin/gcc1/gnulib/longlong/anddi3.c gnu/usr.bin/gcc1/gnulib/longlong/ashldi3.c gnu/usr.bin/gcc1/gnulib/longlong/ashrdi3.c gnu/usr.bin/gcc1/gnulib/longlong/bdiv.c gnu/usr.bin/gcc1/gnulib/longlong/cmpdi2.c gnu/usr.bin/gcc1/gnulib/longlong/divdi3.c gnu/usr.bin/gcc1/gnulib/longlong/fixdfdi.c gnu/usr.bin/gcc1/gnulib/longlong/fixunsdfdi.c gnu/usr.bin/gcc1/gnulib/longlong/floatdidf.c gnu/usr.bin/gcc1/gnulib/longlong/iordi3.c gnu/usr.bin/gcc1/gnulib/longlong/longlong.h gnu/usr.bin/gcc1/gnulib/longlong/lshldi3.c gnu/usr.bin/gcc1/gnulib/longlong/lshrdi3.c gnu/usr.bin/gcc1/gnulib/longlong/moddi3.c gnu/usr.bin/gcc1/gnulib/longlong/muldi3.c gnu/usr.bin/gcc1/gnulib/longlong/negdi2.c gnu/usr.bin/gcc1/gnulib/longlong/one_cmpldi2.c gnu/usr.bin/gcc1/gnulib/longlong/subdi3.c gnu/usr.bin/gcc1/gnulib/longlong/ucmpdi2.c gnu/usr.bin/gcc1/gnulib/longlong/udivdi3.c gnu/usr.bin/gcc1/gnulib/longlong/umoddi3.c gnu/usr.bin/gcc1/gnulib/longlong/xordi3.c gnu/usr.bin/grep/tests/regress.sh gnu/usr.bin/gzip/gzexe.in gnu/usr.bin/gzip/zcmp gnu/usr.bin/man/apropos/apropos gnu/usr.bin/man/apropos/apropos.1 gnu/usr.bin/man/lib/config.h gnu/usr.bin/man/lib/gripes.po gnu/usr.bin/man/lib/util.po gnu/usr.bin/man/makewhatis/makewhatis gnu/usr.bin/man/man/man.1 gnu/usr.bin/man/manpath/manpath gnu/usr.bin/man/manpath/manpath.1 gnu/usr.bin/man/whatis/whatis gnu/usr.bin/man/whatis/whatis.1 gnu/usr.bin/rcs/rcsclean/rcsclean gnu/usr.bin/rcs/rcsclean/rcsclean.0 gnu/usr.bin/tar/y.tab.h lib/libc/db/btree/big.c lib/libc/db/btree/btree.c lib/libc/db/btree/delete.c lib/libc/db/btree/insert.c lib/libc/db/btree/lrucache.c lib/libc/db/btree/lrucache.h lib/libc/db/btree/lruhash.c lib/libc/db/btree/lrutils.c lib/libc/db/btree/search.c lib/libc/db/btree/seq.c lib/libc/db/btree/split.c lib/libc/db/btree/storage.c lib/libc/db/btree/tests/words.c lib/libc/db/btree/updutils.c lib/libc/db/btree/utils.c lib/libc/db/db.3 lib/libc/db/hash/bigkey.c lib/libc/db/hash/buf.c lib/libc/db/hash/dynahash.c lib/libc/db/hash/hash.ps lib/libc/db/hash/hfunc.c lib/libc/db/hash/log2.c lib/libc/db/hash/page.c lib/libc/db/hash/tests/tcreat3.c lib/libc/db/hash/tests/tdel.c lib/libc/db/hash/tests/thash4.c lib/libc/db/hash/tests/tread2.c lib/libc/db/hash/tests/tseq.c lib/libc/db/hash/tests/tverify.c lib/libc/gen/closedir.c lib/libc/gen/crypt_dummy.c lib/libc/gen/opendir.c lib/libc/gen/readdir.c lib/libc/gen/rewinddir.c lib/libc/gen/seekdir.c lib/libc/gen/telldir.c lib/libc/i386/stdlib/atof.c lib/librpc/rpc/rpc.order lib/libtelnet/auth-proto.h lib/libtelnet/auth.c lib/libtelnet/auth.h lib/libtelnet/enc-proto.h lib/libtelnet/enc_des.c lib/libtelnet/encrypt.c lib/libtelnet/encrypt.h lib/libtelnet/kerberos.c lib/libtelnet/kerberos5.c lib/libtelnet/key-proto.h lib/libtelnet/krb_des.c libexec/getty/ttydefaults.c sbin/disklabel/disklabel.5.5 share/man/makewhatis.sed sys/i386/boot/asm.s sys/i386/boot/bios.s sys/i386/boot/boot2.s sys/i386/boot/start.s sys/i386/conf/AHATEST sys/i386/conf/AHBTEST sys/i386/conf/BTTEST sys/i386/conf/CODRV sys/i386/conf/DEVEL sys/i386/conf/GENERICISA sys/i386/conf/LARGE sys/i386/conf/MINITERM sys/i386/conf/ODYSSEUS sys/i386/conf/PHOENIX sys/i386/conf/SMALL sys/i386/conf/SPACE_HEATER sys/i386/conf/SUMNER sys/i386/conf/UHATEST sys/i386/isa/as.c sys/i386/isa/asreg.h sys/i386/isa/codrv/DOC/CHANGES.vak sys/i386/isa/codrv/DOC/CO_HISTORY sys/i386/isa/codrv/DOC/FEATURES sys/i386/isa/codrv/DOC/README sys/i386/isa/codrv/ETC/etc.ttys sys/i386/isa/codrv/co_codrv1.c sys/i386/isa/codrv/co_cons.c sys/i386/isa/codrv/co_hdr.h sys/i386/isa/codrv/co_kbd.c sys/i386/isa/codrv/co_mini.c sys/i386/isa/codrv/co_pc3.c sys/i386/isa/codrv/co_vga.c sys/i386/isa/codrv/co_vty.c sys/i386/isa/ic/ds8390.h sys/i386/isa/if_ec.c sys/i386/isa/if_ec.h sys/i386/isa/if_ne.c sys/i386/isa/if_nereg.h sys/i386/isa/if_we.c sys/i386/isa/if_wereg.h sys/i386/stand/wdboot.x sys/kern/kern_execve sys/scsi/README.AHA1742 sys/sys/ioctl_pc.h usr.bin/ar/ar.5.5 usr.bin/crontab/crontab.5.5 usr.bin/elvis/prsvunix.c usr.bin/elvisrecover/README usr.bin/file/Makefile.ian usr.bin/file/magdir/aa usr.bin/file/magdir/aalocal usr.bin/file/magdir/arc usr.bin/file/magdir/archive usr.bin/file/magdir/c usr.bin/file/magdir/misc usr.bin/file/magdir/misc2 usr.bin/file/magdir/olda.out usr.bin/file/magdir/rasterfile usr.bin/file/magdir/tower usr.bin/file/magdir/vax.byteswap usr.bin/file/magdir/xenix usr.bin/file/magic usr.bin/join/COPYING usr.bin/join/getopt.c usr.bin/join/getopt.h usr.bin/join/getopt1.c usr.bin/lex/lib/ln.order usr.bin/man/Makefile usr.bin/man/apropos/Makefile usr.bin/man/apropos/apropos.1 usr.bin/man/apropos/apropos.c usr.bin/man/man/Makefile usr.bin/man/man/config.c usr.bin/man/man/man.1 usr.bin/man/man/man.c usr.bin/man/man/man.conf.5 usr.bin/man/man/pathnames.h usr.bin/man/whatis/Makefile usr.bin/man/whatis/whatis.1 usr.bin/man/whatis/whatis.c usr.bin/more/more.c usr.bin/ranlib/ranlib.5.5 usr.bin/sccs/Makefile usr.bin/sccs/pathnames.h usr.bin/sccs/sccs.1 usr.bin/sccs/sccs.c usr.bin/sed/COPYING usr.bin/sed/ChangeLog usr.bin/sed/Makefile.gnu usr.bin/sed/README usr.bin/sed/getopt.c usr.bin/sed/getopt.h usr.bin/sed/getopt1.c usr.bin/sed/regex.c usr.bin/sed/regex.h usr.bin/sed/sed.c usr.bin/sed/utils.c usr.bin/tail/COPYING usr.bin/tail/getopt.c usr.bin/tail/getopt.h usr.bin/tail/getopt1.c usr.bin/tn3270/api/makefile usr.bin/tn3270/ascii/makefile usr.bin/tn3270/ctlr/makefile usr.bin/tn3270/general/makefile usr.bin/tn3270/makefile usr.bin/tn3270/map3270.0 usr.bin/tn3270/map3270.5 usr.bin/tn3270/mset.0 usr.bin/tn3270/mset.1 usr.bin/tn3270/sys_curses/makefile usr.bin/tn3270/telnet/Makefile usr.bin/tn3270/tn3270.0 usr.bin/tn3270/tn3270.1 usr.bin/tn3270/tools/dohits.c usr.bin/tn3270/tools/dohits.h usr.bin/tn3270/tools/ectype.c usr.bin/tn3270/tools/ectype.h usr.bin/tn3270/tools/makefile usr.bin/tn3270/tools/mkastods.c usr.bin/tn3270/tools/mkastosc.c usr.bin/tn3270/tools/mkdctype.c usr.bin/tn3270/tools/mkdstoas.c usr.bin/tn3270/tools/mkhits.c usr.sbin/keymap/Makefile usr.sbin/keymap/keymap/Makefile usr.sbin/keymap/keymap/README.keycap usr.sbin/keymap/keymap/keycap.5 usr.sbin/keymap/keymap/keymap.8 usr.sbin/keymap/keymap/keymap.c usr.sbin/keymap/keymap/pathnames.h usr.sbin/keymap/lib/Makefile usr.sbin/keymap/lib/co.4 usr.sbin/keymap/lib/keycap.3 usr.sbin/keymap/lib/keycap.c usr.sbin/keymap/lib/pathnames.h usr.sbin/keymap/lib/setfont.3 usr.sbin/keymap/lib/setfont.c usr.sbin/keymap/lib/vgafont.5 usr.sbin/keymap/lib/whichcons.3 usr.sbin/keymap/lib/whichcons.c usr.sbin/keymap/lib/xc_fns.c usr.sbin/mtree/mtree.1 usr.sbin/sendmail/aux/addr.c usr.sbin/sendmail/aux/arpa.c usr.sbin/sendmail/aux/mail-dm.c usr.sbin/sendmail/aux/matchhdr.c usr.sbin/sendmail/aux/newaliases.c usr.sbin/sendmail/cf/KEY usr.sbin/sendmail/cf/M4_KEY usr.sbin/sendmail/cf/cf/README usr.sbin/sendmail/cf/cf/cad.cf usr.sbin/sendmail/cf/cf/cad.mc usr.sbin/sendmail/cf/cf/cadgroup.cf usr.sbin/sendmail/cf/cf/cadgroup.mc usr.sbin/sendmail/cf/cf/cc.cf usr.sbin/sendmail/cf/cf/cc.mc usr.sbin/sendmail/cf/cf/cchem.cf usr.sbin/sendmail/cf/cf/cchem.mc usr.sbin/sendmail/cf/cf/cogsci.cf usr.sbin/sendmail/cf/cf/ic.cf usr.sbin/sendmail/cf/cf/ic.mc usr.sbin/sendmail/cf/cf/id.cf usr.sbin/sendmail/cf/cf/id.mc usr.sbin/sendmail/cf/cf/kappa.cf usr.sbin/sendmail/cf/cf/lamprey.cf usr.sbin/sendmail/cf/cf/okeeffe.cf usr.sbin/sendmail/cf/cf/okeeffe.mc usr.sbin/sendmail/cf/cf/proto.mc usr.sbin/sendmail/cf/cf/tcpproto.cf usr.sbin/sendmail/cf/cf/tcpuucpproto.cf usr.sbin/sendmail/cf/cf/tcpuucpproto.mc usr.sbin/sendmail/cf/cf/telemuse.cf usr.sbin/sendmail/cf/cf/ucbarpa.cf usr.sbin/sendmail/cf/cf/ucbtcp.cf usr.sbin/sendmail/cf/cf/ucbtcp.mc usr.sbin/sendmail/cf/cf/ucbvax.cf usr.sbin/sendmail/cf/cf/uid.cf usr.sbin/sendmail/cf/cf/uid.mc usr.sbin/sendmail/cf/cf/uucpproto.cf usr.sbin/sendmail/cf/m4/README usr.sbin/sendmail/cf/m4/boilerplate.m4 usr.sbin/sendmail/cf/m4/fake_domains.m4 usr.sbin/sendmail/cf/m4/localm.m4 usr.sbin/sendmail/cf/m4/nsclasses.m4 usr.sbin/sendmail/cf/m4/nsmacros.m4 usr.sbin/sendmail/cf/m4/nstcpldm.m4 usr.sbin/sendmail/cf/m4/nstcpm.m4 usr.sbin/sendmail/cf/m4/postwriterule.m4 usr.sbin/sendmail/cf/m4/prewriterule.m4 usr.sbin/sendmail/cf/m4/rule0.m4 usr.sbin/sendmail/cf/m4/rule3.m4 usr.sbin/sendmail/cf/m4/rule5.m4 usr.sbin/sendmail/cf/m4/smtpuucpm.m4 usr.sbin/sendmail/cf/m4/suucpm.m4 usr.sbin/sendmail/cf/m4/uucpm.m4 usr.sbin/sendmail/cf/sitedep/README usr.sbin/sendmail/cf/sitedep/nicregistered.m4 usr.sbin/sendmail/cf/sitedep/smtpuucp.cad.m4 usr.sbin/sendmail/cf/sitedep/smtpuucp.ucbvax.m4 usr.sbin/sendmail/cf/sitedep/uucp.cad.m4 usr.sbin/sendmail/cf/sitedep/uucp.cogsci.m4 usr.sbin/sendmail/cf/sitedep/uucp.ic.m4 usr.sbin/sendmail/cf/sitedep/uucp.id.m4 usr.sbin/sendmail/cf/sitedep/uucp.okeeffe.m4 usr.sbin/sendmail/cf/sitedep/uucp.proto.m4 usr.sbin/sendmail/cf/sitedep/uucp.ucbarpa.m4 usr.sbin/sendmail/cf/sitedep/uucp.ucbvax.m4 usr.sbin/sendmail/doc/refs usr.sbin/sendmail/doc/rfc819.lpr usr.sbin/sendmail/doc/rfc821.lpr usr.sbin/sendmail/doc/rfc822.lpr usr.sbin/sendmail/doc/spell.good usr.sbin/sendmail/doc/usenix.abs usr.sbin/sendmail/doc/usenix.me usr.sbin/sendmail/mailstats/pathnames.h usr.sbin/sendmail/src/TODO usr.sbin/sendmail/src/sendmail.cf --- BUGNFIX.FORM | 131 - CAREWARE.INFO | 130 - CONTRIB.LIST => CONTRIB.386BSD | 0 CONTRIB.FreeBSD | 179 + FixLinks | 52 + Makefile | 147 +- REGISTRATION | 522 - RELNOTES.FreeBSD | 290 + SOFTWARE.FORM | 131 - bin/Makefile | 4 +- bin/cat/cat.c | 13 +- bin/chmod/chmod.c | 77 +- bin/cp/cp.c | 194 +- bin/cp/extern.h | 51 + bin/cp/path.c | 12 +- bin/csh/Makefile | 4 +- bin/csh/csh.1 | 6 +- bin/csh/dir.c | 3 +- bin/date/date.1 | 2 +- bin/date/date.c | 5 +- bin/dd/Makefile | 3 +- bin/dd/args.c | 373 + bin/dd/conv.c | 260 + bin/dd/conv_tab.c | 357 + bin/dd/dd.1 | 343 + bin/dd/dd.c | 1338 +-- bin/dd/dd.h | 95 + bin/dd/extern.h | 66 + bin/dd/misc.c | 144 + bin/dd/position.c | 163 + bin/df/Makefile | 1 + bin/df/df.1 | 22 +- bin/df/df.c | 87 +- bin/df/getbsize.c | 116 + bin/domainname/Makefile | 6 + bin/domainname/domainname.1 | 60 + bin/domainname/domainname.c | 45 + bin/ed/Makefile | 13 +- bin/ed/POSIX | 12 + bin/ed/README | 1 - bin/ed/buf.c | 44 +- bin/ed/ed.1 | 30 +- bin/ed/ed.c | 280 +- bin/ed/ed.h | 11 +- bin/ed/re.c | 19 +- bin/ed/test/Makefile | 4 +- bin/ed/test/README | 2 +- bin/ed/test/{!1.d => bang1.d} | 0 bin/ed/test/{!1.err => bang1.err} | 0 bin/ed/test/{!1.r => bang1.r} | 0 bin/ed/test/{!1.t => bang1.t} | 0 bin/ed/test/{!2.d => bang2.d} | 0 bin/ed/test/{!2.err => bang2.err} | 0 bin/ed/test/{!2.r => bang2.r} | 0 bin/ed/test/{!2.t => bang2.t} | 0 bin/ed/test/g3.d | 5 + bin/ed/test/g3.r | 5 + bin/ed/test/g3.t | 4 + bin/ed/test/g4.d | 5 + bin/ed/test/g4.r | 7 + bin/ed/test/g4.t | 13 + bin/ed/test/nl.err | 1 + bin/ed/test/nl1.d | 5 + bin/ed/test/nl1.r | 8 + bin/ed/test/nl1.t | 8 + bin/ed/test/nl2.d | 5 + bin/ed/test/nl2.r | 6 + bin/ed/test/nl2.t | 4 + bin/expr/Makefile | 20 +- bin/expr/TODO | 2 - bin/expr/expr.1 | 132 + bin/expr/expr.y | 191 +- bin/kill/kill.1 | 31 +- bin/kill/kill.c | 137 +- bin/ls/cmp.c | 1 + bin/ls/ls.1 | 2 + bin/ls/ls.c | 14 +- bin/ls/ls.h | 2 + bin/ls/print.c | 4 + bin/ls/util.c | 1 + bin/mkdir/mkdir.1 | 37 +- bin/mkdir/mkdir.c | 56 +- bin/ps/devname.c | 2 +- bin/ps/keyword.c | 2 +- bin/ps/ps.1 | 20 +- bin/ps/ps.c | 2 +- bin/pwd/pwd.1 | 1 - bin/rcp/Makefile | 6 + bin/rm/rm.c | 6 +- bin/rmail/Makefile | 2 +- bin/rmail/rmail.8 | 2 - bin/rmdir/rmdir.1 | 38 +- bin/rmdir/rmdir.c | 79 +- bin/sh/Makefile | 21 +- bin/sh/TOUR | 2 + bin/sh/b.c | 4 + bin/sh/bltin/bltin.h | 3 +- bin/sh/bltin/echo.1 | 3 +- bin/sh/bltin/echo.c | 7 +- bin/sh/builtins | 3 + bin/sh/cd.c | 10 +- bin/sh/dirent.c | 3 +- bin/sh/errmsg.c | 3 +- bin/sh/errmsg.h | 3 +- bin/sh/error.c | 5 +- bin/sh/error.h | 3 +- bin/sh/eval.c | 6 +- bin/sh/eval.h | 3 +- bin/sh/exec.c | 84 +- bin/sh/exec.h | 3 +- bin/sh/expand.c | 11 +- bin/sh/expand.h | 3 +- bin/sh/funcs/cmv | 2 + bin/sh/funcs/dirs | 2 + bin/sh/funcs/kill | 2 + bin/sh/funcs/login | 2 + bin/sh/funcs/newgrp | 2 + bin/sh/funcs/popd | 2 + bin/sh/funcs/pushd | 2 + bin/sh/funcs/suspend | 2 + bin/sh/init.h | 3 +- bin/sh/input.c | 3 +- bin/sh/input.h | 3 +- bin/sh/jobs.c | 29 +- bin/sh/jobs.h | 3 +- bin/sh/machdep.h | 3 +- bin/sh/mail.c | 3 +- bin/sh/mail.h | 3 +- bin/sh/main.c | 3 +- bin/sh/main.h | 3 +- bin/sh/memalloc.c | 3 +- bin/sh/memalloc.h | 3 +- bin/sh/miscbltin.c | 83 +- bin/sh/mkbuiltins | 7 +- bin/sh/mkinit.c | 3 +- bin/sh/mknodes.c | 4 +- bin/sh/mksignames.c | 3 +- bin/sh/mksyntax.c | 3 +- bin/sh/mktokens | 2 + bin/sh/mt | 2 + bin/sh/mystring.c | 3 +- bin/sh/mystring.h | 3 +- bin/sh/nodes.c.pat | 2 + bin/sh/nodetypes | 2 + bin/sh/options.c | 3 +- bin/sh/options.h | 3 +- bin/sh/output.c | 5 +- bin/sh/output.h | 3 +- bin/sh/parser.c | 24 +- bin/sh/parser.h | 3 +- bin/sh/redir.c | 11 +- bin/sh/redir.h | 11 +- bin/sh/sh.1 | 12 +- bin/sh/shell.h | 3 +- bin/sh/show.c | 3 +- bin/sh/trap.c | 38 +- bin/sh/trap.h | 3 +- bin/sh/var.c | 3 +- bin/sh/var.h | 3 +- bin/stty/key.c | 2 +- bin/stty/stty.c | 1 + bin/sync/Makefile | 2 +- bin/test/Makefile | 17 +- bin/test/TEST.csh | 147 + bin/test/binary_op | 56 + bin/test/mkops | 84 + bin/test/operators.c | 115 + bin/test/operators.h | 41 + bin/test/test.1 | 251 + bin/test/test.c | 1215 +- bin/test/unary_op | 59 + contrib/Makefile | 2 +- contrib/Makefile.inc | 2 +- contrib/tcpdump/Makefile.inc | 2 +- contrib/tcpdump/tcpdump/Makefile | 4 +- contrib/tcpdump/tcpdump/interface.h | 4 +- contrib/tcpdump/tcpdump/print-nfs.c | 9 +- contrib/tcpdump/tcpdump/print-sunrpc.c | 10 +- contrib/tcpdump/tcpdump/print-udp.c | 9 +- contrib/tcpdump/tcpslice/Makefile | 2 +- etc/Makefile | 427 +- etc/aliases | 30 +- etc/crontab | 27 +- etc/csh.login | 3 + etc/daily | 18 +- etc/etc.i386/MAKEDEV | 333 +- etc/etc.i386/disktab | 22 +- etc/etc.i386/floppy.install_notes | 153 + etc/etc.i386/inst1.install | 1015 ++ etc/etc.i386/inst1.profile | 7 + etc/etc.i386/inst2.install | 40 + etc/etc.i386/inst2.profile | 440 + etc/etc.i386/install_notes | 1055 ++ etc/etc.i386/kc.profile | 83 + etc/gettytab | 36 +- etc/group | 2 +- etc/host.conf | 4 + etc/inetd.conf | 11 +- etc/man.conf | 34 - etc/master.passwd | 14 +- etc/monthly | 2 +- etc/motd | 4 +- etc/mtree/BSD.local.dist | 55 + etc/mtree/BSD.root.dist | 76 +- etc/mtree/BSD.usr.dist | 517 +- etc/mtree/BSD.var.dist | 93 +- etc/myname | 1 + etc/netstart | 26 +- etc/phones | 6 +- etc/printcap | 4 +- etc/rc | 6 +- etc/rc.local | 2 +- etc/remote | 18 +- etc/root/dot.cshrc | 19 +- etc/root/dot.login | 2 +- etc/root/dot.profile | 2 +- etc/rpc | 34 + etc/security | 28 +- etc/services | 1 + etc/ttys | 6 +- etc/weekly | 38 +- games/Makefile | 2 +- games/arithmetic/Makefile | 2 +- games/atc/Makefile | 2 +- games/backgammon/backgammon/Makefile | 2 +- games/banner/Makefile | 2 +- games/battlestar/Makefile | 2 +- games/bcd/Makefile | 2 +- games/caesar/Makefile | 2 +- games/canfield/canfield/Makefile | 2 +- games/canfield/canfield/canfield.6 | 2 +- games/cribbage/Makefile | 2 +- games/dm/Makefile | 5 +- games/factor/Makefile | 2 +- games/fish/Makefile | 4 +- games/fortune/Makefile | 2 +- games/fortune/datfiles/Makefile | 11 +- games/fortune/fortune/Makefile | 2 +- games/fortune/strfile/Makefile | 2 +- games/hack/Makefile | 7 +- games/hack/hack.onames.h | 227 - games/hangman/Makefile | 2 +- games/larn/Makefile | 4 +- games/mille/Makefile | 2 +- games/monop/Makefile | 4 +- games/monop/monop.6 | 2 +- games/monop/roll.c | 2 +- games/number/Makefile | 2 +- games/pom/Makefile | 2 +- games/rain/Makefile | 2 +- games/robots/Makefile | 2 +- games/robots/robots.6 | 4 +- games/rogue/Makefile | 2 +- games/rogue/rogue.6 | 4 +- games/rogue/score.c | 2 +- games/sail/Makefile | 2 +- games/sail/player.h | 2 +- games/snake/snake/Makefile | 2 +- games/snake/snake/snake.6 | 9 +- games/snake/snscore/snscore.c | 2 +- games/trek/Makefile | 2 +- games/worm/Makefile | 2 +- games/worms/Makefile | 2 +- games/wump/Makefile | 2 +- gnu/COPYING.LIB | 481 + gnu/Makefile | 3 +- gnu/Makefile.inc | 4 + gnu/games/chess/Makefile | 9 +- gnu/games/chess/Xchess/xchess | Bin 171979 -> 0 bytes gnu/games/chess/chess.6 | 161 + gnu/lib/libg++/COPYING | 354 +- gnu/lib/libg++/ChangeLog | 1103 -- gnu/lib/libg++/Makefile | 41 +- gnu/lib/libg++/Makefile.gnu | 276 - gnu/lib/libg++/README.gnu | 269 - gnu/lib/libg++/g++-include/ACG.h | 74 - gnu/lib/libg++/g++-include/AVLMap.ccP | 614 + gnu/lib/libg++/g++-include/AVLMap.hP | 141 + gnu/lib/libg++/g++-include/AVLSet.ccP | 892 ++ gnu/lib/libg++/g++-include/AVLSet.hP | 152 + gnu/lib/libg++/g++-include/AVec.ccP | 397 + gnu/lib/libg++/g++-include/AVec.hP | 113 + gnu/lib/libg++/g++-include/AllocRing.h | 68 - gnu/lib/libg++/g++-include/BSTSet.ccP | 378 + gnu/lib/libg++/g++-include/BSTSet.hP | 152 + gnu/lib/libg++/g++-include/Bag.ccP | 74 + gnu/lib/libg++/g++-include/Bag.hP | 79 + gnu/lib/libg++/g++-include/Binomial.h | 63 - gnu/lib/libg++/g++-include/BitSet.h | 385 - gnu/lib/libg++/g++-include/BitString.h | 773 -- gnu/lib/libg++/g++-include/CHBag.ccP | 209 + gnu/lib/libg++/g++-include/CHBag.hP | 76 + gnu/lib/libg++/g++-include/CHMap.ccP | 166 + gnu/lib/libg++/g++-include/CHMap.hP | 104 + gnu/lib/libg++/g++-include/CHNode.ccP | 21 + gnu/lib/libg++/g++-include/CHNode.hP | 43 + gnu/lib/libg++/g++-include/CHSet.ccP | 271 + gnu/lib/libg++/g++-include/CHSet.hP | 84 + gnu/lib/libg++/g++-include/Complex.h | 293 - gnu/lib/libg++/g++-include/CursesW.h | 397 - gnu/lib/libg++/g++-include/DLDeque.ccP | 4 + gnu/lib/libg++/g++-include/DLDeque.hP | 130 + gnu/lib/libg++/g++-include/DLList.ccP | 339 + gnu/lib/libg++/g++-include/DLList.hP | 157 + gnu/lib/libg++/g++-include/Deque.ccP | 11 + gnu/lib/libg++/g++-include/Deque.hP | 57 + gnu/lib/libg++/g++-include/DiscUnif.h | 79 - gnu/lib/libg++/g++-include/Erlang.h | 77 - gnu/lib/libg++/g++-include/FPQueue.ccP | 4 + gnu/lib/libg++/g++-include/FPQueue.hP | 112 + gnu/lib/libg++/g++-include/FPStack.ccP | 4 + gnu/lib/libg++/g++-include/FPStack.hP | 114 + gnu/lib/libg++/g++-include/FPlex.ccP | 167 + gnu/lib/libg++/g++-include/FPlex.hP | 253 + gnu/lib/libg++/g++-include/File.h | 302 - gnu/lib/libg++/g++-include/Filebuf.h | 32 - gnu/lib/libg++/g++-include/Fix.h | 468 - gnu/lib/libg++/g++-include/Fix16.h | 647 - gnu/lib/libg++/g++-include/Fix24.h | 595 - gnu/lib/libg++/g++-include/Fmodes.h | 34 - gnu/lib/libg++/g++-include/Geom.h | 59 - gnu/lib/libg++/g++-include/GetOpt.h | 131 - gnu/lib/libg++/g++-include/HypGeom.h | 78 - gnu/lib/libg++/g++-include/Incremental.h | 13 - gnu/lib/libg++/g++-include/Integer.h | 1114 -- gnu/lib/libg++/g++-include/List.ccP | 956 ++ gnu/lib/libg++/g++-include/List.hP | 273 + gnu/lib/libg++/g++-include/LogNorm.h | 84 - gnu/lib/libg++/g++-include/MLCG.h | 97 - gnu/lib/libg++/g++-include/MPlex.ccP | 845 ++ gnu/lib/libg++/g++-include/MPlex.hP | 414 + gnu/lib/libg++/g++-include/Makefile | 50 - gnu/lib/libg++/g++-include/Map.ccP | 58 + gnu/lib/libg++/g++-include/Map.hP | 87 + gnu/lib/libg++/g++-include/NegExp.h | 63 - gnu/lib/libg++/g++-include/Normal.h | 74 - gnu/lib/libg++/g++-include/OSLBag.ccP | 196 + gnu/lib/libg++/g++-include/OSLBag.hP | 91 + gnu/lib/libg++/g++-include/OSLSet.ccP | 321 + gnu/lib/libg++/g++-include/OSLSet.hP | 101 + gnu/lib/libg++/g++-include/OXPBag.ccP | 221 + gnu/lib/libg++/g++-include/OXPBag.hP | 73 + gnu/lib/libg++/g++-include/OXPSet.ccP | 280 + gnu/lib/libg++/g++-include/OXPSet.hP | 102 + gnu/lib/libg++/g++-include/Obstack.h | 226 - gnu/lib/libg++/g++-include/PHPQ.ccP | 339 + gnu/lib/libg++/g++-include/PHPQ.hP | 108 + gnu/lib/libg++/g++-include/PQ.ccP | 62 + gnu/lib/libg++/g++-include/PQ.hP | 78 + gnu/lib/libg++/g++-include/PSList.hP | 32 + gnu/lib/libg++/g++-include/PVec.hP | 79 + gnu/lib/libg++/g++-include/Pix.h | 9 - gnu/lib/libg++/g++-include/Plex.ccP | 222 + gnu/lib/libg++/g++-include/Plex.hP | 494 + gnu/lib/libg++/g++-include/PlotFile.h | 154 - gnu/lib/libg++/g++-include/Poisson.h | 60 - gnu/lib/libg++/g++-include/Queue.ccP | 14 + gnu/lib/libg++/g++-include/Queue.hP | 51 + gnu/lib/libg++/g++-include/RAVLMap.ccP | 690 ++ gnu/lib/libg++/g++-include/RAVLMap.hP | 147 + gnu/lib/libg++/g++-include/RNG.h | 88 - gnu/lib/libg++/g++-include/RPlex.ccP | 477 + gnu/lib/libg++/g++-include/RPlex.hP | 257 + gnu/lib/libg++/g++-include/Random.h | 67 - gnu/lib/libg++/g++-include/Rational.h | 270 - gnu/lib/libg++/g++-include/Regex.h | 76 - gnu/lib/libg++/g++-include/RndInt.h | 169 - gnu/lib/libg++/g++-include/SFile.h | 88 - gnu/lib/libg++/g++-include/SLBag.ccP | 105 + gnu/lib/libg++/g++-include/SLBag.hP | 96 + gnu/lib/libg++/g++-include/SLList.ccP | 292 + gnu/lib/libg++/g++-include/SLList.hP | 137 + gnu/lib/libg++/g++-include/SLQueue.ccP | 4 + gnu/lib/libg++/g++-include/SLQueue.hP | 108 + gnu/lib/libg++/g++-include/SLSet.ccP | 76 + gnu/lib/libg++/g++-include/SLSet.hP | 87 + gnu/lib/libg++/g++-include/SLStack.ccP | 4 + gnu/lib/libg++/g++-include/SLStack.hP | 109 + gnu/lib/libg++/g++-include/Set.ccP | 116 + gnu/lib/libg++/g++-include/Set.hP | 78 + gnu/lib/libg++/g++-include/SkipBag.ccP | 322 + gnu/lib/libg++/g++-include/SkipBag.hP | 171 + gnu/lib/libg++/g++-include/SkipMap.ccP | 307 + gnu/lib/libg++/g++-include/SkipMap.hP | 176 + gnu/lib/libg++/g++-include/SkipSet.ccP | 395 + gnu/lib/libg++/g++-include/SkipSet.hP | 187 + gnu/lib/libg++/g++-include/SmplHist.h | 82 - gnu/lib/libg++/g++-include/SmplStat.h | 73 - gnu/lib/libg++/g++-include/SplayBag.ccP | 445 + gnu/lib/libg++/g++-include/SplayBag.hP | 144 + gnu/lib/libg++/g++-include/SplayMap.ccP | 401 + gnu/lib/libg++/g++-include/SplayMap.hP | 154 + gnu/lib/libg++/g++-include/SplayNode.ccP | 21 + gnu/lib/libg++/g++-include/SplayNode.hP | 44 + gnu/lib/libg++/g++-include/SplayPQ.ccP | 523 + gnu/lib/libg++/g++-include/SplayPQ.hP | 123 + gnu/lib/libg++/g++-include/SplaySet.ccP | 499 + gnu/lib/libg++/g++-include/SplaySet.hP | 133 + gnu/lib/libg++/g++-include/Stack.ccP | 11 + gnu/lib/libg++/g++-include/Stack.hP | 51 + gnu/lib/libg++/g++-include/String.h | 1341 --- gnu/lib/libg++/g++-include/Uniform.h | 81 - gnu/lib/libg++/g++-include/VHBag.ccP | 264 + gnu/lib/libg++/g++-include/VHBag.hP | 84 + gnu/lib/libg++/g++-include/VHMap.ccP | 210 + gnu/lib/libg++/g++-include/VHMap.hP | 84 + gnu/lib/libg++/g++-include/VHSet.ccP | 263 + gnu/lib/libg++/g++-include/VHSet.hP | 96 + gnu/lib/libg++/g++-include/VOHSet.ccP | 308 + gnu/lib/libg++/g++-include/VOHSet.hP | 88 + gnu/lib/libg++/g++-include/VQueue.ccP | 83 + gnu/lib/libg++/g++-include/VQueue.hP | 130 + gnu/lib/libg++/g++-include/VStack.ccP | 66 + gnu/lib/libg++/g++-include/VStack.hP | 120 + gnu/lib/libg++/g++-include/Vec.ccP | 470 + gnu/lib/libg++/g++-include/Vec.hP | 135 + gnu/lib/libg++/g++-include/Weibull.h | 83 - gnu/lib/libg++/g++-include/XPBag.ccP | 72 + gnu/lib/libg++/g++-include/XPBag.hP | 98 + gnu/lib/libg++/g++-include/XPDeque.ccP | 4 + gnu/lib/libg++/g++-include/XPDeque.hP | 133 + gnu/lib/libg++/g++-include/XPPQ.ccP | 143 + gnu/lib/libg++/g++-include/XPPQ.hP | 105 + gnu/lib/libg++/g++-include/XPQueue.ccP | 4 + gnu/lib/libg++/g++-include/XPQueue.hP | 114 + gnu/lib/libg++/g++-include/XPSet.ccP | 63 + gnu/lib/libg++/g++-include/XPSet.hP | 89 + gnu/lib/libg++/g++-include/XPStack.ccP | 4 + gnu/lib/libg++/g++-include/XPStack.hP | 115 + gnu/lib/libg++/g++-include/XPlex.ccP | 397 + gnu/lib/libg++/g++-include/XPlex.hP | 238 + gnu/lib/libg++/g++-include/abs.h | 1 - gnu/lib/libg++/g++-include/assert.h | 55 +- gnu/lib/libg++/g++-include/bool.h | 10 - gnu/lib/libg++/g++-include/bstring.h | 1 + gnu/lib/libg++/g++-include/builtin.h | 276 - gnu/lib/libg++/g++-include/compare.h | 98 - gnu/lib/libg++/g++-include/complex.h | 9 - gnu/lib/libg++/g++-include/ctype.h | 10 + gnu/lib/libg++/g++-include/curses.h | 398 +- gnu/lib/libg++/g++-include/defs.hP | 57 + gnu/lib/libg++/g++-include/dir.h | 1 + gnu/lib/libg++/g++-include/dirent.h | 44 + gnu/lib/libg++/g++-include/errno.h | 24 + gnu/lib/libg++/g++-include/fcntl.h | 29 + gnu/lib/libg++/g++-include/file.h | 8 - gnu/lib/libg++/g++-include/filebuf.h | 63 - gnu/lib/libg++/g++-include/gen/AVLMap.ccP | 614 + gnu/lib/libg++/g++-include/gen/AVLMap.hP | 141 + gnu/lib/libg++/g++-include/gen/AVLSet.ccP | 892 ++ gnu/lib/libg++/g++-include/gen/AVLSet.hP | 152 + gnu/lib/libg++/g++-include/gen/AVec.ccP | 397 + gnu/lib/libg++/g++-include/gen/AVec.hP | 113 + gnu/lib/libg++/g++-include/gen/BSTSet.ccP | 378 + gnu/lib/libg++/g++-include/gen/BSTSet.hP | 152 + gnu/lib/libg++/g++-include/gen/Bag.ccP | 74 + gnu/lib/libg++/g++-include/gen/Bag.hP | 79 + gnu/lib/libg++/g++-include/gen/CHBag.ccP | 209 + gnu/lib/libg++/g++-include/gen/CHBag.hP | 76 + gnu/lib/libg++/g++-include/gen/CHMap.ccP | 166 + gnu/lib/libg++/g++-include/gen/CHMap.hP | 104 + gnu/lib/libg++/g++-include/gen/CHNode.ccP | 21 + gnu/lib/libg++/g++-include/gen/CHNode.hP | 43 + gnu/lib/libg++/g++-include/gen/CHSet.ccP | 271 + gnu/lib/libg++/g++-include/gen/CHSet.hP | 84 + gnu/lib/libg++/g++-include/gen/DLDeque.ccP | 4 + gnu/lib/libg++/g++-include/gen/DLDeque.hP | 130 + gnu/lib/libg++/g++-include/gen/DLList.ccP | 339 + gnu/lib/libg++/g++-include/gen/DLList.hP | 157 + gnu/lib/libg++/g++-include/gen/Deque.ccP | 11 + gnu/lib/libg++/g++-include/gen/Deque.hP | 57 + gnu/lib/libg++/g++-include/gen/FPQueue.ccP | 4 + gnu/lib/libg++/g++-include/gen/FPQueue.hP | 112 + gnu/lib/libg++/g++-include/gen/FPStack.ccP | 4 + gnu/lib/libg++/g++-include/gen/FPStack.hP | 114 + gnu/lib/libg++/g++-include/gen/FPlex.ccP | 167 + gnu/lib/libg++/g++-include/gen/FPlex.hP | 253 + gnu/lib/libg++/g++-include/gen/List.ccP | 956 ++ gnu/lib/libg++/g++-include/gen/List.hP | 273 + gnu/lib/libg++/g++-include/gen/MPlex.ccP | 845 ++ gnu/lib/libg++/g++-include/gen/MPlex.hP | 414 + gnu/lib/libg++/g++-include/gen/Map.ccP | 58 + gnu/lib/libg++/g++-include/gen/Map.hP | 87 + gnu/lib/libg++/g++-include/gen/OSLBag.ccP | 196 + gnu/lib/libg++/g++-include/gen/OSLBag.hP | 91 + gnu/lib/libg++/g++-include/gen/OSLSet.ccP | 321 + gnu/lib/libg++/g++-include/gen/OSLSet.hP | 101 + gnu/lib/libg++/g++-include/gen/OXPBag.ccP | 221 + gnu/lib/libg++/g++-include/gen/OXPBag.hP | 73 + gnu/lib/libg++/g++-include/gen/OXPSet.ccP | 280 + gnu/lib/libg++/g++-include/gen/OXPSet.hP | 102 + gnu/lib/libg++/g++-include/gen/PHPQ.ccP | 339 + gnu/lib/libg++/g++-include/gen/PHPQ.hP | 108 + gnu/lib/libg++/g++-include/gen/PQ.ccP | 62 + gnu/lib/libg++/g++-include/gen/PQ.hP | 78 + gnu/lib/libg++/g++-include/gen/PSList.hP | 32 + gnu/lib/libg++/g++-include/gen/PVec.hP | 79 + gnu/lib/libg++/g++-include/gen/Plex.ccP | 222 + gnu/lib/libg++/g++-include/gen/Plex.hP | 494 + gnu/lib/libg++/g++-include/gen/Queue.ccP | 14 + gnu/lib/libg++/g++-include/gen/Queue.hP | 51 + gnu/lib/libg++/g++-include/gen/RAVLMap.ccP | 690 ++ gnu/lib/libg++/g++-include/gen/RAVLMap.hP | 147 + gnu/lib/libg++/g++-include/gen/RPlex.ccP | 477 + gnu/lib/libg++/g++-include/gen/RPlex.hP | 257 + gnu/lib/libg++/g++-include/gen/SLBag.ccP | 105 + gnu/lib/libg++/g++-include/gen/SLBag.hP | 96 + gnu/lib/libg++/g++-include/gen/SLList.ccP | 292 + gnu/lib/libg++/g++-include/gen/SLList.hP | 137 + gnu/lib/libg++/g++-include/gen/SLQueue.ccP | 4 + gnu/lib/libg++/g++-include/gen/SLQueue.hP | 108 + gnu/lib/libg++/g++-include/gen/SLSet.ccP | 76 + gnu/lib/libg++/g++-include/gen/SLSet.hP | 87 + gnu/lib/libg++/g++-include/gen/SLStack.ccP | 4 + gnu/lib/libg++/g++-include/gen/SLStack.hP | 109 + gnu/lib/libg++/g++-include/gen/Set.ccP | 116 + gnu/lib/libg++/g++-include/gen/Set.hP | 78 + gnu/lib/libg++/g++-include/gen/SkipBag.ccP | 322 + gnu/lib/libg++/g++-include/gen/SkipBag.hP | 171 + gnu/lib/libg++/g++-include/gen/SkipMap.ccP | 307 + gnu/lib/libg++/g++-include/gen/SkipMap.hP | 176 + gnu/lib/libg++/g++-include/gen/SkipSet.ccP | 395 + gnu/lib/libg++/g++-include/gen/SkipSet.hP | 187 + gnu/lib/libg++/g++-include/gen/SplayBag.ccP | 445 + gnu/lib/libg++/g++-include/gen/SplayBag.hP | 144 + gnu/lib/libg++/g++-include/gen/SplayMap.ccP | 401 + gnu/lib/libg++/g++-include/gen/SplayMap.hP | 154 + gnu/lib/libg++/g++-include/gen/SplayNode.ccP | 21 + gnu/lib/libg++/g++-include/gen/SplayNode.hP | 44 + gnu/lib/libg++/g++-include/gen/SplayPQ.ccP | 523 + gnu/lib/libg++/g++-include/gen/SplayPQ.hP | 123 + gnu/lib/libg++/g++-include/gen/SplaySet.ccP | 499 + gnu/lib/libg++/g++-include/gen/SplaySet.hP | 133 + gnu/lib/libg++/g++-include/gen/Stack.ccP | 11 + gnu/lib/libg++/g++-include/gen/Stack.hP | 51 + gnu/lib/libg++/g++-include/gen/VHBag.ccP | 264 + gnu/lib/libg++/g++-include/gen/VHBag.hP | 84 + gnu/lib/libg++/g++-include/gen/VHMap.ccP | 210 + gnu/lib/libg++/g++-include/gen/VHMap.hP | 84 + gnu/lib/libg++/g++-include/gen/VHSet.ccP | 263 + gnu/lib/libg++/g++-include/gen/VHSet.hP | 96 + gnu/lib/libg++/g++-include/gen/VOHSet.ccP | 308 + gnu/lib/libg++/g++-include/gen/VOHSet.hP | 88 + gnu/lib/libg++/g++-include/gen/VQueue.ccP | 83 + gnu/lib/libg++/g++-include/gen/VQueue.hP | 130 + gnu/lib/libg++/g++-include/gen/VStack.ccP | 66 + gnu/lib/libg++/g++-include/gen/VStack.hP | 120 + gnu/lib/libg++/g++-include/gen/Vec.ccP | 470 + gnu/lib/libg++/g++-include/gen/Vec.hP | 135 + gnu/lib/libg++/g++-include/gen/XPBag.ccP | 72 + gnu/lib/libg++/g++-include/gen/XPBag.hP | 98 + gnu/lib/libg++/g++-include/gen/XPDeque.ccP | 4 + gnu/lib/libg++/g++-include/gen/XPDeque.hP | 133 + gnu/lib/libg++/g++-include/gen/XPPQ.ccP | 143 + gnu/lib/libg++/g++-include/gen/XPPQ.hP | 105 + gnu/lib/libg++/g++-include/gen/XPQueue.ccP | 4 + gnu/lib/libg++/g++-include/gen/XPQueue.hP | 114 + gnu/lib/libg++/g++-include/gen/XPSet.ccP | 63 + gnu/lib/libg++/g++-include/gen/XPSet.hP | 89 + gnu/lib/libg++/g++-include/gen/XPStack.ccP | 4 + gnu/lib/libg++/g++-include/gen/XPStack.hP | 115 + gnu/lib/libg++/g++-include/gen/XPlex.ccP | 397 + gnu/lib/libg++/g++-include/gen/XPlex.hP | 238 + gnu/lib/libg++/g++-include/gen/defs.hP | 57 + gnu/lib/libg++/g++-include/gen/intSList.hP | 33 + gnu/lib/libg++/g++-include/gen/intVec.hP | 80 + gnu/lib/libg++/g++-include/generic.h | 63 - gnu/lib/libg++/g++-include/grp.h | 41 + gnu/lib/libg++/g++-include/intSList.hP | 33 + gnu/lib/libg++/g++-include/intVec.hP | 80 + gnu/lib/libg++/g++-include/istream.h | 259 - gnu/lib/libg++/g++-include/malloc.h | 1 - gnu/lib/libg++/g++-include/math.h | 221 + gnu/lib/libg++/g++-include/max.h | 1 - gnu/lib/libg++/g++-include/memory.h | 42 + gnu/lib/libg++/g++-include/min.h | 1 - gnu/lib/libg++/g++-include/minmax.h | 1 - gnu/lib/libg++/g++-include/netdb.h | 4 + gnu/lib/libg++/g++-include/new.h | 33 - gnu/lib/libg++/g++-include/open.h | 39 - gnu/lib/libg++/g++-include/osfcn.h | 4 - gnu/lib/libg++/g++-include/ostream.h | 256 - gnu/lib/libg++/g++-include/pwd.h | 36 + gnu/lib/libg++/g++-include/regex.h | 280 - gnu/lib/libg++/g++-include/setjmp.h | 29 + gnu/lib/libg++/g++-include/signal.h | 80 + gnu/lib/libg++/g++-include/std.h | 4 - gnu/lib/libg++/g++-include/stdarg.h | 3 + gnu/lib/libg++/g++-include/stddef.h | 12 + gnu/lib/libg++/g++-include/stdio.h | 180 + gnu/lib/libg++/g++-include/stdlib.h | 81 + gnu/lib/libg++/g++-include/strclass.h | 8 - gnu/lib/libg++/g++-include/stream.h | 13 - gnu/lib/libg++/g++-include/streambuf.h | 165 - gnu/lib/libg++/g++-include/string.h | 45 + gnu/lib/libg++/g++-include/strings.h | 1 + gnu/lib/libg++/g++-include/sys/dir.h | 42 + gnu/lib/libg++/g++-include/sys/fcntl.h | 15 + gnu/lib/libg++/g++-include/sys/file.h | 28 + gnu/lib/libg++/g++-include/sys/mman.h | 14 + gnu/lib/libg++/g++-include/sys/param.h | 22 + gnu/lib/libg++/g++-include/sys/resource.h | 29 + gnu/lib/libg++/g++-include/sys/select.h | 8 + gnu/lib/libg++/g++-include/sys/signal.h | 37 + gnu/lib/libg++/g++-include/sys/socket.h | 63 + gnu/lib/libg++/g++-include/sys/stat.h | 47 + gnu/lib/libg++/g++-include/sys/time.h | 41 + gnu/lib/libg++/g++-include/sys/times.h | 20 + gnu/lib/libg++/g++-include/sys/types.h | 25 + gnu/lib/libg++/g++-include/sys/wait.h | 42 + gnu/lib/libg++/g++-include/time.h | 108 + gnu/lib/libg++/g++-include/unistd.h | 187 + gnu/lib/libg++/g++-include/values.h | 33 +- gnu/lib/libg++/genclass/Makefile | 21 +- gnu/lib/libg++/genclass/genclass.1 | 1 - gnu/lib/libg++/genclass/genclass.sh | 481 +- gnu/lib/libg++/iostream/PlotFile.C | 130 + gnu/lib/libg++/iostream/PlotFile.h | 118 + gnu/lib/libg++/iostream/SFile.C | 58 + gnu/lib/libg++/iostream/SFile.h | 46 + gnu/lib/libg++/iostream/editbuf.C | 707 ++ gnu/lib/libg++/iostream/editbuf.h | 173 + gnu/lib/libg++/iostream/filebuf.C | 580 + gnu/lib/libg++/iostream/floatconv.C | 2416 ++++ gnu/lib/libg++/iostream/floatio.h | 27 + gnu/lib/libg++/iostream/fstream.C | 65 + gnu/lib/libg++/iostream/fstream.h | 70 + gnu/lib/libg++/iostream/igetline.C | 135 + gnu/lib/libg++/iostream/igetsb.C | 48 + gnu/lib/libg++/iostream/indstream.C | 108 + gnu/lib/libg++/iostream/indstream.h | 65 + gnu/lib/libg++/iostream/iomanip.C | 77 + gnu/lib/libg++/iostream/iomanip.h | 150 + gnu/lib/libg++/iostream/ioprivate.h | 64 + gnu/lib/libg++/iostream/iostream.C | 783 ++ gnu/lib/libg++/iostream/iostream.h | 226 + gnu/lib/libg++/iostream/makebuf.C | 71 + gnu/lib/libg++/iostream/outfloat.C | 183 + gnu/lib/libg++/iostream/parsestream.C | 307 + gnu/lib/libg++/iostream/parsestream.h | 145 + gnu/lib/libg++/iostream/procbuf.C | 126 + gnu/lib/libg++/iostream/procbuf.h | 29 + gnu/lib/libg++/iostream/sbufvform.C | 856 ++ gnu/lib/libg++/iostream/sbufvscan.C | 746 ++ gnu/lib/libg++/iostream/sgetline.C | 64 + gnu/lib/libg++/iostream/stdiostream.C | 112 + gnu/lib/libg++/iostream/stdiostream.h | 43 + gnu/lib/libg++/iostream/stdstrbufs.C | 113 + gnu/lib/libg++/iostream/stdstreams.C | 145 + gnu/lib/libg++/iostream/stream.C | 120 + gnu/lib/libg++/iostream/stream.h | 31 + gnu/lib/libg++/iostream/streambuf.C | 666 + gnu/lib/libg++/iostream/streambuf.h | 494 + gnu/lib/libg++/iostream/strstream.C | 237 + gnu/lib/libg++/iostream/strstream.h | 103 + gnu/lib/libg++/libg++/ACG.cc | 23 +- gnu/lib/libg++/libg++/ACG.h | 68 + gnu/lib/libg++/libg++/AllocRing.cc | 37 +- gnu/lib/libg++/libg++/AllocRing.h | 62 + gnu/lib/libg++/libg++/Binomial.cc | 28 +- gnu/lib/libg++/libg++/Binomial.h | 55 + gnu/lib/libg++/libg++/BitSet.cc | 195 +- gnu/lib/libg++/libg++/BitSet.h | 361 + gnu/lib/libg++/libg++/BitString.cc | 153 +- gnu/lib/libg++/libg++/BitString.h | 757 ++ gnu/lib/libg++/libg++/Complex.cc | 65 +- gnu/lib/libg++/libg++/Complex.h | 270 + gnu/lib/libg++/libg++/CursesW.cc | 126 +- gnu/lib/libg++/libg++/CursesW.h | 580 + gnu/lib/libg++/libg++/DLList.cc | 327 + gnu/lib/libg++/libg++/DLList.h | 124 + gnu/lib/libg++/libg++/DiscUnif.cc | 28 +- gnu/lib/libg++/libg++/DiscUnif.h | 72 + gnu/lib/libg++/libg++/EH.cc | 43 - gnu/lib/libg++/libg++/EH2.c | 20 - gnu/lib/libg++/libg++/Erlang.cc | 28 +- gnu/lib/libg++/libg++/Erlang.h | 68 + gnu/lib/libg++/libg++/File.cc | 481 - gnu/lib/libg++/libg++/Filebuf.cc | 25 - gnu/lib/libg++/libg++/Fix.cc | 93 +- gnu/lib/libg++/libg++/Fix.h | 469 + gnu/lib/libg++/libg++/Fix16.cc | 43 +- gnu/lib/libg++/libg++/Fix16.h | 648 + gnu/lib/libg++/libg++/Fix24.cc | 57 +- gnu/lib/libg++/libg++/Fix24.h | 597 + gnu/lib/libg++/libg++/Geom.cc | 28 +- gnu/lib/libg++/libg++/Geom.h | 52 + gnu/lib/libg++/libg++/GetOpt.cc | 63 +- gnu/lib/libg++/libg++/GetOpt.h | 129 + gnu/lib/libg++/libg++/HypGeom.cc | 28 +- gnu/lib/libg++/libg++/HypGeom.h | 70 + gnu/lib/libg++/libg++/Incremental.h | 12 + gnu/lib/libg++/libg++/Integer.cc | 383 +- gnu/lib/libg++/libg++/Integer.h | 1103 ++ gnu/lib/libg++/libg++/LogNorm.cc | 28 +- gnu/lib/libg++/libg++/LogNorm.h | 78 + gnu/lib/libg++/libg++/MLCG.cc | 18 +- gnu/lib/libg++/libg++/MLCG.h | 87 + gnu/lib/libg++/libg++/Makefile | 88 +- gnu/lib/libg++/libg++/Makefile.gnu | 463 - gnu/lib/libg++/libg++/NegExp.cc | 28 +- gnu/lib/libg++/libg++/NegExp.h | 55 + gnu/lib/libg++/libg++/Normal.cc | 28 +- gnu/lib/libg++/libg++/Normal.h | 66 + gnu/lib/libg++/libg++/Obstack.cc | 42 +- gnu/lib/libg++/libg++/Obstack.h | 216 + gnu/lib/libg++/libg++/Pix.h | 5 + gnu/lib/libg++/libg++/PlotFile.cc | 183 - gnu/lib/libg++/libg++/Poisson.cc | 28 +- gnu/lib/libg++/libg++/Poisson.h | 51 + gnu/lib/libg++/libg++/RNG.cc | 42 + gnu/lib/libg++/libg++/RNG.h | 57 + gnu/lib/libg++/libg++/Random.h | 54 + gnu/lib/libg++/libg++/Rational.cc | 102 +- gnu/lib/libg++/libg++/Rational.h | 283 + gnu/lib/libg++/libg++/Regex.cc | 45 +- gnu/lib/libg++/libg++/Regex.h | 76 + gnu/lib/libg++/libg++/RndInt.h | 175 + gnu/lib/libg++/libg++/SFile.cc | 53 - gnu/lib/libg++/libg++/SLList.cc | 247 + gnu/lib/libg++/libg++/SLList.h | 115 + gnu/lib/libg++/libg++/Sample.cc | 241 - gnu/lib/libg++/libg++/SmplHist.cc | 39 +- gnu/lib/libg++/libg++/SmplHist.h | 72 + gnu/lib/libg++/libg++/SmplStat.cc | 50 +- gnu/lib/libg++/libg++/SmplStat.h | 66 + gnu/lib/libg++/libg++/String.cc | 97 +- gnu/lib/libg++/libg++/String.h | 1316 ++ gnu/lib/libg++/libg++/Uniform.cc | 28 +- gnu/lib/libg++/libg++/Uniform.h | 71 + gnu/lib/libg++/libg++/Weibull.cc | 28 +- gnu/lib/libg++/libg++/Weibull.h | 74 + gnu/lib/libg++/libg++/_G_config.h | 50 + gnu/lib/libg++/libg++/bool.h | 13 + gnu/lib/libg++/libg++/builtin.h | 159 + gnu/lib/libg++/libg++/chr.cc | 27 +- gnu/lib/libg++/libg++/compare.h | 91 + gnu/lib/libg++/libg++/complex.h | 6 + gnu/lib/libg++/libg++/ctype.cc | 4 - gnu/lib/libg++/libg++/curses.cc | 4 - gnu/lib/libg++/libg++/delete.cc | 38 - gnu/lib/libg++/libg++/dtoa.cc | 35 +- gnu/lib/libg++/libg++/error.cc | 34 +- gnu/lib/libg++/libg++/filebuf.cc | 192 - gnu/lib/libg++/libg++/fmtq.cc | 27 +- gnu/lib/libg++/libg++/form.cc | 56 - gnu/lib/libg++/libg++/g++.order | 70 - gnu/lib/libg++/libg++/gcd.cc | 27 +- gnu/lib/libg++/libg++/generic.h | 54 + gnu/lib/libg++/libg++/getpagesize.h | 27 + gnu/lib/libg++/libg++/gnulib3.c | 109 - gnu/lib/libg++/libg++/hash.cc | 27 +- gnu/lib/libg++/libg++/istream.cc | 507 - gnu/lib/libg++/libg++/itoa.cc | 226 - gnu/lib/libg++/libg++/lg.cc | 27 +- gnu/lib/libg++/libg++/libc.h | 1 + gnu/lib/libg++/libg++/malloc.c | 1261 -- gnu/lib/libg++/libg++/math-68881.h | 516 + gnu/lib/libg++/libg++/max.cc | 4 - gnu/lib/libg++/libg++/min.cc | 4 - gnu/lib/libg++/libg++/minmax.h | 65 + gnu/lib/libg++/libg++/new.cc | 36 +- gnu/lib/libg++/libg++/new.h | 33 + gnu/lib/libg++/libg++/open.cc | 46 - gnu/lib/libg++/libg++/osfcn.h | 16 + gnu/lib/libg++/libg++/ostream.cc | 224 - gnu/lib/libg++/libg++/pow.cc | 27 +- gnu/lib/libg++/libg++/regex.cc | 2353 ++-- gnu/lib/libg++/libg++/regex.h | 272 + gnu/lib/libg++/libg++/sqrt.cc | 27 +- gnu/lib/libg++/libg++/std.cc | 4 - gnu/lib/libg++/libg++/std.h | 37 + gnu/lib/libg++/libg++/str.cc | 27 +- gnu/lib/libg++/libg++/strclass.h | 5 + gnu/lib/libg++/libg++/streambuf.cc | 138 - gnu/lib/libg++/{g++-include => libg++}/swap.h | 0 gnu/lib/libg++/libg++/timer.cc | 52 +- gnu/lib/libg++/libg++/typemacros.h | 8 + gnu/lib/libg++/libg++/xyzzy.cc | 12 - gnu/lib/libg++/libiberty/config.h | 1 + gnu/lib/libg++/libiberty/insque.c | 73 + gnu/lib/libg++/libiberty/strerror.c | 816 ++ gnu/lib/libg++/libiberty/strsignal.c | 623 + gnu/lib/libmalloc/COPYING.LIB | 481 + gnu/lib/libmalloc/ChangeLog | 291 + gnu/lib/libmalloc/Makefile | 11 + gnu/lib/libmalloc/Makefile.gnu | 57 + gnu/lib/libmalloc/OChangeLog | 34 + gnu/lib/libmalloc/README | 12 + gnu/lib/libmalloc/VERSION | 2 + gnu/lib/libmalloc/calloc.c | 39 + gnu/lib/libmalloc/cfree.c | 43 + gnu/lib/libmalloc/getpagesize.h | 38 + gnu/lib/libmalloc/gmalloc-head.c | 6 + gnu/lib/libmalloc/malloc.c | 622 + gnu/lib/libmalloc/malloc.h | 261 + gnu/lib/libmalloc/mcheck.c | 133 + gnu/lib/libmalloc/mem-limits.h | 132 + gnu/lib/libmalloc/memalign.c | 61 + gnu/lib/libmalloc/morecore.c | 44 + gnu/lib/libmalloc/mstats.c | 39 + gnu/lib/libmalloc/mtrace.awk | 36 + gnu/lib/libmalloc/mtrace.c | 150 + gnu/lib/libmalloc/ralloc.c | 514 + gnu/lib/libmalloc/valloc.c | 44 + gnu/lib/libmalloc/vm-limit.c | 134 + gnu/lib/libregex/AUTHORS | 10 + gnu/{usr.bin/diff3 => lib/libregex}/COPYING | 0 gnu/lib/libregex/ChangeLog | 3030 +++++ gnu/lib/libregex/INSTALL | 117 + gnu/lib/libregex/Makefile | 12 + gnu/lib/libregex/Makefile.gnu | 99 + gnu/lib/libregex/Makefile.in | 98 + gnu/lib/libregex/NEWS | 62 + gnu/lib/libregex/README | 60 + gnu/lib/libregex/VERSION | 3 + gnu/lib/libregex/config.status | 59 + gnu/lib/libregex/configure | 462 + gnu/lib/libregex/configure.in | 23 + gnu/lib/libregex/doc/Makefile | 93 + gnu/lib/libregex/doc/Makefile.in | 92 + gnu/lib/libregex/doc/include.awk | 19 + gnu/lib/libregex/doc/regex.aux | 136 + gnu/lib/libregex/doc/regex.cps | 152 + gnu/lib/libregex/doc/regex.info | 2836 +++++ gnu/lib/libregex/doc/regex.texi | 3138 +++++ gnu/lib/libregex/doc/texinfo.tex | 3941 ++++++ gnu/lib/libregex/doc/xregex.texi | 3021 +++++ {bin/ed => gnu/lib/libregex}/regex.c | 0 {bin/ed => gnu/lib/libregex}/regex.h | 0 gnu/lib/libregex/test/ChangeLog | 77 + gnu/lib/libregex/test/Makefile | 169 + gnu/lib/libregex/test/Makefile.in | 168 + gnu/lib/libregex/test/TAGS | 373 + gnu/lib/libregex/test/alloca.c | 194 + gnu/lib/libregex/test/bsd-interf.c | 38 + gnu/lib/libregex/test/debugmalloc.c | 273 + gnu/lib/libregex/test/emacsmalloc.c | 844 ++ gnu/lib/libregex/test/fileregex.c | 77 + gnu/lib/libregex/test/g++malloc.c | 1288 ++ gnu/lib/libregex/test/getpagesize.h | 25 + gnu/lib/libregex/test/iregex.c | 164 + gnu/lib/libregex/test/main.c | 49 + gnu/lib/libregex/test/malloc-test.c | 47 + gnu/lib/libregex/test/other.c | 503 + gnu/lib/libregex/test/printchar.c | 14 + gnu/lib/libregex/test/psx-basic.c | 253 + gnu/lib/libregex/test/psx-extend.c | 1244 ++ gnu/lib/libregex/test/psx-generic.c | 336 + gnu/lib/libregex/test/psx-group.c | 440 + gnu/lib/libregex/test/psx-interf.c | 624 + gnu/lib/libregex/test/psx-interv.c | 140 + gnu/lib/libregex/test/regexcpp.sed | 8 + gnu/lib/libregex/test/syntax.skel | 74 + gnu/lib/libregex/test/test.c | 782 ++ gnu/lib/libregex/test/test.h | 141 + gnu/lib/libregex/test/tregress.c | 464 + gnu/lib/libregex/test/upcase.c | 39 + gnu/lib/libregex/test/xmalloc.c | 21 + gnu/libexec/uucp/COPYING | 339 + gnu/libexec/uucp/ChangeLog | 3152 +++++ gnu/libexec/uucp/Makefile | 9 + gnu/libexec/uucp/Makefile.inc | 31 + gnu/libexec/uucp/README | 207 + gnu/libexec/uucp/TODO | 573 + gnu/libexec/uucp/VERSION | 4 + gnu/libexec/uucp/common_sources/chat.c | 1429 +++ gnu/libexec/uucp/common_sources/conf.h | 444 + gnu/libexec/uucp/common_sources/conn.c | 552 + gnu/libexec/uucp/common_sources/conn.h | 312 + gnu/libexec/uucp/common_sources/copy.c | 202 + gnu/libexec/uucp/common_sources/cu.h | 80 + gnu/libexec/uucp/common_sources/getopt.h | 120 + gnu/libexec/uucp/common_sources/log.c | 699 ++ gnu/libexec/uucp/common_sources/policy.h | 521 + gnu/libexec/uucp/common_sources/prot.c | 237 + gnu/libexec/uucp/common_sources/prot.h | 250 + gnu/libexec/uucp/common_sources/sysdep.h | 530 + gnu/libexec/uucp/common_sources/system.h | 950 ++ gnu/libexec/uucp/common_sources/tcp.c | 470 + gnu/libexec/uucp/common_sources/tli.c | 644 + gnu/libexec/uucp/common_sources/trans.h | 268 + gnu/libexec/uucp/common_sources/util.c | 144 + gnu/libexec/uucp/common_sources/uuconf.h | 1496 +++ gnu/libexec/uucp/common_sources/uucp.h | 367 + gnu/libexec/uucp/common_sources/uudefs.h | 445 + gnu/libexec/uucp/contrib/Dial.Hayes | 108 + gnu/libexec/uucp/contrib/Hangup.Hayes | 57 + gnu/libexec/uucp/contrib/Login.LAT | 137 + gnu/libexec/uucp/contrib/Login.PortSel | 133 + gnu/libexec/uucp/contrib/Login.VMS | 96 + gnu/libexec/uucp/contrib/Makefile.uurt | 30 + gnu/libexec/uucp/contrib/Makefile.xchat | 31 + gnu/libexec/uucp/contrib/README | 46 + gnu/libexec/uucp/contrib/README-UURATE | 20 + gnu/libexec/uucp/contrib/README-XCHAT | 42 + gnu/libexec/uucp/contrib/savelog.man | 130 + gnu/libexec/uucp/contrib/savelog.sh | 247 + gnu/libexec/uucp/contrib/stats.sh | 27 + gnu/libexec/uucp/contrib/tstout.c | 158 + gnu/libexec/uucp/contrib/uuclean | 23 + gnu/libexec/uucp/contrib/uuq.sh | 125 + gnu/libexec/uucp/contrib/uurate.c | 657 + gnu/libexec/uucp/contrib/uurate.man | 217 + gnu/libexec/uucp/contrib/uureroute | 91 + gnu/libexec/uucp/contrib/uusnap.c | 321 + gnu/libexec/uucp/contrib/uutraf | 203 + gnu/libexec/uucp/contrib/uutry | 43 + gnu/libexec/uucp/contrib/xc-conf.h-dist | 38 + gnu/libexec/uucp/contrib/xchat.c | 1444 +++ gnu/libexec/uucp/contrib/xchat.man | 614 + gnu/libexec/uucp/cu/Makefile | 16 + gnu/libexec/uucp/cu/cu.1 | 286 + gnu/libexec/uucp/cu/cu.c | 2068 ++++ gnu/libexec/uucp/libunix/MANIFEST | 76 + gnu/libexec/uucp/libunix/Makefile | 22 + gnu/libexec/uucp/libunix/access.c | 83 + gnu/libexec/uucp/libunix/addbas.c | 50 + gnu/libexec/uucp/libunix/app3.c | 29 + gnu/libexec/uucp/libunix/app4.c | 33 + gnu/libexec/uucp/libunix/basnam.c | 22 + gnu/libexec/uucp/libunix/bytfre.c | 19 + gnu/libexec/uucp/libunix/chmod.c | 25 + gnu/libexec/uucp/libunix/cohtty.c | 244 + gnu/libexec/uucp/libunix/cusub.c | 1163 ++ gnu/libexec/uucp/libunix/cwd.c | 55 + gnu/libexec/uucp/libunix/detach.c | 186 + gnu/libexec/uucp/libunix/dirent.c | 123 + gnu/libexec/uucp/libunix/dup2.c | 69 + gnu/libexec/uucp/libunix/efopen.c | 132 + gnu/libexec/uucp/libunix/epopen.c | 85 + gnu/libexec/uucp/libunix/exists.c | 16 + gnu/libexec/uucp/libunix/filnam.c | 376 + gnu/libexec/uucp/libunix/fsusg.c | 231 + gnu/libexec/uucp/libunix/fsusg.h | 31 + gnu/libexec/uucp/libunix/ftw.c | 250 + gnu/libexec/uucp/libunix/getcwd.c | 59 + gnu/libexec/uucp/libunix/indir.c | 133 + gnu/libexec/uucp/libunix/init.c | 394 + gnu/libexec/uucp/libunix/isdir.c | 18 + gnu/libexec/uucp/libunix/isfork.c | 25 + gnu/libexec/uucp/libunix/iswait.c | 159 + gnu/libexec/uucp/libunix/jobid.c | 101 + gnu/libexec/uucp/libunix/lcksys.c | 41 + gnu/libexec/uucp/libunix/link.c | 38 + gnu/libexec/uucp/libunix/locfil.c | 95 + gnu/libexec/uucp/libunix/lock.c | 477 + gnu/libexec/uucp/libunix/loctim.c | 25 + gnu/libexec/uucp/libunix/mail.c | 85 + gnu/libexec/uucp/libunix/mkdir.c | 58 + gnu/libexec/uucp/libunix/mkdirs.c | 49 + gnu/libexec/uucp/libunix/mode.c | 33 + gnu/libexec/uucp/libunix/move.c | 176 + gnu/libexec/uucp/libunix/opensr.c | 244 + gnu/libexec/uucp/libunix/pause.c | 96 + gnu/libexec/uucp/libunix/picksb.c | 230 + gnu/libexec/uucp/libunix/portnm.c | 51 + gnu/libexec/uucp/libunix/proctm.c | 197 + gnu/libexec/uucp/libunix/recep.c | 197 + gnu/libexec/uucp/libunix/remove.c | 13 + gnu/libexec/uucp/libunix/rename.c | 27 + gnu/libexec/uucp/libunix/rmdir.c | 43 + gnu/libexec/uucp/libunix/run.c | 75 + gnu/libexec/uucp/libunix/seq.c | 126 + gnu/libexec/uucp/libunix/serial.c | 2977 +++++ gnu/libexec/uucp/libunix/signal.c | 208 + gnu/libexec/uucp/libunix/sindir.c | 26 + gnu/libexec/uucp/libunix/size.c | 27 + gnu/libexec/uucp/libunix/sleep.c | 14 + gnu/libexec/uucp/libunix/spawn.c | 398 + gnu/libexec/uucp/libunix/splcmd.c | 115 + gnu/libexec/uucp/libunix/splnam.c | 19 + gnu/libexec/uucp/libunix/spool.c | 420 + gnu/libexec/uucp/libunix/srmdir.c | 112 + gnu/libexec/uucp/libunix/statsb.c | 572 + gnu/libexec/uucp/libunix/status.c | 212 + gnu/libexec/uucp/libunix/strerr.c | 22 + gnu/libexec/uucp/libunix/time.c | 32 + gnu/libexec/uucp/libunix/tmpfil.c | 83 + gnu/libexec/uucp/libunix/trunc.c | 157 + gnu/libexec/uucp/libunix/uacces.c | 205 + gnu/libexec/uucp/libunix/ufopen.c | 218 + gnu/libexec/uucp/libunix/ultspl.c | 21 + gnu/libexec/uucp/libunix/unknwn.c | 43 + gnu/libexec/uucp/libunix/uuto.c | 31 + gnu/libexec/uucp/libunix/walk.c | 59 + gnu/libexec/uucp/libunix/wldcrd.c | 212 + gnu/libexec/uucp/libunix/work.c | 765 ++ gnu/libexec/uucp/libunix/xqtfil.c | 265 + gnu/libexec/uucp/libunix/xqtsub.c | 698 ++ gnu/libexec/uucp/libuuconf/COPYING.LIB | 481 + gnu/libexec/uucp/libuuconf/MANIFEST | 92 + gnu/libexec/uucp/libuuconf/Makefile | 26 + gnu/libexec/uucp/libuuconf/README | 113 + gnu/libexec/uucp/libuuconf/addblk.c | 56 + gnu/libexec/uucp/libuuconf/addstr.c | 139 + gnu/libexec/uucp/libuuconf/allblk.c | 51 + gnu/libexec/uucp/libuuconf/alloc.c | 82 + gnu/libexec/uucp/libuuconf/alloc.h | 71 + gnu/libexec/uucp/libuuconf/base.c | 54 + gnu/libexec/uucp/libuuconf/bool.c | 64 + gnu/libexec/uucp/libuuconf/callin.c | 142 + gnu/libexec/uucp/libuuconf/calout.c | 93 + gnu/libexec/uucp/libuuconf/chatc.c | 202 + gnu/libexec/uucp/libuuconf/cmdarg.c | 185 + gnu/libexec/uucp/libuuconf/cmdfil.c | 103 + gnu/libexec/uucp/libuuconf/cmdlin.c | 142 + gnu/libexec/uucp/libuuconf/debfil.c | 43 + gnu/libexec/uucp/libuuconf/deblev.c | 43 + gnu/libexec/uucp/libuuconf/diacod.c | 129 + gnu/libexec/uucp/libuuconf/dial.c | 61 + gnu/libexec/uucp/libuuconf/diasub.c | 63 + gnu/libexec/uucp/libuuconf/dnams.c | 103 + gnu/libexec/uucp/libuuconf/errno.c | 46 + gnu/libexec/uucp/libuuconf/errstr.c | 241 + gnu/libexec/uucp/libuuconf/filnam.c | 44 + gnu/libexec/uucp/libuuconf/freblk.c | 63 + gnu/libexec/uucp/libuuconf/fredia.c | 44 + gnu/libexec/uucp/libuuconf/free.c | 68 + gnu/libexec/uucp/libuuconf/freprt.c | 44 + gnu/libexec/uucp/libuuconf/fresys.c | 44 + gnu/libexec/uucp/libuuconf/grdcmp.c | 76 + gnu/libexec/uucp/libuuconf/hdial.c | 187 + gnu/libexec/uucp/libuuconf/hdnams.c | 109 + gnu/libexec/uucp/libuuconf/hinit.c | 295 + gnu/libexec/uucp/libuuconf/hlocnm.c | 84 + gnu/libexec/uucp/libuuconf/hport.c | 368 + gnu/libexec/uucp/libuuconf/hrmunk.c | 55 + gnu/libexec/uucp/libuuconf/hsinfo.c | 625 + gnu/libexec/uucp/libuuconf/hsnams.c | 142 + gnu/libexec/uucp/libuuconf/hsys.c | 49 + gnu/libexec/uucp/libuuconf/hunk.c | 142 + gnu/libexec/uucp/libuuconf/iniglb.c | 177 + gnu/libexec/uucp/libuuconf/init.c | 74 + gnu/libexec/uucp/libuuconf/int.c | 59 + gnu/libexec/uucp/libuuconf/lckdir.c | 43 + gnu/libexec/uucp/libuuconf/lineno.c | 44 + gnu/libexec/uucp/libuuconf/llocnm.c | 70 + gnu/libexec/uucp/libuuconf/local.c | 70 + gnu/libexec/uucp/libuuconf/locnm.c | 46 + gnu/libexec/uucp/libuuconf/logfil.c | 43 + gnu/libexec/uucp/libuuconf/maxuxq.c | 86 + gnu/libexec/uucp/libuuconf/mrgblk.c | 50 + gnu/libexec/uucp/libuuconf/paramc.c | 175 + gnu/libexec/uucp/libuuconf/port.c | 77 + gnu/libexec/uucp/libuuconf/prtsub.c | 54 + gnu/libexec/uucp/libuuconf/pubdir.c | 43 + gnu/libexec/uucp/libuuconf/rdlocs.c | 305 + gnu/libexec/uucp/libuuconf/rdperm.c | 446 + gnu/libexec/uucp/libuuconf/reliab.c | 123 + gnu/libexec/uucp/libuuconf/remunk.c | 45 + gnu/libexec/uucp/libuuconf/sinfo.c | 112 + gnu/libexec/uucp/libuuconf/snams.c | 133 + gnu/libexec/uucp/libuuconf/split.c | 106 + gnu/libexec/uucp/libuuconf/spool.c | 43 + gnu/libexec/uucp/libuuconf/stafil.c | 43 + gnu/libexec/uucp/libuuconf/syshdr.h | 106 + gnu/libexec/uucp/libuuconf/syssub.c | 458 + gnu/libexec/uucp/libuuconf/tcalou.c | 201 + gnu/libexec/uucp/libuuconf/tdial.c | 227 + gnu/libexec/uucp/libuuconf/tdialc.c | 211 + gnu/libexec/uucp/libuuconf/tdnams.c | 119 + gnu/libexec/uucp/libuuconf/tgcmp.c | 42 + gnu/libexec/uucp/libuuconf/thread.c | 70 + gnu/libexec/uucp/libuuconf/time.c | 406 + gnu/libexec/uucp/libuuconf/tinit.c | 370 + gnu/libexec/uucp/libuuconf/tlocnm.c | 112 + gnu/libexec/uucp/libuuconf/tport.c | 295 + gnu/libexec/uucp/libuuconf/tportc.c | 465 + gnu/libexec/uucp/libuuconf/tsinfo.c | 922 ++ gnu/libexec/uucp/libuuconf/tsnams.c | 84 + gnu/libexec/uucp/libuuconf/tsys.c | 49 + gnu/libexec/uucp/libuuconf/tval.c | 71 + gnu/libexec/uucp/libuuconf/ugtlin.c | 110 + gnu/libexec/uucp/libuuconf/unk.c | 70 + gnu/libexec/uucp/libuuconf/uucnfi.h | 368 + gnu/libexec/uucp/libuuconf/val.c | 46 + gnu/libexec/uucp/libuuconf/vinit.c | 112 + gnu/libexec/uucp/libuuconf/vport.c | 251 + gnu/libexec/uucp/libuuconf/vsinfo.c | 575 + gnu/libexec/uucp/libuuconf/vsnams.c | 106 + gnu/libexec/uucp/libuuconf/vsys.c | 49 + gnu/libexec/uucp/libuucp/MANIFEST | 27 + gnu/libexec/uucp/libuucp/Makefile | 14 + gnu/libexec/uucp/libuucp/bsrch.c | 54 + gnu/libexec/uucp/libuucp/buffer.c | 109 + gnu/libexec/uucp/libuucp/bzero.c | 15 + gnu/libexec/uucp/libuucp/crc.c | 112 + gnu/libexec/uucp/libuucp/debug.c | 165 + gnu/libexec/uucp/libuucp/escape.c | 98 + gnu/libexec/uucp/libuucp/getlin.c | 81 + gnu/libexec/uucp/libuucp/getop1.c | 144 + gnu/libexec/uucp/libuucp/getopt.c | 621 + gnu/libexec/uucp/libuucp/memchr.c | 149 + gnu/libexec/uucp/libuucp/memcmp.c | 19 + gnu/libexec/uucp/libuucp/memcpy.c | 18 + gnu/libexec/uucp/libuucp/parse.c | 207 + gnu/libexec/uucp/libuucp/spool.c | 30 + gnu/libexec/uucp/libuucp/status.c | 20 + gnu/libexec/uucp/libuucp/strcas.c | 33 + gnu/libexec/uucp/libuucp/strchr.c | 16 + gnu/libexec/uucp/libuucp/strdup.c | 18 + gnu/libexec/uucp/libuucp/strncs.c | 39 + gnu/libexec/uucp/libuucp/strrch.c | 24 + gnu/libexec/uucp/libuucp/strstr.c | 55 + gnu/libexec/uucp/libuucp/strtol.c | 175 + gnu/libexec/uucp/libuucp/xfree.c | 15 + gnu/libexec/uucp/libuucp/xmall.c | 18 + gnu/libexec/uucp/libuucp/xreall.c | 23 + gnu/libexec/uucp/sample/Makefile | 15 + gnu/libexec/uucp/sample/call | 20 + gnu/libexec/uucp/sample/config | 88 + gnu/libexec/uucp/sample/dial | 35 + gnu/libexec/uucp/sample/dialcode | 19 + gnu/libexec/uucp/sample/passwd | 18 + gnu/libexec/uucp/sample/port | 41 + gnu/libexec/uucp/sample/sys1 | 44 + gnu/libexec/uucp/sample/sys2 | 51 + gnu/libexec/uucp/tstuu.c | 1588 +++ gnu/libexec/uucp/uuchk/Makefile | 16 + gnu/libexec/uucp/uuchk/uuchk.c | 856 ++ gnu/libexec/uucp/uucico/Makefile | 20 + gnu/libexec/uucp/uucico/prote.c | 387 + gnu/libexec/uucp/uucico/protf.c | 842 ++ gnu/libexec/uucp/uucico/protg.c | 1933 +++ gnu/libexec/uucp/uucico/proti.c | 1563 +++ gnu/libexec/uucp/uucico/protj.c | 671 ++ gnu/libexec/uucp/uucico/prott.c | 330 + gnu/libexec/uucp/uucico/protz.c | 2626 ++++ gnu/libexec/uucp/uucico/rec.c | 1162 ++ gnu/libexec/uucp/uucico/send.c | 1273 ++ gnu/libexec/uucp/uucico/time.c | 130 + gnu/libexec/uucp/uucico/trans.c | 1439 +++ gnu/libexec/uucp/uucico/uucico.8 | 225 + gnu/libexec/uucp/uucico/uucico.c | 2618 ++++ gnu/libexec/uucp/uucico/xcmd.c | 396 + gnu/libexec/uucp/uuconv/Makefile | 17 + gnu/libexec/uucp/uuconv/uuconv.c | 2012 ++++ gnu/libexec/uucp/uucp/Makefile | 16 + gnu/libexec/uucp/uucp/uucp.1 | 175 + gnu/libexec/uucp/uucp/uucp.c | 1181 ++ gnu/libexec/uucp/uulog/Makefile | 16 + gnu/libexec/uucp/uulog/uulog.c | 444 + gnu/libexec/uucp/uuname/Makefile | 18 + gnu/libexec/uucp/uuname/uuname.c | 192 + gnu/libexec/uucp/uupick/Makefile | 16 + gnu/libexec/uucp/uupick/uupick.c | 323 + gnu/libexec/uucp/uusched/Makefile | 18 + gnu/libexec/uucp/uusched/uusched.in | 13 + gnu/libexec/uucp/uustat/Makefile | 17 + gnu/libexec/uucp/uustat/uustat.1 | 380 + gnu/libexec/uucp/uustat/uustat.c | 2241 ++++ gnu/libexec/uucp/uuto/Makefile | 17 + gnu/libexec/uucp/uuto/uuto.in | 16 + gnu/libexec/uucp/uux/Makefile | 16 + gnu/libexec/uucp/uux/uux.1 | 234 + gnu/libexec/uucp/uux/uux.c | 1502 +++ gnu/libexec/uucp/uuxqt/Makefile | 18 + gnu/libexec/uucp/uuxqt/uuxqt.8 | 92 + gnu/libexec/uucp/uuxqt/uuxqt.c | 1549 +++ {bin/test => gnu/usr.bin/as}/COPYING | 0 gnu/usr.bin/as/ChangeLog | 917 ++ gnu/usr.bin/as/Makefile | 15 + gnu/usr.bin/as/Makefile.gnu | 356 + gnu/usr.bin/as/NOTES | 35 + gnu/usr.bin/as/README.gnu | 133 + gnu/usr.bin/as/app.c | 392 + gnu/usr.bin/as/append.c | 37 + gnu/usr.bin/as/as.1 | 271 + gnu/usr.bin/as/as.c | 324 + gnu/usr.bin/as/as.h | 292 + gnu/usr.bin/as/atof-generic.c | 526 + gnu/usr.bin/as/bignum-copy.c | 75 + gnu/usr.bin/as/bignum.h | 48 + gnu/usr.bin/as/config/Makefile.i386 | 4 + gnu/usr.bin/as/config/a.out.gnu.h | 261 + gnu/usr.bin/as/config/atof-ieee.c | 505 + gnu/usr.bin/as/config/i386-opcode.h | 806 ++ gnu/usr.bin/as/config/i386.c | 1946 +++ gnu/usr.bin/as/config/i386.h | 296 + gnu/usr.bin/as/expr.c | 980 ++ gnu/usr.bin/as/expr.h | 69 + gnu/usr.bin/as/flonum-const.c | 157 + gnu/usr.bin/as/flonum-copy.c | 76 + gnu/usr.bin/as/flonum-mult.c | 200 + gnu/usr.bin/as/flonum.h | 111 + gnu/usr.bin/as/frags.c | 292 + gnu/usr.bin/as/frags.h | 41 + gnu/usr.bin/as/hash.c | 981 ++ gnu/usr.bin/as/hash.h | 59 + gnu/usr.bin/as/hex-value.c | 55 + gnu/usr.bin/as/input-file.c | 306 + gnu/usr.bin/as/input-file.h | 57 + gnu/usr.bin/as/input-scrub.c | 427 + gnu/usr.bin/as/md.h | 57 + gnu/usr.bin/as/messages.c | 238 + gnu/usr.bin/as/objrecdef.h | 255 + gnu/usr.bin/as/obstack.c | 337 + gnu/usr.bin/as/obstack.h | 418 + gnu/usr.bin/as/output-file.c | 81 + gnu/usr.bin/as/read.c | 2188 ++++ gnu/usr.bin/as/read.h | 47 + gnu/usr.bin/as/struc-symbol.h | 72 + gnu/usr.bin/as/subsegs.c | 292 + gnu/usr.bin/as/subsegs.h | 65 + gnu/usr.bin/as/symbols.c | 438 + gnu/usr.bin/as/symbols.h | 42 + gnu/usr.bin/as/version.c | 23 + gnu/usr.bin/as/write.c | 1259 ++ gnu/usr.bin/as/write.h | 77 + gnu/usr.bin/as/xmalloc.c | 60 + gnu/usr.bin/as/xrealloc.c | 61 + gnu/usr.bin/awk/Makefile | 2 +- gnu/usr.bin/bc/Makefile | 3 +- gnu/usr.bin/bc/bc.1 | 2 +- gnu/usr.bin/cc/Makefile | 2 + gnu/usr.bin/cc/Makefile.inc | 12 - gnu/usr.bin/cc/TODO | 3 + gnu/usr.bin/cc/cc/Makefile | 13 +- gnu/usr.bin/cc/cc/g++.script | 111 + gnu/usr.bin/cc/cc/gcc.c | 42 +- gnu/usr.bin/cc/cc1/Makefile | 20 +- gnu/usr.bin/cc/cc1/c-decl.c | 13 +- gnu/usr.bin/cc/cc1/c-iterate.c | 21 +- gnu/usr.bin/cc/cc1/c-typeck.c | 59 +- gnu/usr.bin/cc/cc1obj/Makefile | 28 +- gnu/usr.bin/cc/cc1obj/objc-act.c | 7 +- gnu/usr.bin/cc/cc1plus/Makefile | 18 +- gnu/usr.bin/cc/cc1plus/cp-decl.c | 20 +- gnu/usr.bin/cc/cc1plus/cp-search.c | 78 +- gnu/usr.bin/cc/cc1plus/cp-typeck.c | 1 + gnu/usr.bin/cc/common/aux-output.c | 1901 --- gnu/usr.bin/cc/common/c-common.c | 1219 -- gnu/usr.bin/cc/common/calls.c | 2880 ----- gnu/usr.bin/cc/common/combine.c | 10025 ---------------- gnu/usr.bin/cc/common/cse.c | 8234 ------------- gnu/usr.bin/cc/common/dbxout.c | 2437 ---- gnu/usr.bin/cc/common/dwarfout.c | 5627 --------- gnu/usr.bin/cc/common/emit-rtl.c | 3105 ----- gnu/usr.bin/cc/common/expmed.c | 3159 ----- gnu/usr.bin/cc/common/expr.c | 7994 ------------ gnu/usr.bin/cc/common/final.c | 2740 ----- gnu/usr.bin/cc/common/function.c | 4913 -------- gnu/usr.bin/cc/common/gstddef.h | 217 - gnu/usr.bin/cc/common/insn-emit.c | 2698 ----- gnu/usr.bin/cc/common/insn-extract.c | 500 - gnu/usr.bin/cc/common/insn-flags.h | 510 - gnu/usr.bin/cc/common/insn-opinit.c | 178 - gnu/usr.bin/cc/common/insn-output.c | 5893 --------- gnu/usr.bin/cc/common/insn-recog.c | 6055 ---------- gnu/usr.bin/cc/common/integrate.c | 2898 ----- gnu/usr.bin/cc/common/loop.c | 6496 ---------- gnu/usr.bin/cc/common/real.c | 4978 -------- gnu/usr.bin/cc/common/real.h | 359 - gnu/usr.bin/cc/common/recog.c | 1960 --- gnu/usr.bin/cc/common/reg-stack.c | 2873 ----- gnu/usr.bin/cc/common/reload.c | 5425 --------- gnu/usr.bin/cc/common/reload1.c | 6766 ----------- gnu/usr.bin/cc/common/sched.c | 4674 ------- gnu/usr.bin/cc/common/stmt.c | 4743 -------- gnu/usr.bin/cc/common/toplev.c | 3505 ------ gnu/usr.bin/cc/common/unroll.c | 3223 ----- gnu/usr.bin/cc/common/varasm.c | 3029 ----- gnu/usr.bin/cc/common/version.c | 1 - gnu/usr.bin/cc/cpp/Makefile | 21 +- gnu/usr.bin/cc/cpp/cccp.c | 15 - gnu/usr.bin/cc/cpp/{cpp.1 => gcpp.1} | 0 gnu/usr.bin/cc/lib/Makefile | 20 + gnu/usr.bin/cc/lib/aux-output.c | 1921 +++ gnu/usr.bin/cc/{common => lib}/basic-block.h | 0 gnu/usr.bin/cc/lib/c-common.c | 1222 ++ gnu/usr.bin/cc/{common => lib}/c-lex.h | 0 gnu/usr.bin/cc/{common => lib}/c-parse.h | 0 gnu/usr.bin/cc/{common => lib}/c-tree.h | 0 gnu/usr.bin/cc/{common => lib}/caller-save.c | 0 gnu/usr.bin/cc/lib/calls.c | 2891 +++++ gnu/usr.bin/cc/lib/combine.c | 10025 ++++++++++++++++ gnu/usr.bin/cc/{common => lib}/conditions.h | 0 gnu/usr.bin/cc/{common => lib}/config.h | 0 gnu/usr.bin/cc/{common => lib}/convert.c | 0 gnu/usr.bin/cc/{common => lib}/convert.h | 0 gnu/usr.bin/cc/lib/cse.c | 8243 +++++++++++++ gnu/usr.bin/cc/lib/dbxout.c | 2439 ++++ gnu/usr.bin/cc/{common => lib}/defaults.h | 0 gnu/usr.bin/cc/lib/dwarfout.c | 5647 +++++++++ gnu/usr.bin/cc/lib/emit-rtl.c | 3137 +++++ gnu/usr.bin/cc/{common => lib}/explow.c | 0 gnu/usr.bin/cc/lib/expmed.c | 3160 +++++ gnu/usr.bin/cc/lib/expr.c | 7994 ++++++++++++ gnu/usr.bin/cc/{common => lib}/expr.h | 0 gnu/usr.bin/cc/lib/final.c | 2740 +++++ gnu/usr.bin/cc/{common => lib}/flags.h | 0 gnu/usr.bin/cc/{common => lib}/flow.c | 0 gnu/usr.bin/cc/{common => lib}/fold-const.c | 0 gnu/usr.bin/cc/lib/function.c | 4913 ++++++++ gnu/usr.bin/cc/{common => lib}/function.h | 0 gnu/usr.bin/cc/{common => lib}/gbl-ctors.h | 0 gnu/usr.bin/cc/{common => lib}/getpwd.c | 0 gnu/usr.bin/cc/{common => lib}/glimits.h | 0 gnu/usr.bin/cc/{common => lib}/global.c | 0 gnu/usr.bin/cc/lib/gstddef.h | 217 + gnu/usr.bin/cc/{common => lib}/gvarargs.h | 0 gnu/usr.bin/cc/{common => lib}/hard-reg-set.h | 0 gnu/usr.bin/cc/{common => lib}/i386/bsd.h | 0 gnu/usr.bin/cc/{common => lib}/i386/gas.h | 0 gnu/usr.bin/cc/{common => lib}/i386/gstabs.h | 0 gnu/usr.bin/cc/{common => lib}/i386/i386.h | 0 gnu/usr.bin/cc/{common => lib}/i386/perform.h | 0 gnu/usr.bin/cc/{common => lib}/i386/unix.h | 0 gnu/usr.bin/cc/{common => lib}/input.h | 0 gnu/usr.bin/cc/{common => lib}/insn-attr.h | 0 gnu/usr.bin/cc/{common => lib}/insn-attrtab.c | 0 gnu/usr.bin/cc/{common => lib}/insn-codes.h | 0 gnu/usr.bin/cc/{common => lib}/insn-config.h | 0 gnu/usr.bin/cc/lib/insn-emit.c | 2708 +++++ gnu/usr.bin/cc/lib/insn-extract.c | 505 + gnu/usr.bin/cc/lib/insn-flags.h | 522 + gnu/usr.bin/cc/lib/insn-opinit.c | 179 + gnu/usr.bin/cc/lib/insn-output.c | 5899 +++++++++ gnu/usr.bin/cc/{common => lib}/insn-peep.c | 0 gnu/usr.bin/cc/lib/insn-recog.c | 6158 ++++++++++ gnu/usr.bin/cc/lib/integrate.c | 2902 +++++ gnu/usr.bin/cc/{common => lib}/integrate.h | 0 gnu/usr.bin/cc/{common => lib}/jump.c | 0 gnu/usr.bin/cc/lib/lib.mk | 188 + gnu/usr.bin/cc/{common => lib}/local-alloc.c | 0 gnu/usr.bin/cc/{common => lib}/longlong.h | 0 gnu/usr.bin/cc/lib/loop.c | 6508 ++++++++++ gnu/usr.bin/cc/{common => lib}/loop.h | 0 gnu/usr.bin/cc/{common => lib}/machmode.def | 0 gnu/usr.bin/cc/{common => lib}/machmode.h | 0 gnu/usr.bin/cc/{common => lib}/obstack.c | 0 gnu/usr.bin/cc/{common => lib}/obstack.h | 0 gnu/usr.bin/cc/{common => lib}/optabs.c | 0 gnu/usr.bin/cc/{common => lib}/output.h | 0 gnu/usr.bin/cc/{common => lib}/print-rtl.c | 0 gnu/usr.bin/cc/{common => lib}/print-tree.c | 0 gnu/usr.bin/cc/lib/real.c | 5060 ++++++++ gnu/usr.bin/cc/lib/real.h | 363 + gnu/usr.bin/cc/lib/recog.c | 1961 +++ gnu/usr.bin/cc/{common => lib}/recog.h | 0 gnu/usr.bin/cc/lib/reg-stack.c | 2897 +++++ gnu/usr.bin/cc/{common => lib}/regclass.c | 0 gnu/usr.bin/cc/{common => lib}/regs.h | 0 gnu/usr.bin/cc/lib/reload.c | 5435 +++++++++ gnu/usr.bin/cc/{common => lib}/reload.h | 0 gnu/usr.bin/cc/lib/reload1.c | 6774 +++++++++++ gnu/usr.bin/cc/{common => lib}/reorg.c | 0 gnu/usr.bin/cc/{common => lib}/rtl.c | 0 gnu/usr.bin/cc/{common => lib}/rtl.def | 0 gnu/usr.bin/cc/{common => lib}/rtl.h | 0 gnu/usr.bin/cc/{common => lib}/rtlanal.c | 0 gnu/usr.bin/cc/lib/sched.c | 4675 +++++++ gnu/usr.bin/cc/{common => lib}/sdbout.c | 0 gnu/usr.bin/cc/lib/stmt.c | 4749 ++++++++ gnu/usr.bin/cc/{common => lib}/stor-layout.c | 0 gnu/usr.bin/cc/{common => lib}/stupid.c | 0 gnu/usr.bin/cc/{common => lib}/tconfig.h | 0 gnu/usr.bin/cc/{common => lib}/tm.h | 0 gnu/usr.bin/cc/lib/toplev.c | 3509 ++++++ gnu/usr.bin/cc/{common => lib}/tree.c | 0 gnu/usr.bin/cc/{common => lib}/tree.def | 0 gnu/usr.bin/cc/{common => lib}/tree.h | 0 gnu/usr.bin/cc/{common => lib}/typeclass.h | 0 gnu/usr.bin/cc/lib/unroll.c | 3251 +++++ gnu/usr.bin/cc/lib/varasm.c | 3033 +++++ gnu/usr.bin/cc/lib/version.c | 1 + gnu/usr.bin/cc/{common => lib}/xcoffout.c | 0 gnu/usr.bin/cc/libgcc/Makefile | 6 +- gnu/usr.bin/cc/libgcc/_eprintf.c | 19 +- gnu/usr.bin/cc/libgcc/_new_handler.c | 19 +- gnu/usr.bin/cc/libobjc/Makefile | 100 +- gnu/usr.bin/cc/libobjc/sendmsg.c | 3 +- gnu/usr.bin/cpio/COPYING | 339 + gnu/usr.bin/cpio/COPYING.LIB | 481 + gnu/usr.bin/cpio/ChangeLog | 781 ++ gnu/usr.bin/cpio/Makefile | 9 + gnu/usr.bin/cpio/NEWS | 55 + gnu/usr.bin/cpio/README | 56 + gnu/usr.bin/cpio/alloca.c | 475 + gnu/usr.bin/cpio/copyin.c | 1272 ++ gnu/usr.bin/cpio/copyout.c | 801 ++ gnu/usr.bin/cpio/copypass.c | 449 + gnu/usr.bin/cpio/cpio.1 | 310 + gnu/usr.bin/cpio/cpio.h | 69 + gnu/usr.bin/cpio/cpiohdr.h | 90 + gnu/usr.bin/cpio/defer.c | 43 + gnu/usr.bin/cpio/defer.h | 8 + gnu/usr.bin/cpio/dirname.c | 66 + gnu/usr.bin/cpio/dstring.c | 114 + gnu/usr.bin/cpio/dstring.h | 49 + gnu/usr.bin/cpio/error.c | 106 + gnu/usr.bin/cpio/extern.h | 176 + gnu/usr.bin/cpio/filemode.c | 229 + gnu/usr.bin/cpio/filetypes.h | 84 + gnu/usr.bin/cpio/fnmatch.c | 200 + gnu/usr.bin/cpio/fnmatch.h | 60 + gnu/usr.bin/cpio/getopt.c | 744 ++ gnu/usr.bin/{diff3 => cpio}/getopt.h | 0 gnu/usr.bin/{diff3 => cpio}/getopt1.c | 0 gnu/usr.bin/cpio/global.c | 168 + gnu/usr.bin/cpio/idcache.c | 206 + gnu/usr.bin/cpio/main.c | 479 + gnu/usr.bin/cpio/makepath.c | 297 + gnu/usr.bin/cpio/rmt.h | 98 + gnu/usr.bin/cpio/rtapelib.c | 582 + gnu/usr.bin/cpio/stripslash.c | 39 + gnu/usr.bin/cpio/system.h | 139 + gnu/usr.bin/cpio/tar.c | 522 + gnu/usr.bin/cpio/tar.h | 112 + gnu/usr.bin/cpio/tarhdr.h | 62 + gnu/usr.bin/cpio/tcexparg.c | 240 + gnu/usr.bin/cpio/userspec.c | 180 + gnu/usr.bin/cpio/util.c | 1102 ++ gnu/usr.bin/cpio/version.c | 2 + gnu/usr.bin/cpio/xmalloc.c | 65 + gnu/usr.bin/cpio/xstrdup.c | 32 + gnu/usr.bin/cvs/cvs/Makefile | 11 +- gnu/usr.bin/cvs/cvs/import.c | 33 + gnu/usr.bin/cvs/cvs/update.c | 31 + gnu/usr.bin/cvs/lib/Makefile | 5 + gnu/usr.bin/cvs/lib/Makefile.in | 91 - gnu/usr.bin/cvs/mkmodules/Makefile | 8 +- gnu/usr.bin/cvs/mkmodules/xxx | 5320 -------- gnu/usr.bin/dc/Makefile | 2 +- gnu/usr.bin/dc/dc.1 | 278 + gnu/usr.bin/diff/COPYING | 339 + gnu/usr.bin/{diff3 => diff}/diff3.c | 0 gnu/usr.bin/diff/fnmatch.h | 62 - gnu/usr.bin/diff/sdiff.c | 1067 ++ gnu/usr.bin/diff3/Makefile | 16 +- gnu/usr.bin/diff3/getopt.c | 731 -- gnu/usr.bin/diff3/system.h | 159 - gnu/usr.bin/diff3/version.c | 3 - gnu/usr.bin/gcc1/Makefile | 21 - gnu/usr.bin/gcc1/Makefile.symlinks | 24 - gnu/usr.bin/gcc1/cc/Makefile | 15 - gnu/usr.bin/gcc1/cc/cc.1 | 1455 --- gnu/usr.bin/gcc1/cc/cc.c | 2114 ---- gnu/usr.bin/gcc1/cc1/.dbxinit | 10 - gnu/usr.bin/gcc1/cc1/.gdbinit | 58 - gnu/usr.bin/gcc1/cc1/Makefile | 47 - gnu/usr.bin/gcc1/cc1/Makefile.gnu | 724 -- gnu/usr.bin/gcc1/cc1/PROBLEMS | 128 - gnu/usr.bin/gcc1/cc1/PROJECTS | 364 - gnu/usr.bin/gcc1/cc1/README | 15 - gnu/usr.bin/gcc1/cc1/assert.h | 32 - gnu/usr.bin/gcc1/cc1/basic-block.h | 62 - gnu/usr.bin/gcc1/cc1/c-convert.c | 397 - gnu/usr.bin/gcc1/cc1/c-decl.c | 4060 ------- gnu/usr.bin/gcc1/cc1/c-parse.gperf | 56 - gnu/usr.bin/gcc1/cc1/c-parse.h | 49 - gnu/usr.bin/gcc1/cc1/c-parse.y | 2884 ----- gnu/usr.bin/gcc1/cc1/c-tree.h | 154 - gnu/usr.bin/gcc1/cc1/c-typeck.c | 3812 ------ gnu/usr.bin/gcc1/cc1/caller-save.c | 666 - gnu/usr.bin/gcc1/cc1/combine.c | 2796 ----- gnu/usr.bin/gcc1/cc1/conditions.h | 99 - gnu/usr.bin/gcc1/cc1/config/i386.md | 1986 --- gnu/usr.bin/gcc1/cc1/config/out-i386.c | 1427 --- gnu/usr.bin/gcc1/cc1/config/tm-att386.h | 222 - gnu/usr.bin/gcc1/cc1/config/tm-bsd386.h | 202 - gnu/usr.bin/gcc1/cc1/config/tm-compaq.h | 39 - gnu/usr.bin/gcc1/cc1/config/tm-i386.h | 1083 -- gnu/usr.bin/gcc1/cc1/config/tm-i386b.h | 78 - gnu/usr.bin/gcc1/cc1/config/tm-i386b.h.save | 76 - gnu/usr.bin/gcc1/cc1/config/tm-i386gas.h | 106 - gnu/usr.bin/gcc1/cc1/config/tm-i386v.h | 86 - gnu/usr.bin/gcc1/cc1/config/tm-i386v4.h | 52 - gnu/usr.bin/gcc1/cc1/config/tm-i386vgas.h | 121 - gnu/usr.bin/gcc1/cc1/config/xm-i386.h | 42 - gnu/usr.bin/gcc1/cc1/config/xm-i386v.h | 47 - gnu/usr.bin/gcc1/cc1/cse.c | 3920 ------ gnu/usr.bin/gcc1/cc1/dbxout.c | 1201 -- gnu/usr.bin/gcc1/cc1/emit-rtl.c | 1633 --- gnu/usr.bin/gcc1/cc1/explow.c | 575 - gnu/usr.bin/gcc1/cc1/expmed.c | 1863 --- gnu/usr.bin/gcc1/cc1/expr.c | 5600 --------- gnu/usr.bin/gcc1/cc1/expr.h | 386 - gnu/usr.bin/gcc1/cc1/final.c | 1652 --- gnu/usr.bin/gcc1/cc1/flags.h | 191 - gnu/usr.bin/gcc1/cc1/flow.c | 2094 ---- gnu/usr.bin/gcc1/cc1/fold-const.c | 1838 --- gnu/usr.bin/gcc1/cc1/gdbfiles.h | 15 - gnu/usr.bin/gcc1/cc1/gencodes.c | 154 - gnu/usr.bin/gcc1/cc1/genconfig.c | 267 - gnu/usr.bin/gcc1/cc1/genemit.c | 480 - gnu/usr.bin/gcc1/cc1/genextract.c | 348 - gnu/usr.bin/gcc1/cc1/genflags.c | 138 - gnu/usr.bin/gcc1/cc1/genoutput.c | 786 -- gnu/usr.bin/gcc1/cc1/genpeep.c | 437 - gnu/usr.bin/gcc1/cc1/genrecog.c | 1095 -- gnu/usr.bin/gcc1/cc1/global-alloc.c | 1090 -- gnu/usr.bin/gcc1/cc1/gstdarg.h | 39 - gnu/usr.bin/gcc1/cc1/gvarargs.h | 68 - gnu/usr.bin/gcc1/cc1/hard-reg-set.h | 229 - gnu/usr.bin/gcc1/cc1/input.h | 22 - gnu/usr.bin/gcc1/cc1/integrate.c | 2023 ---- gnu/usr.bin/gcc1/cc1/jump.c | 1618 --- gnu/usr.bin/gcc1/cc1/limits.h | 43 - gnu/usr.bin/gcc1/cc1/local-alloc.c | 1227 -- gnu/usr.bin/gcc1/cc1/loop.c | 5353 --------- gnu/usr.bin/gcc1/cc1/machmode.def | 120 - gnu/usr.bin/gcc1/cc1/math-68881.h | 475 - gnu/usr.bin/gcc1/cc1/move-if-change | 15 - gnu/usr.bin/gcc1/cc1/obstack.c | 326 - gnu/usr.bin/gcc1/cc1/obstack.h | 410 - gnu/usr.bin/gcc1/cc1/optabs.c | 2228 ---- gnu/usr.bin/gcc1/cc1/output.h | 95 - gnu/usr.bin/gcc1/cc1/print-tree.c | 510 - gnu/usr.bin/gcc1/cc1/proto.h | 4 - gnu/usr.bin/gcc1/cc1/real.h | 94 - gnu/usr.bin/gcc1/cc1/recog.c | 1106 -- gnu/usr.bin/gcc1/cc1/recog.h | 78 - gnu/usr.bin/gcc1/cc1/regclass.c | 905 -- gnu/usr.bin/gcc1/cc1/regs.h | 146 - gnu/usr.bin/gcc1/cc1/reload.c | 3291 ----- gnu/usr.bin/gcc1/cc1/reload.h | 69 - gnu/usr.bin/gcc1/cc1/reload1.c | 3466 ------ gnu/usr.bin/gcc1/cc1/rtl.c | 811 -- gnu/usr.bin/gcc1/cc1/rtl.def | 542 - gnu/usr.bin/gcc1/cc1/rtl.h | 544 - gnu/usr.bin/gcc1/cc1/rtlanal.c | 681 -- gnu/usr.bin/gcc1/cc1/sdbout.c | 1098 -- gnu/usr.bin/gcc1/cc1/stab.h | 16 - gnu/usr.bin/gcc1/cc1/stmt.c | 5057 -------- gnu/usr.bin/gcc1/cc1/stor-layout.c | 1075 -- gnu/usr.bin/gcc1/cc1/stupid.c | 528 - gnu/usr.bin/gcc1/cc1/symout.c | 1267 -- gnu/usr.bin/gcc1/cc1/symseg.h | 350 - gnu/usr.bin/gcc1/cc1/toplev.c | 2123 ---- gnu/usr.bin/gcc1/cc1/tree.c | 2211 ---- gnu/usr.bin/gcc1/cc1/tree.def | 619 - gnu/usr.bin/gcc1/cc1/tree.h | 930 -- gnu/usr.bin/gcc1/cc1/typeclass.h | 14 - gnu/usr.bin/gcc1/cc1/varasm.c | 2030 ---- gnu/usr.bin/gcc1/cc1/version.c | 1 - gnu/usr.bin/gcc1/cpp/Makefile | 19 - gnu/usr.bin/gcc1/cpp/cexp.y | 656 - gnu/usr.bin/gcc1/cpp/cpp.c | 5753 --------- gnu/usr.bin/gcc1/cpp/usr.bin.cpp.sh | 91 - gnu/usr.bin/gcc1/gnulib/DIST/gnulib.c | 452 - gnu/usr.bin/gcc1/gnulib/DIST/gnulib2.c | 922 -- gnu/usr.bin/gcc1/gnulib/Makefile | 24 - gnu/usr.bin/gcc1/gnulib/g++/builtin_New.c | 58 - gnu/usr.bin/gcc1/gnulib/g++/builtin_del.c | 31 - gnu/usr.bin/gcc1/gnulib/g++/builtin_new.c | 15 - gnu/usr.bin/gcc1/gnulib/g++/eprintf.c | 13 - gnu/usr.bin/gcc1/gnulib/i386/DEFS.h | 47 - gnu/usr.bin/gcc1/gnulib/i386/Makefile.machine | 1 - gnu/usr.bin/gcc1/gnulib/i386/fixdfsi.s | 46 - gnu/usr.bin/gcc1/gnulib/i386/fixunsdfsi.s | 60 - gnu/usr.bin/gcc1/gnulib/longlong/adddi3.c | 47 - gnu/usr.bin/gcc1/gnulib/longlong/anddi3.c | 17 - gnu/usr.bin/gcc1/gnulib/longlong/ashldi3.c | 33 - gnu/usr.bin/gcc1/gnulib/longlong/ashrdi3.c | 33 - gnu/usr.bin/gcc1/gnulib/longlong/bdiv.c | 185 - gnu/usr.bin/gcc1/gnulib/longlong/cmpdi2.c | 20 - gnu/usr.bin/gcc1/gnulib/longlong/divdi3.c | 17 - gnu/usr.bin/gcc1/gnulib/longlong/fixdfdi.c | 12 - gnu/usr.bin/gcc1/gnulib/longlong/fixunsdfdi.c | 31 - gnu/usr.bin/gcc1/gnulib/longlong/floatdidf.c | 22 - gnu/usr.bin/gcc1/gnulib/longlong/iordi3.c | 17 - gnu/usr.bin/gcc1/gnulib/longlong/longlong.h | 67 - gnu/usr.bin/gcc1/gnulib/longlong/lshldi3.c | 33 - gnu/usr.bin/gcc1/gnulib/longlong/lshrdi3.c | 33 - gnu/usr.bin/gcc1/gnulib/longlong/moddi3.c | 17 - gnu/usr.bin/gcc1/gnulib/longlong/muldi3.c | 54 - gnu/usr.bin/gcc1/gnulib/longlong/negdi2.c | 43 - .../gcc1/gnulib/longlong/one_cmpldi2.c | 16 - gnu/usr.bin/gcc1/gnulib/longlong/subdi3.c | 47 - gnu/usr.bin/gcc1/gnulib/longlong/ucmpdi2.c | 20 - gnu/usr.bin/gcc1/gnulib/longlong/udivdi3.c | 28 - gnu/usr.bin/gcc1/gnulib/longlong/umoddi3.c | 28 - gnu/usr.bin/gcc1/gnulib/longlong/xordi3.c | 17 - gnu/usr.bin/{gcc1/cc1 => gdb}/COPYING | 0 gnu/usr.bin/gdb/ChangeLog | 4887 ++++++++ gnu/usr.bin/gdb/Gdbinit | 15 + gnu/usr.bin/gdb/Makefile | 38 + gnu/usr.bin/gdb/Makefile.dist | 371 + gnu/usr.bin/gdb/Projects | 114 + gnu/usr.bin/gdb/README.gnu | 142 + gnu/usr.bin/gdb/XGdbinit.samp | 15 + gnu/usr.bin/gdb/Xgdb.ad | 8 + gnu/usr.bin/gdb/blockframe.c | 622 + gnu/usr.bin/gdb/breakpoint.c | 1383 +++ gnu/usr.bin/gdb/command.c | 856 ++ gnu/usr.bin/gdb/command.h | 77 + gnu/usr.bin/gdb/config/Makefile.i386 | 6 + gnu/usr.bin/gdb/config/default-dep.c | 585 + gnu/usr.bin/gdb/config/i386-dep.c | 1275 ++ gnu/usr.bin/gdb/config/i386-pinsn.c | 1812 +++ gnu/usr.bin/gdb/config/i386bsd-dep.c | 1884 +++ gnu/usr.bin/gdb/config/m-i386-sv32.h | 28 + gnu/usr.bin/gdb/config/m-i386.h | 394 + gnu/usr.bin/gdb/config/m-i386bsd.h | 375 + gnu/usr.bin/gdb/config/m-i386g-sv32.h | 28 + gnu/usr.bin/gdb/config/m-i386gas.h | 37 + gnu/usr.bin/gdb/copying.c | 215 + gnu/usr.bin/gdb/core.c | 581 + gnu/usr.bin/gdb/cplus-dem.c | 996 ++ gnu/usr.bin/gdb/dbxread.c | 5727 +++++++++ gnu/usr.bin/gdb/defs.h | 122 + gnu/usr.bin/gdb/environ.c | 185 + gnu/usr.bin/gdb/environ.h | 39 + gnu/usr.bin/gdb/eval.c | 1065 ++ gnu/usr.bin/gdb/expprint.c | 324 + gnu/usr.bin/gdb/expread.y | 1782 +++ gnu/usr.bin/gdb/expression.h | 191 + gnu/usr.bin/gdb/findvar.c | 579 + gnu/usr.bin/gdb/frame.h | 115 + gnu/usr.bin/gdb/gdb.1 | 3 + gnu/usr.bin/gdb/getpagesize.h | 25 + gnu/usr.bin/gdb/infcmd.c | 1204 ++ gnu/usr.bin/gdb/inferior.h | 142 + gnu/usr.bin/gdb/inflow.c | 569 + gnu/usr.bin/gdb/infrun.c | 1459 +++ gnu/usr.bin/gdb/kgdb_proto.h | 63 + gnu/usr.bin/gdb/main.c | 2241 ++++ gnu/usr.bin/gdb/ngdb.i386/Makefile | 27 + gnu/usr.bin/gdb/obstack.c | 313 + gnu/usr.bin/gdb/obstack.h | 372 + gnu/usr.bin/gdb/printcmd.c | 1867 +++ gnu/usr.bin/gdb/readline/ChangeLog | 98 + gnu/usr.bin/gdb/readline/Makefile.gnu | 114 + gnu/usr.bin/gdb/readline/chardefs.h | 50 + gnu/usr.bin/gdb/readline/emacs_keymap.c | 472 + gnu/usr.bin/gdb/readline/funmap.c | 217 + gnu/usr.bin/gdb/readline/history.c | 1462 +++ gnu/usr.bin/gdb/readline/history.h | 108 + gnu/usr.bin/gdb/readline/keymaps.c | 172 + gnu/usr.bin/gdb/readline/keymaps.h | 53 + gnu/usr.bin/gdb/readline/readline.c | 5557 +++++++++ gnu/usr.bin/gdb/readline/readline.h | 161 + gnu/usr.bin/gdb/readline/vi_keymap.c | 484 + gnu/usr.bin/gdb/readline/vi_mode.c | 875 ++ gnu/usr.bin/gdb/regex.c | 1738 +++ gnu/usr.bin/gdb/regex.h | 185 + gnu/usr.bin/gdb/remote-sl.c | 10 + gnu/usr.bin/gdb/remote.c | 626 + gnu/usr.bin/gdb/source.c | 1166 ++ gnu/usr.bin/{gcc1/cc1 => gdb}/stab.def | 0 gnu/usr.bin/gdb/stack.c | 960 ++ gnu/usr.bin/gdb/symmisc.c | 584 + gnu/usr.bin/gdb/symseg.h | 523 + gnu/usr.bin/gdb/symtab.c | 2473 ++++ gnu/usr.bin/gdb/symtab.h | 384 + gnu/usr.bin/gdb/utils.c | 1096 ++ gnu/usr.bin/gdb/valarith.c | 690 ++ gnu/usr.bin/gdb/valops.c | 1418 +++ gnu/usr.bin/gdb/valprint.c | 1430 +++ gnu/usr.bin/gdb/value.h | 212 + gnu/usr.bin/gdb/values.c | 1059 ++ gnu/usr.bin/gdb/version.c | 20 + gnu/usr.bin/gdb/wait.h | 81 + gnu/usr.bin/gdb/xgdb/Makefile | 33 + gnu/usr.bin/gdb/xgdb/xgdb.c | 700 ++ gnu/usr.bin/grep/AUTHORS | 29 + gnu/usr.bin/grep/Makefile | 15 +- gnu/usr.bin/grep/NEWS | 35 + gnu/usr.bin/grep/PROJECTS | 15 + gnu/usr.bin/grep/README | 68 +- gnu/usr.bin/grep/dfa.c | 2491 ++-- gnu/usr.bin/grep/dfa.h | 308 +- gnu/usr.bin/grep/getopt.c | 137 +- gnu/usr.bin/grep/getopt.h | 40 +- gnu/usr.bin/grep/getpagesize.h | 42 + gnu/usr.bin/grep/grep.1 | 507 +- gnu/usr.bin/grep/grep.c | 1505 +-- gnu/usr.bin/grep/grep.h | 53 + gnu/usr.bin/grep/kwset.c | 805 ++ gnu/usr.bin/grep/kwset.h | 69 + gnu/usr.bin/grep/obstack.c | 454 + gnu/usr.bin/grep/obstack.h | 484 + gnu/usr.bin/grep/regex.c | 5861 +++++++-- gnu/usr.bin/grep/regex.h | 625 +- gnu/usr.bin/grep/search.c | 481 + gnu/usr.bin/grep/tests/check.sh | 24 + gnu/usr.bin/grep/tests/regress.sh | 30 - gnu/usr.bin/grep/tests/scriptgen.awk | 4 +- gnu/usr.bin/grep/tests/spencer.tests | 2 +- gnu/usr.bin/groff/Makefile | 3 +- gnu/usr.bin/groff/Makefile.cfg | 12 +- gnu/usr.bin/groff/afmtodit/Makefile | 2 +- gnu/usr.bin/groff/devices/Makefile.dev | 9 +- gnu/usr.bin/groff/grog/Makefile | 2 +- gnu/usr.bin/groff/indxbib/Makefile | 2 +- gnu/usr.bin/groff/libdriver/Makefile | 3 + gnu/usr.bin/groff/man/Makefile | 4 +- gnu/usr.bin/groff/mm/Makefile | 6 +- gnu/usr.bin/groff/nroff/Makefile | 2 +- gnu/usr.bin/groff/tmac/Makefile | 4 +- gnu/usr.bin/groff/troff/Makefile | 1 + gnu/usr.bin/groff/xditview/Makefile | 6 +- gnu/usr.bin/gzip/ChangeLog | 137 + gnu/usr.bin/gzip/Makefile | 16 +- gnu/usr.bin/gzip/NEWS | 65 +- gnu/usr.bin/gzip/README | 63 +- gnu/usr.bin/gzip/THANKS | 92 +- gnu/usr.bin/gzip/TODO | 12 +- gnu/usr.bin/gzip/algorithm.doc | 23 + gnu/usr.bin/gzip/bits.c | 6 +- gnu/usr.bin/gzip/crypt.c | 2 +- gnu/usr.bin/gzip/deflate.c | 93 +- gnu/usr.bin/gzip/getopt.c | 236 +- gnu/usr.bin/gzip/getopt.h | 21 +- gnu/usr.bin/gzip/gzexe | 38 +- gnu/usr.bin/gzip/gzexe.1 | 7 + gnu/usr.bin/gzip/gzexe.in | 135 - gnu/usr.bin/gzip/gzip.1 | 180 +- gnu/usr.bin/gzip/gzip.c | 560 +- gnu/usr.bin/gzip/gzip.h | 37 +- gnu/usr.bin/gzip/inflate.c | 8 +- gnu/usr.bin/gzip/lzw.c | 11 +- gnu/usr.bin/gzip/match.S | 20 +- gnu/usr.bin/gzip/revision.h | 6 +- gnu/usr.bin/gzip/tailor.h | 82 +- gnu/usr.bin/gzip/trees.c | 5 +- gnu/usr.bin/gzip/unlzh.c | 401 + gnu/usr.bin/gzip/unlzw.c | 28 +- gnu/usr.bin/gzip/unpack.c | 22 +- gnu/usr.bin/gzip/unzip.c | 8 +- gnu/usr.bin/gzip/util.c | 65 +- gnu/usr.bin/gzip/zcmp | 67 - gnu/usr.bin/gzip/zdiff | 18 +- gnu/usr.bin/gzip/zdiff.1 | 2 +- gnu/usr.bin/gzip/zforce | 22 +- gnu/usr.bin/gzip/zforce.1 | 2 +- gnu/usr.bin/gzip/zgrep | 72 + gnu/usr.bin/gzip/zgrep.1 | 44 + gnu/usr.bin/gzip/zip.c | 5 +- gnu/usr.bin/gzip/zmore | 17 +- gnu/usr.bin/gzip/zmore.1 | 17 +- gnu/usr.bin/gzip/znew | 53 +- gnu/usr.bin/gzip/znew.1 | 5 +- gnu/usr.bin/ld/Makefile | 8 + gnu/usr.bin/ld/cplus-dem.c | 974 ++ gnu/usr.bin/ld/ld.c | 4717 ++++++++ gnu/usr.bin/ld/symseg.h | 358 + gnu/usr.bin/man/Makefile | 2 +- gnu/usr.bin/man/Makefile.inc | 30 +- gnu/usr.bin/man/apropos/Makefile | 33 +- gnu/usr.bin/man/apropos/apropos | 64 - gnu/usr.bin/man/apropos/apropos.1 | 27 - gnu/usr.bin/man/catman/Makefile | 8 + gnu/usr.bin/man/catman/catman | 36 + gnu/usr.bin/man/lib/Makefile | 22 +- gnu/usr.bin/man/lib/config.h | 216 - gnu/usr.bin/man/lib/config.h_dist | 216 + gnu/usr.bin/man/lib/gripes.po | Bin 3219 -> 0 bytes gnu/usr.bin/man/lib/util.c | 15 +- gnu/usr.bin/man/lib/util.po | Bin 1013 -> 0 bytes gnu/usr.bin/man/makewhatis/Makefile | 22 +- gnu/usr.bin/man/makewhatis/makewhatis | 79 - gnu/usr.bin/man/makewhatis/makewhatis.sh | 76 +- gnu/usr.bin/man/man/Makefile | 25 +- gnu/usr.bin/man/man/man.1 | 132 - gnu/usr.bin/man/man/man.c | 107 +- gnu/usr.bin/man/manpath/Makefile | 26 +- gnu/usr.bin/man/manpath/manpath | Bin 61908 -> 0 bytes gnu/usr.bin/man/manpath/manpath.1 | 56 - gnu/usr.bin/man/whatis/Makefile | 33 +- gnu/usr.bin/man/whatis/whatis | 66 - gnu/usr.bin/man/whatis/whatis.1 | 27 - gnu/usr.bin/patch/common.h | 13 +- gnu/usr.bin/pr/system.h | 2 + gnu/usr.bin/rcs/Makefile | 2 +- gnu/usr.bin/rcs/Makefile.inc | 8 +- gnu/usr.bin/rcs/ci/Makefile | 7 +- gnu/usr.bin/rcs/co/Makefile | 7 +- gnu/usr.bin/rcs/ident/Makefile | 7 +- gnu/usr.bin/rcs/lib/Makefile | 15 +- gnu/usr.bin/rcs/lib/rcslex.c | 11 +- gnu/usr.bin/rcs/merge/Makefile | 7 +- gnu/usr.bin/rcs/rcs/Makefile | 11 +- gnu/usr.bin/rcs/rcsclean/Makefile | 7 +- gnu/usr.bin/rcs/rcsclean/rcsclean | Bin 101972 -> 0 bytes gnu/usr.bin/rcs/rcsclean/rcsclean.0 | 132 - gnu/usr.bin/rcs/rcsdiff/Makefile | 7 +- gnu/usr.bin/rcs/rcsfreeze/Makefile | 13 +- gnu/usr.bin/rcs/rcsmerge/Makefile | 7 +- gnu/usr.bin/rcs/rlog/Makefile | 7 +- gnu/usr.bin/rcs/rlog/rlog.c | 6 +- gnu/usr.bin/sdiff/Makefile | 11 + gnu/usr.bin/sort/system.h | 2 + gnu/usr.bin/tar/Makefile | 1 + gnu/usr.bin/tar/y.tab.h | 18 - include/Makefile | 46 +- include/a.out.h | 24 +- include/arpa/inet.h | 2 +- include/arpa/nameser.h | 24 +- include/assert.h | 37 +- include/ctype.h | 20 +- include/db.h | 214 +- include/dirent.h | 1 + include/err.h | 62 + include/fnmatch.h | 51 + include/fts.h | 78 +- include/glob.h | 32 +- include/math.h | 12 +- include/mpool.h | 139 + include/ndbm.h | 9 +- include/netdb.h | 2 +- include/pwd.h | 5 + include/rpcsvc/Makefile | 37 + include/rpcsvc/bootparam_prot.x | 100 + include/rpcsvc/klm_prot.x | 138 + include/rpcsvc/mount.x | 165 + include/rpcsvc/nfs_prot.x | 357 + include/rpcsvc/nlm_prot.x | 182 + include/rpcsvc/rex.x | 234 + include/rpcsvc/rnusers.x | 91 + include/rpcsvc/rquota.x | 66 + include/rpcsvc/rstat.x | 150 + include/rpcsvc/rwall.x | 46 + include/rpcsvc/sm_inter.x | 120 + include/rpcsvc/spray.x | 89 + include/rpcsvc/yp.x | 296 + include/rpcsvc/yp_prot.h | 330 + include/rpcsvc/ypclnt.h | 86 + include/rpcsvc/yppasswd.x | 69 + include/setjmp.h | 10 +- include/stab.h | 5 + include/stdio.h | 6 +- include/stdlib.h | 11 + include/unistd.h | 7 - include/vis.h | 2 +- lib/Makefile | 10 +- lib/csu.i386/Makefile | 2 +- lib/csu.i386/crt0.c | 17 + lib/libc/compat-43/Makefile.inc | 3 +- lib/libc/db/Makefile.inc | 10 +- lib/libc/db/PORT/Makefile | 55 + lib/libc/db/PORT/README | 60 + lib/libc/db/PORT/clib/memmove.c | 139 + lib/libc/db/PORT/clib/mktemp.c | 126 + lib/libc/db/PORT/clib/realloc.c | 11 + lib/libc/db/PORT/clib/snprintf.c | 54 + lib/libc/db/PORT/include/cdefs.h | 94 + lib/libc/db/PORT/include/compat.h | 217 + lib/libc/db/PORT/include/db.h | 194 + lib/libc/db/PORT/include/mpool.h | 135 + lib/libc/db/PORT/include/ndbm.h | 77 + lib/libc/db/VERSION | 58 + lib/libc/db/btree/Makefile.inc | 9 +- lib/libc/db/btree/big.c | 383 - lib/libc/db/btree/bt_close.c | 191 + lib/libc/db/btree/bt_conv.c | 221 + lib/libc/db/btree/bt_debug.c | 332 + lib/libc/db/btree/bt_delete.c | 317 + lib/libc/db/btree/bt_get.c | 222 + lib/libc/db/btree/bt_open.c | 428 + lib/libc/db/btree/bt_overflow.c | 224 + lib/libc/db/btree/bt_page.c | 94 + lib/libc/db/btree/bt_put.c | 313 + lib/libc/db/btree/bt_search.c | 119 + lib/libc/db/btree/bt_seq.c | 365 + lib/libc/db/btree/bt_split.c | 826 ++ lib/libc/db/btree/bt_stack.c | 92 + lib/libc/db/btree/bt_utils.c | 227 + lib/libc/db/btree/btree.c | 752 -- lib/libc/db/btree/btree.h | 525 +- lib/libc/db/btree/delete.c | 201 - lib/libc/db/btree/extern.h | 71 + lib/libc/db/btree/insert.c | 312 - lib/libc/db/btree/lrucache.c | 377 - lib/libc/db/btree/lrucache.h | 110 - lib/libc/db/btree/lruhash.c | 173 - lib/libc/db/btree/lrutils.c | 244 - lib/libc/db/btree/search.c | 296 - lib/libc/db/btree/seq.c | 318 - lib/libc/db/btree/split.c | 691 -- lib/libc/db/btree/storage.c | 664 - lib/libc/db/btree/tests/words.c | 528 - lib/libc/db/btree/updutils.c | 174 - lib/libc/db/btree/utils.c | 350 - lib/libc/db/db.3 | 721 -- lib/libc/db/db/Makefile.inc | 5 + lib/libc/db/db/db.c | 89 + lib/libc/db/doc/btree.3.ps | 364 + lib/libc/db/doc/dbopen.3.ps | 496 + lib/libc/db/doc/hash.3.ps | 289 + lib/libc/db/{hash => doc}/hash.ps | 0 lib/libc/db/doc/mpool.3.ps | 318 + lib/libc/db/doc/recno.3.ps | 317 + lib/libc/db/hash/Makefile.inc | 5 +- lib/libc/db/hash/README | 61 +- lib/libc/db/hash/bigkey.c | 672 -- lib/libc/db/hash/buf.c | 346 - lib/libc/db/hash/dynahash.c | 1013 -- lib/libc/db/hash/extern.h | 65 + lib/libc/db/hash/hash.c | 988 ++ lib/libc/db/hash/hash.h | 359 +- lib/libc/db/hash/hash_bigkey.c | 669 ++ lib/libc/db/hash/hash_buf.c | 344 + lib/libc/db/hash/hash_func.c | 191 + lib/libc/db/hash/hash_log2.c | 52 + lib/libc/db/hash/hash_page.c | 941 ++ lib/libc/db/hash/hfunc.c | 179 - lib/libc/db/hash/hsearch.c | 107 +- lib/libc/db/hash/log2.c | 49 - lib/libc/db/hash/ndbm.c | 224 +- lib/libc/db/hash/page.c | 905 -- lib/libc/db/hash/page.h | 60 +- lib/libc/db/hash/search.h | 22 +- lib/libc/db/hash/tests/tcreat3.c | 104 - lib/libc/db/hash/tests/tdel.c | 122 - lib/libc/db/hash/tests/thash4.c | 132 - lib/libc/db/hash/tests/tread2.c | 105 - lib/libc/db/hash/tests/tseq.c | 88 - lib/libc/db/hash/tests/tverify.c | 107 - lib/libc/db/man/Makefile.inc | 7 + lib/libc/db/man/btree.3 | 224 + lib/libc/db/man/dbopen.3 | 453 + lib/libc/db/man/hash.3 | 151 + lib/libc/db/man/mpool.3 | 219 + lib/libc/db/man/recno.3 | 196 + lib/libc/db/mpool/Makefile.inc | 5 + lib/libc/db/mpool/README | 7 + lib/libc/db/mpool/mpool.c | 534 + lib/libc/db/recno/Makefile.inc | 6 + lib/libc/db/recno/extern.h | 54 + lib/libc/db/recno/rec_close.c | 151 + lib/libc/db/recno/rec_delete.c | 193 + lib/libc/db/recno/rec_get.c | 285 + lib/libc/db/recno/rec_open.c | 221 + lib/libc/db/recno/rec_put.c | 237 + lib/libc/db/recno/rec_search.c | 127 + lib/libc/db/recno/rec_seq.c | 122 + lib/libc/db/recno/rec_utils.c | 103 + lib/libc/db/recno/recno.h | 39 + lib/libc/db/test/Makefile | 10 + lib/libc/db/test/README | 45 + lib/libc/db/test/btree.tests/main.c | 765 ++ lib/libc/db/test/dbtest.c | 656 + lib/libc/db/test/hash.tests/driver2.c | 114 + lib/libc/db/test/hash.tests/makedb.sh | 13 + lib/libc/db/test/hash.tests/tcreat3.c | 105 + lib/libc/db/test/hash.tests/tdel.c | 122 + lib/libc/db/test/hash.tests/testit | 147 + lib/libc/db/test/hash.tests/thash4.c | 132 + lib/libc/db/test/hash.tests/tread2.c | 105 + lib/libc/db/test/hash.tests/tseq.c | 88 + lib/libc/db/test/hash.tests/tverify.c | 107 + lib/libc/db/test/run.test | 616 + lib/libc/gen/Makefile.inc | 67 +- lib/libc/gen/README.crypt | 35 + lib/libc/gen/assert.c | 53 + lib/libc/gen/closedir.c | 58 - lib/libc/gen/crypt.c | 235 + lib/libc/gen/crypt_dummy.c | 5 - lib/libc/gen/directory.c | 234 + lib/libc/gen/err.3 | 129 + lib/libc/gen/err.c | 188 + lib/libc/gen/fnmatch.3 | 55 +- lib/libc/gen/fnmatch.c | 102 +- lib/libc/gen/fts.3 | 256 +- lib/libc/gen/fts.c | 652 +- lib/libc/gen/getcap.3 | 511 + lib/libc/gen/getcap.c | 1038 ++ lib/libc/gen/getcwd.c | 6 +- lib/libc/gen/getgrent.3 | 2 +- lib/libc/gen/getpwent.c | 2 +- lib/libc/gen/getusershell.c | 2 +- lib/libc/gen/glob.3 | 23 +- lib/libc/gen/glob.c | 101 +- lib/libc/gen/infinity.c | 6 + lib/libc/gen/insque.3 | 70 + lib/libc/gen/insque.c | 65 + lib/libc/gen/opendir.c | 88 - lib/libc/gen/readdir.c | 72 - lib/libc/gen/rewinddir.c | 49 - lib/libc/gen/seekdir.c | 52 - lib/libc/gen/setjmp.3 | 3 +- lib/libc/gen/shmat.c | 19 + lib/libc/gen/shmctl.c | 19 + lib/libc/gen/shmdt.c | 17 + lib/libc/gen/shmget.c | 19 + lib/libc/gen/signal.c | 2 +- lib/libc/gen/syslog.c | 34 +- lib/libc/gen/tcsendbreak.3 | 154 + lib/libc/gen/telldir.c | 129 - lib/libc/gen/ttyname.c | 2 +- lib/libc/gen/utime.3 | 103 +- lib/libc/gen/utime.c | 15 +- lib/libc/gen/vis.c | 21 +- lib/libc/i386/DEFS.h | 29 +- lib/libc/i386/SYS.h | 8 +- lib/libc/i386/gen/_setjmp.s | 14 +- lib/libc/i386/gen/fixdfsi.s | 61 +- lib/libc/i386/gen/fixunsdfsi.s | 74 +- lib/libc/i386/gen/ldexp.c | 10 + lib/libc/i386/gen/setjmp.s | 16 +- lib/libc/i386/gen/sigsetjmp.s | 88 + lib/libc/i386/stdlib/abs.s | 1 + lib/libc/i386/stdlib/atof.c | 181 - lib/libc/i386/stdlib/div.s | 47 + lib/libc/i386/stdlib/labs.s | 49 + lib/libc/i386/stdlib/ldiv.s | 47 + lib/libc/i386/string/bcmp.s | 70 + lib/libc/i386/string/bcopy.s | 11 +- lib/libc/i386/string/bzero.s | 95 +- lib/libc/i386/string/ffs.s | 58 + lib/libc/i386/string/index.s | 68 + lib/libc/i386/string/memchr.s | 63 + lib/libc/i386/string/memcmp.s | 81 + lib/libc/i386/string/memmove.s | 90 + lib/libc/i386/string/memset.s | 96 + lib/libc/i386/string/rindex.s | 69 + lib/libc/i386/string/strcat.s | 105 + lib/libc/i386/string/strchr.s | 68 + lib/libc/i386/string/strcmp.s | 124 + lib/libc/i386/string/strcpy.s | 94 + lib/libc/i386/string/strlen.s | 58 + lib/libc/i386/string/strncmp.s | 156 + lib/libc/i386/string/strrchr.s | 69 + lib/libc/i386/sys/syscall.s | 2 + lib/libc/net/Makefile.inc | 9 +- lib/libc/net/gethostnamadr.c | 220 +- lib/libc/net/inet_addr.c | 6 +- lib/libc/stdio/Makefile.inc | 9 +- lib/libc/stdio/fgetpos.c | 2 +- lib/libc/stdio/fread.c | 2 +- lib/libc/stdio/local.h | 3 +- lib/libc/stdio/makebuf.c | 77 +- lib/libc/stdio/scanf.3 | 14 +- lib/libc/stdio/setvbuf.c | 116 +- lib/libc/stdio/sscanf.c | 4 +- lib/libc/stdio/tempnam.c | 10 +- lib/libc/stdio/tmpnam.c | 2 +- lib/libc/stdio/vfprintf.c | 619 +- lib/libc/stdio/vfscanf.c | 10 +- lib/libc/stdlib/Makefile.inc | 30 +- lib/libc/stdlib/_rand48.c | 46 + lib/libc/stdlib/atof.c | 4 +- lib/libc/stdlib/drand48.c | 22 + lib/libc/stdlib/erand48.c | 23 + lib/libc/stdlib/jrand48.c | 21 + lib/libc/stdlib/lcong48.c | 30 + lib/libc/stdlib/lrand48.c | 23 + lib/libc/stdlib/mrand48.c | 23 + lib/libc/stdlib/nrand48.c | 21 + lib/libc/stdlib/rand48.3 | 163 + lib/libc/stdlib/rand48.h | 30 + lib/libc/stdlib/seed48.c | 36 + lib/libc/stdlib/srand48.c | 30 + lib/libc/stdlib/strtod.c | 2410 ++++ lib/libc/string/Makefile.inc | 35 +- lib/libc/string/strftime.3 | 103 +- lib/libc/string/strftime.c | 103 +- lib/libc/sys/Makefile.inc | 75 +- lib/libc/sys/execve.2 | 8 +- lib/libc/sys/getdomainname.2 | 97 + lib/libc/sys/getpgrp.2 | 21 +- lib/libc/sys/reboot.2 | 4 +- lib/libc/sys/uname.2 | 100 + lib/libcrypt/Makefile | 26 + lib/{libtelnet => libcrypt}/auth-proto.h | 0 lib/{libtelnet => libcrypt}/auth.c | 0 lib/{libtelnet => libcrypt}/auth.h | 0 lib/libcrypt/crypt.c | 966 ++ lib/{libtelnet => libcrypt}/enc-proto.h | 0 lib/{libtelnet => libcrypt}/enc_des.c | 0 lib/{libtelnet => libcrypt}/encrypt.c | 0 lib/{libtelnet => libcrypt}/encrypt.h | 0 lib/{libtelnet => libcrypt}/kerberos.c | 0 lib/{libtelnet => libcrypt}/kerberos5.c | 0 lib/{libtelnet => libcrypt}/key-proto.h | 0 lib/{libtelnet => libcrypt}/krb_des.c | 0 lib/libcrypt/misc-proto.h | 79 + lib/libcrypt/misc.c | 113 + lib/libcrypt/misc.h | 42 + lib/libcurses/Makefile | 2 +- lib/libcurses/addbytes.c | 35 +- lib/libcurses/addch.c | 5 +- lib/libcurses/addstr.c | 11 +- lib/libcurses/box.c | 8 +- lib/libcurses/clrtobot.c | 2 +- lib/libcurses/clrtoeol.c | 4 +- lib/libcurses/cr_put.c | 7 +- lib/libcurses/curses.h | 12 +- lib/libcurses/delch.c | 5 +- lib/libcurses/deleteln.c | 8 +- lib/libcurses/erase.c | 2 +- lib/libcurses/getch.c | 8 +- lib/libcurses/getstr.c | 9 +- lib/libcurses/insch.c | 8 +- lib/libcurses/insertln.c | 6 +- lib/libcurses/newwin.c | 6 +- lib/libcurses/overlay.c | 5 +- lib/libcurses/overwrite.c | 3 +- lib/libcurses/printw.c | 2 +- lib/libcurses/refresh.c | 24 +- lib/libm/Makefile | 125 +- lib/libm/common/sincos.c | 6 +- lib/libm/common_source/erf.c | 396 + lib/libm/common_source/exp.c | 47 +- lib/libm/common_source/floor.c | 24 +- lib/libm/common_source/gamma.c | 336 + lib/libm/common_source/j0.c | 439 + lib/libm/common_source/j1.c | 446 + lib/libm/common_source/jn.c | 312 + lib/libm/common_source/lgamma.3 | 137 +- lib/libm/common_source/lgamma.c | 308 + lib/libm/common_source/log.c | 541 +- lib/libm/common_source/mathimpl.h | 5 +- lib/libm/common_source/pow.c | 220 +- lib/librpc/Makefile | 31 +- lib/librpc/man/Makefile | 3 + lib/librpc/man/man1/Makefile | 3 + lib/librpc/man/man3/Makefile | 3 + lib/librpc/man/man5/Makefile | 3 + lib/librpc/man/man8/Makefile | 3 + lib/librpc/rpc/Makefile | 5 + lib/librpc/rpc/auth.h | 22 +- lib/librpc/rpc/auth_none.c | 8 +- lib/librpc/rpc/auth_unix.c | 9 +- lib/librpc/rpc/auth_unix.h | 16 +- lib/librpc/rpc/authunix_prot.c | 8 +- lib/librpc/rpc/bindresvport.c | 7 +- lib/librpc/rpc/clnt.h | 87 +- lib/librpc/rpc/clnt_generic.c | 13 +- lib/librpc/rpc/clnt_perror.c | 13 +- lib/librpc/rpc/clnt_raw.c | 8 +- lib/librpc/rpc/clnt_simple.c | 11 +- lib/librpc/rpc/clnt_tcp.c | 8 +- lib/librpc/rpc/clnt_udp.c | 8 +- lib/librpc/rpc/get_myaddress.c | 9 +- lib/librpc/rpc/getrpcent.c | 183 +- lib/librpc/rpc/getrpcport.c | 10 +- lib/librpc/rpc/pmap_clnt.c | 8 +- lib/librpc/rpc/pmap_clnt.h | 33 +- lib/librpc/rpc/pmap_getmaps.c | 8 +- lib/librpc/rpc/pmap_getport.c | 8 +- lib/librpc/rpc/pmap_prot.c | 8 +- lib/librpc/rpc/pmap_prot.h | 18 +- lib/librpc/rpc/pmap_prot2.c | 8 +- lib/librpc/rpc/pmap_rmt.c | 8 +- lib/librpc/rpc/pmap_rmt.h | 18 +- lib/librpc/rpc/rpc.h | 19 +- lib/librpc/rpc/rpc.order | 37 - lib/librpc/rpc/rpc_callmsg.c | 8 +- lib/librpc/rpc/rpc_commondata.c | 7 +- lib/librpc/rpc/rpc_dtablesize.c | 8 +- lib/librpc/rpc/rpc_msg.h | 22 +- lib/librpc/rpc/rpc_prot.c | 8 +- lib/librpc/rpc/svc.c | 8 +- lib/librpc/rpc/svc.h | 72 +- lib/librpc/rpc/svc_auth.c | 9 +- lib/librpc/rpc/svc_auth.h | 14 +- lib/librpc/rpc/svc_auth_unix.c | 8 +- lib/librpc/rpc/svc_raw.c | 8 +- lib/librpc/rpc/svc_run.c | 11 +- lib/librpc/rpc/svc_simple.c | 8 +- lib/librpc/rpc/svc_tcp.c | 8 +- lib/librpc/rpc/svc_udp.c | 9 +- lib/librpc/rpc/types.h | 29 +- lib/librpc/rpc/xdr.c | 8 +- lib/librpc/rpc/xdr.h | 87 +- lib/librpc/rpc/xdr_array.c | 10 +- lib/librpc/rpc/xdr_float.c | 42 +- lib/librpc/rpc/xdr_mem.c | 8 +- lib/librpc/rpc/xdr_rec.c | 8 +- lib/librpc/rpc/xdr_reference.c | 8 +- lib/librpc/rpc/xdr_stdio.c | 8 +- lib/librpcsvc/Makefile | 31 + lib/libtelnet/Makefile | 4 +- lib/libterm/Makefile | 2 +- lib/libutil/Makefile | 2 +- libexec/Makefile | 15 +- libexec/bugfiler/Makefile | 10 +- libexec/bugfiler/bug.h | 5 +- libexec/bugfiler/bugformat | 17 +- libexec/bugfiler/pathnames.h | 5 +- libexec/bugfiler/sendbug.1 | 11 +- libexec/bugfiler/sendbug.sh | 10 +- libexec/comsat/Makefile | 2 +- libexec/crond/Makefile | 2 +- libexec/elvispreserve/Makefile | 6 +- libexec/elvispreserve/elvispreserve.8 | 8 +- libexec/elvispreserve/elvispreserve.c | 39 +- libexec/elvispreserve/prsvunix.c | 226 + libexec/fingerd/Makefile | 2 +- libexec/ftpd/Makefile | 9 +- libexec/ftpd/ftpd.c | 4 - libexec/getty/Makefile | 6 +- libexec/getty/gettytab.c | 4 +- libexec/getty/main.c | 69 +- libexec/getty/subr.c | 2 + libexec/getty/ttydefaults.c | 51 - libexec/getty/ttys.5 | 1 - libexec/mail.local/Makefile | 2 +- libexec/makekey/Makefile | 7 +- libexec/pppd/Makefile | 24 + libexec/pppd/args.h | 11 + libexec/pppd/callout.h | 16 + libexec/pppd/chap.c | 678 ++ libexec/pppd/chap.h | 106 + libexec/pppd/fsm.c | 765 ++ libexec/pppd/fsm.h | 118 + libexec/pppd/ipcp.c | 968 ++ libexec/pppd/ipcp.h | 63 + libexec/pppd/lcp.c | 1016 ++ libexec/pppd/lcp.h | 71 + libexec/pppd/logwtmp.c | 54 + libexec/pppd/magic.c | 66 + libexec/pppd/magic.h | 22 + libexec/pppd/main.c | 2298 ++++ libexec/pppd/md5.c | 298 + libexec/pppd/md5.h | 58 + libexec/pppd/md5driver.c | 196 + libexec/pppd/patchlevel.h | 4 + libexec/pppd/pathnames.h | 11 + libexec/pppd/pppd.8 | 281 + libexec/pppd/pppd.h | 436 + libexec/pppd/upap.c | 484 + libexec/pppd/upap.h | 90 + libexec/rexecd/Makefile | 7 +- libexec/rlogind/Makefile | 8 +- libexec/rpc.rstatd/Makefile | 10 + libexec/rpc.rstatd/rpc.rstatd.8 | 61 + libexec/rpc.rstatd/rstat_proc.c | 422 + libexec/rpc.rstatd/rstatd.c | 117 + libexec/rpc.rusersd/Makefile | 16 + libexec/rpc.rusersd/rpc.rusersd.8 | 64 + libexec/rpc.rusersd/rusers_proc.c | 385 + libexec/rpc.rusersd/rusersd.c | 108 + libexec/rpc.rwalld/Makefile | 10 + libexec/rpc.rwalld/rpc.rwalld.8 | 67 + libexec/rpc.rwalld/rwalld.c | 209 + libexec/rshd/Makefile | 9 +- libexec/talkd/Makefile | 2 +- libexec/telnetd/Makefile | 18 +- libexec/tftpd/Makefile | 2 +- libexec/tftpd/tftpd.c | 4 +- libexec/uucpd/Makefile | 5 + sbin/Makefile | 29 +- sbin/XNSrouted/Makefile | 10 + sbin/XNSrouted/XNSrouted.8 | 186 + sbin/XNSrouted/af.c | 246 + sbin/XNSrouted/af.h | 64 + sbin/XNSrouted/defs.h | 88 + sbin/XNSrouted/if.c | 147 + sbin/XNSrouted/input.c | 189 + sbin/XNSrouted/interface.h | 90 + sbin/XNSrouted/main.c | 245 + sbin/XNSrouted/output.c | 147 + sbin/XNSrouted/protocol.h | 85 + sbin/XNSrouted/startup.c | 218 + sbin/XNSrouted/table.h | 99 + sbin/XNSrouted/tables.c | 265 + sbin/XNSrouted/timer.c | 139 + sbin/XNSrouted/tools/query.c | 232 + sbin/XNSrouted/trace.c | 313 + sbin/XNSrouted/trace.h | 96 + sbin/badsect/Makefile | 2 +- sbin/disklabel/Makefile | 10 +- sbin/disklabel/{disklabel.5.5 => disklabel.5} | 0 sbin/disklabel/disklabel.c | 1 + sbin/dmesg/Makefile | 2 +- sbin/dmesg/dmesg.8 | 2 +- sbin/dump/Makefile | 8 +- sbin/dump/dump.8 | 2 + sbin/dump/dump.h | 2 + sbin/dump/dumpitime.c | 1 + sbin/dump/dumpmain.c | 13 +- sbin/dump/dumpoptr.c | 5 +- sbin/dump/dumprmt.c | 8 +- sbin/dump/dumptape.c | 1 + sbin/dump/dumptraverse.c | 18 +- sbin/dump/pathnames.h | 2 + sbin/dump/rdump.8 | 4 +- sbin/dump/unctime.c | 1 + sbin/dumpfs/Makefile | 2 +- sbin/fastboot/Makefile | 2 +- sbin/fdisk/Makefile | 2 +- sbin/fdisk/fdisk.8 | 1 + sbin/fsck/Makefile | 6 +- sbin/fsck/dir.c | 42 +- sbin/fsck/fsck.8 | 2 + sbin/fsck/fsck.h | 2 + sbin/fsck/inode.c | 4 +- sbin/fsck/main.c | 1 + sbin/fsck/pass1.c | 8 + sbin/fsck/pass1b.c | 1 + sbin/fsck/pass2.c | 1 + sbin/fsck/pass3.c | 1 + sbin/fsck/pass4.c | 1 + sbin/fsck/pass5.c | 1 + sbin/fsck/preen.c | 1 + sbin/fsck/setup.c | 1 + sbin/fsck/utilities.c | 1 + sbin/halt/Makefile | 2 +- sbin/ifconfig/Makefile | 2 +- sbin/init.bsdi/Makefile | 17 + sbin/init.bsdi/README | 35 + sbin/init.bsdi/init.8 | 291 + sbin/init.bsdi/init.c | 1292 ++ sbin/init.bsdi/pathnames.h | 42 + sbin/init.chmr/Makefile | 8 + sbin/init.chmr/README | 44 + sbin/init.chmr/cf_defs.h | 67 + sbin/init.chmr/cf_table.c | 324 + sbin/init.chmr/configure.c | 316 + sbin/init.chmr/fake_syslog.c | 125 + sbin/init.chmr/init.8 | 359 + sbin/init.chmr/init.c | 828 ++ sbin/init.chmr/init.conf | 125 + sbin/init.chmr/init.h | 76 + sbin/init.chmr/libutil.h | 7 + sbin/init.chmr/prototypes.h | 70 + sbin/init.chmr/ttytab.c | 413 + sbin/init.chmr/utils.c | 180 + sbin/init/Makefile | 4 + sbin/init/init.c | 1 + sbin/mknod/Makefile | 2 +- sbin/mount/Makefile | 2 +- sbin/mount/mount.8 | 7 +- sbin/mount/mount.c | 9 + sbin/mount_isofs/Makefile | 4 +- sbin/mount_isofs/mount_isofs.8 | 98 + sbin/mount_isofs/mount_isofs.c | 35 +- sbin/mountd/Makefile | 4 +- sbin/newfs/Makefile | 2 +- sbin/newfs/newfs.c | 5 +- sbin/nfsd/Makefile | 2 +- sbin/nfsiod/Makefile | 2 +- sbin/ping/Makefile | 2 +- sbin/ping/ping.c | 18 +- sbin/quotacheck/Makefile | 2 +- sbin/reboot/Makefile | 4 +- sbin/reboot/reboot_i386.8 | 10 +- sbin/restore/Makefile | 4 +- sbin/route/Makefile | 2 +- sbin/route/route.c | 38 +- sbin/routed/Makefile | 2 +- sbin/savecore/Makefile | 2 +- sbin/shutdown/Makefile | 2 +- sbin/slattach/Makefile | 7 +- sbin/slattach/slattach.8 | 117 +- sbin/slattach/slattach.c | 461 +- sbin/st/Makefile | 6 + sbin/st/st.8 | 232 + sbin/st/st.c | 259 + sbin/swapon/Makefile | 2 +- sbin/tunefs/Makefile | 2 +- sbin/umount/umount.c | 78 +- share/Makefile | 14 +- share/doc/Makefile | 8 +- share/doc/ps1/04.pascal/Makefile | 11 +- share/doc/ps1/04.pascal/csfix.c | 3 +- share/doc/ps1/06.sysman/Makefile | 13 +- share/doc/ps1/06.sysman/a.t | 6 +- share/doc/ps1/07.ipctut/Makefile | 14 +- share/doc/ps1/07.ipctut/dgramread.c | 3 +- share/doc/ps1/07.ipctut/dgramsend.c | 3 +- share/doc/ps1/07.ipctut/pipe.c | 3 +- share/doc/ps1/07.ipctut/socketpair.c | 3 +- share/doc/ps1/07.ipctut/strchkread.c | 3 +- share/doc/ps1/07.ipctut/streamread.c | 3 +- share/doc/ps1/07.ipctut/streamwrite.c | 3 +- share/doc/ps1/07.ipctut/udgramread.c | 3 +- share/doc/ps1/07.ipctut/udgramsend.c | 3 +- share/doc/ps1/07.ipctut/ustreamread.c | 3 +- share/doc/ps1/07.ipctut/ustreamwrite.c | 3 +- share/doc/ps1/08.ipc/5.t | 2 +- share/doc/ps1/08.ipc/Makefile | 11 +- share/doc/ps1/13.rcs/Makefile | 18 +- share/doc/ps1/13.rcs/man/Makefile | 2 + share/doc/ps1/13.rcs/man/ci.1 | 5 +- share/doc/ps1/13.rcs/man/co.1 | 13 +- share/doc/ps1/13.rcs/man/ident.1 | 5 +- share/doc/ps1/13.rcs/man/merge.1 | 5 +- share/doc/ps1/13.rcs/man/rcs.1 | 5 +- share/doc/ps1/13.rcs/man/rcsdiff.1 | 5 +- share/doc/ps1/13.rcs/man/rcsfile.5 | 5 +- share/doc/ps1/13.rcs/man/rcsintro.1 | 1 + share/doc/ps1/13.rcs/man/rcsmerge.1 | 5 +- share/doc/ps1/13.rcs/man/rlog.1 | 5 +- share/doc/ps1/13.rcs/man/sccstorcs.1 | 1 + share/doc/ps1/13.rcs/rcs.ms | 11 +- share/doc/ps1/14.sccs/Makefile | 11 +- share/doc/ps1/18.curses/Makefile | 25 +- share/doc/ps1/18.curses/appen.B | 2 +- share/doc/ps1/18.curses/appen.C | 3 +- share/doc/ps1/18.curses/doc.III | 2 +- share/doc/ps1/18.curses/intro.0 | 3 +- share/doc/ps1/18.curses/intro.1 | 3 +- share/doc/ps1/18.curses/intro.2 | 3 +- share/doc/ps1/18.curses/intro.3 | 3 +- share/doc/ps1/18.curses/intro.4 | 3 +- share/doc/ps1/18.curses/intro.5 | 5 +- share/doc/ps1/18.curses/life.c | 3 +- share/doc/ps1/18.curses/macros | 2 +- share/doc/ps1/18.curses/twinkle1.c | 3 +- share/doc/ps1/18.curses/twinkle2.c | 3 +- share/doc/ps1/18.curses/win_st.c | 3 +- share/doc/ps1/Makefile | 19 +- share/doc/smm/01.setup/Makefile | 15 +- share/doc/smm/01.setup/common/4.t | 4 +- share/doc/smm/01.setup/common/renohints.t | 8 +- share/doc/smm/01.setup/tahoe/1.t | 2 +- share/doc/smm/01.setup/tahoe/Makefile | 28 +- share/doc/smm/01.setup/vax/Makefile | 38 +- share/doc/smm/02.config/6.t | 2 +- share/doc/smm/02.config/Makefile | 13 +- share/doc/smm/04.quotas/Makefile | 13 +- share/doc/smm/05.fsck/Makefile | 11 +- share/doc/smm/06.lpd/Makefile | 13 +- share/doc/smm/07.sendmailop/Makefile | 12 +- share/doc/smm/08.timedop/Makefile | 13 +- share/doc/smm/10.newsop/Makefile | 15 +- share/doc/smm/11.named/Makefile | 12 +- share/doc/smm/14.fastfs/Makefile | 13 +- share/doc/smm/15.net/7.t | 2 +- share/doc/smm/15.net/Makefile | 13 +- share/doc/smm/15.net/a.t | 2 +- share/doc/smm/16.sendmail/Makefile | 11 +- share/doc/smm/20.termdesc/Makefile | 11 +- share/doc/smm/22.timed/Makefile | 13 +- share/doc/smm/Makefile | 11 +- share/doc/usd/04.csh/Makefile | 11 +- share/doc/usd/04.csh/csh.1 | 3 +- share/doc/usd/04.csh/csh.2 | 3 +- share/doc/usd/04.csh/csh.3 | 3 +- share/doc/usd/04.csh/csh.4 | 3 +- share/doc/usd/04.csh/csh.a | 95 + share/doc/usd/07.Mail/Makefile | 11 +- share/doc/usd/09.newsread/Makefile | 15 +- share/doc/usd/10.etiq/Makefile | 11 +- share/doc/usd/11.notes/0.long | 2 +- share/doc/usd/11.notes/2.1 | 3 +- share/doc/usd/11.notes/2.2 | 3 +- share/doc/usd/11.notes/3.1 | 3 +- share/doc/usd/11.notes/3.2 | 3 +- share/doc/usd/11.notes/4.0 | 3 +- share/doc/usd/11.notes/4.1 | 3 +- share/doc/usd/11.notes/4.2 | 3 +- share/doc/usd/11.notes/4.3 | 3 +- share/doc/usd/11.notes/4.4 | 3 +- share/doc/usd/11.notes/4.5 | 3 +- share/doc/usd/11.notes/4.6 | 3 +- share/doc/usd/11.notes/4.7 | 3 +- share/doc/usd/11.notes/Makefile | 80 +- share/doc/usd/14.edit/Makefile | 17 +- share/doc/usd/14.edit/edittut.ms | 8 +- share/doc/usd/15.vi/Makefile | 22 +- share/doc/usd/15.vi/vi.chars | 2 +- share/doc/usd/15.vi/vi.in | 6 +- share/doc/usd/15.vi/vi.summary | 2 +- share/doc/usd/16.ex/Makefile | 18 +- share/doc/usd/21.msdiffs/Makefile | 13 +- share/doc/usd/22.memacros/Makefile | 11 +- share/doc/usd/23.meref/Makefile | 11 +- share/doc/usd/33.rogue/Makefile | 11 +- share/doc/usd/34.trek/Makefile | 11 +- share/doc/usd/Makefile | 14 +- share/man/Makefile | 12 +- share/man/makewhatis.sed | 63 - share/man/man1/Makefile | 2 +- share/man/man3/Makefile | 6 +- share/man/man3/fpgetround.3 | 138 + share/man/man4/Makefile | 8 +- share/man/man4/ddb.4 | 401 + share/man/man4/man4.i386/Makefile | 16 +- share/man/man4/man4.i386/com.4 | 102 + share/man/man4/man4.i386/keyboard.4 | 134 + share/man/man4/man4.i386/lpa.4 | 62 + share/man/man4/man4.i386/lpt.4 | 59 + share/man/man4/man4.i386/mse.4 | 33 + share/man/man4/man4.i386/npx.4 | 77 + share/man/man4/man4.i386/screen.4 | 157 + share/man/man4/man4.i386/sio.4 | 101 + share/man/man4/sd.4 | 192 + share/man/man4/st.4 | 244 + share/man/man4/termios.4 | 1407 +++ share/man/man5/Makefile | 11 +- share/man/man5/core.5 | 15 +- share/man/man7/Makefile | 7 +- share/man/man7/hier.7 | 2 +- share/man/man7/mdoc.samples.7 | 10 +- share/man/man8/Makefile | 2 +- share/man/man8/man8.i386/Makefile | 4 +- share/me/Makefile | 2 +- share/me/footnote.me | 2 +- share/me/me.7 | 4 +- share/misc/zipcodes | 2 +- share/mk/bsd.README | 34 +- share/mk/bsd.doc.mk | 70 +- share/mk/bsd.lib.mk | 128 +- share/mk/bsd.man.mk | 32 +- share/mk/bsd.own.mk | 2 +- share/mk/bsd.prog.mk | 62 +- share/mk/bsd.subdir.mk | 16 +- share/mk/sys.mk | 7 + share/syscons/Makefile | 5 + share/syscons/fonts/Makefile | 10 + share/syscons/fonts/alt-8x14 | Bin 0 -> 3584 bytes share/syscons/fonts/alt-8x16 | Bin 0 -> 4096 bytes share/syscons/fonts/alt-8x8 | Bin 0 -> 2048 bytes share/syscons/fonts/alt8x16.fnt | Bin 0 -> 4096 bytes share/syscons/fonts/altc-8x16 | Bin 0 -> 4096 bytes share/syscons/fonts/cp850-8x14 | Bin 0 -> 3584 bytes share/syscons/fonts/cp850-8x16 | Bin 0 -> 4096 bytes share/syscons/fonts/cp850-8x8 | Bin 0 -> 2048 bytes share/syscons/fonts/cp865-8x14 | Bin 0 -> 3584 bytes share/syscons/fonts/cp865-8x16 | Bin 0 -> 4096 bytes share/syscons/fonts/cp865-8x8 | Bin 0 -> 2048 bytes share/syscons/fonts/iso-8x14 | Bin 0 -> 3584 bytes share/syscons/fonts/iso-8x16 | Bin 0 -> 4096 bytes share/syscons/fonts/iso-8x8 | Bin 0 -> 2048 bytes share/syscons/fonts/koi8-8x14 | Bin 0 -> 3584 bytes share/syscons/fonts/koi8-8x16 | Bin 0 -> 4096 bytes share/syscons/fonts/koi8-8x8 | Bin 0 -> 2048 bytes share/syscons/fonts/koi8c-8x16 | Bin 0 -> 4096 bytes share/syscons/keymaps/Makefile | 25 + share/syscons/keymaps/danish.cp865 | 127 + share/syscons/keymaps/danish.iso | 127 + share/syscons/keymaps/fff | 127 + share/syscons/keymaps/german.cp850 | 127 + share/syscons/keymaps/german.iso | 127 + share/syscons/keymaps/mkkbdfil.c | 41 + share/syscons/keymaps/ru.koi8.map | Bin 0 -> 2562 bytes share/syscons/keymaps/rus.koi8.map | Bin 0 -> 2562 bytes share/syscons/keymaps/swedish.cp850 | 127 + share/syscons/keymaps/swedish.iso | 127 + share/syscons/keymaps/uk.cp850 | 127 + share/syscons/keymaps/uk.iso | 127 + share/syscons/keymaps/us.iso | 127 + share/syscons/scrnmaps/Makefile | 10 + share/syscons/scrnmaps/koi82alt.scr | Bin 0 -> 256 bytes share/termcap/Makefile | 16 +- share/termcap/reorder | 4 +- share/termcap/termcap.src | 40 +- share/tmac/Makefile | 9 +- share/tmac/doc-syms | 8 +- share/tmac/tmac.andoc | 19 +- share/zoneinfo/Makefile | 27 +- share/zoneinfo/datfiles/russia | 18 + sys/ROADMAP | 2 + sys/compile/.keep_me | 1 + sys/conf/files | 20 +- sys/conf/newvers.sh | 19 +- sys/conf/nfsswapvmunix.c | 3 +- sys/conf/param.c | 6 +- sys/ddb/db_access.c | 18 +- sys/ddb/db_access.h | 16 +- sys/ddb/db_aout.c | 19 +- sys/ddb/db_break.c | 39 +- sys/ddb/db_break.h | 24 +- sys/ddb/db_command.c | 53 +- sys/ddb/db_command.h | 18 +- sys/ddb/db_examine.c | 29 +- sys/ddb/db_expr.c | 27 +- sys/ddb/db_input.c | 22 +- sys/ddb/db_lex.c | 24 +- sys/ddb/db_lex.h | 20 +- sys/ddb/db_output.c | 49 +- sys/ddb/db_output.h | 16 +- sys/ddb/db_print.c | 39 +- sys/ddb/db_run.c | 40 +- sys/ddb/db_sym.c | 46 +- sys/ddb/db_sym.h | 20 +- sys/ddb/db_trap.c | 34 +- sys/ddb/db_variables.c | 25 +- sys/ddb/db_variables.h | 21 +- sys/ddb/db_watch.c | 28 +- sys/ddb/db_watch.h | 18 +- sys/ddb/db_write_cmd.c | 26 +- sys/i386/Makefile | 3 +- sys/i386/boot/Makefile | 90 +- sys/i386/boot/README.386BSD | 2 + sys/i386/boot/README.MACH | 14 +- sys/i386/boot/asm.S | 260 + sys/i386/boot/asm.h | 54 +- sys/i386/boot/asm.s | 270 - sys/i386/boot/bios.S | 329 + sys/i386/boot/bios.s | 326 - sys/i386/boot/boot.c | 99 +- sys/i386/boot/boot.h | 14 +- sys/i386/boot/boot2.S | 167 + sys/i386/boot/boot2.s | 73 - sys/i386/boot/disk.c | 58 +- sys/i386/boot/io.c | 41 +- sys/i386/boot/rmaouthdr | 10 +- sys/i386/boot/start.S | 291 + sys/i386/boot/start.s | 323 - sys/i386/boot/sys.c | 14 +- sys/i386/boot/table.c | 73 +- sys/i386/conf/AHATEST | 59 - sys/i386/conf/AHBTEST | 60 - sys/i386/conf/BTTEST | 59 - sys/i386/conf/CODRV | 68 - sys/i386/conf/DEVEL | 35 - sys/i386/conf/GENERICAH | 64 +- sys/i386/conf/GENERICBT | 87 + sys/i386/conf/GENERICISA | 95 - sys/i386/conf/LARGE | 46 - sys/i386/conf/LINT | 130 + sys/i386/conf/MINITERM | 76 - sys/i386/conf/Makefile.i386 | 65 +- sys/i386/conf/ODYSSEUS | 42 - sys/i386/conf/PHOENIX | 45 - sys/i386/conf/SMALL | 35 - sys/i386/conf/SPACE_HEATER | 101 - sys/i386/conf/SUMNER | 39 - sys/i386/conf/SYSCONS | 87 + sys/i386/conf/UHATEST | 59 - sys/i386/conf/devices.i386 | 10 +- sys/i386/conf/files.i386 | 65 +- sys/i386/doc/config_options.doc | 34 + sys/i386/doc/ed.relnotes | 173 + sys/i386/doc/sound.doc | 33 + sys/i386/doc/vm_layout.doc | 32 + sys/i386/i386/autoconf.c | 25 +- sys/i386/i386/conf.c | 160 +- sys/i386/i386/cons.c | 11 +- sys/i386/i386/cons.h | 3 +- sys/i386/i386/db_disasm.c | 29 +- sys/i386/i386/db_interface.c | 23 +- sys/i386/i386/db_trace.c | 32 +- sys/i386/i386/dkbad.c | 3 +- sys/i386/i386/genassym.c | 58 +- sys/i386/i386/in_cksum.c | 14 +- sys/i386/i386/locore.s | 1744 +-- sys/i386/i386/machdep.c | 358 +- sys/i386/i386/math_emu.h | 2 + sys/i386/i386/math_emulate.c | 24 +- sys/i386/i386/mem.c | 43 +- sys/i386/i386/microtime.s | 3 +- sys/i386/i386/ns_cksum.c | 206 + sys/i386/i386/pmap.c | 64 +- sys/i386/i386/swapgeneric.c | 3 +- sys/i386/i386/symbols.raw | 9 +- sys/i386/i386/sys_machdep.c | 14 +- sys/i386/i386/trap.c | 124 +- sys/i386/i386/vm_machdep.c | 47 +- sys/i386/include/ansi.h | 3 +- sys/i386/include/console.h | 205 + sys/i386/include/cpu.h | 33 +- sys/i386/include/cpufunc.h | 113 + sys/i386/include/cputypes.h | 49 + sys/i386/include/db_machdep.h | 36 +- sys/i386/include/dkio.h | 3 +- sys/i386/include/eflags.h | 17 +- sys/i386/include/endian.h | 11 +- sys/i386/include/float.h | 22 +- sys/i386/include/floatingpoint.h | 108 + sys/i386/include/frame.h | 19 +- sys/i386/include/ioctl_pc.h | 797 ++ sys/i386/include/limits.h | 3 +- sys/i386/include/mtpr.h | 4 + sys/i386/include/npx.h | 11 +- sys/i386/include/param.h | 108 +- sys/i386/include/pc/display.h | 2 + sys/i386/include/pc/msdos.h | 2 + sys/i386/include/pcb.h | 18 +- sys/i386/include/pio.h | 35 +- sys/i386/include/pmap.h | 65 +- sys/i386/include/proc.h | 3 +- sys/i386/include/psl.h | 3 +- sys/i386/include/pte.h | 3 +- sys/i386/include/reg.h | 3 +- sys/i386/include/segments.h | 3 +- sys/i386/include/soundcard.h | 737 ++ sys/i386/include/specialreg.h | 11 +- sys/i386/include/stdarg.h | 3 +- sys/i386/include/sysarch.h | 12 + sys/i386/include/trap.h | 3 +- sys/i386/include/tss.h | 3 +- sys/i386/include/types.h | 3 +- sys/i386/include/vmparam.h | 19 +- sys/i386/isa/TODO | 2 + sys/i386/isa/aha1542.c | 413 +- sys/i386/isa/aha1742.c | 318 +- sys/i386/isa/as.c | 1334 -- sys/i386/isa/asreg.h | 104 - sys/i386/isa/bt742a.c | 824 +- sys/i386/isa/clock.c | 23 +- sys/i386/isa/codrv/DOC/CHANGES.vak | 142 - sys/i386/isa/codrv/DOC/CO_HISTORY | 60 - sys/i386/isa/codrv/DOC/FEATURES | 16 - sys/i386/isa/codrv/DOC/README | 8 - sys/i386/isa/codrv/ETC/etc.ttys | 57 - sys/i386/isa/codrv/co_codrv1.c | 771 -- sys/i386/isa/codrv/co_cons.c | 441 - sys/i386/isa/codrv/co_hdr.h | 490 - sys/i386/isa/codrv/co_kbd.c | 1466 --- sys/i386/isa/codrv/co_mini.c | 359 - sys/i386/isa/codrv/co_pc3.c | 350 - sys/i386/isa/codrv/co_vga.c | 1297 -- sys/i386/isa/codrv/co_vty.c | 267 - sys/i386/isa/com.c | 21 +- sys/i386/isa/comreg.h | 3 +- sys/i386/isa/debug.h | 5 +- sys/i386/isa/fd.c | 226 +- sys/i386/isa/fdreg.h | 10 +- sys/i386/isa/ic/ds8390.h | 146 - sys/i386/isa/ic/i8042.h | 4 + sys/i386/isa/ic/i8237.h | 2 + sys/i386/isa/ic/i82586.h | 325 + sys/i386/isa/ic/nec765.h | 3 +- sys/i386/isa/ic/ns16450.h | 3 +- sys/i386/isa/ic/ns16550.h | 3 +- sys/i386/isa/icu.h | 10 +- sys/i386/isa/icu.s | 95 +- sys/i386/isa/if_ec.c | 777 -- sys/i386/isa/if_ec.h | 206 - sys/i386/isa/if_ed.c | 2375 ++++ sys/i386/isa/if_edreg.h | 903 ++ sys/i386/isa/if_ie.c | 1799 +++ sys/i386/isa/if_iereg.h | 24 + sys/i386/isa/if_is.c | 979 +- sys/i386/isa/if_isreg.h | 26 + sys/i386/isa/if_ne.c | 855 -- sys/i386/isa/if_nereg.h | 55 - sys/i386/isa/if_we.c | 1057 -- sys/i386/isa/if_wereg.h | 277 - sys/i386/isa/isa.c | 341 +- sys/i386/isa/isa.h | 12 +- sys/i386/isa/isa_device.h | 15 +- sys/i386/isa/iso8859.font | 1230 ++ sys/i386/isa/kbd.h | 10 +- sys/i386/isa/kbdtables.h | 858 ++ sys/i386/isa/lpa.c | 62 +- sys/i386/isa/lpt.c | 58 +- sys/i386/isa/lptreg.h | 5 +- sys/i386/isa/mcd.c | 1260 ++ sys/i386/isa/mcdreg.h | 150 + sys/i386/isa/mse.c | 493 + sys/i386/isa/npx.c | 36 +- sys/i386/isa/pccons.c | 71 +- sys/i386/isa/rtc.h | 3 +- sys/i386/isa/sio.c | 75 +- sys/i386/isa/sound/COPYING | 27 + sys/i386/isa/sound/HOWTO_MIDI | 51 + sys/i386/isa/sound/README | 17 + sys/i386/isa/sound/RELNOTES | 38 + sys/i386/isa/sound/RELNOTES.Linux | 191 + sys/i386/isa/sound/adlib_card.c | 32 + sys/i386/isa/sound/audio.c | 278 + sys/i386/isa/sound/debug.h | 29 + sys/i386/isa/sound/dev_table.c | 83 + sys/i386/isa/sound/dev_table.h | 229 + sys/i386/isa/sound/dmabuf.c | 773 ++ sys/i386/isa/sound/dsp.c | 250 + sys/i386/isa/sound/finetune.h | 28 + sys/i386/isa/sound/gus_card.c | 186 + sys/i386/isa/sound/gus_hw.h | 35 + sys/i386/isa/sound/gus_midi.c | 257 + sys/i386/isa/sound/gus_vol.c | 101 + sys/i386/isa/sound/gus_wave.c | 2523 ++++ sys/i386/isa/sound/gustest/Makefile | 16 + sys/i386/isa/sound/gustest/Readme | 67 + sys/i386/isa/sound/gustest/gmidi.h | 131 + sys/i386/isa/sound/gustest/gmod.c | 1589 +++ sys/i386/isa/sound/gustest/gpatinfo.c | 176 + sys/i386/isa/sound/gustest/gusload.c | 350 + sys/i386/isa/sound/gustest/midithru.c | 325 + sys/i386/isa/sound/gustest/pmtest.c | 411 + sys/i386/isa/sound/local.h | 17 + sys/i386/isa/sound/midi.c | 176 + sys/i386/isa/sound/midibuf.c | 105 + sys/i386/isa/sound/mpu401.c | 344 + sys/i386/isa/sound/opl3.c | 913 ++ sys/i386/isa/sound/opl3.h | 263 + sys/i386/isa/sound/os.h | 273 + sys/i386/isa/sound/pas.h | 249 + sys/i386/isa/sound/pas2_card.c | 343 + sys/i386/isa/sound/pas2_midi.c | 269 + sys/i386/isa/sound/pas2_mixer.c | 481 + sys/i386/isa/sound/pas2_pcm.c | 412 + sys/i386/isa/sound/patmgr.c | 239 + sys/i386/isa/sound/pro_midi.c | 155 + sys/i386/isa/sound/sb_card.c | 33 + sys/i386/isa/sound/sb_dsp.c | 1303 ++ sys/i386/isa/sound/sequencer.c | 1137 ++ sys/i386/isa/sound/sound_calls.h | 183 + sys/i386/isa/sound/sound_config.h | 182 + sys/i386/isa/sound/soundcard.c | 593 + sys/i386/isa/sound/soundcard.h | 737 ++ sys/i386/isa/sound/tuning.h | 29 + sys/i386/isa/sound/ulaw.h | 69 + sys/i386/isa/sound/ultrasound.h | 124 + sys/i386/isa/spkr.c | 3 + sys/i386/isa/syscons.c | 2415 ++++ sys/i386/isa/timerreg.h | 6 +- sys/i386/isa/ultra14f.c | 293 +- sys/i386/isa/vector.s | 32 +- sys/i386/isa/wd.c | 123 +- sys/i386/isa/wdreg.h | 3 +- sys/i386/isa/wt.c | 1691 ++- sys/i386/isa/wtreg.h | 134 +- sys/i386/stand/Makefile | 11 +- sys/i386/stand/as.c | 10 +- sys/i386/stand/asbootblk.c | 9 +- sys/i386/stand/bmap.c | 3 +- sys/i386/stand/boot.c | 7 +- sys/i386/stand/breadxx.c | 2 + sys/i386/stand/cga.c | 3 +- sys/i386/stand/fd.c | 3 +- sys/i386/stand/fdbootblk.c | 12 +- sys/i386/stand/fs.c | 9 +- sys/i386/stand/kbd.c | 3 +- sys/i386/stand/prf.c | 3 +- sys/i386/stand/saio.h | 7 +- sys/i386/stand/srt0.c | 3 +- sys/i386/stand/trimhd.c | 6 +- sys/i386/stand/wd.c | 10 +- sys/i386/stand/wdboot.x | Bin 512 -> 0 bytes sys/i386/stand/wdbootblk.c | 3 +- sys/isofs/TODO | 72 +- sys/isofs/iso.h | 13 +- sys/isofs/iso_rrip.h | 56 + sys/isofs/isofs_bmap.c | 4 + sys/isofs/isofs_lookup.c | 52 +- sys/isofs/isofs_node.c | 59 +- sys/isofs/isofs_node.h | 31 +- sys/isofs/isofs_rrip.c | 660 + sys/isofs/isofs_rrip.h | 124 + sys/isofs/isofs_util.c | 10 +- sys/isofs/isofs_vfsops.c | 37 +- sys/isofs/isofs_vnops.c | 179 +- sys/kern/Makefile | 50 + sys/kern/dead_vnops.c | 3 +- sys/kern/fifo_vnops.c | 16 +- sys/kern/init_main.c | 67 +- sys/kern/init_sysent.c | 13 +- sys/kern/kern__physio.c | 9 +- sys/kern/kern_acct.c | 177 +- sys/kern/kern_clock.c | 28 +- sys/kern/kern_descrip.c | 73 +- sys/kern/kern_execve | 307 - sys/kern/kern_execve.c | 197 +- sys/kern/kern_exit.c | 74 +- sys/kern/kern_fork.c | 3 +- sys/kern/kern_kinfo.c | 17 +- sys/kern/kern_ktrace.c | 19 +- sys/kern/kern_malloc.c | 8 +- sys/kern/kern_proc.c | 3 +- sys/kern/kern_prot.c | 107 +- sys/kern/kern_resource.c | 81 +- sys/kern/kern_sig.c | 138 +- sys/kern/kern_subr.c | 11 +- sys/kern/kern_synch.c | 10 +- sys/kern/kern_time.c | 54 +- sys/kern/kern_xxx.c | 98 +- sys/kern/makesyscalls.sh | 172 + sys/kern/spec_vnops.c | 10 +- sys/kern/subr_log.c | 16 +- sys/kern/subr_mcount.c | 28 +- sys/kern/subr_prf.c | 44 +- sys/kern/subr_rlist.c | 2 +- sys/kern/subr_xxx.c | 10 +- sys/kern/sys_generic.c | 90 +- sys/kern/sys_process.c | 132 +- sys/kern/sys_socket.c | 3 +- sys/kern/syscalls.c | 8 +- sys/kern/syscalls.master | 255 + sys/kern/sysv_shm.c | 67 +- sys/kern/tty.c | 54 +- sys/kern/tty_compat.c | 17 +- sys/kern/tty_conf.c | 15 +- sys/kern/tty_pty.c | 26 +- sys/kern/tty_ring.c | 11 +- sys/kern/tty_tb.c | 11 +- sys/kern/tty_tty.c | 3 +- sys/kern/uipc_domain.c | 3 +- sys/kern/uipc_mbuf.c | 11 +- sys/kern/uipc_proto.c | 3 +- sys/kern/uipc_socket.c | 58 +- sys/kern/uipc_socket2.c | 10 +- sys/kern/uipc_syscalls.c | 384 +- sys/kern/uipc_usrreq.c | 18 +- sys/kern/vfs__bio.c | 24 +- sys/kern/vfs_bio.old.c | 2 +- sys/kern/vfs_cache.c | 3 +- sys/kern/vfs_conf.c | 11 +- sys/kern/vfs_lookup.c | 10 +- sys/kern/vfs_subr.c | 3 +- sys/kern/vfs_syscalls.c | 432 +- sys/kern/vfs_vnops.c | 3 +- sys/net/af.c | 3 +- sys/net/af.h | 3 +- sys/net/bpf.c | 15 +- sys/net/bpf.h | 12 +- sys/net/bpf_filter.c | 17 +- sys/net/bpfdesc.h | 12 +- sys/net/if.c | 14 +- sys/net/if.h | 3 +- sys/net/if_arp.h | 3 +- sys/net/if_dl.h | 3 +- sys/net/if_ethersubr.c | 18 +- sys/net/if_llc.h | 3 +- sys/net/if_loop.c | 10 +- sys/net/if_ppp.c | 1441 +++ sys/net/if_ppp.h | 119 + sys/net/if_sl.c | 36 +- sys/net/if_slvar.h | 13 +- sys/net/if_tun.c | 55 +- sys/net/if_types.h | 4 +- sys/net/netisr.h | 3 +- sys/net/ppp.h | 41 + sys/net/radix.c | 4 +- sys/net/radix.h | 3 +- sys/net/raw_cb.c | 3 +- sys/net/raw_cb.h | 3 +- sys/net/raw_usrreq.c | 4 +- sys/net/route.c | 11 +- sys/net/route.h | 3 +- sys/net/rtsock.c | 4 +- sys/net/slcompress.c | 50 +- sys/net/slcompress.h | 19 +- sys/net/slip.h | 13 +- sys/netccitt/README.hdlc | 2 +- sys/netccitt/README.packet | 1 + sys/netccitt/ccitt_proto.c | 3 +- sys/netccitt/hd_debug.c | 3 +- sys/netccitt/hd_input.c | 16 +- sys/netccitt/hd_output.c | 3 +- sys/netccitt/hd_subr.c | 3 +- sys/netccitt/hd_timer.c | 3 +- sys/netccitt/hd_var.h | 3 +- sys/netccitt/hdlc.h | 3 +- sys/netccitt/if_x25subr.c | 3 +- sys/netccitt/pk.h | 3 +- sys/netccitt/pk_acct.c | 3 +- sys/netccitt/pk_debug.c | 3 +- sys/netccitt/pk_input.c | 3 +- sys/netccitt/pk_output.c | 3 +- sys/netccitt/pk_subr.c | 3 +- sys/netccitt/pk_timer.c | 3 +- sys/netccitt/pk_usrreq.c | 13 +- sys/netccitt/pk_var.h | 3 +- sys/netccitt/x25.h | 3 +- sys/netccitt/x25acct.h | 3 +- sys/netccitt/x25err.h | 3 +- sys/netinet/icmp_var.h | 3 +- sys/netinet/if_ether.c | 9 +- sys/netinet/if_ether.h | 3 +- sys/netinet/in.c | 3 +- sys/netinet/in.h | 8 +- sys/netinet/in_cksum.c | 3 +- sys/netinet/in_pcb.c | 3 +- sys/netinet/in_pcb.h | 3 +- sys/netinet/in_proto.c | 3 +- sys/netinet/in_systm.h | 3 +- sys/netinet/in_var.h | 3 +- sys/netinet/ip.h | 3 +- sys/netinet/ip_icmp.c | 3 +- sys/netinet/ip_icmp.h | 3 +- sys/netinet/ip_input.c | 3 +- sys/netinet/ip_output.c | 14 +- sys/netinet/ip_var.h | 3 +- sys/netinet/raw_ip.c | 11 +- sys/netinet/tcp.h | 3 +- sys/netinet/tcp_debug.c | 3 +- sys/netinet/tcp_debug.h | 3 +- sys/netinet/tcp_fsm.h | 3 +- sys/netinet/tcp_input.c | 3 +- sys/netinet/tcp_output.c | 3 +- sys/netinet/tcp_seq.h | 3 +- sys/netinet/tcp_subr.c | 3 +- sys/netinet/tcp_timer.c | 3 +- sys/netinet/tcp_timer.h | 3 +- sys/netinet/tcp_usrreq.c | 14 +- sys/netinet/tcp_var.h | 3 +- sys/netinet/tcpip.h | 3 +- sys/netinet/udp.h | 3 +- sys/netinet/udp_usrreq.c | 3 +- sys/netinet/udp_var.h | 3 +- sys/netiso/argo_debug.h | 7 +- sys/netiso/clnl.h | 3 +- sys/netiso/clnp.h | 5 +- sys/netiso/clnp_debug.c | 5 +- sys/netiso/clnp_er.c | 5 +- sys/netiso/clnp_frag.c | 6 +- sys/netiso/clnp_input.c | 5 +- sys/netiso/clnp_options.c | 5 +- sys/netiso/clnp_output.c | 5 +- sys/netiso/clnp_raw.c | 5 +- sys/netiso/clnp_stat.h | 5 +- sys/netiso/clnp_subr.c | 6 +- sys/netiso/clnp_timer.c | 5 +- sys/netiso/cltp_usrreq.c | 4 +- sys/netiso/cltp_var.h | 3 +- sys/netiso/cons.h | 7 +- sys/netiso/cons_pcb.h | 5 +- sys/netiso/eonvar.h | 3 +- sys/netiso/esis.c | 3 +- sys/netiso/esis.h | 7 +- sys/netiso/if_cons.c | 7 +- sys/netiso/if_eon.c | 7 +- sys/netiso/iso.c | 8 +- sys/netiso/iso.h | 5 +- sys/netiso/iso_chksum.c | 8 +- sys/netiso/iso_errno.h | 3 +- sys/netiso/iso_map.h | 3 +- sys/netiso/iso_pcb.c | 7 +- sys/netiso/iso_pcb.h | 5 +- sys/netiso/iso_proto.c | 8 +- sys/netiso/iso_snpac.c | 5 +- sys/netiso/iso_snpac.h | 3 +- sys/netiso/iso_var.h | 6 +- sys/netiso/tp.trans | 7 +- sys/netiso/tp_astring.c | 3 +- sys/netiso/tp_clnp.h | 7 +- sys/netiso/tp_cons.c | 6 +- sys/netiso/tp_driver.c | 11 +- sys/netiso/tp_emit.c | 8 +- sys/netiso/tp_events.h | 7 +- sys/netiso/tp_inet.c | 6 +- sys/netiso/tp_input.c | 6 +- sys/netiso/tp_ip.h | 7 +- sys/netiso/tp_iso.c | 7 +- sys/netiso/tp_meas.c | 7 +- sys/netiso/tp_meas.h | 3 +- sys/netiso/tp_output.c | 13 +- sys/netiso/tp_param.h | 8 +- sys/netiso/tp_pcb.c | 8 +- sys/netiso/tp_pcb.h | 8 +- sys/netiso/tp_seq.h | 7 +- sys/netiso/tp_stat.h | 7 +- sys/netiso/tp_states.h | 7 +- sys/netiso/tp_states.init | 7 +- sys/netiso/tp_subr.c | 8 +- sys/netiso/tp_subr2.c | 15 +- sys/netiso/tp_timer.c | 8 +- sys/netiso/tp_timer.h | 7 +- sys/netiso/tp_tpdu.h | 7 +- sys/netiso/tp_trace.c | 7 +- sys/netiso/tp_trace.h | 8 +- sys/netiso/tp_user.h | 7 +- sys/netiso/tp_usrreq.c | 7 +- sys/netiso/xebec/Makefile | 3 +- sys/netiso/xebec/debug.h | 6 +- sys/netiso/xebec/llparse.c | 7 +- sys/netiso/xebec/llparse.h | 6 +- sys/netiso/xebec/llscan.c | 7 +- sys/netiso/xebec/main.c | 7 +- sys/netiso/xebec/main.h | 6 +- sys/netiso/xebec/malloc.c | 7 +- sys/netiso/xebec/malloc.h | 6 +- sys/netiso/xebec/procs.c | 7 +- sys/netiso/xebec/procs.h | 6 +- sys/netiso/xebec/putdriver.c | 6 +- sys/netiso/xebec/sets.c | 7 +- sys/netiso/xebec/sets.h | 6 +- sys/netiso/xebec/test.trans | 5 +- sys/netiso/xebec/test_def.h | 4 + sys/netiso/xebec/xebec.bnf | 5 + sys/netiso/xebec/xebec.c | 6 +- sys/netiso/xebec/xebec.h | 6 +- sys/netns/idp.h | 3 +- sys/netns/idp_usrreq.c | 3 +- sys/netns/idp_var.h | 3 +- sys/netns/ns.c | 3 +- sys/netns/ns.h | 3 +- sys/netns/ns_error.c | 3 +- sys/netns/ns_error.h | 3 +- sys/netns/ns_if.h | 3 +- sys/netns/ns_input.c | 3 +- sys/netns/ns_ip.c | 3 +- sys/netns/ns_output.c | 3 +- sys/netns/ns_pcb.c | 3 +- sys/netns/ns_pcb.h | 3 +- sys/netns/ns_proto.c | 3 +- sys/netns/sp.h | 3 +- sys/netns/spidp.h | 3 +- sys/netns/spp_debug.c | 3 +- sys/netns/spp_debug.h | 3 +- sys/netns/spp_timer.h | 3 +- sys/netns/spp_usrreq.c | 3 +- sys/netns/spp_var.h | 3 +- sys/nfs/nfs.h | 8 +- sys/nfs/nfs_bio.c | 23 +- sys/nfs/nfs_node.c | 15 +- sys/nfs/nfs_serv.c | 38 +- sys/nfs/nfs_socket.c | 45 +- sys/nfs/nfs_srvcache.c | 19 +- sys/nfs/nfs_subs.c | 17 +- sys/nfs/nfs_syscalls.c | 42 +- sys/nfs/nfs_vfsops.c | 34 +- sys/nfs/nfs_vnops.c | 55 +- sys/nfs/nfscompress.h | 8 +- sys/nfs/nfsdiskless.h | 8 +- sys/nfs/nfsiom.h | 8 +- sys/nfs/nfsm_subs.h | 21 +- sys/nfs/nfsmount.h | 8 +- sys/nfs/nfsnode.h | 10 +- sys/nfs/nfsrvcache.h | 8 +- sys/nfs/nfsv2.h | 16 +- sys/nfs/rpcv2.h | 8 +- sys/nfs/xdr_subs.h | 8 +- sys/pcfs/bootsect.h | 3 +- sys/pcfs/bpb.h | 3 +- sys/pcfs/denode.h | 3 +- sys/pcfs/direntry.h | 3 +- sys/pcfs/fat.h | 3 +- sys/pcfs/pcfs_conv.c | 3 +- sys/pcfs/pcfs_denode.c | 10 +- sys/pcfs/pcfs_fat.c | 3 +- sys/pcfs/pcfs_lookup.c | 6 +- sys/pcfs/pcfs_vfsops.c | 12 +- sys/pcfs/pcfs_vnops.c | 3 +- sys/pcfs/pcfsmount.h | 3 +- sys/scsi/README | 55 +- sys/scsi/README.AHA1742 | 24 - sys/scsi/cd.c | 984 +- sys/scsi/ch.c | 198 +- sys/scsi/scsi_all.h | 224 +- sys/scsi/scsi_cd.h | 153 +- sys/scsi/scsi_changer.h | 53 +- sys/scsi/scsi_disk.h | 88 +- sys/scsi/scsi_generic.h | 63 + sys/scsi/scsi_tape.h | 171 +- sys/scsi/scsiconf.c | 186 +- sys/scsi/scsiconf.h | 36 +- sys/scsi/sd.c | 865 +- sys/scsi/sg.c | 760 ++ sys/scsi/st.c | 2742 +++-- sys/stand/cat.c | 3 +- sys/stand/copy.c | 3 +- sys/stand/dev.c | 3 +- sys/stand/ls.c | 3 +- sys/stand/printf.c | 3 +- sys/stand/saerrno.h | 3 +- sys/stand/saioctl.h | 3 +- sys/stand/stat.c | 3 +- sys/sys/acct.h | 3 +- sys/sys/asm.h | 3 +- sys/sys/buf.h | 11 +- sys/sys/callout.h | 3 +- sys/sys/cdefs.h | 52 +- sys/sys/cdio.h | 22 +- sys/sys/chio.h | 40 +- sys/sys/clist.h | 3 +- sys/sys/conf.h | 3 +- sys/sys/dir.h | 3 +- sys/sys/disklabel.h | 3 +- sys/sys/dkbad.h | 3 +- sys/sys/dkstat.h | 3 +- sys/sys/dmap.h | 3 +- sys/sys/domain.h | 3 +- sys/sys/errno.h | 3 +- sys/sys/exec.h | 4 +- sys/sys/fcntl.h | 3 +- sys/sys/fifo.h | 3 +- sys/sys/file.h | 3 +- sys/sys/filedesc.h | 3 +- sys/sys/filio.h | 2 + sys/sys/gprof.h | 3 +- sys/sys/ieeefp.h | 104 + sys/sys/ioctl.h | 39 +- sys/sys/ioctl_compat.h | 12 +- sys/sys/ioctl_pc.h | 793 -- sys/sys/ipc.h | 3 +- sys/sys/kernel.h | 5 +- sys/sys/kinfo.h | 3 +- sys/sys/kinfo_proc.h | 3 +- sys/sys/ktrace.h | 3 +- sys/sys/malloc.h | 15 +- sys/sys/map.h | 74 + sys/sys/mapmem.h | 6 +- sys/sys/mbuf.h | 3 +- sys/sys/mman.h | 3 +- sys/sys/mount.h | 13 +- sys/sys/msgbuf.h | 11 +- sys/sys/mtio.h | 28 +- sys/sys/namei.h | 3 +- sys/sys/param.h | 13 +- sys/sys/proc.h | 3 +- sys/sys/protosw.h | 3 +- sys/sys/ptrace.h | 3 +- sys/sys/reboot.h | 3 +- sys/sys/resource.h | 3 +- sys/sys/resourcevar.h | 3 +- sys/sys/rlist.h | 9 +- sys/sys/sgio.h | 150 + sys/sys/shm.h | 19 +- sys/sys/signal.h | 5 +- sys/sys/signalvar.h | 3 +- sys/sys/socket.h | 3 +- sys/sys/socketvar.h | 10 +- sys/sys/specdev.h | 3 +- sys/sys/spkr.h | 3 +- sys/sys/stat.h | 7 +- sys/sys/syscall.h | 5 +- sys/sys/syslimits.h | 3 +- sys/sys/syslog.h | 3 +- sys/sys/systm.h | 52 +- sys/sys/tablet.h | 3 +- sys/sys/termios.h | 13 +- sys/sys/time.h | 3 +- sys/sys/timeb.h | 3 +- sys/sys/times.h | 3 +- sys/sys/tprintf.h | 3 +- sys/sys/trace.h | 3 +- sys/sys/tty.h | 19 +- sys/sys/ttychars.h | 3 +- sys/sys/ttydefaults.h | 3 +- sys/sys/ttydev.h | 11 +- sys/sys/types.h | 3 +- sys/sys/ucred.h | 3 +- sys/sys/uio.h | 3 +- sys/sys/un.h | 3 +- sys/sys/unistd.h | 3 +- sys/sys/unpcb.h | 3 +- sys/sys/user.h | 3 +- sys/sys/utsname.h | 61 + sys/sys/vadvise.h | 3 +- sys/sys/vcmd.h | 3 +- sys/sys/vlimit.h | 3 +- sys/sys/vmmeter.h | 3 +- sys/sys/vnode.h | 13 +- sys/sys/vsio.h | 3 +- sys/sys/vtimes.h | 3 +- sys/sys/wait.h | 7 +- sys/ufs/dinode.h | 26 +- sys/ufs/dir.h | 3 +- sys/ufs/fs.h | 3 +- sys/ufs/inode.h | 6 +- sys/ufs/lockf.h | 5 +- sys/ufs/mfs_vfsops.c | 4 +- sys/ufs/mfs_vnops.c | 3 +- sys/ufs/mfsiom.h | 3 +- sys/ufs/mfsnode.h | 3 +- sys/ufs/quota.h | 3 +- sys/ufs/ufs_alloc.c | 11 +- sys/ufs/ufs_bmap.c | 3 +- sys/ufs/ufs_disksubr.c | 6 +- sys/ufs/ufs_inode.c | 12 +- sys/ufs/ufs_lockf.c | 139 +- sys/ufs/ufs_lookup.c | 6 +- sys/ufs/ufs_quota.c | 4 +- sys/ufs/ufs_subr.c | 3 +- sys/ufs/ufs_tables.c | 3 +- sys/ufs/ufs_vfsops.c | 6 +- sys/ufs/ufs_vnops.c | 109 +- sys/ufs/ufsmount.h | 3 +- sys/vm/device_pager.c | 7 +- sys/vm/device_pager.h | 3 +- sys/vm/kern_lock.c | 8 +- sys/vm/lock.h | 8 +- sys/vm/pmap.h | 8 +- sys/vm/queue.h | 6 +- sys/vm/swap_pager.c | 15 +- sys/vm/swap_pager.h | 3 +- sys/vm/vm.h | 3 +- sys/vm/vm_fault.c | 11 +- sys/vm/vm_glue.c | 35 +- sys/vm/vm_inherit.h | 8 +- sys/vm/vm_init.c | 8 +- sys/vm/vm_kern.c | 19 +- sys/vm/vm_kern.h | 8 +- sys/vm/vm_map.c | 19 +- sys/vm/vm_map.h | 14 +- sys/vm/vm_meter.c | 3 +- sys/vm/vm_mmap.c | 157 +- sys/vm/vm_object.c | 71 +- sys/vm/vm_object.h | 8 +- sys/vm/vm_page.c | 19 +- sys/vm/vm_page.h | 8 +- sys/vm/vm_pageout.c | 17 +- sys/vm/vm_pageout.h | 8 +- sys/vm/vm_pager.c | 18 +- sys/vm/vm_pager.h | 3 +- sys/vm/vm_param.h | 8 +- sys/vm/vm_prot.h | 8 +- sys/vm/vm_statistics.h | 8 +- sys/vm/vm_swap.c | 16 +- sys/vm/vm_unix.c | 22 +- sys/vm/vm_user.c | 65 +- sys/vm/vm_user.h | 8 +- sys/vm/vnode_pager.c | 3 +- sys/vm/vnode_pager.h | 3 +- usr.bin/Makefile | 42 +- usr.bin/ar/Makefile | 9 +- usr.bin/ar/{ar.5.5 => ar.5} | 0 usr.bin/bdes/bdes.c | 14 +- usr.bin/chat/Example | 5 + usr.bin/chat/Makefile | 7 + usr.bin/chat/README | 169 + usr.bin/chat/chat.c | 856 ++ usr.bin/chat/fix-cua | 16 + usr.bin/chat/ppp-off | 5 + usr.bin/chat/ppp-on | 37 + usr.bin/chat/unlock | 23 + usr.bin/checknr/checknr.1 | 2 +- usr.bin/cksum/Makefile | 2 +- usr.bin/cksum/cksum.1 | 58 +- usr.bin/cksum/cksum.c | 28 +- usr.bin/cksum/crc.c | 164 +- usr.bin/cksum/extern.h | 6 +- usr.bin/cksum/print.c | 21 +- usr.bin/cksum/sum1.c | 7 +- usr.bin/cksum/sum2.c | 7 +- usr.bin/col/col.c | 10 + usr.bin/compress/Makefile | 5 +- usr.bin/crontab/Makefile | 10 +- usr.bin/crontab/crontab.5 | 188 + usr.bin/crontab/crontab.5.5 | 188 - usr.bin/du/Makefile | 4 +- usr.bin/du/du.1 | 40 +- usr.bin/du/du.c | 98 +- usr.bin/elvis/Makefile | 4 +- usr.bin/elvis/blk.c | 26 +- usr.bin/elvis/cmd1.c | 353 +- usr.bin/elvis/cmd2.c | 108 +- usr.bin/elvis/config.h | 549 +- usr.bin/elvis/ctype.c | 2 + usr.bin/elvis/curses.c | 118 +- usr.bin/elvis/cut.c | 4 +- usr.bin/elvis/doc/Makedoc | 88 + usr.bin/elvis/doc/Makefile | 46 + usr.bin/elvis/doc/cflags.ms | 36 +- usr.bin/elvis/doc/col | 4 + usr.bin/elvis/doc/ctags.man | 84 + usr.bin/elvis/doc/differ.ms | 8 +- usr.bin/elvis/doc/elvis.man | 149 + usr.bin/elvis/doc/elvprsv.man | 54 + usr.bin/elvis/doc/elvrec.man | 47 + usr.bin/elvis/doc/environ.ms | 45 +- usr.bin/elvis/doc/ex.ms | 10 +- usr.bin/elvis/doc/fmt.man | 26 + usr.bin/elvis/doc/index.ms | 16 +- usr.bin/elvis/doc/internal.ms | 2 +- usr.bin/elvis/doc/makefile.ms | 255 + usr.bin/elvis/doc/options.ms | 38 +- usr.bin/elvis/doc/question.ms | 124 +- usr.bin/elvis/doc/ref.man | 88 + usr.bin/elvis/doc/regexp.ms | 72 +- usr.bin/elvis/doc/termcap.ms | 49 +- usr.bin/elvis/doc/title.ms | 17 +- usr.bin/elvis/doc/ver.ms | 4 +- usr.bin/elvis/doc/versions.ms | 115 +- usr.bin/elvis/doc/visual.ms | 88 +- usr.bin/elvis/elvis.1 | 52 + usr.bin/elvis/ex.c | 78 +- usr.bin/elvis/input.c | 67 +- usr.bin/elvis/main.c | 44 +- usr.bin/elvis/misc.c | 2 +- usr.bin/elvis/modify.c | 2 +- usr.bin/elvis/move1.c | 73 +- usr.bin/elvis/move2.c | 115 +- usr.bin/elvis/move3.c | 46 +- usr.bin/elvis/move4.c | 9 + usr.bin/elvis/move5.c | 2 +- usr.bin/elvis/opts.c | 26 +- usr.bin/elvis/prsvunix.c | 108 - usr.bin/elvis/recycle.c | 27 + usr.bin/elvis/redraw.c | 25 +- usr.bin/elvis/regexp.c | 117 +- usr.bin/elvis/regexp.h | 2 +- usr.bin/elvis/regsub.c | 43 +- usr.bin/elvis/system.c | 25 +- usr.bin/elvis/tio.c | 110 +- usr.bin/elvis/tmp.c | 100 +- usr.bin/elvis/unix.c | 24 +- usr.bin/elvis/vars.c | 1 + usr.bin/elvis/vcmd.c | 32 +- usr.bin/elvis/vi.c | 58 +- usr.bin/elvis/vi.h | 533 +- usr.bin/elvisrecover/Makefile | 2 +- usr.bin/elvisrecover/README | 31 - usr.bin/elvisrecover/elvisrecover.c | 7 +- usr.bin/env/Makefile | 1 - usr.bin/env/env.1 | 113 + usr.bin/env/env.c | 33 +- usr.bin/file/LEGAL.NOTICE | 23 +- usr.bin/file/MAINT | 33 + usr.bin/file/Makefile | 30 +- usr.bin/file/Makefile.ian | 85 - usr.bin/file/PORTING | 48 +- usr.bin/file/README | 68 +- usr.bin/file/apprentice.c | 325 +- usr.bin/file/ascmagic.c | 76 +- usr.bin/file/compress.c | 86 + usr.bin/file/file.1 | 173 +- usr.bin/file/file.c | 256 +- usr.bin/file/file.h | 85 +- usr.bin/file/fsmagic.c | 111 +- usr.bin/file/is_tar.c | 17 +- usr.bin/file/magdir/{aa => Header} | 0 usr.bin/file/magdir/Localstuff | 3 + usr.bin/file/magdir/aalocal | 3 - usr.bin/file/magdir/alliant | 15 + usr.bin/file/magdir/apl | 4 + usr.bin/file/magdir/ar | 66 + usr.bin/file/magdir/arc | 3 - usr.bin/file/magdir/archive | 11 - usr.bin/file/magdir/att3b | 34 + usr.bin/file/magdir/audio | 43 + usr.bin/file/magdir/blit | 16 + usr.bin/file/magdir/bsdi | 3 + usr.bin/file/magdir/c | 7 - usr.bin/file/magdir/c-lang | 3 + usr.bin/file/magdir/clipper | 63 + usr.bin/file/magdir/commands | 46 +- usr.bin/file/magdir/compress | 71 +- usr.bin/file/magdir/convex | 3 + usr.bin/file/magdir/cpio | 16 + usr.bin/file/magdir/diamond | 8 + usr.bin/file/magdir/dump | 43 + usr.bin/file/magdir/elf | 44 + usr.bin/file/magdir/encore | 20 + usr.bin/file/magdir/floppy.raw | 1 + usr.bin/file/magdir/frame | 26 +- usr.bin/file/magdir/freebsd | 5 + usr.bin/file/magdir/hp | 153 + usr.bin/file/magdir/ibm370 | 19 + usr.bin/file/magdir/ibm6000 | 17 + usr.bin/file/magdir/iff | 5 + usr.bin/file/magdir/images | 56 + usr.bin/file/magdir/intel | 51 +- usr.bin/file/magdir/interleaf | 7 + usr.bin/file/magdir/iris | 57 + usr.bin/file/magdir/ispell | 23 + usr.bin/file/magdir/lex | 3 + usr.bin/file/magdir/lif | 6 + usr.bin/file/magdir/linux | 9 + usr.bin/file/magdir/magic | 2 +- usr.bin/file/magdir/microsoft | 68 + usr.bin/file/magdir/mips | 8 + usr.bin/file/magdir/mirage | 3 + usr.bin/file/magdir/misc | 1 - usr.bin/file/magdir/misc2 | 2 - usr.bin/file/magdir/mkid | 7 + usr.bin/file/magdir/mmdf | 1 + usr.bin/file/magdir/motorola | 28 + usr.bin/file/magdir/ms-dos | 8 + usr.bin/file/magdir/ncr | 47 + usr.bin/file/magdir/netbsd | 18 + usr.bin/file/magdir/news | 5 + usr.bin/file/magdir/olda.out | 23 - usr.bin/file/magdir/pbm | 4 + usr.bin/file/magdir/pdp | 22 + usr.bin/file/magdir/pgp | 9 + usr.bin/file/magdir/pkgadd | 4 + usr.bin/file/magdir/plus5 | 16 + usr.bin/file/magdir/postscript | 11 +- usr.bin/file/magdir/psdbms | 6 + usr.bin/file/magdir/pyramid | 10 + usr.bin/file/magdir/rasterfile | 8 - usr.bin/file/magdir/sc | 2 + usr.bin/file/magdir/sendmail | 9 + usr.bin/file/magdir/sequent | 15 +- usr.bin/file/magdir/sgml | 6 + usr.bin/file/magdir/softquad | 27 +- usr.bin/file/magdir/sun | 89 +- usr.bin/file/magdir/sunraster | 12 + usr.bin/file/magdir/terminfo | 8 + usr.bin/file/magdir/tex | 20 + usr.bin/file/magdir/tower | 44 - usr.bin/file/magdir/troff | 6 + usr.bin/file/magdir/typeset | 2 + usr.bin/file/magdir/unknown | 35 + usr.bin/file/magdir/uuencode | 3 + usr.bin/file/magdir/varied.out | 27 +- usr.bin/file/magdir/vax | 33 + usr.bin/file/magdir/vax.byteswap | 17 - usr.bin/file/magdir/visx | 30 + usr.bin/file/magdir/x11 | 9 + usr.bin/file/magdir/xenix | 5 - usr.bin/file/magdir/zilog | 11 + usr.bin/file/magic | 313 - usr.bin/file/magic.5 | 117 +- usr.bin/file/names.h | 14 +- usr.bin/file/patchlevel.h | 34 + usr.bin/file/print.c | 155 +- usr.bin/file/softmagic.c | 234 +- usr.bin/file/tar.h | 2 + usr.bin/file/tst/Makefile | 10 +- usr.bin/find/Makefile | 2 +- usr.bin/find/extern.h | 9 +- usr.bin/find/find.1 | 39 +- usr.bin/find/find.c | 34 +- usr.bin/find/find.h | 35 +- usr.bin/find/function.c | 430 +- usr.bin/find/ls.c | 28 +- usr.bin/find/main.c | 41 +- usr.bin/find/misc.c | 53 +- usr.bin/find/operator.c | 41 +- usr.bin/find/option.c | 110 +- usr.bin/fstat/fstat.1 | 2 +- usr.bin/fstat/fstat.c | 102 +- usr.bin/ftp/ftp.c | 2 +- usr.bin/getopt/Makefile | 7 + usr.bin/getopt/README | 57 + usr.bin/getopt/getopt.1 | 103 + usr.bin/getopt/getopt.c | 30 + usr.bin/groups/groups.1 | 2 +- usr.bin/head/head.1 | 16 +- usr.bin/head/head.c | 40 +- usr.bin/hexdump/Makefile | 2 +- usr.bin/join/COPYING | 249 - usr.bin/join/Makefile | 2 +- usr.bin/join/getopt.c | 595 - usr.bin/join/getopt.h | 102 - usr.bin/join/getopt1.c | 160 - usr.bin/join/join.1 | 293 +- usr.bin/join/join.c | 1252 +- usr.bin/lastcomm/lastcomm.c | 3 +- usr.bin/lex/COPYING | 72 +- usr.bin/lex/Makefile | 16 +- usr.bin/lex/ccl.c | 51 +- usr.bin/lex/dfa.c | 56 +- usr.bin/lex/ecs.c | 51 +- usr.bin/lex/flexdef.h | 93 +- usr.bin/lex/flexdoc.1 | 218 +- usr.bin/lex/gen.c | 66 +- usr.bin/lex/initscan.c | 1117 +- usr.bin/lex/lex.1 | 1035 +- usr.bin/lex/lex.skel | 52 +- usr.bin/lex/lib/Makefile | 8 - usr.bin/lex/lib/ln.order | 1 - usr.bin/lex/main.c | 57 +- usr.bin/lex/misc.c | 130 +- usr.bin/lex/nfa.c | 52 +- usr.bin/lex/parse.y | 83 +- usr.bin/lex/scan.l | 67 +- usr.bin/lex/sym.c | 50 +- usr.bin/lex/tblcmp.c | 61 +- usr.bin/lex/yylex.c | 47 +- usr.bin/locate/locate/locate.c | 6 +- usr.bin/locate/locate/updatedb.sh | 79 + usr.bin/lock/Makefile | 5 + usr.bin/login/Makefile | 8 +- usr.bin/login/klogin.c | 187 + usr.bin/login/login.c | 168 +- usr.bin/lorder/Makefile | 7 +- usr.bin/m4/Makefile | 4 +- usr.bin/m4/TEST/ack.m4 | 38 - usr.bin/m4/TEST/hanoi.m4 | 38 - usr.bin/m4/TEST/hash.m4 | 38 - usr.bin/m4/TEST/sqroot.m4 | 38 - usr.bin/m4/TEST/string.m4 | 37 - usr.bin/m4/TEST/test.m4 | 37 - usr.bin/m4/expr.c | 1211 +- usr.bin/m4/extr.h | 191 +- usr.bin/m4/int2str.c | 58 + usr.bin/m4/look.c | 212 +- usr.bin/m4/m4.1 | 179 + usr.bin/m4/main.c | 1094 +- usr.bin/m4/mdef.h | 123 +- usr.bin/m4/misc.c | 556 +- usr.bin/m4/ourlims.h | 16 + usr.bin/m4/serv.c | 1142 +- usr.bin/mail/misc/mail.rc | 2 +- usr.bin/make/Makefile.dist | 26 +- usr.bin/make/main.c | 34 +- usr.bin/man/Makefile | 5 - usr.bin/man/apropos/Makefile | 8 - usr.bin/man/apropos/apropos.1 | 119 - usr.bin/man/apropos/apropos.c | 204 - usr.bin/man/man/Makefile | 9 - usr.bin/man/man/config.c | 244 - usr.bin/man/man/man.1 | 177 - usr.bin/man/man/man.c | 372 - usr.bin/man/man/man.conf.5 | 170 - usr.bin/man/man/pathnames.h | 38 - usr.bin/man/whatis/Makefile | 8 - usr.bin/man/whatis/whatis.1 | 105 - usr.bin/man/whatis/whatis.c | 210 - usr.bin/mkdep/Makefile | 2 +- usr.bin/mkdep/mkdep.gcc.sh | 4 +- usr.bin/mkdep/mkdep.sh | 2 +- usr.bin/mkfifo/mkfifo.1 | 34 +- usr.bin/mkfifo/mkfifo.c | 59 +- usr.bin/more/Makefile | 12 +- usr.bin/more/ch.c | 454 + usr.bin/more/command.c | 675 ++ usr.bin/more/decode.c | 207 + usr.bin/more/filename.c | 100 + usr.bin/more/help.c | 49 + usr.bin/more/input.c | 241 + usr.bin/more/less.h | 88 + usr.bin/more/line.c | 508 + usr.bin/more/linenum.c | 383 + usr.bin/more/main.c | 367 + usr.bin/more/more.1 | 597 +- usr.bin/more/more.c | 1870 --- usr.bin/more/more.help | 63 +- usr.bin/more/option.c | 128 + usr.bin/more/os.c | 283 + usr.bin/more/output.c | 253 + usr.bin/more/pathnames.h | 6 +- usr.bin/more/position.c | 163 + usr.bin/more/prim.c | 871 ++ usr.bin/more/screen.c | 557 + usr.bin/more/signal.c | 220 + usr.bin/more/tags.c | 205 + usr.bin/more/ttyin.c | 79 + usr.bin/msgs/msgs.1 | 16 +- usr.bin/msgs/msgs.c | 17 +- usr.bin/mt/mt.1 | 4 + usr.bin/mt/mt.c | 74 +- usr.bin/netstat/Makefile | 16 +- usr.bin/netstat/iso.c | 8 +- usr.bin/netstat/netstat.1 | 4 +- usr.bin/netstat/route.c | 28 +- usr.bin/netstat/unix.c | 2 + usr.bin/nfsstat/nfsstat.1 | 4 +- usr.bin/nohup/nohup.1 | 31 +- usr.bin/nohup/nohup.c | 55 +- usr.bin/pagesize/pagesize.1 | 2 - usr.bin/passwd/Makefile | 10 +- usr.bin/passwd/local_passwd.c | 10 +- usr.bin/printenv/Makefile | 1 - usr.bin/printenv/printenv.1 | 38 +- usr.bin/ranlib/Makefile | 10 +- usr.bin/ranlib/{ranlib.5.5 => ranlib.5} | 0 usr.bin/rdist/server.c | 7 +- usr.bin/renice/Makefile | 1 + usr.bin/rev/Makefile | 5 + usr.bin/rev/rev.1 | 48 + usr.bin/rev/rev.c | 133 + usr.bin/rlogin/Makefile | 6 + usr.bin/rlogin/rlogin.c | 4 +- usr.bin/rpcgen/Makefile | 7 + usr.bin/rpcgen/rpc_clntout.c | 130 + usr.bin/rpcgen/rpc_cout.c | 355 + usr.bin/rpcgen/rpc_hout.c | 374 + usr.bin/rpcgen/rpc_main.c | 438 + usr.bin/rpcgen/rpc_parse.c | 423 + usr.bin/rpcgen/rpc_parse.h | 159 + usr.bin/rpcgen/rpc_scan.c | 476 + usr.bin/rpcgen/rpc_scan.h | 103 + usr.bin/rpcgen/rpc_svcout.c | 276 + usr.bin/rpcgen/rpc_util.c | 439 + usr.bin/rpcgen/rpc_util.h | 113 + usr.bin/rpcgen/rpcgen.1 | 156 + usr.bin/rpcinfo/Makefile | 10 + usr.bin/rpcinfo/rpcinfo.8 | 166 + usr.bin/rpcinfo/rpcinfo.c | 666 + usr.bin/rsh/Makefile | 6 + usr.bin/rup/Makefile | 9 + usr.bin/rup/rup.1 | 94 + usr.bin/rup/rup.c | 223 + usr.bin/rusers/Makefile | 9 + usr.bin/rusers/rusers.1 | 93 + usr.bin/rusers/rusers.c | 249 + usr.bin/rwall/Makefile | 9 + usr.bin/rwall/rwall.1 | 79 + usr.bin/rwall/rwall.c | 174 + usr.bin/sccs/Makefile | 5 - usr.bin/sccs/pathnames.h | 51 - usr.bin/sccs/sccs.1 | 398 - usr.bin/sccs/sccs.c | 1623 --- usr.bin/sed/COPYING | 249 - usr.bin/sed/ChangeLog | 194 - usr.bin/sed/Makefile | 11 +- usr.bin/sed/Makefile.gnu | 84 - usr.bin/sed/POSIX | 205 + usr.bin/sed/README | 48 - usr.bin/sed/compile.c | 738 ++ usr.bin/sed/compile.test | 25 + usr.bin/sed/defs.h | 144 + usr.bin/sed/extern.h | 59 + usr.bin/sed/getopt.c | 594 - usr.bin/sed/getopt.h | 102 - usr.bin/sed/getopt1.c | 158 - usr.bin/sed/hanoi.sed | 102 + usr.bin/sed/main.c | 352 + usr.bin/sed/math.sed | 163 + usr.bin/sed/misc.c | 141 + usr.bin/sed/process.c | 614 + usr.bin/sed/regex.c | 2781 ----- usr.bin/sed/regex.h | 253 - usr.bin/sed/sed.1 | 513 + usr.bin/sed/sed.c | 1554 --- usr.bin/sed/sed.test | 545 + usr.bin/sed/utils.c | 359 - usr.bin/shar/Makefile | 5 +- usr.bin/showmount/Makefile | 1 + usr.bin/size/size.1 | 16 +- usr.bin/size/size.c | 126 +- usr.bin/strip/Makefile | 4 +- usr.bin/strip/strip.1 | 6 +- usr.bin/strip/strip.c | 157 +- usr.bin/su/Makefile | 7 + usr.bin/su/su.c | 4 - usr.bin/symorder/symorder.1 | 2 +- usr.bin/syscons/Makefile | 4 + usr.bin/syscons/syscons.1 | 96 + usr.bin/syscons/syscons.c | 500 + usr.bin/syscons/syscons.h | 6 + usr.bin/tail/COPYING | 249 - usr.bin/tail/Makefile | 4 +- usr.bin/tail/extern.h | 53 + usr.bin/tail/forward.c | 202 + usr.bin/tail/getopt.c | 595 - usr.bin/tail/getopt.h | 102 - usr.bin/tail/getopt1.c | 160 - usr.bin/tail/misc.c | 89 + usr.bin/tail/read.c | 195 + usr.bin/tail/reverse.c | 247 + usr.bin/tail/tail.1 | 231 +- usr.bin/tail/tail.c | 1121 +- usr.bin/telnet/Makefile | 43 +- usr.bin/telnet/commands.c | 5 +- usr.bin/tftp/main.c | 36 +- usr.bin/tftp/tftp.c | 18 +- usr.bin/tip/Makefile | 6 +- usr.bin/tip/aculib/courier.c | 2 +- usr.bin/tip/aculib/hayes.c | 46 +- usr.bin/tip/remcap.c | 4 +- usr.bin/tip/tip.1 | 45 +- usr.bin/tip/tipout.c | 2 +- usr.bin/tn3270/Makefile | 8 + usr.bin/tn3270/Makefile.inc | 6 + usr.bin/tn3270/api/makefile | 101 - usr.bin/tn3270/ascii/makefile | 141 - usr.bin/tn3270/ctlr/makefile | 156 - usr.bin/tn3270/general/makefile | 157 - usr.bin/tn3270/makefile | 294 - usr.bin/tn3270/map3270.0 | 396 - usr.bin/tn3270/mset.0 | 132 - usr.bin/tn3270/mset/Makefile | 47 + usr.bin/tn3270/{ => mset}/map3270.5 | 0 usr.bin/tn3270/{ => mset}/mset.1 | 0 usr.bin/tn3270/sys_curses/makefile | 133 - usr.bin/tn3270/telnet/Makefile | 87 - usr.bin/tn3270/tn3270.0 | 198 - usr.bin/tn3270/tn3270/Makefile | 115 + usr.bin/tn3270/{ => tn3270}/tn3270.1 | 0 usr.bin/tn3270/tools/Makefile | 5 + usr.bin/tn3270/tools/makefile | 183 - usr.bin/tn3270/tools/mkastods/Makefile | 12 + .../tn3270/tools/{ => mkastods}/mkastods.c | 0 usr.bin/tn3270/tools/mkastosc/Makefile | 12 + .../tn3270/tools/{ => mkastosc}/mkastosc.c | 0 usr.bin/tn3270/tools/mkdctype/Makefile | 12 + usr.bin/tn3270/tools/{ => mkdctype}/ectype.c | 0 usr.bin/tn3270/tools/{ => mkdctype}/ectype.h | 0 .../tn3270/tools/{ => mkdctype}/mkdctype.c | 0 usr.bin/tn3270/tools/mkdstoas/Makefile | 12 + .../tn3270/tools/{ => mkdstoas}/mkdstoas.c | 0 usr.bin/tn3270/tools/mkhits/Makefile | 12 + usr.bin/tn3270/tools/{ => mkhits}/dohits.c | 0 usr.bin/tn3270/tools/{ => mkhits}/dohits.h | 0 usr.bin/tn3270/tools/{ => mkhits}/mkhits.c | 0 usr.bin/touch/touch.1 | 138 +- usr.bin/touch/touch.c | 368 +- usr.bin/tr/Makefile | 3 +- usr.bin/tr/extern.h | 51 + usr.bin/tr/str.c | 351 + usr.bin/tr/tr.1 | 345 +- usr.bin/tr/tr.c | 337 +- usr.bin/tset/set.c | 2 +- usr.bin/tset/term.c | 45 +- usr.bin/tset/tset.1 | 73 +- usr.bin/tset/tset.c | 81 +- usr.bin/uname/Makefile | 6 + usr.bin/uname/uname.1 | 85 + usr.bin/uname/uname.c | 80 + usr.bin/uniq/uniq.1 | 152 +- usr.bin/uniq/uniq.c | 148 +- usr.bin/uuencode/Makefile | 4 +- usr.bin/vacation/vacation.c | 8 +- usr.bin/vgrind/Makefile | 4 +- usr.bin/vgrind/vgrind.1 | 4 +- usr.bin/vgrind/vgrind.sh | 8 +- usr.bin/vgrind/vgrindefs.c | 4 +- usr.bin/vmstat/Makefile | 2 +- usr.bin/vmstat/vmstat.8 | 4 +- usr.bin/vmstat/vmstat.c | 12 +- usr.bin/w/Makefile | 2 +- usr.bin/w/uptime.1 | 4 +- usr.bin/w/w.c | 2 + usr.bin/whereis/whereis.c | 120 +- usr.bin/which/Makefile | 2 +- usr.bin/window/Makefile | 2 +- usr.bin/window/wwinit.c | 6 +- usr.bin/xargs/xargs.1 | 177 +- usr.bin/xargs/xargs.c | 53 +- usr.bin/xinstall/Makefile | 4 +- usr.bin/yacc/Makefile | 2 +- usr.bin/yacc/NO_WARRANTY | 3 + usr.bin/yacc/README | 31 +- usr.bin/yacc/closure.c | 188 +- usr.bin/yacc/defs.h | 39 +- usr.bin/yacc/error.c | 40 - usr.bin/yacc/lalr.c | 40 - usr.bin/yacc/lr0.c | 39 - usr.bin/yacc/main.c | 59 +- usr.bin/yacc/mkpar.c | 40 +- usr.bin/yacc/output.c | 128 +- usr.bin/yacc/reader.c | 40 - usr.bin/yacc/skeleton.c | 116 +- usr.bin/yacc/symtab.c | 41 +- usr.bin/yacc/test/error.tab.c | 68 +- usr.bin/yacc/test/error.tab.h | 0 usr.bin/yacc/test/ftp.tab.c | 200 +- usr.bin/yacc/test/ftp.y | 1180 ++ usr.bin/yacc/verbose.c | 41 +- usr.bin/yacc/warshall.c | 62 +- usr.bin/yacc/yacc.1 | 229 +- usr.sbin/Makefile | 18 +- usr.sbin/XNSrouted/Makefile | 10 + usr.sbin/XNSrouted/XNSrouted.8 | 186 + usr.sbin/XNSrouted/af.c | 246 + usr.sbin/XNSrouted/af.h | 64 + usr.sbin/XNSrouted/defs.h | 88 + usr.sbin/XNSrouted/if.c | 147 + usr.sbin/XNSrouted/input.c | 189 + usr.sbin/XNSrouted/interface.h | 90 + usr.sbin/XNSrouted/main.c | 245 + usr.sbin/XNSrouted/output.c | 147 + usr.sbin/XNSrouted/protocol.h | 85 + usr.sbin/XNSrouted/startup.c | 218 + usr.sbin/XNSrouted/table.h | 99 + usr.sbin/XNSrouted/tables.c | 265 + usr.sbin/XNSrouted/timer.c | 139 + usr.sbin/XNSrouted/tools/query.c | 232 + usr.sbin/XNSrouted/trace.c | 313 + usr.sbin/XNSrouted/trace.h | 96 + usr.sbin/arp/Makefile | 2 +- usr.sbin/arp/arp.8 | 6 +- usr.sbin/bad144/Makefile | 5 +- usr.sbin/bad144/bad144.8 | 2 +- usr.sbin/bad144/bad144.c | 236 +- usr.sbin/chown/Makefile | 4 +- usr.sbin/chroot/Makefile | 2 +- usr.sbin/config/Makefile | 2 +- usr.sbin/config/config.8 | 42 +- usr.sbin/config/config.h | 2 + usr.sbin/config/config.y | 11 +- usr.sbin/config/lang.l | 1 + usr.sbin/config/main.c | 1 + usr.sbin/config/mkglue.c | 16 - usr.sbin/config/mkioconf.c | 16 +- usr.sbin/config/mkmakefile.c | 24 +- usr.sbin/config/mkswapconf.c | 7 - usr.sbin/dbsym/Makefile | 2 + usr.sbin/dbsym/dbsym.c | 82 +- usr.sbin/dev_mkdb/Makefile | 2 +- usr.sbin/dev_mkdb/dev_mkdb.c | 2 +- usr.sbin/diskpart/Makefile | 2 +- usr.sbin/diskpart/diskpart.c | 2 +- usr.sbin/edquota/Makefile | 2 +- usr.sbin/flcopy/Makefile | 2 +- usr.sbin/gettable/Makefile | 2 +- usr.sbin/gettable/gettable.c | 12 +- usr.sbin/htable/Makefile | 2 +- usr.sbin/inetd/Makefile | 9 +- usr.sbin/inetd/inetd.8 | 54 +- usr.sbin/inetd/inetd.c | 404 +- usr.sbin/inetd/pathnames.h | 3 +- usr.sbin/iostat/Makefile | 2 +- usr.sbin/iostat/iostat.8 | 4 +- usr.sbin/keymap/Makefile | 6 - usr.sbin/keymap/keymap/Makefile | 17 - usr.sbin/keymap/keymap/README.keycap | 214 - usr.sbin/keymap/keymap/keycap.5 | 208 - usr.sbin/keymap/keymap/keymap.8 | 120 - usr.sbin/keymap/keymap/keymap.c | 929 -- usr.sbin/keymap/keymap/pathnames.h | 20 - usr.sbin/keymap/lib/Makefile | 16 - usr.sbin/keymap/lib/co.4 | 262 - usr.sbin/keymap/lib/keycap.3 | 137 - usr.sbin/keymap/lib/keycap.c | 487 - usr.sbin/keymap/lib/pathnames.h | 31 - usr.sbin/keymap/lib/setfont.3 | 93 - usr.sbin/keymap/lib/setfont.c | 272 - usr.sbin/keymap/lib/vgafont.5 | 134 - usr.sbin/keymap/lib/whichcons.3 | 68 - usr.sbin/keymap/lib/whichcons.c | 95 - usr.sbin/keymap/lib/xc_fns.c | 73 - usr.sbin/kgmon/Makefile | 2 +- usr.sbin/kgmon/kgmon.8 | 4 +- usr.sbin/kvm_mkdb/Makefile | 5 +- usr.sbin/kvm_mkdb/kvm_mkdb.8 | 12 +- usr.sbin/kvm_mkdb/kvm_mkdb.c | 9 +- usr.sbin/kvm_mkdb/nlist.c | 14 +- usr.sbin/lpr/lpc/Makefile | 2 +- usr.sbin/lpr/lpd/Makefile | 2 +- usr.sbin/lpr/lpd/printjob.c | 17 +- usr.sbin/lpr/pac/Makefile | 2 +- usr.sbin/mtree/Makefile | 7 +- usr.sbin/mtree/compare.c | 192 +- usr.sbin/mtree/create.c | 311 +- usr.sbin/mtree/extern.h | 44 + usr.sbin/mtree/misc.c | 125 + usr.sbin/mtree/mtree.1 | 227 - usr.sbin/mtree/mtree.8 | 249 + usr.sbin/mtree/mtree.c | 74 +- usr.sbin/mtree/mtree.h | 49 +- usr.sbin/mtree/spec.c | 293 +- usr.sbin/mtree/verify.c | 73 +- usr.sbin/named/Makefile | 2 +- usr.sbin/named/tools/nslookup/Makefile | 2 +- usr.sbin/portmap/Makefile | 2 +- usr.sbin/pppstats/Makefile | 16 + usr.sbin/pppstats/pppstats.c | 346 + usr.sbin/pwd_mkdb/Makefile | 2 +- usr.sbin/pwd_mkdb/pwd_mkdb.8 | 5 +- usr.sbin/pwd_mkdb/pwd_mkdb.c | 41 +- usr.sbin/quotaon/Makefile | 2 +- usr.sbin/repquota/Makefile | 2 +- usr.sbin/rmt/Makefile | 2 +- usr.sbin/routed/Makefile | 2 +- usr.sbin/rwhod/Makefile | 2 +- usr.sbin/rwhod/rwhod.8 | 2 +- usr.sbin/rwhod/rwhod.c | 18 +- usr.sbin/sendmail/CHANGES-R5-R8 | 307 + usr.sbin/sendmail/Makefile | 21 +- usr.sbin/sendmail/READ_ME | 95 +- usr.sbin/sendmail/RELEASE_NOTES | 1310 ++ usr.sbin/sendmail/aux/addr.c | 162 - usr.sbin/sendmail/aux/arpa.c | 704 -- usr.sbin/sendmail/aux/mail-dm.c | 467 - usr.sbin/sendmail/aux/matchhdr.c | 125 - usr.sbin/sendmail/aux/newaliases.c | 240 - usr.sbin/sendmail/cf/KEY | 16 - usr.sbin/sendmail/cf/M4_KEY | 55 - usr.sbin/sendmail/cf/README | 1192 +- usr.sbin/sendmail/cf/cf/Makefile | 119 +- usr.sbin/sendmail/cf/cf/README | 32 - usr.sbin/sendmail/cf/cf/alpha.mc | 41 + usr.sbin/sendmail/cf/cf/auspex.mc | 42 + usr.sbin/sendmail/cf/cf/boat-anchor.mc | 47 + usr.sbin/sendmail/cf/cf/cad.cf | 627 - usr.sbin/sendmail/cf/cf/cad.mc | 18 - usr.sbin/sendmail/cf/cf/cadgroup.cf | 547 - usr.sbin/sendmail/cf/cf/cadgroup.mc | 15 - usr.sbin/sendmail/cf/cf/cc.cf | 547 - usr.sbin/sendmail/cf/cf/cc.mc | 15 - usr.sbin/sendmail/cf/cf/cchem.cf | 547 - usr.sbin/sendmail/cf/cf/cchem.mc | 15 - usr.sbin/sendmail/cf/cf/chez.mc | 44 + usr.sbin/sendmail/cf/cf/cogsci.cf | 610 - usr.sbin/sendmail/cf/cf/cogsci.mc | 53 +- usr.sbin/sendmail/cf/cf/cs-exposed.mc | 40 + usr.sbin/sendmail/cf/cf/cs-hidden.mc | 40 + usr.sbin/sendmail/cf/cf/freefall.mc | 47 + usr.sbin/sendmail/cf/cf/hpux-cs-exposed.mc | 41 + usr.sbin/sendmail/cf/cf/hpux-cs-hidden.mc | 41 + usr.sbin/sendmail/cf/cf/ic.cf | 603 - usr.sbin/sendmail/cf/cf/ic.mc | 18 - usr.sbin/sendmail/cf/cf/id.cf | 615 - usr.sbin/sendmail/cf/cf/id.mc | 17 - usr.sbin/sendmail/cf/cf/kappa.cf | 649 - usr.sbin/sendmail/cf/cf/knecht.mc | 44 + usr.sbin/sendmail/cf/cf/lamprey.cf | 311 - usr.sbin/sendmail/cf/cf/mail.cs.mc | 54 + usr.sbin/sendmail/cf/cf/mail.eecs.mc | 54 + usr.sbin/sendmail/cf/cf/okeeffe.cf | 606 - usr.sbin/sendmail/cf/cf/okeeffe.mc | 17 - usr.sbin/sendmail/cf/cf/proto.mc | 113 - usr.sbin/sendmail/cf/cf/python.mc | 52 + usr.sbin/sendmail/cf/cf/s2k.mc | 42 + usr.sbin/sendmail/cf/cf/sun-lamp.mc | 49 + .../sendmail/cf/cf/sunos3.5-cs-exposed.mc | 41 + usr.sbin/sendmail/cf/cf/sunos3.5-cs-hidden.mc | 41 + .../sendmail/cf/cf/sunos4.1-cs-exposed.mc | 41 + usr.sbin/sendmail/cf/cf/sunos4.1-cs-hidden.mc | 41 + usr.sbin/sendmail/cf/cf/tcpproto.cf | 517 - usr.sbin/sendmail/cf/cf/tcpproto.mc | 48 +- usr.sbin/sendmail/cf/cf/tcpuucpproto.cf | 572 - usr.sbin/sendmail/cf/cf/tcpuucpproto.mc | 15 - usr.sbin/sendmail/cf/cf/telemuse.cf | 649 - usr.sbin/sendmail/cf/cf/ucbarpa.cf | 607 - usr.sbin/sendmail/cf/cf/ucbarpa.mc | 55 +- usr.sbin/sendmail/cf/cf/ucbtcp.cf | 547 - usr.sbin/sendmail/cf/cf/ucbtcp.mc | 15 - usr.sbin/sendmail/cf/cf/ucbvax.cf | 837 -- usr.sbin/sendmail/cf/cf/ucbvax.mc | 252 +- usr.sbin/sendmail/cf/cf/udb.mc | 41 + usr.sbin/sendmail/cf/cf/uid.cf | 480 - usr.sbin/sendmail/cf/cf/uid.mc | 14 - .../sendmail/cf/cf/ultrix4.1-cs-exposed.mc | 41 + .../sendmail/cf/cf/ultrix4.1-cs-hidden.mc | 41 + usr.sbin/sendmail/cf/cf/uucpproto.cf | 484 - usr.sbin/sendmail/cf/cf/uucpproto.mc | 47 +- usr.sbin/sendmail/cf/cf/vangogh.mc | 43 + usr.sbin/sendmail/cf/domain/Berkeley.m4 | 40 + usr.sbin/sendmail/cf/domain/cs.exposed.m4 | 40 + usr.sbin/sendmail/cf/domain/cs.hidden.m4 | 38 + usr.sbin/sendmail/cf/domain/eecs.hidden.m4 | 38 + usr.sbin/sendmail/cf/domain/s2k.m4 | 38 + usr.sbin/sendmail/cf/feature/allmasquerade.m4 | 41 + .../sendmail/cf/feature/always_add_domain.m4 | 40 + usr.sbin/sendmail/cf/feature/bitdomain.m4 | 49 + usr.sbin/sendmail/cf/feature/domaintable.m4 | 40 + usr.sbin/sendmail/cf/feature/mailertable.m4 | 40 + usr.sbin/sendmail/cf/feature/nocanonify.m4 | 40 + usr.sbin/sendmail/cf/feature/notsticky.m4 | 40 + usr.sbin/sendmail/cf/feature/nouucp.m4 | 40 + usr.sbin/sendmail/cf/feature/redirect.m4 | 48 + usr.sbin/sendmail/cf/feature/use_cw_file.m4 | 46 + usr.sbin/sendmail/cf/feature/uucpdomain.m4 | 49 + usr.sbin/sendmail/cf/hack/cssubdomain.m4 | 44 + usr.sbin/sendmail/cf/m4/README | 6 - usr.sbin/sendmail/cf/m4/boilerplate.m4 | 110 - usr.sbin/sendmail/cf/m4/cf.m4 | 147 + usr.sbin/sendmail/cf/m4/fake_domains.m4 | 17 - usr.sbin/sendmail/cf/m4/localm.m4 | 26 - usr.sbin/sendmail/cf/m4/nsclasses.m4 | 20 - usr.sbin/sendmail/cf/m4/nsmacros.m4 | 45 - usr.sbin/sendmail/cf/m4/nstcpldm.m4 | 94 - usr.sbin/sendmail/cf/m4/nstcpm.m4 | 89 - usr.sbin/sendmail/cf/m4/postwriterule.m4 | 33 - usr.sbin/sendmail/cf/m4/prewriterule.m4 | 25 - usr.sbin/sendmail/cf/m4/proto.m4 | 656 + usr.sbin/sendmail/cf/m4/rule0.m4 | 49 - usr.sbin/sendmail/cf/m4/rule3.m4 | 68 - usr.sbin/sendmail/cf/m4/rule5.m4 | 28 - usr.sbin/sendmail/cf/m4/smtpuucpm.m4 | 34 - usr.sbin/sendmail/cf/m4/suucpm.m4 | 43 - usr.sbin/sendmail/cf/m4/uucpm.m4 | 42 - usr.sbin/sendmail/cf/m4/version.m4 | 48 +- usr.sbin/sendmail/cf/mailer/fax.m4 | 48 + usr.sbin/sendmail/cf/mailer/local.m4 | 60 + usr.sbin/sendmail/cf/mailer/smtp.m4 | 96 + usr.sbin/sendmail/cf/mailer/usenet.m4 | 47 + usr.sbin/sendmail/cf/mailer/uucp.m4 | 93 + usr.sbin/sendmail/cf/ostype/aix3.m4 | 40 + usr.sbin/sendmail/cf/ostype/bsd4.3.m4 | 38 + usr.sbin/sendmail/cf/ostype/bsd4.4.m4 | 41 + usr.sbin/sendmail/cf/ostype/hpux.m4 | 39 + usr.sbin/sendmail/cf/ostype/irix.m4 | 38 + usr.sbin/sendmail/cf/ostype/osf1.m4 | 41 + usr.sbin/sendmail/cf/ostype/riscos4.5.m4 | 37 + usr.sbin/sendmail/cf/ostype/solaris2.1.m4 | 17 + usr.sbin/sendmail/cf/ostype/sunos3.5.m4 | 37 + usr.sbin/sendmail/cf/ostype/sunos4.1.m4 | 37 + usr.sbin/sendmail/cf/ostype/ultrix4.1.m4 | 38 + usr.sbin/sendmail/cf/sh/makeinfo.sh | 39 + .../sendmail/cf/siteconfig/uucp.cogsci.m4 | 6 + .../sendmail/cf/siteconfig/uucp.old.arpa.m4 | 4 + .../sendmail/cf/siteconfig/uucp.ucbarpa.m4 | 1 + .../sendmail/cf/siteconfig/uucp.ucbvax.m4 | 73 + usr.sbin/sendmail/cf/sitedep/README | 19 - usr.sbin/sendmail/cf/sitedep/nicregistered.m4 | 38 - usr.sbin/sendmail/cf/sitedep/smtpuucp.cad.m4 | 11 - .../sendmail/cf/sitedep/smtpuucp.ucbvax.m4 | 35 - usr.sbin/sendmail/cf/sitedep/uucp.cad.m4 | 36 - usr.sbin/sendmail/cf/sitedep/uucp.cogsci.m4 | 14 - usr.sbin/sendmail/cf/sitedep/uucp.ic.m4 | 8 - usr.sbin/sendmail/cf/sitedep/uucp.id.m4 | 6 - usr.sbin/sendmail/cf/sitedep/uucp.okeeffe.m4 | 12 - usr.sbin/sendmail/cf/sitedep/uucp.proto.m4 | 8 - usr.sbin/sendmail/cf/sitedep/uucp.ucbarpa.m4 | 12 - usr.sbin/sendmail/cf/sitedep/uucp.ucbvax.m4 | 86 - usr.sbin/sendmail/contrib/README | 10 + usr.sbin/sendmail/contrib/bitdomain.c | 409 + usr.sbin/sendmail/contrib/expn.pl | 1239 ++ usr.sbin/sendmail/contrib/mmuegel | 2073 ++++ usr.sbin/sendmail/contrib/rcpt-streaming | 297 + usr.sbin/sendmail/contrib/xla/README | 207 + usr.sbin/sendmail/contrib/xla/xla.c | 528 + usr.sbin/sendmail/doc/intro/intro.me | 1478 +++ usr.sbin/sendmail/doc/intro/intro.ps | 1295 ++ usr.sbin/sendmail/doc/op/op.me | 6567 ++++++++++ usr.sbin/sendmail/doc/op/op.ps | 5173 ++++++++ usr.sbin/sendmail/doc/refs | 26 - usr.sbin/sendmail/doc/rfc819.lpr | 1044 -- usr.sbin/sendmail/doc/rfc821.lpr | 4050 ------- usr.sbin/sendmail/doc/rfc822.lpr | 2901 ----- usr.sbin/sendmail/doc/spell.good | 219 - usr.sbin/sendmail/doc/usenix.abs | 20 - usr.sbin/sendmail/doc/usenix.me | 1076 -- usr.sbin/sendmail/doc/usenix/usenix.me | 1076 ++ usr.sbin/sendmail/doc/usenix/usenix.ps | 1004 ++ usr.sbin/sendmail/mailstats/Makefile | 2 +- usr.sbin/sendmail/mailstats/mailstats.c | 145 +- usr.sbin/sendmail/mailstats/pathnames.h | 36 - usr.sbin/sendmail/makemap/Makefile | 8 + usr.sbin/sendmail/makemap/makemap.8 | 127 + usr.sbin/sendmail/makemap/makemap.c | 336 + usr.sbin/sendmail/praliases/Makefile | 3 +- usr.sbin/sendmail/praliases/praliases.c | 94 +- usr.sbin/sendmail/rmail/Makefile | 6 + usr.sbin/sendmail/rmail/rmail.8 | 71 + usr.sbin/sendmail/rmail/rmail.c | 359 + usr.sbin/sendmail/src/Makefile | 39 +- usr.sbin/sendmail/src/Makefile.AIX | 99 + usr.sbin/sendmail/src/Makefile.HPUX | 107 + usr.sbin/sendmail/src/Makefile.IRIX | 101 + usr.sbin/sendmail/src/Makefile.OSF1 | 99 + usr.sbin/sendmail/src/Makefile.Solaris | 103 + usr.sbin/sendmail/src/Makefile.SunOS | 36 + usr.sbin/sendmail/src/Makefile.ULTRIX | 34 + usr.sbin/sendmail/src/Makefile.Utah | 42 + usr.sbin/sendmail/src/Makefile.dist | 99 + usr.sbin/sendmail/src/READ_ME | 174 +- usr.sbin/sendmail/src/TODO | 48 - usr.sbin/sendmail/src/TRACEFLAGS | 59 + usr.sbin/sendmail/src/alias.c | 800 +- usr.sbin/sendmail/src/aliases | 52 + usr.sbin/sendmail/src/aliases.5 | 22 +- usr.sbin/sendmail/src/arpadate.c | 18 +- usr.sbin/sendmail/src/cdefs.h | 94 + usr.sbin/sendmail/src/clock.c | 25 +- usr.sbin/sendmail/src/collect.c | 208 +- usr.sbin/sendmail/src/conf.c | 1016 +- usr.sbin/sendmail/src/conf.h | 306 +- usr.sbin/sendmail/src/convtime.c | 15 +- usr.sbin/sendmail/src/daemon.c | 1122 +- usr.sbin/sendmail/src/deliver.c | 2129 ++-- usr.sbin/sendmail/src/domain.c | 579 +- usr.sbin/sendmail/src/envelope.c | 460 +- usr.sbin/sendmail/src/err.c | 202 +- usr.sbin/sendmail/src/headers.c | 625 +- usr.sbin/sendmail/src/macro.c | 34 +- usr.sbin/sendmail/src/mailstats.h | 6 +- usr.sbin/sendmail/src/main.c | 852 +- usr.sbin/sendmail/src/map.c | 1192 ++ usr.sbin/sendmail/src/mci.c | 319 + usr.sbin/sendmail/src/newaliases.1 | 8 +- usr.sbin/sendmail/src/parseaddr.c | 1118 +- usr.sbin/sendmail/src/pathnames.h | 23 +- usr.sbin/sendmail/src/queue.c | 956 +- usr.sbin/sendmail/src/readcf.c | 882 +- usr.sbin/sendmail/src/recipient.c | 500 +- usr.sbin/sendmail/src/savemail.c | 424 +- usr.sbin/sendmail/src/sendmail.8 | 102 +- usr.sbin/sendmail/src/sendmail.cf | 634 - usr.sbin/sendmail/src/sendmail.h | 529 +- usr.sbin/sendmail/src/sendmail.hf | 29 +- usr.sbin/sendmail/src/srvrsmtp.c | 617 +- usr.sbin/sendmail/src/stab.c | 37 +- usr.sbin/sendmail/src/stats.c | 19 +- usr.sbin/sendmail/src/sysexits.c | 73 +- usr.sbin/sendmail/src/sysexits.h | 118 + usr.sbin/sendmail/src/trace.c | 6 +- usr.sbin/sendmail/src/udb.c | 723 ++ usr.sbin/sendmail/src/useful.h | 28 +- usr.sbin/sendmail/src/usersmtp.c | 713 +- usr.sbin/sendmail/src/util.c | 544 +- usr.sbin/sendmail/src/version.c | 8 +- usr.sbin/sliplogin/Makefile | 2 +- usr.sbin/swapinfo/Makefile | 4 +- usr.sbin/swapinfo/swapinfo.c | 58 +- usr.sbin/syslogd/Makefile | 4 +- usr.sbin/timed/timed/Makefile | 6 +- usr.sbin/timed/timedc/Makefile | 4 +- usr.sbin/traceroute/Makefile | 2 +- usr.sbin/trpt/Makefile | 2 +- usr.sbin/trpt/trpt.8 | 2 +- usr.sbin/trsp/Makefile | 2 +- usr.sbin/trsp/trsp.8 | 2 +- usr.sbin/update/Makefile | 2 +- usr.sbin/vipw/Makefile | 2 +- usr.sbin/vipw/vipw.8 | 4 +- 4146 files changed, 616420 insertions(+), 360283 deletions(-) delete mode 100644 BUGNFIX.FORM delete mode 100644 CAREWARE.INFO rename CONTRIB.LIST => CONTRIB.386BSD (100%) create mode 100644 CONTRIB.FreeBSD create mode 100644 FixLinks delete mode 100644 REGISTRATION create mode 100644 RELNOTES.FreeBSD delete mode 100644 SOFTWARE.FORM create mode 100644 bin/cp/extern.h create mode 100644 bin/dd/args.c create mode 100644 bin/dd/conv.c create mode 100644 bin/dd/conv_tab.c create mode 100644 bin/dd/dd.1 create mode 100644 bin/dd/dd.h create mode 100644 bin/dd/extern.h create mode 100644 bin/dd/misc.c create mode 100644 bin/dd/position.c create mode 100644 bin/df/getbsize.c create mode 100644 bin/domainname/Makefile create mode 100644 bin/domainname/domainname.1 create mode 100644 bin/domainname/domainname.c rename bin/ed/test/{!1.d => bang1.d} (100%) rename bin/ed/test/{!1.err => bang1.err} (100%) rename bin/ed/test/{!1.r => bang1.r} (100%) rename bin/ed/test/{!1.t => bang1.t} (100%) rename bin/ed/test/{!2.d => bang2.d} (100%) rename bin/ed/test/{!2.err => bang2.err} (100%) rename bin/ed/test/{!2.r => bang2.r} (100%) rename bin/ed/test/{!2.t => bang2.t} (100%) create mode 100644 bin/ed/test/g3.d create mode 100644 bin/ed/test/g3.r create mode 100644 bin/ed/test/g3.t create mode 100644 bin/ed/test/g4.d create mode 100644 bin/ed/test/g4.r create mode 100644 bin/ed/test/g4.t create mode 100644 bin/ed/test/nl.err create mode 100644 bin/ed/test/nl1.d create mode 100644 bin/ed/test/nl1.r create mode 100644 bin/ed/test/nl1.t create mode 100644 bin/ed/test/nl2.d create mode 100644 bin/ed/test/nl2.r create mode 100644 bin/ed/test/nl2.t delete mode 100644 bin/expr/TODO create mode 100644 bin/expr/expr.1 create mode 100644 bin/test/TEST.csh create mode 100644 bin/test/binary_op create mode 100644 bin/test/mkops create mode 100644 bin/test/operators.c create mode 100644 bin/test/operators.h create mode 100644 bin/test/test.1 create mode 100644 bin/test/unary_op create mode 100644 etc/etc.i386/floppy.install_notes create mode 100644 etc/etc.i386/inst1.install create mode 100644 etc/etc.i386/inst1.profile create mode 100644 etc/etc.i386/inst2.install create mode 100644 etc/etc.i386/inst2.profile create mode 100644 etc/etc.i386/install_notes create mode 100644 etc/etc.i386/kc.profile create mode 100644 etc/host.conf delete mode 100644 etc/man.conf create mode 100644 etc/mtree/BSD.local.dist create mode 100644 etc/myname create mode 100644 etc/rpc delete mode 100644 games/hack/hack.onames.h create mode 100644 gnu/COPYING.LIB create mode 100644 gnu/Makefile.inc delete mode 100644 gnu/games/chess/Xchess/xchess create mode 100644 gnu/games/chess/chess.6 delete mode 100644 gnu/lib/libg++/ChangeLog delete mode 100644 gnu/lib/libg++/Makefile.gnu delete mode 100644 gnu/lib/libg++/README.gnu delete mode 100644 gnu/lib/libg++/g++-include/ACG.h create mode 100644 gnu/lib/libg++/g++-include/AVLMap.ccP create mode 100644 gnu/lib/libg++/g++-include/AVLMap.hP create mode 100644 gnu/lib/libg++/g++-include/AVLSet.ccP create mode 100644 gnu/lib/libg++/g++-include/AVLSet.hP create mode 100644 gnu/lib/libg++/g++-include/AVec.ccP create mode 100644 gnu/lib/libg++/g++-include/AVec.hP delete mode 100644 gnu/lib/libg++/g++-include/AllocRing.h create mode 100644 gnu/lib/libg++/g++-include/BSTSet.ccP create mode 100644 gnu/lib/libg++/g++-include/BSTSet.hP create mode 100644 gnu/lib/libg++/g++-include/Bag.ccP create mode 100644 gnu/lib/libg++/g++-include/Bag.hP delete mode 100644 gnu/lib/libg++/g++-include/Binomial.h delete mode 100644 gnu/lib/libg++/g++-include/BitSet.h delete mode 100644 gnu/lib/libg++/g++-include/BitString.h create mode 100644 gnu/lib/libg++/g++-include/CHBag.ccP create mode 100644 gnu/lib/libg++/g++-include/CHBag.hP create mode 100644 gnu/lib/libg++/g++-include/CHMap.ccP create mode 100644 gnu/lib/libg++/g++-include/CHMap.hP create mode 100644 gnu/lib/libg++/g++-include/CHNode.ccP create mode 100644 gnu/lib/libg++/g++-include/CHNode.hP create mode 100644 gnu/lib/libg++/g++-include/CHSet.ccP create mode 100644 gnu/lib/libg++/g++-include/CHSet.hP delete mode 100644 gnu/lib/libg++/g++-include/Complex.h delete mode 100644 gnu/lib/libg++/g++-include/CursesW.h create mode 100644 gnu/lib/libg++/g++-include/DLDeque.ccP create mode 100644 gnu/lib/libg++/g++-include/DLDeque.hP create mode 100644 gnu/lib/libg++/g++-include/DLList.ccP create mode 100644 gnu/lib/libg++/g++-include/DLList.hP create mode 100644 gnu/lib/libg++/g++-include/Deque.ccP create mode 100644 gnu/lib/libg++/g++-include/Deque.hP delete mode 100644 gnu/lib/libg++/g++-include/DiscUnif.h delete mode 100644 gnu/lib/libg++/g++-include/Erlang.h create mode 100644 gnu/lib/libg++/g++-include/FPQueue.ccP create mode 100644 gnu/lib/libg++/g++-include/FPQueue.hP create mode 100644 gnu/lib/libg++/g++-include/FPStack.ccP create mode 100644 gnu/lib/libg++/g++-include/FPStack.hP create mode 100644 gnu/lib/libg++/g++-include/FPlex.ccP create mode 100644 gnu/lib/libg++/g++-include/FPlex.hP delete mode 100644 gnu/lib/libg++/g++-include/File.h delete mode 100644 gnu/lib/libg++/g++-include/Filebuf.h delete mode 100644 gnu/lib/libg++/g++-include/Fix.h delete mode 100644 gnu/lib/libg++/g++-include/Fix16.h delete mode 100644 gnu/lib/libg++/g++-include/Fix24.h delete mode 100644 gnu/lib/libg++/g++-include/Fmodes.h delete mode 100644 gnu/lib/libg++/g++-include/Geom.h delete mode 100644 gnu/lib/libg++/g++-include/GetOpt.h delete mode 100644 gnu/lib/libg++/g++-include/HypGeom.h delete mode 100644 gnu/lib/libg++/g++-include/Incremental.h delete mode 100644 gnu/lib/libg++/g++-include/Integer.h create mode 100644 gnu/lib/libg++/g++-include/List.ccP create mode 100644 gnu/lib/libg++/g++-include/List.hP delete mode 100644 gnu/lib/libg++/g++-include/LogNorm.h delete mode 100644 gnu/lib/libg++/g++-include/MLCG.h create mode 100644 gnu/lib/libg++/g++-include/MPlex.ccP create mode 100644 gnu/lib/libg++/g++-include/MPlex.hP delete mode 100644 gnu/lib/libg++/g++-include/Makefile create mode 100644 gnu/lib/libg++/g++-include/Map.ccP create mode 100644 gnu/lib/libg++/g++-include/Map.hP delete mode 100644 gnu/lib/libg++/g++-include/NegExp.h delete mode 100644 gnu/lib/libg++/g++-include/Normal.h create mode 100644 gnu/lib/libg++/g++-include/OSLBag.ccP create mode 100644 gnu/lib/libg++/g++-include/OSLBag.hP create mode 100644 gnu/lib/libg++/g++-include/OSLSet.ccP create mode 100644 gnu/lib/libg++/g++-include/OSLSet.hP create mode 100644 gnu/lib/libg++/g++-include/OXPBag.ccP create mode 100644 gnu/lib/libg++/g++-include/OXPBag.hP create mode 100644 gnu/lib/libg++/g++-include/OXPSet.ccP create mode 100644 gnu/lib/libg++/g++-include/OXPSet.hP delete mode 100644 gnu/lib/libg++/g++-include/Obstack.h create mode 100644 gnu/lib/libg++/g++-include/PHPQ.ccP create mode 100644 gnu/lib/libg++/g++-include/PHPQ.hP create mode 100644 gnu/lib/libg++/g++-include/PQ.ccP create mode 100644 gnu/lib/libg++/g++-include/PQ.hP create mode 100644 gnu/lib/libg++/g++-include/PSList.hP create mode 100644 gnu/lib/libg++/g++-include/PVec.hP delete mode 100644 gnu/lib/libg++/g++-include/Pix.h create mode 100644 gnu/lib/libg++/g++-include/Plex.ccP create mode 100644 gnu/lib/libg++/g++-include/Plex.hP delete mode 100644 gnu/lib/libg++/g++-include/PlotFile.h delete mode 100644 gnu/lib/libg++/g++-include/Poisson.h create mode 100644 gnu/lib/libg++/g++-include/Queue.ccP create mode 100644 gnu/lib/libg++/g++-include/Queue.hP create mode 100644 gnu/lib/libg++/g++-include/RAVLMap.ccP create mode 100644 gnu/lib/libg++/g++-include/RAVLMap.hP delete mode 100644 gnu/lib/libg++/g++-include/RNG.h create mode 100644 gnu/lib/libg++/g++-include/RPlex.ccP create mode 100644 gnu/lib/libg++/g++-include/RPlex.hP delete mode 100644 gnu/lib/libg++/g++-include/Random.h delete mode 100644 gnu/lib/libg++/g++-include/Rational.h delete mode 100644 gnu/lib/libg++/g++-include/Regex.h delete mode 100644 gnu/lib/libg++/g++-include/RndInt.h delete mode 100644 gnu/lib/libg++/g++-include/SFile.h create mode 100644 gnu/lib/libg++/g++-include/SLBag.ccP create mode 100644 gnu/lib/libg++/g++-include/SLBag.hP create mode 100644 gnu/lib/libg++/g++-include/SLList.ccP create mode 100644 gnu/lib/libg++/g++-include/SLList.hP create mode 100644 gnu/lib/libg++/g++-include/SLQueue.ccP create mode 100644 gnu/lib/libg++/g++-include/SLQueue.hP create mode 100644 gnu/lib/libg++/g++-include/SLSet.ccP create mode 100644 gnu/lib/libg++/g++-include/SLSet.hP create mode 100644 gnu/lib/libg++/g++-include/SLStack.ccP create mode 100644 gnu/lib/libg++/g++-include/SLStack.hP create mode 100644 gnu/lib/libg++/g++-include/Set.ccP create mode 100644 gnu/lib/libg++/g++-include/Set.hP create mode 100644 gnu/lib/libg++/g++-include/SkipBag.ccP create mode 100644 gnu/lib/libg++/g++-include/SkipBag.hP create mode 100644 gnu/lib/libg++/g++-include/SkipMap.ccP create mode 100644 gnu/lib/libg++/g++-include/SkipMap.hP create mode 100644 gnu/lib/libg++/g++-include/SkipSet.ccP create mode 100644 gnu/lib/libg++/g++-include/SkipSet.hP delete mode 100644 gnu/lib/libg++/g++-include/SmplHist.h delete mode 100644 gnu/lib/libg++/g++-include/SmplStat.h create mode 100644 gnu/lib/libg++/g++-include/SplayBag.ccP create mode 100644 gnu/lib/libg++/g++-include/SplayBag.hP create mode 100644 gnu/lib/libg++/g++-include/SplayMap.ccP create mode 100644 gnu/lib/libg++/g++-include/SplayMap.hP create mode 100644 gnu/lib/libg++/g++-include/SplayNode.ccP create mode 100644 gnu/lib/libg++/g++-include/SplayNode.hP create mode 100644 gnu/lib/libg++/g++-include/SplayPQ.ccP create mode 100644 gnu/lib/libg++/g++-include/SplayPQ.hP create mode 100644 gnu/lib/libg++/g++-include/SplaySet.ccP create mode 100644 gnu/lib/libg++/g++-include/SplaySet.hP create mode 100644 gnu/lib/libg++/g++-include/Stack.ccP create mode 100644 gnu/lib/libg++/g++-include/Stack.hP delete mode 100644 gnu/lib/libg++/g++-include/String.h delete mode 100644 gnu/lib/libg++/g++-include/Uniform.h create mode 100644 gnu/lib/libg++/g++-include/VHBag.ccP create mode 100644 gnu/lib/libg++/g++-include/VHBag.hP create mode 100644 gnu/lib/libg++/g++-include/VHMap.ccP create mode 100644 gnu/lib/libg++/g++-include/VHMap.hP create mode 100644 gnu/lib/libg++/g++-include/VHSet.ccP create mode 100644 gnu/lib/libg++/g++-include/VHSet.hP create mode 100644 gnu/lib/libg++/g++-include/VOHSet.ccP create mode 100644 gnu/lib/libg++/g++-include/VOHSet.hP create mode 100644 gnu/lib/libg++/g++-include/VQueue.ccP create mode 100644 gnu/lib/libg++/g++-include/VQueue.hP create mode 100644 gnu/lib/libg++/g++-include/VStack.ccP create mode 100644 gnu/lib/libg++/g++-include/VStack.hP create mode 100644 gnu/lib/libg++/g++-include/Vec.ccP create mode 100644 gnu/lib/libg++/g++-include/Vec.hP delete mode 100644 gnu/lib/libg++/g++-include/Weibull.h create mode 100644 gnu/lib/libg++/g++-include/XPBag.ccP create mode 100644 gnu/lib/libg++/g++-include/XPBag.hP create mode 100644 gnu/lib/libg++/g++-include/XPDeque.ccP create mode 100644 gnu/lib/libg++/g++-include/XPDeque.hP create mode 100644 gnu/lib/libg++/g++-include/XPPQ.ccP create mode 100644 gnu/lib/libg++/g++-include/XPPQ.hP create mode 100644 gnu/lib/libg++/g++-include/XPQueue.ccP create mode 100644 gnu/lib/libg++/g++-include/XPQueue.hP create mode 100644 gnu/lib/libg++/g++-include/XPSet.ccP create mode 100644 gnu/lib/libg++/g++-include/XPSet.hP create mode 100644 gnu/lib/libg++/g++-include/XPStack.ccP create mode 100644 gnu/lib/libg++/g++-include/XPStack.hP create mode 100644 gnu/lib/libg++/g++-include/XPlex.ccP create mode 100644 gnu/lib/libg++/g++-include/XPlex.hP delete mode 100644 gnu/lib/libg++/g++-include/abs.h delete mode 100644 gnu/lib/libg++/g++-include/bool.h create mode 100644 gnu/lib/libg++/g++-include/bstring.h delete mode 100644 gnu/lib/libg++/g++-include/builtin.h delete mode 100644 gnu/lib/libg++/g++-include/compare.h delete mode 100644 gnu/lib/libg++/g++-include/complex.h create mode 100644 gnu/lib/libg++/g++-include/ctype.h create mode 100644 gnu/lib/libg++/g++-include/defs.hP create mode 100644 gnu/lib/libg++/g++-include/dir.h create mode 100644 gnu/lib/libg++/g++-include/dirent.h create mode 100644 gnu/lib/libg++/g++-include/errno.h create mode 100644 gnu/lib/libg++/g++-include/fcntl.h delete mode 100644 gnu/lib/libg++/g++-include/file.h delete mode 100644 gnu/lib/libg++/g++-include/filebuf.h create mode 100644 gnu/lib/libg++/g++-include/gen/AVLMap.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/AVLMap.hP create mode 100644 gnu/lib/libg++/g++-include/gen/AVLSet.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/AVLSet.hP create mode 100644 gnu/lib/libg++/g++-include/gen/AVec.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/AVec.hP create mode 100644 gnu/lib/libg++/g++-include/gen/BSTSet.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/BSTSet.hP create mode 100644 gnu/lib/libg++/g++-include/gen/Bag.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/Bag.hP create mode 100644 gnu/lib/libg++/g++-include/gen/CHBag.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/CHBag.hP create mode 100644 gnu/lib/libg++/g++-include/gen/CHMap.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/CHMap.hP create mode 100644 gnu/lib/libg++/g++-include/gen/CHNode.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/CHNode.hP create mode 100644 gnu/lib/libg++/g++-include/gen/CHSet.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/CHSet.hP create mode 100644 gnu/lib/libg++/g++-include/gen/DLDeque.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/DLDeque.hP create mode 100644 gnu/lib/libg++/g++-include/gen/DLList.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/DLList.hP create mode 100644 gnu/lib/libg++/g++-include/gen/Deque.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/Deque.hP create mode 100644 gnu/lib/libg++/g++-include/gen/FPQueue.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/FPQueue.hP create mode 100644 gnu/lib/libg++/g++-include/gen/FPStack.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/FPStack.hP create mode 100644 gnu/lib/libg++/g++-include/gen/FPlex.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/FPlex.hP create mode 100644 gnu/lib/libg++/g++-include/gen/List.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/List.hP create mode 100644 gnu/lib/libg++/g++-include/gen/MPlex.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/MPlex.hP create mode 100644 gnu/lib/libg++/g++-include/gen/Map.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/Map.hP create mode 100644 gnu/lib/libg++/g++-include/gen/OSLBag.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/OSLBag.hP create mode 100644 gnu/lib/libg++/g++-include/gen/OSLSet.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/OSLSet.hP create mode 100644 gnu/lib/libg++/g++-include/gen/OXPBag.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/OXPBag.hP create mode 100644 gnu/lib/libg++/g++-include/gen/OXPSet.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/OXPSet.hP create mode 100644 gnu/lib/libg++/g++-include/gen/PHPQ.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/PHPQ.hP create mode 100644 gnu/lib/libg++/g++-include/gen/PQ.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/PQ.hP create mode 100644 gnu/lib/libg++/g++-include/gen/PSList.hP create mode 100644 gnu/lib/libg++/g++-include/gen/PVec.hP create mode 100644 gnu/lib/libg++/g++-include/gen/Plex.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/Plex.hP create mode 100644 gnu/lib/libg++/g++-include/gen/Queue.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/Queue.hP create mode 100644 gnu/lib/libg++/g++-include/gen/RAVLMap.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/RAVLMap.hP create mode 100644 gnu/lib/libg++/g++-include/gen/RPlex.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/RPlex.hP create mode 100644 gnu/lib/libg++/g++-include/gen/SLBag.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/SLBag.hP create mode 100644 gnu/lib/libg++/g++-include/gen/SLList.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/SLList.hP create mode 100644 gnu/lib/libg++/g++-include/gen/SLQueue.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/SLQueue.hP create mode 100644 gnu/lib/libg++/g++-include/gen/SLSet.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/SLSet.hP create mode 100644 gnu/lib/libg++/g++-include/gen/SLStack.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/SLStack.hP create mode 100644 gnu/lib/libg++/g++-include/gen/Set.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/Set.hP create mode 100644 gnu/lib/libg++/g++-include/gen/SkipBag.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/SkipBag.hP create mode 100644 gnu/lib/libg++/g++-include/gen/SkipMap.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/SkipMap.hP create mode 100644 gnu/lib/libg++/g++-include/gen/SkipSet.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/SkipSet.hP create mode 100644 gnu/lib/libg++/g++-include/gen/SplayBag.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/SplayBag.hP create mode 100644 gnu/lib/libg++/g++-include/gen/SplayMap.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/SplayMap.hP create mode 100644 gnu/lib/libg++/g++-include/gen/SplayNode.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/SplayNode.hP create mode 100644 gnu/lib/libg++/g++-include/gen/SplayPQ.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/SplayPQ.hP create mode 100644 gnu/lib/libg++/g++-include/gen/SplaySet.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/SplaySet.hP create mode 100644 gnu/lib/libg++/g++-include/gen/Stack.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/Stack.hP create mode 100644 gnu/lib/libg++/g++-include/gen/VHBag.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/VHBag.hP create mode 100644 gnu/lib/libg++/g++-include/gen/VHMap.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/VHMap.hP create mode 100644 gnu/lib/libg++/g++-include/gen/VHSet.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/VHSet.hP create mode 100644 gnu/lib/libg++/g++-include/gen/VOHSet.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/VOHSet.hP create mode 100644 gnu/lib/libg++/g++-include/gen/VQueue.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/VQueue.hP create mode 100644 gnu/lib/libg++/g++-include/gen/VStack.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/VStack.hP create mode 100644 gnu/lib/libg++/g++-include/gen/Vec.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/Vec.hP create mode 100644 gnu/lib/libg++/g++-include/gen/XPBag.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/XPBag.hP create mode 100644 gnu/lib/libg++/g++-include/gen/XPDeque.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/XPDeque.hP create mode 100644 gnu/lib/libg++/g++-include/gen/XPPQ.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/XPPQ.hP create mode 100644 gnu/lib/libg++/g++-include/gen/XPQueue.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/XPQueue.hP create mode 100644 gnu/lib/libg++/g++-include/gen/XPSet.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/XPSet.hP create mode 100644 gnu/lib/libg++/g++-include/gen/XPStack.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/XPStack.hP create mode 100644 gnu/lib/libg++/g++-include/gen/XPlex.ccP create mode 100644 gnu/lib/libg++/g++-include/gen/XPlex.hP create mode 100644 gnu/lib/libg++/g++-include/gen/defs.hP create mode 100644 gnu/lib/libg++/g++-include/gen/intSList.hP create mode 100644 gnu/lib/libg++/g++-include/gen/intVec.hP delete mode 100644 gnu/lib/libg++/g++-include/generic.h create mode 100644 gnu/lib/libg++/g++-include/grp.h create mode 100644 gnu/lib/libg++/g++-include/intSList.hP create mode 100644 gnu/lib/libg++/g++-include/intVec.hP delete mode 100644 gnu/lib/libg++/g++-include/istream.h delete mode 100644 gnu/lib/libg++/g++-include/malloc.h create mode 100644 gnu/lib/libg++/g++-include/math.h delete mode 100644 gnu/lib/libg++/g++-include/max.h create mode 100644 gnu/lib/libg++/g++-include/memory.h delete mode 100644 gnu/lib/libg++/g++-include/min.h delete mode 100644 gnu/lib/libg++/g++-include/minmax.h create mode 100644 gnu/lib/libg++/g++-include/netdb.h delete mode 100644 gnu/lib/libg++/g++-include/new.h delete mode 100644 gnu/lib/libg++/g++-include/open.h delete mode 100644 gnu/lib/libg++/g++-include/osfcn.h delete mode 100644 gnu/lib/libg++/g++-include/ostream.h create mode 100644 gnu/lib/libg++/g++-include/pwd.h delete mode 100644 gnu/lib/libg++/g++-include/regex.h create mode 100644 gnu/lib/libg++/g++-include/setjmp.h create mode 100644 gnu/lib/libg++/g++-include/signal.h delete mode 100644 gnu/lib/libg++/g++-include/std.h create mode 100644 gnu/lib/libg++/g++-include/stdarg.h create mode 100644 gnu/lib/libg++/g++-include/stddef.h create mode 100644 gnu/lib/libg++/g++-include/stdio.h create mode 100644 gnu/lib/libg++/g++-include/stdlib.h delete mode 100644 gnu/lib/libg++/g++-include/strclass.h delete mode 100644 gnu/lib/libg++/g++-include/stream.h delete mode 100644 gnu/lib/libg++/g++-include/streambuf.h create mode 100644 gnu/lib/libg++/g++-include/string.h create mode 100644 gnu/lib/libg++/g++-include/strings.h create mode 100644 gnu/lib/libg++/g++-include/sys/dir.h create mode 100644 gnu/lib/libg++/g++-include/sys/fcntl.h create mode 100644 gnu/lib/libg++/g++-include/sys/file.h create mode 100644 gnu/lib/libg++/g++-include/sys/mman.h create mode 100644 gnu/lib/libg++/g++-include/sys/param.h create mode 100644 gnu/lib/libg++/g++-include/sys/resource.h create mode 100644 gnu/lib/libg++/g++-include/sys/select.h create mode 100644 gnu/lib/libg++/g++-include/sys/signal.h create mode 100644 gnu/lib/libg++/g++-include/sys/socket.h create mode 100644 gnu/lib/libg++/g++-include/sys/stat.h create mode 100644 gnu/lib/libg++/g++-include/sys/time.h create mode 100644 gnu/lib/libg++/g++-include/sys/times.h create mode 100644 gnu/lib/libg++/g++-include/sys/types.h create mode 100644 gnu/lib/libg++/g++-include/sys/wait.h create mode 100644 gnu/lib/libg++/g++-include/time.h create mode 100644 gnu/lib/libg++/g++-include/unistd.h delete mode 100644 gnu/lib/libg++/genclass/genclass.1 create mode 100644 gnu/lib/libg++/iostream/PlotFile.C create mode 100644 gnu/lib/libg++/iostream/PlotFile.h create mode 100644 gnu/lib/libg++/iostream/SFile.C create mode 100644 gnu/lib/libg++/iostream/SFile.h create mode 100644 gnu/lib/libg++/iostream/editbuf.C create mode 100644 gnu/lib/libg++/iostream/editbuf.h create mode 100644 gnu/lib/libg++/iostream/filebuf.C create mode 100644 gnu/lib/libg++/iostream/floatconv.C create mode 100644 gnu/lib/libg++/iostream/floatio.h create mode 100644 gnu/lib/libg++/iostream/fstream.C create mode 100644 gnu/lib/libg++/iostream/fstream.h create mode 100644 gnu/lib/libg++/iostream/igetline.C create mode 100644 gnu/lib/libg++/iostream/igetsb.C create mode 100644 gnu/lib/libg++/iostream/indstream.C create mode 100644 gnu/lib/libg++/iostream/indstream.h create mode 100644 gnu/lib/libg++/iostream/iomanip.C create mode 100644 gnu/lib/libg++/iostream/iomanip.h create mode 100644 gnu/lib/libg++/iostream/ioprivate.h create mode 100644 gnu/lib/libg++/iostream/iostream.C create mode 100644 gnu/lib/libg++/iostream/iostream.h create mode 100644 gnu/lib/libg++/iostream/makebuf.C create mode 100644 gnu/lib/libg++/iostream/outfloat.C create mode 100644 gnu/lib/libg++/iostream/parsestream.C create mode 100644 gnu/lib/libg++/iostream/parsestream.h create mode 100644 gnu/lib/libg++/iostream/procbuf.C create mode 100644 gnu/lib/libg++/iostream/procbuf.h create mode 100644 gnu/lib/libg++/iostream/sbufvform.C create mode 100644 gnu/lib/libg++/iostream/sbufvscan.C create mode 100644 gnu/lib/libg++/iostream/sgetline.C create mode 100644 gnu/lib/libg++/iostream/stdiostream.C create mode 100644 gnu/lib/libg++/iostream/stdiostream.h create mode 100644 gnu/lib/libg++/iostream/stdstrbufs.C create mode 100644 gnu/lib/libg++/iostream/stdstreams.C create mode 100644 gnu/lib/libg++/iostream/stream.C create mode 100644 gnu/lib/libg++/iostream/stream.h create mode 100644 gnu/lib/libg++/iostream/streambuf.C create mode 100644 gnu/lib/libg++/iostream/streambuf.h create mode 100644 gnu/lib/libg++/iostream/strstream.C create mode 100644 gnu/lib/libg++/iostream/strstream.h create mode 100644 gnu/lib/libg++/libg++/ACG.h create mode 100644 gnu/lib/libg++/libg++/AllocRing.h create mode 100644 gnu/lib/libg++/libg++/Binomial.h create mode 100644 gnu/lib/libg++/libg++/BitSet.h create mode 100644 gnu/lib/libg++/libg++/BitString.h create mode 100644 gnu/lib/libg++/libg++/Complex.h create mode 100644 gnu/lib/libg++/libg++/CursesW.h create mode 100644 gnu/lib/libg++/libg++/DLList.cc create mode 100644 gnu/lib/libg++/libg++/DLList.h create mode 100644 gnu/lib/libg++/libg++/DiscUnif.h delete mode 100644 gnu/lib/libg++/libg++/EH.cc delete mode 100644 gnu/lib/libg++/libg++/EH2.c create mode 100644 gnu/lib/libg++/libg++/Erlang.h delete mode 100644 gnu/lib/libg++/libg++/File.cc delete mode 100644 gnu/lib/libg++/libg++/Filebuf.cc create mode 100644 gnu/lib/libg++/libg++/Fix.h create mode 100644 gnu/lib/libg++/libg++/Fix16.h create mode 100644 gnu/lib/libg++/libg++/Fix24.h create mode 100644 gnu/lib/libg++/libg++/Geom.h create mode 100644 gnu/lib/libg++/libg++/GetOpt.h create mode 100644 gnu/lib/libg++/libg++/HypGeom.h create mode 100644 gnu/lib/libg++/libg++/Incremental.h create mode 100644 gnu/lib/libg++/libg++/Integer.h create mode 100644 gnu/lib/libg++/libg++/LogNorm.h create mode 100644 gnu/lib/libg++/libg++/MLCG.h delete mode 100644 gnu/lib/libg++/libg++/Makefile.gnu create mode 100644 gnu/lib/libg++/libg++/NegExp.h create mode 100644 gnu/lib/libg++/libg++/Normal.h create mode 100644 gnu/lib/libg++/libg++/Obstack.h create mode 100644 gnu/lib/libg++/libg++/Pix.h delete mode 100644 gnu/lib/libg++/libg++/PlotFile.cc create mode 100644 gnu/lib/libg++/libg++/Poisson.h create mode 100644 gnu/lib/libg++/libg++/RNG.h create mode 100644 gnu/lib/libg++/libg++/Random.h create mode 100644 gnu/lib/libg++/libg++/Rational.h create mode 100644 gnu/lib/libg++/libg++/Regex.h create mode 100644 gnu/lib/libg++/libg++/RndInt.h delete mode 100644 gnu/lib/libg++/libg++/SFile.cc create mode 100644 gnu/lib/libg++/libg++/SLList.cc create mode 100644 gnu/lib/libg++/libg++/SLList.h delete mode 100644 gnu/lib/libg++/libg++/Sample.cc create mode 100644 gnu/lib/libg++/libg++/SmplHist.h create mode 100644 gnu/lib/libg++/libg++/SmplStat.h create mode 100644 gnu/lib/libg++/libg++/String.h create mode 100644 gnu/lib/libg++/libg++/Uniform.h create mode 100644 gnu/lib/libg++/libg++/Weibull.h create mode 100644 gnu/lib/libg++/libg++/_G_config.h create mode 100644 gnu/lib/libg++/libg++/bool.h create mode 100644 gnu/lib/libg++/libg++/builtin.h create mode 100644 gnu/lib/libg++/libg++/compare.h create mode 100644 gnu/lib/libg++/libg++/complex.h delete mode 100644 gnu/lib/libg++/libg++/ctype.cc delete mode 100644 gnu/lib/libg++/libg++/curses.cc delete mode 100644 gnu/lib/libg++/libg++/delete.cc delete mode 100644 gnu/lib/libg++/libg++/filebuf.cc delete mode 100644 gnu/lib/libg++/libg++/form.cc delete mode 100644 gnu/lib/libg++/libg++/g++.order create mode 100644 gnu/lib/libg++/libg++/generic.h create mode 100644 gnu/lib/libg++/libg++/getpagesize.h delete mode 100644 gnu/lib/libg++/libg++/gnulib3.c delete mode 100644 gnu/lib/libg++/libg++/istream.cc delete mode 100644 gnu/lib/libg++/libg++/itoa.cc create mode 100644 gnu/lib/libg++/libg++/libc.h delete mode 100644 gnu/lib/libg++/libg++/malloc.c create mode 100644 gnu/lib/libg++/libg++/math-68881.h delete mode 100644 gnu/lib/libg++/libg++/max.cc delete mode 100644 gnu/lib/libg++/libg++/min.cc create mode 100644 gnu/lib/libg++/libg++/minmax.h create mode 100644 gnu/lib/libg++/libg++/new.h delete mode 100644 gnu/lib/libg++/libg++/open.cc create mode 100644 gnu/lib/libg++/libg++/osfcn.h delete mode 100644 gnu/lib/libg++/libg++/ostream.cc create mode 100644 gnu/lib/libg++/libg++/regex.h delete mode 100644 gnu/lib/libg++/libg++/std.cc create mode 100644 gnu/lib/libg++/libg++/std.h create mode 100644 gnu/lib/libg++/libg++/strclass.h delete mode 100644 gnu/lib/libg++/libg++/streambuf.cc rename gnu/lib/libg++/{g++-include => libg++}/swap.h (100%) create mode 100644 gnu/lib/libg++/libg++/typemacros.h delete mode 100644 gnu/lib/libg++/libg++/xyzzy.cc create mode 100644 gnu/lib/libg++/libiberty/config.h create mode 100644 gnu/lib/libg++/libiberty/insque.c create mode 100644 gnu/lib/libg++/libiberty/strerror.c create mode 100644 gnu/lib/libg++/libiberty/strsignal.c create mode 100644 gnu/lib/libmalloc/COPYING.LIB create mode 100644 gnu/lib/libmalloc/ChangeLog create mode 100644 gnu/lib/libmalloc/Makefile create mode 100644 gnu/lib/libmalloc/Makefile.gnu create mode 100644 gnu/lib/libmalloc/OChangeLog create mode 100644 gnu/lib/libmalloc/README create mode 100644 gnu/lib/libmalloc/VERSION create mode 100644 gnu/lib/libmalloc/calloc.c create mode 100644 gnu/lib/libmalloc/cfree.c create mode 100644 gnu/lib/libmalloc/getpagesize.h create mode 100644 gnu/lib/libmalloc/gmalloc-head.c create mode 100644 gnu/lib/libmalloc/malloc.c create mode 100644 gnu/lib/libmalloc/malloc.h create mode 100644 gnu/lib/libmalloc/mcheck.c create mode 100644 gnu/lib/libmalloc/mem-limits.h create mode 100644 gnu/lib/libmalloc/memalign.c create mode 100644 gnu/lib/libmalloc/morecore.c create mode 100644 gnu/lib/libmalloc/mstats.c create mode 100644 gnu/lib/libmalloc/mtrace.awk create mode 100644 gnu/lib/libmalloc/mtrace.c create mode 100644 gnu/lib/libmalloc/ralloc.c create mode 100644 gnu/lib/libmalloc/valloc.c create mode 100644 gnu/lib/libmalloc/vm-limit.c create mode 100644 gnu/lib/libregex/AUTHORS rename gnu/{usr.bin/diff3 => lib/libregex}/COPYING (100%) create mode 100644 gnu/lib/libregex/ChangeLog create mode 100644 gnu/lib/libregex/INSTALL create mode 100644 gnu/lib/libregex/Makefile create mode 100644 gnu/lib/libregex/Makefile.gnu create mode 100644 gnu/lib/libregex/Makefile.in create mode 100644 gnu/lib/libregex/NEWS create mode 100644 gnu/lib/libregex/README create mode 100644 gnu/lib/libregex/VERSION create mode 100644 gnu/lib/libregex/config.status create mode 100644 gnu/lib/libregex/configure create mode 100644 gnu/lib/libregex/configure.in create mode 100644 gnu/lib/libregex/doc/Makefile create mode 100644 gnu/lib/libregex/doc/Makefile.in create mode 100644 gnu/lib/libregex/doc/include.awk create mode 100644 gnu/lib/libregex/doc/regex.aux create mode 100644 gnu/lib/libregex/doc/regex.cps create mode 100644 gnu/lib/libregex/doc/regex.info create mode 100644 gnu/lib/libregex/doc/regex.texi create mode 100644 gnu/lib/libregex/doc/texinfo.tex create mode 100644 gnu/lib/libregex/doc/xregex.texi rename {bin/ed => gnu/lib/libregex}/regex.c (100%) rename {bin/ed => gnu/lib/libregex}/regex.h (100%) create mode 100644 gnu/lib/libregex/test/ChangeLog create mode 100644 gnu/lib/libregex/test/Makefile create mode 100644 gnu/lib/libregex/test/Makefile.in create mode 100644 gnu/lib/libregex/test/TAGS create mode 100644 gnu/lib/libregex/test/alloca.c create mode 100644 gnu/lib/libregex/test/bsd-interf.c create mode 100644 gnu/lib/libregex/test/debugmalloc.c create mode 100644 gnu/lib/libregex/test/emacsmalloc.c create mode 100644 gnu/lib/libregex/test/fileregex.c create mode 100644 gnu/lib/libregex/test/g++malloc.c create mode 100644 gnu/lib/libregex/test/getpagesize.h create mode 100644 gnu/lib/libregex/test/iregex.c create mode 100644 gnu/lib/libregex/test/main.c create mode 100644 gnu/lib/libregex/test/malloc-test.c create mode 100644 gnu/lib/libregex/test/other.c create mode 100644 gnu/lib/libregex/test/printchar.c create mode 100644 gnu/lib/libregex/test/psx-basic.c create mode 100644 gnu/lib/libregex/test/psx-extend.c create mode 100644 gnu/lib/libregex/test/psx-generic.c create mode 100644 gnu/lib/libregex/test/psx-group.c create mode 100644 gnu/lib/libregex/test/psx-interf.c create mode 100644 gnu/lib/libregex/test/psx-interv.c create mode 100644 gnu/lib/libregex/test/regexcpp.sed create mode 100644 gnu/lib/libregex/test/syntax.skel create mode 100644 gnu/lib/libregex/test/test.c create mode 100644 gnu/lib/libregex/test/test.h create mode 100644 gnu/lib/libregex/test/tregress.c create mode 100644 gnu/lib/libregex/test/upcase.c create mode 100644 gnu/lib/libregex/test/xmalloc.c create mode 100644 gnu/libexec/uucp/COPYING create mode 100644 gnu/libexec/uucp/ChangeLog create mode 100644 gnu/libexec/uucp/Makefile create mode 100644 gnu/libexec/uucp/Makefile.inc create mode 100644 gnu/libexec/uucp/README create mode 100644 gnu/libexec/uucp/TODO create mode 100644 gnu/libexec/uucp/VERSION create mode 100644 gnu/libexec/uucp/common_sources/chat.c create mode 100644 gnu/libexec/uucp/common_sources/conf.h create mode 100644 gnu/libexec/uucp/common_sources/conn.c create mode 100644 gnu/libexec/uucp/common_sources/conn.h create mode 100644 gnu/libexec/uucp/common_sources/copy.c create mode 100644 gnu/libexec/uucp/common_sources/cu.h create mode 100644 gnu/libexec/uucp/common_sources/getopt.h create mode 100644 gnu/libexec/uucp/common_sources/log.c create mode 100644 gnu/libexec/uucp/common_sources/policy.h create mode 100644 gnu/libexec/uucp/common_sources/prot.c create mode 100644 gnu/libexec/uucp/common_sources/prot.h create mode 100644 gnu/libexec/uucp/common_sources/sysdep.h create mode 100644 gnu/libexec/uucp/common_sources/system.h create mode 100644 gnu/libexec/uucp/common_sources/tcp.c create mode 100644 gnu/libexec/uucp/common_sources/tli.c create mode 100644 gnu/libexec/uucp/common_sources/trans.h create mode 100644 gnu/libexec/uucp/common_sources/util.c create mode 100644 gnu/libexec/uucp/common_sources/uuconf.h create mode 100644 gnu/libexec/uucp/common_sources/uucp.h create mode 100644 gnu/libexec/uucp/common_sources/uudefs.h create mode 100644 gnu/libexec/uucp/contrib/Dial.Hayes create mode 100644 gnu/libexec/uucp/contrib/Hangup.Hayes create mode 100644 gnu/libexec/uucp/contrib/Login.LAT create mode 100644 gnu/libexec/uucp/contrib/Login.PortSel create mode 100644 gnu/libexec/uucp/contrib/Login.VMS create mode 100644 gnu/libexec/uucp/contrib/Makefile.uurt create mode 100644 gnu/libexec/uucp/contrib/Makefile.xchat create mode 100644 gnu/libexec/uucp/contrib/README create mode 100644 gnu/libexec/uucp/contrib/README-UURATE create mode 100644 gnu/libexec/uucp/contrib/README-XCHAT create mode 100644 gnu/libexec/uucp/contrib/savelog.man create mode 100644 gnu/libexec/uucp/contrib/savelog.sh create mode 100644 gnu/libexec/uucp/contrib/stats.sh create mode 100644 gnu/libexec/uucp/contrib/tstout.c create mode 100644 gnu/libexec/uucp/contrib/uuclean create mode 100644 gnu/libexec/uucp/contrib/uuq.sh create mode 100644 gnu/libexec/uucp/contrib/uurate.c create mode 100644 gnu/libexec/uucp/contrib/uurate.man create mode 100644 gnu/libexec/uucp/contrib/uureroute create mode 100644 gnu/libexec/uucp/contrib/uusnap.c create mode 100644 gnu/libexec/uucp/contrib/uutraf create mode 100644 gnu/libexec/uucp/contrib/uutry create mode 100644 gnu/libexec/uucp/contrib/xc-conf.h-dist create mode 100644 gnu/libexec/uucp/contrib/xchat.c create mode 100644 gnu/libexec/uucp/contrib/xchat.man create mode 100644 gnu/libexec/uucp/cu/Makefile create mode 100644 gnu/libexec/uucp/cu/cu.1 create mode 100644 gnu/libexec/uucp/cu/cu.c create mode 100644 gnu/libexec/uucp/libunix/MANIFEST create mode 100644 gnu/libexec/uucp/libunix/Makefile create mode 100644 gnu/libexec/uucp/libunix/access.c create mode 100644 gnu/libexec/uucp/libunix/addbas.c create mode 100644 gnu/libexec/uucp/libunix/app3.c create mode 100644 gnu/libexec/uucp/libunix/app4.c create mode 100644 gnu/libexec/uucp/libunix/basnam.c create mode 100644 gnu/libexec/uucp/libunix/bytfre.c create mode 100644 gnu/libexec/uucp/libunix/chmod.c create mode 100644 gnu/libexec/uucp/libunix/cohtty.c create mode 100644 gnu/libexec/uucp/libunix/cusub.c create mode 100644 gnu/libexec/uucp/libunix/cwd.c create mode 100644 gnu/libexec/uucp/libunix/detach.c create mode 100644 gnu/libexec/uucp/libunix/dirent.c create mode 100644 gnu/libexec/uucp/libunix/dup2.c create mode 100644 gnu/libexec/uucp/libunix/efopen.c create mode 100644 gnu/libexec/uucp/libunix/epopen.c create mode 100644 gnu/libexec/uucp/libunix/exists.c create mode 100644 gnu/libexec/uucp/libunix/filnam.c create mode 100644 gnu/libexec/uucp/libunix/fsusg.c create mode 100644 gnu/libexec/uucp/libunix/fsusg.h create mode 100644 gnu/libexec/uucp/libunix/ftw.c create mode 100644 gnu/libexec/uucp/libunix/getcwd.c create mode 100644 gnu/libexec/uucp/libunix/indir.c create mode 100644 gnu/libexec/uucp/libunix/init.c create mode 100644 gnu/libexec/uucp/libunix/isdir.c create mode 100644 gnu/libexec/uucp/libunix/isfork.c create mode 100644 gnu/libexec/uucp/libunix/iswait.c create mode 100644 gnu/libexec/uucp/libunix/jobid.c create mode 100644 gnu/libexec/uucp/libunix/lcksys.c create mode 100644 gnu/libexec/uucp/libunix/link.c create mode 100644 gnu/libexec/uucp/libunix/locfil.c create mode 100644 gnu/libexec/uucp/libunix/lock.c create mode 100644 gnu/libexec/uucp/libunix/loctim.c create mode 100644 gnu/libexec/uucp/libunix/mail.c create mode 100644 gnu/libexec/uucp/libunix/mkdir.c create mode 100644 gnu/libexec/uucp/libunix/mkdirs.c create mode 100644 gnu/libexec/uucp/libunix/mode.c create mode 100644 gnu/libexec/uucp/libunix/move.c create mode 100644 gnu/libexec/uucp/libunix/opensr.c create mode 100644 gnu/libexec/uucp/libunix/pause.c create mode 100644 gnu/libexec/uucp/libunix/picksb.c create mode 100644 gnu/libexec/uucp/libunix/portnm.c create mode 100644 gnu/libexec/uucp/libunix/proctm.c create mode 100644 gnu/libexec/uucp/libunix/recep.c create mode 100644 gnu/libexec/uucp/libunix/remove.c create mode 100644 gnu/libexec/uucp/libunix/rename.c create mode 100644 gnu/libexec/uucp/libunix/rmdir.c create mode 100644 gnu/libexec/uucp/libunix/run.c create mode 100644 gnu/libexec/uucp/libunix/seq.c create mode 100644 gnu/libexec/uucp/libunix/serial.c create mode 100644 gnu/libexec/uucp/libunix/signal.c create mode 100644 gnu/libexec/uucp/libunix/sindir.c create mode 100644 gnu/libexec/uucp/libunix/size.c create mode 100644 gnu/libexec/uucp/libunix/sleep.c create mode 100644 gnu/libexec/uucp/libunix/spawn.c create mode 100644 gnu/libexec/uucp/libunix/splcmd.c create mode 100644 gnu/libexec/uucp/libunix/splnam.c create mode 100644 gnu/libexec/uucp/libunix/spool.c create mode 100644 gnu/libexec/uucp/libunix/srmdir.c create mode 100644 gnu/libexec/uucp/libunix/statsb.c create mode 100644 gnu/libexec/uucp/libunix/status.c create mode 100644 gnu/libexec/uucp/libunix/strerr.c create mode 100644 gnu/libexec/uucp/libunix/time.c create mode 100644 gnu/libexec/uucp/libunix/tmpfil.c create mode 100644 gnu/libexec/uucp/libunix/trunc.c create mode 100644 gnu/libexec/uucp/libunix/uacces.c create mode 100644 gnu/libexec/uucp/libunix/ufopen.c create mode 100644 gnu/libexec/uucp/libunix/ultspl.c create mode 100644 gnu/libexec/uucp/libunix/unknwn.c create mode 100644 gnu/libexec/uucp/libunix/uuto.c create mode 100644 gnu/libexec/uucp/libunix/walk.c create mode 100644 gnu/libexec/uucp/libunix/wldcrd.c create mode 100644 gnu/libexec/uucp/libunix/work.c create mode 100644 gnu/libexec/uucp/libunix/xqtfil.c create mode 100644 gnu/libexec/uucp/libunix/xqtsub.c create mode 100644 gnu/libexec/uucp/libuuconf/COPYING.LIB create mode 100644 gnu/libexec/uucp/libuuconf/MANIFEST create mode 100644 gnu/libexec/uucp/libuuconf/Makefile create mode 100644 gnu/libexec/uucp/libuuconf/README create mode 100644 gnu/libexec/uucp/libuuconf/addblk.c create mode 100644 gnu/libexec/uucp/libuuconf/addstr.c create mode 100644 gnu/libexec/uucp/libuuconf/allblk.c create mode 100644 gnu/libexec/uucp/libuuconf/alloc.c create mode 100644 gnu/libexec/uucp/libuuconf/alloc.h create mode 100644 gnu/libexec/uucp/libuuconf/base.c create mode 100644 gnu/libexec/uucp/libuuconf/bool.c create mode 100644 gnu/libexec/uucp/libuuconf/callin.c create mode 100644 gnu/libexec/uucp/libuuconf/calout.c create mode 100644 gnu/libexec/uucp/libuuconf/chatc.c create mode 100644 gnu/libexec/uucp/libuuconf/cmdarg.c create mode 100644 gnu/libexec/uucp/libuuconf/cmdfil.c create mode 100644 gnu/libexec/uucp/libuuconf/cmdlin.c create mode 100644 gnu/libexec/uucp/libuuconf/debfil.c create mode 100644 gnu/libexec/uucp/libuuconf/deblev.c create mode 100644 gnu/libexec/uucp/libuuconf/diacod.c create mode 100644 gnu/libexec/uucp/libuuconf/dial.c create mode 100644 gnu/libexec/uucp/libuuconf/diasub.c create mode 100644 gnu/libexec/uucp/libuuconf/dnams.c create mode 100644 gnu/libexec/uucp/libuuconf/errno.c create mode 100644 gnu/libexec/uucp/libuuconf/errstr.c create mode 100644 gnu/libexec/uucp/libuuconf/filnam.c create mode 100644 gnu/libexec/uucp/libuuconf/freblk.c create mode 100644 gnu/libexec/uucp/libuuconf/fredia.c create mode 100644 gnu/libexec/uucp/libuuconf/free.c create mode 100644 gnu/libexec/uucp/libuuconf/freprt.c create mode 100644 gnu/libexec/uucp/libuuconf/fresys.c create mode 100644 gnu/libexec/uucp/libuuconf/grdcmp.c create mode 100644 gnu/libexec/uucp/libuuconf/hdial.c create mode 100644 gnu/libexec/uucp/libuuconf/hdnams.c create mode 100644 gnu/libexec/uucp/libuuconf/hinit.c create mode 100644 gnu/libexec/uucp/libuuconf/hlocnm.c create mode 100644 gnu/libexec/uucp/libuuconf/hport.c create mode 100644 gnu/libexec/uucp/libuuconf/hrmunk.c create mode 100644 gnu/libexec/uucp/libuuconf/hsinfo.c create mode 100644 gnu/libexec/uucp/libuuconf/hsnams.c create mode 100644 gnu/libexec/uucp/libuuconf/hsys.c create mode 100644 gnu/libexec/uucp/libuuconf/hunk.c create mode 100644 gnu/libexec/uucp/libuuconf/iniglb.c create mode 100644 gnu/libexec/uucp/libuuconf/init.c create mode 100644 gnu/libexec/uucp/libuuconf/int.c create mode 100644 gnu/libexec/uucp/libuuconf/lckdir.c create mode 100644 gnu/libexec/uucp/libuuconf/lineno.c create mode 100644 gnu/libexec/uucp/libuuconf/llocnm.c create mode 100644 gnu/libexec/uucp/libuuconf/local.c create mode 100644 gnu/libexec/uucp/libuuconf/locnm.c create mode 100644 gnu/libexec/uucp/libuuconf/logfil.c create mode 100644 gnu/libexec/uucp/libuuconf/maxuxq.c create mode 100644 gnu/libexec/uucp/libuuconf/mrgblk.c create mode 100644 gnu/libexec/uucp/libuuconf/paramc.c create mode 100644 gnu/libexec/uucp/libuuconf/port.c create mode 100644 gnu/libexec/uucp/libuuconf/prtsub.c create mode 100644 gnu/libexec/uucp/libuuconf/pubdir.c create mode 100644 gnu/libexec/uucp/libuuconf/rdlocs.c create mode 100644 gnu/libexec/uucp/libuuconf/rdperm.c create mode 100644 gnu/libexec/uucp/libuuconf/reliab.c create mode 100644 gnu/libexec/uucp/libuuconf/remunk.c create mode 100644 gnu/libexec/uucp/libuuconf/sinfo.c create mode 100644 gnu/libexec/uucp/libuuconf/snams.c create mode 100644 gnu/libexec/uucp/libuuconf/split.c create mode 100644 gnu/libexec/uucp/libuuconf/spool.c create mode 100644 gnu/libexec/uucp/libuuconf/stafil.c create mode 100644 gnu/libexec/uucp/libuuconf/syshdr.h create mode 100644 gnu/libexec/uucp/libuuconf/syssub.c create mode 100644 gnu/libexec/uucp/libuuconf/tcalou.c create mode 100644 gnu/libexec/uucp/libuuconf/tdial.c create mode 100644 gnu/libexec/uucp/libuuconf/tdialc.c create mode 100644 gnu/libexec/uucp/libuuconf/tdnams.c create mode 100644 gnu/libexec/uucp/libuuconf/tgcmp.c create mode 100644 gnu/libexec/uucp/libuuconf/thread.c create mode 100644 gnu/libexec/uucp/libuuconf/time.c create mode 100644 gnu/libexec/uucp/libuuconf/tinit.c create mode 100644 gnu/libexec/uucp/libuuconf/tlocnm.c create mode 100644 gnu/libexec/uucp/libuuconf/tport.c create mode 100644 gnu/libexec/uucp/libuuconf/tportc.c create mode 100644 gnu/libexec/uucp/libuuconf/tsinfo.c create mode 100644 gnu/libexec/uucp/libuuconf/tsnams.c create mode 100644 gnu/libexec/uucp/libuuconf/tsys.c create mode 100644 gnu/libexec/uucp/libuuconf/tval.c create mode 100644 gnu/libexec/uucp/libuuconf/ugtlin.c create mode 100644 gnu/libexec/uucp/libuuconf/unk.c create mode 100644 gnu/libexec/uucp/libuuconf/uucnfi.h create mode 100644 gnu/libexec/uucp/libuuconf/val.c create mode 100644 gnu/libexec/uucp/libuuconf/vinit.c create mode 100644 gnu/libexec/uucp/libuuconf/vport.c create mode 100644 gnu/libexec/uucp/libuuconf/vsinfo.c create mode 100644 gnu/libexec/uucp/libuuconf/vsnams.c create mode 100644 gnu/libexec/uucp/libuuconf/vsys.c create mode 100644 gnu/libexec/uucp/libuucp/MANIFEST create mode 100644 gnu/libexec/uucp/libuucp/Makefile create mode 100644 gnu/libexec/uucp/libuucp/bsrch.c create mode 100644 gnu/libexec/uucp/libuucp/buffer.c create mode 100644 gnu/libexec/uucp/libuucp/bzero.c create mode 100644 gnu/libexec/uucp/libuucp/crc.c create mode 100644 gnu/libexec/uucp/libuucp/debug.c create mode 100644 gnu/libexec/uucp/libuucp/escape.c create mode 100644 gnu/libexec/uucp/libuucp/getlin.c create mode 100644 gnu/libexec/uucp/libuucp/getop1.c create mode 100644 gnu/libexec/uucp/libuucp/getopt.c create mode 100644 gnu/libexec/uucp/libuucp/memchr.c create mode 100644 gnu/libexec/uucp/libuucp/memcmp.c create mode 100644 gnu/libexec/uucp/libuucp/memcpy.c create mode 100644 gnu/libexec/uucp/libuucp/parse.c create mode 100644 gnu/libexec/uucp/libuucp/spool.c create mode 100644 gnu/libexec/uucp/libuucp/status.c create mode 100644 gnu/libexec/uucp/libuucp/strcas.c create mode 100644 gnu/libexec/uucp/libuucp/strchr.c create mode 100644 gnu/libexec/uucp/libuucp/strdup.c create mode 100644 gnu/libexec/uucp/libuucp/strncs.c create mode 100644 gnu/libexec/uucp/libuucp/strrch.c create mode 100644 gnu/libexec/uucp/libuucp/strstr.c create mode 100644 gnu/libexec/uucp/libuucp/strtol.c create mode 100644 gnu/libexec/uucp/libuucp/xfree.c create mode 100644 gnu/libexec/uucp/libuucp/xmall.c create mode 100644 gnu/libexec/uucp/libuucp/xreall.c create mode 100644 gnu/libexec/uucp/sample/Makefile create mode 100644 gnu/libexec/uucp/sample/call create mode 100644 gnu/libexec/uucp/sample/config create mode 100644 gnu/libexec/uucp/sample/dial create mode 100644 gnu/libexec/uucp/sample/dialcode create mode 100644 gnu/libexec/uucp/sample/passwd create mode 100644 gnu/libexec/uucp/sample/port create mode 100644 gnu/libexec/uucp/sample/sys1 create mode 100644 gnu/libexec/uucp/sample/sys2 create mode 100644 gnu/libexec/uucp/tstuu.c create mode 100644 gnu/libexec/uucp/uuchk/Makefile create mode 100644 gnu/libexec/uucp/uuchk/uuchk.c create mode 100644 gnu/libexec/uucp/uucico/Makefile create mode 100644 gnu/libexec/uucp/uucico/prote.c create mode 100644 gnu/libexec/uucp/uucico/protf.c create mode 100644 gnu/libexec/uucp/uucico/protg.c create mode 100644 gnu/libexec/uucp/uucico/proti.c create mode 100644 gnu/libexec/uucp/uucico/protj.c create mode 100644 gnu/libexec/uucp/uucico/prott.c create mode 100644 gnu/libexec/uucp/uucico/protz.c create mode 100644 gnu/libexec/uucp/uucico/rec.c create mode 100644 gnu/libexec/uucp/uucico/send.c create mode 100644 gnu/libexec/uucp/uucico/time.c create mode 100644 gnu/libexec/uucp/uucico/trans.c create mode 100644 gnu/libexec/uucp/uucico/uucico.8 create mode 100644 gnu/libexec/uucp/uucico/uucico.c create mode 100644 gnu/libexec/uucp/uucico/xcmd.c create mode 100644 gnu/libexec/uucp/uuconv/Makefile create mode 100644 gnu/libexec/uucp/uuconv/uuconv.c create mode 100644 gnu/libexec/uucp/uucp/Makefile create mode 100644 gnu/libexec/uucp/uucp/uucp.1 create mode 100644 gnu/libexec/uucp/uucp/uucp.c create mode 100644 gnu/libexec/uucp/uulog/Makefile create mode 100644 gnu/libexec/uucp/uulog/uulog.c create mode 100644 gnu/libexec/uucp/uuname/Makefile create mode 100644 gnu/libexec/uucp/uuname/uuname.c create mode 100644 gnu/libexec/uucp/uupick/Makefile create mode 100644 gnu/libexec/uucp/uupick/uupick.c create mode 100644 gnu/libexec/uucp/uusched/Makefile create mode 100644 gnu/libexec/uucp/uusched/uusched.in create mode 100644 gnu/libexec/uucp/uustat/Makefile create mode 100644 gnu/libexec/uucp/uustat/uustat.1 create mode 100644 gnu/libexec/uucp/uustat/uustat.c create mode 100644 gnu/libexec/uucp/uuto/Makefile create mode 100644 gnu/libexec/uucp/uuto/uuto.in create mode 100644 gnu/libexec/uucp/uux/Makefile create mode 100644 gnu/libexec/uucp/uux/uux.1 create mode 100644 gnu/libexec/uucp/uux/uux.c create mode 100644 gnu/libexec/uucp/uuxqt/Makefile create mode 100644 gnu/libexec/uucp/uuxqt/uuxqt.8 create mode 100644 gnu/libexec/uucp/uuxqt/uuxqt.c rename {bin/test => gnu/usr.bin/as}/COPYING (100%) create mode 100644 gnu/usr.bin/as/ChangeLog create mode 100644 gnu/usr.bin/as/Makefile create mode 100644 gnu/usr.bin/as/Makefile.gnu create mode 100644 gnu/usr.bin/as/NOTES create mode 100644 gnu/usr.bin/as/README.gnu create mode 100644 gnu/usr.bin/as/app.c create mode 100644 gnu/usr.bin/as/append.c create mode 100644 gnu/usr.bin/as/as.1 create mode 100644 gnu/usr.bin/as/as.c create mode 100644 gnu/usr.bin/as/as.h create mode 100644 gnu/usr.bin/as/atof-generic.c create mode 100644 gnu/usr.bin/as/bignum-copy.c create mode 100644 gnu/usr.bin/as/bignum.h create mode 100644 gnu/usr.bin/as/config/Makefile.i386 create mode 100644 gnu/usr.bin/as/config/a.out.gnu.h create mode 100644 gnu/usr.bin/as/config/atof-ieee.c create mode 100644 gnu/usr.bin/as/config/i386-opcode.h create mode 100644 gnu/usr.bin/as/config/i386.c create mode 100644 gnu/usr.bin/as/config/i386.h create mode 100644 gnu/usr.bin/as/expr.c create mode 100644 gnu/usr.bin/as/expr.h create mode 100644 gnu/usr.bin/as/flonum-const.c create mode 100644 gnu/usr.bin/as/flonum-copy.c create mode 100644 gnu/usr.bin/as/flonum-mult.c create mode 100644 gnu/usr.bin/as/flonum.h create mode 100644 gnu/usr.bin/as/frags.c create mode 100644 gnu/usr.bin/as/frags.h create mode 100644 gnu/usr.bin/as/hash.c create mode 100644 gnu/usr.bin/as/hash.h create mode 100644 gnu/usr.bin/as/hex-value.c create mode 100644 gnu/usr.bin/as/input-file.c create mode 100644 gnu/usr.bin/as/input-file.h create mode 100644 gnu/usr.bin/as/input-scrub.c create mode 100644 gnu/usr.bin/as/md.h create mode 100644 gnu/usr.bin/as/messages.c create mode 100644 gnu/usr.bin/as/objrecdef.h create mode 100644 gnu/usr.bin/as/obstack.c create mode 100644 gnu/usr.bin/as/obstack.h create mode 100644 gnu/usr.bin/as/output-file.c create mode 100644 gnu/usr.bin/as/read.c create mode 100644 gnu/usr.bin/as/read.h create mode 100644 gnu/usr.bin/as/struc-symbol.h create mode 100644 gnu/usr.bin/as/subsegs.c create mode 100644 gnu/usr.bin/as/subsegs.h create mode 100644 gnu/usr.bin/as/symbols.c create mode 100644 gnu/usr.bin/as/symbols.h create mode 100644 gnu/usr.bin/as/version.c create mode 100644 gnu/usr.bin/as/write.c create mode 100644 gnu/usr.bin/as/write.h create mode 100644 gnu/usr.bin/as/xmalloc.c create mode 100644 gnu/usr.bin/as/xrealloc.c create mode 100644 gnu/usr.bin/cc/Makefile delete mode 100644 gnu/usr.bin/cc/Makefile.inc create mode 100644 gnu/usr.bin/cc/TODO create mode 100644 gnu/usr.bin/cc/cc/g++.script delete mode 100644 gnu/usr.bin/cc/common/aux-output.c delete mode 100644 gnu/usr.bin/cc/common/c-common.c delete mode 100644 gnu/usr.bin/cc/common/calls.c delete mode 100644 gnu/usr.bin/cc/common/combine.c delete mode 100644 gnu/usr.bin/cc/common/cse.c delete mode 100644 gnu/usr.bin/cc/common/dbxout.c delete mode 100644 gnu/usr.bin/cc/common/dwarfout.c delete mode 100644 gnu/usr.bin/cc/common/emit-rtl.c delete mode 100644 gnu/usr.bin/cc/common/expmed.c delete mode 100644 gnu/usr.bin/cc/common/expr.c delete mode 100644 gnu/usr.bin/cc/common/final.c delete mode 100644 gnu/usr.bin/cc/common/function.c delete mode 100644 gnu/usr.bin/cc/common/gstddef.h delete mode 100644 gnu/usr.bin/cc/common/insn-emit.c delete mode 100644 gnu/usr.bin/cc/common/insn-extract.c delete mode 100644 gnu/usr.bin/cc/common/insn-flags.h delete mode 100644 gnu/usr.bin/cc/common/insn-opinit.c delete mode 100644 gnu/usr.bin/cc/common/insn-output.c delete mode 100644 gnu/usr.bin/cc/common/insn-recog.c delete mode 100644 gnu/usr.bin/cc/common/integrate.c delete mode 100644 gnu/usr.bin/cc/common/loop.c delete mode 100644 gnu/usr.bin/cc/common/real.c delete mode 100644 gnu/usr.bin/cc/common/real.h delete mode 100644 gnu/usr.bin/cc/common/recog.c delete mode 100644 gnu/usr.bin/cc/common/reg-stack.c delete mode 100644 gnu/usr.bin/cc/common/reload.c delete mode 100644 gnu/usr.bin/cc/common/reload1.c delete mode 100644 gnu/usr.bin/cc/common/sched.c delete mode 100644 gnu/usr.bin/cc/common/stmt.c delete mode 100644 gnu/usr.bin/cc/common/toplev.c delete mode 100644 gnu/usr.bin/cc/common/unroll.c delete mode 100644 gnu/usr.bin/cc/common/varasm.c delete mode 100644 gnu/usr.bin/cc/common/version.c rename gnu/usr.bin/cc/cpp/{cpp.1 => gcpp.1} (100%) create mode 100644 gnu/usr.bin/cc/lib/Makefile create mode 100644 gnu/usr.bin/cc/lib/aux-output.c rename gnu/usr.bin/cc/{common => lib}/basic-block.h (100%) create mode 100644 gnu/usr.bin/cc/lib/c-common.c rename gnu/usr.bin/cc/{common => lib}/c-lex.h (100%) rename gnu/usr.bin/cc/{common => lib}/c-parse.h (100%) rename gnu/usr.bin/cc/{common => lib}/c-tree.h (100%) rename gnu/usr.bin/cc/{common => lib}/caller-save.c (100%) create mode 100644 gnu/usr.bin/cc/lib/calls.c create mode 100644 gnu/usr.bin/cc/lib/combine.c rename gnu/usr.bin/cc/{common => lib}/conditions.h (100%) rename gnu/usr.bin/cc/{common => lib}/config.h (100%) rename gnu/usr.bin/cc/{common => lib}/convert.c (100%) rename gnu/usr.bin/cc/{common => lib}/convert.h (100%) create mode 100644 gnu/usr.bin/cc/lib/cse.c create mode 100644 gnu/usr.bin/cc/lib/dbxout.c rename gnu/usr.bin/cc/{common => lib}/defaults.h (100%) create mode 100644 gnu/usr.bin/cc/lib/dwarfout.c create mode 100644 gnu/usr.bin/cc/lib/emit-rtl.c rename gnu/usr.bin/cc/{common => lib}/explow.c (100%) create mode 100644 gnu/usr.bin/cc/lib/expmed.c create mode 100644 gnu/usr.bin/cc/lib/expr.c rename gnu/usr.bin/cc/{common => lib}/expr.h (100%) create mode 100644 gnu/usr.bin/cc/lib/final.c rename gnu/usr.bin/cc/{common => lib}/flags.h (100%) rename gnu/usr.bin/cc/{common => lib}/flow.c (100%) rename gnu/usr.bin/cc/{common => lib}/fold-const.c (100%) create mode 100644 gnu/usr.bin/cc/lib/function.c rename gnu/usr.bin/cc/{common => lib}/function.h (100%) rename gnu/usr.bin/cc/{common => lib}/gbl-ctors.h (100%) rename gnu/usr.bin/cc/{common => lib}/getpwd.c (100%) rename gnu/usr.bin/cc/{common => lib}/glimits.h (100%) rename gnu/usr.bin/cc/{common => lib}/global.c (100%) create mode 100644 gnu/usr.bin/cc/lib/gstddef.h rename gnu/usr.bin/cc/{common => lib}/gvarargs.h (100%) rename gnu/usr.bin/cc/{common => lib}/hard-reg-set.h (100%) rename gnu/usr.bin/cc/{common => lib}/i386/bsd.h (100%) rename gnu/usr.bin/cc/{common => lib}/i386/gas.h (100%) rename gnu/usr.bin/cc/{common => lib}/i386/gstabs.h (100%) rename gnu/usr.bin/cc/{common => lib}/i386/i386.h (100%) rename gnu/usr.bin/cc/{common => lib}/i386/perform.h (100%) rename gnu/usr.bin/cc/{common => lib}/i386/unix.h (100%) rename gnu/usr.bin/cc/{common => lib}/input.h (100%) rename gnu/usr.bin/cc/{common => lib}/insn-attr.h (100%) rename gnu/usr.bin/cc/{common => lib}/insn-attrtab.c (100%) rename gnu/usr.bin/cc/{common => lib}/insn-codes.h (100%) rename gnu/usr.bin/cc/{common => lib}/insn-config.h (100%) create mode 100644 gnu/usr.bin/cc/lib/insn-emit.c create mode 100644 gnu/usr.bin/cc/lib/insn-extract.c create mode 100644 gnu/usr.bin/cc/lib/insn-flags.h create mode 100644 gnu/usr.bin/cc/lib/insn-opinit.c create mode 100644 gnu/usr.bin/cc/lib/insn-output.c rename gnu/usr.bin/cc/{common => lib}/insn-peep.c (100%) create mode 100644 gnu/usr.bin/cc/lib/insn-recog.c create mode 100644 gnu/usr.bin/cc/lib/integrate.c rename gnu/usr.bin/cc/{common => lib}/integrate.h (100%) rename gnu/usr.bin/cc/{common => lib}/jump.c (100%) create mode 100644 gnu/usr.bin/cc/lib/lib.mk rename gnu/usr.bin/cc/{common => lib}/local-alloc.c (100%) rename gnu/usr.bin/cc/{common => lib}/longlong.h (100%) create mode 100644 gnu/usr.bin/cc/lib/loop.c rename gnu/usr.bin/cc/{common => lib}/loop.h (100%) rename gnu/usr.bin/cc/{common => lib}/machmode.def (100%) rename gnu/usr.bin/cc/{common => lib}/machmode.h (100%) rename gnu/usr.bin/cc/{common => lib}/obstack.c (100%) rename gnu/usr.bin/cc/{common => lib}/obstack.h (100%) rename gnu/usr.bin/cc/{common => lib}/optabs.c (100%) rename gnu/usr.bin/cc/{common => lib}/output.h (100%) rename gnu/usr.bin/cc/{common => lib}/print-rtl.c (100%) rename gnu/usr.bin/cc/{common => lib}/print-tree.c (100%) create mode 100644 gnu/usr.bin/cc/lib/real.c create mode 100644 gnu/usr.bin/cc/lib/real.h create mode 100644 gnu/usr.bin/cc/lib/recog.c rename gnu/usr.bin/cc/{common => lib}/recog.h (100%) create mode 100644 gnu/usr.bin/cc/lib/reg-stack.c rename gnu/usr.bin/cc/{common => lib}/regclass.c (100%) rename gnu/usr.bin/cc/{common => lib}/regs.h (100%) create mode 100644 gnu/usr.bin/cc/lib/reload.c rename gnu/usr.bin/cc/{common => lib}/reload.h (100%) create mode 100644 gnu/usr.bin/cc/lib/reload1.c rename gnu/usr.bin/cc/{common => lib}/reorg.c (100%) rename gnu/usr.bin/cc/{common => lib}/rtl.c (100%) rename gnu/usr.bin/cc/{common => lib}/rtl.def (100%) rename gnu/usr.bin/cc/{common => lib}/rtl.h (100%) rename gnu/usr.bin/cc/{common => lib}/rtlanal.c (100%) create mode 100644 gnu/usr.bin/cc/lib/sched.c rename gnu/usr.bin/cc/{common => lib}/sdbout.c (100%) create mode 100644 gnu/usr.bin/cc/lib/stmt.c rename gnu/usr.bin/cc/{common => lib}/stor-layout.c (100%) rename gnu/usr.bin/cc/{common => lib}/stupid.c (100%) rename gnu/usr.bin/cc/{common => lib}/tconfig.h (100%) rename gnu/usr.bin/cc/{common => lib}/tm.h (100%) create mode 100644 gnu/usr.bin/cc/lib/toplev.c rename gnu/usr.bin/cc/{common => lib}/tree.c (100%) rename gnu/usr.bin/cc/{common => lib}/tree.def (100%) rename gnu/usr.bin/cc/{common => lib}/tree.h (100%) rename gnu/usr.bin/cc/{common => lib}/typeclass.h (100%) create mode 100644 gnu/usr.bin/cc/lib/unroll.c create mode 100644 gnu/usr.bin/cc/lib/varasm.c create mode 100644 gnu/usr.bin/cc/lib/version.c rename gnu/usr.bin/cc/{common => lib}/xcoffout.c (100%) create mode 100644 gnu/usr.bin/cpio/COPYING create mode 100644 gnu/usr.bin/cpio/COPYING.LIB create mode 100644 gnu/usr.bin/cpio/ChangeLog create mode 100644 gnu/usr.bin/cpio/Makefile create mode 100644 gnu/usr.bin/cpio/NEWS create mode 100644 gnu/usr.bin/cpio/README create mode 100644 gnu/usr.bin/cpio/alloca.c create mode 100644 gnu/usr.bin/cpio/copyin.c create mode 100644 gnu/usr.bin/cpio/copyout.c create mode 100644 gnu/usr.bin/cpio/copypass.c create mode 100644 gnu/usr.bin/cpio/cpio.1 create mode 100644 gnu/usr.bin/cpio/cpio.h create mode 100644 gnu/usr.bin/cpio/cpiohdr.h create mode 100644 gnu/usr.bin/cpio/defer.c create mode 100644 gnu/usr.bin/cpio/defer.h create mode 100644 gnu/usr.bin/cpio/dirname.c create mode 100644 gnu/usr.bin/cpio/dstring.c create mode 100644 gnu/usr.bin/cpio/dstring.h create mode 100644 gnu/usr.bin/cpio/error.c create mode 100644 gnu/usr.bin/cpio/extern.h create mode 100644 gnu/usr.bin/cpio/filemode.c create mode 100644 gnu/usr.bin/cpio/filetypes.h create mode 100644 gnu/usr.bin/cpio/fnmatch.c create mode 100644 gnu/usr.bin/cpio/fnmatch.h create mode 100644 gnu/usr.bin/cpio/getopt.c rename gnu/usr.bin/{diff3 => cpio}/getopt.h (100%) rename gnu/usr.bin/{diff3 => cpio}/getopt1.c (100%) create mode 100644 gnu/usr.bin/cpio/global.c create mode 100644 gnu/usr.bin/cpio/idcache.c create mode 100644 gnu/usr.bin/cpio/main.c create mode 100644 gnu/usr.bin/cpio/makepath.c create mode 100644 gnu/usr.bin/cpio/rmt.h create mode 100644 gnu/usr.bin/cpio/rtapelib.c create mode 100644 gnu/usr.bin/cpio/stripslash.c create mode 100644 gnu/usr.bin/cpio/system.h create mode 100644 gnu/usr.bin/cpio/tar.c create mode 100644 gnu/usr.bin/cpio/tar.h create mode 100644 gnu/usr.bin/cpio/tarhdr.h create mode 100644 gnu/usr.bin/cpio/tcexparg.c create mode 100644 gnu/usr.bin/cpio/userspec.c create mode 100644 gnu/usr.bin/cpio/util.c create mode 100644 gnu/usr.bin/cpio/version.c create mode 100644 gnu/usr.bin/cpio/xmalloc.c create mode 100644 gnu/usr.bin/cpio/xstrdup.c delete mode 100644 gnu/usr.bin/cvs/lib/Makefile.in delete mode 100644 gnu/usr.bin/cvs/mkmodules/xxx create mode 100644 gnu/usr.bin/dc/dc.1 create mode 100644 gnu/usr.bin/diff/COPYING rename gnu/usr.bin/{diff3 => diff}/diff3.c (100%) delete mode 100644 gnu/usr.bin/diff/fnmatch.h create mode 100644 gnu/usr.bin/diff/sdiff.c delete mode 100644 gnu/usr.bin/diff3/getopt.c delete mode 100644 gnu/usr.bin/diff3/system.h delete mode 100644 gnu/usr.bin/diff3/version.c delete mode 100644 gnu/usr.bin/gcc1/Makefile delete mode 100644 gnu/usr.bin/gcc1/Makefile.symlinks delete mode 100644 gnu/usr.bin/gcc1/cc/Makefile delete mode 100644 gnu/usr.bin/gcc1/cc/cc.1 delete mode 100644 gnu/usr.bin/gcc1/cc/cc.c delete mode 100644 gnu/usr.bin/gcc1/cc1/.dbxinit delete mode 100644 gnu/usr.bin/gcc1/cc1/.gdbinit delete mode 100644 gnu/usr.bin/gcc1/cc1/Makefile delete mode 100644 gnu/usr.bin/gcc1/cc1/Makefile.gnu delete mode 100644 gnu/usr.bin/gcc1/cc1/PROBLEMS delete mode 100644 gnu/usr.bin/gcc1/cc1/PROJECTS delete mode 100644 gnu/usr.bin/gcc1/cc1/README delete mode 100644 gnu/usr.bin/gcc1/cc1/assert.h delete mode 100644 gnu/usr.bin/gcc1/cc1/basic-block.h delete mode 100644 gnu/usr.bin/gcc1/cc1/c-convert.c delete mode 100644 gnu/usr.bin/gcc1/cc1/c-decl.c delete mode 100644 gnu/usr.bin/gcc1/cc1/c-parse.gperf delete mode 100644 gnu/usr.bin/gcc1/cc1/c-parse.h delete mode 100644 gnu/usr.bin/gcc1/cc1/c-parse.y delete mode 100644 gnu/usr.bin/gcc1/cc1/c-tree.h delete mode 100644 gnu/usr.bin/gcc1/cc1/c-typeck.c delete mode 100644 gnu/usr.bin/gcc1/cc1/caller-save.c delete mode 100644 gnu/usr.bin/gcc1/cc1/combine.c delete mode 100644 gnu/usr.bin/gcc1/cc1/conditions.h delete mode 100644 gnu/usr.bin/gcc1/cc1/config/i386.md delete mode 100644 gnu/usr.bin/gcc1/cc1/config/out-i386.c delete mode 100644 gnu/usr.bin/gcc1/cc1/config/tm-att386.h delete mode 100644 gnu/usr.bin/gcc1/cc1/config/tm-bsd386.h delete mode 100644 gnu/usr.bin/gcc1/cc1/config/tm-compaq.h delete mode 100644 gnu/usr.bin/gcc1/cc1/config/tm-i386.h delete mode 100644 gnu/usr.bin/gcc1/cc1/config/tm-i386b.h delete mode 100644 gnu/usr.bin/gcc1/cc1/config/tm-i386b.h.save delete mode 100644 gnu/usr.bin/gcc1/cc1/config/tm-i386gas.h delete mode 100644 gnu/usr.bin/gcc1/cc1/config/tm-i386v.h delete mode 100644 gnu/usr.bin/gcc1/cc1/config/tm-i386v4.h delete mode 100644 gnu/usr.bin/gcc1/cc1/config/tm-i386vgas.h delete mode 100644 gnu/usr.bin/gcc1/cc1/config/xm-i386.h delete mode 100644 gnu/usr.bin/gcc1/cc1/config/xm-i386v.h delete mode 100644 gnu/usr.bin/gcc1/cc1/cse.c delete mode 100644 gnu/usr.bin/gcc1/cc1/dbxout.c delete mode 100644 gnu/usr.bin/gcc1/cc1/emit-rtl.c delete mode 100644 gnu/usr.bin/gcc1/cc1/explow.c delete mode 100644 gnu/usr.bin/gcc1/cc1/expmed.c delete mode 100644 gnu/usr.bin/gcc1/cc1/expr.c delete mode 100644 gnu/usr.bin/gcc1/cc1/expr.h delete mode 100644 gnu/usr.bin/gcc1/cc1/final.c delete mode 100644 gnu/usr.bin/gcc1/cc1/flags.h delete mode 100644 gnu/usr.bin/gcc1/cc1/flow.c delete mode 100644 gnu/usr.bin/gcc1/cc1/fold-const.c delete mode 100644 gnu/usr.bin/gcc1/cc1/gdbfiles.h delete mode 100644 gnu/usr.bin/gcc1/cc1/gencodes.c delete mode 100644 gnu/usr.bin/gcc1/cc1/genconfig.c delete mode 100644 gnu/usr.bin/gcc1/cc1/genemit.c delete mode 100644 gnu/usr.bin/gcc1/cc1/genextract.c delete mode 100644 gnu/usr.bin/gcc1/cc1/genflags.c delete mode 100644 gnu/usr.bin/gcc1/cc1/genoutput.c delete mode 100644 gnu/usr.bin/gcc1/cc1/genpeep.c delete mode 100644 gnu/usr.bin/gcc1/cc1/genrecog.c delete mode 100644 gnu/usr.bin/gcc1/cc1/global-alloc.c delete mode 100644 gnu/usr.bin/gcc1/cc1/gstdarg.h delete mode 100644 gnu/usr.bin/gcc1/cc1/gvarargs.h delete mode 100644 gnu/usr.bin/gcc1/cc1/hard-reg-set.h delete mode 100644 gnu/usr.bin/gcc1/cc1/input.h delete mode 100644 gnu/usr.bin/gcc1/cc1/integrate.c delete mode 100644 gnu/usr.bin/gcc1/cc1/jump.c delete mode 100644 gnu/usr.bin/gcc1/cc1/limits.h delete mode 100644 gnu/usr.bin/gcc1/cc1/local-alloc.c delete mode 100644 gnu/usr.bin/gcc1/cc1/loop.c delete mode 100644 gnu/usr.bin/gcc1/cc1/machmode.def delete mode 100644 gnu/usr.bin/gcc1/cc1/math-68881.h delete mode 100644 gnu/usr.bin/gcc1/cc1/move-if-change delete mode 100644 gnu/usr.bin/gcc1/cc1/obstack.c delete mode 100644 gnu/usr.bin/gcc1/cc1/obstack.h delete mode 100644 gnu/usr.bin/gcc1/cc1/optabs.c delete mode 100644 gnu/usr.bin/gcc1/cc1/output.h delete mode 100644 gnu/usr.bin/gcc1/cc1/print-tree.c delete mode 100644 gnu/usr.bin/gcc1/cc1/proto.h delete mode 100644 gnu/usr.bin/gcc1/cc1/real.h delete mode 100644 gnu/usr.bin/gcc1/cc1/recog.c delete mode 100644 gnu/usr.bin/gcc1/cc1/recog.h delete mode 100644 gnu/usr.bin/gcc1/cc1/regclass.c delete mode 100644 gnu/usr.bin/gcc1/cc1/regs.h delete mode 100644 gnu/usr.bin/gcc1/cc1/reload.c delete mode 100644 gnu/usr.bin/gcc1/cc1/reload.h delete mode 100644 gnu/usr.bin/gcc1/cc1/reload1.c delete mode 100644 gnu/usr.bin/gcc1/cc1/rtl.c delete mode 100644 gnu/usr.bin/gcc1/cc1/rtl.def delete mode 100644 gnu/usr.bin/gcc1/cc1/rtl.h delete mode 100644 gnu/usr.bin/gcc1/cc1/rtlanal.c delete mode 100644 gnu/usr.bin/gcc1/cc1/sdbout.c delete mode 100644 gnu/usr.bin/gcc1/cc1/stab.h delete mode 100644 gnu/usr.bin/gcc1/cc1/stmt.c delete mode 100644 gnu/usr.bin/gcc1/cc1/stor-layout.c delete mode 100644 gnu/usr.bin/gcc1/cc1/stupid.c delete mode 100644 gnu/usr.bin/gcc1/cc1/symout.c delete mode 100644 gnu/usr.bin/gcc1/cc1/symseg.h delete mode 100644 gnu/usr.bin/gcc1/cc1/toplev.c delete mode 100644 gnu/usr.bin/gcc1/cc1/tree.c delete mode 100644 gnu/usr.bin/gcc1/cc1/tree.def delete mode 100644 gnu/usr.bin/gcc1/cc1/tree.h delete mode 100644 gnu/usr.bin/gcc1/cc1/typeclass.h delete mode 100644 gnu/usr.bin/gcc1/cc1/varasm.c delete mode 100644 gnu/usr.bin/gcc1/cc1/version.c delete mode 100644 gnu/usr.bin/gcc1/cpp/Makefile delete mode 100644 gnu/usr.bin/gcc1/cpp/cexp.y delete mode 100644 gnu/usr.bin/gcc1/cpp/cpp.c delete mode 100644 gnu/usr.bin/gcc1/cpp/usr.bin.cpp.sh delete mode 100644 gnu/usr.bin/gcc1/gnulib/DIST/gnulib.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/DIST/gnulib2.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/Makefile delete mode 100644 gnu/usr.bin/gcc1/gnulib/g++/builtin_New.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/g++/builtin_del.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/g++/builtin_new.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/g++/eprintf.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/i386/DEFS.h delete mode 100644 gnu/usr.bin/gcc1/gnulib/i386/Makefile.machine delete mode 100644 gnu/usr.bin/gcc1/gnulib/i386/fixdfsi.s delete mode 100644 gnu/usr.bin/gcc1/gnulib/i386/fixunsdfsi.s delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/adddi3.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/anddi3.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/ashldi3.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/ashrdi3.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/bdiv.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/cmpdi2.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/divdi3.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/fixdfdi.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/fixunsdfdi.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/floatdidf.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/iordi3.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/longlong.h delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/lshldi3.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/lshrdi3.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/moddi3.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/muldi3.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/negdi2.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/one_cmpldi2.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/subdi3.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/ucmpdi2.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/udivdi3.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/umoddi3.c delete mode 100644 gnu/usr.bin/gcc1/gnulib/longlong/xordi3.c rename gnu/usr.bin/{gcc1/cc1 => gdb}/COPYING (100%) create mode 100644 gnu/usr.bin/gdb/ChangeLog create mode 100644 gnu/usr.bin/gdb/Gdbinit create mode 100644 gnu/usr.bin/gdb/Makefile create mode 100644 gnu/usr.bin/gdb/Makefile.dist create mode 100644 gnu/usr.bin/gdb/Projects create mode 100644 gnu/usr.bin/gdb/README.gnu create mode 100644 gnu/usr.bin/gdb/XGdbinit.samp create mode 100644 gnu/usr.bin/gdb/Xgdb.ad create mode 100644 gnu/usr.bin/gdb/blockframe.c create mode 100644 gnu/usr.bin/gdb/breakpoint.c create mode 100644 gnu/usr.bin/gdb/command.c create mode 100644 gnu/usr.bin/gdb/command.h create mode 100644 gnu/usr.bin/gdb/config/Makefile.i386 create mode 100644 gnu/usr.bin/gdb/config/default-dep.c create mode 100644 gnu/usr.bin/gdb/config/i386-dep.c create mode 100644 gnu/usr.bin/gdb/config/i386-pinsn.c create mode 100644 gnu/usr.bin/gdb/config/i386bsd-dep.c create mode 100644 gnu/usr.bin/gdb/config/m-i386-sv32.h create mode 100644 gnu/usr.bin/gdb/config/m-i386.h create mode 100644 gnu/usr.bin/gdb/config/m-i386bsd.h create mode 100644 gnu/usr.bin/gdb/config/m-i386g-sv32.h create mode 100644 gnu/usr.bin/gdb/config/m-i386gas.h create mode 100644 gnu/usr.bin/gdb/copying.c create mode 100644 gnu/usr.bin/gdb/core.c create mode 100644 gnu/usr.bin/gdb/cplus-dem.c create mode 100644 gnu/usr.bin/gdb/dbxread.c create mode 100644 gnu/usr.bin/gdb/defs.h create mode 100644 gnu/usr.bin/gdb/environ.c create mode 100644 gnu/usr.bin/gdb/environ.h create mode 100644 gnu/usr.bin/gdb/eval.c create mode 100644 gnu/usr.bin/gdb/expprint.c create mode 100644 gnu/usr.bin/gdb/expread.y create mode 100644 gnu/usr.bin/gdb/expression.h create mode 100644 gnu/usr.bin/gdb/findvar.c create mode 100644 gnu/usr.bin/gdb/frame.h create mode 100644 gnu/usr.bin/gdb/gdb.1 create mode 100644 gnu/usr.bin/gdb/getpagesize.h create mode 100644 gnu/usr.bin/gdb/infcmd.c create mode 100644 gnu/usr.bin/gdb/inferior.h create mode 100644 gnu/usr.bin/gdb/inflow.c create mode 100644 gnu/usr.bin/gdb/infrun.c create mode 100644 gnu/usr.bin/gdb/kgdb_proto.h create mode 100644 gnu/usr.bin/gdb/main.c create mode 100644 gnu/usr.bin/gdb/ngdb.i386/Makefile create mode 100644 gnu/usr.bin/gdb/obstack.c create mode 100644 gnu/usr.bin/gdb/obstack.h create mode 100644 gnu/usr.bin/gdb/printcmd.c create mode 100644 gnu/usr.bin/gdb/readline/ChangeLog create mode 100644 gnu/usr.bin/gdb/readline/Makefile.gnu create mode 100644 gnu/usr.bin/gdb/readline/chardefs.h create mode 100644 gnu/usr.bin/gdb/readline/emacs_keymap.c create mode 100644 gnu/usr.bin/gdb/readline/funmap.c create mode 100644 gnu/usr.bin/gdb/readline/history.c create mode 100644 gnu/usr.bin/gdb/readline/history.h create mode 100644 gnu/usr.bin/gdb/readline/keymaps.c create mode 100644 gnu/usr.bin/gdb/readline/keymaps.h create mode 100644 gnu/usr.bin/gdb/readline/readline.c create mode 100644 gnu/usr.bin/gdb/readline/readline.h create mode 100644 gnu/usr.bin/gdb/readline/vi_keymap.c create mode 100644 gnu/usr.bin/gdb/readline/vi_mode.c create mode 100644 gnu/usr.bin/gdb/regex.c create mode 100644 gnu/usr.bin/gdb/regex.h create mode 100644 gnu/usr.bin/gdb/remote-sl.c create mode 100644 gnu/usr.bin/gdb/remote.c create mode 100644 gnu/usr.bin/gdb/source.c rename gnu/usr.bin/{gcc1/cc1 => gdb}/stab.def (100%) create mode 100644 gnu/usr.bin/gdb/stack.c create mode 100644 gnu/usr.bin/gdb/symmisc.c create mode 100644 gnu/usr.bin/gdb/symseg.h create mode 100644 gnu/usr.bin/gdb/symtab.c create mode 100644 gnu/usr.bin/gdb/symtab.h create mode 100644 gnu/usr.bin/gdb/utils.c create mode 100644 gnu/usr.bin/gdb/valarith.c create mode 100644 gnu/usr.bin/gdb/valops.c create mode 100644 gnu/usr.bin/gdb/valprint.c create mode 100644 gnu/usr.bin/gdb/value.h create mode 100644 gnu/usr.bin/gdb/values.c create mode 100644 gnu/usr.bin/gdb/version.c create mode 100644 gnu/usr.bin/gdb/wait.h create mode 100644 gnu/usr.bin/gdb/xgdb/Makefile create mode 100644 gnu/usr.bin/gdb/xgdb/xgdb.c create mode 100644 gnu/usr.bin/grep/AUTHORS create mode 100644 gnu/usr.bin/grep/NEWS create mode 100644 gnu/usr.bin/grep/PROJECTS create mode 100644 gnu/usr.bin/grep/getpagesize.h create mode 100644 gnu/usr.bin/grep/grep.h create mode 100644 gnu/usr.bin/grep/kwset.c create mode 100644 gnu/usr.bin/grep/kwset.h create mode 100644 gnu/usr.bin/grep/obstack.c create mode 100644 gnu/usr.bin/grep/obstack.h create mode 100644 gnu/usr.bin/grep/search.c create mode 100644 gnu/usr.bin/grep/tests/check.sh delete mode 100644 gnu/usr.bin/grep/tests/regress.sh delete mode 100644 gnu/usr.bin/gzip/gzexe.in create mode 100644 gnu/usr.bin/gzip/unlzh.c delete mode 100644 gnu/usr.bin/gzip/zcmp create mode 100644 gnu/usr.bin/gzip/zgrep create mode 100644 gnu/usr.bin/gzip/zgrep.1 create mode 100644 gnu/usr.bin/ld/Makefile create mode 100644 gnu/usr.bin/ld/cplus-dem.c create mode 100644 gnu/usr.bin/ld/ld.c create mode 100644 gnu/usr.bin/ld/symseg.h delete mode 100644 gnu/usr.bin/man/apropos/apropos delete mode 100644 gnu/usr.bin/man/apropos/apropos.1 create mode 100644 gnu/usr.bin/man/catman/Makefile create mode 100644 gnu/usr.bin/man/catman/catman delete mode 100644 gnu/usr.bin/man/lib/config.h create mode 100644 gnu/usr.bin/man/lib/config.h_dist delete mode 100644 gnu/usr.bin/man/lib/gripes.po delete mode 100644 gnu/usr.bin/man/lib/util.po delete mode 100644 gnu/usr.bin/man/makewhatis/makewhatis delete mode 100644 gnu/usr.bin/man/man/man.1 delete mode 100644 gnu/usr.bin/man/manpath/manpath delete mode 100644 gnu/usr.bin/man/manpath/manpath.1 delete mode 100644 gnu/usr.bin/man/whatis/whatis delete mode 100644 gnu/usr.bin/man/whatis/whatis.1 delete mode 100644 gnu/usr.bin/rcs/rcsclean/rcsclean delete mode 100644 gnu/usr.bin/rcs/rcsclean/rcsclean.0 create mode 100644 gnu/usr.bin/sdiff/Makefile delete mode 100644 gnu/usr.bin/tar/y.tab.h create mode 100644 include/err.h create mode 100644 include/fnmatch.h create mode 100644 include/mpool.h create mode 100644 include/rpcsvc/Makefile create mode 100644 include/rpcsvc/bootparam_prot.x create mode 100644 include/rpcsvc/klm_prot.x create mode 100644 include/rpcsvc/mount.x create mode 100644 include/rpcsvc/nfs_prot.x create mode 100644 include/rpcsvc/nlm_prot.x create mode 100644 include/rpcsvc/rex.x create mode 100644 include/rpcsvc/rnusers.x create mode 100644 include/rpcsvc/rquota.x create mode 100644 include/rpcsvc/rstat.x create mode 100644 include/rpcsvc/rwall.x create mode 100644 include/rpcsvc/sm_inter.x create mode 100644 include/rpcsvc/spray.x create mode 100644 include/rpcsvc/yp.x create mode 100644 include/rpcsvc/yp_prot.h create mode 100644 include/rpcsvc/ypclnt.h create mode 100644 include/rpcsvc/yppasswd.x create mode 100644 lib/libc/db/PORT/Makefile create mode 100644 lib/libc/db/PORT/README create mode 100644 lib/libc/db/PORT/clib/memmove.c create mode 100644 lib/libc/db/PORT/clib/mktemp.c create mode 100644 lib/libc/db/PORT/clib/realloc.c create mode 100644 lib/libc/db/PORT/clib/snprintf.c create mode 100644 lib/libc/db/PORT/include/cdefs.h create mode 100644 lib/libc/db/PORT/include/compat.h create mode 100644 lib/libc/db/PORT/include/db.h create mode 100644 lib/libc/db/PORT/include/mpool.h create mode 100644 lib/libc/db/PORT/include/ndbm.h create mode 100644 lib/libc/db/VERSION delete mode 100644 lib/libc/db/btree/big.c create mode 100644 lib/libc/db/btree/bt_close.c create mode 100644 lib/libc/db/btree/bt_conv.c create mode 100644 lib/libc/db/btree/bt_debug.c create mode 100644 lib/libc/db/btree/bt_delete.c create mode 100644 lib/libc/db/btree/bt_get.c create mode 100644 lib/libc/db/btree/bt_open.c create mode 100644 lib/libc/db/btree/bt_overflow.c create mode 100644 lib/libc/db/btree/bt_page.c create mode 100644 lib/libc/db/btree/bt_put.c create mode 100644 lib/libc/db/btree/bt_search.c create mode 100644 lib/libc/db/btree/bt_seq.c create mode 100644 lib/libc/db/btree/bt_split.c create mode 100644 lib/libc/db/btree/bt_stack.c create mode 100644 lib/libc/db/btree/bt_utils.c delete mode 100644 lib/libc/db/btree/btree.c delete mode 100644 lib/libc/db/btree/delete.c create mode 100644 lib/libc/db/btree/extern.h delete mode 100644 lib/libc/db/btree/insert.c delete mode 100644 lib/libc/db/btree/lrucache.c delete mode 100644 lib/libc/db/btree/lrucache.h delete mode 100644 lib/libc/db/btree/lruhash.c delete mode 100644 lib/libc/db/btree/lrutils.c delete mode 100644 lib/libc/db/btree/search.c delete mode 100644 lib/libc/db/btree/seq.c delete mode 100644 lib/libc/db/btree/split.c delete mode 100644 lib/libc/db/btree/storage.c delete mode 100644 lib/libc/db/btree/tests/words.c delete mode 100644 lib/libc/db/btree/updutils.c delete mode 100644 lib/libc/db/btree/utils.c delete mode 100644 lib/libc/db/db.3 create mode 100644 lib/libc/db/db/Makefile.inc create mode 100644 lib/libc/db/db/db.c create mode 100644 lib/libc/db/doc/btree.3.ps create mode 100644 lib/libc/db/doc/dbopen.3.ps create mode 100644 lib/libc/db/doc/hash.3.ps rename lib/libc/db/{hash => doc}/hash.ps (100%) create mode 100644 lib/libc/db/doc/mpool.3.ps create mode 100644 lib/libc/db/doc/recno.3.ps delete mode 100644 lib/libc/db/hash/bigkey.c delete mode 100644 lib/libc/db/hash/buf.c delete mode 100644 lib/libc/db/hash/dynahash.c create mode 100644 lib/libc/db/hash/extern.h create mode 100644 lib/libc/db/hash/hash.c create mode 100644 lib/libc/db/hash/hash_bigkey.c create mode 100644 lib/libc/db/hash/hash_buf.c create mode 100644 lib/libc/db/hash/hash_func.c create mode 100644 lib/libc/db/hash/hash_log2.c create mode 100644 lib/libc/db/hash/hash_page.c delete mode 100644 lib/libc/db/hash/hfunc.c delete mode 100644 lib/libc/db/hash/log2.c delete mode 100644 lib/libc/db/hash/page.c delete mode 100644 lib/libc/db/hash/tests/tcreat3.c delete mode 100644 lib/libc/db/hash/tests/tdel.c delete mode 100644 lib/libc/db/hash/tests/thash4.c delete mode 100644 lib/libc/db/hash/tests/tread2.c delete mode 100644 lib/libc/db/hash/tests/tseq.c delete mode 100644 lib/libc/db/hash/tests/tverify.c create mode 100644 lib/libc/db/man/Makefile.inc create mode 100644 lib/libc/db/man/btree.3 create mode 100644 lib/libc/db/man/dbopen.3 create mode 100644 lib/libc/db/man/hash.3 create mode 100644 lib/libc/db/man/mpool.3 create mode 100644 lib/libc/db/man/recno.3 create mode 100644 lib/libc/db/mpool/Makefile.inc create mode 100644 lib/libc/db/mpool/README create mode 100644 lib/libc/db/mpool/mpool.c create mode 100644 lib/libc/db/recno/Makefile.inc create mode 100644 lib/libc/db/recno/extern.h create mode 100644 lib/libc/db/recno/rec_close.c create mode 100644 lib/libc/db/recno/rec_delete.c create mode 100644 lib/libc/db/recno/rec_get.c create mode 100644 lib/libc/db/recno/rec_open.c create mode 100644 lib/libc/db/recno/rec_put.c create mode 100644 lib/libc/db/recno/rec_search.c create mode 100644 lib/libc/db/recno/rec_seq.c create mode 100644 lib/libc/db/recno/rec_utils.c create mode 100644 lib/libc/db/recno/recno.h create mode 100644 lib/libc/db/test/Makefile create mode 100644 lib/libc/db/test/README create mode 100644 lib/libc/db/test/btree.tests/main.c create mode 100644 lib/libc/db/test/dbtest.c create mode 100644 lib/libc/db/test/hash.tests/driver2.c create mode 100644 lib/libc/db/test/hash.tests/makedb.sh create mode 100644 lib/libc/db/test/hash.tests/tcreat3.c create mode 100644 lib/libc/db/test/hash.tests/tdel.c create mode 100644 lib/libc/db/test/hash.tests/testit create mode 100644 lib/libc/db/test/hash.tests/thash4.c create mode 100644 lib/libc/db/test/hash.tests/tread2.c create mode 100644 lib/libc/db/test/hash.tests/tseq.c create mode 100644 lib/libc/db/test/hash.tests/tverify.c create mode 100644 lib/libc/db/test/run.test create mode 100644 lib/libc/gen/README.crypt create mode 100644 lib/libc/gen/assert.c delete mode 100644 lib/libc/gen/closedir.c create mode 100644 lib/libc/gen/crypt.c delete mode 100644 lib/libc/gen/crypt_dummy.c create mode 100644 lib/libc/gen/directory.c create mode 100644 lib/libc/gen/err.3 create mode 100644 lib/libc/gen/err.c create mode 100644 lib/libc/gen/getcap.3 create mode 100644 lib/libc/gen/getcap.c create mode 100644 lib/libc/gen/infinity.c create mode 100644 lib/libc/gen/insque.3 create mode 100644 lib/libc/gen/insque.c delete mode 100644 lib/libc/gen/opendir.c delete mode 100644 lib/libc/gen/readdir.c delete mode 100644 lib/libc/gen/rewinddir.c delete mode 100644 lib/libc/gen/seekdir.c create mode 100644 lib/libc/gen/shmat.c create mode 100644 lib/libc/gen/shmctl.c create mode 100644 lib/libc/gen/shmdt.c create mode 100644 lib/libc/gen/shmget.c create mode 100644 lib/libc/gen/tcsendbreak.3 delete mode 100644 lib/libc/gen/telldir.c create mode 100644 lib/libc/i386/gen/sigsetjmp.s delete mode 100644 lib/libc/i386/stdlib/atof.c create mode 100644 lib/libc/i386/stdlib/div.s create mode 100644 lib/libc/i386/stdlib/labs.s create mode 100644 lib/libc/i386/stdlib/ldiv.s create mode 100644 lib/libc/i386/string/bcmp.s create mode 100644 lib/libc/i386/string/ffs.s create mode 100644 lib/libc/i386/string/index.s create mode 100644 lib/libc/i386/string/memchr.s create mode 100644 lib/libc/i386/string/memcmp.s create mode 100644 lib/libc/i386/string/memmove.s create mode 100644 lib/libc/i386/string/memset.s create mode 100644 lib/libc/i386/string/rindex.s create mode 100644 lib/libc/i386/string/strcat.s create mode 100644 lib/libc/i386/string/strchr.s create mode 100644 lib/libc/i386/string/strcmp.s create mode 100644 lib/libc/i386/string/strcpy.s create mode 100644 lib/libc/i386/string/strlen.s create mode 100644 lib/libc/i386/string/strncmp.s create mode 100644 lib/libc/i386/string/strrchr.s create mode 100644 lib/libc/stdlib/_rand48.c create mode 100644 lib/libc/stdlib/drand48.c create mode 100644 lib/libc/stdlib/erand48.c create mode 100644 lib/libc/stdlib/jrand48.c create mode 100644 lib/libc/stdlib/lcong48.c create mode 100644 lib/libc/stdlib/lrand48.c create mode 100644 lib/libc/stdlib/mrand48.c create mode 100644 lib/libc/stdlib/nrand48.c create mode 100644 lib/libc/stdlib/rand48.3 create mode 100644 lib/libc/stdlib/rand48.h create mode 100644 lib/libc/stdlib/seed48.c create mode 100644 lib/libc/stdlib/srand48.c create mode 100644 lib/libc/stdlib/strtod.c create mode 100644 lib/libc/sys/getdomainname.2 create mode 100644 lib/libc/sys/uname.2 create mode 100644 lib/libcrypt/Makefile rename lib/{libtelnet => libcrypt}/auth-proto.h (100%) rename lib/{libtelnet => libcrypt}/auth.c (100%) rename lib/{libtelnet => libcrypt}/auth.h (100%) create mode 100644 lib/libcrypt/crypt.c rename lib/{libtelnet => libcrypt}/enc-proto.h (100%) rename lib/{libtelnet => libcrypt}/enc_des.c (100%) rename lib/{libtelnet => libcrypt}/encrypt.c (100%) rename lib/{libtelnet => libcrypt}/encrypt.h (100%) rename lib/{libtelnet => libcrypt}/kerberos.c (100%) rename lib/{libtelnet => libcrypt}/kerberos5.c (100%) rename lib/{libtelnet => libcrypt}/key-proto.h (100%) rename lib/{libtelnet => libcrypt}/krb_des.c (100%) create mode 100644 lib/libcrypt/misc-proto.h create mode 100644 lib/libcrypt/misc.c create mode 100644 lib/libcrypt/misc.h create mode 100644 lib/libm/common_source/erf.c create mode 100644 lib/libm/common_source/gamma.c create mode 100644 lib/libm/common_source/j0.c create mode 100644 lib/libm/common_source/j1.c create mode 100644 lib/libm/common_source/jn.c create mode 100644 lib/libm/common_source/lgamma.c create mode 100644 lib/librpc/man/Makefile create mode 100644 lib/librpc/man/man1/Makefile create mode 100644 lib/librpc/man/man3/Makefile create mode 100644 lib/librpc/man/man5/Makefile create mode 100644 lib/librpc/man/man8/Makefile delete mode 100644 lib/librpc/rpc/rpc.order create mode 100644 lib/librpcsvc/Makefile create mode 100644 libexec/elvispreserve/prsvunix.c delete mode 100644 libexec/getty/ttydefaults.c create mode 100644 libexec/pppd/Makefile create mode 100644 libexec/pppd/args.h create mode 100644 libexec/pppd/callout.h create mode 100644 libexec/pppd/chap.c create mode 100644 libexec/pppd/chap.h create mode 100644 libexec/pppd/fsm.c create mode 100644 libexec/pppd/fsm.h create mode 100644 libexec/pppd/ipcp.c create mode 100644 libexec/pppd/ipcp.h create mode 100644 libexec/pppd/lcp.c create mode 100644 libexec/pppd/lcp.h create mode 100644 libexec/pppd/logwtmp.c create mode 100644 libexec/pppd/magic.c create mode 100644 libexec/pppd/magic.h create mode 100644 libexec/pppd/main.c create mode 100644 libexec/pppd/md5.c create mode 100644 libexec/pppd/md5.h create mode 100644 libexec/pppd/md5driver.c create mode 100644 libexec/pppd/patchlevel.h create mode 100644 libexec/pppd/pathnames.h create mode 100644 libexec/pppd/pppd.8 create mode 100644 libexec/pppd/pppd.h create mode 100644 libexec/pppd/upap.c create mode 100644 libexec/pppd/upap.h create mode 100644 libexec/rpc.rstatd/Makefile create mode 100644 libexec/rpc.rstatd/rpc.rstatd.8 create mode 100644 libexec/rpc.rstatd/rstat_proc.c create mode 100644 libexec/rpc.rstatd/rstatd.c create mode 100644 libexec/rpc.rusersd/Makefile create mode 100644 libexec/rpc.rusersd/rpc.rusersd.8 create mode 100644 libexec/rpc.rusersd/rusers_proc.c create mode 100644 libexec/rpc.rusersd/rusersd.c create mode 100644 libexec/rpc.rwalld/Makefile create mode 100644 libexec/rpc.rwalld/rpc.rwalld.8 create mode 100644 libexec/rpc.rwalld/rwalld.c create mode 100644 sbin/XNSrouted/Makefile create mode 100644 sbin/XNSrouted/XNSrouted.8 create mode 100644 sbin/XNSrouted/af.c create mode 100644 sbin/XNSrouted/af.h create mode 100644 sbin/XNSrouted/defs.h create mode 100644 sbin/XNSrouted/if.c create mode 100644 sbin/XNSrouted/input.c create mode 100644 sbin/XNSrouted/interface.h create mode 100644 sbin/XNSrouted/main.c create mode 100644 sbin/XNSrouted/output.c create mode 100644 sbin/XNSrouted/protocol.h create mode 100644 sbin/XNSrouted/startup.c create mode 100644 sbin/XNSrouted/table.h create mode 100644 sbin/XNSrouted/tables.c create mode 100644 sbin/XNSrouted/timer.c create mode 100644 sbin/XNSrouted/tools/query.c create mode 100644 sbin/XNSrouted/trace.c create mode 100644 sbin/XNSrouted/trace.h rename sbin/disklabel/{disklabel.5.5 => disklabel.5} (100%) create mode 100644 sbin/init.bsdi/Makefile create mode 100644 sbin/init.bsdi/README create mode 100644 sbin/init.bsdi/init.8 create mode 100644 sbin/init.bsdi/init.c create mode 100644 sbin/init.bsdi/pathnames.h create mode 100644 sbin/init.chmr/Makefile create mode 100644 sbin/init.chmr/README create mode 100644 sbin/init.chmr/cf_defs.h create mode 100644 sbin/init.chmr/cf_table.c create mode 100644 sbin/init.chmr/configure.c create mode 100644 sbin/init.chmr/fake_syslog.c create mode 100644 sbin/init.chmr/init.8 create mode 100644 sbin/init.chmr/init.c create mode 100644 sbin/init.chmr/init.conf create mode 100644 sbin/init.chmr/init.h create mode 100644 sbin/init.chmr/libutil.h create mode 100644 sbin/init.chmr/prototypes.h create mode 100644 sbin/init.chmr/ttytab.c create mode 100644 sbin/init.chmr/utils.c create mode 100644 sbin/mount_isofs/mount_isofs.8 create mode 100644 sbin/st/Makefile create mode 100644 sbin/st/st.8 create mode 100644 sbin/st/st.c create mode 100644 share/doc/usd/04.csh/csh.a delete mode 100644 share/man/makewhatis.sed create mode 100644 share/man/man3/fpgetround.3 create mode 100644 share/man/man4/ddb.4 create mode 100644 share/man/man4/man4.i386/com.4 create mode 100644 share/man/man4/man4.i386/keyboard.4 create mode 100644 share/man/man4/man4.i386/lpa.4 create mode 100644 share/man/man4/man4.i386/lpt.4 create mode 100644 share/man/man4/man4.i386/mse.4 create mode 100644 share/man/man4/man4.i386/npx.4 create mode 100644 share/man/man4/man4.i386/screen.4 create mode 100644 share/man/man4/man4.i386/sio.4 create mode 100644 share/man/man4/sd.4 create mode 100644 share/man/man4/st.4 create mode 100644 share/man/man4/termios.4 create mode 100644 share/syscons/Makefile create mode 100644 share/syscons/fonts/Makefile create mode 100644 share/syscons/fonts/alt-8x14 create mode 100644 share/syscons/fonts/alt-8x16 create mode 100644 share/syscons/fonts/alt-8x8 create mode 100644 share/syscons/fonts/alt8x16.fnt create mode 100644 share/syscons/fonts/altc-8x16 create mode 100644 share/syscons/fonts/cp850-8x14 create mode 100644 share/syscons/fonts/cp850-8x16 create mode 100644 share/syscons/fonts/cp850-8x8 create mode 100644 share/syscons/fonts/cp865-8x14 create mode 100644 share/syscons/fonts/cp865-8x16 create mode 100644 share/syscons/fonts/cp865-8x8 create mode 100644 share/syscons/fonts/iso-8x14 create mode 100644 share/syscons/fonts/iso-8x16 create mode 100644 share/syscons/fonts/iso-8x8 create mode 100644 share/syscons/fonts/koi8-8x14 create mode 100644 share/syscons/fonts/koi8-8x16 create mode 100644 share/syscons/fonts/koi8-8x8 create mode 100644 share/syscons/fonts/koi8c-8x16 create mode 100644 share/syscons/keymaps/Makefile create mode 100644 share/syscons/keymaps/danish.cp865 create mode 100644 share/syscons/keymaps/danish.iso create mode 100644 share/syscons/keymaps/fff create mode 100644 share/syscons/keymaps/german.cp850 create mode 100644 share/syscons/keymaps/german.iso create mode 100644 share/syscons/keymaps/mkkbdfil.c create mode 100644 share/syscons/keymaps/ru.koi8.map create mode 100644 share/syscons/keymaps/rus.koi8.map create mode 100644 share/syscons/keymaps/swedish.cp850 create mode 100644 share/syscons/keymaps/swedish.iso create mode 100644 share/syscons/keymaps/uk.cp850 create mode 100644 share/syscons/keymaps/uk.iso create mode 100644 share/syscons/keymaps/us.iso create mode 100644 share/syscons/scrnmaps/Makefile create mode 100644 share/syscons/scrnmaps/koi82alt.scr create mode 100644 share/zoneinfo/datfiles/russia create mode 100644 sys/compile/.keep_me create mode 100644 sys/i386/boot/asm.S delete mode 100644 sys/i386/boot/asm.s create mode 100644 sys/i386/boot/bios.S delete mode 100644 sys/i386/boot/bios.s create mode 100644 sys/i386/boot/boot2.S delete mode 100644 sys/i386/boot/boot2.s create mode 100644 sys/i386/boot/start.S delete mode 100644 sys/i386/boot/start.s delete mode 100644 sys/i386/conf/AHATEST delete mode 100644 sys/i386/conf/AHBTEST delete mode 100644 sys/i386/conf/BTTEST delete mode 100644 sys/i386/conf/CODRV delete mode 100644 sys/i386/conf/DEVEL create mode 100644 sys/i386/conf/GENERICBT delete mode 100644 sys/i386/conf/GENERICISA delete mode 100644 sys/i386/conf/LARGE create mode 100644 sys/i386/conf/LINT delete mode 100644 sys/i386/conf/MINITERM delete mode 100644 sys/i386/conf/ODYSSEUS delete mode 100644 sys/i386/conf/PHOENIX delete mode 100644 sys/i386/conf/SMALL delete mode 100644 sys/i386/conf/SPACE_HEATER delete mode 100644 sys/i386/conf/SUMNER create mode 100644 sys/i386/conf/SYSCONS delete mode 100644 sys/i386/conf/UHATEST create mode 100644 sys/i386/doc/config_options.doc create mode 100644 sys/i386/doc/ed.relnotes create mode 100644 sys/i386/doc/sound.doc create mode 100644 sys/i386/doc/vm_layout.doc create mode 100644 sys/i386/i386/ns_cksum.c create mode 100644 sys/i386/include/console.h create mode 100644 sys/i386/include/cputypes.h create mode 100644 sys/i386/include/floatingpoint.h create mode 100644 sys/i386/include/ioctl_pc.h create mode 100644 sys/i386/include/soundcard.h create mode 100644 sys/i386/include/sysarch.h delete mode 100644 sys/i386/isa/as.c delete mode 100644 sys/i386/isa/asreg.h delete mode 100644 sys/i386/isa/codrv/DOC/CHANGES.vak delete mode 100644 sys/i386/isa/codrv/DOC/CO_HISTORY delete mode 100644 sys/i386/isa/codrv/DOC/FEATURES delete mode 100644 sys/i386/isa/codrv/DOC/README delete mode 100644 sys/i386/isa/codrv/ETC/etc.ttys delete mode 100644 sys/i386/isa/codrv/co_codrv1.c delete mode 100644 sys/i386/isa/codrv/co_cons.c delete mode 100644 sys/i386/isa/codrv/co_hdr.h delete mode 100644 sys/i386/isa/codrv/co_kbd.c delete mode 100644 sys/i386/isa/codrv/co_mini.c delete mode 100644 sys/i386/isa/codrv/co_pc3.c delete mode 100644 sys/i386/isa/codrv/co_vga.c delete mode 100644 sys/i386/isa/codrv/co_vty.c delete mode 100644 sys/i386/isa/ic/ds8390.h create mode 100644 sys/i386/isa/ic/i82586.h delete mode 100644 sys/i386/isa/if_ec.c delete mode 100644 sys/i386/isa/if_ec.h create mode 100644 sys/i386/isa/if_ed.c create mode 100644 sys/i386/isa/if_edreg.h create mode 100644 sys/i386/isa/if_ie.c create mode 100644 sys/i386/isa/if_iereg.h delete mode 100644 sys/i386/isa/if_ne.c delete mode 100644 sys/i386/isa/if_nereg.h delete mode 100644 sys/i386/isa/if_we.c delete mode 100644 sys/i386/isa/if_wereg.h create mode 100644 sys/i386/isa/iso8859.font create mode 100644 sys/i386/isa/kbdtables.h create mode 100644 sys/i386/isa/mcd.c create mode 100644 sys/i386/isa/mcdreg.h create mode 100644 sys/i386/isa/mse.c create mode 100644 sys/i386/isa/sound/COPYING create mode 100644 sys/i386/isa/sound/HOWTO_MIDI create mode 100644 sys/i386/isa/sound/README create mode 100644 sys/i386/isa/sound/RELNOTES create mode 100644 sys/i386/isa/sound/RELNOTES.Linux create mode 100644 sys/i386/isa/sound/adlib_card.c create mode 100644 sys/i386/isa/sound/audio.c create mode 100644 sys/i386/isa/sound/debug.h create mode 100644 sys/i386/isa/sound/dev_table.c create mode 100644 sys/i386/isa/sound/dev_table.h create mode 100644 sys/i386/isa/sound/dmabuf.c create mode 100644 sys/i386/isa/sound/dsp.c create mode 100644 sys/i386/isa/sound/finetune.h create mode 100644 sys/i386/isa/sound/gus_card.c create mode 100644 sys/i386/isa/sound/gus_hw.h create mode 100644 sys/i386/isa/sound/gus_midi.c create mode 100644 sys/i386/isa/sound/gus_vol.c create mode 100644 sys/i386/isa/sound/gus_wave.c create mode 100644 sys/i386/isa/sound/gustest/Makefile create mode 100644 sys/i386/isa/sound/gustest/Readme create mode 100644 sys/i386/isa/sound/gustest/gmidi.h create mode 100644 sys/i386/isa/sound/gustest/gmod.c create mode 100644 sys/i386/isa/sound/gustest/gpatinfo.c create mode 100644 sys/i386/isa/sound/gustest/gusload.c create mode 100644 sys/i386/isa/sound/gustest/midithru.c create mode 100644 sys/i386/isa/sound/gustest/pmtest.c create mode 100644 sys/i386/isa/sound/local.h create mode 100644 sys/i386/isa/sound/midi.c create mode 100644 sys/i386/isa/sound/midibuf.c create mode 100644 sys/i386/isa/sound/mpu401.c create mode 100644 sys/i386/isa/sound/opl3.c create mode 100644 sys/i386/isa/sound/opl3.h create mode 100644 sys/i386/isa/sound/os.h create mode 100644 sys/i386/isa/sound/pas.h create mode 100644 sys/i386/isa/sound/pas2_card.c create mode 100644 sys/i386/isa/sound/pas2_midi.c create mode 100644 sys/i386/isa/sound/pas2_mixer.c create mode 100644 sys/i386/isa/sound/pas2_pcm.c create mode 100644 sys/i386/isa/sound/patmgr.c create mode 100644 sys/i386/isa/sound/pro_midi.c create mode 100644 sys/i386/isa/sound/sb_card.c create mode 100644 sys/i386/isa/sound/sb_dsp.c create mode 100644 sys/i386/isa/sound/sequencer.c create mode 100644 sys/i386/isa/sound/sound_calls.h create mode 100644 sys/i386/isa/sound/sound_config.h create mode 100644 sys/i386/isa/sound/soundcard.c create mode 100644 sys/i386/isa/sound/soundcard.h create mode 100644 sys/i386/isa/sound/tuning.h create mode 100644 sys/i386/isa/sound/ulaw.h create mode 100644 sys/i386/isa/sound/ultrasound.h create mode 100644 sys/i386/isa/syscons.c delete mode 100644 sys/i386/stand/wdboot.x create mode 100644 sys/isofs/iso_rrip.h create mode 100644 sys/isofs/isofs_rrip.c create mode 100644 sys/isofs/isofs_rrip.h create mode 100644 sys/kern/Makefile delete mode 100644 sys/kern/kern_execve create mode 100644 sys/kern/makesyscalls.sh create mode 100644 sys/kern/syscalls.master create mode 100644 sys/net/if_ppp.c create mode 100644 sys/net/if_ppp.h create mode 100644 sys/net/ppp.h delete mode 100644 sys/scsi/README.AHA1742 create mode 100644 sys/scsi/scsi_generic.h create mode 100644 sys/scsi/sg.c create mode 100644 sys/sys/ieeefp.h delete mode 100644 sys/sys/ioctl_pc.h create mode 100644 sys/sys/map.h create mode 100644 sys/sys/sgio.h create mode 100644 sys/sys/utsname.h rename usr.bin/ar/{ar.5.5 => ar.5} (100%) create mode 100644 usr.bin/chat/Example create mode 100644 usr.bin/chat/Makefile create mode 100644 usr.bin/chat/README create mode 100644 usr.bin/chat/chat.c create mode 100644 usr.bin/chat/fix-cua create mode 100644 usr.bin/chat/ppp-off create mode 100644 usr.bin/chat/ppp-on create mode 100644 usr.bin/chat/unlock create mode 100644 usr.bin/crontab/crontab.5 delete mode 100644 usr.bin/crontab/crontab.5.5 create mode 100644 usr.bin/elvis/doc/Makedoc create mode 100644 usr.bin/elvis/doc/Makefile create mode 100644 usr.bin/elvis/doc/col create mode 100644 usr.bin/elvis/doc/ctags.man create mode 100644 usr.bin/elvis/doc/elvis.man create mode 100644 usr.bin/elvis/doc/elvprsv.man create mode 100644 usr.bin/elvis/doc/elvrec.man create mode 100644 usr.bin/elvis/doc/fmt.man create mode 100644 usr.bin/elvis/doc/makefile.ms create mode 100644 usr.bin/elvis/doc/ref.man delete mode 100644 usr.bin/elvis/prsvunix.c delete mode 100644 usr.bin/elvisrecover/README create mode 100644 usr.bin/env/env.1 create mode 100644 usr.bin/file/MAINT delete mode 100644 usr.bin/file/Makefile.ian create mode 100644 usr.bin/file/compress.c rename usr.bin/file/magdir/{aa => Header} (100%) create mode 100644 usr.bin/file/magdir/Localstuff delete mode 100644 usr.bin/file/magdir/aalocal create mode 100644 usr.bin/file/magdir/alliant create mode 100644 usr.bin/file/magdir/apl create mode 100644 usr.bin/file/magdir/ar delete mode 100644 usr.bin/file/magdir/arc delete mode 100644 usr.bin/file/magdir/archive create mode 100644 usr.bin/file/magdir/att3b create mode 100644 usr.bin/file/magdir/audio create mode 100644 usr.bin/file/magdir/blit create mode 100644 usr.bin/file/magdir/bsdi delete mode 100644 usr.bin/file/magdir/c create mode 100644 usr.bin/file/magdir/c-lang create mode 100644 usr.bin/file/magdir/clipper create mode 100644 usr.bin/file/magdir/cpio create mode 100644 usr.bin/file/magdir/diamond create mode 100644 usr.bin/file/magdir/dump create mode 100644 usr.bin/file/magdir/elf create mode 100644 usr.bin/file/magdir/encore create mode 100644 usr.bin/file/magdir/floppy.raw create mode 100644 usr.bin/file/magdir/freebsd create mode 100644 usr.bin/file/magdir/hp create mode 100644 usr.bin/file/magdir/ibm370 create mode 100644 usr.bin/file/magdir/ibm6000 create mode 100644 usr.bin/file/magdir/iff create mode 100644 usr.bin/file/magdir/images create mode 100644 usr.bin/file/magdir/interleaf create mode 100644 usr.bin/file/magdir/iris create mode 100644 usr.bin/file/magdir/ispell create mode 100644 usr.bin/file/magdir/lex create mode 100644 usr.bin/file/magdir/lif create mode 100644 usr.bin/file/magdir/linux create mode 100644 usr.bin/file/magdir/microsoft create mode 100644 usr.bin/file/magdir/mips delete mode 100644 usr.bin/file/magdir/misc delete mode 100644 usr.bin/file/magdir/misc2 create mode 100644 usr.bin/file/magdir/mkid create mode 100644 usr.bin/file/magdir/mmdf create mode 100644 usr.bin/file/magdir/motorola create mode 100644 usr.bin/file/magdir/ms-dos create mode 100644 usr.bin/file/magdir/ncr create mode 100644 usr.bin/file/magdir/netbsd create mode 100644 usr.bin/file/magdir/news delete mode 100644 usr.bin/file/magdir/olda.out create mode 100644 usr.bin/file/magdir/pbm create mode 100644 usr.bin/file/magdir/pdp create mode 100644 usr.bin/file/magdir/pgp create mode 100644 usr.bin/file/magdir/pkgadd create mode 100644 usr.bin/file/magdir/plus5 create mode 100644 usr.bin/file/magdir/psdbms create mode 100644 usr.bin/file/magdir/pyramid delete mode 100644 usr.bin/file/magdir/rasterfile create mode 100644 usr.bin/file/magdir/sc create mode 100644 usr.bin/file/magdir/sendmail create mode 100644 usr.bin/file/magdir/sgml create mode 100644 usr.bin/file/magdir/sunraster create mode 100644 usr.bin/file/magdir/terminfo create mode 100644 usr.bin/file/magdir/tex delete mode 100644 usr.bin/file/magdir/tower create mode 100644 usr.bin/file/magdir/troff create mode 100644 usr.bin/file/magdir/unknown create mode 100644 usr.bin/file/magdir/uuencode create mode 100644 usr.bin/file/magdir/vax delete mode 100644 usr.bin/file/magdir/vax.byteswap create mode 100644 usr.bin/file/magdir/visx create mode 100644 usr.bin/file/magdir/x11 delete mode 100644 usr.bin/file/magdir/xenix create mode 100644 usr.bin/file/magdir/zilog delete mode 100644 usr.bin/file/magic create mode 100644 usr.bin/file/patchlevel.h create mode 100644 usr.bin/getopt/Makefile create mode 100644 usr.bin/getopt/README create mode 100644 usr.bin/getopt/getopt.1 create mode 100644 usr.bin/getopt/getopt.c delete mode 100644 usr.bin/join/COPYING delete mode 100644 usr.bin/join/getopt.c delete mode 100644 usr.bin/join/getopt.h delete mode 100644 usr.bin/join/getopt1.c delete mode 100644 usr.bin/lex/lib/ln.order create mode 100644 usr.bin/locate/locate/updatedb.sh create mode 100644 usr.bin/login/klogin.c create mode 100644 usr.bin/m4/int2str.c create mode 100644 usr.bin/m4/m4.1 create mode 100644 usr.bin/m4/ourlims.h delete mode 100644 usr.bin/man/Makefile delete mode 100644 usr.bin/man/apropos/Makefile delete mode 100644 usr.bin/man/apropos/apropos.1 delete mode 100644 usr.bin/man/apropos/apropos.c delete mode 100644 usr.bin/man/man/Makefile delete mode 100644 usr.bin/man/man/config.c delete mode 100644 usr.bin/man/man/man.1 delete mode 100644 usr.bin/man/man/man.c delete mode 100644 usr.bin/man/man/man.conf.5 delete mode 100644 usr.bin/man/man/pathnames.h delete mode 100644 usr.bin/man/whatis/Makefile delete mode 100644 usr.bin/man/whatis/whatis.1 delete mode 100644 usr.bin/man/whatis/whatis.c create mode 100644 usr.bin/more/ch.c create mode 100644 usr.bin/more/command.c create mode 100644 usr.bin/more/decode.c create mode 100644 usr.bin/more/filename.c create mode 100644 usr.bin/more/help.c create mode 100644 usr.bin/more/input.c create mode 100644 usr.bin/more/less.h create mode 100644 usr.bin/more/line.c create mode 100644 usr.bin/more/linenum.c create mode 100644 usr.bin/more/main.c delete mode 100644 usr.bin/more/more.c create mode 100644 usr.bin/more/option.c create mode 100644 usr.bin/more/os.c create mode 100644 usr.bin/more/output.c create mode 100644 usr.bin/more/position.c create mode 100644 usr.bin/more/prim.c create mode 100644 usr.bin/more/screen.c create mode 100644 usr.bin/more/signal.c create mode 100644 usr.bin/more/tags.c create mode 100644 usr.bin/more/ttyin.c rename usr.bin/ranlib/{ranlib.5.5 => ranlib.5} (100%) create mode 100644 usr.bin/rev/Makefile create mode 100644 usr.bin/rev/rev.1 create mode 100644 usr.bin/rev/rev.c create mode 100644 usr.bin/rpcgen/Makefile create mode 100644 usr.bin/rpcgen/rpc_clntout.c create mode 100644 usr.bin/rpcgen/rpc_cout.c create mode 100644 usr.bin/rpcgen/rpc_hout.c create mode 100644 usr.bin/rpcgen/rpc_main.c create mode 100644 usr.bin/rpcgen/rpc_parse.c create mode 100644 usr.bin/rpcgen/rpc_parse.h create mode 100644 usr.bin/rpcgen/rpc_scan.c create mode 100644 usr.bin/rpcgen/rpc_scan.h create mode 100644 usr.bin/rpcgen/rpc_svcout.c create mode 100644 usr.bin/rpcgen/rpc_util.c create mode 100644 usr.bin/rpcgen/rpc_util.h create mode 100644 usr.bin/rpcgen/rpcgen.1 create mode 100644 usr.bin/rpcinfo/Makefile create mode 100644 usr.bin/rpcinfo/rpcinfo.8 create mode 100644 usr.bin/rpcinfo/rpcinfo.c create mode 100644 usr.bin/rup/Makefile create mode 100644 usr.bin/rup/rup.1 create mode 100644 usr.bin/rup/rup.c create mode 100644 usr.bin/rusers/Makefile create mode 100644 usr.bin/rusers/rusers.1 create mode 100644 usr.bin/rusers/rusers.c create mode 100644 usr.bin/rwall/Makefile create mode 100644 usr.bin/rwall/rwall.1 create mode 100644 usr.bin/rwall/rwall.c delete mode 100644 usr.bin/sccs/Makefile delete mode 100644 usr.bin/sccs/pathnames.h delete mode 100644 usr.bin/sccs/sccs.1 delete mode 100644 usr.bin/sccs/sccs.c delete mode 100644 usr.bin/sed/COPYING delete mode 100644 usr.bin/sed/ChangeLog delete mode 100644 usr.bin/sed/Makefile.gnu create mode 100644 usr.bin/sed/POSIX delete mode 100644 usr.bin/sed/README create mode 100644 usr.bin/sed/compile.c create mode 100644 usr.bin/sed/compile.test create mode 100644 usr.bin/sed/defs.h create mode 100644 usr.bin/sed/extern.h delete mode 100644 usr.bin/sed/getopt.c delete mode 100644 usr.bin/sed/getopt.h delete mode 100644 usr.bin/sed/getopt1.c create mode 100644 usr.bin/sed/hanoi.sed create mode 100644 usr.bin/sed/main.c create mode 100644 usr.bin/sed/math.sed create mode 100644 usr.bin/sed/misc.c create mode 100644 usr.bin/sed/process.c delete mode 100644 usr.bin/sed/regex.c delete mode 100644 usr.bin/sed/regex.h create mode 100644 usr.bin/sed/sed.1 delete mode 100644 usr.bin/sed/sed.c create mode 100644 usr.bin/sed/sed.test delete mode 100644 usr.bin/sed/utils.c create mode 100644 usr.bin/syscons/Makefile create mode 100644 usr.bin/syscons/syscons.1 create mode 100644 usr.bin/syscons/syscons.c create mode 100644 usr.bin/syscons/syscons.h delete mode 100644 usr.bin/tail/COPYING create mode 100644 usr.bin/tail/extern.h create mode 100644 usr.bin/tail/forward.c delete mode 100644 usr.bin/tail/getopt.c delete mode 100644 usr.bin/tail/getopt.h delete mode 100644 usr.bin/tail/getopt1.c create mode 100644 usr.bin/tail/misc.c create mode 100644 usr.bin/tail/read.c create mode 100644 usr.bin/tail/reverse.c create mode 100644 usr.bin/tn3270/Makefile create mode 100644 usr.bin/tn3270/Makefile.inc delete mode 100644 usr.bin/tn3270/api/makefile delete mode 100644 usr.bin/tn3270/ascii/makefile delete mode 100644 usr.bin/tn3270/ctlr/makefile delete mode 100644 usr.bin/tn3270/general/makefile delete mode 100644 usr.bin/tn3270/makefile delete mode 100644 usr.bin/tn3270/map3270.0 delete mode 100644 usr.bin/tn3270/mset.0 create mode 100644 usr.bin/tn3270/mset/Makefile rename usr.bin/tn3270/{ => mset}/map3270.5 (100%) rename usr.bin/tn3270/{ => mset}/mset.1 (100%) delete mode 100644 usr.bin/tn3270/sys_curses/makefile delete mode 100644 usr.bin/tn3270/telnet/Makefile delete mode 100644 usr.bin/tn3270/tn3270.0 create mode 100644 usr.bin/tn3270/tn3270/Makefile rename usr.bin/tn3270/{ => tn3270}/tn3270.1 (100%) create mode 100644 usr.bin/tn3270/tools/Makefile delete mode 100644 usr.bin/tn3270/tools/makefile create mode 100644 usr.bin/tn3270/tools/mkastods/Makefile rename usr.bin/tn3270/tools/{ => mkastods}/mkastods.c (100%) create mode 100644 usr.bin/tn3270/tools/mkastosc/Makefile rename usr.bin/tn3270/tools/{ => mkastosc}/mkastosc.c (100%) create mode 100644 usr.bin/tn3270/tools/mkdctype/Makefile rename usr.bin/tn3270/tools/{ => mkdctype}/ectype.c (100%) rename usr.bin/tn3270/tools/{ => mkdctype}/ectype.h (100%) rename usr.bin/tn3270/tools/{ => mkdctype}/mkdctype.c (100%) create mode 100644 usr.bin/tn3270/tools/mkdstoas/Makefile rename usr.bin/tn3270/tools/{ => mkdstoas}/mkdstoas.c (100%) create mode 100644 usr.bin/tn3270/tools/mkhits/Makefile rename usr.bin/tn3270/tools/{ => mkhits}/dohits.c (100%) rename usr.bin/tn3270/tools/{ => mkhits}/dohits.h (100%) rename usr.bin/tn3270/tools/{ => mkhits}/mkhits.c (100%) create mode 100644 usr.bin/tr/extern.h create mode 100644 usr.bin/tr/str.c create mode 100644 usr.bin/uname/Makefile create mode 100644 usr.bin/uname/uname.1 create mode 100644 usr.bin/uname/uname.c create mode 100644 usr.bin/yacc/NO_WARRANTY create mode 100644 usr.bin/yacc/test/error.tab.h create mode 100644 usr.bin/yacc/test/ftp.y create mode 100644 usr.sbin/XNSrouted/Makefile create mode 100644 usr.sbin/XNSrouted/XNSrouted.8 create mode 100644 usr.sbin/XNSrouted/af.c create mode 100644 usr.sbin/XNSrouted/af.h create mode 100644 usr.sbin/XNSrouted/defs.h create mode 100644 usr.sbin/XNSrouted/if.c create mode 100644 usr.sbin/XNSrouted/input.c create mode 100644 usr.sbin/XNSrouted/interface.h create mode 100644 usr.sbin/XNSrouted/main.c create mode 100644 usr.sbin/XNSrouted/output.c create mode 100644 usr.sbin/XNSrouted/protocol.h create mode 100644 usr.sbin/XNSrouted/startup.c create mode 100644 usr.sbin/XNSrouted/table.h create mode 100644 usr.sbin/XNSrouted/tables.c create mode 100644 usr.sbin/XNSrouted/timer.c create mode 100644 usr.sbin/XNSrouted/tools/query.c create mode 100644 usr.sbin/XNSrouted/trace.c create mode 100644 usr.sbin/XNSrouted/trace.h delete mode 100644 usr.sbin/keymap/Makefile delete mode 100644 usr.sbin/keymap/keymap/Makefile delete mode 100644 usr.sbin/keymap/keymap/README.keycap delete mode 100644 usr.sbin/keymap/keymap/keycap.5 delete mode 100644 usr.sbin/keymap/keymap/keymap.8 delete mode 100644 usr.sbin/keymap/keymap/keymap.c delete mode 100644 usr.sbin/keymap/keymap/pathnames.h delete mode 100644 usr.sbin/keymap/lib/Makefile delete mode 100644 usr.sbin/keymap/lib/co.4 delete mode 100644 usr.sbin/keymap/lib/keycap.3 delete mode 100644 usr.sbin/keymap/lib/keycap.c delete mode 100644 usr.sbin/keymap/lib/pathnames.h delete mode 100644 usr.sbin/keymap/lib/setfont.3 delete mode 100644 usr.sbin/keymap/lib/setfont.c delete mode 100644 usr.sbin/keymap/lib/vgafont.5 delete mode 100644 usr.sbin/keymap/lib/whichcons.3 delete mode 100644 usr.sbin/keymap/lib/whichcons.c delete mode 100644 usr.sbin/keymap/lib/xc_fns.c create mode 100644 usr.sbin/mtree/extern.h create mode 100644 usr.sbin/mtree/misc.c delete mode 100644 usr.sbin/mtree/mtree.1 create mode 100644 usr.sbin/mtree/mtree.8 create mode 100644 usr.sbin/pppstats/Makefile create mode 100644 usr.sbin/pppstats/pppstats.c create mode 100644 usr.sbin/sendmail/CHANGES-R5-R8 create mode 100644 usr.sbin/sendmail/RELEASE_NOTES delete mode 100644 usr.sbin/sendmail/aux/addr.c delete mode 100644 usr.sbin/sendmail/aux/arpa.c delete mode 100644 usr.sbin/sendmail/aux/mail-dm.c delete mode 100644 usr.sbin/sendmail/aux/matchhdr.c delete mode 100644 usr.sbin/sendmail/aux/newaliases.c delete mode 100644 usr.sbin/sendmail/cf/KEY delete mode 100644 usr.sbin/sendmail/cf/M4_KEY delete mode 100644 usr.sbin/sendmail/cf/cf/README create mode 100644 usr.sbin/sendmail/cf/cf/alpha.mc create mode 100644 usr.sbin/sendmail/cf/cf/auspex.mc create mode 100644 usr.sbin/sendmail/cf/cf/boat-anchor.mc delete mode 100644 usr.sbin/sendmail/cf/cf/cad.cf delete mode 100644 usr.sbin/sendmail/cf/cf/cad.mc delete mode 100644 usr.sbin/sendmail/cf/cf/cadgroup.cf delete mode 100644 usr.sbin/sendmail/cf/cf/cadgroup.mc delete mode 100644 usr.sbin/sendmail/cf/cf/cc.cf delete mode 100644 usr.sbin/sendmail/cf/cf/cc.mc delete mode 100644 usr.sbin/sendmail/cf/cf/cchem.cf delete mode 100644 usr.sbin/sendmail/cf/cf/cchem.mc create mode 100644 usr.sbin/sendmail/cf/cf/chez.mc delete mode 100644 usr.sbin/sendmail/cf/cf/cogsci.cf create mode 100644 usr.sbin/sendmail/cf/cf/cs-exposed.mc create mode 100644 usr.sbin/sendmail/cf/cf/cs-hidden.mc create mode 100644 usr.sbin/sendmail/cf/cf/freefall.mc create mode 100644 usr.sbin/sendmail/cf/cf/hpux-cs-exposed.mc create mode 100644 usr.sbin/sendmail/cf/cf/hpux-cs-hidden.mc delete mode 100644 usr.sbin/sendmail/cf/cf/ic.cf delete mode 100644 usr.sbin/sendmail/cf/cf/ic.mc delete mode 100644 usr.sbin/sendmail/cf/cf/id.cf delete mode 100644 usr.sbin/sendmail/cf/cf/id.mc delete mode 100644 usr.sbin/sendmail/cf/cf/kappa.cf create mode 100644 usr.sbin/sendmail/cf/cf/knecht.mc delete mode 100644 usr.sbin/sendmail/cf/cf/lamprey.cf create mode 100644 usr.sbin/sendmail/cf/cf/mail.cs.mc create mode 100644 usr.sbin/sendmail/cf/cf/mail.eecs.mc delete mode 100644 usr.sbin/sendmail/cf/cf/okeeffe.cf delete mode 100644 usr.sbin/sendmail/cf/cf/okeeffe.mc delete mode 100644 usr.sbin/sendmail/cf/cf/proto.mc create mode 100644 usr.sbin/sendmail/cf/cf/python.mc create mode 100644 usr.sbin/sendmail/cf/cf/s2k.mc create mode 100644 usr.sbin/sendmail/cf/cf/sun-lamp.mc create mode 100644 usr.sbin/sendmail/cf/cf/sunos3.5-cs-exposed.mc create mode 100644 usr.sbin/sendmail/cf/cf/sunos3.5-cs-hidden.mc create mode 100644 usr.sbin/sendmail/cf/cf/sunos4.1-cs-exposed.mc create mode 100644 usr.sbin/sendmail/cf/cf/sunos4.1-cs-hidden.mc delete mode 100644 usr.sbin/sendmail/cf/cf/tcpproto.cf delete mode 100644 usr.sbin/sendmail/cf/cf/tcpuucpproto.cf delete mode 100644 usr.sbin/sendmail/cf/cf/tcpuucpproto.mc delete mode 100644 usr.sbin/sendmail/cf/cf/telemuse.cf delete mode 100644 usr.sbin/sendmail/cf/cf/ucbarpa.cf delete mode 100644 usr.sbin/sendmail/cf/cf/ucbtcp.cf delete mode 100644 usr.sbin/sendmail/cf/cf/ucbtcp.mc delete mode 100644 usr.sbin/sendmail/cf/cf/ucbvax.cf create mode 100644 usr.sbin/sendmail/cf/cf/udb.mc delete mode 100644 usr.sbin/sendmail/cf/cf/uid.cf delete mode 100644 usr.sbin/sendmail/cf/cf/uid.mc create mode 100644 usr.sbin/sendmail/cf/cf/ultrix4.1-cs-exposed.mc create mode 100644 usr.sbin/sendmail/cf/cf/ultrix4.1-cs-hidden.mc delete mode 100644 usr.sbin/sendmail/cf/cf/uucpproto.cf create mode 100644 usr.sbin/sendmail/cf/cf/vangogh.mc create mode 100644 usr.sbin/sendmail/cf/domain/Berkeley.m4 create mode 100644 usr.sbin/sendmail/cf/domain/cs.exposed.m4 create mode 100644 usr.sbin/sendmail/cf/domain/cs.hidden.m4 create mode 100644 usr.sbin/sendmail/cf/domain/eecs.hidden.m4 create mode 100644 usr.sbin/sendmail/cf/domain/s2k.m4 create mode 100644 usr.sbin/sendmail/cf/feature/allmasquerade.m4 create mode 100644 usr.sbin/sendmail/cf/feature/always_add_domain.m4 create mode 100644 usr.sbin/sendmail/cf/feature/bitdomain.m4 create mode 100644 usr.sbin/sendmail/cf/feature/domaintable.m4 create mode 100644 usr.sbin/sendmail/cf/feature/mailertable.m4 create mode 100644 usr.sbin/sendmail/cf/feature/nocanonify.m4 create mode 100644 usr.sbin/sendmail/cf/feature/notsticky.m4 create mode 100644 usr.sbin/sendmail/cf/feature/nouucp.m4 create mode 100644 usr.sbin/sendmail/cf/feature/redirect.m4 create mode 100644 usr.sbin/sendmail/cf/feature/use_cw_file.m4 create mode 100644 usr.sbin/sendmail/cf/feature/uucpdomain.m4 create mode 100644 usr.sbin/sendmail/cf/hack/cssubdomain.m4 delete mode 100644 usr.sbin/sendmail/cf/m4/README delete mode 100644 usr.sbin/sendmail/cf/m4/boilerplate.m4 create mode 100644 usr.sbin/sendmail/cf/m4/cf.m4 delete mode 100644 usr.sbin/sendmail/cf/m4/fake_domains.m4 delete mode 100644 usr.sbin/sendmail/cf/m4/localm.m4 delete mode 100644 usr.sbin/sendmail/cf/m4/nsclasses.m4 delete mode 100644 usr.sbin/sendmail/cf/m4/nsmacros.m4 delete mode 100644 usr.sbin/sendmail/cf/m4/nstcpldm.m4 delete mode 100644 usr.sbin/sendmail/cf/m4/nstcpm.m4 delete mode 100644 usr.sbin/sendmail/cf/m4/postwriterule.m4 delete mode 100644 usr.sbin/sendmail/cf/m4/prewriterule.m4 create mode 100644 usr.sbin/sendmail/cf/m4/proto.m4 delete mode 100644 usr.sbin/sendmail/cf/m4/rule0.m4 delete mode 100644 usr.sbin/sendmail/cf/m4/rule3.m4 delete mode 100644 usr.sbin/sendmail/cf/m4/rule5.m4 delete mode 100644 usr.sbin/sendmail/cf/m4/smtpuucpm.m4 delete mode 100644 usr.sbin/sendmail/cf/m4/suucpm.m4 delete mode 100644 usr.sbin/sendmail/cf/m4/uucpm.m4 create mode 100644 usr.sbin/sendmail/cf/mailer/fax.m4 create mode 100644 usr.sbin/sendmail/cf/mailer/local.m4 create mode 100644 usr.sbin/sendmail/cf/mailer/smtp.m4 create mode 100644 usr.sbin/sendmail/cf/mailer/usenet.m4 create mode 100644 usr.sbin/sendmail/cf/mailer/uucp.m4 create mode 100644 usr.sbin/sendmail/cf/ostype/aix3.m4 create mode 100644 usr.sbin/sendmail/cf/ostype/bsd4.3.m4 create mode 100644 usr.sbin/sendmail/cf/ostype/bsd4.4.m4 create mode 100644 usr.sbin/sendmail/cf/ostype/hpux.m4 create mode 100644 usr.sbin/sendmail/cf/ostype/irix.m4 create mode 100644 usr.sbin/sendmail/cf/ostype/osf1.m4 create mode 100644 usr.sbin/sendmail/cf/ostype/riscos4.5.m4 create mode 100644 usr.sbin/sendmail/cf/ostype/solaris2.1.m4 create mode 100644 usr.sbin/sendmail/cf/ostype/sunos3.5.m4 create mode 100644 usr.sbin/sendmail/cf/ostype/sunos4.1.m4 create mode 100644 usr.sbin/sendmail/cf/ostype/ultrix4.1.m4 create mode 100644 usr.sbin/sendmail/cf/sh/makeinfo.sh create mode 100644 usr.sbin/sendmail/cf/siteconfig/uucp.cogsci.m4 create mode 100644 usr.sbin/sendmail/cf/siteconfig/uucp.old.arpa.m4 create mode 100644 usr.sbin/sendmail/cf/siteconfig/uucp.ucbarpa.m4 create mode 100644 usr.sbin/sendmail/cf/siteconfig/uucp.ucbvax.m4 delete mode 100644 usr.sbin/sendmail/cf/sitedep/README delete mode 100644 usr.sbin/sendmail/cf/sitedep/nicregistered.m4 delete mode 100644 usr.sbin/sendmail/cf/sitedep/smtpuucp.cad.m4 delete mode 100644 usr.sbin/sendmail/cf/sitedep/smtpuucp.ucbvax.m4 delete mode 100644 usr.sbin/sendmail/cf/sitedep/uucp.cad.m4 delete mode 100644 usr.sbin/sendmail/cf/sitedep/uucp.cogsci.m4 delete mode 100644 usr.sbin/sendmail/cf/sitedep/uucp.ic.m4 delete mode 100644 usr.sbin/sendmail/cf/sitedep/uucp.id.m4 delete mode 100644 usr.sbin/sendmail/cf/sitedep/uucp.okeeffe.m4 delete mode 100644 usr.sbin/sendmail/cf/sitedep/uucp.proto.m4 delete mode 100644 usr.sbin/sendmail/cf/sitedep/uucp.ucbarpa.m4 delete mode 100644 usr.sbin/sendmail/cf/sitedep/uucp.ucbvax.m4 create mode 100644 usr.sbin/sendmail/contrib/README create mode 100644 usr.sbin/sendmail/contrib/bitdomain.c create mode 100644 usr.sbin/sendmail/contrib/expn.pl create mode 100644 usr.sbin/sendmail/contrib/mmuegel create mode 100644 usr.sbin/sendmail/contrib/rcpt-streaming create mode 100644 usr.sbin/sendmail/contrib/xla/README create mode 100644 usr.sbin/sendmail/contrib/xla/xla.c create mode 100644 usr.sbin/sendmail/doc/intro/intro.me create mode 100644 usr.sbin/sendmail/doc/intro/intro.ps create mode 100644 usr.sbin/sendmail/doc/op/op.me create mode 100644 usr.sbin/sendmail/doc/op/op.ps delete mode 100644 usr.sbin/sendmail/doc/refs delete mode 100644 usr.sbin/sendmail/doc/rfc819.lpr delete mode 100644 usr.sbin/sendmail/doc/rfc821.lpr delete mode 100644 usr.sbin/sendmail/doc/rfc822.lpr delete mode 100644 usr.sbin/sendmail/doc/spell.good delete mode 100644 usr.sbin/sendmail/doc/usenix.abs delete mode 100644 usr.sbin/sendmail/doc/usenix.me create mode 100644 usr.sbin/sendmail/doc/usenix/usenix.me create mode 100644 usr.sbin/sendmail/doc/usenix/usenix.ps delete mode 100644 usr.sbin/sendmail/mailstats/pathnames.h create mode 100644 usr.sbin/sendmail/makemap/Makefile create mode 100644 usr.sbin/sendmail/makemap/makemap.8 create mode 100644 usr.sbin/sendmail/makemap/makemap.c create mode 100644 usr.sbin/sendmail/rmail/Makefile create mode 100644 usr.sbin/sendmail/rmail/rmail.8 create mode 100644 usr.sbin/sendmail/rmail/rmail.c create mode 100644 usr.sbin/sendmail/src/Makefile.AIX create mode 100644 usr.sbin/sendmail/src/Makefile.HPUX create mode 100644 usr.sbin/sendmail/src/Makefile.IRIX create mode 100644 usr.sbin/sendmail/src/Makefile.OSF1 create mode 100644 usr.sbin/sendmail/src/Makefile.Solaris create mode 100644 usr.sbin/sendmail/src/Makefile.SunOS create mode 100644 usr.sbin/sendmail/src/Makefile.ULTRIX create mode 100644 usr.sbin/sendmail/src/Makefile.Utah create mode 100644 usr.sbin/sendmail/src/Makefile.dist delete mode 100644 usr.sbin/sendmail/src/TODO create mode 100644 usr.sbin/sendmail/src/TRACEFLAGS create mode 100644 usr.sbin/sendmail/src/aliases create mode 100644 usr.sbin/sendmail/src/cdefs.h create mode 100644 usr.sbin/sendmail/src/map.c create mode 100644 usr.sbin/sendmail/src/mci.c delete mode 100644 usr.sbin/sendmail/src/sendmail.cf create mode 100644 usr.sbin/sendmail/src/sysexits.h create mode 100644 usr.sbin/sendmail/src/udb.c diff --git a/BUGNFIX.FORM b/BUGNFIX.FORM deleted file mode 100644 index 638a7bee48..0000000000 --- a/BUGNFIX.FORM +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - - 386BSD RELEASE 0.1 - BUG AND FIX REPORT FORM - - - 386BSD is an experimental research system intended for -exploration and study. The inclusion of source code allows -the creative and industrious student of operating systems to -modify, correct, augment and otherwise alter the system in -any manner desired. Due to the "free" nature of 386BSD -Release 0.1, we cannot provide "on-demand" support. - - We do encourage feedback, however, on bug fixes, prob- -lems and solutions which arise during configuration, and -improvements to 386BSD. These changes will be incorporated -back into the system, and made available once again to the -386BSD audience, to further encourage use of this system. - - Please complete this bug and fix report and return to: - - Lynne Greer Jolitz - 386BSD Bug and Fix Report - 447 61ST Street - Oakland, CA. 94609 USA - - +1-510-420-0174 FAX - ljolitz@cardio.ucsf.edu - CIS 76703,4266 - - -_B_U_G _F_I_N_D_E_R _I_N_F_O_R_M_A_T_I_O_N - -NAME: -TITLE: -FIRM: -ADDRESS: -COUNTRY: -PHONE: -FAX: -EMAIL: - - -_P_C _I_N_F_O_R_M_A_T_I_O_N - -1. What is your PC system configuration? Please be - specific, as success can vary with a difference in - manufacture date or variation. - - 386/486/387: - ISA/EISA/SCSI: - RAM size: - drive(s) type and size: - tape(s) type and size: - ethernet type: - - -386BSD BUGNFIX.FORM 1 July 1992 - - - - - - - - - - - graphics type: - serial type: - parallel type: - other: - - -_B_u_g_s - -1. What bugs have you found in this system? Please attach - any pertinent printouts and system messages. Please - also attach any bug fixes for others to use. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -386BSD BUGNFIX.FORM 2 July 1992 - - - diff --git a/CAREWARE.INFO b/CAREWARE.INFO deleted file mode 100644 index c082d3dd87..0000000000 --- a/CAREWARE.INFO +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - "Tiny 386BSD" - and the - DDJ Careware Program: - - Making A Difference - - William Jolitz - Lynne Jolitz - - -_W_h_a_t _i_s _C_a_r_e_w_a_r_e? - - For several years now, the editors of _D_r. _D_o_b_b_s _J_o_u_r_n_a_l -have overseen a charity program called _C_a_r_e_w_a_r_e. Careware, -in a nutshell, is a way to "give back" something to those -less fortunate in our community, and, at the same time, -obtain a copy of a popular piece of software featured in the -magazine -- simply by stuffing a dollar or two in your -floppy mailer for a local charity! It's that easy. - - Now, as part of the DDJ Careware Program, we are making -available to the editors of DDJ the latest version of our -"Tiny 386BSD" installation floppy. Even though we have made -this software freely available -- with no contribution -required for the use of 386BSD -- we hope that the 386BSD -User Community will choose to participate in this charity -drive and help the children aided by the _C_h_i_l_d_r_e_n'_s _S_u_p_p_o_r_t -_L_e_a_g_u_e _o_f _t_h_e _E_a_s_t _B_a_y. - -_W_h_a_t _i_s _t_h_e _C_h_i_l_d_r_e_n'_s _S_u_p_p_o_r_t _L_e_a_g_u_e? - - In this age of "fiscal austerity", many small community -organizations -- especially those working with children -- -have been hard-hit by state and local budget cuts. The CSL -is a volunteer organization which is solely dedicated to -raising funds for small local agencies which help needy -children. Some of the agencies which have received funds -from CSL in the past include those using computers to com- -municate with disabled children, providing respite weekends -for abused children, counciling bereaved children, and set- -ting up safe after-school play areas for children in impo- -verished neighborhoods. - - Simply put, by sending a dollar or two along with your -software request, you directly benefit a child. Even if it -is only a dollar, it does make a difference. - -_H_o_w _d_o _I _P_a_r_t_i_c_i_p_a_t_e _i_n _t_h_e _C_a_r_e_w_a_r_e _P_r_o_g_r_a_m? - - To get a labeled copy of "Tiny 386BSD", send a high- -density error-free formatted floppy (either 3.5-inch or -5.25-inch) and a SASE mailer to: - - -386BSD CAREWARE.INFO 1 July 1992 - - - - - - - - - - - Tiny 386BSD - DDJ - 411 Borel Avenue - San Mateo, CA. 94402 USA - - -While there is no obligation to participate, if you would -like to help a disadvantaged child, just include a dollar or -two (or more, if you so desire) in your mailer and we'll -make sure it gets to the Children's Support League. (If you -send a check, please make it payable to the "Children's Sup- -port League of the East Bay"). - -_A _C_h_a_l_l_e_n_g_e _t_o _t_h_e _3_8_6_B_S_D _U_s_e_r _C_o_m_m_u_n_i_t_y: _W_e _C_a_n _M_a_k_e _a -_D_i_f_f_e_r_e_n_c_e - - Thanks to the generous and enthusiastic response of the -D-Flat C-library users and and X-Sharp 3-D Graphics users, -the DDJ Careware Project has helped hundreds of people -throughout the country. - - While a dollar or two may seem like a trivial amount, -it does add up. The Brevard County Food Bank in Florida has -received over $3,000 in the past year alone from supportive -users of D-Flat, and the Vermont Association for the Blind -and Visually Disabled has received over $100 in the past -month from X-Sharp users. For these charities, a dollar or -two has made a difference! - - Now it's our turn. Just like the generous users of D- -Flat and X-Sharp, we believe that each person in the 386BSD -User Community is quite willing to meet the challenge and -help a child. We know that a dollar or two from 386BSD -users can make a difference as well. The choice, however, -is yours. - - - - - - - - - - - - - - - - - - - -386BSD CAREWARE.INFO 2 July 1992 - - - diff --git a/CONTRIB.LIST b/CONTRIB.386BSD similarity index 100% rename from CONTRIB.LIST rename to CONTRIB.386BSD diff --git a/CONTRIB.FreeBSD b/CONTRIB.FreeBSD new file mode 100644 index 0000000000..fd3109c33b --- /dev/null +++ b/CONTRIB.FreeBSD @@ -0,0 +1,179 @@ + FreeBSD 1.0 + Contributor List + + + +Derived Software Contributors: + +This software was derived from William F. Jolitz's 386BSD release 0.1. +Please see the file CONTRIB.386BSD for the list of contributors from +386BSD. + +Included in this release are the patches from the patch kit version +0.2.4. The names of contributors from the patch kit are listed below. + +There are portions of NetBSD that has been intergraged into FreeBSD +as well. We would like to thank all the contributors to NetBSD for +their work. + + +Hardware Contributors: + +A special thanks to Robert Bruce and Jack Velte of Walnut Creek CDROM +for providing a 486/DX2-66 EISA/VL system that is being used for the +development work. It would have been impossible to do this release +with out their support. + +Dermot McDonnell for his donation of a Toshiba XM3401B CDROM +drive. + + +FreeBSD core: + +Andrew Moore +Andreas Schulz +J.T. Conklin +Christoph Robitschko +David Greenman +Jordan K. Hubbard +Nate Williams +Paul Richards +Rich Murphey +Rodney W. Grimes +Scott Mace + + +Additional FreeBSD Contributors: + +Adam Glass +Andrew A. Chernov +Andrew Herbert +Bob Wilcox +Bruce Evans +Charles Hannum +Chris G. Demetriou +Chris Torek +Dave Burgess +Dave Rivers +David Dawes +Frank Maclachlan +Gary A. Browning +Garrett A. Wollman +Gary Clark II +Guido van Rooij +Havard Eidnes +Holger Veit +Ishii Masahiro, R. Kym Horsell +James Clark +James da Silva et al +Jim Wilson +John Dyson - +Julian Elischer +Julian Stacey > +Keith Bostic +Marc Frajola +Mark Tinguely +Martin Birgmeier +Paul Kranenburg +Paul Mackerras +Poul-Henning Kamp +Rod Shady +Sascha Wildner +Sean Eric Fagan +Steven Wallace +Terry Lee +Theo Deraadt +Yuval Yarom + + +Patch kit patch contributors: + +Adam Glass +Adrian Hall +Andrew A. Chernov +Andrew Herbert +Andrew Moore +Andy Valencia +Arne Henrik Juul +Bakul Shah +Barry Lustig +Bob Wilcox +Branko Lankester +Brett Lymn +Bruce Evans +Charles Hannum +Chris G. Demetriou +Chris Torek +Christoph Robitschko +Daniel Poirot +Dave Burgess +Dave Rivers +David Dawes +David Greenman +Eric J. Haug +Felix Gaehtgens +Frank Maclachlan +Gary A. Browning +Geoff Rehmet +Goran Hammarback +Guido van Rooij +Guy Harris +Havard Eidnes +Herb Peyerl +Ishii Masahiro, R. Kym Horsell +J.T. Conklin +Jagane D Sundar < jagane@netcom.com > +James Clark +James Jegers +James W. Dolter +James da Silva et al +Jay Fenlason +Jim Wilson +Joerg Lohse +Joerg Wunsch +John Dyson - +John Woods +Jordan K. Hubbard +Julian Elischer +Julian Stacey > +Karl Lehenbauer +Keith Bostic +Ken Hughes +Kent Talarico +Kevin Lahey +Marc Frajola +Mark Tinguely +Martin Renters +Michael Galassi +Mike Durkin +Nate Williams +Nick Handel +Pace Willisson +Paul Kranenburg +Paul Mackerras +Paul Popelka +Peter da Silva +Phil Sutherland +Poul-Henning Kamp +Ralf Friedl +Rich Murphey +Rick "gopher I" +Robert D. Thrush +Rodney W. Grimes +Rog Egge +Sascha Wildner +Scott Burris +Scott Reynolds +Scotty ? +Sean Eric Fagan +Simon J Gerraty +Stephen McKay +Terry Lambert +Terry Lee +Warren Toomey +Wiljo Heinen +William Jolitz +Wolfgang Solfrank +Wolfgang Stanglmeier +Yuval Yarom diff --git a/FixLinks b/FixLinks new file mode 100644 index 0000000000..1fcb1a66ec --- /dev/null +++ b/FixLinks @@ -0,0 +1,52 @@ +#!/bin/csh +# +# A silly little script to Fix the Links that cvs does not +# import/ checkout. These need to be made by the Makefiles, this +# is just here as a stop gap solution. +# +# $Header: /a/cvs/386BSD/src/FixLinks,v 1.7 1993/08/05 18:40:33 conklin Exp $ +# +# $Log: FixLinks,v $ +# Revision 1.7 1993/08/05 18:40:33 conklin +# Commented out more links that are no longer needed. +# +# Revision 1.6 1993/08/03 01:40:19 rgrimes +# Commented out many of the links that are no longer needed. +# +# Revision 1.5 1993/07/08 17:58:20 nate +# Removed Fixed Links +# +# Revision 1.4 1993/07/03 02:01:30 root +# Updated since the links for the gnu stuff are no longer needed. +# +# Revision 1.3 1993/06/21 19:47:32 root +# Added rcs -> ../gnu/rcs +# +# Revision 1.2 1993/06/20 12:02:09 rgrimes +# Put back in the ones that usr.bin/Makefile should do, it needs fixed +# as it does not work for a make obj. +# +# Revision 1.1 93/06/19 14:50:18 rgrimes +# Added my silly little FixLinks script until someone fixes all +# the make files. +# + +pushd sys/netccitt ; ln -s x25.h x25_sockaddr.h ; popd +pushd lib/libc/db ; ln -s VERSION README ; popd +pushd lib/libc/db/PORT ; ln -s include sys ; popd +#pushd libexec/uucp ; ln -s sys1.unx sys1.c ; popd +#pushd libexec/uucp ; ln -s sys3.unx sys3.c ; popd +#pushd libexec/uucp ; ln -s sys4.unx sys4.c ; popd +#pushd libexec/uucp ; ln -s sys5.unx sys5.c ; popd +#pushd libexec/uucp ; ln -s sys6.unx sys6.c ; popd +#pushd libexec/uucp ; ln -s sys7.unx sys7.c ; popd +#pushd libexec/uucp ; ln -s sysh.unx sysdep.h ; popd +#pushd usr.bin/yacc/test ; ln -s ../../../libexec/ftpd/ftpcmd.y ftp.y ; popd +#pushd usr.sbin/named ; ln -s /usr/include/arpa/nameser.h nameser.h ; popd +#pushd usr.sbin/named ; ln -s /usr/include/resolv.h resolv.h ; popd +#pushd usr.sbin/sendmail ; ln -s ../../libexec/mail.local mail.local ; popd +#pushd usr.sbin/sendmail ; ln -s ../../bin/rmail rmail ; popd +#pushd share/doc/usd/10.etiq ; ln -s ../../smm/10.newsop/tmac.n tmac.n ; popd +#pushd share/doc/usd/09.newsread ; ln -s ../../smm/10.newsop/tmac.n tmac.n ; popd +#pushd games/chess ; ln -s DOCUMENTATION/MAN-PAGE chess.6 ; popd +pushd games/fortune/datfiles ; ln -s fortunes-o.real.rot13 fortunes-o ; popd diff --git a/Makefile b/Makefile index df83178b0e..3b8ac6bbd9 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,144 @@ # @(#)Makefile 5.1.1.2 (Berkeley) 5/9/91 # -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 1 00128 -# -------------------- ----- ---------------------- +# $Id: Makefile,v 1.18 1993/10/25 20:58:17 rgrimes Exp $ # -# 04 Apr 93 Rodney W. Grimes Pulled in Net/2 version string -# Added not ported comment -# Added special cases comment -# Added contrib, games, and share + +SUBDIR= +.if exists(bin) +SUBDIR+= bin +.endif +.if exists(contrib) +SUBDIR+= contrib +.endif +.if exists(games) +SUBDIR+= games +.endif +.if exists(gnu) +SUBDIR+= gnu +.endif +.if exists(include) +SUBDIR+= include +.endif +.if exists(lib) +SUBDIR+= lib +.endif +.if exists(libexec) +SUBDIR+= libexec +.endif +.if exists(sbin) +SUBDIR+= sbin +.endif +.if exists(share) +SUBDIR+= share +.endif +.if exists(usr.bin) +SUBDIR+= usr.bin +.endif +.if exists(usr.sbin) +SUBDIR+= usr.sbin +.endif + +# Special cases: etc sys +# Not ported: kerberosIV + # +# setenv NOCLEANDIR will prevent make cleandirs from being run +# +.if defined(NOCLEANDIR) +CLEANDIR= +.else +CLEANDIR= cleandir +.endif + +world: directories cleandist mk includes libraries tools mdec + @echo "--------------------------------------------------------------" + @echo " Rebuilding ${DESTDIR} The whole thing" + @echo "--------------------------------------------------------------" + @echo + make depend all install + cd ${.CURDIR}/usr.sbin/sendmail/src; make install + cd ${.CURDIR}/share/man; make makedb + +directories: + cd ${.CURDIR}/etc; make distrib-dirs + +cleandist: +.if !defined(NOCLEANDIR) + @echo "--------------------------------------------------------------" + @echo " Cleaning up the source tree, and rebuilding the obj tree" + @echo "--------------------------------------------------------------" + @echo + here=`pwd`; dest=/usr/obj/`echo $$here | sed 's,/usr/src,,'`; \ + cd $$dest; rm -rf ${SUBDIR} + find . -name obj | xargs -n30 rm -rf + make cleandir + make obj +.endif + +mk: + @echo "--------------------------------------------------------------" + @echo " Rebuilding ${DESTDIR}/usr/share/mk" + @echo "--------------------------------------------------------------" + # DONT DO THIS!! rm -rf ${DESTDIR}/usr/share/mk + # DONT DO THIS!! mkdir ${DESTDIR}/usr/share/mk + chown ${BINOWN}.${BINGRP} ${DESTDIR}/usr/share/mk + chmod 755 ${DESTDIR}/usr/share/mk + cd ${.CURDIR}/share/mk; make install; + +includes: + @echo "--------------------------------------------------------------" + @echo " Rebuilding ${DESTDIR}/usr/include" + @echo "--------------------------------------------------------------" + @echo +.if defined(CLOBBER) + rm -rf ${DESTDIR}/usr/include + mkdir ${DESTDIR}/usr/include + chown ${BINOWN}.${BINGRP} ${DESTDIR}/usr/include + chmod 755 ${DESTDIR}/usr/include +.endif + cd ${.CURDIR}/include; make install + cd ${.CURDIR}/gnu/libg++; make beforeinstall + cd ${.CURDIR}/gnu/libregex; make beforeinstall + cd ${.CURDIR}/lib/libcurses; make beforeinstall + cd ${.CURDIR}/lib/librpc/rpc; make beforeinstall + +libraries: + # setenv NOPROFILE if you do not want profiled libraries + @echo "--------------------------------------------------------------" + @echo " Rebuilding ${DESTDIR}/usr/lib" + @echo "--------------------------------------------------------------" + @echo +.if defined(CLOBBER) + rm -rf ${DESTDIR}/usr/lib + mkdir ${DESTDIR}/usr/lib + chown -R bin.bin ${DESTDIR}/usr/lib + chmod 755 ${DESTDIR}/usr/lib +.endif + cd ${.CURDIR}/lib; make depend all install ${CLEANDIR} obj + cd ${.CURDIR}/gnu/gcc2/libgcc; make depend all install ${CLEANDIR} obj + cd ${.CURDIR}/gnu/libg++; make depend all install ${CLEANDIR} obj + cd ${.CURDIR}/gnu/libregex; make depend all install ${CLEANDIR} obj + cd ${.CURDIR}/gnu/libmalloc; make depend all install ${CLEANDIR} obj + cd ${.CURDIR}/usr.bin/lex; make depend all install ${CLEANDIR} obj -SUBDIR= bin contrib games include lib libexec sbin share usr.bin usr.sbin +tools: + @echo "--------------------------------------------------------------" + @echo " Rebuilding ${DESTDIR} Compiler and Make" + @echo "--------------------------------------------------------------" + @echo + cd ${.CURDIR}/gnu/gcc2; make depend all install ${CLEANDIR} obj + cd ${.CURDIR}/usr.bin/make; make depend all install ${CLEANDIR} obj -# Special cases: etc sys.386bsd -# Not ported: games kerberosIV +mdec: + @echo "--------------------------------------------------------------" + @echo " Rebuilding ${DESTDIR}/usr/mdec" + @echo "--------------------------------------------------------------" + @echo +.if ${MACHINE} == "i386" + cd ${.CURDIR}/sys/i386/boot; make depend all install ${CLEANDIR} +.if defined (DESTDIR) + cd /usr/mdec; find . | cpio -pdamuv ${DESTDIR}/usr/mdec +.endif +.endif .include diff --git a/REGISTRATION b/REGISTRATION deleted file mode 100644 index 371a6a4dff..0000000000 --- a/REGISTRATION +++ /dev/null @@ -1,522 +0,0 @@ - - - - - - - - 386BSD RELEASE 0.1 - OFFICIAL USER GROUP REGISTRATION - - -_3_8_6_B_S_D _U_s_e_r _G_r_o_u_p _R_e_g_i_s_t_r_a_t_i_o_n - - Attached is the 386BSD user group registration form and -survey. Please fill it out completely and return it via -email, regular mail, or fax. We will put you on the Official -386BSD User Group mailing list for information and updates -on 386BSD. - - A userdirectory file is installed at official distribu- -tion sites to allow 386BSD users to contact and become -acquainted with other 386BSD users. If you wish your name, -company or university, and email address to be placed in -this public directory, please check the box on the registra- -tion form. (NOTE: You do not have to select this option in -order to participate in the 386BSD SIG and regular mailing -lists and other activities. This directory has been -installed only as a courtesy to the 386BSD user community. -Participation is not mandatory.) - -_3_8_6_B_S_D _U_s_e_r _A_c_t_i_v_i_t_i_e_s _a_n_d _M_a_i_l_i_n_g _L_i_s_t_s - - There are a number of 386BSD special interest groups -forming, focussing on topics of interest to the research and -development community. Among these areas of interest are: - -* Shared Libraries - -* Filesystems - -* Symmetric Multiprocessing - -* Windowing Systems - -* Virtual Memory System - -* Gigabit Networking - -In addition, a number of smaller projects focussing on -improvements in the kernel and utilities are getting under- -way. - - If you are interested in learning about any of these -groups, or you would like to form your own SIG, please fill -out and send in this REGISTRATION form and we will put you -on the SIG mailing list (along with the general 386BSD mail- -ing list) and help you form contacts with other users -interested in discussing these topics. - - - -386BSD REGFORM 1 July 1992 - - - - - - - - - - - For information and questions on 386BSD seminars, -tutorials, and materials, contact: - - Jim Joyce - The Gawain Group - 139 Noe Street - San Francisco, California 94114 USA - 1-415-626-7581 - - -_M_o_n_e_t_a_r_y _C_o_n_t_r_i_b_u_t_i_o_n_s _f_o_r _3_8_6_B_S_D _R_e_l_e_a_s_e_s - - 386BSD is intended by the developer to be used for edu- -cational and research purposes. As such, the developer of -this release is making it available to the user at no -charge, in order to encourage and facilitate its use in -research and education. However, 386BSD has been a costly -product to develop and maintain, and each release consumes -enormous resources. - - Release 0.0, the first complete freely available -release of BSD for the 386/486 PC, was completed entirely as -a personal project. 386BSD Release 0.1, the second release -of a complete 386BSD system, was motivated primarily due to -the enthusiasm and contributions of the Release 0.0 user -base. - - In sum, 386BSD has taken over four years of personal -financing on the part of the developers to get it out to the -user. Unfortunately, we are not funded by any granting -agency, university, corporation, or non-profit company. - - If you like 386BSD, you may elect to make a contribu- -tion to defray the cost of development and to demonstrate -support of this project. If just 10% of the number of peo- -ple using 386BSD out there right now sent in $100 each, this -project would be adequately funded for the next two years! - - Should you with to make a contribution, return this -form with your check or money order (suggested contribution: -$50.00US binary and $100.00US source) made out to: - - Lynne Greer Jolitz - 386BSD Registration - 447 61ST Street - Oakland, CA. 94609 USA - - +1-510-420-0174 FAX - ljolitz@cardio.ucsf.edu - CIS 76703,4266 - BIX wjolitz - -If you are located in Europe and you wish to make a - - -386BSD REGFORM 2 July 1992 - - - - - - - - - - -contribution, but you are having difficulty with currency -transactions, please contact for instructions: - - BSD-Projekt - Juergen Fey - UNIX Magazin - Hans-Pinsel-Str. 2 - 8013 Haar bei Muenchen - - +49-89-46-13-356 FAX - jf@unixmag.mut.de - -They will collect monetary contributions for 386BSD and -transfer the funds for you. However, please specify that -this is a "386BSD contribution to defray development costs" -to avoid confusion with other items which are offered by -UNIX Magazin. - - There is no mandatory payment required by the developer -for use of 386BSD Release 0.1. However, your contribution -would be greatly appreciated. - -_O_t_h_e_r _C_o_n_t_r_i_b_u_t_i_o_n_s _t_o _3_8_6_B_S_D - - Software contributions, bug fixes, software ports and -other items are welcome for submission. For novel software -contributions (such as major modules) and ports of public -domain or otherwise unencumbered software, please use the -software.form. For bugs and their appropriate fix, please -use the bugnfix.form. - - Please contact us directly for equipment or other -items. - -_S_e_r_v_i_c_e_s _a_n_d _S_u_p_p_o_r_t _F_i_l_e - - A service.support file is available at official distri- -bution sites. This file contains a unverified and dis- -claimed list of people, groups, and/or companies offering -support for 386BSD. We require that anyone wishing to -appear on this list send mail properly requesting inclusion -or exclusion, to avoid any potential problems. Requests for -inclusion should state the following: name, firm (if any), -contact address and country, contact phone/fax/telex/email, -areas of specialty, rates, and experience. This information -is only for the purposes of aiding users, and is definitely -not intended as *advertising*. - -Thank you for your support. - -Lynne Greer Jolitz -TeleMuse - - -386BSD REGFORM 3 July 1992 - - - - - - - - - - -____________________________________________________________ - - - 386BSD RELEASE 0.1 - USER GROUP - REGISTRATION FORM - AND SURVEY - - - Please complete this registration form and survey and -return it to: - - Lynne Greer Jolitz - 386BSD User Registration - 447 61ST Street - Oakland, CA. 94609 USA - - +1-510-420-0174 FAX - ljolitz@cardio.ucsf.edu - CIS 76703,4266 - -Should you wish to make a monetary contribution, please -attach check or money order (made out to "Lynne Greer Jolitz -- 386BSD Contribution") and return with this registration -form. - -_R_e_g_i_s_t_r_a_t_i_o_n _I_n_f_o_r_m_a_t_i_o_n: - -NAME: -TITLE: -FIRM: -ADDRESS: -COUNTRY: -PHONE: -FAX: -EMAIL: - - -1. A userdirectory file is installed at official distribu- - tion sites to allow 386BSD users to contact and become - acquainted with other 386BSD users. Would you like - your name, company or university, and email address to - be placed in this public directory? (y/n). - - NOTE: You do not have to select this option in order to - participate in the 386BSD SIG and regular mailing lists - and other activities. This directory has been installed - only as a courtesy to the 386BSD user community. Parti- - cipation is not mandatory. - -2. What type of site are you working at? - - college/university - - -386BSD REGFORM 4 July 1992 - - - - - - - - - - - research lab - commercial - individual/consultant - other: - - -3. Do you have Internet access? - -4. Do you work on (commercial/government/university/other) - projects? - - - - -_3_8_6_B_S_D _I_n_t_e_r_e_s_t - -1. Where did you first find out about 386BSD? - (email/magazine/other) - - - - -2. What aspect of 386BSD most interests you? - - source availability - binary availability - advanced operating system - advanced networking - other: - - -3. Why do you think 386BSD is interesting ? - - - - -3. We write regularly in Dr. Dobbs Journal and UNIX Maga- - zin about 386BSD and other topics. What aspect of - 386BSD would you like to read more about? What techni- - cal areas should we cover in more depth? (You can also - contact the editors of DDJ at CIS 76703,50 and the edi- - tors of UNIX Magazin at jf@unixmag.mut.de for comments - regarding articles). - - - - -5. Is a 386BSD forum a good idea? - - - - - - -386BSD REGFORM 5 July 1992 - - - - - - - - - - -6. Would you be interested in attending a seminar on the - installation and maintenance of 386BSD ? Is this a good - idea ? - - - - -7. Would you be interested in attending a seminar on the - kernel internals of 386BSD ? Is this a good idea ? - - - - -8. Would you like to hear more about any of the 386BSD - SIGs forming right now? (Please select a topic of - interest, or add one of your own. I will try to match- - make you to others interested in the same area of - study). - - - - -_3_8_6_B_S_D _R_e_l_e_a_s_e _0._0 - -386BSD Release 0.0 (March 1992) was the first fully- -operational and freely available version of 386BSD. - -1. Did you obtain 386BSD Release 0.0 ? - - - - -2. If yes, were you able to get 386BSD Release 0.0 opera- - tional? - - - - -3. If yes, what did you like about 0.0 ? What was unsatis- - factory ? - - - - -4. If no, why not? - - - - -_3_8_6_B_S_D _R_e_l_e_a_s_e _0._1 _U_s_a_g_e - -1. What do you intend to use 336BSD Release 0.1 for? - - -386BSD REGFORM 6 July 1992 - - - - - - - - - - - development system - networking/gateway - individual research - personal work - replacement for MS-DOS - replacement for other UNIX - other: - - -2. Would students be using this system? (yes/no) If yes, - would 386BSD be integrated into a course or lab study? - - - - -3. What aspects of 386BSD Release 0.1 do you most like? - - - - -4. What aspects of 386BSD Release 0.1 would you like to - see improved? - - - - -5. Where do you think 386BSD should head, in terms of - development goals? - - - - -6. Would you like to help contribute to this effort to - facilitate further releases of 386BSD? (Check all that - apply) - - yes - monetary contribution to 386BSD effort (see instructions) - yes - software enhancements to 386BSD (see bugnfix.form and software.form) - yes - hardware loans for new software testing (see instructions) - yes - other items: - no - - -7 Do you see the need for a centrally-supported version - of 386BSD ? How would this best be done ? (This is - intentionally an open-ended question. We would like to - hear your opinion). - - - - - - - -386BSD REGFORM 7 July 1992 - - - - - - - - - - -_O_t_h_e_r _C_o_m_m_e_n_t_s - -1. If you have any suggestions or comments regarding - 386BSD Release 0.0 or our writings on this subject, - please take a moment and attach them. - - - - - - - -Thank you for completing this survey. Your input will help -us in making 386BSD Release 0.1 more accessible and targeted -to your needs. -____________________________________________________________ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -386BSD REGFORM 8 July 1992 - - - diff --git a/RELNOTES.FreeBSD b/RELNOTES.FreeBSD new file mode 100644 index 0000000000..e65997f89e --- /dev/null +++ b/RELNOTES.FreeBSD @@ -0,0 +1,290 @@ + RELEASE NOTES + FreeBSD + Release 1.0 + +1. Technical overview +--------------------- + +FreeBSD is a freely available, full source 4.3 (+4.4 enhancements) BSD +release for Intel i386/i486 (or compatable) based PC's. It is based +heavily on Bill Jolitz's 386BSD 0.1, with additions from "the patchkit", +NetBSD, CSRG, and the Free Software Foundation. + +Many hundreds of bugs from the 386BSD 0.1 distribution were fixed, +and many out-of-date pieces of software were upgraded to their current +releases in the GAMMA distribution. This 1.0 distribution fixes +many of the first-run problems our GAMMA and EPSILON users reported back +to us. + +Additionally, many packages such as XFree86 1.3, xview 3.2, elm, nntp, +mh and dozens of other miscellaneous utilities have been ported and +are now available as add-ons. See then next section of this document +for more details. + +For a list of contributors, please see the file "CONTRIB.FreeBSD", +which should be bundled with your distribution. + +The core of FreeBSD does not contain DES code which would inhibit its +being exported outside the United States. There is an add-on package +to the core distribution, for use only in the United States, that +contains the programs that normally use DES. The auxilliary packages +provided separately can be used by anyone. Work is in progress to +provide a freely (from outside the U.S.) exportable European distribution +of DES for our non U.S. users. + + +2. Supported Configurations +--------------------------- + +FreeBSD currently runs on a wide variety of ISA and EISA bus based +PC's, ranging from 386sx to 486 class machines (though the 386sx is +not recommended). Support for generic IDE or ESDI drive configurations, +various SCSI controller, network and serial cards is also provided. + +Following is a list of all currently known disk controllers and +ethernet cards known to work with FreeBSD. Other configurations may +very well work, and we have simply not received any indication of +this. + + +2.1. Disk Controllers + +WD1003 (any generic MFM/RLL) +WD1007 (any generic IDE/ESDI) + +Adaptec 154x series ISA SCSI controller as long as you have +less than 16MB of memory. + +Adaptec 174x series EISA SCSI controller in standard and enhanced mode. + +Buslogic 545S. + +Bustec 742A. + +DTC 3290 EISA SCSI controller in 1542 emulation mode as long as +you have less than 16MB of memory. + +Ultra Store 14F and 34F. + +With all supported SCSI controllers, full support is provided for +SCSI-I & SCSI-II peripherals, including Disks, tape drives (including +DAT) and CD ROM drives. Note: This and the mcd driver (Mitsumi CDROM +inteface card) is the only way a CD ROM drive may be currently +attached to a FreeBSD system; we do not support SoundBlaster CDROM +interface, or other "mini SCSI" adapters. + + +2.2. Ethernet cards + +SMC Elite 16 WD8013 ethernet interface, and most other WD8003E, +WD8003EBT, WD8003S, WD8003SBT and WD8013EBT based clones. + +Isolan AT 4141-0 (16 bit) + +Isolink 4110 (8 bit) + +Novell NE1000 and NE2000 ethernet interface. + +3Com 3C503 Etherlink II + + +2.3. Misc + +Various 2 and 4 port serial/parallel cards. + +Mitsumi CDROM interface and drive. + +FreeBSD currently does NOT support IBM's microchannel (MCA) bus, but +support is apparently close to materializing. Details will be posted +as they develop. + + +3. Obtaining FreeBSD. +--------------------- + +You may obtain FreeBSD in a variety of ways: + +1. FTP/Mail + +You can ftp FreeBSD and any or all of its optional packages from +`freebsd.cdrom.com' - the offical FreeBSD release site. + +The FreeBSD software is being mirrored at the following locations: + +Country Site/Directory +======= ===================================================== + +Austria ftp.tu-graz.ac.at:/pub/FreeBSD +Germany ftp.informatik.tu-muenchen.de:pub/comp/os/bsd/FreeBSD +Germany ftp.uni-duisburg.de:/pub/unix/FreeBSD +Hong Kong ftp.cs.cuhk.hk:/pub/FreeBSD + + +If you do not have access to the internet and electronic mail is your +only recourse, then you may still fetch the files by sending mail to +`ftpmail@decwrl.dec.com' - putting the keyword "help" in your message +to get more information on how to fetch files from freebsd.cdrom.com. +Note: This approach will end up sending many *tens of megabytes* +through the mail, and should only be employed as an absolute LAST +resort! + + +2. CDROM + +FreeBSD may be ordered on CDROM from: + + Walnut Creek CDROM + 4041 Pike Lane, Suite D + Concord CA 94520 + 1-800-786-9907, +1-510-674-0783, +1-510-674-0821 (fax) + +Or via the internet from orders@cdrom.com. There current catalog can +be obtained via ftp as ftp.cdrom.com:/cdrom/catalog. + +Cost is $39.95. Shipping (per order not per disc) is $5 in the US, Canada, +or Mexico and $10.00 overseas. They accept Visa, Mastercard, American +Express, and ship COD to the United States. California residents please +add 8.25% sales tax. + +Should you be dissatisfied for any reason, the CD comes with an +unconditional return policy. + +Note that Walnut Creek CDROM does NOT provide technical support for FreeBSD, +you need to contact the FreeBSD team for that. Please see section 4 for +more information. + + +3. 1/4" Tape, 4mm DAT, floppy + + Winning Strategies, Inc. 61 Crestwood Drive #18, Daly City, CA 94015 + + + 3.5" & 5.25" floppies $200.00 + Contains "core" FreeBSD. + Shipping - add $15 for U.S. and Canada, $25 for overseas. + + 150MB QIC tape $100.00 + Contains "core" FreeBSD + XFree86. + Choice of either 3.5" or 5.25" boot floppies. + Shipping - add $10 for U.S. and Canada, $30 for overseas. + + DAT & Exabyte tapes $100.00 + Contains everything on the Walnut Creek CD ROM. + Choice of either 3.5" or 5.25" boot floppies. + Shipping - same as for QIC tape. + + All packages contain printed copies of the release and installation notes. + + Visa, Mastercard, U.S. $ Money Orders, or U.S. $ checks drawn on a + U.S. bank accepted for payment. + + +It should be noted, lest you get the wrong impression that "FreeBSD" +is anything but, that almost no one in the "core team" makes money +from distributions or anything else connected with FreeBSD. We simply +provide this information as a public service for those wishing to get +their releases from somewhere other than the net (and the easier it +is for you to obtain our software, the happier we are). + + +4. Reporting problems, making suggestions, submitting code. +----------------------------------------------------------- + +Your suggestions, bug reports and contributions of code are always +valued - please do not hesitate to report any problems you may find +(preferably with a fix attached if you can!). + +The prefered method to submit bug reports from a machine with internet +mail connectivity is to use the sendbug command. Bug reports will be +dutifully filed by our faithful bugfiler program and you can be sure +that we'll do our best to respond to all reported bugs as soon as +possible. + +If, for some reason, you are unable to use the sendbug command to +submit a bug report, you can try to send it to: + + FreeBSD-bugs@freefall.cdrom.com + + +Otherwise, for any questions or suggestions, please send mail to: + + FreeBSD-questions@freefall.cdrom.com + +Additionally, being a volunteer effort, we are always happy to have +extra hands willing to help - there are already far more enhancements +to be done than we can ever manage to do by ourselves! To contact us +on technical matters, or with offers of help, you may send mail to: + + FreeBSD-hackers@freefall.cdrom.com + +All but the FreeBSD-bugs groups can be freely joined by anyone wishing to +do so. Send mail to MajorDomo@freefall.cdrom.com and include the keyword +`help' on a line by itself somewhere in the body of the message. This +will give you more information on joining the various lists, accessing +archives, etc. + + +5. Acknowledgements +------------------- + +FreeBSD represents the cumulative work of many dozens, if not +hundreds, of individuals from around the world who have worked very +hard to bring you this release. It would be very difficult, if not +impossible, to enumerate everyone who's contributed to FreeBSD, but +nonetheless we shall try (in alphabetical order, of course). If your +name is not mentioned, please be assured that its omission is entirely +accidental. + + +The Computer Systems Research Group (CSRG), U.C. Berkeley. + +Bill Jolitz, for his extensive work with 386BSD. + +The FreeBSD "core" group: + + J.T. Conklin + David Greenman + Rodney W. Grimes + Jordan K. Hubbard + Scott Mace + Andrew Moore + Rich Murphey + Paul Richards + Christoph Robitschko + Andreas Schulz + Nate Williams + + +Special mention to: + + Robert Bruce and Jack Velte of Walnut Creek CDROM, without + whose help (and continuing support) this release would never + have been possible. + + Dermot McDonnell for his donation of a Toshiba XM3401B CDROM + drive. + + The NetBSD group for their frequent assistance and commentary. + + Additional FreeBSD helpers and beta testers: + + Gary Browing Jon Cargille + Chris Demetriou Julian Elischer + Bruce Evans Sean Eric Fagan + Guy Helmer Terry Lambert + Gary Moyer Jaye Mathisen + L Jonas Olsson Chris Provenzano + Dave Rivers Guido van Rooij + Steven Wallace Rick Weldon + Terry Williams Garrett Wollman + + And everyone at Montana State University for their initial support. + + +Thanks to everyone, especially those not mentioned, and we sincerely +hope you enjoy this release of FreeBSD! + + + The FreeBSD Core Group + +$Id: RELNOTES.FreeBSD,v 1.14 1993/10/16 12:02:43 rgrimes Exp $ diff --git a/SOFTWARE.FORM b/SOFTWARE.FORM deleted file mode 100644 index ca4067e3cc..0000000000 --- a/SOFTWARE.FORM +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - - 386BSD RELEASE 0.1 - SOFTWARE CONTRIBUTION FORM - - - We are always pleased to look at enhancements, addi- -tions or ports of public domain software for possible incor- -poration into 386BSD releases. However, all contributed -software must contain the following items: - -1. The author's name, date, and copyright (if any). If a - modification of a public-domain work, modification - attribution and original author. - -2. A brief description of the software module or item. - -3. The following statement: "This software contains, to - the best of my knowledge, no proprietary, trade secret, - or otherwise encumbered code. I am making this - software available for incorporation into 386BSD, - understanding that 386BSD is a freely redistributable - and modifiable system." - - In addition, the software should be in the following -format: - -1. Release number upon which generated (0.0, 0.1, etc). - -2. Source code. Generated binary. - -3. List of known bugs. - - 386BSD is a freely available work. All developers sub- -mitting software for official 386BSD releases should be -aware of this fact. Given the extremely wide distribution of -this software, we cannot regulate or enforce specific usage, -license, or proprietary restrictions, nor can we collect any -monies or fees for use. Those developers who wish to -encumber their software in this manner should find other -mechanisms for distribution. - - We will not review or accept any code which is marked -"proprietary" or "trade secret". - - We suggest that copyrights (or copylefts) not be made -too "encumbering" in order to encourage usage, enhancements, -and modifications of the submitted software. (For a sug- -gested copyright format, we suggest the University of Cali- -fornia copyright format.) - - Due to constraints on distribution size, research -directions, and number of submissions, we cannot guarantee -that any particular piece of software will be incorporated -into the next release. However, we always encourage people - - -386BSD SOFTWARE.FORM 1 July 1992 - - - - - - - - - - -to make their software available to other 386BSD Users via -the network and user groups. That way, a large number of -people can immediately benefit from your software and pro- -vide critical feedback for enhancements and refinements. - - Please complete this software contribution form and -return it along with your software contribution to: - - Lynne Greer Jolitz - 386BSD User Registration - 447 61ST Street - Oakland, CA. 94609 USA - - +1-510-420-0174 FAX - ljolitz@cardio.ucsf.edu - CIS 76703,4266 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -386BSD SOFTWARE.FORM 2 July 1992 - - - diff --git a/bin/Makefile b/bin/Makefile index 7fa07f5cf6..cfe9d20398 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.33.1.1 (Berkeley) 5/6/91 -SUBDIR= cat chmod cp csh date dd df echo expr hostname kill ln ls \ - mkdir mv ps pwd rcp rm rmail rmdir sh sleep stty sync test +SUBDIR= cat chmod cp csh date dd df domainname echo ed expr hostname \ + kill ln ls mkdir mv ps pwd rcp rm rmail rmdir sh sleep stty sync test .include diff --git a/bin/cat/cat.c b/bin/cat/cat.c index 631f7c42e7..39196e225b 100644 --- a/bin/cat/cat.c +++ b/bin/cat/cat.c @@ -58,9 +58,13 @@ int bflag, eflag, nflag, sflag, tflag, vflag; int rval; char *filename; -void cook_args(), cook_buf(), raw_args(), raw_cat(); -void err __P((int, const char *, ...)); +void cook_args __P((char **)); +void cook_buf __P((FILE *)); +void raw_args __P((char **)); +void raw_cat __P((int)); +void err __P((int, const char *, ...)); +int main(argc, argv) int argc; char **argv; @@ -68,7 +72,7 @@ main(argc, argv) extern int optind; int ch; - while ((ch = getopt(argc, argv, "benstuv")) != EOF) + while ((ch = getopt(argc, argv, "benstuv")) != -1) switch (ch) { case 'b': bflag = nflag = 1; /* -b implies -n */ @@ -91,6 +95,7 @@ main(argc, argv) case 'v': vflag = 1; break; + default: case '?': (void)fprintf(stderr, "usage: cat [-benstuv] [-] [file ...]\n"); @@ -188,7 +193,7 @@ cook_buf(fp) break; } if (ferror(fp)) { - err(0, "%s: %s", strerror(errno)); + err(0, "%s: %s", filename, strerror(errno)); clearerr(fp); } if (ferror(stdout)) diff --git a/bin/chmod/chmod.c b/bin/chmod/chmod.c index 99e08bbef7..a67e3f91c4 100644 --- a/bin/chmod/chmod.c +++ b/bin/chmod/chmod.c @@ -38,30 +38,36 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)chmod.c 5.19 (Berkeley) 3/12/91"; +static char sccsid[] = "@(#)chmod.c 5.21 (Berkeley) 1/27/92"; #endif /* not lint */ #include #include +#include #include +#include #include +#include #include -extern int errno; int retval; +void err __P((const char *, ...)); +void error __P((char *)); +void usage __P((void)); + +int main(argc, argv) int argc; - char **argv; + char *argv[]; { - extern int optind; register FTS *fts; register FTSENT *p; register int oct, omode; - register char *mode; - mode_t *set, *setmode(); struct stat sb; + mode_t *set; int ch, fflag, rflag; + char *ep, *mode; fflag = rflag = 0; while ((ch = getopt(argc, argv, "Rfrwx")) != EOF) @@ -89,23 +95,21 @@ done: argv += optind; mode = *argv; if (*mode >= '0' && *mode <= '7') { - omode = (int)strtol(mode, (char **)NULL, 8); + omode = (int)strtol(mode, &ep, 8); + if (omode < 0 || *ep) + err("invalid file mode: %s", mode); oct = 1; } else { - if (!(set = setmode(mode))) { - (void)fprintf(stderr, "chmod: invalid file mode.\n"); - exit(1); - } + if (!(set = setmode(mode))) + err("invalid file mode: %s", mode); oct = 0; } retval = 0; if (rflag) { - if (!(fts = fts_open(++argv, - oct ? FTS_NOSTAT|FTS_PHYSICAL : FTS_PHYSICAL, 0))) { - (void)fprintf(stderr, "chmod: %s.\n", strerror(errno)); - exit(1); - } + if ((fts = fts_open(++argv, + oct ? FTS_NOSTAT|FTS_PHYSICAL : FTS_PHYSICAL, 0)) == NULL) + err("%s", strerror(errno)); while (p = fts_read(fts)) switch(p->fts_info) { case FTS_D: @@ -113,12 +117,10 @@ done: argv += optind; case FTS_DNR: case FTS_ERR: case FTS_NS: - (void)fprintf(stderr, "chmod: %s: %s.\n", - p->fts_path, strerror(errno)); - exit(1); + err("%s: %s", p->fts_path, strerror(errno)); default: if (chmod(p->fts_accpath, oct ? omode : - getmode(set, p->fts_statb.st_mode)) && + getmode(set, p->fts_statp->st_mode)) && !fflag) error(p->fts_path); break; @@ -137,15 +139,46 @@ done: argv += optind; exit(retval); } +void error(name) char *name; { - (void)fprintf(stderr, "chmod: %s: %s.\n", name, strerror(errno)); + (void)fprintf(stderr, "chmod: %s: %s\n", name, strerror(errno)); retval = 1; } +void usage() { - (void)fprintf(stderr, "chmod: chmod [-R] mode file ...\n"); + (void)fprintf(stderr, "usage: chmod [-R] mode file ...\n"); + exit(1); +} + +#if __STDC__ +#include +#else +#include +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "chmod: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); exit(1); + /* NOTREACHED */ } diff --git a/bin/cp/cp.c b/bin/cp/cp.c index 8124e8d70d..72dee77695 100644 --- a/bin/cp/cp.c +++ b/bin/cp/cp.c @@ -41,7 +41,7 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)cp.c 5.24 (Berkeley) 5/6/91"; +static char sccsid[] = "@(#)cp.c 5.26 (Berkeley) 10/27/91"; #endif /* not lint */ /* @@ -60,6 +60,7 @@ static char sccsid[] = "@(#)cp.c 5.24 (Berkeley) 5/6/91"; #include #include +#include #include #include #include @@ -68,7 +69,16 @@ static char sccsid[] = "@(#)cp.c 5.24 (Berkeley) 5/6/91"; #include #include #include -#include "cp.h" +#include "extern.h" + +static void copy __P((void)); +static void copy_dir __P((void)); +static void copy_fifo __P((struct stat *, int)); +static void copy_file __P((struct stat *, int)); +static void copy_link __P((int)); +static void copy_special __P((struct stat *, int)); +static void setfile __P((struct stat *, int)); +static void usage __P((void)); PATH_T from = { from.p_path, "" }; PATH_T to = { to.p_path, "" }; @@ -77,7 +87,7 @@ uid_t myuid; int exit_val, myumask; int iflag, pflag, orflag, rflag; int (*statfcn)(); -char *buf, *progname; +char *progname; main(argc, argv) int argc; @@ -134,12 +144,6 @@ main(argc, argv) exit(1); } - buf = (char *)malloc(MAXBSIZE); - if (!buf) { - (void)fprintf(stderr, "%s: out of space.\n", progname); - exit(1); - } - myuid = getuid(); /* copy the umask for explicit mode setting */ @@ -169,7 +173,7 @@ main(argc, argv) r = stat(to.p_path, &to_stat); if (r == -1 && errno != ENOENT) { - error(to.p_path); + err("%s: %s", to.p_path, strerror(errno)); exit(1); } if (r == -1 || !S_ISDIR(to_stat.st_mode)) { @@ -189,15 +193,11 @@ main(argc, argv) * Case (2). Target is a directory. */ for (;; ++argv) { - if (!path_set(&from, *argv)) { - exit_val = 1; + if (!path_set(&from, *argv)) continue; - } - old_to = path_append(&to, path_basename(&from), -1); - if (!old_to) { - exit_val = 1; + if (!(old_to = + path_append(&to, path_basename(&from), -1))) continue; - } copy(); if (!--argc) break; @@ -208,6 +208,7 @@ main(argc, argv) } /* copy file or directory at "from" to "to". */ +static void copy() { struct stat from_stat, to_stat; @@ -215,7 +216,7 @@ copy() statval = statfcn(from.p_path, &from_stat); if (statval == -1) { - error(from.p_path); + err("%s: %s", from.p_path, strerror(errno)); return; } @@ -256,11 +257,11 @@ copy() * umask blocks owner writes cp fails. */ if (mkdir(to.p_path, from_stat.st_mode|S_IRWXU) < 0) { - error(to.p_path); + err("%s: %s", to.p_path, strerror(errno)); return; } } - else if (!S_ISDIR(to_stat.st_mode) != S_IFDIR) { + else if (!S_ISDIR(to_stat.st_mode)) { (void)fprintf(stderr, "%s: %s: not a directory.\n", progname, to.p_path); return; @@ -293,15 +294,18 @@ copy() copy_file(&from_stat, dne); } +static void copy_file(fs, dne) struct stat *fs; int dne; { + static char buf[MAXBSIZE]; register int from_fd, to_fd, rcount, wcount; struct stat to_stat; + char *p; if ((from_fd = open(from.p_path, O_RDONLY, 0)) == -1) { - error(from.p_path); + err("%s: %s", from.p_path, strerror(errno)); return; } @@ -332,20 +336,35 @@ copy_file(fs, dne) fs->st_mode & ~(S_ISUID|S_ISGID)); if (to_fd == -1) { - error(to.p_path); + err("%s: %s", to.p_path, strerror(errno)); (void)close(from_fd); return; } - while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { - wcount = write(to_fd, buf, rcount); - if (rcount != wcount || wcount == -1) { - error(to.p_path); - break; + /* + * Mmap and write if less than 8M (the limit is so we don't totally + * trash memory on big files. This is really a minor hack, but it + * wins some CPU back. + */ + if (fs->st_size <= 8 * 1048576) { + if ((p = mmap(NULL, fs->st_size, PROT_READ, + MAP_FILE, from_fd, (off_t)0)) == (char *)-1) + err("%s: %s", from.p_path, strerror(errno)); + madvise((caddr_t) p, fs->st_size, MADV_SEQUENTIAL); + if (write(to_fd, p, fs->st_size) != fs->st_size) + err("%s: %s", to.p_path, strerror(errno)); + munmap((caddr_t) p, fs->st_size); + } else { + while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { + wcount = write(to_fd, buf, rcount); + if (rcount != wcount || wcount == -1) { + err("%s: %s", to.p_path, strerror(errno)); + break; + } } + if (rcount < 0) + err("%s: %s", from.p_path, strerror(errno)); } - if (rcount < 0) - error(from.p_path); if (pflag) setfile(fs, to_fd); /* @@ -354,16 +373,17 @@ copy_file(fs, dne) */ else if (fs->st_mode & (S_ISUID|S_ISGID) && fs->st_uid == myuid) if (fstat(to_fd, &to_stat)) - error(to.p_path); + err("%s: %s", to.p_path, strerror(errno)); #define RETAINBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) - error(to.p_path); + err("%s: %s", to.p_path, strerror(errno)); (void)close(from_fd); if (close(to_fd)) - error(to.p_path); + err("%s: %s", to.p_path, strerror(errno)); } +static void copy_dir() { struct stat from_stat; @@ -394,14 +414,12 @@ copy_dir() if (dp->d_namlen <= 2 && dp->d_name[0] == '.' && (dp->d_name[1] == NULL || dp->d_name[1] == '.')) goto done; - old_from = path_append(&from, dp->d_name, (int)dp->d_namlen); - if (!old_from) { - exit_val = 1; + if (!(old_from = + path_append(&from, dp->d_name, (int)dp->d_namlen))) goto done; - } if (statfcn(from.p_path, &from_stat) < 0) { - error(dp->d_name); + err("%s: %s", dp->d_name, strerror(errno)); path_restore(&from, old_from); goto done; } @@ -409,15 +427,13 @@ copy_dir() path_restore(&from, old_from); continue; } - old_to = path_append(&to, dp->d_name, (int)dp->d_namlen); - if (old_to) { + if (old_to = path_append(&to, dp->d_name, (int)dp->d_namlen)) { copy(); path_restore(&to, old_to); - } else - exit_val = 1; + } path_restore(&from, old_from); done: dir_list[i] = NULL; - (void)free((void *)dp); + free(dp); } /* copy directories */ @@ -425,27 +441,26 @@ done: dir_list[i] = NULL; dp = dir_list[i]; if (!dp) continue; - old_from = path_append(&from, dp->d_name, (int) dp->d_namlen); - if (!old_from) { - exit_val = 1; - (void)free((void *)dp); + if (!(old_from = + path_append(&from, dp->d_name, (int)dp->d_namlen))) { + free(dp); continue; } - old_to = path_append(&to, dp->d_name, (int) dp->d_namlen); - if (!old_to) { - exit_val = 1; - (void)free((void *)dp); + if (!(old_to = + path_append(&to, dp->d_name, (int)dp->d_namlen))) { + free(dp); path_restore(&from, old_from); continue; } copy(); - free((void *)dp); + free(dp); path_restore(&from, old_from); path_restore(&to, old_to); } - free((void *)dir_list); + free(dir_list); } +static void copy_link(exists) int exists; { @@ -453,67 +468,67 @@ copy_link(exists) char link[MAXPATHLEN]; if ((len = readlink(from.p_path, link, sizeof(link))) == -1) { - error(from.p_path); + err("readlink: %s: %s", from.p_path, strerror(errno)); return; } link[len] = '\0'; if (exists && unlink(to.p_path)) { - error(to.p_path); + err("unlink: %s: %s", to.p_path, strerror(errno)); return; } if (symlink(link, to.p_path)) { - error(link); + err("symlink: %s: %s", link, strerror(errno)); return; } } +static void copy_fifo(from_stat, exists) struct stat *from_stat; int exists; { if (exists && unlink(to.p_path)) { - error(to.p_path); + err("unlink: %s: %s", to.p_path, strerror(errno)); return; } if (mkfifo(to.p_path, from_stat->st_mode)) { - error(to.p_path); + err("mkfifo: %s: %s", to.p_path, strerror(errno)); return; } if (pflag) setfile(from_stat, 0); } +static void copy_special(from_stat, exists) struct stat *from_stat; int exists; { if (exists && unlink(to.p_path)) { - error(to.p_path); + err("unlink: %s: %s", to.p_path, strerror(errno)); return; } if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { - error(to.p_path); + err("mknod: %s: %s", to.p_path, strerror(errno)); return; } if (pflag) setfile(from_stat, 0); } +static void setfile(fs, fd) register struct stat *fs; int fd; { static struct timeval tv[2]; - char path[100]; fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; tv[0].tv_sec = fs->st_atime; tv[1].tv_sec = fs->st_mtime; - if (utimes(to.p_path, tv)) { - (void)snprintf(path, sizeof(path), "utimes: %s", to.p_path); - error(path); - } + if (utimes(to.p_path, tv)) + err("utimes: %s: %s", to.p_path, strerror(errno)); /* * Changing the ownership probably won't succeed, unless we're root * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting @@ -522,29 +537,46 @@ setfile(fs, fd) */ if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : chown(to.p_path, fs->st_uid, fs->st_gid)) { - if (errno != EPERM) { - (void)snprintf(path, sizeof(path), - "chown: %s", to.p_path); - error(path); - } + if (errno != EPERM) + err("chown: %s: %s", to.p_path, strerror(errno)); fs->st_mode &= ~(S_ISUID|S_ISGID); } - if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) { - (void)snprintf(path, sizeof(path), "chown: %s", to.p_path); - error(path); - } -} - -error(s) - char *s; -{ - exit_val = 1; - (void)fprintf(stderr, "%s: %s: %s\n", progname, s, strerror(errno)); + if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) + err("chown: %s: %s", to.p_path, strerror(errno)); } +static void usage() { (void)fprintf(stderr, -"usage: cp [-Rfhip] src target;\n or: cp [-Rfhip] src1 ... srcN directory\n"); +"usage: cp [-Rfhip] src target;\n cp [-Rfhip] src1 ... srcN directory\n"); exit(1); } + +#if __STDC__ +#include +#else +#include +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "%s: ", progname); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit_val = 1; +} diff --git a/bin/cp/extern.h b/bin/cp/extern.h new file mode 100644 index 0000000000..ad4cf2d083 --- /dev/null +++ b/bin/cp/extern.h @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 5.3 (Berkeley) 10/27/91 + */ + +typedef struct { + char *p_end; /* pointer to NULL at end of path */ + char p_path[MAXPATHLEN + 1]; /* pointer to the start of a path */ +} PATH_T; + +extern char *progname; /* program name */ + +#include + +__BEGIN_DECLS +void err __P((const char *fmt, ...)); +int path_set __P((PATH_T *, char *)); +char *path_append __P((PATH_T *, char *, int)); +char *path_basename __P((PATH_T *)); +void path_restore __P((PATH_T *, char *)); +__END_DECLS diff --git a/bin/cp/path.c b/bin/cp/path.c index 96b9e1694a..a848921935 100644 --- a/bin/cp/path.c +++ b/bin/cp/path.c @@ -32,13 +32,12 @@ */ #ifndef lint -static char sccsid[] = "@(#)path.c 5.1 (Berkeley) 4/3/91"; +static char sccsid[] = "@(#)path.c 5.2 (Berkeley) 10/27/91"; #endif /* not lint */ #include -#include #include -#include "cp.h" +#include "extern.h" /* * These functions manipulate paths in PATH_T structures. @@ -59,13 +58,13 @@ static char sccsid[] = "@(#)path.c 5.1 (Berkeley) 4/3/91"; * Move specified string into path. Convert "" to "." to handle BSD * semantics for a null path. Strip trailing slashes. */ +int path_set(p, string) register PATH_T *p; char *string; { if (strlen(string) > MAXPATHLEN) { - (void)fprintf(stderr, - "%s: %s: name too long.\n", progname, string); + err("%s: name too long", string); return(0); } @@ -99,8 +98,7 @@ path_append(p, name, len) /* The "+ 1" accounts for the '/' between old path and name. */ if ((len + p->p_end - p->p_path + 1) > MAXPATHLEN) { - (void)fprintf(stderr, - "%s: %s/%s: name too long.\n", progname, p->p_path, name); + err("%s/%s: name too long", p->p_path, name); return(0); } diff --git a/bin/csh/Makefile b/bin/csh/Makefile index 3d986ef22e..dc7ee8075d 100644 --- a/bin/csh/Makefile +++ b/bin/csh/Makefile @@ -6,12 +6,12 @@ # To profile, put -DPROF in DEFS and -pg in CFLAGS, and recompile. PROG= csh -CFLAGS+=-fcombine-regs -fstrength-reduce -DFILEC -DNLS -DSHORT_STRINGS -I. +CFLAGS+=-fstrength-reduce -DFILEC -DNLS -DSHORT_STRINGS -I. SRCS= alloc.c char.c const.c csh.c dir.c dol.c err.c exec.c exp.c file.c \ func.c glob.c hist.c init.c lex.c misc.c parse.c print.c printf.c \ proc.c sem.c set.c str.c time.c -MAN1= csh.0 +MAN1= csh.1 MLINKS= csh.1 limit.1 csh.1 alias.1 csh.1 bg.1 csh.1 dirs.1 csh.1 fg.1 \ csh.1 foreach.1 csh.1 history.1 csh.1 jobs.1 csh.1 popd.1 \ csh.1 pushd.1 csh.1 rehash.1 csh.1 repeat.1 csh.1 suspend.1 \ diff --git a/bin/csh/csh.1 b/bin/csh/csh.1 index b684007e3f..38bd737d44 100644 --- a/bin/csh/csh.1 +++ b/bin/csh/csh.1 @@ -950,8 +950,8 @@ and commands. The following operators are available: .Bd -ragged -offset indent -\&|\&| && \&| *(ua & == != =~ !~ <= >= -< > << >> + \- * / % ! ~ ( ) +\&|\&| && \&| \*(ua & == != =~ !~ <= >= +< > << >> + \- * / % ! ~ ( ) .Ed .Pp Here the precedence increases to the right, @@ -986,7 +986,7 @@ and file enquiries of the form where .Ic l is one of: -.Bd -ragged -offset indent +.Bd -literal -offset indent r read access w write access x execute access diff --git a/bin/csh/dir.c b/bin/csh/dir.c index dfe4159bd6..b9cad32746 100644 --- a/bin/csh/dir.c +++ b/bin/csh/dir.c @@ -129,7 +129,8 @@ dinit(hp) swd.st_ino == shp.st_ino) tcp = cwd; } - cp = dcanon(str2short(tcp), STRNULL); + /* EWS: dcanon frees its argument; can't just use str2short */ + cp = dcanon(Strsave(str2short(tcp)), STRNULL); } } diff --git a/bin/date/date.1 b/bin/date/date.1 index fc1e2090ad..56b7233cea 100644 --- a/bin/date/date.1 +++ b/bin/date/date.1 @@ -106,7 +106,7 @@ in the manual page, as well as any arbitrary text. The format string for the default display is: .Bd -literal -offset indent -``%a %b %e %H:%M:%S %Z n''. +``%a %b %e %H:%M:%S %Z %Y''. .Ed .Pp If an operand does not have a leading plus sign, it is interpreted as diff --git a/bin/date/date.c b/bin/date/date.c index 0c57318f26..232345e1fe 100644 --- a/bin/date/date.c +++ b/bin/date/date.c @@ -109,7 +109,7 @@ main(argc, argv) exit(1); } - format = "%a %b %e %H:%M:%S %Z %Y\n"; + format = "%a %b %e %H:%M:%S %Z %Y"; /* allow the operands in any order */ if (*argv && **argv == '+') { @@ -126,7 +126,7 @@ main(argc, argv) format = *argv + 1; (void)strftime(buf, sizeof(buf), format, localtime(&tval)); - (void)printf("%s", buf); + (void)printf("%s\n", buf); exit(retval); } @@ -147,6 +147,7 @@ setthetime(p) if (t = index(p, '.')) { /* .ss */ *t++ = '\0'; + lt->tm_sec = ATOI2(t); if (lt->tm_sec > 61) badformat(); } else diff --git a/bin/dd/Makefile b/bin/dd/Makefile index f7ed887b9c..44d4c1e1ee 100644 --- a/bin/dd/Makefile +++ b/bin/dd/Makefile @@ -1,5 +1,6 @@ +# @(#)Makefile 5.4 (Berkeley) 11/23/91 PROG= dd -NOMAN=noman +SRCS= args.c conv.c conv_tab.c dd.c misc.c position.c .include diff --git a/bin/dd/args.c b/bin/dd/args.c new file mode 100644 index 0000000000..3d49a3ebe9 --- /dev/null +++ b/bin/dd/args.c @@ -0,0 +1,373 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)args.c 5.5 (Berkeley) 7/29/91"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include "dd.h" +#include "extern.h" + +static u_long get_bsz __P((char *)); + +static void f_bs __P((char *)); +static void f_cbs __P((char *)); +static void f_conv __P((char *)); +static void f_count __P((char *)); +static void f_files __P((char *)); +static void f_ibs __P((char *)); +static void f_if __P((char *)); +static void f_obs __P((char *)); +static void f_of __P((char *)); +static void f_seek __P((char *)); +static void f_skip __P((char *)); + +static struct arg { + char *name; + void (*f) __P((char *)); + u_int set, noset; +} args[] = { + "bs", f_bs, C_BS, C_BS|C_IBS|C_OBS, + "cbs", f_cbs, C_CBS, C_CBS, + "conv", f_conv, 0, 0, + "count", f_count, C_COUNT, C_COUNT, + "files", f_files, C_FILES, C_FILES, + "ibs", f_ibs, C_IBS, C_BS|C_IBS, + "if", f_if, C_IF, C_IF, + "obs", f_obs, C_OBS, C_BS|C_OBS, + "of", f_of, C_OF, C_OF, + "seek", f_seek, C_SEEK, C_SEEK, + "skip", f_skip, C_SKIP, C_SKIP, +}; + +static char *oper; + +/* + * args -- parse JCL syntax of dd. + */ +void +jcl(argv) + register char **argv; +{ + register struct arg *ap; + struct arg tmp; + char *arg; + static int c_arg __P((const void *, const void *)); + + in.dbsz = out.dbsz = 512; + + while (oper = *++argv) { + if ((arg = index(oper, '=')) == NULL) + err("unknown operand %s", oper); + *arg++ = '\0'; + if (!*arg) + err("no value specified for %s", oper); + tmp.name = oper; + if (!(ap = (struct arg *)bsearch(&tmp, args, + sizeof(args)/sizeof(struct arg), sizeof(struct arg), + c_arg))) + err("unknown operand %s", tmp.name); + if (ddflags & ap->noset) + err("%s: illegal argument combination or already set", + tmp.name); + ddflags |= ap->set; + ap->f(arg); + } + + /* Final sanity checks. */ + + if (ddflags & C_BS) { + /* + * Bs is turned off by any conversion -- we assume the user + * just wanted to set both the input and output block sizes + * and didn't want the bs semantics, so we don't warn. + */ + if (ddflags & (C_BLOCK|C_LCASE|C_SWAB|C_UCASE|C_UNBLOCK)) + ddflags &= ~C_BS; + + /* Bs supersedes ibs and obs. */ + if (ddflags & C_BS && ddflags & (C_IBS|C_OBS)) + warn("bs supersedes ibs and obs"); + } + + /* + * Ascii/ebcdic and cbs implies block/unblock. + * Block/unblock requires cbs and vice-versa. + */ + if (ddflags & (C_BLOCK|C_UNBLOCK)) { + if (!(ddflags & C_CBS)) + err("record operations require cbs"); + if (cbsz == 0) + err("cbs cannot be zero"); + cfunc = ddflags & C_BLOCK ? block : unblock; + } else if (ddflags & C_CBS) { + if (ddflags & (C_ASCII|C_EBCDIC)) { + if (ddflags & C_ASCII) { + ddflags |= C_UNBLOCK; + cfunc = unblock; + } else { + ddflags |= C_BLOCK; + cfunc = block; + } + } else + err("cbs meaningless if not doing record operations"); + if (cbsz == 0) + err("cbs cannot be zero"); + } else + cfunc = def; + + if (in.dbsz == 0 || out.dbsz == 0) + err("buffer sizes cannot be zero"); + + /* + * Read, write and seek calls take ints as arguments. Seek sizes + * could be larger if we wanted to do it in stages or check only + * regular files, but it's probably not worth it. + */ + if (in.dbsz > INT_MAX || out.dbsz > INT_MAX) + err("buffer sizes cannot be greater than %d", INT_MAX); + if (in.offset > INT_MAX / in.dbsz || out.offset > INT_MAX / out.dbsz) + err("seek offsets cannot be larger than %d", INT_MAX); +} + +static int +c_arg(a, b) + const void *a, *b; +{ + return (strcmp(((struct arg *)a)->name, ((struct arg *)b)->name)); +} + +static void +f_bs(arg) + char *arg; +{ + in.dbsz = out.dbsz = (int)get_bsz(arg); +} + +static void +f_cbs(arg) + char *arg; +{ + cbsz = (int)get_bsz(arg); +} + +static void +f_count(arg) + char *arg; +{ + cpy_cnt = (u_int)get_bsz(arg); + if (!cpy_cnt) + terminate(0); +} + +static void +f_files(arg) + char *arg; +{ + files_cnt = (int)get_bsz(arg); +} + +static void +f_ibs(arg) + char *arg; +{ + if (!(ddflags & C_BS)) + in.dbsz = (int)get_bsz(arg); +} + +static void +f_if(arg) + char *arg; +{ + in.name = arg; +} + +static void +f_obs(arg) + char *arg; +{ + if (!(ddflags & C_BS)) + out.dbsz = (int)get_bsz(arg); +} + +static void +f_of(arg) + char *arg; +{ + out.name = arg; +} + +static void +f_seek(arg) + char *arg; +{ + out.offset = (u_int)get_bsz(arg); +} + +static void +f_skip(arg) + char *arg; +{ + in.offset = (u_int)get_bsz(arg); +} + +static struct conv { + char *name; + u_int set, noset; + u_char *ctab; +} clist[] = { + "ascii", C_ASCII, C_EBCDIC, e2a_POSIX, + "block", C_BLOCK, C_UNBLOCK, NULL, + "ebcdic", C_EBCDIC, C_ASCII, a2e_POSIX, + "ibm", C_EBCDIC, C_ASCII, a2ibm_POSIX, + "lcase", C_LCASE, C_UCASE, NULL, + "noerror", C_NOERROR, 0, NULL, + "notrunc", C_NOTRUNC, 0, NULL, + "oldascii", C_ASCII, C_EBCDIC, e2a_32V, + "oldebcdic", C_EBCDIC, C_ASCII, a2e_32V, + "oldibm", C_EBCDIC, C_ASCII, a2ibm_32V, + "swab", C_SWAB, 0, NULL, + "sync", C_SYNC, 0, NULL, + "ucase", C_UCASE, C_LCASE, NULL, + "unblock", C_UNBLOCK, C_BLOCK, NULL, +}; + +static void +f_conv(arg) + char *arg; +{ + register struct conv *cp; + struct conv tmp; + static int c_conv __P((const void *, const void *)); + + while (arg != NULL) { + tmp.name = strsep(&arg, ","); + if (!(cp = (struct conv *)bsearch(&tmp, clist, + sizeof(clist)/sizeof(struct conv), sizeof(struct conv), + c_conv))) + err("unknown conversion %s", tmp.name); + if (ddflags & cp->noset) + err("%s: illegal conversion combination", tmp.name); + ddflags |= cp->set; + if (cp->ctab) + ctab = cp->ctab; + } +} + +static int +c_conv(a, b) + const void *a, *b; +{ + return (strcmp(((struct conv *)a)->name, ((struct conv *)b)->name)); +} + +/* + * Convert an expression of the following forms to an unsigned long. + * 1) A positive decimal number. + * 2) A positive decimal number followed by a b (mult by 512). + * 3) A positive decimal number followed by a k (mult by 1024). + * 4) A positive decimal number followed by a m (mult by 512). + * 5) A positive decimal number followed by a w (mult by sizeof int) + * 6) Two or more positive decimal numbers (with/without k,b or w). + * seperated by x (also * for backwards compatibility), specifying + * the product of the indicated values. + */ +static u_long +get_bsz(val) + char *val; +{ + char *expr; + u_long num, t; + + num = strtoul(val, &expr, 0); + if (num == ULONG_MAX) /* Overflow. */ + err("%s: %s", oper, strerror(errno)); + if (expr == val) /* No digits. */ + err("%s: illegal numeric value", oper); + + switch(*expr) { + case 'b': + t = num; + num *= 512; + if (t > num) + goto erange; + ++expr; + break; + case 'k': + t = num; + num *= 1024; + if (t > num) + goto erange; + ++expr; + break; + case 'm': + t = num; + num *= 1048576; + if (t > num) + goto erange; + ++expr; + break; + case 'w': + t = num; + num *= sizeof(int); + if (t > num) + goto erange; + ++expr; + break; + } + + switch(*expr) { + case '\0': + break; + case '*': /* Backward compatible. */ + case 'x': + t = num; + num *= get_bsz(expr + 1); + if (t > num) +erange: err("%s: %s", oper, strerror(ERANGE)); + break; + default: + err("%s: illegal numeric value", oper); + } + return(num); +} diff --git a/bin/dd/conv.c b/bin/dd/conv.c new file mode 100644 index 0000000000..0c916eee90 --- /dev/null +++ b/bin/dd/conv.c @@ -0,0 +1,260 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)conv.c 5.6 (Berkeley) 4/28/93"; +#endif /* not lint */ + +#include + +#include + +#include "dd.h" +#include "extern.h" + +/* + * def -- + * Copy input to output. Input is buffered until reaches obs, and then + * output until less than obs remains. Only a single buffer is used. + * Worst case buffer calculation is (ibs + obs - 1). + */ +void +def() +{ + register int cnt; + register u_char *inp, *t; + + if (t = ctab) + for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp) + *inp = t[*inp]; + + /* Make the output buffer look right. */ + out.dbp = in.dbp; + out.dbcnt = in.dbcnt; + + if (in.dbcnt >= out.dbsz) { + /* If the output buffer is full, write it. */ + dd_out(0); + + /* + * Ddout copies the leftover output to the beginning of + * the buffer and resets the output buffer. Reset the + * input buffer to match it. + */ + in.dbp = out.dbp; + in.dbcnt = out.dbcnt; + } +} + +void +def_close() +{ + /* Just update the count, everything is already in the buffer. */ + if (in.dbcnt) + out.dbcnt = in.dbcnt; +} + +/* + * Copy variable length newline terminated records with a max size cbsz + * bytes to output. Records less than cbs are padded with spaces. + * + * max in buffer: MAX(ibs, cbsz) + * max out buffer: obs + cbsz + */ +void +block() +{ + static int intrunc; + register int ch, cnt; + register u_char *inp, *outp, *t; + int maxlen; + + /* + * Record truncation can cross block boundaries. If currently in a + * truncation state, keep tossing characters until reach a newline. + * Start at the beginning of the buffer, as the input buffer is always + * left empty. + */ + if (intrunc) { + for (inp = in.db, cnt = in.dbrcnt; + cnt && *inp++ != '\n'; --cnt); + if (!cnt) { + in.dbcnt = 0; + in.dbp = in.db; + return; + } + intrunc = 0; + /* Adjust the input buffer numbers. */ + in.dbcnt = cnt - 1; + in.dbp = inp + cnt - 1; + } + + /* + * Copy records (max cbsz size chunks) into the output buffer. The + * translation is done as we copy into the output buffer. + */ + for (inp = in.dbp - in.dbcnt, outp = out.dbp; in.dbcnt;) { + maxlen = MIN(cbsz, in.dbcnt); + if (t = ctab) + for (cnt = 0; + cnt < maxlen && (ch = *inp++) != '\n'; ++cnt) + *outp++ = t[ch]; + else + for (cnt = 0; + cnt < maxlen && (ch = *inp++) != '\n'; ++cnt) + *outp++ = ch; + /* + * Check for short record without a newline. Reassemble the + * input block. + */ + if (ch != '\n' && in.dbcnt < cbsz) { + memmove(in.db, in.dbp - in.dbcnt, in.dbcnt); + break; + } + + /* Adjust the input buffer numbers. */ + in.dbcnt -= cnt; + if (ch == '\n') + --in.dbcnt; + + /* Pad short records with spaces. */ + if (cnt < cbsz) + (void)memset(outp, ctab ? ctab[' '] : ' ', cbsz - cnt); + else { + /* + * If the next character wouldn't have ended the + * block, it's a truncation. + */ + if (!in.dbcnt || *inp != '\n') + ++st.trunc; + + /* Toss characters to a newline. */ + for (; in.dbcnt && *inp++ != '\n'; --in.dbcnt); + if (!in.dbcnt) + intrunc = 1; + else + --in.dbcnt; + } + + /* Adjust output buffer numbers. */ + out.dbp += cbsz; + if ((out.dbcnt += cbsz) >= out.dbsz) + dd_out(0); + outp = out.dbp; + } + in.dbp = in.db + in.dbcnt; +} + +void +block_close() +{ + /* + * Copy any remaining data into the output buffer and pad to a record. + * Don't worry about truncation or translation, the input buffer is + * always empty when truncating, and no characters have been added for + * translation. The bottom line is that anything left in the input + * buffer is a truncated record. Anything left in the output buffer + * just wasn't big enough. + */ + if (in.dbcnt) { + ++st.trunc; + memmove(out.dbp, in.dbp - in.dbcnt, in.dbcnt); + (void)memset(out.dbp + in.dbcnt, + ctab ? ctab[' '] : ' ', cbsz - in.dbcnt); + out.dbcnt += cbsz; + } +} + +/* + * Convert fixed length (cbsz) records to variable length. Deletes any + * trailing blanks and appends a newline. + * + * max in buffer: MAX(ibs, cbsz) + cbsz + * max out buffer: obs + cbsz + */ +void +unblock() +{ + register int cnt; + register u_char *inp, *t; + + /* Translation and case conversion. */ + if (t = ctab) + for (cnt = in.dbrcnt, inp = in.dbp; cnt--;) + *--inp = t[*inp]; + /* + * Copy records (max cbsz size chunks) into the output buffer. The + * translation has to already be done or we might not recognize the + * spaces. + */ + for (inp = in.db; in.dbcnt >= cbsz; inp += cbsz, in.dbcnt -= cbsz) { + for (t = inp + cbsz - 1; t >= inp && *t == ' '; --t); + if (t >= inp) { + cnt = t - inp + 1; + memmove(out.dbp, inp, cnt); + out.dbp += cnt; + out.dbcnt += cnt; + } + ++out.dbcnt; + *out.dbp++ = '\n'; + if (out.dbcnt >= out.dbsz) + dd_out(0); + } + if (in.dbcnt) + memmove(in.db, in.dbp - in.dbcnt, in.dbcnt); + in.dbp = in.db + in.dbcnt; +} + +void +unblock_close() +{ + register int cnt; + register u_char *t; + + if (in.dbcnt) { + warn("%s: short input record", in.name); + for (t = in.db + in.dbcnt - 1; t >= in.db && *t == ' '; --t); + if (t >= in.db) { + cnt = t - in.db + 1; + memmove(out.dbp, in.db, cnt); + out.dbp += cnt; + out.dbcnt += cnt; + } + ++out.dbcnt; + *out.dbp++ = '\n'; + } +} diff --git a/bin/dd/conv_tab.c b/bin/dd/conv_tab.c new file mode 100644 index 0000000000..5a3069fbd0 --- /dev/null +++ b/bin/dd/conv_tab.c @@ -0,0 +1,357 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)conv_tab.c 5.3 (Berkeley) 7/29/91"; +#endif /* not lint */ + +#include + +/* + * There are currently eight tables: + * + * lower-case -> upper-case conv=upper + * upper-case -> lower-case conv=lower + * + * ebcdic -> ascii 32V conv=oldascii + * ascii -> ebcdic 32V conv=oldebcdic + * ascii -> ibm ebcdic 32V conv=oldibm + * + * ebcdic -> ascii POSIX/S5 conv=ascii + * ascii -> ebcdic POSIX/S5 conv=ebcdic + * ascii -> ibm ebcdic POSIX/S5 conv=ibm + * + * Other tables are built from these if multiple conversions are being + * done. + * + * Tables used for conversions to/from IBM and EBCDIC to support an extension + * to POSIX P1003.2/D11. The tables referencing POSIX contain data extracted + * from tables 4-3 and 4-4 in P1003.2/Draft 11. The historic tables were + * constructed by running against a file with all possible byte values. + * + * More information can be obtained in "Correspondences of 8-Bit and Hollerith + * Codes for Computer Environments-A USASI Tutorial", Communications of the + * ACM, Volume 11, Number 11, November 1968, pp. 783-789. + */ + +/* Lower-case to upper-case */ +u_char l2u[] = { + 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, /* 0000 */ + 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027, /* 0020 */ + 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037, /* 0030 */ + 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, /* 0040 */ + 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, /* 0050 */ + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, /* 0060 */ + 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, /* 0070 */ + 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107, /* 0100 */ + 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, /* 0110 */ + 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0120 */ + 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137, /* 0130 */ + 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107, /* 0140 */ + 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, /* 0150 */ + 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0160 */ + 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177, /* 0170 */ + 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0200 */ + 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, /* 0210 */ + 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, /* 0220 */ + 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, /* 0230 */ + 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, /* 0240 */ + 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, /* 0250 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0260 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0270 */ + 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0300 */ + 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, /* 0310 */ + 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, /* 0320 */ + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, /* 0330 */ + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, /* 0340 */ + 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, /* 0350 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0360 */ + 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* Upper-case to lower-case */ +u_char u2l[] = { + 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, /* 0000 */ + 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027, /* 0020 */ + 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037, /* 0030 */ + 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, /* 0040 */ + 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, /* 0050 */ + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, /* 0060 */ + 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, /* 0070 */ + 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147, /* 0100 */ + 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, /* 0110 */ + 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, /* 0120 */ + 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137, /* 0130 */ + 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147, /* 0140 */ + 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, /* 0150 */ + 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, /* 0160 */ + 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, /* 0170 */ + 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0200 */ + 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, /* 0210 */ + 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, /* 0220 */ + 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, /* 0230 */ + 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, /* 0240 */ + 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, /* 0250 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0260 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0270 */ + 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0300 */ + 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, /* 0310 */ + 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, /* 0320 */ + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, /* 0330 */ + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, /* 0340 */ + 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, /* 0350 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0360 */ + 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* EBCDIC to ASCII -- 32V compatible. */ +u_char e2a_32V[] = { + 0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177, /* 0000 */ + 0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207, /* 0020 */ + 0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037, /* 0030 */ + 0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033, /* 0040 */ + 0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007, /* 0050 */ + 0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004, /* 0060 */ + 0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032, /* 0070 */ + 0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246, /* 0100 */ + 0247, 0250, 0133, 0056, 0074, 0050, 0053, 0041, /* 0110 */ + 0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257, /* 0120 */ + 0260, 0261, 0135, 0044, 0052, 0051, 0073, 0136, /* 0130 */ + 0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267, /* 0140 */ + 0270, 0271, 0174, 0054, 0045, 0137, 0076, 0077, /* 0150 */ + 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, /* 0160 */ + 0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042, /* 0170 */ + 0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147, /* 0200 */ + 0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311, /* 0210 */ + 0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160, /* 0220 */ + 0161, 0162, 0313, 0314, 0315, 0316, 0317, 0320, /* 0230 */ + 0321, 0176, 0163, 0164, 0165, 0166, 0167, 0170, /* 0240 */ + 0171, 0172, 0322, 0323, 0324, 0325, 0326, 0327, /* 0250 */ + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, /* 0260 */ + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, /* 0270 */ + 0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107, /* 0300 */ + 0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355, /* 0310 */ + 0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120, /* 0320 */ + 0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363, /* 0330 */ + 0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130, /* 0340 */ + 0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371, /* 0350 */ + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, /* 0360 */ + 0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to EBCDIC -- 32V compatible. */ +u_char a2e_32V[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0117, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0112, 0340, 0132, 0137, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0152, 0320, 0241, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to IBM EBCDIC -- 32V compatible. */ +u_char a2ibm_32V[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0255, 0340, 0275, 0137, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0117, 0320, 0241, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* EBCDIC to ASCII -- POSIX and System V compatible. */ +u_char e2a_POSIX[] = { + 0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177, /* 0000 */ + 0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207, /* 0020 */ + 0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037, /* 0030 */ + 0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033, /* 0040 */ + 0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007, /* 0050 */ + 0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004, /* 0060 */ + 0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032, /* 0070 */ + 0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246, /* 0100 */ + 0247, 0250, 0325, 0056, 0074, 0050, 0053, 0174, /* 0110 */ + 0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257, /* 0120 */ + 0260, 0261, 0041, 0044, 0052, 0051, 0073, 0176, /* 0130 */ + 0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267, /* 0140 */ + 0270, 0271, 0313, 0054, 0045, 0137, 0076, 0077, /* 0150 */ + 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, /* 0160 */ + 0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042, /* 0170 */ + 0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147, /* 0200 */ + 0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311, /* 0210 */ + 0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160, /* 0220 */ + 0161, 0162, 0136, 0314, 0315, 0316, 0317, 0320, /* 0230 */ + 0321, 0345, 0163, 0164, 0165, 0166, 0167, 0170, /* 0240 */ + 0171, 0172, 0322, 0323, 0324, 0133, 0326, 0327, /* 0250 */ + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, /* 0260 */ + 0340, 0341, 0342, 0343, 0344, 0135, 0346, 0347, /* 0270 */ + 0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107, /* 0300 */ + 0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355, /* 0310 */ + 0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120, /* 0320 */ + 0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363, /* 0330 */ + 0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130, /* 0340 */ + 0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371, /* 0350 */ + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, /* 0360 */ + 0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to EBCDIC -- POSIX and System V compatible. */ +u_char a2e_POSIX[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0255, 0340, 0275, 0232, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0117, 0320, 0137, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0152, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0112, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0241, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to IBM EBCDIC -- POSIX and System V compatible. */ +u_char a2ibm_POSIX[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0255, 0340, 0275, 0137, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0117, 0320, 0241, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; diff --git a/bin/dd/dd.1 b/bin/dd/dd.1 new file mode 100644 index 0000000000..b8bf1528b5 --- /dev/null +++ b/bin/dd/dd.1 @@ -0,0 +1,343 @@ +.\" Copyright (c) 1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Keith Muller of the University of California, San Diego. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)dd.1 6.7 (Berkeley) 8/7/91 +.\" +.Dd August 7, 1991 +.Dt DD 1 +.Os +.Sh NAME +.Nm dd +.Nd Convert and copy a file. +.Sh SYNOPSIS +.Nm dd +.Op operands ... +.Sh DESCRIPTION +The +.Nm +utility copies the standard input to the standard output. +Input data is read and written in 512-byte blocks. +If input reads are short, input from multiple reads are aggregated +to form the output block. +When finished, +.Nm dd +displays the number of complete and partial input and output blocks +and truncated input records to the standard error output. +.Pp +The following operands are available: +.Bl -tag -width of=file +.It Cm bs= Ns Ar n +Set both input and output block size, superseding the +.Cm ibs +and +.Cm obs +operands. +If no conversion values other than +.Cm noerror , +.Cm notrunc +or +.Cm sync +are specified, then each input block is copied to the output as a +single block without any aggregation of short blocks. +.It Cm cbs= Ns Ar n +Set the conversion record size to +.Va n +bytes. +The conversion record size is required by the record oriented conversion +values. +.It Cm count= Ns Ar n +Copy only +.Va n +input blocks. +.It Cm files= Ns Ar n +Copy +.Va n +input files before terminating. +This operand is only applicable when the input device is a tape. +.It Cm ibs= Ns Ar n +Set the input block size to +.Va n +bytes instead of the default 512. +.It Cm if= Ns Ar file +Read input from +.Ar file +instead of the standard input. +.It Cm obs= Ns Ar n +Set the output block size to +.Va n +bytes instead of the default 512. +.It Cm of= Ns Ar file +Write output to +.Ar file +instead of the standard output. +Any regular output file is truncated unless the +.Cm notrunc +conversion value is specified. +If an initial portion of the output file is skipped (see the +.Cm seek +operand) +the output file is truncated at that point. +.It Cm seek= Ns Ar n +Seek +.Va n +blocks from the beginning of the output before copying. +On non-tape devices, a +.Xr lseek 2 +operation is used. +Otherwise, existing blocks are read and the data discarded. +If the user does not have read permission for the tape, it is positioned +using the tape +.Xr ioctl 2 +function calls. +If the seek operation is past the end of file, space from the current +end of file to the specified offset is filled with blocks of +.Tn NUL +bytes. +.It Cm skip= Ns Ar n +Skip +.Va n +blocks from the beginning of the input before copying. +On input which supports seeks, a +.Xr lseek 2 +operation is used. +Otherwise, input data is read and discarded. +For pipes, the correct number of bytes is read. +For all other devices, the correct number of blocks is read without +distinguishing between a partial or complete block being read. +.It Xo +.Cm conv= +.Ns Cm value Ns Op \&, Cm value \&... +.Xc +Where +.Cm value +is one of the symbols from the following list. +.Bl -tag -width unblock +.It Cm ascii , oldascii +The same as the +.Cm unblock +value except that characters are translated from +.Tn ECBDIC +to +.Tn ASCII +before the +records are converted. +(These values imply +.Cm unblock +if the operand +.Cm cbs +is also specified.) +There are two conversion maps for +.Tn ASCII . +The value +.Cm ascii +specifies the recommended one which is compatible with System V. +The value +.Cm oldascii +specifies the one used in historic +.Tn AT&T +and +.Pf pre- Bx 4.3 reno +systems. +.It Cm block +Treats the input as a sequence of newline or end-of-file terminated variable +length records independent of input and output block boundaries. +Any trailing newline character is discarded. +Each input record is converted to a fixed length output record where the +length is specified by the +.Cm cbs +operand. +Input records shorter than the conversion record size are padded with spaces. +Input records longer than the conversion record size are truncated. +The number of truncated input records, if any, are reported to the standard +error at the completion of the copy. +.It Cm ebcdic , ibm , oldebcdic , oldibm +The same as the +.Cm block +value except that characters are translated from +.Tn ASCII +to +.Tn EBCDIC +after the +records are converted. +(These values imply +.Cm block +if the operand +.Cm cbs +is also specified.) +There are four conversion maps for +.Tn EBCDIC . +The value +.Cm ebcdic +specifies the recommended one which is compatible with +.At V . +The value +.Cm ibm +is a slightly different mapping, which is compatible with the +.At V +.Cm ibm +value. +The values +.Cm oldebcdic +and +.Cm oldibm +are maps used in historic +.Tn AT&T +and +.Pf pre- Bx 4.3 reno +systems. +.It Cm lcase +Transform uppercase characters into lowercase characters. +.It Cm noerror +Do not stop processing on an input error. +When an input error occurs, a diagnostic message followed by the current +input and output block counts will be written to standard error in the +same format as the standard completion message. +If the +.Cm sync +conversion is also specified, any missing input data will be replaced +with +.Tn NUL +bytes (or with spaces if a block oriented conversion value was +specified) and processed as a normal input buffer. +If the +.Cm sync +conversion is not specified, the input block is omitted from the output. +On input files which are not tapes or pipes, the file offset +will be positioned past the block in which the error occurred using +.Xr lseek 2 . +.It Cm notrunc +Do not truncate the output file. +This will preserve any blocks in the output file not explicitly written +by +.Nm dd . +The +.Cm notrunc +value is not supported for tapes. +.It Cm swab +Swap every pair of input bytes. +If an input buffer has an odd number of bytes, the last byte will be +ignored during swapping. +.It Cm sync +Pad every input block to the input buffer size. +Spaces are used for pad bytes if a block oriented conversion value is +specified, otherwise +.Tn NUL +bytes are used. +.It Cm ucase +Transform lowercase characters into uppercase characters. +.It Cm unblock +Treats the input as a sequence of fixed length records independent of input +and output block boundaries. +The length of the input records is specified by the +.Cm cbs +operand. +Any trailing space characters are discarded and a newline character is +appended. +.El +.El +.Pp +Where sizes are specified, a decimal number of bytes is expected. +If the number ends with a ``b'', ``k'', ``m'' or ``w'', the number +is multiplied by 512, 1024 (1K), 1048576 (1M) or the number of bytes +in an integer, respectively. +Two or more numbers may be separated by an ``x'' to indicate a product. +.Pp +When finished, +.Nm dd +displays the number of complete and partial input and output blocks, +truncated input records and odd-length byte-swapping blocks to the +standard error output. +A partial input block is one where less than the input block size +was read. +A partial output block is one where less than the output block size +was written. +Partial output blocks to tape devices are considered fatal errors. +Otherwise, the rest of the block will be written. +Partial output blocks to character devices will produce a warning message. +A truncated input block is one where a variable length record oriented +conversion value was specified and the input line was too long to +fit in the conversion record or was not newline terminated. +.Pp +Normally, data resulting from input or conversion or both are aggregated +into output blocks of the specified size. +After the end of input is reached, any remaining output is written as +a block. +This means that the final output block may be shorter than the output +block size. +.Pp +If +.Nm dd +receives a +.Dv SIGINFO +(see the ``status'' argument for +.Xr stty 1 ) +signal, the current input and output block counts will +be written to standard error in the same format as the standard completion +message. +If +.Nm dd +receives a +.Dv SIGINT +signal, the current input and output block counts will +be written to standard error in the same format as the standard completion +message and +.Nm dd +will exit. +.Pp +The +.Nm dd +utility exits 0 on success and >0 if an error occurred. +.Sh SEE ALSO +.Xr cp 1 , +.Xr mt 1 , +.Xr tr 1 +.Sh STANDARDS +The +.Nm dd +utility is expected to be a superset of the +.St -p1003.2 +standard. +The +.Cm files +operand and the +.Cm ascii , +.Cm ebcdic , +.Cm ibm , +.Cm oldascii , +.Cm oldebcdic +and +.Cm oldibm +values are extensions to the +.Tn POSIX +standard. diff --git a/bin/dd/dd.c b/bin/dd/dd.c index 6995226dd6..dada2320d6 100644 --- a/bin/dd/dd.c +++ b/bin/dd/dd.c @@ -1,1037 +1,381 @@ -/* dd -- convert a file while copying it. - Copyright (C) 1985, 1990, 1991 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00140 - * -------------------- ----- ---------------------- + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. * - * 20 Apr 93 Dave Burgess silence compile warnings... + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ -/* Written by Paul Rubin, David MacKenzie, and Stuart Kemp. */ - -/* Options: +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1991 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ - Numbers can be followed by a multiplier: - b=512, k=1024, w=2, xm=number m +#ifndef lint +static char sccsid[] = "@(#)dd.c 5.16 (Berkeley) 4/28/93"; +#endif /* not lint */ - if=FILE Read from FILE instead of stdin. - of=FILE Write to FILE instead of stdout; don't - truncate FILE. - ibs=BYTES Read BYTES bytes at a time. - obs=BYTES Write BYTES bytes at a time. - bs=BYTES Override ibs and obs. - cbs=BYTES Convert BYTES bytes at a time. - skip=BLOCKS Skip BLOCKS ibs-sized blocks at - start of input. - seek=BLOCKS Skip BLOCKS obs-sized blocks at - start of output. - count=BLOCKS Copy only BLOCKS input blocks. - conv=CONVERSION[,CONVERSION...] - - Conversions: - ascii Convert EBCDIC to ASCII. - ebcdic Convert ASCII to EBCDIC. - ibm Convert ASCII to alternate EBCDIC. - block Pad newline-terminated records to size of - cbs, replacing newline with trailing spaces. - unblock Replace trailing spaces in cbs-sized block - with newline. - lcase Change uppercase characters to lowercase. - ucase Change lowercase characters to uppercase. - swab Swap every pair of input bytes. - Unlike the Unix dd, this works when an odd - number of bytes are read. - noerror Continue after read errors. - sync Pad every input block to size of ibs with - trailing NULs. */ +#include +#include +#include +#include -#include #include -#define ISLOWER islower -#define ISUPPER isupper -#include -#include -#include -#include -#include #include -#include #include -#define SIGTYPE void - -#define equal(p, q) (strcmp ((p),(q)) == 0) -#define max(a, b) ((a) > (b) ? (a) : (b)) -#define output_char(c) \ - do { \ - obuf[oc++] = (c); if (oc >= output_blocksize) write_output (); \ - } while (0) - -/* Default input and output blocksize. */ -#define DEFAULT_BLOCKSIZE 512 - -/* Conversions bit masks. */ -#define C_ASCII 01 -#define C_EBCDIC 02 -#define C_IBM 04 -#define C_BLOCK 010 -#define C_UNBLOCK 020 -#define C_LCASE 040 -#define C_UCASE 0100 -#define C_SWAB 0200 -#define C_NOERROR 0400 -#define C_NOTRUNC 01000 -#define C_SYNC 02000 -/* Use separate input and output buffers, and combine partial input blocks. */ -#define C_TWOBUFS 04000 - -/*char *malloc ();*/ -SIGTYPE interrupt_handler (); -int bit_count (); -int parse_integer (); -void apply_translations (); -void copy (); -void copy_simple (); -void copy_with_block (); -void copy_with_unblock (); -void error (); -void parse_conversion (); -void print_stats (); -void translate_charset (); -void quit (); -void scanargs (); -void skip (); -void usage (); -void write_output (); - -/* The name this program was run with. */ -char *program_name; - -/* The name of the input file, or NULL for the standard input. */ -char *input_file = NULL; - -/* The input file descriptor. */ -int input_fd = 0; - -/* The name of the output file, or NULL for the standard output. */ -char *output_file = NULL; - -/* The output file descriptor. */ -int output_fd = 1; - -/* The number of bytes in which atomic reads are done. */ -long input_blocksize = -1; - -/* The number of bytes in which atomic writes are done. */ -long output_blocksize = -1; - -/* Conversion buffer size, in bytes. 0 prevents conversions. */ -long conversion_blocksize = 0; - -/* Skip this many records of `input_blocksize' bytes before input. */ -long skip_records = 0; - -/* Skip this many records of `output_blocksize' bytes before output. */ -long seek_record = 0; - -/* Copy only this many records. <0 means no limit. */ -int max_records = -1; - -/* Bit vector of conversions to apply. */ -int conversions_mask = 0; - -/* If nonzero, filter characters through the translation table. */ -int translation_needed = 0; - -/* Number of partial blocks written. */ -unsigned w_partial = 0; - -/* Number of full blocks written. */ -unsigned w_full = 0; - -/* Number of partial blocks read. */ -unsigned r_partial = 0; - -/* Number of full blocks read. */ -unsigned r_full = 0; - -/* Records truncated by conv=block. */ -unsigned r_truncate = 0; - -/* Output representation of newline and space characters. - They change if we're converting to EBCDIC. */ -unsigned char newline_character = '\n'; -unsigned char space_character = ' '; - -struct conversion -{ - char *convname; - int conversion; -}; - -struct conversion conversions[] = -{ - "ascii", C_ASCII | C_TWOBUFS, /* EBCDIC to ASCII. */ - "ebcdic", C_EBCDIC | C_TWOBUFS, /* ASCII to EBCDIC. */ - "ibm", C_IBM | C_TWOBUFS, /* Slightly different ASCII to EBCDIC. */ - "block", C_BLOCK | C_TWOBUFS, /* Variable to fixed length records. */ - "unblock", C_UNBLOCK | C_TWOBUFS, /* Fixed to variable length records. */ - "lcase", C_LCASE | C_TWOBUFS, /* Translate upper to lower case. */ - "ucase", C_UCASE | C_TWOBUFS, /* Translate lower to upper case. */ - "swab", C_SWAB | C_TWOBUFS, /* Swap bytes of input. */ - "noerror", C_NOERROR, /* Ignore i/o errors. */ - "notrunc", C_NOTRUNC, /* Do not truncate output file. */ - "sync", C_SYNC, /* Pad input records to ibs with NULs. */ - NULL, 0 -}; +#include +#include +#include +#include +#include -/* Translation table formed by applying successive transformations. */ -unsigned char trans_table[256]; +#include "dd.h" +#include "extern.h" -unsigned char ascii_to_ebcdic[] = -{ - 0, 01, 02, 03, 067, 055, 056, 057, - 026, 05, 045, 013, 014, 015, 016, 017, - 020, 021, 022, 023, 074, 075, 062, 046, - 030, 031, 077, 047, 034, 035, 036, 037, - 0100, 0117, 0177, 0173, 0133, 0154, 0120, 0175, - 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, - 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, - 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, - 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, - 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, - 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, - 0347, 0350, 0351, 0112, 0340, 0132, 0137, 0155, - 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, - 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, - 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, - 0247, 0250, 0251, 0300, 0152, 0320, 0241, 07, - 040, 041, 042, 043, 044, 025, 06, 027, - 050, 051, 052, 053, 054, 011, 012, 033, - 060, 061, 032, 063, 064, 065, 066, 010, - 070, 071, 072, 073, 04, 024, 076, 0341, - 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, - 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, - 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, - 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, - 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, - 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, - 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, - 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, - 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, - 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, - 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, - 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377 -}; +static void dd_close __P((void)); +static void dd_in __P((void)); +static void setup __P((void)); -unsigned char ascii_to_ibm[] = -{ - 0, 01, 02, 03, 067, 055, 056, 057, - 026, 05, 045, 013, 014, 015, 016, 017, - 020, 021, 022, 023, 074, 075, 062, 046, - 030, 031, 077, 047, 034, 035, 036, 037, - 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, - 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, - 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, - 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, - 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, - 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, - 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, - 0347, 0350, 0351, 0255, 0340, 0275, 0137, 0155, - 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, - 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, - 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, - 0247, 0250, 0251, 0300, 0117, 0320, 0241, 07, - 040, 041, 042, 043, 044, 025, 06, 027, - 050, 051, 052, 053, 054, 011, 012, 033, - 060, 061, 032, 063, 064, 065, 066, 010, - 070, 071, 072, 073, 04, 024, 076, 0341, - 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, - 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, - 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, - 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, - 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, - 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, - 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, - 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, - 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, - 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, - 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, - 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377 -}; +IO in, out; /* input/output state */ +STAT st; /* statistics */ +void (*cfunc) __P((void)); /* conversion function */ +u_long cpy_cnt; /* # of blocks to copy */ +u_int ddflags; /* conversion options */ +u_int cbsz; /* conversion block size */ +u_int files_cnt = 1; /* # of files to copy */ +int errstats; /* show statistics on error */ +u_char *ctab; /* conversion table */ -unsigned char ebcdic_to_ascii[] = -{ - 0, 01, 02, 03, 0234, 011, 0206, 0177, - 0227, 0215, 0216, 013, 014, 015, 016, 017, - 020, 021, 022, 023, 0235, 0205, 010, 0207, - 030, 031, 0222, 0217, 034, 035, 036, 037, - 0200, 0201, 0202, 0203, 0204, 012, 027, 033, - 0210, 0211, 0212, 0213, 0214, 05, 06, 07, - 0220, 0221, 026, 0223, 0224, 0225, 0226, 04, - 0230, 0231, 0232, 0233, 024, 025, 0236, 032, - 040, 0240, 0241, 0242, 0243, 0244, 0245, 0246, - 0247, 0250, 0133, 056, 074, 050, 053, 041, - 046, 0251, 0252, 0253, 0254, 0255, 0256, 0257, - 0260, 0261, 0135, 044, 052, 051, 073, 0136, - 055, 057, 0262, 0263, 0264, 0265, 0266, 0267, - 0270, 0271, 0174, 054, 045, 0137, 076, 077, - 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, - 0302, 0140, 072, 043, 0100, 047, 075, 042, - 0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147, - 0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311, - 0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160, - 0161, 0162, 0313, 0314, 0315, 0316, 0317, 0320, - 0321, 0176, 0163, 0164, 0165, 0166, 0167, 0170, - 0171, 0172, 0322, 0323, 0324, 0325, 0326, 0327, - 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, - 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, - 0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107, - 0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355, - 0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120, - 0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363, - 0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130, - 0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371, - 060, 061, 062, 063, 064, 065, 066, 067, - 070, 071, 0372, 0373, 0374, 0375, 0376, 0377 -}; - -void -main (argc, argv) - int argc; - char **argv; +int +main(argc, argv) + int argc; + char *argv[]; { - struct sigaction sigact; - int i; - - program_name = argv[0]; - - /* Initialize translation table to identity translation. */ - for (i = 0; i < 256; i++) - trans_table[i] = i; + jcl(argv); + setup(); - /* Decode arguments. */ - scanargs (argc, argv); - apply_translations (); + (void)signal(SIGINFO, summary); + (void)signal(SIGINT, terminate); - if (input_file != NULL) - { - input_fd = open (input_file, O_RDONLY); - if (input_fd < 0) - error (1, errno, "%s", input_file); - } - else - input_file = "standard input"; + for (errstats = 1; files_cnt--;) + dd_in(); - if (input_fd == output_fd) - error (1, 0, "standard %s is closed", input_fd == 0 ? "input" : "output"); - - if (output_file != NULL) - { - int omode = O_RDWR | O_CREAT; - - if (seek_record == 0 && !(conversions_mask & C_NOTRUNC)) - omode |= O_TRUNC; - output_fd = open (output_file, omode, 0666); - if (output_fd < 0) - error (1, errno, "%s", output_file); -#ifndef FTRUNCATE_MISSING - if (seek_record > 0 && !(conversions_mask & C_NOTRUNC)) - { - if (ftruncate (output_fd, seek_record * output_blocksize) < 0) - error (0, errno, "%s", output_file); - } -#endif - } - else - output_file = "standard output"; - - sigaction (SIGINT, NULL, &sigact); - if (sigact.sa_handler != SIG_IGN) - { - sigact.sa_handler = interrupt_handler; - sigemptyset (&sigact.sa_mask); - sigact.sa_flags = 0; - sigaction (SIGINT, &sigact, NULL); - } - copy (); + dd_close(); + summary(0); + exit(0); } -/* Throw away RECORDS blocks of BLOCKSIZE bytes on file descriptor FDESC, - which is open with read permission for FILE. Store up to BLOCKSIZE - bytes of the data at a time in BUF, if necessary. */ - -void -skip (fdesc, file, records, blocksize, buf) - int fdesc; - char *file; - long records; - long blocksize; - char *buf; +static void +setup() { - struct stat stats; - - /* Use fstat instead of checking for errno == ESPIPE because - lseek doesn't work on some special files but doesn't return an - error, either. */ - if (fstat (fdesc, &stats)) - { - error (0, errno, "%s", file); - quit (1); - } - - if (S_ISREG (stats.st_mode)) - { - if (lseek (fdesc, records * blocksize, SEEK_SET) < 0) - { - error (0, errno, "%s", file); - quit (1); - } - } - else - { - while (records-- > 0) - { - if (read (fdesc, buf, blocksize) < 0) - { - error (0, errno, "%s", file); - quit (1); - } - /* FIXME If fewer bytes were read than requested, meaning that - EOF was reached, POSIX wants the output file padded with NULs. */ + register u_int cnt; + struct stat sb; + struct mtget mt; + + if (in.name == NULL) { + in.name = "stdin"; + in.fd = STDIN_FILENO; + } else { + in.fd = open(in.name, O_RDONLY, 0); + if (in.fd < 0) + err("%s: %s", in.name, strerror(errno)); } - } -} - -/* Apply the character-set translations specified by the user - to the NREAD bytes in BUF. */ - -void -translate_buffer (buf, nread) - unsigned char *buf; - int nread; -{ - register unsigned char *cp; - register int i; - - for (i = nread, cp = buf; i; i--, cp++) - *cp = trans_table[*cp]; -} - -/* If nonnzero, the last char from the previous call to `swab_buffer' - is saved in `saved_char'. */ -int char_is_saved = 0; - -/* Odd char from previous call. */ -unsigned char saved_char; - -/* Swap NREAD bytes in BUF, plus possibly an initial char from the - previous call. If NREAD is odd, save the last char for the - next call. Return the new start of the BUF buffer. */ - -unsigned char * -swab_buffer (buf, nread) - unsigned char *buf; - int *nread; -{ - unsigned char *bufstart = buf; - register unsigned char *cp; - register int i; - - /* Is a char left from last time? */ - if (char_is_saved) - { - *--bufstart = saved_char; - *nread++; - char_is_saved = 0; - } - - if (*nread & 1) - { - /* An odd number of chars are in the buffer. */ - saved_char = bufstart[--*nread]; - char_is_saved = 1; - } - - /* Do the byte-swapping by moving every second character two - positions toward the end, working from the end of the buffer - toward the beginning. This way we only move half of the data. */ - - cp = bufstart + *nread; /* Start one char past the last. */ - for (i = *nread / 2; i; i--, cp -= 2) - *cp = *(cp - 2); - - return ++bufstart; -} - -/* Output buffer. */ -unsigned char *obuf; - -/* Current index into `obuf'. */ -int oc = 0; - -/* Index into current line, for `conv=block' and `conv=unblock'. */ -int col = 0; - -/* The main loop. */ - -void -copy () -{ - unsigned char *ibuf, *bufstart; /* Input buffer. */ - int nread; /* Bytes read in the current block. */ - int exit_status = 0; - - /* Leave an extra byte at the beginning and end of `ibuf' for conv=swab. */ - ibuf = (unsigned char *) malloc (input_blocksize + 2) + 1; - if (conversions_mask & C_TWOBUFS) - obuf = (unsigned char *) malloc (output_blocksize); - else - obuf = ibuf; - - if (skip_records > 0) - skip (input_fd, input_file, skip_records, input_blocksize, ibuf); - - if (seek_record > 0) - skip (output_fd, output_file, seek_record, output_blocksize, obuf); - - if (max_records == 0) - quit (exit_status); - - while (1) - { - if (max_records >= 0 && r_partial + r_full >= max_records) - break; - - /* Zero the buffer before reading, so that if we get a read error, - whatever data we are able to read is followed by zeros. - This minimizes data loss. */ - if ((conversions_mask & C_SYNC) && (conversions_mask & C_NOERROR)) - bzero (ibuf, input_blocksize); - - nread = read (input_fd, ibuf, input_blocksize); - - if (nread == 0) - break; /* EOF. */ - if (nread < 0) - { - error (0, errno, "%s", input_file); - if (conversions_mask & C_NOERROR) - { - print_stats (); - /* Seek past the bad block if possible. */ - lseek (input_fd, input_blocksize, SEEK_CUR); - if (conversions_mask & C_SYNC) - /* Replace the missing input with null bytes and - proceed normally. */ - nread = 0; - else - continue; - } - else - { - /* Write any partial block. */ - exit_status = 2; - break; - } + if (fstat(in.fd, &sb)) + err("%s: %s", in.name, strerror(errno)); + if (S_ISCHR(sb.st_mode)) + in.flags |= ioctl(in.fd, MTIOCGET, &mt) ? ISCHR : ISTAPE; + else if (lseek(in.fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) + in.flags |= ISPIPE; /* XXX fixed in 4.4BSD */ + + if (files_cnt > 1 && !(in.flags & ISTAPE)) + err("files is not supported for non-tape devices"); + + if (out.name == NULL) { + /* No way to check for read access here. */ + out.fd = STDOUT_FILENO; + out.name = "stdout"; + } else { +#define OFLAGS \ + (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC)) + out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE); + /* + * May not have read access, so try again with write only. + * Without read we may have a problem if output also does + * not support seeks. + */ + if (out.fd < 0) { + out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE); + out.flags |= NOREAD; + } + if (out.fd < 0) + err("%s: %s", out.name, strerror(errno)); } - if (nread < input_blocksize) - { - r_partial++; - if (conversions_mask & C_SYNC) - { - if (!(conversions_mask & C_NOERROR)) - /* If C_NOERROR, we zeroed the block before reading. */ - bzero (ibuf + nread, input_blocksize - nread); - nread = input_blocksize; - } - } - else - r_full++; - - if (ibuf == obuf) /* If not C_TWOBUFS. */ - { - int nwritten = write (output_fd, obuf, nread); - if (nwritten != nread) - { - error (0, errno, "%s", output_file); - if (nwritten > 0) - w_partial++; - quit (1); - } - else if (nread == input_blocksize) - w_full++; - else - w_partial++; - continue; - } - - /* Do any translations on the whole buffer at once. */ - - if (translation_needed) - translate_buffer (ibuf, nread); - - if (conversions_mask & C_SWAB) - bufstart = swab_buffer (ibuf, &nread); - else - bufstart = ibuf; - - if (conversions_mask & C_BLOCK) - copy_with_block (bufstart, nread); - else if (conversions_mask & C_UNBLOCK) - copy_with_unblock (bufstart, nread); - else - copy_simple (bufstart, nread); - } - - /* If we have a char left as a result of conv=swab, output it. */ - if (char_is_saved) - { - if (conversions_mask & C_BLOCK) - copy_with_block (&saved_char, 1); - else if (conversions_mask & C_UNBLOCK) - copy_with_unblock (&saved_char, 1); - else - output_char (saved_char); - } - - if ((conversions_mask & C_BLOCK) && col > 0) - { - /* If the final input line didn't end with a '\n', pad - the output block to `conversion_blocksize' chars. */ - int pending_spaces = max (0, conversion_blocksize - col); - while (pending_spaces--) - output_char (space_character); - } - - if ((conversions_mask & C_UNBLOCK) && col == conversion_blocksize) - /* Add a final '\n' if there are exactly `conversion_blocksize' - characters in the final record. */ - output_char (newline_character); - - /* Write out the last block. */ - if (oc > 0) - { - int nwritten = write (output_fd, obuf, oc); - if (nwritten > 0) - w_partial++; - if (nwritten != oc) - { - error (0, errno, "%s", output_file); - quit (1); - } - } - - free (ibuf - 1); - if (obuf != ibuf) - free (obuf); - - quit (exit_status); -} - -/* Copy NREAD bytes of BUF, with no conversions. */ - -void -copy_simple (buf, nread) - unsigned char *buf; - int nread; -{ - int nfree; /* Number of unused bytes in `obuf'. */ - unsigned char *start = buf; /* First uncopied char in BUF. */ - - do - { - nfree = output_blocksize - oc; - if (nfree > nread) - nfree = nread; - - bcopy (start, obuf + oc, nfree); - - nread -= nfree; /* Update the number of bytes left to copy. */ - start += nfree; - oc += nfree; - if (oc >= output_blocksize) - write_output (); - } - while (nread > 0); + if (fstat(out.fd, &sb)) + err("%s: %s", out.name, strerror(errno)); + if (S_ISCHR(sb.st_mode)) + out.flags |= ioctl(out.fd, MTIOCGET, &mt) ? ISCHR : ISTAPE; + else if (lseek(out.fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) + out.flags |= ISPIPE; /* XXX fixed in 4.4BSD */ + + /* + * Allocate space for the input and output buffers. If not doing + * record oriented I/O, only need a single buffer. + */ + if (!(ddflags & (C_BLOCK|C_UNBLOCK))) { + if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL) + err("%s", strerror(errno)); + out.db = in.db; + } else if ((in.db = + malloc((u_int)(MAX(in.dbsz, cbsz) + cbsz))) == NULL || + (out.db = malloc((u_int)(out.dbsz + cbsz))) == NULL) + err("%s", strerror(errno)); + in.dbp = in.db; + out.dbp = out.db; + + /* Position the input/output streams. */ + if (in.offset) + pos_in(); + if (out.offset) + pos_out(); + + /* + * Truncate the output file; ignore errors because it fails on some + * kinds of output files, tapes, for example. + */ + if (ddflags & (C_OF | C_SEEK | C_NOTRUNC) == (C_OF | C_SEEK)) + (void)ftruncate(out.fd, (off_t)out.offset * out.dbsz); + + /* + * If converting case at the same time as another conversion, build a + * table that does both at once. If just converting case, use the + * built-in tables. + */ + if (ddflags & (C_LCASE|C_UCASE)) + if (ddflags & C_ASCII) + if (ddflags & C_LCASE) { + for (cnt = 0; cnt < 0377; ++cnt) + if (isupper(ctab[cnt])) + ctab[cnt] = tolower(ctab[cnt]); + } else { + for (cnt = 0; cnt < 0377; ++cnt) + if (islower(ctab[cnt])) + ctab[cnt] = toupper(ctab[cnt]); + } + else if (ddflags & C_EBCDIC) + if (ddflags & C_LCASE) { + for (cnt = 0; cnt < 0377; ++cnt) + if (isupper(cnt)) + ctab[cnt] = ctab[tolower(cnt)]; + } else { + for (cnt = 0; cnt < 0377; ++cnt) + if (islower(cnt)) + ctab[cnt] = ctab[toupper(cnt)]; + } + else + ctab = ddflags & C_LCASE ? u2l : l2u; + (void)time(&st.start); /* Statistics timestamp. */ } -/* Copy NREAD bytes of BUF, doing conv=block - (pad newline-terminated records to `conversion_blocksize', - replacing the newline with trailing spaces). */ - -void -copy_with_block (buf, nread) - unsigned char *buf; - int nread; +static void +dd_in() { - register int i; - - for (i = nread; i; i--, buf++) - { - if (*buf == newline_character) - { - int pending_spaces = max (0, conversion_blocksize - col); - while (pending_spaces--) - output_char (space_character); - col = 0; - } - else - { - if (col == conversion_blocksize) - r_truncate++; - else if (col < conversion_blocksize) - output_char (*buf); - col++; - } - } -} - -/* Copy NREAD bytes of BUF, doing conv=unblock - (replace trailing spaces in `conversion_blocksize'-sized records - with a newline). */ - -void -copy_with_unblock (buf, nread) - unsigned char *buf; - int nread; -{ - register int i; - register unsigned char c; - static int pending_spaces = 0; - - for (i = 0; i < nread; i++) - { - c = buf[i]; - - if (col++ >= conversion_blocksize) - { - col = pending_spaces = 0; /* Wipe out any pending spaces. */ - i--; /* Push the char back; get it later. */ - output_char (newline_character); - } - else if (c == space_character) - pending_spaces++; - else - { - if (pending_spaces) - { - /* `c' is the character after a run of spaces that were not - at the end of the conversion buffer. Output them. */ - while (pending_spaces--) - output_char (space_character); - } - output_char (c); - } - } -} - -/* Write, then empty, the output buffer `obuf'. */ - -void -write_output () -{ - int nwritten = write (output_fd, obuf, output_blocksize); - if (nwritten != output_blocksize) - { - error (0, errno, "%s", output_file); - if (nwritten > 0) - w_partial++; - quit (1); - } - else - w_full++; - oc = 0; -} - -void -scanargs (argc, argv) - int argc; - char **argv; -{ - int i, n; - - for (i = 1; i < argc; i++) - { - char *name, *val; - - name = argv[i]; - val = index (name, '='); - if (val == NULL) - usage ("unrecognized option `%s'", name); - *val++ = '\0'; - - if (equal (name, "if")) - input_file = val; - else if (equal (name, "of")) - output_file = val; - else if (equal (name, "conv")) - parse_conversion (val); - else - { - n = parse_integer (val); - if (n < 0) - error (1, 0, "invalid number `%s'", val); - - if (equal (name, "ibs")) - { - input_blocksize = n; - conversions_mask |= C_TWOBUFS; - } - else if (equal (name, "obs")) - { - output_blocksize = n; - conversions_mask |= C_TWOBUFS; - } - else if (equal (name, "bs")) - output_blocksize = input_blocksize = n; - else if (equal (name, "cbs")) - conversion_blocksize = n; - else if (equal (name, "skip")) - skip_records = n; - else if (equal (name, "seek")) - seek_record = n; - else if (equal (name, "count")) - max_records = n; - else - usage ("unrecognized option `%s=%s'", name, val); + register int flags, n; + + for (flags = ddflags;;) { + if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt) + return; + + /* + * Zero the buffer first if trying to recover from errors so + * lose the minimum amount of data. If doing block operations + * use spaces. + */ + if (flags & (C_NOERROR|C_SYNC)) + if (flags & (C_BLOCK|C_UNBLOCK)) + memset(in.dbp, ' ', in.dbsz); + else + memset(in.dbp, 0, in.dbsz); + + n = read(in.fd, in.dbp, in.dbsz); + if (n == 0) { + in.dbrcnt = 0; + return; + } + + /* Read error. */ + if (n < 0) { + /* + * If noerror not specified, die. POSIX requires that + * the warning message be followed by an I/O display. + */ + if (!(flags & C_NOERROR)) + err("%s: %s", in.name, strerror(errno)); + warn("%s: %s", in.name, strerror(errno)); + summary(0); + + /* + * If it's not a tape drive or a pipe, seek past the + * error. If your OS doesn't do the right thing for + * raw disks this section should be modified to re-read + * in sector size chunks. + */ + if (!(in.flags & (ISPIPE|ISTAPE)) && + lseek(in.fd, (off_t)in.dbsz, SEEK_CUR)) + warn("%s: %s", in.name, strerror(errno)); + + /* If sync not specified, omit block and continue. */ + if (!(ddflags & C_SYNC)) + continue; + + /* Read errors count as full blocks. */ + in.dbcnt += in.dbrcnt = in.dbsz; + ++st.in_full; + + /* Handle full input blocks. */ + } else if (n == in.dbsz) { + in.dbcnt += in.dbrcnt = n; + ++st.in_full; + + /* Handle partial input blocks. */ + } else { + /* If sync, use the entire block. */ + if (ddflags & C_SYNC) + in.dbcnt += in.dbrcnt = in.dbsz; + else + in.dbcnt += in.dbrcnt = n; + ++st.in_part; + } + + /* + * POSIX states that if bs is set and no other conversions + * than noerror, notrunc or sync are specified, the block + * is output without buffering as it is read. + */ + if (ddflags & C_BS) { + out.dbcnt = in.dbcnt; + dd_out(1); + in.dbcnt = 0; + continue; + } + + if (ddflags & C_SWAB) { + if ((n = in.dbcnt) & 1) { + ++st.swab; + --n; + } + swab(in.dbp, in.dbp, n); + } + + in.dbp += in.dbrcnt; + (*cfunc)(); } - } - - /* If bs= was given, both `input_blocksize' and `output_blocksize' will - have been set to non-negative values. If either has not been set, - bs= was not given, so make sure two buffers are used. */ - if (input_blocksize == -1 || output_blocksize == -1) - conversions_mask |= C_TWOBUFS; - if (input_blocksize == -1) - input_blocksize = DEFAULT_BLOCKSIZE; - if (output_blocksize == -1) - output_blocksize = DEFAULT_BLOCKSIZE; - if (conversion_blocksize == 0) - conversions_mask &= ~(C_BLOCK | C_UNBLOCK); } -/* Return the value of STR, interpreted as a non-negative decimal integer, - optionally multiplied by various values. - Return -1 if STR does not represent a number in this format. */ - -int -parse_integer (str) - char *str; +/* + * Cleanup any remaining I/O and flush output. If necesssary, output file + * is truncated. + */ +static void +dd_close() { - register int n = 0; - register int temp; - register char *p = str; - - while (isdigit (*p)) - { - n = n * 10 + *p - '0'; - p++; - } -loop: - switch (*p++) - { - case '\0': - return n; - case 'b': - n *= 512; - goto loop; - case 'k': - n *= 1024; - goto loop; - case 'w': - n *= 2; - goto loop; - case 'x': - temp = parse_integer (p); - if (temp == -1) - return -1; - n *= temp; - break; - default: - return -1; - } - return n; + if (cfunc == def) + def_close(); + else if (cfunc == block) + block_close(); + else if (cfunc == unblock) + unblock_close(); + if (out.dbcnt) + dd_out(1); } -/* Interpret one "conv=..." option. */ - void -parse_conversion (str) - char *str; +dd_out(force) + int force; { - char *new; - int i; - - do - { - new = index (str, ','); - if (new != NULL) - *new++ = '\0'; - for (i = 0; conversions[i].convname != NULL; i++) - if (equal (conversions[i].convname, str)) - { - conversions_mask |= conversions[i].conversion; - break; - } - if (conversions[i].convname == NULL) - { - usage ("%s: invalid conversion", str); - exit (1); + static int warned; + register int cnt, n, nw; + register u_char *outp; + + /* + * Write one or more blocks out. The common case is writing a full + * output block in a single write; increment the full block stats. + * Otherwise, we're into partial block writes. If a partial write, + * and it's a character device, just warn. If a tape device, quit. + * + * The partial writes represent two cases. 1: Where the input block + * was less than expected so the output block was less than expected. + * 2: Where the input block was the right size but we were forced to + * write the block in multiple chunks. The original versions of dd(1) + * never wrote a block in more than a single write, so the latter case + * never happened. + * + * One special case is if we're forced to do the write -- in that case + * we play games with the buffer size, and it's usually a partial write. + */ + outp = out.db; + for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) { + for (cnt = n;; cnt -= nw) { + nw = write(out.fd, outp, cnt); + if (nw < 0) + err("%s: %s", out.name, strerror(errno)); + outp += nw; + st.bytes += nw; + if (nw == n) { + if (n != out.dbsz) + ++st.out_part; + else + ++st.out_full; + break; + } + ++st.out_part; + if (nw == cnt) + break; + if (out.flags & ISCHR && !warned) { + warned = 1; + warn("%s: short write on character device", + out.name); + } + if (out.flags & ISTAPE) + err("%s: short write on tape device", out.name); + } + if ((out.dbcnt -= n) < out.dbsz) + break; } - str = new; - } while (new != NULL); -} - -/* Fix up translation table. */ - -void -apply_translations () -{ - int i; - -#define MX(a) (bit_count (conversions_mask & (a))) - if ((MX (C_ASCII | C_EBCDIC | C_IBM) > 1) - || (MX (C_BLOCK | C_UNBLOCK) > 1) - || (MX (C_LCASE | C_UCASE) > 1) - || (MX (C_UNBLOCK | C_SYNC) > 1)) - { - error (1, 0, "\ -only one conv in {ascii,ebcdic,ibm}, {lcase,ucase}, {block,unblock}, {unblock,sync}"); - } -#undef MX - - if (conversions_mask & C_ASCII) - translate_charset (ebcdic_to_ascii); - - if (conversions_mask & C_UCASE) - { - for (i = 0; i < 256; i++) - if (ISLOWER (trans_table[i])) - trans_table[i] = toupper (trans_table[i]); - translation_needed = 1; - } - else if (conversions_mask & C_LCASE) - { - for (i = 0; i < 256; i++) - if (ISUPPER (trans_table[i])) - trans_table[i] = tolower (trans_table[i]); - translation_needed = 1; - } - - if (conversions_mask & C_EBCDIC) - { - translate_charset (ascii_to_ebcdic); - newline_character = ascii_to_ebcdic['\n']; - space_character = ascii_to_ebcdic[' ']; - } - else if (conversions_mask & C_IBM) - { - translate_charset (ascii_to_ibm); - newline_character = ascii_to_ibm['\n']; - space_character = ascii_to_ibm[' ']; - } -} - -void -translate_charset (new_trans) - unsigned char *new_trans; -{ - int i; - - for (i = 0; i < 256; i++) - trans_table[i] = new_trans[trans_table[i]]; - translation_needed = 1; -} -/* Return the number of 1 bits in `i'. */ - -int -bit_count (i) - register unsigned int i; -{ - register int set_bits; - - for (set_bits = 0; i != 0; set_bits++) - i &= i - 1; - return set_bits; -} - -void -print_stats () -{ - fprintf (stderr, "%u+%u records in\n", r_full, r_partial); - fprintf (stderr, "%u+%u records out\n", w_full, w_partial); - if (r_truncate > 0) - fprintf (stderr, "%u truncated block%s\n", r_truncate, - r_truncate == 1 ? "" : "s"); -} - -void -quit (code) - int code; -{ - int errcode = code ? code : 1; - print_stats (); - if (close (input_fd) < 0) - error (errcode, errno, "%s", input_file); - if (close (output_fd) < 0) - error (errcode, errno, "%s", output_file); - exit (code); -} - -SIGTYPE -interrupt_handler () -{ - quit (1); -} - -void -usage (string, arg0, arg1) - char *string, *arg0, *arg1; -{ - fprintf (stderr, "%s: ", program_name); - fprintf (stderr, string, arg0, arg1); - fprintf (stderr, "\n"); - fprintf (stderr, "\ -Usage: %s [if=file] [of=file] [ibs=bytes] [obs=bytes] [bs=bytes] [cbs=bytes]\n\ - [skip=blocks] [seek=blocks] [count=blocks]\n\ - [conv={ascii,ebcdic,ibm,block,unblock,lcase,ucase,swab,noerror,notrunc,\n\ - sync}]\n\ -Numbers can be followed by a multiplier:\n\ -b=512, k=1024, w=2, xm=number m\n", - program_name); - exit (1); -} - -void -error(n,e,s,s1) -int n, e, s1; -char *s; -{ - if(e) - fprintf(stderr,"error %d:", e); - fprintf(stderr,s, s1); - if(n) - exit(n); + /* Reassemble the output block. */ + if (out.dbcnt) + memmove(out.db, out.dbp - out.dbcnt, out.dbcnt); + out.dbp = out.db + out.dbcnt; } diff --git a/bin/dd/dd.h b/bin/dd/dd.h new file mode 100644 index 0000000000..42724fe035 --- /dev/null +++ b/bin/dd/dd.h @@ -0,0 +1,95 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)dd.h 5.4 (Berkeley) 6/2/92 + */ + +/* Input/output stream state. */ +typedef struct { + u_char *db; /* buffer address */ + u_char *dbp; /* current buffer I/O address */ + u_long dbcnt; /* current buffer byte count */ + int dbrcnt; /* last read byte count */ + u_long dbsz; /* buffer size */ + +#define ISCHR 0x01 /* character device (warn on short) */ +#define ISPIPE 0x02 /* pipe (not truncatable) */ +#define ISTAPE 0x04 /* tape (not seekable) */ +#define NOREAD 0x08 /* not readable */ + u_int flags; + + char *name; /* name */ + int fd; /* file descriptor */ + u_long offset; /* # of blocks to skip */ + + u_long f_stats; /* # of full blocks processed */ + u_long p_stats; /* # of partial blocks processed */ + u_long s_stats; /* # of odd swab blocks */ + u_long t_stats; /* # of truncations */ +} IO; + +typedef struct { + u_long in_full; /* # of full input blocks */ + u_long in_part; /* # of partial input blocks */ + u_long out_full; /* # of full output blocks */ + u_long out_part; /* # of partial output blocks */ + u_long trunc; /* # of truncated records */ + u_long swab; /* # of odd-length swab blocks */ + u_long bytes; /* # of bytes written */ + time_t start; /* start time of dd */ +} STAT; + +/* Flags (in ddflags). */ +#define C_ASCII 0x00001 +#define C_BLOCK 0x00002 +#define C_BS 0x00004 +#define C_CBS 0x00008 +#define C_COUNT 0x00010 +#define C_EBCDIC 0x00020 +#define C_FILES 0x00040 +#define C_IBS 0x00080 +#define C_IF 0x00100 +#define C_LCASE 0x00200 +#define C_NOERROR 0x00400 +#define C_NOTRUNC 0x00800 +#define C_OBS 0x01000 +#define C_OF 0x02000 +#define C_SEEK 0x04000 +#define C_SKIP 0x08000 +#define C_SWAB 0x10000 +#define C_SYNC 0x20000 +#define C_UCASE 0x40000 +#define C_UNBLOCK 0x80000 diff --git a/bin/dd/extern.h b/bin/dd/extern.h new file mode 100644 index 0000000000..e79d39348c --- /dev/null +++ b/bin/dd/extern.h @@ -0,0 +1,66 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 5.4 (Berkeley) 11/13/91 + */ + +#include + +void block __P((void)); +void block_close __P((void)); +void dd_out __P((int)); +void def __P((void)); +void def_close __P((void)); +void err __P((const char *, ...)); +void jcl __P((char **)); +void pos_in __P((void)); +void pos_out __P((void)); +void summary __P((int)); +void terminate __P((int)); +void unblock __P((void)); +void unblock_close __P((void)); +void warn __P((const char *, ...)); + +extern IO in, out; +extern STAT st; +extern void (*cfunc)(); +extern u_long cpy_cnt; +extern u_int cbsz; +extern u_int ddflags; +extern u_int files_cnt; +extern u_char *ctab; +extern u_char a2e_32V[], a2e_POSIX[], a2ibm_32V[], a2ibm_POSIX[], e2a_32V[]; +extern u_char e2a_POSIX[], l2u[], u2l[]; diff --git a/bin/dd/misc.c b/bin/dd/misc.c new file mode 100644 index 0000000000..dce731955d --- /dev/null +++ b/bin/dd/misc.c @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)misc.c 5.7 (Berkeley) 4/28/93"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include + +#include "dd.h" +#include "extern.h" + +/* ARGSUSED */ +void +summary(notused) + int notused; +{ + time_t secs; + char buf[100]; + + (void)time(&secs); + if ((secs -= st.start) == 0) + secs = 1; + /* Use snprintf(3) so that we don't reenter stdio(3). */ + (void)snprintf(buf, sizeof(buf), + "%u+%u records in\n%u+%u records out\n", + st.in_full, st.in_part, st.out_full, st.out_part); + (void)write(STDERR_FILENO, buf, strlen(buf)); + if (st.swab) { + (void)snprintf(buf, sizeof(buf), "%u odd length swab %s\n", + st.swab, (st.swab == 1) ? "block" : "blocks"); + (void)write(STDERR_FILENO, buf, strlen(buf)); + } + if (st.trunc) { + (void)snprintf(buf, sizeof(buf), "%u truncated %s\n", + st.trunc, (st.trunc == 1) ? "block" : "blocks"); + (void)write(STDERR_FILENO, buf, strlen(buf)); + } + (void)snprintf(buf, sizeof(buf), + "%u bytes transferred in %u secs (%u bytes/sec)\n", + st.bytes, secs, st.bytes / secs); + (void)write(STDERR_FILENO, buf, strlen(buf)); +} + +/* ARGSUSED */ +void +terminate(notused) + int notused; +{ + summary(0); + exit(0); +} + +#if __STDC__ +#include +#else +#include +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + extern int errstats; + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "dd: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + if (errstats) + summary(0); + exit(1); + /* NOTREACHED */ +} + +void +#if __STDC__ +warn(const char *fmt, ...) +#else +warn(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "dd: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); +} diff --git a/bin/dd/position.c b/bin/dd/position.c new file mode 100644 index 0000000000..a7bd00146c --- /dev/null +++ b/bin/dd/position.c @@ -0,0 +1,163 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)position.c 5.3 (Berkeley) 8/5/91"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include "dd.h" +#include "extern.h" + +/* + * Position input/output data streams before starting the copy. Device type + * dependent. Seekable devices use lseek, and the rest position by reading. + * Seeking past the end of file can cause null blocks to be written to the + * output. + */ +void +pos_in() +{ + register int bcnt, cnt, nr, warned; + + /* If not a character, pipe or tape device, try to seek on it. */ + if (!(in.flags & (ISCHR|ISPIPE|ISTAPE))) { + if (lseek(in.fd, (off_t)(in.offset * in.dbsz), SEEK_CUR) == -1) + err("%s: %s", in.name, strerror(errno)); + return; + } + + /* + * Read the data. If a pipe, read until satisfy the number of bytes + * being skipped. No differentiation for reading complete and partial + * blocks for other devices. + */ + for (bcnt = in.dbsz, cnt = in.offset, warned = 0; cnt;) { + if ((nr = read(in.fd, in.db, bcnt)) > 0) { + if (in.flags & ISPIPE) { + if (!(bcnt -= nr)) { + bcnt = in.dbsz; + --cnt; + } + } else + --cnt; + continue; + } + + if (nr == 0) { + if (files_cnt > 1) { + --files_cnt; + continue; + } + err("skip reached end of input"); + } + + /* + * Input error -- either EOF with no more files, or I/O error. + * If noerror not set die. POSIX requires that the warning + * message be followed by an I/O display. + */ + if (ddflags & C_NOERROR) { + if (!warned) { + warn("%s: %s", in.name, strerror(errno)); + warned = 1; + summary(0); + } + continue; + } + err("%s: %s", in.name, strerror(errno)); + } +} + +void +pos_out() +{ + register int cnt, n; + struct mtop t_op; + + /* + * If not a tape, try seeking on the file. Seeking on a pipe is + * going to fail, but don't protect the user -- they shouldn't + * have specified the seek operand. + */ + if (!(out.flags & ISTAPE)) { + if (lseek(out.fd, + (off_t)out.offset * out.dbsz, SEEK_SET) == -1) + err("%s: %s", out.name, strerror(errno)); + return; + } + + /* If no read access, try using mtio. */ + if (out.flags & NOREAD) { + t_op.mt_op = MTFSR; + t_op.mt_count = out.offset; + + if (ioctl(out.fd, MTIOCTOP, &t_op) < 0) + err("%s: %s", out.name, strerror(errno)); + return; + } + + /* Read it. */ + for (cnt = 0; cnt < out.offset; ++cnt) { + if ((n = read(out.fd, out.db, out.dbsz)) > 0) + continue; + + if (n < 0) + err("%s: %s", out.name, strerror(errno)); + + /* + * If reach EOF, fill with NUL characters; first, back up over + * the EOF mark. Note, cnt has not yet been incremented, so + * the EOF read does not count as a seek'd block. + */ + t_op.mt_op = MTBSR; + t_op.mt_count = 1; + if (ioctl(out.fd, MTIOCTOP, &t_op) == -1) + err("%s: %s", out.name, strerror(errno)); + + while (cnt++ < out.offset) + if ((n = write(out.fd, out.db, out.dbsz)) != out.dbsz) + err("%s: %s", out.name, strerror(errno)); + break; + } +} diff --git a/bin/df/Makefile b/bin/df/Makefile index d0d329fb63..c987835fae 100644 --- a/bin/df/Makefile +++ b/bin/df/Makefile @@ -1,6 +1,7 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= df +SRCS= df.c getbsize.c CFLAGS+=-DCOMPAT_43 BINGRP= operator BINMODE=2555 diff --git a/bin/df/df.1 b/bin/df/df.1 index 84ad6602f5..43a2e9ca17 100644 --- a/bin/df/df.1 +++ b/bin/df/df.1 @@ -29,9 +29,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)df.1 6.9 (Berkeley) 6/17/91 +.\" @(#)df.1 6.10 (Berkeley) 3/1/92 .\" -.Dd June 17, 1991 +.Dd March 1, 1992 .Dt DF 1 .Os BSD 4 .Sh NAME @@ -48,6 +48,12 @@ displays statistics about the amount of free disk space on the specified or on the filesystem of which .Ar file is a part. +Values are displayed in 512-byte per block block counts, +unless the +.Fl k +option is specified, or the +.Ev BLOCKSIZE +environment variable is set (see below). If neither a file or a filesystem operand is specified, statistics for all mounted filesystems are displayed. .Pp @@ -56,10 +62,7 @@ The following options are available: .It Fl i Include statistics on the number of free inodes. .It Fl k -By default, all sizes are reported in 512-byte block counts. -The -.Fl k -option causes the numbers to be reported in kilobyte counts. +The block counts are forced to be in 1K (1024 8-bit bytes) size blocks. .It Fl n Print out the previously obtained statistics from the filesystems. This option should be used if it is possible that one or more @@ -70,6 +73,13 @@ When this option is specified, will not request new statistics from the filesystems, but will respond with the possibly stale statistics that were previously obtained. .El +.Sh ENVIRONMENTAL VARIABLES +.Bl -tag -width BLOCKSIZE +.It Ev BLOCKSIZE +If the environmental variable +.Ev BLOCKSIZE +is set, the block counts will be displayed in units of that size block. +.El .Sh BUGS The .Fl n diff --git a/bin/df/df.c b/bin/df/df.c index 1ed82c70c2..5fe225d8e1 100644 --- a/bin/df/df.c +++ b/bin/df/df.c @@ -38,38 +38,41 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)df.c 5.24 (Berkeley) 3/6/91"; +static char sccsid[] = "@(#)df.c 5.30 (Berkeley) 4/23/92"; #endif /* not lint */ -/* - * df - */ #include #include #include +#include #include #include #include #include #include -char *getmntpt(); -void ufs_df(), prtstat(); +int bread __P((long, char *, int)); +char *getbsize __P((char *, int *, long *)); +char *getmntpt __P((char *)); +void prtstat __P((struct statfs *, long)); +void ufs_df __P((char *, long)); +void usage __P((void)); + int iflag, kflag, nflag; struct ufs_args mdev; int main(argc, argv) int argc; - char **argv; + char *argv[]; { - extern int errno, optind; - int err, ch, i; - long width, maxwidth, mntsize; - char *mntpt, *mktemp(); struct stat stbuf; struct statfs statfsbuf, *mntbuf; + long width, maxwidth, mntsize; + int err, ch, i; + char *mntpt; + iflag = kflag = nflag = 0; while ((ch = getopt(argc, argv, "ikn")) != EOF) switch(ch) { case 'i': @@ -83,9 +86,7 @@ main(argc, argv) break; case '?': default: - fprintf(stderr, - "usage: df [-ikn] [file | file_system ...]\n"); - exit(1); + usage(); } argc -= optind; argv += optind; @@ -179,36 +180,38 @@ prtstat(sfsp, maxwidth) register struct statfs *sfsp; long maxwidth; { + static long blocksize; + static int headerlen, timesthrough; + static char *header; long used, availblks, inodes; - static int timesthrough; if (maxwidth < 11) maxwidth = 11; if (++timesthrough == 1) { - printf("%-*.*s%s used avail capacity", - maxwidth, maxwidth, "Filesystem", - kflag ? " kbytes" : "512-blks"); + header = getbsize("df", &headerlen, &blocksize); + (void)printf("%-*.*s %s Used Avail Capacity", + maxwidth, maxwidth, "Filesystem", header); if (iflag) - printf(" iused ifree %%iused"); - printf(" Mounted on\n"); + (void)printf(" iused ifree %%iused"); + (void)printf(" Mounted on\n"); } - printf("%-*.*s", maxwidth, maxwidth, sfsp->f_mntfromname); + (void)printf("%-*.*s", maxwidth, maxwidth, sfsp->f_mntfromname); used = sfsp->f_blocks - sfsp->f_bfree; availblks = sfsp->f_bavail + used; - printf("%8ld%8ld%8ld", - sfsp->f_blocks * sfsp->f_fsize / (kflag ? 1024 : 512), - used * sfsp->f_fsize / (kflag ? 1024 : 512), - sfsp->f_bavail * sfsp->f_fsize / (kflag ? 1024 : 512)); - printf("%6.0f%%", + (void)printf(" %*ld %7ld %7ld", headerlen, + sfsp->f_blocks * sfsp->f_fsize / blocksize, + used * sfsp->f_fsize / blocksize, + sfsp->f_bavail * sfsp->f_fsize / blocksize); + (void)printf(" %5.0f%%", availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0); if (iflag) { inodes = sfsp->f_files; used = inodes - sfsp->f_ffree; - printf("%8ld%8ld%6.0f%% ", used, sfsp->f_ffree, + (void)printf(" %7ld %7ld %5.0f%% ", used, sfsp->f_ffree, inodes == 0 ? 100.0 : (double)used / (double)inodes * 100.0); } else - printf(" "); - printf(" %s\n", sfsp->f_mntonname); + (void)printf(" "); + (void)printf(" %s\n", sfsp->f_mntonname); } /* @@ -226,14 +229,12 @@ union { #define sblock sb.iu_fs int fi; -int bread(); void ufs_df(file, maxwidth) char *file; long maxwidth; { - extern int errno; struct statfs statfsbuf; register struct statfs *sfsp; char *mntpt; @@ -243,18 +244,20 @@ ufs_df(file, maxwidth) sync(); if ((fi = open(file, O_RDONLY)) < 0) { - fprintf(stderr, "df: %s: %s\n", file, strerror(errno)); + (void)fprintf(stderr, "df: %s: %s\n", file, strerror(errno)); return; } if (bread((long)SBOFF, (char *)&sblock, SBSIZE) == 0) { - (void) close(fi); + (void)close(fi); return; } sfsp = &statfsbuf; sfsp->f_type = MOUNT_UFS; sfsp->f_flags = 0; - sfsp->f_fsize = sblock.fs_fsize; - sfsp->f_bsize = sblock.fs_bsize; + sfsp->f_bsize = sblock.fs_fsize; +#ifdef notyet + sfsp->f_iosize = sblock.fs_bsize; +#endif sfsp->f_blocks = sblock.fs_dsize; sfsp->f_bfree = sblock.fs_cstotal.cs_nbfree * sblock.fs_frag + sblock.fs_cstotal.cs_nffree; @@ -274,8 +277,6 @@ ufs_df(file, maxwidth) (void) close(fi); } -long lseek(); - int bread(off, buf, cnt) long off; @@ -283,16 +284,22 @@ bread(off, buf, cnt) int cnt; { int n; - extern errno; (void) lseek(fi, off, SEEK_SET); if ((n=read(fi, buf, cnt)) != cnt) { /* probably a dismounted disk if errno == EIO */ if (errno != EIO) { - printf("\nread error off = %ld\n", off); - printf("count = %d; errno = %d\n", n, errno); + (void)printf("\nread error off = %ld\n", off); + (void)printf("count = %d: %s\n", n, strerror(errno)); } return (0); } return (1); } + +void +usage() +{ + (void)fprintf(stderr, "usage: df [-in] [file | file_system ...]\n"); + exit(1); +} diff --git a/bin/df/getbsize.c b/bin/df/getbsize.c new file mode 100644 index 0000000000..c62536ed18 --- /dev/null +++ b/bin/df/getbsize.c @@ -0,0 +1,116 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)getbsize.c 5.3 (Berkeley) 3/9/92"; +#endif /* not lint */ + +#include +#include +extern int kflag; + +char * +getbsize(prog, headerlenp, blocksizep) + char *prog; + int *headerlenp; + long *blocksizep; +{ + static char header[20]; + long n, max, mul, blocksize; + char *ep, *p, *form; + +#define KB (1024L) +#define MB (1024L * 1024L) +#define GB (1024L * 1024L * 1024L) +#define MAXB GB /* No tera, peta, nor exa. */ + form = ""; + /* POSIX requires the -k option to display in 1024-blocks */ + if (kflag) { + n = 1; + blocksize = 1024; + form = "K"; + max = MAXB / KB; + mul = KB; + } + else if ((p = getenv("BLOCKSIZE")) != NULL && *p != '\0') { + if ((n = strtol(p, &ep, 10)) < 0) + goto underflow; + if (n == 0) + n = 1; + if (*ep && ep[1]) + goto fmterr; + switch (*ep) { + case 'G': case 'g': + form = "G"; + max = MAXB / GB; + mul = GB; + break; + case 'K': case 'k': + form = "K"; + max = MAXB / KB; + mul = KB; + break; + case 'M': case 'm': + form = "M"; + max = MAXB / MB; + mul = MB; + break; + case '\0': + max = MAXB; + mul = 1; + break; + default: +fmterr: (void)fprintf(stderr, + "%s: %s: unknown blocksize\n", prog, p); + n = 512; + mul = 1; + break; + } + if (n > max) { + (void)fprintf(stderr, + "%s: maximum blocksize is %dG\n", prog, MAXB / GB); + n = max; + } + if ((blocksize = n * mul) < 512) { +underflow: (void)fprintf(stderr, + "%s: minimum blocksize is 512\n", prog); + form = ""; + blocksize = n = 512; + } + } else + blocksize = n = 512; + + *headerlenp = snprintf(header, sizeof(header), "%d%s-blocks", n, form); + *blocksizep = blocksize; + return (header); +} diff --git a/bin/domainname/Makefile b/bin/domainname/Makefile new file mode 100644 index 0000000000..a0b9020049 --- /dev/null +++ b/bin/domainname/Makefile @@ -0,0 +1,6 @@ +# from: @(#)Makefile 5.3 (Berkeley) 5/11/90 +# $Id$ + +PROG= domainname + +.include diff --git a/bin/domainname/domainname.1 b/bin/domainname/domainname.1 new file mode 100644 index 0000000000..5843bf6686 --- /dev/null +++ b/bin/domainname/domainname.1 @@ -0,0 +1,60 @@ +.\" Copyright (c) 1983, 1988, 1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)domainname.1 6.8 (Berkeley) 7/27/91 +.\" $Id$ +.\" +.Dd July 27, 1991 +.Dt DOMAINNAME 1 +.Os BSD 4.2 +.Sh NAME +.Nm domainname +.Nd set or print the name of the current domain +.Sh SYNOPSIS +.Nm domainname +.Op Ar name-of-domain +.Sh DESCRIPTION +.Nm Domainname +prints the domain name of the current host. The super-user can +set the domain name by supplying an argument; this is usually done in the +network initialization script +.Pa /etc/netstart , +normally run at boot +time. +.Sh SEE ALSO +.Xr hostname 1 , +.Xr getdomainname 2 , +.Xr setdomainname 2 +.Sh HISTORY +The +.Nm domainname +command appeared in +.Bx 4.2 . diff --git a/bin/domainname/domainname.c b/bin/domainname/domainname.c new file mode 100644 index 0000000000..ec1233efc3 --- /dev/null +++ b/bin/domainname/domainname.c @@ -0,0 +1,45 @@ +#ifndef lint +static char rcsid[] = "$Id$"; +#endif /* not lint */ + +#include +#include +#include +#include +#include + +static void usage __P((void)); + +main(argc, argv) + int argc; + char **argv; +{ + char dom[MAXHOSTNAMELEN]; + + if( argc>2 ) { + usage (); + /* NOTREACHED */ + } + + if( argc==2 ) { + if( setdomainname(argv[1], strlen(argv[1])+1) == -1) { + perror("setdomainname"); + exit(1); + } + } else { + if( getdomainname(dom, sizeof(dom)) == -1) { + perror("getdomainname"); + exit(1); + } + printf("%s\n", dom); + } + + exit(0); +} + +static void +usage () +{ + (void)fprintf(stderr, "usage: domainname [name-of-domain]\n"); + exit(1); +} diff --git a/bin/ed/Makefile b/bin/ed/Makefile index a48b0d20fd..5a6a4ea4ae 100644 --- a/bin/ed/Makefile +++ b/bin/ed/Makefile @@ -1,7 +1,16 @@ PROG= ed -CFLAGS+=-I${.CURDIR} -DVI_BANG -DDES -DGNU_REGEX -DHAVE_STRING_H=1 -SRCS= ed.c re.c buf.c cbc.c regex.c +CFLAGS+=-DVI_BANG +SRCS= ed.c re.c buf.c cbc.c LINKS= ${BINDIR}/ed ${BINDIR}/red MLINKS= ed.1 red.1 +.if exists(/usr/lib/libcrypt.a) +CFLAGS+=-DDES +LDADD+= -lcrypt +DPADD+= ${LIBCRYPT} +.endif + +LDADD+= -lgnuregex +DPADD+= /usr/lib/libgnuregex.a + .include diff --git a/bin/ed/POSIX b/bin/ed/POSIX index dd506fb5a6..47a80b9e72 100644 --- a/bin/ed/POSIX +++ b/bin/ed/POSIX @@ -48,3 +48,15 @@ Though ed is not a binary editor, it can be used (if painfully) to edit binary files. To assist in binary editing, when a file containing at least one ASCII NUL character is written, a newline is not appended if it did not already contain one upon reading. + +Since the behavior of `u' (undo) within a `g' (global) command list is +not specified by POSIX D11/2, it follows the behavior of the SunOS ed +(this is the best way, I think, in that the alternatives are either too +complicated to implement or too confusing to use): undo forces a global +command list to be executed only once, rather than for each line matching +a global pattern. In addtion, each instance of `u' within a global command +undoes all previous commands (including undo's) in the command list. + +The `m' (move) command within a `g' command list also follows the SunOS +ed implementation: any moved lines are removed from the global command's +`active' list. diff --git a/bin/ed/README b/bin/ed/README index 9dada652ad..06e302d7b1 100644 --- a/bin/ed/README +++ b/bin/ed/README @@ -7,7 +7,6 @@ compile with little trouble. Otherwise, the macros spl1() and spl0() should be redefined to disable interrupts. The following compiler directives are recognized: -GNU_REGEX - use with GNU regex(3) DES - use to add encryption support (requires crypt(3)) NO_REALLOC_NULL - use if realloc(3) does not accept a NULL pointer BACKWARDS - use for backwards compatibility diff --git a/bin/ed/buf.c b/bin/ed/buf.c index 57c336121c..7c92e55862 100644 --- a/bin/ed/buf.c +++ b/bin/ed/buf.c @@ -49,13 +49,13 @@ static char sccsid[] = "@(#)buf.c 5.5 (Berkeley) 3/28/93"; #include "ed.h" extern char errmsg[]; -extern line_t line0; FILE *sfp; /* scratch file pointer */ char *sfbuf = NULL; /* scratch file input buffer */ int sfbufsz = 0; /* scratch file input buffer size */ off_t sfseek; /* scratch file position */ int seek_write; /* seek before writing */ +line_t line0; /* initial node of line queue */ /* gettxt: get a line of text from the scratch file; return pointer to the text */ @@ -165,7 +165,11 @@ getaddr(lp) while (cp != lp && (cp = cp->next) != &line0) n++; - return (cp != &line0) ? n : 0; + if (n && cp == &line0) { + sprintf(errmsg, "invalid address"); + return ERR; + } + return n; } @@ -244,3 +248,39 @@ quit(n) } exit(n); } + + +unsigned char ctab[256]; /* character translation table */ + +/* init_buf: open scratch buffer; initialize line queue */ +void +init_buf() +{ + int i = 0; + + if (sbopen() < 0) + quit(2); + requeue(&line0, &line0); + for (i = 0; i < 256; i++) + ctab[i] = i; +} + + +/* translit: translate characters in a string */ +char * +translit(s, len, from, to) + char *s; + int len; + int from; + int to; +{ + static int i = 0; + + unsigned char *us; + + ctab[i] = i; /* restore table to initial state */ + ctab[i = from] = to; + for (us = (unsigned char *) s; len-- > 0; us++) + *us = ctab[*us]; + return s; +} diff --git a/bin/ed/ed.1 b/bin/ed/ed.1 index ff6a8499ff..e9a318080b 100644 --- a/bin/ed/ed.1 +++ b/bin/ed/ed.1 @@ -92,9 +92,9 @@ commands have the structure: .I [address [,address]]command[parameters] .RE .sp -The address(es) indicate the line(s) to be affected by the command. -If fewer addresses are given than the command accepts, then default -addresses are supplied. +The address(es) indicate the line or range of lines to be affected by the +command. If fewer addresses are given than the command accepts, then +default addresses are supplied. .SS OPTIONS .TP 8 @@ -311,7 +311,7 @@ not listed below, including `{', '}', `(', `)', `<' and `>', matches itself. .TP 8 -\fR\\\fIc\fR +\fR\e\fIc\fR Any backslash-escaped character .IR c , except for `{', '}', `(', `)', `<' and `>', @@ -389,28 +389,28 @@ anchors the regular expression to the end of a line. Otherwise, it matches itself. .TP 8 -\fR\\<\fR +\fR\e<\fR Anchors the single character regular expression or subexpression immediately following it to the beginning of a word. (This may not be available) .TP 8 -\fR\\>\fR +\fR\e>\fR Anchors the single character regular expression or subexpression immediately following it to the end of a word. (This may not be available) .TP 8 -\fR\\(\fIre\fR\\)\fR +\fR\e(\fIre\fR\e)\fR Defines a subexpression .IR re . Subexpressions may be nested. -A subsequent backreference of the form \fI`\\n'\fR, where +A subsequent backreference of the form \fI`\en'\fR, where .I n is a number in the range [1,9], expands to the text matched by the .IR n th subexpression. -For example, the regular expression `\\(.*\\)\\1' matches any string +For example, the regular expression `\e(.*\e)\e1' matches any string consisting of identical adjacent substrings. Subexpressions are ordered relative to their left delimiter. @@ -426,7 +426,7 @@ the string `abbb' (as opposed to the substring `bbb'), since a null match is the only left-most match. .TP 8 -\fR\\{\fIn,m\fR\\}\fR or \fR\\{\fIn,\fR\\}\fR or \fR\\{\fIn\fR\\}\fR +\fR\e{\fIn,m\fR\e}\fR or \fR\e{\fIn,\fR\e}\fR or \fR\e{\fIn\fR\e}\fR Matches the single character regular expression or subexpression immediately preceding it at least .I n @@ -735,7 +735,7 @@ An unescaped `&' in .I replacement is replaced by the currently matched text. The character sequence -\fI`\\m'\fR, +\fI`\em'\fR, where .I m is a number in the range [1,9], is replaced by the @@ -912,8 +912,8 @@ that line. Buffer file .PD 0 .TP 20 -\fR./ed.hup\fR, $HOME/ed.hup -First and second files to which +ed.hup +The file to which .B ed attempts to write the buffer if the terminal hangs up. @@ -971,6 +971,10 @@ replaces any occurrences of .I old with .IR new . +If the +.I `u' +(undo) command occurs in a global command list, then +the command list is executed only once. If diagnostics are not disabled, attempting to quit .B ed diff --git a/bin/ed/ed.c b/bin/ed/ed.c index de226ff45c..180232770d 100644 --- a/bin/ed/ed.c +++ b/bin/ed/ed.c @@ -334,11 +334,6 @@ getone() } -#define MAXMARK 26 /* max number of marks */ - -line_t *mark[MAXMARK]; /* line markers */ -int markno; /* line marker count */ - /* getnum: return a relative line number from the command buffer */ long getnum(first) @@ -370,7 +365,7 @@ getnum(first) return first ? curln : 1; case '\'': ibufp++; - return (first && islower(*ibufp)) ? getaddr(mark[*ibufp++ - 'a']) : ERR; + return first ? getmark(*ibufp++) : ERR; case '%': case ',': case ';': @@ -583,7 +578,6 @@ doglob(gflag) long ucurln = -1; /* if >= 0, undo enabled */ long ulastln = -1; /* if >= 0, undo enabled */ -int usw = 0; /* if set, undo last undo */ int patlock = 0; /* if set, pattern not released by optpat() */ long rows = 22; /* scroll length: ws_row - 2 */ @@ -645,8 +639,8 @@ docmd(glob) } else if ((fnp = getfn()) == NULL) return ERR; VRFYCMD(); - memset(mark, 0, sizeof mark); - lndelete(1, lastln); + if (lndelete(1, lastln) < 0) + return ERR; ureset(); if (sbclose() < 0) return ERR; @@ -726,13 +720,10 @@ docmd(glob) if (line2 == 0) { sprintf(errmsg, "invalid address"); return ERR; - } else if (!islower(c)) { - sprintf(errmsg, "invalid mark character"); - return ERR; - } + } VRFYCMD(); - if (!mark[c - 'a']) markno++; - mark[c - 'a'] = getlp(line2); + if (putmark(c, getlp(line2)) < 0) + return ERR; break; case 'l': if (ckrange(curln, curln) < 0) @@ -752,9 +743,7 @@ docmd(glob) } VRFYCMD(); if (!glob) ureset(); - if (num == line1 - 1 || num == line2) - curln = line2; - else if (move(num) < 0) + if (move(num, glob) < 0) return ERR; else modified = 1; @@ -844,6 +833,10 @@ docmd(glob) return ERR; } else if (!(sflags & SGF)) sgflag &= 0xff; + if (*ibufp != '\n' && *(ibufp + 1) == '\n') { + sprintf(errmsg, "invalid pattern delimiter"); + return ERR; + } tpat = pat; spl1(); if ((!sflags || (sflags & SGR)) @@ -912,7 +905,7 @@ docmd(glob) return ERR; } VRFYCMD(); - if (undo() < 0) + if (undo(glob) < 0) return ERR; break; case 'v': @@ -1300,74 +1293,6 @@ append(n, glob) } -#ifdef sun -/* subst: change all text matching a pattern in a range of lines according to - a substitution template; return status */ -subst(pat, gflag) - pattern_t *pat; - int gflag; -{ - undo_t *up = NULL; - char *txt; - char *eot; - line_t *bp, *ep, *np; - long ocl; - long nsubs = 0; - int len; - - ep = getlp(curln = line2); - for (bp = getlp(line1); bp != ep->next; bp = bp->next) - if ((len = regsub(pat, bp, gflag)) < 0) - return ERR; - else if (!len) { - /* add copy of bp after current line - this avoids - overloading the undo structure, since only two - undo nodes are needed for the whole substitution; - the cost is high, but the less than if undo is - overloaded on a Sun evidently. XXX */ - if ((np = lpdup(bp)) == NULL) - return ERR; - spl1(); - lpqueue(np); - if (up) - up->t = getlp(curln); - else if ((up = upush(UADD, curln, curln)) == NULL) { - spl0(); - return ERR; - } - spl0(); - } else { - txt = rbuf; - eot = rbuf + len; - spl1(); - do { - if ((txt = puttxt(txt)) == NULL) { - spl0(); - return ERR; - } else if (up) - up->t = getlp(curln); - else if ((up = upush(UADD, curln, curln)) == NULL) { - spl0(); - return ERR; - } - } while (txt != eot); - spl0(); - nsubs++; - } - ocl = curln; - lndelete(line1, line2); - curln = ocl - (line2 - line1 + 1); - if (nsubs == 0 && !(gflag & GLB)) { - sprintf(errmsg, "no match"); - return ERR; - } else if ((gflag & (GPR | GLS | GNP)) - && doprint(curln, curln, gflag) < 0) - return ERR; - return 1; -} -#else /* sun */ - - /* subst: change all text matching a pattern in a range of lines according to a substitution template; return status */ subst(pat, gflag) @@ -1389,7 +1314,8 @@ subst(pat, gflag) return ERR; else if (len) { up = NULL; - lndelete(curln, curln); + if (lndelete(curln, curln) < 0) + return ERR; txt = rbuf; eot = rbuf + len; spl1(); @@ -1416,7 +1342,6 @@ subst(pat, gflag) return ERR; return 1; } -#endif /* sun */ /* regsub: replace text matched by a pattern according to a substitution @@ -1498,7 +1423,8 @@ join(from, to) } CKBUF(buf, n, size + 2, ERR); memcpy(buf + size, "\n", 2); - lndelete(from, to); + if (lndelete(from, to) < 0) + return ERR; curln = from - 1; spl1(); if (puttxt(buf) == NULL @@ -1513,28 +1439,38 @@ join(from, to) /* move: move a range of lines */ -move(num) +move(num, glob) long num; + int glob; { - line_t *b1, *a1, *b2, *a2; + line_t *b1, *a1, *b2, *a2, *lp; long n = nextln(line2, lastln); long p = prevln(line1, lastln); + int done = (num == line1 - 1 || num == line2); spl1(); - if (upush(UMOV, p, n) == NULL + if (done) { + a2 = getlp(n); + b2 = getlp(p); + curln = line2; + } else if (upush(UMOV, p, n) == NULL || upush(UMOV, num, nextln(num, lastln)) == NULL) { - spl0(); - return ERR; + spl0(); + return ERR; + } else { + a1 = getlp(n); + if (num < line1) + b1 = getlp(p), b2 = getlp(num); /* this getlp last! */ + else b2 = getlp(num), b1 = getlp(p); /* this getlp last! */ + a2 = b2->next; + requeue(b2, b1->next); + requeue(a1->prev, a2); + requeue(b1, a1); + curln = num + ((num < line1) ? line2 - line1 + 1 : 0); } - a1 = getlp(n); - if (num < line1) - b1 = getlp(p), b2 = getlp(num); /* this getlp last! */ - else b2 = getlp(num), b1 = getlp(p); /* this getlp last! */ - a2 = b2->next; - requeue(b2, b1->next); - requeue(a1->prev, a2); - requeue(b1, a1); - curln = num + ((num < line1) ? line2 - line1 + 1 : 0); + if (glob) + for (lp = b2->next; lp != a2; lp = lp->next) + lp->len &= ~ACTV; /* zero ACTV bit */ spl0(); return 0; } @@ -1857,7 +1793,6 @@ undo_t *ustack = NULL; /* undo stack */ long usize = 0; /* stack size variable */ long u_p = 0; /* undo stack pointer */ - /* upush: return pointer to intialized undo node */ undo_t * upush(type, from, to) @@ -1894,14 +1829,22 @@ upush(type, from, to) return NULL; } + +/* USWAP: swap undo nodes */ +#define USWAP(x,y) { \ + undo_t utmp; \ + utmp = x, x = y, y = utmp; \ +} + + /* undo: undo last change to the editor buffer */ -undo() +undo(glob) + int glob; { - long n = usw ? 0 : u_p - 1; - int i = usw ? 1 : -1; - long j = u_p; + long n; long ocurln = curln; long olastln = lastln; + line_t *lp, *np; if (ucurln == -1 || ulastln == -1) { sprintf(errmsg, "nothing to undo"); @@ -1910,8 +1853,8 @@ undo() modified = 1; getlp(0); /* this getlp last! */ spl1(); - for (; j-- > 0; n += i) - switch(ustack[n].type ^ usw) { + for (n = u_p; n-- > 0;) { + switch(ustack[n].type) { case UADD: requeue(ustack[n].h->prev, ustack[n].t->next); break; @@ -1921,16 +1864,23 @@ undo() break; case UMOV: case VMOV: - requeue(ustack[n + i].h, ustack[n].h->next); - requeue(ustack[n].t->prev, ustack[n + i].t); + requeue(ustack[n - 1].h, ustack[n].h->next); + requeue(ustack[n].t->prev, ustack[n - 1].t); requeue(ustack[n].h, ustack[n].t); - n += i, j--; + n--; break; default: /*NOTREACHED*/ ; } - usw = 1 - usw; + ustack[n].type ^= 1; + } + /* reverse undo order */ + for (n = u_p; n-- > (u_p + 1)/ 2;) + USWAP(ustack[n], ustack[u_p - 1 - n]); + if (glob) + for (lp = np = getlp(0); (lp = lp->next) != np;) + lp->len &= ~ACTV; /* zero ACTV bit */ curln = ucurln, ucurln = ocurln; lastln = ulastln, ulastln = olastln; spl0(); @@ -1943,28 +1893,71 @@ void ureset() { line_t *lp, *ep, *tl; - int i; while (u_p--) - if ((ustack[u_p].type ^ usw) == UDEL) { + if (ustack[u_p].type == UDEL) { ep = ustack[u_p].t->next; for (lp = ustack[u_p].h; lp != ep; lp = tl) { - if (markno) - for (i = 0; i < MAXMARK; i++) - if (mark[i] == lp) { - mark[i] = NULL; - markno--; - } + clrmark(lp); tl = lp->next; free(lp); } } - u_p = usw = 0; + u_p = 0; ucurln = curln; ulastln = lastln; } +#define MAXMARK 26 /* max number of marks */ + +line_t *mark[MAXMARK]; /* line markers */ +int markno; /* line marker count */ + +/* getmark: return address of a marked line */ +long +getmark(n) + int n; +{ + if (!islower(n)) { + sprintf(errmsg, "invalid mark character"); + return ERR; + } + return getaddr(mark[n - 'a']); +} + + +/* putmark: set a line node mark */ +int +putmark(n, lp) + int n; + line_t *lp; +{ + if (!islower(n)) { + sprintf(errmsg, "invalid mark character"); + return ERR; + } else if (mark[n - 'a'] == NULL) + markno++; + mark[n - 'a'] = lp; + return 0; +} + + +/* clrmark: clear line node marks */ +void +clrmark(lp) + line_t *lp; +{ + int i; + + if (markno) + for (i = 0; i < MAXMARK; i++) + if (mark[i] == lp) { + mark[i] = NULL; + markno--; + } +} + /* sgetline: read a line of text up a maximum size from a file; return line length */ @@ -2146,7 +2139,6 @@ onintr(signo) } - void dohup(signo) int signo; @@ -2201,44 +2193,6 @@ dowinch(signo) } -unsigned char ctab[256]; /* character translation table */ - -/* translit: translate characters in a string */ -char * -translit(s, len, from, to) - char *s; - int len; - int from; - int to; -{ - static int i = 0; - - unsigned char *us; - - ctab[i] = i; /* restore table to initial state */ - ctab[i = from] = to; - for (us = (unsigned char *) s; len-- > 0; us++) - *us = ctab[*us]; - return s; -} - - -line_t line0; /* initial node of line queue */ - -/* init_buf: open scratch buffer; initialize line queue */ -void -init_buf() -{ - int i = 0; - - if (sbopen() < 0) - quit(2); - requeue(&line0, &line0); - for (i = 0; i < 256; i++) - ctab[i] = i; -} - - /* ckfn: return a legal filename */ char * ckfn(s) diff --git a/bin/ed/ed.h b/bin/ed/ed.h index 1b277fe935..b3905370f9 100644 --- a/bin/ed/ed.h +++ b/bin/ed/ed.h @@ -49,7 +49,7 @@ #define BITS(type) (BITSPERBYTE * (int)sizeof(type)) #define CHARBITS BITS(char) #define INTBITS BITS(int) -#define INTHIBIT (1 << (INTBITS - 1)) +#define INTHIBIT (unsigned) (1 << (INTBITS - 1)) #define ERR (-2) #define EMOD (-3) @@ -206,6 +206,7 @@ int desputc __P((int, FILE *)); int docmd __P((int)); void err __P((char *)); char *ccl __P((char *)); +void clrmark __P((line_t *)); void cvtkey __P((char *, char *)); long doglob __P((int)); void dohup __P((int)); @@ -223,6 +224,7 @@ int getkey __P((void)); char *getlhs __P((int)); int getline __P((void)); int getlist __P((void)); +long getmark __P((int)); long getnum __P((int)); long getone __P((void)); line_t *getlp __P((long)); @@ -236,12 +238,12 @@ line_t *lpdup __P((line_t *)); void lpqueue __P((line_t *)); void makekey __P((char *)); char *makesub __P((int)); -char *translit __P((char *, int, int, int)); -int move __P((long)); +int move __P((long, int)); int oddesc __P((char *, char *)); void onhup __P((int)); void onintr __P((int)); pattern_t *optpat __P((void)); +int putmark __P((int, line_t *)); void putstr __P((char *, int, long, int)); char *puttxt __P((char *)); void quit __P((int)); @@ -253,7 +255,8 @@ int catsub __P((char *, regmatch_t *, int)); int subst __P((pattern_t *, int)); int tobinhex __P((int, int)); int transfer __P((long)); -int undo __P((void)); +char *translit __P((char *, int, int, int)); +int undo __P((int)); undo_t *upush __P((int, long, long)); void ureset __P((void)); diff --git a/bin/ed/re.c b/bin/ed/re.c index 462c816626..7553d5f2e3 100644 --- a/bin/ed/re.c +++ b/bin/ed/re.c @@ -64,14 +64,11 @@ optpat() char delim; int n; - if ((delim = *ibufp) == '\n') { - if (!exp) sprintf(errmsg, "no previous pattern"); - return exp; - } else if (delim == ' ' || *++ibufp == '\n') { + if ((delim = *ibufp) == ' ') { sprintf(errmsg, "invalid pattern delimiter"); return NULL; - } else if (*ibufp == delim) { - sprintf(errmsg, "no previous pattern"); + } else if (delim == '\n' || *++ibufp == '\n' || *ibufp == delim) { + if (!exp) sprintf(errmsg, "no previous pattern"); return exp; } else if ((exps = getlhs(delim)) == NULL) return NULL; @@ -84,16 +81,10 @@ optpat() return NULL; } patlock = 0; -#ifdef GNU_REGEX - /* initialize pattern buffer */ - exp->buffer = NULL; - exp->allocated = 0L; - exp->fastmap = 0; /* not used by GNU regex after 0.12 */ - exp->translate = 0; -#endif if (n = regcomp(exp, exps, 0)) { regerror(n, exp, errmsg, sizeof errmsg); - return NULL; + free(exp); + return exp = NULL; } return exp; } diff --git a/bin/ed/test/Makefile b/bin/ed/test/Makefile index 750f388fe4..ca45a5183e 100644 --- a/bin/ed/test/Makefile +++ b/bin/ed/test/Makefile @@ -6,12 +6,12 @@ all: build test build: mkscripts.sh @echo building test scripts... @chmod +x mkscripts.sh - @mkscripts.sh ${ED} + @./mkscripts.sh ${ED} test: build ckscripts.sh @echo running test scripts... @chmod +x ckscripts.sh - @ckscripts.sh ${ED} + @./ckscripts.sh ${ED} clean: rm -f *.ed *.[oz] *~ diff --git a/bin/ed/test/README b/bin/ed/test/README index 46d4133503..8917f36ace 100644 --- a/bin/ed/test/README +++ b/bin/ed/test/README @@ -37,5 +37,5 @@ i1-err.ed k1-err.ed r1-err.ed -In addition, one of !1-err.ed or !2.ed will fail, depending on whether or +In addition, one of bang1-err.ed or bang2.ed will fail, depending on whether or not ed was compiled with the VI_BANG directive. diff --git a/bin/ed/test/!1.d b/bin/ed/test/bang1.d similarity index 100% rename from bin/ed/test/!1.d rename to bin/ed/test/bang1.d diff --git a/bin/ed/test/!1.err b/bin/ed/test/bang1.err similarity index 100% rename from bin/ed/test/!1.err rename to bin/ed/test/bang1.err diff --git a/bin/ed/test/!1.r b/bin/ed/test/bang1.r similarity index 100% rename from bin/ed/test/!1.r rename to bin/ed/test/bang1.r diff --git a/bin/ed/test/!1.t b/bin/ed/test/bang1.t similarity index 100% rename from bin/ed/test/!1.t rename to bin/ed/test/bang1.t diff --git a/bin/ed/test/!2.d b/bin/ed/test/bang2.d similarity index 100% rename from bin/ed/test/!2.d rename to bin/ed/test/bang2.d diff --git a/bin/ed/test/!2.err b/bin/ed/test/bang2.err similarity index 100% rename from bin/ed/test/!2.err rename to bin/ed/test/bang2.err diff --git a/bin/ed/test/!2.r b/bin/ed/test/bang2.r similarity index 100% rename from bin/ed/test/!2.r rename to bin/ed/test/bang2.r diff --git a/bin/ed/test/!2.t b/bin/ed/test/bang2.t similarity index 100% rename from bin/ed/test/!2.t rename to bin/ed/test/bang2.t diff --git a/bin/ed/test/g3.d b/bin/ed/test/g3.d new file mode 100644 index 0000000000..92f337e977 --- /dev/null +++ b/bin/ed/test/g3.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/g3.r b/bin/ed/test/g3.r new file mode 100644 index 0000000000..cc6fbddec2 --- /dev/null +++ b/bin/ed/test/g3.r @@ -0,0 +1,5 @@ +linc 3 +xine 1 +xine 2 +xinc 4 +xinc5 diff --git a/bin/ed/test/g3.t b/bin/ed/test/g3.t new file mode 100644 index 0000000000..2d052a6e84 --- /dev/null +++ b/bin/ed/test/g3.t @@ -0,0 +1,4 @@ +g/./s//x/\ +3m0 +g/./s/e/c/\ +2,3m1 diff --git a/bin/ed/test/g4.d b/bin/ed/test/g4.d new file mode 100644 index 0000000000..92f337e977 --- /dev/null +++ b/bin/ed/test/g4.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/g4.r b/bin/ed/test/g4.r new file mode 100644 index 0000000000..350882d823 --- /dev/null +++ b/bin/ed/test/g4.r @@ -0,0 +1,7 @@ +hello +zine 1 +line 2 +line 3 +line 4 +line5 +world diff --git a/bin/ed/test/g4.t b/bin/ed/test/g4.t new file mode 100644 index 0000000000..ec618166cc --- /dev/null +++ b/bin/ed/test/g4.t @@ -0,0 +1,13 @@ +g/./s/./x/\ +u\ +s/./y/\ +u\ +s/./z/\ +u +u +0a +hello +. +$a +world +. diff --git a/bin/ed/test/nl.err b/bin/ed/test/nl.err new file mode 100644 index 0000000000..8949a85006 --- /dev/null +++ b/bin/ed/test/nl.err @@ -0,0 +1 @@ +,1 diff --git a/bin/ed/test/nl1.d b/bin/ed/test/nl1.d new file mode 100644 index 0000000000..92f337e977 --- /dev/null +++ b/bin/ed/test/nl1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/nl1.r b/bin/ed/test/nl1.r new file mode 100644 index 0000000000..9d8854cd04 --- /dev/null +++ b/bin/ed/test/nl1.r @@ -0,0 +1,8 @@ + + +hello world +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/nl1.t b/bin/ed/test/nl1.t new file mode 100644 index 0000000000..ea192e9b82 --- /dev/null +++ b/bin/ed/test/nl1.t @@ -0,0 +1,8 @@ +1 + + +0a + + +hello world +. diff --git a/bin/ed/test/nl2.d b/bin/ed/test/nl2.d new file mode 100644 index 0000000000..92f337e977 --- /dev/null +++ b/bin/ed/test/nl2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/nl2.r b/bin/ed/test/nl2.r new file mode 100644 index 0000000000..fe99e41628 --- /dev/null +++ b/bin/ed/test/nl2.r @@ -0,0 +1,6 @@ +line 1 +line 2 +line 3 +line 4 +line5 +hello world diff --git a/bin/ed/test/nl2.t b/bin/ed/test/nl2.t new file mode 100644 index 0000000000..73fd27b7e2 --- /dev/null +++ b/bin/ed/test/nl2.t @@ -0,0 +1,4 @@ +a +hello world +. +0;/./ diff --git a/bin/expr/Makefile b/bin/expr/Makefile index 4c9ac6ac4b..c2db2b639a 100644 --- a/bin/expr/Makefile +++ b/bin/expr/Makefile @@ -1,16 +1,10 @@ -# -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 1 00092 -# -------------------- ----- ---------------------- -# -# 15 Mar 93 Patchkit Coordinator Corrected SRCS, missing .c -# -PROG=expr -SRCS=expr.c -CFLAGS+=-I. -I${.CURDIR} -NOMAN=noman -CLEANFILES+=expr.c y.tab.h +# /b/source/CVS/src/bin/expr/Makefile,v 1.5 1993/06/14 19:56:06 jtc Exp + +PROG= expr +SRCS= expr.c +CLEANFILES+= expr.c y.tab.h +LDADD+= -lgnuregex +DPADD+= /usr/lib/libgnuregex.a expr.c: ${YACC} -d ${.IMPSRC} diff --git a/bin/expr/TODO b/bin/expr/TODO deleted file mode 100644 index 23fc96d477..0000000000 --- a/bin/expr/TODO +++ /dev/null @@ -1,2 +0,0 @@ -parenthesis are broken -not well tested, may have other obscure flaws diff --git a/bin/expr/expr.1 b/bin/expr/expr.1 new file mode 100644 index 0000000000..a592564f8d --- /dev/null +++ b/bin/expr/expr.1 @@ -0,0 +1,132 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 1993 Winning Strategies, Inc. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Winning Strategies, Inc. +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software withough specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $Id: expr.1,v 1.5 1993/10/04 22:06:59 jtc Exp $ +.\" +.Dd July 3, 1993 +.Dt EXPR 1 +.Os +.Sh NAME +.Nm expr +.Nd evaluate expression +.Sh SYNOPSIS +.Nm expr +.Ar expression +.Sh DESCRIPTION +The +.Nm expr +utility evaluates +.Ar expression +and writes the result on standard output. +.Pp +All operators are separate arguments to the +.Nm expr +utility. +Characters special to the command interpreter must be escaped. +.Pp +Operators are listed below in order of increasing precidence. +Operators with equal precidence are grouped within { } symbols. +.Bl -tag -width indent +.It Ar expr1 Li | Ar expr2 +Returns the evaluation of +.Ar expr1 +if it is neither an empty string nor zero; +otherwise, returns the evaluation of +.Ar expr2 . +.It Ar expr1 Li & Ar expr2 +Returns the evaluation of +.Ar expr1 +if neither expression evaluates to an empty string or zero; +otherwise, returns zero. +.It Ar expr1 Li "{=, >, >=, <, <=, !=}" Ar expr2 +Returns the results of integer comparision if both arguments are integers; +otherwise, returns the results of string comparison using the locale-specific +collation sequence. +The result of each comparison is 1 if the specified relation is true, +or 0 if the relation is false. +.It Ar expr1 Li "{+, -}" Ar expr2 +Returns the results of addition or subtraction of integer-valued arguments. +.It Ar expr1 Li "{*, /, %}" Ar expr2 +Returns the results of multiplication, integer division, or remainder of integer-valued arguments. +.It Ar expr1 Li : Ar expr2 +The +.Dq \: +operator matches +.Ar expr1 +against +.Ar expr2 , +which must be a regular expression. The regular expression is anchored +to the begining of the string with an implicit +.Dq ^ . +.Pp +If the match succeeds and the pattern contains at least one regular +expression subexpression +.Dq "\e(...\e)" , +the string corresponding to +.Dq "\e1" +is returned; +otherwise the matching operator returns the number of characters matched. +If the match fails and the pattern contains a regular expression subexpression +the null string is returned; +otherwise 0. +.El +.Pp +Parentheses are used for grouping in the usual manner. +.Sh EXAMPLES +.Bl -enum +.It +The following example adds one to the variable a. +.Dl a=`expr $a + 1` +.It +The following example returns the filename portion of a pathname stored +in variable a. The // characters act to eliminate ambiguity with the +division operator. +.Dl expr "//$a" Li : '.*/\e(.*\e)' +.It +The following example returns the number of characters in variable a. +.Dl expr $a Li : '.*' +.El +.Sh DIAGNOSTICS +The +.Nm expr +utility exits with one of the following values: +.Bl -tag -width Ds -compact +.It 0 +the expression is neither an empty string nor 0. +.It 1 +the expression is an empty string or 0. +.It 2 +the expression is invalid. +.El +.Sh STANDARDS +The +.Nm expr +utility conforms to +.St -p1003.2 . diff --git a/bin/expr/expr.y b/bin/expr/expr.y index f8c389c24c..57ad5022e4 100644 --- a/bin/expr/expr.y +++ b/bin/expr/expr.y @@ -1,30 +1,30 @@ %{ /* Written by Pace Willisson (pace@blitz.com) - * and placed in the public domain + * and placed in the public domain. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00148 - * -------------------- ----- ---------------------- + * Largely rewritten by J.T. Conklin (jtc@wimsey.com) * - * 20 Apr 93 J. T. Conklin Many fixes for () and other such things + * $Id : /b/source/CVS/src/bin/expr/expr.y,v 1.11 1993/08/17 16:01:23 jtc Exp $ */ + #include #include #include +#include #include - +#include + enum valtype { integer, string } ; - + struct val { enum valtype type; union { char *s; int i; } u; -}; +} ; struct val *result; struct val *op_or (); @@ -81,7 +81,6 @@ expr: TOKEN | expr '/' expr { $$ = op_div ($1, $3); } | expr '%' expr { $$ = op_rem ($1, $3); } | expr ':' expr { $$ = op_colon ($1, $3); } - | '-' expr %prec UNARY { $$ = op_minus (NULL, $2); } ; @@ -95,8 +94,7 @@ int i; vp = (struct val *) malloc (sizeof (*vp)); if (vp == NULL) { - fprintf (stderr, "expr: out of memory\n"); - exit (2); + err (2, NULL); } vp->type = integer; @@ -112,8 +110,7 @@ char *s; vp = (struct val *) malloc (sizeof (*vp)); if (vp == NULL || ((vp->u.s = strdup (s)) == NULL)) { - fprintf (stderr, "expr: out of memory\n"); - exit (2); + err (2, NULL); } vp->type = string; @@ -176,8 +173,7 @@ struct val *vp; tmp = malloc (25); if (tmp == NULL) { - fprintf (stderr, "expr: out of memory\n"); - exit (2); + err (2, NULL); } sprintf (tmp, "%d", vp->u.i); @@ -224,13 +220,10 @@ int is_zero_or_null (vp) struct val *vp; { - /* Like most other versions of expr, this version will return - false for a string value of multiple zeros.*/ - if (vp->type == integer) { return (vp->u.i == 0); } else { - return (*vp->u.s == 0 || strcmp (vp->u.s, "0") == 0); + return (*vp->u.s == 0 || (to_integer (vp) && vp->u.i == 0)); } /* NOTREACHED */ } @@ -240,6 +233,8 @@ main (argc, argv) int argc; char **argv; { + setlocale (LC_ALL, ""); + av = argv + 1; yyparse (); @@ -249,18 +244,14 @@ char **argv; else printf ("%s\n", result->u.s); - if (is_zero_or_null (result)) - exit (1); - else - exit (0); + exit (is_zero_or_null (result)); } int yyerror (s) char *s; { - fprintf (stderr, "expr: syntax error\n"); - exit (2); + errx (2, "syntax error"); } @@ -297,16 +288,10 @@ struct val *a, *b; { struct val *r; - /* attempt to coerce both arguments to integers */ - (void) to_integer (a); - (void) to_integer (b); - - /* But if either one of them really is a string, do - a string comparison */ if (isstring (a) || isstring (b)) { to_string (a); to_string (b); - r = make_integer (strcmp (a->u.s, b->u.s) == 0); + r = make_integer (strcoll (a->u.s, b->u.s) == 0); } else { r = make_integer (a->u.i == b->u.i); } @@ -322,16 +307,10 @@ struct val *a, *b; { struct val *r; - /* attempt to coerce both arguments to integers */ - (void) to_integer (a); - (void) to_integer (b); - - /* But if either one of them really is a string, do - a string comparison */ if (isstring (a) || isstring (b)) { to_string (a); to_string (b); - r = make_integer (strcmp (a->u.s, b->u.s) > 0); + r = make_integer (strcoll (a->u.s, b->u.s) > 0); } else { r= make_integer (a->u.i > b->u.i); } @@ -347,16 +326,10 @@ struct val *a, *b; { struct val *r; - /* attempt to coerce both arguments to integers */ - (void) to_integer (a); - (void) to_integer (b); - - /* But if either one of them really is a string, do - a string comparison */ if (isstring (a) || isstring (b)) { to_string (a); to_string (b); - r = make_integer (strcmp (a->u.s, b->u.s) < 0); + r = make_integer (strcoll (a->u.s, b->u.s) < 0); } else { r = make_integer (a->u.i < b->u.i); } @@ -372,16 +345,10 @@ struct val *a, *b; { struct val *r; - /* attempt to coerce both arguments to integers */ - (void) to_integer (a); - (void) to_integer (b); - - /* But if either one of them really is a string, do - a string comparison */ if (isstring (a) || isstring (b)) { to_string (a); to_string (b); - r = make_integer (strcmp (a->u.s, b->u.s) >= 0); + r = make_integer (strcoll (a->u.s, b->u.s) >= 0); } else { r = make_integer (a->u.i >= b->u.i); } @@ -397,16 +364,10 @@ struct val *a, *b; { struct val *r; - /* attempt to coerce both arguments to integers */ - (void) to_integer (a); - (void) to_integer (b); - - /* But if either one of them really is a string, do - a string comparison */ if (isstring (a) || isstring (b)) { to_string (a); to_string (b); - r = make_integer (strcmp (a->u.s, b->u.s) <= 0); + r = make_integer (strcoll (a->u.s, b->u.s) <= 0); } else { r = make_integer (a->u.i <= b->u.i); } @@ -422,16 +383,10 @@ struct val *a, *b; { struct val *r; - /* attempt to coerce both arguments to integers */ - (void) to_integer (a); - (void) to_integer (b); - - /* But if either one of them really is a string, do - a string comparison */ if (isstring (a) || isstring (b)) { to_string (a); to_string (b); - r = make_integer (strcmp (a->u.s, b->u.s) != 0); + r = make_integer (strcoll (a->u.s, b->u.s) != 0); } else { r = make_integer (a->u.i != b->u.i); } @@ -448,8 +403,7 @@ struct val *a, *b; struct val *r; if (!to_integer (a) || !to_integer (b)) { - fprintf (stderr, "expr: non-numeric argument\n"); - exit (2); + errx (2, "non-numeric argument"); } r = make_integer (a->u.i + b->u.i); @@ -465,8 +419,7 @@ struct val *a, *b; struct val *r; if (!to_integer (a) || !to_integer (b)) { - fprintf (stderr, "expr: non-numeric argument\n"); - exit (2); + errx (2, "non-numeric argument"); } r = make_integer (a->u.i - b->u.i); @@ -482,8 +435,7 @@ struct val *a, *b; struct val *r; if (!to_integer (a) || !to_integer (b)) { - fprintf (stderr, "expr: non-numeric argument\n"); - exit (2); + errx (2, "non-numeric argument"); } r = make_integer (a->u.i * b->u.i); @@ -499,13 +451,11 @@ struct val *a, *b; struct val *r; if (!to_integer (a) || !to_integer (b)) { - fprintf (stderr, "expr: non-numeric argument\n"); - exit (2); + errx (2, "non-numeric argument"); } if (b->u.i == 0) { - fprintf (stderr, "expr: division by zero\n"); - exit (2); + errx (2, "division by zero"); } r = make_integer (a->u.i / b->u.i); @@ -521,13 +471,11 @@ struct val *a, *b; struct val *r; if (!to_integer (a) || !to_integer (b)) { - fprintf (stderr, "expr: non-numeric argument\n"); - exit (2); + errx (2, "non-numeric argument"); } if (b->u.i == 0) { - fprintf (stderr, "expr: division by zero\n"); - exit (2); + errx (2, "division by zero"); } r = make_integer (a->u.i % b->u.i); @@ -536,59 +484,50 @@ struct val *a, *b; return r; } -#include +#include struct val * op_colon (a, b) struct val *a, *b; { - regexp *rp; - char *newexp; - char *p; - char *q; - - newexp = malloc (3 * strlen (b->u.s)); - p = b->u.s; - q = newexp; - - *q++ = '^'; - while (*p) { - if (*p == '\\') { - p++; - if (*p == '(' || *p == ')') { - *q++ = *p++; - } else { - *q++ = '\\'; - *q++ = *p++; - } - } else if (*p == '(' || *p == ')') { - *q++ = '\\'; - *q++ = *p++; - } else { - *q++ = *p++; - } + regex_t rp; + regmatch_t rm[2]; + char errbuf[256]; + int eval; + struct val *v; + + /* coerce to both arguments to strings */ + to_string(a); + to_string(b); + + /* compile regular expression */ + if ((eval = regcomp (&rp, b->u.s, 0)) != 0) { + regerror (eval, &rp, errbuf, sizeof(errbuf)); + errx (2, "%s", errbuf); } - *q = 0; - - if ((rp = regcomp (newexp)) == NULL) - yyerror ("invalid regular expression"); - - if (regexec (rp, a->u.s)) { - if (rp->startp[1]) { - rp->endp[1][0] = 0; - return (make_str (rp->startp[1])); + + /* compare string against pattern */ + /* remember that patterns are anchored to the beginning of the line */ + if (regexec(&rp, a->u.s, 2, rm, 0) == 0 && rm[0].rm_so == 0) { + if (rm[1].rm_so >= 0) { + *(a->u.s + rm[1].rm_eo) = '\0'; + v = make_str (a->u.s + rm[1].rm_so); + } else { - return (make_integer (rp->endp[0] - rp->startp[0])); + v = make_integer (rm[0].rm_eo - rm[0].rm_so); } } else { - return (make_integer (0)); + if (rp.re_nsub == 0) { + v = make_integer (0); + } else { + v = make_str (""); + } } -} -void -regerror (s) -const char *s; -{ - fprintf (stderr, "expr: %s\n", s); - exit (2); + /* free arguments and pattern buffer */ + free_value (a); + free_value (b); + regfree (&rp); + + return v; } diff --git a/bin/kill/kill.1 b/bin/kill/kill.1 index 810cb2829b..e61eb2f1bb 100644 --- a/bin/kill/kill.1 +++ b/bin/kill/kill.1 @@ -42,6 +42,13 @@ .Nd terminate or signal a process .Sh SYNOPSIS .Nm kill +.Op Fl s Ar signal_name +.Ar pid +\&... +.Nm kill +.Fl l +.Op Ar exit_status +.Nm kill .Op Fl signal_name .Ar pid \&... @@ -49,8 +56,6 @@ .Op Fl signal_number .Ar pid \&... -.Nm kill -.Op Fl l .Sh DESCRIPTION The kill utility sends the .Dv TERM @@ -62,15 +67,18 @@ Only the super-user may send signals to other users' processes. The options are as follows: .Pp .Bl -tag -width Ds -.It Fl l -List the signal names. +.It Fl l Op Ar exit_status +If no operand is given, list the signal names; otherwise, write +the signal name corresponding to +.Ar exit_status . +.It Fl s Ar signal_name +A symbolic signal name specifying the signal to be sent instead of the +default +.Dv TERM . .It Fl signal_name A symbolic signal name specifying the signal to be sent instead of the default .Dv TERM . -The -.Fl l -option displays the signal names. .It Fl signal_number A non-negative decimal integer, specifying the signal to be sent instead of the default @@ -80,8 +88,7 @@ of the default Some of the more commonly used signals: .Bd -ragged -offset indent -compact .Bl -column XXX TERM -.It -1 -1 (broadcast to all processes, super-user only) -.It 0 0 (sh(1) only, signals all members of process group) +.It 1 HUP (hang up) .It 2 INT (interupt) .It 3 QUIT (quit) .It 6 ABRT (abort) @@ -106,6 +113,12 @@ for details. .Xr ps 1 , .Xr kill 2 , .Xr sigvec 2 +.Sh STANDARDS +The +.Nm kill +function is expected to be +.St -p1003.2 +compatible. .Sh HISTORY A .Nm kill diff --git a/bin/kill/kill.c b/bin/kill/kill.c index 9aa50d7129..705b5fce8d 100644 --- a/bin/kill/kill.c +++ b/bin/kill/kill.c @@ -49,12 +49,12 @@ static char sccsid[] = "@(#)kill.c 5.3 (Berkeley) 7/1/91"; #include static char *signals[] = { - "hup", "int", "quit", "ill", "trap", "iot", /* 1 - 6 */ - "emt", "fpe", "kill", "bus", "segv", "sys", /* 7 - 12 */ - "pipe", "alrm", "term", "urg", "stop", "tstp", /* 13 - 18 */ - "cont", "chld", "ttin", "ttou", "io", "xcpu", /* 19 - 24 */ - "xfsz", "vtalrm", "prof", "winch", "info", "usr1", /* 25 - 30 */ - "usr2", NULL, /* 31 - 32 */ + "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", /* 1 - 6 */ + "EMT", "FPE", "KILL", "BUS", "SEGV", "SYS", /* 7 - 12 */ + "PIPE", "ALRM", "TERM", "URG", "STOP", "TSTP", /* 13 - 18 */ + "CONT", "CHLD", "TTIN", "TTOU", "IO", "XCPU", /* 19 - 24 */ + "XFSZ", "VTALRM", "PROF", "WINCH", "INFO", "USR1", /* 25 - 30 */ + "USR2", NULL, /* 31 - 32 */ }; main(argc, argv) @@ -68,24 +68,60 @@ main(argc, argv) if (argc < 2) usage(); - if (!strcmp(*++argv, "-l")) { - printsig(stdout); - exit(0); - } - numsig = SIGTERM; - if (**argv == '-') { + argc--, argv++; + if (strcmp(*argv, "-l") == 0) { + if (argc > 2) { + usage (); + /* NOTREACHED */ + } + if (argc == 2) { + argv++; + if (isdigit(**argv)) { + numsig = strtol(*argv, &ep, 10); + if (*argv && !*ep) { + if (numsig > 0 && numsig < NSIG) { + printsig (numsig); + exit (0); + } + + numsig -= 128; + if (numsig > 0 && numsig < NSIG) { + printsig (numsig); + exit (0); + } + } + (void)fprintf(stderr, + "kill: illegal signal number %s\n", *argv); + exit(1); + } + usage (); + /* NOTREACHED */ + } + printsignals(stdout); + exit(0); + } else if (strcmp(*argv, "-s") == 0) { + if (argc < 2) { + (void)fprintf(stderr, + "kill: option requires an argument -- s\n"); + usage(); + } + argc--,argv++; + if (strcmp (*argv, "0") == 0) { + numsig = 0; + } else { + if ((numsig = signame_to_signum (*argv)) < 0) { + nosig(*argv); + /* NOTREACHED */ + } + } + argc--,argv++; + } else if (**argv == '-') { ++*argv; if (isalpha(**argv)) { - if (!strncasecmp(*argv, "sig", 3)) - *argv += 3; - for (p = signals;; ++p) { - if (!*p) - nosig(*argv); - if (!strcasecmp(*p, *argv)) { - numsig = p - signals + 1; - break; - } + if ((numsig = signame_to_signum (*argv)) < 0) { + nosig(*argv); + /* NOTREACHED */ } } else if (isdigit(**argv)) { numsig = strtol(*argv, &ep, 10); @@ -94,11 +130,13 @@ main(argc, argv) "kill: illegal signal number %s\n", *argv); exit(1); } - if (numsig <= 0 || numsig > NSIG) + if (numsig <= 0 || numsig >= NSIG) { nosig(*argv); + /* NOTREACHED */ + } } else nosig(*argv); - ++argv; + argc--,argv++; } if (!*argv) @@ -120,30 +158,67 @@ main(argc, argv) exit(errors); } +int +signame_to_signum (sig) + char *sig; +{ + char **p; + + if (!strncasecmp(sig, "sig", 3)) + sig += 3; + for (p = signals; *p; ++p) { + if (!strcasecmp(*p, sig)) { + return p - signals + 1; + } + } + return -1; +} + nosig(name) char *name; { (void)fprintf(stderr, "kill: unknown signal %s; valid signals:\n", name); - printsig(stderr); + printsignals(stderr); exit(1); } -printsig(fp) +printsig(sig) + int sig; +{ + printf ("%s\n", signals[sig - 1]); +} + +printsignals(fp) FILE *fp; { - register char **p; + register char **p = signals;; - for (p = signals; *p; ++p) { - (void)fprintf(fp, "%s ", *p); - if ((p - signals) == NSIG / 2 - 1) - (void)fprintf(fp, "\n"); + /* From POSIX 1003.2, Draft 11.2: + When the -l option is specified, the symbolic name of each + signal shall be written in the following format: + "%s%c", , + where the is in uppercase, without the SIG prefix, + and the shall either be a or a . + For the last signal written, shall be a */ + + /* This looses if the signals array is empty; But, since it + will "never happen", there is no need to add wrap this + in a conditional that will always succeed. */ + (void)fprintf(fp, "%s", *p); + + for (++p ; *p; ++p) { + (void)fprintf(fp, " %s", *p); } (void)fprintf(fp, "\n"); } usage() { - (void)fprintf(stderr, "usage: kill [-l] [-sig] pid ...\n"); + (void)fprintf(stderr, "usage: kill [-s signal_name] pid ...\n"); + (void)fprintf(stderr, " kill -l [exit_status]\n"); + (void)fprintf(stderr, "obsolete usage:\n"); + (void)fprintf(stderr, " kill -signal_name pid ...\n"); + (void)fprintf(stderr, " kill -signal_number pid ...\n"); exit(1); } diff --git a/bin/ls/cmp.c b/bin/ls/cmp.c index 1c8d3d5cf0..de579c5c00 100644 --- a/bin/ls/cmp.c +++ b/bin/ls/cmp.c @@ -36,6 +36,7 @@ #ifndef lint static char sccsid[] = "@(#)cmp.c 5.4 (Berkeley) 3/8/91"; +static char rcsid[] = "$Header: /b/source/CVS/src/bin/ls/cmp.c,v 1.3 1993/03/23 00:26:06 cgd Exp $"; #endif /* not lint */ #include diff --git a/bin/ls/ls.1 b/bin/ls/ls.1 index 32d1414a68..a5cb30ec74 100644 --- a/bin/ls/ls.1 +++ b/bin/ls/ls.1 @@ -34,6 +34,8 @@ .\" .\" @(#)ls.1 6.18 (Berkeley) 6/27/91 .\" +.\" $Header: /b/source/CVS/src/bin/ls/ls.1,v 1.3 1993/03/23 00:26:07 cgd Exp $ +.\" .Dd June 27, 1991 .Dt LS 1 .Os diff --git a/bin/ls/ls.c b/bin/ls/ls.c index 4fe6e0c38f..56fff08b56 100644 --- a/bin/ls/ls.c +++ b/bin/ls/ls.c @@ -42,6 +42,7 @@ char copyright[] = #ifndef lint static char sccsid[] = "@(#)ls.c 5.48 (Berkeley) 4/3/91"; +static char rcsid[] = "$Header: /b/source/CVS/src/bin/ls/ls.c,v 1.5 1993/05/26 10:23:24 deraadt Exp $"; #endif /* not lint */ #include @@ -238,13 +239,14 @@ main(argc, argv) (f_longform || f_listdir || f_type || f_recursive) && !f_ignorelink ? lstat : stat; if (!argc) { - static char dot[] = "."; + static char *nargv[2]; + char dot[2]; - argc = 1; - argv[0] = dot; - argv[1] = NULL; - } - doargs(argc, argv); + strcpy(dot, "."); + nargv[0] = dot; + doargs(1, nargv); + } else + doargs(argc, argv); exit(0); } diff --git a/bin/ls/ls.h b/bin/ls/ls.h index 2d30337186..74cab90612 100644 --- a/bin/ls/ls.h +++ b/bin/ls/ls.h @@ -34,6 +34,8 @@ * SUCH DAMAGE. * * @(#)ls.h 5.11 (Berkeley) 7/22/90 + * + * $Header: /b/source/CVS/src/bin/ls/ls.h,v 1.3 1993/03/23 00:26:09 cgd Exp $ */ typedef struct _lsstruct { diff --git a/bin/ls/print.c b/bin/ls/print.c index 09ab426846..0a793ff721 100644 --- a/bin/ls/print.c +++ b/bin/ls/print.c @@ -36,6 +36,7 @@ #ifndef lint static char sccsid[] = "@(#)print.c 5.24 (Berkeley) 10/19/90"; +static char rcsid[] = "$Header: /a/cvs/386BSD/src/bin/ls/print.c,v 1.2 1993/06/29 02:59:33 nate Exp $"; #endif /* not lint */ #include @@ -211,6 +212,9 @@ printtype(mode) case S_IFSOCK: (void)putchar('='); return(1); + case S_IFIFO: + (void)putchar('|'); + return(1); } if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { (void)putchar('*'); diff --git a/bin/ls/util.c b/bin/ls/util.c index 7c3861be52..e9df3bcce5 100644 --- a/bin/ls/util.c +++ b/bin/ls/util.c @@ -36,6 +36,7 @@ #ifndef lint static char sccsid[] = "@(#)util.c 5.8 (Berkeley) 7/22/90"; +static char rcsid[] = "$Header: /b/source/CVS/src/bin/ls/util.c,v 1.3 1993/03/23 00:26:11 cgd Exp $"; #endif /* not lint */ #include diff --git a/bin/mkdir/mkdir.1 b/bin/mkdir/mkdir.1 index 260d42b400..95eb1d3237 100644 --- a/bin/mkdir/mkdir.1 +++ b/bin/mkdir/mkdir.1 @@ -34,7 +34,8 @@ .\" .\" @(#)mkdir.1 6.9 (Berkeley) 6/27/91 .\" -.Vx +.\" $Header: /b/source/CVS/src/bin/mkdir/mkdir.1,v 1.4 1993/07/20 22:27:07 jtc Exp $ +.\" .Dd June 27, 1991 .Dt MKDIR 1 .Os @@ -43,29 +44,47 @@ .Nd make directories .Sh SYNOPSIS .Nm mkdir +.Op Fl m Ar mode .Op Fl p .Ar directory_name ... .Sh DESCRIPTION .Nm Mkdir -creates the directories named as operands, in the order specified, -using mode +creates the directories named as operands, in the order specified. +The default mode of created directories is .Li \&0777 modified by the current .Xr umask 2 . .Pp The options are as follows: -.Tw Ds -.Tp Fl p +.Bl -tag -width Ds +.It Fl m Ar mode +Set the file permission bits of newly-created directories to +.Ar mode . +The +.Ar mode +is specified as in +.Xr chmod 1 . +In symbolic mode strings, the +.Dq + +and +.Dq - +operators are interpreted relative to an assumed initial mode of +.Dq a=rwx . +.It Fl p Create intermediate directories as required. If this option is not specified, the full path prefix of each operand must already exist. -.Tp +.El .Pp The user must have write permission in the parent directory. .Pp .Nm Mkdir exits 0 if successful, and >0 if an error occurred. .Sh SEE ALSO -.Xr rmdir 1 +.Xr chmod 1 , +.Xr rmdir 1 , +.Xr umask 2 .Sh STANDARDS -Mkdir is POSIX 1003.2 compliant. -This manual page is derived from the POSIX 1003.2 manual page. +.Nm Mkdir +is expected to be +.St -p1003.2 +compatible. diff --git a/bin/mkdir/mkdir.c b/bin/mkdir/mkdir.c index 50843c4b17..f7b0cda650 100644 --- a/bin/mkdir/mkdir.c +++ b/bin/mkdir/mkdir.c @@ -39,6 +39,7 @@ char copyright[] = #ifndef lint static char sccsid[] = "@(#)mkdir.c 5.7 (Berkeley) 5/31/90"; +static char rcsid[] = "$Header: /b/source/CVS/src/bin/mkdir/mkdir.c,v 1.4 1993/07/20 22:27:08 jtc Exp $"; #endif /* not lint */ #include @@ -46,22 +47,42 @@ static char sccsid[] = "@(#)mkdir.c 5.7 (Berkeley) 5/31/90"; #include #include #include +#include extern int errno; +extern void *setmode(); +extern mode_t getmode(); main(argc, argv) int argc; char **argv; { - extern int optind; int ch, exitval, pflag; + void *set; + mode_t mode, dir_mode; + + /* default file mode is a=rwx (777) with selected permissions + removed in accordance with the file mode creation mask. + For intermediate path name components, the mode is the default + modified by u+wx so that the subdirectories can always be + created. */ + mode = 0777 & ~umask(0); + dir_mode = mode | S_IWUSR | S_IXUSR; pflag = 0; - while ((ch = getopt(argc, argv, "p")) != EOF) + while ((ch = getopt(argc, argv, "pm:")) != EOF) switch(ch) { case 'p': pflag = 1; break; + case 'm': + if ((set = setmode(optarg)) == NULL) { + (void)fprintf(stderr, + "mkdir: invalid file mode.\n"); + exit(1); + } + mode = getmode (set, S_IRWXU | S_IRWXG | S_IRWXO); + break; case '?': default: usage(); @@ -69,50 +90,53 @@ main(argc, argv) if (!*(argv += optind)) usage(); - - for (exitval = 0; *argv; ++argv) + + for (exitval = 0; *argv; ++argv) { if (pflag) - exitval |= build(*argv); - else if (mkdir(*argv, 0777) < 0) { + exitval |= build(*argv, mode, dir_mode); + else if (mkdir(*argv, mode) < 0) { (void)fprintf(stderr, "mkdir: %s: %s\n", *argv, strerror(errno)); exitval = 1; } + } exit(exitval); } -build(path) +/* + * build -- create directories. + * mode - file mode of terminal directory + * dir_mode - file mode of intermediate directories + */ +build(path, mode, dir_mode) char *path; + mode_t mode; + mode_t dir_mode; { register char *p; struct stat sb; - int create, ch; + int ch; - for (create = 0, p = path;; ++p) + for (p = path;; ++p) { if (!*p || *p == '/') { ch = *p; *p = '\0'; if (stat(path, &sb)) { - if (errno != ENOENT || mkdir(path, 0777) < 0) { + if (errno != ENOENT || mkdir(path, (ch) ? dir_mode : mode) < 0) { (void)fprintf(stderr, "mkdir: %s: %s\n", path, strerror(errno)); return(1); } - create = 1; } if (!(*p = ch)) break; } - if (!create) { - (void)fprintf(stderr, "mkdir: %s: %s\n", path, - strerror(EEXIST)); - return(1); } return(0); } usage() { - (void)fprintf(stderr, "usage: mkdir [-p] dirname ...\n"); + (void)fprintf(stderr, "usage: mkdir [-p] [-m mode] dirname ...\n"); exit(1); } diff --git a/bin/ps/devname.c b/bin/ps/devname.c index c9c2d81a96..5f2815d30b 100644 --- a/bin/ps/devname.c +++ b/bin/ps/devname.c @@ -55,7 +55,7 @@ devname(dev, type) DBT data, key; if (!db && !failure && - !(db = hash_open(_PATH_DEVDB, O_RDONLY, 0, NULL))) { + !(db = dbopen(_PATH_DEVDB, O_RDONLY, 0, DB_HASH, NULL))) { (void)fprintf(stderr, "warning: no device database %s\n", _PATH_DEVDB); failure = 1; diff --git a/bin/ps/keyword.c b/bin/ps/keyword.c index 06d010458c..5776137c9c 100644 --- a/bin/ps/keyword.c +++ b/bin/ps/keyword.c @@ -112,7 +112,7 @@ VAR var[] = { {"msgrcv", "MSGRCV", NULL, USER, rvar, 4, ROFF(ru_msgrcv), LONG, "d"}, {"msgsnd", "MSGSND", NULL, USER, rvar, 4, ROFF(ru_msgsnd), LONG, "d"}, {"ni", "", "nice"}, - {"nice", "NI", NULL, 0, pvar, 2, POFF(p_nice), CHAR, "d"}, + {"nice", "NI", NULL, 0, pvar, 3, POFF(p_nice), CHAR, "d"}, {"nivcsw", "NIVCSW", NULL, USER, rvar, 5, ROFF(ru_nivcsw), LONG, "d"}, {"nsignals", "", "nsigs"}, {"nsigs", "NSIGS", NULL, USER, rvar, 4, ROFF(ru_nsignals), LONG, "d"}, diff --git a/bin/ps/ps.1 b/bin/ps/ps.1 index d9d5dc325b..bfb3fe5312 100644 --- a/bin/ps/ps.1 +++ b/bin/ps/ps.1 @@ -35,10 +35,10 @@ .Dt PS 1 .Os BSD 4 .Sh NAME -.Nm \&ps +.Nm ps .Nd process status .Sh SYNOPSIS -.Nm \&ps +.Nm ps .Op Fl aChjlmrSTuvwx .Op Fl M Ar core .Op Fl N Ar system @@ -50,7 +50,7 @@ .Nm ps .Op Fl L .Sh DESCRIPTION -.Nm \&Ps +.Nm Ps displays a header line followed by lines containing information about your processes that have controlling terminals. This information is sorted by process @@ -96,7 +96,7 @@ Sort by memory usage, instead of by process .Tn ID . .It Fl N Extract the name list from the specified system instead of the default -.Dq Pa /vmunix . +.Dq Pa /386bsd . .It Fl O Add the information associated with the space or comma separated list of keywords specifed, after the process @@ -154,7 +154,7 @@ is your window size. If the .Fl w option is specified more than once, -.Nm \&ps +.Nm ps will use as many columns as necessary without regard for your window size. .It Fl x Display information about processes without controlling terminals. @@ -325,7 +325,7 @@ When printing using the command keyword, a process that has exited and has a parent that has not yet waited for the process (in other words, a zombie) is listed as ``'', and a process which is blocked while trying to exit is listed as ``''. -.Nm \&Ps +.Nm Ps makes an educated guess as to the file name and arguments given when the process was created by examining memory or the swap area. The method is inherently somewhat unreliable and in any event a process @@ -476,7 +476,7 @@ wait channel (as a symbolic name) exit or stop status (valid only for stopped or zombie process) .El .Sh FILES -.Bl -tag -width /var/run/kvm_vmunix.db -compact +.Bl -tag -width /var/run/kvm_386bsd.db -compact .It Pa /dev special files and device names .It Pa /dev/drum @@ -485,9 +485,9 @@ default swap device default kernel memory .It Pa /var/run/dev.db /dev name database -.It Pa /var/run/kvm_vmunix.db +.It Pa /var/run/kvm_386bsd.db system namelist database -.It Pa /vmunix +.It Pa /386bsd default system namelist .El .Sh SEE ALSO @@ -498,6 +498,6 @@ default system namelist .Xr pstat 8 .Sh BUGS Since -.Nm \&ps +.Nm ps cannot run faster than the system and is run as any other scheduled process, the information it displays can never be exact. diff --git a/bin/ps/ps.c b/bin/ps/ps.c index df97859311..15fcf3ffda 100644 --- a/bin/ps/ps.c +++ b/bin/ps/ps.c @@ -291,7 +291,7 @@ main(argc, argv) for (nentries = 0; p = kvm_nextproc(); ++nentries) { kinfo[nentries].ki_p = p; kinfo[nentries].ki_e = kvm_geteproc(p); - if (needuser) + if (needuser || needcomm) saveuser(&kinfo[nentries]); } /* diff --git a/bin/pwd/pwd.1 b/bin/pwd/pwd.1 index 2a99e6bd21..ef8e8d1fa2 100644 --- a/bin/pwd/pwd.1 +++ b/bin/pwd/pwd.1 @@ -34,7 +34,6 @@ .\" .\" @(#)pwd.1 6.5 (Berkeley) 6/27/91 .\" -.Vx .Dd June 27, 1991 .Dt PWD 1 .Os BSD 4 diff --git a/bin/rcp/Makefile b/bin/rcp/Makefile index 96d1ded21b..e8cac815eb 100644 --- a/bin/rcp/Makefile +++ b/bin/rcp/Makefile @@ -6,4 +6,10 @@ BINOWN= root BINMODE=4555 .PATH: ${.CURDIR}/../../usr.bin/rlogin +.if exists(/usr/lib/libcrypt.a) +#CFLAGS+=-DCRYPT -DKERBEROS +#DPADD+= ${LIBCRYPT} ${LIBKRB} +#LDADD+= -lcrypt -lkrb +.endif + .include diff --git a/bin/rm/rm.c b/bin/rm/rm.c index 739ef68e27..6b65b0ce28 100644 --- a/bin/rm/rm.c +++ b/bin/rm/rm.c @@ -38,7 +38,7 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)rm.c 4.26 (Berkeley) 3/10/91"; +static char sccsid[] = "@(#)rm.c 4.27 (Berkeley) 1/27/92"; #endif /* not lint */ #include @@ -154,7 +154,7 @@ rmtree(argv) /* Pre-order: give user chance to skip. */ case FTS_D: if (iflag && !check(p->fts_path, p->fts_accpath, - &p->fts_statb)) { + p->fts_statp)) { (void)fts_set(fts, p, FTS_SKIP); p->fts_number = SKIPPED; } @@ -167,7 +167,7 @@ rmtree(argv) } if (!fflag && - !check(p->fts_path, p->fts_accpath, &p->fts_statb)) + !check(p->fts_path, p->fts_accpath, p->fts_statp)) continue; /* diff --git a/bin/rmail/Makefile b/bin/rmail/Makefile index c575539b7d..15e2977878 100644 --- a/bin/rmail/Makefile +++ b/bin/rmail/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.2 (Berkeley) 4/20/91 PROG= rmail -MAN8= rmail.0 +MAN8= rmail.8 .include diff --git a/bin/rmail/rmail.8 b/bin/rmail/rmail.8 index 5815fa5910..1c4a5828d8 100644 --- a/bin/rmail/rmail.8 +++ b/bin/rmail/rmail.8 @@ -31,8 +31,6 @@ .\" .\" @(#)rmail.8 6.8 (Berkeley) 4/20/91 .\" -.Vx -.Vx .Dd April 20, 1991 .Dt RMAIL 8 .Os BSD 4.2 diff --git a/bin/rmdir/rmdir.1 b/bin/rmdir/rmdir.1 index 2753391822..b0207e5938 100644 --- a/bin/rmdir/rmdir.1 +++ b/bin/rmdir/rmdir.1 @@ -34,7 +34,6 @@ .\" .\" @(#)rmdir.1 6.5 (Berkeley) 6/27/91 .\" -.Vx .Dd June 27, 1991 .Dt RMDIR 1 .Os @@ -58,33 +57,36 @@ must be specified first so the parent directory is empty when .Nm rmdir tries to remove it. -.\" .Pp -.\" The following option is available: -.\" .Tw Ds -.\" .Tp Fl p -.\" Each -.\" .Ar directory -.\" argument is treated as a pathname of which all -.\" components will be removed, if they are empty, -.\" starting with the last most component. -.\" (See -.\" .Xr rm 1 -.\" for fully non-discriminant recursive removal). +.Pp +The following option is available: +.Bl -tag -width Ds +.It Fl p +Each +.Ar directory +argument is treated as a pathname of which all +components will be removed, if they are empty, +starting with the last most component. +(See +.Xr rm 1 +for fully non-discriminant recursive removal). +.El .Pp The .Nm rmdir utility exits with one of the following values: -.Tw Ds -.Tp Li \&0 +.Bl -tag -width Ds +.It Li \&0 Each directory entry specified by a dir operand referred to an empty directory and was removed successfully. -.Tp Li \&>\&0 +.It Li \&>\&0 An error occurred. -.Tp +.El .Sh SEE ALSO .Xr rm 1 .Sh STANDARDS The .Nm rmdir -function is expected to be POSIX 1003.2 compatible. +utility is expected to be +.St -p1003.2 +compatible. diff --git a/bin/rmdir/rmdir.c b/bin/rmdir/rmdir.c index 132af540b7..5ed7919001 100644 --- a/bin/rmdir/rmdir.c +++ b/bin/rmdir/rmdir.c @@ -45,22 +45,85 @@ static char sccsid[] = "@(#)rmdir.c 5.3 (Berkeley) 5/31/90"; * Remove directory */ #include +#include +#include +#include main(argc, argv) int argc; char **argv; { int errors; + int ch; + int delete_parent_directories = 0; - if (argc < 2) { - fprintf(stderr, "usage: rmdir directory ...\n"); - exit(1); + while ((ch = getopt (argc, argv, "p")) != EOF) { + switch (ch) { + case 'p': + delete_parent_directories = 1; + break; + case '?': + default: + usage(); + /* NOTREACHED */ + } + } + + if (!*(argv += optind)) { + usage (); + /* NOTREACHED */ } - for (errors = 0; *++argv;) - if (rmdir(*argv) < 0) { - fprintf(stderr, "rmdir: "); - perror(*argv); - errors = 1; + + for (errors = 0; *argv; argv++) { + if (!delete_parent_directories) { + if (rmdir(*argv) < 0) { + fprintf(stderr, "rmdir: %s: %s\n", + *argv, strerror(errno)); + errors = 1; + } + } else { + if (rmdirp(*argv) < 0) { + errors = 1; + } } + } + exit(errors); } + +int +rmdirp (char *path) +{ + char *slash; + + /* point slash at last slash */ + slash = strrchr (path, '/'); + + while (slash != NULL) { + if (rmdir (path) < 0) { + fprintf(stderr, "rmdir: %s: %s\n", + path, strerror(errno)); + return -1; + } + + /* skip trailing slash characters */ + while (slash > path && *slash == '/') + slash--; + + *++slash = '\0'; + slash = strrchr (path, '/'); + } + + if (rmdir (path) < 0) { + fprintf(stderr, "rmdir: %s: %s\n", path, strerror(errno)); + return -1; + } + + return 0; +} + +usage() +{ + fprintf(stderr, "usage: rmdir [-p] directory ...\n"); + exit(1); +} diff --git a/bin/sh/Makefile b/bin/sh/Makefile index 42e91b7c94..dc22825ed9 100644 --- a/bin/sh/Makefile +++ b/bin/sh/Makefile @@ -1,16 +1,16 @@ -# %W% (Berkeley) %G% +# Makefile,v 1.7 1993/08/09 04:58:18 mycroft Exp PROG= sh SRCS= builtins.c cd.c dirent.c echo.c error.c eval.c exec.c expand.c \ input.c jobs.c mail.c main.c memalloc.c miscbltin.c \ - mystring.c nodes.c options.c parser.c redir.c show.c signames.c \ + mystring.c nodes.c options.c parser.c redir.c show.c \ syntax.c trap.c output.c var.c OBJS+= init.o CFLAGS+=-DSHELL -I. -I${.CURDIR} .PATH: ${.CURDIR}/bltin CLEANFILES+=\ - builtins.c builtins.h init.c mkinit mknodes mksignames mksyntax \ - nodes.c nodes.h signames.c signames.h syntax.c syntax.h token.def + builtins.c builtins.h init.c mkinit mknodes mksyntax \ + nodes.c nodes.h syntax.c syntax.h token.def .depend parser.o: token.def token.def: mktokens @@ -21,26 +21,21 @@ builtins.h builtins.c: ${.CURDIR}/mkbuiltins ${.CURDIR}/builtins init.c: mkinit ${SRCS} ./mkinit '${CC} -c ${CFLAGS} init.c' ${.ALLSRC} + touch ${.TARGET} mkinit: ${.CURDIR}/mkinit.c - ${CC} ${CFLAGS} ${.CURDIR}/mkinit.c -o $@ + ${CC} ${CFLAGS} ${LDFLAGS} ${.CURDIR}/mkinit.c -o $@ ${LDADD} nodes.c nodes.h: mknodes ${.CURDIR}/nodetypes ${.CURDIR}/nodes.c.pat ./mknodes ${.CURDIR}/nodetypes ${.CURDIR}/nodes.c.pat mknodes: ${.CURDIR}/mknodes.c - ${CC} ${CFLAGS} ${.CURDIR}/mknodes.c -o $@ - -signames.c signames.h: mksignames - ./mksignames - -mksignames: ${.CURDIR}/mksignames.c - ${CC} ${CFLAGS} ${.CURDIR}/mksignames.c -o $@ + ${CC} ${CFLAGS} ${LDFLAGS} ${.CURDIR}/mknodes.c -o $@ ${LDADD} syntax.c syntax.h: mksyntax ./mksyntax mksyntax: ${.CURDIR}/mksyntax.c ${.CURDIR}/parser.h - ${CC} ${CFLAGS} ${.CURDIR}/mksyntax.c -o $@ + ${CC} ${CFLAGS} ${LDFLAGS} ${.CURDIR}/mksyntax.c -o $@ ${LDADD} .include diff --git a/bin/sh/TOUR b/bin/sh/TOUR index 669c0a9334..7cc0f226a3 100644 --- a/bin/sh/TOUR +++ b/bin/sh/TOUR @@ -1,4 +1,6 @@ # @(#)TOUR 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/TOUR,v 1.3 1993/03/23 00:27:32 cgd Exp A Tour through Ash diff --git a/bin/sh/b.c b/bin/sh/b.c index 61f63b3824..9b7f568ecd 100644 --- a/bin/sh/b.c +++ b/bin/sh/b.c @@ -2,6 +2,10 @@ * This file was generated by the mkbuiltins program. */ +#ifndef lint +static char rcsid[] = "b.c,v 1.2 1993/08/02 17:15:45 mycroft Exp"; +#endif /* not lint */ + #include "shell.h" #include "builtins.h" diff --git a/bin/sh/bltin/bltin.h b/bin/sh/bltin/bltin.h index 166dc39518..8049e4ba8d 100644 --- a/bin/sh/bltin/bltin.h +++ b/bin/sh/bltin/bltin.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)bltin.h 5.1 (Berkeley) 3/7/91 + * from: @(#)bltin.h 5.1 (Berkeley) 3/7/91 + * bltin.h,v 1.4 1993/08/01 18:58:44 mycroft Exp */ /* diff --git a/bin/sh/bltin/echo.1 b/bin/sh/bltin/echo.1 index bab3657d0f..59369829b4 100644 --- a/bin/sh/bltin/echo.1 +++ b/bin/sh/bltin/echo.1 @@ -32,7 +32,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)echo.1 5.1 (Berkeley) 3/7/91 +.\" from: @(#)echo.1 5.1 (Berkeley) 3/7/91 +.\" echo.1,v 1.4 1993/08/01 07:58:19 mycroft Exp .\" .TH ECHO 1"March 7, 1991" .UC 7 diff --git a/bin/sh/bltin/echo.c b/bin/sh/bltin/echo.c index efabff0b2f..cd2e072ac2 100644 --- a/bin/sh/bltin/echo.c +++ b/bin/sh/bltin/echo.c @@ -32,10 +32,13 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * @(#)echo.c 5.2 (Berkeley) 3/13/91 */ +#ifndef lint +/*static char sccsid[] = "from: @(#)echo.c 5.2 (Berkeley) 3/13/91";*/ +static char rcsid[] = "echo.c,v 1.4 1993/08/01 18:58:43 mycroft Exp"; +#endif /* not lint */ + /* * Echo command. */ diff --git a/bin/sh/builtins b/bin/sh/builtins index e0665d18e5..b605bd8905 100644 --- a/bin/sh/builtins +++ b/bin/sh/builtins @@ -35,6 +35,8 @@ # SUCH DAMAGE. # # @(#)builtins 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/builtins,v 1.4 1993/07/07 01:11:56 jtc Exp # # This file lists all the builtin commands. The first column is the name @@ -61,6 +63,7 @@ execcmd exec exitcmd exit exportcmd export readonly #exprcmd expr test [ +falsecmd false fgcmd -j fg getoptscmd getopts hashcmd hash diff --git a/bin/sh/cd.c b/bin/sh/cd.c index 5abe2983d0..e458c1f0ae 100644 --- a/bin/sh/cd.c +++ b/bin/sh/cd.c @@ -32,17 +32,11 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00050 - * -------------------- ----- ---------------------- - * - * 22 Aug 92 pk (?) Fix "pwd hang bug" */ #ifndef lint -static char sccsid[] = "@(#)cd.c 5.2 (Berkeley) 3/13/91"; +/*static char sccsid[] = "from: @(#)cd.c 5.2 (Berkeley) 3/13/91";*/ +static char rcsid[] = "cd.c,v 1.5 1993/08/01 18:58:22 mycroft Exp"; #endif /* not lint */ /* diff --git a/bin/sh/dirent.c b/bin/sh/dirent.c index 521fcc627e..6c3242d1b7 100644 --- a/bin/sh/dirent.c +++ b/bin/sh/dirent.c @@ -35,7 +35,8 @@ */ #ifndef lint -static char sccsid[] = "@(#)dirent.c 5.1 (Berkeley) 3/7/91"; +/*static char sccsid[] = "from: @(#)dirent.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "dirent.c,v 1.4 1993/08/01 18:58:21 mycroft Exp"; #endif /* not lint */ #include "shell.h" /* definitions for pointer, NULL, DIRENT, and BSD */ diff --git a/bin/sh/errmsg.c b/bin/sh/errmsg.c index 21fd41e0c1..fb9dc84057 100644 --- a/bin/sh/errmsg.c +++ b/bin/sh/errmsg.c @@ -35,7 +35,8 @@ */ #ifndef lint -static char sccsid[] = "@(#)errmsg.c 5.1 (Berkeley) 3/7/91"; +/*static char sccsid[] = "from: @(#)errmsg.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "errmsg.c,v 1.4 1993/08/01 18:58:20 mycroft Exp"; #endif /* not lint */ #include "shell.h" diff --git a/bin/sh/errmsg.h b/bin/sh/errmsg.h index 446f414f1d..b5a43b980d 100644 --- a/bin/sh/errmsg.h +++ b/bin/sh/errmsg.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)errmsg.h 5.1 (Berkeley) 3/7/91 + * from: @(#)errmsg.h 5.1 (Berkeley) 3/7/91 + * errmsg.h,v 1.4 1993/08/01 18:58:33 mycroft Exp */ #define E_OPEN 01 diff --git a/bin/sh/error.c b/bin/sh/error.c index 2863a0da1a..430de28465 100644 --- a/bin/sh/error.c +++ b/bin/sh/error.c @@ -35,7 +35,8 @@ */ #ifndef lint -static char sccsid[] = "@(#)error.c 5.1 (Berkeley) 3/7/91"; +/*static char sccsid[] = "from: @(#)error.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "error.c,v 1.4 1993/08/01 18:58:19 mycroft Exp"; #endif /* not lint */ /* @@ -204,7 +205,7 @@ STATIC const struct errname errormsg[] = { #endif ENOMEM, ALL, "not enough memory", #ifdef ENOLINK - ENOLINK, ALL, "remote access failed" + ENOLINK, ALL, "remote access failed", #endif #ifdef EMULTIHOP EMULTIHOP, ALL, "remote access failed", diff --git a/bin/sh/error.h b/bin/sh/error.h index d8eca4fa29..d3cd46124b 100644 --- a/bin/sh/error.h +++ b/bin/sh/error.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)error.h 5.1 (Berkeley) 3/7/91 + * from: @(#)error.h 5.1 (Berkeley) 3/7/91 + * error.h,v 1.4 1993/08/01 18:58:32 mycroft Exp */ /* diff --git a/bin/sh/eval.c b/bin/sh/eval.c index 8c5a1bbe17..718876e95b 100644 --- a/bin/sh/eval.c +++ b/bin/sh/eval.c @@ -35,7 +35,8 @@ */ #ifndef lint -static char sccsid[] = "@(#)eval.c 5.3 (Berkeley) 4/12/91"; +/*static char sccsid[] = "from: @(#)eval.c 5.3 (Berkeley) 4/12/91";*/ +static char rcsid[] = "eval.c,v 1.5 1993/08/01 18:58:18 mycroft Exp"; #endif /* not lint */ /* @@ -899,6 +900,9 @@ returncmd(argc, argv) char **argv; { return ret; } +falsecmd(argc, argv) char **argv; { + return 1; +} truecmd(argc, argv) char **argv; { return 0; diff --git a/bin/sh/eval.h b/bin/sh/eval.h index 04499cb6c6..abc7bea580 100644 --- a/bin/sh/eval.h +++ b/bin/sh/eval.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)eval.h 5.2 (Berkeley) 4/12/91 + * from: @(#)eval.h 5.2 (Berkeley) 4/12/91 + * eval.h,v 1.4 1993/08/01 18:58:31 mycroft Exp */ extern char *commandname; /* currently executing command */ diff --git a/bin/sh/exec.c b/bin/sh/exec.c index 855a2d4c1c..6f427c1fa0 100644 --- a/bin/sh/exec.c +++ b/bin/sh/exec.c @@ -35,7 +35,8 @@ */ #ifndef lint -static char sccsid[] = "@(#)exec.c 5.2 (Berkeley) 3/13/91"; +/*static char sccsid[] = "from: @(#)exec.c 5.2 (Berkeley) 3/13/91";*/ +static char rcsid[] = "exec.c,v 1.5 1993/08/01 18:58:17 mycroft Exp"; #endif /* not lint */ /* @@ -67,7 +68,17 @@ static char sccsid[] = "@(#)exec.c 5.2 (Berkeley) 3/13/91"; #include #include #include +#include +#ifdef _POSIX_VERSION +#define _POSIX_SOURCE /* try to find NGROUPS_MAX */ +#include +#endif #include +#ifdef BSD +#undef BSD /* temporary, already defined in */ +#include +#include +#endif #define CMDTABLESIZE 31 /* should be prime */ @@ -92,6 +103,7 @@ STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ STATIC void tryexec(char *, char **, char **); STATIC void execinterp(char **, char **); STATIC void printentry(struct tblentry *); +STATIC int in_group(int gid); STATIC void clearcmdentry(int); STATIC struct tblentry *cmdlookup(char *, int); STATIC void delete_cmd_entry(void); @@ -99,6 +111,7 @@ STATIC void delete_cmd_entry(void); STATIC void tryexec(); STATIC void execinterp(); STATIC void printentry(); +STATIC int in_group(); STATIC void clearcmdentry(); STATIC struct tblentry *cmdlookup(); STATIC void delete_cmd_entry(); @@ -479,16 +492,38 @@ loop: stunalloc(fullname); goto success; } - if (statb.st_uid == geteuid()) { + /* XXX this is almost as bogus as using access() */ + if (geteuid() == 0) { + if ((statb.st_mode & 0111) == 0) + goto loop; + } else if (statb.st_uid == geteuid()) { if ((statb.st_mode & 0100) == 0) goto loop; - } else if (statb.st_gid == getegid()) { + } else if (in_group(statb.st_gid)) { if ((statb.st_mode & 010) == 0) goto loop; } else { - if ((statb.st_mode & 01) == 0) + if ((statb.st_mode & 01) == 0) { +#ifdef BSD + if ((statb.st_mode & 010) == 0) + goto loop; + /* Are you in this group too? */ + { + int group_list[NGROUPS]; + int ngroups, i; + + ngroups = getgroups(NGROUPS, group_list); + for (i = 0; i < ngroups; i++) + if (statb.st_gid == group_list[i]) + goto Found; + } +#endif goto loop; + } } +#ifdef BSD + Found: +#endif TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); INTOFF; cmdp = cmdlookup(name, 1); @@ -514,6 +549,47 @@ success: +STATIC int +in_group(gid) + int gid; + { +#ifdef _POSIX_VERSION +#ifdef __STDC__ + /* + * This bogus declararation is to force an error when + * someone fixes getgroups(). + */ + extern int getgroups(int ngroups, int *group_list); +#endif +#if NGROUPS_MAX != 0 + int group_list[NGROUPS_MAX]; +#else +#undef NGROUPS_MAX + size_t NGROUPS_MAX = sysconf(_SC_NGROUPS_MAX); + int *group_list = ckmalloc(NGROUPS_MAX); +#endif + int i; + int ngroups; + + ngroups = getgroups(NGROUPS_MAX, group_list); + for (i = 0; i < ngroups; i++) + if (gid == group_list[i]) { +#ifndef NGROUPS_MAX + ckfree(group_list); +#endif + return 1; + } +#ifndef NGROUPS_MAX + ckfree(group_list); +#endif + return 0; +#else /* ndef _POSIX_VERSION */ + return gid == getegid(); +#endif /* _POSIX_VERSION */ +} + + + /* * Search the table of builtin commands. */ diff --git a/bin/sh/exec.h b/bin/sh/exec.h index cd333dc94d..c9a570317b 100644 --- a/bin/sh/exec.h +++ b/bin/sh/exec.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)exec.h 5.1 (Berkeley) 3/7/91 + * from: @(#)exec.h 5.1 (Berkeley) 3/7/91 + * exec.h,v 1.4 1993/08/01 18:58:30 mycroft Exp */ /* values of cmdtype */ diff --git a/bin/sh/expand.c b/bin/sh/expand.c index a74e736162..1d5864d233 100644 --- a/bin/sh/expand.c +++ b/bin/sh/expand.c @@ -32,18 +32,11 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00168 - * -------------------- ----- ---------------------- - * - * 04 Jun 93 Jim Wilson Seven (7) fixes for misc bugs - * */ #ifndef lint -static char sccsid[] = "@(#)expand.c 5.1 (Berkeley) 3/7/91"; +/*static char sccsid[] = "from: @(#)expand.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "expand.c,v 1.5 1993/08/01 18:58:16 mycroft Exp"; #endif /* not lint */ /* diff --git a/bin/sh/expand.h b/bin/sh/expand.h index 65ecf8b098..9d8eacf9e2 100644 --- a/bin/sh/expand.h +++ b/bin/sh/expand.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)expand.h 5.1 (Berkeley) 3/7/91 + * from: @(#)expand.h 5.1 (Berkeley) 3/7/91 + * expand.h,v 1.4 1993/08/01 18:58:29 mycroft Exp */ struct strlist { diff --git a/bin/sh/funcs/cmv b/bin/sh/funcs/cmv index 0c6c4d3baa..ecd53084a1 100644 --- a/bin/sh/funcs/cmv +++ b/bin/sh/funcs/cmv @@ -33,6 +33,8 @@ # SUCH DAMAGE. # # @(#)cmv 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/funcs/cmv,v 1.3 1993/03/23 00:29:45 cgd Exp # Conditional move--don't replace an existing file. diff --git a/bin/sh/funcs/dirs b/bin/sh/funcs/dirs index a31d231235..e8a85fca74 100644 --- a/bin/sh/funcs/dirs +++ b/bin/sh/funcs/dirs @@ -33,6 +33,8 @@ # SUCH DAMAGE. # # @(#)dirs 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/funcs/dirs,v 1.3 1993/03/23 00:29:47 cgd Exp # pushd, popd, and dirs --- written by Chris Bertin # Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris diff --git a/bin/sh/funcs/kill b/bin/sh/funcs/kill index 9e643183f4..c58c3408bf 100644 --- a/bin/sh/funcs/kill +++ b/bin/sh/funcs/kill @@ -33,6 +33,8 @@ # SUCH DAMAGE. # # @(#)kill 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/funcs/kill,v 1.3 1993/03/23 00:29:49 cgd Exp # Convert job names to process ids and then run /bin/kill. diff --git a/bin/sh/funcs/login b/bin/sh/funcs/login index 6e24b437cc..a07badc75a 100644 --- a/bin/sh/funcs/login +++ b/bin/sh/funcs/login @@ -33,6 +33,8 @@ # SUCH DAMAGE. # # @(#)login 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/funcs/login,v 1.3 1993/03/23 00:29:51 cgd Exp # replaces the login builtin in the BSD shell login () exec login "$@" diff --git a/bin/sh/funcs/newgrp b/bin/sh/funcs/newgrp index 7980ff54d0..d7c2b92d72 100644 --- a/bin/sh/funcs/newgrp +++ b/bin/sh/funcs/newgrp @@ -33,5 +33,7 @@ # SUCH DAMAGE. # # @(#)newgrp 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/funcs/newgrp,v 1.3 1993/03/23 00:29:53 cgd Exp newgrp() exec newgrp "$@" diff --git a/bin/sh/funcs/popd b/bin/sh/funcs/popd index 4cecba9ee4..2ce4346ba5 100644 --- a/bin/sh/funcs/popd +++ b/bin/sh/funcs/popd @@ -33,6 +33,8 @@ # SUCH DAMAGE. # # @(#)popd 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/funcs/popd,v 1.3 1993/03/23 00:29:55 cgd Exp # pushd, popd, and dirs --- written by Chris Bertin # Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris diff --git a/bin/sh/funcs/pushd b/bin/sh/funcs/pushd index 4e8c48cf66..9b46b319d4 100644 --- a/bin/sh/funcs/pushd +++ b/bin/sh/funcs/pushd @@ -33,6 +33,8 @@ # SUCH DAMAGE. # # @(#)pushd 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/funcs/pushd,v 1.3 1993/03/23 00:29:58 cgd Exp # pushd, popd, and dirs --- written by Chris Bertin # Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris diff --git a/bin/sh/funcs/suspend b/bin/sh/funcs/suspend index 6c4e5795de..61b6562e3b 100644 --- a/bin/sh/funcs/suspend +++ b/bin/sh/funcs/suspend @@ -33,6 +33,8 @@ # SUCH DAMAGE. # # @(#)suspend 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/funcs/suspend,v 1.3 1993/03/23 00:30:00 cgd Exp suspend() { local - diff --git a/bin/sh/init.h b/bin/sh/init.h index 51efdb4b48..e520a10671 100644 --- a/bin/sh/init.h +++ b/bin/sh/init.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)init.h 5.1 (Berkeley) 3/7/91 + * from: @(#)init.h 5.1 (Berkeley) 3/7/91 + * init.h,v 1.4 1993/08/01 18:58:28 mycroft Exp */ #ifdef __STDC__ diff --git a/bin/sh/input.c b/bin/sh/input.c index ebcde9789f..0aea0209dd 100644 --- a/bin/sh/input.c +++ b/bin/sh/input.c @@ -35,7 +35,8 @@ */ #ifndef lint -static char sccsid[] = "@(#)input.c 5.4 (Berkeley) 7/1/91"; +/*static char sccsid[] = "from: @(#)input.c 5.4 (Berkeley) 7/1/91";*/ +static char rcsid[] = "input.c,v 1.4 1993/08/01 18:58:15 mycroft Exp"; #endif /* not lint */ /* diff --git a/bin/sh/input.h b/bin/sh/input.h index 848a1df044..90fcbdb54e 100644 --- a/bin/sh/input.h +++ b/bin/sh/input.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)input.h 5.1 (Berkeley) 3/7/91 + * from: @(#)input.h 5.1 (Berkeley) 3/7/91 + * input.h,v 1.4 1993/08/01 18:58:27 mycroft Exp */ /* PEOF (the end of file marker) is defined in syntax.h */ diff --git a/bin/sh/jobs.c b/bin/sh/jobs.c index 273dc5fe19..dfc4a27b31 100644 --- a/bin/sh/jobs.c +++ b/bin/sh/jobs.c @@ -32,18 +32,11 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00168 - * -------------------- ----- ---------------------- - * - * 04 Jun 93 Jim Wilson Seven (7) fixes for misc bugs - * */ #ifndef lint -static char sccsid[] = "@(#)jobs.c 5.1 (Berkeley) 3/7/91"; +/*static char sccsid[] = "from: @(#)jobs.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "jobs.c,v 1.7 1993/08/06 21:50:16 mycroft Exp"; #endif /* not lint */ #include "shell.h" @@ -57,7 +50,6 @@ static char sccsid[] = "@(#)jobs.c 5.1 (Berkeley) 3/7/91"; #include "jobs.h" #include "options.h" #include "trap.h" -#include "signames.h" #include "syntax.h" #include "input.h" #include "output.h" @@ -68,6 +60,7 @@ static char sccsid[] = "@(#)jobs.c 5.1 (Berkeley) 3/7/91"; #include #include #include +#include #ifdef BSD #include #include @@ -130,8 +123,8 @@ setjobctl(on) { return; } if (initialpgrp == -1) - initialpgrp = getpgrp(0); - else if (initialpgrp != getpgrp(0)) { + initialpgrp = getpgrp(); + else if (initialpgrp != getpgrp()) { killpg(initialpgrp, SIGTTIN); continue; } @@ -279,8 +272,8 @@ showjobs(change) { if ((i & 0xFF) == 0177) i >>= 8; #endif - if ((i & 0x7F) <= MAXSIG && sigmesg[i & 0x7F]) - scopy(sigmesg[i & 0x7F], s); + if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F]) + scopy(sys_siglist[i & 0x7F], s); else fmtstr(s, 64, "Signal %d", i & 0x7F); if (i & 0x80) @@ -633,7 +626,7 @@ waitforjob(jp) register struct job *jp; { #if JOBS - int mypgrp = getpgrp(0); + int mypgrp = getpgrp(); #endif int status; int st; @@ -743,8 +736,8 @@ dowait(block, job) if (status == SIGTSTP && rootshell && iflag) outfmt(out2, "%%%d ", job - jobtab + 1); #endif - if (status <= MAXSIG && sigmesg[status]) - out2str(sigmesg[status]); + if (status < NSIG && sys_siglist[status]) + out2str(sys_siglist[status]); else outfmt(out2, "Signal %d", status); if (core) @@ -816,7 +809,7 @@ waitproc(block, status) #endif if (block == 0) flags |= WNOHANG; - return wait3((union wait *)status, flags, (struct rusage *)NULL); + return wait3((int *)status, flags, (struct rusage *)NULL); #else #ifdef SYSV int (*save)(); diff --git a/bin/sh/jobs.h b/bin/sh/jobs.h index 1778fcf826..d5d5f34888 100644 --- a/bin/sh/jobs.h +++ b/bin/sh/jobs.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)jobs.h 5.1 (Berkeley) 3/7/91 + * from: @(#)jobs.h 5.1 (Berkeley) 3/7/91 + * jobs.h,v 1.4 1993/08/01 18:58:26 mycroft Exp */ /* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ diff --git a/bin/sh/machdep.h b/bin/sh/machdep.h index e4ee3c9445..1696c87a3b 100644 --- a/bin/sh/machdep.h +++ b/bin/sh/machdep.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)machdep.h 5.1 (Berkeley) 3/7/91 + * from: @(#)machdep.h 5.1 (Berkeley) 3/7/91 + * machdep.h,v 1.4 1993/08/01 18:58:26 mycroft Exp */ /* diff --git a/bin/sh/mail.c b/bin/sh/mail.c index 2e2bf1609d..18d151ff63 100644 --- a/bin/sh/mail.c +++ b/bin/sh/mail.c @@ -35,7 +35,8 @@ */ #ifndef lint -static char sccsid[] = "@(#)mail.c 5.1 (Berkeley) 3/7/91"; +/*static char sccsid[] = "from: @(#)mail.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "mail.c,v 1.4 1993/08/01 18:58:13 mycroft Exp"; #endif /* not lint */ /* diff --git a/bin/sh/mail.h b/bin/sh/mail.h index 8280791e81..64efe4e2bf 100644 --- a/bin/sh/mail.h +++ b/bin/sh/mail.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)mail.h 5.1 (Berkeley) 3/7/91 + * from: @(#)mail.h 5.1 (Berkeley) 3/7/91 + * mail.h,v 1.4 1993/08/01 18:58:25 mycroft Exp */ #ifdef __STDC__ diff --git a/bin/sh/main.c b/bin/sh/main.c index 236f4cc002..ec6f325591 100644 --- a/bin/sh/main.c +++ b/bin/sh/main.c @@ -41,7 +41,8 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)main.c 5.2 (Berkeley) 3/13/91"; +/*static char sccsid[] = "from: @(#)main.c 5.2 (Berkeley) 3/13/91";*/ +static char rcsid[] = "main.c,v 1.4 1993/08/01 18:58:12 mycroft Exp"; #endif /* not lint */ #include diff --git a/bin/sh/main.h b/bin/sh/main.h index c5fda20ce0..4aa0aa3121 100644 --- a/bin/sh/main.h +++ b/bin/sh/main.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)main.h 5.1 (Berkeley) 3/7/91 + * from: @(#)main.h 5.1 (Berkeley) 3/7/91 + * main.h,v 1.4 1993/08/01 18:58:24 mycroft Exp */ extern int rootpid; /* pid of main shell */ diff --git a/bin/sh/memalloc.c b/bin/sh/memalloc.c index 31fcd3a3a3..caae59110b 100644 --- a/bin/sh/memalloc.c +++ b/bin/sh/memalloc.c @@ -35,7 +35,8 @@ */ #ifndef lint -static char sccsid[] = "@(#)memalloc.c 5.2 (Berkeley) 3/13/91"; +/*static char sccsid[] = "from: @(#)memalloc.c 5.2 (Berkeley) 3/13/91";*/ +static char rcsid[] = "memalloc.c,v 1.4 1993/08/01 18:58:10 mycroft Exp"; #endif /* not lint */ #include "shell.h" diff --git a/bin/sh/memalloc.h b/bin/sh/memalloc.h index 4c663ae201..841220cf3b 100644 --- a/bin/sh/memalloc.h +++ b/bin/sh/memalloc.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)memalloc.h 5.1 (Berkeley) 3/7/91 + * from: @(#)memalloc.h 5.1 (Berkeley) 3/7/91 + * memalloc.h,v 1.4 1993/08/01 18:58:23 mycroft Exp */ struct stackmark { diff --git a/bin/sh/miscbltin.c b/bin/sh/miscbltin.c index 23063722e3..b14da674a1 100644 --- a/bin/sh/miscbltin.c +++ b/bin/sh/miscbltin.c @@ -35,13 +35,16 @@ */ #ifndef lint -static char sccsid[] = "@(#)miscbltin.c 5.2 (Berkeley) 3/13/91"; +/*static char sccsid[] = "from: @(#)miscbltin.c 5.2 (Berkeley) 3/13/91";*/ +static char rcsid[] = "miscbltin.c,v 1.5 1993/08/01 18:57:56 mycroft Exp"; #endif /* not lint */ /* * Miscelaneous builtins. */ +#include +#include #include "shell.h" #include "options.h" #include "var.h" @@ -143,24 +146,74 @@ readcmd(argc, argv) char **argv; { umaskcmd(argc, argv) char **argv; { + extern void *setmode(); + extern mode_t getmode(); + char *ap; int mask; - char *p; int i; + int symbolic_mode = 0; + + while ((i = nextopt("S")) != '\0') { + symbolic_mode = 1; + } + + INTOFF; + mask = umask(0); + umask(mask); + INTON; + + if ((ap = *argptr) == NULL) { + if (symbolic_mode) { + char u[4], g[4], o[4]; + + i = 0; + if ((mask & S_IRUSR) == 0) + u[i++] = 'r'; + if ((mask & S_IWUSR) == 0) + u[i++] = 'w'; + if ((mask & S_IXUSR) == 0) + u[i++] = 'x'; + u[i] = '\0'; + + i = 0; + if ((mask & S_IRGRP) == 0) + g[i++] = 'r'; + if ((mask & S_IWGRP) == 0) + g[i++] = 'w'; + if ((mask & S_IXGRP) == 0) + g[i++] = 'x'; + g[i] = '\0'; - if ((p = argv[1]) == NULL) { - INTOFF; - mask = umask(0); - umask(mask); - INTON; - out1fmt("%.4o\n", mask); /* %#o might be better */ + i = 0; + if ((mask & S_IROTH) == 0) + o[i++] = 'r'; + if ((mask & S_IWOTH) == 0) + o[i++] = 'w'; + if ((mask & S_IXOTH) == 0) + o[i++] = 'x'; + o[i] = '\0'; + + out1fmt("u=%s,g=%s,o=%s\n", u, g, o); + } else { + out1fmt("%.4o\n", mask); + } } else { - mask = 0; - do { - if ((unsigned)(i = *p - '0') >= 8) - error("Illegal number: %s", argv[1]); - mask = (mask << 3) + i; - } while (*++p != '\0'); - umask(mask); + if (isdigit(*ap)) { + mask = 0; + do { + if (*ap >= '8' || *ap < '0') + error("Illegal number: %s", argv[1]); + mask = (mask << 3) + (*ap - '0'); + } while (*++ap != '\0'); + umask(mask); + } else { + void *set; + if ((set = setmode (ap)) == 0) + error("Illegal number: %s", ap); + + mask = getmode (set, ~mask & 0777); + umask(~mask & 0777); + } } return 0; } diff --git a/bin/sh/mkbuiltins b/bin/sh/mkbuiltins index 4192de1839..42d72b7328 100644 --- a/bin/sh/mkbuiltins +++ b/bin/sh/mkbuiltins @@ -35,13 +35,16 @@ # SUCH DAMAGE. # # @(#)mkbuiltins 5.2 (Berkeley) 3/8/91 +# +# /b/source/CVS/src/bin/sh/mkbuiltins,v 1.4 1993/04/18 17:37:21 mycroft Exp temp=/tmp/ka$$ havejobs=0 if grep '^#define JOBS[ ]*1' shell.h > /dev/null then havejobs=1 fi -exec > obj/builtins.c +if [ -d obj ]; then objdir=obj; else objdir=.; fi +exec > ${objdir}/builtins.c cat <<\! /* * This file was generated by the mkbuiltins program. @@ -66,7 +69,7 @@ awk '{ for (i = 2 ; i <= NF ; i++) { echo ' NULL, 0 };' -exec > obj/builtins.h +exec > ${objdir}/builtins.h cat <<\! /* * This file was generated by the mkbuiltins program. diff --git a/bin/sh/mkinit.c b/bin/sh/mkinit.c index ed235ee141..02e9491cb9 100644 --- a/bin/sh/mkinit.c +++ b/bin/sh/mkinit.c @@ -41,7 +41,8 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)mkinit.c 5.3 (Berkeley) 3/13/91"; +/*static char sccsid[] = "from: @(#)mkinit.c 5.3 (Berkeley) 3/13/91";*/ +static char rcsid[] = "mkinit.c,v 1.4 1993/08/01 18:58:09 mycroft Exp"; #endif /* not lint */ /* diff --git a/bin/sh/mknodes.c b/bin/sh/mknodes.c index d339a328ba..53c8508ee1 100644 --- a/bin/sh/mknodes.c +++ b/bin/sh/mknodes.c @@ -41,7 +41,8 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)mknodes.c 5.1 (Berkeley) 3/7/91"; +/*static char sccsid[] = "from: @(#)mknodes.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "mknodes.c,v 1.4 1993/08/01 18:58:08 mycroft Exp"; #endif /* not lint */ /* @@ -112,6 +113,7 @@ main(argc, argv) parsenode(); } output(argv[2]); + return 0; } diff --git a/bin/sh/mksignames.c b/bin/sh/mksignames.c index 2926c33fcc..b308e03bdb 100644 --- a/bin/sh/mksignames.c +++ b/bin/sh/mksignames.c @@ -41,7 +41,8 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)mksignames.c 5.1 (Berkeley) 3/7/91"; +/*static char sccsid[] = "from: @(#)mksignames.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "mksignames.c,v 1.4 1993/08/01 18:58:07 mycroft Exp"; #endif /* not lint */ /* diff --git a/bin/sh/mksyntax.c b/bin/sh/mksyntax.c index 5b26fc004d..e0301d66d7 100644 --- a/bin/sh/mksyntax.c +++ b/bin/sh/mksyntax.c @@ -41,7 +41,8 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)mksyntax.c 5.2 (Berkeley) 3/8/91"; +/*static char sccsid[] = "from: @(#)mksyntax.c 5.2 (Berkeley) 3/8/91";*/ +static char rcsid[] = "mksyntax.c,v 1.4 1993/08/01 18:58:06 mycroft Exp"; #endif /* not lint */ /* diff --git a/bin/sh/mktokens b/bin/sh/mktokens index 8b67b53754..0e36b258c2 100644 --- a/bin/sh/mktokens +++ b/bin/sh/mktokens @@ -35,6 +35,8 @@ # SUCH DAMAGE. # # @(#)mktokens 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/mktokens,v 1.3 1993/03/23 00:28:45 cgd Exp # The following is a list of tokens. The second column is nonzero if the # token marks the end of a list. The third column is the name to print in diff --git a/bin/sh/mt b/bin/sh/mt index 8ea21ecf17..f07a32e28d 100644 --- a/bin/sh/mt +++ b/bin/sh/mt @@ -35,6 +35,8 @@ # SUCH DAMAGE. # # @(#)mktokens 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/mt,v 1.3 1993/03/23 00:28:48 cgd Exp # The following is a list of tokens. The second column is nonzero if the # token marks the end of a list. The third column is the name to print in diff --git a/bin/sh/mystring.c b/bin/sh/mystring.c index 42635cf127..eb9d1db37d 100644 --- a/bin/sh/mystring.c +++ b/bin/sh/mystring.c @@ -35,7 +35,8 @@ */ #ifndef lint -static char sccsid[] = "@(#)mystring.c 5.1 (Berkeley) 3/7/91"; +/*static char sccsid[] = "from: @(#)mystring.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "mystring.c,v 1.4 1993/08/01 18:58:05 mycroft Exp"; #endif /* not lint */ /* diff --git a/bin/sh/mystring.h b/bin/sh/mystring.h index d37ca152a9..9620e7ac72 100644 --- a/bin/sh/mystring.h +++ b/bin/sh/mystring.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)mystring.h 5.1 (Berkeley) 3/7/91 + * from: @(#)mystring.h 5.1 (Berkeley) 3/7/91 + * mystring.h,v 1.4 1993/08/01 18:58:39 mycroft Exp */ #ifndef SYSV diff --git a/bin/sh/nodes.c.pat b/bin/sh/nodes.c.pat index 6ffec71575..5283a81114 100644 --- a/bin/sh/nodes.c.pat +++ b/bin/sh/nodes.c.pat @@ -34,6 +34,8 @@ * SUCH DAMAGE. * * @(#)nodes.c.pat 5.2 (Berkeley) 3/8/91 + * + * /b/source/CVS/src/bin/sh/nodes.c.pat,v 1.3 1993/03/23 00:28:55 cgd Exp */ /* diff --git a/bin/sh/nodetypes b/bin/sh/nodetypes index b7c680c7ec..6226176ce6 100644 --- a/bin/sh/nodetypes +++ b/bin/sh/nodetypes @@ -35,6 +35,8 @@ # SUCH DAMAGE. # # @(#)nodetypes 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/nodetypes,v 1.3 1993/03/23 00:28:58 cgd Exp # This file describes the nodes used in parse trees. Unindented lines # contain a node type followed by a structure tag. Subsequent indented diff --git a/bin/sh/options.c b/bin/sh/options.c index 1325db7461..560e433c4e 100644 --- a/bin/sh/options.c +++ b/bin/sh/options.c @@ -35,7 +35,8 @@ */ #ifndef lint -static char sccsid[] = "@(#)options.c 5.2 (Berkeley) 3/13/91"; +/*static char sccsid[] = "from: @(#)options.c 5.2 (Berkeley) 3/13/91";*/ +static char rcsid[] = "options.c,v 1.4 1993/08/01 18:58:04 mycroft Exp"; #endif /* not lint */ #include "shell.h" diff --git a/bin/sh/options.h b/bin/sh/options.h index 454da361ca..c7c94a32d3 100644 --- a/bin/sh/options.h +++ b/bin/sh/options.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)options.h 5.1 (Berkeley) 3/7/91 + * from: @(#)options.h 5.1 (Berkeley) 3/7/91 + * options.h,v 1.4 1993/08/01 18:58:39 mycroft Exp */ struct shparam { diff --git a/bin/sh/output.c b/bin/sh/output.c index c67a91ca00..af024baf4c 100644 --- a/bin/sh/output.c +++ b/bin/sh/output.c @@ -35,7 +35,8 @@ */ #ifndef lint -static char sccsid[] = "@(#)output.c 5.1 (Berkeley) 3/7/91"; +/*static char sccsid[] = "from: @(#)output.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "output.c,v 1.4 1993/08/01 18:58:03 mycroft Exp"; #endif /* not lint */ /* @@ -70,7 +71,7 @@ static char sccsid[] = "@(#)output.c 5.1 (Berkeley) 3/7/91"; struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0}; -struct output errout = {NULL, 0, NULL, 100, 2, 0};; +struct output errout = {NULL, 0, NULL, 100, 2, 0}; struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0}; struct output *out1 = &output; struct output *out2 = &errout; diff --git a/bin/sh/output.h b/bin/sh/output.h index b8b814c3e2..c633fe9be7 100644 --- a/bin/sh/output.h +++ b/bin/sh/output.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)output.h 5.1 (Berkeley) 3/7/91 + * from: @(#)output.h 5.1 (Berkeley) 3/7/91 + * output.h,v 1.4 1993/08/01 18:58:38 mycroft Exp */ #ifndef OUTPUT_INCL diff --git a/bin/sh/parser.c b/bin/sh/parser.c index 90974170fa..dc0e046ea1 100644 --- a/bin/sh/parser.c +++ b/bin/sh/parser.c @@ -32,18 +32,11 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00168 - * -------------------- ----- ---------------------- - * - * 04 Jun 93 Jim Wilson Seven (7) fixes for misc bugs - * */ #ifndef lint -static char sccsid[] = "@(#)parser.c 5.3 (Berkeley) 4/12/91"; +/*static char sccsid[] = "from: @(#)parser.c 5.3 (Berkeley) 4/12/91";*/ +static char rcsid[] = "parser.c,v 1.8 1993/08/01 18:58:02 mycroft Exp"; #endif /* not lint */ #include "shell.h" @@ -638,7 +631,7 @@ readtoken() { * check for keywords */ if (t == TWORD && !quoteflag) { - register char **pp; + register char *const *pp; for (pp = parsekwd; *pp; pp++) { if (**pp == *wordtext && equal(*pp, wordtext)) { @@ -1083,7 +1076,7 @@ parsebackq: { ckfree(str); parsebackquote = 0; handler = savehandler; - longjmp(handler, 1); + longjmp(handler->loc, 1); } INTOFF; str = NULL; @@ -1132,9 +1125,12 @@ parsebackq: { if (!oldstyle && (readtoken() != TRP)) synexpect(TRP); (*nlpp)->n = n; - /* Start reading from old file again. */ - if (oldstyle) + /* Start reading from old file again, and clear tokpushback since + any pushed back token from the string is no longer relevant. */ + if (oldstyle) { popfile(); + tokpushback = 0; + } while (stackblocksize() <= savelen) growstackblock(); STARTSTACKSTR(out); @@ -1186,7 +1182,7 @@ attyline() { if (exception == EXERROR) out2str("\033]D\n"); handler = savehandler; - longjmp(handler, 1); + longjmp(handler->loc, 1); } savehandler = handler; handler = &jmploc; diff --git a/bin/sh/parser.h b/bin/sh/parser.h index edb130a51b..a4dd897f4d 100644 --- a/bin/sh/parser.h +++ b/bin/sh/parser.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)parser.h 5.1 (Berkeley) 3/7/91 + * from: @(#)parser.h 5.1 (Berkeley) 3/7/91 + * parser.h,v 1.4 1993/08/01 18:58:37 mycroft Exp */ /* control characters in argument strings */ diff --git a/bin/sh/redir.c b/bin/sh/redir.c index f58d491be3..1d15bd6b68 100644 --- a/bin/sh/redir.c +++ b/bin/sh/redir.c @@ -32,18 +32,11 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00168 - * -------------------- ----- ---------------------- - * - * 04 Jun 93 Jim Wilson Seven (7) fixes for misc bugs - * */ #ifndef lint -static char sccsid[] = "@(#)redir.c 5.1 (Berkeley) 3/7/91"; +/*static char sccsid[] = "from: @(#)redir.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "redir.c,v 1.5 1993/08/01 18:58:01 mycroft Exp"; #endif /* not lint */ /* diff --git a/bin/sh/redir.h b/bin/sh/redir.h index 99c995f533..9f1908fcb6 100644 --- a/bin/sh/redir.h +++ b/bin/sh/redir.h @@ -33,15 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)redir.h 5.1 (Berkeley) 3/7/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00168 - * -------------------- ----- ---------------------- - * - * 04 Jun 93 Jim Wilson Seven (7) fixes for misc bugs - * + * from: @(#)redir.h 5.1 (Berkeley) 3/7/91 + * redir.h,v 1.5 1993/08/01 18:58:36 mycroft Exp */ /* flags passed to redirect */ diff --git a/bin/sh/sh.1 b/bin/sh/sh.1 index 5cec2f2cfc..415aa0e42f 100644 --- a/bin/sh/sh.1 +++ b/bin/sh/sh.1 @@ -32,14 +32,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)sh.1 5.1 (Berkeley) 3/7/91 -.\" -.\" PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -.\" -------------------- ----- ---------------------- -.\" CURRENT PATCH LEVEL: 1 00130 -.\" -------------------- ----- ---------------------- -.\" -.\" 06 Apr 93 Sascha Wildner Misc small fixes +.\" from: @(#)sh.1 5.1 (Berkeley) 3/7/91 +.\" sh.1,v 1.6 1993/08/01 07:58:14 mycroft Exp .\" .TH SH 1 "March 7, 1991" .UC 7 @@ -261,7 +255,7 @@ or loops. .I Continue continues with the next iteration of the -.IRnum'th +.IR num'th innermost loop. These are implemented as builtin commands. .PP diff --git a/bin/sh/shell.h b/bin/sh/shell.h index 4c07fecb3d..740c941d3f 100644 --- a/bin/sh/shell.h +++ b/bin/sh/shell.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)shell.h 5.4 (Berkeley) 4/12/91 + * from: @(#)shell.h 5.4 (Berkeley) 4/12/91 + * shell.h,v 1.4 1993/08/01 18:58:35 mycroft Exp */ /* diff --git a/bin/sh/show.c b/bin/sh/show.c index d4458d2ac3..edcd5f3a19 100644 --- a/bin/sh/show.c +++ b/bin/sh/show.c @@ -35,7 +35,8 @@ */ #ifndef lint -static char sccsid[] = "@(#)show.c 5.2 (Berkeley) 4/12/91"; +/*static char sccsid[] = "from: @(#)show.c 5.2 (Berkeley) 4/12/91";*/ +static char rcsid[] = "show.c,v 1.4 1993/08/01 18:58:00 mycroft Exp"; #endif /* not lint */ #include diff --git a/bin/sh/trap.c b/bin/sh/trap.c index 68104cebe0..f1a4e82d24 100644 --- a/bin/sh/trap.c +++ b/bin/sh/trap.c @@ -35,7 +35,8 @@ */ #ifndef lint -static char sccsid[] = "@(#)trap.c 5.2 (Berkeley) 4/12/91"; +/*static char sccsid[] = "from: @(#)trap.c 5.2 (Berkeley) 4/12/91";*/ +static char rcsid[] = "trap.c,v 1.5 1993/08/06 21:50:18 mycroft Exp"; #endif /* not lint */ #include "shell.h" @@ -45,7 +46,6 @@ static char sccsid[] = "@(#)trap.c 5.2 (Berkeley) 4/12/91"; #include "jobs.h" #include "options.h" #include "syntax.h" -#include "signames.h" #include "output.h" #include "memalloc.h" #include "error.h" @@ -68,10 +68,10 @@ static char sccsid[] = "@(#)trap.c 5.2 (Berkeley) 4/12/91"; extern char nullstr[1]; /* null string */ -char *trap[MAXSIG+1]; /* trap handler commands */ -MKINIT char sigmode[MAXSIG]; /* current value of signal */ -char gotsig[MAXSIG]; /* indicates specified signal received */ -int pendingsigs; /* indicates some signal received */ +char *trap[NSIG]; /* trap handler commands */ +MKINIT char sigmode[NSIG]; /* current value of signal */ +char gotsig[NSIG]; /* indicates specified signal received */ +int pendingsigs; /* indicates some signal received */ /* * The trap builtin. @@ -83,7 +83,7 @@ trapcmd(argc, argv) char **argv; { int signo; if (argc <= 1) { - for (signo = 0 ; signo <= MAXSIG ; signo++) { + for (signo = 0 ; signo < NSIG ; signo++) { if (trap[signo] != NULL) out1fmt("%d: %s\n", signo, trap[signo]); } @@ -95,7 +95,7 @@ trapcmd(argc, argv) char **argv; { else action = *ap++; while (*ap) { - if ((signo = number(*ap)) < 0 || signo > MAXSIG) + if ((signo = number(*ap)) < 0 || signo >= NSIG) error("%s: bad trap", *ap); INTOFF; if (action) @@ -121,7 +121,7 @@ void clear_traps() { char **tp; - for (tp = trap ; tp <= &trap[MAXSIG] ; tp++) { + for (tp = trap ; tp < &trap[NSIG] ; tp++) { if (*tp && **tp) { /* trap not NULL or SIG_IGN */ INTOFF; ckfree(*tp); @@ -182,7 +182,7 @@ setsignal(signo) { #endif } } - t = &sigmode[signo - 1]; + t = &sigmode[signo]; if (*t == 0) { /* current setting unknown */ /* * There is a race condition here if action is not S_IGN. @@ -214,22 +214,22 @@ setsignal(signo) { void ignoresig(signo) { - if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) { + if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) { signal(signo, SIG_IGN); } - sigmode[signo - 1] = S_HARD_IGN; + sigmode[signo] = S_HARD_IGN; } #ifdef mkinit -INCLUDE "signames.h" +INCLUDE INCLUDE "trap.h" SHELLPROC { char *sm; clear_traps(); - for (sm = sigmode ; sm < sigmode + MAXSIG ; sm++) { + for (sm = sigmode ; sm < sigmode + NSIG ; sm++) { if (*sm == S_IGN) *sm = S_HARD_IGN; } @@ -249,7 +249,7 @@ onsig(signo) { onint(); return; } - gotsig[signo - 1] = 1; + gotsig[signo] = 1; pendingsigs++; } @@ -267,12 +267,12 @@ dotrap() { for (;;) { for (i = 1 ; ; i++) { - if (gotsig[i - 1]) - break; - if (i >= MAXSIG) + if (i >= NSIG) goto done; + if (gotsig[i]) + break; } - gotsig[i - 1] = 0; + gotsig[i] = 0; savestatus=exitstatus; evalstring(trap[i]); exitstatus=savestatus; diff --git a/bin/sh/trap.h b/bin/sh/trap.h index afa984cb06..16e8d87406 100644 --- a/bin/sh/trap.h +++ b/bin/sh/trap.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)trap.h 5.1 (Berkeley) 3/7/91 + * from: @(#)trap.h 5.1 (Berkeley) 3/7/91 + * trap.h,v 1.4 1993/08/01 18:58:34 mycroft Exp */ extern int pendingsigs; diff --git a/bin/sh/var.c b/bin/sh/var.c index fc6603644c..8adbf5df16 100644 --- a/bin/sh/var.c +++ b/bin/sh/var.c @@ -35,7 +35,8 @@ */ #ifndef lint -static char sccsid[] = "@(#)var.c 5.3 (Berkeley) 4/12/91"; +/*static char sccsid[] = "from: @(#)var.c 5.3 (Berkeley) 4/12/91";*/ +static char rcsid[] = "var.c,v 1.4 1993/08/01 18:57:58 mycroft Exp"; #endif /* not lint */ /* diff --git a/bin/sh/var.h b/bin/sh/var.h index 3c63c5c456..b2c74172d8 100644 --- a/bin/sh/var.h +++ b/bin/sh/var.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)var.h 5.1 (Berkeley) 3/7/91 + * from: @(#)var.h 5.1 (Berkeley) 3/7/91 + * var.h,v 1.4 1993/08/01 18:58:33 mycroft Exp */ /* diff --git a/bin/stty/key.c b/bin/stty/key.c index 8ec7f96d89..6a7aeca935 100644 --- a/bin/stty/key.c +++ b/bin/stty/key.c @@ -76,7 +76,7 @@ static struct key { "dec", f_dec, 0, "everything", f_everything, 0, "extproc", f_extproc, F_OFFOK, - "ispeed", f_ispeed, 0, + "ispeed", f_ispeed, F_NEEDARG, "new", f_tty, 0, "nl", f_nl, F_OFFOK, "old", f_tty, 0, diff --git a/bin/stty/stty.c b/bin/stty/stty.c index 36742316ac..99765e386f 100644 --- a/bin/stty/stty.c +++ b/bin/stty/stty.c @@ -135,6 +135,7 @@ args: argc -= optind; if (!strncmp(*argv, "gfmt1", sizeof("gfmt1") - 1)) { gread(&i.t, *argv + sizeof("gfmt1") - 1); + i.set = 1; continue; } diff --git a/bin/sync/Makefile b/bin/sync/Makefile index 1c3b6ecbeb..792a801801 100644 --- a/bin/sync/Makefile +++ b/bin/sync/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.2 (Berkeley) 5/11/90 PROG= sync -MAN8= sync.0 +MAN8= sync.8 .include diff --git a/bin/test/Makefile b/bin/test/Makefile index 035270cd6c..34d81d2a88 100644 --- a/bin/test/Makefile +++ b/bin/test/Makefile @@ -1,9 +1,14 @@ -PROG = test -CFLAGS += -DSTANDALONE -NOMAN=noman +# @(#)Makefile 5.1 (Berkeley) 6/8/92 -afterinstall: - rm -f /bin/[ - ln /bin/test /bin/[ +PROG= test +SRCS= test.c operators.c +CFLAGS+=-I. +LINKS= ${BINDIR}/test ${BINDIR}/[ +MLINKS= test.1 '[.1' + + +# use this rule to if you update binary_ops, or unary_ops +make_op: + sh ${.CURDIR}/mkops .include diff --git a/bin/test/TEST.csh b/bin/test/TEST.csh new file mode 100644 index 0000000000..b3c8f1a32d --- /dev/null +++ b/bin/test/TEST.csh @@ -0,0 +1,147 @@ +# @(#)TEST.csh 5.1 (Berkeley) 6/8/92 + +alias t './test \!*; echo $status' +#alias t 'test \!*; echo $status' + +echo 't -b /dev/ttyp2' +t -b /dev/ttyp2 +echo 't -b /dev/jb1a' +t -b /dev/jb1a + +echo 't -c test.c' +t -c test.c +echo 't -c /dev/tty' +t -c /dev/tty + +echo 't -d test.c' +t -d test.c +echo 't -d /etc' +t -d /etc + +echo 't -e noexist' +t -e noexist +echo 't -e test.c' +t -e test.c + +echo 't -f noexist' +t -f noexist +echo 't -f /dev/tty' +t -f /dev/tty +echo 't -f test.c' +t -f test.c + +echo 't -g test.c' +t -g test.c +echo 't -g /bin/ps' +t -g /bin/ps + +echo 't -n ""' +t -n "" +echo 't -n "hello"' +t -n "hello" + +echo 't -p test.c' +t -p test.c + +echo 't -r noexist' +t -r noexist +echo 't -r /etc/master.passwd' +t -r /etc/master.passwd +echo 't -r test.c' +t -r test.c + +echo 't -s noexist' +t -s noexist +echo 't -s /dev/null' +t -s /dev/null +echo 't -s test.c' +t -s test.c + +echo 't -t 20' +t -t 20 +echo 't -t 0' +t -t 0 + +echo 't -u test.c' +t -u test.c +echo 't -u /bin/rcp' +t -u /bin/rcp + +echo 't -w noexist' +t -w noexist +echo 't -w /etc/master.passwd' +t -w /etc/master.passwd +echo 't -w /dev/null' +t -w /dev/null + +echo 't -x noexist' +t -x noexist +echo 't -x /bin/ps' +t -x /bin/ps +echo 't -x /etc/motd' +t -x /etc/motd + +echo 't -z ""' +t -z "" +echo 't -z "foo"' +t -z "foo" + +echo 't "foo"' +t "foo" +echo 't ""' +t "" + +echo 't "hello" = "hello"' +t "hello" = "hello" +echo 't "hello" = "goodbye"' +t "hello" = "goodbye" + +echo 't "hello" != "hello"' +t "hello" != "hello" +echo 't "hello" != "goodbye"' +t "hello" != "goodbye" + +echo 't 200 -eq 200' +t 200 -eq 200 +echo 't 34 -eq 222' +t 34 -eq 222 + +echo 't 200 -ne 200' +t 200 -ne 200 +echo 't 34 -ne 222' +t 34 -ne 222 + +echo 't 200 -gt 200' +t 200 -gt 200 +echo 't 340 -gt 222' +t 340 -gt 222 + +echo 't 200 -ge 200' +t 200 -ge 200 +echo 't 34 -ge 222' +t 34 -ge 222 + +echo 't 200 -lt 200' +t 200 -lt 200 +echo 't 34 -lt 222' +t 34 -lt 222 + +echo 't 200 -le 200' +t 200 -le 200 +echo 't 340 -le 222' +t 340 -le 222 + +echo 't 700 -le 1000 -a -n "1" -a "20" = "20"' +t 700 -le 1000 -a -n "1" -a "20" = "20" +echo 't ! \( 700 -le 1000 -a -n "1" -a "20" = "20" \)' +t ! \( 700 -le 1000 -a -n "1" -a "20" = "20" \) + +echo 't foo -a ""' +t foo -a "" +echo 't "" -a foo' +t "" -a foo +echo 't "" -a ""' +t "" -a "" +echo 't "" -o ""' +t "" -o "" + diff --git a/bin/test/binary_op b/bin/test/binary_op new file mode 100644 index 0000000000..d49b4bc10f --- /dev/null +++ b/bin/test/binary_op @@ -0,0 +1,56 @@ +# Copyright (c) 1988 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)binary_op 1.2 (Berkeley) 6/3/92 +# + + +# +# List of binary operators used by test +# + + +OR1 -o 1 +OR2 | 1 +AND1 -a 2 +AND2 & 2 +STREQ = 4 OP_STRING +STRNE != 4 OP_STRING +EQ -eq 4 OP_INT +NE -ne 4 OP_INT +GT -gt 4 OP_INT +LT -lt 4 OP_INT +LE -le 4 OP_INT +GE -ge 4 OP_INT + diff --git a/bin/test/mkops b/bin/test/mkops new file mode 100644 index 0000000000..d37696f828 --- /dev/null +++ b/bin/test/mkops @@ -0,0 +1,84 @@ +# Copyright (c) 1988 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)mkops 1.3 (Berkeley) 6/3/92 +# + + +#exec > operators.h +( +awk '/^[^#]/ {printf "#define %s %d\n", $1, n++}' unary_op binary_op +awk '/^[^#]/ {n++} +END {printf "\n#define FIRST_BINARY_OP %d\n", n} +' unary_op +echo ' +#define OP_INT 1 /* arguments to operator are integer */ +#define OP_STRING 2 /* arguments to operator are string */ +#define OP_FILE 3 /* argument is a file name */ + +extern char *const unary_op[]; +extern char *const binary_op[]; +extern const char op_priority[]; +extern const char op_argflag[];' +) >operators.h + +#exec > operators.c +( +echo '/* + * Operators used in the test command. + */ + +#include +#include "operators.h" + +char *const unary_op[] = {' +awk '/^[^#]/ {printf " \"%s\",\n", $2}' unary_op +echo ' NULL +}; + +char *const binary_op[] = {' +awk '/^[^#]/ {printf " \"%s\",\n", $2}' binary_op +echo ' NULL +}; + +const char op_priority[] = {' +awk '/^[^#]/ {printf " %s,\n", $3}' unary_op binary_op +echo '}; + +const char op_argflag[] = {' +awk '/^[^#]/ {if (length($4) > 0) printf " %s,\n", $4 + else printf " 0,\n"} +' unary_op binary_op +echo '};' +) >operators.c diff --git a/bin/test/operators.c b/bin/test/operators.c new file mode 100644 index 0000000000..6700770fac --- /dev/null +++ b/bin/test/operators.c @@ -0,0 +1,115 @@ +/* + * Operators used in the test command. + */ + +#include +#include "operators.h" + +char *const unary_op[] = { + "!", + "-b", + "-c", + "-d", + "-e", + "-f", + "-g", + "-k", + "-n", + "-p", + "-r", + "-s", + "-t", + "-u", + "-w", + "-x", + "-z", + NULL +}; + +char *const binary_op[] = { + "-o", + "|", + "-a", + "&", + "=", + "!=", + "-eq", + "-ne", + "-gt", + "-lt", + "-le", + "-ge", + NULL +}; + +char *const andor_op[] = { + "-o", + "|", + "-a", + "&", + NULL +}; + +const char op_priority[] = { + 3, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 1, + 1, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, +}; + +const char op_argflag[] = { + 0, + OP_FILE, + OP_FILE, + OP_FILE, + OP_FILE, + OP_FILE, + OP_FILE, + OP_FILE, + OP_STRING, + OP_FILE, + OP_FILE, + OP_FILE, + OP_INT, + OP_FILE, + OP_FILE, + OP_FILE, + OP_STRING, + 0, + 0, + 0, + 0, + OP_STRING, + OP_STRING, + OP_INT, + OP_INT, + OP_INT, + OP_INT, + OP_INT, + OP_INT, +}; diff --git a/bin/test/operators.h b/bin/test/operators.h new file mode 100644 index 0000000000..d49e7c25f0 --- /dev/null +++ b/bin/test/operators.h @@ -0,0 +1,41 @@ +#define NOT 0 +#define ISBLOCK 1 +#define ISCHAR 2 +#define ISDIR 3 +#define ISEXIST 4 +#define ISFILE 5 +#define ISSETGID 6 +#define ISSTICKY 7 +#define STRLEN 8 +#define ISFIFO 9 +#define ISREAD 10 +#define ISSIZE 11 +#define ISTTY 12 +#define ISSETUID 13 +#define ISWRITE 14 +#define ISEXEC 15 +#define NULSTR 16 +#define OR1 17 +#define OR2 18 +#define AND1 19 +#define AND2 20 +#define STREQ 21 +#define STRNE 22 +#define EQ 23 +#define NE 24 +#define GT 25 +#define LT 26 +#define LE 27 +#define GE 28 + +#define FIRST_BINARY_OP 17 + +#define OP_INT 1 /* arguments to operator are integer */ +#define OP_STRING 2 /* arguments to operator are string */ +#define OP_FILE 3 /* argument is a file name */ + +extern char *const unary_op[]; +extern char *const binary_op[]; +extern char *const andor_op[]; +extern const char op_priority[]; +extern const char op_argflag[]; diff --git a/bin/test/test.1 b/bin/test/test.1 new file mode 100644 index 0000000000..3fd02d4bac --- /dev/null +++ b/bin/test/test.1 @@ -0,0 +1,251 @@ +.\" Copyright (c) 1991 Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)test.1 6.6 (Berkeley) 6/8/92 +.\" +.Dd June 8, 1992 +.Dt TEST 1 +.Os +.Sh NAME +.Nm test +.Nd Condition evaluation utility. +.Sh SYNOPSIS +.Nm test +.Ar expression +.Sh DESCRIPTION +The +.Nm test +utility evaluates the expression and, if it evaluates +to true, returns a zero (true) exit status; otherwise +it returns 1 (false). +If there is no expression, test also +returns 1 (false). +.Pp +All operators and flags are separate arguments to the +.Nm test +utility. +.Pp +The following primaries are used to construct expression: +.Bl -tag -width Ar +.It Fl b Ar file +True if +.Ar file +exists and is a block special +file. +.It Fl c Ar file +True if +.Ar file +exists and is a character +special file. +.It Fl d Ar file +True if +.Ar file +exists and is a directory. +.It Fl e Ar file +True if +.Ar file +exists (regardless of type). +.It Fl f Ar file +True if +.Ar file +exists and is a regular file. +.It Fl g Ar file +True if +.Ar file +exists and its set group ID flag +is set. +.It Fl n Ar string +True if the length of +.Ar string +is nonzero. +.It Fl p Ar file +True if +.Ar file +is a named pipe +.Po Tn FIFO Pc . +.It Fl r Ar file +True if +.Ar file exists and is readable. +.It Fl s Ar file +True if +.Ar file +exists and has a size greater +than zero. +.It Fl t Ar [file_descriptor] +True if the file whose file descriptor number +is +.Ar file_descriptor +(default 1) is open and is +associated with a terminal. +.It Fl u Ar file +True if +.Ar file +exists and its set user ID flag +is set. +.It Fl w Ar file +True if +.Ar file +exists and is writable. +True +indicates only that the write flag is on. +The file is not writable on a read-only file +system even if this test indicates true. +.It Fl x Ar file +True if +.Ar file +exists and is executable. +True +indicates only that the execute flag is on. +If +.Ar file +is a directory, true indicates that +.Ar file +can be searched. +.It Fl z Ar string +True if the length of +.Ar string +is zero. +.It Ar string +True if +.Ar string +is not the null +string. +.It Ar \&s\&1 Cm \&= Ar \&s\&2 +True if the strings +.Ar \&s\&1 +and +.Ar \&s\&2 +are identical. +.It Ar \&s\&1 Cm \&!= Ar \&s\&2 +True if the strings +.Ar \&s\&1 +and +.Ar \&s\&2 +are not identical. +.It Ar \&n\&1 Fl \&eq Ar \&n\&2 +True if the integers +.Ar \&n\&1 +and +.Ar \&n\&2 +are algebraically +equal. +.It Ar \&n\&1 Fl \&ne Ar \&n\&2 +True if the integers +.Ar \&n\&1 +and +.Ar \&n\&2 +are not +algebraically equal. +.It Ar \&n\&1 Fl \> Ar \&n\&2 +True if the integer +.Ar \&n\&1 +is algebraically +greater than the integer +.Ar \&n\&2 . +.It Ar \&n\&1 Fl \&ge Ar \&n\&2 +True if the integer +.Ar \&n\&1 +is algebraically +greater than or equal to the integer +.Ar \&n\&2 . +.It Ar \&n\&1 Fl \< Ar \&n\&2 +True if the integer +.Ar \&n\&1 +is algebraically less +than the integer +.Ar \&n\&2 . +.It Ar \&n\&1 Fl \&le Ar \&n\&2 +True if the integer +.Ar \&n\&1 +is algebraically less +than or equal to the integer +.Ar \&n\&2 . +.El +.Pp +These primaries can be combined with the following operators: +.Bl -tag -width Ar +.It Cm \&! Ar expression +True if +.Ar expression +is false. +.It Ar expression1 Fl a Ar expression2 +True if both +.Ar expression1 +and +.Ar expression2 +are true. +.It Ar expression1 Fl o Ar expression2 +True if either +.Ar expression1 +or +.Ar expression2 +are true. +.It Cm \&( Ns Ar expression Ns Cm \&) +True if expression is true. +.El +.Pp +The +.Fl a +operator has higher precedence than the +.Fl o +operator. +.Sh GRAMMAR AMBIGUITY +The +.Nm test +grammar is inherently ambiguous. In order to assure a degree of consistency, +the cases described in the +.St -p1003.2 , +section D11.2/4.62.4, standard +are evaluated consistently according to the rules specified in the +standards document. All other cases are subject to the ambiguity in the +command semantics. +.Sh RETURN VALUES +The +.Nm test +utility exits with one of the following values: +.Bl -tag -width Ds +.It 0 +expression evaluated to true. +.It 1 +expression evaluated to false or expression was +missing. +.It >1 +An error occurred. +.El +.Sh STANDARDS +The +.Nm test +function is expected to be +.St -p1003.2 +compatible. diff --git a/bin/test/test.c b/bin/test/test.c index 4d4f978d20..66d8638130 100644 --- a/bin/test/test.c +++ b/bin/test/test.c @@ -1,757 +1,574 @@ -/* - * GNU test program (ksb and mjb) +/*- + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. * - * Modified to run with the GNU shell Apr 25, 1988 by bfox. + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. * - *??? -G file is owned by same gid; the effective gid is checked - * Chet Ramey, CWRU 3/23/89 + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. * - * Fixed a BSD dependency (_doprnt()) in the port to AIX 2.2 - * Chet Ramey, CWRU 5/3/89 + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ -/* Copyright (C) 1987,1989 Free Software Foundation, Inc. - -This file is part of GNU Bash, the Bourne Again SHell. - -Bash is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free -Software Foundation; either version 1, or (at your option) any later -version. - -Bash is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. - -You should have received a copy of the GNU General Public License along -with Bash; see the file COPYING. If not, write to the Free Software -Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ -#ifndef SONY -#include -#endif +#ifndef lint +static char sccsid[] = "@(#)test.c 5.4 (Berkeley) 2/12/93"; +#endif /* not lint */ #include #include -#include - -#ifndef R_OK -#define R_OK 4 -#define W_OK 2 -#define X_OK 1 -#define F_OK 0 -#endif - -#ifndef lint -static char *rcsid = "$Id: gtest.c,v 1.10 88/07/02 13:34:45 afb Exp Locker: afb $"; -#endif - -/* The following few defines control the truth and false output of each stage. - TRUE and FALSE are what we use to compute the final output value. - SHELL_BOOLEAN is the form which returns truth or falseness in shell terms. - TRUTH_OR is how to do logical or with TRUE and FALSE. - TRUTH_AND is how to do logical and with TRUE and FALSE.. - Default is TRUE = 1, FALSE = 0, TRUTH_OR = a | b, TRUTH_AND = a & b, - SHELL_BOOLEAN = (!value). */ -#define TRUE 1 -#define FALSE 0 -#define SHELL_BOOLEAN(value) (!(value)) -#define TRUTH_OR(a, b) ((a) | (b)) -#define TRUTH_AND(a, b) ((a) & (b)) - - -/* Define STANDALONE to get the /bin/test version. Otherwise, we are - making this for the shell. */ -/* #define STANDALONE */ - -#ifdef STANDALONE -#define test_exit(val) exit (val) -#else -#include -jmp_buf test_exit_buf; -int test_error_return = 0; -#define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1) -#endif /* STANDALONE */ - -#ifdef SYSV -int sys_v = 1; -#else -int sys_v = 0; -#endif - -static int pos; /* position in list */ -static int argc; /* number of args from command line */ -static char **argv; /* the argument list */ - -static void -test_syntax_error (format, arg) - char *format, *arg; -{ - (void) fprintf (stderr, "%s: ", argv[0]); - (void) fprintf (stderr, format, arg); - test_exit (SHELL_BOOLEAN (FALSE)); -} - -test_io_error (name) - char *name; -{ - extern int errno; - int old_errno = errno; - fprintf (stderr, "%s: ", argv[0]); - errno = old_errno; - perror (name); - test_exit (SHELL_BOOLEAN (FALSE)); -} - -/* - * advance - increment our position in the argument list. Check that - * we're not past the end of the argument list. This check is - * supressed if the argument is FALSE. made a macro for efficiency. - */ -#ifndef lint -#define advance(f) (++pos, f && (pos < argc ? 0 : beyond())) -#endif +#include +#include +#include +#include -#if !defined(advance) -static int -advance (f) - int f; -{ - ++pos; - if (f && pos >= argc) - beyond (); -} +#include "operators.h" -#endif +#define STACKSIZE 12 +#define NESTINCR 16 -#define unary_advance() (advance (1),++pos) +/* data types */ +#define STRING 0 +#define INTEGER 1 +#define BOOLEAN 2 -/* - * beyond - call when we're beyond the end of the argument list (an - * error condition) - */ -static int -beyond () -{ - test_syntax_error ("argument expected\n", (char *)NULL); -} +#define IS_BANG(s) (s[0] == '!' && s[1] == '\0') /* - * int_expt_err - when an integer argument was expected, but something else - * was found. + * This structure hold a value. The type keyword specifies the type of + * the value, and the union u holds the value. The value of a boolean + * is stored in u.num (1 = TRUE, 0 = FALSE). */ -static void -int_expt_err (pch) - char *pch; -{ - test_syntax_error ("integer expression expected %s\n", pch); -} +struct value { + int type; + union { + char *string; + long num; + } u; +}; + +struct operator { + short op; /* Which operator. */ + short pri; /* Priority of operator. */ +}; + +struct filestat { + char *name; /* Name of file. */ + int rcode; /* Return code from stat. */ + struct stat stat; /* Status info on file. */ +}; + +static void err __P((const char *, ...)); +static int expr_is_false __P((struct value *)); +static void expr_operator __P((int, struct value *, struct filestat *)); +static long chk_atol __P((char *)); +static int lookup_op __P((char *, char *const *)); +static void overflow __P((void)); +static int posix_binary_op __P((char **)); +static int posix_unary_op __P((char **)); +static void syntax __P((void)); -/* - * isint - is the argument whose position in the argument vector is 'm' an - * integer? Convert it for me too, returning it's value in 'pl'. - */ -static int -isint (m, pl) - int m; - long *pl; +int +main(argc, argv) + int argc; + char *argv[]; { - extern long atol (); - register char *pch; - - pch = argv[m]; - - /* Skip leading whitespace characters. */ - while (*pch == '\t' || *pch == ' ') - pch++; - - /* accept negative numbers but not '-' alone */ - if ('-' == *pch) - if ('\000' == *++pch) - return 0; - - while ('\000' != *pch) - { - switch (*pch) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - break; - default: - return (FALSE); + struct operator opstack[STACKSIZE]; + struct operator *opsp; + struct value valstack[STACKSIZE + 1]; + struct value *valsp; + struct filestat fs; + char c, **ap, *opname, *p; + int binary, nest, op, pri, ret_val, skipping; + + if ((p = argv[0]) == NULL) { + err("test: argc is zero.\n"); + exit(2); } - ++pch; - } - *pl = atol (argv[m]); - return (TRUE); -} -/* - * age_of - find the age of the given file. Return YES or NO depending - * on whether the file exists, and if it does, fill in the age with - * the modify time. - */ -static int -age_of (posit, age) - int posit; - long *age; -{ - struct stat stat_buf; - - if (stat (argv[posit], &stat_buf) < 0) - { - return (FALSE); - } - *age = stat_buf.st_mtime; - return (TRUE); -} - -/* - * term - parse a term and return 1 or 0 depending on whether the term - * evaluates to true or false, respectively. - * - * term ::= - * '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename - * '-'('L'|'x') filename - * '-t' [ int ] - * '-'('z'|'n') string - * string - * string ('!='|'=') string - * '-'(eq|ne|le|lt|ge|gt) - * file '-'(nt|ot|ef) file - * '(' ')' - * int ::= - * '-l' string - * positive and negative integers - */ -static int -term () -{ - int expr (); - auto struct stat stat_buf, stat_spare; - auto long int l, r; - auto int l_is_l, r_is_l; /* are the left and right integer - * expressions of the form '-l string'? - */ - auto int value, fd; - - if (pos >= argc) - beyond (); - - /* Deal with leading "not". */ - if (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1]) - { - advance (1); - - /* This has to be rewritten to handle the TRUTH and FALSE stuff. */ - return (!term ()); - } - - if ('(' == argv[pos][0] && '\000' == argv[pos][1]) - { - advance (1); - value = expr (); - if (')' != argv[pos][0] || '\000' != argv[pos][1]) - test_syntax_error ("argument expected, found %s\n", argv[pos]); - advance (0); - return (TRUE == (value)); - } - /* are there enough arguments left that this could be dyadic? */ - if (pos + 3 <= argc) - { - register int op; - if ('-' == argv[pos][0] && 'l' == argv[pos][1] && - '\000' == argv[pos][2]) - { - l_is_l = 1; - op = pos + 2; - advance (0); - } - else - { - l_is_l = 0; - op = pos + 1; + if (*p != '\0' && p[strlen(p) - 1] == '[') { + if (strcmp(argv[--argc], "]")) + err("missing ]"); + argv[argc] = NULL; } - if ('-' == argv[op + 1][0] && 'l' == argv[op + 1][1] - && '\000' == argv[op + 1][2]) - { - r_is_l = 1; - advance (0); - } - else - r_is_l = 0; - - if ('-' == argv[op][0]) - { - /* check for eq, nt, and stuff */ - switch (argv[op][1]) - { - default: - break; - case 'l': - if ('t' == argv[op][2] && '\000' == argv[op][3]) - { - /* lt */ - if (l_is_l) - l = strlen (argv[op - 1]); - else - { - if (!isint (op - 1, &l)) - int_expt_err ("before -lt"); - } - - if (r_is_l) - r = strlen (argv[op + 2]); - else - { - if (!isint (op + 1, &r)) - int_expt_err ("after -lt"); - } - pos += 3; - return (TRUE == (l < r)); + ap = argv + 1; + fs.name = NULL; + + /* + * Test(1) implements an inherently ambiguous grammer. In order to + * assure some degree of consistency, we special case the POSIX 1003.2 + * requirements to assure correct evaluation for POSIX scripts. The + * following special cases comply with POSIX P1003.2/D11.2 Section + * 4.62.4. + */ + switch(argc - 1) { + case 0: /* % test */ + return (1); + break; + case 1: /* % test arg */ + /* MIPS machine returns NULL of '[ ]' is called. */ + return (argv[1] == 0 || *argv[1] == '\0') ? 1 : 0; + break; + case 2: /* % test op arg */ + opname = argv[1]; + if (IS_BANG(opname)) + return (*argv[2] == '\0') ? 0 : 1; + else { + ret_val = posix_unary_op(&argv[1]); + if (ret_val >= 0) + return (ret_val); } - - if ('e' == argv[op][2] && '\000' == argv[op][3]) - { - /* le */ - if (l_is_l) - l = strlen (argv[op - 1]); - else - { - if (!isint (op - 1, &l)) - int_expt_err ("before -le"); - } - if (r_is_l) - r = strlen (argv[op + 2]); - else - { - if (!isint (op + 1, &r)) - int_expt_err ("after -le"); - } - pos += 3; - return (TRUE == (l <= r)); + break; + case 3: /* % test arg1 op arg2 */ + if (IS_BANG(argv[1])) { + ret_val = posix_unary_op(&argv[1]); + if (ret_val >= 0) + return (!ret_val); + } else if (lookup_op(argv[2], andor_op) < 0) { + ret_val = posix_binary_op(&argv[1]); + if (ret_val >= 0) + return (ret_val); } - break; - - case 'g': - - if ('t' == argv[op][2] && '\000' == argv[op][3]) - { - /* gt integer greater than */ - if (l_is_l) - l = strlen (argv[op - 1]); - else - { - if (!isint (op - 1, &l)) - int_expt_err ("before -gt"); - } - if (r_is_l) - r = strlen (argv[op + 2]); - else - { - if (!isint (op + 1, &r)) - int_expt_err ("after -gt"); - } - pos += 3; - return (TRUE == (l > r)); + break; + case 4: /* % test ! arg1 op arg2 */ + if (IS_BANG(argv[1]) && lookup_op(argv[3], andor_op) < 0) { + ret_val = posix_binary_op(&argv[2]); + if (ret_val >= 0) + return (!ret_val); } + break; + default: + break; + } - if ('e' == argv[op][2] && '\000' == argv[op][3]) - { - /* ge - integer greater than or equal to */ - if (l_is_l) - l = strlen (argv[op - 1]); - else - { - if (!isint (op - 1, &l)) - int_expt_err ("before -ge"); - } - if (r_is_l) - r = strlen (argv[op + 2]); - else - { - if (!isint (op + 1, &r)) - int_expt_err ("after -ge"); - } - pos += 3; - return (TRUE == (l >= r)); - } - break; - - case 'n': - if ('t' == argv[op][2] && '\000' == argv[op][3]) - { - /* nt - newer than */ - pos += 3; - if (l_is_l || r_is_l) - test_syntax_error ("-nt does not accept -l\n", - (char *)NULL); - if (age_of (op - 1, &l) && age_of (op + 1, &r)) - return (TRUE == (l > r)); - else - return (FALSE); + /* + * We use operator precedence parsing, evaluating the expression as + * we parse it. Parentheses are handled by bumping up the priority + * of operators using the variable "nest." We use the variable + * "skipping" to turn off evaluation temporarily for the short + * circuit boolean operators. (It is important do the short circuit + * evaluation because under NFS a stat operation can take infinitely + * long.) + */ + opsp = opstack + STACKSIZE; + valsp = valstack; + nest = skipping = 0; + if (*ap == NULL) { + valstack[0].type = BOOLEAN; + valstack[0].u.num = 0; + goto done; + } + for (;;) { + opname = *ap++; + if (opname == NULL) + syntax(); + if (opname[0] == '(' && opname[1] == '\0') { + nest += NESTINCR; + continue; + } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) { + if (opsp == &opstack[0]) + overflow(); + --opsp; + opsp->op = op; + opsp->pri = op_priority[op] + nest; + continue; + } else { + valsp->type = STRING; + valsp->u.string = opname; + valsp++; } - - if ('e' == argv[op][2] && '\000' == argv[op][3]) - { - /* ne - integer not equal */ - if (l_is_l) - l = strlen (argv[op - 1]); - else - { - if (!isint (op - 1, &l)) - int_expt_err ("before -ne"); - } - if (r_is_l) - r = strlen (argv[op + 2]); - else - { - if (!isint (op + 1, &r)) - int_expt_err ("after -ne"); - } - pos += 3; - return (TRUE == (l != r)); + for (;;) { + opname = *ap++; + if (opname == NULL) { + if (nest != 0) + syntax(); + pri = 0; + break; + } + if (opname[0] != ')' || opname[1] != '\0') { + if ((op = lookup_op(opname, binary_op)) < 0) + syntax(); + op += FIRST_BINARY_OP; + pri = op_priority[op] + nest; + break; + } + if ((nest -= NESTINCR) < 0) + syntax(); } - break; - - case 'e': - if ('q' == argv[op][2] && '\000' == argv[op][3]) - { - /* eq - integer equal */ - if (l_is_l) - l = strlen (argv[op - 1]); - else - { - if (!isint (op - 1, &l)) - int_expt_err ("before -eq"); - } - if (r_is_l) - r = strlen (argv[op + 2]); - else - { - if (!isint (op + 1, &r)) - int_expt_err ("after -eq"); - } - pos += 3; - return (TRUE == (l == r)); + while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) { + binary = opsp->op; + for (;;) { + valsp--; + c = op_argflag[opsp->op]; + if (c == OP_INT) { + if (valsp->type == STRING) + valsp->u.num = + chk_atol(valsp->u.string); + valsp->type = INTEGER; + } else if (c >= OP_STRING) { + /* OP_STRING or OP_FILE */ + if (valsp->type == INTEGER) { + if ((p = malloc(32)) == NULL) + err("%s", + strerror(errno)); +#ifdef SHELL + fmtstr(p, 32, "%d", + valsp->u.num); +#else + (void)sprintf(p, + "%d", valsp->u.num); +#endif + valsp->u.string = p; + } else if (valsp->type == BOOLEAN) { + if (valsp->u.num) + valsp->u.string = + "true"; + else + valsp->u.string = ""; + } + valsp->type = STRING; + if (c == OP_FILE && (fs.name == NULL || + strcmp(fs.name, valsp->u.string))) { + fs.name = valsp->u.string; + fs.rcode = + stat(valsp->u.string, + &fs.stat); + } + } + if (binary < FIRST_BINARY_OP) + break; + binary = 0; + } + if (!skipping) + expr_operator(opsp->op, valsp, &fs); + else if (opsp->op == AND1 || opsp->op == OR1) + skipping--; + valsp++; /* push value */ + opsp++; /* pop operator */ } - - if ('f' == argv[op][2] && '\000' == argv[op][3]) - { - /* ef - hard link? */ - pos += 3; - if (l_is_l || r_is_l) - test_syntax_error ("-ef does not accept -l\n", - (char *)NULL); - if (stat (argv[op - 1], &stat_buf) < 0) - return (FALSE); - if (stat (argv[op + 1], &stat_spare) < 0) - return (FALSE); - return (TRUE == - (stat_buf.st_dev == stat_spare.st_dev && - stat_buf.st_ino == stat_spare.st_ino)); + if (opname == NULL) + break; + if (opsp == &opstack[0]) + overflow(); + if (op == AND1 || op == AND2) { + op = AND1; + if (skipping || expr_is_false(valsp - 1)) + skipping++; } - break; - - case 'o': - if ('t' == argv[op][2] && '\000' == argv[op][3]) - { - /* ot - older than */ - pos += 3; - if (l_is_l || r_is_l) - test_syntax_error ("-nt does not accept -l\n", - (char *)NULL); - if (age_of (op - 1, &l) && age_of (op + 1, &r)) - return (TRUE == (l < r)); - return (FALSE); + if (op == OR1 || op == OR2) { + op = OR1; + if (skipping || !expr_is_false(valsp - 1)) + skipping++; } - break; - } + opsp--; + opsp->op = op; + opsp->pri = pri; } +done: return (expr_is_false(&valstack[0])); +} - if ('=' == argv[op][0] && '\000' == argv[op][1]) - { - value = (0 == strcmp (argv[pos], argv[pos + 2])); - pos += 3; - return (TRUE == value); - } - if ('!' == argv[op][0] && '=' == argv[op][1] && '\000' == argv[op][2]) - { - value = 0 != strcmp (argv[pos], argv[pos + 2]); - pos += 3; - return (TRUE == value); +static int +expr_is_false(val) + struct value *val; +{ + if (val->type == STRING) { + if (val->u.string[0] == '\0') + return (1); + } else { /* INTEGER or BOOLEAN */ + if (val->u.num == 0) + return (1); } - } - - /* Might be a switch type argument */ - if ('-' == argv[pos][0] && '\000' == argv[pos][2] /* && pos < argc-1 */ ) - { - switch (argv[pos][1]) - { - default: - break; - - /* All of the following unary operators use unary_advance (), which - checks to make sure that there is an argument, and then advances - pos right past it. This means that pos - 1 is the location of the - argument. */ - - case 'r': /* file is readable? */ - unary_advance (); - value = -1 != access (argv[pos - 1], R_OK); - return (TRUE == value); - - case 'w': /* File is writeable? */ - unary_advance (); - value = -1 != access (argv[pos - 1], W_OK); - return (TRUE == value); - - case 'x': /* File is executable? */ - unary_advance (); - value = -1 != access (argv[pos - 1], X_OK); - return (TRUE == value); - - case 'O': /* File is owned by you? */ - unary_advance (); - if (stat (argv[pos - 1], &stat_buf) < 0) - return (FALSE); - - return (TRUE == (geteuid () == stat_buf.st_uid)); - - case 'f': /* File is a file? */ - unary_advance (); - if (stat (argv[pos - 1], &stat_buf) < 0) - return (FALSE); - - /* - * Under SYSV, -f is true if the given file exists - * and is a regular file. Other places, this checks - * to see if the given file is not a directory. - */ - if (sys_v) - return (TRUE == ((S_IFREG == (stat_buf.st_mode & S_IFMT)) || - (0 == (stat_buf.st_mode & S_IFMT)))); - else - return (TRUE == (S_IFDIR != (stat_buf.st_mode & S_IFMT))); - - case 'd': /* File is a directory? */ - unary_advance (); - if (stat (argv[pos - 1], &stat_buf) < 0) - return (FALSE); - - return (TRUE == (S_IFDIR == (stat_buf.st_mode & S_IFMT))); - - case 's': /* File has something in it? */ - unary_advance (); - if (stat (argv[pos - 1], &stat_buf) < 0) - return (FALSE); - - return (TRUE == (stat_buf.st_size > (off_t) 0)); - -#ifdef S_IFSOCK - case 'S': /* File is a socket? */ - unary_advance (); - if (stat (argv[pos - 1], &stat_buf) < 0) - return (FALSE); - - return (TRUE == (S_IFSOCK == (stat_buf.st_mode & S_IFMT))); -#endif - - case 'c': /* File is character special? */ - unary_advance (); - if (stat (argv[pos - 1], &stat_buf) < 0) - return (FALSE); - - return (TRUE == (S_IFCHR == (stat_buf.st_mode & S_IFMT))); + return (0); +} - case 'b': /* File is block special? */ - unary_advance (); - if (stat (argv[pos - 1], &stat_buf) < 0) - return (FALSE); - return (TRUE == (S_IFBLK == (stat_buf.st_mode & S_IFMT))); +/* + * Execute an operator. Op is the operator. Sp is the stack pointer; + * sp[0] refers to the first operand, sp[1] refers to the second operand + * (if any), and the result is placed in sp[0]. The operands are converted + * to the type expected by the operator before expr_operator is called. + * Fs is a pointer to a structure which holds the value of the last call + * to stat, to avoid repeated stat calls on the same file. + */ +static void +expr_operator(op, sp, fs) + int op; + struct value *sp; + struct filestat *fs; +{ + int i; + + switch (op) { + case NOT: + sp->u.num = expr_is_false(sp); + sp->type = BOOLEAN; + break; + case ISEXIST: + if (fs == NULL || fs->rcode == -1) + goto false; + else + goto true; + case ISREAD: + i = S_IROTH; + goto permission; + case ISWRITE: + i = S_IWOTH; + goto permission; + case ISEXEC: + i = S_IXOTH; +permission: if (fs->stat.st_uid == geteuid()) + i <<= 6; + else if (fs->stat.st_gid == getegid()) + i <<= 3; + goto filebit; /* true if (stat.st_mode & i) != 0 */ + case ISFILE: + i = S_IFREG; + goto filetype; + case ISDIR: + i = S_IFDIR; + goto filetype; + case ISCHAR: + i = S_IFCHR; + goto filetype; + case ISBLOCK: + i = S_IFBLK; + goto filetype; + case ISFIFO: + i = S_IFIFO; + goto filetype; +filetype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) +true: sp->u.num = 1; + else +false: sp->u.num = 0; + sp->type = BOOLEAN; + break; + case ISSETUID: + i = S_ISUID; + goto filebit; + case ISSETGID: + i = S_ISGID; + goto filebit; + case ISSTICKY: + i = S_ISVTX; +filebit: if (fs->stat.st_mode & i && fs->rcode >= 0) + goto true; + goto false; + case ISSIZE: + sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L; + sp->type = INTEGER; + break; + case ISTTY: + sp->u.num = isatty(sp->u.num); + sp->type = BOOLEAN; + break; + case NULSTR: + if (sp->u.string[0] == '\0') + goto true; + goto false; + case STRLEN: + sp->u.num = strlen(sp->u.string); + sp->type = INTEGER; + break; + case OR1: + case AND1: + /* + * These operators are mostly handled by the parser. If we + * get here it means that both operands were evaluated, so + * the value is the value of the second operand. + */ + *sp = *(sp + 1); + break; + case STREQ: + case STRNE: + i = 0; + if (!strcmp(sp->u.string, (sp + 1)->u.string)) + i++; + if (op == STRNE) + i = 1 - i; + sp->u.num = i; + sp->type = BOOLEAN; + break; + case EQ: + if (sp->u.num == (sp + 1)->u.num) + goto true; + goto false; + case NE: + if (sp->u.num != (sp + 1)->u.num) + goto true; + goto false; + case GT: + if (sp->u.num > (sp + 1)->u.num) + goto true; + goto false; + case LT: + if (sp->u.num < (sp + 1)->u.num) + goto true; + goto false; + case LE: + if (sp->u.num <= (sp + 1)->u.num) + goto true; + goto false; + case GE: + if (sp->u.num >= (sp + 1)->u.num) + goto true; + goto false; - case 'p': /* File is a named pipe? */ - unary_advance (); -#ifndef S_IFIFO - return (FALSE); -#else - if (stat (argv[pos - 1], &stat_buf) < 0) - return (FALSE); - return (TRUE == (S_IFIFO == (stat_buf.st_mode & S_IFMT))); -#endif /* S_IFIFO */ - - case 'L': /* Same as -h */ - /*FALLTHROUGH*/ - - case 'h': /* File is a symbolic link? */ - unary_advance (); -#ifndef S_IFLNK - return (FALSE); -#else - if (lstat (argv[pos - 1], &stat_buf) < 0) - return (FALSE); - - return (TRUE == (S_IFLNK == (stat_buf.st_mode & S_IFMT))); -#endif /* S_IFLNK */ - - case 'u': /* File is setuid? */ - unary_advance (); - if (stat (argv[pos - 1], &stat_buf) < 0) - return (FALSE); - - return (TRUE == (0 != (stat_buf.st_mode & S_ISUID))); - - case 'g': /* File is setgid? */ - unary_advance (); - if (stat (argv[pos - 1], &stat_buf) < 0) - return (FALSE); - - return (TRUE == (0 != (stat_buf.st_mode & S_ISGID))); - - case 'k': /* File has sticky bit set? */ - unary_advance (); - if (stat (argv[pos - 1], &stat_buf) < 0) - return (FALSE); - return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX))); - - case 't': /* File (fd) is a terminal? (fd) defaults to stdout. */ - advance (0); - if (pos < argc && isint (pos, &r)) - { - advance (0); - return (TRUE == (isatty ((int) r))); - } - return (TRUE == (isatty (1))); - - case 'n': /* True if arg has some length. */ - unary_advance (); - return (TRUE == (0 != strlen (argv[pos - 1]))); - break; - - case 'z': /* True if arg has no length. */ - unary_advance (); - return (TRUE == (0 == strlen (argv[pos - 1]))); } - } - value = 0 != strlen (argv[pos]); - advance (0); - return value; } -/* - * and: - * and '-a' term - * term - */ static int -and () +lookup_op(name, table) + char *name; + char *const * table; { - auto int value; - - value = term (); - while (pos < argc && '-' == argv[pos][0] && 'a' == argv[pos][1] && '\000' == argv[pos][2]) - { - advance (0); - value = TRUTH_AND (value, term ()); - } - return (TRUE == value); + register char *const * tp; + register char const *p; + char c; + + c = name[1]; + for (tp = table; (p = *tp) != NULL; tp++) + if (p[1] == c && !strcmp(p, name)) + return (tp - table); + return (-1); } -/* - * or: - * or '-o' and - * and - */ static int -or () +posix_unary_op(argv) + char **argv; { - auto int value; - - value = and (); - while (pos < argc && '-' == argv[pos][0] && 'o' == argv[pos][1] && '\000' == argv[pos][2]) - { - advance (0); - value = TRUTH_OR (value, and ()); - } - return (TRUE == value); + struct filestat fs; + struct value valp; + int op, c; + char *opname; + + opname = *argv; + if ((op = lookup_op(opname, unary_op)) < 0) + return (-1); + c = op_argflag[op]; + opname = argv[1]; + valp.u.string = opname; + if (c == OP_FILE) { + fs.name = opname; + fs.rcode = stat(opname, &fs.stat); + } else if (c != OP_STRING) + return (-1); + + expr_operator(op, &valp, &fs); + return (valp.u.num == 0); } -/* - * expr: - * or - */ -int -expr () +static int +posix_binary_op(argv) + char **argv; { - auto int value; - - if (pos >= argc) - beyond (); - - value = FALSE; - - return value ^ or (); /* Same with this. */ + struct value v[2]; + int op, c; + char *opname; + + opname = argv[1]; + if ((op = lookup_op(opname, binary_op)) < 0) + return (-1); + op += FIRST_BINARY_OP; + c = op_argflag[op]; + + if (c == OP_INT) { + v[0].u.num = chk_atol(argv[0]); + v[1].u.num = chk_atol(argv[2]); + } else { + v[0].u.string = argv[0]; + v[1].u.string = argv[2]; + } + expr_operator(op, v, NULL); + return (v[0].u.num == 0); } /* - * [: - * '[' expr ']' - * test: - * test expr + * Integer type checking. */ -int -#ifdef STANDALONE -main (margc, margv) -#else -test_command (margc, margv) -#endif /* STANDALONE */ - int margc; - char **margv; +static long +chk_atol(v) + char *v; { - auto int value; - int expr (); -#ifndef STANDALONE - int code = setjmp (test_exit_buf); - - if (code) - return (test_error_return); -#endif /* STANDALONE */ - - argv = margv; - - if (margv[0] && strcmp (margv[0], "[") == 0) - { - --margc; - - if (margc < 2) - test_exit (SHELL_BOOLEAN (FALSE)); - - if (margv[margc] && strcmp (margv[margc], "]") != 0) - test_syntax_error ("missing `]'\n", (char *)NULL); - } + char *p; + long r; + + errno = 0; + r = strtol(v, &p, 10); + if (errno != 0) + err("\"%s\" -- out of range.", v); + while (isspace(*p)) + p++; + if (*p != '\0') + err("illegal operand \"%s\" -- expected integer.", v); + return (r); +} - argc = margc; - pos = 1; +static void +syntax() +{ + err("syntax error"); +} - if (pos >= argc) - test_exit (SHELL_BOOLEAN (FALSE)); +static void +overflow() +{ + err("expression is too complex"); +} - value = expr (); - if (pos != argc) - test_syntax_error ("too many arguments\n", (char *)NULL); +#if __STDC__ +#include +#else +#include +#endif - test_exit (SHELL_BOOLEAN (value)); +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "test: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(2); + /* NOTREACHED */ } diff --git a/bin/test/unary_op b/bin/test/unary_op new file mode 100644 index 0000000000..0defa57ad2 --- /dev/null +++ b/bin/test/unary_op @@ -0,0 +1,59 @@ +# Copyright (c) 1988 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)unary_op 1.2 (Berkeley) 6/3/92 +# + +# +# List of unary operators used by test. +# + +NOT ! 3 +ISBLOCK -b 12 OP_FILE +ISCHAR -c 12 OP_FILE +ISDIR -d 12 OP_FILE +ISEXIST -e 12 OP_FILE +ISFILE -f 12 OP_FILE +ISSETGID -g 12 OP_FILE +ISSTICKY -k 12 OP_FILE +STRLEN -n 12 OP_STRING +ISFIFO -p 12 OP_FILE +ISREAD -r 12 OP_FILE +ISSIZE -s 12 OP_FILE +ISTTY -t 12 OP_INT +ISSETUID -u 12 OP_FILE +ISWRITE -w 12 OP_FILE +ISEXEC -x 12 OP_FILE +NULSTR -z 12 OP_STRING + diff --git a/contrib/Makefile b/contrib/Makefile index 9753a96467..c272cb933a 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 0.1 (RGrimes) 4/7/93 -SUBDIR= gzip tcpdump +SUBDIR= tcpdump # Not ported: isode diff --git a/contrib/Makefile.inc b/contrib/Makefile.inc index 9489995061..b9eca7d521 100644 --- a/contrib/Makefile.inc +++ b/contrib/Makefile.inc @@ -1,3 +1,3 @@ # @(#)Makefile.inc 5.1 (Berkeley) 5/11/90 -BINDIR?= /usr/local/bin +BINDIR?= /usr/bin diff --git a/contrib/tcpdump/Makefile.inc b/contrib/tcpdump/Makefile.inc index 9489995061..26c6f1c71b 100644 --- a/contrib/tcpdump/Makefile.inc +++ b/contrib/tcpdump/Makefile.inc @@ -1,3 +1,3 @@ # @(#)Makefile.inc 5.1 (Berkeley) 5/11/90 -BINDIR?= /usr/local/bin +BINDIR?= /usr/sbin diff --git a/contrib/tcpdump/tcpdump/Makefile b/contrib/tcpdump/tcpdump/Makefile index fee95540b9..7d3b5b223c 100644 --- a/contrib/tcpdump/tcpdump/Makefile +++ b/contrib/tcpdump/tcpdump/Makefile @@ -1,8 +1,8 @@ # @(#)Makefile 0.1 (RWGrimes) 3/24/93 PROG= tcpdump -CFLAGS+=-DCSLIP -I. -MAN1= tcpdump.0 +CFLAGS+=-DCSLIP -DPPP -I. +MAN1= tcpdump.1 SRCS= version.c addrtoname.c bpf_dump.c bpf_filter.c bpf_image.c etherent.c \ gencode.c inet.c md.c nametoaddr.c optimize.c os.c pcap.c \ print-arp.c print-atalk.c print-bootp.c print-domain.c \ diff --git a/contrib/tcpdump/tcpdump/interface.h b/contrib/tcpdump/tcpdump/interface.h index dfccba6e67..1ff89ff3f5 100644 --- a/contrib/tcpdump/tcpdump/interface.h +++ b/contrib/tcpdump/tcpdump/interface.h @@ -18,11 +18,13 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * @(#) $Header: interface.h,v 1.46 92/06/02 17:57:22 mccanne Exp $ (LBL) + * @(#) $Header: /a/cvs/386BSD/src/contrib/tcpdump/tcpdump/interface.h,v 1.1.1.1 1993/06/12 14:42:12 rgrimes Exp $ (LBL) */ #ifdef __GNUC__ +#ifndef inline #define inline __inline +#endif #else #define inline #endif diff --git a/contrib/tcpdump/tcpdump/print-nfs.c b/contrib/tcpdump/tcpdump/print-nfs.c index 8b31697711..593e334491 100644 --- a/contrib/tcpdump/tcpdump/print-nfs.c +++ b/contrib/tcpdump/tcpdump/print-nfs.c @@ -21,7 +21,7 @@ #ifndef lint static char rcsid[] = - "@(#) $Header: print-nfs.c,v 1.24 92/01/31 12:27:46 mccanne Exp $ (LBL)"; + "@(#) $Header: /a/cvs/386BSD/src/contrib/tcpdump/tcpdump/print-nfs.c,v 1.1.1.1 1993/06/12 14:42:08 rgrimes Exp $ (LBL)"; #endif #include @@ -37,12 +37,7 @@ static char rcsid[] = #include #include -#include -#include -#include -#include -#include -#include +#include #include diff --git a/contrib/tcpdump/tcpdump/print-sunrpc.c b/contrib/tcpdump/tcpdump/print-sunrpc.c index ad28e93b46..e7e74be42f 100644 --- a/contrib/tcpdump/tcpdump/print-sunrpc.c +++ b/contrib/tcpdump/tcpdump/print-sunrpc.c @@ -21,7 +21,7 @@ #ifndef lint static char rcsid[] = - "@(#) $Header: print-sunrpc.c,v 1.1 92/06/02 11:36:37 mccanne Exp $ (LBL)"; + "@(#) $Header: /a/cvs/386BSD/src/contrib/tcpdump/tcpdump/print-sunrpc.c,v 1.1.1.1 1993/06/12 14:42:07 rgrimes Exp $ (LBL)"; #endif #include @@ -37,13 +37,7 @@ static char rcsid[] = #include #include -#include -#include -#include -#include -#include -#include - +#include #include #include diff --git a/contrib/tcpdump/tcpdump/print-udp.c b/contrib/tcpdump/tcpdump/print-udp.c index cd51808902..14c22dac4f 100644 --- a/contrib/tcpdump/tcpdump/print-udp.c +++ b/contrib/tcpdump/tcpdump/print-udp.c @@ -21,7 +21,7 @@ #ifndef lint static char rcsid[] = - "@(#) $Header: print-udp.c,v 1.26 92/05/22 19:43:17 leres Exp $ (LBL)"; + "@(#) $Header: /a/cvs/386BSD/src/contrib/tcpdump/tcpdump/print-udp.c,v 1.1.1.1 1993/06/12 14:42:06 rgrimes Exp $ (LBL)"; #endif #include @@ -38,12 +38,7 @@ static char rcsid[] = #include #include #include -#include -#include -#include -#include -#include -#include +#include #include "interface.h" /* These must come after interface.h for BSD. */ diff --git a/contrib/tcpdump/tcpslice/Makefile b/contrib/tcpdump/tcpslice/Makefile index d1cd63b19f..55b240273c 100644 --- a/contrib/tcpdump/tcpslice/Makefile +++ b/contrib/tcpdump/tcpslice/Makefile @@ -2,7 +2,7 @@ PROG= tcpslice CFLAGS+=-DCSLIP -I. -I$(.CURDIR)/../tcpdump -MAN1= tcpslice.0 +MAN1= tcpslice.1 SRCS= version.c tcpslice.c gwtm2secs.c search.c \ savefile.c bpf_filter.c md.c util.c .PATH: ${.CURDIR}/../tcpdump /sys/net diff --git a/etc/Makefile b/etc/Makefile index 52225d6af7..444ef5eb41 100644 --- a/etc/Makefile +++ b/etc/Makefile @@ -1,37 +1,116 @@ # @(#)Makefile 5.11 (Berkeley) 5/21/91 -NOOBJ= oobj +NOOBJ= noobj # disktab may be wrong -- hcx9 is a tahoe, but gets its own. # -rw-r--r-- -BIN1= aliases csh.cshrc csh.login csh.logout crontab daily dm.conf \ - ftpusers gettytab group hosts hosts.equiv hosts.lpd inetd.conf \ - man.conf monthly motd netstart phones printcap protocols rc \ - rc.local remote security services shells syslog.conf ttys weekly \ - etc.${MACHINE}/disktab +BINOWN= root +BINGRP= wheel +BIN1= aliases csh.cshrc csh.login csh.logout dm.conf \ + ftpusers gettytab group hosts host.conf hosts.equiv hosts.lpd \ + inetd.conf motd myname netstart networks phones \ + printcap protocols rc rc.local remote security services shells \ + syslog.conf ttys etc.${MACHINE}/disktab rpc # -rw-rw-rw- BIN2= motd -MTREE= BSD.root.dist BSD.usr.dist BSD.var.dist +# -rwxr-xr-x root.wheel, for the new f***ing cron root.wheel +BIN3= daily weekly monthly + +MTREE= BSD.local.dist BSD.root.dist BSD.usr.dist BSD.var.dist NAMEDB= localhost.rev named.boot root.cache PCS= pcs750.bin WCS1= wcs fppwcs poc poc1 poc2 fppoc WCS2= fpevent fppwcs fppwcs_dual hdcwcs load_diags start_fpp wcs wcs_dual +# Special top level files for FreeBSD +COPYRIGHT= COPYRIGHT +FREEBSD= CONTRIB.386BSD CONTRIB.FreeBSD RELNOTES.FreeBSD ${COPYRIGHT} +# +# Floppy drive name and files for building FreeBSD Floppies +FLOPPY?= fd0 +MOUNT?= /mnt +FLOPPY_TYPE?= floppy5 +# +MDEC= usr/mdec/bootfd usr/mdec/fdboot +MDEC+= usr/mdec/bootsd usr/mdec/sdboot +MDEC+= usr/mdec/bootwd usr/mdec/wdboot +# +KC_DIRS= dev mnt +KC_FILES= ${COPYRIGHT} +KC_FILES+= bin/[ bin/cp bin/echo bin/sh bin/test +KC_FILES+= sbin/fsck sbin/halt sbin/init sbin/mount sbin/umount +# +FILESYSTEM_DIRS= bin dev etc mnt sbin usr usr/bin usr/mdec usr/sbin +FILESYSTEM_TREES= dev +FILESYSTEM_FILES= ${COPYRIGHT} +FILESYSTEM_FILES+= bin/[ bin/expr bin/ls bin/mkdir bin/rm +FILESYSTEM_FILES+= bin/sh bin/sync bin/test +FILESYSTEM_FILES+= dev/MAKEDEV +FILESYSTEM_FILES+= etc/group +FILESYSTEM_FILES+= etc/master.passwd etc/passwd etc/pwd.db +FILESYSTEM_FILES+= sbin/disklabel sbin/halt sbin/init +FILESYSTEM_FILES+= sbin/mount sbin/mount_pcfs +FILESYSTEM_FILES+= sbin/newfs +FILESYSTEM_FILES+= sbin/umount +FILESYSTEM_FILES+= sbin/fdisk +FILESYSTEM_FILES+= usr/bin/cpio +FILESYSTEM_FILES+= ${MDEC} +FILESYSTEM_FILES+= usr/sbin/bad144 + +CPIO_FILES= ${COPYRIGHT} +CPIO_FILES+= usr/bin/gunzip usr/bin/gzcat usr/bin/gzip usr/bin/zcat +CPIO_CPIO= bin/chmod bin/cat bin/cp bin/dd bin/df bin/mv bin/pwd bin/stty +CPIO_CPIO+= etc/protocols etc/remote etc/services etc/spwd.db +CPIO_CPIO+= sbin/ifconfig sbin/fsck sbin/mknod sbin/mount_isofs +CPIO_CPIO+= sbin/reboot sbin/route sbin/slattach +CPIO_CPIO+= tmp +CPIO_CPIO+= usr/bin/awk usr/bin/chgrp usr/bin/ftp +CPIO_CPIO+= usr/bin/more usr/bin/tar usr/bin/tip +CPIO_CPIO+= usr/bin/elvis usr/bin/ex usr/bin/vi usr/bin/view +CPIO_CPIO+= usr/sbin/update usr/sbin/chown +CPIO_CPIO_DIRS= var var/tmp var/run var/spool var/spool/lock + +CRYPT_LIB= lib/libcrypt +CRYPT_SRCS= bin/ed bin/rcp +CRYPT_SRCS+= libexec/ftpd libexec/makekey libexec/rexecd libexec/rlogind +CRYPT_SRCS+= libexec/rshd libexec/telnetd libexec/uucpd +CRYPT_SRCS+= usr.bin/bdes usr.bin/lock usr.bin/login usr.bin/passwd +CRYPT_SRCS+= usr.bin/rlogin usr.bin/rsh usr.bin/su usr.bin/telnet +CRYPT_DIRS= bin usr usr/bin usr/lib usr/libexec + all clean cleandir depend etc install lint: -distribution: +crypt: + rm -f ${LIBCRYPT}; + (cd ${.CURDIR}/../${CRYPT_LIB}; \ + ${MAKE} cleandir obj depend all install) + for i in ${CRYPT_SRCS}; do \ + cd ${.CURDIR}/../$$i; \ + ${MAKE} cleandir obj depend all; \ + done + +non-crypt: + rm -f ${LIBCRYPT} + for i in ${CRYPT_SRCS}; do \ + cd ${.CURDIR}/../$$i; \ + ${MAKE} cleandir obj depend all; \ + done + +distribution: distrib-dirs install -c -o ${BINOWN} -g ${BINGRP} -m 644 ${BIN1} ${DESTDIR}/etc install -c -o ${BINOWN} -g ${BINGRP} -m 666 ${BIN2} ${DESTDIR}/etc + install -c -o root -g wheel -m 755 ${BIN3} ${DESTDIR}/etc + install -c -o root -g wheel -m 600 crontab ${DESTDIR}/var/cron/tabs/root + install -c -o root -g wheel -m 600 /dev/null ${DESTDIR}/var/cron/log install -c -o root -g wheel -m 600 master.passwd ${DESTDIR}/etc - (cd ${DESTDIR}/etc; \ - pwd_mkdb -p master.passwd; \ - mv master.passwd.pag passwd.pag; \ - mv master.passwd.dir passwd.dir; \ - mv master.passwd.orig passwd) + pwd_mkdb -p -d ${DESTDIR}/etc ${DESTDIR}/etc/master.passwd install -c -o ${BINOWN} -g ${BINGRP} -m 555 \ MAKEDEV.local etc.${MACHINE}/MAKEDEV ${DESTDIR}/dev +.if defined(CDROMDIST) + (cd ${DESTDIR}/dev; sh MAKEDEV all) +.endif (cd root; \ install -c -o root -g wheel -m 644 dot.cshrc \ ${DESTDIR}/root/.cshrc; \ @@ -50,16 +129,39 @@ distribution: ${DESTDIR}/etc/namedb install -c -o ${BINOWN} -g operator -m 664 /dev/null \ ${DESTDIR}/etc/dumpdates + install -c -o nobody -g ${BINGRP} -m 664 /dev/null \ + ${DESTDIR}/var/db/locate.database install -c -o ${BINOWN} -g ${BINGRP} -m 664 /dev/null \ - ${DESTDIR}/var/log/messages + ${DESTDIR}/var/log/lpd-errs install -c -o ${BINOWN} -g ${BINGRP} -m 664 /dev/null \ ${DESTDIR}/var/log/maillog install -c -o ${BINOWN} -g ${BINGRP} -m 664 /dev/null \ - ${DESTDIR}/var/log/lpd-errs + ${DESTDIR}/var/log/lastlog + install -c -o ${BINOWN} -g ${BINGRP} -m 664 /dev/null \ + ${DESTDIR}/var/log/messages + install -c -o ${BINOWN} -g ${BINGRP} -m 664 /dev/null \ + ${DESTDIR}/var/log/wtmp install -c -o ${BINOWN} -g ${BINGRP} -m 664 /dev/null \ ${DESTDIR}/var/run/utmp (cd etc.${MACHINE}; install -c -o ${BINOWN} -g ${BINGRP} -m 444 \ - fstab.* ${DESTDIR}/) + fstab.* ${DESTDIR}/etc) +.if defined(NOCRYPT) + ${MAKE} non-crypt + (cd ..; NOCRYPT=nocrypt; export NOCRYPT; ${MAKE} install) +.else + ${MAKE} crypt + (cd ..; ${MAKE} install) +.endif + (cd ../usr.sbin/sendmail/src; \ + ${MAKE} install; \ + cd ../cf/cf; \ + ${MAKE} tcpproto.cf; \ + install -o root -g wheel -m 644 tcpproto.cf \ + ${DESTDIR}/etc/sendmail.cf) + (cd ../; \ + install -c -o root -g wheel -m 444 ${FREEBSD} ${DESTDIR}/) + (cd ..; ${MAKE} mdec; ) + (cd ../share/man; ${MAKE} makedb; ) .if ${MACHINE} == "tahoe" (cd etc.tahoe; install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${WCS1} \ ${DESTDIR}/) @@ -73,4 +175,297 @@ hcx9-distribution: (cd etc.tahoe; install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${WCS2} \ ${DESTDIR}/) +kcopy-kernels: ../sys/i386/conf/GENERICAH ../sys/i386/conf/GENERICBT + (cd ../sys/compile; rm -rf GENERICAH GENERICBT) + (cd ../sys/i386/conf; config GENERICAH; config GENERICBT) + (cd ../sys/compile/GENERICAH; ${MAKE} depend; ${MAKE} all; \ + install -c -o root -g wheel -m 755 386bsd \ + ${DESTDIR}/386bsd.GENERICAH) + (cd ../sys/compile/GENERICBT; ${MAKE} depend; ${MAKE} all; \ + install -c -o root -g wheel -m 755 386bsd \ + ${DESTDIR}/386bsd.GENERICBT) + +kcopy-floppy: + disklabel -w -r ${FLOPPY} ${FLOPPY_TYPE} \ + /usr/mdec/fdboot /usr/mdec/bootfd + newfs -b 4096 -c 80 -f 512 -i 16384 -m 0 -o space \ + r${FLOPPY}a ${FLOPPY_TYPE} + mount /dev/${FLOPPY}a ${MOUNT} + chown root.wheel ${MOUNT}/. + chmod 755 ${MOUNT}/. + (cd ${DESTDIR}/; \ + ls -d ${KC_DIRS} | cpio -pdamuv ${MOUNT}) + (cd ${MOUNT}/dev; \ + sh ${DESTDIR}/dev/MAKEDEV std; \ + rm -rf fd; \ + sh ${DESTDIR}/dev/MAKEDEV fd0 wd0 sd0 sd1) + (cd ${DESTDIR}/; \ + ls ${KC_FILES} | cpio -pdamuv ${MOUNT}) + install -c -o root -g wheel -m 755 etc.i386/kc.profile \ + ${MOUNT}/.profile + +kcopy-ah-floppy: + ${MAKE} kcopy-floppy + (cd ../sys/compile/GENERICAH; \ + install -c -o root -g wheel -m 755 386bsd ${MOUNT}/) + df -ik ${MOUNT} + umount /dev/${FLOPPY}a + fsck /dev/r${FLOPPY}a + dd if=/dev/r${FLOPPY}a of=${RELEASEDIR}/floppies/kcopy-ah-floppy \ + bs=15b count=160 + gzip --no-name -9 -c ${RELEASEDIR}/floppies/kcopy-ah-floppy \ + >${RELEASEDIR}/floppies/kcopy-ah-floppy.gz + +kcopy-bt-floppy: + ${MAKE} kcopy-floppy + (cd ../sys/compile/GENERICBT; \ + install -c -o root -g wheel -m 755 386bsd ${MOUNT}/) + df -ik ${MOUNT} + umount /dev/${FLOPPY}a + fsck /dev/r${FLOPPY}a + dd if=/dev/r${FLOPPY}a of=${RELEASEDIR}/floppies/kcopy-bt-floppy \ + bs=15b count=160 + gzip --no-name -9 -c ${RELEASEDIR}/floppies/kcopy-bt-floppy \ + >${RELEASEDIR}/floppies/kcopy-bt-floppy.gz + +filesystem-floppy: + disklabel -w -r ${FLOPPY} ${FLOPPY_TYPE} \ + /usr/mdec/fdboot /usr/mdec/bootfd + newfs -b 4096 -c 80 -f 512 -i 10240 -m 0 -o space \ + r${FLOPPY}a ${FLOPPY_TYPE} + mount /dev/${FLOPPY}a ${MOUNT} + chown root.wheel ${MOUNT}/. + chmod 755 ${MOUNT}/. + (cd ${DESTDIR}/; \ + ls -d ${FILESYSTEM_DIRS} | cpio -pdamuv ${MOUNT}) + (cd ${MOUNT}/dev; \ + sh ${DESTDIR}/dev/MAKEDEV std; \ + rm -rf fd; \ + sh ${DESTDIR}/dev/MAKEDEV fd0 fd1 wd0 sd0 sd1; \ + rm -f rfd1[b-z] fd1[b-z]) + (cd ${DESTDIR}/; \ + ls ${FILESYSTEM_FILES} | cpio -pdamuv ${MOUNT}) + install -c -o root -g wheel -m 755 etc.i386/inst1.profile \ + ${MOUNT}/.profile + install -c -o root -g wheel -m 755 etc.i386/inst1.install \ + ${MOUNT}/install + (cd ${MOUNT}/; \ + ls ${FILESYSTEM_FILES} >/tmp/filelist; \ + ls -d ${FILESYSTEM_DIRS} >>/tmp/filelist; \ + find ${FILESYSTEM_TREES} >>/tmp/filelist; \ + sort -u /tmp/filelist >filelist; \ + rm /tmp/filelist) + df -ik ${MOUNT} + umount /dev/${FLOPPY}a + fsck /dev/r${FLOPPY}a + dd if=/dev/r${FLOPPY}a of=${RELEASEDIR}/floppies/filesystem-floppy \ + bs=15b count=160 + gzip --no-name -9 -c ${RELEASEDIR}/floppies/filesystem-floppy \ + >${RELEASEDIR}/floppies/filesystem-floppy.gz + +cpio-floppy: + disklabel -w -r ${FLOPPY} ${FLOPPY_TYPE} \ + /usr/mdec/fdboot /usr/mdec/bootfd + newfs -b 4096 -c 80 -f 512 -i 65536 -m 0 -o space \ + r${FLOPPY}a ${FLOPPY_TYPE} + mount /dev/${FLOPPY}a ${MOUNT} + chown root.wheel ${MOUNT}/. + chmod 755 ${MOUNT}/. + (cd ${DESTDIR}/; \ + ls ${CPIO_FILES} | cpio -pdamuv ${MOUNT}) + (cd ${DESTDIR}/; \ + (find ${CPIO_CPIO}; ls -d ${CPIO_CPIO_DIRS}) | \ + cpio -oav | gzip -9 >${MOUNT}/inst2.cpio.gz) + install -c -o root -g wheel -m 755 etc.i386/inst2.profile \ + ${MOUNT}/.profile + install -c -o root -g wheel -m 755 etc.i386/inst2.install \ + ${MOUNT}/install + df -ik ${MOUNT} + umount /dev/${FLOPPY}a + fsck /dev/r${FLOPPY}a + dd if=/dev/r${FLOPPY}a of=${RELEASEDIR}/floppies/cpio-floppy \ + bs=15b count=160 + gzip --no-name -9 -c ${RELEASEDIR}/floppies/cpio-floppy \ + >${RELEASEDIR}/floppies/cpio-floppy.gz + +bin-tarball: + (cd ${DESTDIR}; \ + tar cf - . | \ + gzip --no-name -9 -c | \ + split -b 240640 - \ + ${RELEASEDIR}/tarballs/bin_tgz.) + +srcbase-tarball: + (cd ${DESTDIR}; \ + tar -cf - usr/src/[A-Z]* | \ + gzip --no-name -9 -c | \ + split -b 240640 - \ + ${RELEASEDIR}/tarballs/srcbase_tgz.) + +srcbin-tarball: + (cd ${DESTDIR}; \ + tar -cf - usr/src/bin | \ + gzip --no-name -9 -c | \ + split -b 240640 - \ + ${RELEASEDIR}/tarballs/srcbin_tgz.) + +srccontrib-tarball: + (cd ${DESTDIR}; \ + tar -cf - usr/src/contrib | \ + gzip --no-name -9 -c | \ + split -b 240640 - \ + ${RELEASEDIR}/tarballs/srccontrib_tgz.) + +srcetc-tarball: + (cd ${DESTDIR}; \ + tar -cf - usr/src/etc | \ + gzip --no-name -9 -c | \ + split -b 240640 - \ + ${RELEASEDIR}/tarballs/srcetc_tgz.) + +srcgames-tarball: + (cd ${DESTDIR}; \ + tar -cf - usr/src/games | \ + gzip --no-name -9 -c | \ + split -b 240640 - \ + ${RELEASEDIR}/tarballs/srcgames_tgz.) + +srcgnu-tarball: + (cd ${DESTDIR}; \ + tar -cf - usr/src/gnu | \ + gzip --no-name -9 -c | \ + split -b 240640 - \ + ${RELEASEDIR}/tarballs/srcgnu_tgz.) + +srcinclude-tarball: + (cd ${DESTDIR}; \ + tar -cf - usr/src/include | \ + gzip --no-name -9 -c | \ + split -b 240640 - \ + ${RELEASEDIR}/tarballs/srcinclude_tgz.) + +srclib-tarball: + (cd ${DESTDIR}; \ + tar --exclude usr/src/${CRYPT_LIB} -cf - usr/src/lib | \ + gzip --no-name -9 -c | \ + split -b 240640 - \ + ${RELEASEDIR}/tarballs/srclib_tgz.) + +srclibcrypt-tarball: + (cd ${DESTDIR}; \ + tar -cf - usr/src/${CRYPT_LIB} | \ + gzip --no-name -9 -c | \ + split -b 240640 - \ + ${RELEASEDIR}/tarballs/srclibcrypt_tgz.) + +srclibexec-tarball: + (cd ${DESTDIR}; \ + tar -cf - usr/src/libexec | \ + gzip --no-name -9 -c | \ + split -b 240640 - \ + ${RELEASEDIR}/tarballs/srclibexec_tgz.) + +srcsbin-tarball: + (cd ${DESTDIR}; \ + tar -cf - usr/src/sbin | \ + gzip --no-name -9 -c | \ + split -b 240640 - \ + ${RELEASEDIR}/tarballs/srcsbin_tgz.) + +srcshare-tarball: + (cd ${DESTDIR}; \ + tar -cf - usr/src/share | \ + gzip --no-name -9 -c | \ + split -b 240640 - \ + ${RELEASEDIR}/tarballs/srcshare_tgz.) + +srcsys-tarball: + (cd ${DESTDIR}; \ + tar -cf - usr/src/sys | \ + gzip --no-name -9 -c | \ + split -b 240640 - \ + ${RELEASEDIR}/tarballs/srcsys_tgz.) + +srcusrbin-tarball: + (cd ${DESTDIR}; \ + tar -cf - usr/src/usr.bin | \ + gzip --no-name -9 -c | \ + split -b 240640 - \ + ${RELEASEDIR}/tarballs/srcusrbin_tgz.) + +srcusrsbin-tarball: + (cd ${DESTDIR}; \ + tar -cf - usr/src/usr.sbin | \ + gzip --no-name -9 -c | \ + split -b 240640 - \ + ${RELEASEDIR}/tarballs/srcusrsbin_tgz.) + +src-tarball: srcbase-tarball srcbin-tarball srccontrib-tarball srcetc-tarball \ + srcgames-tarball srcgnu-tarball srcinclude-tarball srclib-tarball \ + srclibcrypt-tarball srclibexec-tarball srcsbin-tarball \ + srcshare-tarball srcsys-tarball srcusrbin-tarball \ + srcusrsbin-tarball + +des-tarball: + rm -rf ${RELEASEDIR}/tmpdes + mkdir ${RELEASEDIR}/tmpdes + for i in ${CRYPT_DIRS}; do \ + cd ${RELEASEDIR}/tmpdes; \ + mkdir $$i; \ + chown ${BINOWN}.${GRPOWN} $$i; \ + chmod 755 $$i; \ + done + # This is ugly, it force installs a /usr/lib/libcrypt.a so + # that the other makes will be built with des. + # + (cd ${.CURDIR}/../${CRYPT_LIB}; \ + unset NOCRYPT; \ + DESTDIR=; export DESTDIR; \ + ${MAKE} cleandir obj depend all install; \ + NOMAN=noman; export NOMAN; \ + DESTDIR=${RELEASEDIR}/tmpdes; export DESTDIR; \ + ${MAKE} cleandir obj depend all install) + for i in ${CRYPT_SRCS}; do \ + unset NOCRYPT; \ + DESTDIR=${RELEASEDIR}/tmpdes; export DESTDIR; \ + NOMAN=noman; export NOMAN; \ + cd ${.CURDIR}/../$$i; \ + ${MAKE} cleandir obj depend all install; \ + done + (cd ${RELEASEDIR}/tmpdes; \ + tar cf - . | \ + gzip --no-name -9 -c | \ + split -b 240640 - \ + ${RELEASEDIR}/tarballs/des_tgz.) + rm -rf ${RELEASEDIR}/tmpdes + +distrib-dirs: + mtree -u -f mtree/BSD.root.dist -p ${DESTDIR}/ + mtree -u -f mtree/BSD.var.dist -p ${DESTDIR}/var + mtree -u -f mtree/BSD.usr.dist -p ${DESTDIR}/usr +.if defined(CDROMDIST) + mtree -u -f mtree/BSD.local.dist -p ${DESTDIR}/usr/local +.endif + cd ${DESTDIR}/; rm -f ${DESTDIR}/sys; ln -s usr/src/sys sys + +floppies: kcopy-ah-floppy kcopy-bt-floppy filesystem-floppy \ + cpio-floppy + +release: release-dirs distribution kcopy-kernels floppies \ + bin-tarball des-tarball + +release-dirs: + rm -rf ${RELEASEDIR}/filesystem + mkdir ${RELEASEDIR}/filesystem + chown root.wheel ${RELEASEDIR}/filesystem + chmod 755 ${RELEASEDIR}/filesystem + rm -rf ${RELEASEDIR}/tarballs + mkdir ${RELEASEDIR}/tarballs + chown root.wheel ${RELEASEDIR}/tarballs + chmod 755 ${RELEASEDIR}/tarballs + rm -rf ${RELEASEDIR}/floppies + mkdir ${RELEASEDIR}/floppies + chown root.wheel ${RELEASEDIR}/floppies + chmod 755 ${RELEASEDIR}/floppies + .include diff --git a/etc/aliases b/etc/aliases index 7f23c1d18b..0b5ed72c7e 100644 --- a/etc/aliases +++ b/etc/aliases @@ -23,28 +23,8 @@ system: root toor: root uucp: root -# Well-known aliases -root: -manager: -dumper: -operator: - -# OFFICIAL CSRG/BUG ADDRESSES - -# FTP BUG ADDRESS -ftp-bugs: bigbug@ucbvax.berkeley.edu - -# DISTRIBUTION PERSON -bsd-dist: bsd-dist@ucbvax.berkeley.edu - -# FORTUNE -fortune: fortune@ucbvax.berkeley.edu - -# TERMCAP -termcap: bigbug@ucbvax.berkeley.edu - -# BUG PERSON -ucb-fixes: bigbug@ucbvax.berkeley.edu -ucb-fixes-request: bigbug@ucbvax.berkeley.edu -bugs: bugs@ucbvax.berkeley.edu -# END OFFICIAL BUG ADDRESSES +# Well-known aliases -- these should be filled in! +# root: +# manager: +# dumper: +# operator: diff --git a/etc/crontab b/etc/crontab index f70ff21df6..ef8eafcfec 100644 --- a/etc/crontab +++ b/etc/crontab @@ -1,7 +1,20 @@ -0,15,30,45 * * * * root /usr/libexec/atrun -#0 1 * * * daemon /usr/contrib/news/daily -#0 0 * * * uucp /etc/uucp.daily -#7 5,12,18 * * * uucp /etc/uucp.6hours -0 2 * * * root /bin/sh /etc/daily 2>&1 | tee /var/log/daily.out | mail -s "daily output" root -30 3 * * 6 root /bin/sh /etc/weekly 2>&1 | tee /var/log/weekly.out | mail -s "weekly output" root -30 5 1 * * root /bin/sh /etc/monthly 2>&1 | tee /var/log/monthly.out | mail -s "monthlyoutput" root +# /var/cron/tabs/root - root's crontab for FreeBSD +# +# $Id$ +# From: Id: crontab,v 1.6 1993/05/31 02:03:57 cgd Exp +# +SHELL=/bin/sh +PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin +HOME=/var/log +# +#minute hour mday month wday command +# +#0/15 * * * * /usr/libexec/atrun +# +# rotate log files every hour, if necessary +#0 * * * * /usr/bin/newsyslog +# +# do daily/weekly/monthly maintenance +0 2 * * * /etc/daily +30 3 * * 6 /etc/weekly +30 5 1 * * /etc/monthly diff --git a/etc/csh.login b/etc/csh.login index 0b573ff50d..493a1bbfe2 100644 --- a/etc/csh.login +++ b/etc/csh.login @@ -1 +1,4 @@ # System-wide .login file for csh(1). +# Uncomment this to give you the default 4.2 behavior, where disk +# information is shown in K-Blocks +# setenv BLOCKSIZE K diff --git a/etc/daily b/etc/daily index 0f459ac1a5..bddf22f42a 100644 --- a/etc/daily +++ b/etc/daily @@ -50,13 +50,13 @@ if [ -f /etc/news.expire ]; then /etc/news.expire fi -echo "" -echo "Purging accounting records:" -mv /var/account/acct.2 /var/account/acct.3 -mv /var/account/acct.1 /var/account/acct.2 -mv /var/account/acct.0 /var/account/acct.1 -cp /var/account/acct /var/account/acct.0 -sa -s > /dev/null +#echo "" +#echo "Purging accounting records:" +#mv /var/account/acct.2 /var/account/acct.3 +#mv /var/account/acct.1 /var/account/acct.2 +#mv /var/account/acct.0 /var/account/acct.1 +#cp /var/account/acct /var/account/acct.0 +#sa -s > /dev/null echo "" echo "Backup passwd and group files:" @@ -126,7 +126,7 @@ mailq if [ -d /var/spool/uucp ]; then echo "" echo "uucp:" - uusnap + uustat -a fi echo "" @@ -145,4 +145,4 @@ if [ -f /etc/Distfile ]; then rdist -f /etc/Distfile fi -sh /etc/security | mail -s "daily insecurity output" root +sh /etc/security 2>&1 | mail -s "daily insecurity output" root diff --git a/etc/etc.i386/MAKEDEV b/etc/etc.i386/MAKEDEV index 00fc16288a..2f910f498b 100644 --- a/etc/etc.i386/MAKEDEV +++ b/etc/etc.i386/MAKEDEV @@ -22,76 +22,119 @@ # @(#)MAKEDEV 5.2 (Berkeley) 6/22/90 # # Device "make" file. Valid arguments: +# all makes all known devices, standard number of units (or close) # std standard devices # local configuration specific devices # # Tapes: # wt* QIC-interfaced (e.g. not SCSI) 3M cartridge tape +# st* "NEW type scsi tapes" (old driver uses the +# block devices of the disks to get access) # # Disks: # wd* "winchester" disk drives (ST506,IDE,ESDI,RLL,...) # fd* "floppy" disk drives (3 1/2", 5 1/4") -# as* "SCSI" disk/tape/CDROM drives +# sd* "scsi disks" +# cd* "cdrom disks" +# +# Console ports: +# pc* devices for stock pccons +# vty* virtual console devices for syscons/pcvt/codrv +# +# Pointing devices: +# mse* Logitech and ATI Inport bus mouse +# psm* PS/2 mouse # # Terminal ports: -# com* standard PC COM ports +# com* standard PC COM ports (really makes tty* entries for com) +# sio* fast interrupt PC COM ports (really makes tty* entries for sio) +# tty* alias for sio ports, this is what the system really wants # # Pseudo terminals: # pty* set of 16 master and slave pseudo terminals +# vty* virtual terminals using syscons/pcvt/codrv console # # Printers: +# lpt* stock lp +# lpa* interruptless lp # # Call units: # # Special purpose devices: -# flog* kernel logging device +# bpf* packet filter +# dcf* dcf clock +# speaker pc speaker +# tw* xten power controller +# snd* various sound cards +# +# $Id: MAKEDEV,v 1.12 1993/10/23 10:58:51 jkh Exp $ # -PATH=/sbin:/bin/:/usr/bin +PATH=/sbin:/bin/:/usr/bin:/usr/sbin: umask 77 for i do case $i in +all) + sh MAKEDEV std # standard + sh MAKEDEV wd0 wd1 fd0 fd1 wt0 sd0 sd1 st0 cd0 # bdev + sh MAKEDEV pty0 tty0 tty1 tty2 tty3 pc0 lpt0 lpt1 lpt2 # cdev + sh MAKEDEV ch0 tw0 bpf0 dcf0 lpa0 lpa1 lpa2 # cdev + sh MAKEDEV speaker mse0 sio0 sio1 sio2 sio3 # cdev + # NOTE: co0 and vty04 are not done by a "sh MAKEDEV all" + # these are for codrv and interfere with other devices! - rgrimes + ;; std) - rm -f console drum mem kmdem null tty klog stdin stdout stderr - mknod console c 0 0 - mknod drum c 4 0 ; chmod 640 drum ; chgrp kmem drum - mknod kmem c 2 1 ; chmod 640 kmem ; chgrp kmem kmem - mknod mem c 2 0 ; chmod 640 mem ; chgrp kmem mem - mknod null c 2 2 ; chmod 666 null - mknod tty c 1 0 ; chmod 666 tty - mknod klog c 7 0 ; chmod 600 klog - mknod stdin c 53 0 ; chmod 666 stdin - mknod stdout c 53 1 ; chmod 666 stdout - mknod stderr c 53 2 ; chmod 666 stderr + rm -f console drum mem kmem null tty klog stdin stdout stderr + mknod console c 0 0; chmod 600 console; chown root.wheel console + mknod drum c 4 0; chmod 640 drum; chown root.kmem drum + mknod kmem c 2 1; chmod 640 kmem; chown root.kmem kmem + mknod mem c 2 0; chmod 640 mem; chown root.kmem mem + mknod null c 2 2; chmod 666 null; chown root.wheel null + mknod zero c 2 12; chmod 666 zero; chown root.wheel zero + mknod io c 2 14; chmod 640 io; chown root.kmem io + mknod tty c 1 0; chmod 666 tty; chown root.wheel tty + mknod klog c 7 0; chmod 600 klog; chown root.wheel klog + mknod stdin c 22 0; chmod 666 stdin; chown root.wheel stdin + mknod stdout c 22 1; chmod 666 stdout; chown root.wheel stdout + mknod stderr c 22 2; chmod 666 stderr; chown root.wheel stderr rm -f fd/* mkdir fd > /dev/null 2>&1 (cd fd && eval `echo "" | awk ' BEGIN { \ for (i = 0; i < 64; i++) \ - printf("mknod %d c 53 %d;", i, i)}'`) + printf("mknod %d c 22 %d;", i, i)}'`) chown -R bin.bin fd chmod 555 fd chmod 666 fd/* ;; + +# Create device files for new Archive/Wangtek QIC-02 tape driver (vak) wt*) - umask 2 - mknod wt0 b 3 0 - mknod rwt0 c 10 0 - umask 77 + umask 2 ; u=`expr $i : '..\(.*\)'` + if [ x$u = x ]; then u=0; fi + rm -f rwt$u nrwt$u rwt${u}a nrwt${u}a rwt${u}b nrwt${u}b + mknod rwt${u} c 10 `expr 0 + $u` # 150 megabytes + mknod nrwt${u} c 10 `expr 4 + $u` + mknod rwt${u}a c 10 `expr 8 + $u` # 120 megabytes + mknod nrwt${u}a c 10 `expr 12 + $u` + mknod rwt${u}b c 10 `expr 16 + $u` # 60 megabytes + mknod nrwt${u}b c 10 `expr 20 + $u` + chown root.operator rwt$u nrwt$u rwt${u}a nrwt${u}a rwt${u}b nrwt${u}b + umask 77; ;; -fd*|wd*|as*) +fd*|sd*|wd*) umask 2 ; unit=`expr $i : '..\(.*\)'` case $i in fd*) name=fd; blk=2; chr=9;; + sd*) name=sd; blk=4; chr=13;; wd*) name=wd; blk=0; chr=3;; - as*) name=as; blk=4; chr=13;; esac rm -f $name$unit? r$name$unit? case $unit in - 0|1) + 0|1|2|3|4|5|6) mknod ${name}${unit}a b $blk `expr $unit '*' 8 + 0` mknod ${name}${unit}b b $blk `expr $unit '*' 8 + 1` mknod ${name}${unit}c b $blk `expr $unit '*' 8 + 2` @@ -119,9 +162,10 @@ fd*|wd*|as*) ;; com*) - unit=`expr $i : 'com\(.*\)'` - rm -f com$unit - mknod com$unit c 8 $unit + unit=`expr $i : '...\(.*\)'` + rm -f tty0$unit + mknod tty0$unit c 8 $unit + chown uucp.wheel tty0$unit ;; pty*) @@ -140,9 +184,9 @@ pty*) umask 0 eval `echo $offset $name | awk ' { b=$1; n=$2 } END { for (i = 0; i < 16; i++) - printf("mknod tty%s%x c 5 %d; \ - mknod pty%s%x c 6 %d; ", \ - n, i, b+i, n, i, b+i); }'` + printf("rm -f tty%s%x; mknod tty%s%x c 5 %d; \ + rm -f pty%s%x; mknod pty%s%x c 6 %d; ", \ + n, i, n, i, b+i, n, i, n, i, b+i); }'` umask 77 if [ $class = 1 ]; then mv ttyqf ttyv0; mv ptyqf ptyv0 @@ -151,6 +195,237 @@ pty*) esac ;; +st*) + umask 2 ; unit=`expr $i : '..\(.*\)'` + case $i in + st*) name=st; chr=14;; + esac + rm -f r$name$unit nr$name$unit er$name$unit enr$name$unit + rm -f hr$name$unit hnr$name$unit her$name$unit henr$name$unit + rm -f mr$name$unit mnr$name$unit mer$name$unit menr$name$unit + rm -f lr$name$unit lnr$name$unit ler$name$unit lenr$name$unit + case $unit in + 0|1|2|3|4|5|6) + mknod r${name}${unit} c $chr `expr $unit '*' 16 + 0` + mknod nr${name}${unit} c $chr `expr $unit '*' 16 + 1` + mknod er${name}${unit} c $chr `expr $unit '*' 16 + 2` + mknod hr${name}${unit} c $chr `expr $unit '*' 16 + 4` + mknod hnr${name}${unit} c $chr `expr $unit '*' 16 + 5` + mknod her${name}${unit} c $chr `expr $unit '*' 16 + 6` + mknod mr${name}${unit} c $chr `expr $unit '*' 16 + 8` + mknod mnr${name}${unit} c $chr `expr $unit '*' 16 + 9` + mknod mer${name}${unit} c $chr `expr $unit '*' 16 + 10` + mknod lr${name}${unit} c $chr `expr $unit '*' 16 + 12` + mknod lnr${name}${unit} c $chr `expr $unit '*' 16 + 13` + mknod ler${name}${unit} c $chr `expr $unit '*' 16 + 14` + chgrp operator r${name}${unit} nr${name}${unit} \ + er${name}${unit} + chgrp operator hr${name}${unit} hnr${name}${unit} \ + her${name}${unit} + chgrp operator mr${name}${unit} mnr${name}${unit} \ + mer${name}${unit} + chgrp operator lr${name}${unit} lnr${name}${unit} \ + ler${name}${unit} + chmod 640 r${name}${unit} nr${name}${unit} \ + er${name}${unit} + chmod 640 hr${name}${unit} hnr${name}${unit} \ + her${name}${unit} + chmod 640 mr${name}${unit} mnr${name}${unit} \ + mer${name}${unit} + chmod 640 lr${name}${unit} lnr${name}${unit} \ + ler${name}${unit} + ;; + *) + echo bad unit for tape in: $i + ;; + esac + umask 77 + ;; + +ch*) + umask 2 ; unit=`expr $i : '..\(.*\)'` + case $i in + ch*) name=ch; chr=17;; + esac + rm -f $name$unit + case $unit in + 0|1|2|3|4|5|6) + mknod ${name}${unit} c $chr `expr $unit '*' 16 + 0` + chgrp operator ${name}${unit} + chmod 640 ${name}${unit} + ;; + *) + echo bad unit for media changer in: $i + ;; + esac + umask 77 + ;; + +cd*) + umask 2 ; unit=`expr $i : '..\(.*\)'` + case $i in + cd*) name=cd; blk=6; chr=15;; + esac + rm -f $name$unit? r$name$unit? + case $unit in + 0|1|2|3|4|5|6) + mknod ${name}${unit}a b $blk `expr $unit '*' 8 + 0` + mknod ${name}${unit}d b $blk `expr $unit '*' 8 + 3` + mknod r${name}${unit}a c $chr `expr $unit '*' 8 + 0` + mknod r${name}${unit}d c $chr `expr $unit '*' 8 + 3` + chgrp operator ${name}${unit}[a-h] r${name}${unit}[a-h] + chmod 640 ${name}${unit}[a-h] r${name}${unit}[a-h] + ;; + *) + echo bad unit for disk in: $i + ;; + esac + umask 77 + ;; + +lpt*) + unit=`expr $i : 'lpt\(.*\)'` + rm -f lpt$unit + mknod lpt$unit c 16 $unit + chown root.wheel lpt$unit + ;; + +tw*) + unit=`expr $i : 'tw\(.*\)'` + rm -f tw$unit + mknod tw$unit c 19 $unit + chown root.wheel tw$unit + ;; + +# hv 22-apr-93 use this to create the necessary video device for +# pccons driver +pc*) + chr=12 + minor=0 + name=vga + rm -f ${name} kbd kbdco vgaco ttyv[0-9][0-9] + mknod ${name} c ${chr} ${minor} + chown root.wheel $name + ;; + +# Use this to create virtual consoles for syscons, pcvt or codrv +# /dev/ttyv0-b +# use as MAKEDEV vtyNN to create NN entries +vty*) + chr=12 + units=`expr $i : 'vty\(.*\)'` + umask 0 + eval `echo ${chr} ${units} | awk ' { c=$1; n=$2 } END { + for (i = 0; i < n; i++) + printf("rm -f ttyv%01x; mknod ttyv%01x c %d %d;", \ + i, i, c, i); }'` + umask 77 + ;; + +bpf*) + unit=`expr $i : 'bpf\(.*\)'` + rm -f bpf$unit + mknod bpf$unit c 23 $unit + chown root.wheel bpf$unit + ;; + +dcf*) + unit=`expr $i : 'dcf\(.*\)'` + rm -f dcf$unit + mknod dcf$unit c 24 $unit + chown root.wheel dcf$unit + ;; + +lpa*) + unit=`expr $i : 'lpa\(.*\)'` + chr=25 + rm -f lpa$unit lpa${unit}p + mknod lpa${unit} c $chr `expr $unit + 0` + mknod lpa${unit}p c $chr `expr $unit + 32` + chown root.wheel lpa${unit} lpa${unit}p + ;; + +speaker) + rm -f speaker + mknod speaker c 26 0 + chown root.wheel speaker + ;; + +sio*|tty*) + unit=`expr $i : '...\(.*\)'` + rm -f tty0$unit + mknod tty0$unit c 28 $unit + chown uucp.wheel tty0$unit + ;; + +mse*) + unit=`expr $i : 'mse\(.*\)'` + chr=27 + rm -f mse$unit + mknod mse$unit c $chr `expr $unit '*' 2 + 1` # non-blocking for X11 + chown root.wheel mse$unit + ;; + +psm*) + unit=`expr $i : 'psm\(.*\)'` + chr=21 + rm -f psm$unit + mknod psm$unit c $chr `expr $unit '*' 2 + 1` # non-blocking for X11 + chown root.wheel psm$unit + ;; + +mouse*) + name=`expr $i : 'mouse-\(.*\)'` + if [ ! -c $name ]; then + $0 $name # make the appropriate device + fi + rm -f mouse + ln -s $name mouse + ;; + + +snd*) + chr=30 + rm -f /dev/mixer # Mixer [ Control ] + mknod /dev/mixer c $chr 0 + chmod 666 /dev/mixer + + rm -f /dev/sequencer # Sequencer [ FM Synth and MIDI output ] + mknod /dev/sequencer c $chr 1 + chmod 666 /dev/sequencer + + rm -f /dev/midi # Midi input [ Not implemented ] + mknod /dev/midi c $chr 2 + + rm -f /dev/dsp # DSP [ Digitized voice ] + mknod /dev/dsp c $chr 3 + chmod 666 /dev/dsp + + rm -f /dev/audio # SPARC audio [ Not fully implemented ] + mknod /dev/audio c $chr 4 + chmod 666 /dev/audio + + rm -f /dev/dsp16 # DSP16 [ Same as /dev/dsp, except 16 bits ] + mknod /dev/dsp16 c $chr 5 + chmod 666 /dev/dsp16 + + rm -f /dev/sndstat # Status Device [ Debugging interface ] + mknod /dev/sndstat c $chr 6 + chmod 666 /dev/sndstat + + rm -f /dev/pro_midi # PRO_MIDI [PAS midi input and output] + mknod /dev/pro_midi c $chr 15 + chmod 666 /dev/pro_midi + + rm -f /dev/dsp1 # DSP 1 [ SB emulation of PAS16 or 2nd audio ] + mknod /dev/dsp1 c $chr 19 + chmod 666 /dev/dsp1 + + rm -f /dev/audio1 # Sparc Audio 1 [ SB emulation of PAS16 or 2nd audio ] + mknod /dev/audio1 c $chr 20 + chmod 666 /dev/audio1 + ;; + local) umask 0 sh MAKEDEV.local diff --git a/etc/etc.i386/disktab b/etc/etc.i386/disktab index 61a9c09588..150941352f 100644 --- a/etc/etc.i386/disktab +++ b/etc/etc.i386/disktab @@ -6,7 +6,7 @@ # ns #sectors/track # nt #tracks/cylinder # nc #cylinders/disk -# sc #sectors/cylinder, nc*nt default +# sc #sectors/cylinder, ns*nt default # su #sectors/unit, sc*nc default # se sector size, DEV_BSIZE default # rm rpm, 3600 default @@ -49,12 +49,29 @@ mk156|toshiba156|Toshiba MK156 156Mb:\ :ph#257250:oh#31500:bh#4096:fh#512:th=4.2BSD: cp3100|Connor Peripherals 100MB IDE:\ - :dt=ST506:ty=winchester:se#512:nt#8:ns#33:nc#766:sf: \ + :dt=ST506:ty=winchester:se#512:nt#8:ns#33:nc#766: \ :pa#12144:oa#0:ta=4.2BSD:ba#4096:fa#512: \ :pb#12144:ob#12144:tb=swap: \ :pc#202224:oc#0: \ :ph#177936:oh#24288:th=4.2BSD:bh#4096:fh#512: +# a == root +# b == swap +# c == d == whole disk +# e == /var +# f == scratch +# h == /usr + +cp3100new|Connor Peripherals 100MB IDE, with a different configuration:\ + :dt=ST506:ty=winchester:se#512:nt#8:ns#33:nc#766: \ + :pa#15840:oa#0:ta=4.2BSD:ba#4096:fa#512: \ + :pb#24288:ob#15840:tb=swap: \ + :pc#202224:oc#0: \ + :pd#202224:od#0: \ + :pe#15840:oe#40128:te=4.2BSD:be#4096:fe#512: \ + :pg#15840:og#55968:tg=4.2BSD:bg#4096:fg#512: \ + :ph#130416:oh#71808:th=4.2BSD:bh#4096:fh#512: + floppy|floppy3|3in|3.5in High Density Floppy:\ :ty=floppy:se#512:nt#2:rm#300:ns#18:nc#80:\ :pa#2880:oa#0:ba#4096:fa#512:\ @@ -63,6 +80,7 @@ floppy|floppy3|3in|3.5in High Density Floppy:\ floppy5|5in|5.25in High Density Floppy:\ :ty=floppy:se#512:nt#2:rm#300:ns#15:nc#80:\ + :pa#2400:oa#0:ba#4096:fa#512: :pb#2400:ob#0:bb#4096:fb#512: :pc#2400:oc#0:bc#4096:fc#512: diff --git a/etc/etc.i386/floppy.install_notes b/etc/etc.i386/floppy.install_notes new file mode 100644 index 0000000000..598371005d --- /dev/null +++ b/etc/etc.i386/floppy.install_notes @@ -0,0 +1,153 @@ + FLOPPY INSTALLATION NOTES + FreeBSD + Release 1.0 + +Welcome to FreeBSD! This document has been put together in an effort +to make initial installation of the system from floppy as easy as possible. + +1. To install FreeBSD you will need 3 (or 4 if you choose to add the optional + DOS floppy) floppies, as well as the bulk of the distribution on some + other medium (floppy, tape, CD, etc). If you've retrieved this release + from the net, you'll first have to make the floppies yourself using + the supplied images. + + Due to the differences in PC configurations, we've found it necessary + to provide multiple initial boot images that provide kernels for + different types of systems. + + If your disk controller is one of: + + MFM / RLL / IDE / ST506 + Adaptec 154x series + Adaptec 174x series + Buslogic 545S + + Then please use the disk image: kcopy-ah-floppy + to construct your boot floppy. + + If your disk controller is one of: + + Bustek 742a + UltraStore 14F or 34F + + Then please use the disk image: kcopy-bt-floppy + to construct your boot floppy. + + Next, make a second floppy from the disk image: filesystem-floppy + You'll need this for the second stage of the boot process. + + Finally, make a third floppy from the disk image: cpio-floppy + You'll need this for the last stage of the boot process. + + If you want to use any of the optional tools in the tools + subdirectory of the ftp distribution site, these should be + copied directly to a DOS formatted disk (using, either mcopy + or mount -t pcfs). This disk is referred to later as the + optional "dos" floppy. + + If installing more than one operating system on a disk, then + it is recommended that the dos floppy at least include the + os-bs boot manager. If downloading files via a modem and SLIP + is not available, then the dos floppy should include kermit. + You'll have the option of loading the programs that are on + the dos floppy in the last stage of the boot process. + +2. Boot the first floppy. When it asks you to insert the file system floppy, + insert the second floppy ``filesystem-floppy.'' Follow the instructions + that floppy gives you. If partitions already exist on the hard disk, + then by default FreeBSD attempts to install itself at the end of these. + Before rebooting, note the type of disk it says to copy the kernel + to: ``sd0a'' or ``wd0a'' (``sd0a'' is for SCSI systems, ``wd0a'' is + for all others.) When the system halts, go on to the next step. + +3. Boot the first floppy again, but this time when it asks + you to insert the file system floppy, just press the return key. + Follow the instructions that the floppy gives you. When you see + the ``kc>'' prompt, type ``copy'' (without quotes). At the next prompt, + ``copy kernel to>'', type either ``sd0a'' or ``wd0a'' as given in + the previous step. When the system halts, go on to the next step. + +4. Making sure that there's no floppy in the drive, press return to boot + from the hard disk. After it has booted and is asking what drive the + cpio floppy is in, insert the third floppy ``cpio-floppy'' into a + floppy drive and answer the question about what drive it is in. + Note that 0 is the same as DOS drive A:, and 1 is the same as DOS + drive B: + +5. After the cpio-floppy has been copied to the disk, remove it from the + drive. If there are programs on the dos-floppy that you would like + installed, then insert this disk in a floppy drive, again specifying + the drive to read from. + +6. After the cpio (or optional dos) floppy has been copied to the disk, + enter `halt' at the command prompt. + +7. When the system asks you to press the return key to reboot, first + remove the floppy and then press the return key to boot from the hard + disk. + +8. At this point you will get 4 errors from the fsck on boot, these + are normal and are caused by files that were open when the + /dev entries were built - just ignore them. The system will + correct these errors and then halt, after which you should press + the return key again to reboot with a clean system. + +9. Congratulations, you've got the mini FreeBSD system on your disk! + +10. Follow the instructions about set_tmp_dir and extract that + will come on your screen after you've pressed the return key. + +11. You will get the following errors while extracting the bin + distribution, which you can safely ignore: + + /tmp/tar: Could not create file bin/sh : Text file busy + /tmp/tar: Could not create file sbin/init : Text file busy + /tmp/tar: Could not link .profile to root/.profile : File exists + +12. Run the configure command to set up some of the /etc files by + typing ``configure''. You will have to edit /etc/netstart after + this if you have a networking interface. + +13. Reboot so that the system comes up multiuser by typing ``reboot''. + +14. You are now running FreeBSD! Congradulations! You may now continue + with installing the source distribution, or stop here for now. + + Should you decided to postpone further installation, you should + probably save the ``installing profile'' for possible future use by + saving it and linking a default profile into place. + + Do it like this: + + mv /.profile /.profile.install + ln /root/.profile /.profile + +15. If your disk has several operating systems, you may want to + install the Thomas Wolfram's os-bs boot manager for selecting + which system to boot. This works well with DOS, OS/2, FreeBSD + and other systems. To install it, boot the system with MS-DOS + and insert the dos-floppy of the FreeBSD install suite in + floppy drive A:. Then enter the DOS commands: + > A: + > os-bs135 + > cd os-bs + > os-bs + A menu should now appear on the screen. Use the cursor keys + to highlight the install option and hit ENTER. Simply follow the + instructions from there. + + For more information about the ob-bs program, including its + capabilities and limitations, see the file `readme.1st' in the + os-bs directory. + + If you choose not to install os-bs, then fdisk can be used to + change the boot system. This is done by making the primary + partition for the boot system active. FreeBSD has an fdisk + command that can be used for this purpose as well. + +16. In addition to the FreeBSD source and binary distributions, many + additional packages, such as X11 and TeX, may be obtained from + freebsd.cdrom.com - please have a look around! You may also find + this a good time to read the release notes in RELNOTES.FreeBSD. + +End of $Id: floppy.install_notes,v 1.11 1993/10/16 12:22:45 rgrimes Exp $ diff --git a/etc/etc.i386/inst1.install b/etc/etc.i386/inst1.install new file mode 100644 index 0000000000..cd6626bbb0 --- /dev/null +++ b/etc/etc.i386/inst1.install @@ -0,0 +1,1015 @@ +#!/bin/sh +# install1.fs disk 'install' +# +# Currently, no method for checking to see if the designated disk type is +# already in /etc/disktab. You can edit it out of the file after installation. +# +PATH=/sbin:/bin:/usr/bin:/usr/sbin:. +export PATH + +OPSYSTEM=FreeBSD +OPSYSID=165 +ROOTMIN=7 +SWAPMIN=8 +USRMIN=7 +DISKMIN=`expr $ROOTMIN + $SWAPMIN + 1` +DEFBLOCKING=2 +DEFSECT=17 +DEFHEAD=12 +DEFCYLN=1024 +RUN_FDISK="" + +DOS1_ID=1 +DOS2_ID=4 +DOS3_ID=6 + +set_arbitrary_defaults() { +cyls_per_disk=$DEFCYLN +tracks_per_cyl=$DEFHEAD +sects_per_track=$DEFSECT +unused_last_part=3 +part_cnt=4 +} + + +get_fdisk_data() { +cyls_per_disk= +part_id= +got_sysid= +part_cnt=0 +sysid_cnt=0 +have_opsys_part= +have_dos_part= +unused_last_part= +extent_max=0 +extent_max_part= + +fdisk /dev/r${drivename}d >fdisk.out 2>fdisk.err +if [ $? -gt 0 ]; then + echo "Can't open /dev/r${drivename}d for reading!" + set_arbitrary_defaults + >fdisk.out + >fdisk.err + return 2 +elif [ -s fdisk.err ]; then + echo "Disk doesn't appear to be initialized..." + no_part_table=1 +fi +while read data; do + if [ ! "$cyls_per_disk" ]; then + cyls_per_disk=`expr "$data" : '[^=]*=\([0-9]*\)'` + tracks_per_cyl=`expr "$data" : '[^=]*=[^=]*=\([0-9]*\)'` + sects_per_track=`expr "$data" : '[^=]*=[^=]*=[^=]*=\([0-9]*\)'` + continue + fi + if [ "$got_sysid" ]; then + start_part=`expr "$data" : '[^0-9]*\([0-9]*\)'` + size_part=`expr "$data" : '[^0-9]*[0-9]*[^0-9]*\([0-9]*\)'` + extent_part=`expr $start_part + $size_part` + if [ $extent_part -gt $extent_max ]; then + extent_max=$extent_part + extent_max_part=$part_id + fi + eval start${part_id}=$start_part + eval size${part_id}=$size_part + sysid_cnt=`expr $sysid_cnt + 1` + got_sysid= + part_id= + elif [ "$part_id" ]; then + sysid=`expr "$data" : 'sysid \([0-9]*\)'` + sysname=`expr "$data" : 'sysid[^(]*(\([^)]*\)'` + if [ "$no_part_table" -o "$sysid" = "0" -o \ + "$(expr "$data" : '\(\)')" = "" ]; then + unused_last_part=$part_id + part_id= + continue + fi + if [ "$sysid" = "$OPSYSID" ]; then + have_opsys_part=$part_id + elif [ ! "$have_dos_part" -a \( "$sysid" = "$DOS1_ID" -o \ + "$sysid" = "$DOS2_ID" -o "$sysid" = "$DOS3_ID" \) ]; then + have_dos_part=$part_id + fi + eval sysid${part_id}=$sysid + eval sysname${part_id}=\"$sysname\" + got_sysid=1 + else + part_id=`expr "$data" : 'The data[^0-9]*\([0-9]*\)'` + beg_cyl=`expr "$data" : '[ ]*beg[^0-9]*\([0-9]*\)'` + end_cyl=`expr "$data" : '[ ]*end[^0-9]*\([0-9]*\)'` + if [ "$part_id" ]; then + part_cnt=`expr $part_cnt + 1` + elif [ "${beg_cyl}" -gt "${cyls_per_disk}" -o \ + "${end_cyl}" -gt "${cyls_per_disk}" ]; then + no_part_table=1 + sysid_cnt=0 + have_opsys_part=0 + unused_last_part=`expr $part_cnt - 1` + fi + fi +done fdisk.out +>fdisk.err +return 0 +} + +set_existing_part() { +# Set existing partiton values as default (adjusting to cylinder boundaries) +eval opsys_size=\$size${opsys_part} +eval opsys_start=\$start${opsys_part} +[ $opsys_size -eq 50000 ] && opsys_size=$disksize +opsys_off=`expr $opsys_start / $cylindersize` +opsys_adjusted=`expr $opsys_off \* $cylindersize` +if [ $opsys_adjusted -lt $opsys_start -o $opsys_off -eq 0 ]; then + opsys_off=`expr $opsys_off + 1` + opsys_adjusted=`expr $opsys_off \* $cylindersize` + opsys_size=`expr $opsys_size - $opsys_adjusted + $opsys_start` +fi +cyls_per_opsys=`expr $opsys_size / $cylindersize` +RUN_FDISK="fdisk -u" +} + + +set_overwrite_part() { +while :; do + echo + echo -n "Please specify partition to overwrite: [3] " + read resp junk + opsys_part=${resp:-3} + if [ "$opsys_part" -ge 0 -a "$opsys_part" -le 3 ]; then + break + else + echo + echo "Partition must be in the range [0-3]" + fi +done +set_existing_part +} + +analyze_fdisk_data() { +if [ "$part_cnt" -gt 0 ]; then + echo + echo "Partition Offset* Size* Name" + echo "--------- ------ ---- ----" + i=0 + while [ $i -lt $part_cnt ]; do + pcyls= + poff= + eval psize=\$size${i} + eval pstart=\$start${i} + eval pname=\$sysname${i} + [ "$psize" -eq 50000 ] && psize=$disksize + if [ "$psize" ]; then + poff=`expr $pstart / $cylindersize` + padjusted=`expr $poff \* $cylindersize` + if [ "$padjusted" -lt "$pstart" ]; then + poff=`expr $poff + 1` + padjusted=`expr $poff \* $cylindersize` + psize=`expr $psize - $padjusted + $pstart` + fi + pcyls=`expr $psize / $cylindersize` + fi + echo -n "${i}" + echo -n " ${poff:-0}" + echo -n " ${pcyls:-0}" + echo " ${pname:-(Unused)}" + i=`expr $i + 1` + done + echo "* Sizes and offsets are in units of cylinders." +fi +# Case I: >1024 cylinders +force_offset= +if [ $cyls_per_disk -gt 1024 ]; then + echo + echo " WARNING: >1024 cylinders. On some hardware, this prevents" + echo " ${OPSYSTEM} from sharing the disk with other operating systems." + echo -n "Install ${OPSYSTEM} on entire disk, overwriting existing partitions? [n] " + read resp junk + case "$resp" in + y*|Y*) + RUN_FDISK="" + force_offset=1 + opsys_off=0 + cyls_per_opsys=${cyls_per_disk} + opsys_part=${unused_last_part:-3} + return 0 + ;; + *) + echo + echo "If the number of disk cylinders does not exceed 1024, then ${OPSYSTEM}" + echo "can be installed alongside other operating systems on a single disk." + echo "Otherwise, it is system-dependent whether this will work or not." + echo "In the worst case, ${OPSYSTEM} MUST be installed at the beginning of" + echo "the disk, and existing partitions will be lost." + echo + echo "For now, we will assume that >1024 cylinders creates no problems..." + # FALL THROUGH + ;; + esac +fi +# Case II: no partitions used +if [ $sysid_cnt -eq 0 ]; then + echo + echo " WARNING: partition table is either missing or corrupt." + echo " Existing partitions will be lost." + part_cnt=${part_cnt:-4} + RUN_FDISK="overwrite" + opsys_off=1 + cyls_per_opsys=`expr ${cyls_per_disk} - 1` + opsys_part=${unused_last_part:-3} + return 0 +# Case IIIa: overwrite an existing 386BSD/NetBSD/FreeBSD partition +elif [ "$have_opsys_part" ]; then + echo + echo "386/Net/FreeBSD partition already exists!" + echo -n "Overwrite existing partition? [n] " + read resp junk + case "$resp" in + y*|Y*) + opsys_part=${have_opsys_part} + set_existing_part + return 0 + ;; + *) + have_opsys_part= + # FALL THROUGH + ;; + esac +fi + +# Case IIIb: no partitions available +if [ $sysid_cnt -eq $part_cnt -a ! "$have_opsys_part" ]; then + echo + echo "No unused partitions." + echo -n "Install $OPSYSTEM and overwrite the entire disk? [n] " + read resp junk + case "$resp" in + y*|Y*) + # don't use first cylinder! + opsys_off=1 + cyls_per_opsys=`expr $cyls_per_disk - 1` + opsys_part=${unused_last_part} + RUN_FDISK="overwrite" + ;; + *) + set_overwrite_part + ;; + esac + return 0 +fi + + +# *** CAVEAT *** +# $OPSYSTEM installs at the end of the disk. If the +# beginning of the disk is free but not the end, install fails! + +# Assume `fdisk -u' to add $OPSYSTEM in last unused partition for remaining cases +opsys_part=${unused_last_part} +RUN_FDISK="fdisk -u" +mb_sect=`expr 1024 \* 1024 / $bytes_per_sect` +disk_minimum=`expr $DISKMIN \* $mb_sect` + +# Case IV: No room (at end of disk) for mininal install +[ $extent_max -eq 50000 ] && extent_max=$disksize +disk_remaining=`expr $disksize - $extent_max` +if [ $disk_remaining -lt $disk_minimum ]; then + echo + echo "Not enough space ($DISKMIN Mb) at end of disk to install $OPSYSTEM." + echo -n "Install FreeBSD and overwrite the entire disk? [n] " + read resp junk + case "$resp" in + y*|Y*) + # don't use first cylinder! + opsys_off=1 + cyls_per_opsys=`expr $cyls_per_disk - 1` + opsys_part=${unused_last_part} + RUN_FDISK="overwrite" + ;; + *) + echo + echo -n "Overwrite an existing partition? [n] " + read resp junk + case "$resp" in + y*|Y*) + set_overwrite_part + ;; + *) + echo + echo " WARNING: To install ${OPSYSTEM}, you're on your own in figuring" + echo " out where on the disk it will fit without overwriting another" + echo " partition..." + # Set defaults assuming there is only one partition at end of disk + eval start=\$start${extent_max_part} + # don't use first cylinder! + opsys_off=1 + cyls_per_opsys=`expr $start / $cylindersize - 1` + [ $cyls_per_opsys -lt 0 ] && cyls_per_opsys=0 + ;; + esac + ;; + esac + return 0 +fi + +# Case V: Room for $OPSYSTEM and partition data okay +opsys_off=`expr $extent_max / $cylindersize` +opsys_extent=`expr $opsys_off \* $cylindersize` +[ $opsys_extent -lt $extent_max ] && opsys_off=`expr $opsys_off + 1` +cyls_per_opsys=`expr $cyls_per_disk - $opsys_off` +return 0 +} + +put_fdisk_data() { +start=$root_offset +size=$partition + +if [ "$RUN_FDISK" = "overwrite" ]; then + # How do you overwrite without explicitly editing each entry? + ( + echo y + echo $cyls_per_disk + echo $tracks_per_cyl + echo $sects_per_track + echo y + ) >fdisk.script + i=0 + n=`expr ${part_cnt:-4} - 1` + while [ $i -lt $n ]; do + echo y + echo 0 + echo 0 + echo 0 + echo n + echo y + i=`expr $i + 1` + done >>fdisk.script + ( echo y + echo ${OPSYSID} + echo ${start} + echo ${size} + echo n + echo y + echo y + echo ${n} + echo y + echo y + ) >>fdisk.script + fdisk -u /dev/r${drivename}d /dev/null 2>&1 +elif [ "$RUN_FDISK" ]; then + $RUN_FDISK -${opsys_part:-${unused_last_part:-3}} /dev/r${drivename}d <<-EOF >/dev/null 2>&1 + y + $cyls_per_disk + $tracks_per_cyl + $sects_per_track + y + y + ${OPSYSID} + ${start} + ${size} + n + y + y + ${opsys_part:-${unused_last_part:-3}} + y + y + EOF +fi + +} + +echo "Welcome to ${OPSYSTEM}." +echo +echo "This program is designed to help put ${OPSYSTEM} on a hard disk with" +echo "at least $DISKMIN Megabytes of free space." +echo +echo "Before starting, it is important to know your hard disk's geometry" +echo "(i.e., number of cylinders, heads and sectors/track). If installing" +echo "${OPSYSTEM} on the same disk as another operating system, then the" +echo "two systems should use the same geometry. In particular, ${OPSYSTEM}'s" +echo "default geometry is inappropriate for MS-DOS. So in this case, the" +echo "DOS geometry should be used instead." +echo +echo "As with anything which modifies a hard drive's contents, this program" +echo "can cause SIGNIFICANT data loss. We strongly recommend making sure" +echo "that the hard drive is backed up before going further with the" +echo "installation process." +echo +echo -n "Proceed with installation? [y] " +read resp junk +resp=${resp:-y} +case "$resp" in +y*|Y*) + echo + echo "Cool! Let's get to it..." + echo + echo "If a mistake is made along the way, don't bail out." + echo "At the end, you have the option of redoing the configuration." + echo "If you really must quit at some point, type +C and" + echo "enter \`halt' at the command prompt, \`#'." + ;; +*) + echo + echo "OK, then. Enter \`halt' to halt the machine." + echo "Once the machine has halted, remove the floppy," + echo "and press any key to reboot." + exit + ;; +esac + +mount -u /dev/fd0a / || { + if mount -u /dev/fd1a / ; then + echo "[Please ignore the above error message, that's normal.]" + else + echo "Oh boy, we're in trouble here: Could not mount floppy read-write." + exit 1 + fi +} +sync +verified_install="" +while [ ! "$verified_install" ]; do # Begin of Big Loop + +rotdelay="" +drivename=wd0 +drivetype=wd +sect_fwd="" +echo +echo "First, we need to know the drive type. This can be can be one of" +echo "ESDI, SCSI, ST506, or IDE." +echo -n "Drive type? [${type:-IDE}] " +read resp junk +type=${resp:-${type:-IDE}} +case "$type" in +e*|E*|st*|ST*) + echo -n "Does it support AUTOMATIC sector remapping? [y] " + read remap junk + case "$remap" in + n*|N*) + sect_fwd="sf:" + ;; + esac + case "$type" in + e*|E*) + DEFSECT=36 + ;; + esac + ;; +i*|I*) + type=ST506 + rotdelay="-d 0" + ;; +sc*|SC*) + drivename=sd0 + drivetype=sd + type=SCSI + rotdelay="-d 0" + DEFSECT=32 + DEFHEAD=64 + ;; +*) + echo "Unknown type. Assuming ST506 with automatic sectoring..." + type=ST506 + ;; +esac +echo +echo "Disk is of device type $drivetype." +if [ ! "$partition" ]; then + echo + echo "Please wait. Examining device /dev/r${drivename}d..." + get_fdisk_data + if [ $? -gt 1 ]; then + echo "Hm - we can't seem to read that drive." + echo + echo -n "Are you sure that $type is the correct type? [n] " + read resp + case "$resp" in + y*|Y*) + echo + echo "Well, since we can't even open it, there isn't much" + echo "hope for writing a label on it. But you're free" + echo "to give it a try. You need to specify the geometry." + ;; + *) + echo + echo "Okay. Let's start again from the top." + continue + ;; + esac + fi +fi +echo +echo "Now we want to build a data base entry in /etc/disktab describing" +echo "the geometry of the /dev/$drivename disk. The name of the entry" +echo "should be descriptive of the disk's type and model. For example," +echo "a Maxtor IDE, model 7080 disk might be named \`maxtor7080'." +echo -n "Disk label name (one word, please)? [${name:-mfr_model}] " +read resp junk +name=${resp:-${name:-mfr_model}} +echo +echo "${OPSYSTEM} should use the same hard disk geometry as used by other" +echo "operating systems on the hard disk." +echo -n "Number of bytes per disk sector? [${bytes_per_sect:-512}] " +read resp junk +bytes_per_sect=${resp:-${bytes_per_sect:-512}} +echo +echo -n "Total number of disk cylinders? [${cyls_per_disk:-${DEFCYLN}}] " +read resp junk +cyls_per_disk=${resp:-${cyls_per_disk:-${DEFCYLN}}} +echo +echo -n "Number of disk heads (i.e., tracks/cylinder)? [${tracks_per_cyl:-${DEFHEAD}}] " +read resp junk +tracks_per_cyl=${resp:-${tracks_per_cyl:-${DEFHEAD}}} +echo +echo -n "Number of disk sectors (i.e., sectors/track)? [${sects_per_track:-${DEFSECT}}] " +read resp junk +sects_per_track=${resp:-${sects_per_track:-${DEFSECT}}} +cylindersize=`expr $sects_per_track \* $tracks_per_cyl` +disksize=`expr $cylindersize \* $cyls_per_disk` +mb_sect=`expr 1024 \* 1024 / $bytes_per_sect` +mb_per_disk=`expr $disksize / $mb_sect` +opsys_cyls_min=`expr $DISKMIN \* $mb_sect / $cylindersize` +analyze_fdisk_data +if [ $? -eq 0 ]; then + partition=`expr $cyls_per_opsys \* $cylindersize` + part_offset=`expr $opsys_off \* $cylindersize` +fi +echo +echo "Disk has a total of $mb_per_disk Mb." +echo "The size of the ${OPSYSTEM} portion of the disk must be at least" +echo "${opsys_cyls_min} cylinders, and should not exceed $(expr $cyls_per_disk - 1) cylinders." +echo "The offset of ${OPSYSTEM} from the beginning of the disk should be at" +echo "least 1 cylinder." +echo +echo "For efficiency, partitions begin and end on cylinder boundaries." +echo "If you know the size NN in Megabytes (Mb) of a partition you want, then" +echo "use the following formula to determine the number NC of cylinders to use:" +echo " NC = integer { ( NN * $mb_sect ) / $cylindersize }" +while :; do + echo -n "Total size of the ${OPSYSTEM} portion of the disk (in cylinders)? [${cyls_per_opsys:-`expr ${cyls_per_disk} - 1`}] " + read resp junk + cyls_per_opsys=${resp:-${cyls_per_opsys:-`expr ${cyls_per_disk} - 1`}} + partition=`expr $cyls_per_opsys \* $cylindersize` + if [ $cyls_per_opsys -lt $cyls_per_disk -a ! "$force_offset" ]; then + echo + echo -n "Offset of ${OPSYSTEM} from beginning of disk (in cylinders)? [${opsys_off:-1}] " + read resp junk + opsys_off=${resp:-${opsys_off:-1}} + else + echo + echo " WARNING: Existing partitions will be lost. In addition," + echo " installing at cylinder 0 may cause problems for some disk" + echo " controllers. If the filesystem is corrupted or install" + echo " fails, install at cylinder 1." + RUN_FDISK="" + cyls_per_opsys=$cyls_per_disk + partition=$disksize + opsys_off=0 + fi + part_offset=`expr $opsys_off \* $cylindersize` + opsys_extent=`expr $opsys_off + $cyls_per_opsys` + if [ ${opsys_extent} -gt ${cyls_per_disk} ]; then + echo + echo "${OPSYSTEM} Size + Offset cannot exceed ${cyls_per_disk} cylinders." + elif [ ${cyls_per_opsys} -lt ${opsys_cyls_min} ]; then + echo + echo "${OPSYSTEM} requires at least ${opsys_cyls_min} cylinders to install." + else break + fi +done +badspacesec=0 +if [ "$sect_fwd" = "sf:" ]; then + badspacecyl=`expr $sects_per_track + 126` + badspacecyl=`expr $badspacecyl + $cylindersize - 1` + badspacecyl=`expr $badspacecyl / $cylindersize` + badspacesec=`expr $badspacecyl \* $cylindersize` + echo + echo -n "Using $badspacesec sectors ($badspacecyl cylinders) for the " + echo "bad144 bad block table" +fi +whats_left=`expr $partition - $badspacesec` +cyl_left=`expr $whats_left / $cylindersize` +mb_left=`expr $whats_left / $mb_sect` +swap_cyls_min=`expr $SWAPMIN \* $mb_sect / $cylindersize` +root_cyls_max=`expr ${cyl_left} - ${swap_cyls_min}` +root_cyls_min=`expr $ROOTMIN \* $mb_sect / $cylindersize` +echo +echo "There are $mb_left Mb ($cyl_left cylinders) to allocate." +echo +echo "The $OPSYSTEM portion of the disk must itself be divided into at least" +echo "two partitions: one for the root filesystem and one for swap. It is a" +echo "good idea to have at least a third (large) $OPSYSTEM partition for the /usr" +echo "filesystem." +echo +echo "The root partition cannot exceed ${root_cyls_max} cylinders. It is usually" +echo "no larger than about 15 Mb ($(expr 15 \* $mb_sect / $cylindersize) cylinders), and sometimes" +echo "as small as $ROOTMIN Mb ($root_cyls_min cylinders)." +if [ ! "$cyls_per_root" ]; then + # set default root partition to 15MB + cyls_per_root=`expr \( 15 \* $mb_sect \) / $cylindersize` + usr_cyls_max=`expr ${root_cyls_max} - ${cyls_per_root}` + mb_usr=`expr ${usr_cyls_max} \* $cylindersize / $mb_sect` + [ $cyls_per_root -gt $root_cyls_max -o $mb_usr -lt $USRMIN ] && + cyls_per_root=$root_cyls_max +fi +while :; do + echo -n "Root partition size (in cylinders)? [${cyls_per_root}] " + read resp junk + cyls_per_root=${resp:-${cyls_per_root}} + root=`expr $cyls_per_root \* $cylindersize` + if [ ${cyls_per_root} -gt ${root_cyls_max} ]; then + echo + echo "The root partition size cannot exceed $root_cyls_max cylinders." + elif [ ${cyls_per_root} -lt ${root_cyls_min} ]; then + echo + echo "The root partition size must be at least $root_cyls_min cylinders." + else + part_used=`expr $root + $badspacesec` + break + fi +done +root_offset=$part_offset +whats_left=`expr $partition - $part_used` +cyl_left=`expr $whats_left / $cylindersize` +mb_left=`expr $whats_left / $mb_sect` +echo +# DO NOT USE DIFFERENT BLOCKING FACTORS FOR EACH PARITION.. IT TRASHES THE +# VM SYSTEM! When that gets fixed this can go back the way it was... +# +echo "We can build the filesystems with block/fragment sizes of either" +echo " 1) 4k/512, to save disk space at the expense of speed, or" +echo " 2) 8k/1k for speed at the expense of disk space." +echo -n "Which blocking factor should we use for the filesystems? " +echo -n "[${blocking_factor:-${DEFBLOCKING}}] " +read resp junk +blocking_factor=${resp:-${blocking_factor:-${DEFBLOCKING}}} +fragsize=`expr $bytes_per_sect \* $blocking_factor` +blocksize=`expr $bytes_per_sect \* $blocking_factor \* 8` +echo +echo "$mb_left Mb ($cyl_left cylinders) remaining in ${OPSYSTEM} portion of disk." +echo +echo "Minimum swap space is ${swap_cyls_min} cylinders." +echo "For running X, if your RAM size is NR Mb, then the recomended swap" +echo "size NS (in cylinders) is:" +echo " NS = integer { ( NR x `expr 21 \* $mb_sect / 10` ) / ${cylindersize} }" +if [ ! "$swap_cyl" ]; then + # guess memory size + mb_ram=16 + swap_cyl=`expr \( 21 \* $mb_ram \* $mb_sect \) / 10` + swap_cyl=`expr $swap_cyl / ${cylindersize}` + + # but not swap size more than 10% of disk size... + swap_quot=`expr $mb_left / $mb_ram` + if [ $swap_quot -lt 10 ]; then + swap_cyl=$swap_cyls_min + fi +fi +while :; do + echo -n "Swap partition size (in cylinders)? [${swap_cyl}] " + read resp junk + swap_cyl=${resp:-${swap_cyl}} + swap=`expr $swap_cyl \* $cylindersize` + if [ ${swap_cyl} -gt ${cyl_left} ]; then + echo + echo "Swap size cannot exceed $cyl_left cylinders." + elif [ ${swap_cyl} -lt ${swap_cyls_min} ]; then + echo + echo "Swap size must be at least ${swap_cyls_min} cylinders." + else + break + fi +done +swap_offset=`expr $root_offset + $root` +part_used=`expr $part_used + $swap` +echo "" >/etc/disktab +echo "$name|${OPSYSTEM} installation generated:\\" >>/etc/disktab +echo " :dt=${type}:ty=winchester:\\" >>/etc/disktab +echo -n " :nc#${cyls_per_disk}:ns#${sects_per_track}" >>/etc/disktab +echo ":nt#${tracks_per_cyl}:\\" >>/etc/disktab +echo " :se#${bytes_per_sect}:${sect_fwd}\\" >>/etc/disktab +echo -n " :pa#${root}:oa#${root_offset}" >>/etc/disktab +echo ":ta=4.2BSD:ba#${blocksize}:fa#${fragsize}:\\" >>/etc/disktab +echo " :pb#${swap}:ob#${swap_offset}:tb=swap:\\" >>/etc/disktab +echo " :pc#${partition}:oc#${part_offset}:\\" >>/etc/disktab +ename="";fname="";gname="";hname="" +if [ $part_used -lt $partition ]; then + echo + echo "Now we enter information about any other partitions and filesystems" + echo "to be created in the ${OPSYSTEM} portion of the disk. This process" + echo "is complete when we've filled up all remaining space in the ${OPSYSTEM}" + echo "portion of the disk." +fi +while [ $part_used -lt $partition ]; do + part_size=0 + whats_left=`expr $partition - $part_used` + cyl_left=`expr $whats_left / $cylindersize` + mb_left=`expr $whats_left / $mb_sect` + echo + echo "$mb_left Mb ($cyl_left cylinders) remaining in ${OPSYSTEM} portion of disk." + echo + while :; do + echo -n "Next partition size (in cylinders)? [${cyl_left}] " + read resp junk + part_size=${resp:-${cyl_left}} + part_size=`expr $part_size \* $cylindersize` + total=`expr $part_used + $part_size` + if [ $total -gt $partition ]; then + echo + echo "Partition size cannot exceed ${cyl_left} cylinders." + else + part_used=$total + part_name="" + while [ "$part_name" = "" ]; do + echo + echo -n "On which directory should this filesystem be mounted? [usr] " + read resp junk + part_name=${resp:-usr} + part_name=`expr X"$part_name" : 'X/*\(.*\)'` + done + break + fi + done + if [ ! "$ename" ]; then + ename=$part_name + offset=`expr $part_offset + $root + $swap` + echo -n " :pe#${part_size}:oe#${offset}" >>/etc/disktab + echo ":te=4.2BSD:be#${blocksize}:fe#${fragsize}:\\" >>/etc/disktab + offset=`expr $offset + $part_size` + elif [ ! "$fname" ]; then + fname=$part_name + echo -n " :pf#${part_size}:of#${offset}" >>/etc/disktab + echo ":tf=4.2BSD:bf#${blocksize}:ff#${fragsize}:\\" >>/etc/disktab + offset=`expr $offset + $part_size` + elif [ ! "$gname" ]; then + gname=$part_name + echo -n " :pg#${part_size}:og#${offset}" >>/etc/disktab + echo ":tg=4.2BSD:bg#${blocksize}:fg#${fragsize}:\\" >>/etc/disktab + offset=`expr $offset + $part_size` + elif [ ! "$hname" ]; then + hname=$part_name + echo -n " :ph#${part_size}:oh#${offset}" >>/etc/disktab + echo ":th=4.2BSD:bh#${blocksize}:fh#${fragsize}:\\" >>/etc/disktab + part_used=partition + fi +done +if [ "$have_dos_part" != "$opsys_part" -a "$RUN_FDISK" != "overwrite" -a \ + ! "$hname" ]; then + echo + echo "There appears to be a MS-DOS filesystem on the hard disk." + echo -n "Make this be accessible from ${OPSYSTEM}? [y] " + read resp junk + case "${resp:-y}" in + y*|Y*) + part_name="" + while [ "$part_name" = "" ]; do + echo + echo -n "On which directory should this filesystem be mounted? [dos] " + read resp junk + part_name=${resp:-dos} + part_name=`expr X"$part_name" : 'X/*\(.*\)'` + done + hname=$part_name + eval offset=\$start${have_dos_part} + eval part_size=\$size${have_dos_part} + echo -n " :ph#${part_size}:oh#${offset}" >>/etc/disktab + echo ":th=MSDOS:\\" >>/etc/disktab + part_used=partition + ;; + *) + have_dos_part="" + ;; + esac +else + have_dos_part="" +fi +echo " :pd#${disksize}:od#0:" >>/etc/disktab +sync + +# cat /etc/disktab +OIFS=$IFS +IFS=' +' +while read data; do + echo $data +done < /etc/disktab +IFS=$OIFS + +echo +echo -n "Verbose installation? [n] " +read resp + +case $resp in +y*) + cpioverbose=v + ;; +*) + cpioverbose= + ;; +esac + + +echo +echo "OK! THIS IS THE LAST CHANCE!!! Data on the hard disk wil be lost." +echo -n "Are you sure you want to install on the hard drive? (yes/no) " +resp="" +while [ ! "$resp" ]; do + read resp junk + case "$resp" in + Yes|yes|YES) + verified_install=1 + echo + echo "OK! Here we go..." + ;; + No|no|NO) + echo + echo -n "Would you like to change the configuration? [y] " + read resp junk + resp=${resp:-y} + case "$resp" in + y*|Y*) + ;; + *) + echo + echo "OK, then. Enter 'halt' to halt the machine." + echo "Once the machine has halted, remove the floppy," + echo "and press any key to reboot." + exit + ;; + esac + ;; + *) + echo "Please spell out either of \`yes' or \`no'..." + echo -n "Install on the hard disk? (yes/no) " + resp= + ;; + esac +done +done # End of Big Loop + +put_fdisk_data + +echo +echo -n "Labelling disk..." +echo y | +/sbin/disklabel -w -r ${drivename} $name /usr/mdec/${drivetype}boot /usr/mdec/boot${drivetype} >/dev/null 2>&1 +echo " done." + +if [ "$sect_fwd" = "sf:" ]; then + echo -n "Initializing bad144 badblock table..." + bad144 $drivename 0 + echo " done." + echo "Updating badblock table..." + # `2>&1 >/dev/null' filters stdout and leaves only stderr... + badlist=$(bad144 -s $drivename 2>&1 >/dev/null | + while read data; do + bad_seek=$(expr "$data" : '[^(]*(seek)[^0-9]*\([0-9]*\)') + bad_read=$(expr "$data" : '[^(]*(read)[^0-9]*\([0-9]*\)') + [ "$bad_seek" -o "$bad_read" ] && echo -n "$bad_seek $bad_read " + done) + [ "$badlist" ] && bad144 -a -c $drivename "$badlist" + echo " done." +fi + +echo "Initializing root filesystem, and mounting..." +newfs ${rotdelay} /dev/r${drivename}a $name +mount -v /dev/${drivename}a /mnt +if [ "$ename" != "" ]; then + echo + echo "Initializing $ename filesystem, and mounting..." + newfs ${rotdelay} /dev/r${drivename}e $name + mkdir -p /mnt/$ename + mount -v /dev/${drivename}e /mnt/$ename +fi +if [ "$fname" != "" ]; then + echo + echo "Initializing $fname filesystem, and mounting..." + newfs ${rotdelay} /dev/r${drivename}f $name + mkdir -p /mnt/$fname + mount -v /dev/${drivename}f /mnt/$fname +fi +if [ "$gname" != "" ]; then + echo + echo "Initializing $gname filesystem, and mounting..." + newfs ${rotdelay} /dev/r${drivename}g $name + mkdir -p /mnt/$gname + mount -v /dev/${drivename}g /mnt/$gname +fi +if [ "$hname" != "" ]; then + echo + if [ ! "$have_dos_part" ]; then + echo "Initializing $hname filesystem, and mounting..." + newfs ${rotdelay} /dev/r${drivename}h $name + else + echo "Initializing $hname filesystem..." + fi + mkdir -p /mnt/$hname + [ ! "$have_dos_part" ] && + mount -v /dev/${drivename}h /mnt/$hname +fi + +echo +echo "Please wait. Copying to disk..." +cd / +# cat filelist | cpio -pdamu${cpioverbose} /mnt +OIFS=$IFS +IFS=' +' +while read data; do + echo $data +done etc/fstab +if [ "$ename" != "" ]; then + echo "/dev/${drivename}e /$ename ufs rw 1 2" >>etc/fstab +fi +if [ "$fname" != "" ]; then + echo "/dev/${drivename}f /$fname ufs rw 1 3" >>etc/fstab +fi +if [ "$gname" != "" ]; then + echo "/dev/${drivename}g /$gname ufs rw 1 4" >>etc/fstab +fi +if [ "$hname" != "" ]; then + if [ ! "$have_dos_part" ]; then + echo "/dev/${drivename}h /$hname ufs rw 1 5" >>etc/fstab + else + echo "/dev/${drivename}h /$hname pcfs rw 0 0" >>etc/fstab + fi +fi + +# cat /etc/disktab >etc/disktab.install +OIFS=$IFS +IFS=' +' +while read data; do + echo $data +done etc/disktab.install +IFS=$OIFS + +# cat << EOF >.profile +( +echo "PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/usr/distbin:.:" +echo "export PATH" +echo "HOME=/root" +echo "export HOME" +echo "TERM=pc3" +echo "export TERM" +echo "mount -at ufs" +echo "echo" +echo "echo \"Insert cpio installation floppy in drive and\"" +echo "echo -n \"enter that drive's number (0 or 1): [0] \"" +echo "read resp junk" +echo "driveno=\${resp:-0}" +echo "mount -o ro /dev/fd\${driveno}a /mnt" +echo "cd /mnt" +echo "install" +echo "cd /" +echo "umount /mnt >/dev/null 2>&1" +echo "echo" +echo "echo -n \"Install optional dos floppy? [n] \"" +echo "read resp junk" +echo "case \"\$resp\" in" +echo "y*|Y*)" +echo " echo" +echo " echo \"Remove cpio floppy from drive, insert dos floppy, and\"" +echo " echo -n \"enter that drive's number (0 or 1): [0] \"" +echo " read resp junk" +echo " driveno=\${resp:-0}" +echo " mount -t pcfs -o ro /dev/fd\${driveno}a /mnt" +echo " mkdir -p /usr/distbin" +echo " cp /mnt/* /usr/distbin/ >/dev/null 2>&1" +echo " sync" +echo " umount /mnt >/dev/null 2>&1" +echo " ;;" +echo "esac" +echo "echo" +echo "echo \"OK. All of the base files are installed.\"" +echo "echo" +echo "echo \"The next step: reboot from the hard disk. Further\"" +echo "echo \"instructions are presented upon rebooting.\"" +echo "echo" +echo "echo \"Enter 'halt' now at the prompt to halt the machine.\"" +echo "echo \"After the machine has halted, remove the floppy from the disk\"" +echo "echo \"drive, and hit any key to reboot from the hard disk.\"" +) >.profile + +sync + +echo +echo "The next step: reboot from the kernel-copy disk, copy a kernel" +echo "to the hard disk, and finally reboot from the hard disk." +echo +echo "To do this, enter \`halt' now to halt the machine. After it" +echo "announces that it has halted, remove the floppy from the drive" +echo "and insert the kernel-copy disk that was booted before." +echo "Press any key to reboot. When prompted to insert the filesystem" +echo "floppy this time, just hit RETURN without changing floppies." +echo +echo "If all goes well, you can enter the command \`copy' at the prompt to" +echo "copy the kernel to the hard disk. When asked for which partition to" +echo "copy to, enter to \`${drivename}a' (without the quotes)." +echo +echo "Okay, that's all for now. I'm waiting for you to enter \`halt'..." diff --git a/etc/etc.i386/inst1.profile b/etc/etc.i386/inst1.profile new file mode 100644 index 0000000000..300db6dc67 --- /dev/null +++ b/etc/etc.i386/inst1.profile @@ -0,0 +1,7 @@ +PATH=/sbin:/usr/sbin:/bin:/usr/bin:.: +export PATH +HOME=/root +export HOME +TERM=pc3 +export TERM +install diff --git a/etc/etc.i386/inst2.install b/etc/etc.i386/inst2.install new file mode 100644 index 0000000000..0ef2d46f6b --- /dev/null +++ b/etc/etc.i386/inst2.install @@ -0,0 +1,40 @@ +#!/bin/sh +# install2.fs disk 'install' +# Simplified, interactive FreeBSD installation script. +# D.E. Silvia (dsilvia@net.com) +# +# Heavily hacked on for support of FreeBSD +# by Rodney W. Grimes (rgrimes@cdrom.com) 1993/08/11 +# +# Installs balance of basic FreeBSD system. +# + +echo -n "Verbose installation? [n] " +read resp +case $resp in + y*) + cpioverbose=v + ;; + *) + cpioverbose= + ;; +esac + +echo +echo "Please wait. Copying to disk..." +# remove /.profile so that the right things happen when it gets +# over written +rm /.profile +cd / +mnt/usr/bin/gunzip < mnt/inst2.cpio.gz | cpio -idmu${cpioverbose} +cd /mnt +ls .profile install usr/bin/* | cpio -pdmu${cpioverbose} / +cd /dev +echo " done." +echo +echo "Building /dev files..." +sh MAKEDEV all +cd / +echo " done." + +sync diff --git a/etc/etc.i386/inst2.profile b/etc/etc.i386/inst2.profile new file mode 100644 index 0000000000..759828845c --- /dev/null +++ b/etc/etc.i386/inst2.profile @@ -0,0 +1,440 @@ +stty status '^T' +trap : 2 +trap : 3 +HOME=/; export HOME +PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/distbin; export PATH +if [ -e /fastboot ] +then + echo Fast boot ... skipping disk checks +else + echo Automatic reboot in progress... + fsck -p + case $? in + 0) + ;; + 2) + exit 1 + ;; + 4) + echo; echo README README README README README README README + echo + echo "NOTE: The above errors are expected if this is the" + echo "first time you have booted from the hard disk after" + echo "completing the floppy install"; echo + echo "Automatic file system check changed the root file system" + echo "The system must halt for these corrections to take effect" + echo + reboot + echo "reboot failed... help!" + exit 1 + ;; + 8) + echo "Automatic file system check failed... help!" + exit 1 + ;; + 12) + echo "Reboot interrupted" + exit 1 + ;; + 130) + exit 1 + ;; + *) + echo "Unknown error in reboot" + exit 1 + ;; + esac +fi + +trap 2 +trap "echo 'Reboot interrupted'; exit 1" 3 +umount -a >/dev/null 2>&1 +mount -a -t nonfs +rm -f /fastboot +(cd /var/run && { rm -rf -- *; cp /dev/null utmp; chmod 644 utmp; }) + +TERM=pc3 # terminal emulator, for elvis +TERMCAP="\ +pc3|ibmpc3:li#25:co#80:am:bs:bw:eo:cd=\E[J:ce=\E[K:cl=\Ec:cm=\E[%i%2;%2H:\ +do=\E[B:ho=\E[;H:nd=\E[C:up=\E[A:so=\E[7m:se=\E[0m:us=\E[4m:ue=\E[0m:\ +:ac=l\332q\304k\277x\263j\331m\300w\302u\264v\301t\303n\305:\ +:kb=^h:kh=\E[Y:ku=\E[A:kd=\E[B:kl=\E[D:kr=\E[C:" +OPSYSTEM=FreeBSD +RELEASE="1.0" +export TERMCAP +export TERM +echo "${OPSYSTEM} Base System Release ${RELEASE}" +echo "" +echo "Congratulations, you've got ${OPSYSTEM} on the hard disk!" +echo +echo "Press the return key for more installation instructions" +read junkit +echo +echo "To finish installation:" +echo "Pick a temporary directory by running set_tmp_dir. Make sure it's" +echo "in a place with lots of space, probably under /usr." +echo "Then, load the remaining distribution files into that temporary" +echo "directory by issuing one of the following commands:" +echo +echo " load_fd load_qic_tape load_scsi_tape" +echo +echo "or by fetching the files with ftp (see the installation notes for" +echo "information on how to do that)." +echo +echo "Once this is complete, extract the distribution files by issuing the" +echo "command 'extract ' where is the base name" +echo "of the distribution files, e.g. 'base10'." +echo +echo "Once all of the filesets you wish to install have been extracted," +echo "enter the command 'configure' to finish setting up the system" +echo " " +echo "If you should wish to uninstall ${OPSYSTEM}, delete the partition by using the" +echo "DOS 5 FDISK program. If installed on the entire drive, use the FDISK/MBR" +echo "to remove the ${OPSYSTEM} bootstrap from the drive." +echo 'erase ^?, werase ^H, kill ^U, intr ^C' +stty newcrt werase  intr  kill  erase  9600 +umask 0 +set_tmp_dir() +{ + def_tmp_dir=`pwd` + [ "$def_tmp_dir" = "/" ] && def_tmp_dir=/usr/distrib + echo -n "Copy files to which directory? [${def_tmp_dir}] " + read tmp_dir + [ ! "$tmp_dir" ] && tmp_dir=$def_tmp_dir + if [ ! -d "$tmp_dir" ]; then + /bin/rm -rf $tmp_dir + mkdir -p $tmp_dir + fi + echo +} +tmp_dir() +{ + if [ "$tmp_dir" = "" ]; then + set_tmp_dir + fi + cd $tmp_dir +} +load_fd() +{ + tmp_dir + drive= + altdrive= + subdir= + while [ -z "$drive" ]; do + echo -n "Read from which drive (or ? for help)? [c] " + read answer junk + case "${answer:-c}" in + a*b|A*B) + drive=A; altdrive=B + ;; + b*a|B*A) + drive=B; altdrive=A + ;; + a*|A*) + drive=A; altdrive=A + ;; + b*|B*) + drive=B; altdrive=B + ;; + c*|C*) + while read data; do + msdos_device=`expr X"$data" : 'X[ ]*\([^ ]*\)[^M]*pcfs'` + msdos_dir=`expr X"$data" : 'X[ ]*[^ ]*[ ]*\([^ ]*\)'` + [ "${msdos_device}" ] && break + done /dev/null 2>&1 + [ -f $dir ] && rm -f $dir + mkdir -p $dir + fi + while [ "$drive" != "q" ] + do + device=/dev/fd0a + [ "$drive" = "B" ] && device=/dev/fd1a + [ "$drive" = "C" ] && device=${msdos_device} + echo; + if [ "$drive" != "C" ]; then + echo "Insert floppy in drive $drive:, then press RETURN to copy files," + echo -n "or enter option (? for help): " + else + echo -n "Press RETURN to copy files, or enter option (? for help): " + fi + read answer junk + case "${answer:-g}" in + c*|C*) + if [ "$drive" != "C" ]; then + echo "Cannot change directory: not reading from MS-DOS drive C:" + else + echo + echo -n "Read from which MS-DOS drive C: directory? [/${subdir}] " + read resp junk + [ ! "$resp" ] && resp="/$subdir" + absolute=`expr X"$resp" : 'X[Cc]*:*\([/\]\)'` + subsub=$(echo "${resp}" | \ + awk '{ sub(/^[Cc]*:*/, ""); gsub(/\\/, "/"); gsub(/^\/*/, ""); gsub(/\/*$/, ""); print $0 }') + if [ "$absolute" -o ! "$subdir" ]; then + newsub=$subsub + else + newsub=$subdir/$subsub + fi + if [ -d ${dir}/${newsub} ]; then + subdir=$newsub + else + echo "C:/${newsub}: No such directory" + fi + fi + ;; + g*|G*) + sync + if [ "$drive" = "C" ]; then + [ "$verbose" ] && + { echo; echo "Please wait. Copying files from MS-DOS C:/${subdir}"; } + cp ${msdos_dir}/${subdir}/* . + sync + elif mount -t pcfs $verbose $device $dir; then + [ "$verbose" ] && + { echo; echo "Please wait. Copying files to disk..."; } + cp $interactive $dir/* . + sync + umount $dir + tmp=$drive; drive=$altdrive; altdrive=$tmp + fi + ;; + i*|I*) + tmp=$interactive; interactive=; [ -z "$tmp" ] && interactive=-i + tmp=on; [ -z "$interactive" ] && tmp=off + echo "interactive mode is $tmp" + ;; + l*|L*) + sync + [ "$verbose" ] && echo "Directory of ${drive}:/${subdir}" + if [ "$drive" = "C" ]; then + ls -l $dir/${subdir} + else + umount $dir >/dev/null 2>&1 + if mount -t pcfs $device $dir; then + ls -l $dir/${subdir} + umount $dir + fi + fi + ;; + o*|O*) + tmp=$drive; drive=$altdrive; altdrive=$tmp + ;; + q*|Q*) + drive=q + ;; + s*|S*) + echo; echo -n "tmp_dir is set to $tmp_dir" + [ "$tmp_dir" != "`pwd`" ] && echo -n " (physically `pwd`)" + echo; echo "Free space in tmp_dir:" + df -k . + echo -n "Reading from drive $drive:" + [ "$drive" != "$altdrive" ] && echo -n " and drive $altdrive:" + echo + tmp=on; [ -z "$verbose" ] && tmp=off + echo "Verbose mode is $tmp" + tmp=on; [ -z "$interactive" ] && tmp=off + echo "Interactive mode is $tmp" + ;; + v*|V*) + tmp=$verbose; verbose=; [ -z "$tmp" ] && verbose=-v + tmp=on; [ -z "$verbose" ] && tmp=off + echo "verbose mode is $tmp" + ;; + \?) + echo + echo "Enter: To:" + echo "----- ---" + echo "(just RETURN) Copy files from ${drive}:/${subdir} to $tmp_dir" + echo " c Change directory of MS-DOS drive C:" + echo " i Toggle interactive mode (cp -i)" + echo " l List directory of current drive" + echo " o Read from alternate floppy drive" + echo " q Quit" + echo " s Show status" + echo " v Toggle verbose mode" + echo + ;; + esac + done + echo "Working directory: `pwd`" + unset verbose answer drive altdrive device dir subdir tmp interactive +} +load_qic_tape() +{ + tmp_dir + echo -n "Insert tape into QIC tape drive and hit return to continue: " + read foo + tar xvf /dev/rwt0 +} +load_scsi_tape() +{ + tmp_dir + echo -n "Insert tape into SCSI tape drive and hit return to continue: " + read foo + tar xvf /dev/nrst0 +} +extract() +{ + tmp_dir + echo -n "Would you like to be verbose about this? [n] " + read verbose + case $verbose in + y*|Y*) + tarverbose=--verbose + ;; + *) + tarverbose= + ;; + esac + #XXX ugly hack to eliminate busy files, copy them to /tmp and use them + #from there... + cp -p /bin/cat /usr/bin/gunzip /usr/bin/tar /tmp + + for i in $*; do + /tmp/cat "$i"* | + /tmp/gunzip | + (cd / ; /tmp/tar --extract --file - --preserve-permissions ${tarverbose} ) + done + rm -f /tmp/cat /tmp/gunzip /tmp/tar + sync +} +configure() +{ + echo "You will now be prompted for information about this" + echo "machine. If you hit return, the default answer (in" + echo "brackets) will be used." + + echo + echo -n "What is this machine's hostname? [unknown.host.domain] " + read hname + + if [ "$hname" = "" ]; then + hname=unknown.host.domain + fi + echo $hname > /etc/myname + proto_domain=`echo $hname | sed -e 's/[^.]*\.//'` + + echo + echo "What domain is this machine in (this is NOT its YP" + echo -n "domain name)? [$proto_domain] " + read dname + + if [ "$dname" = "" ]; then + dname=$proto_domain + fi + + echo + echo -n "Does this machine have an ethernet interface? [y] " + read resp + case "$resp" in + n*) + ;; + *) + intf= + while [ "$intf" = "" ]; do + echo -n "What is the primary interface name (i.e. we0, etc.)? " + read intf + done + echo -n "What is the hostname for this interface? [$hname] " + read ifname + if [ "$ifname" = "" ]; then + ifname=$hname + fi + ifaddr= + while [ "$ifaddr" = "" ]; do + echo -n "What is the IP address associated with this interface? " + read ifaddr + done + echo "$ifaddr $ifname `echo $ifname | sed -e s/\.$dname//`" \ + >> /etc/hosts + + echo -n "Does this interface have a special netmask? [n] " + read resp + case "$resp" in + y*) + echo -n "What is the netmask? [0xffffff00] " + read ifnetmask + if [ "$ifnetmask" = "" ]; then + ifnetmask=0xffffff00 + fi + ;; + *) + ifnetmask= + ;; + esac + + echo -n "Does this interface need additional flags? [n] " + read resp + case "$resp" in + y*) + echo -n "What flags? [llc0] " + read ifflags + if [ "$ifflags" = "" ]; then + ifflags=llc0 + fi + ;; + *) + ifflags= + ;; + esac + + echo "inet $ifname $ifnetmask $ifflags" > /etc/hostname.$intf + + echo "" + echo "WARNING: if you have any more ethernet interfaces, you" + echo "will have to configure them by hand. Read the comments" + echo "in /etc/netstart to learn how to do this" + ;; + esac + + sync + + echo + echo "OK. You should be completely set up now." + echo "You should now reboot your machine by issuing the 'reboot' command" + echo "after removing anything that happens to be in your floppy drive." +} diff --git a/etc/etc.i386/install_notes b/etc/etc.i386/install_notes new file mode 100644 index 0000000000..9bee6136d4 --- /dev/null +++ b/etc/etc.i386/install_notes @@ -0,0 +1,1055 @@ + INSTALLATION NOTES + FreeBSD + Release 1.0 + +These notes have been prepared from those written originally for NetBSD +0.9. The conversion was done by someone who has had experience with +installing and upgrading 386bsd, but who is not a unix guru, so there +will be slant towards this experience. Corrections/updates are +welcomed, it is difficult/impossible to test every last hardware +combination. + +Be sure to read _ALL_ of this document before you try to install +FreeBSD. FreeBSD probably looks a bit similar to things that you've +seen before (perhaps 386BSD), but the installation procedures are quite +different. + + +FreeBSD 1.0 Release Contents: +------- --- ------- -------- + +The FreeBSD 1.0 Release consists of the following elements: + +Bootable Kernel-copy floppies + + These disks are bootable and have enough utilities on + board to copy a new kernel to a prepared hard disk. While + they are primarily intended for installing FreeBSD, they + also make upgrading to a new kernel easy: boot from it, + and copy a new kernel to disk. + + You must choose between one of two kernel-copy floppy + images, depending on your disk controller type. The + "kcopy-ah-floppy" image supports the Adaptec 154x and 1742 + SCSI adapters, while "kcopy-bt-floppy" supports the Bustek + 742 and Ultrastore SCSI adapters. For systems with only + MFM, RLL, ESDI or IDE disk controllers, either image can + be used. + +Installation floppies + + In addition to a bootable floppy, currently two additional + disks are required to prepare your hard drive for FreeBSD + and to install the FreeBSD distribution. Like the boot + floppies, these are distributed as binary images. They are + are referred to below as the "filesystem-floppy" and the + "cpio-floppy". + + There is also an optional fourth installation disk referred + to as the "dos-floppy". Unlike the other install disks, + there is no binary image for the dos floppy. Instead this + is a regular MS-DOS-formatted floppy disk containing any + FreeBSD programs you choose to copy to it using mtools or + even the DOS copy command. The most commonly requested + programs have been put in a tools directory at FreeBSD + archives sites. + +Upgrade floppies + + These facilitate upgrading to FreeBSD from any previous + patch-kit level of 386BSD 0.1. They are still in testing, + but should be available by the time you read this from + the tools/upgrade directory at FreeBSD archive sites. + [the current version is: + tools/upgrade/386BSD-to-FreeBSD-update-LATE-BETA.tar.gz] + +FreeBSD distribution sets + + These collections contain the complete FreeBSD system and + utilities in source and binary form. There are three + separate sets: the FreeBSD binaries, the FreeBSD sources, + and the DES sources+binaries. The DES set contains only + crypt(3) code and is subject to U.S.A. export restrictions. + + The binary distribution set can be found in the "binarydist" + subdirectory of the FreeBSD archive sites. It consists + of files named bin_tgz.aa to bin_tgz.db (i.e., 80 files + all told). A CKSUMS file (* see note below) is included + for verifying the integrity of these. + + The source distribution sets can be found in under + "sourcedist" subdirectory of archive sites. It is consists + of files named src_tgz.aa to src_tgz.cp (i.e., 68 files + all told), plus file CKSUMS*. + + Finally, the security distribution set contains + usr/src/libcrypt/*, the source files for the DES encryption + algorithm, and the binaries which depend on it. It can + be found in the "securedist" subdirectory on sites which + choose to carry the complete FreeBSD distribution. + + The individual files in each collection are no more than + 235 Kbytes in size. (The last file is just long enough + to contain the rest of the data for that distribution + set.) + + Each collection is a split, gzip'ed tar archive. They + are reassembled and extracted by the install procedure. + However, to view them without installing FreeBSD, you can + use, e.g., the command line: + + cat bin* | gunzip | tar tvf - | more + + or to extract the files themselves: + + cat bin* | gunzip | tar xvfp - + + Using this method, the files are extracted in the current + directory. So to install the binary distribution, for + instance, you have to run the "tar xvfp" from the root + directory (/). + + In each of the distribution directories, there is a file + named "CKSUMS" which contains the checksums of the files + in that directory, as generated by the cksum(1) command. + You can use cksum to verify the integrity of the archives, + if you suspect one of the files is corrupted. + + N.B.: The CKSUMS files are produced using the 4.4BSD + version of cksum which is POSIX-compliant. The values in + these file do not match the cksums generated by the 386BSD 0.1 + version of cksum (which is based on an earlier "standard"). + A copy of the new cksum binary that will run on + 386bsd/Netbsd/FreeBSD can be found in the "tools" subdirectory + of the distribution. + + +System Requirements and Supported Devices: +------ ------------ --- --------- ------- + +FreeBSD 1.0 runs on ISA (AT-Bus) and EISA systems, with 386 and 486 +processors, with or without math coprocessors. It does NOT support +Micro-channel systems, such as some IBM PS/2 systems. The minimal +configuration includes 4Meg of RAM, and an 80Meg hard disk, but to +install the entire system you'll need much more disk space, and to run +X or compile the system more RAM is recommended. (4Meg will actually +allow you to run X and/or compile, but it's extremely slow.) + +Supported devices include: + + Standard floppy controllers + + Standard hard disk controllers: + MFM + ESDI + IDE + RLL + + SCSI hard disk controllers: + Adaptec 154x series * [kcopy-ah-floppy] + Adaptec 174x series + Buslogic 545S + Bustek 742 (EISA) [kcopy-bt-floppy] + DTC 3290 in 1542 emulation mode * + Ultrastor 14f and 34f + + * Your system can NOT have more than 16MB of memory with + these controllers. + + Display Adaptors: + MDA + CGA + VGA (and SVGA) + HGC + + Serial communications ports + 8250 + 16450 + 16550A + [4-port multi-serial cards - require kernel built + with MULTI_PORT option] + [We do not support the Intel 82501 serial chip used + in some PC's at this time] + + Ethernet controllers + SMC/WD 8003, 8013, and equivalents + (including the SMC "Elite" series) + Novell NE1000, NE2000 + 3COM 3c503 + ISOLAN ISOLink + + Tape drives: + QIC-02 format tape drives + most SCSI tape/DAT drives + [an early QIC-40 or QIC-80 tape driver exists, + but is not yet incorporated into FreeBSD] + + CD-ROM drives: + Mitsumi CDROM drive with Mitsumi Controller + Most SCSI CD-ROM drives on a supported SCSI controller + +To be detected by the distributed kernels, the devices must +be configured as follows: (Note: IRQ 9 is the same as IRQ 2 +on ISA/EISA based machines) + +Device Name Port IRQ DRQ Misc +------ ---- ---- --- --- ---- +Floppy Cntlr. fd0 0x3f0 6 2 + +Std. Hard Disk Cntlr. + wd0 0x1f0 14 + +AHA-154x SCSI Cntlr. 0x330 11 5 [kcopy-ah-floppy] + +AHA-174x SCSI Cntlr. automatically configured [kcopy-ah-floppy] + +BT742 SCSI Cntlr. 0x330 12 [kcopy-bt-floppy] + +UHA-14f SCSI Cntlr. or +UHA-34f SCSI Cntlr. 0x330 14 5 [kcopy-bt-floppy] +(In FreeBSD GAMMA and before, UHA was on IRQ 11) + +SCSI Disks sd[0-2] automatically configured + +SCSI Tapes st[01] automatically configured + +SCSI CD-ROMs cd0 automatically configured + +Serial Ports com0 0x3f8 4 + com1 0x2f8 3 + com2 0x3e8 5 + com3 0x3f8 9 + +SMC/WD Ethernet or +3COM 3c503 ed0 0x280 5 iomem 0xd8000 + +NOTE for 386bsd users: the we0 device for the WD80xxyy card has been +replaced with an ed0 device. The default settings of 9/280/d000 have +been changed to 5/280/d800 as this address accomdates all of the boards. + +Novell Ethernet ed0 0x280 5 + +NOTE for 386bsd users: the ne0 device for the NEx000 card has been +replaced with an ed0 device. The default settings of 9/300 have +been changed to 5/280. + +ISOLAN ISOLink is0 0x280 10 7 + +QIC-02 Tape wt0 0x300 5 1 + +Parallel (Printer) Port + lpt0 0x3BC 7 + +Interruptless Parallel (Printer) Port + lpa0 0x378 + lpa1 0x278 + +N.B.: Disable the lpt interrupt on the board or you will +have problems using the lpa drivers. + +Hard-Disk Storage Requirements +--------- ------- ------------ + +The minimum base installation of FreeBSD requires a free hard disk +partition with at least 16 MB free space. This is only enough for +the three installation disks, which don't support a multi-user +shell. + +The full binary distribution extracts to about 46 MB. +The full source distribution extracts to about 72 MB. +The kernel source only extracts to about 7 MB. +To recompile the sources requires an additional 55 MB. +To recompile the kernel requires an additional 2 MB. + +Since additional room is required for extracting the distributions, +a full binary installation requires a minimum of about 80 MB (46 +MB extracted + 16 MB archived + 8 MB minimum swap + room for +extracting). + +A complete source + binary distribution requires a minimum of +about 210 MB (assuming a minimum 8 MB swap). + + +Getting the System on to Useful Media: +------- --- ------ -- -- ------ ----- + +Installation is supported from several media types, including: + + MS-DOS floppies + MS-DOS hard disk (Primary partition) + Tape + NFS partitions + FTP + Kermit + +No matter what you do, however, you'll need at least three disks (1.2M +or 1.44M) handy, on which you will put the kernel-copy image and the +install (or upgrade) floppy images. + +The images are available from the directory "floppies", under the root +of the FreeBSD/FreeBSD-1.0 tree at your favorite archive site. +They're available both as raw disk images, and gzipped, to save time +downloading. + +If you are using an AHA-154x or AHA-1742 SCSI host adapter, you need +the kcopy-ah-floppy image. If you're using a BT-742 SCSI host adapter +or an Ultrastor adaptor, then you'll need the kcopy-bt-floppy image. +If you're using MFM/RLL/IDE disk controllers, you can use either +kernel-copy floppy image. + +If you are using UNIX to make the floppies, you should use the command +dd(1) to write the raw floppy images (i.e., kcopy-ah-floppy or +kcopy-bt-floppy, filesystem-floppy and cpio-floppy) to the floppies. +For example, to write kcopy-ah-floppy to a 5.25" 1.2 Mb floppy +disk under 386BSD, use: + + $ dd if=kcopy-ah-floppy of=/dev/fd0a bs=30b count=80 + +or for a 3.5" 1.44 Mb floppy: + + $ dd if=kcopy-ah-floppy of=/dev/fd0a bs=36b count=80 + +If you are using DOS to make the floppies, use the rawrite.exe +utility. This can be found in the "tools" subdirectory of the +archive site. Copy rawrite.exe and the binary images to a DOS +disk, type "rawrite" under MS-DOS and follow the instructions. +Rawrite can write binary images to either 1.2MB or 1.44MB +MS-DOS-formatted floppies. + +Any other programs from the tools directory that might be needed +for installing FreeBSD, such as kermit, should be copied to a DOS- +formatted floppy (1.2MB or 1.44MB). Under 386BSD, they can be +copied to floppy using the mcopy command. Under DOS, use the DOS +copy command. + +The steps necessary to prepare the distribution sets for installation +depend on which method of installation you choose. The various methods +are explained below. + +To prepare for installing via MS-DOS hard disk: + + To prepare FreeBSD for installaton from the MS-DOS C: drive + of the hard disk, you need to do the following: + + If FreeBSD is installed on a hard disk containing + a Primary MS-DOS partition (as opposed to an + Extended DOS partition), then the FreeBSD distribution + files can be read directly from DOS. Preparation + is just a matter of copying the FreeBSD distribution + files onto DOS C: drive of the hard disk. + + If FreeBSD is installed on a separate hard disk than + MS-DOS, it is not currently possible to read the FreeBSD + distribution files directly from DOS. In this case, + a different medium should be used. + + Once you have the files on the C: drive, you can proceed to the + next step in the installation process, viz preparing your hard + disk. + +To prepare for installing via MS-DOS floppies: + + To prepare FreeBSD for installaton from MS-DOS floppies, you + need to do the following: + + Count the number of "_tgz.xx" files + you have (these are split, gzip'ed, tar + archives). Call this number N. You will + need N/6 1.44M floppies, or N/5 1.2M + floppies to install the distribution + in this manner. For the set of bin files + (i.e., 80 files) and 1.2 Mb floppies you will + need 16 disks. + + Format all of the floppies, with MS-DOS. + Don't make any of them MS-DOS bootable + floppies (i.e., don't use "format /s"!) + If you use "format /u" then the format + will run a tad faster. + + Copy all of the "_tgz.xx" files on + the DOS disks. Under DOS use the DOS copy + command. Under 386BSD, use, for instance, + the make_floppies script: + + #!/bin/sh + N_PER_DISK=5 + + x=$N_PER_DISK + for dist in bin_tgz.*; do + if [ $x -ge $N_PER_DISK ]; then + x=0 + echo -n "Insert next disk, " + echo -n "and press ENTER... " + read reply + mdel a:/\* + fi + mcopy $dist a:/ + x=`expr $x + 1` + done + + (Or you might use tar instead). + + Once you have the files on DOS disks, you can proceed to the + next step in the installation process, viz preparing your hard + disk. + +To prepare for installing via a tape: + + To install FreeBSD from a tape, you need to be somehow + to get the FreeBSD filesets you wish to install on + your system on to the appropriate kind of tape, + in tar format. + + If you're making the tape on a UN*X system, the easiest + way to do so is: + + tar cvf + + where "" is the name of the tape device + that describes the tape drive you're using (either + /dev/rst0 for SCSI tape, otherwise /dev/rwt0). + If you can't figure it out, ask your system administrator. + "" are the names of the ".tar.gz.xx" files + which you want to be placed on the tape. + + If your tape drive is not a type recognzed by the + kernel, then it may be necessary to set the tape density + using either the st(1) command (for SCSI tape) or the + mt(1) command. Both these programs are available from + the tools directory of the FreeBSD archive site. + +To prepare for installing via an NFS partition: + + NOTE: this method of installation is recommended + only for those already familiar with using + the BSD network-manipulation commands and + interfaces. If you aren't, this documentation + should help, but is not intended to be + all-encompassing. + + Place the FreeBSD software you wish to install into + a directory on an NFS server, and make that directory + mountable by the machine which you will be installing + FreeBSD on. This will probably require modifying the + /etc/exports file of the NFS server and resetting + mountd, acts which will require superuser privileges. + Make a note of the numeric IP address of the NFS server + and make a note of the router closest to the the new + FreeBSD machine if the NFS server is not on a network + which is directly attached to the FreeBSD machine. + + Once you have done this, you can proceed to the next + step in the installation process, preparing your hard disk. + +To prepare for installing via FTP: + + NOTE: this method of installation is recommended + only for those already familiar with using + the BSD network-manipulation commands and + interfaces. If you aren't, this documentation + should help, but is not intended to be + all-encompassing. + + The preparations for this method of installation + are easy: all you have to do is make sure that + there's some FTP site from which you can retrieve + the FreeBSD installation when it's time to do + the install. You should know the numeric IP + address of that site, and the numeric IP address of + your nearest router if the new FreeBSD computer is + not on the same net or subnet as the FTP site. + + Once you have done this, you can proceed to the next + step in the installation process, preparing your hard disk. + +To prepare for installing via Kermit: + + The preparations for this method of installation + require that the kermit program be put on the + dos-floppy installation disk. This will be + loaded as part of the minimum base installation. + Kermit is available from tools directory of the + FreeBSD FTP site. This is a FreeBSD binary and + only executes under the FreeBSD operating system. + + Once you have done this, you can proceed to the next + step in the installation process, preparing your hard disk. + +To upgrade: + + (The beta upgrade script is available on request from + FreeBSD-questions@freefall.cdrom.com) + +Preparing your Hard Disk for FreeBSD Installation: +--------- ---- ---- ---- --- ------ ------------ + +NOTE: If you wish to install FreeBSD on your whole drive, (i.e. you do +not want DOS or any other operating system on your hard disk), you can +skip this section, and go on to "Installing the FreeBSD System." + +Firstly, be sure you have a reliable backup of any data which you may +want to keep; repartitioning your hard drive is an excellent way to +destroy important data. + +WARNING: If you are using a disk controller which supports disk +geometry translation, BE SURE TO USE THE SAME PARAMETERS FOR FreeBSD AS +FOR DOS! If you do not, FreeBSD will not be able to properly coexist +with DOS. + +Secondly, make sure your disk has at least 16 Mbytes free space (or +80 Mbytes for the complete binary distribition). + +You are now set to install FreeBSD on your hard drive. + +Installing the FreeBSD System: +---------- --- ------ ------ + +If DOS or OS/2 is already installed on the hard disk, installation should +be easy. By default FreeBSD is installed after the last DOS or OS/2 +partition. Otherwise, you may need to specify your hard disk's geometry +(i.e., number of cylinders, heads and sectors per track). + +For computing partition sizes, it might help to have a calculator handy. + +And it's finally time to install the system! + +The following is a walk-through of the steps necessary to get FreeBSD +installed on your hard disk. If you wish to stop the installation, you +may hit Control-C at any prompt and then type `halt'. + + Boot from the kcopy-ah or kcopy-bt floppy, depending on + your hard disk controller type. + + When prompted to insert the filesystem floppy, remove the + kcopy floppy from the drive and insert filesystem floppy + and hit any key. + N.B.: The filesystem floppy must not be write protected. + + [When booting, if no message prompt appears after a + reasonable period of time, reboot and try it again. If + this doesn't work, try disabling your CPU's internal and + external caches, and then try to boot again. If there is + still no message prompt, then you can't install FreeBSD + on your hardware. If you were able to install 386bsd, + this is definitely a bug in our software; please report + it! Please include your system configuration, and any + other relevant information in your bug report.] + + The boot sequence continues after the filesystem floppy + has been inserted. A copyright notice is displayed along + with a list of the hardware that FreeBSD recognizes as + being in your machine. You might want to make a note of + the disk values for cylinders, heads, sectors etc for + later use. + + After a short while (approximately 30 to 60 seconds), you + should see a welcome message and a prompt, asking if you + wish to proceed with the installation. + + If you wish to proceed, enter "y" and then return. + + You will then be asked what type of disk drive you have. + The valid options are listed on the screen (e.g., SCSI, ESDI). + + You will then be asked for a label name for your disk. + This should be a short, one-word name for your disk, + e.g., "cp3100-mine" for a Conner Peripherals "3100" disk. + You needn't remember this name. + + Next, you will be prompted for the geometry information. + The default values should be correct, in which case just + hit ENTER to accept them. Otherwise enter the values + that were displayed during the boot sequence as they are + requested. + + The default size of the FreeBSD portion of the disk + is the maximum available at the end of the disk (which may + be the whole disk). Accept the default by hitting ENTER. + Otherwise, enter an appropriate value using the information + displayed. + + If you are not installing on the whole disk, you will be + asked for the offset of the FreeBSD partition from the + beginning of the disk. Again, hit ENTER to accept the + default, or enter a cylinder offset from the beginning of + the disk. + + You will then be asked for the size of your root partition, + in cylinders. The suggested maximum size is 15 Mbytes + which is used as a default. Accept this, or enter a + suitable value (after converting to cylinders using the + formula displayed). + + Next, you will be asked for the size of your swap partition + - again, you must calculate this in cylinders. You should + probably allocate around twice as much swap space as you + have RAM memory. If you wish the system to save crash dumps + when it panics, you will need at least as much swap as you + have RAM. + + The install program will then ask you for information about + the rest of the partitions you want on your disk. For the + purposes of this document, you only want one more: /usr. + Therefore, at the prompt, when in asks you to enter the size + of the next partition, enter the number of cylinders remaining + in the FreeBSD portion of the disk. When it asks you for the + mount point for this partition, say "/usr". + + After the FreeBSD partition have been assigned, install checks + the disk for an MS-DOS partition. If one exists, you are prompted + whether to make this accessible from FreeBSD (i.e., for reading + and writing). And if you choose to make the DOS partition + accessible, you are prompted for what directory it should + be mounted on. "/dos" is used by default. With this + choice, you could copy the contents of the DOS root + directory (i.e., C:\), for instance, with the Unix command: + + # cp /dos/* . + + If have you a DOS partition and you don't want it visible + from FreeBSD, just respond with "n" when asked whether to + make it accessible. + + YOU ARE NOW AT THE POINT OF NO RETURN. + + If you confirm that you want to install FreeBSD, your hard + drive will be modified, and perhaps it contents scrambled at + the whim of the install program. This is especially likely + if you gave the install program incorrect information. + Enter "no" at the prompt to get the option of redoing the + configuration, using your previous choices as defaults. + + If you are sure you want to proceed, enter "yes" at the prompt. + + The install program now makes the filesystems you specified. + If all goes well, there should be no errors in this section + of the installation. If there are, restart from the the + beginning of the installation process. + + After the installation program prompts you to see if you'd + like to be told about all of the files it's going to copy + to your hard drive, it will spend a few minutes copying these + files and then will print out an informative message and + place you at a "#" prompt. + + Read the message and note which partition (e.g., sd0a or wd0a) + you need to copy a kernel to. Reboot the machine off the + kcopy-xx-floppy disk, but this time at the prompt asking + you to insert a file system floppy, do _not_ replace the + floppy, just press . + + At the "kc>" prompt, enter "copy" to prepare to copy the + kernel on the floppy to your hard disk. + + At the next "kc>" prompt, enter the disk partition to which + you want to copy the kernel. (e.g., sd0a or wd0a). + + It will work for a minute or two, then present you with + another "#" prompt. Follow the instructions given, (i.e., + halt the system) and reboot from the hard disk. You will + probably have to do a hardware reset or else your ethernet + card might not be recognised at reboot (e.g., if you have a + WD8003EP card). + + When the machine boots, a three-line banner should appear at + the top of the screen. In a few seconds, a series of + messages will appear, describing the hardware in your machine. + Once again, this stage can take up to two minutes, so DO NOT + PANIC! + + You will be asked to insert the cpio-floppy into a floppy + drive, and enter that drive's number. "0" corresponds to + DOS's "A:" drive, "1" corresponds to DOS's "B:" drive. + + After you enter the number it will ask you if you'd like to + watch its progress, and after you answer this question it + will begin installing still more files on your hard disk. + This should take no more than 3 minutes. + + You are given the option to load the dos-floppy disk. + In particular, if you want to use kermit for downloading + the distribution, the dos-floppy should have the kermit + binary. Or if you are using SCSI tape, the dos-floppy should + contain the st command. + + To load the dos-floppy, remove the cpio-floppy from the + drive, insert the dos-floppy and enter a "yes" response + at the prompt. Otherwise, enter "no" at the prompt. + + After the dos-floppy has been loaded, you are given (more) + instructions, (e.g., to halt the system) and you should + reboot the machine again, from the hard drive and probably + with a hardware reset to kick your ethernet card back into + life. + + CONGRATULATIONS: You now have the minimum base of FreeBSD + files on your hard disk! Now you get to install the + distribution file sets. Remember that, at minimum, you must + install the bin.tar.gz.xx file set (see below for + instructions). + + After the machine is done booting, you will be presented + with a screenful of information about what to do next. + + What you do from this point on depends on which media you're + using to install FreeBSD. Follow the appropriate + instructions, given below. + + To install from MS-DOS hard disk partition, floppy or tape: + + The first thing you should do is to choose a temporary + directory where the distribution files can be stored. + To do this, use the command "set_tmp_dir" and enter + your choice. The default is /usr/distrib. + + After you have chosen a temporary directory, + you should issue the appropriate load command: + + load_fd - for loading from a MS-DOS hard disk + partition, or from floppies, + + load_qic_tape - for loading from QIC-02 tape, or + + load_scsi_tape - for you're loading from the first + SCSI tape drive in the system. + + If loading from tape, it may be necessary to first + set the default density using the mt or st command. + The low-density device (/dev/rst0 or /dev/rmt0) + is used by the load_xx_tape command, so to prepare + a SCSI device for reading QIC-150 tape, you might use: + + # st -f /dev/nrst0 rewind + # st -f /dev/nrst0 low_dnsty 16 + # load_scsi_tape + + If loading from floppy or hard disk, the load_fd + command prompts for information, such as to which + floppy drive or hard disk directory to load from. + Additional options are available, e.g., for listing + and, if loading from hard disk, changing source + directories. + + Go to the directory which contains the first + distribution set you wish to install. This is + either the directory you specified above, if using + load_fd, or possibly a subdirectory of that + directory, if you loaded from tape. + + When there, run "set_tmp_dir" again, and choose + the default temporary directory, by hitting + return at the prompt. + + Run the "extract" command, giving it as its sole + argument the name of the distribution set you + wish to extract. For example, to extract the binary + distribution, use the command: + + extract bin + + and to extract the source distribution: + + extract src + + After the extraction is complete, go to the location + of the next set you want to extract, "set_tmp_dir" + again, and once again issue the appropriate + extract command. Continue this process until + you've finished installing all of the sets which you + desire to have on your hard disk. + + After each set is finished, if you know that you + are running low on space you can remove the + distribution files for that set by saying: + + rm * + + For example, if you wish to remove the distribution + files for the binarydist set, after the "extract bin" + command has completed, issue the command: + + rm bin* + + Once you have extracted all sets and are at the "#" prompt + again, proceed to the section "Configuring Your System," + below. + + To install via FTP or NFS: + + First you must decide on a temporary directory to hold + the .tar.gz.xx files. The directory /usr/distrib + is suggested. You should cd to it, if necessary do + a mkdir first. Use set_tmp_dir to identify this + directory to the install process. + + Configure the appropriate ethernet interface (e.g. ed0, + ne0, etc.) up, with a command like: + + ifconfig [netmask ] + + where is the interface name (e.g. ed0, etc.), + and is the numeric IP address of the interface. + If the interface has a special netmask, supply + the word "netmask" and that netmask at the end of the + command line. For instance, without a special netmask: + + ifconfig ed0 129.133.10.10 + + or with a special netmask + + ifconfig ed0 128.32.240.167 netmask 0xffffff00 + + or the equivalent + + ifconfig ed0 128.32.240.167 netmask 255.255.255.0 + + If you are using the AUI connector on a 3C503 card, you + must also set the LLC0 flag (the default is to use the BNC + connector): + + ifconfig ed0 130.252.23.86 llc0 + + If the NFS server or FTP server is not on a directly- + connected network, you should set up a route to it + with the command: + + route add default + + where is your gateway's numeric IP address. + + If you are NFS-mounting the distribution sets, + mount them on the temporary directory with the command: + + mount -t nfs : + + where is the server's numeric IP address, + is the path to the distribution files on + the server, and is the name of the local + temporary directory (e.g., /usr/distrib). Proceed as if + you had loaded the files from tape, "cd"ing to the + appropriate directories and running "set_tmp_dir" and + "extract" as appropriate. + + If you are retrieving the distribution sets using ftp, + cd into the temp directory, and execute the command: + + ftp + + where is the server's numeric IP address. + Get the files with FTP, taking care to use binary mode + to transfer all files. A simple set of commands is + + ftp + user ftp + passwd @ + hash + binary + prompt + cd + mget * + cd + mget * + quit + + Once you have all of the files for the distribution sets + that you wish to install, you can proceed using the + instructions above as if you had installed the files + from a floppy. + + To install via Kermit: + + First you must decide on a temporary directory to hold + the .tar.gz.xx files. The directory /usr/distrib + is suggested. You should cd to it, if necessary do + a mkdir first. Use set_tmp_dir to identify this + directory to the install process. + + Invoke kermit and dial the remote kermit server. + A typical session might be: + # stty -f /dev/sio01 clocal + # kermit + C-Kermit> set file type binary + C-Kermit> set line /dev/sio01 + C-Kermit> set baud 9600 + C-Kermit> set receive packet 740 + C-Kermit> set window 4 + C-Kermit> set block 2 + C-Kermit> connect + Connecting to /dev/sio01, speed 9600. + The escape character is Ctrl-\ (ASCII 28, FS) + Type the escape character followed by C to get back, + or followed by ? to see other options. + atdt 1234567 <-- dial the remote + Connect 9600 + login: mylogin <-- login to the remote + [...] + remote$ kermit -ix <-- remote kermit as binary server + [...] + ^\C <-- return to local kermit + C-Kermit> get bin_tgz* <-- request files from remote + [...] (wait long for transfer to complete) + C-Kermit> finish <-- terminate remote server + C-Kermit> connect + C-Kermit> exit <-- exit remote kermit + remote$ exit <-- exit remote host + ^\C <-- return to local kermit + C-Kermit> exit <-- exit local kermit + + At this point the binary distribution should be + downloaded to the FreeBSD system. Run the "extract" + command, giving it as its sole argument the name + of the distribution set you wish to extract. For + example, to extract the binary distribution, use + the command: + + extract bin + + and to extract the source distribution: + + extract src + + After the extraction is complete, go to the location + of the next set you want to extract, "set_tmp_dir" + again, and once again issue the appropriate + extract command. Continue this process until + you've finished installing all of the sets which you + desire to have on your hard disk. + + After each set is finished, if you know that you + are running low on space you can remove the + distribution files for that set by saying: + + rm * + + For example, if you wish to remove the distribution + files for the binarydist set, after the "extract bin" + command has completed, issue the command: + + rm bin* + + Once you have extracted all sets and are at the "#" prompt + again, proceed to the section "Configuring Your System," + below. + + +Further Tips on Installing FreeBSD +------- ---- -- ---------- ------- + + You might wish to install the binarydist first, get that + working, and then at a later point in time have a go at + installing the sourcedist. BEFORE YOU REBOOT AFTER INSTALLING + THE BINARYDIS, you must preserve the commands that do the + extracting. They are kept in the single-user-mode .profile + file called /.profile. Proceed like this: + + mv /.profile /.profile.install + ln /root/.profile /.profile + + When you are ready to install the sourcedist at some time + in the future, get into multi-user mode (i.e., the normal + means of running FreeBSD) and issue these commands: + + cp /.profile.install /.profile + shutdown now + + This will cause the system to go into single-user mode, and + the install profile will be active (i.e., you will find the + commands load_fd, extract etc available to you again). + + If your disk has several operating systems, you may want + to install a boot manager such as Thomas Wolfram's os-bs + for selecting which system to boot. os-bs135.exe and other + boot managers are available from the tools directory of + the FreeBSD FTP site. os-bs works well with DOS, OS/2, + FreeBSD and other systems, however, it cannot currently + be used to boot FreeBSD from a second hard disk. Another + boot manager, such as boot-easy should be used. + + To install, for instance, os-bs, boot the system with + MS-DOS and insert the dos-floppy containing os-bs135.exe + in floppy drive A:. Then enter the DOS commands: + > A: + > os-bs135 + > cd os-bs + > os-bs + A menu should now appear on the screen. Use the cursor keys + to highlight the install option, hit ENTER, and follow the + instructions from there. + + For more information about the ob-bs program, including its + capabilities and limitations, see the file `readme.1st' in the + os-bs directory. + + If your disk has several operating systems and you choose + not to install os-bs, then fdisk can be used to change + the boot system. This is done by making the primary + partition for the boot system active. FreeBSD has an + fdisk command that can be used for this purpose as well. + + +Configuring Your System: +----------- ---- ------ + +Once you have finished extracting all of the distribution sets that you +want on your hard drive and are back at the "#" prompt, you are ready +to configure your system. + +The configuration utility expects that you have installed the base +system. If you have not, you will not be able to run it successfully +(nor will you have a functional system regardless of configuration). + +To configure the newly installed operating system, run the command +"configure". + +Configure will ask for the machine's hostname, domain name, and other +network configuration information. You should check that configure has +set up the following files correctly: + + /etc/netstart + /etc/myname + +Once you have supplied configure all that it requests, your machine +will be configured well enough that when you reboot it it will be a +completely functional FreeBSD system. It is not completely configured, +however; you should adjust the /etc/sendmail.cf file as necessary to +suit your site and/or disable sendmail in /etc/rc and you should look +in /etc/netstart to make sure the flags are defined correctly for your +site. You might wish to set up several other tcp/ip files, such as + + /etc/resolv.conf + /etc/networks + +Once you are done with configuration, reboot with the "reboot" command. + +When it boots off of the hard drive, you will have a complete FreeBSD +system! CONGRATULATIONS! (You really deserve them!!!) + + +Administrivia: +------------- + +Registration? What's that? + +If you've got something to say, do so! We'd like your input. + +Please send random comments to: + + FreeBSD-questions@freefall.cdrom.com + +Please send bug reports, and that sort of material to: + + FreeBSD-bugs@freefall.cdrom.com + +If you'd like to help with this effort, and have an idea as to how +you could be useful, send mail to: + + FreeBSD-hackers@freefall.cdrom.com + +THANKS FOR USING THIS; that's what makes it all worthwhile. + +[a favor: Please avoid mailing huge documents or files to these mailing lists, + as they will end up in our personal mail spools. We will be + happy to make other arrangements] + +This is $Id: install_notes,v 1.10 1993/10/16 12:05:51 rgrimes Exp $ diff --git a/etc/etc.i386/kc.profile b/etc/etc.i386/kc.profile new file mode 100644 index 0000000000..3502893429 --- /dev/null +++ b/etc/etc.i386/kc.profile @@ -0,0 +1,83 @@ +# $Header +# +# rc for kernel distribution floppy + +PATH=/bin:/sbin +export PATH + +reboot_it() { + echo "" + echo "halting the machine..." + + halt + sync + sync + echo "Halt failed! Try power-cycling the machine..." + exit 1 +} + +bail_out() { + echo "" + echo "Time to reboot the machine!" + echo "Once the machine has halted (it'll tell you when)," + echo "remove the floppy from the disk drive and press" + echo "any key to reboot." + reboot_it +} + +echo Enter '"copy"' at the prompt to copy the kernel on this +echo floppy to your hard disk. enter anything else to reboot, +echo but wait for the machine to restart to remove the floppy. +echo "" +echo -n "kc> " + +read todo + +if [ X"$todo" = Xcopy ]; then + echo "" + echo "What disk partition should the kernel be installed on?" + echo "(e.g., "wd0a", "sd0a", etc.)" + echo "" + echo -n "copy kernel to> " + while :; do + read diskpart junk + [ -c /dev/r$diskpart ] && break + echo "${diskpart}: invalid partition" + echo + echo -n "copy kernel to> " + done + echo "" + echo "Checking the filesystem on $diskpart..." + fsck -y /dev/r$diskpart + if [ $? -ne 0 ]; then + echo "" + echo "fsck failed... Sorry, can't copy kernel..." + bail_out + fi + echo "" + echo "mounting $diskpart on /mnt..." + mount /dev/$diskpart /mnt + if [ $? -ne 0 ]; then + echo "" + echo "mount failed... Sorry, can't copy kernel..." + bail_out + fi + echo "" + echo "Please wait. Copying kernel..." + cp /386bsd /mnt/386bsd + if [ $? -ne 0 ]; then + echo "" + echo "Copy failed... (?!?!?!)" + bail_out + fi + echo "" + echo "unmounting $diskpart..." + umount /mnt > /dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "" + echo "unmount failed... Shouldn't be a problem..." + fi + bail_out +fi + +reboot_it diff --git a/etc/gettytab b/etc/gettytab index 62f60bf0b3..29eb925ff7 100644 --- a/etc/gettytab +++ b/etc/gettytab @@ -8,8 +8,32 @@ # The default gettytab entry, used to set defaults for all other # entries, and in cases where getty is called with no table name # +# cb, ce and ck are desirable on most crt's. The non-crt entries need to +# be changed to turn them off (ce@:ce@:ck@:). +# +# Parity defaults to even. There ought to be more alternative entries with +# no parity. The Pc entry already has no parity.. The different parities +# are: +# (none): same as even except -inpck instead of inpck for login. +# ep: getty will use raw mode (cs8 -parenb) (unless rw is set) and +# fake parity. login will use even parity (cs7 parenb -parodd). +# op: same as ep except odd parity (cs7 parenb parodd) for login. +# op overrides ep. +# ap: same as ep except -inpck instead of inpck for login. +# ap overrides op and ep. +# np: 1. don't fake parity in getty. The fake parity garbles +# characters on non-terminals (like pccons) that don't +# support parity. It would probably better for getty not to +# try to fake parity. It could just use cbreak mode so as +# as not to force cs8 and let the hardware handle the parity. +# login has to be rely on the hardware anyway. +# 2. set PASS8, giving cs8 -parenb -istrip -inpck. +# np:ep: same as np except inpck. +# np:op: same as np:ep except for parodd (but parodd is overridden). +# np:ap: same as np except istrip. +# default:\ - :ap:fd#1000:im=\r\n 386BSD (%h) (%t)\r\n\r\n:sp#1200: + :cb:ce:ck:fd#1000:im=\r\n FreeBSD (%h) (%t)\r\n\r\n:sp#1200: # # Fixed speed entries @@ -41,6 +65,12 @@ f|std.1200|1200-baud:\ :sp#9600: g|std.19200|19200-baud:\ :sp#19200: +std.38400|38400-baud:\ + :sp#38400: +std.57600|57600-baud:\ + :sp#57600: +std.115200|115200-baud:\ + :sp#115200: # # Dial in rotary tables, speed selection via 'break' @@ -78,7 +108,7 @@ t1200:\ #telebit (9600) # t9600:\ - :nx=t2400a:tc=19200-baud: + :nx=t2400a:tc=9600-baud: t2400a:\ :nx=t1200a:tc=2400-baud: t1200a:\ @@ -106,7 +136,7 @@ X|Xwindow|X window system:\ :fd@:nd@:cd@:rw:sp#9600: P|Pc|Pc console:\ - :np:ap:sp#9600: + :np:sp#115200: # # Wierdo special case for fast crt's with hardcopy devices diff --git a/etc/group b/etc/group index c2d3ae7e98..900aff8a60 100644 --- a/etc/group +++ b/etc/group @@ -1,4 +1,4 @@ -wheel:*:0:root,bill,lynne +wheel:*:0:root daemon:*:1:daemon kmem:*:2:root sys:*:3:root diff --git a/etc/host.conf b/etc/host.conf new file mode 100644 index 0000000000..bda5254786 --- /dev/null +++ b/etc/host.conf @@ -0,0 +1,4 @@ +# Default is to use the nameserver first +bind +# If that doesn't work, then try the /etc/hosts file +hosts diff --git a/etc/inetd.conf b/etc/inetd.conf index 699f6420e2..be1bf69e92 100644 --- a/etc/inetd.conf +++ b/etc/inetd.conf @@ -13,7 +13,7 @@ exec stream tcp nowait root /usr/libexec/rexecd rexecd finger stream tcp nowait nobody /usr/libexec/fingerd fingerd #tftp dgram udp wait nobody /usr/libexec/tftpd tftpd comsat dgram udp wait root /usr/libexec/comsat comsat -talk dgram udp wait root /usr/old/talkd talkd +#talk dgram udp wait root /usr/old/talkd talkd ntalk dgram udp wait root /usr/libexec/ntalkd ntalkd echo stream tcp nowait root internal discard stream tcp nowait root internal @@ -28,7 +28,14 @@ time dgram udp wait root internal # Kerberos authenticated services klogin stream tcp nowait root /usr/libexec/rlogind rlogind -k eklogin stream tcp nowait root /usr/libexec/rlogind rlogind -k -x -kshell stream tcp nowait root /usr/libexec/rshd rshd -k +kshell stream tcp nowait root /usr/libexec/rshd rshd -k # Services run ONLY on the Kerberos server #krbupdate stream tcp nowait root /usr/libexec/registerd registerd #kpasswd stream tcp nowait root /usr/libexec/kpasswdd kpasswdd +# +# RPC based services +# You MUST have portmapper running to use these! +#mountd/1 dgram rpc/udp wait root /sbin/mountd mountd +#rstatd/1-3 dgram rpc/udp wait root /usr/libexec/rpc.rstatd rpc.rstatd +#rusersd/1-2 dgram rpc/udp wait root /usr/libexec/rpc.rusersd rpc.rusersd +#walld/1 dgram rpc/udp wait root /usr/libexec/rpc.rwalld rpc.rwalld diff --git a/etc/man.conf b/etc/man.conf deleted file mode 100644 index e7c5a994e8..0000000000 --- a/etc/man.conf +++ /dev/null @@ -1,34 +0,0 @@ -# @(#)man.conf 5.4 (Berkeley) 6/30/90 - -# sheer, raging paranoia... -_version BSD.1 - -# whatis/apropos database -_whatdb /usr/share/man/whatis.db - -# subdirectories for paths ending in '/'; note order -_subdir cat1 cat8 cat6 cat2 cat3 cat4 cat5 cat7 cat3f - -# sections and their directories -# paths ending in '/' are the equivalent of entries specifying that directory -# with all of the subdirectories listed for the keyword _order. - -# default -_default /usr/share/man/ /usr/share/man/old/ /usr/contrib/man/ /usr/local/man/ - -# section directory -1 /usr/share/man/cat1 -2 /usr/share/man/cat2 -3 /usr/share/man/cat3 -3F /usr/share/man/cat3f -3f /usr/share/man/cat3f -4 /usr/share/man/cat4 -5 /usr/share/man/cat5 -6 /usr/share/man/cat6 -7 /usr/share/man/cat7 -8 /usr/share/man/cat8 - -contrib /usr/contrib/man/ -local /usr/local/man/ -new /usr/contrib/man/ -old /usr/share/man/old/ diff --git a/etc/master.passwd b/etc/master.passwd index d1e148a4f8..3188ec6b89 100644 --- a/etc/master.passwd +++ b/etc/master.passwd @@ -1,14 +1,10 @@ -root::0:10::0:0:Charlie &:/root:/bin/csh -toor::0:10::0:0:Bourne-again Superuser:/root: +root::0:0::0:0:Charlie &:/root:/bin/csh +toor::0:0::0:0:Bourne-again Superuser:/root: daemon:*:1:31::0:0:The devil himself:/root: -operator:*:2:28::0:0:System &:/usr/guest/operator:/bin/csh +operator:*:2:20::0:0:System &:/usr/guest/operator:/bin/csh bin:*:3:7::0:0:Binaries Commands and Source,,,:/:/dev/null games:*:7:13::0:0:Games pseudo-user:/usr/games: -uucp:*:66:1::0:0:UNIX-to-UNIX Copy:/var/spool/uucppublic:/usr/lib/uucp/uucico -nobody:*:32767:9999::0:0:Unprivileged user:/nonexistent:/dev/null -dmr:*:10:31::0:0:Dennis Ritchie:/usr/guest/dmr: -ken:*:11:31::0:0:& Thompson:/usr/guest/ken: -bill::12:10::0:0:& Jolitz:/usr/bill:/bin/csh -lynne::14:10::0:0:& Jolitz:/usr/lynne:/bin/csh +uucp:*:66:1::0:0:UNIX-to-UNIX Copy:/var/spool/uucppublic:/usr/libexec/uucp/uucico ingres:*:267:74::0:0:& Group:/usr/ingres:/bin/csh falcon:*:32766:31::0:0:Prof. Steven &:/usr/games:/usr/games/wargames +nobody:*:32767:9999::0:0:Unprivileged user:/nonexistent:/dev/null diff --git a/etc/monthly b/etc/monthly index e48ddd7033..8f026d6086 100644 --- a/etc/monthly +++ b/etc/monthly @@ -12,7 +12,7 @@ echo "Subject: $host monthly run output" echo "" echo -n "Rotating log files:" -#cd /var/log +cd /var/log for i in kerberos.log lpd-errs wtmp; do echo -n " $i" if [ -f $i.5 ]; then mv -f $i.5 $i.6; fi diff --git a/etc/motd b/etc/motd index 83f232b01e..776706f362 100644 --- a/etc/motd +++ b/etc/motd @@ -1,4 +1,4 @@ -386BSD 0.1.0 07/12/92 22:20 +FreeBSD ?.?.? (UNKNOWN) -Would you like to play a game? +Welcome to FreeBSD! diff --git a/etc/mtree/BSD.local.dist b/etc/mtree/BSD.local.dist new file mode 100644 index 0000000000..1f3bd4fc09 --- /dev/null +++ b/etc/mtree/BSD.local.dist @@ -0,0 +1,55 @@ +# fs: /usr/local +# by: rgrimes +# date: Sun Aug 29 22:56:32 PDT 1993 +# + +/set type=file uname=bin gname=bin mode=0755 +bin type=dir uname=bin gname=bin mode=0755 +.. + +etc type=dir uname=bin gname=bin mode=0755 +.. + +info type=dir uname=bin gname=bin mode=0755 +.. + +lib type=dir uname=bin gname=bin mode=0755 +.. + +man type=dir uname=bin gname=bin mode=0755 + cat1 type=dir uname=bin gname=bin mode=0755 + .. + cat2 type=dir uname=bin gname=bin mode=0755 + .. + cat3 type=dir uname=bin gname=bin mode=0755 + .. + cat4 type=dir uname=bin gname=bin mode=0755 + .. + cat5 type=dir uname=bin gname=bin mode=0755 + .. + cat6 type=dir uname=bin gname=bin mode=0755 + .. + cat7 type=dir uname=bin gname=bin mode=0755 + .. + cat8 type=dir uname=bin gname=bin mode=0755 + .. + catl type=dir uname=bin gname=bin mode=0755 + .. + man1 type=dir uname=bin gname=bin mode=0755 + .. + man2 type=dir uname=bin gname=bin mode=0755 + .. + man3 type=dir uname=bin gname=bin mode=0755 + .. + man4 type=dir uname=bin gname=bin mode=0755 + .. + man5 type=dir uname=bin gname=bin mode=0755 + .. + man6 type=dir uname=bin gname=bin mode=0755 + .. + man7 type=dir uname=bin gname=bin mode=0755 + .. + man8 type=dir uname=bin gname=bin mode=0755 + .. + manl type=dir uname=bin gname=bin mode=0755 +.. diff --git a/etc/mtree/BSD.root.dist b/etc/mtree/BSD.root.dist index f86ffade41..9f9aff7a26 100644 --- a/etc/mtree/BSD.root.dist +++ b/etc/mtree/BSD.root.dist @@ -1,94 +1,46 @@ -# fs: / -# by: bostic +# fs: / +# by: rgrimes +# date: Fri Jul 16 22:24:17 1993 # -# %W% (Berkeley) %G% # top-level files are owned by root.wheel # (else too easy to get root by compromising these) -/set owner=root group=wheel mode=0755 type=file +/set type=file uname=root gname=wheel mode=0775 -sys type=link size=11 link=usr/src/sys -a type=dir -.. -b type=dir -.. +sys type=link size=12 link=usr/src/sys dev type=dir - fd type=dir + fd type=dir uname=bin gname=bin mode=555 .. .. etc type=dir - disklabels type=dir - .. -# what is the following for? -/set nlink=1 - kerberosIV type=dir - .. mtree type=dir .. namedb type=dir .. - passwd mode=0644 - master.passwd mode=0600 - spwd.db mode=0600 - pwd.db mode=0644 - group mode=0644 - crontab mode=0644 - dumpdates group=operator mode=0664 - - # config files, writeable by root - /set mode=0644 type=file - daily - weekly - monthly - exports - fstab - hosts.equiv - hosts.lpd - inetd.conf - printcap - rc - rc.local - netstart mode=0755 - remote - security - sendmail.cf - sendmail.cw - syslog.conf - ttys - - # prototype files, not normally written by root - /set mode=0444 - ftpusers - shells - gettytab - named.boot - hosts - networks - protocols - services - localtime owner=bin group=bin - man.conf owner=bin group=bin - + uucp type=dir uname=root gname=wheel mode=755 + .. .. mnt type=dir .. + root type=dir .. -stand type=dir -.. -tmp type=dir owner=bin group=bin mode=01777 + +tmp type=dir uname=bin gname=bin mode=01777 .. + usr type=dir .. + var type=dir .. # binary directories: -/set owner=bin group=bin mode=0755 +/set uname=bin gname=bin mode=0755 bin type=dir .. sbin type=dir diff --git a/etc/mtree/BSD.usr.dist b/etc/mtree/BSD.usr.dist index 63cd1756e2..5c17453813 100644 --- a/etc/mtree/BSD.usr.dist +++ b/etc/mtree/BSD.usr.dist @@ -1,494 +1,157 @@ -# fs: /usr -# by: bostic +# fs: /usr +# by: rgrimes +# date: Sun Aug 29 22:56:32 PDT 1993 # -# @(#)BSD.usr.dist 5.5 (Berkeley) 5/9/91 -/set group=bin mode=0755 owner=bin type=file -bin type=dir owner=bin group=bin mode=0755 +/set type=file uname=bin gname=bin mode=0755 +bin type=dir uname=bin gname=bin mode=0755 .. -contrib type=dir owner=bin group=bin mode=0755 - X type=dir owner=bin group=bin mode=0755 - bin type=dir owner=bin group=bin mode=0755 - .. - include type=dir owner=bin group=bin mode=0755 - .. - man type=dir owner=bin group=bin mode=0755 - .. + +/set type=file uname=games gname=bin mode=0700 +games type=dir uname=bin gname=bin mode=0755 + hide type=dir uname=games gname=bin mode=0700 .. - bin type=dir owner=bin group=bin mode=0755 - .. - lib type=dir owner=bin group=bin mode=0755 - emacs type=dir owner=bin group=bin mode=0755 - etc type=dir owner=bin group=bin mode=0755 - .. - info type=dir owner=bin group=bin mode=0755 - .. - lisp type=dir owner=bin group=bin mode=0755 - term type=dir owner=bin group=bin mode=0755 - .. - .. - lock type=dir owner=bin group=bin mode=0777 - .. - .. - .. - man type=dir owner=bin group=bin mode=0755 - cat1 type=dir owner=bin group=bin mode=0755 - .. - cat2 type=dir owner=bin group=bin mode=0755 - .. - cat3 type=dir owner=bin group=bin mode=0755 - .. - cat4 type=dir owner=bin group=bin mode=0755 - .. - cat5 type=dir owner=bin group=bin mode=0755 - .. - cat6 type=dir owner=bin group=bin mode=0755 - .. - cat7 type=dir owner=bin group=bin mode=0755 - .. - cat8 type=dir owner=bin group=bin mode=0755 +.. + +/set type=file uname=bin gname=bin mode=0755 +include type=dir uname=bin gname=bin mode=0755 + g++ type=dir uname=bin gname=bin mode=0755 + sys type=dir uname=bin gname=bin mode=0755 .. .. - mh type=dir owner=bin group=bin mode=0755 - bin type=dir owner=bin group=bin mode=0755 - .. - include type=dir owner=bin group=bin mode=0755 - .. - lib type=dir owner=bin group=bin mode=0755 - .. - man type=dir owner=bin group=bin mode=0755 - .. + rpc type=dir uname=bin gname=bin mode=0755 .. .. -/set group=bin mode=0700 owner=games type=file -games type=dir owner=bin mode=0755 - hide type=dir owner=games group=bin mode=0700 - .. +/set type=file uname=bin gname=bin mode=0755 +lib type=dir uname=bin gname=bin mode=0755 .. -/set group=bin mode=0755 owner=bin type=file -include type=dir - X type=dir owner=bin group=bin mode=0755 - .. - -/set group=bin mode=0755 owner=bin type=file - X11 type=dir - bitmaps type=dir owner=bin group=bin mode=0755 - .. - .. - arpa type=dir owner=bin group=bin mode=0755 - .. - kerberosIV type=dir owner=bin group=bin mode=0755 - .. - pascal type=dir owner=bin group=bin mode=0755 +/set type=file uname=bin gname=bin mode=0755 +libexec type=dir uname=bin gname=bin mode=0755 + lpr type=dir uname=bin gname=bin mode=0755 .. - protocols type=dir owner=bin group=bin mode=0755 - .. - rpc type=dir owner=bin group=bin mode=0755 - .. - xnscourier type=dir owner=bin group=bin mode=0755 + uucp type=dir uname=bin gname=bin mode=0755 .. .. -/set group=bin mode=0755 owner=bin type=file -lib type=dir +local type=dir uname=bin gname=bin mode=0755 +.. -/set group=bin mode=0755 owner=bin type=file - uucp type=dir owner=uucp group=daemon mode=0755 - .. +mdec type=dir uname=bin gname=bin mode=0755 .. -/set group=bin mode=0755 owner=bin type=file -libdata type=dir - adb type=dir owner=bin group=bin mode=0755 - .. +obj type=dir uname=bin gname=bin mode=0755 +.. -/set group=bin mode=0755 owner=bin type=file - learn type=dir - C type=dir owner=bin mode=0755 - .. - bin type=dir owner=bin mode=0755 - .. - editor type=dir owner=bin mode=0755 - .. - eqn type=dir owner=bin mode=0755 - .. - files type=dir owner=bin mode=0755 - .. - macros type=dir owner=bin mode=0755 - .. - morefiles type=dir owner=bin mode=0755 - .. - .. - lint type=dir owner=bin mode=0755 - .. - pascal type=dir owner=bin mode=0755 - .. - term type=dir owner=bin mode=0755 - .. - troff_font type=dir owner=bin mode=0755 - .. +sbin type=dir uname=bin gname=bin mode=0755 .. -/set group=bin mode=0755 owner=bin type=file -libexec type=dir - lpr type=dir owner=bin group=bin mode=0755 +share type=dir uname=bin gname=bin mode=0755 + calendar type=dir uname=bin gname=bin mode=0755 .. - pascal type=dir owner=bin group=bin mode=0755 - .. - plot type=dir owner=bin group=bin mode=0755 - .. -.. -local type=dir owner=bin group=bin mode=0755 - bin type=dir owner=bin group=bin mode=0755 + + dict type=dir uname=bin gname=bin mode=0755 .. - man type=dir owner=bin group=bin mode=0755 - cat1 type=dir owner=bin group=bin mode=0755 - .. - cat2 type=dir owner=bin group=bin mode=0755 - .. - cat3 type=dir owner=bin group=bin mode=0755 - .. - cat4 type=dir owner=bin group=bin mode=0755 - .. - cat5 type=dir owner=bin group=bin mode=0755 - .. - cat6 type=dir owner=bin group=bin mode=0755 + + /set type=file uname=games gname=bin mode=0755 + games type=dir uname=bin + atc type=dir uname=games gname=bin mode=0755 .. - cat7 type=dir owner=bin group=bin mode=0755 + fortune type=dir uname=games gname=bin mode=0755 .. - cat8 type=dir owner=bin group=bin mode=0755 + larn type=dir uname=games gname=bin mode=0755 .. .. -.. -obj type=dir owner=bin group=bin mode=0755 -.. -old type=dir owner=bin group=bin mode=0755 -.. -sbin type=dir owner=bin group=bin mode=0755 -.. -/set group=bin mode=0755 owner=bin type=file -share type=dir - calendar type=dir owner=bin group=bin mode=0755 + groff_font type=dir uname=bin gname=bin mode=0755 .. -/set group=bin mode=0755 owner=bin type=file - dict type=dir - papers type=dir owner=bin group=bin mode=0755 + /set type=file uname=bin gname=bin mode=0755 + man type=dir uname=bin gname=bin mode=0755 + cat1 type=dir uname=bin gname=bin mode=0755 .. - special type=dir owner=bin group=bin mode=0755 + cat2 type=dir uname=bin gname=bin mode=0755 .. - .. - doc type=dir owner=bin group=bin mode=0755 - ps1 type=dir owner=bin group=bin mode=0755 - 01.Clang type=dir owner=bin group=bin mode=0755 - .. - 02.f77 type=dir owner=bin group=bin mode=0755 - .. - 03.f77io type=dir owner=bin group=bin mode=0755 - .. - 04.pascal type=dir owner=bin group=bin mode=0755 - .. - 05.as type=dir owner=bin group=bin mode=0755 - .. - 06.sysman type=dir owner=bin group=bin mode=0755 - .. - 07.ipctut type=dir owner=bin group=bin mode=0755 - .. - 08.ipc type=dir owner=bin group=bin mode=0755 - .. - 09.lint type=dir owner=bin group=bin mode=0755 - .. - 10.adb type=dir owner=bin group=bin mode=0755 - .. - 11.dbx type=dir owner=bin group=bin mode=0755 - .. - 12.make type=dir owner=bin group=bin mode=0755 - .. - 13.rcs type=dir owner=bin group=bin mode=0755 - .. - 14.sccs type=dir owner=bin group=bin mode=0755 - .. - 15.yacc type=dir owner=bin group=bin mode=0755 - .. - 16.lex type=dir owner=bin group=bin mode=0755 - .. - 17.m4 type=dir owner=bin group=bin mode=0755 - .. - 18.curses type=dir owner=bin group=bin mode=0755 - .. + cat3 type=dir uname=bin gname=bin mode=0755 .. - ps2 type=dir owner=bin group=bin mode=0755 - 01.cacm type=dir owner=bin group=bin mode=0755 - .. - 02.summary type=dir owner=bin group=bin mode=0755 - .. - 03.uprog type=dir owner=bin group=bin mode=0755 - .. - 04.implement type=dir owner=bin group=bin mode=0755 - .. - 05.iosys type=dir owner=bin group=bin mode=0755 - .. - 07.fp type=dir owner=bin group=bin mode=0755 + cat4 type=dir uname=bin gname=bin mode=0755 + i386 type=dir uname=bin gname=bin mode=0755 .. .. - smm type=dir owner=bin group=bin mode=0755 - 01.setup type=dir owner=bin group=bin mode=0755 - .. - 02.config type=dir owner=bin group=bin mode=0755 - .. - 03.kdebug type=dir owner=bin group=bin mode=0755 - .. - 04.quotas type=dir owner=bin group=bin mode=0755 - .. - 05.fsck type=dir owner=bin group=bin mode=0755 - .. - 06.lpd type=dir owner=bin group=bin mode=0755 - .. - 07.sendmailop type=dir owner=bin group=bin mode=0755 - .. - 08.timedop type=dir owner=bin group=bin mode=0755 - .. - 09.uucpimpl type=dir owner=bin group=bin mode=0755 - .. - 10.newsop type=dir owner=bin group=bin mode=0755 - .. - 11.named type=dir owner=bin group=bin mode=0755 - .. - 12.uchanges type=dir owner=bin group=bin mode=0755 - .. - 13.kchanges type=dir owner=bin group=bin mode=0755 - .. - 14.fastfs type=dir owner=bin group=bin mode=0755 - .. - 15.net type=dir owner=bin group=bin mode=0755 - .. - 16.sendmail type=dir owner=bin group=bin mode=0755 - .. - 17.security type=dir owner=bin group=bin mode=0755 - .. - 18.password type=dir owner=bin group=bin mode=0755 - .. - 19.porttour type=dir owner=bin group=bin mode=0755 - .. - 20.termdesc type=dir owner=bin group=bin mode=0755 - .. - 21.uucpnet type=dir owner=bin group=bin mode=0755 - .. - 22.timed type=dir owner=bin group=bin mode=0755 - .. - .. - usd type=dir owner=bin group=bin mode=0755 - 01.begin type=dir owner=bin group=bin mode=0755 - .. - 02.learn type=dir owner=bin group=bin mode=0755 - .. - 03.shell type=dir owner=bin group=bin mode=0755 - .. - 04.csh type=dir owner=bin group=bin mode=0755 - .. - 05.dc type=dir owner=bin group=bin mode=0755 - .. - 06.bc type=dir owner=bin group=bin mode=0755 - .. - 07.Mail type=dir owner=bin group=bin mode=0755 - .. - 08.mh type=dir owner=bin group=bin mode=0755 - .. - 09.newsread type=dir owner=bin group=bin mode=0755 - .. - 10.etiq type=dir owner=bin group=bin mode=0755 - .. - 11.notes type=dir owner=bin group=bin mode=0755 - .. - 12.edtut type=dir owner=bin group=bin mode=0755 - .. - 13.edadv type=dir owner=bin group=bin mode=0755 - .. - 14.edit type=dir owner=bin group=bin mode=0755 - .. - 15.vi type=dir owner=bin group=bin mode=0755 - .. - 16.ex type=dir owner=bin group=bin mode=0755 - .. - 17.jove type=dir owner=bin group=bin mode=0755 - .. - 18.sed type=dir owner=bin group=bin mode=0755 - .. - 19.awk type=dir owner=bin group=bin mode=0755 - .. - 20.msmacros type=dir owner=bin group=bin mode=0755 - .. - 21.msdiffs type=dir owner=bin group=bin mode=0755 - .. - 22.memacros type=dir owner=bin group=bin mode=0755 - .. - 23.meref type=dir owner=bin group=bin mode=0755 - .. - 24.troff type=dir owner=bin group=bin mode=0755 - .. - 25.trofftut type=dir owner=bin group=bin mode=0755 - .. - 26.eqn type=dir owner=bin group=bin mode=0755 - .. - 27.eqnguide type=dir owner=bin group=bin mode=0755 - .. - 28.tbl type=dir owner=bin group=bin mode=0755 - .. - 29.refer type=dir owner=bin group=bin mode=0755 - .. - 30.invert type=dir owner=bin group=bin mode=0755 - .. - 31.bib type=dir owner=bin group=bin mode=0755 - .. - 32.diction type=dir owner=bin group=bin mode=0755 - .. - 33.rogue type=dir owner=bin group=bin mode=0755 - .. - 34.trek type=dir owner=bin group=bin mode=0755 - .. - .. - .. - -/set group=bin mode=0755 owner=games type=file - games type=dir owner=bin - atc type=dir owner=games group=bin mode=0755 - .. - ching type=dir owner=games group=bin mode=0755 - .. - fortune type=dir owner=games group=bin mode=0755 + cat5 type=dir uname=bin gname=bin mode=0755 .. - larn type=dir owner=games group=bin mode=0755 + cat6 type=dir uname=bin gname=bin mode=0755 .. - quiz.db type=dir owner=games group=bin mode=0755 - .. - .. - -/set group=bin mode=0755 owner=bin type=file - man type=dir - cat.old type=dir owner=bin group=bin mode=0755 + cat7 type=dir uname=bin gname=bin mode=0755 .. - cat1 type=dir owner=bin group=bin mode=0755 + cat8 type=dir uname=bin gname=bin mode=0755 + i386 type=dir uname=bin gname=bin mode=0755 + .. .. - cat2 type=dir owner=bin group=bin mode=0755 + man1 type=dir uname=bin gname=bin mode=0755 .. - cat3 type=dir owner=bin group=bin mode=0755 + man2 type=dir uname=bin gname=bin mode=0755 .. - cat3f type=dir owner=bin group=bin mode=0755 + man3 type=dir uname=bin gname=bin mode=0755 .. - -/set group=bin mode=0755 owner=bin type=file - cat4 type=dir - tahoe type=dir owner=bin group=bin mode=0755 - .. - vax type=dir owner=bin group=bin mode=0755 - .. - hp300 type=dir owner=bin group=bin mode=0755 + man4 type=dir uname=bin gname=bin mode=0755 + i386 type=dir uname=bin gname=bin mode=0755 .. .. - cat5 type=dir owner=bin group=bin mode=0755 + man5 type=dir uname=bin gname=bin mode=0755 .. - -/set group=bin mode=0755 owner=bin type=file - cat6 type=dir - tahoe type=dir owner=bin group=bin mode=0755 - .. - vax type=dir owner=bin group=bin mode=0755 - .. - .. - cat7 type=dir owner=bin group=bin mode=0755 + man6 type=dir uname=bin gname=bin mode=0755 .. - -/set group=bin mode=0755 owner=bin type=file - cat8 type=dir - tahoe type=dir owner=bin group=bin mode=0755 - .. - vax type=dir owner=bin group=bin mode=0755 - .. - hp300 type=dir owner=bin group=bin mode=0755 - .. + man7 type=dir uname=bin gname=bin mode=0755 .. - old type=dir owner=bin group=bin mode=0755 - cat1 type=dir owner=bin group=bin mode=0755 - .. - cat2 type=dir owner=bin group=bin mode=0755 - .. - cat3 type=dir owner=bin group=bin mode=0755 - .. - cat4 type=dir owner=bin group=bin mode=0755 - .. - cat5 type=dir owner=bin group=bin mode=0755 - .. - cat6 type=dir owner=bin group=bin mode=0755 - .. - cat7 type=dir owner=bin group=bin mode=0755 - .. - cat8 type=dir owner=bin group=bin mode=0755 + man8 type=dir uname=bin gname=bin mode=0755 + i386 type=dir uname=bin gname=bin mode=0755 .. .. .. - me type=dir owner=bin group=bin mode=0755 + me type=dir uname=bin gname=bin mode=0755 .. - misc type=dir owner=bin group=bin mode=0755 + misc type=dir uname=bin gname=bin mode=0755 + fonts type=dir uname=bin gname=bin mode=0755 + .. .. - mk type=dir owner=bin group=bin mode=0755 + mk type=dir uname=bin gname=bin mode=0755 .. - ms type=dir owner=bin group=bin mode=0755 + skel type=dir uname=bin gname=bin mode=0755 .. - skel type=dir owner=bin group=bin mode=0755 + syscons type=dir uname=bin gname=bin mode=0755 + fonts type=dir uname=bin gname=bin mode=0755 + .. + keymaps type=dir uname=bin gname=bin mode=0755 + .. + scrnmaps type=dir uname=bin gname=bin mode=0755 + .. .. - tabset type=dir owner=bin group=bin mode=0755 + tabset type=dir uname=bin gname=bin mode=0755 .. - tmac type=dir owner=bin group=bin mode=0755 + tmac type=dir uname=bin gname=bin mode=0755 .. - zoneinfo type=dir owner=bin group=bin mode=0755 - Australia type=dir owner=bin group=bin mode=0555 + zoneinfo type=dir uname=bin gname=bin mode=0755 + Australia type=dir uname=bin gname=bin mode=0555 .. - Brazil type=dir owner=bin group=bin mode=0555 + Brazil type=dir uname=bin gname=bin mode=0555 .. - Canada type=dir owner=bin group=bin mode=0555 + Canada type=dir uname=bin gname=bin mode=0555 .. - Chile type=dir owner=bin group=bin mode=0555 + Chile type=dir uname=bin gname=bin mode=0555 .. - Mexico type=dir owner=bin group=bin mode=0555 + Mexico type=dir uname=bin gname=bin mode=0555 .. - SystemV type=dir owner=bin group=bin mode=0555 + SystemV type=dir uname=bin gname=bin mode=0555 .. - US type=dir owner=bin group=bin mode=0555 + US type=dir uname=bin gname=bin mode=0555 + .. + posix type=dir uname=bin gname=bin mode=0555 + .. + right type=dir uname=bin gname=bin mode=0555 .. .. .. -src type=dir owner=bin group=bin mode=0755 - bin type=dir owner=bin group=bin mode=0755 - .. - contrib type=dir owner=bin group=bin mode=0755 - .. - etc type=dir owner=bin group=bin mode=0755 - .. - games type=dir owner=bin group=bin mode=0755 - .. - include type=dir owner=bin group=bin mode=0755 - .. - kerberosIV type=dir owner=bin group=bin mode=0755 - .. - lib type=dir owner=bin group=bin mode=0755 - .. - libexec type=dir owner=bin group=bin mode=0755 - .. - old type=dir owner=bin group=bin mode=0755 - .. - pgrm type=dir owner=bin group=bin mode=0755 - .. - sbin type=dir owner=bin group=bin mode=0755 - .. - share type=dir owner=bin group=bin mode=0755 - .. - sys type=dir owner=bin group=sys mode=0755 - .. - usr.bin type=dir owner=bin group=bin mode=0755 - .. - usr.sbin type=dir owner=bin group=bin mode=0755 - .. +src type=dir uname=bin gname=bin mode=0755 .. diff --git a/etc/mtree/BSD.var.dist b/etc/mtree/BSD.var.dist index a77b5c1706..10b01b9db3 100644 --- a/etc/mtree/BSD.var.dist +++ b/etc/mtree/BSD.var.dist @@ -1,72 +1,63 @@ -# fs: /var -# by: bostic +# fs: / +# by: rgrimes +# date: Fri Jul 16 22:24:17 1993 # -# @(#)BSD.var.dist 5.3 (Berkeley) 5/9/91 -/set group=bin mode=0755 owner=bin type=file -account type=dir owner=bin group=bin mode=0755 +/set type=dir uname=bin gname=bin mode=0755 +backups .. - -/set group=bin mode=0755 owner=bin type=file -at type=dir - past type=dir owner=bin group=bin mode=0755 - .. -.. -backups type=dir owner=bin group=bin mode=0755 +crash .. -db type=dir owner=bin group=bin mode=0755 -.. - -/set group=bin mode=0755 owner=games type=file -games type=dir - hackdir type=dir owner=games group=bin mode=0755 - .. - larn type=dir owner=games group=bin mode=0755 - .. - phantasia type=dir owner=games group=bin mode=0755 +cron + tabs uname=root gname=wheel mode=0700 .. .. -log type=dir owner=bin group=bin mode=0755 +db .. -mail type=dir owner=bin group=bin mode=0755 +log .. -msgs type=dir owner=bin group=bin mode=0755 +mail .. -preserve type=dir owner=bin group=bin mode=0755 +msgs .. -quotas type=dir owner=bin group=bin mode=0755 +preserve .. -run type=dir owner=bin group=bin mode=0755 +run .. -rwho type=dir owner=bin group=bin mode=0755 +rwho .. - -/set group=bin mode=0755 owner=bin type=file -spool type=dir mode=0755 - -/set group=bin mode=0755 owner=bin type=file - ftp type=dir mode=0755 - bin type=dir owner=bin group=bin mode=0755 - .. - hidden type=dir owner=bin group=bin mode=0111 - .. - pub type=dir owner=bin group=bin mode=0755 - .. - .. - lpd type=dir owner=bin group=bin mode=0755 - .. - mqueue type=dir owner=root group=bin mode=0755 +spool + /set type=dir uname=bin gname=daemon mode=0755 + lock uname=uucp .. - news type=dir owner=bin group=bin mode=0755 + lpd .. - output type=dir owner=bin group=bin mode=0755 + mqueue uname=root gname=daemon .. - secretmail type=dir owner=root group=bin mode=0755 + output .. - uucp type=dir owner=uucp group=daemon mode=0755 + /set type=dir uname=uucp gname=daemon mode=0755 + uucp + .Preserve + .. + .Sequence + .. + .Status + .. + .Temp + .. + .Xqtdir + .. .. - uucppublic type=dir owner=uucp group=daemon mode=01777 + uucppublic .. .. -tmp type=dir owner=bin group=bin mode=01777 +/set type=dir uname=bin gname=bin mode=0755 +tmp mode=01777 +.. + +/set type=file uname=games gname=bin mode=0755 +games type=dir + hackdir type=dir + .. .. diff --git a/etc/myname b/etc/myname new file mode 100644 index 0000000000..a0d6cce38a --- /dev/null +++ b/etc/myname @@ -0,0 +1 @@ +myname.my.domain diff --git a/etc/netstart b/etc/netstart index 744e5f8590..36c5df3c9d 100644 --- a/etc/netstart +++ b/etc/netstart @@ -3,27 +3,27 @@ # @(#)netstart 5.9 (Berkeley) 3/30/91 routedflags=-q -timedflags=YES +timedflags=NO rwhod=NO +#kerberos_server=YES +#nfs_server=YES +#name_server=YES +#gated=YES -# myname is my symbolic name +# my-name is my symbolic name # my-netmask is specified in /etc/networks # -hostname=myname.my.domain +hostname=`cat /etc/myname` hostname $hostname -ifconfig imp0 inet $hostname -ifconfig ace0 inet $hostname netmask my-netmask -ifconfig ex0 inet $hostname netmask my-netmask -ifconfig we0 inet $hostname netmask my-netmask -ifconfig ne0 inet $hostname netmask my-netmask - -# for en ethernet interface, load microcode before ifconfig -# /etc/enpload /dev/enp0ram /etc/enpcode > /dev/console 2>&1 -ifconfig en0 inet $hostname netmask my-netmask +# ifconfig we0 inet $hostname netmask 447-ether +# ifconfig ne0 inet $hostname netmask my-netmask +# ifconfig ec0 inet $hostname netmask my-netmask +# ifconfig is0 inet $hostname netmask my-netmask +# ifconfig ed0 inet $hostname netmask my-netmask # set the address for the loopback interface ifconfig lo0 inet localhost # use loopback, not the wire -route add $hostname localhost +# route add $hostname localhost diff --git a/etc/phones b/etc/phones index 941b0edc15..b18806b833 100644 --- a/etc/phones +++ b/etc/phones @@ -2,6 +2,10 @@ # # phones -- remote host phone number data base # see tip(1), phones(5) - +# [tip cannot currently accept comments in this file, +# so this and the above lines must be removed.] system1 9=3156427750 system2 9148841241 +netcom 1(408)241-9760 +netcom 1(415)328-9940 +omen 1(503)621-3746 diff --git a/etc/printcap b/etc/printcap index 8216698a1f..06791f85fc 100644 --- a/etc/printcap +++ b/etc/printcap @@ -1,4 +1,4 @@ # @(#)printcap 5.3 (Berkeley) 6/30/90 -lp|local line printer:\ - :lp=/dev/lp:sd=/var/spool/lpd:lf=/var/log/lpd-errs: +#lp|local line printer:\ +# :lp=/dev/lp:sd=/var/spool/lpd:lf=/var/log/lpd-errs: diff --git a/etc/rc b/etc/rc index de25fd7230..85715719de 100644 --- a/etc/rc +++ b/etc/rc @@ -6,8 +6,6 @@ # and the console is the controlling terminal. stty status '^T' -# yellow characters with blue background -echo -n "" # Set shell to ignore SIGINT (2), but not children; # shell catches SIGQUIT (3) and returns to single user after fsck. @@ -18,7 +16,7 @@ HOME=/; export HOME PATH=/sbin:/bin:/usr/sbin:/usr/bin export PATH -if [ -r /fastboot ] +if [ -e /fastboot ] then echo Fast boot ... skipping disk checks elif [ $1x = autobootx ] @@ -169,6 +167,4 @@ sh /etc/rc.local date -# reset to normal (no colors) -echo -n "" exit 0 diff --git a/etc/rc.local b/etc/rc.local index d76e9f13a1..dbbd909a5b 100644 --- a/etc/rc.local +++ b/etc/rc.local @@ -6,7 +6,7 @@ T=/tmp/_motd rm -f $T -strings /386bsd | grep version: | sed 's/version: /386BSD 0.1./' > $T +uname -a > $T echo "" >> $T sed '1,/^$/d' < /etc/motd >> $T cp $T /etc/motd diff --git a/etc/remote b/etc/remote index 45f3288510..0b5cc592f6 100644 --- a/etc/remote +++ b/etc/remote @@ -17,12 +17,22 @@ # receive operations # tc to continue a capability +# Systems definitions +netcom|Netcom Unix Access:\ + :pn=@:tc=unix1200: +omen|Omen BBS:\ + :pn=@:tc=dos1200: + # UNIX system definitions unix1200|1200 Baud dial-out to another UNIX system:\ :el=^U^C^R^O^D^S^Q:ie=%$:oe=^D:tc=dial1200: unix300|300 Baud dial-out to another UNIX system:\ :el=^U^C^R^O^D^S^Q:ie=%$:oe=^D:tc=dial300: +# DOS system definitions +dos1200|1200 Baud dial-out to a DOS system:\ + :el=^U^C^R^O^D^S^Q:ie=%$:oe=^Z:pa=none:tc=dial1200: + # General dialer definitions used below # # COURIER switch settings: @@ -31,10 +41,10 @@ unix300|300 Baud dial-out to another UNIX system:\ # Rackmount: U U D U D U D D U D # dial2400|2400 Baud Hayes attributes:\ - :dv=/dev/tty19:br#2400:cu=/dev/tty19:at=hayes:du: + :dv=/dev/tty00:br#2400:cu=/dev/tty00:at=hayes:du: dial1200|1200 Baud Hayes attributes:\ - :dv=/dev/tty19:br#1200:cu=/dev/tty19:at=hayes:du: + :dv=/dev/tty00:br#1200:cu=/dev/tty00:at=hayes:du: # Hardwired line -com1c|com1:dv=/dev/com1:br#9600: -com1b:dv=/dev/com1:br#2400: +tty00b|tty0b:dv=/dev/tty00:br#2400 +tty00c|tty0c:dv=/dev/tty00:br#9600 diff --git a/etc/root/dot.cshrc b/etc/root/dot.cshrc index 7e273b0d6e..d6f049569f 100644 --- a/etc/root/dot.cshrc +++ b/etc/root/dot.cshrc @@ -1,9 +1,9 @@ alias mail Mail set history=1000 -set path=(/sbin /usr/sbin /bin /usr/bin /usr/local /usr/hosts /usr/contrib .) +set path=(/sbin /usr/sbin /bin /usr/bin /usr/local/bin .) # directory stuff: cdpath/cd/back -set cdpath=(/sys /usr/src/{bin,sbin,usr.{bin,sbin},pgrm,lib,libexec,share,contrib,local,devel,games,old,}) +set cdpath=(/sys /usr/src/{bin,sbin,usr.{bin,sbin},lib,libexec,share,contrib,etc,games,gnu,include,}) alias cd 'set old=$cwd; chdir \!*' alias h history alias j jobs -l @@ -11,26 +11,15 @@ alias ll ls -lg alias ls ls -g -k alias back 'set back=$old; set old=$cwd; cd $back; unset back; dirs' -# sccs stuff: sd/co/ci/allout/out/unedit -alias sd sccs diffs -alias co sccs get -e -alias ci sccs delget -alias allout "(cd ..; echo */SCCS/p.*|sed s/SCCS\\/p.//g)" -alias out "echo SCCS/p.*|sed s/SCCS\\/p.//g" -alias info sccs info -alias unedit sccs unedit -alias get sccs get -alias prt sccs prt -alias z suspend +alias z suspend alias x exit alias pd pushd alias pd2 pushd +2 alias pd3 pushd +3 alias pd4 pushd +4 -alias df df -k -alias du du -k alias tset 'set noglob histchars=""; eval `\tset -s \!*`; unset noglob histchars' if ($?prompt) then set prompt="`hostname -s`# " endif +setenv BLOCKSIZE K diff --git a/etc/root/dot.login b/etc/root/dot.login index 6406af639b..424dce8653 100644 --- a/etc/root/dot.login +++ b/etc/root/dot.login @@ -1,4 +1,4 @@ tset -Q \?$TERM -stty crt erase ^H +stty crt erase ^\? umask 2 echo "Don't login as root, use su" diff --git a/etc/root/dot.profile b/etc/root/dot.profile index 47effabab9..1479c6fba5 100644 --- a/etc/root/dot.profile +++ b/etc/root/dot.profile @@ -1,4 +1,4 @@ -PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local:/usr/contrib:. +PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:. echo 'erase ^?, kill ^U, intr ^C' stty crt erase  kill  intr  export PATH diff --git a/etc/rpc b/etc/rpc new file mode 100644 index 0000000000..0ce30ecc4e --- /dev/null +++ b/etc/rpc @@ -0,0 +1,34 @@ +# +# rpc 88/08/01 4.0 RPCSRC; from 1.12 88/02/07 SMI +# +portmapper 100000 portmap sunrpc +rstatd 100001 rstat rstat_svc rup perfmeter +rusersd 100002 rusers +nfs 100003 nfsprog +ypserv 100004 ypprog +mountd 100005 mount showmount +ypbind 100007 +walld 100008 rwall shutdown +yppasswdd 100009 yppasswd +etherstatd 100010 etherstat +rquotad 100011 rquotaprog quota rquota +sprayd 100012 spray +3270_mapper 100013 +rje_mapper 100014 +selection_svc 100015 selnsvc +database_svc 100016 +rexd 100017 rex +alis 100018 +sched 100019 +llockmgr 100020 +nlockmgr 100021 +x25.inr 100022 +statmon 100023 +status 100024 +bootparam 100026 +ypupdated 100028 ypupdate +keyserv 100029 keyserver +tfsd 100037 +nsed 100038 +nsemntd 100039 +pcnfsd 150001 pcnfs diff --git a/etc/security b/etc/security index de01e4556c..41a09312f7 100644 --- a/etc/security +++ b/etc/security @@ -1,6 +1,7 @@ #!/bin/sh - # # @(#)security 5.3 (Berkeley) 5/28/91 +# $Id: security,v 1.3 1993/09/06 23:12:04 rgrimes Exp $ # PATH=/sbin:/bin:/usr/bin @@ -10,18 +11,33 @@ echo "Subject: $host security check output" LOG=/var/log TMP=/tmp/_secure.$$ +umask 027 + echo "checking setuid files and devices:" -MP=`mount -t ufs | sed 's;/dev/;&r;' | awk '{ print $1 " " $3 }'` + +# don't have ncheck, but this does the equivalent of the commented out block. +# note that one of the original problem, the possibility of overrunning +# the args to ls, is still here... +# +MP=`mount -t ufs | sed 's;/dev/;&r;' | awk '{ print $3 }'` set $MP -ls -lgT `while test $# -ge 2; do - device=$1 - shift +ls -lgT `while test $# -ge 1; do mount=$1 shift - ncheck -s $device | sed -e "/:$/d" -e "/\/dev\//d" \ - -e "s;[^/]*;$mount;" -e "s;//;/;g" | sort + find $mount -xdev -perm -u+s -or -perm -g+s | sort done` > $TMP +#MP=`mount -t ufs | sed 's;/dev/;&r;' | awk '{ print $1 " " $3 }'` +#set $MP +#ls -lgT `while test $# -ge 2; do +# device=$1 +# shift +# mount=$1 +# shift +# ncheck -s $device | sed -e "/:$/d" -e "/\/dev\//d" \ +# -e "s;[^/]*;$mount;" -e "s;//;/;g" | sort +#done` > $TMP + if cmp $LOG/setuid.today $TMP >/dev/null; then :; else echo "$host setuid/device diffs:" diff $LOG/setuid.today $TMP diff --git a/etc/services b/etc/services index c579451272..cd7509f1bf 100644 --- a/etc/services +++ b/etc/services @@ -44,6 +44,7 @@ auth 113/tcp authentication sftp 115/tcp uucp-path 117/tcp nntp 119/tcp readnews untp # USENET News Transfer Protocol +ntp 123/udp # Network Time Protocol snmp 161/udp snmp-trap 162/udp # diff --git a/etc/ttys b/etc/ttys index f9e309934c..dd243013c4 100644 --- a/etc/ttys +++ b/etc/ttys @@ -4,8 +4,10 @@ # name getty type status comments # console "/usr/libexec/getty Pc" pc3 on secure -com01 "/usr/libexec/getty std.9600" unknown off secure -com02 "/usr/libexec/getty std.9600" unknown off secure +tty00 "/usr/libexec/getty std.9600" unknown off secure +tty01 "/usr/libexec/getty std.9600" unknown off secure +tty02 "/usr/libexec/getty std.9600" unknown off secure +tty03 "/usr/libexec/getty std.9600" unknown off secure ttyp0 none network ttyp1 none network ttyp2 none network diff --git a/etc/weekly b/etc/weekly index 79b46ffcb5..42a32f9932 100644 --- a/etc/weekly +++ b/etc/weekly @@ -16,25 +16,25 @@ echo "Subject: $host weekly run output" # see if /usr/src exists and is local # before looking there for checked-out files -if [ -d /usr/src -a \ - X"`find -f /usr/src ! -fstype local -prune -or -type d -print -prune`" != X ]; -then - echo "looking for checked out files:" - TDIR=/tmp/_checkout$$ - - mkdir $TDIR - for file in `find -f /usr/src ! -fstype local -prune -or \ - -name 'p.*' -print | egrep 'SCCS/p\.'`; do - owner=`awk '{ print $3 }' $file` - echo "$owner $file" - echo $file >> $TDIR/$owner - done | sed -e 's,SCCS/p.,,' - for file in $TDIR/*; do - sed -e 's,SCCS/p.,,' $file | \ - Mail -s 'checked out files' `basename $file` - done - rm -rf $TDIR -fi +#if [ -d /usr/src -a \ +# X"`find -f /usr/src ! -fstype local -prune -or -type d -print -prune`" != X ]; +#then +# echo "looking for checked out files:" +# TDIR=/tmp/_checkout$$ +# +# mkdir $TDIR +# for file in `find -f /usr/src ! -fstype local -prune -or \ +# -name 'p.*' -print | egrep 'SCCS/p\.'`; do +# owner=`awk '{ print $3 }' $file` +# echo "$owner $file" +# echo $file >> $TDIR/$owner +# done | sed -e 's,SCCS/p.,,' +# for file in $TDIR/*; do +# sed -e 's,SCCS/p.,,' $file | \ +# Mail -s 'checked out files' `basename $file` +# done +# rm -rf $TDIR +#fi if [ -f /usr/lib/uucp/clean.weekly ]; then echo "" diff --git a/games/Makefile b/games/Makefile index b09b5c4953..cc475918ef 100644 --- a/games/Makefile +++ b/games/Makefile @@ -11,7 +11,7 @@ #Missing: adventure boggle ching dungeon hunt phantasia quiz warp SUBDIR= arithmetic atc backgammon banner battlestar bcd caesar canfield \ - chess cribbage dm factor fish fortune hack hangman larn \ + cribbage dm factor fish fortune hack hangman larn \ mille monop morse number pom ppt primes rain robots rogue \ sail snake trek wargames worm worms wump diff --git a/games/arithmetic/Makefile b/games/arithmetic/Makefile index 2ff01223cd..8528499ac3 100644 --- a/games/arithmetic/Makefile +++ b/games/arithmetic/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.2 (Berkeley) 5/11/90 PROG= arithmetic -MAN6= arithmetic.0 +MAN6= arithmetic.6 HIDEGAME=hidegame .include diff --git a/games/atc/Makefile b/games/atc/Makefile index 07e88780c4..d0fd7b3462 100644 --- a/games/atc/Makefile +++ b/games/atc/Makefile @@ -4,7 +4,7 @@ PROG= atc CFLAGS+=-DBSD -I${.CURDIR} -I. SRCS= extern.c grammar.c graphics.c input.c lex.c list.c log.c \ main.c tunable.c update.c -MAN6= atc.0 +MAN6= atc.6 DPADD= ${usr/lib/libl.a ${LIBM} ${LIBTERM} ${LIBCURSES} LDADD= -ll -lm -lcurses -ltermcap GAMES= ATC_scores Game_List Killer crossover default easy game_2 diff --git a/games/backgammon/backgammon/Makefile b/games/backgammon/backgammon/Makefile index 4f8856b075..a45f82bc31 100644 --- a/games/backgammon/backgammon/Makefile +++ b/games/backgammon/backgammon/Makefile @@ -4,7 +4,7 @@ PROG= backgammon CFLAGS+=-DV7 -I${.CURDIR}/../common_source SRCS= allow.c board.c check.c extra.c fancy.c init.c main.c move.c \ odds.c one.c save.c subs.c table.c text.c version.c -MAN6= backgammon.0 +MAN6= backgammon.6 DPADD= ${LIBTERM} LDADD= -ltermcap .PATH: ${.CURDIR}/../common_source diff --git a/games/banner/Makefile b/games/banner/Makefile index fbaa768896..6ef4d38257 100644 --- a/games/banner/Makefile +++ b/games/banner/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.4 (Berkeley) 7/25/90 PROG= banner -MAN6= banner.0 +MAN6= banner.6 .include diff --git a/games/battlestar/Makefile b/games/battlestar/Makefile index eed9c73e8c..3b2a59ce07 100644 --- a/games/battlestar/Makefile +++ b/games/battlestar/Makefile @@ -4,7 +4,7 @@ PROG= battlestar SRCS= battlestar.c com1.c com2.c com3.c com4.c com5.c com6.c com7.c \ init.c cypher.c getcom.c parse.c room.c save.c fly.c misc.c \ globals.c dayfile.c nightfile.c dayobjs.c nightobjs.c words.c -MAN6= battlestar.0 +MAN6= battlestar.6 DPADD= ${LIBCURSES} ${LIBTERMCAP} LDADD= -lcurses -ltermlib HIDEGAME=hidegame diff --git a/games/bcd/Makefile b/games/bcd/Makefile index fef64ac823..40e7637cb0 100644 --- a/games/bcd/Makefile +++ b/games/bcd/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= bcd -MAN6= bcd.0 +MAN6= bcd.6 MLINKS= bcd.6 morse.6 bcd.6 ppt.6 HIDEGAME=hidegame diff --git a/games/caesar/Makefile b/games/caesar/Makefile index c35de8f46e..35b0284f84 100644 --- a/games/caesar/Makefile +++ b/games/caesar/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.2 (Berkeley) 5/11/90 PROG= caesar -MAN6= caesar.0 +MAN6= caesar.6 DPADD= ${LIBM} LDADD= -lm MLINKS= caesar.6 rot13.6 diff --git a/games/canfield/canfield/Makefile b/games/canfield/canfield/Makefile index 0ebac9100e..d111d31448 100644 --- a/games/canfield/canfield/Makefile +++ b/games/canfield/canfield/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.7 (Berkeley) 4/8/91 PROG= canfield -MAN6= canfield.0 +MAN6= canfield.6 DPADD= ${LIBCURSES} ${LIBTERM} LDADD= -lcurses -ltermcap HIDEGAME=hidegame diff --git a/games/canfield/canfield/canfield.6 b/games/canfield/canfield/canfield.6 index 64c24d0405..a367666475 100644 --- a/games/canfield/canfield/canfield.6 +++ b/games/canfield/canfield/canfield.6 @@ -108,7 +108,7 @@ played the game since the database was set up. .br /usr/games/cfscores the database printer .br -/usr/games/lib/cfscores the database of scores +/var/games/cfscores the database of scores .SH BUGS It is impossible to cheat. .SH AUTHORS diff --git a/games/cribbage/Makefile b/games/cribbage/Makefile index 6a139c88e7..794621c368 100644 --- a/games/cribbage/Makefile +++ b/games/cribbage/Makefile @@ -4,7 +4,7 @@ PROG= cribbage DPADD= ${LIBCURSES} ${LIBTERM} LDADD= -lcurses -ltermlib SRCS= extern.c crib.c cards.c instr.c io.c score.c support.c -MAN6= cribbage.0 +MAN6= cribbage.6 HIDEGAME=hidegame beforeinstall: diff --git a/games/dm/Makefile b/games/dm/Makefile index d0d552bdbd..47343d47de 100644 --- a/games/dm/Makefile +++ b/games/dm/Makefile @@ -2,8 +2,9 @@ # -DLOG log games PROG= dm -MAN8= dm.0 -MAN5= dm.conf.0 +MAN8= dm.8 +MAN5= dm.conf.5 +BINOWN= games BINGRP= kmem BINMODE=6555 DPADD= ${LIBUTIL} diff --git a/games/factor/Makefile b/games/factor/Makefile index 846831b4cc..9dc5ef2ebd 100644 --- a/games/factor/Makefile +++ b/games/factor/Makefile @@ -3,7 +3,7 @@ PROG= factor SRCS= factor.c pr_tbl.c CFLAGS+=-I${.CURDIR}/../primes -MAN6= factor.0 +MAN6= factor.6 MLINKS+=factor.6 primes.6 .PATH: ${.CURDIR}/../primes diff --git a/games/fish/Makefile b/games/fish/Makefile index b1847b71f3..3c8742ebcd 100644 --- a/games/fish/Makefile +++ b/games/fish/Makefile @@ -1,10 +1,10 @@ # @(#)Makefile 5.5 (Berkeley) 1/18/91 PROG= fish -MAN6= fish.0 +MAN6= fish.6 HIDEGAME=hidegame beforeinstall: - install -c -o bin -g bin -m 444 ${.CURDIR}/fish.instr /usr/share/games + install -c -o bin -g bin -m 444 ${.CURDIR}/fish.instr ${DESTDIR}/usr/share/games .include diff --git a/games/fortune/Makefile b/games/fortune/Makefile index f4b87ff8a8..ed94afe4a9 100644 --- a/games/fortune/Makefile +++ b/games/fortune/Makefile @@ -2,7 +2,7 @@ SUBDIR= fortune -.ifmake (clean) || (cleandir) +.ifmake (clean) || (cleandir) || (obj) SUBDIR+=datfiles .endif diff --git a/games/fortune/datfiles/Makefile b/games/fortune/datfiles/Makefile index 6af73c47ea..3e17087d64 100644 --- a/games/fortune/datfiles/Makefile +++ b/games/fortune/datfiles/Makefile @@ -1,4 +1,11 @@ # @(#)Makefile 5.2 (Berkeley) 5/6/91 +# +# Grungy obj stuff +.if exists(../strfile/obj) +SOBJ=obj +.else +SOBJ=. +.endif DATFILES=fortunes.dat startrek.dat zippy.dat fortunes-o.dat CLEANFILES+=${DATFILES} @@ -10,11 +17,11 @@ install: ${DATFILES} ${DESTDIR}/usr/share/games/fortune fortunes-o.dat: ${.TARGET:R} - ${.CURDIR}/../strfile/obj/strfile -rsx \ + ${.CURDIR}/../strfile/${SOBJ}/strfile -rsx \ ${.CURDIR}/${.TARGET:R} ${.TARGET} fortunes.dat startrek.dat zippy.dat: ${.TARGET:R} - ${.CURDIR}/../strfile/obj/strfile -rs \ + ${.CURDIR}/../strfile/${SOBJ}/strfile -rs \ ${.CURDIR}/${.TARGET:R} ${.TARGET} .include diff --git a/games/fortune/fortune/Makefile b/games/fortune/fortune/Makefile index 6f21d2073c..84319bcc39 100644 --- a/games/fortune/fortune/Makefile +++ b/games/fortune/fortune/Makefile @@ -1,7 +1,7 @@ # %W% (Berkeley) %G% PROG= fortune -MAN6= fortune.0 +MAN6= fortune.6 CFLAGS+=-I${.CURDIR}/../strfile -DNO_REGEX .include "${.CURDIR}/../../Makefile.inc" diff --git a/games/fortune/strfile/Makefile b/games/fortune/strfile/Makefile index 96ee778229..b78605966b 100644 --- a/games/fortune/strfile/Makefile +++ b/games/fortune/strfile/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.1 (Berkeley) 4/27/91 PROG= strfile -NOMAN= noman +MAN8= strfile.8 .include diff --git a/games/hack/Makefile b/games/hack/Makefile index 3ef9f739e1..0432786c3a 100644 --- a/games/hack/Makefile +++ b/games/hack/Makefile @@ -12,13 +12,16 @@ SRCS= alloc.c hack.Decl.c hack.apply.c hack.bones.c hack.c hack.cmd.c \ hack.timeout.c hack.topl.c hack.track.c hack.trap.c hack.tty.c \ hack.u_init.c hack.unix.c hack.vault.c hack.version.c hack.wield.c \ hack.wizard.c hack.worm.c hack.worn.c hack.zap.c rnd.c -MAN6= hack.0 +CFLAGS+= -I. +MAN6= hack.6 +DPSRCS= hack.onames.h DPADD= ${LIBTERM} LDADD= -ltermcap HIDEGAME=hidegame +CLEANFILES= hack.onames.h makedefs hack.onames.h: makedefs def.objects.h - makedefs ${.CURDIR}/def.objects.h > hack.onames.h + ./makedefs ${.CURDIR}/def.objects.h > hack.onames.h makedefs: makedefs.c ${CC} ${CFLAGS} -o ${.TARGET} ${.CURDIR}/${.PREFIX}.c diff --git a/games/hack/hack.onames.h b/games/hack/hack.onames.h deleted file mode 100644 index c0fe81eaa6..0000000000 --- a/games/hack/hack.onames.h +++ /dev/null @@ -1,227 +0,0 @@ -#define STRANGE_OBJECT 0 -#define AMULET_OF_YENDOR 1 -#define FOOD_RATION 2 -#define TRIPE_RATION 3 -#define PANCAKE 4 -#define DEAD_LIZARD 5 -#define FORTUNE_COOKIE 6 -#define CARROT 7 -#define TIN 8 -#define ORANGE 9 -#define APPLE 10 -#define PEAR 11 -#define MELON 12 -#define BANANA 13 -#define CANDY_BAR 14 -#define EGG 15 -#define CLOVE_OF_GARLIC 16 -#define LUMP_OF_ROYAL_JELLY 17 -#define DEAD_HUMAN 18 -#define DEAD_GIANT_ANT 19 -#define DEAD_GIANT_BAT 20 -#define DEAD_CENTAUR 21 -#define DEAD_DRAGON 22 -#define DEAD_FLOATING_EYE 23 -#define DEAD_FREEZING_SPHERE 24 -#define DEAD_GNOME 25 -#define DEAD_HOBGOBLIN 26 -#define DEAD_STALKER 27 -#define DEAD_JACKAL 28 -#define DEAD_KOBOLD 29 -#define DEAD_LEPRECHAUN 30 -#define DEAD_MIMIC 31 -#define DEAD_NYMPH 32 -#define DEAD_ORC 33 -#define DEAD_PURPLE_WORM 34 -#define DEAD_QUASIT 35 -#define DEAD_RUST_MONSTER 36 -#define DEAD_SNAKE 37 -#define DEAD_TROLL 38 -#define DEAD_UMBER_HULK 39 -#define DEAD_VAMPIRE 40 -#define DEAD_WRAITH 41 -#define DEAD_XORN 42 -#define DEAD_YETI 43 -#define DEAD_ZOMBIE 44 -#define DEAD_ACID_BLOB 45 -#define DEAD_GIANT_BEETLE 46 -#define DEAD_COCKATRICE 47 -#define DEAD_DOG 48 -#define DEAD_ETTIN 49 -#define DEAD_FOG_CLOUD 50 -#define DEAD_GELATINOUS_CUBE 51 -#define DEAD_HOMUNCULUS 52 -#define DEAD_IMP 53 -#define DEAD_JAGUAR 54 -#define DEAD_KILLER_BEE 55 -#define DEAD_LEOCROTTA 56 -#define DEAD_MINOTAUR 57 -#define DEAD_NURSE 58 -#define DEAD_OWLBEAR 59 -#define DEAD_PIERCER 60 -#define DEAD_QUIVERING_BLOB 61 -#define DEAD_GIANT_RAT 62 -#define DEAD_GIANT_SCORPION 63 -#define DEAD_TENGU 64 -#define DEAD_UNICORN 65 -#define DEAD_VIOLET_FUNGI 66 -#define DEAD_LONG_WORM 67 -#define DEAD_XAN 68 -#define DEAD_YELLOW_LIGHT 69 -#define DEAD_ZRUTY 70 -#define ARROW 71 -#define SLING_BULLET 72 -#define CROSSBOW_BOLT 73 -#define DART 74 -#define ROCK 75 -#define BOOMERANG 76 -#define MACE 77 -#define AXE 78 -#define FLAIL 79 -#define LONG_SWORD 80 -#define TWO_HANDED_SWORD 81 -#define DAGGER 82 -#define WORM_TOOTH 83 -#define CRYSKNIFE 84 -#define SPEAR 85 -#define BOW 86 -#define SLING 87 -#define CROSSBOW 88 -#define WHISTLE 89 -#define MAGIC_WHISTLE 90 -#define EXPENSIVE_CAMERA 91 -#define ICE_BOX 92 -#define PICK_AXE 93 -#define CAN_OPENER 94 -#define HEAVY_IRON_BALL 95 -#define IRON_CHAIN 96 -#define ENORMOUS_ROCK 97 -#define HELMET 98 -#define PLATE_MAIL 99 -#define SPLINT_MAIL 100 -#define BANDED_MAIL 101 -#define CHAIN_MAIL 102 -#define SCALE_MAIL 103 -#define RING_MAIL 104 -#define STUDDED_LEATHER_ARMOR 105 -#define LEATHER_ARMOR 106 -#define ELVEN_CLOAK 107 -#define SHIELD 108 -#define PAIR_OF_GLOVES 109 -#define POT_RESTORE_STRENGTH 110 -#define POT_BOOZE 111 -#define POT_INVISIBILITY 112 -#define POT_FRUIT_JUICE 113 -#define POT_HEALING 114 -#define POT_PARALYSIS 115 -#define POT_MONSTER_DETECTION 116 -#define POT_OBJECT_DETECTION 117 -#define POT_SICKNESS 118 -#define POT_CONFUSION 119 -#define POT_GAIN_STRENGTH 120 -#define POT_SPEED 121 -#define POT_BLINDNESS 122 -#define POT_GAIN_LEVEL 123 -#define POT_EXTRA_HEALING 124 -#define POT_LEVITATION 125 -#define SCR_MAIL 130 -#define SCR_ENCHANT_ARMOR 131 -#define SCR_DESTROY_ARMOR 132 -#define SCR_CONFUSE_MONSTER 133 -#define SCR_SCARE_MONSTER 134 -#define SCR_BLANK_PAPER 135 -#define SCR_REMOVE_CURSE 136 -#define SCR_ENCHANT_WEAPON 137 -#define SCR_DAMAGE_WEAPON 138 -#define SCR_CREATE_MONSTER 139 -#define SCR_TAMING 140 -#define SCR_GENOCIDE 141 -#define SCR_LIGHT 142 -#define SCR_TELEPORTATION 143 -#define SCR_GOLD_DETECTION 144 -#define SCR_FOOD_DETECTION 145 -#define SCR_IDENTIFY 146 -#define SCR_MAGIC_MAPPING 147 -#define SCR_AMNESIA 148 -#define SCR_FIRE 149 -#define SCR_PUNISHMENT 150 -#define WAN_LIGHT 155 -#define WAN_SECRET_DOOR_DETECTION 156 -#define WAN_CREATE_MONSTER 157 -#define WAN_WISHING 158 -#define WAN_STRIKING 159 -#define WAN_SLOW_MONSTER 160 -#define WAN_SPEED_MONSTER 161 -#define WAN_UNDEAD_TURNING 162 -#define WAN_POLYMORPH 163 -#define WAN_CANCELLATION 164 -#define WAN_TELEPORTATION 165 -#define WAN_MAKE_INVISIBLE 166 -#define WAN_DIGGING 167 -#define WAN_MAGIC_MISSILE 168 -#define WAN_FIRE 169 -#define WAN_SLEEP 170 -#define WAN_COLD 171 -#define WAN_DEATH 172 -#define Adornment u.uprops[0].p_flgs -#define RIN_ADORNMENT 176 -#define Teleportation u.uprops[1].p_flgs -#define RIN_TELEPORTATION 177 -#define Regeneration u.uprops[2].p_flgs -#define RIN_REGENERATION 178 -#define Searching u.uprops[3].p_flgs -#define RIN_SEARCHING 179 -#define See_invisible u.uprops[4].p_flgs -#define RIN_SEE_INVISIBLE 180 -#define Stealth u.uprops[5].p_flgs -#define RIN_STEALTH 181 -#define Levitation u.uprops[6].p_flgs -#define RIN_LEVITATION 182 -#define Poison_resistance u.uprops[7].p_flgs -#define RIN_POISON_RESISTANCE 183 -#define Aggravate_monster u.uprops[8].p_flgs -#define RIN_AGGRAVATE_MONSTER 184 -#define Hunger u.uprops[9].p_flgs -#define RIN_HUNGER 185 -#define Fire_resistance u.uprops[10].p_flgs -#define RIN_FIRE_RESISTANCE 186 -#define Cold_resistance u.uprops[11].p_flgs -#define RIN_COLD_RESISTANCE 187 -#define Protection_from_shape_changers u.uprops[12].p_flgs -#define RIN_PROTECTION_FROM_SHAPE_CHANGERS 188 -#define Conflict u.uprops[13].p_flgs -#define RIN_CONFLICT 189 -#define Gain_strength u.uprops[14].p_flgs -#define RIN_GAIN_STRENGTH 190 -#define Increase_damage u.uprops[15].p_flgs -#define RIN_INCREASE_DAMAGE 191 -#define Protection u.uprops[16].p_flgs -#define RIN_PROTECTION 192 -#define Warning u.uprops[17].p_flgs -#define RIN_WARNING 193 -#define Teleport_control u.uprops[18].p_flgs -#define RIN_TELEPORT_CONTROL 194 -#define DIAMOND 197 -#define RUBY 198 -#define SAPPHIRE 199 -#define EMERALD 200 -#define TURQUOISE 201 -#define AQUAMARINE 202 -#define TOURMALINE 203 -#define TOPAZ 204 -#define OPAL 205 -#define GARNET 206 -#define AMETHYST 207 -#define AGATE 208 -#define ONYX 209 -#define JASPER 210 -#define JADE 211 -/* #define WORTHLESS_PIECE_OF_BLUE_GLASS 212 */ -/* #define WORTHLESS_PIECE_OF_RED_GLASS 213 */ -/* #define WORTHLESS_PIECE_OF_YELLOW_GLASS 214 */ -/* #define WORTHLESS_PIECE_OF_GREEN_GLASS 215 */ - -#define CORPSE DEAD_HUMAN -#define LAST_GEM (JADE+1) -#define LAST_RING 19 -#define NROFOBJECTS 215 diff --git a/games/hangman/Makefile b/games/hangman/Makefile index 504cdde480..bbcd0d40ab 100644 --- a/games/hangman/Makefile +++ b/games/hangman/Makefile @@ -3,7 +3,7 @@ PROG= hangman SRCS= endgame.c extern.c getguess.c getword.c main.c playgame.c \ prdata.c prman.c prword.c setup.c -MAN6= hangman.0 +MAN6= hangman.6 DPADD= ${LIBCURSES} ${LIBTERM} LDADD= -lcurses -ltermlib HIDEGAME=hidegame diff --git a/games/larn/Makefile b/games/larn/Makefile index d0c1f21a42..1f2d5a4946 100644 --- a/games/larn/Makefile +++ b/games/larn/Makefile @@ -52,8 +52,8 @@ # Turn off logging. PROG= larn -MAN6= larn.0 -CFLAGS+=-DBSD -DVER=12 -DSUBVER=0 -DNONAP +MAN6= larn.6 +CFLAGS+=-DBSD -DVER=12 -DSUBVER=0 -DNONAP -fwritable-strings SRCS= main.c object.c create.c tok.c display.c global.c data.c io.c \ monster.c store.c diag.c help.c config.c nap.c bill.c scores.c \ signal.c moreobj.c movem.c regen.c fortune.c savelev.c diff --git a/games/mille/Makefile b/games/mille/Makefile index 2230f71aa4..3ba6816f59 100644 --- a/games/mille/Makefile +++ b/games/mille/Makefile @@ -5,7 +5,7 @@ SRCS= comp.c end.c extern.c init.c mille.c misc.c move.c print.c \ roll.c save.c types.c varpush.c DPADD= ${LIBCURSES} ${LIBTERM} LDADD= -lcurses -ltermlib -MAN6= mille.0 +MAN6= mille.6 HIDEGAME=hidegame .include diff --git a/games/monop/Makefile b/games/monop/Makefile index 5edeba2221..1b682754a0 100644 --- a/games/monop/Makefile +++ b/games/monop/Makefile @@ -3,7 +3,7 @@ PROG= monop SRCS= monop.c cards.c execute.c getinp.c houses.c jail.c misc.c morg.c \ print.c prop.c rent.c roll.c spec.c trade.c -MAN6= monop.0 +MAN6= monop.6 HIDEGAME=hidegame CLEANFILES+=initdeck cards.pck @@ -16,7 +16,7 @@ initdeck: initdeck.c ${CC} ${CFLAGS} -o ${.TARGET} ${.CURDIR}/initdeck.c beforeinstall: - install -o ${BINOWN} -g ${BINGRP} -m 444 cards.pck \ + install ${COPY} -o ${BINOWN} -g ${BINGRP} -m 444 cards.pck \ ${DESTDIR}/usr/share/games .include diff --git a/games/monop/monop.6 b/games/monop/monop.6 index 6bc7ba070b..52969b0fd9 100644 --- a/games/monop/monop.6 +++ b/games/monop/monop.6 @@ -186,6 +186,6 @@ it is the same as typing .SH AUTHOR Ken Arnold .SH FILES -/usr/games/lib/cards.pck Chance and Community Chest cards +/usr/share/games/cards.pck Chance and Community Chest cards .SH BUGS No command can be given an argument instead of a response to a query. diff --git a/games/monop/roll.c b/games/monop/roll.c index 9de116f492..c5739f8dac 100644 --- a/games/monop/roll.c +++ b/games/monop/roll.c @@ -41,7 +41,7 @@ static char sccsid[] = "@(#)roll.c 5.5 (Berkeley) 6/1/90"; # define reg register -# if !defined(vax) && !defined(tahoe) +# if !defined(vax) && !defined(tahoe) && !defined(i386) # define MAXRAND 32767L roll(ndie, nsides) diff --git a/games/number/Makefile b/games/number/Makefile index 9ef234702d..3ac51f035b 100644 --- a/games/number/Makefile +++ b/games/number/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.2 (Berkeley) 5/11/90 PROG= number -MAN6= number.0 +MAN6= number.6 .include diff --git a/games/pom/Makefile b/games/pom/Makefile index bfae4b4fbb..a010fc8982 100644 --- a/games/pom/Makefile +++ b/games/pom/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.1 (Berkeley) 5/11/90 PROG= pom -MAN6= pom.0 +MAN6= pom.6 DPADD= ${LIBM} LDADD= -lm diff --git a/games/rain/Makefile b/games/rain/Makefile index 9f923ce9a5..fcd99b564c 100644 --- a/games/rain/Makefile +++ b/games/rain/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= rain -MAN6= rain.0 +MAN6= rain.6 DPADD= ${LIBTERM} LDADD= -ltermcap diff --git a/games/robots/Makefile b/games/robots/Makefile index 0839805fa0..581b839802 100644 --- a/games/robots/Makefile +++ b/games/robots/Makefile @@ -4,7 +4,7 @@ PROG= robots CFLAGS+=-DMAX_PER_UID=5 SRCS= extern.c init_field.c main.c make_level.c move.c move_robs.c \ play_level.c query.c rnd_pos.c score.c flush_in.c -MAN6= robots.0 +MAN6= robots.6 DPADD= ${LIBCURSES} ${LIBTERM} LDADD= -lcurses -ltermlib HIDEGAME=hidegame diff --git a/games/robots/robots.6 b/games/robots/robots.6 index 0f76c952ce..ca3a322a6c 100644 --- a/games/robots/robots.6 +++ b/games/robots/robots.6 @@ -132,8 +132,8 @@ Advance into the higher levels directly, skipping the lower, easier levels. Ken Arnold .SH FILES .ta -.ta \w'/usr/games/lib/robots_roll\ \ \ \ 'u -/usr/games/lib/robots_roll the score file +.ta \w'/var/games/robots_roll\ \ \ \ 'u +/var/games/robots_roll the score file .SH BUGS Bugs? You diff --git a/games/rogue/Makefile b/games/rogue/Makefile index 312a611555..421fb1db95 100644 --- a/games/rogue/Makefile +++ b/games/rogue/Makefile @@ -8,6 +8,6 @@ SRCS= curses.c hit.c init.c inventory.c level.c machdep.c main.c \ DPADD= ${LIBCURSES} ${LIBTERM} LDADD= -lcurses -ltermlib HIDEGAME=hidegame -MAN6= rogue.0 +MAN6= rogue.6 .include diff --git a/games/rogue/rogue.6 b/games/rogue/rogue.6 index bad531eb00..e22567a98e 100644 --- a/games/rogue/rogue.6 +++ b/games/rogue/rogue.6 @@ -94,8 +94,8 @@ Kenneth C. R. C. Arnold, Glenn Wichman .SH FILES .DT -.ta \w'/usr/games/lib/rogue_roll\ \ \ 'u -/usr/games/lib/rogue_roll Score file +.ta \w'/var/games/rogue.scores\ \ \ 'u +/var/games/rogue.scores Score file .br \fB~\fP/rogue.save Default save file .SH SEE ALSO diff --git a/games/rogue/score.c b/games/rogue/score.c index 6b1a06fd43..f4fd5d4edb 100644 --- a/games/rogue/score.c +++ b/games/rogue/score.c @@ -206,7 +206,7 @@ short other; md_lock(1); - if ((fp = fopen(_PATH_SCOREFILE, "a+")) == NULL) { + if ((fp = fopen(_PATH_SCOREFILE, "r+")) == NULL) { message("cannot read/write/create score file", 0); sf_error(); } diff --git a/games/sail/Makefile b/games/sail/Makefile index 28a37f0d22..be1e2fbddd 100644 --- a/games/sail/Makefile +++ b/games/sail/Makefile @@ -4,7 +4,7 @@ PROG= sail SRCS= main.c pl_main.c pl_1.c pl_2.c pl_3.c pl_4.c pl_5.c pl_6.c pl_7.c \ dr_main.c dr_1.c dr_2.c dr_3.c dr_4.c dr_5.c lo_main.c \ assorted.c game.c globals.c misc.c parties.c sync.c version.c -MAN6= sail.0 +MAN6= sail.6 DPADD= ${LIBCURSES} ${LIBTERM} LDADD= -lcurses -ltermlib HIDEGAME=hidegame diff --git a/games/sail/player.h b/games/sail/player.h index c28cdbc156..e875ab849e 100644 --- a/games/sail/player.h +++ b/games/sail/player.h @@ -91,7 +91,7 @@ #define SLOT_R (SLOT_L+SLOT_X-1) #ifdef SIGTSTP -#define SCREENTEST() (initscr() != ERR && signal(SIGTSTP, SIG_DFL) != BADSIG && STAT_R < COLS && SCROLL_Y > 0) +#define SCREENTEST() (initscr() != ERR && signal(SIGTSTP, SIG_DFL) != SIG_ERR && STAT_R < COLS && SCROLL_Y > 0) #else #define SCREENTEST() (initscr() != ERR && STAT_R < COLS && SCROLL_Y > 0) #endif diff --git a/games/snake/snake/Makefile b/games/snake/snake/Makefile index 1c78a65e2c..3a3341a547 100644 --- a/games/snake/snake/Makefile +++ b/games/snake/snake/Makefile @@ -2,7 +2,7 @@ PROG= snake SRCS= snake.c move.c -MAN6= snake.0 +MAN6= snake.6 DPADD= ${LIBM} ${LIBTERM} LDADD= -lm -ltermcap HIDEGAME=hidegame diff --git a/games/snake/snake/snake.6 b/games/snake/snake/snake.6 index 5251f1a928..11ac9341cb 100644 --- a/games/snake/snake/snake.6 +++ b/games/snake/snake/snake.6 @@ -96,12 +96,13 @@ As in pinball, matching the last digit of your score to the number which appears after the game is worth a bonus. .PP To see who wastes time playing snake, run -.I snscore . +.I snscore +\&. .SH FILES .nf -.ta \w'/usr/games/lib/snakerawscores 'u -/usr/games/lib/snakerawscores database of personal bests -/usr/games/lib/snake.log log of games played +.ta \w'/var/games/snakerawscores 'u +/var/games/snakerawscores database of personal bests +/var/games/snake.log log of games played .DT .fi .SH BUGS diff --git a/games/snake/snscore/snscore.c b/games/snake/snscore/snscore.c index 2dc091787d..e25e48eebe 100644 --- a/games/snake/snscore/snscore.c +++ b/games/snake/snscore/snscore.c @@ -61,7 +61,7 @@ main() char buf[80], cp; short uid, score; FILE *fd; - int noplayers; + int noplayers=0; int i, j, notsorted; short whoallbest, allbest; char *q; diff --git a/games/trek/Makefile b/games/trek/Makefile index 3460ec7ee2..42a9e37028 100644 --- a/games/trek/Makefile +++ b/games/trek/Makefile @@ -9,7 +9,7 @@ SRCS= abandon.c attack.c autover.c capture.c check_out.c checkcond.c \ ranf.c rest.c schedule.c score.c setup.c setwarp.c \ shield.c snova.c srscan.c systemname.c torped.c utility.c \ visual.c warp.c win.c cgetc.c -MAN6= trek.0 +MAN6= trek.6 DPADD= ${LIBM} LDADD= -lm HIDEGAME=hidegame diff --git a/games/worm/Makefile b/games/worm/Makefile index 8e93b9fb59..0b44839e9a 100644 --- a/games/worm/Makefile +++ b/games/worm/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= worm -MAN6= worm.0 +MAN6= worm.6 DPADD= ${LIBCURSES} ${LIBTERM} LDADD= -lcurses -ltermcap HIDEGAME=hidegame diff --git a/games/worms/Makefile b/games/worms/Makefile index a82195936b..f73b582076 100644 --- a/games/worms/Makefile +++ b/games/worms/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= worms -MAN6= worms.0 +MAN6= worms.6 DPADD= ${LIBCURSES} ${LIBTERM} LDADD= -lcurses -ltermcap diff --git a/games/wump/Makefile b/games/wump/Makefile index ecb491340a..474ad8f849 100644 --- a/games/wump/Makefile +++ b/games/wump/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.4 (Berkeley) 5/11/90 PROG= wump -MAN6= wump.0 +MAN6= wump.6 HIDEGAME=hidegame beforeinstall: diff --git a/gnu/COPYING.LIB b/gnu/COPYING.LIB new file mode 100644 index 0000000000..eb685a5ec9 --- /dev/null +++ b/gnu/COPYING.LIB @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/gnu/Makefile b/gnu/Makefile index 7da2c32376..490e05ae4f 100644 --- a/gnu/Makefile +++ b/gnu/Makefile @@ -1,5 +1,6 @@ # @(#)Makefile 5.33.1.1 (Berkeley) 5/6/91 -SUBDIR= gawk groff tar +SUBDIR= bc chess cpio cvs dc diff diff3 gas gawk gcc2 gdb grep groff gzip \ + ld libg++ libmalloc libregex man patch pr rcs sdiff sort tar uucp .include diff --git a/gnu/Makefile.inc b/gnu/Makefile.inc new file mode 100644 index 0000000000..8eb90e8f16 --- /dev/null +++ b/gnu/Makefile.inc @@ -0,0 +1,4 @@ +# @(#)Makefile.inc 5.1 (Berkeley) 5/11/90 + +BINDIR?= /usr/bin +LIBDIR?= /usr/lib diff --git a/gnu/games/chess/Makefile b/gnu/games/chess/Makefile index d5fa6f48f5..a281d35fb2 100644 --- a/gnu/games/chess/Makefile +++ b/gnu/games/chess/Makefile @@ -3,10 +3,17 @@ PROG= chess SRCS= gnuchess.c uxdsp.c move.c CFLAGS+=-DNEWMOVE=12 -MAN6= chess.0 +MAN6= chess.6 DPADD= ${LIBCURSES} ${LIBTERM} LDADD= -lcurses -ltermlib HIDEGAME=hidegame +BINOWN?= games +.if defined(HIDEGAME) +BINDIR?= /usr/games/hide +BINMODE?= 4700 +.else +BINDIR?= /usr/games +.endif beforeinstall: install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/gnuchess.book \ diff --git a/gnu/games/chess/Xchess/xchess b/gnu/games/chess/Xchess/xchess deleted file mode 100644 index aa455e8302969962c02edb83da0d3e831d0e42c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171979 zcmcG%3t*JR)$qT!kOcx{cY`JxF-lZaR2HeCqN1X;R_hbIaI*+_;pV+s@okIRs?}DF zs8njvZmgACR?%X;fz1*TMS@Ri7EyV!pr}#Ni5ek7lKubA%(I(K0=D1xe*aqX%;n6P zGiT16IdkT+`CgA_{RFS)?ztXMb-BlLU71J!Z^-XF9y>sAKyy*AuRK5jd&PlXPtpqeqvw(Qb~gDjle}L|pgQ<&bs)Ji2*?$JMNPXJgogtCWET3` zJwYF@p~nLhZS|$pEm`Q(40P`X6%kY)ka=k~v)p9n+unFvw1%u7XR~^ntUk6^yrC$0F727`6CQZgj5p~oOWijGrW|9~a#X7%#akl# zB)ryKV8#HmW|XC_0w_AfXOB|TBs7Rxo~AleYPzYVE7y+k3D-7{1G?FpQoqYWpRgxr zuZDh3pjQJuJ`3Hqvwe3Jvf{Dc4zJw$(=!N?`-a~ow@hV zCifh2zXHs(=^>qq+)Vev+JrRvaBotb3qY7&QtD#F(4&^0MFA1nzrNt{B-MMQhDRjh zZINThyy<0+XRXhwsHv!^t*|O0``ao=`FM-10({kOpzU~z+Q~PmzLc^>DRnhjAiV!u z-8=7+3aS8O&QTkPIO;V?@~Nsx9?6;LlZt1M7TM2HS4o~%J%1{RR=Bt0C9?c%LI1iy z^mEj!2I7lhAQaFUS86n`6bhPy1gI&g4&WQ^U9Z+$>h*Xdg-KN)8E+@m3KGI=srK2^ zwWPErt;ma-0W(S&t#LS0f05VY%G*GH^fztE>cGID$b#UguMT(z7TDE6%Z>$92cV?* ziu}>Pb)?i?0HoAS`tQH$Xoz2p7)6Vv5nudRk9UZ!ETqMxTzZ~n(EYp7BGLnJNNa?k zXoQA%>zrQkHmjyOV9oKzTj>MK_QnF=1mmrrY>cE5s_5u=Qd&1dZ z06#S-gC!q`Hlp4{Ls z=pIPq2|-ex$i+P=>~ zBq~rM0p*=2vbE|0M5o;Gc7e6K!zL$qJR9S!_3_qV$$c5-+zuh4$=|j-{*j(Pc|+cj zGE7tIB?Ks?9(qg5O7KJm=?)=Z80qm5fO8&SBH)WoBNnvg^okX0ksw0PR26BlptH3= zS`~A8Rh3mBG8MK(7d=rrwq|UF9Sc@v^6jcpoz?4!vax^xQvrsti1IYSkc5G&vIwe1=)}C3krZF7T*rN}QPJCVVIks_Ol_MpM2Ku=aU>ctL})4Q zLd0v{(1|&qRi!QsNOZP*Rgp_Wp<8!`2HitQV_tZWkl~NEwwvNLL01lfzC?j9G)a&F z=M7f~+#$(WQMj)Q8SsThne3wc@NhTL;|m=oiMfE_iDUtLXX}yC!gwj1Z1;}0IF;BV zX$NC~4y(D>V>vO+KPPrcPSQ|kWngzNwQdKX=sEu77I1T`CHDZQ5{t&emywV~sr$)^ zl?s}C^*aF^jH#)XrU!BX zg;>AZ=t4NL89g&!%4Xi!Gjk!CsM+Xao;Ag&T4&3sOZ24Y876aaPMQ$lKmfJP5QJ8| zS?AWqo41@z8H>p|-kMW@S>%;tx*>71#{jQ`iFoUl(M0Cv5~gd)bVFN9I&CypyrtM5 zosdQ_R+mmjVwf9x$D3s)s2LC|Z-}?l$6Mmf#j_*@S}Nqv*u;lI zuFesVilXT*boo}Ht7LpG=(z$a$$*wu6pfNR%idyK9}|@+II;s#CHg3!I}aHkqdyl0PYdz!Tf0?DK+VKD?NN zts6N;Fux=vG_8ezqo>t{$!+}QtFq)h0Wud4V%yBtJ!>(1T>r9U}?bOK@Y{>0v`QxxA5gJf=L|+ z7(5MvEU7+cN-|!lj=2r(tW-{pfn%f03@aUL0pJgrwlQx z`D$Wbl7J@Wq#R;a10J2Ci4o~^)PsOQ%&$O}R6oxWQ?D~`GMSUfWL5GELohl)mwipL zH5I2z9>YIb^I{IGwf&cPQ92GNpSq5TGCGKc>}DZl8ZtkNB7KlF7n_kcj%DS3Sx)$b%Shlr#8z+ZI8&Bq~u>IxHS972wT>=VtI9DfRtOpe=X zEp;|IdRlCfe0#66cd-2*)D_wFw&-yVnqz0x^3-Q zv&_)WQ1)J@7M)Lh*Qo{aaX{dJ$pNQBf!+O7(JWa7k#1P>)|KiA`Ak(2`OHNM&w0Y*xdtM}UOv{k2I9s}8EZ{b1(H3>s%vgH zg?0Q&)z1{(@iVvZ)~ejXEpA~Y*%aR1&f+blMp7tM*VfL6%G-uiOVF#99x5^;5k~;2 zDcA5378&ES7mfJ3fL4r5JJmrma*F*jFh^2NB&@AlJ?b9F6Qvj`WPG{faIU1#6=qak zM=U_^HmK*mfHA6?ziJVG8q{aZ+>C(F`D82ku05?Stl(tr&p2x+NNLPxXFaXWD zww6+sTFHmme~BdfqcxsdwUiVw#*lldDc+_QkRl3uBypHN&L<78Peq??h#T2_jG}d(~+1 zX31j0xf|4a=Dbw`l-b@@)7|LAdg$U_YAnF^;CNF*e6t?W5yY<>AtCq$wWUXr~l;s%6u%@ds<+G|qgRC88RlGJz zdtB#uD-76Cl7FQ-T0VGO$6jYW5nNkY*x!S7Z>=q$Ktb061*Sm1oC1YCINC$c_fj4R z@6J&h!$dfG$-*Dx=(8@^)LgLthNJg>@?UUtIzZhyD!gV6{)6$G7EIo|nBl}l%o^2` z(5UUszezxJ9kz{E-Brfuu;@>15||bDj*sDlmfeC#QCHT@Ko~>eu4xG99@zOTNc1S% zqhsVDsp1FCl@mAXN&ja1|IF*l5sy5!DON(LD z#V*Mf?M=~SbwOmvf77VV+w_D5Vs@{fpYg3?r=T}}rkzTxLe+U<`<*K4@~4DCMeWz^t?(gQ+X_TbLT3|VT4d6Y^%hgzSixGGRHtJWh8+aoT3dMm^~TELZEmPs zIt}-hSinWKcwMbpFWp)jZ!@7bHZw{>xXXjZeeoKU^wOT${mj@+sU5iGQ!4yVths>t zeYu`s8%~DQ!*K>P*D3W8`Unqvn|Ld6yr~-~5z*_lw+TgmhIip_6bK*b7I-zc0Hoo? z2qajT>mtjf$o^?(7@c(KkF;j~>J=u1cw4NFsJ8ibr)EqFRFr(w`?|3V0o#nt`pqC z@-nwndTW$=J}yaKRh~+EK?+G0LPgBDPt_2}rND;My@K9kq|i zOot`2H<56$t)_xSwWUzYY6pMS*Zc|XYpc)r()QEIj+NW7a3Xda(??UXk95PlAhm7+L83y&~ zpeE!fKSk&wOXd+>>4`@QrFT#!GRPV3q(5xb03Yy?=sPC5o{zQ%gOkM=l1;87JzIQ( zeFw716-cR9X*%2O%c%xx$N6qh^qy;$MJP`#3G!gr1+&HYP@vBZBhXpaX%SJ zbq>0XIySWF21sWYOC7R~lSp4(#}%dywnM#`t;=#UEx+t!mTyCFJ?5_bbm>Pjs{o z6&wATuOYrZ5x^+=&6Kz;GqoKnjc>8B6SvtI4ox;@!*M9hzpyh-R=P#n) zd;e<$jqhm$M`jWDL69hrIx!N>4!n8^pbFyIU3ex!4UlO1m|L~gg^+vD2|Ju5h$ zAfTcj&kZtEelE1xd6{+v;|(0<*n+-5gUA+pzB|Rlx3gRkB#t`K(D-}wxj}j5@)3R7fz#`0*diyg!lW#R}KmlKltzI{=pf6hO=|QmXKM;J6scIuL zi6Qvu@Ap8kKfa~m2k0GRkj%>>nF*2%z5Q%eZYX4%ysxeHHIV`zvtfjJJE{JB5}MW@ zeY#!G*MB5N)}i7jR-eC1OOvtk@Zq*V{Ao~c@z+*s_$xF0D!!qg*y?${ z@wQM13e*yT&|+DRs@E(m4}W5-dAdxj9NXe9f`WIho1|_c&=4!<%vO}KlMgW$9jI5j zj+#go;_>`WsrS50fiVOEjGm<0FH_>CQiheD%71YXA&-+jP>TB0oZk!F=fh~SI0n-} zlRTAVMy*$ka;^Uc^|NQh7CW22>Z6TPpTl3Zi9ZeMO6)XBt98Uc+8YNVgIK*{qPy_1 zIqGSNc-3r8*ez1wky7Ll1G7NQsm)U{aEMpM@vg<_vF7Ai zR-(k3Ya$#q!#U=r=2>%fc?2xCT^VekfY`q3d?D6Ed}{0*&hU+qV*ds9Jz)i9zG{0X zbN-}YjnwQlsIIPuLn2wMI%PKr#+&qJ#A((`2xN4Mx0iTl*Zn_P_-n2fsBtEwx|f{@aNdc0Lq>SBqCUP+%TsPomCe3EM4 zBXp;&p*HVdEUlsAOReu9TdjegdzI$6keZ@!G^9kP^9PgT3TXO&J z+A}{FA@jdPb$fm-sPolLe9Sy1y@0W-8ySYqxwc?#d5Ny;MuT3_cy9>tiP<#oy@a%23%4D`anpb(9@b}*8tCC z&ni!)=OWK}p6DUz1ZSKfR(ssgx%E%cqy0IMmKcCX7QwMfJ{EVrKf0?!xB$r0fP|ah zPUEXU}HzZCjm-C`je*N=L&pSkfdA;S4k!RrjqF2J6K3`FL0g|*qU45 zBuRNYdUHntH*7cb2TI;Qa-sW4N>%hHEN+AmPR#9JxnsaKxs z0s@2`!jLsNd1?{hbQNM+xX~q28xnSuYN$?gje_gm{F2`2N?r;>R~^Z#U-(Oex*#s&cK5dFbfDauw z&uMP&7tY_&hi|B##42wM=j{j**a*OUzTpp2Y9r&MOADt>HI;NpJWmo;ygTf1Bxf2` zPNS0CJvl$6?iM(~wem_NUUfLb@Lz2NsIiIqBWqL@?iZr)R&R!lk z_yAGf!WMND<&8%F>lYAUg^x|D--vVZNb!X3FS+XQx3n$`cc86EJWeMTnZ$NEe~?n6 zvuL*03U0jZea*xk_z)UotEkfB6VB)p7AFD3%3O|BsJGT>>?=T&RDWUPyxEfd+LVX`FFf)6Ci$OFxjEGo6^ zMamX1hO8~V(dse0NBgD#Bfoq<$ya}_k}<$6Bt!x%WU10da+(5 z4(mibn-Z(sz1+H$LI`kkUVISI%QXd{G?fc1}b^Bh; z7nQ~|+7NFD70p}KT6t|GUq0uD{Tt&A+)tUmsx|y+eZ0XJF0*~B+Cl-_yGlDI?7SEo zdpNVI3u1bkF#wc5vc);duliER;73cVEFeQZd@JZCBZTY_esL{9uj_P z65f@B&604sNqA0oWF-qfnW^Z@BWi@e$Y-{H6_VYMLk$&FqzyqkG(pYKJ{D3hc;K`t zXK<6LAyl6MhfZ?rY|h5Gjb$Q?;l@;!KQ)*Z!q)3)TU=z-nium{-VpxA02W1Odo?T< zx3Im0tK8KI)##9)&_8u3K&V=W;C<)`+V(SA6L~vYo+gaL!xMR_sDNOF1lLFVN%Axa z|1;{9@bwa2QFH*o>j=`$i_);^;|>z8k-R&i?@9OulQ}ybUDu(>4)2qp`5JDK2z}mI z5qk^WGN-wjJ~IF8W}YA=7sR~QR!09{@AP_xxUDJ*mE>mKK~|C5%pwFY^m9QzJMw}4 zu$SdBZ5l@&g@C+RQRujCdDpmky~x|o9m&=b8NpGMa!bOZ7)X+~V^)Ojq zYXz-Pfl2!jY43_j@rEuGe}g@7YVEvNCF@G}UKP8rAUjk8%JEC%-7VJlt1`EqC|l2F zdt@K3J>q>rYtPsE`s4tE_=l_oPl=z1N>BQzZ>T9P(I-)|$V zCFzA+Nvf4(SCU?kuq#RX5)?^_98mNrK`le~MCS{HD?`_&qjzb2(GV*Tp0it&#%~Ib z(AI_7anaeoH<&(bh?Pd)=`LTL!dLo3!dJRy;XC+uox)ezRrvDB$_QWSe%RiZyr= zbN!*5{S=b&)mN+7kJG*cI9Ln6a6xRhO73)cO5uH+O|?Ffy44V*f^3ptf33n=%b|rm zR&XB`IJ`$NZ!gH`o*u|v^Y0zs$%4;aXV1GDvj{s^x5cl{BATk>W?CjGpx!0PjxUgt zu?yLx%kVxU2ORmSk8iUE<~77OiIEuJ2ZFsmNpM%*tWW(ap&N1} zG)Gu*i3i}fAW5n7?$i2w^=@frsbo)aR2ijH>UsdEV)l)KupdcKcw%qxsx~HpF4!;A z*z1Zqv5(T&6(?k<{Bp3v6?Hs`9KIuT79mHy@v>fnJ}Y-fydnmq#bkoE`Cszg{z3Nv6{Q4%iRU0b{Y9*4;6H2U9M`~lmT~dqad*?9P zSm)`$`QGn2y6rrW_`>^(EXz(c&e1hMSfL?}YPAj=2!I#Mt`lInPRCaomw;Rs0i926 zcwcx~6EV#2bZ}Lld=^`O)XW&SOR}9qwO7FBsAUi8MqT9>jyy%gMe{92lcVNKu2-$j zm)tvuWcW^1e)R=%l~fnyRCSWB>Sj~bc6=4W)yFq9;W1NZQrwyo3**mZ_0G(lQWGH~ zMIhdq!c|j-s9vQ+qPTJsJ~x?;P$E1gslE-yaIZit)?3q4&Esf2A)3r zT>j{F>@WX}3_YN382|?@S?^0lV{}C)n2Mg}S(xYq zIYKyqTwbr)?Wip}bw87OB&i6E`T_@)bcXCPJEfd)CR>C_{IN7^&z0|=+*^uUBpj9*g=^(Vg+Vix{8Z&x>9iw;|In?taI|z9rzkT z`=!)(hqz>!48OVuuQE0OC!&kC;(Y3!J&SD4T9XWG*HPy?1o0j67nW>%M(d|{D#_l* z;mm{ zfC>T@Dsj}4Ia!w*3}5aL41a;|a(nbPO6gPlw@N^&o2l^}Z-8q6=1itUyy^lpOoN(f zdMu^J0cflJ{;Bs`3Y>HyAt=0-g;!F2Fo3olz}rO{=)31LKwl67=p29=)F4oyUxk#i zl#1$#U%j8YeCo%#;%6n|Rqvw(%!oy|F=KEj6%0cwx^I8lu=hRB4EP z4aAc!#Df}QhnYfd)exUagjbo|cJM9)w{JDrzSK=~U!xJod26`8qXLrTRqwsUxIql+ zaKcH&XA3-&xnCU#PVS}mhw4F&TEmxfBK%FMV_lel$_FNdZ2e8AAD1nwTBPh_2bLQdX@%^aF#42b=)Rw1=nE_t9Ud$|Py>R~B>&5Tc$M~?~( z^q06I1zPUSe@eqB7jI^sA!r&iSqK7No7=i51~>`fdBzSH>C5ze8$NrzJC0*caO$xw zv(mvERRi8TD#U+IfUy92?FN`cZoZTGz^ewaCN#3!BD2jG&*_tR z+7zot?MbH#)1)1xwAfL-bqn61hqMovy_}_{1+6LdW+v6It|7G%cPvvDia6AFM{pJP zdh13aVzW0(VfKHhj-3-m=M98tUHy-xbte~?)(OajEXer=!K>JCsRvlECZtP^4x0VFj@_;%&9qBDk&! zMc*d8oF!yk!P&yh2Bv?4=K$X-?`}Hrr_xyUoJ>1Db?Xv%rHzb0_^3Ud0WMC)%EFP9 zI!u@PwJCKq!>UnTOT^-?#Ujp2M=m#5hKlmL4_0gOze@rl(E8Srcm_B*e>dRBjlgs8 zQBr^ zhEE!W%LVZZPA&&|1tQqiMn!5JWlL=BCa{HYllCm9-Xtm_)yzndETYSAKU zIHbcA2!`FqrA`BRL{abO!Y@TC6^Q%XIe5o{Lz2GkNkz@`H;nh~bgB;Bv(k>03YAR-18Z=?m} zv7(8g-t%V93I{h%+Bk9k?C`hzV;O023;-appR{(&WB1E_u@5z+6(t$ktUKpz;wT%y zd5iv~(krCjA$wV?qh{2;$9hko| z5N>^-I=C`GT&1+yx)dQMZv>*QG#HDx@jMOrD3dPXV2I^ zyILyQJGS|=vNuQI3oihr;!U!g_o=0uG7I~!^tzTw%#zvvFTyd@2;Jqx%J9Gd`}h-@ zjSNQ~s$?MW#^vv|yi9!(tv)HDJ0u}VK6L{_HxY@qBD2q!lqsZOzevQZQlDu2+a4aR zDoJTnf8ek5#p6%E2yriRa@D71QNoVDD9Nmrn136X?C30>q=WVJ#1_^UUtQ((d@1qy zQ^LYP6O0_@X1`7L{*p$iMwWsVHaNyUFy-{A8wFXtq;cM$O`kXDj{8But?fLoDrwhe zcv5QlZ=~f(mNSs}(dE*p$4E*9FPSSNz^`up66dhGhqRKgt?uM&ZB%pl?C3KkG4^juq%SndeImh)>TJTH1JQ?kA5EXg-?^dQoD+~}AGyf8 z6GPkSg_-soc8nfmFA;!Y*m!0<-t;jY!DUgH{vzATbZQG3DRqdf$8d1?)!*n=k@ExR z@CG6GBp(Vumor5&eX4^a@hrt6EH>Qi8znnAC0y^uFL&b?aykdkx2ftpQx)skwotJP zc#;4jP!;x+)5KWFARO!>*rd<&G>q2%Qq_-CUd&K=e9Z=`S1sA5rN%w_`xjz0c+}k1 zy$_`ty9IVL^i%4L{h5SBhQW%$d$G!_y|EGC**BMIbVlMYo1r7Tm=Nh@vy~~Ay@Zb% z@{L@`6LKM$t3~^7#U*d#^y%f3=dBD>&gBq-)`wd;X)h|JA-*CMX+)as_=<`LRtDH( zt>#gy6#yR-{xp-p)@w4pA{>Ug3#n6&#W$y;qWUl>=)yAy(}itxNuH=By@AP9BeL%O zNM|iK)y^eW6JL&vqq}~UocGU;XR-9?+n3*@26VHIB%Y*ryj71ueLf|lJg;K($r2rQ zm@_Nlsgey^zgpF#5lDiJg{P?lnw(hBO_AMnims7VvBNUA*O(?vk*hR7#5|1REmhkgg(yT=*O(V9bu0Tx8rPGpEk#MWkr&MzVK%P>25B_1A9Y# zVSNxTCuxK)Tu8R{7)aL@7TTekxq$FviIrS9z!ppl+ z!$r`?x>P+gQ?)N0{fjn4+5gg!|>K)@-?3?WmGCRNwsB_DjfQ3~68ais15R+xZ;u{r z9?{8g@^Zan!-mhmybWCS)Gu98Hc?e9!$!Jat`<{tV!OFt+^r%Ii%4!V2D@PGf~y8B zp%(=YjnR1kFKg44(vjUOEn7x zwyIPvT~k}FT>p*p_+FbY(ucp5oFhwzBQ(xSHFMb)Y&9OiZ^`sV`U!~k5c;F-yUFwc zHC{tW!N|8ftMvnQa5$*X29yd|tS|>S+}3v?lvKE^8!Q`bUASiQI(xt{W7F%mWo;7xngz%LARty?< z?M)1(bk^2|yZ=!P=%Rj^E<&7&_ku?n_-9<*k2dh#sOOM4P# zEu-?n<@X}CMLRQMT=JzzHjg?#`F-?ceAL*NHJy(#3w8QY<~H|92#ygCw#ACk4>IUJ z)o&)(PtDVZ9^x%bk@0369iR=Cti*_+zF^66>>TqZb61pdaF|K20%O8xo}b(Pc#X@K z&YIEq<`_%&dhMz4*Pw^9PgJ{!W2o{vPP4u{Cq`VnhPi8ft5|4sT|yi%LKl3v(S7z) z!dIlDr{gtes;+G2FQ&pX--tD!8e?+Gb2;lJ4%%e$9x{2W$zxAI{$j@X)RWpJA1P%I zK;ZXfPe|vdcA^r zH-)3&F<+vZvk9_ufIR9~fY7$pzU-xyRcs$;9`Nl9kF-0(L!DL4cyt2L?dStZ=5I>< z;cPL4o5eU2kUX{ZaghNwfE@1{{Sr%caIPCDn44RcO-?TBW@{dr!6HS$)K@(MCjC|& zF`xSL=?GdNw}3$T)%60ELlWJS!KCS_F!b<-+y5;+6xHt{%W2T#?@3PuP|?@DjVjik1y5U}Djp#VpGIZjcjN8b+gK$3 zJ!G;D;DEPeT~sbvp(`8JGIn{T&|t1{O3tLibk4br>RC<-OU~gZo1Cdf=$zsBcUZGX z&N0}8QfB&5I;Xf%tvbNu{OAmmBM0I{^~o?;&J7M3WpdJnGbt@hteJFw^py5& zvc9TVDsy8#wcp81BoAnn-C0He{Yow1_*3`hnX{akp+=FI9%|j1sDRo{4dw$coUHW?7&Z@&O(HUk3h)E)*zC+;pS zd+ZSz5P{ACA@zKHX0{$Vw=4sKjlK-sdl(QwcR=i=l95y*Hp}W_#)JQBGad$$$ao04 z`@|5|v~H|L$6DMNJ~{H5-Pm>=+wSzql@KG6m;4E-l4DeR5ZGSo z6msQ6;81y_Ty}0-#AwJ7%`Tl$2s9@K=Xlj*%v_e7O;*Y4GShbQ)l~ZFe9ch!X%5`z z)0j>CHjIAMF5j*!tI9{?%EOiESAfMp9eJkshva>zr6%EHI)wM%6}>UYNzXTl;Vjf0 zCgEQi>U^Lg1MKv*By!Rx$O**I0d{&yCTiK~3kg@usS0{4-mHMB#yfA00GabYZ=t3v zcBQ@C;V=tw*{iw-tpasNSBsbtU}u5rt@&BR|F*2RK&J`+`eEIYp_-(XUqSd_JH44i zO_G%h`nmzdQzV|`$DfW(j(*BK789>j|CD!-NB$e_vGmu(26?6sOL6comQvrJ&InL< zU#TtI_6C=@6aU;JNE+Q7bHB(4##r~l~iWg!> z9EodN^B^)AI>`EzTOj=-6++5mr$Ng7+TkbQf$)GVRhPfc_06GN2L*%|5--<#7PH@> zN2oTwoNXMP;Ydb6jg<^e%6iq=7|nW3Iac3Wc7HL#H122p$>YStDKh%pryJ1{Zb(#x zJTiyB$<0I&;ND2+XXgwMBP-P3j6GhhY3AQ{Zf6zyN#fF_d^v&9EZ^p2e7k(Nv#iY@ z7ywd!Ho}DX#foMVYUUWP32hhdX+z-f+W2-?&GBZYtJL_l4HM!~$=0ByPw+R((z@y0 zcvJKxX6Tmqws+&(qEC5PiEm>sYD0*FoS5htO4W9(fxx>P0(YZ+RxcEpm6Z zi2IvE_kAt%-%nTzhR=6lF3-Z4rziz2W5-3?u|uPE6Wk)vY!MgjaiIM=zTF$&HkNO* zH{Nuro+lpPup)C(W$Hj%df8a)hR!cY-XnjC*QV7rc94M zb?~!V6y<^U3$VZV4Fg^9nQXzl+=4H57W`~^w&3erb4#he>Vm(_7F<6fQ{(xH|lbyWXnCAEeEAnbe5abS>c!LJM$wOx?oL;FaBg zrx@TW4g9nQJ|PQSnFY=VVLuJ?kcJtUg_+$2=1cZxICrFBzP>-xv>$hYIYHyORKwI| zVFq`B`B>vQOT#S5!lcnyMtp>xH5%q94RdD}=7TOUf7dVrHO$m3%nMy$?$j{t@YYc$ zXJH=d0&|6i`B=l0WMQuD0&}K@c}v55cVDLMKkWi@h=y6NVd}FmhjoF;(=dP1F!3ym zzYEOAp9-IUqhapJ!lZC(`Qb2ar1En(UisOVktE77C6c{Zy=D(fuS9r{Xlm;x_Bj7p>$RvBXFHt!oQ+o?GU(e<| zZjd%4DsRjHzP(4ln+)Kx%(fW}TDnKTa}D5c6P5R7Fy6UGz`+?Zf9p+DF37jg`MnG{U_kiPZBOuz=}NYq7c%=wE8+%MJ8P$ zeRURk-%jXi4Lwdn2MzRZfX*R%L{4}lmW#gq<>(t?y!kO)=UNqO^jJ#V;o(^(v#1VA zJ9?{ScW2sB#Yr1iMd;s?wcw+=;P3R`iJH_d1=syP+rYyA zRB(_#htzqU1&g{CyhPW9J1*0@&h}l>S%~#^Zu@!^sytT^WEeBJvtX&bC@V$z{}|rb z9L5pEW98_jq-o{{Rm)x3Rt>RB>sGW6r^oBw;IqqDx4zvTUiYH>S&2hs1;0Fn!(W#k z;;5G?$@|RmH-l`EL<*fkTq<4b$y4`AJr+lQ_mY2=$>-)q(CIDmlNf<`&CQOl?9bgG zGWqHh1I=j@!Kla4Nk_m0&H$U(Q96dZv9qmywQvXm-By(1U-vfC8>UHR>x=g50j3Ax1;b}q|(x=9VXON|_D4>yqlWG|D20Hp~ z4p<*TuriqM@eDlk8dXe$->&#Ck%h4jF@r-sTNS0*Z z#_x1Vd~rjZZPgXBmOWlJ(X!q>R;U4W@EuY(-sbw4+Klg7Ymcr;D8J6Kf?Hg$`Z{Rp zcbTX0f+a5sE~9+974H(=E3Sufap5#!)AKfx*yhwf-lcv)gkD(QEWL1wLoe_P%##HI z!_RMf$xrPo)Ns+S0r^a*XBrq)VY(e>3u>UlS9imJ`RJxizUKk{h ztAEp_IyiRfSgY>tZ04%tb21Bb=50;00I0i)&uvY0Gn$g%iiGRyZj>~BAG+>3NsWwZ ztb3rzTGNYMS+VaVb4vH58BH-?*R&Ne?yOzjWCIOC8$_T>!ah7Nw|lOKt_7gM{7$nxX@ZP5 z@Zbv{-YAhH#MTCHjLA%ri_Cy~e8cE8-sFR*m96|Tw925t$gHIXkm4Tp(@rFPfZDHC z+@@`%&+64TQLl&9CTE5Y#nny3sV)#g{Hhs3upxWnHK-$!Ei`XB`}o!5CyJRfAeoCfciYa*m*^F7}J0 zP^aKRC_qxuVB5v)q{^7Mb&9E@)a~FhzvjI!cD9y;8l(UKP$bIzazKS%RgWJn+>{xu z_)xUI?53LzRd-}_JN?82ht)JSRfG-s;R=u?fq z7U?!enbw+mrQPq)ZZL&(IU%^qka(V43JEw)*;qlzd;9&Ifc+bzN<6S%j%v9B z*Peph7P1@0!$KnFfr=U#(Lw9kDV(Y2Wl5aZMcW!CnH{b-*9E`* zB%0PJuI%m-CF;wSD1x>7pax(^EI6Lji*a2W;kCv!OlX)CJ;6Ktr!%ZaEo+Ar|5k$Q zD$}jZ`t{a?$YnY4IhCz=#Z6JLx^ll=AELAA^Q^jM2k(nomeRkP5e+;zm~-Hm-0YgA7K65dHq71mEe zwr&sv0HWVQS8~lpUm-x{Pv|4UH!(5ssRCLQZyk8X8{Vf_{p_K(HZ08_tj(q}+k0im zxADpv@MU`;z!dnhJOS!H&0$p)$n{J;zlp+4R0f$}xISfM+Q*U(0%VW{7=DIq2Bd`a zLF0moden18Ntr~^#prHpHC)r9Qoz=EYYP0)NA>C8H+1;^cG(46$d8g0$l-y4V2M5q zFT*#*X&NJj>n@$D5m8~(! z>cU7_ms!GyUvN%a5tk6IL2IEG&Pbr(~lD3$*#i4fqTW|&OOqc zZiwK_RC%AT+>G%uE|0hqh)L5o^B=R_U*^*$}N8YYWwE2o6H4@=mr<7!`6)muqQv{qxAkMB4;<7SW%VI&_ z_K=$jEH&$61>S+9MWB4(-~k$HWKad=V0%HuTDEjS7t{in0SI&hXq6o?4OQ!-4%rDq zr)xUcvXUfGw_H(pmtRDq`nf#4kzJ<`b1i^;)%WIfn#bn3cjd@Sk-FBJ79Y|DHM-$e zg)>!>-6FlzreBJ@WGbPFtN;hdGdXjO8^Hd-^7(23<)e4$R}J~yQyCQNIDBRXMav*F zDD>YmZaPFZtnZh&KJ*(_8u?tqyUwbd0ZNj`Cz$gZzZh}~g*`aXsO&s8H zExA1qnX&OGIJPHC<~rI&b!xCZcuknKgtjU3)utO6U}BAkNpN^O!k`D3`9(n4E{*XM zCemr{0CMH1s>oud?Kyz@=>cS>+2T5Vr3>j2eVojn+|iZrqW$&QtY;=E z;dFGn>`v>{9*RMZ<<|uL@fP5Cw~seIM3wM(sWxdcBdU`V{1zx<4s`K0ipiANQanJ6 z9Ee4tskvlLcGNPYYX@LF@R(NHe;8@SZcF>9{Rhf~5VYP1FiFWE)lJdNWisJyF!P{R zh61*}W&521x`mK_QnpY|YirSh&9Xu%IZ|imWJw2Gt3l#6L&QOAqLP0KS{OmAOsJVQ zvtQ3C86#Rg^_vc@yQ5Dqfo5I~*S6W3A&ipOD=#Nm^Gt3C&J_(SwxmzhvdKz}p03i? zgp_FwjnvDxce(RVt;j~8Q#NGzCambR0`>CkV|*#l4eR;k$b{f$Y{UwqrJA0=I|-3R=E^{HYn$5=Bdy-5LhOlB?1|obw|5}R0O7_)S$Lbl zqkE{6qw!WVgezPlFOjRzWS}7PJx8xFTG+wro{FikJJz<(stc|b2}SWGssfC1U8IH~ z!>>G3iW|nvO#}No6=uwQ%HRv_Cv4MQED)>p+El2|^r?liYaN?-BEYERJi5$60_fUG z0$A$b;;k+?Nyx%dSQ&Kr@EAQWX>)=yd%R&Nu&V-NH&zAacp1aAvgV0k#ZG!oS|qL0 zKg~#_p{C3qy=AwjfIHPY6q{uD=viW3@3i6_8NLpWcT^ZNy8^Si zqT)5ZnIS(gF^Mcj<+6hP`w!>$q^)IzgYuHg#)R{SFB@fTv;va-T7gxwYzz1+92)V;7gxvC8Z(FdX!k}!e`>B9|g@4S{NvEX~UguCM~hTiZM74SfU)>c(4G9>w3 z%IpnyD>&bc6@X!A_bw{etIKSs)nq#DUFkHsvnj@Pm$e>>9wsjPe5pdTzFw&y+oCsz zxe6s;y?+hFP--2|#cEfMo)!Gchag8=xTv#-w%2->dki94fY|$$CEbm-L;9@MycsBj zA0-i?fMk~hlcGmPE)5q84STBSU9%Njg6HF^ZTmz2XGxa3!BK}46{>RsK16f4Gn0%`q$O4;A?Kq z@NVO7J%9M*(Z+)ALvM>7c3Uduh%#U;8LZp1JDSIVIm|G5w6RGL4Tm8E*-X=uFm2 zPG|FOEJ!`M1z*hih3$+dzSF&eFw!*zIgWt1OvWHb2XU5cj~FtE?v|&hrCb}b z7X8V(wilCYvrMj-<;~Ri!H%ZtLV3&KIkCt4g3G;biQ%?pe+4@CrOHPg9qPiicZq?@G2xeTCy`Vt9>e(+tkrs&(>;fs-V zP{AZUI@j-?8J#=AGCC({sWvXNhaEoy3~`Gg3m%1fvx1#r9w4CB=SIx^eVN zMdO{t0e%wW&2sFV#f;J=c)(qg^^jTVH1C4=qjS7lrOXlQeEj8c4c_ILSW;R)F<87W z8($NgiH7D^uxj;j#&Ykx*=301L3}Ic7VR{>Kf#r$H<^mXm0}Zsgf|;lhJm!+TEv85 z0y1GN8WZj{e99s5-nldTzRDgnWYvOHhmiZn2X(FA`OGdb8^3{NujJG9$ znM}bG@bBygjyXYT%7WPyZ!iv)1ek75dnveaNdO;Q_6b=d|7mtx9riU*cWknB5 z!&P3P5!9OL%aM9{0jF0uUvoYb5oY)A1o#!bB5&Y;~;W zbgMat@2(jG>H`D<=*$XdHCjqsYzZu)Qw38F7E$*;F$lUQ^8{DHTtSh$E**Vk&boA- z(CxOo5M9ILR(^cODA9l@QT*`Nqe0gUAyi=udPXwt`FJZ!i&myKEPC-S`_%L&VUu24 z;EI-XTfkaepZ_gSkQ;C5TwP>+{v0=M;Ya7+(?w8|Ma%{mHsT~|ff62pp9~jre5vB; zu3yf{HT2C3g48e~n~d5YnucgrW;8@IN%HI|C=pp{7W$cT4!7gn3(FY5lFtH-OIw-6 zk0x)*)OX{}oQ7t#Y4;!jwod7pSRKT)>Q1eA&iSnfy@R-s`lI)D;IieEyUf7G7EiJO zM(-|cuk~hpTshsVy#dRzS(h6> zC<_?fw1F=OQVEL;U|1&Awg?-@gLJa4tN2$LqVI-iROYtOOijt2*f{R<95!mGF75B1 zL3RC?D9M8bAlusVY2BLtg}bKe!a@e#y4}W|FHb3p6{)L%)b9Mi15(Y)@>!agXmxs_(-z^<-L)67WZ#!@;SI5ue(EL4`FRd4SvY65G{iTD4i$wG zY9^;&q=moz!}!UXtx%($P6SGBl5Li^9}_%V-ddWORZSX?qqw6MOPA`he&UBKlIeHUZ9P#93VxoDxWVg<*W!Xq4B zPG=I^6LxnZ+pZ@v+DUotKVFL4HjiJ+VMJ}?hFWwIx7ujxOm}*NO9qxvFdG4Kj$OBXjjYNxmMzp(i9qi*4wU?gjHvexciuWlw>v3V3sP zQlfa-FsfcQf|nTN&A{1W94#9*amUfKm^+sSYRKQXth`~_kR8=!OT9a*%a{6gR+laf z?yN3e+Gl5VaB0EL>Vl>HcUBiJE!tUKv~-_ne?qK?-D}e3?2pULE~C3R%sgd>rNx1 za%nIi?Hi!yVe=egt#?_#+}oSTIk+NJc+D$vZ*hV>bA0Hi`o#qk!w1wa4yxTeD8LKE z<6%QFR}yqU(Sm@04D<%<8RP56PY7?23ZQL0;5}$NGDDlcJ8iT_Xj1d{RIH_++oa*$ zi`f%}^`*l4GGTpz%laMgx?l%f@3&%U|24f(%55y9+|Dc>Bu3{lSqQRXIdvhf940tx zou8q#Y*=W3Hc5kYgr8Rw^sy+;A-477M7CGWgcP?AZPi5D#QI4~NAXPkq@|;|R>^Om zOIo6MsmRSzk(-H>I#wF6bl8LkDyPkCSc2Fr9ky|4dBf5n_M|1!BLn0;8M_pbzM0uL`NE#R@02{5mzQd2+#^12EsR@c1KIYw&psfRC;go`723Gc$R+Tqi6LAo1NK*N6*?gsdf^hV&lXGv-B&GZ}8T|H@eIf zlwt9kc{T9vKM9HnVeXYp=qnAcKThet7gud&0$Z z@0GfHXM&#Yy>jEl^REnlfxu3{PPfM6e;7||$3k;T*}UiBjMrE=-Y92ZoN=6HLx^sOlyan3>39J6a6eB$EV6~5kwbb*6B0| zSuH&3QZhQNJ$Z5QVa`liD5H&~n%F>1KrN7CCKkWUf#Q$sFO$QGiNMUcF5t-;Fnn$F zpmty%junP(w_oy%pTD>;e1N?;z!l7yi-j0X*Z7%kDEe(1nd9u~^ti zt6(`zVw7;?JFT`q6SX~t7evBM_Toa_>p6N~I(~1ue>~*Yj~gE@3k|Mc!~`&)eo;{E z3WBMxJz-H{KoX0B_H+&8IC607u7^y27crTwznEo_S_k}YZ~6m1wpzvCr-C*|a_k9D z6*`Lo&cz7HM|f}C`C=geJ?4wu9QV<4S+DMjKX4lws%LpKi|P^keW%*zHU2%IBzRUlUP(>y2(G9=E!9eCQo} zN%6!)V4^joc*<;h){n?ci5|Nln$=Cck&mNRBhXjeKkg2j+Iv&7nBiUUg}u zeigRv#tycMG*KZ#kxRnwPZPBSHC}Z$L76<{X9pR*>F@<^#-yP-V@3yd&^~J3g)Bh% zUB$W?Y_Y|Hye?=Tu6`|vg(h()8zb90929wThlX;N7s8cW(8r;?oj&p!`3H%1A!O~O zsdZmeyp-!Ujn1}C$HKz-{$e`w1}AJNl01ofDmTPvt1WJ z!TlW#OV75Kp1t&3JZs)j&KwG2Km0+!W|iFFP@hA^&MZ>zy8_*8MK0WNVqJU3!*%J7 z#4t!fmDF|of2^Ggd{o61@OP6!WJxBWMyt@Bwag1NcZbSX5MWqD4Rn$@f1qyPJS$zwh_^ z0(bAdbLVm9%$YN1&YU>|@(yeKgWr2-qc|R1cY{mgW_6@4xoVvAH1e{l8_*`{LFcxW zSR!!5i3DDr2rQSt|FY%A(%D=!jyz9WK=g!tv!aC}zN%r7s$o?lTu;^OaGuYJR^>&i z@~Yg?Do?~(bpftSGSt3mnYd?*Ju@6(%s$t+`Dme3g2}u-oRN2nj>hGws zpS3mEQI&Pp*6TZ}vPW&5)KQg#(3Q-mMCaprUZ)t<{mT{R%UbBOJZ>B>u=#6*C9CJnYdZSkH6Zyq+Q4_f&YVXxrrBt99w-p^f300eO392b?Vx zR~PxkTK{9c%|MMoLIs)i)_dCn{P{e0gR&UD)` z(fuZz?azx+{QiypZ2uy^OrOyx9l#=im)68fIxin4UhLj;MRREBZTP*KpOWD9&G5$< zl6)77U;a=x7C(iE2w9=VklURWdECQ6MWJpyKJ=P9w8@QKD&oE#DMT=fl4T3SLXe$E zWeFA^)GWR~*}>xVi>jk#>)gQ|p{7vIb_ZQi&v|xQk1>XwPnLQN8+h~>M%CluMx4MR>Xf=;{)$VAu z3^GW4SqzsW_z=__1@Y7p4MeIKs@zBmH8y9@cOaxUed}#L__1HaizdHwcaz_BUA@h( zhq0yrhat|Gi9fACRi2|qWJZ;YYOoXaAtM$dj*4>MQRT5UR@=IAEcdyrwzzZs+kv^+ z9jENcz|dH3kiFGJ=&T|KTx4h|bz}7~CT?)zh&+HwzhkI~#LeY2*yDedju>bRnDly@ z-Z_KR4S`@?z~2;fMwy-4OimzN%e#(TTZ2DP=MQp#her*%7HNfHw$n>8y;>s~*D{pS z!`LK`1d*$WidtI$MAZHUG?_~)6ZrxMkizXRk4;lG#!u^^Sp)9Q70+y`Z( z54H5>-%0v937h_d^kCJ@zm0at%k0L^=^#wy$6c4zufciH1MCin-?^#|CWG*f-alVeb`cuoJ~ zAEnj-XExIBwyWZ;xQp-6nL1rlbsqlU125%qWMZJ&&6T%Z_ef?~4MZc0C#TTn;(qC! z`;AaclSzF=IC&Sq#7EBqM2)G1$04<)s?V2GrjFACe_?m&PeJqP?c}G-00w;!|2b<(FGsp>J+}k*~o2U}dK-kn5S6 zpT96aL!AeuU6$i79Oo+>?Jt~K$?lsl<>Q)vSuF)6AJ;y|Eq_XW!4&g2<>R;5 zmR|5&jm@1oK0kkaLBVf*Hs7+!b{{fI@^7~P>n9F;?q79l_U`QKru}{Ad2`Rd=9Ya; zJ#M`+ZRMXonEdk4c+97KOI1dOZ+undZJoY9R^H}&Fx$tKpYKlD?psuOcznUK)Pju# zm39BJSPGU^wvJysz9iUSE8rF&O2|C=d5ninUiKDiW4 zm{8{{a%Acd^EzST&LaO#OJJvkJV;p=ERsCP?UEwrkVGDtL@v~6)67%R#GRISGBqd# zR%g;iC%h{Uo}jDmcHj018-qHd;5&i9JAVH=6DOiQ>Q@oW2U+4j*Hg5(C=gs+b=us83+4|m>2dDxb38-Gmd-0LomIN{RL?nQde6MzJc%wYUp2?oW;ajD zaV&=>RaYMBtjdNl|8e4u344ka5t6ZGD{?f^9glj zt*5kTl|5RO6)jtx>9d*S`Js3YI>E^%K|1i>?RcGPTKcP>-78h0?(V_g*ep`-nHBSI zTv$4MJ{3Q^d&SQ_^TI^MYcXm)N(zaD{EpQ9!R7k`-$(r9Y$5NDC{O-sD}$E7Z6<76 zBJ8xm-+(`M;ru3&KSf}%WHl)pOvWikly&ZhccO$v!m)LJ&T}Lfatz;S4VfibwL@6z zrpnpZzBY!#hbk`|!K-Xrt5X~Jf+OcFMT-O zhA(dUf;4E!-4wB@hvmy9YOUO?>H%)`cpO!~=K(zZfg5;QBscXNevKx6GJ;K$n#Q|e zAShpv&AAy2T)~qXEujevTx6n*;HEKP4wdR1*Vt`N!$>TR!@SCaJO;pV1`%cPJe9i? zae%dHirB}^6!8g9s#!u4MeH6ds^&knF z)Ea(W7cD}%tyeTTwY`mB^BnMPR!b!;|5bGpckOkYU1LQ3rH-1>eXH%PbXjJtQ9OWlB2mkEy@+3cemc?wxU>+Xjp;7%+ zK1oZD(5iB7)Z6(5D6h(@wLA(K&^z)M<`;m?vgbZ?5}8Ku0%@u0=7OBQxY*eqT8R{$0- z{XaY&QF`3J)TnsrkNv7!=_e;jXT>Q|xUe1ye~!#i{3i>)i)Y7Q`5|-Q;H+nJe$_K9 zQlVO9(cnf(=A2m8{HfRFw-ZxD+D_CZP3jr}kT^?poHO|)&Wa1A?#jy>+75N9gj&@{ zBPGEeau;u@tx^j9FqcY?e^*{CstBk7l30^QqfH#xq`vwN`gAp`Pq-6%6Z*8MdXJYT z^-w&c`%Oj{=!}+`jCzp~`R(kf^V1S3hsT$XboCV5#dLjiTpOlyDXW&7F1dEw-b0h_ zRaar4>O4Dvh>LlnHdT7@MG$d61;^AI7$^{Y$!&2&>|ZY6j1x%3?ia6{I@*c+676?* zvj7(-@#fqGgO6?9dx%vXt~Mc|hX9dAIlGA9{CJyqoGa!>?V~Z z=p!c<3LH`o)yd))=Z*3UR40h>mXzGA{v#pytD(b?-DfCX;@OQgWj{+EE4NzOD@Qx8 zihp@k!>wK*WlTMattjVP3UHiY27F5~#u(pA*E*9NtpMn)=!}1|s+-9cBfNN4i%nJ) z0vwBRI(drfLz)D--__`Tj=F(}*H9A2fhmcpp}Oe014k6SBTE;Z>OHO_S!OC_Ivl!P zd+HXoswcp}(d}<43>R0Y36L+apIHNBRksn_`#7S2-XB$q362~qNQ;5uKVak{jrIW} zQ&#f^IkM{yH)t5jyni_?=UZ6SJsRI|AdJPbh%j6YE()%eJyAAeN(uMh`JdRHBD@LH{mjH!R`2NfEEGz|ezEyV!M#vI*RIoh?F!t1S) zKWAxOuMzqilm9oGz6HL-_dg{|aIW>fgV|9$6X)?U^;Caai@GL(EipZ3N3_gS>JHu2$$V9Z zcgrD@L%pWZ?g$IXAflVp^&TrLx+WzZ-un-Aq3-hT(EKMP46Jk?Flmx_yDR-0F?G#R zFjx~XJka4CcgQr7RXIQ^{bXMU@|2v5wzR{03Iii!Ilk<5&SFMTuVcw+bm$CF@YswS z3=weIHx*MKQ&voUAiw%J`)0L6N>^5Lh^guNcY{bQcC}=buB2pzPGq1CB;;Ko%=5(_jFZvLHFFR)Vcp!a(~jX z4p_4}aW=_6y#`J%)O*rHO7CJtj5lGW14DgyXpUL zHK3t!ol=v7-m?)36MMO|HOi!T$>_L`jW!8B2HGU}7-*AV59LKWOnDpOuGngdxh6U- zK?1(V&SbejE#^gLwZ8qC9OKH1JaZ)J6nPA(^SEin=R>j`!xtjxltj>rxif1>q>i*PCF%@mfk7(yCceH(z;1| zdor!S?CMH8Ql~vRo|Y~wX<1*%F=_Kids#BARBy+eB+MOPJEA^^*9goWlhzYaI|=A5%B6ZQOZcAMpI9FZS%4Bj33w%klZ{B~Xq@oVgA z;>V&;i2oUUjgxIu-Nvt>*qeBQZ`7njI?H*)qnaw=E*%CrNKG`)_F7iMuQJbBYAg@3 z>Ond2nbkRj0Ds^zL4r&uTwlo`vY17Bg1-{3EHkUW-^zp3m_P;)K|iV8-GIJ_olLIpR+jg0?b8aqr!6dqyAG}_AL^XioDFn0LYE?QY!5XLV=NRKuNrA9 z4$&3^o#tWnHgOOl;j2uzYfkg2^@y(~kjhD9R}dzFZo@77Axj4D-aI)@i>X3@CwYqE zotQya+RCyMU8k)o6GkoJU!sQD?t-RiqqcKk{KZ=J5Ih{Qpap0}V6bEY9?IxZ9UDnUoej^N5NK~fn=!kADD z`dD=nzbN1*zd{n%+L77lbg~8dFuHm%ex{Q7oZ?GF?IK3xAUV)O``56lPs^#cku7fD zE6tZK6Yg~N0h2lKy$@itn~k_tEm{I`Q)Le_u`LUCawa+lmUH@gnX_$sXhk_8A8B=> z=rW6E&6-nE{8PHjh8j9kI6MEAu>JZsQYY21_&CYezZkTd@iGU6dfryu;rZRg$yt+VoHD?Ka336PgH>q9RohRyaEj%(F$giY()}$Mz(>b>g zM&m&E(xc<6tDSGdl~4<9;nt`c_^rI44Nr~7AqSVK|$W*n9 zUglU{XA`a9sg&z$4ES=lKtUnoTLh7E@5^j%*mEQS>kb=c1FPwi#42 z$kID2y{x5kX!z%!(=W0+8$&_YnN8{*h7708aS~Ks)rnNbZx7cu&^eu>SuGIlN231D5EqsP?QlRZuZ(Sf-WBhH@l>hhfHTCE^tIp%n<>Vw=1kAYjJ4w_{R^ z^21cI$BSZl7vU1Ea_SkDb1fq+qb-grgu7`^J-B#60Itn3_4sEQCD!J+CVRJcuzR0b zro+YEk%?7!hoen?&X~F~LtL_om?1i8Uy?2(W9o_$rjlML{ zwy1qOAT@G{54c};Mr(7trSSFST9g>Vu2{C*d1(Le>dqB+@*v}Qe^rE%RQ_r!7sc0N zgg?$Jo&7)7ditPw5x%i@TjbEBHI<$CaY$&~EZSQm__nE?eFV?|d;HX2Pw(v98acEJ zpM|u0ubA@e(KFQ>1`vf0>eobIg<>H$cnU_S*qsMBapjOahDKGV%UCj4V*9r4J~XP@ zChvXBd+QR}5{pLxqVSxSlV#XG&TMSTPlLt}SVrPa1|51?hW2b*y6 z;F^iUJJ%JQYumuxUj#*2slKN($F*;rQ?*ZXc!}ye5%U?vaKC0s+JtlleVa5cu;HkX;C%_ zixPH-F0Jn4Es8UX9iRm&S!QhvQjB~5F`;(Eq25^+FjiDmKmzL94%MZYfWMf4y@%@3 zO~4;@K!ER!L`!p?N76<>sTCqXt8V*2)xE`?k~qsb{g_bws)xbl zrDUz~_)jX6Fbq}$`{qf#IP^+tmEWH1v z&$Jz~I$v!9FU}7l0X3UQbZ?0wF|EdGp2c70P}lHH&W#+Xi$0&Xcs)jyh#_p9Pg>TJ z!Y^xB(r7%@ORI|MPp`eWEk0f79~k>!Z>-7%z7!P#K3#mOx1&7@hN8{UL*7$6n*$Nr z&Ii75azZAPnTwx*0$ z`{`?EzgErC^7Ze0TwOT%~XJ(pyS$RTJQLGUpQ-P{)0GMe93kcU zTRXmkZ`BThWKIMPZO=F6_+zM-G2$1W@({A1RcHOdzfsHTe=qhzq+@~HCsdw^vH+E@Bt%XQH-!|NgUqI6-kqw>1#B~AzL+Ux zi?R~Tc7?ELkWMGWdpQZ3)bL$qNpLUsn9AW7IBo%SSc;Q3SrW{YP^)^I3U8^gsm@j{ zmN%(ycyP(OA9s+iKH^Tit<2z}s!7KZ38_iFE)fQEPMt;C==vb0enB2mixh1oCUeu*R+S^;C4*nK2=F1-ldG1us7^$9 zHIfZsElPB7ooDiBaIiR=Xq|(3M@>`A0GrJtV|*I7Bjsb%g?m`^ZBhIARlE3!s1NuB z*L4E1NazYj==UdUT z7IhohBD(xv%axnqu5=wvUT2mKbN!PTb5i#+ge#N-*QeAHNeq)pThNiJS&eIld@SSR z8~UU3GZLO537M0MhE?+0g+NU3sCa$@8>?l#;!D%JnqQI)AFmG8#QGl56tPWek-0i1 z(Rqbta*a@F5@9m)fqC{U7CRa(-udn`N-IJnCcD zxwX8_=airR*7998^-r*ua8=vp8$?*TT6;uTHjyoeQO{P}o~=%Qw%Yz|b!L55jVIWU zRWqbz>&}j?(GJRQj&{iC)xm&f9uVaa0y}nPsB6j3FV12M?ya@Eu5BJp4M11PKwaUV z?NMhCp%3-m&{M`Tr)?XGqkC08p)OBMUBR6cF}1lxm^PbluoXeJlLAoPz;R?>U2oY? zo$76}Yy>o)t-&S-0z?qDcOp&?K}e&_G-YKDjF6i9SH{zZfC@&&z@A2#aLe?VhOTJ3K zT_S->kd@`5~ZNq!)rvorN~MTH3QQh!f-o+CZZ5UGTQ4Z z)3ZVRzU|#;4a!3DXVe)(``q(z6fSjW%hd#pYdgPhi^d4Mrj~Ek(uQBdz7d;Rf;gEQ zmLVk8Z=*IKnI(M>>nD{U_+0lvhSXRK1jR&afouX7+zRWcM?M-8# zCfnUh@{PT-p)|83E zF3yYh1=lR|I~y4mUJ(&b!@g#@;4-+T+_xp@8|4$;8xIdIvIm3k)9s-(li9a(tXGpt z+i%-LXV=)Ot)rkMWp|5*`f~A2Ahkf@xIa{tN^mH|LrUVoDi1^~qfA|@9b3V1?_-D8 z8?IJ~dV|sb9Nys}xtW}dtxn-ZdrRkg|LO8Q==wTZwjUo1PO;ytvax}^%C46hDvQZf z_6=7e2v}S_dAL~~hB#h1V6DntS#3Stnf?^ygsmx@4<8&_eV(e`XTp3Sr7GL;p|MME zs`JZ9&TOuhMS+x{#Ztv-dezw?%-Nki*4wJHqt#iCo{?&MwKiC-&WcidMzOWq zRv*TH&R*1Rpd$P%+#vi^WK{S;#-<7?(?6zeeNT%kiZug#)hKoX+IH@e&+3@X5qr`TKI*qMb zIOj!m+xphaR!ap1-d;x0@P%+2l5#r>x3IU*dVu!uLAvWrDbcN~4>zfI`9MZ@84*5( zIM?Ls7J88n$jU|R`x(Hw;Zn6fQ_$#`YLdLHs@aPGhkj#2;`0JNquzMce14hF=m^cr z;+Yo9HmjP(+xBpqcN9m1X@DOZt$t3bW_7j1L#`Yqh2~a`;^0M!82Jlfz~S93;obPb zPr)%h*j`WjgllB~%(`ZkW71u55t@V)_L|{CkE1NqsTw9`9<6+*vTTMO#glg$)4;57 z>w%Q;ZpYA3;oVEMpP9FJ=)%{#rQ)H!Id!3MD<6FgA#oYI`?>IL=ez6#Bj3hcK?V8# zQRh2|e7`2Kx3|f6)P`08d%j&V3h%>hQg+`{Kpa-z7LN0Kho~`i?-!JoqB4Wj4X(PI z-K(n)JI*}7)}ih|jWj^H{x?vg5vpkV`EK!gNeEF)b;)h zA@D4o#>}u--mE-;P^ZfH2HUD+7q(c!O-K=kn2d@0L|(V&>Y_plIndFOQySd@=iXCw~9T| z^JTu)tiEajR+~CKoicogUil<>O(&U4k`hUB2B*;_Nb(s1x)MJviPO~yz^M~Ud3Wl> zXOsAJN!-?sUoSP6N8ci!nkx6rYNp)6&uec{&qu?x%^u^PW6V^GWwS>G$=0_h&4^sSP{UVmSv!*c zSX$@{-sp&HxzR4)!fG1T2fSGO9tS>MZaJ0jI@+dLHARV%s$>g^@G{i*-bwfwkBipo zU-B9LtYtYILpjL&ZZdj5rmp1XJ$OixJRSaI!&9!40B9b8souWyigS3P?8TIAsN=+G z#6Q%rD@|RaBRzLm$a0rs^0(wUk{|%iGKt1XA~jOVwyH}?HRl+{8(8Q?jBq=J9`ybT zdQZ#m6sFL!eL8r;K&T6hySn2X55KFEvJcev#C z&ig(xN-24o@jVAUsonB;!}l=!MpZ)!*+tUnBD04~W`8r8t>n$tU!?qJUqiT$*5`a6vyq;V0xj^#EvBKKamAs>nx|rK4HJe|K2&IlOHD7Rn50u`zx#>iG zt=`M5EnCxN)p2{bRY&B8RPRBV+R{ogML&gHXw!SpNv=}4WYe4(Q%(s?QHOPQLD#lb zW!_gU&FL{UNJ$VNP7 zy}M)_(l5S7Jtk@NIkQYqR0o;o11YUrh^x_Z`Itlw2YkEUAoQD5vKzn&RPCZccYtdM zQj$!H-um5XfTo)z#Id0t{s~V#L`8s zbbkHL8zR{ON6{u`7puda`3)oT9ha@H9Fy<3fLEyQIrmBL8xU7&1XdaNN@v=*TQ>n zAUg#Iy{;_ex?QQG)scW23AMjVw;! zv^0U!b4Dy*_@{zTviUo6KnEh8jvJJ`dAX~hBD{9i> z5SsE8*La4rS76W{B z&eG=c=4u;~a&vXoCWK}ml3P1<;n^+EJ9&<&rSyxKvWX1ZZ*{!rxw7U7?w7QK7wOIWMhIjwUc|fi0eYxNJi4!c&WppoayiQZ z5$i-0`1|zIThm0GO0lV(^phErG+&u62d7HNGSYNyO?Q3LT)7WS#kOV{e=;G%X|-FH zUro}yWn*J-SnkWLoE@B4@0loq;wM@IrWCBZeuxfh~X;ho|t(-^u zFuXN)G^rPO#~qM*ntNu>-bUY9IiGGyQ)_wk)eYl_OLaew;sm`;t(3sVpf7h*%hn4H z`lCTdKaICyUBH!UA>Zq4{_Sk;RyXjRyNf7UD`s4&PQ+u(cJpDJeE5P7r*6s$`qqwC zzrw{$kj}8x4{f-hjq-Y5>w#mC&f0lzIqU4X`KY3629@zQt+%=Q)K{MKoz;R9r4UgZy^^n&^Qub?NtRo5A4ypMif;bF3*)p9 zIviDEr@E=~RU`vuWfkNdRde};NWyKz-7iBFTf5&PjMv%2rBA%Vi~1xYAhz{m3e|_O+@9M} zhp2;?803Dw<_gt-cNLu!dODr<4>gx##sj(v!3hH_`sFH*X`X7kFH+?}ycnY%ejC=N zo3PrBt38GQtzQ;imZApel%WN2X`XI1_)19uj={>`}I5GT8$1#|Y@tWUr3 zljBq>lnh(}2arIyo4{^dvN8Az_2o8D8{F=1_=Wn2$Dm{Vj{gri*Zo4RlH79txls=b z{^}e&H0Es@F;O@{E}x2`)!Cvj$@Vpjo)+AWE``U&;OJ@YFA$^9pHzEV)Wbxi9ni82 z(fq%^dLuyX_^Ul$_S~{CXV%#gHjm5(hERVqn`0P*^9r%|SN9~+U1ZXUZ6xz0>7)nF zl}_r`%0^LEr=gL>R#Vin7F;9Ppa{ALx(D4oE{&ThHS6vowol9wSw<78VhPR5NTFs! zr4&9AoUc+VdD%aVMU{y>UA7*E!X#8i9tjEi@3C^m#V*aFaX0zjh^j-93^+!h3J+e(pEISVSLd@%lKkvuZHpYL&q1FG!;2xj#Kaz(BR{n4mvviFiVMLyhQwj z9CfAf+wzynu}l(C6}6pJ*?DVtZ;V4TvqT0GhoBu-h{MFaZ1_ZBhB9i^WXE~CGQ#ac zwjn>Fpg!mxuxdlw>J4qYWV+Ib;pj&OYc{kkb)?G!({T25!kQ8#O1-B|FOBbw{}I_N zELf6~5&=YV11#ebZL@GM%_SjnVHxjfSa5o@$i!Nt%wNsqRT}yjM%e7v)YD9JRE_-#htqQ5y_#2q_ zKPz$UI*fSB)M1FJL$vTTsUcN3aBs0IBLaN+j=hn>WIdvV@tU~*?Hf7XSAwW5H6dcX zHNRko&(TVykf!AuKB1Dqiae-*_Kl$X`|PCT=*37K9J$)D)>vSDKW z_WYswycPM-@0^?{r*dI(P8j!n#JXU6Rd#jup6cvW?{142uecY=`oi4Slg|3J{D!1`y9u$gkSPG zTutgFlvA#6$d_5olRU$ns_o$0dAZ;_77N$vGEgWO zL94pMyvg~oj4rguXmZSHWQMyHYSb^UIyf$IAI`RBq&R!DC>C(=3YdWv3GE2N#YsEH z>Kq3rroD88ThvppTPzi945SDtv&V+LL~Fx|E{-lW45xFqZ}o>uaqthU?P?IBKDdFKDyE`+zFER|>4ju84(q$5h#gk`s=spFV;7 z84BgdtI3gdlR1Lh<~wAcOtozI+_mAj-^vUqyqn@rJYJXB+7xbeo>kA8Z89HUH%bC) z-L5=B_DRUyCd9*WJGHU}?8A$RkrCF(3ZbOhJaJcF>*go``1(7S@U_;>_Kv;z`!m_! zK?FArj5+3QUoDR9_chmgWL}u6*6WxaV(yE_+*j+7n7T%*sYv%~tiz6MT^1WiCgaJ` zNI7!K-H>K1O=_^@bH~wPI-Ewn8cu_Of7vU`JnyID{tb)^M{gJb)*~#m?G?wDRI&%;k+eW%9yPS6*o(E%#b6pm=D>*cc3G%?1pzDKa` zHX%u@Pm@;2Fv-Art6;rVV_o*NUN5~^W4$$w%hm+e_XyVaXsquh=2<$XV0}+K=AH!B zsnOj6TsPub*A43tgLTulGWCQ)*m<`OcGd-?PIj;%=Iy6D*!1olY%ciM9c*P+2aC5Z z-As3|>t0KAFzIvgZr1Y$-RwbGl2hqK29;;$b?auv??SlE_4{}?lXE?ya_D%XwW+`K zxNJGxgi|v1NXzdPj=5X6y_+7#R^fwSD#m7Lb$Ad(;R!D1M|<3dy6ry%lBcyvUU+9y zc&Bdr-O|#-(WTJiJi5o}KA2}hhDwj~5Tk4)F)~at+J3jR=x*Jjt+eQ5p$yuP5Ahb= zZCX_N<}lqi*|#r!^E@4M=n;K$sOfRk=w#jYUDa6Et?hSq^|)cpHA78bilZaR0TZop zbiX@x3d11PFq*nszhf=!*e!8X50qAbUR!k2(gXj^!%w$DR}Va$xIgHD>(7_Kcq`~0 zc%KPLw!&_q=k7g_R-gyM&7=puFKwh-p{oaKZUzNOE6@XJ1?hoL>6p?AT|F?~3e+e| zfa^v~>$MJgBeOXuNvz^LBL&!i*pgz{vz2j`MNwSvL&D&417TGHC zikNY|D5E%2*`r5s(PDHP#Wj0rtJmzMEx!8OeH6!(Avua)EGkX)7?J6n(f*nf$dJQ{ z%w!6j`(zZSRl1MjW-K{^yTDkIJcUvGrh##u@;JJ?W)5BJ>Wn`=irw!DRN9xmr8$hv))KIK&gP}stnqs>~>aOwkb3o`0z`Aa97$1MlNcodJHd#9; z`z!#}qh(JXex!D(-oYG&+SOHWb4xwUs3(=vgXLk}`ti!?ag@rnO69t`<5kjmkJTMD z+jvZ}?S|d%l{1~pvi6B*>1NN$FxT6NZiQampY(W#<{&MFS|pToXYnFyBgpYh>Q`+n zt@fp=5QkVBwLpE?z)vMuc-zis_<7`uEluI)UDsjRK<>@qEwS)hm>Immq+iI~xvp zr7>Ka;-Z8)$|2pErjT0kL>qH#ZVErk+WK7ZUYF^5fsIa*lIvB6T~c4TJ^X5udQ$3? z>b;U!tM0-OKULqaj5g3W)_jdYu z61NA>)VM`$pvEY%ZxE@RV+A;5+$8&VUmZd3Y)?qGy4A9MhpdQ%awOvQ;M+A0Qw0oB z4t)i@nUM|c>T8~u@oZ?vpNwW+d5{lUP>eSTWHz)42;=MEY73D8aua$QSf__^MFOnK zui@+4vqk-bfLa^v9N}P@NP1#igdqHSCdD+-r=H%x?99WO#Cjeaed_TSNHQ7hQ<>#9 zsT+86ekR<3t;N|Q((jI_%Lz0Uf4E!4U(V5>OvL*z6qTqbW4Qsb@6+MEDTww~asUZW zubUeF%Dgs(ce~sYw#~ZkdPBj#2ltE{-qD;M1v$ygNTa}eXQ#B1oqdk4d#~4Y7H!+#dh-Bc6=-rNH zL^ViU>?{0+^bWcmrY)w9B}orrh1vQk)6oI~yrqBxG)?M4!BeVt1v}hMJ{quQHIT0) zaUT=1mP<5b#=z>9Q;&qKM^v?tpZA8&sIvaI15>;tgyPMMO2ld9)iGc{e9+aeF?`Ud z2{Rwr%^cx225ixgH0$LG z^NUtmxT7k4Nrs%1=E`*>k#@4H_YK!O=*)yl^gEXAz5I)!omFig`HOTUwhT&WEMU7IcgE~mj zA4sjV)pt8*&ZRgp(9E*-h1}TDe~h~Ph2#~@@=fY*D$}cbW!&CZO|5U%F>7+f_YoDS z!5r(4E*{u^bW}|cIZVjWH5saFvd?*DV{MLD`k&jWK};7AbJKaYqD@sPrbcIGQ=`;c zx3|Y(?7n+AUHW?l6?c2RSX04V*-$<%)()+ckdxH7)WrcYGcEO=5m}Fm@OQirk_PFkn1I9kS)E$IRKZ|lowNfXy5~< z#EsxXMs2qD;X~2^IoJGZQixBa$p|p*z5^-iE@%8eZfwtgYMuSzE9r*lg(Qk}#V<#Z zyxV8Lq>;Wa+IC|?!1=X0HEvU8G*^y>gP2CF*H+_Xqv>q-^E4ufQb>etq-d^DZQnwVU(DFU@rPv0yJsbY28C2~9z^efev%Bt0>|CTJ zhRt~D-%9FqQm^}-k%ciuY9MSqRb8$lI(JCOQ{k3%Qxz#Mb!uP)DSlEdSu-C>ABP z0N@Do6+LW{ZHXLpkx+M7rlChdd?%c<&}47vTZbkwAevz(9j%;nbvOGOPatdRQ7tnE*+sPN0*gh| zvFwL#+1h@H{fXiSv$-;7rX4X&#!AVT~<%syjN_<1_@)9X0c>-2iOrqihTL$fCr zCj@|}-4sfg769~E{n$uovd%-H4Iaw(F4j_n_;C>jahj&rNi*7ghf*dB$5c;KHjYC# zP@PnjHH-|IB(WR6=VjQmU1rI*{EFYzmZ$kW%PJvYf*ui5YrtMiO_GE*_3r8N@}v&B zN`g{U9YKf~;#cl46yaz!&&a|Q!Qq*smH2CnsdF^|{R{xhN#yvKiMhEe=BqlUo0yKM z`B)`H7Kfx4;p~|)A6D{#rq!9&*J?=4BU34oTBQ%M46~eL8D$w~x!O`l#QSx`)h1#u zB6`oqPI&LLl*CA`@2xTQy}Z+9dI|&)9Q4V33;G$k*TH_|{=5MI)|Z6rTEe~Wbx1|v zE0o{oc*}W~pILrxxz)uwf?bVV%i9lgWoj!Hzc~DqFIFGNQT> zAJFmJMOsKzHxl3R7~yam&!m=cYe?oQL*>ke6G3jdNH$h=sqWs@wmMF=RLe+T9!b$} z*h=r|=+tO()^NSRwVmrzu5`Ql?0|K~~GDT%))qaoxzZlTOHJkJQrwR0AD z=GdbkiB@5=vTb%qdcixTN!l^614cWZX8y8Z~sg`l(eagc5 zi!DDF43sVc%K4T{=gpZv|BQI-Su-ojlh`m}y7=O5$hu;_=ZM#en~G=6r1W{y7tSuN zSYR?=P&`GDd{gPXX|v|sY$*g6vx_Shd*;k7t7oS&NnIdolu}TshH-8&n4eWTjrJ8hNOdh&%$ij?y?B;qerajBWqfJz)HCK( z%vx;nF)i5zi&S>LW$e5;<-jy|=G;wW3rJi_1(KPPZ^fL7Gv@1X8g2f9 znH8qEc=LLaEqV>)Ry@75i_m=Y<}8>~GG~_O=6TSaXTjpRr5dC0%&wcO!LFb~%wITn z?wol--cuF}s5+S?nf{04KZ*%q#cIobQ6cB2;v@4NLp=(iU zy7-iZ)25Zq^UT+DTYOXT%vrkJIC_CDQLT8%blR?Og1D}5Q{(xi71VMXyef`v&-@t+ z7w9@BJKVfd$}Fvbt&Id|!PES?v!thVgLJ{n+0J9^)5&KPS4^cblL==fp~msm9APx{&Ef^@ z*(q!n^$PYDd&Ib8 zceOpIx4t>K?WxY{o6%5hXP09iHgx)KnT+2QRCg>iwy33K5{}4HWOyfQTcTKvH1{U= zLx^Q4C_RWKRyCFvnNvj7hz|&6+N2>UAh9ExgM)}A`T(n%KuDA7NhBBB0Lf10^`zvb zrT3NcBy%#Ip1G0CFR`6|?|q)w|AEF+{%R`=lJm!tlVNk>VR3uM|FHrDR`mLh*e$ti zV$F1HODJsteLc0k$V*dM{z z$<|wGDN46S6?}fC!L|_-F?jW2u>JD)7F%$w}^Bv)uvYTi}IBFl=rjx>}2NcP_hOgCmHma33AuFlR?*`y*LsZ$vj_5#5Ro~0KdbG;YVWM zl_)MNnfKpJ?Cw=q`GX7+n8jnq56f#q3-Tl79rwf7nI@5gU%DxPDi zI$(m6CCt>CW@x>OE@zpb)4TPp!)OF_ExPFOkss6|(Kbn*(aU6X_E9_|&!mKvh$cEo z;Ddiqu`WE^k|@6m$}$twg@>%QPt?Is-JxdLGk+!wM+P)ncpU3(%(^-M5q3wv`yAaH z$4&n7y$)vm0e;o>{9v`&Lz|2X_YqRfGy~c=UQtHYr`F{8RLUTq>>;Vir9DE3PY(F`-4h`45Y6{O*ec*U7d*AG)$em&u$RyP@I*T#BvLgZp-bhQ>~T%y?lTyL>ZpX#QcmtI7$_%PR@!8hxN_+< z+im)+)4WsoR$c}`+hDa3pYdZ)jj00dS2-Cl|{v92i zRYM}FRUSwE&@A=s_Kwc*t5hu<#XKOqb5WVK$kFSaB905D6H? z*~3V^KT9$+>w}lcs`}QtDtCBwT~%KAK}XN+;nfY5qsR8Kgdc1SKd7JFUo?hS*W`il zU$UUilQV-)HJ)4*Nvrb6N|q`E6q_^0QQ4?|DUU99qblOzMAzPkx`qe9W=oj2M<)wR zuyFp1w}&d0jWqo()m7cesL!s^+!q@sif`C4osCt6FJA53j|P5XA|g&iAme59QoJq5 zONx43sNEM{1@cSkJwen@&l6hN6S}JMI4^|Mx;2Pa@6rbLNCs_?UNxjCyanQ$+Yw2b zDKTjybv9wn7bvKVOwLo$bzh-t#{c$VU zc+HX6B{F#l?{e>X7_4C=_`lY@HAtL>3)%O`>} zySmJHgLKUkF0@Cu5SDt`dTCoH*(6C~VLY#NW(p~x(Y~8fSex#)){__Mtya9-(aAS{ z6E)WqA2p}jr})aXc!j>;1P!zA*>x$S!X0L>%ZvqcD*oU0gD1z+cTuH6 zf9G`@PwPz3iQUH2unEe>!CgXpT_ZYJLAZFj0*TrURAad;mnODpv#sd|qKqtWoegG$ zGn2YYk_otM_1R_qnEEBdZL=H&DnwNQR|dQ$IY5G4W)6vi)Dm?34g2@wrG=%9Z%MSV z^bie*PLB6^T@rpy^eTP5J-AeJLCN_EK56)mPE*fr^*D6#$SEpJST}E1@!apyf`P6_ zeHOe>>h_Ys)ZR!{7Rq@GuH7&m>Mi8iT$L>`Ud*8m@yJEGDAz0XIq`tYj#GAldO|X*DPLZaPyoIXX@NZabed=TOq&R|`yFl}L5rWP!DI)8CfV=e?cev20&-Rzns>`tx!n-2tlK2{~vt6$;vuKHFn3X=ns<%%x75fgU3Hp1}`zK4&V>XPRH8T5X$R9(l>xQ`>w3)N7Ao?-!RFJAh8w@!d zbE9dYtsZ9aaI zq_bDFF_x=I6V=-y=AW`wN~6~q0!>q|NegxJg`sKj{glQkk3o#^gvP32`end>>yb#h zk;zaW0mJf&+5|dS)JFFRiAEI{ei4B|L`15aWuwbhHB5&Nex{~Zx_0zx-{Gq9hwrICeW^H z{JEijcm?sY^vgHqC0oCIY+iD7jiuW}b-)b$af^OlZbTyJ>b=keH1G6w9*&0!t=4?uY1qcI-zTGy**CS zFCk};Q=a#^!y4S&ZlL$-0v*&k*cx!o!dMY3a@;&6=)??uTv zcrWQNeP(Q;&rCJorF#1vPIj9=nzVa`K0i&e@{u~GYrAf-NRkJgXo6o!jv-DH@I-P9 z5nrx=@>eaM3y1jxbsYhJ0+oitG!f8kG?SgPDOL8Sa;GoevtKrvmoZ5YKQ?)eNJ96Z zi7_|{*K!lkI|U{-ZIK>anOr@& zvdV4R#bPqGjB3tCUpi;ov5}IsVaKLOIuSF(5o2-;XKtm3-2oE)^hkCY0aZ4~8I8US z*QiEc57*CZcv@h{F0)bBYVrRXVib2ENaN3jx8=O)aW>ugW~4eB)KuqG*&VOzXGcS% zI!}T;1hG8dUkzYxE`PO^H^gNS{C_3G?ns7xgV~6QSDcVl;xcPe5<{JEgf8Vm{Z9RD zA)1E0UCCZxlWm#$Rjh|nCJST6{WG$cR&a?>rsohLA-;G*V&Y@FdmfSy_WQ+A=@Qah zxyEBJ!Q&tq7lxDVStyybLMlG=nbKwcXM|flPu+1KVO1+@2XWc(Sh6N0^2xX zJC@$;r@(wmi=H@H63ad|31~ZDP@Zg$+hw#*oF0-DG`LlklpvDo#sF?yL2fZ*(}hz6 z_|(a9(Gh|5Z8_kesiAA$WA{0R9g#q9LX=bvm<4=2(HN*;SM)jyYKlM{0R9k5~#HkRvS}~-zO8WPgiSg)b)(un!PlW?y>xmmE!pNg0Ti!XG&AA=NzD^j0qffWUhf-^E;84t{ zw9rb9LgKx&zUy5YNi=1e>?rzmqqVyRd-s~G0Y8_Mw894s0$gLIX2Utq2-`}TJiQJd z6E`wJsupx|q|=^zrD|EGYPTc5n7snI)KD~5t48C-xLu<(eWZIo&$962UBupBal|ZEoEbJbvA2n zoHhWCgk|Vh4YukWw}?;8FNrvFJnUDfy3R&aJvmOs%6%W(WTL#L5&2S<YVDV zKw~g~o2n_D_=sT-dZ0STTi%)E4J;HjRn=CKXSU3Wz zY#Yv1g=q(KtT<3t-;s+uJEFek9&X=^M{PWk_o`RBc;^K*Z+ss^RL{ro;;q?|xaT0N zNNBXWgqV(u`e3LWC*R+O4}L3Unbq!%?C}V$k(qa;0)DnoKV6jqNG?19+|3dX^|}n^ zhh*fd!*S2kljGl(nQy1^ZB3PxG6&_7qQ-=}2#s8&wqS5r771RZUg1Ibo!-+Z<#M=- zbW!o7W)leTEWZKQi3SE|ukGP|(mOZ{B?#Y?!%85jEr#IgQ-gvYN>r_6J1DIoGvw$= zBfi5MvyYJMmYf0~JH9?e#}qej|BA;9>X_?IOz}_?8gK@`!GWjHJa*q3VBHwI+c>2{Y(oYyc8t%s5s0ZEqqu893VIz z^$rS3S+_hv9xFK!SHd5b7x8KK6tfh4HtoN8Ag%C=DJ64$Y6;ykTJvZCmpz(*A*4$K!=+s4ZeI<@{^?et-13_o*(_pGzeIrqHtFSu~zMWZhM*(IaLjJ#5 zZvV}1e|N|4|M15o9;{^v8#KKHMGKmWpuFV${%`IWl*hK;XodTsNT*WY;at*zVMjx@gWZnSCp zj-Btl|G|gNEwR>JAN^mSW})Lz*5sIAcLY8;>G>b+w8Gq&xI+M*@m{wePzwivZHkB6wj z&=IySY5y4=5(2}LO(g#;`~NQMvzNl2k6l0htp2-sa$0d;jP zz+y*bVJK_er6^(r25jugGErO&l4RcR_nhC%1PCnqJiE{P{@?#|;pX%E{Z2pk+;h)8 z_s+SK_XMSxg!bS!l)-2pUYT*lr4p_2I{d;!H?%$1PG<5uCTFI#$FCOC+FAFcrN%kD zdnR9d*pl|;gLk*tvgd}(g;N(!!B+uRrrK6!KNj?odPzN_|5D<=V?pbv2OY-WO+h=( z`sef4m}@M5L`d$9{{>qDT7{O+^kLjr1+B4k3J$e?9bD@*7DHe7_YtcJ-^Q{k^>*C6 z*EGa`W_fp|9p}rm<+jxLcrRv$-Ii%qwC+92(}t?l>3b$;?n#>*-`NsqwWc`+bkltI zbV}P}O<(QVhaMse-%st3wkH@W-{QzR95O9*wdZ|`yV9rZo9qhC+_OB@la`)3c~xTJ z?LVj~X+L-uPIf8{FX+GB|+PCc0#UUFr4$%Zgn_=d3XlD{s$*oF_d!8yDn>Z0c@>g4k9lIWoQ zS%bq%Jby(b*ZO&@V zuS&ZeX9C-E)R!0wwYA32td9(f&#zw>DCiZ-|_XIM#U5aTc$6 zB5w5)ap|k9OHl1A@12tWK>pwHD(&y57@lRi7pMvv}=m+iX?b zlBk%ysZ)^n6fE$DOUL?JUO4F5E`$d!H6MqeZJ4{+wBWCrPqVJ-yJQ#LqVPNyfwB=O z+wUz9QMQgGJGEW=)%K@MlL*pf-5Gtrr> zQEfxYGczAr@9~6&Wu~ou(wVk%SX%pLtGDk|&{n6ty>cg7Vp`m-YEEiq+o1hvd(zg| z?n+DBo7S;ic!B3I95c5^rn#D@UF^9IN@Z$BW@_fi)aDx^Ys2tIMQsSK^t=QYrR`xG zB2k@dJddHi_O2*t7wlSJi`tE9-+WG{Y9G8bazlg-eh>=nX+uOath$HFh}wXK`&C(3 z>b{Ic`ip?+AE!l!A{$3S($wLMG;2YAsw*u0M6-+^zX?feQ<#dWunuXhLtP#&N;?@I zxAjO%Kb-DKL_R{ddp6Me=qS&0s2R;{qnJH>dM(MY!x#*Aux*)O!)OpUb=SUkS@2e+ z9hw4N(SKyiv0qG1@BX6a`=>+4UHe(9Hh=MM zeYk$}Yh8N}3;zq_XmVTo?$F!5{_?)xDp&S~XbPgz`Sn^%_j%l?|* zR_x1b%a1)aJ7(sIN zvRTY3*8)b|@8eFClQ9Q^^;jC;zJuzy4WVT=XnqiG6SjHEVM+Cr;qO7u+&hrtJ)RsU z5f!p$+TD%v`-AUh<~?K@F@28G>FJ9Id_xccT1Oz!YBXF#&t(!W)seJoluKS*kxNrB z??x2th|J=B>^TOtb(e#lpKix(D^~4g6-5tYT|5`rrWt3&X1UrBjPM^LY1!yRE6&kMb6dw;i@sxK+c_ z71>xfeZ5zfuZu-Nk!h?$_cy$^5r&Ug$G^h2Wn_)L4LJB_jlH9?#$GMg*gML*Z%)3A z_pr>E5WzBgV|b%-IP`_c`xp@tdn*_V4sLsaYkK3JKf3w7>w?1(|4i?WX)gpL7YC+E zCf4dEHWjf1$5(pII(@ZzoxY#APG84IJi~7@i%bmF`Dt^}5hSDNKWc?8WR#6@vbgOo zrWsSDzQ~hI%n^(&%YEedSbNAH%gL4z2(7z{;@75WBfOZwp^cm|X;KxbghM7jB@52X{q! z+Z;?CC-8F{8@wza?rXONUw7@JFIX2n`a%$P%c4hTVb$VSaj*_~u3e8)D8w`NSuegX zJrN26U}d>m&H3P8vzRlB)9}m*i!g`b9Oe`QzAuS$$F>qT26ZL9sZi)o9YZH3e;Iu^+&(F^tx?uEi^m_p ze;Pded0job9y5J0B3kGVROOFop5K;tdPL7Oibz-G?SY7r{egP>BTDhxqECQ!wu#o=tKCDCn|KCURGJZc*SpZ-i+6d_^pcueI7mtKdqbp+!uJ@FBW)@U?`jw z^JidE6291@>or+8)vON?4$p(SKfz?0eqf-lIJ;IdT3pF_m^kat!}NeY0r?!jf!j}4 z5RR_){Dkpbt38jGV_M<&Joq%v%i)=knX{RZMbjONdzi>FOk_XN&lrCkKOHj(;*4L9 zwFx-+c@f&-Ye9IDa4DWxg+Li^f#S8Y2gYkFRwzrL@NJ$?th`y`)1Rvj1p9|u!x1Oe zJqd1|!{?IgkMiSq1*c5zK3~ih$=uzd;Rw&ibvKq)8D{Ebdw)> zB1FIIf|nrSFFsqRv436gBe__>k>3%=PY@1y=1CmEo;MJOYkiGbd_er6g8H#BX|Gv+ zuPc0BtN~Q{C?8T@M9N0x=}ogR>~4big&B0E0gjc^m*c5 z@AOr>^)&Y^asS@${?*?ahaV*FpZMK(oaTOsxbO74KPB$*-LY;BC#k<&ZnC(}@5MyKf%`eSYMA@%vQdfsBJxG6b? zxtNba@wvUv1MRr|j!!m6V5E<=P4No(?HOjgN*eiaNeGwOQ+| zL!Nz$%%KhP^gxyx*WtxD=S*vI`TYdLPyBMMnibmmvZ(EQQ(K_LUR>Wc6e}FG4v%^f zYaGC^yy1|isM;6p-lGVTfgoXR?rrxr-tK69IBMzM)K>7=p`qPoJS}l3Jh8F0Z3nRc zV(V@C1g=4%@vRXKA83Pd9lHed8LdzN-7c1ys^3L#+w0+lHI8`OOh6BF3&ej)r!__=WZ8TtXyO?Xx>zK*m) z8~I|(GrClJpkrOE-(e($ZyR7daL^MCg-`dx+T7i4KkH}zLC=j6tVNrL+qUKM5lh>; z*tNceFIb3E%hnqXda^}XS;so!%6M)0B>>hav~q7-Og5QHU3}0p+UJ=SimIHA#E1KA z4JODSFVkDe`h<^b-~+30=XWAW%vF8ns6LgdPnqg7Q}vmy`sCwp$voAkLiH(8 z$>Y*f$0trznf!hGP+M$8xSF3o6F&*IU2E%8V7s1}fuCgOmzEdI;93USf?q-B&2=;~ z6xps1Zw{}OSW#iFqoN;w=Ui1%RB62D8gUad%6O+6Gcsd@3W=n|s55Od`P*il z;9P?=jd?SR@T2o0{JJ`Cfi_Gp&dZkoAtUbX6BFggc8}#zh(phR&Nm1dKQLK!O&XjuVBl4%>yWtqDFcS6u5ob#lWC{FQ*v%$u8EA)$D=WSqPJ4U^#gT&JxvM-(> z$Xz9##&))=?h;R9JKkSZy(OO1M)BZDr|K{9G`3>^rv^zpjqMmX>N0TEjQp9U_&I#v za5c5GiocM@kNvSu0e)|8OHIs7PqAS=hj4Xu6^?lxR+Y%_+ZSFlW@2rN%9;4xJ&!gp zFR!GsBD}IxFB4*ei>;8qhsQb@<$3e^h9{TW79jRKtO(&qXd-xlBOgDdcU0(*3=*!A z@z(?>K!WGiux5orM_z%Z5YAMX7pw`v6$`K)LW0U-!xXKeFOE2xWk3zb2^_A*Rd%mH zNh=vsxecwV1c_d7p)2!dIc(Dz?M(dUzZk7lpK$(i7h{EsnMJdkDmj)XKvh@hS~ZIr z=yX=9g4y-Y>l@UiI7x~FnIitcDIc?fR~eq~-c6HCKl7?2!nMi*RX6^L9n z#(b`B@-G}nrd`IoAhjgD4h^HiavJ|MpZ}Wgf1xBOe4r?%h8nkn^BjJuNGu8CFAYvA ze{Gr^H@1u}E-EYIv7Cnw+?3AKE#-o9DiP_t@_74e{hZx~oIH|EN2UK@GYcjPWlPDc zSYX3?IV8G0-IbuD$C@>|&9Hr-n^2?Y6)0w(Iu|;@l~kqcM$15rHun92kwU=Xp~SB~ z_|3`tT8>NnyjABXx6gDX8aJo#T<&Yd-52gmmL-{ZeD*VS`>501eg0w#r01KNSK(hR zM0Zde24Lxu*+r-@(>b2ki|H``yIyQrW5;Er*pzM5_{5}ZY$nhAo2A$4G1n1{l(O=0 zI&&E$UPSUsXO~rBc^BP~(3y0{dOeOxuJ^*e8XcSN%W#F#9`x0$6hop4TaVa+-nQ6+ zo`~=C=uIDMuJPyx!&Phru4=_t=LTI5dvECI9)01@9q(Ajg5LVNQ(4M2wTdc@remfu zy{e#K0Z$vf<_t=K>nawtDstrG(#bBWj4R3Hp&Yo3;sPYANHIJ@HAlj5EYQ~x^oja{ z=F3!X8J=KNm^9PD?#WNA98*}1p&X;Bu+1nho$W6Q&k|n@;Z2Toa4_5$snoPmil2Wt zbElo|*qvUlg}YC`Ao}91OHc}rligl#C?-Y`>DimBVwd%_-Rre7%)e`A+P?j?)I*#~ z9j^{j4b>K;6^uQ|>Gd{u)xm$AHws}|V7DUQ3E0VM+|P&Bz2mKJ*yauWb(^<(m&+Sk z?DAIkaC<{h$ZG7ZR1H`otOaYK*MSIsTKZ)jl=AI1jP`P|dH{cfUegtOXB-pmkoDeO zslnKX_roByH=f)LS88O!>3ajBKhKV`;DDa7IiX{BI-O=;>{3qbY8yH#>_I#7Y#}@H z;(~YNMMmt%iw=8dPFVEY@ezaHj&B*aYe-1ht|6hh$A?+n$KSHDu3Ts*+?_an+!cyU zqV9P7&rTo7qmdy4Yfu+9l+CVGkkjI{C@Z88dv08qEjKO_F1gt4k?tc^S3iB{)mce)+N;qBRI{H!AkG5dDvwA(Op#_mk0 zvvBw67euyy#uE{Tr}S>+wm8q*{_Sa32l;jv+rPz$3(vo2ccb2Icz#K$##;#DXZs&& zM{T=;ksfQlxhq12=0>O}glU1@iV}=O?QkLKMbQgYaKsq3FeFh0|Cp#29<;0AV!K+{ zBUc40n_7rH4C@HZSCL>O7!8Jj5%}9w`%lH46VCzL8SOu^GwKg}7}|eW7wnf~?}l|G z(DrxszzYk*l==~F+mS>6o{L=C&$*vfeud3x_9okZBHDiv+J6$-e-he%659V5wExj) z|D)0VC!+mN>=~MS;}uqSuNSQDVaKg@95t6hE!M5UUl%mHa@HSGHA)}<)9XKSl?sV` zQFUp3SarK^jk1@oO$}~oPqha*=e%oC6`v|K`ct(3wf}+k&wEa^Ze@|(3UUvU9XRin zWbVcbY^l7)Rp7#>@6In+HxnExJS5&`%f@vLlO3|aUqwh%D9m6lcXwvDsh$Xixi2FtM+AnyNgnGM%>4~_iltNS=iXkeW{+z8obauG>$MaIwr zZw!a4a@E{XQi$~|XX2uqS6t|rUY=K!@4KwyzO|^pVVgJ8!K*mZzyJdOC4`P%uK#)E zxS{4e&Y5}{TsT~%n_jYWDDEUVb%e$8nzHmjCVJu)GG)MEvhpRZ-rrDt~&v@`{;C?`od%jV3Dc7I0vs%uQVQS@tm# zGi~OG$?Ym?1ozyy(L2jQ&*@;)l^xa2V(JRlsFI3o%U8qaa^cL?gAVM`{@D@sOFpS7+2v74BP*5!@?q z?uX~X<}&^@+q;lIz`hInUhE%X{}}tH*uTJj5W5@u5$s=M{}%gE?7C6=oAnsp{-){4 zJGSieI?7pLJHM}aDq9XfpFX-JH8x3T7-*ytdwR{2}G z3YV-+bj-6c6mpB~Hf{9zp%2EU|774!W2k8ioW<;IGjn=oM)NDc}SeQmLQ;s;jP zlwwXA8?;{0gsER_MQlMI{y_`50s*CgA_na=UkD!+%F(y1uR6`wq&|~i&!4Gud)Cca z!Pdx{9}jE(U{z5%8=VDPHa=p?X=2r5a!;IrGjY2z8#^-RX9Zhbqy9?GDOYn!)ST&R zj>7EebWEVCS>-BKrC|;1QCMI68u*`$g?dZ#eT!Xch3m`4a;eiwM}@+IpXc`w+qZw5 zECF1Cm1N~~K}C$=7 z+{lUKwm`xS9Bz#$)gfJ%kv|i z-k6$d{DD;Ms`qxRfdpf{mqGsnQZ;T_bcI8gMy@yWs zKUUr*YSPXmeI>8=m;8TF3gp1Fc4i3ov1n)PANhPJFv@Q{*5|0-v~2WZ+OH1hqJP|m z;oe&dxUo|GjG*#oemzD-%u4;S;+}JheEcrU!0FXsB+8VLaZL z25%iR>tap*z#v-sj;=_{>)p+ib%(Sj0XBVz64;69Zm60M*tOKaXxI%^H{e`X44U9Q zg=i>k^`5YDMg_k}@&1xvq}^Ums7|c*KN@SScnptY@-t^Gueg>8w+q!S0*!ik|v#PZ8H*Whz+i+BQ5>FhU%v62iB3U ztrN8A+B!j-t|M(T5^cKE);0$9YJWyzSijBbw~2(XF2Bv~_a_p<>I~@kM1oCcMcYJz zO=m^hM1sv+NthRrPO~9j7rKS|&H?92Uo&n>Qht9b$af|=Tk%exn`;ebSoG$u)}0jO z8oyNQ_1S8@apJGe+gT*6)OeQUtt}Lrw|0z?oZi|=BE`5wa>clEj4dF!y|vZGc7CbG z@9#9pYr?Yf|1}ARP|`iWgu|cU%b!m|Gl?;_faLT~EH))6-vEVomYi$h7LWq24fVc} z<*&8A<5tdv7xfm90s%X*O(ef97LE6*HGDNs(n(e*GRQ7 zb@khH2Z)x9_XUv~#ETx2wZ#*~92?j;aSofaRZS~bwTgj169d{tZfUQAGrzjnwO-mH?d22dRp1}dh zbhd0^|5V~53HZa!TU~;S=W>muwz?t42`R458&m5|z(~pcoj26&{f_NQ-+XqP8FOG^{T4NNCOOHPvy^4JUTDs`)OVp(bWg-Jw=BC%kn( z#3Xpvw5mPjtv|75t9NTm?Md&+uT~!NCd6Rub;7fIx3`+ocOH)iQe8a;SKjXu7ADjU zuRG*zI57g!$hy_lbti{VTDT>l_T(^1^AoD;>V{P()HU2(?QLlIZFNFz!&^AK4PPvX zscm@K+feQOU|~${>enerhNYxFy zL(%9cVG!r?cKA*LY8PJosZ#NW$ZR1D?pB zHXz|0%tZ}eRtBWHIxQtYI!P%3$Efy{6~}T7q#7&QX|)Rt!0p}o%fVBo_wJa#d=6Wu z)3fQ9gTs!qjlWY5fA8>6ANL}vQc4Zh2Ct=>Qb=ueU3D%c+~o0zC$_h}p)PMer4ZV3 zwNzJKmtZ6|axJ~h__V*q@gX(ss!wknUqE6^|EY~#fZ5z-J}J)SlOmlyDVpcD-on)R zq=3Ib*g$-NG@6>ficmNPdJ9o4jewJJu#wz2WJcokRXpNJsIlSJ^K2>FA77Y$wnMK0 zDMFchLOu)fjZgwoh`Dz}h#-V9o0WHi+)x)XPIM||qXj*iQn{ptOAB;zo+%+9f^&<+ zZX`x)O2nkh;OAeL0Q4oHD?ZB67VR~G<@E4%(r$>lGR(_czt9TDlxUmJ~TC{SyG zn)KJRJ4T`Db6YsywHVz!wonX=I#58wxE4`z`~PTFJD7TB73n*AedNqB<6mC#ly znTll$JO$AWL%Q&-RY{@~49-#aON_pN|!`^720`1#&PS_0O4w|MTaseD~ifx&@2FZ7|>9 z)?1uksyS);LG}PRo%Ci?wPLjSyBwQxo3mNj*gNTMFwR3GA*AW4*=3l(?ivf*FSfXB z%o6Fiqq*R&j=JT1oP@fycXh+SuEZHd&kM1;5SLxIgIqY+F>*j8LF^K3aK+a+K{vuL zhh|ew{7p6zY?nhqYla_uo3yp=v_ImPaF_ zR)mbk!sfXyzO)v(BSgi;F^e^MXx5$3zqZOnIUYfAkR!Z46roVqR%)YFg+PMMW%^N5 zTkwW-_y?si_sT}4&BZ@X)}Wmd3(Nvw;~_z+*l07|H*HMJ*n+i08VyCR4PTOwktP4=cA`u>A|0 ze_qOYkMA6CE|s)Z!1hn(uO9J*$Ssqys5rY%igx*=FlU@(EyCGZq!4FUk%Ckgk*uLJ zM6xtXHj>lQY`jSB5o3+y!h?Jx+1(>jjg+gJl^MyVn%U1S#ra($8%d!q69R|9X(SZv zJQDo5j6W37er^fH&NZ$!rSP9yuCEC)AcAvq`CEX~-vSy*7GGPySOMp;-uyF4Xcp2w z8o37fXB$I8trB9zJ!eY+*PtM2Ta97~#O#-x{=7K-ImIO+M$&Nl8iWc+E@|8-I_kr0 zak)Mz`W++H#zfq1=C4}T+}Wa9p`0$O?(Q(CDO1XgSlqDH#tbfVdE;?-{-@`!a(&gO zY;y9UM1GqKXQ$avMS64l`UbVpKYta;`73?GZRE_8t}VB+2N|0k5(~}-0ZGT*q+~$| z?P{l_b7QyUhKOXf1-oGDfH-Wyc1Qz#Qp+yN9%2j54N{lE7HkW)1*xdvKIyVDWsk50 zqZ`Kjl|9TBjBeQKh7=bOiS%3+dvs)2G~P!|gM4y?Yny*sZU64jmnr??QTD7WCT_9O?pV1#u zknS@srIf9(g;PRv!rQu(C@5w3cdWVo&erzNBy`W1WZ~rmGcHz_e!Lmk5bCtJtI<^B zth@}n;3+p+VeA@>v&+7Swna$Pj;Sh4R#C#-0OkoGxuO|gG!CoN#D`|&Y(Q&pS5u-l zeW`G2LBZxS5<=LG#Q6MDu91+gn}f(e&tdHUmA@|CHW|_$>MyfqiKpPU@6 zLPKosgt(ofBWgPa$HnF5F3%5%K&y>*Z40yAYPCf~MyMstZ97zMSah%oy8Ed_WseRE zQ6V?IW>c=nFbp?FF2s0-Qgn25B&3KSeP~@v@Se5RrYsTLm)P*#f;^`+$gpiwmDQ=P zv%@t@1#MHk?UB(z3)QVjYB)DjgzDN(6gakGWW$iw+zB-D9?mUwNC4$?D9Z0!veg#j;GyA9n% z=U|0a=Tb)=t5~7fo}0~TFKSp2)J+gnYlZ2ORg{A88h3+>(yy3-cjizE!gx^G zDGgP%3Fb zcq8gRCpGv-3Vz9@=j@q7J`dvI;7z`-V`xg^)V%gakEl$&H6;b!5Nb*aj1T;h)61EE zZh>3{BsX$pTmv~hT{77U%(+oA8EmQ?|8Cpq>7JRF({pO-nKo0RK-mIPpyZIGUYveO z`D-m81=3)H)_0HyAVFM|s+pXdl0MW7U?wP88K$z#odW|0B<-ljbACxjtn(lea(I5p zl=Q5>oKZ5HPpYv(;w8|9XEt6-LkzR5C8TkN6w{z>{JJme$Q140Cz%Klu|)m9lC&mW z(f`NRhH33=cczW=WHl|D-9;0U)7WrUxQrCjxUHGfh$q*7M?%U?ZN45BQ?Pz#eQHyC z%r9;6OFUo9#xavf$eY;$7aacD&1dtSgUG-1?T{oiDl0+z)0LnlT?yKwF0hu^{h2;T z*RBq%YnOqg{vb*HL2{d##8Hr1Lo_-P-4B^V#O~W1B1RpMOi<)dB5ow9+Cf-eaa zR1$+EiJ|B9OMD<~HZAEV-WFzH8?EShycV79R*+0gkR+y7b%zp6wp-PCVvH11`%5c* z5%tE@o(dJI=A_jW0G=?mVa$-EwXwELQ7GxeboHBT(-x3SW~*TfNT#-EGj&9XDVQGL zFFBi#Tz*?qlH2bpwS|}ilGGb)0m)>AZA%WRhp8V(t+r6LHe_%Y|1yRV=MCYijAf7 zgbl>!Y9cT0Ch{Dx@qK04&VJX5qhYD=ggU&B$xac+02^|HIJlu^P9|{1h-c1YT6*h7^zmIhEPZev^9j* zQSwr1u)3<<-ns-YC9m6C4~f69KFq^=C#A2v*zWY=D)JqrcN$AhuZK4Mq7sEh(l2~@ zNjCqLPQSBbjJ^iOe!%bVd{VXFAEc(b=ca7Zp(pq|`G6FIreqRE8y5)&39XnLB$tuU zc+M|L2>1(x4M=CD5lAhN14#d+&86EMg9fCt-l2l)`E8hwkWHipj0zGI{wf@ux&$ZA zX(!J0x@s46?4fmNGhPe!m^z;Fp^y^r3J$sp6t}vr8i%44yNJ4K{E4B&&ca9(;9KJ~ ziQ&!0@c(hipITFSY0}1W=q9T2Clad^KQ!gfLE7aAQ66?=wc(qA3oO!YZ&LgfM zVYa5v1t}7an@BPKb{}YUfi@m!+32O6CSvy2z9jzNlYH&*UuUv^>*C*{{M%Ch=J%HD z_twSlEz0k0>Hqrc^1tc+wqZ(e~!rYoo?rG z5A+gvphaa#B17GpSc9#pzpb#{nt1l#|F8Mir7$JZV^(M4&bx;1UXs{tX>g)*aTorz zbgu2NdkN*RTWQB%?Z@~J^qP62O-1_G)hfaIQjLE2jHS$SxFn%oy2&igU{-OeT!3XG zxP$@z;P>J_$T9?6jLI?B!KDa&{{Ee&acAVR!cjgK%Q%|+A&<&eDb(MNg)|WdMO=}MLJ5%?C^Tp?*&weg4 z9-cMbE>8&IR^LO3;KW&oV2l$Wb(3^{bp+)-PLi8;jtM@}ahDq~kjU z`sVkZYm1NT8$ZA{cwoP{EBp2DZ;QWb=zzGPSM|5uScsK2if!F|wP46wOgsuy>f(7Am93oKhtSORY`tjQEACJoaJ1Mn;$#B zQM@`WTe_pX2+J&VJuTj(s`7I79jFt(Bl7B77Z{@I@Ulg9#NHSC1ndRaZ^nKf_UEu~ z!M+#!57=A2Vo}|(kHnr?G7AglmDu#D!-%-tOnIXs;!spN3LRM6H4jV4Ik?(hsX10= zy^4UDi}2;yvh-SySk~9)+n7^}=FiS6JIg$(sInwC_2}(z+HxhMl2Tg**Z4DiPx`tZCmXugb&HFeeb0|`zaH|* z%ia3_ylUIF!Gk(_cE%3xocg9}(R@!rzpxP;m-e``*VgCt#;WEvdc&T-+;w@*o2~k{ zuUOpw`e*Ndb-|Be%XV+tGg!5_?t{w3uf3IfQP^vR^}R+i*zV^{sI&jh2pxM@S?l!V zZ-u?Xe*Sh}i;)9ngc*CzQpdg%N1u3po!CFSb4kp+tp_5%6?aSX!RvFDm0o^G{B68z zx9hnfOJ??NZsHl%eA%Q^D{dP!b*Tv#vo!ve&)+nl&Jg_0>E|c!$yY|ldb7A^3am!4+Y2lflt^IaI_Bx4wb;RsFLoEAOza{DXW9hn@ zKW&@S{xeB0dg-f=rQP(~NMFf6>e{|d-(_Xi^lpZ6Pw)S-TkZkO+2+AJ=XaDId;pNZ(Q#=JpXCqZ{9PdbDV?s_KkbS#Ix_|o1JeaT=jU% zr%kwvVt4Pj_^XQ1A4&MQ%VWFDym#N6xtE)GMm_WFx}QGAH-#m>Z!S+Bm6g}I{Vx*l zzGu3`W_`GG?na6Ko@bUV4+(iGB;y&APK#%bd{zGpY< zh>nx~-nV|JuYW$3xHtW|t50mdc$8UpwBM*he?Ktc`C;QmnQ+rbeH`!j`SS;F9%cM3 z8};qbxsyU_9}@phjXHeYTTgY0{ey(tJnG9u-d_f$y(i&69`)(kr12B0k4QYnM}4&Y z&b5W>PDy;$q{1H^H*Gx@o@C;UOmZw)+HJh`;v|!wj!DzoKELbJ_Fa=qIz5wepZ@dw zg1CN3CZ7Y7ZWwsHOU>vm_9Z7Lmb6ReH?eRrz~Za=gW&ybJw`doXJJjJ1wD1YKA>OR z5Y`R-p(83Noqe{y0sV&b8_Z8`VdG>A?xXpZF<)5K8~c6s`HeYbRs5sfT%Yy7HGal# zN1b@$diUaC2VX4>KNPgt`ok&viLtZ38CSXSix;;zXVyMP7k5Qo#jRtzl8Kqe1tO6^k zI!bJ_tGH7AFr9=fTaPfTV~mW-=Ak3(n{SKn8?O&R|9)`;Y)M!ct=twrWbl=?^r{lu zgd&UqkmU>7`Z+4|`<3NYRA8~)#vAge(;8|+syMP(#F^tqTg8HkO2=%os5tt5E|kV4 z*?d{MK<`(mUifzf{+XovPG4X~r#d4|YZ%h|Jy#Vmx2%)A%F?3qwK}P{5=SLgIzLa< zUDc@SP#B;83o1|2f_yCaUx4efUb7a%#ip{Xx&O29Lh+@;spre_j%rm_>Xw>9fDp;=_AAfRs>o- z#uqtU=CwxF;7e^E)Fyn=%AzIo+k zdAMCW+w<5-=^2UR$0ep?4Z(8#uPd9G{8x>`m}Q2s+?#(DX0*vzl%40ti~=mTSaR;O zi1!%uhdyC?)r|A|^VMMwEc85?Mbqo5;Z_)x)T1ks?`dP9INC$KVoYCw)!cE%XDUEp z1XzNK^{Mm9`Jn)D!!l{UL&Kt#mE!)yR#}d5KDuNctw!z=54zft0{y456ws>?a;|K{rNvxilE-mqxbjCRrf|Jtq2HPJS=?Q?S<&+rf#&nS(toiA^tp&5T3S4} zFAm%cG-Y$eIRm^zpo>CNQca5!60;|!+t3d3XF4#&!UnJd6wts_J3vLv5?=B?;h0FgPULT3rizU--7*{ z*c0_;X<{=TM(GW|_(J^9VZ%p^Ot6~|#~02lx^Y(V?2^*5IptV* zb?&_R3vRmhy6bPq$vwCG%?oc?RP9`R>yq1UzvIrOcP+bn`8_LEuDbWW`yY7lp@$#& z&7;44?D5rW);{s%Q@?xqncqMA-19H2TfgBC8()0sy!qDVKW^FTI(+1- zufO^ByQ6>o+xN%*e%w>@!;dF^`p3_|)SmqHi!ToxJmfyN`>DG62Hj7VmE(T>JpH6; zOX!BslsJ%UG^`%bum4s31`kpS1Gx#MB^gzYEJs1c%&O7lMd^8!I8ayTm5g>wpO9Cc zSXMqEZ^6}7C0AD!Csq}vJIW^IS57RQo9xJs-ia|Rc8*;;U?=f|y3=?r8^5JW%c+;|Pu0{&!7o0y21 zITVv?nhuSP>tCQIq-1cnC8uYo6v)`MoCrNJ89MD?5|{)|04IQ{U@Dk}FxX9a`lUbF zvq07r#!{o@Pgiu)SCjv1t40@G zx1kD$W7c_2ZtbV2*f-v6vIn4_1!Xx7Jn^`o`=Y6-Ir?5+!s&U-3krx_OPJ}HS-qej z%@BtDB{v^w7dPZJ8 zzXVeYY%&PaX1y`nIc0iw`sQWTxjNrKKdNOw1UUINn&(Qm#%( z%1B8*%fWb`l9D!V^thC?Gd!Pe&X_bQCo3iK8e5NYJVC*Ii!O0b6K34RDY#Wj&Phv2 zPn|R|J;hivCtfpgQr5(r(TU^6XQrhf%NJI(wA7@WGr|tDRmA%$qoTi&@EJAwOwUY8 zN=Z-Ga#G^NjGUB-Nt2ROauUB~ zM~zR>&OZBTQTPf8$E1{GR*^p^l3#xz#4B%#O?(p)Q~edoP)s0a;-m~5gE8aMGg8n5 zw8!+RP$uMtj>c(eU(RKDJ?B^F7xM0$S|pk8u!^zcgjoieiVO>^xTk)3@Ns zY-J9b%sEey##21fAI*)=nU5KQbIk{y7Q)=G4)0=7e9pEA)bpCL=A|fh6MCf5)wuTs z&vZBWyzm4W-$2CA^xhvBNQd$0KB1}r)pB8Tnb-BOs5g6A)E(GYW8Z?EaUhDEeB9`9 zQdXXc`JP|?6K8X5@~SFl=J11-7nqS(uE6s=?3=J}!M+na%cJ{ebdY6u_`jo7Z?e{l$Yh_{0l*Ns8y%F7Ja<$0DWUq#&y1<+qjQr9Ppc?SFk^;>gj?d>Gk#O zjH7~Zl1>f|)CHCDwV@XECH7kEorYP|VC=5BCIua^v`@Tl zVIIFUIRw&WpK$?Wc1-b=bMSEX*xz?I#UpCcc zuel34&7^`%k8_$a1lIrE{{1zu8NaFKadXTsGOgeoHzxFhyLH^M|C8fiT;Us=HyIP- zDXPe;tXy#Zc=X&`No8r?xxFd>j5rWXhhe38i02A^6|ak?wgz_SSLim$s&&cxk5Mqla77IL5;M(A;KZ(BZI z3zMn#aFsYQJqrUh?Tu3qw)B)Vj@+~%F*$i0V@Vuu!ys=`TCy>RH*Y#Rrh}UTBvS>k zpcqdsSpmp_zD2;4>KPOLP1_Ncm#$L0$>A$3FQLA;%sjA7KYGMS9ZIv>wea1hnRbd{uMkkTh5stKdigfVJDwwi!X&SZ`sud=R5SE(6k zYV5d)qbI2e`fpbz`gK^Pgep-MGe+kHZbQT>$SZ%*j>?xv85`CKJ`9jXOGW>jy@h%qo z-C}=M_y@7SCHkL*`^0`w_??jJ`7>UuaH8XF2Qt4gAnilNo+S1OV$T+Pp4exJeXiK? z)j}Q5U1EPg?2n86_hNrZ^tXh668lG@e+ja@-w2P1`#*%I#NHe?EX;2Qkp8;~dkW)) zgM`ap5Fi#|@6CH6egZxp>!xJc}`iM||Ux(|Si|8dcO4>BJwgUrWfFb(c+ zi@ry=A7uCgApL(O_P+~HfQjE5fbf{;u#t;g{n6wdlu1|5#6CutCCnEV3+D@$2=5U-3^KjPMSmJ(xz~d%-)o|~ zK>GVL$b5bxJRt5|;+XD#6Z=oXUqQxW!JDge$HJsKJl7{|u1 zM4tdMo~aGX6V+_kc|Ie$gKl{YlYZ z1leD27X53G{k^5N>F+y%?C-mQjQ0wV@eL4th%ix@1F|1~QS?p1Js`vV0y6%#ZA|>} zAmdK}8UJYE1mP5r;fh6HC|n|3CcGb{zvo2XB-|nPUBaJ1jNMv<-=<(;Y&eYrx$^Ycd5AF3x+{|M7Rc||K~-21!R1iMc)as zy?g*Np1mObe*v(bGW2GZkbx`AKt+qCW#N-!Fm8&l_UjDfYjJz7J%22SLX7jo3Y+|15eP$oRr8G4Ztm znVx{JPU=l3hx5xZxu*?zZd(9!nefz9gz9l57OT;VZGQR@W5TC1JYkV z(T57h3R8uZ!iB=M!e@mWgl`DH6dnbO5Pyv@7%xsS-A=;Z!v4aMApMUPJr!jBrigw$ z$nqC~teP2zs5=u1U^Q1sslp9C5HS#kdZ$m_(bqVE71?|UH6`#m82?-#pU_$|o( z|^drI(Aj^A7bW2wgz75EH+k}^k{YueC3$s9m&k_A*;ZorW zkokE`^bNwdK!)2P`gCoiQclC$=78d!w(icMR*O!c&3UzOSllE|Jy`g z2I_o?{+#d+V*jJ)yMTzeLd|3Nyq$UGz%fogmxs z!=fJm8Q<5U9~XAI+}LBmV8q`~m?ZW^qAwF}7yEmne8M~xD&|x;mbw8LiBi$^)g79Ans#? z>0+NHoC`9ZMWWv+`YJF4>HP*|x=)JzIj|Z2|3UOk!tEgae*n_|$HD_3`+=`T{~Jhu zKY{EIPKiCZrwP{*41;@HVSA9}i4k@KS^i!i>!m-)_=kW@H&OJlqE8e(Q}k;=rtbim zezDjqMZa0}#iB12eFez&^`PjF37--^2QuE5MBgO(R?$BKS*{}>>(3+ZKZ|>V=q-Ag zan%5jG-j`K?Oy?$$<-Jw(4d6Jqe*n_|QIP)e^D$KoMuSf9a*+AB3S>Nq zAmh0XTnzX1;H}`BVpqKt*ST(S8OU(`K!zIzGTao9;a&h4Zj;!9`j~K?LHg?hGW;OX z$ALUa5PhTYkHVcG)BB6)`#_fSJK>KY%UcVwoI(8zqlD2Qk5ebndk6;!M}j3iS7jH ze+5YY4~qRc;UC2PP0{xXKLOc3{tB|c^@=@wpt)|e2N`cykm>aT*$xH>hlqQ!=vRZh z-eie>qqtXzeuwDGh4+j7QIO%*g3QnJ!i^x)eH~=F9|%7aS_c`sO?V~9{Mtny12VoW z(dU9J=ThN1;TGY?Aj5wlJOVP^ABFY8n87BVcwwfnP`C)x#{*>ewZiAc{;KdT;dXKF zIK=qhBJ4lR#4|zo4#@bv5Dpw}+=mO3K)U~7gt5ObOde_M*}}I#=I_sdfal9nXl{xGM!S<=ZU^m^bbJB^SSWv!tP1N zK0t_<;k3VW;WS|$$m3fs`Xb>{;eEo#gwF`qgUr_}!mVQeljt7^_lx~Y(SH#BBCHn% zC!73r6m}QJ35N=kg;#@2KST6uL6&2N=*8kbSM*!NeW~axL|-HN(;(w}QTV!Wi@5I; z{m&rN`B3!F#Ql)izZLzM==GvoQ%wA=gzbbKz+lAJP4vFPD?y&O!^C|o$aE)(o+CHl=^2*Tefyhr!|$aJ0p8UJfye^=}ui~WGuzY%^f{1IgMUq!c!G5SSd2<%ZH z>or>Jy+NikLiALS{;m^F7Z!>864CD#J_vHW{+PIL5dC%0{{%9=4}>3z{V>RU92Na% zkol+s>EAlm|evg1W9>;_yK!!Uh?v|^Kzfh3l zgD7D%NPnF`#@9{Q8)UrwLFQu!NPh{Uj}|=}q`!R8i$UgNuJ9JIF9GRq1<3Rt5v~#U zr$MH_5v0G@K&Jagao+**xxo9Pe+bgwry%_u68kqGpBo$#{U?zAeg)~za*ZiRC`i4f z=$C=?*BN9!dWii>(T9niB)kTs|4h-Z6+K^ABrFv!09k*FMZZ&cuh<_3neG~~KP!Au z_!h|U+d$seegbjVt)f=?>NZ%|A)Ar53{e{ z6aOfX{<$Im{g;bPn*9l(|ZUPzqR*>-@75fh$(+Ns7`M5~fR(L7MaGgPh z8zA-}Vz-NZoY*IWjBmQ=vxJqx+lBWC_X9n3~@)Hfx9s| zqF0E%2yBM`D@6ZTcm(9}`m4DAB=*oW6Ms99?j1mecZj}3_^5EN@Tjm$x(VM~I7Eo| zbalR`fs8j7WO)k3ULpETqTeq1a?$S-{WqfjPW0!6Tg3hq$oPXY3=@RcfGkIb=+lIG zAnUVG^eWMB7JZ3uneZXu{p3CO58IE20rg>bAeQ&vF2HB5x1DU@8 zVjl{!|Fnzy)nd;8>F+vmpCR@NvEK|bzB`2v3fBsM53>C0LB{hc$aJ;||03K6(%*L= z%N>+!^caxgx`Rw-pwJF7o@$W(9|P&{N#S#1uLU{38J=g{F9SKR=>R!@SphQq-5}?O zUl8|yh`V*ValZ_t`!JBh zC1QVB?Ayft9?0YKsqhHMxo<~8}OA zeCpjG!_|SzXT(gSUji~*4#<2~ioIIwJH@_N?4NzFhd2aGh{7*bM&E=d7Lfkm759(D?iT%f(SH#B0@A;wOv(ka9@>lEP4r&E zIFSB_iu)L`r-^<&7>e-uVlM*eze3!X2$zZbD&a#Q{jC-Ejl$Q({g1*OApQMC+`ka} zVc~b;{)4!C#ola=Dc40H!?y!@yt<0Lr?8*64-xk1nN?}_^-Vm~1KO5Fc0?x)0VDL3T{1zEl*kmZXJd#vyZalcC3 zlf*twI7!^6ihH5hXA3LD{T6Y*TkLCvw^W#X+z&GSr$v8D^j)HVE&4B_x2QD!Z6Mp< zK+(sGK3()G(eD<0wdk*i{*LGeL_Z;V^C}a64{#UiXN>Sh;VR+F!u`UN!VYsyxCG&J zVKK=3mJ1hvEO)i&cY(W+-lL+g75*NizxATO4$|MdqVE!Z4AS2hqJIO@-_N4g39a*t zzsVryM4|i9!h1madtCTA$ni?Yg=RcaBwPsc z`hAyh70Bz>Lm>0L8sznRo$zIGKLRrTpF#R-c8g&vVS8a$VIScjVWRLF;dJ3F;X>h~ zAk%qWID3&v=N92}!WV^Kf%K2huIYUB0;$J~J{H7Jqg$kjo+B&<>2IFs)xzasUnlw- zAlvH~VpmQRuC356+ynCb@QAJ!oA#Fneg^xk!ru!oztzMuLii-edR-^_X3^gk{ZrA8 zh~8<5QU?(4RUpfeE}SJ?DEzJPRpAzp>AWlYhr+nqOgtTKH_Q<(6aFa7yTgQ^1v1?) zLAJA>K$i2hJB@t@cnJC)uo?az0z<&Riu(^s&G@?8U4{k1xgh;HLB_v9xNDh-uiM?m ze>_O{)xz&UrdJCxy}iqgd-XkruM0mCdWH5C#(j?PvXw?(Ae^_#yif2T$n?70YtkPI zQlBY&M(q1R=HD&$*7q5IBS4PpqwY8Q1d#U^w}AQJ7O~&@!2i?Mxq#VNwf}!Tvk%WC z3Q3egCa+4OrW%Eua*EJ6OeNc%nLTDRGke>64>L%TqEHf&@G6s(2vLfJC~`ieshpA| zsU&(8)&G0n>pO$%_kS-}pYMII^YK}0jn`QJaWZL&jh{{?K*@gu?UzAm?_0FL1SMa0 zs!cbSTu%NBN_ppK-~5t|*Ml4ZO8OT-Dd!dX50ihB?Wfsvv&l8&AyCpCr(NY`8!tqz z1SS8Cw6BjIw0(1|T^W@8Ym%w-cceX#_DI_M z$iGN$iOuIyQ1YokyCK<%{^8^^^uJ8LMgL;j?~`l6WaPJ<;pTN)jthFF95R*uyUAAc zXOjcyFQh$;_B7h_$erW?@{+l>yemK{?|SkU`fn%iroRo@oy=wU)3nEf(w>)SFCaf6 ze{kM}3lOZxnK2DA$CzCVDh2#qI2q^9Oi}sZZtbG$G z<=s!aAMFX`bh3o}g8Y@Ny3nSt2TJ~Fvc4@;&;G(e^E}@v4Fn zuL14mwENQ@PJ1S~fd2Q%?c~=CZ?M?r+nDSQO8$Mx82zu%UIe-b-w(<-{y_T_!z(PY z{yRWve=hAoprm^ol>DdAza5l#Ro=DX4M7Qi0QBL{Bjh}WZ>3#nsf~XXc|9oU8i7); zyXfx=O8y~+7m!bo&oKOXaw`2LwBI5Z)BiE;_2g#yzoLDBJVO8PwErZ{GF#6|pw#OM z@@o3)kuB)IkM@JK2Y}L#G1`+EK9gKeZY6&u{~)WrXUn~d>`7)>dIyk&LCyfBy>rPWQgQ{kj@(4lf@21_F>`11Q8DxmeBd3rv$=AuZ$@j<=_{pkd4e~m& zKG}$DPPQRCk=@DOWEMGqjF5xLVdOL9RPqgS0r@_;l3Yh_A$O6bMTRPc~EF?#Ql5RXXo16>EI$BJw1tp&yVlH)W>E5LLZ;E*ifm7RSF$JBhv8YY2a+-Ri$E!7Bsqru3A88Eo&{c-WGc-C zr5+2(Wei_IdkrZ0Z6vodd=Kq?v=5V4uD0>(kvDJQ1bgP?RUxbwg)9%ciIor&LQ*Z zA541``3(KflT$#c|8&}OK*@Ij?e}P}roEB&F4{-Q)1+skO<#$u3nnAK#H@@_A6|HHG$U+H+|yru{zcwX`?T-bTBWJV5`?w9k7nBil|7wLbMe4AXt@O9)i`oEz41NlGtBb#jf27^++k+h#8Cy-M? zAO6e+CEi^6=aWmx6%7B3_I}#Gk!G_^UxB<1l<`U-8<9=PR%93QAu^Lpki$T!*HfU( z!*S#!`d=a6Am1mqllwqP_Z@kR{&Qs0EjItoWHwn$js>M1lgR0y9Z=t=P z_F?iT`cKiWwbhpU5IK;1ft&$KIj@0I{+smQvCZ1eK%+lyX0&|1)wYSxSBjO8lds#Q%fzZMXHQNLD4Uvh>ylCEX2V zLr~J+Zo_@|(SC&CLHcv)A58lx+LJ)ZZyNbu`sahvFK202++pKg4T?XFycd-6v&lkG z@_Uluuaff^zJ=TmO1$F?udvhRTLqN-ZXr|YZvjgD_Vo872hl&2_H*PG@-SIB>Mz#2HmzE1x#aw{n5_LJY! z@A<;Uzm}{INks*7k-$$tOk@GyF~R6Z$vN{)YA; zP{!jVDE&~a)aFx%ype1TN_l;0$3fZGpQQg;`d_B~1}OP11ts1JQ1V|#?f|8}rL>)| ztX&b5a_ZA=Li<57lm0w1PX8G4L;Ba#K0=u0lIPwK@8YunrDk%A^v3~Dn`oEz6sP+5m?n5N} zX+SmwC0#EvNdF+Rh@8yu|I%IpO1bOF%?#f|doOvI{tEkT{Oia9@+nZtf1dVBv|p#a zfP9Z!!|*@JDhF)(I%Gq#CmA6}kmJeei7+Hz`=kA90v<4^W?wq5z4 zv?D?L333AcM@au+8}9*7`mZP1hyENgkNyPhCux_EOUc#bHcRi9pcnZbrvE3}f78DF zdmCOIl=AC<|6)J5k#=M9E>QBhm*E}g?@IP!cputXv3?lKxLG^&7j0frN0H)o#C0Z!(@aU%jgDtl@DCOQt z`%bbs{q4yI>5q^j=zog#IC3gEpIl5X2c?`(Xm248kv}s058CFatzQ+gHd!B(d~T=R zoa{h$BYTq(P~s1!J&Jsee4ShXO1uwfe?smcOUVPE#QUE1X)qb~S=vcI+VHDEKm2uR zH==z9*@^5yJ_1UY$DDoL{BEzTBo=Lle_I&a~at*^b(B4ZPCVyi1N!p%ccD__1 zYmh0Rw5JJ~214D2c_JXY0tOxEdeFJ4;a3g{Ep$rX`iI+J8tu- zPTl}Ye>9|h8z}iTrQMm~>9ilB-H+@~K1oin^iBt*zDwy}MZ4zDHvgN+mgE?61t{sN z{$kk}l=|e6d&y%A|BZI?uZV;{Rme0@(zPW!k=;O954}Mt=TX|vGkgL00mDBd50l3j zewudD37by}c_-PE43b01CqOBGJna{0&!YW0?fK*i@)L3=xsUvT{1=q=xWC!_uORD^ z4aw$Ydr;zM(vFhF<(FJjC#$wEqC5KD$oY@cm@N)7EZJM#)j2wDT3Rg#N{}KP0~( zPci%~?Mwf#>Hh<2KhRF4-JI+OO1pxf)cYgy6NY~dN?Crmp+dob-M$<^dW z@-$h&IGSHAvLV@tOpuevJ)n$Jl4H}|N#08iBuA2y$k)i#!6J>_I+FK2N?0 zN;|jH-a~sI?PKI^9-H4iWPfrv`5ZZmTuB}wydn$~eDDzC->*o+6W7o8PUV#J_`fGuj==r^)BZ736mE z6j?Rd=95k4lheorz*#(q+;vw4Ew8zq(NiHKlXZX*w>sGMo?*pZM z13(#iOyDKQ;l|cp>9tNcz5!$24 z>7eAhgggRDyh|$Da<2y^JdM1M%q5G+XULa8>A%;=W#meR?<5b9CmDVg#1hBv^|0mD zARB|?ZwgAj_mjgwY3FmG)USm0e1s7Pm-a+;UrJQ^+LH}&pbIAqtzf1cAay9*%Kxyxn-qTQT!8?qB9>2hf2k#YKm(;iKZCnu9L$P)5xaycmNSV#L$@?X+l z-IjY9DD|yQUPsm^8z@psZLB@fX5Jt+Ac2PL0Vw9hd-3BQO<@=GQ!2PMBd$kz0C17*MKN&8XS zAyDEM&_9g!SlaVw@3!=ol6%R6cP0205E7A?J~AlZ(iubw{ zt}b~O*&dYj_b{1HK10qR-zPr@WnJwfPmq_^v;ONq$*(Ex&a?yMF#4aP{U+^|q(8;x zQwx-Q!n9|SYw7=jw&w;Lzd9)Ko6v4Wy9ez7p!C;p+AorGKuPx=xsm?8wErcm*SGO* z0j2#N$v)&T@)=O}t>?+9^v?ukzncTfel?HwGKQ}s{~~?=vH8>mrM!E{u4ItmFVOy& z+(MosYc#OwuP1wh(%zqGp8=&l?v0KakNUL$rMyX?_-B%H$#=+7P~s)sWO+Gx1KE%K zp8S%}zp*@bAO3olZ2PK~q zWaWmoyn3MKPrD24USu99;}|0+kR=TNo%G*g^QlQTB>R(3fie!0XfGspkjF`1Bj!Wi z3QD>*w4>w`Hr?JhqHYoLLOS>DH zAfF;%A-9s>ktfJ1O>BOR$a~4|pv;R*+MknOl0TB?$V+as`BW$Cled$d$U)=?atiqY zxt{!*{F(fVbZ@ui)F4yHc4RU6Jh_DYgxp0QAl+1(-`!*;IhY(z&LNkQ>&ZQ!jKdFP z@*Ot6n&eI7J!EI{H0in1#=nhhM?OqG21@=D$VzF}P6uT@KLN@(y-WYsiqT56PNj6EclVCj+4L zQx5Gxd+ zy*6D(P~xYPPk>VHiwxgHdk^^?d7L~=E^BGits?i5H@CL_d&z#}IP#x1HoQ_>%Npd< z&s!cN{4BOk(&fhQ9$yJ(kd32THshv=5R;LCN=orME(7 zTfdvhJ3&d;9F+Vz(oQD_kVDC5$=Y3Px)x+#vV>d(VxRDSYU$fbzw>}i7baf@rQA1Y ze?)#w9wYxE>vXm0ZzEfi>7a~99F+a37?g1s$?&IXPh|LXP|AIq_Isf8!&*@KYXki| z>EBBprvDe(e}NL;bhG7F041Nww6CU}Lc0;|G}^6ccc$Hoc0bw!XeVfopgorMB-%5{ z*Xe(U_J`zJ`Zv@5k~~2FkF-yb=jiu$x9zDy)&`}2Z=~IX_C2&afzq!J(axkj0F-nI z`bUz_F#H9Eze4{4ayk8LX>X>zhxYfNl=la1=RsS}rJ#(%m9%TqZUjpFduexKcn{ir zY3I@&0!sYXX>TTfr2ic4OVVw;DxkFU8c@o=k-U@PEg0T`{$6BX`g3UyCP&dfp7ufV z6nT!}{vNix`k<8GnD(8tThe}r4AMW4b}{WIX}?PQBl0u)kJ3I#+w`>MRRE>ESA)`D zDfBm@zXkpG(@tmjqqGN-arz&lJ&t^l{+YDrlkd|15$#RnF8cS;{)PO5e$&g=w*n~r zRgHEEDD`hb|2_2IPk&GPBlM3WU!;E~?G?1w(@yJc%XK_L{^9h$PWv6& zKhXAP*nBPnC7%vtH!_3lN9K}IvWOf>jwL6O)5zK60`epB6HxZm&p{dQ?X(Xw{9m%- zL$-cR$u4As97)b1OF+qYJ}CJvru_lK*N}CL{W)_3vLV@+OeNFEW@JmUE!lzWOm-uC zkQrn)nM>x8Q8Gamk;BQ6Gqsg)4 zcyb~+nVd$>AZL>$6CeuG&KnM=yQLn!4($pl$M4ky9R+GCpaam>xs)gE8ADV5Dq~{QOKTc4jJm-!#ykZr`Se5D z=jp$*t}!#H*WHNmg_gRdgdDS!x&(TCnLo!drDcBTpUV9G9fQ9{q-jf_FLRhru4Dc~ zT>_1(tj#~{m`BR|(1XkT0~|B4%n$u0wA48pbzWE&XP{%=g_ib?$46r&bvJb2r__CJ z(|^-X<4jJ~e;-iuIgEVvm!)lK%n|BlZHzeqEoB`=oYU0Dq0dsEfc_U+`loU}zWEq& zsY8Pf#$0Cidefo6k^fJS#>s`Q3oUt`LfRV{e=E!v?2Nyt#OtQb%x zHrp`+sWav{W;k`xT-2F*8YaLr>g8{v&eWyQ?^B_}n8VZ^pleZghHe0jKjtXn-$woQlg6~9es%)>{Y~nX zSZKYe&y2x5rtUiy>y-NLamI|KHsg(Xf!Yr}lltoCjG0f}1NuYimJ^NHM7{Y1{QHd5 zZ6_J?9rdR#qF<*hW?E@`z>UQ!Sw>(g;=xHqu(*6CUwdptY_+e(2c1#K;KJU zv>5+>Dm4C>4VVu-sVgpVOh3kHu*{eUbq4ff)J4!wtNy}Q3jMt5$2^rkz`rf3dV(hf zdY0lt{#wwBRQGf{K!2dRqMHl7T6KnJIP_<#S9qpDZ&&?}TVs_m->9DN zueQmUL#hXPa-mPC&T)=zHRey%TYL?+VTt2W#A}K@r*`4Hrh1{9yc^#&)yv#e=oHmy zo_YJQ7F7S{PdR8zbJe{~pYQOyPF1gWPe6B7UF>Om*qDb^Pw=!oW=v4^5>Eznp6Y)7 zmB)=4sygJ^4*j(14Q}OM&~K_gOAYE+!E;ds_P|}LN8T4BFX%U?}h4p z{*kARS+DwE??mV=szQ!bX z^nI$=dNxBppn67f$~p9>>a(6S=uFj}{iE?w9%$ou-u?%B4)sdtp{m!ro1sUmPV=Ps z9W#!4Ec8^>d2Z!P95Y*Wl9^Yk1D=UEB8Sarg?9eR!GuiV4X+f?`TpMm~H^)|Od zHOKs@y2RAD$}uNZXPZUPXH{d4Lwhc<>$F;J$6Tserl8H{p9qz2RoZ zJgK_7cQ5oL)%Und8#-pX>TT{(=((yldEUMSeWJRGZ#DEv)yt9^--&aS>alKH=&h<3 zyBW}5sy^rDLGM%jkv{`J$2p=p#Y}`gq53!fWc*y`jOq>UW@uAE`l?p47e8mIta_L` z61t}9-~6lbbA`I9Cwb06-$?)HmX5hyb!Yzx=w_;Ol16t#U#b4mnFgJ%I_jSX{jlmu zp5@R%)oVOQq4QLK<2wgksQOEHWGBarQoSN+GW0n5=Rr?V?fBbvcFYXbhx{4Pud6Qh zOoU#bdYHQx`aSwfA8^bn)%UnZp+8mqntKL%kLvr~ig?dGpnAO982Sh5me9Ydo|4=R z`i$zA+&<9WOQjFnneot-sW;%gq6V~UV!qALb)kLcGJH*p`46;XUUKDI(n$RyJOiLp zRnPWFyY5!q$lnS6cF?YwWVR!pE~>xx?}qN7dZ)J(y07YpcQ15*)o-{5p$Dmc(Q_Dj zxau$6qtIhi?{JSpzo7b*djk3u)#=Hnpx;nk#aHoRoS9W$?$m%@t2*v9fZn1y;WUIU zRejvo82TsGOTDSk|EQklq(N7$C~eu}HiNFOIybo`bgJqjZd>SM!xh++M7Dxhr$>1JzYcpE}s1G=8zK8P4ZhRUh}HLGM>R#ZzOUV}927gFN!B z{9XO4otC&~IhCZ`!R~SF+sV+bDRJAvUsd%RzP8Axmg@Jt)3R_L*7#BXY}Daa)m4(^ zyPT%_K6lhdI5(>9=a=tD2OHm!J7Z7PYmJ=q`>7u5*^Jp2QN7e7cb3Ogf98`r&RErV zc(2ApoT9p`o2Z6wBD8D1btj`f@2LNvZ+bg?2UUCA%sMzTKs)AZkM#2{^}p|aj{C@7 z)#JUx5pY!Xe|**Z8S_8YE4)2$UhrHdee$0qx!YET#wa5fW3Ex%RrRf^!yYm2R((Wu zTh%w=>uF36)m~j++0Z_dVus(1y+!?_)nBCgNvWoJR^$BctBScfU3H`66S()i0qq)} zw-oopWg35lwrhpzm8#ch{J-4sE%7@MRQL0&YJhv9>ZD}(?i_%|i0j-tqW;(2kvACg zi~3*oNhSVN|A)R=NSk!I)cFfvwfl{^9NING&R*1^w(6Ds4wxs6G)@zzZ9e)z^(eQ( zjre!NRTq1bI~da)8eakIN5%wH57Ij1Yn(Bzyblzsp6QkQ%P7@*d?m|_8Kc_kmviQL z)$Q~gG6mW(mtcgjUey1#+YWPMq3SO@a<*9t?V5bICFbx)>fi0&SsnKa)%Y7!C72t~ zj;W`8xKI7n{qnZ(v+6!h#cPfEM|Is~xx-edEPYZh`Dh(us;mCS6TJ`TM%DX#9XcD+ z1llnbb$rv*pPF3l7W}Rx)g6#Ne z_dKcgdr!7EW`gRkeQi%I%89wtNMUX&iM;eUt#18WV!0io+9+^dex&nr5(|B z)#H+nBF=tjd`)m8M%z_KMVhm!U(|lCT1Db?Q~!0)K76Ry?-d2slM6G zhJTjoN1Y>g8uOOwp`MEPI}D3dJN^+5IA*!(7yTRX(6k!bHJ>HzUhSBT>VMpm+a2pg z^%`et8txRTXL{DQ!2SS@61DArtG-+HC77>1^IVeLt*%o2uutw&H>eK!W@96 zu{YkMdV;40?m?|oXLzFUw^x0Sdl2<}K=n{p&h0%_U+tH>#-plt>RltGdZpU|`Q)kY z>`y`3gz8D2Mc9*vs;+DH*1=gt^;kCzXRv2gFZ9XV&P%Fq@m52h%uyY3<&6E7>KVx= za1LLh`U_Xya6VK$BxxG-de!s&3vnLXqPl|H4Ii!Ds)xBtaZdO~b=Tx`i2uFnyZj?C zF2AUDyeSW2zf~RbH-k1;NPqT9jzU*Z-O;lOy0Yr--t9P}UZ?tA_qyMWxl#2c-s->N zj;4B(EBBXXsu%kgrQ=Sa`mCoR+SN_u?7(K3fyEAE(G0&)e!P^pXCaGTQi9*j(y}*$>={(iH`RlaAw@LMA?_PZGRzcer zQQYy>|AI&E>AO@HxN@&Qpn9_J`v2lRM|Fi{x#OQvUF6Dpf)9I~&y4WOeZML+PIUUM zsjd2IU7NS6c61$eR{cNSi?UT$)Hz%X?ZYih-a6#xGCmXZ$UB4l48~^;c;szlj_Uh- z^49T|>KDE8PVy17WB!YSI_j@_qn5Q_<9y`G+r=@}KYF*0GUgA}pLpec!+WKqT`Qjg z{NgDo>r1b^v0S764O-6|)!)chIt}Yc^$4eQ4DO7o+a~2bgL8%IJAF^TWK24=BQN>* zUjW)QkNCWJyGUr9Km78(@tEp?J~@*=rFxm$a5wf+)q_2=mf(z`dW*@!J$IJs)$XFd zu&+Y9xQW+7o*$^cr`r;9V~y(ZZW_+m8&vo3tVLPdHU3n$4&r~S{$;*B?_xhu{~BL1 z{3q1^rYr9%|ELam)*Z*YM0M$t>wNM)P+j#c{*_;1Z&AI?Ep6zShN{oG{jjc6RVTdb za3^i9dXU=;Wwld1CAkCY)aX=}!M#fQs+xZ_&a~B3 zzvWb1ZOnD5hj?z=a+Y?W2!g%#(#zL zo9f-Z0r0yuq<^;g)g#CX=XE}7L>NHOuwEb??PkH`9J=>@* zbZX#x)J1iMX$;*<^)OE-)H$H~C+`vHu+JQRh}BC^BmQS-OiZj^HneLbVi>nR{fl(4)jN=H@M63Zm>@E3AYc@?oi#^ zmG`kyXx9WxpHCcfQ2qaU$DYP|RQ-u(9mez*)t8!He;9LGbsh8hNn_5buIWGdJNmMw z^yfbR=g^f@+qbo9s<-)9BhOl@W8UM)r@rcc9eK0571}jb-RLg#t@`J>C-CiVrT!ZJ za~mAG9#|Xn}cezMzku8$)w{tg*FCqDC?X?KNVs;h2h z`k-#LRk!s01AV9Jp8n%ln{8CL^DKhysQNp7gX*I?Vm`+>=Bmz4miMKY>S;;Y`2TG@ zsXCri8TFZ|I?2e}(Hp88`{d1SvFd8cv+i=t3e^pK^8U6#^-aFFsyb%3>UzG-S7Pr~ zz1AoHzp-DS9rKz8$652Y`b*5y)$ulRjf~6mq{EvXb2YTj)b`h?PuSKjefsqT?HD-C0(`X%Q%r2RtmwZ8i3tK+IGB)tuNR&|Nf z4gHf`OWM-HL@|!nsJ_fQ3i+q1p6Hzge>>IF%x38Bs=J$RDY&1i9%TxkhpFD=k+-;K zRljJ?z&}g%DPMi$xd7TVmv|1NjUTJu=Q$4lP8-MH0`HJVROcFbZ#<>?36H!1dajjv z7Ws~+Vk}hG*0;*KsyF%?H^8Y08ZWEaXfsig(ENHjZa+BfNjAUY4}{4!pyt ze$A1$%0ktvl2W0cRDDNs68dw3>O%Jv{4cAnT8ovU~k!=dV2CG z%*kDxz)sH4mg8o-^Ta$!wsc@aN_alEA+J3d_Qv05% z`b%d#=2#=uYxGSrRrLzrZ2TSY7SOIa=q=gqn6A){*{7 zC}N7TazpVrL}nxy%N7E~>)CK^Io#;*blfba6m5f|f_K@E_@f{-Fe{a40Jz z{(KBvuzv_$84bqb!hqD>L?h9{sCZ+M{;^=b_@m)$3`;DMk6wqVV;T!(MRddiS&_Vg zAaawEC^9jSg;B(gCt^C%GB(ulgv^t{!Mv~#sSES6?Kp{xI}|Co$!JQN!o&cc$@ zNU=~(EELZ*8EwL`tUQ?;SYwgAydI&Vgp5l9?5(4OQBT*PuF*s|QeXnXSS&JxrF1L^ zCpycV3OtC}k{@kdn3EHVnZSKTQ7mf-LGm`a!B~2DC?d5(j`vx=3G|F+2NNNiw+ZwP z#-cqUts~f6FbQoKa*4zXQO~j}w2cfw1#ISJMRh^LUSu>c*E=va^)Dk>|jC*s@c^@yZLBZwsV^oaC`AOQdA5$O<$ zh0ukOXmP7pD2RT`Xagoft>W=WmY8irIY`o`Fcy!*teqFSC^*hu)jeo$6xM*C0#*#!&pbbxY0K@94H_;0&#o{Xy2-(+;gF4!|#UV$#bf#++}B^XUF z%#>l3{2ss%-#0i^AlYSfjfM)^hU3w^V6m9pWW#6^$+NZY7AnZbT9bhEP+lkt-4rd< zWa-5P+Dk>~e*=wV=&dJ@Dj*@-g2bvJ0cP0nu<&+%Zgz zi0p4j5glS%8camO)>9BlAb)K>J|dAU8NYCWdL=D-cCa$vmVrESBk=_GzhJhEC8lK{ ze;~qQ(qm%D0)WiPj-WFkP^HYm90tbGhbpsdu5pZ5SgL$JU#T)?OCk`=))KN3#nDgz zPNv27niY(Pvb2+ZA9oA_!H)NCz=|c1B5+aU!7= z#R>{!<>^oh0?`omFd4*h0Y_e3atg$=bhgIxLLr@vp+s1EN@B;u{iDG`{gTR9Xk$fa zhYgA0@kH6Y%T5F{^UCTHjt3J7sdYG#m5|Elkbzlc(?M4sIzhGqS<%^{%tG1r0xTPI zb8t9@k045j;d_8|1qhXiXX8LeuzTqd*5u>lBcB${#RM?SdWObPtzbw_UJ%D)Ie}TF zCu1uI6^640o)4EfY-7sy10}~hapJ3pgINTdtZF%7$%a8Q8=JYvCMcisHk2#}aGQlq zTy|pDT+3WG7WUR)EL7&R2m6bCm<-vWSXm4?GF+tPjHrDSiRpO)b;sug2_z;euV2No z6j<~DG&+vk22P?P0y2ec%W+D@`974Dqo2C$Fx-J~Boh(s{KKl%Sp@33)X1nDZW{7! zv?o~%UjICt|Iy@dfi1$GO68Lv1BU}F_Jcxf%knwZgMh3^>xc&jhwPvfM8ff6EA8w? zsrE~QlJtvX0tmoVL4V7o2}dtIi%GGR)-Q)M%;xeo>L&q*^hhu}&|imLl3EYv@SWq{AW*m5o_v+X_vznnRH zhqG}muo-pC$NrJddj+deetPAlM!u1@r{oe~M+2RO*^d8WqT`Mez_&SWkE}T11`Fgk zth+kLTIPjJNX(*0ETqG)atMa3e9p+QpD#yue+1f*&`lr&Q$`6V74)N||WtQ4TVHK6vZb%rPO?0Uq zC(#C6#V;D!D5@Hl>a&Yu_;BEO0f%1QIE5sM-7wBiQ~9!#_mIsqqjg9ovRuyX&`6(z zV#tqEOKe@%sHmRS7}@rGd2V*kDC;}S8*Hy-)`g|2&XD6L^Ep4C%SV9A9=0qCtr|O7 z<|w{L>4{?59(1A>7T^FS*Bc(;TZge=qV4c`Z5vDk#f>jsx4fX-bu!wOAI!x-P7mT3 z62$f*!(eaST+2O*676F_>{Jk~va{{VfbK1)1&rnS__kB^{9YI>8)ZFnp$GH>h1rP( zj%lISdG2kv#-Y$0Ooup#@I(ZgZor-$Wg0~>!EFLNQdWmKZ1%pA9?p;Eg?eMti(m?8 z;GXbcFsxGneN%qmM~IE0=X_W=v*WF%xGbHVx^)Gi0&)=7nJBf=lPE&;V2lZ^=VI7$ zo(FQ|zGDJ%LO`INrzDb;TRt!B@XCb|5%j@9Pm(r%P7zB$0G=PT9H`x3xP;DQR+uri^0uC868rlawD?36X863k>pm*V0_%s zCOf{exMXT`tIxoVyP$uleH)As?`}}#_dT7An6L~%~y4_)3%jFCKJe>qGI(EHJZZhr4cQ1P-#`#5#xRUfieJ2RW z75lx(2L{>wxte1`6OVCNUU=h$6xeYeu{kq%c{2Q{QXfXC^Jr@m%S2j)nUL?opY$?`-bjtE>5y+8wzm3!FvWymgwf{UjdNEuzbCX$zp zY|$xYnP@kc_X^HOasiP1GmG_RCi235E7l(QKiw&lZcv~o2Thd}g#j*ITT`8oXp0^g ztP2nExXAy1^TFiNcY(4hVz(=kXtMP9|1TYbY|Qevr?P~sfu=6=VN0;PRB^O?+Fa0k z^j_RuQJge2FKz0DZ)}ZE@u(;FPrYzq zg=3-17Y@%NS-E&>)bFUt4;M%@y!vSDK#nHB4yq46_|laf%L6%h6p>|9erwm~H>^v0 xR Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -224,26 +312,28 @@ Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: - Gnomovision version 69, Copyright (C) 19xx name of author + Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. -The hypothetical commands `show w' and `show c' should show the -appropriate parts of the General Public License. Of course, the -commands you use may be called something other than `show w' and `show -c'; they could even be mouse-clicks or menu items--whatever suits your -program. +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here a sample; alter the names: +necessary. Here is a sample; alter the names: - Yoyodyne, Inc., hereby disclaims all copyright interest in the - program `Gnomovision' (a program to direct compilers to make passes - at assemblers) written by James Hacker. + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice -That's all there is to it! +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/gnu/lib/libg++/ChangeLog b/gnu/lib/libg++/ChangeLog deleted file mode 100644 index 239888d257..0000000000 --- a/gnu/lib/libg++/ChangeLog +++ /dev/null @@ -1,1103 +0,0 @@ -Tue Feb 19 06:41:00 1991 Doug Lea (dl at g.oswego.edu) - - * malloc.c: Don't override use of libg++ version of bcopy - for SYSV users - - * timer.cc: Removed ifdefs for tek4300, since they are reported - not to be necessary or useful - - * CursesW.h, curses.h: removed touchline and touchoverlap, - since they are apparently nonstandard, and aren't supported - in most C curses libraries. - - * Removed test0 and twrapper from tests, since - they are not fully supported in g++-1.39.0 - -Sat Jan 26 05:24:22 1991 Doug Lea (dl at g.oswego.edu) - - * signal.h: Reinstate full path name of C version of signal.h - -Fri Jan 25 08:45:09 1991 Doug Lea (dl at g.oswego.edu) - - * BitSet.h, BitString.h: pos and index fns inlined regardless - of __OPTIMIZE__, since needed elsewhere. - - * RNG.h killed redundant #ifdef __GNU__ - -Fri Dec 28 06:31:03 1990 Doug Lea (dl at g.oswego.edu) - - * FPQueue.hP, FPStack.hP: removed defaults from defs (kept in decls) - -Sat Dec 22 14:51:40 1990 Doug Lea (dl at g.oswego.edu) - - * Integer.cc; atoIntRep: pulled sgn assignment out of loop - -Fri Dec 14 16:43:04 1990 Doug Lea (dl at g.oswego.edu) - - * Complex.cc: Fixed pow(Complex, Complex), and added - pow(Complex, double), from thc@cs.brown.edu - -Wed Dec 12 11:47:51 1990 Doug Lea (dl at g.oswego.edu) - - * malloc.c: Killed prototype decls of fputs, fprintf; - just use whatever stdio.h gives. - -Thu Nov 29 13:02:32 1990 Doug Lea (dl at g.oswego.edu) - - * dtoa.cc: better bounds for workspace arrays - -Wed Nov 7 05:53:36 1990 Doug Lea (dl at g.oswego.edu) - - * ACG.cc: function LCG marked as static - -Sun Oct 28 05:32:30 1990 Doug Lea (dl at g.oswego.edu) - - * std.h: ioctl decl now has void*, not char* as last param, since - sometimes need to pass in structs. - -Sat Oct 20 05:51:21 1990 Doug Lea (dl at g.oswego.edu) - - * sys/socket.h: added getpeername decl - -Tue Oct 16 08:00:14 1990 Doug Lea (dl at g.oswego.edu) - - * Integer.cc (div) Overallocate `r' if necessary to ensure - trailing 0. - -Mon Oct 15 05:11:09 1990 Doug Lea (dl at g.oswego.edu) - - * EH2.c: Added __raise_exception from tiemann - -Thu Oct 11 05:50:34 1990 Doug Lea (dl at g.oswego.edu) - - * VHMap.ccP, CHMap.ccP. Base initializers explicitly name base - classes in constructors - -Sat Oct 6 08:56:56 1990 Doug Lea (dl at g.oswego.edu) - - * RNG.cc: `volatile' added for vars that might have - greater precision in FP hardware than in memory, to - force comparisons to be done with memory versions, - thus avoiding rounding error. - - * Incorporated patches for graph from rich@rice.edu - - * CHMap.hP: Fixed ifdef name to match class name for CHNode - -Sun Sep 30 06:50:45 1990 Doug Lea (dl at g.oswego.edu) - - * pow.cc: removed redundant tests - -Tue Sep 4 15:07:35 1990 Doug Lea (dl at g.oswego.edu) - - * Regex.cc: Don't die when someone declares Regex(0). - -Mon Aug 27 06:06:08 1990 Doug Lea (dl at g.oswego.edu) - - * curses.h, CursesW: added vax to list of implementations not - supporting touchline & touchoverlap - -Thu Aug 23 05:46:16 1990 Doug Lea (dl at g.oswego.edu) - - * std.h, resource.h text.hello.cc: more changes for i386 - - * sys/wait.h: Include , not - - * MPlex.hP: low() now returns lowest valid index. Similar - changes elsewhere. - - * (stdio.h, math.h...) Installed patches for HPUX 7.0 - -Mon Aug 13 08:17:29 1990 Doug Lea (dl at g.oswego.edu) - - * istream.h: ctor istream(int filedesc, char* buf, int buflen, - int sk, ostream* t = 0) -- made sk non-default to - prevent ambiguous matches. - - * Makefiles: rearrangements, patches from Ron Guillmette - to enable compiles of etc and gperf files without install - - * Added swap.h, from Ron Guilmette. Apparently needed for - Hansen's C++ answer book code. - - * String.h: String operator() made a synonym for at(int, int), - for compatibilty with Hansen's classes. - - * ostream.h: added ostream << (const void * p) to print p in hex. - - * std.h: qsort should return void; srand takes unsigned arg - (required under USG, doesn't matter for others). - - * Added complex.h, to include Complex.h & typedef Complex complex; - similarly with strclass.h - - * builtin.h: Added min & max inlines; added min.h, max.h, - minmax.h, and abs.h to just include builtin.h - - * values.h: defined HIBITS, HIBITL - - * CursesW.h, curses.h: sequent patches from jw@sics.se - - * streambuf.h: sputback renamed sputbackc for AT&T 1.2 compatibility - -Sat Aug 11 08:01:00 1990 Doug Lea (dl at g.oswego.edu) - - * MPlex.ccP: fixed off by one errors reported by bashford@scripps.edu - - * stdio.h: more patches from will@nirvana.westford.ccur.com - - * made #pragma implementation files for ctype, MIN, MAX, std, - curses, compare, math. Changed .h files accordingly - -Thu Aug 9 06:19:17 1990 Doug Lea (dl at g.oswego.edu) - - * SmplHist.h: fixed bad #include - -Wed Aug 8 09:49:56 1990 Doug Lea (dl at g.oswego.edu) - - * Shortened all .h and .cc file names to work for SYSV, - even ones preoviously OK because they were in own subdir. - Necessary for #pragma interface. Yuck. - -Mon Aug 6 09:54:23 1990 Doug Lea (dl at g.oswego.edu) - - * stdio.h, math.h, etc., added masscomp support from - will@nirvana.westford.ccur.com - - * twrapper, tgwrapper: killed now-unnecessary deletes - -Wed Jul 25 10:05:13 1990 Doug Lea (dl at g.oswego.edu) - - * stdio.h: new #defines for i386 - - * DLList.ccP (ins_after). Prepend if null pix, as stated in doc. - - * installed malloc.c revisions - -Fri Jul 20 12:00:05 1990 Doug Lea (dl at g.oswego.edu) - - * ostream.h (put) prevent sign extension comparing against EOF - -Tue Jul 17 10:06:12 1990 Doug Lea (dl at g.oswego.edu) - - * libg++.texinfo updated - -Thu Jul 12 08:10:07 1990 Doug Lea (dl at g.oswego.edu) - - * added Maxima.h from Igor Metz - -Fri Jul 6 06:19:32 1990 Doug Lea (dl at g.oswego.edu) - - * etc/PlotFile3D: updates from ngo - - * time.h don't include /usr/include/time.h on NeXT - -Mon Jul 2 07:48:51 1990 Doug Lea (dl at g.oswego.edu) - - * installed VMS patches from Eric Youngdale - - - * filebuf.cc (underflow) only reset iobuf ptrs if successful - - * filebuf.cc (overflow) loop ::write's in case whole - request can't be satisfied in one - -Sat Jun 23 12:18:54 1990 Doug Lea (dl at g.oswego.edu) - - * added bcopy.c, compiled ifdef USG, to guarantee compatibility, - from Eric Newton. Changed corresponding std.h declarations. - - * std.h: added declarations for re_comp, re_exec - - * (.h's, .hP's) added conditional compilation of - inlines under optimization only for all files - with #pragma interface - -Thu Jun 7 08:23:10 1990 Doug Lea (dl at g.oswego.edu) - - * killed all g++ prefix const member functions, since they - are no longer supported in g++. - - * (everywhere) added support for #pragma interface - and #pragma implementation; undid .il files since these - will be done via same mecahnism in g++. - -Fri May 25 10:39:18 1990 Doug Lea (dl at g.oswego.edu) - - * filebuf.cc (open) O_WRONLY added to append mode flags - -Sun May 6 09:27:06 1990 Doug Lea (dl at g.oswego.edu) - - * Complex.cc operator /(Complex&) replaced with that from - romine, that avoids potential under & overflow. - - * std.h; Commented out declaration for umask, pending a better - fix, since it is wrong for SunOS4.1 - - * File.cc, Curses.cc: patches to work with vsscanf from bothner - -Tue May 1 07:45:29 1990 Doug Lea (dl at g.oswego.edu) - - * math.h #include belongs inside extern "C" - -Fri Apr 27 06:15:32 1990 Doug Lea (dl at g.oswego.edu) - - * String.cc (ncopy0): null terminate even if same source - -Thu Apr 19 07:29:24 1990 Doug Lea (dl at g.oswego.edu) - - * Map.ccP (Map::error): error message reads "Map", not "Set" - -Tue Apr 17 10:32:25 1990 Doug Lea (dl at g.oswego.edu) - - * Stack, Queue, Set, Bag, Map .hP : added virtual destructors - -Fri Apr 6 07:05:31 1990 Doug Lea (dl at g.oswego.edu) - - * List.hP (pop) patch from dsouza - -Wed Apr 4 12:21:29 1990 Doug Lea (dl at g.oswego.edu) - - * Makefiles: use make var AR, not just ar - - * streambuf.cc (setbuf) delete old base if one was allocated - -Tue Apr 3 08:07:45 1990 Doug Lea (dl at g.oswego.edu) - - * time.h, std.h: changes for convex from schmidt - - * installed new malloc.c, with valloc & memalign added. - -Thu Mar 29 08:28:16 1990 Doug Lea (dl at g.oswego.edu) - - * stddef.h : added offsetof macro. - -Tue Mar 20 11:24:41 1990 Doug Lea (dl at g.oswego.edu) - - * Sample.cc: Confidence intervals now call t with degrees of freedom, - (n-1), not n. - -Sat Mar 17 10:17:02 1990 Doug Lea (dl at g.oswego.edu) - - * All genclass-able files moved to g++-include/gen. - genclass.sh script file changed accordingly. - - * (Everywhere) All X.h file inlines moved to il/X.il, - and only inlcuded when optimizing. Backup libg++.a - versions now generated via src/Xi.cc files. - Exceptions: ctype.h, and std.h (for SysV->Bsd conv (like bcopy)) - - * Regex.h now a separate file from String.h - -Mon Mar 12 06:53:57 1990 Doug Lea (dl at g.oswego.edu) - - * RPlex.cc:RPlex:: RPlex(int l, int chunksize). Fixed - incorrect biasing of initial chunk indices. - -Sun Mar 11 05:40:29 1990 Doug Lea (dl at g.oswego.edu) - - * timer.cc: ifdef USG -> if defined(USG) || defined(tek4300) - -Wed Feb 28 05:27:15 1990 Doug Lea (dl at g.oswego.edu) - - * gperf: patches from schmidt - - * Makefiles: removed dependencies on /usr/include files - -Tue Feb 27 05:21:22 1990 Doug Lea (dl at g.oswego.edu) - - * Installed c++-mode.el update from detlefs - -Mon Feb 26 08:03:32 1990 Doug Lea (dl at g.oswego.edu) - - * Incremental.h now includes a default destructor to avoid linking - problems. Thanks to eirik@elf.TN.Cornell.EDU. - - * all `error' routines now have const char*, not char* args. - - * Plex classes revamped to support const Plexes. Also, - removed `changes', `changed', since they aren't necessary anymore - -Sat Feb 24 05:56:13 1990 Doug Lea (dl at g.oswego.edu) - - * File.h verbose_error_handler, et al now have const char*, not - char* args - - * Installed EH2.cc in src - -Mon Feb 19 08:34:51 1990 Doug Lea (dl at g.oswego.edu) - - * installed Ngo's PlotFile3D in libg++/etc - -Sat Feb 17 05:28:25 1990 Doug Lea (dl at g.oswego.edu) - - * installed Schmidt's gperf, trie-gen, and Patricia revisions - - * String.cc pos <= 0 should be pos < 0 - -Tue Feb 13 08:21:11 1990 Doug Lea (dl at g.oswego.edu) - - * Itolong (Integer.cc) patch from salzman@rand.org - -Mon Feb 12 08:21:07 1990 Doug Lea (dl at g.oswego.edu) - - * allowed separate inclusion of ostream.h, istream.h and/or stream.h - -Thu Feb 8 07:02:49 1990 Doug Lea (dl at g.oswego.edu) - - * PlotFile: patches from ngo for Convex byte-ordering. - -Tue Feb 6 06:29:11 1990 Doug Lea (dl at g.oswego.edu) - - * Vec.ccP: sort() killed goto, replaced with nested if's, - since g++ complains about binding contours. - -Sat Feb 3 08:30:06 1990 Doug Lea (dl at g.oswego.edu) - - * Getopt.h: opterr is public, not private - - * builtin.cc, Random.cc, streambuf.cc broken into little pieces - - * std.h getpgrp, setpgrp now have (...) signatures, since - some versions on some systems have arguments. - - * put in malloc revision - - * prepend-header: globbing changes via ngo's patches - - * Plex: fixed declaration mismatches for fill - -Tue Jan 30 10:22:35 1990 Doug Lea (dl at g.oswego.edu) - - * kmp.cc: modified to use libg++ GetOpt, not libc getopt - -Wed Jan 24 05:47:53 1990 Doug Lea (dl at g.oswego.edu) - - * broke out struct xyzzy from builtin.cc into its own file in /src - -Sun Jan 21 09:44:10 1990 Doug Lea (dl at g.oswego.edu) - - * sys/types.h: protect wchar_t and ptrdiff_t from /usr/include version - - * stddef.h: wchar_t now defaults as unsigned short - -Sat Jan 20 08:51:01 1990 Doug Lea (dl at g.oswego.edu) - - * sys/file.h KERNEL now defined only if ultrix. - Also a typo: file_f should be file_h - -Fri Jan 19 05:18:03 1990 Doug Lea (dl at g.oswego.edu) - - * malloc.c: added #ifndef NO_NEW_HANDLER, so malloc.c - compilable in C environments with no new handlers, and - other #ifdefs to make it C++-compilable as well. - -Tue Jan 16 04:54:27 1990 Doug Lea (dl at g.oswego.edu) - - * libg++-1.36.3 released. - - * etc/benchmarks: enabled various options, now that - g++ works with them. - - * values.h vax MAX/MINFLOAT changed to be same as expected by gcc. - - * streambuf.cc: Filebuf::overflow(): Fp->eof() is not an - error condition. - - * std.h, stdio.h: more extern C fns declared as - returning int, not void when not specified as void by ANSI - or C man pages. - -Sat Jan 13 13:41:29 1990 Doug Lea (dl at g.oswego.edu) - - * stdio.h: puts returns int - -Fri Jan 12 05:49:09 1990 Doug Lea (dl at g.oswego.edu) - - * etc/lf/Dirent.h closedir returns void on some system, so - Dirent versions do too. - -Wed Jan 10 10:01:15 1990 Doug Lea (dl at g.oswego.edu) - - * Rational.h: 175 typo - -Mon Jan 8 09:43:14 1990 Doug Lea (dl at g.oswego.edu) - - * file.h: more protection against getting bad fn declarations - from /usr/include - - * builtin.cc: removed dependency on float.h - - * String.h Join, replicate need to be friends - -Sat Jan 6 08:48:18 1990 Doug Lea (dl at g.oswego.edu) - - * from rfg: minor DGUX accomodations in std.h, stdio.h, - math.h - -Fri Jan 5 06:41:02 1990 Doug Lea (dl at g.oswego.edu) - - * from Widen: added warning about tCurses needing linefeeds - on broken libcurses, fixed misc typos, added cfree() to malloc.c - - * math.h -- added inline defs of isnan and isinf for sequents - -Wed Jan 3 08:29:43 1990 Doug Lea (dl at g.oswego.edu) - - * builtin.cc: Deleted global _libgxx_io_oblast: no longer needed - killed extern decls of it elsewhere. - - * Strings, Integers: finished/cleaned up previous changes - -Tue Jan 2 10:43:29 1990 Doug Lea (dl at g.oswego.edu) - - * Integer.cc: isolated copy and clear calls to allocation fns - - * Strings: Removed StrTmp class, added double concatenation - -Mon Dec 11 08:31:48 1989 Doug Lea (dl at g.oswego.edu) - - * AVLSet.cc op &= plugged little memory leak: when u is exhausted, - but t isn't, delete rest of t. - - * AVLMap: _delete: cont field wasn't copied in a case it should - have been - - * merged tSet2 back into tSet and tBag2 back into tBag - - * BitSet.cc (BitSetCompl) ensure all 1's in s[0] when - complementing empty set - - * builtin.cc (return_elapsd_time, set_timer) No longer #ifdef'ed - for machines -- new .h organization should work for all. - - * builtin.cc (lg) redeclared as unsigned->long - - * DGUX patches from rfg installed - -Tue Dec 5 11:58:51 1989 Doug Lea (dl at g.oswego.edu) - - * BitSet, Integer, Rational: added constness, removed Tmp - classes, and used named return values - -Sat Dec 2 06:21:12 1989 Doug Lea (dl at g.oswego.edu) - - * builtin.cc (dtoa) #if _IEEE != 0 handle isnan, isinf - - * builtin.cc (itoa) force unsigned division in case - num == MININT - -Fri Dec 1 10:08:21 1989 Doug Lea (dl at g.oswego.edu) - - * istream::operator>> clear eof if at eof but got something valid - - * String::match and Regex::match return -1 on failure, since - 0 could be a legal value - - * gnulib3.c: Commented out ON_EXIT stuff. On Suns, for some - reason, on_exit routines don't link into libraries right. - - * std.h: fixed getopt proto - - * stat.h : added fn protos - - * installed Schmidt's reorganization of etc - - * math-68881.h fix paren error noted on bug-gcc list - - * CursesWindow(WINDOW*) initialize sib - - * Renamed AllocQueue to AllocRing - - * test.hello.cc #ifdefs for MIPSEL - - * Plex::del_chunk() delete the chunk, not just the chunk's data - -Sat Nov 25 12:50:06 1989 Doug Lea (dl at g.oswego.edu) - - * VStack, VQueue: add operator =() - - * Obstack::Obstack don't allocate on constructor, just on - first use - - * String::_gsub: don't build new rep if no matches - - * builtin.h: added more versions of abs - - * installed new malloc, and new.{h, cc} - - -Thu Nov 23 06:20:17 1989 Doug Lea (dl at g.oswego.edu) - - * added Schmidt's g++dep to etc - - * math.h: additions for anint(), etc., HP HAVE_FPU - -Wed Nov 22 14:48:24 1989 Doug Lea (dl at g.oswego.edu) - - * Added Schmidt's trie-gen to libg++/etc - -Tue Nov 21 08:50:47 1989 Doug Lea (dl at g.oswego.edu) - - * streambuf: eptr is now the pointer to the last valid - char in buffer, not the fence pointer, for AT&T compatibilty - - * stream, streambuf : Added line buffered put's as default - must #define NO_LINE_BUFFER_STREAMBUF to override - -Mon Nov 20 09:52:47 1989 Doug Lea (dl at g.oswego.edu) - - * Plex: finish previous change: add_low, add_high don't introduce - straggling chunks - - * new.h: typo, plus add default placement version of new() - - * PlotFile, BitString, Fix16 .h's: more cfrontisms - -Sun Nov 19 07:38:36 1989 Doug Lea (dl at g.oswego.edu) - - * removed File::operator FILE*() because it can lead - to ambiguities. - - * incorporated cfront-dependent #ifdefs, etc. from Schmidt - - * Fix24: integrated patches from wang - -Sat Nov 18 07:17:04 1989 Doug Lea (dl at g.oswego.edu) - - * XPlex, RPlex, MPlex (del_low, del_high) old straggling - empty chunks weren't being deleted. fixed. - -Thu Nov 16 05:56:43 1989 Doug Lea (dl at g.oswego.edu) - - * resource.h: added getrlimit, setrlimit - -Wed Nov 15 05:54:46 1989 Doug Lea (dl at g.oswego.edu) - - * String.h: typo const& Regex => const Regex& - -Fri Nov 10 06:45:55 1989 Doug Lea (dl at g.oswego.edu) - - * Makefile: force submakes in non-gnumake fashion - -Thu Nov 9 09:32:21 1989 Doug Lea (dl at g.oswego.edu) - - * Curses.cc, curses.h, ctype.h: patches based on darrlyo's stuff. - - * Fix.cc : *Correctly* installed ++i patch! - -Wed Nov 8 06:19:39 1989 Doug Lea (dl at g.oswego.edu) - - * stdio.h now is now sub-included in other .h's needing USG-based - info. - - * new etc/c++-mode.el from detlefs - - * etc/Makefile: -DETAGS for etags.c - - * more HPUX patches from darrylo and mike fion - -Tue Nov 7 07:23:25 1989 Doug Lea (dl at g.oswego.edu) - - * Fix.{h, cc}: cleanup in search of memory leaks - - * Added -DNO_GNULIB3 option in top-level Makefile - -Mon Nov 6 05:53:42 1989 Doug Lea (dl at g.oswego.edu) - - * std.h, stdio.h, ctype.h.... HPUX and DGUX patches from - cole & darrylo - - * tests/Makefile tCurses taken out of checktests - - * Bitset.cc: fixed underallocation in BitSettoa according to - patch from darrylo@hpsrdmo.hp.com - -Sun Nov 5 06:45:26 1989 Doug Lea (dl at g.oswego.edu) - - * gnulib3, Incremental.h, test.hello.cc: patched - via Eirik Fuller's incremental loading fixes - -Fri Nov 3 11:22:39 1989 Doug Lea (dl at g.oswego.edu) - - * 1.36.0 released, after misc cleanup - -Tue Oct 31 09:44:32 1989 Doug Lea (dl at g.oswego.edu) - - * added Rich Murphey's graph program to libg++/etc - -Mon Oct 30 10:13:07 1989 Doug Lea (dl at g.oswego.edu) - - * sys/file.h: include types.h & maybe fcntl.h Some folks need them - - * std.h: index, bcopy, etc. now inline, not macro if USG - - * streambuf.h: sputback returns success; - stream.h istream::putback/unget: set(_fail) if bad - -Tue Oct 24 16:53:05 1989 Doug Lea (dl at g.oswego.edu) - - * stddef.h -- now really defines size_t. OK via new sys/types.h - fake-out. - - * time.h -- now includes //usr/include/time.h too - -Sun Oct 22 07:58:36 1989 Doug Lea (dl at g.oswego.edu) - - * String.h, cc: reworked to allow proper operation for consts - (some new stuff #ifdef'ed out because of g++ problems) - -Sat Oct 21 15:29:55 1989 Doug Lea (dl at g.oswego.edu) - - * Fix.cc: (new_Fix) cure for d < 0 problem from eirik fuller - - * builtin.cc: added dtoa - - * AllocQueue.h,cc: added it & use elsewhere for building - formatting & ascii conversions - -Wed Oct 18 05:37:11 1989 Doug Lea (dl at g.oswego.edu) - - * Fix16.cc, Fix24.cc: Fixed operator / per wang's suggestions - -Tue Oct 17 06:47:25 1989 Doug Lea (dl at g.oswego.edu) - - * stream.cc, Integer.cc: istream op >>, fixed to not read - after EOF when decoding numbers - -Mon Oct 16 15:33:11 1989 Doug Lea (dl at g.oswego.edu) - - * added ostream << long long, and itoa's to handle - - * values.h, stdio.h, Fix.cc, File.cc: things for convex from - convex!csmith@uxc.cso.uiuc.edu - -Sat Oct 14 07:19:30 1989 Doug Lea (dl at g.oswego.edu) - - * time.h: typedef'ed timezone to c_proto_timezone if not USG - -Wed Oct 11 09:42:39 1989 Doug Lea (dl at g.oswego.edu) - - * Makefiles: fixed various typos - - * misc: cleaned up enum clashes reported with -Wenum-clash - - * stream.cc Added #ifdefs to use filebufs for standard streams - if Filebufs give people trouble. - -Tue Oct 3 07:02:56 1989 Doug Lea (dl at g.oswego.edu) - - * setjmp.h: now #includes host /usr/include/setjmp.h - -Mon Oct 2 16:00:59 1989 Doug Lea (dl at g.oswego.edu) - - * commented out gcc constness in revised Complex.h since - it's still officially illegal to declare fns with refs(ptrs) - as const - - * incorporated new gperf from schmidt - - * added dhrystone benchmark to etc - -Sat Sep 30 09:02:07 1989 Doug Lea (dl at g.oswego.edu) - - * Complex.h: revamped to use const, etc. - -Fri Sep 29 06:58:56 1989 Doug Lea (dl at g.oswego.edu) - - * added src/EH.cc from tiemann - - * SLList.hP now #include's the .defs file - - * CHSet, CHBag, CHMap, VHSet, VHBag, VHMap -- changed ints - to unsigned ints to ensure unsigned operations throughout. - -Mon Sep 25 07:32:11 1989 Doug Lea (dl at g.oswego.edu) - - * added new.h - -Sun Sep 24 05:31:50 1989 Doug Lea (dl at g.oswego.edu) - - * tgwrapper.cc: added init_nil to avoid crashes on exit. - - * other miscellaneous cleanup (fixed enum/int clashes, etc.) to - adapt to latest g++-1.36.0- - - * bool enum now in bool.h - -Thu Sep 14 06:18:46 1989 Doug Lea (dl at g.oswego.edu) - - * sys/socket.h: select must have void* args, since different - systems use int* or fd_set* - -Wed Sep 13 11:38:19 1989 Doug Lea (dl at g.oswego.edu) - - * builtin.cc: added unsigned versions of itoa, hex, dec, oct - -Tue Sep 12 09:28:01 1989 Doug Lea (dl at g.oswego.edu) - - * more misc. cleanup to avoid warnings: removed redundant - type information from declarations of all coercion operators. - -Sat Sep 9 06:25:03 1989 Doug Lea (dl at g.oswego.edu) - - * (everywhere) miscellaneous aesthetic cleanup to minimize g++ - warning messages. - - * (lots of files) used 'virtual fn() = 0' for pure virtual - functions, removing old `error(unimplemented...)' constructs. - allowed deletion of Stack.ccP, Queue.ccP, Deque.ccP files - which did only this. - - * took all defines out of libconfig.h, and killed it - HAVE_VPRINTF, etc -> stdio.h - CHAR_PER_LONG, etc -> Integer.cc - SHOULD_FREE_TO_REALLOC -> (no longer needed, killed) - USG -> people should run g++ with -DUSG now - -Fri Sep 8 06:48:58 1989 Doug Lea (dl at g.oswego.edu) - - * added Clark's version of etags that handles c++, to etc/ - - * moved special sparc alloca decl from libconfig.h to std.h - - * std.h, math.h, ... killed `overload' declarations - - * etc/getopt* => src/GetOpt.cc, g++-include/GetOpt.h, with - various corresponding changes - -Wed Sep 6 09:15:50 1989 Doug Lea (dl at g.oswego.edu) - - * math.h renamed `struct exception' to `libm_exception' - - * regex.c converted to use prototypes, etc. from schmidt - -Tue Sep 5 06:43:37 1989 Doug Lea (dl at g.oswego.edu) - - * new c++-mode.el from detlefs - - * added sys/param.h, which #undefs common macros, but keeps - needed constants - - * Integer.h: rearranged ordering of some inlines to please g++ - - * Fix.h: need new constructor Fix(int, _Frep*) to please g++ - - * added __xyzzy hack from tiemann to builtin.cc - - * added gnulib3 from tiemann - -Thu Aug 31 07:36:42 1989 Doug Lea (dl at g.oswego.edu) - - * more USG stuff from Klossner (stdio.h, libconfig.h, - values.h, ctype.h) - -Sun Aug 27 08:30:15 1989 Doug Lea (dl at g.oswego.edu) - - * genclass: changed to take output filename prefix argument - to avoid long file names on SYSV; tests files change accordingly - - * installed gperf update from schmidt - - * tests: added runtests, checktests to Makefile. Some tests - modified to suit. - -Sat Aug 26 09:00:14 1989 Doug Lea (dl at g.oswego.edu) - - * Plex, PHPQ files: deleted const qualifiers for some params - as temporary measure until all containers revised to use - const qualifiers as needed. - - * curses.h macros converted into inlines - - * added RankedAVLMap, based on code from paul%lfcs.ed.ac.uk - - * moved non-ANSI stuff (TRUE, etc., ) from stddef.h to builtin.h - - * added more USG stuff sent from rfg, grandi, cole, to standard headers - - * std.h: added #ifdef USG section for USG->BSD conversions - - * Makefiles: made more things adjustable, better USG support - - * PHPQ.ccP: (preallocate) added missing size argument to vector delete - -Fri Aug 25 12:25:12 1989 Doug Lea (dl at g.oswego.edu) - - * streambuf.cc: dumb error in filebuf::overflow - -Thu Aug 24 11:46:16 1989 Doug Lea (dl at g.oswego.edu) - - * libconfig.h, values.h: #defines for sony from jkp - -Wed Aug 23 06:54:43 1989 Doug Lea (dl at g.oswego.edu) - - * Fix16.h, Fix32.h: declared op* as friends correctly - - * String.h: declared StrTmp op + as friends of String - -Mon Aug 21 07:02:53 1989 Doug Lea (dl at g.oswego.edu) - - * Poisson.h, Lognormal.h: add missing `public' - - * assert.h: abort() declared volatile - - * Vec.ccP: made gsort static - - * std.h: added rewind & bsearch - - * Makefiles: deleted -fchar-charconst - -Thu Aug 10 07:31:37 1989 Doug Lea (dl at g.oswego.edu) - - * builtin.{h, cc}: added str(const char*, int width = 0) - - * streambuf.cc: init_streambuf_ptrs: postpone action if fp->_cnt 0 - (apparently needed for some USG systems) - - * stream.cc: get, getline: match AT&T 1.2 _fail conditions - -Sun Aug 6 07:16:19 1989 Doug Lea (dl at g.oswego.edu) - - * stream.cc, File.cc get(char[], int, char) read too many chars - -Thu Jul 20 09:42:44 1989 Doug Lea (dl at g.oswego.edu) - - * adapted more C-compatibility .h files from Interviews - -Wed Jul 19 09:23:27 1989 Doug Lea (dl at g.oswego.edu) - - * installed more C-compatibilty files: pwd.h, grp.h time.h - -Mon Jul 17 07:37:35 1989 Doug Lea (dl at g.oswego.edu) - - * installed Interviews/et++ compatible (I hope) signal.h - - * installed new version of gperf from schmidt - - * std.h: declared abort() and exit() as volatile - - * builtin.cc: typo in gcd - - * math.h: added overload decl for atan, etc - - * VHMap.cc: removed assumption that operator = returns value. - - * Makefiles: default dir is /usr/gnu/... not /usr/local - - * setjmp.h: fixed constants for sun to match those in - sun /usr/include files, added ns32000 - - * BSTSet.ccP added new linear-time rebalancing algorithm - - * builtin.cc: SYSV versions of timing stuff from ron cole - - * File.{h,cc} fixed File::tell, added O_CREAT to exclusive - access open, added fill(), flush(char). - - * incorporated new streams: stream.{h,cc}, streambuf.{h, cc}, - libg++.texinfo - - -Sat May 20 07:42:11 1989 Doug Lea (dl at rocky.oswego.edu) - - * math.h,math-68881.h: incorporated Fyfe's fixes to extern "C" problems - -Tue May 16 05:52:33 1989 Doug Lea (dl at rocky.oswego.edu) - - * RNG.cc ifdef _IEEE_ fixed to if _IEEE == 1 - - * Installed Staelin's prototype Makefile updates - -Mon May 15 06:25:12 1989 Doug Lea (dl at rocky.oswego.edu) - - * BitString.h: g++ optimizer bug workaround in left_trim - - * math-68881.h - fgetman (not fgetmant) fix from widen - -Sat May 13 11:00:35 1989 Doug Lea (dl at rocky.oswego.edu) - - * changes from tiemann for constructs of form X::f() - changed to this->X::f(), necessary now that static members - are implemented. [postscript: no, it wasn't necessary] - - * libg++.texinfo: misc documentation updates - -Fri May 12 05:06:06 1989 Doug Lea (dl at rocky.oswego.edu) - - * (lots of places) added friends and other minor changes - to adapt to new ``correct'' (but losing) g++ interpretation - of `protected:' - - * re-inserted `overload' in .h files -- tiemann - says that gdb needs these for now - - * stream.cc: eatwhite was inline by mistake. fixed. - -Thu May 11 07:31:06 1989 Doug Lea (dl at rocky.oswego.edu) - - * List.ccP: initializer class for Nil, since can't always use - { ... } initializer. Also made `head' a synonym for `get', - per request. - - * installed changes to etc files from schmidt - - * String.cc Scopy: return &NilSrep, not 0 for null - - * added math-68881.h to g++-include (from grunwald) - -Sun May 7 08:38:10 1989 Doug Lea (dl at rocky.oswego.edu) - - * catch-up day!: - converted header files to use extern "C" and #pragma once - killed `overload' declarations everywhere - renamed test files - added the beginnings of SYSV (USG) support - included some useful stuff for Suns in top Makefile (from Guilmette) - cleaned up other Makefiles - added File::gets (from Schmidt) - moved gperf from etc to a top level subdir - added the useless char* chr(ch) to builtin.h - genclass puts dots in file names to use Staelins GNU Makefile stuff - (also added his `prepend-header' utility) - added Schmidt's getopt stuff into etc. - New versions of fixpoint classes from Baudendistel - (needed to change set_overflow_handler to - set_{FixXX}_overflow_handler - for each FixXX, since overloads clash on typedef'ed fn types) - Adapted Schmidt's new quicksort for Vec class - -Fri Apr 28 16:26:17 1989 Doug Lea (dl at rocky.oswego.edu) - - * ACG.cc: fixed ~ACG per grunwald - -Thu Apr 20 05:22:46 1989 Doug Lea (dl at rocky.oswego.edu) - - * List.hP: first for nil list now returns null Pix - - * Integer.cc: rshift fixed problem with 0 shifts - -Mon Apr 10 05:17:04 1989 Doug Lea (dl at rocky.oswego.edu) - - * stream.h, PlotFile.h added explicit `private' for subclasses - -Sat Mar 18 06:08:30 1989 Doug Lea (dl at rocky.oswego.edu) - - * String.[h,cc]: added Regex::match_info - -Fri Mar 17 14:37:12 1989 Doug Lea (dl at rocky.oswego.edu) - - * stream.h: istream >> char now eats whitespace. - - * builtin.h: overloaded `even', `odd' - -Thu Mar 9 06:43:43 1989 Doug Lea (dl at rocky.oswego.edu) - - * Map.ccP: typo in Map::contents => - - * stdio.h : inserted coercion in putc macro to avoid incorrect - sign extension. - -Tue Mar 7 05:35:52 1989 Doug Lea (dl at rocky.oswego.edu) - - * List.hP: List::push no longer incorrectly calls dereference - - * Inserted patches to etc stuff from Doug Schmidt - -Sun Mar 5 07:57:01 1989 Doug Lea (dl at rocky.oswego.edu) - - * stream.h: added File::check_state to public functions - - * BitSet.cc: longtoBitSet: Fixed typo - -Sat Mar 4 10:06:24 1989 Doug Lea (dl at rocky.oswego.edu) - - * installed CursesWindow files - - * miscellaneous corrections to test files in light of - g++-1.34 changes - -Fri Mar 3 06:07:37 1989 Doug Lea (dl at rocky.oswego.edu) - - * incorporated new version of Doug Schmidt's gperf - - * BitString.cc: fixed reverse searching - -Sun Feb 26 05:44:28 1989 Doug Lea (dl at rocky.oswego.edu) - - * assert : killed old assert.cc, adapted gcc assert.h - -Sat Feb 25 09:23:35 1989 Doug Lea (dl at rocky.oswego.edu) - - * tests, libg++.texinfo: miscellaneous updates - - * stddef.h NULL is now just `0', not (void*)0 - - * Makefile: added `prefix' as in g++ Makefile - - * put a new c++-mode.el from david detlefs in etc - - * BitString.[h,cc] BitSet[h,cc] now use unsigned short arrays instead - of unsigned longs to avoid long i; i >> 32, which does not - work on Sun4s and probably other machines. Simplified - a few shift & mask constructs accordingly. - - * values.h, libconfig.h: support for sequent from - Johan Widen - - * Fix.h: repaired type mismatches - - * String.[cc,h] gsub now returns number of matches - - * String.cc gsub(Regex...): repaired using patches - from kadmon!jason@mtxinu.com - - * stream.h scan didn't return *this if fail -- fixed. - - * File.cc get(char*...): get of an empty line not a _fail condition - - * RNG.[h,cc] installed new code from grunwald - - -Tue Feb 7 05:53:23 1989 Doug Lea (dl at rocky.oswego.edu) - - * Integer.h,cc Added optional base to atoI via code from per bothner - - * String.h,cc Added `freq' method in String to count occurrences - using code from john willis - -Mon Feb 6 07:25:06 1989 Doug Lea (dl at rocky.oswego.edu) - - * BitSet.cc:op <=, < now work if first arg shorter than second; - clear() fixed. - * stream.h, stream.cc: made ostream<<(char*) non-inline - -Sun Feb 5 05:31:36 1989 Doug Lea (dl at rocky.oswego.edu) - - * test19.cc: typo c.empty fixed to c.empty() - -Tue Jan 31 05:51:36 1989 Doug Lea (dl at rocky.oswego.edu) - - * String.h: contains(Regex) return fact that search returns >= 0, - not just raw result. - - * Fix.h: correct protection problem in op* - - * replace regex.c with emacs 18.52 version - -Fri Jan 27 06:29:20 1989 Doug Lea (dl at rocky.oswego.edu) - - * AVLSet.ccP, AVLMap.ccP - check to see if root - is null before trying to delete elements - - * libg++/Makefile - change install of libg++ to cd to src - -Sat Jan 14 06:03:33 1989 Doug Lea (dl at rocky.oswego.edu) - - * fixed info node pointers in libg++.texinfo - -Wed Jan 11 06:20:37 1989 Doug Lea (dl at rocky.oswego.edu) - - * libg++-1.32.0 released - * Starting to use ChangeLog as of today - diff --git a/gnu/lib/libg++/Makefile b/gnu/lib/libg++/Makefile index ac428ca1da..6d6b02d6c8 100644 --- a/gnu/lib/libg++/Makefile +++ b/gnu/lib/libg++/Makefile @@ -1,5 +1,42 @@ -# %W% (Berkeley) %G% +SUBDIR= libg++ genclass -SUBDIR= libg++ g++-include genclass +INCLUDEDIRS= iostream libg++ g++-include + +beforeinstall: + @-if [ ! -d ${DESTDIR}/usr/include/g++ ]; then \ + mkdir ${DESTDIR}/usr/include/g++; \ + chown ${BINOWN}.${BINGRP} ${DESTDIR}/usr/include/g++; \ + chmod 755 ${DESTDIR}/usr/include/g++; \ + fi + @-if [ ! -d ${DESTDIR}/usr/include/g++/gen ]; then \ + mkdir ${DESTDIR}/usr/include/g++/gen; \ + chown ${BINOWN}.${BINGRP} ${DESTDIR}/usr/include/g++/gen; \ + chmod 755 ${DESTDIR}/usr/include/g++/gen; \ + fi + @-if [ ! -d ${DESTDIR}/usr/include/g++/sys ]; then \ + mkdir ${DESTDIR}/usr/include/g++/sys; \ + chown ${BINOWN}.${BINGRP} ${DESTDIR}/usr/include/g++/sys; \ + chmod 755 ${DESTDIR}/usr/include/g++/sys; \ + fi + @-for i in ${INCLUDEDIRS}; do \ + echo installing includes from $$i ; \ + (cd $$i; for j in *.[ih]; do \ + cmp -s $$j ${DESTDIR}/usr/include/g++/$$j || \ + install -c -o ${BINOWN} -g ${BINGRP} -m 444 $$j \ + ${DESTDIR}/usr/include/g++/$$j; \ + done); \ + done + @echo installing includes from g++-include/sys + @(cd g++-include/sys ; for j in *.[ih]; do \ + cmp -s $$j ${DESTDIR}/usr/include/g++/sys/$$j || \ + install -c -o ${BINOWN} -g ${BINGRP} -m 444 $$j \ + ${DESTDIR}/usr/include/g++/sys/$$j; \ + done) + @echo installing includes from g++-include/gen + @(cd g++-include/gen ; for j in *.*P; do \ + cmp -s $$j ${DESTDIR}/usr/include/g++/gen/$$j || \ + install -c -o ${BINOWN} -g ${BINGRP} -m 444 $$j \ + ${DESTDIR}/usr/include/g++/gen/$$j; \ + done) .include diff --git a/gnu/lib/libg++/Makefile.gnu b/gnu/lib/libg++/Makefile.gnu deleted file mode 100644 index 9ebb2aa763..0000000000 --- a/gnu/lib/libg++/Makefile.gnu +++ /dev/null @@ -1,276 +0,0 @@ -# Makefile for GNU C++ class library (libg++) -# Copyright (C) 1989 Free Software Foundation, Inc. -# written by Doug Lea (dl@rocky.oswego.edu) - -#This file is part of GNU libg++. - -#GNU libg++ is free software; you can redistribute it and/or modify -#it under the terms of the GNU General Public License as published by -#the Free Software Foundation; either version 1, or (at your option) -#any later version. - -#GNU libg++ is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. - -#You should have received a copy of the GNU General Public License -#along with GNU libg++; see the file COPYING. If not, write to -#the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - -########################################################################### -# -# Directories, paths, compilation flags and program names. -# -# Please make sure these are correct. -# - -# ------ source locations - -# Manually set PWD to *this* directory if you are not using gnu make -PWD := $(shell pwd) -#PWD=/home/dl/libg++ - -# source include directory -SRCIDIR= $(PWD)/g++-include - -# the genclass program -GENCLASS=$(PWD)/genclass -# and its directory of prototype files -PROTODIR=$(PWD)/g++-include/gen - -# ------ installation destinations -# ------ You will require write-permission on the destination directories -# ------ in order to `make install' - -# set `prefix' to something else if you want to install things -# in nonstandard places - -prefix =/usr/gnu - -# libg++.a destination -LIBDIR = $(prefix)/lib - -# executables directory: location to install the genclass class generator -BINDIR = $(prefix)/bin - -# directory to install man pages -MANDIR= $(prefix)/man - -# location to install include file directory -IDIR = $(prefix)/lib/g++-include - - -# ------- System-dependent defines -# ------- use the second form of each for SystemV (USG) - -# g++ flags -OSFLAG= -#OSFLAG = -DUSG - -# other compilation control flags -- use any combination - -# use this only if you have a strange stdio implementation -#XTRAFLAGS = -DDEFAULT_filebuf - -# use this if you do not want gnulib3 in libg++.a -#XTRAFLAGS = -DNO_GNULIB3 - -# use this if you need COFF encapulation defined in gnulib3 -#XTRAFLAGS = -DCOFF_ENCAPSULATE - -# use this if you want to disable line buffering for stream output -#XTRAFLAGS = -DNO_LINE_BUFFER_STREAMBUF - -# Use this to disable placing libg++ version of malloc in libg++.a -#XTRAFLAGS = -DNO_LIBGXX_MALLOC - -# Please use this & send me some results of malloc_stats() sometime -# (it is off by default, since stat gathering hurts performance) -#XTRAFLAGS = -DMALLOC_STATS - -#suggested for NeXT by cdr@acc.stolaf.edu -#XTRAFLAGS = -DNO_GNULIB3 -DNO_LIBGXX_MALLOC - -# ld or ld++ flags -OSLDFLAG = -#OSLDFLAG= -lPW - -# how to install -INSTALL=install -c -#INSTALL=cp - -# ranlib if necessary -RANLIB=ranlib -#RANLIB=echo - -# which make? -MAKE=make - -#which ar? -AR=ar - -# not used, but convenient for those who preprocess things while compiling -SHELL=/bin/sh - - -# ------ compiler names - -# GNU C++ compiler name -GXX = g++ -#GXX=gcc - -# GNU CC compiler name (needed for some .c files in libg++.a) -CC = gcc - -# GNU loader -LDXX = $(LIBDIR)/gcc-ld -#LDXX = $(LIBDIR)/gcc-ld++ - -# crt0+.o location (for dynamic loading tests) -GXXCRT1=$(LIBDIR)/crt1+.o - -# ------ Other compilation flags -# ------ modify as you like -- the ones here are sheer overkill -# ------ However, You MUST compile libg++.a with EITHER -O OR -# ------ -DUSE_LIBGXX_INLINES or both - -GXX_OPTIMIZATION_FLAGS= -O -fstrength-reduce -felide-constructors -fschedule-insns -fdelayed-branch -fsave-memoized - -#GXX_OPTIMIZATION_FLAGS=-DUSE_LIBGXX_INLINES - -GCC_OPTIMIZATION_FLAGS= -O -fstrength-reduce -fdelayed-branch - -DEBUG_FLAGS= -g - -#use this only if you like to look at lots of useless messages -#VERBOSITY_FLAGS= -Wall -v -VERBOSITY_FLAGS= -Wall - -GXX_INCLUDE_DIRS= -I$(SRCIDIR) - -GCC_INCLUDE_DIRS= -I$(prefix)/lib/gcc-include -I/usr/include -I$(SRCIDIR) - -#use this only if you use GNU as (gas) or other assemblers that -#can read from pipes. -PIPE_AS= -pipe -#PIPE_AS= - -# Flags for all C++ compiles -GXXFLAGS = $(OSFLAG) $(GXX_INCLUDE_DIRS) $(DEBUG_FLAGS) $(GXX_OPTIMIZATION_FLAGS) $(VERBOSITY_FLAGS) $(XTRAFLAGS) $(PIPE_AS) - -# Flags for all C compiles -CFLAGS= $(OSFLAG) $(GCC_INCLUDE_DIRS) $(DEBUG_FLAGS) $(GCC_OPTIMIZATION_FLAGS) $(VERBOSITY_FLAGS) $(XTRAFLAGS) $(PIPE_AS) - -# g++ load time flags -GXXLDFLAGS = -L$(PWD)/src -lg++ -lm $(OSLDFLAG) - -# Comment out the next line to disable incremental linking test -# (this test NOT included in 1.39.0, so don't re-enable) -#TEST0=test0 -TEST0= - -########################################################################### -# -# compilation actions -# - - - - -src: FORCE - (cd src; $(MAKE) GXX="$(GXX)" GXXFLAGS="$(GXXFLAGS)" GXXLDFLAGS="$(GXXLDFLAGS)" LIBDIR="$(LIBDIR)" SRCIDIR="$(SRCIDIR)" CC="$(CC)" CFLAGS="$(CFLAGS)" RANLIB="$(RANLIB)" LDXX="$(LDXX)" GXXCRT1="$(GXXCRT1)" MAKE="$(MAKE)" prefix="$(prefix)" VPATH="$(SRCIDIR)" AR="$(AR)" PROTODIR="$(PROTODIR)" GENCLASS="$(GENCLASS)") - -tests: FORCE - (cd tests; $(MAKE) checktests GXX="$(GXX)" GXXFLAGS="$(GXXFLAGS)" GXXLDFLAGS="$(GXXLDFLAGS)" LIBDIR="$(LIBDIR)" SRCIDIR="$(SRCIDIR)" CC="$(CC)" CFLAGS="$(CFLAGS)" RANLIB="$(RANLIB)" LDXX="$(LDXX)" GXXCRT1="$(GXXCRT1)" MAKE="$(MAKE)" prefix="$(prefix)" AR="$(AR)" TEST0="$(TEST0)" PROTODIR="$(PROTODIR)" GENCLASS="$(GENCLASS)") - -etc: FORCE - (cd etc; $(MAKE) GXX="$(GXX)" GXXFLAGS="$(GXXFLAGS)" GXXLDFLAGS="$(GXXLDFLAGS)" LIBDIR="$(LIBDIR)" SRCIDIR="$(SRCIDIR)" CC="$(CC)" CFLAGS="$(CFLAGS)" RANLIB="$(RANLIB)" LDXX="$(LDXX)" GXXCRT1="$(GXXCRT1)" MAKE="$(MAKE)" prefix="$(prefix)" AR="$(AR)" PROTODIR="$(PROTODIR)" GENCLASS="$(GENCLASS)") - -run_etc: FORCE - (cd etc; $(MAKE) run_tests GXX="$(GXX)" GXXFLAGS="$(GXXFLAGS)" GXXLDFLAGS="$(GXXLDFLAGS)" LIBDIR="$(LIBDIR)" SRCIDIR="$(SRCIDIR)" CC="$(CC)" CFLAGS="$(CFLAGS)" RANLIB="$(RANLIB)" LDXX="$(LDXX)" GXXCRT1="$(GXXCRT1)" MAKE="$(MAKE)" prefix="$(prefix)" AR="$(AR)" PROTODIR="$(PROTODIR)" GENCLASS="$(GENCLASS)") - -gperf: FORCE - (cd gperf; $(MAKE) GXX="$(GXX)" GXXFLAGS="$(GXXFLAGS)" GXXLDFLAGS="$(GXXLDFLAGS)" LIBDIR="$(LIBDIR)" SRCIDIR="$(SRCIDIR)" CC="$(CC)" CFLAGS="$(CFLAGS)" RANLIB="$(RANLIB)" LDXX="$(LDXX)" GXXCRT1="$(GXXCRT1)" prefix="$(prefix)" AR="$(AR)" PROTODIR="$(PROTODIR)" GENCLASS="$(GENCLASS)") - -genclass: genclass.sh - echo "/^PROTODIR=/c\\" > sedscript - echo "PROTODIR=$$\{PROTODIR-$(IDIR)/gen\}" >> sedscript - sed -f sedscript < genclass.sh > genclass - chmod 0755 genclass - rm -f sedscript - -#to force sub-makes -FORCE: - - -########################################################################### -# -# Installation -# - -MAKE_ENVIRON=\ - BINDIR=$(BINDIR) \ - LIBDIR=$(LIBDIR) \ - MANDIR=$(MANDIR) \ - INSTALL="$(INSTALL)" - -all: src tests genclass etc gperf - -install: install-lib install-include-files install-progs - -install-lib: - (cd src; $(MAKE) $(MAKE_ENVIRON) install) - -install-progs: - (cd etc; $(MAKE) $(MAKE_ENVIRON) install) - (cd gperf; $(MAKE) $(MAKE_ENVIRON) install) - $(INSTALL) genclass $(BINDIR) - -install-include-files: - -mkdir $(IDIR) - -mkdir $(IDIR)/sys - -mkdir $(IDIR)/gen - cd $(SRCIDIR); \ - FILES=`find . ! -type d -print`; \ - cd gen; \ - GFILES=`find . ! -type d -print`;\ - cd $(IDIR); \ - rm -fr $$FILES; \ - rm -f $$GFILES; \ - cd $(SRCIDIR); \ - FILES=`find . ! -type d -print`; \ - for file in $$FILES; do \ - rm -f $(IDIR)/$$file; \ - cp $$file $(IDIR)/$$file; \ - chmod 0444 $(IDIR)/$$file; \ - echo $$file installed; \ - done - - -########################################################################### -# -# Destructors -# - -clean: - rm -f *.o *~ \#* *.bak *.pl a.out - cd tests; $(MAKE) clean - cd etc; $(MAKE) clean - cd gperf; $(MAKE) clean - -realclean: - cd src; $(MAKE) realclean - cd tests; $(MAKE) realclean - cd etc; $(MAKE) realclean - cd gperf; $(MAKE) realclean - -rm -f genclass - -rm -f libg++.info* libg++.?? libg++.??s libg++.log libg++.toc libg++.*aux - -rm -f *.orig src/*.orig tests/*.orig etc/*.orig g++-include/*.orig g++-include/sys/*.orig g++-include/gen/*.orig - -rm -f *.rej src/*.rej tests/*.rej etc/*.rej g++-include/*.rej g++-include/sys/*.rej g++-include/gen/*.rej - -rm -f *~ src/*~ tests/*~ etc/*~ g++-include/*~ g++-include/sys/*~ g++-include/gen/*~ - -rm -f a.out src/a.out tests/a.out etc/a.out - -rm -f *.s src/*.s tests/*.s etc/*.s - - -.PHONY: src tests genclass etc gperf FORCE install install-include-files diff --git a/gnu/lib/libg++/README.gnu b/gnu/lib/libg++/README.gnu deleted file mode 100644 index 4ffeab679e..0000000000 --- a/gnu/lib/libg++/README.gnu +++ /dev/null @@ -1,269 +0,0 @@ -This is version 1.39.0 of libg++, the GNU C++ class library. -Release date Tue Feb 19 06:43:04 1991 Doug Lea (dl at g.oswego.edu) - -* Please skim through this once BEFORE attempting to make and install libg++. - -* Contents - - * g++ source files are in the ./src directory - * Some simple tests and demo programs are in ./tests - * Header files are in ./g++-include - * documentation is in ./libg++.texinfo. - * Some miscellaneous files of possible interest are in ./etc - (These files are not officially a part of the libg++ distribution, - and are subject to arbitrary changes, deletions, etc. from release - to release.) - -* Pre-installation - - * Install a version of g++ with at least as high a version - number as this version of libg++. You also need gcc installed. - - * If there is a version of the GNU as (gas) assembler that - works on your machine, get it, and install it as gcc-as, - in whatever lib directory holds gcc-cc1plus. Otherwise, - you may have to remake g++ with -DFASCIST_ASSEMBLER in CFLAGS, - and disable the -PIPE_AS flag in the libg++ Makefile. - - * With only trivial modifications (like changing file extensions, - etc.) everything (perhaps except for some demos in ./etc) - should compile and run with any 2.0 C++ compiler. Please tell me - if this is not so. - -* Installation (see libg++.texinfo more more details) - - * For VMS, cd to ./vms, and read AAAREADME.TXT - - * Except for how to set the `PWD' make variable (which you must - manually change if you do not use GNU make), the Makefiles - are reported to be compatible with all flavors of `make'. - - * Check the declared indicated pathnames, etc. in the top-level Makefile - - Be sure to use USG-oriented switches if you run SystemV unix. - - If you run into problems, check through system-dependent - #defines in g++-include/stdio.h, g++-include/math.h - and g++-include/values.h, especially, and report any - incompatibilities or problems to bug-lib-g++@prep.ai.mit.edu. - - * Choose functionality flags. - - In the libg++ Makefile, `XTRAFLAGS' are selectable. By default, - none of these are enabled: - - -DEFAULT_filebuf, if included in XTRAFLAGS, causes standard - cin, cout, and cerr streams to bypass your libc stdio facilities. - I know of no system for which this is absolutely required. However, - if there are known bugs with your stdio implementation, or if - you wish to give up some functionality in order to be conservative, - then select this. If you use this, expect an innocuous difference - between the expected and obtained results of tFile in how the - final status of standard streams is printed. - - -DNO_GNULIB3 suppresses compilation of a dummy `main', - which calls global constructors and destructors around a - user main. Select this if you have made alternative arrangements - for doing this. - - -DCOFF_ENCAPSULATE is required for systems in which all - executables use GNU COFF encapsulation. - - -DNO_LINE_BUFFER_STREAMBUF suppresses line-buffering (automatic - flushing of output whenever a newline is encountered). Selecting - this may result in slightly smaller code, but will require - manual flushing when lines are expected to be printed right - when they are written. - - -DUSE_LIBGXX_INLINES forces inlines to be compiled even if - not compiling with optimization turned on. This is - required when compiling src files only if -O does not work - for you. - - -DNO_LIBGXX_MALLOC suppresses compilation of libg++ malloc - routines, thus causing all malloc, free, realloc, operator - new() and operator delete() calls to go through your libc - version of malloc. Select this if your system requires a - the system version of malloc (possibly necessary on some - shared memory multiprocessors, since libg++ malloc does not - contain any concurrency control), or, perhaps, if your applications - are specially tuned for or otherwise work better with your - system malloc. - - -DMALLOC_STATS compiles statistics counting into libg++ malloc - (but also slows it down a little). Select this if you are - interested in examining the performance of libg++ malloc. - (I welcome this, and would be grateful to receive statistics - (printed via `malloc_stats()') for programs with heavy - dynamic allocation requirements.) - - * Other compilation flags are tunable, but the default versions - should normally suffice. - - * type `make all', or, step-by-step: - - `make src' -- to compile libg++.a - `make tests' -- to make some tests/demos of libg++. - This will first compile tests, then run them, - then diff the results against expected - results. - `make etc' -- (optional) to compile various other things - - `make run_etc' -- (optional) to run tests on some of the - things compiled in etc. - - `make gperf' -- (optional) to compile Doug Schmidt's - perfect hash function generator. - - * Type `make install' to install - - libg++.a (from src) - include files (from g++-include) - prototype files (from g++-include/gen) - etags (from etc) - g++dep (from etc) - - You may also want to install etc/c++-mode.el in your - emacs/lisp directory, probably in byte-compiled form. - - * Install the documentation - - If you are a systems administrator installing libg++ for others, - please make the documentation available to users! - - The libg++.texinfo file may be formatted as a paper document by - - * Get a copy of texinfo.tex. - This file defines various tex macros used in libg++.texinfo - One is in the gcc release. - You can temporarily copy it into the current directory. - * Run tex on libg++.texinfo - and do whatever you normally do from there to print it. - - It may be made into an emacs info file by - - * Inside emacs, run M-x texinfo-format-buffer on libg++.texinfo. - * Save the resulting files: - libg++ - libg++-1 - libg++-2 - ... - - * Copy these files into your emacs info directory - (normally somewhere like /usr/gnu/emacs/info). - * If you have not done so before, edit the emacs/info/dir file - to add a libg++ node, by inserting a line like - - * Libg++: (libg++). The GNU C++ Library - - to the bottom of the file. - - * (Optional) Install, from ./etc - etags (version of etags that understands c++) - g++dep (a version of mkdep that understands c++) - c++-mode.el (a c++-mode for GNU emacs) - -* General compilation notes - -By default, everything is compiled with the g++/gcc -Wall flag -enabled. This causes g++ to produce numerous diagnostic warnings and -reminders. This is perfectly normal. Only true error messages, which -cause the `makes' to halt indicate real problems. If you do not like -to look at the warnings, disable the -Wall in the Makefile. -Wall is -enabled because, if there are true errors, warnings leading up to -them may prove helpful. - -* Notes on compiling and running libg++/tests - -tCurses is not automatically run through `checktests'. -You must run it manually: - -tCurses attempts to place one of each curses library primitive (boxes, -strings, numbers...) on your screen, and asks for some input, to test -curses input routines. Since there is no way to describe the output -in a system- and terminal- independent way, you will have to be the -judge of whether it works acceptably. - -tCurses (and the curses-based classes generally) may fail on the -Sequent and perhaps other systems with unusual or old curses library -implementations if you do not end input with a instead of -the normal . - -It is a very good idea to also cd to the test directory and run tests -manually, to see what they do. - -Compiling and running the tests consumes a fair amount of time and -disk space! - -Some reported diffs may be perfectly reasonable, owing to things like -floating point precision differences: The expected.out file was created -on a Sun4/110. - - Some tRational and tFix results depend on floating point precision - and may generate slightly different output on different machines. - - tRandom seeds some random-numbers in a way that also relies on - floating-point representations -- Your output should be numerically - similar, but probably not identical. - -* changes from libg++-1.37.0 - - * All files use the new g++ #pragma interface / #pragma implementation - convention, which minimies duplication of `outlined' inlines - and vtables. This also causes no inlines to be used at - all when not compiling with `-O', which speeds compilation - and simplifies debugging. - - * Many .h header file names had to be shortened so as to simulaneously - work with SYSV and with #pragma interface (since .h and .cc file - base names must match.) Sorry! - - * All genclass-able files have been moved to g++-include/gen. - - * various and sundry bug fixes, minor enhancements, and/or portability - improvements as described in the ChangeLog. - -* Known bugs and problems - - * Support for the DecStation3100 and other MIPS-based machines is - still uncertain. - - * The file etc/HINTS is an emacs RMAIL file that contains recent - bug-lib-g++ list mail and related messages that may be useful. - -* Lots of other information is in the libg++.texinfo file. It really is - very important to actually read the documentation before using - library classes. Examination of the demo files in the test directory - may also be useful. (Note however, that the demo files are merely - designed to test examples of each class capability, - and are not especially good examples of client functions that might - use these classes.) - -* There is now a gnu libg++ mailing list (bug-lib-g++@prep.ai.mit.edu) and - associated usenet gnu news group. - -* You will be performing a valuable service if you use libg++ - classes and report back any comments, and suggestions, or bugs, - preferably to the bug-lib-g++ list. Your feedback is extremely - helpful in efforts to make libg++ as useful and reliable as possible. - -* See file `etc/release.log' for changes from previous versions - - -* I continue to solicit - - * bug reports. - * suggestions. - * comments. - * questions about installing and using libg++ - * other contributions to be incorporated into libg++. - * sample programs using libg++. - - Often, the best place to send such things is bug-lib-g++@prep.ai.mit.edu, - although direct mail to me is also welcome. - -* Good luck! - -Doug Lea, Computer Science Dept., SUNY Oswego, Oswego, NY, 13126 (315)341-2367 -email: dl@g.oswego.edu or dl@cat.syr.edu -UUCP :...cornell!devvax!oswego!dl or ...rutgers!sunybcs!oswego!dl diff --git a/gnu/lib/libg++/g++-include/ACG.h b/gnu/lib/libg++/g++-include/ACG.h deleted file mode 100644 index aba4ddcb3a..0000000000 --- a/gnu/lib/libg++/g++-include/ACG.h +++ /dev/null @@ -1,74 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ -#ifndef _ACG_h -#define _ACG_h 1 - -#include -#include -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif - -// -// Additive number generator. This method is presented in Volume II -// of The Art of Computer Programming by Knuth. I've coded the algorithm -// and have added the extensions by Andres Nowatzyk of CMU to randomize -// the result of algorithm M a bit by using an LCG & a spatial -// permutation table. -// -// The version presented uses the same constants for the LCG that Andres -// uses (chosen by trial & error). The spatial permutation table is -// the same size (it's based on word size). This is for 32-bit words. -// -// The ``auxillary table'' used by the LCG table varies in size, and -// is chosen to be the the smallest power of two which is larger than -// twice the size of the state table. -// - -class ACG : public RNG { - - unsigned long initialSeed; // used to reset generator - int initialTableEntry; - - unsigned long *state; - unsigned long *auxState; - short stateSize; - short auxSize; - unsigned long lcgRecurr; - short j; - short k; - -protected: - -public: - ACG(unsigned long seed = 0, int size = 55); - virtual ~ACG(); - // - // Return a long-words word of random bits - // - virtual unsigned long asLong(); - virtual void reset(); -}; - -#endif diff --git a/gnu/lib/libg++/g++-include/AVLMap.ccP b/gnu/lib/libg++/g++-include/AVLMap.ccP new file mode 100644 index 0000000000..a9be60f066 --- /dev/null +++ b/gnu/lib/libg++/g++-include/AVLMap.ccP @@ -0,0 +1,614 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include "..AVLMap.h" + + +/* + constants & inlines for maintaining balance & thread status in tree nodes +*/ + +#define AVLBALANCEMASK 3 +#define AVLBALANCED 0 +#define AVLLEFTHEAVY 1 +#define AVLRIGHTHEAVY 2 + +#define LTHREADBIT 4 +#define RTHREADBIT 8 + + +static inline int bf(AVLNode* t) +{ + return t->stat & AVLBALANCEMASK; +} + +static inline void set_bf(AVLNode* t, int b) +{ + t->stat = (t->stat & ~AVLBALANCEMASK) | (b & AVLBALANCEMASK); +} + + +static inline int rthread(AVLNode* t) +{ + return t->stat & RTHREADBIT; +} + +static inline void set_rthread(AVLNode* t, int b) +{ + if (b) + t->stat |= RTHREADBIT; + else + t->stat &= ~RTHREADBIT; +} + +static inline int lthread(AVLNode* t) +{ + return t->stat & LTHREADBIT; +} + +static inline void set_lthread(AVLNode* t, int b) +{ + if (b) + t->stat |= LTHREADBIT; + else + t->stat &= ~LTHREADBIT; +} + +/* + traversal primitives +*/ + + +AVLNode* AVLMap::leftmost() +{ + AVLNode* t = root; + if (t != 0) while (t->lt != 0) t = t->lt; + return t; +} + +AVLNode* AVLMap::rightmost() +{ + AVLNode* t = root; + if (t != 0) while (t->rt != 0) t = t->rt; + return t; +} + +AVLNode* AVLMap::succ(AVLNode* t) +{ + AVLNode* r = t->rt; + if (!rthread(t)) while (!lthread(r)) r = r->lt; + return r; +} + +AVLNode* AVLMap::pred(AVLNode* t) +{ + AVLNode* l = t->lt; + if (!lthread(t)) while (!rthread(l)) l = l->rt; + return l; +} + + +Pix AVLMap::seek( key) +{ + AVLNode* t = root; + if (t == 0) + return 0; + for (;;) + { + int cmp = CMP(key, t->item); + if (cmp == 0) + return Pix(t); + else if (cmp < 0) + { + if (lthread(t)) + return 0; + else + t = t->lt; + } + else if (rthread(t)) + return 0; + else + t = t->rt; + } +} + + +/* + The combination of threads and AVL bits make adding & deleting + interesting, but very awkward. + + We use the following statics to avoid passing them around recursively +*/ + +static int _need_rebalancing; // to send back balance info from rec. calls +static * _target_item; // add/del_item target +static AVLNode* _found_node; // returned added/deleted node +static int _already_found; // for deletion subcases + + +void AVLMap:: _add(AVLNode*& t) +{ + int cmp = CMP(*_target_item, t->item); + if (cmp == 0) + { + _found_node = t; + return; + } + else if (cmp < 0) + { + if (lthread(t)) + { + ++count; + _found_node = new AVLNode(*_target_item, def); + set_lthread(_found_node, 1); + set_rthread(_found_node, 1); + _found_node->lt = t->lt; + _found_node->rt = t; + t->lt = _found_node; + set_lthread(t, 0); + _need_rebalancing = 1; + } + else + _add(t->lt); + if (_need_rebalancing) + { + switch(bf(t)) + { + case AVLRIGHTHEAVY: + set_bf(t, AVLBALANCED); + _need_rebalancing = 0; + return; + case AVLBALANCED: + set_bf(t, AVLLEFTHEAVY); + return; + case AVLLEFTHEAVY: + { + AVLNode* l = t->lt; + if (bf(l) == AVLLEFTHEAVY) + { + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + _need_rebalancing = 0; + } + else + { + AVLNode* r = l->rt; + set_rthread(l, lthread(r)); + if (lthread(r)) + l->rt = r; + else + l->rt = r->lt; + r->lt = l; + set_lthread(r, 0); + set_lthread(t, rthread(r)); + if (rthread(r)) + t->lt = r; + else + t->lt = r->rt; + r->rt = t; + set_rthread(r, 0); + if (bf(r) == AVLLEFTHEAVY) + set_bf(t, AVLRIGHTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(r) == AVLRIGHTHEAVY) + set_bf(l, AVLLEFTHEAVY); + else + set_bf(l, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + _need_rebalancing = 0; + return; + } + } + } + } + } + else + { + if (rthread(t)) + { + ++count; + _found_node = new AVLNode(*_target_item, def); + set_rthread(t, 0); + set_lthread(_found_node, 1); + set_rthread(_found_node, 1); + _found_node->lt = t; + _found_node->rt = t->rt; + t->rt = _found_node; + _need_rebalancing = 1; + } + else + _add(t->rt); + if (_need_rebalancing) + { + switch(bf(t)) + { + case AVLLEFTHEAVY: + set_bf(t, AVLBALANCED); + _need_rebalancing = 0; + return; + case AVLBALANCED: + set_bf(t, AVLRIGHTHEAVY); + return; + case AVLRIGHTHEAVY: + { + AVLNode* r = t->rt; + if (bf(r) == AVLRIGHTHEAVY) + { + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + _need_rebalancing = 0; + } + else + { + AVLNode* l = r->lt; + set_lthread(r, rthread(l)); + if (rthread(l)) + r->lt = l; + else + r->lt = l->rt; + l->rt = r; + set_rthread(l, 0); + set_rthread(t, lthread(l)); + if (lthread(l)) + t->rt = l; + else + t->rt = l->lt; + l->lt = t; + set_lthread(l, 0); + if (bf(l) == AVLRIGHTHEAVY) + set_bf(t, AVLLEFTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(l) == AVLLEFTHEAVY) + set_bf(r, AVLRIGHTHEAVY); + else + set_bf(r, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + _need_rebalancing = 0; + return; + } + } + } + } + } +} + + +& AVLMap::operator [] ( item) +{ + if (root == 0) + { + ++count; + root = new AVLNode(item, def); + set_rthread(root, 1); + set_lthread(root, 1); + return root->cont; + } + else + { + _target_item = &item; + _need_rebalancing = 0; + _add(root); + return _found_node->cont; + } +} + + +void AVLMap::_del(AVLNode* par, AVLNode*& t) +{ + int comp; + if (_already_found) + { + if (rthread(t)) + comp = 0; + else + comp = 1; + } + else + comp = CMP(*_target_item, t->item); + if (comp == 0) + { + if (lthread(t) && rthread(t)) + { + _found_node = t; + if (t == par->lt) + { + set_lthread(par, 1); + par->lt = t->lt; + } + else + { + set_rthread(par, 1); + par->rt = t->rt; + } + _need_rebalancing = 1; + return; + } + else if (lthread(t)) + { + _found_node = t; + AVLNode* s = succ(t); + if (s != 0 && lthread(s)) + s->lt = t->lt; + t = t->rt; + _need_rebalancing = 1; + return; + } + else if (rthread(t)) + { + _found_node = t; + AVLNode* p = pred(t); + if (p != 0 && rthread(p)) + p->rt = t->rt; + t = t->lt; + _need_rebalancing = 1; + return; + } + else // replace item & find someone deletable + { + AVLNode* p = pred(t); + t->item = p->item; + t->cont = p->cont; + _already_found = 1; + comp = -1; // fall through below to left + } + } + + if (comp < 0) + { + if (lthread(t)) + return; + _del(t, t->lt); + if (!_need_rebalancing) + return; + switch (bf(t)) + { + case AVLLEFTHEAVY: + set_bf(t, AVLBALANCED); + return; + case AVLBALANCED: + set_bf(t, AVLRIGHTHEAVY); + _need_rebalancing = 0; + return; + case AVLRIGHTHEAVY: + { + AVLNode* r = t->rt; + switch (bf(r)) + { + case AVLBALANCED: + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLRIGHTHEAVY); + set_bf(r, AVLLEFTHEAVY); + _need_rebalancing = 0; + t = r; + return; + case AVLRIGHTHEAVY: + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + return; + case AVLLEFTHEAVY: + { + AVLNode* l = r->lt; + set_lthread(r, rthread(l)); + if (rthread(l)) + r->lt = l; + else + r->lt = l->rt; + l->rt = r; + set_rthread(l, 0); + set_rthread(t, lthread(l)); + if (lthread(l)) + t->rt = l; + else + t->rt = l->lt; + l->lt = t; + set_lthread(l, 0); + if (bf(l) == AVLRIGHTHEAVY) + set_bf(t, AVLLEFTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(l) == AVLLEFTHEAVY) + set_bf(r, AVLRIGHTHEAVY); + else + set_bf(r, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + return; + } + } + } + } + } + else + { + if (rthread(t)) + return; + _del(t, t->rt); + if (!_need_rebalancing) + return; + switch (bf(t)) + { + case AVLRIGHTHEAVY: + set_bf(t, AVLBALANCED); + return; + case AVLBALANCED: + set_bf(t, AVLLEFTHEAVY); + _need_rebalancing = 0; + return; + case AVLLEFTHEAVY: + { + AVLNode* l = t->lt; + switch (bf(l)) + { + case AVLBALANCED: + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLLEFTHEAVY); + set_bf(l, AVLRIGHTHEAVY); + _need_rebalancing = 0; + t = l; + return; + case AVLLEFTHEAVY: + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + return; + case AVLRIGHTHEAVY: + { + AVLNode* r = l->rt; + set_rthread(l, lthread(r)); + if (lthread(r)) + l->rt = r; + else + l->rt = r->lt; + r->lt = l; + set_lthread(r, 0); + set_lthread(t, rthread(r)); + if (rthread(r)) + t->lt = r; + else + t->lt = r->rt; + r->rt = t; + set_rthread(r, 0); + if (bf(r) == AVLLEFTHEAVY) + set_bf(t, AVLRIGHTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(r) == AVLRIGHTHEAVY) + set_bf(l, AVLLEFTHEAVY); + else + set_bf(l, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + return; + } + } + } + } + } +} + + + +void AVLMap::del( item) +{ + if (root == 0) return; + _need_rebalancing = 0; + _already_found = 0; + _found_node = 0; + _target_item = &item; + _del(root, root); + if (_found_node) + { + delete(_found_node); + if (--count == 0) + root = 0; + } +} + +void AVLMap::_kill(AVLNode* t) +{ + if (t != 0) + { + if (!lthread(t)) _kill(t->lt); + if (!rthread(t)) _kill(t->rt); + delete t; + } +} + + +AVLMap::AVLMap(AVLMap& b) :Map(b.def) +{ + root = 0; + count = 0; + for (Pix i = b.first(); i != 0; b.next(i)) + (*this)[b.key(i)] = b.contents(i); +} + + +int AVLMap::OK() +{ + int v = 1; + if (root == 0) + v = count == 0; + else + { + int n = 1; + AVLNode* trail = leftmost(); + AVLNode* t = succ(trail); + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/AVLMap.hP b/gnu/lib/libg++/g++-include/AVLMap.hP new file mode 100644 index 0000000000..119ee82caa --- /dev/null +++ b/gnu/lib/libg++/g++-include/AVLMap.hP @@ -0,0 +1,141 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _AVLMap_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _AVLMap_h 1 + +#include "..Map.h" + +struct AVLNode +{ + AVLNode* lt; + AVLNode* rt; + item; + cont; + char stat; + AVLNode( h, c, + AVLNode* l=0, AVLNode* r=0); + ~AVLNode(); +}; + +inline AVLNode::AVLNode( h, c, + AVLNode* l, AVLNode* r) + :item(h), cont(c), lt(l), rt(r), stat(0) {} + +inline AVLNode::~AVLNode() {} + +typedef AVLNode* AVLNodePtr; + + +class AVLMap : public Map +{ +protected: + AVLNode* root; + + AVLNode* leftmost(); + AVLNode* rightmost(); + AVLNode* pred(AVLNode* t); + AVLNode* succ(AVLNode* t); + void _kill(AVLNode* t); + void _add(AVLNode*& t); + void _del(AVLNode* p, AVLNode*& t); + +public: + AVLMap( dflt); + AVLMap(AVLMap& a); + ~AVLMap(); + + & operator [] ( key); + + void del( key); + + Pix first(); + void next(Pix& i); + & key(Pix i); + & contents(Pix i); + + Pix seek( key); + int contains( key); + + void clear(); + + Pix last(); + void prev(Pix& i); + + int OK(); +}; + +inline AVLMap::~AVLMap() +{ + _kill(root); +} + +inline AVLMap::AVLMap( dflt) :Map(dflt) +{ + root = 0; +} + +inline Pix AVLMap::first() +{ + return Pix(leftmost()); +} + +inline Pix AVLMap::last() +{ + return Pix(rightmost()); +} + +inline void AVLMap::next(Pix& i) +{ + if (i != 0) i = Pix(succ((AVLNode*)i)); +} + +inline void AVLMap::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((AVLNode*)i)); +} + +inline & AVLMap::key(Pix i) +{ + if (i == 0) error("null Pix"); + return ((AVLNode*)i)->item; +} + +inline & AVLMap::contents(Pix i) +{ + if (i == 0) error("null Pix"); + return ((AVLNode*)i)->cont; +} + +inline void AVLMap::clear() +{ + _kill(root); + count = 0; + root = 0; +} + +inline int AVLMap::contains( key) +{ + return seek(key) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/AVLSet.ccP b/gnu/lib/libg++/g++-include/AVLSet.ccP new file mode 100644 index 0000000000..b170734e54 --- /dev/null +++ b/gnu/lib/libg++/g++-include/AVLSet.ccP @@ -0,0 +1,892 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".AVLSet.h" +#include + + +/* + constants & inlines for maintaining balance & thread status in tree nodes +*/ + +#define AVLBALANCEMASK 3 +#define AVLBALANCED 0 +#define AVLLEFTHEAVY 1 +#define AVLRIGHTHEAVY 2 + +#define LTHREADBIT 4 +#define RTHREADBIT 8 + + +static inline int bf(AVLNode* t) +{ + return t->stat & AVLBALANCEMASK; +} + +static inline void set_bf(AVLNode* t, int b) +{ + t->stat = (t->stat & ~AVLBALANCEMASK) | (b & AVLBALANCEMASK); +} + + +static inline int rthread(AVLNode* t) +{ + return t->stat & RTHREADBIT; +} + +static inline void set_rthread(AVLNode* t, int b) +{ + if (b) + t->stat |= RTHREADBIT; + else + t->stat &= ~RTHREADBIT; +} + +static inline int lthread(AVLNode* t) +{ + return t->stat & LTHREADBIT; +} + +static inline void set_lthread(AVLNode* t, int b) +{ + if (b) + t->stat |= LTHREADBIT; + else + t->stat &= ~LTHREADBIT; +} + +/* + traversal primitives +*/ + + +AVLNode* AVLSet::leftmost() +{ + AVLNode* t = root; + if (t != 0) while (t->lt != 0) t = t->lt; + return t; +} + +AVLNode* AVLSet::rightmost() +{ + AVLNode* t = root; + if (t != 0) while (t->rt != 0) t = t->rt; + return t; +} + +AVLNode* AVLSet::succ(AVLNode* t) +{ + AVLNode* r = t->rt; + if (!rthread(t)) while (!lthread(r)) r = r->lt; + return r; +} + +AVLNode* AVLSet::pred(AVLNode* t) +{ + AVLNode* l = t->lt; + if (!lthread(t)) while (!rthread(l)) l = l->rt; + return l; +} + + +Pix AVLSet::seek( key) +{ + AVLNode* t = root; + if (t == 0) + return 0; + for (;;) + { + int cmp = CMP(key, t->item); + if (cmp == 0) + return Pix(t); + else if (cmp < 0) + { + if (lthread(t)) + return 0; + else + t = t->lt; + } + else if (rthread(t)) + return 0; + else + t = t->rt; + } +} + + +/* + The combination of threads and AVL bits make adding & deleting + interesting, but very awkward. + + We use the following statics to avoid passing them around recursively +*/ + +static int _need_rebalancing; // to send back balance info from rec. calls +static * _target_item; // add/del_item target +static AVLNode* _found_node; // returned added/deleted node +static int _already_found; // for deletion subcases + +static AVLNode** _hold_nodes; // used for rebuilding trees +static int _max_hold_index; // # elements-1 in _hold_nodes + + +void AVLSet:: _add(AVLNode*& t) +{ + int cmp = CMP(*_target_item, t->item); + if (cmp == 0) + { + _found_node = t; + return; + } + else if (cmp < 0) + { + if (lthread(t)) + { + ++count; + _found_node = new AVLNode(*_target_item); + set_lthread(_found_node, 1); + set_rthread(_found_node, 1); + _found_node->lt = t->lt; + _found_node->rt = t; + t->lt = _found_node; + set_lthread(t, 0); + _need_rebalancing = 1; + } + else + _add(t->lt); + if (_need_rebalancing) + { + switch(bf(t)) + { + case AVLRIGHTHEAVY: + set_bf(t, AVLBALANCED); + _need_rebalancing = 0; + return; + case AVLBALANCED: + set_bf(t, AVLLEFTHEAVY); + return; + case AVLLEFTHEAVY: + { + AVLNode* l = t->lt; + if (bf(l) == AVLLEFTHEAVY) + { + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + _need_rebalancing = 0; + } + else + { + AVLNode* r = l->rt; + set_rthread(l, lthread(r)); + if (lthread(r)) + l->rt = r; + else + l->rt = r->lt; + r->lt = l; + set_lthread(r, 0); + set_lthread(t, rthread(r)); + if (rthread(r)) + t->lt = r; + else + t->lt = r->rt; + r->rt = t; + set_rthread(r, 0); + if (bf(r) == AVLLEFTHEAVY) + set_bf(t, AVLRIGHTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(r) == AVLRIGHTHEAVY) + set_bf(l, AVLLEFTHEAVY); + else + set_bf(l, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + _need_rebalancing = 0; + return; + } + } + } + } + } + else + { + if (rthread(t)) + { + ++count; + _found_node = new AVLNode(*_target_item); + set_rthread(t, 0); + set_lthread(_found_node, 1); + set_rthread(_found_node, 1); + _found_node->lt = t; + _found_node->rt = t->rt; + t->rt = _found_node; + _need_rebalancing = 1; + } + else + _add(t->rt); + if (_need_rebalancing) + { + switch(bf(t)) + { + case AVLLEFTHEAVY: + set_bf(t, AVLBALANCED); + _need_rebalancing = 0; + return; + case AVLBALANCED: + set_bf(t, AVLRIGHTHEAVY); + return; + case AVLRIGHTHEAVY: + { + AVLNode* r = t->rt; + if (bf(r) == AVLRIGHTHEAVY) + { + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + _need_rebalancing = 0; + } + else + { + AVLNode* l = r->lt; + set_lthread(r, rthread(l)); + if (rthread(l)) + r->lt = l; + else + r->lt = l->rt; + l->rt = r; + set_rthread(l, 0); + set_rthread(t, lthread(l)); + if (lthread(l)) + t->rt = l; + else + t->rt = l->lt; + l->lt = t; + set_lthread(l, 0); + if (bf(l) == AVLRIGHTHEAVY) + set_bf(t, AVLLEFTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(l) == AVLLEFTHEAVY) + set_bf(r, AVLRIGHTHEAVY); + else + set_bf(r, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + _need_rebalancing = 0; + return; + } + } + } + } + } +} + + +Pix AVLSet::add( item) +{ + if (root == 0) + { + ++count; + root = new AVLNode(item); + set_rthread(root, 1); + set_lthread(root, 1); + return Pix(root); + } + else + { + _target_item = &item; + _need_rebalancing = 0; + _add(root); + return Pix(_found_node); + } +} + + +void AVLSet::_del(AVLNode* par, AVLNode*& t) +{ + int comp; + if (_already_found) + { + if (rthread(t)) + comp = 0; + else + comp = 1; + } + else + comp = CMP(*_target_item, t->item); + if (comp == 0) + { + if (lthread(t) && rthread(t)) + { + _found_node = t; + if (t == par->lt) + { + set_lthread(par, 1); + par->lt = t->lt; + } + else + { + set_rthread(par, 1); + par->rt = t->rt; + } + _need_rebalancing = 1; + return; + } + else if (lthread(t)) + { + _found_node = t; + AVLNode* s = succ(t); + if (s != 0 && lthread(s)) + s->lt = t->lt; + t = t->rt; + _need_rebalancing = 1; + return; + } + else if (rthread(t)) + { + _found_node = t; + AVLNode* p = pred(t); + if (p != 0 && rthread(p)) + p->rt = t->rt; + t = t->lt; + _need_rebalancing = 1; + return; + } + else // replace item & find someone deletable + { + AVLNode* p = pred(t); + t->item = p->item; + _already_found = 1; + comp = -1; // fall through below to left + } + } + + if (comp < 0) + { + if (lthread(t)) + return; + _del(t, t->lt); + if (!_need_rebalancing) + return; + switch (bf(t)) + { + case AVLLEFTHEAVY: + set_bf(t, AVLBALANCED); + return; + case AVLBALANCED: + set_bf(t, AVLRIGHTHEAVY); + _need_rebalancing = 0; + return; + case AVLRIGHTHEAVY: + { + AVLNode* r = t->rt; + switch (bf(r)) + { + case AVLBALANCED: + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLRIGHTHEAVY); + set_bf(r, AVLLEFTHEAVY); + _need_rebalancing = 0; + t = r; + return; + case AVLRIGHTHEAVY: + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + return; + case AVLLEFTHEAVY: + { + AVLNode* l = r->lt; + set_lthread(r, rthread(l)); + if (rthread(l)) + r->lt = l; + else + r->lt = l->rt; + l->rt = r; + set_rthread(l, 0); + set_rthread(t, lthread(l)); + if (lthread(l)) + t->rt = l; + else + t->rt = l->lt; + l->lt = t; + set_lthread(l, 0); + if (bf(l) == AVLRIGHTHEAVY) + set_bf(t, AVLLEFTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(l) == AVLLEFTHEAVY) + set_bf(r, AVLRIGHTHEAVY); + else + set_bf(r, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + return; + } + } + } + } + } + else + { + if (rthread(t)) + return; + _del(t, t->rt); + if (!_need_rebalancing) + return; + switch (bf(t)) + { + case AVLRIGHTHEAVY: + set_bf(t, AVLBALANCED); + return; + case AVLBALANCED: + set_bf(t, AVLLEFTHEAVY); + _need_rebalancing = 0; + return; + case AVLLEFTHEAVY: + { + AVLNode* l = t->lt; + switch (bf(l)) + { + case AVLBALANCED: + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLLEFTHEAVY); + set_bf(l, AVLRIGHTHEAVY); + _need_rebalancing = 0; + t = l; + return; + case AVLLEFTHEAVY: + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + return; + case AVLRIGHTHEAVY: + { + AVLNode* r = l->rt; + set_rthread(l, lthread(r)); + if (lthread(r)) + l->rt = r; + else + l->rt = r->lt; + r->lt = l; + set_lthread(r, 0); + set_lthread(t, rthread(r)); + if (rthread(r)) + t->lt = r; + else + t->lt = r->rt; + r->rt = t; + set_rthread(r, 0); + if (bf(r) == AVLLEFTHEAVY) + set_bf(t, AVLRIGHTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(r) == AVLRIGHTHEAVY) + set_bf(l, AVLLEFTHEAVY); + else + set_bf(l, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + return; + } + } + } + } + } +} + + + +void AVLSet::del( item) +{ + if (root == 0) return; + _need_rebalancing = 0; + _already_found = 0; + _found_node = 0; + _target_item = &item; + _del(root, root); + if (_found_node) + { + delete(_found_node); + if (--count == 0) + root = 0; + } +} + +// build an ordered array of pointers to tree nodes back into a tree +// we know that at least one element exists + +static AVLNode* _do_treeify(int lo, int hi, int& h) +{ + int lh, rh; + int mid = (lo + hi) / 2; + AVLNode* t = _hold_nodes[mid]; + if (lo > mid - 1) + { + set_lthread(t, 1); + if (mid == 0) + t->lt = 0; + else + t->lt = _hold_nodes[mid-1]; + lh = 0; + } + else + { + set_lthread(t, 0); + t->lt = _do_treeify(lo, mid-1, lh); + } + if (hi < mid + 1) + { + set_rthread(t, 1); + if (mid == _max_hold_index) + t->rt = 0; + else + t->rt = _hold_nodes[mid+1]; + rh = 0; + } + else + { + set_rthread(t, 0); + t->rt = _do_treeify(mid+1, hi, rh); + } + if (lh == rh) + { + set_bf(t, AVLBALANCED); + h = lh + 1; + } + else if (lh == rh - 1) + { + set_bf(t, AVLRIGHTHEAVY); + h = rh + 1; + } + else if (rh == lh - 1) + { + set_bf(t, AVLLEFTHEAVY); + h = lh + 1; + } + else // can't happen + abort(); + + return t; +} + +static AVLNode* _treeify(int n) +{ + AVLNode* t; + if (n == 0) + t = 0; + else + { + int b; + _max_hold_index = n-1; + t = _do_treeify(0, _max_hold_index, b); + } + delete _hold_nodes; + return t; +} + + +void AVLSet::_kill(AVLNode* t) +{ + if (t != 0) + { + if (!lthread(t)) _kill(t->lt); + if (!rthread(t)) _kill(t->rt); + delete t; + } +} + + +AVLSet::AVLSet(AVLSet& b) +{ + if ((count = b.count) == 0) + { + root = 0; + } + else + { + _hold_nodes = new AVLNodePtr [count]; + AVLNode* t = b.leftmost(); + int i = 0; + while (t != 0) + { + _hold_nodes[i++] = new AVLNode(t->item); + t = b.succ(t); + } + root = _treeify(count); + } +} + + +int AVLSet::operator == (AVLSet& y) +{ + if (count != y.count) + return 0; + else + { + AVLNode* t = leftmost(); + AVLNode* u = y.leftmost(); + for (;;) + { + if (t == 0) + return 1; + else if (!(EQ(t->item, u->item))) + return 0; + else + { + t = succ(t); + u = y.succ(u); + } + } + } +} + +int AVLSet::operator <= (AVLSet& y) +{ + if (count > y.count) + return 0; + else + { + AVLNode* t = leftmost(); + AVLNode* u = y.leftmost(); + for (;;) + { + if (t == 0) + return 1; + else if (u == 0) + return 0; + int cmp = CMP(t->item, u->item); + if (cmp == 0) + { + t = succ(t); + u = y.succ(u); + } + else if (cmp < 0) + return 0; + else + u = y.succ(u); + } + } +} + +void AVLSet::operator |=(AVLSet& y) +{ + AVLNode* t = leftmost(); + AVLNode* u = y.leftmost(); + int rsize = count + y.count; + _hold_nodes = new AVLNodePtr [rsize]; + int k = 0; + for (;;) + { + if (t == 0) + { + while (u != 0) + { + _hold_nodes[k++] = new AVLNode(u->item); + u = y.succ(u); + } + break; + } + else if (u == 0) + { + while (t != 0) + { + _hold_nodes[k++] = t; + t = succ(t); + } + break; + } + int cmp = CMP(t->item, u->item); + if (cmp == 0) + { + _hold_nodes[k++] = t; + t = succ(t); + u = y.succ(u); + } + else if (cmp < 0) + { + _hold_nodes[k++] = t; + t = succ(t); + } + else + { + _hold_nodes[k++] = new AVLNode(u->item); + u = y.succ(u); + } + } + root = _treeify(k); + count = k; +} + +void AVLSet::operator &= (AVLSet& y) +{ + AVLNode* t = leftmost(); + AVLNode* u = y.leftmost(); + int rsize = (count < y.count)? count : y.count; + _hold_nodes = new AVLNodePtr [rsize]; + int k = 0; + for (;;) + { + if (t == 0) + break; + if (u == 0) + { + while (t != 0) + { + AVLNode* tmp = succ(t); + delete t; + t = tmp; + } + break; + } + int cmp = CMP(t->item, u->item); + if (cmp == 0) + { + _hold_nodes[k++] = t; + t = succ(t); + u = y.succ(u); + } + else if (cmp < 0) + { + AVLNode* tmp = succ(t); + delete t; + t = tmp; + } + else + u = y.succ(u); + } + root = _treeify(k); + count = k; +} + + +void AVLSet::operator -=(AVLSet& y) +{ + AVLNode* t = leftmost(); + AVLNode* u = y.leftmost(); + int rsize = count; + _hold_nodes = new AVLNodePtr [rsize]; + int k = 0; + for (;;) + { + if (t == 0) + break; + else if (u == 0) + { + while (t != 0) + { + _hold_nodes[k++] = t; + t = succ(t); + } + break; + } + int cmp = CMP(t->item, u->item); + if (cmp == 0) + { + AVLNode* tmp = succ(t); + delete t; + t = tmp; + u = y.succ(u); + } + else if (cmp < 0) + { + _hold_nodes[k++] = t; + t = succ(t); + } + else + u = y.succ(u); + } + root = _treeify(k); + count = k; +} + +int AVLSet::owns(Pix i) +{ + if (i == 0) return 0; + for (AVLNode* t = leftmost(); t != 0; t = succ(t)) + if (Pix(t) == i) return 1; + return 0; +} + +int AVLSet::OK() +{ + int v = 1; + if (root == 0) + v = count == 0; + else + { + int n = 1; + AVLNode* trail = leftmost(); + AVLNode* t = succ(trail); + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/AVLSet.hP b/gnu/lib/libg++/g++-include/AVLSet.hP new file mode 100644 index 0000000000..16ad1d1946 --- /dev/null +++ b/gnu/lib/libg++/g++-include/AVLSet.hP @@ -0,0 +1,152 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _AVL_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _AVL_h 1 + +#include ".Set.h" + +struct AVLNode +{ + AVLNode* lt; + AVLNode* rt; + item; + char stat; + AVLNode( h, AVLNode* l=0, AVLNode* r=0); + ~AVLNode(); +}; + +inline AVLNode::AVLNode( h, AVLNode* l, AVLNode* r) +:item(h), lt(l), rt(r), stat(0) {} + +inline AVLNode::~AVLNode() {} + +typedef AVLNode* AVLNodePtr; + + +class AVLSet : public Set +{ +protected: + AVLNode* root; + + AVLSet(AVLNode* p, int l); + + AVLNode* leftmost(); + AVLNode* rightmost(); + AVLNode* pred(AVLNode* t); + AVLNode* succ(AVLNode* t); + void _kill(AVLNode* t); + void _add(AVLNode*& t); + void _del(AVLNode* p, AVLNode*& t); + +public: + AVLSet(); + AVLSet(AVLSet& a); + ~AVLSet(); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item); + + Pix last(); + void prev(Pix& i); + + void operator |= (AVLSet& b); + void operator -= (AVLSet& b); + void operator &= (AVLSet& b); + + int operator == (AVLSet& b); + int operator != (AVLSet& b); + int operator <= (AVLSet& b); + + int OK(); +}; + +inline AVLSet::~AVLSet() +{ + _kill(root); +} + +inline AVLSet::AVLSet() +{ + root = 0; + count = 0; +} + +inline AVLSet::AVLSet(AVLNode* p, int l) +{ + root = p; + count = l; +} + +inline int AVLSet::operator != (AVLSet& b) +{ + return ! ((*this) == b); +} + +inline Pix AVLSet::first() +{ + return Pix(leftmost()); +} + +inline Pix AVLSet::last() +{ + return Pix(rightmost()); +} + +inline void AVLSet::next(Pix& i) +{ + if (i != 0) i = Pix(succ((AVLNode*)i)); +} + +inline void AVLSet::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((AVLNode*)i)); +} + +inline & AVLSet::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((AVLNode*)i)->item; +} + +inline void AVLSet::clear() +{ + _kill(root); + count = 0; + root = 0; +} + +inline int AVLSet::contains( key) +{ + return seek(key) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/AVec.ccP b/gnu/lib/libg++/g++-include/AVec.ccP new file mode 100644 index 0000000000..bc671bf8e1 --- /dev/null +++ b/gnu/lib/libg++/g++-include/AVec.ccP @@ -0,0 +1,397 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include +#include ".AVec.h" + +/* + The following brought to you by the department of redundancy department +*/ + +AVec& AVec::operator = (AVec& v) +{ + if (len != 0 && len != v.capacity()) + error("nonconformant vectors."); + if (len == 0) + s = new [len = v.capacity()]; + if (s != v.vec()) + { + for (int i = 0; i < len; ++i) + s[i] = v.vec()[i]; + } + return *this; +} + +AVec& AVec::operator = ( f) +{ + for (int i = 0; i < len; ++i) s[i] = f; + return *this; +} + + +AVec concat(AVec & a, AVec & b) +{ + int newl = a.capacity() + b.capacity(); + * news = new [newl]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + while (t < top) *p++ = *t++; + top = &(b.vec()[b.capacity()]); + t = b.vec(); + while (t < top) *p++ = *t++; + return AVec(newl, news); +} + + +AVec combine(Combiner f, AVec& a, AVec& b) +{ + int newl = (a.capacity() < b.capacity())? a.capacity() : b.capacity(); + * news = new [newl]; + * p = news; + * top = &(a.vec()[newl]); + * t = a.vec(); + * u = b.vec(); + while (t < top) *p++ = (*f)(*t++, *u++); + return AVec(newl, news); +} + +AVec reverse(AVec& a) +{ + * news = new [a.capacity()]; + if (a.capacity() != 0) + { + * lo = news; + * hi = &(news[a.capacity() - 1]); + while (lo < hi) + { + tmp = *lo; + *lo++ = *hi; + *hi-- = tmp; + } + } + return AVec(a.capacity(), news); +} + +AVec map(Mapper f, AVec& a) +{ + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + while(t < top) *p++ = (*f)(*t++); + return AVec(a.capacity(), news); +} + +AVec AVec::at(int from, int n) +{ + int to; + if (n < 0) + { + n = len - from; + to = len - 1; + } + else + to = from + n - 1; + if ((unsigned)from > (unsigned)to) + range_error(); + * news = new [n]; + * p = news; + * t = &(s[from]); + * top = &(s[to]); + while (t <= top) *p++ = *t++; + return AVec(n, news); +} + +AVec merge(AVec & a, AVec & b, Comparator f) +{ + int newl = a.capacity() + b.capacity(); + * news = new [newl]; + * p = news; + * topa = &(a.vec()[a.capacity()]); + * as = a.vec(); + * topb = &(b.vec()[b.capacity()]); + * bs = b.vec(); + + for (;;) + { + if (as >= topa) + { + while (bs < topb) *p++ = *bs++; + break; + } + else if (bs >= topb) + { + while (as < topa) *p++ = *as++; + break; + } + else if ((*f)(*as, *bs) <= 0) + *p++ = *as++; + else + *p++ = *bs++; + } + return AVec(newl, news); +} + +AVec operator + (AVec& a, AVec& b) +{ + a.check_len(b.capacity()); + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + * u = b.vec(); + while (t < top) *p++ = *t++ + *u++; + return AVec(a.capacity(), news); +} + +AVec operator - (AVec& a, AVec& b) +{ + a.check_len(b.capacity()); + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + * u = b.vec(); + while (t < top) *p++ = *t++ - *u++; + return AVec(a.capacity(), news); +} + +AVec product (AVec& a, AVec& b) +{ + a.check_len(b.capacity()); + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + * u = b.vec(); + while (t < top) *p++ = *t++ * *u++; + return AVec(a.capacity(), news); +} + +AVec quotient(AVec& a, AVec& b) +{ + a.check_len(b.capacity()); + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + * u = b.vec(); + while (t < top) *p++ = *t++ / *u++; + return AVec(a.capacity(), news); +} + +AVec operator + (AVec& a, b) +{ + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + while (t < top) *p++ = *t++ + b; + return AVec(a.capacity(), news); +} + +AVec operator - (AVec& a, b) +{ + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + while (t < top) *p++ = *t++ - b; + return AVec(a.capacity(), news); +} + +AVec operator * (AVec& a, b) +{ + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + while (t < top) *p++ = *t++ * b; + return AVec(a.capacity(), news); +} + +AVec operator / (AVec& a, b) +{ + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + while (t < top) *p++ = *t++ / b; + return AVec(a.capacity(), news); +} + +AVec AVec::operator - () +{ + * news = new [len]; + * p = news; + * top = &(s[len]); + * t = s; + while (t < top) *p++ = -(*t++); + return AVec(len, news); +} + +AVec& AVec::operator += (AVec& b) +{ + check_len(b.capacity()); + * u = b.vec(); + * top = &(s[len]); + * t = s; + while (t < top) *t++ += *u++; + return *this; +} + +AVec& AVec::operator -= (AVec& b) +{ + check_len(b.capacity()); + * u = b.vec(); + * top = &(s[len]); + * t = s; + while (t < top) *t++ -= *u++; + return *this; +} + +AVec& AVec::product(AVec& b) +{ + check_len(b.capacity()); + * u = b.vec(); + * top = &(s[len]); + * t = s; + while (t < top) *t++ *= *u++; + return *this; +} + +AVec& AVec::quotient(AVec& b) +{ + check_len(b.capacity()); + * u = b.vec(); + * top = &(s[len]); + * t = s; + while (t < top) *t++ /= *u++; + return *this; +} + +AVec& AVec::operator += ( b) +{ + * top = &(s[len]); + * t = s; + while (t < top) *t++ += b; + return *this; +} + +AVec& AVec::operator -= ( b) +{ + * top = &(s[len]); + * t = s; + while (t < top) *t++ -= b; + return *this; +} + +AVec& AVec::operator *= ( b) +{ + * top = &(s[len]); + * t = s; + while (t < top) *t++ *= b; + return *this; +} + +AVec& AVec::operator /= ( b) +{ + * top = &(s[len]); + * t = s; + while (t < top) *t++ /= b; + return *this; +} + + AVec::max() +{ + if (len == 0) + return 0; + * top = &(s[len]); + * t = s; + res = *t++; + for (; t < top; ++t) if (*t > res) res = *t; + return res; +} + +int AVec::max_index() +{ + if (len == 0) + return -1; + int ind = 0; + for (int i = 1; i < len; ++i) + if (s[i] > s[ind]) + ind = i; + return ind; +} + + AVec::min() +{ + if (len == 0) + return 0; + * top = &(s[len]); + * t = s; + res = *t++; + for (; t < top; ++t) if (*t < res) res = *t; + return res; +} + +int AVec::min_index() +{ + if (len == 0) + return -1; + int ind = 0; + for (int i = 1; i < len; ++i) + if (s[i] < s[ind]) + ind = i; + return ind; +} + + AVec::sum() +{ + res = 0; + * top = &(s[len]); + * t = s; + while (t < top) res += *t++; + return res; +} + + + AVec::sumsq() +{ + res = 0; + * top = &(s[len]); + * t = s; + for (; t < top; ++t) res += *t * *t; + return res; +} + + operator * (AVec& a, AVec& b) +{ + a.check_len(b.capacity()); + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + * u = b.vec(); + res = 0; + while (t < top) res += *t++ * *u++; + return res; +} diff --git a/gnu/lib/libg++/g++-include/AVec.hP b/gnu/lib/libg++/g++-include/AVec.hP new file mode 100644 index 0000000000..cd9a9c3fe5 --- /dev/null +++ b/gnu/lib/libg++/g++-include/AVec.hP @@ -0,0 +1,113 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _AVec_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _AVec_h 1 + +#include ".Vec.h" + +class AVec : public Vec +{ +protected: + void check_len(int l); + * vec(); + AVec(int l, * d); + public: + AVec (); + AVec (int l); + AVec (int l, fill_value); + AVec (AVec&); + ~AVec (); + + AVec& operator = (AVec& a); + AVec& operator = ( fill_value); + +// vector by scalar -> vector operations + + friend AVec operator + (AVec& a, b); + friend AVec operator - (AVec& a, b); + friend AVec operator * (AVec& a, b); + friend AVec operator / (AVec& a, b); + + AVec& operator += ( b); + AVec& operator -= ( b); + AVec& operator *= ( b); + AVec& operator /= ( b); + +// vector by vector -> vector operations + + friend AVec operator + (AVec& a, AVec& b); + friend AVec operator - (AVec& a, AVec& b); + AVec& operator += (AVec& b); + AVec& operator -= (AVec& b); + + AVec operator - (); + + friend AVec product(AVec& a, AVec& b); + AVec& product(AVec& b); + friend AVec quotient(AVec& a, AVec& b); + AVec& quotient(AVec& b); + +// vector -> scalar operations + + friend operator * (AVec& a, AVec& b); + + sum(); + min(); + max(); + sumsq(); + +// indexing + + int min_index(); + int max_index(); + +// redundant but necesssary + friend AVec concat(AVec& a, AVec& b); + friend AVec map(Mapper f, AVec& a); + friend AVec merge(AVec& a, AVec& b, Comparator f); + friend AVec combine(Combiner f, AVec& a, AVec& b); + friend AVec reverse(AVec& a); + AVec at(int from = 0, int n = -1); +}; + +inline AVec::AVec() {} +inline AVec::AVec(int l) :Vec(l) {} +inline AVec::AVec(int l, fill_value) : Vec (l, fill_value) {} +inline AVec::AVec(AVec& v) :Vec(v) {} +inline AVec::AVec(int l, * d) :Vec(l, d) {} +inline AVec::~AVec() {} + + +inline * AVec::vec() +{ + return s; +} + + +inline void AVec::check_len(int l) +{ + if (l != len) + error("nonconformant vectors."); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/AllocRing.h b/gnu/lib/libg++/g++-include/AllocRing.h deleted file mode 100644 index ac42cbb4dd..0000000000 --- a/gnu/lib/libg++/g++-include/AllocRing.h +++ /dev/null @@ -1,68 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1989 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - - -#ifndef _AllocRing_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _AllocRing_h 1 - - -/* - An AllocRing holds the last n malloc'ed strings, reallocating/reusing - one only when the queue wraps around. It thus guarantees that the - last n allocations are intact. It is useful for things like I/O - formatting where reasonable restrictions may be made about the - number of allowable live allocations before auto-deletion. -*/ - -class AllocRing -{ - - struct AllocQNode - { - void* ptr; - int sz; - }; - - AllocQNode* nodes; - int n; - int current; - - int find(void* p); - -public: - - AllocRing(int max); - ~AllocRing(); - - void* alloc(int size); - int contains(void* ptr); - void clear(); - void free(void* p); -}; - - -#endif diff --git a/gnu/lib/libg++/g++-include/BSTSet.ccP b/gnu/lib/libg++/g++-include/BSTSet.ccP new file mode 100644 index 0000000000..6a69d8f45b --- /dev/null +++ b/gnu/lib/libg++/g++-include/BSTSet.ccP @@ -0,0 +1,378 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".BSTSet.h" + + +/* + traversal primitives +*/ + + +BSTNode* BSTSet::leftmost() +{ + BSTNode* t = root; + if (t != 0) while (t->lt != 0) t = t->lt; + return t; +} + +BSTNode* BSTSet::rightmost() +{ + BSTNode* t = root; + if (t != 0) while (t->rt != 0) t = t->rt; + return t; +} + +BSTNode* BSTSet::succ(BSTNode* t) +{ + if (t == 0) + return 0; + if (t->rt != 0) + { + t = t->rt; + while (t->lt != 0) t = t->lt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->lt) + return t->par; + else + t = t->par; + } + } +} + +BSTNode* BSTSet::pred(BSTNode* t) +{ + if (t == 0) + return 0; + else if (t->lt != 0) + { + t = t->lt; + while (t->rt != 0) t = t->rt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->rt) + return t->par; + else + t = t->par; + } + } +} + + +Pix BSTSet::seek( key) +{ + BSTNode* t = root; + for (;;) + { + if (t == 0) + return 0; + int comp = CMP(key, t->item); + if (comp == 0) + return Pix(t); + else if (comp < 0) + t = t->lt; + else + t = t->rt; + } +} + + +Pix BSTSet::add( item) +{ + if (root == 0) + { + ++count; + root = new BSTNode(item); + return Pix(root); + } + + BSTNode* t = root; + BSTNode* p = root; + int comp; + for (;;) + { + if (t == 0) + { + ++count; + t = new BSTNode(item); + if (comp > 0) + p->lt = t; + else + p->rt = t; + t->par = p; + return Pix(t); + } + p = t; + comp = CMP(t->item, item); + if (comp == 0) + return Pix(t); + else if (comp > 0) + t = t->lt; + else + t = t->rt; + } +} + + +void BSTSet::del( key) +{ + BSTNode* t = root; + BSTNode* p = root; + int comp; + for (;;) + { + if (t == 0) + return; + comp = CMP(key, t->item); + if (comp == 0) + { + --count; + BSTNode* repl; + if (t->lt == 0) + repl = t->rt; + else if (t->rt == 0) + repl = t->lt; + else + { + BSTNode* prepl = t; + repl = t->lt; + while (repl->rt != 0) + { + prepl = repl; + repl = repl->rt; + } + if (prepl != t) + { + prepl->rt = repl->lt; + if (prepl->rt != 0) prepl->rt->par = prepl; + repl->lt = t->lt; + if (repl->lt != 0) repl->lt->par = repl; + } + repl->rt = t->rt; + if (repl->rt != 0) repl->rt->par = repl; + } + if (t == root) + { + root = repl; + if (repl != 0) repl->par = 0; + } + else + { + if (t == p->lt) + p->lt = repl; + else + p->rt = repl; + if (repl != 0) repl->par = p; + } + delete t; + return; + } + p = t; + if (comp < 0) + t = t->lt; + else + t = t->rt; + } +} + + +void BSTSet::_kill(BSTNode* t) +{ + if (t != 0) + { + _kill(t->lt); + _kill(t->rt); + delete t; + } +} + +BSTNode* BSTSet::_copy(BSTNode* t) +{ + if (t == 0) + return 0; + else + { + BSTNode* u = new BSTNode(t->item, _copy(t->lt), _copy(t->rt)); + if (u->lt != 0) u->lt->par = u; + if (u->rt != 0) u->rt->par = u; + return u; + } +} + + +int BSTSet::operator == (BSTSet& y) +{ + if (count != y.count) + return 0; + else + { + BSTNode* t = leftmost(); + BSTNode* u = y.leftmost(); + for (;;) + { + if (t == 0) + return 1; + else if (!EQ(t->item, u->item)) + return 0; + else + { + t = succ(t); + u = y.succ(u); + } + } + } +} + +int BSTSet::operator <= (BSTSet& y) +{ + if (count > y.count) + return 0; + else + { + BSTNode* t = leftmost(); + BSTNode* u = y.leftmost(); + for (;;) + { + if (t == 0) + return 1; + else if (u == 0) + return 0; + int cmp = CMP(t->item, u->item); + if (cmp == 0) + { + t = succ(t); + u = y.succ(u); + } + else if (cmp < 0) + return 0; + else + u = y.succ(u); + } + } +} + + +// linear-time, zero space overhead binary tree rebalancing from +// Stout & Warren, ``Tree rebalancing in linear space and time'' +// CACM, Sept, 1986, p902. + + +void BSTSet::balance() +{ + if (count <= 2) return; // don't bother -- + // also we assume non-null root, below + + // make re-attaching the root easy via trickery + + struct _fake_node { _fake_node *lt, *rt, *par; } fake_root; + + fake_root.rt = (_fake_node*)root; + fake_root.par = 0; + BSTNode* pseudo_root = (BSTNode*)&fake_root; + + // phase 1: tree-to-vine + + BSTNode* vine_tail = pseudo_root; + BSTNode* remainder = root; + + while (remainder != 0) + { + if (remainder->lt == 0) + { + vine_tail = remainder; + remainder = remainder->rt; + } + else + { + BSTNode* tmp = remainder->lt; + remainder->lt = tmp->rt; + if (remainder->lt != 0) remainder->lt->par = remainder; + tmp->rt = remainder; + remainder->par = tmp; + vine_tail->rt = remainder = tmp; + } + } + + // phase 2: vine-to-tree + + // Uses the slightly simpler version adapted from + // Day ``Balancing a binary tree'' Computer Journal, Nov. 1976, + // since it's not generally important whether the `stray' leaves are + // on the left or on the right. + + unsigned int spines = count - 1; + while (spines > 1) + { + int compressions = spines >> 1; // compress every other node + spines -= compressions + 1; // halve for next time + + BSTNode* scanner = pseudo_root; + while (compressions-- > 0) + { + BSTNode* child = scanner->rt; + BSTNode* grandchild = child->rt; + scanner->rt = grandchild; + grandchild->par = scanner; + child->rt = grandchild->lt; + if (child->rt != 0) child->rt->par = child; + grandchild->lt = child; + child->par = grandchild; + scanner = grandchild; + } + } + + root = pseudo_root->rt; + root->par = 0; +} + + +int BSTSet::OK() +{ + int v = 1; + if (root == 0) + v = count == 0; + else + { + int n = 1; + BSTNode* trail = leftmost(); + BSTNode* t = succ(trail); + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/BSTSet.hP b/gnu/lib/libg++/g++-include/BSTSet.hP new file mode 100644 index 0000000000..82f2069c0f --- /dev/null +++ b/gnu/lib/libg++/g++-include/BSTSet.hP @@ -0,0 +1,152 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _BSTSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _BSTSet_h 1 + +#include ".Set.h" + +#ifndef _BSTNode +#define _BSTNode 1 + +struct BSTNode +{ + BSTNode* lt; + BSTNode* rt; + BSTNode* par; + item; + BSTNode( h, BSTNode* l=0, BSTNode* r=0, + BSTNode* p = 0); + ~BSTNode(); +}; + +inline BSTNode::BSTNode( h, BSTNode* l, BSTNode* r, + BSTNode* p) +:item(h), lt(l), rt(r), par(p) {} + +inline BSTNode::~BSTNode() {} + +typedef BSTNode* BSTNodePtr; + +#endif + +class BSTSet : public Set +{ +protected: + BSTNode* root; + + BSTNode* leftmost(); + BSTNode* rightmost(); + BSTNode* pred(BSTNode* t); + BSTNode* succ(BSTNode* t); + void _kill(BSTNode* t); + BSTNode* _copy(BSTNode* t); + +public: + BSTSet(); + BSTSet(BSTSet& a); + ~BSTSet(); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + Pix seek( item); + + Pix last(); + void prev(Pix& i); + + int operator == (BSTSet& b); + int operator != (BSTSet& b); + int operator <= (BSTSet& b); + + void balance(); + int OK(); +}; + +inline BSTSet::~BSTSet() +{ + _kill(root); +} + +inline BSTSet::BSTSet() +{ + root = 0; + count = 0; +} + + +inline BSTSet::BSTSet(BSTSet& a) +{ + count = a.count; + root = _copy(a.root); +} + +inline int BSTSet::operator != (BSTSet& b) +{ + return ! (*this == b); +} + +inline Pix BSTSet::first() +{ + return Pix(leftmost()); +} + +inline Pix BSTSet::last() +{ + return Pix(rightmost()); +} + +inline void BSTSet::next(Pix& i) +{ + if (i != 0) i = Pix(succ((BSTNode*)i)); +} + +inline void BSTSet::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((BSTNode*)i)); +} + +inline & BSTSet::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((BSTNode*)i)->item; +} + +inline void BSTSet::clear() +{ + _kill(root); + count = 0; + root = 0; +} + +inline int BSTSet::contains( key) +{ + return seek(key) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/Bag.ccP b/gnu/lib/libg++/g++-include/Bag.ccP new file mode 100644 index 0000000000..836d0d6656 --- /dev/null +++ b/gnu/lib/libg++/g++-include/Bag.ccP @@ -0,0 +1,74 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".Bag.h" + +// error handling + +void Bag::error(const char* msg) +{ + (*lib_error_handler)("Bag", msg); +} + + +Pix Bag::seek( item, Pix i) +{ + if (i == 0) + i = first(); + else + next(i); + for (;i != 0 && (!(EQ((*this)(i), item))); next(i)); + return i; +} + +int Bag::owns(Pix p) +{ + if (p == 0) return 0; + for (Pix i = first(); i != 0; next(i)) if (i == p) return 1; + return 0; +} + +void Bag::remove( item) +{ + int i = nof(item); + while (i-- > 0) del(item); +} + + +int Bag::nof( item) +{ + int n = 0; + for (Pix p = first(); p; next(p)) if (EQ((*this)(p), item)) ++n; + return n; +} + +void Bag::clear() +{ + Pix i = first(); + while (i != 0) + { + del((*this)(i)); + i = first(); + } +} + + diff --git a/gnu/lib/libg++/g++-include/Bag.hP b/gnu/lib/libg++/g++-include/Bag.hP new file mode 100644 index 0000000000..4b9a87a909 --- /dev/null +++ b/gnu/lib/libg++/g++-include/Bag.hP @@ -0,0 +1,79 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _Bag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Bag_h 1 + +#include +#include ".defs.h" + +class Bag +{ +protected: + int count; + +public: + virtual ~Bag(); + + int length(); // current number of items + int empty(); + + virtual Pix add( item) = 0; // add item; return Pix + + virtual void del( item) = 0; // delete 1 occurrence of item +#undef remove + virtual void remove( item); // delete all occurrences + virtual void clear(); // delete all items + + virtual int contains( item); // is item in Bag? + virtual int nof( item); // how many in Bag? + + virtual Pix first() = 0; // Pix of first item or 0 + virtual void next(Pix& i) = 0; // advance to next or 0 + + virtual & operator () (Pix i) = 0; // access item at i + + virtual Pix seek( item, Pix from=0); // Pix of next occurrence + virtual int owns(Pix i); // is i a valid Pix ? + + void error(const char* msg); + virtual int OK() = 0; // rep invariant +}; + +inline Bag::~Bag() {} + +inline int Bag::length() +{ + return count; +} + +inline int Bag::empty() +{ + return count == 0; +} + +inline int Bag::contains( item) +{ + return seek(item) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/Binomial.h b/gnu/lib/libg++/g++-include/Binomial.h deleted file mode 100644 index bae38a5df5..0000000000 --- a/gnu/lib/libg++/g++-include/Binomial.h +++ /dev/null @@ -1,63 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ -#ifndef _Binomial_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Binomial_h 1 - -#include - -class Binomial: public Random { -protected: - int pN; - double pU; -public: - Binomial(int n, double u, RNG *gen); - - int n(); - int n(int xn); - - double u(); - double u(int xu); - - virtual double operator()(); - -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline Binomial::Binomial(int n, double u, RNG *gen) -: (gen){ - pN = n; pU = u; -} - -inline int Binomial::n() { return pN; } -inline int Binomial::n(int xn) { int tmp = pN; pN = xn; return tmp; } - -inline double Binomial::u() { return pU; } -inline double Binomial::u(int xu) { double tmp = pU; pU = xu; return tmp; } - -#endif -#endif diff --git a/gnu/lib/libg++/g++-include/BitSet.h b/gnu/lib/libg++/g++-include/BitSet.h deleted file mode 100644 index a99340ecea..0000000000 --- a/gnu/lib/libg++/g++-include/BitSet.h +++ /dev/null @@ -1,385 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifndef _BitSet_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif - -#define _BitSet_h 1 - -#include -#include - -#define BITSETBITS BITS(short) - -struct BitSetRep -{ - unsigned short len; // number of shorts in s - unsigned short sz; // allocated slots - unsigned short virt; // virtual 0 or 1 - unsigned short s[1]; // bits start here -}; - -extern BitSetRep* BitSetalloc(BitSetRep*, const unsigned short*, - int, int, int); -extern BitSetRep* BitSetcopy(BitSetRep*, const BitSetRep*); -extern BitSetRep* BitSetresize(BitSetRep*, int); -extern BitSetRep* BitSetop(const BitSetRep*, const BitSetRep*, - BitSetRep*, char); -extern BitSetRep* BitSetcmpl(const BitSetRep*, BitSetRep*); - - -extern BitSetRep _nilBitSetRep; - -class BitSet; - -class BitSetBit -{ -protected: - BitSet* src; - unsigned long pos; - - public: - BitSetBit(BitSet* v, int p); - BitSetBit(const BitSetBit& b); - ~BitSetBit(); - operator int(); - int operator = (int b); - int operator == (int b); - int operator != (int b); -}; - -class BitSet -{ -protected: - BitSetRep* rep; - - -public: - -// constructors - BitSet(); - BitSet(const BitSet&); - - ~BitSet(); - - void operator = (const BitSet& y); - -// equality & subset tests - - friend int operator == (const BitSet& x, const BitSet& y); - friend int operator != (const BitSet& x, const BitSet& y); - friend int operator < (const BitSet& x, const BitSet& y); - friend int operator <= (const BitSet& x, const BitSet& y); - friend int operator > (const BitSet& x, const BitSet& y); - friend int operator >= (const BitSet& x, const BitSet& y); - - -// operations on self - - void operator |= (const BitSet& y); - void operator &= (const BitSet& y); - void operator -= (const BitSet& y); - void operator ^= (const BitSet& y); - - void complement(); - -// individual bit manipulation - - void set(int pos); - void set(int from, int to); - void set(); // set all - - void clear(int pos); - void clear(int from, int to); - void clear(); // clear all - - void invert(int pos); - void invert(int from, int to); - - int test(int pos) const; - int test(int from, int to) const; - - BitSetBit operator [] (int i); - -// iterators - - int first(int b = 1) const; - int last(int b = 1) const; - - int next(int pos, int b = 1) const; - int previous(int pos, int b = 1) const; - -// status - - int empty() const; - int virtual_bit() const; - int count(int b = 1) const; - -// convertors & IO - - friend BitSet atoBitSet(const char* s, - char f='0', char t='1', char star='*'); - friend const char* BitSettoa(const BitSet& x, - char f='0', char t='1', char star='*'); - - friend BitSet shorttoBitSet(unsigned short w); - friend BitSet longtoBitSet(unsigned long w); - - friend ostream& operator << (ostream& s, const BitSet& x); - -// procedural versions of operators - - friend void and(const BitSet& x, const BitSet& y, BitSet& r); - friend void or(const BitSet& x, const BitSet& y, BitSet& r); - friend void xor(const BitSet& x, const BitSet& y, BitSet& r); - friend void diff(const BitSet& x, const BitSet& y, BitSet& r); - friend void complement(const BitSet& x, BitSet& r); - -// misc - - volatile void error(const char* msg) const; - int OK() const; -}; - - -typedef BitSet BitSetTmp; - - - BitSet operator | (const BitSet& x, const BitSet& y); - BitSet operator & (const BitSet& x, const BitSet& y); - BitSet operator - (const BitSet& x, const BitSet& y); - BitSet operator ^ (const BitSet& x, const BitSet& y); - - BitSet operator ~ (const BitSet& x); - -// These are inlined regardless of optimization - -inline int BitSet_index(int l) -{ - return (unsigned)(l) / BITSETBITS; -} - -inline int BitSet_pos(int l) -{ - return l & (BITSETBITS - 1); -} - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - - -inline BitSet::BitSet() : rep(&_nilBitSetRep) {} - -inline BitSet::BitSet(const BitSet& x) :rep(BitSetcopy(0, x.rep)) {} - -inline BitSet::~BitSet() { if (rep != &_nilBitSetRep) delete rep; } - -inline void BitSet::operator = (const BitSet& y) -{ - rep = BitSetcopy(rep, y.rep); -} - -inline int operator != (const BitSet& x, const BitSet& y) { return !(x == y); } - -inline int operator > (const BitSet& x, const BitSet& y) { return y < x; } - -inline int operator >= (const BitSet& x, const BitSet& y) { return y <= x; } - -inline void and(const BitSet& x, const BitSet& y, BitSet& r) -{ - r.rep = BitSetop(x.rep, y.rep, r.rep, '&'); -} - -inline void or(const BitSet& x, const BitSet& y, BitSet& r) -{ - r.rep = BitSetop(x.rep, y.rep, r.rep, '|'); -} - -inline void xor(const BitSet& x, const BitSet& y, BitSet& r) -{ - r.rep = BitSetop(x.rep, y.rep, r.rep, '^'); -} - -inline void diff(const BitSet& x, const BitSet& y, BitSet& r) -{ - r.rep = BitSetop(x.rep, y.rep, r.rep, '-'); -} - -inline void complement(const BitSet& x, BitSet& r) -{ - r.rep = BitSetcmpl(x.rep, r.rep); -} - -#if defined(__GNUG__) && !defined(NO_NRV) - -inline BitSet operator & (const BitSet& x, const BitSet& y) return r -{ - and(x, y, r); -} - -inline BitSet operator | (const BitSet& x, const BitSet& y) return r -{ - or(x, y, r); -} - -inline BitSet operator ^ (const BitSet& x, const BitSet& y) return r -{ - xor(x, y, r); -} - -inline BitSet operator - (const BitSet& x, const BitSet& y) return r -{ - diff(x, y, r); -} - -inline BitSet operator ~ (const BitSet& x) return r -{ - ::complement(x, r); -} - -#else /* NO_NRV */ - -inline BitSet operator & (const BitSet& x, const BitSet& y) -{ - BitSet r; and(x, y, r); return r; -} - -inline BitSet operator | (const BitSet& x, const BitSet& y) -{ - BitSet r; or(x, y, r); return r; -} - -inline BitSet operator ^ (const BitSet& x, const BitSet& y) -{ - BitSet r; xor(x, y, r); return r; -} - -inline BitSet operator - (const BitSet& x, const BitSet& y) -{ - BitSet r; diff(x, y, r); return r; -} - -inline BitSet operator ~ (const BitSet& x) -{ - BitSet r; ::complement(x, r); return r; -} - -#endif - -inline void BitSet::operator &= (const BitSet& y) -{ - and(*this, y, *this); -} - -inline void BitSet::operator |= (const BitSet& y) -{ - or(*this, y, *this); -} - -inline void BitSet::operator ^= (const BitSet& y) -{ - xor(*this, y, *this); -} - -inline void BitSet::operator -= (const BitSet& y) -{ - diff(*this, y, *this); -} - - -inline void BitSet::complement() -{ - ::complement(*this, *this); -} - -inline int BitSet::virtual_bit() const -{ - return rep->virt; -} - -inline int BitSet::first(int b) const -{ - return next(-1, b); -} - -inline int BitSet::test(int p) const -{ - if (p < 0) error("Illegal bit index"); - int index = BitSet_index(p); - return (index >= rep->len)? rep->virt : - ((rep->s[index] & (1 << BitSet_pos(p))) != 0); -} - - -inline void BitSet::clear() -{ - if (rep->len > 0) bzero(rep->s, rep->sz * sizeof(short)); - rep->len = rep->virt = 0; -} - -inline void BitSet::set() -{ - rep = BitSetalloc(rep, 0, 0, 1, 0); -} - -inline BitSetBit::BitSetBit(const BitSetBit& b) :src(b.src), pos(b.pos) {} - -inline BitSetBit::BitSetBit(BitSet* v, int p) -{ - src = v; pos = p; -} - -inline BitSetBit::~BitSetBit() {} - -inline BitSetBit::operator int() -{ - return src->test(pos); -} - -inline int BitSetBit::operator = (int b) -{ - if (b) src->set(pos); else src->clear(pos); return b; -} - -inline int BitSetBit::operator == (int b) -{ - return src->test(pos) == b; -} - -inline int BitSetBit::operator != (int b) -{ - return src->test(pos) != b; -} - -inline BitSetBit BitSet::operator [] (int i) -{ - if (i < 0) error("illegal bit index"); - return BitSetBit(this, i); -} - - - -#endif -#endif diff --git a/gnu/lib/libg++/g++-include/BitString.h b/gnu/lib/libg++/g++-include/BitString.h deleted file mode 100644 index bdcf4c17e4..0000000000 --- a/gnu/lib/libg++/g++-include/BitString.h +++ /dev/null @@ -1,773 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifndef _BitString_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif - -#define _BitString_h 1 - -#include -#include - -#define BITSTRBITS BITS(short) - -struct BitStrRep -{ - unsigned int len; // length in bits - unsigned short sz; // allocated slots - unsigned short s[1]; // bits start here -}; - -extern BitStrRep* BStr_alloc(BitStrRep*, const unsigned short*, int, int,int); -extern BitStrRep* BStr_resize(BitStrRep*, int); -extern BitStrRep* BStr_copy(BitStrRep*, const BitStrRep*); -extern BitStrRep* cmpl(const BitStrRep*, BitStrRep*); -extern BitStrRep* and(const BitStrRep*, const BitStrRep*, BitStrRep*); -extern BitStrRep* or(const BitStrRep*, const BitStrRep*, BitStrRep*); -extern BitStrRep* xor(const BitStrRep*, const BitStrRep*, BitStrRep*); -extern BitStrRep* diff(const BitStrRep*, const BitStrRep*, BitStrRep*); -extern BitStrRep* cat(const BitStrRep*, const BitStrRep*, BitStrRep*); -extern BitStrRep* cat(const BitStrRep*, unsigned int, BitStrRep*); -extern BitStrRep* lshift(const BitStrRep*, int, BitStrRep*); - - -class BitString; -class BitPattern; - -class BitStrBit -{ -protected: - BitString& src; - unsigned int pos; - - public: - BitStrBit(BitString& v, int p); - BitStrBit(const BitStrBit& b); - ~BitStrBit(); - operator unsigned int() const; - int operator = (unsigned int b); - int operator == (unsigned int b) const ; - int operator != (unsigned int b) const ; -}; - -class BitSubString -{ - friend class BitString; - friend class BitPattern; - -protected: - - BitString& S; - int pos; - int len; - - BitSubString(BitString& x, int p, int l); - BitSubString(const BitSubString& x); -public: - ~BitSubString(); - - void operator = (const BitString&); - void operator = (const BitSubString&); - - int length() const; - int empty() const; - - int OK() const; -}; - -class BitString -{ - friend class BitSubString; - friend class BitPattern; -protected: - BitStrRep* rep; - - int search(int, int, const unsigned short*, int, int) const; - int match(int, int, int, const unsigned short*,int,int) const; - BitSubString _substr(int first, int l); - -public: - -// constructors - BitString(); - BitString(const BitString&); - BitString(const BitSubString& y); - - ~BitString(); - - void operator = (unsigned int bit); - void operator = (const BitString& y); - void operator = (const BitSubString& y); - -// equality & subset tests - - friend int operator == (const BitString&, const BitString&); - friend int operator != (const BitString&, const BitString&); - friend int operator < (const BitString&, const BitString&); - friend int operator <= (const BitString&, const BitString&); - friend int operator > (const BitString&, const BitString&); - friend int operator >= (const BitString&, const BitString&); - -// procedural versions of operators - - - friend void and(const BitString&, const BitString&, BitString&); - friend void or(const BitString&, const BitString&, BitString&); - friend void xor(const BitString&, const BitString&, BitString&); - friend void diff(const BitString&, const BitString&, BitString&); - friend void cat(const BitString&, const BitString&, BitString&); - friend void cat(const BitString&, unsigned int, BitString&); - friend void lshift(const BitString&, int, BitString&); - friend void rshift(const BitString&, int, BitString&); - - friend void complement(const BitString&, BitString&); - - friend int lcompare(const BitString&, const BitString&); - -// assignment-based operators -// (constuctive versions decalred inline below - - void operator |= (const BitString&); - void operator &= (const BitString&); - void operator -= (const BitString&); - void operator ^= (const BitString&); - void operator += (const BitString&); - void operator += (unsigned int b); - void operator <<=(int s); - void operator >>=(int s); - - void complement(); - -// individual bit manipulation - - void set(int pos); - void set(int from, int to); - void set(); - - void clear(int pos); - void clear(int from, int to); - void clear(); - - void invert(int pos); - void invert(int from, int to); - - int test(int pos) const; - int test(int from, int to) const; - - void assign(int p, unsigned int bit); - -// indexing - - BitStrBit operator [] (int pos); - -// iterators - - int first(unsigned int bit = 1) const; - int last(unsigned int b = 1) const; - - int next(int pos, unsigned int b = 1) const; - int previous(int pos, unsigned int b = 1) const; - -// searching & matching - - int index(unsigned int bit, int startpos = 0) const ; - int index(const BitString&, int startpos = 0) const; - int index(const BitSubString&, int startpos = 0) const; - int index(const BitPattern&, int startpos = 0) const; - - int contains(const BitString&) const; - int contains(const BitSubString&) const; - int contains(const BitPattern&) const; - - int contains(const BitString&, int pos) const; - int contains(const BitSubString&, int pos) const; - int contains(const BitPattern&, int pos) const; - - int matches(const BitString&, int pos = 0) const; - int matches(const BitSubString&, int pos = 0) const; - int matches(const BitPattern&, int pos = 0) const; - -// BitSubString extraction - - BitSubString at(int pos, int len); - BitSubString at(const BitString&, int startpos = 0); - BitSubString at(const BitSubString&, int startpos = 0); - BitSubString at(const BitPattern&, int startpos = 0); - - BitSubString before(int pos); - BitSubString before(const BitString&, int startpos = 0); - BitSubString before(const BitSubString&, int startpos = 0); - BitSubString before(const BitPattern&, int startpos = 0); - - BitSubString after(int pos); - BitSubString after(const BitString&, int startpos = 0); - BitSubString after(const BitSubString&, int startpos = 0); - BitSubString after(const BitPattern&, int startpos = 0); - -// other friends & utilities - - friend BitString common_prefix(const BitString&, const BitString&, - int pos = 0); - friend BitString common_suffix(const BitString&, const BitString&, - int pos = -1); - friend BitString reverse(const BitString&); - - void right_trim(unsigned int bit); - void left_trim(unsigned int bit); - -// status - - int empty() const ; - int count(unsigned int bit = 1) const; - int length() const; - -// convertors & IO - - friend BitString atoBitString(const char* s, char f='0', char t='1'); - friend const char* BitStringtoa(const BitString&, char f='0', char t='1'); - - friend BitString shorttoBitString(unsigned short); - friend BitString longtoBitString(unsigned long); - - friend ostream& operator << (ostream& s, const BitString&); - -// misc - - volatile void error(const char* msg) const; - -// indirect friends - - friend const char* BitPatterntoa(const BitPattern& p, - char f='0',char t='1',char x='X'); - friend BitPattern atoBitPattern(const char* s, - char f='0',char t='1',char x='X'); - - int OK() const; -}; - - -class BitPattern -{ -public: - BitString pattern; - BitString mask; - - BitPattern(); - BitPattern(const BitPattern&); - BitPattern(const BitString& p, const BitString& m); - - ~BitPattern(); - - friend const char* BitPatterntoa(const BitPattern&, char f, char t, char x); - friend BitPattern atoBitPattern(const char* s, char f,char t, char x); - friend ostream& operator << (ostream& s, const BitPattern&); - - int search(const unsigned short*, int, int) const; - int match(const unsigned short* xs, int, int, int) const; - - int OK() const; -}; - -BitString operator & (const BitString& x, const BitString& y); -BitString operator | (const BitString& x, const BitString& y); -BitString operator ^ (const BitString& x, const BitString& y); -BitString operator << (const BitString& x, int y); -BitString operator >> (const BitString& x, int y); -BitString operator - (const BitString& x, const BitString& y); -BitString operator + (const BitString& x, const BitString& y); -BitString operator + (const BitString& x, unsigned int y); -BitString operator ~ (const BitString& x); -int operator != (const BitString& x, const BitString& y); -int operator>(const BitString& x, const BitString& y); -int operator>=(const BitString& x, const BitString& y); - -extern BitStrRep _nilBitStrRep; -extern BitString _nil_BitString; - -// primitive bit extraction - -// These must be inlined regardless of optimization. - -inline int BitStr_index(int l) { return (unsigned)(l) / BITSTRBITS; } - -inline int BitStr_pos(int l) { return l & (BITSTRBITS - 1); } - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -// constructors & assignment - -inline BitString::BitString() :rep(&_nilBitStrRep) {} - -inline BitString::BitString(const BitString& x) :rep(BStr_copy(0, x.rep)) {} - -inline BitString::BitString(const BitSubString& y) - :rep (BStr_alloc(0, y.S.rep->s, y.pos, y.pos+y.len, y.len)) {} - -inline BitString::~BitString() -{ - if (rep != &_nilBitStrRep) delete rep; -} - -#if defined(__GNUG__) && !defined(NO_NRV) - -inline BitString shorttoBitString(unsigned short w) return r -{ - r.rep = BStr_alloc(0, &w, 0, BITSTRBITS, BITSTRBITS); -} - -inline BitString longtoBitString(unsigned long w) return r -{ - unsigned short u[2]; - u[0] = w & ((unsigned short)(~(0))); - u[1] = w >> BITSTRBITS; - r.rep = BStr_alloc(0, &u[0], 0, 2*BITSTRBITS, 2*BITSTRBITS); -} - -#else - -inline BitString shorttoBitString(unsigned short w) -{ - BitString r; r.rep = BStr_alloc(0, &w, 0, BITSTRBITS, BITSTRBITS); return r; -} - -inline BitString longtoBitString(unsigned long w) -{ - BitString r; - unsigned short u[2]; - u[0] = w & ((unsigned short)(~(0))); - u[1] = w >> BITSTRBITS; - r.rep = BStr_alloc(0, &u[0], 0, 2*BITSTRBITS, 2*BITSTRBITS); - return r; -} - -#endif - -inline void BitString::operator = (const BitString& y) -{ - rep = BStr_copy(rep, y.rep); -} - -inline void BitString::operator = (unsigned int b) -{ - unsigned short bit = b; - rep = BStr_alloc(rep, &bit, 0, 1, 1); -} - -inline void BitString::operator=(const BitSubString& y) -{ - rep = BStr_alloc(rep, y.S.rep->s, y.pos, y.pos+y.len, y.len); -} - -inline BitSubString::BitSubString(const BitSubString& x) - :S(x.S), pos(x.pos), len(x.len) {} - -inline BitSubString::BitSubString(BitString& x, int p, int l) - : S(x), pos(p), len(l) {} - -inline BitSubString::~BitSubString() {} - -inline BitPattern::BitPattern(const BitString& p, const BitString& m) - :pattern(p), mask(m) {} - -inline BitPattern::BitPattern(const BitPattern& b) - :pattern(b.pattern), mask(b.mask) {} - -inline BitPattern::BitPattern() {} -inline BitPattern::~BitPattern() {} - - -// procedural versions of operators - -inline void and(const BitString& x, const BitString& y, BitString& r) -{ - r.rep = and(x.rep, y.rep, r.rep); -} - -inline void or(const BitString& x, const BitString& y, BitString& r) -{ - r.rep = or(x.rep, y.rep, r.rep); -} - -inline void xor(const BitString& x, const BitString& y, BitString& r) -{ - r.rep = xor(x.rep, y.rep, r.rep); -} - -inline void diff(const BitString& x, const BitString& y, BitString& r) -{ - r.rep = diff(x.rep, y.rep, r.rep); -} - -inline void cat(const BitString& x, const BitString& y, BitString& r) -{ - r.rep = cat(x.rep, y.rep, r.rep); -} - -inline void cat(const BitString& x, unsigned int y, BitString& r) -{ - r.rep = cat(x.rep, y, r.rep); -} - -inline void rshift(const BitString& x, int y, BitString& r) -{ - r.rep = lshift(x.rep, -y, r.rep); -} - -inline void lshift(const BitString& x, int y, BitString& r) -{ - r.rep = lshift(x.rep, y, r.rep); -} - -inline void complement(const BitString& x, BitString& r) -{ - r.rep = cmpl(x.rep, r.rep); -} - -// operators - - -inline void BitString::operator &= (const BitString& y) -{ - and(*this, y, *this); -} - - -inline void BitString::operator |= (const BitString& y) -{ - or(*this, y, *this); -} - -inline void BitString::operator ^= (const BitString& y) -{ - xor(*this, y, *this); -} - -inline void BitString::operator <<= (int y) -{ - lshift(*this, y, *this); -} - -inline void BitString::operator >>= (int y) -{ - rshift(*this, y, *this); -} - -inline void BitString::operator -= (const BitString& y) -{ - diff(*this, y, *this); -} - -inline void BitString::operator += (const BitString& y) -{ - cat(*this, y, *this); -} - -inline void BitString::operator += (unsigned int y) -{ - cat(*this, y, *this); -} - -inline void BitString::complement() -{ - ::complement(*this, *this); -} - -#if defined(__GNUG__) && !defined(NO_NRV) - -inline BitString operator & (const BitString& x, const BitString& y) return r -{ - and(x, y, r); -} - -inline BitString operator | (const BitString& x, const BitString& y) return r -{ - or(x, y, r); -} - -inline BitString operator ^ (const BitString& x, const BitString& y) return r -{ - xor(x, y, r); -} - -inline BitString operator << (const BitString& x, int y) return r -{ - lshift(x, y, r); -} - -inline BitString operator >> (const BitString& x, int y) return r -{ - rshift(x, y, r); -} - -inline BitString operator - (const BitString& x, const BitString& y) return r -{ - diff(x, y, r); -} - -inline BitString operator + (const BitString& x, const BitString& y) return r -{ - cat(x, y, r); -} - -inline BitString operator + (const BitString& x, unsigned int y) return r -{ - cat(x, y, r); -} - -inline BitString operator ~ (const BitString& x) return r -{ - complement(x, r); -} - -#else /* NO_NRV */ - -inline BitString operator & (const BitString& x, const BitString& y) -{ - BitString r; and(x, y, r); return r; -} - -inline BitString operator | (const BitString& x, const BitString& y) -{ - BitString r; or(x, y, r); return r; -} - -inline BitString operator ^ (const BitString& x, const BitString& y) -{ - BitString r; xor(x, y, r); return r; -} - -inline BitString operator << (const BitString& x, int y) -{ - BitString r; lshift(x, y, r); return r; -} - -inline BitString operator >> (const BitString& x, int y) -{ - BitString r; rshift(x, y, r); return r; -} - -inline BitString operator - (const BitString& x, const BitString& y) -{ - BitString r; diff(x, y, r); return r; -} - -inline BitString operator + (const BitString& x, const BitString& y) -{ - BitString r; cat(x, y, r); return r; -} - -inline BitString operator + (const BitString& x, unsigned int y) -{ - BitString r; cat(x, y, r); return r; -} - -inline BitString operator ~ (const BitString& x) -{ - BitString r; complement(x, r); return r; -} - -#endif - -// status, matching - -inline int BitString::length() const -{ - return rep->len; -} - -inline int BitString::empty() const -{ - return rep->len == 0; -} - -inline int BitString::index(const BitString& y, int startpos) const -{ - return search(startpos, rep->len, y.rep->s, 0, y.rep->len); -} - -inline int BitString::index(const BitSubString& y, int startpos) const -{ - return search(startpos, rep->len, y.S.rep->s, y.pos, y.pos+y.len); -} - -inline int BitString::contains(const BitString& y) const -{ - return search(0, rep->len, y.rep->s, 0, y.rep->len) >= 0; -} - -inline int BitString::contains(const BitSubString& y) const -{ - return search(0, rep->len, y.S.rep->s, y.pos, y.pos+y.len) >= 0; -} - -inline int BitString::contains(const BitString& y, int p) const -{ - return match(p, rep->len, 0, y.rep->s, 0, y.rep->len); -} - -inline int BitString::matches(const BitString& y, int p) const -{ - return match(p, rep->len, 1, y.rep->s, 0, y.rep->len); -} - -inline int BitString::contains(const BitSubString& y, int p) const -{ - return match(p, rep->len, 0, y.S.rep->s, y.pos, y.pos+y.len); -} - -inline int BitString::matches(const BitSubString& y, int p) const -{ - return match(p, rep->len, 1, y.S.rep->s, y.pos, y.pos+y.len); -} - -inline int BitString::contains(const BitPattern& r) const -{ - return r.search(rep->s, 0, rep->len) >= 0; -} - -inline int BitString::contains(const BitPattern& r, int p) const -{ - return r.match(rep->s, p, rep->len, 0); -} - -inline int BitString::matches(const BitPattern& r, int p) const -{ - return r.match(rep->s, p, rep->len, 1); -} - -inline int BitString::index(const BitPattern& r, int startpos) const -{ - return r.search(rep->s, startpos, rep->len); -} - -inline int BitSubString::length() const -{ - return len; -} - -inline int BitSubString::empty() const -{ - return len == 0; -} - -inline int operator != (const BitString& x, const BitString& y) -{ - return !(x == y); -} - -inline int operator>(const BitString& x, const BitString& y) -{ - return y < x; -} - -inline int operator>=(const BitString& x, const BitString& y) -{ - return y <= x; -} - -inline int BitString::first(unsigned int b) const -{ - return next(-1, b); -} - -inline int BitString::last(unsigned int b) const -{ - return previous(rep->len, b); -} - -inline int BitString::index(unsigned int bit, int startpos) const -{ - if (startpos >= 0) - return next(startpos - 1, bit); - else - return previous(rep->len + startpos + 1, bit); -} - -inline void BitString::right_trim(unsigned int b) -{ - int nb = (b == 0)? 1 : 0; - rep = BStr_resize(rep, previous(rep->len, nb) + 1); -} - -inline void BitString::left_trim(unsigned int b) -{ - int nb = (b == 0)? 1 : 0; - int p = next(-1, nb); - rep = BStr_alloc(rep, rep->s, p, rep->len, rep->len - p); -} - -inline int BitString::test(int i) const -{ - return ((unsigned)(i) >= rep->len)? 0 : - ((rep->s[BitStr_index(i)] & (1 << (BitStr_pos(i)))) != 0); -} - - -// subscripting - -inline BitStrBit::BitStrBit(const BitStrBit& b) :src(b.src), pos(b.pos) {} - -inline BitStrBit::BitStrBit(BitString& v, int p) :src(v), pos(p) {} - -inline BitStrBit::~BitStrBit() {} - -inline BitStrBit::operator unsigned int() const -{ - return src.test(pos); -} - -inline int BitStrBit::operator = (unsigned int b) -{ - src.assign(pos, b); return b; -} - -inline int BitStrBit::operator == (unsigned int b) const -{ - return src.test(pos) == b; -} - -inline int BitStrBit::operator != (unsigned int b) const -{ - return src.test(pos) != b; -} - -inline BitStrBit BitString::operator [] (int i) -{ - if ((unsigned)(i) >= rep->len) error("illegal bit index"); - return BitStrBit(*this, i); -} - -inline BitSubString BitString::_substr(int first, int l) -{ - if (first < 0 || l <= 0 || (unsigned)(first + l) > rep->len) - return BitSubString(_nil_BitString, 0, 0) ; - else - return BitSubString(*this, first, l); -} - -#endif - -#endif diff --git a/gnu/lib/libg++/g++-include/CHBag.ccP b/gnu/lib/libg++/g++-include/CHBag.ccP new file mode 100644 index 0000000000..16649ac22f --- /dev/null +++ b/gnu/lib/libg++/g++-include/CHBag.ccP @@ -0,0 +1,209 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".CHBag.h" + +// The nodes are linked together serially via a version +// of a trick used in some vtables: odd pointers are +// actually links to the next table entry. +// Not terrible, but not wonderful either + +static inline int goodCHptr(CHNode* t) +{ + return ((((unsigned)t) & 1) == 0); +} + +static inline CHNode* index_to_CHptr(int i) +{ + return (CHNode*)((i << 1) + 1); +} + +static inline int CHptr_to_index(CHNode* t) +{ + return ( ((unsigned) t) >> 1); +} + +CHBag::CHBag(unsigned int sz) +{ + tab = (CHNode**)(new CHNodePtr[size = sz]); + for (unsigned int i = 0; i < size; ++i) tab[i] = index_to_CHptr(i+1); + count = 0; +} + +CHBag::CHBag(CHBag& a) +{ + tab = (CHNode**)(new CHNodePtr[size = a.size]); + for (unsigned int i = 0; i < size; ++i) tab[i] = index_to_CHptr(i+1); + count = 0; + for (Pix p = a.first(); p; a.next(p)) add(a(p)); +} + + +Pix CHBag::seek( key, Pix i) +{ + CHNode* p = (CHNode*)i; + if (p == 0 || !EQ(p->hd, key)) + { + unsigned int h = HASH(key) % size; + for (CHNode* t = tab[h]; goodCHptr(t); t = t->tl) + if (EQ(key, t->hd)) + return Pix(t); + } + else + { + for (p = p->tl; goodCHptr(p); p = p->tl) + if (EQ(p->hd, key)) + return Pix(p); + } + return 0; +} + +int CHBag::nof( key) +{ + int n = 0; + unsigned int h = HASH(key) % size; + for (CHNode* t = tab[h]; goodCHptr(t); t = t->tl) + if (EQ(key, t->hd)) ++n; + return n; +} + + +Pix CHBag::add( item) +{ + unsigned int h = HASH(item) % size; + CHNode* t = new CHNode(item); + t->tl = tab[h]; + tab[h] = t; + ++count; + return Pix(t); +} + +void CHBag::del( key) +{ + unsigned int h = HASH(key) % size; + + CHNode* t = tab[h]; + CHNode* trail = t; + while (goodCHptr(t)) + { + if (EQ(key, t->hd)) + { + if (trail == t) + tab[h] = t->tl; + else + trail->tl = t->tl; + delete t; + --count; + return; + } + trail = t; + t = t->tl; + } +} + +void CHBag::remove( key) +{ + unsigned int h = HASH(key) % size; + + CHNode* t = tab[h]; + CHNode* trail = t; + while (goodCHptr(t)) + { + if (EQ(key, t->hd)) + { + --count; + if (trail == t) + { + tab[h] = t->tl; + delete t; + t = trail = tab[h]; + } + else + { + trail->tl = t->tl; + delete t; + t = trail->tl; + } + } + else + { + trail = t; + t = t->tl; + } + } +} + + +void CHBag::clear() +{ + for (unsigned int i = 0; i < size; ++i) + { + CHNode* p = tab[i]; + tab[i] = index_to_CHptr(i+1); + while (goodCHptr(p)) + { + CHNode* nxt = p->tl; + delete(p); + p = nxt; + } + } + count = 0; +} + +Pix CHBag::first() +{ + for (unsigned int i = 0; i < size; ++i) if (goodCHptr(tab[i])) return Pix(tab[i]); + return 0; +} + +void CHBag::next(Pix& p) +{ + if (p == 0) return; + CHNode* t = ((CHNode*)p)->tl; + if (goodCHptr(t)) + p = Pix(t); + else + { + for (unsigned int i = CHptr_to_index(t); i < size; ++i) + { + if (goodCHptr(tab[i])) + { + p = Pix(tab[i]); + return; + } + } + p = 0; + } +} + +int CHBag::OK() +{ + int v = tab != 0; + int n = 0; + for (unsigned int i = 0; i < size; ++i) + { + for (CHNode* p = tab[i]; goodCHptr(p); p = p->tl) ++n; + v &= CHptr_to_index(p) == i + 1; + } + v &= count == n; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/CHBag.hP b/gnu/lib/libg++/g++-include/CHBag.hP new file mode 100644 index 0000000000..f6ca10b3b9 --- /dev/null +++ b/gnu/lib/libg++/g++-include/CHBag.hP @@ -0,0 +1,76 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _CHBag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _CHBag_h 1 + +#include ".Bag.h" + + +#include ".CHNode.h" + +class CHBag : public Bag +{ +protected: + CHNode** tab; + unsigned int size; + +public: + CHBag(unsigned int sz = DEFAULT_INITIAL_CAPACITY); + CHBag(CHBag& a); + ~CHBag(); + + Pix add( item); + void del( item); + void remove(item); + int nof( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + Pix seek( item, Pix from = 0); + + int OK(); +}; + + +inline CHBag::~CHBag() +{ + clear(); + delete tab; +} + +inline int CHBag::contains( key) +{ + return seek(key) != 0; +} + +inline & CHBag::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((CHNode*)i)->hd; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/CHMap.ccP b/gnu/lib/libg++/g++-include/CHMap.ccP new file mode 100644 index 0000000000..1bd76db5fc --- /dev/null +++ b/gnu/lib/libg++/g++-include/CHMap.ccP @@ -0,0 +1,166 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include "..CHMap.h" + +// The nodes are linked together serially via a version +// of a trick used in some vtables: odd pointers are +// actually links to the next table entry. +// Not terrible, but not wonderful either + +static inline int goodCHptr(CHNode* t) +{ + return ((((unsigned)t) & 1) == 0); +} + +static inline CHNode* index_to_CHptr(int i) +{ + return (CHNode*)((i << 1) + 1); +} + +static inline int CHptr_to_index(CHNode* t) +{ + return ( ((unsigned) t) >> 1); +} + +CHMap::CHMap( dflt, unsigned int sz) + :Map(dflt) +{ + tab = (CHNode**)(new CHNodePtr[size = sz]); + for (unsigned int i = 0; i < size; ++i) tab[i] = index_to_CHptr(i+1); + count = 0; +} + +CHMap::CHMap(CHMap& a) :Map(a.def) +{ + tab = (CHNode**)(new CHNodePtr[size = a.size]); + for (unsigned int i = 0; i < size; ++i) tab[i] = index_to_CHptr(i+1); + count = 0; + for (Pix p = a.first(); p; a.next(p)) (*this)[a.key(p)] = a.contents(p); +} + + +Pix CHMap::seek( key) +{ + unsigned int h = HASH(key) % size; + + for (CHNode* t = tab[h]; goodCHptr(t); t = t->tl) + if (EQ(key, t->hd)) + return Pix(t); + + return 0; +} + + +& CHMap::operator []( item) +{ + unsigned int h = HASH(item) % size; + + for (CHNode* t = tab[h]; goodCHptr(t); t = t->tl) + if (EQ(item, t->hd)) + return t->cont; + + t = new CHNode(item, def, tab[h]); + tab[h] = t; + ++count; + return t->cont; +} + + +void CHMap::del( key) +{ + unsigned int h = HASH(key) % size; + + CHNode* t = tab[h]; + CHNode* trail = t; + while (goodCHptr(t)) + { + if (EQ(key, t->hd)) + { + if (trail == t) + tab[h] = t->tl; + else + trail->tl = t->tl; + delete t; + --count; + return; + } + trail = t; + t = t->tl; + } +} + + +void CHMap::clear() +{ + for (unsigned int i = 0; i < size; ++i) + { + CHNode* p = tab[i]; + tab[i] = index_to_CHptr(i+1); + while (goodCHptr(p)) + { + CHNode* nxt = p->tl; + delete(p); + p = nxt; + } + } + count = 0; +} + +Pix CHMap::first() +{ + for (unsigned int i = 0; i < size; ++i) if (goodCHptr(tab[i])) return Pix(tab[i]); + return 0; +} + +void CHMap::next(Pix& p) +{ + CHNode* t = ((CHNode*)p)->tl; + if (goodCHptr(t)) + p = Pix(t); + else + { + for (unsigned int i = CHptr_to_index(t); i < size; ++i) + { + if (goodCHptr(tab[i])) + { + p = Pix(tab[i]); + return; + } + } + p = 0; + } +} + + +int CHMap::OK() +{ + int v = tab != 0; + int n = 0; + for (unsigned int i = 0; i < size; ++i) + { + for (CHNode* p = tab[i]; goodCHptr(p); p = p->tl) ++n; + v &= CHptr_to_index(p) == i + 1; + } + v &= count == n; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/CHMap.hP b/gnu/lib/libg++/g++-include/CHMap.hP new file mode 100644 index 0000000000..95441a3544 --- /dev/null +++ b/gnu/lib/libg++/g++-include/CHMap.hP @@ -0,0 +1,104 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _CHMap_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _CHMap_h 1 + +#include "..Map.h" + +#ifndef _CHNode_h +#define _CHNode_h 1 + +struct CHNode +{ + CHNode* tl; + hd; + cont; + CHNode(); + CHNode( h, c, CHNode* t = 0); + ~CHNode(); +}; + +inline CHNode::CHNode() {} + +inline CHNode::CHNode( h, c, CHNode* t) + : hd(h), cont(c), tl(t) {} + +inline CHNode::~CHNode() {} + +typedef CHNode* CHNodePtr; + +#endif + + +class CHMap : public Map +{ +protected: + CHNode** tab; + unsigned int size; + +public: + CHMap( dflt,unsigned int sz=DEFAULT_INITIAL_CAPACITY); + CHMap(CHMap& a); + ~CHMap(); + + & operator [] ( key); + + void del( key); + + Pix first(); + void next(Pix& i); + & key(Pix i); + & contents(Pix i); + + Pix seek( key); + int contains( key); + + void clear(); + int OK(); +}; + + +inline CHMap::~CHMap() +{ + clear(); + delete tab; +} + +inline int CHMap::contains( key) +{ + return seek(key) != 0; +} + +inline & CHMap::key(Pix p) +{ + if (p == 0) error("null Pix"); + return ((CHNode*)p)->hd; +} + +inline & CHMap::contents(Pix p) +{ + if (p == 0) error("null Pix"); + return ((CHNode*)p)->cont; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/CHNode.ccP b/gnu/lib/libg++/g++-include/CHNode.ccP new file mode 100644 index 0000000000..e16725fd74 --- /dev/null +++ b/gnu/lib/libg++/g++-include/CHNode.ccP @@ -0,0 +1,21 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1992 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".CHNode.h" diff --git a/gnu/lib/libg++/g++-include/CHNode.hP b/gnu/lib/libg++/g++-include/CHNode.hP new file mode 100644 index 0000000000..84e67d069b --- /dev/null +++ b/gnu/lib/libg++/g++-include/CHNode.hP @@ -0,0 +1,43 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988, 1982 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _CHNode_h +#define _CHNode_h 1 +#ifdef __GNUG__ +#pragma interface +#endif +#include ".defs.h" + +struct CHNode +{ + CHNode* tl; + hd; + CHNode(); + CHNode( h, CHNode* t = 0); + ~CHNode(); +}; + +inline CHNode::CHNode() {} + +inline CHNode::CHNode( h, CHNode* t) :hd(h), tl(t) {} + +inline CHNode::~CHNode() {} + +typedef CHNode* CHNodePtr; + +#endif diff --git a/gnu/lib/libg++/g++-include/CHSet.ccP b/gnu/lib/libg++/g++-include/CHSet.ccP new file mode 100644 index 0000000000..330506cb19 --- /dev/null +++ b/gnu/lib/libg++/g++-include/CHSet.ccP @@ -0,0 +1,271 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".CHSet.h" + +// A CHSet is implemented as an array (tab) of buckets, each of which +// contains a pointer to a list of CHNodes. Each node contains a +// pointer to the next node in the list, and a pointer to the . +// The end of the list is marked by a next node pointer which is odd +// when considered as an integer (least significant bit = 1). The +// assumption is that CHNodes will all begin on even addresses. If +// the odd pointer is right-shifted by one bit, it becomes the index +// within the tab array of the next bucket (that is, bucket i has +// next bucket pointer 2*(i+1)+1). + +// The bucket pointers are initialized by the constructor and +// used to support the next(Pix&) method. + +// This implementation is not portable to machines with different +// pointer and integer sizes, or on which CHNodes might be aligned on +// odd byte boundaries, but allows the same pointer to be used for +// chaining within a bucket and to the next bucket. + + +static inline int goodCHptr(CHNode* t) +{ + return ((((unsigned)t) & 1) == 0); +} + +static inline CHNode* index_to_CHptr(int i) +{ + return (CHNode*)((i << 1) + 1); +} + +static inline int CHptr_to_index(CHNode* t) +{ + return ( ((unsigned) t) >> 1); +} + +CHSet::CHSet(unsigned int sz) +{ + tab = (CHNode**)(new CHNodePtr[size = sz]); + for (unsigned int i = 0; i < size; ++i) tab[i] = index_to_CHptr(i+1); + count = 0; +} + +CHSet::CHSet(CHSet& a) +{ + tab = (CHNode**)(new CHNodePtr[size = a.size]); + for (unsigned int i = 0; i < size; ++i) tab[i] = index_to_CHptr(i+1); + count = 0; + for (Pix p = a.first(); p; a.next(p)) add(a(p)); +} + + +Pix CHSet::seek( key) +{ + unsigned int h = HASH(key) % size; + + for (CHNode* t = tab[h]; goodCHptr(t); t = t->tl) + if (EQ(key, t->hd)) + return Pix(t); + + return 0; +} + + +Pix CHSet::add( item) +{ + unsigned int h = HASH(item) % size; + + for (CHNode* t = tab[h]; goodCHptr(t); t = t->tl) + if (EQ(item, t->hd)) + return Pix(t); + + ++count; + t = new CHNode(item, tab[h]); + tab[h] = t; + return Pix(t); +} + + +void CHSet::del( key) +{ + unsigned int h = HASH(key) % size; + + CHNode* t = tab[h]; + CHNode* trail = t; + while (goodCHptr(t)) + { + if (EQ(key, t->hd)) + { + if (trail == t) + tab[h] = t->tl; + else + trail->tl = t->tl; + delete t; + --count; + return; + } + trail = t; + t = t->tl; + } +} + + +void CHSet::clear() +{ + for (unsigned int i = 0; i < size; ++i) + { + CHNode* p = tab[i]; + tab[i] = index_to_CHptr(i+1); + while (goodCHptr(p)) + { + CHNode* nxt = p->tl; + delete(p); + p = nxt; + } + } + count = 0; +} + +Pix CHSet::first() +{ + for (unsigned int i = 0; i < size; ++i) if (goodCHptr(tab[i])) return Pix(tab[i]); + return 0; +} + +void CHSet::next(Pix& p) +{ + if (p == 0) return; + CHNode* t = ((CHNode*)p)->tl; + if (goodCHptr(t)) + p = Pix(t); + else + { + for (unsigned int i = CHptr_to_index(t); i < size; ++i) + { + if (goodCHptr(tab[i])) + { + p = Pix(tab[i]); + return; + } + } + p = 0; + } +} + +int CHSet::operator == (CHSet& b) +{ + if (count != b.count) + return 0; + else + { + CHNode* p; + for (unsigned int i = 0; i < size; ++i) + for (p = tab[i]; goodCHptr(p); p = p->tl) + if (b.seek(p->hd) == 0) + return 0; + for (i = 0; i < b.size; ++i) + for (p = b.tab[i]; goodCHptr(p); p = p->tl) + if (seek(p->hd) == 0) + return 0; + return 1; + } +} + +int CHSet::operator <= (CHSet& b) +{ + if (count > b.count) + return 0; + else + { + for (unsigned int i = 0; i < size; ++i) + for (CHNode* p = tab[i]; goodCHptr(p); p = p->tl) + if (b.seek(p->hd) == 0) + return 0; + return 1; + } +} + +void CHSet::operator |= (CHSet& b) +{ + if (&b == this || b.count == 0) + return; + for (unsigned int i = 0; i < b.size; ++i) + for (CHNode* p = b.tab[i]; goodCHptr(p); p = p->tl) + add(p->hd); +} + +void CHSet::operator &= (CHSet& b) +{ + for (unsigned int i = 0; i < size; ++i) + { + CHNode* t = tab[i]; + CHNode* trail = t; + while (goodCHptr(t)) + { + CHNode* nxt = t->tl; + if (b.seek(t->hd) == 0) + { + if (trail == tab[i]) + trail = tab[i] = nxt; + else + trail->tl = nxt; + delete t; + --count; + } + else + trail = t; + t = nxt; + } + } +} + +void CHSet::operator -= (CHSet& b) +{ + for (unsigned int i = 0; i < size; ++i) + { + CHNode* t = tab[i]; + CHNode* trail = t; + while (goodCHptr(t)) + { + CHNode* nxt = t->tl; + if (b.seek(t->hd) != 0) + { + if (trail == tab[i]) + trail = tab[i] = nxt; + else + trail->tl = nxt; + delete t; + --count; + } + else + trail = t; + t = nxt; + } + } +} + +int CHSet::OK() +{ + int v = tab != 0; + int n = 0; + for (unsigned int i = 0; i < size; ++i) + { + for (CHNode* p = tab[i]; goodCHptr(p); p = p->tl) ++n; + v &= CHptr_to_index(p) == i + 1; + } + v &= count == n; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/CHSet.hP b/gnu/lib/libg++/g++-include/CHSet.hP new file mode 100644 index 0000000000..f0a08de4ce --- /dev/null +++ b/gnu/lib/libg++/g++-include/CHSet.hP @@ -0,0 +1,84 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _CHSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _CHSet_h 1 + +#include ".Set.h" +#include ".CHNode.h" + +class CHSet : public Set +{ +protected: + CHNode** tab; + unsigned int size; + +public: + CHSet(unsigned int sz = DEFAULT_INITIAL_CAPACITY); + CHSet(CHSet& a); + ~CHSet(); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + Pix seek( item); + + void operator |= (CHSet& b); + void operator -= (CHSet& b); + void operator &= (CHSet& b); + + int operator == (CHSet& b); + int operator != (CHSet& b); + int operator <= (CHSet& b); + + int OK(); +}; + +inline CHSet::~CHSet() +{ + clear(); + delete tab; +} + +inline int CHSet::contains( key) +{ + return seek(key) != 0; +} + +inline & CHSet::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((CHNode*)i)->hd; +} + +inline int CHSet::operator != (CHSet& b) +{ + return ! ((*this) == b); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/Complex.h b/gnu/lib/libg++/g++-include/Complex.h deleted file mode 100644 index a16a274a1b..0000000000 --- a/gnu/lib/libg++/g++-include/Complex.h +++ /dev/null @@ -1,293 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifndef _Complex_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Complex_h 1 - - -#include -#include - -class Complex -{ -#ifdef __ATT_complex__ -public: -#else -protected: -#endif - - double re; - double im; - -public: - - double real() const; - double imag() const; - - Complex(); - Complex(const Complex& y); - Complex(double r, double i=0); - - ~Complex(); - - Complex& operator = (const Complex& y); - - Complex& operator += (const Complex& y); - Complex& operator += (double y); - Complex& operator -= (const Complex& y); - Complex& operator -= (double y); - Complex& operator *= (const Complex& y); - Complex& operator *= (double y); - - Complex& operator /= (const Complex& y); - Complex& operator /= (double y); - - void error(const char* msg) const; -}; - - -// error handlers - -extern void default_Complex_error_handler(const char*); -extern one_arg_error_handler_t Complex_error_handler; - -extern one_arg_error_handler_t - set_Complex_error_handler(one_arg_error_handler_t f); - - -// non-inline functions - -Complex operator / (const Complex& x, const Complex& y); -Complex operator / (const Complex& x, double y); -Complex operator / (double x, const Complex& y); - -Complex cos(const Complex& x); -Complex sin(const Complex& x); - -Complex cosh(const Complex& x); -Complex sinh(const Complex& x); - -Complex exp(const Complex& x); -Complex log(const Complex& x); - -Complex pow(const Complex& x, long p); -Complex pow(const Complex& x, const Complex& p); -Complex pow(const Complex& x, double y); -Complex sqrt(const Complex& x); - -istream& operator >> (istream& s, Complex& x); -ostream& operator << (ostream& s, const Complex& x); - -// other functions defined as inlines - -int operator == (const Complex& x, const Complex& y); -int operator == (const Complex& x, double y); -int operator != (const Complex& x, const Complex& y); -int operator != (const Complex& x, double y); - -Complex operator - (const Complex& x); -Complex conj(const Complex& x); -Complex operator + (const Complex& x, const Complex& y); -Complex operator + (const Complex& x, double y); -Complex operator + (double x, const Complex& y); -Complex operator - (const Complex& x, const Complex& y); -Complex operator - (const Complex& x, double y); -Complex operator - (double x, const Complex& y); -Complex operator * (const Complex& x, const Complex& y); -Complex operator * (const Complex& x, double y); -Complex operator * (double x, const Complex& y); - -double real(const Complex& x); -double imag(const Complex& x); -double abs(const Complex& x); -double norm(const Complex& x); -double arg(const Complex& x); - -Complex polar(double r, double t = 0.0); - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -// inline members - -inline double Complex::real() const { return re; } -inline double Complex::imag() const { return im; } - -inline Complex::Complex() {} -inline Complex::Complex(const Complex& y) :re(y.real()), im(y.imag()) {} -inline Complex::Complex(double r, double i) :re(r), im(i) {} - -inline Complex::~Complex() {} - -inline Complex& Complex::operator = (const Complex& y) -{ - re = y.real(); im = y.imag(); return *this; -} - -inline Complex& Complex::operator += (const Complex& y) -{ - re += y.real(); im += y.imag(); return *this; -} - -inline Complex& Complex::operator += (double y) -{ - re += y; return *this; -} - -inline Complex& Complex::operator -= (const Complex& y) -{ - re -= y.real(); im -= y.imag(); return *this; -} - -inline Complex& Complex::operator -= (double y) -{ - re -= y; return *this; -} - -inline Complex& Complex::operator *= (const Complex& y) -{ - double r = re * y.real() - im * y.imag(); - im = re * y.imag() + im * y.real(); - re = r; - return *this; -} - -inline Complex& Complex::operator *= (double y) -{ - re *= y; im *= y; return *this; -} - - -// functions - -inline int operator == (const Complex& x, const Complex& y) -{ - return x.real() == y.real() && x.imag() == y.imag(); -} - -inline int operator == (const Complex& x, double y) -{ - return x.imag() == 0.0 && x.real() == y; -} - -inline int operator != (const Complex& x, const Complex& y) -{ - return x.real() != y.real() || x.imag() != y.imag(); -} - -inline int operator != (const Complex& x, double y) -{ - return x.imag() != 0.0 || x.real() != y; -} - -inline Complex operator - (const Complex& x) -{ - return Complex(-x.real(), -x.imag()); -} - -inline Complex conj(const Complex& x) -{ - return Complex(x.real(), -x.imag()); -} - -inline Complex operator + (const Complex& x, const Complex& y) -{ - return Complex(x.real() + y.real(), x.imag() + y.imag()); -} - -inline Complex operator + (const Complex& x, double y) -{ - return Complex(x.real() + y, x.imag()); -} - -inline Complex operator + (double x, const Complex& y) -{ - return Complex(x + y.real(), y.imag()); -} - -inline Complex operator - (const Complex& x, const Complex& y) -{ - return Complex(x.real() - y.real(), x.imag() - y.imag()); -} - -inline Complex operator - (const Complex& x, double y) -{ - return Complex(x.real() - y, x.imag()); -} - -inline Complex operator - (double x, const Complex& y) -{ - return Complex(x - y.real(), -y.imag()); -} - -inline Complex operator * (const Complex& x, const Complex& y) -{ - return Complex(x.real() * y.real() - x.imag() * y.imag(), - x.real() * y.imag() + x.imag() * y.real()); -} - -inline Complex operator * (const Complex& x, double y) -{ - return Complex(x.real() * y, x.imag() * y); -} - -inline Complex operator * (double x, const Complex& y) -{ - return Complex(x * y.real(), x * y.imag()); -} - -inline double real(const Complex& x) -{ - return x.real(); -} - -inline double imag(const Complex& x) -{ - return x.imag(); -} - -inline double abs(const Complex& x) -{ - return hypot(x.real(), x.imag()); -} - -inline double norm(const Complex& x) -{ - return (x.real() * x.real() + x.imag() * x.imag()); -} - -inline double arg(const Complex& x) -{ - return atan2(x.imag(), x.real()); -} - -inline Complex polar(double r, double t) -{ - return Complex(r * cos(t), r * sin(t)); -} - -#endif __OPTIMIZE__ -#endif diff --git a/gnu/lib/libg++/g++-include/CursesW.h b/gnu/lib/libg++/g++-include/CursesW.h deleted file mode 100644 index 7056db5dc5..0000000000 --- a/gnu/lib/libg++/g++-include/CursesW.h +++ /dev/null @@ -1,397 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- - -/* -Copyright (C) 1989 Free Software Foundation - written by Eric Newton (newton@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifndef _CursesWindow_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _CursesWindow_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif - -#include - -/* - * - * C++ class for windows. - * - * - */ - -class CursesWindow -{ -protected: - static int count; // count of all active windows: - // We rely on the c++ promise that - // all otherwise uninitialized - // static class vars are set to 0 - - WINDOW * w; // the curses WINDOW - - int alloced; // true if we own the WINDOW - - CursesWindow* par; // parent, if subwindow - CursesWindow* subwins; // head of subwindows list - CursesWindow* sib; // next subwindow of parent - - void kill_subwindows(); // disable all subwindows - -public: - CursesWindow(WINDOW* &window); // useful only for stdscr - - CursesWindow(int lines, // number of lines - int cols, // number of columns - int begin_y, // line origin - int begin_x); // col origin - - CursesWindow(CursesWindow& par, // parent window - int lines, // number of lines - int cols, // number of columns - int by, // absolute or relative - int bx, // origins: - char absrel = 'a'); // if `a', by & bx are - // absolute screen pos, - // else if `r', they are - // relative to par origin - ~CursesWindow(); - -// terminal status - int lines(); // number of lines on terminal, *not* window - int cols(); // number of cols on terminal, *not* window - -// window status - int height(); // number of lines in this window - int width(); // number of cols in this window - int begx(); // smallest x coord in window - int begy(); // smallest y coord in window - int maxx(); // largest x coord in window - int maxy(); // largest x coord in window - -// window positioning - int move(int y, int x); - -// coordinate positioning - void getyx(int& y, int& x); - int mvcur(int sy, int ey, int sx, int ex); - -// input - int getch(); - int getstr(char * str); - int scanw(const char *, ...); - -// input + positioning - int mvgetch(int y, int x); - int mvgetstr(int y, int x, char * str); - int mvscanw(int, int, const char*, ...); - -// output - int addch(const char ch); - int addstr(const char * str); - int printw(const char * fmt, ...); - int inch(); - int insch(char c); - int insertln(); - -// output + positioning - int mvaddch(int y, int x, char ch); - int mvaddstr(int y, int x, char * str); - int mvprintw(int y, int x, const char * fmt, ...); - int mvinch(int y, int x); - int mvinsch(int y, int x, char ch); - -// borders - int box(char vert, char hor); - -// erasure - int erase(); - int clear(); - int clearok(cbool bf); - int clrtobot(); - int clrtoeol(); - int delch(); - int mvdelch(int y, int x); - int deleteln(); - -// screen control - int scroll(); - int scrollok(cbool bf); - int touchwin(); - int refresh(); - int leaveok(cbool bf); - int flushok(cbool bf); - int standout(); - int standend(); - -// multiple window control - int overlay(CursesWindow &win); - int overwrite(CursesWindow &win); - - -// traversal support - CursesWindow* child(); - CursesWindow* sibling(); - CursesWindow* parent(); -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - - -inline int CursesWindow::begx() -{ - return w->_begx; -} - -inline int CursesWindow::begy() -{ - return w->_begy; -} - -inline int CursesWindow::maxx() -{ - return w->_maxx; -} - -inline int CursesWindow::maxy() -{ - return w->_maxy; -} - -inline int CursesWindow::height() -{ - return maxy() - begy() + 1; -} - -inline int CursesWindow::width() -{ - return maxx() - begx() + 1; -} - -inline int CursesWindow::box(char vert, char hor) -{ - return ::box(w, vert, hor); -} - -inline int CursesWindow::overlay(CursesWindow &win) -{ - return ::overlay(w, win.w); -} - -inline int CursesWindow::overwrite(CursesWindow &win) -{ - return ::overwrite(w, win.w); -} - -inline int CursesWindow::scroll() -{ - return ::scroll(w); -} - - -inline int CursesWindow::touchwin() -{ - return ::touchwin(w); -} - -inline int CursesWindow::addch(const char ch) -{ - return ::waddch(w, ch); -} - -inline int CursesWindow::addstr(const char * str) -{ - return ::waddstr(w, str); -} - -inline int CursesWindow::clear() -{ - return ::wclear(w); -} - -inline int CursesWindow::clrtobot() -{ - return ::wclrtobot(w); -} - -inline int CursesWindow::clrtoeol() -{ - return ::wclrtoeol(w); -} - -inline int CursesWindow::delch() -{ - return ::wdelch(w); -} - -inline int CursesWindow::deleteln() -{ - return ::wdeleteln(w); -} - -inline int CursesWindow::erase() -{ - return ::werase(w); -} - -inline int CursesWindow::getch() -{ - return ::wgetch(w); -} - -inline int CursesWindow::getstr(char * str) -{ - return ::wgetstr(w, str); -} - -inline int CursesWindow::inch() -{ - return winch(w); -} - -inline int CursesWindow::insch(char c) -{ - return ::winsch(w, c); -} - -inline int CursesWindow::insertln() -{ - return ::winsertln(w); -} - -inline int CursesWindow::move(int y, int x) -{ - return ::wmove(w, y, x); -} - - -inline int CursesWindow::mvcur(int sy, int ey, int sx, int ex) -{ - return ::mvcur(sy, ey, sx,ex); -} - -inline int CursesWindow::mvaddch(int y, int x, char ch) -{ - return (::wmove(w, y, x)==0) ? 0 : ::waddch(w, ch); -} - -inline int CursesWindow::mvgetch(int y, int x) -{ - return (::wmove(w, y, x)==0) ? 0 : ::wgetch(w); -} - -inline int CursesWindow::mvaddstr(int y, int x, char * str) -{ - return (::wmove(w, y, x)==0) ? 0 : ::waddstr(w, str); -} - -inline int CursesWindow::mvgetstr(int y, int x, char * str) -{ - return (::wmove(w, y, x)==0) ? 0 : ::wgetstr(w, str); -} - -inline int CursesWindow::mvinch(int y, int x) -{ - return (::wmove(w, y, x)==0) ? 0 : ::winch(w); -} - -inline int CursesWindow::mvdelch(int y, int x) -{ - return (::wmove(w, y, x)==0) ? 0 : ::wdelch(w); -} - -inline int CursesWindow::mvinsch(int y, int x, char ch) -{ - return (::wmove(w, y, x)==0) ? 0 : ::winsch(w, ch); -} - -inline int CursesWindow::refresh() -{ - return ::wrefresh(w); -} - -inline int CursesWindow::clearok(cbool bf) -{ - return ::clearok(w,bf); -} - -inline int CursesWindow::leaveok(cbool bf) -{ - return ::leaveok(w,bf); -} - -inline int CursesWindow::scrollok(cbool bf) -{ - return ::scrollok(w,bf); -} - -inline int CursesWindow::flushok(cbool bf) -{ - return ::flushok(w, bf); -} - -inline void CursesWindow::getyx(int& y, int& x) -{ - ::getyx(w, y, x); -} - -inline int CursesWindow::standout() -{ - return ::wstandout(w); -} - -inline int CursesWindow::standend() -{ - return ::wstandend(w); -} - -inline int CursesWindow::lines() -{ - return LINES; -} - -inline int CursesWindow::cols() -{ - return COLS; -} - -inline CursesWindow* CursesWindow::child() -{ - return subwins; -} - -inline CursesWindow* CursesWindow::parent() -{ - return par; -} - -inline CursesWindow* CursesWindow::sibling() -{ - return sib; -} - - -# endif -#endif diff --git a/gnu/lib/libg++/g++-include/DLDeque.ccP b/gnu/lib/libg++/g++-include/DLDeque.ccP new file mode 100644 index 0000000000..d5a0db7f91 --- /dev/null +++ b/gnu/lib/libg++/g++-include/DLDeque.ccP @@ -0,0 +1,4 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".DLDeque.h" diff --git a/gnu/lib/libg++/g++-include/DLDeque.hP b/gnu/lib/libg++/g++-include/DLDeque.hP new file mode 100644 index 0000000000..d91cdd41cb --- /dev/null +++ b/gnu/lib/libg++/g++-include/DLDeque.hP @@ -0,0 +1,130 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _DLDeque_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _DLDeque_h + +#include ".DLList.h" +#include ".Deque.h" + +class DLDeque : public Deque +{ + DLList p; + +public: + DLDeque(); + DLDeque(const DLDeque& d); + ~DLDeque(); + + void operator = (const DLDeque&); + + void push( item); // insert at front + void enq( item); // insert at rear + + & front(); + & rear(); + + deq(); + void del_front(); + void del_rear(); + + void clear(); + int empty(); + int full(); + int length(); + + int OK(); +}; + + +inline DLDeque::DLDeque() : p() {} +inline DLDeque::DLDeque(const DLDeque& d) : p(d.p) {} + +inline DLDeque::~DLDeque() {} + +inline void DLDeque::push(item) +{ + p.prepend(item); +} + +inline void DLDeque::enq(item) +{ + p.append(item); +} + +inline DLDeque::deq() +{ + return p.remove_front(); +} + +inline & DLDeque::front() +{ + return p.front(); +} + +inline & DLDeque::rear() +{ + return p.rear(); +} + +inline void DLDeque::del_front() +{ + p.del_front(); +} + +inline void DLDeque::del_rear() +{ + p.del_rear(); +} + +inline void DLDeque::operator =(const DLDeque& s) +{ + p.operator = (s.p); +} + + +inline int DLDeque::empty() +{ + return p.empty(); +} + +inline int DLDeque::full() +{ + return 0; +} + +inline int DLDeque::length() +{ + return p.length(); +} + +inline int DLDeque::OK() +{ + return p.OK(); +} + +inline void DLDeque::clear() +{ + p.clear(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/DLList.ccP b/gnu/lib/libg++/g++-include/DLList.ccP new file mode 100644 index 0000000000..cb1e22a337 --- /dev/null +++ b/gnu/lib/libg++/g++-include/DLList.ccP @@ -0,0 +1,339 @@ +// This may look like C code, but it is really -*- C++ -*- +// WARNING: This file is obsolete. Use ../DLList.cc, if you can. +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include +#include +#include ".DLList.h" + +// error handling + + + +void DLList::error(const char* msg) +{ + (*lib_error_handler)("DLList", msg); +} + +int DLList::length() +{ + int l = 0; + DLListNode* t = h; + if (t != 0) do { ++l; t = t->fd; } while (t != h); + return l; +} + +DLList::DLList(const DLList& a) +{ + if (a.h == 0) + h = 0; + else + { + DLListNode* p = a.h; + DLListNode* t = new DLListNode(p->hd); + h = t; + p = p->fd; + while (p != a.h) + { + DLListNode* n = new DLListNode(p->hd); + t->fd = n; + n->bk = t; + t = n; + p = p->fd; + } + t->fd = h; + h->bk = t; + return; + } +} + +DLList& DLList::operator = (const DLList& a) +{ + if (h != a.h) + { + clear(); + if (a.h != 0) + { + DLListNode* p = a.h; + DLListNode* t = new DLListNode(p->hd); + h = t; + p = p->fd; + while (p != a.h) + { + DLListNode* n = new DLListNode(p->hd); + t->fd = n; + n->bk = t; + t = n; + p = p->fd; + } + t->fd = h; + h->bk = t; + } + } + return *this; +} + +void DLList::clear() +{ + if (h == 0) + return; + + DLListNode* p = h->fd; + h->fd = 0; + h = 0; + + while (p != 0) + { + DLListNode* nxt = p->fd; + delete(p); + p = nxt; + } +} + + +Pix DLList::prepend( item) +{ + DLListNode* t = new DLListNode(item); + if (h == 0) + t->fd = t->bk = h = t; + else + { + t->fd = h; + t->bk = h->bk; + h->bk->fd = t; + h->bk = t; + h = t; + } + return Pix(t); +} + +Pix DLList::append( item) +{ + DLListNode* t = new DLListNode(item); + if (h == 0) + t->fd = t->bk = h = t; + else + { + t->bk = h->bk; + t->bk->fd = t; + t->fd = h; + h->bk = t; + } + return Pix(t); +} + +Pix DLList::ins_after(Pix p, item) +{ + if (p == 0) return prepend(item); + DLListNode* u = (DLListNode*) p; + DLListNode* t = new DLListNode(item, u, u->fd); + u->fd->bk = t; + u->fd = t; + return Pix(t); +} + +Pix DLList::ins_before(Pix p, item) +{ + if (p == 0) error("null Pix"); + DLListNode* u = (DLListNode*) p; + DLListNode* t = new DLListNode(item, u->bk, u); + u->bk->fd = t; + u->bk = t; + if (u == h) h = t; + return Pix(t); +} + +void DLList::join(DLList& b) +{ + DLListNode* t = b.h; + b.h = 0; + if (h == 0) + h = t; + else if (t != 0) + { + DLListNode* l = t->bk; + h->bk->fd = t; + t->bk = h->bk; + h->bk = l; + l->fd = h; + } +} + +int DLList::owns(Pix p) +{ + DLListNode* t = h; + if (t != 0 && p != 0) + { + do + { + if (Pix(t) == p) return 1; + t = t->fd; + } while (t != h); + } + return 0; +} + +void DLList::del(Pix& p, int dir) +{ + if (p == 0) error("null Pix"); + DLListNode* t = (DLListNode*) p; + if (t->fd == t) + { + h = 0; + p = 0; + } + else + { + if (dir < 0) + { + if (t == h) + p = 0; + else + p = Pix(t->bk); + } + else + { + if (t == h->bk) + p = 0; + else + p = Pix(t->fd); + } + t->bk->fd = t->fd; + t->fd->bk = t->bk; + if (t == h) h = t->fd; + } + delete t; +} + +void DLList::del_after(Pix& p) +{ + if (p == 0) + { + del_front(); + return; + } + + DLListNode* b = (DLListNode*) p; + DLListNode* t = b->fd; + + if (b == t) + { + h = 0; + p = 0; + } + else + { + t->bk->fd = t->fd; + t->fd->bk = t->bk; + if (t == h) h = t->fd; + } + delete t; +} + + DLList::remove_front() +{ + if (h == 0) + error("remove_front of empty list"); + DLListNode* t = h; + res = t->hd; + if (h->fd == h) + h = 0; + else + { + h->fd->bk = h->bk; + h->bk->fd = h->fd; + h = h->fd; + } + delete t; + return res; +} + + +void DLList::del_front() +{ + if (h == 0) + error("del_front of empty list"); + DLListNode* t = h; + if (h->fd == h) + h = 0; + else + { + h->fd->bk = h->bk; + h->bk->fd = h->fd; + h = h->fd; + } + delete t; +} + + DLList::remove_rear() +{ + if (h == 0) + error("remove_rear of empty list"); + DLListNode* t = h->bk; + res = t->hd; + if (h->fd == h) + h = 0; + else + { + t->fd->bk = t->bk; + t->bk->fd = t->fd; + } + delete t; + return res; +} + + +void DLList::del_rear() +{ + if (h == 0) + error("del_rear of empty list"); + DLListNode* t = h->bk; + if (h->fd == h) + h = 0; + else + { + t->fd->bk = t->bk; + t->bk->fd = t->fd; + } + delete t; +} + + +int DLList::OK() +{ + int v = 1; + if (h != 0) + { + DLListNode* t = h; + long count = MAXLONG; // Lots of chances to find h! + do + { + count--; + v &= t->bk->fd == t; + v &= t->fd->bk == t; + t = t->fd; + } while (v && count > 0 && t != h); + v &= count > 0; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/DLList.hP b/gnu/lib/libg++/g++-include/DLList.hP new file mode 100644 index 0000000000..b115ab9f6f --- /dev/null +++ b/gnu/lib/libg++/g++-include/DLList.hP @@ -0,0 +1,157 @@ +// This may look like C code, but it is really -*- C++ -*- +// WARNING: This file is obsolete. Use ../DLList.h, if you can. +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _DLList_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _DLList_h 1 + +#include +#include ".defs.h" + +#ifndef _DLListNode_h +#define _DLListNode_h 1 + +struct DLListNode +{ + DLListNode* bk; + DLListNode* fd; + hd; + DLListNode(); + DLListNode(const h, + DLListNode* p = 0, + DLListNode* n = 0); + ~DLListNode(); +}; + +inline DLListNode::DLListNode() {} + +inline DLListNode::DLListNode(const h, DLListNode* p, + DLListNode* n) + :hd(h), bk(p), fd(n) {} + +inline DLListNode::~DLListNode() {} + +typedef DLListNode* DLListNodePtr; + +#endif + +class DLList +{ + friend class DLListTrav; + + DLListNode* h; + +public: + DLList(); + DLList(const DLList& a); + ~DLList(); + + DLList& operator = (const DLList& a); + + int empty(); + int length(); + + void clear(); + + Pix prepend( item); + Pix append( item); + void join(DLList&); + + & front(); + remove_front(); + void del_front(); + + & rear(); + remove_rear(); + void del_rear(); + + & operator () (Pix p); + Pix first(); + Pix last(); + void next(Pix& p); + void prev(Pix& p); + int owns(Pix p); + Pix ins_after(Pix p, item); + Pix ins_before(Pix p, item); + void del(Pix& p, int dir = 1); + void del_after(Pix& p); + + void error(const char* msg); + int OK(); +}; + + +inline DLList::~DLList() +{ + clear(); +} + +inline DLList::DLList() +{ + h = 0; +} + +inline int DLList::empty() +{ + return h == 0; +} + + +inline void DLList::next(Pix& p) +{ + p = (p == 0 || p == h->bk)? 0 : Pix(((DLListNode*)p)->fd); +} + +inline void DLList::prev(Pix& p) +{ + p = (p == 0 || p == h)? 0 : Pix(((DLListNode*)p)->bk); +} + +inline Pix DLList::first() +{ + return Pix(h); +} + +inline Pix DLList::last() +{ + return (h == 0)? 0 : Pix(h->bk); +} + +inline & DLList::operator () (Pix p) +{ + if (p == 0) error("null Pix"); + return ((DLListNode*)p)->hd; +} + +inline & DLList::front() +{ + if (h == 0) error("front: empty list"); + return h->hd; +} + +inline & DLList::rear() +{ + if (h == 0) error("rear: empty list"); + return h->bk->hd; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/Deque.ccP b/gnu/lib/libg++/g++-include/Deque.ccP new file mode 100644 index 0000000000..79a9b72c87 --- /dev/null +++ b/gnu/lib/libg++/g++-include/Deque.ccP @@ -0,0 +1,11 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".Deque.h" + +Deque::~Deque() {} + +void Deque::error(const char* msg) +{ + (*lib_error_handler)("Deque", msg); +} diff --git a/gnu/lib/libg++/g++-include/Deque.hP b/gnu/lib/libg++/g++-include/Deque.hP new file mode 100644 index 0000000000..7ec52d4a0c --- /dev/null +++ b/gnu/lib/libg++/g++-include/Deque.hP @@ -0,0 +1,57 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _Deque_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Deque_h + +#include + +#include ".defs.h" + +class Deque +{ +public: + Deque() { } + virtual ~Deque(); + + virtual void push( item) = 0; // insert at front + virtual void enq( item) = 0; // insert at rear + + virtual & front() = 0; + virtual & rear() = 0; + + virtual deq() = 0; + virtual void del_front() = 0; + virtual void del_rear() = 0; + + virtual int empty() = 0; + virtual int full() = 0; + virtual int length() = 0; + virtual void clear() = 0; + + virtual int OK() = 0; + + void error(const char*); +}; + +#endif diff --git a/gnu/lib/libg++/g++-include/DiscUnif.h b/gnu/lib/libg++/g++-include/DiscUnif.h deleted file mode 100644 index d99a7d47f9..0000000000 --- a/gnu/lib/libg++/g++-include/DiscUnif.h +++ /dev/null @@ -1,79 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ -#ifndef _DiscreteUniform_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _DiscreteUniform_h 1 - -#include - -// -// The interval [lo..hi) -// - -class DiscreteUniform: public Random { - long pLow; - long pHigh; - double delta; -public: - DiscreteUniform(long low, long high, RNG *gen); - - long low(); - long low(long x); - long high(); - long high(long x); - - virtual double operator()(); -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline DiscreteUniform::DiscreteUniform(long low, long high, RNG *gen) : (gen) -{ - pLow = (low < high) ? low : high; - pHigh = (low < high) ? high : low; - delta = (pHigh - pLow) + 1; -} - -inline long DiscreteUniform::low() { return pLow; } - -inline long DiscreteUniform::low(long x) { - long tmp = pLow; - pLow = x; - delta = (pHigh - pLow) + 1; - return tmp; -} - -inline long DiscreteUniform::high() { return pHigh; } - -inline long DiscreteUniform::high(long x) { - long tmp = pHigh; - pHigh = x; - delta = (pHigh - pLow) + 1; - return tmp; -} - -#endif -#endif diff --git a/gnu/lib/libg++/g++-include/Erlang.h b/gnu/lib/libg++/g++-include/Erlang.h deleted file mode 100644 index e770e5a592..0000000000 --- a/gnu/lib/libg++/g++-include/Erlang.h +++ /dev/null @@ -1,77 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ -#ifndef _Erlang_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Erlang_h 1 - -#include - -class Erlang: public Random { -protected: - double pMean; - double pVariance; - int k; - double a; - void setState(); -public: - Erlang(double mean, double variance, RNG *gen); - - double mean(); - double mean(double x); - double variance(); - double variance(double x); - - virtual double operator()(); - -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline void Erlang::setState() { - k = int( (pMean * pMean ) / pVariance + 0.5 ); - k = (k > 0) ? k : 1; - a = k / pMean; -} - -inline Erlang::Erlang(double mean, double variance, RNG *gen) : (gen) -{ - pMean = mean; pVariance = variance; - setState(); -} - -inline double Erlang::mean() { return pMean; } -inline double Erlang::mean(double x) { - double tmp = pMean; pMean = x; setState(); return tmp; -}; - -inline double Erlang::variance() { return pVariance; } -inline double Erlang::variance(double x) { - double tmp = pVariance; pVariance = x; setState(); return tmp; -} - - -#endif -#endif diff --git a/gnu/lib/libg++/g++-include/FPQueue.ccP b/gnu/lib/libg++/g++-include/FPQueue.ccP new file mode 100644 index 0000000000..a358cacb60 --- /dev/null +++ b/gnu/lib/libg++/g++-include/FPQueue.ccP @@ -0,0 +1,4 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".FPQueue.h" diff --git a/gnu/lib/libg++/g++-include/FPQueue.hP b/gnu/lib/libg++/g++-include/FPQueue.hP new file mode 100644 index 0000000000..f647ae9dfd --- /dev/null +++ b/gnu/lib/libg++/g++-include/FPQueue.hP @@ -0,0 +1,112 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _FPQueue_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _FPQueue_h + +#include ".FPlex.h" +#include ".Queue.h" + +class FPQueue : public Queue +{ + FPlex p; + +public: + FPQueue(int chunksize = DEFAULT_INITIAL_CAPACITY); + FPQueue(const FPQueue& q); + ~FPQueue(); + + void operator = (const FPQueue&); + + void enq( item); + deq(); + & front(); + void del_front(); + + void clear(); + int empty(); + int full(); + int length(); + + int OK(); +}; + +inline FPQueue::FPQueue(int chunksize) : p(chunksize) {} +inline FPQueue::FPQueue(const FPQueue& q) : p(q.p) {} + +inline FPQueue::~FPQueue() {} + +inline void FPQueue::enq(item) +{ + p.add_high(item); +} + +inline FPQueue::deq() +{ + res = p.low_element(); + p.del_low(); + return res; +} + +inline & FPQueue::front() +{ + return p.low_element(); +} + + +inline void FPQueue::del_front() +{ + p.del_low(); +} + +inline void FPQueue::operator =(const FPQueue& s) +{ + p = s.p; +} + +inline int FPQueue::empty() +{ + return p.empty(); +} + +inline int FPQueue::full() +{ + return p.full(); +} + +inline int FPQueue::length() +{ + return p.length(); +} + +inline int FPQueue::OK() +{ + return p.OK(); +} + +inline void FPQueue::clear() +{ + p.clear(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/FPStack.ccP b/gnu/lib/libg++/g++-include/FPStack.ccP new file mode 100644 index 0000000000..954991193b --- /dev/null +++ b/gnu/lib/libg++/g++-include/FPStack.ccP @@ -0,0 +1,4 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".FPStack.h" diff --git a/gnu/lib/libg++/g++-include/FPStack.hP b/gnu/lib/libg++/g++-include/FPStack.hP new file mode 100644 index 0000000000..091f255360 --- /dev/null +++ b/gnu/lib/libg++/g++-include/FPStack.hP @@ -0,0 +1,114 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _FPStack_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _FPStack_h + +#include ".FPlex.h" +#include ".Stack.h" + +class FPStack : public Stack +{ + FPlex p; + +public: + FPStack(int chunksize = DEFAULT_INITIAL_CAPACITY); + FPStack(const FPStack& s); + ~FPStack(); + + void operator = (const FPStack&); + + void push( item); + pop(); + & top(); + void del_top(); + + int empty(); + int full(); + int length(); + + void clear(); + + int OK(); + +}; + + +inline FPStack::FPStack(int chunksize) : p(chunksize) {} +inline FPStack::FPStack(const FPStack& s) : p(s.p) {} + +inline FPStack::~FPStack() {} + +inline void FPStack::push(item) +{ + p.add_high(item); +} + +inline FPStack::pop() +{ + res = p.high_element(); + p.del_high(); + return res; +} + +inline & FPStack::top() +{ + return p.high_element(); +} + +inline void FPStack::del_top() +{ + p.del_high(); +} + +inline void FPStack::operator =(const FPStack& s) +{ + p = s.p; +} + +inline int FPStack::empty() +{ + return p.empty(); +} + +inline int FPStack::full() +{ + return p.full(); +} + +inline int FPStack::length() +{ + return p.length(); +} + +inline int FPStack::OK() +{ + return p.OK(); +} + +inline void FPStack::clear() +{ + p.clear(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/FPlex.ccP b/gnu/lib/libg++/g++-include/FPlex.ccP new file mode 100644 index 0000000000..70d6c47557 --- /dev/null +++ b/gnu/lib/libg++/g++-include/FPlex.ccP @@ -0,0 +1,167 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".FPlex.h" + + +FPlex:: FPlex() +{ + lo = fnc = 0; + csize = DEFAULT_INITIAL_CAPACITY; + * data = new [csize]; + hd = new IChunk(data, lo, lo, fnc, csize); +} + +FPlex:: FPlex(int maxsize) +{ + if (maxsize == 0) error("invalid constructor specification"); + lo = fnc = 0; + if (maxsize > 0) + { + csize = maxsize; + * data = new [csize]; + hd = new IChunk(data, lo, lo, fnc, csize); + } + else + { + csize = -maxsize; + * data = new [csize]; + hd = new IChunk(data, maxsize, lo, fnc, fnc); + } +} + + +FPlex:: FPlex(int l, int maxsize) +{ + if (maxsize == 0) error("invalid constructor specification"); + lo = fnc = l; + if (maxsize > 0) + { + csize = maxsize; + * data = new [csize]; + hd = new IChunk(data, lo, lo, fnc, csize+lo); + } + else + { + csize = -maxsize; + * data = new [csize]; + hd = new IChunk(data, maxsize+lo, lo, fnc, fnc); + } +} + +FPlex:: FPlex(int l, int hi, const initval, int maxsize) +{ + lo = l; + fnc = hi + 1; + if (maxsize >= 0) + { + csize = maxsize; + if (csize < fnc - lo) + csize = fnc - lo; + * data = new [csize]; + hd = new IChunk(data, lo, lo, fnc, csize); + } + else + { + csize = -maxsize; + if (csize < fnc - lo) + csize = fnc - lo; + * data = new [csize]; + hd = new IChunk(data, -csize, lo, fnc, fnc); + } + fill(initval); +} + +FPlex::FPlex(const FPlex& a) +{ + lo = a.lo; + fnc = a.fnc; + csize = fnc - lo; + if (csize < a.csize) csize = a.csize; + * data = new [csize]; + hd = new IChunk(data, lo, lo, fnc, lo+csize); + for (int i = a.low(); i < a.fence(); a.next(i)) (*this)[i] = a[i]; +} + +void FPlex::operator= (const FPlex& a) +{ + if (&a != this) + { + del_chunk(hd); + lo = a.lo; + fnc = a.fnc; + csize = fnc - lo; + if (csize < a.csize) csize = a.csize; + * data = new [csize]; + hd = new IChunk(data, lo, lo, fnc, lo+csize); + for (int i = a.low(); i < a.fence(); a.next(i)) (*this)[i] = a[i]; + } +} + + +void FPlex::reverse() +{ + tmp; + int l = lo; + int h = fnc - 1; + while (l < h) + { + tmp = (*this)[l]; + (*this)[l] = (*this)[h]; + (*this)[h] = tmp; + next(l); + prev(h); + } +} + +void FPlex::fill(const x) +{ + for (int i = lo; i < fnc; ++i) (*this)[i] = x; +} + +void FPlex::fill(const x, int lo, int hi) +{ + for (int i = lo; i <= hi; ++i) (*this)[i] = x; +} + +void FPlex::clear() +{ + if (fnc != lo) + { + hd->clear(lo); + fnc = lo; + } +} + +int FPlex::OK () const +{ + int v = hd != 0; // hd exists + v &= hd->IChunk::OK(); // and is OK + v &= fnc - lo <= hd->size(); // and has enough space + v &= lo <= fnc; // plex indices consistent + v &= lo == hd->low_index(); // and match those + v &= fnc == hd->fence_index(); // of chunk + v &= one_chunk(); // and only one chunk + if (!v) error("invariant failure"); + return v; +} + diff --git a/gnu/lib/libg++/g++-include/FPlex.hP b/gnu/lib/libg++/g++-include/FPlex.hP new file mode 100644 index 0000000000..eb93a0c372 --- /dev/null +++ b/gnu/lib/libg++/g++-include/FPlex.hP @@ -0,0 +1,253 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _FPlex_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _FPlex_h 1 + +#include ".Plex.h" + +class FPlex : public Plex +{ +public: + FPlex(); // set low = 0; + // fence = 0; + // csize = default + + FPlex(int maxsize); // low = 0; + // fence = 0; + // csize = maxsize + + FPlex(int lo, // low = lo; + int maxsize); // fence=lo + // csize = maxsize + + FPlex(int lo, // low = lo + int hi, // fence = hi+1 + const initval,// fill with initval, + int maxsize = 0); // csize = maxsize + // or fence - lo if 0 + + FPlex(const FPlex&); // X(X&) + + ~FPlex(); + + void operator= (const FPlex&); + +// virtuals + + & high_element (); + & low_element (); + + const & high_element () const; + const & low_element () const; + + Pix first() const; + Pix last() const; + void prev(Pix& ptr) const; + void next(Pix& ptr) const; + int owns(Pix p) const; + & operator () (Pix p); + const & operator () (Pix p) const; + + int low() const; + int high() const; + int valid(int idx) const; + void prev(int& idx) const; + void next(int& x) const; + & operator [] (int index); + const & operator [] (int index) const; + + int Pix_to_index(Pix p) const; + Pix index_to_Pix(int idx) const; + + int can_add_high() const; + int can_add_low() const; + int full() const; + + int add_high(const elem); + int del_high (); + int add_low (const elem); + int del_low (); + + void fill(const x); + void fill(const x, int from, int to); + void clear(); + void reverse(); + + int OK () const; +}; + + +inline int FPlex::valid (int idx) const +{ + return idx >= lo && idx < fnc; +} + +inline int FPlex::low() const +{ + return lo; +} + +inline int FPlex::high() const +{ + return fnc - 1; +} + +inline Pix FPlex::first() const +{ + return (Pix)(hd->IChunk::first_pointer()); +} + +inline void FPlex::prev(Pix& p) const +{ + p = Pix(hd->IChunk::pred((*) p)); +} + +inline void FPlex::next(Pix& p) const +{ + p = Pix(hd->IChunk::succ((*) p)); +} + +inline Pix FPlex::last() const +{ + return Pix(hd->IChunk::last_pointer()); +} + +inline int FPlex::full () const +{ + return fnc - lo == csize; +} + +inline void FPlex::prev(int& idx) const +{ + --idx; +} + +inline void FPlex::next(int& idx) const +{ + ++idx; +} + +inline & FPlex:: operator [] (int idx) +{ + if (idx < lo || idx >= fnc) index_error(); + return *(hd->pointer_to(idx)); +} + +inline & FPlex:: operator () (Pix p) +{ + return *((*)p); +} + +inline & FPlex::low_element () +{ + if (empty()) index_error(); + return *(hd->pointer_to(lo)); +} + +inline & FPlex::high_element () +{ + if (empty()) index_error(); + return *(hd->pointer_to(fnc - 1)); +} + +inline const & FPlex:: operator [] (int idx) const +{ + if (idx < lo || idx >= fnc) index_error(); + return *(hd->pointer_to(idx)); +} + +inline const & FPlex:: operator () (Pix p) const +{ + return *((const *)p); +} + +inline const & FPlex::low_element () const +{ + if (empty()) index_error(); + return *(hd->pointer_to(lo)); +} + +inline const & FPlex::high_element () const +{ + if (empty()) index_error(); + return *(hd->pointer_to(fnc - 1)); +} + +inline int FPlex::can_add_high() const +{ + return hd->can_grow_high(); +} + +inline int FPlex::can_add_low() const +{ + return hd->can_grow_low(); +} + +inline int FPlex::add_high(const elem) +{ + if (!can_add_high()) full_error(); + *((hd->IChunk::grow_high())) = elem; + return fnc++; +} + +inline int FPlex::del_high () +{ + if (empty()) empty_error(); + hd->IChunk::shrink_high(); + return --fnc - 1; +} + +inline int FPlex::add_low (const elem) +{ + if (!can_add_low()) full_error(); + *((hd->IChunk::grow_low())) = elem; + return --lo; +} + +inline int FPlex::del_low () +{ + if (empty()) empty_error(); + hd->IChunk::shrink_low(); + return ++lo; +} + +inline int FPlex::owns (Pix p) const +{ + return hd->actual_pointer((*)p); +} + +inline int FPlex::Pix_to_index(Pix p) const +{ + if (!hd->actual_pointer((const *)p)) index_error(); + return hd->index_of((const *)p); +} + +inline Pix FPlex::index_to_Pix(int idx) const +{ + if (idx < lo || idx >= fnc) index_error(); + return Pix(hd->pointer_to(idx)); +} + +inline FPlex::~FPlex() {} + +#endif diff --git a/gnu/lib/libg++/g++-include/File.h b/gnu/lib/libg++/g++-include/File.h deleted file mode 100644 index ce9dbef8bf..0000000000 --- a/gnu/lib/libg++/g++-include/File.h +++ /dev/null @@ -1,302 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifndef _File_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _File_h 1 - -#include -#include -#include - -#include - -class Filebuf; - -class File -{ - friend class Filebuf; -protected: - FILE* fp; // _iobuf file pointer - char* nm; // file name (dynamically allocated) - char rw; // 1 = read; 2 = write; 3 = readwrite - // bit 2 (4) means read/write into string - state_value state; // _good/_eof/_fail/_bad - long stat; // last read/write/... return value - - void initialize(); - void reinitialize(const char*); - char *readline (int chunk_number, char terminator); - -public: - File(); - File(const char* filename, io_mode m, access_mode a); - File(const char* filename, const char* m); - File(int filedesc, io_mode m); - File(FILE* fileptr); - File(int sz, char* buf, io_mode m); - - ~File(); - -// binding, rebinding, unbinding to physical files - - File& open(const char* filename, io_mode m, access_mode a); - File& open(const char* filename, const char* m); - File& open(int filedesc, io_mode m); - File& open(FILE* fileptr); - - File& close(); - File& remove(); - -// class variable access - - int filedesc(); - const char* name(); - void setname(const char* newname); - int iocount(); - - int rdstate(); - int eof(); - int fail(); - int bad(); - int good(); - -// other status queries - - int readable(); - int writable(); - int is_open(); - - operator void*(); - -// error handling - - void error(); - void clear(state_value f = _good); // poorly named - void set(state_value f); // set corresponding but - void unset(state_value f); // clear corresponding bit - File& failif(int cond); - void check_state(); - -// character IO - - File& get(char& c); - File& put(char c); - File& unget(char c); - File& putback(char c); // a synonym for unget - -// char* IO - - File& put(const char* s); - File& get (char* s, int n, char terminator = '\n'); - File& getline(char* s, int n, char terminator = '\n'); - File& gets (char **s, char terminator = '\n'); - -// binary IO - - File& read(void* x, int sz, int n); - File& write(void* x, int sz, int n); - -// formatted IO - - File& form(const char* ...); - File& scan(const char* ...); - -// buffer IO - - File& flush(); - -// position control - - File& seek(long pos, int seek_mode=0); // default seek mode=absolute - long tell(); - -// buffer control - - File& setbuf(int buffer_kind); // legal vals: _IONBF, _IOFBF, _IOLBF - File& setbuf(int size, char* buf); - File& raw(); -}; - - -// error handlers - -extern void verbose_File_error_handler(const char*); -extern void quiet_File_error_handler(const char*); -extern void fatal_File_error_handler(const char*); -extern one_arg_error_handler_t File_error_handler; -extern one_arg_error_handler_t set_File_error_handler(one_arg_error_handler_t); - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - - - -inline int File::filedesc() -{ - return fileno(fp); -} - -inline const char* File::name() -{ - return nm; -} - -inline int File::iocount() -{ - return stat; -} - -inline void File::clear(state_value flag) -{ - state = flag; -} - -inline void File::set(state_value flag) -{ - state = state_value(int(state) | int(flag)); -} - -inline void File::unset(state_value flag) -{ - state = state_value(int(state) & ~int(flag)); -} - -inline int File::readable() -{ - if (fp != 0) { if (feof(fp)) set(_eof); if (ferror(fp)) set(_bad);} - return (state == _good && (rw & 01)); -} - -inline int File::writable() -{ - if (fp != 0 && ferror(fp)) set(_bad); - return ((int(state) & (int(_fail)|int(_bad))) == 0 && (rw & 02)); -} - -inline int File::is_open() -{ - return (fp != 0); -} - - -inline File& File::raw() -{ - return this->File::setbuf(_IONBF); -} - - -inline File& File::failif(int cond) -{ - if (cond) set(_fail); return *this; -} - -inline File& File::get(char& c) -{ - if (readable()) - { - int ch = getc(fp); - c = ch; - failif (ch == EOF); - } - return *this; -} - -inline File& File::put(char c) -{ - return failif (!writable() || putc(c, fp) == EOF); -} - -inline File& File::unget(char c) -{ - return failif(!is_open() || !(rw & 01) || ungetc(c, fp) == EOF); -} - -inline File& File::putback(char c) -{ - return failif (!is_open() || !(rw & 01) || ungetc(c, fp) == EOF); -} - -inline File& File::read(void* x, int sz, int n) -{ - return failif (!readable() || (stat = fread(x, sz, n, fp)) != n); -} - -inline File& File::write(void* x, int sz, int n) -{ - return failif (!writable() || (stat = fwrite(x, sz, n, fp)) != n); -} - -inline File& File::flush() -{ - return failif(!is_open() || fflush(fp) == EOF); -} - - -inline File& File::seek(long pos, int seek_mode) -{ - return failif (!is_open() || fseek(fp, pos, seek_mode) < 0); -} - -inline long File::tell() -{ - failif (!is_open() || ((stat = ftell(fp)) < 0)); - return stat; -} - -inline int File::rdstate() -{ - check_state(); return state; // check_state is necessary in rare but -} // possible circumstances - -inline File::operator void*() -{ - check_state(); return (int(state) & (int(_bad)|int(_fail)))? 0 : this ; -} - -inline int File::eof() -{ - check_state(); return state & _eof; -} - -inline int File::fail() -{ - check_state(); return state & _fail; -} - -inline int File::bad() -{ - check_state(); return state & _bad; -} - -inline int File::good() -{ - check_state(); return rdstate() == _good; -} - - -#endif - -#endif diff --git a/gnu/lib/libg++/g++-include/Filebuf.h b/gnu/lib/libg++/g++-include/Filebuf.h deleted file mode 100644 index 2124e21c13..0000000000 --- a/gnu/lib/libg++/g++-include/Filebuf.h +++ /dev/null @@ -1,32 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifndef _Filebuf_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Filebuf_h 1 - - -#endif diff --git a/gnu/lib/libg++/g++-include/Fix.h b/gnu/lib/libg++/g++-include/Fix.h deleted file mode 100644 index d325228ce5..0000000000 --- a/gnu/lib/libg++/g++-include/Fix.h +++ /dev/null @@ -1,468 +0,0 @@ -// -// Fix.h : variable length fixed point data type -// - -#ifndef _Fix_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Fix_h 1 - -#include -#include -#include -#include - -typedef unsigned short uint16; -typedef short int16; -typedef unsigned long uint32; -typedef long int32; - -#define _Fix_min_length 1 -#define _Fix_max_length 65535 - -#define _Fix_min_value -1.0 -#define _Fix_max_value 1.0 - -extern uint16 Fix_default_length; -extern int Fix_default_print_width; - -struct _Frep // internal Fix representation -{ - uint16 len; // length in bits - uint16 siz; // allocated storage - int16 ref; // reference count - uint16 s[1]; // start of ushort array represention -}; - -typedef struct _Frep* _Fix; - -extern _Frep _Frep_0; -extern _Frep _Frep_m1; -extern _Frep _Frep_quotient_bump; - -class Fix -{ - _Fix rep; - - Fix(_Fix); - - void unique(); - -public: - Fix(); - Fix(Fix&); - Fix(double&); - Fix(int); - Fix(int, Fix&); - Fix(int, double&); - Fix(int, _Frep*); - - ~Fix(); - - Fix operator = (Fix&); - Fix operator = (double&); - - friend int operator == (Fix&, Fix& ); - friend int operator != (Fix&, Fix&); - - friend int operator < (Fix&, Fix&); - friend int operator <= (Fix&, Fix&); - friend int operator > (Fix&, Fix&); - friend int operator >= (Fix&, Fix&); - - Fix& operator + (); - Fix operator - (); - - friend Fix operator + (Fix&, Fix&); - friend Fix operator - (Fix&, Fix&); - friend Fix operator * (Fix&, Fix&); - friend Fix operator / (Fix&, Fix&); - - friend Fix operator * (Fix&, int); - friend Fix operator * (int, Fix&); - friend Fix operator % (Fix&, int); - friend Fix operator << (Fix&, int); - friend Fix operator >> (Fix&, int); - -#ifdef __GNUG__ - friend Fix operator ? (Fix&, Fix&); // max -#endif - - Fix operator += (Fix&); - Fix operator -= (Fix&); - Fix operator *= (Fix&); - Fix operator /= (Fix&); - - Fix operator *= (int); - Fix operator %= (int); - Fix operator <<=(int); - Fix operator >>=(int); - - friend char* Ftoa(Fix&, int width = Fix_default_print_width); - friend Fix atoF(const char*, int len = Fix_default_length); - - friend istream& operator >> (istream&, Fix&); - friend ostream& operator << (ostream&, Fix&); - - // built-in functions - friend Fix abs(Fix); // absolute value - friend int sgn(Fix&); // -1, 0, +1 - friend Integer mantissa(Fix&); // integer representation - friend double value(Fix&); // double value - friend int length(Fix&); // field length - friend void show(Fix&); // show contents - - // error handlers - void error(const char* msg); // error handler - void range_error(const char* msg); // range error handler - - // internal class functions - friend void mask(_Fix); - friend int compare(_Fix, _Fix = &_Frep_0); - - friend _Fix new_Fix(uint16); - friend _Fix new_Fix(uint16, _Fix); - friend _Fix new_Fix(uint16, double); - - friend _Fix copy(_Fix, _Fix); - friend _Fix negate(_Fix, _Fix = NULL); - friend _Fix add(_Fix, _Fix, _Fix = NULL); - friend _Fix subtract(_Fix, _Fix, _Fix = NULL); - friend _Fix multiply(_Fix, _Fix, _Fix = NULL); - friend _Fix multiply(_Fix, int, _Fix = NULL); - friend _Fix divide(_Fix, _Fix, _Fix = NULL, _Fix = NULL); - friend _Fix shift(_Fix, int, _Fix = NULL); - - // non-operator versions for user - friend void negate(Fix& x, Fix& r); - friend void add(Fix& x, Fix& y, Fix& r); - friend void subtract(Fix& x, Fix& y, Fix& r); - friend void multiply(Fix& x, Fix& y, Fix& r); - friend void divide(Fix& x, Fix& y, Fix& q, Fix& r); - friend void shift(Fix& x, int y, Fix& r); -}; - -// error handlers - -extern void - default_Fix_error_handler(const char*), - default_Fix_range_error_handler(const char*); - -extern one_arg_error_handler_t - Fix_error_handler, - Fix_range_error_handler; - -extern one_arg_error_handler_t - set_Fix_error_handler(one_arg_error_handler_t f), - set_Fix_range_error_handler(one_arg_error_handler_t f); - -typedef void (*Fix_peh)(_Fix&); -extern Fix_peh Fix_overflow_handler; - -extern void - Fix_overflow_saturate(_Fix&), - Fix_overflow_wrap(_Fix&), - Fix_overflow_warning_saturate(_Fix&), - Fix_overflow_warning(_Fix&), - Fix_overflow_error(_Fix&); - -extern Fix_peh set_overflow_handler(Fix_peh); - -extern int Fix_set_default_length(int); - -// function definitions - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline void Fix::unique() -{ - if ( rep->ref > 1 ) - { - rep->ref--; - rep = new_Fix(rep->len,rep); - } -} - -inline void mask (_Fix x) -{ - int n = x->len & 0x0f; - if ( n ) - x->s[x->siz - 1] &= 0xffff0000 >> n; -} - -inline _Fix copy(_Fix from, _Fix to) -{ - uint16 *ts = to->s, *fs = from->s; - int ilim = to->siz < from->siz ? to->siz : from->siz; - for ( int i=0; i < ilim; i++ ) - *ts++ = *fs++; - for ( ; i < to->siz; i++ ) - *ts++ = 0; - mask(to); - return to; -} - -inline Fix::Fix(_Fix f) -{ - rep = f; -} - -inline Fix::Fix() -{ - rep = new_Fix(Fix_default_length); -} - -inline Fix::Fix(int len) -{ - if ( len < _Fix_min_length || len > _Fix_max_length ) - error("illegal length in declaration"); - rep = new_Fix((uint16 )len); -} - -inline Fix::Fix(double& d) -{ - rep = new_Fix(Fix_default_length,d); -} - -inline Fix::Fix(Fix& y) -{ - rep = y.rep; rep->ref++; -} - -inline Fix::Fix(int len, Fix& y) -{ - if ( len < _Fix_min_length || len > _Fix_max_length ) - error("illegal length in declaration"); - rep = new_Fix((uint16 )len,y.rep); -} - -inline Fix::Fix(int len, _Frep* fr) -{ - if ( len < 1 || len > 65535 ) - error("illegal length in declaration"); - rep = new_Fix((uint16 )len,fr); -} - -inline Fix::Fix(int len, double& d) -{ - if ( len < _Fix_min_length || len > _Fix_max_length ) - error("illegal length in declaration"); - rep = new_Fix((uint16 )len,d); -} - -inline Fix::~Fix() -{ - if ( --rep->ref <= 0 ) delete rep; -} - -inline Fix Fix::operator = (Fix& y) -{ - if ( rep->len == y.rep->len ) { - ++y.rep->ref; - if ( --rep->ref <= 0 ) delete rep; - rep = y.rep; - } - else { - unique(); - copy(y.rep,rep); - } - return *this; -} - -inline Fix Fix::operator = (double& d) -{ - int oldlen = rep->len; - if ( --rep->ref <= 0 ) delete rep; - rep = new_Fix(oldlen,d); - return *this; -} - -inline int operator == (Fix& x, Fix& y) -{ - return compare(x.rep, y.rep) == 0; -} - -inline int operator != (Fix& x, Fix& y) -{ - return compare(x.rep, y.rep) != 0; -} - -inline int operator < (Fix& x, Fix& y) -{ - return compare(x.rep, y.rep) < 0; -} - -inline int operator <= (Fix& x, Fix& y) -{ - return compare(x.rep, y.rep) <= 0; -} - -inline int operator > (Fix& x, Fix& y) -{ - return compare(x.rep, y.rep) > 0; -} - -inline int operator >= (Fix& x, Fix& y) -{ - return compare(x.rep, y.rep) >= 0; -} - -inline Fix& Fix::operator + () -{ - return *this; -} - -inline Fix Fix::operator - () -{ - _Fix r = negate(rep); return r; -} - -inline Fix operator + (Fix& x, Fix& y) -{ - _Fix r = add(x.rep, y.rep); return r; -} - -inline Fix operator - (Fix& x, Fix& y) -{ - _Fix r = subtract(x.rep, y.rep); return r; -} - -inline Fix operator * (Fix& x, Fix& y) -{ - _Fix r = multiply(x.rep, y.rep); return r; -} - -inline Fix operator * (Fix& x, int y) -{ - _Fix r = multiply(x.rep, y); return r; -} - -inline Fix operator * (int y, Fix& x) -{ - _Fix r = multiply(x.rep, y); return r; -} - -inline Fix operator / (Fix& x, Fix& y) -{ - _Fix r = divide(x.rep, y.rep); return r; -} - -inline Fix Fix::operator += (Fix& y) -{ - unique(); add(rep, y.rep, rep); return *this; -} - -inline Fix Fix::operator -= (Fix& y) -{ - unique(); subtract(rep, y.rep, rep); return *this; -} - -inline Fix Fix::operator *= (Fix& y) -{ - unique(); multiply(rep, y.rep, rep); return *this; -} - -inline Fix Fix::operator *= (int y) -{ - unique(); multiply(rep, y, rep); return *this; -} - -inline Fix Fix::operator /= (Fix& y) -{ - unique(); divide(rep, y.rep, rep); return *this; -} - -inline Fix operator % (Fix& x, int y) -{ - Fix r((int )x.rep->len + y, x); return r; -} - -inline Fix operator << (Fix& x, int y) -{ - _Fix rep = shift(x.rep, y); return rep; -} - -inline Fix operator >> (Fix& x, int y) -{ - _Fix rep = shift(x.rep, -y); return rep; -} - -inline Fix Fix::operator <<= (int y) -{ - unique(); shift(rep, y, rep); return *this; -} - -inline Fix Fix::operator >>= (int y) -{ - unique(); shift(rep, -y, rep); return *this; -} - -#ifdef __GNUG__ -inline Fix operator ? (Fix& x, Fix& y) -{ - if ( compare(x.rep, y.rep) >= 0 ) return x; else return y; -} -#endif - -inline Fix abs(Fix x) -{ - _Fix r = (compare(x.rep) >= 0 ? new_Fix(x.rep->len,x.rep) : negate(x.rep)); - return r; -} - -inline int sgn(Fix& x) -{ - int a = compare(x.rep); - return a == 0 ? 0 : (a > 0 ? 1 : -1); -} - -inline int length(Fix& x) -{ - return x.rep->len; -} - -inline ostream& operator << (ostream& s, Fix& y) -{ - return s << Ftoa(y); -} - -inline void negate (Fix& x, Fix& r) -{ - negate(x.rep, r.rep); -} - -inline void add (Fix& x, Fix& y, Fix& r) -{ - add(x.rep, y.rep, r.rep); -} - -inline void subtract (Fix& x, Fix& y, Fix& r) -{ - subtract(x.rep, y.rep, r.rep); -} - -inline void multiply (Fix& x, Fix& y, Fix& r) -{ - multiply(x.rep, y.rep, r.rep); -} - -inline void divide (Fix& x, Fix& y, Fix& q, Fix& r) -{ - divide(x.rep, y.rep, q.rep, r.rep); -} - -inline void shift (Fix& x, int y, Fix& r) -{ - shift(x.rep, y, r.rep); -} - -#endif -#endif diff --git a/gnu/lib/libg++/g++-include/Fix16.h b/gnu/lib/libg++/g++-include/Fix16.h deleted file mode 100644 index 0024c9da11..0000000000 --- a/gnu/lib/libg++/g++-include/Fix16.h +++ /dev/null @@ -1,647 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Kurt Baudendistel (gt-eedsp!baud@gatech.edu) - adapted for libg++ by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifndef _Fix16_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Fix16_h 1 - -#include -#include - -// constant definitions - -#define Fix16_fs ((double)((unsigned)(1 << 15))) - -#define Fix16_msb (1 << 15) -#define Fix16_m_max ((1 << 15) - 1) -#define Fix16_m_min ((short)(1 << 15)) - -#define Fix16_mult Fix16_fs -#define Fix16_div (1./Fix16_fs) -#define Fix16_max (1. - .5/Fix16_fs) -#define Fix16_min (-1.) - - -#define Fix32_fs ((double)((unsigned long)(1 << 31))) - -#define Fix32_msb ((unsigned long)(1 << 31)) -#define Fix32_m_max ((1 << 31) - 1) -#define Fix32_m_min ((long)(1 << 31)) - -#define Fix32_mult Fix32_fs -#define Fix32_div (1./Fix32_fs) -#define Fix32_max (1. - .5/Fix32_fs) -#define Fix32_min (-1.) - - -// -// Fix16 class: 16-bit Fixed point data type -// -// consists of a 16-bit mantissa (sign bit & 15 data bits). -// - -class Fix16 -{ - friend class Fix32; - - short m; - - short round(double d); - short assign(double d); - Fix16(short i); - Fix16(int i); - - operator double(); - - -public: - Fix16(); - Fix16(Fix16& f); - Fix16(double d); - Fix16(Fix32& f); - - ~Fix16(); - - Fix16& operator=(Fix16& f); - Fix16& operator=(double d); - Fix16& operator=(Fix32& f); - - friend short& mantissa(Fix16& f); - friend double value(Fix16& f); - - Fix16 operator + (); - Fix16 operator - (); - - friend Fix16 operator + (Fix16& f, Fix16& g); - friend Fix16 operator - (Fix16& f, Fix16& g); - friend Fix32 operator * (Fix16& f, Fix16& g); - friend Fix16 operator / (Fix16& f, Fix16& g); - friend Fix16 operator << (Fix16& f, int b); - friend Fix16 operator >> (Fix16& f, int b); - - Fix16& operator += (Fix16& f); - Fix16& operator -= (Fix16& f); - Fix16& operator *= (Fix16& ); - Fix16& operator /= (Fix16& f); - - Fix16& operator <<=(int b); - Fix16& operator >>=(int b); - - friend int operator == (Fix16& f, Fix16& g); - friend int operator != (Fix16& f, Fix16& g); - friend int operator >= (Fix16& f, Fix16& g); - friend int operator <= (Fix16& f, Fix16& g); - friend int operator > (Fix16& f, Fix16& g); - friend int operator < (Fix16& f, Fix16& g); - - friend istream& operator >> (istream& s, Fix16& f); - friend ostream& operator << (ostream& s, Fix16& f); - - void overflow(short&); - void range_error(short&); - - friend Fix16 operator * (Fix16& f, int g); - friend Fix16 operator * (int g, Fix16& f); - Fix16& operator *= (int g); -}; - - -// -// Fix32 class: 32-bit Fixed point data type -// -// consists of a 32-bit mantissa (sign bit & 31 data bits). -// - -class Fix32 -{ - friend class Fix16; - - long m; - - long round(double d); - long assign(double d); - - Fix32(long i); - operator double(); - - -public: - Fix32(); - Fix32(Fix32& f); - Fix32(Fix16& f); - Fix32(double d); - ~Fix32(); - - Fix32& operator = (Fix32& f); - Fix32& operator = (Fix16& f); - Fix32& operator = (double d); - - friend long& mantissa(Fix32& f); - friend double value(Fix32& f); - - Fix32 operator + (); - Fix32 operator - (); - - friend Fix32 operator + (Fix32& f, Fix32& g); - friend Fix32 operator - (Fix32& f, Fix32& g); - friend Fix32 operator * (Fix32& f, Fix32& g); - friend Fix32 operator / (Fix32& f, Fix32& g); - friend Fix32 operator << (Fix32& f, int b); - friend Fix32 operator >> (Fix32& f, int b); - - friend Fix32 operator * (Fix16& f, Fix16& g); - - Fix32& operator += (Fix32& f); - Fix32& operator -= (Fix32& f); - Fix32& operator *= (Fix32& f); - Fix32& operator /= (Fix32& f); - Fix32& operator <<=(int b); - Fix32& operator >>=(int b); - - friend int operator == (Fix32& f, Fix32& g); - friend int operator != (Fix32& f, Fix32& g); - friend int operator >= (Fix32& f, Fix32& g); - friend int operator <= (Fix32& f, Fix32& g); - friend int operator > (Fix32& f, Fix32& g); - friend int operator < (Fix32& f, Fix32& g); - - friend istream& operator >> (istream& s, Fix32& f); - friend ostream& operator << (ostream& s, Fix32& f); - - void overflow(long& i); - void range_error(long& i); - - friend Fix32 operator * (Fix32& f, int g); - friend Fix32 operator * (int g, Fix32& f); - Fix32& operator *= (int g); -}; - -// active error handler declarations - -typedef void (*Fix16_peh)(short&); -typedef void (*Fix32_peh)(long&); - -extern Fix16_peh Fix16_overflow_handler; -extern Fix32_peh Fix32_overflow_handler; - -extern Fix16_peh Fix16_range_error_handler; -extern Fix32_peh Fix32_range_error_handler; - -#if defined(SHORT_NAMES) || defined(VMS) -#define set_overflow_handler sohndl -#define set_range_error_handler srnghdl -#endif - - -// error handler declarations - -extern Fix16_peh set_Fix16_overflow_handler(Fix16_peh); -extern Fix32_peh set_Fix32_overflow_handler(Fix32_peh); -extern void set_overflow_handler(Fix16_peh, Fix32_peh); - -extern Fix16_peh set_Fix16_range_error_handler(Fix16_peh); -extern Fix32_peh set_Fix32_range_error_handler(Fix32_peh); -extern void set_range_error_handler(Fix16_peh, Fix32_peh); - -extern void - Fix16_ignore(short&), - Fix16_overflow_saturate(short&), - Fix16_overflow_warning_saturate(short&), - Fix16_warning(short&), - Fix16_abort(short&); - -extern void - Fix32_ignore(long&), - Fix32_overflow_saturate(long&), - Fix32_overflow_warning_saturate(long&), - Fix32_warning(long&), - Fix32_abort(long&); - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline Fix16::~Fix16() {} - -inline short Fix16::round(double d) -{ - return short( (d >= 0)? d + 0.5 : d - 0.5); -} - -inline Fix16::Fix16(short i) -{ - m = i; -} - -inline Fix16::Fix16(int i) -{ - m = i; -} - -inline Fix16::operator double() -{ - return Fix16_div * m; -} - -inline Fix16::Fix16() -{ - m = 0; -} - -inline Fix16::Fix16(Fix16& f) -{ - m = f.m; -} - -inline Fix16::Fix16(double d) -{ - m = assign(d); -} - - -inline Fix16& Fix16::operator=(Fix16& f) -{ - m = f.m; - return *this; -} - -inline Fix16& Fix16::operator=(double d) -{ - m = assign(d); - return *this; -} - - -inline Fix32::Fix32() -{ - m = 0; -} - -inline Fix32::Fix32(long i) -{ - m = i; -} - -inline Fix32:: operator double() -{ - return Fix32_div * m; -} - - -inline Fix32::Fix32(Fix32& f) -{ - m = f.m; -} - -inline Fix32::Fix32(Fix16& f) -{ - m = long(f.m) << 16; -} - -inline Fix32::Fix32(double d) -{ - m = assign(d); -} - -inline Fix16::Fix16(Fix32& f) -{ - m = f.m >> 16; -} - - -inline Fix16& Fix16::operator=(Fix32& f) -{ - m = f.m >> 16; - return *this; -} - -inline Fix32& Fix32::operator=(Fix32& f) -{ - m = f.m; - return *this; -} - -inline Fix32& Fix32::operator=(Fix16& f) -{ - m = long(f.m) << 16; - return *this; -} - -inline Fix32& Fix32::operator=(double d) -{ - m = assign(d); - return *this; -} - -inline short& mantissa(Fix16& f) -{ - return f.m; -} - -inline double value(Fix16& f) -{ - return double(f); -} - -inline Fix16 Fix16::operator+() -{ - return m; -} - -inline Fix16 Fix16::operator-() -{ - return -m; -} - -inline Fix16 operator+(Fix16& f, Fix16& g) -{ - short sum = f.m + g.m; - if ( (f.m ^ sum) & (g.m ^ sum) & Fix16_msb ) - f.overflow(sum); - return sum; -} - -inline Fix16 operator-(Fix16& f, Fix16& g) -{ - short sum = f.m - g.m; - if ( (f.m ^ sum) & (-g.m ^ sum) & Fix16_msb ) - f.overflow(sum); - return sum; -} - -inline Fix32 operator*(Fix16& f, Fix16& g) -{ - return Fix32( long( long(f.m) * long(g.m) << 1)); -} - -inline Fix16 operator<<(Fix16& a, int b) -{ - return a.m << b; -} - -inline Fix16 operator>>(Fix16& a, int b) -{ - return a.m >> b; -} - -inline Fix16& Fix16:: operator+=(Fix16& f) -{ - return *this = *this + f; -} - -inline Fix16& Fix16:: operator-=(Fix16& f) -{ - return *this = *this - f; -} - -inline Fix16& Fix16::operator*=(Fix16& f) -{ - return *this = *this * f; -} - -inline Fix16& Fix16:: operator/=(Fix16& f) -{ - return *this = *this / f; -} - -inline Fix16& Fix16:: operator<<=(int b) -{ - return *this = *this << b; -} - -inline Fix16& Fix16:: operator>>=(int b) -{ - return *this = *this >> b; -} - -inline int operator==(Fix16& f, Fix16& g) -{ - return f.m == g.m; -} - -inline int operator!=(Fix16& f, Fix16& g) -{ - return f.m != g.m; -} - -inline int operator>=(Fix16& f, Fix16& g) -{ - return f.m >= g.m; -} - -inline int operator<=(Fix16& f, Fix16& g) -{ - return f.m <= g.m; -} - -inline int operator>(Fix16& f, Fix16& g) -{ - return f.m > g.m; -} - -inline int operator<(Fix16& f, Fix16& g) -{ - return f.m < g.m; -} - -inline istream& operator>>(istream& s, Fix16& f) -{ - double d; - s >> d; - f = d; - return s; -} - -inline ostream& operator<<(ostream& s, Fix16& f) -{ - return s << double(f); -} - - -inline Fix16 operator*(Fix16& f, int g) -{ - return Fix16(short(f.m * g)); -} - -inline Fix16 operator*(int g, Fix16& f) -{ - return f * g; -} - - -inline Fix16& Fix16::operator*=(int g) -{ - return *this = *this * g; -} - -inline Fix32::~Fix32() {} - -inline long Fix32::round(double d) -{ - return long( (d >= 0)? d + 0.5 : d - 0.5); -} - - -inline long& mantissa(Fix32& f) -{ - return f.m; -} - -inline double value(Fix32& f) -{ - return double(f); -} - -inline Fix32 Fix32::operator+() -{ - return m; -} - -inline Fix32 Fix32::operator-() -{ - return -m; -} - -inline Fix32 operator+(Fix32& f, Fix32& g) -{ - long sum = f.m + g.m; - if ( (f.m ^ sum) & (g.m ^ sum) & Fix32_msb ) - f.overflow(sum); - return sum; -} - -inline Fix32 operator-(Fix32& f, Fix32& g) -{ - long sum = f.m - g.m; - if ( (f.m ^ sum) & (-g.m ^ sum) & Fix32_msb ) - f.overflow(sum); - return sum; -} - -inline Fix32 operator<<(Fix32& a, int b) -{ - return a.m << b; -} - -inline Fix32 operator>>(Fix32& a, int b) -{ - return a.m >> b; -} - -inline Fix32& Fix32::operator+=(Fix32& f) -{ - return *this = *this + f; -} - -inline Fix32& Fix32::operator-=(Fix32& f) -{ - return *this = *this - f; -} - -inline Fix32& Fix32::operator*=(Fix32& f) -{ - return *this = *this * f; -} - -inline Fix32& Fix32::operator/=(Fix32& f) -{ - return *this = *this / f; -} - - -inline Fix32& Fix32::operator<<=(int b) -{ - return *this = *this << b; -} - -inline Fix32& Fix32::operator>>=(int b) -{ - return *this = *this >> b; -} - -inline int operator==(Fix32& f, Fix32& g) -{ - return f.m == g.m; -} - -inline int operator!=(Fix32& f, Fix32& g) -{ - return f.m != g.m; -} - -inline int operator>=(Fix32& f, Fix32& g) -{ - return f.m >= g.m; -} - -inline int operator<=(Fix32& f, Fix32& g) -{ - return f.m <= g.m; -} - -inline int operator>(Fix32& f, Fix32& g) -{ - return f.m > g.m; -} - -inline int operator<(Fix32& f, Fix32& g) -{ - return f.m < g.m; -} - -inline istream& operator>>(istream& s, Fix32& f) -{ - double d; - s >> d; - f = d; - return s; -} - -inline ostream& operator<<(ostream& s, Fix32& f) -{ - return s << double(f); -} - -inline Fix32 operator*(Fix32& f, int g) -{ - return Fix32(long(f.m * g)); -} - -inline Fix32 operator*(int g, Fix32& f) -{ - return f * g; -} - - - -inline Fix32& Fix32::operator*=(int g) -{ - return *this = *this * g; -} - - -#endif -#endif - diff --git a/gnu/lib/libg++/g++-include/Fix24.h b/gnu/lib/libg++/g++-include/Fix24.h deleted file mode 100644 index ec48afcae6..0000000000 --- a/gnu/lib/libg++/g++-include/Fix24.h +++ /dev/null @@ -1,595 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Kurt Baudendistel (gt-eedsp!baud@gatech.edu) - adapted for libg++ by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifndef _Fix24_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Fix24_h 1 - -#include -#include - -// extra type definitions - -typedef struct { - long u; - unsigned long l; -} twolongs; - -// constant definitions - -static const int - Fix24_shift = 31; - -static const double - Fix24_fs = 2147483648., // 2^Fix24_shift - Fix24_mult = Fix24_fs, - Fix24_div = 1./Fix24_fs, - Fix24_max = 1. - .5/Fix24_fs, - Fix24_min = -1.; - -static const unsigned long - Fix24_msb = 0x80000000L, - Fix24_lsb = 0x00000100L, - Fix24_m_max = 0x7fffff00L, - Fix24_m_min = 0x80000000L; - -static const double - Fix48_fs = 36028797018963968., // 2^(24+Fix24_shift) - Fix48_max = 1. - .5/Fix48_fs, - Fix48_min = -1., - Fix48_div_u = 1./Fix24_fs, - Fix48_div_l = 1./Fix48_fs; - -static const twolongs - Fix48_msb = { 0x80000000L, 0L }, - Fix48_lsb = { 0L, 0x00000100L }, - Fix48_m_max = { 0x7fffff00L, 0xffffff00L }, - Fix48_m_min = { 0x80000000L, 0L }; - -// -// Fix24 class: 24-bit Fixed point data type -// -// consists of a 24-bit mantissa (sign bit & 23 data bits). -// - -class Fix24 -{ - friend class Fix48; - - long m; - - long assign(double d); - operator double(); - Fix24(long i); - Fix24(int i); - - -public: - Fix24(); - Fix24(Fix24& f); - Fix24(double d); - Fix24(Fix48& f); - - ~Fix24(); - - Fix24& operator=(Fix24& f); - Fix24& operator=(double d); - Fix24& operator=(Fix48& f); - - friend long& mantissa(Fix24& f); - friend double value(Fix24& f); - - Fix24 operator + (); - Fix24 operator - (); - - friend Fix24 operator + (Fix24& f, Fix24& g); - friend Fix24 operator - (Fix24& f, Fix24& g); - friend Fix48 operator * (Fix24& f, Fix24& g); - friend Fix24 operator * (Fix24& f, int g); - friend Fix24 operator * (int g, Fix24& f); - friend Fix24 operator / (Fix24& f, Fix24& g); - friend Fix24 operator << (Fix24& f, int b); - friend Fix24 operator >> (Fix24& f, int b); - - Fix24& operator += (Fix24& f); - Fix24& operator -= (Fix24& f); - Fix24& operator *= (Fix24& f); - Fix24& operator *= (int b); - Fix24& operator /= (Fix24& f); - - Fix24& operator <<=(int b); - Fix24& operator >>=(int b); - - friend int operator == (Fix24& f, Fix24& g); - friend int operator != (Fix24& f, Fix24& g); - friend int operator >= (Fix24& f, Fix24& g); - friend int operator <= (Fix24& f, Fix24& g); - friend int operator > (Fix24& f, Fix24& g); - friend int operator < (Fix24& f, Fix24& g); - - friend istream& operator >> (istream& s, Fix24& f); - friend ostream& operator << (ostream& s, Fix24& f); - - void overflow(long&); - void range_error(long&); -}; - - -// -// Fix48 class: 48-bit Fixed point data type -// -// consists of a 48-bit mantissa (sign bit & 47 data bits). -// - -class Fix48 -{ - friend class Fix24; - - twolongs m; - - twolongs assign(double d); - operator double(); - Fix48(twolongs i); - -public: - Fix48(); - Fix48(Fix48& f); - Fix48(Fix24& f); - Fix48(double d); - ~Fix48(); - - Fix48& operator = (Fix48& f); - Fix48& operator = (Fix24& f); - Fix48& operator = (double d); - - friend twolongs& mantissa(Fix48& f); - friend double value(Fix48& f); - - Fix48 operator + (); - Fix48 operator - (); - - friend Fix48 operator + (Fix48& f, Fix48& g); - friend Fix48 operator - (Fix48& f, Fix48& g); - friend Fix48 operator * (Fix48& f, int g); - friend Fix48 operator * (int g, Fix48& f); - friend Fix48 operator << (Fix48& f, int b); - friend Fix48 operator >> (Fix48& f, int b); - - friend Fix48 operator * (Fix24& f, Fix24& g); - - Fix48& operator += (Fix48& f); - Fix48& operator -= (Fix48& f); - Fix48& operator *= (int b); - Fix48& operator <<=(int b); - Fix48& operator >>=(int b); - - friend int operator == (Fix48& f, Fix48& g); - friend int operator != (Fix48& f, Fix48& g); - friend int operator >= (Fix48& f, Fix48& g); - friend int operator <= (Fix48& f, Fix48& g); - friend int operator > (Fix48& f, Fix48& g); - friend int operator < (Fix48& f, Fix48& g); - - friend istream& operator >> (istream& s, Fix48& f); - friend ostream& operator << (ostream& s, Fix48& f); - - void overflow(twolongs& i); - void range_error(twolongs& i); -}; - - -// active error handler declarations - -typedef void (*Fix24_peh)(long&); -typedef void (*Fix48_peh)(twolongs&); - -extern Fix24_peh Fix24_overflow_handler; -extern Fix48_peh Fix48_overflow_handler; - -extern Fix24_peh Fix24_range_error_handler; -extern Fix48_peh Fix48_range_error_handler; - - -// error handler declarations - -#if defined(SHORT_NAMES) || defined(VMS) -#define set_overflow_handler sohndl -#define set_range_error_handler srnghdl -#endif - -extern Fix24_peh set_Fix24_overflow_handler(Fix24_peh); -extern Fix48_peh set_Fix48_overflow_handler(Fix48_peh); -extern void set_overflow_handler(Fix24_peh, Fix48_peh); - -extern Fix24_peh set_Fix24_range_error_handler(Fix24_peh); -extern Fix48_peh set_Fix48_range_error_handler(Fix48_peh); -extern void set_range_error_handler(Fix24_peh, Fix48_peh); - -extern void - Fix24_ignore(long&), - Fix24_overflow_saturate(long&), - Fix24_overflow_warning_saturate(long&), - Fix24_warning(long&), - Fix24_abort(long&); - -extern void - Fix48_ignore(twolongs&), - Fix48_overflow_saturate(twolongs&), - Fix48_overflow_warning_saturate(twolongs&), - Fix48_warning(twolongs&), - Fix48_abort(twolongs&); - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline Fix24::~Fix24() {} - -inline Fix24::Fix24(long i) -{ - m = i; -} - -inline Fix24::Fix24(int i) -{ - m = i; -} - -inline Fix24::operator double() -{ - return Fix24_div * m; -} - -inline Fix24::Fix24() -{ - m = 0; -} - -inline Fix24::Fix24(Fix24& f) -{ - m = f.m; -} - -inline Fix24::Fix24(double d) -{ - m = assign(d); -} - -inline Fix24::Fix24(Fix48& f) -{ - m = f.m.u; -} - -inline Fix24& Fix24::operator=(Fix24& f) -{ - m = f.m; - return *this; -} - -inline Fix24& Fix24::operator=(double d) -{ - m = assign(d); - return *this; -} - -inline Fix24& Fix24::operator=(Fix48& f) -{ - m = f.m.u; - return *this; -} - -inline long& mantissa(Fix24& f) -{ - return f.m; -} - -inline double value(Fix24& f) -{ - return double(f); -} - -inline Fix24 Fix24::operator+() -{ - return m; -} - -inline Fix24 Fix24::operator-() -{ - return -m; -} - -inline Fix24 operator+(Fix24& f, Fix24& g) -{ - long sum = f.m + g.m; - if ( (f.m ^ sum) & (g.m ^ sum) & Fix24_msb ) - f.overflow(sum); - return sum; -} - -inline Fix24 operator-(Fix24& f, Fix24& g) -{ - long sum = f.m - g.m; - if ( (f.m ^ sum) & (-g.m ^ sum) & Fix24_msb ) - f.overflow(sum); - return sum; -} - -inline Fix24 operator*(Fix24& a, int b) -{ - return a.m * b; -} - -inline Fix24 operator*(int b, Fix24& a) -{ - return a * b; -} - -inline Fix24 operator<<(Fix24& a, int b) -{ - return a.m << b; -} - -inline Fix24 operator>>(Fix24& a, int b) -{ - return (a.m >> b) & 0xffffff00L; -} - -inline Fix24& Fix24:: operator+=(Fix24& f) -{ - return *this = *this + f; -} - -inline Fix24& Fix24:: operator-=(Fix24& f) -{ - return *this = *this - f; -} - -inline Fix24& Fix24::operator*=(Fix24& f) -{ - return *this = *this * f; -} - -inline Fix24& Fix24:: operator/=(Fix24& f) -{ - return *this = *this / f; -} - -inline Fix24& Fix24:: operator<<=(int b) -{ - return *this = *this << b; -} - -inline Fix24& Fix24:: operator>>=(int b) -{ - return *this = *this >> b; -} - -inline Fix24& Fix24::operator*=(int b) -{ - return *this = *this * b; -} - -inline int operator==(Fix24& f, Fix24& g) -{ - return f.m == g.m; -} - -inline int operator!=(Fix24& f, Fix24& g) -{ - return f.m != g.m; -} - -inline int operator>=(Fix24& f, Fix24& g) -{ - return f.m >= g.m; -} - -inline int operator<=(Fix24& f, Fix24& g) -{ - return f.m <= g.m; -} - -inline int operator>(Fix24& f, Fix24& g) -{ - return f.m > g.m; -} - -inline int operator<(Fix24& f, Fix24& g) -{ - return f.m < g.m; -} - -inline istream& operator>>(istream& s, Fix24& f) -{ - double d; - s >> d; - f = d; - return s; -} - -inline ostream& operator<<(ostream& s, Fix24& f) -{ - return s << double(f); -} - -inline Fix48::~Fix48() {} - -inline Fix48::Fix48(twolongs i) -{ - m = i; -} - -inline Fix48:: operator double() -{ -/* - * Note: can't simply do Fix48_div_u * m.u + Fix48_div_l * m.l, because - * m.u is signed and m.l is unsigned. - */ - return (m.u >= 0)? Fix48_div_u * m.u + Fix48_div_l * m.l : - (Fix48_div_u * ((unsigned long)(m.u & 0xffffff00)) - + Fix48_div_l * m.l) - 2; -} - -inline Fix48::Fix48() -{ - m.u = 0; - m.l = 0; -} - -inline Fix48::Fix48(Fix48& f) -{ - m = f.m; -} - -inline Fix48::Fix48(Fix24& f) -{ - m.u = f.m; - m.l = 0; -} - -inline Fix48::Fix48(double d) -{ - m = assign(d); -} - -inline Fix48& Fix48::operator=(Fix48& f) -{ - m = f.m; - return *this; -} - -inline Fix48& Fix48::operator=(Fix24& f) -{ - m.u = f.m; - m.l = 0; - return *this; -} - -inline Fix48& Fix48::operator=(double d) -{ - m = assign(d); - return *this; -} - -inline twolongs& mantissa(Fix48& f) -{ - return f.m; -} - -inline double value(Fix48& f) -{ - return double(f); -} - -inline Fix48 Fix48::operator+() -{ - return m; -} - -inline Fix48 Fix48::operator-() -{ - twolongs n; - n.l = -m.l; - n.u = ~m.u + ((n.l ^ m.l) & Fix24_msb ? 0 : Fix24_lsb); - return Fix48(n); -} - -inline Fix48 operator*(int b, Fix48& a) -{ - return a * b; -} - -inline Fix48& Fix48::operator+=(Fix48& f) -{ - return *this = *this + f; -} - -inline Fix48& Fix48::operator-=(Fix48& f) -{ - return *this = *this - f; -} - -inline Fix48& Fix48::operator*=(int b) -{ - return *this = *this * b; -} - -inline Fix48& Fix48::operator<<=(int b) -{ - return *this = *this << b; -} - -inline Fix48& Fix48::operator>>=(int b) -{ - return *this = *this >> b; -} - -inline int operator==(Fix48& f, Fix48& g) -{ - return f.m.u == g.m.u && f.m.l == g.m.l; -} - -inline int operator!=(Fix48& f, Fix48& g) -{ - return f.m.u != g.m.u || f.m.l != g.m.l; -} - -inline int operator>=(Fix48& f, Fix48& g) -{ - return f.m.u >= g.m.u || (f.m.u == g.m.u && f.m.l >= g.m.l); -} - -inline int operator<=(Fix48& f, Fix48& g) -{ - return f.m.u <= g.m.u || (f.m.u == g.m.u && f.m.l <= g.m.l); -} - -inline int operator>(Fix48& f, Fix48& g) -{ - return f.m.u > g.m.u || (f.m.u == g.m.u && f.m.l > g.m.l); -} - -inline int operator<(Fix48& f, Fix48& g) -{ - return f.m.u < g.m.u || (f.m.u == g.m.u && f.m.l < g.m.l); -} - -inline istream& operator>>(istream& s, Fix48& f) -{ - double d; - s >> d; - f = d; - return s; -} - -inline ostream& operator<<(ostream& s, Fix48& f) -{ - return s << double(f); -} - - -#endif -#endif - diff --git a/gnu/lib/libg++/g++-include/Fmodes.h b/gnu/lib/libg++/g++-include/Fmodes.h deleted file mode 100644 index 524c2bdc4b..0000000000 --- a/gnu/lib/libg++/g++-include/Fmodes.h +++ /dev/null @@ -1,34 +0,0 @@ - -#ifndef _Fmodes_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Fmodes_h 1 - -enum io_mode // known unix file IO modes -{ - io_readonly = 0, - io_writeonly = 1, - io_readwrite = 2, - io_appendonly = 3, - io_append = 4, // append, plus allow reads -}; - -enum access_mode // ways to open a file -{ - a_createonly = 0, // create, fail if file exists - a_create = 1, // create if doesn't exist, else truncate - a_useonly = 2, // use (no truncate) fail if doesn't exist - a_use = 3, // use (no truncate), create if doesn't exist -}; - -enum state_value // File states -{ - _good = 0, // all is well - _eof = 1, // at eof - _fail = 2, // logical or physical IO error - _bad = 4 // unopened/corrupted -}; - -#endif diff --git a/gnu/lib/libg++/g++-include/Geom.h b/gnu/lib/libg++/g++-include/Geom.h deleted file mode 100644 index d70b96a668..0000000000 --- a/gnu/lib/libg++/g++-include/Geom.h +++ /dev/null @@ -1,59 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ -#ifndef _Geometric_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Geometric_h - -#include - -class Geometric: public Random { -protected: - double pMean; -public: - Geometric(double mean, RNG *gen); - - double mean(); - double mean(double x); - - virtual double operator()(); - -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline Geometric::Geometric(double mean, RNG *gen) : (gen) -{ - pMean = mean; -} - - -inline double Geometric::mean() { return pMean; } -inline double Geometric::mean(double x) { - double tmp = pMean; pMean = x; return tmp; -} - -#endif -#endif diff --git a/gnu/lib/libg++/g++-include/GetOpt.h b/gnu/lib/libg++/g++-include/GetOpt.h deleted file mode 100644 index 413c52d64c..0000000000 --- a/gnu/lib/libg++/g++-include/GetOpt.h +++ /dev/null @@ -1,131 +0,0 @@ -/* Getopt for GNU. - Copyright (C) 1987, 1989 Free Software Foundation, Inc. - (Modified by Douglas C. Schmidt for use with GNU G++.) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - - - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of `argv' so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. - - Setting the environment variable _POSIX_OPTION_ORDER disables permutation. - Then the behavior is completely standard. - - GNU application programs can use a third alternative mode in which - they can distinguish the relative order of options and other arguments. */ - -#ifndef GetOpt_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define GetOpt_h 1 - -#include -#include - -class GetOpt -{ -private: - /* The next char to be scanned in the option-element - in which the last option character we returned was found. - This allows us to pick up the scan where we left off. - - If this is zero, or a null string, it means resume the scan - by advancing to the next ARGV-element. */ - - static char *nextchar; - - - /* Describe how to deal with options that follow non-option ARGV-elements. - - UNSPECIFIED means the caller did not specify anything; - the default is then REQUIRE_ORDER if the environment variable - _OPTIONS_FIRST is defined, PERMUTE otherwise. - - REQUIRE_ORDER means don't recognize them as options. - Stop option processing when the first non-option is seen. - This is what Unix does. - - PERMUTE is the default. We permute the contents of `argv' as we scan, - so that eventually all the options are at the end. This allows options - to be given in any order, even with programs that were not written to - expect this. - - RETURN_IN_ORDER is an option available to programs that were written - to expect options and other ARGV-elements in any order and that care about - the ordering of the two. We describe each non-option ARGV-element - as if it were the argument of an option with character code zero. - Using `-' as the first character of the list of option characters - requests this mode of operation. - - The special argument `--' forces an end of option-scanning regardless - of the value of `ordering'. In the case of RETURN_IN_ORDER, only - `--' can cause `getopt' to return EOF with `optind' != ARGC. */ - - static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; - - /* Handle permutation of arguments. */ - - /* Describe the part of ARGV that contains non-options that have - been skipped. `first_nonopt' is the index in ARGV of the first of them; - `last_nonopt' is the index after the last of them. */ - - static int first_nonopt; - static int last_nonopt; - - void exchange (char **argv); -public: - /* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - - char *optarg; - - /* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns EOF, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - - int optind; - - /* Callers store zero here to inhibit the error message - for unrecognized options. */ - - int opterr; - - int nargc; - char **nargv; - char *noptstring; - - GetOpt (int argc, char **argv, char *optstring); - int operator () (void); -}; - -#endif diff --git a/gnu/lib/libg++/g++-include/HypGeom.h b/gnu/lib/libg++/g++-include/HypGeom.h deleted file mode 100644 index b120f104e9..0000000000 --- a/gnu/lib/libg++/g++-include/HypGeom.h +++ /dev/null @@ -1,78 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ -#ifndef _HyperGeometric_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _HyperGeometric_h - -#include - -class HyperGeometric: public Random { -protected: - double pMean; - double pVariance; - double pP; - void setState(); - -public: - HyperGeometric(double mean, double variance, RNG *gen); - - double mean(); - double mean(double x); - double variance(); - double variance(double x); - - virtual double operator()(); -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline void HyperGeometric::setState() { - double z = pVariance / (pMean * pMean); - pP = 0.5 * (1.0 - sqrt((z - 1.0) / ( z + 1.0 ))); -} - -inline HyperGeometric::HyperGeometric(double mean, double variance, RNG *gen) -: (gen) { - pMean = mean; pVariance = variance; - setState(); -} - -inline double HyperGeometric::mean() { return pMean; }; - -inline double HyperGeometric::mean(double x) { - double t = pMean; pMean = x; - setState(); return t; -} - -inline double HyperGeometric::variance() { return pVariance; } - -inline double HyperGeometric::variance(double x) { - double t = pVariance; pVariance = x; - setState(); return t; -} - -#endif -#endif diff --git a/gnu/lib/libg++/g++-include/Incremental.h b/gnu/lib/libg++/g++-include/Incremental.h deleted file mode 100644 index 9b6bb3f894..0000000000 --- a/gnu/lib/libg++/g++-include/Incremental.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef Incremental_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define Incremental_h -#define DECLARE_INIT_FUNCTION(USER_INIT_FUNCTION) \ -static void USER_INIT_FUNCTION (); extern void (*_initfn)(); \ -static struct xyzzy { xyzzy () {_initfn = USER_INIT_FUNCTION;}; \ -~xyzzy () {};} __2xyzzy; -#else -#error Incremental.h was not the first file included in this module -#endif diff --git a/gnu/lib/libg++/g++-include/Integer.h b/gnu/lib/libg++/g++-include/Integer.h deleted file mode 100644 index a241e65a29..0000000000 --- a/gnu/lib/libg++/g++-include/Integer.h +++ /dev/null @@ -1,1114 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- - -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifndef _Integer_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Integer_h 1 - -#include - -struct IntRep // internal Integer representations -{ - unsigned short len; // current length - unsigned short sz; // allocated space - short sgn; // 1 means >= 0; 0 means < 0 - unsigned short s[1]; // represented as ushort array starting here -}; - -extern IntRep* Ialloc(IntRep*, const unsigned short *, int, int, int); -extern IntRep* Icalloc(IntRep*, int); -extern IntRep* Icopy_long(IntRep*, long); -extern IntRep* Icopy(IntRep*, const IntRep*); -extern IntRep* Iresize(IntRep*, int); -extern IntRep* add(const IntRep*, int, const IntRep*, int, IntRep*); -extern IntRep* add(const IntRep*, int, long, IntRep*); -extern IntRep* multiply(const IntRep*, const IntRep*, IntRep*); -extern IntRep* multiply(const IntRep*, long, IntRep*); -extern IntRep* lshift(const IntRep*, long, IntRep*); -extern IntRep* lshift(const IntRep*, const IntRep*, int, IntRep*); -extern IntRep* bitop(const IntRep*, const IntRep*, IntRep*, char); -extern IntRep* bitop(const IntRep*, long, IntRep*, char); -extern IntRep* power(const IntRep*, long, IntRep*); -extern IntRep* div(const IntRep*, const IntRep*, IntRep*); -extern IntRep* mod(const IntRep*, const IntRep*, IntRep*); -extern IntRep* div(const IntRep*, long, IntRep*); -extern IntRep* mod(const IntRep*, long, IntRep*); -extern IntRep* compl(const IntRep*, IntRep*); -extern IntRep* abs(const IntRep*, IntRep*); -extern IntRep* negate(const IntRep*, IntRep*); -extern IntRep* pow(const IntRep*, long); -extern IntRep* gcd(const IntRep*, const IntRep* y); -extern int compare(const IntRep*, const IntRep*); -extern int compare(const IntRep*, long); -extern int ucompare(const IntRep*, const IntRep*); -extern int ucompare(const IntRep*, long); -extern char* Itoa(const IntRep* x, int base = 10, int width = 0); -extern IntRep* atoIntRep(const char* s, int base = 10); -extern long Itolong(const IntRep*); -extern double Itodouble(const IntRep*); -extern int Iislong(const IntRep*); -extern int Iisdouble(const IntRep*); -extern long lg(const IntRep*); - - -class Integer -{ -protected: - IntRep* rep; -public: - Integer(); - Integer(long); - Integer(const Integer&); - - ~Integer(); - - void operator = (const Integer&); - void operator = (long); - -// unary operations to self - - void operator ++ (); - void operator -- (); - void negate(); // negate in-place - void abs(); // absolute-value in-place - void complement(); // bitwise complement in-place - -// assignment-based operations - - void operator += (const Integer&); - void operator -= (const Integer&); - void operator *= (const Integer&); - void operator /= (const Integer&); - void operator %= (const Integer&); - void operator <<=(const Integer&); - void operator >>=(const Integer&); - void operator &= (const Integer&); - void operator |= (const Integer&); - void operator ^= (const Integer&); - - void operator += (long); - void operator -= (long); - void operator *= (long); - void operator /= (long); - void operator %= (long); - void operator <<=(long); - void operator >>=(long); - void operator &= (long); - void operator |= (long); - void operator ^= (long); - -// (constructive binary operations are inlined below) - -#ifdef __GNUG__ - friend Integer operator ? (const Integer& x, const Integer& y); // max -#endif - -// builtin Integer functions that must be friends - - friend long lg (const Integer&); // floor log base 2 of abs(x) - friend double ratio(const Integer& x, const Integer& y); - // return x/y as a double - - friend Integer gcd(const Integer&, const Integer&); - friend int even(const Integer&); // true if even - friend int odd(const Integer&); // true if odd - friend int sign(const Integer&); // returns -1, 0, +1 - - friend void setbit(Integer& x, long b); // set b'th bit of x - friend void clearbit(Integer& x, long b); // clear b'th bit - friend int testbit(const Integer& x, long b); // return b'th bit - -// procedural versions of operators - - friend void abs(const Integer& x, Integer& dest); - friend void negate(const Integer& x, Integer& dest); - friend void complement(const Integer& x, Integer& dest); - - friend int compare(const Integer&, const Integer&); - friend int ucompare(const Integer&, const Integer&); - friend void add(const Integer& x, const Integer& y, Integer& dest); - friend void sub(const Integer& x, const Integer& y, Integer& dest); - friend void mul(const Integer& x, const Integer& y, Integer& dest); - friend void div(const Integer& x, const Integer& y, Integer& dest); - friend void mod(const Integer& x, const Integer& y, Integer& dest); - friend void divide(const Integer& x, const Integer& y, - Integer& q, Integer& r); - friend void and(const Integer& x, const Integer& y, Integer& dest); - friend void or(const Integer& x, const Integer& y, Integer& dest); - friend void xor(const Integer& x, const Integer& y, Integer& dest); - friend void lshift(const Integer& x, const Integer& y, Integer& dest); - friend void rshift(const Integer& x, const Integer& y, Integer& dest); - friend void pow(const Integer& x, const Integer& y, Integer& dest); - - friend int compare(const Integer&, long); - friend int ucompare(const Integer&, long); - friend void add(const Integer& x, long y, Integer& dest); - friend void sub(const Integer& x, long y, Integer& dest); - friend void mul(const Integer& x, long y, Integer& dest); - friend void div(const Integer& x, long y, Integer& dest); - friend void mod(const Integer& x, long y, Integer& dest); - friend void divide(const Integer& x, long y, Integer& q, long& r); - friend void and(const Integer& x, long y, Integer& dest); - friend void or(const Integer& x, long y, Integer& dest); - friend void xor(const Integer& x, long y, Integer& dest); - friend void lshift(const Integer& x, long y, Integer& dest); - friend void rshift(const Integer& x, long y, Integer& dest); - friend void pow(const Integer& x, long y, Integer& dest); - - friend int compare(long, const Integer&); - friend int ucompare(long, const Integer&); - friend void add(long x, const Integer& y, Integer& dest); - friend void sub(long x, const Integer& y, Integer& dest); - friend void mul(long x, const Integer& y, Integer& dest); - friend void and(long x, const Integer& y, Integer& dest); - friend void or(long x, const Integer& y, Integer& dest); - friend void xor(long x, const Integer& y, Integer& dest); - -// coercion & conversion - - int fits_in_long() const; - int fits_in_double() const; - - operator long() const; - operator double() const; - - friend char* Itoa(const Integer& x, int base = 10, int width = 0); - friend Integer atoI(const char* s, int base = 10); - - friend istream& operator >> (istream& s, Integer& y); - friend ostream& operator << (ostream& s, const Integer& y); - -// error detection - - int initialized() const; - volatile void error(const char* msg) const; - int OK() const; -}; - - -// (These are declared inline) - - int operator == (const Integer&, const Integer&); - int operator == (const Integer&, long); - int operator != (const Integer&, const Integer&); - int operator != (const Integer&, long); - int operator < (const Integer&, const Integer&); - int operator < (const Integer&, long); - int operator <= (const Integer&, const Integer&); - int operator <= (const Integer&, long); - int operator > (const Integer&, const Integer&); - int operator > (const Integer&, long); - int operator >= (const Integer&, const Integer&); - int operator >= (const Integer&, long); - Integer operator - (const Integer&); - Integer operator ~ (const Integer&); - Integer operator + (const Integer&, const Integer&); - Integer operator + (const Integer&, long); - Integer operator + (long, const Integer&); - Integer operator - (const Integer&, const Integer&); - Integer operator - (const Integer&, long); - Integer operator - (long, const Integer&); - Integer operator * (const Integer&, const Integer&); - Integer operator * (const Integer&, long); - Integer operator * (long, const Integer&); - Integer operator / (const Integer&, const Integer&); - Integer operator / (const Integer&, long); - Integer operator % (const Integer&, const Integer&); - Integer operator % (const Integer&, long); - Integer operator << (const Integer&, const Integer&); - Integer operator << (const Integer&, long); - Integer operator >> (const Integer&, const Integer&); - Integer operator >> (const Integer&, long); - Integer operator & (const Integer&, const Integer&); - Integer operator & (const Integer&, long); - Integer operator & (long, const Integer&); - Integer operator | (const Integer&, const Integer&); - Integer operator | (const Integer&, long); - Integer operator | (long, const Integer&); - Integer operator ^ (const Integer&, const Integer&); - Integer operator ^ (const Integer&, long); - Integer operator ^ (long, const Integer&); - - Integer abs(const Integer&); // absolute value - Integer sqr(const Integer&); // square - - Integer pow(const Integer& x, const Integer& y); - Integer pow(const Integer& x, long y); - Integer Ipow(long x, long y); // x to the y as Integer - - -extern char* dec(const Integer& x, int width = 0); -extern char* oct(const Integer& x, int width = 0); -extern char* hex(const Integer& x, int width = 0); -extern Integer sqrt(const Integer&); // floor of square root -extern Integer lcm(const Integer& x, const Integer& y); // least common mult - - -typedef Integer IntTmp; // for backward compatibility - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - - -inline Integer::Integer() :rep(0) {} - -inline Integer::Integer(long y) :rep(Icopy_long(0, y)) {} - -inline Integer::Integer(const Integer& y) :rep(Icopy(0, y.rep)) {} - -inline Integer::~Integer() { delete rep; } - -inline void Integer::operator = (const Integer& y) -{ - rep = Icopy(rep, y.rep); -} - -inline void Integer::operator = (long y) -{ - rep = Icopy_long(rep, y); -} - -inline Integer::operator long() const -{ - return Itolong(rep); -} - -inline int Integer::initialized() const -{ - return rep != 0; -} - -inline int Integer::fits_in_long() const -{ - return Iislong(rep); -} - -inline Integer::operator double() const -{ - return Itodouble(rep); -} - -inline int Integer::fits_in_double() const -{ - return Iisdouble(rep); -} - -// procedural versions - -inline int compare(const Integer& x, const Integer& y) -{ - return compare(x.rep, y.rep); -} - -inline int ucompare(const Integer& x, const Integer& y) -{ - return ucompare(x.rep, y.rep); -} - -inline int compare(const Integer& x, long y) -{ - return compare(x.rep, y); -} - -inline int ucompare(const Integer& x, long y) -{ - return ucompare(x.rep, y); -} - -inline int compare(long x, const Integer& y) -{ - return -compare(y.rep, x); -} - -inline int ucompare(long x, const Integer& y) -{ - return -ucompare(y.rep, x); -} - -inline void add(const Integer& x, const Integer& y, Integer& dest) -{ - dest.rep = add(x.rep, 0, y.rep, 0, dest.rep); -} - -inline void sub(const Integer& x, const Integer& y, Integer& dest) -{ - dest.rep = add(x.rep, 0, y.rep, 1, dest.rep); -} - -inline void mul(const Integer& x, const Integer& y, Integer& dest) -{ - dest.rep = multiply(x.rep, y.rep, dest.rep); -} - -inline void div(const Integer& x, const Integer& y, Integer& dest) -{ - dest.rep = div(x.rep, y.rep, dest.rep); -} - -inline void mod(const Integer& x, const Integer& y, Integer& dest) -{ - dest.rep = mod(x.rep, y.rep, dest.rep); -} - -inline void and(const Integer& x, const Integer& y, Integer& dest) -{ - dest.rep = bitop(x.rep, y.rep, dest.rep, '&'); -} - -inline void or(const Integer& x, const Integer& y, Integer& dest) -{ - dest.rep = bitop(x.rep, y.rep, dest.rep, '|'); -} - -inline void xor(const Integer& x, const Integer& y, Integer& dest) -{ - dest.rep = bitop(x.rep, y.rep, dest.rep, '^'); -} - -inline void lshift(const Integer& x, const Integer& y, Integer& dest) -{ - dest.rep = lshift(x.rep, y.rep, 0, dest.rep); -} - -inline void rshift(const Integer& x, const Integer& y, Integer& dest) -{ - dest.rep = lshift(x.rep, y.rep, 1, dest.rep); -} - -inline void pow(const Integer& x, const Integer& y, Integer& dest) -{ - dest.rep = power(x.rep, long(y), dest.rep); // not incorrect -} - -inline void add(const Integer& x, long y, Integer& dest) -{ - dest.rep = add(x.rep, 0, y, dest.rep); -} - -inline void sub(const Integer& x, long y, Integer& dest) -{ - dest.rep = add(x.rep, 0, -y, dest.rep); -} - -inline void mul(const Integer& x, long y, Integer& dest) -{ - dest.rep = multiply(x.rep, y, dest.rep); -} - -inline void div(const Integer& x, long y, Integer& dest) -{ - dest.rep = div(x.rep, y, dest.rep); -} - -inline void mod(const Integer& x, long y, Integer& dest) -{ - dest.rep = mod(x.rep, y, dest.rep); -} - -inline void and(const Integer& x, long y, Integer& dest) -{ - dest.rep = bitop(x.rep, y, dest.rep, '&'); -} - -inline void or(const Integer& x, long y, Integer& dest) -{ - dest.rep = bitop(x.rep, y, dest.rep, '|'); -} - -inline void xor(const Integer& x, long y, Integer& dest) -{ - dest.rep = bitop(x.rep, y, dest.rep, '^'); -} - -inline void lshift(const Integer& x, long y, Integer& dest) -{ - dest.rep = lshift(x.rep, y, dest.rep); -} - -inline void rshift(const Integer& x, long y, Integer& dest) -{ - dest.rep = lshift(x.rep, -y, dest.rep); -} - -inline void pow(const Integer& x, long y, Integer& dest) -{ - dest.rep = power(x.rep, y, dest.rep); -} - -inline void abs(const Integer& x, Integer& dest) -{ - dest.rep = abs(x.rep, dest.rep); -} - -inline void negate(const Integer& x, Integer& dest) -{ - dest.rep = negate(x.rep, dest.rep); -} - -inline void complement(const Integer& x, Integer& dest) -{ - dest.rep = compl(x.rep, dest.rep); -} - -inline void add(long x, const Integer& y, Integer& dest) -{ - dest.rep = add(y.rep, 0, x, dest.rep); -} - -inline void sub(long x, const Integer& y, Integer& dest) -{ - dest.rep = add(y.rep, 1, x, dest.rep); -} - -inline void mul(long x, const Integer& y, Integer& dest) -{ - dest.rep = multiply(y.rep, x, dest.rep); -} - -inline void and(long x, const Integer& y, Integer& dest) -{ - dest.rep = bitop(y.rep, x, dest.rep, '&'); -} - -inline void or(long x, const Integer& y, Integer& dest) -{ - dest.rep = bitop(y.rep, x, dest.rep, '|'); -} - -inline void xor(long x, const Integer& y, Integer& dest) -{ - dest.rep = bitop(y.rep, x, dest.rep, '^'); -} - - -// operator versions - -inline int operator == (const Integer& x, const Integer& y) -{ - return compare(x, y) == 0; -} - -inline int operator == (const Integer& x, long y) -{ - return compare(x, y) == 0; -} - -inline int operator != (const Integer& x, const Integer& y) -{ - return compare(x, y) != 0; -} - -inline int operator != (const Integer& x, long y) -{ - return compare(x, y) != 0; -} - -inline int operator < (const Integer& x, const Integer& y) -{ - return compare(x, y) < 0; -} - -inline int operator < (const Integer& x, long y) -{ - return compare(x, y) < 0; -} - -inline int operator <= (const Integer& x, const Integer& y) -{ - return compare(x, y) <= 0; -} - -inline int operator <= (const Integer& x, long y) -{ - return compare(x, y) <= 0; -} - -inline int operator > (const Integer& x, const Integer& y) -{ - return compare(x, y) > 0; -} - -inline int operator > (const Integer& x, long y) -{ - return compare(x, y) > 0; -} - -inline int operator >= (const Integer& x, const Integer& y) -{ - return compare(x, y) >= 0; -} - -inline int operator >= (const Integer& x, long y) -{ - return compare(x, y) >= 0; -} - - -inline void Integer::operator += (const Integer& y) -{ - add(*this, y, *this); -} - -inline void Integer::operator += (long y) -{ - add(*this, y, *this); -} - -inline void Integer::operator ++ () -{ - add(*this, 1, *this); -} - - -inline void Integer::operator -= (const Integer& y) -{ - sub(*this, y, *this); -} - -inline void Integer::operator -= (long y) -{ - sub(*this, y, *this); -} - -inline void Integer::operator -- () -{ - add(*this, -1, *this); -} - - - -inline void Integer::operator *= (const Integer& y) -{ - mul(*this, y, *this); -} - -inline void Integer::operator *= (long y) -{ - mul(*this, y, *this); -} - - -inline void Integer::operator &= (const Integer& y) -{ - and(*this, y, *this); -} - -inline void Integer::operator &= (long y) -{ - and(*this, y, *this); -} - -inline void Integer::operator |= (const Integer& y) -{ - or(*this, y, *this); -} - -inline void Integer::operator |= (long y) -{ - or(*this, y, *this); -} - - -inline void Integer::operator ^= (const Integer& y) -{ - xor(*this, y, *this); -} - -inline void Integer::operator ^= (long y) -{ - xor(*this, y, *this); -} - - - -inline void Integer::operator /= (const Integer& y) -{ - div(*this, y, *this); -} - -inline void Integer::operator /= (long y) -{ - div(*this, y, *this); -} - - -inline void Integer::operator %= (const Integer& y) -{ - mod(*this, y, *this); -} - -inline void Integer::operator %= (long y) -{ - mod(*this, y, *this); -} - - -inline void Integer::operator <<= (const Integer& y) -{ - lshift(*this, y, *this); -} - -inline void Integer::operator <<= (long y) -{ - lshift(*this, y, *this); -} - - -inline void Integer::operator >>= (const Integer& y) -{ - rshift(*this, y, *this); -} - -inline void Integer::operator >>= (long y) -{ - rshift(*this, y, *this); -} - -#ifdef __GNUG__ -inline Integer operator ? (const Integer& x, const Integer& y) -{ - return (compare(x.rep, y.rep) >= 0)? x : y; -} -#endif - - -inline void Integer::abs() -{ - ::abs(*this, *this); -} - -inline void Integer::negate() -{ - ::negate(*this, *this); -} - - -inline void Integer::complement() -{ - ::complement(*this, *this); -} - - -inline int sign(const Integer& x) -{ - return (x.rep->len == 0) ? 0 : ( (x.rep->sgn == 1) ? 1 : -1 ); -} - -inline int even(const Integer& y) -{ - return y.rep->len == 0 || !(y.rep->s[0] & 1); -} - -inline int odd(const Integer& y) -{ - return y.rep->len > 0 && (y.rep->s[0] & 1); -} - -inline char* Itoa(const Integer& y, int base, int width) -{ - return Itoa(y.rep, base, width); -} - - -inline ostream& operator << (ostream& s, const Integer& y) -{ - return s << Itoa(y.rep); -} - -inline long lg(const Integer& x) -{ - return lg(x.rep); -} - -// constructive operations - -#if defined(__GNUG__) && !defined(NO_NRV) - -inline Integer operator + (const Integer& x, const Integer& y) return r -{ - add(x, y, r); -} - -inline Integer operator + (const Integer& x, long y) return r -{ - add(x, y, r); -} - -inline Integer operator + (long x, const Integer& y) return r -{ - add(x, y, r); -} - -inline Integer operator - (const Integer& x, const Integer& y) return r -{ - sub(x, y, r); -} - -inline Integer operator - (const Integer& x, long y) return r -{ - sub(x, y, r); -} - -inline Integer operator - (long x, const Integer& y) return r -{ - sub(x, y, r); -} - -inline Integer operator * (const Integer& x, const Integer& y) return r -{ - mul(x, y, r); -} - -inline Integer operator * (const Integer& x, long y) return r -{ - mul(x, y, r); -} - -inline Integer operator * (long x, const Integer& y) return r -{ - mul(x, y, r); -} - -inline Integer sqr(const Integer& x) return r -{ - mul(x, x, r); -} - -inline Integer operator & (const Integer& x, const Integer& y) return r -{ - and(x, y, r); -} - -inline Integer operator & (const Integer& x, long y) return r -{ - and(x, y, r); -} - -inline Integer operator & (long x, const Integer& y) return r -{ - and(x, y, r); -} - -inline Integer operator | (const Integer& x, const Integer& y) return r -{ - or(x, y, r); -} - -inline Integer operator | (const Integer& x, long y) return r -{ - or(x, y, r); -} - -inline Integer operator | (long x, const Integer& y) return r -{ - or(x, y, r); -} - -inline Integer operator ^ (const Integer& x, const Integer& y) return r -{ - xor(x, y, r); -} - -inline Integer operator ^ (const Integer& x, long y) return r -{ - xor(x, y, r); -} - -inline Integer operator ^ (long x, const Integer& y) return r -{ - xor(x, y, r); -} - -inline Integer operator / (const Integer& x, const Integer& y) return r -{ - div(x, y, r); -} - -inline Integer operator / (const Integer& x, long y) return r -{ - div(x, y, r); -} - -inline Integer operator % (const Integer& x, const Integer& y) return r -{ - mod(x, y, r); -} - -inline Integer operator % (const Integer& x, long y) return r -{ - mod(x, y, r); -} - -inline Integer operator << (const Integer& x, const Integer& y) return r -{ - lshift(x, y, r); -} - -inline Integer operator << (const Integer& x, long y) return r -{ - lshift(x, y, r); -} - -inline Integer operator >> (const Integer& x, const Integer& y) return r; -{ - rshift(x, y, r); -} - -inline Integer operator >> (const Integer& x, long y) return r -{ - rshift(x, y, r); -} - -inline Integer pow(const Integer& x, long y) return r -{ - pow(x, y, r); -} - -inline Integer Ipow(long x, long y) return r(x) -{ - pow(r, y, r); -} - -inline Integer pow(const Integer& x, const Integer& y) return r -{ - pow(x, y, r); -} - - - -inline Integer abs(const Integer& x) return r -{ - abs(x, r); -} - -inline Integer operator - (const Integer& x) return r -{ - negate(x, r); -} - -inline Integer operator ~ (const Integer& x) return r -{ - complement(x, r); -} - -inline Integer atoI(const char* s, int base) return r -{ - r.rep = atoIntRep(s, base); -} - -inline Integer gcd(const Integer& x, const Integer& y) return r -{ - r.rep = gcd(x.rep, y.rep); -} - -#else /* NO_NRV */ - -inline Integer operator + (const Integer& x, const Integer& y) -{ - Integer r; add(x, y, r); return r; -} - -inline Integer operator + (const Integer& x, long y) -{ - Integer r; add(x, y, r); return r; -} - -inline Integer operator + (long x, const Integer& y) -{ - Integer r; add(x, y, r); return r; -} - -inline Integer operator - (const Integer& x, const Integer& y) -{ - Integer r; sub(x, y, r); return r; -} - -inline Integer operator - (const Integer& x, long y) -{ - Integer r; sub(x, y, r); return r; -} - -inline Integer operator - (long x, const Integer& y) -{ - Integer r; sub(x, y, r); return r; -} - -inline Integer operator * (const Integer& x, const Integer& y) -{ - Integer r; mul(x, y, r); return r; -} - -inline Integer operator * (const Integer& x, long y) -{ - Integer r; mul(x, y, r); return r; -} - -inline Integer operator * (long x, const Integer& y) -{ - Integer r; mul(x, y, r); return r; -} - -inline Integer sqr(const Integer& x) -{ - Integer r; mul(x, x, r); return r; -} - -inline Integer operator & (const Integer& x, const Integer& y) -{ - Integer r; and(x, y, r); return r; -} - -inline Integer operator & (const Integer& x, long y) -{ - Integer r; and(x, y, r); return r; -} - -inline Integer operator & (long x, const Integer& y) -{ - Integer r; and(x, y, r); return r; -} - -inline Integer operator | (const Integer& x, const Integer& y) -{ - Integer r; or(x, y, r); return r; -} - -inline Integer operator | (const Integer& x, long y) -{ - Integer r; or(x, y, r); return r; -} - -inline Integer operator | (long x, const Integer& y) -{ - Integer r; or(x, y, r); return r; -} - -inline Integer operator ^ (const Integer& x, const Integer& y) -{ - Integer r; xor(x, y, r); return r; -} - -inline Integer operator ^ (const Integer& x, long y) -{ - Integer r; xor(x, y, r); return r; -} - -inline Integer operator ^ (long x, const Integer& y) -{ - Integer r; xor(x, y, r); return r; -} - -inline Integer operator / (const Integer& x, const Integer& y) -{ - Integer r; div(x, y, r); return r; -} - -inline Integer operator / (const Integer& x, long y) -{ - Integer r; div(x, y, r); return r; -} - -inline Integer operator % (const Integer& x, const Integer& y) -{ - Integer r; mod(x, y, r); return r; -} - -inline Integer operator % (const Integer& x, long y) -{ - Integer r; mod(x, y, r); return r; -} - -inline Integer operator << (const Integer& x, const Integer& y) -{ - Integer r; lshift(x, y, r); return r; -} - -inline Integer operator << (const Integer& x, long y) -{ - Integer r; lshift(x, y, r); return r; -} - -inline Integer operator >> (const Integer& x, const Integer& y) -{ - Integer r; rshift(x, y, r); return r; -} - -inline Integer operator >> (const Integer& x, long y) -{ - Integer r; rshift(x, y, r); return r; -} - -inline Integer pow(const Integer& x, long y) -{ - Integer r; pow(x, y, r); return r; -} - -inline Integer Ipow(long x, long y) -{ - Integer r(x); pow(r, y, r); return r; -} - -inline Integer pow(const Integer& x, const Integer& y) -{ - Integer r; pow(x, y, r); return r; -} - - - -inline Integer abs(const Integer& x) -{ - Integer r; abs(x, r); return r; -} - -inline Integer operator - (const Integer& x) -{ - Integer r; negate(x, r); return r; -} - -inline Integer operator ~ (const Integer& x) -{ - Integer r; complement(x, r); return r; -} - -inline Integer atoI(const char* s, int base) -{ - Integer r; r.rep = atoIntRep(s, base); return r; -} - -inline Integer gcd(const Integer& x, const Integer& y) -{ - Integer r; r.rep = gcd(x.rep, y.rep); return r; -} - -#endif -#endif -#endif diff --git a/gnu/lib/libg++/g++-include/List.ccP b/gnu/lib/libg++/g++-include/List.ccP new file mode 100644 index 0000000000..2afbdaf972 --- /dev/null +++ b/gnu/lib/libg++/g++-include/List.ccP @@ -0,0 +1,956 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".List.h" + +ListNode NilListNode; + +class init_NilListNode +{ +public: + init_NilListNode() + { + NilListNode.tl = &NilListNode; + NilListNode.ref = -1; + } +}; + +static init_NilListNode NilListNode_initializer; + +List& List::operator = (List& a) +{ + reference(a.P); + dereference(P); + P = a.P; + return *this; +} + + List::pop() +{ + res = P->hd; + ListNode* tail = P->tl; + reference(tail); + dereference(P); + P = tail; + return res; +} + +void List::set_tail(List& a) +{ + reference(a.P); + dereference(P->tl); + P->tl = a.P; +} + +List List::nth(int n) +{ + for (ListNode* p = P; n-- > 0; p = p->tl); + reference(p); + return List(p); +} + +List List::last() +{ + ListNode* p = P; + if (p != &NilListNode) while (p->tl != &NilListNode) p = p->tl; + reference(p); + return List(p); +} + +void List::append(List& l) +{ + ListNode* p = P; + ListNode* a = l.P; + reference(a); + if (p != &NilListNode) + { + while (p->tl != &NilListNode) p = p->tl; + p->tl = a; + } + else + P = a; +} + +int List::length() +{ + int l = 0; + for (ListNode* p = P; p != &NilListNode; p = p->tl) ++l; + return l; +} + +& List::operator [] (int n) +{ + for (ListNode* p = P; n-- > 0; p = p->tl); + return (p->hd); +} + +int operator == (List& x, List& y) +{ + ListNode* a = x.P; + ListNode* b = y.P; + + for (;;) + { + if (a == &NilListNode) + return b == &NilListNode; + else if (b == &NilListNode) + return 0; + else if (EQ(a->hd, b->hd)) + { + a = a->tl; + b = b->tl; + } + else + return 0; + } +} + + +void List::apply(Procedure f) +{ + for(ListNode* p = P; p != &NilListNode; p = p->tl) + (*f)((p->hd)); +} + +void List::subst( old, repl) +{ + for(ListNode* p = P; p != &NilListNode; p = p->tl) + if (EQ(p->hd, old)) + p->hd = repl; +} + + List::reduce(Combiner f, base) +{ + r = base; + for(ListNode* p = P; p != &NilListNode; p = p->tl) + r = (*f)(r, (p->hd)); + return r; +} + +int List::position( targ) +{ + int l = 0; + ListNode* p = P; + for (;;) + { + if (p == &NilListNode) + return -1; + else if (EQ(p->hd, targ)) + return l; + else + { + ++l; + p = p->tl; + } + } +} + +int List::contains( targ) +{ + ListNode* p = P; + for (;;) + { + if (p == &NilListNode) + return 0; + else if (EQ(p->hd, targ)) + return 1; + else + p = p->tl; + } +} + +List List::find( targ) +{ + ListNode* p = P; + while (p != &NilListNode && !EQ(p->hd, targ)) + p=p->tl; + reference(p); + return List(p); +} + +Pix List::seek( targ) +{ + ListNode* p = P; + for (;;) + { + if (p == &NilListNode) + return 0; + else if (EQ(p->hd, targ)) + return Pix(p); + else + p = p->tl; + } +} + +int List::owns(Pix i) +{ + ListNode* p = P; + for (;;) + { + if (p == &NilListNode) + return 0; + else if (Pix(p) == i) + return 1; + else + p = p->tl; + } +} + +List List::find(List& target) +{ + ListNode* targ = target.P; + if (targ == &NilListNode) + return List(targ); + + ListNode* p = P; + while (p != &NilListNode) + { + if (EQ(p->hd, targ->hd)) + { + ListNode* a = p->tl; + ListNode* t = targ->tl; + for(;;) + { + if (t == &NilListNode) + { + reference(p); + return List(p); + } + else if (a == &NilListNode || !EQ(a->hd, t->hd)) + break; + else + { + a = a->tl; + t = t->tl; + } + } + } + p = p->tl; + } + return List(&NilListNode); +} + +int List::contains(List& target) +{ + ListNode* targ = target.P; + if (targ == &NilListNode) + return 0; + + ListNode* p = P; + while (p != &NilListNode) + { + if (EQ(p->hd, targ->hd)) + { + ListNode* a = p->tl; + ListNode* t = targ->tl; + for(;;) + { + if (t == &NilListNode) + return 1; + else if (a == &NilListNode || !EQ(a->hd, t->hd)) + break; + else + { + a = a->tl; + t = t->tl; + } + } + } + p = p->tl; + } + return 0; +} + +void List::del( targ) +{ + ListNode* h = P; + + for (;;) + { + if (h == &NilListNode) + { + P = h; + return; + } + else if (EQ(h->hd, targ)) + { + ListNode* nxt = h->tl; + reference(nxt); + dereference(h); + h = nxt; + } + else + break; + } + + ListNode* trail = h; + ListNode* p = h->tl; + while (p != &NilListNode) + { + if (EQ(p->hd, targ)) + { + ListNode* nxt = p->tl; + reference(nxt); + dereference(p); + trail->tl = nxt; + p = nxt; + } + else + { + trail = p; + p = p->tl; + } + } + P = h; +} + +void List::del(Predicate f) +{ + ListNode* h = P; + for (;;) + { + if (h == &NilListNode) + { + P = h; + return; + } + else if ((*f)(h->hd)) + { + ListNode* nxt = h->tl; + reference(nxt); + dereference(h); + h = nxt; + } + else + break; + } + + ListNode* trail = h; + ListNode* p = h->tl; + while (p != &NilListNode) + { + if ((*f)(p->hd)) + { + ListNode* nxt = p->tl; + reference(nxt); + dereference(p); + trail->tl = nxt; + p = nxt; + } + else + { + trail = p; + p = p->tl; + } + } + P = h; +} + +void List::select(Predicate f) +{ + ListNode* h = P; + for (;;) + { + if (h == &NilListNode) + { + P = h; + return; + } + else if (!(*f)(h->hd)) + { + ListNode* nxt = h->tl; + reference(nxt); + dereference(h); + h = nxt; + } + else + break; + } + ListNode* trail = h; + ListNode* p = h->tl; + while (p != &NilListNode) + { + if (!(*f)(p->hd)) + { + ListNode* nxt = p->tl; + reference(nxt); + dereference(p); + trail->tl = nxt; + p = nxt; + } + else + { + trail = p; + p = p->tl; + } + } + P = h; +} + +void List::reverse() +{ + ListNode* l = &NilListNode; + ListNode* p = P; + while (p != &NilListNode) + { + ListNode* nxt = p->tl; + p->tl = l; + l = p; + p = nxt; + } + P = l; +} + + +List copy(List& x) +{ + ListNode* a = x.P; + if (a == &NilListNode) + return List(a); + else + { + ListNode* h = newListNode(a->hd); + ListNode* trail = h; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + ListNode* n = newListNode(a->hd); + trail->tl = n; + trail = n; + } + trail->tl = &NilListNode; + return List(h); + } +} + + +List subst( old, repl, List& x) +{ + ListNode* a = x.P; + if (a == &NilListNode) + return List(a); + else + { + ListNode* h = new ListNode; + h->ref = 1; + if (EQ(a->hd, old)) + h->hd = repl; + else + h->hd = a->hd; + ListNode* trail = h; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + ListNode* n = new ListNode; + n->ref = 1; + if (EQ(a->hd, old)) + n->hd = repl; + else + n->hd = a->hd; + trail->tl = n; + trail = n; + } + trail->tl = &NilListNode; + return List(h); + } +} + +List combine(Combiner f, List& x, List& y) +{ + ListNode* a = x.P; + ListNode* b = y.P; + if (a == &NilListNode || b == &NilListNode) + return List(&NilListNode); + else + { + ListNode* h = newListNode((*f)(a->hd, b->hd)); + ListNode* trail = h; + a = a->tl; + b = b->tl; + while (a != &NilListNode && b != &NilListNode) + { + ListNode* n = newListNode((*f)(a->hd, b->hd)); + trail->tl = n; + trail = n; + a = a->tl; + b = b->tl; + } + trail->tl = &NilListNode; + return List(h); + } +} + +List reverse(List& x) +{ + ListNode* a = x.P; + if (a == &NilListNode) + return List(a); + else + { + ListNode* l = newListNode(a->hd); + l->tl = &NilListNode; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + ListNode* n = newListNode(a->hd); + n->tl = l; + l = n; + } + return List(l); + } +} + +List append(List& x, List& y) +{ + ListNode* a = x.P; + ListNode* b = y.P; + reference(b); + if (a != &NilListNode) + { + ListNode* h = newListNode(a->hd); + ListNode* trail = h; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + ListNode* n = newListNode(a->hd); + trail->tl = n; + trail = n; + } + trail->tl = b; + return List(h); + } + else + return List(b); +} + +void List::prepend(List& y) +{ + ListNode* b = y.P; + if (b != &NilListNode) + { + ListNode* h = newListNode(b->hd); + ListNode* trail = h; + for(b = b->tl; b != &NilListNode; b = b->tl) + { + ListNode* n = newListNode(b->hd); + trail->tl = n; + trail = n; + } + trail->tl = P; + P = h; + } +} + +List concat(List& x, List& y) +{ + ListNode* a = x.P; + ListNode* b = y.P; + if (a != &NilListNode) + { + ListNode* h = newListNode(a->hd); + ListNode* trail = h; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + ListNode* n = newListNode(a->hd); + trail->tl = n; + trail = n; + }; + for(;b != &NilListNode; b = b->tl) + { + ListNode* n = newListNode(b->hd); + trail->tl = n; + trail = n; + } + trail->tl = &NilListNode; + return List(h); + } + else if (b != &NilListNode) + { + ListNode* h = newListNode(b->hd); + ListNode* trail = h; + for(b = b->tl; b != &NilListNode; b = b->tl) + { + ListNode* n = newListNode(b->hd); + trail->tl = n; + trail = n; + } + trail->tl = &NilListNode; + return List(h); + } + else + return List(&NilListNode); +} + +List select(Predicate f, List& x) +{ + ListNode* a = x.P; + ListNode* h = &NilListNode; + while (a != &NilListNode) + { + if ((*f)(a->hd)) + { + h = newListNode(a->hd); + ListNode* trail = h; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + if ((*f)(a->hd)) + { + ListNode* n = newListNode(a->hd); + trail->tl = n; + trail = n; + } + } + trail->tl = &NilListNode; + break; + } + else + a = a->tl; + } + return List(h); +} + +List remove(Predicate f, List& x) +{ + ListNode* a = x.P; + ListNode* h = &NilListNode; + while (a != &NilListNode) + { + if (!(*f)(a->hd)) + { + h = newListNode(a->hd); + ListNode* trail = h; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + if (!(*f)(a->hd)) + { + ListNode* n = newListNode(a->hd); + trail->tl = n; + trail = n; + } + } + trail->tl = &NilListNode; + break; + } + else + a = a->tl; + } + return List(h); +} + +List remove( targ, List& x) +{ + ListNode* a = x.P; + ListNode* h = &NilListNode; + while (a != &NilListNode) + { + if (!(EQ(a->hd, targ))) + { + h = newListNode(a->hd); + ListNode* trail = h; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + if (!EQ(a->hd, targ)) + { + ListNode* n = newListNode(a->hd); + trail->tl = n; + trail = n; + } + } + trail->tl = &NilListNode; + break; + } + else + a = a->tl; + } + return List(h); +} + +List map(Mapper f, List& x) +{ + ListNode* a = x.P; + ListNode* h = &NilListNode; + if (a != &NilListNode) + { + h = newListNode((*f)(a->hd)); + ListNode* trail = h; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + ListNode* n = newListNode((*f)(a->hd)); + trail->tl = n; + trail = n; + } + trail->tl = &NilListNode; + } + return List(h); +} + + +List merge(List& x, List& y, Comparator f) +{ + ListNode* a = x.P; + ListNode* b = y.P; + + if (a == &NilListNode) + { + if (b == &NilListNode) + return List(&NilListNode); + else + return copy(y); + } + else if (b == &NilListNode) + return copy(x); + + ListNode* h = new ListNode; + h->ref = 1; + if ((*f)(a->hd, b->hd) <= 0) + { + h->hd = a->hd; + a = a->tl; + } + else + { + h->hd = b->hd; + b = b->tl; + } + + ListNode* r = h; + + for(;;) + { + if (a == &NilListNode) + { + while (b != &NilListNode) + { + ListNode* n = new ListNode; + n->ref = 1; + n->hd = b->hd; + r->tl = n; + r = n; + b = b->tl; + } + r->tl = &NilListNode; + return List(h); + } + else if (b == &NilListNode) + { + while (a != &NilListNode) + { + ListNode* n = new ListNode; + n->ref = 1; + n->hd = a->hd; + r->tl = n; + r = n; + a = a->tl; + } + r->tl = &NilListNode; + return List(h); + } + else if ((*f)(a->hd, b->hd) <= 0) + { + ListNode* n = new ListNode; + n->ref = 1; + n->hd = a->hd; + r->tl = n; + r = n; + a = a->tl; + } + else + { + ListNode* n = new ListNode; + n->ref = 1; + n->hd = b->hd; + r->tl = n; + r = n; + b = b->tl; + } + } +} + +void List::sort(Comparator f) +{ + // strategy: place runs in queue, merge runs until done + // This is often very fast + + if (P == &NilListNode || P->tl == &NilListNode) + return; + + int qlen = 250; // guess a good queue size, realloc if necessary + + ListNode** queue = (ListNode**)malloc(qlen * sizeof(ListNode*)); + + ListNode* h = P; + ListNode* a = h; + ListNode* b = a->tl; + int qin = 0; + + while (b != &NilListNode) + { + if ((*f)(a->hd, b->hd) > 0) + { + if (h == a) // minor optimization: ensure runlen >= 2 + { + h = b; + a->tl = b->tl; + b->tl = a; + b = a->tl; + } + else + { + if (qin >= qlen) + { + qlen *= 2; + queue = (ListNode**)realloc(queue, qlen * sizeof(ListNode*)); + } + queue[qin++] = h; + a->tl = &NilListNode; + h = a = b; + b = b->tl; + } + } + else + { + a = b; + b = b->tl; + } + } + + int count = qin; + queue[qin] = h; + if (++qin >= qlen) qin = 0; + int qout = 0; + + while (count-- > 0) + { + a = queue[qout]; + if (++qout >= qlen) qout = 0; + b = queue[qout]; + if (++qout >= qlen) qout = 0; + + if ((*f)(a->hd, b->hd) <= 0) + { + h = a; + a = a->tl; + } + else + { + h = b; + b = b->tl; + } + queue[qin] = h; + if (++qin >= qlen) qin = 0; + + for (;;) + { + if (a == &NilListNode) + { + h->tl = b; + break; + } + else if (b == &NilListNode) + { + h->tl = a; + break; + } + else if ((*f)(a->hd, b->hd) <= 0) + { + h->tl = a; + h = a; + a = a->tl; + } + else + { + h->tl = b; + h = b; + b = b->tl; + } + } + } + P = queue[qout]; + free(queue); +} + +int List::list_length() +{ + ListNode* fast = P; + if (fast == &NilListNode) + return 0; + + ListNode* slow = fast->tl; + if (slow == &NilListNode) + return 1; + + fast = slow->tl; + int n = 2; + + for (;;) + { + if (fast == &NilListNode) + return n; + else if (fast->tl == &NilListNode) + return n+1; + else if (fast == slow) + return -1; + else + { + n += 2; + fast = fast->tl->tl; + slow = slow->tl; + } + } +} + +void List::error(const char* msg) +{ + (*lib_error_handler)("List", msg); +} + +int List::OK() +{ + int v = P != 0; // have a node + // check that all nodes OK, even if circular: + + ListNode* fast = P; + if (fast != &NilListNode) + { + v &= fast->ref != 0; + ListNode* slow = fast->tl; + v &= slow->ref != 0; + if (v && slow != &NilListNode) + { + fast = slow->tl; + v &= fast->ref != 0; + while (v) + { + if (fast == &NilListNode) + break; + else if (fast->tl == &NilListNode) + break; + else if (fast == slow) + break; + else + { + v &= fast->ref != 0 && slow->ref != 0; + fast = fast->tl->tl; + slow = slow->tl; + } + } + } + } + if (!v) error ("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/List.hP b/gnu/lib/libg++/g++-include/List.hP new file mode 100644 index 0000000000..7ccdbc3bc4 --- /dev/null +++ b/gnu/lib/libg++/g++-include/List.hP @@ -0,0 +1,273 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _List_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _List_h 1 + +#ifndef __typedefs +#define __typedefs 1 +typedef void (*Procedure)(); +typedef (*Mapper)(); +typedef (*Combiner)(, ); +typedef int (*Predicate)(); +typedef int (*Comparator)(, ); +#endif + +#include +#include ".defs.h" + +struct ListNode +{ + ListNode* tl; + short ref; + hd; +}; + +extern ListNode NilListNode; + +class List +{ +protected: + ListNode* P; + + List(ListNode* p); +public: + List(); + List( head); + List( head, List& tl); + List(List& a); + List(Pix p); + ~List(); + + List& operator = (List& a); + + int null(); + int valid(); + operator const void* (); + int operator ! (); + + int length(); + int list_length(); + + & get(); + & head(); + & operator [] (int n); + + List nth(int n); + List tail(); + List last(); + + List find( targ); + List find(List& targ); + int contains( targ); + int contains(List& targ); + int position( targ); + + friend List copy(List& a); + friend List concat(List& a, List& b); + friend List append(List& a, List& b); + friend List map(Mapper f, List& a); + friend List merge(List& a, List& b, Comparator f); + friend List combine(Combiner f, List& a, List& b); + friend List reverse(List& a); + friend List select(Predicate f, List& a); +#undef remove + friend List remove( targ, List& a); + friend List remove(Predicate f, List& a); + friend List subst( old, repl, List& a); + + void push( x); + pop(); + + void set_tail(List& p); + void append(List& p); + void prepend(List& p); + void del( targ); + void del(Predicate f); + void select(Predicate f); + void subst( old, repl); + void reverse(); + void sort(Comparator f); + + void apply(Procedure f); + reduce(Combiner f, base); + + friend int operator == (List& a, List& b); + friend int operator != (List& a, List& b); + + Pix first(); + void next(Pix& p); + Pix seek( item); + & operator () (Pix p); + int owns(Pix p); + + void error(const char*); + int OK(); +}; + + +inline void reference(ListNode* p) +{ + if (p->ref >= 0) ++p->ref; +} + +inline void dereference(ListNode* p) +{ + while (p->ref > 0 && --p->ref == 0) + { + ListNode* n = p->tl; + delete(p); + p = n; + } +} + + +inline ListNode* newListNode( h) +{ + ListNode* p = new ListNode; + p->ref = 1; + p->hd = h; + return p; +} + +inline ListNode* newListNode( h, ListNode* t) +{ + ListNode* p = new ListNode; + p->ref = 1; + p->hd = h; + p->tl = t; + return p; +} + + +inline List::~List() +{ + dereference(P); +} + +inline List::List() +{ + P = &NilListNode; +} + +inline List::List(ListNode* p) +{ + P = p; +} + +inline List::List( head) +{ + P = newListNode(head); + P->tl = &NilListNode; +} + +inline List::List( head, List& tl) +{ + P = newListNode(head, tl.P); + reference(P->tl); +} + +inline List::List(List& a) +{ + reference(a.P); + P = a.P; +} + + +inline & List::get() +{ + return P->hd; +} + +inline & List::head() +{ + return P->hd; +} + + +inline List List::tail() +{ + reference(P->tl); + return List(P->tl); +} + + + +inline int List::null() +{ + return P == &NilListNode; +} + +inline int List::valid() +{ + return P != &NilListNode; +} + +inline List::operator const void* () +{ + return (P == &NilListNode)? 0 : this; +} + +inline int List::operator ! () +{ + return (P == &NilListNode); +} + + +inline void List::push( head) +{ + ListNode* oldp = P; + P = newListNode(head, oldp); +} + + +inline int operator != (List& x, List& y) +{ + return !(x == y); +} + +inline Pix List::first() +{ + return (P == &NilListNode)? 0 : Pix(P); +} + +inline & List::operator () (Pix p) +{ + return ((ListNode*)p)->hd; +} + +inline void List::next(Pix& p) +{ + if (p != 0) + { + p = Pix(((ListNode*)p)->tl); + if (p == &NilListNode) p = 0; + } +} + +inline List::List(Pix p) +{ + P = (ListNode*)p; + reference(P); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/LogNorm.h b/gnu/lib/libg++/g++-include/LogNorm.h deleted file mode 100644 index 6c7c74cdef..0000000000 --- a/gnu/lib/libg++/g++-include/LogNorm.h +++ /dev/null @@ -1,84 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ -#ifndef _LogNormal_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _LogNormal_h - -#include "Normal.h" - -class LogNormal: public Normal { -protected: - double logMean; - double logVariance; - void setState(); -public: - LogNormal(double mean, double variance, RNG *gen); - double mean(); - double mean(double x); - double variance(); - double variance(double x); - virtual double operator()(); -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline void LogNormal::setState() -{ - double m2 = logMean * logMean; - pMean = log(m2 / sqrt(logVariance + m2) ); - pVariance = log((sqrt(logVariance + m2)/m2 )); -} - -inline LogNormal::LogNormal(double mean, double variance, RNG *gen) - : (mean, variance, gen) -{ - logMean = mean; - logVariance = variance; - setState(); -} - -inline double LogNormal::mean() { - return logMean; -} - -inline double LogNormal::mean(double x) -{ - double t=logMean; logMean = x; setState(); - return t; -} - -inline double LogNormal::variance() { - return logVariance; -} - -inline double LogNormal::variance(double x) -{ - double t=logVariance; logVariance = x; setState(); - return t; -} - -#endif -#endif diff --git a/gnu/lib/libg++/g++-include/MLCG.h b/gnu/lib/libg++/g++-include/MLCG.h deleted file mode 100644 index 73791f055c..0000000000 --- a/gnu/lib/libg++/g++-include/MLCG.h +++ /dev/null @@ -1,97 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ -#ifndef _MLCG_h -#define _MLCG_h 1 -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif - -#include -#include - -// -// Multiplicative Linear Conguential Generator -// - -class MLCG : public RNG { - long initialSeedOne; - long initialSeedTwo; - long seedOne; - long seedTwo; - -protected: - -public: - MLCG(long seed1 = 0, long seed2 = 1); - // - // Return a long-words word of random bits - // - virtual unsigned long asLong(); - virtual void reset(); - long seed1(); - void seed1(long); - long seed2(); - void seed2(long); - void reseed(long, long); -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline long -MLCG::seed1() -{ - return(seedOne); -} - -inline void -MLCG::seed1(long s) -{ - initialSeedOne = s; - reset(); -} - -inline long -MLCG::seed2() -{ - return(seedTwo); -} - -inline void -MLCG::seed2(long s) -{ - initialSeedTwo = s; - reset(); -} - -inline void -MLCG::reseed(long s1, long s2) -{ - initialSeedOne = s1; - initialSeedTwo = s2; - reset(); -} - -#endif - -#endif diff --git a/gnu/lib/libg++/g++-include/MPlex.ccP b/gnu/lib/libg++/g++-include/MPlex.ccP new file mode 100644 index 0000000000..89a1bcf9e6 --- /dev/null +++ b/gnu/lib/libg++/g++-include/MPlex.ccP @@ -0,0 +1,845 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".MPlex.h" + +// MChunk support + + +MChunk::MChunk(* d, + int baseidx, + int lowidx, + int fenceidx, + int topidx) + : IChunk(d, baseidx, lowidx, fenceidx, topidx) +{ + unused = fence - low; + unsigned msize = (top - base)/_MAP_BITS + 1; + map = (unsigned long *) (new long[msize]); + memset((void*)map, 0, msize * sizeof(long)); +} + +void MChunk:: shrink_high () +{ + if (fence <= low) empty_error(); + --fence; + if (!valid(fence)) + --unused; + else + free(fence); + reset_high(); +} + +void MChunk:: shrink_low () +{ + if (fence <= low) empty_error(); + if (!valid(low)) + --unused; + else + free(low); + ++low; + reset_low(); +} + +void MChunk::clear(int lo) +{ + int s = top - base; + low = base = fence = lo; + top = base + s; + unused = 0; + memset((void*)map, 0, ((top - base)/_MAP_BITS + 1) * sizeof(long)); +} + +void MChunk::cleardown(int hi) +{ + int s = top - base; + low = top = fence = hi; + base = top - s; + unused = 0; + memset((void*)map, 0, ((top - base)/_MAP_BITS + 1) * sizeof(long)); +} + +int MChunk::del(int idx) +{ + if (idx < low || idx >= fence) index_error(); + int v = valid(idx); + if (v) + { + free(idx); + ++unused; + } + return v; +} + + +int MChunk::undel(int idx) +{ + if (idx < low || idx >= fence) index_error(); + int v = valid(idx); + if (!v) + { + mark(idx); + --unused; + } + return v; +} + +int MChunk::unused_index() const +{ + if (unused_indices() == 0) index_error(); + int slot; + if (low == base) // can traverse 32 slots at a time + { + int blk = 0; + while (map[blk] == ~0L) ++blk; + slot = blk * _MAP_BITS + base; + } + else + slot = low; + + while(valid(slot)) ++slot; + return slot; +} + +int MChunk::first_index() const +{ + if (empty()) return fence; + int slot; + if (low == base) + { + int blk = 0; + while (map[blk] == 0) ++blk; + slot = blk * _MAP_BITS + base; + } + else + slot = low; + + while(!valid(slot)) ++slot; + return slot; +} + +int MChunk::last_index() const +{ + if (empty()) return low - 1; + int slot; + if (top == fence) + { + int blk = (top - base) / _MAP_BITS; + while (map[blk] == 0) --blk; + slot = blk * _MAP_BITS + base + _MAP_BITS - 1; + } + else + slot = fence - 1; + + while(!valid(slot)) --slot; + return slot; +} + + +int MChunk:: OK() const +{ + int v = data != 0; // have some data + v &= map != 0; // and a map + v &= base <= low; // ok, index-wise + v &= low <= fence; + v &= fence <= top; + + v &= ((MChunk*)(nxt->prev())) == this; // and links are OK + v &= ((MChunk*)(prv->next())) == this; + + int bitcount = 0; // and unused count correct + for (int i = low; i < fence; ++i) if (!valid(i)) ++bitcount; + v &= unused == bitcount; + + if (!v) error("invariant failure"); + return(v); +} + +* MChunk::succ(* p) const +{ + int i = ((int) p - (int) data) / sizeof() + base + 1; + if (p == 0 || i < low) return 0; + while (i < fence && !valid(i)) ++i; + if (i >= fence) return 0; + return pointer_to(i); +} + +* MChunk::pred(* p) const +{ + int i = ((int) p - (int) data) / sizeof() + base - 1; + if (p == 0 || i >= fence) return 0; + while (i >= low && !valid(i)) --i; + if (i < low) return 0; + return pointer_to(i); +} + +* MChunk::first_pointer() const +{ + if (empty()) return 0; + int slot; + if (low == base) + { + int blk = 0; + while (map[blk] == 0) ++blk; + slot = blk * _MAP_BITS + base; + } + else + slot = low; + + while(!valid(slot)) ++slot; + return pointer_to(slot); +} + +* MChunk::last_pointer() const +{ + if (empty()) return 0; + int slot; + if (top == fence) + { + int blk = (top - base) / _MAP_BITS; + while (map[blk] == 0) --blk; + slot = blk * _MAP_BITS + base + _MAP_BITS - 1; + } + else + slot = fence - 1; + + while(!valid(slot)) --slot; + return pointer_to(slot); +} + +MPlex:: MPlex() +{ + unused = 0; + lo = fnc = 0; + csize = DEFAULT_INITIAL_CAPACITY; + * data = new [csize]; + hd = ch = new MChunk(data, lo, lo, fnc, lo+csize); +} + +MPlex:: MPlex(int chunksize) +{ + if (chunksize == 0) error("invalid constructor specification"); + unused = 0; + lo = fnc = 0; + if (chunksize > 0) + { + csize = chunksize; + * data = new [csize]; + hd = ch = new MChunk(data, lo, lo, fnc, csize); + } + else + { + csize = -chunksize; + * data = new [csize]; + hd = ch = new MChunk(data, chunksize, lo, fnc, fnc); + } +} + + +MPlex:: MPlex(int l, int chunksize) +{ + if (chunksize == 0) error("invalid constructor specification"); + unused = 0; + lo = fnc = l; + if (chunksize > 0) + { + csize = chunksize; + * data = new [csize]; + hd = ch = new MChunk(data, lo, lo, fnc, csize+lo); + } + else + { + csize = -chunksize; + * data = new [csize]; + hd = ch = new MChunk(data, chunksize+lo, lo, fnc, fnc); + } +} + + +void MPlex::make_initial_chunks(int up) +{ + int need = fnc - lo; + hd = 0; + if (up) + { + int l = lo; + do + { + int sz; + if (need >= csize) + sz = csize; + else + sz = need; + * data = new [csize]; + MChunk* h = new MChunk(data, l, l, l+sz, l+csize); + if (hd != 0) + h->link_to_next(hd); + else + hd = h; + l += sz; + need -= sz; + } while (need > 0); + } + else + { + int hi = fnc; + do + { + int sz; + if (need >= csize) + sz = csize; + else + sz = need; + * data = new [csize]; + MChunk* h = new MChunk(data, hi-csize, hi-sz, hi, hi); + if (hd != 0) + h->link_to_next(hd); + hd = h; + hi -= sz; + need -= sz; + } while (need > 0); + } + ch = (MChunk*) hd; +} + +MPlex:: MPlex(int l, int hi, const initval, int chunksize) +{ + lo = l; + fnc = hi + 1; + if (chunksize == 0) + { + csize = fnc - l; + make_initial_chunks(1); + } + else if (chunksize < 0) + { + csize = -chunksize; + make_initial_chunks(0); + } + else + { + csize = chunksize; + make_initial_chunks(1); + } + unused = fnc - lo; + for (int i=lo; iMPlex::MPlex(const MPlex& a) +{ + lo = a.lo; + fnc = a.fnc; + csize = a.csize; + unused = fnc - lo; + hd = 0; + const IChunk* p = a.hd; + do + { + * data = new [p->size()]; + MChunk* h = new MChunk(data, p->base_index(), + p->low_index(), p->fence_index(), p->top_index()); + if (hd != 0) + h->link_to_next(hd); + else + hd = h; + p = p->next(); + } while (p != a.hd); + ch = (MChunk*) hd; + for (int i = a.low(); i < a.fence(); a.next(i)) + { + undel_index(i); + (*this)[i] = a[i]; + } +} + +void MPlex::operator= (const MPlex& a) +{ + if (&a != this) + { + invalidate(); + lo = a.lo; + fnc = a.fnc; + csize = a.csize; + unused = fnc - lo; + hd = 0; + const IChunk* p = a.hd; + do + { + * data = new [p->size()]; + MChunk* h = new MChunk(data, p->base_index(), + p->low_index(), p->fence_index(), + p->top_index()); + if (hd != 0) + h->link_to_next(hd); + else + hd = h; + p = p->next(); + } while (p != a.hd); + ch = (MChunk*) hd; + for (int i = a.low(); i < a.fence(); a.next(i)) + { + undel_index(i); + (*this)[i] = a[i]; + } + } +} + +int MPlex::valid(int idx) const +{ + const MChunk* tail = (MChunk*)tl(); + const MChunk* t = ch; + while (idx >= t->fence_index()) + { + if (t == tail) return 0; + t = ((MChunk*)(t->next())); + } + while (idx < t->low_index()) + { + if (t == (MChunk*)(hd)) return 0; + t = ((MChunk*)(t->prev())); + } + set_cache(t); + return t->MChunk::valid_index(idx); +} + +void MPlex::cache(int idx) const +{ + const MChunk* tail = (MChunk*)tl(); + const MChunk* t = ch; + while (idx >= t->fence_index()) + { + if (t == tail) index_error(); + t = ((MChunk*)(t->next())); + } + while (idx < t->low_index()) + { + if (t == (MChunk*)hd) index_error(); + t = ((MChunk*)(t->prev())); + } + if (!t->MChunk::valid_index(idx)) index_error(); + set_cache(t); +} + +void MPlex::cache(const * p) const +{ + const MChunk* old = ch; + const MChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = ((MChunk*)(t->next())); + if (t == old) index_error(); + } + if (!t->MChunk::valid_pointer(p)) index_error(); + set_cache(t); +} + +int MPlex::owns(Pix px) const +{ + * p = (*)px; + const MChunk* old = ch; + const MChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = ((MChunk*)(t->next())); + if (t == old) return 0; + } + set_cache(t); + return t->MChunk::valid_pointer(p); +} + +int MPlex::add_high(const elem) +{ + MChunk* t = ((MChunk*) tl()); + + if (!t->can_grow_high()) + { + * data = new [csize]; + t = (new MChunk(data, fnc,fnc,fnc,fnc+csize)); + t->link_to_prev(tl()); + } + + *((t->MChunk::grow_high())) = elem; + set_cache(t); + return fnc++; +} + +int MPlex::add_low (const elem) +{ + MChunk* t = ((MChunk*) hd); + if (!t->can_grow_low()) + { + * data = new [csize]; + hd = new MChunk(data, lo-csize, lo, lo, lo); + hd->link_to_next(t); + t = ((MChunk*) hd); + } + + *((t->MChunk::grow_low())) = elem; + set_cache(t); + return --lo; +} + +void MPlex::adjust_bounds() +{ + MChunk* t = ((MChunk*) tl()); + + // clean up tail + + t->reset_high(); + while (t->MChunk::empty() && !one_chunk()) + { + MChunk* pred = (MChunk*)(t->prev()); + del_chunk(t); + pred->reset_high(); + t = (pred); + } + if (one_chunk()) + t->reset_high(); + + int oldfnc = fnc; + fnc = t->fence_index(); + unused -= oldfnc - fnc; + + // and head.. + t = ((MChunk*) hd); + t->reset_low(); + while (t->MChunk::empty() && !one_chunk()) + { + hd = (MChunk*)(t->next()); + del_chunk(t); + t = ((MChunk*) hd); + t->reset_low(); + } + + int oldlo = lo; + lo = t->low_index(); + unused -= lo - oldlo; + + + set_cache(t); +} + +int MPlex::del_high () +{ + if (empty()) empty_error(); + MChunk* t = ((MChunk*) tl()); + while (t->MChunk::empty() && !one_chunk()) // possible stragglers + { + MChunk* pred = (MChunk*)(t->prev()); + del_chunk(t); + pred->reset_high(); + t = (pred); + } + t->MChunk::shrink_high(); + while (t->MChunk::empty() && !one_chunk()) + { + MChunk* pred = (MChunk*)(t->prev()); + del_chunk(t); + pred->reset_high(); + t = (pred); + } + int oldfnc = fnc; + fnc = t->fence_index(); + unused -= oldfnc - fnc - 1; + set_cache(t); + return fnc - 1; +} + +int MPlex::del_low () +{ + if (empty()) empty_error(); + MChunk* t = ((MChunk*) hd); + while (t->MChunk::empty() && !one_chunk()) + { + hd = (MChunk*)(t->next()); + del_chunk(t); + t = ((MChunk*) hd); + t->reset_low(); + } + t->MChunk::shrink_low(); + while (t->MChunk::empty() && !one_chunk()) + { + hd = (MChunk*)(t->next()); + del_chunk(t); + t = ((MChunk*) hd); + t->reset_low(); + } + int oldlo = lo; + lo = t->low_index(); + unused -= lo - oldlo - 1; + set_cache(t); + return lo; +} + +int MPlex::add(const elem) +{ + if (unused == 0) + return add_high(elem); + + for(MChunk* t = ch; + t->unused_indices() == 0; + t = (MChunk*)(t->prev())) + ; + + int i = t->unused_index(); + set_cache(t); + undel_index(i); + (*this)[i] = elem; + return i; +} + +int MPlex::unused_index() const +{ + if (unused == 0) index_error(); + + for(MChunk* t = ch; + t->unused_indices() == 0; + t = (MChunk*)(t->prev())) + ; + + set_cache(t); + return t->unused_index(); +} + +Pix MPlex::unused_Pix() const +{ + if (unused == 0) return 0; + + for(MChunk* t = ch; + t->unused_indices() == 0; + t = (MChunk*)(t->prev())) + ; + + set_cache(t); + return t->pointer_to(t->unused_index()); +} + +int MPlex::del_index(int idx) +{ + if (idx < lo || idx >= fnc) index_error(); + if (MPlex::valid(idx)) + { + ++unused; + ch->MChunk::del(idx); + return 1; + } + else + return 0; +} + +int MPlex::dopred(int idx) const +{ + + if (idx >= fnc) idx = fnc; + if (idx <= lo) return lo - 1; + + const MChunk* t = ch; + + while (idx > t->fence_index()) + { + t = ((MChunk*)(t->next())); + } + while (idx <= t->low_index()) + { + t = ((MChunk*)(t->prev())); + } + int i = t->MChunk::pred(idx); + while (i < t->low_index() && i >= lo) + { + t = ((MChunk*)(t->prev())); + i = t->MChunk::last_index(); + } + set_cache(t); + return i; +} + + +int MPlex::dosucc(int idx) const +{ + if (idx < lo) idx = lo; + if (idx >= fnc - 1) return fnc; + + const MChunk* t = ch; + while (idx >= t->fence_index()) + { + t = ((MChunk*)(t->next())); + } + while (idx < t->low_index()) + { + t = ((MChunk*)(t->prev())); + } + int i = t->MChunk::succ(idx); + while (i >= t->fence_index() && i < fnc) + { + t = (MChunk*)(t->next()); + i = t->MChunk::first_index(); + } + set_cache(t); + return i; +} + +void MPlex::prev(Pix& i) const +{ + if (i == 0) return; + + * p = (*) i; + const MChunk* old = ch; + const MChunk* t = ch; + + while (!t->actual_pointer(p)) + { + t = ((MChunk*)(t->prev())); + if (t == old) + { + i = 0; + return; + } + } + * q = t->MChunk::pred(p); + while (q == 0 && t != (MChunk*)hd) + { + t = ((MChunk*)(t->prev())); + q = t->MChunk::last_pointer(); + } + + i = Pix(q); + set_cache(t); + return; +} + +void MPlex::next(Pix& i) const +{ + if (i == 0) return; + + * p = (*) i; + const MChunk* tail = (MChunk*)(tl()); + const MChunk* old = ch; + const MChunk* t = ch; + + while (!t->actual_pointer(p)) + { + t = ((MChunk*)(t->next())); + if (t == old) + { + i = 0; + return; + } + } + * q = t->MChunk::succ(p); + while (q == 0 && t != tail) + { + t = ((MChunk*)(t->next())); + q = t->MChunk::first_pointer(); + } + + i = Pix(q); + set_cache(t); + return; +} + + +void MPlex::undel_index(int idx) +{ + if (idx < lo || idx >= fnc) index_error(); + + MChunk* t = ch; + while (idx >= t->fence_index()) + { + t = ((MChunk*)(t->next())); + } + while (idx < t->low_index()) + { + t = ((MChunk*)(t->prev())); + } + int was_present = t->MChunk::undel(idx); + if (!was_present) + { + --unused; + } + set_cache(t); + return; +} + +void MPlex::clear() +{ + if (fnc != lo) + { + MChunk* t = ((MChunk*)tl()); + while (t != hd) + { + MChunk* prv = (MChunk*)(t->prev()); + del_chunk(t); + t = prv; + } + t->MChunk::clear(lo); + set_cache(t); + fnc = lo; + unused = 0; + } +} + +int MPlex::OK () const +{ + int v = hd != 0; // at least one chunk + + int found_ch = 0; // to make sure ch is in list; + + int count = 0; // to count unused slots + + const MChunk* t = (MChunk*)(hd); + + int gap = t->low_index() - lo; + v &= gap == 0; // hd lo not less than lo. + count += gap; + + for (;;) + { + if (t == ch) ++found_ch; + v &= t->MChunk::OK(); // each chunk is OK + count += t->unused_indices(); + if (t == (MChunk*)(tl())) + break; + else // and has indices less than succ + { + gap = t->next()->base_index() - t->top_index(); + v &= gap == 0; + count += gap; + + if (t != (MChunk*)hd) // internal chunks can't grow + v &= !t->can_grow_low() && !t->can_grow_high(); + + t = (const MChunk*)(t->next()); + } + } + gap = fnc - t->fence_index(); + v &= gap == 0; + count += gap; + + v &= count == unused; // chunk counts agree with plex + + v &= found_ch == 1; + if (!v) error("invariant failure"); + return v; +} + diff --git a/gnu/lib/libg++/g++-include/MPlex.hP b/gnu/lib/libg++/g++-include/MPlex.hP new file mode 100644 index 0000000000..8bf78d13a1 --- /dev/null +++ b/gnu/lib/libg++/g++-include/MPlex.hP @@ -0,0 +1,414 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _MPlex_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _MPlex_h 1 + + +#include ".Plex.h" + + +// Number of bits per long, used in MChunk bit map operations + +#define _MAP_BITS 32 + + +class MChunk : public IChunk +{ +protected: + + unsigned long* map; // bitmap of slots + int unused; // number of unused internal slots + + void mark(int); // bitmap operations + void free(int); + int valid(int) const; + +public: + + MChunk(* d, // ptr to array of elements + int base_idx, // initial indices + int low_idx, // & initially clear map + int fence_idx, + int top_idx); + + ~MChunk(); + +// virtuals + + int first_index() const; + int last_index() const; + int succ(int idx) const; + int pred(int idx) const; + * first_pointer() const; + * last_pointer() const; + * succ(*) const; + * pred(*) const; + int empty() const; + int full() const; + int valid_index(int i) const; + int valid_pointer(const * p) const; + * grow_high (); + * grow_low (); + void shrink_high (); + void shrink_low (); + void clear(int); + void cleardown(int); + int OK() const; + +// extensions + + int unused_indices() const; // how many free slot in low..fence? + + int unused_index() const; // return index of free slot + + int del(int i); // delete data indexed by i + // return true if was present + int undel(int idx); // un-delete data indexed by i + // return true if already present + + void reset_low(); // reset low = lowest valid index; + void reset_high(); // same for high + +}; + + +class MPlex: public Plex +{ + MChunk* ch; // cached chunk + int unused; // # of free slots between low & fence + + void make_initial_chunks(int up = 1); + void cache(int idx) const; + void cache(const * p) const; + int dopred(int) const; + int dosucc(int) const; + + void set_cache(const MChunk* t) const; // logically, + // not physically const + +public: + MPlex(); // set low = 0; + // fence = 0; + // csize = default + + MPlex(int ch_size); // low = 0; + // fence = 0; + // csize = ch_size + + MPlex(int lo, // low = lo; + int ch_size); // fence=lo + // csize = ch_size + + MPlex(int lo, // low = lo + int hi, // fence = hi+1 + const initval,// fill with initval, + int ch_size = 0); // csize= ch_size + // or fence-lo if 0 + + MPlex(const MPlex&); + + void operator= (const MPlex&); + +// virtuals + + & high_element (); + & low_element (); + const & high_element () const; + const & low_element () const; + + Pix first() const; + Pix last() const ; + void prev(Pix& ptr) const; + void next(Pix& ptr) const; + int owns(Pix p) const; + & operator () (Pix p); + const & operator () (Pix p) const; + + int low() const; + int high() const; + int valid(int idx) const; + void prev(int& idx) const; + void next(int& x) const; + & operator [] (int index); + const & operator [] (int index) const; + + int Pix_to_index(Pix p) const; + Pix index_to_Pix(int idx) const; + + int can_add_high() const; + int can_add_low() const; + int full() const; + + int add_high(const elem); + int del_high (); + int add_low (const elem); + int del_low (); + void clear(); + + int OK () const; + +// extensions + + int count() const; // # valid elements + int available() const; // # deleted elements + + int unused_index()const; // return index of a deleted elem + Pix unused_Pix() const; // return Pix of a deleted elem + + int del_index(int idx); // logically delete at idx; + // return true if was present + int del_Pix(Pix p); // delete at p + + void undel_index(int idx); // undelete at idx; + void undel_Pix(Pix p); // undelete at p; + + void adjust_bounds(); // reset lo, hi to lowest & + // highest valid indices + + int add(const elem); // add anywhere +}; + + +inline MChunk:: ~MChunk() +{ + delete map; +} + +inline void MChunk::mark(int idx) +{ + unsigned int i = idx - base; + map[i / _MAP_BITS] |= 1 << (i & (_MAP_BITS - 1)); +} + +inline void MChunk::free(int idx) +{ + unsigned int i = idx - base; + map[i / _MAP_BITS] &= ~(1 << (i & (_MAP_BITS - 1))); +} + +inline int MChunk::valid(int idx) const +{ + unsigned int i = idx - base; + return map[i / _MAP_BITS] & (1 << (i & (_MAP_BITS - 1))); +} + +inline int MChunk:: valid_index(int i) const +{ + return i >= low && i < fence && valid(i); +} + +inline int MChunk:: valid_pointer(const * p) const +{ + int i = ((int)p - (int)data) / sizeof(); + return i >= 0 && i < (fence - base) && + (map[(unsigned)i / _MAP_BITS] & (1 << (i & (_MAP_BITS - 1)))); +} + +inline int MChunk::empty() const +{ + return fence - low - unused == 0; +} + +inline int MChunk::full() const +{ + return unused + (top - fence) + (low - base) == 0; +} + +inline int MChunk::succ(int idx) const +{ + int i = (idx < low)? low : idx + 1; + while (i < fence && !valid(i)) ++i; + return i; +} + +inline int MChunk::pred(int idx) const +{ + int i = (idx > fence)? (fence - 1) : idx - 1; + while (i >= low && !valid(i)) --i; + return i; +} + +inline int MChunk::unused_indices() const +{ + return unused; +} + +inline * MChunk:: grow_high () +{ + if (!can_grow_high()) full_error(); + mark(fence); + return &(data[fence++ - base]); +} + +inline * MChunk:: grow_low () +{ + if (!can_grow_low()) full_error(); + mark(--low); + return &(data[low - base]); +} + +inline void MChunk::reset_low() +{ + while (low < fence && !valid(low)) + { + --unused; + ++low; + } +} + +inline void MChunk::reset_high() +{ + while (fence > low && !valid(fence - 1)) + { + --unused; + --fence; + } +} + +inline int MPlex::full () const +{ + return 0; +} + +inline int MPlex::can_add_high() const +{ + return 1; +} + +inline int MPlex::can_add_low() const +{ + return 1; +} + +inline int MPlex::available() const +{ + return unused; +} + +inline int MPlex::count() const +{ + return fnc - lo - unused; +} + +inline void MPlex::set_cache(const MChunk* t) const +{ + ((MPlex*)(this))->ch = (MChunk*)t; +} + +inline & MPlex:: operator [] (int idx) +{ + if (!ch->MChunk::valid_index(idx)) cache(idx); + return * (ch->pointer_to(idx)); +} + +inline const & MPlex:: operator [] (int idx) const +{ + if (!ch->MChunk::valid_index(idx)) cache(idx); + return * ((const *)(ch->pointer_to(idx))); +} + +inline int MPlex::Pix_to_index(Pix p) const +{ + if (!ch->MChunk::valid_pointer((*)p)) cache((*)p); + return ch->index_of((*)p); +} + +inline int MPlex::high() const +{ + return (((const MChunk*)tl())->MChunk::valid_index(fnc-1)) ? + fnc-1 : dopred(fnc-1); +} + +inline int MPlex::low() const +{ + return (((const MChunk*)hd)->MChunk::valid_index(lo))? lo : dosucc(lo); +} + +inline & MPlex::low_element () +{ + return (*this)[low()]; +} + +inline const & MPlex::low_element () const +{ + return (*this)[low()]; +} + +inline & MPlex::high_element () +{ + return (*this)[high()]; +} + +inline const & MPlex::high_element () const +{ + return (*this)[high()]; +} + +inline Pix MPlex::index_to_Pix(int idx) const +{ + if (!ch->MChunk::valid_index(idx)) cache(idx); + return Pix(ch->pointer_to(idx)); +} + +inline void MPlex::next(int& idx) const +{ + idx = (ch->MChunk::valid_index(idx+1))? idx+1 : dosucc(idx); +} + +inline void MPlex::prev(int& idx) const +{ + idx = (ch->MChunk::valid_index(idx-1))? idx-1 : dopred(idx); +} + +inline Pix MPlex::first() const +{ + return index_to_Pix(low()); +} + +inline Pix MPlex::last() const +{ + return index_to_Pix(high()); +} + + +inline void MPlex::undel_Pix(Pix p) +{ + undel_index(Pix_to_index(p)); +} + +inline int MPlex::del_Pix(Pix p) +{ + return del_index(Pix_to_index(p)); +} + +inline & MPlex:: operator () (Pix p) +{ + return *((*)p); +} + +inline const & MPlex:: operator () (Pix p) const +{ + return *((const *)p); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/Makefile b/gnu/lib/libg++/g++-include/Makefile deleted file mode 100644 index 115f619ec1..0000000000 --- a/gnu/lib/libg++/g++-include/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -# %W% (Berkeley) %G% -# -# Cloned from /usr/src/include/Makefile -# Doing a make install builds /usr/include/g++ -# - -all clean cleandir depend lint tags: - -FILES= ACG.h AllocRing.h Binomial.h BitSet.h BitString.h \ - Complex.h CursesW.h DiscUnif.h Erlang.h File.h \ - Filebuf.h Fix.h Fix16.h Fix24.h Fmodes.h Geom.h \ - GetOpt.h HypGeom.h Incremental.h Integer.h LogNorm.h \ - MLCG.h NegExp.h Normal.h Obstack.h Pix.h PlotFile.h \ - Poisson.h RNG.h Random.h Rational.h Regex.h RndInt.h \ - SFile.h SmplHist.h SmplStat.h String.h Uniform.h \ - Weibull.h abs.h assert.h bool.h builtin.h compare.h \ - complex.h curses.h file.h filebuf.h generic.h \ - istream.h malloc.h max.h min.h minmax.h new.h open.h \ - osfcn.h ostream.h regex.h std.h strclass.h stream.h \ - streambuf.h swap.h values.h - -DIRS= gen - -NOOBJ= noobj - -INCDIR= /usr/include/g++ - -install: - @echo installing ${FILES} - @-for i in ${FILES}; do \ - cmp -s $$i ${DESTDIR}${INCDIR}/$$i || \ - install -c -o ${BINOWN} -g ${BINGRP} -m 444 $$i \ - ${DESTDIR}${INCDIR}/$$i; \ - done - @echo installing ${DIRS} - @-for i in ${DIRS}; do \ - if [ ! -d ${DESTDIR}${INCDIR}/$$i ]; \ - then \ - mkdir ${DESTDIR}${INCDIR}/$$i; \ - fi; \ - chown ${BINOWN}.${BINGRP} ${DESTDIR}${INCDIR}/$$i; \ - chmod 755 ${DESTDIR}${INCDIR}/$$i; \ - (cd $$i; for j in *.*P; do \ - cmp -s $$j ${DESTDIR}${INCDIR}/$$i/$$j || \ - install -c -o ${BINOWN} -g ${BINGRP} -m 444 $$j \ - ${DESTDIR}${INCDIR}/$$i/$$j; \ - done); \ - done - -.include diff --git a/gnu/lib/libg++/g++-include/Map.ccP b/gnu/lib/libg++/g++-include/Map.ccP new file mode 100644 index 0000000000..03bb4b84f0 --- /dev/null +++ b/gnu/lib/libg++/g++-include/Map.ccP @@ -0,0 +1,58 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include "..Map.h" + + +Pix Map::seek( item) +{ + for (Pix i = first(); i != 0 && !(EQ(key(i), item)); next(i)); + return i; +} + +int Map::owns(Pix idx) +{ + if (idx == 0) return 0; + for (Pix i = first(); i; next(i)) if (i == idx) return 1; + return 0; +} + +void Map::clear() +{ + Pix i = first(); + while (i != 0) + { + del(key(i)); + i = first(); + } +} + +int Map::contains ( item) +{ + return seek(item) != 0; +} + + +void Map::error(const char* msg) +{ + (*lib_error_handler)("Map", msg); +} diff --git a/gnu/lib/libg++/g++-include/Map.hP b/gnu/lib/libg++/g++-include/Map.hP new file mode 100644 index 0000000000..716a17fdd9 --- /dev/null +++ b/gnu/lib/libg++/g++-include/Map.hP @@ -0,0 +1,87 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _Map_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Map_h 1 + +#include +#include ".defs.h" + +class Map +{ +protected: + int count; + def; + +public: + Map( dflt); + virtual ~Map(); + + int length(); // current number of items + int empty(); + + virtual int contains( key); // is key mapped? + + virtual void clear(); // delete all items + + virtual & operator [] ( key) = 0; // access contents by key + + virtual void del( key) = 0; // delete entry + + virtual Pix first() = 0; // Pix of first item or 0 + virtual void next(Pix& i) = 0; // advance to next or 0 + virtual & key(Pix i) = 0; // access key at i + virtual & contents(Pix i) = 0; // access contents at i + + virtual int owns(Pix i); // is i a valid Pix ? + virtual Pix seek( key); // Pix of key + + & dflt(); // access default val + + void error(const char* msg); + virtual int OK() = 0; // rep invariant +}; + + +inline Map::~Map() {} + +inline int Map::length() +{ + return count; +} + +inline int Map::empty() +{ + return count == 0; +} + +inline & Map::dflt() +{ + return def; +} + +inline Map::Map( dflt) :def(dflt) +{ + count = 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/NegExp.h b/gnu/lib/libg++/g++-include/NegExp.h deleted file mode 100644 index 21a1a18f23..0000000000 --- a/gnu/lib/libg++/g++-include/NegExp.h +++ /dev/null @@ -1,63 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ -#ifndef _NegativeExpntl_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _NegativeExpntl_h 1 - - -// -// Negative Exponential Random Numbers -// -// - -#include - -class NegativeExpntl: public Random { -protected: - double pMean; -public: - NegativeExpntl(double xmean, RNG *gen); - double mean(); - double mean(double x); - - virtual double operator()(); -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline NegativeExpntl::NegativeExpntl(double xmean, RNG *gen) -: (gen) { - pMean = xmean; -} - -inline double NegativeExpntl::mean() { return pMean; } -inline double NegativeExpntl::mean(double x) { - double t = pMean; pMean = x; - return t; -} - -#endif -#endif diff --git a/gnu/lib/libg++/g++-include/Normal.h b/gnu/lib/libg++/g++-include/Normal.h deleted file mode 100644 index b8a209eba7..0000000000 --- a/gnu/lib/libg++/g++-include/Normal.h +++ /dev/null @@ -1,74 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ -#ifndef _Normal_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Normal_h - -#include - -class Normal: public Random { - char haveCachedNormal; - double cachedNormal; - -protected: - double pMean; - double pVariance; - double pStdDev; - -public: - Normal(double xmean, double xvariance, RNG *gen); - double mean(); - double mean(double x); - double variance(); - double variance(double x); - virtual double operator()(); -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline Normal::Normal(double xmean, double xvariance, RNG *gen) -: (gen) { - pMean = xmean; - pVariance = xvariance; - pStdDev = sqrt(pVariance); - haveCachedNormal = 0; -} - -inline double Normal::mean() { return pMean; }; -inline double Normal::mean(double x) { - double t=pMean; pMean = x; - return t; -} - -inline double Normal::variance() { return pVariance; } -inline double Normal::variance(double x) { - double t=pVariance; pVariance = x; - pStdDev = sqrt(pVariance); - return t; -}; - -#endif -#endif diff --git a/gnu/lib/libg++/g++-include/OSLBag.ccP b/gnu/lib/libg++/g++-include/OSLBag.ccP new file mode 100644 index 0000000000..78398192bc --- /dev/null +++ b/gnu/lib/libg++/g++-include/OSLBag.ccP @@ -0,0 +1,196 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".OSLBag.h" + + +Pix OSLBag::seek( item, Pix i) +{ + if (i == 0) i = p.first(); else next(i); + for (; i != 0; p.next(i)) + { + int cmp = CMP(item, p(i)); + if (cmp == 0) + return i; + else if (cmp < 0) + return 0; + } + return 0; +} + +int OSLBag::nof( item) +{ + int n = 0; + for (Pix i = p.first(); i != 0; p.next(i)) + { + int cmp = CMP(item, p(i)); + if (cmp == 0) + ++n; + else if (cmp < 0) + break; + } + return n; +} + +Pix OSLBag::add( item) +{ + Pix i = p.first(); + if (i == 0) + { + ++count; + return p.prepend(item); + } + int cmp = CMP(item, p(i)); + if (cmp <= 0) + { + ++count; + return p.prepend(item); + } + else + { + Pix trail = i; + p.next(i); + for (;;) + { + if (i == 0) + { + ++count; + return p.append(item); + } + cmp = CMP(item, p(i)); + if (cmp <= 0) + { + ++count; + return p.ins_after(trail, item); + } + else + { + trail = i; + p.next(i); + } + } + } +} + +void OSLBag::del( item) +{ + Pix i = p.first(); + if (i == 0) + return; + int cmp = CMP(item, p(i)); + if (cmp < 0) + return; + else if (cmp == 0) + { + --count; + p.del_front(); + } + else + { + Pix trail = i; + p.next(i); + while (i != 0) + { + cmp = CMP(item, p(i)); + if (cmp < 0) + return; + else if (cmp == 0) + { + --count; + p.del_after(trail); + return; + } + else + { + trail = i; + p.next(i); + } + } + } +} + +void OSLBag::remove( item) +{ + Pix i = p.first(); + if (i == 0) + return; + int cmp = CMP(item, p(i)); + if (cmp < 0) + return; + else if (cmp == 0) + { + do + { + --count; + p.del_front(); + i = p.first(); + } while (i != 0 && EQ(item, p(i))); + } + else + { + Pix trail = i; + p.next(i); + while (i != 0) + { + cmp = CMP(item, p(i)); + if (cmp < 0) + return; + else if (cmp == 0) + { + do + { + --count; + p.del_after(trail); + i = trail; + next(i); + } while (i != 0 && EQ(item, p(i))); + return; + } + else + { + trail = i; + p.next(i); + } + } + } +} + +int OSLBag::OK() +{ + int v = p.OK(); + v &= count == p.length(); + Pix trail = p.first(); + if (trail == 0) + v &= count == 0; + else + { + Pix i = trail; next(i); + while (i != 0) + { + v &= CMP(p(trail), p(i)) <= 0; + trail = i; + next(i); + } + } + if (!v) error("invariant failure"); + return v; +} + diff --git a/gnu/lib/libg++/g++-include/OSLBag.hP b/gnu/lib/libg++/g++-include/OSLBag.hP new file mode 100644 index 0000000000..de4d67cf9a --- /dev/null +++ b/gnu/lib/libg++/g++-include/OSLBag.hP @@ -0,0 +1,91 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _OSLBag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _OSLBag_h 1 + +#include ".Bag.h" +#include ".SLList.h" + +class OSLBag : public Bag +{ +protected: + SLList p; + +public: + OSLBag(); + OSLBag(const OSLBag&); + + Pix add( item); + void del( item); + void remove(item); + + int contains( item); + int nof( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item, Pix from = 0); + + int OK(); +}; + + +inline OSLBag::OSLBag() : p() { count = 0; } + +inline OSLBag::OSLBag(const OSLBag& s) : p(s.p) { count = s.count; } + +inline Pix OSLBag::first() +{ + return p.first(); +} + +inline void OSLBag::next(Pix & idx) +{ + p.next(idx); +} + +inline & OSLBag::operator ()(Pix idx) +{ + return p(idx); +} + +inline void OSLBag::clear() +{ + count = 0; p.clear(); +} + +inline int OSLBag::owns (Pix idx) +{ + return p.owns(idx); +} + +inline int OSLBag::contains( item) +{ + return seek(item) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/OSLSet.ccP b/gnu/lib/libg++/g++-include/OSLSet.ccP new file mode 100644 index 0000000000..bfd32ae954 --- /dev/null +++ b/gnu/lib/libg++/g++-include/OSLSet.ccP @@ -0,0 +1,321 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".OSLSet.h" + + +Pix OSLSet::seek( item) +{ + for (Pix i = p.first(); i != 0; p.next(i)) + { + int cmp = CMP(item, p(i)); + if (cmp == 0) + return i; + else if (cmp < 0) + return 0; + } + return 0; +} + +Pix OSLSet::add( item) +{ + Pix i = p.first(); + if (i == 0) + { + ++count; + return p.prepend(item); + } + int cmp = CMP(item, p(i)); + if (cmp == 0) + return i; + else if (cmp < 0) + { + ++count; + return p.prepend(item); + } + else + { + Pix trail = i; + p.next(i); + for (;;) + { + if (i == 0) + { + ++count; + return p.append(item); + } + cmp = CMP(item, p(i)); + if (cmp == 0) + return i; + else if (cmp < 0) + { + ++count; + return p.ins_after(trail, item); + } + else + { + trail = i; + p.next(i); + } + } + } +} + +void OSLSet::del( item) +{ + Pix i = p.first(); + if (i == 0) + return; + int cmp = CMP(item, p(i)); + if (cmp < 0) + return; + else if (cmp == 0) + { + --count; + p.del_front(); + } + else + { + Pix trail = i; + p.next(i); + while (i != 0) + { + cmp = CMP(item, p(i)); + if (cmp < 0) + return; + else if (cmp == 0) + { + --count; + p.del_after(trail); + return; + } + else + { + trail = i; + p.next(i); + } + } + } +} + + +int OSLSet::operator <= (OSLSet& b) +{ + if (count > b.count) return 0; + Pix i = first(); + Pix j = b.first(); + for (;;) + { + if (i == 0) + return 1; + else if (j == 0) + return 0; + int cmp = CMP(p(i), b.p(j)); + if (cmp == 0) + { + next(i); b.next(j); + } + else if (cmp < 0) + return 0; + else + b.next(j); + } +} + +int OSLSet::operator == (OSLSet& b) +{ + if (count != b.count) return 0; + if (count == 0) return 1; + Pix i = p.first(); + Pix j = b.p.first(); + while (i != 0) + { + if (!EQ(p(i),b.p(j))) return 0; + next(i); + b.next(j); + } + return 1; +} + + +void OSLSet::operator |= (OSLSet& b) +{ + if (&b == this || b.count == 0) + return; + else + { + Pix j = b.p.first(); + Pix i = p.first(); + Pix trail = 0; + for (;;) + { + if (j == 0) + return; + else if (i == 0) + { + for (; j != 0; b.next(j)) + { + ++count; + p.append(b.p(j)); + } + return; + } + int cmp = CMP(p(i), b.p(j)); + if (cmp <= 0) + { + if (cmp == 0) b.next(j); + trail = i; + next(i); + } + else + { + ++count; + if (trail == 0) + trail = p.prepend(b.p(j)); + else + trail = p.ins_after(trail, b.p(j)); + b.next(j); + } + } + } +} + + +void OSLSet::operator -= (OSLSet& b) +{ + if (&b == this) + clear(); + else if (count != 0 && b.count != 0) + { + Pix i = p.first(); + Pix j = b.p.first(); + Pix trail = 0; + for (;;) + { + if (j == 0 || i == 0) + return; + int cmp = CMP(p(i), b.p(j)); + if (cmp == 0) + { + --count; + b.next(j); + if (trail == 0) + { + p.del_front(); + i = p.first(); + } + else + { + next(i); + p.del_after(trail); + } + } + else if (cmp < 0) + { + trail = i; + next(i); + } + else + b.next(j); + } + } +} + +void OSLSet::operator &= (OSLSet& b) +{ + if (b.count == 0) + clear(); + else if (&b != this && count != 0) + { + Pix i = p.first(); + Pix j = b.p.first(); + Pix trail = 0; + for (;;) + { + if (i == 0) + return; + else if (j == 0) + { + if (trail == 0) + { + p.clear(); + count = 0; + } + else + { + while (i != 0) + { + --count; + next(i); + p.del_after(trail); + } + } + return; + } + int cmp = CMP(p(i), b.p(j)); + + if (cmp == 0) + { + trail = i; + next(i); + b.next(j); + } + else if (cmp < 0) + { + --count; + if (trail == 0) + { + p.del_front(); + i = p.first(); + } + else + { + next(i); + p.del_after(trail); + } + } + else + b.next(j); + } + } +} + + +int OSLSet::OK() +{ + int v = p.OK(); + v &= count == p.length(); + Pix trail = p.first(); + if (trail == 0) + v &= count == 0; + else + { + Pix i = trail; next(i); + while (i != 0) + { + v &= CMP(p(trail), p(i)) < 0; + trail = i; + next(i); + } + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/OSLSet.hP b/gnu/lib/libg++/g++-include/OSLSet.hP new file mode 100644 index 0000000000..bf3707f6c7 --- /dev/null +++ b/gnu/lib/libg++/g++-include/OSLSet.hP @@ -0,0 +1,101 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _OSLSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _OSLSet_h 1 + +#include ".Set.h" +#include ".SLList.h" + +class OSLSet : public Set +{ +protected: + SLList p; + +public: + OSLSet(); + OSLSet(const OSLSet&); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item); + + void operator |= (OSLSet& b); + void operator -= (OSLSet& b); + void operator &= (OSLSet& b); + + int operator == (OSLSet& b); + int operator != (OSLSet& b); + int operator <= (OSLSet& b); + + int OK(); +}; + + +inline OSLSet::OSLSet() : p() { count = 0; } + +inline OSLSet::OSLSet(const OSLSet& s) : p(s.p) { count = s.count; } + +inline Pix OSLSet::first() +{ + return p.first(); +} + +inline void OSLSet::next(Pix & idx) +{ + p.next(idx); +} + +inline & OSLSet::operator ()(Pix idx) +{ + return p(idx); +} + +inline void OSLSet::clear() +{ + count = 0; p.clear(); +} + +inline int OSLSet::contains ( item) +{ + return seek(item) != 0; +} + +inline int OSLSet::owns (Pix idx) +{ + return p.owns(idx); +} + +inline int OSLSet::operator != (OSLSet& b) +{ + return !(*this == b); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/OXPBag.ccP b/gnu/lib/libg++/g++-include/OXPBag.ccP new file mode 100644 index 0000000000..6619e25ea1 --- /dev/null +++ b/gnu/lib/libg++/g++-include/OXPBag.ccP @@ -0,0 +1,221 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".OXPBag.h" + + +Pix OXPBag::seek( item, Pix i) +{ + if (i == 0) + { + int l = p.low(); + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + int cmp = CMP(item, p[mid]); + if (cmp == 0) + { + while (mid > p.low() && EQ(item, p[mid - 1])) --mid; + return p.index_to_Pix(mid); + } + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } + return 0; + } + int cmp = CMP(item, p(i)); + if (cmp == 0) + { + next(i); + return (EQ(item, p(i)))? i : 0; + } + else if (cmp < 0) + { + int ind = p.Pix_to_index(i); + int l = ind; + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + cmp = CMP(item, p[mid]); + if (cmp == 0) + { + while (mid > ind && EQ(item, p[mid - 1])) --mid; + return p.index_to_Pix(mid); + } + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } + return 0; + } + else + return 0; +} + +int OXPBag::nof( item) +{ + int l = p.low(); + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + int cmp = CMP(item, p[mid]); + if (cmp == 0) + { + l = h = mid; + while (l > p.low() && EQ(item, p[l - 1])) --l; + while (h < p.high() && EQ(item, p[h + 1])) ++h; + return h - l + 1; + } + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } + return 0; +} + +Pix OXPBag::add( item) +{ + if (count == 0) + { + ++count; + return p.index_to_Pix(p.add_high(item)); + } + int l = p.low(); + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + int cmp = CMP(item, p[mid]); + if (cmp == 0) + { + l = mid; + break; + } + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } + // add on whichever side is shortest + ++count; + if (l == p.fence()) + return p.index_to_Pix(p.add_high(item)); + else if (l == p.low()) + return p.index_to_Pix(p.add_low(item)); + else + { + if (p.high() - l < l - p.low()) + { + h = p.add_high(p.high_element()); + for (int i = h - 1; i > l; --i) p[i] = p[i-1]; + } + else + { + --l; + h = p.add_low(p.low_element()); + for (int i = h + 1; i < l; ++i) p[i] = p[i+1]; + } + p[l] = item; + return p.index_to_Pix(l); + } +} + +void OXPBag::del( item) +{ + int l = p.low(); + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + int cmp = CMP(item, p[mid]); + if (cmp == 0) + { + --count; + if (p.high() - mid < mid - p.low()) + { + for (int i = mid; i < p.high(); ++i) p[i] = p[i+1]; + p.del_high(); + } + else + { + for (int i = mid; i > p.low(); --i) p[i] = p[i-1]; + p.del_low(); + } + return; + } + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } +} + +void OXPBag::remove( item) +{ + int l = p.low(); + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + int cmp = CMP(item, p[mid]); + if (cmp == 0) + { + l = h = mid; + while (l > p.low() && EQ(item, p[l - 1])) --l; + while (h < p.high() && EQ(item, p[h + 1])) ++h; + int n = h - l + 1; + count -= n; + if (p.high() - h < l - p.low()) + { + h = p.high() - n; + for (int i = l; i <= h; ++i) p[i] = p[i+n]; + while (n-- > 0) p.del_high(); + } + else + { + l = p.low() + n; + for (int i = h; i >= l; --i) p[i] = p[i-n]; + while (n-- > 0) p.del_low(); + } + return; + } + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } +} + +int OXPBag::OK() +{ + int v = p.OK(); + v &= count == p.length(); + for (int i = p.low(); i < p.high(); ++i) v &= CMP(p[i], p[i+1]) <= 0; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/OXPBag.hP b/gnu/lib/libg++/g++-include/OXPBag.hP new file mode 100644 index 0000000000..128d4a20e4 --- /dev/null +++ b/gnu/lib/libg++/g++-include/OXPBag.hP @@ -0,0 +1,73 @@ +#ifndef _OXPBag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _OXPBag_h 1 + +#include ".Bag.h" +#include ".XPlex.h" + +class OXPBag : public Bag +{ +protected: + XPlex p; + +public: + OXPBag(int chunksize = DEFAULT_INITIAL_CAPACITY); + OXPBag(const OXPBag&); + + Pix add( item); + void del( item); +#undef remove + void remove(item); + int nof( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item, Pix from = 0); + + int OK(); +}; + + +inline OXPBag::OXPBag(int chunksize) + : p(chunksize) { count = 0; } + +inline OXPBag::OXPBag(const OXPBag& s) : p(s.p) { count = s.count; } + +inline Pix OXPBag::first() +{ + return p.first(); +} + +inline void OXPBag::next(Pix & idx) +{ + p.next(idx); +} + +inline & OXPBag::operator ()(Pix idx) +{ + return p(idx); +} + +inline void OXPBag::clear() +{ + count = 0; p.clear(); +} + +inline int OXPBag::owns (Pix idx) +{ + return p.owns(idx); +} + +inline int OXPBag::contains( item) +{ + return seek(item) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/OXPSet.ccP b/gnu/lib/libg++/g++-include/OXPSet.ccP new file mode 100644 index 0000000000..1461195451 --- /dev/null +++ b/gnu/lib/libg++/g++-include/OXPSet.ccP @@ -0,0 +1,280 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".OXPSet.h" + + +Pix OXPSet::seek( item) +{ + int l = p.low(); + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + int cmp = CMP(item, p[mid]); + if (cmp == 0) + return p.index_to_Pix(mid); + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } + return 0; +} + +Pix OXPSet::add( item) +{ + if (count == 0) + { + ++count; + return p.index_to_Pix(p.add_high(item)); + } + int l = p.low(); + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + int cmp = CMP(item, p[mid]); + if (cmp == 0) + return p.index_to_Pix(mid); + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } + // add on whichever side is shortest + ++count; + if (l == p.fence()) + return p.index_to_Pix(p.add_high(item)); + else if (l == p.low()) + return p.index_to_Pix(p.add_low(item)); + else + { + if (p.fence() - l < l - p.low()) + { + h = p.add_high(p.high_element()); + for (int i = h - 1; i > l; --i) p[i] = p[i-1]; + } + else + { + --l; + h = p.add_low(p.low_element()); + for (int i = h + 1; i < l; ++i) p[i] = p[i+1]; + } + p[l] = item; + return p.index_to_Pix(l); + } +} + +void OXPSet::del( item) +{ + int l = p.low(); + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + int cmp = CMP(item, p[mid]); + if (cmp == 0) + { + --count; + if (p.high() - mid < mid - p.low()) + { + for (int i = mid; i < p.high(); ++i) p[i] = p[i+1]; + p.del_high(); + } + else + { + for (int i = mid; i > p.low(); --i) p[i] = p[i-1]; + p.del_low(); + } + return; + } + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } +} + +int OXPSet::operator <= (OXPSet& b) +{ + if (count > b.count) return 0; + int i = p.low(); + int j = b.p.low(); + for (;;) + { + if (i >= p.fence()) + return 1; + else if (j >= b.p.fence()) + return 0; + int cmp = CMP(p[i], b.p[j]); + if (cmp == 0) + { + ++i; ++j; + } + else if (cmp < 0) + return 0; + else + ++j; + } +} + +int OXPSet::operator == (OXPSet& b) +{ + int n = count; + if (n != b.count) return 0; + if (n == 0) return 1; + int i = p.low(); + int j = b.p.low(); + while (n-- > 0) if (!EQ(p[i++], b.p[j++])) return 0; + return 1; +} + + +void OXPSet::operator |= (OXPSet& b) +{ + if (&b == this || b.count == 0) + return; + else if (b.count <= 2) // small b -- just add + for (Pix i = b.first(); i; b.next(i)) add(b(i)); + else + { + // strategy: merge into top of p, simultaneously killing old bottom + int oldfence = p.fence(); + int i = p.low(); + int j = b.p.low(); + for (;;) + { + if (i == oldfence) + { + while (j < b.p.fence()) p.add_high(b.p[j++]); + break; + } + else if (j == b.p.fence()) + { + while (i++ < oldfence) + { + p.add_high(p.low_element()); + p.del_low(); + } + break; + } + int cmp = CMP(p[i], b.p[j]); + if (cmp <= 0) + { + ++i; + if (cmp == 0) ++j; + p.add_high(p.low_element()); + p.del_low(); + } + else + p.add_high(b.p[j++]); + } + count = p.length(); + } +} + + + +void OXPSet::operator -= (OXPSet& b) +{ + if (&b == this) + clear(); + else if (count != 0 && b.count != 0) + { + int i = p.low(); + int k = i; + int j = b.p.low(); + int oldfence = p.fence(); + for (;;) + { + if (i >= oldfence) + break; + else if (j >= b.p.fence()) + { + if (k != i) + while (i < oldfence) p[k++] = p[i++]; + else + k = oldfence; + break; + } + int cmp = CMP(p[i], b.p[j]); + if (cmp == 0) + { + ++i; ++j; + } + else if (cmp < 0) + { + if (k != i) p[k] = p[i]; + ++i; ++k; + } + else + j++; + } + while (k++ < oldfence) + { + --count; + p.del_high(); + } + } +} + +void OXPSet::operator &= (OXPSet& b) +{ + if (b.count == 0) + clear(); + else if (&b != this && count != 0) + { + int i = p.low(); + int k = i; + int j = b.p.low(); + int oldfence = p.fence(); + for (;;) + { + if (i >= oldfence || j >= b.p.fence()) + break; + int cmp = CMP(p[i], b.p[j]); + if (cmp == 0) + { + if (k != i) p[k] = p[i]; + ++i; ++k; ++j; + } + else if (cmp < 0) + ++i; + else + ++j; + } + while (k++ < oldfence) + { + --count; + p.del_high(); + } + } +} + +int OXPSet::OK() +{ + int v = p.OK(); + v &= count == p.length(); + for (int i = p.low(); i < p.high(); ++i) v &= CMP(p[i], p[i+1]) < 0; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/OXPSet.hP b/gnu/lib/libg++/g++-include/OXPSet.hP new file mode 100644 index 0000000000..4e0c97712d --- /dev/null +++ b/gnu/lib/libg++/g++-include/OXPSet.hP @@ -0,0 +1,102 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _OXPSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _OXPSet_h 1 + +#include ".Set.h" +#include ".XPlex.h" + +class OXPSet : public Set +{ +protected: + XPlex p; + +public: + OXPSet(int chunksize = DEFAULT_INITIAL_CAPACITY); + OXPSet(const OXPSet&); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item); + + void operator |= (OXPSet& b); + void operator -= (OXPSet& b); + void operator &= (OXPSet& b); + + int operator == (OXPSet& b); + int operator != (OXPSet& b); + int operator <= (OXPSet& b); + + int OK(); +}; + + +inline OXPSet::OXPSet(int chunksize) + : p(chunksize) { count = 0; } + +inline OXPSet::OXPSet(const OXPSet& s) : p(s.p) { count = s.count; } + +inline Pix OXPSet::first() +{ + return p.first(); +} + +inline void OXPSet::next(Pix & idx) +{ + p.next(idx); +} + +inline & OXPSet::operator ()(Pix idx) +{ + return p(idx); +} + +inline void OXPSet::clear() +{ + count = 0; p.clear(); +} + +inline int OXPSet::contains ( item) +{ + return seek(item) != 0; +} + +inline int OXPSet::owns (Pix idx) +{ + return p.owns(idx); +} + +inline int OXPSet::operator != (OXPSet& b) +{ + return !(*this == b); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/Obstack.h b/gnu/lib/libg++/g++-include/Obstack.h deleted file mode 100644 index 0cc2e9bb37..0000000000 --- a/gnu/lib/libg++/g++-include/Obstack.h +++ /dev/null @@ -1,226 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - - -#ifndef _Obstack_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Obstack_h 1 - -#include - -class Obstack -{ - struct _obstack_chunk - { - char* limit; - _obstack_chunk* prev; - char contents[4]; - }; - -protected: - long chunksize; - _obstack_chunk* chunk; - char* objectbase; - char* nextfree; - char* chunklimit; - int alignmentmask; - - void _free(void* obj); - void newchunk(int size); - -public: - Obstack(int size = 4080, int alignment = 4); // 4080=4096-mallocslop - - ~Obstack(); - - void* base(); - void* next_free(); - int alignment_mask(); - int chunk_size(); - int size(); - int room(); - int contains(void* p); // does Obstack hold pointer p? - - void grow(const void* data, int size); - void grow(const void* data, int size, char terminator); - void grow(const char* s); - void grow(char c); - void grow_fast(char c); - void blank(int size); - void blank_fast(int size); - - void* finish(); - void* finish(char terminator); - - void* copy(const void* data, int size); - void* copy(const void* data, int size, char terminator); - void* copy(const char* s); - void* copy(char c); - void* alloc(int size); - - void free(void* obj); - void shrink(int size = 1); // suggested by ken@cs.rochester.edu - - int OK(); // rep invariant -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - - -inline Obstack::~Obstack() -{ - _free(0); -} - -inline void* Obstack::base() -{ - return objectbase; -} - -inline void* Obstack::next_free() -{ - return nextfree; -} - -inline int Obstack::alignment_mask() -{ - return alignmentmask; -} - -inline int Obstack::chunk_size() -{ - return chunksize; -} - -inline int Obstack::size() -{ - return nextfree - objectbase; -} - -inline int Obstack::room() -{ - return chunklimit - nextfree; -} - -inline void Obstack:: grow(const void* data, int size) -{ - if (nextfree+size > chunklimit) - newchunk(size); - bcopy(data, nextfree, size); - nextfree += size; -} - -inline void Obstack:: grow(const void* data, int size, char terminator) -{ - if (nextfree+size+1 > chunklimit) - newchunk(size+1); - bcopy(data, nextfree, size); - nextfree += size; - *(nextfree)++ = terminator; -} - -inline void Obstack:: grow(const char* s) -{ - grow((void*)s, strlen(s), 0); -} - -inline void Obstack:: grow(char c) -{ - if (nextfree+1 > chunklimit) - newchunk(1); - *(nextfree)++ = c; -} - -inline void Obstack:: blank(int size) -{ - if (nextfree+size > chunklimit) - newchunk(size); - nextfree += size; -} - -inline void* Obstack::finish(char terminator) -{ - grow(terminator); - return finish(); -} - -inline void* Obstack::copy(const void* data, int size) -{ - grow (data, size); - return finish(); -} - -inline void* Obstack::copy(const void* data, int size, char terminator) -{ - grow(data, size, terminator); - return finish(); -} - -inline void* Obstack::copy(const char* s) -{ - grow((void*)s, strlen(s), 0); - return finish(); -} - -inline void* Obstack::copy(char c) -{ - grow(c); - return finish(); -} - -inline void* Obstack::alloc(int size) -{ - blank(size); - return finish(); -} - -inline void Obstack:: free(void* obj) -{ - if (obj >= (void*)chunk && obj<(void*)chunklimit) - nextfree = objectbase = (char *) obj; - else - _free(obj); -} - -inline void Obstack:: grow_fast(char c) -{ - *(nextfree)++ = c; -} - -inline void Obstack:: blank_fast(int size) -{ - nextfree += size; -} - -inline void Obstack:: shrink(int size) // from ken@cs.rochester.edu -{ - if (nextfree >= objectbase + size) - nextfree -= size; -} - -#endif - -#endif diff --git a/gnu/lib/libg++/g++-include/PHPQ.ccP b/gnu/lib/libg++/g++-include/PHPQ.ccP new file mode 100644 index 0000000000..764b11c5cf --- /dev/null +++ b/gnu/lib/libg++/g++-include/PHPQ.ccP @@ -0,0 +1,339 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + adapted for libg++ by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".PHPQ.h" + +// +// This defines a Pairing Heap structure +// +// See ``The Pairing Heap: A New Form of Self-Adjusting Heap'' +// Fredman, Segdewick et al, +// Algorithmica (1986) 1:111-129 +// +// In particular, this implements the pairing heap using the circular +// list. +// +// + +PHPQ::PHPQ(int sz) +{ + storage = 0; + root = 0; + count = 0; + size = 0; + prealloc(sz); +} + +PHPQ::PHPQ(PHPQ& a) +{ + storage = 0; + root = 0; + count = 0; + size = 0; + prealloc(a.size); + for (Pix i = a.first(); i != 0; a.next(i)) enq(a(i)); +} + + +void PHPQ::prealloc(int newsize) +{ + ++newsize; // leave a spot for freelist + if (size != 0) + { + int news = size; + while (news <= newsize) news = (news * 3) / 2; + newsize = news; + } + // see if indices are OK + PHPQNode test; + test.sibling = 0; + test.sibling = ~test.sibling; + if ((unsigned long)newsize > (unsigned long)(test.sibling)) + error("storage size exceeds index range"); + + if (storage == 0) + { + storage = new PHPQNode[size = newsize]; + for (int i = 0; i < size; ++i) + { + storage[i].sibling = i + 1; + storage[i].valid = 0; + } + storage[size-1].sibling = 0; + } + else + { + PHPQNode* newstor = new PHPQNode[newsize]; + for (int i = 1; i < size; ++i) + newstor[i] = storage[i]; + delete [] storage; + storage = newstor; + for (i = size; i < newsize; ++i) + { + storage[i].sibling = i + 1; + storage[i].valid = 0; + } + storage[newsize-1].sibling = 0; + storage[0].sibling = size; + size = newsize; + } +} + + +void PHPQ::clear() +{ + for (int i = 0; i < size; ++i) + { + storage[i].sibling = i + 1; + storage[i].valid = 0; + } + storage[size-1].sibling = 0; + root = 0; + count = 0; +} + +Pix PHPQ::enq( item) +{ + ++count; + if (storage[0].sibling == 0) + prealloc(count); + + int cell = storage[0].sibling; + storage[0].sibling = storage[cell].sibling; + storage[cell].sibling = 0; + storage[cell].children = 0; + storage[cell].item = item; + storage[cell].valid = 1; + + if (root == 0) + { + root = cell; + return Pix(root); + } + else + { + int parent; + int child; + + if (LE(storage[root].item, storage[cell].item)) + { + parent = root; child = cell; + } + else + { + parent = cell; child = root; + } + int popsKid = storage[parent].children; + + if (popsKid == 0) + { + storage[parent].children = child; + storage[child].sibling = child; + } + else + { + int temp = storage[popsKid].sibling; + storage[popsKid].sibling = child; + storage[child].sibling = temp; + storage[parent].children = child; + } + root = parent; + return Pix(cell); + } +} + +// +// Item removal is the most complicated routine. +// +// We remove the root (should there be one) and then select a new +// root. The siblings of the root are in a circular list. We continue +// to pair elements in this list until there is a single element. +// This element will be the new root. + +void PHPQ::del_front() +{ + int valid = 0; + do + { + if (root == 0) return; + if (valid = storage[root].valid) + --count; + storage[root].valid = 0; + int child = storage[root].children; + storage[root].sibling = storage[0].sibling; + storage[0].sibling = root; + + if (child == 0) + { + root = 0; + return; + } + else + { + while(storage[child].sibling != child) + { + // We have at least two kids, but we may only have + // two kids. So, oneChild != child, but it is possible + // that twoChild == child. + + int oneChild = storage[child].sibling; + int twoChild = storage[oneChild].sibling; + + // Remove the two from the sibling list + + storage[child].sibling = storage[twoChild].sibling; + storage[oneChild].sibling = 0; + storage[twoChild].sibling = 0; + + int bestChild; + int worstChild; + + if (LE(storage[oneChild].item, storage[twoChild].item)) + { + bestChild = oneChild; worstChild = twoChild; + } + else + { + bestChild = twoChild; worstChild = oneChild; + } + int popsKid = storage[bestChild].children; + + if (popsKid == 0) + { + storage[bestChild].children = worstChild; + storage[worstChild].sibling = worstChild; + } + else + { + int temp = storage[popsKid].sibling; + storage[popsKid].sibling = worstChild; + storage[worstChild].sibling = temp; + storage[bestChild].children = worstChild; + } + if (twoChild == child) + { + // We have reduced the two to one, so we'll be exiting. + child = bestChild; + storage[child].sibling = child; + } + else + { + // We've removed two siblings, now we need to insert + // the better of the two + storage[bestChild].sibling = storage[child].sibling; + storage[child].sibling = bestChild; + child = storage[bestChild].sibling; + } + } + root = child; + } + } while ( !valid ); +} + +void PHPQ::del(Pix p) +{ + if (p == 0) error("null Pix"); + int i = int(p); + if (storage[i].valid) + { + if (i == root) + del_front(); + else + { + storage[i].valid = 0; + --count; + } + } +} + + +Pix PHPQ::seek( key) +{ + for (int i = 1; i < size; ++i) + if (storage[i].valid && EQ(storage[i].item, key)) + return Pix(i); + return 0; +} + +Pix PHPQ::first() +{ + for (int i = 1; i < size; ++i) + if (storage[i].valid) + return Pix(i); + return 0; +} + + +void PHPQ::next(Pix& p) +{ + if (p == 0) return; + for (int i = int(p)+1; i < size; ++i) + if (storage[i].valid) + { + p = Pix(i); + return; + } + p = 0; +} + +int PHPQ::OK() +{ + int v = storage != 0; + int n = 0; + for (int i = 0; i < size; ++i) if (storage[i].valid) ++n; + v &= n == count; + v &= check_sibling_list(root); + int ct = MAXLONG; + n = 0; + int f = storage[0].sibling; + while (f != 0 && ct-- > 0) + { + f = storage[f].sibling; + ++n; + } + v &= ct > 0; + v &= n <= size - count; + if (!v) error("invariant failure"); + return v; +} + + +int PHPQ::check_sibling_list(int t) +{ + if (t != 0) + { + int s = t; + long ct = MAXLONG; // Lots of chances to find self! + do + { + if (storage[s].valid && !check_sibling_list(storage[s].children)) + return 0; + s = storage[s].sibling; + } while (ct-- > 0 && s != t && s != 0); + if (ct <= 0) return 0; + } + return 1; +} + + diff --git a/gnu/lib/libg++/g++-include/PHPQ.hP b/gnu/lib/libg++/g++-include/PHPQ.hP new file mode 100644 index 0000000000..359c527c60 --- /dev/null +++ b/gnu/lib/libg++/g++-include/PHPQ.hP @@ -0,0 +1,108 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + adapted for libg++ by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef PHPQ_h +#ifdef __GNUG__ +#pragma interface +#endif +#define PHPQ_h 1 + +#include ".PQ.h" + +#ifndef PHPQIndex +#define PHPQIndex unsigned short +#endif + +struct PHPQNode +{ + PHPQIndex sibling; + PHPQIndex children; + item; + char valid; +}; + + +class PHPQ : public PQ +{ + PHPQNode* storage; // table -- freelist in storage[0].sibling + int root; + int size; + + void prealloc(int); + int check_sibling_list(int); + +public: + + PHPQ(int sz = DEFAULT_INITIAL_CAPACITY); + PHPQ(PHPQ&); + ~PHPQ(); + + Pix enq( item); + deq(); + + & front(); + void del_front(); + + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + void del(Pix i); + Pix seek( item); + + int OK(); // rep invariant +}; + + +inline PHPQ::~PHPQ() +{ + delete [] storage; +} + + +inline PHPQ::deq() +{ + if (count == 0) error("deq of empty PQ"); + x = storage[root].item; + del_front(); + return x; +} + + +inline & PHPQ::front() +{ + if (count == 0) error("front of empty PQ"); + return storage[root].item; +} + +inline int PHPQ::contains( item) +{ + return seek(item) != 0; +} + +inline & PHPQ::operator() (Pix p) +{ + if (p == 0) error("null Pix"); + return storage[int(p)].item; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/PQ.ccP b/gnu/lib/libg++/g++-include/PQ.ccP new file mode 100644 index 0000000000..810240759c --- /dev/null +++ b/gnu/lib/libg++/g++-include/PQ.ccP @@ -0,0 +1,62 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".PQ.h" + + + + PQ::deq() +{ + x = front(); + del_front(); + return x; +} + +Pix PQ::seek( item) +{ + for (Pix i = first(); i != 0 && !(EQ((*this)(i), item)); next(i)); + return i; +} + +int PQ::owns(Pix idx) +{ + if (idx == 0) return 0; + for (Pix i = first(); i; next(i)) if (i == idx) return 1; + return 0; +} + +void PQ::clear() +{ + while (count != 0) del_front(); +} + +int PQ::contains ( item) +{ + return seek(item) != 0; +} + + +void PQ::error(const char* msg) +{ + (*lib_error_handler)("PQ", msg); +} + diff --git a/gnu/lib/libg++/g++-include/PQ.hP b/gnu/lib/libg++/g++-include/PQ.hP new file mode 100644 index 0000000000..981592ae85 --- /dev/null +++ b/gnu/lib/libg++/g++-include/PQ.hP @@ -0,0 +1,78 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _PQ_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _PQ_h 1 + +#include +#include ".defs.h" + +class PQ +{ +protected: + + int count; + +public: + PQ(); + virtual ~PQ(); + + int length(); // current number of items + int empty(); + + virtual Pix enq( item) = 0; // add item; return Pix + virtual deq(); // return & remove min + + virtual & front() = 0; // access min item + virtual void del_front() = 0; // delete min item + + virtual int contains( item); // is item in PQ? + + virtual void clear(); // delete all items + + virtual Pix first() = 0; // Pix of first item or 0 + virtual void next(Pix& i) = 0; // advance to next or 0 + virtual & operator () (Pix i) = 0; // access item at i + virtual void del(Pix i) = 0; // delete item at i + virtual int owns(Pix i); // is i a valid Pix ? + virtual Pix seek( item); // Pix of item + + void error(const char* msg); + virtual int OK() = 0; // rep invariant +}; + + +inline PQ::PQ() :count(0) {} + +inline PQ::~PQ() {} + +inline int PQ::length() +{ + return count; +} + +inline int PQ::empty() +{ + return count == 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/PSList.hP b/gnu/lib/libg++/g++-include/PSList.hP new file mode 100644 index 0000000000..eacb34dbe3 --- /dev/null +++ b/gnu/lib/libg++/g++-include/PSList.hP @@ -0,0 +1,32 @@ +/* : Light weight list: This will simply reuse code from a VoidP List, which +was genclassed from the SLList libg++ class. The classes generated from this file +will all be derived classes from class VoidSLList or intSLList. Note that class SLList does not +offer all the functionality of List classes, such as sharing of sub-lists. +However, no additional code is needed at all and no .cc file is generated. So it costs nothing +to use these type-safe lists. Only member functions needing type casting are re-defined */ + + +#ifndef _SList_h +#define _SList_h 1 + +#include "VoidP.SLList.h" +#include ".defs.h" + +class SList : public VoidPSLList +{ +public: + SList() {} + SList(SList& a) : (a) {} + ~SList() {} + + SList& operator = (SList& a) { + return (SList&) VoidPSLList::operator= (a); } + + & operator () (Pix p) { return (&) (VoidPSLList::operator() (p)); } + & front() { return (&) VoidPSLList::front(); } + & rear() { return (&) VoidPSLList::rear(); } + remove_front() { return () VoidPSLList::remove_front(); } + +}; + +#endif /* conditional include */ diff --git a/gnu/lib/libg++/g++-include/PVec.hP b/gnu/lib/libg++/g++-include/PVec.hP new file mode 100644 index 0000000000..de32482610 --- /dev/null +++ b/gnu/lib/libg++/g++-include/PVec.hP @@ -0,0 +1,79 @@ +/* : light weight Vector: This will simply reuse code from */ +/* a VoidP Vec, which was genclassed from the Vec libg++ class. */ +/* The classes generated from this file will all be derived classes */ +/* from class VoidVec or intVec. No .cc file is generated. So */ +/* it costs nothing to use these type-safe Vectors. Only member */ +/* functions needing type casting are re-defined. */ +/* */ + +#ifndef _Vec_h +#define _Vec_h 1 + +#include "VoidP.Vec.h" +#include ".defs.h" + + +#ifndef __typedefs +#define __typedefs 1 +typedef void (*Procedure)( ); +typedef (*Mapper)( ); +typedef (*Combiner)( , ); +typedef int (*Predicate)( ); +typedef int (*Comparator)( , ); +#endif + +class Vec : public VoidPVec +{ +protected: + Vec(int l, * d) : (l, (VoidP*) d) {}; +public: + Vec() {}; + Vec(int l) : (l) {}; + Vec(int l, fill_value) : (l, fill_value) {}; + Vec(Vec& v) : (v) {}; + Vec(VoidPVec& v) {fake_copy(v, s, len);} + ~Vec() {}; + + Vec& operator = (Vec& a) + {return (Vec&) VoidPVec::operator= (a);} + Vec at(int from, int n) {return (Vec) VoidPVec::at(from, n);} + + & operator [] (int n) {return (&)VoidPVec::operator[] (n);} + & elem(int n) {return (&)VoidPVec::elem(n);} + + friend Vec concat(Vec& a, Vec& b); + friend Vec map(Mapper f, Vec & a); + friend Vec merge(Vec & a, Vec & b, Comparator f); + friend Vec combine(Combiner f, Vec & a, Vec & b); + friend Vec reverse(Vec& a); + + void sort(Comparator f); + void apply(Procedure f); + reduce(Combiner f, base); +}; + +inline Vec concat(Vec& a, Vec& b) +{return (Vec)concat((VoidPVec&)a, (VoidPVec&)b);} + +inline Vec map(Mapper f, Vec & a) { + return (Vec)map((VoidPMapper)f, (VoidPVec&)a); } + +inline Vec merge(Vec & a, Vec & b, Comparator f) { + return (Vec)merge((VoidPVec&)a, (VoidPVec&)b, (VoidPComparator)f); } + +inline Vec combine(Combiner f, Vec & a, Vec & b) { + return (Vec)combine((VoidPCombiner)f, (VoidPVec&)a, (VoidPVec&)b); } + +inline Vec reverse(Vec& a) { + return (Vec)reverse((VoidPVec&)a);} + +inline void Vec::sort(Comparator f) { + VoidPVec::sort((VoidPComparator) f); } + +inline void Vec::apply(Procedure f) { + VoidPVec::apply((VoidPProcedure) f); } + +inline Vec::reduce(Combiner f, base) { + return ()VoidPVec::reduce((VoidPCombiner)f, base);} + +#endif /* conditional include */ diff --git a/gnu/lib/libg++/g++-include/Pix.h b/gnu/lib/libg++/g++-include/Pix.h deleted file mode 100644 index 0e087967e0..0000000000 --- a/gnu/lib/libg++/g++-include/Pix.h +++ /dev/null @@ -1,9 +0,0 @@ - -#ifndef _Pix_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Pix_h 1 -typedef void* Pix; -#endif diff --git a/gnu/lib/libg++/g++-include/Plex.ccP b/gnu/lib/libg++/g++-include/Plex.ccP new file mode 100644 index 0000000000..5eb13c85f4 --- /dev/null +++ b/gnu/lib/libg++/g++-include/Plex.ccP @@ -0,0 +1,222 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include +#include ".Plex.h" + +// IChunk support + +void IChunk::error(const char* msg) const +{ + (*lib_error_handler)("IChunk", msg); +} + +void IChunk::index_error() const +{ + error("attempt to use invalid index"); +} + +void IChunk::empty_error() const +{ + error("invalid use of empty chunk"); +} + +void IChunk::full_error() const +{ + error("attempt to extend chunk beyond bounds"); +} + +IChunk:: ~IChunk() {} + +IChunk::IChunk(* d, + int baseidx, + int lowidx, + int fenceidx, + int topidx) +{ + if (d == 0 || baseidx > lowidx || lowidx > fenceidx || fenceidx > topidx) + error("inconsistent specification"); + data = d; + base = baseidx; + low = lowidx; + fence = fenceidx; + top = topidx; + nxt = prv = this; +} + +void IChunk:: re_index(int lo) +{ + int delta = lo - low; + base += delta; + low += delta; + fence += delta; + top += delta; +} + + +void IChunk::clear(int lo) +{ + int s = top - base; + low = base = fence = lo; + top = base + s; +} + +void IChunk::cleardown(int hi) +{ + int s = top - base; + low = top = fence = hi; + base = top - s; +} + +int IChunk:: OK() const +{ + int v = data != 0; // have some data + v &= base <= low; // ok, index-wise + v &= low <= fence; + v &= fence <= top; + + v &= nxt->prv == this; // and links are OK + v &= prv->nxt == this; + if (!v) error("invariant failure"); + return(v); +} + + +// error handling + + +void Plex::error(const char* msg) const +{ + (*lib_error_handler)("Plex", msg); +} + +void Plex::index_error() const +{ + error("attempt to access invalid index"); +} + +void Plex::empty_error() const +{ + error("attempted operation on empty plex"); +} + +void Plex::full_error() const +{ + error("attempt to increase size of plex past limit"); +} + +// generic plex ops + +Plex:: ~Plex() +{ + invalidate(); +} + + +void Plex::append (const Plex& a) +{ + for (int i = a.low(); i < a.fence(); a.next(i)) add_high(a[i]); +} + +void Plex::prepend (const Plex& a) +{ + for (int i = a.high(); i > a.ecnef(); a.prev(i)) add_low(a[i]); +} + +void Plex::reverse() +{ + tmp; + int l = low(); + int h = high(); + while (l < h) + { + tmp = (*this)[l]; + (*this)[l] = (*this)[h]; + (*this)[h] = tmp; + next(l); + prev(h); + } +} + + +void Plex::fill(const x) +{ + for (int i = lo; i < fnc; ++i) (*this)[i] = x; +} + +void Plex::fill(const x, int lo, int hi) +{ + for (int i = lo; i <= hi; ++i) (*this)[i] = x; +} + + +void Plex::del_chunk(IChunk* x) +{ + if (x != 0) + { + x->unlink(); + * data = (*)(x->invalidate()); + delete [] data; + delete x; + } +} + + +void Plex::invalidate() +{ + IChunk* t = hd; + if (t != 0) + { + IChunk* tail = tl(); + while (t != tail) + { + IChunk* nxt = t->next(); + del_chunk(t); + t = nxt; + } + del_chunk(t); + hd = 0; + } +} + +int Plex::reset_low(int l) +{ + int old = lo; + int diff = l - lo; + if (diff != 0) + { + lo += diff; + fnc += diff; + IChunk* t = hd; + do + { + t->re_index(t->low_index() + diff); + t = t->next(); + } while (t != hd); + } + return old; +} + + + + diff --git a/gnu/lib/libg++/g++-include/Plex.hP b/gnu/lib/libg++/g++-include/Plex.hP new file mode 100644 index 0000000000..f8af1b6ba7 --- /dev/null +++ b/gnu/lib/libg++/g++-include/Plex.hP @@ -0,0 +1,494 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _Plex_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Plex_h 1 + +#include +#include +#include ".defs.h" + +// Plexes are made out of IChunks + +#include + +class IChunk +{ +//public: // kludge until C++ `protected' policies settled +protected: + + * data; // data, from client + + int base; // lowest possible index + int low; // lowest valid index + int fence; // highest valid index + 1 + int top; // highest possible index + 1 + + IChunk* nxt; // circular links + IChunk* prv; + +public: + +// constructors + + IChunk(* d, // ptr to array of elements + int base_idx, // initial indices + int low_idx, + int fence_idx, + int top_idx); + + virtual ~IChunk(); + +// status reports + + int size() const; // number of slots + + virtual int empty() const ; + virtual int full() const ; + + int can_grow_high () const ; // there is space to add data + int can_grow_low () const; + + int base_index() const; // lowest possible index; + int low_index() const; // lowest actual index; + virtual int first_index() const; // lowest valid index or fence if none + virtual int last_index() const; // highest valid index or low-1 if none + int fence_index() const; // highest actual index + 1 + int top_index() const; // highest possible index + 1 + +// indexing conversion + + int possible_index(int i) const; // i between base and top + int actual_index(int i) const; // i between low and fence + virtual int valid_index(int i) const; // i not deleted (mainly for mchunks) + + int possible_pointer(const * p) const; // same for ptr + int actual_pointer(const * p) const; + virtual int valid_pointer(const * p) const; + + * pointer_to(int i) const ; // pointer to data indexed by i + // caution: i is not checked for validity + int index_of(const * p) const; // index of data pointed to by p + // caution: p is not checked for validity + + virtual int succ(int idx) const; // next valid index or fence if none + virtual int pred(int idx) const; // previous index or low - 1 if none + + virtual * first_pointer() const; // pointer to first valid pos or 0 + virtual * last_pointer() const; // pointer to first valid pos or 0 + virtual * succ(* p) const; // next pointer or 0 + virtual * pred(* p) const; // previous pointer or 0 + +// modification + + virtual * grow_high (); // return spot to add an element + virtual * grow_low (); + + virtual void shrink_high (); // logically delete top index + virtual void shrink_low (); + + virtual void clear(int lo); // reset to empty ch with base = lo + virtual void cleardown(int hi); // reset to empty ch with top = hi + void re_index(int lo); // re-index so lo is new low + +// chunk traversal + + IChunk* next() const; + IChunk* prev() const; + + void link_to_prev(IChunk* prev); + void link_to_next(IChunk* next); + void unlink(); + +// state checks + + * invalidate(); // mark self as invalid; return data + // for possible deletion + + virtual int OK() const; // representation invariant + + void error(const char*) const; + void empty_error() const; + void full_error() const; + void index_error() const; +}; + +// Plex is a partly `abstract' class: few of the virtuals +// are implemented at the Plex level, only in the subclasses + +class Plex +{ +protected: + + IChunk* hd; // a chunk holding the data + int lo; // lowest index + int fnc; // highest index + 1 + int csize; // size of the chunk + + void invalidate(); // mark so OK() is false + void del_chunk(IChunk*); // delete a chunk + + IChunk* tl() const; // last chunk; + int one_chunk() const; // true if hd == tl() + +public: + +// constructors, etc. + + Plex(); // no-op + + virtual ~Plex(); + + +// Access functions + + virtual & operator [] (int idx) = 0; // access by index; + virtual & operator () (Pix p) = 0; // access by Pix; + + virtual & high_element () = 0; // access high element + virtual & low_element () = 0; // access low element + +// read-only versions for const Plexes + + virtual const & operator [] (int idx) const = 0; // access by index; + virtual const & operator () (Pix p) const = 0; // access by Pix; + + virtual const & high_element () const = 0; // access high element + virtual const & low_element () const = 0; // access low element + + +// Index functions + + virtual int valid (int idx) const = 0; // idx is an OK index + + virtual int low() const = 0; // lowest index or fence if none + virtual int high() const = 0; // highest index or low-1 if none + + int ecnef() const; // low limit index (low-1) + int fence() const; // high limit index (high+1) + + virtual void prev(int& idx) const= 0; // set idx to preceding index + // caution: pred may be out of bounds + + virtual void next(int& idx) const = 0; // set to next index + // caution: succ may be out of bounds + + virtual Pix first() const = 0; // Pix to low element or 0 + virtual Pix last() const = 0; // Pix to high element or 0 + virtual void prev(Pix& pix) const = 0; // preceding pix or 0 + virtual void next(Pix& pix) const = 0; // next pix or 0 + virtual int owns(Pix p) const = 0; // p is an OK Pix + +// index<->Pix + + virtual int Pix_to_index(Pix p) const = 0; // get index via Pix + virtual Pix index_to_Pix(int idx) const = 0; // Pix via index + +// Growth + + virtual int add_high(const elem) =0;// add new element at high end + // return new high + + virtual int add_low(const elem) = 0; // add new low element, + // return new low + +// Shrinkage + + virtual int del_high() = 0; // remove the element at high end + // return new high + virtual int del_low() = 0; // delete low element, return new lo + + // caution: del_low/high + // does not necessarily + // immediately call ::~ + + +// operations on multiple elements + + virtual void fill(const x); // set all elements = x + virtual void fill(const x, int from, int to); // fill from to to + virtual void clear() = 0; // reset to zero-sized Plex + virtual int reset_low(int newlow); // change low index,return old + virtual void reverse(); // reverse in-place + virtual void append(const Plex& a); // concatenate a copy + virtual void prepend(const Plex& a); // prepend a copy + +// status + + virtual int can_add_high() const = 0; + virtual int can_add_low() const = 0; + + int length () const; // number of slots + + int empty () const; // is the plex empty? + virtual int full() const = 0; // it it full? + + int chunk_size() const; // report chunk size; + + virtual int OK() const = 0; // representation invariant + + void error(const char* msg) const; + void index_error() const; + void empty_error() const; + void full_error() const; +}; + + +// IChunk ops + +inline int IChunk:: size() const +{ + return top - base; +} + + +inline int IChunk:: base_index() const +{ + return base; +} + +inline int IChunk:: low_index() const +{ + return low; +} + +inline int IChunk:: fence_index() const +{ + return fence; +} + +inline int IChunk:: top_index() const +{ + return top; +} + +inline * IChunk:: pointer_to(int i) const +{ + return &(data[i-base]); +} + +inline int IChunk:: index_of(const * p) const +{ + return ((int)p - (int)data) / sizeof() + base; +} + +inline int IChunk:: possible_index(int i) const +{ + return i >= base && i < top; +} + +inline int IChunk:: possible_pointer(const * p) const +{ + return p >= data && p < &(data[top-base]); +} + +inline int IChunk:: actual_index(int i) const +{ + return i >= low && i < fence; +} + +inline int IChunk:: actual_pointer(const * p) const +{ + return p >= data && p < &(data[fence-base]); +} + +inline int IChunk:: can_grow_high () const +{ + return fence < top; +} + +inline int IChunk:: can_grow_low () const +{ + return base < low; +} + +inline * IChunk:: invalidate() +{ + * p = data; + data = 0; + return p; +} + + +inline IChunk* IChunk::prev() const +{ + return prv; +} + +inline IChunk* IChunk::next() const +{ + return nxt; +} + +inline void IChunk::link_to_prev(IChunk* prev) +{ + nxt = prev->nxt; + prv = prev; + nxt->prv = this; + prv->nxt = this; +} + +inline void IChunk::link_to_next(IChunk* next) +{ + prv = next->prv; + nxt = next; + nxt->prv = this; + prv->nxt = this; +} + +inline void IChunk::unlink() +{ + IChunk* n = nxt; + IChunk* p = prv; + n->prv = p; + p->nxt = n; + prv = nxt = this; +} + +inline int IChunk:: empty() const +{ + return low == fence; +} + +inline int IChunk:: full() const +{ + return top - base == fence - low; +} + +inline int IChunk:: first_index() const +{ + return (low == fence)? fence : low; +} + +inline int IChunk:: last_index() const +{ + return (low == fence)? low - 1 : fence - 1; +} + +inline int IChunk:: succ(int i) const +{ + return (i < low) ? low : i + 1; +} + +inline int IChunk:: pred(int i) const +{ + return (i > fence) ? (fence - 1) : i - 1; +} + +inline int IChunk:: valid_index(int i) const +{ + return i >= low && i < fence; +} + +inline int IChunk:: valid_pointer(const * p) const +{ + return p >= &(data[low - base]) && p < &(data[fence - base]); +} + +inline * IChunk:: grow_high () +{ + if (!can_grow_high()) full_error(); + return &(data[fence++ - base]); +} + +inline * IChunk:: grow_low () +{ + if (!can_grow_low()) full_error(); + return &(data[--low - base]); +} + +inline void IChunk:: shrink_high () +{ + if (empty()) empty_error(); + --fence; +} + +inline void IChunk:: shrink_low () +{ + if (empty()) empty_error(); + ++low; +} + +inline * IChunk::first_pointer() const +{ + return (low == fence)? 0 : &(data[low - base]); +} + +inline * IChunk::last_pointer() const +{ + return (low == fence)? 0 : &(data[fence - base - 1]); +} + +inline * IChunk::succ(* p) const +{ + return ((p+1) < &(data[low - base]) || (p+1) >= &(data[fence - base])) ? + 0 : (p+1); +} + +inline * IChunk::pred(* p) const +{ + return ((p-1) < &(data[low - base]) || (p-1) >= &(data[fence - base])) ? + 0 : (p-1); +} + + +// generic Plex operations + +inline Plex::Plex() {} + +inline int Plex::chunk_size() const +{ + return csize; +} + +inline int Plex::ecnef () const +{ + return lo - 1; +} + + +inline int Plex::fence () const +{ + return fnc; +} + +inline int Plex::length () const +{ + return fnc - lo; +} + +inline int Plex::empty () const +{ + return fnc == lo; +} + +inline IChunk* Plex::tl() const +{ + return hd->prev(); +} + +inline int Plex::one_chunk() const +{ + return hd == hd->prev(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/PlotFile.h b/gnu/lib/libg++/g++-include/PlotFile.h deleted file mode 100644 index 1a6939641e..0000000000 --- a/gnu/lib/libg++/g++-include/PlotFile.h +++ /dev/null @@ -1,154 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -/* - a very simple implementation of a class to output unix "plot" - format plotter files. See corresponding unix man pages for - more details. -*/ - -#ifndef _PlotFile_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _PlotFile_h - -#include - -/* - Some plot libraries have the `box' command to draw boxes. Some don't. - `box' is included here via moves & lines to allow both possiblilties. -*/ - - -class PlotFile : private File -{ -protected: - PlotFile& cmd(char c); - PlotFile& operator << (const int x); - PlotFile& operator << (const char *s); - -public: - - PlotFile(); - PlotFile(const char* filename, io_mode m, access_mode a); - PlotFile(const char* filename, const char* m); - PlotFile(int filedesc, const io_mode m = io_writeonly); - PlotFile(FILE* fileptr); - - ~PlotFile(); - - operator void*(); - - PlotFile& close() { File::close(); return *this; } - PlotFile& remove() { File::remove(); return *this; } - - int filedesc() { return File::filedesc(); } - const char* name() { return File::name(); } - void setname(const char* newname) { File::setname(newname); } - int iocount() { return File::iocount(); } - - int rdstate() { return File::rdstate(); } - int eof() { return File::eof(); } - int fail() { return File::fail(); } - int bad() { return File::bad(); } - int good() { return File::good(); } - - // other status queries - - int readable() { return File::readable(); } - int writable() { return File::writable(); } - int is_open() { return File::is_open(); } - - void error() { File::error(); } - void clear(state_value f = _good) { File::clear(f); } - void set(state_value f) { File::set(f); } - void unset(state_value f) { File::unset(f); } - PlotFile& failif(int cond) { File::failif(cond); return *this; } - void check_state() { File::check_state(); } - - PlotFile& raw() { File::raw(); return *this; } - - PlotFile& open(const char* filename, io_mode m, access_mode a); - PlotFile& open(const char* filename, const char* m); - PlotFile& open(int filedesc, io_mode m); - PlotFile& open(FILE* fileptr); - PlotFile& setbuf(const int buffer_kind); // vals: _IONBF, _IOFBF, _IOLBF - PlotFile& setbuf(const int size, char* buf); - - PlotFile& arc(const int xi, const int yi, - const int x0, const int y0, - const int x1, const int y1); - PlotFile& box(const int x0, const int y0, - const int x1, const int y1); - PlotFile& circle(const int x, const int y, const int r); - PlotFile& cont(const int xi, const int yi); - PlotFile& dot(const int xi, const int yi, const int dx, - int n, const int* pat); - PlotFile& erase(); - PlotFile& label(const char* s); - PlotFile& line(const int x0, const int y0, - const int x1, const int y1); - PlotFile& linemod(const char* s); - PlotFile& move(const int xi, const int yi); - PlotFile& point(const int xi, const int yi); - PlotFile& space(const int x0, const int y0, - const int x1, const int y1); -}; - - -#endif - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/gnu/lib/libg++/g++-include/Poisson.h b/gnu/lib/libg++/g++-include/Poisson.h deleted file mode 100644 index aaac6e75ae..0000000000 --- a/gnu/lib/libg++/g++-include/Poisson.h +++ /dev/null @@ -1,60 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ -#ifndef _Poisson_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Poisson_h - -#include - -class Poisson: public Random { -protected: - double pMean; -public: - Poisson(double mean, RNG *gen); - - double mean(); - double mean(double x); - - virtual double operator()(); -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline Poisson::Poisson(double mean, RNG *gen) -: (gen) { - pMean = mean; -} - -inline double Poisson::mean() { return pMean; } -inline double Poisson::mean(double x) { - double t = pMean; - pMean = x; - return t; -} - - -#endif -#endif diff --git a/gnu/lib/libg++/g++-include/Queue.ccP b/gnu/lib/libg++/g++-include/Queue.ccP new file mode 100644 index 0000000000..fb48d952ff --- /dev/null +++ b/gnu/lib/libg++/g++-include/Queue.ccP @@ -0,0 +1,14 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".Queue.h" + +Queue::~Queue() {} + + +// error handling + +void Queue::error(const char* msg) +{ + (*lib_error_handler)("Queue", msg); +} diff --git a/gnu/lib/libg++/g++-include/Queue.hP b/gnu/lib/libg++/g++-include/Queue.hP new file mode 100644 index 0000000000..73db6e034e --- /dev/null +++ b/gnu/lib/libg++/g++-include/Queue.hP @@ -0,0 +1,51 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _Queue_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Queue_h + +#include + +#include ".defs.h" + +class Queue +{ +public: + Queue() { } + virtual ~Queue(); + + virtual void enq( item) = 0; + virtual deq() = 0; + virtual & front() = 0; + virtual void del_front() = 0; + + virtual void clear() = 0; + virtual int empty() = 0; + virtual int full() = 0; + virtual int length() = 0; + + void error(const char*); + + virtual int OK() = 0; +}; + +#endif diff --git a/gnu/lib/libg++/g++-include/RAVLMap.ccP b/gnu/lib/libg++/g++-include/RAVLMap.ccP new file mode 100644 index 0000000000..7537dd0788 --- /dev/null +++ b/gnu/lib/libg++/g++-include/RAVLMap.ccP @@ -0,0 +1,690 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include "..RAVLMap.h" + + +/* + constants & inlines for maintaining balance & thread status in tree nodes +*/ + +#define AVLBALANCEMASK 3 +#define AVLBALANCED 0 +#define AVLLEFTHEAVY 1 +#define AVLRIGHTHEAVY 2 + +#define LTHREADBIT 4 +#define RTHREADBIT 8 + + +static inline int bf(RAVLNode* t) +{ + return t->stat & AVLBALANCEMASK; +} + +static inline void set_bf(RAVLNode* t, int b) +{ + t->stat = (t->stat & ~AVLBALANCEMASK) | (b & AVLBALANCEMASK); +} + + +static inline int rthread(RAVLNode* t) +{ + return t->stat & RTHREADBIT; +} + +static inline void set_rthread(RAVLNode* t, int b) +{ + if (b) + t->stat |= RTHREADBIT; + else + t->stat &= ~RTHREADBIT; +} + +static inline int lthread(RAVLNode* t) +{ + return t->stat & LTHREADBIT; +} + +static inline void set_lthread(RAVLNode* t, int b) +{ + if (b) + t->stat |= LTHREADBIT; + else + t->stat &= ~LTHREADBIT; +} + +/* + traversal primitives +*/ + + +RAVLNode* RAVLMap::leftmost() +{ + RAVLNode* t = root; + if (t != 0) while (t->lt != 0) t = t->lt; + return t; +} + +RAVLNode* RAVLMap::rightmost() +{ + RAVLNode* t = root; + if (t != 0) while (t->rt != 0) t = t->rt; + return t; +} + +RAVLNode* RAVLMap::succ(RAVLNode* t) +{ + RAVLNode* r = t->rt; + if (!rthread(t)) while (!lthread(r)) r = r->lt; + return r; +} + +RAVLNode* RAVLMap::pred(RAVLNode* t) +{ + RAVLNode* l = t->lt; + if (!lthread(t)) while (!rthread(l)) l = l->rt; + return l; +} + + +Pix RAVLMap::seek( key) +{ + RAVLNode* t = root; + if (t == 0) + return 0; + for (;;) + { + int cmp = CMP(key, t->item); + if (cmp == 0) + return Pix(t); + else if (cmp < 0) + { + if (lthread(t)) + return 0; + else + t = t->lt; + } + else if (rthread(t)) + return 0; + else + t = t->rt; + } +} + + +int RAVLMap::rankof( key) +{ + int r; + RAVLNode* t = root; + if (t == 0) + return 0; + for (r=t->rank; t != 0; r+=t->rank) + { + int cmp = CMP(key, t->item); + if (cmp == 0) + return r; + else if (cmp < 0) + { + if (lthread(t)) + return 0; + else + { + r -= t->rank; + t = t->lt; + } + } + else if (rthread(t)) + return 0; + else + { + t = t->rt; + } + } + return 0; +} + +Pix RAVLMap::ranktoPix(int i) +{ + int r; + RAVLNode* t = root; + + if ((i<1)||(i>count)) + return 0; + for (r=t->rank; r!=i; r+=t->rank) + { + if (r>i) + { + r -= t->rank; + t = t->lt; + } + else + t = t->rt; + } + return Pix(t); +} + +/* + The combination of threads and AVL bits make adding & deleting + interesting, but very awkward. + + We use the following statics to avoid passing them around recursively +*/ + +static int _need_rebalancing; // to send back balance info from rec. calls +static * _target_item; // add/del_item target +static RAVLNode* _found_node; // returned added/deleted node +static int _already_found; // for deletion subcases +static int _rank_changed; // for rank computation + + +void RAVLMap:: _add(RAVLNode*& t) +{ + int cmp = CMP(*_target_item, t->item); + if (cmp == 0) + { + _found_node = t; + return; + } + else if (cmp < 0) + { + if (lthread(t)) + { + ++count; + _found_node = new RAVLNode(*_target_item, def); + set_lthread(_found_node, 1); + set_rthread(_found_node, 1); + _found_node->lt = t->lt; + _found_node->rt = t; + t->lt = _found_node; + set_lthread(t, 0); + _need_rebalancing = 1; + _rank_changed = 1; + } + else + _add(t->lt); + if (_rank_changed) ++t->rank; + if (_need_rebalancing) + { + switch(bf(t)) + { + case AVLRIGHTHEAVY: + set_bf(t, AVLBALANCED); + _need_rebalancing = 0; + return; + case AVLBALANCED: + set_bf(t, AVLLEFTHEAVY); + return; + case AVLLEFTHEAVY: + { + RAVLNode* l = t->lt; + if (bf(l) == AVLLEFTHEAVY) + { + t->rank -= l->rank; + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + _need_rebalancing = 0; + } + else + { + RAVLNode* r = l->rt; + r->rank += l->rank; + t->rank -= r->rank; + set_rthread(l, lthread(r)); + if (lthread(r)) + l->rt = r; + else + l->rt = r->lt; + r->lt = l; + set_lthread(r, 0); + set_lthread(t, rthread(r)); + if (rthread(r)) + t->lt = r; + else + t->lt = r->rt; + r->rt = t; + set_rthread(r, 0); + if (bf(r) == AVLLEFTHEAVY) + set_bf(t, AVLRIGHTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(r) == AVLRIGHTHEAVY) + set_bf(l, AVLLEFTHEAVY); + else + set_bf(l, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + _need_rebalancing = 0; + return; + } + } + } + } + } + else + { + if (rthread(t)) + { + ++count; + _found_node = new RAVLNode(*_target_item, def); + set_rthread(t, 0); + set_lthread(_found_node, 1); + set_rthread(_found_node, 1); + _found_node->lt = t; + _found_node->rt = t->rt; + t->rt = _found_node; + _need_rebalancing = 1; + _rank_changed = 1; + } + else + _add(t->rt); + if (_need_rebalancing) + { + switch(bf(t)) + { + case AVLLEFTHEAVY: + set_bf(t, AVLBALANCED); + _need_rebalancing = 0; + return; + case AVLBALANCED: + set_bf(t, AVLRIGHTHEAVY); + return; + case AVLRIGHTHEAVY: + { + RAVLNode* r = t->rt; + if (bf(r) == AVLRIGHTHEAVY) + { + r->rank += t->rank; + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + _need_rebalancing = 0; + } + else + { + RAVLNode* l = r->lt; + r->rank -= l->rank; + l->rank += t->rank; + set_lthread(r, rthread(l)); + if (rthread(l)) + r->lt = l; + else + r->lt = l->rt; + l->rt = r; + set_rthread(l, 0); + set_rthread(t, lthread(l)); + if (lthread(l)) + t->rt = l; + else + t->rt = l->lt; + l->lt = t; + set_lthread(l, 0); + if (bf(l) == AVLRIGHTHEAVY) + set_bf(t, AVLLEFTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(l) == AVLLEFTHEAVY) + set_bf(r, AVLRIGHTHEAVY); + else + set_bf(r, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + _need_rebalancing = 0; + return; + } + } + } + } + } +} + + +& RAVLMap::operator [] ( item) +{ + if (root == 0) + { + ++count; + root = new RAVLNode(item, def); + set_rthread(root, 1); + set_lthread(root, 1); + return root->cont; + } + else + { + _target_item = &item; + _need_rebalancing = 0; + _rank_changed = 0; + _add(root); + return _found_node->cont; + } +} + + +void RAVLMap::_del(RAVLNode* par, RAVLNode*& t) +{ + int comp; + if (_already_found) + { + if (rthread(t)) + comp = 0; + else + comp = 1; + } + else + comp = CMP(*_target_item, t->item); + if (comp == 0) + { + if (lthread(t) && rthread(t)) + { + _found_node = t; + if (t == par->lt) + { + set_lthread(par, 1); + par->lt = t->lt; + } + else + { + set_rthread(par, 1); + par->rt = t->rt; + } + _need_rebalancing = 1; + _rank_changed = 1; + return; + } + else if (lthread(t)) + { + _found_node = t; + RAVLNode* s = succ(t); + if (s != 0 && lthread(s)) + s->lt = t->lt; + t = t->rt; + _need_rebalancing = 1; + _rank_changed = 1; + return; + } + else if (rthread(t)) + { + _found_node = t; + RAVLNode* p = pred(t); + if (p != 0 && rthread(p)) + p->rt = t->rt; + t = t->lt; + _need_rebalancing = 1; + _rank_changed = 1; + return; + } + else // replace item & find someone deletable + { + RAVLNode* p = pred(t); + t->item = p->item; + t->cont = p->cont; + _already_found = 1; + comp = -1; // fall through below to left + } + } + + if (comp < 0) + { + if (lthread(t)) + return; + _del(t, t->lt); + if (_rank_changed) --t->rank; + if (!_need_rebalancing) + return; + switch (bf(t)) + { + case AVLLEFTHEAVY: + set_bf(t, AVLBALANCED); + return; + case AVLBALANCED: + set_bf(t, AVLRIGHTHEAVY); + _need_rebalancing = 0; + return; + case AVLRIGHTHEAVY: + { + RAVLNode* r = t->rt; + switch (bf(r)) + { + case AVLBALANCED: + r->rank += t->rank; + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLRIGHTHEAVY); + set_bf(r, AVLLEFTHEAVY); + _need_rebalancing = 0; + t = r; + return; + case AVLRIGHTHEAVY: + r->rank += t->rank; + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + return; + case AVLLEFTHEAVY: + { + RAVLNode* l = r->lt; + r->rank -= l->rank; + l->rank += t->rank; + set_lthread(r, rthread(l)); + if (rthread(l)) + r->lt = l; + else + r->lt = l->rt; + l->rt = r; + set_rthread(l, 0); + set_rthread(t, lthread(l)); + if (lthread(l)) + t->rt = l; + else + t->rt = l->lt; + l->lt = t; + set_lthread(l, 0); + if (bf(l) == AVLRIGHTHEAVY) + set_bf(t, AVLLEFTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(l) == AVLLEFTHEAVY) + set_bf(r, AVLRIGHTHEAVY); + else + set_bf(r, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + return; + } + } + } + } + } + else + { + if (rthread(t)) + return; + _del(t, t->rt); + if (!_need_rebalancing) + return; + switch (bf(t)) + { + case AVLRIGHTHEAVY: + set_bf(t, AVLBALANCED); + return; + case AVLBALANCED: + set_bf(t, AVLLEFTHEAVY); + _need_rebalancing = 0; + return; + case AVLLEFTHEAVY: + { + RAVLNode* l = t->lt; + switch (bf(l)) + { + case AVLBALANCED: + t->rank -= l->rank; + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLLEFTHEAVY); + set_bf(l, AVLRIGHTHEAVY); + _need_rebalancing = 0; + t = l; + return; + case AVLLEFTHEAVY: + t->rank -= l->rank; + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + return; + case AVLRIGHTHEAVY: + { + RAVLNode* r = l->rt; + r->rank += l->rank; + t->rank -= r->rank; + set_rthread(l, lthread(r)); + if (lthread(r)) + l->rt = r; + else + l->rt = r->lt; + r->lt = l; + set_lthread(r, 0); + set_lthread(t, rthread(r)); + if (rthread(r)) + t->lt = r; + else + t->lt = r->rt; + r->rt = t; + set_rthread(r, 0); + if (bf(r) == AVLLEFTHEAVY) + set_bf(t, AVLRIGHTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(r) == AVLRIGHTHEAVY) + set_bf(l, AVLLEFTHEAVY); + else + set_bf(l, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + return; + } + } + } + } + } +} + + +void RAVLMap::del( item) +{ + if (root == 0) return; + _need_rebalancing = 0; + _already_found = 0; + _found_node = 0; + _rank_changed = 0; + _target_item = &item; + _del(root, root); + if (_found_node) + { + delete(_found_node); + if (--count == 0) + root = 0; + } +} + +void RAVLMap::_kill(RAVLNode* t) +{ + if (t != 0) + { + if (!lthread(t)) _kill(t->lt); + if (!rthread(t)) _kill(t->rt); + delete t; + } +} + + +RAVLMap::RAVLMap(RAVLMap& b) :Map(b.def) +{ + root = 0; + count = 0; + for (Pix i = b.first(); i != 0; b.next(i)) + (*this)[b.key(i)] = b.contents(i); +} + + +int RAVLMap::OK() +{ + int v = 1; + if (root == 0) + v = count == 0; + else + { + int n = 1; + RAVLNode* trail = leftmost(); + v &= rankof(trail->item) == n; + RAVLNode* t = succ(trail); + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + v &= rankof(t->item) == n; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/RAVLMap.hP b/gnu/lib/libg++/g++-include/RAVLMap.hP new file mode 100644 index 0000000000..d3c523ee29 --- /dev/null +++ b/gnu/lib/libg++/g++-include/RAVLMap.hP @@ -0,0 +1,147 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + ranking code from Paul Anderson (paul%lfcs.ed.ac.uk) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _RAVLMap_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _RAVLMap_h 1 + +#include "..Map.h" + +struct RAVLNode +{ + RAVLNode* lt; + RAVLNode* rt; + item; + cont; + int rank; + char stat; + RAVLNode( h, c, + RAVLNode* l=0, RAVLNode* r=0, int k=1); + ~RAVLNode(); +}; + +inline RAVLNode::RAVLNode( h, c, + RAVLNode* l, RAVLNode* r, int k) + :item(h), cont(c), lt(l), rt(r), rank(k), stat(0) {} + +inline RAVLNode::~RAVLNode() {} + +typedef RAVLNode* RAVLNodePtr; + + +class RAVLMap : public Map +{ +protected: + RAVLNode* root; + + RAVLNode* leftmost(); + RAVLNode* rightmost(); + RAVLNode* pred(RAVLNode* t); + RAVLNode* succ(RAVLNode* t); + void _kill(RAVLNode* t); + void _add(RAVLNode*& t); + void _del(RAVLNode* p, RAVLNode*& t); + +public: + RAVLMap( dflt); + RAVLMap(RAVLMap& a); + ~RAVLMap(); + + & operator [] ( key); + + void del( key); + + Pix first(); + void next(Pix& i); + & key(Pix i); + & contents(Pix i); + + Pix seek( key); + int contains( key); + + Pix ranktoPix(int i); + int rankof( key); + + void clear(); + + Pix last(); + void prev(Pix& i); + + int OK(); +}; + +inline RAVLMap::~RAVLMap() +{ + _kill(root); +} + +inline RAVLMap::RAVLMap( dflt) :Map(dflt) +{ + root = 0; +} + + +inline Pix RAVLMap::first() +{ + return Pix(leftmost()); +} + +inline Pix RAVLMap::last() +{ + return Pix(rightmost()); +} + +inline void RAVLMap::next(Pix& i) +{ + if (i != 0) i = Pix(succ((RAVLNode*)i)); +} + +inline void RAVLMap::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((RAVLNode*)i)); +} + +inline & RAVLMap::key(Pix i) +{ + if (i == 0) error("null Pix"); + return ((RAVLNode*)i)->item; +} + +inline & RAVLMap::contents(Pix i) +{ + if (i == 0) error("null Pix"); + return ((RAVLNode*)i)->cont; +} + +inline void RAVLMap::clear() +{ + _kill(root); + count = 0; + root = 0; +} + +inline int RAVLMap::contains( key) +{ + return seek(key) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/RNG.h b/gnu/lib/libg++/g++-include/RNG.h deleted file mode 100644 index 414b57f3af..0000000000 --- a/gnu/lib/libg++/g++-include/RNG.h +++ /dev/null @@ -1,88 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ -#ifndef _RNG_h -#define _RNG_h 1 -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif - -#include -#include - -union PrivateRNGSingleType { // used to access floats as unsigneds - float s; - unsigned long u; -}; - -union PrivateRNGDoubleType { // used to access doubles as unsigneds - double d; - unsigned long u[2]; -}; - -// -// Base class for Random Number Generators. See ACG and MLCG for instances. -// -class RNG { - static PrivateRNGSingleType singleMantissa; // mantissa bit vector - static PrivateRNGDoubleType doubleMantissa; // mantissa bit vector -public: - RNG(); - // - // Return a long-words word of random bits - // - virtual unsigned long asLong() = 0; - virtual void reset() = 0; - // - // Return random bits converted to either a float or a double - // - float asFloat(); - double asDouble(); -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - - -inline float RNG::asFloat() -{ - PrivateRNGSingleType result; - result.s = 1.0; - result.u |= (asLong() & singleMantissa.u); - result.s -= 1.0; - assert( result.s < 1.0 && result.s >= 0); - return( result.s ); -} - -inline double RNG::asDouble() -{ - PrivateRNGDoubleType result; - result.d = 1.0; - result.u[0] |= (asLong() & doubleMantissa.u[0]); - result.u[1] |= (asLong() & doubleMantissa.u[1]); - result.d -= 1.0; - assert( result.d < 1.0 && result.d >= 0); - return( result.d ); -} - -#endif -#endif diff --git a/gnu/lib/libg++/g++-include/RPlex.ccP b/gnu/lib/libg++/g++-include/RPlex.ccP new file mode 100644 index 0000000000..0dbd2cee7e --- /dev/null +++ b/gnu/lib/libg++/g++-include/RPlex.ccP @@ -0,0 +1,477 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".RPlex.h" + +typedef IChunk* _IChunk_ptr; + +RPlex:: RPlex() +{ + lo = fnc = 0; + csize = DEFAULT_INITIAL_CAPACITY; + * data = new [csize]; + set_cache(new IChunk(data, lo, lo, fnc, lo+csize)); + hd = ch; + maxch = MIN_NCHUNKS; + lch = maxch / 2; + fch = lch + 1; + base = ch->base_index() - lch * csize; + chunks = new _IChunk_ptr[maxch]; + chunks[lch] = ch; +} + +RPlex:: RPlex(int chunksize) +{ + if (chunksize == 0) error("invalid constructor specification"); + lo = fnc = 0; + if (chunksize > 0) + { + csize = chunksize; + * data = new [csize]; + set_cache(new IChunk(data, lo, lo, fnc, csize+lo)); + hd = ch; + } + else + { + csize = -chunksize; + * data = new [csize]; + set_cache(new IChunk(data, chunksize+lo, lo, fnc, fnc)); + hd = ch; + } + maxch = MIN_NCHUNKS; + lch = maxch / 2; + fch = lch + 1; + base = ch->base_index() - lch * csize; + chunks = new _IChunk_ptr[maxch]; + chunks[lch] = ch; +} + + +RPlex:: RPlex(int l, int chunksize) +{ + if (chunksize == 0) error("invalid constructor specification"); + lo = fnc = l; + if (chunksize > 0) + { + csize = chunksize; + * data = new [csize]; + set_cache(new IChunk(data, lo, lo, fnc, lo+csize)); + hd = ch; + } + else + { + csize = -chunksize; + * data = new [csize]; + set_cache(new IChunk(data, chunksize+lo, lo, fnc, fnc)); + hd = ch; + } + maxch = MIN_NCHUNKS; + lch = maxch / 2; + fch = lch + 1; + base = ch->base_index() - lch * csize; + chunks = new _IChunk_ptr[maxch]; + chunks[lch] = ch; +} + +void RPlex::make_initial_chunks(int up) +{ + int count = 0; + int need = fnc - lo; + hd = 0; + if (up) + { + int l = lo; + do + { + ++count; + int sz; + if (need >= csize) + sz = csize; + else + sz = need; + * data = new [csize]; + IChunk* h = new IChunk(data, l, l, l+sz, l+csize); + if (hd != 0) + h->link_to_next(hd); + else + hd = h; + l += sz; + need -= sz; + } while (need > 0); + } + else + { + int hi = fnc; + do + { + ++count; + int sz; + if (need >= csize) + sz = csize; + else + sz = need; + * data = new [csize]; + IChunk* h = new IChunk(data, hi-csize, hi-sz, hi, hi); + if (hd != 0) + h->link_to_next(hd); + hd = h; + hi -= sz; + need -= sz; + } while (need > 0); + } + set_cache((IChunk*)hd); + + maxch = MIN_NCHUNKS; + if (maxch < count * 2) + maxch = count * 2; + chunks = new _IChunk_ptr[maxch]; + lch = maxch / 3; + fch = lch + count; + base = ch->base_index() - csize * lch; + int k = lch; + do + { + chunks[k++] = ch; + set_cache(ch->next()); + } while (ch != hd); +} + +RPlex:: RPlex(int l, int hi, const initval, int chunksize) +{ + lo = l; + fnc = hi + 1; + if (chunksize == 0) + { + csize = fnc - l; + make_initial_chunks(1); + } + else if (chunksize < 0) + { + csize = -chunksize; + make_initial_chunks(0); + } + else + { + csize = chunksize; + make_initial_chunks(1); + } + fill(initval); +} + +RPlex::RPlex(const RPlex& a) +{ + lo = a.lo; + fnc = a.fnc; + csize = a.csize; + make_initial_chunks(); + for (int i = a.low(); i < a.fence(); a.next(i)) (*this)[i] = a[i]; +} + +void RPlex::operator= (const RPlex& a) +{ + if (&a != this) + { + invalidate(); + lo = a.lo; + fnc = a.fnc; + csize = a.csize; + make_initial_chunks(); + for (int i = a.low(); i < a.fence(); a.next(i)) (*this)[i] = a[i]; + } +} + + +void RPlex::cache(const * p) const +{ + const IChunk* old = ch; + const IChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = (t->next()); + if (t == old) index_error(); + } + set_cache(t); +} + +int RPlex::owns(Pix px) const +{ + * p = (*)px; + const IChunk* old = ch; + const IChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = (t->next()); + if (t == old) return 0; + } + set_cache(t); + return 1; +} + + +* RPlex::dosucc(const * p) const +{ + if (p == 0) return 0; + const IChunk* old = ch; + const IChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = (t->next()); + if (t == old) return 0; + } + int i = t->index_of(p) + 1; + if (i >= fnc) return 0; + if (i >= t->fence_index()) t = (t->next()); + set_cache(t); + return t->pointer_to(i); +} + +* RPlex::dopred(const * p) const +{ + if (p == 0) return 0; + const IChunk* old = ch; + const IChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = (t->prev()); + if (t == old) return 0; + } + int i = t->index_of(p) - 1; + if (i < lo) return 0; + if (i < t->low_index()) t = (t->prev()); + set_cache(t); + return (t->pointer_to(i)); +} + +int RPlex::add_high(const elem) +{ + IChunk* t = tl(); + if (!t->can_grow_high()) + { + if (t->IChunk::empty() && one_chunk()) + { + t->clear(fnc); + base = t->base_index() - lch * csize; + } + else + { + * data = new [csize]; + t = (new IChunk(data, fnc, fnc, fnc,fnc+csize)); + t->link_to_prev(tl()); + if (fch == maxch) + { + maxch *= 2; + IChunk** newch = new _IChunk_ptr [maxch]; + memcpy(newch, chunks, fch * sizeof(_IChunk_ptr)); + delete chunks; + chunks = newch; + } + chunks[fch++] = t; + } + } + *((t->IChunk::grow_high())) = elem; + set_cache(t); + return fnc++; +} + +int RPlex::del_high () +{ + if (empty()) empty_error(); + IChunk* t = tl(); + if (t->IChunk::empty()) // kill straggler first + { + IChunk* pred = t->prev(); + del_chunk(t); + t = (pred); + --fch; + } + t->IChunk::shrink_high(); + if (t->IChunk::empty() && !one_chunk()) + { + IChunk* pred = t->prev(); + del_chunk(t); + t = (pred); + --fch; + } + set_cache(t); + return --fnc - 1; +} + +int RPlex::add_low (const elem) +{ + IChunk* t = hd; + if (!t->can_grow_low()) + { + if (t->IChunk::empty() && one_chunk()) + { + t->cleardown(lo); + base = t->base_index() - lch * csize; + } + else + { + * data = new [csize]; + hd = new IChunk(data, lo-csize, lo, lo, lo); + hd->link_to_next(t); + t = ( hd); + if (lch == 0) + { + lch = maxch; + fch += maxch; + maxch *= 2; + IChunk** newch = new _IChunk_ptr [maxch]; + memcpy(&(newch[lch]), chunks, lch * sizeof(_IChunk_ptr)); + delete chunks; + chunks = newch; + base = t->base_index() - (lch - 1) * csize; + } + chunks[--lch] = t; + } + } + *((t->IChunk::grow_low())) = elem; + set_cache(t); + return --lo; +} + + +int RPlex::del_low () +{ + if (empty()) empty_error(); + IChunk* t = hd; + if (t->IChunk::empty()) + { + hd = t->next(); + del_chunk(t); + t = hd; + ++lch; + } + t->IChunk::shrink_low(); + if (t->IChunk::empty() && !one_chunk()) + { + hd = t->next(); + del_chunk(t); + t = hd; + ++lch; + } + set_cache(t); + return ++lo; +} + +void RPlex::reverse() +{ + tmp; + int l = lo; + int h = fnc - 1; + IChunk* loch = hd; + IChunk* hich = tl(); + while (l < h) + { + * lptr = loch->pointer_to(l); + * hptr = hich->pointer_to(h); + tmp = *lptr; + *lptr = *hptr; + *hptr = tmp; + if (++l >= loch->fence_index()) loch = loch->next(); + if (--h < hich->low_index()) hich = hich->prev(); + } +} + +void RPlex::fill(const x) +{ + for (int i = lo; i < fnc; ++i) (*this)[i] = x; +} + +void RPlex::fill(const x, int lo, int hi) +{ + for (int i = lo; i <= hi; ++i) (*this)[i] = x; +} + + +void RPlex::clear() +{ + for (int i = lch + 1; i < fch; ++i) + del_chunk(chunks[i]); + fch = lch + 1; + set_cache(chunks[lch]); + ch->IChunk::clear(lo); + fnc = lo; +} + +int RPlex::reset_low(int l) +{ + int old = lo; + int diff = l - lo; + if (diff != 0) + { + lo += diff; + fnc += diff; + IChunk* t = hd; + do + { + t->re_index(t->low_index() + diff); + t = t->next(); + } while (t != hd); + } + base = hd->base_index() - lch * csize; + return old; +} + + +int RPlex::OK () const +{ + int v = hd != 0 && ch != 0; // at least one chunk + + v &= fnc == tl()->fence_index(); // last chunk fnc == plex fnc + v &= lo == hd->IChunk::low_index(); // first lo == plex lo + + v &= base == hd->base_index() - lch * csize; // base is correct; + v &= lch < fch; + v &= fch <= maxch; // within allocation; + +// loop for others: + + int k = lch; // to cross-check nch + + int found_ch = 0; // to make sure ch is in list; + const IChunk* t = (hd); + for (;;) + { + v &= chunks[k++] == t; // each chunk is at proper index + if (t == ch) ++found_ch; + v &= t->IChunk::OK(); // each chunk is OK + if (t == tl()) + break; + else // and has indices contiguous to succ + { + v &= t->top_index() == t->next()->base_index(); + if (t != hd) // internal chunks full + { + v &= !t->empty(); + v &= !t->can_grow_low(); + v &= !t->can_grow_high(); + } + t = t->next(); + } + } + v &= found_ch == 1; + v &= fch == k; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/RPlex.hP b/gnu/lib/libg++/g++-include/RPlex.hP new file mode 100644 index 0000000000..ed28c357e4 --- /dev/null +++ b/gnu/lib/libg++/g++-include/RPlex.hP @@ -0,0 +1,257 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _RPlex_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _RPlex_h 1 + +#include ".Plex.h" + +// minimum number of chunks to index + +#ifndef MIN_NCHUNKS +#define MIN_NCHUNKS 16 +#endif + +class RPlex: public Plex +{ + int base; // base index of lowest chunk + int lch; // index of lowest used chunk + int fch; // 1 + index of highest used chunk + int maxch; // max chunks in array + IChunk** chunks; // array of chunks + IChunk* ch; // cached chunk + + void make_initial_chunks(int up = 1); + + void cache(int idx) const; + void cache(const * p) const; + * dopred(const * p) const; + * dosucc(const * p) const; + + void set_cache(const IChunk* t) const; // logically, + // not physically const + +public: + RPlex(); // set low = 0; + // fence = 0; + // csize = default + + RPlex(int ch_size); // low = 0; + // fence = 0; + // csize = ch_size + + RPlex(int lo, // low = lo; + int ch_size); // fence=lo + // csize = ch_size + + RPlex(int lo, // low = lo + int hi, // fence = hi+1 + const initval,// fill with initval, + int ch_size = 0); // csize= ch_size + // or fence-lo if 0 + + RPlex(const RPlex&); + + ~RPlex(); + + void operator= (const RPlex&); + +// virtuals + + & high_element (); + & low_element (); + + const & high_element () const; + const & low_element () const; + + Pix first() const; + Pix last() const; + void prev(Pix& ptr) const; + void next(Pix& ptr) const; + int owns(Pix p) const; + & operator () (Pix p); + const & operator () (Pix p) const; + + int low() const; + int high() const; + int valid(int idx) const; + void prev(int& idx) const; + void next(int& x) const; + & operator [] (int index); + const & operator [] (int index) const; + + int Pix_to_index(Pix p) const; + Pix index_to_Pix(int idx) const; + + int can_add_high() const; + int can_add_low() const; + int full() const; + + int add_high(const elem); + int del_high (); + int add_low (const elem); + int del_low (); + + void fill(const x); + void fill(const x, int from, int to); + void clear(); + void reverse(); + + int reset_low(int newlow); + + int OK () const; +}; + + +inline void RPlex::prev(int& idx) const +{ + --idx; +} + +inline void RPlex::next(int& idx) const +{ + ++idx; +} + +inline int RPlex::full () const +{ + return 0; +} + +inline int RPlex::can_add_high() const +{ + return 1; +} + +inline int RPlex::can_add_low() const +{ + return 1; +} + +inline int RPlex::valid (int idx) const +{ + return idx >= lo && idx < fnc; +} + +inline int RPlex::low() const +{ + return lo; +} + +inline int RPlex::high() const +{ + return fnc - 1; +} + +inline void RPlex::set_cache(const IChunk* t) const +{ + ((RPlex*)(this))->ch = (IChunk*)t; +} + +inline void RPlex::cache(int idx) const +{ + if (idx < lo || idx >= fnc) index_error(); + set_cache(chunks[(idx - base) / csize]); +} + +inline & RPlex::low_element () +{ + cache(lo); return *(ch->pointer_to(lo)); +} + +inline & RPlex::high_element () +{ + cache(fnc-1); return *(ch->pointer_to(fnc - 1)); +} + +inline const & RPlex::low_element () const +{ + cache(lo); return *((const *)(ch->pointer_to(lo))); +} + +inline const & RPlex::high_element () const +{ + cache(fnc-1); return *((const *)(ch->pointer_to(fnc - 1))); +} + +inline int RPlex::Pix_to_index(Pix px) const +{ + * p = (*)px; + if (!ch->actual_pointer(p)) cache(p); + return ch->index_of(p); +} + +inline Pix RPlex::index_to_Pix(int idx) const +{ + if (!ch->actual_index(idx)) cache(idx); + return (Pix)(ch->pointer_to(idx)); +} + +inline Pix RPlex::first() const +{ + return Pix(hd->IChunk::first_pointer()); +} + +inline Pix RPlex::last() const +{ + return Pix(tl()->IChunk::last_pointer()); +} + +inline void RPlex::prev(Pix& p) const +{ + Pix q = Pix(ch->IChunk::pred((*)p)); + p = (q == 0)? Pix(dopred((*)p)) : q; +} + +inline void RPlex::next(Pix& p) const +{ + Pix q = Pix(ch->IChunk::succ((*)p)); + p = (q == 0)? Pix(dosucc((*)p)) : q; +} + +inline & RPlex:: operator () (Pix p) +{ + return *((*)p); +} + + +inline & RPlex:: operator [] (int idx) +{ + cache(idx); return *(ch->pointer_to(idx)); +} + +inline const & RPlex:: operator () (Pix p) const +{ + return *((const *)p); +} + +inline const & RPlex:: operator [] (int idx) const +{ + cache(idx); return *((const *)(ch->pointer_to(idx))); +} + +inline RPlex::~RPlex() +{ + delete chunks; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/Random.h b/gnu/lib/libg++/g++-include/Random.h deleted file mode 100644 index 94efa09fc0..0000000000 --- a/gnu/lib/libg++/g++-include/Random.h +++ /dev/null @@ -1,67 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ -#ifndef _Random_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Random_h 1 -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif - -#include -#include "RNG.h" - -class Random { -protected: - RNG *pGenerator; -public: - Random(RNG *generator); - virtual double operator()() = 0; - - RNG *generator(); - void generator(RNG *p); -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline Random::Random(RNG *gen) -{ - pGenerator = gen; -} - -inline RNG *Random::generator() -{ - return(pGenerator); -} - -inline void Random::generator(RNG *p) -{ - pGenerator = p; -} - -#endif - -#endif diff --git a/gnu/lib/libg++/g++-include/Rational.h b/gnu/lib/libg++/g++-include/Rational.h deleted file mode 100644 index 3e7e35f1b7..0000000000 --- a/gnu/lib/libg++/g++-include/Rational.h +++ /dev/null @@ -1,270 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- - -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifndef _Rational_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Rational_h 1 - -#include -#include - -class Rational -{ -protected: - Integer num; - Integer den; - - void normalize(); - -public: - Rational(); - Rational(double); - Rational(long n, long d = 1); - Rational(const Integer& n); - Rational(const Integer& n, const Integer& d); - Rational(const Rational&); - - ~Rational(); - - void operator = (const Rational& y); - - friend int operator == (const Rational& x, const Rational& y); - friend int operator != (const Rational& x, const Rational& y); - friend int operator < (const Rational& x, const Rational& y); - friend int operator <= (const Rational& x, const Rational& y); - friend int operator > (const Rational& x, const Rational& y); - friend int operator >= (const Rational& x, const Rational& y); - - friend Rational operator + (const Rational& x, const Rational& y); - friend Rational operator - (const Rational& x, const Rational& y); - friend Rational operator * (const Rational& x, const Rational& y); - friend Rational operator / (const Rational& x, const Rational& y); - - void operator += (const Rational& y); - void operator -= (const Rational& y); - void operator *= (const Rational& y); - void operator /= (const Rational& y); - -#ifdef __GNUG__ - friend Rational operator ? (const Rational& x, const Rational& y); // max -#endif - - friend Rational operator - (const Rational& x); - - -// builtin Rational functions - - - void negate(); // x = -x - void invert(); // x = 1/x - - friend int sign(const Rational& x); // -1, 0, or +1 - friend Rational abs(const Rational& x); // absolute value - friend Rational sqr(const Rational& x); // square - friend Rational pow(const Rational& x, long y); - friend Rational pow(const Rational& x, Integer& y); - const Integer& numerator() const; - const Integer& denominator() const; - -// coercion & conversion - - operator double() const; - friend Integer floor(const Rational& x); - friend Integer ceil(const Rational& x); - friend Integer trunc(const Rational& x); - friend Integer round(const Rational& x); - - friend istream& operator >> (istream& s, Rational& y); - friend ostream& operator << (ostream& s, const Rational& y); - - -// procedural versions of operators - - friend int compare(const Rational& x, const Rational& y); - friend void add(const Rational& x, const Rational& y, Rational& dest); - friend void sub(const Rational& x, const Rational& y, Rational& dest); - friend void mul(const Rational& x, const Rational& y, Rational& dest); - friend void div(const Rational& x, const Rational& y, Rational& dest); - -// error detection - - volatile void error(const char* msg) const; - int OK() const; - -}; - -typedef Rational RatTmp; // backwards compatibility - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline Rational::Rational() {} -inline Rational::~Rational() {} - -inline Rational::Rational(const Rational& y) :num(y.num), den(y.den) {} - -inline Rational::Rational(const Integer& n) :num(n), den(1) {} - -inline Rational::Rational(const Integer& n, const Integer& d) :num(n),den(d) -{ - normalize(); -} - -inline Rational::Rational(long n, long d) :num(n), den(d) -{ - normalize(); -} - -inline void Rational::operator = (const Rational& y) -{ - num = y.num; den = y.den; -} - -inline int operator == (const Rational& x, const Rational& y) -{ - return compare(x.num, y.num) == 0 && compare(x.den, y.den) == 0; -} - -inline int operator != (const Rational& x, const Rational& y) -{ - return compare(x.num, y.num) != 0 || compare(x.den, y.den) != 0; -} - -inline int operator < (const Rational& x, const Rational& y) -{ - return compare(x, y) < 0; -} - -inline int operator <= (const Rational& x, const Rational& y) -{ - return compare(x, y) <= 0; -} - -inline int operator > (const Rational& x, const Rational& y) -{ - return compare(x, y) > 0; -} - -inline int operator >= (const Rational& x, const Rational& y) -{ - return compare(x, y) >= 0; -} - -inline int sign(const Rational& x) -{ - return sign(x.num); -} - -inline void Rational::negate() -{ - num.negate(); -} - - -inline void Rational::operator += (const Rational& y) -{ - add(*this, y, *this); -} - -inline void Rational::operator -= (const Rational& y) -{ - sub(*this, y, *this); -} - -inline void Rational::operator *= (const Rational& y) -{ - mul(*this, y, *this); -} - -inline void Rational::operator /= (const Rational& y) -{ - div(*this, y, *this); -} - -inline const Integer& Rational::numerator() const { return num; } -inline const Integer& Rational::denominator() const { return den; } -inline Rational::operator double() const { return ratio(num, den); } - -#ifdef __GNUG__ -inline Rational operator ? (const Rational& x, const Rational& y) -{ - if (compare(x, y) >= 0) return x; else return y; -} -#endif - -#if defined(__GNUG__) && !defined(NO_NRV) - -inline Rational operator + (const Rational& x, const Rational& y) return r -{ - add(x, y, r); -} - -inline Rational operator - (const Rational& x, const Rational& y) return r -{ - sub(x, y, r); -} - -inline Rational operator * (const Rational& x, const Rational& y) return r -{ - mul(x, y, r); -} - -inline Rational operator / (const Rational& x, const Rational& y) return r -{ - div(x, y, r); -} - -#else /* NO_NRV */ - -inline Rational operator + (const Rational& x, const Rational& y) -{ - Rational r; add(x, y, r); return r; -} - -inline Rational operator - (const Rational& x, const Rational& y) -{ - Rational r; sub(x, y, r); return r; -} - -inline Rational operator * (const Rational& x, const Rational& y) -{ - Rational r; mul(x, y, r); return r; -} - -inline Rational operator / (const Rational& x, const Rational& y) -{ - Rational r; div(x, y, r); return r; -} -#endif -#endif - -#endif diff --git a/gnu/lib/libg++/g++-include/Regex.h b/gnu/lib/libg++/g++-include/Regex.h deleted file mode 100644 index d0cff263f2..0000000000 --- a/gnu/lib/libg++/g++-include/Regex.h +++ /dev/null @@ -1,76 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - - -#ifndef _Regex_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Regex_h 1 - -struct re_pattern_buffer; // defined elsewhere -struct re_registers; - -class Regex -{ -private: - - Regex(const Regex&) {} // no X(X&) - void operator = (const Regex&) {} // no assignment - -protected: - re_pattern_buffer* buf; - re_registers* reg; - -public: - Regex(const char* t, - int fast = 0, - int bufsize = 40, - const char* transtable = 0); - - ~Regex(); - - int match(const char* s, int len, int pos = 0) const; - int search(const char* s, int len, - int& matchlen, int startpos = 0) const; - int match_info(int& start, int& length, int nth = 0) const; - - int OK() const; // representation invariant -}; - -// some built in regular expressions - -extern const Regex RXwhite; // = "[ \n\t\r\v\f]+" -extern const Regex RXint; // = "-?[0-9]+" -extern const Regex RXdouble; // = "-?\\(\\([0-9]+\\.[0-9]*\\)\\| - // \\([0-9]+\\)\\|\\(\\.[0-9]+\\)\\) - // \\([eE][---+]?[0-9]+\\)?" -extern const Regex RXalpha; // = "[A-Za-z]+" -extern const Regex RXlowercase; // = "[a-z]+" -extern const Regex RXuppercase; // = "[A-Z]+" -extern const Regex RXalphanum; // = "[0-9A-Za-z]+" -extern const Regex RXidentifier; // = "[A-Za-z_][A-Za-z0-9_]*" - - -#endif diff --git a/gnu/lib/libg++/g++-include/RndInt.h b/gnu/lib/libg++/g++-include/RndInt.h deleted file mode 100644 index f674796f05..0000000000 --- a/gnu/lib/libg++/g++-include/RndInt.h +++ /dev/null @@ -1,169 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1990 Free Software Foundation - adapted from a submission from John Reidl - - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifndef _RandomInteger_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _RandomInteger_h 1 - -// RandomInteger uses a random number generator to generate an integer -// in a specified range. By default the range is 0..1. Since in my -// experience random numbers are often needed for a wide variety of -// ranges in the same program, this generator accepts a new low or high value -// as an argument to the asLong and operator() methods to temporarily -// override stored values - -#include -#include "RNG.h" - -class RandomInteger -{ - protected: - RNG *pGenerator; - long pLow; - long pHigh; - - long _asLong(long, long); - - public: - - RandomInteger(long low, long high, RNG *gen); - RandomInteger(long high, RNG *gen); - RandomInteger(RNG *gen); - -// read params - - long low() const; - long high() const; - RNG* generator() const; - -// change params - - long low(long x); - long high(long x); - RNG* generator(RNG *gen); - -// get a random number - - long asLong(); - long operator()(); // synonym for asLong - int asInt(); // (possibly) truncate as int - -// override params for one shot - - long asLong(long high); - long asLong(long low, long high); - - long operator () (long high); // synonyms - long operator () (long low, long high); - -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline RandomInteger::RandomInteger(long low, long high, RNG *gen) - : pLow((low < high) ? low : high), - pHigh((low < high) ? high : low), - pGenerator(gen) -{} - -inline RandomInteger::RandomInteger(long high, RNG *gen) - : pLow((0 < high) ? 0 : high), - pHigh((0 < high) ? high : 0), - pGenerator(gen) -{} - - -inline RandomInteger::RandomInteger(RNG *gen) - : pLow(0), - pHigh(1), - pGenerator(gen) -{} - -inline RNG* RandomInteger::generator() const { return pGenerator;} -inline long RandomInteger::low() const { return pLow; } -inline long RandomInteger::high() const { return pHigh; } - -inline RNG* RandomInteger::generator(RNG *gen) -{ - RNG *tmp = pGenerator; pGenerator = gen; return tmp; -} - -inline long RandomInteger::low(long x) -{ - long tmp = pLow; pLow = x; return tmp; -} - -inline long RandomInteger:: high(long x) -{ - long tmp = pHigh; pHigh = x; return tmp; -} - -inline long RandomInteger:: _asLong(long low, long high) -{ - return (pGenerator->asLong() % (high-low+1)) + low; -} - - -inline long RandomInteger:: asLong() -{ - return _asLong(pLow, pHigh); -} - -inline long RandomInteger:: asLong(long high) -{ - return _asLong(pLow, high); -} - -inline long RandomInteger:: asLong(long low, long high) -{ - return _asLong(low, high); -} - -inline long RandomInteger:: operator () () -{ - return _asLong(pLow, pHigh); -} - -inline long RandomInteger:: operator () (long high) -{ - return _asLong(pLow, high); -} - -inline long RandomInteger:: operator () (long low, long high) -{ - return _asLong(low, high); -} - - - - -inline int RandomInteger:: asInt() -{ - return int(asLong()); -} - -#endif - -#endif diff --git a/gnu/lib/libg++/g++-include/SFile.h b/gnu/lib/libg++/g++-include/SFile.h deleted file mode 100644 index 598078ed8f..0000000000 --- a/gnu/lib/libg++/g++-include/SFile.h +++ /dev/null @@ -1,88 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifndef _SFile_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _SFile_h 1 - -#include - -class SFile: public File -{ -protected: - int sz; // unit size for structured binary IO - -public: - SFile(); - SFile(const char* filename, int size, io_mode m, access_mode a); - SFile(const char* filename, int size, const char* m); - SFile(int filedesc, int size, io_mode m); - SFile(FILE* fileptr, int size); - - ~SFile(); - - int size(); - int setsize(int s); - - SFile& get(void* x); - SFile& put(void* x); - SFile& operator[](long i); -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - - -inline int SFile::size() -{ - return sz; -} - -inline int SFile::setsize(int s) -{ - int old = sz; - sz = s; - return old; -} - -inline SFile& SFile::get(void* x) -{ - read(x, sz, 1); return *this; -} - -inline SFile& SFile::put(void* x) -{ - write(x, sz, 1); return *this; -} - -inline SFile& SFile::operator[](long i) -{ - seek(i * sz, 0); return *this; -} - - -#endif - -#endif diff --git a/gnu/lib/libg++/g++-include/SLBag.ccP b/gnu/lib/libg++/g++-include/SLBag.ccP new file mode 100644 index 0000000000..50d2447c7d --- /dev/null +++ b/gnu/lib/libg++/g++-include/SLBag.ccP @@ -0,0 +1,105 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".SLBag.h" + +int SLBag::OK() +{ + int v = p.OK(); + v &= count == p.length(); + if (!v) error("invariant failure"); + return v; +} + +Pix SLBag::seek( item, Pix i) +{ + if (i == 0) i = first(); else next(i); + for (; i != 0 && (!(EQ(p(i), item))); p.next(i)); + return i; +} + +int SLBag::nof( item) +{ + int n = 0; + for (Pix p = first(); p; next(p)) if (EQ((*this)(p), item)) ++n; + return n; +} + + +void SLBag::del( item) +{ + Pix i = p.first(); + if (i == 0) + return; + else if (EQ(p(i), item)) + { + --count; + p.del_front(); + } + else + { + Pix trail = i; + p.next(i); + while (i != 0) + { + if (EQ(p(i), item)) + { + --count; + p.del_after(trail); + return; + } + trail = i; + p.next(i); + } + } +} + +void SLBag::remove( item) +{ + Pix i = p.first(); + while (i != 0 && EQ(p(i), item)) + { + --count; + p.del_front(); + i = p.first(); + } + if (i != 0) + { + Pix trail = i; + p.next(i); + while (i != 0) + { + if (EQ(p(i), item)) + { + --count; + p.del_after(trail); + i = trail; + p.next(i); + } + else + { + trail = i; + p.next(i); + } + } + } +} + diff --git a/gnu/lib/libg++/g++-include/SLBag.hP b/gnu/lib/libg++/g++-include/SLBag.hP new file mode 100644 index 0000000000..edb648e9d3 --- /dev/null +++ b/gnu/lib/libg++/g++-include/SLBag.hP @@ -0,0 +1,96 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _SLBag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SLBag_h 1 + +#include ".Bag.h" +#include ".SLList.h" + +class SLBag : public Bag +{ +protected: + SLList p; + +public: + SLBag(); + SLBag(const SLBag&); + + Pix add( item); + void del( item); + void remove(item); + int contains( item); + int nof( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item, Pix from = 0); + + int OK(); +}; + + +inline SLBag::SLBag() : p() { count = 0; } + +inline SLBag::SLBag(const SLBag& s) : p(s.p) { count = s.count; } + +inline Pix SLBag::first() +{ + return p.first(); +} + +inline void SLBag::next(Pix & idx) +{ + p.next(idx); +} + +inline & SLBag::operator ()(Pix idx) +{ + return p(idx); +} + +inline void SLBag::clear() +{ + count = 0; p.clear(); +} + +inline int SLBag::owns (Pix idx) +{ + return p.owns(idx); +} + +inline Pix SLBag::add( item) +{ + ++count; + return p.append(item); +} + +inline int SLBag::contains( item) +{ + return seek(item) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/SLList.ccP b/gnu/lib/libg++/g++-include/SLList.ccP new file mode 100644 index 0000000000..3ebd8d5bc9 --- /dev/null +++ b/gnu/lib/libg++/g++-include/SLList.ccP @@ -0,0 +1,292 @@ +// This may look like C code, but it is really -*- C++ -*- +// WARNING: This file is obsolete. Use ../SLList.cc, if you can. +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include +#include +#include ".SLList.h" + +void SLList::error(const char* msg) +{ + (*lib_error_handler)("SLList", msg); +} + +int SLList::length() +{ + int l = 0; + SLListNode* t = last; + if (t != 0) do { ++l; t = t->tl; } while (t != last); + return l; +} + +SLList::SLList(const SLList& a) +{ + if (a.last == 0) + last = 0; + else + { + SLListNode* p = a.last->tl; + SLListNode* h = new SLListNode(p->hd); + last = h; + for (;;) + { + if (p == a.last) + { + last->tl = h; + return; + } + p = p->tl; + SLListNode* n = new SLListNode(p->hd); + last->tl = n; + last = n; + } + } +} + +SLList& SLList::operator = (const SLList& a) +{ + if (last != a.last) + { + clear(); + if (a.last != 0) + { + SLListNode* p = a.last->tl; + SLListNode* h = new SLListNode(p->hd); + last = h; + for (;;) + { + if (p == a.last) + { + last->tl = h; + break; + } + p = p->tl; + SLListNode* n = new SLListNode(p->hd); + last->tl = n; + last = n; + } + } + } + return *this; +} + +void SLList::clear() +{ + if (last == 0) + return; + + SLListNode* p = last->tl; + last->tl = 0; + last = 0; + + while (p != 0) + { + SLListNode* nxt = p->tl; + delete(p); + p = nxt; + } +} + + +Pix SLList::prepend( item) +{ + SLListNode* t = new SLListNode(item); + if (last == 0) + t->tl = last = t; + else + { + t->tl = last->tl; + last->tl = t; + } + return Pix(t); +} + + +Pix SLList::prepend(SLListNode* t) +{ + if (t == 0) return 0; + if (last == 0) + t->tl = last = t; + else + { + t->tl = last->tl; + last->tl = t; + } + return Pix(t); +} + + +Pix SLList::append( item) +{ + SLListNode* t = new SLListNode(item); + if (last == 0) + t->tl = last = t; + else + { + t->tl = last->tl; + last->tl = t; + last = t; + } + return Pix(t); +} + +Pix SLList::append(SLListNode* t) +{ + if (t == 0) return 0; + if (last == 0) + t->tl = last = t; + else + { + t->tl = last->tl; + last->tl = t; + last = t; + } + return Pix(t); +} + +void SLList::join(SLList& b) +{ + SLListNode* t = b.last; + b.last = 0; + if (last == 0) + last = t; + else if (t != 0) + { + SLListNode* f = last->tl; + last->tl = t->tl; + t->tl = f; + last = t; + } +} + +Pix SLList::ins_after(Pix p, item) +{ + SLListNode* u = (SLListNode*)p; + SLListNode* t = new SLListNode(item); + if (last == 0) + t->tl = last = t; + else if (u == 0) // ins_after 0 means prepend + { + t->tl = last->tl; + last->tl = t; + } + else + { + t->tl = u->tl; + u->tl = t; + if (u == last) + last = t; + } + return Pix(t); +} + + +void SLList::del_after(Pix p) +{ + SLListNode* u = (SLListNode*)p; + if (last == 0 || u == last) error("cannot del_after last"); + if (u == 0) u = last; // del_after 0 means delete first + SLListNode* t = u->tl; + if (u == t) + last = 0; + else + { + u->tl = t->tl; + if (last == t) + last = u; + } + delete t; +} + +int SLList::owns(Pix p) +{ + SLListNode* t = last; + if (t != 0 && p != 0) + { + do + { + if (Pix(t) == p) return 1; + t = t->tl; + } while (t != last); + } + return 0; +} + + SLList::remove_front() +{ + if (last == 0) error("remove_front of empty list"); + SLListNode* t = last->tl; + res = t->hd; + if (t == last) + last = 0; + else + last->tl = t->tl; + delete t; + return res; +} + +int SLList::remove_front(& x) +{ + if (last == 0) + return 0; + else + { + SLListNode* t = last->tl; + x = t->hd; + if (t == last) + last = 0; + else + last->tl = t->tl; + delete t; + return 1; + } +} + + +void SLList::del_front() +{ + if (last == 0) error("del_front of empty list"); + SLListNode* t = last->tl; + if (t == last) + last = 0; + else + last->tl = t->tl; + delete t; +} + +int SLList::OK() +{ + int v = 1; + if (last != 0) + { + SLListNode* t = last; + long count = MAXLONG; // Lots of chances to find last! + do + { + count--; + t = t->tl; + } while (count > 0 && t != last); + v &= count > 0; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/SLList.hP b/gnu/lib/libg++/g++-include/SLList.hP new file mode 100644 index 0000000000..e5570bdf8f --- /dev/null +++ b/gnu/lib/libg++/g++-include/SLList.hP @@ -0,0 +1,137 @@ +// This may look like C code, but it is really -*- C++ -*- +// WARNING: This file is obsolete. Use ../SLList.h, if you can. +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _SLList_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SLList_h 1 + +#include +#include ".defs.h" + +#ifndef _SLListNode_h +#define _SLListNode_h 1 + +struct SLListNode +{ + SLListNode* tl; + hd; + SLListNode() { } + SLListNode(const h, SLListNode* t = 0); + ~SLListNode() { } +}; + + +inline SLListNode::SLListNode(const h, SLListNode* t) +:hd(h), tl(t) {} + +typedef SLListNode* SLListNodePtr; + +#endif + + +class SLList +{ +protected: + SLListNode* last; + +public: + SLList(); + SLList(const SLList& a); + ~SLList(); + + SLList& operator = (const SLList& a); + + int empty(); + int length(); + + void clear(); + + Pix prepend( item); + Pix append( item); + + void join(SLList&); + + Pix prepend(SLListNode*); + Pix append(SLListNode*); + + & operator () (Pix p); + Pix first(); + void next(Pix& p); + int owns(Pix p); + Pix ins_after(Pix p, item); + void del_after(Pix p); + + & front(); + & rear(); + remove_front(); + int remove_front(& x); + void del_front(); + + void error(const char* msg); + int OK(); +}; + +inline SLList::~SLList() +{ + clear(); +} + +inline SLList::SLList() +{ + last = 0; +} + +inline int SLList::empty() +{ + return last == 0; +} + + +inline Pix SLList::first() +{ + return (last == 0)? 0 : Pix(last->tl); +} + +inline void SLList::next(Pix& p) +{ + p = (p == 0 || p == last)? 0 : Pix(((SLListNode*)(p))->tl); +} + +inline & SLList::operator () (Pix p) +{ + if (p == 0) error("null Pix"); + return ((SLListNode*)(p))->hd; +} + +inline & SLList::front() +{ + if (last == 0) error("front: empty list"); + return last->tl->hd; +} + +inline & SLList::rear() +{ + if (last == 0) error("rear: empty list"); + return last->hd; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/SLQueue.ccP b/gnu/lib/libg++/g++-include/SLQueue.ccP new file mode 100644 index 0000000000..8a5935b775 --- /dev/null +++ b/gnu/lib/libg++/g++-include/SLQueue.ccP @@ -0,0 +1,4 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".SLQueue.h" diff --git a/gnu/lib/libg++/g++-include/SLQueue.hP b/gnu/lib/libg++/g++-include/SLQueue.hP new file mode 100644 index 0000000000..20fd74c9c5 --- /dev/null +++ b/gnu/lib/libg++/g++-include/SLQueue.hP @@ -0,0 +1,108 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _SLQueue_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SLQueue_h + +#include ".SLList.h" +#include ".Queue.h" + +class SLQueue : public Queue +{ + SLList p; + +public: + SLQueue(); + SLQueue(const SLQueue& q); + ~SLQueue(); + + void operator = (const SLQueue&); + + void enq( item); + deq(); + & front(); + void del_front(); + + void clear(); + int empty(); + int full(); + int length(); + + int OK(); +}; + +inline SLQueue::SLQueue() :p() {} +inline SLQueue::SLQueue(const SLQueue& q) : p(q.p) {} + +inline SLQueue::~SLQueue() {} + +inline void SLQueue::enq(item) +{ + p.append(item); +} + +inline SLQueue::deq() +{ + return p.remove_front(); +} + +inline & SLQueue::front() +{ + return p.front(); +} + + +inline void SLQueue::del_front() +{ + p.del_front(); +} + +inline void SLQueue::operator =(const SLQueue& s) +{ + p = s.p; +} + +inline int SLQueue::empty() +{ + return p.empty(); +} + +inline int SLQueue::full() +{ + return 0; +} + +inline int SLQueue::length() +{ + return p.length(); +} + +inline int SLQueue::OK() +{ + return p.OK(); +} + +inline void SLQueue::clear() +{ + p.clear(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/SLSet.ccP b/gnu/lib/libg++/g++-include/SLSet.ccP new file mode 100644 index 0000000000..5f580849ff --- /dev/null +++ b/gnu/lib/libg++/g++-include/SLSet.ccP @@ -0,0 +1,76 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".SLSet.h" + +int SLSet::OK() +{ + int v = p.OK(); + v &= count == p.length(); + if (!v) error("invariant failure"); + return v; +} + +Pix SLSet::seek( item) +{ + for (Pix i = p.first(); i != 0 && !EQ(p(i),item); p.next(i)); + return i; +} + +Pix SLSet::add( item) +{ + Pix i = seek(item); + if (i == 0) + { + ++count; + i = p.append(item); + } + return i; +} + +void SLSet::del( item) +{ + Pix i = p.first(); + if (i == 0) + return; + else if (EQ(p(i), item)) + { + --count; + p.del_front(); + } + else + { + Pix trail = i; + p.next(i); + while (i != 0) + { + if (EQ(p(i), item)) + { + --count; + p.del_after(trail); + return; + } + trail = i; + p.next(i); + } + } +} + diff --git a/gnu/lib/libg++/g++-include/SLSet.hP b/gnu/lib/libg++/g++-include/SLSet.hP new file mode 100644 index 0000000000..fcf153633f --- /dev/null +++ b/gnu/lib/libg++/g++-include/SLSet.hP @@ -0,0 +1,87 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _SLSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SLSet_h 1 + +#include ".Set.h" +#include ".SLList.h" + +class SLSet : public Set +{ +protected: + SLList p; + +public: + SLSet(); + SLSet(const SLSet&); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item); + + int OK(); +}; + +inline SLSet::SLSet() : p() { count = 0; } + +inline SLSet::SLSet(const SLSet& s) : p(s.p) { count = s.count; } + +inline Pix SLSet::first() +{ + return p.first(); +} + +inline void SLSet::next(Pix & idx) +{ + p.next(idx); +} + +inline & SLSet::operator ()(Pix idx) +{ + return p(idx); +} + +inline void SLSet::clear() +{ + count = 0; p.clear(); +} + +inline int SLSet::contains ( item) +{ + return seek(item) != 0; +} + +inline int SLSet::owns (Pix idx) +{ + return p.owns(idx); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/SLStack.ccP b/gnu/lib/libg++/g++-include/SLStack.ccP new file mode 100644 index 0000000000..3996b41fac --- /dev/null +++ b/gnu/lib/libg++/g++-include/SLStack.ccP @@ -0,0 +1,4 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".SLStack.h" diff --git a/gnu/lib/libg++/g++-include/SLStack.hP b/gnu/lib/libg++/g++-include/SLStack.hP new file mode 100644 index 0000000000..e20d9b9c2a --- /dev/null +++ b/gnu/lib/libg++/g++-include/SLStack.hP @@ -0,0 +1,109 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _SLStack_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SLStack_h 1 + +#include ".SLList.h" +#include ".Stack.h" + +class SLStack : public Stack +{ + SLList p; + +public: + SLStack(); + SLStack(const SLStack& s); + ~SLStack(); + + void operator = (const SLStack&); + + void push( item); + pop(); + & top(); + void del_top(); + + int empty(); + int full(); + int length(); + + void clear(); + + int OK(); + +}; + +inline SLStack::SLStack() :p() {} +inline SLStack::SLStack(const SLStack& a) : p(a.p) {} +inline SLStack::~SLStack() {} + +inline void SLStack::push( item) +{ + p.prepend(item); +} + +inline SLStack::pop() +{ + return p.remove_front(); +} + +inline & SLStack::top() +{ + return p.front(); +} + +inline void SLStack::del_top() +{ + p.del_front(); +} + +inline void SLStack::operator =(const SLStack& s) +{ + p = s.p; +} + +inline int SLStack::empty() +{ + return p.empty(); +} + +inline int SLStack::full() +{ + return 0; +} + +inline int SLStack::length() +{ + return p.length(); +} + +inline int SLStack::OK() +{ + return p.OK(); +} + +inline void SLStack::clear() +{ + p.clear(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/Set.ccP b/gnu/lib/libg++/g++-include/Set.ccP new file mode 100644 index 0000000000..f312aa15cc --- /dev/null +++ b/gnu/lib/libg++/g++-include/Set.ccP @@ -0,0 +1,116 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".Set.h" + + +Pix Set::seek( item) +{ + for (Pix i = first(); i != 0 && !(EQ((*this)(i), item)); next(i)); + return i; +} + +int Set::owns(Pix idx) +{ + if (idx == 0) return 0; + for (Pix i = first(); i; next(i)) if (i == idx) return 1; + return 0; +} + +void Set::clear() +{ + Pix i = first(); + while (i != 0) + { + del((*this)(i)); + i = first(); + } +} + +int Set::contains ( item) +{ + return seek(item) != 0; +} + +int Set::operator <= (Set& b) +{ + if (count > b.count) return 0; + if (count == 0) return 1; + for (Pix i = first(); i; next(i)) if (b.seek((*this)(i)) == 0) return 0; + return 1; +} + +int Set::operator == (Set& b) +{ + int n = count; + if (n != b.count) return 0; + if (n == 0) return 1; + Pix i = first(); + Pix j = b.first(); + while (n-- > 0) + { + if ((b.seek((*this)(i)) == 0) || (seek(b(j)) == 0)) return 0; + next(i); + b.next(j); + } + return 1; +} + +int Set::operator != (Set& b) +{ + return !(*this == b); +} + +void Set::operator |= (Set& b) +{ + if (&b != this) + for (Pix i = b.first(); i; b.next(i)) add(b(i)); +} + +void Set::operator -= (Set& b) +{ + if (&b == this) + clear(); + else + for (Pix i = b.first(); i; b.next(i)) del(b(i)); +} + + +void Set::operator &= (Set& b) +{ + if (&b != this) + { + Pix i = first(); + Pix n = i; + while (i != 0) + { + next(n); + if (b.seek((*this)(i)) == 0) del((*this)(i)); + i = n; + } + } +} + +void Set::error(const char* msg) +{ + (*lib_error_handler)("Set", msg); +} diff --git a/gnu/lib/libg++/g++-include/Set.hP b/gnu/lib/libg++/g++-include/Set.hP new file mode 100644 index 0000000000..6c21922627 --- /dev/null +++ b/gnu/lib/libg++/g++-include/Set.hP @@ -0,0 +1,78 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _Set_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Set_h 1 + +#include +#include ".defs.h" + +class Set +{ +protected: + + int count; + +public: + virtual ~Set(); + + int length(); // current number of items + int empty(); + + virtual Pix add( item) = 0; // add item; return Pix + virtual void del( item) = 0; // delete item + virtual int contains( item); // is item in set? + + virtual void clear(); // delete all items + + virtual Pix first() = 0; // Pix of first item or 0 + virtual void next(Pix& i) = 0; // advance to next or 0 + virtual & operator () (Pix i) = 0; // access item at i + + virtual int owns(Pix i); // is i a valid Pix ? + virtual Pix seek( item); // Pix of item + + void operator |= (Set& b); // add all items in b + void operator -= (Set& b); // delete items also in b + void operator &= (Set& b); // delete items not in b + + int operator == (Set& b); + int operator != (Set& b); + int operator <= (Set& b); + + void error(const char* msg); + virtual int OK() = 0; // rep invariant +}; + +inline Set::~Set() {} + +inline int Set::length() +{ + return count; +} + +inline int Set::empty() +{ + return count == 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/SkipBag.ccP b/gnu/lib/libg++/g++-include/SkipBag.ccP new file mode 100644 index 0000000000..cc347e9813 --- /dev/null +++ b/gnu/lib/libg++/g++-include/SkipBag.ccP @@ -0,0 +1,322 @@ +// This may look like C code, but it is really -*- C++ -*- + +/* +Copyright (C) 1991 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + * Bags implemented via William Pugh SkipList algorithms. + * CACM, June 1990, p 668-676. + * + */ + +#include +#include +#include ".SkipBag.h" + +MLCG* SkipBag::gen = 0; +int SkipBaginit::count = 0; + +static int countbits(long bits) +{ + int n = 0; + while(bits>>=1L) n++; + return n; +} + +SkipBag::SkipBag(long size) +: level(0), + header(new SkipBagNode (countbits(size)+1)), + max_levels (countbits(size)+1), + random_bits(gen->asLong()), + randoms_left(BITS_IN_RANDOM / 2) +{ + SkipBagNodePtr *buffer_start = header->forward; + SkipBagNodePtr *trav = &header->forward[max_levels]; + + count = 0; + while (trav > buffer_start) + *--trav = (SkipBagNodePtr) header; +} + +SkipBag::SkipBag(SkipBag& b) +: level (0), + header (new SkipBagNode (b.max_levels)), + max_levels (b.max_levels), + random_bits (gen->asLong()), + randoms_left (BITS_IN_RANDOM / 2) +{ + SkipBagNodePtr *buffer_start = header->forward; + SkipBagNodePtr *trav = &header->forward[max_levels]; + + count = 0; + + while (trav > buffer_start) + *--trav = (SkipBagNodePtr)header; + + for (SkipBagNodePtr t = b.leftmost(); t; t = b.succ(t)) + add(t->item); +} + +Pix SkipBag::add ( item) +{ + SkipBagNodePtr *update = new SkipBagNodePtr[max_levels+1]; + SkipBagNodePtr curr = (SkipBagNodePtr) this->header; + int l = level; + SkipBagNodePtr temp; + + do + { + while ((temp = curr->forward[l])!=header && + CMP(temp->item, item) < 0) + curr = temp; + update[l] = curr; + } + while (--l >= 0); + + if ((l = random_level ()) > level) + { + l = ++level; + update[l] = (SkipBagNodePtr)header; + }; + + temp = new RealSkipBagNode (item, l); + SkipBagNodePtr *temp_forward = temp->forward; + + do + { + SkipBagNodePtr *curr_forward = update[l]->forward; + + temp_forward[l] = curr_forward[l]; + curr_forward[l] = temp; + } + while (--l >= 0); + + count++; + delete update; + return Pix(temp); +} + +void SkipBag::del( key) +{ + + int l = level; + int curr_level = level; + SkipBagNodePtr *update = new SkipBagNodePtr[max_levels+1]; + SkipBagNodePtr curr = (SkipBagNodePtr)header; + SkipBagNodePtr temp; + + do + { + while ((temp = curr->forward[l])!=header + && CMP(temp->item,key) < 0) + curr = temp; + update[l] = curr; + } + while (--l >= 0); + + if (CMP(temp->item,key)==0) + { + SkipBagNodePtr *temp_forward = temp->forward; + + for (l = 0; + l <= curr_level && (curr = update[l])->forward[l] == temp; + l++) + curr->forward[l] = temp_forward[l]; + + delete temp; + + SkipBagNodePtr *forward = header->forward; + + while (forward[curr_level]==header && curr_level > 0) + curr_level--; + + level = curr_level; + count--; + delete update; + return; + } +} + +SkipBagNodePtr SkipBag::rightmost() +{ + SkipBagNodePtr temp; + SkipBagNode* curr = header; + int l = level; + + do + while ((temp = curr->forward[l])!=header) + curr = temp; + while (--l >= 0); + + return temp==header ? 0 : temp; +} + +SkipBagNodePtr SkipBag::pred(SkipBagNodePtr t) +{ + SkipBagNodePtr temp, curr = (SkipBagNodePtr) header; + int l = level; + + do + while ((temp = curr->forward[l])!=t) + curr = temp; + while (--l >= 0); + + return curr == header ? 0 : curr; +} + +void SkipBag::_kill() +{ + SkipBagNode *p = this->header->forward[0]; + + while (p != header) + { + SkipBagNodePtr q = p->forward[0]; + delete p; + p = q; + } +} + +void SkipBag::clear() +{ + SkipBagNodePtr *buffer_start = header->forward; + SkipBagNodePtr *trav = &header->forward[level+1]; + _kill(); + count = 0; + + while (trav > buffer_start) + *--trav = (SkipBagNodePtr)header; +} + +Pix SkipBag::seek( key, Pix i) +{ + SkipBagNodePtr temp; + SkipBagNode *curr = header; + int l = level; + if (i) + curr = (SkipBagNode *)i; + + do + { + while ((temp = curr->forward[l])!=header && + CMP(temp->item, key) < 0) + curr = temp; + } + while (--l >= 0); + + if (CMP(temp->item, key) != 0) + return 0; + else + { + return Pix(temp); + } +} + + +int SkipBag::nof( item) +{ + int n = 0; + SkipBagNodePtr t = (SkipBagNodePtr)(seek(item)); + if (t != 0) + { + do + { + ++n; + t = succ(t); + } while (t != 0 && EQ(item, t->item)); + } + return n; +} + +void SkipBag::remove( key) +{ + Pix t = seek(key); + while (t != 0) + { + del(key); + t = seek(key); + } +} + + +/* + * random function for probabilistic balancing + * + * Hardwired for p = .25. Not too flexible, + * but fast. Changing this would require a constructor + * that would accept a different value for p, etc. + * Perhaps someone else would like to implement this? + * + */ +int SkipBag::random_level (void) +{ + int rlevel = 0; + int b; + + do + { + b = random_bits & 3L; + if (!b) + rlevel++; + random_bits >>= 2; + if (--randoms_left == 0) + { + random_bits = gen->asLong(); + randoms_left = BITS_IN_RANDOM / 2; + }; + } + while (!b); + + return rlevel > max_levels ? max_levels : rlevel; +} + +int SkipBag::OK() +{ + int v = 1; + if (header == 0) + v = 0; + else + { + int n = 0; + SkipBagNodePtr trail = leftmost(); + SkipBagNodePtr t = 0; + if (trail) t = succ(trail); + if (t) n++; + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} + +SkipBaginit::SkipBaginit() +{ + if (!count) + SkipBag::gen = new MLCG(time(0)); + count++; +} + +SkipBaginit::~SkipBaginit() +{ + count--; + if (!count) + delete SkipBag::gen; +} diff --git a/gnu/lib/libg++/g++-include/SkipBag.hP b/gnu/lib/libg++/g++-include/SkipBag.hP new file mode 100644 index 0000000000..1bfe9da446 --- /dev/null +++ b/gnu/lib/libg++/g++-include/SkipBag.hP @@ -0,0 +1,171 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1991 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + * Bags implemented via William Pugh SkipList algorithms. + * CACM, June 1990, p 668-676. + * + */ + +#ifndef _SkipBag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SkipBag_h 1 + +#include ".Bag.h" + +#include +#include + +class SkipBag; +class RealSkipBagNode; + +class SkipBagNode +{ +friend class SkipBag; + private: + RealSkipBagNode * * forward; + SkipBagNode(int size); +}; + +class RealSkipBagNode : public SkipBagNode +{ +friend class SkipBag; + private: + item; + RealSkipBagNode( h, int size); +}; + +typedef RealSkipBagNode* SkipBagNodePtr; + +inline SkipBagNode::SkipBagNode(int size) +: forward(new SkipBagNodePtr[size+1]) +{ +} + +inline RealSkipBagNode::RealSkipBagNode( h, int size) +: item(h), + SkipBagNode(size) +{ +} + +class SkipBag : public Bag +{ +friend class SkipBaginit; + protected: + SkipBagNode* header; + int level; + int max_levels; + int randoms_left; + long random_bits; + + static MLCG* gen; + int random_level(void); + + SkipBagNodePtr leftmost(void); + SkipBagNodePtr rightmost(void); + SkipBagNodePtr pred(SkipBagNodePtr t); + SkipBagNodePtr succ(SkipBagNodePtr t); + void _kill(void); + + private: + enum { BITS_IN_RANDOM = LONGBITS-1 }; + + public: + SkipBag(long size=DEFAULT_INITIAL_CAPACITY); + SkipBag(SkipBag& a); + ~SkipBag(void); + + Pix add( i); + void del( i); + void remove(i); + int nof( i); + int contains( i); + + void clear(void); + + Pix first(void); + void next(Pix& i); + & operator () (Pix i); + Pix seek( i, Pix from = 0); + + Pix last(void); + void prev(Pix& i); + + int OK(void); +}; + +inline SkipBagNodePtr SkipBag::leftmost(void) +{ + return header->forward[0]; +} + +inline SkipBagNodePtr SkipBag::succ(SkipBagNodePtr t) +{ + SkipBagNodePtr result = 0; + if (t->forward[0]!=header) result = t->forward[0]; + return result; +} + +inline Pix SkipBag::first(void) +{ + return Pix(leftmost()); +} + +inline Pix SkipBag::last(void) +{ + return Pix(rightmost()); +} + +inline void SkipBag::next(Pix& i) +{ + if (i != 0) i = Pix(succ((SkipBagNodePtr)i)); +} + +inline & SkipBag::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SkipBagNodePtr)i)->item; +} + +inline void SkipBag::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((SkipBagNodePtr)i)); +} + +inline int SkipBag::contains( key) +{ + return seek(key) != 0; +} + +inline SkipBag::~SkipBag() +{ + _kill(); + delete header; +} + +static class SkipBaginit +{ + public: + SkipBaginit(); + ~SkipBaginit(); + private: + static int count; +} skipBaginit; + +#endif diff --git a/gnu/lib/libg++/g++-include/SkipMap.ccP b/gnu/lib/libg++/g++-include/SkipMap.ccP new file mode 100644 index 0000000000..8b6afe2edd --- /dev/null +++ b/gnu/lib/libg++/g++-include/SkipMap.ccP @@ -0,0 +1,307 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1991 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif + +#include +#include +#include "..SkipMap.h" + +/* + * Bags implemented via William Pugh SkipList algorithms. + * CACM, June 1990, p 668-676. + * + */ + +MLCG* SkipMap::gen = 0; +int SkipMapinit::count = 0; + +static int countbits(long bits) +{ + int n = 0; + while(bits>>=1) n++; + return n; +} + +SkipMap::SkipMap( dflt, long size) +: Map(dflt), + level(0), + header(new SkipMapNode (countbits(size)+1)), + max_levels (countbits(size)+1), + random_bits(gen->asLong()), + randoms_left(BITS_IN_RANDOM / 2) +{ + SkipMapNodePtr *buffer_start = header->forward; + SkipMapNodePtr *trav = &header->forward[max_levels]; + + count = 0; + while (trav > buffer_start) + *--trav = (SkipMapNodePtr) header; +} + +SkipMap::SkipMap(SkipMap& b) +: Map(b.def), + level (0), + header (new SkipMapNode (b.max_levels)), + max_levels (b.max_levels), + random_bits (gen->asLong()), + randoms_left (BITS_IN_RANDOM / 2) +{ + SkipMapNodePtr *buffer_start = header->forward; + SkipMapNodePtr *trav = &header->forward[max_levels]; + + count = 0; + + while (trav > buffer_start) + *--trav = (SkipMapNodePtr)header; + + for (SkipMapNodePtr t = b.leftmost(); t; t = b.succ(t)) + (*this)[t->item] = t->cont; +} + +& SkipMap::operator [] ( item) +{ + SkipMapNodePtr *update = new SkipMapNodePtr[max_levels+1]; + SkipMapNodePtr curr = + (SkipMapNodePtr) this->header; + int l = level; + SkipMapNodePtr temp; + + do + { + while ((temp = curr->forward[l])!=header && + CMP(temp->item, item) < 0) + curr = temp; + update[l] = curr; + } + while (--l >= 0); + + if (temp != header && CMP(temp->item, item) == 0) + { + delete update; + return temp->cont; + } + + if ((l = random_level ()) > level) + { + l = ++level; + update[l] = (SkipMapNodePtr)header; + }; + + temp = new RealSkipMapNode (item, def, l); + SkipMapNodePtr *temp_forward = temp->forward; + + do + { + SkipMapNodePtr *curr_forward = update[l]->forward; + + temp_forward[l] = curr_forward[l]; + curr_forward[l] = temp; + } + while (--l >= 0); + + count++; + delete update; + return temp->cont; +} + +void SkipMap::del( key) +{ + + int l = level; + int curr_level = level; + SkipMapNodePtr *update = new SkipMapNodePtr[max_levels+1]; + SkipMapNodePtr curr = (SkipMapNodePtr)header; + SkipMapNodePtr temp; + + do + { + while ((temp = curr->forward[l])!=header + && CMP(temp->item,key) < 0) + curr = temp; + update[l] = curr; + } + while (--l >= 0); + + if (CMP(temp->item,key)==0) + { + SkipMapNodePtr *temp_forward = temp->forward; + + for (l = 0; + l <= curr_level && (curr = update[l])->forward[l] == temp; + l++) + curr->forward[l] = temp_forward[l]; + + delete temp; + + SkipMapNodePtr *forward = header->forward; + + while (forward[curr_level]==header && curr_level > 0) + curr_level--; + + level = curr_level; + count--; + delete update; + return; + } +} + +SkipMapNodePtr SkipMap::rightmost() +{ + SkipMapNodePtr temp; + SkipMapNode* curr = header; + int l = level; + + do + while ((temp = curr->forward[l])!=header) + curr = temp; + while (--l >= 0); + + return temp==header ? 0 : temp; +} + +SkipMapNodePtr SkipMap::pred(SkipMapNodePtr t) +{ + SkipMapNodePtr temp, curr = (SkipMapNodePtr) header; + int l = level; + + do + while ((temp = curr->forward[l])!=t) + curr = temp; + while (--l >= 0); + + return curr == header ? 0 : curr; +} + +void SkipMap::_kill() +{ + SkipMapNode *p = this->header->forward[0]; + + while (p != header) + { + SkipMapNodePtr q = p->forward[0]; + delete p; + p = q; + } +} + +void SkipMap::clear() +{ + SkipMapNodePtr *buffer_start = header->forward; + SkipMapNodePtr *trav = &header->forward[level+1]; + _kill(); + count = 0; + + while (trav > buffer_start) + *--trav = (SkipMapNodePtr)header; +} + +Pix SkipMap::seek( key) +{ + SkipMapNodePtr temp; + SkipMapNode *curr = header; + int l = level; + + do + { + while ((temp = curr->forward[l])!=header && + CMP(temp->item, key) < 0) + curr = temp; + } + while (--l >= 0); + + if (CMP(temp->item, key) != 0) + return 0; + else + { + return Pix(temp); + } +} + +/* + * random function for probabilistic balancing + * + * Hardwired for p = .25. Not too flexible, + * but fast. Changing this would require a constructor + * that would accept a different value for p, etc. + * Perhaps someone else would like to implement this? + * + */ +int SkipMap::random_level (void) +{ + int rlevel = 0; + int b; + + do + { + b = random_bits & 3L; + if (!b) + rlevel++; + random_bits >>= 2; + if (--randoms_left == 0) + { + random_bits = gen->asLong(); + randoms_left = BITS_IN_RANDOM / 2; + }; + } + while (!b); + + return rlevel > max_levels ? max_levels : rlevel; +} + +int SkipMap::OK() +{ + int v = 1; + if (header == 0) + v = 0; + else + { + int n = 0; + SkipMapNodePtr trail = leftmost(); + SkipMapNodePtr t = 0; + if (trail) t = succ(trail); + if (t) n++; + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} + +SkipMapinit::SkipMapinit() +{ + if (!count) + SkipMap::gen = new MLCG(time(0)); + count++; +} + +SkipMapinit::~SkipMapinit() +{ + count--; + if (!count) + delete SkipMap::gen; +} + + diff --git a/gnu/lib/libg++/g++-include/SkipMap.hP b/gnu/lib/libg++/g++-include/SkipMap.hP new file mode 100644 index 0000000000..65766d64c7 --- /dev/null +++ b/gnu/lib/libg++/g++-include/SkipMap.hP @@ -0,0 +1,176 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1991 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + * Bags implemented via William Pugh SkipList algorithms. + * CACM, June 1990, p 668-676. + * + */ + +#ifndef _SkipMap_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SkipMap_h 1 + +#include "..Map.h" + +#include +#include + +class SkipMap; +class RealSkipMapNode; + +class SkipMapNode +{ +friend class SkipMap; + private: + RealSkipMapNode * * forward; + protected: + SkipMapNode(int size); +}; + +class RealSkipMapNode : public SkipMapNode +{ +friend class SkipMap; + private: + item; + cont; + RealSkipMapNode( h, i, int size); +}; + +typedef RealSkipMapNode* SkipMapNodePtr; + +inline SkipMapNode::SkipMapNode(int size) +: forward(new SkipMapNodePtr[size+1]) +{ +} + +inline RealSkipMapNode::RealSkipMapNode( h, i, int size) +: item(h), cont(i), + SkipMapNode(size) +{ +} + +class SkipMap : public Map +{ +friend class SkipMapinit; + protected: + SkipMapNode* header; + int level; + int max_levels; + int randoms_left; + long random_bits; + + static MLCG* gen; + int random_level(void); + + SkipMapNodePtr leftmost(); + SkipMapNodePtr rightmost(); + SkipMapNodePtr pred(SkipMapNodePtr t); + SkipMapNodePtr succ(SkipMapNodePtr t); + void _kill(); + private: + enum { BITS_IN_RANDOM = LONGBITS-1 }; + + public: + SkipMap( dflt, long size=DEFAULT_INITIAL_CAPACITY); + SkipMap(SkipMap& a); + ~SkipMap(); + + & operator [] ( key); + + void del( key); + + Pix first(); + void next(Pix& i); + & key(Pix i); + & contents(Pix i); + + Pix seek( key); + int contains( key); + void clear(); + + Pix last(); + void prev(Pix& i); + + int OK(); +}; + +inline SkipMap::~SkipMap() +{ + _kill(); + delete header; +} + +inline SkipMapNodePtr SkipMap::leftmost() +{ + return header->forward[0]==header ? 0 : header->forward[0]; +} + +inline Pix SkipMap::first() +{ + return Pix(leftmost()); +} + +inline Pix SkipMap::last() +{ + return Pix(rightmost()); +} + +inline SkipMapNodePtr SkipMap::succ(SkipMapNodePtr t) +{ + return t->forward[0]==header ? 0 : t->forward[0]; +} + +inline void SkipMap::next(Pix& i) +{ + if (i != 0) i = Pix(succ((SkipMapNodePtr)i)); +} + +inline void SkipMap::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((SkipMapNodePtr)i)); +} + +inline & SkipMap::key (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SkipMapNodePtr)i)->item; +} + +inline & SkipMap::contents (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SkipMapNodePtr)i)->cont; +} + +inline int SkipMap::contains( key) +{ + return seek(key) != 0; +} + +static class SkipMapinit +{ + public: + SkipMapinit(); + ~SkipMapinit(); + private: + static int count; +} skipMapinit; + +#endif diff --git a/gnu/lib/libg++/g++-include/SkipSet.ccP b/gnu/lib/libg++/g++-include/SkipSet.ccP new file mode 100644 index 0000000000..a1bf33d0fa --- /dev/null +++ b/gnu/lib/libg++/g++-include/SkipSet.ccP @@ -0,0 +1,395 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1991 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + * Sets implemented via William Pugh SkipList algorithms. + * CACM, June 1990, p 668-676. + * + */ + +#include +#include + +#include ".SkipSet.h" + +MLCG* SkipSet::gen = 0; +int SkipSetinit::count = 0; + +static int countbits(long bits) +{ + int n = 0; + while(bits>>=1L) n++; + return n; +} + +SkipSet::SkipSet(long size) +: level(0), + header(new SkipSetNode (countbits(size)+1)), + max_levels (countbits(size)+1), + random_bits(gen->asLong()), + randoms_left(BITS_IN_RANDOM / 2) +{ + SkipSetNodePtr *buffer_start = header->forward; + SkipSetNodePtr *trav = &header->forward[max_levels]; + + count = 0; + while (trav > buffer_start) + *--trav = (SkipSetNodePtr) header; +} + +SkipSet::SkipSet(SkipSet& b) +: level (0), + header (new SkipSetNode (b.max_levels)), + max_levels (b.max_levels), + random_bits (gen->asLong()), + randoms_left (BITS_IN_RANDOM / 2) +{ + SkipSetNodePtr *buffer_start = header->forward; + SkipSetNodePtr *trav = &header->forward[max_levels]; + + count = 0; + + while (trav > buffer_start) + *--trav = (SkipSetNodePtr)header; + + for (SkipSetNodePtr t = b.leftmost(); t; t = b.succ(t)) + add(t->item); +} + +/* relationals */ + +int SkipSet::operator == (SkipSet& y) +{ + if (count != y.count) + return 0; + else + { + SkipSetNodePtr t = leftmost(); + SkipSetNodePtr u = y.leftmost(); + for (;;) + { + if (t == 0) + return 1; + else if (!EQ(t->item, u->item)) + return 0; + else + { + t = succ(t); + u = y.succ(u); + } + } + } +} + +int SkipSet::operator <= (SkipSet& y) +{ + if (count > y.count) + return 0; + else + { + SkipSetNodePtr t = leftmost(); + SkipSetNodePtr u = y.leftmost(); + for (;;) + { + if (t == 0) + return 1; + else if (u == 0) + return 0; + int cmp = CMP(t->item, u->item); + if (cmp == 0) + { + t = succ(t); + u = y.succ(u); + } + else if (cmp < 0) + return 0; + else + u = y.succ(u); + } + } +} + + +void SkipSet::operator |=(SkipSet& y) +{ + if (&y == this) return; + SkipSetNodePtr u = y.leftmost(); + while (u != 0) + { + add(u->item); + u = y.succ(u); + } +} + +void SkipSet::operator &= (SkipSet& y) +{ + if (y.count == 0) + clear(); + else if (&y != this && count != 0) + { + SkipSetNodePtr t = leftmost(); + while (t != 0) + { + SkipSetNodePtr s = succ(t); + if (y.seek(t->item) == 0) del(t->item); + t = s; + } + } +} + + +void SkipSet::operator -=(SkipSet& y) +{ + if (&y == this) + clear(); + else if (y.count != 0) + { + SkipSetNodePtr t = leftmost(); + while (t != 0) + { + SkipSetNodePtr s = succ(t); + if (y.seek(t->item) != 0) del(t->item); + t = s; + } + } +} + +Pix SkipSet::add ( i) +{ + SkipSetNodePtr *update = new SkipSetNodePtr[max_levels+1]; + SkipSetNodePtr curr = (SkipSetNodePtr) this->header; + int l = level; + SkipSetNodePtr temp; + + do + { + while ((temp = curr->forward[l])!=header && + CMP(temp->item, i) < 0) + curr = temp; + update[l] = curr; + } + while (--l >= 0); + + if (temp != header && CMP(temp->item, i) == 0) + return Pix(temp); + + if ((l = random_level ()) > level) + { + l = ++level; + update[l] = (SkipSetNodePtr)header; + }; + + temp = new RealSkipSetNode (i, l); + SkipSetNodePtr *temp_forward = temp->forward; + + do + { + SkipSetNodePtr *curr_forward = update[l]->forward; + + temp_forward[l] = curr_forward[l]; + curr_forward[l] = temp; + } + while (--l >= 0); + + count++; + delete update; + return Pix(temp); +} + +void SkipSet::del( key) +{ + + int l = level; + int curr_level = level; + SkipSetNodePtr *update = new SkipSetNodePtr[max_levels+1]; + SkipSetNodePtr curr = (SkipSetNodePtr)header; + SkipSetNodePtr temp; + + do + { + while ((temp = curr->forward[l])!=header + && CMP(temp->item,key) < 0) + curr = temp; + update[l] = curr; + } + while (--l >= 0); + + if (CMP(temp->item,key)==0) + { + SkipSetNodePtr *temp_forward = temp->forward; + + for (l = 0; + l <= curr_level && (curr = update[l])->forward[l] == temp; + l++) + curr->forward[l] = temp_forward[l]; + + delete temp; + + SkipSetNodePtr *forward = header->forward; + + while (forward[curr_level]==header && curr_level > 0) + curr_level--; + + level = curr_level; + count--; + delete update; + return; + } +} + +SkipSetNodePtr SkipSet::rightmost() +{ + SkipSetNodePtr temp; + SkipSetNode* curr = header; + int l = level; + + do + while ((temp = curr->forward[l])!=header) + curr = temp; + while (--l >= 0); + + return temp==header ? 0 : temp; +} + +SkipSetNodePtr SkipSet::pred(SkipSetNodePtr t) +{ + SkipSetNodePtr temp, curr = (SkipSetNodePtr) header; + int l = level; + + do + while ((temp = curr->forward[l])!=t) + curr = temp; + while (--l >= 0); + + return curr == header ? 0 : curr; +} + +void SkipSet::_kill() +{ + SkipSetNode *p = this->header->forward[0]; + + while (p != header) + { + SkipSetNodePtr q = p->forward[0]; + delete p; + p = q; + } +} + +void SkipSet::clear() +{ + SkipSetNodePtr *buffer_start = header->forward; + SkipSetNodePtr *trav = &header->forward[level+1]; + _kill(); + count = 0; + + while (trav > buffer_start) + *--trav = (SkipSetNodePtr)header; +} + +Pix SkipSet::seek( key) +{ + SkipSetNodePtr temp; + SkipSetNode *curr = header; + int l = level; + + do + { + while ((temp = curr->forward[l])!=header && + CMP(temp->item, key) < 0) + curr = temp; + } + while (--l >= 0); + + if (CMP(temp->item, key) != 0) + return 0; + else + { + return Pix(temp); + } +} + + +/* + * random function for probabilistic balancing + * + * Hardwired for p = .25. Not too flexible, + * but fast. Changing this would require a constructor + * that would accept a different value for p, etc. + * Perhaps someone else would like to implement this? + * + */ +int SkipSet::random_level (void) +{ + int rlevel = 0; + int b; + + do + { + b = random_bits & 3L; + if (!b) + rlevel++; + random_bits >>= 2; + if (--randoms_left == 0) + { + random_bits = gen->asLong(); + randoms_left = BITS_IN_RANDOM / 2; + }; + } + while (!b); + + return rlevel > max_levels ? max_levels : rlevel; +} + +int SkipSet::OK() +{ + int v = 1; + if (header == 0) + v = 0; + else + { + int n = 0; + SkipSetNodePtr trail = leftmost(); + SkipSetNodePtr t = 0; + if (trail) t = succ(trail); + if (t) n++; + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} + +SkipSetinit::SkipSetinit() +{ + if (!count) + SkipSet::gen = new MLCG(time(0)); + count++; +} + +SkipSetinit::~SkipSetinit() +{ + count--; + if (!count) + delete SkipSet::gen; +} diff --git a/gnu/lib/libg++/g++-include/SkipSet.hP b/gnu/lib/libg++/g++-include/SkipSet.hP new file mode 100644 index 0000000000..cd95305239 --- /dev/null +++ b/gnu/lib/libg++/g++-include/SkipSet.hP @@ -0,0 +1,187 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1991 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + * Sets implemented via William Pugh SkipList algorithms. + * CACM, June 1990, p 668-676. + * + */ + +#ifndef _SkipSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SkipSet_h 1 + +#include ".Set.h" + +#include +#include + +class SkipSet; +class RealSkipSetNode; + +class SkipSetNode +{ +friend class SkipSet; + private: + RealSkipSetNode * * forward; + SkipSetNode(int size); +}; + +class RealSkipSetNode : public SkipSetNode +{ +friend class SkipSet; + private: + item; + RealSkipSetNode( h, int size); +}; + +typedef RealSkipSetNode* SkipSetNodePtr; + +inline SkipSetNode::SkipSetNode(int size) +: forward(new SkipSetNodePtr[size+1]) +{ +} + +inline RealSkipSetNode::RealSkipSetNode( h, int size) +: item(h), + SkipSetNode(size) +{ +} + +class SkipSet : public Set +{ +friend class SkipSetinit; + protected: + SkipSetNode* header; + int level; + int max_levels; + int randoms_left; + long random_bits; + + static MLCG* gen; + int random_level(void); + + SkipSetNodePtr leftmost(void); + SkipSetNodePtr rightmost(void); + SkipSetNodePtr pred(SkipSetNodePtr t); + SkipSetNodePtr succ(SkipSetNodePtr t); + void _kill(void); + + private: + enum { BITS_IN_RANDOM = LONGBITS-1 }; + public: + SkipSet(long size=DEFAULT_INITIAL_CAPACITY); + SkipSet(SkipSet& a); + ~SkipSet(); + + Pix add( i); + void del( i); + int contains( i); + + void clear(void); + + Pix first(void); + void next(Pix& i); + & operator () (Pix i); + Pix seek( i); + + Pix last(void); + void prev(Pix& i); + + void operator |= (SkipSet& b); + void operator -= (SkipSet& b); + void operator &= (SkipSet& b); + + int operator == (SkipSet& b); + int operator != (SkipSet& b); + int operator <= (SkipSet& b); + + int OK(void); +}; + +/* + * A little overkill on the inlines. + * + */ + +inline SkipSet::~SkipSet(void) +{ + _kill(); + delete header; +} + +inline int SkipSet::operator != (SkipSet& b) +{ + return ! (*this == b); +} + +inline SkipSetNodePtr SkipSet::leftmost(void) +{ + return header->forward[0]; +} + +inline SkipSetNodePtr SkipSet::succ(SkipSetNodePtr t) +{ + SkipSetNodePtr result = 0; + if (t->forward[0]!=header) result = t->forward[0]; + return result; +} + +inline Pix SkipSet::first(void) +{ + return Pix(leftmost()); +} + +inline Pix SkipSet::last(void) +{ + return Pix(rightmost()); +} + +inline void SkipSet::next(Pix& i) +{ + if (i != 0) i = Pix(succ((SkipSetNodePtr)i)); +} + +inline void SkipSet::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((SkipSetNodePtr)i)); +} + +inline & SkipSet::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SkipSetNodePtr)i)->item; +} + + +inline int SkipSet::contains( key) +{ + return seek(key) != 0; +} + +static class SkipSetinit +{ + public: + SkipSetinit(); + ~SkipSetinit(); + private: + static int count; +} skipSetinit; + +#endif diff --git a/gnu/lib/libg++/g++-include/SmplHist.h b/gnu/lib/libg++/g++-include/SmplHist.h deleted file mode 100644 index ed1fb5d136..0000000000 --- a/gnu/lib/libg++/g++-include/SmplHist.h +++ /dev/null @@ -1,82 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifndef SampleHistogram_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define SampleHistogram_h 1 - -#include -#include - -extern const int SampleHistogramMinimum; -extern const int SampleHistogramMaximum; - -class SampleHistogram : public SampleStatistic -{ -protected: - short howManyBuckets; - int *bucketCount; - double *bucketLimit; - -public: - - SampleHistogram(double low, double hi, double bucketWidth = -1.0); - - ~SampleHistogram(); - - virtual void reset(); - virtual void operator+=(double); - - int similarSamples(double); - - int buckets(); - - double bucketThreshold(int i); - int inBucket(int i); - void printBuckets(ostream&); - -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - - inline int SampleHistogram:: buckets() { return(howManyBuckets); }; - - inline double SampleHistogram:: bucketThreshold(int i) { - if (i < 0 || i >= howManyBuckets) - error("invalid bucket access"); - return(bucketLimit[i]); - } - - inline int SampleHistogram:: inBucket(int i) { - if (i < 0 || i >= howManyBuckets) - error("invalid bucket access"); - return(bucketCount[i]); - } - - -#endif - -#endif diff --git a/gnu/lib/libg++/g++-include/SmplStat.h b/gnu/lib/libg++/g++-include/SmplStat.h deleted file mode 100644 index 6aea14aef0..0000000000 --- a/gnu/lib/libg++/g++-include/SmplStat.h +++ /dev/null @@ -1,73 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ -#ifndef SampleStatistic_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define SampleStatistic_h 1 - -#include - -class SampleStatistic { -protected: - int n; - double x; - double x2; - double minValue, maxValue; - - public : - - SampleStatistic(); - virtual void reset(); - - virtual void operator+=(double); - int samples(); - double mean(); - double stdDev(); - double var(); - double min(); - double max(); - double confidence(int p_percentage); - double confidence(double p_value); - - void error(const char* msg); -}; - -// error handlers - -extern void default_SampleStatistic_error_handler(const char*); -extern one_arg_error_handler_t SampleStatistic_error_handler; - -extern one_arg_error_handler_t - set_SampleStatistic_error_handler(one_arg_error_handler_t f); - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - - inline SampleStatistic:: SampleStatistic(){ reset();} - inline int SampleStatistic:: samples() {return(n);} - inline double SampleStatistic:: min() {return(minValue);} - inline double SampleStatistic:: max() {return(maxValue);} - -#endif -#endif diff --git a/gnu/lib/libg++/g++-include/SplayBag.ccP b/gnu/lib/libg++/g++-include/SplayBag.ccP new file mode 100644 index 0000000000..ff02a992a6 --- /dev/null +++ b/gnu/lib/libg++/g++-include/SplayBag.ccP @@ -0,0 +1,445 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".SplayBag.h" + + +/* + + struct to simulate the special `null' node in the Sleater & Tarjan JACM 1985 + splay tree algorithms + + All routines use a version of their `simple top-down' splay alg. (p 669) + +*/ + +struct _dummySplayNode +{ + SplayNode* lt; + SplayNode* rt; + SplayNode* par; +} _dummy_null; + + +/* + traversal primitives +*/ + + +SplayNode* SplayBag::leftmost() +{ + SplayNode* t = root; + if (t != 0) while (t->lt != 0) t = t->lt; + return t; +} + +SplayNode* SplayBag::rightmost() +{ + SplayNode* t = root; + if (t != 0) while (t->rt != 0) t = t->rt; + return t; +} + +SplayNode* SplayBag::succ(SplayNode* t) +{ + if (t == 0) + return 0; + if (t->rt != 0) + { + t = t->rt; + while (t->lt != 0) t = t->lt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->lt) + return t->par; + else + t = t->par; + } + } +} + +SplayNode* SplayBag::pred(SplayNode* t) +{ + if (t == 0) + return 0; + else if (t->lt != 0) + { + t = t->lt; + while (t->rt != 0) t = t->rt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->rt) + return t->par; + else + t = t->par; + } + } +} + + + +Pix SplayBag::seek( key, Pix i) +{ + if (root == 0) return 0; + + SplayNode* t = (SplayNode*) i; + if (t != 0) + { + int cmp = CMP(key, t->item); + if (cmp == 0) + { + t = succ(t); + if (t != 0 && EQ(key, t->item)) + return Pix(t); + else + return 0; + } + else if (cmp < 0) + return 0; + } + + t = root; + int comp = CMP(key, t->item); + if (comp == 0) + return Pix(t); + + SplayNode* dummy = (SplayNode*)(&_dummy_null); + SplayNode* l = dummy; + SplayNode* r = dummy; + dummy->rt = dummy->lt = dummy->par = 0; + + while (comp != 0) + { + if (comp > 0) + { + SplayNode* tr = t->rt; + if (tr == 0) + break; + else + { + comp = CMP(key, tr->item); + if (comp <= 0 || tr->rt == 0) + { + l->rt = t; t->par = l; + l = t; + t = tr; + if (comp >= 0) + break; + } + else + { + if ((t->rt = tr->lt) != 0) t->rt->par = t; + tr->lt = t; t->par = tr; + l->rt = tr; tr->par = l; + l = tr; + t = tr->rt; + comp = CMP(key, t->item); + } + } + } + else + { + SplayNode* tl = t->lt; + if (tl == 0) + break; + else + { + comp = CMP(key, tl->item); + if (comp >= 0 || tl->lt == 0) + { + r->lt = t; t->par = r; + r = t; + t = tl; + if (comp <= 0) + break; + } + else + { + if ((t->lt = tl->rt) != 0) t->lt->par = t; + tl->rt = t; t->par = tl; + r->lt = tl; tl->par = r; + r = tl; + t = tl->lt; + comp = CMP(key, t->item); + } + } + } + } + if ((r->lt = t->rt) != 0) r->lt->par = r; + if ((l->rt = t->lt) != 0) l->rt->par = l; + if ((t->lt = dummy->rt) != 0) t->lt->par = t; + if ((t->rt = dummy->lt) != 0) t->rt->par = t; + t->par = 0; + root = t; + if (comp != 0) + return 0; + else + { + l = pred(t); + while (l != 0 && EQ(l->item, key)) { t = l; l = pred(l); } + return Pix(t); + } +} + +int SplayBag::nof( item) +{ + int n = 0; + SplayNode* t = (SplayNode*)(seek(item)); + if (t != 0) + { + do + { + ++n; + t = succ(t); + } while (t != 0 && EQ(item, t->item)); + } + return n; +} + +Pix SplayBag::add( item) +{ + ++count; + SplayNode* newnode = new SplayNode(item); + SplayNode* t = root; + if (t == 0) + { + root = newnode; + return Pix(root); + } + + int comp = CMP(item, t->item); + + SplayNode* dummy = (SplayNode*)(&_dummy_null); + SplayNode* l = dummy; + SplayNode* r = dummy; + dummy->rt = dummy->lt = dummy->par = 0; + + int done = 0; + while (!done) + { + if (comp >= 0) + { + SplayNode* tr = t->rt; + if (tr == 0) + { + tr = newnode; + comp = 0; done = 1; + } + else + comp = CMP(item, tr->item); + + if (comp <= 0) + { + l->rt = t; t->par = l; + l = t; + t = tr; + } + else + { + SplayNode* trr = tr->rt; + if (trr == 0) + { + trr = newnode; + comp = 0; done = 1; + } + else + comp = CMP(item, trr->item); + + if ((t->rt = tr->lt) != 0) t->rt->par = t; + tr->lt = t; t->par = tr; + l->rt = tr; tr->par = l; + l = tr; + t = trr; + } + } + else + { + SplayNode* tl = t->lt; + if (tl == 0) + { + tl = newnode; + comp = 0; done = 1; + } + else + comp = CMP(item, tl->item); + + if (comp >= 0) + { + r->lt = t; t->par = r; + r = t; + t = tl; + } + else + { + SplayNode* tll = tl->lt; + if (tll == 0) + { + tll = newnode; + comp = 0; done = 1; + } + else + comp = CMP(item, tll->item); + + if ((t->lt = tl->rt) != 0) t->lt->par = t; + tl->rt = t; t->par = tl; + r->lt = tl; tl->par = r; + r = tl; + t = tll; + } + } + } + if ((r->lt = t->rt) != 0) r->lt->par = r; + if ((l->rt = t->lt) != 0) l->rt->par = l; + if ((t->lt = dummy->rt) != 0) t->lt->par = t; + if ((t->rt = dummy->lt) != 0) t->rt->par = t; + t->par = 0; + root = t; + return Pix(root); +} + +void SplayBag::_del(SplayNode* t) +{ + if (t == 0) return; + + SplayNode* p = t->par; + + --count; + if (t->rt == 0) + { + if (t == root) + { + if ((root = t->lt) != 0) root->par = 0; + } + else if (t == p->lt) + { + if ((p->lt = t->lt) != 0) p->lt->par = p; + } + else + if ((p->rt = t->lt) != 0) p->rt->par = p; + } + else + { + SplayNode* r = t->rt; + SplayNode* l = r->lt; + for(;;) + { + if (l == 0) + { + if (t == root) + { + root = r; + r->par = 0; + } + else if (t == p->lt) + { + p->lt = r; + r->par = p; + } + else + { + p->rt = r; + r->par = p; + } + if ((r->lt = t->lt) != 0) r->lt->par = r; + break; + } + else + { + if ((r->lt = l->rt) != 0) r->lt->par = r; + l->rt = r; r->par = l; + r = l; + l = l->lt; + } + } + } + delete t; +} + + +void SplayBag::remove( key) +{ + SplayNode* t = (SplayNode*)(seek(key)); + while (t != 0) + { + _del(t); + t = (SplayNode*)(seek(key)); + } +} + + +void SplayBag::_kill(SplayNode* t) +{ + if (t != 0) + { + _kill(t->lt); + _kill(t->rt); + delete t; + } +} + + +SplayNode* SplayBag::_copy(SplayNode* t) +{ + if (t != 0) + { + SplayNode* l = _copy(t->lt); + SplayNode* r = _copy(t->rt); + SplayNode* x = new SplayNode(t->item, l, r); + if (l != 0) l->par = x; + if (r != 0) r->par = x; + return x; + } + else + return 0; +} + +int SplayBag::OK() +{ + int v = 1; + if (root == 0) + v = count == 0; + else + { + int n = 1; + SplayNode* trail = leftmost(); + SplayNode* t = succ(trail); + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) <= 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} + diff --git a/gnu/lib/libg++/g++-include/SplayBag.hP b/gnu/lib/libg++/g++-include/SplayBag.hP new file mode 100644 index 0000000000..9d7fcad1dd --- /dev/null +++ b/gnu/lib/libg++/g++-include/SplayBag.hP @@ -0,0 +1,144 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988, 1982 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _SplayBag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SplayBag_h 1 + +#include ".Bag.h" +#include ".SplayNode.h" + +class SplayBag : public Bag +{ +protected: + SplayNode* root; + + SplayNode* leftmost(); + SplayNode* rightmost(); + SplayNode* pred(SplayNode* t); + SplayNode* succ(SplayNode* t); + void _kill(SplayNode* t); + SplayNode* _copy(SplayNode* t); + void _del(SplayNode* t); + +public: + SplayBag(); + SplayBag(SplayBag& a); + ~SplayBag(); + + Pix add( item); + void del( item); + void remove(item); + int nof( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + Pix seek( item, Pix from = 0); + + Pix last(); + void prev(Pix& i); + + int OK(); +}; + + +inline SplayBag::~SplayBag() +{ + _kill(root); +} + +inline SplayBag::SplayBag() +{ + root = 0; + count = 0; +} + +inline SplayBag::SplayBag(SplayBag& b) +{ + count = b.count; + root = _copy(b.root); +} + +inline Pix SplayBag::first() +{ + return Pix(leftmost()); +} + +inline Pix SplayBag::last() +{ + return Pix(rightmost()); +} + +inline void SplayBag::next(Pix& i) +{ + if (i != 0) i = Pix(succ((SplayNode*)i)); +} + +inline void SplayBag::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((SplayNode*)i)); +} + +inline & SplayBag::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SplayNode*)i)->item; +} + +inline void SplayBag::clear() +{ + _kill(root); + count = 0; + root = 0; +} + +inline int SplayBag::contains( key) +{ + return seek(key) != 0; +} + +inline void SplayBag::del( key) +{ + _del((SplayNode*)(seek(key))); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/SplayMap.ccP b/gnu/lib/libg++/g++-include/SplayMap.ccP new file mode 100644 index 0000000000..4be340db0f --- /dev/null +++ b/gnu/lib/libg++/g++-include/SplayMap.ccP @@ -0,0 +1,401 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include "..SplayMap.h" + + +/* + + struct to simulate the special `null' node in the Sleater & Tarjan JACM 1985 + splay tree algorithms + + All routines use a version of their `simple top-down' splay alg. (p 669) + +*/ + +struct _dummySplayNode +{ + SplayNode* lt; + SplayNode* rt; + SplayNode* par; +} _dummy_null; + + +/* + traversal primitives +*/ + + +SplayNode* SplayMap::leftmost() +{ + SplayNode* t = root; + if (t != 0) while (t->lt != 0) t = t->lt; + return t; +} + +SplayNode* SplayMap::rightmost() +{ + SplayNode* t = root; + if (t != 0) while (t->rt != 0) t = t->rt; + return t; +} + +SplayNode* SplayMap::succ(SplayNode* t) +{ + if (t == 0) + return 0; + if (t->rt != 0) + { + t = t->rt; + while (t->lt != 0) t = t->lt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->lt) + return t->par; + else + t = t->par; + } + } +} + +SplayNode* SplayMap::pred(SplayNode* t) +{ + if (t == 0) + return 0; + else if (t->lt != 0) + { + t = t->lt; + while (t->rt != 0) t = t->rt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->rt) + return t->par; + else + t = t->par; + } + } +} + + +Pix SplayMap::seek( key) +{ + SplayNode* t = root; + if (t == 0) + return 0; + + int comp = CMP(key, t->item); + if (comp == 0) + return Pix(t); + + SplayNode* dummy = (SplayNode*)(&_dummy_null); + SplayNode* l = dummy; + SplayNode* r = dummy; + dummy->rt = dummy->lt = dummy->par = 0; + + while (comp != 0) + { + if (comp > 0) + { + SplayNode* tr = t->rt; + if (tr == 0) + break; + else + { + comp = CMP(key, tr->item); + if (comp <= 0 || tr->rt == 0) + { + l->rt = t; t->par = l; + l = t; + t = tr; + if (comp >= 0) + break; + } + else + { + if ((t->rt = tr->lt) != 0) t->rt->par = t; + tr->lt = t; t->par = tr; + l->rt = tr; tr->par = l; + l = tr; + t = tr->rt; + comp = CMP(key, t->item); + } + } + } + else + { + SplayNode* tl = t->lt; + if (tl == 0) + break; + else + { + comp = CMP(key, tl->item); + if (comp >= 0 || tl->lt == 0) + { + r->lt = t; t->par = r; + r = t; + t = tl; + if (comp <= 0) + break; + } + else + { + if ((t->lt = tl->rt) != 0) t->lt->par = t; + tl->rt = t; t->par = tl; + r->lt = tl; tl->par = r; + r = tl; + t = tl->lt; + comp = CMP(key, t->item); + } + } + } + } + if ((r->lt = t->rt) != 0) r->lt->par = r; + if ((l->rt = t->lt) != 0) l->rt->par = l; + if ((t->lt = dummy->rt) != 0) t->lt->par = t; + if ((t->rt = dummy->lt) != 0) t->rt->par = t; + t->par = 0; + root = t; + return (comp == 0) ? Pix(t) : 0; +} + + +& SplayMap::operator [] ( item) +{ + SplayNode* t = root; + if (t == 0) + { + ++count; + root = new SplayNode(item, def); + return root->cont; + } + int comp = CMP(item, t->item); + if (comp == 0) + return t->cont; + + SplayNode* dummy = (SplayNode*)(&_dummy_null); + SplayNode* l = dummy; + SplayNode* r = dummy; + dummy->rt = dummy->lt = dummy->par = 0; + + while (comp != 0) + { + if (comp > 0) + { + SplayNode* tr = t->rt; + if (tr == 0) + { + ++count; + tr = new SplayNode(item, def); + comp = 0; + } + else + comp = CMP(item, tr->item); + + if (comp <= 0) + { + l->rt = t; t->par = l; + l = t; + t = tr; + } + else + { + SplayNode* trr = tr->rt; + if (trr == 0) + { + ++count; + trr = new SplayNode(item, def); + comp = 0; + } + else + comp = CMP(item, trr->item); + + if ((t->rt = tr->lt) != 0) t->rt->par = t; + tr->lt = t; t->par = tr; + l->rt = tr; tr->par = l; + l = tr; + t = trr; + } + } + else + { + SplayNode* tl = t->lt; + if (tl == 0) + { + ++count; + tl = new SplayNode(item, def); + comp = 0; + } + else + comp = CMP(item, tl->item); + + if (comp >= 0) + { + r->lt = t; t->par = r; + r = t; + t = tl; + } + else + { + SplayNode* tll = tl->lt; + if (tll == 0) + { + ++count; + tll = new SplayNode(item, def); + comp = 0; + } + else + comp = CMP(item, tll->item); + + if ((t->lt = tl->rt) != 0) t->lt->par = t; + tl->rt = t; t->par = tl; + r->lt = tl; tl->par = r; + r = tl; + t = tll; + } + } + } + if ((r->lt = t->rt) != 0) r->lt->par = r; + if ((l->rt = t->lt) != 0) l->rt->par = l; + if ((t->lt = dummy->rt) != 0) t->lt->par = t; + if ((t->rt = dummy->lt) != 0) t->rt->par = t; + t->par = 0; + root = t; + return root->cont; +} + +void SplayMap::del( key) +{ + SplayNode* t = (SplayNode*)(seek(key)); + if (t == 0) return; + + SplayNode* p = t->par; + + --count; + if (t->rt == 0) + { + if (t == root) + { + if ((root = t->lt) != 0) root->par = 0; + } + else if (t == p->lt) + { + if ((p->lt = t->lt) != 0) p->lt->par = p; + } + else + if ((p->rt = t->lt) != 0) p->rt->par = p; + } + else + { + SplayNode* r = t->rt; + SplayNode* l = r->lt; + for(;;) + { + if (l == 0) + { + if (t == root) + { + root = r; + r->par = 0; + } + else if (t == p->lt) + { + p->lt = r; + r->par = p; + } + else + { + p->rt = r; + r->par = p; + } + if ((r->lt = t->lt) != 0) r->lt->par = r; + break; + } + else + { + if ((r->lt = l->rt) != 0) r->lt->par = r; + l->rt = r; r->par = l; + r = l; + l = l->lt; + } + } + } + delete t; +} + + +void SplayMap::_kill(SplayNode* t) +{ + if (t != 0) + { + _kill(t->lt); + _kill(t->rt); + delete t; + } +} + + +SplayNode* SplayMap::_copy(SplayNode* t) +{ + if (t != 0) + { + SplayNode* l = _copy(t->lt); + SplayNode* r = _copy(t->rt); + SplayNode* x = new SplayNode(t->item, t->cont, l, r); + if (l != 0) l->par = x; + if (r != 0) r->par = x; + return x; + } + else + return 0; +} + + +int SplayMap::OK() +{ + int v = 1; + if (root == 0) + v = count == 0; + else + { + int n = 1; + SplayNode* trail = leftmost(); + SplayNode* t = succ(trail); + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/SplayMap.hP b/gnu/lib/libg++/g++-include/SplayMap.hP new file mode 100644 index 0000000000..ced95378ab --- /dev/null +++ b/gnu/lib/libg++/g++-include/SplayMap.hP @@ -0,0 +1,154 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _SplayMap_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SplayMap_h 1 + +#include "..Map.h" + +#ifndef _SplayNode +#define _SplayNode 1 + +struct SplayNode +{ + SplayNode* lt; + SplayNode* rt; + SplayNode* par; + item; + cont; + SplayNode( h, c, + SplayNode* l=0, + SplayNode* r=0); + ~SplayNode(); +}; + + +inline SplayNode::SplayNode( h, c, + SplayNode* l, + SplayNode* r) + :item(h), cont(c), lt(l), rt(r), par(0) {} + +inline SplayNode::~SplayNode() {} + +typedef SplayNode* SplayNodePtr; + +#endif + +class SplayMap : public Map +{ +protected: + SplayNode* root; + + SplayNode* leftmost(); + SplayNode* rightmost(); + SplayNode* pred(SplayNode* t); + SplayNode* succ(SplayNode* t); + void _kill(SplayNode* t); + SplayNode* _copy(SplayNode* t); + +public: + SplayMap( dflt); + SplayMap(SplayMap& a); + ~SplayMap(); + + & operator [] ( key); + + void del( key); + + Pix first(); + void next(Pix& i); + & key(Pix i); + & contents(Pix i); + + Pix seek( key); + int contains( key); + + void clear(); + + Pix last(); + void prev(Pix& i); + + int OK(); +}; + + +inline SplayMap::~SplayMap() +{ + _kill(root); +} + +inline SplayMap::SplayMap( dflt) :Map(dflt) +{ + root = 0; +} + +inline SplayMap::SplayMap(SplayMap& b) :Map(b.def) +{ + count = b.count; + root = _copy(b.root); +} + +inline Pix SplayMap::first() +{ + return Pix(leftmost()); +} + +inline Pix SplayMap::last() +{ + return Pix(rightmost()); +} + +inline void SplayMap::next(Pix& i) +{ + if (i != 0) i = Pix(succ((SplayNode*)i)); +} + +inline void SplayMap::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((SplayNode*)i)); +} + +inline & SplayMap::key (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SplayNode*)i)->item; +} + +inline & SplayMap::contents (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SplayNode*)i)->cont; +} + +inline void SplayMap::clear() +{ + _kill(root); + count = 0; + root = 0; +} + +inline int SplayMap::contains( key) +{ + return seek(key) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/SplayNode.ccP b/gnu/lib/libg++/g++-include/SplayNode.ccP new file mode 100644 index 0000000000..9dfb1d8c06 --- /dev/null +++ b/gnu/lib/libg++/g++-include/SplayNode.ccP @@ -0,0 +1,21 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1992 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".SplayNode.h" diff --git a/gnu/lib/libg++/g++-include/SplayNode.hP b/gnu/lib/libg++/g++-include/SplayNode.hP new file mode 100644 index 0000000000..a196861fc9 --- /dev/null +++ b/gnu/lib/libg++/g++-include/SplayNode.hP @@ -0,0 +1,44 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988, 1982 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _SplayNode +#define _SplayNode 1 +#ifdef __GNUG__ +#pragma interface +#endif +#include ".defs.h" + +struct SplayNode +{ + SplayNode* lt; + SplayNode* rt; + SplayNode* par; + item; + SplayNode( h, SplayNode* l=0, SplayNode* r=0); + ~SplayNode(); +}; + + +inline SplayNode::SplayNode( h, SplayNode* l, SplayNode* r) +:item(h), lt(l), rt(r), par(0) {} + +inline SplayNode::~SplayNode() {} + +typedef SplayNode* SplayNodePtr; + +#endif diff --git a/gnu/lib/libg++/g++-include/SplayPQ.ccP b/gnu/lib/libg++/g++-include/SplayPQ.ccP new file mode 100644 index 0000000000..0740cd9a93 --- /dev/null +++ b/gnu/lib/libg++/g++-include/SplayPQ.ccP @@ -0,0 +1,523 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".SplayPQ.h" + + +/* + + struct to simulate the special `null' node in the Sleater & Tarjan JACM 1985 + splay tree algorithms + + All routines use a version of their `simple top-down' splay alg. (p 669) + +*/ + +struct _dummySplayNode +{ + SplayNode* lt; + SplayNode* rt; + SplayNode* par; +} _dummy_null; + + +/* + traversal primitives +*/ + + +SplayNode* SplayPQ::leftmost() +{ + SplayNode* t = root; + if (t != 0) while (t->lt != 0) t = t->lt; + return t; +} + +SplayNode* SplayPQ::rightmost() +{ + SplayNode* t = root; + if (t != 0) while (t->rt != 0) t = t->rt; + return t; +} + +SplayNode* SplayPQ::succ(SplayNode* t) +{ + if (t == 0) + return 0; + if (t->rt != 0) + { + t = t->rt; + while (t->lt != 0) t = t->lt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->lt) + return t->par; + else + t = t->par; + } + } +} + +SplayNode* SplayPQ::pred(SplayNode* t) +{ + if (t == 0) + return 0; + else if (t->lt != 0) + { + t = t->lt; + while (t->rt != 0) t = t->rt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->rt) + return t->par; + else + t = t->par; + } + } +} + + +Pix SplayPQ::seek( key) +{ + SplayNode* t = root; + if (t == 0) + return 0; + + int comp = CMP(key, t->item); + if (comp == 0) + return Pix(t); + + SplayNode* dummy = (SplayNode*)(&_dummy_null); + SplayNode* l = dummy; + SplayNode* r = dummy; + dummy->rt = dummy->lt = dummy->par = 0; + + while (comp != 0) + { + if (comp > 0) + { + SplayNode* tr = t->rt; + if (tr == 0) + break; + else + { + comp = CMP(key, tr->item); + if (comp <= 0 || tr->rt == 0) + { + l->rt = t; t->par = l; + l = t; + t = tr; + if (comp >= 0) + break; + } + else + { + if ((t->rt = tr->lt) != 0) t->rt->par = t; + tr->lt = t; t->par = tr; + l->rt = tr; tr->par = l; + l = tr; + t = tr->rt; + comp = CMP(key, t->item); + } + } + } + else + { + SplayNode* tl = t->lt; + if (tl == 0) + break; + else + { + comp = CMP(key, tl->item); + if (comp >= 0 || tl->lt == 0) + { + r->lt = t; t->par = r; + r = t; + t = tl; + if (comp <= 0) + break; + } + else + { + if ((t->lt = tl->rt) != 0) t->lt->par = t; + tl->rt = t; t->par = tl; + r->lt = tl; tl->par = r; + r = tl; + t = tl->lt; + comp = CMP(key, t->item); + } + } + } + } + if ((r->lt = t->rt) != 0) r->lt->par = r; + if ((l->rt = t->lt) != 0) l->rt->par = l; + if ((t->lt = dummy->rt) != 0) t->lt->par = t; + if ((t->rt = dummy->lt) != 0) t->rt->par = t; + t->par = 0; + root = t; + return (comp == 0) ? Pix(t) : 0; +} + + +Pix SplayPQ::enq( item) +{ + ++count; + SplayNode* newnode = new SplayNode(item); + SplayNode* t = root; + if (t == 0) + { + root = newnode; + return Pix(root); + } + + int comp = CMP(item, t->item); + + SplayNode* dummy = (SplayNode*)(&_dummy_null); + SplayNode* l = dummy; + SplayNode* r = dummy; + dummy->rt = dummy->lt = dummy->par = 0; + + int done = 0; + while (!done) + { + if (comp >= 0) + { + SplayNode* tr = t->rt; + if (tr == 0) + { + tr = newnode; + comp = 0; done = 1; + } + else + comp = CMP(item, tr->item); + + if (comp <= 0) + { + l->rt = t; t->par = l; + l = t; + t = tr; + } + else + { + SplayNode* trr = tr->rt; + if (trr == 0) + { + trr = newnode; + comp = 0; done = 1; + } + else + comp = CMP(item, trr->item); + + if ((t->rt = tr->lt) != 0) t->rt->par = t; + tr->lt = t; t->par = tr; + l->rt = tr; tr->par = l; + l = tr; + t = trr; + } + } + else + { + SplayNode* tl = t->lt; + if (tl == 0) + { + tl = newnode; + comp = 0; done = 1; + } + else + comp = CMP(item, tl->item); + + if (comp >= 0) + { + r->lt = t; t->par = r; + r = t; + t = tl; + } + else + { + SplayNode* tll = tl->lt; + if (tll == 0) + { + tll = newnode; + comp = 0; done = 1; + } + else + comp = CMP(item, tll->item); + + if ((t->lt = tl->rt) != 0) t->lt->par = t; + tl->rt = t; t->par = tl; + r->lt = tl; tl->par = r; + r = tl; + t = tll; + } + } + } + if ((r->lt = t->rt) != 0) r->lt->par = r; + if ((l->rt = t->lt) != 0) l->rt->par = l; + if ((t->lt = dummy->rt) != 0) t->lt->par = t; + if ((t->rt = dummy->lt) != 0) t->rt->par = t; + t->par = 0; + root = t; + return Pix(root); +} + + +void SplayPQ::del(Pix pix) +{ + SplayNode* t = (SplayNode*)pix; + if (t == 0) return; + + SplayNode* p = t->par; + + --count; + if (t->rt == 0) + { + if (t == root) + { + if ((root = t->lt) != 0) root->par = 0; + } + else if (t == p->lt) + { + if ((p->lt = t->lt) != 0) p->lt->par = p; + } + else + if ((p->rt = t->lt) != 0) p->rt->par = p; + } + else + { + SplayNode* r = t->rt; + SplayNode* l = r->lt; + for(;;) + { + if (l == 0) + { + if (t == root) + { + root = r; + r->par = 0; + } + else if (t == p->lt) + { + p->lt = r; + r->par = p; + } + else + { + p->rt = r; + r->par = p; + } + if ((r->lt = t->lt) != 0) r->lt->par = r; + break; + } + else + { + if ((r->lt = l->rt) != 0) r->lt->par = r; + l->rt = r; r->par = l; + r = l; + l = l->lt; + } + } + } + delete t; +} + +& SplayPQ::front() +{ + if (root == 0) + error ("min: empty tree\n"); +// else + { + SplayNode* t = root; + SplayNode* l = root->lt; + for(;;) + { + if (l == 0) + { + root = t; + root->par = 0; + return root->item; + } + else + { + if ((t->lt = l->rt) != 0) t->lt->par = t; + l->rt = t; t->par = l; + t = l; + l = l->lt; + } + } + } +} + +void SplayPQ::del_front() +{ + if (root != 0) + { + --count; + SplayNode* t = root; + SplayNode* l = root->lt; + if (l == 0) + { + if ((root = t->rt) != 0) root->par = 0; + delete t; + } + else + { + for(;;) + { + SplayNode* ll = l->lt; + if (ll == 0) + { + if ((t->lt = l->rt) != 0) t->lt->par = t; + delete l; + break; + } + else + { + SplayNode* lll = ll->lt; + if (lll == 0) + { + if ((l->lt = ll->rt) != 0) l->lt->par = l; + delete ll; + break; + } + else + { + t->lt = ll; ll->par = t; + if ((l->lt = ll->rt) != 0) l->lt->par = l; + ll->rt = l; l->par = ll; + t = ll; + l = lll; + } + } + } + } + } +} + + SplayPQ::deq() +{ + if (root == 0) + error("deq: empty tree"); +// else + { + --count; + SplayNode* t = root; + SplayNode* l = root->lt; + if (l == 0) + { + if ((root = t->rt) != 0) root->par = 0; + res = t->item; + delete t; + return res; + } + else + { + for(;;) + { + SplayNode* ll = l->lt; + if (ll == 0) + { + if ((t->lt = l->rt) != 0) t->lt->par = t; + res = l->item; + delete l; + return res; + } + else + { + SplayNode* lll = ll->lt; + if (lll == 0) + { + if ((l->lt = ll->rt) != 0) l->lt->par = l; + res = ll->item; + delete ll; + return res; + } + else + { + t->lt = ll; ll->par = t; + if ((l->lt = ll->rt) != 0) l->lt->par = l; + ll->rt = l; l->par = ll; + t = ll; + l = lll; + } + } + } + } + } +} + + +void SplayPQ::_kill(SplayNode* t) +{ + if (t != 0) + { + _kill(t->lt); + _kill(t->rt); + delete t; + } +} + + +SplayNode* SplayPQ::_copy(SplayNode* t) +{ + if (t != 0) + { + SplayNode* l = _copy(t->lt); + SplayNode* r = _copy(t->rt); + SplayNode* x = new SplayNode(t->item, l, r); + if (l != 0) l->par = x; + if (r != 0) r->par = x; + return x; + } + else + return 0; +} + +int SplayPQ::OK() +{ + int v = 1; + if (root == 0) + v = count == 0; + else + { + int n = 1; + SplayNode* trail = leftmost(); + SplayNode* t = succ(trail); + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/SplayPQ.hP b/gnu/lib/libg++/g++-include/SplayPQ.hP new file mode 100644 index 0000000000..c75c4a0529 --- /dev/null +++ b/gnu/lib/libg++/g++-include/SplayPQ.hP @@ -0,0 +1,123 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _SplayPQ_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SplayPQ_h 1 + +#include ".PQ.h" +#include ".SplayNode.h" + +class SplayPQ : public PQ +{ +protected: + SplayNode* root; + + SplayNode* leftmost(); + SplayNode* rightmost(); + SplayNode* pred(SplayNode* t); + SplayNode* succ(SplayNode* t); + void _kill(SplayNode* t); + SplayNode* _copy(SplayNode* t); + +public: + SplayPQ(); + SplayPQ(SplayPQ& a); + virtual ~SplayPQ(); + + Pix enq( item); + deq(); + + & front(); + void del_front(); + + int contains( item); + + void clear(); + + Pix first(); + Pix last(); + void next(Pix& i); + void prev(Pix& i); + & operator () (Pix i); + void del(Pix i); + Pix seek( item); + + int OK(); // rep invariant +}; + + +inline SplayPQ::~SplayPQ() +{ + _kill(root); +} + +inline SplayPQ::SplayPQ() +{ + root = 0; + count = 0; +} + +inline SplayPQ::SplayPQ(SplayPQ& b) +{ + count = b.count; + root = _copy(b.root); +} + +inline Pix SplayPQ::first() +{ + return Pix(leftmost()); +} + +inline Pix SplayPQ::last() +{ + return Pix(rightmost()); +} + +inline void SplayPQ::next(Pix& i) +{ + if (i != 0) i = Pix(succ((SplayNode*)i)); +} + +inline void SplayPQ::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((SplayNode*)i)); +} + +inline & SplayPQ::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SplayNode*)i)->item; +} + +inline void SplayPQ::clear() +{ + _kill(root); + count = 0; + root = 0; +} + +inline int SplayPQ::contains( key) +{ + return seek(key) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/SplaySet.ccP b/gnu/lib/libg++/g++-include/SplaySet.ccP new file mode 100644 index 0000000000..bba5601c7e --- /dev/null +++ b/gnu/lib/libg++/g++-include/SplaySet.ccP @@ -0,0 +1,499 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".SplaySet.h" + + +/* + + struct to simulate the special `null' node in the Sleater & Tarjan JACM 1985 + splay tree algorithms + + All routines use a version of their `simple top-down' splay alg. (p 669) + +*/ + +struct _dummySplayNode +{ + SplayNode* lt; + SplayNode* rt; + SplayNode* par; +} _dummy_null; + + +/* + traversal primitives +*/ + + +SplayNode* SplaySet::leftmost() +{ + SplayNode* t = root; + if (t != 0) while (t->lt != 0) t = t->lt; + return t; +} + +SplayNode* SplaySet::rightmost() +{ + SplayNode* t = root; + if (t != 0) while (t->rt != 0) t = t->rt; + return t; +} + +SplayNode* SplaySet::succ(SplayNode* t) +{ + if (t == 0) + return 0; + if (t->rt != 0) + { + t = t->rt; + while (t->lt != 0) t = t->lt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->lt) + return t->par; + else + t = t->par; + } + } +} + +SplayNode* SplaySet::pred(SplayNode* t) +{ + if (t == 0) + return 0; + else if (t->lt != 0) + { + t = t->lt; + while (t->rt != 0) t = t->rt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->rt) + return t->par; + else + t = t->par; + } + } +} + + +Pix SplaySet::seek( key) +{ + SplayNode* t = root; + if (t == 0) + return 0; + + int comp = CMP(key, t->item); + if (comp == 0) + return Pix(t); + + SplayNode* dummy = (SplayNode*)(&_dummy_null); + SplayNode* l = dummy; + SplayNode* r = dummy; + dummy->rt = dummy->lt = dummy->par = 0; + + while (comp != 0) + { + if (comp > 0) + { + SplayNode* tr = t->rt; + if (tr == 0) + break; + else + { + comp = CMP(key, tr->item); + if (comp <= 0 || tr->rt == 0) + { + l->rt = t; t->par = l; + l = t; + t = tr; + if (comp >= 0) + break; + } + else + { + if ((t->rt = tr->lt) != 0) t->rt->par = t; + tr->lt = t; t->par = tr; + l->rt = tr; tr->par = l; + l = tr; + t = tr->rt; + comp = CMP(key, t->item); + } + } + } + else + { + SplayNode* tl = t->lt; + if (tl == 0) + break; + else + { + comp = CMP(key, tl->item); + if (comp >= 0 || tl->lt == 0) + { + r->lt = t; t->par = r; + r = t; + t = tl; + if (comp <= 0) + break; + } + else + { + if ((t->lt = tl->rt) != 0) t->lt->par = t; + tl->rt = t; t->par = tl; + r->lt = tl; tl->par = r; + r = tl; + t = tl->lt; + comp = CMP(key, t->item); + } + } + } + } + if ((r->lt = t->rt) != 0) r->lt->par = r; + if ((l->rt = t->lt) != 0) l->rt->par = l; + if ((t->lt = dummy->rt) != 0) t->lt->par = t; + if ((t->rt = dummy->lt) != 0) t->rt->par = t; + t->par = 0; + root = t; + return (comp == 0) ? Pix(t) : 0; +} + + + +Pix SplaySet::add( item) +{ + SplayNode* t = root; + if (t == 0) + { + ++count; + root = new SplayNode(item); + return Pix(root); + } + int comp = CMP(item, t->item); + if (comp == 0) + return Pix(t); + + SplayNode* dummy = (SplayNode*)(&_dummy_null); + SplayNode* l = dummy; + SplayNode* r = dummy; + dummy->rt = dummy->lt = dummy->par = 0; + + while (comp != 0) + { + if (comp > 0) + { + SplayNode* tr = t->rt; + if (tr == 0) + { + ++count; + tr = new SplayNode(item); + comp = 0; + } + else + comp = CMP(item, tr->item); + + if (comp <= 0) + { + l->rt = t; t->par = l; + l = t; + t = tr; + } + else + { + SplayNode* trr = tr->rt; + if (trr == 0) + { + ++count; + trr = new SplayNode(item); + comp = 0; + } + else + comp = CMP(item, trr->item); + + if ((t->rt = tr->lt) != 0) t->rt->par = t; + tr->lt = t; t->par = tr; + l->rt = tr; tr->par = l; + l = tr; + t = trr; + } + } + else + { + SplayNode* tl = t->lt; + if (tl == 0) + { + ++count; + tl = new SplayNode(item); + comp = 0; + } + else + comp = CMP(item, tl->item); + + if (comp >= 0) + { + r->lt = t; t->par = r; + r = t; + t = tl; + } + else + { + SplayNode* tll = tl->lt; + if (tll == 0) + { + ++count; + tll = new SplayNode(item); + comp = 0; + } + else + comp = CMP(item, tll->item); + + if ((t->lt = tl->rt) != 0) t->lt->par = t; + tl->rt = t; t->par = tl; + r->lt = tl; tl->par = r; + r = tl; + t = tll; + } + } + } + if ((r->lt = t->rt) != 0) r->lt->par = r; + if ((l->rt = t->lt) != 0) l->rt->par = l; + if ((t->lt = dummy->rt) != 0) t->lt->par = t; + if ((t->rt = dummy->lt) != 0) t->rt->par = t; + t->par = 0; + root = t; + return Pix(root); +} + +void SplaySet::del( key) +{ + SplayNode* t = (SplayNode*)(seek(key)); + if (t == 0) return; + + SplayNode* p = t->par; + + --count; + if (t->rt == 0) + { + if (t == root) + { + if ((root = t->lt) != 0) root->par = 0; + } + else if (t == p->lt) + { + if ((p->lt = t->lt) != 0) p->lt->par = p; + } + else + if ((p->rt = t->lt) != 0) p->rt->par = p; + } + else + { + SplayNode* r = t->rt; + SplayNode* l = r->lt; + for(;;) + { + if (l == 0) + { + if (t == root) + { + root = r; + r->par = 0; + } + else if (t == p->lt) + { + p->lt = r; + r->par = p; + } + else + { + p->rt = r; + r->par = p; + } + if ((r->lt = t->lt) != 0) r->lt->par = r; + break; + } + else + { + if ((r->lt = l->rt) != 0) r->lt->par = r; + l->rt = r; r->par = l; + r = l; + l = l->lt; + } + } + } + delete t; +} + + +void SplaySet::_kill(SplayNode* t) +{ + if (t != 0) + { + _kill(t->lt); + _kill(t->rt); + delete t; + } +} + + +SplayNode* SplaySet::_copy(SplayNode* t) +{ + if (t != 0) + { + SplayNode* l = _copy(t->lt); + SplayNode* r = _copy(t->rt); + SplayNode* x = new SplayNode(t->item, l, r); + if (l != 0) l->par = x; + if (r != 0) r->par = x; + return x; + } + else + return 0; +} + +/* relationals */ + +int SplaySet::operator == (SplaySet& y) +{ + if (count != y.count) + return 0; + else + { + SplayNode* t = leftmost(); + SplayNode* u = y.leftmost(); + for (;;) + { + if (t == 0) + return 1; + else if (!EQ(t->item, u->item)) + return 0; + else + { + t = succ(t); + u = y.succ(u); + } + } + } +} + +int SplaySet::operator <= (SplaySet& y) +{ + if (count > y.count) + return 0; + else + { + SplayNode* t = leftmost(); + SplayNode* u = y.leftmost(); + for (;;) + { + if (t == 0) + return 1; + else if (u == 0) + return 0; + int cmp = CMP(t->item, u->item); + if (cmp == 0) + { + t = succ(t); + u = y.succ(u); + } + else if (cmp < 0) + return 0; + else + u = y.succ(u); + } + } +} + + +void SplaySet::operator |=(SplaySet& y) +{ + if (&y == this) return; + SplayNode* u = y.leftmost(); + while (u != 0) + { + add(u->item); + u = y.succ(u); + } +} + +void SplaySet::operator &= (SplaySet& y) +{ + if (y.count == 0) + clear(); + else if (&y != this && count != 0) + { + SplayNode* t = leftmost(); + while (t != 0) + { + SplayNode* s = succ(t); + if (y.seek(t->item) == 0) del(t->item); + t = s; + } + } +} + + +void SplaySet::operator -=(SplaySet& y) +{ + if (&y == this) + clear(); + else if (y.count != 0) + { + SplayNode* t = leftmost(); + while (t != 0) + { + SplayNode* s = succ(t); + if (y.seek(t->item) != 0) del(t->item); + t = s; + } + } +} + +int SplaySet::OK() +{ + int v = 1; + if (root == 0) + v = count == 0; + else + { + int n = 1; + SplayNode* trail = leftmost(); + SplayNode* t = succ(trail); + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/SplaySet.hP b/gnu/lib/libg++/g++-include/SplaySet.hP new file mode 100644 index 0000000000..cf50b97554 --- /dev/null +++ b/gnu/lib/libg++/g++-include/SplaySet.hP @@ -0,0 +1,133 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _SplaySet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SplaySet_h 1 + +#include ".Set.h" +#include ".SplayNode.h" + +class SplaySet : public Set +{ +protected: + SplayNode* root; + + SplayNode* leftmost(); + SplayNode* rightmost(); + SplayNode* pred(SplayNode* t); + SplayNode* succ(SplayNode* t); + void _kill(SplayNode* t); + SplayNode* _copy(SplayNode* t); + +public: + SplaySet(); + SplaySet(SplaySet& a); + ~SplaySet(); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + Pix seek( item); + + Pix last(); + void prev(Pix& i); + + void operator |= (SplaySet& b); + void operator -= (SplaySet& b); + void operator &= (SplaySet& b); + + int operator == (SplaySet& b); + int operator != (SplaySet& b); + int operator <= (SplaySet& b); + + int OK(); +}; + + +inline SplaySet::~SplaySet() +{ + _kill(root); +} + +inline SplaySet::SplaySet() +{ + root = 0; + count = 0; +} + +inline SplaySet::SplaySet(SplaySet& b) +{ + count = b.count; + root = _copy(b.root); +} + + +inline int SplaySet::operator != (SplaySet& b) +{ + return ! (*this == b); +} + +inline Pix SplaySet::first() +{ + return Pix(leftmost()); +} + +inline Pix SplaySet::last() +{ + return Pix(rightmost()); +} + +inline void SplaySet::next(Pix& i) +{ + if (i != 0) i = Pix(succ((SplayNode*)i)); +} + +inline void SplaySet::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((SplayNode*)i)); +} + +inline & SplaySet::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SplayNode*)i)->item; +} + +inline void SplaySet::clear() +{ + _kill(root); + count = 0; + root = 0; +} + +inline int SplaySet::contains( key) +{ + return seek(key) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/Stack.ccP b/gnu/lib/libg++/g++-include/Stack.ccP new file mode 100644 index 0000000000..efb6b8edbd --- /dev/null +++ b/gnu/lib/libg++/g++-include/Stack.ccP @@ -0,0 +1,11 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".Stack.h" + +Stack::~Stack() {} + +void Stack::error(const char* msg) +{ + (*lib_error_handler)("Stack", msg); +} diff --git a/gnu/lib/libg++/g++-include/Stack.hP b/gnu/lib/libg++/g++-include/Stack.hP new file mode 100644 index 0000000000..37acb2fac5 --- /dev/null +++ b/gnu/lib/libg++/g++-include/Stack.hP @@ -0,0 +1,51 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _Stack_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Stack_h + +#include + +#include ".defs.h" + +class Stack +{ +public: + Stack() { } + virtual ~Stack(); + + virtual void push( item) = 0; + virtual pop() = 0; + virtual & top() = 0; + virtual void del_top() = 0; + + virtual int empty() = 0; + virtual int full() = 0; + virtual int length() = 0; + + virtual void clear() = 0; + + void error(const char*); + virtual int OK() = 0; +}; + +#endif diff --git a/gnu/lib/libg++/g++-include/String.h b/gnu/lib/libg++/g++-include/String.h deleted file mode 100644 index 871b9a2d54..0000000000 --- a/gnu/lib/libg++/g++-include/String.h +++ /dev/null @@ -1,1341 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#if defined(SHORT_NAMES) || defined(VMS) -#define re_compile_pattern recmppat -#define re_pattern_buffer repatbuf -#define re_registers reregs -#endif - -#ifndef _String_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _String_h 1 - -#include -#include - -struct StrRep // internal String representations -{ - unsigned short len; // string length - unsigned short sz; // allocated space - char s[1]; // the string starts here - // (at least 1 char for trailing null) - // allocated & expanded via non-public fcts -}; - -// primitive ops on StrReps -- nearly all String fns go through these. - -StrRep* Salloc(StrRep*, const char*, int, int); -StrRep* Scopy(StrRep*, StrRep*); -StrRep* Sresize(StrRep*, int); -StrRep* Scat(StrRep*, const char*, int, const char*, int); -StrRep* Scat(StrRep*, const char*, int,const char*,int, const char*,int); -StrRep* Sprepend(StrRep*, const char*, int); -StrRep* Sreverse(StrRep*, StrRep*); -StrRep* Supcase(StrRep*, StrRep*); -StrRep* Sdowncase(StrRep*, StrRep*); -StrRep* Scapitalize(StrRep*, StrRep*); - -// These classes need to be defined in the order given - -class String; -class SubString; - -class SubString -{ - friend class String; -protected: - - String& S; // The String I'm a substring of - unsigned short pos; // starting position in S's rep - unsigned short len; // length of substring - - void assign(StrRep*, const char*, int = -1); - SubString(String& x, int p, int l); - SubString(const SubString& x); - -public: - -// Note there are no public constructors. SubStrings are always -// created via String operations - - ~SubString(); - - void operator = (const String& y); - void operator = (const SubString& y); - void operator = (const char* t); - void operator = (char c); - -// return 1 if target appears anywhere in SubString; else 0 - - int contains(char c) const; - int contains(const String& y) const; - int contains(const SubString& y) const; - int contains(const char* t) const; - int contains(const Regex& r) const; - -// return 1 if target matches entire SubString - - int matches(const Regex& r) const; - -// IO - - friend ostream& operator<<(ostream& s, const SubString& x); - -// status - - int length() const; - int empty() const; - const char* chars() const; - - int OK() const; - -}; - - -class String -{ - friend class SubString; - -protected: - StrRep* rep; // Strings are pointers to their representations - -// some helper functions - - int search(int, int, const char*, int = -1) const; - int search(int, int, char) const; - int match(int, int, int, const char*, int = -1) const; - int _gsub(const char*, int, const char* ,int); - int _gsub(const Regex&, const char*, int); - SubString _substr(int, int); - -public: - -// constructors & assignment - - String(); - String(const String& x); - String(const SubString& x); - String(const char* t); - String(const char* t, int len); - String(char c); - - ~String(); - - void operator = (const String& y); - void operator = (const char* y); - void operator = (char c); - void operator = (const SubString& y); - -// concatenation - - void operator += (const String& y); - void operator += (const SubString& y); - void operator += (const char* t); - void operator += (char c); - - void prepend(const String& y); - void prepend(const SubString& y); - void prepend(const char* t); - void prepend(char c); - - -// procedural versions: -// concatenate first 2 args, store result in last arg - - friend void cat(const String&, const String&, String&); - friend void cat(const String&, const SubString&, String&); - friend void cat(const String&, const char*, String&); - friend void cat(const String&, char, String&); - - friend void cat(const SubString&, const String&, String&); - friend void cat(const SubString&, const SubString&, String&); - friend void cat(const SubString&, const char*, String&); - friend void cat(const SubString&, char, String&); - - friend void cat(const char*, const String&, String&); - friend void cat(const char*, const SubString&, String&); - friend void cat(const char*, const char*, String&); - friend void cat(const char*, char, String&); - -// double concatenation, by request. (yes, there are too many versions, -// but if one is supported, then the others should be too...) -// Concatenate first 3 args, store in last arg - - friend void cat(const String&,const String&, const String&,String&); - friend void cat(const String&,const String&,const SubString&,String&); - friend void cat(const String&,const String&, const char*, String&); - friend void cat(const String&,const String&, char, String&); - friend void cat(const String&,const SubString&,const String&,String&); - friend void cat(const String&,const SubString&,const SubString&,String&); - friend void cat(const String&,const SubString&, const char*, String&); - friend void cat(const String&,const SubString&, char, String&); - friend void cat(const String&,const char*, const String&, String&); - friend void cat(const String&,const char*, const SubString&, String&); - friend void cat(const String&,const char*, const char*, String&); - friend void cat(const String&,const char*, char, String&); - - friend void cat(const char*, const String&, const String&,String&); - friend void cat(const char*,const String&,const SubString&,String&); - friend void cat(const char*,const String&, const char*, String&); - friend void cat(const char*,const String&, char, String&); - friend void cat(const char*,const SubString&,const String&,String&); - friend void cat(const char*,const SubString&,const SubString&,String&); - friend void cat(const char*,const SubString&, const char*, String&); - friend void cat(const char*,const SubString&, char, String&); - friend void cat(const char*,const char*, const String&, String&); - friend void cat(const char*,const char*, const SubString&, String&); - friend void cat(const char*,const char*, const char*, String&); - friend void cat(const char*,const char*, char, String&); - - -// searching & matching - -// return position of target in string or -1 for failure - - int index(char c, int startpos = 0) const; - int index(const String& y, int startpos = 0) const; - int index(const SubString& y, int startpos = 0) const; - int index(const char* t, int startpos = 0) const; - int index(const Regex& r, int startpos = 0) const; - -// return 1 if target appears anyhere in String; else 0 - - int contains(char c) const; - int contains(const String& y) const; - int contains(const SubString& y) const; - int contains(const char* t) const; - int contains(const Regex& r) const; - -// return 1 if target appears anywhere after position pos -// (or before, if pos is negative) in String; else 0 - - int contains(char c, int pos) const; - int contains(const String& y, int pos) const; - int contains(const SubString& y, int pos) const; - int contains(const char* t, int pos) const; - int contains(const Regex& r, int pos) const; - -// return 1 if target appears at position pos in String; else 0 - - int matches(char c, int pos = 0) const; - int matches(const String& y, int pos = 0) const; - int matches(const SubString& y, int pos = 0) const; - int matches(const char* t, int pos = 0) const; - int matches(const Regex& r, int pos = 0) const; - -// return number of occurences of target in String - - int freq(char c) const; - int freq(const String& y) const; - int freq(const SubString& y) const; - int freq(const char* t) const; - -// SubString extraction - -// Note that you can't take a substring of a const String, since -// this leaves open the possiblility of indirectly modifying the -// String through the SubString - - SubString at(int pos, int len); - SubString operator () (int pos, int len); // synonym for at - - SubString at(const String& x, int startpos = 0); - SubString at(const SubString& x, int startpos = 0); - SubString at(const char* t, int startpos = 0); - SubString at(char c, int startpos = 0); - SubString at(const Regex& r, int startpos = 0); - - SubString before(int pos); - SubString before(const String& x, int startpos = 0); - SubString before(const SubString& x, int startpos = 0); - SubString before(const char* t, int startpos = 0); - SubString before(char c, int startpos = 0); - SubString before(const Regex& r, int startpos = 0); - - SubString through(int pos); - SubString through(const String& x, int startpos = 0); - SubString through(const SubString& x, int startpos = 0); - SubString through(const char* t, int startpos = 0); - SubString through(char c, int startpos = 0); - SubString through(const Regex& r, int startpos = 0); - - SubString from(int pos); - SubString from(const String& x, int startpos = 0); - SubString from(const SubString& x, int startpos = 0); - SubString from(const char* t, int startpos = 0); - SubString from(char c, int startpos = 0); - SubString from(const Regex& r, int startpos = 0); - - SubString after(int pos); - SubString after(const String& x, int startpos = 0); - SubString after(const SubString& x, int startpos = 0); - SubString after(const char* t, int startpos = 0); - SubString after(char c, int startpos = 0); - SubString after(const Regex& r, int startpos = 0); - - -// deletion - -// delete len chars starting at pos - void del(int pos, int len); - -// delete the first occurrence of target after startpos - - void del(const String& y, int startpos = 0); - void del(const SubString& y, int startpos = 0); - void del(const char* t, int startpos = 0); - void del(char c, int startpos = 0); - void del(const Regex& r, int startpos = 0); - -// global substitution: substitute all occurrences of pat with repl - - int gsub(const String& pat, const String& repl); - int gsub(const SubString& pat, const String& repl); - int gsub(const char* pat, const String& repl); - int gsub(const char* pat, const char* repl); - int gsub(const Regex& pat, const String& repl); - -// friends & utilities - -// split string into array res at separators; return number of elements - - friend int split(const String& x, String res[], int maxn, - const String& sep); - friend int split(const String& x, String res[], int maxn, - const Regex& sep); - - friend String common_prefix(const String& x, const String& y, - int startpos = 0); - friend String common_suffix(const String& x, const String& y, - int startpos = -1); - friend String replicate(char c, int n); - friend String replicate(const String& y, int n); - friend String join(String src[], int n, const String& sep); - -// simple builtin transformations - - friend String reverse(const String& x); - friend String upcase(const String& x); - friend String downcase(const String& x); - friend String capitalize(const String& x); - -// in-place versions of above - - void reverse(); - void upcase(); - void downcase(); - void capitalize(); - -// element extraction - - char& operator [] (int i); - char elem(int i) const; - char firstchar() const; - char lastchar() const; - -// conversion - - operator const char*() const; - const char* chars() const; - - -// IO - - friend ostream& operator<<(ostream& s, const String& x); - friend ostream& operator<<(ostream& s, const SubString& x); - friend istream& operator>>(istream& s, String& x); - - friend int readline(istream& s, String& x, - char terminator = '\n', - int discard_terminator = 1); - -// status - - int length() const; - int empty() const; - -// preallocate some space for String - void alloc(int newsize); - -// report current allocation (not length!) - - int allocation() const; - - - volatile void error(const char* msg) const; - - int OK() const; -}; - -typedef String StrTmp; // for backward compatibility - -// other externs - -int compare(const String& x, const String& y); -int compare(const String& x, const SubString& y); -int compare(const String& x, const char* y); -int compare(const SubString& x, const String& y); -int compare(const SubString& x, const SubString& y); -int compare(const SubString& x, const char* y); -int fcompare(const String& x, const String& y); // ignore case - -extern StrRep _nilStrRep; -extern String _nilString; - -// other inlines - -String operator + (const String& x, const String& y); -String operator + (const String& x, const SubString& y); -String operator + (const String& x, const char* y); -String operator + (const String& x, char y); -String operator + (const SubString& x, const String& y); -String operator + (const SubString& x, const SubString& y); -String operator + (const SubString& x, const char* y); -String operator + (const SubString& x, char y); -String operator + (const char* x, const String& y); -String operator + (const char* x, const SubString& y); - -int operator==(const String& x, const String& y); -int operator!=(const String& x, const String& y); -int operator> (const String& x, const String& y); -int operator>=(const String& x, const String& y); -int operator< (const String& x, const String& y); -int operator<=(const String& x, const String& y); -int operator==(const String& x, const SubString& y); -int operator!=(const String& x, const SubString& y); -int operator> (const String& x, const SubString& y); -int operator>=(const String& x, const SubString& y); -int operator< (const String& x, const SubString& y); -int operator<=(const String& x, const SubString& y); -int operator==(const String& x, const char* t); -int operator!=(const String& x, const char* t); -int operator> (const String& x, const char* t); -int operator>=(const String& x, const char* t); -int operator< (const String& x, const char* t); -int operator<=(const String& x, const char* t); -int operator==(const SubString& x, const String& y); -int operator!=(const SubString& x, const String& y); -int operator> (const SubString& x, const String& y); -int operator>=(const SubString& x, const String& y); -int operator< (const SubString& x, const String& y); -int operator<=(const SubString& x, const String& y); -int operator==(const SubString& x, const SubString& y); -int operator!=(const SubString& x, const SubString& y); -int operator> (const SubString& x, const SubString& y); -int operator>=(const SubString& x, const SubString& y); -int operator< (const SubString& x, const SubString& y); -int operator<=(const SubString& x, const SubString& y); -int operator==(const SubString& x, const char* t); -int operator!=(const SubString& x, const char* t); -int operator> (const SubString& x, const char* t); -int operator>=(const SubString& x, const char* t); -int operator< (const SubString& x, const char* t); -int operator<=(const SubString& x, const char* t); - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - - -// status reports, needed before defining other things - -inline int String::length() const { return rep->len; } -inline int String::empty() const { return rep->len == 0; } -inline const char* String::chars() const { return &(rep->s[0]); } -inline int String::allocation() const { return rep->sz; } -inline void String::alloc(int newsize) { rep = Sresize(rep, newsize); } - -inline int SubString::length() const { return len; } -inline int SubString::empty() const { return len == 0; } -inline const char* SubString::chars() const { return &(S.rep->s[pos]); } - - -// constructors - -inline String::String() - : rep(&_nilStrRep) {} -inline String::String(const String& x) - : rep(Scopy(0, x.rep)) {} -inline String::String(const char* t) - : rep(Salloc(0, t, -1, -1)) {} -inline String::String(const char* t, int tlen) - : rep(Salloc(0, t, tlen, tlen)) {} -inline String::String(const SubString& y) - : rep(Salloc(0, y.chars(), y.length(), y.length())) {} -inline String::String(char c) - : rep(Salloc(0, &c, 1, 1)) {} - -inline String::~String() { if (rep != &_nilStrRep) delete rep; } - -inline SubString::SubString(const SubString& x) - :S(x.S), pos(x.pos), len(x.len) {} -inline SubString::SubString(String& x, int first, int l) - :S(x), pos(first), len(l) {} - -inline SubString::~SubString() {} - -// assignment - -inline void String::operator = (const String& y) -{ - rep = Scopy(rep, y.rep); -} - -inline void String::operator=(const char* t) -{ - rep = Salloc(rep, t, -1, -1); -} - -inline void String::operator=(const SubString& y) -{ - rep = Salloc(rep, y.chars(), y.length(), y.length()); -} - -inline void String::operator=(char c) -{ - rep = Salloc(rep, &c, 1, 1); -} - - -inline void SubString::operator = (const char* ys) -{ - assign(0, ys); -} - -inline void SubString::operator = (char ch) -{ - assign(0, &ch, 1); -} - -inline void SubString::operator = (const String& y) -{ - assign(y.rep, y.chars(), y.length()); -} - -inline void SubString::operator = (const SubString& y) -{ - assign(y.S.rep, y.chars(), y.length()); -} - -// Zillions of cats... - -inline void cat(const String& x, const String& y, String& r) -{ - r.rep = Scat(r.rep, x.chars(), x.length(), y.chars(), y.length()); -} - -inline void cat(const String& x, const SubString& y, String& r) -{ - r.rep = Scat(r.rep, x.chars(), x.length(), y.chars(), y.length()); -} - -inline void cat(const String& x, const char* y, String& r) -{ - r.rep = Scat(r.rep, x.chars(), x.length(), y, -1); -} - -inline void cat(const String& x, char y, String& r) -{ - r.rep = Scat(r.rep, x.chars(), x.length(), &y, 1); -} - -inline void cat(const SubString& x, const String& y, String& r) -{ - r.rep = Scat(r.rep, x.chars(), x.length(), y.chars(), y.length()); -} - -inline void cat(const SubString& x, const SubString& y, String& r) -{ - r.rep = Scat(r.rep, x.chars(), x.length(), y.chars(), y.length()); -} - -inline void cat(const SubString& x, const char* y, String& r) -{ - r.rep = Scat(r.rep, x.chars(), x.length(), y, -1); -} - -inline void cat(const SubString& x, char y, String& r) -{ - r.rep = Scat(r.rep, x.chars(), x.length(), &y, 1); -} - -inline void cat(const char* x, const String& y, String& r) -{ - r.rep = Scat(r.rep, x, -1, y.chars(), y.length()); -} - -inline void cat(const char* x, const SubString& y, String& r) -{ - r.rep = Scat(r.rep, x, -1, y.chars(), y.length()); -} - -inline void cat(const char* x, const char* y, String& r) -{ - r.rep = Scat(r.rep, x, -1, y, -1); -} - -inline void cat(const char* x, char y, String& r) -{ - r.rep = Scat(r.rep, x, -1, &y, 1); -} - -inline void cat(const String& a, const String& x, const String& y, String& r) -{ - r.rep = Scat(r.rep, a.chars(), a.length(), x.chars(), x.length(), y.chars(), y.length()); -} - -inline void cat(const String& a, const String& x, const SubString& y, String& r) -{ - r.rep = Scat(r.rep, a.chars(), a.length(), x.chars(), x.length(), y.chars(), y.length()); -} - -inline void cat(const String& a, const String& x, const char* y, String& r) -{ - r.rep = Scat(r.rep, a.chars(), a.length(), x.chars(), x.length(), y, -1); -} - -inline void cat(const String& a, const String& x, char y, String& r) -{ - r.rep = Scat(r.rep, a.chars(), a.length(), x.chars(), x.length(), &y, 1); -} - -inline void cat(const String& a, const SubString& x, const String& y, String& r) -{ - r.rep = Scat(r.rep, a.chars(), a.length(), x.chars(), x.length(), y.chars(), y.length()); -} - -inline void cat(const String& a, const SubString& x, const SubString& y, String& r) -{ - r.rep = Scat(r.rep, a.chars(), a.length(), x.chars(), x.length(), y.chars(), y.length()); -} - -inline void cat(const String& a, const SubString& x, const char* y, String& r) -{ - r.rep = Scat(r.rep, a.chars(), a.length(), x.chars(), x.length(), y, -1); -} - -inline void cat(const String& a, const SubString& x, char y, String& r) -{ - r.rep = Scat(r.rep, a.chars(), a.length(), x.chars(), x.length(), &y, 1); -} - -inline void cat(const String& a, const char* x, const String& y, String& r) -{ - r.rep = Scat(r.rep, a.chars(), a.length(), x, -1, y.chars(), y.length()); -} - -inline void cat(const String& a, const char* x, const SubString& y, String& r) -{ - r.rep = Scat(r.rep, a.chars(), a.length(), x, -1, y.chars(), y.length()); -} - -inline void cat(const String& a, const char* x, const char* y, String& r) -{ - r.rep = Scat(r.rep, a.chars(), a.length(), x, -1, y, -1); -} - -inline void cat(const String& a, const char* x, char y, String& r) -{ - r.rep = Scat(r.rep, a.chars(), a.length(), x, -1, &y, 1); -} - - -inline void cat(const char* a, const String& x, const String& y, String& r) -{ - r.rep = Scat(r.rep, a, -1, x.chars(), x.length(), y.chars(), y.length()); -} - -inline void cat(const char* a, const String& x, const SubString& y, String& r) -{ - r.rep = Scat(r.rep, a, -1, x.chars(), x.length(), y.chars(), y.length()); -} - -inline void cat(const char* a, const String& x, const char* y, String& r) -{ - r.rep = Scat(r.rep, a, -1, x.chars(), x.length(), y, -1); -} - -inline void cat(const char* a, const String& x, char y, String& r) -{ - r.rep = Scat(r.rep, a, -1, x.chars(), x.length(), &y, 1); -} - -inline void cat(const char* a, const SubString& x, const String& y, String& r) -{ - r.rep = Scat(r.rep, a, -1, x.chars(), x.length(), y.chars(), y.length()); -} - -inline void cat(const char* a, const SubString& x, const SubString& y, String& r) -{ - r.rep = Scat(r.rep, a, -1, x.chars(), x.length(), y.chars(), y.length()); -} - -inline void cat(const char* a, const SubString& x, const char* y, String& r) -{ - r.rep = Scat(r.rep, a, -1, x.chars(), x.length(), y, -1); -} - -inline void cat(const char* a, const SubString& x, char y, String& r) -{ - r.rep = Scat(r.rep, a, -1, x.chars(), x.length(), &y, 1); -} - -inline void cat(const char* a, const char* x, const String& y, String& r) -{ - r.rep = Scat(r.rep, a, -1, x, -1, y.chars(), y.length()); -} - -inline void cat(const char* a, const char* x, const SubString& y, String& r) -{ - r.rep = Scat(r.rep, a, -1, x, -1, y.chars(), y.length()); -} - -inline void cat(const char* a, const char* x, const char* y, String& r) -{ - r.rep = Scat(r.rep, a, -1, x, -1, y, -1); -} - -inline void cat(const char* a, const char* x, char y, String& r) -{ - r.rep = Scat(r.rep, a, -1, x, -1, &y, 1); -} - - -// operator versions - -inline void String::operator +=(const String& y) -{ - cat(*this, y, *this); -} - -inline void String::operator +=(const SubString& y) -{ - cat(*this, y, *this); -} - -inline void String::operator += (const char* y) -{ - cat(*this, y, *this); -} - -inline void String:: operator +=(char y) -{ - cat(*this, y, *this); -} - -// constructive concatenation - -#if defined(__GNUG__) && !defined(NO_NRV) - -inline String operator + (const String& x, const String& y) return r; -{ - cat(x, y, r); -} - -inline String operator + (const String& x, const SubString& y) return r; -{ - cat(x, y, r); -} - -inline String operator + (const String& x, const char* y) return r; -{ - cat(x, y, r); -} - -inline String operator + (const String& x, char y) return r; -{ - cat(x, y, r); -} - -inline String operator + (const SubString& x, const String& y) return r; -{ - cat(x, y, r); -} - -inline String operator + (const SubString& x, const SubString& y) return r; -{ - cat(x, y, r); -} - -inline String operator + (const SubString& x, const char* y) return r; -{ - cat(x, y, r); -} - -inline String operator + (const SubString& x, char y) return r; -{ - cat(x, y, r); -} - -inline String operator + (const char* x, const String& y) return r; -{ - cat(x, y, r); -} - -inline String operator + (const char* x, const SubString& y) return r; -{ - cat(x, y, r); -} - -inline String reverse(const String& x) return r; -{ - r.rep = Sreverse(x.rep, r.rep); -} - -inline String upcase(const String& x) return r; -{ - r.rep = Supcase(x.rep, r.rep); -} - -inline String downcase(const String& x) return r; -{ - r.rep = Sdowncase(x.rep, r.rep); -} - -inline String capitalize(const String& x) return r; -{ - r.rep = Scapitalize(x.rep, r.rep); -} - -#else /* NO_NRV */ - -inline String operator + (const String& x, const String& y) -{ - String r; cat(x, y, r); return r; -} - -inline String operator + (const String& x, const SubString& y) -{ - String r; cat(x, y, r); return r; -} - -inline String operator + (const String& x, const char* y) -{ - String r; cat(x, y, r); return r; -} - -inline String operator + (const String& x, char y) -{ - String r; cat(x, y, r); return r; -} - -inline String operator + (const SubString& x, const String& y) -{ - String r; cat(x, y, r); return r; -} - -inline String operator + (const SubString& x, const SubString& y) -{ - String r; cat(x, y, r); return r; -} - -inline String operator + (const SubString& x, const char* y) -{ - String r; cat(x, y, r); return r; -} - -inline String operator + (const SubString& x, char y) -{ - String r; cat(x, y, r); return r; -} - -inline String operator + (const char* x, const String& y) -{ - String r; cat(x, y, r); return r; -} - -inline String operator + (const char* x, const SubString& y) -{ - String r; cat(x, y, r); return r; -} - -inline String reverse(const String& x) -{ - String r; r.rep = Sreverse(x.rep, r.rep); return r; -} - -inline String upcase(const String& x) -{ - String r; r.rep = Supcase(x.rep, r.rep); return r; -} - -inline String downcase(const String& x) -{ - String r; r.rep = Sdowncase(x.rep, r.rep); return r; -} - -inline String capitalize(const String& x) -{ - String r; r.rep = Scapitalize(x.rep, r.rep); return r; -} - -#endif - -// prepend - -inline void String::prepend(const String& y) -{ - rep = Sprepend(rep, y.chars(), y.length()); -} - -inline void String::prepend(const char* y) -{ - rep = Sprepend(rep, y, -1); -} - -inline void String::prepend(char y) -{ - rep = Sprepend(rep, &y, 1); -} - -inline void String::prepend(const SubString& y) -{ - rep = Sprepend(rep, y.chars(), y.length()); -} - -// misc transformations - - -inline void String::reverse() -{ - rep = Sreverse(rep, rep); -} - - -inline void String::upcase() -{ - rep = Supcase(rep, rep); -} - - -inline void String::downcase() -{ - rep = Sdowncase(rep, rep); -} - - -inline void String::capitalize() -{ - rep = Scapitalize(rep, rep); -} - -// element extraction - -inline char& String::operator [] (int i) -{ - if (((unsigned)i) >= length()) error("invalid index"); - return rep->s[i]; -} - -inline char String::elem (int i) const -{ - if (((unsigned)i) >= length()) error("invalid index"); - return rep->s[i]; -} - -inline char String::firstchar() const -{ - return elem(0); -} - -inline char String::lastchar() const -{ - return elem(length() - 1); -} - -// searching - -inline int String::index(char c, int startpos) const -{ - return search(startpos, length(), c); -} - -inline int String::index(const char* t, int startpos) const -{ - return search(startpos, length(), t); -} - -inline int String::index(const String& y, int startpos) const -{ - return search(startpos, length(), y.chars(), y.length()); -} - -inline int String::index(const SubString& y, int startpos) const -{ - return search(startpos, length(), y.chars(), y.length()); -} - -inline int String::index(const Regex& r, int startpos) const -{ - int unused; return r.search(chars(), length(), unused, startpos); -} - -inline int String::contains(char c) const -{ - return search(0, length(), c) >= 0; -} - -inline int String::contains(const char* t) const -{ - return search(0, length(), t) >= 0; -} - -inline int String::contains(const String& y) const -{ - return search(0, length(), y.chars(), y.length()) >= 0; -} - -inline int String::contains(const SubString& y) const -{ - return search(0, length(), y.chars(), y.length()) >= 0; -} - -inline int String::contains(char c, int p) const -{ - return match(p, length(), 0, &c, 1) >= 0; -} - -inline int String::contains(const char* t, int p) const -{ - return match(p, length(), 0, t) >= 0; -} - -inline int String::contains(const String& y, int p) const -{ - return match(p, length(), 0, y.chars(), y.length()) >= 0; -} - -inline int String::contains(const SubString& y, int p) const -{ - return match(p, length(), 0, y.chars(), y.length()) >= 0; -} - -inline int String::contains(const Regex& r) const -{ - int unused; return r.search(chars(), length(), unused, 0) >= 0; -} - -inline int String::contains(const Regex& r, int p) const -{ - return r.match(chars(), length(), p) >= 0; -} - - -inline int String::matches(const SubString& y, int p) const -{ - return match(p, length(), 1, y.chars(), y.length()) >= 0; -} - -inline int String::matches(const String& y, int p) const -{ - return match(p, length(), 1, y.chars(), y.length()) >= 0; -} - -inline int String::matches(const char* t, int p) const -{ - return match(p, length(), 1, t) >= 0; -} - -inline int String::matches(char c, int p) const -{ - return match(p, length(), 1, &c, 1) >= 0; -} - -inline int String::matches(const Regex& r, int p) const -{ - int l = (p < 0)? -p : length() - p; - return r.match(chars(), length(), p) == l; -} - - -inline int SubString::contains(const char* t) const -{ - return S.search(pos, pos+len, t) >= 0; -} - -inline int SubString::contains(const String& y) const -{ - return S.search(pos, pos+len, y.chars(), y.length()) >= 0; -} - -inline int SubString::contains(const SubString& y) const -{ - return S.search(pos, pos+len, y.chars(), y.length()) >= 0; -} - -inline int SubString::contains(char c) const -{ - return S.search(pos, pos+len, 0, c) >= 0; -} - -inline int SubString::contains(const Regex& r) const -{ - int unused; return r.search(chars(), len, unused, 0) >= 0; -} - -inline int SubString::matches(const Regex& r) const -{ - return r.match(chars(), len, 0) == len; -} - - -inline int String::gsub(const String& pat, const String& r) -{ - return _gsub(pat.chars(), pat.length(), r.chars(), r.length()); -} - -inline int String::gsub(const SubString& pat, const String& r) -{ - return _gsub(pat.chars(), pat.length(), r.chars(), r.length()); -} - -inline int String::gsub(const Regex& pat, const String& r) -{ - return _gsub(pat, r.chars(), r.length()); -} - -inline int String::gsub(const char* pat, const String& r) -{ - return _gsub(pat, -1, r.chars(), r.length()); -} - -inline int String::gsub(const char* pat, const char* r) -{ - return _gsub(pat, -1, r, -1); -} - - -inline String::operator const char*() const -{ - return str(chars()); -} - -inline ostream& operator<<(ostream& s, const String& x) -{ -#ifdef VMS - s << x.chars(); return s; -#else - s.put(x.chars()); return s; -#endif - -} - -// a zillion comparison operators - -inline int operator==(const String& x, const String& y) -{ - return compare(x, y) == 0; -} - -inline int operator!=(const String& x, const String& y) -{ - return compare(x, y) != 0; -} - -inline int operator>(const String& x, const String& y) -{ - return compare(x, y) > 0; -} - -inline int operator>=(const String& x, const String& y) -{ - return compare(x, y) >= 0; -} - -inline int operator<(const String& x, const String& y) -{ - return compare(x, y) < 0; -} - -inline int operator<=(const String& x, const String& y) -{ - return compare(x, y) <= 0; -} - -inline int operator==(const String& x, const SubString& y) -{ - return compare(x, y) == 0; -} - -inline int operator!=(const String& x, const SubString& y) -{ - return compare(x, y) != 0; -} - -inline int operator>(const String& x, const SubString& y) -{ - return compare(x, y) > 0; -} - -inline int operator>=(const String& x, const SubString& y) -{ - return compare(x, y) >= 0; -} - -inline int operator<(const String& x, const SubString& y) -{ - return compare(x, y) < 0; -} - -inline int operator<=(const String& x, const SubString& y) -{ - return compare(x, y) <= 0; -} - -inline int operator==(const String& x, const char* t) -{ - return compare(x, t) == 0; -} - -inline int operator!=(const String& x, const char* t) -{ - return compare(x, t) != 0; -} - -inline int operator>(const String& x, const char* t) -{ - return compare(x, t) > 0; -} - -inline int operator>=(const String& x, const char* t) -{ - return compare(x, t) >= 0; -} - -inline int operator<(const String& x, const char* t) -{ - return compare(x, t) < 0; -} - -inline int operator<=(const String& x, const char* t) -{ - return compare(x, t) <= 0; -} - -inline int operator==(const SubString& x, const String& y) -{ - return compare(y, x) == 0; -} - -inline int operator!=(const SubString& x, const String& y) -{ - return compare(y, x) != 0; -} - -inline int operator>(const SubString& x, const String& y) -{ - return compare(y, x) < 0; -} - -inline int operator>=(const SubString& x, const String& y) -{ - return compare(y, x) <= 0; -} - -inline int operator<(const SubString& x, const String& y) -{ - return compare(y, x) > 0; -} - -inline int operator<=(const SubString& x, const String& y) -{ - return compare(y, x) >= 0; -} - -inline int operator==(const SubString& x, const SubString& y) -{ - return compare(x, y) == 0; -} - -inline int operator!=(const SubString& x, const SubString& y) -{ - return compare(x, y) != 0; -} - -inline int operator>(const SubString& x, const SubString& y) -{ - return compare(x, y) > 0; -} - -inline int operator>=(const SubString& x, const SubString& y) -{ - return compare(x, y) >= 0; -} - -inline int operator<(const SubString& x, const SubString& y) -{ - return compare(x, y) < 0; -} - -inline int operator<=(const SubString& x, const SubString& y) -{ - return compare(x, y) <= 0; -} - -inline int operator==(const SubString& x, const char* t) -{ - return compare(x, t) == 0; -} - -inline int operator!=(const SubString& x, const char* t) -{ - return compare(x, t) != 0; -} - -inline int operator>(const SubString& x, const char* t) -{ - return compare(x, t) > 0; -} - -inline int operator>=(const SubString& x, const char* t) -{ - return compare(x, t) >= 0; -} - -inline int operator<(const SubString& x, const char* t) -{ - return compare(x, t) < 0; -} - -inline int operator<=(const SubString& x, const char* t) -{ - return compare(x, t) <= 0; -} - - -// a helper needed by at, before, etc. - -inline SubString String::_substr(int first, int l) -{ - if (first < 0 || (unsigned)(first + l) > length()) - return SubString(_nilString, 0, 0) ; - else - return SubString(*this, first, l); -} - - -#endif - -#endif diff --git a/gnu/lib/libg++/g++-include/Uniform.h b/gnu/lib/libg++/g++-include/Uniform.h deleted file mode 100644 index 5d896e8f0c..0000000000 --- a/gnu/lib/libg++/g++-include/Uniform.h +++ /dev/null @@ -1,81 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ -#ifndef _Uniform_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Uniform_h 1 - -#include - -// -// The interval [lo..hi] -// - -class Uniform: public Random { - double pLow; - double pHigh; - double delta; -public: - Uniform(double low, double high, RNG *gen); - - double low(); - double low(double x); - double high(); - double high(double x); - - virtual double operator()(); -}; - - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline Uniform::Uniform(double low, double high, RNG *gen):(gen) -{ - pLow = (low < high) ? low : high; - pHigh = (low < high) ? high : low; - delta = pHigh - pLow; -} - -inline double Uniform::low() { return pLow; } - -inline double Uniform::low(double x) { - double tmp = pLow; - pLow = x; - delta = pHigh - pLow; - return tmp; -} - -inline double Uniform::high() { return pHigh; } - -inline double Uniform::high(double x) { - double tmp = pHigh; - pHigh = x; - delta = pHigh - pLow; - return tmp; -} - - -#endif -#endif diff --git a/gnu/lib/libg++/g++-include/VHBag.ccP b/gnu/lib/libg++/g++-include/VHBag.ccP new file mode 100644 index 0000000000..81a20eb140 --- /dev/null +++ b/gnu/lib/libg++/g++-include/VHBag.ccP @@ -0,0 +1,264 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".VHBag.h" + +/* codes for status fields */ + +#define EMPTYCELL 0 +#define VALIDCELL 1 +#define DELETEDCELL 2 + + +VHBag::VHBag(unsigned int sz) +{ + tab = new [size = sz]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; +} + +VHBag::VHBag(VHBag& a) +{ + tab = new [size = a.size]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; + for (Pix p = a.first(); p; a.next(p)) add(a(p)); +} + + +/* + * hashing method: double hash based on high bits of hash fct, + * followed by linear probe. Can't do too much better if table + * sizes not constrained to be prime. +*/ + + +static inline unsigned int doublehashinc(unsigned int h, unsigned int s) +{ + unsigned int dh = ((h / s) % s); + return (dh > 1)? dh : 1; +} + +Pix VHBag::seek( key, Pix p) +{ + * t = (*) p; + if (t == 0 || !EQ(*t, key)) + { + unsigned int hashval = HASH(key); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return 0; + else if (status[h] == VALIDCELL && EQ(key, tab[h])) + return Pix(&tab[h]); + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } + return 0; + } + else + { + int seent = 0; + unsigned int hashval = HASH(key); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return 0; + else if (&tab[h] == t) + seent = 1; + else if (seent && status[h] == VALIDCELL && EQ(key, tab[h])) + return Pix(&tab[h]); + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } + return 0; + } +} + +int VHBag::nof( item) +{ + int n = 0; + unsigned int hashval = HASH(item); + unsigned int h = hashval % size; + unsigned int firsth = size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return n; + else if (h != firsth && status[h] == VALIDCELL && EQ(item, tab[h])) + { + ++n; + if (firsth >= size) + firsth = h; + } + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } + return n; +} + + +Pix VHBag::add( item) +{ + if (HASHTABLE_TOO_CROWDED(count, size)) + resize(); + unsigned int bestspot = size; + unsigned int hashval = HASH(item); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + { + if (bestspot >= size) bestspot = h; + tab[bestspot] = item; + status[bestspot] = VALIDCELL; + ++count; + return Pix(&tab[bestspot]); + } + else if (status[h] == DELETEDCELL) + { + if (bestspot >= size) bestspot = h; + } + + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } + tab[bestspot] = item; + status[bestspot] = VALIDCELL; + ++count; + return Pix(&tab[bestspot]); +} + + +void VHBag::del( key) +{ + unsigned int hashval = HASH(key); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return; + else if (status[h] == VALIDCELL && EQ(key, tab[h])) + { + status[h] = DELETEDCELL; + --count; + return; + } + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } +} + +void VHBag::remove( key) +{ + unsigned int hashval = HASH(key); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return; + else if (status[h] == VALIDCELL && EQ(key, tab[h])) + { + status[h] = DELETEDCELL; + --count; + } + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } +} + +void VHBag::clear() +{ + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; +} + +void VHBag::resize(unsigned int newsize) +{ + if (newsize <= count) + { + newsize = DEFAULT_INITIAL_CAPACITY; + while (HASHTABLE_TOO_CROWDED(count, newsize)) newsize <<= 1; + } + * oldtab = tab; + char* oldstatus = status; + unsigned int oldsize = size; + tab = new [size = newsize]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; + for (i = 0; i < oldsize; ++i) if (oldstatus[i] == VALIDCELL) add(oldtab[i]); + delete [] oldtab; + delete oldstatus; +} + +Pix VHBag::first() +{ + for (unsigned int pos = 0; pos < size; ++pos) + if (status[pos] == VALIDCELL) return Pix(&tab[pos]); + return 0; +} + +void VHBag::next(Pix& i) +{ + if (i == 0) return; + unsigned int pos = ((unsigned)i - (unsigned)tab) / sizeof() + 1; + for (; pos < size; ++pos) + if (status[pos] == VALIDCELL) + { + i = Pix(&tab[pos]); + return; + } + i = 0; +} + + +int VHBag::OK() +{ + int v = tab != 0; + v &= status != 0; + int n = 0; + for (unsigned int i = 0; i < size; ++i) + { + if (status[i] == VALIDCELL) ++n; + else if (status[i] != DELETEDCELL && status[i] != EMPTYCELL) + v = 0; + } + v &= n == count; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/VHBag.hP b/gnu/lib/libg++/g++-include/VHBag.hP new file mode 100644 index 0000000000..72c774a559 --- /dev/null +++ b/gnu/lib/libg++/g++-include/VHBag.hP @@ -0,0 +1,84 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _VHBag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _VHBag_h 1 + +#include ".Bag.h" + + +class VHBag : public Bag +{ +protected: + * tab; + char* status; + unsigned int size; + +public: + VHBag(unsigned int sz = DEFAULT_INITIAL_CAPACITY); + VHBag(VHBag& a); + ~VHBag(); + + Pix add( item); + void del( item); + void remove(item); + int nof( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + Pix seek( item, Pix from = 0); + + int capacity(); + void resize(unsigned int newsize = 0); + + int OK(); +}; + + +inline VHBag::~VHBag() +{ + delete [] tab; + delete status; +} + + +inline int VHBag::capacity() +{ + return size; +} + +inline int VHBag::contains( key) +{ + return seek(key) != 0; +} + +inline & VHBag::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return *((*)i); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/VHMap.ccP b/gnu/lib/libg++/g++-include/VHMap.ccP new file mode 100644 index 0000000000..d6b60e997a --- /dev/null +++ b/gnu/lib/libg++/g++-include/VHMap.ccP @@ -0,0 +1,210 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include "..VHMap.h" + +/* codes for status fields */ + +#define EMPTYCELL 0 +#define VALIDCELL 1 +#define DELETEDCELL 2 + + +VHMap::VHMap( dflt, unsigned int sz) + :Map(dflt) +{ + tab = new [size = sz]; + cont = new [size]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; +} + +VHMap::VHMap(VHMap& a) : Map(a.def) +{ + tab = new [size = a.size]; + cont = new [size]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; + for (Pix p = a.first(); p; a.next(p)) (*this)[a.key(p)] = a.contents(p); +} + + +/* + * hashing method: double hash based on high bits of hash fct, + * followed by linear probe. Can't do too much better if table + * sizes not constrained to be prime. +*/ + + +static inline unsigned int doublehashinc(unsigned int h, unsigned int s) +{ + unsigned int dh = ((h / s) % s); + return (dh > 1)? dh : 1; +} + +Pix VHMap::seek( key) +{ + unsigned int hashval = HASH(key); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return 0; + else if (status[h] == VALIDCELL && EQ(key, tab[h])) + return Pix(&tab[h]); + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } + return 0; +} + + +& VHMap::operator []( item) +{ + if (HASHTABLE_TOO_CROWDED(count, size)) + resize(); + + unsigned int bestspot = size; + unsigned int hashval = HASH(item); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + { + ++count; + if (bestspot >= size) bestspot = h; + tab[bestspot] = item; + status[bestspot] = VALIDCELL; + cont[bestspot] = def; + return cont[bestspot]; + } + else if (status[h] == DELETEDCELL) + { + if (bestspot >= size) bestspot = h; + } + else if (EQ(tab[h],item)) + return cont[h]; + + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } + + ++count; + status[bestspot] = VALIDCELL; + tab[bestspot] = item; + cont[bestspot] = def; + return cont[bestspot]; +} + + +void VHMap::del( key) +{ + unsigned int hashval = HASH(key); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return; + else if (status[h] == VALIDCELL && EQ(key, tab[h])) + { + status[h] = DELETEDCELL; + --count; + return; + } + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } +} + + +void VHMap::clear() +{ + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; +} + +void VHMap::resize(unsigned int newsize) +{ + if (newsize <= count) + { + newsize = DEFAULT_INITIAL_CAPACITY; + while (HASHTABLE_TOO_CROWDED(count, newsize)) newsize <<= 1; + } + * oldtab = tab; + * oldcont = cont; + char* oldstatus = status; + unsigned int oldsize = size; + tab = new [size = newsize]; + cont = new [size]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; + for (i = 0; i < oldsize; ++i) + if (oldstatus[i] == VALIDCELL) + (*this)[oldtab[i]] = oldcont[i]; + delete [] oldtab; + delete [] oldcont; + delete oldstatus; +} + +Pix VHMap::first() +{ + for (unsigned int pos = 0; pos < size; ++pos) + if (status[pos] == VALIDCELL) return Pix(&tab[pos]); + return 0; +} + +void VHMap::next(Pix& i) +{ + if (i == 0) return; + unsigned int pos = ((unsigned)i - (unsigned)tab) / sizeof() + 1; + for (; pos < size; ++pos) + if (status[pos] == VALIDCELL) + { + i = Pix(&tab[pos]); + return; + } + i = 0; +} + + +int VHMap::OK() +{ + int v = tab != 0; + v &= status != 0; + int n = 0; + for (unsigned int i = 0; i < size; ++i) + { + if (status[i] == VALIDCELL) ++n; + else if (status[i] != DELETEDCELL && status[i] != EMPTYCELL) + v = 0; + } + v &= n == count; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/VHMap.hP b/gnu/lib/libg++/g++-include/VHMap.hP new file mode 100644 index 0000000000..ac8fe4d219 --- /dev/null +++ b/gnu/lib/libg++/g++-include/VHMap.hP @@ -0,0 +1,84 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _VHMap_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _VHMap_h 1 + +#include "..Map.h" + + +class VHMap : public Map +{ +protected: + * tab; + * cont; + char* status; + unsigned int size; + +public: + VHMap( dflt,unsigned int sz=DEFAULT_INITIAL_CAPACITY); + VHMap(VHMap& a); + ~VHMap(); + + & operator [] ( key); + + void del( key); + + Pix first(); + void next(Pix& i); + & key(Pix i); + & contents(Pix i); + + Pix seek( key); + int contains( key); + + void clear(); + void resize(unsigned int newsize = 0); + + int OK(); +}; + +inline VHMap::~VHMap() +{ + delete [] tab; + delete [] cont; + delete [] status; +} + +inline int VHMap::contains( key) +{ + return seek(key) != 0; +} + +inline & VHMap::key(Pix i) +{ + if (i == 0) error("null Pix"); + return *((*)i); +} + +inline & VHMap::contents(Pix i) +{ + if (i == 0) error("null Pix"); + return cont[((unsigned)(i) - (unsigned)(tab)) / sizeof()]; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/VHSet.ccP b/gnu/lib/libg++/g++-include/VHSet.ccP new file mode 100644 index 0000000000..a78b319834 --- /dev/null +++ b/gnu/lib/libg++/g++-include/VHSet.ccP @@ -0,0 +1,263 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".VHSet.h" + +/* codes for status fields */ + +#define EMPTYCELL 0 +#define VALIDCELL 1 +#define DELETEDCELL 2 + + +VHSet::VHSet(unsigned int sz) +{ + tab = new [size = sz]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; +} + +VHSet::VHSet(VHSet& a) +{ + tab = new [size = a.size]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; + for (Pix p = a.first(); p; a.next(p)) add(a(p)); +} + + +/* + * hashing method: double hash based on high bits of hash fct, + * followed by linear probe. Can't do too much better if table + * sizes not constrained to be prime. +*/ + + +static inline unsigned int doublehashinc(unsigned int h, unsigned int s) +{ + unsigned int dh = ((h / s) % s); + return (dh > 1)? dh : 1; +} + +Pix VHSet::seek( key) +{ + unsigned int hashval = HASH(key); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return 0; + else if (status[h] == VALIDCELL && EQ(key, tab[h])) + return Pix(&tab[h]); + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } + return 0; +} + + +Pix VHSet::add( item) +{ + if (HASHTABLE_TOO_CROWDED(count, size)) + resize(); + + unsigned int bestspot = size; + unsigned int hashval = HASH(item); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + { + if (bestspot >= size) bestspot = h; + tab[bestspot] = item; + status[bestspot] = VALIDCELL; + ++count; + return Pix(&tab[bestspot]); + } + else if (status[h] == DELETEDCELL) + { + if (bestspot >= size) bestspot = h; + } + else if (EQ(tab[h],item)) + return Pix(&tab[h]); + + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } + tab[bestspot] = item; + status[bestspot] = VALIDCELL; + ++count; + return Pix(&tab[bestspot]); + +} + + +void VHSet::del( key) +{ + unsigned int hashval = HASH(key); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return; + else if (status[h] == VALIDCELL && EQ(key, tab[h])) + { + status[h] = DELETEDCELL; + --count; + return; + } + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } +} + + +void VHSet::clear() +{ + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; +} + +void VHSet::resize(unsigned int newsize) +{ + if (newsize <= count) + { + newsize = DEFAULT_INITIAL_CAPACITY; + while (HASHTABLE_TOO_CROWDED(count, newsize)) newsize <<= 1; + } + * oldtab = tab; + char* oldstatus = status; + unsigned int oldsize = size; + tab = new [size = newsize]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; + for (i = 0; i < oldsize; ++i) if (oldstatus[i] == VALIDCELL) add(oldtab[i]); + delete [] oldtab; + delete oldstatus; +} + +Pix VHSet::first() +{ + for (unsigned int pos = 0; pos < size; ++pos) + if (status[pos] == VALIDCELL) return Pix(&tab[pos]); + return 0; +} + +void VHSet::next(Pix& i) +{ + if (i == 0) return; + unsigned int pos = ((unsigned)i - (unsigned)tab) / sizeof() + 1; + for (; pos < size; ++pos) + if (status[pos] == VALIDCELL) + { + i = Pix(&tab[pos]); + return; + } + i = 0; +} + +int VHSet:: operator == (VHSet& b) +{ + if (count != b.count) + return 0; + else + { + for (unsigned int i = 0; i < size; ++i) + if (status[i] == VALIDCELL && b.seek(tab[i]) == 0) + return 0; + for (i = 0; i < b.size; ++i) + if (b.status[i] == VALIDCELL && seek(b.tab[i]) == 0) + return 0; + return 1; + } +} + +int VHSet::operator <= (VHSet& b) +{ + if (count > b.count) + return 0; + else + { + for (unsigned int i = 0; i < size; ++i) + if (status[i] == VALIDCELL && b.seek(tab[i]) == 0) + return 0; + return 1; + } +} + +void VHSet::operator |= (VHSet& b) +{ + if (&b == this || b.count == 0) + return; + for (unsigned int i = 0; i < b.size; ++i) + if (b.status[i] == VALIDCELL) add(b.tab[i]); +} + +void VHSet::operator &= (VHSet& b) +{ + if (&b == this || count == 0) + return; + for (unsigned int i = 0; i < size; ++i) + { + if (status[i] == VALIDCELL && b.seek(tab[i]) == 0) + { + status[i] = DELETEDCELL; + --count; + } + } +} + +void VHSet::operator -= (VHSet& b) +{ + for (unsigned int i = 0; i < size; ++i) + { + if (status[i] == VALIDCELL && b.seek(tab[i]) != 0) + { + status[i] = DELETEDCELL; + --count; + } + } +} + +int VHSet::OK() +{ + int v = tab != 0; + v &= status != 0; + int n = 0; + for (unsigned int i = 0; i < size; ++i) + { + if (status[i] == VALIDCELL) ++n; + else if (status[i] != DELETEDCELL && status[i] != EMPTYCELL) + v = 0; + } + v &= n == count; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/VHSet.hP b/gnu/lib/libg++/g++-include/VHSet.hP new file mode 100644 index 0000000000..b7b3a3578c --- /dev/null +++ b/gnu/lib/libg++/g++-include/VHSet.hP @@ -0,0 +1,96 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _VHSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _VHSet_h 1 + +#include ".Set.h" + + + +class VHSet : public Set +{ +protected: + * tab; + char* status; + unsigned int size; + +public: + VHSet(unsigned int sz = DEFAULT_INITIAL_CAPACITY); + VHSet(VHSet& a); + ~VHSet(); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + Pix seek( item); + + void operator |= (VHSet& b); + void operator -= (VHSet& b); + void operator &= (VHSet& b); + + int operator == (VHSet& b); + int operator != (VHSet& b); + int operator <= (VHSet& b); + + int capacity(); + void resize(unsigned int newsize = 0); + + int OK(); +}; + + +inline VHSet::~VHSet() +{ + delete [] tab; + delete status; +} + + +inline int VHSet::capacity() +{ + return size; +} + +inline int VHSet::contains( key) +{ + return seek(key) != 0; +} + +inline & VHSet::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return *((*)i); +} + +inline int VHSet::operator != (VHSet& b) +{ + return ! ((*this) == b); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/VOHSet.ccP b/gnu/lib/libg++/g++-include/VOHSet.ccP new file mode 100644 index 0000000000..c5d4557a4c --- /dev/null +++ b/gnu/lib/libg++/g++-include/VOHSet.ccP @@ -0,0 +1,308 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Doug Schmidt + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".VOHSet.h" + + +/* codes for status fields */ +#define EMPTYCELL 0 +#define VALIDCELL 1 +#define DELETEDCELL 2 + + +VOHSet::VOHSet(int sz) +{ +// The size of the hash table is always the smallest power of 2 >= the size +// indicated by the user. This allows several optimizations, including +// the use of actual double hashing and elimination of the mod instruction. + + size = 1; + while (size < sz) size <<= 1; + tab = new [size]; + status = new char[size]; + for (int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = cnt = 0; +} + +VOHSet::VOHSet(VOHSet& a) +{ + tab = new [size = a.size]; + status = new char[size]; + for (int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = cnt = 0; + for (Pix p = a.first(); p; a.next(p)) add(a(p)); +} + +Pix VOHSet::seek( key) +{ +// Uses ordered double hashing to perform a search of the table. +// This greatly speeds up the average-case time for an unsuccessful search. + + unsigned hashval = HASH(key); + + // We can avoid the mod operation since size is a power of 2. + unsigned h = hashval & (size - 1); + + // The increment must be odd, since all odd numbers are relatively + // prime to a power of 2!! + unsigned inc = ((((hashval / size) << 1) + 1) & (size - 1)); + + // There is always at least 1 empty cell, so this loop is guaranteed to halt! + while (status[h] != EMPTYCELL) + { + int cmp = CMP (key, tab[h]); + if (cmp == 0) + { + if (status[h] == VALIDCELL) + return Pix(&tab[h]); + else + return 0; + } + else if (cmp > 0) + return 0; + else + h = ((h + inc) & (size - 1)); + } + return 0; +} + +// This adds an item if it doesn't already exist. By performing the initial +// comparison we assure that the table always contains at least 1 empty +// spot. This speeds up later searching by a constant factor. +// The insertion algorithm uses ordered double hashing. See Standish's +// 1980 ``Data Structure's Techniques'' book for details. + +Pix VOHSet::add( x) +{ + if (size <= cnt+1) + resize(); + + unsigned hashval = HASH(x); + unsigned h = hashval & (size - 1); + + if (status[h] != VALIDCELL) // save some work if possible + { + if (status[h] == EMPTYCELL) + cnt++; + count++; + tab[h] = x; + status[h] = VALIDCELL; + return Pix(&tab[h]); + } + int cmp = CMP(x, tab[h]); + if (cmp == 0) + return Pix(&tab[h]); + + item = x; + Pix mypix = 0; + unsigned inc = ((((hashval / size) << 1) + 1) & (size - 1)); + + for (;;) + { + if (cmp < 0) + { + temp = tab[h]; + tab[h] = item; + item = temp; + if (mypix == 0) mypix = Pix(&tab[h]); + inc = ((((HASH(item) / size) << 1) + 1) & (size - 1)); + h = ((h + inc) & (size - 1)); + if (status[h] != EMPTYCELL) cmp = CMP(item, tab[h]); + } + else + h = ((h + inc) & (size - 1)); + if (status[h] != VALIDCELL) + { + if (status[h] == EMPTYCELL) + cnt++; + count++; + tab[h] = item; + status[h] = VALIDCELL; + return (mypix == 0)? Pix(&tab[h]) : mypix; + } + } +} + + +void VOHSet::del( key) +{ +// This performs a deletion by marking the item's status field. +// Note that we only decrease the count, *not* the cnt, since this +// would cause trouble for subsequent steps in the algorithm. See +// Reingold and Hanson's ``Data Structure's'' book for a justification +// of this approach. + + unsigned hashval = HASH(key); + unsigned h = hashval & (size - 1); + unsigned inc = ((((hashval / size) << 1) + 1) & (size - 1)); + + while (status[h] != EMPTYCELL) + { + int cmp = CMP(key, tab[h]); + if (cmp > 0) + h = ((h + inc) & (size - 1)); + else if (status[h] == VALIDCELL && cmp == 0) + { + status[h] = DELETEDCELL; + count--; + return; + } + else + return; + } +} + +void VOHSet::clear() +{ + for (int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = cnt = 0; +} + +void VOHSet::resize(int newsize) +{ + if (newsize <= count) + newsize = count; + int s = 1; + while (s <= newsize) s <<= 1; + newsize = s; + + * oldtab = tab; + char* oldstatus = status; + int oldsize = size; + tab = new [size = newsize]; + status = new char[size]; + for (int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = cnt = 0; + + for (i = 0; i < oldsize; ++i) if (oldstatus[i] == VALIDCELL) add(oldtab[i]); + delete [] oldtab; + delete oldstatus; +} + +Pix VOHSet::first() +{ + for (int pos = 0; pos < size; ++pos) + if (status[pos] == VALIDCELL) return Pix(&tab[pos]); + return 0; +} + +void VOHSet::next(Pix& i) +{ + if (i == 0) return; + int pos = ((unsigned)i - (unsigned)tab) / sizeof() + 1; + for (; pos < size; ++pos) + if (status[pos] == VALIDCELL) + { + i = Pix(&tab[pos]); + return; + } + i = 0; +} + + +int VOHSet:: operator == (VOHSet& b) +{ + if (count != b.count) + return 0; + else + { + for (int i = 0; i < size; ++i) + if (status[i] == VALIDCELL && b.seek(tab[i]) == 0) + return 0; + for (i = 0; i < b.size; ++i) + if (b.status[i] == VALIDCELL && seek(b.tab[i]) == 0) + return 0; + return 1; + } +} + +int VOHSet:: operator != (VOHSet& b) +{ + return !(*this == b); +} + +int VOHSet::operator <= (VOHSet& b) +{ + if (count > b.count) + return 0; + else + { + for (int i = 0; i < size; ++i) + if (status[i] == VALIDCELL && b.seek(tab[i]) == 0) + return 0; + return 1; + } +} + +void VOHSet::operator |= (VOHSet& b) +{ + if (&b == this || b.count == 0) + return; + for (int i = 0; i < b.size; ++i) + if (b.status[i] == VALIDCELL) add(b.tab[i]); +} + +void VOHSet::operator &= (VOHSet& b) +{ + if (&b == this || count == 0) + return; + for (int i = 0; i < size; ++i) + { + if (status[i] == VALIDCELL && b.seek(tab[i]) == 0) + { + status[i] = DELETEDCELL; + --count; + } + } +} + +void VOHSet::operator -= (VOHSet& b) +{ + for (int i = 0; i < size; ++i) + { + if (status[i] == VALIDCELL && b.seek(tab[i]) != 0) + { + status[i] = DELETEDCELL; + --count; + } + } +} + +int VOHSet::OK() +{ + int v = tab != 0; + v &= status != 0; + int n = 0; + for (int i = 0; i < size; ++i) + { + if (status[i] == VALIDCELL) ++n; + else if (status[i] != DELETEDCELL && status[i] != EMPTYCELL) + v = 0; + } + v &= n == count; + if (!v) error("invariant failure"); + return v; +} + + + diff --git a/gnu/lib/libg++/g++-include/VOHSet.hP b/gnu/lib/libg++/g++-include/VOHSet.hP new file mode 100644 index 0000000000..94decaad12 --- /dev/null +++ b/gnu/lib/libg++/g++-include/VOHSet.hP @@ -0,0 +1,88 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Doug Schmidt + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _VOHSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _VOHSet_h 1 + +#include ".Set.h" + + + +class VOHSet : public Set +{ + * tab; + char* status; + int size; + int cnt; // keeps track of VALIDCELLs and DELETEDCELLs + +public: + VOHSet(int sz = DEFAULT_INITIAL_CAPACITY); + VOHSet(VOHSet&); + ~VOHSet(); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + Pix seek( item); + + void operator |= (VOHSet& b); + void operator -= (VOHSet& b); + void operator &= (VOHSet& b); + + int operator == (VOHSet& b); + int operator != (VOHSet& b); + int operator <= (VOHSet& b); + + int capacity(); + void resize(int newsize = 0); + + int OK(); +}; + + +inline VOHSet::~VOHSet() +{ + delete [] tab; + delete status; +} + + +inline int VOHSet::contains( key) +{ + return seek(key) != 0; +} + + +inline & VOHSet::operator () (Pix p) +{ + if (p == 0) error("null Pix"); + return *((*)p); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/VQueue.ccP b/gnu/lib/libg++/g++-include/VQueue.ccP new file mode 100644 index 0000000000..1181b3f50d --- /dev/null +++ b/gnu/lib/libg++/g++-include/VQueue.ccP @@ -0,0 +1,83 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".VQueue.h" + +VQueue::VQueue(VQueue& b) +:size(b.size), cnt(b.cnt), inp(b.inp), outp(b.outp), s(new [b.size]) +{ + int j = outp; + for (int i = 0; i < cnt; ++i) + { + s[j] = b.s[j]; + if (++j == size) j = 0; + } +} + +void VQueue::operator = (VQueue& b) +{ + if (&b == this) return; + if (size != b.size) + { + delete [] s; + s = new [b.size]; + size = b.size; + } + inp = b.inp; outp = b.outp; cnt = b.cnt; + int j = outp; + for (int i = 0; i < cnt; ++i) + { + s[j] = b.s[j]; + if (++j == size) j = 0; + } +} + + +void VQueue::resize(int newsz) +{ + if (newsz < cnt) + error("resize: new size too small"); + * news = new [newsz]; + int j = outp; + for (int i = 0; i < cnt; ++i) + { + news[i] = s[j]; + if (++j == size) j = 0; + } + inp = j; + outp = 0; + delete [] s; + s = news; + size = newsz; +} + +int VQueue::OK() +{ + int v = s != 0; // have space + v &= size >= 0; // a legal size + v &= inp >= 0 && inp <= size; // pointers with bounds + v &= outp >= 0 && outp <= size; + int c = (size + inp - outp) % size; + v &= cnt == size || cnt == c; // correct count + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/VQueue.hP b/gnu/lib/libg++/g++-include/VQueue.hP new file mode 100644 index 0000000000..cce2c85d24 --- /dev/null +++ b/gnu/lib/libg++/g++-include/VQueue.hP @@ -0,0 +1,130 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _VQueue_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _VQueue_h 1 + +#include ".Queue.h" + +class VQueue : public Queue +{ +protected: + int size; + int cnt; + int inp; + int outp; + * s; + +public: + + VQueue(int sz = DEFAULT_INITIAL_CAPACITY); + VQueue(VQueue&); + ~VQueue(); + + void operator = (VQueue&); + + void enq( item); + deq(); + & front(); + void del_front(); + + int length(); + int empty(); + int full(); + + int capacity(); + void resize(int sz); + void clear(); + + int OK(); +}; + + +inline VQueue::VQueue(int sz) +{ + s = new [size = sz]; + cnt = inp = outp = 0; +} + +inline VQueue::~VQueue() +{ + delete [] s; +} + +inline void VQueue::clear() +{ + inp = outp = 0; + cnt = 0; +} + +inline int VQueue::empty() +{ + return cnt <= 0; +} + +inline int VQueue::capacity() +{ + return size; +} + +inline int VQueue::full() +{ + return cnt >= size; +} + +inline int VQueue::length() +{ + return cnt; +} + +inline void VQueue::enq( item) +{ + if (cnt >= size) error("enq to full Queue."); + ++cnt; + s[inp] = item; + if (++inp == size) inp = 0; +} + +inline VQueue::deq() +{ + if (cnt <= 0) error("deq from empty Queue."); + --cnt; + int i = outp; + if (++outp == size) outp = 0; + return s[i]; +} + + +inline void VQueue::del_front() +{ + if (cnt <= 0) error("delete from empty Queue."); + --cnt; + if (++outp == size) outp = 0; +} + +inline & VQueue::front() +{ + if (empty()) error("top from empty Queue."); + return s[outp]; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/VStack.ccP b/gnu/lib/libg++/g++-include/VStack.ccP new file mode 100644 index 0000000000..5203d51341 --- /dev/null +++ b/gnu/lib/libg++/g++-include/VStack.ccP @@ -0,0 +1,66 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".VStack.h" + +// error handling + + +VStack::VStack(VStack& b) +:size(b.size), ptr(b.ptr), s(new [b.size]) +{ + for (int i = 0; i < ptr; ++i) s[i] = b.s[i]; +} + +void VStack::operator = (VStack& b) +{ + if (&b == this) return; + if (size < b.ptr) + { + delete [] s; + s = new [b.size]; + size = b.size; + } + ptr = b.ptr; + for (int i = 0; i < ptr; ++i) s[i] = b.s[i]; +} + + +void VStack::resize(int newsz) +{ + if (newsz < ptr) error("resize: new size too small"); + * news = new [newsz]; + for (int i = 0; i < ptr; ++i) news[i] = s[i]; + delete [] s; + s = news; + size = newsz; +} + +int VStack::OK() +{ + int v = s != 0; // have space + v &= size >= 0; // a legal size + v &= ptr <= size; // ptr within bounds + v &= ptr >= 0; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/VStack.hP b/gnu/lib/libg++/g++-include/VStack.hP new file mode 100644 index 0000000000..c8190bf064 --- /dev/null +++ b/gnu/lib/libg++/g++-include/VStack.hP @@ -0,0 +1,120 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _VStack_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _VStack_h 1 + +#include ".Stack.h" + +class VStack : public Stack +{ +protected: + int size; + int ptr; + * s; + +public: + + VStack(int sz = DEFAULT_INITIAL_CAPACITY); + VStack(VStack&); + ~VStack(); + + void operator = (VStack&); + void push( item); + pop(); + & top(); + void del_top(); + + int length(); + int empty(); + int full(); + void clear(); + + void resize(int sz); + int capacity(); + + int OK(); +}; + + +inline VStack::VStack(int sz) +{ + s = new [size = sz]; + ptr = 0; +} + +inline VStack::~VStack() +{ + delete [] s; +} + +inline void VStack::clear() +{ + ptr = 0; +} + +inline int VStack::capacity() +{ + return size; +} + +inline int VStack::empty() +{ + return ptr == 0; +} + +inline int VStack::full() +{ + return ptr == size; +} + +inline int VStack::length() +{ + return ptr; +} + +inline void VStack::push( item) +{ + if (full()) error("push to full stack."); + s[ptr++] = item; +} + +inline VStack::pop() +{ + if (empty()) error("pop from empty stack."); + return s[--ptr]; +} + + +inline void VStack::del_top() +{ + if (empty()) error("del_top from empty stack."); + --ptr; +} + +inline & VStack::top() +{ + if (empty()) error("top from empty stack."); + return s[ptr-1]; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/Vec.ccP b/gnu/lib/libg++/g++-include/Vec.ccP new file mode 100644 index 0000000000..c9cbfb2109 --- /dev/null +++ b/gnu/lib/libg++/g++-include/Vec.ccP @@ -0,0 +1,470 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include +#include ".Vec.h" + +// error handling + + +void default_Vec_error_handler(const char* msg) +{ + cerr << "Fatal Vec error. " << msg << "\n"; + exit(1); +} + +one_arg_error_handler_t Vec_error_handler = default_Vec_error_handler; + +one_arg_error_handler_t set_Vec_error_handler(one_arg_error_handler_t f) +{ + one_arg_error_handler_t old = Vec_error_handler; + Vec_error_handler = f; + return old; +} + +void Vec::error(const char* msg) +{ + (*Vec_error_handler)(msg); +} + +void Vec::range_error() +{ + (*Vec_error_handler)("Index out of range."); +} + +Vec::Vec(Vec& v) +{ + s = new [len = v.len]; + * top = &(s[len]); + * t = s; + * u = v.s; + while (t < top) *t++ = *u++; +} + +Vec::Vec(int l, fill_value) +{ + s = new [len = l]; + * top = &(s[len]); + * t = s; + while (t < top) *t++ = fill_value; +} + + +Vec& Vec::operator = (Vec& v) +{ + if (this != &v) + { + delete [] s; + s = new [len = v.len]; + * top = &(s[len]); + * t = s; + * u = v.s; + while (t < top) *t++ = *u++; + } + return *this; +} + +void Vec::apply(Procedure f) +{ + * top = &(s[len]); + * t = s; + while (t < top) (*f)(*t++); +} + +// can't just realloc since there may be need for constructors/destructors +void Vec::resize(int newl) +{ + * news = new [newl]; + * p = news; + int minl = (len < newl)? len : newl; + * top = &(s[minl]); + * t = s; + while (t < top) *p++ = *t++; + delete [] s; + s = news; + len = newl; +} + +Vec concat(Vec & a, Vec & b) +{ + int newl = a.len + b.len; + * news = new [newl]; + * p = news; + * top = &(a.s[a.len]); + * t = a.s; + while (t < top) *p++ = *t++; + top = &(b.s[b.len]); + t = b.s; + while (t < top) *p++ = *t++; + return Vec(newl, news); +} + + +Vec combine(Combiner f, Vec& a, Vec& b) +{ + int newl = (a.len < b.len)? a.len : b.len; + * news = new [newl]; + * p = news; + * top = &(a.s[newl]); + * t = a.s; + * u = b.s; + while (t < top) *p++ = (*f)(*t++, *u++); + return Vec(newl, news); +} + + Vec::reduce(Combiner f, base) +{ + r = base; + * top = &(s[len]); + * t = s; + while (t < top) r = (*f)(r, *t++); + return r; +} + +Vec reverse(Vec& a) +{ + * news = new [a.len]; + if (a.len != 0) + { + * lo = news; + * hi = &(news[a.len - 1]); + while (lo < hi) + { + tmp = *lo; + *lo++ = *hi; + *hi-- = tmp; + } + } + return Vec(a.len, news); +} + +void Vec::reverse() +{ + if (len != 0) + { + * lo = s; + * hi = &(s[len - 1]); + while (lo < hi) + { + tmp = *lo; + *lo++ = *hi; + *hi-- = tmp; + } + } +} + +int Vec::index( targ) +{ + for (int i = 0; i < len; ++i) if (EQ(targ, s[i])) return i; + return -1; +} + +Vec map(Mapper f, Vec& a) +{ + * news = new [a.len]; + * p = news; + * top = &(a.s[a.len]); + * t = a.s; + while(t < top) *p++ = (*f)(*t++); + return Vec(a.len, news); +} + +int operator == (Vec& a, Vec& b) +{ + if (a.len != b.len) + return 0; + * top = &(a.s[a.len]); + * t = a.s; + * u = b.s; + while (t < top) if (!(EQ(*t++, *u++))) return 0; + return 1; +} + +void Vec::fill( val, int from, int n) +{ + int to; + if (n < 0) + to = len - 1; + else + to = from + n - 1; + if ((unsigned)from > (unsigned)to) + range_error(); + * t = &(s[from]); + * top = &(s[to]); + while (t <= top) *t++ = val; +} + +Vec Vec::at(int from, int n) +{ + int to; + if (n < 0) + { + n = len - from; + to = len - 1; + } + else + to = from + n - 1; + if ((unsigned)from > (unsigned)to) + range_error(); + * news = new [n]; + * p = news; + * t = &(s[from]); + * top = &(s[to]); + while (t <= top) *p++ = *t++; + return Vec(n, news); +} + +Vec merge(Vec & a, Vec & b, Comparator f) +{ + int newl = a.len + b.len; + * news = new [newl]; + * p = news; + * topa = &(a.s[a.len]); + * as = a.s; + * topb = &(b.s[b.len]); + * bs = b.s; + + for (;;) + { + if (as >= topa) + { + while (bs < topb) *p++ = *bs++; + break; + } + else if (bs >= topb) + { + while (as < topa) *p++ = *as++; + break; + } + else if ((*f)(*as, *bs) <= 0) + *p++ = *as++; + else + *p++ = *bs++; + } + return Vec(newl, news); +} + +static int gsort(*, int, Comparator); + +void Vec::sort (Comparator compar) +{ + gsort(s, len, compar); +} + + +// An adaptation of Schmidt's new quicksort + +static inline void SWAP(* A, * B) +{ + tmp = *A; *A = *B; *B = tmp; +} + +/* This should be replaced by a standard ANSI macro. */ +#define BYTES_PER_WORD 8 +#define BYTES_PER_LONG 4 + +/* The next 4 #defines implement a very fast in-line stack abstraction. */ + +#define STACK_SIZE (BYTES_PER_WORD * BYTES_PER_LONG) +#define PUSH(LOW,HIGH) do {top->lo = LOW;top++->hi = HIGH;} while (0) +#define POP(LOW,HIGH) do {LOW = (--top)->lo;HIGH = top->hi;} while (0) +#define STACK_NOT_EMPTY (stack < top) + +/* Discontinue quicksort algorithm when partition gets below this size. + This particular magic number was chosen to work best on a Sun 4/260. */ +#define MAX_THRESH 4 + + +/* Order size using quicksort. This implementation incorporates + four optimizations discussed in Sedgewick: + + 1. Non-recursive, using an explicit stack of pointer that + store the next array partition to sort. To save time, this + maximum amount of space required to store an array of + MAX_INT is allocated on the stack. Assuming a 32-bit integer, + this needs only 32 * sizeof (stack_node) == 136 bits. Pretty + cheap, actually. + + 2. Chose the pivot element using a median-of-three decision tree. + This reduces the probability of selecting a bad pivot value and + eliminates certain extraneous comparisons. + + 3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving + insertion sort to order the MAX_THRESH items within each partition. + This is a big win, since insertion sort is faster for small, mostly + sorted array segements. + + 4. The larger of the two sub-partitions is always pushed onto the + stack first, with the algorithm then concentrating on the + smaller partition. This *guarantees* no more than log (n) + stack size is needed! */ + +static int gsort ( *base_ptr, int total_elems, Comparator cmp) +{ +/* Stack node declarations used to store unfulfilled partition obligations. */ + struct stack_node { *lo; *hi; }; + pivot_buffer; + int max_thresh = MAX_THRESH; + + if (total_elems > MAX_THRESH) + { + *lo = base_ptr; + *hi = lo + (total_elems - 1); + *left_ptr; + *right_ptr; + stack_node stack[STACK_SIZE]; /* Largest size needed for 32-bit int!!! */ + stack_node *top = stack + 1; + + while (STACK_NOT_EMPTY) + { + { + *pivot = &pivot_buffer; + { + /* Select median value from among LO, MID, and HI. Rearrange + LO and HI so the three values are sorted. This lowers the + probability of picking a pathological pivot value and + skips a comparison for both the LEFT_PTR and RIGHT_PTR. */ + + *mid = lo + ((hi - lo) >> 1); + + if ((*cmp) (*mid, *lo) < 0) + SWAP (mid, lo); + if ((*cmp) (*hi, *mid) < 0) + { + SWAP (mid, hi); + if ((*cmp) (*mid, *lo) < 0) + SWAP (mid, lo); + } + *pivot = *mid; + pivot = &pivot_buffer; + } + left_ptr = lo + 1; + right_ptr = hi - 1; + + /* Here's the famous ``collapse the walls'' section of quicksort. + Gotta like those tight inner loops! They are the main reason + that this algorithm runs much faster than others. */ + do + { + while ((*cmp) (*left_ptr, *pivot) < 0) + left_ptr += 1; + + while ((*cmp) (*pivot, *right_ptr) < 0) + right_ptr -= 1; + + if (left_ptr < right_ptr) + { + SWAP (left_ptr, right_ptr); + left_ptr += 1; + right_ptr -= 1; + } + else if (left_ptr == right_ptr) + { + left_ptr += 1; + right_ptr -= 1; + break; + } + } + while (left_ptr <= right_ptr); + + } + + /* Set up pointers for next iteration. First determine whether + left and right partitions are below the threshold size. If so, + ignore one or both. Otherwise, push the larger partition's + bounds on the stack and continue sorting the smaller one. */ + + if ((right_ptr - lo) <= max_thresh) + { + if ((hi - left_ptr) <= max_thresh) /* Ignore both small partitions. */ + POP (lo, hi); + else /* Ignore small left partition. */ + lo = left_ptr; + } + else if ((hi - left_ptr) <= max_thresh) /* Ignore small right partition. */ + hi = right_ptr; + else if ((right_ptr - lo) > (hi - left_ptr)) /* Push larger left partition indices. */ + { + PUSH (lo, right_ptr); + lo = left_ptr; + } + else /* Push larger right partition indices. */ + { + PUSH (left_ptr, hi); + hi = right_ptr; + } + } + } + + /* Once the BASE_PTR array is partially sorted by quicksort the rest + is completely sorted using insertion sort, since this is efficient + for partitions below MAX_THRESH size. BASE_PTR points to the beginning + of the array to sort, and END_PTR points at the very last element in + the array (*not* one beyond it!). */ + + + { + *end_ptr = base_ptr + 1 * (total_elems - 1); + *run_ptr; + *tmp_ptr = base_ptr; + *thresh = (end_ptr < (base_ptr + max_thresh))? + end_ptr : (base_ptr + max_thresh); + + /* Find smallest element in first threshold and place it at the + array's beginning. This is the smallest array element, + and the operation speeds up insertion sort's inner loop. */ + + for (run_ptr = tmp_ptr + 1; run_ptr <= thresh; run_ptr += 1) + if ((*cmp) (*run_ptr, *tmp_ptr) < 0) + tmp_ptr = run_ptr; + + if (tmp_ptr != base_ptr) + SWAP (tmp_ptr, base_ptr); + + /* Insertion sort, running from left-hand-side up to `right-hand-side.' + Pretty much straight out of the original GNU qsort routine. */ + + for (run_ptr = base_ptr + 1; (tmp_ptr = run_ptr += 1) <= end_ptr; ) + { + + while ((*cmp) (*run_ptr, *(tmp_ptr -= 1)) < 0) + ; + + if ((tmp_ptr += 1) != run_ptr) + { + *trav; + + for (trav = run_ptr + 1; --trav >= run_ptr;) + { + c = *trav; + *hi, *lo; + + for (hi = lo = trav; (lo -= 1) >= tmp_ptr; hi = lo) + *hi = *lo; + *hi = c; + } + } + + } + } + return 1; +} diff --git a/gnu/lib/libg++/g++-include/Vec.hP b/gnu/lib/libg++/g++-include/Vec.hP new file mode 100644 index 0000000000..97ff3f5fef --- /dev/null +++ b/gnu/lib/libg++/g++-include/Vec.hP @@ -0,0 +1,135 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _Vec_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Vec_h 1 + +#include +#include ".defs.h" + +#ifndef __typedefs +#define __typedefs 1 +typedef void (*Procedure)(); +typedef (*Mapper)(); +typedef (*Combiner)(, ); +typedef int (*Predicate)(); +typedef int (*Comparator)(, ); +#endif + + +class Vec +{ +protected: + int len; + *s; + + Vec(int l, * d); +public: + Vec (); + Vec (int l); + Vec (int l, fill_value); + Vec (Vec&); + ~Vec (); + + Vec & operator = (Vec & a); + Vec at(int from = 0, int n = -1); + + int capacity(); + void resize(int newlen); + + & operator [] (int n); + & elem(int n); + + friend Vec concat(Vec & a, Vec & b); + friend Vec map(Mapper f, Vec & a); + friend Vec merge(Vec & a, Vec & b, Comparator f); + friend Vec combine(Combiner f, Vec & a, Vec & b); + friend Vec reverse(Vec & a); + + void reverse(); + void sort(Comparator f); + void fill( val, int from = 0, int n = -1); + + void apply(Procedure f); + reduce(Combiner f, base); + int index( targ); + + friend int operator == (Vec& a, Vec& b); + friend int operator != (Vec& a, Vec& b); + + void error(const char* msg); + void range_error(); +}; + +extern void default_Vec_error_handler(const char*); +extern one_arg_error_handler_t Vec_error_handler; + +extern one_arg_error_handler_t + set_Vec_error_handler(one_arg_error_handler_t f); + + +inline Vec::Vec() +{ + len = 0; s = 0; +} + +inline Vec::Vec(int l) +{ + s = new [len = l]; +} + + +inline Vec::Vec(int l, * d) :len(l), s(d) {} + + +inline Vec::~Vec() +{ + delete [] s; +} + + +inline & Vec::operator [] (int n) +{ + if ((unsigned)n >= (unsigned)len) + range_error(); + return s[n]; +} + +inline & Vec::elem(int n) +{ + return s[n]; +} + + +inline int Vec::capacity() +{ + return len; +} + + + +inline int operator != (Vec& a, Vec& b) +{ + return !(a == b); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/Weibull.h b/gnu/lib/libg++/g++-include/Weibull.h deleted file mode 100644 index 6ce8eabf8b..0000000000 --- a/gnu/lib/libg++/g++-include/Weibull.h +++ /dev/null @@ -1,83 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ -#ifndef _Weibull_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _Weibull_h - -#include - -class Weibull: public Random { -protected: - double pAlpha; - double pInvAlpha; - double pBeta; - - void setState(); - -public: - Weibull(double alpha, double beta, RNG *gen); - - double alpha(); - double alpha(double x); - - double beta(); - double beta(double x); - - virtual double operator()(); -}; - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline void Weibull::setState() { - pInvAlpha = 1.0 / pAlpha; -} - -inline Weibull::Weibull(double alpha, double beta, RNG *gen) : (gen) -{ - pAlpha = alpha; - pBeta = beta; - setState(); -} - -inline double Weibull::alpha() { return pAlpha; } - -inline double Weibull::alpha(double x) { - double tmp = pAlpha; - pAlpha = x; - setState(); - return tmp; -} - -inline double Weibull::beta() { return pBeta; }; -inline double Weibull::beta(double x) { - double tmp = pBeta; - pBeta = x; - return tmp; -}; - - -#endif -#endif diff --git a/gnu/lib/libg++/g++-include/XPBag.ccP b/gnu/lib/libg++/g++-include/XPBag.ccP new file mode 100644 index 0000000000..76dc35cf39 --- /dev/null +++ b/gnu/lib/libg++/g++-include/XPBag.ccP @@ -0,0 +1,72 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".XPBag.h" + +int XPBag::OK() +{ + int v = p.OK(); + v &= count == p.length(); + if (!v) error("invariant failure"); + return v; +} + +Pix XPBag::seek( item, Pix i) +{ + if (i == 0) i = p.first(); else next(i); + for (; i != 0; p.next(i)) if (EQ(p(i), item)) return i; + return 0; +} + +int XPBag::nof( item) +{ + int n = 0; + for (int i = p.low(); i < p.fence(); p.next(i)) if (EQ(p[i], item)) ++n; + return n; +} + +void XPBag::del( item) +{ + for (int i = p.low(); i < p.fence(); p.next(i)) + { + if (EQ(p[i], item)) + { + --count; + p[i] = p.low_element(); + p.del_low(); + return; + } + } +} + +void XPBag::remove( item) +{ + for (int i = p.low(); i < p.fence(); p.next(i)) + { + if (EQ(p[i], item)) + { + --count; + p[i] = p.low_element(); + p.del_low(); + } + } +} + diff --git a/gnu/lib/libg++/g++-include/XPBag.hP b/gnu/lib/libg++/g++-include/XPBag.hP new file mode 100644 index 0000000000..296a59082d --- /dev/null +++ b/gnu/lib/libg++/g++-include/XPBag.hP @@ -0,0 +1,98 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _XPBag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _XPBag_h 1 + +#include ".Bag.h" +#include ".XPlex.h" + +class XPBag : public Bag +{ +protected: + XPlex p; + +public: + XPBag(int chunksize = DEFAULT_INITIAL_CAPACITY); + XPBag(const XPBag&); + + Pix add( item); + void del( item); +#undef remove + void remove(item); + int nof( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item, Pix from = 0); + + int OK(); +}; + + +inline XPBag::XPBag(int chunksize) + : p(chunksize) { count = 0; } + +inline XPBag::XPBag(const XPBag& s) : p(s.p) { count = s.count; } + +inline Pix XPBag::first() +{ + return p.first(); +} + +inline void XPBag::next(Pix & idx) +{ + p.next(idx); +} + +inline & XPBag::operator ()(Pix idx) +{ + return p(idx); +} + +inline void XPBag::clear() +{ + count = 0; p.clear(); +} + +inline int XPBag::owns (Pix idx) +{ + return p.owns(idx); +} + +inline Pix XPBag::add( item) +{ + ++count; + return p.index_to_Pix(p.add_high(item)); +} + +inline int XPBag::contains( item) +{ + return seek(item) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/XPDeque.ccP b/gnu/lib/libg++/g++-include/XPDeque.ccP new file mode 100644 index 0000000000..6b363d9bdc --- /dev/null +++ b/gnu/lib/libg++/g++-include/XPDeque.ccP @@ -0,0 +1,4 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".XPDeque.h" diff --git a/gnu/lib/libg++/g++-include/XPDeque.hP b/gnu/lib/libg++/g++-include/XPDeque.hP new file mode 100644 index 0000000000..b8e7c8268f --- /dev/null +++ b/gnu/lib/libg++/g++-include/XPDeque.hP @@ -0,0 +1,133 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _XPDeque_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _XPDeque_h + +#include ".XPlex.h" +#include ".Deque.h" + +class XPDeque : public Deque +{ + XPlex p; + +public: + XPDeque(int chunksize = DEFAULT_INITIAL_CAPACITY); + XPDeque(const XPDeque& d); + ~XPDeque(); + + void operator = (const XPDeque&); + + void push( item); // insert at front + void enq( item); // insert at rear + + & front(); + & rear(); + + deq(); + void del_front(); + void del_rear(); + + void clear(); + int empty(); + int full(); + int length(); + + int OK(); +}; + +inline XPDeque::XPDeque(int chunksize) + : p(chunksize) {} +inline XPDeque::XPDeque(const XPDeque& d) : p(d.p) {} + +inline XPDeque::~XPDeque() {} + +inline void XPDeque::push(item) +{ + p.add_low(item); +} + +inline void XPDeque::enq(item) +{ + p.add_high(item); +} + +inline XPDeque::deq() +{ + res = p.low_element(); + p.del_low(); + return res; +} + +inline & XPDeque::front() +{ + return p.low_element(); +} + +inline & XPDeque::rear() +{ + return p.high_element(); +} + +inline void XPDeque::del_front() +{ + p.del_low(); +} + +inline void XPDeque::del_rear() +{ + p.del_high(); +} + +inline void XPDeque::operator =(const XPDeque& s) +{ + p.operator = (s.p); +} + + +inline int XPDeque::empty() +{ + return p.empty(); +} + +inline int XPDeque::full() +{ + return p.full(); +} + +inline int XPDeque::length() +{ + return p.length(); +} + +inline int XPDeque::OK() +{ + return p.OK(); +} + +inline void XPDeque::clear() +{ + p.clear(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/XPPQ.ccP b/gnu/lib/libg++/g++-include/XPPQ.ccP new file mode 100644 index 0000000000..41515a39bb --- /dev/null +++ b/gnu/lib/libg++/g++-include/XPPQ.ccP @@ -0,0 +1,143 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".XPPQ.h" + +int XPPQ::OK() +{ + int v = p.OK(); + v &= p.low() == 1; + v &= count == p.length(); + if (!v) error("invariant failure"); + return v; +} + +Pix XPPQ::seek( item) +{ + for (int i = p.low(); i < p.fence(); p.next(i)) + if (EQ(p[i],item)) return p.index_to_Pix(i); + return 0; +} + +// standard 2-ary heap ops +// pointers are used a lot to avoid thrashing across chunks with plexes + +Pix XPPQ::enq( item) +{ + p.add_high(item); + * pk = &(p.high_element()); + int par = ++count >> 1; + while (par != 0) + { + * ppar = &(p[par]); + if (!(LE(*ppar, item))) + { + *pk = *ppar; + pk = ppar; + par >>= 1; + } + else + break; + } + *pk = item; + return Pix(pk); +} + +void XPPQ::del_front() +{ + if (count == 0) error("empty PQ"); + --count; + * pk = &(p.low_element()); + * ph = &(p.high_element()); + int child = 2; + while (child <= count) + { + * pchild = &(p[child]); + if (child < count) + { + * prchild = &(p[child+1]); + if (!(LE(*pchild, *prchild))) + { + pchild = prchild; + ++child; + } + } + if (!(LE(*ph, *pchild))) + { + *pk = *pchild; + pk = pchild; + child <<= 1; + } + else + break; + } + *pk = *ph; + p.del_high(); +} + + +void XPPQ::del(Pix i) +{ + if (i == 0) error("null Pix"); + --count; + int k = p.Pix_to_index(i); + * pk = &(p[k]); + * ph = &(p.high_element()); + int child = k << 1; + while (child <= count) + { + * pchild = &(p[child]); + if (child < count) + { + * prchild = &(p[child+1]); + if (!(LE(*pchild, *prchild))) + { + pchild = prchild; + ++child; + } + } + if (!(LE(*ph, *pchild))) + { + *pk = *pchild; + pk = pchild; + child <<= 1; + } + else + break; + } + int par = child >> 2; + while (par != 0) + { + * ppar = &(p[par]); + if (!(LE(*ppar, *ph))) + { + *pk = *ppar; + pk = ppar; + par >>= 1; + } + else + break; + } + *pk = *ph; + p.del_high(); +} + + diff --git a/gnu/lib/libg++/g++-include/XPPQ.hP b/gnu/lib/libg++/g++-include/XPPQ.hP new file mode 100644 index 0000000000..af813a2b90 --- /dev/null +++ b/gnu/lib/libg++/g++-include/XPPQ.hP @@ -0,0 +1,105 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _XPPQ_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _XPPQ_h 1 + +#include ".PQ.h" +#include ".XPlex.h" + +class XPPQ : public PQ +{ +protected: + XPlex p; + +public: + XPPQ(int chunksize = DEFAULT_INITIAL_CAPACITY); + XPPQ(const XPPQ&); + + Pix enq( item); + deq(); + + & front(); + void del_front(); + + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + void del(Pix i); + int owns(Pix i); + Pix seek( item); + + int OK(); // rep invariant +}; + +inline XPPQ::XPPQ(int chunksize) + : p(1, chunksize) { count = 0; } + +inline XPPQ::XPPQ(const XPPQ& s) : p(s.p) { count = s.count; } + +inline Pix XPPQ::first() +{ + return p.first(); +} + +inline void XPPQ::next(Pix & idx) +{ + p.next(idx); +} + +inline & XPPQ::operator ()(Pix idx) +{ + return p(idx); +} + +inline & XPPQ::front () +{ + return p.low_element(); +} + +inline XPPQ::deq () +{ + x = p.low_element(); + del_front(); + return x; +} + +inline void XPPQ::clear() +{ + count = 0; p.clear(); +} + +inline int XPPQ::contains ( item) +{ + return seek(item) != 0; +} + +inline int XPPQ::owns (Pix idx) +{ + return p.owns(idx); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/XPQueue.ccP b/gnu/lib/libg++/g++-include/XPQueue.ccP new file mode 100644 index 0000000000..77bfd1c7a9 --- /dev/null +++ b/gnu/lib/libg++/g++-include/XPQueue.ccP @@ -0,0 +1,4 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".XPQueue.h" diff --git a/gnu/lib/libg++/g++-include/XPQueue.hP b/gnu/lib/libg++/g++-include/XPQueue.hP new file mode 100644 index 0000000000..ebae20f3f6 --- /dev/null +++ b/gnu/lib/libg++/g++-include/XPQueue.hP @@ -0,0 +1,114 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _XPQueue_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _XPQueue_h + +#include ".XPlex.h" +#include ".Queue.h" + +class XPQueue : public Queue +{ +protected: + XPlex p; + +public: + XPQueue(int chunksize = DEFAULT_INITIAL_CAPACITY); + XPQueue(const XPQueue& q); + ~XPQueue(); + + void operator = (const XPQueue&); + + void enq( item); + deq(); + & front(); + void del_front(); + + void clear(); + int empty(); + int full(); + int length(); + + int OK(); +}; + +inline XPQueue::XPQueue(int chunksize) + : p(chunksize) {} +inline XPQueue::XPQueue(const XPQueue& q) : p(q.p) {} + +inline XPQueue::~XPQueue() {} + +inline void XPQueue::enq(item) +{ + p.add_high(item); +} + +inline XPQueue::deq() +{ + res = p.low_element(); + p.del_low(); + return res; +} + +inline & XPQueue::front() +{ + return p.low_element(); +} + + +inline void XPQueue::del_front() +{ + p.del_low(); +} + +inline void XPQueue::operator =(const XPQueue& s) +{ + p.operator = (s.p); +} + +inline int XPQueue::empty() +{ + return p.empty(); +} + +inline int XPQueue::full() +{ + return p.full(); +} + +inline int XPQueue::length() +{ + return p.length(); +} + +inline int XPQueue::OK() +{ + return p.OK(); +} + +inline void XPQueue::clear() +{ + p.clear(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/XPSet.ccP b/gnu/lib/libg++/g++-include/XPSet.ccP new file mode 100644 index 0000000000..1102790700 --- /dev/null +++ b/gnu/lib/libg++/g++-include/XPSet.ccP @@ -0,0 +1,63 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".XPSet.h" + +int XPSet::OK() +{ + int v = p.OK(); + v &= count == p.length(); + if (!v) error("invariant failure"); + return v; +} + +Pix XPSet::seek( item) +{ + for (int i = p.low(); i < p.fence(); p.next(i)) + if (EQ(p[i],item)) return p.index_to_Pix(i); + return 0; +} + +Pix XPSet::add( item) +{ + Pix i = seek(item); + if (i == 0) + { + ++count; + i = p.index_to_Pix(p.add_high(item)); + } + return i; +} + +void XPSet::del( item) +{ + for (int i = p.low(); i < p.fence(); p.next(i)) + { + if (EQ(p[i], item)) + { + --count; + p[i] = p.low_element(); + p.del_low(); + return; + } + } +} + diff --git a/gnu/lib/libg++/g++-include/XPSet.hP b/gnu/lib/libg++/g++-include/XPSet.hP new file mode 100644 index 0000000000..9269ec12c8 --- /dev/null +++ b/gnu/lib/libg++/g++-include/XPSet.hP @@ -0,0 +1,89 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _XPSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _XPSet_h 1 + +#include ".Set.h" +#include ".XPlex.h" + +class XPSet : public Set +{ +protected: + XPlex p; + +public: + XPSet(int chunksize = DEFAULT_INITIAL_CAPACITY); + XPSet(const XPSet&); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item); + + int OK(); +}; + + +inline XPSet::XPSet(int chunksize) + : p(chunksize) { count = 0; } + +inline XPSet::XPSet(const XPSet& s) : p(s.p) { count = s.count; } + +inline Pix XPSet::first() +{ + return p.first(); +} + +inline void XPSet::next(Pix & idx) +{ + p.next(idx); +} + +inline & XPSet::operator ()(Pix idx) +{ + return p(idx); +} + +inline void XPSet::clear() +{ + count = 0; p.clear(); +} + +inline int XPSet::contains ( item) +{ + return seek(item) != 0; +} + +inline int XPSet::owns (Pix idx) +{ + return p.owns(idx); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/XPStack.ccP b/gnu/lib/libg++/g++-include/XPStack.ccP new file mode 100644 index 0000000000..fe24f0f044 --- /dev/null +++ b/gnu/lib/libg++/g++-include/XPStack.ccP @@ -0,0 +1,4 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".XPStack.h" diff --git a/gnu/lib/libg++/g++-include/XPStack.hP b/gnu/lib/libg++/g++-include/XPStack.hP new file mode 100644 index 0000000000..9d103e530d --- /dev/null +++ b/gnu/lib/libg++/g++-include/XPStack.hP @@ -0,0 +1,115 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _XPStack_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _XPStack_h + +#include ".XPlex.h" +#include ".Stack.h" + +class XPStack : public Stack +{ + XPlex p; + +public: + XPStack(int chunksize = DEFAULT_INITIAL_CAPACITY); + XPStack(const XPStack& s); + ~XPStack(); + + void operator = (const XPStack&); + + void push( item); + pop(); + & top(); + void del_top(); + + int empty(); + int full(); + int length(); + + void clear(); + + int OK(); + +}; + + +inline XPStack::XPStack(int chunksize) + : p(chunksize) {} +inline XPStack::XPStack(const XPStack& s) : p(s.p) {} + +inline XPStack::~XPStack() {} + +inline void XPStack::push(item) +{ + p.add_high(item); +} + +inline XPStack::pop() +{ + res = p.high_element(); + p.del_high(); + return res; +} + +inline & XPStack::top() +{ + return p.high_element(); +} + +inline void XPStack::del_top() +{ + p.del_high(); +} + +inline void XPStack::operator =(const XPStack& s) +{ + p.operator = (s.p); +} + +inline int XPStack::empty() +{ + return p.empty(); +} + +inline int XPStack::full() +{ + return p.full(); +} + +inline int XPStack::length() +{ + return p.length(); +} + +inline int XPStack::OK() +{ + return p.OK(); +} + +inline void XPStack::clear() +{ + p.clear(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/XPlex.ccP b/gnu/lib/libg++/g++-include/XPlex.ccP new file mode 100644 index 0000000000..c91e5035a4 --- /dev/null +++ b/gnu/lib/libg++/g++-include/XPlex.ccP @@ -0,0 +1,397 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".XPlex.h" + + +XPlex:: XPlex() +{ + lo = fnc = 0; + csize = DEFAULT_INITIAL_CAPACITY; + * data = new [csize]; + set_cache(new IChunk(data, lo, lo, fnc, lo+csize)); + hd = ch; +} + +XPlex:: XPlex(int chunksize) +{ + if (chunksize == 0) error("invalid constructor specification"); + lo = fnc = 0; + if (chunksize > 0) + { + csize = chunksize; + * data = new [csize]; + set_cache(new IChunk(data, lo, lo, fnc, csize)); + hd = ch; + } + else + { + csize = -chunksize; + * data = new [csize]; + set_cache(new IChunk(data, chunksize, lo, fnc, fnc)); + hd = ch; + } +} + + +XPlex:: XPlex(int l, int chunksize) +{ + if (chunksize == 0) error("invalid constructor specification"); + lo = fnc = l; + if (chunksize > 0) + { + csize = chunksize; + * data = new [csize]; + set_cache(new IChunk(data, lo, lo, fnc, csize+lo)); + hd = ch; + } + else + { + csize = -chunksize; + * data = new [csize]; + set_cache(new IChunk(data, chunksize+lo, lo, fnc, fnc)); + hd = ch; + } +} + +void XPlex::make_initial_chunks(int up) +{ + int need = fnc - lo; + hd = 0; + if (up) + { + int l = lo; + do + { + int sz; + if (need >= csize) + sz = csize; + else + sz = need; + * data = new [csize]; + IChunk* h = new IChunk(data, l, l, l+sz, l+csize); + if (hd != 0) + h->link_to_next(hd); + else + hd = h; + l += sz; + need -= sz; + } while (need > 0); + } + else + { + int hi = fnc; + do + { + int sz; + if (need >= csize) + sz = csize; + else + sz = need; + * data = new [csize]; + IChunk* h = new IChunk(data, hi-csize, hi-sz, hi, hi); + if (hd != 0) + h->link_to_next(hd); + hd = h; + hi -= sz; + need -= sz; + } while (need > 0); + } + set_cache(hd); +} + +XPlex:: XPlex(int l, int hi, const initval, int chunksize) +{ + lo = l; + fnc = hi + 1; + if (chunksize == 0) + { + csize = fnc - l; + make_initial_chunks(1); + } + else if (chunksize < 0) + { + csize = -chunksize; + make_initial_chunks(0); + } + else + { + csize = chunksize; + make_initial_chunks(1); + } + fill(initval); +} + +XPlex::XPlex(const XPlex& a) +{ + lo = a.lo; + fnc = a.fnc; + csize = a.csize; + make_initial_chunks(); + for (int i = a.low(); i < a.fence(); a.next(i)) (*this)[i] = a[i]; +} + +void XPlex::operator= (const XPlex& a) +{ + if (&a != this) + { + invalidate(); + lo = a.lo; + fnc = a.fnc; + csize = a.csize; + make_initial_chunks(); + for (int i = a.low(); i < a.fence(); a.next(i)) (*this)[i] = a[i]; + } +} + + +void XPlex::cache(int idx) const +{ + const IChunk* tail = tl(); + const IChunk* t = ch; + while (idx >= t->fence_index()) + { + if (t == tail) index_error(); + t = (t->next()); + } + while (idx < t->low_index()) + { + if (t == hd) index_error(); + t = (t->prev()); + } + set_cache(t); +} + + +void XPlex::cache(const * p) const +{ + const IChunk* old = ch; + const IChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = (t->next()); + if (t == old) index_error(); + } + set_cache(t); +} + +int XPlex::owns(Pix px) const +{ + * p = (*)px; + const IChunk* old = ch; + const IChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = (t->next()); + if (t == old) { set_cache(t); return 0; } + } + set_cache(t); + return 1; +} + + +* XPlex::dosucc(const * p) const +{ + if (p == 0) return 0; + const IChunk* old = ch; + const IChunk* t = ch; + + while (!t->actual_pointer(p)) + { + t = (t->next()); + if (t == old) return 0; + } + int i = t->index_of(p) + 1; + if (i >= fnc) return 0; + if (i >= t->fence_index()) t = (t->next()); + set_cache(t); + return (t->pointer_to(i)); +} + +* XPlex::dopred(const * p) const +{ + if (p == 0) return 0; + const IChunk* old = ch; + const IChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = (t->prev()); + if (t == old) return 0; + } + int i = t->index_of(p) - 1; + if (i < lo) return 0; + if (i < t->low_index()) t = (t->prev()); + set_cache(t); + return (t->pointer_to(i)); +} + + +int XPlex::add_high(const elem) +{ + IChunk* t = tl(); + if (!t->can_grow_high()) + { + if (t->IChunk::empty() && one_chunk()) + t->clear(fnc); + else + { + * data = new [csize]; + t = (new IChunk(data, fnc, fnc, fnc,fnc+csize)); + t->link_to_prev(tl()); + } + } + *((t->IChunk::grow_high())) = elem; + set_cache(t); + return fnc++; +} + +int XPlex::del_high () +{ + if (empty()) empty_error(); + IChunk* t = tl(); + t->IChunk::shrink_high(); + if (t->IChunk::empty() && !one_chunk()) + { + IChunk* pred = t->prev(); + del_chunk(t); + t = pred; + } + set_cache(t); + return --fnc - 1; +} + +int XPlex::add_low (const elem) +{ + IChunk* t = hd; + if (!t->can_grow_low()) + { + if (t->IChunk::empty() && one_chunk()) + t->cleardown(lo); + else + { + * data = new [csize]; + hd = new IChunk(data, lo-csize, lo, lo, lo); + hd->link_to_next(t); + t = hd; + } + } + *((t->IChunk::grow_low())) = elem; + set_cache(t); + return --lo; +} + + +int XPlex::del_low () +{ + if (empty()) empty_error(); + IChunk* t = hd; + t->IChunk::shrink_low(); + if (t->IChunk::empty() && !one_chunk()) + { + hd = t->next(); + del_chunk(t); + t = hd; + } + set_cache(t); + return ++lo; +} + +void XPlex::reverse() +{ + tmp; + int l = lo; + int h = fnc - 1; + IChunk* loch = hd; + IChunk* hich = tl(); + while (l < h) + { + * lptr = loch->pointer_to(l); + * hptr = hich->pointer_to(h); + tmp = *lptr; + *lptr = *hptr; + *hptr = tmp; + if (++l >= loch->fence_index()) loch = loch->next(); + if (--h < hich->low_index()) hich = hich->prev(); + } +} + +void XPlex::fill(const x) +{ + for (int i = lo; i < fnc; ++i) (*this)[i] = x; +} + +void XPlex::fill(const x, int l, int hi) +{ + for (int i = l; i <= hi; ++i) (*this)[i] = x; +} + + +void XPlex::clear() +{ + if (fnc != lo) + { + IChunk* t = tl(); + while (t != hd) + { + IChunk* prv = t->prev(); + del_chunk(t); + t = prv; + } + t->IChunk::clear(lo); + set_cache(t); + fnc = lo; + } +} + + +int XPlex::OK () const +{ + int v = hd != 0 && ch != 0; // at least one chunk + + v &= fnc == tl()->fence_index();// last chunk fence == plex fence + v &= lo == ((hd))->IChunk::low_index(); // first lo == plex lo + +// loop for others: + int found_ch = 0; // to make sure ch is in list; + const IChunk* t = (hd); + for (;;) + { + if (t == ch) ++found_ch; + v &= t->IChunk::OK(); // each chunk is OK + if (t == tl()) + break; + else // and has indices contiguous to succ + { + v &= t->top_index() == t->next()->base_index(); + if (t != hd) // internal chunks full + { + v &= !t->empty(); + v &= !t->can_grow_low(); + v &= !t->can_grow_high(); + } + t = t->next(); + } + } + v &= found_ch == 1; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/XPlex.hP b/gnu/lib/libg++/g++-include/XPlex.hP new file mode 100644 index 0000000000..41f100091b --- /dev/null +++ b/gnu/lib/libg++/g++-include/XPlex.hP @@ -0,0 +1,238 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _XPlex_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _XPlex_h 1 + +#include ".Plex.h" + +class XPlex: public Plex +{ + IChunk* ch; // cached chunk + + void make_initial_chunks(int up = 1); + + void cache(int idx) const; + void cache(const * p) const; + + * dopred(const * p) const; + * dosucc(const * p) const; + + void set_cache(const IChunk* t) const; // logically, + // not physically const +public: + XPlex(); // set low = 0; + // fence = 0; + // csize = default + + XPlex(int ch_size); // low = 0; + // fence = 0; + // csize = ch_size + + XPlex(int lo, // low = lo; + int ch_size); // fence=lo + // csize = ch_size + + XPlex(int lo, // low = lo + int hi, // fence = hi+1 + const initval,// fill with initval, + int ch_size = 0); // csize= ch_size + // or fence-lo if 0 + + XPlex(const XPlex&); + + void operator= (const XPlex&); + +// virtuals + + + & high_element (); + & low_element (); + + const & high_element () const; + const & low_element () const; + + Pix first() const; + Pix last() const; + void prev(Pix& ptr) const; + void next(Pix& ptr) const; + int owns(Pix p) const; + & operator () (Pix p); + const & operator () (Pix p) const; + + int low() const; + int high() const; + int valid(int idx) const; + void prev(int& idx) const; + void next(int& x) const; + & operator [] (int index); + const & operator [] (int index) const; + + int Pix_to_index(Pix p) const; + Pix index_to_Pix(int idx) const; + + int can_add_high() const; + int can_add_low() const; + int full() const; + + int add_high(const elem); + int del_high (); + int add_low (const elem); + int del_low (); + + void fill(const x); + void fill(const x, int from, int to); + void clear(); + void reverse(); + + int OK () const; + +}; + + +inline void XPlex::prev(int& idx) const +{ + --idx; +} + +inline void XPlex::next(int& idx) const +{ + ++idx; +} + +inline int XPlex::full () const +{ + return 0; +} + +inline int XPlex::can_add_high() const +{ + return 1; +} + +inline int XPlex::can_add_low() const +{ + return 1; +} + +inline int XPlex::valid (int idx) const +{ + return idx >= lo && idx < fnc; +} + +inline int XPlex::low() const +{ + return lo; +} + +inline int XPlex::high() const +{ + return fnc - 1; +} + +inline & XPlex:: operator [] (int idx) +{ + if (!ch->actual_index(idx)) cache(idx); + return *(ch->pointer_to(idx)); +} + +inline const & XPlex:: operator [] (int idx) const +{ + if (!ch->actual_index(idx)) cache(idx); + return *((const *)(ch->pointer_to(idx))); +} + +inline & XPlex::low_element () +{ + if (empty()) index_error(); + return *(hd->pointer_to(lo)); +} + +inline const & XPlex::low_element () const +{ + if (empty()) index_error(); + return *((const *)(hd->pointer_to(lo))); +} + +inline & XPlex::high_element () +{ + if (empty()) index_error(); + return *(tl()->pointer_to(fnc - 1)); +} + +inline const & XPlex::high_element () const +{ + if (empty()) index_error(); + return *((const *)(tl()->pointer_to(fnc - 1))); +} + +inline int XPlex::Pix_to_index(Pix px) const +{ + * p = (*)px; + if (!ch->actual_pointer(p)) cache(p); + return ch->index_of(p); +} + +inline Pix XPlex::index_to_Pix(int idx) const +{ + if (!ch->actual_index(idx)) cache(idx); + return (Pix)(ch->pointer_to(idx)); +} + +inline Pix XPlex::first() const +{ + return Pix(hd->IChunk::first_pointer()); +} + +inline Pix XPlex::last() const +{ + return Pix(tl()->IChunk::last_pointer()); +} + +inline void XPlex::prev(Pix& p) const +{ + Pix q = Pix(ch->IChunk::pred((*) p)); + p = (q == 0)? Pix(dopred((const *) p)) : q; +} + +inline void XPlex::next(Pix& p) const +{ + Pix q = Pix(ch->IChunk::succ((*) p)); + p = (q == 0)? Pix(dosucc((const *)p)) : q; +} + +inline & XPlex:: operator () (Pix p) +{ + return *((*)p); +} + +inline const & XPlex:: operator () (Pix p) const +{ + return *((const *)p); +} + +inline void XPlex::set_cache(const IChunk* t) const +{ + ((XPlex*)(this))->ch = (IChunk*)t; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/abs.h b/gnu/lib/libg++/g++-include/abs.h deleted file mode 100644 index 8c2c8f98ce..0000000000 --- a/gnu/lib/libg++/g++-include/abs.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/gnu/lib/libg++/g++-include/assert.h b/gnu/lib/libg++/g++-include/assert.h index 4a96c96ecc..44a65704cf 100644 --- a/gnu/lib/libg++/g++-include/assert.h +++ b/gnu/lib/libg++/g++-include/assert.h @@ -1,45 +1,14 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation +#ifndef __libgxx_assert_h -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -/* Allow this file to be included multiple times - with different settings of NDEBUG. */ -#undef assert -#undef assertval - -#ifdef NDEBUG -#define assert(ignore) -#define assertval(ex) (ex) +extern "C" { +#ifdef __assert_h_recursive +#include_next #else - -extern "C" void __eprintf (char*, int, char*); /* Defined in gnulib */ -extern "C" volatile void abort(); - -#define assert(ex) \ - ((ex) ? 1 : \ - (__eprintf("Failed assertion " #ex " at line %d of `%s'.\n", \ - __LINE__, __FILE__), abort (), 0)) -#define assertval(ex) \ - ((ex) ? 1 : \ - (__eprintf("Failed assertion " #ex " at line %d of `%s'.\n", \ - __LINE__, __FILE__), abort (), 0)) - -#endif NDEBUG +/* assert.h on some systems needs stdio.h, in violation of ANSI. */ +#include +#include_next + +#define __libgxx_assert_h 1 +#endif +} +#endif diff --git a/gnu/lib/libg++/g++-include/bool.h b/gnu/lib/libg++/g++-include/bool.h deleted file mode 100644 index e843aa9c47..0000000000 --- a/gnu/lib/libg++/g++-include/bool.h +++ /dev/null @@ -1,10 +0,0 @@ - -#ifndef _bool_h -#ifdef __GNUG__ -#pragma once -#endif -#define _bool_h 1 - -enum bool { FALSE = 0, TRUE = 1 }; - -#endif diff --git a/gnu/lib/libg++/g++-include/bstring.h b/gnu/lib/libg++/g++-include/bstring.h new file mode 100644 index 0000000000..3b2f590027 --- /dev/null +++ b/gnu/lib/libg++/g++-include/bstring.h @@ -0,0 +1 @@ +#include diff --git a/gnu/lib/libg++/g++-include/builtin.h b/gnu/lib/libg++/g++-include/builtin.h deleted file mode 100644 index 904059b8af..0000000000 --- a/gnu/lib/libg++/g++-include/builtin.h +++ /dev/null @@ -1,276 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- - -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -/* - arithmetic, etc. functions on built in types -*/ - - -#ifndef _builtin_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _builtin_h 1 - - -typedef void (*one_arg_error_handler_t)(const char*); -typedef void (*two_arg_error_handler_t)(const char*, const char*); - - - -#include -#include -#include - -long gcd(long, long); -long lg(unsigned long); -double pow(double, long); -long pow(long, long); - -double start_timer(); -double return_elapsed_time(double last_time = 0.0); - -char* itoa(long x, int base = 10, int width = 0); -char* itoa(unsigned long x, int base = 10, int width = 0); -#ifdef __GNUG__ -char* itoa(long long x, int base = 10, int width = 0); -char* itoa(unsigned long long x, int base = 10, int width = 0); -#endif -char* dtoa(double x, char cvt = 'g', int width = 0, int prec = 6); - -char* hex(long x, int width = 0); -char* hex(unsigned long x, int width = 0); -char* hex(int x, int width = 0); -char* hex(short x, int width = 0); -char* hex(unsigned int x, int width = 0); -char* hex(unsigned short x, int width = 0); - -char* oct(long x, int width = 0); -char* oct(unsigned long x, int width = 0); -char* oct(int x, int width = 0); -char* oct(short x, int width = 0); -char* oct(unsigned int x, int width = 0) ; -char* oct(unsigned short x, int width = 0); - -char* dec(long x, int width = 0); -char* dec(unsigned long x, int width = 0); -char* dec(int x, int width = 0); -char* dec(short x, int width = 0); -char* dec(unsigned int x, int width = 0) ; -char* dec(unsigned short x, int width = 0); - -char* form(const char* fmt ...); -char* chr(char ch, int width = 0); -char* str(const char* s, int width = 0); - -unsigned int hashpjw(const char*); -unsigned int multiplicativehash(int); -unsigned int foldhash(double); - -extern void default_one_arg_error_handler(const char*); -extern void default_two_arg_error_handler(const char*, const char*); - -extern two_arg_error_handler_t lib_error_handler; - -extern two_arg_error_handler_t - set_lib_error_handler(two_arg_error_handler_t f); - - -double abs(double arg); -float abs(float arg); -short abs(short arg); -long abs(long arg); -int sign(long arg); -int sign(double arg); -long sqr(long arg); -double sqr(double arg); -int even(long arg); -int odd(long arg); -long lcm(long x, long y); -void setbit(long& x, long b); -void clearbit(long& x, long b); -int testbit(long x, long b); - -signed char min(signed char a, signed char b); -unsigned char min(unsigned char a, unsigned char b); - -signed short min(signed short a, signed short b); -unsigned short min(unsigned short a, unsigned short b); - -signed int min(signed int a, signed int b); -unsigned int min(unsigned int a, unsigned int b); - -signed long min(signed long a, signed long b); -unsigned long min(unsigned long a, unsigned long b); - -float min(float a, float b); - -double min(double a, double b); - -signed char max(signed char a, signed char b); -unsigned char max(unsigned char a, unsigned char b); - -signed short max(signed short a, signed short b); -unsigned short max(unsigned short a, unsigned short b); - -signed int max(signed int a, signed int b); -unsigned int max(unsigned int a, unsigned int b); - -signed long max(signed long a, signed long b); -unsigned long max(unsigned long a, unsigned long b); - -float max(float a, float b); - -double max(double a, double b); - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - - -inline double abs(double arg) -{ - return (arg < 0.0)? -arg : arg; -} - -inline float abs(float arg) -{ - return (arg < 0.0)? -arg : arg; -} - -inline short abs(short arg) -{ - return (arg < 0)? -arg : arg; -} - -inline long abs(long arg) -{ - return (arg < 0)? -arg : arg; -} - -inline int sign(long arg) -{ - return (arg == 0) ? 0 : ( (arg > 0) ? 1 : -1 ); -} - -inline int sign(double arg) -{ - return (arg == 0.0) ? 0 : ( (arg > 0.0) ? 1 : -1 ); -} - -inline long sqr(long arg) -{ - return arg * arg; -} - -inline double sqr(double arg) -{ - return arg * arg; -} - -inline int even(long arg) -{ - return !(arg & 1); -} - -inline int odd(long arg) -{ - return (arg & 1); -} - -inline long lcm(long x, long y) -{ - return x / gcd(x, y) * y; -} - -inline void setbit(long& x, long b) -{ - x |= (1 << b); -} - -inline void clearbit(long& x, long b) -{ - x &= ~(1 << b); -} - -inline int testbit(long x, long b) -{ - return ((x & (1 << b)) != 0); -} - -inline char* hex(int x, int width = 0) { return hex(long(x), width); } -inline char* hex(short x, int width = 0) { return hex(long(x), width); } -inline char* hex(unsigned int x, int width = 0) -{ return hex((unsigned long)(x), width); } -inline char* hex(unsigned short x, int width = 0) -{ return hex((unsigned long)(x), width); } - -inline char* oct(int x, int width = 0) { return oct(long(x), width); } -inline char* oct(short x, int width = 0) { return oct(long(x), width); } -inline char* oct(unsigned int x, int width = 0) -{ return oct((unsigned long)(x), width); } -inline char* oct(unsigned short x, int width = 0) -{ return oct((unsigned long)(x), width); } - -inline char* dec(int x, int width = 0) { return dec(long(x), width); } -inline char* dec(short x, int width = 0) { return dec(long(x), width); } -inline char* dec(unsigned int x, int width = 0) -{ return dec((unsigned long)(x), width); } -inline char* dec(unsigned short x, int width = 0) -{ return dec((unsigned long)(x), width); } - -inline signed char min(signed char a, signed char b) { return (a < b)?a:b;} -inline unsigned char min(unsigned char a, unsigned char b) {return (a < b)?a:b;} - -inline signed short min(signed short a, signed short b) {return (a < b) ?a:b;} -inline unsigned short min(unsigned short a, unsigned short b) {return (a < b)?a:b;} - -inline signed int min(signed int a, signed int b) {return (a < b)?a:b;} -inline unsigned int min(unsigned int a, unsigned int b) {return (a < b)?a:b;} - -inline signed long min(signed long a, signed long b) {return (a < b)?a:b;} -inline unsigned long min(unsigned long a, unsigned long b) {return (a < b)?a:b;} - -inline float min(float a, float b) {return (a < b)?a:b;} - -inline double min(double a, double b) {return (a < b)?a:b;} - -inline signed char max(signed char a, signed char b) { return (a > b)?a:b;} -inline unsigned char max(unsigned char a, unsigned char b) {return (a > b)?a:b;} - -inline signed short max(signed short a, signed short b) {return (a > b) ?a:b;} -inline unsigned short max(unsigned short a, unsigned short b) {return (a > b)?a:b;} - -inline signed int max(signed int a, signed int b) {return (a > b)?a:b;} -inline unsigned int max(unsigned int a, unsigned int b) {return (a > b)?a:b;} - -inline signed long max(signed long a, signed long b) {return (a > b)?a:b;} -inline unsigned long max(unsigned long a, unsigned long b) {return (a > b)?a:b;} - -inline float max(float a, float b) {return (a > b)?a:b;} - -inline double max(double a, double b) {return (a > b)?a:b;} - -#endif - -#endif diff --git a/gnu/lib/libg++/g++-include/compare.h b/gnu/lib/libg++/g++-include/compare.h deleted file mode 100644 index e298e2f195..0000000000 --- a/gnu/lib/libg++/g++-include/compare.h +++ /dev/null @@ -1,98 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- - -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifndef _compare_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _compare_h 1 - -#include - -int compare(int a, int b); -int compare(short a, short b); -int compare(char a, char b); -int compare(unsigned long a, unsigned long b); -int compare(unsigned int a, unsigned int b); -int compare(unsigned short a, unsigned short b); -int compare(unsigned char a, unsigned char b); -int compare(float a, float b); -int compare(double a, double b); -int compare(const char* a, const char* b); - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline int compare(int a, int b) -{ - return a - b; -} - -inline int compare(short a, short b) -{ - return a - b; -} - -inline int compare(char a, char b) -{ - return a - b; -} - -inline int compare(unsigned long a, unsigned long b) -{ - return (a < b)? -1 : (a > b)? 1 : 0; -} - -inline int compare(unsigned int a, unsigned int b) -{ - return (a < b)? -1 : (a > b)? 1 : 0; -} - -inline int compare(unsigned short a, unsigned short b) -{ - return (a < b)? -1 : (a > b)? 1 : 0; -} - -inline int compare(unsigned char a, unsigned char b) -{ - return (a < b)? -1 : (a > b)? 1 : 0; -} - -inline int compare(float a, float b) -{ - return (a < b)? -1 : (a > b)? 1 : 0; -} - -inline int compare(double a, double b) -{ - return (a < b)? -1 : (a > b)? 1 : 0; -} - -inline int compare(const char* a, const char* b) -{ - return strcmp(a,b); -} - -#endif -#endif diff --git a/gnu/lib/libg++/g++-include/complex.h b/gnu/lib/libg++/g++-include/complex.h deleted file mode 100644 index 1411c22f0c..0000000000 --- a/gnu/lib/libg++/g++-include/complex.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _complex_h -#ifdef __GNUG__ -#pragma once -#endif -#define _complex_h -#define __ATT_complex__ -#include -typedef class Complex complex; -#endif diff --git a/gnu/lib/libg++/g++-include/ctype.h b/gnu/lib/libg++/g++-include/ctype.h new file mode 100644 index 0000000000..b573e1995d --- /dev/null +++ b/gnu/lib/libg++/g++-include/ctype.h @@ -0,0 +1,10 @@ +#include <_G_config.h> +extern "C" { +#include_next +#ifndef toupper +extern int toupper _G_ARGS((int)); +#endif +#ifndef tolower +extern int tolower _G_ARGS((int)); +#endif +} diff --git a/gnu/lib/libg++/g++-include/curses.h b/gnu/lib/libg++/g++-include/curses.h index 6a4d61c8e2..b7b32be78c 100644 --- a/gnu/lib/libg++/g++-include/curses.h +++ b/gnu/lib/libg++/g++-include/curses.h @@ -1,342 +1,82 @@ -// This may look like C code, but it is really -*- C++ -*- +#ifndef _G_curses_h -/* -Copyright (C) 1989 Free Software Foundation - written by Eric Newton (newton@rocky.oswego.edu) +#include <_G_config.h> -This file is part of GNU CC. +#if _G_HAVE_CURSES -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -/* - * Edited for compatablity with C++, 2/28/89 - * This file has all the /usr/include/curses.h info (with proper prototypes) - * used for the CursesWindow classes. You may need to change this to be - * compatable with your curses implementation. - * - */ - -#ifndef _curses_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _curses_h +#ifdef __curses_h_recursive +#include_next +#else +#define __curses_h_recursive -# include -# include -# include extern "C" { -#ifndef VMS -# include -#endif - -// protection against possibility that these are macros: - -#ifndef stty -extern int stty(int, struct sgttyb*); -#endif - -#ifndef gtty -extern int gtty(int, struct sgttyb*); -#endif - -} - -typedef char cbool; // curses explicitly declares bools as chars - -#if defined(hpux) -enum CursesStatus { ERR = -1, OK = 0 }; // curses lib uses define's +#include_next + +/* Some systems (SVR4 for example) allow the definition of CHTYPE to set the + type of some arguments to the curses functions. It can be set to "char" + to save space, or it can be set to something longer to store both a + character and some attributes. By default they do not define CHTYPE, + and when CHTYPE is not defined, the default type is "unsigned long" instead + of the traditional "char". However, SVR4 does define + _VR3_COMPAT_CODE, so we can use that to detect when we should use the SVR4 + default if CHTYPE is not defined. For other systems, just default to the + traditional default "char". */ + +#ifdef CHTYPE + typedef CHTYPE _G_chtype; /* Use specified type. */ #else -enum CursesStatus { ERR = 0, OK = 1 }; // curses lib uses define's -#endif - -/* - * BSD'ish. Warning!! - * - */ -# define _ENDLINE 001 -# define _FULLWIN 002 -# define _SCROLLWIN 004 -# define _FLUSH 010 -# define _FULLLINE 020 -# define _IDLINE 040 -# define _STANDOUT 0200 -# define _NOCHANGE -1 - -# define _puts(s) tputs(s, 0, _putchar) - -/* - * Capabilities from termcap - */ - -extern cbool AM, BS, CA, DA, DB, EO, HC, HZ, IN, MI, MS, NC, NS, OS, UL, - XB, XN, XT, XS, XX; -extern char *AL, *BC, *BT, *CD, *CE, *CL, *CM, *CR, *CS, *DC, *DL, - *DM, *DO, *ED, *EI, *K0, *K1, *K2, *K3, *K4, *K5, *K6, - *K7, *K8, *K9, *HO, *IC, *IM, *IP, *KD, *KE, *KH, *KL, - *KR, *KS, *KU, *LL, *MA, *ND, *NL, *RC, *SC, *SE, *SF, - *SO, *SR, *TA, *TE, *TI, *UC, *UE, *UP, *US, *VB, *VS, - *VE, *AL_PARM, *DL_PARM, *UP_PARM, *DOWN_PARM, - *LEFT_PARM, *RIGHT_PARM; - -extern char PC; - -extern cbool GT, NONL, UPPERCASE, normtty, _pfast; - -struct _win_st { -#ifdef VMS - int _cury, _curx; - int _maxy, _maxx; - int _begy, _begx; +#ifdef _VR3_COMPAT_CODE + typedef unsigned long _G_chtype; /* SVR4 default is "unsigned long" */ +#elif defined(hpux) + typedef unsigned int _G_chtype; #else - short _cury, _curx; - short _maxy, _maxx; - short _begy, _begx; -#endif - short _flags; -#ifndef VMS - short _ch_off; -#endif - cbool _clear; - cbool _leave; - cbool _scroll; -#ifdef VMS - cbool _wrap; + typedef char _G_chtype; /* Traditional default is "char" */ #endif - char **_y; - short *_firstch; - short *_lastch; - struct _win_st *_nextp, *_orig; -#ifdef VMS - struct _win_st *_parent , *_child; - int _id; #endif -}; - -#define WINDOW struct _win_st - -extern cbool My_term; -extern cbool _echoit; -extern cbool _rawmode; -extern cbool _endwin; - -extern char *Def_term; -extern char ttytype[]; - -extern int LINES; -extern int COLS; -extern int _tty_ch; -extern int _res_flg; - - -typedef struct sgttyb SGTTY; - -extern SGTTY _tty; - -/* - * standard curses functions. - * - */ - -extern "C" -{ -extern WINDOW * stdscr; -extern WINDOW * curscr; -WINDOW * newwin(int lines, int cols, int sy, int sx); -WINDOW * subwin(WINDOW *w, int lines, int cols, int sy, int sx); -WINDOW * initscr(); -int box (WINDOW*, char, char); -int delwin(WINDOW*); -int mvcur(int, int, int, int); -int overlay(WINDOW*, WINDOW*); -int overwrite(WINDOW*, WINDOW*); -int scroll(WINDOW*); -int touchwin(WINDOW*); -int waddch(WINDOW*, char); -int waddstr(WINDOW*, const char*); -int wclear(WINDOW*); -int wclrtobot(WINDOW*); -int wclrtoeol(WINDOW*); -int wdelch(WINDOW*); -int wdeleteln(WINDOW*); -int werase(WINDOW*); -int wgetch(WINDOW*); -int wgetstr(WINDOW*, char*); -int winsch(WINDOW*, char); -int winsertln(WINDOW*); -int wmove(WINDOW*, int, int); -int wrefresh(WINDOW*); -int wstandend(WINDOW*); -int wstandout(WINDOW*); -int wprintw(WINDOW*, const char * fmt, ...); -int mvwprintw(WINDOW*, int y, int x, const char * fmt, ...); -int wscanw(WINDOW*, const char *, ...); -int mvwscanw(int, int, WINDOW*, const char*, ...); -int endwin(); -} - -/* Pseudo functions */ -/* - * these are inlines rather than defines here so as to allow overloaded - * versions in the CursesWindow class - */ - -int clearok(WINDOW* win, cbool bf); -int leaveok(WINDOW* win, cbool bf); -int scrollok(WINDOW* win, cbool bf); -int flushok(WINDOW* win, cbool bf); -void getyx(WINDOW* win, int& y, int& x); -int winch(WINDOW* win); - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline int clearok(WINDOW* win, cbool bf) { return(win->_clear = bf); } -inline int leaveok(WINDOW* win, cbool bf) { return(win->_leave = bf); } -inline int scrollok(WINDOW* win, cbool bf) { return(win->_scroll = bf); } -inline int flushok(WINDOW* win, cbool bf) -{ return(bf ? (win->_flags |= _FLUSH):(win->_flags &= ~_FLUSH)); } -inline void getyx(WINDOW* win, int& y, int& x) -{ y = win->_cury; x = win->_curx; } -inline int winch(WINDOW* win) -{return win->_y[win->_cury][win->_curx] & 0177; } -#endif /* __OPTIMIZE__ */ +/* Some args are conceptually const, but SVR4 (and others?) get it wrong. */ +#define _C_const /* const */ + +WINDOW * (newwin)(int, int, int, int); +WINDOW * (subwin)(WINDOW *, int, int, int, int); +WINDOW * (initscr)(); +int (box) (WINDOW*, _G_chtype, _G_chtype); +int (delwin)(WINDOW*); +int (getcurx)(WINDOW*); +int (getcury)(WINDOW*); +int (mvcur)(int, int, int, int); +int (overlay)(WINDOW*, WINDOW*); +int (overwrite)(WINDOW*, WINDOW*); +int (scroll)(WINDOW*); +int (touchwin)(WINDOW*); +int (waddch)(WINDOW*, _G_chtype); +int (waddstr) _G_ARGS((WINDOW*, const char*)); +int (wclear)(WINDOW*); +int (wclrtobot)(WINDOW*); +int (wclrtoeol)(WINDOW*); +int (wdelch)(WINDOW*); +int (wdeleteln)(WINDOW*); +int (werase)(WINDOW*); +int (wgetch)(WINDOW*); +int (wgetstr)(WINDOW*, char*); +int (winsch)(WINDOW*, _G_chtype); +int (winsertln)(WINDOW*); +int (wmove)(WINDOW*, int, int); +int (wrefresh)(WINDOW*); +int (wstandend)(WINDOW*); +int (wstandout)(WINDOW*); + +// SVR4 rather inanely bundles the format-string parameter with the '...'. +// This breaks VMS, and I don't want to penalize VMS for being right for once! + +int (wprintw)(WINDOW*, _G_CURSES_FORMAT_ARG ...); +int (mvwprintw)(WINDOW*, int y, int x, _G_CURSES_FORMAT_ARG ...); +int (wscanw)(WINDOW*, _G_CURSES_FORMAT_ARG ...); +int (mvwscanw)(WINDOW*, int, int, _G_CURSES_FORMAT_ARG ...); +int (endwin)(); -#if defined(USG) || defined(VMS) -extern "C" { -int raw(); -int noraw(); -int cbreak(); -int nocbreak(); -} - -#else - -int raw(); -int noraw(); -int cbreak(); -int nocbreak(); - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline int raw() -{ return _tty.sg_flags|=RAW, _pfast=_rawmode=1, stty(_tty_ch,&_tty); } -inline int noraw() -{ return _tty.sg_flags&=~RAW,_rawmode=0,_pfast=!(_tty.sg_flags&CRMOD),stty(_tty_ch,&_tty); } -inline int cbreak() -{ return _tty.sg_flags |= CBREAK, _rawmode = 1, stty(_tty_ch,&_tty); } -inline int nocbreak() -{ return _tty.sg_flags &= ~CBREAK,_rawmode=0,stty(_tty_ch,&_tty); } - -#endif /* __ OPTIMIZE __ */ - -#endif /* USG */ - - -int crmode(); -int nocrmode(); - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline int crmode() { return cbreak(); } -inline int nocrmode() { return nocbreak(); } - -#endif /* __ OPTIMIZE __ */ - -#if defined(USG) || defined(VMS) - -extern "C" { -int _setecho(int); -int _setnonl(int); } - -int echo(); -int noecho(); -int nl(); -int nonl(); - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline int echo() { return _setecho(1); } -inline int noecho() { return _setecho(0); } -inline int nl() { return _setnonl(0); } -inline int nonl() { return _setnonl(1); } - -#endif /* __ OPTIMIZE __ */ - -extern "C" { -int savetty(); -int resetty(); -int erasechar(); -int killchar(); -int baudrate(); -} - -#else /* not USG */ - -int echo(); -int noecho(); -int nl(); -int nonl(); -int savetty(); -inline int resetty(); -int erasechar(); -int killchar(); -int baudrate(); - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline int echo() -{ return _tty.sg_flags |= ECHO, _echoit = 1, stty(_tty_ch, &_tty); } -inline int noecho() -{ return _tty.sg_flags &= ~ECHO, _echoit = 0, stty(_tty_ch, &_tty); } -inline int nl() -{ return _tty.sg_flags |= CRMOD,_pfast = _rawmode,stty(_tty_ch, &_tty); } -inline int nonl() -{ return _tty.sg_flags &= ~CRMOD, _pfast = 1, stty(_tty_ch, &_tty); } -inline int savetty() -{ return (void) gtty(_tty_ch, &_tty), _res_flg = _tty.sg_flags; } -inline int resetty() -{ return _tty.sg_flags = _res_flg, stty(_tty_ch, &_tty); } -inline int erasechar() -{ return _tty.sg_erase; } -inline int killchar() -{ return _tty.sg_kill; } -inline int baudrate() -{ return _tty.sg_ospeed; } - -#endif /* __ OPTIMIZE __ */ - -#endif /* USG */ - -extern "C" { -char *longname(char *, char *); -char *getcap(char *); -extern char *_unctrl[]; -} - - -char * unctrl(int c); - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - -inline char * unctrl(int c) { return _unctrl[(c) & 0177]; } - -#endif /* __ OPTIMIZE __ */ - +#define _G_curses_h #endif +#endif /* _G_HAVE_CURSES */ +#endif /* _G_curses_h */ diff --git a/gnu/lib/libg++/g++-include/defs.hP b/gnu/lib/libg++/g++-include/defs.hP new file mode 100644 index 0000000000..054f6a65c3 --- /dev/null +++ b/gnu/lib/libg++/g++-include/defs.hP @@ -0,0 +1,57 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _defs_h +#define _defs_h 1 + + +// equality operator +#ifndef EQ +#define EQ(a, b) ((a) == (b)) +#endif + +// less-than-or-equal +#ifndef LE +#define LE(a, b) ((a) <= (b)) +#endif + +// comparison : less-than -> < 0; equal -> 0; greater-than -> > 0 +#ifndef CMP +#define CMP(a, b) ( ((a) <= (b))? (((a) == (b))? 0 : -1) : 1 ) +#endif + +// hash function +#ifndef HASH +extern unsigned int hash(); +#define HASH(x) hash(x) +#endif + +// initial capacity for structures requiring one + +#ifndef DEFAULT_INITIAL_CAPACITY +#define DEFAULT_INITIAL_CAPACITY 100 +#endif + +// HASHTABLE_TOO_CROWDED(COUNT, SIZE) is true iff a hash table with COUNT +// elements and SIZE slots is too full, and should be resized. +// This is so if available space is less than 1/8. + +#define HASHTABLE_TOO_CROWDED(COUNT, SIZE) ((SIZE) - ((SIZE) >> 3) <= (COUNT)) + +#endif diff --git a/gnu/lib/libg++/g++-include/dir.h b/gnu/lib/libg++/g++-include/dir.h new file mode 100644 index 0000000000..78c540242c --- /dev/null +++ b/gnu/lib/libg++/g++-include/dir.h @@ -0,0 +1 @@ +#include diff --git a/gnu/lib/libg++/g++-include/dirent.h b/gnu/lib/libg++/g++-include/dirent.h new file mode 100644 index 0000000000..4c06ea837a --- /dev/null +++ b/gnu/lib/libg++/g++-include/dirent.h @@ -0,0 +1,44 @@ +#ifndef __libgxx_dirent_h + +#include <_G_config.h> + +#if !_G_HAVE_DIRENT +#define __libgxx_dirent_h +#define direct dirent +#include +#else + +extern "C" { + +#ifdef __dirent_h_recursive +#include_next +#else +// Note: sys/dir.h checks __dirent_h_recursive +#define __dirent_h_recursive +#define opendir __hide_opendir +#define closedir __hide_closedir +#define readdir __hide_readdir +#define telldir __hide_telldir +#define seekdir __hide_seekdir + +#include_next + +#define __libgxx_dirent_h +#undef opendir +#undef closedir +#undef readdir +#undef telldir +#undef seekdir + +DIR *opendir(const char *); +int closedir(DIR *); +struct dirent *readdir(DIR *); +long telldir(DIR *); +void seekdir(DIR *, long); +// We don't bother with rewinddir (many systems define it as a macro). +// void rewinddir(DIR *); +#endif +} + +#endif +#endif diff --git a/gnu/lib/libg++/g++-include/errno.h b/gnu/lib/libg++/g++-include/errno.h new file mode 100644 index 0000000000..f9f86c85d1 --- /dev/null +++ b/gnu/lib/libg++/g++-include/errno.h @@ -0,0 +1,24 @@ +#ifndef errno_h + +extern "C" { + +#ifdef __errno_h_recursive +#include_next +#else +#define __errno_h_recursive +#include_next + +#define errno_h 1 + +extern char* sys_errlist[]; +extern int sys_nerr; +#ifndef errno +extern int errno; +#endif +void perror(const char*); +char* strerr(int); + +#endif +} + +#endif diff --git a/gnu/lib/libg++/g++-include/fcntl.h b/gnu/lib/libg++/g++-include/fcntl.h new file mode 100644 index 0000000000..48637ef016 --- /dev/null +++ b/gnu/lib/libg++/g++-include/fcntl.h @@ -0,0 +1,29 @@ +#ifndef fcntl_h + +extern "C" { + +#ifdef __fcntl_h_recursive +#include_next +#else +#define fcntl __hide_fcntl +#define open __hide_open +#define creat __hide_creat + +#define __fcntl_h_recursive +#include <_G_config.h> +#include_next + +#undef fcntl +#undef open +#undef creat + +#define fcntl_h 1 + +int fcntl(int, int, ...); +int creat _G_ARGS((const char*, unsigned short int)); + +int open _G_ARGS((const char*, int, ...)); + +#endif +} +#endif diff --git a/gnu/lib/libg++/g++-include/file.h b/gnu/lib/libg++/g++-include/file.h deleted file mode 100644 index ca112456ea..0000000000 --- a/gnu/lib/libg++/g++-include/file.h +++ /dev/null @@ -1,8 +0,0 @@ - -#ifndef file_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#include -#endif diff --git a/gnu/lib/libg++/g++-include/filebuf.h b/gnu/lib/libg++/g++-include/filebuf.h deleted file mode 100644 index 9cf35ccadb..0000000000 --- a/gnu/lib/libg++/g++-include/filebuf.h +++ /dev/null @@ -1,63 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifndef _filebuf_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _filebuf_h 1 - -#include -#include - -class filebuf: public streambuf -{ -public: - int fd; - char opened; - - int overflow(int c = EOF); - int underflow(); - - filebuf(); - filebuf(int newfd); - filebuf(int newfd, char* buf, int buflen); - filebuf(const char* filename, io_mode m, access_mode a); - filebuf(const char* filename, const char* m); - filebuf(int filedesc, io_mode m); - filebuf(FILE* fileptr); - - ~filebuf(); - - streambuf* open(const char* name, open_mode m); - streambuf* open(const char* filename, io_mode m, access_mode a); - streambuf* open(const char* filename, const char* m); - streambuf* open(int filedesc, io_mode m); - streambuf* open(FILE* fileptr); - int is_open(); - int close(); -}; - - -#endif diff --git a/gnu/lib/libg++/g++-include/gen/AVLMap.ccP b/gnu/lib/libg++/g++-include/gen/AVLMap.ccP new file mode 100644 index 0000000000..a9be60f066 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/AVLMap.ccP @@ -0,0 +1,614 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include "..AVLMap.h" + + +/* + constants & inlines for maintaining balance & thread status in tree nodes +*/ + +#define AVLBALANCEMASK 3 +#define AVLBALANCED 0 +#define AVLLEFTHEAVY 1 +#define AVLRIGHTHEAVY 2 + +#define LTHREADBIT 4 +#define RTHREADBIT 8 + + +static inline int bf(AVLNode* t) +{ + return t->stat & AVLBALANCEMASK; +} + +static inline void set_bf(AVLNode* t, int b) +{ + t->stat = (t->stat & ~AVLBALANCEMASK) | (b & AVLBALANCEMASK); +} + + +static inline int rthread(AVLNode* t) +{ + return t->stat & RTHREADBIT; +} + +static inline void set_rthread(AVLNode* t, int b) +{ + if (b) + t->stat |= RTHREADBIT; + else + t->stat &= ~RTHREADBIT; +} + +static inline int lthread(AVLNode* t) +{ + return t->stat & LTHREADBIT; +} + +static inline void set_lthread(AVLNode* t, int b) +{ + if (b) + t->stat |= LTHREADBIT; + else + t->stat &= ~LTHREADBIT; +} + +/* + traversal primitives +*/ + + +AVLNode* AVLMap::leftmost() +{ + AVLNode* t = root; + if (t != 0) while (t->lt != 0) t = t->lt; + return t; +} + +AVLNode* AVLMap::rightmost() +{ + AVLNode* t = root; + if (t != 0) while (t->rt != 0) t = t->rt; + return t; +} + +AVLNode* AVLMap::succ(AVLNode* t) +{ + AVLNode* r = t->rt; + if (!rthread(t)) while (!lthread(r)) r = r->lt; + return r; +} + +AVLNode* AVLMap::pred(AVLNode* t) +{ + AVLNode* l = t->lt; + if (!lthread(t)) while (!rthread(l)) l = l->rt; + return l; +} + + +Pix AVLMap::seek( key) +{ + AVLNode* t = root; + if (t == 0) + return 0; + for (;;) + { + int cmp = CMP(key, t->item); + if (cmp == 0) + return Pix(t); + else if (cmp < 0) + { + if (lthread(t)) + return 0; + else + t = t->lt; + } + else if (rthread(t)) + return 0; + else + t = t->rt; + } +} + + +/* + The combination of threads and AVL bits make adding & deleting + interesting, but very awkward. + + We use the following statics to avoid passing them around recursively +*/ + +static int _need_rebalancing; // to send back balance info from rec. calls +static * _target_item; // add/del_item target +static AVLNode* _found_node; // returned added/deleted node +static int _already_found; // for deletion subcases + + +void AVLMap:: _add(AVLNode*& t) +{ + int cmp = CMP(*_target_item, t->item); + if (cmp == 0) + { + _found_node = t; + return; + } + else if (cmp < 0) + { + if (lthread(t)) + { + ++count; + _found_node = new AVLNode(*_target_item, def); + set_lthread(_found_node, 1); + set_rthread(_found_node, 1); + _found_node->lt = t->lt; + _found_node->rt = t; + t->lt = _found_node; + set_lthread(t, 0); + _need_rebalancing = 1; + } + else + _add(t->lt); + if (_need_rebalancing) + { + switch(bf(t)) + { + case AVLRIGHTHEAVY: + set_bf(t, AVLBALANCED); + _need_rebalancing = 0; + return; + case AVLBALANCED: + set_bf(t, AVLLEFTHEAVY); + return; + case AVLLEFTHEAVY: + { + AVLNode* l = t->lt; + if (bf(l) == AVLLEFTHEAVY) + { + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + _need_rebalancing = 0; + } + else + { + AVLNode* r = l->rt; + set_rthread(l, lthread(r)); + if (lthread(r)) + l->rt = r; + else + l->rt = r->lt; + r->lt = l; + set_lthread(r, 0); + set_lthread(t, rthread(r)); + if (rthread(r)) + t->lt = r; + else + t->lt = r->rt; + r->rt = t; + set_rthread(r, 0); + if (bf(r) == AVLLEFTHEAVY) + set_bf(t, AVLRIGHTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(r) == AVLRIGHTHEAVY) + set_bf(l, AVLLEFTHEAVY); + else + set_bf(l, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + _need_rebalancing = 0; + return; + } + } + } + } + } + else + { + if (rthread(t)) + { + ++count; + _found_node = new AVLNode(*_target_item, def); + set_rthread(t, 0); + set_lthread(_found_node, 1); + set_rthread(_found_node, 1); + _found_node->lt = t; + _found_node->rt = t->rt; + t->rt = _found_node; + _need_rebalancing = 1; + } + else + _add(t->rt); + if (_need_rebalancing) + { + switch(bf(t)) + { + case AVLLEFTHEAVY: + set_bf(t, AVLBALANCED); + _need_rebalancing = 0; + return; + case AVLBALANCED: + set_bf(t, AVLRIGHTHEAVY); + return; + case AVLRIGHTHEAVY: + { + AVLNode* r = t->rt; + if (bf(r) == AVLRIGHTHEAVY) + { + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + _need_rebalancing = 0; + } + else + { + AVLNode* l = r->lt; + set_lthread(r, rthread(l)); + if (rthread(l)) + r->lt = l; + else + r->lt = l->rt; + l->rt = r; + set_rthread(l, 0); + set_rthread(t, lthread(l)); + if (lthread(l)) + t->rt = l; + else + t->rt = l->lt; + l->lt = t; + set_lthread(l, 0); + if (bf(l) == AVLRIGHTHEAVY) + set_bf(t, AVLLEFTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(l) == AVLLEFTHEAVY) + set_bf(r, AVLRIGHTHEAVY); + else + set_bf(r, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + _need_rebalancing = 0; + return; + } + } + } + } + } +} + + +& AVLMap::operator [] ( item) +{ + if (root == 0) + { + ++count; + root = new AVLNode(item, def); + set_rthread(root, 1); + set_lthread(root, 1); + return root->cont; + } + else + { + _target_item = &item; + _need_rebalancing = 0; + _add(root); + return _found_node->cont; + } +} + + +void AVLMap::_del(AVLNode* par, AVLNode*& t) +{ + int comp; + if (_already_found) + { + if (rthread(t)) + comp = 0; + else + comp = 1; + } + else + comp = CMP(*_target_item, t->item); + if (comp == 0) + { + if (lthread(t) && rthread(t)) + { + _found_node = t; + if (t == par->lt) + { + set_lthread(par, 1); + par->lt = t->lt; + } + else + { + set_rthread(par, 1); + par->rt = t->rt; + } + _need_rebalancing = 1; + return; + } + else if (lthread(t)) + { + _found_node = t; + AVLNode* s = succ(t); + if (s != 0 && lthread(s)) + s->lt = t->lt; + t = t->rt; + _need_rebalancing = 1; + return; + } + else if (rthread(t)) + { + _found_node = t; + AVLNode* p = pred(t); + if (p != 0 && rthread(p)) + p->rt = t->rt; + t = t->lt; + _need_rebalancing = 1; + return; + } + else // replace item & find someone deletable + { + AVLNode* p = pred(t); + t->item = p->item; + t->cont = p->cont; + _already_found = 1; + comp = -1; // fall through below to left + } + } + + if (comp < 0) + { + if (lthread(t)) + return; + _del(t, t->lt); + if (!_need_rebalancing) + return; + switch (bf(t)) + { + case AVLLEFTHEAVY: + set_bf(t, AVLBALANCED); + return; + case AVLBALANCED: + set_bf(t, AVLRIGHTHEAVY); + _need_rebalancing = 0; + return; + case AVLRIGHTHEAVY: + { + AVLNode* r = t->rt; + switch (bf(r)) + { + case AVLBALANCED: + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLRIGHTHEAVY); + set_bf(r, AVLLEFTHEAVY); + _need_rebalancing = 0; + t = r; + return; + case AVLRIGHTHEAVY: + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + return; + case AVLLEFTHEAVY: + { + AVLNode* l = r->lt; + set_lthread(r, rthread(l)); + if (rthread(l)) + r->lt = l; + else + r->lt = l->rt; + l->rt = r; + set_rthread(l, 0); + set_rthread(t, lthread(l)); + if (lthread(l)) + t->rt = l; + else + t->rt = l->lt; + l->lt = t; + set_lthread(l, 0); + if (bf(l) == AVLRIGHTHEAVY) + set_bf(t, AVLLEFTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(l) == AVLLEFTHEAVY) + set_bf(r, AVLRIGHTHEAVY); + else + set_bf(r, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + return; + } + } + } + } + } + else + { + if (rthread(t)) + return; + _del(t, t->rt); + if (!_need_rebalancing) + return; + switch (bf(t)) + { + case AVLRIGHTHEAVY: + set_bf(t, AVLBALANCED); + return; + case AVLBALANCED: + set_bf(t, AVLLEFTHEAVY); + _need_rebalancing = 0; + return; + case AVLLEFTHEAVY: + { + AVLNode* l = t->lt; + switch (bf(l)) + { + case AVLBALANCED: + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLLEFTHEAVY); + set_bf(l, AVLRIGHTHEAVY); + _need_rebalancing = 0; + t = l; + return; + case AVLLEFTHEAVY: + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + return; + case AVLRIGHTHEAVY: + { + AVLNode* r = l->rt; + set_rthread(l, lthread(r)); + if (lthread(r)) + l->rt = r; + else + l->rt = r->lt; + r->lt = l; + set_lthread(r, 0); + set_lthread(t, rthread(r)); + if (rthread(r)) + t->lt = r; + else + t->lt = r->rt; + r->rt = t; + set_rthread(r, 0); + if (bf(r) == AVLLEFTHEAVY) + set_bf(t, AVLRIGHTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(r) == AVLRIGHTHEAVY) + set_bf(l, AVLLEFTHEAVY); + else + set_bf(l, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + return; + } + } + } + } + } +} + + + +void AVLMap::del( item) +{ + if (root == 0) return; + _need_rebalancing = 0; + _already_found = 0; + _found_node = 0; + _target_item = &item; + _del(root, root); + if (_found_node) + { + delete(_found_node); + if (--count == 0) + root = 0; + } +} + +void AVLMap::_kill(AVLNode* t) +{ + if (t != 0) + { + if (!lthread(t)) _kill(t->lt); + if (!rthread(t)) _kill(t->rt); + delete t; + } +} + + +AVLMap::AVLMap(AVLMap& b) :Map(b.def) +{ + root = 0; + count = 0; + for (Pix i = b.first(); i != 0; b.next(i)) + (*this)[b.key(i)] = b.contents(i); +} + + +int AVLMap::OK() +{ + int v = 1; + if (root == 0) + v = count == 0; + else + { + int n = 1; + AVLNode* trail = leftmost(); + AVLNode* t = succ(trail); + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/AVLMap.hP b/gnu/lib/libg++/g++-include/gen/AVLMap.hP new file mode 100644 index 0000000000..119ee82caa --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/AVLMap.hP @@ -0,0 +1,141 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _AVLMap_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _AVLMap_h 1 + +#include "..Map.h" + +struct AVLNode +{ + AVLNode* lt; + AVLNode* rt; + item; + cont; + char stat; + AVLNode( h, c, + AVLNode* l=0, AVLNode* r=0); + ~AVLNode(); +}; + +inline AVLNode::AVLNode( h, c, + AVLNode* l, AVLNode* r) + :item(h), cont(c), lt(l), rt(r), stat(0) {} + +inline AVLNode::~AVLNode() {} + +typedef AVLNode* AVLNodePtr; + + +class AVLMap : public Map +{ +protected: + AVLNode* root; + + AVLNode* leftmost(); + AVLNode* rightmost(); + AVLNode* pred(AVLNode* t); + AVLNode* succ(AVLNode* t); + void _kill(AVLNode* t); + void _add(AVLNode*& t); + void _del(AVLNode* p, AVLNode*& t); + +public: + AVLMap( dflt); + AVLMap(AVLMap& a); + ~AVLMap(); + + & operator [] ( key); + + void del( key); + + Pix first(); + void next(Pix& i); + & key(Pix i); + & contents(Pix i); + + Pix seek( key); + int contains( key); + + void clear(); + + Pix last(); + void prev(Pix& i); + + int OK(); +}; + +inline AVLMap::~AVLMap() +{ + _kill(root); +} + +inline AVLMap::AVLMap( dflt) :Map(dflt) +{ + root = 0; +} + +inline Pix AVLMap::first() +{ + return Pix(leftmost()); +} + +inline Pix AVLMap::last() +{ + return Pix(rightmost()); +} + +inline void AVLMap::next(Pix& i) +{ + if (i != 0) i = Pix(succ((AVLNode*)i)); +} + +inline void AVLMap::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((AVLNode*)i)); +} + +inline & AVLMap::key(Pix i) +{ + if (i == 0) error("null Pix"); + return ((AVLNode*)i)->item; +} + +inline & AVLMap::contents(Pix i) +{ + if (i == 0) error("null Pix"); + return ((AVLNode*)i)->cont; +} + +inline void AVLMap::clear() +{ + _kill(root); + count = 0; + root = 0; +} + +inline int AVLMap::contains( key) +{ + return seek(key) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/AVLSet.ccP b/gnu/lib/libg++/g++-include/gen/AVLSet.ccP new file mode 100644 index 0000000000..b170734e54 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/AVLSet.ccP @@ -0,0 +1,892 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".AVLSet.h" +#include + + +/* + constants & inlines for maintaining balance & thread status in tree nodes +*/ + +#define AVLBALANCEMASK 3 +#define AVLBALANCED 0 +#define AVLLEFTHEAVY 1 +#define AVLRIGHTHEAVY 2 + +#define LTHREADBIT 4 +#define RTHREADBIT 8 + + +static inline int bf(AVLNode* t) +{ + return t->stat & AVLBALANCEMASK; +} + +static inline void set_bf(AVLNode* t, int b) +{ + t->stat = (t->stat & ~AVLBALANCEMASK) | (b & AVLBALANCEMASK); +} + + +static inline int rthread(AVLNode* t) +{ + return t->stat & RTHREADBIT; +} + +static inline void set_rthread(AVLNode* t, int b) +{ + if (b) + t->stat |= RTHREADBIT; + else + t->stat &= ~RTHREADBIT; +} + +static inline int lthread(AVLNode* t) +{ + return t->stat & LTHREADBIT; +} + +static inline void set_lthread(AVLNode* t, int b) +{ + if (b) + t->stat |= LTHREADBIT; + else + t->stat &= ~LTHREADBIT; +} + +/* + traversal primitives +*/ + + +AVLNode* AVLSet::leftmost() +{ + AVLNode* t = root; + if (t != 0) while (t->lt != 0) t = t->lt; + return t; +} + +AVLNode* AVLSet::rightmost() +{ + AVLNode* t = root; + if (t != 0) while (t->rt != 0) t = t->rt; + return t; +} + +AVLNode* AVLSet::succ(AVLNode* t) +{ + AVLNode* r = t->rt; + if (!rthread(t)) while (!lthread(r)) r = r->lt; + return r; +} + +AVLNode* AVLSet::pred(AVLNode* t) +{ + AVLNode* l = t->lt; + if (!lthread(t)) while (!rthread(l)) l = l->rt; + return l; +} + + +Pix AVLSet::seek( key) +{ + AVLNode* t = root; + if (t == 0) + return 0; + for (;;) + { + int cmp = CMP(key, t->item); + if (cmp == 0) + return Pix(t); + else if (cmp < 0) + { + if (lthread(t)) + return 0; + else + t = t->lt; + } + else if (rthread(t)) + return 0; + else + t = t->rt; + } +} + + +/* + The combination of threads and AVL bits make adding & deleting + interesting, but very awkward. + + We use the following statics to avoid passing them around recursively +*/ + +static int _need_rebalancing; // to send back balance info from rec. calls +static * _target_item; // add/del_item target +static AVLNode* _found_node; // returned added/deleted node +static int _already_found; // for deletion subcases + +static AVLNode** _hold_nodes; // used for rebuilding trees +static int _max_hold_index; // # elements-1 in _hold_nodes + + +void AVLSet:: _add(AVLNode*& t) +{ + int cmp = CMP(*_target_item, t->item); + if (cmp == 0) + { + _found_node = t; + return; + } + else if (cmp < 0) + { + if (lthread(t)) + { + ++count; + _found_node = new AVLNode(*_target_item); + set_lthread(_found_node, 1); + set_rthread(_found_node, 1); + _found_node->lt = t->lt; + _found_node->rt = t; + t->lt = _found_node; + set_lthread(t, 0); + _need_rebalancing = 1; + } + else + _add(t->lt); + if (_need_rebalancing) + { + switch(bf(t)) + { + case AVLRIGHTHEAVY: + set_bf(t, AVLBALANCED); + _need_rebalancing = 0; + return; + case AVLBALANCED: + set_bf(t, AVLLEFTHEAVY); + return; + case AVLLEFTHEAVY: + { + AVLNode* l = t->lt; + if (bf(l) == AVLLEFTHEAVY) + { + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + _need_rebalancing = 0; + } + else + { + AVLNode* r = l->rt; + set_rthread(l, lthread(r)); + if (lthread(r)) + l->rt = r; + else + l->rt = r->lt; + r->lt = l; + set_lthread(r, 0); + set_lthread(t, rthread(r)); + if (rthread(r)) + t->lt = r; + else + t->lt = r->rt; + r->rt = t; + set_rthread(r, 0); + if (bf(r) == AVLLEFTHEAVY) + set_bf(t, AVLRIGHTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(r) == AVLRIGHTHEAVY) + set_bf(l, AVLLEFTHEAVY); + else + set_bf(l, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + _need_rebalancing = 0; + return; + } + } + } + } + } + else + { + if (rthread(t)) + { + ++count; + _found_node = new AVLNode(*_target_item); + set_rthread(t, 0); + set_lthread(_found_node, 1); + set_rthread(_found_node, 1); + _found_node->lt = t; + _found_node->rt = t->rt; + t->rt = _found_node; + _need_rebalancing = 1; + } + else + _add(t->rt); + if (_need_rebalancing) + { + switch(bf(t)) + { + case AVLLEFTHEAVY: + set_bf(t, AVLBALANCED); + _need_rebalancing = 0; + return; + case AVLBALANCED: + set_bf(t, AVLRIGHTHEAVY); + return; + case AVLRIGHTHEAVY: + { + AVLNode* r = t->rt; + if (bf(r) == AVLRIGHTHEAVY) + { + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + _need_rebalancing = 0; + } + else + { + AVLNode* l = r->lt; + set_lthread(r, rthread(l)); + if (rthread(l)) + r->lt = l; + else + r->lt = l->rt; + l->rt = r; + set_rthread(l, 0); + set_rthread(t, lthread(l)); + if (lthread(l)) + t->rt = l; + else + t->rt = l->lt; + l->lt = t; + set_lthread(l, 0); + if (bf(l) == AVLRIGHTHEAVY) + set_bf(t, AVLLEFTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(l) == AVLLEFTHEAVY) + set_bf(r, AVLRIGHTHEAVY); + else + set_bf(r, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + _need_rebalancing = 0; + return; + } + } + } + } + } +} + + +Pix AVLSet::add( item) +{ + if (root == 0) + { + ++count; + root = new AVLNode(item); + set_rthread(root, 1); + set_lthread(root, 1); + return Pix(root); + } + else + { + _target_item = &item; + _need_rebalancing = 0; + _add(root); + return Pix(_found_node); + } +} + + +void AVLSet::_del(AVLNode* par, AVLNode*& t) +{ + int comp; + if (_already_found) + { + if (rthread(t)) + comp = 0; + else + comp = 1; + } + else + comp = CMP(*_target_item, t->item); + if (comp == 0) + { + if (lthread(t) && rthread(t)) + { + _found_node = t; + if (t == par->lt) + { + set_lthread(par, 1); + par->lt = t->lt; + } + else + { + set_rthread(par, 1); + par->rt = t->rt; + } + _need_rebalancing = 1; + return; + } + else if (lthread(t)) + { + _found_node = t; + AVLNode* s = succ(t); + if (s != 0 && lthread(s)) + s->lt = t->lt; + t = t->rt; + _need_rebalancing = 1; + return; + } + else if (rthread(t)) + { + _found_node = t; + AVLNode* p = pred(t); + if (p != 0 && rthread(p)) + p->rt = t->rt; + t = t->lt; + _need_rebalancing = 1; + return; + } + else // replace item & find someone deletable + { + AVLNode* p = pred(t); + t->item = p->item; + _already_found = 1; + comp = -1; // fall through below to left + } + } + + if (comp < 0) + { + if (lthread(t)) + return; + _del(t, t->lt); + if (!_need_rebalancing) + return; + switch (bf(t)) + { + case AVLLEFTHEAVY: + set_bf(t, AVLBALANCED); + return; + case AVLBALANCED: + set_bf(t, AVLRIGHTHEAVY); + _need_rebalancing = 0; + return; + case AVLRIGHTHEAVY: + { + AVLNode* r = t->rt; + switch (bf(r)) + { + case AVLBALANCED: + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLRIGHTHEAVY); + set_bf(r, AVLLEFTHEAVY); + _need_rebalancing = 0; + t = r; + return; + case AVLRIGHTHEAVY: + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + return; + case AVLLEFTHEAVY: + { + AVLNode* l = r->lt; + set_lthread(r, rthread(l)); + if (rthread(l)) + r->lt = l; + else + r->lt = l->rt; + l->rt = r; + set_rthread(l, 0); + set_rthread(t, lthread(l)); + if (lthread(l)) + t->rt = l; + else + t->rt = l->lt; + l->lt = t; + set_lthread(l, 0); + if (bf(l) == AVLRIGHTHEAVY) + set_bf(t, AVLLEFTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(l) == AVLLEFTHEAVY) + set_bf(r, AVLRIGHTHEAVY); + else + set_bf(r, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + return; + } + } + } + } + } + else + { + if (rthread(t)) + return; + _del(t, t->rt); + if (!_need_rebalancing) + return; + switch (bf(t)) + { + case AVLRIGHTHEAVY: + set_bf(t, AVLBALANCED); + return; + case AVLBALANCED: + set_bf(t, AVLLEFTHEAVY); + _need_rebalancing = 0; + return; + case AVLLEFTHEAVY: + { + AVLNode* l = t->lt; + switch (bf(l)) + { + case AVLBALANCED: + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLLEFTHEAVY); + set_bf(l, AVLRIGHTHEAVY); + _need_rebalancing = 0; + t = l; + return; + case AVLLEFTHEAVY: + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + return; + case AVLRIGHTHEAVY: + { + AVLNode* r = l->rt; + set_rthread(l, lthread(r)); + if (lthread(r)) + l->rt = r; + else + l->rt = r->lt; + r->lt = l; + set_lthread(r, 0); + set_lthread(t, rthread(r)); + if (rthread(r)) + t->lt = r; + else + t->lt = r->rt; + r->rt = t; + set_rthread(r, 0); + if (bf(r) == AVLLEFTHEAVY) + set_bf(t, AVLRIGHTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(r) == AVLRIGHTHEAVY) + set_bf(l, AVLLEFTHEAVY); + else + set_bf(l, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + return; + } + } + } + } + } +} + + + +void AVLSet::del( item) +{ + if (root == 0) return; + _need_rebalancing = 0; + _already_found = 0; + _found_node = 0; + _target_item = &item; + _del(root, root); + if (_found_node) + { + delete(_found_node); + if (--count == 0) + root = 0; + } +} + +// build an ordered array of pointers to tree nodes back into a tree +// we know that at least one element exists + +static AVLNode* _do_treeify(int lo, int hi, int& h) +{ + int lh, rh; + int mid = (lo + hi) / 2; + AVLNode* t = _hold_nodes[mid]; + if (lo > mid - 1) + { + set_lthread(t, 1); + if (mid == 0) + t->lt = 0; + else + t->lt = _hold_nodes[mid-1]; + lh = 0; + } + else + { + set_lthread(t, 0); + t->lt = _do_treeify(lo, mid-1, lh); + } + if (hi < mid + 1) + { + set_rthread(t, 1); + if (mid == _max_hold_index) + t->rt = 0; + else + t->rt = _hold_nodes[mid+1]; + rh = 0; + } + else + { + set_rthread(t, 0); + t->rt = _do_treeify(mid+1, hi, rh); + } + if (lh == rh) + { + set_bf(t, AVLBALANCED); + h = lh + 1; + } + else if (lh == rh - 1) + { + set_bf(t, AVLRIGHTHEAVY); + h = rh + 1; + } + else if (rh == lh - 1) + { + set_bf(t, AVLLEFTHEAVY); + h = lh + 1; + } + else // can't happen + abort(); + + return t; +} + +static AVLNode* _treeify(int n) +{ + AVLNode* t; + if (n == 0) + t = 0; + else + { + int b; + _max_hold_index = n-1; + t = _do_treeify(0, _max_hold_index, b); + } + delete _hold_nodes; + return t; +} + + +void AVLSet::_kill(AVLNode* t) +{ + if (t != 0) + { + if (!lthread(t)) _kill(t->lt); + if (!rthread(t)) _kill(t->rt); + delete t; + } +} + + +AVLSet::AVLSet(AVLSet& b) +{ + if ((count = b.count) == 0) + { + root = 0; + } + else + { + _hold_nodes = new AVLNodePtr [count]; + AVLNode* t = b.leftmost(); + int i = 0; + while (t != 0) + { + _hold_nodes[i++] = new AVLNode(t->item); + t = b.succ(t); + } + root = _treeify(count); + } +} + + +int AVLSet::operator == (AVLSet& y) +{ + if (count != y.count) + return 0; + else + { + AVLNode* t = leftmost(); + AVLNode* u = y.leftmost(); + for (;;) + { + if (t == 0) + return 1; + else if (!(EQ(t->item, u->item))) + return 0; + else + { + t = succ(t); + u = y.succ(u); + } + } + } +} + +int AVLSet::operator <= (AVLSet& y) +{ + if (count > y.count) + return 0; + else + { + AVLNode* t = leftmost(); + AVLNode* u = y.leftmost(); + for (;;) + { + if (t == 0) + return 1; + else if (u == 0) + return 0; + int cmp = CMP(t->item, u->item); + if (cmp == 0) + { + t = succ(t); + u = y.succ(u); + } + else if (cmp < 0) + return 0; + else + u = y.succ(u); + } + } +} + +void AVLSet::operator |=(AVLSet& y) +{ + AVLNode* t = leftmost(); + AVLNode* u = y.leftmost(); + int rsize = count + y.count; + _hold_nodes = new AVLNodePtr [rsize]; + int k = 0; + for (;;) + { + if (t == 0) + { + while (u != 0) + { + _hold_nodes[k++] = new AVLNode(u->item); + u = y.succ(u); + } + break; + } + else if (u == 0) + { + while (t != 0) + { + _hold_nodes[k++] = t; + t = succ(t); + } + break; + } + int cmp = CMP(t->item, u->item); + if (cmp == 0) + { + _hold_nodes[k++] = t; + t = succ(t); + u = y.succ(u); + } + else if (cmp < 0) + { + _hold_nodes[k++] = t; + t = succ(t); + } + else + { + _hold_nodes[k++] = new AVLNode(u->item); + u = y.succ(u); + } + } + root = _treeify(k); + count = k; +} + +void AVLSet::operator &= (AVLSet& y) +{ + AVLNode* t = leftmost(); + AVLNode* u = y.leftmost(); + int rsize = (count < y.count)? count : y.count; + _hold_nodes = new AVLNodePtr [rsize]; + int k = 0; + for (;;) + { + if (t == 0) + break; + if (u == 0) + { + while (t != 0) + { + AVLNode* tmp = succ(t); + delete t; + t = tmp; + } + break; + } + int cmp = CMP(t->item, u->item); + if (cmp == 0) + { + _hold_nodes[k++] = t; + t = succ(t); + u = y.succ(u); + } + else if (cmp < 0) + { + AVLNode* tmp = succ(t); + delete t; + t = tmp; + } + else + u = y.succ(u); + } + root = _treeify(k); + count = k; +} + + +void AVLSet::operator -=(AVLSet& y) +{ + AVLNode* t = leftmost(); + AVLNode* u = y.leftmost(); + int rsize = count; + _hold_nodes = new AVLNodePtr [rsize]; + int k = 0; + for (;;) + { + if (t == 0) + break; + else if (u == 0) + { + while (t != 0) + { + _hold_nodes[k++] = t; + t = succ(t); + } + break; + } + int cmp = CMP(t->item, u->item); + if (cmp == 0) + { + AVLNode* tmp = succ(t); + delete t; + t = tmp; + u = y.succ(u); + } + else if (cmp < 0) + { + _hold_nodes[k++] = t; + t = succ(t); + } + else + u = y.succ(u); + } + root = _treeify(k); + count = k; +} + +int AVLSet::owns(Pix i) +{ + if (i == 0) return 0; + for (AVLNode* t = leftmost(); t != 0; t = succ(t)) + if (Pix(t) == i) return 1; + return 0; +} + +int AVLSet::OK() +{ + int v = 1; + if (root == 0) + v = count == 0; + else + { + int n = 1; + AVLNode* trail = leftmost(); + AVLNode* t = succ(trail); + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/AVLSet.hP b/gnu/lib/libg++/g++-include/gen/AVLSet.hP new file mode 100644 index 0000000000..16ad1d1946 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/AVLSet.hP @@ -0,0 +1,152 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _AVL_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _AVL_h 1 + +#include ".Set.h" + +struct AVLNode +{ + AVLNode* lt; + AVLNode* rt; + item; + char stat; + AVLNode( h, AVLNode* l=0, AVLNode* r=0); + ~AVLNode(); +}; + +inline AVLNode::AVLNode( h, AVLNode* l, AVLNode* r) +:item(h), lt(l), rt(r), stat(0) {} + +inline AVLNode::~AVLNode() {} + +typedef AVLNode* AVLNodePtr; + + +class AVLSet : public Set +{ +protected: + AVLNode* root; + + AVLSet(AVLNode* p, int l); + + AVLNode* leftmost(); + AVLNode* rightmost(); + AVLNode* pred(AVLNode* t); + AVLNode* succ(AVLNode* t); + void _kill(AVLNode* t); + void _add(AVLNode*& t); + void _del(AVLNode* p, AVLNode*& t); + +public: + AVLSet(); + AVLSet(AVLSet& a); + ~AVLSet(); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item); + + Pix last(); + void prev(Pix& i); + + void operator |= (AVLSet& b); + void operator -= (AVLSet& b); + void operator &= (AVLSet& b); + + int operator == (AVLSet& b); + int operator != (AVLSet& b); + int operator <= (AVLSet& b); + + int OK(); +}; + +inline AVLSet::~AVLSet() +{ + _kill(root); +} + +inline AVLSet::AVLSet() +{ + root = 0; + count = 0; +} + +inline AVLSet::AVLSet(AVLNode* p, int l) +{ + root = p; + count = l; +} + +inline int AVLSet::operator != (AVLSet& b) +{ + return ! ((*this) == b); +} + +inline Pix AVLSet::first() +{ + return Pix(leftmost()); +} + +inline Pix AVLSet::last() +{ + return Pix(rightmost()); +} + +inline void AVLSet::next(Pix& i) +{ + if (i != 0) i = Pix(succ((AVLNode*)i)); +} + +inline void AVLSet::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((AVLNode*)i)); +} + +inline & AVLSet::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((AVLNode*)i)->item; +} + +inline void AVLSet::clear() +{ + _kill(root); + count = 0; + root = 0; +} + +inline int AVLSet::contains( key) +{ + return seek(key) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/AVec.ccP b/gnu/lib/libg++/g++-include/gen/AVec.ccP new file mode 100644 index 0000000000..bc671bf8e1 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/AVec.ccP @@ -0,0 +1,397 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include +#include ".AVec.h" + +/* + The following brought to you by the department of redundancy department +*/ + +AVec& AVec::operator = (AVec& v) +{ + if (len != 0 && len != v.capacity()) + error("nonconformant vectors."); + if (len == 0) + s = new [len = v.capacity()]; + if (s != v.vec()) + { + for (int i = 0; i < len; ++i) + s[i] = v.vec()[i]; + } + return *this; +} + +AVec& AVec::operator = ( f) +{ + for (int i = 0; i < len; ++i) s[i] = f; + return *this; +} + + +AVec concat(AVec & a, AVec & b) +{ + int newl = a.capacity() + b.capacity(); + * news = new [newl]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + while (t < top) *p++ = *t++; + top = &(b.vec()[b.capacity()]); + t = b.vec(); + while (t < top) *p++ = *t++; + return AVec(newl, news); +} + + +AVec combine(Combiner f, AVec& a, AVec& b) +{ + int newl = (a.capacity() < b.capacity())? a.capacity() : b.capacity(); + * news = new [newl]; + * p = news; + * top = &(a.vec()[newl]); + * t = a.vec(); + * u = b.vec(); + while (t < top) *p++ = (*f)(*t++, *u++); + return AVec(newl, news); +} + +AVec reverse(AVec& a) +{ + * news = new [a.capacity()]; + if (a.capacity() != 0) + { + * lo = news; + * hi = &(news[a.capacity() - 1]); + while (lo < hi) + { + tmp = *lo; + *lo++ = *hi; + *hi-- = tmp; + } + } + return AVec(a.capacity(), news); +} + +AVec map(Mapper f, AVec& a) +{ + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + while(t < top) *p++ = (*f)(*t++); + return AVec(a.capacity(), news); +} + +AVec AVec::at(int from, int n) +{ + int to; + if (n < 0) + { + n = len - from; + to = len - 1; + } + else + to = from + n - 1; + if ((unsigned)from > (unsigned)to) + range_error(); + * news = new [n]; + * p = news; + * t = &(s[from]); + * top = &(s[to]); + while (t <= top) *p++ = *t++; + return AVec(n, news); +} + +AVec merge(AVec & a, AVec & b, Comparator f) +{ + int newl = a.capacity() + b.capacity(); + * news = new [newl]; + * p = news; + * topa = &(a.vec()[a.capacity()]); + * as = a.vec(); + * topb = &(b.vec()[b.capacity()]); + * bs = b.vec(); + + for (;;) + { + if (as >= topa) + { + while (bs < topb) *p++ = *bs++; + break; + } + else if (bs >= topb) + { + while (as < topa) *p++ = *as++; + break; + } + else if ((*f)(*as, *bs) <= 0) + *p++ = *as++; + else + *p++ = *bs++; + } + return AVec(newl, news); +} + +AVec operator + (AVec& a, AVec& b) +{ + a.check_len(b.capacity()); + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + * u = b.vec(); + while (t < top) *p++ = *t++ + *u++; + return AVec(a.capacity(), news); +} + +AVec operator - (AVec& a, AVec& b) +{ + a.check_len(b.capacity()); + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + * u = b.vec(); + while (t < top) *p++ = *t++ - *u++; + return AVec(a.capacity(), news); +} + +AVec product (AVec& a, AVec& b) +{ + a.check_len(b.capacity()); + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + * u = b.vec(); + while (t < top) *p++ = *t++ * *u++; + return AVec(a.capacity(), news); +} + +AVec quotient(AVec& a, AVec& b) +{ + a.check_len(b.capacity()); + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + * u = b.vec(); + while (t < top) *p++ = *t++ / *u++; + return AVec(a.capacity(), news); +} + +AVec operator + (AVec& a, b) +{ + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + while (t < top) *p++ = *t++ + b; + return AVec(a.capacity(), news); +} + +AVec operator - (AVec& a, b) +{ + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + while (t < top) *p++ = *t++ - b; + return AVec(a.capacity(), news); +} + +AVec operator * (AVec& a, b) +{ + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + while (t < top) *p++ = *t++ * b; + return AVec(a.capacity(), news); +} + +AVec operator / (AVec& a, b) +{ + * news = new [a.capacity()]; + * p = news; + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + while (t < top) *p++ = *t++ / b; + return AVec(a.capacity(), news); +} + +AVec AVec::operator - () +{ + * news = new [len]; + * p = news; + * top = &(s[len]); + * t = s; + while (t < top) *p++ = -(*t++); + return AVec(len, news); +} + +AVec& AVec::operator += (AVec& b) +{ + check_len(b.capacity()); + * u = b.vec(); + * top = &(s[len]); + * t = s; + while (t < top) *t++ += *u++; + return *this; +} + +AVec& AVec::operator -= (AVec& b) +{ + check_len(b.capacity()); + * u = b.vec(); + * top = &(s[len]); + * t = s; + while (t < top) *t++ -= *u++; + return *this; +} + +AVec& AVec::product(AVec& b) +{ + check_len(b.capacity()); + * u = b.vec(); + * top = &(s[len]); + * t = s; + while (t < top) *t++ *= *u++; + return *this; +} + +AVec& AVec::quotient(AVec& b) +{ + check_len(b.capacity()); + * u = b.vec(); + * top = &(s[len]); + * t = s; + while (t < top) *t++ /= *u++; + return *this; +} + +AVec& AVec::operator += ( b) +{ + * top = &(s[len]); + * t = s; + while (t < top) *t++ += b; + return *this; +} + +AVec& AVec::operator -= ( b) +{ + * top = &(s[len]); + * t = s; + while (t < top) *t++ -= b; + return *this; +} + +AVec& AVec::operator *= ( b) +{ + * top = &(s[len]); + * t = s; + while (t < top) *t++ *= b; + return *this; +} + +AVec& AVec::operator /= ( b) +{ + * top = &(s[len]); + * t = s; + while (t < top) *t++ /= b; + return *this; +} + + AVec::max() +{ + if (len == 0) + return 0; + * top = &(s[len]); + * t = s; + res = *t++; + for (; t < top; ++t) if (*t > res) res = *t; + return res; +} + +int AVec::max_index() +{ + if (len == 0) + return -1; + int ind = 0; + for (int i = 1; i < len; ++i) + if (s[i] > s[ind]) + ind = i; + return ind; +} + + AVec::min() +{ + if (len == 0) + return 0; + * top = &(s[len]); + * t = s; + res = *t++; + for (; t < top; ++t) if (*t < res) res = *t; + return res; +} + +int AVec::min_index() +{ + if (len == 0) + return -1; + int ind = 0; + for (int i = 1; i < len; ++i) + if (s[i] < s[ind]) + ind = i; + return ind; +} + + AVec::sum() +{ + res = 0; + * top = &(s[len]); + * t = s; + while (t < top) res += *t++; + return res; +} + + + AVec::sumsq() +{ + res = 0; + * top = &(s[len]); + * t = s; + for (; t < top; ++t) res += *t * *t; + return res; +} + + operator * (AVec& a, AVec& b) +{ + a.check_len(b.capacity()); + * top = &(a.vec()[a.capacity()]); + * t = a.vec(); + * u = b.vec(); + res = 0; + while (t < top) res += *t++ * *u++; + return res; +} diff --git a/gnu/lib/libg++/g++-include/gen/AVec.hP b/gnu/lib/libg++/g++-include/gen/AVec.hP new file mode 100644 index 0000000000..cd9a9c3fe5 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/AVec.hP @@ -0,0 +1,113 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _AVec_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _AVec_h 1 + +#include ".Vec.h" + +class AVec : public Vec +{ +protected: + void check_len(int l); + * vec(); + AVec(int l, * d); + public: + AVec (); + AVec (int l); + AVec (int l, fill_value); + AVec (AVec&); + ~AVec (); + + AVec& operator = (AVec& a); + AVec& operator = ( fill_value); + +// vector by scalar -> vector operations + + friend AVec operator + (AVec& a, b); + friend AVec operator - (AVec& a, b); + friend AVec operator * (AVec& a, b); + friend AVec operator / (AVec& a, b); + + AVec& operator += ( b); + AVec& operator -= ( b); + AVec& operator *= ( b); + AVec& operator /= ( b); + +// vector by vector -> vector operations + + friend AVec operator + (AVec& a, AVec& b); + friend AVec operator - (AVec& a, AVec& b); + AVec& operator += (AVec& b); + AVec& operator -= (AVec& b); + + AVec operator - (); + + friend AVec product(AVec& a, AVec& b); + AVec& product(AVec& b); + friend AVec quotient(AVec& a, AVec& b); + AVec& quotient(AVec& b); + +// vector -> scalar operations + + friend operator * (AVec& a, AVec& b); + + sum(); + min(); + max(); + sumsq(); + +// indexing + + int min_index(); + int max_index(); + +// redundant but necesssary + friend AVec concat(AVec& a, AVec& b); + friend AVec map(Mapper f, AVec& a); + friend AVec merge(AVec& a, AVec& b, Comparator f); + friend AVec combine(Combiner f, AVec& a, AVec& b); + friend AVec reverse(AVec& a); + AVec at(int from = 0, int n = -1); +}; + +inline AVec::AVec() {} +inline AVec::AVec(int l) :Vec(l) {} +inline AVec::AVec(int l, fill_value) : Vec (l, fill_value) {} +inline AVec::AVec(AVec& v) :Vec(v) {} +inline AVec::AVec(int l, * d) :Vec(l, d) {} +inline AVec::~AVec() {} + + +inline * AVec::vec() +{ + return s; +} + + +inline void AVec::check_len(int l) +{ + if (l != len) + error("nonconformant vectors."); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/BSTSet.ccP b/gnu/lib/libg++/g++-include/gen/BSTSet.ccP new file mode 100644 index 0000000000..6a69d8f45b --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/BSTSet.ccP @@ -0,0 +1,378 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".BSTSet.h" + + +/* + traversal primitives +*/ + + +BSTNode* BSTSet::leftmost() +{ + BSTNode* t = root; + if (t != 0) while (t->lt != 0) t = t->lt; + return t; +} + +BSTNode* BSTSet::rightmost() +{ + BSTNode* t = root; + if (t != 0) while (t->rt != 0) t = t->rt; + return t; +} + +BSTNode* BSTSet::succ(BSTNode* t) +{ + if (t == 0) + return 0; + if (t->rt != 0) + { + t = t->rt; + while (t->lt != 0) t = t->lt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->lt) + return t->par; + else + t = t->par; + } + } +} + +BSTNode* BSTSet::pred(BSTNode* t) +{ + if (t == 0) + return 0; + else if (t->lt != 0) + { + t = t->lt; + while (t->rt != 0) t = t->rt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->rt) + return t->par; + else + t = t->par; + } + } +} + + +Pix BSTSet::seek( key) +{ + BSTNode* t = root; + for (;;) + { + if (t == 0) + return 0; + int comp = CMP(key, t->item); + if (comp == 0) + return Pix(t); + else if (comp < 0) + t = t->lt; + else + t = t->rt; + } +} + + +Pix BSTSet::add( item) +{ + if (root == 0) + { + ++count; + root = new BSTNode(item); + return Pix(root); + } + + BSTNode* t = root; + BSTNode* p = root; + int comp; + for (;;) + { + if (t == 0) + { + ++count; + t = new BSTNode(item); + if (comp > 0) + p->lt = t; + else + p->rt = t; + t->par = p; + return Pix(t); + } + p = t; + comp = CMP(t->item, item); + if (comp == 0) + return Pix(t); + else if (comp > 0) + t = t->lt; + else + t = t->rt; + } +} + + +void BSTSet::del( key) +{ + BSTNode* t = root; + BSTNode* p = root; + int comp; + for (;;) + { + if (t == 0) + return; + comp = CMP(key, t->item); + if (comp == 0) + { + --count; + BSTNode* repl; + if (t->lt == 0) + repl = t->rt; + else if (t->rt == 0) + repl = t->lt; + else + { + BSTNode* prepl = t; + repl = t->lt; + while (repl->rt != 0) + { + prepl = repl; + repl = repl->rt; + } + if (prepl != t) + { + prepl->rt = repl->lt; + if (prepl->rt != 0) prepl->rt->par = prepl; + repl->lt = t->lt; + if (repl->lt != 0) repl->lt->par = repl; + } + repl->rt = t->rt; + if (repl->rt != 0) repl->rt->par = repl; + } + if (t == root) + { + root = repl; + if (repl != 0) repl->par = 0; + } + else + { + if (t == p->lt) + p->lt = repl; + else + p->rt = repl; + if (repl != 0) repl->par = p; + } + delete t; + return; + } + p = t; + if (comp < 0) + t = t->lt; + else + t = t->rt; + } +} + + +void BSTSet::_kill(BSTNode* t) +{ + if (t != 0) + { + _kill(t->lt); + _kill(t->rt); + delete t; + } +} + +BSTNode* BSTSet::_copy(BSTNode* t) +{ + if (t == 0) + return 0; + else + { + BSTNode* u = new BSTNode(t->item, _copy(t->lt), _copy(t->rt)); + if (u->lt != 0) u->lt->par = u; + if (u->rt != 0) u->rt->par = u; + return u; + } +} + + +int BSTSet::operator == (BSTSet& y) +{ + if (count != y.count) + return 0; + else + { + BSTNode* t = leftmost(); + BSTNode* u = y.leftmost(); + for (;;) + { + if (t == 0) + return 1; + else if (!EQ(t->item, u->item)) + return 0; + else + { + t = succ(t); + u = y.succ(u); + } + } + } +} + +int BSTSet::operator <= (BSTSet& y) +{ + if (count > y.count) + return 0; + else + { + BSTNode* t = leftmost(); + BSTNode* u = y.leftmost(); + for (;;) + { + if (t == 0) + return 1; + else if (u == 0) + return 0; + int cmp = CMP(t->item, u->item); + if (cmp == 0) + { + t = succ(t); + u = y.succ(u); + } + else if (cmp < 0) + return 0; + else + u = y.succ(u); + } + } +} + + +// linear-time, zero space overhead binary tree rebalancing from +// Stout & Warren, ``Tree rebalancing in linear space and time'' +// CACM, Sept, 1986, p902. + + +void BSTSet::balance() +{ + if (count <= 2) return; // don't bother -- + // also we assume non-null root, below + + // make re-attaching the root easy via trickery + + struct _fake_node { _fake_node *lt, *rt, *par; } fake_root; + + fake_root.rt = (_fake_node*)root; + fake_root.par = 0; + BSTNode* pseudo_root = (BSTNode*)&fake_root; + + // phase 1: tree-to-vine + + BSTNode* vine_tail = pseudo_root; + BSTNode* remainder = root; + + while (remainder != 0) + { + if (remainder->lt == 0) + { + vine_tail = remainder; + remainder = remainder->rt; + } + else + { + BSTNode* tmp = remainder->lt; + remainder->lt = tmp->rt; + if (remainder->lt != 0) remainder->lt->par = remainder; + tmp->rt = remainder; + remainder->par = tmp; + vine_tail->rt = remainder = tmp; + } + } + + // phase 2: vine-to-tree + + // Uses the slightly simpler version adapted from + // Day ``Balancing a binary tree'' Computer Journal, Nov. 1976, + // since it's not generally important whether the `stray' leaves are + // on the left or on the right. + + unsigned int spines = count - 1; + while (spines > 1) + { + int compressions = spines >> 1; // compress every other node + spines -= compressions + 1; // halve for next time + + BSTNode* scanner = pseudo_root; + while (compressions-- > 0) + { + BSTNode* child = scanner->rt; + BSTNode* grandchild = child->rt; + scanner->rt = grandchild; + grandchild->par = scanner; + child->rt = grandchild->lt; + if (child->rt != 0) child->rt->par = child; + grandchild->lt = child; + child->par = grandchild; + scanner = grandchild; + } + } + + root = pseudo_root->rt; + root->par = 0; +} + + +int BSTSet::OK() +{ + int v = 1; + if (root == 0) + v = count == 0; + else + { + int n = 1; + BSTNode* trail = leftmost(); + BSTNode* t = succ(trail); + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/BSTSet.hP b/gnu/lib/libg++/g++-include/gen/BSTSet.hP new file mode 100644 index 0000000000..82f2069c0f --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/BSTSet.hP @@ -0,0 +1,152 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _BSTSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _BSTSet_h 1 + +#include ".Set.h" + +#ifndef _BSTNode +#define _BSTNode 1 + +struct BSTNode +{ + BSTNode* lt; + BSTNode* rt; + BSTNode* par; + item; + BSTNode( h, BSTNode* l=0, BSTNode* r=0, + BSTNode* p = 0); + ~BSTNode(); +}; + +inline BSTNode::BSTNode( h, BSTNode* l, BSTNode* r, + BSTNode* p) +:item(h), lt(l), rt(r), par(p) {} + +inline BSTNode::~BSTNode() {} + +typedef BSTNode* BSTNodePtr; + +#endif + +class BSTSet : public Set +{ +protected: + BSTNode* root; + + BSTNode* leftmost(); + BSTNode* rightmost(); + BSTNode* pred(BSTNode* t); + BSTNode* succ(BSTNode* t); + void _kill(BSTNode* t); + BSTNode* _copy(BSTNode* t); + +public: + BSTSet(); + BSTSet(BSTSet& a); + ~BSTSet(); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + Pix seek( item); + + Pix last(); + void prev(Pix& i); + + int operator == (BSTSet& b); + int operator != (BSTSet& b); + int operator <= (BSTSet& b); + + void balance(); + int OK(); +}; + +inline BSTSet::~BSTSet() +{ + _kill(root); +} + +inline BSTSet::BSTSet() +{ + root = 0; + count = 0; +} + + +inline BSTSet::BSTSet(BSTSet& a) +{ + count = a.count; + root = _copy(a.root); +} + +inline int BSTSet::operator != (BSTSet& b) +{ + return ! (*this == b); +} + +inline Pix BSTSet::first() +{ + return Pix(leftmost()); +} + +inline Pix BSTSet::last() +{ + return Pix(rightmost()); +} + +inline void BSTSet::next(Pix& i) +{ + if (i != 0) i = Pix(succ((BSTNode*)i)); +} + +inline void BSTSet::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((BSTNode*)i)); +} + +inline & BSTSet::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((BSTNode*)i)->item; +} + +inline void BSTSet::clear() +{ + _kill(root); + count = 0; + root = 0; +} + +inline int BSTSet::contains( key) +{ + return seek(key) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/Bag.ccP b/gnu/lib/libg++/g++-include/gen/Bag.ccP new file mode 100644 index 0000000000..836d0d6656 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/Bag.ccP @@ -0,0 +1,74 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".Bag.h" + +// error handling + +void Bag::error(const char* msg) +{ + (*lib_error_handler)("Bag", msg); +} + + +Pix Bag::seek( item, Pix i) +{ + if (i == 0) + i = first(); + else + next(i); + for (;i != 0 && (!(EQ((*this)(i), item))); next(i)); + return i; +} + +int Bag::owns(Pix p) +{ + if (p == 0) return 0; + for (Pix i = first(); i != 0; next(i)) if (i == p) return 1; + return 0; +} + +void Bag::remove( item) +{ + int i = nof(item); + while (i-- > 0) del(item); +} + + +int Bag::nof( item) +{ + int n = 0; + for (Pix p = first(); p; next(p)) if (EQ((*this)(p), item)) ++n; + return n; +} + +void Bag::clear() +{ + Pix i = first(); + while (i != 0) + { + del((*this)(i)); + i = first(); + } +} + + diff --git a/gnu/lib/libg++/g++-include/gen/Bag.hP b/gnu/lib/libg++/g++-include/gen/Bag.hP new file mode 100644 index 0000000000..4b9a87a909 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/Bag.hP @@ -0,0 +1,79 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _Bag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Bag_h 1 + +#include +#include ".defs.h" + +class Bag +{ +protected: + int count; + +public: + virtual ~Bag(); + + int length(); // current number of items + int empty(); + + virtual Pix add( item) = 0; // add item; return Pix + + virtual void del( item) = 0; // delete 1 occurrence of item +#undef remove + virtual void remove( item); // delete all occurrences + virtual void clear(); // delete all items + + virtual int contains( item); // is item in Bag? + virtual int nof( item); // how many in Bag? + + virtual Pix first() = 0; // Pix of first item or 0 + virtual void next(Pix& i) = 0; // advance to next or 0 + + virtual & operator () (Pix i) = 0; // access item at i + + virtual Pix seek( item, Pix from=0); // Pix of next occurrence + virtual int owns(Pix i); // is i a valid Pix ? + + void error(const char* msg); + virtual int OK() = 0; // rep invariant +}; + +inline Bag::~Bag() {} + +inline int Bag::length() +{ + return count; +} + +inline int Bag::empty() +{ + return count == 0; +} + +inline int Bag::contains( item) +{ + return seek(item) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/CHBag.ccP b/gnu/lib/libg++/g++-include/gen/CHBag.ccP new file mode 100644 index 0000000000..16649ac22f --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/CHBag.ccP @@ -0,0 +1,209 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".CHBag.h" + +// The nodes are linked together serially via a version +// of a trick used in some vtables: odd pointers are +// actually links to the next table entry. +// Not terrible, but not wonderful either + +static inline int goodCHptr(CHNode* t) +{ + return ((((unsigned)t) & 1) == 0); +} + +static inline CHNode* index_to_CHptr(int i) +{ + return (CHNode*)((i << 1) + 1); +} + +static inline int CHptr_to_index(CHNode* t) +{ + return ( ((unsigned) t) >> 1); +} + +CHBag::CHBag(unsigned int sz) +{ + tab = (CHNode**)(new CHNodePtr[size = sz]); + for (unsigned int i = 0; i < size; ++i) tab[i] = index_to_CHptr(i+1); + count = 0; +} + +CHBag::CHBag(CHBag& a) +{ + tab = (CHNode**)(new CHNodePtr[size = a.size]); + for (unsigned int i = 0; i < size; ++i) tab[i] = index_to_CHptr(i+1); + count = 0; + for (Pix p = a.first(); p; a.next(p)) add(a(p)); +} + + +Pix CHBag::seek( key, Pix i) +{ + CHNode* p = (CHNode*)i; + if (p == 0 || !EQ(p->hd, key)) + { + unsigned int h = HASH(key) % size; + for (CHNode* t = tab[h]; goodCHptr(t); t = t->tl) + if (EQ(key, t->hd)) + return Pix(t); + } + else + { + for (p = p->tl; goodCHptr(p); p = p->tl) + if (EQ(p->hd, key)) + return Pix(p); + } + return 0; +} + +int CHBag::nof( key) +{ + int n = 0; + unsigned int h = HASH(key) % size; + for (CHNode* t = tab[h]; goodCHptr(t); t = t->tl) + if (EQ(key, t->hd)) ++n; + return n; +} + + +Pix CHBag::add( item) +{ + unsigned int h = HASH(item) % size; + CHNode* t = new CHNode(item); + t->tl = tab[h]; + tab[h] = t; + ++count; + return Pix(t); +} + +void CHBag::del( key) +{ + unsigned int h = HASH(key) % size; + + CHNode* t = tab[h]; + CHNode* trail = t; + while (goodCHptr(t)) + { + if (EQ(key, t->hd)) + { + if (trail == t) + tab[h] = t->tl; + else + trail->tl = t->tl; + delete t; + --count; + return; + } + trail = t; + t = t->tl; + } +} + +void CHBag::remove( key) +{ + unsigned int h = HASH(key) % size; + + CHNode* t = tab[h]; + CHNode* trail = t; + while (goodCHptr(t)) + { + if (EQ(key, t->hd)) + { + --count; + if (trail == t) + { + tab[h] = t->tl; + delete t; + t = trail = tab[h]; + } + else + { + trail->tl = t->tl; + delete t; + t = trail->tl; + } + } + else + { + trail = t; + t = t->tl; + } + } +} + + +void CHBag::clear() +{ + for (unsigned int i = 0; i < size; ++i) + { + CHNode* p = tab[i]; + tab[i] = index_to_CHptr(i+1); + while (goodCHptr(p)) + { + CHNode* nxt = p->tl; + delete(p); + p = nxt; + } + } + count = 0; +} + +Pix CHBag::first() +{ + for (unsigned int i = 0; i < size; ++i) if (goodCHptr(tab[i])) return Pix(tab[i]); + return 0; +} + +void CHBag::next(Pix& p) +{ + if (p == 0) return; + CHNode* t = ((CHNode*)p)->tl; + if (goodCHptr(t)) + p = Pix(t); + else + { + for (unsigned int i = CHptr_to_index(t); i < size; ++i) + { + if (goodCHptr(tab[i])) + { + p = Pix(tab[i]); + return; + } + } + p = 0; + } +} + +int CHBag::OK() +{ + int v = tab != 0; + int n = 0; + for (unsigned int i = 0; i < size; ++i) + { + for (CHNode* p = tab[i]; goodCHptr(p); p = p->tl) ++n; + v &= CHptr_to_index(p) == i + 1; + } + v &= count == n; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/CHBag.hP b/gnu/lib/libg++/g++-include/gen/CHBag.hP new file mode 100644 index 0000000000..f6ca10b3b9 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/CHBag.hP @@ -0,0 +1,76 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _CHBag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _CHBag_h 1 + +#include ".Bag.h" + + +#include ".CHNode.h" + +class CHBag : public Bag +{ +protected: + CHNode** tab; + unsigned int size; + +public: + CHBag(unsigned int sz = DEFAULT_INITIAL_CAPACITY); + CHBag(CHBag& a); + ~CHBag(); + + Pix add( item); + void del( item); + void remove(item); + int nof( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + Pix seek( item, Pix from = 0); + + int OK(); +}; + + +inline CHBag::~CHBag() +{ + clear(); + delete tab; +} + +inline int CHBag::contains( key) +{ + return seek(key) != 0; +} + +inline & CHBag::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((CHNode*)i)->hd; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/CHMap.ccP b/gnu/lib/libg++/g++-include/gen/CHMap.ccP new file mode 100644 index 0000000000..1bd76db5fc --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/CHMap.ccP @@ -0,0 +1,166 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include "..CHMap.h" + +// The nodes are linked together serially via a version +// of a trick used in some vtables: odd pointers are +// actually links to the next table entry. +// Not terrible, but not wonderful either + +static inline int goodCHptr(CHNode* t) +{ + return ((((unsigned)t) & 1) == 0); +} + +static inline CHNode* index_to_CHptr(int i) +{ + return (CHNode*)((i << 1) + 1); +} + +static inline int CHptr_to_index(CHNode* t) +{ + return ( ((unsigned) t) >> 1); +} + +CHMap::CHMap( dflt, unsigned int sz) + :Map(dflt) +{ + tab = (CHNode**)(new CHNodePtr[size = sz]); + for (unsigned int i = 0; i < size; ++i) tab[i] = index_to_CHptr(i+1); + count = 0; +} + +CHMap::CHMap(CHMap& a) :Map(a.def) +{ + tab = (CHNode**)(new CHNodePtr[size = a.size]); + for (unsigned int i = 0; i < size; ++i) tab[i] = index_to_CHptr(i+1); + count = 0; + for (Pix p = a.first(); p; a.next(p)) (*this)[a.key(p)] = a.contents(p); +} + + +Pix CHMap::seek( key) +{ + unsigned int h = HASH(key) % size; + + for (CHNode* t = tab[h]; goodCHptr(t); t = t->tl) + if (EQ(key, t->hd)) + return Pix(t); + + return 0; +} + + +& CHMap::operator []( item) +{ + unsigned int h = HASH(item) % size; + + for (CHNode* t = tab[h]; goodCHptr(t); t = t->tl) + if (EQ(item, t->hd)) + return t->cont; + + t = new CHNode(item, def, tab[h]); + tab[h] = t; + ++count; + return t->cont; +} + + +void CHMap::del( key) +{ + unsigned int h = HASH(key) % size; + + CHNode* t = tab[h]; + CHNode* trail = t; + while (goodCHptr(t)) + { + if (EQ(key, t->hd)) + { + if (trail == t) + tab[h] = t->tl; + else + trail->tl = t->tl; + delete t; + --count; + return; + } + trail = t; + t = t->tl; + } +} + + +void CHMap::clear() +{ + for (unsigned int i = 0; i < size; ++i) + { + CHNode* p = tab[i]; + tab[i] = index_to_CHptr(i+1); + while (goodCHptr(p)) + { + CHNode* nxt = p->tl; + delete(p); + p = nxt; + } + } + count = 0; +} + +Pix CHMap::first() +{ + for (unsigned int i = 0; i < size; ++i) if (goodCHptr(tab[i])) return Pix(tab[i]); + return 0; +} + +void CHMap::next(Pix& p) +{ + CHNode* t = ((CHNode*)p)->tl; + if (goodCHptr(t)) + p = Pix(t); + else + { + for (unsigned int i = CHptr_to_index(t); i < size; ++i) + { + if (goodCHptr(tab[i])) + { + p = Pix(tab[i]); + return; + } + } + p = 0; + } +} + + +int CHMap::OK() +{ + int v = tab != 0; + int n = 0; + for (unsigned int i = 0; i < size; ++i) + { + for (CHNode* p = tab[i]; goodCHptr(p); p = p->tl) ++n; + v &= CHptr_to_index(p) == i + 1; + } + v &= count == n; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/CHMap.hP b/gnu/lib/libg++/g++-include/gen/CHMap.hP new file mode 100644 index 0000000000..95441a3544 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/CHMap.hP @@ -0,0 +1,104 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _CHMap_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _CHMap_h 1 + +#include "..Map.h" + +#ifndef _CHNode_h +#define _CHNode_h 1 + +struct CHNode +{ + CHNode* tl; + hd; + cont; + CHNode(); + CHNode( h, c, CHNode* t = 0); + ~CHNode(); +}; + +inline CHNode::CHNode() {} + +inline CHNode::CHNode( h, c, CHNode* t) + : hd(h), cont(c), tl(t) {} + +inline CHNode::~CHNode() {} + +typedef CHNode* CHNodePtr; + +#endif + + +class CHMap : public Map +{ +protected: + CHNode** tab; + unsigned int size; + +public: + CHMap( dflt,unsigned int sz=DEFAULT_INITIAL_CAPACITY); + CHMap(CHMap& a); + ~CHMap(); + + & operator [] ( key); + + void del( key); + + Pix first(); + void next(Pix& i); + & key(Pix i); + & contents(Pix i); + + Pix seek( key); + int contains( key); + + void clear(); + int OK(); +}; + + +inline CHMap::~CHMap() +{ + clear(); + delete tab; +} + +inline int CHMap::contains( key) +{ + return seek(key) != 0; +} + +inline & CHMap::key(Pix p) +{ + if (p == 0) error("null Pix"); + return ((CHNode*)p)->hd; +} + +inline & CHMap::contents(Pix p) +{ + if (p == 0) error("null Pix"); + return ((CHNode*)p)->cont; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/CHNode.ccP b/gnu/lib/libg++/g++-include/gen/CHNode.ccP new file mode 100644 index 0000000000..e16725fd74 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/CHNode.ccP @@ -0,0 +1,21 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1992 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".CHNode.h" diff --git a/gnu/lib/libg++/g++-include/gen/CHNode.hP b/gnu/lib/libg++/g++-include/gen/CHNode.hP new file mode 100644 index 0000000000..84e67d069b --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/CHNode.hP @@ -0,0 +1,43 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988, 1982 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _CHNode_h +#define _CHNode_h 1 +#ifdef __GNUG__ +#pragma interface +#endif +#include ".defs.h" + +struct CHNode +{ + CHNode* tl; + hd; + CHNode(); + CHNode( h, CHNode* t = 0); + ~CHNode(); +}; + +inline CHNode::CHNode() {} + +inline CHNode::CHNode( h, CHNode* t) :hd(h), tl(t) {} + +inline CHNode::~CHNode() {} + +typedef CHNode* CHNodePtr; + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/CHSet.ccP b/gnu/lib/libg++/g++-include/gen/CHSet.ccP new file mode 100644 index 0000000000..330506cb19 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/CHSet.ccP @@ -0,0 +1,271 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".CHSet.h" + +// A CHSet is implemented as an array (tab) of buckets, each of which +// contains a pointer to a list of CHNodes. Each node contains a +// pointer to the next node in the list, and a pointer to the . +// The end of the list is marked by a next node pointer which is odd +// when considered as an integer (least significant bit = 1). The +// assumption is that CHNodes will all begin on even addresses. If +// the odd pointer is right-shifted by one bit, it becomes the index +// within the tab array of the next bucket (that is, bucket i has +// next bucket pointer 2*(i+1)+1). + +// The bucket pointers are initialized by the constructor and +// used to support the next(Pix&) method. + +// This implementation is not portable to machines with different +// pointer and integer sizes, or on which CHNodes might be aligned on +// odd byte boundaries, but allows the same pointer to be used for +// chaining within a bucket and to the next bucket. + + +static inline int goodCHptr(CHNode* t) +{ + return ((((unsigned)t) & 1) == 0); +} + +static inline CHNode* index_to_CHptr(int i) +{ + return (CHNode*)((i << 1) + 1); +} + +static inline int CHptr_to_index(CHNode* t) +{ + return ( ((unsigned) t) >> 1); +} + +CHSet::CHSet(unsigned int sz) +{ + tab = (CHNode**)(new CHNodePtr[size = sz]); + for (unsigned int i = 0; i < size; ++i) tab[i] = index_to_CHptr(i+1); + count = 0; +} + +CHSet::CHSet(CHSet& a) +{ + tab = (CHNode**)(new CHNodePtr[size = a.size]); + for (unsigned int i = 0; i < size; ++i) tab[i] = index_to_CHptr(i+1); + count = 0; + for (Pix p = a.first(); p; a.next(p)) add(a(p)); +} + + +Pix CHSet::seek( key) +{ + unsigned int h = HASH(key) % size; + + for (CHNode* t = tab[h]; goodCHptr(t); t = t->tl) + if (EQ(key, t->hd)) + return Pix(t); + + return 0; +} + + +Pix CHSet::add( item) +{ + unsigned int h = HASH(item) % size; + + for (CHNode* t = tab[h]; goodCHptr(t); t = t->tl) + if (EQ(item, t->hd)) + return Pix(t); + + ++count; + t = new CHNode(item, tab[h]); + tab[h] = t; + return Pix(t); +} + + +void CHSet::del( key) +{ + unsigned int h = HASH(key) % size; + + CHNode* t = tab[h]; + CHNode* trail = t; + while (goodCHptr(t)) + { + if (EQ(key, t->hd)) + { + if (trail == t) + tab[h] = t->tl; + else + trail->tl = t->tl; + delete t; + --count; + return; + } + trail = t; + t = t->tl; + } +} + + +void CHSet::clear() +{ + for (unsigned int i = 0; i < size; ++i) + { + CHNode* p = tab[i]; + tab[i] = index_to_CHptr(i+1); + while (goodCHptr(p)) + { + CHNode* nxt = p->tl; + delete(p); + p = nxt; + } + } + count = 0; +} + +Pix CHSet::first() +{ + for (unsigned int i = 0; i < size; ++i) if (goodCHptr(tab[i])) return Pix(tab[i]); + return 0; +} + +void CHSet::next(Pix& p) +{ + if (p == 0) return; + CHNode* t = ((CHNode*)p)->tl; + if (goodCHptr(t)) + p = Pix(t); + else + { + for (unsigned int i = CHptr_to_index(t); i < size; ++i) + { + if (goodCHptr(tab[i])) + { + p = Pix(tab[i]); + return; + } + } + p = 0; + } +} + +int CHSet::operator == (CHSet& b) +{ + if (count != b.count) + return 0; + else + { + CHNode* p; + for (unsigned int i = 0; i < size; ++i) + for (p = tab[i]; goodCHptr(p); p = p->tl) + if (b.seek(p->hd) == 0) + return 0; + for (i = 0; i < b.size; ++i) + for (p = b.tab[i]; goodCHptr(p); p = p->tl) + if (seek(p->hd) == 0) + return 0; + return 1; + } +} + +int CHSet::operator <= (CHSet& b) +{ + if (count > b.count) + return 0; + else + { + for (unsigned int i = 0; i < size; ++i) + for (CHNode* p = tab[i]; goodCHptr(p); p = p->tl) + if (b.seek(p->hd) == 0) + return 0; + return 1; + } +} + +void CHSet::operator |= (CHSet& b) +{ + if (&b == this || b.count == 0) + return; + for (unsigned int i = 0; i < b.size; ++i) + for (CHNode* p = b.tab[i]; goodCHptr(p); p = p->tl) + add(p->hd); +} + +void CHSet::operator &= (CHSet& b) +{ + for (unsigned int i = 0; i < size; ++i) + { + CHNode* t = tab[i]; + CHNode* trail = t; + while (goodCHptr(t)) + { + CHNode* nxt = t->tl; + if (b.seek(t->hd) == 0) + { + if (trail == tab[i]) + trail = tab[i] = nxt; + else + trail->tl = nxt; + delete t; + --count; + } + else + trail = t; + t = nxt; + } + } +} + +void CHSet::operator -= (CHSet& b) +{ + for (unsigned int i = 0; i < size; ++i) + { + CHNode* t = tab[i]; + CHNode* trail = t; + while (goodCHptr(t)) + { + CHNode* nxt = t->tl; + if (b.seek(t->hd) != 0) + { + if (trail == tab[i]) + trail = tab[i] = nxt; + else + trail->tl = nxt; + delete t; + --count; + } + else + trail = t; + t = nxt; + } + } +} + +int CHSet::OK() +{ + int v = tab != 0; + int n = 0; + for (unsigned int i = 0; i < size; ++i) + { + for (CHNode* p = tab[i]; goodCHptr(p); p = p->tl) ++n; + v &= CHptr_to_index(p) == i + 1; + } + v &= count == n; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/CHSet.hP b/gnu/lib/libg++/g++-include/gen/CHSet.hP new file mode 100644 index 0000000000..f0a08de4ce --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/CHSet.hP @@ -0,0 +1,84 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _CHSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _CHSet_h 1 + +#include ".Set.h" +#include ".CHNode.h" + +class CHSet : public Set +{ +protected: + CHNode** tab; + unsigned int size; + +public: + CHSet(unsigned int sz = DEFAULT_INITIAL_CAPACITY); + CHSet(CHSet& a); + ~CHSet(); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + Pix seek( item); + + void operator |= (CHSet& b); + void operator -= (CHSet& b); + void operator &= (CHSet& b); + + int operator == (CHSet& b); + int operator != (CHSet& b); + int operator <= (CHSet& b); + + int OK(); +}; + +inline CHSet::~CHSet() +{ + clear(); + delete tab; +} + +inline int CHSet::contains( key) +{ + return seek(key) != 0; +} + +inline & CHSet::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((CHNode*)i)->hd; +} + +inline int CHSet::operator != (CHSet& b) +{ + return ! ((*this) == b); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/DLDeque.ccP b/gnu/lib/libg++/g++-include/gen/DLDeque.ccP new file mode 100644 index 0000000000..d5a0db7f91 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/DLDeque.ccP @@ -0,0 +1,4 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".DLDeque.h" diff --git a/gnu/lib/libg++/g++-include/gen/DLDeque.hP b/gnu/lib/libg++/g++-include/gen/DLDeque.hP new file mode 100644 index 0000000000..d91cdd41cb --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/DLDeque.hP @@ -0,0 +1,130 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _DLDeque_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _DLDeque_h + +#include ".DLList.h" +#include ".Deque.h" + +class DLDeque : public Deque +{ + DLList p; + +public: + DLDeque(); + DLDeque(const DLDeque& d); + ~DLDeque(); + + void operator = (const DLDeque&); + + void push( item); // insert at front + void enq( item); // insert at rear + + & front(); + & rear(); + + deq(); + void del_front(); + void del_rear(); + + void clear(); + int empty(); + int full(); + int length(); + + int OK(); +}; + + +inline DLDeque::DLDeque() : p() {} +inline DLDeque::DLDeque(const DLDeque& d) : p(d.p) {} + +inline DLDeque::~DLDeque() {} + +inline void DLDeque::push(item) +{ + p.prepend(item); +} + +inline void DLDeque::enq(item) +{ + p.append(item); +} + +inline DLDeque::deq() +{ + return p.remove_front(); +} + +inline & DLDeque::front() +{ + return p.front(); +} + +inline & DLDeque::rear() +{ + return p.rear(); +} + +inline void DLDeque::del_front() +{ + p.del_front(); +} + +inline void DLDeque::del_rear() +{ + p.del_rear(); +} + +inline void DLDeque::operator =(const DLDeque& s) +{ + p.operator = (s.p); +} + + +inline int DLDeque::empty() +{ + return p.empty(); +} + +inline int DLDeque::full() +{ + return 0; +} + +inline int DLDeque::length() +{ + return p.length(); +} + +inline int DLDeque::OK() +{ + return p.OK(); +} + +inline void DLDeque::clear() +{ + p.clear(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/DLList.ccP b/gnu/lib/libg++/g++-include/gen/DLList.ccP new file mode 100644 index 0000000000..cb1e22a337 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/DLList.ccP @@ -0,0 +1,339 @@ +// This may look like C code, but it is really -*- C++ -*- +// WARNING: This file is obsolete. Use ../DLList.cc, if you can. +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include +#include +#include ".DLList.h" + +// error handling + + + +void DLList::error(const char* msg) +{ + (*lib_error_handler)("DLList", msg); +} + +int DLList::length() +{ + int l = 0; + DLListNode* t = h; + if (t != 0) do { ++l; t = t->fd; } while (t != h); + return l; +} + +DLList::DLList(const DLList& a) +{ + if (a.h == 0) + h = 0; + else + { + DLListNode* p = a.h; + DLListNode* t = new DLListNode(p->hd); + h = t; + p = p->fd; + while (p != a.h) + { + DLListNode* n = new DLListNode(p->hd); + t->fd = n; + n->bk = t; + t = n; + p = p->fd; + } + t->fd = h; + h->bk = t; + return; + } +} + +DLList& DLList::operator = (const DLList& a) +{ + if (h != a.h) + { + clear(); + if (a.h != 0) + { + DLListNode* p = a.h; + DLListNode* t = new DLListNode(p->hd); + h = t; + p = p->fd; + while (p != a.h) + { + DLListNode* n = new DLListNode(p->hd); + t->fd = n; + n->bk = t; + t = n; + p = p->fd; + } + t->fd = h; + h->bk = t; + } + } + return *this; +} + +void DLList::clear() +{ + if (h == 0) + return; + + DLListNode* p = h->fd; + h->fd = 0; + h = 0; + + while (p != 0) + { + DLListNode* nxt = p->fd; + delete(p); + p = nxt; + } +} + + +Pix DLList::prepend( item) +{ + DLListNode* t = new DLListNode(item); + if (h == 0) + t->fd = t->bk = h = t; + else + { + t->fd = h; + t->bk = h->bk; + h->bk->fd = t; + h->bk = t; + h = t; + } + return Pix(t); +} + +Pix DLList::append( item) +{ + DLListNode* t = new DLListNode(item); + if (h == 0) + t->fd = t->bk = h = t; + else + { + t->bk = h->bk; + t->bk->fd = t; + t->fd = h; + h->bk = t; + } + return Pix(t); +} + +Pix DLList::ins_after(Pix p, item) +{ + if (p == 0) return prepend(item); + DLListNode* u = (DLListNode*) p; + DLListNode* t = new DLListNode(item, u, u->fd); + u->fd->bk = t; + u->fd = t; + return Pix(t); +} + +Pix DLList::ins_before(Pix p, item) +{ + if (p == 0) error("null Pix"); + DLListNode* u = (DLListNode*) p; + DLListNode* t = new DLListNode(item, u->bk, u); + u->bk->fd = t; + u->bk = t; + if (u == h) h = t; + return Pix(t); +} + +void DLList::join(DLList& b) +{ + DLListNode* t = b.h; + b.h = 0; + if (h == 0) + h = t; + else if (t != 0) + { + DLListNode* l = t->bk; + h->bk->fd = t; + t->bk = h->bk; + h->bk = l; + l->fd = h; + } +} + +int DLList::owns(Pix p) +{ + DLListNode* t = h; + if (t != 0 && p != 0) + { + do + { + if (Pix(t) == p) return 1; + t = t->fd; + } while (t != h); + } + return 0; +} + +void DLList::del(Pix& p, int dir) +{ + if (p == 0) error("null Pix"); + DLListNode* t = (DLListNode*) p; + if (t->fd == t) + { + h = 0; + p = 0; + } + else + { + if (dir < 0) + { + if (t == h) + p = 0; + else + p = Pix(t->bk); + } + else + { + if (t == h->bk) + p = 0; + else + p = Pix(t->fd); + } + t->bk->fd = t->fd; + t->fd->bk = t->bk; + if (t == h) h = t->fd; + } + delete t; +} + +void DLList::del_after(Pix& p) +{ + if (p == 0) + { + del_front(); + return; + } + + DLListNode* b = (DLListNode*) p; + DLListNode* t = b->fd; + + if (b == t) + { + h = 0; + p = 0; + } + else + { + t->bk->fd = t->fd; + t->fd->bk = t->bk; + if (t == h) h = t->fd; + } + delete t; +} + + DLList::remove_front() +{ + if (h == 0) + error("remove_front of empty list"); + DLListNode* t = h; + res = t->hd; + if (h->fd == h) + h = 0; + else + { + h->fd->bk = h->bk; + h->bk->fd = h->fd; + h = h->fd; + } + delete t; + return res; +} + + +void DLList::del_front() +{ + if (h == 0) + error("del_front of empty list"); + DLListNode* t = h; + if (h->fd == h) + h = 0; + else + { + h->fd->bk = h->bk; + h->bk->fd = h->fd; + h = h->fd; + } + delete t; +} + + DLList::remove_rear() +{ + if (h == 0) + error("remove_rear of empty list"); + DLListNode* t = h->bk; + res = t->hd; + if (h->fd == h) + h = 0; + else + { + t->fd->bk = t->bk; + t->bk->fd = t->fd; + } + delete t; + return res; +} + + +void DLList::del_rear() +{ + if (h == 0) + error("del_rear of empty list"); + DLListNode* t = h->bk; + if (h->fd == h) + h = 0; + else + { + t->fd->bk = t->bk; + t->bk->fd = t->fd; + } + delete t; +} + + +int DLList::OK() +{ + int v = 1; + if (h != 0) + { + DLListNode* t = h; + long count = MAXLONG; // Lots of chances to find h! + do + { + count--; + v &= t->bk->fd == t; + v &= t->fd->bk == t; + t = t->fd; + } while (v && count > 0 && t != h); + v &= count > 0; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/DLList.hP b/gnu/lib/libg++/g++-include/gen/DLList.hP new file mode 100644 index 0000000000..b115ab9f6f --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/DLList.hP @@ -0,0 +1,157 @@ +// This may look like C code, but it is really -*- C++ -*- +// WARNING: This file is obsolete. Use ../DLList.h, if you can. +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _DLList_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _DLList_h 1 + +#include +#include ".defs.h" + +#ifndef _DLListNode_h +#define _DLListNode_h 1 + +struct DLListNode +{ + DLListNode* bk; + DLListNode* fd; + hd; + DLListNode(); + DLListNode(const h, + DLListNode* p = 0, + DLListNode* n = 0); + ~DLListNode(); +}; + +inline DLListNode::DLListNode() {} + +inline DLListNode::DLListNode(const h, DLListNode* p, + DLListNode* n) + :hd(h), bk(p), fd(n) {} + +inline DLListNode::~DLListNode() {} + +typedef DLListNode* DLListNodePtr; + +#endif + +class DLList +{ + friend class DLListTrav; + + DLListNode* h; + +public: + DLList(); + DLList(const DLList& a); + ~DLList(); + + DLList& operator = (const DLList& a); + + int empty(); + int length(); + + void clear(); + + Pix prepend( item); + Pix append( item); + void join(DLList&); + + & front(); + remove_front(); + void del_front(); + + & rear(); + remove_rear(); + void del_rear(); + + & operator () (Pix p); + Pix first(); + Pix last(); + void next(Pix& p); + void prev(Pix& p); + int owns(Pix p); + Pix ins_after(Pix p, item); + Pix ins_before(Pix p, item); + void del(Pix& p, int dir = 1); + void del_after(Pix& p); + + void error(const char* msg); + int OK(); +}; + + +inline DLList::~DLList() +{ + clear(); +} + +inline DLList::DLList() +{ + h = 0; +} + +inline int DLList::empty() +{ + return h == 0; +} + + +inline void DLList::next(Pix& p) +{ + p = (p == 0 || p == h->bk)? 0 : Pix(((DLListNode*)p)->fd); +} + +inline void DLList::prev(Pix& p) +{ + p = (p == 0 || p == h)? 0 : Pix(((DLListNode*)p)->bk); +} + +inline Pix DLList::first() +{ + return Pix(h); +} + +inline Pix DLList::last() +{ + return (h == 0)? 0 : Pix(h->bk); +} + +inline & DLList::operator () (Pix p) +{ + if (p == 0) error("null Pix"); + return ((DLListNode*)p)->hd; +} + +inline & DLList::front() +{ + if (h == 0) error("front: empty list"); + return h->hd; +} + +inline & DLList::rear() +{ + if (h == 0) error("rear: empty list"); + return h->bk->hd; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/Deque.ccP b/gnu/lib/libg++/g++-include/gen/Deque.ccP new file mode 100644 index 0000000000..79a9b72c87 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/Deque.ccP @@ -0,0 +1,11 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".Deque.h" + +Deque::~Deque() {} + +void Deque::error(const char* msg) +{ + (*lib_error_handler)("Deque", msg); +} diff --git a/gnu/lib/libg++/g++-include/gen/Deque.hP b/gnu/lib/libg++/g++-include/gen/Deque.hP new file mode 100644 index 0000000000..7ec52d4a0c --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/Deque.hP @@ -0,0 +1,57 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _Deque_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Deque_h + +#include + +#include ".defs.h" + +class Deque +{ +public: + Deque() { } + virtual ~Deque(); + + virtual void push( item) = 0; // insert at front + virtual void enq( item) = 0; // insert at rear + + virtual & front() = 0; + virtual & rear() = 0; + + virtual deq() = 0; + virtual void del_front() = 0; + virtual void del_rear() = 0; + + virtual int empty() = 0; + virtual int full() = 0; + virtual int length() = 0; + virtual void clear() = 0; + + virtual int OK() = 0; + + void error(const char*); +}; + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/FPQueue.ccP b/gnu/lib/libg++/g++-include/gen/FPQueue.ccP new file mode 100644 index 0000000000..a358cacb60 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/FPQueue.ccP @@ -0,0 +1,4 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".FPQueue.h" diff --git a/gnu/lib/libg++/g++-include/gen/FPQueue.hP b/gnu/lib/libg++/g++-include/gen/FPQueue.hP new file mode 100644 index 0000000000..f647ae9dfd --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/FPQueue.hP @@ -0,0 +1,112 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _FPQueue_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _FPQueue_h + +#include ".FPlex.h" +#include ".Queue.h" + +class FPQueue : public Queue +{ + FPlex p; + +public: + FPQueue(int chunksize = DEFAULT_INITIAL_CAPACITY); + FPQueue(const FPQueue& q); + ~FPQueue(); + + void operator = (const FPQueue&); + + void enq( item); + deq(); + & front(); + void del_front(); + + void clear(); + int empty(); + int full(); + int length(); + + int OK(); +}; + +inline FPQueue::FPQueue(int chunksize) : p(chunksize) {} +inline FPQueue::FPQueue(const FPQueue& q) : p(q.p) {} + +inline FPQueue::~FPQueue() {} + +inline void FPQueue::enq(item) +{ + p.add_high(item); +} + +inline FPQueue::deq() +{ + res = p.low_element(); + p.del_low(); + return res; +} + +inline & FPQueue::front() +{ + return p.low_element(); +} + + +inline void FPQueue::del_front() +{ + p.del_low(); +} + +inline void FPQueue::operator =(const FPQueue& s) +{ + p = s.p; +} + +inline int FPQueue::empty() +{ + return p.empty(); +} + +inline int FPQueue::full() +{ + return p.full(); +} + +inline int FPQueue::length() +{ + return p.length(); +} + +inline int FPQueue::OK() +{ + return p.OK(); +} + +inline void FPQueue::clear() +{ + p.clear(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/FPStack.ccP b/gnu/lib/libg++/g++-include/gen/FPStack.ccP new file mode 100644 index 0000000000..954991193b --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/FPStack.ccP @@ -0,0 +1,4 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".FPStack.h" diff --git a/gnu/lib/libg++/g++-include/gen/FPStack.hP b/gnu/lib/libg++/g++-include/gen/FPStack.hP new file mode 100644 index 0000000000..091f255360 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/FPStack.hP @@ -0,0 +1,114 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _FPStack_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _FPStack_h + +#include ".FPlex.h" +#include ".Stack.h" + +class FPStack : public Stack +{ + FPlex p; + +public: + FPStack(int chunksize = DEFAULT_INITIAL_CAPACITY); + FPStack(const FPStack& s); + ~FPStack(); + + void operator = (const FPStack&); + + void push( item); + pop(); + & top(); + void del_top(); + + int empty(); + int full(); + int length(); + + void clear(); + + int OK(); + +}; + + +inline FPStack::FPStack(int chunksize) : p(chunksize) {} +inline FPStack::FPStack(const FPStack& s) : p(s.p) {} + +inline FPStack::~FPStack() {} + +inline void FPStack::push(item) +{ + p.add_high(item); +} + +inline FPStack::pop() +{ + res = p.high_element(); + p.del_high(); + return res; +} + +inline & FPStack::top() +{ + return p.high_element(); +} + +inline void FPStack::del_top() +{ + p.del_high(); +} + +inline void FPStack::operator =(const FPStack& s) +{ + p = s.p; +} + +inline int FPStack::empty() +{ + return p.empty(); +} + +inline int FPStack::full() +{ + return p.full(); +} + +inline int FPStack::length() +{ + return p.length(); +} + +inline int FPStack::OK() +{ + return p.OK(); +} + +inline void FPStack::clear() +{ + p.clear(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/FPlex.ccP b/gnu/lib/libg++/g++-include/gen/FPlex.ccP new file mode 100644 index 0000000000..70d6c47557 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/FPlex.ccP @@ -0,0 +1,167 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".FPlex.h" + + +FPlex:: FPlex() +{ + lo = fnc = 0; + csize = DEFAULT_INITIAL_CAPACITY; + * data = new [csize]; + hd = new IChunk(data, lo, lo, fnc, csize); +} + +FPlex:: FPlex(int maxsize) +{ + if (maxsize == 0) error("invalid constructor specification"); + lo = fnc = 0; + if (maxsize > 0) + { + csize = maxsize; + * data = new [csize]; + hd = new IChunk(data, lo, lo, fnc, csize); + } + else + { + csize = -maxsize; + * data = new [csize]; + hd = new IChunk(data, maxsize, lo, fnc, fnc); + } +} + + +FPlex:: FPlex(int l, int maxsize) +{ + if (maxsize == 0) error("invalid constructor specification"); + lo = fnc = l; + if (maxsize > 0) + { + csize = maxsize; + * data = new [csize]; + hd = new IChunk(data, lo, lo, fnc, csize+lo); + } + else + { + csize = -maxsize; + * data = new [csize]; + hd = new IChunk(data, maxsize+lo, lo, fnc, fnc); + } +} + +FPlex:: FPlex(int l, int hi, const initval, int maxsize) +{ + lo = l; + fnc = hi + 1; + if (maxsize >= 0) + { + csize = maxsize; + if (csize < fnc - lo) + csize = fnc - lo; + * data = new [csize]; + hd = new IChunk(data, lo, lo, fnc, csize); + } + else + { + csize = -maxsize; + if (csize < fnc - lo) + csize = fnc - lo; + * data = new [csize]; + hd = new IChunk(data, -csize, lo, fnc, fnc); + } + fill(initval); +} + +FPlex::FPlex(const FPlex& a) +{ + lo = a.lo; + fnc = a.fnc; + csize = fnc - lo; + if (csize < a.csize) csize = a.csize; + * data = new [csize]; + hd = new IChunk(data, lo, lo, fnc, lo+csize); + for (int i = a.low(); i < a.fence(); a.next(i)) (*this)[i] = a[i]; +} + +void FPlex::operator= (const FPlex& a) +{ + if (&a != this) + { + del_chunk(hd); + lo = a.lo; + fnc = a.fnc; + csize = fnc - lo; + if (csize < a.csize) csize = a.csize; + * data = new [csize]; + hd = new IChunk(data, lo, lo, fnc, lo+csize); + for (int i = a.low(); i < a.fence(); a.next(i)) (*this)[i] = a[i]; + } +} + + +void FPlex::reverse() +{ + tmp; + int l = lo; + int h = fnc - 1; + while (l < h) + { + tmp = (*this)[l]; + (*this)[l] = (*this)[h]; + (*this)[h] = tmp; + next(l); + prev(h); + } +} + +void FPlex::fill(const x) +{ + for (int i = lo; i < fnc; ++i) (*this)[i] = x; +} + +void FPlex::fill(const x, int lo, int hi) +{ + for (int i = lo; i <= hi; ++i) (*this)[i] = x; +} + +void FPlex::clear() +{ + if (fnc != lo) + { + hd->clear(lo); + fnc = lo; + } +} + +int FPlex::OK () const +{ + int v = hd != 0; // hd exists + v &= hd->IChunk::OK(); // and is OK + v &= fnc - lo <= hd->size(); // and has enough space + v &= lo <= fnc; // plex indices consistent + v &= lo == hd->low_index(); // and match those + v &= fnc == hd->fence_index(); // of chunk + v &= one_chunk(); // and only one chunk + if (!v) error("invariant failure"); + return v; +} + diff --git a/gnu/lib/libg++/g++-include/gen/FPlex.hP b/gnu/lib/libg++/g++-include/gen/FPlex.hP new file mode 100644 index 0000000000..eb93a0c372 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/FPlex.hP @@ -0,0 +1,253 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _FPlex_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _FPlex_h 1 + +#include ".Plex.h" + +class FPlex : public Plex +{ +public: + FPlex(); // set low = 0; + // fence = 0; + // csize = default + + FPlex(int maxsize); // low = 0; + // fence = 0; + // csize = maxsize + + FPlex(int lo, // low = lo; + int maxsize); // fence=lo + // csize = maxsize + + FPlex(int lo, // low = lo + int hi, // fence = hi+1 + const initval,// fill with initval, + int maxsize = 0); // csize = maxsize + // or fence - lo if 0 + + FPlex(const FPlex&); // X(X&) + + ~FPlex(); + + void operator= (const FPlex&); + +// virtuals + + & high_element (); + & low_element (); + + const & high_element () const; + const & low_element () const; + + Pix first() const; + Pix last() const; + void prev(Pix& ptr) const; + void next(Pix& ptr) const; + int owns(Pix p) const; + & operator () (Pix p); + const & operator () (Pix p) const; + + int low() const; + int high() const; + int valid(int idx) const; + void prev(int& idx) const; + void next(int& x) const; + & operator [] (int index); + const & operator [] (int index) const; + + int Pix_to_index(Pix p) const; + Pix index_to_Pix(int idx) const; + + int can_add_high() const; + int can_add_low() const; + int full() const; + + int add_high(const elem); + int del_high (); + int add_low (const elem); + int del_low (); + + void fill(const x); + void fill(const x, int from, int to); + void clear(); + void reverse(); + + int OK () const; +}; + + +inline int FPlex::valid (int idx) const +{ + return idx >= lo && idx < fnc; +} + +inline int FPlex::low() const +{ + return lo; +} + +inline int FPlex::high() const +{ + return fnc - 1; +} + +inline Pix FPlex::first() const +{ + return (Pix)(hd->IChunk::first_pointer()); +} + +inline void FPlex::prev(Pix& p) const +{ + p = Pix(hd->IChunk::pred((*) p)); +} + +inline void FPlex::next(Pix& p) const +{ + p = Pix(hd->IChunk::succ((*) p)); +} + +inline Pix FPlex::last() const +{ + return Pix(hd->IChunk::last_pointer()); +} + +inline int FPlex::full () const +{ + return fnc - lo == csize; +} + +inline void FPlex::prev(int& idx) const +{ + --idx; +} + +inline void FPlex::next(int& idx) const +{ + ++idx; +} + +inline & FPlex:: operator [] (int idx) +{ + if (idx < lo || idx >= fnc) index_error(); + return *(hd->pointer_to(idx)); +} + +inline & FPlex:: operator () (Pix p) +{ + return *((*)p); +} + +inline & FPlex::low_element () +{ + if (empty()) index_error(); + return *(hd->pointer_to(lo)); +} + +inline & FPlex::high_element () +{ + if (empty()) index_error(); + return *(hd->pointer_to(fnc - 1)); +} + +inline const & FPlex:: operator [] (int idx) const +{ + if (idx < lo || idx >= fnc) index_error(); + return *(hd->pointer_to(idx)); +} + +inline const & FPlex:: operator () (Pix p) const +{ + return *((const *)p); +} + +inline const & FPlex::low_element () const +{ + if (empty()) index_error(); + return *(hd->pointer_to(lo)); +} + +inline const & FPlex::high_element () const +{ + if (empty()) index_error(); + return *(hd->pointer_to(fnc - 1)); +} + +inline int FPlex::can_add_high() const +{ + return hd->can_grow_high(); +} + +inline int FPlex::can_add_low() const +{ + return hd->can_grow_low(); +} + +inline int FPlex::add_high(const elem) +{ + if (!can_add_high()) full_error(); + *((hd->IChunk::grow_high())) = elem; + return fnc++; +} + +inline int FPlex::del_high () +{ + if (empty()) empty_error(); + hd->IChunk::shrink_high(); + return --fnc - 1; +} + +inline int FPlex::add_low (const elem) +{ + if (!can_add_low()) full_error(); + *((hd->IChunk::grow_low())) = elem; + return --lo; +} + +inline int FPlex::del_low () +{ + if (empty()) empty_error(); + hd->IChunk::shrink_low(); + return ++lo; +} + +inline int FPlex::owns (Pix p) const +{ + return hd->actual_pointer((*)p); +} + +inline int FPlex::Pix_to_index(Pix p) const +{ + if (!hd->actual_pointer((const *)p)) index_error(); + return hd->index_of((const *)p); +} + +inline Pix FPlex::index_to_Pix(int idx) const +{ + if (idx < lo || idx >= fnc) index_error(); + return Pix(hd->pointer_to(idx)); +} + +inline FPlex::~FPlex() {} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/List.ccP b/gnu/lib/libg++/g++-include/gen/List.ccP new file mode 100644 index 0000000000..2afbdaf972 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/List.ccP @@ -0,0 +1,956 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".List.h" + +ListNode NilListNode; + +class init_NilListNode +{ +public: + init_NilListNode() + { + NilListNode.tl = &NilListNode; + NilListNode.ref = -1; + } +}; + +static init_NilListNode NilListNode_initializer; + +List& List::operator = (List& a) +{ + reference(a.P); + dereference(P); + P = a.P; + return *this; +} + + List::pop() +{ + res = P->hd; + ListNode* tail = P->tl; + reference(tail); + dereference(P); + P = tail; + return res; +} + +void List::set_tail(List& a) +{ + reference(a.P); + dereference(P->tl); + P->tl = a.P; +} + +List List::nth(int n) +{ + for (ListNode* p = P; n-- > 0; p = p->tl); + reference(p); + return List(p); +} + +List List::last() +{ + ListNode* p = P; + if (p != &NilListNode) while (p->tl != &NilListNode) p = p->tl; + reference(p); + return List(p); +} + +void List::append(List& l) +{ + ListNode* p = P; + ListNode* a = l.P; + reference(a); + if (p != &NilListNode) + { + while (p->tl != &NilListNode) p = p->tl; + p->tl = a; + } + else + P = a; +} + +int List::length() +{ + int l = 0; + for (ListNode* p = P; p != &NilListNode; p = p->tl) ++l; + return l; +} + +& List::operator [] (int n) +{ + for (ListNode* p = P; n-- > 0; p = p->tl); + return (p->hd); +} + +int operator == (List& x, List& y) +{ + ListNode* a = x.P; + ListNode* b = y.P; + + for (;;) + { + if (a == &NilListNode) + return b == &NilListNode; + else if (b == &NilListNode) + return 0; + else if (EQ(a->hd, b->hd)) + { + a = a->tl; + b = b->tl; + } + else + return 0; + } +} + + +void List::apply(Procedure f) +{ + for(ListNode* p = P; p != &NilListNode; p = p->tl) + (*f)((p->hd)); +} + +void List::subst( old, repl) +{ + for(ListNode* p = P; p != &NilListNode; p = p->tl) + if (EQ(p->hd, old)) + p->hd = repl; +} + + List::reduce(Combiner f, base) +{ + r = base; + for(ListNode* p = P; p != &NilListNode; p = p->tl) + r = (*f)(r, (p->hd)); + return r; +} + +int List::position( targ) +{ + int l = 0; + ListNode* p = P; + for (;;) + { + if (p == &NilListNode) + return -1; + else if (EQ(p->hd, targ)) + return l; + else + { + ++l; + p = p->tl; + } + } +} + +int List::contains( targ) +{ + ListNode* p = P; + for (;;) + { + if (p == &NilListNode) + return 0; + else if (EQ(p->hd, targ)) + return 1; + else + p = p->tl; + } +} + +List List::find( targ) +{ + ListNode* p = P; + while (p != &NilListNode && !EQ(p->hd, targ)) + p=p->tl; + reference(p); + return List(p); +} + +Pix List::seek( targ) +{ + ListNode* p = P; + for (;;) + { + if (p == &NilListNode) + return 0; + else if (EQ(p->hd, targ)) + return Pix(p); + else + p = p->tl; + } +} + +int List::owns(Pix i) +{ + ListNode* p = P; + for (;;) + { + if (p == &NilListNode) + return 0; + else if (Pix(p) == i) + return 1; + else + p = p->tl; + } +} + +List List::find(List& target) +{ + ListNode* targ = target.P; + if (targ == &NilListNode) + return List(targ); + + ListNode* p = P; + while (p != &NilListNode) + { + if (EQ(p->hd, targ->hd)) + { + ListNode* a = p->tl; + ListNode* t = targ->tl; + for(;;) + { + if (t == &NilListNode) + { + reference(p); + return List(p); + } + else if (a == &NilListNode || !EQ(a->hd, t->hd)) + break; + else + { + a = a->tl; + t = t->tl; + } + } + } + p = p->tl; + } + return List(&NilListNode); +} + +int List::contains(List& target) +{ + ListNode* targ = target.P; + if (targ == &NilListNode) + return 0; + + ListNode* p = P; + while (p != &NilListNode) + { + if (EQ(p->hd, targ->hd)) + { + ListNode* a = p->tl; + ListNode* t = targ->tl; + for(;;) + { + if (t == &NilListNode) + return 1; + else if (a == &NilListNode || !EQ(a->hd, t->hd)) + break; + else + { + a = a->tl; + t = t->tl; + } + } + } + p = p->tl; + } + return 0; +} + +void List::del( targ) +{ + ListNode* h = P; + + for (;;) + { + if (h == &NilListNode) + { + P = h; + return; + } + else if (EQ(h->hd, targ)) + { + ListNode* nxt = h->tl; + reference(nxt); + dereference(h); + h = nxt; + } + else + break; + } + + ListNode* trail = h; + ListNode* p = h->tl; + while (p != &NilListNode) + { + if (EQ(p->hd, targ)) + { + ListNode* nxt = p->tl; + reference(nxt); + dereference(p); + trail->tl = nxt; + p = nxt; + } + else + { + trail = p; + p = p->tl; + } + } + P = h; +} + +void List::del(Predicate f) +{ + ListNode* h = P; + for (;;) + { + if (h == &NilListNode) + { + P = h; + return; + } + else if ((*f)(h->hd)) + { + ListNode* nxt = h->tl; + reference(nxt); + dereference(h); + h = nxt; + } + else + break; + } + + ListNode* trail = h; + ListNode* p = h->tl; + while (p != &NilListNode) + { + if ((*f)(p->hd)) + { + ListNode* nxt = p->tl; + reference(nxt); + dereference(p); + trail->tl = nxt; + p = nxt; + } + else + { + trail = p; + p = p->tl; + } + } + P = h; +} + +void List::select(Predicate f) +{ + ListNode* h = P; + for (;;) + { + if (h == &NilListNode) + { + P = h; + return; + } + else if (!(*f)(h->hd)) + { + ListNode* nxt = h->tl; + reference(nxt); + dereference(h); + h = nxt; + } + else + break; + } + ListNode* trail = h; + ListNode* p = h->tl; + while (p != &NilListNode) + { + if (!(*f)(p->hd)) + { + ListNode* nxt = p->tl; + reference(nxt); + dereference(p); + trail->tl = nxt; + p = nxt; + } + else + { + trail = p; + p = p->tl; + } + } + P = h; +} + +void List::reverse() +{ + ListNode* l = &NilListNode; + ListNode* p = P; + while (p != &NilListNode) + { + ListNode* nxt = p->tl; + p->tl = l; + l = p; + p = nxt; + } + P = l; +} + + +List copy(List& x) +{ + ListNode* a = x.P; + if (a == &NilListNode) + return List(a); + else + { + ListNode* h = newListNode(a->hd); + ListNode* trail = h; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + ListNode* n = newListNode(a->hd); + trail->tl = n; + trail = n; + } + trail->tl = &NilListNode; + return List(h); + } +} + + +List subst( old, repl, List& x) +{ + ListNode* a = x.P; + if (a == &NilListNode) + return List(a); + else + { + ListNode* h = new ListNode; + h->ref = 1; + if (EQ(a->hd, old)) + h->hd = repl; + else + h->hd = a->hd; + ListNode* trail = h; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + ListNode* n = new ListNode; + n->ref = 1; + if (EQ(a->hd, old)) + n->hd = repl; + else + n->hd = a->hd; + trail->tl = n; + trail = n; + } + trail->tl = &NilListNode; + return List(h); + } +} + +List combine(Combiner f, List& x, List& y) +{ + ListNode* a = x.P; + ListNode* b = y.P; + if (a == &NilListNode || b == &NilListNode) + return List(&NilListNode); + else + { + ListNode* h = newListNode((*f)(a->hd, b->hd)); + ListNode* trail = h; + a = a->tl; + b = b->tl; + while (a != &NilListNode && b != &NilListNode) + { + ListNode* n = newListNode((*f)(a->hd, b->hd)); + trail->tl = n; + trail = n; + a = a->tl; + b = b->tl; + } + trail->tl = &NilListNode; + return List(h); + } +} + +List reverse(List& x) +{ + ListNode* a = x.P; + if (a == &NilListNode) + return List(a); + else + { + ListNode* l = newListNode(a->hd); + l->tl = &NilListNode; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + ListNode* n = newListNode(a->hd); + n->tl = l; + l = n; + } + return List(l); + } +} + +List append(List& x, List& y) +{ + ListNode* a = x.P; + ListNode* b = y.P; + reference(b); + if (a != &NilListNode) + { + ListNode* h = newListNode(a->hd); + ListNode* trail = h; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + ListNode* n = newListNode(a->hd); + trail->tl = n; + trail = n; + } + trail->tl = b; + return List(h); + } + else + return List(b); +} + +void List::prepend(List& y) +{ + ListNode* b = y.P; + if (b != &NilListNode) + { + ListNode* h = newListNode(b->hd); + ListNode* trail = h; + for(b = b->tl; b != &NilListNode; b = b->tl) + { + ListNode* n = newListNode(b->hd); + trail->tl = n; + trail = n; + } + trail->tl = P; + P = h; + } +} + +List concat(List& x, List& y) +{ + ListNode* a = x.P; + ListNode* b = y.P; + if (a != &NilListNode) + { + ListNode* h = newListNode(a->hd); + ListNode* trail = h; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + ListNode* n = newListNode(a->hd); + trail->tl = n; + trail = n; + }; + for(;b != &NilListNode; b = b->tl) + { + ListNode* n = newListNode(b->hd); + trail->tl = n; + trail = n; + } + trail->tl = &NilListNode; + return List(h); + } + else if (b != &NilListNode) + { + ListNode* h = newListNode(b->hd); + ListNode* trail = h; + for(b = b->tl; b != &NilListNode; b = b->tl) + { + ListNode* n = newListNode(b->hd); + trail->tl = n; + trail = n; + } + trail->tl = &NilListNode; + return List(h); + } + else + return List(&NilListNode); +} + +List select(Predicate f, List& x) +{ + ListNode* a = x.P; + ListNode* h = &NilListNode; + while (a != &NilListNode) + { + if ((*f)(a->hd)) + { + h = newListNode(a->hd); + ListNode* trail = h; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + if ((*f)(a->hd)) + { + ListNode* n = newListNode(a->hd); + trail->tl = n; + trail = n; + } + } + trail->tl = &NilListNode; + break; + } + else + a = a->tl; + } + return List(h); +} + +List remove(Predicate f, List& x) +{ + ListNode* a = x.P; + ListNode* h = &NilListNode; + while (a != &NilListNode) + { + if (!(*f)(a->hd)) + { + h = newListNode(a->hd); + ListNode* trail = h; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + if (!(*f)(a->hd)) + { + ListNode* n = newListNode(a->hd); + trail->tl = n; + trail = n; + } + } + trail->tl = &NilListNode; + break; + } + else + a = a->tl; + } + return List(h); +} + +List remove( targ, List& x) +{ + ListNode* a = x.P; + ListNode* h = &NilListNode; + while (a != &NilListNode) + { + if (!(EQ(a->hd, targ))) + { + h = newListNode(a->hd); + ListNode* trail = h; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + if (!EQ(a->hd, targ)) + { + ListNode* n = newListNode(a->hd); + trail->tl = n; + trail = n; + } + } + trail->tl = &NilListNode; + break; + } + else + a = a->tl; + } + return List(h); +} + +List map(Mapper f, List& x) +{ + ListNode* a = x.P; + ListNode* h = &NilListNode; + if (a != &NilListNode) + { + h = newListNode((*f)(a->hd)); + ListNode* trail = h; + for(a = a->tl; a != &NilListNode; a = a->tl) + { + ListNode* n = newListNode((*f)(a->hd)); + trail->tl = n; + trail = n; + } + trail->tl = &NilListNode; + } + return List(h); +} + + +List merge(List& x, List& y, Comparator f) +{ + ListNode* a = x.P; + ListNode* b = y.P; + + if (a == &NilListNode) + { + if (b == &NilListNode) + return List(&NilListNode); + else + return copy(y); + } + else if (b == &NilListNode) + return copy(x); + + ListNode* h = new ListNode; + h->ref = 1; + if ((*f)(a->hd, b->hd) <= 0) + { + h->hd = a->hd; + a = a->tl; + } + else + { + h->hd = b->hd; + b = b->tl; + } + + ListNode* r = h; + + for(;;) + { + if (a == &NilListNode) + { + while (b != &NilListNode) + { + ListNode* n = new ListNode; + n->ref = 1; + n->hd = b->hd; + r->tl = n; + r = n; + b = b->tl; + } + r->tl = &NilListNode; + return List(h); + } + else if (b == &NilListNode) + { + while (a != &NilListNode) + { + ListNode* n = new ListNode; + n->ref = 1; + n->hd = a->hd; + r->tl = n; + r = n; + a = a->tl; + } + r->tl = &NilListNode; + return List(h); + } + else if ((*f)(a->hd, b->hd) <= 0) + { + ListNode* n = new ListNode; + n->ref = 1; + n->hd = a->hd; + r->tl = n; + r = n; + a = a->tl; + } + else + { + ListNode* n = new ListNode; + n->ref = 1; + n->hd = b->hd; + r->tl = n; + r = n; + b = b->tl; + } + } +} + +void List::sort(Comparator f) +{ + // strategy: place runs in queue, merge runs until done + // This is often very fast + + if (P == &NilListNode || P->tl == &NilListNode) + return; + + int qlen = 250; // guess a good queue size, realloc if necessary + + ListNode** queue = (ListNode**)malloc(qlen * sizeof(ListNode*)); + + ListNode* h = P; + ListNode* a = h; + ListNode* b = a->tl; + int qin = 0; + + while (b != &NilListNode) + { + if ((*f)(a->hd, b->hd) > 0) + { + if (h == a) // minor optimization: ensure runlen >= 2 + { + h = b; + a->tl = b->tl; + b->tl = a; + b = a->tl; + } + else + { + if (qin >= qlen) + { + qlen *= 2; + queue = (ListNode**)realloc(queue, qlen * sizeof(ListNode*)); + } + queue[qin++] = h; + a->tl = &NilListNode; + h = a = b; + b = b->tl; + } + } + else + { + a = b; + b = b->tl; + } + } + + int count = qin; + queue[qin] = h; + if (++qin >= qlen) qin = 0; + int qout = 0; + + while (count-- > 0) + { + a = queue[qout]; + if (++qout >= qlen) qout = 0; + b = queue[qout]; + if (++qout >= qlen) qout = 0; + + if ((*f)(a->hd, b->hd) <= 0) + { + h = a; + a = a->tl; + } + else + { + h = b; + b = b->tl; + } + queue[qin] = h; + if (++qin >= qlen) qin = 0; + + for (;;) + { + if (a == &NilListNode) + { + h->tl = b; + break; + } + else if (b == &NilListNode) + { + h->tl = a; + break; + } + else if ((*f)(a->hd, b->hd) <= 0) + { + h->tl = a; + h = a; + a = a->tl; + } + else + { + h->tl = b; + h = b; + b = b->tl; + } + } + } + P = queue[qout]; + free(queue); +} + +int List::list_length() +{ + ListNode* fast = P; + if (fast == &NilListNode) + return 0; + + ListNode* slow = fast->tl; + if (slow == &NilListNode) + return 1; + + fast = slow->tl; + int n = 2; + + for (;;) + { + if (fast == &NilListNode) + return n; + else if (fast->tl == &NilListNode) + return n+1; + else if (fast == slow) + return -1; + else + { + n += 2; + fast = fast->tl->tl; + slow = slow->tl; + } + } +} + +void List::error(const char* msg) +{ + (*lib_error_handler)("List", msg); +} + +int List::OK() +{ + int v = P != 0; // have a node + // check that all nodes OK, even if circular: + + ListNode* fast = P; + if (fast != &NilListNode) + { + v &= fast->ref != 0; + ListNode* slow = fast->tl; + v &= slow->ref != 0; + if (v && slow != &NilListNode) + { + fast = slow->tl; + v &= fast->ref != 0; + while (v) + { + if (fast == &NilListNode) + break; + else if (fast->tl == &NilListNode) + break; + else if (fast == slow) + break; + else + { + v &= fast->ref != 0 && slow->ref != 0; + fast = fast->tl->tl; + slow = slow->tl; + } + } + } + } + if (!v) error ("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/List.hP b/gnu/lib/libg++/g++-include/gen/List.hP new file mode 100644 index 0000000000..7ccdbc3bc4 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/List.hP @@ -0,0 +1,273 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _List_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _List_h 1 + +#ifndef __typedefs +#define __typedefs 1 +typedef void (*Procedure)(); +typedef (*Mapper)(); +typedef (*Combiner)(, ); +typedef int (*Predicate)(); +typedef int (*Comparator)(, ); +#endif + +#include +#include ".defs.h" + +struct ListNode +{ + ListNode* tl; + short ref; + hd; +}; + +extern ListNode NilListNode; + +class List +{ +protected: + ListNode* P; + + List(ListNode* p); +public: + List(); + List( head); + List( head, List& tl); + List(List& a); + List(Pix p); + ~List(); + + List& operator = (List& a); + + int null(); + int valid(); + operator const void* (); + int operator ! (); + + int length(); + int list_length(); + + & get(); + & head(); + & operator [] (int n); + + List nth(int n); + List tail(); + List last(); + + List find( targ); + List find(List& targ); + int contains( targ); + int contains(List& targ); + int position( targ); + + friend List copy(List& a); + friend List concat(List& a, List& b); + friend List append(List& a, List& b); + friend List map(Mapper f, List& a); + friend List merge(List& a, List& b, Comparator f); + friend List combine(Combiner f, List& a, List& b); + friend List reverse(List& a); + friend List select(Predicate f, List& a); +#undef remove + friend List remove( targ, List& a); + friend List remove(Predicate f, List& a); + friend List subst( old, repl, List& a); + + void push( x); + pop(); + + void set_tail(List& p); + void append(List& p); + void prepend(List& p); + void del( targ); + void del(Predicate f); + void select(Predicate f); + void subst( old, repl); + void reverse(); + void sort(Comparator f); + + void apply(Procedure f); + reduce(Combiner f, base); + + friend int operator == (List& a, List& b); + friend int operator != (List& a, List& b); + + Pix first(); + void next(Pix& p); + Pix seek( item); + & operator () (Pix p); + int owns(Pix p); + + void error(const char*); + int OK(); +}; + + +inline void reference(ListNode* p) +{ + if (p->ref >= 0) ++p->ref; +} + +inline void dereference(ListNode* p) +{ + while (p->ref > 0 && --p->ref == 0) + { + ListNode* n = p->tl; + delete(p); + p = n; + } +} + + +inline ListNode* newListNode( h) +{ + ListNode* p = new ListNode; + p->ref = 1; + p->hd = h; + return p; +} + +inline ListNode* newListNode( h, ListNode* t) +{ + ListNode* p = new ListNode; + p->ref = 1; + p->hd = h; + p->tl = t; + return p; +} + + +inline List::~List() +{ + dereference(P); +} + +inline List::List() +{ + P = &NilListNode; +} + +inline List::List(ListNode* p) +{ + P = p; +} + +inline List::List( head) +{ + P = newListNode(head); + P->tl = &NilListNode; +} + +inline List::List( head, List& tl) +{ + P = newListNode(head, tl.P); + reference(P->tl); +} + +inline List::List(List& a) +{ + reference(a.P); + P = a.P; +} + + +inline & List::get() +{ + return P->hd; +} + +inline & List::head() +{ + return P->hd; +} + + +inline List List::tail() +{ + reference(P->tl); + return List(P->tl); +} + + + +inline int List::null() +{ + return P == &NilListNode; +} + +inline int List::valid() +{ + return P != &NilListNode; +} + +inline List::operator const void* () +{ + return (P == &NilListNode)? 0 : this; +} + +inline int List::operator ! () +{ + return (P == &NilListNode); +} + + +inline void List::push( head) +{ + ListNode* oldp = P; + P = newListNode(head, oldp); +} + + +inline int operator != (List& x, List& y) +{ + return !(x == y); +} + +inline Pix List::first() +{ + return (P == &NilListNode)? 0 : Pix(P); +} + +inline & List::operator () (Pix p) +{ + return ((ListNode*)p)->hd; +} + +inline void List::next(Pix& p) +{ + if (p != 0) + { + p = Pix(((ListNode*)p)->tl); + if (p == &NilListNode) p = 0; + } +} + +inline List::List(Pix p) +{ + P = (ListNode*)p; + reference(P); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/MPlex.ccP b/gnu/lib/libg++/g++-include/gen/MPlex.ccP new file mode 100644 index 0000000000..89a1bcf9e6 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/MPlex.ccP @@ -0,0 +1,845 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".MPlex.h" + +// MChunk support + + +MChunk::MChunk(* d, + int baseidx, + int lowidx, + int fenceidx, + int topidx) + : IChunk(d, baseidx, lowidx, fenceidx, topidx) +{ + unused = fence - low; + unsigned msize = (top - base)/_MAP_BITS + 1; + map = (unsigned long *) (new long[msize]); + memset((void*)map, 0, msize * sizeof(long)); +} + +void MChunk:: shrink_high () +{ + if (fence <= low) empty_error(); + --fence; + if (!valid(fence)) + --unused; + else + free(fence); + reset_high(); +} + +void MChunk:: shrink_low () +{ + if (fence <= low) empty_error(); + if (!valid(low)) + --unused; + else + free(low); + ++low; + reset_low(); +} + +void MChunk::clear(int lo) +{ + int s = top - base; + low = base = fence = lo; + top = base + s; + unused = 0; + memset((void*)map, 0, ((top - base)/_MAP_BITS + 1) * sizeof(long)); +} + +void MChunk::cleardown(int hi) +{ + int s = top - base; + low = top = fence = hi; + base = top - s; + unused = 0; + memset((void*)map, 0, ((top - base)/_MAP_BITS + 1) * sizeof(long)); +} + +int MChunk::del(int idx) +{ + if (idx < low || idx >= fence) index_error(); + int v = valid(idx); + if (v) + { + free(idx); + ++unused; + } + return v; +} + + +int MChunk::undel(int idx) +{ + if (idx < low || idx >= fence) index_error(); + int v = valid(idx); + if (!v) + { + mark(idx); + --unused; + } + return v; +} + +int MChunk::unused_index() const +{ + if (unused_indices() == 0) index_error(); + int slot; + if (low == base) // can traverse 32 slots at a time + { + int blk = 0; + while (map[blk] == ~0L) ++blk; + slot = blk * _MAP_BITS + base; + } + else + slot = low; + + while(valid(slot)) ++slot; + return slot; +} + +int MChunk::first_index() const +{ + if (empty()) return fence; + int slot; + if (low == base) + { + int blk = 0; + while (map[blk] == 0) ++blk; + slot = blk * _MAP_BITS + base; + } + else + slot = low; + + while(!valid(slot)) ++slot; + return slot; +} + +int MChunk::last_index() const +{ + if (empty()) return low - 1; + int slot; + if (top == fence) + { + int blk = (top - base) / _MAP_BITS; + while (map[blk] == 0) --blk; + slot = blk * _MAP_BITS + base + _MAP_BITS - 1; + } + else + slot = fence - 1; + + while(!valid(slot)) --slot; + return slot; +} + + +int MChunk:: OK() const +{ + int v = data != 0; // have some data + v &= map != 0; // and a map + v &= base <= low; // ok, index-wise + v &= low <= fence; + v &= fence <= top; + + v &= ((MChunk*)(nxt->prev())) == this; // and links are OK + v &= ((MChunk*)(prv->next())) == this; + + int bitcount = 0; // and unused count correct + for (int i = low; i < fence; ++i) if (!valid(i)) ++bitcount; + v &= unused == bitcount; + + if (!v) error("invariant failure"); + return(v); +} + +* MChunk::succ(* p) const +{ + int i = ((int) p - (int) data) / sizeof() + base + 1; + if (p == 0 || i < low) return 0; + while (i < fence && !valid(i)) ++i; + if (i >= fence) return 0; + return pointer_to(i); +} + +* MChunk::pred(* p) const +{ + int i = ((int) p - (int) data) / sizeof() + base - 1; + if (p == 0 || i >= fence) return 0; + while (i >= low && !valid(i)) --i; + if (i < low) return 0; + return pointer_to(i); +} + +* MChunk::first_pointer() const +{ + if (empty()) return 0; + int slot; + if (low == base) + { + int blk = 0; + while (map[blk] == 0) ++blk; + slot = blk * _MAP_BITS + base; + } + else + slot = low; + + while(!valid(slot)) ++slot; + return pointer_to(slot); +} + +* MChunk::last_pointer() const +{ + if (empty()) return 0; + int slot; + if (top == fence) + { + int blk = (top - base) / _MAP_BITS; + while (map[blk] == 0) --blk; + slot = blk * _MAP_BITS + base + _MAP_BITS - 1; + } + else + slot = fence - 1; + + while(!valid(slot)) --slot; + return pointer_to(slot); +} + +MPlex:: MPlex() +{ + unused = 0; + lo = fnc = 0; + csize = DEFAULT_INITIAL_CAPACITY; + * data = new [csize]; + hd = ch = new MChunk(data, lo, lo, fnc, lo+csize); +} + +MPlex:: MPlex(int chunksize) +{ + if (chunksize == 0) error("invalid constructor specification"); + unused = 0; + lo = fnc = 0; + if (chunksize > 0) + { + csize = chunksize; + * data = new [csize]; + hd = ch = new MChunk(data, lo, lo, fnc, csize); + } + else + { + csize = -chunksize; + * data = new [csize]; + hd = ch = new MChunk(data, chunksize, lo, fnc, fnc); + } +} + + +MPlex:: MPlex(int l, int chunksize) +{ + if (chunksize == 0) error("invalid constructor specification"); + unused = 0; + lo = fnc = l; + if (chunksize > 0) + { + csize = chunksize; + * data = new [csize]; + hd = ch = new MChunk(data, lo, lo, fnc, csize+lo); + } + else + { + csize = -chunksize; + * data = new [csize]; + hd = ch = new MChunk(data, chunksize+lo, lo, fnc, fnc); + } +} + + +void MPlex::make_initial_chunks(int up) +{ + int need = fnc - lo; + hd = 0; + if (up) + { + int l = lo; + do + { + int sz; + if (need >= csize) + sz = csize; + else + sz = need; + * data = new [csize]; + MChunk* h = new MChunk(data, l, l, l+sz, l+csize); + if (hd != 0) + h->link_to_next(hd); + else + hd = h; + l += sz; + need -= sz; + } while (need > 0); + } + else + { + int hi = fnc; + do + { + int sz; + if (need >= csize) + sz = csize; + else + sz = need; + * data = new [csize]; + MChunk* h = new MChunk(data, hi-csize, hi-sz, hi, hi); + if (hd != 0) + h->link_to_next(hd); + hd = h; + hi -= sz; + need -= sz; + } while (need > 0); + } + ch = (MChunk*) hd; +} + +MPlex:: MPlex(int l, int hi, const initval, int chunksize) +{ + lo = l; + fnc = hi + 1; + if (chunksize == 0) + { + csize = fnc - l; + make_initial_chunks(1); + } + else if (chunksize < 0) + { + csize = -chunksize; + make_initial_chunks(0); + } + else + { + csize = chunksize; + make_initial_chunks(1); + } + unused = fnc - lo; + for (int i=lo; iMPlex::MPlex(const MPlex& a) +{ + lo = a.lo; + fnc = a.fnc; + csize = a.csize; + unused = fnc - lo; + hd = 0; + const IChunk* p = a.hd; + do + { + * data = new [p->size()]; + MChunk* h = new MChunk(data, p->base_index(), + p->low_index(), p->fence_index(), p->top_index()); + if (hd != 0) + h->link_to_next(hd); + else + hd = h; + p = p->next(); + } while (p != a.hd); + ch = (MChunk*) hd; + for (int i = a.low(); i < a.fence(); a.next(i)) + { + undel_index(i); + (*this)[i] = a[i]; + } +} + +void MPlex::operator= (const MPlex& a) +{ + if (&a != this) + { + invalidate(); + lo = a.lo; + fnc = a.fnc; + csize = a.csize; + unused = fnc - lo; + hd = 0; + const IChunk* p = a.hd; + do + { + * data = new [p->size()]; + MChunk* h = new MChunk(data, p->base_index(), + p->low_index(), p->fence_index(), + p->top_index()); + if (hd != 0) + h->link_to_next(hd); + else + hd = h; + p = p->next(); + } while (p != a.hd); + ch = (MChunk*) hd; + for (int i = a.low(); i < a.fence(); a.next(i)) + { + undel_index(i); + (*this)[i] = a[i]; + } + } +} + +int MPlex::valid(int idx) const +{ + const MChunk* tail = (MChunk*)tl(); + const MChunk* t = ch; + while (idx >= t->fence_index()) + { + if (t == tail) return 0; + t = ((MChunk*)(t->next())); + } + while (idx < t->low_index()) + { + if (t == (MChunk*)(hd)) return 0; + t = ((MChunk*)(t->prev())); + } + set_cache(t); + return t->MChunk::valid_index(idx); +} + +void MPlex::cache(int idx) const +{ + const MChunk* tail = (MChunk*)tl(); + const MChunk* t = ch; + while (idx >= t->fence_index()) + { + if (t == tail) index_error(); + t = ((MChunk*)(t->next())); + } + while (idx < t->low_index()) + { + if (t == (MChunk*)hd) index_error(); + t = ((MChunk*)(t->prev())); + } + if (!t->MChunk::valid_index(idx)) index_error(); + set_cache(t); +} + +void MPlex::cache(const * p) const +{ + const MChunk* old = ch; + const MChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = ((MChunk*)(t->next())); + if (t == old) index_error(); + } + if (!t->MChunk::valid_pointer(p)) index_error(); + set_cache(t); +} + +int MPlex::owns(Pix px) const +{ + * p = (*)px; + const MChunk* old = ch; + const MChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = ((MChunk*)(t->next())); + if (t == old) return 0; + } + set_cache(t); + return t->MChunk::valid_pointer(p); +} + +int MPlex::add_high(const elem) +{ + MChunk* t = ((MChunk*) tl()); + + if (!t->can_grow_high()) + { + * data = new [csize]; + t = (new MChunk(data, fnc,fnc,fnc,fnc+csize)); + t->link_to_prev(tl()); + } + + *((t->MChunk::grow_high())) = elem; + set_cache(t); + return fnc++; +} + +int MPlex::add_low (const elem) +{ + MChunk* t = ((MChunk*) hd); + if (!t->can_grow_low()) + { + * data = new [csize]; + hd = new MChunk(data, lo-csize, lo, lo, lo); + hd->link_to_next(t); + t = ((MChunk*) hd); + } + + *((t->MChunk::grow_low())) = elem; + set_cache(t); + return --lo; +} + +void MPlex::adjust_bounds() +{ + MChunk* t = ((MChunk*) tl()); + + // clean up tail + + t->reset_high(); + while (t->MChunk::empty() && !one_chunk()) + { + MChunk* pred = (MChunk*)(t->prev()); + del_chunk(t); + pred->reset_high(); + t = (pred); + } + if (one_chunk()) + t->reset_high(); + + int oldfnc = fnc; + fnc = t->fence_index(); + unused -= oldfnc - fnc; + + // and head.. + t = ((MChunk*) hd); + t->reset_low(); + while (t->MChunk::empty() && !one_chunk()) + { + hd = (MChunk*)(t->next()); + del_chunk(t); + t = ((MChunk*) hd); + t->reset_low(); + } + + int oldlo = lo; + lo = t->low_index(); + unused -= lo - oldlo; + + + set_cache(t); +} + +int MPlex::del_high () +{ + if (empty()) empty_error(); + MChunk* t = ((MChunk*) tl()); + while (t->MChunk::empty() && !one_chunk()) // possible stragglers + { + MChunk* pred = (MChunk*)(t->prev()); + del_chunk(t); + pred->reset_high(); + t = (pred); + } + t->MChunk::shrink_high(); + while (t->MChunk::empty() && !one_chunk()) + { + MChunk* pred = (MChunk*)(t->prev()); + del_chunk(t); + pred->reset_high(); + t = (pred); + } + int oldfnc = fnc; + fnc = t->fence_index(); + unused -= oldfnc - fnc - 1; + set_cache(t); + return fnc - 1; +} + +int MPlex::del_low () +{ + if (empty()) empty_error(); + MChunk* t = ((MChunk*) hd); + while (t->MChunk::empty() && !one_chunk()) + { + hd = (MChunk*)(t->next()); + del_chunk(t); + t = ((MChunk*) hd); + t->reset_low(); + } + t->MChunk::shrink_low(); + while (t->MChunk::empty() && !one_chunk()) + { + hd = (MChunk*)(t->next()); + del_chunk(t); + t = ((MChunk*) hd); + t->reset_low(); + } + int oldlo = lo; + lo = t->low_index(); + unused -= lo - oldlo - 1; + set_cache(t); + return lo; +} + +int MPlex::add(const elem) +{ + if (unused == 0) + return add_high(elem); + + for(MChunk* t = ch; + t->unused_indices() == 0; + t = (MChunk*)(t->prev())) + ; + + int i = t->unused_index(); + set_cache(t); + undel_index(i); + (*this)[i] = elem; + return i; +} + +int MPlex::unused_index() const +{ + if (unused == 0) index_error(); + + for(MChunk* t = ch; + t->unused_indices() == 0; + t = (MChunk*)(t->prev())) + ; + + set_cache(t); + return t->unused_index(); +} + +Pix MPlex::unused_Pix() const +{ + if (unused == 0) return 0; + + for(MChunk* t = ch; + t->unused_indices() == 0; + t = (MChunk*)(t->prev())) + ; + + set_cache(t); + return t->pointer_to(t->unused_index()); +} + +int MPlex::del_index(int idx) +{ + if (idx < lo || idx >= fnc) index_error(); + if (MPlex::valid(idx)) + { + ++unused; + ch->MChunk::del(idx); + return 1; + } + else + return 0; +} + +int MPlex::dopred(int idx) const +{ + + if (idx >= fnc) idx = fnc; + if (idx <= lo) return lo - 1; + + const MChunk* t = ch; + + while (idx > t->fence_index()) + { + t = ((MChunk*)(t->next())); + } + while (idx <= t->low_index()) + { + t = ((MChunk*)(t->prev())); + } + int i = t->MChunk::pred(idx); + while (i < t->low_index() && i >= lo) + { + t = ((MChunk*)(t->prev())); + i = t->MChunk::last_index(); + } + set_cache(t); + return i; +} + + +int MPlex::dosucc(int idx) const +{ + if (idx < lo) idx = lo; + if (idx >= fnc - 1) return fnc; + + const MChunk* t = ch; + while (idx >= t->fence_index()) + { + t = ((MChunk*)(t->next())); + } + while (idx < t->low_index()) + { + t = ((MChunk*)(t->prev())); + } + int i = t->MChunk::succ(idx); + while (i >= t->fence_index() && i < fnc) + { + t = (MChunk*)(t->next()); + i = t->MChunk::first_index(); + } + set_cache(t); + return i; +} + +void MPlex::prev(Pix& i) const +{ + if (i == 0) return; + + * p = (*) i; + const MChunk* old = ch; + const MChunk* t = ch; + + while (!t->actual_pointer(p)) + { + t = ((MChunk*)(t->prev())); + if (t == old) + { + i = 0; + return; + } + } + * q = t->MChunk::pred(p); + while (q == 0 && t != (MChunk*)hd) + { + t = ((MChunk*)(t->prev())); + q = t->MChunk::last_pointer(); + } + + i = Pix(q); + set_cache(t); + return; +} + +void MPlex::next(Pix& i) const +{ + if (i == 0) return; + + * p = (*) i; + const MChunk* tail = (MChunk*)(tl()); + const MChunk* old = ch; + const MChunk* t = ch; + + while (!t->actual_pointer(p)) + { + t = ((MChunk*)(t->next())); + if (t == old) + { + i = 0; + return; + } + } + * q = t->MChunk::succ(p); + while (q == 0 && t != tail) + { + t = ((MChunk*)(t->next())); + q = t->MChunk::first_pointer(); + } + + i = Pix(q); + set_cache(t); + return; +} + + +void MPlex::undel_index(int idx) +{ + if (idx < lo || idx >= fnc) index_error(); + + MChunk* t = ch; + while (idx >= t->fence_index()) + { + t = ((MChunk*)(t->next())); + } + while (idx < t->low_index()) + { + t = ((MChunk*)(t->prev())); + } + int was_present = t->MChunk::undel(idx); + if (!was_present) + { + --unused; + } + set_cache(t); + return; +} + +void MPlex::clear() +{ + if (fnc != lo) + { + MChunk* t = ((MChunk*)tl()); + while (t != hd) + { + MChunk* prv = (MChunk*)(t->prev()); + del_chunk(t); + t = prv; + } + t->MChunk::clear(lo); + set_cache(t); + fnc = lo; + unused = 0; + } +} + +int MPlex::OK () const +{ + int v = hd != 0; // at least one chunk + + int found_ch = 0; // to make sure ch is in list; + + int count = 0; // to count unused slots + + const MChunk* t = (MChunk*)(hd); + + int gap = t->low_index() - lo; + v &= gap == 0; // hd lo not less than lo. + count += gap; + + for (;;) + { + if (t == ch) ++found_ch; + v &= t->MChunk::OK(); // each chunk is OK + count += t->unused_indices(); + if (t == (MChunk*)(tl())) + break; + else // and has indices less than succ + { + gap = t->next()->base_index() - t->top_index(); + v &= gap == 0; + count += gap; + + if (t != (MChunk*)hd) // internal chunks can't grow + v &= !t->can_grow_low() && !t->can_grow_high(); + + t = (const MChunk*)(t->next()); + } + } + gap = fnc - t->fence_index(); + v &= gap == 0; + count += gap; + + v &= count == unused; // chunk counts agree with plex + + v &= found_ch == 1; + if (!v) error("invariant failure"); + return v; +} + diff --git a/gnu/lib/libg++/g++-include/gen/MPlex.hP b/gnu/lib/libg++/g++-include/gen/MPlex.hP new file mode 100644 index 0000000000..8bf78d13a1 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/MPlex.hP @@ -0,0 +1,414 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _MPlex_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _MPlex_h 1 + + +#include ".Plex.h" + + +// Number of bits per long, used in MChunk bit map operations + +#define _MAP_BITS 32 + + +class MChunk : public IChunk +{ +protected: + + unsigned long* map; // bitmap of slots + int unused; // number of unused internal slots + + void mark(int); // bitmap operations + void free(int); + int valid(int) const; + +public: + + MChunk(* d, // ptr to array of elements + int base_idx, // initial indices + int low_idx, // & initially clear map + int fence_idx, + int top_idx); + + ~MChunk(); + +// virtuals + + int first_index() const; + int last_index() const; + int succ(int idx) const; + int pred(int idx) const; + * first_pointer() const; + * last_pointer() const; + * succ(*) const; + * pred(*) const; + int empty() const; + int full() const; + int valid_index(int i) const; + int valid_pointer(const * p) const; + * grow_high (); + * grow_low (); + void shrink_high (); + void shrink_low (); + void clear(int); + void cleardown(int); + int OK() const; + +// extensions + + int unused_indices() const; // how many free slot in low..fence? + + int unused_index() const; // return index of free slot + + int del(int i); // delete data indexed by i + // return true if was present + int undel(int idx); // un-delete data indexed by i + // return true if already present + + void reset_low(); // reset low = lowest valid index; + void reset_high(); // same for high + +}; + + +class MPlex: public Plex +{ + MChunk* ch; // cached chunk + int unused; // # of free slots between low & fence + + void make_initial_chunks(int up = 1); + void cache(int idx) const; + void cache(const * p) const; + int dopred(int) const; + int dosucc(int) const; + + void set_cache(const MChunk* t) const; // logically, + // not physically const + +public: + MPlex(); // set low = 0; + // fence = 0; + // csize = default + + MPlex(int ch_size); // low = 0; + // fence = 0; + // csize = ch_size + + MPlex(int lo, // low = lo; + int ch_size); // fence=lo + // csize = ch_size + + MPlex(int lo, // low = lo + int hi, // fence = hi+1 + const initval,// fill with initval, + int ch_size = 0); // csize= ch_size + // or fence-lo if 0 + + MPlex(const MPlex&); + + void operator= (const MPlex&); + +// virtuals + + & high_element (); + & low_element (); + const & high_element () const; + const & low_element () const; + + Pix first() const; + Pix last() const ; + void prev(Pix& ptr) const; + void next(Pix& ptr) const; + int owns(Pix p) const; + & operator () (Pix p); + const & operator () (Pix p) const; + + int low() const; + int high() const; + int valid(int idx) const; + void prev(int& idx) const; + void next(int& x) const; + & operator [] (int index); + const & operator [] (int index) const; + + int Pix_to_index(Pix p) const; + Pix index_to_Pix(int idx) const; + + int can_add_high() const; + int can_add_low() const; + int full() const; + + int add_high(const elem); + int del_high (); + int add_low (const elem); + int del_low (); + void clear(); + + int OK () const; + +// extensions + + int count() const; // # valid elements + int available() const; // # deleted elements + + int unused_index()const; // return index of a deleted elem + Pix unused_Pix() const; // return Pix of a deleted elem + + int del_index(int idx); // logically delete at idx; + // return true if was present + int del_Pix(Pix p); // delete at p + + void undel_index(int idx); // undelete at idx; + void undel_Pix(Pix p); // undelete at p; + + void adjust_bounds(); // reset lo, hi to lowest & + // highest valid indices + + int add(const elem); // add anywhere +}; + + +inline MChunk:: ~MChunk() +{ + delete map; +} + +inline void MChunk::mark(int idx) +{ + unsigned int i = idx - base; + map[i / _MAP_BITS] |= 1 << (i & (_MAP_BITS - 1)); +} + +inline void MChunk::free(int idx) +{ + unsigned int i = idx - base; + map[i / _MAP_BITS] &= ~(1 << (i & (_MAP_BITS - 1))); +} + +inline int MChunk::valid(int idx) const +{ + unsigned int i = idx - base; + return map[i / _MAP_BITS] & (1 << (i & (_MAP_BITS - 1))); +} + +inline int MChunk:: valid_index(int i) const +{ + return i >= low && i < fence && valid(i); +} + +inline int MChunk:: valid_pointer(const * p) const +{ + int i = ((int)p - (int)data) / sizeof(); + return i >= 0 && i < (fence - base) && + (map[(unsigned)i / _MAP_BITS] & (1 << (i & (_MAP_BITS - 1)))); +} + +inline int MChunk::empty() const +{ + return fence - low - unused == 0; +} + +inline int MChunk::full() const +{ + return unused + (top - fence) + (low - base) == 0; +} + +inline int MChunk::succ(int idx) const +{ + int i = (idx < low)? low : idx + 1; + while (i < fence && !valid(i)) ++i; + return i; +} + +inline int MChunk::pred(int idx) const +{ + int i = (idx > fence)? (fence - 1) : idx - 1; + while (i >= low && !valid(i)) --i; + return i; +} + +inline int MChunk::unused_indices() const +{ + return unused; +} + +inline * MChunk:: grow_high () +{ + if (!can_grow_high()) full_error(); + mark(fence); + return &(data[fence++ - base]); +} + +inline * MChunk:: grow_low () +{ + if (!can_grow_low()) full_error(); + mark(--low); + return &(data[low - base]); +} + +inline void MChunk::reset_low() +{ + while (low < fence && !valid(low)) + { + --unused; + ++low; + } +} + +inline void MChunk::reset_high() +{ + while (fence > low && !valid(fence - 1)) + { + --unused; + --fence; + } +} + +inline int MPlex::full () const +{ + return 0; +} + +inline int MPlex::can_add_high() const +{ + return 1; +} + +inline int MPlex::can_add_low() const +{ + return 1; +} + +inline int MPlex::available() const +{ + return unused; +} + +inline int MPlex::count() const +{ + return fnc - lo - unused; +} + +inline void MPlex::set_cache(const MChunk* t) const +{ + ((MPlex*)(this))->ch = (MChunk*)t; +} + +inline & MPlex:: operator [] (int idx) +{ + if (!ch->MChunk::valid_index(idx)) cache(idx); + return * (ch->pointer_to(idx)); +} + +inline const & MPlex:: operator [] (int idx) const +{ + if (!ch->MChunk::valid_index(idx)) cache(idx); + return * ((const *)(ch->pointer_to(idx))); +} + +inline int MPlex::Pix_to_index(Pix p) const +{ + if (!ch->MChunk::valid_pointer((*)p)) cache((*)p); + return ch->index_of((*)p); +} + +inline int MPlex::high() const +{ + return (((const MChunk*)tl())->MChunk::valid_index(fnc-1)) ? + fnc-1 : dopred(fnc-1); +} + +inline int MPlex::low() const +{ + return (((const MChunk*)hd)->MChunk::valid_index(lo))? lo : dosucc(lo); +} + +inline & MPlex::low_element () +{ + return (*this)[low()]; +} + +inline const & MPlex::low_element () const +{ + return (*this)[low()]; +} + +inline & MPlex::high_element () +{ + return (*this)[high()]; +} + +inline const & MPlex::high_element () const +{ + return (*this)[high()]; +} + +inline Pix MPlex::index_to_Pix(int idx) const +{ + if (!ch->MChunk::valid_index(idx)) cache(idx); + return Pix(ch->pointer_to(idx)); +} + +inline void MPlex::next(int& idx) const +{ + idx = (ch->MChunk::valid_index(idx+1))? idx+1 : dosucc(idx); +} + +inline void MPlex::prev(int& idx) const +{ + idx = (ch->MChunk::valid_index(idx-1))? idx-1 : dopred(idx); +} + +inline Pix MPlex::first() const +{ + return index_to_Pix(low()); +} + +inline Pix MPlex::last() const +{ + return index_to_Pix(high()); +} + + +inline void MPlex::undel_Pix(Pix p) +{ + undel_index(Pix_to_index(p)); +} + +inline int MPlex::del_Pix(Pix p) +{ + return del_index(Pix_to_index(p)); +} + +inline & MPlex:: operator () (Pix p) +{ + return *((*)p); +} + +inline const & MPlex:: operator () (Pix p) const +{ + return *((const *)p); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/Map.ccP b/gnu/lib/libg++/g++-include/gen/Map.ccP new file mode 100644 index 0000000000..03bb4b84f0 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/Map.ccP @@ -0,0 +1,58 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include "..Map.h" + + +Pix Map::seek( item) +{ + for (Pix i = first(); i != 0 && !(EQ(key(i), item)); next(i)); + return i; +} + +int Map::owns(Pix idx) +{ + if (idx == 0) return 0; + for (Pix i = first(); i; next(i)) if (i == idx) return 1; + return 0; +} + +void Map::clear() +{ + Pix i = first(); + while (i != 0) + { + del(key(i)); + i = first(); + } +} + +int Map::contains ( item) +{ + return seek(item) != 0; +} + + +void Map::error(const char* msg) +{ + (*lib_error_handler)("Map", msg); +} diff --git a/gnu/lib/libg++/g++-include/gen/Map.hP b/gnu/lib/libg++/g++-include/gen/Map.hP new file mode 100644 index 0000000000..716a17fdd9 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/Map.hP @@ -0,0 +1,87 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _Map_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Map_h 1 + +#include +#include ".defs.h" + +class Map +{ +protected: + int count; + def; + +public: + Map( dflt); + virtual ~Map(); + + int length(); // current number of items + int empty(); + + virtual int contains( key); // is key mapped? + + virtual void clear(); // delete all items + + virtual & operator [] ( key) = 0; // access contents by key + + virtual void del( key) = 0; // delete entry + + virtual Pix first() = 0; // Pix of first item or 0 + virtual void next(Pix& i) = 0; // advance to next or 0 + virtual & key(Pix i) = 0; // access key at i + virtual & contents(Pix i) = 0; // access contents at i + + virtual int owns(Pix i); // is i a valid Pix ? + virtual Pix seek( key); // Pix of key + + & dflt(); // access default val + + void error(const char* msg); + virtual int OK() = 0; // rep invariant +}; + + +inline Map::~Map() {} + +inline int Map::length() +{ + return count; +} + +inline int Map::empty() +{ + return count == 0; +} + +inline & Map::dflt() +{ + return def; +} + +inline Map::Map( dflt) :def(dflt) +{ + count = 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/OSLBag.ccP b/gnu/lib/libg++/g++-include/gen/OSLBag.ccP new file mode 100644 index 0000000000..78398192bc --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/OSLBag.ccP @@ -0,0 +1,196 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".OSLBag.h" + + +Pix OSLBag::seek( item, Pix i) +{ + if (i == 0) i = p.first(); else next(i); + for (; i != 0; p.next(i)) + { + int cmp = CMP(item, p(i)); + if (cmp == 0) + return i; + else if (cmp < 0) + return 0; + } + return 0; +} + +int OSLBag::nof( item) +{ + int n = 0; + for (Pix i = p.first(); i != 0; p.next(i)) + { + int cmp = CMP(item, p(i)); + if (cmp == 0) + ++n; + else if (cmp < 0) + break; + } + return n; +} + +Pix OSLBag::add( item) +{ + Pix i = p.first(); + if (i == 0) + { + ++count; + return p.prepend(item); + } + int cmp = CMP(item, p(i)); + if (cmp <= 0) + { + ++count; + return p.prepend(item); + } + else + { + Pix trail = i; + p.next(i); + for (;;) + { + if (i == 0) + { + ++count; + return p.append(item); + } + cmp = CMP(item, p(i)); + if (cmp <= 0) + { + ++count; + return p.ins_after(trail, item); + } + else + { + trail = i; + p.next(i); + } + } + } +} + +void OSLBag::del( item) +{ + Pix i = p.first(); + if (i == 0) + return; + int cmp = CMP(item, p(i)); + if (cmp < 0) + return; + else if (cmp == 0) + { + --count; + p.del_front(); + } + else + { + Pix trail = i; + p.next(i); + while (i != 0) + { + cmp = CMP(item, p(i)); + if (cmp < 0) + return; + else if (cmp == 0) + { + --count; + p.del_after(trail); + return; + } + else + { + trail = i; + p.next(i); + } + } + } +} + +void OSLBag::remove( item) +{ + Pix i = p.first(); + if (i == 0) + return; + int cmp = CMP(item, p(i)); + if (cmp < 0) + return; + else if (cmp == 0) + { + do + { + --count; + p.del_front(); + i = p.first(); + } while (i != 0 && EQ(item, p(i))); + } + else + { + Pix trail = i; + p.next(i); + while (i != 0) + { + cmp = CMP(item, p(i)); + if (cmp < 0) + return; + else if (cmp == 0) + { + do + { + --count; + p.del_after(trail); + i = trail; + next(i); + } while (i != 0 && EQ(item, p(i))); + return; + } + else + { + trail = i; + p.next(i); + } + } + } +} + +int OSLBag::OK() +{ + int v = p.OK(); + v &= count == p.length(); + Pix trail = p.first(); + if (trail == 0) + v &= count == 0; + else + { + Pix i = trail; next(i); + while (i != 0) + { + v &= CMP(p(trail), p(i)) <= 0; + trail = i; + next(i); + } + } + if (!v) error("invariant failure"); + return v; +} + diff --git a/gnu/lib/libg++/g++-include/gen/OSLBag.hP b/gnu/lib/libg++/g++-include/gen/OSLBag.hP new file mode 100644 index 0000000000..de4d67cf9a --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/OSLBag.hP @@ -0,0 +1,91 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _OSLBag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _OSLBag_h 1 + +#include ".Bag.h" +#include ".SLList.h" + +class OSLBag : public Bag +{ +protected: + SLList p; + +public: + OSLBag(); + OSLBag(const OSLBag&); + + Pix add( item); + void del( item); + void remove(item); + + int contains( item); + int nof( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item, Pix from = 0); + + int OK(); +}; + + +inline OSLBag::OSLBag() : p() { count = 0; } + +inline OSLBag::OSLBag(const OSLBag& s) : p(s.p) { count = s.count; } + +inline Pix OSLBag::first() +{ + return p.first(); +} + +inline void OSLBag::next(Pix & idx) +{ + p.next(idx); +} + +inline & OSLBag::operator ()(Pix idx) +{ + return p(idx); +} + +inline void OSLBag::clear() +{ + count = 0; p.clear(); +} + +inline int OSLBag::owns (Pix idx) +{ + return p.owns(idx); +} + +inline int OSLBag::contains( item) +{ + return seek(item) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/OSLSet.ccP b/gnu/lib/libg++/g++-include/gen/OSLSet.ccP new file mode 100644 index 0000000000..bfd32ae954 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/OSLSet.ccP @@ -0,0 +1,321 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".OSLSet.h" + + +Pix OSLSet::seek( item) +{ + for (Pix i = p.first(); i != 0; p.next(i)) + { + int cmp = CMP(item, p(i)); + if (cmp == 0) + return i; + else if (cmp < 0) + return 0; + } + return 0; +} + +Pix OSLSet::add( item) +{ + Pix i = p.first(); + if (i == 0) + { + ++count; + return p.prepend(item); + } + int cmp = CMP(item, p(i)); + if (cmp == 0) + return i; + else if (cmp < 0) + { + ++count; + return p.prepend(item); + } + else + { + Pix trail = i; + p.next(i); + for (;;) + { + if (i == 0) + { + ++count; + return p.append(item); + } + cmp = CMP(item, p(i)); + if (cmp == 0) + return i; + else if (cmp < 0) + { + ++count; + return p.ins_after(trail, item); + } + else + { + trail = i; + p.next(i); + } + } + } +} + +void OSLSet::del( item) +{ + Pix i = p.first(); + if (i == 0) + return; + int cmp = CMP(item, p(i)); + if (cmp < 0) + return; + else if (cmp == 0) + { + --count; + p.del_front(); + } + else + { + Pix trail = i; + p.next(i); + while (i != 0) + { + cmp = CMP(item, p(i)); + if (cmp < 0) + return; + else if (cmp == 0) + { + --count; + p.del_after(trail); + return; + } + else + { + trail = i; + p.next(i); + } + } + } +} + + +int OSLSet::operator <= (OSLSet& b) +{ + if (count > b.count) return 0; + Pix i = first(); + Pix j = b.first(); + for (;;) + { + if (i == 0) + return 1; + else if (j == 0) + return 0; + int cmp = CMP(p(i), b.p(j)); + if (cmp == 0) + { + next(i); b.next(j); + } + else if (cmp < 0) + return 0; + else + b.next(j); + } +} + +int OSLSet::operator == (OSLSet& b) +{ + if (count != b.count) return 0; + if (count == 0) return 1; + Pix i = p.first(); + Pix j = b.p.first(); + while (i != 0) + { + if (!EQ(p(i),b.p(j))) return 0; + next(i); + b.next(j); + } + return 1; +} + + +void OSLSet::operator |= (OSLSet& b) +{ + if (&b == this || b.count == 0) + return; + else + { + Pix j = b.p.first(); + Pix i = p.first(); + Pix trail = 0; + for (;;) + { + if (j == 0) + return; + else if (i == 0) + { + for (; j != 0; b.next(j)) + { + ++count; + p.append(b.p(j)); + } + return; + } + int cmp = CMP(p(i), b.p(j)); + if (cmp <= 0) + { + if (cmp == 0) b.next(j); + trail = i; + next(i); + } + else + { + ++count; + if (trail == 0) + trail = p.prepend(b.p(j)); + else + trail = p.ins_after(trail, b.p(j)); + b.next(j); + } + } + } +} + + +void OSLSet::operator -= (OSLSet& b) +{ + if (&b == this) + clear(); + else if (count != 0 && b.count != 0) + { + Pix i = p.first(); + Pix j = b.p.first(); + Pix trail = 0; + for (;;) + { + if (j == 0 || i == 0) + return; + int cmp = CMP(p(i), b.p(j)); + if (cmp == 0) + { + --count; + b.next(j); + if (trail == 0) + { + p.del_front(); + i = p.first(); + } + else + { + next(i); + p.del_after(trail); + } + } + else if (cmp < 0) + { + trail = i; + next(i); + } + else + b.next(j); + } + } +} + +void OSLSet::operator &= (OSLSet& b) +{ + if (b.count == 0) + clear(); + else if (&b != this && count != 0) + { + Pix i = p.first(); + Pix j = b.p.first(); + Pix trail = 0; + for (;;) + { + if (i == 0) + return; + else if (j == 0) + { + if (trail == 0) + { + p.clear(); + count = 0; + } + else + { + while (i != 0) + { + --count; + next(i); + p.del_after(trail); + } + } + return; + } + int cmp = CMP(p(i), b.p(j)); + + if (cmp == 0) + { + trail = i; + next(i); + b.next(j); + } + else if (cmp < 0) + { + --count; + if (trail == 0) + { + p.del_front(); + i = p.first(); + } + else + { + next(i); + p.del_after(trail); + } + } + else + b.next(j); + } + } +} + + +int OSLSet::OK() +{ + int v = p.OK(); + v &= count == p.length(); + Pix trail = p.first(); + if (trail == 0) + v &= count == 0; + else + { + Pix i = trail; next(i); + while (i != 0) + { + v &= CMP(p(trail), p(i)) < 0; + trail = i; + next(i); + } + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/OSLSet.hP b/gnu/lib/libg++/g++-include/gen/OSLSet.hP new file mode 100644 index 0000000000..bf3707f6c7 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/OSLSet.hP @@ -0,0 +1,101 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _OSLSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _OSLSet_h 1 + +#include ".Set.h" +#include ".SLList.h" + +class OSLSet : public Set +{ +protected: + SLList p; + +public: + OSLSet(); + OSLSet(const OSLSet&); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item); + + void operator |= (OSLSet& b); + void operator -= (OSLSet& b); + void operator &= (OSLSet& b); + + int operator == (OSLSet& b); + int operator != (OSLSet& b); + int operator <= (OSLSet& b); + + int OK(); +}; + + +inline OSLSet::OSLSet() : p() { count = 0; } + +inline OSLSet::OSLSet(const OSLSet& s) : p(s.p) { count = s.count; } + +inline Pix OSLSet::first() +{ + return p.first(); +} + +inline void OSLSet::next(Pix & idx) +{ + p.next(idx); +} + +inline & OSLSet::operator ()(Pix idx) +{ + return p(idx); +} + +inline void OSLSet::clear() +{ + count = 0; p.clear(); +} + +inline int OSLSet::contains ( item) +{ + return seek(item) != 0; +} + +inline int OSLSet::owns (Pix idx) +{ + return p.owns(idx); +} + +inline int OSLSet::operator != (OSLSet& b) +{ + return !(*this == b); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/OXPBag.ccP b/gnu/lib/libg++/g++-include/gen/OXPBag.ccP new file mode 100644 index 0000000000..6619e25ea1 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/OXPBag.ccP @@ -0,0 +1,221 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".OXPBag.h" + + +Pix OXPBag::seek( item, Pix i) +{ + if (i == 0) + { + int l = p.low(); + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + int cmp = CMP(item, p[mid]); + if (cmp == 0) + { + while (mid > p.low() && EQ(item, p[mid - 1])) --mid; + return p.index_to_Pix(mid); + } + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } + return 0; + } + int cmp = CMP(item, p(i)); + if (cmp == 0) + { + next(i); + return (EQ(item, p(i)))? i : 0; + } + else if (cmp < 0) + { + int ind = p.Pix_to_index(i); + int l = ind; + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + cmp = CMP(item, p[mid]); + if (cmp == 0) + { + while (mid > ind && EQ(item, p[mid - 1])) --mid; + return p.index_to_Pix(mid); + } + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } + return 0; + } + else + return 0; +} + +int OXPBag::nof( item) +{ + int l = p.low(); + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + int cmp = CMP(item, p[mid]); + if (cmp == 0) + { + l = h = mid; + while (l > p.low() && EQ(item, p[l - 1])) --l; + while (h < p.high() && EQ(item, p[h + 1])) ++h; + return h - l + 1; + } + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } + return 0; +} + +Pix OXPBag::add( item) +{ + if (count == 0) + { + ++count; + return p.index_to_Pix(p.add_high(item)); + } + int l = p.low(); + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + int cmp = CMP(item, p[mid]); + if (cmp == 0) + { + l = mid; + break; + } + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } + // add on whichever side is shortest + ++count; + if (l == p.fence()) + return p.index_to_Pix(p.add_high(item)); + else if (l == p.low()) + return p.index_to_Pix(p.add_low(item)); + else + { + if (p.high() - l < l - p.low()) + { + h = p.add_high(p.high_element()); + for (int i = h - 1; i > l; --i) p[i] = p[i-1]; + } + else + { + --l; + h = p.add_low(p.low_element()); + for (int i = h + 1; i < l; ++i) p[i] = p[i+1]; + } + p[l] = item; + return p.index_to_Pix(l); + } +} + +void OXPBag::del( item) +{ + int l = p.low(); + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + int cmp = CMP(item, p[mid]); + if (cmp == 0) + { + --count; + if (p.high() - mid < mid - p.low()) + { + for (int i = mid; i < p.high(); ++i) p[i] = p[i+1]; + p.del_high(); + } + else + { + for (int i = mid; i > p.low(); --i) p[i] = p[i-1]; + p.del_low(); + } + return; + } + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } +} + +void OXPBag::remove( item) +{ + int l = p.low(); + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + int cmp = CMP(item, p[mid]); + if (cmp == 0) + { + l = h = mid; + while (l > p.low() && EQ(item, p[l - 1])) --l; + while (h < p.high() && EQ(item, p[h + 1])) ++h; + int n = h - l + 1; + count -= n; + if (p.high() - h < l - p.low()) + { + h = p.high() - n; + for (int i = l; i <= h; ++i) p[i] = p[i+n]; + while (n-- > 0) p.del_high(); + } + else + { + l = p.low() + n; + for (int i = h; i >= l; --i) p[i] = p[i-n]; + while (n-- > 0) p.del_low(); + } + return; + } + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } +} + +int OXPBag::OK() +{ + int v = p.OK(); + v &= count == p.length(); + for (int i = p.low(); i < p.high(); ++i) v &= CMP(p[i], p[i+1]) <= 0; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/OXPBag.hP b/gnu/lib/libg++/g++-include/gen/OXPBag.hP new file mode 100644 index 0000000000..128d4a20e4 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/OXPBag.hP @@ -0,0 +1,73 @@ +#ifndef _OXPBag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _OXPBag_h 1 + +#include ".Bag.h" +#include ".XPlex.h" + +class OXPBag : public Bag +{ +protected: + XPlex p; + +public: + OXPBag(int chunksize = DEFAULT_INITIAL_CAPACITY); + OXPBag(const OXPBag&); + + Pix add( item); + void del( item); +#undef remove + void remove(item); + int nof( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item, Pix from = 0); + + int OK(); +}; + + +inline OXPBag::OXPBag(int chunksize) + : p(chunksize) { count = 0; } + +inline OXPBag::OXPBag(const OXPBag& s) : p(s.p) { count = s.count; } + +inline Pix OXPBag::first() +{ + return p.first(); +} + +inline void OXPBag::next(Pix & idx) +{ + p.next(idx); +} + +inline & OXPBag::operator ()(Pix idx) +{ + return p(idx); +} + +inline void OXPBag::clear() +{ + count = 0; p.clear(); +} + +inline int OXPBag::owns (Pix idx) +{ + return p.owns(idx); +} + +inline int OXPBag::contains( item) +{ + return seek(item) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/OXPSet.ccP b/gnu/lib/libg++/g++-include/gen/OXPSet.ccP new file mode 100644 index 0000000000..1461195451 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/OXPSet.ccP @@ -0,0 +1,280 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".OXPSet.h" + + +Pix OXPSet::seek( item) +{ + int l = p.low(); + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + int cmp = CMP(item, p[mid]); + if (cmp == 0) + return p.index_to_Pix(mid); + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } + return 0; +} + +Pix OXPSet::add( item) +{ + if (count == 0) + { + ++count; + return p.index_to_Pix(p.add_high(item)); + } + int l = p.low(); + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + int cmp = CMP(item, p[mid]); + if (cmp == 0) + return p.index_to_Pix(mid); + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } + // add on whichever side is shortest + ++count; + if (l == p.fence()) + return p.index_to_Pix(p.add_high(item)); + else if (l == p.low()) + return p.index_to_Pix(p.add_low(item)); + else + { + if (p.fence() - l < l - p.low()) + { + h = p.add_high(p.high_element()); + for (int i = h - 1; i > l; --i) p[i] = p[i-1]; + } + else + { + --l; + h = p.add_low(p.low_element()); + for (int i = h + 1; i < l; ++i) p[i] = p[i+1]; + } + p[l] = item; + return p.index_to_Pix(l); + } +} + +void OXPSet::del( item) +{ + int l = p.low(); + int h = p.high(); + while (l <= h) + { + int mid = (l + h) / 2; + int cmp = CMP(item, p[mid]); + if (cmp == 0) + { + --count; + if (p.high() - mid < mid - p.low()) + { + for (int i = mid; i < p.high(); ++i) p[i] = p[i+1]; + p.del_high(); + } + else + { + for (int i = mid; i > p.low(); --i) p[i] = p[i-1]; + p.del_low(); + } + return; + } + else if (cmp < 0) + h = mid - 1; + else + l = mid + 1; + } +} + +int OXPSet::operator <= (OXPSet& b) +{ + if (count > b.count) return 0; + int i = p.low(); + int j = b.p.low(); + for (;;) + { + if (i >= p.fence()) + return 1; + else if (j >= b.p.fence()) + return 0; + int cmp = CMP(p[i], b.p[j]); + if (cmp == 0) + { + ++i; ++j; + } + else if (cmp < 0) + return 0; + else + ++j; + } +} + +int OXPSet::operator == (OXPSet& b) +{ + int n = count; + if (n != b.count) return 0; + if (n == 0) return 1; + int i = p.low(); + int j = b.p.low(); + while (n-- > 0) if (!EQ(p[i++], b.p[j++])) return 0; + return 1; +} + + +void OXPSet::operator |= (OXPSet& b) +{ + if (&b == this || b.count == 0) + return; + else if (b.count <= 2) // small b -- just add + for (Pix i = b.first(); i; b.next(i)) add(b(i)); + else + { + // strategy: merge into top of p, simultaneously killing old bottom + int oldfence = p.fence(); + int i = p.low(); + int j = b.p.low(); + for (;;) + { + if (i == oldfence) + { + while (j < b.p.fence()) p.add_high(b.p[j++]); + break; + } + else if (j == b.p.fence()) + { + while (i++ < oldfence) + { + p.add_high(p.low_element()); + p.del_low(); + } + break; + } + int cmp = CMP(p[i], b.p[j]); + if (cmp <= 0) + { + ++i; + if (cmp == 0) ++j; + p.add_high(p.low_element()); + p.del_low(); + } + else + p.add_high(b.p[j++]); + } + count = p.length(); + } +} + + + +void OXPSet::operator -= (OXPSet& b) +{ + if (&b == this) + clear(); + else if (count != 0 && b.count != 0) + { + int i = p.low(); + int k = i; + int j = b.p.low(); + int oldfence = p.fence(); + for (;;) + { + if (i >= oldfence) + break; + else if (j >= b.p.fence()) + { + if (k != i) + while (i < oldfence) p[k++] = p[i++]; + else + k = oldfence; + break; + } + int cmp = CMP(p[i], b.p[j]); + if (cmp == 0) + { + ++i; ++j; + } + else if (cmp < 0) + { + if (k != i) p[k] = p[i]; + ++i; ++k; + } + else + j++; + } + while (k++ < oldfence) + { + --count; + p.del_high(); + } + } +} + +void OXPSet::operator &= (OXPSet& b) +{ + if (b.count == 0) + clear(); + else if (&b != this && count != 0) + { + int i = p.low(); + int k = i; + int j = b.p.low(); + int oldfence = p.fence(); + for (;;) + { + if (i >= oldfence || j >= b.p.fence()) + break; + int cmp = CMP(p[i], b.p[j]); + if (cmp == 0) + { + if (k != i) p[k] = p[i]; + ++i; ++k; ++j; + } + else if (cmp < 0) + ++i; + else + ++j; + } + while (k++ < oldfence) + { + --count; + p.del_high(); + } + } +} + +int OXPSet::OK() +{ + int v = p.OK(); + v &= count == p.length(); + for (int i = p.low(); i < p.high(); ++i) v &= CMP(p[i], p[i+1]) < 0; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/OXPSet.hP b/gnu/lib/libg++/g++-include/gen/OXPSet.hP new file mode 100644 index 0000000000..4e0c97712d --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/OXPSet.hP @@ -0,0 +1,102 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _OXPSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _OXPSet_h 1 + +#include ".Set.h" +#include ".XPlex.h" + +class OXPSet : public Set +{ +protected: + XPlex p; + +public: + OXPSet(int chunksize = DEFAULT_INITIAL_CAPACITY); + OXPSet(const OXPSet&); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item); + + void operator |= (OXPSet& b); + void operator -= (OXPSet& b); + void operator &= (OXPSet& b); + + int operator == (OXPSet& b); + int operator != (OXPSet& b); + int operator <= (OXPSet& b); + + int OK(); +}; + + +inline OXPSet::OXPSet(int chunksize) + : p(chunksize) { count = 0; } + +inline OXPSet::OXPSet(const OXPSet& s) : p(s.p) { count = s.count; } + +inline Pix OXPSet::first() +{ + return p.first(); +} + +inline void OXPSet::next(Pix & idx) +{ + p.next(idx); +} + +inline & OXPSet::operator ()(Pix idx) +{ + return p(idx); +} + +inline void OXPSet::clear() +{ + count = 0; p.clear(); +} + +inline int OXPSet::contains ( item) +{ + return seek(item) != 0; +} + +inline int OXPSet::owns (Pix idx) +{ + return p.owns(idx); +} + +inline int OXPSet::operator != (OXPSet& b) +{ + return !(*this == b); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/PHPQ.ccP b/gnu/lib/libg++/g++-include/gen/PHPQ.ccP new file mode 100644 index 0000000000..764b11c5cf --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/PHPQ.ccP @@ -0,0 +1,339 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + adapted for libg++ by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".PHPQ.h" + +// +// This defines a Pairing Heap structure +// +// See ``The Pairing Heap: A New Form of Self-Adjusting Heap'' +// Fredman, Segdewick et al, +// Algorithmica (1986) 1:111-129 +// +// In particular, this implements the pairing heap using the circular +// list. +// +// + +PHPQ::PHPQ(int sz) +{ + storage = 0; + root = 0; + count = 0; + size = 0; + prealloc(sz); +} + +PHPQ::PHPQ(PHPQ& a) +{ + storage = 0; + root = 0; + count = 0; + size = 0; + prealloc(a.size); + for (Pix i = a.first(); i != 0; a.next(i)) enq(a(i)); +} + + +void PHPQ::prealloc(int newsize) +{ + ++newsize; // leave a spot for freelist + if (size != 0) + { + int news = size; + while (news <= newsize) news = (news * 3) / 2; + newsize = news; + } + // see if indices are OK + PHPQNode test; + test.sibling = 0; + test.sibling = ~test.sibling; + if ((unsigned long)newsize > (unsigned long)(test.sibling)) + error("storage size exceeds index range"); + + if (storage == 0) + { + storage = new PHPQNode[size = newsize]; + for (int i = 0; i < size; ++i) + { + storage[i].sibling = i + 1; + storage[i].valid = 0; + } + storage[size-1].sibling = 0; + } + else + { + PHPQNode* newstor = new PHPQNode[newsize]; + for (int i = 1; i < size; ++i) + newstor[i] = storage[i]; + delete [] storage; + storage = newstor; + for (i = size; i < newsize; ++i) + { + storage[i].sibling = i + 1; + storage[i].valid = 0; + } + storage[newsize-1].sibling = 0; + storage[0].sibling = size; + size = newsize; + } +} + + +void PHPQ::clear() +{ + for (int i = 0; i < size; ++i) + { + storage[i].sibling = i + 1; + storage[i].valid = 0; + } + storage[size-1].sibling = 0; + root = 0; + count = 0; +} + +Pix PHPQ::enq( item) +{ + ++count; + if (storage[0].sibling == 0) + prealloc(count); + + int cell = storage[0].sibling; + storage[0].sibling = storage[cell].sibling; + storage[cell].sibling = 0; + storage[cell].children = 0; + storage[cell].item = item; + storage[cell].valid = 1; + + if (root == 0) + { + root = cell; + return Pix(root); + } + else + { + int parent; + int child; + + if (LE(storage[root].item, storage[cell].item)) + { + parent = root; child = cell; + } + else + { + parent = cell; child = root; + } + int popsKid = storage[parent].children; + + if (popsKid == 0) + { + storage[parent].children = child; + storage[child].sibling = child; + } + else + { + int temp = storage[popsKid].sibling; + storage[popsKid].sibling = child; + storage[child].sibling = temp; + storage[parent].children = child; + } + root = parent; + return Pix(cell); + } +} + +// +// Item removal is the most complicated routine. +// +// We remove the root (should there be one) and then select a new +// root. The siblings of the root are in a circular list. We continue +// to pair elements in this list until there is a single element. +// This element will be the new root. + +void PHPQ::del_front() +{ + int valid = 0; + do + { + if (root == 0) return; + if (valid = storage[root].valid) + --count; + storage[root].valid = 0; + int child = storage[root].children; + storage[root].sibling = storage[0].sibling; + storage[0].sibling = root; + + if (child == 0) + { + root = 0; + return; + } + else + { + while(storage[child].sibling != child) + { + // We have at least two kids, but we may only have + // two kids. So, oneChild != child, but it is possible + // that twoChild == child. + + int oneChild = storage[child].sibling; + int twoChild = storage[oneChild].sibling; + + // Remove the two from the sibling list + + storage[child].sibling = storage[twoChild].sibling; + storage[oneChild].sibling = 0; + storage[twoChild].sibling = 0; + + int bestChild; + int worstChild; + + if (LE(storage[oneChild].item, storage[twoChild].item)) + { + bestChild = oneChild; worstChild = twoChild; + } + else + { + bestChild = twoChild; worstChild = oneChild; + } + int popsKid = storage[bestChild].children; + + if (popsKid == 0) + { + storage[bestChild].children = worstChild; + storage[worstChild].sibling = worstChild; + } + else + { + int temp = storage[popsKid].sibling; + storage[popsKid].sibling = worstChild; + storage[worstChild].sibling = temp; + storage[bestChild].children = worstChild; + } + if (twoChild == child) + { + // We have reduced the two to one, so we'll be exiting. + child = bestChild; + storage[child].sibling = child; + } + else + { + // We've removed two siblings, now we need to insert + // the better of the two + storage[bestChild].sibling = storage[child].sibling; + storage[child].sibling = bestChild; + child = storage[bestChild].sibling; + } + } + root = child; + } + } while ( !valid ); +} + +void PHPQ::del(Pix p) +{ + if (p == 0) error("null Pix"); + int i = int(p); + if (storage[i].valid) + { + if (i == root) + del_front(); + else + { + storage[i].valid = 0; + --count; + } + } +} + + +Pix PHPQ::seek( key) +{ + for (int i = 1; i < size; ++i) + if (storage[i].valid && EQ(storage[i].item, key)) + return Pix(i); + return 0; +} + +Pix PHPQ::first() +{ + for (int i = 1; i < size; ++i) + if (storage[i].valid) + return Pix(i); + return 0; +} + + +void PHPQ::next(Pix& p) +{ + if (p == 0) return; + for (int i = int(p)+1; i < size; ++i) + if (storage[i].valid) + { + p = Pix(i); + return; + } + p = 0; +} + +int PHPQ::OK() +{ + int v = storage != 0; + int n = 0; + for (int i = 0; i < size; ++i) if (storage[i].valid) ++n; + v &= n == count; + v &= check_sibling_list(root); + int ct = MAXLONG; + n = 0; + int f = storage[0].sibling; + while (f != 0 && ct-- > 0) + { + f = storage[f].sibling; + ++n; + } + v &= ct > 0; + v &= n <= size - count; + if (!v) error("invariant failure"); + return v; +} + + +int PHPQ::check_sibling_list(int t) +{ + if (t != 0) + { + int s = t; + long ct = MAXLONG; // Lots of chances to find self! + do + { + if (storage[s].valid && !check_sibling_list(storage[s].children)) + return 0; + s = storage[s].sibling; + } while (ct-- > 0 && s != t && s != 0); + if (ct <= 0) return 0; + } + return 1; +} + + diff --git a/gnu/lib/libg++/g++-include/gen/PHPQ.hP b/gnu/lib/libg++/g++-include/gen/PHPQ.hP new file mode 100644 index 0000000000..359c527c60 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/PHPQ.hP @@ -0,0 +1,108 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + adapted for libg++ by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef PHPQ_h +#ifdef __GNUG__ +#pragma interface +#endif +#define PHPQ_h 1 + +#include ".PQ.h" + +#ifndef PHPQIndex +#define PHPQIndex unsigned short +#endif + +struct PHPQNode +{ + PHPQIndex sibling; + PHPQIndex children; + item; + char valid; +}; + + +class PHPQ : public PQ +{ + PHPQNode* storage; // table -- freelist in storage[0].sibling + int root; + int size; + + void prealloc(int); + int check_sibling_list(int); + +public: + + PHPQ(int sz = DEFAULT_INITIAL_CAPACITY); + PHPQ(PHPQ&); + ~PHPQ(); + + Pix enq( item); + deq(); + + & front(); + void del_front(); + + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + void del(Pix i); + Pix seek( item); + + int OK(); // rep invariant +}; + + +inline PHPQ::~PHPQ() +{ + delete [] storage; +} + + +inline PHPQ::deq() +{ + if (count == 0) error("deq of empty PQ"); + x = storage[root].item; + del_front(); + return x; +} + + +inline & PHPQ::front() +{ + if (count == 0) error("front of empty PQ"); + return storage[root].item; +} + +inline int PHPQ::contains( item) +{ + return seek(item) != 0; +} + +inline & PHPQ::operator() (Pix p) +{ + if (p == 0) error("null Pix"); + return storage[int(p)].item; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/PQ.ccP b/gnu/lib/libg++/g++-include/gen/PQ.ccP new file mode 100644 index 0000000000..810240759c --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/PQ.ccP @@ -0,0 +1,62 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".PQ.h" + + + + PQ::deq() +{ + x = front(); + del_front(); + return x; +} + +Pix PQ::seek( item) +{ + for (Pix i = first(); i != 0 && !(EQ((*this)(i), item)); next(i)); + return i; +} + +int PQ::owns(Pix idx) +{ + if (idx == 0) return 0; + for (Pix i = first(); i; next(i)) if (i == idx) return 1; + return 0; +} + +void PQ::clear() +{ + while (count != 0) del_front(); +} + +int PQ::contains ( item) +{ + return seek(item) != 0; +} + + +void PQ::error(const char* msg) +{ + (*lib_error_handler)("PQ", msg); +} + diff --git a/gnu/lib/libg++/g++-include/gen/PQ.hP b/gnu/lib/libg++/g++-include/gen/PQ.hP new file mode 100644 index 0000000000..981592ae85 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/PQ.hP @@ -0,0 +1,78 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _PQ_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _PQ_h 1 + +#include +#include ".defs.h" + +class PQ +{ +protected: + + int count; + +public: + PQ(); + virtual ~PQ(); + + int length(); // current number of items + int empty(); + + virtual Pix enq( item) = 0; // add item; return Pix + virtual deq(); // return & remove min + + virtual & front() = 0; // access min item + virtual void del_front() = 0; // delete min item + + virtual int contains( item); // is item in PQ? + + virtual void clear(); // delete all items + + virtual Pix first() = 0; // Pix of first item or 0 + virtual void next(Pix& i) = 0; // advance to next or 0 + virtual & operator () (Pix i) = 0; // access item at i + virtual void del(Pix i) = 0; // delete item at i + virtual int owns(Pix i); // is i a valid Pix ? + virtual Pix seek( item); // Pix of item + + void error(const char* msg); + virtual int OK() = 0; // rep invariant +}; + + +inline PQ::PQ() :count(0) {} + +inline PQ::~PQ() {} + +inline int PQ::length() +{ + return count; +} + +inline int PQ::empty() +{ + return count == 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/PSList.hP b/gnu/lib/libg++/g++-include/gen/PSList.hP new file mode 100644 index 0000000000..eacb34dbe3 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/PSList.hP @@ -0,0 +1,32 @@ +/* : Light weight list: This will simply reuse code from a VoidP List, which +was genclassed from the SLList libg++ class. The classes generated from this file +will all be derived classes from class VoidSLList or intSLList. Note that class SLList does not +offer all the functionality of List classes, such as sharing of sub-lists. +However, no additional code is needed at all and no .cc file is generated. So it costs nothing +to use these type-safe lists. Only member functions needing type casting are re-defined */ + + +#ifndef _SList_h +#define _SList_h 1 + +#include "VoidP.SLList.h" +#include ".defs.h" + +class SList : public VoidPSLList +{ +public: + SList() {} + SList(SList& a) : (a) {} + ~SList() {} + + SList& operator = (SList& a) { + return (SList&) VoidPSLList::operator= (a); } + + & operator () (Pix p) { return (&) (VoidPSLList::operator() (p)); } + & front() { return (&) VoidPSLList::front(); } + & rear() { return (&) VoidPSLList::rear(); } + remove_front() { return () VoidPSLList::remove_front(); } + +}; + +#endif /* conditional include */ diff --git a/gnu/lib/libg++/g++-include/gen/PVec.hP b/gnu/lib/libg++/g++-include/gen/PVec.hP new file mode 100644 index 0000000000..de32482610 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/PVec.hP @@ -0,0 +1,79 @@ +/* : light weight Vector: This will simply reuse code from */ +/* a VoidP Vec, which was genclassed from the Vec libg++ class. */ +/* The classes generated from this file will all be derived classes */ +/* from class VoidVec or intVec. No .cc file is generated. So */ +/* it costs nothing to use these type-safe Vectors. Only member */ +/* functions needing type casting are re-defined. */ +/* */ + +#ifndef _Vec_h +#define _Vec_h 1 + +#include "VoidP.Vec.h" +#include ".defs.h" + + +#ifndef __typedefs +#define __typedefs 1 +typedef void (*Procedure)( ); +typedef (*Mapper)( ); +typedef (*Combiner)( , ); +typedef int (*Predicate)( ); +typedef int (*Comparator)( , ); +#endif + +class Vec : public VoidPVec +{ +protected: + Vec(int l, * d) : (l, (VoidP*) d) {}; +public: + Vec() {}; + Vec(int l) : (l) {}; + Vec(int l, fill_value) : (l, fill_value) {}; + Vec(Vec& v) : (v) {}; + Vec(VoidPVec& v) {fake_copy(v, s, len);} + ~Vec() {}; + + Vec& operator = (Vec& a) + {return (Vec&) VoidPVec::operator= (a);} + Vec at(int from, int n) {return (Vec) VoidPVec::at(from, n);} + + & operator [] (int n) {return (&)VoidPVec::operator[] (n);} + & elem(int n) {return (&)VoidPVec::elem(n);} + + friend Vec concat(Vec& a, Vec& b); + friend Vec map(Mapper f, Vec & a); + friend Vec merge(Vec & a, Vec & b, Comparator f); + friend Vec combine(Combiner f, Vec & a, Vec & b); + friend Vec reverse(Vec& a); + + void sort(Comparator f); + void apply(Procedure f); + reduce(Combiner f, base); +}; + +inline Vec concat(Vec& a, Vec& b) +{return (Vec)concat((VoidPVec&)a, (VoidPVec&)b);} + +inline Vec map(Mapper f, Vec & a) { + return (Vec)map((VoidPMapper)f, (VoidPVec&)a); } + +inline Vec merge(Vec & a, Vec & b, Comparator f) { + return (Vec)merge((VoidPVec&)a, (VoidPVec&)b, (VoidPComparator)f); } + +inline Vec combine(Combiner f, Vec & a, Vec & b) { + return (Vec)combine((VoidPCombiner)f, (VoidPVec&)a, (VoidPVec&)b); } + +inline Vec reverse(Vec& a) { + return (Vec)reverse((VoidPVec&)a);} + +inline void Vec::sort(Comparator f) { + VoidPVec::sort((VoidPComparator) f); } + +inline void Vec::apply(Procedure f) { + VoidPVec::apply((VoidPProcedure) f); } + +inline Vec::reduce(Combiner f, base) { + return ()VoidPVec::reduce((VoidPCombiner)f, base);} + +#endif /* conditional include */ diff --git a/gnu/lib/libg++/g++-include/gen/Plex.ccP b/gnu/lib/libg++/g++-include/gen/Plex.ccP new file mode 100644 index 0000000000..5eb13c85f4 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/Plex.ccP @@ -0,0 +1,222 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include +#include ".Plex.h" + +// IChunk support + +void IChunk::error(const char* msg) const +{ + (*lib_error_handler)("IChunk", msg); +} + +void IChunk::index_error() const +{ + error("attempt to use invalid index"); +} + +void IChunk::empty_error() const +{ + error("invalid use of empty chunk"); +} + +void IChunk::full_error() const +{ + error("attempt to extend chunk beyond bounds"); +} + +IChunk:: ~IChunk() {} + +IChunk::IChunk(* d, + int baseidx, + int lowidx, + int fenceidx, + int topidx) +{ + if (d == 0 || baseidx > lowidx || lowidx > fenceidx || fenceidx > topidx) + error("inconsistent specification"); + data = d; + base = baseidx; + low = lowidx; + fence = fenceidx; + top = topidx; + nxt = prv = this; +} + +void IChunk:: re_index(int lo) +{ + int delta = lo - low; + base += delta; + low += delta; + fence += delta; + top += delta; +} + + +void IChunk::clear(int lo) +{ + int s = top - base; + low = base = fence = lo; + top = base + s; +} + +void IChunk::cleardown(int hi) +{ + int s = top - base; + low = top = fence = hi; + base = top - s; +} + +int IChunk:: OK() const +{ + int v = data != 0; // have some data + v &= base <= low; // ok, index-wise + v &= low <= fence; + v &= fence <= top; + + v &= nxt->prv == this; // and links are OK + v &= prv->nxt == this; + if (!v) error("invariant failure"); + return(v); +} + + +// error handling + + +void Plex::error(const char* msg) const +{ + (*lib_error_handler)("Plex", msg); +} + +void Plex::index_error() const +{ + error("attempt to access invalid index"); +} + +void Plex::empty_error() const +{ + error("attempted operation on empty plex"); +} + +void Plex::full_error() const +{ + error("attempt to increase size of plex past limit"); +} + +// generic plex ops + +Plex:: ~Plex() +{ + invalidate(); +} + + +void Plex::append (const Plex& a) +{ + for (int i = a.low(); i < a.fence(); a.next(i)) add_high(a[i]); +} + +void Plex::prepend (const Plex& a) +{ + for (int i = a.high(); i > a.ecnef(); a.prev(i)) add_low(a[i]); +} + +void Plex::reverse() +{ + tmp; + int l = low(); + int h = high(); + while (l < h) + { + tmp = (*this)[l]; + (*this)[l] = (*this)[h]; + (*this)[h] = tmp; + next(l); + prev(h); + } +} + + +void Plex::fill(const x) +{ + for (int i = lo; i < fnc; ++i) (*this)[i] = x; +} + +void Plex::fill(const x, int lo, int hi) +{ + for (int i = lo; i <= hi; ++i) (*this)[i] = x; +} + + +void Plex::del_chunk(IChunk* x) +{ + if (x != 0) + { + x->unlink(); + * data = (*)(x->invalidate()); + delete [] data; + delete x; + } +} + + +void Plex::invalidate() +{ + IChunk* t = hd; + if (t != 0) + { + IChunk* tail = tl(); + while (t != tail) + { + IChunk* nxt = t->next(); + del_chunk(t); + t = nxt; + } + del_chunk(t); + hd = 0; + } +} + +int Plex::reset_low(int l) +{ + int old = lo; + int diff = l - lo; + if (diff != 0) + { + lo += diff; + fnc += diff; + IChunk* t = hd; + do + { + t->re_index(t->low_index() + diff); + t = t->next(); + } while (t != hd); + } + return old; +} + + + + diff --git a/gnu/lib/libg++/g++-include/gen/Plex.hP b/gnu/lib/libg++/g++-include/gen/Plex.hP new file mode 100644 index 0000000000..f8af1b6ba7 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/Plex.hP @@ -0,0 +1,494 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _Plex_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Plex_h 1 + +#include +#include +#include ".defs.h" + +// Plexes are made out of IChunks + +#include + +class IChunk +{ +//public: // kludge until C++ `protected' policies settled +protected: + + * data; // data, from client + + int base; // lowest possible index + int low; // lowest valid index + int fence; // highest valid index + 1 + int top; // highest possible index + 1 + + IChunk* nxt; // circular links + IChunk* prv; + +public: + +// constructors + + IChunk(* d, // ptr to array of elements + int base_idx, // initial indices + int low_idx, + int fence_idx, + int top_idx); + + virtual ~IChunk(); + +// status reports + + int size() const; // number of slots + + virtual int empty() const ; + virtual int full() const ; + + int can_grow_high () const ; // there is space to add data + int can_grow_low () const; + + int base_index() const; // lowest possible index; + int low_index() const; // lowest actual index; + virtual int first_index() const; // lowest valid index or fence if none + virtual int last_index() const; // highest valid index or low-1 if none + int fence_index() const; // highest actual index + 1 + int top_index() const; // highest possible index + 1 + +// indexing conversion + + int possible_index(int i) const; // i between base and top + int actual_index(int i) const; // i between low and fence + virtual int valid_index(int i) const; // i not deleted (mainly for mchunks) + + int possible_pointer(const * p) const; // same for ptr + int actual_pointer(const * p) const; + virtual int valid_pointer(const * p) const; + + * pointer_to(int i) const ; // pointer to data indexed by i + // caution: i is not checked for validity + int index_of(const * p) const; // index of data pointed to by p + // caution: p is not checked for validity + + virtual int succ(int idx) const; // next valid index or fence if none + virtual int pred(int idx) const; // previous index or low - 1 if none + + virtual * first_pointer() const; // pointer to first valid pos or 0 + virtual * last_pointer() const; // pointer to first valid pos or 0 + virtual * succ(* p) const; // next pointer or 0 + virtual * pred(* p) const; // previous pointer or 0 + +// modification + + virtual * grow_high (); // return spot to add an element + virtual * grow_low (); + + virtual void shrink_high (); // logically delete top index + virtual void shrink_low (); + + virtual void clear(int lo); // reset to empty ch with base = lo + virtual void cleardown(int hi); // reset to empty ch with top = hi + void re_index(int lo); // re-index so lo is new low + +// chunk traversal + + IChunk* next() const; + IChunk* prev() const; + + void link_to_prev(IChunk* prev); + void link_to_next(IChunk* next); + void unlink(); + +// state checks + + * invalidate(); // mark self as invalid; return data + // for possible deletion + + virtual int OK() const; // representation invariant + + void error(const char*) const; + void empty_error() const; + void full_error() const; + void index_error() const; +}; + +// Plex is a partly `abstract' class: few of the virtuals +// are implemented at the Plex level, only in the subclasses + +class Plex +{ +protected: + + IChunk* hd; // a chunk holding the data + int lo; // lowest index + int fnc; // highest index + 1 + int csize; // size of the chunk + + void invalidate(); // mark so OK() is false + void del_chunk(IChunk*); // delete a chunk + + IChunk* tl() const; // last chunk; + int one_chunk() const; // true if hd == tl() + +public: + +// constructors, etc. + + Plex(); // no-op + + virtual ~Plex(); + + +// Access functions + + virtual & operator [] (int idx) = 0; // access by index; + virtual & operator () (Pix p) = 0; // access by Pix; + + virtual & high_element () = 0; // access high element + virtual & low_element () = 0; // access low element + +// read-only versions for const Plexes + + virtual const & operator [] (int idx) const = 0; // access by index; + virtual const & operator () (Pix p) const = 0; // access by Pix; + + virtual const & high_element () const = 0; // access high element + virtual const & low_element () const = 0; // access low element + + +// Index functions + + virtual int valid (int idx) const = 0; // idx is an OK index + + virtual int low() const = 0; // lowest index or fence if none + virtual int high() const = 0; // highest index or low-1 if none + + int ecnef() const; // low limit index (low-1) + int fence() const; // high limit index (high+1) + + virtual void prev(int& idx) const= 0; // set idx to preceding index + // caution: pred may be out of bounds + + virtual void next(int& idx) const = 0; // set to next index + // caution: succ may be out of bounds + + virtual Pix first() const = 0; // Pix to low element or 0 + virtual Pix last() const = 0; // Pix to high element or 0 + virtual void prev(Pix& pix) const = 0; // preceding pix or 0 + virtual void next(Pix& pix) const = 0; // next pix or 0 + virtual int owns(Pix p) const = 0; // p is an OK Pix + +// index<->Pix + + virtual int Pix_to_index(Pix p) const = 0; // get index via Pix + virtual Pix index_to_Pix(int idx) const = 0; // Pix via index + +// Growth + + virtual int add_high(const elem) =0;// add new element at high end + // return new high + + virtual int add_low(const elem) = 0; // add new low element, + // return new low + +// Shrinkage + + virtual int del_high() = 0; // remove the element at high end + // return new high + virtual int del_low() = 0; // delete low element, return new lo + + // caution: del_low/high + // does not necessarily + // immediately call ::~ + + +// operations on multiple elements + + virtual void fill(const x); // set all elements = x + virtual void fill(const x, int from, int to); // fill from to to + virtual void clear() = 0; // reset to zero-sized Plex + virtual int reset_low(int newlow); // change low index,return old + virtual void reverse(); // reverse in-place + virtual void append(const Plex& a); // concatenate a copy + virtual void prepend(const Plex& a); // prepend a copy + +// status + + virtual int can_add_high() const = 0; + virtual int can_add_low() const = 0; + + int length () const; // number of slots + + int empty () const; // is the plex empty? + virtual int full() const = 0; // it it full? + + int chunk_size() const; // report chunk size; + + virtual int OK() const = 0; // representation invariant + + void error(const char* msg) const; + void index_error() const; + void empty_error() const; + void full_error() const; +}; + + +// IChunk ops + +inline int IChunk:: size() const +{ + return top - base; +} + + +inline int IChunk:: base_index() const +{ + return base; +} + +inline int IChunk:: low_index() const +{ + return low; +} + +inline int IChunk:: fence_index() const +{ + return fence; +} + +inline int IChunk:: top_index() const +{ + return top; +} + +inline * IChunk:: pointer_to(int i) const +{ + return &(data[i-base]); +} + +inline int IChunk:: index_of(const * p) const +{ + return ((int)p - (int)data) / sizeof() + base; +} + +inline int IChunk:: possible_index(int i) const +{ + return i >= base && i < top; +} + +inline int IChunk:: possible_pointer(const * p) const +{ + return p >= data && p < &(data[top-base]); +} + +inline int IChunk:: actual_index(int i) const +{ + return i >= low && i < fence; +} + +inline int IChunk:: actual_pointer(const * p) const +{ + return p >= data && p < &(data[fence-base]); +} + +inline int IChunk:: can_grow_high () const +{ + return fence < top; +} + +inline int IChunk:: can_grow_low () const +{ + return base < low; +} + +inline * IChunk:: invalidate() +{ + * p = data; + data = 0; + return p; +} + + +inline IChunk* IChunk::prev() const +{ + return prv; +} + +inline IChunk* IChunk::next() const +{ + return nxt; +} + +inline void IChunk::link_to_prev(IChunk* prev) +{ + nxt = prev->nxt; + prv = prev; + nxt->prv = this; + prv->nxt = this; +} + +inline void IChunk::link_to_next(IChunk* next) +{ + prv = next->prv; + nxt = next; + nxt->prv = this; + prv->nxt = this; +} + +inline void IChunk::unlink() +{ + IChunk* n = nxt; + IChunk* p = prv; + n->prv = p; + p->nxt = n; + prv = nxt = this; +} + +inline int IChunk:: empty() const +{ + return low == fence; +} + +inline int IChunk:: full() const +{ + return top - base == fence - low; +} + +inline int IChunk:: first_index() const +{ + return (low == fence)? fence : low; +} + +inline int IChunk:: last_index() const +{ + return (low == fence)? low - 1 : fence - 1; +} + +inline int IChunk:: succ(int i) const +{ + return (i < low) ? low : i + 1; +} + +inline int IChunk:: pred(int i) const +{ + return (i > fence) ? (fence - 1) : i - 1; +} + +inline int IChunk:: valid_index(int i) const +{ + return i >= low && i < fence; +} + +inline int IChunk:: valid_pointer(const * p) const +{ + return p >= &(data[low - base]) && p < &(data[fence - base]); +} + +inline * IChunk:: grow_high () +{ + if (!can_grow_high()) full_error(); + return &(data[fence++ - base]); +} + +inline * IChunk:: grow_low () +{ + if (!can_grow_low()) full_error(); + return &(data[--low - base]); +} + +inline void IChunk:: shrink_high () +{ + if (empty()) empty_error(); + --fence; +} + +inline void IChunk:: shrink_low () +{ + if (empty()) empty_error(); + ++low; +} + +inline * IChunk::first_pointer() const +{ + return (low == fence)? 0 : &(data[low - base]); +} + +inline * IChunk::last_pointer() const +{ + return (low == fence)? 0 : &(data[fence - base - 1]); +} + +inline * IChunk::succ(* p) const +{ + return ((p+1) < &(data[low - base]) || (p+1) >= &(data[fence - base])) ? + 0 : (p+1); +} + +inline * IChunk::pred(* p) const +{ + return ((p-1) < &(data[low - base]) || (p-1) >= &(data[fence - base])) ? + 0 : (p-1); +} + + +// generic Plex operations + +inline Plex::Plex() {} + +inline int Plex::chunk_size() const +{ + return csize; +} + +inline int Plex::ecnef () const +{ + return lo - 1; +} + + +inline int Plex::fence () const +{ + return fnc; +} + +inline int Plex::length () const +{ + return fnc - lo; +} + +inline int Plex::empty () const +{ + return fnc == lo; +} + +inline IChunk* Plex::tl() const +{ + return hd->prev(); +} + +inline int Plex::one_chunk() const +{ + return hd == hd->prev(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/Queue.ccP b/gnu/lib/libg++/g++-include/gen/Queue.ccP new file mode 100644 index 0000000000..fb48d952ff --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/Queue.ccP @@ -0,0 +1,14 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".Queue.h" + +Queue::~Queue() {} + + +// error handling + +void Queue::error(const char* msg) +{ + (*lib_error_handler)("Queue", msg); +} diff --git a/gnu/lib/libg++/g++-include/gen/Queue.hP b/gnu/lib/libg++/g++-include/gen/Queue.hP new file mode 100644 index 0000000000..73db6e034e --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/Queue.hP @@ -0,0 +1,51 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _Queue_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Queue_h + +#include + +#include ".defs.h" + +class Queue +{ +public: + Queue() { } + virtual ~Queue(); + + virtual void enq( item) = 0; + virtual deq() = 0; + virtual & front() = 0; + virtual void del_front() = 0; + + virtual void clear() = 0; + virtual int empty() = 0; + virtual int full() = 0; + virtual int length() = 0; + + void error(const char*); + + virtual int OK() = 0; +}; + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/RAVLMap.ccP b/gnu/lib/libg++/g++-include/gen/RAVLMap.ccP new file mode 100644 index 0000000000..7537dd0788 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/RAVLMap.ccP @@ -0,0 +1,690 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include "..RAVLMap.h" + + +/* + constants & inlines for maintaining balance & thread status in tree nodes +*/ + +#define AVLBALANCEMASK 3 +#define AVLBALANCED 0 +#define AVLLEFTHEAVY 1 +#define AVLRIGHTHEAVY 2 + +#define LTHREADBIT 4 +#define RTHREADBIT 8 + + +static inline int bf(RAVLNode* t) +{ + return t->stat & AVLBALANCEMASK; +} + +static inline void set_bf(RAVLNode* t, int b) +{ + t->stat = (t->stat & ~AVLBALANCEMASK) | (b & AVLBALANCEMASK); +} + + +static inline int rthread(RAVLNode* t) +{ + return t->stat & RTHREADBIT; +} + +static inline void set_rthread(RAVLNode* t, int b) +{ + if (b) + t->stat |= RTHREADBIT; + else + t->stat &= ~RTHREADBIT; +} + +static inline int lthread(RAVLNode* t) +{ + return t->stat & LTHREADBIT; +} + +static inline void set_lthread(RAVLNode* t, int b) +{ + if (b) + t->stat |= LTHREADBIT; + else + t->stat &= ~LTHREADBIT; +} + +/* + traversal primitives +*/ + + +RAVLNode* RAVLMap::leftmost() +{ + RAVLNode* t = root; + if (t != 0) while (t->lt != 0) t = t->lt; + return t; +} + +RAVLNode* RAVLMap::rightmost() +{ + RAVLNode* t = root; + if (t != 0) while (t->rt != 0) t = t->rt; + return t; +} + +RAVLNode* RAVLMap::succ(RAVLNode* t) +{ + RAVLNode* r = t->rt; + if (!rthread(t)) while (!lthread(r)) r = r->lt; + return r; +} + +RAVLNode* RAVLMap::pred(RAVLNode* t) +{ + RAVLNode* l = t->lt; + if (!lthread(t)) while (!rthread(l)) l = l->rt; + return l; +} + + +Pix RAVLMap::seek( key) +{ + RAVLNode* t = root; + if (t == 0) + return 0; + for (;;) + { + int cmp = CMP(key, t->item); + if (cmp == 0) + return Pix(t); + else if (cmp < 0) + { + if (lthread(t)) + return 0; + else + t = t->lt; + } + else if (rthread(t)) + return 0; + else + t = t->rt; + } +} + + +int RAVLMap::rankof( key) +{ + int r; + RAVLNode* t = root; + if (t == 0) + return 0; + for (r=t->rank; t != 0; r+=t->rank) + { + int cmp = CMP(key, t->item); + if (cmp == 0) + return r; + else if (cmp < 0) + { + if (lthread(t)) + return 0; + else + { + r -= t->rank; + t = t->lt; + } + } + else if (rthread(t)) + return 0; + else + { + t = t->rt; + } + } + return 0; +} + +Pix RAVLMap::ranktoPix(int i) +{ + int r; + RAVLNode* t = root; + + if ((i<1)||(i>count)) + return 0; + for (r=t->rank; r!=i; r+=t->rank) + { + if (r>i) + { + r -= t->rank; + t = t->lt; + } + else + t = t->rt; + } + return Pix(t); +} + +/* + The combination of threads and AVL bits make adding & deleting + interesting, but very awkward. + + We use the following statics to avoid passing them around recursively +*/ + +static int _need_rebalancing; // to send back balance info from rec. calls +static * _target_item; // add/del_item target +static RAVLNode* _found_node; // returned added/deleted node +static int _already_found; // for deletion subcases +static int _rank_changed; // for rank computation + + +void RAVLMap:: _add(RAVLNode*& t) +{ + int cmp = CMP(*_target_item, t->item); + if (cmp == 0) + { + _found_node = t; + return; + } + else if (cmp < 0) + { + if (lthread(t)) + { + ++count; + _found_node = new RAVLNode(*_target_item, def); + set_lthread(_found_node, 1); + set_rthread(_found_node, 1); + _found_node->lt = t->lt; + _found_node->rt = t; + t->lt = _found_node; + set_lthread(t, 0); + _need_rebalancing = 1; + _rank_changed = 1; + } + else + _add(t->lt); + if (_rank_changed) ++t->rank; + if (_need_rebalancing) + { + switch(bf(t)) + { + case AVLRIGHTHEAVY: + set_bf(t, AVLBALANCED); + _need_rebalancing = 0; + return; + case AVLBALANCED: + set_bf(t, AVLLEFTHEAVY); + return; + case AVLLEFTHEAVY: + { + RAVLNode* l = t->lt; + if (bf(l) == AVLLEFTHEAVY) + { + t->rank -= l->rank; + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + _need_rebalancing = 0; + } + else + { + RAVLNode* r = l->rt; + r->rank += l->rank; + t->rank -= r->rank; + set_rthread(l, lthread(r)); + if (lthread(r)) + l->rt = r; + else + l->rt = r->lt; + r->lt = l; + set_lthread(r, 0); + set_lthread(t, rthread(r)); + if (rthread(r)) + t->lt = r; + else + t->lt = r->rt; + r->rt = t; + set_rthread(r, 0); + if (bf(r) == AVLLEFTHEAVY) + set_bf(t, AVLRIGHTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(r) == AVLRIGHTHEAVY) + set_bf(l, AVLLEFTHEAVY); + else + set_bf(l, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + _need_rebalancing = 0; + return; + } + } + } + } + } + else + { + if (rthread(t)) + { + ++count; + _found_node = new RAVLNode(*_target_item, def); + set_rthread(t, 0); + set_lthread(_found_node, 1); + set_rthread(_found_node, 1); + _found_node->lt = t; + _found_node->rt = t->rt; + t->rt = _found_node; + _need_rebalancing = 1; + _rank_changed = 1; + } + else + _add(t->rt); + if (_need_rebalancing) + { + switch(bf(t)) + { + case AVLLEFTHEAVY: + set_bf(t, AVLBALANCED); + _need_rebalancing = 0; + return; + case AVLBALANCED: + set_bf(t, AVLRIGHTHEAVY); + return; + case AVLRIGHTHEAVY: + { + RAVLNode* r = t->rt; + if (bf(r) == AVLRIGHTHEAVY) + { + r->rank += t->rank; + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + _need_rebalancing = 0; + } + else + { + RAVLNode* l = r->lt; + r->rank -= l->rank; + l->rank += t->rank; + set_lthread(r, rthread(l)); + if (rthread(l)) + r->lt = l; + else + r->lt = l->rt; + l->rt = r; + set_rthread(l, 0); + set_rthread(t, lthread(l)); + if (lthread(l)) + t->rt = l; + else + t->rt = l->lt; + l->lt = t; + set_lthread(l, 0); + if (bf(l) == AVLRIGHTHEAVY) + set_bf(t, AVLLEFTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(l) == AVLLEFTHEAVY) + set_bf(r, AVLRIGHTHEAVY); + else + set_bf(r, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + _need_rebalancing = 0; + return; + } + } + } + } + } +} + + +& RAVLMap::operator [] ( item) +{ + if (root == 0) + { + ++count; + root = new RAVLNode(item, def); + set_rthread(root, 1); + set_lthread(root, 1); + return root->cont; + } + else + { + _target_item = &item; + _need_rebalancing = 0; + _rank_changed = 0; + _add(root); + return _found_node->cont; + } +} + + +void RAVLMap::_del(RAVLNode* par, RAVLNode*& t) +{ + int comp; + if (_already_found) + { + if (rthread(t)) + comp = 0; + else + comp = 1; + } + else + comp = CMP(*_target_item, t->item); + if (comp == 0) + { + if (lthread(t) && rthread(t)) + { + _found_node = t; + if (t == par->lt) + { + set_lthread(par, 1); + par->lt = t->lt; + } + else + { + set_rthread(par, 1); + par->rt = t->rt; + } + _need_rebalancing = 1; + _rank_changed = 1; + return; + } + else if (lthread(t)) + { + _found_node = t; + RAVLNode* s = succ(t); + if (s != 0 && lthread(s)) + s->lt = t->lt; + t = t->rt; + _need_rebalancing = 1; + _rank_changed = 1; + return; + } + else if (rthread(t)) + { + _found_node = t; + RAVLNode* p = pred(t); + if (p != 0 && rthread(p)) + p->rt = t->rt; + t = t->lt; + _need_rebalancing = 1; + _rank_changed = 1; + return; + } + else // replace item & find someone deletable + { + RAVLNode* p = pred(t); + t->item = p->item; + t->cont = p->cont; + _already_found = 1; + comp = -1; // fall through below to left + } + } + + if (comp < 0) + { + if (lthread(t)) + return; + _del(t, t->lt); + if (_rank_changed) --t->rank; + if (!_need_rebalancing) + return; + switch (bf(t)) + { + case AVLLEFTHEAVY: + set_bf(t, AVLBALANCED); + return; + case AVLBALANCED: + set_bf(t, AVLRIGHTHEAVY); + _need_rebalancing = 0; + return; + case AVLRIGHTHEAVY: + { + RAVLNode* r = t->rt; + switch (bf(r)) + { + case AVLBALANCED: + r->rank += t->rank; + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLRIGHTHEAVY); + set_bf(r, AVLLEFTHEAVY); + _need_rebalancing = 0; + t = r; + return; + case AVLRIGHTHEAVY: + r->rank += t->rank; + if (lthread(r)) + t->rt = r; + else + t->rt = r->lt; + set_rthread(t, lthread(r)); + r->lt = t; + set_lthread(r, 0); + set_bf(t, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + return; + case AVLLEFTHEAVY: + { + RAVLNode* l = r->lt; + r->rank -= l->rank; + l->rank += t->rank; + set_lthread(r, rthread(l)); + if (rthread(l)) + r->lt = l; + else + r->lt = l->rt; + l->rt = r; + set_rthread(l, 0); + set_rthread(t, lthread(l)); + if (lthread(l)) + t->rt = l; + else + t->rt = l->lt; + l->lt = t; + set_lthread(l, 0); + if (bf(l) == AVLRIGHTHEAVY) + set_bf(t, AVLLEFTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(l) == AVLLEFTHEAVY) + set_bf(r, AVLRIGHTHEAVY); + else + set_bf(r, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + return; + } + } + } + } + } + else + { + if (rthread(t)) + return; + _del(t, t->rt); + if (!_need_rebalancing) + return; + switch (bf(t)) + { + case AVLRIGHTHEAVY: + set_bf(t, AVLBALANCED); + return; + case AVLBALANCED: + set_bf(t, AVLLEFTHEAVY); + _need_rebalancing = 0; + return; + case AVLLEFTHEAVY: + { + RAVLNode* l = t->lt; + switch (bf(l)) + { + case AVLBALANCED: + t->rank -= l->rank; + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLLEFTHEAVY); + set_bf(l, AVLRIGHTHEAVY); + _need_rebalancing = 0; + t = l; + return; + case AVLLEFTHEAVY: + t->rank -= l->rank; + if (rthread(l)) + t->lt = l; + else + t->lt = l->rt; + set_lthread(t, rthread(l)); + l->rt = t; + set_rthread(l, 0); + set_bf(t, AVLBALANCED); + set_bf(l, AVLBALANCED); + t = l; + return; + case AVLRIGHTHEAVY: + { + RAVLNode* r = l->rt; + r->rank += l->rank; + t->rank -= r->rank; + set_rthread(l, lthread(r)); + if (lthread(r)) + l->rt = r; + else + l->rt = r->lt; + r->lt = l; + set_lthread(r, 0); + set_lthread(t, rthread(r)); + if (rthread(r)) + t->lt = r; + else + t->lt = r->rt; + r->rt = t; + set_rthread(r, 0); + if (bf(r) == AVLLEFTHEAVY) + set_bf(t, AVLRIGHTHEAVY); + else + set_bf(t, AVLBALANCED); + if (bf(r) == AVLRIGHTHEAVY) + set_bf(l, AVLLEFTHEAVY); + else + set_bf(l, AVLBALANCED); + set_bf(r, AVLBALANCED); + t = r; + return; + } + } + } + } + } +} + + +void RAVLMap::del( item) +{ + if (root == 0) return; + _need_rebalancing = 0; + _already_found = 0; + _found_node = 0; + _rank_changed = 0; + _target_item = &item; + _del(root, root); + if (_found_node) + { + delete(_found_node); + if (--count == 0) + root = 0; + } +} + +void RAVLMap::_kill(RAVLNode* t) +{ + if (t != 0) + { + if (!lthread(t)) _kill(t->lt); + if (!rthread(t)) _kill(t->rt); + delete t; + } +} + + +RAVLMap::RAVLMap(RAVLMap& b) :Map(b.def) +{ + root = 0; + count = 0; + for (Pix i = b.first(); i != 0; b.next(i)) + (*this)[b.key(i)] = b.contents(i); +} + + +int RAVLMap::OK() +{ + int v = 1; + if (root == 0) + v = count == 0; + else + { + int n = 1; + RAVLNode* trail = leftmost(); + v &= rankof(trail->item) == n; + RAVLNode* t = succ(trail); + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + v &= rankof(t->item) == n; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/RAVLMap.hP b/gnu/lib/libg++/g++-include/gen/RAVLMap.hP new file mode 100644 index 0000000000..d3c523ee29 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/RAVLMap.hP @@ -0,0 +1,147 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + ranking code from Paul Anderson (paul%lfcs.ed.ac.uk) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _RAVLMap_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _RAVLMap_h 1 + +#include "..Map.h" + +struct RAVLNode +{ + RAVLNode* lt; + RAVLNode* rt; + item; + cont; + int rank; + char stat; + RAVLNode( h, c, + RAVLNode* l=0, RAVLNode* r=0, int k=1); + ~RAVLNode(); +}; + +inline RAVLNode::RAVLNode( h, c, + RAVLNode* l, RAVLNode* r, int k) + :item(h), cont(c), lt(l), rt(r), rank(k), stat(0) {} + +inline RAVLNode::~RAVLNode() {} + +typedef RAVLNode* RAVLNodePtr; + + +class RAVLMap : public Map +{ +protected: + RAVLNode* root; + + RAVLNode* leftmost(); + RAVLNode* rightmost(); + RAVLNode* pred(RAVLNode* t); + RAVLNode* succ(RAVLNode* t); + void _kill(RAVLNode* t); + void _add(RAVLNode*& t); + void _del(RAVLNode* p, RAVLNode*& t); + +public: + RAVLMap( dflt); + RAVLMap(RAVLMap& a); + ~RAVLMap(); + + & operator [] ( key); + + void del( key); + + Pix first(); + void next(Pix& i); + & key(Pix i); + & contents(Pix i); + + Pix seek( key); + int contains( key); + + Pix ranktoPix(int i); + int rankof( key); + + void clear(); + + Pix last(); + void prev(Pix& i); + + int OK(); +}; + +inline RAVLMap::~RAVLMap() +{ + _kill(root); +} + +inline RAVLMap::RAVLMap( dflt) :Map(dflt) +{ + root = 0; +} + + +inline Pix RAVLMap::first() +{ + return Pix(leftmost()); +} + +inline Pix RAVLMap::last() +{ + return Pix(rightmost()); +} + +inline void RAVLMap::next(Pix& i) +{ + if (i != 0) i = Pix(succ((RAVLNode*)i)); +} + +inline void RAVLMap::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((RAVLNode*)i)); +} + +inline & RAVLMap::key(Pix i) +{ + if (i == 0) error("null Pix"); + return ((RAVLNode*)i)->item; +} + +inline & RAVLMap::contents(Pix i) +{ + if (i == 0) error("null Pix"); + return ((RAVLNode*)i)->cont; +} + +inline void RAVLMap::clear() +{ + _kill(root); + count = 0; + root = 0; +} + +inline int RAVLMap::contains( key) +{ + return seek(key) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/RPlex.ccP b/gnu/lib/libg++/g++-include/gen/RPlex.ccP new file mode 100644 index 0000000000..0dbd2cee7e --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/RPlex.ccP @@ -0,0 +1,477 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".RPlex.h" + +typedef IChunk* _IChunk_ptr; + +RPlex:: RPlex() +{ + lo = fnc = 0; + csize = DEFAULT_INITIAL_CAPACITY; + * data = new [csize]; + set_cache(new IChunk(data, lo, lo, fnc, lo+csize)); + hd = ch; + maxch = MIN_NCHUNKS; + lch = maxch / 2; + fch = lch + 1; + base = ch->base_index() - lch * csize; + chunks = new _IChunk_ptr[maxch]; + chunks[lch] = ch; +} + +RPlex:: RPlex(int chunksize) +{ + if (chunksize == 0) error("invalid constructor specification"); + lo = fnc = 0; + if (chunksize > 0) + { + csize = chunksize; + * data = new [csize]; + set_cache(new IChunk(data, lo, lo, fnc, csize+lo)); + hd = ch; + } + else + { + csize = -chunksize; + * data = new [csize]; + set_cache(new IChunk(data, chunksize+lo, lo, fnc, fnc)); + hd = ch; + } + maxch = MIN_NCHUNKS; + lch = maxch / 2; + fch = lch + 1; + base = ch->base_index() - lch * csize; + chunks = new _IChunk_ptr[maxch]; + chunks[lch] = ch; +} + + +RPlex:: RPlex(int l, int chunksize) +{ + if (chunksize == 0) error("invalid constructor specification"); + lo = fnc = l; + if (chunksize > 0) + { + csize = chunksize; + * data = new [csize]; + set_cache(new IChunk(data, lo, lo, fnc, lo+csize)); + hd = ch; + } + else + { + csize = -chunksize; + * data = new [csize]; + set_cache(new IChunk(data, chunksize+lo, lo, fnc, fnc)); + hd = ch; + } + maxch = MIN_NCHUNKS; + lch = maxch / 2; + fch = lch + 1; + base = ch->base_index() - lch * csize; + chunks = new _IChunk_ptr[maxch]; + chunks[lch] = ch; +} + +void RPlex::make_initial_chunks(int up) +{ + int count = 0; + int need = fnc - lo; + hd = 0; + if (up) + { + int l = lo; + do + { + ++count; + int sz; + if (need >= csize) + sz = csize; + else + sz = need; + * data = new [csize]; + IChunk* h = new IChunk(data, l, l, l+sz, l+csize); + if (hd != 0) + h->link_to_next(hd); + else + hd = h; + l += sz; + need -= sz; + } while (need > 0); + } + else + { + int hi = fnc; + do + { + ++count; + int sz; + if (need >= csize) + sz = csize; + else + sz = need; + * data = new [csize]; + IChunk* h = new IChunk(data, hi-csize, hi-sz, hi, hi); + if (hd != 0) + h->link_to_next(hd); + hd = h; + hi -= sz; + need -= sz; + } while (need > 0); + } + set_cache((IChunk*)hd); + + maxch = MIN_NCHUNKS; + if (maxch < count * 2) + maxch = count * 2; + chunks = new _IChunk_ptr[maxch]; + lch = maxch / 3; + fch = lch + count; + base = ch->base_index() - csize * lch; + int k = lch; + do + { + chunks[k++] = ch; + set_cache(ch->next()); + } while (ch != hd); +} + +RPlex:: RPlex(int l, int hi, const initval, int chunksize) +{ + lo = l; + fnc = hi + 1; + if (chunksize == 0) + { + csize = fnc - l; + make_initial_chunks(1); + } + else if (chunksize < 0) + { + csize = -chunksize; + make_initial_chunks(0); + } + else + { + csize = chunksize; + make_initial_chunks(1); + } + fill(initval); +} + +RPlex::RPlex(const RPlex& a) +{ + lo = a.lo; + fnc = a.fnc; + csize = a.csize; + make_initial_chunks(); + for (int i = a.low(); i < a.fence(); a.next(i)) (*this)[i] = a[i]; +} + +void RPlex::operator= (const RPlex& a) +{ + if (&a != this) + { + invalidate(); + lo = a.lo; + fnc = a.fnc; + csize = a.csize; + make_initial_chunks(); + for (int i = a.low(); i < a.fence(); a.next(i)) (*this)[i] = a[i]; + } +} + + +void RPlex::cache(const * p) const +{ + const IChunk* old = ch; + const IChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = (t->next()); + if (t == old) index_error(); + } + set_cache(t); +} + +int RPlex::owns(Pix px) const +{ + * p = (*)px; + const IChunk* old = ch; + const IChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = (t->next()); + if (t == old) return 0; + } + set_cache(t); + return 1; +} + + +* RPlex::dosucc(const * p) const +{ + if (p == 0) return 0; + const IChunk* old = ch; + const IChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = (t->next()); + if (t == old) return 0; + } + int i = t->index_of(p) + 1; + if (i >= fnc) return 0; + if (i >= t->fence_index()) t = (t->next()); + set_cache(t); + return t->pointer_to(i); +} + +* RPlex::dopred(const * p) const +{ + if (p == 0) return 0; + const IChunk* old = ch; + const IChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = (t->prev()); + if (t == old) return 0; + } + int i = t->index_of(p) - 1; + if (i < lo) return 0; + if (i < t->low_index()) t = (t->prev()); + set_cache(t); + return (t->pointer_to(i)); +} + +int RPlex::add_high(const elem) +{ + IChunk* t = tl(); + if (!t->can_grow_high()) + { + if (t->IChunk::empty() && one_chunk()) + { + t->clear(fnc); + base = t->base_index() - lch * csize; + } + else + { + * data = new [csize]; + t = (new IChunk(data, fnc, fnc, fnc,fnc+csize)); + t->link_to_prev(tl()); + if (fch == maxch) + { + maxch *= 2; + IChunk** newch = new _IChunk_ptr [maxch]; + memcpy(newch, chunks, fch * sizeof(_IChunk_ptr)); + delete chunks; + chunks = newch; + } + chunks[fch++] = t; + } + } + *((t->IChunk::grow_high())) = elem; + set_cache(t); + return fnc++; +} + +int RPlex::del_high () +{ + if (empty()) empty_error(); + IChunk* t = tl(); + if (t->IChunk::empty()) // kill straggler first + { + IChunk* pred = t->prev(); + del_chunk(t); + t = (pred); + --fch; + } + t->IChunk::shrink_high(); + if (t->IChunk::empty() && !one_chunk()) + { + IChunk* pred = t->prev(); + del_chunk(t); + t = (pred); + --fch; + } + set_cache(t); + return --fnc - 1; +} + +int RPlex::add_low (const elem) +{ + IChunk* t = hd; + if (!t->can_grow_low()) + { + if (t->IChunk::empty() && one_chunk()) + { + t->cleardown(lo); + base = t->base_index() - lch * csize; + } + else + { + * data = new [csize]; + hd = new IChunk(data, lo-csize, lo, lo, lo); + hd->link_to_next(t); + t = ( hd); + if (lch == 0) + { + lch = maxch; + fch += maxch; + maxch *= 2; + IChunk** newch = new _IChunk_ptr [maxch]; + memcpy(&(newch[lch]), chunks, lch * sizeof(_IChunk_ptr)); + delete chunks; + chunks = newch; + base = t->base_index() - (lch - 1) * csize; + } + chunks[--lch] = t; + } + } + *((t->IChunk::grow_low())) = elem; + set_cache(t); + return --lo; +} + + +int RPlex::del_low () +{ + if (empty()) empty_error(); + IChunk* t = hd; + if (t->IChunk::empty()) + { + hd = t->next(); + del_chunk(t); + t = hd; + ++lch; + } + t->IChunk::shrink_low(); + if (t->IChunk::empty() && !one_chunk()) + { + hd = t->next(); + del_chunk(t); + t = hd; + ++lch; + } + set_cache(t); + return ++lo; +} + +void RPlex::reverse() +{ + tmp; + int l = lo; + int h = fnc - 1; + IChunk* loch = hd; + IChunk* hich = tl(); + while (l < h) + { + * lptr = loch->pointer_to(l); + * hptr = hich->pointer_to(h); + tmp = *lptr; + *lptr = *hptr; + *hptr = tmp; + if (++l >= loch->fence_index()) loch = loch->next(); + if (--h < hich->low_index()) hich = hich->prev(); + } +} + +void RPlex::fill(const x) +{ + for (int i = lo; i < fnc; ++i) (*this)[i] = x; +} + +void RPlex::fill(const x, int lo, int hi) +{ + for (int i = lo; i <= hi; ++i) (*this)[i] = x; +} + + +void RPlex::clear() +{ + for (int i = lch + 1; i < fch; ++i) + del_chunk(chunks[i]); + fch = lch + 1; + set_cache(chunks[lch]); + ch->IChunk::clear(lo); + fnc = lo; +} + +int RPlex::reset_low(int l) +{ + int old = lo; + int diff = l - lo; + if (diff != 0) + { + lo += diff; + fnc += diff; + IChunk* t = hd; + do + { + t->re_index(t->low_index() + diff); + t = t->next(); + } while (t != hd); + } + base = hd->base_index() - lch * csize; + return old; +} + + +int RPlex::OK () const +{ + int v = hd != 0 && ch != 0; // at least one chunk + + v &= fnc == tl()->fence_index(); // last chunk fnc == plex fnc + v &= lo == hd->IChunk::low_index(); // first lo == plex lo + + v &= base == hd->base_index() - lch * csize; // base is correct; + v &= lch < fch; + v &= fch <= maxch; // within allocation; + +// loop for others: + + int k = lch; // to cross-check nch + + int found_ch = 0; // to make sure ch is in list; + const IChunk* t = (hd); + for (;;) + { + v &= chunks[k++] == t; // each chunk is at proper index + if (t == ch) ++found_ch; + v &= t->IChunk::OK(); // each chunk is OK + if (t == tl()) + break; + else // and has indices contiguous to succ + { + v &= t->top_index() == t->next()->base_index(); + if (t != hd) // internal chunks full + { + v &= !t->empty(); + v &= !t->can_grow_low(); + v &= !t->can_grow_high(); + } + t = t->next(); + } + } + v &= found_ch == 1; + v &= fch == k; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/RPlex.hP b/gnu/lib/libg++/g++-include/gen/RPlex.hP new file mode 100644 index 0000000000..ed28c357e4 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/RPlex.hP @@ -0,0 +1,257 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _RPlex_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _RPlex_h 1 + +#include ".Plex.h" + +// minimum number of chunks to index + +#ifndef MIN_NCHUNKS +#define MIN_NCHUNKS 16 +#endif + +class RPlex: public Plex +{ + int base; // base index of lowest chunk + int lch; // index of lowest used chunk + int fch; // 1 + index of highest used chunk + int maxch; // max chunks in array + IChunk** chunks; // array of chunks + IChunk* ch; // cached chunk + + void make_initial_chunks(int up = 1); + + void cache(int idx) const; + void cache(const * p) const; + * dopred(const * p) const; + * dosucc(const * p) const; + + void set_cache(const IChunk* t) const; // logically, + // not physically const + +public: + RPlex(); // set low = 0; + // fence = 0; + // csize = default + + RPlex(int ch_size); // low = 0; + // fence = 0; + // csize = ch_size + + RPlex(int lo, // low = lo; + int ch_size); // fence=lo + // csize = ch_size + + RPlex(int lo, // low = lo + int hi, // fence = hi+1 + const initval,// fill with initval, + int ch_size = 0); // csize= ch_size + // or fence-lo if 0 + + RPlex(const RPlex&); + + ~RPlex(); + + void operator= (const RPlex&); + +// virtuals + + & high_element (); + & low_element (); + + const & high_element () const; + const & low_element () const; + + Pix first() const; + Pix last() const; + void prev(Pix& ptr) const; + void next(Pix& ptr) const; + int owns(Pix p) const; + & operator () (Pix p); + const & operator () (Pix p) const; + + int low() const; + int high() const; + int valid(int idx) const; + void prev(int& idx) const; + void next(int& x) const; + & operator [] (int index); + const & operator [] (int index) const; + + int Pix_to_index(Pix p) const; + Pix index_to_Pix(int idx) const; + + int can_add_high() const; + int can_add_low() const; + int full() const; + + int add_high(const elem); + int del_high (); + int add_low (const elem); + int del_low (); + + void fill(const x); + void fill(const x, int from, int to); + void clear(); + void reverse(); + + int reset_low(int newlow); + + int OK () const; +}; + + +inline void RPlex::prev(int& idx) const +{ + --idx; +} + +inline void RPlex::next(int& idx) const +{ + ++idx; +} + +inline int RPlex::full () const +{ + return 0; +} + +inline int RPlex::can_add_high() const +{ + return 1; +} + +inline int RPlex::can_add_low() const +{ + return 1; +} + +inline int RPlex::valid (int idx) const +{ + return idx >= lo && idx < fnc; +} + +inline int RPlex::low() const +{ + return lo; +} + +inline int RPlex::high() const +{ + return fnc - 1; +} + +inline void RPlex::set_cache(const IChunk* t) const +{ + ((RPlex*)(this))->ch = (IChunk*)t; +} + +inline void RPlex::cache(int idx) const +{ + if (idx < lo || idx >= fnc) index_error(); + set_cache(chunks[(idx - base) / csize]); +} + +inline & RPlex::low_element () +{ + cache(lo); return *(ch->pointer_to(lo)); +} + +inline & RPlex::high_element () +{ + cache(fnc-1); return *(ch->pointer_to(fnc - 1)); +} + +inline const & RPlex::low_element () const +{ + cache(lo); return *((const *)(ch->pointer_to(lo))); +} + +inline const & RPlex::high_element () const +{ + cache(fnc-1); return *((const *)(ch->pointer_to(fnc - 1))); +} + +inline int RPlex::Pix_to_index(Pix px) const +{ + * p = (*)px; + if (!ch->actual_pointer(p)) cache(p); + return ch->index_of(p); +} + +inline Pix RPlex::index_to_Pix(int idx) const +{ + if (!ch->actual_index(idx)) cache(idx); + return (Pix)(ch->pointer_to(idx)); +} + +inline Pix RPlex::first() const +{ + return Pix(hd->IChunk::first_pointer()); +} + +inline Pix RPlex::last() const +{ + return Pix(tl()->IChunk::last_pointer()); +} + +inline void RPlex::prev(Pix& p) const +{ + Pix q = Pix(ch->IChunk::pred((*)p)); + p = (q == 0)? Pix(dopred((*)p)) : q; +} + +inline void RPlex::next(Pix& p) const +{ + Pix q = Pix(ch->IChunk::succ((*)p)); + p = (q == 0)? Pix(dosucc((*)p)) : q; +} + +inline & RPlex:: operator () (Pix p) +{ + return *((*)p); +} + + +inline & RPlex:: operator [] (int idx) +{ + cache(idx); return *(ch->pointer_to(idx)); +} + +inline const & RPlex:: operator () (Pix p) const +{ + return *((const *)p); +} + +inline const & RPlex:: operator [] (int idx) const +{ + cache(idx); return *((const *)(ch->pointer_to(idx))); +} + +inline RPlex::~RPlex() +{ + delete chunks; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/SLBag.ccP b/gnu/lib/libg++/g++-include/gen/SLBag.ccP new file mode 100644 index 0000000000..50d2447c7d --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SLBag.ccP @@ -0,0 +1,105 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".SLBag.h" + +int SLBag::OK() +{ + int v = p.OK(); + v &= count == p.length(); + if (!v) error("invariant failure"); + return v; +} + +Pix SLBag::seek( item, Pix i) +{ + if (i == 0) i = first(); else next(i); + for (; i != 0 && (!(EQ(p(i), item))); p.next(i)); + return i; +} + +int SLBag::nof( item) +{ + int n = 0; + for (Pix p = first(); p; next(p)) if (EQ((*this)(p), item)) ++n; + return n; +} + + +void SLBag::del( item) +{ + Pix i = p.first(); + if (i == 0) + return; + else if (EQ(p(i), item)) + { + --count; + p.del_front(); + } + else + { + Pix trail = i; + p.next(i); + while (i != 0) + { + if (EQ(p(i), item)) + { + --count; + p.del_after(trail); + return; + } + trail = i; + p.next(i); + } + } +} + +void SLBag::remove( item) +{ + Pix i = p.first(); + while (i != 0 && EQ(p(i), item)) + { + --count; + p.del_front(); + i = p.first(); + } + if (i != 0) + { + Pix trail = i; + p.next(i); + while (i != 0) + { + if (EQ(p(i), item)) + { + --count; + p.del_after(trail); + i = trail; + p.next(i); + } + else + { + trail = i; + p.next(i); + } + } + } +} + diff --git a/gnu/lib/libg++/g++-include/gen/SLBag.hP b/gnu/lib/libg++/g++-include/gen/SLBag.hP new file mode 100644 index 0000000000..edb648e9d3 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SLBag.hP @@ -0,0 +1,96 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _SLBag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SLBag_h 1 + +#include ".Bag.h" +#include ".SLList.h" + +class SLBag : public Bag +{ +protected: + SLList p; + +public: + SLBag(); + SLBag(const SLBag&); + + Pix add( item); + void del( item); + void remove(item); + int contains( item); + int nof( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item, Pix from = 0); + + int OK(); +}; + + +inline SLBag::SLBag() : p() { count = 0; } + +inline SLBag::SLBag(const SLBag& s) : p(s.p) { count = s.count; } + +inline Pix SLBag::first() +{ + return p.first(); +} + +inline void SLBag::next(Pix & idx) +{ + p.next(idx); +} + +inline & SLBag::operator ()(Pix idx) +{ + return p(idx); +} + +inline void SLBag::clear() +{ + count = 0; p.clear(); +} + +inline int SLBag::owns (Pix idx) +{ + return p.owns(idx); +} + +inline Pix SLBag::add( item) +{ + ++count; + return p.append(item); +} + +inline int SLBag::contains( item) +{ + return seek(item) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/SLList.ccP b/gnu/lib/libg++/g++-include/gen/SLList.ccP new file mode 100644 index 0000000000..3ebd8d5bc9 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SLList.ccP @@ -0,0 +1,292 @@ +// This may look like C code, but it is really -*- C++ -*- +// WARNING: This file is obsolete. Use ../SLList.cc, if you can. +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include +#include +#include ".SLList.h" + +void SLList::error(const char* msg) +{ + (*lib_error_handler)("SLList", msg); +} + +int SLList::length() +{ + int l = 0; + SLListNode* t = last; + if (t != 0) do { ++l; t = t->tl; } while (t != last); + return l; +} + +SLList::SLList(const SLList& a) +{ + if (a.last == 0) + last = 0; + else + { + SLListNode* p = a.last->tl; + SLListNode* h = new SLListNode(p->hd); + last = h; + for (;;) + { + if (p == a.last) + { + last->tl = h; + return; + } + p = p->tl; + SLListNode* n = new SLListNode(p->hd); + last->tl = n; + last = n; + } + } +} + +SLList& SLList::operator = (const SLList& a) +{ + if (last != a.last) + { + clear(); + if (a.last != 0) + { + SLListNode* p = a.last->tl; + SLListNode* h = new SLListNode(p->hd); + last = h; + for (;;) + { + if (p == a.last) + { + last->tl = h; + break; + } + p = p->tl; + SLListNode* n = new SLListNode(p->hd); + last->tl = n; + last = n; + } + } + } + return *this; +} + +void SLList::clear() +{ + if (last == 0) + return; + + SLListNode* p = last->tl; + last->tl = 0; + last = 0; + + while (p != 0) + { + SLListNode* nxt = p->tl; + delete(p); + p = nxt; + } +} + + +Pix SLList::prepend( item) +{ + SLListNode* t = new SLListNode(item); + if (last == 0) + t->tl = last = t; + else + { + t->tl = last->tl; + last->tl = t; + } + return Pix(t); +} + + +Pix SLList::prepend(SLListNode* t) +{ + if (t == 0) return 0; + if (last == 0) + t->tl = last = t; + else + { + t->tl = last->tl; + last->tl = t; + } + return Pix(t); +} + + +Pix SLList::append( item) +{ + SLListNode* t = new SLListNode(item); + if (last == 0) + t->tl = last = t; + else + { + t->tl = last->tl; + last->tl = t; + last = t; + } + return Pix(t); +} + +Pix SLList::append(SLListNode* t) +{ + if (t == 0) return 0; + if (last == 0) + t->tl = last = t; + else + { + t->tl = last->tl; + last->tl = t; + last = t; + } + return Pix(t); +} + +void SLList::join(SLList& b) +{ + SLListNode* t = b.last; + b.last = 0; + if (last == 0) + last = t; + else if (t != 0) + { + SLListNode* f = last->tl; + last->tl = t->tl; + t->tl = f; + last = t; + } +} + +Pix SLList::ins_after(Pix p, item) +{ + SLListNode* u = (SLListNode*)p; + SLListNode* t = new SLListNode(item); + if (last == 0) + t->tl = last = t; + else if (u == 0) // ins_after 0 means prepend + { + t->tl = last->tl; + last->tl = t; + } + else + { + t->tl = u->tl; + u->tl = t; + if (u == last) + last = t; + } + return Pix(t); +} + + +void SLList::del_after(Pix p) +{ + SLListNode* u = (SLListNode*)p; + if (last == 0 || u == last) error("cannot del_after last"); + if (u == 0) u = last; // del_after 0 means delete first + SLListNode* t = u->tl; + if (u == t) + last = 0; + else + { + u->tl = t->tl; + if (last == t) + last = u; + } + delete t; +} + +int SLList::owns(Pix p) +{ + SLListNode* t = last; + if (t != 0 && p != 0) + { + do + { + if (Pix(t) == p) return 1; + t = t->tl; + } while (t != last); + } + return 0; +} + + SLList::remove_front() +{ + if (last == 0) error("remove_front of empty list"); + SLListNode* t = last->tl; + res = t->hd; + if (t == last) + last = 0; + else + last->tl = t->tl; + delete t; + return res; +} + +int SLList::remove_front(& x) +{ + if (last == 0) + return 0; + else + { + SLListNode* t = last->tl; + x = t->hd; + if (t == last) + last = 0; + else + last->tl = t->tl; + delete t; + return 1; + } +} + + +void SLList::del_front() +{ + if (last == 0) error("del_front of empty list"); + SLListNode* t = last->tl; + if (t == last) + last = 0; + else + last->tl = t->tl; + delete t; +} + +int SLList::OK() +{ + int v = 1; + if (last != 0) + { + SLListNode* t = last; + long count = MAXLONG; // Lots of chances to find last! + do + { + count--; + t = t->tl; + } while (count > 0 && t != last); + v &= count > 0; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/SLList.hP b/gnu/lib/libg++/g++-include/gen/SLList.hP new file mode 100644 index 0000000000..e5570bdf8f --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SLList.hP @@ -0,0 +1,137 @@ +// This may look like C code, but it is really -*- C++ -*- +// WARNING: This file is obsolete. Use ../SLList.h, if you can. +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _SLList_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SLList_h 1 + +#include +#include ".defs.h" + +#ifndef _SLListNode_h +#define _SLListNode_h 1 + +struct SLListNode +{ + SLListNode* tl; + hd; + SLListNode() { } + SLListNode(const h, SLListNode* t = 0); + ~SLListNode() { } +}; + + +inline SLListNode::SLListNode(const h, SLListNode* t) +:hd(h), tl(t) {} + +typedef SLListNode* SLListNodePtr; + +#endif + + +class SLList +{ +protected: + SLListNode* last; + +public: + SLList(); + SLList(const SLList& a); + ~SLList(); + + SLList& operator = (const SLList& a); + + int empty(); + int length(); + + void clear(); + + Pix prepend( item); + Pix append( item); + + void join(SLList&); + + Pix prepend(SLListNode*); + Pix append(SLListNode*); + + & operator () (Pix p); + Pix first(); + void next(Pix& p); + int owns(Pix p); + Pix ins_after(Pix p, item); + void del_after(Pix p); + + & front(); + & rear(); + remove_front(); + int remove_front(& x); + void del_front(); + + void error(const char* msg); + int OK(); +}; + +inline SLList::~SLList() +{ + clear(); +} + +inline SLList::SLList() +{ + last = 0; +} + +inline int SLList::empty() +{ + return last == 0; +} + + +inline Pix SLList::first() +{ + return (last == 0)? 0 : Pix(last->tl); +} + +inline void SLList::next(Pix& p) +{ + p = (p == 0 || p == last)? 0 : Pix(((SLListNode*)(p))->tl); +} + +inline & SLList::operator () (Pix p) +{ + if (p == 0) error("null Pix"); + return ((SLListNode*)(p))->hd; +} + +inline & SLList::front() +{ + if (last == 0) error("front: empty list"); + return last->tl->hd; +} + +inline & SLList::rear() +{ + if (last == 0) error("rear: empty list"); + return last->hd; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/SLQueue.ccP b/gnu/lib/libg++/g++-include/gen/SLQueue.ccP new file mode 100644 index 0000000000..8a5935b775 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SLQueue.ccP @@ -0,0 +1,4 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".SLQueue.h" diff --git a/gnu/lib/libg++/g++-include/gen/SLQueue.hP b/gnu/lib/libg++/g++-include/gen/SLQueue.hP new file mode 100644 index 0000000000..20fd74c9c5 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SLQueue.hP @@ -0,0 +1,108 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _SLQueue_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SLQueue_h + +#include ".SLList.h" +#include ".Queue.h" + +class SLQueue : public Queue +{ + SLList p; + +public: + SLQueue(); + SLQueue(const SLQueue& q); + ~SLQueue(); + + void operator = (const SLQueue&); + + void enq( item); + deq(); + & front(); + void del_front(); + + void clear(); + int empty(); + int full(); + int length(); + + int OK(); +}; + +inline SLQueue::SLQueue() :p() {} +inline SLQueue::SLQueue(const SLQueue& q) : p(q.p) {} + +inline SLQueue::~SLQueue() {} + +inline void SLQueue::enq(item) +{ + p.append(item); +} + +inline SLQueue::deq() +{ + return p.remove_front(); +} + +inline & SLQueue::front() +{ + return p.front(); +} + + +inline void SLQueue::del_front() +{ + p.del_front(); +} + +inline void SLQueue::operator =(const SLQueue& s) +{ + p = s.p; +} + +inline int SLQueue::empty() +{ + return p.empty(); +} + +inline int SLQueue::full() +{ + return 0; +} + +inline int SLQueue::length() +{ + return p.length(); +} + +inline int SLQueue::OK() +{ + return p.OK(); +} + +inline void SLQueue::clear() +{ + p.clear(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/SLSet.ccP b/gnu/lib/libg++/g++-include/gen/SLSet.ccP new file mode 100644 index 0000000000..5f580849ff --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SLSet.ccP @@ -0,0 +1,76 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".SLSet.h" + +int SLSet::OK() +{ + int v = p.OK(); + v &= count == p.length(); + if (!v) error("invariant failure"); + return v; +} + +Pix SLSet::seek( item) +{ + for (Pix i = p.first(); i != 0 && !EQ(p(i),item); p.next(i)); + return i; +} + +Pix SLSet::add( item) +{ + Pix i = seek(item); + if (i == 0) + { + ++count; + i = p.append(item); + } + return i; +} + +void SLSet::del( item) +{ + Pix i = p.first(); + if (i == 0) + return; + else if (EQ(p(i), item)) + { + --count; + p.del_front(); + } + else + { + Pix trail = i; + p.next(i); + while (i != 0) + { + if (EQ(p(i), item)) + { + --count; + p.del_after(trail); + return; + } + trail = i; + p.next(i); + } + } +} + diff --git a/gnu/lib/libg++/g++-include/gen/SLSet.hP b/gnu/lib/libg++/g++-include/gen/SLSet.hP new file mode 100644 index 0000000000..fcf153633f --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SLSet.hP @@ -0,0 +1,87 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _SLSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SLSet_h 1 + +#include ".Set.h" +#include ".SLList.h" + +class SLSet : public Set +{ +protected: + SLList p; + +public: + SLSet(); + SLSet(const SLSet&); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item); + + int OK(); +}; + +inline SLSet::SLSet() : p() { count = 0; } + +inline SLSet::SLSet(const SLSet& s) : p(s.p) { count = s.count; } + +inline Pix SLSet::first() +{ + return p.first(); +} + +inline void SLSet::next(Pix & idx) +{ + p.next(idx); +} + +inline & SLSet::operator ()(Pix idx) +{ + return p(idx); +} + +inline void SLSet::clear() +{ + count = 0; p.clear(); +} + +inline int SLSet::contains ( item) +{ + return seek(item) != 0; +} + +inline int SLSet::owns (Pix idx) +{ + return p.owns(idx); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/SLStack.ccP b/gnu/lib/libg++/g++-include/gen/SLStack.ccP new file mode 100644 index 0000000000..3996b41fac --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SLStack.ccP @@ -0,0 +1,4 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".SLStack.h" diff --git a/gnu/lib/libg++/g++-include/gen/SLStack.hP b/gnu/lib/libg++/g++-include/gen/SLStack.hP new file mode 100644 index 0000000000..e20d9b9c2a --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SLStack.hP @@ -0,0 +1,109 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _SLStack_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SLStack_h 1 + +#include ".SLList.h" +#include ".Stack.h" + +class SLStack : public Stack +{ + SLList p; + +public: + SLStack(); + SLStack(const SLStack& s); + ~SLStack(); + + void operator = (const SLStack&); + + void push( item); + pop(); + & top(); + void del_top(); + + int empty(); + int full(); + int length(); + + void clear(); + + int OK(); + +}; + +inline SLStack::SLStack() :p() {} +inline SLStack::SLStack(const SLStack& a) : p(a.p) {} +inline SLStack::~SLStack() {} + +inline void SLStack::push( item) +{ + p.prepend(item); +} + +inline SLStack::pop() +{ + return p.remove_front(); +} + +inline & SLStack::top() +{ + return p.front(); +} + +inline void SLStack::del_top() +{ + p.del_front(); +} + +inline void SLStack::operator =(const SLStack& s) +{ + p = s.p; +} + +inline int SLStack::empty() +{ + return p.empty(); +} + +inline int SLStack::full() +{ + return 0; +} + +inline int SLStack::length() +{ + return p.length(); +} + +inline int SLStack::OK() +{ + return p.OK(); +} + +inline void SLStack::clear() +{ + p.clear(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/Set.ccP b/gnu/lib/libg++/g++-include/gen/Set.ccP new file mode 100644 index 0000000000..f312aa15cc --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/Set.ccP @@ -0,0 +1,116 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".Set.h" + + +Pix Set::seek( item) +{ + for (Pix i = first(); i != 0 && !(EQ((*this)(i), item)); next(i)); + return i; +} + +int Set::owns(Pix idx) +{ + if (idx == 0) return 0; + for (Pix i = first(); i; next(i)) if (i == idx) return 1; + return 0; +} + +void Set::clear() +{ + Pix i = first(); + while (i != 0) + { + del((*this)(i)); + i = first(); + } +} + +int Set::contains ( item) +{ + return seek(item) != 0; +} + +int Set::operator <= (Set& b) +{ + if (count > b.count) return 0; + if (count == 0) return 1; + for (Pix i = first(); i; next(i)) if (b.seek((*this)(i)) == 0) return 0; + return 1; +} + +int Set::operator == (Set& b) +{ + int n = count; + if (n != b.count) return 0; + if (n == 0) return 1; + Pix i = first(); + Pix j = b.first(); + while (n-- > 0) + { + if ((b.seek((*this)(i)) == 0) || (seek(b(j)) == 0)) return 0; + next(i); + b.next(j); + } + return 1; +} + +int Set::operator != (Set& b) +{ + return !(*this == b); +} + +void Set::operator |= (Set& b) +{ + if (&b != this) + for (Pix i = b.first(); i; b.next(i)) add(b(i)); +} + +void Set::operator -= (Set& b) +{ + if (&b == this) + clear(); + else + for (Pix i = b.first(); i; b.next(i)) del(b(i)); +} + + +void Set::operator &= (Set& b) +{ + if (&b != this) + { + Pix i = first(); + Pix n = i; + while (i != 0) + { + next(n); + if (b.seek((*this)(i)) == 0) del((*this)(i)); + i = n; + } + } +} + +void Set::error(const char* msg) +{ + (*lib_error_handler)("Set", msg); +} diff --git a/gnu/lib/libg++/g++-include/gen/Set.hP b/gnu/lib/libg++/g++-include/gen/Set.hP new file mode 100644 index 0000000000..6c21922627 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/Set.hP @@ -0,0 +1,78 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _Set_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Set_h 1 + +#include +#include ".defs.h" + +class Set +{ +protected: + + int count; + +public: + virtual ~Set(); + + int length(); // current number of items + int empty(); + + virtual Pix add( item) = 0; // add item; return Pix + virtual void del( item) = 0; // delete item + virtual int contains( item); // is item in set? + + virtual void clear(); // delete all items + + virtual Pix first() = 0; // Pix of first item or 0 + virtual void next(Pix& i) = 0; // advance to next or 0 + virtual & operator () (Pix i) = 0; // access item at i + + virtual int owns(Pix i); // is i a valid Pix ? + virtual Pix seek( item); // Pix of item + + void operator |= (Set& b); // add all items in b + void operator -= (Set& b); // delete items also in b + void operator &= (Set& b); // delete items not in b + + int operator == (Set& b); + int operator != (Set& b); + int operator <= (Set& b); + + void error(const char* msg); + virtual int OK() = 0; // rep invariant +}; + +inline Set::~Set() {} + +inline int Set::length() +{ + return count; +} + +inline int Set::empty() +{ + return count == 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/SkipBag.ccP b/gnu/lib/libg++/g++-include/gen/SkipBag.ccP new file mode 100644 index 0000000000..cc347e9813 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SkipBag.ccP @@ -0,0 +1,322 @@ +// This may look like C code, but it is really -*- C++ -*- + +/* +Copyright (C) 1991 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + * Bags implemented via William Pugh SkipList algorithms. + * CACM, June 1990, p 668-676. + * + */ + +#include +#include +#include ".SkipBag.h" + +MLCG* SkipBag::gen = 0; +int SkipBaginit::count = 0; + +static int countbits(long bits) +{ + int n = 0; + while(bits>>=1L) n++; + return n; +} + +SkipBag::SkipBag(long size) +: level(0), + header(new SkipBagNode (countbits(size)+1)), + max_levels (countbits(size)+1), + random_bits(gen->asLong()), + randoms_left(BITS_IN_RANDOM / 2) +{ + SkipBagNodePtr *buffer_start = header->forward; + SkipBagNodePtr *trav = &header->forward[max_levels]; + + count = 0; + while (trav > buffer_start) + *--trav = (SkipBagNodePtr) header; +} + +SkipBag::SkipBag(SkipBag& b) +: level (0), + header (new SkipBagNode (b.max_levels)), + max_levels (b.max_levels), + random_bits (gen->asLong()), + randoms_left (BITS_IN_RANDOM / 2) +{ + SkipBagNodePtr *buffer_start = header->forward; + SkipBagNodePtr *trav = &header->forward[max_levels]; + + count = 0; + + while (trav > buffer_start) + *--trav = (SkipBagNodePtr)header; + + for (SkipBagNodePtr t = b.leftmost(); t; t = b.succ(t)) + add(t->item); +} + +Pix SkipBag::add ( item) +{ + SkipBagNodePtr *update = new SkipBagNodePtr[max_levels+1]; + SkipBagNodePtr curr = (SkipBagNodePtr) this->header; + int l = level; + SkipBagNodePtr temp; + + do + { + while ((temp = curr->forward[l])!=header && + CMP(temp->item, item) < 0) + curr = temp; + update[l] = curr; + } + while (--l >= 0); + + if ((l = random_level ()) > level) + { + l = ++level; + update[l] = (SkipBagNodePtr)header; + }; + + temp = new RealSkipBagNode (item, l); + SkipBagNodePtr *temp_forward = temp->forward; + + do + { + SkipBagNodePtr *curr_forward = update[l]->forward; + + temp_forward[l] = curr_forward[l]; + curr_forward[l] = temp; + } + while (--l >= 0); + + count++; + delete update; + return Pix(temp); +} + +void SkipBag::del( key) +{ + + int l = level; + int curr_level = level; + SkipBagNodePtr *update = new SkipBagNodePtr[max_levels+1]; + SkipBagNodePtr curr = (SkipBagNodePtr)header; + SkipBagNodePtr temp; + + do + { + while ((temp = curr->forward[l])!=header + && CMP(temp->item,key) < 0) + curr = temp; + update[l] = curr; + } + while (--l >= 0); + + if (CMP(temp->item,key)==0) + { + SkipBagNodePtr *temp_forward = temp->forward; + + for (l = 0; + l <= curr_level && (curr = update[l])->forward[l] == temp; + l++) + curr->forward[l] = temp_forward[l]; + + delete temp; + + SkipBagNodePtr *forward = header->forward; + + while (forward[curr_level]==header && curr_level > 0) + curr_level--; + + level = curr_level; + count--; + delete update; + return; + } +} + +SkipBagNodePtr SkipBag::rightmost() +{ + SkipBagNodePtr temp; + SkipBagNode* curr = header; + int l = level; + + do + while ((temp = curr->forward[l])!=header) + curr = temp; + while (--l >= 0); + + return temp==header ? 0 : temp; +} + +SkipBagNodePtr SkipBag::pred(SkipBagNodePtr t) +{ + SkipBagNodePtr temp, curr = (SkipBagNodePtr) header; + int l = level; + + do + while ((temp = curr->forward[l])!=t) + curr = temp; + while (--l >= 0); + + return curr == header ? 0 : curr; +} + +void SkipBag::_kill() +{ + SkipBagNode *p = this->header->forward[0]; + + while (p != header) + { + SkipBagNodePtr q = p->forward[0]; + delete p; + p = q; + } +} + +void SkipBag::clear() +{ + SkipBagNodePtr *buffer_start = header->forward; + SkipBagNodePtr *trav = &header->forward[level+1]; + _kill(); + count = 0; + + while (trav > buffer_start) + *--trav = (SkipBagNodePtr)header; +} + +Pix SkipBag::seek( key, Pix i) +{ + SkipBagNodePtr temp; + SkipBagNode *curr = header; + int l = level; + if (i) + curr = (SkipBagNode *)i; + + do + { + while ((temp = curr->forward[l])!=header && + CMP(temp->item, key) < 0) + curr = temp; + } + while (--l >= 0); + + if (CMP(temp->item, key) != 0) + return 0; + else + { + return Pix(temp); + } +} + + +int SkipBag::nof( item) +{ + int n = 0; + SkipBagNodePtr t = (SkipBagNodePtr)(seek(item)); + if (t != 0) + { + do + { + ++n; + t = succ(t); + } while (t != 0 && EQ(item, t->item)); + } + return n; +} + +void SkipBag::remove( key) +{ + Pix t = seek(key); + while (t != 0) + { + del(key); + t = seek(key); + } +} + + +/* + * random function for probabilistic balancing + * + * Hardwired for p = .25. Not too flexible, + * but fast. Changing this would require a constructor + * that would accept a different value for p, etc. + * Perhaps someone else would like to implement this? + * + */ +int SkipBag::random_level (void) +{ + int rlevel = 0; + int b; + + do + { + b = random_bits & 3L; + if (!b) + rlevel++; + random_bits >>= 2; + if (--randoms_left == 0) + { + random_bits = gen->asLong(); + randoms_left = BITS_IN_RANDOM / 2; + }; + } + while (!b); + + return rlevel > max_levels ? max_levels : rlevel; +} + +int SkipBag::OK() +{ + int v = 1; + if (header == 0) + v = 0; + else + { + int n = 0; + SkipBagNodePtr trail = leftmost(); + SkipBagNodePtr t = 0; + if (trail) t = succ(trail); + if (t) n++; + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} + +SkipBaginit::SkipBaginit() +{ + if (!count) + SkipBag::gen = new MLCG(time(0)); + count++; +} + +SkipBaginit::~SkipBaginit() +{ + count--; + if (!count) + delete SkipBag::gen; +} diff --git a/gnu/lib/libg++/g++-include/gen/SkipBag.hP b/gnu/lib/libg++/g++-include/gen/SkipBag.hP new file mode 100644 index 0000000000..1bfe9da446 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SkipBag.hP @@ -0,0 +1,171 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1991 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + * Bags implemented via William Pugh SkipList algorithms. + * CACM, June 1990, p 668-676. + * + */ + +#ifndef _SkipBag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SkipBag_h 1 + +#include ".Bag.h" + +#include +#include + +class SkipBag; +class RealSkipBagNode; + +class SkipBagNode +{ +friend class SkipBag; + private: + RealSkipBagNode * * forward; + SkipBagNode(int size); +}; + +class RealSkipBagNode : public SkipBagNode +{ +friend class SkipBag; + private: + item; + RealSkipBagNode( h, int size); +}; + +typedef RealSkipBagNode* SkipBagNodePtr; + +inline SkipBagNode::SkipBagNode(int size) +: forward(new SkipBagNodePtr[size+1]) +{ +} + +inline RealSkipBagNode::RealSkipBagNode( h, int size) +: item(h), + SkipBagNode(size) +{ +} + +class SkipBag : public Bag +{ +friend class SkipBaginit; + protected: + SkipBagNode* header; + int level; + int max_levels; + int randoms_left; + long random_bits; + + static MLCG* gen; + int random_level(void); + + SkipBagNodePtr leftmost(void); + SkipBagNodePtr rightmost(void); + SkipBagNodePtr pred(SkipBagNodePtr t); + SkipBagNodePtr succ(SkipBagNodePtr t); + void _kill(void); + + private: + enum { BITS_IN_RANDOM = LONGBITS-1 }; + + public: + SkipBag(long size=DEFAULT_INITIAL_CAPACITY); + SkipBag(SkipBag& a); + ~SkipBag(void); + + Pix add( i); + void del( i); + void remove(i); + int nof( i); + int contains( i); + + void clear(void); + + Pix first(void); + void next(Pix& i); + & operator () (Pix i); + Pix seek( i, Pix from = 0); + + Pix last(void); + void prev(Pix& i); + + int OK(void); +}; + +inline SkipBagNodePtr SkipBag::leftmost(void) +{ + return header->forward[0]; +} + +inline SkipBagNodePtr SkipBag::succ(SkipBagNodePtr t) +{ + SkipBagNodePtr result = 0; + if (t->forward[0]!=header) result = t->forward[0]; + return result; +} + +inline Pix SkipBag::first(void) +{ + return Pix(leftmost()); +} + +inline Pix SkipBag::last(void) +{ + return Pix(rightmost()); +} + +inline void SkipBag::next(Pix& i) +{ + if (i != 0) i = Pix(succ((SkipBagNodePtr)i)); +} + +inline & SkipBag::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SkipBagNodePtr)i)->item; +} + +inline void SkipBag::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((SkipBagNodePtr)i)); +} + +inline int SkipBag::contains( key) +{ + return seek(key) != 0; +} + +inline SkipBag::~SkipBag() +{ + _kill(); + delete header; +} + +static class SkipBaginit +{ + public: + SkipBaginit(); + ~SkipBaginit(); + private: + static int count; +} skipBaginit; + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/SkipMap.ccP b/gnu/lib/libg++/g++-include/gen/SkipMap.ccP new file mode 100644 index 0000000000..8b6afe2edd --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SkipMap.ccP @@ -0,0 +1,307 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1991 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif + +#include +#include +#include "..SkipMap.h" + +/* + * Bags implemented via William Pugh SkipList algorithms. + * CACM, June 1990, p 668-676. + * + */ + +MLCG* SkipMap::gen = 0; +int SkipMapinit::count = 0; + +static int countbits(long bits) +{ + int n = 0; + while(bits>>=1) n++; + return n; +} + +SkipMap::SkipMap( dflt, long size) +: Map(dflt), + level(0), + header(new SkipMapNode (countbits(size)+1)), + max_levels (countbits(size)+1), + random_bits(gen->asLong()), + randoms_left(BITS_IN_RANDOM / 2) +{ + SkipMapNodePtr *buffer_start = header->forward; + SkipMapNodePtr *trav = &header->forward[max_levels]; + + count = 0; + while (trav > buffer_start) + *--trav = (SkipMapNodePtr) header; +} + +SkipMap::SkipMap(SkipMap& b) +: Map(b.def), + level (0), + header (new SkipMapNode (b.max_levels)), + max_levels (b.max_levels), + random_bits (gen->asLong()), + randoms_left (BITS_IN_RANDOM / 2) +{ + SkipMapNodePtr *buffer_start = header->forward; + SkipMapNodePtr *trav = &header->forward[max_levels]; + + count = 0; + + while (trav > buffer_start) + *--trav = (SkipMapNodePtr)header; + + for (SkipMapNodePtr t = b.leftmost(); t; t = b.succ(t)) + (*this)[t->item] = t->cont; +} + +& SkipMap::operator [] ( item) +{ + SkipMapNodePtr *update = new SkipMapNodePtr[max_levels+1]; + SkipMapNodePtr curr = + (SkipMapNodePtr) this->header; + int l = level; + SkipMapNodePtr temp; + + do + { + while ((temp = curr->forward[l])!=header && + CMP(temp->item, item) < 0) + curr = temp; + update[l] = curr; + } + while (--l >= 0); + + if (temp != header && CMP(temp->item, item) == 0) + { + delete update; + return temp->cont; + } + + if ((l = random_level ()) > level) + { + l = ++level; + update[l] = (SkipMapNodePtr)header; + }; + + temp = new RealSkipMapNode (item, def, l); + SkipMapNodePtr *temp_forward = temp->forward; + + do + { + SkipMapNodePtr *curr_forward = update[l]->forward; + + temp_forward[l] = curr_forward[l]; + curr_forward[l] = temp; + } + while (--l >= 0); + + count++; + delete update; + return temp->cont; +} + +void SkipMap::del( key) +{ + + int l = level; + int curr_level = level; + SkipMapNodePtr *update = new SkipMapNodePtr[max_levels+1]; + SkipMapNodePtr curr = (SkipMapNodePtr)header; + SkipMapNodePtr temp; + + do + { + while ((temp = curr->forward[l])!=header + && CMP(temp->item,key) < 0) + curr = temp; + update[l] = curr; + } + while (--l >= 0); + + if (CMP(temp->item,key)==0) + { + SkipMapNodePtr *temp_forward = temp->forward; + + for (l = 0; + l <= curr_level && (curr = update[l])->forward[l] == temp; + l++) + curr->forward[l] = temp_forward[l]; + + delete temp; + + SkipMapNodePtr *forward = header->forward; + + while (forward[curr_level]==header && curr_level > 0) + curr_level--; + + level = curr_level; + count--; + delete update; + return; + } +} + +SkipMapNodePtr SkipMap::rightmost() +{ + SkipMapNodePtr temp; + SkipMapNode* curr = header; + int l = level; + + do + while ((temp = curr->forward[l])!=header) + curr = temp; + while (--l >= 0); + + return temp==header ? 0 : temp; +} + +SkipMapNodePtr SkipMap::pred(SkipMapNodePtr t) +{ + SkipMapNodePtr temp, curr = (SkipMapNodePtr) header; + int l = level; + + do + while ((temp = curr->forward[l])!=t) + curr = temp; + while (--l >= 0); + + return curr == header ? 0 : curr; +} + +void SkipMap::_kill() +{ + SkipMapNode *p = this->header->forward[0]; + + while (p != header) + { + SkipMapNodePtr q = p->forward[0]; + delete p; + p = q; + } +} + +void SkipMap::clear() +{ + SkipMapNodePtr *buffer_start = header->forward; + SkipMapNodePtr *trav = &header->forward[level+1]; + _kill(); + count = 0; + + while (trav > buffer_start) + *--trav = (SkipMapNodePtr)header; +} + +Pix SkipMap::seek( key) +{ + SkipMapNodePtr temp; + SkipMapNode *curr = header; + int l = level; + + do + { + while ((temp = curr->forward[l])!=header && + CMP(temp->item, key) < 0) + curr = temp; + } + while (--l >= 0); + + if (CMP(temp->item, key) != 0) + return 0; + else + { + return Pix(temp); + } +} + +/* + * random function for probabilistic balancing + * + * Hardwired for p = .25. Not too flexible, + * but fast. Changing this would require a constructor + * that would accept a different value for p, etc. + * Perhaps someone else would like to implement this? + * + */ +int SkipMap::random_level (void) +{ + int rlevel = 0; + int b; + + do + { + b = random_bits & 3L; + if (!b) + rlevel++; + random_bits >>= 2; + if (--randoms_left == 0) + { + random_bits = gen->asLong(); + randoms_left = BITS_IN_RANDOM / 2; + }; + } + while (!b); + + return rlevel > max_levels ? max_levels : rlevel; +} + +int SkipMap::OK() +{ + int v = 1; + if (header == 0) + v = 0; + else + { + int n = 0; + SkipMapNodePtr trail = leftmost(); + SkipMapNodePtr t = 0; + if (trail) t = succ(trail); + if (t) n++; + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} + +SkipMapinit::SkipMapinit() +{ + if (!count) + SkipMap::gen = new MLCG(time(0)); + count++; +} + +SkipMapinit::~SkipMapinit() +{ + count--; + if (!count) + delete SkipMap::gen; +} + + diff --git a/gnu/lib/libg++/g++-include/gen/SkipMap.hP b/gnu/lib/libg++/g++-include/gen/SkipMap.hP new file mode 100644 index 0000000000..65766d64c7 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SkipMap.hP @@ -0,0 +1,176 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1991 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + * Bags implemented via William Pugh SkipList algorithms. + * CACM, June 1990, p 668-676. + * + */ + +#ifndef _SkipMap_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SkipMap_h 1 + +#include "..Map.h" + +#include +#include + +class SkipMap; +class RealSkipMapNode; + +class SkipMapNode +{ +friend class SkipMap; + private: + RealSkipMapNode * * forward; + protected: + SkipMapNode(int size); +}; + +class RealSkipMapNode : public SkipMapNode +{ +friend class SkipMap; + private: + item; + cont; + RealSkipMapNode( h, i, int size); +}; + +typedef RealSkipMapNode* SkipMapNodePtr; + +inline SkipMapNode::SkipMapNode(int size) +: forward(new SkipMapNodePtr[size+1]) +{ +} + +inline RealSkipMapNode::RealSkipMapNode( h, i, int size) +: item(h), cont(i), + SkipMapNode(size) +{ +} + +class SkipMap : public Map +{ +friend class SkipMapinit; + protected: + SkipMapNode* header; + int level; + int max_levels; + int randoms_left; + long random_bits; + + static MLCG* gen; + int random_level(void); + + SkipMapNodePtr leftmost(); + SkipMapNodePtr rightmost(); + SkipMapNodePtr pred(SkipMapNodePtr t); + SkipMapNodePtr succ(SkipMapNodePtr t); + void _kill(); + private: + enum { BITS_IN_RANDOM = LONGBITS-1 }; + + public: + SkipMap( dflt, long size=DEFAULT_INITIAL_CAPACITY); + SkipMap(SkipMap& a); + ~SkipMap(); + + & operator [] ( key); + + void del( key); + + Pix first(); + void next(Pix& i); + & key(Pix i); + & contents(Pix i); + + Pix seek( key); + int contains( key); + void clear(); + + Pix last(); + void prev(Pix& i); + + int OK(); +}; + +inline SkipMap::~SkipMap() +{ + _kill(); + delete header; +} + +inline SkipMapNodePtr SkipMap::leftmost() +{ + return header->forward[0]==header ? 0 : header->forward[0]; +} + +inline Pix SkipMap::first() +{ + return Pix(leftmost()); +} + +inline Pix SkipMap::last() +{ + return Pix(rightmost()); +} + +inline SkipMapNodePtr SkipMap::succ(SkipMapNodePtr t) +{ + return t->forward[0]==header ? 0 : t->forward[0]; +} + +inline void SkipMap::next(Pix& i) +{ + if (i != 0) i = Pix(succ((SkipMapNodePtr)i)); +} + +inline void SkipMap::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((SkipMapNodePtr)i)); +} + +inline & SkipMap::key (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SkipMapNodePtr)i)->item; +} + +inline & SkipMap::contents (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SkipMapNodePtr)i)->cont; +} + +inline int SkipMap::contains( key) +{ + return seek(key) != 0; +} + +static class SkipMapinit +{ + public: + SkipMapinit(); + ~SkipMapinit(); + private: + static int count; +} skipMapinit; + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/SkipSet.ccP b/gnu/lib/libg++/g++-include/gen/SkipSet.ccP new file mode 100644 index 0000000000..a1bf33d0fa --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SkipSet.ccP @@ -0,0 +1,395 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1991 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + * Sets implemented via William Pugh SkipList algorithms. + * CACM, June 1990, p 668-676. + * + */ + +#include +#include + +#include ".SkipSet.h" + +MLCG* SkipSet::gen = 0; +int SkipSetinit::count = 0; + +static int countbits(long bits) +{ + int n = 0; + while(bits>>=1L) n++; + return n; +} + +SkipSet::SkipSet(long size) +: level(0), + header(new SkipSetNode (countbits(size)+1)), + max_levels (countbits(size)+1), + random_bits(gen->asLong()), + randoms_left(BITS_IN_RANDOM / 2) +{ + SkipSetNodePtr *buffer_start = header->forward; + SkipSetNodePtr *trav = &header->forward[max_levels]; + + count = 0; + while (trav > buffer_start) + *--trav = (SkipSetNodePtr) header; +} + +SkipSet::SkipSet(SkipSet& b) +: level (0), + header (new SkipSetNode (b.max_levels)), + max_levels (b.max_levels), + random_bits (gen->asLong()), + randoms_left (BITS_IN_RANDOM / 2) +{ + SkipSetNodePtr *buffer_start = header->forward; + SkipSetNodePtr *trav = &header->forward[max_levels]; + + count = 0; + + while (trav > buffer_start) + *--trav = (SkipSetNodePtr)header; + + for (SkipSetNodePtr t = b.leftmost(); t; t = b.succ(t)) + add(t->item); +} + +/* relationals */ + +int SkipSet::operator == (SkipSet& y) +{ + if (count != y.count) + return 0; + else + { + SkipSetNodePtr t = leftmost(); + SkipSetNodePtr u = y.leftmost(); + for (;;) + { + if (t == 0) + return 1; + else if (!EQ(t->item, u->item)) + return 0; + else + { + t = succ(t); + u = y.succ(u); + } + } + } +} + +int SkipSet::operator <= (SkipSet& y) +{ + if (count > y.count) + return 0; + else + { + SkipSetNodePtr t = leftmost(); + SkipSetNodePtr u = y.leftmost(); + for (;;) + { + if (t == 0) + return 1; + else if (u == 0) + return 0; + int cmp = CMP(t->item, u->item); + if (cmp == 0) + { + t = succ(t); + u = y.succ(u); + } + else if (cmp < 0) + return 0; + else + u = y.succ(u); + } + } +} + + +void SkipSet::operator |=(SkipSet& y) +{ + if (&y == this) return; + SkipSetNodePtr u = y.leftmost(); + while (u != 0) + { + add(u->item); + u = y.succ(u); + } +} + +void SkipSet::operator &= (SkipSet& y) +{ + if (y.count == 0) + clear(); + else if (&y != this && count != 0) + { + SkipSetNodePtr t = leftmost(); + while (t != 0) + { + SkipSetNodePtr s = succ(t); + if (y.seek(t->item) == 0) del(t->item); + t = s; + } + } +} + + +void SkipSet::operator -=(SkipSet& y) +{ + if (&y == this) + clear(); + else if (y.count != 0) + { + SkipSetNodePtr t = leftmost(); + while (t != 0) + { + SkipSetNodePtr s = succ(t); + if (y.seek(t->item) != 0) del(t->item); + t = s; + } + } +} + +Pix SkipSet::add ( i) +{ + SkipSetNodePtr *update = new SkipSetNodePtr[max_levels+1]; + SkipSetNodePtr curr = (SkipSetNodePtr) this->header; + int l = level; + SkipSetNodePtr temp; + + do + { + while ((temp = curr->forward[l])!=header && + CMP(temp->item, i) < 0) + curr = temp; + update[l] = curr; + } + while (--l >= 0); + + if (temp != header && CMP(temp->item, i) == 0) + return Pix(temp); + + if ((l = random_level ()) > level) + { + l = ++level; + update[l] = (SkipSetNodePtr)header; + }; + + temp = new RealSkipSetNode (i, l); + SkipSetNodePtr *temp_forward = temp->forward; + + do + { + SkipSetNodePtr *curr_forward = update[l]->forward; + + temp_forward[l] = curr_forward[l]; + curr_forward[l] = temp; + } + while (--l >= 0); + + count++; + delete update; + return Pix(temp); +} + +void SkipSet::del( key) +{ + + int l = level; + int curr_level = level; + SkipSetNodePtr *update = new SkipSetNodePtr[max_levels+1]; + SkipSetNodePtr curr = (SkipSetNodePtr)header; + SkipSetNodePtr temp; + + do + { + while ((temp = curr->forward[l])!=header + && CMP(temp->item,key) < 0) + curr = temp; + update[l] = curr; + } + while (--l >= 0); + + if (CMP(temp->item,key)==0) + { + SkipSetNodePtr *temp_forward = temp->forward; + + for (l = 0; + l <= curr_level && (curr = update[l])->forward[l] == temp; + l++) + curr->forward[l] = temp_forward[l]; + + delete temp; + + SkipSetNodePtr *forward = header->forward; + + while (forward[curr_level]==header && curr_level > 0) + curr_level--; + + level = curr_level; + count--; + delete update; + return; + } +} + +SkipSetNodePtr SkipSet::rightmost() +{ + SkipSetNodePtr temp; + SkipSetNode* curr = header; + int l = level; + + do + while ((temp = curr->forward[l])!=header) + curr = temp; + while (--l >= 0); + + return temp==header ? 0 : temp; +} + +SkipSetNodePtr SkipSet::pred(SkipSetNodePtr t) +{ + SkipSetNodePtr temp, curr = (SkipSetNodePtr) header; + int l = level; + + do + while ((temp = curr->forward[l])!=t) + curr = temp; + while (--l >= 0); + + return curr == header ? 0 : curr; +} + +void SkipSet::_kill() +{ + SkipSetNode *p = this->header->forward[0]; + + while (p != header) + { + SkipSetNodePtr q = p->forward[0]; + delete p; + p = q; + } +} + +void SkipSet::clear() +{ + SkipSetNodePtr *buffer_start = header->forward; + SkipSetNodePtr *trav = &header->forward[level+1]; + _kill(); + count = 0; + + while (trav > buffer_start) + *--trav = (SkipSetNodePtr)header; +} + +Pix SkipSet::seek( key) +{ + SkipSetNodePtr temp; + SkipSetNode *curr = header; + int l = level; + + do + { + while ((temp = curr->forward[l])!=header && + CMP(temp->item, key) < 0) + curr = temp; + } + while (--l >= 0); + + if (CMP(temp->item, key) != 0) + return 0; + else + { + return Pix(temp); + } +} + + +/* + * random function for probabilistic balancing + * + * Hardwired for p = .25. Not too flexible, + * but fast. Changing this would require a constructor + * that would accept a different value for p, etc. + * Perhaps someone else would like to implement this? + * + */ +int SkipSet::random_level (void) +{ + int rlevel = 0; + int b; + + do + { + b = random_bits & 3L; + if (!b) + rlevel++; + random_bits >>= 2; + if (--randoms_left == 0) + { + random_bits = gen->asLong(); + randoms_left = BITS_IN_RANDOM / 2; + }; + } + while (!b); + + return rlevel > max_levels ? max_levels : rlevel; +} + +int SkipSet::OK() +{ + int v = 1; + if (header == 0) + v = 0; + else + { + int n = 0; + SkipSetNodePtr trail = leftmost(); + SkipSetNodePtr t = 0; + if (trail) t = succ(trail); + if (t) n++; + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} + +SkipSetinit::SkipSetinit() +{ + if (!count) + SkipSet::gen = new MLCG(time(0)); + count++; +} + +SkipSetinit::~SkipSetinit() +{ + count--; + if (!count) + delete SkipSet::gen; +} diff --git a/gnu/lib/libg++/g++-include/gen/SkipSet.hP b/gnu/lib/libg++/g++-include/gen/SkipSet.hP new file mode 100644 index 0000000000..cd95305239 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SkipSet.hP @@ -0,0 +1,187 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1991 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + * Sets implemented via William Pugh SkipList algorithms. + * CACM, June 1990, p 668-676. + * + */ + +#ifndef _SkipSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SkipSet_h 1 + +#include ".Set.h" + +#include +#include + +class SkipSet; +class RealSkipSetNode; + +class SkipSetNode +{ +friend class SkipSet; + private: + RealSkipSetNode * * forward; + SkipSetNode(int size); +}; + +class RealSkipSetNode : public SkipSetNode +{ +friend class SkipSet; + private: + item; + RealSkipSetNode( h, int size); +}; + +typedef RealSkipSetNode* SkipSetNodePtr; + +inline SkipSetNode::SkipSetNode(int size) +: forward(new SkipSetNodePtr[size+1]) +{ +} + +inline RealSkipSetNode::RealSkipSetNode( h, int size) +: item(h), + SkipSetNode(size) +{ +} + +class SkipSet : public Set +{ +friend class SkipSetinit; + protected: + SkipSetNode* header; + int level; + int max_levels; + int randoms_left; + long random_bits; + + static MLCG* gen; + int random_level(void); + + SkipSetNodePtr leftmost(void); + SkipSetNodePtr rightmost(void); + SkipSetNodePtr pred(SkipSetNodePtr t); + SkipSetNodePtr succ(SkipSetNodePtr t); + void _kill(void); + + private: + enum { BITS_IN_RANDOM = LONGBITS-1 }; + public: + SkipSet(long size=DEFAULT_INITIAL_CAPACITY); + SkipSet(SkipSet& a); + ~SkipSet(); + + Pix add( i); + void del( i); + int contains( i); + + void clear(void); + + Pix first(void); + void next(Pix& i); + & operator () (Pix i); + Pix seek( i); + + Pix last(void); + void prev(Pix& i); + + void operator |= (SkipSet& b); + void operator -= (SkipSet& b); + void operator &= (SkipSet& b); + + int operator == (SkipSet& b); + int operator != (SkipSet& b); + int operator <= (SkipSet& b); + + int OK(void); +}; + +/* + * A little overkill on the inlines. + * + */ + +inline SkipSet::~SkipSet(void) +{ + _kill(); + delete header; +} + +inline int SkipSet::operator != (SkipSet& b) +{ + return ! (*this == b); +} + +inline SkipSetNodePtr SkipSet::leftmost(void) +{ + return header->forward[0]; +} + +inline SkipSetNodePtr SkipSet::succ(SkipSetNodePtr t) +{ + SkipSetNodePtr result = 0; + if (t->forward[0]!=header) result = t->forward[0]; + return result; +} + +inline Pix SkipSet::first(void) +{ + return Pix(leftmost()); +} + +inline Pix SkipSet::last(void) +{ + return Pix(rightmost()); +} + +inline void SkipSet::next(Pix& i) +{ + if (i != 0) i = Pix(succ((SkipSetNodePtr)i)); +} + +inline void SkipSet::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((SkipSetNodePtr)i)); +} + +inline & SkipSet::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SkipSetNodePtr)i)->item; +} + + +inline int SkipSet::contains( key) +{ + return seek(key) != 0; +} + +static class SkipSetinit +{ + public: + SkipSetinit(); + ~SkipSetinit(); + private: + static int count; +} skipSetinit; + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/SplayBag.ccP b/gnu/lib/libg++/g++-include/gen/SplayBag.ccP new file mode 100644 index 0000000000..ff02a992a6 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SplayBag.ccP @@ -0,0 +1,445 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".SplayBag.h" + + +/* + + struct to simulate the special `null' node in the Sleater & Tarjan JACM 1985 + splay tree algorithms + + All routines use a version of their `simple top-down' splay alg. (p 669) + +*/ + +struct _dummySplayNode +{ + SplayNode* lt; + SplayNode* rt; + SplayNode* par; +} _dummy_null; + + +/* + traversal primitives +*/ + + +SplayNode* SplayBag::leftmost() +{ + SplayNode* t = root; + if (t != 0) while (t->lt != 0) t = t->lt; + return t; +} + +SplayNode* SplayBag::rightmost() +{ + SplayNode* t = root; + if (t != 0) while (t->rt != 0) t = t->rt; + return t; +} + +SplayNode* SplayBag::succ(SplayNode* t) +{ + if (t == 0) + return 0; + if (t->rt != 0) + { + t = t->rt; + while (t->lt != 0) t = t->lt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->lt) + return t->par; + else + t = t->par; + } + } +} + +SplayNode* SplayBag::pred(SplayNode* t) +{ + if (t == 0) + return 0; + else if (t->lt != 0) + { + t = t->lt; + while (t->rt != 0) t = t->rt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->rt) + return t->par; + else + t = t->par; + } + } +} + + + +Pix SplayBag::seek( key, Pix i) +{ + if (root == 0) return 0; + + SplayNode* t = (SplayNode*) i; + if (t != 0) + { + int cmp = CMP(key, t->item); + if (cmp == 0) + { + t = succ(t); + if (t != 0 && EQ(key, t->item)) + return Pix(t); + else + return 0; + } + else if (cmp < 0) + return 0; + } + + t = root; + int comp = CMP(key, t->item); + if (comp == 0) + return Pix(t); + + SplayNode* dummy = (SplayNode*)(&_dummy_null); + SplayNode* l = dummy; + SplayNode* r = dummy; + dummy->rt = dummy->lt = dummy->par = 0; + + while (comp != 0) + { + if (comp > 0) + { + SplayNode* tr = t->rt; + if (tr == 0) + break; + else + { + comp = CMP(key, tr->item); + if (comp <= 0 || tr->rt == 0) + { + l->rt = t; t->par = l; + l = t; + t = tr; + if (comp >= 0) + break; + } + else + { + if ((t->rt = tr->lt) != 0) t->rt->par = t; + tr->lt = t; t->par = tr; + l->rt = tr; tr->par = l; + l = tr; + t = tr->rt; + comp = CMP(key, t->item); + } + } + } + else + { + SplayNode* tl = t->lt; + if (tl == 0) + break; + else + { + comp = CMP(key, tl->item); + if (comp >= 0 || tl->lt == 0) + { + r->lt = t; t->par = r; + r = t; + t = tl; + if (comp <= 0) + break; + } + else + { + if ((t->lt = tl->rt) != 0) t->lt->par = t; + tl->rt = t; t->par = tl; + r->lt = tl; tl->par = r; + r = tl; + t = tl->lt; + comp = CMP(key, t->item); + } + } + } + } + if ((r->lt = t->rt) != 0) r->lt->par = r; + if ((l->rt = t->lt) != 0) l->rt->par = l; + if ((t->lt = dummy->rt) != 0) t->lt->par = t; + if ((t->rt = dummy->lt) != 0) t->rt->par = t; + t->par = 0; + root = t; + if (comp != 0) + return 0; + else + { + l = pred(t); + while (l != 0 && EQ(l->item, key)) { t = l; l = pred(l); } + return Pix(t); + } +} + +int SplayBag::nof( item) +{ + int n = 0; + SplayNode* t = (SplayNode*)(seek(item)); + if (t != 0) + { + do + { + ++n; + t = succ(t); + } while (t != 0 && EQ(item, t->item)); + } + return n; +} + +Pix SplayBag::add( item) +{ + ++count; + SplayNode* newnode = new SplayNode(item); + SplayNode* t = root; + if (t == 0) + { + root = newnode; + return Pix(root); + } + + int comp = CMP(item, t->item); + + SplayNode* dummy = (SplayNode*)(&_dummy_null); + SplayNode* l = dummy; + SplayNode* r = dummy; + dummy->rt = dummy->lt = dummy->par = 0; + + int done = 0; + while (!done) + { + if (comp >= 0) + { + SplayNode* tr = t->rt; + if (tr == 0) + { + tr = newnode; + comp = 0; done = 1; + } + else + comp = CMP(item, tr->item); + + if (comp <= 0) + { + l->rt = t; t->par = l; + l = t; + t = tr; + } + else + { + SplayNode* trr = tr->rt; + if (trr == 0) + { + trr = newnode; + comp = 0; done = 1; + } + else + comp = CMP(item, trr->item); + + if ((t->rt = tr->lt) != 0) t->rt->par = t; + tr->lt = t; t->par = tr; + l->rt = tr; tr->par = l; + l = tr; + t = trr; + } + } + else + { + SplayNode* tl = t->lt; + if (tl == 0) + { + tl = newnode; + comp = 0; done = 1; + } + else + comp = CMP(item, tl->item); + + if (comp >= 0) + { + r->lt = t; t->par = r; + r = t; + t = tl; + } + else + { + SplayNode* tll = tl->lt; + if (tll == 0) + { + tll = newnode; + comp = 0; done = 1; + } + else + comp = CMP(item, tll->item); + + if ((t->lt = tl->rt) != 0) t->lt->par = t; + tl->rt = t; t->par = tl; + r->lt = tl; tl->par = r; + r = tl; + t = tll; + } + } + } + if ((r->lt = t->rt) != 0) r->lt->par = r; + if ((l->rt = t->lt) != 0) l->rt->par = l; + if ((t->lt = dummy->rt) != 0) t->lt->par = t; + if ((t->rt = dummy->lt) != 0) t->rt->par = t; + t->par = 0; + root = t; + return Pix(root); +} + +void SplayBag::_del(SplayNode* t) +{ + if (t == 0) return; + + SplayNode* p = t->par; + + --count; + if (t->rt == 0) + { + if (t == root) + { + if ((root = t->lt) != 0) root->par = 0; + } + else if (t == p->lt) + { + if ((p->lt = t->lt) != 0) p->lt->par = p; + } + else + if ((p->rt = t->lt) != 0) p->rt->par = p; + } + else + { + SplayNode* r = t->rt; + SplayNode* l = r->lt; + for(;;) + { + if (l == 0) + { + if (t == root) + { + root = r; + r->par = 0; + } + else if (t == p->lt) + { + p->lt = r; + r->par = p; + } + else + { + p->rt = r; + r->par = p; + } + if ((r->lt = t->lt) != 0) r->lt->par = r; + break; + } + else + { + if ((r->lt = l->rt) != 0) r->lt->par = r; + l->rt = r; r->par = l; + r = l; + l = l->lt; + } + } + } + delete t; +} + + +void SplayBag::remove( key) +{ + SplayNode* t = (SplayNode*)(seek(key)); + while (t != 0) + { + _del(t); + t = (SplayNode*)(seek(key)); + } +} + + +void SplayBag::_kill(SplayNode* t) +{ + if (t != 0) + { + _kill(t->lt); + _kill(t->rt); + delete t; + } +} + + +SplayNode* SplayBag::_copy(SplayNode* t) +{ + if (t != 0) + { + SplayNode* l = _copy(t->lt); + SplayNode* r = _copy(t->rt); + SplayNode* x = new SplayNode(t->item, l, r); + if (l != 0) l->par = x; + if (r != 0) r->par = x; + return x; + } + else + return 0; +} + +int SplayBag::OK() +{ + int v = 1; + if (root == 0) + v = count == 0; + else + { + int n = 1; + SplayNode* trail = leftmost(); + SplayNode* t = succ(trail); + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) <= 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} + diff --git a/gnu/lib/libg++/g++-include/gen/SplayBag.hP b/gnu/lib/libg++/g++-include/gen/SplayBag.hP new file mode 100644 index 0000000000..9d7fcad1dd --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SplayBag.hP @@ -0,0 +1,144 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988, 1982 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _SplayBag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SplayBag_h 1 + +#include ".Bag.h" +#include ".SplayNode.h" + +class SplayBag : public Bag +{ +protected: + SplayNode* root; + + SplayNode* leftmost(); + SplayNode* rightmost(); + SplayNode* pred(SplayNode* t); + SplayNode* succ(SplayNode* t); + void _kill(SplayNode* t); + SplayNode* _copy(SplayNode* t); + void _del(SplayNode* t); + +public: + SplayBag(); + SplayBag(SplayBag& a); + ~SplayBag(); + + Pix add( item); + void del( item); + void remove(item); + int nof( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + Pix seek( item, Pix from = 0); + + Pix last(); + void prev(Pix& i); + + int OK(); +}; + + +inline SplayBag::~SplayBag() +{ + _kill(root); +} + +inline SplayBag::SplayBag() +{ + root = 0; + count = 0; +} + +inline SplayBag::SplayBag(SplayBag& b) +{ + count = b.count; + root = _copy(b.root); +} + +inline Pix SplayBag::first() +{ + return Pix(leftmost()); +} + +inline Pix SplayBag::last() +{ + return Pix(rightmost()); +} + +inline void SplayBag::next(Pix& i) +{ + if (i != 0) i = Pix(succ((SplayNode*)i)); +} + +inline void SplayBag::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((SplayNode*)i)); +} + +inline & SplayBag::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SplayNode*)i)->item; +} + +inline void SplayBag::clear() +{ + _kill(root); + count = 0; + root = 0; +} + +inline int SplayBag::contains( key) +{ + return seek(key) != 0; +} + +inline void SplayBag::del( key) +{ + _del((SplayNode*)(seek(key))); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/SplayMap.ccP b/gnu/lib/libg++/g++-include/gen/SplayMap.ccP new file mode 100644 index 0000000000..4be340db0f --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SplayMap.ccP @@ -0,0 +1,401 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include "..SplayMap.h" + + +/* + + struct to simulate the special `null' node in the Sleater & Tarjan JACM 1985 + splay tree algorithms + + All routines use a version of their `simple top-down' splay alg. (p 669) + +*/ + +struct _dummySplayNode +{ + SplayNode* lt; + SplayNode* rt; + SplayNode* par; +} _dummy_null; + + +/* + traversal primitives +*/ + + +SplayNode* SplayMap::leftmost() +{ + SplayNode* t = root; + if (t != 0) while (t->lt != 0) t = t->lt; + return t; +} + +SplayNode* SplayMap::rightmost() +{ + SplayNode* t = root; + if (t != 0) while (t->rt != 0) t = t->rt; + return t; +} + +SplayNode* SplayMap::succ(SplayNode* t) +{ + if (t == 0) + return 0; + if (t->rt != 0) + { + t = t->rt; + while (t->lt != 0) t = t->lt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->lt) + return t->par; + else + t = t->par; + } + } +} + +SplayNode* SplayMap::pred(SplayNode* t) +{ + if (t == 0) + return 0; + else if (t->lt != 0) + { + t = t->lt; + while (t->rt != 0) t = t->rt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->rt) + return t->par; + else + t = t->par; + } + } +} + + +Pix SplayMap::seek( key) +{ + SplayNode* t = root; + if (t == 0) + return 0; + + int comp = CMP(key, t->item); + if (comp == 0) + return Pix(t); + + SplayNode* dummy = (SplayNode*)(&_dummy_null); + SplayNode* l = dummy; + SplayNode* r = dummy; + dummy->rt = dummy->lt = dummy->par = 0; + + while (comp != 0) + { + if (comp > 0) + { + SplayNode* tr = t->rt; + if (tr == 0) + break; + else + { + comp = CMP(key, tr->item); + if (comp <= 0 || tr->rt == 0) + { + l->rt = t; t->par = l; + l = t; + t = tr; + if (comp >= 0) + break; + } + else + { + if ((t->rt = tr->lt) != 0) t->rt->par = t; + tr->lt = t; t->par = tr; + l->rt = tr; tr->par = l; + l = tr; + t = tr->rt; + comp = CMP(key, t->item); + } + } + } + else + { + SplayNode* tl = t->lt; + if (tl == 0) + break; + else + { + comp = CMP(key, tl->item); + if (comp >= 0 || tl->lt == 0) + { + r->lt = t; t->par = r; + r = t; + t = tl; + if (comp <= 0) + break; + } + else + { + if ((t->lt = tl->rt) != 0) t->lt->par = t; + tl->rt = t; t->par = tl; + r->lt = tl; tl->par = r; + r = tl; + t = tl->lt; + comp = CMP(key, t->item); + } + } + } + } + if ((r->lt = t->rt) != 0) r->lt->par = r; + if ((l->rt = t->lt) != 0) l->rt->par = l; + if ((t->lt = dummy->rt) != 0) t->lt->par = t; + if ((t->rt = dummy->lt) != 0) t->rt->par = t; + t->par = 0; + root = t; + return (comp == 0) ? Pix(t) : 0; +} + + +& SplayMap::operator [] ( item) +{ + SplayNode* t = root; + if (t == 0) + { + ++count; + root = new SplayNode(item, def); + return root->cont; + } + int comp = CMP(item, t->item); + if (comp == 0) + return t->cont; + + SplayNode* dummy = (SplayNode*)(&_dummy_null); + SplayNode* l = dummy; + SplayNode* r = dummy; + dummy->rt = dummy->lt = dummy->par = 0; + + while (comp != 0) + { + if (comp > 0) + { + SplayNode* tr = t->rt; + if (tr == 0) + { + ++count; + tr = new SplayNode(item, def); + comp = 0; + } + else + comp = CMP(item, tr->item); + + if (comp <= 0) + { + l->rt = t; t->par = l; + l = t; + t = tr; + } + else + { + SplayNode* trr = tr->rt; + if (trr == 0) + { + ++count; + trr = new SplayNode(item, def); + comp = 0; + } + else + comp = CMP(item, trr->item); + + if ((t->rt = tr->lt) != 0) t->rt->par = t; + tr->lt = t; t->par = tr; + l->rt = tr; tr->par = l; + l = tr; + t = trr; + } + } + else + { + SplayNode* tl = t->lt; + if (tl == 0) + { + ++count; + tl = new SplayNode(item, def); + comp = 0; + } + else + comp = CMP(item, tl->item); + + if (comp >= 0) + { + r->lt = t; t->par = r; + r = t; + t = tl; + } + else + { + SplayNode* tll = tl->lt; + if (tll == 0) + { + ++count; + tll = new SplayNode(item, def); + comp = 0; + } + else + comp = CMP(item, tll->item); + + if ((t->lt = tl->rt) != 0) t->lt->par = t; + tl->rt = t; t->par = tl; + r->lt = tl; tl->par = r; + r = tl; + t = tll; + } + } + } + if ((r->lt = t->rt) != 0) r->lt->par = r; + if ((l->rt = t->lt) != 0) l->rt->par = l; + if ((t->lt = dummy->rt) != 0) t->lt->par = t; + if ((t->rt = dummy->lt) != 0) t->rt->par = t; + t->par = 0; + root = t; + return root->cont; +} + +void SplayMap::del( key) +{ + SplayNode* t = (SplayNode*)(seek(key)); + if (t == 0) return; + + SplayNode* p = t->par; + + --count; + if (t->rt == 0) + { + if (t == root) + { + if ((root = t->lt) != 0) root->par = 0; + } + else if (t == p->lt) + { + if ((p->lt = t->lt) != 0) p->lt->par = p; + } + else + if ((p->rt = t->lt) != 0) p->rt->par = p; + } + else + { + SplayNode* r = t->rt; + SplayNode* l = r->lt; + for(;;) + { + if (l == 0) + { + if (t == root) + { + root = r; + r->par = 0; + } + else if (t == p->lt) + { + p->lt = r; + r->par = p; + } + else + { + p->rt = r; + r->par = p; + } + if ((r->lt = t->lt) != 0) r->lt->par = r; + break; + } + else + { + if ((r->lt = l->rt) != 0) r->lt->par = r; + l->rt = r; r->par = l; + r = l; + l = l->lt; + } + } + } + delete t; +} + + +void SplayMap::_kill(SplayNode* t) +{ + if (t != 0) + { + _kill(t->lt); + _kill(t->rt); + delete t; + } +} + + +SplayNode* SplayMap::_copy(SplayNode* t) +{ + if (t != 0) + { + SplayNode* l = _copy(t->lt); + SplayNode* r = _copy(t->rt); + SplayNode* x = new SplayNode(t->item, t->cont, l, r); + if (l != 0) l->par = x; + if (r != 0) r->par = x; + return x; + } + else + return 0; +} + + +int SplayMap::OK() +{ + int v = 1; + if (root == 0) + v = count == 0; + else + { + int n = 1; + SplayNode* trail = leftmost(); + SplayNode* t = succ(trail); + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/SplayMap.hP b/gnu/lib/libg++/g++-include/gen/SplayMap.hP new file mode 100644 index 0000000000..ced95378ab --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SplayMap.hP @@ -0,0 +1,154 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _SplayMap_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SplayMap_h 1 + +#include "..Map.h" + +#ifndef _SplayNode +#define _SplayNode 1 + +struct SplayNode +{ + SplayNode* lt; + SplayNode* rt; + SplayNode* par; + item; + cont; + SplayNode( h, c, + SplayNode* l=0, + SplayNode* r=0); + ~SplayNode(); +}; + + +inline SplayNode::SplayNode( h, c, + SplayNode* l, + SplayNode* r) + :item(h), cont(c), lt(l), rt(r), par(0) {} + +inline SplayNode::~SplayNode() {} + +typedef SplayNode* SplayNodePtr; + +#endif + +class SplayMap : public Map +{ +protected: + SplayNode* root; + + SplayNode* leftmost(); + SplayNode* rightmost(); + SplayNode* pred(SplayNode* t); + SplayNode* succ(SplayNode* t); + void _kill(SplayNode* t); + SplayNode* _copy(SplayNode* t); + +public: + SplayMap( dflt); + SplayMap(SplayMap& a); + ~SplayMap(); + + & operator [] ( key); + + void del( key); + + Pix first(); + void next(Pix& i); + & key(Pix i); + & contents(Pix i); + + Pix seek( key); + int contains( key); + + void clear(); + + Pix last(); + void prev(Pix& i); + + int OK(); +}; + + +inline SplayMap::~SplayMap() +{ + _kill(root); +} + +inline SplayMap::SplayMap( dflt) :Map(dflt) +{ + root = 0; +} + +inline SplayMap::SplayMap(SplayMap& b) :Map(b.def) +{ + count = b.count; + root = _copy(b.root); +} + +inline Pix SplayMap::first() +{ + return Pix(leftmost()); +} + +inline Pix SplayMap::last() +{ + return Pix(rightmost()); +} + +inline void SplayMap::next(Pix& i) +{ + if (i != 0) i = Pix(succ((SplayNode*)i)); +} + +inline void SplayMap::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((SplayNode*)i)); +} + +inline & SplayMap::key (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SplayNode*)i)->item; +} + +inline & SplayMap::contents (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SplayNode*)i)->cont; +} + +inline void SplayMap::clear() +{ + _kill(root); + count = 0; + root = 0; +} + +inline int SplayMap::contains( key) +{ + return seek(key) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/SplayNode.ccP b/gnu/lib/libg++/g++-include/gen/SplayNode.ccP new file mode 100644 index 0000000000..9dfb1d8c06 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SplayNode.ccP @@ -0,0 +1,21 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1992 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".SplayNode.h" diff --git a/gnu/lib/libg++/g++-include/gen/SplayNode.hP b/gnu/lib/libg++/g++-include/gen/SplayNode.hP new file mode 100644 index 0000000000..a196861fc9 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SplayNode.hP @@ -0,0 +1,44 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988, 1982 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _SplayNode +#define _SplayNode 1 +#ifdef __GNUG__ +#pragma interface +#endif +#include ".defs.h" + +struct SplayNode +{ + SplayNode* lt; + SplayNode* rt; + SplayNode* par; + item; + SplayNode( h, SplayNode* l=0, SplayNode* r=0); + ~SplayNode(); +}; + + +inline SplayNode::SplayNode( h, SplayNode* l, SplayNode* r) +:item(h), lt(l), rt(r), par(0) {} + +inline SplayNode::~SplayNode() {} + +typedef SplayNode* SplayNodePtr; + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/SplayPQ.ccP b/gnu/lib/libg++/g++-include/gen/SplayPQ.ccP new file mode 100644 index 0000000000..0740cd9a93 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SplayPQ.ccP @@ -0,0 +1,523 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".SplayPQ.h" + + +/* + + struct to simulate the special `null' node in the Sleater & Tarjan JACM 1985 + splay tree algorithms + + All routines use a version of their `simple top-down' splay alg. (p 669) + +*/ + +struct _dummySplayNode +{ + SplayNode* lt; + SplayNode* rt; + SplayNode* par; +} _dummy_null; + + +/* + traversal primitives +*/ + + +SplayNode* SplayPQ::leftmost() +{ + SplayNode* t = root; + if (t != 0) while (t->lt != 0) t = t->lt; + return t; +} + +SplayNode* SplayPQ::rightmost() +{ + SplayNode* t = root; + if (t != 0) while (t->rt != 0) t = t->rt; + return t; +} + +SplayNode* SplayPQ::succ(SplayNode* t) +{ + if (t == 0) + return 0; + if (t->rt != 0) + { + t = t->rt; + while (t->lt != 0) t = t->lt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->lt) + return t->par; + else + t = t->par; + } + } +} + +SplayNode* SplayPQ::pred(SplayNode* t) +{ + if (t == 0) + return 0; + else if (t->lt != 0) + { + t = t->lt; + while (t->rt != 0) t = t->rt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->rt) + return t->par; + else + t = t->par; + } + } +} + + +Pix SplayPQ::seek( key) +{ + SplayNode* t = root; + if (t == 0) + return 0; + + int comp = CMP(key, t->item); + if (comp == 0) + return Pix(t); + + SplayNode* dummy = (SplayNode*)(&_dummy_null); + SplayNode* l = dummy; + SplayNode* r = dummy; + dummy->rt = dummy->lt = dummy->par = 0; + + while (comp != 0) + { + if (comp > 0) + { + SplayNode* tr = t->rt; + if (tr == 0) + break; + else + { + comp = CMP(key, tr->item); + if (comp <= 0 || tr->rt == 0) + { + l->rt = t; t->par = l; + l = t; + t = tr; + if (comp >= 0) + break; + } + else + { + if ((t->rt = tr->lt) != 0) t->rt->par = t; + tr->lt = t; t->par = tr; + l->rt = tr; tr->par = l; + l = tr; + t = tr->rt; + comp = CMP(key, t->item); + } + } + } + else + { + SplayNode* tl = t->lt; + if (tl == 0) + break; + else + { + comp = CMP(key, tl->item); + if (comp >= 0 || tl->lt == 0) + { + r->lt = t; t->par = r; + r = t; + t = tl; + if (comp <= 0) + break; + } + else + { + if ((t->lt = tl->rt) != 0) t->lt->par = t; + tl->rt = t; t->par = tl; + r->lt = tl; tl->par = r; + r = tl; + t = tl->lt; + comp = CMP(key, t->item); + } + } + } + } + if ((r->lt = t->rt) != 0) r->lt->par = r; + if ((l->rt = t->lt) != 0) l->rt->par = l; + if ((t->lt = dummy->rt) != 0) t->lt->par = t; + if ((t->rt = dummy->lt) != 0) t->rt->par = t; + t->par = 0; + root = t; + return (comp == 0) ? Pix(t) : 0; +} + + +Pix SplayPQ::enq( item) +{ + ++count; + SplayNode* newnode = new SplayNode(item); + SplayNode* t = root; + if (t == 0) + { + root = newnode; + return Pix(root); + } + + int comp = CMP(item, t->item); + + SplayNode* dummy = (SplayNode*)(&_dummy_null); + SplayNode* l = dummy; + SplayNode* r = dummy; + dummy->rt = dummy->lt = dummy->par = 0; + + int done = 0; + while (!done) + { + if (comp >= 0) + { + SplayNode* tr = t->rt; + if (tr == 0) + { + tr = newnode; + comp = 0; done = 1; + } + else + comp = CMP(item, tr->item); + + if (comp <= 0) + { + l->rt = t; t->par = l; + l = t; + t = tr; + } + else + { + SplayNode* trr = tr->rt; + if (trr == 0) + { + trr = newnode; + comp = 0; done = 1; + } + else + comp = CMP(item, trr->item); + + if ((t->rt = tr->lt) != 0) t->rt->par = t; + tr->lt = t; t->par = tr; + l->rt = tr; tr->par = l; + l = tr; + t = trr; + } + } + else + { + SplayNode* tl = t->lt; + if (tl == 0) + { + tl = newnode; + comp = 0; done = 1; + } + else + comp = CMP(item, tl->item); + + if (comp >= 0) + { + r->lt = t; t->par = r; + r = t; + t = tl; + } + else + { + SplayNode* tll = tl->lt; + if (tll == 0) + { + tll = newnode; + comp = 0; done = 1; + } + else + comp = CMP(item, tll->item); + + if ((t->lt = tl->rt) != 0) t->lt->par = t; + tl->rt = t; t->par = tl; + r->lt = tl; tl->par = r; + r = tl; + t = tll; + } + } + } + if ((r->lt = t->rt) != 0) r->lt->par = r; + if ((l->rt = t->lt) != 0) l->rt->par = l; + if ((t->lt = dummy->rt) != 0) t->lt->par = t; + if ((t->rt = dummy->lt) != 0) t->rt->par = t; + t->par = 0; + root = t; + return Pix(root); +} + + +void SplayPQ::del(Pix pix) +{ + SplayNode* t = (SplayNode*)pix; + if (t == 0) return; + + SplayNode* p = t->par; + + --count; + if (t->rt == 0) + { + if (t == root) + { + if ((root = t->lt) != 0) root->par = 0; + } + else if (t == p->lt) + { + if ((p->lt = t->lt) != 0) p->lt->par = p; + } + else + if ((p->rt = t->lt) != 0) p->rt->par = p; + } + else + { + SplayNode* r = t->rt; + SplayNode* l = r->lt; + for(;;) + { + if (l == 0) + { + if (t == root) + { + root = r; + r->par = 0; + } + else if (t == p->lt) + { + p->lt = r; + r->par = p; + } + else + { + p->rt = r; + r->par = p; + } + if ((r->lt = t->lt) != 0) r->lt->par = r; + break; + } + else + { + if ((r->lt = l->rt) != 0) r->lt->par = r; + l->rt = r; r->par = l; + r = l; + l = l->lt; + } + } + } + delete t; +} + +& SplayPQ::front() +{ + if (root == 0) + error ("min: empty tree\n"); +// else + { + SplayNode* t = root; + SplayNode* l = root->lt; + for(;;) + { + if (l == 0) + { + root = t; + root->par = 0; + return root->item; + } + else + { + if ((t->lt = l->rt) != 0) t->lt->par = t; + l->rt = t; t->par = l; + t = l; + l = l->lt; + } + } + } +} + +void SplayPQ::del_front() +{ + if (root != 0) + { + --count; + SplayNode* t = root; + SplayNode* l = root->lt; + if (l == 0) + { + if ((root = t->rt) != 0) root->par = 0; + delete t; + } + else + { + for(;;) + { + SplayNode* ll = l->lt; + if (ll == 0) + { + if ((t->lt = l->rt) != 0) t->lt->par = t; + delete l; + break; + } + else + { + SplayNode* lll = ll->lt; + if (lll == 0) + { + if ((l->lt = ll->rt) != 0) l->lt->par = l; + delete ll; + break; + } + else + { + t->lt = ll; ll->par = t; + if ((l->lt = ll->rt) != 0) l->lt->par = l; + ll->rt = l; l->par = ll; + t = ll; + l = lll; + } + } + } + } + } +} + + SplayPQ::deq() +{ + if (root == 0) + error("deq: empty tree"); +// else + { + --count; + SplayNode* t = root; + SplayNode* l = root->lt; + if (l == 0) + { + if ((root = t->rt) != 0) root->par = 0; + res = t->item; + delete t; + return res; + } + else + { + for(;;) + { + SplayNode* ll = l->lt; + if (ll == 0) + { + if ((t->lt = l->rt) != 0) t->lt->par = t; + res = l->item; + delete l; + return res; + } + else + { + SplayNode* lll = ll->lt; + if (lll == 0) + { + if ((l->lt = ll->rt) != 0) l->lt->par = l; + res = ll->item; + delete ll; + return res; + } + else + { + t->lt = ll; ll->par = t; + if ((l->lt = ll->rt) != 0) l->lt->par = l; + ll->rt = l; l->par = ll; + t = ll; + l = lll; + } + } + } + } + } +} + + +void SplayPQ::_kill(SplayNode* t) +{ + if (t != 0) + { + _kill(t->lt); + _kill(t->rt); + delete t; + } +} + + +SplayNode* SplayPQ::_copy(SplayNode* t) +{ + if (t != 0) + { + SplayNode* l = _copy(t->lt); + SplayNode* r = _copy(t->rt); + SplayNode* x = new SplayNode(t->item, l, r); + if (l != 0) l->par = x; + if (r != 0) r->par = x; + return x; + } + else + return 0; +} + +int SplayPQ::OK() +{ + int v = 1; + if (root == 0) + v = count == 0; + else + { + int n = 1; + SplayNode* trail = leftmost(); + SplayNode* t = succ(trail); + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/SplayPQ.hP b/gnu/lib/libg++/g++-include/gen/SplayPQ.hP new file mode 100644 index 0000000000..c75c4a0529 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SplayPQ.hP @@ -0,0 +1,123 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _SplayPQ_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SplayPQ_h 1 + +#include ".PQ.h" +#include ".SplayNode.h" + +class SplayPQ : public PQ +{ +protected: + SplayNode* root; + + SplayNode* leftmost(); + SplayNode* rightmost(); + SplayNode* pred(SplayNode* t); + SplayNode* succ(SplayNode* t); + void _kill(SplayNode* t); + SplayNode* _copy(SplayNode* t); + +public: + SplayPQ(); + SplayPQ(SplayPQ& a); + virtual ~SplayPQ(); + + Pix enq( item); + deq(); + + & front(); + void del_front(); + + int contains( item); + + void clear(); + + Pix first(); + Pix last(); + void next(Pix& i); + void prev(Pix& i); + & operator () (Pix i); + void del(Pix i); + Pix seek( item); + + int OK(); // rep invariant +}; + + +inline SplayPQ::~SplayPQ() +{ + _kill(root); +} + +inline SplayPQ::SplayPQ() +{ + root = 0; + count = 0; +} + +inline SplayPQ::SplayPQ(SplayPQ& b) +{ + count = b.count; + root = _copy(b.root); +} + +inline Pix SplayPQ::first() +{ + return Pix(leftmost()); +} + +inline Pix SplayPQ::last() +{ + return Pix(rightmost()); +} + +inline void SplayPQ::next(Pix& i) +{ + if (i != 0) i = Pix(succ((SplayNode*)i)); +} + +inline void SplayPQ::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((SplayNode*)i)); +} + +inline & SplayPQ::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SplayNode*)i)->item; +} + +inline void SplayPQ::clear() +{ + _kill(root); + count = 0; + root = 0; +} + +inline int SplayPQ::contains( key) +{ + return seek(key) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/SplaySet.ccP b/gnu/lib/libg++/g++-include/gen/SplaySet.ccP new file mode 100644 index 0000000000..bba5601c7e --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SplaySet.ccP @@ -0,0 +1,499 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".SplaySet.h" + + +/* + + struct to simulate the special `null' node in the Sleater & Tarjan JACM 1985 + splay tree algorithms + + All routines use a version of their `simple top-down' splay alg. (p 669) + +*/ + +struct _dummySplayNode +{ + SplayNode* lt; + SplayNode* rt; + SplayNode* par; +} _dummy_null; + + +/* + traversal primitives +*/ + + +SplayNode* SplaySet::leftmost() +{ + SplayNode* t = root; + if (t != 0) while (t->lt != 0) t = t->lt; + return t; +} + +SplayNode* SplaySet::rightmost() +{ + SplayNode* t = root; + if (t != 0) while (t->rt != 0) t = t->rt; + return t; +} + +SplayNode* SplaySet::succ(SplayNode* t) +{ + if (t == 0) + return 0; + if (t->rt != 0) + { + t = t->rt; + while (t->lt != 0) t = t->lt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->lt) + return t->par; + else + t = t->par; + } + } +} + +SplayNode* SplaySet::pred(SplayNode* t) +{ + if (t == 0) + return 0; + else if (t->lt != 0) + { + t = t->lt; + while (t->rt != 0) t = t->rt; + return t; + } + else + { + for (;;) + { + if (t->par == 0 || t == t->par->rt) + return t->par; + else + t = t->par; + } + } +} + + +Pix SplaySet::seek( key) +{ + SplayNode* t = root; + if (t == 0) + return 0; + + int comp = CMP(key, t->item); + if (comp == 0) + return Pix(t); + + SplayNode* dummy = (SplayNode*)(&_dummy_null); + SplayNode* l = dummy; + SplayNode* r = dummy; + dummy->rt = dummy->lt = dummy->par = 0; + + while (comp != 0) + { + if (comp > 0) + { + SplayNode* tr = t->rt; + if (tr == 0) + break; + else + { + comp = CMP(key, tr->item); + if (comp <= 0 || tr->rt == 0) + { + l->rt = t; t->par = l; + l = t; + t = tr; + if (comp >= 0) + break; + } + else + { + if ((t->rt = tr->lt) != 0) t->rt->par = t; + tr->lt = t; t->par = tr; + l->rt = tr; tr->par = l; + l = tr; + t = tr->rt; + comp = CMP(key, t->item); + } + } + } + else + { + SplayNode* tl = t->lt; + if (tl == 0) + break; + else + { + comp = CMP(key, tl->item); + if (comp >= 0 || tl->lt == 0) + { + r->lt = t; t->par = r; + r = t; + t = tl; + if (comp <= 0) + break; + } + else + { + if ((t->lt = tl->rt) != 0) t->lt->par = t; + tl->rt = t; t->par = tl; + r->lt = tl; tl->par = r; + r = tl; + t = tl->lt; + comp = CMP(key, t->item); + } + } + } + } + if ((r->lt = t->rt) != 0) r->lt->par = r; + if ((l->rt = t->lt) != 0) l->rt->par = l; + if ((t->lt = dummy->rt) != 0) t->lt->par = t; + if ((t->rt = dummy->lt) != 0) t->rt->par = t; + t->par = 0; + root = t; + return (comp == 0) ? Pix(t) : 0; +} + + + +Pix SplaySet::add( item) +{ + SplayNode* t = root; + if (t == 0) + { + ++count; + root = new SplayNode(item); + return Pix(root); + } + int comp = CMP(item, t->item); + if (comp == 0) + return Pix(t); + + SplayNode* dummy = (SplayNode*)(&_dummy_null); + SplayNode* l = dummy; + SplayNode* r = dummy; + dummy->rt = dummy->lt = dummy->par = 0; + + while (comp != 0) + { + if (comp > 0) + { + SplayNode* tr = t->rt; + if (tr == 0) + { + ++count; + tr = new SplayNode(item); + comp = 0; + } + else + comp = CMP(item, tr->item); + + if (comp <= 0) + { + l->rt = t; t->par = l; + l = t; + t = tr; + } + else + { + SplayNode* trr = tr->rt; + if (trr == 0) + { + ++count; + trr = new SplayNode(item); + comp = 0; + } + else + comp = CMP(item, trr->item); + + if ((t->rt = tr->lt) != 0) t->rt->par = t; + tr->lt = t; t->par = tr; + l->rt = tr; tr->par = l; + l = tr; + t = trr; + } + } + else + { + SplayNode* tl = t->lt; + if (tl == 0) + { + ++count; + tl = new SplayNode(item); + comp = 0; + } + else + comp = CMP(item, tl->item); + + if (comp >= 0) + { + r->lt = t; t->par = r; + r = t; + t = tl; + } + else + { + SplayNode* tll = tl->lt; + if (tll == 0) + { + ++count; + tll = new SplayNode(item); + comp = 0; + } + else + comp = CMP(item, tll->item); + + if ((t->lt = tl->rt) != 0) t->lt->par = t; + tl->rt = t; t->par = tl; + r->lt = tl; tl->par = r; + r = tl; + t = tll; + } + } + } + if ((r->lt = t->rt) != 0) r->lt->par = r; + if ((l->rt = t->lt) != 0) l->rt->par = l; + if ((t->lt = dummy->rt) != 0) t->lt->par = t; + if ((t->rt = dummy->lt) != 0) t->rt->par = t; + t->par = 0; + root = t; + return Pix(root); +} + +void SplaySet::del( key) +{ + SplayNode* t = (SplayNode*)(seek(key)); + if (t == 0) return; + + SplayNode* p = t->par; + + --count; + if (t->rt == 0) + { + if (t == root) + { + if ((root = t->lt) != 0) root->par = 0; + } + else if (t == p->lt) + { + if ((p->lt = t->lt) != 0) p->lt->par = p; + } + else + if ((p->rt = t->lt) != 0) p->rt->par = p; + } + else + { + SplayNode* r = t->rt; + SplayNode* l = r->lt; + for(;;) + { + if (l == 0) + { + if (t == root) + { + root = r; + r->par = 0; + } + else if (t == p->lt) + { + p->lt = r; + r->par = p; + } + else + { + p->rt = r; + r->par = p; + } + if ((r->lt = t->lt) != 0) r->lt->par = r; + break; + } + else + { + if ((r->lt = l->rt) != 0) r->lt->par = r; + l->rt = r; r->par = l; + r = l; + l = l->lt; + } + } + } + delete t; +} + + +void SplaySet::_kill(SplayNode* t) +{ + if (t != 0) + { + _kill(t->lt); + _kill(t->rt); + delete t; + } +} + + +SplayNode* SplaySet::_copy(SplayNode* t) +{ + if (t != 0) + { + SplayNode* l = _copy(t->lt); + SplayNode* r = _copy(t->rt); + SplayNode* x = new SplayNode(t->item, l, r); + if (l != 0) l->par = x; + if (r != 0) r->par = x; + return x; + } + else + return 0; +} + +/* relationals */ + +int SplaySet::operator == (SplaySet& y) +{ + if (count != y.count) + return 0; + else + { + SplayNode* t = leftmost(); + SplayNode* u = y.leftmost(); + for (;;) + { + if (t == 0) + return 1; + else if (!EQ(t->item, u->item)) + return 0; + else + { + t = succ(t); + u = y.succ(u); + } + } + } +} + +int SplaySet::operator <= (SplaySet& y) +{ + if (count > y.count) + return 0; + else + { + SplayNode* t = leftmost(); + SplayNode* u = y.leftmost(); + for (;;) + { + if (t == 0) + return 1; + else if (u == 0) + return 0; + int cmp = CMP(t->item, u->item); + if (cmp == 0) + { + t = succ(t); + u = y.succ(u); + } + else if (cmp < 0) + return 0; + else + u = y.succ(u); + } + } +} + + +void SplaySet::operator |=(SplaySet& y) +{ + if (&y == this) return; + SplayNode* u = y.leftmost(); + while (u != 0) + { + add(u->item); + u = y.succ(u); + } +} + +void SplaySet::operator &= (SplaySet& y) +{ + if (y.count == 0) + clear(); + else if (&y != this && count != 0) + { + SplayNode* t = leftmost(); + while (t != 0) + { + SplayNode* s = succ(t); + if (y.seek(t->item) == 0) del(t->item); + t = s; + } + } +} + + +void SplaySet::operator -=(SplaySet& y) +{ + if (&y == this) + clear(); + else if (y.count != 0) + { + SplayNode* t = leftmost(); + while (t != 0) + { + SplayNode* s = succ(t); + if (y.seek(t->item) != 0) del(t->item); + t = s; + } + } +} + +int SplaySet::OK() +{ + int v = 1; + if (root == 0) + v = count == 0; + else + { + int n = 1; + SplayNode* trail = leftmost(); + SplayNode* t = succ(trail); + while (t != 0) + { + ++n; + v &= CMP(trail->item, t->item) < 0; + trail = t; + t = succ(t); + } + v &= n == count; + } + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/SplaySet.hP b/gnu/lib/libg++/g++-include/gen/SplaySet.hP new file mode 100644 index 0000000000..cf50b97554 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/SplaySet.hP @@ -0,0 +1,133 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _SplaySet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SplaySet_h 1 + +#include ".Set.h" +#include ".SplayNode.h" + +class SplaySet : public Set +{ +protected: + SplayNode* root; + + SplayNode* leftmost(); + SplayNode* rightmost(); + SplayNode* pred(SplayNode* t); + SplayNode* succ(SplayNode* t); + void _kill(SplayNode* t); + SplayNode* _copy(SplayNode* t); + +public: + SplaySet(); + SplaySet(SplaySet& a); + ~SplaySet(); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + Pix seek( item); + + Pix last(); + void prev(Pix& i); + + void operator |= (SplaySet& b); + void operator -= (SplaySet& b); + void operator &= (SplaySet& b); + + int operator == (SplaySet& b); + int operator != (SplaySet& b); + int operator <= (SplaySet& b); + + int OK(); +}; + + +inline SplaySet::~SplaySet() +{ + _kill(root); +} + +inline SplaySet::SplaySet() +{ + root = 0; + count = 0; +} + +inline SplaySet::SplaySet(SplaySet& b) +{ + count = b.count; + root = _copy(b.root); +} + + +inline int SplaySet::operator != (SplaySet& b) +{ + return ! (*this == b); +} + +inline Pix SplaySet::first() +{ + return Pix(leftmost()); +} + +inline Pix SplaySet::last() +{ + return Pix(rightmost()); +} + +inline void SplaySet::next(Pix& i) +{ + if (i != 0) i = Pix(succ((SplayNode*)i)); +} + +inline void SplaySet::prev(Pix& i) +{ + if (i != 0) i = Pix(pred((SplayNode*)i)); +} + +inline & SplaySet::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return ((SplayNode*)i)->item; +} + +inline void SplaySet::clear() +{ + _kill(root); + count = 0; + root = 0; +} + +inline int SplaySet::contains( key) +{ + return seek(key) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/Stack.ccP b/gnu/lib/libg++/g++-include/gen/Stack.ccP new file mode 100644 index 0000000000..efb6b8edbd --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/Stack.ccP @@ -0,0 +1,11 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".Stack.h" + +Stack::~Stack() {} + +void Stack::error(const char* msg) +{ + (*lib_error_handler)("Stack", msg); +} diff --git a/gnu/lib/libg++/g++-include/gen/Stack.hP b/gnu/lib/libg++/g++-include/gen/Stack.hP new file mode 100644 index 0000000000..37acb2fac5 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/Stack.hP @@ -0,0 +1,51 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _Stack_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Stack_h + +#include + +#include ".defs.h" + +class Stack +{ +public: + Stack() { } + virtual ~Stack(); + + virtual void push( item) = 0; + virtual pop() = 0; + virtual & top() = 0; + virtual void del_top() = 0; + + virtual int empty() = 0; + virtual int full() = 0; + virtual int length() = 0; + + virtual void clear() = 0; + + void error(const char*); + virtual int OK() = 0; +}; + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/VHBag.ccP b/gnu/lib/libg++/g++-include/gen/VHBag.ccP new file mode 100644 index 0000000000..81a20eb140 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/VHBag.ccP @@ -0,0 +1,264 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".VHBag.h" + +/* codes for status fields */ + +#define EMPTYCELL 0 +#define VALIDCELL 1 +#define DELETEDCELL 2 + + +VHBag::VHBag(unsigned int sz) +{ + tab = new [size = sz]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; +} + +VHBag::VHBag(VHBag& a) +{ + tab = new [size = a.size]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; + for (Pix p = a.first(); p; a.next(p)) add(a(p)); +} + + +/* + * hashing method: double hash based on high bits of hash fct, + * followed by linear probe. Can't do too much better if table + * sizes not constrained to be prime. +*/ + + +static inline unsigned int doublehashinc(unsigned int h, unsigned int s) +{ + unsigned int dh = ((h / s) % s); + return (dh > 1)? dh : 1; +} + +Pix VHBag::seek( key, Pix p) +{ + * t = (*) p; + if (t == 0 || !EQ(*t, key)) + { + unsigned int hashval = HASH(key); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return 0; + else if (status[h] == VALIDCELL && EQ(key, tab[h])) + return Pix(&tab[h]); + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } + return 0; + } + else + { + int seent = 0; + unsigned int hashval = HASH(key); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return 0; + else if (&tab[h] == t) + seent = 1; + else if (seent && status[h] == VALIDCELL && EQ(key, tab[h])) + return Pix(&tab[h]); + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } + return 0; + } +} + +int VHBag::nof( item) +{ + int n = 0; + unsigned int hashval = HASH(item); + unsigned int h = hashval % size; + unsigned int firsth = size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return n; + else if (h != firsth && status[h] == VALIDCELL && EQ(item, tab[h])) + { + ++n; + if (firsth >= size) + firsth = h; + } + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } + return n; +} + + +Pix VHBag::add( item) +{ + if (HASHTABLE_TOO_CROWDED(count, size)) + resize(); + unsigned int bestspot = size; + unsigned int hashval = HASH(item); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + { + if (bestspot >= size) bestspot = h; + tab[bestspot] = item; + status[bestspot] = VALIDCELL; + ++count; + return Pix(&tab[bestspot]); + } + else if (status[h] == DELETEDCELL) + { + if (bestspot >= size) bestspot = h; + } + + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } + tab[bestspot] = item; + status[bestspot] = VALIDCELL; + ++count; + return Pix(&tab[bestspot]); +} + + +void VHBag::del( key) +{ + unsigned int hashval = HASH(key); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return; + else if (status[h] == VALIDCELL && EQ(key, tab[h])) + { + status[h] = DELETEDCELL; + --count; + return; + } + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } +} + +void VHBag::remove( key) +{ + unsigned int hashval = HASH(key); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return; + else if (status[h] == VALIDCELL && EQ(key, tab[h])) + { + status[h] = DELETEDCELL; + --count; + } + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } +} + +void VHBag::clear() +{ + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; +} + +void VHBag::resize(unsigned int newsize) +{ + if (newsize <= count) + { + newsize = DEFAULT_INITIAL_CAPACITY; + while (HASHTABLE_TOO_CROWDED(count, newsize)) newsize <<= 1; + } + * oldtab = tab; + char* oldstatus = status; + unsigned int oldsize = size; + tab = new [size = newsize]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; + for (i = 0; i < oldsize; ++i) if (oldstatus[i] == VALIDCELL) add(oldtab[i]); + delete [] oldtab; + delete oldstatus; +} + +Pix VHBag::first() +{ + for (unsigned int pos = 0; pos < size; ++pos) + if (status[pos] == VALIDCELL) return Pix(&tab[pos]); + return 0; +} + +void VHBag::next(Pix& i) +{ + if (i == 0) return; + unsigned int pos = ((unsigned)i - (unsigned)tab) / sizeof() + 1; + for (; pos < size; ++pos) + if (status[pos] == VALIDCELL) + { + i = Pix(&tab[pos]); + return; + } + i = 0; +} + + +int VHBag::OK() +{ + int v = tab != 0; + v &= status != 0; + int n = 0; + for (unsigned int i = 0; i < size; ++i) + { + if (status[i] == VALIDCELL) ++n; + else if (status[i] != DELETEDCELL && status[i] != EMPTYCELL) + v = 0; + } + v &= n == count; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/VHBag.hP b/gnu/lib/libg++/g++-include/gen/VHBag.hP new file mode 100644 index 0000000000..72c774a559 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/VHBag.hP @@ -0,0 +1,84 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _VHBag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _VHBag_h 1 + +#include ".Bag.h" + + +class VHBag : public Bag +{ +protected: + * tab; + char* status; + unsigned int size; + +public: + VHBag(unsigned int sz = DEFAULT_INITIAL_CAPACITY); + VHBag(VHBag& a); + ~VHBag(); + + Pix add( item); + void del( item); + void remove(item); + int nof( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + Pix seek( item, Pix from = 0); + + int capacity(); + void resize(unsigned int newsize = 0); + + int OK(); +}; + + +inline VHBag::~VHBag() +{ + delete [] tab; + delete status; +} + + +inline int VHBag::capacity() +{ + return size; +} + +inline int VHBag::contains( key) +{ + return seek(key) != 0; +} + +inline & VHBag::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return *((*)i); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/VHMap.ccP b/gnu/lib/libg++/g++-include/gen/VHMap.ccP new file mode 100644 index 0000000000..d6b60e997a --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/VHMap.ccP @@ -0,0 +1,210 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include "..VHMap.h" + +/* codes for status fields */ + +#define EMPTYCELL 0 +#define VALIDCELL 1 +#define DELETEDCELL 2 + + +VHMap::VHMap( dflt, unsigned int sz) + :Map(dflt) +{ + tab = new [size = sz]; + cont = new [size]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; +} + +VHMap::VHMap(VHMap& a) : Map(a.def) +{ + tab = new [size = a.size]; + cont = new [size]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; + for (Pix p = a.first(); p; a.next(p)) (*this)[a.key(p)] = a.contents(p); +} + + +/* + * hashing method: double hash based on high bits of hash fct, + * followed by linear probe. Can't do too much better if table + * sizes not constrained to be prime. +*/ + + +static inline unsigned int doublehashinc(unsigned int h, unsigned int s) +{ + unsigned int dh = ((h / s) % s); + return (dh > 1)? dh : 1; +} + +Pix VHMap::seek( key) +{ + unsigned int hashval = HASH(key); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return 0; + else if (status[h] == VALIDCELL && EQ(key, tab[h])) + return Pix(&tab[h]); + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } + return 0; +} + + +& VHMap::operator []( item) +{ + if (HASHTABLE_TOO_CROWDED(count, size)) + resize(); + + unsigned int bestspot = size; + unsigned int hashval = HASH(item); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + { + ++count; + if (bestspot >= size) bestspot = h; + tab[bestspot] = item; + status[bestspot] = VALIDCELL; + cont[bestspot] = def; + return cont[bestspot]; + } + else if (status[h] == DELETEDCELL) + { + if (bestspot >= size) bestspot = h; + } + else if (EQ(tab[h],item)) + return cont[h]; + + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } + + ++count; + status[bestspot] = VALIDCELL; + tab[bestspot] = item; + cont[bestspot] = def; + return cont[bestspot]; +} + + +void VHMap::del( key) +{ + unsigned int hashval = HASH(key); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return; + else if (status[h] == VALIDCELL && EQ(key, tab[h])) + { + status[h] = DELETEDCELL; + --count; + return; + } + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } +} + + +void VHMap::clear() +{ + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; +} + +void VHMap::resize(unsigned int newsize) +{ + if (newsize <= count) + { + newsize = DEFAULT_INITIAL_CAPACITY; + while (HASHTABLE_TOO_CROWDED(count, newsize)) newsize <<= 1; + } + * oldtab = tab; + * oldcont = cont; + char* oldstatus = status; + unsigned int oldsize = size; + tab = new [size = newsize]; + cont = new [size]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; + for (i = 0; i < oldsize; ++i) + if (oldstatus[i] == VALIDCELL) + (*this)[oldtab[i]] = oldcont[i]; + delete [] oldtab; + delete [] oldcont; + delete oldstatus; +} + +Pix VHMap::first() +{ + for (unsigned int pos = 0; pos < size; ++pos) + if (status[pos] == VALIDCELL) return Pix(&tab[pos]); + return 0; +} + +void VHMap::next(Pix& i) +{ + if (i == 0) return; + unsigned int pos = ((unsigned)i - (unsigned)tab) / sizeof() + 1; + for (; pos < size; ++pos) + if (status[pos] == VALIDCELL) + { + i = Pix(&tab[pos]); + return; + } + i = 0; +} + + +int VHMap::OK() +{ + int v = tab != 0; + v &= status != 0; + int n = 0; + for (unsigned int i = 0; i < size; ++i) + { + if (status[i] == VALIDCELL) ++n; + else if (status[i] != DELETEDCELL && status[i] != EMPTYCELL) + v = 0; + } + v &= n == count; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/VHMap.hP b/gnu/lib/libg++/g++-include/gen/VHMap.hP new file mode 100644 index 0000000000..ac8fe4d219 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/VHMap.hP @@ -0,0 +1,84 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _VHMap_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _VHMap_h 1 + +#include "..Map.h" + + +class VHMap : public Map +{ +protected: + * tab; + * cont; + char* status; + unsigned int size; + +public: + VHMap( dflt,unsigned int sz=DEFAULT_INITIAL_CAPACITY); + VHMap(VHMap& a); + ~VHMap(); + + & operator [] ( key); + + void del( key); + + Pix first(); + void next(Pix& i); + & key(Pix i); + & contents(Pix i); + + Pix seek( key); + int contains( key); + + void clear(); + void resize(unsigned int newsize = 0); + + int OK(); +}; + +inline VHMap::~VHMap() +{ + delete [] tab; + delete [] cont; + delete [] status; +} + +inline int VHMap::contains( key) +{ + return seek(key) != 0; +} + +inline & VHMap::key(Pix i) +{ + if (i == 0) error("null Pix"); + return *((*)i); +} + +inline & VHMap::contents(Pix i) +{ + if (i == 0) error("null Pix"); + return cont[((unsigned)(i) - (unsigned)(tab)) / sizeof()]; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/VHSet.ccP b/gnu/lib/libg++/g++-include/gen/VHSet.ccP new file mode 100644 index 0000000000..a78b319834 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/VHSet.ccP @@ -0,0 +1,263 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".VHSet.h" + +/* codes for status fields */ + +#define EMPTYCELL 0 +#define VALIDCELL 1 +#define DELETEDCELL 2 + + +VHSet::VHSet(unsigned int sz) +{ + tab = new [size = sz]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; +} + +VHSet::VHSet(VHSet& a) +{ + tab = new [size = a.size]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; + for (Pix p = a.first(); p; a.next(p)) add(a(p)); +} + + +/* + * hashing method: double hash based on high bits of hash fct, + * followed by linear probe. Can't do too much better if table + * sizes not constrained to be prime. +*/ + + +static inline unsigned int doublehashinc(unsigned int h, unsigned int s) +{ + unsigned int dh = ((h / s) % s); + return (dh > 1)? dh : 1; +} + +Pix VHSet::seek( key) +{ + unsigned int hashval = HASH(key); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return 0; + else if (status[h] == VALIDCELL && EQ(key, tab[h])) + return Pix(&tab[h]); + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } + return 0; +} + + +Pix VHSet::add( item) +{ + if (HASHTABLE_TOO_CROWDED(count, size)) + resize(); + + unsigned int bestspot = size; + unsigned int hashval = HASH(item); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + { + if (bestspot >= size) bestspot = h; + tab[bestspot] = item; + status[bestspot] = VALIDCELL; + ++count; + return Pix(&tab[bestspot]); + } + else if (status[h] == DELETEDCELL) + { + if (bestspot >= size) bestspot = h; + } + else if (EQ(tab[h],item)) + return Pix(&tab[h]); + + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } + tab[bestspot] = item; + status[bestspot] = VALIDCELL; + ++count; + return Pix(&tab[bestspot]); + +} + + +void VHSet::del( key) +{ + unsigned int hashval = HASH(key); + unsigned int h = hashval % size; + for (unsigned int i = 0; i <= size; ++i) + { + if (status[h] == EMPTYCELL) + return; + else if (status[h] == VALIDCELL && EQ(key, tab[h])) + { + status[h] = DELETEDCELL; + --count; + return; + } + if (i == 0) + h = (h + doublehashinc(hashval, size)) % size; + else if (++h >= size) + h -= size; + } +} + + +void VHSet::clear() +{ + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; +} + +void VHSet::resize(unsigned int newsize) +{ + if (newsize <= count) + { + newsize = DEFAULT_INITIAL_CAPACITY; + while (HASHTABLE_TOO_CROWDED(count, newsize)) newsize <<= 1; + } + * oldtab = tab; + char* oldstatus = status; + unsigned int oldsize = size; + tab = new [size = newsize]; + status = new char[size]; + for (unsigned int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = 0; + for (i = 0; i < oldsize; ++i) if (oldstatus[i] == VALIDCELL) add(oldtab[i]); + delete [] oldtab; + delete oldstatus; +} + +Pix VHSet::first() +{ + for (unsigned int pos = 0; pos < size; ++pos) + if (status[pos] == VALIDCELL) return Pix(&tab[pos]); + return 0; +} + +void VHSet::next(Pix& i) +{ + if (i == 0) return; + unsigned int pos = ((unsigned)i - (unsigned)tab) / sizeof() + 1; + for (; pos < size; ++pos) + if (status[pos] == VALIDCELL) + { + i = Pix(&tab[pos]); + return; + } + i = 0; +} + +int VHSet:: operator == (VHSet& b) +{ + if (count != b.count) + return 0; + else + { + for (unsigned int i = 0; i < size; ++i) + if (status[i] == VALIDCELL && b.seek(tab[i]) == 0) + return 0; + for (i = 0; i < b.size; ++i) + if (b.status[i] == VALIDCELL && seek(b.tab[i]) == 0) + return 0; + return 1; + } +} + +int VHSet::operator <= (VHSet& b) +{ + if (count > b.count) + return 0; + else + { + for (unsigned int i = 0; i < size; ++i) + if (status[i] == VALIDCELL && b.seek(tab[i]) == 0) + return 0; + return 1; + } +} + +void VHSet::operator |= (VHSet& b) +{ + if (&b == this || b.count == 0) + return; + for (unsigned int i = 0; i < b.size; ++i) + if (b.status[i] == VALIDCELL) add(b.tab[i]); +} + +void VHSet::operator &= (VHSet& b) +{ + if (&b == this || count == 0) + return; + for (unsigned int i = 0; i < size; ++i) + { + if (status[i] == VALIDCELL && b.seek(tab[i]) == 0) + { + status[i] = DELETEDCELL; + --count; + } + } +} + +void VHSet::operator -= (VHSet& b) +{ + for (unsigned int i = 0; i < size; ++i) + { + if (status[i] == VALIDCELL && b.seek(tab[i]) != 0) + { + status[i] = DELETEDCELL; + --count; + } + } +} + +int VHSet::OK() +{ + int v = tab != 0; + v &= status != 0; + int n = 0; + for (unsigned int i = 0; i < size; ++i) + { + if (status[i] == VALIDCELL) ++n; + else if (status[i] != DELETEDCELL && status[i] != EMPTYCELL) + v = 0; + } + v &= n == count; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/VHSet.hP b/gnu/lib/libg++/g++-include/gen/VHSet.hP new file mode 100644 index 0000000000..b7b3a3578c --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/VHSet.hP @@ -0,0 +1,96 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _VHSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _VHSet_h 1 + +#include ".Set.h" + + + +class VHSet : public Set +{ +protected: + * tab; + char* status; + unsigned int size; + +public: + VHSet(unsigned int sz = DEFAULT_INITIAL_CAPACITY); + VHSet(VHSet& a); + ~VHSet(); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + Pix seek( item); + + void operator |= (VHSet& b); + void operator -= (VHSet& b); + void operator &= (VHSet& b); + + int operator == (VHSet& b); + int operator != (VHSet& b); + int operator <= (VHSet& b); + + int capacity(); + void resize(unsigned int newsize = 0); + + int OK(); +}; + + +inline VHSet::~VHSet() +{ + delete [] tab; + delete status; +} + + +inline int VHSet::capacity() +{ + return size; +} + +inline int VHSet::contains( key) +{ + return seek(key) != 0; +} + +inline & VHSet::operator () (Pix i) +{ + if (i == 0) error("null Pix"); + return *((*)i); +} + +inline int VHSet::operator != (VHSet& b) +{ + return ! ((*this) == b); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/VOHSet.ccP b/gnu/lib/libg++/g++-include/gen/VOHSet.ccP new file mode 100644 index 0000000000..c5d4557a4c --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/VOHSet.ccP @@ -0,0 +1,308 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Doug Schmidt + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".VOHSet.h" + + +/* codes for status fields */ +#define EMPTYCELL 0 +#define VALIDCELL 1 +#define DELETEDCELL 2 + + +VOHSet::VOHSet(int sz) +{ +// The size of the hash table is always the smallest power of 2 >= the size +// indicated by the user. This allows several optimizations, including +// the use of actual double hashing and elimination of the mod instruction. + + size = 1; + while (size < sz) size <<= 1; + tab = new [size]; + status = new char[size]; + for (int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = cnt = 0; +} + +VOHSet::VOHSet(VOHSet& a) +{ + tab = new [size = a.size]; + status = new char[size]; + for (int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = cnt = 0; + for (Pix p = a.first(); p; a.next(p)) add(a(p)); +} + +Pix VOHSet::seek( key) +{ +// Uses ordered double hashing to perform a search of the table. +// This greatly speeds up the average-case time for an unsuccessful search. + + unsigned hashval = HASH(key); + + // We can avoid the mod operation since size is a power of 2. + unsigned h = hashval & (size - 1); + + // The increment must be odd, since all odd numbers are relatively + // prime to a power of 2!! + unsigned inc = ((((hashval / size) << 1) + 1) & (size - 1)); + + // There is always at least 1 empty cell, so this loop is guaranteed to halt! + while (status[h] != EMPTYCELL) + { + int cmp = CMP (key, tab[h]); + if (cmp == 0) + { + if (status[h] == VALIDCELL) + return Pix(&tab[h]); + else + return 0; + } + else if (cmp > 0) + return 0; + else + h = ((h + inc) & (size - 1)); + } + return 0; +} + +// This adds an item if it doesn't already exist. By performing the initial +// comparison we assure that the table always contains at least 1 empty +// spot. This speeds up later searching by a constant factor. +// The insertion algorithm uses ordered double hashing. See Standish's +// 1980 ``Data Structure's Techniques'' book for details. + +Pix VOHSet::add( x) +{ + if (size <= cnt+1) + resize(); + + unsigned hashval = HASH(x); + unsigned h = hashval & (size - 1); + + if (status[h] != VALIDCELL) // save some work if possible + { + if (status[h] == EMPTYCELL) + cnt++; + count++; + tab[h] = x; + status[h] = VALIDCELL; + return Pix(&tab[h]); + } + int cmp = CMP(x, tab[h]); + if (cmp == 0) + return Pix(&tab[h]); + + item = x; + Pix mypix = 0; + unsigned inc = ((((hashval / size) << 1) + 1) & (size - 1)); + + for (;;) + { + if (cmp < 0) + { + temp = tab[h]; + tab[h] = item; + item = temp; + if (mypix == 0) mypix = Pix(&tab[h]); + inc = ((((HASH(item) / size) << 1) + 1) & (size - 1)); + h = ((h + inc) & (size - 1)); + if (status[h] != EMPTYCELL) cmp = CMP(item, tab[h]); + } + else + h = ((h + inc) & (size - 1)); + if (status[h] != VALIDCELL) + { + if (status[h] == EMPTYCELL) + cnt++; + count++; + tab[h] = item; + status[h] = VALIDCELL; + return (mypix == 0)? Pix(&tab[h]) : mypix; + } + } +} + + +void VOHSet::del( key) +{ +// This performs a deletion by marking the item's status field. +// Note that we only decrease the count, *not* the cnt, since this +// would cause trouble for subsequent steps in the algorithm. See +// Reingold and Hanson's ``Data Structure's'' book for a justification +// of this approach. + + unsigned hashval = HASH(key); + unsigned h = hashval & (size - 1); + unsigned inc = ((((hashval / size) << 1) + 1) & (size - 1)); + + while (status[h] != EMPTYCELL) + { + int cmp = CMP(key, tab[h]); + if (cmp > 0) + h = ((h + inc) & (size - 1)); + else if (status[h] == VALIDCELL && cmp == 0) + { + status[h] = DELETEDCELL; + count--; + return; + } + else + return; + } +} + +void VOHSet::clear() +{ + for (int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = cnt = 0; +} + +void VOHSet::resize(int newsize) +{ + if (newsize <= count) + newsize = count; + int s = 1; + while (s <= newsize) s <<= 1; + newsize = s; + + * oldtab = tab; + char* oldstatus = status; + int oldsize = size; + tab = new [size = newsize]; + status = new char[size]; + for (int i = 0; i < size; ++i) status[i] = EMPTYCELL; + count = cnt = 0; + + for (i = 0; i < oldsize; ++i) if (oldstatus[i] == VALIDCELL) add(oldtab[i]); + delete [] oldtab; + delete oldstatus; +} + +Pix VOHSet::first() +{ + for (int pos = 0; pos < size; ++pos) + if (status[pos] == VALIDCELL) return Pix(&tab[pos]); + return 0; +} + +void VOHSet::next(Pix& i) +{ + if (i == 0) return; + int pos = ((unsigned)i - (unsigned)tab) / sizeof() + 1; + for (; pos < size; ++pos) + if (status[pos] == VALIDCELL) + { + i = Pix(&tab[pos]); + return; + } + i = 0; +} + + +int VOHSet:: operator == (VOHSet& b) +{ + if (count != b.count) + return 0; + else + { + for (int i = 0; i < size; ++i) + if (status[i] == VALIDCELL && b.seek(tab[i]) == 0) + return 0; + for (i = 0; i < b.size; ++i) + if (b.status[i] == VALIDCELL && seek(b.tab[i]) == 0) + return 0; + return 1; + } +} + +int VOHSet:: operator != (VOHSet& b) +{ + return !(*this == b); +} + +int VOHSet::operator <= (VOHSet& b) +{ + if (count > b.count) + return 0; + else + { + for (int i = 0; i < size; ++i) + if (status[i] == VALIDCELL && b.seek(tab[i]) == 0) + return 0; + return 1; + } +} + +void VOHSet::operator |= (VOHSet& b) +{ + if (&b == this || b.count == 0) + return; + for (int i = 0; i < b.size; ++i) + if (b.status[i] == VALIDCELL) add(b.tab[i]); +} + +void VOHSet::operator &= (VOHSet& b) +{ + if (&b == this || count == 0) + return; + for (int i = 0; i < size; ++i) + { + if (status[i] == VALIDCELL && b.seek(tab[i]) == 0) + { + status[i] = DELETEDCELL; + --count; + } + } +} + +void VOHSet::operator -= (VOHSet& b) +{ + for (int i = 0; i < size; ++i) + { + if (status[i] == VALIDCELL && b.seek(tab[i]) != 0) + { + status[i] = DELETEDCELL; + --count; + } + } +} + +int VOHSet::OK() +{ + int v = tab != 0; + v &= status != 0; + int n = 0; + for (int i = 0; i < size; ++i) + { + if (status[i] == VALIDCELL) ++n; + else if (status[i] != DELETEDCELL && status[i] != EMPTYCELL) + v = 0; + } + v &= n == count; + if (!v) error("invariant failure"); + return v; +} + + + diff --git a/gnu/lib/libg++/g++-include/gen/VOHSet.hP b/gnu/lib/libg++/g++-include/gen/VOHSet.hP new file mode 100644 index 0000000000..94decaad12 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/VOHSet.hP @@ -0,0 +1,88 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Doug Schmidt + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _VOHSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _VOHSet_h 1 + +#include ".Set.h" + + + +class VOHSet : public Set +{ + * tab; + char* status; + int size; + int cnt; // keeps track of VALIDCELLs and DELETEDCELLs + +public: + VOHSet(int sz = DEFAULT_INITIAL_CAPACITY); + VOHSet(VOHSet&); + ~VOHSet(); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + Pix seek( item); + + void operator |= (VOHSet& b); + void operator -= (VOHSet& b); + void operator &= (VOHSet& b); + + int operator == (VOHSet& b); + int operator != (VOHSet& b); + int operator <= (VOHSet& b); + + int capacity(); + void resize(int newsize = 0); + + int OK(); +}; + + +inline VOHSet::~VOHSet() +{ + delete [] tab; + delete status; +} + + +inline int VOHSet::contains( key) +{ + return seek(key) != 0; +} + + +inline & VOHSet::operator () (Pix p) +{ + if (p == 0) error("null Pix"); + return *((*)p); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/VQueue.ccP b/gnu/lib/libg++/g++-include/gen/VQueue.ccP new file mode 100644 index 0000000000..1181b3f50d --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/VQueue.ccP @@ -0,0 +1,83 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".VQueue.h" + +VQueue::VQueue(VQueue& b) +:size(b.size), cnt(b.cnt), inp(b.inp), outp(b.outp), s(new [b.size]) +{ + int j = outp; + for (int i = 0; i < cnt; ++i) + { + s[j] = b.s[j]; + if (++j == size) j = 0; + } +} + +void VQueue::operator = (VQueue& b) +{ + if (&b == this) return; + if (size != b.size) + { + delete [] s; + s = new [b.size]; + size = b.size; + } + inp = b.inp; outp = b.outp; cnt = b.cnt; + int j = outp; + for (int i = 0; i < cnt; ++i) + { + s[j] = b.s[j]; + if (++j == size) j = 0; + } +} + + +void VQueue::resize(int newsz) +{ + if (newsz < cnt) + error("resize: new size too small"); + * news = new [newsz]; + int j = outp; + for (int i = 0; i < cnt; ++i) + { + news[i] = s[j]; + if (++j == size) j = 0; + } + inp = j; + outp = 0; + delete [] s; + s = news; + size = newsz; +} + +int VQueue::OK() +{ + int v = s != 0; // have space + v &= size >= 0; // a legal size + v &= inp >= 0 && inp <= size; // pointers with bounds + v &= outp >= 0 && outp <= size; + int c = (size + inp - outp) % size; + v &= cnt == size || cnt == c; // correct count + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/VQueue.hP b/gnu/lib/libg++/g++-include/gen/VQueue.hP new file mode 100644 index 0000000000..cce2c85d24 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/VQueue.hP @@ -0,0 +1,130 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _VQueue_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _VQueue_h 1 + +#include ".Queue.h" + +class VQueue : public Queue +{ +protected: + int size; + int cnt; + int inp; + int outp; + * s; + +public: + + VQueue(int sz = DEFAULT_INITIAL_CAPACITY); + VQueue(VQueue&); + ~VQueue(); + + void operator = (VQueue&); + + void enq( item); + deq(); + & front(); + void del_front(); + + int length(); + int empty(); + int full(); + + int capacity(); + void resize(int sz); + void clear(); + + int OK(); +}; + + +inline VQueue::VQueue(int sz) +{ + s = new [size = sz]; + cnt = inp = outp = 0; +} + +inline VQueue::~VQueue() +{ + delete [] s; +} + +inline void VQueue::clear() +{ + inp = outp = 0; + cnt = 0; +} + +inline int VQueue::empty() +{ + return cnt <= 0; +} + +inline int VQueue::capacity() +{ + return size; +} + +inline int VQueue::full() +{ + return cnt >= size; +} + +inline int VQueue::length() +{ + return cnt; +} + +inline void VQueue::enq( item) +{ + if (cnt >= size) error("enq to full Queue."); + ++cnt; + s[inp] = item; + if (++inp == size) inp = 0; +} + +inline VQueue::deq() +{ + if (cnt <= 0) error("deq from empty Queue."); + --cnt; + int i = outp; + if (++outp == size) outp = 0; + return s[i]; +} + + +inline void VQueue::del_front() +{ + if (cnt <= 0) error("delete from empty Queue."); + --cnt; + if (++outp == size) outp = 0; +} + +inline & VQueue::front() +{ + if (empty()) error("top from empty Queue."); + return s[outp]; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/VStack.ccP b/gnu/lib/libg++/g++-include/gen/VStack.ccP new file mode 100644 index 0000000000..5203d51341 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/VStack.ccP @@ -0,0 +1,66 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include ".VStack.h" + +// error handling + + +VStack::VStack(VStack& b) +:size(b.size), ptr(b.ptr), s(new [b.size]) +{ + for (int i = 0; i < ptr; ++i) s[i] = b.s[i]; +} + +void VStack::operator = (VStack& b) +{ + if (&b == this) return; + if (size < b.ptr) + { + delete [] s; + s = new [b.size]; + size = b.size; + } + ptr = b.ptr; + for (int i = 0; i < ptr; ++i) s[i] = b.s[i]; +} + + +void VStack::resize(int newsz) +{ + if (newsz < ptr) error("resize: new size too small"); + * news = new [newsz]; + for (int i = 0; i < ptr; ++i) news[i] = s[i]; + delete [] s; + s = news; + size = newsz; +} + +int VStack::OK() +{ + int v = s != 0; // have space + v &= size >= 0; // a legal size + v &= ptr <= size; // ptr within bounds + v &= ptr >= 0; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/VStack.hP b/gnu/lib/libg++/g++-include/gen/VStack.hP new file mode 100644 index 0000000000..c8190bf064 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/VStack.hP @@ -0,0 +1,120 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _VStack_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _VStack_h 1 + +#include ".Stack.h" + +class VStack : public Stack +{ +protected: + int size; + int ptr; + * s; + +public: + + VStack(int sz = DEFAULT_INITIAL_CAPACITY); + VStack(VStack&); + ~VStack(); + + void operator = (VStack&); + void push( item); + pop(); + & top(); + void del_top(); + + int length(); + int empty(); + int full(); + void clear(); + + void resize(int sz); + int capacity(); + + int OK(); +}; + + +inline VStack::VStack(int sz) +{ + s = new [size = sz]; + ptr = 0; +} + +inline VStack::~VStack() +{ + delete [] s; +} + +inline void VStack::clear() +{ + ptr = 0; +} + +inline int VStack::capacity() +{ + return size; +} + +inline int VStack::empty() +{ + return ptr == 0; +} + +inline int VStack::full() +{ + return ptr == size; +} + +inline int VStack::length() +{ + return ptr; +} + +inline void VStack::push( item) +{ + if (full()) error("push to full stack."); + s[ptr++] = item; +} + +inline VStack::pop() +{ + if (empty()) error("pop from empty stack."); + return s[--ptr]; +} + + +inline void VStack::del_top() +{ + if (empty()) error("del_top from empty stack."); + --ptr; +} + +inline & VStack::top() +{ + if (empty()) error("top from empty stack."); + return s[ptr-1]; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/Vec.ccP b/gnu/lib/libg++/g++-include/gen/Vec.ccP new file mode 100644 index 0000000000..c9cbfb2109 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/Vec.ccP @@ -0,0 +1,470 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include +#include +#include ".Vec.h" + +// error handling + + +void default_Vec_error_handler(const char* msg) +{ + cerr << "Fatal Vec error. " << msg << "\n"; + exit(1); +} + +one_arg_error_handler_t Vec_error_handler = default_Vec_error_handler; + +one_arg_error_handler_t set_Vec_error_handler(one_arg_error_handler_t f) +{ + one_arg_error_handler_t old = Vec_error_handler; + Vec_error_handler = f; + return old; +} + +void Vec::error(const char* msg) +{ + (*Vec_error_handler)(msg); +} + +void Vec::range_error() +{ + (*Vec_error_handler)("Index out of range."); +} + +Vec::Vec(Vec& v) +{ + s = new [len = v.len]; + * top = &(s[len]); + * t = s; + * u = v.s; + while (t < top) *t++ = *u++; +} + +Vec::Vec(int l, fill_value) +{ + s = new [len = l]; + * top = &(s[len]); + * t = s; + while (t < top) *t++ = fill_value; +} + + +Vec& Vec::operator = (Vec& v) +{ + if (this != &v) + { + delete [] s; + s = new [len = v.len]; + * top = &(s[len]); + * t = s; + * u = v.s; + while (t < top) *t++ = *u++; + } + return *this; +} + +void Vec::apply(Procedure f) +{ + * top = &(s[len]); + * t = s; + while (t < top) (*f)(*t++); +} + +// can't just realloc since there may be need for constructors/destructors +void Vec::resize(int newl) +{ + * news = new [newl]; + * p = news; + int minl = (len < newl)? len : newl; + * top = &(s[minl]); + * t = s; + while (t < top) *p++ = *t++; + delete [] s; + s = news; + len = newl; +} + +Vec concat(Vec & a, Vec & b) +{ + int newl = a.len + b.len; + * news = new [newl]; + * p = news; + * top = &(a.s[a.len]); + * t = a.s; + while (t < top) *p++ = *t++; + top = &(b.s[b.len]); + t = b.s; + while (t < top) *p++ = *t++; + return Vec(newl, news); +} + + +Vec combine(Combiner f, Vec& a, Vec& b) +{ + int newl = (a.len < b.len)? a.len : b.len; + * news = new [newl]; + * p = news; + * top = &(a.s[newl]); + * t = a.s; + * u = b.s; + while (t < top) *p++ = (*f)(*t++, *u++); + return Vec(newl, news); +} + + Vec::reduce(Combiner f, base) +{ + r = base; + * top = &(s[len]); + * t = s; + while (t < top) r = (*f)(r, *t++); + return r; +} + +Vec reverse(Vec& a) +{ + * news = new [a.len]; + if (a.len != 0) + { + * lo = news; + * hi = &(news[a.len - 1]); + while (lo < hi) + { + tmp = *lo; + *lo++ = *hi; + *hi-- = tmp; + } + } + return Vec(a.len, news); +} + +void Vec::reverse() +{ + if (len != 0) + { + * lo = s; + * hi = &(s[len - 1]); + while (lo < hi) + { + tmp = *lo; + *lo++ = *hi; + *hi-- = tmp; + } + } +} + +int Vec::index( targ) +{ + for (int i = 0; i < len; ++i) if (EQ(targ, s[i])) return i; + return -1; +} + +Vec map(Mapper f, Vec& a) +{ + * news = new [a.len]; + * p = news; + * top = &(a.s[a.len]); + * t = a.s; + while(t < top) *p++ = (*f)(*t++); + return Vec(a.len, news); +} + +int operator == (Vec& a, Vec& b) +{ + if (a.len != b.len) + return 0; + * top = &(a.s[a.len]); + * t = a.s; + * u = b.s; + while (t < top) if (!(EQ(*t++, *u++))) return 0; + return 1; +} + +void Vec::fill( val, int from, int n) +{ + int to; + if (n < 0) + to = len - 1; + else + to = from + n - 1; + if ((unsigned)from > (unsigned)to) + range_error(); + * t = &(s[from]); + * top = &(s[to]); + while (t <= top) *t++ = val; +} + +Vec Vec::at(int from, int n) +{ + int to; + if (n < 0) + { + n = len - from; + to = len - 1; + } + else + to = from + n - 1; + if ((unsigned)from > (unsigned)to) + range_error(); + * news = new [n]; + * p = news; + * t = &(s[from]); + * top = &(s[to]); + while (t <= top) *p++ = *t++; + return Vec(n, news); +} + +Vec merge(Vec & a, Vec & b, Comparator f) +{ + int newl = a.len + b.len; + * news = new [newl]; + * p = news; + * topa = &(a.s[a.len]); + * as = a.s; + * topb = &(b.s[b.len]); + * bs = b.s; + + for (;;) + { + if (as >= topa) + { + while (bs < topb) *p++ = *bs++; + break; + } + else if (bs >= topb) + { + while (as < topa) *p++ = *as++; + break; + } + else if ((*f)(*as, *bs) <= 0) + *p++ = *as++; + else + *p++ = *bs++; + } + return Vec(newl, news); +} + +static int gsort(*, int, Comparator); + +void Vec::sort (Comparator compar) +{ + gsort(s, len, compar); +} + + +// An adaptation of Schmidt's new quicksort + +static inline void SWAP(* A, * B) +{ + tmp = *A; *A = *B; *B = tmp; +} + +/* This should be replaced by a standard ANSI macro. */ +#define BYTES_PER_WORD 8 +#define BYTES_PER_LONG 4 + +/* The next 4 #defines implement a very fast in-line stack abstraction. */ + +#define STACK_SIZE (BYTES_PER_WORD * BYTES_PER_LONG) +#define PUSH(LOW,HIGH) do {top->lo = LOW;top++->hi = HIGH;} while (0) +#define POP(LOW,HIGH) do {LOW = (--top)->lo;HIGH = top->hi;} while (0) +#define STACK_NOT_EMPTY (stack < top) + +/* Discontinue quicksort algorithm when partition gets below this size. + This particular magic number was chosen to work best on a Sun 4/260. */ +#define MAX_THRESH 4 + + +/* Order size using quicksort. This implementation incorporates + four optimizations discussed in Sedgewick: + + 1. Non-recursive, using an explicit stack of pointer that + store the next array partition to sort. To save time, this + maximum amount of space required to store an array of + MAX_INT is allocated on the stack. Assuming a 32-bit integer, + this needs only 32 * sizeof (stack_node) == 136 bits. Pretty + cheap, actually. + + 2. Chose the pivot element using a median-of-three decision tree. + This reduces the probability of selecting a bad pivot value and + eliminates certain extraneous comparisons. + + 3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving + insertion sort to order the MAX_THRESH items within each partition. + This is a big win, since insertion sort is faster for small, mostly + sorted array segements. + + 4. The larger of the two sub-partitions is always pushed onto the + stack first, with the algorithm then concentrating on the + smaller partition. This *guarantees* no more than log (n) + stack size is needed! */ + +static int gsort ( *base_ptr, int total_elems, Comparator cmp) +{ +/* Stack node declarations used to store unfulfilled partition obligations. */ + struct stack_node { *lo; *hi; }; + pivot_buffer; + int max_thresh = MAX_THRESH; + + if (total_elems > MAX_THRESH) + { + *lo = base_ptr; + *hi = lo + (total_elems - 1); + *left_ptr; + *right_ptr; + stack_node stack[STACK_SIZE]; /* Largest size needed for 32-bit int!!! */ + stack_node *top = stack + 1; + + while (STACK_NOT_EMPTY) + { + { + *pivot = &pivot_buffer; + { + /* Select median value from among LO, MID, and HI. Rearrange + LO and HI so the three values are sorted. This lowers the + probability of picking a pathological pivot value and + skips a comparison for both the LEFT_PTR and RIGHT_PTR. */ + + *mid = lo + ((hi - lo) >> 1); + + if ((*cmp) (*mid, *lo) < 0) + SWAP (mid, lo); + if ((*cmp) (*hi, *mid) < 0) + { + SWAP (mid, hi); + if ((*cmp) (*mid, *lo) < 0) + SWAP (mid, lo); + } + *pivot = *mid; + pivot = &pivot_buffer; + } + left_ptr = lo + 1; + right_ptr = hi - 1; + + /* Here's the famous ``collapse the walls'' section of quicksort. + Gotta like those tight inner loops! They are the main reason + that this algorithm runs much faster than others. */ + do + { + while ((*cmp) (*left_ptr, *pivot) < 0) + left_ptr += 1; + + while ((*cmp) (*pivot, *right_ptr) < 0) + right_ptr -= 1; + + if (left_ptr < right_ptr) + { + SWAP (left_ptr, right_ptr); + left_ptr += 1; + right_ptr -= 1; + } + else if (left_ptr == right_ptr) + { + left_ptr += 1; + right_ptr -= 1; + break; + } + } + while (left_ptr <= right_ptr); + + } + + /* Set up pointers for next iteration. First determine whether + left and right partitions are below the threshold size. If so, + ignore one or both. Otherwise, push the larger partition's + bounds on the stack and continue sorting the smaller one. */ + + if ((right_ptr - lo) <= max_thresh) + { + if ((hi - left_ptr) <= max_thresh) /* Ignore both small partitions. */ + POP (lo, hi); + else /* Ignore small left partition. */ + lo = left_ptr; + } + else if ((hi - left_ptr) <= max_thresh) /* Ignore small right partition. */ + hi = right_ptr; + else if ((right_ptr - lo) > (hi - left_ptr)) /* Push larger left partition indices. */ + { + PUSH (lo, right_ptr); + lo = left_ptr; + } + else /* Push larger right partition indices. */ + { + PUSH (left_ptr, hi); + hi = right_ptr; + } + } + } + + /* Once the BASE_PTR array is partially sorted by quicksort the rest + is completely sorted using insertion sort, since this is efficient + for partitions below MAX_THRESH size. BASE_PTR points to the beginning + of the array to sort, and END_PTR points at the very last element in + the array (*not* one beyond it!). */ + + + { + *end_ptr = base_ptr + 1 * (total_elems - 1); + *run_ptr; + *tmp_ptr = base_ptr; + *thresh = (end_ptr < (base_ptr + max_thresh))? + end_ptr : (base_ptr + max_thresh); + + /* Find smallest element in first threshold and place it at the + array's beginning. This is the smallest array element, + and the operation speeds up insertion sort's inner loop. */ + + for (run_ptr = tmp_ptr + 1; run_ptr <= thresh; run_ptr += 1) + if ((*cmp) (*run_ptr, *tmp_ptr) < 0) + tmp_ptr = run_ptr; + + if (tmp_ptr != base_ptr) + SWAP (tmp_ptr, base_ptr); + + /* Insertion sort, running from left-hand-side up to `right-hand-side.' + Pretty much straight out of the original GNU qsort routine. */ + + for (run_ptr = base_ptr + 1; (tmp_ptr = run_ptr += 1) <= end_ptr; ) + { + + while ((*cmp) (*run_ptr, *(tmp_ptr -= 1)) < 0) + ; + + if ((tmp_ptr += 1) != run_ptr) + { + *trav; + + for (trav = run_ptr + 1; --trav >= run_ptr;) + { + c = *trav; + *hi, *lo; + + for (hi = lo = trav; (lo -= 1) >= tmp_ptr; hi = lo) + *hi = *lo; + *hi = c; + } + } + + } + } + return 1; +} diff --git a/gnu/lib/libg++/g++-include/gen/Vec.hP b/gnu/lib/libg++/g++-include/gen/Vec.hP new file mode 100644 index 0000000000..97ff3f5fef --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/Vec.hP @@ -0,0 +1,135 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _Vec_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Vec_h 1 + +#include +#include ".defs.h" + +#ifndef __typedefs +#define __typedefs 1 +typedef void (*Procedure)(); +typedef (*Mapper)(); +typedef (*Combiner)(, ); +typedef int (*Predicate)(); +typedef int (*Comparator)(, ); +#endif + + +class Vec +{ +protected: + int len; + *s; + + Vec(int l, * d); +public: + Vec (); + Vec (int l); + Vec (int l, fill_value); + Vec (Vec&); + ~Vec (); + + Vec & operator = (Vec & a); + Vec at(int from = 0, int n = -1); + + int capacity(); + void resize(int newlen); + + & operator [] (int n); + & elem(int n); + + friend Vec concat(Vec & a, Vec & b); + friend Vec map(Mapper f, Vec & a); + friend Vec merge(Vec & a, Vec & b, Comparator f); + friend Vec combine(Combiner f, Vec & a, Vec & b); + friend Vec reverse(Vec & a); + + void reverse(); + void sort(Comparator f); + void fill( val, int from = 0, int n = -1); + + void apply(Procedure f); + reduce(Combiner f, base); + int index( targ); + + friend int operator == (Vec& a, Vec& b); + friend int operator != (Vec& a, Vec& b); + + void error(const char* msg); + void range_error(); +}; + +extern void default_Vec_error_handler(const char*); +extern one_arg_error_handler_t Vec_error_handler; + +extern one_arg_error_handler_t + set_Vec_error_handler(one_arg_error_handler_t f); + + +inline Vec::Vec() +{ + len = 0; s = 0; +} + +inline Vec::Vec(int l) +{ + s = new [len = l]; +} + + +inline Vec::Vec(int l, * d) :len(l), s(d) {} + + +inline Vec::~Vec() +{ + delete [] s; +} + + +inline & Vec::operator [] (int n) +{ + if ((unsigned)n >= (unsigned)len) + range_error(); + return s[n]; +} + +inline & Vec::elem(int n) +{ + return s[n]; +} + + +inline int Vec::capacity() +{ + return len; +} + + + +inline int operator != (Vec& a, Vec& b) +{ + return !(a == b); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/XPBag.ccP b/gnu/lib/libg++/g++-include/gen/XPBag.ccP new file mode 100644 index 0000000000..76dc35cf39 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/XPBag.ccP @@ -0,0 +1,72 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".XPBag.h" + +int XPBag::OK() +{ + int v = p.OK(); + v &= count == p.length(); + if (!v) error("invariant failure"); + return v; +} + +Pix XPBag::seek( item, Pix i) +{ + if (i == 0) i = p.first(); else next(i); + for (; i != 0; p.next(i)) if (EQ(p(i), item)) return i; + return 0; +} + +int XPBag::nof( item) +{ + int n = 0; + for (int i = p.low(); i < p.fence(); p.next(i)) if (EQ(p[i], item)) ++n; + return n; +} + +void XPBag::del( item) +{ + for (int i = p.low(); i < p.fence(); p.next(i)) + { + if (EQ(p[i], item)) + { + --count; + p[i] = p.low_element(); + p.del_low(); + return; + } + } +} + +void XPBag::remove( item) +{ + for (int i = p.low(); i < p.fence(); p.next(i)) + { + if (EQ(p[i], item)) + { + --count; + p[i] = p.low_element(); + p.del_low(); + } + } +} + diff --git a/gnu/lib/libg++/g++-include/gen/XPBag.hP b/gnu/lib/libg++/g++-include/gen/XPBag.hP new file mode 100644 index 0000000000..296a59082d --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/XPBag.hP @@ -0,0 +1,98 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _XPBag_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _XPBag_h 1 + +#include ".Bag.h" +#include ".XPlex.h" + +class XPBag : public Bag +{ +protected: + XPlex p; + +public: + XPBag(int chunksize = DEFAULT_INITIAL_CAPACITY); + XPBag(const XPBag&); + + Pix add( item); + void del( item); +#undef remove + void remove(item); + int nof( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item, Pix from = 0); + + int OK(); +}; + + +inline XPBag::XPBag(int chunksize) + : p(chunksize) { count = 0; } + +inline XPBag::XPBag(const XPBag& s) : p(s.p) { count = s.count; } + +inline Pix XPBag::first() +{ + return p.first(); +} + +inline void XPBag::next(Pix & idx) +{ + p.next(idx); +} + +inline & XPBag::operator ()(Pix idx) +{ + return p(idx); +} + +inline void XPBag::clear() +{ + count = 0; p.clear(); +} + +inline int XPBag::owns (Pix idx) +{ + return p.owns(idx); +} + +inline Pix XPBag::add( item) +{ + ++count; + return p.index_to_Pix(p.add_high(item)); +} + +inline int XPBag::contains( item) +{ + return seek(item) != 0; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/XPDeque.ccP b/gnu/lib/libg++/g++-include/gen/XPDeque.ccP new file mode 100644 index 0000000000..6b363d9bdc --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/XPDeque.ccP @@ -0,0 +1,4 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".XPDeque.h" diff --git a/gnu/lib/libg++/g++-include/gen/XPDeque.hP b/gnu/lib/libg++/g++-include/gen/XPDeque.hP new file mode 100644 index 0000000000..b8e7c8268f --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/XPDeque.hP @@ -0,0 +1,133 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _XPDeque_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _XPDeque_h + +#include ".XPlex.h" +#include ".Deque.h" + +class XPDeque : public Deque +{ + XPlex p; + +public: + XPDeque(int chunksize = DEFAULT_INITIAL_CAPACITY); + XPDeque(const XPDeque& d); + ~XPDeque(); + + void operator = (const XPDeque&); + + void push( item); // insert at front + void enq( item); // insert at rear + + & front(); + & rear(); + + deq(); + void del_front(); + void del_rear(); + + void clear(); + int empty(); + int full(); + int length(); + + int OK(); +}; + +inline XPDeque::XPDeque(int chunksize) + : p(chunksize) {} +inline XPDeque::XPDeque(const XPDeque& d) : p(d.p) {} + +inline XPDeque::~XPDeque() {} + +inline void XPDeque::push(item) +{ + p.add_low(item); +} + +inline void XPDeque::enq(item) +{ + p.add_high(item); +} + +inline XPDeque::deq() +{ + res = p.low_element(); + p.del_low(); + return res; +} + +inline & XPDeque::front() +{ + return p.low_element(); +} + +inline & XPDeque::rear() +{ + return p.high_element(); +} + +inline void XPDeque::del_front() +{ + p.del_low(); +} + +inline void XPDeque::del_rear() +{ + p.del_high(); +} + +inline void XPDeque::operator =(const XPDeque& s) +{ + p.operator = (s.p); +} + + +inline int XPDeque::empty() +{ + return p.empty(); +} + +inline int XPDeque::full() +{ + return p.full(); +} + +inline int XPDeque::length() +{ + return p.length(); +} + +inline int XPDeque::OK() +{ + return p.OK(); +} + +inline void XPDeque::clear() +{ + p.clear(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/XPPQ.ccP b/gnu/lib/libg++/g++-include/gen/XPPQ.ccP new file mode 100644 index 0000000000..41515a39bb --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/XPPQ.ccP @@ -0,0 +1,143 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".XPPQ.h" + +int XPPQ::OK() +{ + int v = p.OK(); + v &= p.low() == 1; + v &= count == p.length(); + if (!v) error("invariant failure"); + return v; +} + +Pix XPPQ::seek( item) +{ + for (int i = p.low(); i < p.fence(); p.next(i)) + if (EQ(p[i],item)) return p.index_to_Pix(i); + return 0; +} + +// standard 2-ary heap ops +// pointers are used a lot to avoid thrashing across chunks with plexes + +Pix XPPQ::enq( item) +{ + p.add_high(item); + * pk = &(p.high_element()); + int par = ++count >> 1; + while (par != 0) + { + * ppar = &(p[par]); + if (!(LE(*ppar, item))) + { + *pk = *ppar; + pk = ppar; + par >>= 1; + } + else + break; + } + *pk = item; + return Pix(pk); +} + +void XPPQ::del_front() +{ + if (count == 0) error("empty PQ"); + --count; + * pk = &(p.low_element()); + * ph = &(p.high_element()); + int child = 2; + while (child <= count) + { + * pchild = &(p[child]); + if (child < count) + { + * prchild = &(p[child+1]); + if (!(LE(*pchild, *prchild))) + { + pchild = prchild; + ++child; + } + } + if (!(LE(*ph, *pchild))) + { + *pk = *pchild; + pk = pchild; + child <<= 1; + } + else + break; + } + *pk = *ph; + p.del_high(); +} + + +void XPPQ::del(Pix i) +{ + if (i == 0) error("null Pix"); + --count; + int k = p.Pix_to_index(i); + * pk = &(p[k]); + * ph = &(p.high_element()); + int child = k << 1; + while (child <= count) + { + * pchild = &(p[child]); + if (child < count) + { + * prchild = &(p[child+1]); + if (!(LE(*pchild, *prchild))) + { + pchild = prchild; + ++child; + } + } + if (!(LE(*ph, *pchild))) + { + *pk = *pchild; + pk = pchild; + child <<= 1; + } + else + break; + } + int par = child >> 2; + while (par != 0) + { + * ppar = &(p[par]); + if (!(LE(*ppar, *ph))) + { + *pk = *ppar; + pk = ppar; + par >>= 1; + } + else + break; + } + *pk = *ph; + p.del_high(); +} + + diff --git a/gnu/lib/libg++/g++-include/gen/XPPQ.hP b/gnu/lib/libg++/g++-include/gen/XPPQ.hP new file mode 100644 index 0000000000..af813a2b90 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/XPPQ.hP @@ -0,0 +1,105 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _XPPQ_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _XPPQ_h 1 + +#include ".PQ.h" +#include ".XPlex.h" + +class XPPQ : public PQ +{ +protected: + XPlex p; + +public: + XPPQ(int chunksize = DEFAULT_INITIAL_CAPACITY); + XPPQ(const XPPQ&); + + Pix enq( item); + deq(); + + & front(); + void del_front(); + + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + void del(Pix i); + int owns(Pix i); + Pix seek( item); + + int OK(); // rep invariant +}; + +inline XPPQ::XPPQ(int chunksize) + : p(1, chunksize) { count = 0; } + +inline XPPQ::XPPQ(const XPPQ& s) : p(s.p) { count = s.count; } + +inline Pix XPPQ::first() +{ + return p.first(); +} + +inline void XPPQ::next(Pix & idx) +{ + p.next(idx); +} + +inline & XPPQ::operator ()(Pix idx) +{ + return p(idx); +} + +inline & XPPQ::front () +{ + return p.low_element(); +} + +inline XPPQ::deq () +{ + x = p.low_element(); + del_front(); + return x; +} + +inline void XPPQ::clear() +{ + count = 0; p.clear(); +} + +inline int XPPQ::contains ( item) +{ + return seek(item) != 0; +} + +inline int XPPQ::owns (Pix idx) +{ + return p.owns(idx); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/XPQueue.ccP b/gnu/lib/libg++/g++-include/gen/XPQueue.ccP new file mode 100644 index 0000000000..77bfd1c7a9 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/XPQueue.ccP @@ -0,0 +1,4 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".XPQueue.h" diff --git a/gnu/lib/libg++/g++-include/gen/XPQueue.hP b/gnu/lib/libg++/g++-include/gen/XPQueue.hP new file mode 100644 index 0000000000..ebae20f3f6 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/XPQueue.hP @@ -0,0 +1,114 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _XPQueue_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _XPQueue_h + +#include ".XPlex.h" +#include ".Queue.h" + +class XPQueue : public Queue +{ +protected: + XPlex p; + +public: + XPQueue(int chunksize = DEFAULT_INITIAL_CAPACITY); + XPQueue(const XPQueue& q); + ~XPQueue(); + + void operator = (const XPQueue&); + + void enq( item); + deq(); + & front(); + void del_front(); + + void clear(); + int empty(); + int full(); + int length(); + + int OK(); +}; + +inline XPQueue::XPQueue(int chunksize) + : p(chunksize) {} +inline XPQueue::XPQueue(const XPQueue& q) : p(q.p) {} + +inline XPQueue::~XPQueue() {} + +inline void XPQueue::enq(item) +{ + p.add_high(item); +} + +inline XPQueue::deq() +{ + res = p.low_element(); + p.del_low(); + return res; +} + +inline & XPQueue::front() +{ + return p.low_element(); +} + + +inline void XPQueue::del_front() +{ + p.del_low(); +} + +inline void XPQueue::operator =(const XPQueue& s) +{ + p.operator = (s.p); +} + +inline int XPQueue::empty() +{ + return p.empty(); +} + +inline int XPQueue::full() +{ + return p.full(); +} + +inline int XPQueue::length() +{ + return p.length(); +} + +inline int XPQueue::OK() +{ + return p.OK(); +} + +inline void XPQueue::clear() +{ + p.clear(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/XPSet.ccP b/gnu/lib/libg++/g++-include/gen/XPSet.ccP new file mode 100644 index 0000000000..1102790700 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/XPSet.ccP @@ -0,0 +1,63 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".XPSet.h" + +int XPSet::OK() +{ + int v = p.OK(); + v &= count == p.length(); + if (!v) error("invariant failure"); + return v; +} + +Pix XPSet::seek( item) +{ + for (int i = p.low(); i < p.fence(); p.next(i)) + if (EQ(p[i],item)) return p.index_to_Pix(i); + return 0; +} + +Pix XPSet::add( item) +{ + Pix i = seek(item); + if (i == 0) + { + ++count; + i = p.index_to_Pix(p.add_high(item)); + } + return i; +} + +void XPSet::del( item) +{ + for (int i = p.low(); i < p.fence(); p.next(i)) + { + if (EQ(p[i], item)) + { + --count; + p[i] = p.low_element(); + p.del_low(); + return; + } + } +} + diff --git a/gnu/lib/libg++/g++-include/gen/XPSet.hP b/gnu/lib/libg++/g++-include/gen/XPSet.hP new file mode 100644 index 0000000000..9269ec12c8 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/XPSet.hP @@ -0,0 +1,89 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _XPSet_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _XPSet_h 1 + +#include ".Set.h" +#include ".XPlex.h" + +class XPSet : public Set +{ +protected: + XPlex p; + +public: + XPSet(int chunksize = DEFAULT_INITIAL_CAPACITY); + XPSet(const XPSet&); + + Pix add( item); + void del( item); + int contains( item); + + void clear(); + + Pix first(); + void next(Pix& i); + & operator () (Pix i); + int owns(Pix i); + Pix seek( item); + + int OK(); +}; + + +inline XPSet::XPSet(int chunksize) + : p(chunksize) { count = 0; } + +inline XPSet::XPSet(const XPSet& s) : p(s.p) { count = s.count; } + +inline Pix XPSet::first() +{ + return p.first(); +} + +inline void XPSet::next(Pix & idx) +{ + p.next(idx); +} + +inline & XPSet::operator ()(Pix idx) +{ + return p(idx); +} + +inline void XPSet::clear() +{ + count = 0; p.clear(); +} + +inline int XPSet::contains ( item) +{ + return seek(item) != 0; +} + +inline int XPSet::owns (Pix idx) +{ + return p.owns(idx); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/XPStack.ccP b/gnu/lib/libg++/g++-include/gen/XPStack.ccP new file mode 100644 index 0000000000..fe24f0f044 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/XPStack.ccP @@ -0,0 +1,4 @@ +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".XPStack.h" diff --git a/gnu/lib/libg++/g++-include/gen/XPStack.hP b/gnu/lib/libg++/g++-include/gen/XPStack.hP new file mode 100644 index 0000000000..9d103e530d --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/XPStack.hP @@ -0,0 +1,115 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _XPStack_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _XPStack_h + +#include ".XPlex.h" +#include ".Stack.h" + +class XPStack : public Stack +{ + XPlex p; + +public: + XPStack(int chunksize = DEFAULT_INITIAL_CAPACITY); + XPStack(const XPStack& s); + ~XPStack(); + + void operator = (const XPStack&); + + void push( item); + pop(); + & top(); + void del_top(); + + int empty(); + int full(); + int length(); + + void clear(); + + int OK(); + +}; + + +inline XPStack::XPStack(int chunksize) + : p(chunksize) {} +inline XPStack::XPStack(const XPStack& s) : p(s.p) {} + +inline XPStack::~XPStack() {} + +inline void XPStack::push(item) +{ + p.add_high(item); +} + +inline XPStack::pop() +{ + res = p.high_element(); + p.del_high(); + return res; +} + +inline & XPStack::top() +{ + return p.high_element(); +} + +inline void XPStack::del_top() +{ + p.del_high(); +} + +inline void XPStack::operator =(const XPStack& s) +{ + p.operator = (s.p); +} + +inline int XPStack::empty() +{ + return p.empty(); +} + +inline int XPStack::full() +{ + return p.full(); +} + +inline int XPStack::length() +{ + return p.length(); +} + +inline int XPStack::OK() +{ + return p.OK(); +} + +inline void XPStack::clear() +{ + p.clear(); +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/XPlex.ccP b/gnu/lib/libg++/g++-include/gen/XPlex.ccP new file mode 100644 index 0000000000..c91e5035a4 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/XPlex.ccP @@ -0,0 +1,397 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include ".XPlex.h" + + +XPlex:: XPlex() +{ + lo = fnc = 0; + csize = DEFAULT_INITIAL_CAPACITY; + * data = new [csize]; + set_cache(new IChunk(data, lo, lo, fnc, lo+csize)); + hd = ch; +} + +XPlex:: XPlex(int chunksize) +{ + if (chunksize == 0) error("invalid constructor specification"); + lo = fnc = 0; + if (chunksize > 0) + { + csize = chunksize; + * data = new [csize]; + set_cache(new IChunk(data, lo, lo, fnc, csize)); + hd = ch; + } + else + { + csize = -chunksize; + * data = new [csize]; + set_cache(new IChunk(data, chunksize, lo, fnc, fnc)); + hd = ch; + } +} + + +XPlex:: XPlex(int l, int chunksize) +{ + if (chunksize == 0) error("invalid constructor specification"); + lo = fnc = l; + if (chunksize > 0) + { + csize = chunksize; + * data = new [csize]; + set_cache(new IChunk(data, lo, lo, fnc, csize+lo)); + hd = ch; + } + else + { + csize = -chunksize; + * data = new [csize]; + set_cache(new IChunk(data, chunksize+lo, lo, fnc, fnc)); + hd = ch; + } +} + +void XPlex::make_initial_chunks(int up) +{ + int need = fnc - lo; + hd = 0; + if (up) + { + int l = lo; + do + { + int sz; + if (need >= csize) + sz = csize; + else + sz = need; + * data = new [csize]; + IChunk* h = new IChunk(data, l, l, l+sz, l+csize); + if (hd != 0) + h->link_to_next(hd); + else + hd = h; + l += sz; + need -= sz; + } while (need > 0); + } + else + { + int hi = fnc; + do + { + int sz; + if (need >= csize) + sz = csize; + else + sz = need; + * data = new [csize]; + IChunk* h = new IChunk(data, hi-csize, hi-sz, hi, hi); + if (hd != 0) + h->link_to_next(hd); + hd = h; + hi -= sz; + need -= sz; + } while (need > 0); + } + set_cache(hd); +} + +XPlex:: XPlex(int l, int hi, const initval, int chunksize) +{ + lo = l; + fnc = hi + 1; + if (chunksize == 0) + { + csize = fnc - l; + make_initial_chunks(1); + } + else if (chunksize < 0) + { + csize = -chunksize; + make_initial_chunks(0); + } + else + { + csize = chunksize; + make_initial_chunks(1); + } + fill(initval); +} + +XPlex::XPlex(const XPlex& a) +{ + lo = a.lo; + fnc = a.fnc; + csize = a.csize; + make_initial_chunks(); + for (int i = a.low(); i < a.fence(); a.next(i)) (*this)[i] = a[i]; +} + +void XPlex::operator= (const XPlex& a) +{ + if (&a != this) + { + invalidate(); + lo = a.lo; + fnc = a.fnc; + csize = a.csize; + make_initial_chunks(); + for (int i = a.low(); i < a.fence(); a.next(i)) (*this)[i] = a[i]; + } +} + + +void XPlex::cache(int idx) const +{ + const IChunk* tail = tl(); + const IChunk* t = ch; + while (idx >= t->fence_index()) + { + if (t == tail) index_error(); + t = (t->next()); + } + while (idx < t->low_index()) + { + if (t == hd) index_error(); + t = (t->prev()); + } + set_cache(t); +} + + +void XPlex::cache(const * p) const +{ + const IChunk* old = ch; + const IChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = (t->next()); + if (t == old) index_error(); + } + set_cache(t); +} + +int XPlex::owns(Pix px) const +{ + * p = (*)px; + const IChunk* old = ch; + const IChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = (t->next()); + if (t == old) { set_cache(t); return 0; } + } + set_cache(t); + return 1; +} + + +* XPlex::dosucc(const * p) const +{ + if (p == 0) return 0; + const IChunk* old = ch; + const IChunk* t = ch; + + while (!t->actual_pointer(p)) + { + t = (t->next()); + if (t == old) return 0; + } + int i = t->index_of(p) + 1; + if (i >= fnc) return 0; + if (i >= t->fence_index()) t = (t->next()); + set_cache(t); + return (t->pointer_to(i)); +} + +* XPlex::dopred(const * p) const +{ + if (p == 0) return 0; + const IChunk* old = ch; + const IChunk* t = ch; + while (!t->actual_pointer(p)) + { + t = (t->prev()); + if (t == old) return 0; + } + int i = t->index_of(p) - 1; + if (i < lo) return 0; + if (i < t->low_index()) t = (t->prev()); + set_cache(t); + return (t->pointer_to(i)); +} + + +int XPlex::add_high(const elem) +{ + IChunk* t = tl(); + if (!t->can_grow_high()) + { + if (t->IChunk::empty() && one_chunk()) + t->clear(fnc); + else + { + * data = new [csize]; + t = (new IChunk(data, fnc, fnc, fnc,fnc+csize)); + t->link_to_prev(tl()); + } + } + *((t->IChunk::grow_high())) = elem; + set_cache(t); + return fnc++; +} + +int XPlex::del_high () +{ + if (empty()) empty_error(); + IChunk* t = tl(); + t->IChunk::shrink_high(); + if (t->IChunk::empty() && !one_chunk()) + { + IChunk* pred = t->prev(); + del_chunk(t); + t = pred; + } + set_cache(t); + return --fnc - 1; +} + +int XPlex::add_low (const elem) +{ + IChunk* t = hd; + if (!t->can_grow_low()) + { + if (t->IChunk::empty() && one_chunk()) + t->cleardown(lo); + else + { + * data = new [csize]; + hd = new IChunk(data, lo-csize, lo, lo, lo); + hd->link_to_next(t); + t = hd; + } + } + *((t->IChunk::grow_low())) = elem; + set_cache(t); + return --lo; +} + + +int XPlex::del_low () +{ + if (empty()) empty_error(); + IChunk* t = hd; + t->IChunk::shrink_low(); + if (t->IChunk::empty() && !one_chunk()) + { + hd = t->next(); + del_chunk(t); + t = hd; + } + set_cache(t); + return ++lo; +} + +void XPlex::reverse() +{ + tmp; + int l = lo; + int h = fnc - 1; + IChunk* loch = hd; + IChunk* hich = tl(); + while (l < h) + { + * lptr = loch->pointer_to(l); + * hptr = hich->pointer_to(h); + tmp = *lptr; + *lptr = *hptr; + *hptr = tmp; + if (++l >= loch->fence_index()) loch = loch->next(); + if (--h < hich->low_index()) hich = hich->prev(); + } +} + +void XPlex::fill(const x) +{ + for (int i = lo; i < fnc; ++i) (*this)[i] = x; +} + +void XPlex::fill(const x, int l, int hi) +{ + for (int i = l; i <= hi; ++i) (*this)[i] = x; +} + + +void XPlex::clear() +{ + if (fnc != lo) + { + IChunk* t = tl(); + while (t != hd) + { + IChunk* prv = t->prev(); + del_chunk(t); + t = prv; + } + t->IChunk::clear(lo); + set_cache(t); + fnc = lo; + } +} + + +int XPlex::OK () const +{ + int v = hd != 0 && ch != 0; // at least one chunk + + v &= fnc == tl()->fence_index();// last chunk fence == plex fence + v &= lo == ((hd))->IChunk::low_index(); // first lo == plex lo + +// loop for others: + int found_ch = 0; // to make sure ch is in list; + const IChunk* t = (hd); + for (;;) + { + if (t == ch) ++found_ch; + v &= t->IChunk::OK(); // each chunk is OK + if (t == tl()) + break; + else // and has indices contiguous to succ + { + v &= t->top_index() == t->next()->base_index(); + if (t != hd) // internal chunks full + { + v &= !t->empty(); + v &= !t->can_grow_low(); + v &= !t->can_grow_high(); + } + t = t->next(); + } + } + v &= found_ch == 1; + if (!v) error("invariant failure"); + return v; +} diff --git a/gnu/lib/libg++/g++-include/gen/XPlex.hP b/gnu/lib/libg++/g++-include/gen/XPlex.hP new file mode 100644 index 0000000000..41f100091b --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/XPlex.hP @@ -0,0 +1,238 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + based on code by Marc Shapiro (shapiro@sor.inria.fr) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _XPlex_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _XPlex_h 1 + +#include ".Plex.h" + +class XPlex: public Plex +{ + IChunk* ch; // cached chunk + + void make_initial_chunks(int up = 1); + + void cache(int idx) const; + void cache(const * p) const; + + * dopred(const * p) const; + * dosucc(const * p) const; + + void set_cache(const IChunk* t) const; // logically, + // not physically const +public: + XPlex(); // set low = 0; + // fence = 0; + // csize = default + + XPlex(int ch_size); // low = 0; + // fence = 0; + // csize = ch_size + + XPlex(int lo, // low = lo; + int ch_size); // fence=lo + // csize = ch_size + + XPlex(int lo, // low = lo + int hi, // fence = hi+1 + const initval,// fill with initval, + int ch_size = 0); // csize= ch_size + // or fence-lo if 0 + + XPlex(const XPlex&); + + void operator= (const XPlex&); + +// virtuals + + + & high_element (); + & low_element (); + + const & high_element () const; + const & low_element () const; + + Pix first() const; + Pix last() const; + void prev(Pix& ptr) const; + void next(Pix& ptr) const; + int owns(Pix p) const; + & operator () (Pix p); + const & operator () (Pix p) const; + + int low() const; + int high() const; + int valid(int idx) const; + void prev(int& idx) const; + void next(int& x) const; + & operator [] (int index); + const & operator [] (int index) const; + + int Pix_to_index(Pix p) const; + Pix index_to_Pix(int idx) const; + + int can_add_high() const; + int can_add_low() const; + int full() const; + + int add_high(const elem); + int del_high (); + int add_low (const elem); + int del_low (); + + void fill(const x); + void fill(const x, int from, int to); + void clear(); + void reverse(); + + int OK () const; + +}; + + +inline void XPlex::prev(int& idx) const +{ + --idx; +} + +inline void XPlex::next(int& idx) const +{ + ++idx; +} + +inline int XPlex::full () const +{ + return 0; +} + +inline int XPlex::can_add_high() const +{ + return 1; +} + +inline int XPlex::can_add_low() const +{ + return 1; +} + +inline int XPlex::valid (int idx) const +{ + return idx >= lo && idx < fnc; +} + +inline int XPlex::low() const +{ + return lo; +} + +inline int XPlex::high() const +{ + return fnc - 1; +} + +inline & XPlex:: operator [] (int idx) +{ + if (!ch->actual_index(idx)) cache(idx); + return *(ch->pointer_to(idx)); +} + +inline const & XPlex:: operator [] (int idx) const +{ + if (!ch->actual_index(idx)) cache(idx); + return *((const *)(ch->pointer_to(idx))); +} + +inline & XPlex::low_element () +{ + if (empty()) index_error(); + return *(hd->pointer_to(lo)); +} + +inline const & XPlex::low_element () const +{ + if (empty()) index_error(); + return *((const *)(hd->pointer_to(lo))); +} + +inline & XPlex::high_element () +{ + if (empty()) index_error(); + return *(tl()->pointer_to(fnc - 1)); +} + +inline const & XPlex::high_element () const +{ + if (empty()) index_error(); + return *((const *)(tl()->pointer_to(fnc - 1))); +} + +inline int XPlex::Pix_to_index(Pix px) const +{ + * p = (*)px; + if (!ch->actual_pointer(p)) cache(p); + return ch->index_of(p); +} + +inline Pix XPlex::index_to_Pix(int idx) const +{ + if (!ch->actual_index(idx)) cache(idx); + return (Pix)(ch->pointer_to(idx)); +} + +inline Pix XPlex::first() const +{ + return Pix(hd->IChunk::first_pointer()); +} + +inline Pix XPlex::last() const +{ + return Pix(tl()->IChunk::last_pointer()); +} + +inline void XPlex::prev(Pix& p) const +{ + Pix q = Pix(ch->IChunk::pred((*) p)); + p = (q == 0)? Pix(dopred((const *) p)) : q; +} + +inline void XPlex::next(Pix& p) const +{ + Pix q = Pix(ch->IChunk::succ((*) p)); + p = (q == 0)? Pix(dosucc((const *)p)) : q; +} + +inline & XPlex:: operator () (Pix p) +{ + return *((*)p); +} + +inline const & XPlex:: operator () (Pix p) const +{ + return *((const *)p); +} + +inline void XPlex::set_cache(const IChunk* t) const +{ + ((XPlex*)(this))->ch = (IChunk*)t; +} + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/defs.hP b/gnu/lib/libg++/g++-include/gen/defs.hP new file mode 100644 index 0000000000..054f6a65c3 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/defs.hP @@ -0,0 +1,57 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _defs_h +#define _defs_h 1 + + +// equality operator +#ifndef EQ +#define EQ(a, b) ((a) == (b)) +#endif + +// less-than-or-equal +#ifndef LE +#define LE(a, b) ((a) <= (b)) +#endif + +// comparison : less-than -> < 0; equal -> 0; greater-than -> > 0 +#ifndef CMP +#define CMP(a, b) ( ((a) <= (b))? (((a) == (b))? 0 : -1) : 1 ) +#endif + +// hash function +#ifndef HASH +extern unsigned int hash(); +#define HASH(x) hash(x) +#endif + +// initial capacity for structures requiring one + +#ifndef DEFAULT_INITIAL_CAPACITY +#define DEFAULT_INITIAL_CAPACITY 100 +#endif + +// HASHTABLE_TOO_CROWDED(COUNT, SIZE) is true iff a hash table with COUNT +// elements and SIZE slots is too full, and should be resized. +// This is so if available space is less than 1/8. + +#define HASHTABLE_TOO_CROWDED(COUNT, SIZE) ((SIZE) - ((SIZE) >> 3) <= (COUNT)) + +#endif diff --git a/gnu/lib/libg++/g++-include/gen/intSList.hP b/gnu/lib/libg++/g++-include/gen/intSList.hP new file mode 100644 index 0000000000..24aa3b6a68 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/intSList.hP @@ -0,0 +1,33 @@ +/* : Light weight List: This will simply reuse code from a int List, which +was genclassed from the SLList libg++ class. The classes generated from this file +will all be derived classes from class VoidSLList or intSLList. Note that class SLList does not +offer all the functionality of List classes, such as sharing of sub-Lists. +However, no additional code is needed at all and no .cc file is generated. So it costs nothing +to use these type-safe Lists. Only member functions needing type casting are re-defined */ + + +#ifndef _SList_h +#define _SList_h 1 + +#include "int.SLList.h" +#include ".defs.h" + +class SList : public intSLList +{ +public: + SList() {} + SList(SList& a) : (a) {} + ~SList() {} + + SList& operator = (SList& a) { + return (SList&) intSLList::operator= (a); } + + & operator () (Pix p) { return (&) (intSLList::operator() (p)); } + & front() { return (&) intSLList::front(); } + & rear() { return (&) intSLList::rear(); } + remove_front() { return () intSLList::remove_front(); } + +}; + +#endif /* conditional include */ + diff --git a/gnu/lib/libg++/g++-include/gen/intVec.hP b/gnu/lib/libg++/g++-include/gen/intVec.hP new file mode 100644 index 0000000000..e383870025 --- /dev/null +++ b/gnu/lib/libg++/g++-include/gen/intVec.hP @@ -0,0 +1,80 @@ +/* : light weight Vector: This will simply reuse code from */ +/* a int Vec, which was genclassed from the Vec libg++ class. */ +/* The classes generated from this file will all be derived classes */ +/* from class VoidVec or intVec. No .cc file is generated. So */ +/* it costs nothing to use these type-safe Vectors. Only member */ +/* functions needing type casting are re-defined. */ +/* */ + +#ifndef _Vec_h +#define _Vec_h 1 + +#include "int.Vec.h" +#include ".defs.h" + + +#ifndef __typedefs +#define __typedefs 1 +typedef void (*Procedure)( ); +typedef (*Mapper)( ); +typedef (*Combiner)( , ); +typedef int (*Predicate)( ); +typedef int (*Comparator)( , ); +#endif + +class Vec : public intVec +{ +protected: + Vec(int l, * d) : (l, (int*) d) {}; +public: + Vec() {}; + Vec(int l) : (l) {}; + Vec(int l, fill_value) : (l, fill_value) {}; + Vec(Vec& v) : (v) {}; + Vec(intVec& v) {fake_copy(v, s, len);} + ~Vec() {}; + + Vec& operator = (Vec& a) + {return (Vec&) intVec::operator= (a);} + Vec at(int from, int n) {return (Vec) intVec::at(from, n);} + + & operator [] (int n) {return (&)intVec::operator[] (n);} + & elem(int n) {return (&)intVec::elem(n);} + + friend Vec concat(Vec& a, Vec& b); + friend Vec map(Mapper f, Vec & a); + friend Vec merge(Vec & a, Vec & b, Comparator f); + friend Vec combine(Combiner f, Vec & a, Vec & b); + friend Vec reverse(Vec& a); + + void sort(Comparator f); + void apply(Procedure f); + reduce(Combiner f, base); +}; + +inline Vec concat(Vec& a, Vec& b) +{return (Vec)concat((intVec&)a, (intVec&)b);} + +inline Vec map(Mapper f, Vec & a) { + return (Vec)map((intMapper)f, (intVec&)a); } + +inline Vec merge(Vec & a, Vec & b, Comparator f) { + return (Vec)merge((intVec&)a, (intVec&)b, (intComparator)f); } + +inline Vec combine(Combiner f, Vec & a, Vec & b) { + return (Vec)combine((intCombiner)f, (intVec&)a, (intVec&)b); } + +inline Vec reverse(Vec& a) { + return (Vec)reverse((intVec&)a);} + +inline void Vec::sort(Comparator f) { + intVec::sort((intComparator) f); } + +inline void Vec::apply(Procedure f) { + intVec::apply((intProcedure) f); } + +inline Vec::reduce(Combiner f, base) { + return ()intVec::reduce((intCombiner)f, base);} + +#endif /* conditional include */ + diff --git a/gnu/lib/libg++/g++-include/generic.h b/gnu/lib/libg++/g++-include/generic.h deleted file mode 100644 index 743a26dcbc..0000000000 --- a/gnu/lib/libg++/g++-include/generic.h +++ /dev/null @@ -1,63 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifndef generic_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define generic_h 1 - -/* - * See the CPP manual, argument prescan section for explanation - */ - -#define name2(a,b) gEnErIc2(a,b) -#define gEnErIc2(a,b) a ## b - -#define name3(a,b,c) gEnErIc3(a,b,c) -#define gEnErIc3(a,b,c) a ## b ## c - -#define name4(a,b,c,d) gEnErIc4(a,b,c,d) -#define gEnErIc4(a,b,c,d) a ## b ## c ## d - -#define GENERIC_STRING(a) gEnErIcStRiNg(a) -#define gEnErIcStRiNg(a) #a - -#define declare(clas,t) name2(clas,declare)(t) -#define declare2(clas,t1,t2) name2(clas,declare2)(t1,t2) - -#define implement(clas,t) name2(clas,implement)(t) -#define implement2(clas,t1,t2) name2(clas,implement2)(t1,t2) - -extern genericerror(int,char*); -typedef int (*GPT)(int,char*); - -#define set_handler(gen,type,x) name4(set_,type,gen,_handler)(x) - -#define errorhandler(gen,type) name3(type,gen,handler) - -#define callerror(gen,type,a,b) (*errorhandler(gen,type))(a,b) - - -#endif generic_h diff --git a/gnu/lib/libg++/g++-include/grp.h b/gnu/lib/libg++/g++-include/grp.h new file mode 100644 index 0000000000..ed937deb7d --- /dev/null +++ b/gnu/lib/libg++/g++-include/grp.h @@ -0,0 +1,41 @@ +#ifndef grp_h + +extern "C" { + +#ifdef __grp_h_recursive +#include_next +#else +#define __grp_h_recursive + +#include + +#define getgrent c_proto_getgrent +#define getgrgid c_proto_getgrgid +#define getgrnam c_proto_getgrnam +#define setgrent c_proto_setgrent +#define endgrent c_proto_endgrent +#define fgetgrent c_proto_fgetgrent + +#include_next + +#define grp_h 1 + +#undef getgrent +#undef getgrgid +#undef getgrnam + +extern struct group* getgrent(); +extern struct group* fgetgrent(FILE*); +extern struct group* getgrgid(int); +extern struct group* getgrnam(const char*); +#if defined(__OSF1__) || defined (__386BSD__) +extern int setgrent(); +#else +extern void setgrent(); +#endif +extern void endgrent(); + +#endif +} + +#endif diff --git a/gnu/lib/libg++/g++-include/intSList.hP b/gnu/lib/libg++/g++-include/intSList.hP new file mode 100644 index 0000000000..24aa3b6a68 --- /dev/null +++ b/gnu/lib/libg++/g++-include/intSList.hP @@ -0,0 +1,33 @@ +/* : Light weight List: This will simply reuse code from a int List, which +was genclassed from the SLList libg++ class. The classes generated from this file +will all be derived classes from class VoidSLList or intSLList. Note that class SLList does not +offer all the functionality of List classes, such as sharing of sub-Lists. +However, no additional code is needed at all and no .cc file is generated. So it costs nothing +to use these type-safe Lists. Only member functions needing type casting are re-defined */ + + +#ifndef _SList_h +#define _SList_h 1 + +#include "int.SLList.h" +#include ".defs.h" + +class SList : public intSLList +{ +public: + SList() {} + SList(SList& a) : (a) {} + ~SList() {} + + SList& operator = (SList& a) { + return (SList&) intSLList::operator= (a); } + + & operator () (Pix p) { return (&) (intSLList::operator() (p)); } + & front() { return (&) intSLList::front(); } + & rear() { return (&) intSLList::rear(); } + remove_front() { return () intSLList::remove_front(); } + +}; + +#endif /* conditional include */ + diff --git a/gnu/lib/libg++/g++-include/intVec.hP b/gnu/lib/libg++/g++-include/intVec.hP new file mode 100644 index 0000000000..e383870025 --- /dev/null +++ b/gnu/lib/libg++/g++-include/intVec.hP @@ -0,0 +1,80 @@ +/* : light weight Vector: This will simply reuse code from */ +/* a int Vec, which was genclassed from the Vec libg++ class. */ +/* The classes generated from this file will all be derived classes */ +/* from class VoidVec or intVec. No .cc file is generated. So */ +/* it costs nothing to use these type-safe Vectors. Only member */ +/* functions needing type casting are re-defined. */ +/* */ + +#ifndef _Vec_h +#define _Vec_h 1 + +#include "int.Vec.h" +#include ".defs.h" + + +#ifndef __typedefs +#define __typedefs 1 +typedef void (*Procedure)( ); +typedef (*Mapper)( ); +typedef (*Combiner)( , ); +typedef int (*Predicate)( ); +typedef int (*Comparator)( , ); +#endif + +class Vec : public intVec +{ +protected: + Vec(int l, * d) : (l, (int*) d) {}; +public: + Vec() {}; + Vec(int l) : (l) {}; + Vec(int l, fill_value) : (l, fill_value) {}; + Vec(Vec& v) : (v) {}; + Vec(intVec& v) {fake_copy(v, s, len);} + ~Vec() {}; + + Vec& operator = (Vec& a) + {return (Vec&) intVec::operator= (a);} + Vec at(int from, int n) {return (Vec) intVec::at(from, n);} + + & operator [] (int n) {return (&)intVec::operator[] (n);} + & elem(int n) {return (&)intVec::elem(n);} + + friend Vec concat(Vec& a, Vec& b); + friend Vec map(Mapper f, Vec & a); + friend Vec merge(Vec & a, Vec & b, Comparator f); + friend Vec combine(Combiner f, Vec & a, Vec & b); + friend Vec reverse(Vec& a); + + void sort(Comparator f); + void apply(Procedure f); + reduce(Combiner f, base); +}; + +inline Vec concat(Vec& a, Vec& b) +{return (Vec)concat((intVec&)a, (intVec&)b);} + +inline Vec map(Mapper f, Vec & a) { + return (Vec)map((intMapper)f, (intVec&)a); } + +inline Vec merge(Vec & a, Vec & b, Comparator f) { + return (Vec)merge((intVec&)a, (intVec&)b, (intComparator)f); } + +inline Vec combine(Combiner f, Vec & a, Vec & b) { + return (Vec)combine((intCombiner)f, (intVec&)a, (intVec&)b); } + +inline Vec reverse(Vec& a) { + return (Vec)reverse((intVec&)a);} + +inline void Vec::sort(Comparator f) { + intVec::sort((intComparator) f); } + +inline void Vec::apply(Procedure f) { + intVec::apply((intProcedure) f); } + +inline Vec::reduce(Combiner f, base) { + return ()intVec::reduce((intCombiner)f, base);} + +#endif /* conditional include */ + diff --git a/gnu/lib/libg++/g++-include/istream.h b/gnu/lib/libg++/g++-include/istream.h deleted file mode 100644 index 1fa10fe594..0000000000 --- a/gnu/lib/libg++/g++-include/istream.h +++ /dev/null @@ -1,259 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1989 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -/* *** Version 1.2 -- nearly 100% AT&T 1.2 compatible *** */ - -/* istream.h now separately includable */ - -#ifndef _istream_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _istream_h 1 - - -#include -#include -#include -#include - -class whitespace // a class used only to input and -{ // discard white space characters - char filler; -}; - -class ostream; - -class istream -{ - friend void eatwhite(istream& s); -protected: - streambuf* bp; - state_value state; // _good/_eof/_fail/_bad - ostream* tied_to; - char skipws; - char ownbuf; - void _flush(); - char* readline (int chunk_number, char terminator); - -public: - istream(const char* filename, io_mode m, access_mode a, - int sk=1, ostream* t = 0); - istream(const char* filename, const char* m, - int sk=1, ostream* t = 0); - istream(int filedesc, io_mode m, int sk=1, ostream* t = 0); - istream(FILE* fileptr, int sk=1, ostream* t = 0); - istream(int sz, char* buf, int sk=1, ostream* t = 0); - istream(int filedesc, int sk=1, ostream* t = 0); - istream(int filedesc, char* buf, int buflen, - int sk, ostream* t = 0); - istream(streambuf* s, int sk=1, ostream* t = 0); - - ~istream(); - - istream& open(const char* filename, io_mode m, access_mode a); - istream& open(const char* filename, const char* m); - istream& open(int filedesc, io_mode m); - istream& open(FILE* fileptr); - istream& open(const char* filenam, open_mode m); - - istream& close(); - - ostream* tie(ostream* s); - int skip(int); - -// stream status - - int rdstate(); - int eof(); - int fail(); - int bad(); - int good(); - -// other status queries - - int readable(); - int writable(); - int is_open(); - - operator void*(); - int operator !(); - - const char* name(); - - char* bufptr(); - -// error handling - - void error(); - void clear(state_value f = _good); // poorly named - void set(state_value f); // set corresponding bit - void unset(state_value f); // clear corresponding bit - istream& failif(int cond); - -// unformatted IO - - istream& get(char& c); - istream& unget(char c); - istream& putback(char c); // a synonym for unget - - istream& get (char* s, int n, char terminator = '\n'); - istream& getline(char* s, int n, char terminator = '\n'); - istream& gets (char **s, char terminator = '\n'); - - - istream& operator >> (char& c); - istream& operator >> (short& n); - istream& operator >> (unsigned short& n); - istream& operator >> (int& n); - istream& operator >> (unsigned int& n); - istream& operator >> (long& n); - istream& operator >> (unsigned long& n); -#ifdef __GNUG__ - istream& operator >> (long long& n); - istream& operator >> (unsigned long long& n); -#endif - istream& operator >> (float& n); - istream& operator >> (double& n); - istream& operator >> (char* s); - istream& operator >> (whitespace& w); -}; - -// pre-declared streams - -extern istream cin; // stdin - -extern whitespace WS; // for convenience - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - - -inline void istream::clear(state_value flag) -{ - state = flag; -} - -inline void istream::set(state_value flag) -{ - state = state_value(int(state) | int(flag)); -} - -inline void istream::unset(state_value flag) -{ - state = state_value(int(state) & ~int(flag)); -} - -inline int istream::rdstate() -{ - return int(state); -} - -inline int istream::good() -{ - return state == _good; -} - -inline int istream::eof() -{ - return int(state) & int(_eof); -} - -inline int istream::fail() -{ - return int(state) & int(_fail); -} - -inline int istream::bad() -{ - return int(state) & int(_bad); -} - -inline istream::operator void*() -{ - return (state == _good)? this : 0; -} - -inline int istream::operator !() -{ - return (state != _good); -} - -inline istream& istream::failif(int cond) -{ - if (cond) set(_fail); return *this; -} - -inline int istream::is_open() -{ - return bp->is_open(); -} - -inline int istream::readable() -{ - return (bp != 0) && (bp->is_open()) && (state == _good); -} - -inline int istream::writable() -{ - return 0; -} - - -inline char* istream::bufptr() -{ - return bp->base; -} - - -inline istream& istream::close() -{ - bp->close(); return *this; -} - - -inline int istream::skip(int sk) -{ - int was = skipws; skipws = sk; return was; -} - - -inline istream& istream::unget(char c) -{ - if (bp->sputbackc(c) == EOF) set(_fail); return *this; -} - -inline istream& istream::putback(char c) -{ - if (bp->sputbackc(c) == EOF) set(_fail); return *this; -} - -inline void eatwhite(istream& s) -{ - s >> WS; -} - -#endif - - -#endif diff --git a/gnu/lib/libg++/g++-include/malloc.h b/gnu/lib/libg++/g++-include/malloc.h deleted file mode 100644 index dff79ed6b0..0000000000 --- a/gnu/lib/libg++/g++-include/malloc.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/gnu/lib/libg++/g++-include/math.h b/gnu/lib/libg++/g++-include/math.h new file mode 100644 index 0000000000..2bf4330519 --- /dev/null +++ b/gnu/lib/libg++/g++-include/math.h @@ -0,0 +1,221 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _math_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _math_h 1 + +#if defined(hp300) && defined(__HAVE_FPU__) +#define __HAVE_68881__ 1 +#endif + +#if defined(masscomp) +#define __HAVE_68881__ 1 +#endif + +#ifdef __HAVE_68881__ /* MC68881/2 Floating-Point Coprocessor */ +extern "C" { /* fill in what we've left out */ +#include + +double acosh(double); +double asinh(double); +double cbrt(double); +double copysign(double,double); +double erf(double); +double erfc(double); +double finite(double); +double gamma(double); +double hypot(double,double); +double infnan(int); +int isinf(double); +int isnan(double); +double j0(double); +double j1(double); +double jn(int, double); +double lgamma(double); +double y0(double); +double y1(double); +double yn(int, double); + +double aint(double); +double anint(double); +int irint(double); +int nint(double); +} +/* Please add inline asm code for other machines here! */ +#else +extern "C" { + +#include <_G_config.h> + +double acos(double); +double acosh(double); +double asin(double); +double asinh(double); +double atan(double); +double atan2(double, double); +double atanh(double); +double cbrt(double); +double ceil(double); +double copysign(double,double); +double cos(double); +double cosh(double); +double drem(double,double); +double erf(double); +double erfc(double); +double exp(double); +double expm1(double); +double fabs(double); +int finite(double); +double floor(double); +double fmod(double, double); +double frexp(double, int*); +double gamma(double); +double hypot(double,double); +double infnan(int); +#if !defined(sequent) && !defined(DGUX) &&!defined(sony) && !defined(masscomp) && !defined(hpux) +/* see below */ +int isinf(double); +int isnan(double); +#endif +double j0(double); +double j1(double); +double jn(int, double); +double ldexp(double, int); +double lgamma(double); +double log(double); +double log10(double); +double log1p(double); +double logb(double); +double modf(double, double*); +double pow(double, double); +double rint(double); +double scalb _G_ARGS((double, int)); +double sin(double); +double sinh(double); +double sqrt(double); +double tan(double); +double tanh(double); +double y0(double); +double y1(double); +double yn(int, double); + +double aint(double); +double anint(double); +int irint(double); +int nint(double); +} + +#endif + +/* libg++ doesn't use this since it is not available on some systems */ + +/* the following ifdef is just for compiling OOPS */ + +#ifndef DONT_DECLARE_EXCEPTION +struct libm_exception +{ + int type; + char* name; + double arg1, arg2, retval; +}; + +#define DOMAIN 1 +#define SING 2 +#define OVERFLOW 3 +#define UNDERFLOW 4 +#define TLOSS 5 +#define PLOSS 6 + +extern "C" int matherr(libm_exception*); + +#endif + +#include + +/* On some systems, HUGE ought to be MAXFLOAT or IEEE infinity */ + +#ifndef HUGE +#define HUGE DBL_MAX +#endif +#ifndef HUGE_VAL +#define HUGE_VAL DBL_MAX +#endif + + +/* sequents don't supply these. The following should suffice */ +#if defined(sequent) || defined(DGUX) || defined(sony) || defined(masscomp) \ +|| defined(hpux) +#include +static inline int isnan(double x) { return x != x; } +static inline int isinf(double x) { return x > DBL_MAX || x < -DBL_MAX; } +#endif + +/* These seem to be sun & sysV names of these constants */ + +#ifndef M_E +#define M_E 2.7182818284590452354 +#endif +#ifndef M_LOG2E +#define M_LOG2E 1.4426950408889634074 +#endif +#ifndef M_LOG10E +#define M_LOG10E 0.43429448190325182765 +#endif +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif +#ifndef M_LN10 +#define M_LN10 2.30258509299404568402 +#endif +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923 +#endif +#ifndef M_1_PI +#define M_1_PI 0.31830988618379067154 +#endif +#ifndef M_PI_4 +#define M_PI_4 0.78539816339744830962 +#endif +#ifndef M_2_PI +#define M_2_PI 0.63661977236758134308 +#endif +#ifndef M_2_SQRTPI +#define M_2_SQRTPI 1.12837916709551257390 +#endif +#ifndef M_SQRT2 +#define M_SQRT2 1.41421356237309504880 +#endif +#ifndef M_SQRT1_2 +#define M_SQRT1_2 0.70710678118654752440 +#endif + +#ifndef PI // as in stroustrup +#define PI M_PI +#endif +#ifndef PI2 +#define PI2 M_PI_2 +#endif + +#endif diff --git a/gnu/lib/libg++/g++-include/max.h b/gnu/lib/libg++/g++-include/max.h deleted file mode 100644 index 8c2c8f98ce..0000000000 --- a/gnu/lib/libg++/g++-include/max.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/gnu/lib/libg++/g++-include/memory.h b/gnu/lib/libg++/g++-include/memory.h new file mode 100644 index 0000000000..dc3b3d5282 --- /dev/null +++ b/gnu/lib/libg++/g++-include/memory.h @@ -0,0 +1,42 @@ + +#ifndef _memory_h +#define _memory_h 1 + +#include "_G_config.h" +#include + +extern "C" { + +void* memalign _G_ARGS((_G_size_t, _G_size_t)); +void* memccpy _G_ARGS((void*, const void*, int, _G_size_t)); +void* memchr _G_ARGS((const void*, int, _G_size_t)); +int memcmp _G_ARGS((const void*, const void*, _G_size_t)); +void* memcpy _G_ARGS((void*, const void*, _G_size_t)); +void* memmove _G_ARGS((void*, const void*, _G_size_t)); +void* memset _G_ARGS((void*, int, _G_size_t)); +int ffs _G_ARGS((int)); +#if defined(__OSF1__) || defined(__386BSD__) +int getpagesize _G_ARGS((void)); +#else +_G_size_t getpagesize _G_ARGS((void)); +#endif +void* valloc _G_ARGS((_G_size_t)); + +void bcopy _G_ARGS((const void*, void*, _G_size_t)); +int bcmp _G_ARGS((const void*, const void*, int)); +void bzero _G_ARGS((void*, int)); +} + +#ifdef __GNUG__ +#ifndef alloca +#define alloca(x) __builtin_alloca(x) +#endif +#else +#ifndef IV +extern "C" void* alloca(_G_size_t); +#else +extern "C" void* alloca(unsigned long); +#endif /* IV */ +#endif + +#endif diff --git a/gnu/lib/libg++/g++-include/min.h b/gnu/lib/libg++/g++-include/min.h deleted file mode 100644 index 8c2c8f98ce..0000000000 --- a/gnu/lib/libg++/g++-include/min.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/gnu/lib/libg++/g++-include/minmax.h b/gnu/lib/libg++/g++-include/minmax.h deleted file mode 100644 index 8c2c8f98ce..0000000000 --- a/gnu/lib/libg++/g++-include/minmax.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/gnu/lib/libg++/g++-include/netdb.h b/gnu/lib/libg++/g++-include/netdb.h new file mode 100644 index 0000000000..c718a8e5b7 --- /dev/null +++ b/gnu/lib/libg++/g++-include/netdb.h @@ -0,0 +1,4 @@ +extern "C" +{ +#include_next +} diff --git a/gnu/lib/libg++/g++-include/new.h b/gnu/lib/libg++/g++-include/new.h deleted file mode 100644 index 95d86e0218..0000000000 --- a/gnu/lib/libg++/g++-include/new.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef _new_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _new_h 1 - -#include -#include - -#ifndef NO_LIBGXX_MALLOC -#define MALLOC_ALIGN_MASK 7 /* ptrs aligned at 8 byte boundaries */ -#define MALLOC_MIN_OVERHEAD 8 /* 8 bytes of overhead per pointer */ -#endif - -typedef void (*new_handler_t)(); -extern "C" void default_new_handler(); -extern "C" new_handler_t set_new_handler(new_handler_t); - -#ifdef __GNUG__ -#define NEW(where) new { where } -#endif - -// default placement version of operator new -static inline void *operator new(size_t, void *place) { return place; } - -// provide a C++ interface to vector-resize via realloc -static inline void *operator new(size_t size, void *ptr, size_t new_len) -{ - return realloc(ptr, new_len * size); -} - -#endif diff --git a/gnu/lib/libg++/g++-include/open.h b/gnu/lib/libg++/g++-include/open.h deleted file mode 100644 index ffe81a15ac..0000000000 --- a/gnu/lib/libg++/g++-include/open.h +++ /dev/null @@ -1,39 +0,0 @@ - -#ifndef _open_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _open_h 1 - -#include -#include // needed to determine values of O_RDONLY... - -/* - - translation stuff for opening files. - -*/ - - -enum sys_open_cmd_io_mode // These should be correct for most systems -{ - sio_read = O_RDONLY, - sio_write = O_WRONLY, - sio_readwrite = O_RDWR, - sio_append = O_APPEND -}; - -enum sys_open_cmd_access_mode -{ - sa_create = O_CREAT, - sa_truncate = O_TRUNC, - sa_createonly = O_EXCL | O_CREAT -}; - - -int open_cmd_arg(io_mode i, access_mode a); // decode modes -char* fopen_cmd_arg(io_mode i); -int open_cmd_arg(const char* m); - -#endif diff --git a/gnu/lib/libg++/g++-include/osfcn.h b/gnu/lib/libg++/g++-include/osfcn.h deleted file mode 100644 index 52b3063586..0000000000 --- a/gnu/lib/libg++/g++-include/osfcn.h +++ /dev/null @@ -1,4 +0,0 @@ -#include -#include -#include -#include diff --git a/gnu/lib/libg++/g++-include/ostream.h b/gnu/lib/libg++/g++-include/ostream.h deleted file mode 100644 index 3bf30d735d..0000000000 --- a/gnu/lib/libg++/g++-include/ostream.h +++ /dev/null @@ -1,256 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1989 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -/* *** Version 1.2 -- nearly 100% AT&T 1.2 compatible *** */ - -/* ostream.h now separately includable */ - -#ifndef _ostream_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _ostream_h 1 - -/* uncomment the next line to disable ostream << char */ -//#define NO_OUTPUT_CHAR - - -#include -#include -#include -#include - -class istream; - -class ostream -{ - friend class istream; -protected: - streambuf* bp; - state_value state; // _good/_eof/_fail/_bad - char ownbuf; // true if we own *bp - -public: - ostream(const char* filename, io_mode m, access_mode a); - ostream(const char* filename, const char* m); - ostream(int filedesc, io_mode m); - ostream(FILE* fileptr); - ostream(int sz, char* buf); - ostream(int filedesc, char* buf, int buflen); - ostream(int filedesc); - ostream(streambuf* s); - - ~ostream(); - - ostream& open(const char* filename, io_mode m, access_mode a); - ostream& open(const char* filename, const char* m); - ostream& open(int filedesc, io_mode m); - ostream& open(FILE* fileptr); - ostream& open(const char* filenam, open_mode m); - - ostream& close(); - ostream& flush(); - -// stream status - - int rdstate(); - int eof(); - int fail(); - int bad(); - int good(); - -// other status queries - - int readable(); - int writable(); - int is_open(); - - operator void*(); - int operator !(); - - const char* name(); - - char* bufptr(); - -// error handling - - void error(); - void clear(state_value f = _good); // poorly named - void set(state_value f); // set corresponding bit - void unset(state_value); // clear corresponding bit - ostream& failif(int cond); - -// unformatted IO - - ostream& put(char c); - ostream& put(const char* s); - ostream& put(const char* s, int slen); - -// formatted IO - - ostream& form(const char* fmt, ...); - - ostream& operator << (short n); - ostream& operator << (unsigned short n); - ostream& operator << (int n); - ostream& operator << (unsigned int n); - ostream& operator << (long n); - ostream& operator << (unsigned long n); -#ifdef __GNUG__ - ostream& operator << (long long n); - ostream& operator << (unsigned long long n); -#endif __GNUG__ - ostream& operator << (float n); - ostream& operator << (double n); - ostream& operator << (const char* s); - ostream& operator << (const void* ptr); - -#ifndef NO_OUTPUT_CHAR - ostream& operator << (char c); -#endif - -}; - -extern ostream cout; // stdout -extern ostream cerr; // stderr - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - - -inline void ostream::clear(state_value flag) -{ - state = flag; -} - -inline void ostream::set(state_value flag) -{ - state = state_value(int(state) | int(flag)); -} - -inline void ostream::unset(state_value flag) -{ - state = state_value(int(state) & ~int(flag)); -} - -inline int ostream::rdstate() -{ - return int(state); -} - -inline int ostream::good() -{ - return state == _good; -} - -inline int ostream::eof() -{ - return int(state) & int(_eof); -} - -inline int ostream::fail() -{ - return int(state) & int(_fail); -} - -inline int ostream::bad() -{ - return int(state) & int(_bad); -} - -inline ostream::operator void*() -{ - return (state == _good)? this : 0; -} - -inline int ostream::operator !() -{ - return (state != _good); -} - -inline ostream& ostream::failif(int cond) -{ - if (cond) set(_fail); return *this; -} - -inline int ostream::is_open() -{ - return bp->is_open(); -} - -inline int ostream::readable() -{ - return 0; -} - -inline int ostream::writable() -{ - return (bp != 0) && (state == _good); -} - - -inline char* ostream::bufptr() -{ - return bp->base; -} - -inline ostream& ostream::flush() -{ - bp->overflow(); return *this; -} - -inline ostream& ostream::close() -{ - bp->overflow(); bp->close(); return *this; -} - -inline ostream& ostream::put(char ch) -{ - return failif((state != _good) || bp->sputc((int)ch &0xff) == EOF); -} - -#ifndef NO_OUTPUT_CHAR -inline ostream& ostream::operator << (char ch) -{ - return failif((state != _good) || bp->sputc((int)ch &0xff) == EOF); -} -#endif - -inline ostream& ostream::put(const char* s) -{ - return failif((state != _good) || bp->sputs(s) == EOF); -} - -inline ostream& ostream::put(const char* s, int len) -{ - return failif((state != _good) || bp->sputsn(s, len) == EOF); -} - -inline ostream& ostream::operator << (const char* s) -{ - return failif((state != _good) || bp->sputs(s) == EOF); -} - -#endif - -#endif diff --git a/gnu/lib/libg++/g++-include/pwd.h b/gnu/lib/libg++/g++-include/pwd.h new file mode 100644 index 0000000000..f33d7ee414 --- /dev/null +++ b/gnu/lib/libg++/g++-include/pwd.h @@ -0,0 +1,36 @@ +#ifndef _pwd_h + +// the Interviews-based standard kludge again + +extern "C" { + +#ifdef __pwd_h_recursive +#include_next +#else +#define getpwent c_proto_getpwent +#define getpwuid c_proto_getpwuid +#define getpwnam c_proto_getpwnam +#define setpwent c_proto_setpwent +#define endpwent c_proto_endpwent + +#define __pwd_h_recursive +#include_next + +#define _pwd_h 1 + +#undef getpwent +#undef getpwuid +#undef getpwnam +#undef setpwent +#undef endpwent + +extern struct passwd* getpwent(); +extern struct passwd* getpwuid(int); +extern struct passwd* getpwnam(const char*); +extern int setpwent(); +extern int endpwent(); + +#endif +} + +#endif diff --git a/gnu/lib/libg++/g++-include/regex.h b/gnu/lib/libg++/g++-include/regex.h deleted file mode 100644 index e0252cb72d..0000000000 --- a/gnu/lib/libg++/g++-include/regex.h +++ /dev/null @@ -1,280 +0,0 @@ -/* Definitions for data structures callers pass the regex library. - Copyright (C) 1985 Free Software Foundation, Inc. - - NO WARRANTY - - BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY -NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT -WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC, -RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS" -WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY -AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE -DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR -CORRECTION. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M. -STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY -WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE -LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR -OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR -DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR -A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS -PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY. - - GENERAL PUBLIC LICENSE TO COPY - - 1. You may copy and distribute verbatim copies of this source file -as you receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy a valid copyright notice "Copyright -(C) 1985 Free Software Foundation, Inc."; and include following the -copyright notice a verbatim copy of the above disclaimer of warranty -and of this License. You may charge a distribution fee for the -physical act of transferring a copy. - - 2. You may modify your copy or copies of this source file or -any portion of it, and copy and distribute such modifications under -the terms of Paragraph 1 above, provided that you also do the following: - - a) cause the modified files to carry prominent notices stating - that you changed the files and the date of any change; and - - b) cause the whole of any work that you distribute or publish, - that in whole or in part contains or is a derivative of this - program or any part thereof, to be licensed at no charge to all - third parties on terms identical to those contained in this - License Agreement (except that you may choose to grant more extensive - warranty protection to some or all third parties, at your option). - - c) You may charge a distribution fee for the physical act of - transferring a copy, and you may at your option offer warranty - protection in exchange for a fee. - -Mere aggregation of another unrelated program with this program (or its -derivative) on a volume of a storage or distribution medium does not bring -the other program under the scope of these terms. - - 3. You may copy and distribute this program (or a portion or derivative -of it, under Paragraph 2) in object code or executable form under the terms -of Paragraphs 1 and 2 above provided that you also do one of the following: - - a) accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of - Paragraphs 1 and 2 above; or, - - b) accompany it with a written offer, valid for at least three - years, to give any third party free (except for a nominal - shipping charge) a complete machine-readable copy of the - corresponding source code, to be distributed under the terms of - Paragraphs 1 and 2 above; or, - - c) accompany it with the information you received as to where the - corresponding source code may be obtained. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form alone.) - -For an executable file, complete source code means all the source code for -all modules it contains; but, as a special exception, it need not include -source code for modules which are standard libraries that accompany the -operating system on which the executable file runs. - - 4. You may not copy, sublicense, distribute or transfer this program -except as expressly provided under this License Agreement. Any attempt -otherwise to copy, sublicense, distribute or transfer this program is void and -your rights to use the program under this License agreement shall be -automatically terminated. However, parties who have received computer -software programs from you with this License Agreement will not have -their licenses terminated so long as such parties remain in full compliance. - - 5. If you wish to incorporate parts of this program into other free -programs whose distribution conditions are different, write to the Free -Software Foundation at 675 Mass Ave, Cambridge, MA 02139. We have not yet -worked out a simple rule that can be stated here, but we will often permit -this. We will be guided by the two goals of preserving the free status of -all derivatives of our free software and of promoting the sharing and reuse of -software. - - -In other words, you are welcome to use, share and improve this program. -You are forbidden to forbid anyone else to use, share and improve -what you give them. Help stamp out software-hoarding! */ - -#if defined(SHORT_NAMES) || defined(VMS) -#define re_compile_pattern recmppat -#define re_pattern_buffer repatbuf -#define re_registers reregs -#endif - -/* Define number of parens for which we record the beginnings and ends. - This affects how much space the `struct re_registers' type takes up. */ -#ifndef RE_NREGS -#define RE_NREGS 10 -#endif - -/* These bits are used in the obscure_syntax variable to choose among - alternative regexp syntaxes. */ - -/* 1 means plain parentheses serve as grouping, and backslash - parentheses are needed for literal searching. - 0 means backslash-parentheses are grouping, and plain parentheses - are for literal searching. */ -#define RE_NO_BK_PARENS 1 - -/* 1 means plain | serves as the "or"-operator, and \| is a literal. - 0 means \| serves as the "or"-operator, and | is a literal. */ -#define RE_NO_BK_VBAR 2 - -/* 0 means plain + or ? serves as an operator, and \+, \? are literals. - 1 means \+, \? are operators and plain +, ? are literals. */ -#define RE_BK_PLUS_QM 4 - -/* 1 means | binds tighter than ^ or $. - 0 means the contrary. */ -#define RE_TIGHT_VBAR 8 - -/* 1 means treat \n as an _OR operator - 0 means treat it as a normal character */ -#define RE_NEWLINE_OR 16 - -/* 0 means that a special characters (such as *, ^, and $) always have - their special meaning regardless of the surrounding context. - 1 means that special characters may act as normal characters in some - contexts. Specifically, this applies to: - ^ - only special at the beginning, or after ( or | - $ - only special at the end, or before ) or | - *, +, ? - only special when not after the beginning, (, or | */ -#define RE_CONTEXT_INDEP_OPS 32 - -/* Now define combinations of bits for the standard possibilities. */ -#define RE_SYNTAX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_CONTEXT_INDEP_OPS) -#define RE_SYNTAX_EGREP (RE_SYNTAX_AWK | RE_NEWLINE_OR) -#define RE_SYNTAX_GREP (RE_BK_PLUS_QM | RE_NEWLINE_OR) -#define RE_SYNTAX_EMACS 0 - -/* This data structure is used to represent a compiled pattern. */ - -struct re_pattern_buffer - { - char *buffer; /* Space holding the compiled pattern commands. */ - int allocated; /* Size of space that buffer points to */ - int used; /* Length of portion of buffer actually occupied */ - char *fastmap; /* Pointer to fastmap, if any, or zero if none. */ - /* re_search uses the fastmap, if there is one, - to skip quickly over totally implausible characters */ - char *translate; /* Translate table to apply to all characters before comparing. - Or zero for no translation. - The translation is applied to a pattern when it is compiled - and to data when it is matched. */ - char fastmap_accurate; - /* Set to zero when a new pattern is stored, - set to one when the fastmap is updated from it. */ - char can_be_null; /* Set to one by compiling fastmap - if this pattern might match the null string. - It does not necessarily match the null string - in that case, but if this is zero, it cannot. - 2 as value means can match null string - but at end of range or before a character - listed in the fastmap. */ - }; - -/* Structure to store "register" contents data in. - - Pass the address of such a structure as an argument to re_match, etc., - if you want this information back. - - start[i] and end[i] record the string matched by \( ... \) grouping i, - for i from 1 to RE_NREGS - 1. - start[0] and end[0] record the entire string matched. */ - -struct re_registers - { - int start[RE_NREGS]; - int end[RE_NREGS]; - }; - -/* These are the command codes that appear in compiled regular expressions, one per byte. - Some command codes are followed by argument bytes. - A command code can specify any interpretation whatever for its arguments. - Zero-bytes may appear in the compiled regular expression. */ - -enum regexpcode - { - unused, - exactn, /* followed by one byte giving n, and then by n literal bytes */ - begline, /* fails unless at beginning of line */ - endline, /* fails unless at end of line */ - jump, /* followed by two bytes giving relative address to jump to */ - on_failure_jump, /* followed by two bytes giving relative address of place - to resume at in case of failure. */ - finalize_jump, /* Throw away latest failure point and then jump to address. */ - maybe_finalize_jump, /* Like jump but finalize if safe to do so. - This is used to jump back to the beginning - of a repeat. If the command that follows - this jump is clearly incompatible with the - one at the beginning of the repeat, such that - we can be sure that there is no use backtracking - out of repetitions already completed, - then we finalize. */ - dummy_failure_jump, /* jump, and push a dummy failure point. - This failure point will be thrown away - if an attempt is made to use it for a failure. - A + construct makes this before the first repeat. */ - anychar, /* matches any one character */ - charset, /* matches any one char belonging to specified set. - First following byte is # bitmap bytes. - Then come bytes for a bit-map saying which chars are in. - Bits in each byte are ordered low-bit-first. - A character is in the set if its bit is 1. - A character too large to have a bit in the map - is automatically not in the set */ - charset_not, /* similar but match any character that is NOT one of those specified */ - start_memory, /* starts remembering the text that is matched - and stores it in a memory register. - followed by one byte containing the register number. - Register numbers must be in the range 0 through NREGS. */ - stop_memory, /* stops remembering the text that is matched - and stores it in a memory register. - followed by one byte containing the register number. - Register numbers must be in the range 0 through NREGS. */ - duplicate, /* match a duplicate of something remembered. - Followed by one byte containing the index of the memory register. */ - before_dot, /* Succeeds if before dot */ - at_dot, /* Succeeds if at dot */ - after_dot, /* Succeeds if after dot */ - begbuf, /* Succeeds if at beginning of buffer */ - endbuf, /* Succeeds if at end of buffer */ - wordchar, /* Matches any word-constituent character */ - notwordchar, /* Matches any char that is not a word-constituent */ - wordbeg, /* Succeeds if at word beginning */ - wordend, /* Succeeds if at word end */ - wordbound, /* Succeeds if at a word boundary */ - notwordbound, /* Succeeds if not at a word boundary */ - syntaxspec, /* Matches any character whose syntax is specified. - followed by a byte which contains a syntax code, Sword or such like */ - notsyntaxspec /* Matches any character whose syntax differs from the specified. */ - }; - - -extern char *re_compile_pattern (char*, int, struct re_pattern_buffer*); -/* Is this really advertised? */ -extern void re_compile_fastmap (struct re_pattern_buffer*); -extern int re_search(struct re_pattern_buffer*, char*, int, int, - int, struct re_registers*); -extern int re_search_2 (struct re_pattern_buffer*, char*, int, - char*, int, int, int, struct re_registers*, int); -extern int re_match (struct re_pattern_buffer*, char*, int, int, - struct re_registers*); -extern int re_match_2 (struct re_pattern_buffer*, unsigned char*, int, - unsigned char*, int, int, struct re_registers*, int); - -/* 4.2 bsd compatibility (yuck) */ -extern char *re_comp (char*); -extern int re_exec (char*); - -#ifdef SYNTAX_TABLE -extern char *re_syntax_table; -#endif - diff --git a/gnu/lib/libg++/g++-include/setjmp.h b/gnu/lib/libg++/g++-include/setjmp.h new file mode 100644 index 0000000000..dc35dea4e7 --- /dev/null +++ b/gnu/lib/libg++/g++-include/setjmp.h @@ -0,0 +1,29 @@ +#ifndef _setjmp_h + +extern "C" { + +#ifdef __setjmp_h_recursive +#include_next +#else +#define __setjmp_h_recursive +#define setjmp C_header_setjmp +#define longjmp C_header_longjmp + +#ifdef VMS +#include "gnu_cc_include:[000000]setjmp.h" +#else +#include_next +#endif + +#undef setjmp +#undef longjmp + +#define _setjmp_h 1 + +extern int setjmp(jmp_buf); +extern void longjmp(jmp_buf, int); + +#endif +} + +#endif diff --git a/gnu/lib/libg++/g++-include/signal.h b/gnu/lib/libg++/g++-include/signal.h new file mode 100644 index 0000000000..1fe0f1611e --- /dev/null +++ b/gnu/lib/libg++/g++-include/signal.h @@ -0,0 +1,80 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1989 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _signal_h + +#include <_G_config.h> + +extern "C" { + +#ifdef __signal_h_recursive +#include_next +#else + +#define __signal_h_recursive + +#define signal __hide_signal +#define psignal __hide_psignal +#include_next +#undef signal +#undef psignal + +#define _signal_h 1 + +// The Interviews folks call this SignalHandler. Might as well conform. +typedef _G_signal_return_type (*SignalHandler) (...); +typedef int (*SSignalHandler) (...); + +extern SignalHandler signal _G_ARGS((int sig, SignalHandler action)); +//extern SignalHandler sigset _G_ARGS((int sig, SignalHandler action)); +extern SSignalHandler ssignal _G_ARGS((int sig, SSignalHandler action)); +extern int gsignal _G_ARGS((int sig)); +extern int kill _G_ARGS((_G_pid_t pid, int sig)); +#ifndef __386BSD__ +extern int killpg _G_ARGS((short int, int)); +#else +extern int killpg _G_ARGS((_G_pid_t, int)); +#endif +extern int siginterrupt _G_ARGS((int, int)); +extern void psignal _G_ARGS((unsigned, const char*)); + +#ifndef hpux // Interviews folks claim that hpux doesn't like these +extern int sigsetmask _G_ARGS((int mask)); +extern int sigblock _G_ARGS((int mask)); +extern int sigpause _G_ARGS((int mask)); +extern int sigvec _G_ARGS((int, struct sigvec*, struct sigvec*)); +#endif + +// The Interviews version also has these ... + +#define SignalBad ((SignalHandler)-1) +#define SignalDefault ((SignalHandler)0) +#define SignalIgnore ((SignalHandler)1) + +#undef BADSIG +#undef SIG_DFL +#undef SIG_IGN +#define BAD_SIG SignalBad +#define SIG_DFL SignalDefault +#define SIG_IGN SignalIgnore + +#endif +} + +#endif + diff --git a/gnu/lib/libg++/g++-include/std.h b/gnu/lib/libg++/g++-include/std.h deleted file mode 100644 index 52b3063586..0000000000 --- a/gnu/lib/libg++/g++-include/std.h +++ /dev/null @@ -1,4 +0,0 @@ -#include -#include -#include -#include diff --git a/gnu/lib/libg++/g++-include/stdarg.h b/gnu/lib/libg++/g++-include/stdarg.h new file mode 100644 index 0000000000..67dc22e1ab --- /dev/null +++ b/gnu/lib/libg++/g++-include/stdarg.h @@ -0,0 +1,3 @@ +extern "C" { +#include_next +} diff --git a/gnu/lib/libg++/g++-include/stddef.h b/gnu/lib/libg++/g++-include/stddef.h new file mode 100644 index 0000000000..66e78bab20 --- /dev/null +++ b/gnu/lib/libg++/g++-include/stddef.h @@ -0,0 +1,12 @@ +#ifndef __libgxx_stddef_h + +extern "C" { +#ifdef __stddef_h_recursive +#include_next +#else +#include_next + +#define __libgxx_stddef_h 1 +#endif +} +#endif diff --git a/gnu/lib/libg++/g++-include/stdio.h b/gnu/lib/libg++/g++-include/stdio.h new file mode 100644 index 0000000000..7ee1cf2ffa --- /dev/null +++ b/gnu/lib/libg++/g++-include/stdio.h @@ -0,0 +1,180 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _stdio_h +#ifdef __GNUG__ +#pragma interface +#endif + +#ifdef __stdio_h_recursive +#include_next +#else +#define __stdio_h_recursive + +// Note: The #define _stdio_h is at the end of this file, +// in case #include_next finds an installed version of this +// same file -- we want it to continue until it finds the C version. + +#include <_G_config.h> + +extern "C" { + +#undef NULL + +#define fdopen __hide_fdopen +#define fopen __hide_fopen +#define fprintf __hide_fprintf +#define fputs __hide_fputs +#define fread __hide_fread +#define freopen __hide_freopen +#define fscanf __hide_fscanf +#define ftell __hide_ftell +#define fwrite __hide_fwrite +#define new __hide_new /* In case 'new' is used as a parameter name. */ +#define perror __hide_perror +#define popen __hide_popen +#define printf __hide_printf +#define puts __hide_puts +#define putw __hide_putw +#define rewind __hide_rewind +#define tempnam __hide_tempnam +#define scanf __hide_scanf +#define setbuf __hide_setbuf +#define setbuffer __hide_setbuffer +#define setlinebuf __hide_setlinebuf +#define setvbuf __hide_setvbuf +#define sprintf __hide_sprintf +#define sscanf __hide_sscanf +#define tempnam __hide_tempnam +#define vfprintf __hide_vfprintf +#define vprintf __hide_vprintf +#define vsprintf __hide_vsprintf +#define _flsbuf __hide__flsbuf + +#include_next + +#undef fdopen +#undef fopen +#undef fprintf +#undef fputs +#undef fread +#undef freopen +#undef fscanf +#undef ftell +#undef fwrite +#undef new +#undef perror +#undef popen +#undef printf +#undef puts +#undef putw +/* SCO defines remove to call unlink; that's very dangerous for us. */ +#undef remove +#undef rewind +#undef tempnam +#undef scanf +#undef setbuf +#undef setbuffer +#undef setlinebuf +#undef setvbuf +#undef sprintf +#undef sscanf +#undef tempnam +#undef vprintf +#undef vfprintf +#undef vsprintf +#undef _flsbuf + +#ifndef NULL +#define NULL _G_NULL +#endif + +#ifndef size_t +#define size_t _G_size_t +#endif +} + +extern "C" { + +int fclose(FILE*); +FILE* fdopen(int, const char*); +int fflush(FILE*); +int fgetc(FILE*); +#ifndef __386BSD__ +char* fgets _G_ARGS((char*, int, FILE *)); +#else +char* fgets _G_ARGS((char*, _G_size_t, FILE *)); +#endif +FILE* fopen(const char*, const char*); +int fprintf(FILE*, const char* ...); +int fputc(int, FILE*); +int fputs(const char*, FILE*); +size_t fread(void*, size_t, size_t, FILE*); +#ifdef VMS +FILE* freopen(const char*, const char*, FILE* ...); +#else +FILE* freopen(const char*, const char*, FILE*); +#endif +int fscanf(FILE*, const char* ...); +int fseek(FILE*, long, int); +long ftell(FILE *); +size_t fwrite(const void*, size_t, size_t, FILE*); +char* gets(char*); +int getw(FILE*); +int pclose(FILE*); +void perror(const char*); +FILE* popen(const char*, const char*); +int printf(const char* ...); +int puts(const char*); +int putw(int, FILE*); +int rewind(FILE*); +int scanf(const char* ...); +void setbuf(FILE*, char*); +void setbuffer(FILE*, char*, int); +int setlinebuf(FILE*); +int setvbuf(FILE*, char*, int, size_t); +int sscanf(char*, const char* ...); +FILE* tmpfile(); +int ungetc(int, FILE*); +int vfprintf _G_ARGS((FILE*, const char*, _G_va_list)); +int vprintf _G_ARGS((const char*, _G_va_list)); +_G_sprintf_return_type sprintf _G_ARGS((char*, const char* ...)); +_G_sprintf_return_type vsprintf _G_ARGS((char*, const char*, _G_va_list)); + +extern int _filbuf _G_ARGS((FILE*)); +extern int _flsbuf _G_ARGS((unsigned, FILE*)); + +} + +#ifndef L_ctermid +#define L_ctermid 9 +#endif +#ifndef L_cuserid +#define L_cuserid 9 +#endif +#ifndef P_tmpdir +#define P_tmpdir "/tmp/" +#endif +#ifndef L_tmpnam +#define L_tmpnam (sizeof(P_tmpdir) + 15) +#endif + +#define _stdio_h 1 + +#endif +#endif // _stdio_h diff --git a/gnu/lib/libg++/g++-include/stdlib.h b/gnu/lib/libg++/g++-include/stdlib.h new file mode 100644 index 0000000000..b0682435da --- /dev/null +++ b/gnu/lib/libg++/g++-include/stdlib.h @@ -0,0 +1,81 @@ + + +#ifndef _stdlib_h +#define _stdlib_h 1 + +#include <_G_config.h> +#include + +extern "C" { + +int abs(int); + +#ifdef __GNUG__ +void volatile abort(void); +#else +void abort(void); +#endif + +double atof(const char*); +int atoi(const char*); +long atol(const char*); + +int atexit(auto void (*p) (void)); +void* bsearch (const void *, const void *, size_t, + size_t, auto int (*ptf)(const void*, const void*)); +void* calloc(size_t, size_t); +void cfree(void*); + +#ifdef __GNUG__ +void volatile exit(int); +#else +void exit(int); +#endif + +char* fcvt(double, int, int*, int*); +void free(void*); +char* getenv(const char*); +int getopt _G_ARGS((int, char * const *, const char*)); +int getpw(int, char*); +char* gcvt(double, int, char*); +char* ecvt(double, int, int*, int*); +extern char** environ; + +long labs(long); +void* malloc(size_t); +size_t malloc_usable_size(void*); +int putenv(const char*); +extern char* optarg; +extern int opterr; +extern int optind; +void qsort(void*, size_t, size_t, auto int (*ptf)(void*,void*)); +int rand(void); +void* realloc(void*, size_t); +int setkey(const char*); +int srand(unsigned int); +double strtod(const char*, char**); +long strtol(const char*, char**, int); +unsigned long strtoul(const char*, char **, int); +int system(const char*); + +long random(void); +void srandom(int); +char* setstate(char*); +char* initstate(unsigned, char*, int); + +double drand48(void); +void lcong48(short*); +long jrand48(short*); +long lrand48(void); +long mrand48(void); +long nrand48(short*); +short* seed48(short*); +void srand48(long); + +char* ctermid(char*); +char* cuserid(char*); +char* tempnam(const char*, const char*); +char* tmpnam(char*); + +} +#endif diff --git a/gnu/lib/libg++/g++-include/strclass.h b/gnu/lib/libg++/g++-include/strclass.h deleted file mode 100644 index e1799f1ac4..0000000000 --- a/gnu/lib/libg++/g++-include/strclass.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _strclass_h -#ifdef __GNUG__ -#pragma once -#endif -#define _strclass_h -#include -typedef class String string; -#endif diff --git a/gnu/lib/libg++/g++-include/stream.h b/gnu/lib/libg++/g++-include/stream.h deleted file mode 100644 index f55e978734..0000000000 --- a/gnu/lib/libg++/g++-include/stream.h +++ /dev/null @@ -1,13 +0,0 @@ -/* ostream.h and istream.h now separately includable */ - -#ifndef _stream_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _stream_h 1 - -#include -#include - -#endif diff --git a/gnu/lib/libg++/g++-include/streambuf.h b/gnu/lib/libg++/g++-include/streambuf.h deleted file mode 100644 index 61a3de5e9c..0000000000 --- a/gnu/lib/libg++/g++-include/streambuf.h +++ /dev/null @@ -1,165 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifndef _streambuf_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif -#define _streambuf_h 1 - -/* streambufs. - basic streambufs and filebufs are as in Stroustrup, ch 8, - but the base class contains virtual extensions that allow - most capabilities of libg++ Files to be used as streambufs - via `Filebufs'. -*/ - -#include -#include -#include - -// see below for NO_LINE_BUFFER_STREAMBUFS - -#ifndef BUFSIZE -#ifdef BUFSIZ -#define BUFSIZE BUFSIZ -#else -#define BUFSIZE 1024 -#endif -#endif - -enum open_mode // filebuf open modes -{ - input=0, - output=1, - append=2 -}; - -class streambuf -{ -public: - char* base; // start of buffer - char* pptr; // put-pointer (and gptr fence) - char* gptr; // get-pointer - char* eptr; // last valid addr in buffer - - char alloc; // true if we own freestore alloced buffer - - streambuf(); - streambuf(char* buf, int buflen); - - virtual ~streambuf(); - - int doallocate(); - int allocate(); - - - int must_overflow(int ch); // true if should call overflow - - virtual int overflow(int c = EOF); // flush -- return EOF if fail - virtual int underflow(); // fill -- return EOF if fail - - int sgetc(); // get one char (as int) or EOF - int snextc(); // get and advance - void stossc(); // advance only - - int sputbackc(char); // unget - - int sputc(int c = EOF); // write one char - - virtual streambuf* setbuf(char* buf, int buflen, int preloaded_count = 0); - // (not virtual in AT&T) - -// the following aren't in AT&T version: - - int sputs(const char* s); // write null-terminated str - int sputsn(const char* s, int len); // write len bytes - - virtual const char* name(); - - - virtual streambuf* open(const char* name, open_mode m); - virtual streambuf* open(const char* filename, io_mode m, access_mode a); - virtual streambuf* open(const char* filename, const char* m); - virtual streambuf* open(int filedesc, io_mode m); - virtual streambuf* open(FILE* fileptr); - - virtual int is_open(); - virtual int close(); - - virtual void error(); -}; - - -#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES) - - -inline int streambuf::must_overflow(int ch) -{ -#ifndef NO_LINE_BUFFER_STREAMBUF - return pptr >= eptr || ch == '\n'; -#else - return pptr >= eptr; -#endif -} - - -inline int streambuf::allocate() -{ - return (base == 0)? doallocate() : 0; -} - -inline int streambuf::sgetc() -{ - return (gptr >= pptr)? underflow() : int((unsigned char)(*gptr)); -} - - -inline int streambuf::snextc() -{ - ++gptr; - return (gptr >= pptr)? underflow() : int((unsigned char)(*gptr)); -} - - -inline void streambuf::stossc() -{ - if (gptr >= pptr) underflow(); else gptr++; -} - - -inline int streambuf::sputbackc(char ch) -{ - return (gptr > base)? int((unsigned char)(*--gptr = ch)) : EOF; -} - -inline int streambuf::sputc(int ch) -{ - return must_overflow(ch)? overflow(ch) : - int((unsigned char)(*pptr++ = char(ch))); -} - -#endif - -#endif diff --git a/gnu/lib/libg++/g++-include/string.h b/gnu/lib/libg++/g++-include/string.h new file mode 100644 index 0000000000..b11ca0acff --- /dev/null +++ b/gnu/lib/libg++/g++-include/string.h @@ -0,0 +1,45 @@ + +#ifndef _string_h +#define _string_h 1 + +#include <_G_config.h> + +#ifndef size_t +#define size_t _G_size_t +#endif + +#ifndef NULL +#define NULL _G_NULL +#endif + +extern "C" { + +char* strcat(char*, const char*); +char* strchr(const char*, int); +int strcmp(const char*, const char*); +int strcoll(const char*, const char*); +char* strcpy(char*, const char*); +size_t strcspn(const char*, const char*); +char* strdup(const char*); +// NOTE: If you get a error message from g++ that this declaration +// conflicts with a declaration of strlen(), that usually +// indicates that __SIZE_TYPE__ is incorrectly defined by gcc. +// Fix or add SIZE_TYPE in the appropriate file in gcc/config/*.h. +size_t strlen(const char*); +char* strncat(char*, const char*, size_t); +int strncmp(const char*, const char*, size_t); +char* strncpy(char*, const char*, size_t); +char* strpbrk(const char*, const char*); +char* strrchr(const char*, int); +size_t strspn(const char*, const char*); +char* strstr(const char*, const char *); +char* strtok(char*, const char*); +size_t strxfrm(char*, const char*, size_t); + +char* index(const char*, int); +char* rindex(const char*, int); +} + +#include + +#endif diff --git a/gnu/lib/libg++/g++-include/strings.h b/gnu/lib/libg++/g++-include/strings.h new file mode 100644 index 0000000000..3b2f590027 --- /dev/null +++ b/gnu/lib/libg++/g++-include/strings.h @@ -0,0 +1 @@ +#include diff --git a/gnu/lib/libg++/g++-include/sys/dir.h b/gnu/lib/libg++/g++-include/sys/dir.h new file mode 100644 index 0000000000..586867252a --- /dev/null +++ b/gnu/lib/libg++/g++-include/sys/dir.h @@ -0,0 +1,42 @@ +#ifndef __libgxx_sys_dir_h + +extern "C" { + +#ifdef __sys_dir_h_recursive +#include_next +#else +#define __sys_dir_h_recursive +#define opendir __hide_opendir +#define closedir __hide_closedir +#define readdir __hide_readdir +#define telldir __hide_telldir +#define seekdir __hide_seekdir + +#include_next + +#define __libgxx_sys_dir_h +#undef opendir +#undef closedir +#undef readdir +#undef telldir +#undef seekdir + +DIR *opendir(const char *); +int closedir(DIR *); +#ifdef __dirent_h_recursive +// Some operating systems we won't mention (such as the imitation +// of Unix marketed by IBM) implement dirent.h by including sys/dir.h, +// in which case sys/dir.h defines struct dirent, rather than +// the struct direct originally used by BSD. +struct dirent *readdir(DIR *); +#else +struct direct *readdir(DIR *); +#endif +long telldir(DIR *); +void seekdir(DIR *, long); +// We don't bother with rewinddir (many systems define it as a macro). +// void rewinddir(DIR *); +#endif +} + +#endif diff --git a/gnu/lib/libg++/g++-include/sys/fcntl.h b/gnu/lib/libg++/g++-include/sys/fcntl.h new file mode 100644 index 0000000000..d4fdcfedda --- /dev/null +++ b/gnu/lib/libg++/g++-include/sys/fcntl.h @@ -0,0 +1,15 @@ +#ifndef __libgxx_sys_fcntl_h + +extern "C" +{ +#ifdef __sys_fcntl_h_recursive +#include_next +#else +#define __sys_fcntl_h_recursive +#include_next +#define __libgxx_sys_fcntl_h 1 +#endif +} + + +#endif diff --git a/gnu/lib/libg++/g++-include/sys/file.h b/gnu/lib/libg++/g++-include/sys/file.h new file mode 100644 index 0000000000..70b49c244a --- /dev/null +++ b/gnu/lib/libg++/g++-include/sys/file.h @@ -0,0 +1,28 @@ +#ifndef __libgxx_sys_file_h + +extern "C" +{ +#ifdef __sys_file_h_recursive +#include_next +#else +#define fcntl __hide_fcntl +#define open __hide_open +#define creat __hide_creat + +#define __sys_file_h_recursive + +#ifdef VMS +#include "GNU_CC_INCLUDE:[sys]file.h" +#else +#include_next +#endif + +#undef fcntl +#undef open +#undef creat + +#define __libgxx_sys_file_h 1 + +#endif +} +#endif diff --git a/gnu/lib/libg++/g++-include/sys/mman.h b/gnu/lib/libg++/g++-include/sys/mman.h new file mode 100644 index 0000000000..c4b2e15793 --- /dev/null +++ b/gnu/lib/libg++/g++-include/sys/mman.h @@ -0,0 +1,14 @@ +#ifndef __libgxx_sys_mman_h + +extern "C" { +#ifdef __sys_mman_h_recursive +#include_next +#else +#define __sys_mman_h_recursive +#include_next + +#define __libgxx_sys_mman_h 1 +#endif +} + +#endif diff --git a/gnu/lib/libg++/g++-include/sys/param.h b/gnu/lib/libg++/g++-include/sys/param.h new file mode 100644 index 0000000000..610a19b8f4 --- /dev/null +++ b/gnu/lib/libg++/g++-include/sys/param.h @@ -0,0 +1,22 @@ +#ifndef __libgxx_sys_param_h + +extern "C" +{ +#ifdef __sys_param_h_recursive +#include_next +#else +#define __sys_param_h_recursive + +#ifdef VMS +#include "GNU_CC_INCLUDE:[sys]param.h" +#else +#include_next +#endif + +#undef setbit /* Conflicts with Integer::setbit(). */ + +#define __libgxx_sys_param_h 1 +#endif +} + +#endif diff --git a/gnu/lib/libg++/g++-include/sys/resource.h b/gnu/lib/libg++/g++-include/sys/resource.h new file mode 100644 index 0000000000..3596c1b314 --- /dev/null +++ b/gnu/lib/libg++/g++-include/sys/resource.h @@ -0,0 +1,29 @@ +#ifndef __libgxx_sys_resource_h + +extern "C" +{ +#ifdef __sys_resource_h_recursive +#include_next +#else +#include <_G_config.h> +#define __sys_resource_h_recursive +#include + +#ifdef VMS +#include "GNU_CC_INCLUDE:[sys]resource.h" +#else +#include_next +#endif + +#define __libgxx_sys_resource_h 1 + +int getrusage(int, struct rusage*); +int getrlimit (int resource, struct rlimit *rlp); +int setrlimit _G_ARGS((int resource, const struct rlimit *rlp)); +long ulimit(int, long); +int getpriority(int, int); +int setpriority(int, int, int); +#endif +} + +#endif diff --git a/gnu/lib/libg++/g++-include/sys/select.h b/gnu/lib/libg++/g++-include/sys/select.h new file mode 100644 index 0000000000..1c80ffef05 --- /dev/null +++ b/gnu/lib/libg++/g++-include/sys/select.h @@ -0,0 +1,8 @@ +/* Needed by Interviews for AIX. */ +#ifndef __libgxx_sys_select_h +extern "C" +{ +#include_next +#define __libgxx_sys_select_h 1 +} +#endif diff --git a/gnu/lib/libg++/g++-include/sys/signal.h b/gnu/lib/libg++/g++-include/sys/signal.h new file mode 100644 index 0000000000..68212052ce --- /dev/null +++ b/gnu/lib/libg++/g++-include/sys/signal.h @@ -0,0 +1,37 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1989 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* Partly for systems that think signal.h is is sys/ */ +/* But note that some systems that use sys/signal.h to define signal.h. */ + +#ifndef __libgxx_sys_signal_h +#if defined(__sys_signal_h_recursive) || defined(__signal_h_recursive) +#include_next +#else +#define __sys_signal_h_recursive + +extern "C" { +#define signal __hide_signal +#include_next +#undef signal +} + +#define __libgxx_sys_signal_h 1 +#endif +#endif + diff --git a/gnu/lib/libg++/g++-include/sys/socket.h b/gnu/lib/libg++/g++-include/sys/socket.h new file mode 100644 index 0000000000..5ff6018ccb --- /dev/null +++ b/gnu/lib/libg++/g++-include/sys/socket.h @@ -0,0 +1,63 @@ +#ifndef __libgxx_sys_socket_h + +#include <_G_config.h> + +extern "C" +{ +#ifdef __sys_socket_h_recursive +#include_next +#else +#define __sys_socket_h_recursive +#include + +#ifdef VMS +#include "GNU_CC_INCLUDE:[sys]socket.h" +#else +#include_next +#endif + +#define __libgxx_sys_socket_h 1 + +// void* in select, since different systems use int* or fd_set* +int accept _G_ARGS((int, struct sockaddr*, int*)); +#ifndef __386BSD__ +int select _G_ARGS((int, void*, void*, void*, struct timeval*)); + +int bind _G_ARGS((int, const void*, int)); +int connect _G_ARGS((int, struct sockaddr*, int)); +#else +int select _G_ARGS((int, struct fd_set*, struct fd_set*, struct fd_set*, struct timeval*)); + +int bind _G_ARGS((int, const struct sockaddr *, int)); +int connect _G_ARGS((int, const struct sockaddr*, int)); +#endif +int getsockname _G_ARGS((int, struct sockaddr*, int*)); +int getpeername _G_ARGS((int, struct sockaddr*, int*)); +int getsockopt(int, int, int, void*, int*); +int listen(int, int); +#ifndef hpux +int rcmd _G_ARGS((char**, int, const char*, const char*, const char*, int*)); +#endif +int recv(int, void*, int, int); +int recvmsg(int, struct msghdr*, int); +int rexec(char**, int, const char*, const char*, const char*, int*); +int rresvport(int*); +int send _G_ARGS((int, const void*, int, int)); +int sendmsg _G_ARGS((int, const struct msghdr*, int)); +int shutdown(int, int); +int socket(int, int, int); +int socketpair(int, int, int, int sv[2]); + +#ifndef __386BSD__ +int recvfrom _G_ARGS((int, void*, int, int, void*, int *)); +int sendto _G_ARGS((int, const void*, int, int, void*, int)); +int setsockopt _G_ARGS((int, int, int, const char*, int)); +#else +int recvfrom _G_ARGS((int, void*, int, int, struct sockaddr*, int *)); +int sendto _G_ARGS((int, const void*, int, int, const struct sockaddr*, int)); +int setsockopt _G_ARGS((int, int, int, const void*, int)); +#endif +#endif +} + +#endif diff --git a/gnu/lib/libg++/g++-include/sys/stat.h b/gnu/lib/libg++/g++-include/sys/stat.h new file mode 100644 index 0000000000..cffe2a5e1d --- /dev/null +++ b/gnu/lib/libg++/g++-include/sys/stat.h @@ -0,0 +1,47 @@ +#ifndef __libgxx_sys_stat_h + +extern "C" +{ +#ifdef __sys_stat_h_recursive +#include_next +#else +#define __sys_stat_h_recursive +#include <_G_config.h> +#define chmod __hide_chmod +#ifdef VMS +#include "GNU_CC_INCLUDE:[sys]stat.h" +#else +#include_next +#endif +#undef chmod + +#define __libgxx_sys_stat_h 1 + +extern int chmod _G_ARGS((const char*, _G_mode_t)); +extern int stat _G_ARGS((const char *path, struct stat *buf)); +extern int lstat _G_ARGS((const char *path, struct stat *buf)); +extern int fstat _G_ARGS((int fd, struct stat *buf)); + +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#ifndef S_ISBLK +#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif +#ifndef S_ISCHR +#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#ifndef S_ISFIFO +#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFFIFO) +#endif +#ifndef S_ISREG +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) && defined(S_IFLNK) +#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif + +#endif +} + +#endif diff --git a/gnu/lib/libg++/g++-include/sys/time.h b/gnu/lib/libg++/g++-include/sys/time.h new file mode 100644 index 0000000000..81419b8c48 --- /dev/null +++ b/gnu/lib/libg++/g++-include/sys/time.h @@ -0,0 +1,41 @@ +#ifndef __libgxx_sys_time_h + +extern "C" +{ +#ifdef __sys_time_h_recursive +#include_next +#else +#define __sys_time_h_recursive + +// Some system (e.g Ultrix) define these in sys/time.h. + +#ifndef __time_h_recursive +#define time __hide_time +#define clock __hide_clock +#define gmtime __hide_gmtime +#define localtime __hide_localtime +#define ctime __hide_ctime +#define asctime __hide_asctime +#define strftime __hide_strftime +#endif + +#ifdef VMS +#include "GNU_CC_INCLUDE:[sys]resourcetime.h" +#else +#include_next +#endif +#undef __sys_time_h_recursive + +#define __libgxx_sys_time_h 1 + +#undef clock +#undef ctime +#undef gmtime +#undef localtime +#undef time +#undef asctime +#undef strftime +#endif +} + +#endif diff --git a/gnu/lib/libg++/g++-include/sys/times.h b/gnu/lib/libg++/g++-include/sys/times.h new file mode 100644 index 0000000000..d026ca5258 --- /dev/null +++ b/gnu/lib/libg++/g++-include/sys/times.h @@ -0,0 +1,20 @@ +#ifndef __libgxx_sys_times_h + +extern "C" +{ +#ifdef __sys_times_h_recursive +#include_next +#else +#define __sys_times_h_recursive +#include_next +#define __libgxx_sys_times_h 1 + +#include <_G_config.h> + +extern _G_clock_t times _G_ARGS((struct tms*)); + +#endif +} + + +#endif diff --git a/gnu/lib/libg++/g++-include/sys/types.h b/gnu/lib/libg++/g++-include/sys/types.h new file mode 100644 index 0000000000..67f4d316b5 --- /dev/null +++ b/gnu/lib/libg++/g++-include/sys/types.h @@ -0,0 +1,25 @@ +#ifndef __libgxx_sys_types_h + +extern "C" +{ +#ifdef __sys_types_h_recursive +#include_next +#else + +#define __sys_types_h_recursive +#include + +#ifdef VMS +#include "GNU_CC_INCLUDE:[sys]types.h" +#else +#include_next +#endif + +#define __libgxx_sys_types_h 1 + +#endif +} + + +#endif + diff --git a/gnu/lib/libg++/g++-include/sys/wait.h b/gnu/lib/libg++/g++-include/sys/wait.h new file mode 100644 index 0000000000..e6e4e31924 --- /dev/null +++ b/gnu/lib/libg++/g++-include/sys/wait.h @@ -0,0 +1,42 @@ +#ifndef __libgxx_sys_wait_h + +#include <_G_config.h> + +extern "C" { +#ifdef __sys_wait_h_recursive +#include_next +#else +#define __sys_wait_h_recursive + + +#if _G_HAVE_SYS_WAIT +#ifdef VMS +#include "GNU_CC_INCLUDE:[sys]wait.h" +#else +#include_next +#endif +#else /* !_G_HAVE_SYS_WAIT */ +/* Traditional definitions. */ +#define WEXITSTATUS(status) (((x) >> 8) & 0xFF) +#define WIFSTOPPED(x) (((x) & 0xFF) == 0177) +#define WIFEXITED(x) (! WIFSTOPPED(x) && WTERMSIG(x) == 0) +#define WIFSIGNALED(x) (! WIFSTOPPED(x) && WTERMSIG(x) != 0) +#define WTERMSIG(status) ((x) & 0x7F) +#define WSTOPSIG(status) (((x) >> 8) & 0xFF) +#endif /* !_G_HAVE_SYS_WAIT */ + +#define __libgxx_sys_wait_h 1 + +struct rusage; +extern _G_pid_t wait _G_ARGS((int*)); +extern _G_pid_t waitpid _G_ARGS((_G_pid_t, int*, int)); +extern _G_pid_t wait3 _G_ARGS((int*, int options, struct rusage*)); +#ifndef __386BSD__ +extern _G_pid_t wait4 _G_ARGS((int, int*, int, struct rusage*)); +#else +extern _G_pid_t wait4 _G_ARGS((_G_pid_t, int*, int, struct rusage*)); +#endif +#endif +} + +#endif diff --git a/gnu/lib/libg++/g++-include/time.h b/gnu/lib/libg++/g++-include/time.h new file mode 100644 index 0000000000..ea9c00f4a4 --- /dev/null +++ b/gnu/lib/libg++/g++-include/time.h @@ -0,0 +1,108 @@ +#ifndef _G_time_h +#define _G_time_h + +extern "C" { + +#ifdef __time_h_recursive +#include_next +#else +#define __time_h_recursive + +#include <_G_config.h> + +// A clean way to use and/or define time_t might allow removal of this crud. +#ifndef __sys_time_h_recursive +#define time __hide_time +#define clock __hide_clock +#define difftime __hide_difftime +#define gmtime __hide_gmtime +#define localtime __hide_localtime +#define ctime __hide_ctime +#define asctime __hide_asctime +#define strftime __hide_strftime +#endif +#define mktime __hide_mktime +#define tzset __hide_tzset +#define tzsetwall __hide_tzsetwall +#define getitimer __hide_getitimer +#define setitimer __hide_setitimer +#define gettimeofday __hide_gettimeofday +#define settimeofday __hide_settimeofday + +#ifdef VMS + struct unix_time + { + long int tv_sec; + long int tv_usec; + }; + + struct rusage + { + struct unix_time ru_utime; + }; + +#define RUSAGE_SELF 0 //define it, it will be unused +#else +#ifdef hpux +#define _INCLUDE_POSIX_SOURCE +#endif + +#include_next +#endif +#undef __time_h_recursive + +#define time_h 1 + +#undef time +#undef clock +#undef difftime +#undef gmtime +#undef localtime +#undef asctime +#undef ctime +#undef mktime +#undef strftime +#undef tzset +#undef tzsetwall +#undef getitimer +#undef setitimer +#undef gettimeofday +#undef settimeofday + +extern char* asctime _G_ARGS((const struct tm*)); +extern char* ctime _G_ARGS((const _G_time_t*)); +double difftime _G_ARGS((_G_time_t, _G_time_t)); +extern struct tm* gmtime _G_ARGS((const _G_time_t*)); +extern struct tm* localtime _G_ARGS((const _G_time_t*)); +extern _G_time_t mktime(struct tm*); +extern _G_size_t strftime _G_ARGS((char*,_G_size_t,const char*,const struct tm*)); +extern void tzset(); +extern void tzsetwall(); + +extern int getitimer(int, struct itimerval*); +extern int setitimer _G_ARGS((int, const struct itimerval*,struct itimerval*)); +extern int gettimeofday(struct timeval*, struct timezone*); +extern int settimeofday _G_ARGS((const struct timeval*,const struct timezone*)); +extern int stime _G_ARGS((const _G_time_t*)); +extern int dysize(int); + +#if defined(___AIX__) +int clock (void); +#elif defined(hpux) +unsigned long clock(void); +#else +long clock(void); +#endif +_G_time_t time(_G_time_t*); +unsigned ualarm(unsigned, unsigned); +#ifndef __386BSD__ +unsigned usleep(unsigned); +void profil _G_ARGS((unsigned short*, _G_size_t, unsigned int, unsigned)); +#else +void usleep(unsigned); +int profil _G_ARGS((char*, int, int, int)); +#endif + +#endif +} +#endif diff --git a/gnu/lib/libg++/g++-include/unistd.h b/gnu/lib/libg++/g++-include/unistd.h new file mode 100644 index 0000000000..bd7a93529a --- /dev/null +++ b/gnu/lib/libg++/g++-include/unistd.h @@ -0,0 +1,187 @@ +#ifndef _G_unistd_h +#define _G_unistd_h 1 + +#include <_G_config.h> + +extern "C" { + +#if _G_HAVE_UNISTD +#ifndef _G_USE_PROTOS +#define chmod __hide_chmod +#define chown __hide_chown +#define execl __hide_execl +#define execlp __hide_execlp +#define execle __hide_execle +#define fchown __hide_fchown +#define ioctl __hide_ioctl +#define setgid __hide_setgid +#define setuid __hide_setuid +#endif +#ifdef _AIX +// AIX's unistd.h defines int rename (const char *old, const char *new). +// This is not legal ANSI. It causes a C++ syntax error (because of 'new'). +#define new __new +#endif +#include_next +#ifdef _AIX +#undef new +#endif +#ifndef _G_USE_PROTOS +#undef chmod +#undef chown +#undef execl +#undef execle +#undef execlp +#undef fchown +#undef ioctl +#undef setgid +#undef setuid +#endif +#else +#ifndef SEEK_SET +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 +#endif + +#ifndef F_OK +#define F_OK 0 +#endif +#ifndef X_OK +#define X_OK 1 +#endif +#ifndef W_OK +#define W_OK 2 +#endif +#ifndef R_OK +#define R_OK 4 +#endif +#endif + +#ifdef __GNUG__ +extern void volatile _exit(int); +#else +void _exit(int); +#endif + +extern unsigned alarm _G_ARGS((unsigned)); +#ifndef __386BSD__ +extern int brk _G_ARGS((void*)); +#else +extern char* brk _G_ARGS((const char*)); +#endif +extern int chdir _G_ARGS((const char*)); +extern int chmod _G_ARGS((const char*, _G_mode_t)); +extern int chown (const char*, _G_uid_t, _G_gid_t); +extern int close _G_ARGS((int)); +extern char* crypt _G_ARGS((const char*, const char*)); +extern int dup _G_ARGS((int)); +extern int dup2 _G_ARGS((int, int)); +#ifndef __386BSD__ +extern void encrypt _G_ARGS((char*, int)); +#else +extern int encrypt _G_ARGS((char*, int)); +#endif +extern int execl (const char*, const char *, ...); +extern int execle (const char*, const char *, ...); +extern int execlp (const char*, const char*, ...); +#ifndef __386BSD__ +extern int exect _G_ARGS((const char*, const char**, char**)); +extern int execv _G_ARGS((const char*, const char * const *)); +extern int execve _G_ARGS((const char*, const char * const *, const char * const *)); +extern int execvp _G_ARGS((const char*, const char * const *)); +extern int fchown (int, _G_uid_t, _G_gid_t); +#else +extern int exect _G_ARGS((const char*, char * const*, char * const *)); +extern int execv _G_ARGS((const char*, char * const *)); +extern int execve _G_ARGS((const char*, char * const *, char * const *)); +extern int execvp _G_ARGS((const char*, char * const *)); +extern int fchown (int, int, int); +#endif +extern _G_pid_t fork _G_ARGS((void)); +extern int fsync _G_ARGS((int)); +extern int ftruncate _G_ARGS((int, _G_off_t)); +extern char* getcwd _G_ARGS((char*, _G_size_t)); +extern int getdomainname _G_ARGS((char*, int)); +extern int getdtablesize _G_ARGS((void)); +#ifndef __386BSD__ +extern int getgroups _G_ARGS((int, _G_gid_t*)); +#else +extern int getgroups _G_ARGS((int, int*)); +#endif +extern _G_uid_t geteuid _G_ARGS((void)); +extern _G_gid_t getegid _G_ARGS((void)); +extern _G_gid_t getgid _G_ARGS((void)); +extern long gethostid _G_ARGS((void)); +extern int gethostname _G_ARGS((char*, int)); +extern _G_pid_t getpgrp _G_ARGS((...)); +extern _G_pid_t getpid _G_ARGS((void)); +extern _G_pid_t getppid _G_ARGS((void)); +extern char* getlogin _G_ARGS((void)); +extern char* getpass _G_ARGS((const char*)); +extern _G_uid_t getuid _G_ARGS((void)); +#ifndef __386BSD__ +extern int ioctl (int, int, ... ); +#else +extern int ioctl (int, unsigned long, ... ); +#endif +extern int isatty _G_ARGS((int)); +extern int link _G_ARGS((const char*, const char*)); +extern int lockf _G_ARGS((int, int, long)); +extern int mkstemp _G_ARGS((char*)); +extern char* mktemp _G_ARGS((char*)); +extern int nice _G_ARGS((int)); +extern int pause _G_ARGS((void)); +extern int pipe _G_ARGS((int*)); +extern int readlink _G_ARGS((const char*, char*, int)); +extern int rename _G_ARGS((const char*, const char*)); +extern int rmdir _G_ARGS((const char*)); +#if defined( __OSF1__ ) || defined (__386BSD__) +extern char* sbrk _G_ARGS((int)); +#else +extern void* sbrk _G_ARGS((int)); +#endif +extern int syscall _G_ARGS((int, ...)); +extern int setgid (_G_gid_t); +extern int sethostname _G_ARGS((const char*, int)); +#ifdef _G_SYSV +extern _G_pid_t setpgrp _G_ARGS((void)); +extern _G_pid_t setsid _G_ARGS((void)); +#else +#ifndef __386BSD__ +extern _G_pid_t setpgrp _G_ARGS((_G_pid_t, _G_pid_t)); +#else +extern _G_pid_t setsid _G_ARGS((void)); +extern int setpgrp _G_ARGS((_G_pid_t, _G_pid_t)); +#endif +#endif +extern int setregid _G_ARGS((int, int)); +extern int setreuid _G_ARGS((int, int)); +extern int setuid (_G_uid_t); +extern unsigned sleep _G_ARGS((unsigned)); +extern void swab _G_ARGS((void*, void*, int)); +extern int symlink _G_ARGS((const char*, const char*)); +extern long sysconf _G_ARGS((int)); +extern int truncate _G_ARGS((const char*, _G_off_t)); +extern char* ttyname _G_ARGS((int)); +extern int ttyslot _G_ARGS((void)); +//extern int umask _G_ARGS((int)); /* commented out for now; wrong for SunOs4.1 */ +extern int unlink _G_ARGS((const char*)); +#ifndef __386BSD__ +extern _G_pid_t vfork _G_ARGS((void)); +#else +extern int vfork _G_ARGS((void)); +#endif +extern int vadvise _G_ARGS((int)); +extern int vhangup _G_ARGS((void)); +extern _G_off_t lseek _G_ARGS((int, long, int)); +extern _G_ssize_t read _G_ARGS((int, void*, _G_size_t)); +extern _G_ssize_t write _G_ARGS((int, const void*, _G_size_t)); +extern int access _G_ARGS((const char*, int)); +#ifndef hpux +extern int flock _G_ARGS((int, int)); +#endif + +} + +#endif diff --git a/gnu/lib/libg++/g++-include/values.h b/gnu/lib/libg++/g++-include/values.h index 9b6bb5c5c4..56f8a94155 100644 --- a/gnu/lib/libg++/g++-include/values.h +++ b/gnu/lib/libg++/g++-include/values.h @@ -3,30 +3,21 @@ Copyright (C) 1988 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _values_h -#ifdef __GNUG__ -#pragma once -#pragma interface -#endif #define _values_h 1 #define BITSPERBYTE 8 @@ -51,7 +42,7 @@ and this notice must be preserved on all copies. #define HIBITS MINSHORT #define HIBITL MINLONG -#if defined(sun) || defined(hp300) || defined(hpux) || defined(masscomp) +#if defined(sun) || defined(hp300) || defined(hpux) || defined(masscomp) || defined(sgi) #ifdef masscomp #define MAXDOUBLE \ ({ \ diff --git a/gnu/lib/libg++/genclass/Makefile b/gnu/lib/libg++/genclass/Makefile index a7a4975025..a5b92ef745 100644 --- a/gnu/lib/libg++/genclass/Makefile +++ b/gnu/lib/libg++/genclass/Makefile @@ -1,18 +1,11 @@ -# %W% (Berkeley) %G% +# Makefile for g++ library genclass test -BINDIR= /usr/bin +NOMAN= noman -MAN1= genclass.0 - -all genclass: ${MAN1} - -clean depend lint tags: - -cleandir: - rm -f ${MAN1} - -beforeinstall: - install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ - ${.CURDIR}/genclass.sh ${DESTDIR}${BINDIR}/genclass +afterinstall: + sed -e '/^PROTODIR/s_PROTODIR.*_PROTODIR=${DESTDIR}/usr/include/g++/gen_;' $(.CURDIR)/genclass.sh > $(DESTDIR)$(BINDIR)/genclass + chown ${BINOWN}.${BINGRP} $(DESTDIR)$(BINDIR)/genclass + chmod ${BINMODE} $(DESTDIR)$(BINDIR)/genclass .include +.include "../../../usr.bin/Makefile.inc" diff --git a/gnu/lib/libg++/genclass/genclass.1 b/gnu/lib/libg++/genclass/genclass.1 deleted file mode 100644 index ed106c320c..0000000000 --- a/gnu/lib/libg++/genclass/genclass.1 +++ /dev/null @@ -1 +0,0 @@ -.\" NEED A MAN PAGE! diff --git a/gnu/lib/libg++/genclass/genclass.sh b/gnu/lib/libg++/genclass/genclass.sh index 96fc81d1cf..08d510754a 100644 --- a/gnu/lib/libg++/genclass/genclass.sh +++ b/gnu/lib/libg++/genclass/genclass.sh @@ -1,93 +1,452 @@ #!/bin/sh -# shell script for generating classes from prototypes + +# Copyright (C) 1989 Free Software Foundation, Inc. +# +# genclass program enhanced by Wendell C. Baker +# (original by Doug Lea (dl@rocky.oswego.edu)) + +#This file is part of GNU libg++. + +#GNU libg++ is free software; you can redistribute it and/or modify +#it under the terms of the GNU General Public License as published by +#the Free Software Foundation; either version 1, or (at your option) +#any later version. + +#GNU libg++ is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. + +#You should have received a copy of the GNU General Public License +#along with GNU libg++; see the file COPYING. If not, write to +#the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + +# +# genclass -list [proto ...] +# genclass -catalog [proto ...] +# genclass type1 {ref|val} proto [out_prefix] +# genclass -2 type1 {ref|val} type2 {ref, val} proto [out_prefix] +# +# Generate classes from prototypes # -# usage: genclass [-2] type1 {ref, val} [type2 {ref, val}] proto [out-prefix] +name=genclass ; +usage=" + $name -list [proto ...] + $name -catalog [proto ...] + $name type1 {ref|val} proto [out_prefix] + $name -2 type1 {ref|val} type2 {ref|val} proto [out_prefix]" ; + +case "$1" in +-usage) + # + # -usage + # + echo "usage: $usage" 1>&2 ; + exit 0; + ;; +-version) + # + # -version + # + # is substituted by the build process. + # We currently use the libg++ version number (extracted from ../Makefile). + echo "$name: version " ; + exit 0; + ;; +-requires) + # + # -requires + # + # The following line should contain any nonstandard programs + # which must be in the users's path (i.e. not referenced by a + # fullpath);it allows one to check a script for dependencies + # without exhaustively testing its usages. + # ... in this case genclass depends on nothing else. + echo ; + exit 0; + ;; +esac ; + +# pull it in from the environment +[ "$TRACE" = "" ] || set -xv +# Search in standard g++ prototype directory and in the current directory +# NOTE: this variable is edited by the install process +PROTODIR=/projects/gnu-cygnus/gnu-cygnus-2/mips/lib/g++-include/gen -# search in standard g++ prototype directory & in current +pwd=`pwd` -PROTODIR=${PROTODIR-/usr/local/lib/g++-include/gen} -CURRENTDIR=`pwd` +case "$1" in +-catalog*|-list*) + # + # genclass -catalog [proto ...] + # genclass -list [proto ...] + # + option="$1" ; + shift ; -N="1" -T2="" -T2ACC="" + case $# in + 0) + # + # -catalog + # -list + # + select=all ; + select_pattern=p ; + ;; + *) + # + # -catalog proto ... + # -list proto ... + # + select="$@" ; + select_pattern= ; + for i in $@ ; do + select_pattern="\ +$select_pattern +/.*$i\$/ p +" ; + done ; -case $1 in - -2) N="2"; shift;; - *) ;; + ;; + esac ; + + # + # select_pattern is now a (possibly-vacuous) newline- + # separated list of patterns of the form: + # + # /.*Proto1$/ p + # /.*Proto2$/ p + # /.*Proto3$/ p + # + # or select_pattern is simply ``p'' to select everything + + # Hmmm... not all systems have a fmt program; should we + # just go ahead and use ``nroff -Tcrt | cat -s'' here? + fmt='nroff -Tcrt | cat -s' + #fmt=fmt ; + + case "$option" in + -catalog*) + # + # -catalog [proto ...] + # + echo "\ +Catalog of ${name} class templates +directories searched: + $PROTODIR + $pwd +selecting: $select +classes available:" ; + ;; + -list*) + # + # -list [proto ...] + # + # no need to do anything (the list is coming out next) + ;; + esac ; + +# The sed script does the following: +# - If it does not end in a .ccP or .hP then +# it's not a template and we are not intereseted. +# - Get rid of pathname components [s;.*/;;] +# - Just take the template names +# - change quoting conventions and select off what we want to see +# -if it did not pass the patterns, kill it + + ls $pwd $PROTODIR | sed -e ' +/\.ccP$/ !{ + /\.hP$/ !{ + d + } +} +s;.*/;; +s/\.ccP$// +s/\.hP$// +' -e "$select_pattern +d +" | sort -u | case "$option" in + -catalog*) + # The library catalog information preceded the list + # format the list, and tab in in a bit to make it readable. + # Re-evaluate $fmt because it might contain a shell command + eval $fmt | sed -e 's/.*/ &/' ; + ;; + -list*) + # nothing special, just let the sorted list dribble out + # we must use cat to receive (and reproduce) the incoming list + cat ; + ;; + esac ; + exit 0; + ;; +-2) + # + # genclass -2 type1 {ref|val} type2 {ref|val} proto [out_prefix] + # + N=2 ; + + case $# in + 6) # genclass -2 type1 {ref|val} type2 {ref|val} proto + ;; + 7) # genclass -2 type1 {ref|val} type2 {ref|val} proto out_prefix + ;; + *) + echo "usage: $usage" 1>&2 ; + exit 1; + esac ; + shift ; + ;; +*) + # + # genclass type1 {ref|val} proto [out_prefix] + # + N=1 ; + + case $# in + 3) # genclass type1 {ref|val} proto + ;; + 4) # genclass type1 {ref|val} proto out_prefix + ;; + *) + echo "usage: $usage" 1>&2 ; + exit 1; + esac ; + ;; esac -T1=$1; -T1NAME=$T1.; -T1SEDNAME=$T1; +# +# Args are now (the point being the leading ``-2'' is gone) +# +# type1 {ref|val} proto [out_prefix] +# type1 {ref|val} type2 {ref|val} proto [out_prefix] +# -case $2 in - ref) T1ACC="\&";; - val) T1ACC=" ";; - *) echo "Must specify type1 access: ref or val"; exit 1;; +# +# Quote all of the $1 $2 etc references to guard against +# dynamic syntax errors due to vacuous arguments (i.e. '') +# as sometimes occurs when genclass is used from a Makefile +# + +T1="$1"; +T1NAME="$T1." ; +T1SEDNAME="$T1" ; + +case "$2" in +ref) + T1ACC="\&" ; + ;; +val) + T1ACC=" " ; + ;; +*) + echo "${name}: Must specify type1 access as ref or val" 1>&2 ; + echo "usage: $usage" 1>&2 ; + exit 1; + ;; esac +# N is either 1 or 2 + case $N in - 2) T2=$3; - T2NAME=$T2.; T2SEDNAME=$T2; - case $4 in - ref) T2ACC="\&";; - val) T2ACC=" ";; - *) echo "Must specify type2 access: ref or val"; exit 1;; - esac; - CLASS=$5;; - *) CLASS=$3;; -esac +1) + # + # type1 {ref|val} proto [out_prefix] + # + class="$3" ; -REPLACEPREFIX="N" -DFLTPREFIX=$T1NAME$T2NAME + T2="" ; + T2ACC="" ; + ;; +2) + # + # type1 {ref|val} type2 {ref|val} proto [out_prefix] + # + class="$5" ; -case $# in - 3) PREFIX=$DFLTPREFIX;; - 5) PREFIX=$DFLTPREFIX;; - 4) PREFIX=$4; REPLACEPREFIX="Y";; - 6) PREFIX=$6; REPLACEPREFIX="Y";; - *) echo "bad arguments"; exit 1 ;; + T2="$3"; + T2NAME="$T2." ; + T2SEDNAME="$T2" ; + + case "$4" in + ref) + T2ACC="\&" ; + ;; + val) + T2ACC=" " ; + ;; + *) + echo "${name}: Must specify type2 access: ref or val" 1>&2 ; + echo "usage: $usage" 1>&2 ; + exit 1;; + esac; + ;; esac -HSRC=$CLASS.hP -CCSRC=$CLASS.ccP -HOUT=$PREFIX$CLASS.h; -CCOUT=$PREFIX$CLASS.cc ; +defaultprefix="$T1NAME$T2NAME" ; +case $# in +3) # type1 {ref|val} proto + replaceprefix="N" ; + prefix="$defaultprefix" ; + ;; +5) # type1 {ref|val} type2 {ref|val} proto + replaceprefix="N" ; + prefix="$defaultprefix" ; + ;; +4) # type1 {ref|val} proto out_prefix + prefix="$4" ; + replaceprefix="Y" ; + ;; +6) # type1 {ref|val} type2 {ref|val} proto out_prefix + prefix="$6" ; + replaceprefix="Y" ; + ;; +*) + echo "${name}: too many arguments" 1>&2 ; + echo "usage: $usage" 1>&2 ; + exit 1; + ;; +esac ; -# .h and .cc parts done separately in case only a .h +src_h=$class.hP +src_cc=$class.ccP +out_h=$prefix$class.h; +out_cc=$prefix$class.cc ; +# +# Note #1: The .h and .cc parts are done separately +# in case only a .h exists for the prototype +# +# Note #2: Bind the .h and .cc parts to the fullpath +# directories at the same time to ensure consistency. +# -if test -f $CURRENTDIR/$HSRC -then HSRC=$CURRENTDIR/$HSRC -elif test -f $PROTODIR/$HSRC -then HSRC=$PROTODIR/$HSRC -else echo "genclass: $HSRC: no such file"; exit 1; +if [ -f $pwd/$src_h ] ; then + fullsrc_h=$pwd/$src_h ; + fullsrc_cc=$pwd/$src_cc ; +elif [ -f $PROTODIR/$src_h ] ; then + fullsrc_h=$PROTODIR/$src_h ; + fullsrc_cc=$PROTODIR/$src_cc ; +else + echo "${name}: there is no prototype for class $class - file $src_h" 1>&2 ; + $0 -list ; + exit 1; fi -CASES=$N$REPLACEPREFIX +CASES="$N$replaceprefix" ; +# CASES is one of { 2Y 2N 1Y 1N } + +# +# WATCHOUT - we have no way of checking whether or not +# the proper case type is being used with the prototype. +# +# For example, we have no way of ensuring that any of +# Map variants are specified with the -2 argument set +# Further, we have no way of ensuring that -2 is not +# used with the prototypes which require only one. +# +# The second problem is not serious because it still +# results in correctly-generated C++ code; the first +# problem is serious because it results in C++ code that +# still has ``'' and ``'' syntax inside it. Such +# code of course will not compile. +# +# SO THE BEST WE CAN DO - is check for the presence of +# and AFTER the thing has been generated. +# case $CASES in - 2Y) sed < $HSRC > $HOUT -e "s//$T1/g" -e "s//$T1$T1ACC/g" -e "s//$T2/g" -e "s//$T2$T2ACC/g" -e "s/$T1SEDNAME\.$T2SEDNAME\./$PREFIX/g" -e "s/$T1SEDNAME\./$PREFIX/g" -e "s/$T2SEDNAME\./$PREFIX/g" ;; - 2N) sed < $HSRC > $HOUT -e "s//$T1/g" -e "s//$T1$T1ACC/g" -e "s//$T2/g" -e "s//$T2$T2ACC/g" ;; - 1Y) sed < $HSRC > $HOUT -e "s//$T1/g" -e "s//$T1$T1ACC/g" -e "s/$T1SEDNAME\./$PREFIX/g" ;; - *) sed < $HSRC > $HOUT -e "s//$T1/g" -e "s//$T1$T1ACC/g";; +2Y) # Two output substitutions, change the prefix + sed < $fullsrc_h > $out_h -e " +s//$T1/g +s//$T1$T1ACC/g +s//$T2/g +s//$T2$T2ACC/g +s/$T1SEDNAME\.$T2SEDNAME\./$prefix/g +s/$T1SEDNAME\./$prefix/g +s/$T2SEDNAME\./$prefix/g +" ; + ;; +2N) # Two output substitutions, use the default prefix + sed < $fullsrc_h > $out_h -e " +s//$T1/g +s//$T1$T1ACC/g +s//$T2/g +s//$T2$T2ACC/g +" ; + ;; +1Y) # One output substitution, change the prefix + sed < $fullsrc_h > $out_h -e " +s//$T1/g +s//$T1$T1ACC/g +s/$T1SEDNAME\./$prefix/g +" ; + ;; +1N) # One output substitution, use the default prefix + sed < $fullsrc_h > $out_h -e " +s//$T1/g +s//$T1$T1ACC/g +" ; + ;; esac -if test -f $CURRENTDIR/$CCSRC -then CCSRC=$CURRENTDIR/$CCSRC -elif test -f $PROTODIR/$CCSRC -then CCSRC=$PROTODIR/$CCSRC -else echo "genclass warning: class has a .h but no .cc file"; exit 0; +if egrep '' $out_h > /dev/null ; then + echo "${name}: the $class class requires the -2 syntax for the 2nd type" 1>&2 ; + echo "usage: $usage" 1>&2 ; + # the user does not get to see the mistakes (he might try to compile it) + rm $out_h ; + exit 1; +fi ; + +if [ ! -f $fullsrc_cc ] ; then + echo "${name}: warning, class has a .h but no .cc file" 1>&2 ; + exit 0; fi case $CASES in - 2Y) sed < $CCSRC > $CCOUT -e "s//$T1/g" -e "s//$T1$T1ACC/g" -e "s//$T2/g" -e "s//$T2$T2ACC/g" -e "s/$T1SEDNAME\.$T2SEDNAME\./$PREFIX/g" -e "s/$T1SEDNAME\./$PREFIX/g" -e "s/$T2SEDNAME\./$PREFIX/g" ;; - 2N) sed < $CCSRC > $CCOUT -e "s//$T1/g" -e "s//$T1$T1ACC/g" -e "s//$T2/g" -e "s//$T2$T2ACC/g" ;; - 1Y) sed < $CCSRC > $CCOUT -e "s//$T1/g" -e "s//$T1$T1ACC/g" -e "s/$T1SEDNAME\./$PREFIX/g" ;; - *) sed < $CCSRC > $CCOUT -e "s//$T1/g" -e "s//$T1$T1ACC/g";; +2Y) # Two output substitutions, change the prefix + sed < $fullsrc_cc > $out_cc -e " +s//$T1/g +s//$T1$T1ACC/g +s//$T2/g +s//$T2$T2ACC/g +s/$T1SEDNAME\.$T2SEDNAME\./$prefix/g +s/$T1SEDNAME\./$prefix/g +s/$T2SEDNAME\./$prefix/g +" + ;; +2N) # Two output substitutions, use the default prefix + sed < $fullsrc_cc > $out_cc -e " +s//$T1/g +s//$T1$T1ACC/g +s//$T2/g +s//$T2$T2ACC/g +" + ;; +1Y) # One output substitution, change the prefix + sed < $fullsrc_cc > $out_cc -e " +s//$T1/g +s//$T1$T1ACC/g +s/$T1SEDNAME\./$prefix/g +" + ;; +1N) # One output substitution, use the default prefix + sed < $fullsrc_cc > $out_cc -e " +s//$T1/g +s//$T1$T1ACC/g +" + ;; esac -exit 0 +if egrep '' $out_h $out_cc > /dev/null ; then + echo "${name}: the $class class requires the -2 syntax for the 2nd type" 1>&2 ; + echo "usage: $usage" 1>&2 ; + # the user does not get to see the mistakes (he might try to compile it) + rm $out_h $out_cc ; + exit 1; +fi ; + +exit 0; diff --git a/gnu/lib/libg++/iostream/PlotFile.C b/gnu/lib/libg++/iostream/PlotFile.C new file mode 100644 index 0000000000..306ba5e902 --- /dev/null +++ b/gnu/lib/libg++/iostream/PlotFile.C @@ -0,0 +1,130 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988, 1992 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + converted to use iostream library by Per Bothner (bothner@cygnus.com) + +This file is part of GNU CC. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY. No author or distributor +accepts responsibility to anyone for the consequences of using it +or for whether it serves any particular purpose or works at all, +unless he says so in writing. Refer to the GNU CC General Public +License for full details. + +Everyone is granted permission to copy, modify and redistribute +GNU CC, but only under the conditions described in the +GNU CC General Public License. A copy of this license is +supposed to have been given to you along with GNU CC so you +can know your rights and responsibilities. It should be in a +file named COPYING. Among other things, the copyright notice +and this notice must be preserved on all copies. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include + +/* + PlotFile implementation module +*/ + + +PlotFile& PlotFile:: cmd(char c) +{ + ofstream::put(c); + return *this; +} + +PlotFile& PlotFile:: operator<<(const int x) +{ +#if defined(convex) + ofstream::put((char)(x>>8)); + ofstream::put((char)(x&0377)); +#else + ofstream::put((char)(x&0377)); + ofstream::put((char)(x>>8)); +#endif + return *this; +} + +PlotFile& PlotFile:: operator<<(const char *s) +{ + *(ofstream*)this << s; + return *this; +} + + +PlotFile& PlotFile:: arc(const int xi, const int yi, + const int x0, const int y0, + const int x1, const int y1) +{ + return cmd('a') << xi << yi << x0 << y0 << x1 << y1; +} + + +PlotFile& PlotFile:: box(const int x0, const int y0, + const int x1, const int y1) +{ + line(x0, y0, x0, y1); + line(x0, y1, x1, y1); + line(x1, y1, x1, y0); + return line(x1, y0, x0, y0); +} + +PlotFile& PlotFile:: circle(const int x, const int y, const int r) +{ + return cmd('c') << x << y << r; +} + +PlotFile& PlotFile:: cont(const int xi, const int yi) +{ + return cmd('n') << xi << yi; +} + +PlotFile& PlotFile:: dot(const int xi, const int yi, const int dx, + int n, const int* pat) +{ + cmd('d') << xi << yi << dx << n; + while (n-- > 0) *this << *pat++; + return *this; +} + +PlotFile& PlotFile:: erase() +{ + return cmd('e'); +} + +PlotFile& PlotFile:: label(const char* s) +{ + return cmd('t') << s << "\n"; +} + +PlotFile& PlotFile:: line(const int x0, const int y0, + const int x1, const int y1) +{ + return cmd('l') << x0 << y0 << x1 << y1; +} + +PlotFile& PlotFile:: linemod(const char* s) +{ + return cmd('f') << s << "\n"; +} + +PlotFile& PlotFile:: move(const int xi, const int yi) +{ + return cmd('m') << xi << yi; +} + +PlotFile& PlotFile:: point(const int xi, const int yi) +{ + return cmd('p') << xi << yi; +} + +PlotFile& PlotFile:: space(const int x0, const int y0, + const int x1, const int y1) +{ + return cmd('s') << x0 << y0 << x1 << y1; +} diff --git a/gnu/lib/libg++/iostream/PlotFile.h b/gnu/lib/libg++/iostream/PlotFile.h new file mode 100644 index 0000000000..e157633b0a --- /dev/null +++ b/gnu/lib/libg++/iostream/PlotFile.h @@ -0,0 +1,118 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988, 1992 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + converted to use iostream library by Per Bothner (bothner@cygnus.com) + +This file is part of GNU CC. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY. No author or distributor +accepts responsibility to anyone for the consequences of using it +or for whether it serves any particular purpose or works at all, +unless he says so in writing. Refer to the GNU CC General Public +License for full details. + +Everyone is granted permission to copy, modify and redistribute +GNU CC, but only under the conditions described in the +GNU CC General Public License. A copy of this license is +supposed to have been given to you along with GNU CC so you +can know your rights and responsibilities. It should be in a +file named COPYING. Among other things, the copyright notice +and this notice must be preserved on all copies. +*/ + +/* + a very simple implementation of a class to output unix "plot" + format plotter files. See corresponding unix man pages for + more details. +*/ + +#ifndef _PlotFile_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _PlotFile_h + +#include + +/* + Some plot libraries have the `box' command to draw boxes. Some don't. + `box' is included here via moves & lines to allow both possiblilties. +*/ + + +class PlotFile : public ofstream +{ +protected: + PlotFile& cmd(char c); + PlotFile& operator << (const int x); + PlotFile& operator << (const char *s); + +public: + + PlotFile() : ofstream() { } + PlotFile(int fd) : ofstream(fd) { } + PlotFile(const char *name, int mode=ios::out, int prot=0664) + : ofstream(name, mode, prot) { } + +// PlotFile& remove() { ofstream::remove(); return *this; } + +// int filedesc() { return ofstream::filedesc(); } +// const char* name() { return File::name(); } +// void setname(const char* newname) { File::setname(newname); } +// int iocount() { return File::iocount(); } + + PlotFile& arc(const int xi, const int yi, + const int x0, const int y0, + const int x1, const int y1); + PlotFile& box(const int x0, const int y0, + const int x1, const int y1); + PlotFile& circle(const int x, const int y, const int r); + PlotFile& cont(const int xi, const int yi); + PlotFile& dot(const int xi, const int yi, const int dx, + int n, const int* pat); + PlotFile& erase(); + PlotFile& label(const char* s); + PlotFile& line(const int x0, const int y0, + const int x1, const int y1); + PlotFile& linemod(const char* s); + PlotFile& move(const int xi, const int yi); + PlotFile& point(const int xi, const int yi); + PlotFile& space(const int x0, const int y0, + const int x1, const int y1); +}; +#endif + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gnu/lib/libg++/iostream/SFile.C b/gnu/lib/libg++/iostream/SFile.C new file mode 100644 index 0000000000..9221bb34f7 --- /dev/null +++ b/gnu/lib/libg++/iostream/SFile.C @@ -0,0 +1,58 @@ +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef __GNUG__ +#pragma implementation +#endif +#include + +SFile::SFile(const char *filename, int size, int mode, int prot) +: fstream(filename, mode, prot) +{ + sz = size; +} + +SFile::SFile(int fd, int size) +: fstream(fd) +{ + sz = size; +} + +void SFile::open(const char *name, int size, int mode, int prot) +{ + fstream::open(name, mode, prot); + sz = size; +} + +SFile& SFile::get(void* x) +{ + read(x, sz); + return *this; +} + +SFile& SFile::put(void* x) +{ + write(x, sz); + return *this; +} + +SFile& SFile::operator[](long i) +{ + if (rdbuf()->seekoff(i * sz, ios::beg) == EOF) + set(ios::badbit); + return *this; +} diff --git a/gnu/lib/libg++/iostream/SFile.h b/gnu/lib/libg++/iostream/SFile.h new file mode 100644 index 0000000000..c7a986821a --- /dev/null +++ b/gnu/lib/libg++/iostream/SFile.h @@ -0,0 +1,46 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988, 1992 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _SFile_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _SFile_h 1 + +#include + +class SFile: public fstream +{ + protected: + int sz; // unit size for structured binary IO + +public: + SFile() : fstream() { } + SFile(int fd, int size); + SFile(const char *name, int size, int mode, int prot=0664); + void open(const char *name, int size, int mode, int prot=0664); + + int size() { return sz; } + int setsize(int s) { int old = sz; sz = s; return old; } + + SFile& get(void* x); + SFile& put(void* x); + SFile& operator[](long i); +}; + +#endif diff --git a/gnu/lib/libg++/iostream/editbuf.C b/gnu/lib/libg++/iostream/editbuf.C new file mode 100644 index 0000000000..3d99e8442d --- /dev/null +++ b/gnu/lib/libg++/iostream/editbuf.C @@ -0,0 +1,707 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1991 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifdef __GNUG__ +#pragma implementation +#endif +#include "ioprivate.h" +#include "editbuf.h" +#include + +/* NOTE: Some of the code here is taken from GNU emacs */ +/* Hence this file falls under the GNU License! */ + +// Invariants for edit_streambuf: +// An edit_streambuf is associated with a specific edit_string, +// which again is a sub-string of a specific edit_buffer. +// An edit_streambuf is always in either get mode or put mode, never both. +// In get mode, gptr() is the current position, +// and pbase(), pptr(), and epptr() are all NULL. +// In put mode, pptr() is the current position, +// and eback(), gptr(), and egptr() are all NULL. +// Any edit_streambuf that is actively doing insertion (as opposed to +// replacing) // must have its pptr() pointing to the start of the gap. +// Only one edit_streambuf can be actively inserting into a specific +// edit_buffer; the edit_buffer's _writer field points to that edit_streambuf. +// That edit_streambuf "owns" the gap, and the actual start of the +// gap is the pptr() of the edit_streambuf; the edit_buffer::_gap_start pointer +// will only be updated on an edit_streambuf::overflow(). + +int edit_streambuf::truncate() +{ + str->buffer->delete_range(str->buffer->tell((buf_char*)pptr()), + str->buffer->tell(str->end)); + return 0; +} + +#ifdef OLD_STDIO +inline void disconnect_gap_from_file(edit_buffer* buffer, FILE* fp) +{ + if (buffer->gap_start_ptr != &fp->__bufp) + return; + buffer->gap_start_normal = fp->__bufp; + buffer->gap_start_ptr = &buffer->gap_start_normal; +} +#endif + +void edit_streambuf::flush_to_buffer(edit_buffer* buffer) +{ + if (pptr() > buffer->_gap_start && pptr() < buffer->gap_end()) + buffer->_gap_start = pptr(); +} + +void edit_streambuf::disconnect_gap_from_file(edit_buffer* buffer) +{ + if (buffer->_writer != this) return; + flush_to_buffer(buffer); + setp(pptr(),pptr()); + buffer->_writer = NULL; +} + +buf_index edit_buffer::tell(buf_char *ptr) +{ + if (ptr <= gap_start()) + return ptr - data; + else + return ptr - gap_end() + size1(); +} + +#if 0 +buf_index buf_cookie::tell() +{ + return str->buffer->tell(file->__bufp); +} +#endif + +buf_index edit_buffer::tell(edit_mark*mark) +{ + return tell(data + mark->index_in_buffer(this)); +} + +// adjust the position of the gap + +void edit_buffer::move_gap(buf_offset pos) +{ + if (pos < size1()) + gap_left (pos); + else if (pos > size1()) + gap_right (pos); +} + +void edit_buffer::gap_left (int pos) +{ + register buf_char *to, *from; + register int i; + int new_s1; + + i = size1(); + from = gap_start(); + to = from + gap_size(); + new_s1 = size1(); + + /* Now copy the characters. To move the gap down, + copy characters up. */ + + for (;;) + { + /* I gets number of characters left to copy. */ + i = new_s1 - pos; + if (i == 0) + break; +#if 0 + /* If a quit is requested, stop copying now. + Change POS to be where we have actually moved the gap to. */ + if (QUITP) + { + pos = new_s1; + break; + } +#endif + /* Move at most 32000 chars before checking again for a quit. */ + if (i > 32000) + i = 32000; + new_s1 -= i; + while (--i >= 0) + *--to = *--from; + } + + /* Adjust markers, and buffer data structure, to put the gap at POS. + POS is where the loop above stopped, which may be what was specified + or may be where a quit was detected. */ + adjust_markers (pos << 1, size1() << 1, gap_size(), data); +#ifndef OLD_STDIO + _gap_start = data + pos; +#else + if (gap_start_ptr == &gap_start_normal) + gap_start_normal = data + pos; +#endif + __gap_end_pos = to - data; +/* QUIT;*/ +} + +void edit_buffer::gap_right (int pos) +{ + register buf_char *to, *from; + register int i; + int new_s1; + + i = size1(); + to = gap_start(); + from = i + gap_end(); + new_s1 = i; + + /* Now copy the characters. To move the gap up, + copy characters down. */ + + while (1) + { + /* I gets number of characters left to copy. */ + i = pos - new_s1; + if (i == 0) + break; +#if 0 + /* If a quit is requested, stop copying now. + Change POS to be where we have actually moved the gap to. */ + if (QUITP) + { + pos = new_s1; + break; + } +#endif + /* Move at most 32000 chars before checking again for a quit. */ + if (i > 32000) + i = 32000; + new_s1 += i; + while (--i >= 0) + *to++ = *from++; + } + + adjust_markers ((size1() + gap_size()) << 1, (pos + gap_size()) << 1, + - gap_size(), data); +#ifndef OLD_STDIO + _gap_start = data+pos; +#else + if (gap_start_ptr == &gap_start_normal) + gap_start_normal = data + pos; +#endif + __gap_end_pos = from - data; +/* QUIT;*/ +} + +/* make sure that the gap in the current buffer is at least k + characters wide */ + +void edit_buffer::make_gap(buf_offset k) +{ + register buf_char *p1, *p2, *lim; + buf_char *old_data = data; + int s1 = size1(); + + if (gap_size() >= k) + return; + + /* Get more than just enough */ + if (buf_size > 1000) k += 2000; + else k += /*200;*/ 20; // for testing! + + p1 = (buf_char *) realloc (data, s1 + size2() + k); + if (p1 == 0) + abort(); /*memory_full ();*/ + + k -= gap_size(); /* Amount of increase. */ + + /* Record new location of text */ + data = p1; + + /* Transfer the new free space from the end to the gap + by shifting the second segment upward */ + p2 = data + buf_size; + p1 = p2 + k; + lim = p2 - size2(); + while (lim < p2) + *--p1 = *--p2; + + /* Finish updating text location data */ + __gap_end_pos += k; + +#ifndef OLD_STDIO + _gap_start = data + s1; +#else + if (gap_start_ptr == &gap_start_normal) + gap_start_normal = data + s1; +#endif + + /* adjust markers */ + adjust_markers (s1 << 1, (buf_size << 1) + 1, k, old_data); + buf_size += k; +} + +/* Add `amount' to the position of every marker in the current buffer + whose current position is between `from' (exclusive) and `to' (inclusive). + Also, any markers past the outside of that interval, in the direction + of adjustment, are first moved back to the near end of the interval + and then adjusted by `amount'. */ + +void edit_buffer::adjust_markers(register mark_pointer low, + register mark_pointer high, + int amount, buf_char *old_data) +{ + register struct edit_mark *m; + register mark_pointer mpos; + /* convert to mark_pointer */ + amount <<= 1; + + if (_writer) + _writer->disconnect_gap_from_file(this); + + for (m = mark_list(); m != NULL; m = m->chain) + { + mpos = m->_pos; + if (amount > 0) + { + if (mpos > high && mpos < high + amount) + mpos = high + amount; + } + else + { + if (mpos > low + amount && mpos <= low) + mpos = low + amount; + } + if (mpos > low && mpos <= high) + mpos += amount; + m->_pos = mpos; + } + + // Now adjust files + edit_streambuf *file; + + for (file = files; file != NULL; file = file->next) { + mpos = file->current() - old_data; + if (amount > 0) + { + if (mpos > high && mpos < high + amount) + mpos = high + amount; + } + else + { + if (mpos > low + amount && mpos <= low) + mpos = low + amount; + } + if (mpos > low && mpos <= high) + mpos += amount; + char* new_pos = data + mpos; + file->set_current(new_pos, file->is_reading()); + } +} + +#if 0 +stdio_ + __off == index at start of buffer (need only be valid after seek ? ) + __buf == + +if read/read_delete/overwrite mode: + __endp <= min(*gap_start_ptr, edit_string->end->ptr(buffer)) + +if inserting: + must have *gap_start_ptr == __bufp && *gap_start_ptr+gap == __endp + file->edit_string->end->ptr(buffer) == *gap_start_ptr+end +if write_mode: + if before gap +#endif + +int edit_streambuf::underflow() +{ + if (!(_mode & ios::in)) + return EOF; + struct edit_buffer *buffer = str->buffer; + if (!is_reading()) { // Must switch from put to get mode. + disconnect_gap_from_file(buffer); + set_current(pptr(), 1); + } + buf_char *str_end = str->end->ptr(buffer); + retry: + if (gptr() < egptr()) { + return *gptr(); + } + if ((buf_char*)gptr() == str_end) + return EOF; + if (str_end <= buffer->gap_start()) { + setg(eback(), gptr(), str_end); + goto retry; + } + if (gptr() < buffer->gap_start()) { + setg(eback(), gptr(), buffer->gap_start()); + goto retry; + } + if (gptr() == buffer->gap_start()) { + disconnect_gap_from_file(buffer); +// fp->__offset += fp->__bufp - fp->__buffer; + setg(buffer->gap_end(), buffer->gap_end(), str_end); + } + else + setg(eback(), gptr(), str_end); + goto retry; +} + +int edit_streambuf::overflow(int ch) +{ + if (_mode == ios::in) + return EOF; + struct edit_buffer *buffer = str->buffer; + flush_to_buffer(buffer); + if (ch == EOF) + return 0; + if (is_reading()) { // Must switch from get to put mode. + set_current(gptr(), 0); + } + buf_char *str_end = str->end->ptr(buffer); + retry: + if (pptr() < epptr()) { + *pptr() = ch; + pbump(1); + return (unsigned char)ch; + } + if ((buf_char*)pptr() == str_end || inserting()) { + /* insert instead */ + if (buffer->_writer) + buffer->_writer->flush_to_buffer(); // Redundant? + buffer->_writer = NULL; + if (pptr() >= buffer->gap_end()) + buffer->move_gap(pptr() - buffer->gap_size()); + else + buffer->move_gap(pptr()); + buffer->make_gap(1); + setp(buffer->gap_start(), buffer->gap_end()); + buffer->_writer = this; + *pptr() = ch; + pbump(1); + return (unsigned char)ch; + } + if (str_end <= buffer->gap_start()) { + // Entire string is left of gap. + setp(pptr(), str_end); + } + else if (pptr() < buffer->gap_start()) { + // Current pos is left of gap. + setp(pptr(), buffer->gap_start()); + goto retry; + } + else if (pptr() == buffer->gap_start()) { + // Current pos is at start of gap; move to end of gap. +// disconnect_gap_from_file(buffer); + setp(buffer->gap_end(), str_end); +// __offset += __bufp - __buffer; + } + else { + // Otherwise, current pos is right of gap. + setp(pptr(), str_end); + } + goto retry; +} + +void edit_streambuf::set_current(char *new_pos, int reading) +{ + if (reading) { + setg(new_pos, new_pos, new_pos); + setp(NULL, NULL); + } + else { + setg(NULL, NULL, NULL); + setp(new_pos, new_pos); + } +} + +// Called by fseek(fp, pos, whence) if fp is bound to a edit_buffer. + +streampos edit_streambuf::seekoff(streamoff offset, _seek_dir dir, + int mode /* =ios::in|ios::out*/) +{ + struct edit_buffer *buffer = str->buffer; + disconnect_gap_from_file(buffer); + buf_index cur_pos = buffer->tell((buf_char*)current());; + buf_index start_pos = buffer->tell(str->start); + buf_index end_pos = buffer->tell(str->end); + switch (dir) { + case ios::beg: + offset += start_pos; + break; + case ios::cur: + offset += cur_pos; + break; + case ios::end: + offset += end_pos; + break; + } + if (offset < start_pos || offset > end_pos) + return EOF; + buf_char *new_pos = buffer->data + offset; + buf_char* gap_start = buffer->gap_start(); + if (new_pos > gap_start) { + buf_char* gap_end = buffer->gap_end(); + new_pos += gap_end - gap_start; + if (new_pos >= buffer->data + buffer->buf_size) abort(); // Paranoia. + } + set_current(new_pos, is_reading()); + return EOF; +} + +#if 0 +int buf_seek(void *arg_cookie, fpos_t * pos, int whence) +{ + struct buf_cookie *cookie = arg_cookie; + FILE *file = cookie->file; + struct edit_buffer *buffer = cookie->str->buffer; + buf_char *str_start = cookie->str->start->ptr(buffer); + disconnect_gap_from_file(buffer, cookie->file); + fpos_t cur_pos, new_pos; + if (file->__bufp <= *buffer->gap_start_ptr + || str_start >= buffer->__gap_end) + cur_pos = str_start - file->__bufp; + else + cur_pos = + (*buffer->gap_start_ptr - str_start) + (file->__bufp - __gap_end); + end_pos = ...; + switch (whence) { + case SEEK_SET: + new_pos = *pos; + break; + case SEEK_CUR: + new_pos = cur_pos + *pos; + break; + case SEEK_END: + new_pos = end_pos + *pos; + break; + } + if (new_pos > end_pos) { + seek to end_pos; + insert_nulls(new_pos - end_pos); + return; + } + if (str_start + new_pos <= *gap_start_ptr &* *gap_start_ptr < end) { + __buffer = str_start; + __off = 0; + __bufp = str_start + new_pos; + file->__get_limit = + *buffer->gap_start_ptr; /* what if gap_start_ptr == &bufp ??? */ + } else if () { + + } + *pos = new_pos; +} +#endif + +/* Delete characters from `from' up to (but not incl) `to' */ + +void edit_buffer::delete_range (buf_index from, buf_index to) +{ + register int numdel; + + if ((numdel = to - from) <= 0) + return; + + /* Make sure the gap is somewhere in or next to what we are deleting */ + if (from > size1()) + gap_right (from); + if (to < size1()) + gap_left (to); + + /* Relocate all markers pointing into the new, larger gap + to point at the end of the text before the gap. */ + adjust_markers ((to + gap_size()) << 1, (to + gap_size()) << 1, + - numdel - gap_size(), data); + + __gap_end_pos = to + gap_size(); + _gap_start = data + from; +} + +void edit_buffer::delete_range(struct edit_mark *start, struct edit_mark *end) +{ + delete_range(tell(start), tell(end)); +} + +void buf_delete_chars(struct edit_buffer *buf, struct edit_mark *mark, size_t count) +{ + abort(); +} + +edit_streambuf::edit_streambuf(edit_string* bstr, int mode) +{ + _mode = mode; + str = bstr; + edit_buffer* buffer = bstr->buffer; + next = buffer->files; + buffer->files = this; + char* buf_ptr = bstr->start->ptr(buffer); + _inserting = 0; +// setb(buf_ptr, buf_ptr, 0); + set_current(buf_ptr, !(mode & ios::out+ios::trunc+ios::app)); + if (_mode & ios::trunc) + truncate(); + if (_mode & ios::ate) + seekoff(0, ios::end); +} + +// Called by fclose(fp) if fp is bound to a edit_buffer. + +#if 0 +static int buf_close(void *arg) +{ + register struct buf_cookie *cookie = arg; + struct edit_buffer *buffer = cookie->str->buffer; + struct buf_cookie **ptr; + for (ptr = &buffer->files; *ptr != cookie; ptr = &(*ptr)->next) ; + *ptr = cookie->next; + disconnect_gap_from_file(buffer, cookie->file); + free (cookie); + return 0; +} +#endif + +edit_streambuf::~edit_streambuf() +{ + if (_mode == ios::out) + truncate(); + // Unlink this from list of files associated with bstr->buffer. + edit_streambuf **ptr = &str->buffer->files; + for (; *ptr != this; ptr = &(*ptr)->next) { } + *ptr = next; + + disconnect_gap_from_file(str->buffer); +} + +edit_buffer::edit_buffer() +{ + buf_size = /*200;*/ 15; /* for testing! */ + data = (buf_char*)malloc(buf_size); + files = NULL; +#ifndef OLD_STDIO + _gap_start = data; + _writer = NULL; +#else + gap_start_normal = data; + gap_start_ptr = &gap_start_normal; +#endif + __gap_end_pos = buf_size; + start_mark.chain = &end_mark; + start_mark._pos = 0; + end_mark.chain = NULL; + end_mark._pos = 2 * buf_size + 1; +} + +// Allocate a new mark, which is adjusted by 'delta' bytes from 'this'. +// Restrict new mark to lie within 'str'. + +edit_mark::edit_mark(struct edit_string *str, long delta) +{ + struct edit_buffer *buf = str->buffer; + chain = buf->start_mark.chain; + buf->start_mark.chain = this; + mark_pointer size1 = buf->size1() << 1; + int gap_size = buf->gap_size() << 1; + delta <<= 1; + + // check if new and old marks are opposite sides of gap + if (_pos <= size1 && _pos + delta > size1) + delta += gap_size; + else if (_pos >= size1 + gap_size && _pos + delta < size1 + gap_size) + delta -= gap_size; + + _pos = _pos + delta; + if (_pos < str->start->_pos & ~1) + _pos = (str->start->_pos & ~ 1) + (_pos & 1); + else if (_pos >= str->end->_pos) + _pos = (str->end->_pos & ~ 1) + (_pos & 1); +} + +// A (slow) way to find the buffer a mark belongs to. + +edit_buffer * edit_mark::buffer() +{ + struct edit_mark *mark; + for (mark = this; mark->chain != NULL; mark = mark->chain) ; + // Assume that the last mark on the chain is the end_mark. + return (edit_buffer *)((char*)mark - offsetof(edit_buffer, end_mark)); +} + +edit_mark::~edit_mark() +{ + // Must unlink mark from chain of owning buffer + struct edit_buffer *buf = buffer(); + if (this == &buf->start_mark || this == &buf->end_mark) abort(); + edit_mark **ptr; + for (ptr = &buf->start_mark.chain; *ptr != this; ptr = &(*ptr)->chain) ; + *ptr = this->chain; +} + +int edit_string::length() const +{ + ptrdiff_t delta = end->ptr(buffer) - start->ptr(buffer); + if (end->ptr(buffer) <= buffer->gap_start() || + start->ptr(buffer) >= buffer->gap_end()) + return delta; + return delta - buffer->gap_size(); +} + +buf_char * edit_string::copy_bytes(int *lenp) const +{ + char *new_str; + int len1, len2; + buf_char *start1, *start2; + start1 = start->ptr(buffer); + if (end->ptr(buffer) <= buffer->gap_start() + || start->ptr(buffer) >= buffer->gap_end()) { + len1 = end->ptr(buffer) - start1; + len2 = 0; + start2 = NULL; // To avoid a warning from g++. + } + else { + len1 = buffer->gap_start() - start1; + start2 = buffer->gap_end(); + len2 = end->ptr(buffer) - start2; + } + new_str = (char*)malloc(len1 + len2 + 1); + memcpy(new_str, start1, len1); + if (len2 > 0) memcpy(new_str + len1, start2, len2); + new_str[len1+len2] = '\0'; + *lenp = len1+len2; + return new_str; +} + +// Replace the buf_chars in 'this' with ones from 'src'. +// Equivalent to deleting this, then inserting src, except tries +// to leave marks in place: Marks whose offset from the start +// of 'this' is less than 'src->length()' will still have the +// same offset in 'this' when done. + +void edit_string::assign(struct edit_string *src) +{ + edit_streambuf dst_file(this, ios::out); + if (buffer == src->buffer /*&& ???*/) { /* overly conservative */ + int src_len; + buf_char *new_str; + new_str = src->copy_bytes(&src_len); + dst_file.sputn(new_str, src_len); + free (new_str); + } else { + edit_streambuf src_file(src, ios::in); + for ( ; ; ) { + int ch = src_file.sbumpc(); + if (ch == EOF) break; + dst_file.sputc(ch); + } + } +} diff --git a/gnu/lib/libg++/iostream/editbuf.h b/gnu/lib/libg++/iostream/editbuf.h new file mode 100644 index 0000000000..df77c3c255 --- /dev/null +++ b/gnu/lib/libg++/iostream/editbuf.h @@ -0,0 +1,173 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1991 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifndef _EDITBUF_H +#define _EDITBUF_H +#ifdef __GNUG__ +#pragma interface +#endif +#include +#include + +typedef unsigned long mark_pointer; +// At some point, it might be nice to parameterize this code +// in terms of buf_char. +typedef /*unsigned*/ char buf_char; + +// Logical pos from start of buffer (does not count gap). +typedef long buf_index; + +// Pos from start of buffer, possibly including gap_size. +typedef long buf_offset; + +#if 0 +struct buf_cookie { + FILE *file; + struct edit_string *str; + struct buf_cookie *next; + buf_index tell(); +}; +#endif + +struct edit_buffer; +struct edit_mark; + +// A edit_string is defined as the region between the 'start' and 'end' marks. +// Normally (always?) 'start->insert_before()' should be false, +// and 'end->insert_before()' should be true. + +struct edit_string { + struct edit_buffer *buffer; // buffer that 'start' and 'end' belong to + struct edit_mark *start, *end; + int length() const; // count of buf_chars currently in string + edit_string(struct edit_buffer *b, + struct edit_mark *ms, struct edit_mark *me) + { buffer = b; start = ms; end = me; } +/* Make a fresh, contiguous copy of the data in STR. + Assign length of STR to *LENP. + (Output has extra NUL at out[*LENP].) */ + buf_char *copy_bytes(int *lenp) const; +// FILE *open_file(char *mode); + void assign(struct edit_string *src); // copy bytes from src to this +}; + +struct edit_streambuf : public streambuf { + friend edit_buffer; + edit_string *str; + edit_streambuf* next; // Chain of edit_streambuf's for a edit_buffer. + short _mode; + edit_streambuf(edit_string* bstr, int mode); + ~edit_streambuf(); + virtual int underflow(); + virtual int overflow(int c = EOF); + virtual streampos seekoff(streamoff, _seek_dir, int mode=ios::in|ios::out); + void flush_to_buffer(); + void flush_to_buffer(edit_buffer* buffer); + int _inserting; + int inserting() { return _inserting; } + void inserting(int i) { _inserting = i; } +// int delete_chars(int count, char* cut_buf); Not implemented. + int truncate(); + int is_reading() { return gptr() != NULL; } + buf_char* current() { return is_reading() ? gptr() : pptr(); } + void set_current(char *p, int is_reading); + protected: + void disconnect_gap_from_file(edit_buffer* buffer); +}; + +// A 'edit_mark' indicates a position in a buffer. +// It is "attached" the text (rather than the offset). +// There are two kinds of mark, which have different behavior +// when text is inserted at the mark: +// If 'insert_before()' is true the mark will be adjusted to be +// *after* the new text. + +struct edit_mark { + struct edit_mark *chain; + mark_pointer _pos; + inline int insert_before() { return _pos & 1; } + inline unsigned long index_in_buffer(struct edit_buffer *buffer) + { return _pos >> 1; } + inline buf_char *ptr(struct edit_buffer *buf); + buf_index tell(); + edit_mark() { } + edit_mark(struct edit_string *str, long delta); + edit_buffer *buffer(); + ~edit_mark(); +}; + +// A 'edit_buffer' consists of a sequence of buf_chars (the data), +// a list of edit_marks pointing into the data, and a list of FILEs +// also pointing into the data. +// A 'edit_buffer' coerced to a edit_string is the string of +// all the buf_chars in the buffer. + +// This implementation uses a conventional buffer gap (as in Emacs). +// The gap start is defined by de-referencing a (buf_char**). +// This is because sometimes a FILE is inserting into the buffer, +// so rather than having each putc adjust the gap, we use indirection +// to have the gap be defined as the write pointer of the FILE. +// (This assumes that putc adjusts a pointer (as in GNU's libc), not an index.) + +struct edit_buffer { + buf_char *data; /* == emacs buffer_text.p1+1 */ + buf_char *_gap_start; + edit_streambuf* _writer; // If non-NULL, currently writing stream + inline buf_char *gap_start() + { return _writer ? _writer->pptr() : _gap_start; } + buf_offset __gap_end_pos; // size of part 1 + size of gap + /* int gap; implicit: buf_size - size1 - size2 */ + int buf_size; + struct edit_streambuf *files; + struct edit_mark start_mark; + struct edit_mark end_mark; + edit_buffer(); + inline buf_offset gap_end_pos() { return __gap_end_pos; } + inline struct edit_mark *start_marker() { return &start_mark; } + inline struct edit_mark *end_marker() { return &end_mark; } +/* these should be protected, ultimately */ + buf_index tell(edit_mark*); + buf_index tell(buf_char*); + inline buf_char *gap_end() { return data + gap_end_pos(); } + inline int gap_size() { return gap_end() - gap_start(); } + inline int size1() { return gap_start() - data; } + inline int size2() { return buf_size - gap_end_pos(); } + inline struct edit_mark * mark_list() { return &start_mark; } + void make_gap (buf_offset); + void move_gap (buf_offset pos); + void move_gap (buf_char *pos) { move_gap(pos - data); } + void gap_left (int pos); + void gap_right (int pos); + void adjust_markers(mark_pointer low, mark_pointer high, + int amount, buf_char *old_data); + void delete_range(buf_index from, buf_index to); + void delete_range(struct edit_mark *start, struct edit_mark *end); +}; + +extern buf_char * bstr_copy(struct edit_string *str, int *lenp); + +// Convert a edit_mark to a (buf_char*) + +inline buf_char *edit_mark::ptr(struct edit_buffer *buf) + { return buf->data + index_in_buffer(buf); } + +inline void edit_streambuf::flush_to_buffer() +{ + edit_buffer* buffer = str->buffer; + if (buffer->_writer == this) flush_to_buffer(buffer); +} +#endif /* !_EDITBUF_H*/ + diff --git a/gnu/lib/libg++/iostream/filebuf.C b/gnu/lib/libg++/iostream/filebuf.C new file mode 100644 index 0000000000..4d49d7f0e6 --- /dev/null +++ b/gnu/lib/libg++/iostream/filebuf.C @@ -0,0 +1,580 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1991, 1992 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#include "ioprivate.h" +#include +#include +#include +#include + +// An fstream can be in at most one of put mode, get mode, or putback mode. +// Putback mode is a variant of get mode. + +// In a filebuf, there is only one current position, instead of two +// separate get and put pointers. In get mode, the current posistion +// is that of gptr(); in put mode that of pptr(). + +// The position in the buffer that corresponds to the position +// in external file system is file_ptr(). +// This is normally egptr(), except in putback mode, when it is _save_egptr. +// If the field _fb._offset is >= 0, it gives the offset in +// the file as a whole corresponding to eGptr(). (???) + +// PUT MODE: +// If a filebuf is in put mode, pbase() is non-NULL and equal to base(). +// Also, epptr() == ebuf(). +// Also, eback() == gptr() && gptr() == egptr(). +// The un-flushed character are those between pbase() and pptr(). +// GET MODE: +// If a filebuf is in get or putback mode, eback() != egptr(). +// In get mode, the unread characters are between gptr() and egptr(). +// The OS file position corresponds to that of egptr(). +// PUTBACK MODE: +// Putback mode is used to remember "excess" characters that have +// been sputbackc'd in a separate putback buffer. +// In putback mode, the get buffer points to the special putback buffer. +// The unread characters are the characters between gptr() and egptr() +// in the putback buffer, as well as the area between save_gptr() +// and save_egptr(), which point into the original reserve buffer. +// (The pointers save_gptr() and save_egptr() are the values +// of gptr() and egptr() at the time putback mode was entered.) +// The OS position corresponds to that of save_egptr(). +// +// LINE BUFFERED OUTPUT: +// During line buffered output, pbase()==base() && epptr()==base(). +// However, ptr() may be anywhere between base() and ebuf(). +// This forces a call to filebuf::overflow(int C) on every put. +// If there is more space in the buffer, and C is not a '\n', +// then C is inserted, and pptr() incremented. +// +// UNBUFFERED STREAMS: +// If a filebuf is unbuffered(), the _shortbuf[1] is used as the buffer. + +#define CLOSED_FILEBUF_FLAGS \ + (_S_IS_FILEBUF+_S_NO_READS+_S_NO_WRITES+_S_TIED_PUT_GET) + +void filebuf::init() +{ + _fb._offset = 0; + + _link_in(); + _fb._fileno = -1; +} + +filebuf::filebuf() : backupbuf(CLOSED_FILEBUF_FLAGS) +{ + init(); +} + +filebuf::filebuf(int fd) : backupbuf(CLOSED_FILEBUF_FLAGS) +{ + init(); + attach(fd); +} + +filebuf::filebuf(int fd, char* p, int len) : backupbuf(CLOSED_FILEBUF_FLAGS) +{ + init(); + attach(fd); + setbuf(p, len); +} + +filebuf::~filebuf() +{ + if (!(xflags() & _S_DELETE_DONT_CLOSE)) + close(); + + _un_link(); +} + +filebuf* filebuf::open(const char *filename, ios::openmode mode, int prot) +{ + if (is_open()) + return NULL; + int posix_mode; + int read_write; + if (mode & ios::app) + mode |= ios::out; + if ((mode & (ios::in|ios::out)) == (ios::in|ios::out)) { + posix_mode = O_RDWR; + read_write = 0; + } + else if (mode & ios::out) + posix_mode = O_WRONLY, read_write = _S_NO_READS; + else if (mode & (int)ios::in) + posix_mode = O_RDONLY, read_write = _S_NO_WRITES; + else + posix_mode = 0, read_write = _S_NO_READS+_S_NO_WRITES; + if ((mode & (int)ios::trunc) || mode == (int)ios::out) + posix_mode |= O_TRUNC; + if (mode & ios::app) + posix_mode |= O_APPEND, read_write |= _S_IS_APPENDING; + if (!(mode & (int)ios::nocreate) && mode != ios::in) + posix_mode |= O_CREAT; + if (mode & (int)ios::noreplace) + posix_mode |= O_EXCL; + int fd = ::open(filename, posix_mode, prot); + if (fd < 0) + return NULL; + _fb._fileno = fd; + xsetflags(read_write, _S_NO_READS+_S_NO_WRITES+_S_IS_APPENDING); + if (mode & (ios::ate|ios::app)) { + if (seekoff(0, ios::end) == EOF) + return NULL; + } + _link_in(); + return this; +} + +filebuf* filebuf::open(const char *filename, const char *mode) +{ + if (is_open()) + return NULL; + int oflags = 0, omode; + int read_write; + int oprot = 0666; + switch (*mode++) { + case 'r': + omode = O_RDONLY; + read_write = _S_NO_WRITES; + break; + case 'w': + omode = O_WRONLY; + oflags = O_CREAT|O_TRUNC; + read_write = _S_NO_READS; + break; + case 'a': + omode = O_WRONLY; + oflags = O_CREAT|O_APPEND; + read_write = _S_NO_READS|_S_IS_APPENDING; + break; + default: + errno = EINVAL; + return NULL; + } + if (mode[0] == '+' || (mode[0] == 'b' && mode[1] == '+')) { + omode = O_RDWR; + read_write &= _S_IS_APPENDING; + } + int fdesc = ::open(filename, omode|oflags, oprot); + if (fdesc < 0) + return NULL; + _fb._fileno = fdesc; + xsetflags(read_write, _S_NO_READS+_S_NO_WRITES+_S_IS_APPENDING); + if (read_write & _S_IS_APPENDING) + if (seekoff(0, ios::end) == EOF) + return NULL; + _link_in(); + return this; +} + +filebuf* filebuf::attach(int fd) +{ + if (is_open()) + return NULL; + _fb._fileno = fd; + xsetflags(0, _S_NO_READS+_S_NO_WRITES); + return this; +} + +streambuf* filebuf::setbuf(char* p, int len) +{ + if (streambuf::setbuf(p, len) == NULL) + return NULL; + setp(_base, _base); + setg(_base, _base, _base); + return this; +} + +int filebuf::overflow(int c) +{ + if (xflags() & _S_NO_WRITES) // SET ERROR + return EOF; + // Allocate a buffer if needed. + if (base() == NULL) { + doallocbuf(); + if (xflags() & _S_LINE_BUF+_S_UNBUFFERED) setp(_base, _base); + else setp(_base, _ebuf); + setg(_base, _base, _base); + _flags |= _S_CURRENTLY_PUTTING; + } + // If currently reading, switch to writing. + else if ((_flags & _S_CURRENTLY_PUTTING) == 0) { + if (xflags() & _S_LINE_BUF+_S_UNBUFFERED) setp(gptr(), gptr()); + else setp(gptr(), ebuf()); + setg(egptr(), egptr(), egptr()); + _flags |= _S_CURRENTLY_PUTTING; + } + if (c == EOF) + return do_flush(); + if (pptr() == ebuf() ) // Buffer is really full + if (do_flush() == EOF) + return EOF; + xput_char(c); + if (unbuffered() || (linebuffered() && c == '\n')) + if (do_flush() == EOF) + return EOF; + return (unsigned char)c; +} + +int filebuf::underflow() +{ +#if 0 + /* SysV does not make this test; take it out for compatibility */ + if (fp->_flags & __SEOF) + return (EOF); +#endif + + if (xflags() & _S_NO_READS) + return EOF; + if (gptr() < egptr()) + return *(unsigned char*)gptr(); + allocbuf(); + + // FIXME This can/should be moved to __streambuf ?? + if ((xflags() & _S_LINE_BUF) || unbuffered()) { + // Flush all line buffered files before reading. + streambuf::flush_all_linebuffered(); + } + + switch_to_get_mode(); + + _G_ssize_t count = sys_read(base(), ebuf() - base()); + if (count <= 0) { + if (count == 0) + xsetflags(_S_EOF_SEEN); + else + xsetflags(_S_ERR_SEEN), count = 0; + } + setg(base(), base(), base() + count); + setp(base(), base()); + if (count == 0) + return EOF; + if (_fb._offset >= 0) + _fb._offset += count; + return *(unsigned char*)gptr(); +} + +int filebuf::do_write(const char *data, int to_do) +{ + if (to_do == 0) + return 0; + if (xflags() & _S_IS_APPENDING) { + // On a system without a proper O_APPEND implementation, + // you would need to sys_seek(0, ios::end) here, but is + // is not needed nor desirable for Unix- or Posix-like systems. + // Instead, just indicate that offset (before and after) is + // unpredictable. + _fb._offset = -1; + } + else if (egptr() != pbase()) { + long new_pos = sys_seek(pbase()-egptr(), ios::cur); + if (new_pos == -1) + return EOF; + _fb._offset = new_pos; + } + _G_ssize_t count = sys_write(data, to_do); + if (_cur_column) + _cur_column = __adjust_column(_cur_column - 1, data, to_do) + 1; + setg(base(), base(), base()); + if (xflags() & _S_LINE_BUF+_S_UNBUFFERED) setp(base(), base()); + else setp(base(), ebuf()); + return count != to_do ? EOF : 0; +} + +int filebuf::sync() +{ +// char* ptr = cur_ptr(); + if (pptr() > pbase()) + if (do_flush()) return EOF; + if (gptr() != egptr()) { + streampos delta = gptr() - egptr(); + if (in_backup()) + delta -= eGptr() - Gbase(); + _G_fpos_t new_pos = sys_seek(delta, ios::cur); + if (new_pos == EOF) + return EOF; + _fb._offset = new_pos; + setg(eback(), gptr(), gptr()); + } + // FIXME: Cleanup - can this be shared? +// setg(base(), ptr, ptr); + return 0; +} + +streampos filebuf::seekoff(streamoff offset, _seek_dir dir, int mode) +{ + streampos result, new_offset, delta; + _G_ssize_t count; + + if (mode == 0) // Don't move any pointers. + dir = ios::cur, offset = 0; + + // Flush unwritten characters. + // (This may do an unneeded write if we seek within the buffer. + // But to be able to switch to reading, we would need to set + // egptr to ptr. That can't be done in the current design, + // which assumes file_ptr() is eGptr. Anyway, since we probably + // end up flushing when we close(), it doesn't make much difference.) + if (pptr() > pbase() || put_mode()) + if (switch_to_get_mode()) return EOF; + + if (base() == NULL) { + doallocbuf(); + setp(base(), base()); + setg(base(), base(), base()); + } + switch (dir) { + case ios::cur: + if (_fb._offset < 0) { + _fb._offset = sys_seek(0, ios::cur); + if (_fb._offset < 0) + return EOF; + } + // Make offset absolute, assuming current pointer is file_ptr(). + offset += _fb._offset; + + offset -= _egptr - _gptr; + if (in_backup()) + offset -= _other_egptr - _other_gbase; + dir = ios::beg; + break; + case ios::beg: + break; + case ios::end: + struct stat st; + if (sys_stat(&st) == 0 && S_ISREG(st.st_mode)) { + offset += st.st_size; + dir = ios::beg; + } + else + goto dumb; + } + // At this point, dir==ios::beg. + + // If destination is within current buffer, optimize: + if (_fb._offset >= 0 && _eback != NULL) { + // Offset relative to start of main get area. + _G_fpos_t rel_offset = offset - _fb._offset + + (eGptr()-Gbase()); + if (rel_offset >= 0) { + if (in_backup()) + switch_to_main_get_area(); + if (rel_offset <= _egptr - _eback) { + setg(base(), base() + rel_offset, egptr()); + setp(base(), base()); + return offset; + } + // If we have streammarkers, seek forward by reading ahead. + if (have_markers()) { + int to_skip = rel_offset - (_gptr - _eback); + if (ignore(to_skip) != to_skip) + goto dumb; + return offset; + } + } + if (rel_offset < 0 && rel_offset >= Bbase() - Bptr()) { + if (!in_backup()) + switch_to_backup_area(); + gbump(_egptr + rel_offset - gptr()); + return offset; + } + } + + unsave_markers(); + + // Try to seek to a block boundary, to improve kernel page management. + new_offset = offset & ~(ebuf() - base() - 1); + delta = offset - new_offset; + if (delta > ebuf() - base()) { + new_offset = offset; + delta = 0; + } + result = sys_seek(new_offset, ios::beg); + if (result < 0) + return EOF; + if (delta == 0) + count = 0; + else { + count = sys_read(base(), ebuf()-base()); + if (count < delta) { + // We weren't allowed to read, but try to seek the remainder. + offset = count == EOF ? delta : delta-count; + dir = ios::cur; + goto dumb; + } + } + setg(base(), base()+delta, base()+count); + setp(base(), base()); + _fb._offset = result + count; + xflags(xflags() & ~ _S_EOF_SEEN); + return offset; + dumb: + unsave_markers(); + result = sys_seek(offset, dir); + if (result != EOF) { + xflags(xflags() & ~_S_EOF_SEEN); + } + _fb._offset = result; + setg(base(), base(), base()); + setp(base(), base()); + return result; +} + +filebuf* filebuf::close() +{ + if (!is_open()) + return NULL; + + // This flushes as well as switching mode. + if (pptr() > pbase() || put_mode()) + if (switch_to_get_mode()) return NULL; + + unsave_markers(); + + int status = sys_close(); + + // Free buffer. + setb(NULL, NULL, 0); + setg(NULL, NULL, NULL); + setp(NULL, NULL); + + _un_link(); + _flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS; + _fb._fileno = EOF; + _fb._offset = 0; + + return status < 0 ? NULL : this; +} + +_G_ssize_t filebuf::sys_read(char* buf, size_t size) +{ + for (;;) { + _G_ssize_t count = ::read(_fb._fileno, buf, size); + if (count != -1 || errno != EINTR) + return count; + } +} + +_G_fpos_t filebuf::sys_seek(_G_fpos_t offset, _seek_dir dir) +{ + return ::lseek(fd(), offset, (int)dir); +} + +_G_ssize_t filebuf::sys_write(const void *buf, long n) +{ + long to_do = n; + while (to_do > 0) { + _G_ssize_t count = ::write(fd(), buf, to_do); + if (count == EOF) { + if (errno == EINTR) + continue; + else { + _flags |= _S_ERR_SEEN; + break; + } + } + to_do -= count; + buf = (void*)((char*)buf + count); + } + n -= to_do; + if (_fb._offset >= 0) + _fb._offset += n; + return n; +} + +int filebuf::sys_stat(void* st) +{ + return ::_fstat(fd(), (struct stat*)st); +} + +int filebuf::sys_close() +{ + return ::close(fd()); +} + +int filebuf::xsputn(const char *s, int n) +{ + if (n <= 0) + return 0; + // This is an optimized implementation. + // If the amount to be written straddles a block boundary + // (or the filebuf is unbuffered), use sys_write directly. + + int to_do = n; + int must_flush = 0; + // First figure out how much space is available in the buffer. + int count = _epptr - _pptr; // Space available. + if (linebuffered() && (_flags & _S_CURRENTLY_PUTTING)) { + count =_ebuf - _pptr; + if (count >= n) { + for (register const char *p = s + n; p > s; ) { + if (*--p == '\n') { + count = p - s + 1; + must_flush = 1; + break; + } + } + } + } + // Then fill the buffer. + if (count > 0) { + if (count > to_do) + count = to_do; + if (count > 20) { + memcpy(pptr(), s, count); + s += count; + } + else { + register char *p = pptr();; + for (register int i = count; --i >= 0; ) *p++ = *s++; + } + pbump(count); + to_do -= count; + } + if (to_do + must_flush > 0) { + // Next flush the (full) buffer. + if (__overflow(this, EOF) == EOF) + return n - to_do; + + // Try to maintain alignment: write a whole number of blocks. + // dont_write is what gets left over. + int block_size = _ebuf - _base; + int dont_write = block_size >= 128 ? to_do % block_size : 0; + + _G_ssize_t count = to_do - dont_write; + if (do_write(s, count) == EOF) + return n - to_do; + to_do = dont_write; + + // Now write out the remainder. Normally, this will fit in the + // buffer, but it's somewhat messier for line-buffered files, + // so we let streambuf::sputn handle the general case. + if (dont_write) + to_do -= streambuf::sputn(s+count, dont_write); + } + return n - to_do; +} + +int filebuf::xsgetn(char *s, int n) +{ + // FIXME: OPTIMIZE THIS (specifically, when unbuffered()). + return streambuf::xsgetn(s, n); +} + +// Non-ANSI AT&T-ism: Default open protection. +const int filebuf::openprot = 0644; diff --git a/gnu/lib/libg++/iostream/floatconv.C b/gnu/lib/libg++/iostream/floatconv.C new file mode 100644 index 0000000000..fb70052c03 --- /dev/null +++ b/gnu/lib/libg++/iostream/floatconv.C @@ -0,0 +1,2416 @@ +#include +#ifdef USE_DTOA +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991 by AT&T. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR AT&T MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ + +/* Some cleaning up by Per Bothner, bothner@cygnus.com, 1992, 1993. */ + +/* Please send bug reports to + David M. Gay + AT&T Bell Laboratories, Room 2C-463 + 600 Mountain Avenue + Murray Hill, NJ 07974-2070 + U.S.A. + dmg@research.att.com or research!dmg + */ + +/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. + * + * This strtod returns a nearest machine number to the input decimal + * string (or sets errno to ERANGE). With IEEE arithmetic, ties are + * broken by the IEEE round-even rule. Otherwise ties are broken by + * biased rounding (add half and chop). + * + * Inspired loosely by William D. Clinger's paper "How to Read Floating + * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * + * 1. We only require IEEE, IBM, or VAX double-precision + * arithmetic (not IEEE double-extended). + * 2. We get by with floating-point arithmetic in a case that + * Clinger missed -- when we're computing d * 10^n + * for a small integer d and the integer n is not too + * much larger than 22 (the maximum integer k for which + * we can represent 10^k exactly), we may be able to + * compute (d*10^k) * 10^(e-k) with just one roundoff. + * 3. Rather than a bit-at-a-time adjustment of the binary + * result in the hard case, we use floating-point + * arithmetic to determine the adjustment to within + * one bit; only in really hard cases do we need to + * compute a second residual. + * 4. Because of 3., we don't need a large table of powers of 10 + * for ten-to-e (just some small tables, e.g. of 10^k + * for 0 <= k <= 22). + */ + +/* + * #define IEEE_8087 for IEEE-arithmetic machines where the least + * significant byte has the lowest address. + * #define IEEE_MC68k for IEEE-arithmetic machines where the most + * significant byte has the lowest address. + * #define Sudden_Underflow for IEEE-format machines without gradual + * underflow (i.e., that flush to zero on underflow). + * #define IBM for IBM mainframe-style floating-point arithmetic. + * #define VAX for VAX-style floating-point arithmetic. + * #define Unsigned_Shifts if >> does treats its left operand as unsigned. + * #define No_leftright to omit left-right logic in fast floating-point + * computation of dtoa. + * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3. + * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines + * that use extended-precision instructions to compute rounded + * products and quotients) with IBM. + * #define ROUND_BIASED for IEEE-format with biased rounding. + * #define Inaccurate_Divide for IEEE-format with correctly rounded + * products but inaccurate quotients, e.g., for Intel i860. + * #define Just_16 to store 16 bits per 32-bit long when doing high-precision + * integer arithmetic. Whether this speeds things up or slows things + * down depends on the machine and the number being converted. + * #define KR_headers for old-style C function headers. + */ + +#ifdef DEBUG +#include +#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} +#endif + +#include +#include +#define CONST const + +#include +#include +#ifndef __MATH_H__ +#include +#endif + +#ifdef Unsigned_Shifts +#define Sign_Extend(a,b) if (b < 0) a |= 0xffff0000; +#else +#define Sign_Extend(a,b) /*no-op*/ +#endif + +#if defined(__i386__) +#define IEEE_8087 +#endif + +#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1 + +#if FLT_RADIX==16 +#define IBM +#elif DBL_MANT_DIG==56 +#define VAX +#elif DBL_MANT_DIG==53 && DBL_MAX_10_EXP==308 +#define IEEE_Unknown +#else +Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. +#endif +#endif + +#ifdef IEEE_8087 +#define HIWORD 1 +#define LOWORD 0 +#define TEST_ENDIANNESS /* nothing */ +#elif defined(IEEE_MC68k) +#define HIWORD 0 +#define LOWORD 1 +#define TEST_ENDIANNESS /* nothing */ +#else +static int HIWORD = -1, LOWORD; +static void test_endianness() +{ + union doubleword { + double d; + unsigned long u[2]; + } dw; + dw.d = 10; + if (dw.u[0] != 0) /* big-endian */ + HIWORD=0, LOWORD=1; + else + HIWORD=1, LOWORD=0; +} +#define TEST_ENDIANNESS if (HIWORD<0) test_endianness(); +#endif +#define word0(x) ((unsigned long *)&x)[HIWORD] +#define word1(x) ((unsigned long *)&x)[LOWORD] + +/* The following definition of Storeinc is appropriate for MIPS processors. */ +#if defined(IEEE_8087) + defined(VAX) +#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ +((unsigned short *)a)[0] = (unsigned short)c, a++) +#elif defined(IEEE_MC68k) +#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ +((unsigned short *)a)[1] = (unsigned short)c, a++) +#else +#define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) +#endif + +/* #define P DBL_MANT_DIG */ +/* Ten_pmax = floor(P*log(2)/log(5)) */ +/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ +/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ +/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ + +#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(IEEE_Unknown) +#define Exp_shift 20 +#define Exp_shift1 20 +#define Exp_msk1 0x100000 +#define Exp_msk11 0x100000 +#define Exp_mask 0x7ff00000 +#define P 53 +#define Bias 1023 +#define IEEE_Arith +#define Emin (-1022) +#define Exp_1 0x3ff00000 +#define Exp_11 0x3ff00000 +#define Ebits 11 +#define Frac_mask 0xfffff +#define Frac_mask1 0xfffff +#define Ten_pmax 22 +#define Bletch 0x10 +#define Bndry_mask 0xfffff +#define Bndry_mask1 0xfffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 1 +#define Tiny0 0 +#define Tiny1 1 +#define Quick_max 14 +#define Int_max 14 +#define Infinite(x) (word0(x) == 0x7ff00000) /* sufficient test for here */ +#else +#undef Sudden_Underflow +#define Sudden_Underflow +#ifdef IBM +#define Exp_shift 24 +#define Exp_shift1 24 +#define Exp_msk1 0x1000000 +#define Exp_msk11 0x1000000 +#define Exp_mask 0x7f000000 +#define P 14 +#define Bias 65 +#define Exp_1 0x41000000 +#define Exp_11 0x41000000 +#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ +#define Frac_mask 0xffffff +#define Frac_mask1 0xffffff +#define Bletch 4 +#define Ten_pmax 22 +#define Bndry_mask 0xefffff +#define Bndry_mask1 0xffffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 4 +#define Tiny0 0x100000 +#define Tiny1 0 +#define Quick_max 14 +#define Int_max 15 +#else /* VAX */ +#define Exp_shift 23 +#define Exp_shift1 7 +#define Exp_msk1 0x80 +#define Exp_msk11 0x800000 +#define Exp_mask 0x7f80 +#define P 56 +#define Bias 129 +#define Exp_1 0x40800000 +#define Exp_11 0x4080 +#define Ebits 8 +#define Frac_mask 0x7fffff +#define Frac_mask1 0xffff007f +#define Ten_pmax 24 +#define Bletch 2 +#define Bndry_mask 0xffff007f +#define Bndry_mask1 0xffff007f +#define LSB 0x10000 +#define Sign_bit 0x8000 +#define Log2P 1 +#define Tiny0 0x80 +#define Tiny1 0 +#define Quick_max 15 +#define Int_max 15 +#endif +#endif + +#ifndef IEEE_Arith +#define ROUND_BIASED +#endif + +#ifdef RND_PRODQUOT +#define rounded_product(a,b) a = rnd_prod(a, b) +#define rounded_quotient(a,b) a = rnd_quot(a, b) +extern double rnd_prod(double, double), rnd_quot(double, double); +#else +#define rounded_product(a,b) a *= b +#define rounded_quotient(a,b) a /= b +#endif + +#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) +#define Big1 0xffffffff + +#ifndef Just_16 +/* When Pack_32 is not defined, we store 16 bits per 32-bit long. + * This makes some inner loops simpler and sometimes saves work + * during multiplications, but it often seems to make things slightly + * slower. Hence the default is now to store 32 bits per long. + */ +#ifndef Pack_32 +#define Pack_32 +#endif +#endif + +#define Kmax 15 + +extern "C" double _Xstrtod(const char *s00, char **se); +extern "C" char *dtoa(double d, int mode, int ndigits, + int *decpt, int *sign, char **rve); + +struct Bigint { + /* Note: Order of fields is significant: Cfr. Bcopy macro. */ + struct Bigint *next; + int k; /* Parameter given to Balloc(k) */ + int maxwds; /* Allocated space: equals 1<next; + } + else { + x = 1 << k; + rv = (Bigint *)malloc(sizeof(Bigint) + (x-1)*sizeof(long)); + rv->k = k; + rv->maxwds = x; + } + rv->sign = rv->wds = 0; + return rv; + } + + static void +Bfree +#ifdef KR_headers + (v) Bigint *v; +#else + (Bigint *v) +#endif +{ + if (v) { + v->next = freelist[v->k]; + freelist[v->k] = v; + } + } + +#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ +y->wds*sizeof(long) + 2*sizeof(int)) + +/* Return b*m+a. b is modified. */ + + static Bigint * +multadd +#ifdef KR_headers + (b, m, a) Bigint *b; int m, a; +#else + (Bigint *b, int m, int a) +#endif +{ + int i, wds; + unsigned long *x, y; +#ifdef Pack_32 + unsigned long xi, z; +#endif + Bigint *b1; + + wds = b->wds; + x = b->x; + i = 0; + do { +#ifdef Pack_32 + xi = *x; + y = (xi & 0xffff) * m + a; + z = (xi >> 16) * m + (y >> 16); + a = (int)(z >> 16); + *x++ = (z << 16) + (y & 0xffff); +#else + y = *x * m + a; + a = (int)(y >> 16); + *x++ = y & 0xffff; +#endif + } + while(++i < wds); + if (a) { + if (wds >= b->maxwds) { + b1 = Balloc(b->k+1); + Bcopy(b1, b); + Bfree(b); + b = b1; + } + b->x[wds++] = a; + b->wds = wds; + } + return b; + } + + static Bigint * +s2b +#ifdef KR_headers + (s, nd0, nd, y9) CONST char *s; int nd0, nd; unsigned long y9; +#else + (CONST char *s, int nd0, int nd, unsigned long y9) +#endif +{ + Bigint *b; + int i, k; + long x, y; + + x = (nd + 8) / 9; + for(k = 0, y = 1; x > y; y <<= 1, k++) ; +#ifdef Pack_32 + b = Balloc(k); + b->x[0] = y9; + b->wds = 1; +#else + b = Balloc(k+1); + b->x[0] = y9 & 0xffff; + b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; +#endif + + i = 9; + if (9 < nd0) { + s += 9; + do b = multadd(b, 10, *s++ - '0'); + while(++i < nd0); + s++; + } + else + s += 10; + for(; i < nd; i++) + b = multadd(b, 10, *s++ - '0'); + return b; + } + + static int +hi0bits +#ifdef KR_headers + (x) register unsigned long x; +#else + (register unsigned long x) +#endif +{ + register int k = 0; + + if (!(x & 0xffff0000)) { + k = 16; + x <<= 16; + } + if (!(x & 0xff000000)) { + k += 8; + x <<= 8; + } + if (!(x & 0xf0000000)) { + k += 4; + x <<= 4; + } + if (!(x & 0xc0000000)) { + k += 2; + x <<= 2; + } + if (!(x & 0x80000000)) { + k++; + if (!(x & 0x40000000)) + return 32; + } + return k; + } + + static int +lo0bits +#ifdef KR_headers + (y) unsigned long *y; +#else + (unsigned long *y) +#endif +{ + register int k; + register unsigned long x = *y; + + if (x & 7) { + if (x & 1) + return 0; + if (x & 2) { + *y = x >> 1; + return 1; + } + *y = x >> 2; + return 2; + } + k = 0; + if (!(x & 0xffff)) { + k = 16; + x >>= 16; + } + if (!(x & 0xff)) { + k += 8; + x >>= 8; + } + if (!(x & 0xf)) { + k += 4; + x >>= 4; + } + if (!(x & 0x3)) { + k += 2; + x >>= 2; + } + if (!(x & 1)) { + k++; + x >>= 1; + if (!x & 1) + return 32; + } + *y = x; + return k; + } + + static Bigint * +i2b +#ifdef KR_headers + (i) int i; +#else + (int i) +#endif +{ + Bigint *b; + + b = Balloc(1); + b->x[0] = i; + b->wds = 1; + return b; + } + + static Bigint * +mult +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int k, wa, wb, wc; + unsigned long carry, y, z; + unsigned long *x, *xa, *xae, *xb, *xbe, *xc, *xc0; +#ifdef Pack_32 + unsigned long z2; +#endif + + if (a->wds < b->wds) { + c = a; + a = b; + b = c; + } + k = a->k; + wa = a->wds; + wb = b->wds; + wc = wa + wb; + if (wc > a->maxwds) + k++; + c = Balloc(k); + for(x = c->x, xa = x + wc; x < xa; x++) + *x = 0; + xa = a->x; + xae = xa + wa; + xb = b->x; + xbe = xb + wb; + xc0 = c->x; +#ifdef Pack_32 + for(; xb < xbe; xb++, xc0++) { + if (y = *xb & 0xffff) { + x = xa; + xc = xc0; + carry = 0; + do { + z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; + carry = z >> 16; + z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; + carry = z2 >> 16; + Storeinc(xc, z2, z); + } + while(x < xae); + *xc = carry; + } + if (y = *xb >> 16) { + x = xa; + xc = xc0; + carry = 0; + z2 = *xc; + do { + z = (*x & 0xffff) * y + (*xc >> 16) + carry; + carry = z >> 16; + Storeinc(xc, z, z2); + z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; + carry = z2 >> 16; + } + while(x < xae); + *xc = z2; + } + } +#else + for(; xb < xbe; xc0++) { + if (y = *xb++) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * y + *xc + carry; + carry = z >> 16; + *xc++ = z & 0xffff; + } + while(x < xae); + *xc = carry; + } + } +#endif + for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; + c->wds = wc; + return c; + } + + static Bigint *p5s; + +/* Returns b*(5**k). b is modified. */ + + static Bigint * +pow5mult +#ifdef KR_headers + (b, k) Bigint *b; int k; +#else + (Bigint *b, int k) +#endif +{ + Bigint *b1, *p5, *p51; + int i; + static int p05[3] = { 5, 25, 125 }; + + if (i = k & 3) + b = multadd(b, p05[i-1], 0); + + if (!(k >>= 2)) + return b; + if (!(p5 = p5s)) { + /* first time */ + p5 = p5s = i2b(625); + p5->next = 0; + } + for(;;) { + if (k & 1) { + b1 = mult(b, p5); + Bfree(b); + b = b1; + } + if (!(k >>= 1)) + break; + if (!(p51 = p5->next)) { + p51 = p5->next = mult(p5,p5); + p51->next = 0; + } + p5 = p51; + } + return b; + } + + static Bigint * +lshift +#ifdef KR_headers + (b, k) Bigint *b; int k; +#else + (Bigint *b, int k) +#endif +{ + int i, k1, n, n1; + Bigint *b1; + unsigned long *x, *x1, *xe, z; + +#ifdef Pack_32 + n = k >> 5; +#else + n = k >> 4; +#endif + k1 = b->k; + n1 = n + b->wds + 1; + for(i = b->maxwds; n1 > i; i <<= 1) + k1++; + b1 = Balloc(k1); + x1 = b1->x; + for(i = 0; i < n; i++) + *x1++ = 0; + x = b->x; + xe = x + b->wds; +#ifdef Pack_32 + if (k &= 0x1f) { + k1 = 32 - k; + z = 0; + do { + *x1++ = *x << k | z; + z = *x++ >> k1; + } + while(x < xe); + if (*x1 = z) + ++n1; + } +#else + if (k &= 0xf) { + k1 = 16 - k; + z = 0; + do { + *x1++ = *x << k & 0xffff | z; + z = *x++ >> k1; + } + while(x < xe); + if (*x1 = z) + ++n1; + } +#endif + else do + *x1++ = *x++; + while(x < xe); + b1->wds = n1 - 1; + Bfree(b); + return b1; + } + + static int +cmp +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + unsigned long *xa, *xa0, *xb, *xb0; + int i, j; + + i = a->wds; + j = b->wds; +#ifdef DEBUG + if (i > 1 && !a->x[i-1]) + Bug("cmp called with a->x[a->wds-1] == 0"); + if (j > 1 && !b->x[j-1]) + Bug("cmp called with b->x[b->wds-1] == 0"); +#endif + if (i -= j) + return i; + xa0 = a->x; + xa = xa0 + j; + xb0 = b->x; + xb = xb0 + j; + for(;;) { + if (*--xa != *--xb) + return *xa < *xb ? -1 : 1; + if (xa <= xa0) + break; + } + return 0; + } + + static Bigint * +diff +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int i, wa, wb; + long borrow, y; /* We need signed shifts here. */ + unsigned long *xa, *xae, *xb, *xbe, *xc; +#ifdef Pack_32 + long z; +#endif + + i = cmp(a,b); + if (!i) { + c = Balloc(0); + c->wds = 1; + c->x[0] = 0; + return c; + } + if (i < 0) { + c = a; + a = b; + b = c; + i = 1; + } + else + i = 0; + c = Balloc(a->k); + c->sign = i; + wa = a->wds; + xa = a->x; + xae = xa + wa; + wb = b->wds; + xb = b->x; + xbe = xb + wb; + xc = c->x; + borrow = 0; +#ifdef Pack_32 + do { + y = (*xa & 0xffff) - (*xb & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + z = (*xa++ >> 16) - (*xb++ >> 16) + borrow; + borrow = z >> 16; + Sign_Extend(borrow, z); + Storeinc(xc, z, y); + } + while(xb < xbe); + while(xa < xae) { + y = (*xa & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + z = (*xa++ >> 16) + borrow; + borrow = z >> 16; + Sign_Extend(borrow, z); + Storeinc(xc, z, y); + } +#else + do { + y = *xa++ - *xb++ + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + *xc++ = y & 0xffff; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + *xc++ = y & 0xffff; + } +#endif + while(!*--xc) + wa--; + c->wds = wa; + return c; + } + + static double +ulp +#ifdef KR_headers + (x) double x; +#else + (double x) +#endif +{ + register long L; + double a; + + L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; +#ifndef Sudden_Underflow + if (L > 0) { +#endif +#ifdef IBM + L |= Exp_msk1 >> 4; +#endif + word0(a) = L; + word1(a) = 0; +#ifndef Sudden_Underflow + } + else { + L = -L >> Exp_shift; + if (L < Exp_shift) { + word0(a) = 0x80000 >> L; + word1(a) = 0; + } + else { + word0(a) = 0; + L -= Exp_shift; + word1(a) = L >= 31 ? 1 : 1 << 31 - L; + } + } +#endif + return a; + } + + static double +b2d +#ifdef KR_headers + (a, e) Bigint *a; int *e; +#else + (Bigint *a, int *e) +#endif +{ + unsigned long *xa, *xa0, w, y, z; + int k; + double d; +#ifdef VAX + unsigned long d0, d1; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + + xa0 = a->x; + xa = xa0 + a->wds; + y = *--xa; +#ifdef DEBUG + if (!y) Bug("zero y in b2d"); +#endif + k = hi0bits(y); + *e = 32 - k; +#ifdef Pack_32 + if (k < Ebits) { + d0 = Exp_1 | y >> Ebits - k; + w = xa > xa0 ? *--xa : 0; + d1 = y << (32-Ebits) + k | w >> Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + if (k -= Ebits) { + d0 = Exp_1 | y << k | z >> 32 - k; + y = xa > xa0 ? *--xa : 0; + d1 = z << k | y >> 32 - k; + } + else { + d0 = Exp_1 | y; + d1 = z; + } +#else + if (k < Ebits + 16) { + z = xa > xa0 ? *--xa : 0; + d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k; + w = xa > xa0 ? *--xa : 0; + y = xa > xa0 ? *--xa : 0; + d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + w = xa > xa0 ? *--xa : 0; + k -= Ebits + 16; + d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; + y = xa > xa0 ? *--xa : 0; + d1 = w << k + 16 | y << k; +#endif + ret_d: +#ifdef VAX + word0(d) = d0 >> 16 | d0 << 16; + word1(d) = d1 >> 16 | d1 << 16; +#else +#undef d0 +#undef d1 +#endif + return d; + } + + static Bigint * +d2b +#ifdef KR_headers + (d, e, bits) double d; int *e, *bits; +#else + (double d, int *e, int *bits) +#endif +{ + Bigint *b; + int de, i, k; + unsigned long *x, y, z; +#ifdef VAX + unsigned long d0, d1; + d0 = word0(d) >> 16 | word0(d) << 16; + d1 = word1(d) >> 16 | word1(d) << 16; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + +#ifdef Pack_32 + b = Balloc(1); +#else + b = Balloc(2); +#endif + x = b->x; + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ + + de = (int)(d0 >> Exp_shift); /* The exponent part of d. */ + + /* Put back the suppressed high-order bit, if normalized. */ +#ifndef IBM +#ifndef Sudden_Underflow + if (de) +#endif + z |= Exp_msk1; +#endif + +#ifdef Pack_32 + if (y = d1) { + if (k = lo0bits(&y)) { + x[0] = y | z << 32 - k; + z >>= k; + } + else + x[0] = y; + i = b->wds = (x[1] = z) ? 2 : 1; + } + else { +#ifdef DEBUG + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + x[0] = z; + i = b->wds = 1; + k += 32; + } +#else + if (y = d1) { + if (k = lo0bits(&y)) + if (k >= 16) { + x[0] = y | z << 32 - k & 0xffff; + x[1] = z >> k - 16 & 0xffff; + x[2] = z >> k; + i = 2; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16 | z << 16 - k & 0xffff; + x[2] = z >> k & 0xffff; + x[3] = z >> k+16; + i = 3; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16; + x[2] = z & 0xffff; + x[3] = z >> 16; + i = 3; + } + } + else { +#ifdef DEBUG + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + if (k >= 16) { + x[0] = z; + i = 0; + } + else { + x[0] = z & 0xffff; + x[1] = z >> 16; + i = 1; + } + k += 32; + } + while(!x[i]) + --i; + b->wds = i + 1; +#endif +#ifndef Sudden_Underflow + if (de) { +#endif +#ifdef IBM + *e = (de - Bias - (P-1) << 2) + k; + *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask); +#else + *e = de - Bias - (P-1) + k; + *bits = P - k; +#endif +#ifndef Sudden_Underflow + } + else { + *e = de - Bias - (P-1) + 1 + k; +#ifdef Pack_32 + *bits = 32*i - hi0bits(x[i-1]); +#else + *bits = (i+2)*16 - hi0bits(x[i]); +#endif + } +#endif + return b; + } +#undef d0 +#undef d1 + + static double +ratio +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + double da, db; + int k, ka, kb; + + da = b2d(a, &ka); + db = b2d(b, &kb); +#ifdef Pack_32 + k = ka - kb + 32*(a->wds - b->wds); +#else + k = ka - kb + 16*(a->wds - b->wds); +#endif +#ifdef IBM + if (k > 0) { + word0(da) += (k >> 2)*Exp_msk1; + if (k &= 3) + da *= 1 << k; + } + else { + k = -k; + word0(db) += (k >> 2)*Exp_msk1; + if (k &= 3) + db *= 1 << k; + } +#else + if (k > 0) + word0(da) += k*Exp_msk1; + else { + k = -k; + word0(db) += k*Exp_msk1; + } +#endif + return da / db; + } + + static double +tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 +#ifdef VAX + , 1e23, 1e24 +#endif + }; + + static double +#ifdef IEEE_Arith +bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +static double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 }; +#define n_bigtens 5 +#else +#ifdef IBM +bigtens[] = { 1e16, 1e32, 1e64 }; +static double tinytens[] = { 1e-16, 1e-32, 1e-64 }; +#define n_bigtens 3 +#else +bigtens[] = { 1e16, 1e32 }; +static double tinytens[] = { 1e-16, 1e-32 }; +#define n_bigtens 2 +#endif +#endif + + double +_Xstrtod +#ifdef KR_headers + (s00, se) CONST char *s00; char **se; +#else + (CONST char *s00, char **se) +#endif +{ + int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, + e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; + CONST char *s, *s0, *s1; + double aadj, aadj1, adj, rv, rv0; + long L; + unsigned long y, z; + Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; + TEST_ENDIANNESS; + sign = nz0 = nz = 0; + rv = 0.; + for(s = s00;;s++) switch(*s) { + case '-': + sign = 1; + /* no break */ + case '+': + if (*++s) + goto break2; + /* no break */ + case 0: + goto ret; + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + continue; + default: + goto break2; + } + break2: + if (*s == '0') { + nz0 = 1; + while(*++s == '0') ; + if (!*s) + goto ret; + } + s0 = s; + y = z = 0; + for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 9) + y = 10*y + c - '0'; + else if (nd < 16) + z = 10*z + c - '0'; + nd0 = nd; + if (c == '.') { + c = *++s; + if (!nd) { + for(; c == '0'; c = *++s) + nz++; + if (c > '0' && c <= '9') { + s0 = s; + nf += nz; + nz = 0; + goto have_dig; + } + goto dig_done; + } + for(; c >= '0' && c <= '9'; c = *++s) { + have_dig: + nz++; + if (c -= '0') { + nf += nz; + for(i = 1; i < nz; i++) + if (nd++ < 9) + y *= 10; + else if (nd <= DBL_DIG + 1) + z *= 10; + if (nd++ < 9) + y = 10*y + c; + else if (nd <= DBL_DIG + 1) + z = 10*z + c; + nz = 0; + } + } + } + dig_done: + e = 0; + if (c == 'e' || c == 'E') { + if (!nd && !nz && !nz0) { + s = s00; + goto ret; + } + s00 = s; + esign = 0; + switch(c = *++s) { + case '-': + esign = 1; + case '+': + c = *++s; + } + if (c >= '0' && c <= '9') { + while(c == '0') + c = *++s; + if (c > '0' && c <= '9') { + e = c - '0'; + s1 = s; + while((c = *++s) >= '0' && c <= '9') + e = 10*e + c - '0'; + if (s - s1 > 8) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + e = 9999999; + if (esign) + e = -e; + } + else + e = 0; + } + else + s = s00; + } + if (!nd) { + if (!nz && !nz0) + s = s00; + goto ret; + } + e1 = e -= nf; + + /* Now we have nd0 digits, starting at s0, followed by a + * decimal point, followed by nd-nd0 digits. The number we're + * after is the integer represented by those digits times + * 10**e */ + + if (!nd0) + nd0 = nd; + k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; + rv = y; + if (k > 9) + rv = tens[k - 9] * rv + z; + if (nd <= DBL_DIG +#ifndef RND_PRODQUOT + && FLT_ROUNDS == 1 +#endif + ) { + if (!e) + goto ret; + if (e > 0) { + if (e <= Ten_pmax) { +#ifdef VAX + goto vax_ovfl_check; +#else + /* rv = */ rounded_product(rv, tens[e]); + goto ret; +#endif + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ + e -= i; + rv *= tens[i]; +#ifdef VAX + /* VAX exponent range is so narrow we must + * worry about overflow here... + */ + vax_ovfl_check: + word0(rv) -= P*Exp_msk1; + /* rv = */ rounded_product(rv, tens[e]); + if ((word0(rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) + goto ovfl; + word0(rv) += P*Exp_msk1; +#else + /* rv = */ rounded_product(rv, tens[e]); +#endif + goto ret; + } + } +#ifndef Inaccurate_Divide + else if (e >= -Ten_pmax) { + /* rv = */ rounded_quotient(rv, tens[-e]); + goto ret; + } +#endif + } + e1 += nd - k; + + /* Get starting approximation = rv * 10**e1 */ + + if (e1 > 0) { + if (i = e1 & 15) + rv *= tens[i]; + if (e1 &= ~15) { + if (e1 > DBL_MAX_10_EXP) { + ovfl: + errno = ERANGE; +#ifndef HUGE_VAL +#define HUGE_VAL 1.7976931348623157E+308 +#endif + rv = HUGE_VAL; + goto ret; + } + if (e1 >>= 4) { + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + rv *= bigtens[j]; + /* The last multiplication could overflow. */ + word0(rv) -= P*Exp_msk1; + rv *= bigtens[j]; + if ((z = word0(rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-P)) + goto ovfl; + if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { + /* set to largest number */ + /* (Can't trust DBL_MAX) */ + word0(rv) = Big0; + word1(rv) = Big1; + } + else + word0(rv) += P*Exp_msk1; + } + + } + } + else if (e1 < 0) { + e1 = -e1; + if (i = e1 & 15) + rv /= tens[i]; + if (e1 &= ~15) { + e1 >>= 4; + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + rv *= tinytens[j]; + /* The last multiplication could underflow. */ + rv0 = rv; + rv *= tinytens[j]; + if (!rv) { + rv = 2.*rv0; + rv *= tinytens[j]; + if (!rv) { + undfl: + rv = 0.; + errno = ERANGE; + goto ret; + } + word0(rv) = Tiny0; + word1(rv) = Tiny1; + /* The refinement below will clean + * this approximation up. + */ + } + } + } + + /* Now the hard part -- adjusting rv to the correct value.*/ + + /* Put digits into bd: true value = bd * 10^e */ + + bd0 = s2b(s0, nd0, nd, y); + + for(;;) { + bd = Balloc(bd0->k); + Bcopy(bd, bd0); + bb = d2b(rv, &bbe, &bbbits); /* rv = bb * 2^bbe */ + bs = i2b(1); + + if (e >= 0) { + bb2 = bb5 = 0; + bd2 = bd5 = e; + } + else { + bb2 = bb5 = -e; + bd2 = bd5 = 0; + } + if (bbe >= 0) + bb2 += bbe; + else + bd2 -= bbe; + bs2 = bb2; +#ifdef Sudden_Underflow +#ifdef IBM + j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); +#else + j = P + 1 - bbbits; +#endif +#else + i = bbe + bbbits - 1; /* logb(rv) */ + if (i < Emin) /* denormal */ + j = bbe + (P-Emin); + else + j = P + 1 - bbbits; +#endif + bb2 += j; + bd2 += j; + i = bb2 < bd2 ? bb2 : bd2; + if (i > bs2) + i = bs2; + if (i > 0) { + bb2 -= i; + bd2 -= i; + bs2 -= i; + } + if (bb5 > 0) { + bs = pow5mult(bs, bb5); + bb1 = mult(bs, bb); + Bfree(bb); + bb = bb1; + } + if (bb2 > 0) + bb = lshift(bb, bb2); + if (bd5 > 0) + bd = pow5mult(bd, bd5); + if (bd2 > 0) + bd = lshift(bd, bd2); + if (bs2 > 0) + bs = lshift(bs, bs2); + delta = diff(bb, bd); + dsign = delta->sign; + delta->sign = 0; + i = cmp(delta, bs); + if (i < 0) { + /* Error is less than half an ulp -- check for + * special case of mantissa a power of two. + */ + if (dsign || word1(rv) || word0(rv) & Bndry_mask) + break; + delta = lshift(delta,Log2P); + if (cmp(delta, bs) > 0) + goto drop_down; + break; + } + if (i == 0) { + /* exactly half-way between */ + if (dsign) { + if ((word0(rv) & Bndry_mask1) == Bndry_mask1 + && word1(rv) == 0xffffffff) { + /*boundary case -- increment exponent*/ + word0(rv) = (word0(rv) & Exp_mask) + + Exp_msk1 +#ifdef IBM + | Exp_msk1 >> 4 +#endif + ; + word1(rv) = 0; + break; + } + } + else if (!(word0(rv) & Bndry_mask) && !word1(rv)) { + drop_down: + /* boundary case -- decrement exponent */ +#ifdef Sudden_Underflow + L = word0(rv) & Exp_mask; +#ifdef IBM + if (L < Exp_msk1) +#else + if (L <= Exp_msk1) +#endif + goto undfl; + L -= Exp_msk1; +#else + L = (word0(rv) & Exp_mask) - Exp_msk1; +#endif + word0(rv) = L | Bndry_mask1; + word1(rv) = 0xffffffff; +#ifdef IBM + goto cont; +#else + break; +#endif + } +#ifndef ROUND_BIASED + if (!(word1(rv) & LSB)) + break; +#endif + if (dsign) + rv += ulp(rv); +#ifndef ROUND_BIASED + else { + rv -= ulp(rv); +#ifndef Sudden_Underflow + if (!rv) + goto undfl; +#endif + } +#endif + break; + } + if ((aadj = ratio(delta, bs)) <= 2.) { + if (dsign) + aadj = aadj1 = 1.; + else if (word1(rv) || word0(rv) & Bndry_mask) { +#ifndef Sudden_Underflow + if (word1(rv) == Tiny1 && !word0(rv)) + goto undfl; +#endif + aadj = 1.; + aadj1 = -1.; + } + else { + /* special case -- power of FLT_RADIX to be */ + /* rounded down... */ + + if (aadj < 2./FLT_RADIX) + aadj = 1./FLT_RADIX; + else + aadj *= 0.5; + aadj1 = -aadj; + } + } + else { + aadj *= 0.5; + aadj1 = dsign ? aadj : -aadj; +#ifdef Check_FLT_ROUNDS + switch(FLT_ROUNDS) { + case 2: /* towards +infinity */ + aadj1 -= 0.5; + break; + case 0: /* towards 0 */ + case 3: /* towards -infinity */ + aadj1 += 0.5; + } +#else + if (FLT_ROUNDS == 0) + aadj1 += 0.5; +#endif + } + y = word0(rv) & Exp_mask; + + /* Check for overflow */ + + if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { + rv0 = rv; + word0(rv) -= P*Exp_msk1; + adj = aadj1 * ulp(rv); + rv += adj; + if ((word0(rv) & Exp_mask) >= + Exp_msk1*(DBL_MAX_EXP+Bias-P)) { + if (word0(rv0) == Big0 && word1(rv0) == Big1) + goto ovfl; + word0(rv) = Big0; + word1(rv) = Big1; + goto cont; + } + else + word0(rv) += P*Exp_msk1; + } + else { +#ifdef Sudden_Underflow + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { + rv0 = rv; + word0(rv) += P*Exp_msk1; + adj = aadj1 * ulp(rv); + rv += adj; +#ifdef IBM + if ((word0(rv) & Exp_mask) < P*Exp_msk1) +#else + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) +#endif + { + if (word0(rv0) == Tiny0 + && word1(rv0) == Tiny1) + goto undfl; + word0(rv) = Tiny0; + word1(rv) = Tiny1; + goto cont; + } + else + word0(rv) -= P*Exp_msk1; + } + else { + adj = aadj1 * ulp(rv); + rv += adj; + } +#else + /* Compute adj so that the IEEE rounding rules will + * correctly round rv + adj in some half-way cases. + * If rv * ulp(rv) is denormalized (i.e., + * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid + * trouble from bits lost to denormalization; + * example: 1.2e-307 . + */ + if (y <= (P-1)*Exp_msk1 && aadj >= 1.) { + aadj1 = (double)(int)(aadj + 0.5); + if (!dsign) + aadj1 = -aadj1; + } + adj = aadj1 * ulp(rv); + rv += adj; +#endif + } + z = word0(rv) & Exp_mask; + if (y == z) { + /* Can we stop now? */ + L = (long)aadj; + aadj -= L; + /* The tolerances below are conservative. */ + if (dsign || word1(rv) || word0(rv) & Bndry_mask) { + if (aadj < .4999999 || aadj > .5000001) + break; + } + else if (aadj < .4999999/FLT_RADIX) + break; + } + cont: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(delta); + } + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); + ret: + if (se) + *se = (char *)s; + return sign ? -rv : rv; + } + + static int +quorem +#ifdef KR_headers + (b, S) Bigint *b, *S; +#else + (Bigint *b, Bigint *S) +#endif +{ + int n; + long borrow, y; + unsigned long carry, q, ys; + unsigned long *bx, *bxe, *sx, *sxe; +#ifdef Pack_32 + long z; + unsigned long si, zs; +#endif + + n = S->wds; +#ifdef DEBUG + /*debug*/ if (b->wds > n) + /*debug*/ Bug("oversize b in quorem"); +#endif + if (b->wds < n) + return 0; + sx = S->x; + sxe = sx + --n; + bx = b->x; + bxe = bx + n; + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ +#ifdef DEBUG + /*debug*/ if (q > 9) + /*debug*/ Bug("oversized quotient in quorem"); +#endif + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + z = (*bx >> 16) - (zs & 0xffff) + borrow; + borrow = z >> 16; + Sign_Extend(borrow, z); + Storeinc(bx, z, y); +#else + ys = *sx++ * q + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + *bx++ = y & 0xffff; +#endif + } + while(sx <= sxe); + if (!*bxe) { + bx = b->x; + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b->x; + sx = S->x; + do { +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + z = (*bx >> 16) - (zs & 0xffff) + borrow; + borrow = z >> 16; + Sign_Extend(borrow, z); + Storeinc(bx, z, y); +#else + ys = *sx++ + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + *bx++ = y & 0xffff; +#endif + } + while(sx <= sxe); + bx = b->x; + bxe = bx + n; + if (!*bxe) { + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + return q; + } + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the long + * calculation. + */ + + char * +dtoa +#ifdef KR_headers + (d, mode, ndigits, decpt, sign, rve) + double d; int mode, ndigits, *decpt, *sign; char **rve; +#else + (double d, int mode, int ndigits, int *decpt, int *sign, char **rve) +#endif +{ + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4-9 should give the same return values as 2-3, i.e., + 4 <= mode <= 9 ==> same return as mode + 2 + (mode & 1). These modes are mainly for + debugging; often they run slower but sometimes + faster than modes 2-3. + 4,5,8,9 ==> left-to-right digit generation. + 6-9 ==> don't try fast floating-point estimate + (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + + int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, + j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, + spec_case, try_quick; + long L; +#ifndef Sudden_Underflow + int denorm; + unsigned long x; +#endif + Bigint *b, *b1, *delta, *mlo, *mhi, *S; + double d2, ds, eps; + char *s, *s0; + static Bigint *result; + static int result_k; + + TEST_ENDIANNESS; + if (result) { + result->k = result_k; + result->maxwds = 1 << result_k; + Bfree(result); + result = 0; + } + + if (word0(d) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + word0(d) &= ~Sign_bit; /* clear sign bit */ + } + else + *sign = 0; + +#if defined(IEEE_Arith) + defined(VAX) +#ifdef IEEE_Arith + if ((word0(d) & Exp_mask) == Exp_mask) +#else + if (word0(d) == 0x8000) +#endif + { + /* Infinity or NaN */ + *decpt = 9999; +#ifdef IEEE_Arith + if (!word1(d) && !(word0(d) & 0xfffff)) + { + s = "Infinity"; + if (*rve) + *rve = s + 8; + } + else +#endif + { + s = "NaN"; + if (rve) + *rve = s +3; + } + return s; + } +#endif +#ifdef IBM + d += 0; /* normalize */ +#endif + if (!d) { + *decpt = 1; + s = "0"; + if (rve) + *rve = s + 1; + return s; + } + + b = d2b(d, &be, &bbits); + i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#ifndef Sudden_Underflow + if (i) { +#endif + d2 = d; + word0(d2) &= Frac_mask1; + word0(d2) |= Exp_11; +#ifdef IBM + if (j = 11 - hi0bits(word0(d2) & Frac_mask)) + d2 /= 1 << j; +#endif + + i -= Bias; +#ifdef IBM + i <<= 2; + i += j; +#endif +#ifndef Sudden_Underflow + denorm = 0; + } + else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P-1) - 1); + x = i > 32 ? word0(d) << 64 - i | word1(d) >> i - 32 + : word1(d) << 32 - i; + d2 = x; + word0(d2) -= 31*Exp_msk1; /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; + } +#endif + + /* Now i is the unbiased base-2 exponent. */ + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = i*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = i*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i) by 0.301029995663981; since |i| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + k = (int)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (d < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } + if (mode < 0 || mode > 9) + mode = 0; + try_quick = 1; + if (mode > 5) { + mode -= 4; + try_quick = 0; + } + leftright = 1; + switch(mode) { + case 0: + case 1: + ilim = ilim1 = -1; + i = 18; + ndigits = 0; + break; + case 2: + leftright = 0; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = 0; + /* no break */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + /* i is now an upper bound of the number of digits to generate. */ + j = sizeof(unsigned long); + /* The test is <= so as to allow room for the final '\0'. */ + for(result_k = 0; sizeof(Bigint) - sizeof(unsigned long) + j <= i; + j <<= 1) result_k++; + result = Balloc(result_k); + s = s0 = (char *)result; + + if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + d2 = d; + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + d /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + d /= ds; + } + else if (j1 = -k) { + d *= tens[j1 & 0xf]; + for(j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + d *= bigtens[i]; + } + } + if (k_check && d < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + d *= 10.; + ieps++; + } + eps = ieps*d + 7.; + word0(eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + d -= 5.; + if (d > eps) + goto one_digit; + if (d < -eps) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + eps = 0.5/tens[ilim-1] - eps; + for(i = 0;;) { + L = (long)d; + d -= L; + *s++ = '0' + (int)L; + if (d < eps) + goto ret1; + if (1. - d < eps) + goto bump_up; + if (++i >= ilim) + break; + eps *= 10.; + d *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + eps *= tens[ilim-1]; + for(i = 1;; i++, d *= 10.) { + L = (long)d; + d -= L; + *s++ = '0' + (int)L; + if (i == ilim) { + if (d > 0.5 + eps) + goto bump_up; + else if (d < 0.5 - eps) { + while(*--s == '0'); + s++; + goto ret1; + } + break; + } + } +#ifndef No_leftright + } +#endif + fast_failed: + s = s0; + d = d2; + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || d <= 5*ds) + goto no_digits; + goto one_digit; + } + for(i = 1;; i++) { + L = (long)(d / ds); + d -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (d < 0) { + L--; + d += ds; + } +#endif + *s++ = '0' + (int)L; + if (i == ilim) { + d += d; + if (d > ds || d == ds && L & 1) { + bump_up: + while(*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + if (!(d *= 10.)) + break; + } + goto ret1; + } + + m2 = b2; + m5 = b5; + mhi = mlo = 0; + if (leftright) { + if (mode < 2) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif +#ifdef IBM + 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); +#else + 1 + P - bbits; +#endif + } + else { + j = ilim - 1; + if (m5 >= j) + m5 -= j; + else { + s5 += j -= m5; + b5 += j; + m5 = 0; + } + if ((i = ilim) < 0) { + m2 -= i; + i = 0; + } + } + b2 += i; + s2 += i; + mhi = i2b(1); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + b1 = mult(mhi, b); + Bfree(b); + b = b1; + } + if (j = b5 - m5) + b = pow5mult(b, j); + } + else + b = pow5mult(b, b5); + } + S = i2b(1); + if (s5 > 0) + S = pow5mult(S, s5); + + /* Check for special case that d is a normalized power of 2. */ + + if (mode < 2) { + if (!word1(d) && !(word0(d) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(d) & Exp_mask +#endif + ) { + /* The special case */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } + else + spec_case = 0; + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ +#ifdef Pack_32 + if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) + i = 32 - i; +#else + if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf) + i = 16 - i; +#endif + if (i > 4) { + i -= 4; + b2 += i; + m2 += i; + s2 += i; + } + else if (i < 4) { + i += 28; + b2 += i; + m2 += i; + s2 += i; + } + if (b2 > 0) + b = lshift(b, b2); + if (s2 > 0) + S = lshift(S, s2); + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0); /* we botched the k estimate */ + if (leftright) + mhi = multadd(mhi, 10, 0); + ilim = ilim1; + } + } + if (ilim <= 0 && mode > 2) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { + /* no digits, fcvt style */ + no_digits: + k = -1 - ndigits; + goto ret; + } + one_digit: + *s++ = '1'; + k++; + goto ret; + } + if (leftright) { + if (m2 > 0) + mhi = lshift(mhi, m2); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k); + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P); + } + + for(i = 1;;i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi); + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); +#ifndef ROUND_BIASED + if (j1 == 0 && !mode && !(word1(d) & 1)) { + if (dig == '9') + goto round_9_up; + if (j > 0) + dig++; + *s++ = dig; + goto ret; + } +#endif + if (j < 0 || j == 0 && !mode +#ifndef ROUND_BIASED + && !(word1(d) & 1) +#endif + ) { + if (j1 > 0) { + b = lshift(b, 1); + j1 = cmp(b, S); + if ((j1 > 0 || j1 == 0 && dig & 1) + && dig++ == '9') + goto round_9_up; + } + *s++ = dig; + goto ret; + } + if (j1 > 0) { + if (dig == '9') { /* possible if i == 1 */ + round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } + *s++ = dig; + if (i == ilim) + break; + b = multadd(b, 10, 0); + if (mlo == mhi) + mlo = mhi = multadd(mhi, 10, 0); + else { + mlo = multadd(mlo, 10, 0); + mhi = multadd(mhi, 10, 0); + } + } + } + else + for(i = 1;; i++) { + *s++ = dig = quorem(b,S) + '0'; + if (i >= ilim) + break; + b = multadd(b, 10, 0); + } + + /* Round off last digit */ + + b = lshift(b, 1); + j = cmp(b, S); + if (j > 0 || j == 0 && dig & 1) { + roundoff: + while(*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } + else { + while(*--s == '0'); + s++; + } + ret: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } + ret1: + Bfree(b); + *s = 0; + *decpt = k + 1; + if (rve) + *rve = s; + return s0; + } +#endif /* USE_DTOA */ diff --git a/gnu/lib/libg++/iostream/floatio.h b/gnu/lib/libg++/iostream/floatio.h new file mode 100644 index 0000000000..d9577b2b24 --- /dev/null +++ b/gnu/lib/libg++/iostream/floatio.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * %W% (Berkeley) %G% + */ + +/* + * Floating point scanf/printf (input/output) definitions. + */ + +/* 11-bit exponent (VAX G floating point) is 308 decimal digits */ +#define MAXEXP 308 +/* 128 bit fraction takes up 39 decimal digits; max reasonable precision */ +#define MAXFRACT 39 diff --git a/gnu/lib/libg++/iostream/fstream.C b/gnu/lib/libg++/iostream/fstream.C new file mode 100644 index 0000000000..9b45573ea0 --- /dev/null +++ b/gnu/lib/libg++/iostream/fstream.C @@ -0,0 +1,65 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1991 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifdef __GNUG__ +#pragma implementation +#endif +#define _STREAM_COMPAT +#include "ioprivate.h" +#include + +fstreambase::fstreambase() +{ + init(new filebuf()); +} + +fstreambase::fstreambase(int fd) +{ + init(new filebuf(fd)); +} + +fstreambase::fstreambase(const char *name, int mode, int prot) +{ + init(new filebuf()); + if (!rdbuf()->open(name, mode, prot)) + set(ios::badbit); +} + +void fstreambase::open(const char *name, int mode, int prot) +{ + clear(); + if (!rdbuf()->open(name, mode, prot)) + set(ios::badbit); +} + +void fstreambase::close() +{ + if (!rdbuf()->close()) + set(ios::failbit); +} + +#if 0 +static int mode_to_sys(enum open_mode mode) +{ + return O_WRONLY; +} + +static char* fopen_cmd_arg(io_mode i) +{ + return "w"; +} +#endif diff --git a/gnu/lib/libg++/iostream/fstream.h b/gnu/lib/libg++/iostream/fstream.h new file mode 100644 index 0000000000..495eea5d7a --- /dev/null +++ b/gnu/lib/libg++/iostream/fstream.h @@ -0,0 +1,70 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1991 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifndef _FSTREAM_H +#define _FSTREAM_H +#ifdef __GNUG__ +#pragma interface +#endif +#include + +class fstreambase : virtual public ios { + public: + fstreambase(); + fstreambase(int fd); + fstreambase(const char *name, int mode, int prot=0664); + void close(); + filebuf* rdbuf() const { return (filebuf*)_strbuf; } + void open(const char *name, int mode, int prot=0664); + int is_open() const { return rdbuf()->is_open(); } + void setbuf(char *ptr, int len) { rdbuf()->setbuf(ptr, len); } +#ifdef _STREAM_COMPAT + int filedesc() { return rdbuf()->fd(); } + fstreambase& raw() { rdbuf()->setbuf(NULL, 0); return *this; } +#endif +}; + +class ifstream : public fstreambase, public istream { + public: + ifstream() : fstreambase() { } + ifstream(int fd) : fstreambase(fd) { } + ifstream(const char *name, int mode=ios::in, int prot=0664) + : fstreambase(name, mode, prot) { } + void open(const char *name, int mode=ios::in, int prot=0664) + { fstreambase::open(name, mode, prot); } +}; + +class ofstream : public fstreambase, public ostream { + public: + ofstream() : fstreambase() { } + ofstream(int fd) : fstreambase(fd) { } + ofstream(const char *name, int mode=ios::out, int prot=0664) + : fstreambase(name, mode, prot) { } + void open(const char *name, int mode=ios::out, int prot=0664) + { fstreambase::open(name, mode, prot); } +}; + +class fstream : public fstreambase, public iostream { + public: + fstream() : fstreambase() { } + fstream(int fd) : fstreambase(fd) { } + fstream(const char *name, int mode, int prot=0664) + : fstreambase(name, mode, prot) { } + void open(const char *name, int mode, int prot=0664) + { fstreambase::open(name, mode, prot); } +}; +#endif /*!_FSTREAM_H*/ diff --git a/gnu/lib/libg++/iostream/igetline.C b/gnu/lib/libg++/iostream/igetline.C new file mode 100644 index 0000000000..90ed189fea --- /dev/null +++ b/gnu/lib/libg++/iostream/igetline.C @@ -0,0 +1,135 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1992 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#include "iostream.h" +#include + +istream& istream::getline(char* buf, int len, char delim) +{ + _gcount = 0; + if (ipfx1()) { + streambuf *sb = rdbuf(); + long count = sb->sgetline(buf, len, delim, -1); + if (count == len-1) + set(ios::failbit); + else { + int ch = sb->sbumpc(); + if (ch == EOF) + set(ios::failbit|ios::eofbit); + else if (ch == (unsigned char)delim) + count++; + else + sb->sungetc(); // Leave delimiter unread. + } + _gcount = count; + } + return *this; +} + +istream& istream::get(char* buf, int len, char delim) +{ + _gcount = 0; + if (ipfx1()) { + streambuf *sbuf = rdbuf(); + long count = sbuf->sgetline(buf, len, delim, -1); + if (count < 0 || (count == 0 && sbuf->sgetc() == EOF)) + set(ios::failbit|ios::eofbit); + else + _gcount = count; + } + return *this; +} + +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1992 Free Software Foundation. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +// from Doug Schmidt + +#define CHUNK_SIZE 512 + +/* Reads an arbitrarily long input line terminated by a user-specified + TERMINATOR. Super-nifty trick using recursion avoids unnecessary calls + to NEW! */ + +char *_sb_readline (streambuf *sb, long& total, char terminator) +{ + char buf[CHUNK_SIZE+1]; + char *ptr; + int ch; + + long count = sb->sgetline(buf, CHUNK_SIZE+1, terminator, -1); + if (count == EOF) + return NULL; + ch = sb->sbumpc(); + long old_total = total; + total += count; + if (ch != EOF && ch != terminator) { + total++; // Include ch in total. + ptr = _sb_readline(sb, total, terminator); + if (ptr) { + memcpy(ptr + old_total, buf, count); + ptr[old_total+count] = ch; + } + return ptr; + } + + if (ptr = new char[total+1]) { + ptr[total] = '\0'; + memcpy(ptr + total - count, buf, count); + return ptr; + } + else + return NULL; +} + +/* Reads an arbitrarily long input line terminated by TERMINATOR. + This routine allocates its own memory, so the user should + only supply the address of a (char *). */ + +istream& istream::gets(char **s, char delim /* = '\n' */) +{ + if (ipfx1()) { + long size = 0; + streambuf *sb = rdbuf(); + *s = _sb_readline (sb, size, delim); + _gcount = *s ? size : 0; + if (sb->_flags & _S_EOF_SEEN) { + set(ios::eofbit); + if (_gcount == 0) + set(ios::failbit); + } + } + else { + _gcount = 0; + *s = NULL; + } + return *this; +} diff --git a/gnu/lib/libg++/iostream/igetsb.C b/gnu/lib/libg++/iostream/igetsb.C new file mode 100644 index 0000000000..a6a2e6315d --- /dev/null +++ b/gnu/lib/libg++/iostream/igetsb.C @@ -0,0 +1,48 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1992 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#include "ioprivate.h" +#include "iostream.h" + +istream& istream::get(streambuf& sb, char delim /* = '\n' */) +{ + _gcount = 0; + if (ipfx1()) { + register streambuf* isb = rdbuf(); + for (;;) { + int len = isb->egptr() - isb->gptr(); + if (len <= 0) + if (isb->underflow() == EOF) + break; + else + len = isb->egptr() - isb->gptr(); + char *delimp = (char*)memchr((void*)isb->gptr(), delim, len); + if (delimp != NULL) + len = delimp - isb->gptr(); + int written = sb.sputn(isb->gptr(), len); + isb->gbump(written); + _gcount += written; + if (written != len) { + set(ios::failbit); + break; + } + if (delimp != NULL) + break; + } + } + return *this; +} diff --git a/gnu/lib/libg++/iostream/indstream.C b/gnu/lib/libg++/iostream/indstream.C new file mode 100644 index 0000000000..b58e362d4b --- /dev/null +++ b/gnu/lib/libg++/iostream/indstream.C @@ -0,0 +1,108 @@ +// This is part of the iostream library, providing -*- C++ -*- input/output. +// Copyright (C) 1992 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifdef __GNUG__ +#pragma implementation +#endif + +#include + +indirectbuf::indirectbuf(streambuf *get, streambuf *put, int delete_mode) +: streambuf() +{ + _get_stream = get; + _put_stream = put == NULL ? get : put; + _delete_flags = delete_mode; +} + +indirectbuf::~indirectbuf() +{ + if (_delete_flags & ios::in) delete get_stream(); + if (_delete_flags & ios::out) delete put_stream(); +} + +int indirectbuf::xsputn(const char* s, int n) +{ + return put_stream()->sputn(s, n); +} + +int indirectbuf::xsgetn(char* s, int n) +{ + return get_stream()->sgetn(s, n); +} + +int indirectbuf::overflow(int c /* = EOF */) +{ + if (c == EOF) + return put_stream()->overflow(c); + else + return put_stream()->sputc(c); +} + +int indirectbuf::underflow() +{ + return get_stream()->sbumpc(); +} + +streampos indirectbuf::seekoff(streamoff off, _seek_dir dir, int mode) +{ + int ret_val = 0; + int select = mode == 0 ? (ios::in|ios::out) : mode; + streambuf *gbuf = (select & ios::in) ? get_stream() : NULL; + streambuf *pbuf = (select & ios::out) ? put_stream() : NULL; + if (gbuf == pbuf) + ret_val = gbuf->seekoff(off, dir, mode); + else { + if (gbuf) + ret_val = gbuf->seekoff(off, dir, ios::in); + if (pbuf && ret_val != EOF) + ret_val = pbuf->seekoff(off, dir, ios::out); + } + return ret_val; +} + +streampos indirectbuf::seekpos(streampos pos, int mode) +{ + int ret_val = EOF; + int select = mode == 0 ? (ios::in|ios::out) : mode; + streambuf *gbuf = (select & ios::in) ? get_stream() : NULL; + streambuf *pbuf = (select & ios::out) ? put_stream() : NULL; + if (gbuf == pbuf) + ret_val = gbuf->seekpos(pos, mode); + else { + if (gbuf) + ret_val = gbuf->seekpos(pos, ios::in); + if (pbuf && ret_val != EOF) + ret_val = pbuf->seekpos(pos, ios::out); + } + return ret_val; +} + +int indirectbuf::sync() +{ + streambuf *gbuf = get_stream(); + int ret_val = gbuf->sync(); + if (ret_val == EOF) return ret_val; + streambuf *pbuf = put_stream(); + if (pbuf != gbuf) return pbuf->sync(); + else return ret_val; +} + +int indirectbuf::pbackfail(int c) +{ + return get_stream()->sputbackc(c); +} diff --git a/gnu/lib/libg++/iostream/indstream.h b/gnu/lib/libg++/iostream/indstream.h new file mode 100644 index 0000000000..109928b4d5 --- /dev/null +++ b/gnu/lib/libg++/iostream/indstream.h @@ -0,0 +1,65 @@ +// This is part of the iostream library, providing -*- C++ -*- input/output. +// Copyright (C) 1992 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifndef _INDSTREAM_H +#define _INDSTREAM_H + +#ifdef __GNUG__ +#pragma interface +#endif + +#include + +// An indirectbuf is one that forwards all of its I/O requests +// to another streambuf. +// All get-related requests are sent to get_stream(). +// All put-related requests are sent to put_stream(). + +// An indirectbuf can be used to implement Common Lisp +// synonym-streams and two-way-streams. +// +// class synonymbuf : public indirectbuf { +// Symbol *sym; +// synonymbuf(Symbol *s) { sym = s; } +// virtual streambuf *lookup_stream(int mode) { +// return coerce_to_streambuf(lookup_value(sym)); } +// }; + +class indirectbuf : public streambuf { + protected: + streambuf *_get_stream; // Optional cache for get_stream(). + streambuf *_put_stream; // Optional cache for put_stream(). + int _delete_flags; + public: + streambuf *get_stream() + { return _get_stream ? _get_stream : lookup_stream(ios::in); } + streambuf *put_stream() + { return _put_stream ? _put_stream : lookup_stream(ios::out); } + virtual streambuf *lookup_stream(int/*mode*/) { return NULL; } // ERROR! + indirectbuf(streambuf *get=NULL, streambuf *put=NULL, int delete_mode=0); + virtual ~indirectbuf(); + virtual int xsputn(const char* s, int n); + virtual int xsgetn(char* s, int n); + virtual int underflow(); + virtual int overflow(int c = EOF); + virtual streampos seekoff(streamoff, _seek_dir, int mode=ios::in|ios::out); + virtual streampos seekpos(streampos pos, int mode = ios::in|ios::out); + virtual int sync(); + virtual int pbackfail(int c); +}; + +#endif /* !_INDSTREAM_H */ diff --git a/gnu/lib/libg++/iostream/iomanip.C b/gnu/lib/libg++/iostream/iomanip.C new file mode 100644 index 0000000000..b2aec86396 --- /dev/null +++ b/gnu/lib/libg++/iostream/iomanip.C @@ -0,0 +1,77 @@ +// -*- C++ -*- +// This is part of the iostream library, providing parametrized manipulators +// Written by Heinz G. Seidl, Copyright (C) 1992 Cygnus Support +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifdef __GNUG__ +//#pragma implementation +#endif + +#include "iomanip.h" + + +// Those functions are called through a pointer, +// thus it does not make sense, to inline them. + +ios & __iomanip_setbase (ios& i, int n) +{ + ios::fmtflags b; + switch (n) + { + case 8: + b = ios::oct; break; + case 10: + b = ios::dec; break; + case 16: + b = ios::hex; break; + default: + b = 0; + } + i.setf(b, ios::basefield); + return i; +} + +ios & __iomanip_setfill (ios& i, int n) +{ + //FIXME if ( i.flags() & ios::widechar ) + i.fill( (char) n); + //FIXME else + //FIXME i.fill( (wchar) n); + return i; +} + +ios & __iomanip_setprecision (ios& i, int n) +{ + i.precision(n); + return i; +} +ios & __iomanip_setw (ios& i, int n) +{ + i.width(n); + return i; +} + +ios & __iomanip_setiosflags (ios& i, ios::fmtflags n) +{ + i.setf(n,n); + return i; +} + +ios & __iomanip_resetiosflags (ios& i, ios::fmtflags n) +{ + i.setf(0,n); + return i; +} diff --git a/gnu/lib/libg++/iostream/iomanip.h b/gnu/lib/libg++/iostream/iomanip.h new file mode 100644 index 0000000000..826e25909c --- /dev/null +++ b/gnu/lib/libg++/iostream/iomanip.h @@ -0,0 +1,150 @@ +// -*- C++ -*- +// This is part of the iostream library, providing parametrized manipulators +// Written by Heinz G. Seidl, Copyright (C) 1992 Cygnus Support +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifndef _IOMANIP_H +// +// Not specifying `pragma interface' causes the compiler to emit the +// template definitions in the files, where they are used. +// +//#ifdef __GNUG__ +//#pragma interface +//#endif +#define _IOMANIP_H + +#include <_G_config.h> + +#ifndef _G_NO_TEMPLATES + +#include + +//----------------------------------------------------------------------------- +// Parametrized Manipulators as specified by ANSI draft +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Stream Manipulators +//----------------------------------------------------------------------------- +// +template class smanip; // TP = Type Param + +template class sapp { + ios& (*_f)(ios&, TP); +public: + sapp(ios& (*f)(ios&, TP)) : _f(f) {} + // + smanip operator()(TP a) + { return smanip(_f, a); } +}; + +template class smanip { + ios& (*_f)(ios&, TP); + TP _a; +public: + smanip(ios& (*f)(ios&, TP), TP a) : _f(f), _a(a) {} + // + friend + istream& operator>>(istream& i, const smanip& m); + friend + ostream& operator<<(ostream& o, const smanip& m); +}; + +template +inline istream& operator>>(istream& i, const smanip& m) + { (*m._f)(i, m._a); return i; } + +template +inline ostream& operator<<(ostream& o, const smanip& m) + { (*m._f)(o, m._a); return o;} + +//----------------------------------------------------------------------------- +// Input-Stream Manipulators +//----------------------------------------------------------------------------- +// +template class imanip; + +template class iapp { + istream& (*_f)(istream&, TP); +public: + iapp(ostream& (*f)(istream&,TP)) : _f(f) {} + // + imanip operator()(TP a) + { return imanip(_f, a); } +}; + +template class imanip { + istream& (*_f)(istream&, TP); + TP _a; +public: + imanip(istream& (*f)(istream&, TP), TP a) : _f(f), _a(a) {} + // + friend + istream& operator>>(istream& i, const imanip& m) + { return (*m._f)( i, m._a); } +}; + + +//----------------------------------------------------------------------------- +// Output-Stream Manipulators +//----------------------------------------------------------------------------- +// +template class omanip; + +template class oapp { + ostream& (*_f)(ostream&, TP); +public: + oapp(ostream& (*f)(ostream&,TP)) : _f(f) {} + // + omanip operator()(TP a) + { return omanip(_f, a); } +}; + +template class omanip { + ostream& (*_f)(ostream&, TP); + TP _a; +public: + omanip(ostream& (*f)(ostream&, TP), TP a) : _f(f), _a(a) {} + // + friend + ostream& operator<<(ostream& o, omanip& m) + { return (*m._f)(o, m._a); } +}; + + +//----------------------------------------------------------------------------- +// Available Manipulators +//----------------------------------------------------------------------------- + +// +// Macro to define an iomanip function, with one argument +// The underlying function is `__iomanip_' +// +#define __DEFINE_IOMANIP_FN1(type,param,function) \ + extern ios& __iomanip_##function (ios&, param); \ + inline type function (param n) \ + { return type (__iomanip_##function, n); } + +__DEFINE_IOMANIP_FN1( smanip, int, setbase) +__DEFINE_IOMANIP_FN1( smanip, int, setfill) +__DEFINE_IOMANIP_FN1( smanip, int, setprecision) +__DEFINE_IOMANIP_FN1( smanip, int, setw) + +__DEFINE_IOMANIP_FN1( smanip, ios::fmtflags, resetiosflags) +__DEFINE_IOMANIP_FN1( smanip, ios::fmtflags, setiosflags) + +#endif /*!_G_NO_TEMPLATES*/ +#endif /*!_IOMANIP_H*/ diff --git a/gnu/lib/libg++/iostream/ioprivate.h b/gnu/lib/libg++/iostream/ioprivate.h new file mode 100644 index 0000000000..84a3482d0c --- /dev/null +++ b/gnu/lib/libg++/iostream/ioprivate.h @@ -0,0 +1,64 @@ +// This is part of the iostream library, providing -*- C++ -*- input/output. +// Copyright (C) 1991 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#include +#include +#include +#include +#include "streambuf.h" +#include +#include + +#define _fstat(x, y) fstat(x,y) +#define _isatty(fd) isatty(fd) + +extern int __cvt_double(double number, register int prec, int flags, + int *signp, int fmtch, char *startp, char *endp); + +/*#define USE_MALLOC_BUF*/ + +#ifndef USE_MALLOC_BUF +#define ALLOC_BUF(size) new char[size] +#define FREE_BUF(ptr) delete [] (ptr) +#else +#define ALLOC_BUF(size) (char*)malloc(size) +#define FREE_BUF(ptr) free(ptr) +#endif + +#define USE_DTOA + +// Advantages: +// - Input gets closest value +// - Output emits string that when read yields identical value. +// - Handles Infinity and NaNs (but not re-readable). +// Disadvantages of dtoa: +// - May not work for all double formats. +// - Big chunck of code. +// - Not reentrant - uses atatic variables freelist, +// result, result_k in dtoa +// (plus initializes p5s, HOWORD, and LOWORD). + +#ifdef USE_DTOA +extern "C" double _Xstrtod(const char *s00, char **se); +#define strtod(s, e) _Xstrtod(s, e) +extern "C" char *dtoa(double d, int mode, int ndigits, + int *decpt, int *sign, char **rve); +extern int __outfloat(double value, streambuf *sb, char mode, + int width, int precision, __fmtflags flags, + char sign_mode, char fill); +#endif + diff --git a/gnu/lib/libg++/iostream/iostream.C b/gnu/lib/libg++/iostream/iostream.C new file mode 100644 index 0000000000..21e26c5747 --- /dev/null +++ b/gnu/lib/libg++/iostream/iostream.C @@ -0,0 +1,783 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1991, 1992 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifdef __GNUG__ +#pragma implementation +#endif +#define _STREAM_COMPAT +#include "ioprivate.h" +#include +#include /* Needed for sprintf */ +#include +#include +#include "floatio.h" + +#define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ + +//#define isspace(ch) ((ch)==' ' || (ch)=='\t' || (ch)=='\n') + +istream::istream(streambuf *sb, ostream* tied) : ios(sb, tied) +{ + _flags |= ios::dont_close; + _gcount = 0; +} + +int skip_ws(streambuf* sb) +{ + int ch; + for (;;) { + ch = sb->sbumpc(); + if (ch == EOF || !isspace(ch)) + return ch; + } +} + +istream& istream::get(char& c) +{ + if (ipfx1()) { + int ch = _strbuf->sbumpc(); + if (ch == EOF) { + set(ios::eofbit|ios::failbit); + _gcount = 0; + } + else { + c = (char)ch; + _gcount = 1; + } + } + return *this; +} + +int istream::peek() +{ + if (!good()) + return EOF; + if (_tie && rdbuf()->in_avail() == 0) + _tie->flush(); + int ch = _strbuf->sgetc(); + if (ch == EOF) + set(ios::eofbit); + return ch; +} + +istream& istream::ignore(int n /* = 1 */, int delim /* = EOF */) +{ + if (ipfx1()) { + register streambuf* sb = _strbuf; + if (delim == EOF) { + _gcount = sb->ignore(n); + return *this; + } + _gcount = 0; + for (;;) { +#if 0 + if (n != MAXINT) // FIXME +#endif + if (--n < 0) + break; + int ch = sb->sbumpc(); + if (ch == EOF) { + set(ios::eofbit|ios::failbit); + break; + } + _gcount++; + if (ch == delim) + break; + } + } + return *this; +} + +istream& istream::read(char *s, int n) +{ + if (ipfx1()) { + _gcount = _strbuf->sgetn(s, n); + if (_gcount != n) + set(ios::failbit); + } + return *this; +} + +istream& istream::seekg(streampos pos) +{ + pos = _strbuf->seekpos(pos, ios::in); + if (pos == streampos(EOF)) + set(ios::badbit); + return *this; +} + +istream& istream::seekg(streamoff off, _seek_dir dir) +{ + streampos pos = _strbuf->seekoff(off, dir, ios::in); + if (pos == streampos(EOF)) + set(ios::badbit); + return *this; +} + +streampos istream::tellg() +{ + streampos pos = _strbuf->seekoff(0, ios::cur, ios::in); + if (pos == streampos(EOF)) + set(ios::badbit); + return pos; +} + +istream& istream::scan(const char *format ...) +{ + if (ipfx0()) { + va_list ap; + va_start(ap, format); + _strbuf->vscan(format, ap, this); + va_end(ap); + } + return *this; +} + +istream& istream::vscan(const char *format, _G_va_list args) +{ + if (ipfx0()) + _strbuf->vscan(format, args, this); + return *this; +} + +istream& istream::operator>>(char& c) +{ + if (ipfx0()) { + int ch = _strbuf->sbumpc(); + if (ch == EOF) + set(ios::eofbit|ios::failbit); + else + c = (char)ch; + } + return *this; +} + +istream& istream::operator>>(char* ptr) +{ + register char *p = ptr; + int w = width(0); + if (ipfx0()) { + register streambuf* sb = _strbuf; + for (;;) + { + int ch = sb->sbumpc(); + if (ch == EOF) + { + set(p == ptr ? (ios::eofbit|ios::failbit) : (ios::eofbit)); + break; + } + else if (isspace(ch)) + { + sb->sputbackc(ch); + break; + } + else if (w == 1) + { + set(ios::failbit); + sb->sputbackc(ch); + break; + } + else *p++ = ch; + w--; + } + } + *p = '\0'; + return *this; +} + +#ifdef __GNUC__ +#define LONGEST long long +#else +#define LONGEST long +#endif + +static int read_int(istream& stream, unsigned LONGEST& val, int& neg) +{ + if (!stream.ipfx0()) + return 0; + register streambuf* sb = stream.rdbuf(); + int base = 10; + int ndigits = 0; + register int ch = skip_ws(sb); + if (ch == EOF) + goto eof_fail; + neg = 0; + if (ch == '+') { + ch = skip_ws(sb); + } + else if (ch == '-') { + neg = 1; + ch = skip_ws(sb); + } + if (ch == EOF) goto eof_fail; + if (!(stream.flags() & ios::basefield)) { + if (ch == '0') { + ch = sb->sbumpc(); + if (ch == EOF) { + val = 0; + return 1; + } + if (ch == 'x' || ch == 'X') { + base = 16; + ch = sb->sbumpc(); + if (ch == EOF) goto eof_fail; + } + else { + sb->sputbackc(ch); + base = 8; + ch = '0'; + } + } + } + else if ((stream.flags() & ios::basefield) == ios::hex) + base = 16; + else if ((stream.flags() & ios::basefield) == ios::oct) + base = 8; + val = 0; + for (;;) { + if (ch == EOF) + break; + int digit; + if (ch >= '0' && ch <= '9') + digit = ch - '0'; + else if (ch >= 'A' && ch <= 'F') + digit = ch - 'A' + 10; + else if (ch >= 'a' && ch <= 'f') + digit = ch - 'a' + 10; + else + digit = 999; + if (digit >= base) { + sb->sputbackc(ch); + if (ndigits == 0) + goto fail; + else + return 1; + } + ndigits++; + val = base * val + digit; + ch = sb->sbumpc(); + } + return 1; + fail: + stream.set(ios::failbit); + return 0; + eof_fail: + stream.set(ios::failbit|ios::eofbit); + return 0; +} + +#define READ_INT(TYPE) \ +istream& istream::operator>>(TYPE& i)\ +{\ + unsigned LONGEST val; int neg;\ + if (read_int(*this, val, neg)) {\ + if (neg) val = -val;\ + i = (TYPE)val;\ + }\ + return *this;\ +} + +READ_INT(short) +READ_INT(unsigned short) +READ_INT(int) +READ_INT(unsigned int) +READ_INT(long) +READ_INT(unsigned long) +#ifdef __GNUG__ +READ_INT(long long) +READ_INT(unsigned long long) +#endif + +istream& istream::operator>>(double& x) +{ + if (ipfx0()) + scan("%lg", &x); + return *this; +} + +istream& istream::operator>>(float& x) +{ + if (ipfx0()) + scan("%g", &x); + return *this; +} + +istream& istream::operator>>(register streambuf* sbuf) +{ + if (ipfx0()) { + register streambuf* inbuf = rdbuf(); + // FIXME: Should optimize! + for (;;) { + register int ch = inbuf->sbumpc(); + if (ch == EOF) { + set(ios::eofbit); + break; + } + if (sbuf->sputc(ch) == EOF) { + set(ios::failbit); + break; + } + } + } + return *this; +} + +ostream& ostream::operator<<(char c) +{ + if (opfx()) { +#if 1 + // This is what the cfront implementation does. + _strbuf->sputc(c); +#else + // This is what cfront documentation and current ANSI drafts say. + int w = width(0); + char fill_char = fill(); + register int padding = w > 0 ? w - 1 : 0; + register streambuf *sb = _strbuf; + if (!(flags() & ios::left)) // Default adjustment. + while (--padding >= 0) sb->sputc(fill_char); + sb->sputc(c); + if (flags() & ios::left) // Left adjustment. + while (--padding >= 0) sb->sputc(fill_char); +#endif + osfx(); + } + return *this; +} + +/* Write VAL on STREAM. + If SIGN<0, val is the absolute value of a negative number. + If SIGN>0, val is a signed non-negative number. + If SIGN==0, val is unsigned. */ + +static void write_int(ostream& stream, unsigned LONGEST val, int sign) +{ +#define WRITE_BUF_SIZE (10 + sizeof(unsigned LONGEST) * 3) + char buf[WRITE_BUF_SIZE]; + register char *buf_ptr = buf+WRITE_BUF_SIZE; // End of buf. + char *show_base = ""; + int show_base_len = 0; + int show_pos = 0; // If 1, print a '+'. + + // Now do the actual conversion, placing the result at the *end* of buf. + // Note that we use separate code for decimal, octal, and hex, + // so we can divide by optimizable constants. + if ((stream.flags() & ios::basefield) == ios::oct) { // Octal + do { + *--buf_ptr = (val & 7) + '0'; + val = val >> 3; + } while (val != 0); + if ((stream.flags() & ios::showbase) && (val != 0)) + *--buf_ptr = '0'; + } + else if ((stream.flags() & ios::basefield) == ios::hex) { // Hex + char *xdigs = (stream.flags() & ios::uppercase) ? "0123456789ABCDEF0X" + : "0123456789abcdef0x"; + do { + *--buf_ptr = xdigs[val & 15]; + val = val >> 4; + } while (val != 0); + if ((stream.flags() & ios::showbase) && (val != 0)) { + show_base = xdigs + 16; // Either "0X" or "0x". + show_base_len = 2; + } + } + else { // Decimal +#ifdef __GNUC__ + // Optimization: Only use long long when we need to. + while (val > UINT_MAX) { + *--buf_ptr = (val % 10) + '0'; + val /= 10; + } + // Use more efficient (int) arithmetic for the rest. + register unsigned int ival = (unsigned int)val; +#else + register unsigned LONGEST ival = val; +#endif + do { + *--buf_ptr = (ival % 10) + '0'; + ival /= 10; + } while (ival != 0); + if (sign > 0 && (stream.flags() & ios::showpos)) + show_pos=1; + } + + int buf_len = buf+WRITE_BUF_SIZE - buf_ptr; + int w = stream.width(0); + + // Calculate padding. + int len = buf_len+show_pos; + if (sign < 0) len++; + len += show_base_len; + int padding = len > w ? 0 : w - len; + + // Do actual output. + register streambuf* sbuf = stream.rdbuf(); + ios::fmtflags pad_kind = + stream.flags() & (ios::left|ios::right|ios::internal); + char fill_char = stream.fill(); + if (padding > 0 + && pad_kind != (ios::fmtflags)ios::left + && pad_kind != (ios::fmtflags)ios::internal) // Default (right) adjust. + sbuf->padn(fill_char, padding); + if (sign < 0) sbuf->sputc('-'); + else if (show_pos) sbuf->sputc('+'); + if (show_base_len) + sbuf->sputn(show_base, show_base_len); + if (pad_kind == (ios::fmtflags)ios::internal && padding > 0) + sbuf->padn(fill_char, padding); + sbuf->sputn(buf_ptr, buf_len); + if (pad_kind == (ios::fmtflags)ios::left && padding > 0) // Left adjustment + sbuf->padn(fill_char, padding); + stream.osfx(); +} + +ostream& ostream::operator<<(int n) +{ + if (opfx()) { + int sign = 1; + if (n < 0 && (flags() & (ios::oct|ios::hex)) == 0) + n = -n, sign = -1; + write_int(*this, n, sign); + } + return *this; +} + +ostream& ostream::operator<<(unsigned int n) +{ + if (opfx()) + write_int(*this, n, 0); + return *this; +} + + +ostream& ostream::operator<<(long n) +{ + if (opfx()) { + int sign = 1; + if (n < 0 && (flags() & (ios::oct|ios::hex)) == 0) + n = -n, sign = -1; + write_int(*this, n, sign); + } + return *this; +} + +ostream& ostream::operator<<(unsigned long n) +{ + if (opfx()) + write_int(*this, n, 0); + return *this; +} + +#ifdef __GNUG__ +ostream& ostream::operator<<(long long n) +{ + if (opfx()) { + int sign = 1; + if (n < 0 && (flags() & (ios::oct|ios::hex)) == 0) + n = -n, sign = -1; + write_int(*this, n, sign); + } + return *this; +} + + +ostream& ostream::operator<<(unsigned long long n) +{ + if (opfx()) + write_int(*this, n, 0); + return *this; +} +#endif /*__GNUG__*/ + +ostream& ostream::operator<<(double n) +{ + if (opfx()) { + // Uses __cvt_double (renamed from static cvt), in Chris Torek's + // stdio implementation. The setup code uses the same logic + // as in __vsbprintf.C (also based on Torek's code). + int format_char; +#if 0 + if (flags() ios::showpos) sign = '+'; +#endif + if ((flags() & ios::floatfield) == ios::fixed) + format_char = 'f'; + else if ((flags() & ios::floatfield) == ios::scientific) + format_char = flags() & ios::uppercase ? 'E' : 'e'; + else + format_char = flags() & ios::uppercase ? 'G' : 'g'; + + int fpprec = 0; // 'Extra' (suppressed) floating precision. + int prec = precision(); + if (prec > MAXFRACT) { + if (flags() & (ios::fixed|ios::scientific) & ios::showpos) + fpprec = prec - MAXFRACT; + prec = MAXFRACT; + } + else if (prec <= 0 && !(flags() & ios::fixed)) + prec = 6; /* default */ + + // Do actual conversion. +#ifdef USE_DTOA + if (__outfloat(n, rdbuf(), format_char, width(0), + prec, flags(), 0, fill()) < 0) + set(ios::badbit|ios::failbit); // ?? +#else + int negative; + char buf[BUF]; + int sign = '\0'; + char *cp = buf; + *cp = 0; + int size = __cvt_double(n, prec, + flags() & ios::showpoint ? 0x80 : 0, + &negative, + format_char, cp, buf + sizeof(buf)); + if (negative) sign = '-'; + if (*cp == 0) + cp++; + + // Calculate padding. + int fieldsize = size + fpprec; + if (sign) fieldsize++; + int padding = 0; + int w = width(0); + if (fieldsize < w) + padding = w - fieldsize; + + // Do actual output. + register streambuf* sbuf = rdbuf(); + register i; + char fill_char = fill(); + ios::fmtflags pad_kind = + flags() & (ios::left|ios::right|ios::internal); + if (pad_kind != (ios::fmtflags)ios::left // Default (right) adjust. + && pad_kind != (ios::fmtflags)ios::internal) + for (i = padding; --i >= 0; ) sbuf->sputc(fill_char); + if (sign) + sbuf->sputc(sign); + if (pad_kind == (ios::fmtflags)ios::internal) + for (i = padding; --i >= 0; ) sbuf->sputc(fill_char); + + // Emit the actual concented field, followed by extra zeros. + sbuf->sputn(cp, size); + for (i = fpprec; --i >= 0; ) sbuf->sputc('0'); + + if (pad_kind == (ios::fmtflags)ios::left) // Left adjustment + for (i = padding; --i >= 0; ) sbuf->sputc(fill_char); +#endif + osfx(); + } + return *this; +} + +ostream& ostream::operator<<(const char *s) +{ + if (opfx()) { + if (s == NULL) + s = "(null)"; + int len = strlen(s); + int w = width(0); + char fill_char = fill(); + register streambuf *sbuf = rdbuf(); + register int padding = w > len ? w - len : 0; + if (!(flags() & ios::left)) // Default adjustment. + while (--padding >= 0) sbuf->sputc(fill_char); + sbuf->sputn(s, len); + if (flags() & ios::left) // Left adjustment. + while (--padding >= 0) sbuf->sputc(fill_char); + osfx(); + } + return *this; +} + +ostream& ostream::operator<<(const void *p) +{ + if (opfx()) { + form("%p", p); + osfx(); + } + return *this; +} + +ostream& ostream::operator<<(register streambuf* sbuf) +{ + if (opfx()) { + register streambuf* outbuf = rdbuf(); + // FIXME: Should optimize! + for (;;) { + register int ch = sbuf->sbumpc(); + if (ch == EOF) break; + if (outbuf->sputc(ch) == EOF) { + set(ios::badbit); + break; + } + } + osfx(); + } + return *this; +} + +ostream::ostream(streambuf* sb, ostream* tied) : ios(sb, tied) +{ + _flags |= ios::dont_close; +} + +ostream& ostream::seekp(streampos pos) +{ + pos = _strbuf->seekpos(pos, ios::out); + if (pos == streampos(EOF)) + set(ios::badbit); + return *this; +} + +ostream& ostream::seekp(streamoff off, _seek_dir dir) +{ + streampos pos = _strbuf->seekoff(off, dir, ios::out); + if (pos == streampos(EOF)) + set(ios::badbit); + return *this; +} + +streampos ostream::tellp() +{ + streampos pos = _strbuf->seekoff(0, ios::cur, ios::out); + if (pos == streampos(EOF)) + set(ios::badbit); + return pos; +} + +ostream& ostream::form(const char *format ...) +{ + if (opfx()) { + va_list ap; + va_start(ap, format); + _strbuf->vform(format, ap); + va_end(ap); + } + return *this; +} + +ostream& ostream::vform(const char *format, _G_va_list args) +{ + if (opfx()) + _strbuf->vform(format, args); + return *this; +} + +ostream& ostream::flush() +{ + if (_strbuf->sync()) + set(ios::badbit); + return *this; +} + +ostream& flush(ostream& outs) +{ + return outs.flush(); +} + +istream& ws(istream& ins) +{ + if (ins.ipfx1()) { + int ch = skip_ws(ins._strbuf); + if (ch == EOF) + ins.set(ios::eofbit); + else + ins._strbuf->sputbackc(ch); + } + return ins; +} + +// Skip white-space. Return 0 on failure (EOF), or 1 on success. +// Differs from ws() manipulator in that failbit is set on EOF. +// Called by ipfx() and ipfx0() if needed. + +int istream::_skip_ws() +{ + int ch = skip_ws(_strbuf); + if (ch == EOF) { + set(ios::eofbit|ios::failbit); + return 0; + } + else { + _strbuf->sputbackc(ch); + return 1; + } +} + +ostream& ends(ostream& outs) +{ + outs.put('\0'); + return outs; +} + +ostream& endl(ostream& outs) +{ + return flush(outs.put('\n')); +} + +ostream& ostream::write(const char *s, int n) +{ + if (opfx()) { + if (_strbuf->sputn(s, n) != n) + set(ios::failbit); + } + return *this; +} + +void ostream::do_osfx() +{ + if (flags() & ios::unitbuf) + flush(); + if (flags() & ios::stdio) { + fflush(stdout); + fflush(stderr); + } +} + +iostream::iostream(streambuf* sb, ostream* tied) : ios(sb, tied) +{ + _flags |= ios::dont_close; + _gcount = 0; +} + +// NOTE: extension for compatibility with old libg++. +// Not really compatible with fistream::close(). +#ifdef _STREAM_COMPAT +void ios::close() +{ + if (!(_flags & (unsigned int)ios::dont_close)) + delete _strbuf; + else if (_strbuf->_flags & _S_IS_FILEBUF) + ((struct filebuf*)_strbuf)->close(); + else if (_strbuf != NULL) + _strbuf->sync(); + _flags |= ios::dont_close; + _strbuf = NULL; + _state = badbit; +} + +int istream::skip(int i) +{ + int old = (_flags & ios::skipws) != 0; + if (i) + _flags |= ios::skipws; + else + _flags &= ~ios::skipws; + return old; +} +#endif diff --git a/gnu/lib/libg++/iostream/iostream.h b/gnu/lib/libg++/iostream/iostream.h new file mode 100644 index 0000000000..052260f8a3 --- /dev/null +++ b/gnu/lib/libg++/iostream/iostream.h @@ -0,0 +1,226 @@ +// This is part of the iostream library, providing -*- C++ -*- input/output. +// Copyright (C) 1991 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifndef _IOSTREAM_H +#ifdef __GNUG__ +#pragma interface +#endif +#define _IOSTREAM_H + +#include + +class istream; class ostream; +typedef ios& (*__manip)(ios&); +typedef istream& (*__imanip)(istream&); +typedef ostream& (*__omanip)(ostream&); + +extern istream& ws(istream& ins); +extern ostream& flush(ostream& outs); +extern ostream& endl(ostream& outs); +extern ostream& ends(ostream& outs); + +class ostream : virtual public ios +{ + // NOTE: If fields are changed, you must fix _fake_ostream in stdstreams.C! + void do_osfx(); + public: + ostream() { } + ostream(streambuf* sb, ostream* tied=NULL); + int opfx() { + if (!good()) return 0; else { if (_tie) _tie->flush(); return 1;} } + void osfx() { if (flags() & (ios::unitbuf|ios::stdio)) + do_osfx(); } + streambuf* ostreambuf() const { return _strbuf; } + ostream& flush(); + ostream& put(char c) { _strbuf->sputc(c); return *this; } + ostream& put(unsigned char c) { return put((char)c); } + + ostream& write(const char *s, int n); + ostream& write(const unsigned char *s, int n) { return write((const char*)s, n);} +#ifndef _G_BROKEN_SIGNED_CHAR + ostream& put(signed char c) { return put((char)c); } + ostream& write(const signed char *s, int n) { return write((const char*)s, n);} +#endif + ostream& write(const void *s, int n) { return write((const char*)s, n);} + ostream& seekp(streampos); + ostream& seekp(streamoff, _seek_dir); + streampos tellp(); + ostream& form(const char *format ...); + ostream& vform(const char *format, _G_va_list args); + + ostream& operator<<(char c); + ostream& operator<<(unsigned char c) { return (*this) << (char)c; } +#ifndef _G_BROKEN_SIGNED_CHAR + ostream& operator<<(signed char c) { return (*this) << (char)c; } +#endif + ostream& operator<<(const char *s); + ostream& operator<<(const unsigned char *s) + { return (*this) << (const char*)s; } +#ifndef _G_BROKEN_SIGNED_CHAR + ostream& operator<<(const signed char *s) + { return (*this) << (const char*)s; } +#endif + ostream& operator<<(const void *p); + ostream& operator<<(int n); + ostream& operator<<(unsigned int n); + ostream& operator<<(long n); + ostream& operator<<(unsigned long n); +#ifdef __GNUG__ + ostream& operator<<(long long n); + ostream& operator<<(unsigned long long n); +#endif + ostream& operator<<(short n) {return operator<<((int)n);} + ostream& operator<<(unsigned short n) {return operator<<((unsigned int)n);} + ostream& operator<<(double n); + ostream& operator<<(float n) { return operator<<((double)n); } + ostream& operator<<(__omanip func) { return (*func)(*this); } + ostream& operator<<(__manip func) {(*func)(*this); return *this;} + ostream& operator<<(streambuf*); +}; + +class istream : virtual public ios +{ + // NOTE: If fields are changed, you must fix _fake_istream in stdstreams.C! + _G_ssize_t _gcount; + + int _skip_ws(); + public: + istream() { _gcount = 0; } + istream(streambuf* sb, ostream*tied=NULL); + streambuf* istreambuf() const { return _strbuf; } + istream& get(char* ptr, int len, char delim = '\n'); + istream& get(unsigned char* ptr, int len, char delim = '\n') + { return get((char*)ptr, len, delim); } + istream& get(char& c); + istream& get(unsigned char& c) { return get((char&)c); } + istream& getline(char* ptr, int len, char delim = '\n'); + istream& getline(unsigned char* ptr, int len, char delim = '\n') + { return getline((char*)ptr, len, delim); } +#ifndef _G_BROKEN_SIGNED_CHAR + istream& get(signed char& c) { return get((char&)c); } + istream& get(signed char* ptr, int len, char delim = '\n') + { return get((char*)ptr, len, delim); } + istream& getline(signed char* ptr, int len, char delim = '\n') + { return getline((char*)ptr, len, delim); } +#endif + istream& read(char *ptr, int n); + istream& read(unsigned char *ptr, int n) { return read((char*)ptr, n); } +#ifndef _G_BROKEN_SIGNED_CHAR + istream& read(signed char *ptr, int n) { return read((char*)ptr, n); } +#endif + istream& read(void *ptr, int n) { return read((char*)ptr, n); } + istream& get(streambuf& sb, char delim = '\n'); + istream& gets(char **s, char delim = '\n'); + int ipfx(int need) { + if (!good()) { set(ios::failbit); return 0; } + else { + if (_tie && (need == 0 || rdbuf()->in_avail() < need)) _tie->flush(); + if (!need && (flags() & ios::skipws)) return _skip_ws(); + else return 1; + } + } + int ipfx0() { // Optimized version of ipfx(0). + if (!good()) { set(ios::failbit); return 0; } + else { + if (_tie) _tie->flush(); + if (flags() & ios::skipws) return _skip_ws(); + else return 1; + } + } + int ipfx1() { // Optimized version of ipfx(1). + if (!good()) { set(ios::failbit); return 0; } + else { + if (_tie && rdbuf()->in_avail() == 0) _tie->flush(); + return 1; + } + } + void isfx() { } + int get() { if (!ipfx1()) return EOF; + else { int ch = _strbuf->sbumpc(); + if (ch == EOF) set(ios::eofbit); + return ch; + } } + int peek(); + _G_ssize_t gcount() { return _gcount; } + istream& ignore(int n=1, int delim = EOF); + istream& seekg(streampos); + istream& seekg(streamoff, _seek_dir); + streampos tellg(); + istream& putback(char ch) { + if (good() && _strbuf->sputbackc(ch) == EOF) clear(ios::badbit); + return *this;} + istream& unget() { + if (good() && _strbuf->sungetc() == EOF) clear(ios::badbit); + return *this;} + istream& scan(const char *format ...); + istream& vscan(const char *format, _G_va_list args); +#ifdef _STREAM_COMPAT + istream& unget(char ch) { return putback(ch); } + int skip(int i); +#endif + + istream& operator>>(char*); + istream& operator>>(unsigned char* p) { return operator>>((char*)p); } +#ifndef _G_BROKEN_SIGNED_CHAR + istream& operator>>(signed char*p) { return operator>>((char*)p); } +#endif + istream& operator>>(char& c); + istream& operator>>(unsigned char& c) {return operator>>((char&)c);} +#ifndef _G_BROKEN_SIGNED_CHAR + istream& operator>>(signed char& c) {return operator>>((char&)c);} +#endif + istream& operator>>(int&); + istream& operator>>(long&); +#ifdef __GNUG__ + istream& operator>>(long long&); +#endif + istream& operator>>(short&); + istream& operator>>(unsigned int&); + istream& operator>>(unsigned long&); +#ifdef __GNUG__ + istream& operator>>(unsigned long long&); +#endif + istream& operator>>(unsigned short&); + istream& operator>>(float&); + istream& operator>>(double&); + istream& operator>>( __manip func) {(*func)(*this); return *this;} + istream& operator>>(__imanip func) { return (*func)(*this); } + istream& operator>>(streambuf*); +}; + + +class iostream : public istream, public ostream +{ + _G_ssize_t _gcount; + public: + iostream() { _gcount = 0; } + iostream(streambuf* sb, ostream*tied=NULL); +}; + +extern istream cin; +extern ostream cout, cerr, clog; // clog->rdbuf() == cerr->rdbuf() + +struct Iostream_init { } ; // Compatibility hack for AT&T library. + +inline ios& dec(ios& i) +{ i.setf(ios::dec, ios::dec|ios::hex|ios::oct); return i; } +inline ios& hex(ios& i) +{ i.setf(ios::hex, ios::dec|ios::hex|ios::oct); return i; } +inline ios& oct(ios& i) +{ i.setf(ios::oct, ios::dec|ios::hex|ios::oct); return i; } + +#endif /*!_IOSTREAM_H*/ diff --git a/gnu/lib/libg++/iostream/makebuf.C b/gnu/lib/libg++/iostream/makebuf.C new file mode 100644 index 0000000000..592f59ec39 --- /dev/null +++ b/gnu/lib/libg++/iostream/makebuf.C @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +// Modified for GNU iostream by Per Bothner 1991. + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "%W% (Berkeley) %G%"; +#endif /* LIBC_SCCS and not lint */ + +#include "ioprivate.h" +#include "fstream.h" +#include +#include + +/* + * Allocate a file buffer, or switch to unbuffered I/O. + * Per the ANSI C standard, ALL tty devices default to line buffered. + * + * As a side effect, we set __SOPT or __SNPT (en/dis-able fseek + * optimisation) right after the _fstat() that finds the buffer size. + */ +int filebuf::doallocate() +{ + register size_t size, couldbetty; + register char *p; + struct stat st; + + if (fd() < 0 || _fstat(fd(), &st) < 0) { + couldbetty = 0; + size = _G_BUFSIZ; +#if 0 + /* do not try to optimise fseek() */ + fp->_flags |= __SNPT; +#endif + } else { + couldbetty = S_ISCHR(st.st_mode); +#if _G_HAVE_ST_BLKSIZE + size = st.st_blksize <= 0 ? _G_BUFSIZ : st.st_blksize; +#else + size = _G_BUFSIZ; +#endif + } +#ifdef USE_MALLOC_BUF + if ((p = malloc(size)) == NULL) { + unbuffered(1); +// fp->_bf._base = fp->_p = fp->_nbuf; +// fp->_bf._size = 1; + return EOF; + } +#else + p = ALLOC_BUF(size); +#endif + setb(p, p+size, 1); + if (couldbetty && _isatty(fd())) + _flags |= _S_LINE_BUF; + return 1; +} diff --git a/gnu/lib/libg++/iostream/outfloat.C b/gnu/lib/libg++/iostream/outfloat.C new file mode 100644 index 0000000000..c677844b83 --- /dev/null +++ b/gnu/lib/libg++/iostream/outfloat.C @@ -0,0 +1,183 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1992 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#include "ioprivate.h" + +// Format floating-point number and print them. +// Return number of chars printed, or EOF on error. + +// sign_mode == '+' : print "-" or "+" +// sign_mode == ' ' : print "-" or " " +// sign_mode == '\0' : print "-' or "" + +int __outfloat(double value, streambuf *sb, char type, + int width, int precision, ios::fmtflags flags, + char sign_mode, char fill) +{ + int count = 0; +#define PUT(x) do {if (sb->sputc(x) < 0) goto error; count++;} while (0) +#define PUTN(p, n) \ + do {int _n=n; count+=_n; if (sb->sputn(p,_n) != _n) goto error;} while(0) +#define PADN(fill, n) \ + do {int _n = n; count+=_n; if (sb->padn(fill, _n) < 0) goto error;} while (0) + ios::fmtflags pad_kind = flags & (ios::left|ios::right|ios::internal); + int skip_zeroes = 0; + int show_dot = (flags & ios::showpoint) != 0; + int decpt; + int sign; + int mode; +#define EBUF_SIZE 12 +#define EBUF_END &ebuf[EBUF_SIZE] + char ebuf[EBUF_SIZE]; + char *end; + int exp = 0; + switch (type) { + case 'f': + mode = 3; + break; + case 'e': + case 'E': + exp = type; + mode = 2; + if (precision != 999) + precision++; // Add one to include digit before decimal point. + break; + case 'g': + case 'G': + exp = type == 'g' ? 'e' : 'E'; + if (precision == 0) precision = 1; + if (!(flags & ios::showpoint)) + skip_zeroes = 1; + type = 'g'; + mode = 2; + break; + } + /* Do the actual convension */ + if (precision == 999 && mode != 3) + mode = 0; + char *p = dtoa(value, mode, precision, &decpt, &sign, &end); + register int i; + int useful_digits = end-p; + char *exponent_start = EBUF_END; + if (mode == 0) + precision = useful_digits; + // Check if we need to emit an exponent. + if (mode != 3 && decpt != 9999) { + i = decpt - 1; + if ((type != 'g' && type != 'F') || i < -4 || i >= precision) { + // Print the exponent into ebuf. + // We write ebuf in reverse order (right-to-left). + char sign; + if (i >= 0) + sign = '+'; + else + sign = '-', i = -i; + /* Note: ANSI requires at least 2 exponent digits. */ + do { + *--exponent_start = (i % 10) + '0'; + i /= 10; + } while (i >= 10); + *--exponent_start = i + '0'; + *--exponent_start = sign; + *--exponent_start = exp; + } + } + int exponent_size = EBUF_END - exponent_start; + if (mode == 1) + precision = 1; + /* If we print an exponent, always show just one digit before point. */ + if (exponent_size) + decpt = 1; + if (decpt == 9999) { // Infinity or NaN + decpt = useful_digits; + precision = 0; + show_dot = 0; + } + + // dtoa truncates trailing zeroes. Set the variable trailing_zeroes to + // the number of 0's we have to add (after the decimal point). + int trailing_zeroes = 0; + if (skip_zeroes) + trailing_zeroes = 0; + else if (type == 'f') + trailing_zeroes = useful_digits <= decpt ? precision + : precision-(useful_digits-decpt); + else if (exponent_size) // 'e' 'E' or 'g' format using exponential notation. + trailing_zeroes = precision - useful_digits; + else // 'g' format not using exponential notation. + trailing_zeroes = useful_digits <= decpt ? precision - decpt + : precision-useful_digits; + if (trailing_zeroes < 0) trailing_zeroes = 0; + + if (trailing_zeroes != 0 || useful_digits > decpt) + show_dot = 1; + int print_sign; + if (sign_mode == 0) + print_sign = sign ? '-' : 0; + else if (sign_mode == '+') + print_sign = sign ? '-' : '+'; + else /* if (sign_mode == ' ') */ + print_sign = sign ? '-' : ' '; + + // Calculate the width (before padding). + int unpadded_width = + (print_sign != 0) + trailing_zeroes + exponent_size + show_dot + + useful_digits + + (decpt > useful_digits ? decpt - useful_digits + : decpt > 0 ? 0 : 1 - decpt); + + int padding = width > unpadded_width ? width - unpadded_width : 0; + if (padding > 0 + && pad_kind != (ios::fmtflags)ios::left + && pad_kind != (ios::fmtflags)ios::internal) // Default (right) adjust. + PADN(fill, padding); + if (print_sign) + PUT(print_sign); + if (pad_kind == (ios::fmtflags)ios::internal && padding > 0) + PADN(fill, padding); + if (decpt > 0) { + if (useful_digits >= decpt) + PUTN(p, decpt); + else { + PUTN(p, useful_digits); + PADN('0', decpt-useful_digits); + } + if (show_dot) { + PUT('.'); + // Print digits after the decimal point. + if (useful_digits > decpt) + PUTN(p + decpt, useful_digits-decpt); + } + } + else { + PUT('0'); + if (show_dot) { + PUT('.'); + PADN('0', -decpt); + // Print digits after the decimal point. + PUTN(p, useful_digits); + } + } + PADN('0', trailing_zeroes); + if (exponent_size) + PUTN(exponent_start, exponent_size); + if (pad_kind == (ios::fmtflags)ios::left && padding > 0) // Left adjustment + PADN(fill, padding); + return count; + error: + return EOF; +} diff --git a/gnu/lib/libg++/iostream/parsestream.C b/gnu/lib/libg++/iostream/parsestream.C new file mode 100644 index 0000000000..49135da770 --- /dev/null +++ b/gnu/lib/libg++/iostream/parsestream.C @@ -0,0 +1,307 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1991 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifdef __GNUG__ +#pragma implementation +#endif +#include "ioprivate.h" +#include "parsestream.h" + +streambuf* parsebuf::setbuf(char*, int) +{ + return NULL; +} + +int parsebuf::tell_in_line() +{ + return 0; +} + +int parsebuf::pbackfail(int c) +{ + if (c == EOF) + return 0; + if (seekoff(-1, ios::cur) == EOF) + return EOF; + return (unsigned char)c; +} + +char* parsebuf::current_line() { return NULL; } + +streampos parsebuf::seekoff(streamoff offset, _seek_dir dir, int) +{ + // Make offset relative to line start. + switch (dir) { + case ios::beg: + offset -= pos_at_line_start; + break; + case ios::cur: + offset += tell_in_line(); + break; + default: + return EOF; + } + if (offset < -1) + return EOF; + if (offset > _line_length + 1) + return EOF; + return seek_in_line(offset) + pos_at_line_start; +} + +// string_parsebuf invariants: +// The reserve ares (base() .. ebuf()) is always the entire string. +// The get area (eback() .. egptr()) is the extended current line +// (i.e. with the '\n' at either end, if these exist). + +string_parsebuf::string_parsebuf(char *buf, int len, + int delete_at_close /* = 0*/) +: parsebuf() +{ + setb(buf, buf+len, delete_at_close); + register char *ptr = buf; + while (ptr < ebuf() && *ptr != '\n') ptr++; + _line_length = ptr - buf; + setg(buf, buf, ptr); +} + +int string_parsebuf::underflow() +{ + register char* ptr = egptr(); // Point to end of current_line + do { + int i = right() - ptr; + if (i <= 0) + return EOF; + ptr++; i--; // Skip '\n'. + char *line_start = ptr; + while (ptr < right() && *ptr == '\n') ptr++; + setg(line_start-1, line_start, ptr + (ptr < right())); + pos_at_line_start = line_start - left(); + _line_length = ptr - line_start; + __line_number++; + } while (gptr() == ptr); + return *gptr(); +} + +char* string_parsebuf::current_line() +{ + char *ptr = eback(); + if (__line_number > 0) + ptr++; // Skip '\n' at end of previous line. + return ptr; +} + +int string_parsebuf::tell_in_line() +{ + int offset = gptr() - eback(); + if (__line_number > 0) + offset--; + return offset; +} + +int string_parsebuf::seek_in_line(int i) +{ + int delta = i - tell_in_line(); + gbump(delta); // FIXME: Needs error (bounds) checking! + return i; +} + +static const char NewLine[1] = { '\n' }; + +general_parsebuf::general_parsebuf(streambuf *buf, int delete_arg_buf) + : parsebuf() +{ + delete_buf = delete_arg_buf; + sbuf = buf; + int buf_size = 128; + char* buffer = ALLOC_BUF(buf_size); + setb(buffer, buffer+buf_size, 1); +// setg(buffer, buffer, buffer); +} + +general_parsebuf::~general_parsebuf() +{ + if (delete_buf) + delete sbuf; +} + +int general_parsebuf::underflow() +{ + register char *ptr = base(); + int has_newline = eback() < gptr() && gptr()[-1] == '\n'; + if (has_newline) + *ptr++ = '\n'; + register streambuf *sb = sbuf; + register int ch; + for (;;) { + ch = sb->sbumpc(); + if (ch == EOF) + break; + if (ptr == ebuf()) { + int old_size = ebuf() - base(); + char *new_buffer = new char[old_size * 2]; + memcpy(new_buffer, base(), old_size); + setb(new_buffer, new_buffer + 2 * old_size, 1); + ptr = new_buffer + old_size; + } + *ptr++ = ch; + if (ch == '\n') + break; + } + char *cur_pos = base() + has_newline; + pos_at_line_start += _line_length + 1; + _line_length = ptr - cur_pos; + if (ch != EOF || _line_length > 0) + __line_number++; + setg(base(), cur_pos, ptr); + return ptr == cur_pos ? EOF : cur_pos[0]; +} + +char* general_parsebuf::current_line() +{ + char* ret = base(); + if (__line_number > 1) + ret++; // Move past '\n' from end of previous line. + return ret; +} + +int general_parsebuf::tell_in_line() +{ + int off = gptr() - base(); + if (__line_number > 1) + off--; // Subtract 1 for '\n' from end of previous line. + return off; +} + +int general_parsebuf::seek_in_line(int i) +{ + if (__line_number == 0) + (void)general_parsebuf::underflow(); + if (__line_number > 1) + i++; // Add 1 for '\n' from end of previous line. + if (i < 0) i = 0; + int len = egptr() - eback(); + if (i > len) i = len; + setg(base(), base() + i, egptr()); + return i; +} + +func_parsebuf::func_parsebuf(CharReader func, void *argm) : parsebuf() +{ + read_func = func; + arg = argm; + buf_start = NULL; + buf_end = NULL; + setb((char*)NewLine, (char*)NewLine+1, 0); + setg((char*)NewLine, (char*)NewLine+1, (char*)NewLine+1); + backed_up_to_newline = 0; +} + +int func_parsebuf::tell_in_line() +{ + if (buf_start == NULL) + return 0; + if (egptr() != (char*)NewLine+1) + // Get buffer was line buffer. + return gptr() - buf_start; + if (backed_up_to_newline) + return -1; // Get buffer is '\n' preceding current line. + // Get buffer is '\n' following current line. + return (buf_end - buf_start) + (gptr() - (char*)NewLine); +} + +char* func_parsebuf::current_line() +{ + return buf_start; +} + +int func_parsebuf::seek_in_line(int i) +{ + if (i < 0) { + // Back up to preceding '\n'. + if (i < -1) i = -1; + backed_up_to_newline = 1; + setg((char*)NewLine, (char*)NewLine+(i+1), (char*)NewLine+1); + return i; + } + backed_up_to_newline = 0; + int line_length = buf_end-buf_start; + if (i <= line_length) { + setg(buf_start, buf_start+i, buf_end); + return i; + } + i -= line_length; + if (i > 0) i = 1; + setg((char*)NewLine, (char*)NewLine+i, (char*)NewLine+1); + return line_length + i; +} + +int func_parsebuf::underflow() +{ + retry: + if (gptr() < egptr()) + return *gptr(); + if (gptr() != (char*)NewLine+1) { + // Get buffer was line buffer. Move to following '\n'. + setg((char*)NewLine, (char*)NewLine, (char*)NewLine+1); + return *gptr(); + } + if (backed_up_to_newline) + // Get buffer was '\n' preceding current line. Move to current line. + backed_up_to_newline = 0; + else { + // Get buffer was '\n' following current line. Read new line. + if (buf_start) free(buf_start); + char *str = (*read_func)(arg); + buf_start = str; + if (str == NULL) + return EOF; + // Initially, _line_length == -1, so pos_at_line_start becomes 0. + pos_at_line_start += _line_length + 1; + _line_length = strlen(str); + buf_end = str + _line_length; + __line_number++; + } + setg(buf_start, buf_start, buf_end); + goto retry; +} + +#if 0 +size_t parsebuf::line_length() +{ + if (current_line_length == (size_t)(-1)) // Initial value; + (void)sgetc(); + return current_line_length; +} +#endif + +int parsebuf::seek_in_line(int i) +{ +#if 1 + abort(); + return 0; // Suppress warning. +#else + if (i > 0) { + size_t len = line_length(); + if ((unsigned)i > len) i = len; + } + else if (i < -1) i = -1; + int new_pos = seekoff(pos_at_line_start + i, ios::beg); + if (new_pos == EOF) + return tell_in_line(); + else return new_pos - pos_at_line_start; +#endif +} diff --git a/gnu/lib/libg++/iostream/parsestream.h b/gnu/lib/libg++/iostream/parsestream.h new file mode 100644 index 0000000000..eaa235123a --- /dev/null +++ b/gnu/lib/libg++/iostream/parsestream.h @@ -0,0 +1,145 @@ +// This is part of the iostream library, providing -*- C++ -*- input/output. +// Copyright (C) 1991 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifndef PARSESTREAM_H +#define PARSESTREAM_H +#ifdef __GNUG__ +#pragma interface +#endif +#include "streambuf.h" + +// A parsebuf is a streambuf optimized for scanning text files. +// It keeps track of line and column numbers. +// It is guaranteed to remember the entire current line, +// as well the '\n'-s on either side of it (if they exist). +// You can arbitrarily seek (or unget) within this extended line. +// Other backward seeks are not supported. +// Normal read semantics are supported (and hence istream operators like >>). + +class parsebuf : public backupbuf { + protected: + _G_fpos_t pos_at_line_start; + long _line_length; + unsigned long __line_number; + char *buf_start; + char *buf_end; + + public: + parsebuf *chain; + + // Return column number (raw - don't handle tabs etc). + // Retult can be -1, meaning: at '\n' before current line. + virtual int tell_in_line(); + + // seek to (raw) column I in current line. + // Result is new (raw) column position - differs from I if unable to seek. + // Seek to -1 tries to seek to before previous LF. + virtual int seek_in_line(int i); + + // Note: there is no "current line" initially, until something is read. + + // Current line number, starting with 0. + // If tell_in_line()==-1, then line number of next line. + int line_number() { return __line_number; } + + // Length of current line, not counting either '\n'. + int line_length() { return _line_length; } + // Current line - not a copy, so file ops may trash it. + virtual char* current_line(); + virtual streampos seekoff(streamoff, _seek_dir, int mode=ios::in|ios::out); + virtual streambuf* setbuf(char* p, int len); + protected: + parsebuf() : backupbuf() { chain= NULL; + __line_number = 0; pos_at_line_start = 0; _line_length = -1; } + virtual int pbackfail(int c); +}; + +// A string_parsebuf is a parsebuf whose source is a fixed string. + +class string_parsebuf : public parsebuf { + public: + int do_delete; + string_parsebuf(char *str, int len, int delete_at_close=0); + virtual int underflow(); + virtual char* current_line(); + virtual int seek_in_line(int i); + virtual int tell_in_line(); + char *left() const { return base(); } + char *right() const { return ebuf(); } +// streampos seekoff(streamoff, _seek_dir, int); +}; + +// A func_parsebuf calls a given function to get new input. +// Each call returns an entire NUL-terminated line (without the '\n'). +// That line has been allocated with malloc(), not new. +// The interface is tailored to the GNU readline library. +// Example: +// char* DoReadLine(void* arg) +// { +// char *line = readline((char*)arg); /* 'arg' is used as prompt. */ +// if line == NULL) { putc('\n', stderr); return NULL; } +// if (line[0] != '\0') add_history(line); +// return line; +// } +// char PromptBuffer[100] = "> "; +// func_parsebuf my_stream(DoReadLine, PromptBuffer); + +typedef char *(*CharReader)(void *arg); +class istream; + +class func_parsebuf : public parsebuf { + public: + void *arg; + CharReader read_func; + int backed_up_to_newline; + func_parsebuf(CharReader func, void *argm = NULL); + int underflow(); + virtual int tell_in_line(); + virtual int seek_in_line(int i); + virtual char* current_line(); +}; + +// A general_parsebuf is a parsebuf which gets its input from some +// other streambuf. It explicitly buffers up an entire line. + +class general_parsebuf : public parsebuf { + public: + streambuf *sbuf; + int delete_buf; // Delete sbuf when destroying this. + general_parsebuf(streambuf *buf, int delete_arg_buf = 0); + int underflow(); + virtual int tell_in_line(); + virtual int seek_in_line(int i); + ~general_parsebuf(); + virtual char* current_line(); +}; + +#if 0 +class parsestream : public istream { + streammarker marks[2]; + short _first; // of the two marks; either 0 or 1 + int _lineno; + int first() { return _first; } + int second() { return 1-_first; } + int line_length() { marks[second].delta(marks[first]); } + int line_length() { marks[second].delta(marks[first]); } + int seek_in_line(int i); + int tell_in_line(); + int line_number(); +}; +#endif +#endif /*!defined(PARSESTREAM_H)*/ diff --git a/gnu/lib/libg++/iostream/procbuf.C b/gnu/lib/libg++/iostream/procbuf.C new file mode 100644 index 0000000000..e7ac67c1ff --- /dev/null +++ b/gnu/lib/libg++/iostream/procbuf.C @@ -0,0 +1,126 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1992 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#define _POSIX_SOURCE +#include "ioprivate.h" +#include "procbuf.h" +#include +#include +#include + + +#ifndef FORK +#define FORK vfork +#ifndef __386BSD__ +extern "C" _G_pid_t vfork(void); +#else +extern "C" int vfork(void); +#endif +#endif + +procbuf::procbuf(const char *command, int mode) : filebuf() +{ + open(command, mode); +} + +procbuf *procbuf::open(const char *command, int mode) +{ + int read_or_write; + if (is_open()) + return NULL; + int pipe_fds[2]; + int parent_end, child_end; + if (::pipe(pipe_fds) < 0) + return NULL; + if (mode == ios::in) { + parent_end = pipe_fds[0]; + child_end = pipe_fds[1]; + read_or_write = _S_NO_WRITES; + } + else { + parent_end = pipe_fds[1]; + child_end = pipe_fds[0]; + read_or_write = _S_NO_READS; + } + _pid = FORK(); + if (_pid == 0) { + ::close(parent_end); + int child_std_end = mode == ios::in ? 1 : 0; + if (child_end != child_std_end) { + ::dup2(child_end, child_std_end); + ::close(child_end); + } + ::execl("/bin/sh", "sh", "-c", command, NULL); + ::_exit(127); + } + ::close(child_end); + if (_pid < 0) { + ::close(parent_end); + return NULL; + } + _fb._fileno = parent_end; + xsetflags(read_or_write, _S_NO_READS|_S_NO_WRITES); + return this; +} + +/* #define USE_SIGMASK */ + +int procbuf::sys_close() +{ + _G_pid_t wait_pid; + int status = filebuf::sys_close(); + if (status < 0) + return status; + int wstatus; +#if defined(SIG_BLOCK) && defined(SIG_SETMASK) + sigset_t set, oset; + sigemptyset (&set); + sigaddset (&set, SIGINT); + sigaddset (&set, SIGQUIT); + sigaddset (&set, SIGHUP); + sigprocmask (SIG_BLOCK, &set, &oset); +#else +#ifdef USE_SIGMASK + int mask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGHUP)); +#else + typedef void (*void_func)(int); + void_func intsave = (void_func)signal(SIGINT, SIG_IGN); + void_func quitsave = (void_func)signal(SIGQUIT, SIG_IGN); + void_func hupsave = (void_func)signal(SIGHUP, SIG_IGN); +#endif +#endif + while ((wait_pid = wait(&wstatus)) != _pid && wait_pid != -1) { } +#if defined(SIG_BLOCK) && defined(SIG_SETMASK) + sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL); +#else +#ifdef USE_SIGMASK + (void) sigsetmask(mask); +#else + signal(SIGINT, intsave); + signal(SIGQUIT, quitsave); + signal(SIGHUP, hupsave); +#endif +#endif + if (wait_pid == -1) + return -1; + return 0; +} + +procbuf::~procbuf() +{ + close(); +} diff --git a/gnu/lib/libg++/iostream/procbuf.h b/gnu/lib/libg++/iostream/procbuf.h new file mode 100644 index 0000000000..47ab81e12e --- /dev/null +++ b/gnu/lib/libg++/iostream/procbuf.h @@ -0,0 +1,29 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1992 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#include + +class procbuf : public filebuf { + _G_pid_t _pid; + public: + procbuf() : filebuf() { } + procbuf(const char *command, int mode); + procbuf* open(const char *command, int mode); + procbuf *close() { return (procbuf*)filebuf::close(); } + virtual int sys_close(); + ~procbuf(); +}; diff --git a/gnu/lib/libg++/iostream/sbufvform.C b/gnu/lib/libg++/iostream/sbufvform.C new file mode 100644 index 0000000000..533fd719d0 --- /dev/null +++ b/gnu/lib/libg++/iostream/sbufvform.C @@ -0,0 +1,856 @@ +/* + * Copyright (c) 1990 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "%W% (Berkeley) %G%"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Actual printf innards. + * + * This code is large and complicated... + */ + +#include +#include "ioprivate.h" +#include +#include + +/* + * Define FLOATING_POINT to get floating point. + */ +#ifndef NO_FLOATING_POINT +#define FLOATING_POINT +#endif + +/* end of configuration stuff */ + + +/* + * Helper class and function for `fprintf to unbuffered': creates a + * temporary buffer. We only work on write-only files; this avoids + * worries about ungetc buffers and so forth. + */ + +class help_streambuf : public backupbuf { + public: + char *buffer; + int buf_size; + streambuf *sb; + help_streambuf(streambuf *sbuf, char *buf, int n) { + sb = sbuf; buffer = buf; buf_size = n; + setp(buffer, buffer+buf_size); } + ~help_streambuf(); + virtual int overflow(int c = EOF); +}; + +int help_streambuf::overflow(int c) +{ + int used = pptr() - pbase(); + if (used) { + sb->sputn(pbase(), used); + pbump(-used); + } + if (c == EOF || buf_size == 0) + return sb->overflow(c); + return sputc(c); +} +help_streambuf::~help_streambuf() +{ + int used = pptr() - pbase(); + if (used) { + sb->sputn(pbase(), used); + pbump(-used); + } +} + +int help_vform(streambuf *sb, char const *fmt0, va_list ap) +{ + char buf[_G_BUFSIZ]; + help_streambuf helper(sb, buf, _G_BUFSIZ); + return helper.vform(fmt0, ap); +} + +#ifdef FLOATING_POINT + +#include "floatio.h" +#define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ +#define DEFPREC 6 +extern "C" double modf(double, double*); + +#else /* no FLOATING_POINT */ + +#define BUF 40 + +#endif /* FLOATING_POINT */ + + +/* + * Macros for converting digits to letters and vice versa + */ +#define to_digit(c) ((c) - '0') +#define is_digit(c) ((unsigned)to_digit(c) <= 9) +#define to_char(n) ((n) + '0') + +/* + * Flags used during conversion. + */ +#define LONGINT 0x01 /* long integer */ +#define LONGDBL 0x02 /* long double; unimplemented */ +#define SHORTINT 0x04 /* short integer */ +#define ALT 0x08 /* alternate form */ +#define LADJUST 0x10 /* left adjustment */ +#define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ +#define HEXPREFIX 0x40 /* add 0x or 0X prefix */ + +int streambuf::vform(char const *fmt0, _G_va_list ap) +{ + register const char *fmt; /* format string */ + register int ch; /* character from fmt */ + register int n; /* handy integer (short term usage) */ + register char *cp; /* handy char pointer (short term usage) */ + const char *fmark; /* for remembering a place in fmt */ + register int flags; /* flags as above */ + int ret; /* return value accumulator */ + int width; /* width from format (%8d), or 0 */ + int prec; /* precision from format (%.3d), or -1 */ + char sign; /* sign prefix (' ', '+', '-', or \0) */ +#ifdef FLOATING_POINT + int softsign; /* temporary negative sign for floats */ + double _double; /* double precision arguments %[eEfgG] */ +#ifndef USE_DTOA + int fpprec; /* `extra' floating precision in [eEfgG] */ +#endif +#endif + unsigned long _ulong; /* integer arguments %[diouxX] */ + enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */ + int dprec; /* a copy of prec if [diouxX], 0 otherwise */ + int dpad; /* extra 0 padding needed for integers */ + int fieldsz; /* field size expanded by sign, dpad etc */ + // The initialization of 'size' is to suppress a warning that + // 'size' might be used unitialized. It seems gcc can't + // quite grok this spaghetti code ... + int size = 0; /* size of converted field or string */ + char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ + char ox[2]; /* space for 0x hex-prefix */ + + /* + * Choose PADSIZE to trade efficiency vs size. If larger + * printf fields occur frequently, increase PADSIZE (and make + * the initialisers below longer). + */ +#define PADSIZE 16 /* pad chunk size */ + static char const blanks[PADSIZE] = + {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; + static char const zeroes[PADSIZE] = + {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; + + /* + * BEWARE, these `goto error' on error, and PAD uses `n'. + */ +#define PRINT(ptr, len) \ + do { if (sputn(ptr, len) != len) goto error; } while (0) +#if 1 +#define PAD_SP(howmany) if (padn(' ', howmany) < 0) goto error; +#define PAD_0(howmany) if (padn('0', howmany) < 0) goto error; +#else +#define PAD(howmany, with) { \ + if ((n = (howmany)) > 0) { \ + while (n > PADSIZE) { \ + PRINT(with, PADSIZE); \ + n -= PADSIZE; \ + } \ + PRINT(with, n); \ + } \ +} +#define PAD_SP(howmany) PAD(howmany, blanks) +#define PAD_0(howmany) PAD(howmany, zeroes) +#endif + + /* + * To extend shorts properly, we need both signed and unsigned + * argument extraction methods. + */ +#define SARG() \ + (flags&LONGINT ? va_arg(ap, long) : \ + flags&SHORTINT ? (long)(short)va_arg(ap, int) : \ + (long)va_arg(ap, int)) +#define UARG() \ + (flags&LONGINT ? va_arg(ap, unsigned long) : \ + flags&SHORTINT ? (unsigned long)(unsigned short)va_arg(ap, int) : \ + (unsigned long)va_arg(ap, unsigned int)) + + /* optimise cerr (and other unbuffered Unix files) */ + if (unbuffered()) + return help_vform(this, fmt0, ap); + + fmt = fmt0; + ret = 0; + + /* + * Scan the format for conversions (`%' character). + */ + for (;;) { + for (fmark = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) + /* void */; + if ((n = fmt - fmark) != 0) { + PRINT(fmark, n); + ret += n; + } + if (ch == '\0') + goto done; + fmt++; /* skip over '%' */ + + flags = 0; + dprec = 0; +#if defined(FLOATING_POINT) && !defined (USE_DTOA) + fpprec = 0; +#endif + width = 0; + prec = -1; + sign = '\0'; + +rflag: ch = *fmt++; +reswitch: switch (ch) { + case ' ': + /* + * ``If the space and + flags both appear, the space + * flag will be ignored.'' + * -- ANSI X3J11 + */ + if (!sign) + sign = ' '; + goto rflag; + case '#': + flags |= ALT; + goto rflag; + case '*': + /* + * ``A negative field width argument is taken as a + * - flag followed by a positive field width.'' + * -- ANSI X3J11 + * They don't exclude field widths read from args. + */ + if ((width = va_arg(ap, int)) >= 0) + goto rflag; + width = -width; + /* FALLTHROUGH */ + case '-': + flags |= LADJUST; + flags &= ~ZEROPAD; /* '-' disables '0' */ + goto rflag; + case '+': + sign = '+'; + goto rflag; + case '.': + if ((ch = *fmt++) == '*') { + n = va_arg(ap, int); + prec = n < 0 ? -1 : n; + goto rflag; + } + n = 0; + while (is_digit(ch)) { + n = 10 * n + to_digit(ch); + ch = *fmt++; + } + prec = n < 0 ? -1 : n; + goto reswitch; + case '0': + /* + * ``Note that 0 is taken as a flag, not as the + * beginning of a field width.'' + * -- ANSI X3J11 + */ + if (!(flags & LADJUST)) + flags |= ZEROPAD; /* '-' disables '0' */ + goto rflag; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + do { + n = 10 * n + to_digit(ch); + ch = *fmt++; + } while (is_digit(ch)); + width = n; + goto reswitch; +#ifdef FLOATING_POINT + case 'L': + flags |= LONGDBL; + goto rflag; +#endif + case 'h': + flags |= SHORTINT; + goto rflag; + case 'l': + flags |= LONGINT; + goto rflag; + case 'c': + *(cp = buf) = va_arg(ap, int); + size = 1; + sign = '\0'; + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + _ulong = SARG(); + if ((long)_ulong < 0) { + _ulong = -_ulong; + sign = '-'; + } + base = DEC; + goto number; +#ifdef FLOATING_POINT + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + _double = va_arg(ap, double); +#ifdef USE_DTOA + { + ios::fmtflags fmt_flags = 0; + int fill = ' '; + if (flags & ALT) + fmt_flags |= ios::showpoint; + if (flags & LADJUST) + fmt_flags |= ios::left; + else if (flags & ZEROPAD) + fmt_flags |= ios::internal, fill = '0'; + n = __outfloat(_double, this, ch, width, + prec < 0 ? DEFPREC : prec, + fmt_flags, sign, fill); + if (n < 0) + goto error; + ret += n; + } + // CHECK ERROR! + continue; +#else + /* + * don't do unrealistic precision; just pad it with + * zeroes later, so buffer size stays rational. + */ + if (prec > MAXFRACT) { + if ((ch != 'g' && ch != 'G') || (flags&ALT)) + fpprec = prec - MAXFRACT; + prec = MAXFRACT; + } else if (prec == -1) + prec = DEFPREC; + // __cvt_double may have to round up before the + // "start" of its buffer, i.e. + // ``intf("%.2f", (double)9.999);''; + // if the first character is still NUL, it did. + // softsign avoids negative 0 if _double < 0 but + // no significant digits will be shown. + cp = buf; + *cp = '\0'; + size = __cvt_double(_double, prec, flags, &softsign, + ch, cp, buf + sizeof(buf)); + if (softsign) + sign = '-'; + if (*cp == '\0') + cp++; + break; +#endif +#endif /* FLOATING_POINT */ + case 'n': + if (flags & LONGINT) + *va_arg(ap, long *) = ret; + else if (flags & SHORTINT) + *va_arg(ap, short *) = ret; + else + *va_arg(ap, int *) = ret; + continue; /* no output */ + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + _ulong = UARG(); + base = OCT; + goto nosign; + case 'p': + /* + * ``The argument shall be a pointer to void. The + * value of the pointer is converted to a sequence + * of printable characters, in an implementation- + * defined manner.'' + * -- ANSI X3J11 + */ + /* NOSTRICT */ + _ulong = (unsigned long)va_arg(ap, void *); + base = HEX; + flags |= HEXPREFIX; + ch = 'x'; + goto nosign; + case 's': + if ((cp = va_arg(ap, char *)) == NULL) + cp = "(null)"; + if (prec >= 0) { + /* + * can't use strlen; can only look for the + * NUL in the first `prec' characters, and + * strlen() will go further. + */ + char *p = (char*)memchr(cp, 0, prec); + + if (p != NULL) { + size = p - cp; + if (size > prec) + size = prec; + } else + size = prec; + } else + size = strlen(cp); + sign = '\0'; + break; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + _ulong = UARG(); + base = DEC; + goto nosign; + case 'X': + case 'x': + _ulong = UARG(); + base = HEX; + /* leading 0x/X only if non-zero */ + if (flags & ALT && _ulong != 0) + flags |= HEXPREFIX; + + /* unsigned conversions */ +nosign: sign = '\0'; + /* + * ``... diouXx conversions ... if a precision is + * specified, the 0 flag will be ignored.'' + * -- ANSI X3J11 + */ +number: if ((dprec = prec) >= 0) + flags &= ~ZEROPAD; + + /* + * ``The result of converting a zero value with an + * explicit precision of zero is no characters.'' + * -- ANSI X3J11 + */ + cp = buf + BUF; + if (_ulong != 0 || prec != 0) { + char *xdigs; /* digits for [xX] conversion */ + /* + * unsigned mod is hard, and unsigned mod + * by a constant is easier than that by + * a variable; hence this switch. + */ + switch (base) { + case OCT: + do { + *--cp = to_char(_ulong & 7); + _ulong >>= 3; + } while (_ulong); + /* handle octal leading 0 */ + if (flags & ALT && *cp != '0') + *--cp = '0'; + break; + + case DEC: + /* many numbers are 1 digit */ + while (_ulong >= 10) { + *--cp = to_char(_ulong % 10); + _ulong /= 10; + } + *--cp = to_char(_ulong); + break; + + case HEX: + if (ch == 'X') + xdigs = "0123456789ABCDEF"; + else /* ch == 'x' || ch == 'p' */ + xdigs = "0123456789abcdef"; + do { + *--cp = xdigs[_ulong & 15]; + _ulong >>= 4; + } while (_ulong); + break; + + default: + cp = "bug in vform: bad base"; + goto skipsize; + } + } + size = buf + BUF - cp; + skipsize: + break; + default: /* "%?" prints ?, unless ? is NUL */ + if (ch == '\0') + goto done; + /* pretend it was %c with argument ch */ + cp = buf; + *cp = ch; + size = 1; + sign = '\0'; + break; + } + + /* + * All reasonable formats wind up here. At this point, + * `cp' points to a string which (if not flags&LADJUST) + * should be padded out to `width' places. If + * flags&ZEROPAD, it should first be prefixed by any + * sign or other prefix; otherwise, it should be blank + * padded before the prefix is emitted. After any + * left-hand padding and prefixing, emit zeroes + * required by a decimal [diouxX] precision, then print + * the string proper, then emit zeroes required by any + * leftover floating precision; finally, if LADJUST, + * pad with blanks. + */ + + /* + * compute actual size, so we know how much to pad. + */ +#if defined(FLOATING_POINT) && !defined (USE_DTOA) + fieldsz = size + fpprec; +#else + fieldsz = size; +#endif + dpad = dprec - size; + if (dpad < 0) + dpad = 0; + + if (sign) + fieldsz++; + else if (flags & HEXPREFIX) + fieldsz += 2; + fieldsz += dpad; + + /* right-adjusting blank padding */ + if ((flags & (LADJUST|ZEROPAD)) == 0) + PAD_SP(width - fieldsz); + + /* prefix */ + if (sign) { + PRINT(&sign, 1); + } else if (flags & HEXPREFIX) { + ox[0] = '0'; + ox[1] = ch; + PRINT(ox, 2); + } + + /* right-adjusting zero padding */ + if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) + PAD_0(width - fieldsz); + + /* leading zeroes from decimal precision */ + PAD_0(dpad); + + /* the string or number proper */ + PRINT(cp, size); + +#if defined(FLOATING_POINT) && !defined (USE_DTOA) + /* trailing f.p. zeroes */ + PAD_0(fpprec); +#endif + + /* left-adjusting padding (always blank) */ + if (flags & LADJUST) + PAD_SP(width - fieldsz); + + /* finally, adjust ret */ + ret += width > fieldsz ? width : fieldsz; + + } +done: + return ret; +error: + return EOF; + /* NOTREACHED */ +} + +#if defined(FLOATING_POINT) && !defined(USE_DTOA) + +static char *exponent(register char *p, register int exp, int fmtch) +{ + register char *t; + char expbuf[MAXEXP]; + + *p++ = fmtch; + if (exp < 0) { + exp = -exp; + *p++ = '-'; + } + else + *p++ = '+'; + t = expbuf + MAXEXP; + if (exp > 9) { + do { + *--t = to_char(exp % 10); + } while ((exp /= 10) > 9); + *--t = to_char(exp); + for (; t < expbuf + MAXEXP; *p++ = *t++); + } + else { + *p++ = '0'; + *p++ = to_char(exp); + } + return (p); +} + +static char * round(double fract, int *exp, + register char *start, register char *end, + char ch, int *signp) +{ + double tmp; + + if (fract) + (void)modf(fract * 10, &tmp); + else + tmp = to_digit(ch); + if (tmp > 4) + for (;; --end) { + if (*end == '.') + --end; + if (++*end <= '9') + break; + *end = '0'; + if (end == start) { + if (exp) { /* e/E; increment exponent */ + *end = '1'; + ++*exp; + } + else { /* f; add extra digit */ + *--end = '1'; + --start; + } + break; + } + } + /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ + else if (*signp == '-') + for (;; --end) { + if (*end == '.') + --end; + if (*end != '0') + break; + if (end == start) + *signp = 0; + } + return (start); +} + +int __cvt_double(double number, register int prec, int flags, int *signp, + int fmtch, char *startp, char *endp) +{ + register char *p, *t; + register double fract; + int dotrim = 0, expcnt, gformat = 0; + double integer, tmp; + + expcnt = 0; + if (number < 0) { + number = -number; + *signp = '-'; + } else + *signp = 0; + + fract = modf(number, &integer); + + /* get an extra slot for rounding. */ + t = ++startp; + + /* + * get integer portion of number; put into the end of the buffer; the + * .01 is added for modf(356.0 / 10, &integer) returning .59999999... + */ + for (p = endp - 1; integer; ++expcnt) { + tmp = modf(integer / 10, &integer); + *p-- = to_char((int)((tmp + .01) * 10)); + } + switch (fmtch) { + case 'f': + case 'F': + /* reverse integer into beginning of buffer */ + if (expcnt) + for (; ++p < endp; *t++ = *p); + else + *t++ = '0'; + /* + * if precision required or alternate flag set, add in a + * decimal point. + */ + if (prec || flags&ALT) + *t++ = '.'; + /* if requires more precision and some fraction left */ + if (fract) { + if (prec) + do { + fract = modf(fract * 10, &tmp); + *t++ = to_char((int)tmp); + } while (--prec && fract); + if (fract) + startp = round(fract, (int *)NULL, startp, + t - 1, (char)0, signp); + } + for (; prec--; *t++ = '0'); + break; + case 'e': + case 'E': +eformat: if (expcnt) { + *t++ = *++p; + if (prec || flags&ALT) + *t++ = '.'; + /* if requires more precision and some integer left */ + for (; prec && ++p < endp; --prec) + *t++ = *p; + /* + * if done precision and more of the integer component, + * round using it; adjust fract so we don't re-round + * later. + */ + if (!prec && ++p < endp) { + fract = 0; + startp = round((double)0, &expcnt, startp, + t - 1, *p, signp); + } + /* adjust expcnt for digit in front of decimal */ + --expcnt; + } + /* until first fractional digit, decrement exponent */ + else if (fract) { + /* adjust expcnt for digit in front of decimal */ + for (expcnt = -1;; --expcnt) { + fract = modf(fract * 10, &tmp); + if (tmp) + break; + } + *t++ = to_char((int)tmp); + if (prec || flags&ALT) + *t++ = '.'; + } + else { + *t++ = '0'; + if (prec || flags&ALT) + *t++ = '.'; + } + /* if requires more precision and some fraction left */ + if (fract) { + if (prec) + do { + fract = modf(fract * 10, &tmp); + *t++ = to_char((int)tmp); + } while (--prec && fract); + if (fract) + startp = round(fract, &expcnt, startp, + t - 1, (char)0, signp); + } + /* if requires more precision */ + for (; prec--; *t++ = '0'); + + /* unless alternate flag, trim any g/G format trailing 0's */ + if (gformat && !(flags&ALT)) { + while (t > startp && *--t == '0'); + if (*t == '.') + --t; + ++t; + } + t = exponent(t, expcnt, fmtch); + break; + case 'g': + case 'G': + /* a precision of 0 is treated as a precision of 1. */ + if (!prec) + ++prec; + /* + * ``The style used depends on the value converted; style e + * will be used only if the exponent resulting from the + * conversion is less than -4 or greater than the precision.'' + * -- ANSI X3J11 + */ + if (expcnt > prec || (!expcnt && fract && fract < .0001)) { + /* + * g/G format counts "significant digits, not digits of + * precision; for the e/E format, this just causes an + * off-by-one problem, i.e. g/G considers the digit + * before the decimal point significant and e/E doesn't + * count it as precision. + */ + --prec; + fmtch -= 2; /* G->E, g->e */ + gformat = 1; + goto eformat; + } + /* + * reverse integer into beginning of buffer, + * note, decrement precision + */ + if (expcnt) + for (; ++p < endp; *t++ = *p, --prec); + else + *t++ = '0'; + /* + * if precision required or alternate flag set, add in a + * decimal point. If no digits yet, add in leading 0. + */ + if (prec || flags&ALT) { + dotrim = 1; + *t++ = '.'; + } + else + dotrim = 0; + /* if requires more precision and some fraction left */ + if (fract) { + if (prec) { + /* If no integer part, don't count initial + * zeros as significant digits. */ + do { + fract = modf(fract * 10, &tmp); + *t++ = to_char((int)tmp); + } while(!tmp && !expcnt); + while (--prec && fract) { + fract = modf(fract * 10, &tmp); + *t++ = to_char((int)tmp); + } + } + if (fract) + startp = round(fract, (int *)NULL, startp, + t - 1, (char)0, signp); + } + /* alternate format, adds 0's for precision, else trim 0's */ + if (flags&ALT) + for (; prec--; *t++ = '0'); + else if (dotrim) { + while (t > startp && *--t == '0'); + if (*t != '.') + ++t; + } + } + return (t - startp); +} + +#endif /* defined(FLOATING_POINT) && !defined(USE_DTOA) */ + +int streambuf::form(char const *format ...) +{ + va_list ap; + va_start(ap, format); + int count = vform(format, ap); + va_end(ap); + return count; +} diff --git a/gnu/lib/libg++/iostream/sbufvscan.C b/gnu/lib/libg++/iostream/sbufvscan.C new file mode 100644 index 0000000000..3c7d29a8c7 --- /dev/null +++ b/gnu/lib/libg++/iostream/sbufvscan.C @@ -0,0 +1,746 @@ +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +// Extensively hacked for GNU iostream by Per Bothner 1991, 1992. +// Changes copyright Per Bothner 1992. + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "%W% (Berkeley) %G%"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#ifndef NO_STDARG +#include +#else +#include +#endif + +#ifndef NO_FLOATING_POINT +#define FLOATING_POINT +#endif + +#ifdef FLOATING_POINT +#include "floatio.h" +#define BUF (MAXEXP+MAXFRACT+3) /* 3 = sign + decimal point + NUL */ +#else +#define BUF 40 +#endif + +/* + * Flags used during conversion. + */ +#define LONG 0x01 /* l: long or double */ +#define LONGDBL 0x02 /* L: long double; unimplemented */ +#define SHORT 0x04 /* h: short */ +#define SUPPRESS 0x08 /* suppress assignment */ +#define POINTER 0x10 /* weird %p pointer (`fake hex') */ +#define NOSKIP 0x20 /* do not skip blanks */ + +/* + * The following are used in numeric conversions only: + * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point; + * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral. + */ +#define SIGNOK 0x40 /* +/- is (still) legal */ +#define NDIGITS 0x80 /* no digits detected */ + +#define DPTOK 0x100 /* (float) decimal point is still legal */ +#define EXPOK 0x200 /* (float) exponent (e+3, etc) still legal */ + +#define PFXOK 0x100 /* 0x prefix is (still) legal */ +#define NZDIGITS 0x200 /* no zero digits detected */ + +/* + * Conversion types. + */ +#define CT_CHAR 0 /* %c conversion */ +#define CT_CCL 1 /* %[...] conversion */ +#define CT_STRING 2 /* %s conversion */ +#define CT_INT 3 /* integer, i.e., strtol or strtoul */ +#define CT_FLOAT 4 /* floating, i.e., strtod */ + +#define u_char unsigned char +#define u_long unsigned long + +extern "C" u_long strtoul(const char*, char**, int); +static const u_char *__sccl(register char *tab, register const u_char *fmt); + +// If state is non-NULL, set failbit and/or eofbit as appropriate. + +int streambuf::vscan(char const *fmt0, + _G_va_list ap, + ios *stream /* = NULL */) +{ + register const u_char *fmt = (const u_char *)fmt0; + register int c; /* character from format, or conversion */ + register size_t width; /* field width, or 0 */ + register char *p; /* points into all kinds of strings */ + register int n; /* handy integer */ + register int flags; /* flags as defined above */ + register char *p0; /* saves original value of p when necessary */ + int nassigned; /* number of fields assigned */ + int nread; /* number of characters consumed from fp */ + // Assignments to base and ccfn are just to suppress warnings from gcc. + int base = 0; /* base argument to strtol/strtoul */ + typedef u_long (*strtoulfn)(const char*, char**, int); + strtoulfn ccfn = 0; + // conversion function (strtol/strtoul) + char ccltab[256]; /* character class table for %[...] */ + char buf[BUF]; /* buffer for numeric conversions */ + int seen_eof = 0; + + /* `basefix' is used to avoid `if' tests in the integer scanner */ + static short basefix[17] = + { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; + + nassigned = 0; + nread = 0; + for (;;) { + c = *fmt++; + if (c == 0) + goto done; + if (isspace(c)) { + for (;;) { + c = sbumpc(); + if (c == EOF) + goto eof_failure; + if (!isspace(c)) { + sputbackc(c); + break; + } + nread++; + } + continue; + } + if (c != '%') + goto literal; + width = 0; + flags = 0; + /* + * switch on the format. continue if done; + * break once format type is derived. + */ +again: c = *fmt++; + switch (c) { + case '%': +literal: + n = sbumpc(); + if (n == EOF) + goto eof_failure; + if (n != c) { + sputbackc(n); + goto match_failure; + } + nread++; + continue; + + case '*': + flags |= SUPPRESS; + goto again; + case 'l': + flags |= LONG; + goto again; + case 'L': + flags |= LONGDBL; + goto again; + case 'h': + flags |= SHORT; + goto again; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + width = width * 10 + c - '0'; + goto again; + + /* + * Conversions. + * Those marked `compat' are for 4.[123]BSD compatibility. + * + * (According to ANSI, E and X formats are supposed + * to the same as e and x. Sorry about that.) + */ + case 'D': /* compat */ + flags |= LONG; + /* FALLTHROUGH */ + case 'd': + c = CT_INT; + ccfn = (strtoulfn)strtol; + base = 10; + break; + + case 'i': + c = CT_INT; + ccfn = (strtoulfn)strtol; + base = 0; + break; + + case 'O': /* compat */ + flags |= LONG; + /* FALLTHROUGH */ + case 'o': + c = CT_INT; + ccfn = strtoul; + base = 8; + break; + + case 'u': + c = CT_INT; + ccfn = strtoul; + base = 10; + break; + + case 'X': + case 'x': + flags |= PFXOK; /* enable 0x prefixing */ + c = CT_INT; + ccfn = strtoul; + base = 16; + break; + +#ifdef FLOATING_POINT + case 'E': case 'F': + case 'e': case 'f': case 'g': + c = CT_FLOAT; + break; +#endif + + case 's': + c = CT_STRING; + break; + + case '[': + fmt = __sccl(ccltab, fmt); + flags |= NOSKIP; + c = CT_CCL; + break; + + case 'c': + flags |= NOSKIP; + c = CT_CHAR; + break; + + case 'p': /* pointer format is like hex */ + flags |= POINTER | PFXOK; + c = CT_INT; + ccfn = strtoul; + base = 16; + break; + + case 'n': + if (flags & SUPPRESS) /* ??? */ + continue; + if (flags & SHORT) + *va_arg(ap, short *) = nread; + else if (flags & LONG) + *va_arg(ap, long *) = nread; + else + *va_arg(ap, int *) = nread; + continue; + + /* + * Disgusting backwards compatibility hacks. XXX + */ + case '\0': /* compat */ + nassigned = EOF; + goto done; + + default: /* compat */ + if (isupper(c)) + flags |= LONG; + c = CT_INT; + ccfn = (strtoulfn)strtol; + base = 10; + break; + } + + /* + * We have a conversion that requires input. + */ + if (sgetc() == EOF) + goto eof_failure; + + /* + * Consume leading white space, except for formats + * that suppress this. + */ + if ((flags & NOSKIP) == 0) { + n = (unsigned char)*_gptr; + while (isspace(n)) { + _gptr++; + nread++; + n = sgetc(); + if (n == EOF) + goto eof_failure; + } + // Note that there is at least one character in + // the buffer, so conversions that do not set NOSKIP + // can no longer result in an input failure. + } + + /* + * Do the conversion. + */ + switch (c) { + + case CT_CHAR: + /* scan arbitrary characters (sets NOSKIP) */ + if (width == 0) // FIXME! + width = 1; + if (flags & SUPPRESS) { + size_t sum = 0; + for (;;) { + if ((n = _egptr - _gptr) < (int)width) { + sum += n; + width -= n; + _gptr += n; + if (underflow() == EOF) + if (sum == 0) + goto eof_failure; + else { + seen_eof++; + break; + } + } else { + sum += width; + _gptr += width; + break; + } + } + nread += sum; + } else { + size_t r = sgetn((char*)va_arg(ap, char*), + width); + if (r != width) + goto eof_failure; + nread += r; + nassigned++; + } + break; + + case CT_CCL: + /* scan a (nonempty) character class (sets NOSKIP) */ + if (width == 0) + width = ~0; /* `infinity' */ + /* take only those things in the class */ + if (flags & SUPPRESS) { + n = 0; + while (ccltab[(unsigned char)*_gptr]) { + n++, _gptr++; + if (--width == 0) + break; + if (sgetc() == EOF) { + if (n == 0) + goto eof_failure; + seen_eof++; + break; + } + } + if (n == 0) + goto match_failure; + } else { + p0 = p = va_arg(ap, char *); + while (ccltab[(unsigned char)*_gptr]) { + *p++ = *_gptr++; + if (--width == 0) + break; + if (sgetc() == EOF) { + if (p == p0) + goto eof_failure; + seen_eof++; + break; + } + } + n = p - p0; + if (n == 0) + goto match_failure; + *p = 0; + nassigned++; + } + nread += n; + break; + + case CT_STRING: + /* like CCL, but zero-length string OK, & no NOSKIP */ + if (width == 0) + width = ~0; + if (flags & SUPPRESS) { + n = 0; + while (!isspace((unsigned char)*_gptr)) { + n++, _gptr++; + if (--width == 0) + break; + if (sgetc() == EOF) { + seen_eof++; + break; + } + } + nread += n; + } else { + p0 = p = va_arg(ap, char *); + while (!isspace((unsigned char)*_gptr)) { + *p++ = *_gptr++; + if (--width == 0) + break; + if (sgetc() == EOF) { + seen_eof++; + break; + } + } + *p = 0; + nread += p - p0; + nassigned++; + } + continue; + + case CT_INT: + /* scan an integer as if by strtol/strtoul */ + if (width == 0 || width > sizeof(buf) - 1) + width = sizeof(buf) - 1; + flags |= SIGNOK | NDIGITS | NZDIGITS; + for (p = buf; width; width--) { + c = (unsigned char)*_gptr; + /* + * Switch on the character; `goto ok' + * if we accept it as a part of number. + */ + switch (c) { + + /* + * The digit 0 is always legal, but is + * special. For %i conversions, if no + * digits (zero or nonzero) have been + * scanned (only signs), we will have + * base==0. In that case, we should set + * it to 8 and enable 0x prefixing. + * Also, if we have not scanned zero digits + * before this, do not turn off prefixing + * (someone else will turn it off if we + * have scanned any nonzero digits). + */ + case '0': + if (base == 0) { + base = 8; + flags |= PFXOK; + } + if (flags & NZDIGITS) + flags &= ~(SIGNOK|NZDIGITS|NDIGITS); + else + flags &= ~(SIGNOK|PFXOK|NDIGITS); + goto ok; + + /* 1 through 7 always legal */ + case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + base = basefix[base]; + flags &= ~(SIGNOK | PFXOK | NDIGITS); + goto ok; + + /* digits 8 and 9 ok iff decimal or hex */ + case '8': case '9': + base = basefix[base]; + if (base <= 8) + break; /* not legal here */ + flags &= ~(SIGNOK | PFXOK | NDIGITS); + goto ok; + + /* letters ok iff hex */ + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + /* no need to fix base here */ + if (base <= 10) + break; /* not legal here */ + flags &= ~(SIGNOK | PFXOK | NDIGITS); + goto ok; + + /* sign ok only as first character */ + case '+': case '-': + if (flags & SIGNOK) { + flags &= ~SIGNOK; + goto ok; + } + break; + + /* x ok iff flag still set & 2nd char */ + case 'x': case 'X': + if (flags & PFXOK && p == buf + 1) { + base = 16; /* if %i */ + flags &= ~PFXOK; + goto ok; + } + break; + } + + /* + * If we got here, c is not a legal character + * for a number. Stop accumulating digits. + */ + break; + ok: + /* + * c is legal: store it and look at the next. + */ + *p++ = c; + _gptr++; + if (sgetc() == EOF) { + seen_eof++; + break; /* EOF */ + } + } + /* + * If we had only a sign, it is no good; push + * back the sign. If the number ends in `x', + * it was [sign] '0' 'x', so push back the x + * and treat it as [sign] '0'. + */ + if (flags & NDIGITS) { + if (p > buf) + (void) sputbackc(*(u_char *)--p); + goto match_failure; + } + c = ((u_char *)p)[-1]; + if (c == 'x' || c == 'X') { + --p; + (void) sputbackc(c); + } + if ((flags & SUPPRESS) == 0) { + u_long res; + + *p = 0; + res = (*ccfn)(buf, (char **)NULL, base); + if (flags & POINTER) + *va_arg(ap, void **) = (void *)res; + else if (flags & SHORT) + *va_arg(ap, short *) = res; + else if (flags & LONG) + *va_arg(ap, long *) = res; + else + *va_arg(ap, int *) = res; + nassigned++; + } + nread += p - buf; + break; + +#ifdef FLOATING_POINT + case CT_FLOAT: + /* scan a floating point number as if by strtod */ + if (width == 0 || width > sizeof(buf) - 1) + width = sizeof(buf) - 1; + flags |= SIGNOK | NDIGITS | DPTOK | EXPOK; + for (p = buf; width; width--) { + c = (unsigned char)*_gptr; + /* + * This code mimicks the integer conversion + * code, but is much simpler. + */ + switch (c) { + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + flags &= ~(SIGNOK | NDIGITS); + goto fok; + + case '+': case '-': + if (flags & SIGNOK) { + flags &= ~SIGNOK; + goto fok; + } + break; + case '.': + if (flags & DPTOK) { + flags &= ~(SIGNOK | DPTOK); + goto fok; + } + break; + case 'e': case 'E': + /* no exponent without some digits */ + if ((flags&(NDIGITS|EXPOK)) == EXPOK) { + flags = + (flags & ~(EXPOK|DPTOK)) | + SIGNOK | NDIGITS; + goto fok; + } + break; + } + break; + fok: + *p++ = c; + _gptr++; + if (sgetc() == EOF) { + seen_eof++; + break; /* EOF */ + } + } + /* + * If no digits, might be missing exponent digits + * (just give back the exponent) or might be missing + * regular digits, but had sign and/or decimal point. + */ + if (flags & NDIGITS) { + if (flags & EXPOK) { + /* no digits at all */ + while (p > buf) + sputbackc(*(u_char *)--p); + goto match_failure; + } + /* just a bad exponent (e and maybe sign) */ + c = *(u_char *)--p; + if (c != 'e' && c != 'E') { + (void)sputbackc(c);/* sign */ + c = *(u_char *)--p; + } + (void) sputbackc(c); + } + if ((flags & SUPPRESS) == 0) { + double res; + *p = 0; +#ifdef USE_DTOA + res = strtod(buf, NULL); +#else + res = atof(buf); +#endif + if (flags & LONG) + *va_arg(ap, double *) = res; + else + *va_arg(ap, float *) = res; + nassigned++; + } + nread += p - buf; + break; +#endif /* FLOATING_POINT */ + } + } +eof_failure: + seen_eof++; +input_failure: + if (nassigned == 0) + nassigned = -1; +match_failure: + if (stream) + stream->set(ios::failbit); +done: + if (stream && seen_eof) + stream->set(ios::eofbit); + return (nassigned); +} + +/* + * Fill in the given table from the scanset at the given format + * (just after `['). Return a pointer to the character past the + * closing `]'. The table has a 1 wherever characters should be + * considered part of the scanset. + */ +static const u_char *__sccl(register char *tab, register const u_char *fmt) +{ + register int c, n, v; + + /* first `clear' the whole table */ + c = *fmt++; /* first char hat => negated scanset */ + if (c == '^') { + v = 1; /* default => accept */ + c = *fmt++; /* get new first char */ + } else + v = 0; /* default => reject */ + /* should probably use memset here */ + for (n = 0; n < 256; n++) + tab[n] = v; + if (c == 0) + return (fmt - 1);/* format ended before closing ] */ + + /* + * Now set the entries corresponding to the actual scanset + * to the opposite of the above. + * + * The first character may be ']' (or '-') without being special; + * the last character may be '-'. + */ + v = 1 - v; + for (;;) { + tab[c] = v; /* take character c */ +doswitch: + n = *fmt++; /* and examine the next */ + switch (n) { + + case 0: /* format ended too soon */ + return (fmt - 1); + + case '-': + /* + * A scanset of the form + * [01+-] + * is defined as `the digit 0, the digit 1, + * the character +, the character -', but + * the effect of a scanset such as + * [a-zA-Z0-9] + * is implementation defined. The V7 Unix + * scanf treats `a-z' as `the letters a through + * z', but treats `a-a' as `the letter a, the + * character -, and the letter a'. + * + * For compatibility, the `-' is not considerd + * to define a range if the character following + * it is either a close bracket (required by ANSI) + * or is not numerically greater than the character + * we just stored in the table (c). + */ + n = *fmt; + if (n == ']' || n < c) { + c = '-'; + break; /* resume the for(;;) */ + } + fmt++; + do { /* fill in the range */ + tab[++c] = v; + } while (c < n); +#if 1 /* XXX another disgusting compatibility hack */ + /* + * Alas, the V7 Unix scanf also treats formats + * such as [a-c-e] as `the letters a through e'. + * This too is permitted by the standard.... + */ + goto doswitch; +#else + c = *fmt++; + if (c == 0) + return (fmt - 1); + if (c == ']') + return (fmt); +#endif + break; + + case ']': /* end of scanset */ + return (fmt); + + default: /* just another character */ + c = n; + break; + } + } + /* NOTREACHED */ +} + +int streambuf::scan(char const *format ...) +{ + va_list ap; + va_start(ap, format); + int count = vscan(format, ap); + va_end(ap); + return count; +} diff --git a/gnu/lib/libg++/iostream/sgetline.C b/gnu/lib/libg++/iostream/sgetline.C new file mode 100644 index 0000000000..0474596922 --- /dev/null +++ b/gnu/lib/libg++/iostream/sgetline.C @@ -0,0 +1,64 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1991 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#include "ioprivate.h" + +// Algorithm based on that used by Berkeley pre-4.4 fgets implementation. + +// Read chars into buf (of size n), until delim is seen. +// Return number of chars read (at most n-1). +// If extract_delim < 0, leave delimiter unread. +// If extract_delim > 0, insert delim in output. + +long streambuf::sgetline(char* buf, size_t n, char delim, int extract_delim) +{ + register char *ptr = buf; + if (n <= 0) + return EOF; + n--; // Leave space for final '\0'. + do { + int len = egptr() - gptr(); + if (len <= 0) + if (underflow() == EOF) + break; + else + len = egptr() - gptr(); + if (len >= (int)n) + len = n; + char *t = (char*)memchr((void*)_gptr, delim, len); + if (t != NULL) { + size_t old_len = ptr-buf; + len = t - _gptr; + if (extract_delim >= 0) { + t++; + old_len++; + if (extract_delim > 0) + len++; + } + memcpy((void*)ptr, (void*)_gptr, len); + ptr[len] = 0; + _gptr = t; + return old_len + len; + } + memcpy((void*)ptr, (void*)_gptr, len); + _gptr += len; + ptr += len; + n -= len; + } while (n != 0); + *ptr = 0; + return ptr - buf; +} diff --git a/gnu/lib/libg++/iostream/stdiostream.C b/gnu/lib/libg++/iostream/stdiostream.C new file mode 100644 index 0000000000..960a9a4a60 --- /dev/null +++ b/gnu/lib/libg++/iostream/stdiostream.C @@ -0,0 +1,112 @@ +// This is part of the iostream library, providing -*- C++ -*- input/output. +// Copyright (C) 1992 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifdef __GNUG__ +#pragma implementation +#endif + +#include + +// A stdiobuf is "tied" to a FILE object (as used by the stdio package). +// Thus a stdiobuf is always synchronized with the corresponding FILE, +// though at the cost of some overhead. (If you use the implementation +// of stdio supplied with this library, you don't need stdiobufs.) +// This implementation inherits from filebuf, but implement the virtual +// functions sys_read/..., using the stdio functions fread/... instead +// of the low-level read/... system calls. This has the advantage that +// we get all of the nice filebuf semantics automatically, though +// with some overhead. + + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +stdiobuf::stdiobuf(FILE *f) : filebuf(fileno(f)) +{ + _file = f; + // Turn off buffer in stdiobuf. Instead, rely on buffering in (FILE). + // Thus the stdiobuf will be synchronized with the FILE. + setbuf(NULL, 0); +} + +_G_ssize_t stdiobuf::sys_read(char* buf, size_t size) +{ + return fread(buf, 1, size, _file); +} + +_G_ssize_t stdiobuf::sys_write(const void *buf, long n) +{ + _G_ssize_t count = fwrite(buf, 1, n, _file); + if (_fb._offset >= 0) + _fb._offset += n; + return count; +} + +_G_fpos_t stdiobuf::sys_seek(_G_fpos_t offset, _seek_dir dir) +{ + // Normally, equivalent to: fdir=dir + int fdir = + (dir == ios::beg) ? SEEK_SET : + (dir == ios::cur) ? SEEK_CUR : + (dir == ios::end) ? SEEK_END : + dir; + return fseek(_file, offset, fdir); +} + +int stdiobuf::sys_close() +{ + int status = fclose(_file); + _file = NULL; + return status; +} + +int stdiobuf::sync() +{ + if (filebuf::sync() == EOF) + return EOF; + if (!(xflags() & _S_NO_WRITES)) + if (fflush(_file)) + return EOF; +#if 0 + // This loses when writing to a pipe. + if (fseek(_file, 0, SEEK_CUR) == EOF) + return EOF; +#endif + return 0; +} + +int stdiobuf::overflow(int c /* = EOF*/) +{ + if (filebuf::overflow(c) == EOF) + return EOF; + if (c != EOF) + return c; + return fflush(_file); +} + +int stdiobuf::xsputn(const char* s, int n) +{ + // The filebuf implementation of sputn loses. + return streambuf::xsputn(s, n); +} diff --git a/gnu/lib/libg++/iostream/stdiostream.h b/gnu/lib/libg++/iostream/stdiostream.h new file mode 100644 index 0000000000..4f29d31a58 --- /dev/null +++ b/gnu/lib/libg++/iostream/stdiostream.h @@ -0,0 +1,43 @@ +// This is part of the iostream library, providing -*- C++ -*- input/output. +// Copyright (C) 1992 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifndef _STDIOSTREAM_H +#define _STDIOSTREAM_H + +#ifdef __GNUG__ +#pragma interface +#endif + +#include +#include + +class stdiobuf : public filebuf { + protected: + FILE *_file; + public: + FILE* stdiofile() const { return _file; } + stdiobuf(FILE *f); + virtual _G_ssize_t sys_read(char* buf, _G_size_t size); + virtual _G_fpos_t sys_seek(_G_fpos_t, _seek_dir); + virtual _G_ssize_t sys_write(const void*, long); + virtual int sys_close(); + virtual int sync(); + virtual int overflow(int c = EOF); + virtual int xsputn(const char* s, int n); +}; + +#endif /* !_STDIOSTREAM_H */ diff --git a/gnu/lib/libg++/iostream/stdstrbufs.C b/gnu/lib/libg++/iostream/stdstrbufs.C new file mode 100644 index 0000000000..2ae80fa9c4 --- /dev/null +++ b/gnu/lib/libg++/iostream/stdstrbufs.C @@ -0,0 +1,113 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1992 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#include "ioprivate.h" +#include + +// This file defines the standard streambufs, corresponding to cin, cout, cerr. +// We define two sets: +// +// __std_filebuf_0, __std_filebuf_1, __std_filebuf_2 are filebufs using +// file descriptor 0/1/2. +// +// __stdin_stdiobuf, __stdout_stdiobuf, __stderr_stdiobuf are stdiostreams +// pointing to stdin, stdout, stderr. + + +// To avoid problems depending on constructor order (and for +// efficiency) the standard streambufs (and streams) are +// constructed statically using C-style '{ ... }' initializers. +// Since you're not allowed to do this for structs that +// have virtuals, we define fake streambuf and stream classes +// that don't have any C++-isms, and initialize those. +// To initialize the vtable field of the standard filebufs, +// we use the expression 'vt_filebuf' which must evaluate to +// (the address of) the virtual function table for the +// filebuf class. + +#if _G_NAMES_HAVE_UNDERSCORE +#define UNDERSCORE "_" +#else +#define UNDERSCORE "" +#endif + +// First define the filebuf-based objects. + +#if !defined(vt_filebuf) +#ifndef __GNUG__ +// This works for cfront. +#define vt_filebuf __vtbl__7filebuf +extern char vt_filebuf[1]; +#elif _G_DOLLAR_IN_LABEL +extern char vt_filebuf[1] asm(UNDERSCORE "_vt$filebuf"); +#else +extern char vt_filebuf[1] asm(UNDERSCORE "_vt.filebuf"); +#endif +#endif /* !defined(vt_filebuf) */ + +struct _fake_filebuf { + struct __streambuf s; + char* vtable; + struct __file_fields f; +}; + +#define FILEBUF_LITERAL(CHAIN, FLAGS) \ + { _IO_MAGIC+_S_LINKED+_S_IS_FILEBUF+_S_IS_BACKUPBUF+FLAGS, \ + 0, 0, 0, 0, 0, 0, 0, 0, CHAIN, 0, 0, 0, 0, 0} + +#define DEF_FILEBUF(NAME, FD, CHAIN, FLAGS) \ + _fake_filebuf NAME = {FILEBUF_LITERAL(CHAIN, FLAGS), vt_filebuf, {FD}}; + +DEF_FILEBUF(__std_filebuf_0, 0, 0, _S_NO_WRITES); +DEF_FILEBUF(__std_filebuf_1, 1, (streambuf*)&__std_filebuf_0, _S_NO_READS); +DEF_FILEBUF(__std_filebuf_2, 2, (streambuf*)&__std_filebuf_1, + _S_NO_READS+_S_UNBUFFERED); + +// Nest define the stdiobuf-bases objects. + +#if !defined(vt_stdiobuf) +#ifndef __GNUG__ +// This works for cfront. +#define vt_stdiobuf __vtbl__8stdiobuf +extern char vt_stdiobuf[1]; +#elif _G_DOLLAR_IN_LABEL +extern char vt_stdiobuf[1] asm(UNDERSCORE "_vt$stdiobuf"); +#else +extern char vt_stdiobuf[1] asm(UNDERSCORE "_vt.stdiobuf"); +#endif +#endif /* !defined(vt_stdiobuf) */ + +struct _fake_stdiobuf { + struct __streambuf s; + char* vtable; + struct __file_fields f; + FILE *_f; +}; + +#define DEF_STDIOBUF(NAME, FILE, FD, CHAIN, FLAGS) \ + _fake_stdiobuf NAME[1] = {{ \ + FILEBUF_LITERAL(CHAIN, (FLAGS)|_S_UNBUFFERED),\ + vt_stdiobuf, {FD}, FILE}}; + +DEF_STDIOBUF(__stdin_stdiobuf, stdin, 0, (streambuf*)&__std_filebuf_2, + _S_NO_WRITES); +DEF_STDIOBUF(__stdout_stdiobuf, stdout, 1, (streambuf*)__stdin_stdiobuf, + _S_NO_READS); +DEF_STDIOBUF(__stderr_stdiobuf, stderr, 2, (streambuf*)__stdout_stdiobuf, + _S_NO_READS); + +streambuf* streambuf::_list_all = (streambuf*)__stderr_stdiobuf; diff --git a/gnu/lib/libg++/iostream/stdstreams.C b/gnu/lib/libg++/iostream/stdstreams.C new file mode 100644 index 0000000000..991371a107 --- /dev/null +++ b/gnu/lib/libg++/iostream/stdstreams.C @@ -0,0 +1,145 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1992 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#include "ioprivate.h" + +// The ANSI draft requires that operations on cin/cout/cerr can be +// mixed with operations on stdin/stdout/stderr on a character by +// character basis. This normally requires that the streambuf's +// used by cin/cout/cerr be stdiostreams. However, if the stdio +// implementation is the one that is built using this library, +// then we don't need to, since in that case stdin/stdout/stderr +// are identical to &__std_filebuf_0/&__std_filebuf_1/&__std_filebuf_2. + +#ifdef _STDIO_USES_IOSTREAM +#define USE_FILEBUF +#endif + +#ifdef NAMES_HAVE_UNDERSCORE +#define UNDERSCORE "_" +#else +#define UNDERSCORE "" +#endif + +#ifdef USE_FILEBUF +#define CIN_SBUF __std_filebuf_0 +#define COUT_SBUF __std_filebuf_1 +#define CERR_SBUF __std_filebuf_2 +static int use_stdiobuf = 0; +#else +#define CIN_SBUF __stdin_stdiobuf +#define COUT_SBUF __stdout_stdiobuf +#define CERR_SBUF __stderr_stdiobuf +static int use_stdiobuf = 1; +#endif + +struct _fake_filebuf; +extern _fake_filebuf __std_filebuf_0, __std_filebuf_1, __std_filebuf_2; +struct _fake_stdiobuf; +extern _fake_stdiobuf __stdin_stdiobuf, __stdout_stdiobuf, __stderr_stdiobuf; + +#define cin CIN +#define cout COUT +#define cerr CERR +#define clog CLOG +#include "iostream.h" +#undef cin +#undef cout +#undef cerr +#undef clog + +#ifdef __GNUC__ +#define PAD 0 /* g++ allows 0-length arrays. */ +#else +#define PAD 1 +#endif +struct _fake_istream { + struct myfields { +#ifdef __GNUC__ + _ios_fields *vb; /* pointer to virtual base class ios */ + _G_ssize_t _gcount; +#else + /* This is supposedly correct for cfront. */ + _G_ssize_t _gcount; + void *vptr; + _ios_fields *vb; /* pointer to virtual base class ios */ +#endif + } mine; + _ios_fields base; + char filler[sizeof(struct istream)-sizeof(struct _ios_fields)+PAD]; +}; +struct _fake_ostream { + struct myfields { +#ifndef __GNUC__ + void *vptr; +#endif + _ios_fields *vb; /* pointer to virtual base class ios */ + } mine; + _ios_fields base; + char filler[sizeof(struct ostream)-sizeof(struct _ios_fields)+PAD]; +}; + +#define STD_STR(SBUF, TIE, EXTRA_FLAGS) \ + (streambuf*)&SBUF, TIE, 0, ios::dont_close|ios::skipws|EXTRA_FLAGS, ' ',0,0,6 + +#ifdef __GNUC__ +#define OSTREAM_DEF(TYPE, NAME, SBUF, TIE, EXTRA_FLAGS) \ + TYPE NAME = { {&NAME.base}, {STD_STR(SBUF, TIE, EXTRA_FLAGS) }}; +#define ISTREAM_DEF(TYPE, NAME, SBUF, TIE, EXTRA_FLAGS) \ + TYPE NAME = { {&NAME.base}, {STD_STR(SBUF, TIE, EXTRA_FLAGS) }}; +#else +#define OSTREAM_DEF(TYPE, NAME, SBUF, TIE, EXTRA_FLAGS) \ + TYPE NAME = { {0, &NAME.base}, {STD_STR(SBUF, TIE, EXTRA_FLAGS) }}; +#define ISTREAM_DEF(TYPE, NAME, SBUF, TIE, EXTRA_FLAGS) \ + TYPE NAME = { {0, 0, &NAME.base}, {STD_STR(SBUF, TIE, EXTRA_FLAGS) }}; +#endif + +OSTREAM_DEF(_fake_ostream, cout, COUT_SBUF, NULL, 0) +OSTREAM_DEF(_fake_ostream, cerr, CERR_SBUF, (ostream*)&cout, ios::unitbuf) +ISTREAM_DEF(_fake_istream, cin, CIN_SBUF, (ostream*)&cout, 0) + +/* Only for (partial) compatibility with AT&T's library. */ +OSTREAM_DEF(_fake_ostream, clog, CERR_SBUF, (ostream*)&cout, 0) + +// Switches between using __std_filebuf_{0,1,2} and +// __std{in,out,err}_stdiobuf for standard streams. This is +// normally not needed, but is provided for AT&T compatibility. + +int ios::sync_with_stdio(int new_state) +{ +#ifdef _STDIO_USES_IOSTREAM + // It is always synced. + return 0; +#else + if (new_state == use_stdiobuf) // The usual case now. + return use_stdiobuf; + if (new_state) { + cout.base._strbuf = (streambuf*)&__stdout_stdiobuf; + cin.base._strbuf = (streambuf*)&__stdin_stdiobuf; + cerr.base._strbuf = (streambuf*)&__stderr_stdiobuf; + clog.base._strbuf = (streambuf*)&__stderr_stdiobuf; + } else { + cout.base._strbuf = (streambuf*)&__std_filebuf_1; + cin.base._strbuf = (streambuf*)&__std_filebuf_0; + cerr.base._strbuf = (streambuf*)&__std_filebuf_2; + clog.base._strbuf = (streambuf*)&__std_filebuf_2; + } + int old_state = use_stdiobuf; + use_stdiobuf = new_state; + return old_state; +#endif +} diff --git a/gnu/lib/libg++/iostream/stream.C b/gnu/lib/libg++/iostream/stream.C new file mode 100644 index 0000000000..5ee6bbeb5a --- /dev/null +++ b/gnu/lib/libg++/iostream/stream.C @@ -0,0 +1,120 @@ +#include +#include "ioprivate.h" +#include "stream.h" +#include "strstream.h" + +static char Buffer[_G_BUFSIZ]; +#define EndBuffer (Buffer+_G_BUFSIZ) +static char* next_chunk = Buffer; // Start of available part of Buffer. + +char* form(const char* format, ...) +{ + int space_left = EndBuffer - next_chunk; + // If less that 25% of the space is available start over. + if (space_left < (_G_BUFSIZ>>2)) + next_chunk = Buffer; + char* buf = next_chunk; + + strstreambuf stream(buf, EndBuffer-buf-1, buf); + va_list ap; + va_start(ap, format); + int count = stream.vform(format, ap); + va_end(ap); + stream.sputc(0); + next_chunk = buf + stream.pcount(); + return buf; +} + +#define u_long unsigned long + +static char* itoa(unsigned long i, int size, int neg, int base) +{ + // Conservative estimate: If base==2, might need 8 characters + // for each input byte, but normally 3 is plenty. + int needed = size ? size + : (base >= 8 ? 3 : 8) * sizeof(unsigned long) + 2; + int space_left = EndBuffer - next_chunk; + if (space_left <= needed) + next_chunk = Buffer; // start over. + + char* buf = next_chunk; + + register char* ptr = buf+needed+1; + next_chunk = ptr; + + if (needed < (2+neg) || ptr > EndBuffer) + return NULL; + *--ptr = 0; + + if (i == 0) + *--ptr = '0'; + while (i != 0 && ptr > buf) { + int ch = i % base; + i = i / base; + if (ch >= 10) + ch += 'a' - 10; + else + ch += '0'; + *--ptr = ch; + } + if (neg) + *--ptr = '-'; + if (size == 0) + return ptr; + while (ptr > buf) + *--ptr = ' '; + return buf; +} + +char* dec(long i, int len /* = 0 */) +{ + if (i >= 0) return itoa((unsigned long)i, len, 0, 10); + else return itoa((unsigned long)(-i), len, 1, 10); +} +char* dec(int i, int len /* = 0 */) +{ + if (i >= 0) return itoa((unsigned long)i, len, 0, 10); + else return itoa((unsigned long)(-i), len, 1, 10); +} +char* dec(unsigned long i, int len /* = 0 */) +{ + return itoa(i, len, 0, 10); +} +char* dec(unsigned int i, int len /* = 0 */) +{ + return itoa(i, len, 0, 10); +} + +char* hex(long i, int len /* = 0 */) +{ + return itoa((unsigned long)i, len, 0, 16); +} +char* hex(int i, int len /* = 0 */) +{ + return itoa((unsigned long)i, len, 0, 16); +} +char* hex(unsigned long i, int len /* = 0 */) +{ + return itoa(i, len, 0, 16); +} +char* hex(unsigned int i, int len /* = 0 */) +{ + return itoa(i, len, 0, 16); +} + +char* oct(long i, int len /* = 0 */) +{ + return itoa((unsigned long)i, len, 0, 8); +} +char* oct(int i, int len /* = 0 */) +{ + return itoa((unsigned long)i, len, 0, 8); +} +char* oct(unsigned long i, int len /* = 0 */) +{ + return itoa(i, len, 0, 8); +} +char* oct(unsigned int i, int len /* = 0 */) +{ + return itoa(i, len, 0, 8); +} diff --git a/gnu/lib/libg++/iostream/stream.h b/gnu/lib/libg++/iostream/stream.h new file mode 100644 index 0000000000..dfc2e21225 --- /dev/null +++ b/gnu/lib/libg++/iostream/stream.h @@ -0,0 +1,31 @@ +#ifndef _COMPAT_STREAM_H +#define _COMPAT_STREAM_H + +// Compatibility with old library. + +#define _STREAM_COMPAT +#include + +extern char* form(const char*, ...); + +extern char* dec(long, int=0); +extern char* dec(int, int=0); +extern char* dec(unsigned long, int=0); +extern char* dec(unsigned int, int=0); + +extern char* hex(long, int=0); +extern char* hex(int, int=0); +extern char* hex(unsigned long, int=0); +extern char* hex(unsigned int, int=0); + +extern char* oct(long, int=0); +extern char* oct(int, int=0); +extern char* oct(unsigned long, int=0); +extern char* oct(unsigned int, int=0); + +char* chr(char ch, int width = 0); +char* str(const char* s, int width = 0); + +inline istream& WS(istream& str) { return ws(str); } + +#endif /* !_COMPAT_STREAM_H */ diff --git a/gnu/lib/libg++/iostream/streambuf.C b/gnu/lib/libg++/iostream/streambuf.C new file mode 100644 index 0000000000..0038f39190 --- /dev/null +++ b/gnu/lib/libg++/iostream/streambuf.C @@ -0,0 +1,666 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1991, 1992 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#define _STREAM_COMPAT +#ifdef __GNUG__ +#pragma implementation +#endif +#include "ioprivate.h" +#include + +void streambuf::_un_link() +{ + if (_flags & _S_LINKED) { + streambuf **f; + for (f = &_list_all; *f != NULL; f = &(*f)->xchain()) { + if (*f == this) { + *f = xchain(); + break; + } + } + _flags &= ~_S_LINKED; + } +} + +void streambuf::_link_in() +{ + if ((_flags & _S_LINKED) == 0) { + _flags |= _S_LINKED; + xchain() = _list_all; + _list_all = this; + } +} + +// Return minimum _pos markers +// Assumes the current get area is the main get area. +int streambuf::_least_marker() +{ + int least_so_far = _egptr - _eback; + for (register streammarker *mark = _markers; + mark != NULL; mark = mark->_next) + if (mark->_pos < least_so_far) + least_so_far = mark->_pos; + return least_so_far; +} + +// Switch current get area from backup buffer to (start of) main get area. + +void streambuf::switch_to_main_get_area() +{ + char *tmp; + _flags &= ~_S_IN_BACKUP; + // Swap _egptr and _other_egptr. + tmp= _egptr; _egptr= _other_egptr; _other_egptr= tmp; + // Swap _eback and _other_gbase. + tmp= _eback; _eback = _other_gbase; _other_gbase = tmp; + _gptr = _eback; +} + +// Switch current get area from main get area to (end of) backup area. + +void streambuf::switch_to_backup_area() +{ + char *tmp; + _flags |= _S_IN_BACKUP; + // Swap _egptr and _other_egptr. + tmp = _egptr; _egptr = _other_egptr; _other_egptr = tmp; + // Swap _gbase and _other_gbase. + tmp = _eback; _eback = _other_gbase; _other_gbase = tmp; + _gptr = _egptr; +} + +int streambuf::switch_to_get_mode() +{ + if (_pptr > _pbase) + if (overflow(EOF) == EOF) + return EOF; + if (in_backup()) { + _eback = _aux_limit; + } + else { + _eback = _base; + if (_pptr > _egptr) + _egptr = _pptr; + } + _gptr = _pptr; + + setp(_gptr, _gptr); + + _flags &= ~_S_CURRENTLY_PUTTING; + return 0; +} + +void streambuf::free_backup_area() +{ + if (in_backup()) + switch_to_main_get_area(); // Just in case. + delete [] _other_gbase; + _other_gbase = NULL; + _other_egptr = NULL; + _aux_limit = NULL; +} + +#if 0 +int streambuf::switch_to_put_mode() +{ + _pbase = _gptr; + _pptr = _gptr; + _epptr = in_backup() ? _egptr : _ebuf; // wrong if line- or un-buffered? + + _gptr = _egptr; + _eback = _egptr; + + _flags |= _S_CURRENTLY_PUTTING; + return 0; +} +#endif + +#ifdef _G_FRIEND_BUG +int __underflow(register streambuf *sb) { return __UNDERFLOW(sb); } +int __UNDERFLOW(register streambuf *sb) +#else +int __underflow(register streambuf *sb) +#endif +{ + if (sb->put_mode()) + if (sb->switch_to_get_mode() == EOF) return EOF; + if (sb->_gptr < sb->_egptr) + return *(unsigned char*)sb->_gptr; + if (sb->in_backup()) { + sb->switch_to_main_get_area(); + if (sb->_gptr < sb->_egptr) + return *sb->_gptr; + } + if (sb->have_markers()) { + // Append [_gbase.._egptr] to backup area. + int least_mark = sb->_least_marker(); + // needed_size is how much space we need in the backup area. + int needed_size = (sb->_egptr - sb->_eback) - least_mark; + int current_Bsize = sb->_other_egptr - sb->_other_gbase; + int avail; // Extra space available for future expansion. + if (needed_size > current_Bsize) { + avail = 0; // 100 ?? FIXME + char *new_buffer = new char[avail+needed_size]; + if (least_mark < 0) { + memcpy(new_buffer + avail, + sb->_other_egptr + least_mark, + -least_mark); + memcpy(new_buffer +avail - least_mark, + sb->_eback, + sb->_egptr - sb->_eback); + } + else + memcpy(new_buffer + avail, + sb->_eback + least_mark, + needed_size); + delete [] sb->_other_gbase; + sb->_other_gbase = new_buffer; + sb->_other_egptr = new_buffer + avail + needed_size; + } + else { + avail = current_Bsize - needed_size; + if (least_mark < 0) { + memmove(sb->_other_gbase + avail, + sb->_other_egptr + least_mark, + -least_mark); + memcpy(sb->_other_gbase + avail - least_mark, + sb->_eback, + sb->_egptr - sb->_eback); + } + else if (needed_size > 0) + memcpy(sb->_other_gbase + avail, + sb->_eback + least_mark, + needed_size); + } + // FIXME: Dubious arithmetic if pointers are NULL + sb->_aux_limit = sb->_other_gbase + avail; + // Adjust all the streammarkers. + int delta = sb->_egptr - sb->_eback; + for (register streammarker *mark = sb->_markers; + mark != NULL; mark = mark->_next) + mark->_pos -= delta; + } + else if (sb->have_backup()) + sb->free_backup_area(); + return sb->underflow(); +} + +#ifdef _G_FRIEND_BUG +int __overflow(register streambuf *sb, int c) { return __OVERFLOW(sb, c); } +int __OVERFLOW(register streambuf *sb, int c) +#else +int __overflow(streambuf* sb, int c) +#endif +{ + return sb->overflow(c); +} + +int streambuf::xsputn(register const char* s, int n) +{ + if (n <= 0) + return 0; + register int more = n; + for (;;) { + int count = _epptr - _pptr; // Space available. + if (count > 0) { + if (count > more) + count = more; + if (count > 20) { + memcpy(_pptr, s, count); + s += count; + _pptr += count; + } + else if (count <= 0) + count = 0; + else { + register char *p = _pptr; + for (register int i = count; --i >= 0; ) *p++ = *s++; + _pptr = p; + } + more -= count; + } + if (more == 0 || __overflow(this, (unsigned char)*s++) == EOF) + break; + more--; + } + return n - more; +} + +int streambuf::padn(char pad, int count) +{ +#define PADSIZE 16 + static char const blanks[PADSIZE] = + {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; + static char const zeroes[PADSIZE] = + {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; + char padbuf[PADSIZE]; + const char *padptr; + register int i; + + if (pad == ' ') + padptr = blanks; + else if (pad == '0') + padptr = zeroes; + else { + for (i = PADSIZE; --i >= 0; ) padbuf[i] = pad; + padptr = padbuf; + } + for (i = count; i >= PADSIZE; i -= PADSIZE) + if (sputn(padptr, PADSIZE) != PADSIZE) + return EOF; + if (i > 0 && sputn(padptr, i) != i) + return EOF; + return pad; +} + +int streambuf::xsgetn(char* s, int n) +{ + register int more = n; + for (;;) { + int count = _egptr - _gptr; // Data available. + if (count > 0) { + if (count > more) + count = more; + if (count > 20) { + memcpy(s, _gptr, count); + s += count; + _gptr += count; + } + else if (count <= 0) + count = 0; + else { + register char *p = _gptr; + for (register int i = count; --i >= 0; ) *s++ = *p++; + _gptr = p; + } + more -= count; + } + if (more == 0 || __underflow(this) == EOF) + break; + } + return n - more; +} + +int streambuf::ignore(int n) +{ + register int more = n; + for (;;) { + int count = _egptr - _gptr; // Data available. + if (count > 0) { + if (count > more) + count = more; + _gptr += count; + more -= count; + } + if (more == 0 || __underflow(this) == EOF) + break; + } + return n - more; +} + +int streambuf::sync() +{ + if (gptr() == egptr() && pptr() == pbase()) + return 0; + return EOF; +} + +int streambuf::pbackfail(int c) +{ + if (_gptr > _eback) + _gptr--; + else if (seekoff(-1, ios::cur, ios::in) == EOF) + return EOF; + if (c != EOF && *_gptr != c) + *_gptr = c; + return (unsigned char)c; +} + +streambuf* streambuf::setbuf(char* p, int len) +{ + if (sync() == EOF) + return NULL; + if (p == NULL || len == 0) { + unbuffered(1); + setb(_shortbuf, _shortbuf+1, 0); + } + else { + unbuffered(0); + setb(p, p+len, 0); + } + setp(0, 0); + setg(0, 0, 0); + return this; +} + +streampos streambuf::seekpos(streampos pos, int mode) +{ + return seekoff(pos, ios::beg, mode); +} + +void streambuf::setb(char* b, char* eb, int a) +{ + if (_base && !(_flags & _S_USER_BUF)) + FREE_BUF(_base); + _base = b; + _ebuf = eb; + if (a) + _flags &= ~_S_USER_BUF; + else + _flags |= _S_USER_BUF; +} + +int streambuf::doallocate() +{ + char *buf = ALLOC_BUF(_G_BUFSIZ); + if (buf == NULL) + return EOF; + setb(buf, buf+_G_BUFSIZ, 1); + return 1; +} + +void streambuf::doallocbuf() +{ + if (base() || (!unbuffered() && doallocate() != EOF)) return; + setb(_shortbuf, _shortbuf+1, 0); +} + +streambuf::streambuf(int flags) +{ + _flags = _IO_MAGIC|flags; + _base = NULL; + _ebuf = NULL; + _eback = NULL; + _gptr = NULL; + _egptr = NULL; + _pbase = NULL; + _pptr = NULL; + _epptr = NULL; + _chain = NULL; // Not necessary. + + _other_gbase = NULL; + _aux_limit = NULL; + _other_egptr = NULL; + _markers = NULL; + _cur_column = 0; +} + +streambuf::~streambuf() +{ + if (_base && !(_flags & _S_USER_BUF)) + FREE_BUF(_base); + + for (register streammarker *mark = _markers; + mark != NULL; mark = mark->_next) + mark->_sbuf = NULL; + +} + +streampos +streambuf::seekoff(streamoff, _seek_dir, int mode /*=ios::in|ios::out*/) +{ + return EOF; +} + +int streambuf::sputbackc(char c) +{ + if (_gptr > _eback && (unsigned char)_gptr[-1] == (unsigned char)c) { + _gptr--; + return (unsigned char)c; + } + return pbackfail(c); +} + +int streambuf::sungetc() +{ + if (_gptr > _eback) { + _gptr--; + return (unsigned char)*_gptr; + } + else + return pbackfail(EOF); +} + +#if 0 /* Work in progress */ +void streambuf::collumn(int c) +{ + if (c == -1) + _collumn = -1; + else + _collumn = c - (_pptr - _pbase); +} +#endif + + +int streambuf::get_column() +{ + if (_cur_column) + return __adjust_column(_cur_column - 1, pbase(), pptr() - pbase()); + return -1; +} + +int streambuf::set_column(int i) +{ + _cur_column = i+1; + return 0; +} + +int streambuf::flush_all() +{ + int result = 0; + for (streambuf *sb = _list_all; sb != NULL; sb = sb->xchain()) + if (sb->overflow(EOF) == EOF) + result = EOF; + return result; +} + +void streambuf::flush_all_linebuffered() +{ + for (streambuf *sb = _list_all; sb != NULL; sb = sb->xchain()) + if (sb->linebuffered()) + sb->overflow(EOF); +} + +int backupbuf::underflow() +{ + return EOF; +} + +int backupbuf::overflow(int c) +{ + return EOF; +} + +streammarker::streammarker(streambuf *sb) +{ + _sbuf = sb; + if (!(sb->xflags() & _S_IS_BACKUPBUF)) { + set_streampos(sb->seekoff(0, ios::cur, ios::in)); + _next = 0; + } + else { + if (sb->put_mode()) + sb->switch_to_get_mode(); + if (((backupbuf*)sb)->in_backup()) + set_offset(sb->_gptr - sb->_egptr); + else + set_offset(sb->_gptr - sb->_eback); + + // Should perhaps sort the chain? + _next = ((backupbuf*)sb)->_markers; + ((backupbuf*)sb)->_markers = this; + } +} + +streammarker::~streammarker() +{ + if (saving()) { + // Unlink from sb's chain. + register streammarker **ptr = &((backupbuf*)_sbuf)->_markers; + for (; ; ptr = &(*ptr)->_next) + if (*ptr == NULL) + break; + else if (*ptr == this) { + *ptr = _next; + return; + } + } +#if 0 + if _sbuf has a backup area that is no longer needed, should we delete + it now, or wait until underflow()? +#endif +} + +#define BAD_DELTA EOF + +int streammarker::delta(streammarker& other_mark) +{ + if (_sbuf != other_mark._sbuf) + return BAD_DELTA; + if (saving() && other_mark.saving()) + return _pos - other_mark._pos; + else if (!saving() && !other_mark.saving()) + return _spos - other_mark._spos; + else + return BAD_DELTA; +} + +int streammarker::delta() +{ + if (_sbuf == NULL) + return BAD_DELTA; + if (saving()) { + int cur_pos; + if (_sbuf->in_backup()) + cur_pos = _sbuf->_gptr - _sbuf->_egptr; + else + cur_pos = _sbuf->_gptr - _sbuf->_eback; + return _pos - cur_pos; + } + else { + if (_spos == EOF) + return BAD_DELTA; + int cur_pos = _sbuf->seekoff(0, ios::cur); + if (cur_pos == EOF) + return BAD_DELTA; + return _pos - cur_pos; + } +} + +int streambuf::seekmark(streammarker& mark, int delta /* = 0 */) +{ + if (mark._sbuf != this) + return EOF; + if (!mark.saving()) { + return seekpos(mark._spos, ios::in); + } + else if (mark._pos >= 0) { + if (in_backup()) + switch_to_main_get_area(); + _gptr = _eback + mark._pos; + } + else { + if (!in_backup()) + switch_to_backup_area(); + _gptr = _egptr + mark._pos; + } + return 0; +} + +void streambuf::unsave_markers() +{ + register streammarker *mark =_markers; + if (_markers) { + streampos offset = seekoff(0, ios::cur, ios::in); + if (offset != EOF) { + offset += eGptr() - Gbase(); + for ( ; mark != NULL; mark = mark->_next) + mark->set_streampos(mark->_pos + offset); + } + else { + for ( ; mark != NULL; mark = mark->_next) + mark->set_streampos(EOF); + } + _markers = 0; + } + + free_backup_area(); +} + +int backupbuf::pbackfail(int c) +{ + if (_gptr <= _eback) { + // Need to handle a filebuf in write mode (switch to read mode). FIXME! + + if (have_backup() && !in_backup()) { + switch_to_backup_area(); + } + if (!have_backup()) { + // No backup buffer: allocate one. + // Use short buffer, if unused? (probably not) FIXME + int backup_size = 128; + _other_gbase = new char [backup_size]; + _other_egptr = _other_gbase + backup_size; + _aux_limit = _other_egptr; + switch_to_backup_area(); + } + else if (gptr() <= eback()) { + // Increase size of existing backup buffer. + size_t new_size; + size_t old_size = egptr() - eback(); + new_size = 2 * old_size; + char* new_buf = new char [new_size]; + memcpy(new_buf+(new_size-old_size), eback(), old_size); + delete [] eback(); + setg(new_buf, new_buf+(new_size-old_size), new_buf+new_size); + _aux_limit = _gptr; + } + } + _gptr--; + if (c != EOF && *_gptr != c) + *_gptr = c; + return (unsigned char)*_gptr; +} + +unsigned __adjust_column(unsigned start, const char *line, int count) +{ + register const char *ptr = line + count; + while (ptr > line) + if (*--ptr == '\n') + return line + count - ptr - 1; + return start + count; +} + +int ios::readable() { return !(rdbuf()->_flags & _S_NO_READS); } +int ios::writable() { return !(rdbuf()->_flags & _S_NO_WRITES); } +int ios::is_open() { return rdbuf() + && (rdbuf()->_flags & _S_NO_READS+_S_NO_WRITES) + != _S_NO_READS+_S_NO_WRITES; } + +#if defined(linux) +#define IO_CLEANUP ; +#endif + +#ifdef IO_CLEANUP + IO_CLEANUP +#else +struct __io_defs { + __io_defs() { } + ~__io_defs() { streambuf::flush_all(); } +}; +__io_defs io_defs__; +#endif diff --git a/gnu/lib/libg++/iostream/streambuf.h b/gnu/lib/libg++/iostream/streambuf.h new file mode 100644 index 0000000000..f808c05ae0 --- /dev/null +++ b/gnu/lib/libg++/iostream/streambuf.h @@ -0,0 +1,494 @@ +// This is part of the iostream library, providing -*- C++ -*- input/output. +// Copyright (C) 1991 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public + + +#ifndef _STREAMBUF_H +#define _STREAMBUF_H +#ifdef __GNUG__ +#pragma interface +#endif + +/* #define _G_IO_THROW */ /* Not implemented: ios::failure */ + +#include <_G_config.h> +#ifdef _G_NEED_STDARG_H +#include +#endif + +#ifndef EOF +#define EOF (-1) +#endif +#ifndef NULL +#ifdef __GNUC__ +#define NULL ((void*)0) +#else +#define NULL (0) +#endif +#endif + +class ostream; class streambuf; class backupbuf; + +// In case some header files defines these as macros. +#undef open +#undef close + +#ifdef _G_FRIEND_BUG +extern int __UNDERFLOW(streambuf*); +extern int __OVERFLOW(streambuf*, int); +#endif +extern "C" int __underflow(streambuf*); +extern "C" int __overflow(streambuf*, int); + +typedef _G_off_t streamoff; +typedef _G_off_t streampos; // Should perhaps be _G_fpos_t ? + +typedef unsigned long __fmtflags; +typedef unsigned char __iostate; + +struct _ios_fields { // The data members of an ios. + streambuf *_strbuf; + ostream* _tie; + int _width; + __fmtflags _flags; + _G_wchar_t _fill; + __iostate _state; + __iostate _exceptions; + int _precision; +}; + +#define _IOS_GOOD 0 +#define _IOS_EOF 1 +#define _IOS_FAIL 2 +#define _IOS_BAD 4 + +#define _IOS_INPUT 1 +#define _IOS_OUTPUT 2 +#define _IOS_ATEND 4 +#define _IOS_APPEND 8 +#define _IOS_TRUNC 16 +#define _IOS_NOCREATE 32 +#define _IOS_NOREPLACE 64 +#define _IOS_BIN 128 + +#ifdef _STREAM_COMPAT +enum state_value { + _good = _IOS_GOOD, + _eof = _IOS_EOF, + _fail = _IOS_FAIL, + _bad = _IOS_BAD }; +enum open_mode { + input = _IOS_INPUT, + output = _IOS_OUTPUT, + atend = _IOS_ATEND, + append = _IOS_APPEND }; +#endif + +class ios : public _ios_fields { + public: + typedef __fmtflags fmtflags; + typedef int iostate; + typedef int openmode; + typedef int streamsize; + enum io_state { + goodbit = _IOS_GOOD, + eofbit = _IOS_EOF, + failbit = _IOS_FAIL, + badbit = _IOS_BAD }; + enum open_mode { + in = _IOS_INPUT, + out = _IOS_OUTPUT, + ate = _IOS_ATEND, + app = _IOS_APPEND, + trunc = _IOS_TRUNC, + nocreate = _IOS_NOCREATE, + noreplace = _IOS_NOREPLACE, + bin = _IOS_BIN }; + enum seek_dir { beg, cur, end}; + // ANSI: typedef enum seek_dir seekdir; etc + enum { skipws=01, left=02, right=04, internal=010, + dec=020, oct=040, hex=0100, + showbase=0200, showpoint=0400, uppercase=01000, showpos=02000, + scientific=04000, fixed=010000, unitbuf=020000, stdio=040000, + dont_close=0100000 //Don't delete streambuf on stream destruction + }; + enum { // Masks. + basefield=dec+oct+hex, + floatfield = scientific+fixed, + adjustfield = left+right+internal + }; + +#ifdef _G_IO_THROW + class failure : public xmsg { + ios* _stream; + public: + failure(ios* stream) { _stream = stream; } + failure(string cause, ios* stream) { _stream = stream; } + ios* rdios() const { return _stream; } + }; +#endif + + ostream* tie() const { return _tie; } + ostream* tie(ostream* val) { ostream* save=_tie; _tie=val; return save; } + + // Methods to change the format state. + _G_wchar_t fill() const { return (_G_wchar_t)_fill; } + _G_wchar_t fill(_G_wchar_t newf) + {_G_wchar_t oldf = (_G_wchar_t)_fill; _fill = (char)newf; return oldf;} + fmtflags flags() const { return _flags; } + fmtflags flags(fmtflags new_val) { + fmtflags old_val = _flags; _flags = new_val; return old_val; } + int precision() const { return _precision; } + int precision(int newp) { + unsigned short oldp = _precision; _precision = (unsigned short)newp; + return oldp; } + fmtflags setf(fmtflags val) { + fmtflags oldbits = _flags; + _flags |= val; return oldbits; } + fmtflags setf(fmtflags val, fmtflags mask) { + fmtflags oldbits = _flags; + _flags = (_flags & ~mask) | (val & mask); return oldbits; } + fmtflags unsetf(fmtflags mask) { + fmtflags oldbits = _flags & mask; + _flags &= ~mask; return oldbits; } + int width() const { return _width; } + int width(int val) { int save = _width; _width = val; return save; } + +#ifdef _G_IO_THROW + void _throw_failure() { throw new ios::failure(this); } +#else + void _throw_failure() { } +#endif + + streambuf* rdbuf() const { return _strbuf; } + void clear(iostate state = 0) { + _state = _strbuf ? state : state|badbit; + if (_state & _exceptions) _throw_failure(); } + void set(iostate flag) { _state |= flag; + if (_state & _exceptions) _throw_failure(); } + void setstate(iostate flag) { _state |= flag; // ANSI + if (_state & _exceptions) _throw_failure(); } + int good() const { return _state == 0; } + int eof() const { return _state & ios::eofbit; } + int fail() const { return _state & (ios::badbit|ios::failbit); } + int bad() const { return _state & ios::badbit; } + iostate rdstate() const { return _state; } + operator void*() const { return fail() ? (void*)0 : (void*)(-1); } + int operator!() const { return fail(); } + iostate exceptions() const { return _exceptions; } + void exceptions(iostate enable) { + _exceptions = enable; + if (_state & _exceptions) _throw_failure(); } + + static int sync_with_stdio(int on); + static void sync_with_stdio() { sync_with_stdio(1); } + +#ifdef _STREAM_COMPAT + void unset(state_value flag) { _state &= ~flag; } + void close(); + int is_open(); + int readable(); + int writable(); +#endif + + // Used to initialize standard streams. Not needed in this implementation. + class Init { + public: + Init () { } + }; + + protected: + ios(streambuf* sb = 0, ostream* tie = 0); + virtual ~ios(); + void init(streambuf* sb) { _state=0; _strbuf=sb; } +}; + +#if __GNUG__==1 +typedef int _seek_dir; +#else +typedef ios::seek_dir _seek_dir; +#endif + +// Magic numbers and bits for the _flags field. +// The magic numbers use the high-order bits of _flags; +// the remaining bits are abailable for variable flags. +// Note: The magic numbers must all be negative if stdio +// emulation is desired. + +#define _IO_MAGIC 0xFBAD0000 /* Magic number */ +#define _OLD_STDIO_MAGIC 0xFABC0000 /* Emulate old stdio. */ +#define _IO_MAGIC_MASK 0xFFFF0000 +#define _S_USER_BUF 1 /* User owns buffer; don't delete it on close. */ +#define _S_UNBUFFERED 2 +#define _S_NO_READS 4 /* Reading not allowed */ +#define _S_NO_WRITES 8 /* Writing not allowd */ +#define _S_EOF_SEEN 0x10 +#define _S_ERR_SEEN 0x20 +#define _S_DELETE_DONT_CLOSE 0x40 +#define _S_LINKED 0x80 // Set if linked (using _chain) to streambuf::_list_all. +#define _S_IN_BACKUP 0x100 +#define _S_LINE_BUF 0x200 +#define _S_TIED_PUT_GET 0x400 // Set if put and get pointer logicly tied. +#define _S_CURRENTLY_PUTTING 0x800 +#define _S_IS_APPENDING 0x1000 +#define _S_IS_BACKUPBUF 0x4000 +#define _S_IS_FILEBUF 0x8000 + +// A streammarker remembers a position in a buffer. +// You are guaranteed to be able to seek back to it if it is saving(). +class streammarker { + friend class streambuf; +#ifdef _G_FRIEND_BUG + friend int __UNDERFLOW(streambuf*); +#else + friend int __underflow(streambuf*); +#endif + struct streammarker *_next; // Only if saving() + streambuf *_sbuf; // Only valid if saving(). + streampos _spos; // -2: means that _pos is valid. + void set_streampos(streampos sp) { _spos = sp; } + void set_offset(int offset) { _pos = offset; _spos = (streampos)(-2); } + // If _pos >= 0, it points to _buf->Gbase()+_pos. + // if _pos < 0, it points to _buf->eBptr()+_pos. + int _pos; + public: + streammarker(streambuf *sb); + ~streammarker(); + int saving() { return _spos == -2; } + int delta(streammarker&); + int delta(); +}; + +struct __streambuf { + // NOTE: If this is changed, also change __FILE in stdio/stdio.h! + int _flags; /* High-order word is _IO_MAGIC; rest is flags. */ + char* _gptr; /* Current get pointer */ + char* _egptr; /* End of get area. */ + char* _eback; /* Start of putback+get area. */ + char* _pbase; /* Start of put area. */ + char* _pptr; /* Current put pointer. */ + char* _epptr; /* End of put area. */ + char* _base; /* Start of reserve area. */ + char* _ebuf; /* End of reserve area. */ + struct streambuf *_chain; + + // The following fields are used to support backing up and undo. + friend class streammarker; + char *_other_gbase; // Pointer to start of non-current get area. + char *_aux_limit; // Pointer to first valid character of backup area, + char *_other_egptr; // Pointer to end of non-current get area. + streammarker *_markers; + +#define __HAVE_COLUMN /* temporary */ + // 1+column number of pbase(); 0 is unknown. + unsigned short _cur_column; + char _unused; + char _shortbuf[1]; +}; + +extern unsigned __adjust_column(unsigned start, const char *line, int count); + +struct streambuf : public __streambuf { + friend class ios; + friend class istream; + friend class ostream; + friend class streammarker; +#ifdef _G_FRIEND_BUG + friend int __UNDERFLOW(streambuf*); +#else + friend int __underflow(streambuf*); +#endif + protected: + static streambuf* _list_all; /* List of open streambufs. */ + streambuf*& xchain() { return _chain; } + void _un_link(); + void _link_in(); + char* gptr() const { return _gptr; } + char* pptr() const { return _pptr; } + char* egptr() const { return _egptr; } + char* epptr() const { return _epptr; } + char* pbase() const { return _pbase; } + char* eback() const { return _eback; } + char* base() const { return _base; } + char* ebuf() const { return _ebuf; } + int blen() const { return _ebuf - _base; } + void xput_char(char c) { *_pptr++ = c; } + int xflags() { return _flags; } + int xflags(int f) { int fl = _flags; _flags = f; return fl; } + void xsetflags(int f) { _flags |= f; } + void xsetflags(int f, int mask) { _flags = (_flags & ~mask) | (f & mask); } + void gbump(int n) { _gptr += n; } + void pbump(int n) { _pptr += n; } + void setb(char* b, char* eb, int a=0); + void setp(char* p, char* ep) { _pbase=_pptr=p; _epptr=ep; } + void setg(char* eb, char* g, char *eg) { _eback=eb; _gptr=g; _egptr=eg; } + char *shortbuf() { return _shortbuf; } + + int in_backup() { return _flags & _S_IN_BACKUP; } + // The start of the main get area: FIXME: wrong for write-mode filebuf? + char *Gbase() { return in_backup() ? _other_gbase : _eback; } + // The end of the main get area: + char *eGptr() { return in_backup() ? _other_egptr : _egptr; } + // The start of the backup area: + char *Bbase() { return in_backup() ? _eback : _other_gbase; } + char *Bptr() { return _aux_limit; } + // The end of the backup area: + char *eBptr() { return in_backup() ? _egptr : _other_egptr; } + char *Nbase() { return _other_gbase; } + char *eNptr() { return _other_egptr; } + int have_backup() { return _other_gbase != NULL; } + int have_markers() { return _markers != NULL; } + int _least_marker(); + void switch_to_main_get_area(); + void switch_to_backup_area(); + void free_backup_area(); + void unsave_markers(); // Make all streammarkers !saving(). + int put_mode() { return _flags & _S_CURRENTLY_PUTTING; } + int switch_to_get_mode(); + + streambuf(int flags=0); + public: + static int flush_all(); + static void flush_all_linebuffered(); // Flush all line buffered files. + virtual int underflow() = 0; // Leave public for now + virtual int overflow(int c = EOF) = 0; // Leave public for now + virtual int doallocate(); + virtual streampos seekoff(streamoff, _seek_dir, int mode=ios::in|ios::out); + virtual streampos seekpos(streampos pos, int mode = ios::in|ios::out); + int seekmark(streammarker& mark, int delta = 0); + int sputbackc(char c); + int sungetc(); + virtual ~streambuf(); + int unbuffered() { return _flags & _S_UNBUFFERED ? 1 : 0; } + int linebuffered() { return _flags & _S_LINE_BUF ? 1 : 0; } + void unbuffered(int i) + { if (i) _flags |= _S_UNBUFFERED; else _flags &= ~_S_UNBUFFERED; } + void linebuffered(int i) + { if (i) _flags |= _S_LINE_BUF; else _flags &= ~_S_LINE_BUF; } + int allocate() { // For AT&T compatibility + if (base() || unbuffered()) return 0; + else return doallocate(); } + // Allocate a buffer if needed; use _shortbuf if appropriate. + void allocbuf() { if (base() == NULL) doallocbuf(); } + void doallocbuf(); + virtual int sync(); + virtual int pbackfail(int c); + virtual streambuf* setbuf(char* p, int len); + int in_avail() { return _egptr - _gptr; } + int out_waiting() { return _pptr - _pbase; } + virtual int xsputn(const char* s, int n); + int sputn(const char* s, int n) { return xsputn(s, n); } + int padn(char pad, int n); // Emit 'n' copies of 'pad'. + virtual int xsgetn(char* s, int n); + int sgetn(char* s, int n) { return xsgetn(s, n); } + int ignore(int); + virtual int get_column(); + virtual int set_column(int); + long sgetline(char* buf, _G_size_t n, char delim, int putback_delim); + int sbumpc() { + if (_gptr >= _egptr && __underflow(this) == EOF) return EOF; + else return *(unsigned char*)_gptr++; } + int sgetc() { + if (_gptr >= _egptr && __underflow(this) == EOF) return EOF; + else return *(unsigned char*)_gptr; } + int snextc() { + if (_gptr >= _egptr && __underflow(this) == EOF) return EOF; + else return _gptr++, sgetc(); } + int sputc(int c) { + if (_pptr >= _epptr) return __overflow(this, (unsigned char)c); + else return *_pptr++ = c, (unsigned char)c; } + void stossc() { if (_gptr < _egptr) _gptr++; } + int vscan(char const *fmt0, _G_va_list ap, ios* stream = NULL); + int scan(char const *fmt0 ...); + int vform(char const *fmt0, _G_va_list ap); + int form(char const *fmt0 ...); +#if 0 /* Work in progress */ + int collumn(); // Current collumn number (of put pointer). -1 is unknown. + void collumn(int c); // Set collumn number of put pointer to c. +#endif +}; + +// A backupbuf is a streambuf with full backup and savepoints on reading. +// All standard streambufs in the GNU iostream library are backupbufs. + +// A backupbuf may have two get area: +// - The main get area, and (sometimes) the putback area. +// Whichever one of these contains the gptr is the current get area; +// the other one is the non-current get area. + +class backupbuf : public streambuf { + friend class streammarker; + protected: + backupbuf(int flags=0) : streambuf(flags|_S_IS_BACKUPBUF) { } + public: + virtual int pbackfail(int c); + virtual int underflow(); + virtual int overflow(int c = EOF); +}; + +struct __file_fields { + short _fileno; + int _blksize; + _G_off_t _offset; +// char* _save_gptr; char* _save_egptr; +}; + +class filebuf : public backupbuf { + protected: + struct __file_fields _fb; + void init(); + public: + static const int openprot; // Non-ANSI AT&T-ism: Default open protection. + filebuf(); + filebuf(int fd); + filebuf(int fd, char* p, int len); + ~filebuf(); + filebuf* attach(int fd); + filebuf* open(const char *filename, const char *mode); + filebuf* open(const char *filename, ios::openmode mode, int prot = 0664); + virtual int underflow(); + virtual int overflow(int c = EOF); + int is_open() const { return _fb._fileno >= 0; } + int fd() const { return is_open() ? _fb._fileno : EOF; } + filebuf* close(); + virtual int doallocate(); + virtual streampos seekoff(streamoff, _seek_dir, int mode=ios::in|ios::out); + virtual streambuf* setbuf(char* p, int len); + int xsputn(const char* s, int n); + int xsgetn(char* s, int n); + virtual int sync(); + protected: // See documentation in filebuf.C. +// virtual int pbackfail(int c); + int is_reading() { return eback() != egptr(); } + char* cur_ptr() { return is_reading() ? gptr() : pptr(); } + /* System's idea of pointer */ + char* file_ptr() { return eGptr(); } + int do_write(const char *data, int to_do); + int do_flush() { return do_write(_pbase, _pptr-_pbase); } + // Low-level operations (Usually invoke system calls.) + virtual _G_ssize_t sys_read(char* buf, _G_size_t size); + virtual _G_fpos_t sys_seek(_G_fpos_t, _seek_dir); + virtual _G_ssize_t sys_write(const void*, long); + virtual int sys_stat(void*); // Actually, a (struct stat*) + virtual int sys_close(); +}; + +inline ios::ios(streambuf* sb /* = 0 */, ostream* tie_to /* = 0 */) { + _state = sb ? ios::goodbit : ios::badbit; _exceptions=0; + _strbuf=sb; _tie = tie_to; _width=0; _fill=' '; + _flags=ios::skipws|ios::dec; _precision=6; } +inline ios::~ios() { + if (!(_flags & (unsigned int)ios::dont_close)) delete _strbuf; } + +#endif /* _STREAMBUF_H */ diff --git a/gnu/lib/libg++/iostream/strstream.C b/gnu/lib/libg++/iostream/strstream.C new file mode 100644 index 0000000000..d5a57137d5 --- /dev/null +++ b/gnu/lib/libg++/iostream/strstream.C @@ -0,0 +1,237 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1991 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifdef __GNUG__ +#pragma implementation +#endif +#include "ioprivate.h" +#include + +static void* default_alloc(_G_size_t size) +{ + return (void*)new char[size]; +} + +static void default_free(void* ptr) +{ + delete [] (char*)ptr; +} + +istrstream::istrstream(const char *cp, int n) +{ + init(new strstreambuf(cp, n)); +} + +ostrstream::ostrstream() +{ + init(new strstreambuf()); +} + +strstreambase::strstreambase(char *cp, int n, int mode) +{ + char *pstart; + if (mode == ios::app || mode == ios::ate) + pstart = cp + strlen(cp); + else + pstart = cp; + init(new strstreambuf(cp, n, pstart)); +} + +char *strstreambuf::str() +{ + freeze(1); + return base(); +} + +_G_size_t strstreambuf::pcount() +{ + _G_size_t put_len = pptr() - pbase(); + if (put_len < _len) put_len = _len; + return put_len; +} + +int strstreambuf::overflow(int c /* = EOF */) +{ + const int flush_only = c == EOF; + if (_flags & _S_NO_WRITES) + return flush_only ? 0 : EOF; + size_t pos = pptr() - pbase(); + size_t get_pos = gptr() - pbase(); + if (pos > _len) _len = pos; + if (pos >= blen() + flush_only) { + char *new_buf; + size_t new_size = 2 * blen(); + if (frozen()) /* not allowed to enlarge */ + return EOF; + new_buf = (char*)(*_allocate_buffer)(new_size); + memcpy(new_buf, base(), blen()); + if (new_buf == NULL) { +// __ferror(fp) = 1; + return EOF; + } +#if 0 + if (lenp == &_len) /* use '\0'-filling */ + memset(new_buf + pos, 0, blen() - pos); +#endif + if (_base) { + (*_free_buffer)(_base); + _base = NULL; // So setb() won't try to delete _base. + } + setb(new_buf, new_buf + new_size, 1); + } + + setp(base(), ebuf()); + pbump(pos); + setg(base(), base() + get_pos, base() + _len); + if (!flush_only) { + *pptr() = (unsigned char) c; + pbump(1); + } + return c; +} + +int strstreambuf::underflow() +{ + size_t ppos = pptr() - pbase(); + if (ppos > _len) _len = ppos; + setg(base(), gptr(), base() + _len); + if (gptr() < egptr()) + return *gptr(); + else + return EOF; +} + + +void strstreambuf::init_dynamic(_alloc_type alloc, _free_type free, + int initial_size) + +{ + _len = 0; + if (initial_size < 16) + initial_size = 16; + _allocate_buffer = alloc ? alloc : default_alloc; + _free_buffer = free ? free : default_free; + char * buf = (char*)(*_allocate_buffer)(initial_size); + setb(buf, buf + initial_size, 1); + setp(buf, buf + initial_size); + setg(buf, buf, buf); +} + +void strstreambuf::init_static(char *ptr, int size, char *pstart) +{ + if (size == 0) + size = strlen(ptr); + else if (size < 0) { + // If size is negative 'the characters are assumed to + // continue indefinitely.' This is kind of messy ... +#if 1 + size = 512; + // Try increasing powers of 2, as long as we don't wrap around. + // This can lose in pathological cases (ptr near the end + // of the address space). A better solution might be to + // adjust the size on underflow/overflow. FIXME. + for (int s; s = 2*size, s > 0 && ptr + s > ptr && s < 0x4000000L; ) + size = s; + size = s; +#else + // The following semi-portable kludge assumes that + // sizeof(unsigned long) == sizeof(char*). Hence, + // (unsigned long)(-1) should be the largest possible address. + unsigned long highest = (unsigned long)(-1); + // Pointers are signed on some brain-damaged systems, in + // which case we divide by two to get the maximum signed address. + if ((char*)highest < ptr) + highest >>= 1; + size = (char*)highest - ptr; +#endif + } + setb(ptr, ptr+size); + if (pstart) { + setp(ptr, ebuf()); + pbump(pstart-ptr); + setg(ptr, ptr, pstart); + } + else { + setp(ptr, ptr); + setg(ptr, ptr, ebuf()); + } + _len = egptr() - ptr; +} + +void strstreambuf::init_static (const char *ptr, int size) +{ + init_static((char*)ptr, size, NULL); + xsetflags(_S_NO_WRITES); +} + +strstreambuf::~strstreambuf() +{ + if (_base && !(_flags & _S_USER_BUF)) + (_free_buffer)(_base); + _base = NULL; +} + +streampos strstreambuf::seekoff(streamoff off, _seek_dir dir, + int mode /*=ios::in|ios::out*/) +{ + size_t cur_size = pcount(); + streampos new_pos = EOF; + + // Move the get pointer, if requested. + if (mode & ios::in) { + switch (dir) { + case ios::end: + off += cur_size; + break; + case ios::cur: + off += gptr() - pbase(); + break; + default: /*case ios::beg: */ + break; + } + if (off < 0 || (size_t)off > cur_size) + return EOF; + setg(base(), base() + off, base() + cur_size); + new_pos = off; + } + + // Move the put pointer, if requested. + if (mode & ios::out) { + switch (dir) { + case ios::end: + off += cur_size; + break; + case ios::cur: + off += pptr() - pbase(); + break; + default: /*case ios::beg: */ + break; + } + if (off < 0 || (size_t)off > cur_size) + return EOF; + pbump(base() + off - pptr()); + new_pos = off; + } + return new_pos; +} + +int strstreambuf::pbackfail(int c) +{ + if ((_flags & _S_NO_WRITES) && c != EOF) + return EOF; + return backupbuf::pbackfail(c); +} diff --git a/gnu/lib/libg++/iostream/strstream.h b/gnu/lib/libg++/iostream/strstream.h new file mode 100644 index 0000000000..aa26c498ca --- /dev/null +++ b/gnu/lib/libg++/iostream/strstream.h @@ -0,0 +1,103 @@ +// This is part of the iostream library, providing -*- C++ -*- input/output. +// Copyright (C) 1991 Per Bothner. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free +// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifndef __STRSTREAM_H +#define __STRSTREAM_H +#ifdef __GNUG__ +#pragma interface +#endif +#include + +class strstreambuf : public backupbuf { + _G_size_t _len; // The current length is max(_len, _pptr-_pbase). + typedef void *(*_alloc_type)(_G_size_t); + typedef void (*_free_type)(void*); + _alloc_type _allocate_buffer; + _free_type _free_buffer; + void init_dynamic(_alloc_type alloc, _free_type free, + int initial_size = 128); + void init_static(char *ptr, int size, char *pstart); + void init_static(const char *ptr, int size); + protected: + int is_static() const { return _allocate_buffer == (_alloc_type)0; } + virtual int overflow(int = EOF); + virtual int underflow(); + virtual int pbackfail(int c); + public: + virtual ~strstreambuf(); + strstreambuf() { init_dynamic(0, 0); } + strstreambuf(int initial_size) { init_dynamic(0, 0, initial_size); } + strstreambuf(void *(*alloc)(_G_size_t), void (*free)(void*)) + { init_dynamic(alloc, free); } + strstreambuf(char *ptr, int size, char *pstart = NULL) + { init_static(ptr, size, pstart); } + strstreambuf(unsigned char *ptr, int size, unsigned char *pstart = NULL) + { init_static((char*)ptr, size, (char*)pstart); } + strstreambuf(const char *ptr, int size) + { init_static(ptr, size); } + strstreambuf(const unsigned char *ptr, int size) + { init_static((const char*)ptr, size); } +#ifndef _G_BROKEN_SIGNED_CHAR + strstreambuf(signed char *ptr, int size, signed char *pstart = NULL) + { init_static((char*)ptr, size, (char*)pstart); } + strstreambuf(const signed char *ptr, int size) + { init_static((const char*)ptr, size); } +#endif + // Note: frozen() is always true if is_static(). + int frozen() { return _flags & _S_USER_BUF ? 1 : 0; } + void freeze(int n=1) + { if (!is_static()) + { if (n) _flags |= _S_USER_BUF; else _flags &= ~_S_USER_BUF; } } + _G_size_t pcount(); + char *str(); + virtual streampos seekoff(streamoff, _seek_dir, int mode=ios::in|ios::out); +}; + +class strstreambase : virtual public ios { + public: + strstreambuf* rdbuf() { return (strstreambuf*)_strbuf; } + protected: + strstreambase() { } + strstreambase(char *cp, int n, int mode=ios::out); +}; + +class istrstream : public strstreambase, public istream { + public: + istrstream(const char*, int=0); +}; + +class ostrstream : public strstreambase, public ostream { + public: + ostrstream(); + ostrstream(char *cp, int n, int mode=ios::out) :strstreambase(cp,n,mode){} + _G_size_t pcount() { return ((strstreambuf*)_strbuf)->pcount(); } + char *str() { return ((strstreambuf*)_strbuf)->str(); } + void freeze(int n = 1) { ((strstreambuf*)_strbuf)->freeze(n); } + int frozen() { return ((strstreambuf*)_strbuf)->frozen(); } +}; + +class strstream : public strstreambase, public iostream { + public: + strstream() : strstreambase() { init(new strstreambuf()); } + strstream(char *cp, int n, int mode=ios::out) :strstreambase(cp,n,mode){} + _G_size_t pcount() { return ((strstreambuf*)_strbuf)->pcount(); } + char *str() { return ((strstreambuf*)_strbuf)->str(); } + void freeze(int n = 1) { ((strstreambuf*)_strbuf)->freeze(n); } + int frozen() { return ((strstreambuf*)_strbuf)->frozen(); } +}; + +#endif /*!__STRSTREAM_H*/ diff --git a/gnu/lib/libg++/libg++/ACG.cc b/gnu/lib/libg++/libg++/ACG.cc index 6870a303aa..27e7aa2256 100644 --- a/gnu/lib/libg++/libg++/ACG.cc +++ b/gnu/lib/libg++/libg++/ACG.cc @@ -1,8 +1,25 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1989 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + #ifdef __GNUG__ #pragma implementation #endif -#include "ACG.h" -#include "assert.h" +#include +#include // // This is an extension of the older implementation of Algorithm M @@ -202,7 +219,7 @@ ACG::reset() { register unsigned long u; - if (initialSeed > -1 && initialSeed < SEED_TABLE_SIZE) { + if (initialSeed < SEED_TABLE_SIZE) { u = seedTable[ initialSeed ]; } else { u = initialSeed ^ seedTable[ initialSeed & (SEED_TABLE_SIZE-1) ]; diff --git a/gnu/lib/libg++/libg++/ACG.h b/gnu/lib/libg++/libg++/ACG.h new file mode 100644 index 0000000000..d7101c785a --- /dev/null +++ b/gnu/lib/libg++/libg++/ACG.h @@ -0,0 +1,68 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef _ACG_h +#define _ACG_h 1 + +#include +#include +#ifdef __GNUG__ +#pragma interface +#endif + +// +// Additive number generator. This method is presented in Volume II +// of The Art of Computer Programming by Knuth. I've coded the algorithm +// and have added the extensions by Andres Nowatzyk of CMU to randomize +// the result of algorithm M a bit by using an LCG & a spatial +// permutation table. +// +// The version presented uses the same constants for the LCG that Andres +// uses (chosen by trial & error). The spatial permutation table is +// the same size (it's based on word size). This is for 32-bit words. +// +// The ``auxillary table'' used by the LCG table varies in size, and +// is chosen to be the the smallest power of two which is larger than +// twice the size of the state table. +// + +class ACG : public RNG { + + unsigned long initialSeed; // used to reset generator + int initialTableEntry; + + unsigned long *state; + unsigned long *auxState; + short stateSize; + short auxSize; + unsigned long lcgRecurr; + short j; + short k; + +protected: + +public: + ACG(unsigned long seed = 0, int size = 55); + virtual ~ACG(); + // + // Return a long-words word of random bits + // + virtual unsigned long asLong(); + virtual void reset(); +}; + +#endif diff --git a/gnu/lib/libg++/libg++/AllocRing.cc b/gnu/lib/libg++/libg++/AllocRing.cc index ea57d9f042..5be1ff06f4 100644 --- a/gnu/lib/libg++/libg++/AllocRing.cc +++ b/gnu/lib/libg++/libg++/AllocRing.cc @@ -3,22 +3,17 @@ Copyright (C) 1989 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ @@ -99,8 +94,8 @@ void* AllocRing::alloc(int s) void* p; if (nodes[current].ptr != 0 && - nodes[current].sz >= size && - nodes[current].sz < (4 * size)) + nodes[current].sz >= int(size) && + nodes[current].sz < int(4 * size)) p = nodes[current].ptr; else { @@ -113,9 +108,3 @@ void* AllocRing::alloc(int s) if (current >= n) current = 0; return p; } - - - -#ifdef VMS -#include "libgxx-fmtq.cc" // see Obstack.cc for reason for this -#endif diff --git a/gnu/lib/libg++/libg++/AllocRing.h b/gnu/lib/libg++/libg++/AllocRing.h new file mode 100644 index 0000000000..15dd161075 --- /dev/null +++ b/gnu/lib/libg++/libg++/AllocRing.h @@ -0,0 +1,62 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1989 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _AllocRing_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _AllocRing_h 1 + + +/* + An AllocRing holds the last n malloc'ed strings, reallocating/reusing + one only when the queue wraps around. It thus guarantees that the + last n allocations are intact. It is useful for things like I/O + formatting where reasonable restrictions may be made about the + number of allowable live allocations before auto-deletion. +*/ + +class AllocRing +{ + + struct AllocQNode + { + void* ptr; + int sz; + }; + + AllocQNode* nodes; + int n; + int current; + + int find(void* p); + +public: + + AllocRing(int max); + ~AllocRing(); + + void* alloc(int size); + int contains(void* ptr); + void clear(); + void free(void* p); +}; + + +#endif diff --git a/gnu/lib/libg++/libg++/Binomial.cc b/gnu/lib/libg++/libg++/Binomial.cc index 55f9bf3977..7166e55927 100644 --- a/gnu/lib/libg++/libg++/Binomial.cc +++ b/gnu/lib/libg++/libg++/Binomial.cc @@ -2,29 +2,23 @@ Copyright (C) 1988 Free Software Foundation written by Dirk Grunwald (grunwald@cs.uiuc.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ #pragma implementation #endif #include #include - #include double Binomial::operator()() diff --git a/gnu/lib/libg++/libg++/Binomial.h b/gnu/lib/libg++/libg++/Binomial.h new file mode 100644 index 0000000000..f88dfc3a46 --- /dev/null +++ b/gnu/lib/libg++/libg++/Binomial.h @@ -0,0 +1,55 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef _Binomial_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Binomial_h 1 + +#include + +class Binomial: public Random { +protected: + int pN; + double pU; +public: + Binomial(int n, double u, RNG *gen); + + int n(); + int n(int xn); + + double u(); + double u(int xu); + + virtual double operator()(); + +}; + + +inline Binomial::Binomial(int n, double u, RNG *gen) +: Random(gen){ + pN = n; pU = u; +} + +inline int Binomial::n() { return pN; } +inline int Binomial::n(int xn) { int tmp = pN; pN = xn; return tmp; } + +inline double Binomial::u() { return pU; } +inline double Binomial::u(int xu) { double tmp = pU; pU = xu; return tmp; } + +#endif diff --git a/gnu/lib/libg++/libg++/BitSet.cc b/gnu/lib/libg++/libg++/BitSet.cc index 18aa7b8ce2..7255f3d6bc 100644 --- a/gnu/lib/libg++/libg++/BitSet.cc +++ b/gnu/lib/libg++/libg++/BitSet.cc @@ -2,22 +2,17 @@ Copyright (C) 1988 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* @@ -29,12 +24,15 @@ and this notice must be preserved on all copies. #endif #include #include -#include +#include #include #include #include +#include +#include +#include -volatile void BitSet::error(const char* msg) const +void BitSet::error(const char* msg) const { (*lib_error_handler)("BitSet", msg); } @@ -44,7 +42,7 @@ volatile void BitSet::error(const char* msg) const BitSetRep _nilBitSetRep = { 0, 1, 0, {0} }; // nil BitSets point here #define ONES ((unsigned short)(~0L)) -#define MAXBitSetRep_SIZE ((1 << (SHORTBITS - 1)) - 1) +#define MAXBitSetRep_SIZE ((1 << (sizeof(short)*CHAR_BIT - 1)) - 1) #define MINBitSetRep_SIZE 16 #ifndef MALLOC_MIN_OVERHEAD @@ -80,15 +78,15 @@ inline static BitSetRep* BSnew(int newlen) (*lib_error_handler)("BitSet", "Requested length out of range"); BitSetRep* rep = (BitSetRep *) new char[allocsiz]; - bzero(rep, allocsiz); + memset(rep, 0, allocsiz); rep->sz = (allocsiz - sizeof(BitSetRep) + sizeof(short)) / sizeof(short); return rep; } -BitSetRep* BitSetalloc(BitSetRep* old, const unsigned short* src, int srclen, +BitSetRep* BitSetalloc(BitSetRep* old, const unsigned short* src, int srclen, int newvirt, int newlen) { - if (old == &_nilBitSetRep) old = 0; + if (old == &_nilBitSetRep) old = 0; BitSetRep* rep; if (old == 0 || newlen >= old->sz) rep = BSnew(newlen); @@ -99,8 +97,10 @@ BitSetRep* BitSetalloc(BitSetRep* old, const unsigned short* src, int srclen, rep->virt = newvirt; if (srclen != 0 && src != rep->s) - bcopy(src, rep->s, srclen * sizeof(short)); - + memcpy(rep->s, src, srclen * sizeof(short)); + // BUG fix: extend virtual bit! 20 Oct 1992 Kevin Karplus + if (rep->virt) + memset(&rep->s[srclen], ONES, (newlen - srclen) * sizeof(short)); if (old != rep && old != 0) delete old; return rep; } @@ -116,8 +116,11 @@ BitSetRep* BitSetresize(BitSetRep* old, int newlen) else if (newlen >= old->sz) { rep = BSnew(newlen); - bcopy(old->s, rep->s, old->len * sizeof(short)); + memcpy(rep->s, old->s, old->len * sizeof(short)); rep->virt = old->virt; + // BUG fix: extend virtual bit! 20 Oct 1992 Kevin Karplus + if (rep->virt) + memset(&rep->s[old->len], ONES, (newlen - old->len) * sizeof(short)); delete old; } else @@ -156,7 +159,7 @@ BitSetRep* BitSetcopy(BitSetRep* old, const BitSetRep* src) else rep = old; - bcopy(src->s, rep->s, newlen * sizeof(short)); + memcpy(rep->s, src->s, newlen * sizeof(short)); rep->len = newlen; rep->virt = src->virt; } @@ -180,11 +183,32 @@ inline static void trim(BitSetRep* rep) int operator == (const BitSet& x, const BitSet& y) { - return x.rep->len == y.rep->len && x.rep->virt == y.rep->virt && - bcmp((void*)x.rep->s, (void*)y.rep->s, - x.rep->len * sizeof(short)) == 0; -} + if (x.rep->virt != y.rep->virt) + return 0; + int xl = x.rep->len; + int yl = y.rep->len; + unsigned short* xs = x.rep->s; + unsigned short* ys = y.rep->s; + if (xl<=yl) + { + if (memcmp((void*)xs, (void*)ys, xl * sizeof(short))) + return 0; + for (register int i=xl; is[index] |= (1 << pos); } +void BitSet::clear() +{ + if (rep->len > 0) memset(rep->s, 0, rep->sz * sizeof(short)); + rep->len = rep->virt = 0; +} + void BitSet::clear(int p) { if (p < 0) error("Illegal bit index"); @@ -657,7 +687,7 @@ int BitSet::next(int p, int b) const } } -int BitSet::previous(int p, int b) const +int BitSet::prev(int p, int b) const { if (--p < 0) return -1; @@ -740,7 +770,7 @@ int BitSet::last(int b) const if (b == rep->virt) return -1; else - return previous((rep->len) * BITSETBITS, b); + return prev((rep->len) * BITSETBITS, b); } @@ -749,50 +779,12 @@ extern AllocRing _libgxx_fmtq; const char* BitSettoa(const BitSet& x, char f, char t, char star) { trim(x.rep); - int wrksiz = (x.rep->len + 1) * BITSETBITS + 2; char* fmtbase = (char *) _libgxx_fmtq.alloc(wrksiz); - char* fmt = fmtbase; - - const unsigned short* s = x.rep->s; - const unsigned short* top = &(s[x.rep->len - 1]); - - while (s < top) - { - unsigned short a = *s++; - for (int j = 0; j < BITSETBITS; ++j) - { - *fmt++ = (a & 1)? t : f; - a >>= 1; - } - } - - if (!x.rep->virt) - { - unsigned short a = *s; - for (int j = 0; j < BITSETBITS && a != 0; ++j) - { - *fmt++ = (a & 1)? t : f; - a >>= 1; - } - *fmt++ = f; - } - else - { - unsigned short a = *s; - unsigned short mask = ONES; - unsigned short himask = (1 << (BITSETBITS - 1)) - 1; - for (int j = 0; j < BITSETBITS && a != mask; ++j) - { - *fmt++ = (a & 1)? t : f; - a = (a >> 1) & himask; - mask = (mask >> 1) & himask; - } - *fmt++ = t; - } - - *fmt++ = star; - *fmt++ = 0; + ostrstream stream(fmtbase, wrksiz); + + x.printon(stream, f, t, star); + stream << ends; return fmtbase; } @@ -941,7 +933,60 @@ BitSet atoBitSet(const char* s, char f, char t, char star) ostream& operator << (ostream& s, const BitSet& x) { - return s << BitSettoa(x); + if (s.opfx()) + x.printon(s); + return s; +} + +void BitSet::printon(ostream& os, char f, char t, char star) const +// FIXME: Does not respect s.width()! +{ + trim(rep); + register streambuf* sb = os.rdbuf(); + const unsigned short* s = rep->s; + const unsigned short* top = &(s[rep->len - 1]); + + while (s < top) + { + unsigned short a = *s++; + for (int j = 0; j < BITSETBITS; ++j) + { + sb->sputc((a & 1)? t : f); + a >>= 1; + } + } + + if (!rep->virt) + { + unsigned short a = *s; + if (rep->len != 0) + { + for (int j = 0; j < BITSETBITS && a != 0; ++j) + { + sb->sputc((a & 1)? t : f); + a >>= 1; + } + } + sb->sputc(f); + } + else + { + unsigned short a = *s; + unsigned short mask = ONES; + unsigned short himask = (1 << (BITSETBITS - 1)) - 1; + if (rep->len != 0) + { + for (int j = 0; j < BITSETBITS && a != mask; ++j) + { + sb->sputc((a & 1)? t : f); + a = (a >> 1) & himask; + mask = (mask >> 1) & himask; + } + } + sb->sputc(t); + } + + sb->sputc(star); } int BitSet::OK() const diff --git a/gnu/lib/libg++/libg++/BitSet.h b/gnu/lib/libg++/libg++/BitSet.h new file mode 100644 index 0000000000..daf9c897d5 --- /dev/null +++ b/gnu/lib/libg++/libg++/BitSet.h @@ -0,0 +1,361 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _BitSet_h +#ifdef __GNUG__ +#pragma interface +#endif + +#define _BitSet_h 1 + +#include +#include + +#define BITSETBITS (sizeof(short) * CHAR_BIT) + +struct BitSetRep +{ + unsigned short len; // number of shorts in s + unsigned short sz; // allocated slots + unsigned short virt; // virtual 0 or 1 + unsigned short s[1]; // bits start here +}; + +extern BitSetRep* BitSetalloc(BitSetRep*, const unsigned short*, + int, int, int); +extern BitSetRep* BitSetcopy(BitSetRep*, const BitSetRep*); +extern BitSetRep* BitSetresize(BitSetRep*, int); +extern BitSetRep* BitSetop(const BitSetRep*, const BitSetRep*, + BitSetRep*, char); +extern BitSetRep* BitSetcmpl(const BitSetRep*, BitSetRep*); + + +extern BitSetRep _nilBitSetRep; + +class BitSet; + +class BitSetBit +{ +protected: + BitSet* src; + unsigned long pos; + + public: + BitSetBit(BitSet* v, int p); + BitSetBit(const BitSetBit& b); + ~BitSetBit(); + operator int(); + int operator = (int b); +}; + +class BitSet +{ +protected: + BitSetRep* rep; + + +public: + +// constructors + BitSet(); + BitSet(const BitSet&); + + ~BitSet(); + + void operator = (const BitSet& y); + +// equality & subset tests + + friend int operator == (const BitSet& x, const BitSet& y); + friend int operator != (const BitSet& x, const BitSet& y); + friend int operator < (const BitSet& x, const BitSet& y); + friend int operator <= (const BitSet& x, const BitSet& y); + friend int operator > (const BitSet& x, const BitSet& y); + friend int operator >= (const BitSet& x, const BitSet& y); + + +// operations on self + + void operator |= (const BitSet& y); + void operator &= (const BitSet& y); + void operator -= (const BitSet& y); + void operator ^= (const BitSet& y); + + void complement(); + +// individual bit manipulation + + void set(int pos); + void set(int from, int to); + void set(); // set all + + void clear(int pos); + void clear(int from, int to); + void clear(); // clear all + + void invert(int pos); + void invert(int from, int to); + + int test(int pos) const; + int test(int from, int to) const; + + BitSetBit operator [] (int i); + +// iterators + + int first(int b = 1) const; + int last(int b = 1) const; + + int next(int pos, int b = 1) const; + int prev(int pos, int b = 1) const; + int previous(int pos, int b = 1) const /* Obsolete synonym */ + { return prev(pos, b); } + +// status + + int empty() const; + int virtual_bit() const; + int count(int b = 1) const; + +// convertors & IO + + friend BitSet atoBitSet(const char* s, + char f='0', char t='1', char star='*'); + // BitSettoa is deprecated; do not use in new programs. + friend const char* BitSettoa(const BitSet& x, + char f='0', char t='1', char star='*'); + + friend BitSet shorttoBitSet(unsigned short w); + friend BitSet longtoBitSet(unsigned long w); + + friend ostream& operator << (ostream& s, const BitSet& x); + void printon(ostream& s, + char f='0', char t='1', char star='*') const; + +// procedural versions of operators + + friend void and(const BitSet& x, const BitSet& y, BitSet& r); + friend void or(const BitSet& x, const BitSet& y, BitSet& r); + friend void xor(const BitSet& x, const BitSet& y, BitSet& r); + friend void diff(const BitSet& x, const BitSet& y, BitSet& r); + friend void complement(const BitSet& x, BitSet& r); + +// misc + + void error(const char* msg) const; + int OK() const; +}; + + +typedef BitSet BitSetTmp; + + + BitSet operator | (const BitSet& x, const BitSet& y); + BitSet operator & (const BitSet& x, const BitSet& y); + BitSet operator - (const BitSet& x, const BitSet& y); + BitSet operator ^ (const BitSet& x, const BitSet& y); + + BitSet operator ~ (const BitSet& x); + +// These are inlined regardless of optimization + +inline int BitSet_index(int l) +{ + return (unsigned)(l) / BITSETBITS; +} + +inline int BitSet_pos(int l) +{ + return l & (BITSETBITS - 1); +} + + +inline BitSet::BitSet() : rep(&_nilBitSetRep) {} + +inline BitSet::BitSet(const BitSet& x) :rep(BitSetcopy(0, x.rep)) {} + +inline BitSet::~BitSet() { if (rep != &_nilBitSetRep) delete rep; } + +inline void BitSet::operator = (const BitSet& y) +{ + rep = BitSetcopy(rep, y.rep); +} + +inline int operator != (const BitSet& x, const BitSet& y) { return !(x == y); } + +inline int operator > (const BitSet& x, const BitSet& y) { return y < x; } + +inline int operator >= (const BitSet& x, const BitSet& y) { return y <= x; } + +inline void and(const BitSet& x, const BitSet& y, BitSet& r) +{ + r.rep = BitSetop(x.rep, y.rep, r.rep, '&'); +} + +inline void or(const BitSet& x, const BitSet& y, BitSet& r) +{ + r.rep = BitSetop(x.rep, y.rep, r.rep, '|'); +} + +inline void xor(const BitSet& x, const BitSet& y, BitSet& r) +{ + r.rep = BitSetop(x.rep, y.rep, r.rep, '^'); +} + +inline void diff(const BitSet& x, const BitSet& y, BitSet& r) +{ + r.rep = BitSetop(x.rep, y.rep, r.rep, '-'); +} + +inline void complement(const BitSet& x, BitSet& r) +{ + r.rep = BitSetcmpl(x.rep, r.rep); +} + +#if defined(__GNUG__) && !defined(NO_NRV) + +inline BitSet operator & (const BitSet& x, const BitSet& y) return r +{ + and(x, y, r); +} + +inline BitSet operator | (const BitSet& x, const BitSet& y) return r +{ + or(x, y, r); +} + +inline BitSet operator ^ (const BitSet& x, const BitSet& y) return r +{ + xor(x, y, r); +} + +inline BitSet operator - (const BitSet& x, const BitSet& y) return r +{ + diff(x, y, r); +} + +inline BitSet operator ~ (const BitSet& x) return r +{ + ::complement(x, r); +} + +#else /* NO_NRV */ + +inline BitSet operator & (const BitSet& x, const BitSet& y) +{ + BitSet r; and(x, y, r); return r; +} + +inline BitSet operator | (const BitSet& x, const BitSet& y) +{ + BitSet r; or(x, y, r); return r; +} + +inline BitSet operator ^ (const BitSet& x, const BitSet& y) +{ + BitSet r; xor(x, y, r); return r; +} + +inline BitSet operator - (const BitSet& x, const BitSet& y) +{ + BitSet r; diff(x, y, r); return r; +} + +inline BitSet operator ~ (const BitSet& x) +{ + BitSet r; ::complement(x, r); return r; +} + +#endif + +inline void BitSet::operator &= (const BitSet& y) +{ + and(*this, y, *this); +} + +inline void BitSet::operator |= (const BitSet& y) +{ + or(*this, y, *this); +} + +inline void BitSet::operator ^= (const BitSet& y) +{ + xor(*this, y, *this); +} + +inline void BitSet::operator -= (const BitSet& y) +{ + diff(*this, y, *this); +} + + +inline void BitSet::complement() +{ + ::complement(*this, *this); +} + +inline int BitSet::virtual_bit() const +{ + return rep->virt; +} + +inline int BitSet::first(int b) const +{ + return next(-1, b); +} + +inline int BitSet::test(int p) const +{ + if (p < 0) error("Illegal bit index"); + int index = BitSet_index(p); + return (index >= rep->len)? rep->virt : + ((rep->s[index] & (1 << BitSet_pos(p))) != 0); +} + + +inline void BitSet::set() +{ + rep = BitSetalloc(rep, 0, 0, 1, 0); +} + +inline BitSetBit::BitSetBit(const BitSetBit& b) :src(b.src), pos(b.pos) {} + +inline BitSetBit::BitSetBit(BitSet* v, int p) +{ + src = v; pos = p; +} + +inline BitSetBit::~BitSetBit() {} + +inline BitSetBit::operator int() +{ + return src->test(pos); +} + +inline int BitSetBit::operator = (int b) +{ + if (b) src->set(pos); else src->clear(pos); return b; +} + +inline BitSetBit BitSet::operator [] (int i) +{ + if (i < 0) error("illegal bit index"); + return BitSetBit(this, i); +} + +#endif diff --git a/gnu/lib/libg++/libg++/BitString.cc b/gnu/lib/libg++/libg++/BitString.cc index ca9ed237f6..261426c3c9 100644 --- a/gnu/lib/libg++/libg++/BitString.cc +++ b/gnu/lib/libg++/libg++/BitString.cc @@ -2,22 +2,17 @@ Copyright (C) 1988 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* @@ -29,12 +24,14 @@ and this notice must be preserved on all copies. #endif #include #include -#include +#include #include #include #include +#include +#include -volatile void BitString::error(const char* msg) const +void BitString::error(const char* msg) const { (*lib_error_handler)("BitString", msg); } @@ -46,7 +43,7 @@ BitStrRep _nilBitStrRep = { 0, 1, {0} }; BitString _nil_BitString; #define MINBitStrRep_SIZE 8 -#define MAXBitStrRep_SIZE ((1 << (SHORTBITS - 1)) - 1) +#define MAXBitStrRep_SIZE ((1 << (sizeof(short)*CHAR_BIT - 1)) - 1) #ifndef MALLOC_MIN_OVERHEAD #define MALLOC_MIN_OVERHEAD 4 @@ -151,7 +148,7 @@ static inline void bit_copy(const unsigned short* ss, unsigned short* ds, if (ss != ds) { int n = (unsigned)(nbits) / BITSTRBITS; - if (n > 0) bcopy((void*)ss, (void*)ds, n * sizeof(short)); + if (n > 0) memmove((void*)ds, (const void*)ss, n * sizeof(short)); unsigned short m = ONES << (nbits & (BITSTRBITS - 1)); ds[n] = (ss[n] & ~m) | (ds[n] & m); } @@ -162,7 +159,7 @@ static inline void bit_copy(const unsigned short* ss, unsigned short* ds, static inline void bit_clear(unsigned short* ds, int nbits) { int n = (unsigned)(nbits) / BITSTRBITS; - if (n > 0) bzero((void*)ds, n * sizeof(short)); + if (n > 0) memset((void*)ds, 0, n * sizeof(short)); ds[n] &= ONES << (nbits & (BITSTRBITS - 1)); } @@ -299,7 +296,7 @@ inline static BitStrRep* BSnew(int newlen) (*lib_error_handler)("BitString", "Requested length out of range"); BitStrRep* rep = (BitStrRep *) new char[allocsiz]; - bzero(rep, allocsiz); + memset(rep, 0, allocsiz); rep->sz = (allocsiz - sizeof(BitStrRep) + sizeof(short)) / sizeof(short); return rep; } @@ -339,7 +336,7 @@ BitStrRep* BStr_resize(BitStrRep* old, int newlen) else if (news > old->sz) { rep = BSnew(newlen); - bcopy(old->s, rep->s, BitStr_len(old->len) * sizeof(short)); + memcpy(rep->s, old->s, BitStr_len(old->len) * sizeof(short)); delete old; } else @@ -376,7 +373,7 @@ BitStrRep* BStr_copy(BitStrRep* old, const BitStrRep* src) else rep = old; - bcopy(src->s, rep->s, news * sizeof(short)); + memcpy(rep->s, src->s, news * sizeof(short)); rep->len = newlen; } check_last(rep); @@ -387,7 +384,7 @@ BitStrRep* BStr_copy(BitStrRep* old, const BitStrRep* src) int operator == (const BitString& x, const BitString& y) { return x.rep->len == y.rep->len && - bcmp((void*)x.rep->s, (void*)y.rep->s, + memcmp((void*)x.rep->s, (void*)y.rep->s, BitStr_len(x.rep->len) * sizeof(short)) == 0; } @@ -747,14 +744,14 @@ BitStrRep* lshift(const BitStrRep* x, int s, BitStrRep* r) void BitString::set(int p) { if (p < 0) error("Illegal bit index"); - if (p >= rep->len) rep = BStr_resize(rep, p + 1); + if ((unsigned)(p) >= rep->len) rep = BStr_resize(rep, p + 1); rep->s[BitStr_index(p)] |= (1 << (BitStr_pos(p))); } void BitString::assign(int p, unsigned int bit) { if (p < 0) error("Illegal bit index"); - if (p >= rep->len) rep = BStr_resize(rep, p + 1); + if ((unsigned)(p) >= rep->len) rep = BStr_resize(rep, p + 1); if (bit) rep->s[BitStr_index(p)] |= (1 << (BitStr_pos(p))); else @@ -764,7 +761,7 @@ void BitString::assign(int p, unsigned int bit) void BitString::clear(int p) { if (p < 0) error("Illegal bit index"); - if (p >= rep->len) rep = BStr_resize(rep, p + 1); + if ((unsigned)(p) >= rep->len) rep = BStr_resize(rep, p + 1); rep->s[BitStr_index(p)] &= ~(1 << (BitStr_pos(p))); } @@ -786,7 +783,7 @@ void BitString::set() void BitString::invert(int p) { if (p < 0) error("Illegal bit index"); - if (p >= rep->len) rep = BStr_resize(rep, p + 1); + if ((unsigned)(p) >= rep->len) rep = BStr_resize(rep, p + 1); rep->s[BitStr_index(p)] ^= (1 << (BitStr_pos(p))); } @@ -795,7 +792,7 @@ void BitString::invert(int p) void BitString::set(int from, int to) { if (from < 0 || from > to) error("Illegal bit index"); - if (to >= rep->len) rep = BStr_resize(rep, to+1); + if ((unsigned)(to) >= rep->len) rep = BStr_resize(rep, to+1); int ind1 = BitStr_index(from); int pos1 = BitStr_pos(from); @@ -819,7 +816,7 @@ void BitString::set(int from, int to) void BitString::clear(int from, int to) { if (from < 0 || from > to) error("Illegal bit index"); - if (to >= rep->len) rep = BStr_resize(rep, to+1); + if ((unsigned)(to) >= rep->len) rep = BStr_resize(rep, to+1); int ind1 = BitStr_index(from); int pos1 = BitStr_pos(from); @@ -843,7 +840,7 @@ void BitString::clear(int from, int to) void BitString::invert(int from, int to) { if (from < 0 || from > to) error("Illegal bit index"); - if (to >= rep->len) rep = BStr_resize(rep, to+1); + if ((unsigned)(to) >= rep->len) rep = BStr_resize(rep, to+1); int ind1 = BitStr_index(from); int pos1 = BitStr_pos(from); @@ -870,14 +867,14 @@ void BitString::invert(int from, int to) int BitString::test(int from, int to) const { - if (from < 0 || from > to || from >= rep->len) return 0; + if (from < 0 || from > to || (unsigned)(from) >= rep->len) return 0; int ind1 = BitStr_index(from); int pos1 = BitStr_pos(from); int ind2 = BitStr_index(to); int pos2 = BitStr_pos(to); - if (to >= rep->len) + if ((unsigned)(to) >= rep->len) { ind2 = BitStr_index(rep->len - 1); pos2 = BitStr_pos(rep->len - 1); @@ -905,7 +902,7 @@ int BitString::test(int from, int to) const int BitString::next(int p, unsigned int b) const { - if (++p >= rep->len) + if ((unsigned)(++p) >= rep->len) return -1; int ind = BitStr_index(p); @@ -981,7 +978,7 @@ int BitString::next(int p, unsigned int b) const } } -int BitString::previous(int p, unsigned int b) const +int BitString::prev(int p, unsigned int b) const { if (--p < 0) return -1; @@ -991,7 +988,7 @@ int BitString::previous(int p, unsigned int b) const const unsigned short* s = rep->s; - if (p >= rep->len) + if ((unsigned)(p) >= rep->len) { ind = BitStr_index(rep->len - 1); pos = BitStr_pos(rep->len - 1); @@ -1407,7 +1404,7 @@ void BitSubString::operator = (const BitString& y) if (&S == &_nil_BitString) return; BitStrRep* targ = S.rep; - int ylen = y.rep->len; + unsigned int ylen = y.rep->len; int sl = targ->len - len + ylen; if (y.rep == targ || ylen > len) @@ -1435,12 +1432,12 @@ void BitSubString::operator = (const BitSubString& y) { if (&S == &_nil_BitString) return; BitStrRep* targ = S.rep; - + if (len == 0 || pos >= targ->len) return; - + int sl = targ->len - len + y.len; - + if (y.S.rep == targ || y.len > len) { BitStrRep* oldtarg = targ; @@ -1544,7 +1541,7 @@ BitString common_prefix(const BitString& x, const BitString& y, int startpos) unsigned int xl = x.rep->len; unsigned int yl = y.rep->len; - int startx, starty; + unsigned int startx, starty; if (startpos < 0) { startx = xl + startpos; @@ -1553,16 +1550,16 @@ BitString common_prefix(const BitString& x, const BitString& y, int startpos) else startx = starty = startpos; - if (startx < 0 || startx >= xl || starty < 0 || starty >= yl) + if (startx >= xl || starty >= yl) return; const unsigned short* xs = &(x.rep->s[BitStr_index(startx)]); unsigned short a = *xs++; - int xp = startx; + unsigned int xp = startx; const unsigned short* ys = &(y.rep->s[BitStr_index(starty)]); unsigned short b = *ys++; - int yp = starty; + unsigned int yp = starty; for(; xp < xl && yp < yl; ++xp, ++yp) { @@ -1585,7 +1582,7 @@ BitString common_suffix(const BitString& x, const BitString& y, int startpos) unsigned int xl = x.rep->len; unsigned int yl = y.rep->len; - int startx, starty; + unsigned int startx, starty; if (startpos < 0) { startx = xl + startpos; @@ -1594,7 +1591,7 @@ BitString common_suffix(const BitString& x, const BitString& y, int startpos) else startx = starty = startpos; - if (startx < 0 || startx >= xl || starty < 0 || starty >= yl) + if (startx >= xl || starty >= yl) return; const unsigned short* xs = &(x.rep->s[BitStr_index(startx)]); @@ -1974,32 +1971,37 @@ BitPattern atoBitPattern(const char* s, char f,char t,char x) extern AllocRing _libgxx_fmtq; -const char* BitStringtoa(const BitString& x, char f, char t) +void BitString::printon(ostream& os, char f, char t) const { - int wrksiz = x.length() + 2; - char* fmtbase = (char *) _libgxx_fmtq.alloc(wrksiz); - char* fmt = fmtbase; - unsigned int xl = x.rep->len; - const unsigned short* s = x.rep->s; + unsigned int xl = rep->len; + const unsigned short* ptr = rep->s; + register streambuf *sb = os.rdbuf(); unsigned short a = 0; for (unsigned int i = 0; i < xl; ++i) { if (i % BITSTRBITS == 0) - a = *s++; - *fmt++ = (a & 1)? t : f; + a = *ptr++; + sb->sputc((a & 1)? t : f); a >>= 1; } - - *fmt = 0; - +} +const char* BitStringtoa(const BitString& x, char f, char t) +{ + int wrksiz = x.length() + 2; + char* fmtbase = (char *) _libgxx_fmtq.alloc(wrksiz); + ostrstream stream(fmtbase, wrksiz); + + x.printon(stream, f, t); + stream << ends; return fmtbase; } - ostream& operator << (ostream& s, const BitString& x) { - return s << BitStringtoa(x); + if (s.opfx()) + x.printon(s); + return s; } const char* BitPatterntoa(const BitPattern& p, char f,char t,char x) @@ -2010,10 +2012,22 @@ const char* BitPatterntoa(const BitPattern& p, char f,char t,char x) int wrksiz = l + 2; char* fmtbase = (char *) _libgxx_fmtq.alloc(wrksiz); - char* fmt = fmtbase; + ostrstream stream(fmtbase, wrksiz); + + p.printon(stream, f, t, x); + stream << ends; + return fmtbase; +} + +void BitPattern::printon(ostream& s, char f,char t,char x) const +{ + unsigned int pl = pattern.rep->len; + unsigned int ml = mask.rep->len; + unsigned int l = (pl <= ml)? pl : ml; + register streambuf *sb = s.rdbuf(); - const unsigned short* ps = p.pattern.rep->s; - const unsigned short* ms = p.mask.rep->s; + const unsigned short* ps = pattern.rep->s; + const unsigned short* ms = mask.rep->s; unsigned short a = 0; unsigned short m = 0; @@ -2025,21 +2039,19 @@ const char* BitPatterntoa(const BitPattern& p, char f,char t,char x) m = *ms++; } if (m & 1) - *fmt++ =(a & 1)? t : f; + sb->sputc((a & 1)? t : f); else - *fmt++ = x; + sb->sputc(x); a >>= 1; m >>= 1; } - - *fmt = 0; - return fmtbase; } - ostream& operator << (ostream& s, const BitPattern& x) { - return s << BitPatterntoa(x); + if (s.opfx()) + x.printon(s); + return s; } @@ -2054,7 +2066,6 @@ int BitString::OK() const int BitSubString::OK() const { int v = S.OK(); // valid BitString - v &= pos >= 0 && len >= 0; // valid indices v &= pos + len <= S.rep->len; // within bounds of targ if (!v) S.error("BitSubString invariant failure"); return v; diff --git a/gnu/lib/libg++/libg++/BitString.h b/gnu/lib/libg++/libg++/BitString.h new file mode 100644 index 0000000000..f40efeeeb4 --- /dev/null +++ b/gnu/lib/libg++/libg++/BitString.h @@ -0,0 +1,757 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _BitString_h +#ifdef __GNUG__ +#pragma interface +#endif + +#define _BitString_h 1 + +#include +#include + +#define BITSTRBITS (sizeof(short) * CHAR_BIT) + +struct BitStrRep +{ + unsigned int len; // length in bits + unsigned short sz; // allocated slots + unsigned short s[1]; // bits start here +}; + +extern BitStrRep* BStr_alloc(BitStrRep*, const unsigned short*, int, int,int); +extern BitStrRep* BStr_resize(BitStrRep*, int); +extern BitStrRep* BStr_copy(BitStrRep*, const BitStrRep*); +extern BitStrRep* cmpl(const BitStrRep*, BitStrRep*); +extern BitStrRep* and(const BitStrRep*, const BitStrRep*, BitStrRep*); +extern BitStrRep* or(const BitStrRep*, const BitStrRep*, BitStrRep*); +extern BitStrRep* xor(const BitStrRep*, const BitStrRep*, BitStrRep*); +extern BitStrRep* diff(const BitStrRep*, const BitStrRep*, BitStrRep*); +extern BitStrRep* cat(const BitStrRep*, const BitStrRep*, BitStrRep*); +extern BitStrRep* cat(const BitStrRep*, unsigned int, BitStrRep*); +extern BitStrRep* lshift(const BitStrRep*, int, BitStrRep*); + + +class BitString; +class BitPattern; + +class BitStrBit +{ +protected: + BitString& src; + unsigned int pos; + + public: + BitStrBit(BitString& v, int p); + BitStrBit(const BitStrBit& b); + ~BitStrBit(); + operator unsigned int() const; + int operator = (unsigned int b); +}; + +class BitSubString +{ + friend class BitString; + friend class BitPattern; + +protected: + + BitString& S; + unsigned int pos; + unsigned int len; + + BitSubString(BitString& x, int p, int l); + BitSubString(const BitSubString& x); +public: + ~BitSubString(); + + void operator = (const BitString&); + void operator = (const BitSubString&); + + int length() const; + int empty() const; + + int OK() const; +}; + +class BitString +{ + friend class BitSubString; + friend class BitPattern; +protected: + BitStrRep* rep; + + int search(int, int, const unsigned short*, int, int) const; + int match(int, int, int, const unsigned short*,int,int) const; + BitSubString _substr(int first, int l); + +public: + +// constructors + BitString(); + BitString(const BitString&); + BitString(const BitSubString& y); + + ~BitString(); + + void operator = (unsigned int bit); + void operator = (const BitString& y); + void operator = (const BitSubString& y); + +// equality & subset tests + + friend int operator == (const BitString&, const BitString&); + friend int operator != (const BitString&, const BitString&); + friend int operator < (const BitString&, const BitString&); + friend int operator <= (const BitString&, const BitString&); + friend int operator > (const BitString&, const BitString&); + friend int operator >= (const BitString&, const BitString&); + +// procedural versions of operators + + + friend void and(const BitString&, const BitString&, BitString&); + friend void or(const BitString&, const BitString&, BitString&); + friend void xor(const BitString&, const BitString&, BitString&); + friend void diff(const BitString&, const BitString&, BitString&); + friend void cat(const BitString&, const BitString&, BitString&); + friend void cat(const BitString&, unsigned int, BitString&); + friend void lshift(const BitString&, int, BitString&); + friend void rshift(const BitString&, int, BitString&); + + friend void complement(const BitString&, BitString&); + + friend int lcompare(const BitString&, const BitString&); + +// assignment-based operators +// (constuctive versions decalred inline below + + void operator |= (const BitString&); + void operator &= (const BitString&); + void operator -= (const BitString&); + void operator ^= (const BitString&); + void operator += (const BitString&); + void operator += (unsigned int b); + void operator <<=(int s); + void operator >>=(int s); + + void complement(); + +// individual bit manipulation + + void set(int pos); + void set(int from, int to); + void set(); + + void clear(int pos); + void clear(int from, int to); + void clear(); + + void invert(int pos); + void invert(int from, int to); + + int test(int pos) const; + int test(int from, int to) const; + + void assign(int p, unsigned int bit); + +// indexing + + BitStrBit operator [] (int pos); + +// iterators + + int first(unsigned int bit = 1) const; + int last(unsigned int b = 1) const; + + int next(int pos, unsigned int b = 1) const; + int prev(int pos, unsigned int b = 1) const; + int previous(int pos, unsigned int b = 1) const + { return prev(pos, b); } /* Obsolete synonym */ + +// searching & matching + + int index(unsigned int bit, int startpos = 0) const ; + int index(const BitString&, int startpos = 0) const; + int index(const BitSubString&, int startpos = 0) const; + int index(const BitPattern&, int startpos = 0) const; + + int contains(const BitString&) const; + int contains(const BitSubString&) const; + int contains(const BitPattern&) const; + + int contains(const BitString&, int pos) const; + int contains(const BitSubString&, int pos) const; + int contains(const BitPattern&, int pos) const; + + int matches(const BitString&, int pos = 0) const; + int matches(const BitSubString&, int pos = 0) const; + int matches(const BitPattern&, int pos = 0) const; + +// BitSubString extraction + + BitSubString at(int pos, int len); + BitSubString at(const BitString&, int startpos = 0); + BitSubString at(const BitSubString&, int startpos = 0); + BitSubString at(const BitPattern&, int startpos = 0); + + BitSubString before(int pos); + BitSubString before(const BitString&, int startpos = 0); + BitSubString before(const BitSubString&, int startpos = 0); + BitSubString before(const BitPattern&, int startpos = 0); + + BitSubString after(int pos); + BitSubString after(const BitString&, int startpos = 0); + BitSubString after(const BitSubString&, int startpos = 0); + BitSubString after(const BitPattern&, int startpos = 0); + +// other friends & utilities + + friend BitString common_prefix(const BitString&, const BitString&, + int pos = 0); + friend BitString common_suffix(const BitString&, const BitString&, + int pos = -1); + friend BitString reverse(const BitString&); + + void right_trim(unsigned int bit); + void left_trim(unsigned int bit); + +// status + + int empty() const ; + int count(unsigned int bit = 1) const; + int length() const; + +// convertors & IO + + friend BitString atoBitString(const char* s, char f='0', char t='1'); + // BitStringtoa is deprecated; do not use in new programs! + friend const char* BitStringtoa(const BitString&, char f='0', char t='1'); + void printon(ostream&, char f='0', char t='1') const; + + friend BitString shorttoBitString(unsigned short); + friend BitString longtoBitString(unsigned long); + + friend ostream& operator << (ostream& s, const BitString&); + +// misc + + void error(const char* msg) const; + +// indirect friends + + friend BitPattern atoBitPattern(const char* s, + char f='0',char t='1',char x='X'); + friend const char* BitPatterntoa(const BitPattern& p, + char f='0',char t='1',char x='X'); + int OK() const; +}; + + +class BitPattern +{ +public: + BitString pattern; + BitString mask; + + BitPattern(); + BitPattern(const BitPattern&); + BitPattern(const BitString& p, const BitString& m); + + ~BitPattern(); + + friend const char* BitPatterntoa(const BitPattern& p, + char f/*='0'*/,char t/*='1'*/,char x/*='X'*/); + void printon(ostream&, char f='0',char t='1',char x='X') const; + friend BitPattern atoBitPattern(const char* s, char f,char t, char x); + friend ostream& operator << (ostream& s, const BitPattern&); + + int search(const unsigned short*, int, int) const; + int match(const unsigned short* xs, int, int, int) const; + + int OK() const; +}; + +BitString operator & (const BitString& x, const BitString& y); +BitString operator | (const BitString& x, const BitString& y); +BitString operator ^ (const BitString& x, const BitString& y); +BitString operator << (const BitString& x, int y); +BitString operator >> (const BitString& x, int y); +BitString operator - (const BitString& x, const BitString& y); +BitString operator + (const BitString& x, const BitString& y); +BitString operator + (const BitString& x, unsigned int y); +BitString operator ~ (const BitString& x); +int operator != (const BitString& x, const BitString& y); +int operator>(const BitString& x, const BitString& y); +int operator>=(const BitString& x, const BitString& y); + +extern BitStrRep _nilBitStrRep; +extern BitString _nil_BitString; + +// primitive bit extraction + +// These must be inlined regardless of optimization. + +inline int BitStr_index(int l) { return (unsigned)(l) / BITSTRBITS; } + +inline int BitStr_pos(int l) { return l & (BITSTRBITS - 1); } + + +// constructors & assignment + +inline BitString::BitString() :rep(&_nilBitStrRep) {} + +inline BitString::BitString(const BitString& x) :rep(BStr_copy(0, x.rep)) {} + +inline BitString::BitString(const BitSubString& y) + :rep (BStr_alloc(0, y.S.rep->s, y.pos, y.pos+y.len, y.len)) {} + +inline BitString::~BitString() +{ + if (rep != &_nilBitStrRep) delete rep; +} + +#if defined(__GNUG__) && !defined(NO_NRV) + +inline BitString shorttoBitString(unsigned short w) return r +{ + r.rep = BStr_alloc(0, &w, 0, BITSTRBITS, BITSTRBITS); +} + +inline BitString longtoBitString(unsigned long w) return r +{ + unsigned short u[2]; + u[0] = w & ((unsigned short)(~(0))); + u[1] = w >> BITSTRBITS; + r.rep = BStr_alloc(0, &u[0], 0, 2*BITSTRBITS, 2*BITSTRBITS); +} + +#else + +inline BitString shorttoBitString(unsigned short w) +{ + BitString r; r.rep = BStr_alloc(0, &w, 0, BITSTRBITS, BITSTRBITS); return r; +} + +inline BitString longtoBitString(unsigned long w) +{ + BitString r; + unsigned short u[2]; + u[0] = w & ((unsigned short)(~(0))); + u[1] = w >> BITSTRBITS; + r.rep = BStr_alloc(0, &u[0], 0, 2*BITSTRBITS, 2*BITSTRBITS); + return r; +} + +#endif + +inline void BitString::operator = (const BitString& y) +{ + rep = BStr_copy(rep, y.rep); +} + +inline void BitString::operator = (unsigned int b) +{ + unsigned short bit = b; + rep = BStr_alloc(rep, &bit, 0, 1, 1); +} + +inline void BitString::operator=(const BitSubString& y) +{ + rep = BStr_alloc(rep, y.S.rep->s, y.pos, y.pos+y.len, y.len); +} + +inline BitSubString::BitSubString(const BitSubString& x) + :S(x.S), pos(x.pos), len(x.len) {} + +inline BitSubString::BitSubString(BitString& x, int p, int l) + : S(x), pos(p), len(l) {} + +inline BitSubString::~BitSubString() {} + +inline BitPattern::BitPattern(const BitString& p, const BitString& m) + :pattern(p), mask(m) {} + +inline BitPattern::BitPattern(const BitPattern& b) + :pattern(b.pattern), mask(b.mask) {} + +inline BitPattern::BitPattern() {} +inline BitPattern::~BitPattern() {} + + +// procedural versions of operators + +inline void and(const BitString& x, const BitString& y, BitString& r) +{ + r.rep = and(x.rep, y.rep, r.rep); +} + +inline void or(const BitString& x, const BitString& y, BitString& r) +{ + r.rep = or(x.rep, y.rep, r.rep); +} + +inline void xor(const BitString& x, const BitString& y, BitString& r) +{ + r.rep = xor(x.rep, y.rep, r.rep); +} + +inline void diff(const BitString& x, const BitString& y, BitString& r) +{ + r.rep = diff(x.rep, y.rep, r.rep); +} + +inline void cat(const BitString& x, const BitString& y, BitString& r) +{ + r.rep = cat(x.rep, y.rep, r.rep); +} + +inline void cat(const BitString& x, unsigned int y, BitString& r) +{ + r.rep = cat(x.rep, y, r.rep); +} + +inline void rshift(const BitString& x, int y, BitString& r) +{ + r.rep = lshift(x.rep, -y, r.rep); +} + +inline void lshift(const BitString& x, int y, BitString& r) +{ + r.rep = lshift(x.rep, y, r.rep); +} + +inline void complement(const BitString& x, BitString& r) +{ + r.rep = cmpl(x.rep, r.rep); +} + +// operators + + +inline void BitString::operator &= (const BitString& y) +{ + and(*this, y, *this); +} + + +inline void BitString::operator |= (const BitString& y) +{ + or(*this, y, *this); +} + +inline void BitString::operator ^= (const BitString& y) +{ + xor(*this, y, *this); +} + +inline void BitString::operator <<= (int y) +{ + lshift(*this, y, *this); +} + +inline void BitString::operator >>= (int y) +{ + rshift(*this, y, *this); +} + +inline void BitString::operator -= (const BitString& y) +{ + diff(*this, y, *this); +} + +inline void BitString::operator += (const BitString& y) +{ + cat(*this, y, *this); +} + +inline void BitString::operator += (unsigned int y) +{ + cat(*this, y, *this); +} + +inline void BitString::complement() +{ + ::complement(*this, *this); +} + +#if defined(__GNUG__) && !defined(NO_NRV) + +inline BitString operator & (const BitString& x, const BitString& y) return r +{ + and(x, y, r); +} + +inline BitString operator | (const BitString& x, const BitString& y) return r +{ + or(x, y, r); +} + +inline BitString operator ^ (const BitString& x, const BitString& y) return r +{ + xor(x, y, r); +} + +inline BitString operator << (const BitString& x, int y) return r +{ + lshift(x, y, r); +} + +inline BitString operator >> (const BitString& x, int y) return r +{ + rshift(x, y, r); +} + +inline BitString operator - (const BitString& x, const BitString& y) return r +{ + diff(x, y, r); +} + +inline BitString operator + (const BitString& x, const BitString& y) return r +{ + cat(x, y, r); +} + +inline BitString operator + (const BitString& x, unsigned int y) return r +{ + cat(x, y, r); +} + +inline BitString operator ~ (const BitString& x) return r +{ + complement(x, r); +} + +#else /* NO_NRV */ + +inline BitString operator & (const BitString& x, const BitString& y) +{ + BitString r; and(x, y, r); return r; +} + +inline BitString operator | (const BitString& x, const BitString& y) +{ + BitString r; or(x, y, r); return r; +} + +inline BitString operator ^ (const BitString& x, const BitString& y) +{ + BitString r; xor(x, y, r); return r; +} + +inline BitString operator << (const BitString& x, int y) +{ + BitString r; lshift(x, y, r); return r; +} + +inline BitString operator >> (const BitString& x, int y) +{ + BitString r; rshift(x, y, r); return r; +} + +inline BitString operator - (const BitString& x, const BitString& y) +{ + BitString r; diff(x, y, r); return r; +} + +inline BitString operator + (const BitString& x, const BitString& y) +{ + BitString r; cat(x, y, r); return r; +} + +inline BitString operator + (const BitString& x, unsigned int y) +{ + BitString r; cat(x, y, r); return r; +} + +inline BitString operator ~ (const BitString& x) +{ + BitString r; complement(x, r); return r; +} + +#endif + +// status, matching + +inline int BitString::length() const +{ + return rep->len; +} + +inline int BitString::empty() const +{ + return rep->len == 0; +} + +inline int BitString::index(const BitString& y, int startpos) const +{ + return search(startpos, rep->len, y.rep->s, 0, y.rep->len); +} + +inline int BitString::index(const BitSubString& y, int startpos) const +{ + return search(startpos, rep->len, y.S.rep->s, y.pos, y.pos+y.len); +} + +inline int BitString::contains(const BitString& y) const +{ + return search(0, rep->len, y.rep->s, 0, y.rep->len) >= 0; +} + +inline int BitString::contains(const BitSubString& y) const +{ + return search(0, rep->len, y.S.rep->s, y.pos, y.pos+y.len) >= 0; +} + +inline int BitString::contains(const BitString& y, int p) const +{ + return match(p, rep->len, 0, y.rep->s, 0, y.rep->len); +} + +inline int BitString::matches(const BitString& y, int p) const +{ + return match(p, rep->len, 1, y.rep->s, 0, y.rep->len); +} + +inline int BitString::contains(const BitSubString& y, int p) const +{ + return match(p, rep->len, 0, y.S.rep->s, y.pos, y.pos+y.len); +} + +inline int BitString::matches(const BitSubString& y, int p) const +{ + return match(p, rep->len, 1, y.S.rep->s, y.pos, y.pos+y.len); +} + +inline int BitString::contains(const BitPattern& r) const +{ + return r.search(rep->s, 0, rep->len) >= 0; +} + +inline int BitString::contains(const BitPattern& r, int p) const +{ + return r.match(rep->s, p, rep->len, 0); +} + +inline int BitString::matches(const BitPattern& r, int p) const +{ + return r.match(rep->s, p, rep->len, 1); +} + +inline int BitString::index(const BitPattern& r, int startpos) const +{ + return r.search(rep->s, startpos, rep->len); +} + +inline int BitSubString::length() const +{ + return len; +} + +inline int BitSubString::empty() const +{ + return len == 0; +} + +inline int operator != (const BitString& x, const BitString& y) +{ + return !(x == y); +} + +inline int operator>(const BitString& x, const BitString& y) +{ + return y < x; +} + +inline int operator>=(const BitString& x, const BitString& y) +{ + return y <= x; +} + +inline int BitString::first(unsigned int b) const +{ + return next(-1, b); +} + +inline int BitString::last(unsigned int b) const +{ + return prev(rep->len, b); +} + +inline int BitString::index(unsigned int bit, int startpos) const +{ + if (startpos >= 0) + return next(startpos - 1, bit); + else + return prev(rep->len + startpos + 1, bit); +} + +inline void BitString::right_trim(unsigned int b) +{ + int nb = (b == 0)? 1 : 0; + rep = BStr_resize(rep, prev(rep->len, nb) + 1); +} + +inline void BitString::left_trim(unsigned int b) +{ + int nb = (b == 0)? 1 : 0; + int p = next(-1, nb); + rep = BStr_alloc(rep, rep->s, p, rep->len, rep->len - p); +} + +inline int BitString::test(int i) const +{ + return ((unsigned)(i) >= rep->len)? 0 : + ((rep->s[BitStr_index(i)] & (1 << (BitStr_pos(i)))) != 0); +} + + +// subscripting + +inline BitStrBit::BitStrBit(const BitStrBit& b) :src(b.src), pos(b.pos) {} + +inline BitStrBit::BitStrBit(BitString& v, int p) :src(v), pos(p) {} + +inline BitStrBit::~BitStrBit() {} + +inline BitStrBit::operator unsigned int() const +{ + return src.test(pos); +} + +inline int BitStrBit::operator = (unsigned int b) +{ + src.assign(pos, b); return b; +} + +inline BitStrBit BitString::operator [] (int i) +{ + if ((unsigned)(i) >= rep->len) error("illegal bit index"); + return BitStrBit(*this, i); +} + +inline BitSubString BitString::_substr(int first, int l) +{ + if (first < 0 || l <= 0 || (unsigned)(first + l) > rep->len) + return BitSubString(_nil_BitString, 0, 0) ; + else + return BitSubString(*this, first, l); +} + +#endif diff --git a/gnu/lib/libg++/libg++/Complex.cc b/gnu/lib/libg++/libg++/Complex.cc index 712d24e563..709e633334 100644 --- a/gnu/lib/libg++/libg++/Complex.cc +++ b/gnu/lib/libg++/libg++/Complex.cc @@ -2,22 +2,17 @@ Copyright (C) 1988 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ @@ -25,6 +20,7 @@ and this notice must be preserved on all copies. #endif #include #include +#include // error handling @@ -51,7 +47,7 @@ void Complex::error(const char* msg) const /* from romine@xagsun.epm.ornl.gov */ Complex /* const */ operator / (const Complex& x, const Complex& y) { - double den = abs(y.real()) + abs(y.imag()); + double den = fabs(y.real()) + fabs(y.imag()); if (den == 0.0) x.error ("Attempted division by zero."); double xrden = x.real() / den; double xiden = x.imag() / den; @@ -64,7 +60,7 @@ Complex /* const */ operator / (const Complex& x, const Complex& y) Complex& Complex::operator /= (const Complex& y) { - double den = abs(y.real()) + abs(y.imag()); + double den = fabs(y.real()) + fabs(y.imag()); if (den == 0.0) error ("Attempted division by zero."); double xrden = re / den; double xiden = im / den; @@ -170,7 +166,7 @@ Complex /* const */ sqrt(const Complex& x) return Complex(0.0, 0.0); else { - double s = sqrt((abs(x.real()) + hypot(x.real(), x.imag())) * 0.5); + double s = sqrt((fabs(x.real()) + hypot(x.real(), x.imag())) * 0.5); double d = (x.imag() / s) * 0.5; if (x.real() > 0.0) return Complex(s, d); @@ -182,7 +178,7 @@ Complex /* const */ sqrt(const Complex& x) } -Complex /* const */ pow(const Complex& x, long p) +Complex /* const */ pow(const Complex& x, int p) { if (p == 0) return Complex(1.0, 0.0); @@ -216,33 +212,50 @@ ostream& operator << (ostream& s, const Complex& x) istream& operator >> (istream& s, Complex& x) { +#ifdef _OLD_STREAMS + if (!s.good()) + { + return s; + } +#else + if (!s.ipfx(0)) + { + s.clear(ios::failbit|s.rdstate()); // Redundant if using GNU iostreams. + return s; + } +#endif double r, i; char ch; - s >> WS; + s >> ws; s.get(ch); if (ch == '(') { s >> r; - s >> WS; + s >> ws; s.get(ch); if (ch == ',') { s >> i; - s >> WS; + s >> ws; s .get(ch); } else i = 0; if (ch != ')') - s.set(_bad); + s.clear(ios::failbit); } else { - s.unget(ch); + s.putback(ch); s >> r; i = 0; } x = Complex(r, i); return s; } - + +Complex operator * (const Complex& x, const Complex& y) +{ + return Complex(x.real() * y.real() - x.imag() * y.imag(), + x.real() * y.imag() + x.imag() * y.real()); +} diff --git a/gnu/lib/libg++/libg++/Complex.h b/gnu/lib/libg++/libg++/Complex.h new file mode 100644 index 0000000000..fecc994372 --- /dev/null +++ b/gnu/lib/libg++/libg++/Complex.h @@ -0,0 +1,270 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _Complex_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Complex_h 1 + + +#include +#include + +class Complex +{ +#ifdef __ATT_complex__ +public: +#else +protected: +#endif + + double re; + double im; + +public: + + double real() const; + double imag() const; + + Complex(); + Complex(const Complex& y); + Complex(double r, double i=0); + + ~Complex(); + + Complex& operator = (const Complex& y); + + Complex& operator += (const Complex& y); + Complex& operator += (double y); + Complex& operator -= (const Complex& y); + Complex& operator -= (double y); + Complex& operator *= (const Complex& y); + Complex& operator *= (double y); + + Complex& operator /= (const Complex& y); + Complex& operator /= (double y); + + void error(const char* msg) const; +}; + + +// non-inline functions + +Complex operator / (const Complex& x, const Complex& y); +Complex operator / (const Complex& x, double y); +Complex operator / (double x, const Complex& y); + +Complex cos(const Complex& x); +Complex sin(const Complex& x); + +Complex cosh(const Complex& x); +Complex sinh(const Complex& x); + +Complex exp(const Complex& x); +Complex log(const Complex& x); + +Complex pow(const Complex& x, int p); +Complex pow(const Complex& x, const Complex& p); +Complex pow(const Complex& x, double y); +Complex sqrt(const Complex& x); + +istream& operator >> (istream& s, Complex& x); +ostream& operator << (ostream& s, const Complex& x); + +// other functions defined as inlines + +int operator == (const Complex& x, const Complex& y); +int operator == (const Complex& x, double y); +int operator != (const Complex& x, const Complex& y); +int operator != (const Complex& x, double y); + +Complex operator - (const Complex& x); +Complex conj(const Complex& x); +Complex operator + (const Complex& x, const Complex& y); +Complex operator + (const Complex& x, double y); +Complex operator + (double x, const Complex& y); +Complex operator - (const Complex& x, const Complex& y); +Complex operator - (const Complex& x, double y); +Complex operator - (double x, const Complex& y); +Complex operator * (const Complex& x, const Complex& y); +Complex operator * (const Complex& x, double y); +Complex operator * (double x, const Complex& y); + +double real(const Complex& x); +double imag(const Complex& x); +double abs(const Complex& x); +double norm(const Complex& x); +double arg(const Complex& x); + +Complex polar(double r, double t = 0.0); + + +// inline members + +inline double Complex::real() const { return re; } +inline double Complex::imag() const { return im; } + +inline Complex::Complex() {} +inline Complex::Complex(const Complex& y) :re(y.real()), im(y.imag()) {} +inline Complex::Complex(double r, double i) :re(r), im(i) {} + +inline Complex::~Complex() {} + +inline Complex& Complex::operator = (const Complex& y) +{ + re = y.real(); im = y.imag(); return *this; +} + +inline Complex& Complex::operator += (const Complex& y) +{ + re += y.real(); im += y.imag(); return *this; +} + +inline Complex& Complex::operator += (double y) +{ + re += y; return *this; +} + +inline Complex& Complex::operator -= (const Complex& y) +{ + re -= y.real(); im -= y.imag(); return *this; +} + +inline Complex& Complex::operator -= (double y) +{ + re -= y; return *this; +} + +inline Complex& Complex::operator *= (const Complex& y) +{ + double r = re * y.real() - im * y.imag(); + im = re * y.imag() + im * y.real(); + re = r; + return *this; +} + +inline Complex& Complex::operator *= (double y) +{ + re *= y; im *= y; return *this; +} + + +// functions + +inline int operator == (const Complex& x, const Complex& y) +{ + return x.real() == y.real() && x.imag() == y.imag(); +} + +inline int operator == (const Complex& x, double y) +{ + return x.imag() == 0.0 && x.real() == y; +} + +inline int operator != (const Complex& x, const Complex& y) +{ + return x.real() != y.real() || x.imag() != y.imag(); +} + +inline int operator != (const Complex& x, double y) +{ + return x.imag() != 0.0 || x.real() != y; +} + +inline Complex operator - (const Complex& x) +{ + return Complex(-x.real(), -x.imag()); +} + +inline Complex conj(const Complex& x) +{ + return Complex(x.real(), -x.imag()); +} + +inline Complex operator + (const Complex& x, const Complex& y) +{ + return Complex(x.real() + y.real(), x.imag() + y.imag()); +} + +inline Complex operator + (const Complex& x, double y) +{ + return Complex(x.real() + y, x.imag()); +} + +inline Complex operator + (double x, const Complex& y) +{ + return Complex(x + y.real(), y.imag()); +} + +inline Complex operator - (const Complex& x, const Complex& y) +{ + return Complex(x.real() - y.real(), x.imag() - y.imag()); +} + +inline Complex operator - (const Complex& x, double y) +{ + return Complex(x.real() - y, x.imag()); +} + +inline Complex operator - (double x, const Complex& y) +{ + return Complex(x - y.real(), -y.imag()); +} + +inline Complex operator * (const Complex& x, double y) +{ + return Complex(x.real() * y, x.imag() * y); +} + +inline Complex operator * (double x, const Complex& y) +{ + return Complex(x * y.real(), x * y.imag()); +} + +inline double real(const Complex& x) +{ + return x.real(); +} + +inline double imag(const Complex& x) +{ + return x.imag(); +} + +inline double abs(const Complex& x) +{ + return hypot(x.real(), x.imag()); +} + +inline double norm(const Complex& x) +{ + return (x.real() * x.real() + x.imag() * x.imag()); +} + +inline double arg(const Complex& x) +{ + return atan2(x.imag(), x.real()); +} + +inline Complex polar(double r, double t) +{ + return Complex(r * cos(t), r * sin(t)); +} + +#endif diff --git a/gnu/lib/libg++/libg++/CursesW.cc b/gnu/lib/libg++/libg++/CursesW.cc index 29170c8084..9533f69155 100644 --- a/gnu/lib/libg++/libg++/CursesW.cc +++ b/gnu/lib/libg++/libg++/CursesW.cc @@ -1,23 +1,18 @@ /* -Copyright (C) 1989 Free Software Foundation +Copyright (C) 1989, 1992 Free Software Foundation written by Eric Newton (newton@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ #pragma implementation @@ -26,15 +21,41 @@ and this notice must be preserved on all copies. #include #include #include +#ifndef _OLD_STREAMS +#include +//#include +#endif +// Include CurseW.h and/or curses.h *after* iostream includes, +// because curses.h defines a clear macro that conflicts with iostream. Sigh. #include -extern "C" int _sscans(WINDOW*, const char*, va_list); +#if _G_HAVE_CURSES + +int CursesWindow::count = 0; /* * C++ interface to curses library. * */ +#if !defined(_IO_MAGIC) && !defined(HAVE_VSCANF) &&!defined vsscanf +extern "C" int _doscan(FILE *, const char*, va_list args); + +static int vsscanf(char *buf, const char * fmt, va_list args) +{ + FILE b; +#ifdef _IOSTRG + b._flag = _IOREAD|_IOSTRG; +#else + b._flag = _IOREAD; +#endif + b._base = (unsigned char*)buf; + b._ptr = (unsigned char*)buf; + b._cnt = BUFSIZ; + return _doscan(&b, fmt, args); +} +#endif + /* * varargs functions are handled conservatively: * They interface directly into the underlying @@ -46,28 +67,21 @@ int CursesWindow::scanw(const char * fmt, ...) { va_list args; va_start(args, fmt); -#if 1 /* bsd */ - int result = _sscans(w, fmt, args); -#else #ifdef VMS int result = wscanw(w , fmt , args); -#else +#else /* NOT VMS */ char buf[BUFSIZ]; int result = wgetstr(w, buf); if (result == OK) { -#ifndef HAVE_VSCANF - FILE b; - b._flag = _IOREAD|_IOSTRG; - b._base = buf; - b._ptr = buf; - b._cnt = BUFSIZ; - result = _doscan(&b, fmt, args); + +#ifdef _IO_MAGIC /* GNU iostreams */ + strstreambuf ss(buf, BUFSIZ); + result = ss.vscan(fmt, args); #else - result = vsscanf(buf, fmt, args); -#endif + result = vsscanf(buf, fmt, args); #endif } -#endif +#endif /* !VMS */ va_end(args); return result; } @@ -76,34 +90,24 @@ int CursesWindow::mvscanw(int y, int x, const char * fmt, ...) { va_list args; va_start(args, fmt); -#ifndef VMS char buf[BUFSIZ]; int result = wmove(w, y, x); if (result == OK) -#if 1 /* bsd */ - result = _sscans(w, fmt, args); -#else #ifdef VMS result=wscanw(w , fmt , args); -#else - { +#else /* !VMS */ + { result = wgetstr(w, buf); if (result == OK) { -#ifndef HAVE_VSCANF - FILE b; - b._flag = _IOREAD|_IOSTRG; - b._base = buf; - b._ptr = buf; - b._cnt = BUFSIZ; - result = _doscan(&b, fmt, args); +#ifdef _IO_MAGIC /* GNU iostreams */ + strstreambuf ss(buf, BUFSIZ); + result = ss.vscan(fmt, args); #else - result = vsscanf(buf, fmt, args); -#endif + result = vsscanf(buf, fmt, args); #endif - } } -#endif -#endif + } +#endif /* !VMS */ va_end(args); return result; } @@ -113,16 +117,7 @@ int CursesWindow::printw(const char * fmt, ...) va_list args; va_start(args, fmt); char buf[BUFSIZ]; -#ifndef HAVE_VPRINTF - FILE b; - b._flag = _IOWRT|_IOSTRG; - b._ptr = buf; - b._cnt = BUFSIZ; - _doprnt(fmt, args, &b); - putc('\0', &b); -#else vsprintf(buf, fmt, args); -#endif va_end(args); return waddstr(w, buf); } @@ -132,20 +127,11 @@ int CursesWindow::mvprintw(int y, int x, const char * fmt, ...) { va_list args; va_start(args, fmt); - char buf[BUFSIZ]; int result = wmove(w, y, x); if (result == OK) { -#ifndef HAVE_VPRINTF - FILE b; - b._flag = _IOWRT|_IOSTRG; - b._ptr = buf; - b._cnt = BUFSIZ; - _doprnt(fmt, args, &b); - putc('\0', &b); -#else + char buf[BUFSIZ]; vsprintf(buf, fmt, args); -#endif result = waddstr(w, buf); } va_end(args); @@ -265,3 +251,5 @@ CursesWindow::~CursesWindow() (*lib_error_handler)("CursesWindow", "Too many windows destroyed"); } } + +#endif /* _G_HAVE_CURSES */ diff --git a/gnu/lib/libg++/libg++/CursesW.h b/gnu/lib/libg++/libg++/CursesW.h new file mode 100644 index 0000000000..c152e8e6a0 --- /dev/null +++ b/gnu/lib/libg++/libg++/CursesW.h @@ -0,0 +1,580 @@ +// This may look like C code, but it is really -*- C++ -*- + +/* +Copyright (C) 1989 Free Software Foundation + written by Eric Newton (newton@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _CursesWindow_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _CursesWindow_h + +#include <_G_config.h> +#if _G_HAVE_CURSES +#include + +/* SCO 3.2v4 curses.h includes term.h, which defines lines as a macro. + Undefine it here, because CursesWindow uses lines as a method. */ +#undef lines + +// "Convert" macros to inlines, if needed. +#ifdef addch +inline int (addch)(char ch) { return addch(ch); } +#undef addch +#endif +#ifdef addstr +/* The (char*) cast is to hack around missing const's */ +inline int (addstr)(const char * str) { return addstr((char*)str); } +#undef addstr +#endif +#ifdef clear +inline int (clear)() { return clear(); } +#undef clear +#endif +#ifdef clearok +inline int (clearok)(WINDOW* win, int bf) { return clearok(win, bf); } +#undef clearok +#else +extern "C" int clearok(WINDOW*, int); +#endif +#ifdef clrtobot +inline int (clrtobot)() { return clrtobot(); } +#undef clrtobot +#endif +#ifdef clrtoeol +inline int (clrtoeol)() { return clrtoeol(); } +#undef clrtoeol +#endif +#ifdef delch +inline int (delch)() { return delch(); } +#undef delch +#endif +#ifdef deleteln +inline int (deleteln)() { return deleteln(); } +#undef deleteln +#endif +#ifdef erase +inline int (erase)() { return erase(); } +#undef erase +#endif +#ifdef flushok +inline int (flushok)(WINDOW* _win, int _bf) { return flushok(_win, _bf); } +#undef flushok +#else +#define _no_flushok +#endif +#ifdef getch +inline int (getch)() { return getch(); } +#undef getch +#endif +#ifdef getstr +inline int (getstr)(char *_str) { return getstr(_str); } +#undef getstr +#endif +#ifdef getyx +inline void (getyx)(WINDOW* win, int& y, int& x) { getyx(win, y, x); } +#undef getyx +#endif +#ifdef inch +inline int (inch)() { return inch(); } +#undef inch +#endif +#ifdef insch +inline int (insch)(char c) { return insch(c); } +#undef insch +#endif +#ifdef insertln +inline int (insertln)() { return insertln(); } +#undef insertln +#endif +#ifdef leaveok +inline int (leaveok)(WINDOW* win, int bf) { return leaveok(win, bf); } +#undef leaveok +#else +extern "C" int leaveok(WINDOW* win, int bf); +#endif +#ifdef move +inline int (move)(int x, int y) { return move(x, y); } +#undef move +#endif +#ifdef refresh +inline int (rfresh)() { return refresh(); } +#undef refresh +#endif +#ifdef scrollok +inline int (scrollok)(WINDOW* win, int bf) { return scrollok(win, bf); } +#undef scrollok +#else +#ifndef hpux +extern "C" int scrollok(WINDOW*, int); +#else +extern "C" int scrollok(WINDOW*, char); +#endif +#endif +#ifdef standend +inline int (standend)() { return standend(); } +#undef standend +#endif +#ifdef standout +inline int (standout)() { return standout(); } +#undef standout +#endif +#ifdef wstandend +inline int (wstandend)(WINDOW *win) { return wstandend(win); } +#undef wstandend +#endif +#ifdef wstandout +inline int (wstandout)(WINDOW *win) { return wstandout(win); } +#undef wstandout +#endif +#ifdef wattrset +inline int (wattrset)(WINDOW *win, int att) { return wattrset(win, att); } +#undef wattrset +#endif +#ifdef winch +inline int (winch)(WINDOW* win) { return winch(win); } +#undef winch +#endif + +#ifdef mvwaddch +inline int (mvwaddch)(WINDOW *win, int y, int x, char ch) +{ return mvwaddch(win, y, x, ch); } +#undef mvwaddch +#endif +#ifdef mvwaddstr +inline int (mvwaddstr)(WINDOW *win, int y, int x, const char * str) +{ return mvwaddstr(win, y, x, (char*)str); } +#undef mvwaddstr +#endif +#ifdef mvwdelch +inline int (mvwdelch)(WINDOW *win, int y, int x) { return mvwdelch(win, y, x);} +#undef mvwdelch +#endif +#ifdef mvwgetch +inline int (mvwgetch)(WINDOW *win, int y, int x) { return mvwgetch(win, y, x);} +#undef mvwgetch +#endif +#ifdef mvwgetstr +inline int (mvwgetstr)(WINDOW *win, int y, int x, char *str) +{return mvwgetstr(win,y,x, str);} +#undef mvwgetstr +#endif +#ifdef mvwinch +inline int (mvwinch)(WINDOW *win, int y, int x) { return mvwinch(win, y, x);} +#undef mvwinch +#endif +#ifdef mvwinsch +inline int (mvwinsch)(WINDOW *win, int y, int x, char c) +{ return mvwinsch(win, y, x, c); } +#undef mvwinsch +#endif + +#ifdef mvaddch +inline int (mvaddch)(int y, int x, char ch) +{ return mvaddch(y, x, ch); } +#undef mvaddch +#endif +#ifdef mvaddstr +inline int (mvaddstr)(int y, int x, const char * str) +{ return mvaddstr(y, x, (char*)str); } +#undef mvaddstr +#endif +#ifdef mvdelch +inline int (mvdelch)(int y, int x) { return mvdelch(y, x);} +#undef mvdelch +#endif +#ifdef mvgetch +inline int (mvgetch)(int y, int x) { return mvgetch(y, x);} +#undef mvgetch +#endif +#ifdef mvgetstr +inline int (mvgetstr)(int y, int x, char *str) {return mvgetstr(y, x, str);} +#undef mvgetstr +#endif +#ifdef mvinch +inline int (mvinch)(int y, int x) { return mvinch(y, x);} +#undef mvinch +#endif +#ifdef mvinsch +inline int (mvinsch)(int y, int x, char c) +{ return mvinsch(y, x, c); } +#undef mvinsch +#endif + +/* + * + * C++ class for windows. + * + * + */ + +class CursesWindow +{ +protected: + static int count; // count of all active windows: + // We rely on the c++ promise that + // all otherwise uninitialized + // static class vars are set to 0 + + WINDOW * w; // the curses WINDOW + + int alloced; // true if we own the WINDOW + + CursesWindow* par; // parent, if subwindow + CursesWindow* subwins; // head of subwindows list + CursesWindow* sib; // next subwindow of parent + + void kill_subwindows(); // disable all subwindows + +public: + CursesWindow(WINDOW* &window); // useful only for stdscr + + CursesWindow(int lines, // number of lines + int cols, // number of columns + int begin_y, // line origin + int begin_x); // col origin + + CursesWindow(CursesWindow& par, // parent window + int lines, // number of lines + int cols, // number of columns + int by, // absolute or relative + int bx, // origins: + char absrel = 'a'); // if `a', by & bx are + // absolute screen pos, + // else if `r', they are + // relative to par origin + ~CursesWindow(); + +// terminal status + int lines(); // number of lines on terminal, *not* window + int cols(); // number of cols on terminal, *not* window + +// window status + int height(); // number of lines in this window + int width(); // number of cols in this window + int begx(); // smallest x coord in window + int begy(); // smallest y coord in window + int maxx(); // largest x coord in window + int maxy(); // largest x coord in window + +// window positioning + int move(int y, int x); + +// coordinate positioning + void getyx(int& y, int& x); + int mvcur(int sy, int ey, int sx, int ex); + +// input + int getch(); + int getstr(char * str); + int scanw(const char *, ...); + +// input + positioning + int mvgetch(int y, int x); + int mvgetstr(int y, int x, char * str); + int mvscanw(int, int, const char*, ...); + +// output + int addch(const char ch); + int addstr(const char * str); + int printw(const char * fmt, ...); + int inch(); + int insch(char c); + int insertln(); + +// output + positioning + int mvaddch(int y, int x, char ch); + int mvaddstr(int y, int x, const char * str); + int mvprintw(int y, int x, const char * fmt, ...); + int mvinch(int y, int x); + int mvinsch(int y, int x, char ch); + +// borders + int box(char vert, char hor); + +// erasure + int erase(); + int clear(); + int clearok(int bf); + int clrtobot(); + int clrtoeol(); + int delch(); + int mvdelch(int y, int x); + int deleteln(); + +// screen control + int scroll(); + int scrollok(int bf); + int touchwin(); + int refresh(); + int leaveok(int bf); +#ifndef _no_flushok + int flushok(int bf); +#endif + int standout(); + int standend(); + +// multiple window control + int overlay(CursesWindow &win); + int overwrite(CursesWindow &win); + + +// traversal support + CursesWindow* child(); + CursesWindow* sibling(); + CursesWindow* parent(); +}; + + +inline int CursesWindow::begx() +{ + return w->_begx; +} + +inline int CursesWindow::begy() +{ + return w->_begy; +} + +inline int CursesWindow::maxx() +{ + return w->_maxx; +} + +inline int CursesWindow::maxy() +{ + return w->_maxy; +} + +inline int CursesWindow::height() +{ + return maxy() - begy() + 1; +} + +inline int CursesWindow::width() +{ + return maxx() - begx() + 1; +} + +inline int CursesWindow::box(char vert, char hor) +{ + return ::box(w, vert, hor); +} + +inline int CursesWindow::overlay(CursesWindow &win) +{ + return ::overlay(w, win.w); +} + +inline int CursesWindow::overwrite(CursesWindow &win) +{ + return ::overwrite(w, win.w); +} + +inline int CursesWindow::scroll() +{ + return ::scroll(w); +} + + +inline int CursesWindow::touchwin() +{ + return ::touchwin(w); +} + +inline int CursesWindow::addch(const char ch) +{ + return ::waddch(w, ch); +} + +inline int CursesWindow::addstr(const char * str) +{ + // The (char*) cast is to hack around prototypes in curses.h that + // have const missing in the parameter lists. [E.g. SVR4] + return ::waddstr(w, (char*)str); +} + +inline int CursesWindow::clear() +{ + return ::wclear(w); +} + +inline int CursesWindow::clrtobot() +{ + return ::wclrtobot(w); +} + +inline int CursesWindow::clrtoeol() +{ + return ::wclrtoeol(w); +} + +inline int CursesWindow::delch() +{ + return ::wdelch(w); +} + +inline int CursesWindow::deleteln() +{ + return ::wdeleteln(w); +} + +inline int CursesWindow::erase() +{ + return ::werase(w); +} + +inline int CursesWindow::getch() +{ + return ::wgetch(w); +} + +inline int CursesWindow::getstr(char * str) +{ + return ::wgetstr(w, str); +} + +inline int CursesWindow::inch() +{ + return winch(w); +} + +inline int CursesWindow::insch(char c) +{ + return ::winsch(w, c); +} + +inline int CursesWindow::insertln() +{ + return ::winsertln(w); +} + +inline int CursesWindow::move(int y, int x) +{ + return ::wmove(w, y, x); +} + + +inline int CursesWindow::mvcur(int sy, int ey, int sx, int ex) +{ + return ::mvcur(sy, ey, sx,ex); +} + +inline int CursesWindow::mvaddch(int y, int x, char ch) +{ + return (::wmove(w, y, x)==ERR) ? ERR : ::waddch(w, ch); +} + +inline int CursesWindow::mvgetch(int y, int x) +{ + return (::wmove(w, y, x)==ERR) ? ERR : ::wgetch(w); +} + +inline int CursesWindow::mvaddstr(int y, int x, const char * str) +{ + return (::wmove(w, y, x)==ERR) ? ERR : ::waddstr(w, (char*)str); +} + +inline int CursesWindow::mvgetstr(int y, int x, char * str) +{ + return (::wmove(w, y, x)==ERR) ? ERR : ::wgetstr(w, str); +} + +inline int CursesWindow::mvinch(int y, int x) +{ + return (::wmove(w, y, x)==ERR) ? ERR : ::winch(w); +} + +inline int CursesWindow::mvdelch(int y, int x) +{ + return (::wmove(w, y, x)==ERR) ? ERR : ::wdelch(w); +} + +inline int CursesWindow::mvinsch(int y, int x, char ch) +{ + return (::wmove(w, y, x)==ERR) ? ERR : ::winsch(w, ch); +} + +inline int CursesWindow::refresh() +{ + return ::wrefresh(w); +} + +inline int CursesWindow::clearok(int bf) +{ + return ::clearok(w,bf); +} + +inline int CursesWindow::leaveok(int bf) +{ + return ::leaveok(w,bf); +} + +inline int CursesWindow::scrollok(int bf) +{ + return ::scrollok(w,bf); +} + +#ifndef _no_flushok +inline int CursesWindow::flushok(int bf) +{ + return ::flushok(w, bf); +} +#endif + +inline void CursesWindow::getyx(int& y, int& x) +{ + ::getyx(w, y, x); +} + +inline int CursesWindow::standout() +{ + return ::wstandout(w); +} + +inline int CursesWindow::standend() +{ + return ::wstandend(w); +} + +inline int CursesWindow::lines() +{ + return LINES; +} + +inline int CursesWindow::cols() +{ + return COLS; +} + +inline CursesWindow* CursesWindow::child() +{ + return subwins; +} + +inline CursesWindow* CursesWindow::parent() +{ + return par; +} + +inline CursesWindow* CursesWindow::sibling() +{ + return sib; +} + +#endif /* _G_HAVE_CURSES */ +#endif diff --git a/gnu/lib/libg++/libg++/DLList.cc b/gnu/lib/libg++/libg++/DLList.cc new file mode 100644 index 0000000000..25c0be4e54 --- /dev/null +++ b/gnu/lib/libg++/libg++/DLList.cc @@ -0,0 +1,327 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _G_NO_TEMPLATES +#ifdef __GNUG__ +//#pragma implementation +#endif +#include +#include +#include +#include "DLList.h" + +void BaseDLList::error(const char* msg) +{ + (*lib_error_handler)("DLList", msg); +} + +int BaseDLList::length() +{ + int l = 0; + BaseDLNode* t = h; + if (t != 0) do { ++l; t = t->fd; } while (t != h); + return l; +} + +// Note: This is an internal method. It does *not* free old contents! + +void BaseDLList::copy(const BaseDLList& a) +{ + if (a.h == 0) + h = 0; + else + { + BaseDLNode* p = a.h; + BaseDLNode* t = copy_node(p->item()); + h = t; + p = p->fd; + while (p != a.h) + { + BaseDLNode* n = copy_node(p->item()); + t->fd = n; + n->bk = t; + t = n; + p = p->fd; + } + t->fd = h; + h->bk = t; + return; + } +} + +void BaseDLList::clear() +{ + if (h == 0) + return; + + BaseDLNode* p = h->fd; + h->fd = 0; + h = 0; + + while (p != 0) + { + BaseDLNode* nxt = p->fd; + delete_node(p); + p = nxt; + } +} + +BaseDLList& BaseDLList::operator = (const BaseDLList& a) +{ + if (h != a.h) + { + clear(); + copy(a); + } + return *this; +} + + +Pix BaseDLList::prepend(void *datum) +{ + BaseDLNode* t = copy_node(datum); + if (h == 0) + t->fd = t->bk = h = t; + else + { + t->fd = h; + t->bk = h->bk; + h->bk->fd = t; + h->bk = t; + h = t; + } + return Pix(t); +} + +Pix BaseDLList::append(void *datum) +{ + BaseDLNode* t = copy_node(datum); + if (h == 0) + t->fd = t->bk = h = t; + else + { + t->bk = h->bk; + t->bk->fd = t; + t->fd = h; + h->bk = t; + } + return Pix(t); +} + +Pix BaseDLList::ins_after(Pix p, void *datum) +{ + if (p == 0) return prepend(datum); + BaseDLNode* u = (BaseDLNode*) p; + BaseDLNode* t = copy_node(datum); + t->bk = u; + t->fd = u->fd; + u->fd->bk = t; + u->fd = t; + return Pix(t); +} + +Pix BaseDLList::ins_before(Pix p, void *datum) +{ + if (p == 0) error("null Pix"); + BaseDLNode* u = (BaseDLNode*) p; + BaseDLNode* t = copy_node(datum); + t->bk = u->bk; + t->fd = u; + u->bk->fd = t; + u->bk = t; + if (u == h) h = t; + return Pix(t); +} + +void BaseDLList::join(BaseDLList& b) +{ + BaseDLNode* t = b.h; + b.h = 0; + if (h == 0) + h = t; + else if (t != 0) + { + BaseDLNode* l = t->bk; + h->bk->fd = t; + t->bk = h->bk; + h->bk = l; + l->fd = h; + } +} + +int BaseDLList::owns(Pix p) +{ + BaseDLNode* t = h; + if (t != 0 && p != 0) + { + do + { + if (Pix(t) == p) return 1; + t = t->fd; + } while (t != h); + } + return 0; +} + +void BaseDLList::del(Pix& p, int dir) +{ + if (p == 0) error("null Pix"); + BaseDLNode* t = (BaseDLNode*) p; + if (t->fd == t) + { + h = 0; + p = 0; + } + else + { + if (dir < 0) + { + if (t == h) + p = 0; + else + p = Pix(t->bk); + } + else + { + if (t == h->bk) + p = 0; + else + p = Pix(t->fd); + } + t->bk->fd = t->fd; + t->fd->bk = t->bk; + if (t == h) h = t->fd; + } + delete t; +} + +void BaseDLList::del_after(Pix& p) +{ + if (p == 0) + { + del_front(); + return; + } + + BaseDLNode* b = (BaseDLNode*) p; + BaseDLNode* t = b->fd; + + if (b == t) + { + h = 0; + p = 0; + } + else + { + t->bk->fd = t->fd; + t->fd->bk = t->bk; + if (t == h) h = t->fd; + } + delete_node(t); +} + +void BaseDLList::remove_front(void *dst) +{ + if (h == 0) + error("remove_front of empty list"); + else { + BaseDLNode* t = h; + copy_item(dst, t->item()); + if (h->fd == h) + h = 0; + else + { + h->fd->bk = h->bk; + h->bk->fd = h->fd; + h = h->fd; + } + delete_node(t); + } +} + +void BaseDLList::del_front() +{ + if (h == 0) + error("del_front of empty list"); + BaseDLNode* t = h; + if (h->fd == h) + h = 0; + else + { + h->fd->bk = h->bk; + h->bk->fd = h->fd; + h = h->fd; + } + delete_node(t); +} + +void BaseDLList::remove_rear(void *dst) +{ + if (h == 0) + error("remove_rear of empty list"); + else + { + BaseDLNode* t = h->bk; + copy_item(dst, t->item()); + if (h->fd == h) + h = 0; + else + { + t->fd->bk = t->bk; + t->bk->fd = t->fd; + } + delete_node(t); + } +} + +void BaseDLList::del_rear() +{ + if (h == 0) + error("del_rear of empty list"); + BaseDLNode* t = h->bk; + if (h->fd == h) + h = 0; + else + { + t->fd->bk = t->bk; + t->bk->fd = t->fd; + } + delete_node(t); +} + + +int BaseDLList::OK() +{ + int v = 1; + if (h != 0) + { + BaseDLNode* t = h; + long count = MAXLONG; // Lots of chances to find h! + do + { + count--; + v &= t->bk->fd == t; + v &= t->fd->bk == t; + t = t->fd; + } while (v && count > 0 && t != h); + v &= count > 0; + } + if (!v) error("invariant failure"); + return v; +} +#endif diff --git a/gnu/lib/libg++/libg++/DLList.h b/gnu/lib/libg++/libg++/DLList.h new file mode 100644 index 0000000000..0f027d7263 --- /dev/null +++ b/gnu/lib/libg++/libg++/DLList.h @@ -0,0 +1,124 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _DLList_h +#ifdef __GNUG__ +//#pragma interface +#endif +#define _DLList_h 1 + +#include + +struct BaseDLNode { + BaseDLNode *bk; + BaseDLNode *fd; + void *item() {return (void*)(this+1);} //Return ((DLNode*)this)->hd +}; + +template +class DLNode : public BaseDLNode +{ + public: + T hd; + DLNode() { } + DLNode(const T& h, DLNode* p = 0, DLNode* n = 0) + : hd(h) { bk = p; fd = n; } + ~DLNode() { } +}; + +class BaseDLList { + protected: + BaseDLNode *h; + + BaseDLList() { h = 0; } + void copy(const BaseDLList&); + BaseDLList& operator= (const BaseDLList& a); + virtual void delete_node(BaseDLNode*node) = 0; + virtual BaseDLNode* copy_node(void* datum) = 0; + virtual void copy_item(void *dst, void *src) = 0; + virtual ~BaseDLList() { } + + Pix prepend(void*); + Pix append(void*); + Pix ins_after(Pix p, void *datum); + Pix ins_before(Pix p, void *datum); + void remove_front(void *dst); + void remove_rear(void *dst); + void join(BaseDLList&); + + public: + int empty() { return h == 0; } + int length(); + void clear(); + void error(const char* msg); + int owns(Pix p); + int OK(); + void del(Pix& p, int dir = 1); + void del_after(Pix& p); + void del_front(); + void del_rear(); +}; + +template +class DLList : public BaseDLList { + //friend class DLListTrav; + + virtual void delete_node(BaseDLNode *node) { delete (DLNode*)node; } + virtual BaseDLNode* copy_node(void *datum) + { return new DLNode(*(T*)datum); } + virtual void copy_item(void *dst, void *src) { *(T*)dst = *(T*)src; } + + public: + DLList() : BaseDLList() { } + DLList(const DLList& a) : BaseDLList() { copy(a); } + + DLList& operator = (const DLList& a) + { BaseDLList::operator=((const BaseDLList&) a); return *this; } + virtual ~DLList() { clear(); } + + Pix prepend(T& item) {return BaseDLList::prepend(&item);} + Pix append(T& item) {return BaseDLList::append(&item);} + + void join(DLList& a) { BaseDLList::join(a); } + + T& front() { + if (h == 0) error("front: empty list"); + return ((DLNode*)h)->hd; } + T& rear() { + if (h == 0) error("rear: empty list"); + return ((DLNode*)h->bk)->hd; + } + T remove_front() { T dst; BaseDLList::remove_front(&dst); return dst; } + T remove_rear() { T dst; BaseDLList::remove_rear(&dst); return dst; } + + T& operator () (Pix p) { + if (p == 0) error("null Pix"); + return ((DLNode*)p)->hd; + } + Pix first() { return Pix(h); } + Pix last() { return (h == 0)? 0 : Pix(h->bk); } + void next(Pix& p) + { p = (p == 0 || p == h->bk)? 0 : Pix(((DLNode*)p)->fd); } + void prev(Pix& p) + { p = (p == 0 || p == h)? 0 : Pix(((DLNode*)p)->bk); } + Pix ins_after(Pix p, T& item) {return BaseDLList::ins_after(p, &item); } + Pix ins_before(Pix p, T& item) {return BaseDLList::ins_before(p, &item);} +}; + +#endif diff --git a/gnu/lib/libg++/libg++/DiscUnif.cc b/gnu/lib/libg++/libg++/DiscUnif.cc index ebd2ef5fe9..136ad11aba 100644 --- a/gnu/lib/libg++/libg++/DiscUnif.cc +++ b/gnu/lib/libg++/libg++/DiscUnif.cc @@ -2,29 +2,23 @@ Copyright (C) 1988 Free Software Foundation written by Dirk Grunwald (grunwald@cs.uiuc.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ #pragma implementation #endif #include #include - #include double DiscreteUniform::operator()() diff --git a/gnu/lib/libg++/libg++/DiscUnif.h b/gnu/lib/libg++/libg++/DiscUnif.h new file mode 100644 index 0000000000..5f26f90b95 --- /dev/null +++ b/gnu/lib/libg++/libg++/DiscUnif.h @@ -0,0 +1,72 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef _DiscreteUniform_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _DiscreteUniform_h 1 + +#include + +// +// The interval [lo..hi) +// + +class DiscreteUniform: public Random { + long pLow; + long pHigh; + double delta; +public: + DiscreteUniform(long low, long high, RNG *gen); + + long low(); + long low(long x); + long high(); + long high(long x); + + virtual double operator()(); +}; + + +inline DiscreteUniform::DiscreteUniform(long low, long high, RNG *gen) +: Random(gen) +{ + pLow = (low < high) ? low : high; + pHigh = (low < high) ? high : low; + delta = (pHigh - pLow) + 1; +} + +inline long DiscreteUniform::low() { return pLow; } + +inline long DiscreteUniform::low(long x) { + long tmp = pLow; + pLow = x; + delta = (pHigh - pLow) + 1; + return tmp; +} + +inline long DiscreteUniform::high() { return pHigh; } + +inline long DiscreteUniform::high(long x) { + long tmp = pHigh; + pHigh = x; + delta = (pHigh - pLow) + 1; + return tmp; +} + +#endif diff --git a/gnu/lib/libg++/libg++/EH.cc b/gnu/lib/libg++/libg++/EH.cc deleted file mode 100644 index 9440bd3b44..0000000000 --- a/gnu/lib/libg++/libg++/EH.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* Library code for programs which use -fhandle-exceptions. - Note: do *not* compile this with -fhandle-exceptions. */ - - -#ifdef __GNUG__ -#pragma implementation -#endif -#include -#include - -struct -ExceptionHandler -{ - ExceptionHandler *prev; - jmp_buf handler; - void *name; - void *parameters; - ExceptionHandler (); - ~ExceptionHandler (); -} EHS, *exceptionHandlerStack = &EHS; - -ExceptionHandler::ExceptionHandler () -{ - if (this == &EHS) - { - if (setjmp (EHS.handler)) - { - cerr << ("unhandled exception, aborting...\n"); - abort (); - } - } - else - { - this->prev = exceptionHandlerStack; - exceptionHandlerStack = this; - } -} - -ExceptionHandler::~ExceptionHandler () -{ - exceptionHandlerStack = this->prev; -} - diff --git a/gnu/lib/libg++/libg++/EH2.c b/gnu/lib/libg++/libg++/EH2.c deleted file mode 100644 index 52c91f408d..0000000000 --- a/gnu/lib/libg++/libg++/EH2.c +++ /dev/null @@ -1,20 +0,0 @@ -/* -Unhandled exceptions cause the program to abort. -Argument FILENAME is the name of the file that caught the exception. -Argument LINENO is the line number at which the exception was -caught. -*/ - -extern volatile void abort(); - -void -__unhandled_exception (char *filename, int lineno) -{ - abort (); -} - -void -__raise_exception (void **addr, void *id) -{ - *addr = id; -} diff --git a/gnu/lib/libg++/libg++/Erlang.cc b/gnu/lib/libg++/libg++/Erlang.cc index f93c44b463..da3e4e7bd0 100644 --- a/gnu/lib/libg++/libg++/Erlang.cc +++ b/gnu/lib/libg++/libg++/Erlang.cc @@ -2,29 +2,23 @@ Copyright (C) 1988 Free Software Foundation written by Dirk Grunwald (grunwald@cs.uiuc.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ #pragma implementation #endif #include #include - #include double Erlang::operator()() diff --git a/gnu/lib/libg++/libg++/Erlang.h b/gnu/lib/libg++/libg++/Erlang.h new file mode 100644 index 0000000000..98093884f7 --- /dev/null +++ b/gnu/lib/libg++/libg++/Erlang.h @@ -0,0 +1,68 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef _Erlang_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Erlang_h 1 + +#include + +class Erlang: public Random { +protected: + double pMean; + double pVariance; + int k; + double a; + void setState(); +public: + Erlang(double mean, double variance, RNG *gen); + + double mean(); + double mean(double x); + double variance(); + double variance(double x); + + virtual double operator()(); + +}; + + +inline void Erlang::setState() { + k = int( (pMean * pMean ) / pVariance + 0.5 ); + k = (k > 0) ? k : 1; + a = k / pMean; +} + +inline Erlang::Erlang(double mean, double variance, RNG *gen) : Random(gen) +{ + pMean = mean; pVariance = variance; + setState(); +} + +inline double Erlang::mean() { return pMean; } +inline double Erlang::mean(double x) { + double tmp = pMean; pMean = x; setState(); return tmp; +}; + +inline double Erlang::variance() { return pVariance; } +inline double Erlang::variance(double x) { + double tmp = pVariance; pVariance = x; setState(); return tmp; +} + +#endif diff --git a/gnu/lib/libg++/libg++/File.cc b/gnu/lib/libg++/libg++/File.cc deleted file mode 100644 index 30ad65057d..0000000000 --- a/gnu/lib/libg++/libg++/File.cc +++ /dev/null @@ -1,481 +0,0 @@ -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifdef __GNUG__ -#pragma implementation -#endif -#include -#include -#include -#include -#include // needed to determine values of O_RDONLY... -#include - -#ifdef VMS -#include // needed to get the psect magic loaded -#define FP (*fp) -#else -#define FP fp -#endif - - -// error handlers - -void verbose_File_error_handler(const char* msg) -{ - perror(msg); - errno = 0; -} - -void quiet_File_error_handler(const char*) -{ - errno = 0; -} - -void fatal_File_error_handler(const char* msg) -{ - perror(msg); - exit(1); -} - -one_arg_error_handler_t File_error_handler = verbose_File_error_handler; - - -one_arg_error_handler_t set_File_error_handler(one_arg_error_handler_t f) -{ - one_arg_error_handler_t old = File_error_handler; - File_error_handler = f; - return old; -} - - - -void File::initialize() -{ - fp = 0; nm = 0; stat = 0; state = _bad; rw = 0; -} - -// reset class vars after open -// fp->_flag inspection is isolated here - -void File::reinitialize(const char* filename) -{ - if (filename != 0) setname(filename); - else if (fp == stdin) setname("(stdin)"); - else if (fp == stdout) setname("(stdout)"); - else if (fp == stderr) setname("(stderr)"); - else if (rw & 4) setname("(string)"); - else setname(0); - - if (fp != 0) - { - state = _good; -#if 1 /* bsd 4.4 */ - if (FP->_flags & (__SRD|__SRW)) - rw |= 01; - if (FP->_flags & (__SWR|__SRW|__SAPP)) - rw |= 02; -#else - if (FP->_flag & (_IOREAD|_IORW)) - rw |= 01; - if (FP->_flag & (_IOWRT|_IORW|_IOAPPEND)) - rw |= 02; -#endif - check_state(); - } - else - { - set(_fail); set(_bad); - error(); - } -} - - -File& File::open(const char* filename, io_mode m, access_mode a) -{ - close(); - int open_arg = open_cmd_arg(m, a); - if (open_arg != -1) - { - int fd = ::open(filename, open_arg, 0666); - if (fd >= 0) - fp = fdopen(fd, fopen_cmd_arg(m)); - } - reinitialize(filename); - return *this; -} - -File& File::open(const char* filename, const char* m) -{ - close(); - fp = fopen(filename, m); - reinitialize(filename); - return *this; -} - -File& File::open(FILE* fileptr) -{ - close(); - fp = fileptr; - reinitialize(0); - return *this; -} - -File& File::open(int filedesc, io_mode m) -{ - close(); - fp = fdopen(filedesc, fopen_cmd_arg(m)); - reinitialize(0); - return *this; -} - -File& File::close() -{ - if (fp != 0) - { -#ifdef VMS - if (rw & 4) // we own the iobuf, kill it - delete(*fp); // kill the _iobuf -#endif - if (rw & 4) // we own the iobuf, kill it - delete fp; - else if (fp == stdin || fp == stdout || fp == stderr) - flush(); - else - fclose(fp); - } - fp = 0; - rw = 0; - set(_bad); - return *this; -} - -File& File::remove() -{ - close(); - return failif (nm == 0 || unlink(nm) != 0); -} - - -File::File() -{ - initialize(); -} - -File::File(const char* filename, io_mode m, access_mode a) -{ - initialize(); - open(filename, m, a); -} - -File::File(const char* filename, const char* m) -{ - initialize(); - open(filename, m); -} - -File::File(int filedesc, io_mode m) -{ - initialize(); - open(filedesc, m); -} - -File::File(FILE* fileptr) -{ - initialize(); - open(fileptr); -} - - -File::~File() -{ - delete(nm); - close(); -} - -void File::setname(const char* newname) -{ - if (nm == newname) return; - - if (nm != 0) - delete(nm); - if (newname != 0) - { - nm = new char[strlen(newname) + 1]; - strcpy(nm, newname); - } - else - nm = 0; -} - - -File& File::setbuf(int buffer_kind) -{ - if (!is_open()) - { - set(_fail); - return *this; - } - switch(buffer_kind) - { - case _IOFBF: -#ifdef HAVE_SETVBUF - setvbuf(fp, 0, _IOFBF, 0); -#endif - break; - case _IONBF: - ::setbuf(fp, 0); - break; - case _IOLBF: -#ifdef HAVE_SETLINEBUF - setlinebuf(fp); -#else -#ifdef HAVE_SETVBUF - setvbuf(fp, 0, _IOLBF, 0); -#endif -#endif - break; - default: - break; - } - return *this; -} - -File& File::setbuf(int size, char* buf) -{ - if (!is_open()) - { - set(_fail); - return *this; - } -#ifdef HAVE_SETVBUF - setvbuf(fp, buf, _IOFBF, size); -#else - setbuffer(fp, buf, size); -#endif - return *this; -} - -void File::error() -{ - check_state(); - set(_fail); - if (errno != 0) - { - char error_string[400]; - strcpy(error_string, "\nerror in File "); - if (nm != 0) - strcat(error_string, nm); - (*File_error_handler)(error_string); - } -} - - -//------------------------------------------------------------------ - -void File::check_state() // ensure fp & state agree about eof -{ - if (fp != 0) - { - if (feof(fp)) - set(_eof); - else - unset(_eof); - if (ferror(fp)) - set(_bad); - } -} - -File& File::put(const char* s) -{ - return failif(!writable() || fputs(s, fp) == EOF); -} - -File& File::get(char* s, int n, char terminator) -{ - if (!readable()) - { - set(_fail); - return *this; - } - - char ch; - stat = --n; - - if (n > 0 && (get(ch))) - { - if (ch == terminator) { - unget(ch); - stat= 0; // This is not an error condition ! - } - else - { - *s++ = ch; --n; - while (n > 0 && (get(ch))) - { - if (ch == terminator) - { - unget(ch); - break; - } - else - { - *s++ = ch; --n; - } - } - } - } - - *s = 0; - return failif((stat != 0) && ((stat -= n) == 0)); -} - -File& File::getline(char* s, int n, char terminator) -{ - if (!readable()) - { - set(_fail); - return *this; - } - - char ch; - stat = --n; - - while (n > 0 && (get(ch))) - { - --n; - if ((*s++ = ch) == terminator) - break; - } - - *s = 0; - return failif((stat != 0) && ((stat -= n) == 0)); -} - -// from Doug Schmidt - -// This should probably be a page size.... -#define CHUNK_SIZE 512 - -/* Reads an arbitrarily long input line terminated by a user-specified - TERMINATOR. Super-nifty trick using recursion avoids unnecessary calls - to NEW! */ - -char *File::readline (int chunk_number, char terminator) -{ - char buf[CHUNK_SIZE]; - register char *bufptr = buf; - register char *ptr; - char ch; - int continu; - - while ((continu = !!get(ch)) && ch != terminator) /* fill the current buffer */ - { - *bufptr++ = ch; - if (bufptr - buf >= CHUNK_SIZE) /* prepend remainder to ptr buffer */ - { - if (ptr = readline (chunk_number + 1, terminator)) - - for (; bufptr != buf; *--ptr = *--bufptr); - - return ptr; - } - } - if (!continu && bufptr == buf) - return NULL; - - int size = (chunk_number * CHUNK_SIZE + bufptr - buf) + 1; - - if (ptr = new char[stat = size]) - { - - for (*(ptr += (size - 1)) = '\0'; bufptr != buf; *--ptr = *--bufptr) - ; - - return ptr; - } - else - return NULL; -} - -/* Reads an arbitrarily long input line terminated by TERMINATOR. - This routine allocates its own memory, so the user should - only supply the address of a (char *). */ - -File& File::gets(char **s, char terminator) -{ - if (!readable()) - { - set(_fail); - return *this; - } - - return failif(!(*s = readline (0, terminator))); -} - -#ifndef VMS -File& File::scan(const char* fmt ...) -{ - if (readable()) - { - va_list args; - va_start(args, fmt); -#ifndef HAVE_VSCANF - stat = _doscan(fp, fmt, args); -#else - stat = vfscanf(fp, fmt, args); -#endif - va_end(args); - failif(stat <= 0); - } - return *this; -} -#endif - -File& File::form(const char* fmt ...) -{ - va_list args; - va_start(args, fmt); -#ifndef HAVE_VPRINTF - stat = _doprnt(fmt, args, fp); -#ifdef HAVE_VOID_DOPRNT - stat = ferror(fp) ? -1 : 0; -#endif -#else - stat = vfprintf(fp, fmt, args); -#endif - va_end(args); - failif(stat < 0); - return *this; -} - -#ifdef VMS -extern "C" { - unlink(const char *s) - { - int remove(const char *); - - return remove(s); - } -} -#endif - diff --git a/gnu/lib/libg++/libg++/Filebuf.cc b/gnu/lib/libg++/libg++/Filebuf.cc deleted file mode 100644 index e8f331c226..0000000000 --- a/gnu/lib/libg++/libg++/Filebuf.cc +++ /dev/null @@ -1,25 +0,0 @@ -/* -Copyright (C) 1990 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifdef __GNUG__ -#pragma implementation -#endif diff --git a/gnu/lib/libg++/libg++/Fix.cc b/gnu/lib/libg++/libg++/Fix.cc index 5f724fb987..b2037b3033 100644 --- a/gnu/lib/libg++/libg++/Fix.cc +++ b/gnu/lib/libg++/libg++/Fix.cc @@ -1,3 +1,20 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1989 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ // // Fix.cc : variable length fixed point data type class functions // @@ -9,6 +26,7 @@ #include #include #include +#include // default parameters @@ -71,7 +89,7 @@ static inline _Fix _new_Fix(uint16 len) if (siz <= 0) siz = 1; unsigned int allocsiz = (sizeof(_Frep) + (siz - 1) * sizeof(uint16)); _Fix z = (_Fix)(new char[allocsiz]); - bzero(z, allocsiz); + memset(z, 0, allocsiz); z->len = len; z->siz = siz; z->ref = 1; @@ -83,7 +101,7 @@ _Fix new_Fix(uint16 len) return _new_Fix(len); } -_Fix new_Fix(uint16 len, _Fix x) +_Fix new_Fix(uint16 len, const _Fix x) { _Fix z = _new_Fix(len); return copy(x,z); @@ -121,7 +139,7 @@ _Fix new_Fix(uint16 len, double d) // convert to a double -double value(Fix& x) +double value(const Fix& x) { double d = 0.0; for ( int i=x.rep->siz-1; i >= 0; i-- ) @@ -140,7 +158,8 @@ Integer mantissa(Fix& x) Integer a = 1, b=1; for ( int i=0; i < x.rep->siz; i++ ) { - a = (a << 16) + x.rep->s[i]; + a <<= 16; + a += x.rep->s[i]; b <<= 16; } return a-b; @@ -163,7 +182,7 @@ inline static int docmpz(uint16* x, int siz) return 0; } -int compare(_Fix x, _Fix y) +int compare(const _Fix x, const _Fix y) { if ( x->siz == y->siz ) return docmp(x->s, y->s, x->siz); @@ -274,7 +293,7 @@ _Fix multiply(_Fix x, _Fix y, _Fix r) b += r->s[k]; r->s[k] = b; } - if ( k < r->siz + 1 ) + if ( k < (int)r->siz + 1 ) carry = (a >> 15) + (b >> 16); } r->s[i] = carry; @@ -327,7 +346,7 @@ _Fix divide(_Fix x, _Fix y, _Fix q, _Fix r) if ( !compare(y) ) (*Fix_range_error_handler)("division -- division by zero"); else if ( compare(x,y) >= 0 ) - if ( compare(x,y) == 0 && xsign ^ ysign != 0 ) + if ( compare(x,y) == 0 && (xsign ^ ysign) != 0 ) { copy(&_Frep_m1,q); copy(&_Frep_0,r); @@ -434,18 +453,29 @@ Fix atoF(const char* a, int len) extern AllocRing _libgxx_fmtq; +void Fix::printon(ostream& s, int width) const +{ + char format[20]; + double val = value(*this); + int old_precision = s.precision(width-3); + long old_flags = s.setf(ios::fixed, ios::fixed|ios::scientific); + if (val >= 0) + s << ' '; + s.width(width-2); + s << val; + s.precision(old_precision); + s.flags(old_flags); +} + char* Ftoa(Fix& x, int width) { int wrksiz = width + 2; - char *s = (char *) _libgxx_fmtq.alloc(wrksiz); - char format[100]; - double val = value(x); - if (val < 0) - sprintf(format,"%%%d.%dlf",width-2,width-3); - else - sprintf(format," %%%d.%dlf",width-2,width-3); - sprintf(s,format,val); - return s; + char *fmtbase = (char *) _libgxx_fmtq.alloc(wrksiz); + ostrstream stream(fmtbase, wrksiz); + + x.printon(stream, width); + stream << ends; + return fmtbase; } extern Obstack _libgxx_io_ob; @@ -458,18 +488,18 @@ Fix Fix::operator %= (int y) istream& operator >> (istream& s, Fix& y) { int got_one = 0; - if (!s.readable()) + if (!s.ipfx(0)) { - s.set(_bad); + s.clear(ios::failbit|s.rdstate()); // Redundant if using GNU iostreams. return s; } char sign = 0, point = 0; char ch; - s >> WS; + s >> ws; if (!s.good()) { - s.set(_bad); + s.clear(ios::failbit|s.rdstate()); return s; } while (s.get(ch)) @@ -504,9 +534,9 @@ istream& operator >> (istream& s, Fix& y) } char * p = (char*)(_libgxx_io_ob.finish(0)); if (s.good()) - s.unget(ch); + s.putback(ch); if (!got_one) - s.error(); + s.clear(ios::failbit|s.rdstate()); else y = atoF(p); _libgxx_io_ob.free(p); @@ -518,7 +548,16 @@ void show(Fix& x) cout << "len = " << x.rep->len << "\n"; cout << "siz = " << x.rep->siz << "\n"; cout << "ref = " << x.rep->ref << "\n"; - cout << "man = " << Itoa(mantissa(x),16,4*x.rep->siz) << "\n"; + cout << "man = "; +#ifdef _OLD_STREAMS + cout << Itoa(mantissa(x),16,4*x.rep->siz); +#else + int old_flags = cout.setf(ios::hex, ios::hex|ios::dec|ios::oct); + cout.width(4*x.rep->siz); + cout << mantissa(x); + cout.setf(old_flags, ios::hex|ios::dec|ios::oct); +#endif + cout << "\n"; cout << "val = " << value(x) << "\n"; } @@ -551,24 +590,24 @@ void Fix_overflow_saturate(_Fix& r) { else { r->s[0] = 0x7fff; - for ( int i=1; i < r->siz; i++ ) + for ( int i = 1; i < (int)r->siz; i++ ) r->s[i] = 0xffff; mask(r); } } -void Fix_overflow_wrap(_Fix& r) {} +void Fix_overflow_wrap(_Fix&) {} void Fix_overflow_warning_saturate(_Fix& r) { Fix_overflow_warning(r); Fix_overflow_saturate(r); } -void Fix_overflow_warning(_Fix& r) { +void Fix_overflow_warning(_Fix&) { cerr << "Fix: overflow warning\n"; } -void Fix_overflow_error(_Fix& r) { +void Fix_overflow_error(_Fix&) { cerr << "Fix: overflow error\n"; abort(); } diff --git a/gnu/lib/libg++/libg++/Fix.h b/gnu/lib/libg++/libg++/Fix.h new file mode 100644 index 0000000000..7b1cf1d4a9 --- /dev/null +++ b/gnu/lib/libg++/libg++/Fix.h @@ -0,0 +1,469 @@ +// +// Fix.h : variable length fixed point data type +// + +#ifndef _Fix_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Fix_h 1 + +#include +#include +#include +#include +#include + +typedef unsigned short uint16; +typedef short int16; +typedef unsigned long uint32; +typedef long int32; + +#define _Fix_min_length 1 +#define _Fix_max_length 65535 + +#define _Fix_min_value -1.0 +#define _Fix_max_value 1.0 + +extern uint16 Fix_default_length; +extern int Fix_default_print_width; + +struct _Frep // internal Fix representation +{ + uint16 len; // length in bits + uint16 siz; // allocated storage + int16 ref; // reference count + uint16 s[1]; // start of ushort array represention +}; + +typedef _Frep* _Fix; + +extern _Frep _Frep_0; +extern _Frep _Frep_m1; +extern _Frep _Frep_quotient_bump; + +class Fix +{ + _Fix rep; + + Fix(_Fix); + + void unique(); + +public: + Fix(); + Fix(Fix&); + Fix(double); + Fix(int); + Fix(int, const Fix&); + Fix(int, double); + Fix(int, const _Fix); + + ~Fix(); + + Fix operator = (Fix&); + Fix operator = (double); + + friend int operator == (const Fix&, const Fix& ); + friend int operator != (const Fix&, const Fix&); + + friend int operator < (const Fix&, const Fix&); + friend int operator <= (const Fix&, const Fix&); + friend int operator > (const Fix&, const Fix&); + friend int operator >= (const Fix&, const Fix&); + + Fix& operator + (); + Fix operator - (); + + friend Fix operator + (Fix&, Fix&); + friend Fix operator - (Fix&, Fix&); + friend Fix operator * (Fix&, Fix&); + friend Fix operator / (Fix&, Fix&); + + friend Fix operator * (Fix&, int); + friend Fix operator * (int, Fix&); + friend Fix operator % (Fix&, int); + friend Fix operator << (Fix&, int); + friend Fix operator >> (Fix&, int); + +#ifdef __GNUG__ + friend Fix operator ? (Fix&, Fix&); // max +#endif + + Fix operator += (Fix&); + Fix operator -= (Fix&); + Fix operator *= (Fix&); + Fix operator /= (Fix&); + + Fix operator *= (int); + Fix operator %= (int); + Fix operator <<=(int); + Fix operator >>=(int); + + friend char* Ftoa(Fix&, int width = Fix_default_print_width); + void printon(ostream&, int width = Fix_default_print_width) const; + friend Fix atoF(const char*, int len = Fix_default_length); + + friend istream& operator >> (istream&, Fix&); + friend ostream& operator << (ostream&, const Fix&); + + // built-in functions + friend Fix abs(Fix); // absolute value + friend int sgn(Fix&); // -1, 0, +1 + friend Integer mantissa(Fix&); // integer representation + friend double value(const Fix&); // double value + friend int length(const Fix&); // field length + friend void show(Fix&); // show contents + + // error handlers + void error(const char* msg); // error handler + void range_error(const char* msg); // range error handler + + // internal class functions + friend void mask(_Fix); + friend int compare(const _Fix, const _Fix = &_Frep_0); + + friend _Fix new_Fix(uint16); + friend _Fix new_Fix(uint16, const _Fix); + friend _Fix new_Fix(uint16, double); + + friend _Fix copy(const _Fix, _Fix); + friend _Fix negate(_Fix, _Fix = NULL); + friend _Fix add(_Fix, _Fix, _Fix = NULL); + friend _Fix subtract(_Fix, _Fix, _Fix = NULL); + friend _Fix multiply(_Fix, _Fix, _Fix = NULL); + friend _Fix multiply(_Fix, int, _Fix = NULL); + friend _Fix divide(_Fix, _Fix, _Fix = NULL, _Fix = NULL); + friend _Fix shift(_Fix, int, _Fix = NULL); + + // non-operator versions for user + friend void negate(Fix& x, Fix& r); + friend void add(Fix& x, Fix& y, Fix& r); + friend void subtract(Fix& x, Fix& y, Fix& r); + friend void multiply(Fix& x, Fix& y, Fix& r); + friend void divide(Fix& x, Fix& y, Fix& q, Fix& r); + friend void shift(Fix& x, int y, Fix& r); +}; + +// error handlers + +extern void + default_Fix_error_handler(const char*), + default_Fix_range_error_handler(const char*); + +extern one_arg_error_handler_t + Fix_error_handler, + Fix_range_error_handler; + +extern one_arg_error_handler_t + set_Fix_error_handler(one_arg_error_handler_t f), + set_Fix_range_error_handler(one_arg_error_handler_t f); + +typedef void (*Fix_peh)(_Fix&); +extern Fix_peh Fix_overflow_handler; + +extern void + Fix_overflow_saturate(_Fix&), + Fix_overflow_wrap(_Fix&), + Fix_overflow_warning_saturate(_Fix&), + Fix_overflow_warning(_Fix&), + Fix_overflow_error(_Fix&); + +extern Fix_peh set_overflow_handler(Fix_peh); + +extern int Fix_set_default_length(int); + +// function definitions + + +inline void Fix::unique() +{ + if ( rep->ref > 1 ) + { + rep->ref--; + rep = new_Fix(rep->len,rep); + } +} + +inline void mask (_Fix x) +{ + int n = x->len & 0x0f; + if ( n ) + x->s[x->siz - 1] &= 0xffff0000 >> n; +} + +inline _Fix copy(const _Fix from, _Fix to) +{ + uint16 *ts = to->s, *fs = from->s; + int ilim = to->siz < from->siz ? to->siz : from->siz; + for ( int i=0; i < ilim; i++ ) + *ts++ = *fs++; + for ( ; i < to->siz; i++ ) + *ts++ = 0; + mask(to); + return to; +} + +inline Fix::Fix(_Fix f) +{ + rep = f; +} + +inline Fix::Fix() +{ + rep = new_Fix(Fix_default_length); +} + +inline Fix::Fix(int len) +{ + if ( len < _Fix_min_length || len > _Fix_max_length ) + error("illegal length in declaration"); + rep = new_Fix((uint16 )len); +} + +inline Fix::Fix(double d) +{ + rep = new_Fix(Fix_default_length,d); +} + +inline Fix::Fix(Fix& y) +{ + rep = y.rep; rep->ref++; +} + +inline Fix::Fix(int len, const Fix& y) +{ + if ( len < _Fix_min_length || len > _Fix_max_length ) + error("illegal length in declaration"); + rep = new_Fix((uint16 )len,y.rep); +} + +inline Fix::Fix(int len, const _Fix fr) +{ + if ( len < 1 || len > 65535 ) + error("illegal length in declaration"); + rep = new_Fix((uint16 )len,fr); +} + +inline Fix::Fix(int len, double d) +{ + if ( len < _Fix_min_length || len > _Fix_max_length ) + error("illegal length in declaration"); + rep = new_Fix((uint16 )len,d); +} + +inline Fix::~Fix() +{ + if ( --rep->ref <= 0 ) delete rep; +} + +inline Fix Fix::operator = (Fix& y) +{ + if ( rep->len == y.rep->len ) { + ++y.rep->ref; + if ( --rep->ref <= 0 ) delete rep; + rep = y.rep; + } + else { + unique(); + copy(y.rep,rep); + } + return *this; +} + +inline Fix Fix::operator = (double d) +{ + int oldlen = rep->len; + if ( --rep->ref <= 0 ) delete rep; + rep = new_Fix(oldlen,d); + return *this; +} + +inline int operator == (const Fix& x, const Fix& y) +{ + return compare(x.rep, y.rep) == 0; +} + +inline int operator != (const Fix& x, const Fix& y) +{ + return compare(x.rep, y.rep) != 0; +} + +inline int operator < (const Fix& x, const Fix& y) +{ + return compare(x.rep, y.rep) < 0; +} + +inline int operator <= (const Fix& x, const Fix& y) +{ + return compare(x.rep, y.rep) <= 0; +} + +inline int operator > (const Fix& x, const Fix& y) +{ + return compare(x.rep, y.rep) > 0; +} + +inline int operator >= (const Fix& x, const Fix& y) +{ + return compare(x.rep, y.rep) >= 0; +} + +inline Fix& Fix::operator + () +{ + return *this; +} + +inline Fix Fix::operator - () +{ + _Fix r = negate(rep); return r; +} + +inline Fix operator + (Fix& x, Fix& y) +{ + _Fix r = add(x.rep, y.rep); return r; +} + +inline Fix operator - (Fix& x, Fix& y) +{ + _Fix r = subtract(x.rep, y.rep); return r; +} + +inline Fix operator * (Fix& x, Fix& y) +{ + _Fix r = multiply(x.rep, y.rep); return r; +} + +inline Fix operator * (Fix& x, int y) +{ + _Fix r = multiply(x.rep, y); return r; +} + +inline Fix operator * (int y, Fix& x) +{ + _Fix r = multiply(x.rep, y); return r; +} + +inline Fix operator / (Fix& x, Fix& y) +{ + _Fix r = divide(x.rep, y.rep); return r; +} + +inline Fix Fix::operator += (Fix& y) +{ + unique(); add(rep, y.rep, rep); return *this; +} + +inline Fix Fix::operator -= (Fix& y) +{ + unique(); subtract(rep, y.rep, rep); return *this; +} + +inline Fix Fix::operator *= (Fix& y) +{ + unique(); multiply(rep, y.rep, rep); return *this; +} + +inline Fix Fix::operator *= (int y) +{ + unique(); multiply(rep, y, rep); return *this; +} + +inline Fix Fix::operator /= (Fix& y) +{ + unique(); divide(rep, y.rep, rep); return *this; +} + +inline Fix operator % (Fix& x, int y) +{ + Fix r((int )x.rep->len + y, x); return r; +} + +inline Fix operator << (Fix& x, int y) +{ + _Fix rep = shift(x.rep, y); return rep; +} + +inline Fix operator >> (Fix& x, int y) +{ + _Fix rep = shift(x.rep, -y); return rep; +} + +inline Fix Fix::operator <<= (int y) +{ + unique(); shift(rep, y, rep); return *this; +} + +inline Fix Fix::operator >>= (int y) +{ + unique(); shift(rep, -y, rep); return *this; +} + +#ifdef __GNUG__ +inline Fix operator ? (Fix& x, Fix& y) +{ + if ( compare(x.rep, y.rep) >= 0 ) return x; else return y; +} +#endif + +inline Fix abs(Fix x) +{ + _Fix r = (compare(x.rep) >= 0 ? new_Fix(x.rep->len,x.rep) : negate(x.rep)); + return r; +} + +inline int sgn(Fix& x) +{ + int a = compare(x.rep); + return a == 0 ? 0 : (a > 0 ? 1 : -1); +} + +inline int length(const Fix& x) +{ + return x.rep->len; +} + +inline ostream& operator << (ostream& s, const Fix& y) +{ + if (s.opfx()) + y.printon(s); + return s; +} + +inline void negate (Fix& x, Fix& r) +{ + negate(x.rep, r.rep); +} + +inline void add (Fix& x, Fix& y, Fix& r) +{ + add(x.rep, y.rep, r.rep); +} + +inline void subtract (Fix& x, Fix& y, Fix& r) +{ + subtract(x.rep, y.rep, r.rep); +} + +inline void multiply (Fix& x, Fix& y, Fix& r) +{ + multiply(x.rep, y.rep, r.rep); +} + +inline void divide (Fix& x, Fix& y, Fix& q, Fix& r) +{ + divide(x.rep, y.rep, q.rep, r.rep); +} + +inline void shift (Fix& x, int y, Fix& r) +{ + shift(x.rep, y, r.rep); +} + +#endif diff --git a/gnu/lib/libg++/libg++/Fix16.cc b/gnu/lib/libg++/libg++/Fix16.cc index 863506614d..a66bfbf9b1 100644 --- a/gnu/lib/libg++/libg++/Fix16.cc +++ b/gnu/lib/libg++/libg++/Fix16.cc @@ -4,22 +4,17 @@ Copyright (C) 1988 Free Software Foundation written by Kurt Baudendistel (gt-eedsp!baud@gatech.edu) adapted for libg++ by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ // @@ -74,7 +69,7 @@ long Fix32::assign(double d) } -Fix32 operator * (Fix32& a, Fix32& b) +Fix32 operator * (const Fix32& a, const Fix32& b) { // break a and b into lo and hi parts, and do a multiple-precision // multiply, with rounding @@ -97,7 +92,7 @@ Fix32 operator * (Fix32& a, Fix32& b) return Fix32(p); } -Fix16 operator / (Fix16& a, Fix16& b) +Fix16 operator / (const Fix16& a, const Fix16& b) { short q; int apos = (a.m >= 0); @@ -119,7 +114,7 @@ Fix16 operator / (Fix16& a, Fix16& b) return Fix16(q); } -Fix32 operator / (Fix32& a, Fix32& b) +Fix32 operator / (const Fix32& a, const Fix32& b) { long q; int apos = (a.m >= 0); @@ -138,7 +133,7 @@ Fix32 operator / (Fix32& a, Fix32& b) for (int i = 32; i > 0; i--) { - if (r > lb) { + if ((unsigned)(r) > lb) { q = (q << 1) | 1; r -= lb; } @@ -155,22 +150,22 @@ Fix32 operator / (Fix32& a, Fix32& b) // error handling -void Fix16::overflow(short& i) +void Fix16::overflow(short& i) const { (*Fix16_overflow_handler)(i); } -void Fix32::overflow(long& i) +void Fix32::overflow(long& i) const { (*Fix32_overflow_handler)(i); } -void Fix16::range_error(short& i) +void Fix16::range_error(short& i) const { (*Fix16_range_error_handler)(i); } -void Fix32::range_error(long& i) +void Fix32::range_error(long& i) const { (*Fix32_range_error_handler)(i); } diff --git a/gnu/lib/libg++/libg++/Fix16.h b/gnu/lib/libg++/libg++/Fix16.h new file mode 100644 index 0000000000..36728b40ce --- /dev/null +++ b/gnu/lib/libg++/libg++/Fix16.h @@ -0,0 +1,648 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Kurt Baudendistel (gt-eedsp!baud@gatech.edu) + adapted for libg++ by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _Fix16_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Fix16_h 1 + +#include +#include + +// constant definitions + +#define Fix16_fs ((double)((unsigned)(1 << 15))) + +#define Fix16_msb (1 << 15) +#define Fix16_m_max ((1 << 15) - 1) +#define Fix16_m_min ((short)(1 << 15)) + +#define Fix16_mult Fix16_fs +#define Fix16_div (1./Fix16_fs) +#define Fix16_max (1. - .5/Fix16_fs) +#define Fix16_min (-1.) + + +#define Fix32_fs ((double)((unsigned long)(1 << 31))) + +#define Fix32_msb ((unsigned long)(1 << 31)) +#define Fix32_m_max ((long)((1 << 31) - 1)) +#define Fix32_m_min ((long)(1 << 31)) + +#define Fix32_mult Fix32_fs +#define Fix32_div (1./Fix32_fs) +#define Fix32_max (1. - .5/Fix32_fs) +#define Fix32_min (-1.) + + +// +// Fix16 class: 16-bit Fixed point data type +// +// consists of a 16-bit mantissa (sign bit & 15 data bits). +// + +class Fix16 +{ + friend class Fix32; + + short m; + + short round(double d); + short assign(double d); + Fix16(short i); + Fix16(int i); + + operator double() const; + + +public: + Fix16(); + Fix16(const Fix16& f); + Fix16(double d); + Fix16(const Fix32& f); + + ~Fix16(); + + Fix16& operator=(const Fix16& f); + Fix16& operator=(double d); + Fix16& operator=(const Fix32& f); + + friend short& mantissa(Fix16& f); + friend const short& mantissa(const Fix16& f); + friend double value(const Fix16& f); + + Fix16 operator + () const; + Fix16 operator - () const; + + friend Fix16 operator + (const Fix16& f, const Fix16& g); + friend Fix16 operator - (const Fix16& f, const Fix16& g); + friend Fix32 operator * (const Fix16& f, const Fix16& g); + friend Fix16 operator / (const Fix16& f, const Fix16& g); + friend Fix16 operator << (const Fix16& f, int b); + friend Fix16 operator >> (const Fix16& f, int b); + + Fix16& operator += (const Fix16& f); + Fix16& operator -= (const Fix16& f); + Fix16& operator *= (const Fix16& ); + Fix16& operator /= (const Fix16& f); + + Fix16& operator <<=(int b); + Fix16& operator >>=(int b); + + friend int operator == (const Fix16& f, const Fix16& g); + friend int operator != (const Fix16& f, const Fix16& g); + friend int operator >= (const Fix16& f, const Fix16& g); + friend int operator <= (const Fix16& f, const Fix16& g); + friend int operator > (const Fix16& f, const Fix16& g); + friend int operator < (const Fix16& f, const Fix16& g); + + friend istream& operator >> (istream& s, Fix16& f); + friend ostream& operator << (ostream& s, const Fix16& f); + + void overflow(short&) const; + void range_error(short&) const; + + friend Fix16 operator * (const Fix16& f, int g); + friend Fix16 operator * (int g, const Fix16& f); + Fix16& operator *= (int g); +}; + + +// +// Fix32 class: 32-bit Fixed point data type +// +// consists of a 32-bit mantissa (sign bit & 31 data bits). +// + +class Fix32 +{ + friend class Fix16; + + long m; + + long round(double d); + long assign(double d); + + Fix32(long i); + operator double() const; + + +public: + Fix32(); + Fix32(const Fix32& f); + Fix32(const Fix16& f); + Fix32(double d); + ~Fix32(); + + Fix32& operator = (const Fix32& f); + Fix32& operator = (const Fix16& f); + Fix32& operator = (double d); + + friend long& mantissa(Fix32& f); + friend const long& mantissa(const Fix32& f); + friend double value(const Fix32& f); + + Fix32 operator + () const; + Fix32 operator - () const; + + friend Fix32 operator + (const Fix32& f, const Fix32& g); + friend Fix32 operator - (const Fix32& f, const Fix32& g); + friend Fix32 operator * (const Fix32& f, const Fix32& g); + friend Fix32 operator / (const Fix32& f, const Fix32& g); + friend Fix32 operator << (const Fix32& f, int b); + friend Fix32 operator >> (const Fix32& f, int b); + + friend Fix32 operator * (const Fix16& f, const Fix16& g); + + Fix32& operator += (const Fix32& f); + Fix32& operator -= (const Fix32& f); + Fix32& operator *= (const Fix32& f); + Fix32& operator /= (const Fix32& f); + Fix32& operator <<=(int b); + Fix32& operator >>=(int b); + + friend int operator == (const Fix32& f, const Fix32& g); + friend int operator != (const Fix32& f, const Fix32& g); + friend int operator >= (const Fix32& f, const Fix32& g); + friend int operator <= (const Fix32& f, const Fix32& g); + friend int operator > (const Fix32& f, const Fix32& g); + friend int operator < (const Fix32& f, const Fix32& g); + + friend istream& operator >> (istream& s, Fix32& f); + friend ostream& operator << (ostream& s, const Fix32& f); + + void overflow(long& i) const; + void range_error(long& i) const; + + friend Fix32 operator * (const Fix32& f, int g); + friend Fix32 operator * (int g, const Fix32& f); + Fix32& operator *= (int g); +}; + +// active error handler declarations + +typedef void (*Fix16_peh)(short&); +typedef void (*Fix32_peh)(long&); + +extern Fix16_peh Fix16_overflow_handler; +extern Fix32_peh Fix32_overflow_handler; + +extern Fix16_peh Fix16_range_error_handler; +extern Fix32_peh Fix32_range_error_handler; + +#if defined(SHORT_NAMES) || defined(VMS) +#define set_overflow_handler sohndl +#define set_range_error_handler srnghdl +#endif + + +// error handler declarations + +extern Fix16_peh set_Fix16_overflow_handler(Fix16_peh); +extern Fix32_peh set_Fix32_overflow_handler(Fix32_peh); +extern void set_overflow_handler(Fix16_peh, Fix32_peh); + +extern Fix16_peh set_Fix16_range_error_handler(Fix16_peh); +extern Fix32_peh set_Fix32_range_error_handler(Fix32_peh); +extern void set_range_error_handler(Fix16_peh, Fix32_peh); + +extern void + Fix16_ignore(short&), + Fix16_overflow_saturate(short&), + Fix16_overflow_warning_saturate(short&), + Fix16_warning(short&), + Fix16_abort(short&); + +extern void + Fix32_ignore(long&), + Fix32_overflow_saturate(long&), + Fix32_overflow_warning_saturate(long&), + Fix32_warning(long&), + Fix32_abort(long&); + + +inline Fix16::~Fix16() {} + +inline short Fix16::round(double d) +{ + return short( (d >= 0)? d + 0.5 : d - 0.5); +} + +inline Fix16::Fix16(short i) +{ + m = i; +} + +inline Fix16::Fix16(int i) +{ + m = i; +} + +inline Fix16::operator double() const +{ + return Fix16_div * m; +} + +inline Fix16::Fix16() +{ + m = 0; +} + +inline Fix16::Fix16(const Fix16& f) +{ + m = f.m; +} + +inline Fix16::Fix16(double d) +{ + m = assign(d); +} + + +inline Fix16& Fix16::operator=(const Fix16& f) +{ + m = f.m; + return *this; +} + +inline Fix16& Fix16::operator=(double d) +{ + m = assign(d); + return *this; +} + + +inline Fix32::Fix32() +{ + m = 0; +} + +inline Fix32::Fix32(long i) +{ + m = i; +} + +inline Fix32:: operator double() const +{ + return Fix32_div * m; +} + + +inline Fix32::Fix32(const Fix32& f) +{ + m = f.m; +} + +inline Fix32::Fix32(const Fix16& f) +{ + m = long(f.m) << 16; +} + +inline Fix32::Fix32(double d) +{ + m = assign(d); +} + +inline Fix16::Fix16(const Fix32& f) +{ + m = f.m >> 16; +} + + +inline Fix16& Fix16::operator=(const Fix32& f) +{ + m = f.m >> 16; + return *this; +} + +inline Fix32& Fix32::operator=(const Fix32& f) +{ + m = f.m; + return *this; +} + +inline Fix32& Fix32::operator=(const Fix16& f) +{ + m = long(f.m) << 16; + return *this; +} + +inline Fix32& Fix32::operator=(double d) +{ + m = assign(d); + return *this; +} + +inline short& mantissa(Fix16& f) +{ + return f.m; +} + +inline const short& mantissa(const Fix16& f) +{ + return f.m; +} + +inline double value(const Fix16& f) +{ + return double(f); +} + +inline Fix16 Fix16::operator+() const +{ + return m; +} + +inline Fix16 Fix16::operator-() const +{ + return -m; +} + +inline Fix16 operator+(const Fix16& f, const Fix16& g) +{ + short sum = f.m + g.m; + if ( (f.m ^ sum) & (g.m ^ sum) & Fix16_msb ) + f.overflow(sum); + return sum; +} + +inline Fix16 operator-(const Fix16& f, const Fix16& g) +{ + short sum = f.m - g.m; + if ( (f.m ^ sum) & (-g.m ^ sum) & Fix16_msb ) + f.overflow(sum); + return sum; +} + +inline Fix32 operator*(const Fix16& f, const Fix16& g) +{ + return Fix32( long( long(f.m) * long(g.m) << 1)); +} + +inline Fix16 operator<<(const Fix16& a, int b) +{ + return a.m << b; +} + +inline Fix16 operator>>(const Fix16& a, int b) +{ + return a.m >> b; +} + +inline Fix16& Fix16:: operator+=(const Fix16& f) +{ + return *this = *this + f; +} + +inline Fix16& Fix16:: operator-=(const Fix16& f) +{ + return *this = *this - f; +} + +inline Fix16& Fix16::operator*=(const Fix16& f) +{ + return *this = *this * f; +} + +inline Fix16& Fix16:: operator/=(const Fix16& f) +{ + return *this = *this / f; +} + +inline Fix16& Fix16:: operator<<=(int b) +{ + return *this = *this << b; +} + +inline Fix16& Fix16:: operator>>=(int b) +{ + return *this = *this >> b; +} + +inline int operator==(const Fix16& f, const Fix16& g) +{ + return f.m == g.m; +} + +inline int operator!=(const Fix16& f, const Fix16& g) +{ + return f.m != g.m; +} + +inline int operator>=(const Fix16& f, const Fix16& g) +{ + return f.m >= g.m; +} + +inline int operator<=(const Fix16& f, const Fix16& g) +{ + return f.m <= g.m; +} + +inline int operator>(const Fix16& f, const Fix16& g) +{ + return f.m > g.m; +} + +inline int operator<(const Fix16& f, const Fix16& g) +{ + return f.m < g.m; +} + +inline istream& operator>>(istream& s, Fix16& f) +{ + double d; + s >> d; + f = d; + return s; +} + +inline ostream& operator<<(ostream& s, const Fix16& f) +{ + return s << double(f); +} + + +inline Fix16 operator*(const Fix16& f, int g) +{ + return Fix16(short(f.m * g)); +} + +inline Fix16 operator*(int g, const Fix16& f) +{ + return f * g; +} + + +inline Fix16& Fix16::operator*=(int g) +{ + return *this = *this * g; +} + +inline Fix32::~Fix32() {} + +inline long Fix32::round(double d) +{ + return long( (d >= 0)? d + 0.5 : d - 0.5); +} + +inline long& mantissa(Fix32& f) +{ + return f.m; +} + +inline const long& mantissa(const Fix32& f) +{ + return f.m; +} + +inline double value(const Fix32& f) +{ + return double(f); +} + +inline Fix32 Fix32::operator+() const +{ + return m; +} + +inline Fix32 Fix32::operator-() const +{ + return -m; +} + +inline Fix32 operator+(const Fix32& f, const Fix32& g) +{ + long sum = f.m + g.m; + if ( (f.m ^ sum) & (g.m ^ sum) & Fix32_msb ) + f.overflow(sum); + return sum; +} + +inline Fix32 operator-(const Fix32& f, const Fix32& g) +{ + long sum = f.m - g.m; + if ( (f.m ^ sum) & (-g.m ^ sum) & Fix32_msb ) + f.overflow(sum); + return sum; +} + +inline Fix32 operator<<(const Fix32& a, int b) +{ + return a.m << b; +} + +inline Fix32 operator>>(const Fix32& a, int b) +{ + return a.m >> b; +} + +inline Fix32& Fix32::operator+=(const Fix32& f) +{ + return *this = *this + f; +} + +inline Fix32& Fix32::operator-=(const Fix32& f) +{ + return *this = *this - f; +} + +inline Fix32& Fix32::operator*=(const Fix32& f) +{ + return *this = *this * f; +} + +inline Fix32& Fix32::operator/=(const Fix32& f) +{ + return *this = *this / f; +} + + +inline Fix32& Fix32::operator<<=(int b) +{ + return *this = *this << b; +} + +inline Fix32& Fix32::operator>>=(int b) +{ + return *this = *this >> b; +} + +inline int operator==(const Fix32& f, const Fix32& g) +{ + return f.m == g.m; +} + +inline int operator!=(const Fix32& f, const Fix32& g) +{ + return f.m != g.m; +} + +inline int operator>=(const Fix32& f, const Fix32& g) +{ + return f.m >= g.m; +} + +inline int operator<=(const Fix32& f, const Fix32& g) +{ + return f.m <= g.m; +} + +inline int operator>(const Fix32& f, const Fix32& g) +{ + return f.m > g.m; +} + +inline int operator<(const Fix32& f, const Fix32& g) +{ + return f.m < g.m; +} + +inline istream& operator>>(istream& s, Fix32& f) +{ + double d; + s >> d; + f = d; + return s; +} + +inline ostream& operator<<(ostream& s, const Fix32& f) +{ + return s << double(f); +} + +inline Fix32 operator*(const Fix32& f, int g) +{ + return Fix32(long(f.m * g)); +} + +inline Fix32 operator*(int g, const Fix32& f) +{ + return f * g; +} + + + +inline Fix32& Fix32::operator*=(int g) +{ + return *this = *this * g; +} + +#endif diff --git a/gnu/lib/libg++/libg++/Fix24.cc b/gnu/lib/libg++/libg++/Fix24.cc index 1eb8442117..22e23dcbcf 100644 --- a/gnu/lib/libg++/libg++/Fix24.cc +++ b/gnu/lib/libg++/libg++/Fix24.cc @@ -4,22 +4,17 @@ Copyright (C) 1988 Free Software Foundation written by Kurt Baudendistel (gt-eedsp!baud@gatech.edu) adapted for libg++ by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ // @@ -91,7 +86,7 @@ twolongs Fix48::assign(double d) } -Fix48 operator * (Fix24& a, Fix24& b) +Fix48 operator * (const Fix24& a, const Fix24& b) { // break a and b into lo and hi parts, and do a multiple-precision // multiply, with rounding @@ -125,7 +120,7 @@ Fix48 operator * (Fix24& a, Fix24& b) return r; } -Fix24 operator / (Fix24& a, Fix24& b) +Fix24 operator / (const Fix24& a, const Fix24& b) { long q; int apos = (a.m >= 0); @@ -144,7 +139,7 @@ Fix24 operator / (Fix24& a, Fix24& b) for (int i = 32; i > 0; i--) { - if (r > lb) { + if ((unsigned)(r) > lb) { q = (q << 1) | 1; r -= lb; } @@ -156,11 +151,11 @@ Fix24 operator / (Fix24& a, Fix24& b) q += 0x80; // Round result to 24 bits if (apos != bpos) q = -q; // Fix sign } - return (q & 0xffffff00); + return (q & ~0xFF); } -Fix48 operator + (Fix48& f, Fix48& g) +Fix48 operator + (const Fix48& f, const Fix48& g) { long lo_r = (f.m.l >> 8) + (g.m.l >> 8); twolongs r; @@ -172,7 +167,7 @@ Fix48 operator + (Fix48& f, Fix48& g) return r; } -Fix48 operator - (Fix48& f, Fix48& g) +Fix48 operator - (const Fix48& f, const Fix48& g) { unsigned lo_r = (f.m.l >> 8) - (g.m.l >> 8); twolongs r; @@ -184,7 +179,7 @@ Fix48 operator - (Fix48& f, Fix48& g) return r; } -Fix48 operator * (Fix48& a, int b) +Fix48 operator * (const Fix48& a, int b) { twolongs r; int bpos = (b >= 0); @@ -209,9 +204,9 @@ Fix48 operator * (Fix48& a, int b) return r; } -Fix48 operator << (Fix48& a, int b) +Fix48 operator << (const Fix48& a, int b) { - twolongs r; r.u = r.l = 0; + twolongs r; r.u = 0; r.l = 0; if ( b >= 0 ) if ( b < 24 ) { r.u = (a.m.u << b) + ((a.m.l >> (24 - b)) & 0xffffff00L); @@ -223,9 +218,9 @@ Fix48 operator << (Fix48& a, int b) return r; } -Fix48 operator >> (Fix48& a, int b) +Fix48 operator >> (const Fix48& a, int b) { - twolongs r; r.u = r.l = 0; + twolongs r; r.u = 0; r.l = 0; if ( b >= 0 ) if ( b < 24 ) { r.l = (a.m.u << (24 - b)) + ((a.m.l >> b) & 0xffffff00L); @@ -244,22 +239,22 @@ Fix48 operator >> (Fix48& a, int b) // error handling -void Fix24::overflow(long& i) +void Fix24::overflow(long& i) const { (*Fix24_overflow_handler)(i); } -void Fix48::overflow(twolongs& i) +void Fix48::overflow(twolongs& i) const { (*Fix48_overflow_handler)(i); } -void Fix24::range_error(long& i) +void Fix24::range_error(long& i) const { (*Fix24_range_error_handler)(i); } -void Fix48::range_error(twolongs& i) +void Fix48::range_error(twolongs& i) const { (*Fix48_range_error_handler)(i); } diff --git a/gnu/lib/libg++/libg++/Fix24.h b/gnu/lib/libg++/libg++/Fix24.h new file mode 100644 index 0000000000..5477b368b8 --- /dev/null +++ b/gnu/lib/libg++/libg++/Fix24.h @@ -0,0 +1,597 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Kurt Baudendistel (gt-eedsp!baud@gatech.edu) + adapted for libg++ by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _Fix24_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Fix24_h 1 + +#include +#include + +// extra type definitions + +typedef struct { + long u; + unsigned long l; +} twolongs; + +// constant definitions + +static const int + Fix24_shift = 31; + +static const double + Fix24_fs = 2147483648., // 2^Fix24_shift + Fix24_mult = Fix24_fs, + Fix24_div = 1./Fix24_fs, + Fix24_max = 1. - .5/Fix24_fs, + Fix24_min = -1.; + +static const unsigned long + Fix24_msb = 0x80000000L, + Fix24_lsb = 0x00000100L, + Fix24_m_max = 0x7fffff00L, + Fix24_m_min = 0x80000000L; + +static const double + Fix48_fs = 36028797018963968., // 2^(24+Fix24_shift) + Fix48_max = 1. - .5/Fix48_fs, + Fix48_min = -1., + Fix48_div_u = 1./Fix24_fs, + Fix48_div_l = 1./Fix48_fs; + +static const twolongs + Fix48_msb = { 0x80000000L, 0L }, + Fix48_lsb = { 0L, 0x00000100L }, + Fix48_m_max = { 0x7fffff00L, 0xffffff00L }, + Fix48_m_min = { 0x80000000L, 0L }; + +// +// Fix24 class: 24-bit Fixed point data type +// +// consists of a 24-bit mantissa (sign bit & 23 data bits). +// + +class Fix24 +{ + friend class Fix48; + + long m; + + long assign(double d); + operator double() const; + Fix24(long i); + Fix24(int i); + + +public: + Fix24(); + Fix24(const Fix24& f); + Fix24(double d); + Fix24(const Fix48& f); + + ~Fix24(); + + Fix24& operator=(const Fix24& f); + Fix24& operator=(double d); + Fix24& operator=(const Fix48& f); + + friend long& mantissa(Fix24& f); + friend const long& mantissa(const Fix24& f); + friend double value(const Fix24& f); + + Fix24 operator + () const; + Fix24 operator - () const; + + friend Fix24 operator + (const Fix24& f, const Fix24& g); + friend Fix24 operator - (const Fix24& f, const Fix24& g); + friend Fix48 operator * (const Fix24& f, const Fix24& g); + friend Fix24 operator * (const Fix24& f, int g); + friend Fix24 operator * (int g, const Fix24& f); + friend Fix24 operator / (const Fix24& f, const Fix24& g); + friend Fix24 operator << (const Fix24& f, int b); + friend Fix24 operator >> (const Fix24& f, int b); + + Fix24& operator += (const Fix24& f); + Fix24& operator -= (const Fix24& f); + Fix24& operator *= (const Fix24& f); + Fix24& operator *= (int b); + Fix24& operator /= (const Fix24& f); + + Fix24& operator <<=(int b); + Fix24& operator >>=(int b); + + friend int operator == (const Fix24& f, const Fix24& g); + friend int operator != (const Fix24& f, const Fix24& g); + friend int operator >= (const Fix24& f, const Fix24& g); + friend int operator <= (const Fix24& f, const Fix24& g); + friend int operator > (const Fix24& f, const Fix24& g); + friend int operator < (const Fix24& f, const Fix24& g); + + friend istream& operator >> (istream& s, Fix24& f); + friend ostream& operator << (ostream& s, const Fix24& f); + + void overflow(long&) const; + void range_error(long&) const; +}; + + +// +// Fix48 class: 48-bit Fixed point data type +// +// consists of a 48-bit mantissa (sign bit & 47 data bits). +// + +class Fix48 +{ + friend class Fix24; + + twolongs m; + + twolongs assign(double d); + operator double() const; + Fix48(twolongs i); + +public: + Fix48(); + Fix48(const Fix48& f); + Fix48(const Fix24& f); + Fix48(double d); + ~Fix48(); + + Fix48& operator = (const Fix48& f); + Fix48& operator = (const Fix24& f); + Fix48& operator = (double d); + + friend twolongs& mantissa(Fix48& f); + friend const twolongs& mantissa(const Fix48& f); + friend double value(const Fix48& f); + + Fix48 operator + () const; + Fix48 operator - () const; + + friend Fix48 operator + (const Fix48& f, const Fix48& g); + friend Fix48 operator - (const Fix48& f, const Fix48& g); + friend Fix48 operator * (const Fix48& f, int g); + friend Fix48 operator * (int g, const Fix48& f); + friend Fix48 operator << (const Fix48& f, int b); + friend Fix48 operator >> (const Fix48& f, int b); + + friend Fix48 operator * (const Fix24& f, const Fix24& g); + + Fix48& operator += (const Fix48& f); + Fix48& operator -= (const Fix48& f); + Fix48& operator *= (int b); + Fix48& operator <<=(int b); + Fix48& operator >>=(int b); + + friend int operator == (const Fix48& f, const Fix48& g); + friend int operator != (const Fix48& f, const Fix48& g); + friend int operator >= (const Fix48& f, const Fix48& g); + friend int operator <= (const Fix48& f, const Fix48& g); + friend int operator > (const Fix48& f, const Fix48& g); + friend int operator < (const Fix48& f, const Fix48& g); + + friend istream& operator >> (istream& s, Fix48& f); + friend ostream& operator << (ostream& s, const Fix48& f); + + void overflow(twolongs& i) const; + void range_error(twolongs& i) const; +}; + + +// active error handler declarations + +typedef void (*Fix24_peh)(long&); +typedef void (*Fix48_peh)(twolongs&); + +extern Fix24_peh Fix24_overflow_handler; +extern Fix48_peh Fix48_overflow_handler; + +extern Fix24_peh Fix24_range_error_handler; +extern Fix48_peh Fix48_range_error_handler; + + +// error handler declarations + +#if defined(SHORT_NAMES) || defined(VMS) +#define set_overflow_handler sohndl +#define set_range_error_handler srnghdl +#endif + +extern Fix24_peh set_Fix24_overflow_handler(Fix24_peh); +extern Fix48_peh set_Fix48_overflow_handler(Fix48_peh); +extern void set_overflow_handler(Fix24_peh, Fix48_peh); + +extern Fix24_peh set_Fix24_range_error_handler(Fix24_peh); +extern Fix48_peh set_Fix48_range_error_handler(Fix48_peh); +extern void set_range_error_handler(Fix24_peh, Fix48_peh); + +extern void + Fix24_ignore(long&), + Fix24_overflow_saturate(long&), + Fix24_overflow_warning_saturate(long&), + Fix24_warning(long&), + Fix24_abort(long&); + +extern void + Fix48_ignore(twolongs&), + Fix48_overflow_saturate(twolongs&), + Fix48_overflow_warning_saturate(twolongs&), + Fix48_warning(twolongs&), + Fix48_abort(twolongs&); + + +inline Fix24::~Fix24() {} + +inline Fix24::Fix24(long i) +{ + m = i; +} + +inline Fix24::Fix24(int i) +{ + m = i; +} + +inline Fix24::operator double() const +{ + return Fix24_div * m; +} + +inline Fix24::Fix24() +{ + m = 0; +} + +inline Fix24::Fix24(const Fix24& f) +{ + m = f.m; +} + +inline Fix24::Fix24(double d) +{ + m = assign(d); +} + +inline Fix24::Fix24(const Fix48& f) +{ + m = f.m.u; +} + +inline Fix24& Fix24::operator=(const Fix24& f) +{ + m = f.m; + return *this; +} + +inline Fix24& Fix24::operator=(double d) +{ + m = assign(d); + return *this; +} + +inline Fix24& Fix24::operator=(const Fix48& f) +{ + m = f.m.u; + return *this; +} + +inline long& mantissa(Fix24& f) +{ + return f.m; +} + +inline const long& mantissa(const Fix24& f) +{ + return f.m; +} + +inline double value(const Fix24& f) +{ + return double(f); +} + +inline Fix24 Fix24::operator+() const +{ + return m; +} + +inline Fix24 Fix24::operator-() const +{ + return -m; +} + +inline Fix24 operator+(const Fix24& f, const Fix24& g) +{ + long sum = f.m + g.m; + if ( (f.m ^ sum) & (g.m ^ sum) & Fix24_msb ) + f.overflow(sum); + return sum; +} + +inline Fix24 operator-(const Fix24& f, const Fix24& g) +{ + long sum = f.m - g.m; + if ( (f.m ^ sum) & (-g.m ^ sum) & Fix24_msb ) + f.overflow(sum); + return sum; +} + +inline Fix24 operator*(const Fix24& a, int b) +{ + return a.m * b; +} + +inline Fix24 operator*(int b, const Fix24& a) +{ + return a * b; +} + +inline Fix24 operator<<(const Fix24& a, int b) +{ + return a.m << b; +} + +inline Fix24 operator>>(const Fix24& a, int b) +{ + return (a.m >> b) & 0xffffff00L; +} + +inline Fix24& Fix24:: operator+=(const Fix24& f) +{ + return *this = *this + f; +} + +inline Fix24& Fix24:: operator-=(const Fix24& f) +{ + return *this = *this - f; +} + +inline Fix24& Fix24::operator*=(const Fix24& f) +{ + return *this = *this * f; +} + +inline Fix24& Fix24:: operator/=(const Fix24& f) +{ + return *this = *this / f; +} + +inline Fix24& Fix24:: operator<<=(int b) +{ + return *this = *this << b; +} + +inline Fix24& Fix24:: operator>>=(int b) +{ + return *this = *this >> b; +} + +inline Fix24& Fix24::operator*=(int b) +{ + return *this = *this * b; +} + +inline int operator==(const Fix24& f, const Fix24& g) +{ + return f.m == g.m; +} + +inline int operator!=(const Fix24& f, const Fix24& g) +{ + return f.m != g.m; +} + +inline int operator>=(const Fix24& f, const Fix24& g) +{ + return f.m >= g.m; +} + +inline int operator<=(const Fix24& f, const Fix24& g) +{ + return f.m <= g.m; +} + +inline int operator>(const Fix24& f, const Fix24& g) +{ + return f.m > g.m; +} + +inline int operator<(const Fix24& f, const Fix24& g) +{ + return f.m < g.m; +} + +inline istream& operator>>(istream& s, Fix24& f) +{ + double d; + s >> d; + f = d; + return s; +} + +inline ostream& operator<<(ostream& s, const Fix24& f) +{ + return s << double(f); +} + +inline Fix48::~Fix48() {} + +inline Fix48::Fix48(twolongs i) +{ + m = i; +} + +inline Fix48:: operator double() const +{ +/* + * Note: can't simply do Fix48_div_u * m.u + Fix48_div_l * m.l, because + * m.u is signed and m.l is unsigned. + */ + return (m.u >= 0)? Fix48_div_u * m.u + Fix48_div_l * m.l : + (Fix48_div_u * ((unsigned long)(m.u & 0xffffff00)) + + Fix48_div_l * m.l) - 2; +} + +inline Fix48::Fix48() +{ + m.u = 0; + m.l = 0; +} + +inline Fix48::Fix48(const Fix48& f) +{ + m = f.m; +} + +inline Fix48::Fix48(const Fix24& f) +{ + m.u = f.m; + m.l = 0; +} + +inline Fix48::Fix48(double d) +{ + m = assign(d); +} + +inline Fix48& Fix48::operator=(const Fix48& f) +{ + m = f.m; + return *this; +} + +inline Fix48& Fix48::operator=(const Fix24& f) +{ + m.u = f.m; + m.l = 0; + return *this; +} + +inline Fix48& Fix48::operator=(double d) +{ + m = assign(d); + return *this; +} + +inline twolongs& mantissa(Fix48& f) +{ + return f.m; +} + +inline const twolongs& mantissa(const Fix48& f) +{ + return f.m; +} + +inline double value(const Fix48& f) +{ + return double(f); +} + +inline Fix48 Fix48::operator+() const +{ + return m; +} + +inline Fix48 Fix48::operator-() const +{ + twolongs n; + n.l = -m.l; + n.u = ~m.u + ((n.l ^ m.l) & Fix24_msb ? 0 : Fix24_lsb); + return Fix48(n); +} + +inline Fix48 operator*(int b, const Fix48& a) +{ + return a * b; +} + +inline Fix48& Fix48::operator+=(const Fix48& f) +{ + return *this = *this + f; +} + +inline Fix48& Fix48::operator-=(const Fix48& f) +{ + return *this = *this - f; +} + +inline Fix48& Fix48::operator*=(int b) +{ + return *this = *this * b; +} + +inline Fix48& Fix48::operator<<=(int b) +{ + return *this = *this << b; +} + +inline Fix48& Fix48::operator>>=(int b) +{ + return *this = *this >> b; +} + +inline int operator==(const Fix48& f, const Fix48& g) +{ + return f.m.u == g.m.u && f.m.l == g.m.l; +} + +inline int operator!=(const Fix48& f, const Fix48& g) +{ + return f.m.u != g.m.u || f.m.l != g.m.l; +} + +inline int operator>=(const Fix48& f, const Fix48& g) +{ + return f.m.u >= g.m.u || (f.m.u == g.m.u && f.m.l >= g.m.l); +} + +inline int operator<=(const Fix48& f, const Fix48& g) +{ + return f.m.u <= g.m.u || (f.m.u == g.m.u && f.m.l <= g.m.l); +} + +inline int operator>(const Fix48& f, const Fix48& g) +{ + return f.m.u > g.m.u || (f.m.u == g.m.u && f.m.l > g.m.l); +} + +inline int operator<(const Fix48& f, const Fix48& g) +{ + return f.m.u < g.m.u || (f.m.u == g.m.u && f.m.l < g.m.l); +} + +inline istream& operator>>(istream& s, Fix48& f) +{ + double d; + s >> d; + f = d; + return s; +} + +inline ostream& operator<<(ostream& s, const Fix48& f) +{ + return s << double(f); +} + +#endif diff --git a/gnu/lib/libg++/libg++/Geom.cc b/gnu/lib/libg++/libg++/Geom.cc index e7af12a3c7..0353738a3d 100644 --- a/gnu/lib/libg++/libg++/Geom.cc +++ b/gnu/lib/libg++/libg++/Geom.cc @@ -2,29 +2,23 @@ Copyright (C) 1988 Free Software Foundation written by Dirk Grunwald (grunwald@cs.uiuc.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ #pragma implementation #endif #include #include - #include double Geometric::operator()() diff --git a/gnu/lib/libg++/libg++/Geom.h b/gnu/lib/libg++/libg++/Geom.h new file mode 100644 index 0000000000..5cfa39aaa3 --- /dev/null +++ b/gnu/lib/libg++/libg++/Geom.h @@ -0,0 +1,52 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef _Geometric_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Geometric_h + +#include + +class Geometric: public Random { +protected: + double pMean; +public: + Geometric(double mean, RNG *gen); + + double mean(); + double mean(double x); + + virtual double operator()(); + +}; + + +inline Geometric::Geometric(double mean, RNG *gen) : Random(gen) +{ + pMean = mean; +} + + +inline double Geometric::mean() { return pMean; } +inline double Geometric::mean(double x) { + double tmp = pMean; pMean = x; return tmp; +} + + +#endif diff --git a/gnu/lib/libg++/libg++/GetOpt.cc b/gnu/lib/libg++/libg++/GetOpt.cc index d1644e4dd4..16c647ddb6 100644 --- a/gnu/lib/libg++/libg++/GetOpt.cc +++ b/gnu/lib/libg++/libg++/GetOpt.cc @@ -1,36 +1,49 @@ -/* Getopt for GNU. - Copyright (C) 1987, 1989 Free Software Foundation, Inc. - (Modified by Douglas C. Schmidt for use with GNU G++.) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* +Getopt for GNU. +Copyright (C) 1987, 1989 Free Software Foundation, Inc. + +(Modified by Douglas C. Schmidt for use with GNU G++.) +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ #ifdef __GNUG__ #pragma implementation #endif +/* AIX requires the alloca decl to be the first thing in the file. */ +#ifdef __GNUC__ +#define alloca __builtin_alloca +#elif defined(sparc) +#include +extern "C" void *__builtin_alloca(...); +#elif defined(_AIX) +#pragma alloca +#else +char *alloca (); +#endif #include -GetOpt::GetOpt (int argc, char **argv, char *optstring): opterr (1) +char* GetOpt::nextchar = 0; +int GetOpt::first_nonopt = 0; +int GetOpt::last_nonopt = 0; + +GetOpt::GetOpt (int argc, char **argv, const char *optstring) + :opterr (1), nargc (argc), nargv (argv), noptstring (optstring) { /* Initialize the internal data when the first call is made. Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ - nargc = argc; - nargv = argv; - noptstring = optstring; first_nonopt = last_nonopt = optind = 1; optarg = nextchar = 0; @@ -53,10 +66,10 @@ GetOpt::exchange (char **argv) /* Interchange the two blocks of data in argv. */ - bcopy (&argv[first_nonopt], temp, nonopts_size); - bcopy (&argv[last_nonopt], &argv[first_nonopt], + memcpy (temp, &argv[first_nonopt], nonopts_size); + memcpy (&argv[first_nonopt], &argv[last_nonopt], (optind - last_nonopt) * sizeof (char *)); - bcopy (temp, &argv[first_nonopt + optind - last_nonopt], + memcpy (&argv[first_nonopt + optind - last_nonopt], temp, nonopts_size); /* Update records for the slots the non-options now occupy. */ @@ -178,7 +191,7 @@ GetOpt::operator () (void) { char c = *nextchar++; - char *temp = (char *) index (noptstring, c); + char *temp = (char *) strchr (noptstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == 0) diff --git a/gnu/lib/libg++/libg++/GetOpt.h b/gnu/lib/libg++/libg++/GetOpt.h new file mode 100644 index 0000000000..66ecf5c161 --- /dev/null +++ b/gnu/lib/libg++/libg++/GetOpt.h @@ -0,0 +1,129 @@ +/* Getopt for GNU. + Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc. + (Modified by Douglas C. Schmidt for use with GNU G++.) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of `argv' so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable _POSIX_OPTION_ORDER disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#ifndef GetOpt_h +#ifdef __GNUG__ +#pragma interface +#endif +#define GetOpt_h 1 + +#include +#include + +class GetOpt +{ +private: + /* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + + static char *nextchar; + + + /* Describe how to deal with options that follow non-option ARGV-elements. + + UNSPECIFIED means the caller did not specify anything; + the default is then REQUIRE_ORDER if the environment variable + _OPTIONS_FIRST is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options. + Stop option processing when the first non-option is seen. + This is what Unix does. + + PERMUTE is the default. We permute the contents of `argv' as we scan, + so that eventually all the options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code zero. + Using `-' as the first character of the list of option characters + requests this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + + enum OrderingEnum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER }; + OrderingEnum ordering; + + /* Handle permutation of arguments. */ + + /* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + + static int first_nonopt; + static int last_nonopt; + + void exchange (char **argv); +public: + /* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + + char *optarg; + + /* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + + int optind; + + /* Callers store zero here to inhibit the error message + for unrecognized options. */ + + int opterr; + + int nargc; + char **nargv; + const char *noptstring; + + GetOpt (int argc, char **argv, const char *optstring); + int operator () (void); +}; + +#endif diff --git a/gnu/lib/libg++/libg++/HypGeom.cc b/gnu/lib/libg++/libg++/HypGeom.cc index dd1a1d0c82..50d95867d3 100644 --- a/gnu/lib/libg++/libg++/HypGeom.cc +++ b/gnu/lib/libg++/libg++/HypGeom.cc @@ -3,29 +3,23 @@ Copyright (C) 1988 Free Software Foundation written by Dirk Grunwald (grunwald@cs.uiuc.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ #pragma implementation #endif #include #include - #include double HyperGeometric::operator()() diff --git a/gnu/lib/libg++/libg++/HypGeom.h b/gnu/lib/libg++/libg++/HypGeom.h new file mode 100644 index 0000000000..e16e6552cc --- /dev/null +++ b/gnu/lib/libg++/libg++/HypGeom.h @@ -0,0 +1,70 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef _HyperGeometric_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _HyperGeometric_h + +#include + +class HyperGeometric: public Random { +protected: + double pMean; + double pVariance; + double pP; + void setState(); + +public: + HyperGeometric(double mean, double variance, RNG *gen); + + double mean(); + double mean(double x); + double variance(); + double variance(double x); + + virtual double operator()(); +}; + + +inline void HyperGeometric::setState() { + double z = pVariance / (pMean * pMean); + pP = 0.5 * (1.0 - sqrt((z - 1.0) / ( z + 1.0 ))); +} + +inline HyperGeometric::HyperGeometric(double mean, double variance, RNG *gen) +: Random(gen) { + pMean = mean; pVariance = variance; + setState(); +} + +inline double HyperGeometric::mean() { return pMean; }; + +inline double HyperGeometric::mean(double x) { + double t = pMean; pMean = x; + setState(); return t; +} + +inline double HyperGeometric::variance() { return pVariance; } + +inline double HyperGeometric::variance(double x) { + double t = pVariance; pVariance = x; + setState(); return t; +} + +#endif diff --git a/gnu/lib/libg++/libg++/Incremental.h b/gnu/lib/libg++/libg++/Incremental.h new file mode 100644 index 0000000000..267970a8cf --- /dev/null +++ b/gnu/lib/libg++/libg++/Incremental.h @@ -0,0 +1,12 @@ +#ifndef Incremental_h +#ifdef __GNUG__ +#pragma interface +#endif +#define Incremental_h +#define DECLARE_INIT_FUNCTION(USER_INIT_FUNCTION) \ +static void USER_INIT_FUNCTION (); extern void (*_initfn)(); \ +static struct xyzzy { xyzzy () {_initfn = USER_INIT_FUNCTION;}; \ +~xyzzy () {};} __2xyzzy; +#else +#error Incremental.h was not the first file included in this module +#endif diff --git a/gnu/lib/libg++/libg++/Integer.cc b/gnu/lib/libg++/libg++/Integer.cc index 3cd5c3626a..277ad63a0e 100644 --- a/gnu/lib/libg++/libg++/Integer.cc +++ b/gnu/lib/libg++/libg++/Integer.cc @@ -2,22 +2,17 @@ Copyright (C) 1988 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* @@ -36,18 +31,29 @@ and this notice must be preserved on all copies. #include #include #include +#include +#include #include -#include #include #include #include +#include + +#ifndef HUGE_VAL +#ifdef HUGE +#define HUGE_VAL HUGE +#else +#define HUGE_VAL DBL_MAX +#endif +#endif + /* Sizes of shifts for multiple-precision arithmetic. These should not be changed unless Integer representation as unsigned shorts is changed in the implementation files. */ -#define I_SHIFT SHORTBITS +#define I_SHIFT (sizeof(short) * CHAR_BIT) #define I_RADIX ((unsigned long)(1L << I_SHIFT)) #define I_MAXNUM ((unsigned long)((I_RADIX - 1))) #define I_MINNUM ((unsigned long)(I_RADIX >> 1)) @@ -55,8 +61,8 @@ and this notice must be preserved on all copies. #define I_NEGATIVE 0 /* All routines assume SHORT_PER_LONG > 1 */ -#define SHORT_PER_LONG ((LONGBITS + SHORTBITS - 1) / SHORTBITS) -#define CHAR_PER_LONG ((LONGBITS + CHARBITS - 1) / CHARBITS) +#define SHORT_PER_LONG ((unsigned)(((sizeof(long) + sizeof(short) - 1) / sizeof(short)))) +#define CHAR_PER_LONG ((unsigned)sizeof(long)) /* minimum and maximum sizes for an IntRep @@ -69,6 +75,10 @@ and this notice must be preserved on all copies. #define MALLOC_MIN_OVERHEAD 4 #endif +IntRep _ZeroRep = {1, 0, 1, {0}}; +IntRep _OneRep = {1, 0, 1, {1}}; +IntRep _MinusOneRep = {1, 0, 0, {1}}; + // utilities to extract and transfer bits @@ -180,7 +190,7 @@ IntRep* Ialloc(IntRep* old, const unsigned short* src, int srclen, int newsgn, scpy(src, rep->s, srclen); Iclear_from(rep, srclen); - if (old != rep && old != 0) delete old; + if (old != rep && old != 0 && !STATIC_IntRep(old)) delete old; return rep; } @@ -191,7 +201,7 @@ IntRep* Icalloc(IntRep* old, int newlen) IntRep* rep; if (old == 0 || newlen > old->sz) { - if (old != 0) delete old; + if (old != 0 && !STATIC_IntRep(old)) delete old; rep = Inew(newlen); } else @@ -224,7 +234,7 @@ IntRep* Iresize(IntRep* old, int newlen) rep = Inew(newlen); scpy(old->s, rep->s, oldlen); rep->sgn = old->sgn; - delete old; + if (!STATIC_IntRep(old)) delete old; } else rep = old; @@ -260,7 +270,7 @@ IntRep* Icopy(IntRep* old, const IntRep* src) int newlen = src->len; if (old == 0 || newlen > old->sz) { - if (old != 0) delete old; + if (old != 0 && !STATIC_IntRep(old)) delete old; rep = Inew(newlen); } else @@ -278,74 +288,69 @@ IntRep* Icopy(IntRep* old, const IntRep* src) // allocate & copy space for a long IntRep* Icopy_long(IntRep* old, long x) +{ + int newsgn = (x >= 0); + IntRep* rep = Icopy_ulong(old, newsgn ? x : -x); + rep->sgn = newsgn; + return rep; +} + +IntRep* Icopy_ulong(IntRep* old, unsigned long x) { unsigned short src[SHORT_PER_LONG]; - unsigned long u; - int newsgn; - if (newsgn = (x >= 0)) - u = x; - else - u = -x; unsigned short srclen = 0; - while (u != 0) + while (x != 0) { - src[srclen++] = extract(u); - u = down(u); + src[srclen++] = extract(x); + x = down(x); } IntRep* rep; if (old == 0 || srclen > old->sz) { - if (old != 0) delete old; + if (old != 0 && !STATIC_IntRep(old)) delete old; rep = Inew(srclen); } else rep = old; rep->len = srclen; - rep->sgn = newsgn; + rep->sgn = I_POSITIVE; scpy(src, rep->s, srclen); return rep; - } // special case for zero -- it's worth it! IntRep* Icopy_zero(IntRep* old) { - IntRep* rep; - if (old == 0) - rep = Inew(0); - else - rep = old; + if (old == 0 || STATIC_IntRep(old)) + return &_ZeroRep; - rep->len = 0; - rep->sgn = I_POSITIVE; + old->len = 0; + old->sgn = I_POSITIVE; - return rep; + return old; } // special case for 1 or -1 IntRep* Icopy_one(IntRep* old, int newsgn) { - IntRep* rep; if (old == 0 || 1 > old->sz) { - if (old != 0) delete old; - rep = Inew(1); + if (old != 0 && !STATIC_IntRep(old)) delete old; + return newsgn==I_NEGATIVE ? &_MinusOneRep : &_OneRep; } - else - rep = old; - rep->sgn = newsgn; - rep->len = 1; - rep->s[0] = 1; + old->sgn = newsgn; + old->len = 1; + old->s[0] = 1; - return rep; + return old; } // convert to a legal two's complement long if possible @@ -353,11 +358,11 @@ IntRep* Icopy_one(IntRep* old, int newsgn) long Itolong(const IntRep* rep) { - if (rep->len > SHORT_PER_LONG) - return (rep->sgn == I_POSITIVE) ? MAXLONG : MINLONG; + if ((unsigned)(rep->len) > (unsigned)(SHORT_PER_LONG)) + return (rep->sgn == I_POSITIVE) ? LONG_MAX : LONG_MIN; else if (rep->len == 0) return 0; - else if (rep->len < SHORT_PER_LONG) + else if ((unsigned)(rep->len) < (unsigned)(SHORT_PER_LONG)) { unsigned long a = rep->s[rep->len-1]; if (SHORT_PER_LONG > 2) // normally optimized out @@ -371,7 +376,7 @@ long Itolong(const IntRep* rep) { unsigned long a = rep->s[SHORT_PER_LONG - 1]; if (a >= I_MINNUM) - return (rep->sgn == I_POSITIVE) ? MAXLONG : MINLONG; + return (rep->sgn == I_POSITIVE) ? LONG_MAX : LONG_MIN; else { a = up(a) | rep->s[SHORT_PER_LONG - 2]; @@ -386,20 +391,20 @@ long Itolong(const IntRep* rep) } // test whether op long() will work. -// careful about asymmetry between MINLONG & MAXLONG +// careful about asymmetry between LONG_MIN & LONG_MAX int Iislong(const IntRep* rep) { - int l = rep->len; + unsigned int l = rep->len; if (l < SHORT_PER_LONG) return 1; else if (l > SHORT_PER_LONG) return 0; - else if (rep->s[SHORT_PER_LONG - 1] < I_MINNUM) + else if ((unsigned)(rep->s[SHORT_PER_LONG - 1]) < (unsigned)(I_MINNUM)) return 1; else if (rep->sgn == I_NEGATIVE && rep->s[SHORT_PER_LONG - 1] == I_MINNUM) { - for (int i = 0; i < SHORT_PER_LONG - 1; ++i) + for (unsigned int i = 0; i < SHORT_PER_LONG - 1; ++i) if (rep->s[i] != 0) return 0; return 1; @@ -413,14 +418,14 @@ int Iislong(const IntRep* rep) double Itodouble(const IntRep* rep) { double d = 0.0; - double bound = HUGE / 2.0; + double bound = DBL_MAX / 2.0; for (int i = rep->len - 1; i >= 0; --i) { unsigned short a = I_RADIX >> 1; while (a != 0) { if (d >= bound) - return (rep->sgn == I_NEGATIVE) ? -HUGE : HUGE; + return (rep->sgn == I_NEGATIVE) ? -HUGE_VAL : HUGE_VAL; d *= 2.0; if (rep->s[i] & a) d += 1.0; @@ -440,7 +445,7 @@ double Itodouble(const IntRep* rep) int Iisdouble(const IntRep* rep) { double d = 0.0; - double bound = HUGE / 2.0; + double bound = DBL_MAX / 2.0; for (int i = rep->len - 1; i >= 0; --i) { unsigned short a = I_RADIX >> 1; @@ -463,26 +468,26 @@ double ratio(const Integer& num, const Integer& den) { Integer q, r; divide(num, den, q, r); - double d1 = double(q); + double d1 = q.as_double(); - if (d1 == HUGE || d1 == -HUGE || sign(r) == 0) + if (d1 >= DBL_MAX || d1 <= -DBL_MAX || sign(r) == 0) return d1; else // use as much precision as available for fractional part { double d2 = 0.0; double d3 = 0.0; - double bound = HUGE / 2.0; int cont = 1; for (int i = den.rep->len - 1; i >= 0 && cont; --i) { unsigned short a = I_RADIX >> 1; while (a != 0) { - if (d2 >= bound) + if (d2 + 1.0 == d2) // out of precision when we get here { cont = 0; break; } + d2 *= 2.0; if (den.rep->s[i] & a) d2 += 1.0; @@ -493,6 +498,7 @@ double ratio(const Integer& num, const Integer& den) if (r.rep->s[i] & a) d3 += 1.0; } + a >>= 1; } } @@ -566,9 +572,9 @@ int compare(const IntRep* x, long y) if (diff == 0) diff = docmp(x->s, tmp, xl); } + if (xsgn == I_NEGATIVE) + diff = -diff; } - if (xsgn == I_NEGATIVE) - diff = -diff; return diff; } } @@ -1236,8 +1242,8 @@ IntRep* div(const IntRep* x, const IntRep* y, IntRep* q) q = Icalloc(q, ql); do_divide(r->s, yy->s, yl, q->s, ql); - if (yy != y) delete yy; - delete r; + if (yy != y && !STATIC_IntRep(yy)) delete yy; + if (!STATIC_IntRep(r)) delete r; } q->sgn = samesign; Icheck(q); @@ -1304,7 +1310,7 @@ IntRep* div(const IntRep* x, long y, IntRep* q) q = Icalloc(q, ql); do_divide(r->s, ys, yl, q->s, ql); - delete r; + if (!STATIC_IntRep(r)) delete r; } q->sgn = samesign; Icheck(q); @@ -1386,7 +1392,7 @@ void divide(const Integer& Ix, long y, Integer& Iq, long& rem) } Icheck(r); rem = Itolong(r); - delete r; + if (!STATIC_IntRep(r)) delete r; } rem = abs(rem); if (xsgn == I_NEGATIVE) rem = -rem; @@ -1455,7 +1461,7 @@ void divide(const Integer& Ix, const Integer& Iy, Integer& Iq, Integer& Ir) q = Icalloc(q, ql); do_divide(r->s, yy->s, yl, q->s, ql); - if (yy != y) delete yy; + if (yy != y && !STATIC_IntRep(yy)) delete yy; if (prescale != 1) { Icheck(r); @@ -1509,7 +1515,7 @@ IntRep* mod(const IntRep* x, const IntRep* y, IntRep* r) do_divide(r->s, yy->s, yl, 0, xl - yl + 1); - if (yy != y) delete yy; + if (yy != y && !STATIC_IntRep(yy)) delete yy; if (prescale != 1) { @@ -1810,19 +1816,15 @@ IntRep* compl(const IntRep* src, IntRep* r) return r; } -void setbit(Integer& x, long b) +void (setbit)(Integer& x, long b) { if (b >= 0) { int bw = (unsigned long)b / I_SHIFT; int sw = (unsigned long)b % I_SHIFT; - if (x.rep == 0) - x.rep = Icalloc(0, bw + 1); - else if (x.rep->len < bw) - { - int xl = x.rep->len; + int xl = x.rep ? x.rep->len : 0; + if (xl <= bw) x.rep = Iresize(x.rep, calc_len(xl, bw+1, 0)); - } x.rep->s[bw] |= (1 << sw); Icheck(x.rep); } @@ -1831,17 +1833,16 @@ void setbit(Integer& x, long b) void clearbit(Integer& x, long b) { if (b >= 0) - { - int bw = (unsigned long)b / I_SHIFT; - int sw = (unsigned long)b % I_SHIFT; - if (x.rep == 0) - x.rep = Icalloc(0, bw + 1); - else if (x.rep->len < bw) { - int xl = x.rep->len; - x.rep = Iresize(x.rep, calc_len(xl, bw+1, 0)); - } - x.rep->s[bw] &= ~(1 << sw); + if (x.rep == 0) + x.rep = &_ZeroRep; + else + { + int bw = (unsigned long)b / I_SHIFT; + int sw = (unsigned long)b % I_SHIFT; + if (x.rep->len > bw) + x.rep->s[bw] &= ~(1 << sw); + } Icheck(x.rep); } } @@ -1949,8 +1950,8 @@ IntRep* gcd(const IntRep* x, const IntRep* y) t = add(t, 0, u, 0, t); } } - delete t; - delete v; + if (!STATIC_IntRep(t)) delete t; + if (!STATIC_IntRep(v)) delete v; if (k != 0) u = lshift(u, k, u); return u; } @@ -2008,7 +2009,7 @@ IntRep* power(const IntRep* x, long y, IntRep* r) else b = multiply(b, b, b); } - delete b; + if (!STATIC_IntRep(b)) delete b; } r->sgn = sgn; Icheck(r); @@ -2151,9 +2152,48 @@ extern AllocRing _libgxx_fmtq; char* Itoa(const IntRep* x, int base, int width) { - int wrksiz = (x->len + 1) * I_SHIFT / lg(base) + 2 + width; - char* fmtbase = (char *) _libgxx_fmtq.alloc(wrksiz); - char* e = fmtbase + wrksiz - 1; + int fmtlen = (x->len + 1) * I_SHIFT / lg(base) + 4 + width; + char* fmtbase = (char *) _libgxx_fmtq.alloc(fmtlen); + char* f = cvtItoa(x, fmtbase, fmtlen, base, 0, width, 0, ' ', 'X', 0); + return f; +} + +ostream& operator << (ostream& s, const Integer& y) +{ +#ifdef _OLD_STREAMS + return s << Itoa(y.rep); +#else + if (s.opfx()) + { + int base = (s.flags() & ios::oct) ? 8 : (s.flags() & ios::hex) ? 16 : 10; + int width = s.width(); + y.printon(s, base, width); + } + return s; +#endif +} + +void Integer::printon(ostream& s, int base /* =10 */, int width /* =0 */) const +{ + int align_right = !(s.flags() & ios::left); + int showpos = s.flags() & ios::showpos; + int showbase = s.flags() & ios::showbase; + char fillchar = s.fill(); + char Xcase = (s.flags() & ios::uppercase)? 'X' : 'x'; + const IntRep* x = rep; + int fmtlen = (x->len + 1) * I_SHIFT / lg(base) + 4 + width; + char* fmtbase = new char[fmtlen]; + char* f = cvtItoa(x, fmtbase, fmtlen, base, showbase, width, align_right, + fillchar, Xcase, showpos); + s.write(f, fmtlen); + delete fmtbase; +} + +char* cvtItoa(const IntRep* x, char* fmt, int& fmtlen, int base, int showbase, + int width, int align_right, char fillchar, char Xcase, + int showpos) +{ + char* e = fmt + fmtlen - 1; char* s = e; *--s = 0; @@ -2192,7 +2232,7 @@ char* Itoa(const IntRep* x, int base, int width) ch += '0'; *--s = ch; } - delete z; + if (!STATIC_IntRep(z)) delete z; break; } else @@ -2211,11 +2251,33 @@ char* Itoa(const IntRep* x, int base, int width) } } + if (base == 8 && showbase) + *--s = '0'; + else if (base == 16 && showbase) + { + *--s = Xcase; + *--s = '0'; + } if (x->sgn == I_NEGATIVE) *--s = '-'; + else if (showpos) *--s = '+'; int w = e - s - 1; - while (w++ < width) - *--s = ' '; - return s; + if (!align_right || w >= width) + { + while (w++ < width) + *--s = fillchar; + fmtlen = e - s - 1; + return s; + } + else + { + char* p = fmt; + int gap = s - p; + for (char* t = s; *t != 0; ++t, ++p) *p = *t; + while (w++ < width) *p++ = fillchar; + *p = 0; + fmtlen = p - fmt; + return fmt; + } } char* dec(const Integer& x, int width) @@ -2235,24 +2297,36 @@ char* hex(const Integer& x, int width) istream& operator >> (istream& s, Integer& y) { -//#ifndef VMS // is this really needed? I think not. - if (!s.readable()) +#ifdef _OLD_STREAMS + if (!s.good()) + return s; +#else + if (!s.ipfx(0)) + { + s.clear(ios::failbit|s.rdstate()); + return s; + } +#endif + s >> ws; + if (!s.good()) { - s.set(_bad); + s.clear(ios::failbit|s.rdstate()); return s; } -//#endif + +#ifdef _OLD_STREAMS + int know_base = 0; + int base = 10; +#else + int know_base = s.flags() & (ios::oct | ios::hex | ios::dec); + int base = (s.flags() & ios::oct) ? 8 : (s.flags() & ios::hex) ? 16 : 10; +#endif int got_one = 0; char sgn = 0; char ch; y.rep = Icopy_zero(y.rep); - s >> WS; - if (!s.good()) - { - s.set(_bad); - return s; - } + while (s.get(ch)) { if (ch == '-') @@ -2262,20 +2336,61 @@ istream& operator >> (istream& s, Integer& y) else break; } - else if (ch >= '0' && ch <= '9') + else if (!know_base & !got_one && ch == '0') + base = 8, got_one = 1; + else if (!know_base & !got_one && base == 8 && (ch == 'X' || ch == 'x')) + base = 16; + else if (base == 8) + { + if (ch >= '0' && ch <= '7') + { + long digit = ch - '0'; + y <<= 3; + y += digit; + got_one = 1; + } + else + break; + } + else if (base == 16) + { + long digit; + if (ch >= '0' && ch <= '9') + digit = ch - '0'; + else if (ch >= 'A' && ch <= 'F') + digit = ch - 'A' + 10; + else if (ch >= 'a' && ch <= 'f') + digit = ch - 'a' + 10; + else + digit = base; + if (digit < base) + { + y <<= 4; + y += digit; + got_one = 1; + } + else + break; + } + else if (base == 10) { - long digit = ch - '0'; - y *= 10; - y += digit; - got_one = 1; + if (ch >= '0' && ch <= '9') + { + long digit = ch - '0'; + y *= 10; + y += digit; + got_one = 1; + } + else + break; } else - break; + abort(); // can't happen for now } if (s.good()) - s.unget(ch); + s.putback(ch); if (!got_one) - s.set(_bad); + s.clear(ios::failbit|s.rdstate()); if (sgn == '-') y.negate(); @@ -2285,19 +2400,23 @@ istream& operator >> (istream& s, Integer& y) int Integer::OK() const { - int v = rep != 0; // have a rep - int l = rep->len; - int s = rep->sgn; - v &= l <= rep->sz; // length with bounds - v &= s == 0 || s == 1; // legal sign - Icheck(rep); // and correctly adjusted - v &= rep->len == l; - v &= rep->sgn == s; - if (!v) error("invariant failure"); - return v; + if (rep != 0) + { + int l = rep->len; + int s = rep->sgn; + int v = l <= rep->sz || STATIC_IntRep(rep); // length within bounds + v &= s == 0 || s == 1; // legal sign + Icheck(rep); // and correctly adjusted + v &= rep->len == l; + v &= rep->sgn == s; + if (v) + return v; + } + error("invariant failure"); + return 0; } -volatile void Integer::error(const char* msg) const +void Integer::error(const char* msg) const { (*lib_error_handler)("Integer", msg); } diff --git a/gnu/lib/libg++/libg++/Integer.h b/gnu/lib/libg++/libg++/Integer.h new file mode 100644 index 0000000000..d96d5b1975 --- /dev/null +++ b/gnu/lib/libg++/libg++/Integer.h @@ -0,0 +1,1103 @@ +// This may look like C code, but it is really -*- C++ -*- + +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _Integer_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Integer_h 1 + +#include + +struct IntRep // internal Integer representations +{ + unsigned short len; // current length + unsigned short sz; // allocated space (0 means static). + short sgn; // 1 means >= 0; 0 means < 0 + unsigned short s[1]; // represented as ushort array starting here +}; + +// True if REP is staticly (or manually) allocated, +// and should not be deleted by an Integer destructor. +#define STATIC_IntRep(rep) ((rep)->sz==0) + +extern IntRep* Ialloc(IntRep*, const unsigned short *, int, int, int); +extern IntRep* Icalloc(IntRep*, int); +extern IntRep* Icopy_ulong(IntRep*, unsigned long); +extern IntRep* Icopy_long(IntRep*, long); +extern IntRep* Icopy(IntRep*, const IntRep*); +extern IntRep* Iresize(IntRep*, int); +extern IntRep* add(const IntRep*, int, const IntRep*, int, IntRep*); +extern IntRep* add(const IntRep*, int, long, IntRep*); +extern IntRep* multiply(const IntRep*, const IntRep*, IntRep*); +extern IntRep* multiply(const IntRep*, long, IntRep*); +extern IntRep* lshift(const IntRep*, long, IntRep*); +extern IntRep* lshift(const IntRep*, const IntRep*, int, IntRep*); +extern IntRep* bitop(const IntRep*, const IntRep*, IntRep*, char); +extern IntRep* bitop(const IntRep*, long, IntRep*, char); +extern IntRep* power(const IntRep*, long, IntRep*); +extern IntRep* div(const IntRep*, const IntRep*, IntRep*); +extern IntRep* mod(const IntRep*, const IntRep*, IntRep*); +extern IntRep* div(const IntRep*, long, IntRep*); +extern IntRep* mod(const IntRep*, long, IntRep*); +extern IntRep* compl(const IntRep*, IntRep*); +extern IntRep* abs(const IntRep*, IntRep*); +extern IntRep* negate(const IntRep*, IntRep*); +extern IntRep* pow(const IntRep*, long); +extern IntRep* gcd(const IntRep*, const IntRep* y); +extern int compare(const IntRep*, const IntRep*); +extern int compare(const IntRep*, long); +extern int ucompare(const IntRep*, const IntRep*); +extern int ucompare(const IntRep*, long); +extern char* Itoa(const IntRep* x, int base = 10, int width = 0); +extern char* cvtItoa(const IntRep* x, char* fmt, int& fmtlen, int base, + int showbase, int width, int align_right, + char fillchar, char Xcase, int showpos); +extern IntRep* atoIntRep(const char* s, int base = 10); +extern long Itolong(const IntRep*); +extern double Itodouble(const IntRep*); +extern int Iislong(const IntRep*); +extern int Iisdouble(const IntRep*); +extern long lg(const IntRep*); + +extern IntRep _ZeroRep, _OneRep, _MinusOneRep; + +class Integer +{ +protected: + IntRep* rep; +public: + Integer(); + Integer(int); + Integer(long); + Integer(unsigned long); + Integer(IntRep*); + Integer(const Integer&); + + ~Integer(); + + void operator = (const Integer&); + void operator = (long); + +// unary operations to self + + void operator ++ (); + void operator -- (); + void negate(); // negate in-place + void abs(); // absolute-value in-place + void complement(); // bitwise complement in-place + +// assignment-based operations + + void operator += (const Integer&); + void operator -= (const Integer&); + void operator *= (const Integer&); + void operator /= (const Integer&); + void operator %= (const Integer&); + void operator <<=(const Integer&); + void operator >>=(const Integer&); + void operator &= (const Integer&); + void operator |= (const Integer&); + void operator ^= (const Integer&); + + void operator += (long); + void operator -= (long); + void operator *= (long); + void operator /= (long); + void operator %= (long); + void operator <<=(long); + void operator >>=(long); + void operator &= (long); + void operator |= (long); + void operator ^= (long); + +// (constructive binary operations are inlined below) + +#ifdef __GNUG__ + friend Integer operator ? (const Integer& x, const Integer& y); // max +#endif + +// builtin Integer functions that must be friends + + friend long lg (const Integer&); // floor log base 2 of abs(x) + friend double ratio(const Integer& x, const Integer& y); + // return x/y as a double + + friend Integer gcd(const Integer&, const Integer&); + friend int even(const Integer&); // true if even + friend int odd(const Integer&); // true if odd + friend int sign(const Integer&); // returns -1, 0, +1 + + friend void (setbit)(Integer& x, long b); // set b'th bit of x + friend void clearbit(Integer& x, long b); // clear b'th bit + friend int testbit(const Integer& x, long b); // return b'th bit + +// procedural versions of operators + + friend void abs(const Integer& x, Integer& dest); + friend void negate(const Integer& x, Integer& dest); + friend void complement(const Integer& x, Integer& dest); + + friend int compare(const Integer&, const Integer&); + friend int ucompare(const Integer&, const Integer&); + friend void add(const Integer& x, const Integer& y, Integer& dest); + friend void sub(const Integer& x, const Integer& y, Integer& dest); + friend void mul(const Integer& x, const Integer& y, Integer& dest); + friend void div(const Integer& x, const Integer& y, Integer& dest); + friend void mod(const Integer& x, const Integer& y, Integer& dest); + friend void divide(const Integer& x, const Integer& y, + Integer& q, Integer& r); + friend void and(const Integer& x, const Integer& y, Integer& dest); + friend void or(const Integer& x, const Integer& y, Integer& dest); + friend void xor(const Integer& x, const Integer& y, Integer& dest); + friend void lshift(const Integer& x, const Integer& y, Integer& dest); + friend void rshift(const Integer& x, const Integer& y, Integer& dest); + friend void pow(const Integer& x, const Integer& y, Integer& dest); + + friend int compare(const Integer&, long); + friend int ucompare(const Integer&, long); + friend void add(const Integer& x, long y, Integer& dest); + friend void sub(const Integer& x, long y, Integer& dest); + friend void mul(const Integer& x, long y, Integer& dest); + friend void div(const Integer& x, long y, Integer& dest); + friend void mod(const Integer& x, long y, Integer& dest); + friend void divide(const Integer& x, long y, Integer& q, long& r); + friend void and(const Integer& x, long y, Integer& dest); + friend void or(const Integer& x, long y, Integer& dest); + friend void xor(const Integer& x, long y, Integer& dest); + friend void lshift(const Integer& x, long y, Integer& dest); + friend void rshift(const Integer& x, long y, Integer& dest); + friend void pow(const Integer& x, long y, Integer& dest); + + friend int compare(long, const Integer&); + friend int ucompare(long, const Integer&); + friend void add(long x, const Integer& y, Integer& dest); + friend void sub(long x, const Integer& y, Integer& dest); + friend void mul(long x, const Integer& y, Integer& dest); + friend void and(long x, const Integer& y, Integer& dest); + friend void or(long x, const Integer& y, Integer& dest); + friend void xor(long x, const Integer& y, Integer& dest); + +// coercion & conversion + + int fits_in_long() const { return Iislong(rep); } + int fits_in_double() const { return Iisdouble(rep); } + +#if 0 + // There two operators cause a number of ambiguities. + operator long() const { return Itolong(rep); } + operator double() const { return Itodouble(rep); } +#endif + long as_long() const { return Itolong(rep); } + double as_double() const { return Itodouble(rep); } + + friend char* Itoa(const Integer& x, int base = 10, int width = 0); + friend Integer atoI(const char* s, int base = 10); + void printon(ostream& s, int base = 10, int width = 0) const; + + friend istream& operator >> (istream& s, Integer& y); + friend ostream& operator << (ostream& s, const Integer& y); + +// error detection + + int initialized() const; + void error(const char* msg) const; + int OK() const; +}; + + +// (These are declared inline) + + int operator == (const Integer&, const Integer&); + int operator == (const Integer&, long); + int operator != (const Integer&, const Integer&); + int operator != (const Integer&, long); + int operator < (const Integer&, const Integer&); + int operator < (const Integer&, long); + int operator <= (const Integer&, const Integer&); + int operator <= (const Integer&, long); + int operator > (const Integer&, const Integer&); + int operator > (const Integer&, long); + int operator >= (const Integer&, const Integer&); + int operator >= (const Integer&, long); + Integer operator - (const Integer&); + Integer operator ~ (const Integer&); + Integer operator + (const Integer&, const Integer&); + Integer operator + (const Integer&, long); + Integer operator + (long, const Integer&); + Integer operator - (const Integer&, const Integer&); + Integer operator - (const Integer&, long); + Integer operator - (long, const Integer&); + Integer operator * (const Integer&, const Integer&); + Integer operator * (const Integer&, long); + Integer operator * (long, const Integer&); + Integer operator / (const Integer&, const Integer&); + Integer operator / (const Integer&, long); + Integer operator % (const Integer&, const Integer&); + Integer operator % (const Integer&, long); + Integer operator << (const Integer&, const Integer&); + Integer operator << (const Integer&, long); + Integer operator >> (const Integer&, const Integer&); + Integer operator >> (const Integer&, long); + Integer operator & (const Integer&, const Integer&); + Integer operator & (const Integer&, long); + Integer operator & (long, const Integer&); + Integer operator | (const Integer&, const Integer&); + Integer operator | (const Integer&, long); + Integer operator | (long, const Integer&); + Integer operator ^ (const Integer&, const Integer&); + Integer operator ^ (const Integer&, long); + Integer operator ^ (long, const Integer&); + + Integer abs(const Integer&); // absolute value + Integer sqr(const Integer&); // square + + Integer pow(const Integer& x, const Integer& y); + Integer pow(const Integer& x, long y); + Integer Ipow(long x, long y); // x to the y as Integer + + +extern char* dec(const Integer& x, int width = 0); +extern char* oct(const Integer& x, int width = 0); +extern char* hex(const Integer& x, int width = 0); +extern Integer sqrt(const Integer&); // floor of square root +extern Integer lcm(const Integer& x, const Integer& y); // least common mult + + +typedef Integer IntTmp; // for backward compatibility + +inline Integer::Integer() :rep(&_ZeroRep) {} + +inline Integer::Integer(IntRep* r) :rep(r) {} + +inline Integer::Integer(int y) :rep(Icopy_long(0, (long)y)) {} + +inline Integer::Integer(long y) :rep(Icopy_long(0, y)) {} + +inline Integer::Integer(unsigned long y) :rep(Icopy_ulong(0, y)) {} + +inline Integer::Integer(const Integer& y) :rep(Icopy(0, y.rep)) {} + +inline Integer::~Integer() { if (rep && !STATIC_IntRep(rep)) delete rep; } + +inline void Integer::operator = (const Integer& y) +{ + rep = Icopy(rep, y.rep); +} + +inline void Integer::operator = (long y) +{ + rep = Icopy_long(rep, y); +} + +inline int Integer::initialized() const +{ + return rep != 0; +} + +// procedural versions + +inline int compare(const Integer& x, const Integer& y) +{ + return compare(x.rep, y.rep); +} + +inline int ucompare(const Integer& x, const Integer& y) +{ + return ucompare(x.rep, y.rep); +} + +inline int compare(const Integer& x, long y) +{ + return compare(x.rep, y); +} + +inline int ucompare(const Integer& x, long y) +{ + return ucompare(x.rep, y); +} + +inline int compare(long x, const Integer& y) +{ + return -compare(y.rep, x); +} + +inline int ucompare(long x, const Integer& y) +{ + return -ucompare(y.rep, x); +} + +inline void add(const Integer& x, const Integer& y, Integer& dest) +{ + dest.rep = add(x.rep, 0, y.rep, 0, dest.rep); +} + +inline void sub(const Integer& x, const Integer& y, Integer& dest) +{ + dest.rep = add(x.rep, 0, y.rep, 1, dest.rep); +} + +inline void mul(const Integer& x, const Integer& y, Integer& dest) +{ + dest.rep = multiply(x.rep, y.rep, dest.rep); +} + +inline void div(const Integer& x, const Integer& y, Integer& dest) +{ + dest.rep = div(x.rep, y.rep, dest.rep); +} + +inline void mod(const Integer& x, const Integer& y, Integer& dest) +{ + dest.rep = mod(x.rep, y.rep, dest.rep); +} + +inline void and(const Integer& x, const Integer& y, Integer& dest) +{ + dest.rep = bitop(x.rep, y.rep, dest.rep, '&'); +} + +inline void or(const Integer& x, const Integer& y, Integer& dest) +{ + dest.rep = bitop(x.rep, y.rep, dest.rep, '|'); +} + +inline void xor(const Integer& x, const Integer& y, Integer& dest) +{ + dest.rep = bitop(x.rep, y.rep, dest.rep, '^'); +} + +inline void lshift(const Integer& x, const Integer& y, Integer& dest) +{ + dest.rep = lshift(x.rep, y.rep, 0, dest.rep); +} + +inline void rshift(const Integer& x, const Integer& y, Integer& dest) +{ + dest.rep = lshift(x.rep, y.rep, 1, dest.rep); +} + +inline void pow(const Integer& x, const Integer& y, Integer& dest) +{ + dest.rep = power(x.rep, Itolong(y.rep), dest.rep); // not incorrect +} + +inline void add(const Integer& x, long y, Integer& dest) +{ + dest.rep = add(x.rep, 0, y, dest.rep); +} + +inline void sub(const Integer& x, long y, Integer& dest) +{ + dest.rep = add(x.rep, 0, -y, dest.rep); +} + +inline void mul(const Integer& x, long y, Integer& dest) +{ + dest.rep = multiply(x.rep, y, dest.rep); +} + +inline void div(const Integer& x, long y, Integer& dest) +{ + dest.rep = div(x.rep, y, dest.rep); +} + +inline void mod(const Integer& x, long y, Integer& dest) +{ + dest.rep = mod(x.rep, y, dest.rep); +} + +inline void and(const Integer& x, long y, Integer& dest) +{ + dest.rep = bitop(x.rep, y, dest.rep, '&'); +} + +inline void or(const Integer& x, long y, Integer& dest) +{ + dest.rep = bitop(x.rep, y, dest.rep, '|'); +} + +inline void xor(const Integer& x, long y, Integer& dest) +{ + dest.rep = bitop(x.rep, y, dest.rep, '^'); +} + +inline void lshift(const Integer& x, long y, Integer& dest) +{ + dest.rep = lshift(x.rep, y, dest.rep); +} + +inline void rshift(const Integer& x, long y, Integer& dest) +{ + dest.rep = lshift(x.rep, -y, dest.rep); +} + +inline void pow(const Integer& x, long y, Integer& dest) +{ + dest.rep = power(x.rep, y, dest.rep); +} + +inline void abs(const Integer& x, Integer& dest) +{ + dest.rep = abs(x.rep, dest.rep); +} + +inline void negate(const Integer& x, Integer& dest) +{ + dest.rep = negate(x.rep, dest.rep); +} + +inline void complement(const Integer& x, Integer& dest) +{ + dest.rep = compl(x.rep, dest.rep); +} + +inline void add(long x, const Integer& y, Integer& dest) +{ + dest.rep = add(y.rep, 0, x, dest.rep); +} + +inline void sub(long x, const Integer& y, Integer& dest) +{ + dest.rep = add(y.rep, 1, x, dest.rep); +} + +inline void mul(long x, const Integer& y, Integer& dest) +{ + dest.rep = multiply(y.rep, x, dest.rep); +} + +inline void and(long x, const Integer& y, Integer& dest) +{ + dest.rep = bitop(y.rep, x, dest.rep, '&'); +} + +inline void or(long x, const Integer& y, Integer& dest) +{ + dest.rep = bitop(y.rep, x, dest.rep, '|'); +} + +inline void xor(long x, const Integer& y, Integer& dest) +{ + dest.rep = bitop(y.rep, x, dest.rep, '^'); +} + + +// operator versions + +inline int operator == (const Integer& x, const Integer& y) +{ + return compare(x, y) == 0; +} + +inline int operator == (const Integer& x, long y) +{ + return compare(x, y) == 0; +} + +inline int operator != (const Integer& x, const Integer& y) +{ + return compare(x, y) != 0; +} + +inline int operator != (const Integer& x, long y) +{ + return compare(x, y) != 0; +} + +inline int operator < (const Integer& x, const Integer& y) +{ + return compare(x, y) < 0; +} + +inline int operator < (const Integer& x, long y) +{ + return compare(x, y) < 0; +} + +inline int operator <= (const Integer& x, const Integer& y) +{ + return compare(x, y) <= 0; +} + +inline int operator <= (const Integer& x, long y) +{ + return compare(x, y) <= 0; +} + +inline int operator > (const Integer& x, const Integer& y) +{ + return compare(x, y) > 0; +} + +inline int operator > (const Integer& x, long y) +{ + return compare(x, y) > 0; +} + +inline int operator >= (const Integer& x, const Integer& y) +{ + return compare(x, y) >= 0; +} + +inline int operator >= (const Integer& x, long y) +{ + return compare(x, y) >= 0; +} + + +inline void Integer::operator += (const Integer& y) +{ + add(*this, y, *this); +} + +inline void Integer::operator += (long y) +{ + add(*this, y, *this); +} + +inline void Integer::operator ++ () +{ + add(*this, 1, *this); +} + + +inline void Integer::operator -= (const Integer& y) +{ + sub(*this, y, *this); +} + +inline void Integer::operator -= (long y) +{ + sub(*this, y, *this); +} + +inline void Integer::operator -- () +{ + add(*this, -1, *this); +} + + + +inline void Integer::operator *= (const Integer& y) +{ + mul(*this, y, *this); +} + +inline void Integer::operator *= (long y) +{ + mul(*this, y, *this); +} + + +inline void Integer::operator &= (const Integer& y) +{ + and(*this, y, *this); +} + +inline void Integer::operator &= (long y) +{ + and(*this, y, *this); +} + +inline void Integer::operator |= (const Integer& y) +{ + or(*this, y, *this); +} + +inline void Integer::operator |= (long y) +{ + or(*this, y, *this); +} + + +inline void Integer::operator ^= (const Integer& y) +{ + xor(*this, y, *this); +} + +inline void Integer::operator ^= (long y) +{ + xor(*this, y, *this); +} + + + +inline void Integer::operator /= (const Integer& y) +{ + div(*this, y, *this); +} + +inline void Integer::operator /= (long y) +{ + div(*this, y, *this); +} + + +inline void Integer::operator <<= (const Integer& y) +{ + lshift(*this, y, *this); +} + +inline void Integer::operator <<= (long y) +{ + lshift(*this, y, *this); +} + + +inline void Integer::operator >>= (const Integer& y) +{ + rshift(*this, y, *this); +} + +inline void Integer::operator >>= (long y) +{ + rshift(*this, y, *this); +} + +#ifdef __GNUG__ +inline Integer operator ? (const Integer& x, const Integer& y) +{ + return (compare(x.rep, y.rep) >= 0)? x : y; +} +#endif + + +inline void Integer::abs() +{ + ::abs(*this, *this); +} + +inline void Integer::negate() +{ + ::negate(*this, *this); +} + + +inline void Integer::complement() +{ + ::complement(*this, *this); +} + + +inline int sign(const Integer& x) +{ + return (x.rep->len == 0) ? 0 : ( (x.rep->sgn == 1) ? 1 : -1 ); +} + +inline int even(const Integer& y) +{ + return y.rep->len == 0 || !(y.rep->s[0] & 1); +} + +inline int odd(const Integer& y) +{ + return y.rep->len > 0 && (y.rep->s[0] & 1); +} + +inline char* Itoa(const Integer& y, int base, int width) +{ + return Itoa(y.rep, base, width); +} + + + +inline long lg(const Integer& x) +{ + return lg(x.rep); +} + +// constructive operations + +#if defined(__GNUG__) && !defined(NO_NRV) + +inline Integer operator + (const Integer& x, const Integer& y) return r +{ + add(x, y, r); +} + +inline Integer operator + (const Integer& x, long y) return r +{ + add(x, y, r); +} + +inline Integer operator + (long x, const Integer& y) return r +{ + add(x, y, r); +} + +inline Integer operator - (const Integer& x, const Integer& y) return r +{ + sub(x, y, r); +} + +inline Integer operator - (const Integer& x, long y) return r +{ + sub(x, y, r); +} + +inline Integer operator - (long x, const Integer& y) return r +{ + sub(x, y, r); +} + +inline Integer operator * (const Integer& x, const Integer& y) return r +{ + mul(x, y, r); +} + +inline Integer operator * (const Integer& x, long y) return r +{ + mul(x, y, r); +} + +inline Integer operator * (long x, const Integer& y) return r +{ + mul(x, y, r); +} + +inline Integer sqr(const Integer& x) return r +{ + mul(x, x, r); +} + +inline Integer operator & (const Integer& x, const Integer& y) return r +{ + and(x, y, r); +} + +inline Integer operator & (const Integer& x, long y) return r +{ + and(x, y, r); +} + +inline Integer operator & (long x, const Integer& y) return r +{ + and(x, y, r); +} + +inline Integer operator | (const Integer& x, const Integer& y) return r +{ + or(x, y, r); +} + +inline Integer operator | (const Integer& x, long y) return r +{ + or(x, y, r); +} + +inline Integer operator | (long x, const Integer& y) return r +{ + or(x, y, r); +} + +inline Integer operator ^ (const Integer& x, const Integer& y) return r +{ + xor(x, y, r); +} + +inline Integer operator ^ (const Integer& x, long y) return r +{ + xor(x, y, r); +} + +inline Integer operator ^ (long x, const Integer& y) return r +{ + xor(x, y, r); +} + +inline Integer operator / (const Integer& x, const Integer& y) return r +{ + div(x, y, r); +} + +inline Integer operator / (const Integer& x, long y) return r +{ + div(x, y, r); +} + +inline Integer operator % (const Integer& x, const Integer& y) return r +{ + mod(x, y, r); +} + +inline Integer operator % (const Integer& x, long y) return r +{ + mod(x, y, r); +} + +inline Integer operator << (const Integer& x, const Integer& y) return r +{ + lshift(x, y, r); +} + +inline Integer operator << (const Integer& x, long y) return r +{ + lshift(x, y, r); +} + +inline Integer operator >> (const Integer& x, const Integer& y) return r; +{ + rshift(x, y, r); +} + +inline Integer operator >> (const Integer& x, long y) return r +{ + rshift(x, y, r); +} + +inline Integer pow(const Integer& x, long y) return r +{ + pow(x, y, r); +} + +inline Integer Ipow(long x, long y) return r(x) +{ + pow(r, y, r); +} + +inline Integer pow(const Integer& x, const Integer& y) return r +{ + pow(x, y, r); +} + + + +inline Integer abs(const Integer& x) return r +{ + abs(x, r); +} + +inline Integer operator - (const Integer& x) return r +{ + negate(x, r); +} + +inline Integer operator ~ (const Integer& x) return r +{ + complement(x, r); +} + +inline Integer atoI(const char* s, int base) return r +{ + r.rep = atoIntRep(s, base); +} + +inline Integer gcd(const Integer& x, const Integer& y) return r +{ + r.rep = gcd(x.rep, y.rep); +} + +#else /* NO_NRV */ + +inline Integer operator + (const Integer& x, const Integer& y) +{ + Integer r; add(x, y, r); return r; +} + +inline Integer operator + (const Integer& x, long y) +{ + Integer r; add(x, y, r); return r; +} + +inline Integer operator + (long x, const Integer& y) +{ + Integer r; add(x, y, r); return r; +} + +inline Integer operator - (const Integer& x, const Integer& y) +{ + Integer r; sub(x, y, r); return r; +} + +inline Integer operator - (const Integer& x, long y) +{ + Integer r; sub(x, y, r); return r; +} + +inline Integer operator - (long x, const Integer& y) +{ + Integer r; sub(x, y, r); return r; +} + +inline Integer operator * (const Integer& x, const Integer& y) +{ + Integer r; mul(x, y, r); return r; +} + +inline Integer operator * (const Integer& x, long y) +{ + Integer r; mul(x, y, r); return r; +} + +inline Integer operator * (long x, const Integer& y) +{ + Integer r; mul(x, y, r); return r; +} + +inline Integer sqr(const Integer& x) +{ + Integer r; mul(x, x, r); return r; +} + +inline Integer operator & (const Integer& x, const Integer& y) +{ + Integer r; and(x, y, r); return r; +} + +inline Integer operator & (const Integer& x, long y) +{ + Integer r; and(x, y, r); return r; +} + +inline Integer operator & (long x, const Integer& y) +{ + Integer r; and(x, y, r); return r; +} + +inline Integer operator | (const Integer& x, const Integer& y) +{ + Integer r; or(x, y, r); return r; +} + +inline Integer operator | (const Integer& x, long y) +{ + Integer r; or(x, y, r); return r; +} + +inline Integer operator | (long x, const Integer& y) +{ + Integer r; or(x, y, r); return r; +} + +inline Integer operator ^ (const Integer& x, const Integer& y) +{ + Integer r; xor(x, y, r); return r; +} + +inline Integer operator ^ (const Integer& x, long y) +{ + Integer r; xor(x, y, r); return r; +} + +inline Integer operator ^ (long x, const Integer& y) +{ + Integer r; xor(x, y, r); return r; +} + +inline Integer operator / (const Integer& x, const Integer& y) +{ + Integer r; div(x, y, r); return r; +} + +inline Integer operator / (const Integer& x, long y) +{ + Integer r; div(x, y, r); return r; +} + +inline Integer operator % (const Integer& x, const Integer& y) +{ + Integer r; mod(x, y, r); return r; +} + +inline Integer operator % (const Integer& x, long y) +{ + Integer r; mod(x, y, r); return r; +} + +inline Integer operator << (const Integer& x, const Integer& y) +{ + Integer r; lshift(x, y, r); return r; +} + +inline Integer operator << (const Integer& x, long y) +{ + Integer r; lshift(x, y, r); return r; +} + +inline Integer operator >> (const Integer& x, const Integer& y) +{ + Integer r; rshift(x, y, r); return r; +} + +inline Integer operator >> (const Integer& x, long y) +{ + Integer r; rshift(x, y, r); return r; +} + +inline Integer pow(const Integer& x, long y) +{ + Integer r; pow(x, y, r); return r; +} + +inline Integer Ipow(long x, long y) +{ + Integer r(x); pow(r, y, r); return r; +} + +inline Integer pow(const Integer& x, const Integer& y) +{ + Integer r; pow(x, y, r); return r; +} + + + +inline Integer abs(const Integer& x) +{ + Integer r; abs(x, r); return r; +} + +inline Integer operator - (const Integer& x) +{ + Integer r; negate(x, r); return r; +} + +inline Integer operator ~ (const Integer& x) +{ + Integer r; complement(x, r); return r; +} + +inline Integer atoI(const char* s, int base) +{ + Integer r; r.rep = atoIntRep(s, base); return r; +} + +inline Integer gcd(const Integer& x, const Integer& y) +{ + Integer r; r.rep = gcd(x.rep, y.rep); return r; +} + +#endif /* NO_NRV */ + +inline void Integer::operator %= (const Integer& y) +{ + *this = *this % y; // mod(*this, y, *this) doesn't work. +} + +inline void Integer::operator %= (long y) +{ + *this = *this % y; // mod(*this, y, *this) doesn't work. +} +#endif /* !_Integer_h */ diff --git a/gnu/lib/libg++/libg++/LogNorm.cc b/gnu/lib/libg++/libg++/LogNorm.cc index d08e6a2646..8adc86cbdb 100644 --- a/gnu/lib/libg++/libg++/LogNorm.cc +++ b/gnu/lib/libg++/libg++/LogNorm.cc @@ -2,29 +2,23 @@ Copyright (C) 1988 Free Software Foundation written by Dirk Grunwald (grunwald@cs.uiuc.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ #pragma implementation #endif #include #include - #include #include diff --git a/gnu/lib/libg++/libg++/LogNorm.h b/gnu/lib/libg++/libg++/LogNorm.h new file mode 100644 index 0000000000..ac98141e62 --- /dev/null +++ b/gnu/lib/libg++/libg++/LogNorm.h @@ -0,0 +1,78 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef _LogNormal_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _LogNormal_h + +#include + +class LogNormal: public Normal { +protected: + double logMean; + double logVariance; + void setState(); +public: + LogNormal(double mean, double variance, RNG *gen); + double mean(); + double mean(double x); + double variance(); + double variance(double x); + virtual double operator()(); +}; + + +inline void LogNormal::setState() +{ + double m2 = logMean * logMean; + pMean = log(m2 / sqrt(logVariance + m2) ); +// from ch@heike.informatik.uni-dortmund.de: +// (was pVariance = log((sqrt(logVariance + m2)/m2 )); ) + pStdDev = sqrt(log((logVariance + m2)/m2 )); +} + +inline LogNormal::LogNormal(double mean, double variance, RNG *gen) + : Normal(mean, variance, gen) +{ + logMean = mean; + logVariance = variance; + setState(); +} + +inline double LogNormal::mean() { + return logMean; +} + +inline double LogNormal::mean(double x) +{ + double t=logMean; logMean = x; setState(); + return t; +} + +inline double LogNormal::variance() { + return logVariance; +} + +inline double LogNormal::variance(double x) +{ + double t=logVariance; logVariance = x; setState(); + return t; +} + +#endif diff --git a/gnu/lib/libg++/libg++/MLCG.cc b/gnu/lib/libg++/libg++/MLCG.cc index b3023ef15d..74836c65af 100644 --- a/gnu/lib/libg++/libg++/MLCG.cc +++ b/gnu/lib/libg++/libg++/MLCG.cc @@ -1,7 +1,23 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1989 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ #ifdef __GNUG__ #pragma implementation #endif -#include "MLCG.h" +#include // // SEED_TABLE_SIZE must be a power of 2 // diff --git a/gnu/lib/libg++/libg++/MLCG.h b/gnu/lib/libg++/libg++/MLCG.h new file mode 100644 index 0000000000..afa5f2c9db --- /dev/null +++ b/gnu/lib/libg++/libg++/MLCG.h @@ -0,0 +1,87 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef _MLCG_h +#define _MLCG_h 1 +#ifdef __GNUG__ +#pragma interface +#endif + +#include +#include + +// +// Multiplicative Linear Conguential Generator +// + +class MLCG : public RNG { + long initialSeedOne; + long initialSeedTwo; + long seedOne; + long seedTwo; + +protected: + +public: + MLCG(long seed1 = 0, long seed2 = 1); + // + // Return a long-words word of random bits + // + virtual unsigned long asLong(); + virtual void reset(); + long seed1(); + void seed1(long); + long seed2(); + void seed2(long); + void reseed(long, long); +}; + +inline long +MLCG::seed1() +{ + return(seedOne); +} + +inline void +MLCG::seed1(long s) +{ + initialSeedOne = s; + reset(); +} + +inline long +MLCG::seed2() +{ + return(seedTwo); +} + +inline void +MLCG::seed2(long s) +{ + initialSeedTwo = s; + reset(); +} + +inline void +MLCG::reseed(long s1, long s2) +{ + initialSeedOne = s1; + initialSeedTwo = s2; + reset(); +} + +#endif diff --git a/gnu/lib/libg++/libg++/Makefile b/gnu/lib/libg++/libg++/Makefile index 92809cb4b7..3e1d1eb5d5 100644 --- a/gnu/lib/libg++/libg++/Makefile +++ b/gnu/lib/libg++/libg++/Makefile @@ -1,61 +1,41 @@ -# @(#)Makefile 6.2 (Berkeley) 4/24/91 -# -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 1 00118 -# -------------------- ----- ---------------------- -# -# 30 Mar 93 Rodney W. Grimes Add CFLAGS so that make depend works -# - -# -# Define g++ compilation rules. -# - -.SUFFIXES: -.SUFFIXES: .out .o .po .s .c .f .y .cc .l .8 .7 .6 .5 .4 .3 .2 .1 .0 - -CFLAGS+=-I$(.CURDIR)/../g++-include -+ -GXX= g++ -GXXFLAGS= -O -felide-constructors -LDXX= /usr/libexec/ld++ - -.cc.o: - $(GXX) $(GXXFLAGS) -c $(.IMPSRC) - @$(LDXX) -x -r $(.TARGET) - @mv a.out $(.TARGET) - -.cc.po: - $(GXX) -p $(GXXFLAGS) -c $(.IMPSRC) -o $(.TARGET) - @$(LDXX) -X -r $(.TARGET) - @mv a.out $(.TARGET) - -# -# Build the library. -# - LIB= g++ -GXXFLAGS+= -I$(.CURDIR)/../g++-include -DHAVE_VPRINTF -DHAVE_VSCANF \ - -DHAVE_SETVBUF -DHAVE_SETLINEBUF -DHAVE_GETPAGESIZE -SRCS= AllocRing.cc Obstack.cc File.cc ostream.cc istream.cc \ - streambuf.cc filebuf.cc Filebuf.cc open.o PlotFile.cc \ - SFile.cc builtin.cc regex.cc Regex.cc String.cc \ - Integer.cc Rational.cc Complex.cc Random.cc BitSet.cc \ - BitString.cc LogNorm.cc SmplHist.cc SmplStat.cc \ +CC= gcc +SRCS= AllocRing.cc Obstack.cc builtin.cc \ + regex.cc Regex.cc String.cc Integer.cc Rational.cc Complex.cc Random.cc \ + BitSet.cc BitString.cc LogNorm.cc SmplHist.cc SmplStat.cc \ Normal.cc NegExp.cc Weibull.cc Erlang.cc DiscUnif.cc \ Uniform.cc Poisson.cc HypGeom.cc Geom.cc Binomial.cc \ - max.cc min.cc RNG.cc ACG.cc MLCG.cc RndInt.cc \ - Fix.cc Fix16.cc Fix24.cc CursesW.cc GetOpt.cc EH.cc \ - EH2.c xyzzy.cc gnulib3.c new.cc delete.cc \ - chr.cc dtoa.cc error.cc form.cc gcd.cc hash.cc \ - itoa.cc lg.cc fmtq.cc ioob.cc pow.cc sqrt.cc str.cc \ - timer.cc compare.cc ctype.cc curses.cc math.cc std.cc + RNG.cc ACG.cc MLCG.cc RndInt.cc \ + Fix.cc Fix16.cc Fix24.cc CursesW.cc GetOpt.cc $(EH_FILES) \ + new.cc chr.cc dtoa.cc error.cc gcd.cc hash.cc \ + lg.cc fmtq.cc ioob.cc pow.cc sqrt.cc str.cc timer.cc \ + math.cc compare.cc SLList.cc DLList.cc \ + streambuf.C stdstrbufs.C iostream.C stdstreams.C \ + strstream.C indstream.C PlotFile.C SFile.C fstream.C \ + parsestream.C stream.C makebuf.C editbuf.C filebuf.C \ + sgetline.C igetline.C igetsb.C procbuf.C sbufvform.C \ + sbufvscan.C stdiostream.C floatconv.C outfloat.C iomanip.C \ + insque.c strerror.c + +CFLAGS+= -I$(.CURDIR) -I$(.CURDIR)/../libiberty +GFLAGS= -nostdinc++ -I$(.CURDIR) -I$(.CURDIR)/../g++-include \ + -I$(.CURDIR)/../iostream NOMAN= noman +.PATH: $(.CURDIR)/../iostream $(.CURDIR)/../libiberty +depend: + @echo "BROKEN MAKE DEPEND" .include -# -# For some reason, pmake won't cut it without this redundant declaration. -# -.SUFFIXES: -.SUFFIXES: .out .o .po .s .c .f .y .cc .l .8 .7 .6 .5 .4 .3 .2 .1 .0 +.SUFFIXES: .cc .C +.cc.o: + $(CC) $(GFLAGS) -c $(.IMPSRC) + +.cc.po: + $(CC) -p $(GFLAGS) -c $(.IMPSRC) -o $(.TARGET) + +.C.o: + $(CC) $(GFLAGS) -c $(.IMPSRC) + +.C.po: + $(CC) -p $(GFLAGS) -c $(.IMPSRC) -o $(.TARGET) diff --git a/gnu/lib/libg++/libg++/Makefile.gnu b/gnu/lib/libg++/libg++/Makefile.gnu deleted file mode 100644 index 212dc9cdfe..0000000000 --- a/gnu/lib/libg++/libg++/Makefile.gnu +++ /dev/null @@ -1,463 +0,0 @@ -# Makefile for libg++.a - -# Copyright (C) 1988 Free Software Foundation -# written by Doug Lea (dl@rocky.oswego.edu) - -# This file is part of GNU CC. - -# GNU CC is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY. No author or distributor -# accepts responsibility to anyone for the consequences of using it -# or for whether it serves any particular purpose or works at all, -# unless he says so in writing. Refer to the GNU CC General Public -# License for full details. - -# Everyone is granted permission to copy, modify and redistribute -# GNU CC, but only under the conditions described in the -# GNU CC General Public License. A copy of this license is -# supposed to have been given to you along with GNU CC so you -# can know your rights and responsibilities. It should be in a -# file named COPYING. Among other things, the copyright notice -# and this notice must be preserved on all copies. - -# make parameters -- these should normally be inherited from parent make - -# ------ source locations - -# source directory for libg++.a -SRCDIR = ../src - -# source include directory -SRCIDIR= ../g++-include - -# ------ installation destinations -# ------ You will require write-permission on the destination directories -# ------ in order to `make install' - - -prefix =/usr/gnu - -# libg++.a destination -LIBDIR = $(prefix)/lib - -# executables directory: location to install the genclass class generator -BINDIR = $(prefix)/bin - -# location to install include file directory -IDIR = $(prefix)/lib/g++-include - - -# ------- System-dependent defines -# ------- use the second form of each for SystemV (USG) - -# g++ flags -OSFLAG= -#OSFLAG = -DUSG -# use this only if you have a strange stdio implementation -#OSFLAG = -DDEFAULT_filebuf - -# ld or ld++ flags -OSLDFLAG = -#OSLDFLAG= -lPW - -# how to install -INSTALL=install -c -#INSTALL=cp - -# ranlib if necessary -RANLIB=ranlib -#RANLIB=echo - -# which make? -MAKE=make - -#which ar? -AR=ar - -# not used, but convenient for those who preprocess things while compiling -SHELL=/bin/sh - - -# ------ compiler names - -# GNU C++ compiler name -GXX = g++ -#GXX=gcc - -# GNU CC compiler name (needed for some .c files in libg++.a) -CC = gcc - -# GNU loader -LDXX = $(LIBDIR)/gcc-ld++ - -# crt0+.o location (for dynamic loading tests) -GXXCRT1=$(LIBDIR)/crt1+.o - -# ------ Other compilation flags -# ------ modify as you like -- the ones here are sheer overkill - -GXX_OPTIMIZATION_FLAGS= -O -fstrength-reduce -felide-constructors -fschedule-insns -fdelayed-branch -fsave-memoized - -GCC_OPTIMIZATION_FLAGS= -O -fstrength-reduce -fdelayed-branch - -DEBUG_FLAGS= -g - -#use this only if you like to look at lots of useless messages -VERBOSITY_FLAGS= -Wall -v - -GXX_INCLUDE_DIRS= -I$(SRCIDIR) - -GCC_INCLUDE_DIRS= -I$(prefix)/lib/gcc-include -I/usr/include -I$(SRCIDIR) - -PIPE_AS= -pipe - -# Flags for all C++ compiles -GXXFLAGS = $(OSFLAG) $(GXX_INCLUDE_DIRS) $(DEBUG_FLAGS) $(GXX_OPTIMIZATION_FLAGS) $(VERBOSITY_FLAGS) $(PIPE_AS) - -# Flags for all C compiles -CFLAGS= $(OSFLAG) $(GCC_INCLUDE_DIRS) $(DEBUG_FLAGS) $(GCC_OPTIMIZATION_FLAGS) $(VERBOSITY_FLAGS) $(PIPE_AS) - -# g++ load time flags -GXXLDFLAGS = -L$(SRCDIR) -lg++ -lm $(OSLDFLAG) - -# these flags tell test0 where ld++ and crt1+.o are -TFLAGS = -DLDXX=\"$(LDXX)\" -DCRT1X=\"$(GXXCRT1)\" - -# g++ files should have extension .cc -.SUFFIXES: .cc -.cc.o: - $(GXX) $(GXXFLAGS) -c $< - -########################################################################### -# -# declarations from here on should not normally need to be changed -# in order to compile libg++.a -# - -# library sources - -OBJS = AllocRing.o Obstack.o File.o ostream.o istream.o \ - streambuf.o filebuf.o Filebuf.o \ - PlotFile.o SFile.o builtin.o \ - regex.o Regex.o String.o Integer.o Rational.o Complex.o Random.o \ - BitSet.o BitString.o LogNorm.o SmplHist.o SmplStat.o \ - Normal.o NegExp.o Weibull.o Erlang.o DiscUnif.o \ - Uniform.o Poisson.o HypGeom.o Geom.o Binomial.o \ - RNG.o ACG.o MLCG.o RndInt.o \ - Fix.o Fix16.o Fix24.o CursesW.o GetOpt.o EH.o EH2.o\ - xyzzy.o gnulib3.o new.o delete.o malloc.o chr.o dtoa.o error.o form.o gcd.o \ - hash.o itoa.o \ - lg.o fmtq.o ioob.o pow.o sqrt.o str.o timer.o bcopy.o \ - std.o ctype.o curses.o math.o compare.o - -########################################################################### -# -# compilation actions -# - -all: libg++.a - -libg++.a: $(OBJS) - rm -f libg++.a - $(AR) r libg++.a $(OBJS) - $(RANLIB) libg++.a - -install: - $(INSTALL) libg++.a $(LIBDIR)/libg++.a - -if [ -x /usr/bin/$(RANLIB) -o -x /bin/ranlib ] ; then \ - $(RANLIB) $(LIBDIR)/libg++.a; \ - fi - -clean: - rm -f *.o core - -realclean: clean - rm -f libg++.a - - -########################################################################### -# -# dependencies -# - -# DO NOT DELETE THIS LINE -- g++dep uses it. -# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. - -ACG.o : ACG.cc $(SRCIDIR)/ACG.h \ - $(SRCIDIR)/RNG.h $(SRCIDIR)/assert.h \ - $(SRCIDIR)/math.h $(SRCIDIR)/values.h -AllocRing.o : AllocRing.cc $(SRCIDIR)/std.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/stdio.h \ - $(SRCIDIR)/AllocRing.h $(SRCIDIR)/new.h -Binomial.o : Binomial.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/Random.h \ - $(SRCIDIR)/RNG.h $(SRCIDIR)/assert.h \ - $(SRCIDIR)/Binomial.h -BitSet.o : BitSet.cc $(SRCIDIR)/BitSet.h \ - $(SRCIDIR)/stream.h $(SRCIDIR)/File.h \ - $(SRCIDIR)/builtin.h $(SRCIDIR)/stddef.h \ - $(SRCIDIR)/std.h $(SRCIDIR)/stdio.h \ - $(SRCIDIR)/math.h $(SRCIDIR)/values.h \ - $(SRCIDIR)/streambuf.h $(SRCIDIR)/Obstack.h \ - $(SRCIDIR)/AllocRing.h $(SRCIDIR)/new.h -BitString.o : BitString.cc $(SRCIDIR)/BitString.h \ - $(SRCIDIR)/stream.h $(SRCIDIR)/File.h \ - $(SRCIDIR)/builtin.h $(SRCIDIR)/stddef.h \ - $(SRCIDIR)/std.h $(SRCIDIR)/stdio.h \ - $(SRCIDIR)/math.h $(SRCIDIR)/values.h \ - $(SRCIDIR)/streambuf.h $(SRCIDIR)/Obstack.h \ - $(SRCIDIR)/AllocRing.h $(SRCIDIR)/new.h -Complex.o : Complex.cc $(SRCIDIR)/Complex.h \ - $(SRCIDIR)/stream.h $(SRCIDIR)/File.h \ - $(SRCIDIR)/builtin.h $(SRCIDIR)/stddef.h \ - $(SRCIDIR)/std.h $(SRCIDIR)/stdio.h \ - $(SRCIDIR)/math.h $(SRCIDIR)/values.h \ - $(SRCIDIR)/streambuf.h -CursesW.o : CursesW.cc $(SRCIDIR)/stdio.h \ - $(SRCIDIR)/stdarg.h $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/math.h $(SRCIDIR)/values.h \ - $(SRCIDIR)/CursesW.h $(SRCIDIR)/curses.h -DiscUnif.o : DiscUnif.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/Random.h \ - $(SRCIDIR)/RNG.h $(SRCIDIR)/assert.h \ - $(SRCIDIR)/DiscUnif.h -EH.o : EH.cc $(SRCIDIR)/setjmp.h \ - $(SRCIDIR)/File.h $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/streambuf.h -EH2.o : EH2.c -Erlang.o : Erlang.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/Random.h \ - $(SRCIDIR)/RNG.h $(SRCIDIR)/assert.h \ - $(SRCIDIR)/Erlang.h -File.o : File.cc $(SRCIDIR)/File.h \ - $(SRCIDIR)/builtin.h $(SRCIDIR)/stddef.h \ - $(SRCIDIR)/std.h $(SRCIDIR)/stdio.h \ - $(SRCIDIR)/math.h $(SRCIDIR)/values.h \ - $(SRCIDIR)/stdarg.h $(SRCIDIR)/sys/file.h \ - $(SRCIDIR)/sys/types.h -Filebuf.o : Filebuf.cc $(SRCIDIR)/streambuf.h \ - $(SRCIDIR)/builtin.h $(SRCIDIR)/stddef.h \ - $(SRCIDIR)/std.h $(SRCIDIR)/stdio.h \ - $(SRCIDIR)/math.h $(SRCIDIR)/values.h \ - $(SRCIDIR)/File.h $(SRCIDIR)/sys/file.h \ - $(SRCIDIR)/sys/types.h -Fix.o : Fix.cc $(SRCIDIR)/Fix.h \ - $(SRCIDIR)/stream.h $(SRCIDIR)/File.h \ - $(SRCIDIR)/builtin.h $(SRCIDIR)/stddef.h \ - $(SRCIDIR)/std.h $(SRCIDIR)/stdio.h \ - $(SRCIDIR)/math.h $(SRCIDIR)/values.h \ - $(SRCIDIR)/streambuf.h $(SRCIDIR)/Integer.h \ - $(SRCIDIR)/Obstack.h $(SRCIDIR)/AllocRing.h -Fix16.o : Fix16.cc $(SRCIDIR)/Fix16.h \ - $(SRCIDIR)/stream.h $(SRCIDIR)/File.h \ - $(SRCIDIR)/builtin.h $(SRCIDIR)/stddef.h \ - $(SRCIDIR)/std.h $(SRCIDIR)/stdio.h \ - $(SRCIDIR)/math.h $(SRCIDIR)/values.h \ - $(SRCIDIR)/streambuf.h -Fix24.o : Fix24.cc $(SRCIDIR)/Fix24.h \ - $(SRCIDIR)/stream.h $(SRCIDIR)/File.h \ - $(SRCIDIR)/builtin.h $(SRCIDIR)/stddef.h \ - $(SRCIDIR)/std.h $(SRCIDIR)/stdio.h \ - $(SRCIDIR)/math.h $(SRCIDIR)/values.h \ - $(SRCIDIR)/streambuf.h -Geom.o : Geom.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/Random.h \ - $(SRCIDIR)/RNG.h $(SRCIDIR)/assert.h \ - $(SRCIDIR)/Geom.h -GetOpt.o : GetOpt.cc $(SRCIDIR)/GetOpt.h \ - $(SRCIDIR)/std.h $(SRCIDIR)/stddef.h \ - $(SRCIDIR)/stdio.h -HypGeom.o : HypGeom.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/Random.h \ - $(SRCIDIR)/RNG.h $(SRCIDIR)/assert.h \ - $(SRCIDIR)/HypGeom.h -Integer.o : Integer.cc $(SRCIDIR)/Integer.h \ - $(SRCIDIR)/stream.h $(SRCIDIR)/File.h \ - $(SRCIDIR)/builtin.h $(SRCIDIR)/stddef.h \ - $(SRCIDIR)/std.h $(SRCIDIR)/stdio.h \ - $(SRCIDIR)/math.h $(SRCIDIR)/values.h \ - $(SRCIDIR)/streambuf.h $(SRCIDIR)/ctype.h \ - $(SRCIDIR)/Obstack.h $(SRCIDIR)/AllocRing.h \ - $(SRCIDIR)/new.h -MLCG.o : MLCG.cc $(SRCIDIR)/MLCG.h \ - $(SRCIDIR)/RNG.h $(SRCIDIR)/assert.h \ - $(SRCIDIR)/math.h $(SRCIDIR)/values.h -Normal.o : Normal.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/Random.h \ - $(SRCIDIR)/RNG.h $(SRCIDIR)/assert.h -NegExp.o : NegExp.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/Random.h \ - $(SRCIDIR)/RNG.h $(SRCIDIR)/assert.h \ - $(SRCIDIR)/NegExp.h -Obstack.o : Obstack.cc $(SRCIDIR)/values.h \ - $(SRCIDIR)/builtin.h $(SRCIDIR)/stddef.h \ - $(SRCIDIR)/std.h $(SRCIDIR)/stdio.h \ - $(SRCIDIR)/math.h $(SRCIDIR)/Obstack.h -PlotFile.o : PlotFile.cc $(SRCIDIR)/PlotFile.h \ - $(SRCIDIR)/File.h $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h -Poisson.o : Poisson.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/Random.h \ - $(SRCIDIR)/RNG.h $(SRCIDIR)/assert.h \ - $(SRCIDIR)/Poisson.h -RNG.o : RNG.cc $(SRCIDIR)/values.h \ - $(SRCIDIR)/assert.h $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/RNG.h -Rational.o : Rational.cc $(SRCIDIR)/Rational.h \ - $(SRCIDIR)/Integer.h $(SRCIDIR)/stream.h \ - $(SRCIDIR)/File.h $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/streambuf.h -SFile.o : SFile.cc $(SRCIDIR)/SFile.h \ - $(SRCIDIR)/File.h $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h -SmplHist.o : SmplHist.cc $(SRCIDIR)/stream.h \ - $(SRCIDIR)/File.h $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/streambuf.h \ - $(SRCIDIR)/SmplHist.h \ - $(SRCIDIR)/SmplStat.h -SmplStat.o : SmplStat.cc $(SRCIDIR)/stream.h \ - $(SRCIDIR)/File.h $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/streambuf.h \ - $(SRCIDIR)/SmplStat.h -String.o : String.cc $(SRCIDIR)/String.h \ - $(SRCIDIR)/stream.h $(SRCIDIR)/File.h \ - $(SRCIDIR)/builtin.h $(SRCIDIR)/stddef.h \ - $(SRCIDIR)/std.h $(SRCIDIR)/stdio.h \ - $(SRCIDIR)/math.h $(SRCIDIR)/values.h \ - $(SRCIDIR)/streambuf.h $(SRCIDIR)/ctype.h \ - $(SRCIDIR)/new.h $(SRCIDIR)/regex.h -Uniform.o : Uniform.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/Random.h \ - $(SRCIDIR)/RNG.h $(SRCIDIR)/assert.h \ - $(SRCIDIR)/Uniform.h -Weibell.o : Weibell.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/Random.h \ - $(SRCIDIR)/RNG.h $(SRCIDIR)/assert.h \ - $(SRCIDIR)/Weibull.h -chr.o : chr.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/AllocRing.h -dtoa.o : dtoa.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/AllocRing.h -error.o : error.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h -filebuf.o : filebuf.cc $(SRCIDIR)/streambuf.h \ - $(SRCIDIR)/builtin.h $(SRCIDIR)/stddef.h \ - $(SRCIDIR)/std.h $(SRCIDIR)/stdio.h \ - $(SRCIDIR)/math.h $(SRCIDIR)/values.h \ - $(SRCIDIR)/File.h $(SRCIDIR)/sys/file.h \ - $(SRCIDIR)/sys/types.h -form.o : form.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/stdarg.h \ - $(SRCIDIR)/AllocRing.h -gcd.o : gcd.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h -hash.o : hash.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h -istream.o : istream.cc $(SRCIDIR)/stream.h \ - $(SRCIDIR)/File.h $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/streambuf.h \ - $(SRCIDIR)/stdarg.h $(SRCIDIR)/ctype.h \ - $(SRCIDIR)/Obstack.h -itoa.o : itoa.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/AllocRing.h -lg.o : lg.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h -fmtq.o : fmtq.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/AllocRing.h -ioob.o : ioob.cc $(SRCIDIR)/Obstack.h \ - $(SRCIDIR)/std.h $(SRCIDIR)/stddef.h \ - $(SRCIDIR)/stdio.h -new.o : new.cc $(SRCIDIR)/stddef.h \ - $(SRCIDIR)/malloc.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h -ostream.o : ostream.cc $(SRCIDIR)/stream.h \ - $(SRCIDIR)/File.h $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/streambuf.h \ - $(SRCIDIR)/stdarg.h $(SRCIDIR)/ctype.h \ - $(SRCIDIR)/Obstack.h -pow.o : pow.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h -regex.o : regex.cc $(SRCIDIR)/std.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/stdio.h \ - $(SRCIDIR)/malloc.h $(SRCIDIR)/regex.h -sqrt.o : sqrt.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h -str.o : str.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/AllocRing.h -streambuf.o : streambuf.cc $(SRCIDIR)/streambuf.h \ - $(SRCIDIR)/builtin.h $(SRCIDIR)/stddef.h \ - $(SRCIDIR)/std.h $(SRCIDIR)/stdio.h \ - $(SRCIDIR)/math.h $(SRCIDIR)/values.h \ - $(SRCIDIR)/File.h -timer.o : timer.cc $(SRCIDIR)/builtin.h \ - $(SRCIDIR)/stddef.h $(SRCIDIR)/std.h \ - $(SRCIDIR)/stdio.h $(SRCIDIR)/math.h \ - $(SRCIDIR)/values.h $(SRCIDIR)/osfcn.h \ - $(SRCIDIR)/time.h $(SRCIDIR)/sys/types.h \ - $(SRCIDIR)/sys/socket.h $(SRCIDIR)/sys/resource.h -xyzzy.o : xyzzy.cc - -# IF YOU PUT ANYTHING HERE IT WILL GO AWAY diff --git a/gnu/lib/libg++/libg++/NegExp.cc b/gnu/lib/libg++/libg++/NegExp.cc index 848eb6aaec..8bd6d0514d 100644 --- a/gnu/lib/libg++/libg++/NegExp.cc +++ b/gnu/lib/libg++/libg++/NegExp.cc @@ -2,29 +2,23 @@ Copyright (C) 1988 Free Software Foundation written by Dirk Grunwald (grunwald@cs.uiuc.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ #pragma implementation #endif #include #include - #include double NegativeExpntl::operator()() diff --git a/gnu/lib/libg++/libg++/NegExp.h b/gnu/lib/libg++/libg++/NegExp.h new file mode 100644 index 0000000000..b96f9132df --- /dev/null +++ b/gnu/lib/libg++/libg++/NegExp.h @@ -0,0 +1,55 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef _NegativeExpntl_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _NegativeExpntl_h 1 + + +// +// Negative Exponential Random Numbers +// +// + +#include + +class NegativeExpntl: public Random { +protected: + double pMean; +public: + NegativeExpntl(double xmean, RNG *gen); + double mean(); + double mean(double x); + + virtual double operator()(); +}; + + +inline NegativeExpntl::NegativeExpntl(double xmean, RNG *gen) +: Random(gen) { + pMean = xmean; +} + +inline double NegativeExpntl::mean() { return pMean; } +inline double NegativeExpntl::mean(double x) { + double t = pMean; pMean = x; + return t; +} + +#endif diff --git a/gnu/lib/libg++/libg++/Normal.cc b/gnu/lib/libg++/libg++/Normal.cc index 48695aafaf..bae43e1055 100644 --- a/gnu/lib/libg++/libg++/Normal.cc +++ b/gnu/lib/libg++/libg++/Normal.cc @@ -2,29 +2,23 @@ Copyright (C) 1988 Free Software Foundation written by Dirk Grunwald (grunwald@cs.uiuc.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ #pragma implementation #endif #include #include - #include // // See Simulation, Modelling & Analysis by Law & Kelton, pp259 diff --git a/gnu/lib/libg++/libg++/Normal.h b/gnu/lib/libg++/libg++/Normal.h new file mode 100644 index 0000000000..8c8358ee12 --- /dev/null +++ b/gnu/lib/libg++/libg++/Normal.h @@ -0,0 +1,66 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef _Normal_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Normal_h + +#include + +class Normal: public Random { + char haveCachedNormal; + double cachedNormal; + +protected: + double pMean; + double pVariance; + double pStdDev; + +public: + Normal(double xmean, double xvariance, RNG *gen); + double mean(); + double mean(double x); + double variance(); + double variance(double x); + virtual double operator()(); +}; + + +inline Normal::Normal(double xmean, double xvariance, RNG *gen) +: Random(gen) { + pMean = xmean; + pVariance = xvariance; + pStdDev = sqrt(pVariance); + haveCachedNormal = 0; +} + +inline double Normal::mean() { return pMean; }; +inline double Normal::mean(double x) { + double t=pMean; pMean = x; + return t; +} + +inline double Normal::variance() { return pVariance; } +inline double Normal::variance(double x) { + double t=pVariance; pVariance = x; + pStdDev = sqrt(pVariance); + return t; +}; + +#endif diff --git a/gnu/lib/libg++/libg++/Obstack.cc b/gnu/lib/libg++/libg++/Obstack.cc index 360a978d24..f748eb4df8 100644 --- a/gnu/lib/libg++/libg++/Obstack.cc +++ b/gnu/lib/libg++/libg++/Obstack.cc @@ -2,22 +2,17 @@ Copyright (C) 1988 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ @@ -45,7 +40,7 @@ void Obstack::_free(void* obj) while (lp != 0 && ((void*)lp > obj || (void*)(lp)->limit < obj)) { plp = lp -> prev; - delete(lp); + delete [] (char*)lp; lp = plp; } if (lp) @@ -73,7 +68,7 @@ void Obstack::newchunk(int size) new_chunk->prev = old_chunk; new_chunk->limit = chunklimit = (char *) new_chunk + new_size; - bcopy((void*)objectbase, (void*)new_chunk->contents, obj_size); + memcpy((void*)new_chunk->contents, (void*)objectbase, obj_size); objectbase = new_chunk->contents; nextfree = objectbase + obj_size; } @@ -115,14 +110,3 @@ int Obstack::OK() (*lib_error_handler)("Obstack", "invariant failure"); return v; } - - -#ifdef VMS -#include "libgxx-io-ob.cc" -// The reason that this needs to be included is that if the modules for libg++ -// are placed in a library, and libgxx-ob-io is in a seperate module, then -// that module contains only two symbols - the contstructor and destructor. -// they are not called explicitly anywhere else, so that module is not linked -// in, and the contstructor is not called. Chaos ensues. -#endif - diff --git a/gnu/lib/libg++/libg++/Obstack.h b/gnu/lib/libg++/libg++/Obstack.h new file mode 100644 index 0000000000..384157f630 --- /dev/null +++ b/gnu/lib/libg++/libg++/Obstack.h @@ -0,0 +1,216 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _Obstack_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Obstack_h 1 + +#include + +class Obstack +{ + struct _obstack_chunk + { + char* limit; + _obstack_chunk* prev; + char contents[4]; + }; + +protected: + long chunksize; + _obstack_chunk* chunk; + char* objectbase; + char* nextfree; + char* chunklimit; + int alignmentmask; + + void _free(void* obj); + void newchunk(int size); + +public: + Obstack(int size = 4080, int alignment = 4); // 4080=4096-mallocslop + + ~Obstack(); + + void* base(); + void* next_free(); + int alignment_mask(); + int chunk_size(); + int size(); + int room(); + int contains(void* p); // does Obstack hold pointer p? + + void grow(const void* data, int size); + void grow(const void* data, int size, char terminator); + void grow(const char* s); + void grow(char c); + void grow_fast(char c); + void blank(int size); + void blank_fast(int size); + + void* finish(); + void* finish(char terminator); + + void* copy(const void* data, int size); + void* copy(const void* data, int size, char terminator); + void* copy(const char* s); + void* copy(char c); + void* alloc(int size); + + void free(void* obj); + void shrink(int size = 1); // suggested by ken@cs.rochester.edu + + int OK(); // rep invariant +}; + + +inline Obstack::~Obstack() +{ + _free(0); +} + +inline void* Obstack::base() +{ + return objectbase; +} + +inline void* Obstack::next_free() +{ + return nextfree; +} + +inline int Obstack::alignment_mask() +{ + return alignmentmask; +} + +inline int Obstack::chunk_size() +{ + return chunksize; +} + +inline int Obstack::size() +{ + return nextfree - objectbase; +} + +inline int Obstack::room() +{ + return chunklimit - nextfree; +} + +inline void Obstack:: grow(const void* data, int size) +{ + if (nextfree+size > chunklimit) + newchunk(size); + memcpy(nextfree, data, size); + nextfree += size; +} + +inline void Obstack:: grow(const void* data, int size, char terminator) +{ + if (nextfree+size+1 > chunklimit) + newchunk(size+1); + memcpy(nextfree, data, size); + nextfree += size; + *(nextfree)++ = terminator; +} + +inline void Obstack:: grow(const char* s) +{ + grow((const void*)s, strlen(s), 0); +} + +inline void Obstack:: grow(char c) +{ + if (nextfree+1 > chunklimit) + newchunk(1); + *(nextfree)++ = c; +} + +inline void Obstack:: blank(int size) +{ + if (nextfree+size > chunklimit) + newchunk(size); + nextfree += size; +} + +inline void* Obstack::finish(char terminator) +{ + grow(terminator); + return finish(); +} + +inline void* Obstack::copy(const void* data, int size) +{ + grow (data, size); + return finish(); +} + +inline void* Obstack::copy(const void* data, int size, char terminator) +{ + grow(data, size, terminator); + return finish(); +} + +inline void* Obstack::copy(const char* s) +{ + grow((const void*)s, strlen(s), 0); + return finish(); +} + +inline void* Obstack::copy(char c) +{ + grow(c); + return finish(); +} + +inline void* Obstack::alloc(int size) +{ + blank(size); + return finish(); +} + +inline void Obstack:: free(void* obj) +{ + if (obj >= (void*)chunk && obj<(void*)chunklimit) + nextfree = objectbase = (char *) obj; + else + _free(obj); +} + +inline void Obstack:: grow_fast(char c) +{ + *(nextfree)++ = c; +} + +inline void Obstack:: blank_fast(int size) +{ + nextfree += size; +} + +inline void Obstack:: shrink(int size) // from ken@cs.rochester.edu +{ + if (nextfree >= objectbase + size) + nextfree -= size; +} + +#endif diff --git a/gnu/lib/libg++/libg++/Pix.h b/gnu/lib/libg++/libg++/Pix.h new file mode 100644 index 0000000000..be90525c63 --- /dev/null +++ b/gnu/lib/libg++/libg++/Pix.h @@ -0,0 +1,5 @@ + +#ifndef _Pix_h +#define _Pix_h 1 +typedef void* Pix; +#endif diff --git a/gnu/lib/libg++/libg++/PlotFile.cc b/gnu/lib/libg++/libg++/PlotFile.cc deleted file mode 100644 index 485c984b08..0000000000 --- a/gnu/lib/libg++/libg++/PlotFile.cc +++ /dev/null @@ -1,183 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifdef __GNUG__ -#pragma implementation -#endif -#include - -/* - PlotFile implementation module -*/ - - -PlotFile:: PlotFile() {} -PlotFile::~PlotFile() {} - - -PlotFile::PlotFile(const char* filename, io_mode m, access_mode a) -:(filename, m, a) {} - -PlotFile::PlotFile(const char* filename, const char* m) -:(filename, m) {} - -PlotFile::PlotFile(int filedesc, const io_mode m) -:(filedesc, m) {} - -PlotFile::PlotFile(FILE* fileptr) -:(fileptr) {} - -PlotFile::operator void*() -{ - return (state & (_bad|_fail))? 0 : this ; -} - - -PlotFile& PlotFile::open(const char* filename, - io_mode m, access_mode a) -{ - File::open(filename, m, a); return *this; -} - -PlotFile& PlotFile::open(const char* filename, const char* m) -{ - File::open(filename, m); return *this; -} - -PlotFile& PlotFile::open(int filedesc, io_mode m) -{ - File::open(filedesc, m); return *this; -} - -PlotFile& PlotFile::open(FILE* fileptr) -{ - File::open(fileptr); return *this; -} - -PlotFile& PlotFile::setbuf(const int buffer_kind) -{ - File::setbuf(buffer_kind); return *this; -} - -PlotFile& PlotFile::setbuf(const int size, char* buf) -{ - File::setbuf(size, buf); return *this; -} - - -PlotFile& PlotFile:: cmd(char c) -{ - File::put(c); - return *this; -} - -PlotFile& PlotFile:: operator<<(const int x) -{ -#if defined(convex) - File::put((char)(x>>8)); - File::put((char)(x&0377)); -#else - File::put((char)(x&0377)); - File::put((char)(x>>8)); -#endif - return *this; -} - -PlotFile& PlotFile:: operator<<(const char *s) -{ - File::put(s); - return *this; -} - - -PlotFile& PlotFile:: arc(const int xi, const int yi, - const int x0, const int y0, - const int x1, const int y1) -{ - return cmd('a') << xi << yi << x0 << y0 << x1 << y1; -} - - -PlotFile& PlotFile:: box(const int x0, const int y0, - const int x1, const int y1) -{ - line(x0, y0, x0, y1); - line(x0, y1, x1, y1); - line(x1, y1, x1, y0); - return line(x1, y0, x0, y0); -} - -PlotFile& PlotFile:: circle(const int x, const int y, const int r) -{ - return cmd('c') << x << y << r; -} - -PlotFile& PlotFile:: cont(const int xi, const int yi) -{ - return cmd('n') << xi << yi; -} - -PlotFile& PlotFile:: dot(const int xi, const int yi, const int dx, - int n, const int* pat) -{ - cmd('d') << xi << yi << dx << n; - while (n-- > 0) *this << *pat++; - return *this; -} - -PlotFile& PlotFile:: erase() -{ - return cmd('e'); -} - -PlotFile& PlotFile:: label(const char* s) -{ - return cmd('t') << s << "\n"; -} - -PlotFile& PlotFile:: line(const int x0, const int y0, - const int x1, const int y1) -{ - return cmd('l') << x0 << y0 << x1 << y1; -} - -PlotFile& PlotFile:: linemod(const char* s) -{ - return cmd('f') << s << "\n"; -} - -PlotFile& PlotFile:: move(const int xi, const int yi) -{ - return cmd('m') << xi << yi; -} - -PlotFile& PlotFile:: point(const int xi, const int yi) -{ - return cmd('p') << xi << yi; -} - -PlotFile& PlotFile:: space(const int x0, const int y0, - const int x1, const int y1) -{ - return cmd('s') << x0 << y0 << x1 << y1; -} diff --git a/gnu/lib/libg++/libg++/Poisson.cc b/gnu/lib/libg++/libg++/Poisson.cc index 89418b1fc0..8d70f1751b 100644 --- a/gnu/lib/libg++/libg++/Poisson.cc +++ b/gnu/lib/libg++/libg++/Poisson.cc @@ -3,29 +3,23 @@ Copyright (C) 1988 Free Software Foundation written by Dirk Grunwald (grunwald@cs.uiuc.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ #pragma implementation #endif #include #include - #include double Poisson::operator()() diff --git a/gnu/lib/libg++/libg++/Poisson.h b/gnu/lib/libg++/libg++/Poisson.h new file mode 100644 index 0000000000..e5851453be --- /dev/null +++ b/gnu/lib/libg++/libg++/Poisson.h @@ -0,0 +1,51 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef _Poisson_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Poisson_h + +#include + +class Poisson: public Random { +protected: + double pMean; +public: + Poisson(double mean, RNG *gen); + + double mean(); + double mean(double x); + + virtual double operator()(); +}; + + +inline Poisson::Poisson(double mean, RNG *gen) +: Random(gen) { + pMean = mean; +} + +inline double Poisson::mean() { return pMean; } +inline double Poisson::mean(double x) { + double t = pMean; + pMean = x; + return t; +} + +#endif diff --git a/gnu/lib/libg++/libg++/RNG.cc b/gnu/lib/libg++/libg++/RNG.cc index 24df30dfb4..568b721898 100644 --- a/gnu/lib/libg++/libg++/RNG.cc +++ b/gnu/lib/libg++/libg++/RNG.cc @@ -1,3 +1,19 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1989 Free Software Foundation + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ #ifdef __GNUG__ #pragma implementation #endif @@ -6,6 +22,10 @@ #include #include +// These two static fields get initialized by RNG::RNG(). +PrivateRNGSingleType RNG::singleMantissa; +PrivateRNGDoubleType RNG::doubleMantissa; + // // The scale constant is 2^-31. It is used to scale a 31 bit // long to a double. @@ -88,3 +108,25 @@ RNG::RNG() initialized = 1; } } + +float RNG::asFloat() +{ + PrivateRNGSingleType result; + result.s = 1.0; + result.u |= (asLong() & singleMantissa.u); + result.s -= 1.0; + assert( result.s < 1.0 && result.s >= 0); + return( result.s ); +} + +double RNG::asDouble() +{ + PrivateRNGDoubleType result; + result.d = 1.0; + result.u[0] |= (asLong() & doubleMantissa.u[0]); + result.u[1] |= (asLong() & doubleMantissa.u[1]); + result.d -= 1.0; + assert( result.d < 1.0 && result.d >= 0); + return( result.d ); +} + diff --git a/gnu/lib/libg++/libg++/RNG.h b/gnu/lib/libg++/libg++/RNG.h new file mode 100644 index 0000000000..d2b3a1dafe --- /dev/null +++ b/gnu/lib/libg++/libg++/RNG.h @@ -0,0 +1,57 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef _RNG_h +#define _RNG_h 1 +#ifdef __GNUG__ +#pragma interface +#endif + +#include +#include + +union PrivateRNGSingleType { // used to access floats as unsigneds + float s; + unsigned long u; +}; + +union PrivateRNGDoubleType { // used to access doubles as unsigneds + double d; + unsigned long u[2]; +}; + +// +// Base class for Random Number Generators. See ACG and MLCG for instances. +// +class RNG { + static PrivateRNGSingleType singleMantissa; // mantissa bit vector + static PrivateRNGDoubleType doubleMantissa; // mantissa bit vector +public: + RNG(); + // + // Return a long-words word of random bits + // + virtual unsigned long asLong() = 0; + virtual void reset() = 0; + // + // Return random bits converted to either a float or a double + // + float asFloat(); + double asDouble(); +}; + +#endif diff --git a/gnu/lib/libg++/libg++/Random.h b/gnu/lib/libg++/libg++/Random.h new file mode 100644 index 0000000000..925698f68b --- /dev/null +++ b/gnu/lib/libg++/libg++/Random.h @@ -0,0 +1,54 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef _Random_h +#define _Random_h 1 +#ifdef __GNUG__ +#pragma interface +#endif + +#include +#include + +class Random { +protected: + RNG *pGenerator; +public: + Random(RNG *generator); + virtual double operator()() = 0; + + RNG *generator(); + void generator(RNG *p); +}; + + +inline Random::Random(RNG *gen) +{ + pGenerator = gen; +} + +inline RNG *Random::generator() +{ + return(pGenerator); +} + +inline void Random::generator(RNG *p) +{ + pGenerator = p; +} + +#endif diff --git a/gnu/lib/libg++/libg++/Rational.cc b/gnu/lib/libg++/libg++/Rational.cc index 2482b229f5..ea36f04fcf 100644 --- a/gnu/lib/libg++/libg++/Rational.cc +++ b/gnu/lib/libg++/libg++/Rational.cc @@ -2,22 +2,17 @@ Copyright (C) 1988 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ @@ -27,10 +22,10 @@ and this notice must be preserved on all copies. #include #include #include +#include +#include - - -volatile void Rational::error(const char* msg) const +void Rational::error(const char* msg) const { (*lib_error_handler)("Rational", msg); } @@ -193,7 +188,7 @@ Integer trunc(const Rational& x) Rational pow(const Rational& x, const Integer& y) { - long yy = long(y); + long yy = y.as_long(); return pow(x, yy); } @@ -221,14 +216,14 @@ Integer floor(const Rational& x) return q { Integer r; divide(x.num, x.den, q, r); - if (sign(x.num) < 0 && sign(r) != 0) q--; + if (sign(x.num) < 0 && sign(r) != 0) --q; } Integer ceil(const Rational& x) return q { Integer r; divide(x.num, x.den, q, r); - if (sign(x.num) >= 0 && sign(r) != 0) q++; + if (sign(x.num) >= 0 && sign(r) != 0) ++q; } Integer round(const Rational& x) return q @@ -239,9 +234,9 @@ Integer round(const Rational& x) return q if (ucompare(r, x.den) >= 0) { if (sign(x.num) >= 0) - q++; + ++q; else - q--; + --q; } } @@ -296,7 +291,7 @@ Integer floor(const Rational& x) Integer q; Integer r; divide(x.num, x.den, q, r); - if (sign(x.num) < 0 && sign(r) != 0) q--; + if (sign(x.num) < 0 && sign(r) != 0) --q; return q; } @@ -305,7 +300,7 @@ Integer ceil(const Rational& x) Integer q; Integer r; divide(x.num, x.den, q, r); - if (sign(x.num) >= 0 && sign(r) != 0) q++; + if (sign(x.num) >= 0 && sign(r) != 0) ++q; return q; } @@ -318,9 +313,9 @@ Integer round(const Rational& x) if (ucompare(r, x.den) >= 0) { if (sign(x.num) >= 0) - q++; + ++q; else - q--; + --q; } return q; } @@ -351,43 +346,70 @@ Rational pow(const Rational& x, long y) ostream& operator << (ostream& s, const Rational& y) { - if (y.den == 1) - s << Itoa(y.num); + if (y.denominator() == 1L) + s << y.numerator(); else { - s << Itoa(y.num); + s << y.numerator(); s << "/"; - s << Itoa(y.den); + s << y.denominator(); } return s; } istream& operator >> (istream& s, Rational& y) { - s >> y.num; - if (s) +#ifdef _OLD_STREAMS + if (!s.good()) + { + return s; + } +#else + if (!s.ipfx(0)) + { + s.clear(ios::failbit|s.rdstate()); // Redundant if using GNU iostreams. + return s; + } +#endif + Integer n = 0; + Integer d = 1; + if (s >> n) { char ch = 0; s.get(ch); if (ch == '/') { - s >> y.den; - y.normalize(); + s >> d; } else { - s.unget(ch); - y.den = 1; + s.putback(ch); } } + y = Rational(n, d); return s; } int Rational::OK() const { int v = num.OK() && den.OK(); // have valid num and denom - v &= sign(den) > 0; // denominator positive; - v &= ucompare(gcd(num, den), _Int_One) == 0; // relatively prime + if (v) + { + v &= sign(den) > 0; // denominator positive; + v &= ucompare(gcd(num, den), _Int_One) == 0; // relatively prime + } if (!v) error("invariant failure"); return v; } + +int +Rational::fits_in_float() const +{ + return Rational (FLT_MIN) <= *this && *this <= Rational (FLT_MAX); +} + +int +Rational::fits_in_double() const +{ + return Rational (DBL_MIN) <= *this && *this <= Rational (DBL_MAX); +} diff --git a/gnu/lib/libg++/libg++/Rational.h b/gnu/lib/libg++/libg++/Rational.h new file mode 100644 index 0000000000..d955c1bfea --- /dev/null +++ b/gnu/lib/libg++/libg++/Rational.h @@ -0,0 +1,283 @@ +// This may look like C code, but it is really -*- C++ -*- + +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _Rational_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Rational_h 1 + +#include +#include + +class Rational +{ +protected: + Integer num; + Integer den; + + void normalize(); + +public: + Rational(); + Rational(double); + Rational(int n); + Rational(long n); + Rational(int n, int d); + Rational(long n, long d); + Rational(long n, unsigned long d); + Rational(unsigned long n, long d); + Rational(unsigned long n, unsigned long d); + Rational(const Integer& n); + Rational(const Integer& n, const Integer& d); + Rational(const Rational&); + + ~Rational(); + + void operator = (const Rational& y); + + friend int operator == (const Rational& x, const Rational& y); + friend int operator != (const Rational& x, const Rational& y); + friend int operator < (const Rational& x, const Rational& y); + friend int operator <= (const Rational& x, const Rational& y); + friend int operator > (const Rational& x, const Rational& y); + friend int operator >= (const Rational& x, const Rational& y); + + friend Rational operator + (const Rational& x, const Rational& y); + friend Rational operator - (const Rational& x, const Rational& y); + friend Rational operator * (const Rational& x, const Rational& y); + friend Rational operator / (const Rational& x, const Rational& y); + + void operator += (const Rational& y); + void operator -= (const Rational& y); + void operator *= (const Rational& y); + void operator /= (const Rational& y); + +#ifdef __GNUG__ + friend Rational operator ? (const Rational& x, const Rational& y); // max +#endif + + friend Rational operator - (const Rational& x); + + +// builtin Rational functions + + + void negate(); // x = -x + void invert(); // x = 1/x + + friend int sign(const Rational& x); // -1, 0, or +1 + friend Rational abs(const Rational& x); // absolute value + friend Rational sqr(const Rational& x); // square + friend Rational pow(const Rational& x, long y); + friend Rational pow(const Rational& x, const Integer& y); + const Integer& numerator() const; + const Integer& denominator() const; + +// coercion & conversion + + operator double() const; + friend Integer floor(const Rational& x); + friend Integer ceil(const Rational& x); + friend Integer trunc(const Rational& x); + friend Integer round(const Rational& x); + + friend istream& operator >> (istream& s, Rational& y); + friend ostream& operator << (ostream& s, const Rational& y); + + int fits_in_float() const; + int fits_in_double() const; + +// procedural versions of operators + + friend int compare(const Rational& x, const Rational& y); + friend void add(const Rational& x, const Rational& y, Rational& dest); + friend void sub(const Rational& x, const Rational& y, Rational& dest); + friend void mul(const Rational& x, const Rational& y, Rational& dest); + friend void div(const Rational& x, const Rational& y, Rational& dest); + +// error detection + + void error(const char* msg) const; + int OK() const; + +}; + +typedef Rational RatTmp; // backwards compatibility + +inline Rational::Rational() : num(&_ZeroRep), den(&_OneRep) {} +inline Rational::~Rational() {} + +inline Rational::Rational(const Rational& y) :num(y.num), den(y.den) {} + +inline Rational::Rational(const Integer& n) :num(n), den(&_OneRep) {} + +inline Rational::Rational(const Integer& n, const Integer& d) :num(n),den(d) +{ + normalize(); +} + +inline Rational::Rational(long n) :num(n), den(&_OneRep) { } + +inline Rational::Rational(int n) :num(n), den(&_OneRep) { } + +inline Rational::Rational(long n, long d) :num(n), den(d) { normalize(); } +inline Rational::Rational(int n, int d) :num(n), den(d) { normalize(); } +inline Rational::Rational(long n, unsigned long d) :num(n), den(d) +{ + normalize(); +} +inline Rational::Rational(unsigned long n, long d) :num(n), den(d) +{ + normalize(); +} +inline Rational::Rational(unsigned long n, unsigned long d) :num(n), den(d) +{ + normalize(); +} + +inline void Rational::operator = (const Rational& y) +{ + num = y.num; den = y.den; +} + +inline int operator == (const Rational& x, const Rational& y) +{ + return compare(x.num, y.num) == 0 && compare(x.den, y.den) == 0; +} + +inline int operator != (const Rational& x, const Rational& y) +{ + return compare(x.num, y.num) != 0 || compare(x.den, y.den) != 0; +} + +inline int operator < (const Rational& x, const Rational& y) +{ + return compare(x, y) < 0; +} + +inline int operator <= (const Rational& x, const Rational& y) +{ + return compare(x, y) <= 0; +} + +inline int operator > (const Rational& x, const Rational& y) +{ + return compare(x, y) > 0; +} + +inline int operator >= (const Rational& x, const Rational& y) +{ + return compare(x, y) >= 0; +} + +inline int sign(const Rational& x) +{ + return sign(x.num); +} + +inline void Rational::negate() +{ + num.negate(); +} + + +inline void Rational::operator += (const Rational& y) +{ + add(*this, y, *this); +} + +inline void Rational::operator -= (const Rational& y) +{ + sub(*this, y, *this); +} + +inline void Rational::operator *= (const Rational& y) +{ + mul(*this, y, *this); +} + +inline void Rational::operator /= (const Rational& y) +{ + div(*this, y, *this); +} + +inline const Integer& Rational::numerator() const { return num; } +inline const Integer& Rational::denominator() const { return den; } +inline Rational::operator double() const { return ratio(num, den); } + +#ifdef __GNUG__ +inline Rational operator ? (const Rational& x, const Rational& y) +{ + if (compare(x, y) >= 0) return x; else return y; +} +#endif + +#if defined(__GNUG__) && !defined(NO_NRV) + +inline Rational operator + (const Rational& x, const Rational& y) return r +{ + add(x, y, r); +} + +inline Rational operator - (const Rational& x, const Rational& y) return r +{ + sub(x, y, r); +} + +inline Rational operator * (const Rational& x, const Rational& y) return r +{ + mul(x, y, r); +} + +inline Rational operator / (const Rational& x, const Rational& y) return r +{ + div(x, y, r); +} + +#else /* NO_NRV */ + +inline Rational operator + (const Rational& x, const Rational& y) +{ + Rational r; add(x, y, r); return r; +} + +inline Rational operator - (const Rational& x, const Rational& y) +{ + Rational r; sub(x, y, r); return r; +} + +inline Rational operator * (const Rational& x, const Rational& y) +{ + Rational r; mul(x, y, r); return r; +} + +inline Rational operator / (const Rational& x, const Rational& y) +{ + Rational r; div(x, y, r); return r; +} +#endif + +#endif diff --git a/gnu/lib/libg++/libg++/Regex.cc b/gnu/lib/libg++/libg++/Regex.cc index 01e093b004..d10eaec940 100644 --- a/gnu/lib/libg++/libg++/Regex.cc +++ b/gnu/lib/libg++/libg++/Regex.cc @@ -2,22 +2,17 @@ Copyright (C) 1988 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* @@ -33,16 +28,16 @@ and this notice must be preserved on all copies. #include #include -// extern "C" { +extern "C" { #include -// } +} #include Regex::~Regex() { - delete(buf->buffer); - delete(buf->fastmap); + if (buf->buffer) free(buf->buffer); + if (buf->fastmap) free(buf->fastmap); delete(buf); delete(reg); } @@ -54,15 +49,15 @@ Regex::Regex(const char* t, int fast, int bufsize, buf = new re_pattern_buffer; reg = new re_registers; if (fast) - buf->fastmap = new char[256]; + buf->fastmap = (char*)malloc(256); else buf->fastmap = 0; buf->translate = (char*)transtable; if (tlen > bufsize) bufsize = tlen; buf->allocated = bufsize; - buf->buffer = new char [buf->allocated]; - char* msg = re_compile_pattern((char*)t, tlen, buf); + buf->buffer = (char *)malloc(buf->allocated); + char* msg = re_compile_pattern((const char*)t, tlen, buf); if (msg != 0) (*lib_error_handler)("Regex", msg); else if (fast) @@ -109,12 +104,12 @@ int Regex::match(const char*s, int len, int p) const p += len; if (p > len) return -1; - return re_match_2(buf, 0, 0, (unsigned char*)s, p, 0, reg, p); + return re_match_2(buf, 0, 0, (char*)s, p, 0, reg, p); } else if (p > len) return -1; else - return re_match_2(buf, 0, 0, (unsigned char*)s, len, p, reg, len); + return re_match_2(buf, 0, 0, (char*)s, len, p, reg, len); } int Regex::OK() const diff --git a/gnu/lib/libg++/libg++/Regex.h b/gnu/lib/libg++/libg++/Regex.h new file mode 100644 index 0000000000..c8cf7319f2 --- /dev/null +++ b/gnu/lib/libg++/libg++/Regex.h @@ -0,0 +1,76 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _Regex_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Regex_h 1 + +#if defined(SHORT_NAMES) || defined(VMS) +#define re_compile_pattern recmppat +#define re_pattern_buffer repatbuf +#define re_registers reregs +#endif + +struct re_pattern_buffer; // defined elsewhere +struct re_registers; + +class Regex +{ +private: + + Regex(const Regex&) {} // no X(X&) + void operator = (const Regex&) {} // no assignment + +protected: + re_pattern_buffer* buf; + re_registers* reg; + +public: + Regex(const char* t, + int fast = 0, + int bufsize = 40, + const char* transtable = 0); + + ~Regex(); + + int match(const char* s, int len, int pos = 0) const; + int search(const char* s, int len, + int& matchlen, int startpos = 0) const; + int match_info(int& start, int& length, int nth = 0) const; + + int OK() const; // representation invariant +}; + +// some built in regular expressions + +extern const Regex RXwhite; // = "[ \n\t\r\v\f]+" +extern const Regex RXint; // = "-?[0-9]+" +extern const Regex RXdouble; // = "-?\\(\\([0-9]+\\.[0-9]*\\)\\| + // \\([0-9]+\\)\\|\\(\\.[0-9]+\\)\\) + // \\([eE][---+]?[0-9]+\\)?" +extern const Regex RXalpha; // = "[A-Za-z]+" +extern const Regex RXlowercase; // = "[a-z]+" +extern const Regex RXuppercase; // = "[A-Z]+" +extern const Regex RXalphanum; // = "[0-9A-Za-z]+" +extern const Regex RXidentifier; // = "[A-Za-z_][A-Za-z0-9_]*" + + +#endif diff --git a/gnu/lib/libg++/libg++/RndInt.h b/gnu/lib/libg++/libg++/RndInt.h new file mode 100644 index 0000000000..b16c7f88ec --- /dev/null +++ b/gnu/lib/libg++/libg++/RndInt.h @@ -0,0 +1,175 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1990 Free Software Foundation + adapted from a submission from John Reidl + + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY. No author or distributor +accepts responsibility to anyone for the consequences of using it +or for whether it serves any particular purpose or works at all, +unless he says so in writing. Refer to the GNU CC General Public +License for full details. + +Everyone is granted permission to copy, modify and redistribute +GNU CC, but only under the conditions described in the +GNU CC General Public License. A copy of this license is +supposed to have been given to you along with GNU CC so you +can know your rights and responsibilities. It should be in a +file named COPYING. Among other things, the copyright notice +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _RandomInteger_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _RandomInteger_h 1 + +// RandomInteger uses a random number generator to generate an integer +// in a specified range. By default the range is 0..1. Since in my +// experience random numbers are often needed for a wide variety of +// ranges in the same program, this generator accepts a new low or high value +// as an argument to the asLong and operator() methods to temporarily +// override stored values + +#include +#include + +class RandomInteger +{ + protected: + RNG *pGenerator; + long pLow; + long pHigh; + + long _asLong(long, long); + + public: + + RandomInteger(long low, long high, RNG *gen); + RandomInteger(long high, RNG *gen); + RandomInteger(RNG *gen); + +// read params + + long low() const; + long high() const; + RNG* generator() const; + +// change params + + long low(long x); + long high(long x); + RNG* generator(RNG *gen); + +// get a random number + + long asLong(); + long operator()(); // synonym for asLong + int asInt(); // (possibly) truncate as int + +// override params for one shot + + long asLong(long high); + long asLong(long low, long high); + + long operator () (long high); // synonyms + long operator () (long low, long high); + +}; + + +inline RandomInteger::RandomInteger(long low, long high, RNG *gen) + : pLow((low < high) ? low : high), + pHigh((low < high) ? high : low), + pGenerator(gen) +{} + +inline RandomInteger::RandomInteger(long high, RNG *gen) + : pLow((0 < high) ? 0 : high), + pHigh((0 < high) ? high : 0), + pGenerator(gen) +{} + + +inline RandomInteger::RandomInteger(RNG *gen) + : pLow(0), + pHigh(1), + pGenerator(gen) +{} + +inline RNG* RandomInteger::generator() const { return pGenerator;} +inline long RandomInteger::low() const { return pLow; } +inline long RandomInteger::high() const { return pHigh; } + +inline RNG* RandomInteger::generator(RNG *gen) +{ + RNG *tmp = pGenerator; pGenerator = gen; return tmp; +} + +inline long RandomInteger::low(long x) +{ + long tmp = pLow; pLow = x; return tmp; +} + +inline long RandomInteger:: high(long x) +{ + long tmp = pHigh; pHigh = x; return tmp; +} + +inline long RandomInteger:: _asLong(long low, long high) +{ + return (pGenerator->asLong() % (high-low+1)) + low; +} + + +inline long RandomInteger:: asLong() +{ + return _asLong(pLow, pHigh); +} + +inline long RandomInteger:: asLong(long high) +{ + return _asLong(pLow, high); +} + +inline long RandomInteger:: asLong(long low, long high) +{ + return _asLong(low, high); +} + +inline long RandomInteger:: operator () () +{ + return _asLong(pLow, pHigh); +} + +inline long RandomInteger:: operator () (long high) +{ + return _asLong(pLow, high); +} + +inline long RandomInteger:: operator () (long low, long high) +{ + return _asLong(low, high); +} + + + + +inline int RandomInteger:: asInt() +{ + return int(asLong()); +} + +#endif diff --git a/gnu/lib/libg++/libg++/SFile.cc b/gnu/lib/libg++/libg++/SFile.cc deleted file mode 100644 index 951196f3f8..0000000000 --- a/gnu/lib/libg++/libg++/SFile.cc +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright (C) 1988 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifdef __GNUG__ -#pragma implementation -#endif -#include - -SFile::SFile() {} -SFile::~SFile() {} - -SFile::SFile(const char* filename, int size, io_mode m, access_mode a) -: (filename, m, a) -{ - sz = size; -} - -SFile::SFile(const char* filename, int size, const char* m) -: (filename, m) -{ - sz = size; -} - -SFile::SFile(int filedesc, int size, io_mode m) -: (filedesc, m) -{ - sz = size; -} - -SFile::SFile(FILE* fileptr, int size) -: (fileptr) -{ - sz = size; -} diff --git a/gnu/lib/libg++/libg++/SLList.cc b/gnu/lib/libg++/libg++/SLList.cc new file mode 100644 index 0000000000..725488cab4 --- /dev/null +++ b/gnu/lib/libg++/libg++/SLList.cc @@ -0,0 +1,247 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988, 1992 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _G_NO_TEMPLATES +#ifdef __GNUG__ +//#pragma implementation +#endif +#include +#include +#include +#include "SLList.h" + +void BaseSLList::error(const char* msg) +{ + (*lib_error_handler)("SLList", msg); +} + +int BaseSLList::length() +{ + int l = 0; + BaseSLNode* t = last; + if (t != 0) do { ++l; t = t->tl; } while (t != last); + return l; +} + +void BaseSLList::clear() +{ + if (last == 0) + return; + + BaseSLNode* p = last->tl; + last->tl = 0; + last = 0; + + while (p != 0) + { + BaseSLNode* nxt = p->tl; + delete_node(p); + p = nxt; + } +} + + +// Note: This is an internal method. It does *not* free old contents! + +void BaseSLList::copy(const BaseSLList& a) +{ + if (a.last == 0) + last = 0; + else + { + BaseSLNode* p = a.last->tl; + BaseSLNode* h = copy_node(p->item()); + last = h; + for (;;) + { + if (p == a.last) + { + last->tl = h; + return; + } + p = p->tl; + BaseSLNode* n = copy_node(p->item()); + last->tl = n; + last = n; + } + } +} + +BaseSLList& BaseSLList::operator = (const BaseSLList& a) +{ + if (last != a.last) + { + clear(); + copy(a); + } + return *this; +} + +Pix BaseSLList::prepend(void *datum) +{ + return prepend(copy_node(datum)); +} + + +Pix BaseSLList::prepend(BaseSLNode* t) +{ + if (t == 0) return 0; + if (last == 0) + t->tl = last = t; + else + { + t->tl = last->tl; + last->tl = t; + } + return Pix(t); +} + + +Pix BaseSLList::append(void *datum) +{ + return append(copy_node(datum)); +} + +Pix BaseSLList::append(BaseSLNode* t) +{ + if (t == 0) return 0; + if (last == 0) + t->tl = last = t; + else + { + t->tl = last->tl; + last->tl = t; + last = t; + } + return Pix(t); +} + +void BaseSLList::join(BaseSLList& b) +{ + BaseSLNode* t = b.last; + b.last = 0; + if (last == 0) + last = t; + else if (t != 0) + { + BaseSLNode* f = last->tl; + last->tl = t->tl; + t->tl = f; + last = t; + } +} + +Pix BaseSLList::ins_after(Pix p, void *datum) +{ + BaseSLNode* u = (BaseSLNode*)p; + BaseSLNode* t = copy_node(datum); + if (last == 0) + t->tl = last = t; + else if (u == 0) // ins_after 0 means prepend + { + t->tl = last->tl; + last->tl = t; + } + else + { + t->tl = u->tl; + u->tl = t; + if (u == last) + last = t; + } + return Pix(t); +} + +void BaseSLList::del_after(Pix p) +{ + BaseSLNode* u = (BaseSLNode*)p; + if (last == 0 || u == last) error("cannot del_after last"); + if (u == 0) u = last; // del_after 0 means delete first + BaseSLNode* t = u->tl; + if (u == t) + last = 0; + else + { + u->tl = t->tl; + if (last == t) + last = u; + } + delete_node(t); +} + +int BaseSLList::owns(Pix p) +{ + BaseSLNode* t = last; + if (t != 0 && p != 0) + { + do + { + if (Pix(t) == p) return 1; + t = t->tl; + } while (t != last); + } + return 0; +} + +int BaseSLList::remove_front(void *dst, int signal_error) +{ + if (last) + { + BaseSLNode* t = last->tl; + copy_item(dst, t->item()); + if (t == last) + last = 0; + else + last->tl = t->tl; + delete_node(t); + return 1; + } + if (signal_error) + error("remove_front of empty list"); + return 0; +} + +void BaseSLList::del_front() +{ + if (last == 0) error("del_front of empty list"); + BaseSLNode* t = last->tl; + if (t == last) + last = 0; + else + last->tl = t->tl; + delete_node(t); +} + +int BaseSLList::OK() +{ + int v = 1; + if (last != 0) + { + BaseSLNode* t = last; + long count = MAXLONG; // Lots of chances to find last! + do + { + count--; + t = t->tl; + } while (count > 0 && t != last); + v &= count > 0; + } + if (!v) error("invariant failure"); + return v; +} +#endif /*!_G_NO_TEMPLATES*/ diff --git a/gnu/lib/libg++/libg++/SLList.h b/gnu/lib/libg++/libg++/SLList.h new file mode 100644 index 0000000000..a900e19c4a --- /dev/null +++ b/gnu/lib/libg++/libg++/SLList.h @@ -0,0 +1,115 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988, 1992 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _SLList_h +#ifdef __GNUG__ +//#pragma interface +#endif +#define _SLList_h 1 + +#include + +struct BaseSLNode +{ + BaseSLNode *tl; + void *item() {return (void*)(this+1);} // Return ((SLNode*)this)->hd +}; + +template +class SLNode : public BaseSLNode +{ + public: + T hd; // Data part of node + SLNode() { } + SLNode(const T& h, SLNode* t = 0) + : hd(h) { tl = t; } + ~SLNode() { } +}; + +extern int __SLListLength(BaseSLNode *ptr); + +class BaseSLList { + protected: + BaseSLNode *last; + virtual void delete_node(BaseSLNode*node) = 0; + virtual BaseSLNode* copy_node(void* datum) = 0; + virtual void copy_item(void *dst, void *src) = 0; + virtual ~BaseSLList() { } + BaseSLList() { last = 0; } + void copy(const BaseSLList&); + BaseSLList& operator = (const BaseSLList& a); + Pix ins_after(Pix p, void *datum); + Pix prepend(void *datum); + Pix append(void *datum); + int remove_front(void *dst, int signal_error = 0); + void join(BaseSLList&); + public: + int length(); + void clear(); + Pix prepend(BaseSLNode*); + Pix append(BaseSLNode*); + int OK(); + void error(const char* msg); + void del_after(Pix p); + int owns(Pix p); + void del_front(); +}; + +template +class SLList : public BaseSLList +{ + private: + virtual void delete_node(BaseSLNode *node) { delete (SLNode*)node; } + virtual BaseSLNode* copy_node(void *datum) + { return new SLNode(*(T*)datum); } + virtual void copy_item(void *dst, void *src) { *(T*)dst = *(T*)src; } + +public: + SLList() : BaseSLList() { } + SLList(const SLList& a) : BaseSLList() { copy(a); } + SLList& operator = (const SLList& a) + { BaseSLList::operator=((const BaseSLList&) a); return *this; } + virtual ~SLList() { clear(); } + + int empty() { return last == 0; } + + Pix prepend(T& item) {return BaseSLList::prepend(&item);} + Pix append(T& item) {return BaseSLList::append(&item);} + Pix prepend(SLNode* node) {return BaseSLList::prepend(node);} + Pix append(SLNode* node) {return BaseSLList::append(node);} + + T& operator () (Pix p) { + if (p == 0) error("null Pix"); + return ((SLNode*)(p))->hd; } + inline Pix first() { return (last == 0)? 0 : Pix(last->tl); } + void next(Pix& p) + { p = (p == 0 || p == last)? 0 : Pix(((SLNode*)(p))->tl); } + Pix ins_after(Pix p, T& item) { return BaseSLList::ins_after(p, &item); } + void join(SLList& a) { BaseSLList::join(a); } + + T& front() { + if (last == 0) error("front: empty list"); + return ((SLNode*)last->tl)->hd; } + T& rear() { + if (last == 0) error("rear: empty list"); + return ((SLNode*)last)->hd; } + int remove_front(T& x) { return BaseSLList::remove_front(&x); } + T remove_front() { T dst; BaseSLList::remove_front(&dst, 1); return dst; } +}; + +#endif diff --git a/gnu/lib/libg++/libg++/Sample.cc b/gnu/lib/libg++/libg++/Sample.cc deleted file mode 100644 index c5edf108b1..0000000000 --- a/gnu/lib/libg++/libg++/Sample.cc +++ /dev/null @@ -1,241 +0,0 @@ -// @(#)Sample.cc 6.2 (Berkeley) 2/25/91 - -// Modified for Berkeley Unix by Donn Seeley, donn@okeeffe.berkeley.edu - -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988 Free Software Foundation - written by Dirk Grunwald (grunwald@cs.uiuc.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ -#ifdef __GNUG__ -#pragma implementation -#endif -#include -#include -#include - -// error handling - -void default_SampleStatistic_error_handler(const char* msg) -{ - cerr << "Fatal SampleStatistic error. " << msg << "\n"; - exit(1); -} - -one_arg_error_handler_t SampleStatistic_error_handler = default_SampleStatistic_error_handler; - -one_arg_error_handler_t set_SampleStatistic_error_handler(one_arg_error_handler_t f) -{ - one_arg_error_handler_t old = SampleStatistic_error_handler; - SampleStatistic_error_handler = f; - return old; -} - -void SampleStatistic::error(const char* msg) -{ - (*SampleStatistic_error_handler)(msg); -} - -// t-distribution: given p-value and degrees of freedom, return t-value -// adapted from Peizer & Pratt JASA, vol63, p1416 - -double tval(double p, int df) -{ - double t; - int positive = p >= 0.5; - p = (positive)? 1.0 - p : p; - if (p <= 0.0 || df <= 0) - t = HUGE; - else if (p == 0.5) - t = 0.0; - else if (df == 1) - t = 1.0 / tan((p + p) * 1.57079633); - else if (df == 2) - t = sqrt(1.0 / ((p + p) * (1.0 - p)) - 2.0); - else - { - double ddf = df; - double a = sqrt(log(1.0 / (p * p))); - double aa = a * a; - a = a - ((2.515517 + (0.802853 * a) + (0.010328 * aa)) / - (1.0 + (1.432788 * a) + (0.189269 * aa) + - (0.001308 * aa * a))); - t = ddf - 0.666666667 + 1.0 / (10.0 * ddf); - t = sqrt(ddf * (exp(a * a * (ddf - 0.833333333) / (t * t)) - 1.0)); - } - return (positive)? t : -t; -} - -void -SampleStatistic::reset() -{ - n = 0; x = x2 = 0.0; - maxValue = -HUGE; - minValue = HUGE; -} - -void -SampleStatistic::operator+=(double value) -{ - n += 1; - x += value; - x2 += (value * value); - if ( minValue > value) minValue = value; - if ( maxValue < value) maxValue = value; -} - -double -SampleStatistic::mean() -{ - if ( n > 0) { - return (x / n); - } - else { - return ( 0.0 ); - } -} - -double -SampleStatistic::var() -{ - if ( n > 1) { - return(( x2 - ((x * x) / n)) / ( n - 1)); - } - else { - return ( 0.0 ); - } -} - -double -SampleStatistic::stdDev() -{ - if ( n <= 0 || this -> var() <= 0) { - return(0); - } else { - return( (double) sqrt( var() ) ); - } -} - -double -SampleStatistic::confidence(int interval) -{ - int df = n - 1; - if (df <= 0) return HUGE; - double t = tval(double(100 + interval) * 0.005, df); - if (t == HUGE) - return t; - else - return (t * stdDev()) / sqrt(double(n)); -} - -double -SampleStatistic::confidence(double p_value) -{ - int df = n - 1; - if (df <= 0) return HUGE; - double t = tval((1.0 + p_value) * 0.5, df); - if (t == HUGE) - return t; - else - return (t * stdDev()) / sqrt(double(n)); -} - - -#include - -const int SampleHistogramMinimum = -2; -const int SampleHistogramMaximum = -1; - -SampleHistogram::SampleHistogram(double low, double high, double width) -{ - if (high < low) { - double t = high; - high = low; - low = t; - } - - if (width == -1) { - width = (high - low) / 10; - } - - howManyBuckets = int((high - low) / width) + 2; - bucketCount = new int[howManyBuckets]; - bucketLimit = new double[howManyBuckets]; - double lim = low; - for (int i = 0; i < howManyBuckets; i++) { - bucketCount[i] = 0; - bucketLimit[i] = lim; - lim += width; - } - bucketLimit[howManyBuckets-1] = HUGE; /* from math.h */ -} - -SampleHistogram::~SampleHistogram() -{ - if (howManyBuckets > 0) { - delete bucketCount; - delete bucketLimit; - } -} - -void -SampleHistogram::operator+=(double value) -{ - int i; - for (i = 0; i < howManyBuckets; i++) { - if (value < bucketLimit[i]) break; - } - bucketCount[i]++; - this->SampleStatistic::operator+=(value); -} - -int -SampleHistogram::similarSamples(double d) -{ - int i; - for (i = 0; i < howManyBuckets; i++) { - if (d < bucketLimit[i]) return(bucketCount[i]); - } - return(0); -} - -void -SampleHistogram::printBuckets(ostream& s) -{ - for(int i = 0; i < howManyBuckets; i++) { - if (bucketLimit[i] >= HUGE) { - s << "< max : " << bucketCount[i] << "\n"; - } else { - s << "< " << bucketLimit[i] << " : " << bucketCount[i] << "\n"; - } - } -} - -void -SampleHistogram::reset() -{ - this->SampleStatistic::reset(); - if (howManyBuckets > 0) { - for (register int i = 0; i < howManyBuckets; i++) { - bucketCount[i] = 0; - } - } -} - diff --git a/gnu/lib/libg++/libg++/SmplHist.cc b/gnu/lib/libg++/libg++/SmplHist.cc index 40f78fe354..a1fcd155a8 100644 --- a/gnu/lib/libg++/libg++/SmplHist.cc +++ b/gnu/lib/libg++/libg++/SmplHist.cc @@ -3,22 +3,17 @@ Copyright (C) 1988 Free Software Foundation written by Dirk Grunwald (grunwald@cs.uiuc.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ #pragma implementation @@ -27,6 +22,14 @@ and this notice must be preserved on all copies. #include #include +#ifndef HUGE_VAL +#ifdef HUGE +#define HUGE_VAL HUGE +#else +#include +#define HUGE_VAL DBL_MAX +#endif +#endif const int SampleHistogramMinimum = -2; const int SampleHistogramMaximum = -1; @@ -52,7 +55,7 @@ SampleHistogram::SampleHistogram(double low, double high, double width) bucketLimit[i] = lim; lim += width; } - bucketLimit[howManyBuckets-1] = HUGE; /* from math.h */ + bucketLimit[howManyBuckets-1] = HUGE_VAL; /* from math.h */ } SampleHistogram::~SampleHistogram() @@ -88,7 +91,7 @@ void SampleHistogram::printBuckets(ostream& s) { for(int i = 0; i < howManyBuckets; i++) { - if (bucketLimit[i] >= HUGE) { + if (bucketLimit[i] >= HUGE_VAL) { s << "< max : " << bucketCount[i] << "\n"; } else { s << "< " << bucketLimit[i] << " : " << bucketCount[i] << "\n"; diff --git a/gnu/lib/libg++/libg++/SmplHist.h b/gnu/lib/libg++/libg++/SmplHist.h new file mode 100644 index 0000000000..a9a7550183 --- /dev/null +++ b/gnu/lib/libg++/libg++/SmplHist.h @@ -0,0 +1,72 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef SampleHistogram_h +#ifdef __GNUG__ +#pragma interface +#endif +#define SampleHistogram_h 1 + +#include +#include + +extern const int SampleHistogramMinimum; +extern const int SampleHistogramMaximum; + +class SampleHistogram : public SampleStatistic +{ +protected: + short howManyBuckets; + int *bucketCount; + double *bucketLimit; + +public: + + SampleHistogram(double low, double hi, double bucketWidth = -1.0); + + ~SampleHistogram(); + + virtual void reset(); + virtual void operator+=(double); + + int similarSamples(double); + + int buckets(); + + double bucketThreshold(int i); + int inBucket(int i); + void printBuckets(ostream&); + +}; + + +inline int SampleHistogram:: buckets() { return(howManyBuckets); }; + +inline double SampleHistogram:: bucketThreshold(int i) { + if (i < 0 || i >= howManyBuckets) + error("invalid bucket access"); + return(bucketLimit[i]); +} + +inline int SampleHistogram:: inBucket(int i) { + if (i < 0 || i >= howManyBuckets) + error("invalid bucket access"); + return(bucketCount[i]); +} + +#endif diff --git a/gnu/lib/libg++/libg++/SmplStat.cc b/gnu/lib/libg++/libg++/SmplStat.cc index e57dc47e76..461bea43af 100644 --- a/gnu/lib/libg++/libg++/SmplStat.cc +++ b/gnu/lib/libg++/libg++/SmplStat.cc @@ -3,22 +3,17 @@ Copyright (C) 1988 Free Software Foundation written by Dirk Grunwald (grunwald@cs.uiuc.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ #pragma implementation @@ -27,6 +22,15 @@ and this notice must be preserved on all copies. #include #include +#ifndef HUGE_VAL +#ifdef HUGE +#define HUGE_VAL HUGE +#else +#include +#define HUGE_VAL DBL_MAX +#endif +#endif + // error handling void default_SampleStatistic_error_handler(const char* msg) @@ -58,7 +62,7 @@ double tval(double p, int df) int positive = p >= 0.5; p = (positive)? 1.0 - p : p; if (p <= 0.0 || df <= 0) - t = HUGE; + t = HUGE_VAL; else if (p == 0.5) t = 0.0; else if (df == 1) @@ -83,8 +87,8 @@ void SampleStatistic::reset() { n = 0; x = x2 = 0.0; - maxValue = -HUGE; - minValue = HUGE; + maxValue = -HUGE_VAL; + minValue = HUGE_VAL; } void @@ -133,9 +137,9 @@ double SampleStatistic::confidence(int interval) { int df = n - 1; - if (df <= 0) return HUGE; + if (df <= 0) return HUGE_VAL; double t = tval(double(100 + interval) * 0.005, df); - if (t == HUGE) + if (t == HUGE_VAL) return t; else return (t * stdDev()) / sqrt(double(n)); @@ -145,9 +149,9 @@ double SampleStatistic::confidence(double p_value) { int df = n - 1; - if (df <= 0) return HUGE; + if (df <= 0) return HUGE_VAL; double t = tval((1.0 + p_value) * 0.5, df); - if (t == HUGE) + if (t == HUGE_VAL) return t; else return (t * stdDev()) / sqrt(double(n)); diff --git a/gnu/lib/libg++/libg++/SmplStat.h b/gnu/lib/libg++/libg++/SmplStat.h new file mode 100644 index 0000000000..191ec676f7 --- /dev/null +++ b/gnu/lib/libg++/libg++/SmplStat.h @@ -0,0 +1,66 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef SampleStatistic_h +#ifdef __GNUG__ +#pragma interface +#endif +#define SampleStatistic_h 1 + +#include + +class SampleStatistic { +protected: + int n; + double x; + double x2; + double minValue, maxValue; + + public : + + SampleStatistic(); + virtual ~SampleStatistic(); + virtual void reset(); + + virtual void operator+=(double); + int samples(); + double mean(); + double stdDev(); + double var(); + double min(); + double max(); + double confidence(int p_percentage); + double confidence(double p_value); + + void error(const char* msg); +}; + +// error handlers + +extern void default_SampleStatistic_error_handler(const char*); +extern one_arg_error_handler_t SampleStatistic_error_handler; + +extern one_arg_error_handler_t + set_SampleStatistic_error_handler(one_arg_error_handler_t f); + +inline SampleStatistic:: SampleStatistic(){ reset();} +inline int SampleStatistic:: samples() {return(n);} +inline double SampleStatistic:: min() {return(minValue);} +inline double SampleStatistic:: max() {return(maxValue);} +inline SampleStatistic::~SampleStatistic() {} + +#endif diff --git a/gnu/lib/libg++/libg++/String.cc b/gnu/lib/libg++/libg++/String.cc index a79a4deb4d..6e7a9f87e6 100644 --- a/gnu/lib/libg++/libg++/String.cc +++ b/gnu/lib/libg++/libg++/String.cc @@ -2,22 +2,17 @@ Copyright (C) 1988 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* @@ -30,16 +25,22 @@ and this notice must be preserved on all copies. #include #include #include -#include +#include #include +#include // extern "C" { #include // } -volatile void String::error(const char* msg) const +void String::error(const char* msg) const { - (void)((*lib_error_handler)("String", msg)); + (*lib_error_handler)("String", msg); +} + +String::operator const char*() const +{ + return (const char*)chars(); } // globals @@ -108,7 +109,7 @@ inline static int slen(const char* t) // inline strlen // minimum & maximum representable rep size -#define MAXStrRep_SIZE ((1 << (SHORTBITS - 1)) - 1) +#define MAXStrRep_SIZE ((1 << (sizeof(short) * CHAR_BIT - 1)) - 1) #define MINStrRep_SIZE 16 #ifndef MALLOC_MIN_OVERHEAD @@ -255,7 +256,7 @@ StrRep* Scat(StrRep* old, const char* s, int srclen, const char* t, int tlen, if (old == &_nilStrRep) old = 0; if (srclen < 0) srclen = slen(s); if (tlen < 0) tlen = slen(t); - if (ulen < 0) tlen = slen(u); + if (ulen < 0) ulen = slen(u); int newlen = srclen + tlen + ulen; StrRep* rep; if (old == 0 || newlen > old->sz || @@ -670,7 +671,7 @@ int String::_gsub(const Regex& pat, const char* r, int rl) void String::del(int pos, int len) { - if (pos < 0 || len <= 0 || pos + len > length()) return; + if (pos < 0 || len <= 0 || (unsigned)(pos + len) > length()) return; int nlen = length() - len; int first = pos + len; ncopy0(&(rep->s[first]), &(rep->s[pos]), length() - first); @@ -1194,21 +1195,16 @@ String common_suffix(const String& x, const String& y, int startpos) istream& operator>>(istream& s, String& x) { - if (!s.readable()) + if (!s.ipfx(0) || (!(s.flags() & ios::skipws) && !ws(s))) { - s.set(_bad); + s.clear(ios::failbit|s.rdstate()); // Redundant if using GNU iostreams. return s; } - char ch; + int ch; int i = 0; x.rep = Sresize(x.rep, 20); - s >> WS; - if (!s.good()) - { - s.set(_bad); - return s; - } - while (s.get(ch)) + register streambuf *sb = s.rdbuf(); + while ((ch = sb->sbumpc()) != EOF) { if (isspace(ch)) break; @@ -1218,21 +1214,22 @@ istream& operator>>(istream& s, String& x) } x.rep->s[i] = 0; x.rep->len = i; - s.failif(i == 0); + int new_state = s.rdstate(); + if (i == 0) new_state |= ios::failbit; + if (ch == EOF) new_state |= ios::eofbit; + s.clear(new_state); return s; } int readline(istream& s, String& x, char terminator, int discard) { - if (!s.readable()) - { - s.set(_bad); + if (!s.ipfx(0)) return 0; - } - char ch; + int ch; int i = 0; x.rep = Sresize(x.rep, 80); - while (s.get(ch)) + register streambuf *sb = s.rdbuf(); + while ((ch = sb->sbumpc()) != EOF) { if (ch != terminator || !discard) { @@ -1245,6 +1242,7 @@ int readline(istream& s, String& x, char terminator, int discard) } x.rep->s[i] = 0; x.rep->len = i; + if (ch == EOF) s.clear(ios::eofbit|s.rdstate()); return i; } @@ -1263,7 +1261,7 @@ ostream& operator<<(ostream& s, const SubString& x) int String::freq(const SubString& y) const { int found = 0; - for (int i = 0; i < length(); i++) + for (unsigned int i = 0; i < length(); i++) if (match(i,length(),0,y.chars(), y.length())>= 0) found++; return(found); } @@ -1271,7 +1269,7 @@ int String::freq(const SubString& y) const int String::freq(const String& y) const { int found = 0; - for (int i = 0; i < length(); i++) + for (unsigned int i = 0; i < length(); i++) if (match(i,length(),0,y.chars(),y.length()) >= 0) found++; return(found); } @@ -1279,14 +1277,15 @@ int String::freq(const String& y) const int String::freq(const char* t) const { int found = 0; - for (int i = 0; i < length(); i++) if (match(i,length(),0,t) >= 0) found++; + for (unsigned int i = 0; i < length(); i++) + if (match(i,length(),0,t) >= 0) found++; return(found); } int String::freq(char c) const { int found = 0; - for (int i = 0; i < length(); i++) + for (unsigned int i = 0; i < length(); i++) if (match(i,length(),0,&c,1) >= 0) found++; return(found); } @@ -1294,16 +1293,16 @@ int String::freq(char c) const int String::OK() const { - int v = rep != 0; // have a rep - v &= rep->len <= rep->sz; // string within bounds - v &= rep->s[rep->len] == 0; // null-terminated - if (!v) error("invariant failure"); - return v; + if (rep == 0 // don't have a rep + || rep->len > rep->sz // string oustide bounds + || rep->s[rep->len] != 0) // not null-terminated + error("invariant failure"); + return 1; } int SubString::OK() const { - int v = S != 0; // have a String; + int v = S != (const char*)0; // have a String; v &= S.OK(); // that is legal v &= pos + len >= S.rep->len;// pos and len within bounds if (!v) S.error("SubString invariant failure"); diff --git a/gnu/lib/libg++/libg++/String.h b/gnu/lib/libg++/libg++/String.h new file mode 100644 index 0000000000..178dd69b43 --- /dev/null +++ b/gnu/lib/libg++/libg++/String.h @@ -0,0 +1,1316 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _String_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _String_h 1 + +#include +#include + +struct StrRep // internal String representations +{ + unsigned short len; // string length + unsigned short sz; // allocated space + char s[1]; // the string starts here + // (at least 1 char for trailing null) + // allocated & expanded via non-public fcts +}; + +// primitive ops on StrReps -- nearly all String fns go through these. + +StrRep* Salloc(StrRep*, const char*, int, int); +StrRep* Scopy(StrRep*, StrRep*); +StrRep* Sresize(StrRep*, int); +StrRep* Scat(StrRep*, const char*, int, const char*, int); +StrRep* Scat(StrRep*, const char*, int,const char*,int, const char*,int); +StrRep* Sprepend(StrRep*, const char*, int); +StrRep* Sreverse(StrRep*, StrRep*); +StrRep* Supcase(StrRep*, StrRep*); +StrRep* Sdowncase(StrRep*, StrRep*); +StrRep* Scapitalize(StrRep*, StrRep*); + +// These classes need to be defined in the order given + +class String; +class SubString; + +class SubString +{ + friend class String; +protected: + + String& S; // The String I'm a substring of + unsigned short pos; // starting position in S's rep + unsigned short len; // length of substring + + void assign(StrRep*, const char*, int = -1); + SubString(String& x, int p, int l); + SubString(const SubString& x); + +public: + +// Note there are no public constructors. SubStrings are always +// created via String operations + + ~SubString(); + + void operator = (const String& y); + void operator = (const SubString& y); + void operator = (const char* t); + void operator = (char c); + +// return 1 if target appears anywhere in SubString; else 0 + + int contains(char c) const; + int contains(const String& y) const; + int contains(const SubString& y) const; + int contains(const char* t) const; + int contains(const Regex& r) const; + +// return 1 if target matches entire SubString + + int matches(const Regex& r) const; + +// IO + + friend ostream& operator<<(ostream& s, const SubString& x); + +// status + + unsigned int length() const; + int empty() const; + const char* chars() const; + + int OK() const; + +}; + + +class String +{ + friend class SubString; + +protected: + StrRep* rep; // Strings are pointers to their representations + +// some helper functions + + int search(int, int, const char*, int = -1) const; + int search(int, int, char) const; + int match(int, int, int, const char*, int = -1) const; + int _gsub(const char*, int, const char* ,int); + int _gsub(const Regex&, const char*, int); + SubString _substr(int, int); + +public: + +// constructors & assignment + + String(); + String(const String& x); + String(const SubString& x); + String(const char* t); + String(const char* t, int len); + String(char c); + + ~String(); + + void operator = (const String& y); + void operator = (const char* y); + void operator = (char c); + void operator = (const SubString& y); + +// concatenation + + void operator += (const String& y); + void operator += (const SubString& y); + void operator += (const char* t); + void operator += (char c); + + void prepend(const String& y); + void prepend(const SubString& y); + void prepend(const char* t); + void prepend(char c); + + +// procedural versions: +// concatenate first 2 args, store result in last arg + + friend void cat(const String&, const String&, String&); + friend void cat(const String&, const SubString&, String&); + friend void cat(const String&, const char*, String&); + friend void cat(const String&, char, String&); + + friend void cat(const SubString&, const String&, String&); + friend void cat(const SubString&, const SubString&, String&); + friend void cat(const SubString&, const char*, String&); + friend void cat(const SubString&, char, String&); + + friend void cat(const char*, const String&, String&); + friend void cat(const char*, const SubString&, String&); + friend void cat(const char*, const char*, String&); + friend void cat(const char*, char, String&); + +// double concatenation, by request. (yes, there are too many versions, +// but if one is supported, then the others should be too...) +// Concatenate first 3 args, store in last arg + + friend void cat(const String&,const String&, const String&,String&); + friend void cat(const String&,const String&,const SubString&,String&); + friend void cat(const String&,const String&, const char*, String&); + friend void cat(const String&,const String&, char, String&); + friend void cat(const String&,const SubString&,const String&,String&); + friend void cat(const String&,const SubString&,const SubString&,String&); + friend void cat(const String&,const SubString&, const char*, String&); + friend void cat(const String&,const SubString&, char, String&); + friend void cat(const String&,const char*, const String&, String&); + friend void cat(const String&,const char*, const SubString&, String&); + friend void cat(const String&,const char*, const char*, String&); + friend void cat(const String&,const char*, char, String&); + + friend void cat(const char*, const String&, const String&,String&); + friend void cat(const char*,const String&,const SubString&,String&); + friend void cat(const char*,const String&, const char*, String&); + friend void cat(const char*,const String&, char, String&); + friend void cat(const char*,const SubString&,const String&,String&); + friend void cat(const char*,const SubString&,const SubString&,String&); + friend void cat(const char*,const SubString&, const char*, String&); + friend void cat(const char*,const SubString&, char, String&); + friend void cat(const char*,const char*, const String&, String&); + friend void cat(const char*,const char*, const SubString&, String&); + friend void cat(const char*,const char*, const char*, String&); + friend void cat(const char*,const char*, char, String&); + + +// searching & matching + +// return position of target in string or -1 for failure + + int index(char c, int startpos = 0) const; + int index(const String& y, int startpos = 0) const; + int index(const SubString& y, int startpos = 0) const; + int index(const char* t, int startpos = 0) const; + int index(const Regex& r, int startpos = 0) const; + +// return 1 if target appears anyhere in String; else 0 + + int contains(char c) const; + int contains(const String& y) const; + int contains(const SubString& y) const; + int contains(const char* t) const; + int contains(const Regex& r) const; + +// return 1 if target appears anywhere after position pos +// (or before, if pos is negative) in String; else 0 + + int contains(char c, int pos) const; + int contains(const String& y, int pos) const; + int contains(const SubString& y, int pos) const; + int contains(const char* t, int pos) const; + int contains(const Regex& r, int pos) const; + +// return 1 if target appears at position pos in String; else 0 + + int matches(char c, int pos = 0) const; + int matches(const String& y, int pos = 0) const; + int matches(const SubString& y, int pos = 0) const; + int matches(const char* t, int pos = 0) const; + int matches(const Regex& r, int pos = 0) const; + +// return number of occurences of target in String + + int freq(char c) const; + int freq(const String& y) const; + int freq(const SubString& y) const; + int freq(const char* t) const; + +// SubString extraction + +// Note that you can't take a substring of a const String, since +// this leaves open the possiblility of indirectly modifying the +// String through the SubString + + SubString at(int pos, int len); + SubString operator () (int pos, int len); // synonym for at + + SubString at(const String& x, int startpos = 0); + SubString at(const SubString& x, int startpos = 0); + SubString at(const char* t, int startpos = 0); + SubString at(char c, int startpos = 0); + SubString at(const Regex& r, int startpos = 0); + + SubString before(int pos); + SubString before(const String& x, int startpos = 0); + SubString before(const SubString& x, int startpos = 0); + SubString before(const char* t, int startpos = 0); + SubString before(char c, int startpos = 0); + SubString before(const Regex& r, int startpos = 0); + + SubString through(int pos); + SubString through(const String& x, int startpos = 0); + SubString through(const SubString& x, int startpos = 0); + SubString through(const char* t, int startpos = 0); + SubString through(char c, int startpos = 0); + SubString through(const Regex& r, int startpos = 0); + + SubString from(int pos); + SubString from(const String& x, int startpos = 0); + SubString from(const SubString& x, int startpos = 0); + SubString from(const char* t, int startpos = 0); + SubString from(char c, int startpos = 0); + SubString from(const Regex& r, int startpos = 0); + + SubString after(int pos); + SubString after(const String& x, int startpos = 0); + SubString after(const SubString& x, int startpos = 0); + SubString after(const char* t, int startpos = 0); + SubString after(char c, int startpos = 0); + SubString after(const Regex& r, int startpos = 0); + + +// deletion + +// delete len chars starting at pos + void del(int pos, int len); + +// delete the first occurrence of target after startpos + + void del(const String& y, int startpos = 0); + void del(const SubString& y, int startpos = 0); + void del(const char* t, int startpos = 0); + void del(char c, int startpos = 0); + void del(const Regex& r, int startpos = 0); + +// global substitution: substitute all occurrences of pat with repl + + int gsub(const String& pat, const String& repl); + int gsub(const SubString& pat, const String& repl); + int gsub(const char* pat, const String& repl); + int gsub(const char* pat, const char* repl); + int gsub(const Regex& pat, const String& repl); + +// friends & utilities + +// split string into array res at separators; return number of elements + + friend int split(const String& x, String res[], int maxn, + const String& sep); + friend int split(const String& x, String res[], int maxn, + const Regex& sep); + + friend String common_prefix(const String& x, const String& y, + int startpos = 0); + friend String common_suffix(const String& x, const String& y, + int startpos = -1); + friend String replicate(char c, int n); + friend String replicate(const String& y, int n); + friend String join(String src[], int n, const String& sep); + +// simple builtin transformations + + friend String reverse(const String& x); + friend String upcase(const String& x); + friend String downcase(const String& x); + friend String capitalize(const String& x); + +// in-place versions of above + + void reverse(); + void upcase(); + void downcase(); + void capitalize(); + +// element extraction + + char& operator [] (int i); + char elem(int i) const; + char firstchar() const; + char lastchar() const; + +// conversion + + operator const char*() const; + const char* chars() const; + + +// IO + + friend ostream& operator<<(ostream& s, const String& x); + friend ostream& operator<<(ostream& s, const SubString& x); + friend istream& operator>>(istream& s, String& x); + + friend int readline(istream& s, String& x, + char terminator = '\n', + int discard_terminator = 1); + +// status + + unsigned int length() const; + int empty() const; + +// preallocate some space for String + void alloc(int newsize); + +// report current allocation (not length!) + + int allocation() const; + + + void error(const char* msg) const; + + int OK() const; +}; + +typedef String StrTmp; // for backward compatibility + +// other externs + +int compare(const String& x, const String& y); +int compare(const String& x, const SubString& y); +int compare(const String& x, const char* y); +int compare(const SubString& x, const String& y); +int compare(const SubString& x, const SubString& y); +int compare(const SubString& x, const char* y); +int fcompare(const String& x, const String& y); // ignore case + +extern StrRep _nilStrRep; +extern String _nilString; + +// other inlines + +String operator + (const String& x, const String& y); +String operator + (const String& x, const SubString& y); +String operator + (const String& x, const char* y); +String operator + (const String& x, char y); +String operator + (const SubString& x, const String& y); +String operator + (const SubString& x, const SubString& y); +String operator + (const SubString& x, const char* y); +String operator + (const SubString& x, char y); +String operator + (const char* x, const String& y); +String operator + (const char* x, const SubString& y); + +int operator==(const String& x, const String& y); +int operator!=(const String& x, const String& y); +int operator> (const String& x, const String& y); +int operator>=(const String& x, const String& y); +int operator< (const String& x, const String& y); +int operator<=(const String& x, const String& y); +int operator==(const String& x, const SubString& y); +int operator!=(const String& x, const SubString& y); +int operator> (const String& x, const SubString& y); +int operator>=(const String& x, const SubString& y); +int operator< (const String& x, const SubString& y); +int operator<=(const String& x, const SubString& y); +int operator==(const String& x, const char* t); +int operator!=(const String& x, const char* t); +int operator> (const String& x, const char* t); +int operator>=(const String& x, const char* t); +int operator< (const String& x, const char* t); +int operator<=(const String& x, const char* t); +int operator==(const SubString& x, const String& y); +int operator!=(const SubString& x, const String& y); +int operator> (const SubString& x, const String& y); +int operator>=(const SubString& x, const String& y); +int operator< (const SubString& x, const String& y); +int operator<=(const SubString& x, const String& y); +int operator==(const SubString& x, const SubString& y); +int operator!=(const SubString& x, const SubString& y); +int operator> (const SubString& x, const SubString& y); +int operator>=(const SubString& x, const SubString& y); +int operator< (const SubString& x, const SubString& y); +int operator<=(const SubString& x, const SubString& y); +int operator==(const SubString& x, const char* t); +int operator!=(const SubString& x, const char* t); +int operator> (const SubString& x, const char* t); +int operator>=(const SubString& x, const char* t); +int operator< (const SubString& x, const char* t); +int operator<=(const SubString& x, const char* t); + + +// status reports, needed before defining other things + +inline unsigned int String::length() const { return rep->len; } +inline int String::empty() const { return rep->len == 0; } +inline const char* String::chars() const { return &(rep->s[0]); } +inline int String::allocation() const { return rep->sz; } +inline void String::alloc(int newsize) { rep = Sresize(rep, newsize); } + +inline unsigned int SubString::length() const { return len; } +inline int SubString::empty() const { return len == 0; } +inline const char* SubString::chars() const { return &(S.rep->s[pos]); } + + +// constructors + +inline String::String() + : rep(&_nilStrRep) {} +inline String::String(const String& x) + : rep(Scopy(0, x.rep)) {} +inline String::String(const char* t) + : rep(Salloc(0, t, -1, -1)) {} +inline String::String(const char* t, int tlen) + : rep(Salloc(0, t, tlen, tlen)) {} +inline String::String(const SubString& y) + : rep(Salloc(0, y.chars(), y.length(), y.length())) {} +inline String::String(char c) + : rep(Salloc(0, &c, 1, 1)) {} + +inline String::~String() { if (rep != &_nilStrRep) delete rep; } + +inline SubString::SubString(const SubString& x) + :S(x.S), pos(x.pos), len(x.len) {} +inline SubString::SubString(String& x, int first, int l) + :S(x), pos(first), len(l) {} + +inline SubString::~SubString() {} + +// assignment + +inline void String::operator = (const String& y) +{ + rep = Scopy(rep, y.rep); +} + +inline void String::operator=(const char* t) +{ + rep = Salloc(rep, t, -1, -1); +} + +inline void String::operator=(const SubString& y) +{ + rep = Salloc(rep, y.chars(), y.length(), y.length()); +} + +inline void String::operator=(char c) +{ + rep = Salloc(rep, &c, 1, 1); +} + + +inline void SubString::operator = (const char* ys) +{ + assign(0, ys); +} + +inline void SubString::operator = (char ch) +{ + assign(0, &ch, 1); +} + +inline void SubString::operator = (const String& y) +{ + assign(y.rep, y.chars(), y.length()); +} + +inline void SubString::operator = (const SubString& y) +{ + assign(y.S.rep, y.chars(), y.length()); +} + +// Zillions of cats... + +inline void cat(const String& x, const String& y, String& r) +{ + r.rep = Scat(r.rep, x.chars(), x.length(), y.chars(), y.length()); +} + +inline void cat(const String& x, const SubString& y, String& r) +{ + r.rep = Scat(r.rep, x.chars(), x.length(), y.chars(), y.length()); +} + +inline void cat(const String& x, const char* y, String& r) +{ + r.rep = Scat(r.rep, x.chars(), x.length(), y, -1); +} + +inline void cat(const String& x, char y, String& r) +{ + r.rep = Scat(r.rep, x.chars(), x.length(), &y, 1); +} + +inline void cat(const SubString& x, const String& y, String& r) +{ + r.rep = Scat(r.rep, x.chars(), x.length(), y.chars(), y.length()); +} + +inline void cat(const SubString& x, const SubString& y, String& r) +{ + r.rep = Scat(r.rep, x.chars(), x.length(), y.chars(), y.length()); +} + +inline void cat(const SubString& x, const char* y, String& r) +{ + r.rep = Scat(r.rep, x.chars(), x.length(), y, -1); +} + +inline void cat(const SubString& x, char y, String& r) +{ + r.rep = Scat(r.rep, x.chars(), x.length(), &y, 1); +} + +inline void cat(const char* x, const String& y, String& r) +{ + r.rep = Scat(r.rep, x, -1, y.chars(), y.length()); +} + +inline void cat(const char* x, const SubString& y, String& r) +{ + r.rep = Scat(r.rep, x, -1, y.chars(), y.length()); +} + +inline void cat(const char* x, const char* y, String& r) +{ + r.rep = Scat(r.rep, x, -1, y, -1); +} + +inline void cat(const char* x, char y, String& r) +{ + r.rep = Scat(r.rep, x, -1, &y, 1); +} + +inline void cat(const String& a, const String& x, const String& y, String& r) +{ + r.rep = Scat(r.rep, a.chars(), a.length(), x.chars(), x.length(), y.chars(), y.length()); +} + +inline void cat(const String& a, const String& x, const SubString& y, String& r) +{ + r.rep = Scat(r.rep, a.chars(), a.length(), x.chars(), x.length(), y.chars(), y.length()); +} + +inline void cat(const String& a, const String& x, const char* y, String& r) +{ + r.rep = Scat(r.rep, a.chars(), a.length(), x.chars(), x.length(), y, -1); +} + +inline void cat(const String& a, const String& x, char y, String& r) +{ + r.rep = Scat(r.rep, a.chars(), a.length(), x.chars(), x.length(), &y, 1); +} + +inline void cat(const String& a, const SubString& x, const String& y, String& r) +{ + r.rep = Scat(r.rep, a.chars(), a.length(), x.chars(), x.length(), y.chars(), y.length()); +} + +inline void cat(const String& a, const SubString& x, const SubString& y, String& r) +{ + r.rep = Scat(r.rep, a.chars(), a.length(), x.chars(), x.length(), y.chars(), y.length()); +} + +inline void cat(const String& a, const SubString& x, const char* y, String& r) +{ + r.rep = Scat(r.rep, a.chars(), a.length(), x.chars(), x.length(), y, -1); +} + +inline void cat(const String& a, const SubString& x, char y, String& r) +{ + r.rep = Scat(r.rep, a.chars(), a.length(), x.chars(), x.length(), &y, 1); +} + +inline void cat(const String& a, const char* x, const String& y, String& r) +{ + r.rep = Scat(r.rep, a.chars(), a.length(), x, -1, y.chars(), y.length()); +} + +inline void cat(const String& a, const char* x, const SubString& y, String& r) +{ + r.rep = Scat(r.rep, a.chars(), a.length(), x, -1, y.chars(), y.length()); +} + +inline void cat(const String& a, const char* x, const char* y, String& r) +{ + r.rep = Scat(r.rep, a.chars(), a.length(), x, -1, y, -1); +} + +inline void cat(const String& a, const char* x, char y, String& r) +{ + r.rep = Scat(r.rep, a.chars(), a.length(), x, -1, &y, 1); +} + + +inline void cat(const char* a, const String& x, const String& y, String& r) +{ + r.rep = Scat(r.rep, a, -1, x.chars(), x.length(), y.chars(), y.length()); +} + +inline void cat(const char* a, const String& x, const SubString& y, String& r) +{ + r.rep = Scat(r.rep, a, -1, x.chars(), x.length(), y.chars(), y.length()); +} + +inline void cat(const char* a, const String& x, const char* y, String& r) +{ + r.rep = Scat(r.rep, a, -1, x.chars(), x.length(), y, -1); +} + +inline void cat(const char* a, const String& x, char y, String& r) +{ + r.rep = Scat(r.rep, a, -1, x.chars(), x.length(), &y, 1); +} + +inline void cat(const char* a, const SubString& x, const String& y, String& r) +{ + r.rep = Scat(r.rep, a, -1, x.chars(), x.length(), y.chars(), y.length()); +} + +inline void cat(const char* a, const SubString& x, const SubString& y, String& r) +{ + r.rep = Scat(r.rep, a, -1, x.chars(), x.length(), y.chars(), y.length()); +} + +inline void cat(const char* a, const SubString& x, const char* y, String& r) +{ + r.rep = Scat(r.rep, a, -1, x.chars(), x.length(), y, -1); +} + +inline void cat(const char* a, const SubString& x, char y, String& r) +{ + r.rep = Scat(r.rep, a, -1, x.chars(), x.length(), &y, 1); +} + +inline void cat(const char* a, const char* x, const String& y, String& r) +{ + r.rep = Scat(r.rep, a, -1, x, -1, y.chars(), y.length()); +} + +inline void cat(const char* a, const char* x, const SubString& y, String& r) +{ + r.rep = Scat(r.rep, a, -1, x, -1, y.chars(), y.length()); +} + +inline void cat(const char* a, const char* x, const char* y, String& r) +{ + r.rep = Scat(r.rep, a, -1, x, -1, y, -1); +} + +inline void cat(const char* a, const char* x, char y, String& r) +{ + r.rep = Scat(r.rep, a, -1, x, -1, &y, 1); +} + + +// operator versions + +inline void String::operator +=(const String& y) +{ + cat(*this, y, *this); +} + +inline void String::operator +=(const SubString& y) +{ + cat(*this, y, *this); +} + +inline void String::operator += (const char* y) +{ + cat(*this, y, *this); +} + +inline void String:: operator +=(char y) +{ + cat(*this, y, *this); +} + +// constructive concatenation + +#if defined(__GNUG__) && !defined(NO_NRV) + +inline String operator + (const String& x, const String& y) return r; +{ + cat(x, y, r); +} + +inline String operator + (const String& x, const SubString& y) return r; +{ + cat(x, y, r); +} + +inline String operator + (const String& x, const char* y) return r; +{ + cat(x, y, r); +} + +inline String operator + (const String& x, char y) return r; +{ + cat(x, y, r); +} + +inline String operator + (const SubString& x, const String& y) return r; +{ + cat(x, y, r); +} + +inline String operator + (const SubString& x, const SubString& y) return r; +{ + cat(x, y, r); +} + +inline String operator + (const SubString& x, const char* y) return r; +{ + cat(x, y, r); +} + +inline String operator + (const SubString& x, char y) return r; +{ + cat(x, y, r); +} + +inline String operator + (const char* x, const String& y) return r; +{ + cat(x, y, r); +} + +inline String operator + (const char* x, const SubString& y) return r; +{ + cat(x, y, r); +} + +inline String reverse(const String& x) return r; +{ + r.rep = Sreverse(x.rep, r.rep); +} + +inline String upcase(const String& x) return r; +{ + r.rep = Supcase(x.rep, r.rep); +} + +inline String downcase(const String& x) return r; +{ + r.rep = Sdowncase(x.rep, r.rep); +} + +inline String capitalize(const String& x) return r; +{ + r.rep = Scapitalize(x.rep, r.rep); +} + +#else /* NO_NRV */ + +inline String operator + (const String& x, const String& y) +{ + String r; cat(x, y, r); return r; +} + +inline String operator + (const String& x, const SubString& y) +{ + String r; cat(x, y, r); return r; +} + +inline String operator + (const String& x, const char* y) +{ + String r; cat(x, y, r); return r; +} + +inline String operator + (const String& x, char y) +{ + String r; cat(x, y, r); return r; +} + +inline String operator + (const SubString& x, const String& y) +{ + String r; cat(x, y, r); return r; +} + +inline String operator + (const SubString& x, const SubString& y) +{ + String r; cat(x, y, r); return r; +} + +inline String operator + (const SubString& x, const char* y) +{ + String r; cat(x, y, r); return r; +} + +inline String operator + (const SubString& x, char y) +{ + String r; cat(x, y, r); return r; +} + +inline String operator + (const char* x, const String& y) +{ + String r; cat(x, y, r); return r; +} + +inline String operator + (const char* x, const SubString& y) +{ + String r; cat(x, y, r); return r; +} + +inline String reverse(const String& x) +{ + String r; r.rep = Sreverse(x.rep, r.rep); return r; +} + +inline String upcase(const String& x) +{ + String r; r.rep = Supcase(x.rep, r.rep); return r; +} + +inline String downcase(const String& x) +{ + String r; r.rep = Sdowncase(x.rep, r.rep); return r; +} + +inline String capitalize(const String& x) +{ + String r; r.rep = Scapitalize(x.rep, r.rep); return r; +} + +#endif + +// prepend + +inline void String::prepend(const String& y) +{ + rep = Sprepend(rep, y.chars(), y.length()); +} + +inline void String::prepend(const char* y) +{ + rep = Sprepend(rep, y, -1); +} + +inline void String::prepend(char y) +{ + rep = Sprepend(rep, &y, 1); +} + +inline void String::prepend(const SubString& y) +{ + rep = Sprepend(rep, y.chars(), y.length()); +} + +// misc transformations + + +inline void String::reverse() +{ + rep = Sreverse(rep, rep); +} + + +inline void String::upcase() +{ + rep = Supcase(rep, rep); +} + + +inline void String::downcase() +{ + rep = Sdowncase(rep, rep); +} + + +inline void String::capitalize() +{ + rep = Scapitalize(rep, rep); +} + +// element extraction + +inline char& String::operator [] (int i) +{ + if (((unsigned)i) >= length()) error("invalid index"); + return rep->s[i]; +} + +inline char String::elem (int i) const +{ + if (((unsigned)i) >= length()) error("invalid index"); + return rep->s[i]; +} + +inline char String::firstchar() const +{ + return elem(0); +} + +inline char String::lastchar() const +{ + return elem(length() - 1); +} + +// searching + +inline int String::index(char c, int startpos) const +{ + return search(startpos, length(), c); +} + +inline int String::index(const char* t, int startpos) const +{ + return search(startpos, length(), t); +} + +inline int String::index(const String& y, int startpos) const +{ + return search(startpos, length(), y.chars(), y.length()); +} + +inline int String::index(const SubString& y, int startpos) const +{ + return search(startpos, length(), y.chars(), y.length()); +} + +inline int String::index(const Regex& r, int startpos) const +{ + int unused; return r.search(chars(), length(), unused, startpos); +} + +inline int String::contains(char c) const +{ + return search(0, length(), c) >= 0; +} + +inline int String::contains(const char* t) const +{ + return search(0, length(), t) >= 0; +} + +inline int String::contains(const String& y) const +{ + return search(0, length(), y.chars(), y.length()) >= 0; +} + +inline int String::contains(const SubString& y) const +{ + return search(0, length(), y.chars(), y.length()) >= 0; +} + +inline int String::contains(char c, int p) const +{ + return match(p, length(), 0, &c, 1) >= 0; +} + +inline int String::contains(const char* t, int p) const +{ + return match(p, length(), 0, t) >= 0; +} + +inline int String::contains(const String& y, int p) const +{ + return match(p, length(), 0, y.chars(), y.length()) >= 0; +} + +inline int String::contains(const SubString& y, int p) const +{ + return match(p, length(), 0, y.chars(), y.length()) >= 0; +} + +inline int String::contains(const Regex& r) const +{ + int unused; return r.search(chars(), length(), unused, 0) >= 0; +} + +inline int String::contains(const Regex& r, int p) const +{ + return r.match(chars(), length(), p) >= 0; +} + + +inline int String::matches(const SubString& y, int p) const +{ + return match(p, length(), 1, y.chars(), y.length()) >= 0; +} + +inline int String::matches(const String& y, int p) const +{ + return match(p, length(), 1, y.chars(), y.length()) >= 0; +} + +inline int String::matches(const char* t, int p) const +{ + return match(p, length(), 1, t) >= 0; +} + +inline int String::matches(char c, int p) const +{ + return match(p, length(), 1, &c, 1) >= 0; +} + +inline int String::matches(const Regex& r, int p) const +{ + int l = (p < 0)? -p : length() - p; + return r.match(chars(), length(), p) == l; +} + + +inline int SubString::contains(const char* t) const +{ + return S.search(pos, pos+len, t) >= 0; +} + +inline int SubString::contains(const String& y) const +{ + return S.search(pos, pos+len, y.chars(), y.length()) >= 0; +} + +inline int SubString::contains(const SubString& y) const +{ + return S.search(pos, pos+len, y.chars(), y.length()) >= 0; +} + +inline int SubString::contains(char c) const +{ + return S.search(pos, pos+len, c) >= 0; +} + +inline int SubString::contains(const Regex& r) const +{ + int unused; return r.search(chars(), len, unused, 0) >= 0; +} + +inline int SubString::matches(const Regex& r) const +{ + return r.match(chars(), len, 0) == len; +} + + +inline int String::gsub(const String& pat, const String& r) +{ + return _gsub(pat.chars(), pat.length(), r.chars(), r.length()); +} + +inline int String::gsub(const SubString& pat, const String& r) +{ + return _gsub(pat.chars(), pat.length(), r.chars(), r.length()); +} + +inline int String::gsub(const Regex& pat, const String& r) +{ + return _gsub(pat, r.chars(), r.length()); +} + +inline int String::gsub(const char* pat, const String& r) +{ + return _gsub(pat, -1, r.chars(), r.length()); +} + +inline int String::gsub(const char* pat, const char* r) +{ + return _gsub(pat, -1, r, -1); +} + + + +inline ostream& operator<<(ostream& s, const String& x) +{ + s << x.chars(); return s; +} + +// a zillion comparison operators + +inline int operator==(const String& x, const String& y) +{ + return compare(x, y) == 0; +} + +inline int operator!=(const String& x, const String& y) +{ + return compare(x, y) != 0; +} + +inline int operator>(const String& x, const String& y) +{ + return compare(x, y) > 0; +} + +inline int operator>=(const String& x, const String& y) +{ + return compare(x, y) >= 0; +} + +inline int operator<(const String& x, const String& y) +{ + return compare(x, y) < 0; +} + +inline int operator<=(const String& x, const String& y) +{ + return compare(x, y) <= 0; +} + +inline int operator==(const String& x, const SubString& y) +{ + return compare(x, y) == 0; +} + +inline int operator!=(const String& x, const SubString& y) +{ + return compare(x, y) != 0; +} + +inline int operator>(const String& x, const SubString& y) +{ + return compare(x, y) > 0; +} + +inline int operator>=(const String& x, const SubString& y) +{ + return compare(x, y) >= 0; +} + +inline int operator<(const String& x, const SubString& y) +{ + return compare(x, y) < 0; +} + +inline int operator<=(const String& x, const SubString& y) +{ + return compare(x, y) <= 0; +} + +inline int operator==(const String& x, const char* t) +{ + return compare(x, t) == 0; +} + +inline int operator!=(const String& x, const char* t) +{ + return compare(x, t) != 0; +} + +inline int operator>(const String& x, const char* t) +{ + return compare(x, t) > 0; +} + +inline int operator>=(const String& x, const char* t) +{ + return compare(x, t) >= 0; +} + +inline int operator<(const String& x, const char* t) +{ + return compare(x, t) < 0; +} + +inline int operator<=(const String& x, const char* t) +{ + return compare(x, t) <= 0; +} + +inline int operator==(const SubString& x, const String& y) +{ + return compare(y, x) == 0; +} + +inline int operator!=(const SubString& x, const String& y) +{ + return compare(y, x) != 0; +} + +inline int operator>(const SubString& x, const String& y) +{ + return compare(y, x) < 0; +} + +inline int operator>=(const SubString& x, const String& y) +{ + return compare(y, x) <= 0; +} + +inline int operator<(const SubString& x, const String& y) +{ + return compare(y, x) > 0; +} + +inline int operator<=(const SubString& x, const String& y) +{ + return compare(y, x) >= 0; +} + +inline int operator==(const SubString& x, const SubString& y) +{ + return compare(x, y) == 0; +} + +inline int operator!=(const SubString& x, const SubString& y) +{ + return compare(x, y) != 0; +} + +inline int operator>(const SubString& x, const SubString& y) +{ + return compare(x, y) > 0; +} + +inline int operator>=(const SubString& x, const SubString& y) +{ + return compare(x, y) >= 0; +} + +inline int operator<(const SubString& x, const SubString& y) +{ + return compare(x, y) < 0; +} + +inline int operator<=(const SubString& x, const SubString& y) +{ + return compare(x, y) <= 0; +} + +inline int operator==(const SubString& x, const char* t) +{ + return compare(x, t) == 0; +} + +inline int operator!=(const SubString& x, const char* t) +{ + return compare(x, t) != 0; +} + +inline int operator>(const SubString& x, const char* t) +{ + return compare(x, t) > 0; +} + +inline int operator>=(const SubString& x, const char* t) +{ + return compare(x, t) >= 0; +} + +inline int operator<(const SubString& x, const char* t) +{ + return compare(x, t) < 0; +} + +inline int operator<=(const SubString& x, const char* t) +{ + return compare(x, t) <= 0; +} + + +// a helper needed by at, before, etc. + +inline SubString String::_substr(int first, int l) +{ + if (first < 0 || (unsigned)(first + l) > length() ) + return SubString(_nilString, 0, 0) ; + else + return SubString(*this, first, l); +} + +#endif diff --git a/gnu/lib/libg++/libg++/Uniform.cc b/gnu/lib/libg++/libg++/Uniform.cc index 3d04c61848..2bf7259bdc 100644 --- a/gnu/lib/libg++/libg++/Uniform.cc +++ b/gnu/lib/libg++/libg++/Uniform.cc @@ -2,29 +2,23 @@ Copyright (C) 1988 Free Software Foundation written by Dirk Grunwald (grunwald@cs.uiuc.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ #pragma implementation #endif #include #include - #include double Uniform::operator()() diff --git a/gnu/lib/libg++/libg++/Uniform.h b/gnu/lib/libg++/libg++/Uniform.h new file mode 100644 index 0000000000..5933f6b09b --- /dev/null +++ b/gnu/lib/libg++/libg++/Uniform.h @@ -0,0 +1,71 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef _Uniform_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Uniform_h 1 + +#include + +// +// The interval [lo..hi] +// + +class Uniform: public Random { + double pLow; + double pHigh; + double delta; +public: + Uniform(double low, double high, RNG *gen); + + double low(); + double low(double x); + double high(); + double high(double x); + + virtual double operator()(); +}; + + +inline Uniform::Uniform(double low, double high, RNG *gen) : Random(gen) +{ + pLow = (low < high) ? low : high; + pHigh = (low < high) ? high : low; + delta = pHigh - pLow; +} + +inline double Uniform::low() { return pLow; } + +inline double Uniform::low(double x) { + double tmp = pLow; + pLow = x; + delta = pHigh - pLow; + return tmp; +} + +inline double Uniform::high() { return pHigh; } + +inline double Uniform::high(double x) { + double tmp = pHigh; + pHigh = x; + delta = pHigh - pLow; + return tmp; +} + +#endif diff --git a/gnu/lib/libg++/libg++/Weibull.cc b/gnu/lib/libg++/libg++/Weibull.cc index 16024a2417..02670b933d 100644 --- a/gnu/lib/libg++/libg++/Weibull.cc +++ b/gnu/lib/libg++/libg++/Weibull.cc @@ -2,29 +2,23 @@ Copyright (C) 1988 Free Software Foundation written by Dirk Grunwald (grunwald@cs.uiuc.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ #pragma implementation #endif #include #include - #include // diff --git a/gnu/lib/libg++/libg++/Weibull.h b/gnu/lib/libg++/libg++/Weibull.h new file mode 100644 index 0000000000..d03567aa74 --- /dev/null +++ b/gnu/lib/libg++/libg++/Weibull.h @@ -0,0 +1,74 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Dirk Grunwald (grunwald@cs.uiuc.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef _Weibull_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _Weibull_h + +#include + +class Weibull: public Random { +protected: + double pAlpha; + double pInvAlpha; + double pBeta; + + void setState(); + +public: + Weibull(double alpha, double beta, RNG *gen); + + double alpha(); + double alpha(double x); + + double beta(); + double beta(double x); + + virtual double operator()(); +}; + + +inline void Weibull::setState() { + pInvAlpha = 1.0 / pAlpha; +} + +inline Weibull::Weibull(double alpha, double beta, RNG *gen) : Random(gen) +{ + pAlpha = alpha; + pBeta = beta; + setState(); +} + +inline double Weibull::alpha() { return pAlpha; } + +inline double Weibull::alpha(double x) { + double tmp = pAlpha; + pAlpha = x; + setState(); + return tmp; +} + +inline double Weibull::beta() { return pBeta; }; +inline double Weibull::beta(double x) { + double tmp = pBeta; + pBeta = x; + return tmp; +}; + +#endif diff --git a/gnu/lib/libg++/libg++/_G_config.h b/gnu/lib/libg++/libg++/_G_config.h new file mode 100644 index 0000000000..ea6b1ce10b --- /dev/null +++ b/gnu/lib/libg++/libg++/_G_config.h @@ -0,0 +1,50 @@ +/* AUTOMATICALLY GENERATED; DO NOT EDIT! */ +#ifndef _G_config_h +#define _G_config_h +#define _G_LIB_VERSION "2.4" +#define _G_NAMES_HAVE_UNDERSCORE 1 +#define _G_DOLLAR_IN_LABEL 1 +#define _G_HAVE_ST_BLKSIZE 1 +typedef unsigned long _G_clock_t; +typedef short _G_dev_t; +typedef long _G_fpos_t; +typedef unsigned short _G_gid_t; +typedef unsigned long _G_ino_t; +typedef unsigned short _G_mode_t; +typedef unsigned short _G_nlink_t; +typedef long _G_off_t; +typedef short _G_pid_t; +typedef int _G_ptrdiff_t; +typedef unsigned int _G_sigset_t; +typedef unsigned int _G_size_t; +typedef long _G_time_t; +typedef unsigned short _G_uid_t; +typedef unsigned short _G_wchar_t; +typedef int /* default */ _G_int32_t; +typedef unsigned int /* default */ _G_uint32_t; +typedef int /* default */ _G_ssize_t; +typedef char* /* default */ _G_va_list; +#define _G_signal_return_type void +#define _G_sprintf_return_type int +#define _G_BUFSIZ 1024 +#define _G_FOPEN_MAX 20 +#define _G_FILENAME_MAX 1024 +#define _G_NULL 0 /* default */ +#define _G_USE_PROTOS +#ifdef _G_USE_PROTOS +#define _G_ARGS(ARGLIST) ARGLIST +#else +#define _G_ARGS(ARGLIST) (...) +#endif +/* #define _G_SYSV */ +#define _G_HAVE_SYS_RESOURCE 1 +#define _G_HAVE_SYS_SOCKET 1 +#define _G_HAVE_SYS_WAIT 1 +#define _G_HAVE_UNISTD 1 +#define _G_HAVE_DIRENT 1 +#define _G_HAVE_CURSES 1 +#define _G_CURSES_FORMAT_ARG const char *fmt, +#define _G_MATH_H_INLINES 0 +/* #define _G_BROKEN_SIGNED_CHAR */ +/* #define _G_FRIEND_BUG */ +#endif /* !_G_config_h */ diff --git a/gnu/lib/libg++/libg++/bool.h b/gnu/lib/libg++/libg++/bool.h new file mode 100644 index 0000000000..9f0fd9115a --- /dev/null +++ b/gnu/lib/libg++/libg++/bool.h @@ -0,0 +1,13 @@ +// Defining TRUE and FALSE is usually a Bad Idea, +// because you will probably be inconsistent with anyone +// else who had the same clever idea. +// Therefore: DON'T USE THIS FILE. + +#ifndef _bool_h +#define _bool_h 1 + +#undef FALSE +#undef TRUE +enum bool { FALSE = 0, TRUE = 1 }; + +#endif diff --git a/gnu/lib/libg++/libg++/builtin.h b/gnu/lib/libg++/libg++/builtin.h new file mode 100644 index 0000000000..2212fef6c6 --- /dev/null +++ b/gnu/lib/libg++/libg++/builtin.h @@ -0,0 +1,159 @@ +// This may look like C code, but it is really -*- C++ -*- + +/* +Copyright (C) 1988, 1992 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + arithmetic, etc. functions on built in types +*/ + + +#ifndef _builtin_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _builtin_h 1 + +#include +#include +#include + +#ifdef __GNUG__ +#define _VOLATILE_VOID volatile void +#else +#define _VOLATILE_VOID void +#endif + +typedef void (*one_arg_error_handler_t)(const char*); +typedef void (*two_arg_error_handler_t)(const char*, const char*); + +long gcd(long, long); +long lg(unsigned long); +double pow(double, long); +long pow(long, long); + +double start_timer(); +double return_elapsed_time(double last_time = 0.0); + +char* dtoa(double x, char cvt = 'g', int width = 0, int prec = 6); + +unsigned int hashpjw(const char*); +unsigned int multiplicativehash(int); +unsigned int foldhash(double); + +extern _VOLATILE_VOID default_one_arg_error_handler(const char*); +extern _VOLATILE_VOID default_two_arg_error_handler(const char*, const char*); + +extern two_arg_error_handler_t lib_error_handler; + +extern two_arg_error_handler_t + set_lib_error_handler(two_arg_error_handler_t f); + + +double abs(double arg); +float abs(float arg); +short abs(short arg); +long abs(long arg); +int sign(long arg); +int sign(double arg); +long sqr(long arg); +double sqr(double arg); +int even(long arg); +int odd(long arg); +long lcm(long x, long y); +void (setbit)(long& x, long b); +void clearbit(long& x, long b); +int testbit(long x, long b); + +#if !defined(IV) + +#if ! _G_MATH_H_INLINES /* hpux and SCO define this in math.h */ +inline double abs(double arg) +{ + return (arg < 0.0)? -arg : arg; +} +#endif + +inline float abs(float arg) +{ + return (arg < 0.0)? -arg : arg; +} + +inline short abs(short arg) +{ + return (arg < 0)? -arg : arg; +} + +inline long abs(long arg) +{ + return (arg < 0)? -arg : arg; +} + +inline int sign(long arg) +{ + return (arg == 0) ? 0 : ( (arg > 0) ? 1 : -1 ); +} + +inline int sign(double arg) +{ + return (arg == 0.0) ? 0 : ( (arg > 0.0) ? 1 : -1 ); +} + +inline long sqr(long arg) +{ + return arg * arg; +} + +#if ! _G_MATH_H_INLINES /* hpux and SCO define this in math.h */ +inline double sqr(double arg) +{ + return arg * arg; +} +#endif + +inline int even(long arg) +{ + return !(arg & 1); +} + +inline int odd(long arg) +{ + return (arg & 1); +} + +inline long lcm(long x, long y) +{ + return x / gcd(x, y) * y; +} + +inline void (setbit)(long& x, long b) +{ + x |= (1 << b); +} + +inline void clearbit(long& x, long b) +{ + x &= ~(1 << b); +} + +inline int testbit(long x, long b) +{ + return ((x & (1 << b)) != 0); +} + +#endif +#endif diff --git a/gnu/lib/libg++/libg++/chr.cc b/gnu/lib/libg++/libg++/chr.cc index 2726f310a1..d580a629f5 100644 --- a/gnu/lib/libg++/libg++/chr.cc +++ b/gnu/lib/libg++/libg++/chr.cc @@ -2,22 +2,17 @@ Copyright (C) 1990 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ diff --git a/gnu/lib/libg++/libg++/compare.h b/gnu/lib/libg++/libg++/compare.h new file mode 100644 index 0000000000..bd13614179 --- /dev/null +++ b/gnu/lib/libg++/libg++/compare.h @@ -0,0 +1,91 @@ +// This may look like C code, but it is really -*- C++ -*- + +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _compare_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _compare_h 1 + +#include + +int compare(int a, int b); +int compare(short a, short b); +int compare(unsigned long a, unsigned long b); +int compare(unsigned int a, unsigned int b); +int compare(unsigned short a, unsigned short b); +int compare(unsigned char a, unsigned char b); +int compare(signed char a, signed char b); +int compare(float a, float b); +int compare(double a, double b); +int compare(const char* a, const char* b); + + +inline int compare(int a, int b) +{ + return a - b; +} + +inline int compare(short a, short b) +{ + return a - b; +} + + +inline int compare(signed char a, signed char b) +{ + return a - b; +} + +inline int compare(unsigned long a, unsigned long b) +{ + return (a < b)? -1 : (a > b)? 1 : 0; +} + +inline int compare(unsigned int a, unsigned int b) +{ + return (a < b)? -1 : (a > b)? 1 : 0; +} + +inline int compare(unsigned short a, unsigned short b) +{ + return (a < b)? -1 : (a > b)? 1 : 0; +} + +inline int compare(unsigned char a, unsigned char b) +{ + return (a < b)? -1 : (a > b)? 1 : 0; +} + +inline int compare(float a, float b) +{ + return (a < b)? -1 : (a > b)? 1 : 0; +} + +inline int compare(double a, double b) +{ + return (a < b)? -1 : (a > b)? 1 : 0; +} + +inline int compare(const char* a, const char* b) +{ + return strcmp(a,b); +} + +#endif diff --git a/gnu/lib/libg++/libg++/complex.h b/gnu/lib/libg++/libg++/complex.h new file mode 100644 index 0000000000..afe2c8bcd9 --- /dev/null +++ b/gnu/lib/libg++/libg++/complex.h @@ -0,0 +1,6 @@ +#ifndef _complex_h +#define _complex_h +#define __ATT_complex__ +#include +typedef class Complex complex; +#endif diff --git a/gnu/lib/libg++/libg++/ctype.cc b/gnu/lib/libg++/libg++/ctype.cc deleted file mode 100644 index 3401290f58..0000000000 --- a/gnu/lib/libg++/libg++/ctype.cc +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __GNUG__ -#pragma implementation -#endif -#include diff --git a/gnu/lib/libg++/libg++/curses.cc b/gnu/lib/libg++/libg++/curses.cc deleted file mode 100644 index 943fb380f8..0000000000 --- a/gnu/lib/libg++/libg++/curses.cc +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __GNUG__ -#pragma implementation -#endif -#include diff --git a/gnu/lib/libg++/libg++/delete.cc b/gnu/lib/libg++/libg++/delete.cc deleted file mode 100644 index 7633587a01..0000000000 --- a/gnu/lib/libg++/libg++/delete.cc +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef NO_LIBGXX_MALLOC - -// This may look like C code, but it is really -*- C++ -*- - -/* -Copyright (C) 1989 Free Software Foundation - written by Doug Lea (dl@oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifdef __GNUG__ -#pragma implementation -#endif -#include -#include - -void operator delete(void* ptr) -{ - free (ptr); -} - -#endif diff --git a/gnu/lib/libg++/libg++/dtoa.cc b/gnu/lib/libg++/libg++/dtoa.cc index 25eb7bf01c..81d7551daa 100644 --- a/gnu/lib/libg++/libg++/dtoa.cc +++ b/gnu/lib/libg++/libg++/dtoa.cc @@ -2,22 +2,17 @@ Copyright (C) 1990 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ @@ -30,6 +25,9 @@ and this notice must be preserved on all copies. extern AllocRing _libgxx_fmtq; +#ifdef __GNUC__ /* cfront cannot compile this routine */ +// OBSOLETE ROUTINE! + char* dtoa(double fpnum, char cvt, int width, int prec) { // set up workspace @@ -222,7 +220,8 @@ char* dtoa(double fpnum, char cvt, int width, int prec) fpnum *= 0.1; ++exp; } - while (fpnum > 0.0 && fpnum < 1.0) + double almost_one = 1.0 - rounder; + while (fpnum > 0.0 && fpnum < almost_one) { fpnum *= 10.0; --exp; @@ -333,4 +332,4 @@ char* dtoa(double fpnum, char cvt, int width, int prec) return fmtbase; } - +#endif diff --git a/gnu/lib/libg++/libg++/error.cc b/gnu/lib/libg++/libg++/error.cc index bc4fc1a258..fc14959c4d 100644 --- a/gnu/lib/libg++/libg++/error.cc +++ b/gnu/lib/libg++/libg++/error.cc @@ -2,31 +2,27 @@ Copyright (C) 1990 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ #pragma implementation #endif -#include #include -void default_one_arg_error_handler(const char* msg) +extern "C" _VOLATILE_VOID abort(); + +_VOLATILE_VOID default_one_arg_error_handler(const char* msg) { fputs("Error: ", stderr); fputs(msg, stderr); @@ -35,7 +31,7 @@ void default_one_arg_error_handler(const char* msg) } -void default_two_arg_error_handler(const char* kind, const char* msg) +_VOLATILE_VOID default_two_arg_error_handler(const char* kind, const char* msg) { fputs(kind, stderr); fputs(" Error: ", stderr); diff --git a/gnu/lib/libg++/libg++/filebuf.cc b/gnu/lib/libg++/libg++/filebuf.cc deleted file mode 100644 index 0f8f2a4454..0000000000 --- a/gnu/lib/libg++/libg++/filebuf.cc +++ /dev/null @@ -1,192 +0,0 @@ -/* -Copyright (C) 1990 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#if 1 -#ifdef __GNUG__ -#pragma implementation -#endif -#endif - -#include -#include // needed to determine values of O_RDONLY... -#include -#include - -filebuf::filebuf() - :streambuf(), fd(-1), opened(0) {} - -filebuf::filebuf(int newfd) - : streambuf(), fd(newfd), opened(1) {} - -filebuf::filebuf(int newfd, char* buf, int buflen) - : streambuf(buf, buflen), fd(newfd), opened(1) {} - -filebuf::filebuf(const char* filename, io_mode m, access_mode a) - : streambuf() -{ - open(filename, m, a); -} - -filebuf::filebuf(const char* filename, const char* m) - : streambuf() -{ - open(filename, m); -} - -filebuf::filebuf(int filedesc, io_mode m) - : streambuf() -{ - open(filedesc, m); -} - -filebuf::filebuf(FILE* fileptr) - : streambuf() -{ - open(fileptr); -} - -int filebuf::is_open() -{ - return opened; -} - -int filebuf::close() -{ - int was = opened; - if (was) ::close(fd); - opened = 0; - return was; -} - -streambuf* filebuf::open(const char* filename, open_mode m) -{ - if (opened) return 0; - int mode = -1; // any illegal value - switch (m) - { - case input: mode = O_RDONLY; - break; - case output: mode = O_WRONLY | O_CREAT | O_TRUNC; - break; - case append: mode = O_APPEND | O_CREAT | O_WRONLY; - break; - } - fd = ::open(filename, mode, 0666); - if (opened = (fd >= 0)) - { - allocate(); - return this; - } - else - return 0; -} - - -streambuf* filebuf::open(const char* filename, io_mode m, access_mode a) -{ - int open_arg = open_cmd_arg(m, a); - if (open_arg == -1) return 0; - fd = ::open(filename, open_arg, 0666); - if (opened = (fd >= 0)) - { - allocate(); - return this; - } - else - return 0; - -} - -streambuf* filebuf::open(const char* filename, const char* m) -{ - int open_arg = open_cmd_arg(m); - if (open_arg == -1) return 0; - fd = ::open(filename, open_arg, 0666); - if (opened = (fd >= 0)) - { - allocate(); - return this; - } - else - return 0; -} - -streambuf* filebuf::open(int filedesc, io_mode m) -{ - int open_arg = open_cmd_arg(m, a_use); - if (open_arg == -1) return 0; - fd = ::open(name, open_arg, 0666); - if (opened = (fd >= 0)) - { - allocate(); - return this; - } - else - return 0; -} - -streambuf* filebuf::open(FILE* fileptr) -{ - opened = fileptr != 0; - fd = fileno(fileptr); - return this; -} - -int filebuf::underflow() -{ - if (!opened) return EOF; - if (base == 0) allocate(); - int nwanted = eptr - base + 1; - int nread = ::read(fd, base, nwanted); - if (nread >= 0) - { - gptr = base; - pptr = base + nread; - } - return (nread <= 0)? EOF : int(*gptr); -} - -int filebuf::overflow(int ch) -{ - if (!opened) return EOF; - if (base == 0) allocate(); - if (ch != EOF) // overflow *must* be called before really full - *pptr++ = (char)(ch); - - // loop, in case write can't handle full request - // From: Rene' Seindal - - int w, n, t; - for (w = t = 0, n = pptr - base; n > 0; n -= w, t += w) - { - if ((w = ::write(fd, base + t, n)) < 0) - break; - } - - pptr = base; - return (n == 0 && w >= 0)? 0 : EOF; -} - -filebuf::~filebuf() -{ - close(); -} diff --git a/gnu/lib/libg++/libg++/fmtq.cc b/gnu/lib/libg++/libg++/fmtq.cc index b2699a5886..7a9dd7b4f3 100644 --- a/gnu/lib/libg++/libg++/fmtq.cc +++ b/gnu/lib/libg++/libg++/fmtq.cc @@ -2,22 +2,17 @@ Copyright (C) 1990 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ diff --git a/gnu/lib/libg++/libg++/form.cc b/gnu/lib/libg++/libg++/form.cc deleted file mode 100644 index ae373ed13d..0000000000 --- a/gnu/lib/libg++/libg++/form.cc +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright (C) 1990 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifdef __GNUG__ -#pragma implementation -#endif -#include -#include -#include -#include - -extern AllocRing _libgxx_fmtq; - -char* form(const char* fmt ...) -{ - va_list args; - va_start(args, fmt); - char* fmtbase = (char *) _libgxx_fmtq.alloc(BUFSIZ); -#ifndef HAVE_VPRINTF - FILE b; -#ifdef VMS - b->_flag = _IOWRT|_IOSTRG; - b->_ptr = fmtbase; - b->_cnt = BUFSIZ-1; -#else - b._flag = _IOWRT|_IOSTRG; - b._ptr = fmtbase; - b._cnt = BUFSIZ-1; -#endif - _doprnt(fmt, args, &b); - putc('\0', &b); -#else - vsprintf(fmtbase, fmt, args); -#endif - va_end(args); - return fmtbase; -} diff --git a/gnu/lib/libg++/libg++/g++.order b/gnu/lib/libg++/libg++/g++.order deleted file mode 100644 index 3639cd5097..0000000000 --- a/gnu/lib/libg++/libg++/g++.order +++ /dev/null @@ -1,70 +0,0 @@ -std.o -math.o -curses.o -ctype.o -compare.o -timer.o -str.o -sqrt.o -pow.o -ioob.o -fmtq.o -lg.o -itoa.o -hash.o -gcd.o -form.o -error.o -dtoa.o -chr.o -delete.o -new.o -gnulib3.o -xyzzy.o -EH2.o -EH.o -GetOpt.o -CursesW.o -Fix24.o -Fix16.o -Fix.o -RndInt.o -MLCG.o -ACG.o -RNG.o -min.o -max.o -Binomial.o -Geom.o -HypGeom.o -Poisson.o -Uniform.o -DiscUnif.o -Erlang.o -Weibull.o -NegExp.o -Normal.o -SmplStat.o -SmplHist.o -LogNorm.o -BitString.o -BitSet.o -Random.o -Complex.o -Rational.o -Integer.o -String.o -Regex.o -regex.o -builtin.o -SFile.o -PlotFile.o -open.o -Filebuf.o -istream.o -ostream.o -File.o -Obstack.o -AllocRing.o -filebuf.o -streambuf.o diff --git a/gnu/lib/libg++/libg++/gcd.cc b/gnu/lib/libg++/libg++/gcd.cc index ca7ace9d08..76ff0ef1b8 100644 --- a/gnu/lib/libg++/libg++/gcd.cc +++ b/gnu/lib/libg++/libg++/gcd.cc @@ -2,22 +2,17 @@ Copyright (C) 1990 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ diff --git a/gnu/lib/libg++/libg++/generic.h b/gnu/lib/libg++/libg++/generic.h new file mode 100644 index 0000000000..56d7cfb8d6 --- /dev/null +++ b/gnu/lib/libg++/libg++/generic.h @@ -0,0 +1,54 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef generic_h +#define generic_h 1 + +/* + * See the CPP manual, argument prescan section for explanation + */ + +#define name2(a,b) gEnErIc2(a,b) +#define gEnErIc2(a,b) a ## b + +#define name3(a,b,c) gEnErIc3(a,b,c) +#define gEnErIc3(a,b,c) a ## b ## c + +#define name4(a,b,c,d) gEnErIc4(a,b,c,d) +#define gEnErIc4(a,b,c,d) a ## b ## c ## d + +#define GENERIC_STRING(a) gEnErIcStRiNg(a) +#define gEnErIcStRiNg(a) #a + +#define declare(clas,t) name2(clas,declare)(t) +#define declare2(clas,t1,t2) name2(clas,declare2)(t1,t2) + +#define implement(clas,t) name2(clas,implement)(t) +#define implement2(clas,t1,t2) name2(clas,implement2)(t1,t2) + +//extern genericerror(int,char*); +typedef int (*GPT)(int,char*); + +#define set_handler(gen,type,x) name4(set_,type,gen,_handler)(x) + +#define errorhandler(gen,type) name3(type,gen,handler) + +#define callerror(gen,type,a,b) (*errorhandler(gen,type))(a,b) + + +#endif generic_h diff --git a/gnu/lib/libg++/libg++/getpagesize.h b/gnu/lib/libg++/libg++/getpagesize.h new file mode 100644 index 0000000000..17d84316bd --- /dev/null +++ b/gnu/lib/libg++/libg++/getpagesize.h @@ -0,0 +1,27 @@ +#if defined(BSD) || defined(DGUX) +#ifndef BSD4_1 +#define HAVE_GETPAGESIZE +#endif +#endif + +#ifndef HAVE_GETPAGESIZE + +#include + +#ifdef EXEC_PAGESIZE +#define getpagesize() EXEC_PAGESIZE +#else +#ifdef NBPG +#define getpagesize() NBPG * CLSIZE +#ifndef CLSIZE +#define CLSIZE 1 +#endif /* no CLSIZE */ +#else /* no NBPG */ +#ifdef NBPC +#define getpagesize() NBPC +#endif /* NBPC */ +#endif /* no NBPG */ +#endif /* no EXEC_PAGESIZE */ + +#endif /* not HAVE_GETPAGESIZE */ + diff --git a/gnu/lib/libg++/libg++/gnulib3.c b/gnu/lib/libg++/libg++/gnulib3.c deleted file mode 100644 index d8d212e40f..0000000000 --- a/gnu/lib/libg++/libg++/gnulib3.c +++ /dev/null @@ -1,109 +0,0 @@ - -#ifndef NO_GNULIB3 /* skip entire file if NO_GNULIB3 */ - -typedef struct set_vector -{ - int length; - int vector[1]; - /* struct set_vector *next; */ -} set_vector; - -set_vector __CTOR_LIST__; -set_vector __DTOR_LIST__; -set_vector *__dlp; -int __dli; - -extern void exit (int); -extern void __do_global_init (); -extern void __do_global_cleanup (); -extern void on_exit(void*, void*); -extern void _cleanup(); -extern void _exit(int); - - -#if defined(i386) && !defined(sequent) -#define COFF -#endif - -#ifdef COFF_ENCAPSULATE -#undef COFF -#endif - -/* - ON_EXIT macro commented out - dl - Sun on_exit doesn't get linked in right!!! -*/ - -#if 0 -#if defined(sun) -#define ON_EXIT(PROCP, ARG) \ - do { extern void PROCP (); on_exit (PROCP, ARG); } while (0) -#endif -#endif - -int -__main () -{ - /* Gross hack for GNU ld. This is defined in `builtin.cc' - from libg++. */ -#ifndef COFF - extern int __1xyzzy__; -#endif - -#ifdef ON_EXIT - -#ifdef sun - ON_EXIT (_cleanup, 0); -#endif - - ON_EXIT (__do_global_cleanup, 0); - -#endif - __dli = __DTOR_LIST__.length; - __dlp = &__DTOR_LIST__; -#ifndef COFF - __do_global_init (&__1xyzzy__); -#else - __do_global_init (); -#endif -} - -#ifndef ON_EXIT - -void -exit (status) - int status; -{ - __do_global_cleanup (); - _cleanup (); - _exit (status); -} -#endif - -void -__do_global_init () -{ - register int i, len; - register void (**ppf)() = (void (**)())__CTOR_LIST__.vector; - - len = __CTOR_LIST__.length; - for (i = 0; i < len; i++) - (*ppf[i])(); -} - -void -__do_global_cleanup () -{ - while (__dlp) - { - while (--__dli >= 0) - { - void (*pf)() = (void (*)())__dlp->vector[__dli]; - (*pf)(); - } - __dlp = (struct set_vector *)__dlp->vector[__dlp->length]; - if (__dlp) __dli = __dlp->length; - } -} - -#endif diff --git a/gnu/lib/libg++/libg++/hash.cc b/gnu/lib/libg++/libg++/hash.cc index 9fc3e6bffd..4f16f97598 100644 --- a/gnu/lib/libg++/libg++/hash.cc +++ b/gnu/lib/libg++/libg++/hash.cc @@ -2,22 +2,17 @@ Copyright (C) 1990 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ diff --git a/gnu/lib/libg++/libg++/istream.cc b/gnu/lib/libg++/libg++/istream.cc deleted file mode 100644 index 4898ab6d62..0000000000 --- a/gnu/lib/libg++/libg++/istream.cc +++ /dev/null @@ -1,507 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1989 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -/* *** Version 1.2 -- nearly 100% AT&T 1.2 compatible *** */ - -#ifdef __GNUG__ -#pragma implementation -#endif -#include -#include -#include -#include -#include - - -istream::istream(streambuf* s, int sk, ostream* t) - : bp(s), state(_good), skipws(sk), tied_to(t), ownbuf(0) {} - -istream::istream(int sz, char* buf, int sk, ostream* t) - : state(_good), skipws(sk), tied_to(t), ownbuf(1) -{ - bp = new streambuf; - if (buf == 0) - { - bp->alloc = 1; - buf = new char[sz]; - } - else - bp->alloc = 0; - - bp->setbuf(buf, sz, sz); -} - -istream::~istream() -{ - if (ownbuf) delete bp; -} - -istream::istream(const char* filename, io_mode m, access_mode a, int sk, ostream* t) - : state(_good), skipws(sk), tied_to(t), ownbuf(1) -{ - bp = new filebuf(filename, m, a); -} - -istream::istream(const char* filename, const char* m, int sk, ostream* t) - : state(_good), skipws(sk), tied_to(t), ownbuf(1) -{ - bp = new filebuf(filename, m); -} - -istream::istream(int filedesc, io_mode m, int sk, ostream* t) - : state(_good), skipws(sk), tied_to(t), ownbuf(1) -{ - bp = new filebuf(filedesc, m); -} - -istream::istream(FILE* fileptr, int sk, ostream* t) - : state(_good), skipws(sk), tied_to(t), ownbuf(1) -{ - bp = new filebuf(fileptr); - -} - -istream::istream(int filedesc, int sk, ostream* t) - : state(_good), skipws(sk), tied_to(t), ownbuf(1) -{ - bp = new filebuf(filedesc); -} - -istream::istream(int filedesc, char* buf, int buflen, int sk, ostream* t) - : state(_good), skipws(sk), tied_to(t), ownbuf(1) -{ - bp = new filebuf(filedesc, buf, buflen); -} - -istream& istream::open(const char* filename, io_mode m, access_mode a) -{ - return failif(bp->open(filename, m, a) == 0); -} - -istream& istream::open(const char* filename, const char* m) -{ - return failif(bp->open(filename, m) == 0); -} - -istream& istream::open(int filedesc, io_mode m) -{ - return failif(bp->open(filedesc, m) == 0); -} - -istream& istream::open(FILE* fileptr) -{ - return failif(bp->open(fileptr) == 0); -} - -istream& istream::open(const char* filenam, open_mode m) -{ - return failif(bp->open(filenam, m) == 0); -} - -istream& istream::get(char& c) -{ - if (good()) - { - if(tied_to != 0) tied_to->flush(); - int ch = bp->sgetc(); - if (ch == EOF) - set(_eof); - else - { - c = ch; - bp->stossc(); - } - } - return *this; -} - - -istream& istream::operator >> (whitespace&) -{ - if (good()) - { - int ch; - if(tied_to != 0) tied_to->flush(); - while (((ch = bp->sgetc()) != EOF) && isspace(ch)) bp->stossc(); - if (ch == EOF) set(_eof); - } - return *this; -} - - -istream& istream::operator >> (char& c) -{ - if (skipws) (*this >> WS); - return get(c); -} - -istream& istream::get(char* s, int n, char terminator) -{ - if (!readable()) - { - set(_fail); - return *this; - } - - char ch = 0; - char* start = s; - if (--n > 0 && get(ch)) - { - if (ch == terminator) - unget(ch); - else - { - *s++ = ch; --n; - while (n-- > 0 && get(ch)) - { - if (ch == terminator) - { - unget(ch); - break; - } - else - *s++ = ch; - } - } - } - - *s = 0; - if (s != start) clear(); - return *this; -} - - -istream& istream::operator >> (char* s) -{ - if (!readable() || s == 0) - { - set(_fail); - return *this; - } - - if (skipws && !(*this >> WS)) return *this; - - char ch; - char* start = s; - if (get(ch)) - { - *s++ = ch; - while (get(ch)) - { - if (isspace(ch)) - { - unget(ch); - break; - } - else - *s++ = ch; - } - } - - *s = 0; - if (s != start) clear(); - return *this; -} - - -istream& istream::getline(char* s, int n, char terminator) -{ - if (!readable()) - { - set(_fail); - return *this; - } - char* start = s; - char ch; - while (--n > 0 && get(ch) && ((*s++ = ch) != terminator)); - - *s = 0; - if (s != start) clear(); - return *this; -} - -// from Doug Schmidt - -// This should probably be a page size.... -#define CHUNK_SIZE 512 - -/* Reads an arbitrarily long input line terminated by a user-specified - TERMINATOR. Super-nifty trick using recursion avoids unnecessary calls - to NEW! */ - -char *istream::readline (int chunk_number, char terminator) -{ - char buf[CHUNK_SIZE]; - register char *bufptr = buf; - register char *ptr; - char ch; - int continu; - - while ((continu = !!get(ch)) && ch != terminator) /* fill the current buffer */ - { - *bufptr++ = ch; - if (bufptr - buf >= CHUNK_SIZE) /* prepend remainder to ptr buffer */ - { - if (ptr = readline (chunk_number + 1, terminator)) - - for (; bufptr != buf; *--ptr = *--bufptr); - - return ptr; - } - } - if (!continu && bufptr == buf) - return NULL; - - int size = (chunk_number * CHUNK_SIZE + bufptr - buf) + 1; - - if (ptr = new char[size]) - { - - for (*(ptr += (size - 1)) = '\0'; bufptr != buf; *--ptr = *--bufptr) - ; - - return ptr; - } - else - return NULL; -} - -/* Reads an arbitrarily long input line terminated by TERMINATOR. - This routine allocates its own memory, so the user should - only supply the address of a (char *). */ - -istream& istream::gets(char **s, char terminator) -{ - return failif(!readable() || !(*s = readline (0, terminator))); -} - -istream& istream::operator >> (long& y) -{ - if (!readable()) - { - set(_bad); - return *this; - } - - int got_one = 0; - char sgn = 0; - char ch; - y = 0; - if (skipws) *this >> WS; - if (!good()) - { - set(_bad); - return *this; - } - while (get(ch)) - { - if (ch == '-') - { - if (sgn == 0 && got_one == 0) - sgn = '-'; - else - break; - } - else if (ch >= '0' && ch <= '9') - y = y * 10 + ((got_one = ch) - '0'); - else - break; - } - if (good()) - unget(ch); - if (!got_one) - set(_bad); - else - clear(); - - if (sgn == '-') - y = -y; - - return *this; -} - -istream& istream::operator >> (unsigned long& y) -{ - if (!readable()) - { - set(_bad); - return *this; - } - - int got_one = 0; - char ch; - y = 0; - if (skipws) *this >> WS; - if (!good()) - while (get(ch)) - { - if (ch >= '0' && ch <= '9') - y = y * 10 + ((got_one = ch) - '0'); - else - break; - } - if (good()) - unget(ch); - if (!got_one) - set(_bad); - else - clear(); - return *this; -} - - -/* for input to a double, we must trust atof (cannot even use -the better strtod since it is not universally supported). So -guaranteed legal chars are gathered up into an obstack. The -only possible, undiagnosable error is that the input number -might give a floating overflow or underflow inside atof. -I know of no way to avoid this */ - -extern Obstack _libgxx_io_ob; - -istream& istream::operator >> (double & y) -{ - if (!readable()) - { - set(_bad); - return *this; - } - - - char seenint = 0; - char seendec = 0; - char seenexp = 0; - char seensgn = 0; - char seene = 0; - char seenexpsgn = 0; - char seendot = 0; - char ch; - - if (skipws) *this >> WS; - if (!good()) - { - set(_bad); - return *this; - } - while (get(ch)) - { - if (ch == '-' || ch == '+') - { - if (seene && !seenexpsgn) - _libgxx_io_ob.grow(seenexpsgn = ch); - else if (!seensgn && !seenint) - _libgxx_io_ob.grow(seensgn = ch); - else - break; - } - else if (ch == '.' && !seendot) - { - _libgxx_io_ob.grow(seendot = ch); - } - else if ((ch == 'e' || ch == 'E') && !seene) - { - _libgxx_io_ob.grow(seene = ch); - } - else if (ch >= '0' && ch <= '9') - { - _libgxx_io_ob.grow(ch); - if (seene) seenexp = ch; - else if (seendot) seendec = ch; - else seenint = ch; - } - else - break; - } - char* str = (char *) _libgxx_io_ob.finish(0); - if (good()) - unget(ch); - if ((seenint || seendec) && (!seene || seenexp)) - y = atof(str); - else - set(_bad); - _libgxx_io_ob.free(str); - return *this; -} - -istream& istream::operator >> (int& y) -{ - long l; (*this >> l); y = int(l); return *this; -} - -istream& istream:: operator >> (unsigned int& y) -{ - long l; (*this >> l); y = (unsigned int)(l); return *this; -} - -istream& istream:: operator >> (short& y) -{ - long l; (*this >> l); y = short(l); return *this; -} - -istream& istream:: operator >> (unsigned short& y) -{ - long l; (*this >> l); y = (unsigned short)(l); return *this; -} - -istream& istream:: operator >> (float& y) -{ - double d; (*this >> d); y = float(d); return *this; -} - -const char* istream::name() -{ - return bp->name(); -} - -void istream::error() -{ - bp->error(); -} - -ostream* istream::tie(ostream* s) -{ - ostream* was = tied_to; tied_to = s; return was; -} - -void istream::_flush() -{ - if(tied_to != 0) tied_to->flush(); -} - - -//-------------------------------------------------------------- - -extern ostream cout; - -#if 0 /* BSD 4.4 */ - -istream cin(stdin, 1, &cout); - -#else - -static char cinbuf[BUFSIZE]; -istream cin (0, cinbuf, BUFSIZE, 1, &cout); - -#endif - -whitespace WS; diff --git a/gnu/lib/libg++/libg++/itoa.cc b/gnu/lib/libg++/libg++/itoa.cc deleted file mode 100644 index 489f252e29..0000000000 --- a/gnu/lib/libg++/libg++/itoa.cc +++ /dev/null @@ -1,226 +0,0 @@ -/* -Copyright (C) 1990 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifdef __GNUG__ -#pragma implementation -#endif -#include -#include -#include - -extern AllocRing _libgxx_fmtq; - -char* itoa(long x, int base, int width) -{ - int wrksiz; - if (base == 10) - wrksiz = width + 13; - else - wrksiz = (BITS(long) + 1) / lg(base) + 1 + width + 1; - - char* fmtbase = (char *) _libgxx_fmtq.alloc(wrksiz); - char* e = fmtbase + wrksiz - 1; - char* s = e; - *--s = 0; - char sgn = 0; - - if (x == 0) - *--s = '0'; - else - { - unsigned int z; - if (x < 0) - { - sgn = '-'; - z = -x; - } - else - z = x; - while (z != 0) - { - char ch = char(z % base); - z = z / base; - if (ch >= 10) - ch += 'a' - 10; - else - ch += '0'; - *--s = ch; - } - } - - if (sgn) *--s = sgn; - int w = e - s - 1; - while (w++ < width) - *--s = ' '; - return s; -} - -char* itoa(unsigned long x, int base, int width) -{ - int wrksiz; - if (base == 10) - wrksiz = width + 13; - else - wrksiz = (BITS(long) + 1) / lg(base) + 1 + width + 1; - - char* fmtbase = (char *) _libgxx_fmtq.alloc(wrksiz); - char* e = fmtbase + wrksiz - 1; - char* s = e; - *--s = 0; - - if (x == 0) - *--s = '0'; - else - { - unsigned int b = base; - while (x != 0) - { - char ch = char(x % b); - x = x / b; - if (ch >= 10) - ch += 'a' - 10; - else - ch += '0'; - *--s = ch; - } - } - - int w = e - s - 1; - while (w++ < width) - *--s = ' '; - return s; -} - -#ifdef __GNUG__ -#ifndef VMS -char* itoa(long long x, int base, int width) -{ - int wrksiz; - if (base == 10) - wrksiz = width + 23; - else - wrksiz = (BITS(long long) + 1) / lg(base) + 1 + width + 1; - - char* fmtbase = (char *) _libgxx_fmtq.alloc(wrksiz); - char* e = fmtbase + wrksiz - 1; - char* s = e; - *--s = 0; - char sgn = 0; - - if (x == 0) - *--s = '0'; - else - { - long long z; - if (x < 0) - { - sgn = '-'; - z = -x; - } - else - z = x; - while (z != 0) - { - char ch = char(z % base); - z = z / base; - if (ch >= 10) - ch += 'a' - 10; - else - ch += '0'; - *--s = ch; - } - } - - if (sgn) *--s = sgn; - int w = e - s - 1; - while (w++ < width) - *--s = ' '; - return s; -} - -char* itoa(unsigned long long x, int base, int width) -{ - int wrksiz; - if (base == 10) - wrksiz = width + 23; - else - wrksiz = (BITS(long long) + 1) / lg(base) + 1 + width + 1; - - char* fmtbase = (char *) _libgxx_fmtq.alloc(wrksiz); - char* e = fmtbase + wrksiz - 1; - char* s = e; - *--s = 0; - - if (x == 0) - *--s = '0'; - else - { - unsigned int b = base; - while (x != 0) - { - char ch = char(x % b); - x = x / b; - if (ch >= 10) - ch += 'a' - 10; - else - ch += '0'; - *--s = ch; - } - } - - int w = e - s - 1; - while (w++ < width) - *--s = ' '; - return s; -} -#endif -#endif - -char* hex(long i, int width) -{ - return itoa(i, 16, width); -} - -char* oct(long i, int width) -{ - return itoa(i, 8, width); -} - -char* dec(long i, int width) -{ - return itoa(i, 10, width); -} - -char* hex(unsigned long i, int width) -{ - return itoa(i, 16, width); -} - -char* oct(unsigned long i, int width) -{ - return itoa(i, 8, width); -} - -char* dec(unsigned long i, int width) -{ - return itoa(i, 10, width); -} diff --git a/gnu/lib/libg++/libg++/lg.cc b/gnu/lib/libg++/libg++/lg.cc index aaac3c1c34..b5ea5fd0fb 100644 --- a/gnu/lib/libg++/libg++/lg.cc +++ b/gnu/lib/libg++/libg++/lg.cc @@ -2,22 +2,17 @@ Copyright (C) 1990 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ diff --git a/gnu/lib/libg++/libg++/libc.h b/gnu/lib/libg++/libg++/libc.h new file mode 100644 index 0000000000..c8b49f26d1 --- /dev/null +++ b/gnu/lib/libg++/libg++/libc.h @@ -0,0 +1 @@ +#include diff --git a/gnu/lib/libg++/libg++/malloc.c b/gnu/lib/libg++/libg++/malloc.c deleted file mode 100644 index 071ee94a59..0000000000 --- a/gnu/lib/libg++/libg++/malloc.c +++ /dev/null @@ -1,1261 +0,0 @@ -/* -Copyright (C) 1989 Free Software Foundation - written by Doug Lea (dl@oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - - - -#ifndef NO_LIBGXX_MALLOC /* ignore whole file otherwise */ - -/* compile with -DMALLOC_STATS to collect statistics */ -/* collecting statistics slows down malloc by at least 15% */ - -#ifdef MALLOC_STATS -#define UPDATE_STATS(ARGS) {ARGS;} -#else -#define UPDATE_STATS(ARGS) -#endif - -/* History - - - Tue Jan 16 04:54:27 1990 Doug Lea (dl at g.oswego.edu) - - version 1 released in libg++ - - Sun Jan 21 05:52:47 1990 Doug Lea (dl at g.oswego.edu) - - bins are now own struct for, sanity. - - new victim search strategy: scan up and consolidate. - Both faster and less fragmentation. - - refined when to scan bins for consolidation, via consollink, etc. - - realloc: always try to expand chunk, avoiding some fragmentation. - - changed a few inlines into macros - - hardwired SBRK_UNIT to 4096 for uniformity across systems - - Tue Mar 20 14:18:23 1990 Doug Lea (dl at g.oswego.edu) - - calloc and cfree now correctly parameterized. - - Sun Apr 1 10:00:48 1990 Doug Lea (dl at g.oswego.edu) - - added memalign and valloc. - - Sun Jun 24 05:46:48 1990 Doug Lea (dl at g.oswego.edu) - - #include gepagesize.h only ifndef sun - cache pagesize after first call - - Wed Jul 25 08:35:19 1990 Doug Lea (dl at g.oswego.edu) - - No longer rely on a `designated victim': - - 1. It sometimes caused splits of large chunks - when smaller ones would do, leading to - bad worst-case fragmentation. - - 2. Scanning through the av array fast anyway, - so the overhead isn't worth it. - - To compensate, several other minor changes: - - 1. Unusable chunks are checked for consolidation during - searches inside bins, better distributing chunks - across bins. - - 2. Chunks are returned when found in malloc_find_space, - rather than finishing cleaning everything up, to - avoid wasted iterations due to (1). -*/ - -/* - A version of malloc/free/realloc tuned for C++ applications. - - Here's what you probably want to know first: - - In various tests, this appears to be about as fast as, - and usually substantially less memory-wasteful than BSD/GNUemacs malloc. - - Generally, it is slower (by perhaps 20%) than bsd-style malloc - only when bsd malloc would waste a great deal of space in - fragmented blocks, which this malloc recovers; or when, by - chance or design, nearly all requests are near the bsd malloc - power-of-2 allocation bin boundaries, and as many chunks are - used as are allocated. - - It uses more space than bsd malloc only when, again by chance - or design, only bsdmalloc bin-sized requests are malloced, or when - little dynamic space is malloced, since this malloc may grab larger - chunks from the system at a time than bsd. - - In other words, this malloc seems generally superior to bsd - except perhaps for programs that are specially tuned to - deal with bsdmalloc's characteristics. But even here, the - performance differences are slight. - - - This malloc, like any other, is a compromised design. - - - Chunks of memory are maintained using a `boundary tag' method as - described in e.g., Knuth or Standish. This means that the size of - the chunk is stored both in the front of the chunk and at the end. - This makes consolidating fragmented chunks into bigger chunks very fast. - The size field is also used to hold bits representing whether a - chunk is free or in use. - - Malloced chunks have space overhead of 8 bytes: The preceding - and trailing size fields. When they are freed, the list pointer - fields are also needed. - - Available chunks are kept in doubly linked lists. The lists are - maintained in an array of bins using a power-of-two method, except - that instead of 32 bins (one for each 1 << i), there are 128: each - power of two is split in quarters. The use of very fine bin sizes - closely approximates the use of one bin per actually used size, - without necessitating the overhead of locating such bins. It is - especially desirable in common C++ applications where large numbers - of identically-sized blocks are malloced/freed in some dynamic - manner, and then later are all freed. The finer bin sizes make - finding blocks fast, with little wasted overallocation. The - consolidation methods ensure that once the collection of blocks is - no longer useful, fragments are gathered into bigger chunks awaiting new - roles. - - The bins av[i] serve as heads of the lists. Bins contain a dummy - header for the chunk lists, and a `dirty' field used to indicate - whether the list may need to be scanned for consolidation. - - On allocation, the bin corresponding to the request size is - scanned, and if there is a chunk with size >= requested, it - is split, if too big, and used. Chunks on the list which are - too small are examined for consolidation during this traversal. - - If no chunk exists in the list bigger bins are scanned in search of - a victim. - - If no victim can be found, then smaller bins are examined for - consolidation in order to construct a victim. - - Finally, if consolidation fails to come up with a usable chunk, - more space is obtained from the system. - - After a split, the remainder is placed on - the back of the appropriate bin list. (All freed chunks are placed - on fronts of lists. All remaindered or consolidated chunks are - placed on the rear. Correspondingly, searching within a bin - starts at the front, but finding victims is from the back. All - of this approximates the effect of having 2 kinds of lists per - bin: returned chunks vs unallocated chunks, but without the overhead - of maintaining 2 lists.) - - Deallocation (free) consists only of placing the chunk on - a list. - - Reallocation proceeds in the usual way. If a chunk can be extended, - it is, else a malloc-copy-free sequence is taken. - - memalign requests more than enough space from malloc, finds a - spot within that chunk that meets the alignment request, and - then possibly frees the leading and trailing space. Overreliance - on memalign is a sure way to fragment space. - - - Some other implementation matters: - - 8 byte alignment is currently hardwired into the design. Calling - memalign will return a chunk that is both 8-byte aligned, and - meets the requested alignment. - - The basic overhead of a used chunk is 8 bytes: 4 at the front and - 4 at the end. - - When a chunk is free, 8 additional bytes are needed for free list - pointers. Thus, the minimum allocatable size is 16 bytes. - - The existence of front and back overhead permits some reasonably - effective fence-bashing checks: The front and back fields must - be identical. This is checked only within free() and realloc(). - The checks are fast enough to be made non-optional. - - The overwriting of parts of freed memory with the freelist pointers - can also be very effective (albeit in an annoying way) in helping - users track down dangling pointers. - - User overwriting of freed space will often result in crashes - within malloc or free. - - These routines are also tuned to C++ in that free(0) is a noop and - a failed malloc automatically calls (*new_handler)(). - - malloc(0) returns a pointer to something of the minimum allocatable size. - - Additional memory is gathered from the system (via sbrk) in a - way that allows chunks obtained across different sbrk calls to - be consolidated, but does not require contiguous memory: Thus, - it should be safe to intersperse mallocs with other sbrk calls. - - This malloc is NOT designed to work in multiprocessing applications. - No semaphores or other concurrency control are provided to ensure - that multiple malloc or free calls don't run at the same time, - which could be disasterous. - - VERY heavy use of inlines is made, for clarity. If this malloc - is ported via a compiler without inlining capabilities, all - inlines should be transformed into macros -- making them non-inline - makes malloc at least twice as slow. - - -*/ - - -/* preliminaries */ - -#ifdef __cplusplus -#include -#else -#include "//usr/include/stdio.h" /* needed for error reporting */ -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef USG -extern void* memset(void*, int, int); -extern void* memcpy(void*, const void*, int); -inline void bzero(void* s, int l) { memset(s, 0, l); } -#else -extern void bzero(void*, unsigned int); -#endif - -extern void bcopy(void*, void*, unsigned int); - -extern void* sbrk(unsigned int); - - -#ifdef __GNUC__ -extern volatile void abort(); -#else -extern void abort(); -#endif - -#ifdef __cplusplus -}; /* end of extern "C" */ -#endif - - -/* A good multiple to call sbrk with */ - -#define SBRK_UNIT 4096 - - - -/* how to die on detected error */ - -#ifdef __GNUC__ -static volatile void malloc_user_error() -#else -static void malloc_user_error() -#endif -{ - fputs("malloc/free/realloc: clobbered space detected\n", stderr); abort(); -} - - - -/* Basic overhead for each malloc'ed chunk */ - - -struct malloc_chunk -{ - unsigned int size; /* Size in bytes, including overhead. */ - /* Or'ed with INUSE if in use. */ - - struct malloc_chunk* fd; /* double links -- used only if free. */ - struct malloc_chunk* bk; - -}; - -typedef struct malloc_chunk* mchunkptr; - -struct malloc_bin -{ - struct malloc_chunk hd; /* dummy list header */ - unsigned int dirty; /* True if maybe consolidatable */ - /* Wasting a word here makes */ - /* sizeof(bin) a power of 2, */ - /* which makes size2bin() faster */ -}; - -typedef struct malloc_bin* mbinptr; - - -/* sizes, alignments */ - - -#define SIZE_SZ (sizeof(unsigned int)) -#define MALLOC_MIN_OVERHEAD (SIZE_SZ + SIZE_SZ) -#define MALLOC_ALIGN_MASK (MALLOC_MIN_OVERHEAD - 1) - -#define MINSIZE (sizeof(struct malloc_chunk) + SIZE_SZ) /* MUST == 16! */ - - -/* pad request bytes into a usable size */ - -static inline unsigned int request2size(unsigned int request) -{ - return (request == 0) ? MINSIZE : - ((request + MALLOC_MIN_OVERHEAD + MALLOC_ALIGN_MASK) - & ~(MALLOC_ALIGN_MASK)); -} - - -static inline int aligned_OK(void* m) -{ - return ((unsigned int)(m) & (MALLOC_ALIGN_MASK)) == 0; -} - - -/* size field or'd with INUSE when in use */ -#define INUSE 0x1 - - - -/* the bins, initialized to have null double linked lists */ - -#define MAXBIN 120 /* 1 more than needed for 32 bit addresses */ - -#define FIRSTBIN (&(av[0])) - -static struct malloc_bin av[MAXBIN] = -{ - { { 0, &(av[0].hd), &(av[0].hd) }, 0 }, - { { 0, &(av[1].hd), &(av[1].hd) }, 0 }, - { { 0, &(av[2].hd), &(av[2].hd) }, 0 }, - { { 0, &(av[3].hd), &(av[3].hd) }, 0 }, - { { 0, &(av[4].hd), &(av[4].hd) }, 0 }, - { { 0, &(av[5].hd), &(av[5].hd) }, 0 }, - { { 0, &(av[6].hd), &(av[6].hd) }, 0 }, - { { 0, &(av[7].hd), &(av[7].hd) }, 0 }, - { { 0, &(av[8].hd), &(av[8].hd) }, 0 }, - { { 0, &(av[9].hd), &(av[9].hd) }, 0 }, - - { { 0, &(av[10].hd), &(av[10].hd) }, 0 }, - { { 0, &(av[11].hd), &(av[11].hd) }, 0 }, - { { 0, &(av[12].hd), &(av[12].hd) }, 0 }, - { { 0, &(av[13].hd), &(av[13].hd) }, 0 }, - { { 0, &(av[14].hd), &(av[14].hd) }, 0 }, - { { 0, &(av[15].hd), &(av[15].hd) }, 0 }, - { { 0, &(av[16].hd), &(av[16].hd) }, 0 }, - { { 0, &(av[17].hd), &(av[17].hd) }, 0 }, - { { 0, &(av[18].hd), &(av[18].hd) }, 0 }, - { { 0, &(av[19].hd), &(av[19].hd) }, 0 }, - - { { 0, &(av[20].hd), &(av[20].hd) }, 0 }, - { { 0, &(av[21].hd), &(av[21].hd) }, 0 }, - { { 0, &(av[22].hd), &(av[22].hd) }, 0 }, - { { 0, &(av[23].hd), &(av[23].hd) }, 0 }, - { { 0, &(av[24].hd), &(av[24].hd) }, 0 }, - { { 0, &(av[25].hd), &(av[25].hd) }, 0 }, - { { 0, &(av[26].hd), &(av[26].hd) }, 0 }, - { { 0, &(av[27].hd), &(av[27].hd) }, 0 }, - { { 0, &(av[28].hd), &(av[28].hd) }, 0 }, - { { 0, &(av[29].hd), &(av[29].hd) }, 0 }, - - { { 0, &(av[30].hd), &(av[30].hd) }, 0 }, - { { 0, &(av[31].hd), &(av[31].hd) }, 0 }, - { { 0, &(av[32].hd), &(av[32].hd) }, 0 }, - { { 0, &(av[33].hd), &(av[33].hd) }, 0 }, - { { 0, &(av[34].hd), &(av[34].hd) }, 0 }, - { { 0, &(av[35].hd), &(av[35].hd) }, 0 }, - { { 0, &(av[36].hd), &(av[36].hd) }, 0 }, - { { 0, &(av[37].hd), &(av[37].hd) }, 0 }, - { { 0, &(av[38].hd), &(av[38].hd) }, 0 }, - { { 0, &(av[39].hd), &(av[39].hd) }, 0 }, - - { { 0, &(av[40].hd), &(av[40].hd) }, 0 }, - { { 0, &(av[41].hd), &(av[41].hd) }, 0 }, - { { 0, &(av[42].hd), &(av[42].hd) }, 0 }, - { { 0, &(av[43].hd), &(av[43].hd) }, 0 }, - { { 0, &(av[44].hd), &(av[44].hd) }, 0 }, - { { 0, &(av[45].hd), &(av[45].hd) }, 0 }, - { { 0, &(av[46].hd), &(av[46].hd) }, 0 }, - { { 0, &(av[47].hd), &(av[47].hd) }, 0 }, - { { 0, &(av[48].hd), &(av[48].hd) }, 0 }, - { { 0, &(av[49].hd), &(av[49].hd) }, 0 }, - - { { 0, &(av[50].hd), &(av[50].hd) }, 0 }, - { { 0, &(av[51].hd), &(av[51].hd) }, 0 }, - { { 0, &(av[52].hd), &(av[52].hd) }, 0 }, - { { 0, &(av[53].hd), &(av[53].hd) }, 0 }, - { { 0, &(av[54].hd), &(av[54].hd) }, 0 }, - { { 0, &(av[55].hd), &(av[55].hd) }, 0 }, - { { 0, &(av[56].hd), &(av[56].hd) }, 0 }, - { { 0, &(av[57].hd), &(av[57].hd) }, 0 }, - { { 0, &(av[58].hd), &(av[58].hd) }, 0 }, - { { 0, &(av[59].hd), &(av[59].hd) }, 0 }, - - { { 0, &(av[60].hd), &(av[60].hd) }, 0 }, - { { 0, &(av[61].hd), &(av[61].hd) }, 0 }, - { { 0, &(av[62].hd), &(av[62].hd) }, 0 }, - { { 0, &(av[63].hd), &(av[63].hd) }, 0 }, - { { 0, &(av[64].hd), &(av[64].hd) }, 0 }, - { { 0, &(av[65].hd), &(av[65].hd) }, 0 }, - { { 0, &(av[66].hd), &(av[66].hd) }, 0 }, - { { 0, &(av[67].hd), &(av[67].hd) }, 0 }, - { { 0, &(av[68].hd), &(av[68].hd) }, 0 }, - { { 0, &(av[69].hd), &(av[69].hd) }, 0 }, - - { { 0, &(av[70].hd), &(av[70].hd) }, 0 }, - { { 0, &(av[71].hd), &(av[71].hd) }, 0 }, - { { 0, &(av[72].hd), &(av[72].hd) }, 0 }, - { { 0, &(av[73].hd), &(av[73].hd) }, 0 }, - { { 0, &(av[74].hd), &(av[74].hd) }, 0 }, - { { 0, &(av[75].hd), &(av[75].hd) }, 0 }, - { { 0, &(av[76].hd), &(av[76].hd) }, 0 }, - { { 0, &(av[77].hd), &(av[77].hd) }, 0 }, - { { 0, &(av[78].hd), &(av[78].hd) }, 0 }, - { { 0, &(av[79].hd), &(av[79].hd) }, 0 }, - - { { 0, &(av[80].hd), &(av[80].hd) }, 0 }, - { { 0, &(av[81].hd), &(av[81].hd) }, 0 }, - { { 0, &(av[82].hd), &(av[82].hd) }, 0 }, - { { 0, &(av[83].hd), &(av[83].hd) }, 0 }, - { { 0, &(av[84].hd), &(av[84].hd) }, 0 }, - { { 0, &(av[85].hd), &(av[85].hd) }, 0 }, - { { 0, &(av[86].hd), &(av[86].hd) }, 0 }, - { { 0, &(av[87].hd), &(av[87].hd) }, 0 }, - { { 0, &(av[88].hd), &(av[88].hd) }, 0 }, - { { 0, &(av[89].hd), &(av[89].hd) }, 0 }, - - { { 0, &(av[90].hd), &(av[90].hd) }, 0 }, - { { 0, &(av[91].hd), &(av[91].hd) }, 0 }, - { { 0, &(av[92].hd), &(av[92].hd) }, 0 }, - { { 0, &(av[93].hd), &(av[93].hd) }, 0 }, - { { 0, &(av[94].hd), &(av[94].hd) }, 0 }, - { { 0, &(av[95].hd), &(av[95].hd) }, 0 }, - { { 0, &(av[96].hd), &(av[96].hd) }, 0 }, - { { 0, &(av[97].hd), &(av[97].hd) }, 0 }, - { { 0, &(av[98].hd), &(av[98].hd) }, 0 }, - { { 0, &(av[99].hd), &(av[99].hd) }, 0 }, - - { { 0, &(av[100].hd), &(av[100].hd) }, 0 }, - { { 0, &(av[101].hd), &(av[101].hd) }, 0 }, - { { 0, &(av[102].hd), &(av[102].hd) }, 0 }, - { { 0, &(av[103].hd), &(av[103].hd) }, 0 }, - { { 0, &(av[104].hd), &(av[104].hd) }, 0 }, - { { 0, &(av[105].hd), &(av[105].hd) }, 0 }, - { { 0, &(av[106].hd), &(av[106].hd) }, 0 }, - { { 0, &(av[107].hd), &(av[107].hd) }, 0 }, - { { 0, &(av[108].hd), &(av[108].hd) }, 0 }, - { { 0, &(av[109].hd), &(av[109].hd) }, 0 }, - - { { 0, &(av[110].hd), &(av[110].hd) }, 0 }, - { { 0, &(av[111].hd), &(av[111].hd) }, 0 }, - { { 0, &(av[112].hd), &(av[112].hd) }, 0 }, - { { 0, &(av[113].hd), &(av[113].hd) }, 0 }, - { { 0, &(av[114].hd), &(av[114].hd) }, 0 }, - { { 0, &(av[115].hd), &(av[115].hd) }, 0 }, - { { 0, &(av[116].hd), &(av[116].hd) }, 0 }, - { { 0, &(av[117].hd), &(av[117].hd) }, 0 }, - { { 0, &(av[118].hd), &(av[118].hd) }, 0 }, - { { 0, &(av[119].hd), &(av[119].hd) }, 0 } -}; - -/* - indexing into bins -*/ - -static inline mbinptr size2bin(unsigned int sz) -{ - mbinptr b = av; - while (sz >= (MINSIZE * 2)) { b += 4; sz >>= 1; } /* find power of 2 */ - b += (sz - MINSIZE) >> 2; /* find quadrant */ - return b; -} - - - -/* counts maintained if MALLOC_STATS defined */ - -#ifdef MALLOC_STATS - -static unsigned int sbrked_mem; -static unsigned int requested_mem; -static unsigned int malloced_mem; -static unsigned int freed_mem; -static unsigned int max_used_mem; - -static unsigned int n_sbrks; -static unsigned int n_mallocs; -static unsigned int n_frees; -static unsigned int n_reallocs; -static unsigned int n_reallocs_with_copy; -static unsigned int n_avail; -static unsigned int max_inuse; - -static unsigned int n_malloc_chunks; -static unsigned int n_malloc_bins; - -static unsigned int n_split; -static unsigned int n_consol; - - -static void do_malloc_stats(const mchunkptr p) -{ - ++n_mallocs; - if ((n_mallocs-n_frees) > max_inuse) - max_inuse = n_mallocs - n_frees; - malloced_mem += (p->size & ~(INUSE)); - if (malloced_mem - freed_mem > max_used_mem) - max_used_mem = malloced_mem - freed_mem; -} - -static void do_free_stats(const mchunkptr p) -{ - ++n_frees; - freed_mem += (p->size & ~(INUSE)); -} - -#endif - - - -/* Utilities needed below for memalign */ -/* This is redundant with libg++ support, but not if used stand-alone */ - -static unsigned int gcd(unsigned int a, unsigned int b) -{ - unsigned int tmp; - - if (b > a) - { - tmp = a; a = b; b = tmp; - } - for(;;) - { - if (b == 0) - return a; - else if (b == 1) - return b; - else - { - tmp = b; - b = a % b; - a = tmp; - } - } -} - -static inline unsigned int lcm(unsigned int x, unsigned int y) -{ - return x / gcd(x, y) * y; -} - - - -/* maintaining INUSE via size field */ - - -#define inuse(p) ((p)->size & INUSE) -#define set_inuse(p) ((p)->size |= INUSE) -#define clear_inuse(b) ((p)->size &= ~INUSE) - - -/* operations on malloc_chunk addresses */ - - -/* return ptr to next physical malloc_chunk */ - -#define next_chunk(p) ((mchunkptr)((char*)(p) + (p)->size)) - -/* return ptr to previous physical malloc_chunk */ - -#define prev_chunk(p) ((mchunkptr)((char*)(p)-((((int*)(p))[-1]) & ~(INUSE)))) - -/* place size at front and back of chunk */ - - -static inline void set_size(mchunkptr p, unsigned int sz) -{ - p->size = *((int*)((char*)(p) + sz - SIZE_SZ)) = sz; -} - - - - -/* conversion from malloc headers to user pointers, and back */ - -static inline void* chunk2mem(mchunkptr p) -{ - set_inuse(p); - return (void*)((char*)(p) + SIZE_SZ); -} - -static inline mchunkptr mem2chunk(void* mem) -{ - mchunkptr p = (mchunkptr)((char*)(mem) - SIZE_SZ); - - /* a quick sanity check */ - unsigned int sz = p->size & ~(INUSE); - if (p->size == sz || sz != *((int*)((char*)(p) + sz - SIZE_SZ))) - malloc_user_error(); - - p->size = sz; /* clears INUSE */ - return p; -} - - - -/* maintaining bins & pointers */ - - -/* maximum bin actually used */ - -static mbinptr malloc_maxbin = FIRSTBIN; - - -/* operations on lists inside bins */ - - -/* take a chunk off a list */ - -static inline void unlink(mchunkptr p) -{ - mchunkptr b = p->bk; - mchunkptr f = p->fd; - - f->bk = b; b->fd = f; - - UPDATE_STATS (--n_avail); -} - - - -/* split a chunk and place on the back of a list */ - -static inline void split(mchunkptr p, unsigned int offset) -{ - unsigned int room = p->size - offset; - if (room >= MINSIZE) - { - mbinptr bn = size2bin(room); /* new bin */ - mchunkptr h = &(bn->hd); /* its head */ - mchunkptr b = h->bk; /* old back element */ - mchunkptr t = (mchunkptr)((char*)(p) + offset); /* remaindered chunk */ - - /* set size */ - t->size = *((int*)((char*)(t) + room - SIZE_SZ)) = room; - - /* link up */ - t->bk = b; t->fd = h; h->bk = b->fd = t; - - /* adjust maxbin (h == b means was empty) */ - if (h == b && bn > malloc_maxbin) malloc_maxbin = bn; - - /* adjust size of chunk to be returned */ - p->size = *((int*)((char*)(p) + offset - SIZE_SZ)) = offset; - - UPDATE_STATS ((++n_split, ++n_avail)); - } -} - - - -/* place a consolidated chunk on the back of a list */ -/* like above, except no split */ - -static inline void consollink(mchunkptr p) -{ - mbinptr bn = size2bin(p->size); - mchunkptr h = &(bn->hd); - mchunkptr b = h->bk; - - p->bk = b; p->fd = h; h->bk = b->fd = p; - - if (h == b && bn > malloc_maxbin) malloc_maxbin = bn; - - UPDATE_STATS(++n_avail); -} - - -/* place a freed chunk on the front of a list */ - -static inline void frontlink(mchunkptr p) -{ - mbinptr bn = size2bin(p->size); - mchunkptr h = &(bn->hd); - mchunkptr f = h->fd; - - p->bk = h; p->fd = f; f->bk = h->fd = p; - - if (h == f && bn > malloc_maxbin) malloc_maxbin = bn; - - bn->dirty = 1; - - UPDATE_STATS(++n_avail); -} - - - -/* Dealing with sbrk */ - - -/* To link consecutive sbrk regions when possible */ - -static int* last_sbrk_end; - - -/* who to call when sbrk returns failure */ - -#ifndef NO_NEW_HANDLER -typedef volatile void (*vfp)(); -#ifdef __cplusplus -extern "C" vfp __new_handler; -#else -extern vfp __new_handler; -#endif -#endif - -static mchunkptr malloc_from_sys(unsigned nb) -{ - mchunkptr p; - unsigned int sbrk_size; - int* ip; - - /* Minimally, we need to pad with enough space */ - /* to place dummy size/use fields to ends if needed */ - - sbrk_size = ((nb + SBRK_UNIT - 1 + SIZE_SZ + SIZE_SZ) - / SBRK_UNIT) * SBRK_UNIT; - - ip = (int*)(sbrk(sbrk_size)); - if ((char*)ip == (char*)(-1)) /* sbrk returns -1 on failure */ - { -#ifndef NO_NEW_HANDLER - (*__new_handler) (); -#endif - return 0; - } - - UPDATE_STATS ((++n_sbrks, sbrked_mem += sbrk_size)); - - - if (last_sbrk_end != &ip[-1]) - { - /* It's either first time through or someone else called sbrk. */ - /* Arrange end-markers at front & back */ - - /* Shouldn't be necessary, but better to be safe */ - while (!aligned_OK(ip)) { ++ip; sbrk_size -= SIZE_SZ; } - - - /* Mark the front as in use to prevent merging. */ - /* Note we can get away with only 1 word, not MINSIZE overhead here */ - - *ip++ = SIZE_SZ | INUSE; - - p = (mchunkptr)ip; - set_size(p,sbrk_size - (SIZE_SZ + SIZE_SZ)); - - } - else - { - mchunkptr l; - - /* We can safely make the header start at end of prev sbrked chunk. */ - /* We will still have space left at the end from a previous call */ - /* to place the end marker, below */ - - p = (mchunkptr)(last_sbrk_end); - set_size(p, sbrk_size); - - - /* Even better, maybe we can merge with last fragment: */ - - l = prev_chunk(p); - if (!inuse(l)) - { - unlink(l); - set_size(l, p->size + l->size); - p = l; - } - - } - - /* mark the end of sbrked space as in use to prevent merging */ - - last_sbrk_end = (int*)((char*)p + p->size); - *last_sbrk_end = SIZE_SZ | INUSE; - - UPDATE_STATS((++n_avail, ++n_malloc_chunks)); - - /* make it safe to unlink in malloc */ - UPDATE_STATS(++n_avail); - p->fd = p->bk = p; - - return p; -} - - - -/* Consolidate dirty bins. */ -/* Stop if found a chunk big enough to satisfy current malloc request */ - -/* (It requires much less bookkeeping to consolidate entire bins */ -/* at once than to keep records of which chunks might be */ -/* consolidatable. So long as the lists are short, which we */ -/* try to ensure via small bin ranges, there is little wasted effort.) */ - -static mchunkptr malloc_find_space(unsigned int nb) -{ - mbinptr b; - - /* first, re-adjust max used bin */ - - while (malloc_maxbin >= FIRSTBIN && - malloc_maxbin->hd.bk == &(malloc_maxbin->hd)) - { - malloc_maxbin->dirty = 0; - --malloc_maxbin; - } - - for (b = malloc_maxbin; b >= FIRSTBIN; --b) - { - UPDATE_STATS(++n_malloc_bins); - - if (b->dirty) - { - mchunkptr h = &(b->hd); /* head of list */ - mchunkptr p = h->fd; /* chunk traverser */ - - while (p != h) - { - mchunkptr nextp = p->fd; /* save, in case of relinks */ - int consolidated = 0; /* only unlink/relink if consolidated */ - - mchunkptr t; - - UPDATE_STATS(++n_malloc_chunks); - - while (!inuse(t = prev_chunk(p))) /* consolidate backward */ - { - if (!consolidated) { consolidated = 1; unlink(p); } - if (t == nextp) nextp = t->fd; - unlink(t); - set_size(t, t->size + p->size); - p = t; - UPDATE_STATS (++n_consol); - } - - while (!inuse(t = next_chunk(p))) /* consolidate forward */ - { - if (!consolidated) { consolidated = 1; unlink(p); } - if (t == nextp) nextp = t->fd; - unlink(t); - set_size(p, p->size + t->size); - UPDATE_STATS (++n_consol); - } - - if (consolidated) - { - if (p->size >= nb) - { - /* make it safe to unlink in malloc */ - UPDATE_STATS(++n_avail); - p->fd = p->bk = p; - return p; - } - else - consollink(p); - } - - p = nextp; - - } - - b->dirty = 0; - - } - } - - /* nothing available - sbrk some more */ - - return malloc_from_sys(nb); -} - - - -/* Finally, the user-level functions */ - -void* malloc(unsigned int bytes) -{ - unsigned int nb = request2size(bytes); /* padded request size */ - mbinptr b = size2bin(nb); /* corresponding bin */ - mchunkptr hd = &(b->hd); /* head of its list */ - mchunkptr p = hd->fd; /* chunk traverser */ - - UPDATE_STATS((requested_mem+=bytes, ++n_malloc_bins)); - - /* Try a (near) exact match in own bin */ - /* clean out unusable but consolidatable chunks in bin while traversing */ - - while (p != hd) - { - UPDATE_STATS(++n_malloc_chunks); - if (p->size >= nb) - goto found; - else /* try to consolidate; same code as malloc_find_space */ - { - mchunkptr nextp = p->fd; /* save, in case of relinks */ - int consolidated = 0; /* only unlink/relink if consolidated */ - - mchunkptr t; - - while (!inuse(t = prev_chunk(p))) /* consolidate backward */ - { - if (!consolidated) { consolidated = 1; unlink(p); } - if (t == nextp) nextp = t->fd; - unlink(t); - set_size(t, t->size + p->size); - p = t; - UPDATE_STATS (++n_consol); - } - - while (!inuse(t = next_chunk(p))) /* consolidate forward */ - { - if (!consolidated) { consolidated = 1; unlink(p); } - if (t == nextp) nextp = t->fd; - unlink(t); - set_size(p, p->size + t->size); - UPDATE_STATS (++n_consol); - } - - if (consolidated) - { - if (p->size >= nb) - { - /* make it safe to unlink again below */ - UPDATE_STATS(++n_avail); - p->fd = p->bk = p; - goto found; - } - else - consollink(p); - } - - p = nextp; - - } - } - - b->dirty = 0; /* true if got here */ - - /* Scan bigger bins for a victim */ - - while (++b <= malloc_maxbin) - { - UPDATE_STATS(++n_malloc_bins); - if ((p = b->hd.bk) != &(b->hd)) /* no need to check size */ - goto found; - } - - /* Consolidate or sbrk */ - - p = malloc_find_space(nb); - - if (p == 0) return 0; /* allocation failure */ - - found: /* Use what we found */ - - unlink(p); - split(p, nb); - UPDATE_STATS(do_malloc_stats(p)); - return chunk2mem(p); -} - - - - -void free(void* mem) -{ - if (mem != 0) - { - mchunkptr p = mem2chunk(mem); - UPDATE_STATS(do_free_stats(p)); - frontlink(p); - } -} - - -void* calloc(unsigned int n, unsigned int elem_size) -{ - unsigned int sz = n * elem_size; - void* p = malloc(sz); - bzero(p, sz); - return p; -}; - -/* This is here for compatibility with older systems */ -void cfree(void *mem) -{ - free(mem); -} - - -unsigned int malloc_usable_size(void* mem) -{ - if (mem == 0) - return 0; - else - { - mchunkptr p = (mchunkptr)((char*)(mem) - SIZE_SZ); - unsigned int sz = p->size & ~(INUSE); - if (p->size == sz || sz != *((int*)((char*)(p) + sz - SIZE_SZ))) - return 0; - else - return sz - MALLOC_MIN_OVERHEAD; - } -} - - - -void* realloc(void* mem, unsigned int bytes) -{ - if (mem == 0) - return malloc(bytes); - else - { - unsigned int nb = request2size(bytes); - mchunkptr p = mem2chunk(mem); - unsigned int oldsize = p->size; - int room; - mchunkptr nxt; - - UPDATE_STATS((++n_reallocs, requested_mem += bytes-oldsize)); - - /* try to expand (even if already big enough), to clean up chunk */ - - while (!inuse(nxt = next_chunk(p))) - { - UPDATE_STATS ((malloced_mem += nxt->size, ++n_consol)); - unlink(nxt); - set_size(p, p->size + nxt->size); - } - - room = p->size - nb; - if (room >= 0) - { - split(p, nb); - UPDATE_STATS(malloced_mem -= room); - return chunk2mem(p); - } - else /* do the obvious */ - { - void* newmem; - set_inuse(p); /* don't let malloc consolidate us yet! */ - newmem = malloc(nb); - bcopy(mem, newmem, oldsize - SIZE_SZ); - free(mem); - UPDATE_STATS(++n_reallocs_with_copy); - return newmem; - } - } -} - - - -/* return a pointer to space with at least the alignment requested */ - -void* memalign(unsigned int alignment, unsigned int bytes) -{ - mchunkptr p; - unsigned int nb = request2size(bytes); - - /* find an alignment that both we and the user can live with: */ - /* least common multiple guarantees mutual happiness */ - unsigned int align = lcm(alignment, MALLOC_MIN_OVERHEAD); - unsigned int mask = align - 1; - - /* call malloc with worst case padding to hit alignment; */ - /* we will give back extra */ - - unsigned int req = nb + align + MINSIZE; - void* m = malloc(req); - - if (m == 0) return m; - - p = mem2chunk(m); - - /* keep statistics on track */ - - UPDATE_STATS(--n_mallocs); - UPDATE_STATS(malloced_mem -= p->size); - UPDATE_STATS(requested_mem -= req); - UPDATE_STATS(requested_mem += bytes); - - if (((int)(m) & (mask)) != 0) /* misaligned */ - { - - /* find an aligned spot inside chunk */ - - mchunkptr ap = (mchunkptr)(( ((int)(m) + mask) & -align) - SIZE_SZ); - - unsigned int gap = (unsigned int)(ap) - (unsigned int)(p); - unsigned int room; - - /* we need to give back leading space in a chunk of at least MINSIZE */ - - if (gap < MINSIZE) - { - /* This works since align >= MINSIZE */ - /* and we've malloc'd enough total room */ - - ap = (mchunkptr)( (int)(ap) + align ); - gap += align; - } - - if (gap + nb > p->size) /* can't happen unless chunk sizes corrupted */ - malloc_user_error(); - - room = p->size - gap; - - /* give back leader */ - set_size(p, gap); - consollink(p); - - /* use the rest */ - p = ap; - set_size(p, room); - } - - /* also give back spare room at the end */ - - split(p, nb); - UPDATE_STATS(do_malloc_stats(p)); - return chunk2mem(p); - -} - -#ifndef sun -#include -#endif - -static unsigned int malloc_pagesize = 0; - -void* valloc(unsigned int bytes) -{ - if (malloc_pagesize == 0) malloc_pagesize = getpagesize(); - return memalign (malloc_pagesize, bytes); -} - - -void malloc_stats() -{ -#ifndef MALLOC_STATS -} -#else - int i; - mchunkptr p; - double nm = (double)(n_mallocs + n_reallocs); - - fprintf(stderr, "\nmalloc statistics\n\n"); - - if (n_mallocs != 0) - fprintf(stderr, "requests = %10u total size = %10u\tave = %10u\n", - n_mallocs, requested_mem, requested_mem/n_mallocs); - - if (n_mallocs != 0) - fprintf(stderr, "mallocs = %10u total size = %10u\tave = %10u\n", - n_mallocs, malloced_mem, malloced_mem/n_mallocs); - - if (n_frees != 0) - fprintf(stderr, "frees = %10u total size = %10u\tave = %10u\n", - n_frees, freed_mem, freed_mem/n_frees); - - if (n_mallocs-n_frees != 0) - fprintf(stderr, "in use = %10u total size = %10u\tave = %10u\n", - n_mallocs-n_frees, malloced_mem-freed_mem, - (malloced_mem-freed_mem) / (n_mallocs-n_frees)); - - if (max_inuse != 0) - fprintf(stderr, "max in use= %10u total size = %10u\tave = %10u\n", - max_inuse, max_used_mem, max_used_mem / max_inuse); - - if (n_avail != 0) - fprintf(stderr, "available = %10u total size = %10u\tave = %10u\n", - n_avail, sbrked_mem - (malloced_mem-freed_mem), - (sbrked_mem - (malloced_mem-freed_mem)) / n_avail); - - if (n_sbrks != 0) - fprintf(stderr, "sbrks = %10u total size = %10u\tave = %10u\n\n", - n_sbrks, sbrked_mem, sbrked_mem/ n_sbrks); - - if (n_reallocs != 0) - fprintf(stderr, "reallocs = %10u with copy = %10u\n\n", - n_reallocs, n_reallocs_with_copy); - - - if (nm != 0) - { - fprintf(stderr, "chunks scanned per malloc = %6.3f\n", - n_malloc_chunks / nm); - fprintf(stderr, "bins scanned per malloc = %6.3f\n", - n_malloc_bins / nm); - fprintf(stderr, "splits per malloc = %6.3f\n", - n_split / nm); - fprintf(stderr, "consolidations per malloc = %6.3f\n", - n_consol / nm); - } - - fprintf(stderr, "\nfree chunks:\n"); - for (i = 0; i < MAXBIN; ++i) - { - p = av[i].hd.fd; - if (p != &(av[i].hd)) - { - unsigned int count = 1; - unsigned int sz = p->size; - for (p = p->fd; p != &(av[i].hd); p = p->fd) - { - if (p->size == sz) - ++count; - else - { - fprintf(stderr, "\tsize = %10u count = %5u\n", sz, count); - count = 1; - sz = p->size; - } - } - - fprintf(stderr, "\tsize = %10u count = %5u\n", sz, count); - - } - } -} -#endif /* MALLOC_STATS */ - -#endif /* NO_LIBGXX_MALLOC */ diff --git a/gnu/lib/libg++/libg++/math-68881.h b/gnu/lib/libg++/libg++/math-68881.h new file mode 100644 index 0000000000..02f50a8e1b --- /dev/null +++ b/gnu/lib/libg++/libg++/math-68881.h @@ -0,0 +1,516 @@ +/******************************************************************\ +* * +* last modified: 23 May 1992. * +* * +* Copyright (C) 1989 by Matthew Self. * +* You may freely distribute verbatim copies of this software * +* provided that this copyright notice is retained in all copies. * +* You may distribute modifications to this software under the * +* conditions above if you also clearly note such modifications * +* with their author and date. * +* * +* Note: errno is not set to EDOM when domain errors occur for * +* most of these functions. Rather, it is assumed that the * +* 68881's OPERR exception will be enabled and handled * +* appropriately by the operating system. Similarly, overflow * +* and underflow do not set errno to ERANGE. * +* * +* Send bugs to Matthew Self (self@bayes.arc.nasa.gov). * +* * +\******************************************************************/ + +/* If you find this in GCC, + please send bug reports to bug-gcc@prep.ai.mit.edu. */ + +/* Changed by Richard Stallman: % inserted before a #. + New function `hypot' added. + Nans written in hex to avoid 0rnan. + May 1992, use %! for fpcr register. Break lines before function names. + December 1989, add parens around `&' in pow. + November 1990, added alternate definition of HUGE_VAL for Sun. */ + +#include + +#ifndef HUGE_VAL +#ifdef __sun__ +/* The Sun assembler fails to handle the hex constant in the usual defn. */ +#define HUGE_VAL \ +({ \ + static union { int i[2]; double d; } u = { {0x7ff00000, 0} }; \ + u.d; \ +}) +#else +#define HUGE_VAL \ +({ \ + double huge_val; \ + \ + __asm ("fmove%.d %#0x7ff0000000000000,%0" /* Infinity */ \ + : "=f" (huge_val) \ + : /* no inputs */); \ + huge_val; \ +}) +#endif +#endif + +__inline static const double +sin (double x) +{ + double value; + + __asm ("fsin%.x %1,%0" + : "=f" (value) + : "f" (x)); + return value; +} + +__inline static const double +cos (double x) +{ + double value; + + __asm ("fcos%.x %1,%0" + : "=f" (value) + : "f" (x)); + return value; +} + +__inline static const double +tan (double x) +{ + double value; + + __asm ("ftan%.x %1,%0" + : "=f" (value) + : "f" (x)); + return value; +} + +__inline static const double +asin (double x) +{ + double value; + + __asm ("fasin%.x %1,%0" + : "=f" (value) + : "f" (x)); + return value; +} + +__inline static const double +acos (double x) +{ + double value; + + __asm ("facos%.x %1,%0" + : "=f" (value) + : "f" (x)); + return value; +} + +__inline static const double +atan (double x) +{ + double value; + + __asm ("fatan%.x %1,%0" + : "=f" (value) + : "f" (x)); + return value; +} + +__inline static const double +atan2 (double y, double x) +{ + double pi, pi_over_2; + + __asm ("fmovecr%.x %#0,%0" /* extended precision pi */ + : "=f" (pi) + : /* no inputs */ ); + __asm ("fscale%.b %#-1,%0" /* no loss of accuracy */ + : "=f" (pi_over_2) + : "0" (pi)); + if (x > 0) + { + if (y > 0) + { + if (x > y) + return atan (y / x); + else + return pi_over_2 - atan (x / y); + } + else + { + if (x > -y) + return atan (y / x); + else + return - pi_over_2 - atan (x / y); + } + } + else + { + if (y > 0) + { + if (-x > y) + return pi + atan (y / x); + else + return pi_over_2 - atan (x / y); + } + else + { + if (-x > -y) + return - pi + atan (y / x); + else if (y < 0) + return - pi_over_2 - atan (x / y); + else + { + double value; + + errno = EDOM; + __asm ("fmove%.d %#0x7fffffffffffffff,%0" /* quiet NaN */ + : "=f" (value) + : /* no inputs */); + return value; + } + } + } +} + +__inline static const double +sinh (double x) +{ + double value; + + __asm ("fsinh%.x %1,%0" + : "=f" (value) + : "f" (x)); + return value; +} + +__inline static const double +cosh (double x) +{ + double value; + + __asm ("fcosh%.x %1,%0" + : "=f" (value) + : "f" (x)); + return value; +} + +__inline static const double +tanh (double x) +{ + double value; + + __asm ("ftanh%.x %1,%0" + : "=f" (value) + : "f" (x)); + return value; +} + +__inline static const double +atanh (double x) +{ + double value; + + __asm ("fatanh%.x %1,%0" + : "=f" (value) + : "f" (x)); + return value; +} + +__inline static const double +exp (double x) +{ + double value; + + __asm ("fetox%.x %1,%0" + : "=f" (value) + : "f" (x)); + return value; +} + +__inline static const double +expm1 (double x) +{ + double value; + + __asm ("fetoxm1%.x %1,%0" + : "=f" (value) + : "f" (x)); + return value; +} + +__inline static const double +log (double x) +{ + double value; + + __asm ("flogn%.x %1,%0" + : "=f" (value) + : "f" (x)); + return value; +} + +__inline static const double +log1p (double x) +{ + double value; + + __asm ("flognp1%.x %1,%0" + : "=f" (value) + : "f" (x)); + return value; +} + +__inline static const double +log10 (double x) +{ + double value; + + __asm ("flog10%.x %1,%0" + : "=f" (value) + : "f" (x)); + return value; +} + +__inline static const double +sqrt (double x) +{ + double value; + + __asm ("fsqrt%.x %1,%0" + : "=f" (value) + : "f" (x)); + return value; +} + +__inline static const double +hypot (const double x, const double y) +{ + return sqrt (x*x + y*y); +} + +__inline static const double +pow (const double x, const double y) +{ + if (x > 0) + return exp (y * log (x)); + else if (x == 0) + { + if (y > 0) + return 0.0; + else + { + double value; + + errno = EDOM; + __asm ("fmove%.d %#0x7fffffffffffffff,%0" /* quiet NaN */ + : "=f" (value) + : /* no inputs */); + return value; + } + } + else + { + double temp; + + __asm ("fintrz%.x %1,%0" + : "=f" (temp) /* integer-valued float */ + : "f" (y)); + if (y == temp) + { + int i = (int) y; + + if ((i & 1) == 0) /* even */ + return exp (y * log (-x)); + else + return - exp (y * log (-x)); + } + else + { + double value; + + errno = EDOM; + __asm ("fmove%.d %#0x7fffffffffffffff,%0" /* quiet NaN */ + : "=f" (value) + : /* no inputs */); + return value; + } + } +} + +__inline static const double +fabs (double x) +{ + double value; + + __asm ("fabs%.x %1,%0" + : "=f" (value) + : "f" (x)); + return value; +} + +__inline static const double +ceil (double x) +{ + int rounding_mode, round_up; + double value; + + __asm volatile ("fmove%.l %!,%0" + : "=dm" (rounding_mode) + : /* no inputs */ ); + round_up = rounding_mode | 0x30; + __asm volatile ("fmove%.l %0,%!" + : /* no outputs */ + : "dmi" (round_up)); + __asm volatile ("fint%.x %1,%0" + : "=f" (value) + : "f" (x)); + __asm volatile ("fmove%.l %0,%!" + : /* no outputs */ + : "dmi" (rounding_mode)); + return value; +} + +__inline static const double +floor (double x) +{ + int rounding_mode, round_down; + double value; + + __asm volatile ("fmove%.l %!,%0" + : "=dm" (rounding_mode) + : /* no inputs */ ); + round_down = (rounding_mode & ~0x10) + | 0x20; + __asm volatile ("fmove%.l %0,%!" + : /* no outputs */ + : "dmi" (round_down)); + __asm volatile ("fint%.x %1,%0" + : "=f" (value) + : "f" (x)); + __asm volatile ("fmove%.l %0,%!" + : /* no outputs */ + : "dmi" (rounding_mode)); + return value; +} + +__inline static const double +rint (double x) +{ + int rounding_mode, round_nearest; + double value; + + __asm volatile ("fmove%.l %!,%0" + : "=dm" (rounding_mode) + : /* no inputs */ ); + round_nearest = rounding_mode & ~0x30; + __asm volatile ("fmove%.l %0,%!" + : /* no outputs */ + : "dmi" (round_nearest)); + __asm volatile ("fint%.x %1,%0" + : "=f" (value) + : "f" (x)); + __asm volatile ("fmove%.l %0,%!" + : /* no outputs */ + : "dmi" (rounding_mode)); + return value; +} + +__inline static const double +fmod (double x, double y) +{ + double value; + + __asm ("fmod%.x %2,%0" + : "=f" (value) + : "0" (x), + "f" (y)); + return value; +} + +__inline static const double +drem (double x, double y) +{ + double value; + + __asm ("frem%.x %2,%0" + : "=f" (value) + : "0" (x), + "f" (y)); + return value; +} + +__inline static const double +scalb (double x, int n) +{ + double value; + + __asm ("fscale%.l %2,%0" + : "=f" (value) + : "0" (x), + "dmi" (n)); + return value; +} + +__inline static double +logb (double x) +{ + double exponent; + + __asm ("fgetexp%.x %1,%0" + : "=f" (exponent) + : "f" (x)); + return exponent; +} + +__inline static const double +ldexp (double x, int n) +{ + double value; + + __asm ("fscale%.l %2,%0" + : "=f" (value) + : "0" (x), + "dmi" (n)); + return value; +} + +__inline static double +frexp (double x, int *exp) +{ + double float_exponent; + int int_exponent; + double mantissa; + + __asm ("fgetexp%.x %1,%0" + : "=f" (float_exponent) /* integer-valued float */ + : "f" (x)); + int_exponent = (int) float_exponent; + __asm ("fgetman%.x %1,%0" + : "=f" (mantissa) /* 1.0 <= mantissa < 2.0 */ + : "f" (x)); + if (mantissa != 0) + { + __asm ("fscale%.b %#-1,%0" + : "=f" (mantissa) /* mantissa /= 2.0 */ + : "0" (mantissa)); + int_exponent += 1; + } + *exp = int_exponent; + return mantissa; +} + +__inline static double +modf (double x, double *ip) +{ + double temp; + + __asm ("fintrz%.x %1,%0" + : "=f" (temp) /* integer-valued float */ + : "f" (x)); + *ip = temp; + return x - temp; +} + diff --git a/gnu/lib/libg++/libg++/max.cc b/gnu/lib/libg++/libg++/max.cc deleted file mode 100644 index cb42168a43..0000000000 --- a/gnu/lib/libg++/libg++/max.cc +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __GNUG__ -#pragma implementation -#endif -#include diff --git a/gnu/lib/libg++/libg++/min.cc b/gnu/lib/libg++/libg++/min.cc deleted file mode 100644 index 7f0c9b5d0b..0000000000 --- a/gnu/lib/libg++/libg++/min.cc +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __GNUG__ -#pragma implementation -#endif -#include diff --git a/gnu/lib/libg++/libg++/minmax.h b/gnu/lib/libg++/libg++/minmax.h new file mode 100644 index 0000000000..ae912881e9 --- /dev/null +++ b/gnu/lib/libg++/libg++/minmax.h @@ -0,0 +1,65 @@ +/* +Copyright (C) 1992 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _minmax_h +#ifdef _GNUG_ +#pragma interface +#endif +#define _minmax_h 1 + +#include <_G_config.h> + +inline char min(char a, char b) { return (a < b)?a:b;} +#ifndef _G_BROKEN_SIGNED_CHAR +inline signed char min(signed char a, signed char b) { return (a < b)?a:b;} +#endif +inline unsigned char min(unsigned char a, unsigned char b) {return (a b)?a:b;} +#ifndef _G_BROKEN_SIGNED_CHAR +inline signed char max(signed char a, signed char b) {return (a > b)?a:b;} +#endif +inline unsigned char max(unsigned char a, unsigned char b) {return (a>b)?a:b;} + +inline short max(short a, short b) {return (a > b) ?a:b;} +inline unsigned short max(unsigned short a, unsigned short b) {return (a > b)?a:b;} + +inline int max(int a, int b) {return (a > b)?a:b;} +inline unsigned int max(unsigned int a, unsigned int b) {return (a > b)?a:b;} + +inline long max(long a, long b) {return (a > b)?a:b;} +inline unsigned long max(unsigned long a, unsigned long b) {return (a>b)?a:b;} + +inline float max(float a, float b) {return (a > b)?a:b;} + +inline double max(double a, double b) {return (a > b)?a:b;} + +#endif + diff --git a/gnu/lib/libg++/libg++/new.cc b/gnu/lib/libg++/libg++/new.cc index 870785fa71..7a707a6ee9 100644 --- a/gnu/lib/libg++/libg++/new.cc +++ b/gnu/lib/libg++/libg++/new.cc @@ -1,39 +1,31 @@ -#ifndef NO_LIBGXX_MALLOC - // This may look like C code, but it is really -*- C++ -*- /* Copyright (C) 1989 Free Software Foundation written by Doug Lea (dl@oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ #pragma implementation #endif -#include -#include +#include + +#if 0 /* ndef NO_LIBGXX_MALLOC */ void* operator new(size_t n) { return malloc (n); } - - #endif diff --git a/gnu/lib/libg++/libg++/new.h b/gnu/lib/libg++/libg++/new.h new file mode 100644 index 0000000000..351dbd52d0 --- /dev/null +++ b/gnu/lib/libg++/libg++/new.h @@ -0,0 +1,33 @@ +#ifndef _new_h +#ifdef __GNUG__ +#pragma interface +#endif +#define _new_h 1 + +#include +#include + +#ifndef NO_LIBGXX_MALLOC +#define MALLOC_ALIGN_MASK 7 /* ptrs aligned at 8 byte boundaries */ +#define MALLOC_MIN_OVERHEAD 8 /* 8 bytes of overhead per pointer */ +#endif + +typedef void (*new_handler_t)(); +extern new_handler_t __new_handler; +extern "C" void default_new_handler(); +extern "C" new_handler_t set_new_handler(new_handler_t); + +#ifdef __GNUG__ +#define NEW(where) new { where } +#endif + +// default placement version of operator new +static inline void *operator new(size_t, void *place) { return place; } + +// provide a C++ interface to vector-resize via realloc +inline void *operator new(size_t size, void *ptr, size_t new_len) +{ + return realloc(ptr, new_len * size); +} + +#endif diff --git a/gnu/lib/libg++/libg++/open.cc b/gnu/lib/libg++/libg++/open.cc deleted file mode 100644 index 7c13ee9ed1..0000000000 --- a/gnu/lib/libg++/libg++/open.cc +++ /dev/null @@ -1,46 +0,0 @@ -#include -#pragma implementation - -int open_cmd_arg(io_mode i, access_mode a) // decode modes -{ - int arg; - switch(i) - { - case io_readonly: arg = sio_read; break; - case io_writeonly: arg = sio_write; break; - case io_readwrite: arg = sio_readwrite; break; - case io_appendonly: arg = sio_append | sio_write; break; - case io_append: arg = sio_append | sio_readwrite; break; - default: return -1; - }; - switch(a) - { - case a_createonly: return arg | sa_createonly; - case a_create: return arg | sa_create | sa_truncate; - case a_useonly: return arg; - case a_use: return arg | sa_create; - default: return -1; - } -} - -char* fopen_cmd_arg(io_mode i) -{ - switch(i) - { - case io_readonly: return "r"; - case io_writeonly: return "w"; - case io_readwrite: return "r+"; - case io_appendonly:return "a"; - case io_append: return "a+"; - default: return 0; - } -} - -int open_cmd_arg(const char* m) -{ - if (m == 0) return -1; - else if (m[0] == 'r') return (m[1] == '+')? io_readwrite : io_readonly; - else if (m[0] == 'w') return (m[1] == '+')? io_readwrite : io_writeonly; - else if (m[0] == 'a') return (m[1] == '+')? io_append : io_appendonly; - else return -1; -} diff --git a/gnu/lib/libg++/libg++/osfcn.h b/gnu/lib/libg++/libg++/osfcn.h new file mode 100644 index 0000000000..554e1570da --- /dev/null +++ b/gnu/lib/libg++/libg++/osfcn.h @@ -0,0 +1,16 @@ + +#ifndef OSFCN_H +#define OSFCN_H 1 + +#include +#include +#include +#if _G_HAVE_SYS_SOCKET +#include +#endif +#if _G_HAVE_SYS_RESOURCE +#include +#endif + + +#endif diff --git a/gnu/lib/libg++/libg++/ostream.cc b/gnu/lib/libg++/libg++/ostream.cc deleted file mode 100644 index adee1bd267..0000000000 --- a/gnu/lib/libg++/libg++/ostream.cc +++ /dev/null @@ -1,224 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1989 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -/* *** Version 1.2 -- nearly 100% AT&T 1.2 compatible *** */ - -#ifdef __GNUG__ -#pragma implementation -#endif -#include -#include -#include -#include -#include - -ostream::ostream(streambuf* s) - : bp(s), state(_good), ownbuf(0) {} - -ostream::ostream(int sz, char* buf) - : state(_good), ownbuf(1) -{ - if (buf == 0) - { - buf = new char[sz]; - bp = new streambuf(buf, sz); - bp->setbuf(buf, sz); - bp->alloc = 1; - } - else - { - bp = new streambuf(buf, sz); - bp->alloc = 0; - } -} - - -ostream::ostream(const char* filename, io_mode m, access_mode a) - : state(_good), ownbuf(1) -{ - bp = new filebuf(filename, m, a); -} - -ostream::ostream(const char* filename, const char* m) - : state(_good), ownbuf(1) -{ - bp = new filebuf(filename, m); -} - -ostream::ostream(int filedesc, io_mode m) - : state(_good), ownbuf(1) -{ - bp = new filebuf(filedesc, m); -} - -ostream::ostream(FILE* fileptr) - : state(_good), ownbuf(1) -{ - bp = new filebuf(fileptr); -} - -ostream::ostream(int filedesc) - : state(_good), ownbuf(1) -{ - bp = new filebuf(filedesc); -} - -ostream::ostream(int filedesc, char* buf, int buflen) - : state(_good), ownbuf(1) -{ - bp = new filebuf(filedesc, buf, buflen); -} - -ostream::~ostream() -{ - if (ownbuf) delete bp; -} - -ostream& ostream::open(const char* filename, io_mode m, access_mode a) -{ - return failif(bp->open(filename, m, a) == 0); -} - -ostream& ostream::open(const char* filename, const char* m) -{ - return failif(bp->open(filename, m) == 0); -} - -ostream& ostream::open(int filedesc, io_mode m) -{ - return failif(bp->open(filedesc, m) == 0); -} - -ostream& ostream::open(FILE* fileptr) -{ - return failif(bp->open(fileptr) == 0); -} - -ostream& ostream::open(const char* filenam, open_mode m) -{ - return failif(bp->open(filenam, m) == 0); -} - -ostream& ostream::form(const char* fmt...) -{ - va_list args; - va_start(args, fmt); - char buf[BUFSIZ]; -#ifndef HAVE_VPRINTF - FILE b; - b._flag = _IOWRT|_IOSTRG; - b._ptr = buf; - b._cnt = BUFSIZ; - _doprnt(fmt, args, &b); - putc('\0', &b); -#else - vsprintf(buf, fmt, args); -#endif - va_end(args); - return put(buf); -} - -ostream& ostream::operator<<(short n) -{ - return put(itoa(long(n))); -} - -ostream& ostream::operator<<(unsigned short n) -{ - return put(itoa((unsigned long)(n))); -} - -ostream& ostream::operator<<(int n) -{ - return put(itoa(long(n))); -} - -ostream& ostream::operator<<(unsigned int n) -{ - return put(itoa((unsigned long)(n))); -} - -ostream& ostream::operator<<(long n) -{ - return put(itoa(n)); -} - -ostream& ostream::operator<<(unsigned long n) -{ - return put(itoa(n)); -} - -#ifdef __GNUG__ -#ifndef VMS -ostream& ostream::operator<<(long long n) -{ - return put(itoa(n)); -} - -ostream& ostream::operator<<(unsigned long long n) -{ - return put(itoa(n)); -} -#endif -#endif -ostream& ostream::operator<<(float n) -{ - return put(dtoa(double(n))); -} - -ostream& ostream::operator<<(double n) -{ - return put(dtoa(n)); -} - -ostream& ostream::operator<<(const void* p) -{ - put("0x"); - return put(itoa((unsigned long)(p), 16)); -} - - -const char* ostream::name() -{ - return bp->name(); -} - -void ostream::error() -{ - bp->error(); -} - -#if 0 /* BSD 4.4 */ - -ostream cerr(stderr); -ostream cout(stdout); - -#else - -static char cerrbuf[1]; -static char coutbuf[BUFSIZE]; - -ostream cerr(2, cerrbuf, 1); -ostream cout(1, coutbuf, BUFSIZE); - -#endif diff --git a/gnu/lib/libg++/libg++/pow.cc b/gnu/lib/libg++/libg++/pow.cc index 2f374a2bfc..b56a8b7d77 100644 --- a/gnu/lib/libg++/libg++/pow.cc +++ b/gnu/lib/libg++/libg++/pow.cc @@ -2,22 +2,17 @@ Copyright (C) 1990 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ diff --git a/gnu/lib/libg++/libg++/regex.cc b/gnu/lib/libg++/libg++/regex.cc index 92860f1a18..199308d0da 100644 --- a/gnu/lib/libg++/libg++/regex.cc +++ b/gnu/lib/libg++/libg++/regex.cc @@ -1,121 +1,51 @@ -/* Extended regular expression matching and search. - Copyright (C) 1985 Free Software Foundation, Inc. - - NO WARRANTY - - BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY -NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT -WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC, -RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS" -WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY -AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE -DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR -CORRECTION. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M. -STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY -WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE -LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR -OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR -DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR -A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS -PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY. - - GENERAL PUBLIC LICENSE TO COPY - - 1. You may copy and distribute verbatim copies of this source file -as you receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy a valid copyright notice "Copyright -(C) 1985 Free Software Foundation, Inc."; and include following the -copyright notice a verbatim copy of the above disclaimer of warranty -and of this License. You may charge a distribution fee for the -physical act of transferring a copy. - - 2. You may modify your copy or copies of this source file or -any portion of it, and copy and distribute such modifications under -the terms of Paragraph 1 above, provided that you also do the following: - - a) cause the modified files to carry prominent notices stating - that you changed the files and the date of any change; and - - b) cause the whole of any work that you distribute or publish, - that in whole or in part contains or is a derivative of this - program or any part thereof, to be licensed at no charge to all - third parties on terms identical to those contained in this - License Agreement (except that you may choose to grant more extensive - warranty protection to some or all third parties, at your option). - - c) You may charge a distribution fee for the physical act of - transferring a copy, and you may at your option offer warranty - protection in exchange for a fee. - -Mere aggregation of another unrelated program with this program (or its -derivative) on a volume of a storage or distribution medium does not bring -the other program under the scope of these terms. - - 3. You may copy and distribute this program (or a portion or derivative -of it, under Paragraph 2) in object code or executable form under the terms -of Paragraphs 1 and 2 above provided that you also do one of the following: - - a) accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of - Paragraphs 1 and 2 above; or, - - b) accompany it with a written offer, valid for at least three - years, to give any third party free (except for a nominal - shipping charge) a complete machine-readable copy of the - corresponding source code, to be distributed under the terms of - Paragraphs 1 and 2 above; or, - - c) accompany it with the information you received as to where the - corresponding source code may be obtained. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form alone.) - -For an executable file, complete source code means all the source code for -all modules it contains; but, as a special exception, it need not include -source code for modules which are standard libraries that accompany the -operating system on which the executable file runs. - - 4. You may not copy, sublicense, distribute or transfer this program -except as expressly provided under this License Agreement. Any attempt -otherwise to copy, sublicense, distribute or transfer this program is void and -your rights to use the program under this License agreement shall be -automatically terminated. However, parties who have received computer -software programs from you with this License Agreement will not have -their licenses terminated so long as such parties remain in full compliance. - - 5. If you wish to incorporate parts of this program into other free -programs whose distribution conditions are different, write to the Free -Software Foundation at 675 Mass Ave, Cambridge, MA 02139. We have not yet -worked out a simple rule that can be stated here, but we will often permit -this. We will be guided by the two goals of preserving the free status of -all derivatives of our free software and of promoting the sharing and reuse of -software. - - -In other words, you are welcome to use, share and improve this program. -You are forbidden to forbid anyone else to use, share and improve -what you give them. Help stamp out software-hoarding! */ - - -/* To test, compile with -Dtest. - This Dtestable feature turns this into a self-contained program - which reads a pattern, describes how it compiles, - then reads a string and searches for it. */ +/* Extended regular expression matching and search library. + Copyright (C) 1985, 1989-90 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +// This is a translation into C++ of regex.c, the GNU regexp package. + +/* To test, compile with -Dtest. This Dtestable feature turns this into + a self-contained program which reads a pattern, describes how it + compiles, then reads a string and searches for it. + + On the other hand, if you compile with both -Dtest and -Dcanned you + can run some tests we've already thought of. */ + +/* AIX requires the alloca decl to be the first thing in the file. */ +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +#ifdef sparc +#include +extern "C" void *__builtin_alloca(...); +#else +#ifdef _AIX +#pragma alloca +#else +char *alloca (); +#endif +#endif +#endif #ifdef emacs /* The `emacs' switch turns on certain special matching commands - that make sense only in emacs. */ + that make sense only in emacs. */ -#ifdef __GNUG__ -#pragma implementation -#endif #include "config.h" #include "lisp.h" #include "buffer.h" @@ -123,29 +53,32 @@ what you give them. Help stamp out software-hoarding! */ #else /* not emacs */ -#include -#include +#include <_G_config.h> +#include +#include -/* - * Define the syntax stuff, so we can do the \<...\> things. - */ +/* Define the syntax stuff, so we can do the \<, \>, etc. */ -#ifndef Sword /* must be non-zero in some of the tests below... */ +/* This must be nonzero for the wordchar and notwordchar pattern + commands in re_match_2. */ +#ifndef Sword #define Sword 1 #endif #define SYNTAX(c) re_syntax_table[c] + #ifdef SYNTAX_TABLE char *re_syntax_table; -#else +#else /* not SYNTAX_TABLE */ static char re_syntax_table[256]; + static void -init_syntax_once (void) +init_syntax_once () { register int c; static int done = 0; @@ -153,7 +86,7 @@ init_syntax_once (void) if (done) return; - bzero (re_syntax_table, sizeof re_syntax_table); + memset (re_syntax_table, 0, sizeof re_syntax_table); for (c = 'a'; c <= 'z'; c++) re_syntax_table[c] = Sword; @@ -168,34 +101,160 @@ init_syntax_once (void) } #endif /* SYNTAX_TABLE */ -#endif /* not emacs */ +#endif /* emacs */ + +/* We write fatal error messages on standard error. */ +#include +/* isalpha(3) etc. are used for the character classes. */ +#include +/* Sequents are missing isgraph. */ +#ifndef isgraph +#define isgraph(c) (isprint((c)) && !isspace((c))) +#endif + +/* Get the interface, including the syntax bits. */ #include "regex.h" + +/* These are the command codes that appear in compiled regular + expressions, one per byte. Some command codes are followed by + argument bytes. A command code can specify any interpretation + whatsoever for its arguments. Zero-bytes may appear in the compiled + regular expression. + + The value of `exactn' is needed in search.c (search_buffer) in emacs. + So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of + `exactn' we use here must also be 1. */ + +enum regexpcode + { + unused=0, + exactn=1, /* Followed by one byte giving n, then by n literal bytes. */ + begline, /* Fail unless at beginning of line. */ + endline, /* Fail unless at end of line. */ + jump, /* Followed by two bytes giving relative address to jump to. */ + on_failure_jump, /* Followed by two bytes giving relative address of + place to resume at in case of failure. */ + finalize_jump, /* Throw away latest failure point and then jump to + address. */ + maybe_finalize_jump, /* Like jump but finalize if safe to do so. + This is used to jump back to the beginning + of a repeat. If the command that follows + this jump is clearly incompatible with the + one at the beginning of the repeat, such that + we can be sure that there is no use backtracking + out of repetitions already completed, + then we finalize. */ + dummy_failure_jump, /* Jump, and push a dummy failure point. This + failure point will be thrown away if an attempt + is made to use it for a failure. A + construct + makes this before the first repeat. Also + use it as an intermediary kind of jump when + compiling an or construct. */ + succeed_n, /* Used like on_failure_jump except has to succeed n times; + then gets turned into an on_failure_jump. The relative + address following it is useless until then. The + address is followed by two bytes containing n. */ + jump_n, /* Similar to jump, but jump n times only; also the relative + address following is in turn followed by yet two more bytes + containing n. */ + set_number_at, /* Set the following relative location to the + subsequent number. */ + anychar, /* Matches any (more or less) one character. */ + charset, /* Matches any one char belonging to specified set. + First following byte is number of bitmap bytes. + Then come bytes for a bitmap saying which chars are in. + Bits in each byte are ordered low-bit-first. + A character is in the set if its bit is 1. + A character too large to have a bit in the map + is automatically not in the set. */ + charset_not, /* Same parameters as charset, but match any character + that is not one of those specified. */ + start_memory, /* Start remembering the text that is matched, for + storing in a memory register. Followed by one + byte containing the register number. Register numbers + must be in the range 0 through RE_NREGS. */ + stop_memory, /* Stop remembering the text that is matched + and store it in a memory register. Followed by + one byte containing the register number. Register + numbers must be in the range 0 through RE_NREGS. */ + duplicate, /* Match a duplicate of something remembered. + Followed by one byte containing the index of the memory + register. */ +#ifdef emacs + before_dot, /* Succeeds if before point. */ + at_dot, /* Succeeds if at point. */ + after_dot, /* Succeeds if after point. */ +#endif + begbuf, /* Succeeds if at beginning of buffer. */ + endbuf, /* Succeeds if at end of buffer. */ + wordchar, /* Matches any word-constituent character. */ + notwordchar, /* Matches any char that is not a word-constituent. */ + wordbeg, /* Succeeds if at word beginning. */ + wordend, /* Succeeds if at word end. */ + wordbound, /* Succeeds if at a word boundary. */ + notwordbound,/* Succeeds if not at a word boundary. */ +#ifdef emacs + syntaxspec, /* Matches any character whose syntax is specified. + followed by a byte which contains a syntax code, + e.g., Sword. */ + notsyntaxspec /* Matches any character whose syntax differs from + that specified. */ +#endif + }; + + /* Number of failure points to allocate space for initially, - when matching. If this number is exceeded, more space is allocated, - so it is not a hard limit. */ + when matching. If this number is exceeded, more space is allocated, + so it is not a hard limit. */ #ifndef NFAILURES #define NFAILURES 80 -#endif /* NFAILURES */ - -/* width of a byte in bits */ +#endif -#define BYTEWIDTH 8 #ifndef SIGN_EXTEND_CHAR -#define SIGN_EXTEND_CHAR(x) (x) +#ifdef __STDC__ +#define SIGN_EXTEND_CHAR(c) ((signed char)(c)) +#else +#define SIGN_EXTEND_CHAR(c) (((c)^128) - 128) /* As in Harbison and Steele. */ #endif - -static int obscure_syntax = 0; +#endif /* not SIGN_EXTEND_CHAR */ -/* Specify the precise syntax of regexp for compilation. - This provides for compatibility for various utilities - which historically have different, incompatible syntaxes. - - The argument SYNTAX is a bit-mask containing the two bits - RE_NO_BK_PARENS and RE_NO_BK_VBAR. */ +/* Store NUMBER in two contiguous bytes starting at DESTINATION. */ +#define STORE_NUMBER(destination, number) \ + { (destination)[0] = (char)((number) & 0377); \ + (destination)[1] = (number) >> 8; } + +/* Same as STORE_NUMBER, except increment the destination pointer to + the byte after where the number is stored. Watch out that values for + DESTINATION such as p + 1 won't work, whereas p will. */ +#define STORE_NUMBER_AND_INCR(destination, number) \ + { STORE_NUMBER(destination, number); \ + (destination) += 2; } + + +/* Put into DESTINATION a number stored in two contingous bytes starting + at SOURCE. */ +#define EXTRACT_NUMBER(destination, source) \ + { (destination) = *(source) & 0377; \ + (destination) += SIGN_EXTEND_CHAR (*(char *)((source) + 1)) << 8; } + +/* Same as EXTRACT_NUMBER, except increment the pointer for source to + point to second byte of SOURCE. Note that SOURCE has to be a value + such as p, not, e.g., p + 1. */ +#define EXTRACT_NUMBER_AND_INCR(destination, source) \ + { EXTRACT_NUMBER (destination, source); \ + (source) += 2; } + + +/* Specify the precise syntax of regexps for compilation. This provides + for compatibility for various utilities which historically have + different, incompatible syntaxes. + + The argument SYNTAX is a bit-mask comprised of the various bits + defined in regex.h. */ int re_set_syntax (int syntax) @@ -206,99 +265,164 @@ re_set_syntax (int syntax) obscure_syntax = syntax; return ret; } - -/* re_compile_pattern takes a regular-expression string - and converts it into a buffer full of byte commands for matching. - PATTERN is the address of the pattern string - SIZE is the length of it. - BUFP is a struct re_pattern_buffer * which points to the info - on where to store the byte commands. - This structure contains a char * which points to the - actual space, which should have been obtained with malloc. - re_compile_pattern may use realloc to grow the buffer space. +/* Set by re_set_syntax to the current regexp syntax to recognize. */ +int obscure_syntax = 0; - The number of bytes of commands can be found out by looking in - the struct re_pattern_buffer that bufp pointed to, - after re_compile_pattern returns. -*/ -#define PATPUSH(ch) (*b++ = (char) (ch)) + +/* Macros for re_compile_pattern, which is found below these definitions. */ + +#define CHAR_CLASS_MAX_LENGTH 6 -#define PATFETCH(c) \ - {if (p == pend) goto end_of_pattern; \ - c = * (unsigned char *) p++; \ +/* Fetch the next character in the uncompiled pattern, translating it if + necessary. */ +#define PATFETCH(c) \ + {if (p == pend) goto end_of_pattern; \ + c = * (const unsigned char *) p++; \ if (translate) c = translate[c]; } -#define PATFETCH_RAW(c) \ - {if (p == pend) goto end_of_pattern; \ - c = * (unsigned char *) p++; } +/* Fetch the next character in the uncompiled pattern, with no + translation. */ +#define PATFETCH_RAW(c) \ + {if (p == pend) goto end_of_pattern; \ + c = * (const unsigned char *) p++; } #define PATUNFETCH p-- -#define EXTEND_BUFFER \ - { char *old_buffer = bufp->buffer; \ - if (bufp->allocated == (1<<16)) goto too_big; \ - bufp->allocated *= 2; \ - if (bufp->allocated > (1<<16)) bufp->allocated = (1<<16); \ - if (!(bufp->buffer = (char *) realloc (bufp->buffer, bufp->allocated))) \ - goto memory_exhausted; \ - c = bufp->buffer - old_buffer; \ - b += c; \ - if (fixup_jump) \ - fixup_jump += c; \ - if (laststart) \ - laststart += c; \ - begalt += c; \ - if (pending_exact) \ - pending_exact += c; \ + +/* If the buffer isn't allocated when it comes in, use this. */ +#define INIT_BUF_SIZE 28 + +/* Make sure we have at least N more bytes of space in buffer. */ +#define GET_BUFFER_SPACE(n) \ + { \ + while (b - bufp->buffer + (n) >= bufp->allocated) \ + EXTEND_BUFFER; \ } -static void store_jump (char *, char, char *); -static void insert_jump (char, char *, char *, char *); -int re_match_2 (struct re_pattern_buffer *, unsigned char *, int, unsigned char *, - int, int, struct re_registers *, int mstop); +/* Make sure we have one more byte of buffer space and then add CH to it. */ +#define BUFPUSH(ch) \ + { \ + GET_BUFFER_SPACE (1); \ + *b++ = (char) (ch); \ + } + +/* Extend the buffer by twice its current size via reallociation and + reset the pointers that pointed into the old allocation to point to + the correct places in the new allocation. If extending the buffer + results in it being larger than 1 << 16, then flag memory exhausted. */ +#define EXTEND_BUFFER \ + { char *old_buffer = bufp->buffer; \ + if (bufp->allocated == (1L<<16)) goto too_big; \ + bufp->allocated *= 2; \ + if (bufp->allocated > (1L<<16)) bufp->allocated = (1L<<16); \ + bufp->buffer = (char *) realloc (bufp->buffer, bufp->allocated); \ + if (bufp->buffer == 0) \ + goto memory_exhausted; \ + b = (b - old_buffer) + bufp->buffer; \ + if (fixup_jump) \ + fixup_jump = (fixup_jump - old_buffer) + bufp->buffer; \ + if (laststart) \ + laststart = (laststart - old_buffer) + bufp->buffer; \ + begalt = (begalt - old_buffer) + bufp->buffer; \ + if (pending_exact) \ + pending_exact = (pending_exact - old_buffer) + bufp->buffer; \ + } + +/* Set the bit for character C in a character set list. */ +#define SET_LIST_BIT(c) (b[(c) / BYTEWIDTH] |= 1 << ((c) % BYTEWIDTH)) + +/* Get the next unsigned number in the uncompiled pattern. */ +#define GET_UNSIGNED_NUMBER(num) \ + { if (p != pend) \ + { \ + PATFETCH (c); \ + while (isdigit (c)) \ + { \ + if (num < 0) \ + num = 0; \ + num = num * 10 + c - '0'; \ + if (p == pend) \ + break; \ + PATFETCH (c); \ + } \ + } \ + } + +/* Subroutines for re_compile_pattern. */ +static void store_jump (char *from, char opcode, char *to); +static void insert_jump (char op, char *from, char *to, char *current_end); +static void store_jump_n (char *from, char opcode, char *to, unsigned n); +static void insert_jump_n (char, char *, char *, char *, unsigned); +static void insert_op_2 (char, char *, char *_end, int, int); + + +/* re_compile_pattern takes a regular-expression string + and converts it into a buffer full of byte commands for matching. + + PATTERN is the address of the pattern string + SIZE is the length of it. + BUFP is a struct re_pattern_buffer * which points to the info + on where to store the byte commands. + This structure contains a char * which points to the + actual space, which should have been obtained with malloc. + re_compile_pattern may use realloc to grow the buffer space. + + The number of bytes of commands can be found out by looking in + the `struct re_pattern_buffer' that bufp pointed to, after + re_compile_pattern returns. */ char * -re_compile_pattern (char *pattern, int size, struct re_pattern_buffer *bufp) +re_compile_pattern (const char *pattern, int size, struct re_pattern_buffer *bufp) { register char *b = bufp->buffer; - register char *p = pattern; - char *pend = pattern + size; + register const char *p = pattern; + const char *pend = pattern + size; register unsigned c, c1; - char *p1; + const char *p1; unsigned char *translate = (unsigned char *) bufp->translate; - /* address of the count-byte of the most recently inserted "exactn" command. - This makes it possible to tell whether a new exact-match character - can be added to that command or requires a new "exactn" command. */ + /* Address of the count-byte of the most recently inserted `exactn' + command. This makes it possible to tell whether a new exact-match + character can be added to that command or requires a new `exactn' + command. */ char *pending_exact = 0; - /* address of the place where a forward-jump should go - to the end of the containing expression. - Each alternative of an "or", except the last, ends with a forward-jump - of this sort. */ + /* Address of the place where a forward-jump should go to the end of + the containing expression. Each alternative of an `or', except the + last, ends with a forward-jump of this sort. */ char *fixup_jump = 0; - /* address of start of the most recently finished expression. - This tells postfix * where to find the start of its operand. */ + /* Address of start of the most recently finished expression. + This tells postfix * where to find the start of its operand. */ char *laststart = 0; - /* In processing a repeat, 1 means zero matches is allowed */ + /* In processing a repeat, 1 means zero matches is allowed. */ char zero_times_ok; - /* In processing a repeat, 1 means many matches is allowed */ + /* In processing a repeat, 1 means many matches is allowed. */ char many_times_ok; - /* address of beginning of regexp, or inside of last \( */ + /* Address of beginning of regexp, or inside of last \(. */ char *begalt = b; + /* In processing an interval, at least this many matches must be made. */ + int lower_bound; + + /* In processing an interval, at most this many matches can be made. */ + int upper_bound; + + /* Place in pattern (i.e., the {) to which to go back if the interval + is invalid. */ + const char *beg_interval = 0; + /* Stack of information saved by \( and restored by \). Four stack elements are pushed by each \(: First, the value of b. @@ -312,102 +436,126 @@ re_compile_pattern (char *pattern, int size, struct re_pattern_buffer *bufp) int *stackt; /* Counts \('s as they are encountered. Remembered for the matching \), - where it becomes the "register number" to put in the stop_memory command */ + where it becomes the register number to put in the stop_memory + command. */ - int regnum = 1; + unsigned regnum = 1; bufp->fastmap_accurate = 0; #ifndef emacs #ifndef SYNTAX_TABLE - /* - * Initialize the syntax table. - */ + /* Initialize the syntax table. */ init_syntax_once(); #endif #endif if (bufp->allocated == 0) { - bufp->allocated = 28; + bufp->allocated = INIT_BUF_SIZE; if (bufp->buffer) - /* EXTEND_BUFFER loses when bufp->allocated is 0 */ - bufp->buffer = (char *) realloc (bufp->buffer, 28); + /* EXTEND_BUFFER loses when bufp->allocated is 0. */ + bufp->buffer = (char *) realloc (bufp->buffer, INIT_BUF_SIZE); else - /* Caller did not allocate a buffer. Do it for him */ - bufp->buffer = (char *) new char[28]; + /* Caller did not allocate a buffer. Do it for them. */ + bufp->buffer = (char *) malloc (INIT_BUF_SIZE); if (!bufp->buffer) goto memory_exhausted; begalt = b = bufp->buffer; } while (p != pend) { - if (b - bufp->buffer > bufp->allocated - 10) - /* Note that EXTEND_BUFFER clobbers c */ - EXTEND_BUFFER; - PATFETCH (c); switch (c) { case '$': - if (obscure_syntax & RE_TIGHT_VBAR) - { - if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS) && p != pend) - goto normal_char; - /* Make operand of last vbar end before this `$'. */ - if (fixup_jump) - store_jump (fixup_jump, jump, b); - fixup_jump = 0; - PATPUSH (endline); - break; - } - - /* $ means succeed if at end of line, but only in special contexts. - If randomly in the middle of a pattern, it is a normal character. */ - if (p == pend || *p == '\n' - || (obscure_syntax & RE_CONTEXT_INDEP_OPS) - || (obscure_syntax & RE_NO_BK_PARENS - ? *p == ')' - : *p == '\\' && p[1] == ')') - || (obscure_syntax & RE_NO_BK_VBAR - ? *p == '|' - : *p == '\\' && p[1] == '|')) - { - PATPUSH (endline); - break; - } - goto normal_char; - + { + const char *p1 = p; + /* When testing what follows the $, + look past the \-constructs that don't consume anything. */ + if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) + while (p1 != pend) + { + if (*p1 == '\\' && p1 + 1 != pend + && (p1[1] == '<' || p1[1] == '>' + || p1[1] == '`' || p1[1] == '\'' +#ifdef emacs + || p1[1] == '=' +#endif + || p1[1] == 'b' || p1[1] == 'B')) + p1 += 2; + else + break; + } + if (obscure_syntax & RE_TIGHT_VBAR) + { + if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS) && p1 != pend) + goto normal_char; + /* Make operand of last vbar end before this `$'. */ + if (fixup_jump) + store_jump (fixup_jump, jump, b); + fixup_jump = 0; + BUFPUSH (endline); + break; + } + /* $ means succeed if at end of line, but only in special contexts. + If validly in the middle of a pattern, it is a normal character. */ + + if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) && p1 != pend) + goto invalid_pattern; + if (p1 == pend || *p1 == '\n' + || (obscure_syntax & RE_CONTEXT_INDEP_OPS) + || (obscure_syntax & RE_NO_BK_PARENS + ? *p1 == ')' + : *p1 == '\\' && p1[1] == ')') + || (obscure_syntax & RE_NO_BK_VBAR + ? *p1 == '|' + : *p1 == '\\' && p1[1] == '|')) + { + BUFPUSH (endline); + break; + } + goto normal_char; + } case '^': - /* ^ means succeed if at beg of line, but only if no preceding pattern. */ - - if (laststart && p[-2] != '\n' - && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) + /* ^ means succeed if at beg of line, but only if no preceding + pattern. */ + + if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) && laststart) + goto invalid_pattern; + if (laststart && p - 2 >= pattern && p[-2] != '\n' + && !(obscure_syntax & RE_CONTEXT_INDEP_OPS)) goto normal_char; if (obscure_syntax & RE_TIGHT_VBAR) { if (p != pattern + 1 && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) goto normal_char; - PATPUSH (begline); + BUFPUSH (begline); begalt = b; } else - PATPUSH (begline); + BUFPUSH (begline); break; case '+': case '?': - if (obscure_syntax & RE_BK_PLUS_QM) + if ((obscure_syntax & RE_BK_PLUS_QM) + || (obscure_syntax & RE_LIMITED_OPS)) goto normal_char; handle_plus: case '*': /* If there is no previous pattern, char not special. */ - if (!laststart && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) - goto normal_char; + if (!laststart) + { + if (obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) + goto invalid_pattern; + else if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + } /* If there is a sequence of repetition chars, - collapse it down to equivalent to just one. */ + collapse it down to just one. */ zero_times_ok = 0; many_times_ok = 0; while (1) @@ -444,74 +592,201 @@ re_compile_pattern (char *pattern, int size, struct re_pattern_buffer *bufp) /* Star, etc. applied to an empty pattern is equivalent to an empty pattern. */ - if (!laststart) + if (!laststart) break; - /* Now we know whether 0 matches is allowed, - and whether 2 or more matches is allowed. */ + /* Now we know whether or not zero matches is allowed + and also whether or not two or more matches is allowed. */ if (many_times_ok) { - /* If more than one repetition is allowed, - put in a backward jump at the end. */ + /* If more than one repetition is allowed, put in at the + end a backward relative jump from b to before the next + jump we're going to put in below (which jumps from + laststart to after this jump). */ + GET_BUFFER_SPACE (3); store_jump (b, maybe_finalize_jump, laststart - 3); - b += 3; + b += 3; /* Because store_jump put stuff here. */ } + /* On failure, jump from laststart to b + 3, which will be the + end of the buffer after this jump is inserted. */ + GET_BUFFER_SPACE (3); insert_jump (on_failure_jump, laststart, b + 3, b); pending_exact = 0; b += 3; if (!zero_times_ok) { - /* At least one repetition required: insert before the loop - a skip over the initial on-failure-jump instruction */ - insert_jump (dummy_failure_jump, laststart, laststart + 6, b); + /* At least one repetition is required, so insert a + dummy-failure before the initial on-failure-jump + instruction of the loop. This effects a skip over that + instruction the first time we hit that loop. */ + GET_BUFFER_SPACE (6); + insert_jump (dummy_failure_jump, laststart, laststart + 6, b); b += 3; } break; case '.': laststart = b; - PATPUSH (anychar); + BUFPUSH (anychar); break; - case '[': + case '[': + if (p == pend) + goto invalid_pattern; while (b - bufp->buffer > bufp->allocated - 3 - (1 << BYTEWIDTH) / BYTEWIDTH) - /* Note that EXTEND_BUFFER clobbers c */ EXTEND_BUFFER; laststart = b; if (*p == '^') - PATPUSH (charset_not), p++; + { + BUFPUSH (charset_not); + p++; + } else - PATPUSH (charset); + BUFPUSH (charset); p1 = p; - PATPUSH ((1 << BYTEWIDTH) / BYTEWIDTH); + BUFPUSH ((1 << BYTEWIDTH) / BYTEWIDTH); /* Clear the whole map */ - bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH); - /* Read in characters and ranges, setting map bits */ + memset (b, 0, (1 << BYTEWIDTH) / BYTEWIDTH); + + if ((obscure_syntax & RE_HAT_NOT_NEWLINE) && b[-2] == charset_not) + SET_LIST_BIT ('\n'); + + + /* Read in characters and ranges, setting map bits. */ while (1) { - PATFETCH (c); - if (c == ']' && p != p1 + 1) break; - if (*p == '-' && p[1] != ']') + /* Don't translate while fetching, in case it's a range bound. + When we set the bit for the character, we translate it. */ + PATFETCH_RAW (c); + + /* If set, \ escapes characters when inside [...]. */ + if ((obscure_syntax & RE_AWK_CLASS_HACK) && c == '\\') + { + PATFETCH(c1); + SET_LIST_BIT (c1); + continue; + } + if (c == ']') + { + if (p == p1 + 1) + { + /* If this is an empty bracket expression. */ + if ((obscure_syntax & RE_NO_EMPTY_BRACKETS) + && p == pend) + goto invalid_pattern; + } + else + /* Stop if this isn't merely a ] inside a bracket + expression, but rather the end of a bracket + expression. */ + break; + } + /* Get a range. */ + if (p[0] == '-' && p[1] != ']') { - PATFETCH (c1); - PATFETCH (c1); - while (c <= c1) - b[c / BYTEWIDTH] |= 1 << (c % BYTEWIDTH), c++; - } + PATFETCH (c1); + /* Don't translate the range bounds while fetching them. */ + PATFETCH_RAW (c1); + + if ((obscure_syntax & RE_NO_EMPTY_RANGES) && c > c1) + goto invalid_pattern; + + if ((obscure_syntax & RE_NO_HYPHEN_RANGE_END) + && c1 == '-' && *p != ']') + goto invalid_pattern; + + while (c <= c1) + { + /* Translate each char that's in the range. */ + if (translate) + SET_LIST_BIT (translate[c]); + else + SET_LIST_BIT (c); + c++; + } + } + else if ((obscure_syntax & RE_CHAR_CLASSES) + && c == '[' && p[0] == ':') + { + /* Longest valid character class word has six characters. */ + char str[CHAR_CLASS_MAX_LENGTH]; + PATFETCH (c); + c1 = 0; + /* If no ] at end. */ + if (p == pend) + goto invalid_pattern; + while (1) + { + /* Don't translate the ``character class'' characters. */ + PATFETCH_RAW (c); + if (c == ':' || c == ']' || p == pend + || c1 == CHAR_CLASS_MAX_LENGTH) + break; + str[c1++] = c; + } + str[c1] = '\0'; + if (p == pend + || c == ']' /* End of the bracket expression. */ + || p[0] != ']' + || p + 1 == pend + || (strcmp (str, "alpha") != 0 + && strcmp (str, "upper") != 0 + && strcmp (str, "lower") != 0 + && strcmp (str, "digit") != 0 + && strcmp (str, "alnum") != 0 + && strcmp (str, "xdigit") != 0 + && strcmp (str, "space") != 0 + && strcmp (str, "print") != 0 + && strcmp (str, "punct") != 0 + && strcmp (str, "graph") != 0 + && strcmp (str, "cntrl") != 0)) + { + /* Undo the ending character, the letters, and leave + the leading : and [ (but set bits for them). */ + c1++; + while (c1--) + PATUNFETCH; + SET_LIST_BIT ('['); + SET_LIST_BIT (':'); + } + else + { + /* The ] at the end of the character class. */ + PATFETCH (c); + if (c != ']') + goto invalid_pattern; + for (c = 0; c < (1 << BYTEWIDTH); c++) + { + if ((strcmp (str, "alpha") == 0 && isalpha (c)) + || (strcmp (str, "upper") == 0 && isupper (c)) + || (strcmp (str, "lower") == 0 && islower (c)) + || (strcmp (str, "digit") == 0 && isdigit (c)) + || (strcmp (str, "alnum") == 0 && isalnum (c)) + || (strcmp (str, "xdigit") == 0 && isxdigit (c)) + || (strcmp (str, "space") == 0 && isspace (c)) + || (strcmp (str, "print") == 0 && isprint (c)) + || (strcmp (str, "punct") == 0 && ispunct (c)) + || (strcmp (str, "graph") == 0 && isgraph (c)) + || (strcmp (str, "cntrl") == 0 && iscntrl (c))) + SET_LIST_BIT (c); + } + } + } + else if (translate) + SET_LIST_BIT (translate[c]); else - { - b[c / BYTEWIDTH] |= 1 << (c % BYTEWIDTH); - } + SET_LIST_BIT (c); } - /* Discard any bitmap bytes that are all 0 at the end of the map. - Decrement the map-length byte too. */ - while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) - b[-1]--; - b += b[-1]; - break; + + /* Discard any character set/class bitmap bytes that are all + 0 at the end of the map. Decrement the map-length byte too. */ + while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) + b[-1]--; + b += b[-1]; + break; case '(': if (! (obscure_syntax & RE_NO_BK_PARENS)) @@ -525,18 +800,28 @@ re_compile_pattern (char *pattern, int size, struct re_pattern_buffer *bufp) else goto handle_close; - case '\n': + case '\n': if (! (obscure_syntax & RE_NEWLINE_OR)) goto normal_char; else goto handle_bar; case '|': - if (! (obscure_syntax & RE_NO_BK_VBAR)) + if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) + && (! laststart || p == pend)) + goto invalid_pattern; + else if (! (obscure_syntax & RE_NO_BK_VBAR)) goto normal_char; else goto handle_bar; + case '{': + if (! ((obscure_syntax & RE_NO_BK_CURLY_BRACES) + && (obscure_syntax & RE_INTERVALS))) + goto normal_char; + else + goto handle_interval; + case '\\': if (p == pend) goto invalid_pattern; PATFETCH_RAW (c); @@ -547,12 +832,15 @@ re_compile_pattern (char *pattern, int size, struct re_pattern_buffer *bufp) goto normal_backsl; handle_open: if (stackp == stacke) goto nesting_too_deep; + + /* Laststart should point to the start_memory that we are about + to push (unless the pattern has RE_NREGS or more ('s). */ + *stackp++ = b - bufp->buffer; if (regnum < RE_NREGS) { - PATPUSH (start_memory); - PATPUSH (regnum); + BUFPUSH (start_memory); + BUFPUSH (regnum); } - *stackp++ = b - bufp->buffer; *stackp++ = fixup_jump ? fixup_jump - bufp->buffer + 1 : 0; *stackp++ = regnum++; *stackp++ = begalt - bufp->buffer; @@ -571,83 +859,242 @@ re_compile_pattern (char *pattern, int size, struct re_pattern_buffer *bufp) store_jump (fixup_jump, jump, b); if (stackp[-1] < RE_NREGS) { - PATPUSH (stop_memory); - PATPUSH (stackp[-1]); + BUFPUSH (stop_memory); + BUFPUSH (stackp[-1]); } stackp -= 2; - fixup_jump = 0; - if (*stackp) - fixup_jump = *stackp + bufp->buffer - 1; - laststart = *--stackp + bufp->buffer; + fixup_jump = *stackp ? *stackp + bufp->buffer - 1 : 0; + laststart = *--stackp + bufp->buffer; break; case '|': - if (obscure_syntax & RE_NO_BK_VBAR) + if ((obscure_syntax & RE_LIMITED_OPS) + || (obscure_syntax & RE_NO_BK_VBAR)) goto normal_backsl; handle_bar: - insert_jump (on_failure_jump, begalt, b + 6, b); + if (obscure_syntax & RE_LIMITED_OPS) + goto normal_char; + /* Insert before the previous alternative a jump which + jumps to this alternative if the former fails. */ + GET_BUFFER_SPACE (6); + insert_jump (on_failure_jump, begalt, b + 6, b); pending_exact = 0; b += 3; - if (fixup_jump) + /* The alternative before the previous alternative has a + jump after it which gets executed if it gets matched. + Adjust that jump so it will jump to the previous + alternative's analogous jump (put in below, which in + turn will jump to the next (if any) alternative's such + jump, etc.). The last such jump jumps to the correct + final destination. */ + if (fixup_jump) store_jump (fixup_jump, jump, b); - fixup_jump = b; - b += 3; - laststart = 0; + + /* Leave space for a jump after previous alternative---to be + filled in later. */ + fixup_jump = b; + b += 3; + + laststart = 0; begalt = b; break; + case '{': + if (! (obscure_syntax & RE_INTERVALS) + /* Let \{ be a literal. */ + || ((obscure_syntax & RE_INTERVALS) + && (obscure_syntax & RE_NO_BK_CURLY_BRACES)) + /* If it's the string "\{". */ + || (p - 2 == pattern && p == pend)) + goto normal_backsl; + handle_interval: + beg_interval = p - 1; /* The {. */ + /* If there is no previous pattern, this isn't an interval. */ + if (!laststart) + { + if (obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) + goto invalid_pattern; + else + goto normal_backsl; + } + /* It also isn't an interval if not preceded by an re + matching a single character or subexpression, or if + the current type of intervals can't handle back + references and the previous thing is a back reference. */ + if (! (*laststart == anychar + || *laststart == charset + || *laststart == charset_not + || *laststart == start_memory + || (*laststart == exactn && laststart[1] == 1) + || (! (obscure_syntax & RE_NO_BK_REFS) + && *laststart == duplicate))) + { + if (obscure_syntax & RE_NO_BK_CURLY_BRACES) + goto normal_char; + + /* Posix extended syntax is handled in previous + statement; this is for Posix basic syntax. */ + if (obscure_syntax & RE_INTERVALS) + goto invalid_pattern; + + goto normal_backsl; + } + lower_bound = -1; /* So can see if are set. */ + upper_bound = -1; + GET_UNSIGNED_NUMBER (lower_bound); + if (c == ',') + { + GET_UNSIGNED_NUMBER (upper_bound); + if (upper_bound < 0) + upper_bound = RE_DUP_MAX; + } + if (upper_bound < 0) + upper_bound = lower_bound; + if (! (obscure_syntax & RE_NO_BK_CURLY_BRACES)) + { + if (c != '\\') + goto invalid_pattern; + PATFETCH (c); + } + if (c != '}' || lower_bound < 0 || upper_bound > RE_DUP_MAX + || lower_bound > upper_bound + || ((obscure_syntax & RE_NO_BK_CURLY_BRACES) + && p != pend && *p == '{')) + { + if (obscure_syntax & RE_NO_BK_CURLY_BRACES) + goto unfetch_interval; + else + goto invalid_pattern; + } + + /* If upper_bound is zero, don't want to succeed at all; + jump from laststart to b + 3, which will be the end of + the buffer after this jump is inserted. */ + + if (upper_bound == 0) + { + GET_BUFFER_SPACE (3); + insert_jump (jump, laststart, b + 3, b); + b += 3; + } + + /* Otherwise, after lower_bound number of succeeds, jump + to after the jump_n which will be inserted at the end + of the buffer, and insert that jump_n. */ + else + { /* Set to 5 if only one repetition is allowed and + hence no jump_n is inserted at the current end of + the buffer; then only space for the succeed_n is + needed. Otherwise, need space for both the + succeed_n and the jump_n. */ + + unsigned slots_needed = upper_bound == 1 ? 5 : 10; + + GET_BUFFER_SPACE ((int) slots_needed); + /* Initialize the succeed_n to n, even though it will + be set by its attendant set_number_at, because + re_compile_fastmap will need to know it. Jump to + what the end of buffer will be after inserting + this succeed_n and possibly appending a jump_n. */ + insert_jump_n (succeed_n, laststart, b + slots_needed, + b, lower_bound); + b += 5; /* Just increment for the succeed_n here. */ + + /* More than one repetition is allowed, so put in at + the end of the buffer a backward jump from b to the + succeed_n we put in above. By the time we've gotten + to this jump when matching, we'll have matched once + already, so jump back only upper_bound - 1 times. */ + + if (upper_bound > 1) + { + store_jump_n (b, jump_n, laststart, upper_bound - 1); + b += 5; + /* When hit this when matching, reset the + preceding jump_n's n to upper_bound - 1. */ + BUFPUSH (set_number_at); + GET_BUFFER_SPACE (2); + STORE_NUMBER_AND_INCR (b, -5); + STORE_NUMBER_AND_INCR (b, upper_bound - 1); + } + /* When hit this when matching, set the succeed_n's n. */ + GET_BUFFER_SPACE (5); + insert_op_2 (set_number_at, laststart, b, 5, lower_bound); + b += 5; + } + pending_exact = 0; + beg_interval = 0; + break; + + + unfetch_interval: + /* If an invalid interval, match the characters as literals. */ + if (beg_interval) + p = beg_interval; + else + { + fprintf (stderr, + "regex: no interval beginning to which to backtrack.\n"); + exit (1); + } + + beg_interval = 0; + PATFETCH (c); /* normal_char expects char in `c'. */ + goto normal_char; + break; + #ifdef emacs case '=': - PATPUSH (at_dot); + BUFPUSH (at_dot); break; case 's': laststart = b; - PATPUSH (syntaxspec); + BUFPUSH (syntaxspec); PATFETCH (c); - PATPUSH (syntax_spec_code[c]); + BUFPUSH (syntax_spec_code[c]); break; case 'S': laststart = b; - PATPUSH (notsyntaxspec); + BUFPUSH (notsyntaxspec); PATFETCH (c); - PATPUSH (syntax_spec_code[c]); + BUFPUSH (syntax_spec_code[c]); break; #endif /* emacs */ case 'w': laststart = b; - PATPUSH (wordchar); + BUFPUSH (wordchar); break; case 'W': laststart = b; - PATPUSH (notwordchar); + BUFPUSH (notwordchar); break; case '<': - PATPUSH (wordbeg); + BUFPUSH (wordbeg); break; case '>': - PATPUSH (wordend); + BUFPUSH (wordend); break; case 'b': - PATPUSH (wordbound); + BUFPUSH (wordbound); break; case 'B': - PATPUSH (notwordbound); + BUFPUSH (notwordbound); break; case '`': - PATPUSH (begbuf); + BUFPUSH (begbuf); break; case '\'': - PATPUSH (endbuf); + BUFPUSH (endbuf); break; case '1': @@ -659,23 +1106,34 @@ re_compile_pattern (char *pattern, int size, struct re_pattern_buffer *bufp) case '7': case '8': case '9': - c1 = c - '0'; + if (obscure_syntax & RE_NO_BK_REFS) + goto normal_char; + c1 = c - '0'; if (c1 >= regnum) - goto normal_char; - for (stackt = stackp - 2; stackt > stackb; stackt -= 4) + { + if (obscure_syntax & RE_NO_EMPTY_BK_REF) + goto invalid_pattern; + else + goto normal_char; + } + /* Can't back reference to a subexpression if inside of it. */ + for (stackt = stackp - 2; stackt > stackb; stackt -= 4) if (*stackt == c1) goto normal_char; laststart = b; - PATPUSH (duplicate); - PATPUSH (c1); + BUFPUSH (duplicate); + BUFPUSH (c1); break; case '+': case '?': if (obscure_syntax & RE_BK_PLUS_QM) goto handle_plus; + else + goto normal_backsl; + break; - default: + default: normal_backsl: /* You might think it would be useful for \ to mean not to translate; but if we don't translate it @@ -686,19 +1144,23 @@ re_compile_pattern (char *pattern, int size, struct re_pattern_buffer *bufp) break; default: - normal_char: + normal_char: /* Expects the character in `c'. */ if (!pending_exact || pending_exact + *pending_exact + 1 != b || *pending_exact == 0177 || *p == '*' || *p == '^' || ((obscure_syntax & RE_BK_PLUS_QM) ? *p == '\\' && (p[1] == '+' || p[1] == '?') - : (*p == '+' || *p == '?'))) + : (*p == '+' || *p == '?')) + || ((obscure_syntax & RE_INTERVALS) + && ((obscure_syntax & RE_NO_BK_CURLY_BRACES) + ? *p == '{' + : (p[0] == '\\' && p[1] == '{')))) { laststart = b; - PATPUSH (exactn); + BUFPUSH (exactn); pending_exact = b; - PATPUSH (0); + BUFPUSH (0); } - PATPUSH (c); + BUFPUSH (c); (*pending_exact)++; } } @@ -733,42 +1195,104 @@ re_compile_pattern (char *pattern, int size, struct re_pattern_buffer *bufp) return "Memory exhausted"; } -/* Store where `from' points a jump operation to jump to where `to' points. - `opcode' is the opcode to store. */ + +/* Store a jump of the form . + Store in the location FROM a jump operation to jump to relative + address FROM - TO. OPCODE is the opcode to store. */ static void store_jump (char *from, char opcode, char *to) { from[0] = opcode; - from[1] = (to - (from + 3)) & 0377; - from[2] = (to - (from + 3)) >> 8; + STORE_NUMBER(from + 1, to - (from + 3)); } -/* Open up space at char FROM, and insert there a jump to TO. - CURRENT_END gives te end of the storage no in use, - so we know how much data to copy up. - OP is the opcode of the jump to insert. + +/* Open up space before char FROM, and insert there a jump to TO. + CURRENT_END gives the end of the storage not in use, so we know + how much data to copy up. OP is the opcode of the jump to insert. If you call this function, you must zero out pending_exact. */ static void insert_jump (char op, char *from, char *to, char *current_end) { - register char *pto = current_end + 3; - register char *pfrom = current_end; - while (pfrom != from) + register char *pfrom = current_end; /* Copy from here... */ + register char *pto = current_end + 3; /* ...to here. */ + + while (pfrom != from) *--pto = *--pfrom; store_jump (from, op, to); } + + +/* Store a jump of the form . + + Store in the location FROM a jump operation to jump to relative + address FROM - TO. OPCODE is the opcode to store, N is a number the + jump uses, say, to decide how many times to jump. + + If you call this function, you must zero out pending_exact. */ + +static void +store_jump_n (char *from, char opcode, char *to, unsigned n) +{ + from[0] = opcode; + STORE_NUMBER (from + 1, to - (from + 3)); + STORE_NUMBER (from + 3, n); +} + + +/* Similar to insert_jump, but handles a jump which needs an extra + number to handle minimum and maximum cases. Open up space at + location FROM, and insert there a jump to TO. CURRENT_END gives the + end of the storage in use, so we know how much data to copy up. OP is + the opcode of the jump to insert. + + If you call this function, you must zero out pending_exact. */ + +static void +insert_jump_n (char op, char *from, char *to, char *current_end, unsigned n) +{ + register char *pfrom = current_end; /* Copy from here... */ + register char *pto = current_end + 5; /* ...to here. */ + + while (pfrom != from) + *--pto = *--pfrom; + store_jump_n (from, op, to, n); +} + + +/* Open up space at location THERE, and insert operation OP followed by + NUM_1 and NUM_2. CURRENT_END gives the end of the storage in use, so + we know how much data to copy up. + + If you call this function, you must zero out pending_exact. */ + +static void +insert_op_2 (char op, char *there, char *current_end, int num_1, int num_2) +{ + register char *pfrom = current_end; /* Copy from here... */ + register char *pto = current_end + 5; /* ...to here. */ + + while (pfrom != there) + *--pto = *--pfrom; + + there[0] = op; + STORE_NUMBER (there + 1, num_1); + STORE_NUMBER (there + 3, num_2); +} + + -/* Given a pattern, compute a fastmap from it. - The fastmap records which of the (1 << BYTEWIDTH) possible characters - can start a string that matches the pattern. - This fastmap is used by re_search to skip quickly over totally implausible text. +/* Given a pattern, compute a fastmap from it. The fastmap records + which of the (1 << BYTEWIDTH) possible characters can start a string + that matches the pattern. This fastmap is used by re_search to skip + quickly over totally implausible text. - The caller must supply the address of a (1 << BYTEWIDTH)-byte data area - as bufp->fastmap. - The other components of bufp describe the pattern to be used. */ + The caller must supply the address of a (1 << BYTEWIDTH)-byte data + area as bufp->fastmap. + The other components of bufp describe the pattern to be used. */ void re_compile_fastmap (struct re_pattern_buffer *bufp) @@ -778,18 +1302,21 @@ re_compile_fastmap (struct re_pattern_buffer *bufp) register char *fastmap = bufp->fastmap; register unsigned char *p = pattern; register unsigned char *pend = pattern + size; - register int j; + register int j, k; unsigned char *translate = (unsigned char *) bufp->translate; unsigned char *stackb[NFAILURES]; unsigned char **stackp = stackb; - bzero (fastmap, (1 << BYTEWIDTH)); + unsigned is_a_succeed_n; + + memset (fastmap, 0, (1 << BYTEWIDTH)); bufp->fastmap_accurate = 1; bufp->can_be_null = 0; while (p) { + is_a_succeed_n = 0; if (p == pend) { bufp->can_be_null = 1; @@ -808,61 +1335,83 @@ re_compile_fastmap (struct re_pattern_buffer *bufp) fastmap[p[1]] = 1; break; + case unused: case begline: +#ifdef emacs case before_dot: case at_dot: case after_dot: +#endif case begbuf: case endbuf: case wordbound: case notwordbound: case wordbeg: case wordend: - continue; + continue; case endline: if (translate) fastmap[translate['\n']] = 1; else fastmap['\n'] = 1; + if (bufp->can_be_null != 1) bufp->can_be_null = 2; break; - case finalize_jump: + case jump_n: + case finalize_jump: case maybe_finalize_jump: case jump: case dummy_failure_jump: - bufp->can_be_null = 1; - j = *p++ & 0377; - j += SIGN_EXTEND_CHAR (*(char *)p) << 8; - p += j + 1; /* The 1 compensates for missing ++ above */ + EXTRACT_NUMBER_AND_INCR (j, p); + p += j; if (j > 0) continue; - /* Jump backward reached implies we just went through + /* Jump backward reached implies we just went through the body of a loop and matched nothing. Opcode jumped to should be an on_failure_jump. Just treat it like an ordinary jump. For a * loop, it has pushed its failure point already; - if so, discard that as redundant. */ - if ((enum regexpcode) *p != on_failure_jump) + If so, discard that as redundant. */ + + if ((enum regexpcode) *p != on_failure_jump + && (enum regexpcode) *p != succeed_n) continue; - p++; - j = *p++ & 0377; - j += SIGN_EXTEND_CHAR (*(char *)p) << 8; - p += j + 1; /* The 1 compensates for missing ++ above */ - if (stackp != stackb && *stackp == p) - stackp--; - continue; + p++; + EXTRACT_NUMBER_AND_INCR (j, p); + p += j; + if (stackp != stackb && *stackp == p) + stackp--; + continue; - case on_failure_jump: - j = *p++ & 0377; - j += SIGN_EXTEND_CHAR (*(char *)p) << 8; - p++; - *++stackp = p + j; + case on_failure_jump: + handle_on_failure_jump: + EXTRACT_NUMBER_AND_INCR (j, p); + *++stackp = p + j; + if (is_a_succeed_n) + EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */ continue; - case start_memory: + case succeed_n: + is_a_succeed_n = 1; + /* Get to the number of times to succeed. */ + p += 2; + /* Increment p past the n for when k != 0. */ + EXTRACT_NUMBER_AND_INCR (k, p); + if (k == 0) + { + p -= 4; + goto handle_on_failure_jump; + } + continue; + + case set_number_at: + p += 4; + continue; + + case start_memory: case stop_memory: p++; continue; @@ -906,7 +1455,7 @@ re_compile_fastmap (struct re_pattern_buffer *bufp) if (SYNTAX (j) != (enum syntaxcode) k) fastmap[j] = 1; break; -#endif /* emacs */ +#endif /* not emacs */ case charset: for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) @@ -938,53 +1487,82 @@ re_compile_fastmap (struct re_pattern_buffer *bufp) break; } - /* Get here means we have successfully found the possible starting characters - of one path of the pattern. We need not follow this path any farther. - Instead, look at the next alternative remembered in the stack. */ - if (stackp != stackb) + /* Get here means we have successfully found the possible starting + characters of one path of the pattern. We need not follow this + path any farther. Instead, look at the next alternative + remembered in the stack. */ + if (stackp != stackb) p = *stackp--; else break; } } + + -/* Like re_search_2, below, but only one string is specified. */ +/* Like re_search_2, below, but only one string is specified, and + doesn't let you say where to stop matching. */ int -re_search (struct re_pattern_buffer *pbufp, char *string, int size, - int startpos, int range, struct re_registers *regs) +re_search (struct re_pattern_buffer *pbufp, + char *string, + int size, + int startpos, + int range, + struct re_registers *regs) { - return re_search_2 (pbufp, 0, 0, string, size, startpos, range, regs, size); + return re_search_2 (pbufp, (char *) 0, 0, string, size, startpos, range, + regs, size); } -/* Like re_match_2 but tries first a match starting at index STARTPOS, - then at STARTPOS + 1, and so on. - RANGE is the number of places to try before giving up. - If RANGE is negative, the starting positions tried are - STARTPOS, STARTPOS - 1, etc. - It is up to the caller to make sure that range is not so large - as to take the starting position outside of the input strings. -The value returned is the position at which the match was found, - or -1 if no match was found, - or -2 if error (such as failure stack overflow). */ +/* Using the compiled pattern in PBUFP->buffer, first tries to match the + virtual concatenation of STRING1 and STRING2, starting first at index + STARTPOS, then at STARTPOS + 1, and so on. RANGE is the number of + places to try before giving up. If RANGE is negative, it searches + backwards, i.e., the starting positions tried are STARTPOS, STARTPOS + - 1, etc. STRING1 and STRING2 are of SIZE1 and SIZE2, respectively. + In REGS, return the indices of the virtual concatenation of STRING1 + and STRING2 that matched the entire PBUFP->buffer and its contained + subexpressions. Do not consider matching one past the index MSTOP in + the virtual concatenation of STRING1 and STRING2. + + The value returned is the position in the strings at which the match + was found, or -1 if no match was found, or -2 if error (such as + failure stack overflow). */ int -re_search_2 (struct re_pattern_buffer *pbufp, char *string1, int size1, char *string2, - int size2, int startpos, register int range, struct re_registers *regs, - int mstop) +re_search_2 (struct re_pattern_buffer *pbufp, + char *string1, int size1, + char *string2, int size2, + int startpos, + register int range, + struct re_registers *regs, + int mstop) { register char *fastmap = pbufp->fastmap; register unsigned char *translate = (unsigned char *) pbufp->translate; - int total = size1 + size2; + int total_size = size1 + size2; + int endpos = startpos + range; int val; - /* Update the fastmap now if not correct already */ + /* Check for out-of-range starting position. */ + if (startpos < 0 || startpos > total_size) + return -1; + + /* Fix up range if it would eventually take startpos outside of the + virtual concatenation of string1 and string2. */ + if (endpos < -1) + range = -1 - startpos; + else if (endpos > total_size) + range = total_size - startpos; + + /* Update the fastmap now if not correct already. */ if (fastmap && !pbufp->fastmap_accurate) re_compile_fastmap (pbufp); - /* Don't waste time in a long search for a pattern - that says it is anchored. */ + /* If the search isn't to be a backwards one, don't waste time in a + long search for a pattern that says it is anchored. */ if (pbufp->used > 0 && (enum regexpcode) pbufp->buffer[0] == begbuf && range > 0) { @@ -995,16 +1573,16 @@ re_search_2 (struct re_pattern_buffer *pbufp, char *string1, int size1, char *st } while (1) - { - /* If a fastmap is supplied, skip quickly over characters - that cannot possibly be the start of a match. - Note, however, that if the pattern can possibly match - the null string, we must test it at each starting point - so that we take the first null string we get. */ - - if (fastmap && startpos < total && pbufp->can_be_null != 1) + { + /* If a fastmap is supplied, skip quickly over characters that + cannot possibly be the start of a match. Note, however, that + if the pattern can possibly match the null string, we must + test it at each starting point so that we take the first null + string we get. */ + + if (fastmap && startpos < total_size && pbufp->can_be_null != 1) { - if (range > 0) + if (range > 0) /* Searching forwards. */ { register int lim = 0; register unsigned char *p; @@ -1015,132 +1593,342 @@ re_search_2 (struct re_pattern_buffer *pbufp, char *string1, int size1, char *st p = ((unsigned char *) &(startpos >= size1 ? string2 - size1 : string1)[startpos]); - if (translate) - { - while (range > lim && !fastmap[translate[*p++]]) + while (range > lim && !fastmap[translate + ? translate[*p++] + : *p++]) range--; - } - else - { - while (range > lim && !fastmap[*p++]) - range--; - } startpos += irange - range; } - else + else /* Searching backwards. */ { register unsigned char c; - if (startpos >= size1) + + if (string1 == 0 || startpos >= size1) c = string2[startpos - size1]; - else + else c = string1[startpos]; - c &= 0xff; + + c &= 0xff; if (translate ? !fastmap[translate[c]] : !fastmap[c]) goto advance; } } - if (range >= 0 && startpos == total + if (range >= 0 && startpos == total_size && fastmap && pbufp->can_be_null == 0) return -1; - val = re_match_2 (pbufp, (unsigned char *) string1, size1, (unsigned char *) string2, size2, startpos, regs, mstop); - if (0 <= val) - { - if (val == -2) - return -2; - return startpos; - } + val = re_match_2 (pbufp, string1, size1, string2, size2, startpos, + regs, mstop); + if (val >= 0) + return startpos; + if (val == -2) + return -2; #ifdef C_ALLOCA alloca (0); #endif /* C_ALLOCA */ advance: - if (!range) break; - if (range > 0) range--, startpos++; else range++, startpos--; + if (!range) + break; + else if (range > 0) + { + range--; + startpos++; + } + else + { + range++; + startpos--; + } } return -1; } + + -#ifndef emacs /* emacs never uses this */ +#ifndef emacs /* emacs never uses this. */ int -re_match (struct re_pattern_buffer *pbufp, char *string, int size, int pos, - struct re_registers *regs) +re_match (struct re_pattern_buffer *pbufp, + char *string, + int size, + int pos, + struct re_registers *regs) { - return re_match_2 (pbufp, (unsigned char *) 0, 0, (unsigned char *) string, size, pos, regs, size); + return re_match_2 (pbufp, (char *) 0, 0, string, size, pos, regs, size); } -#endif /* emacs */ +#endif /* not emacs */ + -/* Maximum size of failure stack. Beyond this, overflow is an error. */ +/* The following are used for re_match_2, defined below: */ +/* Roughly the maximum number of failure points on the stack. Would be + exactly that if always pushed MAX_NUM_FAILURE_ITEMS each time we failed. */ + int re_max_failures = 2000; -static int bcmp_translate(unsigned char *, unsigned char *, int, unsigned char *); +/* Routine used by re_match_2. */ +static int bcmp_translate (char *, char *, int, unsigned char *); + + +/* Structure and accessing macros used in re_match_2: */ + +struct register_info +{ + unsigned is_active : 1; + unsigned matched_something : 1; +}; + +#define IS_ACTIVE(R) ((R).is_active) +#define MATCHED_SOMETHING(R) ((R).matched_something) + + +/* Macros used by re_match_2: */ + + +/* I.e., regstart, regend, and reg_info. */ + +#define NUM_REG_ITEMS 3 + +/* We push at most this many things on the stack whenever we + fail. The `+ 2' refers to PATTERN_PLACE and STRING_PLACE, which are + arguments to the PUSH_FAILURE_POINT macro. */ + +#define MAX_NUM_FAILURE_ITEMS (RE_NREGS * NUM_REG_ITEMS + 2) + + +/* We push this many things on the stack whenever we fail. */ + +#define NUM_FAILURE_ITEMS (last_used_reg * NUM_REG_ITEMS + 2) + + +/* This pushes most of the information about the current state we will want + if we ever fail back to it. */ + +#define PUSH_FAILURE_POINT(pattern_place, string_place) \ + { \ + short last_used_reg, this_reg; \ + \ + /* Find out how many registers are active or have been matched. \ + (Aside from register zero, which is only set at the end.) */ \ + for (last_used_reg = RE_NREGS - 1; last_used_reg > 0; last_used_reg--)\ + if (regstart[last_used_reg] != (unsigned char *) -1) \ + break; \ + \ + if (stacke - stackp < NUM_FAILURE_ITEMS) \ + { \ + unsigned char **stackx; \ + int len = stacke - stackb; \ + if (len > re_max_failures * MAX_NUM_FAILURE_ITEMS) \ + return -2; \ + \ + /* Roughly double the size of the stack. */ \ + stackx = (unsigned char **) alloca (2 * len \ + * sizeof (unsigned char *));\ + /* Only copy what is in use. */ \ + memcpy (stackx, stackb, len * sizeof (char *)); \ + stackp = stackx + (stackp - stackb); \ + stackb = stackx; \ + stacke = stackb + 2 * len; \ + } \ + \ + /* Now push the info for each of those registers. */ \ + for (this_reg = 1; this_reg <= last_used_reg; this_reg++) \ + { \ + *stackp++ = regstart[this_reg]; \ + *stackp++ = regend[this_reg]; \ + *stackp++ = (unsigned char *) ®_info[this_reg]; \ + } \ + \ + /* Push how many registers we saved. */ \ + *stackp++ = (unsigned char *) last_used_reg; \ + \ + *stackp++ = pattern_place; \ + *stackp++ = string_place; \ + } + + +/* This pops what PUSH_FAILURE_POINT pushes. */ + +#define POP_FAILURE_POINT() \ + { \ + int temp; \ + stackp -= 2; /* Remove failure points. */ \ + temp = (int) *--stackp; /* How many regs pushed. */ \ + temp *= NUM_REG_ITEMS; /* How much to take off the stack. */ \ + stackp -= temp; /* Remove the register info. */ \ + } -/* Match the pattern described by PBUFP - against data which is the virtual concatenation of STRING1 and STRING2. - SIZE1 and SIZE2 are the sizes of the two data strings. - Start the match at position POS. - Do not consider matching past the position MSTOP. + +#define MATCHING_IN_FIRST_STRING (dend == end_match_1) + +/* Is true if there is a first string and if PTR is pointing anywhere + inside it or just past the end. */ + +#define IS_IN_FIRST_STRING(ptr) \ + (size1 && string1 <= (ptr) && (ptr) <= string1 + size1) + +/* Call before fetching a character with *d. This switches over to + string2 if necessary. */ + +#define PREFETCH \ + while (d == dend) \ + { \ + /* end of string2 => fail. */ \ + if (dend == end_match_2) \ + goto fail; \ + /* end of string1 => advance to string2. */ \ + d = string2; \ + dend = end_match_2; \ + } + + +/* Call this when have matched something; it sets `matched' flags for the + registers corresponding to the subexpressions of which we currently + are inside. */ +#define SET_REGS_MATCHED \ + { unsigned this_reg; \ + for (this_reg = 0; this_reg < RE_NREGS; this_reg++) \ + { \ + if (IS_ACTIVE(reg_info[this_reg])) \ + MATCHED_SOMETHING(reg_info[this_reg]) = 1; \ + else \ + MATCHED_SOMETHING(reg_info[this_reg]) = 0; \ + } \ + } + +/* Test if at very beginning or at very end of the virtual concatenation + of string1 and string2. If there is only one string, we've put it in + string2. */ + +#define AT_STRINGS_BEG (d == (size1 ? string1 : string2) || !size2) +#define AT_STRINGS_END (d == end2) + +#define AT_WORD_BOUNDARY \ + (AT_STRINGS_BEG || AT_STRINGS_END || IS_A_LETTER (d - 1) != IS_A_LETTER (d)) + +/* We have two special cases to check for: + 1) if we're past the end of string1, we have to look at the first + character in string2; + 2) if we're before the beginning of string2, we have to look at the + last character in string1; we assume there is a string1, so use + this in conjunction with AT_STRINGS_BEG. */ +#define IS_A_LETTER(d) \ + (SYNTAX ((d) == end1 ? *string2 : (d) == string2 - 1 ? *(end1 - 1) : *(d))\ + == Sword) + + +/* Match the pattern described by PBUFP against the virtual + concatenation of STRING1 and STRING2, which are of SIZE1 and SIZE2, + respectively. Start the match at index POS in the virtual + concatenation of STRING1 and STRING2. In REGS, return the indices of + the virtual concatenation of STRING1 and STRING2 that matched the + entire PBUFP->buffer and its contained subexpressions. Do not + consider matching one past the index MSTOP in the virtual + concatenation of STRING1 and STRING2. If pbufp->fastmap is nonzero, then it had better be up to date. The reason that the data to match are specified as two components - which are to be regarded as concatenated - is so this function can be used directly on the contents of an Emacs buffer. + which are to be regarded as concatenated is so this function can be + used directly on the contents of an Emacs buffer. - -1 is returned if there is no match. -2 is returned if there is - an error (such as match stack overflow). Otherwise the value is the length - of the substring which was matched. */ + -1 is returned if there is no match. -2 is returned if there is an + error (such as match stack overflow). Otherwise the value is the + length of the substring which was matched. */ int -re_match_2 (struct re_pattern_buffer *pbufp, unsigned char *string1, int size1, - unsigned char *string2, int size2, int pos, struct re_registers *regs, - int mstop) +re_match_2 (struct re_pattern_buffer *pbufp, + char *string1_arg, int size1, + char *string2_arg, int size2, + int pos, + struct re_registers *regs, + int mstop) { register unsigned char *p = (unsigned char *) pbufp->buffer; + + /* Pointer to beyond end of buffer. */ register unsigned char *pend = p + pbufp->used; - /* End of first string */ - unsigned char *end1; - /* End of second string */ - unsigned char *end2; - /* Pointer just past last char to consider matching */ + + unsigned char *string1 = (unsigned char *) string1_arg; + unsigned char *string2 = (unsigned char *) string2_arg; + unsigned char *end1; /* Just past end of first string. */ + unsigned char *end2; /* Just past end of second string. */ + + /* Pointers into string1 and string2, just past the last characters in + each to consider matching. */ unsigned char *end_match_1, *end_match_2; + register unsigned char *d, *dend; - register int mcnt; + register int mcnt; /* Multipurpose. */ unsigned char *translate = (unsigned char *) pbufp->translate; - - /* Failure point stack. Each place that can handle a failure further down the line - pushes a failure point on this stack. It consists of two char *'s. - The first one pushed is where to resume scanning the pattern; - the second pushed is where to resume scanning the strings. - If the latter is zero, the failure point is a "dummy". - If a failure happens and the innermost failure point is dormant, - it discards that failure point and tries the next one. */ - - unsigned char *initial_stack[2 * NFAILURES]; + unsigned is_a_jump_n = 0; + + /* Failure point stack. Each place that can handle a failure further + down the line pushes a failure point on this stack. It consists of + restart, regend, and reg_info for all registers corresponding to the + subexpressions we're currently inside, plus the number of such + registers, and, finally, two char *'s. The first char * is where to + resume scanning the pattern; the second one is where to resume + scanning the strings. If the latter is zero, the failure point is a + ``dummy''; if a failure happens and the failure point is a dummy, it + gets discarded and the next next one is tried. */ + + unsigned char *initial_stack[MAX_NUM_FAILURE_ITEMS * NFAILURES]; unsigned char **stackb = initial_stack; - unsigned char **stackp = stackb, **stacke = &stackb[2 * NFAILURES]; - - /* Information on the "contents" of registers. - These are pointers into the input strings; they record - just what was matched (on this attempt) by some part of the pattern. - The start_memory command stores the start of a register's contents - and the stop_memory command stores the end. + unsigned char **stackp = stackb; + unsigned char **stacke = &stackb[MAX_NUM_FAILURE_ITEMS * NFAILURES]; - At that point, regstart[regnum] points to the first character in the register, - regend[regnum] points to the first character beyond the end of the register, - regstart_seg1[regnum] is true iff regstart[regnum] points into string1, - and regend_seg1[regnum] is true iff regend[regnum] points into string1. */ + /* Information on the contents of registers. These are pointers into + the input strings; they record just what was matched (on this + attempt) by a subexpression part of the pattern, that is, the + regnum-th regstart pointer points to where in the pattern we began + matching and the regnum-th regend points to right after where we + stopped matching the regnum-th subexpression. (The zeroth register + keeps track of what the whole pattern matches.) */ + unsigned char *regstart[RE_NREGS]; unsigned char *regend[RE_NREGS]; - unsigned char regstart_seg1[RE_NREGS], regend_seg1[RE_NREGS]; + + /* The is_active field of reg_info helps us keep track of which (possibly + nested) subexpressions we are currently in. The matched_something + field of reg_info[reg_num] helps us tell whether or not we have + matched any of the pattern so far this time through the reg_num-th + subexpression. These two fields get reset each time through any + loop their register is in. */ + + struct register_info reg_info[RE_NREGS]; + + + /* The following record the register info as found in the above + variables when we find a match better than any we've seen before. + This happens as we backtrack through the failure points, which in + turn happens only if we have not yet matched the entire string. */ + + unsigned best_regs_set = 0; + unsigned char *best_regstart[RE_NREGS]; + unsigned char *best_regend[RE_NREGS]; + + /* Initialize subexpression text positions to -1 to mark ones that no + \( or ( and \) or ) has been seen for. Also set all registers to + inactive and mark them as not having matched anything or ever + failed. */ + for (mcnt = 0; mcnt < RE_NREGS; mcnt++) + { + regstart[mcnt] = regend[mcnt] = (unsigned char *) -1; + IS_ACTIVE (reg_info[mcnt]) = 0; + MATCHED_SOMETHING (reg_info[mcnt]) = 0; + } + + if (regs) + for (mcnt = 0; mcnt < RE_NREGS; mcnt++) + regs->start[mcnt] = regs->end[mcnt] = -1; /* Set up pointers to ends of strings. Don't allow the second string to be empty unless both are empty. */ - if (!size2) + if (size2 == 0) { string2 = string1; size2 = size1; @@ -1150,7 +1938,7 @@ re_match_2 (struct re_pattern_buffer *pbufp, unsigned char *string1, int size1, end1 = string1 + size1; end2 = string2 + size2; - /* Compute where to stop matching, within the two strings */ + /* Compute where to stop matching, within the two strings. */ if (mstop <= size1) { end_match_1 = string1 + mstop; @@ -1162,50 +1950,81 @@ re_match_2 (struct re_pattern_buffer *pbufp, unsigned char *string1, int size1, end_match_2 = string2 + mstop - size1; } - /* Initialize \) text positions to -1 - to mark ones that no \( or \) has been seen for. */ - - for (mcnt = 0; mcnt < sizeof (regend) / sizeof (*regend); mcnt++) - regend[mcnt] = (unsigned char *) -1; + /* `p' scans through the pattern as `d' scans through the data. `dend' + is the end of the input string that `d' points within. `d' is + advanced into the following input string whenever necessary, but + this happens before fetching; therefore, at the beginning of the + loop, `d' can be pointing at the end of a string, but it cannot + equal string2. */ - /* `p' scans through the pattern as `d' scans through the data. - `dend' is the end of the input string that `d' points within. - `d' is advanced into the following input string whenever necessary, - but this happens before fetching; - therefore, at the beginning of the loop, - `d' can be pointing at the end of a string, - but it cannot equal string2. */ - - if (pos <= size1) + if (size1 != 0 && pos <= size1) d = string1 + pos, dend = end_match_1; else d = string2 + pos - size1, dend = end_match_2; -/* Write PREFETCH; just before fetching a character with *d. */ -#define PREFETCH \ - while (d == dend) \ - { if (dend == end_match_2) goto fail; /* end of string2 => failure */ \ - d = string2; /* end of string1 => advance to string2. */ \ - dend = end_match_2; } - /* This loop loops over pattern commands. - It exits by returning from the function if match is complete, - or it drops through if match fails at this starting point in the input data. */ + /* This loops over pattern commands. It exits by returning from the + function if match is complete, or it drops through if match fails + at this starting point in the input data. */ while (1) { + is_a_jump_n = 0; + /* End of pattern means we might have succeeded. */ if (p == pend) - /* End of pattern means we have succeeded! */ { - /* If caller wants register contents data back, convert it to indices */ + /* If not end of string, try backtracking. Otherwise done. */ + if (d != end_match_2) + { + if (stackp != stackb) + { + /* More failure points to try. */ + + unsigned in_same_string = + IS_IN_FIRST_STRING (best_regend[0]) + == MATCHING_IN_FIRST_STRING; + + /* If exceeds best match so far, save it. */ + if (! best_regs_set + || (in_same_string && d > best_regend[0]) + || (! in_same_string && ! MATCHING_IN_FIRST_STRING)) + { + best_regs_set = 1; + best_regend[0] = d; /* Never use regstart[0]. */ + + for (mcnt = 1; mcnt < RE_NREGS; mcnt++) + { + best_regstart[mcnt] = regstart[mcnt]; + best_regend[mcnt] = regend[mcnt]; + } + } + goto fail; + } + /* If no failure points, don't restore garbage. */ + else if (best_regs_set) + { + restore_best_regs: + /* Restore best match. */ + d = best_regend[0]; + + for (mcnt = 0; mcnt < RE_NREGS; mcnt++) + { + regstart[mcnt] = best_regstart[mcnt]; + regend[mcnt] = best_regend[mcnt]; + } + } + } + + /* If caller wants register contents data back, convert it + to indices. */ if (regs) { - regs->start[0] = pos; - if (dend == end_match_1) - regs->end[0] = d - string1; - else - regs->end[0] = d - string2 + size1; - for (mcnt = 1; mcnt < RE_NREGS; mcnt++) + regs->start[0] = pos; + if (MATCHING_IN_FIRST_STRING) + regs->end[0] = d - string1; + else + regs->end[0] = d - string2 + size1; + for (mcnt = 1; mcnt < RE_NREGS; mcnt++) { if (regend[mcnt] == (unsigned char *) -1) { @@ -1213,23 +2032,23 @@ re_match_2 (struct re_pattern_buffer *pbufp, unsigned char *string1, int size1, regs->end[mcnt] = -1; continue; } - if (regstart_seg1[mcnt]) + if (IS_IN_FIRST_STRING (regstart[mcnt])) regs->start[mcnt] = regstart[mcnt] - string1; else regs->start[mcnt] = regstart[mcnt] - string2 + size1; - if (regend_seg1[mcnt]) + + if (IS_IN_FIRST_STRING (regend[mcnt])) regs->end[mcnt] = regend[mcnt] - string1; else regs->end[mcnt] = regend[mcnt] - string2 + size1; } } - if (dend == end_match_1) - return (d - string1 - pos); - else - return d - string2 + size1 - pos; - } + return d - pos - (MATCHING_IN_FIRST_STRING + ? string1 + : string2 - size1); + } - /* Otherwise match next pattern command */ + /* Otherwise match next pattern command. */ #ifdef SWITCH_ENUM_BUG switch ((int) ((enum regexpcode) *p++)) #else @@ -1237,33 +2056,80 @@ re_match_2 (struct re_pattern_buffer *pbufp, unsigned char *string1, int size1, #endif { - /* \( is represented by a start_memory, \) by a stop_memory. - Both of those commands contain a "register number" argument. - The text matched within the \( and \) is recorded under that number. - Then, \ turns into a `duplicate' command which - is followed by the numeric value of as the register number. */ - + /* \( [or `(', as appropriate] is represented by start_memory, + \) by stop_memory. Both of those commands are followed by + a register number in the next byte. The text matched + within the \( and \) is recorded under that number. */ case start_memory: - regstart[*p] = d; - regstart_seg1[*p++] = (dend == end_match_1); - break; + regstart[*p] = d; + IS_ACTIVE (reg_info[*p]) = 1; + MATCHED_SOMETHING (reg_info[*p]) = 0; + p++; + break; case stop_memory: - regend[*p] = d; - regend_seg1[*p++] = (dend == end_match_1); - break; - - case duplicate: + regend[*p] = d; + IS_ACTIVE (reg_info[*p]) = 0; + + /* If just failed to match something this time around with a sub- + expression that's in a loop, try to force exit from the loop. */ + if ((! MATCHED_SOMETHING (reg_info[*p]) + || (enum regexpcode) p[-3] == start_memory) + && (p + 1) != pend) + { + register unsigned char *p2 = p + 1; + mcnt = 0; + switch (*p2++) + { + case jump_n: + is_a_jump_n = 1; + case finalize_jump: + case maybe_finalize_jump: + case jump: + case dummy_failure_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p2); + if (is_a_jump_n) + p2 += 2; + break; + } + p2 += mcnt; + + /* If the next operation is a jump backwards in the pattern + to an on_failure_jump, exit from the loop by forcing a + failure after pushing on the stack the on_failure_jump's + jump in the pattern, and d. */ + if (mcnt < 0 && (enum regexpcode) *p2++ == on_failure_jump) + { + EXTRACT_NUMBER_AND_INCR (mcnt, p2); + PUSH_FAILURE_POINT (p2 + mcnt, d); + goto fail; + } + } + p++; + break; + + /* \ has been turned into a `duplicate' command which is + followed by the numeric value of as the register number. */ + case duplicate: { int regno = *p++; /* Get which register to match against */ register unsigned char *d2, *dend2; - d2 = regstart[regno]; - dend2 = ((regstart_seg1[regno] == regend_seg1[regno]) + /* Where in input to try to start matching. */ + d2 = regstart[regno]; + + /* Where to stop matching; if both the place to start and + the place to stop matching are in the same string, then + set to the place to stop, otherwise, for now have to use + the end of the first string. */ + + dend2 = ((IS_IN_FIRST_STRING (regstart[regno]) + == IS_IN_FIRST_STRING (regend[regno])) ? regend[regno] : end_match_1); while (1) { - /* Advance to next segment in register contents, if necessary */ + /* If necessary, advance to next segment in register + contents. */ while (d2 == dend2) { if (dend2 == end_match_2) break; @@ -1273,15 +2139,22 @@ re_match_2 (struct re_pattern_buffer *pbufp, unsigned char *string1, int size1, /* At end of register contents => success */ if (d2 == dend2) break; - /* Advance to next segment in data being matched, if necessary */ + /* If necessary, advance to next segment in data. */ PREFETCH; - /* mcnt gets # consecutive chars to compare */ + /* How many characters left in this segment to match. */ mcnt = dend - d; - if (mcnt > dend2 - d2) + + /* Want how many consecutive characters we can match in + one shot, so, if necessary, adjust the count. */ + if (mcnt > dend2 - d2) mcnt = dend2 - d2; - /* Compare that many; failure if mismatch, else skip them. */ - if (translate ? bcmp_translate (d, d2, mcnt, translate) : bcmp (d, d2, mcnt)) + + /* Compare that many; failure if mismatch, else move + past them. */ + if (translate + ? bcmp_translate ((char*)d, (char*)d2, mcnt, translate) + : memcmp (d, d2, mcnt)) goto fail; d += mcnt, d2 += mcnt; } @@ -1289,27 +2162,28 @@ re_match_2 (struct re_pattern_buffer *pbufp, unsigned char *string1, int size1, break; case anychar: - /* fetch a data character */ - PREFETCH; - /* Match anything but a newline. */ - if ((translate ? translate[*d++] : *d++) == '\n') + PREFETCH; /* Fetch a data character. */ + /* Match anything but a newline, maybe even a null. */ + if ((translate ? translate[*d] : *d) == '\n' + || ((obscure_syntax & RE_DOT_NOT_NULL) + && (translate ? translate[*d] : *d) == '\000')) goto fail; + SET_REGS_MATCHED; + d++; break; case charset: case charset_not: { - /* Nonzero for charset_not */ - int not = 0; + int not = 0; /* Nonzero for charset_not. */ register int c; if (*(p - 1) == (unsigned char) charset_not) not = 1; - /* fetch a data character */ - PREFETCH; + PREFETCH; /* Fetch a data character. */ if (translate) - c = translate [*d]; + c = translate[*d]; else c = *d; @@ -1320,72 +2194,61 @@ re_match_2 (struct re_pattern_buffer *pbufp, unsigned char *string1, int size1, p += 1 + *p; if (!not) goto fail; - d++; + SET_REGS_MATCHED; + d++; break; } case begline: - if (d == string1 || d[-1] == '\n') - break; - goto fail; - + if ((size1 != 0 && d == string1) + || (size1 == 0 && size2 != 0 && d == string2) + || (d && d[-1] == '\n') + || (size1 == 0 && size2 == 0)) + break; + else + goto fail; + case endline: if (d == end2 || (d == end1 ? (size2 == 0 || *string2 == '\n') : *d == '\n')) break; goto fail; - /* "or" constructs ("|") are handled by starting each alternative - with an on_failure_jump that points to the start of the next alternative. - Each alternative except the last ends with a jump to the joining point. - (Actually, each jump except for the last one really jumps - to the following jump, because tensioning the jumps is a hassle.) */ + /* `or' constructs are handled by starting each alternative with + an on_failure_jump that points to the start of the next + alternative. Each alternative except the last ends with a + jump to the joining point. (Actually, each jump except for + the last one really jumps to the following jump, because + tensioning the jumps is a hassle.) */ /* The start of a stupid repeat has an on_failure_jump that points - past the end of the repeat text. - This makes a failure point so that, on failure to match a repetition, - matching restarts past as many repetitions have been found - with no way to fail and look for another one. */ + past the end of the repeat text. This makes a failure point so + that on failure to match a repetition, matching restarts past + as many repetitions have been found with no way to fail and + look for another one. */ /* A smart repeat is similar but loops back to the on_failure_jump - so that each repetition makes another failure point. */ + so that each repetition makes another failure point. */ case on_failure_jump: - if (stackp == stacke) - { - unsigned char **stackx; - if (stacke - stackb > re_max_failures * 2) - return -2; - stackx = (unsigned char **) alloca (2 * (stacke - stackb) - * sizeof (char *)); - bcopy (stackb, stackx, (stacke - stackb) * sizeof (char *)); - stackp = stackx + (stackp - stackb); - stacke = stackx + 2 * (stacke - stackb); - stackb = stackx; - } - mcnt = *p++ & 0377; - mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8; - p++; - *stackp++ = mcnt + p; - *stackp++ = d; - break; - - /* The end of a smart repeat has an maybe_finalize_jump back. - Change it either to a finalize_jump or an ordinary jump. */ + on_failure: + EXTRACT_NUMBER_AND_INCR (mcnt, p); + PUSH_FAILURE_POINT (p + mcnt, d); + break; + /* The end of a smart repeat has a maybe_finalize_jump back. + Change it either to a finalize_jump or an ordinary jump. */ case maybe_finalize_jump: - mcnt = *p++ & 0377; - mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8; - p++; + EXTRACT_NUMBER_AND_INCR (mcnt, p); { register unsigned char *p2 = p; - /* Compare what follows with the begining of the repeat. + /* Compare what follows with the beginning of the repeat. If we can establish that there is nothing that they would - both match, we can change to finalize_jump */ - while (p2 != pend + both match, we can change to finalize_jump. */ + while (p2 + 1 != pend && (*p2 == (unsigned char) stop_memory || *p2 == (unsigned char) start_memory)) - p2++; + p2 += 2; /* Skip over reg number. */ if (p2 == pend) p[-3] = (unsigned char) finalize_jump; else if (*p2 == (unsigned char) exactn @@ -1394,7 +2257,7 @@ re_match_2 (struct re_pattern_buffer *pbufp, unsigned char *string1, int size1, register int c = *p2 == (unsigned char) endline ? '\n' : p2[2]; register unsigned char *p1 = p + mcnt; /* p1[0] ... p1[2] are an on_failure_jump. - Examine what follows that */ + Examine what follows that. */ if (p1[3] == (unsigned char) exactn && p1[5] != c) p[-3] = (unsigned char) finalize_jump; else if (p1[3] == (unsigned char) charset @@ -1404,108 +2267,140 @@ re_match_2 (struct re_pattern_buffer *pbufp, unsigned char *string1, int size1, if (c < p1[4] * BYTEWIDTH && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) not = !not; - /* not is 1 if c would match */ - /* That means it is not safe to finalize */ + /* `not' is 1 if c would match. */ + /* That means it is not safe to finalize. */ if (!not) p[-3] = (unsigned char) finalize_jump; } } } - p -= 2; + p -= 2; /* Point at relative address again. */ if (p[-1] != (unsigned char) finalize_jump) { - p[-1] = (unsigned char) jump; + p[-1] = (unsigned char) jump; goto nofinalize; } - - /* The end of a stupid repeat has a finalize-jump - back to the start, where another failure point will be made - which will point after all the repetitions found so far. */ - - case finalize_jump: - stackp -= 2; - - case jump: + /* Note fall through. */ + + /* The end of a stupid repeat has a finalize_jump back to the + start, where another failure point will be made which will + point to after all the repetitions found so far. */ + + /* Take off failure points put on by matching on_failure_jump + because didn't fail. Also remove the register information + put on by the on_failure_jump. */ + case finalize_jump: + POP_FAILURE_POINT (); + /* Note fall through. */ + + /* Jump without taking off any failure points. */ + case jump: nofinalize: - mcnt = *p++ & 0377; - mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8; - p += mcnt + 1; /* The 1 compensates for missing ++ above */ + EXTRACT_NUMBER_AND_INCR (mcnt, p); + p += mcnt; break; - case dummy_failure_jump: - if (stackp == stacke) - { - unsigned char **stackx - = (unsigned char **) alloca (2 * (stacke - stackb) - * sizeof (char *)); - bcopy (stackb, stackx, (stacke - stackb) * sizeof (char *)); - stackp = stackx + (stackp - stackb); - stacke = stackx + 2 * (stacke - stackb); - stackb = stackx; + case dummy_failure_jump: + /* Normally, the on_failure_jump pushes a failure point, which + then gets popped at finalize_jump. We will end up at + finalize_jump, also, and with a pattern of, say, `a+', we + are skipping over the on_failure_jump, so we have to push + something meaningless for finalize_jump to pop. */ + PUSH_FAILURE_POINT (0, 0); + goto nofinalize; + + + /* Have to succeed matching what follows at least n times. Then + just handle like an on_failure_jump. */ + case succeed_n: + EXTRACT_NUMBER (mcnt, p + 2); + /* Originally, this is how many times we HAVE to succeed. */ + if (mcnt) + { + mcnt--; + p += 2; + STORE_NUMBER_AND_INCR (p, mcnt); + } + else if (mcnt == 0) + { + p[2] = unused; + p[3] = unused; + goto on_failure; + } + else + { + fprintf (stderr, "regex: the succeed_n's n is not set.\n"); + exit (1); } - *stackp++ = 0; - *stackp++ = 0; - goto nofinalize; - - case wordbound: - if (d == string1 /* Points to first char */ - || d == end2 /* Points to end */ - || (d == end1 && size2 == 0)) /* Points to end */ - break; - if ((SYNTAX (d[-1]) == Sword) - != (SYNTAX (d == end1 ? *string2 : *d) == Sword)) + break; + + case jump_n: + EXTRACT_NUMBER (mcnt, p + 2); + /* Originally, this is how many times we CAN jump. */ + if (mcnt) + { + mcnt--; + STORE_NUMBER(p + 2, mcnt); + goto nofinalize; /* Do the jump without taking off + any failure points. */ + } + /* If don't have to jump any more, skip over the rest of command. */ + else + p += 4; + break; + + case set_number_at: + { + register unsigned char *p1; + + EXTRACT_NUMBER_AND_INCR (mcnt, p); + p1 = p + mcnt; + EXTRACT_NUMBER_AND_INCR (mcnt, p); + STORE_NUMBER (p1, mcnt); + break; + } + + /* Ignore these. Used to ignore the n of succeed_n's which + currently have n == 0. */ + case unused: + break; + + case wordbound: + if (AT_WORD_BOUNDARY) break; goto fail; case notwordbound: - if (d == string1 /* Points to first char */ - || d == end2 /* Points to end */ - || (d == end1 && size2 == 0)) /* Points to end */ - goto fail; - if ((SYNTAX (d[-1]) == Sword) - != (SYNTAX (d == end1 ? *string2 : *d) == Sword)) + if (AT_WORD_BOUNDARY) goto fail; break; case wordbeg: - if (d == end2 /* Points to end */ - || (d == end1 && size2 == 0) /* Points to end */ - || SYNTAX (* (d == end1 ? string2 : d)) != Sword) /* Next char not a letter */ - goto fail; - if (d == string1 /* Points to first char */ - || SYNTAX (d[-1]) != Sword) /* prev char not letter */ + /* Have to check if AT_STRINGS_BEG before looking at d - 1. */ + if (IS_A_LETTER (d) && (AT_STRINGS_BEG || !IS_A_LETTER (d - 1))) break; goto fail; case wordend: - if (d == string1 /* Points to first char */ - || SYNTAX (d[-1]) != Sword) /* prev char not letter */ - goto fail; - if (d == end2 /* Points to end */ - || (d == end1 && size2 == 0) /* Points to end */ - || SYNTAX (d == end1 ? *string2 : *d) != Sword) /* Next char not a letter */ + /* Have to check if AT_STRINGS_BEG before looking at d - 1. */ + if (!AT_STRINGS_BEG && IS_A_LETTER (d - 1) + && (!IS_A_LETTER (d) || AT_STRINGS_END)) break; goto fail; #ifdef emacs case before_dot: - if (((d - string2 <= (unsigned) size2) - ? d - bf_p2 : d - bf_p1) - <= point) + if (PTR_CHAR_POS (d) >= point) goto fail; break; case at_dot: - if (((d - string2 <= (unsigned) size2) - ? d - bf_p2 : d - bf_p1) - == point) + if (PTR_CHAR_POS (d) != point) goto fail; break; case after_dot: - if (((d - string2 <= (unsigned) size2) - ? d - bf_p2 : d - bf_p1) - >= point) + if (PTR_CHAR_POS (d) <= point) goto fail; break; @@ -1518,6 +2413,7 @@ re_match_2 (struct re_pattern_buffer *pbufp, unsigned char *string1, int size1, matchsyntax: PREFETCH; if (SYNTAX (*d++) != (enum syntaxcode) mcnt) goto fail; + SET_REGS_MATCHED; break; case notwordchar: @@ -1529,34 +2425,44 @@ re_match_2 (struct re_pattern_buffer *pbufp, unsigned char *string1, int size1, matchnotsyntax: PREFETCH; if (SYNTAX (*d++) == (enum syntaxcode) mcnt) goto fail; - break; -#else + SET_REGS_MATCHED; + break; + +#else /* not emacs */ + case wordchar: PREFETCH; - if (SYNTAX (*d++) == 0) goto fail; + if (!IS_A_LETTER (d)) + goto fail; + SET_REGS_MATCHED; break; case notwordchar: PREFETCH; - if (SYNTAX (*d++) != 0) goto fail; + if (IS_A_LETTER (d)) + goto fail; + SET_REGS_MATCHED; break; + #endif /* not emacs */ case begbuf: - if (d == string1) /* Note, d cannot equal string2 */ - break; /* unless string1 == string2. */ - goto fail; + if (AT_STRINGS_BEG) + break; + goto fail; - case endbuf: - if (d == end2 || (d == end1 && size2 == 0)) + case endbuf: + if (AT_STRINGS_END) break; goto fail; case exactn: /* Match the next few pattern characters exactly. - mcnt is how many characters to match. */ + mcnt is how many characters to match. */ mcnt = *p++; - if (translate) + /* This is written out as an if-else so we don't waste time + testing `translate' inside the loop. */ + if (translate) { do { @@ -1574,35 +2480,65 @@ re_match_2 (struct re_pattern_buffer *pbufp, unsigned char *string1, int size1, } while (--mcnt); } - break; + SET_REGS_MATCHED; + break; } - continue; /* Successfully matched one pattern command; keep matching */ + continue; /* Successfully executed one pattern command; keep going. */ - /* Jump here if any matching operation fails. */ + /* Jump here if any matching operation fails. */ fail: if (stackp != stackb) /* A restart point is known. Restart there and pop it. */ { + short last_used_reg, this_reg; + + /* If this failure point is from a dummy_failure_point, just + skip it. */ if (!stackp[-2]) - { /* If innermost failure point is dormant, flush it and keep looking */ - stackp -= 2; - goto fail; - } - d = *--stackp; + { + POP_FAILURE_POINT (); + goto fail; + } + + d = *--stackp; p = *--stackp; - if (d >= string1 && d <= end1) + if (d >= string1 && d <= end1) dend = end_match_1; + /* Restore register info. */ + last_used_reg = (short) (int) *--stackp; + + /* Make the ones that weren't saved -1 or 0 again. */ + for (this_reg = RE_NREGS - 1; this_reg > last_used_reg; this_reg--) + { + regend[this_reg] = (unsigned char *) -1; + regstart[this_reg] = (unsigned char *) -1; + IS_ACTIVE (reg_info[this_reg]) = 0; + MATCHED_SOMETHING (reg_info[this_reg]) = 0; + } + + /* And restore the rest from the stack. */ + for ( ; this_reg > 0; this_reg--) + { + reg_info[this_reg] = *(struct register_info *) *--stackp; + regend[this_reg] = *--stackp; + regstart[this_reg] = *--stackp; + } } - else break; /* Matching at this starting point really fails! */ + else + break; /* Matching at this starting point really fails. */ } - return -1; /* Failure to match */ + + if (best_regs_set) + goto restore_best_regs; + return -1; /* Failure to match. */ } + static int -bcmp_translate (unsigned char *s1, unsigned char *s2, register int len, - unsigned char *translate) +bcmp_translate (char *s1, char *s2, int len, unsigned char *translate) { - register unsigned char *p1 = s1, *p2 = s2; + register unsigned char *p1 = (unsigned char*)s1; + register unsigned char *p2 = (unsigned char*)s2; while (len) { if (translate [*p1++] != translate [*p2++]) return 1; @@ -1610,10 +2546,12 @@ bcmp_translate (unsigned char *s1, unsigned char *s2, register int len, } return 0; } + + -/* Entry points compatible with bsd4.2 regex library */ +/* Entry points compatible with 4.2 BSD regex library. */ -#ifndef emacs +#if 0 static struct re_pattern_buffer re_comp_buf; @@ -1629,10 +2567,10 @@ re_comp (char *s) if (!re_comp_buf.buffer) { - if (!(re_comp_buf.buffer = (char *) new char[200])) + if (!(re_comp_buf.buffer = (char *) malloc (200))) return "Memory exhausted"; re_comp_buf.allocated = 200; - if (!(re_comp_buf.fastmap = (char *) new char[ (1 << BYTEWIDTH)])) + if (!(re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH))) return "Memory exhausted"; } return re_compile_pattern (s, strlen (s), &re_comp_buf); @@ -1642,18 +2580,21 @@ int re_exec (char *s) { int len = strlen (s); - return 0 <= re_search (&re_comp_buf, s, len, 0, len, 0); + return 0 <= re_search (&re_comp_buf, s, len, 0, len, + (struct re_registers *) 0); } +#endif /* not emacs */ + -#endif /* emacs */ #ifdef test #include -/* Indexed by a character, gives the upper case equivalent of the character */ +/* Indexed by a character, gives the upper case equivalent of the + character. */ -static char upcase[0400] = +char upcase[0400] = { 000, 001, 002, 003, 004, 005, 006, 007, 010, 011, 012, 013, 014, 015, 016, 017, 020, 021, 022, 023, 024, 025, 026, 027, @@ -1688,6 +2629,36 @@ static char upcase[0400] = 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377 }; +#ifdef canned + +#include "tests.h" + +typedef enum { extended_test, basic_test } test_type; + +/* Use this to run the tests we've thought of. */ + +void +main () +{ + test_type t = extended_test; + + if (t == basic_test) + { + printf ("Running basic tests:\n\n"); + test_posix_basic (); + } + else if (t == extended_test) + { + printf ("Running extended tests:\n\n"); + test_posix_extended (); + } +} + +#else /* not canned */ + +/* Use this to run interactive tests. */ + +void main (int argc, char **argv) { char pat[80]; @@ -1701,7 +2672,7 @@ main (int argc, char **argv) obscure_syntax = atoi (argv[1]); buf.allocated = 40; - buf.buffer = (char *) new char[buf.allocated]; + buf.buffer = (char *) malloc (buf.allocated); buf.fastmap = fastmap; buf.translate = upcase; @@ -1734,8 +2705,12 @@ main (int argc, char **argv) } } +#endif + + #ifdef NOTDEF -print_buf (struct re_pattern_buffer *bufp) +void +print_buf (struct re_pattern_buffer *bufpbufp) { int i; @@ -1757,11 +2732,12 @@ print_buf (struct re_pattern_buffer *bufp) printf ("\nfastmap is%s accurate\n", bufp->fastmap_accurate ? "" : "n't"); printf ("can %s be null\n----------", bufp->can_be_null ? "" : "not"); } -#endif +#endif /* NOTDEF */ +void printchar (char c) { - if (c < 041 || c >= 0177) + if (c < 040 || c >= 0177) { putchar ('\\'); putchar (((c >> 6) & 3) + '0'); @@ -1772,11 +2748,10 @@ printchar (char c) putchar (c); } +void error (char *string) { puts (string); exit (1); } - #endif /* test */ - diff --git a/gnu/lib/libg++/libg++/regex.h b/gnu/lib/libg++/libg++/regex.h new file mode 100644 index 0000000000..9e404e8da1 --- /dev/null +++ b/gnu/lib/libg++/libg++/regex.h @@ -0,0 +1,272 @@ +/* Definitions for data structures callers pass the regex library. + + Copyright (C) 1985, 1989-92 Free Software Foundation, Inc. + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __REGEXP_LIBRARY +#define __REGEXP_LIBRARY + +#if defined(SHORT_NAMES) || defined(VMS) +#define re_compile_pattern recmppat +#define re_pattern_buffer repatbuf +#define re_registers reregs +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Define number of parens for which we record the beginnings and ends. + This affects how much space the `struct re_registers' type takes up. */ +#ifndef RE_NREGS +#define RE_NREGS 10 +#endif + +#define BYTEWIDTH 8 + + +/* Maximum number of duplicates an interval can allow. */ +#ifndef RE_DUP_MAX /* kludge for AIX, which defines it */ +#define RE_DUP_MAX ((1 << 15) - 1) +#endif + +/* This defines the various regexp syntaxes. */ +extern int obscure_syntax; + + +/* The following bits are used in the obscure_syntax variable to choose among + alternative regexp syntaxes. */ + +/* If this bit is set, plain parentheses serve as grouping, and backslash + parentheses are needed for literal searching. + If not set, backslash-parentheses are grouping, and plain parentheses + are for literal searching. */ +#define RE_NO_BK_PARENS 1 + +/* If this bit is set, plain | serves as the `or'-operator, and \| is a + literal. + If not set, \| serves as the `or'-operator, and | is a literal. */ +#define RE_NO_BK_VBAR (1 << 1) + +/* If this bit is not set, plain + or ? serves as an operator, and \+, \? are + literals. + If set, \+, \? are operators and plain +, ? are literals. */ +#define RE_BK_PLUS_QM (1 << 2) + +/* If this bit is set, | binds tighter than ^ or $. + If not set, the contrary. */ +#define RE_TIGHT_VBAR (1 << 3) + +/* If this bit is set, then treat newline as an OR operator. + If not set, treat it as a normal character. */ +#define RE_NEWLINE_OR (1 << 4) + +/* If this bit is set, then special characters may act as normal + characters in some contexts. Specifically, this applies to: + ^ -- only special at the beginning, or after ( or |; + $ -- only special at the end, or before ) or |; + *, +, ? -- only special when not after the beginning, (, or |. + If this bit is not set, special characters (such as *, ^, and $) + always have their special meaning regardless of the surrounding + context. */ +#define RE_CONTEXT_INDEP_OPS (1 << 5) + +/* If this bit is not set, then \ before anything inside [ and ] is taken as + a real \. + If set, then such a \ escapes the following character. This is a + special case for awk. */ +#define RE_AWK_CLASS_HACK (1 << 6) + +/* If this bit is set, then \{ and \} or { and } serve as interval operators. + If not set, then \{ and \} and { and } are treated as literals. */ +#define RE_INTERVALS (1 << 7) + +/* If this bit is not set, then \{ and \} serve as interval operators and + { and } are literals. + If set, then { and } serve as interval operators and \{ and \} are + literals. */ +#define RE_NO_BK_CURLY_BRACES (1 << 8) + +/* If this bit is set, then character classes are supported; they are: + [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], + [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. + If not set, then character classes are not supported. */ +#define RE_CHAR_CLASSES (1 << 9) + +/* If this bit is set, then the dot re doesn't match a null byte. + If not set, it does. */ +#define RE_DOT_NOT_NULL (1 << 10) + +/* If this bit is set, then [^...] doesn't match a newline. + If not set, it does. */ +#define RE_HAT_NOT_NEWLINE (1 << 11) + +/* If this bit is set, back references are recognized. + If not set, they aren't. */ +#define RE_NO_BK_REFS (1 << 12) + +/* If this bit is set, back references must refer to a preceding + subexpression. If not set, a back reference to a nonexistent + subexpression is treated as literal characters. */ +#define RE_NO_EMPTY_BK_REF (1 << 13) + +/* If this bit is set, bracket expressions can't be empty. + If it is set, they can be empty. */ +#define RE_NO_EMPTY_BRACKETS (1 << 14) + +/* If this bit is set, then *, +, ? and { cannot be first in an re or + immediately after a |, or a (. Furthermore, a | cannot be first or + last in an re, or immediately follow another | or a (. Also, a ^ + cannot appear in a nonleading position and a $ cannot appear in a + nontrailing position (outside of bracket expressions, that is). */ +#define RE_CONTEXTUAL_INVALID_OPS (1 << 15) + +/* If this bit is set, then +, ? and | aren't recognized as operators. + If it's not, they are. */ +#define RE_LIMITED_OPS (1 << 16) + +/* If this bit is set, then an ending range point has to collate higher + or equal to the starting range point. + If it's not set, then when the ending range point collates higher + than the starting range point, the range is just considered empty. */ +#define RE_NO_EMPTY_RANGES (1 << 17) + +/* If this bit is set, then a hyphen (-) can't be an ending range point. + If it isn't, then it can. */ +#define RE_NO_HYPHEN_RANGE_END (1 << 18) + + +/* Define combinations of bits for the standard possibilities. */ +#define RE_SYNTAX_POSIX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_CONTEXT_INDEP_OPS) +#define RE_SYNTAX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_CONTEXT_INDEP_OPS | RE_AWK_CLASS_HACK) +#define RE_SYNTAX_EGREP (RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_CONTEXT_INDEP_OPS | RE_NEWLINE_OR) +#define RE_SYNTAX_GREP (RE_BK_PLUS_QM | RE_NEWLINE_OR) +#define RE_SYNTAX_EMACS 0 +#define RE_SYNTAX_POSIX_BASIC (RE_INTERVALS | RE_BK_PLUS_QM \ + | RE_CHAR_CLASSES | RE_DOT_NOT_NULL \ + | RE_HAT_NOT_NEWLINE | RE_NO_EMPTY_BK_REF \ + | RE_NO_EMPTY_BRACKETS | RE_LIMITED_OPS \ + | RE_NO_EMPTY_RANGES | RE_NO_HYPHEN_RANGE_END) + +#define RE_SYNTAX_POSIX_EXTENDED (RE_INTERVALS | RE_NO_BK_CURLY_BRACES \ + | RE_NO_BK_VBAR | RE_NO_BK_PARENS \ + | RE_HAT_NOT_NEWLINE | RE_CHAR_CLASSES \ + | RE_NO_EMPTY_BRACKETS | RE_CONTEXTUAL_INVALID_OPS \ + | RE_NO_BK_REFS | RE_NO_EMPTY_RANGES \ + | RE_NO_HYPHEN_RANGE_END) + + +/* This data structure is used to represent a compiled pattern. */ + +struct re_pattern_buffer + { + char *buffer; /* Space holding the compiled pattern commands. */ + long allocated; /* Size of space that `buffer' points to. */ + long used; /* Length of portion of buffer actually occupied */ + char *fastmap; /* Pointer to fastmap, if any, or zero if none. */ + /* re_search uses the fastmap, if there is one, + to skip over totally implausible characters. */ + char *translate; /* Translate table to apply to all characters before + comparing, or zero for no translation. + The translation is applied to a pattern when it is + compiled and to data when it is matched. */ + char fastmap_accurate; + /* Set to zero when a new pattern is stored, + set to one when the fastmap is updated from it. */ + char can_be_null; /* Set to one by compiling fastmap + if this pattern might match the null string. + It does not necessarily match the null string + in that case, but if this is zero, it cannot. + 2 as value means can match null string + but at end of range or before a character + listed in the fastmap. */ + }; + + +/* search.c (search_buffer) needs this one value. It is defined both in + regex.c and here. */ +#define RE_EXACTN_VALUE 1 + + +/* Structure to store register contents data in. + + Pass the address of such a structure as an argument to re_match, etc., + if you want this information back. + + For i from 1 to RE_NREGS - 1, start[i] records the starting index in + the string of where the ith subexpression matched, and end[i] records + one after the ending index. start[0] and end[0] are analogous, for + the entire pattern. */ + +struct re_registers + { + int start[RE_NREGS]; + int end[RE_NREGS]; + }; + + + +#if defined(__STDC__) || defined(__cplusplus) + +extern char *re_compile_pattern (const char *, int, struct re_pattern_buffer *); +/* Is this really advertised? */ +extern void re_compile_fastmap (struct re_pattern_buffer *); +extern int re_search (struct re_pattern_buffer *, char*, int, int, int, + struct re_registers *); +extern int re_search_2 (struct re_pattern_buffer *, char *, int, + char *, int, int, int, + struct re_registers *, int); +extern int re_match (struct re_pattern_buffer *, char *, int, int, + struct re_registers *); +extern int re_match_2 (struct re_pattern_buffer *, char *, int, + char *, int, int, struct re_registers *, int); + +#if 0 +/* 4.2 bsd compatibility. */ +extern char *re_comp (char *); +extern int re_exec (char *); +#endif + +#else /* !__STDC__ */ + +#define const /* nothing */ +extern char *re_compile_pattern (); +/* Is this really advertised? */ +extern void re_compile_fastmap (); +extern int re_search (), re_search_2 (); +extern int re_match (), re_match_2 (); + +#if 0 +/* 4.2 bsd compatibility. */ +extern char *re_comp (); +extern int re_exec (); +#endif + +#endif /* __STDC__ */ + + +#ifdef SYNTAX_TABLE +extern char *re_syntax_table; +#endif + +#ifdef __cplusplus +extern int re_max_failures; +} +#endif + +#endif /* !__REGEXP_LIBRARY */ diff --git a/gnu/lib/libg++/libg++/sqrt.cc b/gnu/lib/libg++/libg++/sqrt.cc index 3ad6a94cd5..6df9af3790 100644 --- a/gnu/lib/libg++/libg++/sqrt.cc +++ b/gnu/lib/libg++/libg++/sqrt.cc @@ -2,22 +2,17 @@ Copyright (C) 1990 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ diff --git a/gnu/lib/libg++/libg++/std.cc b/gnu/lib/libg++/libg++/std.cc deleted file mode 100644 index d2bb1140a9..0000000000 --- a/gnu/lib/libg++/libg++/std.cc +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __GNUG__ -#pragma implementation -#endif -#include diff --git a/gnu/lib/libg++/libg++/std.h b/gnu/lib/libg++/libg++/std.h new file mode 100644 index 0000000000..24e0ae98b2 --- /dev/null +++ b/gnu/lib/libg++/libg++/std.h @@ -0,0 +1,37 @@ +// This may look like C code, but it is really -*- C++ -*- +/* +Copyright (C) 1988, 1992 Free Software Foundation + written by Doug Lea (dl@rocky.oswego.edu) + +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _std_h +#define _std_h 1 + +#include <_G_config.h> +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +int strcasecmp _G_ARGS((const char*, const char*)); +} + +#endif diff --git a/gnu/lib/libg++/libg++/str.cc b/gnu/lib/libg++/libg++/str.cc index b2366663dd..bf77c02547 100644 --- a/gnu/lib/libg++/libg++/str.cc +++ b/gnu/lib/libg++/libg++/str.cc @@ -2,22 +2,17 @@ Copyright (C) 1990 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ diff --git a/gnu/lib/libg++/libg++/strclass.h b/gnu/lib/libg++/libg++/strclass.h new file mode 100644 index 0000000000..57dbcc8cf9 --- /dev/null +++ b/gnu/lib/libg++/libg++/strclass.h @@ -0,0 +1,5 @@ +#ifndef _strclass_h +#define _strclass_h +#include +typedef class String string; +#endif diff --git a/gnu/lib/libg++/libg++/streambuf.cc b/gnu/lib/libg++/libg++/streambuf.cc deleted file mode 100644 index 4552433d88..0000000000 --- a/gnu/lib/libg++/libg++/streambuf.cc +++ /dev/null @@ -1,138 +0,0 @@ -/* -Copyright (C) 1990 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. -*/ - -#ifdef __GNUG__ -#pragma implementation -#endif - -#include - - -streambuf::streambuf() - :base(0), gptr(0), pptr(0), eptr(0), alloc(0) -{} - -streambuf::streambuf(char* buf, int buflen) - : base(buf), gptr(buf), pptr(buf), eptr(buf+buflen-1), alloc(0) -{} - -streambuf::~streambuf() -{ - if (alloc && (base != 0)) delete base; -} - -int streambuf::doallocate() -{ - if (alloc && base != 0) delete base; - base = new char[BUFSIZ]; - gptr = pptr = base; - eptr = base + BUFSIZ - 1; - alloc = 1; - return BUFSIZ; -} - -streambuf* streambuf::setbuf(char* buf, int buflen, int preloaded_count) -{ - if (alloc && (base != 0)) delete base; - alloc = 0; - base = gptr = buf; - pptr = base + preloaded_count; - eptr = base + buflen - 1; - return this; -} - -const char* streambuf::name() -{ - return 0; -} - -int streambuf::overflow(int c) -{ - if (base == 0) allocate(); - return (c == EOF)? c : ((pptr <= eptr)? (*pptr++ = (char)(c)) : EOF); -} - -int streambuf::underflow() -{ - return EOF; -} - -int streambuf::sputs(const char* s) -{ - if (s != 0) - { - for(; *s != 0; ++s) - { - if (must_overflow(*s)) { if (overflow(*s) == EOF) return EOF; } - else *pptr++ = *s; - } - } - return 0; -} - -int streambuf::sputsn(const char* s, int len) -{ - for(; --len >= 0; ++s) - { - if (must_overflow(*s)) { if (overflow(*s) == EOF) return EOF; } - else *pptr++ = *s; - } - return 0; -} - - -int streambuf::is_open() -{ - return 1; -} - -int streambuf::close() -{ - return 1; -} - -void streambuf::error() -{ - abort(); -} - -streambuf* streambuf::open(const char*, open_mode) -{ - return 0; -} - -streambuf* streambuf::open(const char*, io_mode, access_mode) -{ - return 0; -} -streambuf* streambuf::open(const char*, const char*) -{ - return 0; -} -streambuf* streambuf::open(int, io_mode) -{ - return 0; -} -streambuf* streambuf::open(FILE*) -{ - return 0; -} diff --git a/gnu/lib/libg++/g++-include/swap.h b/gnu/lib/libg++/libg++/swap.h similarity index 100% rename from gnu/lib/libg++/g++-include/swap.h rename to gnu/lib/libg++/libg++/swap.h diff --git a/gnu/lib/libg++/libg++/timer.cc b/gnu/lib/libg++/libg++/timer.cc index 672e08adfd..fdf35f4c18 100644 --- a/gnu/lib/libg++/libg++/timer.cc +++ b/gnu/lib/libg++/libg++/timer.cc @@ -1,30 +1,24 @@ /* -Copyright (C) 1990 Free Software Foundation +Copyright (C) 1990, 1992 Free Software Foundation written by Doug Lea (dl@rocky.oswego.edu) -This file is part of GNU CC. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY. No author or distributor -accepts responsibility to anyone for the consequences of using it -or for whether it serves any particular purpose or works at all, -unless he says so in writing. Refer to the GNU CC General Public -License for full details. - -Everyone is granted permission to copy, modify and redistribute -GNU CC, but only under the conditions described in the -GNU CC General Public License. A copy of this license is -supposed to have been given to you along with GNU CC so you -can know your rights and responsibilities. It should be in a -file named COPYING. Among other things, the copyright notice -and this notice must be preserved on all copies. +This file is part of the GNU C++ Library. This library is free +software; you can redistribute it and/or modify it under the terms of +the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. This library is distributed in the hope +that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU Library General Public License for more details. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __GNUG__ #pragma implementation #endif #include -#include // Timing functions from Doug Schmidt... @@ -36,17 +30,15 @@ and this notice must be preserved on all copies. #if 1 -#if defined(USG) -extern "C" { -#include +#include <_G_config.h> +#include +#if !_G_HAVE_SYS_RESOURCE || !defined(RUSAGE_SELF) +#define USE_TIMES #include #include -} -#else -#include +#if !defined (HZ) && defined(CLK_TCK) +#define HZ CLK_TCK #endif - -#if defined(USG) static struct tms Old_Time; static struct tms New_Time; #else @@ -58,7 +50,7 @@ static int Timer_Set = 0; double start_timer() { Timer_Set = 1; -#if defined(USG) +#ifdef USE_TIMES times(&Old_Time); return((double) Old_Time.tms_utime / HZ); #else @@ -78,13 +70,13 @@ double return_elapsed_time(double Last_Time) } else { /* get process time */ -#if defined(USG) +#ifdef USE_TIMES times(&New_Time); #else getrusage(RUSAGE_SELF,&New_Time); #endif if (Last_Time == 0.0) { -#if defined(USG) +#ifdef USE_TIMES return((double) (New_Time.tms_utime - Old_Time.tms_utime) / HZ); #else return((New_Time.ru_utime.tv_sec - Old_Time.ru_utime.tv_sec) + @@ -93,7 +85,7 @@ double return_elapsed_time(double Last_Time) #endif } else { -#if defined(USG) +#ifdef USE_TIMES return((double) New_Time.tms_utime / HZ - Last_Time); #else return((New_Time.ru_utime.tv_sec + diff --git a/gnu/lib/libg++/libg++/typemacros.h b/gnu/lib/libg++/libg++/typemacros.h new file mode 100644 index 0000000000..f2bd7877e3 --- /dev/null +++ b/gnu/lib/libg++/libg++/typemacros.h @@ -0,0 +1,8 @@ +#define _T(type) typeof(type) +#define pointer_to(type) _T(_T(type)*) +#define member_of(cls,type) _T(_T(type) cls::) +#define function(res, args) _T(_T(res) args) + +#define _xq_yq(x,y) x ## _ ## y +#define _x_y(x,y) _xq_yq(x,y) +#define _gensym(stem) _x_y(stem, __LINE__) diff --git a/gnu/lib/libg++/libg++/xyzzy.cc b/gnu/lib/libg++/libg++/xyzzy.cc deleted file mode 100644 index 8008e05ea5..0000000000 --- a/gnu/lib/libg++/libg++/xyzzy.cc +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef NO_GNULIB3 -// from tiemann - -/* Needed, in case there are no other objects which - need static initialization and cleanup. */ -struct __xyzzy__ -{ - __xyzzy__ () {} - ~__xyzzy__ () {} -} __1xyzzy__; - -#endif diff --git a/gnu/lib/libg++/libiberty/config.h b/gnu/lib/libg++/libiberty/config.h new file mode 100644 index 0000000000..b37ee84a78 --- /dev/null +++ b/gnu/lib/libg++/libiberty/config.h @@ -0,0 +1 @@ +/* !Automatically generated from ./functions.def - DO NOT EDIT! */ diff --git a/gnu/lib/libg++/libiberty/insque.c b/gnu/lib/libg++/libiberty/insque.c new file mode 100644 index 0000000000..9ae8e2c43a --- /dev/null +++ b/gnu/lib/libg++/libiberty/insque.c @@ -0,0 +1,73 @@ +/* insque(3C) routines + Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of the libiberty library. +Libiberty is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +Libiberty is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with libiberty; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +/* + +NAME + + insque, remque -- insert, remove an element from a queue + +SYNOPSIS + + struct qelem { + struct qelem *q_forw; + struct qelem *q_back; + char q_data[]; + }; + + void insque (struct qelem *elem, struct qelem *pred) + + void remque (struct qelem *elem) + +DESCRIPTION + + Routines to manipulate queues built from doubly linked lists. + The insque routine inserts ELEM in the queue immediately after + PRED. The remque routine removes ELEM from its containing queue. + +BUGS + +*/ + + +struct qelem { + struct qelem *q_forw; + struct qelem *q_back; +}; + + +void +insque (elem, pred) + struct qelem *elem; + struct qelem *pred; +{ + elem -> q_forw = pred -> q_forw; + pred -> q_forw -> q_back = elem; + elem -> q_back = pred; + pred -> q_forw = elem; +} + + +void +remque (elem) + struct qelem *elem; +{ + elem -> q_forw -> q_back = elem -> q_back; + elem -> q_back -> q_forw = elem -> q_forw; +} diff --git a/gnu/lib/libg++/libiberty/strerror.c b/gnu/lib/libg++/libiberty/strerror.c new file mode 100644 index 0000000000..b0dc63d842 --- /dev/null +++ b/gnu/lib/libg++/libiberty/strerror.c @@ -0,0 +1,816 @@ +/* Extended support for using errno values. + Copyright (C) 1992 Free Software Foundation, Inc. + Written by Fred Fish. fnf@cygnus.com + +This file is part of the libiberty library. +Libiberty is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +Libiberty is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with libiberty; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include "config.h" + +#include +#ifndef NEED_sys_errlist +/* Note that errno.h might declare sys_errlist in a way that the + * compiler might consider incompatible with our later declaration, + * perhaps by using const attributes. So we hide the declaration + * in errno.h (if any) using a macro. */ +#define sys_errlist sys_errlist__ +#endif +#include +#ifndef NEED_sys_errlist +#undef sys_errlist +#endif + +/* Routines imported from standard C runtime libraries. */ + +#ifdef __STDC__ +#include +extern void *malloc (size_t size); /* 4.10.3.3 */ +extern void *memset (void *s, int c, size_t n); /* 4.11.6.1 */ +#else /* !__STDC__ */ +#ifndef const +#define const +#endif +extern char *malloc (); /* Standard memory allocater */ +extern char *memset (); +#endif /* __STDC__ */ + +#ifndef NULL +# ifdef __STDC__ +# define NULL (void *) 0 +# else +# define NULL 0 +# endif +#endif + +#ifndef MAX +# define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +/* Translation table for errno values. See intro(2) in most UNIX systems + Programmers Reference Manuals. + + Note that this table is generally only accessed when it is used at runtime + to initialize errno name and message tables that are indexed by errno + value. + + Not all of these errnos will exist on all systems. This table is the only + thing that should have to be updated as new error numbers are introduced. + It's sort of ugly, but at least its portable. */ + +struct error_info +{ + int value; /* The numeric value from */ + char *name; /* The equivalent symbolic value */ + char *msg; /* Short message about this value */ +}; + +static const struct error_info error_table[] = +{ +#if defined (EPERM) + EPERM, "EPERM", "Not owner", +#endif +#if defined (ENOENT) + ENOENT, "ENOENT", "No such file or directory", +#endif +#if defined (ESRCH) + ESRCH, "ESRCH", "No such process", +#endif +#if defined (EINTR) + EINTR, "EINTR", "Interrupted system call", +#endif +#if defined (EIO) + EIO, "EIO", "I/O error", +#endif +#if defined (ENXIO) + ENXIO, "ENXIO", "No such device or address", +#endif +#if defined (E2BIG) + E2BIG, "E2BIG", "Arg list too long", +#endif +#if defined (ENOEXEC) + ENOEXEC, "ENOEXEC", "Exec format error", +#endif +#if defined (EBADF) + EBADF, "EBADF", "Bad file number", +#endif +#if defined (ECHILD) + ECHILD, "ECHILD", "No child processes", +#endif +#if defined (EWOULDBLOCK) /* Put before EAGAIN, sometimes aliased */ + EWOULDBLOCK, "EWOULDBLOCK", "Operation would block", +#endif +#if defined (EAGAIN) + EAGAIN, "EAGAIN", "No more processes", +#endif +#if defined (ENOMEM) + ENOMEM, "ENOMEM", "Not enough space", +#endif +#if defined (EACCES) + EACCES, "EACCES", "Permission denied", +#endif +#if defined (EFAULT) + EFAULT, "EFAULT", "Bad address", +#endif +#if defined (ENOTBLK) + ENOTBLK, "ENOTBLK", "Block device required", +#endif +#if defined (EBUSY) + EBUSY, "EBUSY", "Device busy", +#endif +#if defined (EEXIST) + EEXIST, "EEXIST", "File exists", +#endif +#if defined (EXDEV) + EXDEV, "EXDEV", "Cross-device link", +#endif +#if defined (ENODEV) + ENODEV, "ENODEV", "No such device", +#endif +#if defined (ENOTDIR) + ENOTDIR, "ENOTDIR", "Not a directory", +#endif +#if defined (EISDIR) + EISDIR, "EISDIR", "Is a directory", +#endif +#if defined (EINVAL) + EINVAL, "EINVAL", "Invalid argument", +#endif +#if defined (ENFILE) + ENFILE, "ENFILE", "File table overflow", +#endif +#if defined (EMFILE) + EMFILE, "EMFILE", "Too many open files", +#endif +#if defined (ENOTTY) + ENOTTY, "ENOTTY", "Not a typewriter", +#endif +#if defined (ETXTBSY) + ETXTBSY, "ETXTBSY", "Text file busy", +#endif +#if defined (EFBIG) + EFBIG, "EFBIG", "File too large", +#endif +#if defined (ENOSPC) + ENOSPC, "ENOSPC", "No space left on device", +#endif +#if defined (ESPIPE) + ESPIPE, "ESPIPE", "Illegal seek", +#endif +#if defined (EROFS) + EROFS, "EROFS", "Read-only file system", +#endif +#if defined (EMLINK) + EMLINK, "EMLINK", "Too many links", +#endif +#if defined (EPIPE) + EPIPE, "EPIPE", "Broken pipe", +#endif +#if defined (EDOM) + EDOM, "EDOM", "Math argument out of domain of func", +#endif +#if defined (ERANGE) + ERANGE, "ERANGE", "Math result not representable", +#endif +#if defined (ENOMSG) + ENOMSG, "ENOMSG", "No message of desired type", +#endif +#if defined (EIDRM) + EIDRM, "EIDRM", "Identifier removed", +#endif +#if defined (ECHRNG) + ECHRNG, "ECHRNG", "Channel number out of range", +#endif +#if defined (EL2NSYNC) + EL2NSYNC, "EL2NSYNC", "Level 2 not synchronized", +#endif +#if defined (EL3HLT) + EL3HLT, "EL3HLT", "Level 3 halted", +#endif +#if defined (EL3RST) + EL3RST, "EL3RST", "Level 3 reset", +#endif +#if defined (ELNRNG) + ELNRNG, "ELNRNG", "Link number out of range", +#endif +#if defined (EUNATCH) + EUNATCH, "EUNATCH", "Protocol driver not attached", +#endif +#if defined (ENOCSI) + ENOCSI, "ENOCSI", "No CSI structure available", +#endif +#if defined (EL2HLT) + EL2HLT, "EL2HLT", "Level 2 halted", +#endif +#if defined (EDEADLK) + EDEADLK, "EDEADLK", "Deadlock condition", +#endif +#if defined (ENOLCK) + ENOLCK, "ENOLCK", "No record locks available", +#endif +#if defined (EBADE) + EBADE, "EBADE", "Invalid exchange", +#endif +#if defined (EBADR) + EBADR, "EBADR", "Invalid request descriptor", +#endif +#if defined (EXFULL) + EXFULL, "EXFULL", "Exchange full", +#endif +#if defined (ENOANO) + ENOANO, "ENOANO", "No anode", +#endif +#if defined (EBADRQC) + EBADRQC, "EBADRQC", "Invalid request code", +#endif +#if defined (EBADSLT) + EBADSLT, "EBADSLT", "Invalid slot", +#endif +#if defined (EDEADLOCK) + EDEADLOCK, "EDEADLOCK", "File locking deadlock error", +#endif +#if defined (EBFONT) + EBFONT, "EBFONT", "Bad font file format", +#endif +#if defined (ENOSTR) + ENOSTR, "ENOSTR", "Device not a stream", +#endif +#if defined (ENODATA) + ENODATA, "ENODATA", "No data available", +#endif +#if defined (ETIME) + ETIME, "ETIME", "Timer expired", +#endif +#if defined (ENOSR) + ENOSR, "ENOSR", "Out of streams resources", +#endif +#if defined (ENONET) + ENONET, "ENONET", "Machine is not on the network", +#endif +#if defined (ENOPKG) + ENOPKG, "ENOPKG", "Package not installed", +#endif +#if defined (EREMOTE) + EREMOTE, "EREMOTE", "Object is remote", +#endif +#if defined (ENOLINK) + ENOLINK, "ENOLINK", "Link has been severed", +#endif +#if defined (EADV) + EADV, "EADV", "Advertise error", +#endif +#if defined (ESRMNT) + ESRMNT, "ESRMNT", "Srmount error", +#endif +#if defined (ECOMM) + ECOMM, "ECOMM", "Communication error on send", +#endif +#if defined (EPROTO) + EPROTO, "EPROTO", "Protocol error", +#endif +#if defined (EMULTIHOP) + EMULTIHOP, "EMULTIHOP", "Multihop attempted", +#endif +#if defined (EDOTDOT) + EDOTDOT, "EDOTDOT", "RFS specific error", +#endif +#if defined (EBADMSG) + EBADMSG, "EBADMSG", "Not a data message", +#endif +#if defined (ENAMETOOLONG) + ENAMETOOLONG, "ENAMETOOLONG", "File name too long", +#endif +#if defined (EOVERFLOW) + EOVERFLOW, "EOVERFLOW", "Value too large for defined data type", +#endif +#if defined (ENOTUNIQ) + ENOTUNIQ, "ENOTUNIQ", "Name not unique on network", +#endif +#if defined (EBADFD) + EBADFD, "EBADFD", "File descriptor in bad state", +#endif +#if defined (EREMCHG) + EREMCHG, "EREMCHG", "Remote address changed", +#endif +#if defined (ELIBACC) + ELIBACC, "ELIBACC", "Can not access a needed shared library", +#endif +#if defined (ELIBBAD) + ELIBBAD, "ELIBBAD", "Accessing a corrupted shared library", +#endif +#if defined (ELIBSCN) + ELIBSCN, "ELIBSCN", ".lib section in a.out corrupted", +#endif +#if defined (ELIBMAX) + ELIBMAX, "ELIBMAX", "Attempting to link in too many shared libraries", +#endif +#if defined (ELIBEXEC) + ELIBEXEC, "ELIBEXEC", "Cannot exec a shared library directly", +#endif +#if defined (EILSEQ) + EILSEQ, "EILSEQ", "Illegal byte sequence", +#endif +#if defined (ENOSYS) + ENOSYS, "ENOSYS", "Operation not applicable", +#endif +#if defined (ELOOP) + ELOOP, "ELOOP", "Too many symbolic links encountered", +#endif +#if defined (ERESTART) + ERESTART, "ERESTART", "Interrupted system call should be restarted", +#endif +#if defined (ESTRPIPE) + ESTRPIPE, "ESTRPIPE", "Streams pipe error", +#endif +#if defined (ENOTEMPTY) + ENOTEMPTY, "ENOTEMPTY", "Directory not empty", +#endif +#if defined (EUSERS) + EUSERS, "EUSERS", "Too many users", +#endif +#if defined (ENOTSOCK) + ENOTSOCK, "ENOTSOCK", "Socket operation on non-socket", +#endif +#if defined (EDESTADDRREQ) + EDESTADDRREQ, "EDESTADDRREQ", "Destination address required", +#endif +#if defined (EMSGSIZE) + EMSGSIZE, "EMSGSIZE", "Message too long", +#endif +#if defined (EPROTOTYPE) + EPROTOTYPE, "EPROTOTYPE", "Protocol wrong type for socket", +#endif +#if defined (ENOPROTOOPT) + ENOPROTOOPT, "ENOPROTOOPT", "Protocol not available", +#endif +#if defined (EPROTONOSUPPORT) + EPROTONOSUPPORT, "EPROTONOSUPPORT", "Protocol not supported", +#endif +#if defined (ESOCKTNOSUPPORT) + ESOCKTNOSUPPORT, "ESOCKTNOSUPPORT", "Socket type not supported", +#endif +#if defined (EOPNOTSUPP) + EOPNOTSUPP, "EOPNOTSUPP", "Operation not supported on transport endpoint", +#endif +#if defined (EPFNOSUPPORT) + EPFNOSUPPORT, "EPFNOSUPPORT", "Protocol family not supported", +#endif +#if defined (EAFNOSUPPORT) + EAFNOSUPPORT, "EAFNOSUPPORT", "Address family not supported by protocol", +#endif +#if defined (EADDRINUSE) + EADDRINUSE, "EADDRINUSE", "Address already in use", +#endif +#if defined (EADDRNOTAVAIL) + EADDRNOTAVAIL, "EADDRNOTAVAIL","Cannot assign requested address", +#endif +#if defined (ENETDOWN) + ENETDOWN, "ENETDOWN", "Network is down", +#endif +#if defined (ENETUNREACH) + ENETUNREACH, "ENETUNREACH", "Network is unreachable", +#endif +#if defined (ENETRESET) + ENETRESET, "ENETRESET", "Network dropped connection because of reset", +#endif +#if defined (ECONNABORTED) + ECONNABORTED, "ECONNABORTED", "Software caused connection abort", +#endif +#if defined (ECONNRESET) + ECONNRESET, "ECONNRESET", "Connection reset by peer", +#endif +#if defined (ENOBUFS) + ENOBUFS, "ENOBUFS", "No buffer space available", +#endif +#if defined (EISCONN) + EISCONN, "EISCONN", "Transport endpoint is already connected", +#endif +#if defined (ENOTCONN) + ENOTCONN, "ENOTCONN", "Transport endpoint is not connected", +#endif +#if defined (ESHUTDOWN) + ESHUTDOWN, "ESHUTDOWN", "Cannot send after transport endpoint shutdown", +#endif +#if defined (ETOOMANYREFS) + ETOOMANYREFS, "ETOOMANYREFS", "Too many references: cannot splice", +#endif +#if defined (ETIMEDOUT) + ETIMEDOUT, "ETIMEDOUT", "Connection timed out", +#endif +#if defined (ECONNREFUSED) + ECONNREFUSED, "ECONNREFUSED", "Connection refused", +#endif +#if defined (EHOSTDOWN) + EHOSTDOWN, "EHOSTDOWN", "Host is down", +#endif +#if defined (EHOSTUNREACH) + EHOSTUNREACH, "EHOSTUNREACH", "No route to host", +#endif +#if defined (EALREADY) + EALREADY, "EALREADY", "Operation already in progress", +#endif +#if defined (EINPROGRESS) + EINPROGRESS, "EINPROGRESS", "Operation now in progress", +#endif +#if defined (ESTALE) + ESTALE, "ESTALE", "Stale NFS file handle", +#endif +#if defined (EUCLEAN) + EUCLEAN, "EUCLEAN", "Structure needs cleaning", +#endif +#if defined (ENOTNAM) + ENOTNAM, "ENOTNAM", "Not a XENIX named type file", +#endif +#if defined (ENAVAIL) + ENAVAIL, "ENAVAIL", "No XENIX semaphores available", +#endif +#if defined (EISNAM) + EISNAM, "EISNAM", "Is a named type file", +#endif +#if defined (EREMOTEIO) + EREMOTEIO, "EREMOTEIO", "Remote I/O error", +#endif + 0, NULL, NULL +}; + +/* Translation table allocated and initialized at runtime. Indexed by the + errno value to find the equivalent symbolic value. */ + +static char **error_names; +static int num_error_names = 0; + +/* Translation table allocated and initialized at runtime, if it does not + already exist in the host environment. Indexed by the errno value to find + the descriptive string. + + We don't export it for use in other modules because even though it has the + same name, it differs from other implementations in that it is dynamically + initialized rather than statically initialized. */ + +#ifdef NEED_sys_errlist + +static int sys_nerr; +static char **sys_errlist; + +#else + +extern int sys_nerr; +extern char *sys_errlist[]; + +#endif + + +/* + +NAME + + init_error_tables -- initialize the name and message tables + +SYNOPSIS + + static void init_error_tables (); + +DESCRIPTION + + Using the error_table, which is initialized at compile time, generate + the error_names and the sys_errlist (if needed) tables, which are + indexed at runtime by a specific errno value. + +BUGS + + The initialization of the tables may fail under low memory conditions, + in which case we don't do anything particularly useful, but we don't + bomb either. Who knows, it might succeed at a later point if we free + some memory in the meantime. In any case, the other routines know + how to deal with lack of a table after trying to initialize it. This + may or may not be considered to be a bug, that we don't specifically + warn about this particular failure mode. + +*/ + +static void +init_error_tables () +{ + const struct error_info *eip; + int nbytes; + + /* If we haven't already scanned the error_table once to find the maximum + errno value, then go find it now. */ + + if (num_error_names == 0) + { + for (eip = error_table; eip -> name != NULL; eip++) + { + if (eip -> value >= num_error_names) + { + num_error_names = eip -> value + 1; + } + } + } + + /* Now attempt to allocate the error_names table, zero it out, and then + initialize it from the statically initialized error_table. */ + + if (error_names == NULL) + { + nbytes = num_error_names * sizeof (char *); + if ((error_names = (char **) malloc (nbytes)) != NULL) + { + memset (error_names, 0, nbytes); + for (eip = error_table; eip -> name != NULL; eip++) + { + error_names[eip -> value] = eip -> name; + } + } + } + +#ifdef NEED_sys_errlist + + /* Now attempt to allocate the sys_errlist table, zero it out, and then + initialize it from the statically initialized error_table. */ + + if (sys_errlist == NULL) + { + nbytes = num_error_names * sizeof (char *); + if ((sys_errlist = (char **) malloc (nbytes)) != NULL) + { + memset (sys_errlist, 0, nbytes); + sys_nerr = num_error_names; + for (eip = error_table; eip -> name != NULL; eip++) + { + sys_errlist[eip -> value] = eip -> msg; + } + } + } + +#endif + +} + +/* + +NAME + + errno_max -- return the max errno value + +SYNOPSIS + + int errno_max (); + +DESCRIPTION + + Returns the maximum errno value for which a corresponding symbolic + name or message is available. Note that in the case where + we use the sys_errlist supplied by the system, it is possible for + there to be more symbolic names than messages, or vice versa. + In fact, the manual page for perror(3C) explicitly warns that one + should check the size of the table (sys_nerr) before indexing it, + since new error codes may be added to the system before they are + added to the table. Thus sys_nerr might be smaller than value + implied by the largest errno value defined in . + + We return the maximum value that can be used to obtain a meaningful + symbolic name or message. + +*/ + +int +errno_max () +{ + int maxsize; + + if (error_names == NULL) + { + init_error_tables (); + } + maxsize = MAX (sys_nerr, num_error_names); + return (maxsize - 1); +} + +#ifdef NEED_strerror + +/* + +NAME + + strerror -- map an error number to an error message string + +SYNOPSIS + + char *strerror (int errnoval) + +DESCRIPTION + + Maps an errno number to an error message string, the contents of + which are implementation defined. On systems which have the external + variables sys_nerr and sys_errlist, these strings will be the same + as the ones used by perror(). + + If the supplied error number is within the valid range of indices + for the sys_errlist, but no message is available for the particular + error number, then returns the string "Error NUM", where NUM is the + error number. + + If the supplied error number is not a valid index into sys_errlist, + returns NULL. + + The returned string is only guaranteed to be valid only until the + next call to strerror. + +*/ + +char * +strerror (errnoval) + int errnoval; +{ + char *msg; + static char buf[32]; + +#ifdef NEED_sys_errlist + + if (error_names == NULL) + { + init_error_tables (); + } + +#endif + + if ((errnoval < 0) || (errnoval >= sys_nerr)) + { + /* Out of range, just return NULL */ + msg = NULL; + } + else if ((sys_errlist == NULL) || (sys_errlist[errnoval] == NULL)) + { + /* In range, but no sys_errlist or no entry at this index. */ + sprintf (buf, "Error %d", errnoval); + msg = buf; + } + else + { + /* In range, and a valid message. Just return the message. */ + msg = sys_errlist[errnoval]; + } + + return (msg); +} + +#endif /* NEED_strerror */ + + +/* + +NAME + + strerrno -- map an error number to a symbolic name string + +SYNOPSIS + + char *strerrno (int errnoval) + +DESCRIPTION + + Given an error number returned from a system call (typically + returned in errno), returns a pointer to a string containing the + symbolic name of that error number, as found in . + + If the supplied error number is within the valid range of indices + for symbolic names, but no name is available for the particular + error number, then returns the string "Error NUM", where NUM is + the error number. + + If the supplied error number is not within the range of valid + indices, then returns NULL. + +BUGS + + The contents of the location pointed to are only guaranteed to be + valid until the next call to strerrno. + +*/ + +char * +strerrno (errnoval) + int errnoval; +{ + char *name; + static char buf[32]; + + if (error_names == NULL) + { + init_error_tables (); + } + + if ((errnoval < 0) || (errnoval >= num_error_names)) + { + /* Out of range, just return NULL */ + name = NULL; + } + else if ((error_names == NULL) || (error_names[errnoval] == NULL)) + { + /* In range, but no error_names or no entry at this index. */ + sprintf (buf, "Error %d", errnoval); + name = buf; + } + else + { + /* In range, and a valid name. Just return the name. */ + name = error_names[errnoval]; + } + + return (name); +} + +/* + +NAME + + strtoerrno -- map a symbolic errno name to a numeric value + +SYNOPSIS + + int strtoerrno (char *name) + +DESCRIPTION + + Given the symbolic name of a error number, map it to an errno value. + If no translation is found, returns 0. + +*/ + +int +strtoerrno (name) + char *name; +{ + int errnoval = 0; + + if (name != NULL) + { + if (error_names == NULL) + { + init_error_tables (); + } + for (errnoval = 0; errnoval < num_error_names; errnoval++) + { + if ((error_names[errnoval] != NULL) && + (strcmp (name, error_names[errnoval]) == 0)) + { + break; + } + } + if (errnoval == num_error_names) + { + errnoval = 0; + } + } + return (errnoval); +} + + +/* A simple little main that does nothing but print all the errno translations + if MAIN is defined and this file is compiled and linked. */ + +#ifdef MAIN + +main () +{ + int errn; + int errnmax; + char *name; + char *msg; + char *strerrno (); + char *strerror (); + + errnmax = errno_max (); + printf ("%d entries in names table.\n", num_error_names); + printf ("%d entries in messages table.\n", sys_nerr); + printf ("%d is max useful index.\n", errnmax); + + /* Keep printing values until we get to the end of *both* tables, not + *either* table. Note that knowing the maximum useful index does *not* + relieve us of the responsibility of testing the return pointer for + NULL. */ + + for (errn = 0; errn <= errnmax; errn++) + { + name = strerrno (errn); + name = (name == NULL) ? "" : name; + msg = strerror (errn); + msg = (msg == NULL) ? "" : msg; + printf ("%-4d%-18s%s\n", errn, name, msg); + } +} + +#endif diff --git a/gnu/lib/libg++/libiberty/strsignal.c b/gnu/lib/libg++/libiberty/strsignal.c new file mode 100644 index 0000000000..83d9252b9f --- /dev/null +++ b/gnu/lib/libg++/libiberty/strsignal.c @@ -0,0 +1,623 @@ +/* Extended support for using signal values. + Copyright (C) 1992 Free Software Foundation, Inc. + Written by Fred Fish. fnf@cygnus.com + +This file is part of the libiberty library. +Libiberty is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +Libiberty is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with libiberty; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include "config.h" + +#include +#include + +/* Routines imported from standard C runtime libraries. */ + +#ifdef __STDC__ +#include +extern void *malloc (size_t size); /* 4.10.3.3 */ +extern void *memset (void *s, int c, size_t n); /* 4.11.6.1 */ +#else /* !__STDC__ */ +#ifndef const +#define const +#endif +extern char *malloc (); /* Standard memory allocater */ +extern char *memset (); +#endif /* __STDC__ */ + +#ifndef NULL +# ifdef __STDC__ +# define NULL (void *) 0 +# else +# define NULL 0 +# endif +#endif + +#ifndef MAX +# define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +/* Translation table for signal values. + + Note that this table is generally only accessed when it is used at runtime + to initialize signal name and message tables that are indexed by signal + value. + + Not all of these signals will exist on all systems. This table is the only + thing that should have to be updated as new signal numbers are introduced. + It's sort of ugly, but at least its portable. */ + +struct signal_info +{ + int value; /* The numeric value from */ + char *name; /* The equivalent symbolic value */ + char *msg; /* Short message about this value */ +}; + +static const struct signal_info signal_table[] = +{ +#if defined (SIGHUP) + SIGHUP, "SIGHUP", "Hangup", +#endif +#if defined (SIGINT) + SIGINT, "SIGINT", "Interrupt", +#endif +#if defined (SIGQUIT) + SIGQUIT, "SIGQUIT", "Quit", +#endif +#if defined (SIGILL) + SIGILL, "SIGILL", "Illegal instruction", +#endif +#if defined (SIGTRAP) + SIGTRAP, "SIGTRAP", "Trace/breakpoint trap", +#endif +/* Put SIGIOT before SIGABRT, so that if SIGIOT==SIGABRT then SIGABRT + overrides SIGIOT. SIGABRT is in ANSI and POSIX.1, and SIGIOT isn't. */ +#if defined (SIGIOT) + SIGIOT, "SIGIOT", "IOT trap", +#endif +#if defined (SIGABRT) + SIGABRT, "SIGABRT", "Aborted", +#endif +#if defined (SIGEMT) + SIGEMT, "SIGEMT", "Emulation trap", +#endif +#if defined (SIGFPE) + SIGFPE, "SIGFPE", "Arithmetic exception", +#endif +#if defined (SIGKILL) + SIGKILL, "SIGKILL", "Killed", +#endif +#if defined (SIGBUS) + SIGBUS, "SIGBUS", "Bus error", +#endif +#if defined (SIGSEGV) + SIGSEGV, "SIGSEGV", "Segmentation fault", +#endif +#if defined (SIGSYS) + SIGSYS, "SIGSYS", "Bad system call", +#endif +#if defined (SIGPIPE) + SIGPIPE, "SIGPIPE", "Broken pipe", +#endif +#if defined (SIGALRM) + SIGALRM, "SIGALRM", "Alarm clock", +#endif +#if defined (SIGTERM) + SIGTERM, "SIGTERM", "Terminated", +#endif +#if defined (SIGUSR1) + SIGUSR1, "SIGUSR1", "User defined signal 1", +#endif +#if defined (SIGUSR2) + SIGUSR2, "SIGUSR2", "User defined signal 2", +#endif +/* Put SIGCLD before SIGCHLD, so that if SIGCLD==SIGCHLD then SIGCHLD + overrides SIGCLD. SIGCHLD is in POXIX.1 */ +#if defined (SIGCLD) + SIGCLD, "SIGCLD", "Child status changed", +#endif +#if defined (SIGCHLD) + SIGCHLD, "SIGCHLD", "Child status changed", +#endif +#if defined (SIGPWR) + SIGPWR, "SIGPWR", "Power fail/restart", +#endif +#if defined (SIGWINCH) + SIGWINCH, "SIGWINCH", "Window size changed", +#endif +#if defined (SIGURG) + SIGURG, "SIGURG", "Urgent I/O condition", +#endif +#if defined (SIGIO) + /* "I/O pending has also been suggested, but is misleading since the + signal only happens when the process has asked for it, not everytime + I/O is pending. */ + SIGIO, "SIGIO", "I/O possible", +#endif +#if defined (SIGPOLL) + SIGPOLL, "SIGPOLL", "Pollable event occurred", +#endif +#if defined (SIGSTOP) + SIGSTOP, "SIGSTOP", "Stopped (signal)", +#endif +#if defined (SIGTSTP) + SIGTSTP, "SIGTSTP", "Stopped (user)", +#endif +#if defined (SIGCONT) + SIGCONT, "SIGCONT", "Continued", +#endif +#if defined (SIGTTIN) + SIGTTIN, "SIGTTIN", "Stopped (tty input)", +#endif +#if defined (SIGTTOU) + SIGTTOU, "SIGTTOU", "Stopped (tty output)", +#endif +#if defined (SIGVTALRM) + SIGVTALRM, "SIGVTALRM", "Virtual timer expired", +#endif +#if defined (SIGPROF) + SIGPROF, "SIGPROF", "Profiling timer expired", +#endif +#if defined (SIGXCPU) + SIGXCPU, "SIGXCPU", "CPU time limit exceeded", +#endif +#if defined (SIGXFSZ) + SIGXFSZ, "SIGXFSZ", "File size limit exceeded", +#endif +#if defined (SIGWIND) + SIGWIND, "SIGWIND", "SIGWIND", +#endif +#if defined (SIGPHONE) + SIGPHONE, "SIGPHONE", "SIGPHONE", +#endif +#if defined (SIGLOST) + SIGLOST, "SIGLOST", "Resource lost", +#endif +#if defined (SIGWAITING) + SIGWAITING, "SIGWAITING", "Process's LWPs are blocked", +#endif +#if defined (SIGLWP) + SIGLWP, "SIGLWP", "Signal LWP", +#endif +#if defined (SIGDANGER) + SIGDANGER, "SIGDANGER", "Swap space dangerously low", +#endif +#if defined (SIGGRANT) + SIGGRANT, "SIGGRANT", "Monitor mode granted", +#endif +#if defined (SIGRETRACT) + SIGRETRACT, "SIGRETRACT", "Need to relinguish monitor mode", +#endif +#if defined (SIGMSG) + SIGMSG, "SIGMSG", "Monitor mode data available", +#endif +#if defined (SIGSOUND) + SIGSOUND, "SIGSOUND", "Sound completed", +#endif +#if defined (SIGSAK) + SIGSAK, "SIGSAK", "Secure attention", +#endif + 0, NULL, NULL +}; + +/* Translation table allocated and initialized at runtime. Indexed by the + signal value to find the equivalent symbolic value. */ + +static char **signal_names; +static int num_signal_names = 0; + +/* Translation table allocated and initialized at runtime, if it does not + already exist in the host environment. Indexed by the signal value to find + the descriptive string. + + We don't export it for use in other modules because even though it has the + same name, it differs from other implementations in that it is dynamically + initialized rather than statically initialized. */ + +#ifdef NEED_sys_siglist + +static int sys_nsig; +static char **sys_siglist; + +#else + +static int sys_nsig = NSIG; +extern const char * const sys_siglist[]; + +#endif + + +/* + +NAME + + init_signal_tables -- initialize the name and message tables + +SYNOPSIS + + static void init_signal_tables (); + +DESCRIPTION + + Using the signal_table, which is initialized at compile time, generate + the signal_names and the sys_siglist (if needed) tables, which are + indexed at runtime by a specific signal value. + +BUGS + + The initialization of the tables may fail under low memory conditions, + in which case we don't do anything particularly useful, but we don't + bomb either. Who knows, it might succeed at a later point if we free + some memory in the meantime. In any case, the other routines know + how to deal with lack of a table after trying to initialize it. This + may or may not be considered to be a bug, that we don't specifically + warn about this particular failure mode. + +*/ + +static void +init_signal_tables () +{ + const struct signal_info *eip; + int nbytes; + + /* If we haven't already scanned the signal_table once to find the maximum + signal value, then go find it now. */ + + if (num_signal_names == 0) + { + for (eip = signal_table; eip -> name != NULL; eip++) + { + if (eip -> value >= num_signal_names) + { + num_signal_names = eip -> value + 1; + } + } + } + + /* Now attempt to allocate the signal_names table, zero it out, and then + initialize it from the statically initialized signal_table. */ + + if (signal_names == NULL) + { + nbytes = num_signal_names * sizeof (char *); + if ((signal_names = (char **) malloc (nbytes)) != NULL) + { + memset (signal_names, 0, nbytes); + for (eip = signal_table; eip -> name != NULL; eip++) + { + signal_names[eip -> value] = eip -> name; + } + } + } + +#ifdef NEED_sys_siglist + + /* Now attempt to allocate the sys_siglist table, zero it out, and then + initialize it from the statically initialized signal_table. */ + + if (sys_siglist == NULL) + { + nbytes = num_signal_names * sizeof (char *); + if ((sys_siglist = (char **) malloc (nbytes)) != NULL) + { + memset (sys_siglist, 0, nbytes); + sys_nsig = num_signal_names; + for (eip = signal_table; eip -> name != NULL; eip++) + { + sys_siglist[eip -> value] = eip -> msg; + } + } + } + +#endif + +} + + +/* + +NAME + + signo_max -- return the max signo value + +SYNOPSIS + + int signo_max (); + +DESCRIPTION + + Returns the maximum signo value for which a corresponding symbolic + name or message is available. Note that in the case where + we use the sys_siglist supplied by the system, it is possible for + there to be more symbolic names than messages, or vice versa. + In fact, the manual page for psignal(3b) explicitly warns that one + should check the size of the table (NSIG) before indexing it, + since new signal codes may be added to the system before they are + added to the table. Thus NSIG might be smaller than value + implied by the largest signo value defined in . + + We return the maximum value that can be used to obtain a meaningful + symbolic name or message. + +*/ + +int +signo_max () +{ + int maxsize; + + if (signal_names == NULL) + { + init_signal_tables (); + } + maxsize = MAX (sys_nsig, num_signal_names); + return (maxsize - 1); +} + + +/* + +NAME + + strsignal -- map a signal number to a signal message string + +SYNOPSIS + + char *strsignal (int signo) + +DESCRIPTION + + Maps an signal number to an signal message string, the contents of + which are implementation defined. On systems which have the external + variable sys_siglist, these strings will be the same as the ones used + by psignal(). + + If the supplied signal number is within the valid range of indices + for the sys_siglist, but no message is available for the particular + signal number, then returns the string "Signal NUM", where NUM is the + signal number. + + If the supplied signal number is not a valid index into sys_siglist, + returns NULL. + + The returned string is only guaranteed to be valid only until the + next call to strsignal. + +*/ + +char * +strsignal (signo) + int signo; +{ + char *msg; + static char buf[32]; + +#ifdef NEED_sys_siglist + + if (signal_names == NULL) + { + init_signal_tables (); + } + +#endif + + if ((signo < 0) || (signo >= sys_nsig)) + { + /* Out of range, just return NULL */ + msg = NULL; + } + else if ((sys_siglist == NULL) || (sys_siglist[signo] == NULL)) + { + /* In range, but no sys_siglist or no entry at this index. */ + sprintf (buf, "Signal %d", signo); + msg = buf; + } + else + { + /* In range, and a valid message. Just return the message. */ + msg = (char*)sys_siglist[signo]; + } + + return (msg); +} + + +/* + +NAME + + strsigno -- map an signal number to a symbolic name string + +SYNOPSIS + + char *strsigno (int signo) + +DESCRIPTION + + Given an signal number, returns a pointer to a string containing + the symbolic name of that signal number, as found in . + + If the supplied signal number is within the valid range of indices + for symbolic names, but no name is available for the particular + signal number, then returns the string "Signal NUM", where NUM is + the signal number. + + If the supplied signal number is not within the range of valid + indices, then returns NULL. + +BUGS + + The contents of the location pointed to are only guaranteed to be + valid until the next call to strsigno. + +*/ + +char * +strsigno (signo) + int signo; +{ + char *name; + static char buf[32]; + + if (signal_names == NULL) + { + init_signal_tables (); + } + + if ((signo < 0) || (signo >= num_signal_names)) + { + /* Out of range, just return NULL */ + name = NULL; + } + else if ((signal_names == NULL) || (signal_names[signo] == NULL)) + { + /* In range, but no signal_names or no entry at this index. */ + sprintf (buf, "Signal %d", signo); + name = buf; + } + else + { + /* In range, and a valid name. Just return the name. */ + name = signal_names[signo]; + } + + return (name); +} + + +/* + +NAME + + strtosigno -- map a symbolic signal name to a numeric value + +SYNOPSIS + + int strtosigno (char *name) + +DESCRIPTION + + Given the symbolic name of a signal, map it to a signal number. + If no translation is found, returns 0. + +*/ + +int +strtosigno (name) + char *name; +{ + int signo = 0; + + if (name != NULL) + { + if (signal_names == NULL) + { + init_signal_tables (); + } + for (signo = 0; signo < num_signal_names; signo++) + { + if ((signal_names[signo] != NULL) && + (strcmp (name, signal_names[signo]) == 0)) + { + break; + } + } + if (signo == num_signal_names) + { + signo = 0; + } + } + return (signo); +} + + +/* + +NAME + + psignal -- print message about signal to stderr + +SYNOPSIS + + void psignal (unsigned signo, char *message); + +DESCRIPTION + + Print to the standard error the message, followed by a colon, + followed by the description of the signal specified by signo, + followed by a newline. +*/ + +#ifdef NEED_psignal + +void +psignal (signo, message) + unsigned signo; + char *message; +{ + if (signal_names == NULL) + { + init_signal_tables (); + } + if ((signo <= 0) || (signo >= sys_nsig)) + { + fprintf (stderr, "%s: unknown signal\n", message); + } + else + { + fprintf (stderr, "%s: %s\n", message, sys_siglist[signo]); + } +} + +#endif /* NEED_psignal */ + + +/* A simple little main that does nothing but print all the signal translations + if MAIN is defined and this file is compiled and linked. */ + +#ifdef MAIN + +main () +{ + int signo; + int maxsigno; + char *name; + char *msg; + char *strsigno (); + char *strsignal (); + + maxsigno = signo_max (); + printf ("%d entries in names table.\n", num_signal_names); + printf ("%d entries in messages table.\n", sys_nsig); + printf ("%d is max useful index.\n", maxsigno); + + /* Keep printing values until we get to the end of *both* tables, not + *either* table. Note that knowing the maximum useful index does *not* + relieve us of the responsibility of testing the return pointer for + NULL. */ + + for (signo = 0; signo <= maxsigno; signo++) + { + name = strsigno (signo); + name = (name == NULL) ? "" : name; + msg = strsignal (signo); + msg = (msg == NULL) ? "" : msg; + printf ("%-4d%-18s%s\n", signo, name, msg); + } +} + +#endif diff --git a/gnu/lib/libmalloc/COPYING.LIB b/gnu/lib/libmalloc/COPYING.LIB new file mode 100644 index 0000000000..eb685a5ec9 --- /dev/null +++ b/gnu/lib/libmalloc/COPYING.LIB @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/gnu/lib/libmalloc/ChangeLog b/gnu/lib/libmalloc/ChangeLog new file mode 100644 index 0000000000..260c9797ab --- /dev/null +++ b/gnu/lib/libmalloc/ChangeLog @@ -0,0 +1,291 @@ +Wed Jun 2 17:45:38 1993 Roland McGrath (roland@churchy.gnu.ai.mit.edu) + + * malloc/Makefile (%.gz): Renamed target from %.z; use -v flag. + (malloc/ChangeLog): Use mv -f. + +Mon May 31 21:49:04 1993 Roland McGrath (roland@churchy.gnu.ai.mit.edu) + + * malloc/mtrace.c: #include ; malloc.h no longer does. + +Sun May 30 20:04:50 1993 Roland McGrath (roland@churchy.gnu.ai.mit.edu) + + * malloc/malloc.c (malloc): #if 0 out ``if (SIZE == 0) return NULL''. + + * malloc/malloc.h [_MALLOC_INTERNAL]: Don't include . + [_MALLOC_INTERNAL]: Move config.h, limits.h, and mem* to front of file. + (NULL): Move after stddef.h. + + * malloc/valloc.c: Don't include config.h; malloc.h already did. + + * malloc/malloc.c: Undo rms's change. + + * malloc/mcheck.c, malloc/malloc.h: Undo rms's change. + +Sat May 29 13:04:38 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) + + * malloc/malloc.c (malloc) [emacs]: If size is 0, make it 1. + + * malloc/malloc.h (CONST): Define this always, + rather than `const' sometimes. + (memory_warnings): Use CONST, not __const, in decl. + * malloc/mcheck.c (checkhdr): Use CONST, not const. + +Fri May 14 19:34:54 1993 Roland McGrath (roland@churchy.gnu.ai.mit.edu) + + * malloc/Makefile: Remove depend-malloc dependency on malloc/gmalloc.c. + +Wed May 12 19:43:37 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) + + * malloc/cfree.c: Put malloc.h include in _MALLOC_INTERNAL conditional. + +Wed May 12 16:24:23 1993 Roland McGrath (roland@geech.gnu.ai.mit.edu) + + * malloc/cfree.c: Include instead of . + [_LIBC]: Use function_alias only under this conditional. + [! _LIBC] (cfree): Define a function that just calls free. + +Mon May 10 16:56:09 1993 Jim Blandy (jimb@geech.gnu.ai.mit.edu) + + * malloc/cfree.c: Put the meat of the file inside a "#if + defined(__GNU_LIBRARY__)" clause, so that gmalloc.c, which + incorporates this file, can be used outside of the C library. + +Sun May 9 16:57:43 1993 Roland McGrath (roland@churchy.gnu.ai.mit.edu) + + * malloc/Makefile (malloc-dist): Add ChangeLog and mem-limits.h. + (malloc/ChangeLog): New rule using Noah's changelog-extract. + (malloc.tar): Use o flag to tar to make compatible archives. + (gmalloc-routines): Add calloc, valloc, and cfree. + (dist-routines): Remove [cv]alloc from here. + (routines): Remove cfree from here. + +Fri Mar 26 14:53:30 1993 Michael John Haertel (mike@skinner.cs.uoregon.edu) + + * malloc/malloc.c (malloc): Start searching at _heapindex, not + MALLOC_SEARCH_START. + * malloc/malloc.h (MALLOC_SEARCH_START): Macro removed. + * malloc/realloc.c (realloc): When malloc returns NULL, handle the + case of the block we need to unfree (which was just freed) having + been coalesced with its neighbors. + +Thu Mar 25 13:40:17 1993 Roland McGrath (roland@churchy.gnu.ai.mit.edu) + + * Rules (mostlyclean): Remove the .o files for $(tests) and $(others). + Remove $(objpfx)depend-$(subdir). + (clean): Remove $(extra-objs). + * Makefile (clean): Remove $(install-lib) from $(objdir). + Don't try to remove ansi/ and trad/ or dist.tar or lint.out. + Remove $(objpfx)depend-. + * time/Makefile (extra-objs): Define new var. + * malloc/Makefile (extra-objs): Likewise. + +Wed Mar 24 16:09:26 1993 Roland McGrath (roland@churchy.gnu.ai.mit.edu) + + * malloc/Makefile (malloc/%.c, malloc/%.h): Rules removed. + +Mon Mar 22 15:35:54 1993 Roland McGrath (roland@churchy.gnu.ai.mit.edu) + + * malloc/malloc.h [_MALLOC_INTERNAL] + [__GNU_LIBRARY__ || STDC_HEADERS || USG] (memmove): Define in + terms of bcopy. + * malloc/malloc/gmalloc-head.c: Redo previously undone change. + +Thu Mar 18 04:59:21 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) + + * malloc/malloc/gmalloc-head.c: Undo previous change. + +Tue Mar 9 11:32:35 1993 Jim Blandy (jimb@wookumz.gnu.ai.mit.edu) + + * malloc/malloc/gmalloc-head.c: Remove #definitions of memset, + memcpy, and memmove; this is taken care of by malloc.h anyway. + +Thu Feb 25 14:49:52 1993 Roland McGrath (roland@churchy.gnu.ai.mit.edu) + + * malloc/malloc.h [_MALLOC_INTERNAL]: Move #include to + front of file; it needs to come before size_t/ptrdiff_t frobnication. + +Mon Feb 22 12:19:19 1993 Roland McGrath (roland@churchy.gnu.ai.mit.edu) + + * malloc/dist-Makefile (gmalloc.c): Depend on Makefile. + + * malloc/Makefile (gmalloc-routines): New variable. + (dist-routines): Use it. + (malloc/Makefile): Also replace with + $(gmalloc-routines). + (malloc/Makefile): Make it unwritable to avoid accidental lossage. + Depend on Makefile. + + * malloc/dist-Makefile (gmalloc): New variable: . + (gmalloc.c): Use $(gmalloc), not $(sources). + Make the file unwritable to avoid accidental lossage. + + * malloc/mtrace.c: Don't #include because did + it for us. + + * malloc/valloc.c [! __GNU_LIBRARY__]: Replace hairy conditionals + with #include "getpagesize.h". + * malloc/Makefile (distribute, malloc-dist): Add getpagesize.h. + +Thu Feb 18 14:34:00 1993 Roland McGrath (roland@geech.gnu.ai.mit.edu) + + * malloc/mcheck.c (checkhdr): Use `const', not `__const'. + +Tue Dec 29 18:18:58 1992 Roland McGrath (roland@geech.gnu.ai.mit.edu) + + * malloc/Makefile (dist-headers): Define to malloc.h. + (headers): Replace malloc.h with $(dist-headers). + (malloc/Makefile): Use $(dist-headers) in place of $(headers). + (malloc-dist, distribute): Replace ChangeLog with OChangeLog. + + * malloc/dist-Makefile (malloc.tar{,.Z}): Depend on FORCE. + (FORCE): Define empty target. + +Tue Oct 27 18:11:19 1992 Roland McGrath (roland@geech.gnu.ai.mit.edu) + + * malloc/mcheck.c (mcheck): Set abortfunc to either FUNC or abort; + never leave it unchanged. + Return 0 if mcheck_used; -1 if not. + * malloc/malloc.h (mcheck): Change return type in decl. + +Thu Oct 15 19:25:46 1992 Roland McGrath (roland@geech.gnu.ai.mit.edu) + + * malloc/malloc.h (__after_morecore_hook): Declare new var. + * malloc/malloc.c (__after_morecore_hook): Define it. + (align): Call it. + +Mon Oct 12 15:56:07 1992 Roland McGrath (roland@geech.gnu.ai.mit.edu) + + * malloc/malloc.h (r_alloc, r_alloc_free, r_re_alloc): Declare. + + * malloc/Makefile (dist-routines): Add ralloc. + (gpl2lgpl): Add ralloc.c. + +Mon Oct 12 13:37:16 1992 Roland McGrath (roland@geech.gnu.ai.mit.edu) + + * malloc/malloc.h: Declare memory_warnings. + * malloc/Makefile (dist-routines): Add vm-limit. + (distribute): Add mem-limits.h. + (gpl2lgpl): Add vm-limit.c, mem-limits.h. + +Thu Aug 27 15:58:13 1992 Roland McGrath (roland@wookumz.gnu.ai.mit.edu) + + * malloc/malloc.h [! __STDC__] (ptrdiff_t): #define. + +Wed Aug 26 18:15:47 1992 Roland McGrath (roland@geech.gnu.ai.mit.edu) + + * malloc/free.c (__free): Rename to _free_internal. + (free), malloc/malloc.c (morecore): Change callers. + * malloc/malloc.h: Change decl. + +Tue Aug 18 17:38:13 1992 Roland McGrath (roland@geech.gnu.ai.mit.edu) + + * malloc/Makefile (obstack.%): Remove rule. + (gpl2lgpl): Define this instead. + * posix/Makefile (gpl2lgpl): Define to include getopt source files. + * Makerules ($(gpl2lgpl)): New rule to snarf code and frob its + copying notices. + +Tue Jul 7 03:11:23 1992 Roland McGrath (roland@churchy.gnu.ai.mit.edu) + + * malloc/Makefile (dist-routines): Define with routines for malloc.tar. + (routines): Define with that plus the rest. + (nodist): Remove. + (routines): Add obstack. + (headers): Add obstack.h. + (obstack.%): New rule. + +Thu Jun 25 21:01:40 1992 Roland McGrath (roland@geech.gnu.ai.mit.edu) + + * sysdeps/unix/morecore.c (__default_morecore): Deansideclized. + * malloc/*.c: Only #include #ifndef _MALLOC_INTERNAL. + +Thu Jun 4 16:41:56 1992 Roland McGrath (roland@geech.gnu.ai.mit.edu) + + * malloc/malloc.h (mtrace): Declare. + + * malloc/malloc.h, malloc/calloc.c, malloc/free.c, malloc/malloc.c, + malloc/mcheck.c, malloc/memalign.c, malloc/mstats.c, + malloc/mtrace.c, malloc/realloc.c, malloc/valloc.c: Deansideclized; + changed copyright notices to be independent of libc. + * malloc/Makefile (glob/%.c, glob/%.h): Don't need to ansideclificate. + +Fri May 22 01:52:04 1992 Roland McGrath (roland@geech.gnu.ai.mit.edu) + + * assert/assert.h, ctype/ctype.h, dirent/dirent.h, grp/grp.h, + locale/locale.h, locale/localeinfo.h, math/math.h, + misc/sys/file.h, misc/sys/ioctl.h, misc/sys/ptrace.h, + misc/sys/uio.h, misc/sgtty.h, misc/nlist.h, posix/gnu/types.h, + posix/sys/wait.h, posix/sys/types.h, posix/sys/times.h, + posix/sys/utsname.h, posix/unistd.h, posix/tar.h, posix/utime.h, + posix/wordexp.h, posix/glob.h, posix/fnmatch.h, pwd/pwd.h, + resource/sys/resource.h, resource/sys/vlimit.h, + resource/sys/vtimes.h, setjmp/setjmp.h, signal/signal.h, + signal/gnu/signal.h, socket/sys/socket.h, stdio/stdio.h, + stdio/printf.h, stdlib/alloca.h, stdlib/stdlib.h, string/string.h, + termios/termios.h, time/sys/time.h, time/time.h, io/sys/stat.h, + io/fcntl.h, errno.h, stddef.h, malloc/malloc.h: + Deansideclized. Use macros instead of ansidecl and + C++ cruft. + * features.h: #include . + +Sun May 17 15:50:00 1992 Roland McGrath (roland@albert.gnu.ai.mit.edu) + + * malloc/mtrace.c (old_{free,malloc,realloc}_hook): Renamed to tr_& + to not conflict with mcheck.c when combined into gmalloc.c. + +Tue Apr 28 19:25:21 1992 Roland McGrath (roland@albert.gnu.ai.mit.edu) + + * malloc/valloc.c [emacs]: #include "config.h" + +Thu Apr 23 13:55:34 1992 Roland McGrath (roland@albert.gnu.ai.mit.edu) + + * malloc/realloc.c, malloc/malloc.c: Don't #define memcpy or memset + if already #define'd. + +Tue Apr 21 04:16:56 1992 Roland McGrath (roland@geech.gnu.ai.mit.edu) + + * malloc/Makefile: Moved include ../Rules after malloc.tar rules. + They need $(routines), which Rules clears. + + * malloc/realloc.c (MIN): Renamed to min. Conflicted with HPUX + system header files. + +Tue Mar 17 17:31:06 1992 Roland McGrath (roland@churchy.gnu.ai.mit.edu) + + * malloc/dist-README: Changed mailing list addr to bug-glibc. + +Sun Mar 15 00:01:05 1992 Roland McGrath (roland@albert.gnu.ai.mit.edu) + + * malloc/Makefile (malloc/gmalloc.c): Depend on headers and sources. + +Fri Mar 13 17:20:19 1992 Roland McGrath (roland@wookumz.gnu.ai.mit.edu) + + * malloc/Makefile (libmcheck.a), Makefile (crt0.o): Remove target + first; don't use -f to ln. + +Tue Feb 25 01:42:16 1992 Roland McGrath (roland@wookumz.gnu.ai.mit.edu) + + * malloc/dist-README: Fixed mailing list addr. + +Mon Feb 17 05:04:00 1992 Roland McGrath (roland@wookumz.gnu.ai.mit.edu) + + * malloc/Makefile (distribute): Add mcheck-init.c. + +Fri Feb 14 01:52:12 1992 Roland McGrath (roland@wookumz.gnu.ai.mit.edu) + + * Makeconfig: Added comments describing objdir/Makeconfig and + editting Makeconfig. + (prefix, libdir, INSTALL, INSTALL_DATA): New variables for installing. + * Makerules (install): New target. + * Makefile (+subdir_targets): Add subdir_install. + (install): Depend on subdir_install. + (install-lib): Define variable to install libc.a and crt0.o. + * misc/Makefile (install-lib): Install bsd-compat. + * malloc/Makefile (install-lib): Install mcheck-init. + +Wed Feb 12 12:12:12 1992 Roland McGrath (roland@wookumz.gnu.ai.mit.edu) + + * malloc/mtrace.c: Use %p fmt for pointers. + + +Find older changes in OChangeLog. diff --git a/gnu/lib/libmalloc/Makefile b/gnu/lib/libmalloc/Makefile new file mode 100644 index 0000000000..463927d29b --- /dev/null +++ b/gnu/lib/libmalloc/Makefile @@ -0,0 +1,11 @@ +# $Id: Makefile,v 1.1 1993/09/24 13:03:14 rgrimes Exp $ + +CFLAGS+= -I${.CURDIR} + +LIB= gnumalloc +SRCS+= malloc.c cfree.c calloc.c morecore.c +SRCS+= memalign.c valloc.c mcheck.c mtrace.c mstats.c vm-limit.c +SRCS+= ralloc.c +NOMAN= noman + +.include diff --git a/gnu/lib/libmalloc/Makefile.gnu b/gnu/lib/libmalloc/Makefile.gnu new file mode 100644 index 0000000000..ec35f74dcb --- /dev/null +++ b/gnu/lib/libmalloc/Makefile.gnu @@ -0,0 +1,57 @@ +# Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. +# This file is part of the GNU C Library. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public License +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. + +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. + +# You should have received a copy of the GNU Library General Public +# License along with the GNU C Library; see the file COPYING.LIB. If +# not, write to the Free Software Foundation, Inc., 675 Mass Ave, +# Cambridge, MA 02139, USA. + +# Makefile for standalone distribution of malloc. + +# Use this on System V. +#CPPFLAGS = -DUSG + +.PHONY: all +all: libmalloc.a gmalloc.o + +gmalloc = malloc.c free.c cfree.c realloc.c calloc.c morecore.c memalign.c valloc.c +sources = malloc.c free.c cfree.c realloc.c calloc.c morecore.c memalign.c valloc.c mcheck.c mtrace.c mstats.c vm-limit.c ralloc.c +objects = malloc.o free.o cfree.o realloc.o calloc.o morecore.o memalign.o valloc.o mcheck.o mtrace.o mstats.o vm-limit.o ralloc.o +headers = malloc.h + +libmalloc.a: $(objects) + ar crv $@ $(objects) + ranlib $@ + +$(objects): $(headers) + +gmalloc.c: gmalloc-head.c $(headers) $(gmalloc) Makefile + cat gmalloc-head.c $(headers) $(gmalloc) > $@-tmp + mv -f $@-tmp $@ +# Make it unwritable to avoid accidentally changing the file, +# since it is generated and any changes would be lost. + chmod a-w $@ + +.c.o: + $(CC) $(CFLAGS) $(CPPFLAGS) -I. -c $< $(OUTPUT_OPTION) + +.PHONY: clean realclean malloc-clean malloc-realclean +clean malloc-clean: + -rm -f libmalloc.a *.o core +realclean malloc-realclean: clean + -rm -f TAGS tags *~ + +# For inside the C library. +malloc.tar malloc.tar.Z: FORCE + $(MAKE) -C .. $@ +FORCE: diff --git a/gnu/lib/libmalloc/OChangeLog b/gnu/lib/libmalloc/OChangeLog new file mode 100644 index 0000000000..452aa53b32 --- /dev/null +++ b/gnu/lib/libmalloc/OChangeLog @@ -0,0 +1,34 @@ + **** All newer entries are in the C library ChangeLog file. **** + +Thu Jul 11 18:15:04 1991 Roland McGrath (roland@churchy.gnu.ai.mit.edu) + + * Merged with C library version, which now has its own subdir. + * malloc.h, *.c: Use ansideclisms and #ifdefs for portability both + in and out of the C library. + * Makefile: New makefile for malloc subdir in libc. + Has targets to create malloc.tar{,.Z} by ansidecl processing on srcs. + * malloc/Makefile: New file; Makefile for standalone distribution. + * malloc/README: New file; info for same. + +Fri Apr 6 00:18:36 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * Makefile: Add comments. + +Thu Apr 5 23:08:14 1990 Mike Haertel (mike at albert.ai.mit.edu) + + * mcheck.c (mcheck, checkhdr): Support user-supplied abort() + function. + * malloc.h: Declare __free(). + * Makefile: New target libmalloc.a. + +Thu Apr 5 21:56:03 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * free.c (free): Split into free and __free. + * malloc.c (morecore): Call __free on oldinfo. + +Local Variables: +mode: indented-text +left-margin: 8 +fill-column: 76 +version-control: never +End: diff --git a/gnu/lib/libmalloc/README b/gnu/lib/libmalloc/README new file mode 100644 index 0000000000..b5655c9282 --- /dev/null +++ b/gnu/lib/libmalloc/README @@ -0,0 +1,12 @@ +This is the standalone distribution of GNU malloc. +GNU malloc is part of the GNU C Library, but is also distributed separately. + +If you find bugs in GNU malloc, send reports to bug-glibc@prep.ai.mit.edu. + +GNU malloc is free software. See the file COPYING.LIB for copying conditions. + +The makefile builds libmalloc.a and gmalloc.o. If you are using GNU malloc +to replace your system's existing malloc package, it is important to make +sure you get all GNU functions, not some of the GNU functions and some from +the system library. gmalloc.o has all the functions in one file, so using +that will make sure you don't accidentally mix the two malloc packages. diff --git a/gnu/lib/libmalloc/VERSION b/gnu/lib/libmalloc/VERSION new file mode 100644 index 0000000000..c07e0806aa --- /dev/null +++ b/gnu/lib/libmalloc/VERSION @@ -0,0 +1,2 @@ +this version of GNU malloc was obtained from prep.ai.mit.edu on +9/22/1993. There was no version noted. diff --git a/gnu/lib/libmalloc/calloc.c b/gnu/lib/libmalloc/calloc.c new file mode 100644 index 0000000000..f870e94480 --- /dev/null +++ b/gnu/lib/libmalloc/calloc.c @@ -0,0 +1,39 @@ +/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +#ifndef _MALLOC_INTERNAL +#define _MALLOC_INTERNAL +#include +#endif + +/* Allocate an array of NMEMB elements each SIZE bytes long. + The entire array is initialized to zeros. */ +__ptr_t +calloc (nmemb, size) + register size_t nmemb; + register size_t size; +{ + register __ptr_t result = malloc (nmemb * size); + + if (result != NULL) + (void) memset (result, 0, nmemb * size); + + return result; +} diff --git a/gnu/lib/libmalloc/cfree.c b/gnu/lib/libmalloc/cfree.c new file mode 100644 index 0000000000..adc1ff698e --- /dev/null +++ b/gnu/lib/libmalloc/cfree.c @@ -0,0 +1,43 @@ +/* Copyright (C) 1991, 1993 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#ifndef _MALLOC_INTERNAL +#define _MALLOC_INTERNAL +#include +#endif + +#undef cfree + +#ifdef _LIBC + +#include +#include + +function_alias(cfree, free, void, (ptr), + DEFUN(cfree, (ptr), PTR ptr)) + +#else + +void +cfree (ptr) + __ptr_t ptr; +{ + free (ptr); +} + +#endif diff --git a/gnu/lib/libmalloc/getpagesize.h b/gnu/lib/libmalloc/getpagesize.h new file mode 100644 index 0000000000..2d43f262c7 --- /dev/null +++ b/gnu/lib/libmalloc/getpagesize.h @@ -0,0 +1,38 @@ +#ifdef BSD +#ifndef BSD4_1 +#define HAVE_GETPAGESIZE +#endif +#endif + +#ifndef HAVE_GETPAGESIZE + +#ifdef VMS +#define getpagesize() 512 +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef _SC_PAGESIZE +#define getpagesize() sysconf(_SC_PAGESIZE) +#else + +#include + +#ifdef EXEC_PAGESIZE +#define getpagesize() EXEC_PAGESIZE +#else +#ifdef NBPG +#define getpagesize() NBPG * CLSIZE +#ifndef CLSIZE +#define CLSIZE 1 +#endif /* no CLSIZE */ +#else /* no NBPG */ +#define getpagesize() NBPC +#endif /* no NBPG */ +#endif /* no EXEC_PAGESIZE */ +#endif /* no _SC_PAGESIZE */ + +#endif /* not HAVE_GETPAGESIZE */ + diff --git a/gnu/lib/libmalloc/gmalloc-head.c b/gnu/lib/libmalloc/gmalloc-head.c new file mode 100644 index 0000000000..e5f82c3e0d --- /dev/null +++ b/gnu/lib/libmalloc/gmalloc-head.c @@ -0,0 +1,6 @@ +/* DO NOT EDIT THIS FILE -- it is automagically generated. -*- C -*- */ + +#define _MALLOC_INTERNAL + +/* The malloc headers and source files from the C library follow here. */ + diff --git a/gnu/lib/libmalloc/malloc.c b/gnu/lib/libmalloc/malloc.c new file mode 100644 index 0000000000..3c1ee9943d --- /dev/null +++ b/gnu/lib/libmalloc/malloc.c @@ -0,0 +1,622 @@ +/* Memory allocator `malloc'. + Copyright 1990, 1991, 1992, 1993 Free Software Foundation + Written May 1989 by Mike Haertel. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +#ifndef _MALLOC_INTERNAL +#define _MALLOC_INTERNAL +#include +#endif + +/* How to really get more memory. */ +__ptr_t (*__morecore) __P ((ptrdiff_t __size)) = __default_morecore; + +/* Debugging hook for `malloc'. */ +__ptr_t (*__malloc_hook) __P ((size_t __size)); + +/* Pointer to the base of the first block. */ +char *_heapbase; + +/* Block information table. Allocated with align/__free (not malloc/free). */ +malloc_info *_heapinfo; + +/* Number of info entries. */ +static size_t heapsize; + +/* Search index in the info table. */ +size_t _heapindex; + +/* Limit of valid info table indices. */ +size_t _heaplimit; + +/* Free lists for each fragment size. */ +struct list _fraghead[BLOCKLOG]; + +/* Instrumentation. */ +size_t _chunks_used; +size_t _bytes_used; +size_t _chunks_free; +size_t _bytes_free; + +/* Are you experienced? */ +int __malloc_initialized; + +void (*__after_morecore_hook) __P ((void)); + +/* Aligned allocation. */ +static __ptr_t align __P ((size_t)); +static __ptr_t +align (size) + size_t size; +{ + __ptr_t result; + unsigned long int adj; + + result = (*__morecore) (size); + adj = (unsigned long int) ((unsigned long int) ((char *) result - + (char *) NULL)) % BLOCKSIZE; + if (adj != 0) + { + adj = BLOCKSIZE - adj; + (void) (*__morecore) (adj); + result = (char *) result + adj; + } + + if (__after_morecore_hook) + (*__after_morecore_hook) (); + + return result; +} + +/* Set everything up and remember that we have. */ +static int initialize __P ((void)); +static int +initialize () +{ + heapsize = HEAP / BLOCKSIZE; + _heapinfo = (malloc_info *) align (heapsize * sizeof (malloc_info)); + if (_heapinfo == NULL) + return 0; + memset (_heapinfo, 0, heapsize * sizeof (malloc_info)); + _heapinfo[0].free.size = 0; + _heapinfo[0].free.next = _heapinfo[0].free.prev = 0; + _heapindex = 0; + _heapbase = (char *) _heapinfo; + __malloc_initialized = 1; + return 1; +} + +/* Get neatly aligned memory, initializing or + growing the heap info table as necessary. */ +static __ptr_t morecore __P ((size_t)); +static __ptr_t +morecore (size) + size_t size; +{ + __ptr_t result; + malloc_info *newinfo, *oldinfo; + size_t newsize; + + result = align (size); + if (result == NULL) + return NULL; + + /* Check if we need to grow the info table. */ + if ((size_t) BLOCK ((char *) result + size) > heapsize) + { + newsize = heapsize; + while ((size_t) BLOCK ((char *) result + size) > newsize) + newsize *= 2; + newinfo = (malloc_info *) align (newsize * sizeof (malloc_info)); + if (newinfo == NULL) + { + (*__morecore) (-size); + return NULL; + } + memset (newinfo, 0, newsize * sizeof (malloc_info)); + memcpy (newinfo, _heapinfo, heapsize * sizeof (malloc_info)); + oldinfo = _heapinfo; + newinfo[BLOCK (oldinfo)].busy.type = 0; + newinfo[BLOCK (oldinfo)].busy.info.size + = BLOCKIFY (heapsize * sizeof (malloc_info)); + _heapinfo = newinfo; + _free_internal (oldinfo); + heapsize = newsize; + } + + _heaplimit = BLOCK ((char *) result + size); + return result; +} + +/* Allocate memory from the heap. */ +__ptr_t +malloc (size) + size_t size; +{ + __ptr_t result; + size_t block, blocks, lastblocks, start; + register size_t i; + struct list *next; + + /* ANSI C allows `malloc (0)' to either return NULL, or to return a + valid address you can realloc and free (though not dereference). + + It turns out that some extant code (sunrpc, at least Ultrix's version) + expects `malloc (0)' to return non-NULL and breaks otherwise. + Be compatible. */ + +#if 0 + if (size == 0) + return NULL; +#endif + + if (__malloc_hook != NULL) + return (*__malloc_hook) (size); + + if (!__malloc_initialized) + if (!initialize ()) + return NULL; + + if (size < sizeof (struct list)) + size = sizeof (struct list); + + /* Determine the allocation policy based on the request size. */ + if (size <= BLOCKSIZE / 2) + { + /* Small allocation to receive a fragment of a block. + Determine the logarithm to base two of the fragment size. */ + register size_t log = 1; + --size; + while ((size /= 2) != 0) + ++log; + + /* Look in the fragment lists for a + free fragment of the desired size. */ + next = _fraghead[log].next; + if (next != NULL) + { + /* There are free fragments of this size. + Pop a fragment out of the fragment list and return it. + Update the block's nfree and first counters. */ + result = (__ptr_t) next; + next->prev->next = next->next; + if (next->next != NULL) + next->next->prev = next->prev; + block = BLOCK (result); + if (--_heapinfo[block].busy.info.frag.nfree != 0) + _heapinfo[block].busy.info.frag.first = (unsigned long int) + ((unsigned long int) ((char *) next->next - (char *) NULL) + % BLOCKSIZE) >> log; + + /* Update the statistics. */ + ++_chunks_used; + _bytes_used += 1 << log; + --_chunks_free; + _bytes_free -= 1 << log; + } + else + { + /* No free fragments of the desired size, so get a new block + and break it into fragments, returning the first. */ + result = malloc (BLOCKSIZE); + if (result == NULL) + return NULL; + + /* Link all fragments but the first into the free list. */ + for (i = 1; i < (size_t) (BLOCKSIZE >> log); ++i) + { + next = (struct list *) ((char *) result + (i << log)); + next->next = _fraghead[log].next; + next->prev = &_fraghead[log]; + next->prev->next = next; + if (next->next != NULL) + next->next->prev = next; + } + + /* Initialize the nfree and first counters for this block. */ + block = BLOCK (result); + _heapinfo[block].busy.type = log; + _heapinfo[block].busy.info.frag.nfree = i - 1; + _heapinfo[block].busy.info.frag.first = i - 1; + + _chunks_free += (BLOCKSIZE >> log) - 1; + _bytes_free += BLOCKSIZE - (1 << log); + _bytes_used -= BLOCKSIZE - (1 << log); + } + } + else + { + /* Large allocation to receive one or more blocks. + Search the free list in a circle starting at the last place visited. + If we loop completely around without finding a large enough + space we will have to get more memory from the system. */ + blocks = BLOCKIFY (size); + start = block = _heapindex; + while (_heapinfo[block].free.size < blocks) + { + block = _heapinfo[block].free.next; + if (block == start) + { + /* Need to get more from the system. Check to see if + the new core will be contiguous with the final free + block; if so we don't need to get as much. */ + block = _heapinfo[0].free.prev; + lastblocks = _heapinfo[block].free.size; + if (_heaplimit != 0 && block + lastblocks == _heaplimit && + (*__morecore) (0) == ADDRESS (block + lastblocks) && + (morecore ((blocks - lastblocks) * BLOCKSIZE)) != NULL) + { + _heapinfo[block].free.size = blocks; + _bytes_free += (blocks - lastblocks) * BLOCKSIZE; + continue; + } + result = morecore (blocks * BLOCKSIZE); + if (result == NULL) + return NULL; + block = BLOCK (result); + _heapinfo[block].busy.type = 0; + _heapinfo[block].busy.info.size = blocks; + ++_chunks_used; + _bytes_used += blocks * BLOCKSIZE; + return result; + } + } + + /* At this point we have found a suitable free list entry. + Figure out how to remove what we need from the list. */ + result = ADDRESS (block); + if (_heapinfo[block].free.size > blocks) + { + /* The block we found has a bit left over, + so relink the tail end back into the free list. */ + _heapinfo[block + blocks].free.size + = _heapinfo[block].free.size - blocks; + _heapinfo[block + blocks].free.next + = _heapinfo[block].free.next; + _heapinfo[block + blocks].free.prev + = _heapinfo[block].free.prev; + _heapinfo[_heapinfo[block].free.prev].free.next + = _heapinfo[_heapinfo[block].free.next].free.prev + = _heapindex = block + blocks; + } + else + { + /* The block exactly matches our requirements, + so just remove it from the list. */ + _heapinfo[_heapinfo[block].free.next].free.prev + = _heapinfo[block].free.prev; + _heapinfo[_heapinfo[block].free.prev].free.next + = _heapindex = _heapinfo[block].free.next; + --_chunks_free; + } + + _heapinfo[block].busy.type = 0; + _heapinfo[block].busy.info.size = blocks; + ++_chunks_used; + _bytes_used += blocks * BLOCKSIZE; + _bytes_free -= blocks * BLOCKSIZE; + } + + return result; +} + +#define min(A, B) ((A) < (B) ? (A) : (B)) + +/* Debugging hook for realloc. */ +__ptr_t (*__realloc_hook) __P ((__ptr_t __ptr, size_t __size)); + +/* Resize the given region to the new size, returning a pointer + to the (possibly moved) region. This is optimized for speed; + some benchmarks seem to indicate that greater compactness is + achieved by unconditionally allocating and copying to a + new region. This module has incestuous knowledge of the + internals of both free and malloc. */ +__ptr_t +realloc (ptr, size) + __ptr_t ptr; + size_t size; +{ + __ptr_t result; + int type; + size_t block, blocks, oldlimit; + + if (size == 0) + { + free (ptr); + return malloc (0); + } + else if (ptr == NULL) + return malloc (size); + + if (__realloc_hook != NULL) + return (*__realloc_hook) (ptr, size); + + block = BLOCK (ptr); + + type = _heapinfo[block].busy.type; + switch (type) + { + case 0: + /* Maybe reallocate a large block to a small fragment. */ + if (size <= BLOCKSIZE / 2) + { + result = malloc (size); + if (result != NULL) + { + memcpy (result, ptr, size); + free (ptr); + return result; + } + } + + /* The new size is a large allocation as well; + see if we can hold it in place. */ + blocks = BLOCKIFY (size); + if (blocks < _heapinfo[block].busy.info.size) + { + /* The new size is smaller; return + excess memory to the free list. */ + _heapinfo[block + blocks].busy.type = 0; + _heapinfo[block + blocks].busy.info.size + = _heapinfo[block].busy.info.size - blocks; + _heapinfo[block].busy.info.size = blocks; + free (ADDRESS (block + blocks)); + result = ptr; + } + else if (blocks == _heapinfo[block].busy.info.size) + /* No size change necessary. */ + result = ptr; + else + { + /* Won't fit, so allocate a new region that will. + Free the old region first in case there is sufficient + adjacent free space to grow without moving. */ + blocks = _heapinfo[block].busy.info.size; + /* Prevent free from actually returning memory to the system. */ + oldlimit = _heaplimit; + _heaplimit = 0; + free (ptr); + _heaplimit = oldlimit; + result = malloc (size); + if (result == NULL) + { + /* Now we're really in trouble. We have to unfree + the thing we just freed. Unfortunately it might + have been coalesced with its neighbors. */ + if (_heapindex == block) + (void) malloc (blocks * BLOCKSIZE); + else + { + __ptr_t previous = malloc ((block - _heapindex) * BLOCKSIZE); + (void) malloc (blocks * BLOCKSIZE); + free (previous); + } + return NULL; + } + if (ptr != result) + memmove (result, ptr, blocks * BLOCKSIZE); + } + break; + + default: + /* Old size is a fragment; type is logarithm + to base two of the fragment size. */ + if (size > (size_t) (1 << (type - 1)) && size <= (size_t) (1 << type)) + /* The new size is the same kind of fragment. */ + result = ptr; + else + { + /* The new size is different; allocate a new space, + and copy the lesser of the new size and the old. */ + result = malloc (size); + if (result == NULL) + return NULL; + memcpy (result, ptr, min (size, (size_t) 1 << type)); + free (ptr); + } + break; + } + + return result; +} + +/* Debugging hook for free. */ +void (*__free_hook) __P ((__ptr_t __ptr)); + +/* List of blocks allocated by memalign. */ +struct alignlist *_aligned_blocks = NULL; + +/* Return memory to the heap. + Like `free' but don't call a __free_hook if there is one. */ +void +_free_internal (ptr) + __ptr_t ptr; +{ + int type; + size_t block, blocks; + register size_t i; + struct list *prev, *next; + + block = BLOCK (ptr); + + type = _heapinfo[block].busy.type; + switch (type) + { + case 0: + /* Get as many statistics as early as we can. */ + --_chunks_used; + _bytes_used -= _heapinfo[block].busy.info.size * BLOCKSIZE; + _bytes_free += _heapinfo[block].busy.info.size * BLOCKSIZE; + + /* Find the free cluster previous to this one in the free list. + Start searching at the last block referenced; this may benefit + programs with locality of allocation. */ + i = _heapindex; + if (i > block) + while (i > block) + i = _heapinfo[i].free.prev; + else + { + do + i = _heapinfo[i].free.next; + while (i > 0 && i < block); + i = _heapinfo[i].free.prev; + } + + /* Determine how to link this block into the free list. */ + if (block == i + _heapinfo[i].free.size) + { + /* Coalesce this block with its predecessor. */ + _heapinfo[i].free.size += _heapinfo[block].busy.info.size; + block = i; + } + else + { + /* Really link this block back into the free list. */ + _heapinfo[block].free.size = _heapinfo[block].busy.info.size; + _heapinfo[block].free.next = _heapinfo[i].free.next; + _heapinfo[block].free.prev = i; + _heapinfo[i].free.next = block; + _heapinfo[_heapinfo[block].free.next].free.prev = block; + ++_chunks_free; + } + + /* Now that the block is linked in, see if we can coalesce it + with its successor (by deleting its successor from the list + and adding in its size). */ + if (block + _heapinfo[block].free.size == _heapinfo[block].free.next) + { + _heapinfo[block].free.size + += _heapinfo[_heapinfo[block].free.next].free.size; + _heapinfo[block].free.next + = _heapinfo[_heapinfo[block].free.next].free.next; + _heapinfo[_heapinfo[block].free.next].free.prev = block; + --_chunks_free; + } + + /* Now see if we can return stuff to the system. */ + blocks = _heapinfo[block].free.size; + if (blocks >= FINAL_FREE_BLOCKS && block + blocks == _heaplimit + && (*__morecore) (0) == ADDRESS (block + blocks)) + { + register size_t bytes = blocks * BLOCKSIZE; + _heaplimit -= blocks; + (*__morecore) (-bytes); + _heapinfo[_heapinfo[block].free.prev].free.next + = _heapinfo[block].free.next; + _heapinfo[_heapinfo[block].free.next].free.prev + = _heapinfo[block].free.prev; + block = _heapinfo[block].free.prev; + --_chunks_free; + _bytes_free -= bytes; + } + + /* Set the next search to begin at this block. */ + _heapindex = block; + break; + + default: + /* Do some of the statistics. */ + --_chunks_used; + _bytes_used -= 1 << type; + ++_chunks_free; + _bytes_free += 1 << type; + + /* Get the address of the first free fragment in this block. */ + prev = (struct list *) ((char *) ADDRESS (block) + + (_heapinfo[block].busy.info.frag.first << type)); + + if (_heapinfo[block].busy.info.frag.nfree == (BLOCKSIZE >> type) - 1) + { + /* If all fragments of this block are free, remove them + from the fragment list and free the whole block. */ + next = prev; + for (i = 1; i < (size_t) (BLOCKSIZE >> type); ++i) + next = next->next; + prev->prev->next = next; + if (next != NULL) + next->prev = prev->prev; + _heapinfo[block].busy.type = 0; + _heapinfo[block].busy.info.size = 1; + + /* Keep the statistics accurate. */ + ++_chunks_used; + _bytes_used += BLOCKSIZE; + _chunks_free -= BLOCKSIZE >> type; + _bytes_free -= BLOCKSIZE; + + free (ADDRESS (block)); + } + else if (_heapinfo[block].busy.info.frag.nfree != 0) + { + /* If some fragments of this block are free, link this + fragment into the fragment list after the first free + fragment of this block. */ + next = (struct list *) ptr; + next->next = prev->next; + next->prev = prev; + prev->next = next; + if (next->next != NULL) + next->next->prev = next; + ++_heapinfo[block].busy.info.frag.nfree; + } + else + { + /* No fragments of this block are free, so link this + fragment into the fragment list and announce that + it is the first free fragment of this block. */ + prev = (struct list *) ptr; + _heapinfo[block].busy.info.frag.nfree = 1; + _heapinfo[block].busy.info.frag.first = (unsigned long int) + ((unsigned long int) ((char *) ptr - (char *) NULL) + % BLOCKSIZE >> type); + prev->next = _fraghead[type].next; + prev->prev = &_fraghead[type]; + prev->prev->next = prev; + if (prev->next != NULL) + prev->next->prev = prev; + } + break; + } +} + +/* Return memory to the heap. */ +void +free (ptr) + __ptr_t ptr; +{ + register struct alignlist *l; + + if (ptr == NULL) + return; + + for (l = _aligned_blocks; l != NULL; l = l->next) + if (l->aligned == ptr) + { + l->aligned = NULL; /* Mark the slot in the list as free. */ + ptr = l->exact; + break; + } + + if (__free_hook != NULL) + (*__free_hook) (ptr); + else + _free_internal (ptr); +} diff --git a/gnu/lib/libmalloc/malloc.h b/gnu/lib/libmalloc/malloc.h new file mode 100644 index 0000000000..daf40f2e05 --- /dev/null +++ b/gnu/lib/libmalloc/malloc.h @@ -0,0 +1,261 @@ +/* Declarations for `malloc' and friends. + Copyright 1990, 1991, 1992, 1993 Free Software Foundation, Inc. + Written May 1989 by Mike Haertel. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +#ifndef _MALLOC_H + +#define _MALLOC_H 1 + +#ifdef _MALLOC_INTERNAL + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(_LIBC) || defined(STDC_HEADERS) || defined(USG) +#include +#else +#ifndef memset +#define memset(s, zero, n) bzero ((s), (n)) +#endif +#ifndef memcpy +#define memcpy(d, s, n) bcopy ((s), (d), (n)) +#endif +#ifndef memmove +#define memmove(d, s, n) bcopy ((s), (d), (n)) +#endif +#endif + +#if defined(__GNU_LIBRARY__) || defined(__STDC__) +#include +#else +#define CHAR_BIT 8 +#endif + +#endif /* _MALLOC_INTERNAL. */ + + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if defined (__cplusplus) || (defined (__STDC__) && __STDC__) +#undef __P +#define __P(args) args +#undef __ptr_t +#define __ptr_t void * +#else /* Not C++ or ANSI C. */ +#undef __P +#define __P(args) () +#undef const +#define const +#undef __ptr_t +#define __ptr_t char * +#endif /* C++ or ANSI C. */ + +#ifdef __STDC__ +#include +#else +#undef size_t +#define size_t unsigned int +#undef ptrdiff_t +#define ptrdiff_t int +#endif + +#ifndef NULL +#define NULL 0 +#endif + + +/* Allocate SIZE bytes of memory. */ +extern __ptr_t malloc __P ((size_t __size)); +/* Re-allocate the previously allocated block + in __ptr_t, making the new block SIZE bytes long. */ +extern __ptr_t realloc __P ((__ptr_t __ptr, size_t __size)); +/* Allocate NMEMB elements of SIZE bytes each, all initialized to 0. */ +extern __ptr_t calloc __P ((size_t __nmemb, size_t __size)); +/* Free a block allocated by `malloc', `realloc' or `calloc'. */ +extern void free __P ((__ptr_t __ptr)); + +/* Allocate SIZE bytes allocated to ALIGNMENT bytes. */ +extern __ptr_t memalign __P ((size_t __alignment, size_t __size)); + +/* Allocate SIZE bytes on a page boundary. */ +extern __ptr_t valloc __P ((size_t __size)); + + +#ifdef _MALLOC_INTERNAL + +/* The allocator divides the heap into blocks of fixed size; large + requests receive one or more whole blocks, and small requests + receive a fragment of a block. Fragment sizes are powers of two, + and all fragments of a block are the same size. When all the + fragments in a block have been freed, the block itself is freed. */ +#define INT_BIT (CHAR_BIT * sizeof(int)) +#define BLOCKLOG (INT_BIT > 16 ? 12 : 9) +#define BLOCKSIZE (1 << BLOCKLOG) +#define BLOCKIFY(SIZE) (((SIZE) + BLOCKSIZE - 1) / BLOCKSIZE) + +/* Determine the amount of memory spanned by the initial heap table + (not an absolute limit). */ +#define HEAP (INT_BIT > 16 ? 4194304 : 65536) + +/* Number of contiguous free blocks allowed to build up at the end of + memory before they will be returned to the system. */ +#define FINAL_FREE_BLOCKS 8 + +/* Data structure giving per-block information. */ +typedef union + { + /* Heap information for a busy block. */ + struct + { + /* Zero for a large block, or positive giving the + logarithm to the base two of the fragment size. */ + int type; + union + { + struct + { + size_t nfree; /* Free fragments in a fragmented block. */ + size_t first; /* First free fragment of the block. */ + } frag; + /* Size (in blocks) of a large cluster. */ + size_t size; + } info; + } busy; + /* Heap information for a free block + (that may be the first of a free cluster). */ + struct + { + size_t size; /* Size (in blocks) of a free cluster. */ + size_t next; /* Index of next free cluster. */ + size_t prev; /* Index of previous free cluster. */ + } free; + } malloc_info; + +/* Pointer to first block of the heap. */ +extern char *_heapbase; + +/* Table indexed by block number giving per-block information. */ +extern malloc_info *_heapinfo; + +/* Address to block number and vice versa. */ +#define BLOCK(A) (((char *) (A) - _heapbase) / BLOCKSIZE + 1) +#define ADDRESS(B) ((__ptr_t) (((B) - 1) * BLOCKSIZE + _heapbase)) + +/* Current search index for the heap table. */ +extern size_t _heapindex; + +/* Limit of valid info table indices. */ +extern size_t _heaplimit; + +/* Doubly linked lists of free fragments. */ +struct list + { + struct list *next; + struct list *prev; + }; + +/* Free list headers for each fragment size. */ +extern struct list _fraghead[]; + +/* List of blocks allocated with `memalign' (or `valloc'). */ +struct alignlist + { + struct alignlist *next; + __ptr_t aligned; /* The address that memaligned returned. */ + __ptr_t exact; /* The address that malloc returned. */ + }; +extern struct alignlist *_aligned_blocks; + +/* Instrumentation. */ +extern size_t _chunks_used; +extern size_t _bytes_used; +extern size_t _chunks_free; +extern size_t _bytes_free; + +/* Internal version of `free' used in `morecore' (malloc.c). */ +extern void _free_internal __P ((__ptr_t __ptr)); + +#endif /* _MALLOC_INTERNAL. */ + +/* Underlying allocation function; successive calls should + return contiguous pieces of memory. */ +extern __ptr_t (*__morecore) __P ((ptrdiff_t __size)); + +/* Default value of `__morecore'. */ +extern __ptr_t __default_morecore __P ((ptrdiff_t __size)); + +/* If not NULL, this function is called after each time + `__morecore' is called to increase the data size. */ +extern void (*__after_morecore_hook) __P ((void)); + +/* Nonzero if `malloc' has been called and done its initialization. */ +extern int __malloc_initialized; + +/* Hooks for debugging versions. */ +extern void (*__free_hook) __P ((__ptr_t __ptr)); +extern __ptr_t (*__malloc_hook) __P ((size_t __size)); +extern __ptr_t (*__realloc_hook) __P ((__ptr_t __ptr, size_t __size)); + +/* Activate a standard collection of debugging hooks. */ +extern int mcheck __P ((void (*__func) __P ((void)))); + +/* Activate a standard collection of tracing hooks. */ +extern void mtrace __P ((void)); + +/* Statistics available to the user. */ +struct mstats + { + size_t bytes_total; /* Total size of the heap. */ + size_t chunks_used; /* Chunks allocated by the user. */ + size_t bytes_used; /* Byte total of user-allocated chunks. */ + size_t chunks_free; /* Chunks in the free list. */ + size_t bytes_free; /* Byte total of chunks in the free list. */ + }; + +/* Pick up the current statistics. */ +extern struct mstats mstats __P ((void)); + +/* Call WARNFUN with a warning message when memory usage is high. */ +extern void memory_warnings __P ((__ptr_t __start, + void (*__warnfun) __P ((__const char *)))); + + +/* Relocating allocator. */ + +/* Allocate SIZE bytes, and store the address in *HANDLEPTR. */ +extern __ptr_t r_alloc __P ((__ptr_t *__handleptr, size_t __size)); + +/* Free the storage allocated in HANDLEPTR. */ +extern void r_alloc_free __P ((__ptr_t *__handleptr)); + +/* Adjust the block at HANDLEPTR to be SIZE bytes long. */ +extern __ptr_t r_re_alloc __P ((__ptr_t *__handleptr, size_t __size)); + + +#ifdef __cplusplus +} +#endif + +#endif /* malloc.h */ diff --git a/gnu/lib/libmalloc/mcheck.c b/gnu/lib/libmalloc/mcheck.c new file mode 100644 index 0000000000..f7d9d4f892 --- /dev/null +++ b/gnu/lib/libmalloc/mcheck.c @@ -0,0 +1,133 @@ +/* Standard debugging hooks for `malloc'. + Copyright 1990, 1991, 1992, 1993 Free Software Foundation + Written May 1989 by Mike Haertel. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +#ifndef _MALLOC_INTERNAL +#define _MALLOC_INTERNAL +#include +#endif + +/* Old hook values. */ +static void (*old_free_hook) __P ((__ptr_t ptr)); +static __ptr_t (*old_malloc_hook) __P ((size_t size)); +static __ptr_t (*old_realloc_hook) __P ((__ptr_t ptr, size_t size)); + +/* Function to call when something awful happens. */ +static void (*abortfunc) __P ((void)); + +/* Arbitrary magical numbers. */ +#define MAGICWORD 0xfedabeeb +#define MAGICBYTE ((char) 0xd7) + +struct hdr + { + size_t size; /* Exact size requested by user. */ + unsigned long int magic; /* Magic number to check header integrity. */ + }; + +static void checkhdr __P ((const struct hdr *)); +static void +checkhdr (hdr) + const struct hdr *hdr; +{ + if (hdr->magic != MAGICWORD || ((char *) &hdr[1])[hdr->size] != MAGICBYTE) + (*abortfunc) (); +} + +static void freehook __P ((__ptr_t)); +static void +freehook (ptr) + __ptr_t ptr; +{ + struct hdr *hdr = ((struct hdr *) ptr) - 1; + checkhdr (hdr); + hdr->magic = 0; + __free_hook = old_free_hook; + free (hdr); + __free_hook = freehook; +} + +static __ptr_t mallochook __P ((size_t)); +static __ptr_t +mallochook (size) + size_t size; +{ + struct hdr *hdr; + + __malloc_hook = old_malloc_hook; + hdr = (struct hdr *) malloc (sizeof (struct hdr) + size + 1); + __malloc_hook = mallochook; + if (hdr == NULL) + return NULL; + + hdr->size = size; + hdr->magic = MAGICWORD; + ((char *) &hdr[1])[size] = MAGICBYTE; + return (__ptr_t) (hdr + 1); +} + +static __ptr_t reallochook __P ((__ptr_t, size_t)); +static __ptr_t +reallochook (ptr, size) + __ptr_t ptr; + size_t size; +{ + struct hdr *hdr = ((struct hdr *) ptr) - 1; + + checkhdr (hdr); + __free_hook = old_free_hook; + __malloc_hook = old_malloc_hook; + __realloc_hook = old_realloc_hook; + hdr = (struct hdr *) realloc ((__ptr_t) hdr, sizeof (struct hdr) + size + 1); + __free_hook = freehook; + __malloc_hook = mallochook; + __realloc_hook = reallochook; + if (hdr == NULL) + return NULL; + + hdr->size = size; + ((char *) &hdr[1])[size] = MAGICBYTE; + return (__ptr_t) (hdr + 1); +} + +int +mcheck (func) + void (*func) __P ((void)); +{ + extern void abort __P ((void)); + static int mcheck_used = 0; + + abortfunc = (func != NULL) ? func : abort; + + /* These hooks may not be safely inserted if malloc is already in use. */ + if (!__malloc_initialized && !mcheck_used) + { + old_free_hook = __free_hook; + __free_hook = freehook; + old_malloc_hook = __malloc_hook; + __malloc_hook = mallochook; + old_realloc_hook = __realloc_hook; + __realloc_hook = reallochook; + mcheck_used = 1; + } + + return mcheck_used ? 0 : -1; +} diff --git a/gnu/lib/libmalloc/mem-limits.h b/gnu/lib/libmalloc/mem-limits.h new file mode 100644 index 0000000000..ab594fcf3c --- /dev/null +++ b/gnu/lib/libmalloc/mem-limits.h @@ -0,0 +1,132 @@ +/* Includes for memory limit warnings. + Copyright (C) 1990, 1993 Free Software Foundation, Inc. + + +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#if defined(__osf__) && (defined(__mips) || defined(mips)) +#include +#include +#endif + +#ifdef __bsdi__ +#define BSD4_2 +#endif + +#ifndef BSD4_2 +#ifndef USG +#include +#endif /* not USG */ +#else /* if BSD4_2 */ +#include +#include +#endif /* BSD4_2 */ + +#ifdef emacs +/* The important properties of this type are that 1) it's a pointer, and + 2) arithmetic on it should work as if the size of the object pointed + to has a size of 1. */ +#ifdef __STDC__ +typedef void *POINTER; +#else +typedef char *POINTER; +#endif + +typedef unsigned long SIZE; + +#ifdef NULL +#undef NULL +#endif +#define NULL ((POINTER) 0) + +extern POINTER start_of_data (); +#ifdef DATA_SEG_BITS +#define EXCEEDS_LISP_PTR(ptr) \ + (((unsigned int) (ptr) & ~DATA_SEG_BITS) >> VALBITS) +#else +#define EXCEEDS_LISP_PTR(ptr) ((unsigned int) (ptr) >> VALBITS) +#endif + +#ifdef BSD +#ifndef DATA_SEG_BITS +extern char etext; +#define start_of_data() &etext +#endif +#endif + +#else /* Not emacs */ +extern char etext; +#define start_of_data() &etext +#endif /* Not emacs */ + + + +/* start of data space; can be changed by calling malloc_init */ +static POINTER data_space_start; + +/* Number of bytes of writable memory we can expect to be able to get */ +static unsigned int lim_data; + +#ifdef USG + +static void +get_lim_data () +{ + extern long ulimit (); + + lim_data = -1; + + /* Use the ulimit call, if we seem to have it. */ +#if !defined (ULIMIT_BREAK_VALUE) || defined (LINUX) + lim_data = ulimit (3, 0); +#endif + + /* If that didn't work, just use the macro's value. */ +#ifdef ULIMIT_BREAK_VALUE + if (lim_data == -1) + lim_data = ULIMIT_BREAK_VALUE; +#endif + + lim_data -= (long) data_space_start; +} + +#else /* not USG */ +#if !defined(BSD4_2) && !defined(__osf__) + +static void +get_lim_data () +{ + lim_data = vlimit (LIM_DATA, -1); +} + +#else /* BSD4_2 */ + +static void +get_lim_data () +{ + struct rlimit XXrlimit; + + getrlimit (RLIMIT_DATA, &XXrlimit); +#ifdef RLIM_INFINITY + lim_data = XXrlimit.rlim_cur & RLIM_INFINITY; /* soft limit */ +#else + lim_data = XXrlimit.rlim_cur; /* soft limit */ +#endif +} +#endif /* BSD4_2 */ +#endif /* not USG */ diff --git a/gnu/lib/libmalloc/memalign.c b/gnu/lib/libmalloc/memalign.c new file mode 100644 index 0000000000..f5ad17cb23 --- /dev/null +++ b/gnu/lib/libmalloc/memalign.c @@ -0,0 +1,61 @@ +/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#ifndef _MALLOC_INTERNAL +#define _MALLOC_INTERNAL +#include +#endif + +__ptr_t +memalign (alignment, size) + size_t alignment; + size_t size; +{ + __ptr_t result; + unsigned long int adj; + + size = ((size + alignment - 1) / alignment) * alignment; + + result = malloc (size); + if (result == NULL) + return NULL; + adj = (unsigned long int) ((unsigned long int) ((char *) result - + (char *) NULL)) % alignment; + if (adj != 0) + { + struct alignlist *l; + for (l = _aligned_blocks; l != NULL; l = l->next) + if (l->aligned == NULL) + /* This slot is free. Use it. */ + break; + if (l == NULL) + { + l = (struct alignlist *) malloc (sizeof (struct alignlist)); + if (l == NULL) + { + free (result); + return NULL; + } + } + l->exact = result; + result = l->aligned = (char *) result + alignment - adj; + l->next = _aligned_blocks; + _aligned_blocks = l; + } + + return result; +} diff --git a/gnu/lib/libmalloc/morecore.c b/gnu/lib/libmalloc/morecore.c new file mode 100644 index 0000000000..c9a9ca5aaa --- /dev/null +++ b/gnu/lib/libmalloc/morecore.c @@ -0,0 +1,44 @@ +/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the GNU C Library; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _MALLOC_INTERNAL +#define _MALLOC_INTERNAL +#include +#endif + +#ifndef __GNU_LIBRARY__ +#define __sbrk sbrk +#endif + +extern __ptr_t __sbrk __P ((int increment)); + +#ifndef NULL +#define NULL 0 +#endif + +/* Allocate INCREMENT more bytes of data space, + and return the start of data space, or NULL on errors. + If INCREMENT is negative, shrink data space. */ +__ptr_t +__default_morecore (increment) + ptrdiff_t increment; +{ + __ptr_t result = __sbrk ((int) increment); + if (result == (__ptr_t) -1) + return NULL; + return result; +} diff --git a/gnu/lib/libmalloc/mstats.c b/gnu/lib/libmalloc/mstats.c new file mode 100644 index 0000000000..511cdad987 --- /dev/null +++ b/gnu/lib/libmalloc/mstats.c @@ -0,0 +1,39 @@ +/* Access the statistics maintained by `malloc'. + Copyright 1990, 1991, 1992 Free Software Foundation + Written May 1989 by Mike Haertel. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +#ifndef _MALLOC_INTERNAL +#define _MALLOC_INTERNAL +#include +#endif + +struct mstats +mstats () +{ + struct mstats result; + + result.bytes_total = (char *) (*__morecore) (0) - _heapbase; + result.chunks_used = _chunks_used; + result.bytes_used = _bytes_used; + result.chunks_free = _chunks_free; + result.bytes_free = _bytes_free; + return result; +} diff --git a/gnu/lib/libmalloc/mtrace.awk b/gnu/lib/libmalloc/mtrace.awk new file mode 100644 index 0000000000..d7689cec3f --- /dev/null +++ b/gnu/lib/libmalloc/mtrace.awk @@ -0,0 +1,36 @@ +# +# Awk program to analyze mtrace.c output. +# +$1 == "+" { if (allocated[$2] != "") + print "+", $2, "Alloc", NR, "duplicate:", allocated[$2]; + else + allocated[$2] = $3; + } +$1 == "-" { if (allocated[$2] != "") { + allocated[$2] = ""; + if (allocated[$2] != "") + print "DELETE FAILED", $2, allocated[$2]; + } else + print "-", $2, "Free", NR, "was never alloc'd"; + } +$1 == "<" { if (allocated[$2] != "") + allocated[$2] = ""; + else + print "-", $2, "Realloc", NR, "was never alloc'd"; + } +$1 == ">" { if (allocated[$2] != "") + print "+", $2, "Realloc", NR, "duplicate:", allocated[$2]; + else + allocated[$2] = $3; + } + +# Ignore "= Start" +$1 == "=" { } +# Ignore failed realloc attempts for now +$1 == "!" { } + + +END { for (x in allocated) + if (allocated[x] != "") + print "+", x, allocated[x]; + } diff --git a/gnu/lib/libmalloc/mtrace.c b/gnu/lib/libmalloc/mtrace.c new file mode 100644 index 0000000000..5dae86aa80 --- /dev/null +++ b/gnu/lib/libmalloc/mtrace.c @@ -0,0 +1,150 @@ +/* More debugging hooks for `malloc'. + Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. + Written April 2, 1991 by John Gilmore of Cygnus Support. + Based on mcheck.c by Mike Haertel. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +#ifndef _MALLOC_INTERNAL +#define _MALLOC_INTERNAL +#include +#endif + +#include + +#ifndef __GNU_LIBRARY__ +extern char *getenv (); +#else +#include +#endif + +static FILE *mallstream; +static char mallenv[]= "MALLOC_TRACE"; +static char mallbuf[BUFSIZ]; /* Buffer for the output. */ + +/* Address to breakpoint on accesses to... */ +__ptr_t mallwatch; + +/* Old hook values. */ +static void (*tr_old_free_hook) __P ((__ptr_t ptr)); +static __ptr_t (*tr_old_malloc_hook) __P ((size_t size)); +static __ptr_t (*tr_old_realloc_hook) __P ((__ptr_t ptr, size_t size)); + +/* This function is called when the block being alloc'd, realloc'd, or + freed has an address matching the variable "mallwatch". In a debugger, + set "mallwatch" to the address of interest, then put a breakpoint on + tr_break. */ + +void tr_break __P ((void)); +void +tr_break () +{ +} + +static void tr_freehook __P ((__ptr_t)); +static void +tr_freehook (ptr) + __ptr_t ptr; +{ + fprintf (mallstream, "- %p\n", ptr); /* Be sure to print it first. */ + if (ptr == mallwatch) + tr_break (); + __free_hook = tr_old_free_hook; + free (ptr); + __free_hook = tr_freehook; +} + +static __ptr_t tr_mallochook __P ((size_t)); +static __ptr_t +tr_mallochook (size) + size_t size; +{ + __ptr_t hdr; + + __malloc_hook = tr_old_malloc_hook; + hdr = (__ptr_t) malloc (size); + __malloc_hook = tr_mallochook; + + /* We could be printing a NULL here; that's OK. */ + fprintf (mallstream, "+ %p %x\n", hdr, size); + + if (hdr == mallwatch) + tr_break (); + + return hdr; +} + +static __ptr_t tr_reallochook __P ((__ptr_t, size_t)); +static __ptr_t +tr_reallochook (ptr, size) + __ptr_t ptr; + size_t size; +{ + __ptr_t hdr; + + if (ptr == mallwatch) + tr_break (); + + __free_hook = tr_old_free_hook; + __malloc_hook = tr_old_malloc_hook; + __realloc_hook = tr_old_realloc_hook; + hdr = (__ptr_t) realloc (ptr, size); + __free_hook = tr_freehook; + __malloc_hook = tr_mallochook; + __realloc_hook = tr_reallochook; + if (hdr == NULL) + /* Failed realloc. */ + fprintf (mallstream, "! %p %x\n", ptr, size); + else + fprintf (mallstream, "< %p\n> %p %x\n", ptr, hdr, size); + + if (hdr == mallwatch) + tr_break (); + + return hdr; +} + +/* We enable tracing if either the environment variable MALLOC_TRACE + is set, or if the variable mallwatch has been patched to an address + that the debugging user wants us to stop on. When patching mallwatch, + don't forget to set a breakpoint on tr_break! */ + +void +mtrace () +{ + char *mallfile; + + mallfile = getenv (mallenv); + if (mallfile != NULL || mallwatch != NULL) + { + mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "w"); + if (mallstream != NULL) + { + /* Be sure it doesn't malloc its buffer! */ + setbuf (mallstream, mallbuf); + fprintf (mallstream, "= Start\n"); + tr_old_free_hook = __free_hook; + __free_hook = tr_freehook; + tr_old_malloc_hook = __malloc_hook; + __malloc_hook = tr_mallochook; + tr_old_realloc_hook = __realloc_hook; + __realloc_hook = tr_reallochook; + } + } +} diff --git a/gnu/lib/libmalloc/ralloc.c b/gnu/lib/libmalloc/ralloc.c new file mode 100644 index 0000000000..78b9f62fec --- /dev/null +++ b/gnu/lib/libmalloc/ralloc.c @@ -0,0 +1,514 @@ +/* Block-relocating memory allocator. + Copyright (C) 1993 Free Software Foundation, Inc. + + +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +/* NOTES: + + Only relocate the blocs neccessary for SIZE in r_alloc_sbrk, + rather than all of them. This means allowing for a possible + hole between the first bloc and the end of malloc storage. */ + +#ifdef emacs + +#include "config.h" +#include "lisp.h" /* Needed for VALBITS. */ + +#undef NULL + +/* The important properties of this type are that 1) it's a pointer, and + 2) arithmetic on it should work as if the size of the object pointed + to has a size of 1. */ +#if 0 /* Arithmetic on void* is a GCC extension. */ +#ifdef __STDC__ +typedef void *POINTER; +#else + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +typedef char *POINTER; + +#endif +#endif /* 0 */ + +/* Unconditionally use char * for this. */ +typedef char *POINTER; + +typedef unsigned long SIZE; + +/* Declared in dispnew.c, this version doesn't screw up if regions + overlap. */ +extern void safe_bcopy (); + +#include "getpagesize.h" + +#else /* Not emacs. */ + +#include + +typedef size_t SIZE; +typedef void *POINTER; + +#include +#include +#include + +#define safe_bcopy(x, y, z) memmove (y, x, z) + +#endif /* emacs. */ + +#define NIL ((POINTER) 0) + +/* A flag to indicate whether we have initialized ralloc yet. For + Emacs's sake, please do not make this local to malloc_init; on some + machines, the dumping procedure makes all static variables + read-only. On these machines, the word static is #defined to be + the empty string, meaning that r_alloc_initialized becomes an + automatic variable, and loses its value each time Emacs is started up. */ +static int r_alloc_initialized = 0; + +static void r_alloc_init (); + +/* Declarations for working with the malloc, ralloc, and system breaks. */ + +/* Function to set the real break value. */ +static POINTER (*real_morecore) (); + +/* The break value, as seen by malloc (). */ +static POINTER virtual_break_value; + +/* The break value, viewed by the relocatable blocs. */ +static POINTER break_value; + +/* The REAL (i.e., page aligned) break value of the process. */ +static POINTER page_break_value; + +/* This is the size of a page. We round memory requests to this boundary. */ +static int page_size; + +/* Whenever we get memory from the system, get this many extra bytes. This + must be a multiple of page_size. */ +static int extra_bytes; + +/* Macros for rounding. Note that rounding to any value is possible + by changing the definition of PAGE. */ +#define PAGE (getpagesize ()) +#define ALIGNED(addr) (((unsigned int) (addr) & (page_size - 1)) == 0) +#define ROUNDUP(size) (((unsigned int) (size) + page_size - 1) & ~(page_size - 1)) +#define ROUND_TO_PAGE(addr) (addr & (~(page_size - 1))) + +/* Functions to get and return memory from the system. */ + +/* Obtain SIZE bytes of space. If enough space is not presently available + in our process reserve, (i.e., (page_break_value - break_value)), + this means getting more page-aligned space from the system. + + Return non-zero if all went well, or zero if we couldn't allocate + the memory. */ +static int +obtain (size) + SIZE size; +{ + SIZE already_available = page_break_value - break_value; + + if (already_available < size) + { + SIZE get = ROUNDUP (size - already_available); + /* Get some extra, so we can come here less often. */ + get += extra_bytes; + + if ((*real_morecore) (get) == 0) + return 0; + + page_break_value += get; + } + + break_value += size; + + return 1; +} + +/* Obtain SIZE bytes of space and return a pointer to the new area. + If we could not allocate the space, return zero. */ + +static POINTER +get_more_space (size) + SIZE size; +{ + POINTER ptr = break_value; + if (obtain (size)) + return ptr; + else + return 0; +} + +/* Note that SIZE bytes of space have been relinquished by the process. + If SIZE is more than a page, return the space to the system. */ + +static void +relinquish (size) + SIZE size; +{ + POINTER new_page_break; + int excess; + + break_value -= size; + new_page_break = (POINTER) ROUNDUP (break_value); + excess = (char *) page_break_value - (char *) new_page_break; + + if (excess > extra_bytes * 2) + { + /* Keep extra_bytes worth of empty space. + And don't free anything unless we can free at least extra_bytes. */ + if ((*real_morecore) (extra_bytes - excess) == 0) + abort (); + + page_break_value += extra_bytes - excess; + } + + /* Zero the space from the end of the "official" break to the actual + break, so that bugs show up faster. */ + bzero (break_value, ((char *) page_break_value - (char *) break_value)); +} + +/* The meat - allocating, freeing, and relocating blocs. */ + +/* These structures are allocated in the malloc arena. + The linked list is kept in order of increasing '.data' members. + The data blocks abut each other; if b->next is non-nil, then + b->data + b->size == b->next->data. */ +typedef struct bp +{ + struct bp *next; + struct bp *prev; + POINTER *variable; + POINTER data; + SIZE size; +} *bloc_ptr; + +#define NIL_BLOC ((bloc_ptr) 0) +#define BLOC_PTR_SIZE (sizeof (struct bp)) + +/* Head and tail of the list of relocatable blocs. */ +static bloc_ptr first_bloc, last_bloc; + +/* Find the bloc referenced by the address in PTR. Returns a pointer + to that block. */ + +static bloc_ptr +find_bloc (ptr) + POINTER *ptr; +{ + register bloc_ptr p = first_bloc; + + while (p != NIL_BLOC) + { + if (p->variable == ptr && p->data == *ptr) + return p; + + p = p->next; + } + + return p; +} + +/* Allocate a bloc of SIZE bytes and append it to the chain of blocs. + Returns a pointer to the new bloc, or zero if we couldn't allocate + memory for the new block. */ + +static bloc_ptr +get_bloc (size) + SIZE size; +{ + register bloc_ptr new_bloc; + + if (! (new_bloc = (bloc_ptr) malloc (BLOC_PTR_SIZE)) + || ! (new_bloc->data = get_more_space (size))) + { + if (new_bloc) + free (new_bloc); + + return 0; + } + + new_bloc->size = size; + new_bloc->next = NIL_BLOC; + new_bloc->variable = (POINTER *) NIL; + + if (first_bloc) + { + new_bloc->prev = last_bloc; + last_bloc->next = new_bloc; + last_bloc = new_bloc; + } + else + { + first_bloc = last_bloc = new_bloc; + new_bloc->prev = NIL_BLOC; + } + + return new_bloc; +} + +/* Relocate all blocs from BLOC on upward in the list to the zone + indicated by ADDRESS. Direction of relocation is determined by + the position of ADDRESS relative to BLOC->data. + + If BLOC is NIL_BLOC, nothing is done. + + Note that ordering of blocs is not affected by this function. */ + +static void +relocate_some_blocs (bloc, address) + bloc_ptr bloc; + POINTER address; +{ + if (bloc != NIL_BLOC) + { + register SIZE offset = address - bloc->data; + register SIZE data_size = 0; + register bloc_ptr b; + + for (b = bloc; b != NIL_BLOC; b = b->next) + { + data_size += b->size; + b->data += offset; + *b->variable = b->data; + } + + safe_bcopy (address - offset, address, data_size); + } +} + + +/* Free BLOC from the chain of blocs, relocating any blocs above it + and returning BLOC->size bytes to the free area. */ + +static void +free_bloc (bloc) + bloc_ptr bloc; +{ + if (bloc == first_bloc && bloc == last_bloc) + { + first_bloc = last_bloc = NIL_BLOC; + } + else if (bloc == last_bloc) + { + last_bloc = bloc->prev; + last_bloc->next = NIL_BLOC; + } + else if (bloc == first_bloc) + { + first_bloc = bloc->next; + first_bloc->prev = NIL_BLOC; + } + else + { + bloc->next->prev = bloc->prev; + bloc->prev->next = bloc->next; + } + + relocate_some_blocs (bloc->next, bloc->data); + relinquish (bloc->size); + free (bloc); +} + +/* Interface routines. */ + +static int use_relocatable_buffers; + +/* Obtain SIZE bytes of storage from the free pool, or the system, as + necessary. If relocatable blocs are in use, this means relocating + them. This function gets plugged into the GNU malloc's __morecore + hook. + + We provide hysteresis, never relocating by less than extra_bytes. + + If we're out of memory, we should return zero, to imitate the other + __morecore hook values - in particular, __default_morecore in the + GNU malloc package. */ + +POINTER +r_alloc_sbrk (size) + long size; +{ + /* This is the first address not currently available for the heap. */ + POINTER top; + /* Amount of empty space below that. */ + /* It is not correct to use SIZE here, because that is usually unsigned. + ptrdiff_t would be okay, but is not always available. + `long' will work in all cases, in practice. */ + long already_available; + POINTER ptr; + + if (! use_relocatable_buffers) + return (*real_morecore) (size); + + top = first_bloc ? first_bloc->data : page_break_value; + already_available = (char *) top - (char *) virtual_break_value; + + /* Do we not have enough gap already? */ + if (size > 0 && already_available < size) + { + /* Get what we need, plus some extra so we can come here less often. */ + SIZE get = size - already_available + extra_bytes; + + if (! obtain (get)) + return 0; + + if (first_bloc) + relocate_some_blocs (first_bloc, first_bloc->data + get); + + /* Zero out the space we just allocated, to help catch bugs + quickly. */ + bzero (virtual_break_value, get); + } + /* Can we keep extra_bytes of gap while freeing at least extra_bytes? */ + else if (size < 0 && already_available - size > 2 * extra_bytes) + { + /* Ok, do so. This is how many to free. */ + SIZE give_back = already_available - size - extra_bytes; + + if (first_bloc) + relocate_some_blocs (first_bloc, first_bloc->data - give_back); + relinquish (give_back); + } + + ptr = virtual_break_value; + virtual_break_value += size; + + return ptr; +} + +/* Allocate a relocatable bloc of storage of size SIZE. A pointer to + the data is returned in *PTR. PTR is thus the address of some variable + which will use the data area. + + If we can't allocate the necessary memory, set *PTR to zero, and + return zero. */ + +POINTER +r_alloc (ptr, size) + POINTER *ptr; + SIZE size; +{ + register bloc_ptr new_bloc; + + if (! r_alloc_initialized) + r_alloc_init (); + + new_bloc = get_bloc (size); + if (new_bloc) + { + new_bloc->variable = ptr; + *ptr = new_bloc->data; + } + else + *ptr = 0; + + return *ptr; +} + +/* Free a bloc of relocatable storage whose data is pointed to by PTR. + Store 0 in *PTR to show there's no block allocated. */ + +void +r_alloc_free (ptr) + register POINTER *ptr; +{ + register bloc_ptr dead_bloc; + + dead_bloc = find_bloc (ptr); + if (dead_bloc == NIL_BLOC) + abort (); + + free_bloc (dead_bloc); + *ptr = 0; +} + +/* Given a pointer at address PTR to relocatable data, resize it to SIZE. + Do this by shifting all blocks above this one up in memory, unless + SIZE is less than or equal to the current bloc size, in which case + do nothing. + + Change *PTR to reflect the new bloc, and return this value. + + If more memory cannot be allocated, then leave *PTR unchanged, and + return zero. */ + +POINTER +r_re_alloc (ptr, size) + POINTER *ptr; + SIZE size; +{ + register bloc_ptr bloc; + + bloc = find_bloc (ptr); + if (bloc == NIL_BLOC) + abort (); + + if (size <= bloc->size) + /* Wouldn't it be useful to actually resize the bloc here? */ + return *ptr; + + if (! obtain (size - bloc->size)) + return 0; + + relocate_some_blocs (bloc->next, bloc->data + size); + + /* Zero out the new space in the bloc, to help catch bugs faster. */ + bzero (bloc->data + bloc->size, size - bloc->size); + + /* Indicate that this block has a new size. */ + bloc->size = size; + + return *ptr; +} + +/* The hook `malloc' uses for the function which gets more space + from the system. */ +extern POINTER (*__morecore) (); + +/* Intialize various things for memory allocation. */ + +static void +r_alloc_init () +{ + if (r_alloc_initialized) + return; + + r_alloc_initialized = 1; + real_morecore = __morecore; + __morecore = r_alloc_sbrk; + + virtual_break_value = break_value = (*real_morecore) (0); + if (break_value == NIL) + abort (); + + page_size = PAGE; + extra_bytes = ROUNDUP (50000); + + page_break_value = (POINTER) ROUNDUP (break_value); + /* Clear the rest of the last page; this memory is in our address space + even though it is after the sbrk value. */ + bzero (break_value, (page_break_value - break_value)); + use_relocatable_buffers = 1; +} diff --git a/gnu/lib/libmalloc/valloc.c b/gnu/lib/libmalloc/valloc.c new file mode 100644 index 0000000000..fcf117447f --- /dev/null +++ b/gnu/lib/libmalloc/valloc.c @@ -0,0 +1,44 @@ +/* Allocate memory on a page boundary. + Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +#ifndef _MALLOC_INTERNAL +#define _MALLOC_INTERNAL +#include +#endif + +#ifdef __GNU_LIBRARY__ +extern size_t __getpagesize __P ((void)); +#else +#include "getpagesize.h" +#define __getpagesize() getpagesize() +#endif + +static size_t pagesize; + +__ptr_t +valloc (size) + size_t size; +{ + if (pagesize == 0) + pagesize = __getpagesize (); + + return memalign (pagesize, size); +} diff --git a/gnu/lib/libmalloc/vm-limit.c b/gnu/lib/libmalloc/vm-limit.c new file mode 100644 index 0000000000..a2ac96ce4c --- /dev/null +++ b/gnu/lib/libmalloc/vm-limit.c @@ -0,0 +1,134 @@ +/* Functions for memory limit warnings. + Copyright (C) 1990, 1992 Free Software Foundation, Inc. + + +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#ifdef emacs +#include "config.h" +#include "lisp.h" +#endif + +#ifndef emacs +#include +typedef size_t SIZE; +typedef void *POINTER; +#define EXCEEDS_LISP_PTR(x) 0 +#endif + +#include "mem-limits.h" + +/* + Level number of warnings already issued. + 0 -- no warnings issued. + 1 -- 75% warning already issued. + 2 -- 85% warning already issued. + 3 -- 95% warning issued; keep warning frequently. +*/ +static int warnlevel; + +/* Function to call to issue a warning; + 0 means don't issue them. */ +static void (*warn_function) (); + +/* Get more memory space, complaining if we're near the end. */ + +static void +check_memory_limits () +{ + extern POINTER (*__morecore) (); + + register POINTER cp; + int five_percent; + int data_size; + + if (lim_data == 0) + get_lim_data (); + five_percent = lim_data / 20; + + /* Find current end of memory and issue warning if getting near max */ + cp = (char *) (*__morecore) (0); + data_size = (char *) cp - (char *) data_space_start; + + if (warn_function) + switch (warnlevel) + { + case 0: + if (data_size > five_percent * 15) + { + warnlevel++; + (*warn_function) ("Warning: past 75% of memory limit"); + } + break; + + case 1: + if (data_size > five_percent * 17) + { + warnlevel++; + (*warn_function) ("Warning: past 85% of memory limit"); + } + break; + + case 2: + if (data_size > five_percent * 19) + { + warnlevel++; + (*warn_function) ("Warning: past 95% of memory limit"); + } + break; + + default: + (*warn_function) ("Warning: past acceptable memory limits"); + break; + } + + /* If we go down below 70% full, issue another 75% warning + when we go up again. */ + if (data_size < five_percent * 14) + warnlevel = 0; + /* If we go down below 80% full, issue another 85% warning + when we go up again. */ + else if (warnlevel > 1 && data_size < five_percent * 16) + warnlevel = 1; + /* If we go down below 90% full, issue another 95% warning + when we go up again. */ + else if (warnlevel > 2 && data_size < five_percent * 18) + warnlevel = 2; + + if (EXCEEDS_LISP_PTR (cp)) + (*warn_function) ("Warning: memory in use exceeds lisp pointer size"); +} + +/* Cause reinitialization based on job parameters; + also declare where the end of pure storage is. */ + +void +memory_warnings (start, warnfun) + POINTER start; + void (*warnfun) (); +{ + extern void (* __after_morecore_hook) (); /* From gmalloc.c */ + + if (start) + data_space_start = start; + else + data_space_start = start_of_data (); + + warn_function = warnfun; + __after_morecore_hook = check_memory_limits; +} diff --git a/gnu/lib/libregex/AUTHORS b/gnu/lib/libregex/AUTHORS new file mode 100644 index 0000000000..058be996a2 --- /dev/null +++ b/gnu/lib/libregex/AUTHORS @@ -0,0 +1,10 @@ +Richard Stallman -- original version and continuing revisions of + regex.c and regex.h, and original version of the documentation. + +Karl Berry and Kathryn Hargreaves -- extensive modifications to above, + and all test files. + +Jim Blandy -- original version of re_set_registers, revisions to regex.c. + +Joe Arceneaux, David MacKenzie, Mike Haertel, Charles Hannum, and +probably others -- revisions to regex.c. diff --git a/gnu/usr.bin/diff3/COPYING b/gnu/lib/libregex/COPYING similarity index 100% rename from gnu/usr.bin/diff3/COPYING rename to gnu/lib/libregex/COPYING diff --git a/gnu/lib/libregex/ChangeLog b/gnu/lib/libregex/ChangeLog new file mode 100644 index 0000000000..ef919d276a --- /dev/null +++ b/gnu/lib/libregex/ChangeLog @@ -0,0 +1,3030 @@ +Fri Apr 2 17:31:59 1993 Jim Blandy (jimb@totoro.cs.oberlin.edu) + + * Released version 0.12. + + * regex.c (regerror): If errcode is zero, that's not a valid + error code, according to POSIX, but return "Success." + + * regex.c (regerror): Remember to actually fetch the message + from re_error_msg. + + * regex.c (regex_compile): Don't use the trick for ".*\n" on + ".+\n". Since the latter involves laying an extra choice + point, the backward jump isn't adjusted properly. + +Thu Mar 25 21:35:18 1993 Jim Blandy (jimb@totoro.cs.oberlin.edu) + + * regex.c (regex_compile): In the handle_open and handle_close + sections, clear pending_exact to zero. + +Tue Mar 9 12:03:07 1993 Jim Blandy (jimb@wookumz.gnu.ai.mit.edu) + + * regex.c (re_search_2): In the loop which searches forward + using fastmap, don't forget to cast the character from the + string to an unsigned before using it as an index into the + translate map. + +Thu Jan 14 15:41:46 1993 David J. MacKenzie (djm@kropotkin.gnu.ai.mit.edu) + + * regex.h: Never define const; let the callers do it. + configure.in: Don't define USING_AUTOCONF. + +Wed Jan 6 20:49:29 1993 Jim Blandy (jimb@geech.gnu.ai.mit.edu) + + * regex.c (regerror): Abort if ERRCODE is out of range. + +Sun Dec 20 16:19:10 1992 Jim Blandy (jimb@totoro.cs.oberlin.edu) + + * configure.in: Arrange to #define USING_AUTOCONF. + * regex.h: If USING_AUTOCONF is #defined, don't mess with + `const' at all; autoconf has taken care of it. + +Mon Dec 14 21:40:39 1992 David J. MacKenzie (djm@kropotkin.gnu.ai.mit.edu) + + * regex.h (RE_SYNTAX_AWK): Fix typo. From Arnold Robbins. + +Sun Dec 13 20:35:39 1992 Jim Blandy (jimb@totoro.cs.oberlin.edu) + + * regex.c (compile_range): Fetch the range start and end by + casting the pattern pointer to an `unsigned char *' before + fetching through it. + +Sat Dec 12 09:41:01 1992 Jim Blandy (jimb@totoro.cs.oberlin.edu) + + * regex.c: Undo change of 12/7/92; it's better for Emacs to + #define HAVE_CONFIG_H. + +Fri Dec 11 22:00:34 1992 Jim Meyering (meyering@hal.gnu.ai.mit.edu) + + * regex.c: Define and use isascii-protected ctype.h macros. + +Fri Dec 11 05:10:38 1992 Jim Blandy (jimb@totoro.cs.oberlin.edu) + + * regex.c (re_match_2): Undo Karl's November 10th change; it + keeps the group in :\(.*\) from matching :/ properly. + +Mon Dec 7 19:44:56 1992 Jim Blandy (jimb@wookumz.gnu.ai.mit.edu) + + * regex.c: #include config.h if either HAVE_CONFIG_H or emacs + is #defined. + +Tue Dec 1 13:33:17 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * regex.c [HAVE_CONFIG_H]: Include config.h. + +Wed Nov 25 23:46:02 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * regex.c (regcomp): Add parens around bitwise & for clarity. + Initialize preg->allocated to prevent segv. + +Tue Nov 24 09:22:29 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * regex.c: Use HAVE_STRING_H, not USG. + * configure.in: Check for string.h, not USG. + +Fri Nov 20 06:33:24 1992 Karl Berry (karl@cs.umb.edu) + + * regex.c (SIGN_EXTEND_CHAR) [VMS]: Back out of this change, + since Roland Roberts now says it was a localism. + +Mon Nov 16 07:01:36 1992 Karl Berry (karl@cs.umb.edu) + + * regex.h (const) [!HAVE_CONST]: Test another cpp symbol (from + Autoconf) before zapping const. + +Sun Nov 15 05:36:42 1992 Jim Blandy (jimb@wookumz.gnu.ai.mit.edu) + + * regex.c, regex.h: Changes for VMS from Roland B Roberts + . + +Thu Nov 12 11:31:15 1992 Karl Berry (karl@cs.umb.edu) + + * Makefile.in (distfiles): Include INSTALL. + +Tue Nov 10 09:29:23 1992 Karl Berry (karl@cs.umb.edu) + + * regex.c (re_match_2): At maybe_pop_jump, if at end of string + and pattern, just quit the matching loop. + + * regex.c (LETTER_P): Rename to `WORDCHAR_P'. + + * regex.c (AT_STRINGS_{BEG,END}): Take `d' as an arg; change + callers. + + * regex.c (re_match_2) [!emacs]: In wordchar and notwordchar + cases, advance d. + +Wed Nov 4 15:43:58 1992 Karl Berry (karl@hal.gnu.ai.mit.edu) + + * regex.h (const) [!__STDC__]: Don't define if it's already defined. + +Sat Oct 17 19:28:19 1992 Karl Berry (karl@cs.umb.edu) + + * regex.c (bcmp, bcopy, bzero): Only #define if they are not + already #defined. + + * configure.in: Use AC_CONST. + +Thu Oct 15 08:39:06 1992 Karl Berry (karl@cs.umb.edu) + + * regex.h (const) [!const]: Conditionalize. + +Fri Oct 2 13:31:42 1992 Karl Berry (karl@cs.umb.edu) + + * regex.h (RE_SYNTAX_ED): New definition. + +Sun Sep 20 12:53:39 1992 Karl Berry (karl@cs.umb.edu) + + * regex.[ch]: remove traces of `longest_p' -- dumb idea to put + this into the pattern buffer, as it means parallelism loses. + + * Makefile.in (config.status): use sh to run configure --no-create. + + * Makefile.in (realclean): OK, don't remove configure. + +Sat Sep 19 09:05:08 1992 Karl Berry (karl@hayley) + + * regex.c (PUSH_FAILURE_POINT, POP_FAILURE_POINT) [DEBUG]: keep + track of how many failure points we push and pop. + (re_match_2) [DEBUG]: declare variables for that, and print results. + (DEBUG_PRINT4): new macro. + + * regex.h (re_pattern_buffer): new field `longest_p' (to + eliminate backtracking if the user doesn't need it). + * regex.c (re_compile_pattern): initialize it (to 1). + (re_search_2): set it to zero if register information is not needed. + (re_match_2): if it's set, don't backtrack. + + * regex.c (re_search_2): update fastmap only after checking that + the pattern is anchored. + + * regex.c (re_match_2): do more debugging at maybe_pop_jump. + + * regex.c (re_search_2): cast result of TRANSLATE for use in + array subscript. + +Thu Sep 17 19:47:16 1992 Karl Berry (karl@geech.gnu.ai.mit.edu) + + * Version 0.11. + +Wed Sep 16 08:17:10 1992 Karl Berry (karl@hayley) + + * regex.c (INIT_FAIL_STACK): rewrite as statements instead of a + complicated comma expr, to avoid compiler warnings (and also + simplify). + (re_compile_fastmap, re_match_2): change callers. + + * regex.c (POP_FAILURE_POINT): cast pop of regstart and regend + to avoid compiler warnings. + + * regex.h (RE_NEWLINE_ORDINARY): remove this syntax bit, and + remove uses. + * regex.c (at_{beg,end}line_loc_p): go the last mile: remove + the RE_NEWLINE_ORDINARY case which made the ^ in \n^ be an anchor. + +Tue Sep 15 09:55:29 1992 Karl Berry (karl@hayley) + + * regex.c (at_begline_loc_p): new fn. + (at_endline_loc_p): simplify at_endline_op_p. + (regex_compile): in ^/$ cases, call the above. + + * regex.c (POP_FAILURE_POINT): rewrite the fn as a macro again, + as lord's profiling indicates the function is 20% of the time. + (re_match_2): callers changed. + + * configure.in (AC_MEMORY_H): remove, since we never use memcpy et al. + +Mon Sep 14 17:49:27 1992 Karl Berry (karl@hayley) + + * Makefile.in (makeargs): include MFLAGS. + +Sun Sep 13 07:41:45 1992 Karl Berry (karl@hayley) + + * regex.c (regex_compile): in \1..\9 case, make it always + invalid to use \ if there is no preceding th subexpr. + * regex.h (RE_NO_MISSING_BK_REF): remove this syntax bit. + + * regex.c (regex_compile): remove support for invalid empty groups. + * regex.h (RE_NO_EMPTY_GROUPS): remove this syntax bit. + + * regex.c (FREE_VARIABLES) [!REGEX_MALLOC]: define as alloca (0), + to reclaim memory. + + * regex.h (RE_SYNTAX_POSIX_SED): don't bother with this. + +Sat Sep 12 13:37:21 1992 Karl Berry (karl@hayley) + + * README: incorporate emacs.diff. + + * regex.h (_RE_ARGS) [!__STDC__]: define as empty parens. + + * configure.in: add AC_ALLOCA. + + * Put test files in subdir test, documentation in subdir doc. + Adjust Makefile.in and configure.in accordingly. + +Thu Sep 10 10:29:11 1992 Karl Berry (karl@hayley) + + * regex.h (RE_SYNTAX_{POSIX_,}SED): new definitions. + +Wed Sep 9 06:27:09 1992 Karl Berry (karl@hayley) + + * Version 0.10. + +Tue Sep 8 07:32:30 1992 Karl Berry (karl@hayley) + + * xregex.texinfo: put the day of month into the date. + + * Makefile.in (realclean): remove Texinfo-generated files. + (distclean): remove empty sorted index files. + (clean): remove dvi files, etc. + + * configure.in: test for more Unix variants. + + * fileregex.c: new file. + Makefile.in (fileregex): new target. + + * iregex.c (main): move variable decls to smallest scope. + + * regex.c (FREE_VARIABLES): free reg_{,info_}dummy. + (re_match_2): check that the allocation for those two succeeded. + + * regex.c (FREE_VAR): replace FREE_NONNULL with this. + (FREE_VARIABLES): call it. + (re_match_2) [REGEX_MALLOC]: initialize all our vars to NULL. + + * tregress.c (do_match): generalize simple_match. + (SIMPLE_NONMATCH): new macro. + (SIMPLE_MATCH): change from routine. + + * Makefile.in (regex.texinfo): make file readonly, so we don't + edit it by mistake. + + * many files (re_default_syntax): rename to `re_syntax_options'; + call re_set_syntax instead of assigning to the variable where + possible. + +Mon Sep 7 10:12:16 1992 Karl Berry (karl@hayley) + + * syntax.skel: don't use prototypes. + + * {configure,Makefile}.in: new files. + + * regex.c: include `#if USG || STDC_HEADERS'; remove + obsolete test for `POSIX', and test for BSRTING. + Include if we are not USG or STDC_HEADERS. + Do not include . What did we ever need that for? + + * regex.h (RE_NO_EMPTY_ALTS): remove this. + (RE_SYNTAX_AWK): remove from here, too. + * regex.c (regex_compile): remove the check. + * xregex.texinfo (Alternation Operator): update. + * other.c (test_others): remove tests for this. + + * regex.h (RE_DUP_MAX): undefine if already defined. + + * regex.h: (RE_SYNTAX_POSIX*): redo to allow more operators, and + define new syntaxes with the minimal set. + + * syntax.skel (main): used sscanf instead of scanf. + + * regex.h (RE_SYNTAX_*GREP): new definitions from mike. + + * regex.c (regex_compile): initialize the upper bound of + intervals at the beginning of the interval, not the end. + (From pclink@qld.tne.oz.au.) + + * regex.c (handle_bar): rename to `handle_alt', for consistency. + + * regex.c ({store,insert}_{op1,op2}): new routines (except the last). + ({STORE,INSERT}_JUMP{,2}): macros to replace the old routines, + which took arguments in different orders, and were generally weird. + + * regex.c (PAT_PUSH*): rename to `BUF_PUSH*' -- we're not + appending info to the pattern! + +Sun Sep 6 11:26:49 1992 Karl Berry (karl@hayley) + + * regex.c (regex_compile): delete the variable + `following_left_brace', since we never use it. + + * regex.c (print_compiled_pattern): don't print the fastmap if + it's null. + + * regex.c (re_compile_fastmap): handle + `on_failure_keep_string_jump' like `on_failure_jump'. + + * regex.c (re_match_2): in `charset{,_not' case, cast the bit + count to unsigned, not unsigned char, in case we have a full + 32-byte bit list. + + * tregress.c (simple_match): remove. + (simple_test): rename as `simple_match'. + (simple_compile): print the error string if the compile failed. + + * regex.c (DO_RANGE): rewrite as a function, `compile_range', so + we can debug it. Change pattern characters to unsigned char + *'s, and change the range variable to an unsigned. + (regex_compile): change calls. + +Sat Sep 5 17:40:49 1992 Karl Berry (karl@hayley) + + * regex.h (_RE_ARGS): new macro to put in argument lists (if + ANSI) or omit them (if K&R); don't declare routines twice. + + * many files (obscure_syntax): rename to `re_default_syntax'. + +Fri Sep 4 09:06:53 1992 Karl Berry (karl@hayley) + + * GNUmakefile (extraclean): new target. + (realclean): delete the info files. + +Wed Sep 2 08:14:42 1992 Karl Berry (karl@hayley) + + * regex.h: doc fix. + +Sun Aug 23 06:53:15 1992 Karl Berry (karl@hayley) + + * regex.[ch] (re_comp): no const in the return type (from djm). + +Fri Aug 14 07:25:46 1992 Karl Berry (karl@hayley) + + * regex.c (DO_RANGE): declare variables as unsigned chars, not + signed chars (from jimb). + +Wed Jul 29 18:33:53 1992 Karl Berry (karl@claude.cs.umb.edu) + + * Version 0.9. + + * GNUmakefile (distclean): do not remove regex.texinfo. + (realclean): remove it here. + + * tregress.c (simple_test): initialize buf.buffer. + +Sun Jul 26 08:59:38 1992 Karl Berry (karl@hayley) + + * regex.c (push_dummy_failure): new opcode and corresponding + case in the various routines. Pushed at the end of + alternatives. + + * regex.c (jump_past_next_alt): rename to `jump_past_alt', for + brevity. + (no_pop_jump): rename to `jump'. + + * regex.c (regex_compile) [DEBUG]: terminate printing of pattern + with a newline. + + * NEWS: new file. + + * tregress.c (simple_{compile,match,test}): routines to simplify all + these little tests. + + * tregress.c: test for matching as much as possible. + +Fri Jul 10 06:53:32 1992 Karl Berry (karl@hayley) + + * Version 0.8. + +Wed Jul 8 06:39:31 1992 Karl Berry (karl@hayley) + + * regex.c (SIGN_EXTEND_CHAR): #undef any previous definition, as + ours should always work properly. + +Mon Jul 6 07:10:50 1992 Karl Berry (karl@hayley) + + * iregex.c (main) [DEBUG]: conditionalize the call to + print_compiled_pattern. + + * iregex.c (main): initialize buf.buffer to NULL. + * tregress (test_regress): likewise. + + * regex.c (alloca) [sparc]: #if on HAVE_ALLOCA_H instead. + + * tregress.c (test_regress): didn't have jla's test quite right. + +Sat Jul 4 09:02:12 1992 Karl Berry (karl@hayley) + + * regex.c (re_match_2): only REGEX_ALLOCATE all the register + vectors if the pattern actually has registers. + (match_end): new variable to avoid having to use best_regend[0]. + + * regex.c (IS_IN_FIRST_STRING): rename to FIRST_STRING_P. + + * regex.c: doc fixes. + + * tregess.c (test_regress): new fastmap test forwarded by rms. + + * tregress.c (test_regress): initialize the fastmap field. + + * tregress.c (test_regress): new test from jla that aborted + in re_search_2. + +Fri Jul 3 09:10:05 1992 Karl Berry (karl@hayley) + + * tregress.c (test_regress): add tests for translating charsets, + from kaoru. + + * GNUmakefile (common): add alloca.o. + * alloca.c: new file, copied from bison. + + * other.c (test_others): remove var `buf', since it's no longer used. + + * Below changes from ro@TechFak.Uni-Bielefeld.DE. + + * tregress.c (test_regress): initialize buf.allocated. + + * regex.c (re_compile_fastmap): initialize `succeed_n_p'. + + * GNUmakefile (regex): depend on $(common). + +Wed Jul 1 07:12:46 1992 Karl Berry (karl@hayley) + + * Version 0.7. + + * regex.c: doc fixes. + +Mon Jun 29 08:09:47 1992 Karl Berry (karl@fosse) + + * regex.c (pop_failure_point): change string vars to + `const char *' from `unsigned char *'. + + * regex.c: consolidate debugging stuff. + (print_partial_compiled_pattern): avoid enum clash. + +Mon Jun 29 07:50:27 1992 Karl Berry (karl@hayley) + + * xmalloc.c: new file. + * GNUmakefile (common): add it. + + * iregex.c (print_regs): new routine (from jimb). + (main): call it. + +Sat Jun 27 10:50:59 1992 Jim Blandy (jimb@pogo.cs.oberlin.edu) + + * xregex.c (re_match_2): When we have accepted a match and + restored d from best_regend[0], we need to set dend + appropriately as well. + +Sun Jun 28 08:48:41 1992 Karl Berry (karl@hayley) + + * tregress.c: rename from regress.c. + + * regex.c (print_compiled_pattern): improve charset case to ease + byte-counting. + Also, don't distinguish between Emacs and non-Emacs + {not,}wordchar opcodes. + + * regex.c (print_fastmap): move here. + * test.c: from here. + * regex.c (print_{{partial,}compiled_pattern,double_string}): + rename from ..._printer. Change calls here and in test.c. + + * regex.c: create from xregex.c and regexinc.c for once and for + all, and change the debug fns to be extern, instead of static. + * GNUmakefile: remove traces of xregex.c. + * test.c: put in externs, instead of including regexinc.c. + + * xregex.c: move interactive main program and scanstring to iregex.c. + * iregex.c: new file. + * upcase.c, printchar.c: new files. + + * various doc fixes and other cosmetic changes throughout. + + * regexinc.c (compiled_pattern_printer): change variable name, + for consistency. + (partial_compiled_pattern_printer): print other info about the + compiled pattern, besides just the opcodes. + * xregex.c (regex_compile) [DEBUG]: print the compiled pattern + when we're done. + + * xregex.c (re_compile_fastmap): in the duplicate case, set + `can_be_null' and return. + Also, set `bufp->can_be_null' according to a new variable, + `path_can_be_null'. + Also, rewrite main while loop to not test `p != NULL', since + we never set it that way. + Also, eliminate special `can_be_null' value for the endline case. + (re_search_2): don't test for the special value. + * regex.h (struct re_pattern_buffer): remove the definition. + +Sat Jun 27 15:00:40 1992 Karl Berry (karl@hayley) + + * xregex.c (re_compile_fastmap): remove the `RE_' from + `REG_RE_MATCH_NULL_AT_END'. + Also, assert the fastmap in the pattern buffer is non-null. + Also, reset `succeed_n_p' after we've + paid attention to it, instead of every time through the loop. + Also, in the `anychar' case, only clear fastmap['\n'] if the + syntax says to, and don't return prematurely. + Also, rearrange cases in some semblance of a rational order. + * regex.h (REG_RE_MATCH_NULL_AT_END): remove the `RE_' from the name. + + * other.c: take bug reports from here. + * regress.c: new file for them. + * GNUmakefile (test): add it. + * main.c (main): new possible test. + * test.h (test_type): new value in enum. + +Thu Jun 25 17:37:43 1992 Karl Berry (karl@hayley) + + * xregex.c (scanstring) [test]: new function from jimb to allow some + escapes. + (main) [test]: call it (on the string, not the pattern). + + * xregex.c (main): make return type `int'. + +Wed Jun 24 10:43:03 1992 Karl Berry (karl@hayley) + + * xregex.c (pattern_offset_t): change to `int', for the benefit + of patterns which compile to more than 2^15 bytes. + + * xregex.c (GET_BUFFER_SPACE): remove spurious braces. + + * xregex.texinfo (Using Registers): put in a stub to ``document'' + the new function. + * regex.h (re_set_registers) [!__STDC__]: declare. + * xregex.c (re_set_registers): declare K&R style (also move to a + different place in the file). + +Mon Jun 8 18:03:28 1992 Jim Blandy (jimb@pogo.cs.oberlin.edu) + + * regex.h (RE_NREGS): Doc fix. + + * xregex.c (re_set_registers): New function. + * regex.h (re_set_registers): Declaration for new function. + +Fri Jun 5 06:55:18 1992 Karl Berry (karl@hayley) + + * main.c (main): `return 0' instead of `exit (0)'. (From Paul Eggert) + + * regexinc.c (SIGN_EXTEND_CHAR): cast to unsigned char. + (extract_number, EXTRACT_NUMBER): don't bother to cast here. + +Tue Jun 2 07:37:53 1992 Karl Berry (karl@hayley) + + * Version 0.6. + + * Change copyrights to `1985, 89, ...'. + + * regex.h (REG_RE_MATCH_NULL_AT_END): new macro. + * xregex.c (re_compile_fastmap): initialize `can_be_null' to + `p==pend', instead of in the test at the top of the loop (as + it was, it was always being set). + Also, set `can_be_null'=1 if we would jump to the end of the + pattern in the `on_failure_jump' cases. + (re_search_2): check if `can_be_null' is 1, not nonzero. This + was the original test in rms' regex; why did we change this? + + * xregex.c (re_compile_fastmap): rename `is_a_succeed_n' to + `succeed_n_p'. + +Sat May 30 08:09:08 1992 Karl Berry (karl@hayley) + + * xregex.c (re_compile_pattern): declare `regnum' as `unsigned', + not `regnum_t', for the benefit of those patterns with more + than 255 groups. + + * xregex.c: rename `failure_stack' to `fail_stack', for brevity; + likewise for `match_nothing' to `match_null'. + + * regexinc.c (REGEX_REALLOCATE): take both the new and old + sizes, and copy only the old bytes. + * xregex.c (DOUBLE_FAILURE_STACK): pass both old and new. + * This change from Thorsten Ohl. + +Fri May 29 11:45:22 1992 Karl Berry (karl@hayley) + + * regexinc.c (SIGN_EXTEND_CHAR): define as `(signed char) c' + instead of relying on __CHAR_UNSIGNED__, to work with + compilers other than GCC. From Per Bothner. + + * main.c (main): change return type to `int'. + +Mon May 18 06:37:08 1992 Karl Berry (karl@hayley) + + * regex.h (RE_SYNTAX_AWK): typo in RE_RE_UNMATCHED... + +Fri May 15 10:44:46 1992 Karl Berry (karl@hayley) + + * Version 0.5. + +Sun May 3 13:54:00 1992 Karl Berry (karl@hayley) + + * regex.h (struct re_pattern_buffer): now it's just `regs_allocated'. + (REGS_UNALLOCATED, REGS_REALLOCATE, REGS_FIXED): new constants. + * xregex.c (regexec, re_compile_pattern): set the field appropriately. + (re_match_2): and use it. bufp can't be const any more. + +Fri May 1 15:43:09 1992 Karl Berry (karl@hayley) + + * regexinc.c: unconditionally include , first. + + * regex.h (struct re_pattern_buffer): rename + `caller_allocated_regs' to `regs_allocated_p'. + * xregex.c (re_compile_pattern): same change here. + (regexec): and here. + (re_match_2): reallocate registers if necessary. + +Fri Apr 10 07:46:50 1992 Karl Berry (karl@hayley) + + * regex.h (RE_SYNTAX{_POSIX,}_AWK): new definitions from Arnold. + +Sun Mar 15 07:34:30 1992 Karl Berry (karl at hayley) + + * GNUmakefile (dist): versionize regex.{c,h,texinfo}. + +Tue Mar 10 07:05:38 1992 Karl Berry (karl at hayley) + + * Version 0.4. + + * xregex.c (PUSH_FAILURE_POINT): always increment the failure id. + (DEBUG_STATEMENT) [DEBUG]: execute the statement even if `debug'==0. + + * xregex.c (pop_failure_point): if the saved string location is + null, keep the current value. + (re_match_2): at fail, test for a dummy failure point by + checking the restored pattern value, not string value. + (re_match_2): new case, `on_failure_keep_string_jump'. + (regex_compile): output this opcode in the .*\n case. + * regexinc.c (re_opcode_t): define the opcode. + (partial_compiled_pattern_pattern): add the new case. + +Mon Mar 9 09:09:27 1992 Karl Berry (karl at hayley) + + * xregex.c (regex_compile): optimize .*\n to output an + unconditional jump to the ., instead of pushing failure points + each time through the loop. + + * xregex.c (DOUBLE_FAILURE_STACK): compute the maximum size + ourselves (and correctly); change callers. + +Sun Mar 8 17:07:46 1992 Karl Berry (karl at hayley) + + * xregex.c (failure_stack_elt_t): change to `const char *', to + avoid warnings. + + * regex.h (re_set_syntax): declare this. + + * xregex.c (pop_failure_point) [DEBUG]: conditionally pass the + original strings and sizes; change callers. + +Thu Mar 5 16:35:35 1992 Karl Berry (karl at claude.cs.umb.edu) + + * xregex.c (regnum_t): new type for register/group numbers. + (compile_stack_elt_t, regex_compile): use it. + + * xregex.c (regexec): declare len as `int' to match re_search. + + * xregex.c (re_match_2): don't declare p1 twice. + + * xregex.c: change `while (1)' to `for (;;)' to avoid silly + compiler warnings. + + * regex.h [__STDC__]: use #if, not #ifdef. + + * regexinc.c (REGEX_REALLOCATE): cast the result of alloca to + (char *), to avoid warnings. + + * xregex.c (regerror): declare variable as const. + + * xregex.c (re_compile_pattern, re_comp): define as returning a const + char *. + * regex.h (re_compile_pattern, re_comp): likewise. + +Thu Mar 5 15:57:56 1992 Karl Berry (karl@hal) + + * xregex.c (regcomp): declare `syntax' as unsigned. + + * xregex.c (re_match_2): try to avoid compiler warnings about + unsigned comparisons. + + * GNUmakefile (test-xlc): new target. + + * regex.h (reg_errcode_t): remove trailing comma from definition. + * regexinc.c (re_opcode_t): likewise. + +Thu Mar 5 06:56:07 1992 Karl Berry (karl at hayley) + + * GNUmakefile (dist): add version numbers automatically. + (versionfiles): new variable. + (regex.{c,texinfo}): don't add version numbers here. + * regex.h: put in placeholder instead of the version number. + +Fri Feb 28 07:11:33 1992 Karl Berry (karl at hayley) + + * xregex.c (re_error_msg): declare const, since it is. + +Sun Feb 23 05:41:57 1992 Karl Berry (karl at fosse) + + * xregex.c (PAT_PUSH{,_2,_3}, ...): cast args to avoid warnings. + (regex_compile, regexec): return REG_NOERROR, instead + of 0, on success. + (boolean): define as char, and #define false and true. + * regexinc.c (STREQ): cast the result. + +Sun Feb 23 07:45:38 1992 Karl Berry (karl at hayley) + + * GNUmakefile (test-cc, test-hc, test-pcc): new targets. + + * regex.inc (extract_number, extract_number_and_incr) [DEBUG]: + only define if we are debugging. + + * xregex.c [_AIX]: do #pragma alloca first if necessary. + * regexinc.c [_AIX]: remove the #pragma from here. + + * regex.h (reg_syntax_t): declare as unsigned, and redo the enum + as #define's again. Some compilers do stupid things with enums. + +Thu Feb 20 07:19:47 1992 Karl Berry (karl at hayley) + + * Version 0.3. + + * xregex.c, regex.h (newline_anchor_match_p): rename to + `newline_anchor'; dumb idea to change the name. + +Tue Feb 18 07:09:02 1992 Karl Berry (karl at hayley) + + * regexinc.c: go back to original, i.e., don't include + or define strchr. + * xregex.c (regexec): don't bother with adding characters after + newlines to the fastmap; instead, just don't use a fastmap. + * xregex.c (regcomp): set the buffer and fastmap fields to zero. + + * xregex.texinfo (GNU r.e. compiling): have to initialize more + than two fields. + + * regex.h (struct re_pattern_buffer): rename `newline_anchor' to + `newline_anchor_match_p', as we're back to two cases. + * xregex.c (regcomp, re_compile_pattern, re_comp): change + accordingly. + (re_match_2): at begline and endline, POSIX is not a special + case anymore; just check newline_anchor_match_p. + +Thu Feb 13 16:29:33 1992 Karl Berry (karl at hayley) + + * xregex.c (*empty_string*): rename to *null_string*, for brevity. + +Wed Feb 12 06:36:22 1992 Karl Berry (karl at hayley) + + * xregex.c (re_compile_fastmap): at endline, don't set fastmap['\n']. + (re_match_2): rewrite the begline/endline cases to take account + of the new field newline_anchor. + +Tue Feb 11 14:34:55 1992 Karl Berry (karl at hayley) + + * regexinc.c [!USG etc.]: include and define strchr + as index. + + * xregex.c (re_search_2): when searching backwards, declare `c' + as a char and use casts when using it as an array subscript. + + * xregex.c (regcomp): if REG_NEWLINE, set + RE_HAT_LISTS_NOT_NEWLINE. Set the `newline_anchor' field + appropriately. + (regex_compile): compile [^...] as matching a \n according to + the syntax bit. + (regexec): if doing REG_NEWLINE stuff, compile a fastmap and add + characters after any \n's to the newline. + * regex.h (RE_HAT_LISTS_NOT_NEWLINE): new syntax bit. + (struct re_pattern_buffer): rename `posix_newline' to + `newline_anchor', define constants for its values. + +Mon Feb 10 07:22:50 1992 Karl Berry (karl at hayley) + + * xregex.c (re_compile_fastmap): combine the code at the top and + bottom of the loop, as it's essentially identical. + +Sun Feb 9 10:02:19 1992 Karl Berry (karl at hayley) + + * xregex.texinfo (POSIX Translate Tables): remove this, as it + doesn't match the spec. + + * xregex.c (re_compile_fastmap): if we finish off a path, go + back to the top (to set can_be_null) instead of returning + immediately. + + * xregex.texinfo: changes from bob. + +Sat Feb 1 07:03:25 1992 Karl Berry (karl at hayley) + + * xregex.c (re_search_2): doc fix (from rms). + +Fri Jan 31 09:52:04 1992 Karl Berry (karl at hayley) + + * xregex.texinfo (GNU Searching): clarify the range arg. + + * xregex.c (re_match_2, at_endline_op_p): add extra parens to + get rid of GCC 2's (silly, IMHO) warning about && within ||. + + * xregex.c (common_op_match_empty_string_p): use + MATCH_NOTHING_UNSET_VALUE, not -1. + +Thu Jan 16 08:43:02 1992 Karl Berry (karl at hayley) + + * xregex.c (SET_REGS_MATCHED): only set the registers from + lowest to highest. + + * regexinc.c (MIN): new macro. + * xregex.c (re_match_2): only check min (num_regs, + regs->num_regs) when we set the returned regs. + + * xregex.c (re_match_2): set registers after the first + num_regs to -1 before we return. + +Tue Jan 14 16:01:42 1992 Karl Berry (karl at hayley) + + * xregex.c (re_match_2): initialize max (RE_NREGS, re_nsub + 1) + registers (from rms). + + * xregex.c, regex.h: don't abbreviate `19xx' to `xx'. + + * regexinc.c [!emacs]: include before . + (from ro@thp.Uni-Koeln.DE). + +Thu Jan 9 07:23:00 1992 Karl Berry (karl at hayley) + + * xregex.c (*unmatchable): rename to `match_empty_string_p'. + (CAN_MATCH_NOTHING): rename to `REG_MATCH_EMPTY_STRING_P'. + + * regexinc.c (malloc, realloc): remove prototypes, as they can + cause clashes (from rms). + +Mon Jan 6 12:43:24 1992 Karl Berry (karl at claude.cs.umb.edu) + + * Version 0.2. + +Sun Jan 5 10:50:38 1992 Karl Berry (karl at hayley) + + * xregex.texinfo: bring more or less up-to-date. + * GNUmakefile (regex.texinfo): generate from regex.h and + xregex.texinfo. + * include.awk: new file. + + * xregex.c: change all calls to the fn extract_number_and_incr + to the macro. + + * xregex.c (re_match_2) [emacs]: in at_dot, use PTR_CHAR_POS + 1, + instead of bf_* and sl_*. Cast d to unsigned char *, to match + the declaration in Emacs' buffer.h. + [emacs19]: in before_dot, at_dot, and after_dot, likewise. + + * regexinc.c: unconditionally include . + + * regexinc.c (alloca) [!alloca]: Emacs config files sometimes + define this, so don't define it if it's already defined. + +Sun Jan 5 06:06:53 1992 Karl Berry (karl at fosse) + + * xregex.c (re_comp): fix type conflicts with regex_compile (we + haven't been compiling this). + + * regexinc.c (SIGN_EXTEND_CHAR): use `__CHAR_UNSIGNED__', not + `CHAR_UNSIGNED'. + + * regexinc.c (NULL) [!NULL]: define it (as zero). + + * regexinc.c (extract_number): remove the temporaries. + +Sun Jan 5 07:50:14 1992 Karl Berry (karl at hayley) + + * regex.h (regerror) [!__STDC__]: return a size_t, not a size_t *. + + * xregex.c (PUSH_FAILURE_POINT, ...): declare `destination' as + `char *' instead of `void *', to match alloca declaration. + + * xregex.c (regerror): use `size_t' for the intermediate values + as well as the return type. + + * xregex.c (regexec): cast the result of malloc. + + * xregex.c (regexec): don't initialize `private_preg' in the + declaration, as old C compilers can't do that. + + * xregex.c (main) [test]: declare printchar void. + + * xregex.c (assert) [!DEBUG]: define this to do nothing, and + remove #ifdef DEBUG's from around asserts. + + * xregex.c (re_match_2): remove error message when not debugging. + +Sat Jan 4 09:45:29 1992 Karl Berry (karl at hayley) + + * other.c: test the bizarre duplicate case in re_compile_fastmap + that I just noticed. + + * test.c (general_test): don't test registers beyond the end of + correct_regs, as well as regs. + + * xregex.c (regex_compile): at handle_close, don't assign to + *inner_group_loc if we didn't push a start_memory (because the + group number was too big). In fact, don't push or pop the + inner_group_offset in that case. + + * regex.c: rename to xregex.c, since it's not the whole thing. + * regex.texinfo: likewise. + * GNUmakefile: change to match. + + * regex.c [DEBUG]: only include if debugging. + + * regexinc.c (SIGN_EXTEND_CHAR) [CHAR_UNSIGNED]: if it's already + defined, don't redefine it. + + * regex.c: define _GNU_SOURCE at the beginning. + * regexinc.c (isblank) [!isblank]: define it. + (isgraph) [!isgraph]: change conditional to this, and remove the + sequent stuff. + + * regex.c (regex_compile): add `blank' character class. + + * regex.c (regex_compile): don't use a uchar variable to loop + through all characters. + + * regex.c (regex_compile): at '[', improve logic for checking + that we have enough space for the charset. + + * regex.h (struct re_pattern_buffer): declare translate as char + * again. We only use it as an array subscript once, I think. + + * regex.c (TRANSLATE): new macro to cast the data character + before subscripting. + (num_internal_regs): rename to `num_regs'. + +Fri Jan 3 07:58:01 1992 Karl Berry (karl at hayley) + + * regex.h (struct re_pattern_buffer): declare `allocated' and + `used' as unsigned long, since these are never negative. + + * regex.c (compile_stack_element): rename to compile_stack_elt_t. + (failure_stack_element): similarly. + + * regexinc.c (TALLOC, RETALLOC): new macros to simplify + allocation of arrays. + + * regex.h (re_*) [__STDC__]: don't declare string args unsigned + char *; that makes them incompatible with string constants. + (struct re_pattern_buffer): declare the pattern and translate + table as unsigned char *. + * regex.c (most routines): use unsigned char vs. char consistently. + + * regex.h (re_compile_pattern): do not declare the length arg as + const. + * regex.c (re_compile_pattern): likewise. + + * regex.c (POINTER_TO_REG): rename to `POINTER_TO_OFFSET'. + + * regex.h (re_registers): declare `start' and `end' as + `regoff_t', instead of `int'. + + * regex.c (regexec): if either of the malloc's for the register + information fail, return failure. + + * regex.h (RE_NREGS): define this again, as 30 (from jla). + (RE_ALLOCATE_REGISTERS): remove this. + (RE_SYNTAX_*): remove it from definitions. + (re_pattern_buffer): remove `return_default_num_regs', add + `caller_allocated_regs'. + * regex.c (re_compile_pattern): clear no_sub and + caller_allocated_regs in the pattern. + (regcomp): set caller_allocated_regs. + (re_match_2): do all register allocation at the end of the + match; implement new semantics. + + * regex.c (MAX_REGNUM): new macro. + (regex_compile): at handle_open and handle_close, if the group + number is too large, don't push the start/stop memory. + +Thu Jan 2 07:56:10 1992 Karl Berry (karl at hayley) + + * regex.c (re_match_2): if the back reference is to a group that + never matched, then goto fail, not really_fail. Also, don't + test if the pattern can match the empty string. Why did we + ever do that? + (really_fail): this label no longer needed. + + * regexinc.c [STDC_HEADERS]: use only this to test if we should + include . + + * regex.c (DO_RANGE, regex_compile): translate in all cases + except the single character after a \. + + * regex.h (RE_AWK_CLASS_HACK): rename to + RE_BACKSLASH_ESCAPE_IN_LISTS. + * regex.c (regex_compile): change use. + + * regex.c (re_compile_fastmap): do not translate the characters + again; we already translated them at compilation. (From ylo@ngs.fi.) + + * regex.c (re_match_2): in case for at_dot, invert sense of + comparison and find the character number properly. (From + worley@compass.com.) + (re_match_2) [emacs]: remove the cases for before_dot and + after_dot, since there's no way to specify them, and the code + is wrong (judging from this change). + +Wed Jan 1 09:13:38 1992 Karl Berry (karl at hayley) + + * psx-{interf,basic,extend}.c, other.c: set `t' as the first + thing, so that if we run them in sucession, general_test's + kludge to see if we're doing POSIX tests works. + + * test.h (test_type): add `all_test'. + * main.c: add case for `all_test'. + + * regexinc.c (partial_compiled_pattern_printer, + double_string_printer): don't print anything if we're passed null. + + * regex.c (PUSH_FAILURE_POINT): do not scan for the highest and + lowest active registers. + (re_match_2): compute lowest/highest active regs at start_memory and + stop_memory. + (NO_{LOW,HIGH}EST_ACTIVE_REG): new sentinel values. + (pop_failure_point): return the lowest/highest active reg values + popped; change calls. + + * regex.c [DEBUG]: include . + (various routines) [DEBUG]: change conditionals to assertions. + + * regex.c (DEBUG_STATEMENT): new macro. + (PUSH_FAILURE_POINT): use it to increment num_regs_pushed. + (re_match_2) [DEBUG]: only declare num_regs_pushed if DEBUG. + + * regex.c (*can_match_nothing): rename to *unmatchable. + + * regex.c (re_match_2): at stop_memory, adjust argument reading. + + * regex.h (re_pattern_buffer): declare `can_be_null' as a 2-bit + bit field. + + * regex.h (re_pattern_buffer): declare `buffer' unsigned char *; + no, dumb idea. The pattern can have signed number. + + * regex.c (re_match_2): in maybe_pop_jump case, skip over the + right number of args to the group operators, and don't do + anything with endline if posix_newline is not set. + + * regex.c, regexinc.c (all the things we just changed): go back + to putting the inner group count after the start_memory, + because we need it in the on_failure_jump case in re_match_2. + But leave it after the stop_memory also, since we need it + there in re_match_2, and we don't have any way of getting back + to the start_memory. + + * regexinc.c (partial_compiled_pattern_printer): adjust argument + reading for start/stop_memory. + * regex.c (re_compile_fastmap, group_can_match_nothing): likewise. + +Tue Dec 31 10:15:08 1991 Karl Berry (karl at hayley) + + * regex.c (bits list routines): remove these. + (re_match_2): get the number of inner groups from the pattern, + instead of keeping track of it at start and stop_memory. + Put the count after the stop_memory, not after the + start_memory. + (compile_stack_element): remove `fixup_inner_group' member, + since we now put it in when we can compute it. + (regex_compile): at handle_open, don't push the inner group + offset, and at handle_close, don't pop it. + + * regex.c (level routines): remove these, and their uses in + regex_compile. This was another manifestation of having to find + $'s that were endlines. + + * regex.c (regexec): this does searching, not matching (a + well-disguised part of the standard). So rewrite to use + `re_search' instead of `re_match'. + * psx-interf.c (test_regexec): add tests to, uh, match. + + * regex.h (RE_TIGHT_ALT): remove this; nobody uses it. + * regex.c: remove the code that was supposed to implement it. + + * other.c (test_others): ^ and $ never match newline characters; + RE_CONTEXT_INVALID_OPS doesn't affect anchors. + + * psx-interf.c (test_regerror): update for new error messages. + + * psx-extend.c: it's now ok to have an alternative be just a $, + so remove all the tests which supposed that was invalid. + +Wed Dec 25 09:00:05 1991 Karl Berry (karl at hayley) + + * regex.c (regex_compile): in handle_open, don't skip over ^ and + $ when checking for an empty group. POSIX has changed the + grammar. + * psx-extend.c (test_posix_extended): thus, move (^$) tests to + valid section. + + * regexinc.c (boolean): move from here to test.h and regex.c. + * test files: declare verbose, omit_register_tests, and + test_should_match as boolean. + + * psx-interf.c (test_posix_c_interface): remove the `c_'. + * main.c: likewise. + + * psx-basic.c (test_posix_basic): ^ ($) is an anchor after + (before) an open (close) group. + + * regex.c (re_match_2): in endline, correct precedence of + posix_newline condition. + +Tue Dec 24 06:45:11 1991 Karl Berry (karl at hayley) + + * test.h: incorporate private-tst.h. + * test files: include test.h, not private-tst.h. + + * test.c (general_test): set posix_newline to zero if we are + doing POSIX tests (unfortunately, it's difficult to call + regcomp in this case, which is what we should really be doing). + + * regex.h (reg_syntax_t): make this an enumeration type which + defines the syntax bits; renames re_syntax_t. + + * regex.c (at_endline_op_p): don't preincrement p; then if it's + not an empty string op, we lose. + + * regex.h (reg_errcode_t): new enumeration type of the error + codes. + * regex.c (regex_compile): return that type. + + * regex.c (regex_compile): in [, initialize + just_had_a_char_class to false; somehow I had changed this to + true. + + * regex.h (RE_NO_CONSECUTIVE_REPEATS): remove this, since we + don't use it, and POSIX doesn't require this behavior anymore. + * regex.c (regex_compile): remove it from here. + + * regex.c (regex_compile): remove the no_op insertions for + verify_and_adjust_endlines, since that doesn't exist anymore. + + * regex.c (regex_compile) [DEBUG]: use printchar to print the + pattern, so unprintable bytes will print properly. + + * regex.c: move re_error_msg back. + * test.c (general_test): print the compile error if the pattern + was invalid. + +Mon Dec 23 08:54:53 1991 Karl Berry (karl at hayley) + + * regexinc.c: move re_error_msg here. + + * regex.c (re_error_msg): the ``message'' for success must be + NULL, to keep the interface to re_compile_pattern the same. + (regerror): if the msg is null, use "Success". + + * rename most test files for consistency. Change Makefile + correspondingly. + + * test.c (most routines): add casts to (unsigned char *) when we + call re_{match,search}{,_2}. + +Sun Dec 22 09:26:06 1991 Karl Berry (karl at hayley) + + * regex.c (re_match_2): declare string args as unsigned char * + again; don't declare non-pointer args const; declare the + pattern buffer const. + (re_match): likewise. + (re_search_2, re_search): likewise, except don't declare the + pattern const, since we make a fastmap. + * regex.h [__STDC__]: change prototypes. + + * regex.c (regex_compile): return an error code, not a string. + (re_err_list): new table to map from error codes to string. + (re_compile_pattern): return an element of re_err_list. + (regcomp): don't test all the strings. + (regerror): just use the list. + (put_in_buffer): remove this. + + * regex.c (equivalent_failure_points): remove this. + + * regex.c (re_match_2): don't copy the string arguments into + non-const pointers. We never alter the data. + + * regex.c (re_match_2): move assignment to `is_a_jump_n' out of + the main loop. Just initialize it right before we do + something with it. + + * regex.[ch] (re_match_2): don't declare the int parameters const. + +Sat Dec 21 08:52:20 1991 Karl Berry (karl at hayley) + + * regex.h (re_syntax_t): new type; declare to be unsigned + (previously we used int, but since we do bit operations on + this, unsigned is better, according to H&S). + (obscure_syntax, re_pattern_buffer): use that type. + * regex.c (re_set_syntax, regex_compile): likewise. + + * regex.h (re_pattern_buffer): new field `posix_newline'. + * regex.c (re_comp, re_compile_pattern): set to zero. + (regcomp): set to REG_NEWLINE. + * regex.h (RE_HAT_LISTS_NOT_NEWLINE): remove this (we can just + check `posix_newline' instead.) + + * regex.c (op_list_type, op_list, add_op): remove these. + (verify_and_adjust_endlines): remove this. + (pattern_offset_list_type, *pattern_offset* routines): and these. + These things all implemented the nonleading/nontrailing position + code, which was very long, had a few remaining problems, and + is no longer needed. So... + + * regexinc.c (STREQ): new macro to abbreviate strcmp(,)==0, for + brevity. Change various places in regex.c to use it. + + * regex{,inc}.c (enum regexpcode): change to a typedef + re_opcode_t, for brevity. + + * regex.h (re_syntax_table) [SYNTAX_TABLE]: remove this; it + should only be in regex.c, I think, since we don't define it + in this case. Maybe it should be conditional on !SYNTAX_TABLE? + + * regexinc.c (partial_compiled_pattern_printer): simplify and + distinguish the emacs/not-emacs (not)wordchar cases. + +Fri Dec 20 08:11:38 1991 Karl Berry (karl at hayley) + + * regexinc.c (regexpcode) [emacs]: only define the Emacs opcodes + if we are ifdef emacs. + + * regex.c (BUF_PUSH*): rename to PAT_PUSH*. + + * regex.c (regex_compile): in $ case, go back to essentially the + original code for deciding endline op vs. normal char. + (at_endline_op_p): new routine. + * regex.h (RE_ANCHORS_ONLY_AT_ENDS, RE_CONTEXT_INVALID_ANCHORS, + RE_REPEATED_ANCHORS_AWAY, RE_NO_ANCHOR_AT_NEWLINE): remove + these. POSIX has simplified the rules for anchors in draft + 11.2. + (RE_NEWLINE_ORDINARY): new syntax bit. + (RE_CONTEXT_INDEP_ANCHORS): change description to be compatible + with POSIX. + * regex.texinfo (Syntax Bits): remove the descriptions. + +Mon Dec 16 08:12:40 1991 Karl Berry (karl at hayley) + + * regex.c (re_match_2): in jump_past_next_alt, unconditionally + goto no_pop. The only register we were finding was one which + enclosed the whole alternative expression, not one around an + individual alternative. So we were never doing what we + thought we were doing, and this way makes (|a) against the + empty string fail. + + * regex.c (regex_compile): remove `highest_ever_regnum', and + don't restore regnum from the stack; just put it into a + temporary to put into the stop_memory. Otherwise, groups + aren't numbered consecutively. + + * regex.c (is_in_compile_stack): rename to + `group_in_compile_stack'; remove unnecessary test for the + stack being empty. + + * regex.c (re_match_2): in on_failure_jump, skip no_op's before + checking for the start_memory, in case we were called from + succeed_n. + +Sun Dec 15 16:20:48 1991 Karl Berry (karl at hayley) + + * regex.c (regex_compile): in duplicate case, use + highest_ever_regnum instead of regnum, since the latter is + reverted at stop_memory. + + * regex.c (re_match_2): in on_failure_jump, if the * applied to + a group, save the information for that group and all inner + groups (by making it active), even though we're not inside it + yet. + +Sat Dec 14 09:50:59 1991 Karl Berry (karl at hayley) + + * regex.c (PUSH_FAILURE_ITEM, POP_FAILURE_ITEM): new macros. + Use them instead of copying the stack manipulating a zillion + times. + + * regex.c (PUSH_FAILURE_POINT, pop_failure_point) [DEBUG]: save + and restore a unique identification value for each failure point. + + * regexinc.c (partial_compiled_pattern_printer): don't print an + extra / after duplicate commands. + + * regex.c (regex_compile): in back-reference case, allow a back + reference to register `regnum'. Otherwise, even `\(\)\1' + fails, since regnum is 1 at the back-reference. + + * regex.c (re_match_2): in fail, don't examine the pattern if we + restored to pend. + + * test_private.h: rename to private_tst.h. Change includes. + + * regex.c (extend_bits_list): compute existing size for realloc + in bytes, not blocks. + + * regex.c (re_match_2): in jump_past_next_alt, the for loop was + missing its (empty) statement. Even so, some register tests + still fail, although in a different way than in the previous change. + +Fri Dec 13 15:55:08 1991 Karl Berry (karl at hayley) + + * regex.c (re_match_2): in jump_past_next_alt, unconditionally + goto no_pop, since we weren't properly detecting if the + alternative matched something anyway. No, we need to not jump + to keep the register values correct; just change to not look at + register zero and not test RE_NO_EMPTY_ALTS (which is a + compile-time thing). + + * regex.c (SET_REGS_MATCHED): start the loop at 1, since we never + care about register zero until the very end. (I think.) + + * regex.c (PUSH_FAILURE_POINT, pop_failure_point): go back to + pushing and popping the active registers, instead of only doing + the registers before a group: (fooq|fo|o)*qbar against fooqbar + fails, since we restore back into the middle of group 1, yet it + isn't active, because the previous restore clobbered the active flag. + +Thu Dec 12 17:25:36 1991 Karl Berry (karl at hayley) + + * regex.c (PUSH_FAILURE_POINT): do not call + `equivalent_failure_points' after all; it causes the registers + to be ``wrong'' (according to POSIX), and an infinite loop on + `((a*)*)*' against `ab'. + + * regex.c (re_compile_fastmap): don't push `pend' on the failure + stack. + +Tue Dec 10 10:30:03 1991 Karl Berry (karl at hayley) + + * regex.c (PUSH_FAILURE_POINT): if pushing same failure point that + is on the top of the stack, fail. + (equivalent_failure_points): new routine. + + * regex.c (re_match_2): add debug statements for every opcode we + execute. + + * regex.c (regex_compile/handle_close): restore + `fixup_inner_group_count' and `regnum' from the stack. + +Mon Dec 9 13:51:15 1991 Karl Berry (karl at hayley) + + * regex.c (PUSH_FAILURE_POINT): declare `this_reg' as int, so + unsigned arithmetic doesn't happen when we don't want to save + the registers. + +Tue Dec 3 08:11:10 1991 Karl Berry (karl at hayley) + + * regex.c (extend_bits_list): divide size by bits/block. + + * regex.c (init_bits_list): remove redundant assignmen to + `bits_list_ptr'. + + * regexinc.c (partial_compiled_pattern_printer): don't do *p++ + twice in the same expr. + + * regex.c (re_match_2): at on_failure_jump, use the correct + pattern positions for getting the stuff following the start_memory. + + * regex.c (struct register_info): remove the bits_list for the + inner groups; make that a separate variable. + +Mon Dec 2 10:42:07 1991 Karl Berry (karl at hayley) + + * regex.c (PUSH_FAILURE_POINT): don't pass `failure_stack' as an + arg; change callers. + + * regex.c (PUSH_FAILURE_POINT): print items in order they are + pushed. + (pop_failure_point): likewise. + + * regex.c (main): prompt for the pattern and string. + + * regex.c (FREE_VARIABLES) [!REGEX_MALLOC]: declare as nothing; + remove #ifdefs from around calls. + + * regex.c (extract_number, extract_number_and_incr): declare static. + + * regex.c: remove the canned main program. + * main.c: new file. + * Makefile (COMMON): add main.o. + +Tue Sep 24 06:26:51 1991 Kathy Hargreaves (kathy at fosse) + + * regex.c (re_match_2): Made `pend' and `dend' not register variables. + Only set string2 to string1 if string1 isn't null. + Send address of p, d, regstart, regend, and reg_info to + pop_failure_point. + Put in more debug statements. + + * regex.c [debug]: Added global variable. + (DEBUG_*PRINT*): Only print if `debug' is true. + (DEBUG_DOUBLE_STRING_PRINTER): Changed DEBUG_STRING_PRINTER's + name to this. + Changed some comments. + (PUSH_FAILURE_POINT): Moved and added some debugging statements. + Was saving regstart on the stack twice instead of saving both + regstart and regend; remedied this. + [NUM_REGS_ITEMS]: Changed from 3 to 4, as now save lowest and + highest active registers instead of highest used one. + [NUM_NON_REG_ITEMS]: Changed name of NUM_OTHER_ITEMS to this. + (NUM_FAILURE_ITEMS): Use active registers instead of number 0 + through highest used one. + (re_match_2): Have pop_failure_point put things in the variables. + (pop_failure_point): Have it do what the fail case in re_match_2 + did with the failure stack, instead of throwing away the stuff + popped off. re_match_2 can ignore results when it doesn't + need them. + + +Thu Sep 5 13:23:28 1991 Kathy Hargreaves (kathy at fosse) + + * regex.c (banner): Changed copyright years to be separate. + + * regex.c [CHAR_UNSIGNED]: Put __ at both ends of this name. + [DEBUG, debug_count, *debug_p, DEBUG_PRINT_1, DEBUG_PRINT_2, + DEBUG_COMPILED_PATTERN_PRINTER ,DEBUG_STRING_PRINTER]: + defined these for debugging. + (extract_number): Added this (debuggable) routine version of + the macro EXTRACT_NUMBER. Ditto for EXTRACT_NUMBER_AND_INCR. + (re_compile_pattern): Set return_default_num_regs if the + syntax bit RE_ALLOCATE_REGISTERS is set. + [REGEX_MALLOC]: Renamed USE_ALLOCA to this. + (BUF_POP): Got rid of this, as don't ever use it. + (regex_compile): Made the type of `pattern' not be register. + If DEBUG, print the pattern to compile. + (re_match_2): If had a `$' in the pattern before a `^' then + don't record the `^' as an anchor. + Put (enum regexpcode) before references to b, as suggested + [RE_NO_BK_BRACES]: Changed RE_NO_BK_CURLY_BRACES to this. + (remove_pattern_offset): Removed this unused routine. + (PUSH_FAILURE_POINT): Changed to only save active registers. + Put in debugging statements. + (re_compile_fastmap): Made `pattern' not a register variable. + Use routine for extracting numbers instead of macro. + (re_match_2): Made `p', `mcnt' and `mcnt2' not register variables. + Added `num_regs_pushed' for debugging. + Only malloc registers if the syntax bit RE_ALLOCATE_REGISTERS is set. + Put in debug statements. + Put the macro NOTE_INNER_GROUP's code inline, as it was the + only called in one place. + For debugging, extract numbers using routines instead of macros. + In case fail: only restore pushed active registers, and added + debugging statements. + (pop_failure_point): Test for underfull stack. + (group_can_match_nothing, common_op_can_match_nothing): For + debugging, extract numbers using routines instead of macros. + (regexec): Changed formal parameters to not be prototypes. + Don't initialize `regs' or `private_preg' in their declarations. + +Tue Jul 23 18:38:36 1991 Kathy Hargreaves (kathy at hayley) + + * regex.h [RE_CONTEX_INDEP_OPS]: Moved the anchor stuff out of + this bit. + [RE_UNMATCHED_RIGHT_PAREN_ORD]: Defined this bit. + [RE_CONTEXT_INVALID_ANCHORS]: Defined this bit. + [RE_CONTEXT_INDEP_ANCHORS]: Defined this bit. + Added RE_CONTEXT_INDEP_ANCHORS to all syntaxes which had + RE_CONTEXT_INDEP_OPS. + Took RE_ANCHORS_ONLY_AT_ENDS out of the POSIX basic syntax. + Added RE_UNMATCHED_RIGHT_PAREN_ORD to the POSIX extended + syntax. + Took RE_REPEATED_ANCHORS_AWAY out of the POSIX extended syntax. + Defined REG_NOERROR (which will probably have to go away again). + Changed the type `off_t' to `regoff_t'. + + * regex.c: Changed some commments. + (regex_compile): Added variable `had_an_endline' to keep track + of if hit a `$' since the beginning of the pattern or the last + alternative (if any). + Changed RE_CONTEXT_INVALID_OPS and RE_CONTEXT_INDEP_OPS to + RE_CONTEXT_INVALID_ANCHORS and RE_CONTEXT_INDEP_ANCHORS where + appropriate. + Put a `no_op' in the pattern if a repeat is only zero or one + times; in this case and if it is many times (whereupon a jump + backwards is pushed instead), keep track of the operator for + verify_and_adjust_endlines. + If RE_UNMATCHED_RIGHT_PAREN is set, make an unmatched + close-group operator match `)'. + Changed all error exits to exit (1). + (remove_pattern_offset): Added this routine, but don't use it. + (verify_and_adjust_endlines): At top of routine, if initialize + routines run out of memory, return true after setting + enough_memory false. + At end of endline, et al. case, don't set *p to no_op. + Repetition operators also set the level and active groups' + match statuses, unless RE_REPEATED_ANCHORS_AWAY is set. + (get_group_match_status): Put a return in front of call to get_bit. + (re_compile_fastmap): Changed is_a_succeed_n to a boolean. + If at end of pattern, then if the failure stack isn't empty, + go back to the failure point. + In *jump* case, only pop the stack if what's on top of it is + where we've just jumped to. + (re_search_2): Return -2 instead of val if val is -2. + (group_can_match_nothing, alternative_can_match_nothing, + common_op_can-match_nothing): Now pass in reg_info for the + `duplicate' case. + (re_match_2): Don't skip over the next alternative also if + empty alternatives aren't allowed. + In fail case, if failed to a backwards jump that's part of a + repetition loop, pop the current failure point and use the + next one. + (pop_failure_point): Check that there's as many register items + on the failure stack as the stack says there are. + (common_op_can_match_nothing): Added variables `ret' and + `reg_no' so can set reg_info for the group encountered. + Also break without doing anything if hit a no_op or the other + kinds of `endline's. + If not done already, set reg_info in start_memory case. + Put in no_pop_jump for an optimized succeed_n of zero repetitions. + In succeed_n case, if the number isn't zero, then return false. + Added `duplicate' case. + +Sat Jul 13 11:27:38 1991 Kathy Hargreaves (kathy at hayley) + + * regex.h (REG_NOERROR): Added this error code definition. + + * regex.c: Took some redundant parens out of macros. + (enum regexpcode): Added jump_past_next_alt. + Wrapped some macros in `do..while (0)'. + Changed some comments. + (regex_compile): Use `fixup_alt_jump' instead of `fixup_jump'. + Use `maybe_pop_jump' instead of `maybe_pop_failure_jump'. + Use `jump_past_next_alt' instead of `no_pop_jump' when at the + end of an alternative. + (re_match_2): Used REGEX_ALLOCATE for the registers stuff. + In stop_memory case: Add more boolean tests to see if the + group is in a loop. + Added jump_past_next_alt case, which doesn't jump over the + next alternative if the last one didn't match anything. + Unfortunately, to make this work with, e.g., `(a+?*|b)*' + against `bb', I also had to pop the alternative's failure + point, which in turn broke backtracking! + In fail case: Detect a dummy failure point by looking at + failure_stack.avail - 2, not stack[-2]. + (pop_failure_point): Only pop if the stack isn't empty; don't + give an error if it is. (Not sure yet this is correct.) + (group_can_match_nothing): Make it return a boolean instead of int. + Make it take an argument indicating the end of where it should look. + If find a group that can match nothing, set the pointer + argument to past the group in the pattern. + Took out cases which can share with alternative_can_match_nothing + and call common_op_can_match_nothing. + Took ++ out of switch, so could call common_op_can_match_nothing. + Wrote lots more for on_failure_jump case to handle alternatives. + Main loop now doesn't look for matching stop_memory, but + rather the argument END; return true if hit the matching + stop_memory; this way can call itself for inner groups. + (alternative_can_match_nothing): Added for alternatives. + (common_op_can_match_nothing): Added for previous two routines' + common operators. + (regerror): Returns a message saying there's no error if gets + sent REG_NOERROR. + +Wed Jul 3 10:43:15 1991 Kathy Hargreaves (kathy at hayley) + + * regex.c: Removed unnecessary enclosing parens from several macros. + Put `do..while (0)' around a few. + Corrected some comments. + (INIT_FAILURE_STACK_SIZE): Deleted in favor of using + INIT_FAILURE_ALLOC. + (INIT_FAILURE_STACK, DOUBLE_FAILURE_STACK, PUSH_PATTERN_OP, + PUSH_FAILURE_POINT): Made routines of the same name (but with all + lowercase letters) into these macros, so could use `alloca' + when USE_ALLOCA is defined. The reason is stated below for + bits lists. Deleted analogous routines. + (re_compile_fastmap): Added variable void *destination for + PUSH_PATTERN_OP. + (re_match_2): Added variable void *destination for REGEX_REALLOCATE. + Used the failure stack macros in place of the routines. + Detected a dummy failure point by inspecting the failure stack's + (avail - 2)th element, not failure_stack.stack[-2]. This bug + arose when used the failure stack macros instead of the routines. + + * regex.c [USE_ALLOCA]: Put this conditional around previous + alloca stuff and defined these to work differently depending + on whether or not USE_ALLOCA is defined: + (REGEX_ALLOCATE): Uses either `alloca' or `malloc'. + (REGEX_REALLOCATE): Uses either `alloca' or `realloc'. + (INIT_BITS_LIST, EXTEND_BITS_LIST, SET_BIT_TO_VALUE): Defined + macro versions of routines with the same name (only with all + lowercase letters) so could use `alloc' in re_match_2. This + is to prevent core leaks when C-g is used in Emacs and to make + things faster and avoid storage fragmentation. These things + have to be macros because the results of `alloca' go away with + the routine by which it's called. + (BITS_BLOCK_SIZE, BITS_BLOCK, BITS_MASK): Moved to above the + above-mentioned macros instead of before the routines defined + below regex_compile. + (set_bit_to_value): Compacted some code. + (reg_info_type): Changed inner_groups field to be bits_list_type + so could be arbitrarily long and thus handle arbitrary nesting. + (NOTE_INNER_GROUP): Put `do...while (0)' around it so could + use as a statement. + Changed code to use bits lists. + Added variable void *destination for REGEX_REALLOCATE (whose call + is several levels in). + Changed variable name of `this_bit' to `this_reg'. + (FREE_VARIABLES): Only define and use if USE_ALLOCA is defined. + (re_match_2): Use REGEX_ALLOCATE instead of malloc. + Instead of setting INNER_GROUPS of reg_info to zero, have to + use INIT_BITS_LIST and return -2 (and free variables if + USE_ALLOCA isn't defined) if it fails. + +Fri Jun 28 13:45:07 1991 Karl Berry (karl at hayley) + + * regex.c (re_match_2): set value of `dend' when we restore `d'. + + * regex.c: remove declaration of alloca. + + * regex.c (MISSING_ISGRAPH): rename to `ISGRAPH_MISSING'. + + * regex.h [_POSIX_SOURCE]: remove these conditionals; always + define POSIX stuff. + * regex.c (_POSIX_SOURCE): change conditionals to use `POSIX' + instead. + +Sat Jun 1 16:56:50 1991 Kathy Hargreaves (kathy at hayley) + + * regex.*: Changed RE_CONTEXTUAL_* to RE_CONTEXT_*, + RE_TIGHT_VBAR to RE_TIGHT_ALT, RE_NEWLINE_OR to + RE_NEWLINE_ALT, and RE_DOT_MATCHES_NEWLINE to RE_DOT_NEWLINE. + +Wed May 29 09:24:11 1991 Karl Berry (karl at hayley) + + * regex.texinfo (POSIX Pattern Buffers): cross-reference the + correct node name (Match-beginning-of-line, not ..._line). + (Syntax Bits): put @code around all syntax bits. + +Sat May 18 16:29:58 1991 Karl Berry (karl at hayley) + + * regex.c (global): add casts to keep broken compilers from + complaining about malloc and realloc calls. + + * regex.c (isgraph) [MISSING_ISGRAPH]: change test to this, + instead of `#ifndef isgraph', since broken compilers can't + have both a macro and a symbol by the same name. + + * regex.c (re_comp, re_exec) [_POSIX_SOURCE]: do not define. + (regcomp, regfree, regexec, regerror) [_POSIX_SOURCE && !emacs]: + only define in this case. + +Mon May 6 17:37:04 1991 Kathy Hargreaves (kathy at hayley) + + * regex.h (re_search, re_search_2): Changed BUFFER to not be const. + + * regex.c (re_compile_pattern): `^' is in a leading position if + it precedes a newline. + (various routines): Added or changed header comments. + (double_pattern_offsets_list): Changed name from + `extend_pattern_offsets_list'. + (adjust_pattern_offsets_list): Changed return value from + unsigned to void. + (verify_and_adjust_endlines): Now returns `true' and `false' + instead of 1 and 0. + `$' is in a leading position if it follows a newline. + (set_bit_to_value, get_bit_value): Exit with error if POSITION < 0 + so now calling routines don't have to. + (init_failure_stack, inspect_failure_stack_top, + pop_failure_stack_top, push_pattern_op, double_failure_stack): + Now return value unsigned instead of boolean. + (re_search, re_search_2): Changed BUFP to not be const. + (re_search_2): Added variable const `private_bufp' to send to + re_match_2. + (push_failure_point): Made return value unsigned instead of boolean. + +Sat May 4 15:32:22 1991 Kathy Hargreaves (kathy at hayley) + + * regex.h (re_compile_fastmap): Added extern for this. + Changed some comments. + + * regex.c (re_compile_pattern): In case handle_bar: put invalid + pattern test before levels matching stuff. + Changed some commments. + Added optimizing test for detecting an empty alternative that + ends with a trailing '$' at the end of the pattern. + (re_compile_fastmap): Moved failure_stack stuff to before this + so could use it. Made its stack dynamic. + Made it return an int so that it could return -2 if its stack + couldn't be allocated. + Added to header comment (about the return values). + (init_failure_stack): Wrote so both re_match_2 and + re_compile_fastmap could use it similar stacks. + (double_failure_stack): Added for above reasons. + (push_pattern_op): Wrote for re_compile_fastmap. + (re_search_2): Now return -2 if re_compile_fastmap does. + (re_match_2): Made regstart and regend type failure_stack_element*. + (push_failure_point): Made pattern_place and string_place type + failure_stack_element*. + Call double_failure_stack now. + Return true instead of 1. + +Wed May 1 12:57:21 1991 Kathy Hargreaves (kathy at hayley) + + * regex.c (remove_intervening_anchors): Avoid erroneously making + ops into no_op's by making them no_op only when they're beglines. + (verify_and_adjust_endlines): Don't make '$' a normal character + if it's before a newline. + Look for the endline op in *p, not p[1]. + (failure_stack_element): Added this declaration. + (failure_stack_type): Added this declaration. + (INIT_FAILURE_STACK_SIZE, FAILURE_STACK_EMPTY, + FAILURE_STACK_PTR_EMPTY, REMAINING_AVAIL_SLOTS): Added for + failure stack. + (FAILURE_ITEM_SIZE, PUSH_FAILURE_POINT): Deleted. + (FREE_VARIABLES): Now free failure_stack.stack instead of stackb. + (re_match_2): deleted variables `initial_stack', `stackb', + `stackp', and `stacke' and added `failure_stack' to replace them. + Replaced calls to PUSH_FAILURE_POINT with those to + push_failure_point. + (push_failure_point): Added for re_match_2. + (pop_failure_point): Rewrote to use a failure_stack_type of stack. + (can_match_nothing): Moved definition to below re_match_2. + (bcmp_translate): Moved definition to below re_match_2. + +Mon Apr 29 14:20:54 1991 Kathy Hargreaves (kathy at hayley) + + * regex.c (enum regexpcode): Added codes endline_before_newline + and repeated_endline_before_newline so could detect these + types of endlines in the intermediate stages of a compiled + pattern. + (INIT_FAILURE_ALLOC): Renamed NFAILURES to this and set it to 5. + (BUF_PUSH): Put `do {...} while 0' around this. + (BUF_PUSH_2): Defined this to cut down on expansion of EXTEND_BUFFER. + (regex_compile): Changed some comments. + Now push endline_before_newline if find a `$' before a newline + in the pattern. + If a `$' might turn into an ordinary character, set laststart + to point to it. + In '^' case, if syntax bit RE_TIGHT_VBAR is set, then for `^' + to be in a leading position, it must be first in the pattern. + Don't have to check in one of the else clauses that it's not set. + If RE_CONTEXTUAL_INDEP_OPS isn't set but RE_ANCHORS_ONLY_AT_ENDS + is, make '^' a normal character if it isn't first in the pattern. + Can only detect at the end if a '$' after an alternation op is a + trailing one, so can't immediately detect empty alternatives + if a '$' follows a vbar. + Added a picture of the ``success jumps'' in alternatives. + Have to set bufp->used before calling verify_and_adjust_endlines. + Also do it before returning all error strings. + (remove_intervening_anchors): Now replaces the anchor with + repeated_endline_before_newline if it's an endline_before_newline. + (verify_and_adjust_endlines): Deleted SYNTAX parameter (could + use bufp's) and added GROUP_FORWARD_MATCH_STATUS so could + detect back references referring to empty groups. + Added variable `bend' to point past the end of the pattern buffer. + Added variable `previous_p' so wouldn't have to reinspect the + pattern buffer to see what op we just looked at. + Added endline_before_newline and repeated_endline_before_newline + cases. + When checking if in a trailing position, added case where '$' + has to be at the pattern's end if either of the syntax bits + RE_ANCHORS_ONLY_AT_ENDS or RE_TIGHT_VBAR are set. + Since `endline' can have the intermediate form `endline_in_repeat', + have to change it to `endline' if RE_REPEATED_ANCHORS_AWAY + isn't set. + Now disallow empty alternatives with trailing endlines in them + if RE_NO_EMPTY_ALTS is set. + Now don't make '$' an ordinary character if it precedes a newline. + Don't make it an ordinary character if it's before a newline. + Back references now affect the level matching something only if + they refer to nonempty groups. + (can_match_nothing): Now increment p1 in the switch, which + changes many of the cases, but makes the code more like what + it was derived from. + Adjust the return statement to reflect above. + (struct register_info): Made `can_match_nothing' field an int + instead of a bit so could have -1 in it if never set. + (MAX_FAILURE_ITEMS): Changed name from MAX_NUM_FAILURE_ITEMS. + (FAILURE_ITEM_SIZE): Defined how much space a failure items uses. + (PUSH_FAILURE_POINT): Changed variable `last_used_reg's name + to `highest_used_reg'. + Added variable `num_stack_items' and changed `len's name to + `stack_length'. + Test failure stack limit in terms of number of items in it, not + in terms of its length. rms' fix tested length against number + of items, which was a misunderstanding. + Use `realloc' instead of `alloca' to extend the failure stack. + Use shifts instead of multiplying by 2. + (FREE_VARIABLES): Free `stackb' instead of `initial_stack', as + might may have been reallocated. + (re_match_2): When mallocing `initial_stack', now multiply + the number of items wanted (what was there before) by + FAILURE_ITEM_SIZE. + (pop_failure_point): Need this procedure form of the macro of + the same name for debugging, so left it in and deleted the + macro. + (recomp): Don't free the pattern buffer's translate field. + +Mon Apr 15 09:47:47 1991 Kathy Hargreaves (kathy at hayley) + + * regex.h (RE_DUP_MAX): Moved to outside of #ifdef _POSIX_SOURCE. + * regex.c (#include ): Removed #ifdef _POSIX_SOURCE + condition. + (malloc, realloc): Made return type void* #ifdef __STDC__. + (enum regexpcode): Added endline_in_repeat for the compiler's + use; this never ends up on the final compiled pattern. + (INIT_PATTERN_OFFSETS_LIST_SIZE): Initial size for + pattern_offsets_list_type. + (pattern_offset_type): Type for pattern offsets. + (pattern_offsets_list_type): Type for keeping a list of + pattern offsets. + (anchor_list_type): Changed to above type. + (PATTERN_OFFSETS_LIST_PTR_FULL): Tests if a pattern offsets + list is full. + (ANCHOR_LIST_PTR_FULL): Changed to above. + (BIT_BLOCK_SIZE): Changed to BITS_BLOCK_SIZE and moved to + above bits list routines below regex_compile. + (op_list_type): Defined to be pattern_offsets_list_type. + (compile_stack_type): Changed offsets to be + pattern_offset_type instead of unsigned. + (pointer): Changed the name of all structure fields from this + to `avail'. + (COMPILE_STACK_FULL): Changed so the stack is full if `avail' + is equal to `size' instead of `size' - 1. + (GET_BUFFER_SPACE): Changed `>=' to `>' in the while statement. + (regex_compile): Added variable `enough_memory' so could check + that routine that verifies '$' positions could return an + allocation error. + (group_count): Deleted this variable, as `regnum' already does + this work. + (op_list): Added this variable to keep track of operations + needed for verifying '$' positions. + (anchor_list): Now initialize using routine + `init_pattern_offsets_list'. + Consolidated the three bits_list initializations. + In case '$': Instead of trying to go past constructs which can + follow '$', merely detect the special case where it has to be + at the pattern's end, fix up any fixup jumps if necessary, + record the anchor if necessary and add an `endline' (and + possibly two `no-op's) to the pattern; will call a routine at + the end to verify if it's in a valid position or not. + (init_pattern_offsets_list): Added to initialize pattern + offsets lists. + (extend_anchor_list): Renamed this extend_pattern_offsets_list + and renamed parameters and internal variables appropriately. + (add_pattern_offset): Added this routine which both + record_anchor_position and add_op call. + (adjust_pattern_offsets_list): Add this routine to adjust by + some increment all the pattern offsets a list of such after a + given position. + (record_anchor_position): Now send in offset instead of + calculating it and just call add_pattern_offset. + (adjust_anchor_list): Replaced by above routine. + (remove_intervening_anchors): If the anchor is an `endline' + then replace it with `endline_in_repeat' instead of `no_op'. + (add_op): Added this routine to call in regex_compile + wherever push something relevant to verifying '$' positions. + (verify_and_adjust_endlines): Added routine to (1) verify that + '$'s in a pattern buffer (represented by `endline') were in + valid positions and (2) whether or not they were anchors. + (BITS_BLOCK_SIZE): Renamed BIT_BLOCK_SIZE and moved to right + above bits list routines. + (BITS_BLOCK): Defines which array element of a bits list the + bit corresponding to a given position is in. + (BITS_MASK): Has a 1 where the bit (in a bit list array element) + for a given position is. + +Mon Apr 1 12:09:06 1991 Kathy Hargreaves (kathy at hayley) + + * regex.c (BIT_BLOCK_SIZE): Defined this for using with + bits_list_type, abstracted from level_list_type so could use + for more things than just the level match status. + (regex_compile): Renamed `level_list' variable to + `level_match_status'. + Added variable `group_match_status' of type bits_list_type. + Kept track of whether or not for all groups any of them + matched other than the empty string, so detect if a back + reference in front of a '^' made it nonleading or not. + Do this by setting a match status bit for all active groups + whenever leave a group that matches other than the empty string. + Could detect which groups are active by going through the + stack each time, but or-ing a bits list of active groups with + a bits list of group match status is faster, so make a bits + list of active groups instead. + Have to check that '^' isn't in a leading position before + going to normal_char. + Whenever set level match status of the current level, also set + the match status of all active groups. + Increase the group count and make that group active whenever + open a group. + When close a group, only set the next level down if the + current level matches other than the empty string, and make + the current group inactive. + At a back reference, only set a level's match status if the + group to which the back reference refers matches other than + the empty string. + (init_bits_list): Added to initialize a bits list. + (get_level_value): Deleted this. (Made into + get_level_match_status.) + (extend_bits_list): Added to extend a bits list. (Made this + from deleted routine `extend_level_list'.) + (get_bit): Added to get a bit value from a bits list. (Made + this from deleted routine `get_level_value'.) + (set_bit_to_value): Added to set a bit in a bits list. (Made + this from deleted routine `set_level_value'.) + (get_level_match_status): Added this to get the match status + of a given level. (Made from get_level_value.) + (set_this_level, set_next_lower_level): Made all routines + which set bits extend the bits list if necessary, thus they + now return an unsigned value to indicate whether or not the + reallocation failed. + (increase_level): No longer extends the level list. + (make_group_active): Added to mark as active a given group in + an active groups list. + (make_group_inactive): Added to mark as inactive a given group + in an active groups list. + (set_match_status_of_active_groups): Added to set the match + status of all currently active groups. + (get_group_match_status): Added to get a given group's match status. + (no_levels_match_anything): Removed the paramenter LEVEL. + (PUSH_FAILURE_POINT): Added rms' bug fix and changed RE_NREGS + to num_internal_regs. + +Sun Mar 31 09:04:30 1991 Kathy Hargreaves (kathy at hayley) + + * regex.h (RE_ANCHORS_ONLY_AT_ENDS): Added syntax so could + constrain '^' and '$' to only be anchors if at the beginning + and end of the pattern. + (RE_SYNTAX_POSIX_BASIC): Added the above bit. + + * regex.c (enum regexcode): Changed `unused' to `no_op'. + (this_and_lower_levels_match_nothing): Deleted forward reference. + (regex_compile): case '^': if the syntax bit RE_ANCHORS_ONLY_AT_ENDS + is set, then '^' is only an anchor if at the beginning of the + pattern; only record anchor position if the syntax bit + RE_REPEATED_ANCHORS_AWAY is set; the '^' is a normal char if + the syntax bit RE_ANCHORS_ONLY_AT_END is set and we're not at + the beginning of the pattern (and neither RE_CONTEXTUAL_INDEP_OPS + nor RE_CONTEXTUAL_INDEP_OPS syntax bits are set). + Only adjust the anchor list if the syntax bit + RE_REPEATED_ANCHORS_AWAY is set. + + * regex.c (level_list_type): Use to detect when '^' is + in a leading position. + (regex_compile): Added level_list_type level_list variable in + which we keep track of whether or not a grouping level (in its + current or most recent incarnation) matches anything besides the + empty string. Set the bit for the i-th level when detect it + should match something other than the empty string and the bit + for the (i-1)-th level when leave the i-th group. Clear all + bits for the i-th and higher levels if none of 0--(i - 1)-th's + bits are set when encounter an alternation operator on that + level. If no levels are set when hit a '^', then it is in a + leading position. We keep track of which level we're at by + increasing a variable current_level whenever we encounter an + open-group operator and decreasing it whenever we encounter a + close-group operator. + Have to adjust the anchor list contents whenever insert + something ahead of them (such as on_failure_jump's) in the + pattern. + (adjust_anchor_list): Adjusts the offsets in an anchor list by + a given increment starting at a given start position. + (get_level_value): Returns the bit setting of a given level. + (set_level_value): Sets the bit of a given level to a given value. + (set_this_level): Sets (to 1) the bit of a given level. + (set_next_lower_level): Sets (to 1) the bit of (LEVEL - 1) for a + given LEVEL. + (clear_this_and_higher_levels): Clears the bits for a given + level and any higher levels. + (extend_level_list): Adds sizeof(unsigned) more bits to a level list. + (increase_level): Increases by 1 the value of a given level variable. + (decrease_level): Decreases by 1 the value of a given level variable. + (lower_levels_match_nothing): Checks if any levels lower than + the given one match anything. + (no_levels_match_anything): Checks if any levels match anything. + (re_match_2): At case wordbeg: before looking at d-1, check that + we're not at the string's beginning. + At case wordend: Added some illuminating parentheses. + +Mon Mar 25 13:58:51 1991 Kathy Hargreaves (kathy at hayley) + + * regex.h (RE_NO_ANCHOR_AT_NEWLINE): Changed syntax bit name + from RE_ANCHOR_NOT_NEWLINE because an anchor never matches the + newline itself, just the empty string either before or after it. + (RE_REPEATED_ANCHORS_AWAY): Added this syntax bit for ignoring + anchors inside groups which are operated on by repetition + operators. + (RE_DOT_MATCHES_NEWLINE): Added this bit so the match-any-character + operator could match a newline when it's set. + (RE_SYNTAX_POSIX_BASIC): Set RE_DOT_MATCHES_NEWLINE in this. + (RE_SYNTAX_POSIX_EXTENDED): Set RE_DOT_MATCHES_NEWLINE and + RE_REPEATED_ANCHORS_AWAY in this. + (regerror): Changed prototypes to new POSIX spec. + + * regex.c (anchor_list_type): Added so could null out anchors inside + repeated groups. + (ANCHOR_LIST_PTR_FULL): Added for above type. + (compile_stack_element): Changed name from stack_element. + (compile_stack_type): Changed name from compile_stack. + (INIT_COMPILE_STACK_SIZE): Changed name from INIT_STACK_SIZE. + (COMPILE_STACK_EMPTY): Changed name from STACK_EMPTY. + (COMPILE_STACK_FULL): Changed name from STACK_FULL. + (regex_compile): Changed SYNTAX parameter to non-const. + Changed variable name `stack' to `compile_stack'. + If syntax bit RE_REPEATED_ANCHORS_AWAY is set, then naively put + anchors in a list when encounter them and then set them to + `unused' when detect they are within a group operated on by a + repetition operator. Need something more sophisticated than + this, as they should only get set to `unused' if they are in + positions where they would be anchors. Also need a better way to + detect contextually invalid anchors. + Changed some commments. + (is_in_compile_stack): Changed name from `is_in_stack'. + (extend_anchor_list): Added to do anchor stuff. + (record_anchor_position): Added to do anchor stuff. + (remove_intervening_anchors): Added to do anchor stuff. + (re_match_2): Now match a newline with the match-any-character + operator if RE_DOT_MATCHES_NEWLINE is set. + Compacted some code. + (regcomp): Added new POSIX newline information to the header + commment. + If REG_NEWLINE cflag is set, then now unset RE_DOT_MATCHES_NEWLINE + in syntax. + (put_in_buffer): Added to do new POSIX regerror spec. Called + by regerror. + (regerror): Changed to take a pattern buffer, error buffer and + its size, and return type `size_t', the size of the full error + message, and the first ERRBUF_SIZE - 1 characters of the full + error message in the error buffer. + +Wed Feb 27 16:38:33 1991 Kathy Hargreaves (kathy at hayley) + + * regex.h (#include ): Removed this as new POSIX + standard has the user include it. + (RE_SYNTAX_POSIX_BASIC and RE_SYNTAX_POSIX_EXTENDED): Removed + RE_HAT_LISTS_NOT_NEWLINE as new POSIX standard has the cflag + REG_NEWLINE now set this. Similarly, added syntax bit + RE_ANCHOR_NOT_NEWLINE as this is now unset by REG_NEWLINE. + (RE_SYNTAX_POSIX_BASIC): Removed syntax bit + RE_NO_CONSECUTIVE_REPEATS as POSIX now allows them. + + * regex.c (#include ): Added this as new POSIX + standard has the user include it instead of us putting it in + regex.h. + (extern char *re_syntax_table): Made into an extern so the + user could allocate it. + (DO_RANGE): If don't find a range end, now goto invalid_range_end + instead of unmatched_left_bracket. + (regex_compile): Made variable SYNTAX non-const.???? + Reformatted some code. + (re_compile_fastmap): Moved is_a_succeed_n's declaration to + inner braces. + Compacted some code. + (SET_NEWLINE_FLAG): Removed and put inline. + (regcomp): Made variable `syntax' non-const so can unset + RE_ANCHOR_NOT_NEWLINE syntax bit if cflag RE_NEWLINE is set. + If cflag RE_NEWLINE is set, set the RE_HAT_LISTS_NOT_NEWLINE + syntax bit and unset RE_ANCHOR_NOT_NEWLINE one of `syntax'. + +Wed Feb 20 16:33:38 1991 Kathy Hargreaves (kathy at hayley) + + * regex.h (RE_NO_CONSECUTIVE_REPEATS): Changed name from + RE_NO_CONSEC_REPEATS. + (REG_ENESTING): Deleted this POSIX return value, as the stack + is now unbounded. + (struct re_pattern_buffer): Changed some comments. + (re_compile_pattern): Changed a comment. + Deleted check on stack upper bound and corresponding error. + Now when there's no interval contents and it's the end of the + pattern, go to unmatched_left_curly_brace instead of end_of_pattern. + Removed nesting_too_deep error, as the stack is now unbounded. + (regcomp): Removed REG_ENESTING case, as the stack is now unbounded. + (regerror): Removed REG_ENESTING case, as the stack is now unbounded. + + * regex.c (MAX_STACK_SIZE): Deleted because don't need upper + bound on array indexed with an unsigned number. + +Sun Feb 17 15:50:24 1991 Kathy Hargreaves (kathy at hayley) + + * regex.h: Changed and added some comments. + + * regex.c (init_syntax_once): Made `_' a word character. + (re_compile_pattern): Added a comment. + (re_match_2): Redid header comment. + (regexec): With header comment about PMATCH, corrected and + removed details found regex.h, adding a reference. + +Fri Feb 15 09:21:31 1991 Kathy Hargreaves (kathy at hayley) + + * regex.c (DO_RANGE): Removed argument parentheses. + Now get untranslated range start and end characters and set + list bits for the translated (if at all) versions of them and + all characters between them. + (re_match_2): Now use regs->num_regs instead of num_regs_wanted + wherever possible. + (regcomp): Now build case-fold translate table using isupper + and tolower facilities so will work on foreign language characters. + +Sat Feb 9 16:40:03 1991 Kathy Hargreaves (kathy at hayley) + + * regex.h (RE_HAT_LISTS_NOT_NEWLINE): Changed syntax bit name + from RE_LISTS_NOT_NEWLINE as it only affects nonmatching lists. + Changed all references to the match-beginning-of-string + operator to match-beginning-of-line operator, as this is what + it does. + (RE_NO_CONSEC_REPEATS): Added this syntax bit. + (RE_SYNTAX_POSIX_BASIC): Added above bit to this. + (REG_PREMATURE_END): Changed name to REG_EEND. + (REG_EXCESS_NESTING): Changed name to REG_ENESTING. + (REG_TOO_BIG): Changed name to REG_ESIZE. + (REG_INVALID_PREV_RE): Deleted this return POSIX value. + Added and changed some comments. + + * regex.c (re_compile_pattern): Now sets the pattern buffer's + `return_default_num_regs' field. + (typedef struct stack_element, stack_type, INIT_STACK_SIZE, + MAX_STACK_SIZE, STACK_EMPTY, STACK_FULL): Added for regex_compile. + (INIT_BUF_SIZE): Changed value from 28 to 32. + (BUF_PUSH): Changed name from BUFPUSH. + (MAX_BUF_SIZE): Added so could use in many places. + (IS_CHAR_CLASS_STRING): Replaced is_char_class with this. + (regex_compile): Added a stack which could grow dynamically + and which has struct elements. + Go back to initializing `zero_times_ok' and `many_time_ok' to + 0 and |=ing them inside the loop. + Now disallow consecutive repetition operators if the syntax + bit RE_NO_CONSEC_REPEATS is set. + Now detect trailing backslash when the compiler is expecting a + `?' or a `+'. + Changed calls to GET_BUFFER_SPACE which asked for 6 to ask for + 3, as that's all they needed. + Now check for trailing backslash inside lists. + Now disallow an empty alternative right before an end-of-line + operator. + Now get buffer space before leaving space for a fixup jump. + Now check if at pattern end when at open-interval operator. + Added some comments. + Now check if non-interval repetition operators follow an + interval one if the syntax bit RE_NO_CONSEC_REPEATS is set. + Now only check if what precedes an interval repetition + operator isn't a regular expression which matches one + character if the syntax bit RE_NO_CONSEC_REPEATS is set. + Now return "Unmatched [ or [^" instead of "Unmatched [". + (is_in_stack): Added to check if a given register number is in + the stack. + (re_match_2): If initial variable allocations fail, return -2, + instead of -1. + Now set reg's `num_regs' field when allocating regs. + Now before allocating them, free regs->start and end if they + aren't NULL and return -2 if either allocation fails. + Now use regs->num_regs instead of num_regs_wanted to control + regs loops. + Now increment past the newline when matching it with an + end-of-line operator. + (recomp): Added to the header comment. + Now return REG_ESUBREG if regex_compile returns "Unmatched [ + or [^" instead of doing so if it returns "Unmatched [". + Now return REG_BADRPT if in addition to returning "Missing + preceding regular expression", regex_compile returns "Invalid + preceding regular expression". + Now return new return value names (see regex.h changes). + (regexec): Added to header comment. + Initialize regs structure. + Now match whole string. + Now always free regs.start and regs.end instead of just when + the string matched. + (regerror): Now return "Regex error: Unmatched [ or [^.\n" + instead of "Regex error: Unmatched [.\n". + Now return "Regex error: Preceding regular expression either + missing or not simple.\n" instead of "Regex error: Missing + preceding regular expression.\n". + Removed REG_INVALID_PREV_RE case (it got subsumed into the + REG_BADRPT case). + +Thu Jan 17 09:52:35 1991 Kathy Hargreaves (kathy at hayley) + + * regex.h: Changed a comment. + + * regex.c: Changed and added large header comments. + (re_compile_pattern): Now if detect that `laststart' for an + interval points to a byte code for a regular expression which + matches more than one character, make it an internal error. + (regerror): Return error message, don't print it. + +Tue Jan 15 15:32:49 1991 Kathy Hargreaves (kathy at hayley) + + * regex.h (regcomp return codes): Added GNU ones. + Updated some comments. + + * regex.c (DO_RANGE): Changed `obscure_syntax' to `syntax'. + (regex_compile): Added `following_left_brace' to keep track of + where pseudo interval following a valid interval starts. + Changed some instances that returned "Invalid regular + expression" to instead return error strings coinciding with + POSIX error codes. + Changed some comments. + Now consider only things between `[:' and `:]' to be possible + character class names. + Now a character class expression can't end a pattern; at + least a `]' must close the list. + Now if the syntax bit RE_NO_BK_CURLY_BRACES is set, then a + valid interval must be followed by yet another to get an error + for preceding an interval (in this case, the second one) with + a regular expression that matches more than one character. + Now if what follows a valid interval begins with a open + interval operator but doesn't begin a valid interval, then set + following_left_bracket to it, put it in C and go to + normal_char label. + Added some comments. + Return "Invalid character class name" instead of "Invalid + character class". + (regerror): Return messages for all POSIX error codes except + REG_ECOLLATE and REG_NEWLINE, along with all GNU error codes. + Added `break's after all cases. + (main): Call re_set_syntax instead of setting `obscure_syntax' + directly. + +Sat Jan 12 13:37:59 1991 Kathy Hargreaves (kathy at hayley) + + * regex.h (Copyright): Updated date. + (#include ): Include unconditionally. + (RE_CANNOT_MATCH_NEWLINE): Deleted this syntax bit. + (RE_SYNTAX_POSIX_BASIC, RE_SYNTAX_POSIX_EXTENDED): Removed + setting the RE_ANCHOR_NOT_NEWLINE syntax bit from these. + Changed and added some comments. + (struct re_pattern_buffer): Changed some flags from chars to bits. + Added field `syntax'; holds which syntax pattern was compiled with. + Added bit flag `return_default_num_regs'. + (externs for GNU and Berkeley UNIX routines): Added `const's to + parameter types to be compatible with POSIX. + (#define const): Added to support old C compilers. + + * regex.c (Copyright): Updated date. + (enum regexpcode): Deleted `newline'. + (regex_compile): Renamed re_compile_pattern to this, added a + syntax parameter so it can set the pattern buffer's `syntax' + field. + Made `pattern', and `size' `const's so could pass to POSIX + interface routines; also made `const' whatever interval + variables had to be to make this work. + Changed references to `obscure_syntax' to new parameter `syntax'. + Deleted putting `newline' in buffer when see `\n'. + Consider invalid character classes which have nothing wrong + except the character class name; if so, return character-class error. + (is_char_class): Added routine for regex_compile. + (re_compile_pattern): added a new one which calls + regex_compile with `obscure_syntax' as the actual parameter + for the formal `syntax'. + Gave this the old routine's header comments. + Made `pattern', and `size' `const's so could use POSIX interface + routine parameters. + (re_search, re_search_2, re_match, re_match_2): Changed + `pbufp' to `bufp'. + (re_search_2, re_match_2): Changed `mstop' to `stop'. + (re_search, re_search_2): Made all parameters except `regs' + `const's so could use POSIX interface routines parameters. + (re_search_2): Added private copies of `const' parameters so + could change their values. + (re_match_2): Made all parameters except `regs' `const's so + could use POSIX interface routines parameters. + Changed `size1' and `size2' parameters to `size1_arg' and + `size2_arg' and so could change; added local `size1' and + `size2' and set to these. + Added some comments. + Deleted `newline' case. + `begline' can also possibly match if `d' contains a newline; + if it does, we have to increment d to point past the newline. + Replaced references to `obscure_syntax' with `bufp->syntax'. + (re_comp, re_exec): Made parameter `s' a `const' so could use POSIX + interface routines parameters. + Now call regex_compile, passing `obscure_syntax' via the + `syntax' parameter. + (re_exec): Made local `len' a `const' so could pass to re_search. + (regcomp): Added header comment. + Added local `syntax' to set and pass to regex_compile rather + than setting global `obscure_syntax' and passing it. + Call regex_compile with its `syntax' parameter rather than + re_compile_pattern. + Return REG_ECTYPE if character-class error. + (regexec): Don't initialize `regs' to anything. + Made `private_preg' a nonpointer so could set to what the + constant `preg' points. + Initialize `private_preg's `return_default_num_regs' field to + zero because want to return `nmatch' registers, not however + many there are subexpressions in the pattern. + Also test if `nmatch' > 0 to see if should pass re_match `regs'. + +Tue Jan 8 15:57:17 1991 Kathy Hargreaves (kathy at hayley) + + * regex.h (struct re_pattern_buffer): Reworded comment. + + * regex.c (EXTEND_BUFFER): Also reset beg_interval. + (re_search_2): Return val if val = -2. + (NUM_REG_ITEMS): Listed items in comment. + (NUM_OTHER_ITEMS): Defined this for using in > 1 definition. + (MAX_NUM_FAILURE_ITEMS): Replaced `+ 2' with NUM_OTHER_ITEMS. + (NUM_FAILURE_ITEMS): As with definition above and added to + comment. + (PUSH_FAILURE_POINT): Replaced `* 2's with `<< 1's. + (re_match_2): Test with equality with 1 to see pbufp->bol and + pbufp->eol are set. + +Fri Jan 4 15:07:22 1991 Kathy Hargreaves (kathy at hayley) + + * regex.h (struct re_pattern_buffer): Reordered some fields. + Updated some comments. + Added not_bol and not_eol fields. + (extern regcomp, regexec, regerror): Added return types. + (extern regfree): Added `extern'. + + * regex.c (min): Deleted unused macro. + (re_match_2): Compacted some code. + Removed call to macro `min' from `for' loop. + Fixed so unused registers get filled with -1's. + Fail if the pattern buffer's `not_bol' field is set and + encounter a `begline'. + Fail if the pattern buffer's `not_eol' field is set and + encounter a `endline'. + Deleted redundant check for empty stack in fail case. + Don't free pattern buffer's components in re_comp. + (regexec): Initialize variable regs. + Added `private_preg' pattern buffer so could set `not_bol' and + `not_eol' fields and hand to re_match. + Deleted naive attempt to detect anchors. + Set private pattern buffer's `not_bol' and `not_eol' fields + according to eflags value. + `nmatch' must also be > 0 for us to bother allocating + registers to send to re_match and filling pmatch + with their results after the call to re_match. + Send private pattern buffer instead of argument to re_match. + If use the registers, always free them and then set them to NULL. + (regerror): Added this Posix routine. + (regfree): Added this Posix routine. + +Tue Jan 1 15:02:45 1991 Kathy Hargreaves (kathy at hayley) + + * regex.h (RE_NREGS): Deleted this definition, as now the user + can choose how many registers to have. + (REG_NOTBOL, REG_NOTEOL): Defined these Posix eflag bits. + (REG_NOMATCH, REG_BADPAT, REG_ECOLLATE, REG_ECTYPE, + REG_EESCAPE, REG_ESUBREG, REG_EBRACK, REG_EPAREN, REG_EBRACE, + REG_BADBR, REG_ERANGE, REG_ESPACE, REG_BADRPT, REG_ENEWLINE): + Defined these return values for Posix's regcomp and regexec. + Updated some comments. + (struct re_pattern_buffer): Now typedef this as regex_t + instead of the other way around. + (struct re_registers): Added num_regs field. Made start and + end fields pointers to char instead of fixed size arrays. + (regmatch_t): Added this Posix register type. + (regcomp, regexec, regerror, regfree): Added externs for these + Posix routines. + + * regex.c (enum boolean): Typedefed this. + (re_pattern_buffer): Reformatted some comments. + (re_compile_pattern): Updated some comments. + Always push start_memory and its attendant number whenever + encounter a group, not just when its number is less than the + previous maximum number of registers; same for stop_memory. + Get 4 bytes of buffer space instead of 2 when pushing a + set_number_at. + (can_match_nothing): Added this to elaborate on and replace + code in re_match_2. + (reg_info_type): Made can_match_nothing field a bit instead of int. + (MIN): Added for re_match_2. + (re_match_2 macros): Changed all `for' loops which used + RE_NREGS to now use num_internal_regs as upper bounds. + (MAX_NUM_FAILURE_ITEMS): Use num_internal_regs instead of RE_NREGS. + (POP_FAILURE_POINT): Added check for empty stack. + (FREE_VARIABLES): Added this to free (and set to NULL) + variables allocated in re_match_2. + (re_match_2): Rearranged parameters to be in order. + Added variables num_regs_wanted (how many registers the user wants) + and num_internal_regs (how many groups there are). + Allocated initial_stack, regstart, regend, old_regstart, + old_regend, reginfo, best_regstart, and best_regend---all + which used to be fixed size arrays. Free them all and return + -1 if any fail. + Free above variables if starting position pos isn't valid. + Changed all `for' loops which used RE_NREGS to now use + num_internal_regs as upper bounds---except for the loops which + fill regs; then use num_regs_wanted. + Allocate regs if the user has passed it and wants more than 0 + registers filled. + Set regs->start[i] and regs->end[i] to -1 if either + regstart[i] or regend[i] equals -1, not just the first. + Free allocated variables before returning. + Updated some comments. + (regcomp): Return REG_ESPACE, REG_BADPAT, REG_EPAREN when + appropriate. + Free translate array. + (regexec): Added this Posix interface routine. + +Mon Dec 24 14:21:13 1990 Kathy Hargreaves (kathy at hayley) + + * regex.h: If _POSIX_SOURCE is defined then #include . + Added syntax bit RE_CANNOT_MATCH_NEWLINE. + Defined Posix cflags: REG_EXTENDED, REG_NEWLINE, REG_ICASE, and + REG_NOSUB. + Added fields re_nsub and no_sub to struct re_pattern_buffer. + Typedefed regex_t to be `struct re_pattern_buffer'. + + * regex.c (CHAR_SET_SIZE): Defined this to be 256 and replaced + incidences of this value with this constant. + (re_compile_pattern): Added switch case for `\n' and put + `newline' into the pattern buffer when encounter this. + Increment the pattern_buffer's `re_nsub' field whenever open a + group. + (re_match_2): Match a newline with `newline'---provided the + syntax bit RE_CANNOT_MATCH_NEWLINE isn't set. + (regcomp): Added this Posix interface routine. + (enum test_type): Added interface_test tag. + (main): Added Posix interface test. + +Tue Dec 18 12:58:12 1990 Kathy Hargreaves (kathy at hayley) + + * regex.h (struct re_pattern_buffer): reformatted so would fit + in texinfo documentation. + +Thu Nov 29 15:49:16 1990 Kathy Hargreaves (kathy at hayley) + + * regex.h (RE_NO_EMPTY_ALTS): Added this bit. + (RE_SYNTAX_POSIX_EXTENDED): Added above bit. + + * regex.c (re_compile_pattern): Disallow empty alternatives only + when RE_NO_EMPTY_ALTS is set, not when RE_CONTEXTUAL_INVALID_OPS is. + Changed RE_NO_BK_CURLY_BRACES to RE_NO_BK_PARENS when testing + for empty groups at label handle_open. + At label handle_bar: disallow empty alternatives if RE_NO_EMPTY_ALTS + is set. + Rewrote some comments. + + (re_compile_fastmap): cleaned up code. + + (re_search_2): Rewrote comment. + + (struct register_info): Added field `inner_groups'; it records + which groups are inside of the current one. + Added field can_match_nothing; it's set if the current group + can match nothing. + Added field ever_match_something; it's set if current group + ever matched something. + + (INNER_GROUPS): Added macro to access inner_groups field of + struct register_info. + + (CAN_MATCH_NOTHING): Added macro to access can_match_nothing + field of struct register_info. + + (EVER_MATCHED_SOMETHING): Added macro to access + ever_matched_something field of struct register_info. + + (NOTE_INNER_GROUP): Defined macro to record that a given group + is inside of all currently active groups. + + (re_match_2): Added variables *p1 and mcnt2 (multipurpose). + Added old_regstart and old_regend arrays to hold previous + register values if they need be restored. + Initialize added fields and variables. + case start_memory: Find out if the group can match nothing. + Save previous register values in old_restart and old_regend. + Record that current group is inside of all currently active + groups. + If the group is inside a loop and it ever matched anything, + restore its registers to values before the last failed match. + Restore the registers for the inner groups, too. + case duplicate: Can back reference to a group that never + matched if it can match nothing. + +Thu Nov 29 11:12:54 1990 Karl Berry (karl at hayley) + + * regex.c (bcopy, ...): define these if either _POSIX_SOURCE or + STDC_HEADERS is defined; same for including . + +Sat Oct 6 16:04:55 1990 Kathy Hargreaves (kathy at hayley) + + * regex.h (struct re_pattern_buffer): Changed field comments. + + * regex.c (re_compile_pattern): Allow a `$' to precede an + alternation operator (`|' or `\|'). + Disallow `^' and/or `$' in empty groups if the syntax bit + RE_NO_EMPTY_GROUPS is set. + Wait until have parsed a valid `\{...\}' interval expression + before testing RE_CONTEXTUAL_INVALID_OPS to see if it's + invalidated by that. + Don't use RE_NO_BK_CURLY_BRACES to test whether or not a validly + parsed interval expression is invalid if it has no preceding re; + rather, use RE_CONTEXTUAL_INVALID_OPS. + If an interval parses, but there is no preceding regular + expression, yet the syntax bit RE_CONTEXTUAL_INDEP_OPS is set, + then that interval can match the empty regular expression; if + the bit isn't set, then the characters in the interval + expression are parsed as themselves (sans the backslashes). + In unfetch_interval case: Moved PATFETCH to above the test for + RE_NO_BK_CURLY_BRACES being set, which would force a goto + normal_backslash; the code at both normal_backsl and normal_char + expect a character in `c.' + +Sun Sep 30 11:13:48 1990 Kathy Hargreaves (kathy at hayley) + + * regex.h: Changed some comments to use the terms used in the + documentation. + (RE_CONTEXTUAL_INDEP_OPS): Changed name from `RE_CONTEXT_INDEP_OPS'. + (RE_LISTS_NOT_NEWLINE): Changed name from `RE_HAT_NOT_NEWLINE.' + (RE_ANCHOR_NOT_NEWLINE): Added this syntax bit. + (RE_NO_EMPTY_GROUPS): Added this syntax bit. + (RE_NO_HYPHEN_RANGE_END): Deleted this syntax bit. + (RE_SYNTAX_...): Reformatted. + (RE_SYNTAX_POSIX_BASIC, RE_SYNTAX_EXTENDED): Added syntax bits + RE_ANCHOR_NOT_NEWLINE and RE_NO_EMPTY_GROUPS, and deleted + RE_NO_HYPHEN_RANGE_END. + (RE_SYNTAX_POSIX_EXTENDED): Added syntax bit RE_DOT_NOT_NULL. + + * regex.c (bcopy, bcmp, bzero): Define if _POSIX_SOURCE is defined. + (_POSIX_SOURCE): ifdef this, #include + (#ifdef emacs): Changed comment of the #endif for the its #else + clause to be `not emacs', not `emacs.' + (no_pop_jump): Changed name from `jump'. + (pop_failure_jump): Changed name from `finalize_jump.' + (maybe_pop_failure_jump): Changed name from `maybe_finalize_jump'. + (no_pop_jump_n): Changed name from `jump_n.' + (EXTEND_BUFFER): Use shift instead of multiplication to double + buf->allocated. + (DO_RANGE, recompile_pattern): Added macro to set the list bits + for a range. + (re_compile_pattern): Fixed grammar problems in some comments. + Checked that RE_NO_BK_VBAR is set to make `$' valid before a `|' + and not set to make it valid before a `\|'. + Checked that RE_NO_BK_PARENS is set to make `$' valid before a ')' + and not set to make it valid before a `\)'. + Disallow ranges starting with `-', unless the range is the + first item in a list, rather than disallowing ranges which end + with `-'. + Disallow empty groups if the syntax bit RE_NO_EMPTY_GROUPS is set. + Disallow nothing preceding `{' and `\{' if they represent the + open-interval operator and RE_CONTEXTUAL_INVALID_OPS is set. + (register_info_type): typedef-ed this using `struct register_info.' + (SET_REGS_MATCHED): Compacted the code. + (re_match_2): Made it fail if back reference a group which we've + never matched. + Made `^' not match a newline if the syntax bit + RE_ANCHOR_NOT_NEWLINE is set. + (really_fail): Added this label so could force a final fail that + would not try to use the failure stack to recover. + +Sat Aug 25 14:23:01 1990 Kathy Hargreaves (kathy at hayley) + + * regex.h (RE_CONTEXTUAL_OPS): Changed name from RE_CONTEXT_OPS. + (global): Rewrote comments and rebroke some syntax #define lines. + + * regex.c (isgraph): Added definition for sequents. + (global): Now refer to character set lists as ``lists.'' + Rewrote comments containing ``\('' or ``\)'' to now refer to + ``groups.'' + (RE_CONTEXTUAL_OPS): Changed name from RE_CONTEXT_OPS. + + (re_compile_pattern): Expanded header comment. + +Sun Jul 15 14:50:25 1990 Kathy Hargreaves (kathy at hayley) + + * regex.h (RE_CONTEX_INDEP_OPS): the comment's sense got turned + around when we changed how it read; changed it to be correct. + +Sat Jul 14 16:38:06 1990 Kathy Hargreaves (kathy at hayley) + + * regex.h (RE_NO_EMPTY_BK_REF): changed name to + RE_NO_MISSING_BK_REF, as this describes it better. + + * regex.c (re_compile_pattern): changed RE_NO_EMPTY_BK_REF + to RE_NO_MISSING_BK_REF, as above. + +Thu Jul 12 11:45:05 1990 Kathy Hargreaves (kathy at hayley) + + * regex.h (RE_NO_EMPTY_BRACKETS): removed this syntax bit, as + bracket expressions should *never* be empty regardless of the + syntax. Removes this bit from RE_SYNTAX_POSIX_BASIC and + RE_SYNTAX_POSIX_EXTENDED. + + * regex.c (SET_LIST_BIT): in the comment, now refer to character + sets as (non)matching sets, as bracket expressions can now match + other things in addition to characters. + (re_compile_pattern): refer to groups as such instead of `\(...\)' + or somesuch, because groups can now be enclosed in either plain + parens or backslashed ones, depending on the syntax. + In the '[' case, added a boolean just_had_a_char_class to detect + whether or not a character class begins a range (which is invalid). + Restore way of breaking out of a bracket expression to original way. + Add way to detect a range if the last thing in a bracket + expression was a character class. + Took out check for c != ']' at the end of a character class in + the else clause, as it had already been checked in the if part + that also checked the validity of the string. + Set or clear just_had_a_char_class as appropriate. + Added some comments. Changed references to character sets to + ``(non)matching lists.'' + +Sun Jul 1 12:11:29 1990 Karl Berry (karl at hayley) + + * regex.h (BYTEWIDTH): moved back to regex.c. + + * regex.h (re_compile_fastmap): removed declaration; this + shouldn't be advertised. + +Mon May 28 15:27:53 1990 Kathy Hargreaves (kathy at hayley) + + * regex.c (ifndef Sword): Made comments more specific. + (global): include so can write fatal messages on + standard error. Replaced calls to assert with fprintfs to + stderr and exit (1)'s. + (PREFETCH): Reformatted to make more readable. + (AT_STRINGS_BEG): Defined to test if we're at the beginning of + the virtual concatenation of string1 and string2. + (AT_STRINGS_END): Defined to test if at the end of the virtual + concatenation of string1 and string2. + (AT_WORD_BOUNDARY): Defined to test if are at a word boundary. + (IS_A_LETTER(d)): Defined to test if the contents of the pointer D + is a letter. + (re_match_2): Rewrote the wordbound, notwordbound, wordbeg, wordend, + begbuf, and endbuf cases in terms of the above four new macros. + Called SET_REGS_MATCHED in the matchsyntax, matchnotsyntax, + wordchar, and notwordchar cases. + +Mon May 14 14:49:13 1990 Kathy Hargreaves (kathy at hayley) + + * regex.c (re_search_2): Fixed RANGE to not ever take STARTPOS + outside of virtual concatenation of STRING1 and STRING2. + Updated header comment as to this. + (re_match_2): Clarified comment about MSTOP in header. + +Sat May 12 15:39:00 1990 Kathy Hargreaves (kathy at hayley) + + * regex.c (re_search_2): Checked for out-of-range STARTPOS. + Added comments. + When searching backwards, not only get the character with which + to compare to the fastmap from string2 if the starting position + >= size1, but also if size1 is zero; this is so won't get a + segmentation fault if string1 is null. + Reformatted code at label advance. + +Thu Apr 12 20:26:21 1990 Kathy Hargreaves (kathy at hayley) + + * regex.h: Added #pragma once and #ifdef...endif __REGEXP_LIBRARY. + (RE_EXACTN_VALUE): Added for search.c to use. + Reworded some comments. + + regex.c: Punctuated some comments correctly. + (NULL): Removed this. + (RE_EXACTN_VALUE): Added for search.c to use. + (): Moved this include to top of file. + (): Added this include. + (struct regexpcode): Assigned 0 to unused and 1 to exactn + because of RE_EXACTN_VALUE. + Added comment. + (various macros): Lined up backslashes near end of line. + (insert_jump): Cleaned up the header comment. + (re_search): Corrected the header comment. + (re_search_2): Cleaned up and completed the header comment. + (re_max_failures): Updated comment. + (struct register_info): Constructed as bits so as to save space + on the stack when pushing register information. + (IS_ACTIVE): Macro for struct register_info. + (MATCHED_SOMETHING): Macro for struct register_info. + (NUM_REG_ITEMS): How many register information items for each + register we have to push on the stack at each failure. + (MAX_NUM_FAILURE_ITEMS): If push all the registers on failure, + this is how many items we push on the stack. + (PUSH_FAILURE_POINT): Now pushes whether or not the register is + currently active, and whether or not it matched something. + Checks that there's enough space allocated to accomodate all the + items we currently want to push. (Before, a test for an empty + stack sufficed because we always pushed and popped the same + number of items). + Replaced ``2'' with MAX_NUM_FAILURE_POINTS when ``2'' refers + to how many things get pushed on the stack each time. + When copy the stack into the newly allocated storage, now only copy + the area in use. + Clarified comment. + (POP_FAILURE_POINT): Defined to use in places where put number + of registers on the stack into a variable before using it to + decrement the stack, so as to not confuse the compiler. + (IS_IN_FIRST_STRING): Defined to check if a pointer points into + the first string. + (SET_REGS_MATCHED): Changed to use the struct register_info + bits; also set the matched-something bit to false if the + register isn't currently active. (This is a redundant setting.) + (re_match_2): Cleaned up and completed the header comment. + Updated the failure stack comment. + Replaced the ``2'' with MAX_NUM_FAILURE_ITEMS in the static + allocation of initial_stack, because now more than two (now up + to MAX_FAILURE_ITEMS) items get pushed on the failure stack each + time. + Ditto for stackb. + Trashed restart_seg1, regend_seg1, best_regstart_seg1, and + best_regend_seg1 because they could have erroneous information + in them, such as when matching ``a'' (in string1) and ``ab'' (in + string2) with ``(a)*ab''; before using IS_IN_FIRST_STRING to see + whether or not the register starts or ends in string1, + regstart[1] pointed past the end of string1, yet regstart_seg1 + was 0! + Added variable reg_info of type struct register_info to keep + track of currently active registers and whether or not they + currently match anything. + Commented best_regs_set. + Trashed reg_active and reg_matched_something and put the + information they held into reg_info; saves space on the stack. + Replaced NULL with '\000'. + In begline case, compacted the code. + Used assert to exit if had an internal error. + In begbuf case, because now force the string we're working on + into string2 if there aren't two strings, now allow d == string2 + if there is no string1 (and the check for that is size1 == 0!); + also now succeeds if there aren't any strings at all. + (main, ifdef canned): Put test type into a variable so could + change it while debugging. + +Sat Mar 24 12:24:13 1990 Kathy Hargreaves (kathy at hayley) + + * regex.c (GET_UNSIGNED_NUMBER): Deleted references to num_fetches. + (re_compile_pattern): Deleted num_fetches because could keep + track of the number of fetches done by saving a pointer into the + pattern. + Added variable beg_interval to be used as a pointer, as above. + Assert that beg_interval points to something when it's used as above. + Initialize succeed_n's to lower_bound because re_compile_fastmap + needs to know it. + (re_compile_fastmap): Deleted unnecessary variable is_a_jump_n. + Added comment. + (re_match_2): Put number of registers on the stack into a + variable before using it to decrement the stack, so as to not + confuse the compiler. + Updated comments. + Used error routine instead of printf and exit. + In exactn case, restored longer code from ``original'' regex.c + which doesn't test translate inside a loop. + + * regex.h: Moved #define NULL and the enum regexpcode definition + and to regex.c. Changed some comments. + + regex.c (global): Updated comments about compiling and for the + re_compile_pattern jump routines. + Added #define NULL and the enum regexpcode definition (from + regex.h). + (enum regexpcode): Added set_number_at to reset the n's of + succeed_n's and jump_n's. + (re_set_syntax): Updated its comment. + (re_compile_pattern): Moved its heading comment to after its macros. + Moved its include statement to the top of the file. + Commented or added to comments of its macros. + In start_memory case: Push laststart value before adding + start_memory and its register number to the buffer, as they + might not get added. + Added code to put a set_number_at before each succeed_n and one + after each jump_n; rewrote code in what seemed a more + straightforward manner to put all these things in the pattern so + the succeed_n's would correctly jump to the set_number_at's of + the matching jump_n's, and so the jump_n's would correctly jump + to after the set_number_at's of the matching succeed_n's. + Initialize succeed_n n's to -1. + (insert_op_2): Added this to insert an operation followed by + two integers. + (re_compile_fastmap): Added set_number_at case. + (re_match_2): Moved heading comment to after macros. + Added mention of REGS to heading comment. + No longer turn a succeed_n with n = 0 into an on_failure_jump, + because n needs to be reset each time through a loop. + Check to see if a succeed_n's n is set by its set_number_at. + Added set_number_at case. + Updated some comments. + (main): Added another main to run posix tests, which is compiled + ifdef both test and canned. (Old main is still compiled ifdef + test only). + +Tue Mar 19 09:22:55 1990 Kathy Hargreaves (kathy at hayley) + + * regex.[hc]: Change all instances of the word ``legal'' to + ``valid'' and all instances of ``illegal'' to ``invalid.'' + +Sun Mar 4 12:11:31 1990 Kathy Hargreaves (kathy at hayley) + + * regex.h: Added syntax bit RE_NO_EMPTY_RANGES which is set if + an ending range point has to collate higher or equal to the + starting range point. + Added syntax bit RE_NO_HYPHEN_RANGE_END which is set if a hyphen + can't be an ending range point. + Set to two above bits in RE_SYNTAX_POSIX_BASIC and + RE_SYNTAX_POSIX_EXTENDED. + + regex.c: (re_compile_pattern): Don't allow empty ranges if the + RE_NO_EMPTY_RANGES syntax bit is set. + Don't let a hyphen be a range end if the RE_NO_HYPHEN_RANGE_END + syntax bit is set. + (ESTACK_PUSH_2): renamed this PUSH_FAILURE_POINT and made it + push all the used registers on the stack, as well as the number + of the highest numbered register used, and (as before) the two + failure points. + (re_match_2): Fixed up comments. + Added arrays best_regstart[], best_regstart_seg1[], best_regend[], + and best_regend_seg1[] to keep track of the best match so far + whenever reach the end of the pattern but not the end of the + string, and there are still failure points on the stack with + which to backtrack; if so, do the saving and force a fail. + If reach the end of the pattern but not the end of the string, + but there are no more failure points to try, restore the best + match so far, set the registers and return. + Compacted some code. + In stop_memory case, if the subexpression we've just left is in + a loop, push onto the stack the loop's on_failure_jump failure + point along with the current pointer into the string (d). + In finalize_jump case, in addition to popping the failure + points, pop the saved registers. + In the fail case, restore the registers, as well as the failure + points. + +Sun Feb 18 15:08:10 1990 Kathy Hargreaves (kathy at hayley) + + * regex.c: (global): Defined a macro GET_BUFFER_SPACE which + makes sure you have a specified number of buffer bytes + allocated. + Redefined the macro BUFPUSH to use this. + Added comments. + + (re_compile_pattern): Call GET_BUFFER_SPACE before storing or + inserting any jumps. + + (re_match_2): Set d to string1 + pos and dend to end_match_1 + only if string1 isn't null. + Force exit from a loop if it's around empty parentheses. + In stop_memory case, if found some jumps, increment p2 before + extracting address to which to jump. Also, don't need to know + how many more times can jump_n. + In begline case, d must equal string1 or string2, in that order, + only if they are not null. + In maybe_finalize_jump case, skip over start_memorys' and + stop_memorys' register numbers, too. + +Thu Feb 15 15:53:55 1990 Kathy Hargreaves (kathy at hayley) + + * regex.c (BUFPUSH): off by one goof in deciding whether to + EXTEND_BUFFER. + +Wed Jan 24 17:07:46 1990 Kathy Hargreaves (kathy at hayley) + + * regex.h: Moved definition of NULL to here. + Got rid of ``In other words...'' comment. + Added to some comments. + + regex.c: (re_compile_pattern): Tried to bulletproof some code, + i.e., checked if backward references (e.g., p[-1]) were within + the range of pattern. + + (re_compile_fastmap): Fixed a bug in succeed_n part where was + getting the amount to jump instead of how many times to jump. + + (re_search_2): Changed the name of the variable ``total'' to + ``total_size.'' + Condensed some code. + + (re_match_2): Moved the comment about duplicate from above the + start_memory case to above duplicate case. + + (global): Rewrote some comments. + Added commandline arguments to testing. + +Wed Jan 17 11:47:27 1990 Kathy Hargreaves (kathy at hayley) + + * regex.c: (global): Defined a macro STORE_NUMBER which stores a + number into two contiguous bytes. Also defined STORE_NUMBER_AND_INCR + which does the same thing and then increments the pointer to the + storage place to point after the number. + Defined a macro EXTRACT_NUMBER which extracts a number from two + continguous bytes. Also defined EXTRACT_NUMBER_AND_INCR which + does the same thing and then increments the pointer to the + source to point to after where the number was. + +Tue Jan 16 12:09:19 1990 Kathy Hargreaves (kathy at hayley) + + * regex.h: Incorporated rms' changes. + Defined RE_NO_BK_REFS syntax bit which is set when want to + interpret back reference patterns as literals. + Defined RE_NO_EMPTY_BRACKETS syntax bit which is set when want + empty bracket expressions to be illegal. + Defined RE_CONTEXTUAL_ILLEGAL_OPS syntax bit which is set when want + it to be illegal for *, +, ? and { to be first in an re or come + immediately after a | or a (, and for ^ not to appear in a + nonleading position and $ in a nontrailing position (outside of + bracket expressions, that is). + Defined RE_LIMITED_OPS syntax bit which is set when want +, ? + and | to always be literals instead of ops. + Fixed up the Posix syntax. + Changed the syntax bit comments from saying, e.g., ``0 means...'' + to ``If this bit is set, it means...''. + Changed the syntax bit defines to use shifts instead of integers. + + * regex.c: (global): Incorporated rms' changes. + + (re_compile_pattern): Incorporated rms' changes + Made it illegal for a $ to appear anywhere but inside a bracket + expression or at the end of an re when RE_CONTEXTUAL_ILLEGAL_OPS + is set. Made the same hold for $ except it has to be at the + beginning of an re instead of the end. + Made the re "[]" illegal if RE_NO_EMPTY_BRACKETS is set. + Made it illegal for | to be first or last in an re, or immediately + follow another | or a (. + Added and embellished some comments. + Allowed \{ to be interpreted as a literal if RE_NO_BK_CURLY_BRACES + is set. + Made it illegal for *, +, ?, and { to appear first in an re, or + immediately follow a | or a ( when RE_CONTEXTUAL_ILLEGAL_OPS is set. + Made back references interpreted as literals if RE_NO_BK_REFS is set. + Made recursive intervals either illegal (if RE_NO_BK_CURLY_BRACES + isn't set) or interpreted as literals (if is set), if RE_INTERVALS + is set. + Made it treat +, ? and | as literals if RE_LIMITED_OPS is set. + Cleaned up some code. + +Thu Dec 21 15:31:32 1989 Kathy Hargreaves (kathy at hayley) + + * regex.c: (global): Moved RE_DUP_MAX to regex.h and made it + equal 2^15 - 1 instead of 1000. + Defined NULL to be zero. + Moved the definition of BYTEWIDTH to regex.h. + Made the global variable obscure_syntax nonstatic so the tests in + another file could use it. + + (re_compile_pattern): Defined a maximum length (CHAR_CLASS_MAX_LENGTH) + for character class strings (i.e., what's between the [: and the + :]'s). + Defined a macro SET_LIST_BIT(c) which sets the bit for C in a + character set list. + Took out comments that EXTEND_BUFFER clobbers C. + Made the string "^" match itself, if not RE_CONTEXT_IND_OPS. + Added character classes to bracket expressions. + Change the laststart pointer saved with the start of each + subexpression to point to start_memory instead of after the + following register number. This is because the subexpression + might be in a loop. + Added comments and compacted some code. + Made intervals only work if preceded by an re matching a single + character or a subexpression. + Made back references to nonexistent subexpressions illegal if + using POSIX syntax. + Made intervals work on the last preceding character of a + concatenation of characters, e.g., ab{0,} matches abbb, not abab. + Moved macro PREFETCH to outside the routine. + + (re_compile_fastmap): Added succeed_n to work analogously to + on_failure_jump if n is zero and jump_n to work analogously to + the other backward jumps. + + (re_match_2): Defined macro SET_REGS_MATCHED to set which + current subexpressions had matches within them. + Changed some comments. + Added reg_active and reg_matched_something arrays to keep track + of in which subexpressions currently have matched something. + Defined MATCHING_IN_FIRST_STRING and replaced ``dend == end_match_1'' + with it to make code easier to understand. + Fixed so can apply * and intervals to arbitrarily nested + subexpressions. (Lots of previous bugs here.) + Changed so won't match a newline if syntax bit RE_DOT_NOT_NULL is set. + Made the upcase array nonstatic so the testing file could use it also. + + (main.c): Moved the tests out to another file. + + (tests.c): Moved all the testing stuff here. + +Sat Nov 18 19:30:30 1989 Kathy Hargreaves (kathy at hayley) + + * regex.c: (re_compile_pattern): Defined RE_DUP_MAX, the maximum + number of times an interval can match a pattern. + Added macro GET_UNSIGNED_NUMBER (used to get below): + Added variables lower_bound and upper_bound for upper and lower + bounds of intervals. + Added variable num_fetches so intervals could do backtracking. + Added code to handle '{' and "\{" and intervals. + Added to comments. + + (store_jump_n): (Added) Stores a jump with a number following the + relative address (for intervals). + + (insert_jump_n): (Added) Inserts a jump_n. + + (re_match_2): Defined a macro ESTACK_PUSH_2 for the error stack; + it checks for overflow and reallocates if necessary. + + * regex.h: Added bits (RE_INTERVALS and RE_NO_BK_CURLY_BRACES) + to obscure syntax to indicate whether or not + a syntax handles intervals and recognizes either \{ and + \} or { and } as operators. Also added two syntaxes + RE_SYNTAX_POSIX_BASIC and RE_POSIX_EXTENDED and two command codes + to the enumeration regexpcode; they are succeed_n and jump_n. + +Sat Nov 18 19:30:30 1989 Kathy Hargreaves (kathy at hayley) + + * regex.c: (re_compile_pattern): Defined INIT_BUFF_SIZE to get rid + of repeated constants in code. Tested with value 1. + Renamed PATPUSH as BUFPUSH, since it pushes things onto the + buffer, not the pattern. Also made this macro extend the buffer + if it's full (so could do the following): + Took out code at top of loop that checks to see if buffer is going + to be full after 10 additions (and reallocates if necessary). + + (insert_jump): Rearranged declaration lines so comments would read + better. + + (re_match_2): Compacted exactn code and added more comments. + + (main): Defined macros TEST_MATCH and MATCH_SELF to do + testing; took out loop so could use these instead. + +Tue Oct 24 20:57:18 1989 Kathy Hargreaves (kathy at hayley) + + * regex.c (re_set_syntax): Gave argument `syntax' a type. + (store_jump, insert_jump): made them void functions. + +Local Variables: +mode: indented-text +left-margin: 8 +version-control: never +End: diff --git a/gnu/lib/libregex/INSTALL b/gnu/lib/libregex/INSTALL new file mode 100644 index 0000000000..014e0f728a --- /dev/null +++ b/gnu/lib/libregex/INSTALL @@ -0,0 +1,117 @@ +This is a generic INSTALL file for utilities distributions. +If this package does not come with, e.g., installable documentation or +data files, please ignore the references to them below. + +To compile this package: + +1. Configure the package for your system. In the directory that this +file is in, type `./configure'. If you're using `csh' on an old +version of System V, you might need to type `sh configure' instead to +prevent `csh' from trying to execute `configure' itself. + +The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation, and +creates the Makefile(s) (one in each subdirectory of the source +directory). In some packages it creates a C header file containing +system-dependent definitions. It also creates a file `config.status' +that you can run in the future to recreate the current configuration. + +Running `configure' takes a minute or two. While it is running, it +prints some messages that tell what it is doing. If you don't want to +see the messages, run `configure' with its standard output redirected +to `/dev/null'; for example, `./configure >/dev/null'. + +To compile the package in a different directory from the one +containing the source code, you must use a version of `make' that +supports the VPATH variable, such as GNU `make'. `cd' to the directory +where you want the object files and executables to go and run +`configure'. `configure' automatically checks for the source code in +the directory that `configure' is in and in `..'. If for some reason +`configure' is not in the source code directory that you are +configuring, then it will report that it can't find the source code. +In that case, run `configure' with the option `--srcdir=DIR', where +DIR is the directory that contains the source code. + +By default, `make install' will install the package's files in +/usr/local/bin, /usr/local/lib, /usr/local/man, etc. You can specify +an installation prefix other than /usr/local by giving `configure' the +option `--prefix=PATH'. Alternately, you can do so by giving a value +for the `prefix' variable when you run `make', e.g., + make prefix=/usr/gnu + +You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If +you give `configure' the option `--exec-prefix=PATH' or set the +`make' variable `exec_prefix' to PATH, the package will use PATH as +the prefix for installing programs and libraries. Data files and +documentation will still use the regular prefix. Normally, all files +are installed using the regular prefix. + +Another `configure' option is useful mainly in `Makefile' rules for +updating `config.status' and `Makefile'. The `--no-create' option +figures out the configuration for your system and records it in +`config.status', without actually configuring the package (creating +`Makefile's and perhaps a configuration header file). Later, you can +run `./config.status' to actually configure the package. You can also +give `config.status' the `--recheck' option, which makes it re-run +`configure' with the same arguments you used before. This option is +useful if you change `configure'. + +Some packages pay attention to `--with-PACKAGE' options to `configure', +where PACKAGE is something like `gnu-libc' or `x' (for the X Window System). +The README should mention any --with- options that the package recognizes. + +`configure' ignores any other arguments that you give it. + +If your system requires unusual options for compilation or linking +that `configure' doesn't know about, you can give `configure' initial +values for some variables by setting them in the environment. In +Bourne-compatible shells, you can do that on the command line like +this: + CC='gcc -traditional' DEFS=-D_POSIX_SOURCE ./configure + +The `make' variables that you might want to override with environment +variables when running `configure' are: + +(For these variables, any value given in the environment overrides the +value that `configure' would choose:) +CC C compiler program. + Default is `cc', or `gcc' if `gcc' is in your PATH. +INSTALL Program to use to install files. + Default is `install' if you have it, `cp' otherwise. + +(For these variables, any value given in the environment is added to +the value that `configure' chooses:) +DEFS Configuration options, in the form `-Dfoo -Dbar ...' + Do not use this variable in packages that create a + configuration header file. +LIBS Libraries to link with, in the form `-lfoo -lbar ...' + +If you need to do unusual things to compile the package, we encourage +you to figure out how `configure' could check whether to do them, and +mail diffs or instructions to the address given in the README so we +can include them in the next release. + +2. Type `make' to compile the package. If you want, you can override +the `make' variables CFLAGS and LDFLAGS like this: + + make CFLAGS=-O2 LDFLAGS=-s + +3. If the package comes with self-tests and you want to run them, +type `make check'. If you're not sure whether there are any, try it; +if `make' responds with something like + make: *** No way to make target `check'. Stop. +then the package does not come with self-tests. + +4. Type `make install' to install programs, data files, and +documentation. + +5. You can remove the program binaries and object files from the +source directory by typing `make clean'. To also remove the +Makefile(s), the header file containing system-dependent definitions +(if the package uses one), and `config.status' (all the files that +`configure' created), type `make distclean'. + +The file `configure.in' is used as a template to create `configure' by +a program called `autoconf'. You will only need it if you want to +regenerate `configure' using a newer version of `autoconf'. diff --git a/gnu/lib/libregex/Makefile b/gnu/lib/libregex/Makefile new file mode 100644 index 0000000000..d6e822f3f7 --- /dev/null +++ b/gnu/lib/libregex/Makefile @@ -0,0 +1,12 @@ +# $Header: /a/cvs/386BSD/src/gnu/libregex/Makefile,v 1.2 1993/08/08 04:38:56 rgrimes Exp $ + +LIB= gnuregex +CFLAGS+=-DHAVE_STRING_H=1 +SRCS= regex.c +NOMAN= noman + +beforeinstall: + install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/regex.h \ + ${DESTDIR}/usr/include + +.include diff --git a/gnu/lib/libregex/Makefile.gnu b/gnu/lib/libregex/Makefile.gnu new file mode 100644 index 0000000000..0976aa8bdc --- /dev/null +++ b/gnu/lib/libregex/Makefile.gnu @@ -0,0 +1,99 @@ +# Generated automatically from Makefile.in by configure. +# Makefile for regex. +# +# Copyright (C) 1992, 1993 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +version = 0.12 + +# You can define CPPFLAGS on the command line. Aside from system-specific +# flags, you can define: +# -DREGEX_MALLOC to use malloc/realloc/free instead of alloca. +# -DDEBUG to enable the compiled pattern disassembler and execution +# tracing; code runs substantially slower. +# -DEXTRACT_MACROS to use the macros EXTRACT_* (as opposed to +# the corresponding C procedures). If not -DDEBUG, the macros +# are used. +CPPFLAGS = + +# Likewise, you can override CFLAGS to optimize, use -Wall, etc. +CFLAGS = -g + +# Ditto for LDFLAGS and LOADLIBES. +LDFLAGS = +LOADLIBES = + +srcdir = . +VPATH = . + +CC = gcc +DEFS = -DHAVE_STRING_H=1 + +SHELL = /bin/sh + +subdirs = doc test + +default all:: regex.o +.PHONY: default all + +regex.o: regex.c regex.h + $(CC) $(CFLAGS) $(CPPFLAGS) $(DEFS) -I. -I$(srcdir) -c $< + +clean mostlyclean:: + rm -f *.o + +distclean:: clean + rm -f Makefile config.status + +extraclean:: distclean + rm -f patch* *~* *\#* *.orig *.rej *.bak core a.out + +configure: configure.in + autoconf + +config.status: configure + sh configure --no-create + +Makefile: Makefile.in config.status + sh config.status + +makeargs = $(MFLAGS) CPPFLAGS='$(CPPFLAGS)' CFLAGS='$(CFLAGS)' CC='$(CC)' \ +DEFS='$(DEFS)' LDFLAGS='$(LDFLAGS)' LOADLIBES='$(LOADLIBES)' + +default all install \ +mostlyclean clean distclean extraclean realclean \ +TAGS check:: + for d in $(subdirs); do (cd $$d; $(MAKE) $(makeargs) $@); done +.PHONY: install mostlyclean clean distclean extraclean realclean TAGS check + +# Prevent GNU make 3 from overflowing arg limit on system V. +.NOEXPORT: + +distfiles = AUTHORS ChangeLog COPYING INSTALL NEWS README \ + *.in configure regex.c regex.h +distdir = regex-$(version) +distargs = version=$(version) distdir=../$(distdir)/$$d +dist: TAGS configure + @echo "Version numbers in: Makefile.in, ChangeLog, NEWS," + @echo " regex.c, regex.h," + @echo " and doc/xregex.texi (if modified)." + rm -rf $(distdir) + mkdir $(distdir) + ln $(distfiles) $(distdir) + for d in $(subdirs); do (cd $$d; $(MAKE) $(distargs) dist); done + tar czhf $(distdir).tar.Z $(distdir) + rm -rf $(distdir) +.PHONY: dist diff --git a/gnu/lib/libregex/Makefile.in b/gnu/lib/libregex/Makefile.in new file mode 100644 index 0000000000..836e6de0ba --- /dev/null +++ b/gnu/lib/libregex/Makefile.in @@ -0,0 +1,98 @@ +# Makefile for regex. +# +# Copyright (C) 1992, 1993 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +version = 0.12 + +# You can define CPPFLAGS on the command line. Aside from system-specific +# flags, you can define: +# -DREGEX_MALLOC to use malloc/realloc/free instead of alloca. +# -DDEBUG to enable the compiled pattern disassembler and execution +# tracing; code runs substantially slower. +# -DEXTRACT_MACROS to use the macros EXTRACT_* (as opposed to +# the corresponding C procedures). If not -DDEBUG, the macros +# are used. +CPPFLAGS = + +# Likewise, you can override CFLAGS to optimize, use -Wall, etc. +CFLAGS = -g + +# Ditto for LDFLAGS and LOADLIBES. +LDFLAGS = +LOADLIBES = + +srcdir = @srcdir@ +VPATH = @srcdir@ + +CC = @CC@ +DEFS = @DEFS@ + +SHELL = /bin/sh + +subdirs = doc test + +default all:: regex.o +.PHONY: default all + +regex.o: regex.c regex.h + $(CC) $(CFLAGS) $(CPPFLAGS) $(DEFS) -I. -I$(srcdir) -c $< + +clean mostlyclean:: + rm -f *.o + +distclean:: clean + rm -f Makefile config.status + +extraclean:: distclean + rm -f patch* *~* *\#* *.orig *.rej *.bak core a.out + +configure: configure.in + autoconf + +config.status: configure + sh configure --no-create + +Makefile: Makefile.in config.status + sh config.status + +makeargs = $(MFLAGS) CPPFLAGS='$(CPPFLAGS)' CFLAGS='$(CFLAGS)' CC='$(CC)' \ +DEFS='$(DEFS)' LDFLAGS='$(LDFLAGS)' LOADLIBES='$(LOADLIBES)' + +default all install \ +mostlyclean clean distclean extraclean realclean \ +TAGS check:: + for d in $(subdirs); do (cd $$d; $(MAKE) $(makeargs) $@); done +.PHONY: install mostlyclean clean distclean extraclean realclean TAGS check + +# Prevent GNU make 3 from overflowing arg limit on system V. +.NOEXPORT: + +distfiles = AUTHORS ChangeLog COPYING INSTALL NEWS README \ + *.in configure regex.c regex.h +distdir = regex-$(version) +distargs = version=$(version) distdir=../$(distdir)/$$d +dist: TAGS configure + @echo "Version numbers in: Makefile.in, ChangeLog, NEWS," + @echo " regex.c, regex.h," + @echo " and doc/xregex.texi (if modified)." + rm -rf $(distdir) + mkdir $(distdir) + ln $(distfiles) $(distdir) + for d in $(subdirs); do (cd $$d; $(MAKE) $(distargs) dist); done + tar czhf $(distdir).tar.Z $(distdir) + rm -rf $(distdir) +.PHONY: dist diff --git a/gnu/lib/libregex/NEWS b/gnu/lib/libregex/NEWS new file mode 100644 index 0000000000..b3a899b456 --- /dev/null +++ b/gnu/lib/libregex/NEWS @@ -0,0 +1,62 @@ +Version 0.12 + +* regex.c does not #define bcmp/bcopy/bzero if they already are. + +* regex.h does not redefine `const' if it is already defined, even if + __STDC__ is not defined. + +* RE_SYNTAX_ED added (same as POSIX BRE's). + +* The following bugs have been fixed, among others: + * The pattern \w+ doesn't infinite loop. + * The pattern ".+\n" is compiled correctly. + * Expressions with more than MAX_REGNUM groups are compiled correctly. + +* Patterns that end in a repetition operator (e.g., `*') match + slightly faster if no looping is actually necessary. + +Version 0.11 (17 Sep 92) + +* Back-references to nonexistent subexpressions, as in the r.e. `abc\1', + are always invalid. Previously, they could match the literal digit, + e.g., the stated r.e. might have matched `abc1'. + +* Empty subexpressions are always valid (POSIX leaves this undefined). + +* Simplified rules for ^ and $ being anchors. + +* One minor speedup (rewriting the C procedure `pop_failure_point' as a + macro again). + +* Bug fixes involving: + - Declarations in regex.h and non-ANSI compilers. + - Bracket expressions with characters between 0x80-0xff. + - Memory leak in re_match_2 on systems requiring `alloca (0)' to + free alloca'd storage. + +* Test and documentation files moved into subdirectories. + +Version 0.10 (9 Sep 92) + +* `obscure_syntax' is now called `re_default_syntax'. + +* `re_comp's return type is no longer `const', for compatibility with BSD. + +* POSIX syntaxes now include as much functionality as possible + (consistent with the standard). + +* Compilation conditionals normalized to what the rest of GNU is + migrating towards these days. + +* Bug fixes involving: + - Ranges with characters between 0x80 and 0xff, e.g., [\001-\377]. + - `re_compile_fastmap' and the sequence `.*\n'. + - Intervals with exact counts, e.g., a{5}. + +* Changed distribution to use a standard Makefile, install the info + files, use a configure script, etc. + +Version 0.9 + +* The longest match was not always chosen: `a*|ab' didn't match `aab'. + diff --git a/gnu/lib/libregex/README b/gnu/lib/libregex/README new file mode 100644 index 0000000000..918e1a03b8 --- /dev/null +++ b/gnu/lib/libregex/README @@ -0,0 +1,60 @@ +This directory contains the GNU regex library. It is compliant with +POSIX.2, except for internationalization features. + +See the file NEWS for a list of major changes in the current release. + +See the file INSTALL for compilation instructions. (The only thing +installed is the documentation; regex.c is compiled into regex.o, but +not installed anywhere.) + +The subdirectory `doc' contains a (programmers') manual for the library. +It's probably out-of-date. Improvements are welcome. + +The subdirectory `test' contains the various tests we've written. + +We know this code is not as fast as it might be. If you have specific +suggestions, profiling results, or other such useful information to +report, please do. + +Emacs 18 is not going use this revised regex (but Emacs 19 will). If +you want to try it with Emacs 18, apply the patch at the end of this +file first. + +Mail bug reports to bug-gnu-utils@prep.ai.mit.edu. + +Please include an actual regular expression that fails (and the syntax +used to compile it); without that, there's no way to reproduce the bug, +so there's no way we can fix it. Even if you include a patch, also +include the regular expression in error; otherwise, we can't know for +sure what you're trying to fix. + +Here is the patch to make this version of regex work with Emacs 18. + +*** ORIG/search.c Tue Jan 8 13:04:55 1991 +--- search.c Sun Jan 5 10:57:00 1992 +*************** +*** 25,26 **** +--- 25,28 ---- + #include "commands.h" ++ ++ #include + #include "regex.h" +*************** +*** 477,479 **** + /* really needed. */ +! && *(searchbuf.buffer) == (char) exactn /* first item is "exact match" */ + && searchbuf.buffer[1] + 2 == searchbuf.used) /*first is ONLY item */ +--- 479,482 ---- + /* really needed. */ +! /* first item is "exact match" */ +! && *(searchbuf.buffer) == (char) RE_EXACTN_VALUE + && searchbuf.buffer[1] + 2 == searchbuf.used) /*first is ONLY item */ +*************** +*** 1273,1275 **** + searchbuf.allocated = 100; +! searchbuf.buffer = (char *) malloc (searchbuf.allocated); + searchbuf.fastmap = search_fastmap; +--- 1276,1278 ---- + searchbuf.allocated = 100; +! searchbuf.buffer = (unsigned char *) malloc (searchbuf.allocated); + searchbuf.fastmap = search_fastmap; diff --git a/gnu/lib/libregex/VERSION b/gnu/lib/libregex/VERSION new file mode 100644 index 0000000000..7182be23eb --- /dev/null +++ b/gnu/lib/libregex/VERSION @@ -0,0 +1,3 @@ +GNU regex version 0.12 + +complete, unmodified regex sources are available from prep.ai.mit.edu. diff --git a/gnu/lib/libregex/config.status b/gnu/lib/libregex/config.status new file mode 100644 index 0000000000..1b276aacf3 --- /dev/null +++ b/gnu/lib/libregex/config.status @@ -0,0 +1,59 @@ +#!/bin/sh +# Generated automatically by configure. +# Run this file to recreate the current configuration. +# This directory was configured as follows, +# on host sun-lamp.cs.berkeley.edu: +# +# configure + +for arg +do + case "$arg" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + exec /bin/sh configure ;; + *) echo "Usage: config.status --recheck" 2>&1; exit 1 ;; + esac +done + +trap 'rm -f Makefile doc/Makefile test/Makefile; exit 1' 1 3 15 +CC='gcc' +INSTALL='/usr/bin/install -c' +INSTALL_PROGRAM='$(INSTALL)' +INSTALL_DATA='$(INSTALL) -m 644' +CPP='${CC-cc} -E' +ALLOCA='' +LIBS='' +srcdir='.' +DEFS=' -DHAVE_STRING_H=1' +prefix='/usr' +exec_prefix='${prefix}' +prsub='s%^prefix\([ ]*\)=\([ ]*\).*$%prefix\1=\2/usr% +s%^exec_prefix\([ ]*\)=\([ ]*\).*$%exec_prefix\1=\2${prefix}%' + +top_srcdir=$srcdir +for file in .. Makefile doc/Makefile test/Makefile; do if [ "x$file" != "x.." ]; then + srcdir=$top_srcdir + # Remove last slash and all that follows it. Not all systems have dirname. + dir=`echo $file|sed 's%/[^/][^/]*$%%'` + if test "$dir" != "$file"; then + test "$top_srcdir" != . && srcdir=$top_srcdir/$dir + test ! -d $dir && mkdir $dir + fi + echo creating $file + rm -f $file + echo "# Generated automatically from `echo $file|sed 's|.*/||'`.in by configure." > $file + sed -e " +$prsub +s%@CC@%$CC%g +s%@INSTALL@%$INSTALL%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@CPP@%$CPP%g +s%@ALLOCA@%$ALLOCA%g +s%@LIBS@%$LIBS%g +s%@srcdir@%$srcdir%g +s%@DEFS@%$DEFS% +" $top_srcdir/${file}.in >> $file +fi; done + +exit 0 diff --git a/gnu/lib/libregex/configure b/gnu/lib/libregex/configure new file mode 100644 index 0000000000..29c5b803c3 --- /dev/null +++ b/gnu/lib/libregex/configure @@ -0,0 +1,462 @@ +#!/bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf. +# Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +# Usage: configure [--srcdir=DIR] [--host=HOST] [--gas] [--nfp] [--no-create] +# [--prefix=PREFIX] [--exec-prefix=PREFIX] [--with-PACKAGE] [TARGET] +# Ignores all args except --srcdir, --prefix, --exec-prefix, --no-create, and +# --with-PACKAGE unless this script has special code to handle it. + + +for arg +do + # Handle --exec-prefix with a space before the argument. + if test x$next_exec_prefix = xyes; then exec_prefix=$arg; next_exec_prefix= + # Handle --host with a space before the argument. + elif test x$next_host = xyes; then next_host= + # Handle --prefix with a space before the argument. + elif test x$next_prefix = xyes; then prefix=$arg; next_prefix= + # Handle --srcdir with a space before the argument. + elif test x$next_srcdir = xyes; then srcdir=$arg; next_srcdir= + else + case $arg in + # For backward compatibility, also recognize exact --exec_prefix. + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* | --exec=* | --exe=* | --ex=* | --e=*) + exec_prefix=`echo $arg | sed 's/[-a-z_]*=//'` ;; + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- | --exec | --exe | --ex | --e) + next_exec_prefix=yes ;; + + -gas | --gas | --ga | --g) ;; + + -host=* | --host=* | --hos=* | --ho=* | --h=*) ;; + -host | --host | --hos | --ho | --h) + next_host=yes ;; + + -nfp | --nfp | --nf) ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre | --no-cr | --no-c | --no- | --no) + no_create=1 ;; + + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=`echo $arg | sed 's/[-a-z_]*=//'` ;; + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + next_prefix=yes ;; + + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=* | --s=*) + srcdir=`echo $arg | sed 's/[-a-z_]*=//'` ;; + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr | --s) + next_srcdir=yes ;; + + -with-* | --with-*) + package=`echo $arg|sed 's/-*with-//'` + # Delete all the valid chars; see if any are left. + if test -n "`echo $package|sed 's/[-a-zA-Z0-9_]*//g'`"; then + echo "configure: $package: invalid package name" >&2; exit 1 + fi + eval "with_`echo $package|sed s/-/_/g`=1" ;; + + *) ;; + esac + fi +done + +trap 'rm -f conftest* core; exit 1' 1 3 15 + +rm -f conftest* +compile='${CC-cc} $CFLAGS $DEFS conftest.c -o conftest $LIBS >/dev/null 2>&1' + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +unique_file=regex.c + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + srcdirdefaulted=yes + # Try the directory containing this script, then `..'. + prog=$0 + confdir=`echo $prog|sed 's%/[^/][^/]*$%%'` + test "X$confdir" = "X$prog" && confdir=. + srcdir=$confdir + if test ! -r $srcdir/$unique_file; then + srcdir=.. + fi +fi +if test ! -r $srcdir/$unique_file; then + if test x$srcdirdefaulted = xyes; then + echo "configure: Can not find sources in \`${confdir}' or \`..'." 1>&2 + else + echo "configure: Can not find sources in \`${srcdir}'." 1>&2 + fi + exit 1 +fi +# Preserve a srcdir of `.' to avoid automounter screwups with pwd. +# But we can't avoid them for `..', to make subdirectories work. +case $srcdir in + .|/*|~*) ;; + *) srcdir=`cd $srcdir; pwd` ;; # Make relative path absolute. +esac + + +if test -z "$CC"; then + echo checking for gcc + saveifs="$IFS"; IFS="${IFS}:" + for dir in $PATH; do + test -z "$dir" && dir=. + if test -f $dir/gcc; then + CC="gcc" + break + fi + done + IFS="$saveifs" +fi +test -z "$CC" && CC="cc" + +# Find out if we are using GNU C, under whatever name. +cat > conftest.c < conftest.out 2>&1 +if egrep yes conftest.out >/dev/null 2>&1; then + GCC=1 # For later tests. +fi +rm -f conftest* + +# Make sure to not get the incompatible SysV /etc/install and +# /usr/sbin/install, which might be in PATH before a BSD-like install, +# or the SunOS /usr/etc/install directory, or the AIX /bin/install, +# or the AFS install, which mishandles nonexistent args. (Sigh.) +if test -z "$INSTALL"; then + echo checking for install + saveifs="$IFS"; IFS="${IFS}:" + for dir in $PATH; do + test -z "$dir" && dir=. + case $dir in + /etc|/usr/sbin|/usr/etc|/usr/afsws/bin) ;; + *) + if test -f $dir/install; then + if grep dspmsg $dir/install >/dev/null 2>&1; then + : # AIX + else + INSTALL="$dir/install -c" + INSTALL_PROGRAM='$(INSTALL)' + INSTALL_DATA='$(INSTALL) -m 644' + break + fi + fi + ;; + esac + done + IFS="$saveifs" +fi +INSTALL=${INSTALL-cp} +INSTALL_PROGRAM=${INSTALL_PROGRAM-'$(INSTALL)'} +INSTALL_DATA=${INSTALL_DATA-'$(INSTALL)'} + + +echo checking for AIX +echo checking how to run the C preprocessor +if test -z "$CPP"; then + CPP='${CC-cc} -E' + cat > conftest.c < +EOF +err=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"` +if test -z "$err"; then + : +else + CPP=/lib/cpp +fi +rm -f conftest* +fi + +cat > conftest.c < conftest.out 2>&1" +if egrep "yes" conftest.out >/dev/null 2>&1; then + DEFS="$DEFS -D_ALL_SOURCE=1" +fi +rm -f conftest* + + +echo checking for DYNIX/ptx libseq +cat > conftest.c < conftest.out 2>&1" +if egrep "yes" conftest.out >/dev/null 2>&1; then + SEQUENT=1 +fi +rm -f conftest* + +test -n "$SEQUENT" && test -f /usr/lib/libseq.a && + LIBS="$LIBS -lseq" + +echo checking for POSIXized ISC +if test -d /etc/conf/kconfig.d && + grep _POSIX_VERSION /usr/include/sys/unistd.h >/dev/null 2>&1 +then + ISC=1 # If later tests want to check for ISC. + DEFS="$DEFS -D_POSIX_SOURCE=1" + if test -n "$GCC"; then + CC="$CC -posix" + else + CC="$CC -Xp" + fi +fi + +echo checking for minix/config.h +cat > conftest.c < +EOF +err=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"` +if test -z "$err"; then + MINIX=1 +fi +rm -f conftest* + +# The Minix shell can't assign to the same variable on the same line! +if test -n "$MINIX"; then + DEFS="$DEFS -D_POSIX_SOURCE=1" + DEFS="$DEFS -D_POSIX_1_SOURCE=2" + DEFS="$DEFS -D_MINIX=1" +fi + + +echo checking for ANSI C header files +cat > conftest.c < +#include +#include +#include +EOF +err=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"` +if test -z "$err"; then + # SunOS string.h does not declare mem*, contrary to ANSI. +echo '#include ' > conftest.c +eval "$CPP $DEFS conftest.c > conftest.out 2>&1" +if egrep "memchr" conftest.out >/dev/null 2>&1; then + # SGI's /bin/cc from Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +cat > conftest.c < +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#define XOR(e,f) (((e) && !(f)) || (!(e) && (f))) +int main () { int i; for (i = 0; i < 256; i++) +if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); +exit (0); } + +EOF +eval $compile +if test -s conftest && (./conftest; exit) 2>/dev/null; then + DEFS="$DEFS -DSTDC_HEADERS=1" +fi +rm -f conftest* +fi +rm -f conftest* + +fi +rm -f conftest* + +for hdr in string.h +do +trhdr=HAVE_`echo $hdr | tr '[a-z]./' '[A-Z]__'` +echo checking for ${hdr} +cat > conftest.c < +EOF +err=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"` +if test -z "$err"; then + DEFS="$DEFS -D${trhdr}=1" +fi +rm -f conftest* +done + + +# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works +# for constant arguments. Useless! +echo checking for working alloca.h +cat > conftest.c < +main() { exit(0); } +t() { char *p = alloca(2 * sizeof(int)); } +EOF +if eval $compile; then + DEFS="$DEFS -DHAVE_ALLOCA_H=1" +fi +rm -f conftest* + +decl="#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +#if HAVE_ALLOCA_H +#include +#else +#ifdef _AIX + #pragma alloca +#else +char *alloca (); +#endif +#endif +#endif +" +echo checking for alloca +cat > conftest.c < conftest.c < config.status </dev/null`: +# +# $0 $* + +for arg +do + case "\$arg" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + exec /bin/sh $0 $* ;; + *) echo "Usage: config.status --recheck" 2>&1; exit 1 ;; + esac +done + +trap 'rm -f Makefile doc/Makefile test/Makefile; exit 1' 1 3 15 +CC='$CC' +INSTALL='$INSTALL' +INSTALL_PROGRAM='$INSTALL_PROGRAM' +INSTALL_DATA='$INSTALL_DATA' +CPP='$CPP' +ALLOCA='$ALLOCA' +LIBS='$LIBS' +srcdir='$srcdir' +DEFS='$DEFS' +prefix='$prefix' +exec_prefix='$exec_prefix' +prsub='$prsub' +EOF +cat >> config.status <<\EOF + +top_srcdir=$srcdir +for file in .. Makefile doc/Makefile test/Makefile; do if [ "x$file" != "x.." ]; then + srcdir=$top_srcdir + # Remove last slash and all that follows it. Not all systems have dirname. + dir=`echo $file|sed 's%/[^/][^/]*$%%'` + if test "$dir" != "$file"; then + test "$top_srcdir" != . && srcdir=$top_srcdir/$dir + test ! -d $dir && mkdir $dir + fi + echo creating $file + rm -f $file + echo "# Generated automatically from `echo $file|sed 's|.*/||'`.in by configure." > $file + sed -e " +$prsub +s%@CC@%$CC%g +s%@INSTALL@%$INSTALL%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@CPP@%$CPP%g +s%@ALLOCA@%$ALLOCA%g +s%@LIBS@%$LIBS%g +s%@srcdir@%$srcdir%g +s%@DEFS@%$DEFS% +" $top_srcdir/${file}.in >> $file +fi; done + +exit 0 +EOF +chmod +x config.status +test -n "$no_create" || ./config.status + diff --git a/gnu/lib/libregex/configure.in b/gnu/lib/libregex/configure.in new file mode 100644 index 0000000000..f0fc780250 --- /dev/null +++ b/gnu/lib/libregex/configure.in @@ -0,0 +1,23 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(regex.c) + +AC_PROG_CC +AC_PROG_INSTALL + +dnl I'm not sure if AC_AIX and AC_DYNIX_SEQ are really necessary. The +dnl Autoconf documentation isn't specific about which BSD functions they +dnl provide. +AC_AIX +AC_DYNIX_SEQ +AC_ISC_POSIX +AC_MINIX + +AC_STDC_HEADERS +AC_HAVE_HEADERS(string.h) + +AC_ALLOCA +AC_CONST + +AC_PREFIX(gcc) + +AC_OUTPUT(Makefile doc/Makefile test/Makefile) diff --git a/gnu/lib/libregex/doc/Makefile b/gnu/lib/libregex/doc/Makefile new file mode 100644 index 0000000000..13753ae8ff --- /dev/null +++ b/gnu/lib/libregex/doc/Makefile @@ -0,0 +1,93 @@ +# Generated automatically from Makefile.in by configure. +# Makefile for regex documentation. +# +# Copyright (C) 1992 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +# Installation directories. +prefix = /usr +infodir = $(prefix)/info + +srcdir = . +VPATH = .:../. + +INSTALL = /usr/bin/install -c +INSTALL_DATA = $(INSTALL) -m 644 + +MAKEINFO = makeinfo --no-split +SHELL = /bin/sh +TEX = tex +TEXINDEX = texindex + +default all: regex.info regex.dvi +.PHONY: default all + +# We need to include some code from regex.h. +regex.texi: xregex.texi + rm -f $@ + gawk -f include.awk -vsource=../$(srcdir)/regex.h \ + <../$(srcdir)/doc/xregex.texi \ + | expand >$@ + chmod a-w $@ + +regex.dvi: regex.cps + $(TEX) regex.texi +regex.cps: regex.cp + $(TEXINDEX) regex.?? +regex.cp: regex.texi + $(TEX) ../$(srcdir)/doc/regex.texi + +regex.info: regex.texi + $(MAKEINFO) ../$(srcdir)/doc/regex.texi + +# I know of no way to make a good TAGS file from Texinfo source. +TAGS: + +check: +.PHONY: check + +install: regex.info + -mkdir $(prefix) $(infodir) + for i in *.info*; do $(INSTALL_DATA) $$i $(infodir)/$$i; done +.PHONY: install + +clean mostlyclean: + rm -f regex.?? *.dvi *.log *.toc + +distclean: clean + rm -f Makefile + for f in regex.??s; do if test -z "`cat $$f`"; then rm -f $$f; fi; done + +realclean: distclean + rm -f *.info* regex.??? regex.texi TAGS + +extraclean: distclean + rm -f patch* *~* *\#* *.orig *.rej *.bak core a.out +.PHONY: mostlyclean clean distclean realclean extraclean + +Makefile: Makefile.in ../config.status + (cd ..; sh config.status) + +# Prevent GNU make 3 from overflowing arg limit on system V. +.NOEXPORT: + +# Assumes $(distdir) is the place to put our files. +distfiles = Makefile.in *.texi texinfo.tex include.awk \ + regex.info* regex.aux regex.cps +dist: Makefile regex.info regex.cps + mkdir $(distdir) + ln $(distfiles) $(distdir) +.PHONY: dist diff --git a/gnu/lib/libregex/doc/Makefile.in b/gnu/lib/libregex/doc/Makefile.in new file mode 100644 index 0000000000..2f5d382c06 --- /dev/null +++ b/gnu/lib/libregex/doc/Makefile.in @@ -0,0 +1,92 @@ +# Makefile for regex documentation. +# +# Copyright (C) 1992 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +# Installation directories. +prefix = /usr/local +infodir = $(prefix)/info + +srcdir = @srcdir@ +VPATH = @srcdir@:../@srcdir@ + +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ + +MAKEINFO = makeinfo --no-split +SHELL = /bin/sh +TEX = tex +TEXINDEX = texindex + +default all: regex.info regex.dvi +.PHONY: default all + +# We need to include some code from regex.h. +regex.texi: xregex.texi + rm -f $@ + gawk -f include.awk -vsource=../$(srcdir)/regex.h \ + <../$(srcdir)/doc/xregex.texi \ + | expand >$@ + chmod a-w $@ + +regex.dvi: regex.cps + $(TEX) regex.texi +regex.cps: regex.cp + $(TEXINDEX) regex.?? +regex.cp: regex.texi + $(TEX) ../$(srcdir)/doc/regex.texi + +regex.info: regex.texi + $(MAKEINFO) ../$(srcdir)/doc/regex.texi + +# I know of no way to make a good TAGS file from Texinfo source. +TAGS: + +check: +.PHONY: check + +install: regex.info + -mkdir $(prefix) $(infodir) + for i in *.info*; do $(INSTALL_DATA) $$i $(infodir)/$$i; done +.PHONY: install + +clean mostlyclean: + rm -f regex.?? *.dvi *.log *.toc + +distclean: clean + rm -f Makefile + for f in regex.??s; do if test -z "`cat $$f`"; then rm -f $$f; fi; done + +realclean: distclean + rm -f *.info* regex.??? regex.texi TAGS + +extraclean: distclean + rm -f patch* *~* *\#* *.orig *.rej *.bak core a.out +.PHONY: mostlyclean clean distclean realclean extraclean + +Makefile: Makefile.in ../config.status + (cd ..; sh config.status) + +# Prevent GNU make 3 from overflowing arg limit on system V. +.NOEXPORT: + +# Assumes $(distdir) is the place to put our files. +distfiles = Makefile.in *.texi texinfo.tex include.awk \ + regex.info* regex.aux regex.cps +dist: Makefile regex.info regex.cps + mkdir $(distdir) + ln $(distfiles) $(distdir) +.PHONY: dist diff --git a/gnu/lib/libregex/doc/include.awk b/gnu/lib/libregex/doc/include.awk new file mode 100644 index 0000000000..a1df3f8463 --- /dev/null +++ b/gnu/lib/libregex/doc/include.awk @@ -0,0 +1,19 @@ +# Assume `source' is set with -vsource=filename on the command line. +# +/^\[\[\[/ { inclusion = $2; # name of the thing to include. + printing = 0; + while ((getline line < source) > 0) + { + if (match (line, "\\[\\[\\[end " inclusion "\\]\\]\\]")) + printing = 0; + + if (printing) + print line; + + if (match (line,"\\[\\[\\[begin " inclusion "\\]\\]\\]")) + printing = 1; + } + close (source); + next; + } + { print } diff --git a/gnu/lib/libregex/doc/regex.aux b/gnu/lib/libregex/doc/regex.aux new file mode 100644 index 0000000000..fd6a245eb1 --- /dev/null +++ b/gnu/lib/libregex/doc/regex.aux @@ -0,0 +1,136 @@ +'xrdef {Overview-pg}{1} +'xrdef {Overview-snt}{Chapter'tie1} +'xrdef {Regular Expression Syntax-pg}{2} +'xrdef {Regular Expression Syntax-snt}{Chapter'tie2} +'xrdef {Syntax Bits-pg}{2} +'xrdef {Syntax Bits-snt}{Section'tie2.1} +'xrdef {Predefined Syntaxes-pg}{5} +'xrdef {Predefined Syntaxes-snt}{Section'tie2.2} +'xrdef {Collating Elements vs. Characters-pg}{6} +'xrdef {Collating Elements vs. Characters-snt}{Section'tie2.3} +'xrdef {The Backslash Character-pg}{7} +'xrdef {The Backslash Character-snt}{Section'tie2.4} +'xrdef {Common Operators-pg}{9} +'xrdef {Common Operators-snt}{Chapter'tie3} +'xrdef {Match-self Operator-pg}{9} +'xrdef {Match-self Operator-snt}{Section'tie3.1} +'xrdef {Match-any-character Operator-pg}{9} +'xrdef {Match-any-character Operator-snt}{Section'tie3.2} +'xrdef {Concatenation Operator-pg}{10} +'xrdef {Concatenation Operator-snt}{Section'tie3.3} +'xrdef {Repetition Operators-pg}{10} +'xrdef {Repetition Operators-snt}{Section'tie3.4} +'xrdef {Match-zero-or-more Operator-pg}{10} +'xrdef {Match-zero-or-more Operator-snt}{Section'tie3.4.1} +'xrdef {Match-one-or-more Operator-pg}{11} +'xrdef {Match-one-or-more Operator-snt}{Section'tie3.4.2} +'xrdef {Match-zero-or-one Operator-pg}{11} +'xrdef {Match-zero-or-one Operator-snt}{Section'tie3.4.3} +'xrdef {Interval Operators-pg}{12} +'xrdef {Interval Operators-snt}{Section'tie3.4.4} +'xrdef {Alternation Operator-pg}{13} +'xrdef {Alternation Operator-snt}{Section'tie3.5} +'xrdef {List Operators-pg}{13} +'xrdef {List Operators-snt}{Section'tie3.6} +'xrdef {Character Class Operators-pg}{14} +'xrdef {Character Class Operators-snt}{Section'tie3.6.1} +'xrdef {Range Operator-pg}{15} +'xrdef {Range Operator-snt}{Section'tie3.6.2} +'xrdef {Grouping Operators-pg}{16} +'xrdef {Grouping Operators-snt}{Section'tie3.7} +'xrdef {Back-reference Operator-pg}{17} +'xrdef {Back-reference Operator-snt}{Section'tie3.8} +'xrdef {Anchoring Operators-pg}{18} +'xrdef {Anchoring Operators-snt}{Section'tie3.9} +'xrdef {Match-beginning-of-line Operator-pg}{18} +'xrdef {Match-beginning-of-line Operator-snt}{Section'tie3.9.1} +'xrdef {Match-end-of-line Operator-pg}{18} +'xrdef {Match-end-of-line Operator-snt}{Section'tie3.9.2} +'xrdef {GNU Operators-pg}{20} +'xrdef {GNU Operators-snt}{Chapter'tie4} +'xrdef {Word Operators-pg}{20} +'xrdef {Word Operators-snt}{Section'tie4.1} +'xrdef {Non-Emacs Syntax Tables-pg}{20} +'xrdef {Non-Emacs Syntax Tables-snt}{Section'tie4.1.1} +'xrdef {Match-word-boundary Operator-pg}{20} +'xrdef {Match-word-boundary Operator-snt}{Section'tie4.1.2} +'xrdef {Match-within-word Operator-pg}{20} +'xrdef {Match-within-word Operator-snt}{Section'tie4.1.3} +'xrdef {Match-beginning-of-word Operator-pg}{21} +'xrdef {Match-beginning-of-word Operator-snt}{Section'tie4.1.4} +'xrdef {Match-end-of-word Operator-pg}{21} +'xrdef {Match-end-of-word Operator-snt}{Section'tie4.1.5} +'xrdef {Match-word-constituent Operator-pg}{21} +'xrdef {Match-word-constituent Operator-snt}{Section'tie4.1.6} +'xrdef {Match-non-word-constituent Operator-pg}{21} +'xrdef {Match-non-word-constituent Operator-snt}{Section'tie4.1.7} +'xrdef {Buffer Operators-pg}{21} +'xrdef {Buffer Operators-snt}{Section'tie4.2} +'xrdef {Match-beginning-of-buffer Operator-pg}{21} +'xrdef {Match-beginning-of-buffer Operator-snt}{Section'tie4.2.1} +'xrdef {Match-end-of-buffer Operator-pg}{21} +'xrdef {Match-end-of-buffer Operator-snt}{Section'tie4.2.2} +'xrdef {GNU Emacs Operators-pg}{22} +'xrdef {GNU Emacs Operators-snt}{Chapter'tie5} +'xrdef {Syntactic Class Operators-pg}{22} +'xrdef {Syntactic Class Operators-snt}{Section'tie5.1} +'xrdef {Emacs Syntax Tables-pg}{22} +'xrdef {Emacs Syntax Tables-snt}{Section'tie5.1.1} +'xrdef {Match-syntactic-class Operator-pg}{22} +'xrdef {Match-syntactic-class Operator-snt}{Section'tie5.1.2} +'xrdef {Match-not-syntactic-class Operator-pg}{22} +'xrdef {Match-not-syntactic-class Operator-snt}{Section'tie5.1.3} +'xrdef {What Gets Matched?-pg}{23} +'xrdef {What Gets Matched?-snt}{Chapter'tie6} +'xrdef {Programming with Regex-pg}{24} +'xrdef {Programming with Regex-snt}{Chapter'tie7} +'xrdef {GNU Regex Functions-pg}{24} +'xrdef {GNU Regex Functions-snt}{Section'tie7.1} +'xrdef {GNU Pattern Buffers-pg}{24} +'xrdef {GNU Pattern Buffers-snt}{Section'tie7.1.1} +'xrdef {GNU Regular Expression Compiling-pg}{26} +'xrdef {GNU Regular Expression Compiling-snt}{Section'tie7.1.2} +'xrdef {GNU Matching-pg}{27} +'xrdef {GNU Matching-snt}{Section'tie7.1.3} +'xrdef {GNU Searching-pg}{28} +'xrdef {GNU Searching-snt}{Section'tie7.1.4} +'xrdef {Matching/Searching with Split Data-pg}{29} +'xrdef {Matching/Searching with Split Data-snt}{Section'tie7.1.5} +'xrdef {Searching with Fastmaps-pg}{30} +'xrdef {Searching with Fastmaps-snt}{Section'tie7.1.6} +'xrdef {GNU Translate Tables-pg}{31} +'xrdef {GNU Translate Tables-snt}{Section'tie7.1.7} +'xrdef {Using Registers-pg}{32} +'xrdef {Using Registers-snt}{Section'tie7.1.8} +'xrdef {Freeing GNU Pattern Buffers-pg}{34} +'xrdef {Freeing GNU Pattern Buffers-snt}{Section'tie7.1.9} +'xrdef {POSIX Regex Functions-pg}{35} +'xrdef {POSIX Regex Functions-snt}{Section'tie7.2} +'xrdef {POSIX Pattern Buffers-pg}{35} +'xrdef {POSIX Pattern Buffers-snt}{Section'tie7.2.1} +'xrdef {POSIX Regular Expression Compiling-pg}{35} +'xrdef {POSIX Regular Expression Compiling-snt}{Section'tie7.2.2} +'xrdef {POSIX Matching-pg}{37} +'xrdef {POSIX Matching-snt}{Section'tie7.2.3} +'xrdef {Reporting Errors-pg}{38} +'xrdef {Reporting Errors-snt}{Section'tie7.2.4} +'xrdef {Using Byte Offsets-pg}{39} +'xrdef {Using Byte Offsets-snt}{Section'tie7.2.5} +'xrdef {Freeing POSIX Pattern Buffers-pg}{39} +'xrdef {Freeing POSIX Pattern Buffers-snt}{Section'tie7.2.6} +'xrdef {BSD Regex Functions-pg}{40} +'xrdef {BSD Regex Functions-snt}{Section'tie7.3} +'xrdef {BSD Regular Expression Compiling-pg}{40} +'xrdef {BSD Regular Expression Compiling-snt}{Section'tie7.3.1} +'xrdef {BSD Searching-pg}{40} +'xrdef {BSD Searching-snt}{Section'tie7.3.2} +'xrdef {Copying-pg}{42} +'xrdef {Copying-snt}{Appendix'tie'char65{}} +'xrdef {Copying-pg}{42} +'xrdef {Copying-snt}{} +'xrdef {Copying-pg}{43} +'xrdef {Copying-snt}{} +'xrdef {Copying-pg}{48} +'xrdef {Copying-snt}{} +'xrdef {Index-pg}{50} +'xrdef {Index-snt}{} diff --git a/gnu/lib/libregex/doc/regex.cps b/gnu/lib/libregex/doc/regex.cps new file mode 100644 index 0000000000..8b2e57c64e --- /dev/null +++ b/gnu/lib/libregex/doc/regex.cps @@ -0,0 +1,152 @@ +\initial {$} +\entry {\code {$}}{18} +\initial {(} +\entry {\code {(}}{16} +\initial {)} +\entry {\code {)}}{16} +\initial {*} +\entry {\samp {*}}{10} +\initial {-} +\entry {\samp {-}}{13} +\initial {.} +\entry {\samp {.}}{9} +\initial {:} +\entry {\samp {:]} in regex}{14} +\initial {?} +\entry {\samp {?}}{11} +\initial {[} +\entry {\samp {[}}{13} +\entry {\samp {[:} in regex}{14} +\entry {\samp {[{\tt\hat}}}{13} +\initial {]} +\entry {\samp {]}}{13} +\initial {{\tt\char'173}} +\entry {\samp {{\tt\char'173}}}{12} +\initial {{\tt\char'174}} +\entry {\code {{\tt\char'174}}}{13} +\initial {{\tt\char'175}} +\entry {\samp {{\tt\char'175}}}{12} +\initial {{\tt\char43}} +\entry {\samp {{\tt\char43}}}{11} +\initial {{\tt\hat}} +\entry {\samp {{\tt\hat}}}{13} +\entry {\code {{\tt\hat}}}{18} +\initial {{\tt\indexbackslash }} +\entry {{\tt\indexbackslash }}{7} +\entry {\samp {{\tt\indexbackslash }}}{13} +\entry {\samp {{\tt\indexbackslash }'}}{21} +\entry {\code {{\tt\indexbackslash }(}}{16} +\entry {\code {{\tt\indexbackslash })}}{16} +\entry {\samp {{\tt\indexbackslash }`}}{21} +\entry {\samp {{\tt\indexbackslash }{\tt\char'173}}}{12} +\entry {\code {{\tt\indexbackslash }{\tt\char'174}}}{13} +\entry {\samp {{\tt\indexbackslash }{\tt\char'175}}}{12} +\entry {\samp {{\tt\indexbackslash }{\tt\gtr}}}{21} +\entry {\samp {{\tt\indexbackslash }{\tt\less}}}{21} +\entry {\samp {{\tt\indexbackslash }b}}{20} +\entry {\samp {{\tt\indexbackslash }B}}{20} +\entry {\samp {{\tt\indexbackslash }s}}{22} +\entry {\samp {{\tt\indexbackslash }S}}{22} +\entry {\samp {{\tt\indexbackslash }w}}{21} +\entry {\samp {{\tt\indexbackslash }W}}{21} +\initial {A} +\entry {\code {allocated \r {initialization}}}{26} +\entry {alternation operator}{13} +\entry {alternation operator and \samp {{\tt\hat}}}{18} +\entry {anchoring}{18} +\entry {anchors}{18} +\entry {Awk}{5} +\initial {B} +\entry {back references}{17} +\entry {backtracking}{10, 13} +\entry {beginning-of-line operator}{18} +\entry {bracket expression}{13} +\entry {\code {buffer \r {field, set by \code {re{\_}compile{\_}pattern}}}}{27} +\entry {\code {buffer \r {initialization}}}{26} +\initial {C} +\entry {character classes}{14} +\initial {E} +\entry {Egrep}{5} +\entry {Emacs}{5} +\entry {end-of-line operator}{18} +\entry {\code {end\penalty 10000{\spaceskip = 0pt{} }\r {in\penalty 10000{\spaceskip = 0pt{} }\code {struct\penalty 10000{\spaceskip = 0pt{} }re_registers}}}}{32} +\initial {F} +\entry {\code {fastmap \r {initialization}}}{26} +\entry {\code {fastmap{\_}accurate \r {field, set by \code {re{\_}compile{\_}pattern}}}}{27} +\entry {fastmaps}{30} +\initial {G} +\entry {Grep}{5} +\entry {grouping}{16} +\initial {I} +\entry {ignoring case}{35} +\entry {interval expression}{12} +\initial {M} +\entry {matching list}{13} +\entry {matching newline}{13} +\entry {matching with GNU functions}{27} +\initial {N} +\entry {\code {newline{\_}anchor \r {field in pattern buffer}}}{18} +\entry {nonmatching list}{13} +\entry {\code {not{\_}bol \r {field in pattern buffer}}}{18} +\entry {\code {num_regs\penalty 10000{\spaceskip = 0pt{} }\r {in\penalty 10000{\spaceskip = 0pt{} }\code {struct\penalty 10000{\spaceskip = 0pt{} }re_registers}}}}{32} +\initial {O} +\entry {open-group operator and \samp {{\tt\hat}}}{18} +\entry {or operator}{13} +\initial {P} +\entry {parenthesizing}{16} +\entry {pattern buffer initialization}{26} +\entry {pattern buffer, definition of}{24} +\entry {POSIX Awk}{5} +\initial {R} +\entry {\code {range \r {argument to \code {re{\_}search}}}}{28} +\entry {\code {re_registers}}{32} +\entry {\code {RE{\_}BACKSLASH{\_}ESCAPE{\_}IN{\_}LIST}}{3} +\entry {\code {RE{\_}BK{\_}PLUS{\_}QM}}{3} +\entry {\code {RE{\_}CHAR{\_}CLASSES}}{3} +\entry {\code {RE{\_}CONTEXT{\_}INDEP{\_}ANCHORS}}{3} +\entry {\code {RE{\_}CONTEXT{\_}INDEP{\_}ANCHORS \r {(and \samp {{\tt\hat}})}}}{18} +\entry {\code {RE{\_}CONTEXT{\_}INDEP{\_}OPS}}{3} +\entry {\code {RE{\_}CONTEXT{\_}INVALID{\_}OPS}}{3} +\entry {\code {RE{\_}DOT{\_}NEWLINE}}{3} +\entry {\code {RE{\_}DOT{\_}NOT{\_}NULL}}{4} +\entry {\code {RE{\_}INTERVALS}}{4} +\entry {\code {RE{\_}LIMITED{\_}OPS}}{4} +\entry {\code {RE{\_}NEWLINE{\_}ALT}}{4} +\entry {\code {RE{\_}NO{\_}BK{\_}BRACES}}{4} +\entry {\code {RE{\_}NO{\_}BK{\_}PARENS}}{4} +\entry {\code {RE{\_}NO{\_}BK{\_}REFS}}{4} +\entry {\code {RE{\_}NO{\_}BK{\_}VBAR}}{4} +\entry {\code {RE{\_}NO{\_}EMPTY{\_}RANGES}}{4} +\entry {\code {re{\_}nsub \r {field, set by \code {re{\_}compile{\_}pattern}}}}{27} +\entry {\code {re{\_}pattern{\_}buffer \r {definition}}}{24} +\entry {\code {re{\_}syntax{\_}options \r {initialization}}}{26} +\entry {\code {RE{\_}UNMATCHED{\_}RIGHT{\_}PAREN{\_}ORD}}{4} +\entry {\code {REG{\_}EXTENDED}}{35} +\entry {\code {REG{\_}ICASE}}{35} +\entry {\code {REG{\_}NEWLINE}}{36} +\entry {\code {REG{\_}NOSUB}}{35} +\entry {\code {regex.c}}{1} +\entry {\code {regex.h}}{1} +\entry {regexp anchoring}{18} +\entry {\code {regmatch{\_}t}}{39} +\entry {\code {regs{\_}allocated}}{32} +\entry {\code {REGS{\_}FIXED}}{33} +\entry {\code {REGS{\_}REALLOCATE}}{32} +\entry {\code {REGS{\_}UNALLOCATED}}{32} +\entry {regular expressions, syntax of}{2} +\initial {S} +\entry {searching with GNU functions}{28} +\entry {\code {start \r {argument to \code {re{\_}search}}}}{28} +\entry {\code {start\penalty 10000{\spaceskip = 0pt{} }\r {in\penalty 10000{\spaceskip = 0pt{} }\code {struct\penalty 10000{\spaceskip = 0pt{} }re_registers}}}}{32} +\entry {\code {struct re{\_}pattern{\_}buffer \r {definition}}}{24} +\entry {subexpressions}{16} +\entry {syntax bits}{2} +\entry {\code {syntax \r {field, set by \code {re{\_}compile{\_}pattern}}}}{27} +\entry {syntax initialization}{26} +\entry {syntax of regular expressions}{2} +\initial {T} +\entry {\code {translate \r {initialization}}}{26} +\initial {U} +\entry {\code {used \r {field, set by \code {re{\_}compile{\_}pattern}}}}{27} +\initial {W} +\entry {word boundaries, matching}{20} diff --git a/gnu/lib/libregex/doc/regex.info b/gnu/lib/libregex/doc/regex.info new file mode 100644 index 0000000000..90deedeaf4 --- /dev/null +++ b/gnu/lib/libregex/doc/regex.info @@ -0,0 +1,2836 @@ +This is Info file regex.info, produced by Makeinfo-1.52 from the input +file .././doc/regex.texi. + + This file documents the GNU regular expression library. + + Copyright (C) 1992, 1993 Free Software Foundation, Inc. + + Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + + Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +section entitled "GNU General Public License" is included exactly as in +the original, and provided that the entire resulting derived work is +distributed under the terms of a permission notice identical to this +one. + + Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that the section entitled "GNU General Public License" +may be included in a translation approved by the Free Software +Foundation instead of in the original English. + + +File: regex.info, Node: Top, Next: Overview, Prev: (dir), Up: (dir) + +Regular Expression Library +************************** + + This manual documents how to program with the GNU regular expression +library. This is edition 0.12a of the manual, 19 September 1992. + + The first part of this master menu lists the major nodes in this Info +document, including the index. The rest of the menu lists all the +lower level nodes in the document. + +* Menu: + +* Overview:: +* Regular Expression Syntax:: +* Common Operators:: +* GNU Operators:: +* GNU Emacs Operators:: +* What Gets Matched?:: +* Programming with Regex:: +* Copying:: Copying and sharing Regex. +* Index:: General index. + -- The Detailed Node Listing -- + +Regular Expression Syntax + +* Syntax Bits:: +* Predefined Syntaxes:: +* Collating Elements vs. Characters:: +* The Backslash Character:: + +Common Operators + +* Match-self Operator:: Ordinary characters. +* Match-any-character Operator:: . +* Concatenation Operator:: Juxtaposition. +* Repetition Operators:: * + ? {} +* Alternation Operator:: | +* List Operators:: [...] [^...] +* Grouping Operators:: (...) +* Back-reference Operator:: \digit +* Anchoring Operators:: ^ $ + +Repetition Operators + +* Match-zero-or-more Operator:: * +* Match-one-or-more Operator:: + +* Match-zero-or-one Operator:: ? +* Interval Operators:: {} + +List Operators (`[' ... `]' and `[^' ... `]') + +* Character Class Operators:: [:class:] +* Range Operator:: start-end + +Anchoring Operators + +* Match-beginning-of-line Operator:: ^ +* Match-end-of-line Operator:: $ + +GNU Operators + +* Word Operators:: +* Buffer Operators:: + +Word Operators + +* Non-Emacs Syntax Tables:: +* Match-word-boundary Operator:: \b +* Match-within-word Operator:: \B +* Match-beginning-of-word Operator:: \< +* Match-end-of-word Operator:: \> +* Match-word-constituent Operator:: \w +* Match-non-word-constituent Operator:: \W + +Buffer Operators + +* Match-beginning-of-buffer Operator:: \` +* Match-end-of-buffer Operator:: \' + +GNU Emacs Operators + +* Syntactic Class Operators:: + +Syntactic Class Operators + +* Emacs Syntax Tables:: +* Match-syntactic-class Operator:: \sCLASS +* Match-not-syntactic-class Operator:: \SCLASS + +Programming with Regex + +* GNU Regex Functions:: +* POSIX Regex Functions:: +* BSD Regex Functions:: + +GNU Regex Functions + +* GNU Pattern Buffers:: The re_pattern_buffer type. +* GNU Regular Expression Compiling:: re_compile_pattern () +* GNU Matching:: re_match () +* GNU Searching:: re_search () +* Matching/Searching with Split Data:: re_match_2 (), re_search_2 () +* Searching with Fastmaps:: re_compile_fastmap () +* GNU Translate Tables:: The `translate' field. +* Using Registers:: The re_registers type and related fns. +* Freeing GNU Pattern Buffers:: regfree () + +POSIX Regex Functions + +* POSIX Pattern Buffers:: The regex_t type. +* POSIX Regular Expression Compiling:: regcomp () +* POSIX Matching:: regexec () +* Reporting Errors:: regerror () +* Using Byte Offsets:: The regmatch_t type. +* Freeing POSIX Pattern Buffers:: regfree () + +BSD Regex Functions + +* BSD Regular Expression Compiling:: re_comp () +* BSD Searching:: re_exec () + + +File: regex.info, Node: Overview, Next: Regular Expression Syntax, Prev: Top, Up: Top + +Overview +******** + + A "regular expression" (or "regexp", or "pattern") is a text string +that describes some (mathematical) set of strings. A regexp R +"matches" a string S if S is in the set of strings described by R. + + Using the Regex library, you can: + + * see if a string matches a specified pattern as a whole, and + + * search within a string for a substring matching a specified + pattern. + + Some regular expressions match only one string, i.e., the set they +describe has only one member. For example, the regular expression +`foo' matches the string `foo' and no others. Other regular +expressions match more than one string, i.e., the set they describe has +more than one member. For example, the regular expression `f*' matches +the set of strings made up of any number (including zero) of `f's. As +you can see, some characters in regular expressions match themselves +(such as `f') and some don't (such as `*'); the ones that don't match +themselves instead let you specify patterns that describe many +different strings. + + To either match or search for a regular expression with the Regex +library functions, you must first compile it with a Regex pattern +compiling function. A "compiled pattern" is a regular expression +converted to the internal format used by the library functions. Once +you've compiled a pattern, you can use it for matching or searching any +number of times. + + The Regex library consists of two source files: `regex.h' and +`regex.c'. Regex provides three groups of functions with which you can +operate on regular expressions. One group--the GNU group--is more +powerful but not completely compatible with the other two, namely the +POSIX and Berkeley UNIX groups; its interface was designed specifically +for GNU. The other groups have the same interfaces as do the regular +expression functions in POSIX and Berkeley UNIX. + + We wrote this chapter with programmers in mind, not users of +programs--such as Emacs--that use Regex. We describe the Regex library +in its entirety, not how to write regular expressions that a particular +program understands. + + +File: regex.info, Node: Regular Expression Syntax, Next: Common Operators, Prev: Overview, Up: Top + +Regular Expression Syntax +************************* + + "Characters" are things you can type. "Operators" are things in a +regular expression that match one or more characters. You compose +regular expressions from operators, which in turn you specify using one +or more characters. + + Most characters represent what we call the match-self operator, i.e., +they match themselves; we call these characters "ordinary". Other +characters represent either all or parts of fancier operators; e.g., +`.' represents what we call the match-any-character operator (which, no +surprise, matches (almost) any character); we call these characters +"special". Two different things determine what characters represent +what operators: + + 1. the regular expression syntax your program has told the Regex + library to recognize, and + + 2. the context of the character in the regular expression. + + In the following sections, we describe these things in more detail. + +* Menu: + +* Syntax Bits:: +* Predefined Syntaxes:: +* Collating Elements vs. Characters:: +* The Backslash Character:: + + +File: regex.info, Node: Syntax Bits, Next: Predefined Syntaxes, Up: Regular Expression Syntax + +Syntax Bits +=========== + + In any particular syntax for regular expressions, some characters are +always special, others are sometimes special, and others are never +special. The particular syntax that Regex recognizes for a given +regular expression depends on the value in the `syntax' field of the +pattern buffer of that regular expression. + + You get a pattern buffer by compiling a regular expression. *Note +GNU Pattern Buffers::, and *Note POSIX Pattern Buffers::, for more +information on pattern buffers. *Note GNU Regular Expression +Compiling::, *Note POSIX Regular Expression Compiling::, and *Note BSD +Regular Expression Compiling::, for more information on compiling. + + Regex considers the value of the `syntax' field to be a collection of +bits; we refer to these bits as "syntax bits". In most cases, they +affect what characters represent what operators. We describe the +meanings of the operators to which we refer in *Note Common Operators::, +*Note GNU Operators::, and *Note GNU Emacs Operators::. + + For reference, here is the complete list of syntax bits, in +alphabetical order: + +`RE_BACKSLASH_ESCAPE_IN_LISTS' + If this bit is set, then `\' inside a list (*note List Operators::. + quotes (makes ordinary, if it's special) the following character; + if this bit isn't set, then `\' is an ordinary character inside + lists. (*Note The Backslash Character::, for what `\' does + outside of lists.) + +`RE_BK_PLUS_QM' + If this bit is set, then `\+' represents the match-one-or-more + operator and `\?' represents the match-zero-or-more operator; if + this bit isn't set, then `+' represents the match-one-or-more + operator and `?' represents the match-zero-or-one operator. This + bit is irrelevant if `RE_LIMITED_OPS' is set. + +`RE_CHAR_CLASSES' + If this bit is set, then you can use character classes in lists; + if this bit isn't set, then you can't. + +`RE_CONTEXT_INDEP_ANCHORS' + If this bit is set, then `^' and `$' are special anywhere outside + a list; if this bit isn't set, then these characters are special + only in certain contexts. *Note Match-beginning-of-line + Operator::, and *Note Match-end-of-line Operator::. + +`RE_CONTEXT_INDEP_OPS' + If this bit is set, then certain characters are special anywhere + outside a list; if this bit isn't set, then those characters are + special only in some contexts and are ordinary elsewhere. + Specifically, if this bit isn't set then `*', and (if the syntax + bit `RE_LIMITED_OPS' isn't set) `+' and `?' (or `\+' and `\?', + depending on the syntax bit `RE_BK_PLUS_QM') represent repetition + operators only if they're not first in a regular expression or + just after an open-group or alternation operator. The same holds + for `{' (or `\{', depending on the syntax bit `RE_NO_BK_BRACES') if + it is the beginning of a valid interval and the syntax bit + `RE_INTERVALS' is set. + +`RE_CONTEXT_INVALID_OPS' + If this bit is set, then repetition and alternation operators + can't be in certain positions within a regular expression. + Specifically, the regular expression is invalid if it has: + + * a repetition operator first in the regular expression or just + after a match-beginning-of-line, open-group, or alternation + operator; or + + * an alternation operator first or last in the regular + expression, just before a match-end-of-line operator, or just + after an alternation or open-group operator. + + If this bit isn't set, then you can put the characters + representing the repetition and alternation characters anywhere in + a regular expression. Whether or not they will in fact be + operators in certain positions depends on other syntax bits. + +`RE_DOT_NEWLINE' + If this bit is set, then the match-any-character operator matches + a newline; if this bit isn't set, then it doesn't. + +`RE_DOT_NOT_NULL' + If this bit is set, then the match-any-character operator doesn't + match a null character; if this bit isn't set, then it does. + +`RE_INTERVALS' + If this bit is set, then Regex recognizes interval operators; if + this bit isn't set, then it doesn't. + +`RE_LIMITED_OPS' + If this bit is set, then Regex doesn't recognize the + match-one-or-more, match-zero-or-one or alternation operators; if + this bit isn't set, then it does. + +`RE_NEWLINE_ALT' + If this bit is set, then newline represents the alternation + operator; if this bit isn't set, then newline is ordinary. + +`RE_NO_BK_BRACES' + If this bit is set, then `{' represents the open-interval operator + and `}' represents the close-interval operator; if this bit isn't + set, then `\{' represents the open-interval operator and `\}' + represents the close-interval operator. This bit is relevant only + if `RE_INTERVALS' is set. + +`RE_NO_BK_PARENS' + If this bit is set, then `(' represents the open-group operator and + `)' represents the close-group operator; if this bit isn't set, + then `\(' represents the open-group operator and `\)' represents + the close-group operator. + +`RE_NO_BK_REFS' + If this bit is set, then Regex doesn't recognize `\'DIGIT as the + back reference operator; if this bit isn't set, then it does. + +`RE_NO_BK_VBAR' + If this bit is set, then `|' represents the alternation operator; + if this bit isn't set, then `\|' represents the alternation + operator. This bit is irrelevant if `RE_LIMITED_OPS' is set. + +`RE_NO_EMPTY_RANGES' + If this bit is set, then a regular expression with a range whose + ending point collates lower than its starting point is invalid; if + this bit isn't set, then Regex considers such a range to be empty. + +`RE_UNMATCHED_RIGHT_PAREN_ORD' + If this bit is set and the regular expression has no matching + open-group operator, then Regex considers what would otherwise be + a close-group operator (based on how `RE_NO_BK_PARENS' is set) to + match `)'. + + +File: regex.info, Node: Predefined Syntaxes, Next: Collating Elements vs. Characters, Prev: Syntax Bits, Up: Regular Expression Syntax + +Predefined Syntaxes +=================== + + If you're programming with Regex, you can set a pattern buffer's +(*note GNU Pattern Buffers::., and *Note POSIX Pattern Buffers::) +`syntax' field either to an arbitrary combination of syntax bits (*note +Syntax Bits::.) or else to the configurations defined by Regex. These +configurations define the syntaxes used by certain programs--GNU Emacs, +POSIX Awk, traditional Awk, Grep, Egrep--in addition to syntaxes for +POSIX basic and extended regular expressions. + + The predefined syntaxes-taken directly from `regex.h'--are: + + #define RE_SYNTAX_EMACS 0 + + #define RE_SYNTAX_AWK \ + (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \ + | RE_UNMATCHED_RIGHT_PAREN_ORD) + + #define RE_SYNTAX_POSIX_AWK \ + (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS) + + #define RE_SYNTAX_GREP \ + (RE_BK_PLUS_QM | RE_CHAR_CLASSES \ + | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \ + | RE_NEWLINE_ALT) + + #define RE_SYNTAX_EGREP \ + (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \ + | RE_NEWLINE_ALT | RE_NO_BK_PARENS \ + | RE_NO_BK_VBAR) + + #define RE_SYNTAX_POSIX_EGREP \ + (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES) + + /* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */ + #define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC + + #define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC + + /* Syntax bits common to both basic and extended POSIX regex syntax. */ + #define _RE_SYNTAX_POSIX_COMMON \ + (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \ + | RE_INTERVALS | RE_NO_EMPTY_RANGES) + + #define RE_SYNTAX_POSIX_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM) + + /* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes + RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this + isn't minimal, since other operators, such as \`, aren't disabled. */ + #define RE_SYNTAX_POSIX_MINIMAL_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) + + #define RE_SYNTAX_POSIX_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_UNMATCHED_RIGHT_PAREN_ORD) + + /* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS + replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added. */ + #define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) + + +File: regex.info, Node: Collating Elements vs. Characters, Next: The Backslash Character, Prev: Predefined Syntaxes, Up: Regular Expression Syntax + +Collating Elements vs. Characters +================================= + + POSIX generalizes the notion of a character to that of a collating +element. It defines a "collating element" to be "a sequence of one or +more bytes defined in the current collating sequence as a unit of +collation." + + This generalizes the notion of a character in two ways. First, a +single character can map into two or more collating elements. For +example, the German "es-zet" collates as the collating element `s' +followed by another collating element `s'. Second, two or more +characters can map into one collating element. For example, the +Spanish `ll' collates after `l' and before `m'. + + Since POSIX's "collating element" preserves the essential idea of a +"character," we use the latter, more familiar, term in this document. + + +File: regex.info, Node: The Backslash Character, Prev: Collating Elements vs. Characters, Up: Regular Expression Syntax + +The Backslash Character +======================= + + The `\' character has one of four different meanings, depending on +the context in which you use it and what syntax bits are set (*note +Syntax Bits::.). It can: 1) stand for itself, 2) quote the next +character, 3) introduce an operator, or 4) do nothing. + + 1. It stands for itself inside a list (*note List Operators::.) if + the syntax bit `RE_BACKSLASH_ESCAPE_IN_LISTS' is not set. For + example, `[\]' would match `\'. + + 2. It quotes (makes ordinary, if it's special) the next character + when you use it either: + + * outside a list,(1) or + + * inside a list and the syntax bit + `RE_BACKSLASH_ESCAPE_IN_LISTS' is set. + + 3. It introduces an operator when followed by certain ordinary + characters--sometimes only when certain syntax bits are set. See + the cases `RE_BK_PLUS_QM', `RE_NO_BK_BRACES', `RE_NO_BK_VAR', + `RE_NO_BK_PARENS', `RE_NO_BK_REF' in *Note Syntax Bits::. Also: + + * `\b' represents the match-word-boundary operator (*note + Match-word-boundary Operator::.). + + * `\B' represents the match-within-word operator (*note + Match-within-word Operator::.). + + * `\<' represents the match-beginning-of-word operator + (*note Match-beginning-of-word Operator::.). + + * `\>' represents the match-end-of-word operator (*note + Match-end-of-word Operator::.). + + * `\w' represents the match-word-constituent operator (*note + Match-word-constituent Operator::.). + + * `\W' represents the match-non-word-constituent operator + (*note Match-non-word-constituent Operator::.). + + * `\`' represents the match-beginning-of-buffer operator and + `\'' represents the match-end-of-buffer operator (*note + Buffer Operators::.). + + * If Regex was compiled with the C preprocessor symbol `emacs' + defined, then `\sCLASS' represents the match-syntactic-class + operator and `\SCLASS' represents the + match-not-syntactic-class operator (*note Syntactic Class + Operators::.). + + 4. In all other cases, Regex ignores `\'. For example, `\n' matches + `n'. + + + ---------- Footnotes ---------- + + (1) Sometimes you don't have to explicitly quote special characters +to make them ordinary. For instance, most characters lose any special +meaning inside a list (*note List Operators::.). In addition, if the +syntax bits `RE_CONTEXT_INVALID_OPS' and `RE_CONTEXT_INDEP_OPS' aren't +set, then (for historical reasons) the matcher considers special +characters ordinary if they are in contexts where the operations they +represent make no sense; for example, then the match-zero-or-more +operator (represented by `*') matches itself in the regular expression +`*foo' because there is no preceding expression on which it can +operate. It is poor practice, however, to depend on this behavior; if +you want a special character to be ordinary outside a list, it's better +to always quote it, regardless. + + +File: regex.info, Node: Common Operators, Next: GNU Operators, Prev: Regular Expression Syntax, Up: Top + +Common Operators +**************** + + You compose regular expressions from operators. In the following +sections, we describe the regular expression operators specified by +POSIX; GNU also uses these. Most operators have more than one +representation as characters. *Note Regular Expression Syntax::, for +what characters represent what operators under what circumstances. + + For most operators that can be represented in two ways, one +representation is a single character and the other is that character +preceded by `\'. For example, either `(' or `\(' represents the +open-group operator. Which one does depends on the setting of a syntax +bit, in this case `RE_NO_BK_PARENS'. Why is this so? Historical +reasons dictate some of the varying representations, while POSIX +dictates others. + + Finally, almost all characters lose any special meaning inside a list +(*note List Operators::.). + +* Menu: + +* Match-self Operator:: Ordinary characters. +* Match-any-character Operator:: . +* Concatenation Operator:: Juxtaposition. +* Repetition Operators:: * + ? {} +* Alternation Operator:: | +* List Operators:: [...] [^...] +* Grouping Operators:: (...) +* Back-reference Operator:: \digit +* Anchoring Operators:: ^ $ + + +File: regex.info, Node: Match-self Operator, Next: Match-any-character Operator, Up: Common Operators + +The Match-self Operator (ORDINARY CHARACTER) +============================================ + + This operator matches the character itself. All ordinary characters +(*note Regular Expression Syntax::.) represent this operator. For +example, `f' is always an ordinary character, so the regular expression +`f' matches only the string `f'. In particular, it does *not* match +the string `ff'. + + +File: regex.info, Node: Match-any-character Operator, Next: Concatenation Operator, Prev: Match-self Operator, Up: Common Operators + +The Match-any-character Operator (`.') +====================================== + + This operator matches any single printing or nonprinting character +except it won't match a: + +newline + if the syntax bit `RE_DOT_NEWLINE' isn't set. + +null + if the syntax bit `RE_DOT_NOT_NULL' is set. + + The `.' (period) character represents this operator. For example, +`a.b' matches any three-character string beginning with `a' and ending +with `b'. + + +File: regex.info, Node: Concatenation Operator, Next: Repetition Operators, Prev: Match-any-character Operator, Up: Common Operators + +The Concatenation Operator +========================== + + This operator concatenates two regular expressions A and B. No +character represents this operator; you simply put B after A. The +result is a regular expression that will match a string if A matches +its first part and B matches the rest. For example, `xy' (two +match-self operators) matches `xy'. + + +File: regex.info, Node: Repetition Operators, Next: Alternation Operator, Prev: Concatenation Operator, Up: Common Operators + +Repetition Operators +==================== + + Repetition operators repeat the preceding regular expression a +specified number of times. + +* Menu: + +* Match-zero-or-more Operator:: * +* Match-one-or-more Operator:: + +* Match-zero-or-one Operator:: ? +* Interval Operators:: {} + + +File: regex.info, Node: Match-zero-or-more Operator, Next: Match-one-or-more Operator, Up: Repetition Operators + +The Match-zero-or-more Operator (`*') +------------------------------------- + + This operator repeats the smallest possible preceding regular +expression as many times as necessary (including zero) to match the +pattern. `*' represents this operator. For example, `o*' matches any +string made up of zero or more `o's. Since this operator operates on +the smallest preceding regular expression, `fo*' has a repeating `o', +not a repeating `fo'. So, `fo*' matches `f', `fo', `foo', and so on. + + Since the match-zero-or-more operator is a suffix operator, it may be +useless as such when no regular expression precedes it. This is the +case when it: + + * is first in a regular expression, or + + * follows a match-beginning-of-line, open-group, or alternation + operator. + +Three different things can happen in these cases: + + 1. If the syntax bit `RE_CONTEXT_INVALID_OPS' is set, then the + regular expression is invalid. + + 2. If `RE_CONTEXT_INVALID_OPS' isn't set, but `RE_CONTEXT_INDEP_OPS' + is, then `*' represents the match-zero-or-more operator (which + then operates on the empty string). + + 3. Otherwise, `*' is ordinary. + + + The matcher processes a match-zero-or-more operator by first matching +as many repetitions of the smallest preceding regular expression as it +can. Then it continues to match the rest of the pattern. + + If it can't match the rest of the pattern, it backtracks (as many +times as necessary), each time discarding one of the matches until it +can either match the entire pattern or be certain that it cannot get a +match. For example, when matching `ca*ar' against `caaar', the matcher +first matches all three `a's of the string with the `a*' of the regular +expression. However, it cannot then match the final `ar' of the +regular expression against the final `r' of the string. So it +backtracks, discarding the match of the last `a' in the string. It can +then match the remaining `ar'. + + +File: regex.info, Node: Match-one-or-more Operator, Next: Match-zero-or-one Operator, Prev: Match-zero-or-more Operator, Up: Repetition Operators + +The Match-one-or-more Operator (`+' or `\+') +-------------------------------------------- + + If the syntax bit `RE_LIMITED_OPS' is set, then Regex doesn't +recognize this operator. Otherwise, if the syntax bit `RE_BK_PLUS_QM' +isn't set, then `+' represents this operator; if it is, then `\+' does. + + This operator is similar to the match-zero-or-more operator except +that it repeats the preceding regular expression at least once; *note +Match-zero-or-more Operator::., for what it operates on, how some +syntax bits affect it, and how Regex backtracks to match it. + + For example, supposing that `+' represents the match-one-or-more +operator; then `ca+r' matches, e.g., `car' and `caaaar', but not `cr'. + + +File: regex.info, Node: Match-zero-or-one Operator, Next: Interval Operators, Prev: Match-one-or-more Operator, Up: Repetition Operators + +The Match-zero-or-one Operator (`?' or `\?') +-------------------------------------------- + + If the syntax bit `RE_LIMITED_OPS' is set, then Regex doesn't +recognize this operator. Otherwise, if the syntax bit `RE_BK_PLUS_QM' +isn't set, then `?' represents this operator; if it is, then `\?' does. + + This operator is similar to the match-zero-or-more operator except +that it repeats the preceding regular expression once or not at all; +*note Match-zero-or-more Operator::., to see what it operates on, how +some syntax bits affect it, and how Regex backtracks to match it. + + For example, supposing that `?' represents the match-zero-or-one +operator; then `ca?r' matches both `car' and `cr', but nothing else. + + +File: regex.info, Node: Interval Operators, Prev: Match-zero-or-one Operator, Up: Repetition Operators + +Interval Operators (`{' ... `}' or `\{' ... `\}') +------------------------------------------------- + + If the syntax bit `RE_INTERVALS' is set, then Regex recognizes +"interval expressions". They repeat the smallest possible preceding +regular expression a specified number of times. + + If the syntax bit `RE_NO_BK_BRACES' is set, `{' represents the +"open-interval operator" and `}' represents the "close-interval +operator" ; otherwise, `\{' and `\}' do. + + Specifically, supposing that `{' and `}' represent the open-interval +and close-interval operators; then: + +`{COUNT}' + matches exactly COUNT occurrences of the preceding regular + expression. + +`{MIN,}' + matches MIN or more occurrences of the preceding regular + expression. + +`{MIN, MAX}' + matches at least MIN but no more than MAX occurrences of the + preceding regular expression. + + The interval expression (but not necessarily the regular expression +that contains it) is invalid if: + + * MIN is greater than MAX, or + + * any of COUNT, MIN, or MAX are outside the range zero to + `RE_DUP_MAX' (which symbol `regex.h' defines). + + If the interval expression is invalid and the syntax bit +`RE_NO_BK_BRACES' is set, then Regex considers all the characters in +the would-be interval to be ordinary. If that bit isn't set, then the +regular expression is invalid. + + If the interval expression is valid but there is no preceding regular +expression on which to operate, then if the syntax bit +`RE_CONTEXT_INVALID_OPS' is set, the regular expression is invalid. If +that bit isn't set, then Regex considers all the characters--other than +backslashes, which it ignores--in the would-be interval to be ordinary. + + +File: regex.info, Node: Alternation Operator, Next: List Operators, Prev: Repetition Operators, Up: Common Operators + +The Alternation Operator (`|' or `\|') +====================================== + + If the syntax bit `RE_LIMITED_OPS' is set, then Regex doesn't +recognize this operator. Otherwise, if the syntax bit `RE_NO_BK_VBAR' +is set, then `|' represents this operator; otherwise, `\|' does. + + Alternatives match one of a choice of regular expressions: if you put +the character(s) representing the alternation operator between any two +regular expressions A and B, the result matches the union of the +strings that A and B match. For example, supposing that `|' is the +alternation operator, then `foo|bar|quux' would match any of `foo', +`bar' or `quux'. + + The alternation operator operates on the *largest* possible +surrounding regular expressions. (Put another way, it has the lowest +precedence of any regular expression operator.) Thus, the only way you +can delimit its arguments is to use grouping. For example, if `(' and +`)' are the open and close-group operators, then `fo(o|b)ar' would +match either `fooar' or `fobar'. (`foo|bar' would match `foo' or +`bar'.) + + The matcher usually tries all combinations of alternatives so as to +match the longest possible string. For example, when matching +`(fooq|foo)*(qbarquux|bar)' against `fooqbarquux', it cannot take, say, +the first ("depth-first") combination it could match, since then it +would be content to match just `fooqbar'. + + +File: regex.info, Node: List Operators, Next: Grouping Operators, Prev: Alternation Operator, Up: Common Operators + +List Operators (`[' ... `]' and `[^' ... `]') +============================================= + + "Lists", also called "bracket expressions", are a set of one or more +items. An "item" is a character, a character class expression, or a +range expression. The syntax bits affect which kinds of items you can +put in a list. We explain the last two items in subsections below. +Empty lists are invalid. + + A "matching list" matches a single character represented by one of +the list items. You form a matching list by enclosing one or more items +within an "open-matching-list operator" (represented by `[') and a +"close-list operator" (represented by `]'). + + For example, `[ab]' matches either `a' or `b'. `[ad]*' matches the +empty string and any string composed of just `a's and `d's in any +order. Regex considers invalid a regular expression with a `[' but no +matching `]'. + + "Nonmatching lists" are similar to matching lists except that they +match a single character *not* represented by one of the list items. +You use an "open-nonmatching-list operator" (represented by `[^'(1)) +instead of an open-matching-list operator to start a nonmatching list. + + For example, `[^ab]' matches any character except `a' or `b'. + + If the `posix_newline' field in the pattern buffer (*note GNU Pattern +Buffers::. is set, then nonmatching lists do not match a newline. + + Most characters lose any special meaning inside a list. The special +characters inside a list follow. + +`]' + ends the list if it's not the first list item. So, if you want to + make the `]' character a list item, you must put it first. + +`\' + quotes the next character if the syntax bit + `RE_BACKSLASH_ESCAPE_IN_LISTS' is set. + +`[:' + represents the open-character-class operator (*note Character + Class Operators::.) if the syntax bit `RE_CHAR_CLASSES' is set and + what follows is a valid character class expression. + +`:]' + represents the close-character-class operator if the syntax bit + `RE_CHAR_CLASSES' is set and what precedes it is an + open-character-class operator followed by a valid character class + name. + +`-' + represents the range operator (*note Range Operator::.) if it's + not first or last in a list or the ending point of a range. + +All other characters are ordinary. For example, `[.*]' matches `.' and +`*'. + +* Menu: + +* Character Class Operators:: [:class:] +* Range Operator:: start-end + + ---------- Footnotes ---------- + + (1) Regex therefore doesn't consider the `^' to be the first +character in the list. If you put a `^' character first in (what you +think is) a matching list, you'll turn it into a nonmatching list. + + +File: regex.info, Node: Character Class Operators, Next: Range Operator, Up: List Operators + +Character Class Operators (`[:' ... `:]') +----------------------------------------- + + If the syntax bit `RE_CHARACTER_CLASSES' is set, then Regex +recognizes character class expressions inside lists. A "character +class expression" matches one character from a given class. You form a +character class expression by putting a character class name between an +"open-character-class operator" (represented by `[:') and a +"close-character-class operator" (represented by `:]'). The character +class names and their meanings are: + +`alnum' + letters and digits + +`alpha' + letters + +`blank' + system-dependent; for GNU, a space or tab + +`cntrl' + control characters (in the ASCII encoding, code 0177 and codes + less than 040) + +`digit' + digits + +`graph' + same as `print' except omits space + +`lower' + lowercase letters + +`print' + printable characters (in the ASCII encoding, space tilde--codes + 040 through 0176) + +`punct' + neither control nor alphanumeric characters + +`space' + space, carriage return, newline, vertical tab, and form feed + +`upper' + uppercase letters + +`xdigit' + hexadecimal digits: `0'-`9', `a'-`f', `A'-`F' + +These correspond to the definitions in the C library's `' +facility. For example, `[:alpha:]' corresponds to the standard +facility `isalpha'. Regex recognizes character class expressions only +inside of lists; so `[[:alpha:]]' matches any letter, but `[:alpha:]' +outside of a bracket expression and not followed by a repetition +operator matches just itself. + + +File: regex.info, Node: Range Operator, Prev: Character Class Operators, Up: List Operators + +The Range Operator (`-') +------------------------ + + Regex recognizes "range expressions" inside a list. They represent +those characters that fall between two elements in the current +collating sequence. You form a range expression by putting a "range +operator" between two characters.(1) `-' represents the range operator. +For example, `a-f' within a list represents all the characters from `a' +through `f' inclusively. + + If the syntax bit `RE_NO_EMPTY_RANGES' is set, then if the range's +ending point collates less than its starting point, the range (and the +regular expression containing it) is invalid. For example, the regular +expression `[z-a]' would be invalid. If this bit isn't set, then Regex +considers such a range to be empty. + + Since `-' represents the range operator, if you want to make a `-' +character itself a list item, you must do one of the following: + + * Put the `-' either first or last in the list. + + * Include a range whose starting point collates strictly lower than + `-' and whose ending point collates equal or higher. Unless a + range is the first item in a list, a `-' can't be its starting + point, but *can* be its ending point. That is because Regex + considers `-' to be the range operator unless it is preceded by + another `-'. For example, in the ASCII encoding, `)', `*', `+', + `,', `-', `.', and `/' are contiguous characters in the collating + sequence. You might think that `[)-+--/]' has two ranges: `)-+' + and `--/'. Rather, it has the ranges `)-+' and `+--', plus the + character `/', so it matches, e.g., `,', not `.'. + + * Put a range whose starting point is `-' first in the list. + + For example, `[-a-z]' matches a lowercase letter or a hyphen (in +English, in ASCII). + + ---------- Footnotes ---------- + + (1) You can't use a character class for the starting or ending point +of a range, since a character class is not a single character. + + +File: regex.info, Node: Grouping Operators, Next: Back-reference Operator, Prev: List Operators, Up: Common Operators + +Grouping Operators (`(' ... `)' or `\(' ... `\)') +================================================= + + A "group", also known as a "subexpression", consists of an +"open-group operator", any number of other operators, and a +"close-group operator". Regex treats this sequence as a unit, just as +mathematics and programming languages treat a parenthesized expression +as a unit. + + Therefore, using "groups", you can: + + * delimit the argument(s) to an alternation operator (*note + Alternation Operator::.) or a repetition operator (*note + Repetition Operators::.). + + * keep track of the indices of the substring that matched a given + group. *Note Using Registers::, for a precise explanation. This + lets you: + + * use the back-reference operator (*note Back-reference + Operator::.). + + * use registers (*note Using Registers::.). + + If the syntax bit `RE_NO_BK_PARENS' is set, then `(' represents the +open-group operator and `)' represents the close-group operator; +otherwise, `\(' and `\)' do. + + If the syntax bit `RE_UNMATCHED_RIGHT_PAREN_ORD' is set and a +close-group operator has no matching open-group operator, then Regex +considers it to match `)'. + + +File: regex.info, Node: Back-reference Operator, Next: Anchoring Operators, Prev: Grouping Operators, Up: Common Operators + +The Back-reference Operator ("\"DIGIT) +====================================== + + If the syntax bit `RE_NO_BK_REF' isn't set, then Regex recognizes +back references. A back reference matches a specified preceding group. +The back reference operator is represented by `\DIGIT' anywhere after +the end of a regular expression's DIGIT-th group (*note Grouping +Operators::.). + + DIGIT must be between `1' and `9'. The matcher assigns numbers 1 +through 9 to the first nine groups it encounters. By using one of `\1' +through `\9' after the corresponding group's close-group operator, you +can match a substring identical to the one that the group does. + + Back references match according to the following (in all examples +below, `(' represents the open-group, `)' the close-group, `{' the +open-interval and `}' the close-interval operator): + + * If the group matches a substring, the back reference matches an + identical substring. For example, `(a)\1' matches `aa' and + `(bana)na\1bo\1' matches `bananabanabobana'. Likewise, `(.*)\1' + matches any (newline-free if the syntax bit `RE_DOT_NEWLINE' isn't + set) string that is composed of two identical halves; the `(.*)' + matches the first half and the `\1' matches the second half. + + * If the group matches more than once (as it might if followed by, + e.g., a repetition operator), then the back reference matches the + substring the group *last* matched. For example, `((a*)b)*\1\2' + matches `aabababa'; first group 1 (the outer one) matches `aab' + and group 2 (the inner one) matches `aa'. Then group 1 matches + `ab' and group 2 matches `a'. So, `\1' matches `ab' and `\2' + matches `a'. + + * If the group doesn't participate in a match, i.e., it is part of an + alternative not taken or a repetition operator allows zero + repetitions of it, then the back reference makes the whole match + fail. For example, `(one()|two())-and-(three\2|four\3)' matches + `one-and-three' and `two-and-four', but not `one-and-four' or + `two-and-three'. For example, if the pattern matches `one-and-', + then its group 2 matches the empty string and its group 3 doesn't + participate in the match. So, if it then matches `four', then + when it tries to back reference group 3--which it will attempt to + do because `\3' follows the `four'--the match will fail because + group 3 didn't participate in the match. + + You can use a back reference as an argument to a repetition operator. +For example, `(a(b))\2*' matches `a' followed by two or more `b's. +Similarly, `(a(b))\2{3}' matches `abbbb'. + + If there is no preceding DIGIT-th subexpression, the regular +expression is invalid. + + +File: regex.info, Node: Anchoring Operators, Prev: Back-reference Operator, Up: Common Operators + +Anchoring Operators +=================== + + These operators can constrain a pattern to match only at the +beginning or end of the entire string or at the beginning or end of a +line. + +* Menu: + +* Match-beginning-of-line Operator:: ^ +* Match-end-of-line Operator:: $ + + +File: regex.info, Node: Match-beginning-of-line Operator, Next: Match-end-of-line Operator, Up: Anchoring Operators + +The Match-beginning-of-line Operator (`^') +------------------------------------------ + + This operator can match the empty string either at the beginning of +the string or after a newline character. Thus, it is said to "anchor" +the pattern to the beginning of a line. + + In the cases following, `^' represents this operator. (Otherwise, +`^' is ordinary.) + + * It (the `^') is first in the pattern, as in `^foo'. + + * The syntax bit `RE_CONTEXT_INDEP_ANCHORS' is set, and it is outside + a bracket expression. + + * It follows an open-group or alternation operator, as in `a\(^b\)' + and `a\|^b'. *Note Grouping Operators::, and *Note Alternation + Operator::. + + These rules imply that some valid patterns containing `^' cannot be +matched; for example, `foo^bar' if `RE_CONTEXT_INDEP_ANCHORS' is set. + + If the `not_bol' field is set in the pattern buffer (*note GNU +Pattern Buffers::.), then `^' fails to match at the beginning of the +string. *Note POSIX Matching::, for when you might find this useful. + + If the `newline_anchor' field is set in the pattern buffer, then `^' +fails to match after a newline. This is useful when you do not regard +the string to be matched as broken into lines. + + +File: regex.info, Node: Match-end-of-line Operator, Prev: Match-beginning-of-line Operator, Up: Anchoring Operators + +The Match-end-of-line Operator (`$') +------------------------------------ + + This operator can match the empty string either at the end of the +string or before a newline character in the string. Thus, it is said +to "anchor" the pattern to the end of a line. + + It is always represented by `$'. For example, `foo$' usually +matches, e.g., `foo' and, e.g., the first three characters of +`foo\nbar'. + + Its interaction with the syntax bits and pattern buffer fields is +exactly the dual of `^''s; see the previous section. (That is, +"beginning" becomes "end", "next" becomes "previous", and "after" +becomes "before".) + + +File: regex.info, Node: GNU Operators, Next: GNU Emacs Operators, Prev: Common Operators, Up: Top + +GNU Operators +************* + + Following are operators that GNU defines (and POSIX doesn't). + +* Menu: + +* Word Operators:: +* Buffer Operators:: + + +File: regex.info, Node: Word Operators, Next: Buffer Operators, Up: GNU Operators + +Word Operators +============== + + The operators in this section require Regex to recognize parts of +words. Regex uses a syntax table to determine whether or not a +character is part of a word, i.e., whether or not it is +"word-constituent". + +* Menu: + +* Non-Emacs Syntax Tables:: +* Match-word-boundary Operator:: \b +* Match-within-word Operator:: \B +* Match-beginning-of-word Operator:: \< +* Match-end-of-word Operator:: \> +* Match-word-constituent Operator:: \w +* Match-non-word-constituent Operator:: \W + + +File: regex.info, Node: Non-Emacs Syntax Tables, Next: Match-word-boundary Operator, Up: Word Operators + +Non-Emacs Syntax Tables +----------------------- + + A "syntax table" is an array indexed by the characters in your +character set. In the ASCII encoding, therefore, a syntax table has +256 elements. Regex always uses a `char *' variable `re_syntax_table' +as its syntax table. In some cases, it initializes this variable and +in others it expects you to initialize it. + + * If Regex is compiled with the preprocessor symbols `emacs' and + `SYNTAX_TABLE' both undefined, then Regex allocates + `re_syntax_table' and initializes an element I either to `Sword' + (which it defines) if I is a letter, number, or `_', or to zero if + it's not. + + * If Regex is compiled with `emacs' undefined but `SYNTAX_TABLE' + defined, then Regex expects you to define a `char *' variable + `re_syntax_table' to be a valid syntax table. + + * *Note Emacs Syntax Tables::, for what happens when Regex is + compiled with the preprocessor symbol `emacs' defined. + + +File: regex.info, Node: Match-word-boundary Operator, Next: Match-within-word Operator, Prev: Non-Emacs Syntax Tables, Up: Word Operators + +The Match-word-boundary Operator (`\b') +--------------------------------------- + + This operator (represented by `\b') matches the empty string at +either the beginning or the end of a word. For example, `\brat\b' +matches the separate word `rat'. + + +File: regex.info, Node: Match-within-word Operator, Next: Match-beginning-of-word Operator, Prev: Match-word-boundary Operator, Up: Word Operators + +The Match-within-word Operator (`\B') +------------------------------------- + + This operator (represented by `\B') matches the empty string within a +word. For example, `c\Brat\Be' matches `crate', but `dirty \Brat' +doesn't match `dirty rat'. + + +File: regex.info, Node: Match-beginning-of-word Operator, Next: Match-end-of-word Operator, Prev: Match-within-word Operator, Up: Word Operators + +The Match-beginning-of-word Operator (`\<') +------------------------------------------- + + This operator (represented by `\<') matches the empty string at the +beginning of a word. + + +File: regex.info, Node: Match-end-of-word Operator, Next: Match-word-constituent Operator, Prev: Match-beginning-of-word Operator, Up: Word Operators + +The Match-end-of-word Operator (`\>') +------------------------------------- + + This operator (represented by `\>') matches the empty string at the +end of a word. + + +File: regex.info, Node: Match-word-constituent Operator, Next: Match-non-word-constituent Operator, Prev: Match-end-of-word Operator, Up: Word Operators + +The Match-word-constituent Operator (`\w') +------------------------------------------ + + This operator (represented by `\w') matches any word-constituent +character. + + +File: regex.info, Node: Match-non-word-constituent Operator, Prev: Match-word-constituent Operator, Up: Word Operators + +The Match-non-word-constituent Operator (`\W') +---------------------------------------------- + + This operator (represented by `\W') matches any character that is not +word-constituent. + + +File: regex.info, Node: Buffer Operators, Prev: Word Operators, Up: GNU Operators + +Buffer Operators +================ + + Following are operators which work on buffers. In Emacs, a "buffer" +is, naturally, an Emacs buffer. For other programs, Regex considers the +entire string to be matched as the buffer. + +* Menu: + +* Match-beginning-of-buffer Operator:: \` +* Match-end-of-buffer Operator:: \' + + +File: regex.info, Node: Match-beginning-of-buffer Operator, Next: Match-end-of-buffer Operator, Up: Buffer Operators + +The Match-beginning-of-buffer Operator (`\`') +--------------------------------------------- + + This operator (represented by `\`') matches the empty string at the +beginning of the buffer. + + +File: regex.info, Node: Match-end-of-buffer Operator, Prev: Match-beginning-of-buffer Operator, Up: Buffer Operators + +The Match-end-of-buffer Operator (`\'') +--------------------------------------- + + This operator (represented by `\'') matches the empty string at the +end of the buffer. + + +File: regex.info, Node: GNU Emacs Operators, Next: What Gets Matched?, Prev: GNU Operators, Up: Top + +GNU Emacs Operators +******************* + + Following are operators that GNU defines (and POSIX doesn't) that you +can use only when Regex is compiled with the preprocessor symbol +`emacs' defined. + +* Menu: + +* Syntactic Class Operators:: + + +File: regex.info, Node: Syntactic Class Operators, Up: GNU Emacs Operators + +Syntactic Class Operators +========================= + + The operators in this section require Regex to recognize the syntactic +classes of characters. Regex uses a syntax table to determine this. + +* Menu: + +* Emacs Syntax Tables:: +* Match-syntactic-class Operator:: \sCLASS +* Match-not-syntactic-class Operator:: \SCLASS + + +File: regex.info, Node: Emacs Syntax Tables, Next: Match-syntactic-class Operator, Up: Syntactic Class Operators + +Emacs Syntax Tables +------------------- + + A "syntax table" is an array indexed by the characters in your +character set. In the ASCII encoding, therefore, a syntax table has +256 elements. + + If Regex is compiled with the preprocessor symbol `emacs' defined, +then Regex expects you to define and initialize the variable +`re_syntax_table' to be an Emacs syntax table. Emacs' syntax tables +are more complicated than Regex's own (*note Non-Emacs Syntax +Tables::.). *Note Syntax: (emacs)Syntax, for a description of Emacs' +syntax tables. + + +File: regex.info, Node: Match-syntactic-class Operator, Next: Match-not-syntactic-class Operator, Prev: Emacs Syntax Tables, Up: Syntactic Class Operators + +The Match-syntactic-class Operator (`\s'CLASS) +---------------------------------------------- + + This operator matches any character whose syntactic class is +represented by a specified character. `\sCLASS' represents this +operator where CLASS is the character representing the syntactic class +you want. For example, `w' represents the syntactic class of +word-constituent characters, so `\sw' matches any word-constituent +character. + + +File: regex.info, Node: Match-not-syntactic-class Operator, Prev: Match-syntactic-class Operator, Up: Syntactic Class Operators + +The Match-not-syntactic-class Operator (`\S'CLASS) +-------------------------------------------------- + + This operator is similar to the match-syntactic-class operator except +that it matches any character whose syntactic class is *not* +represented by the specified character. `\SCLASS' represents this +operator. For example, `w' represents the syntactic class of +word-constituent characters, so `\Sw' matches any character that is not +word-constituent. + + +File: regex.info, Node: What Gets Matched?, Next: Programming with Regex, Prev: GNU Emacs Operators, Up: Top + +What Gets Matched? +****************** + + Regex usually matches strings according to the "leftmost longest" +rule; that is, it chooses the longest of the leftmost matches. This +does not mean that for a regular expression containing subexpressions +that it simply chooses the longest match for each subexpression, left to +right; the overall match must also be the longest possible one. + + For example, `(ac*)(c*d[ac]*)\1' matches `acdacaaa', not `acdac', as +it would if it were to choose the longest match for the first +subexpression. + + +File: regex.info, Node: Programming with Regex, Next: Copying, Prev: What Gets Matched?, Up: Top + +Programming with Regex +********************** + + Here we describe how you use the Regex data structures and functions +in C programs. Regex has three interfaces: one designed for GNU, one +compatible with POSIX and one compatible with Berkeley UNIX. + +* Menu: + +* GNU Regex Functions:: +* POSIX Regex Functions:: +* BSD Regex Functions:: + + +File: regex.info, Node: GNU Regex Functions, Next: POSIX Regex Functions, Up: Programming with Regex + +GNU Regex Functions +=================== + + If you're writing code that doesn't need to be compatible with either +POSIX or Berkeley UNIX, you can use these functions. They provide more +options than the other interfaces. + +* Menu: + +* GNU Pattern Buffers:: The re_pattern_buffer type. +* GNU Regular Expression Compiling:: re_compile_pattern () +* GNU Matching:: re_match () +* GNU Searching:: re_search () +* Matching/Searching with Split Data:: re_match_2 (), re_search_2 () +* Searching with Fastmaps:: re_compile_fastmap () +* GNU Translate Tables:: The `translate' field. +* Using Registers:: The re_registers type and related fns. +* Freeing GNU Pattern Buffers:: regfree () + + +File: regex.info, Node: GNU Pattern Buffers, Next: GNU Regular Expression Compiling, Up: GNU Regex Functions + +GNU Pattern Buffers +------------------- + + To compile, match, or search for a given regular expression, you must +supply a pattern buffer. A "pattern buffer" holds one compiled regular +expression.(1) + + You can have several different pattern buffers simultaneously, each +holding a compiled pattern for a different regular expression. + + `regex.h' defines the pattern buffer `struct' as follows: + + /* Space that holds the compiled pattern. It is declared as + `unsigned char *' because its elements are + sometimes used as array indexes. */ + unsigned char *buffer; + + /* Number of bytes to which `buffer' points. */ + unsigned long allocated; + + /* Number of bytes actually used in `buffer'. */ + unsigned long used; + + /* Syntax setting with which the pattern was compiled. */ + reg_syntax_t syntax; + + /* Pointer to a fastmap, if any, otherwise zero. re_search uses + the fastmap, if there is one, to skip over impossible + starting points for matches. */ + char *fastmap; + + /* Either a translate table to apply to all characters before + comparing them, or zero for no translation. The translation + is applied to a pattern when it is compiled and to a string + when it is matched. */ + char *translate; + + /* Number of subexpressions found by the compiler. */ + size_t re_nsub; + + /* Zero if this pattern cannot match the empty string, one else. + Well, in truth it's used only in `re_search_2', to see + whether or not we should use the fastmap, so we don't set + this absolutely perfectly; see `re_compile_fastmap' (the + `duplicate' case). */ + unsigned can_be_null : 1; + + /* If REGS_UNALLOCATED, allocate space in the `regs' structure + for `max (RE_NREGS, re_nsub + 1)' groups. + If REGS_REALLOCATE, reallocate space if necessary. + If REGS_FIXED, use what's there. */ + #define REGS_UNALLOCATED 0 + #define REGS_REALLOCATE 1 + #define REGS_FIXED 2 + unsigned regs_allocated : 2; + + /* Set to zero when `regex_compile' compiles a pattern; set to one + by `re_compile_fastmap' if it updates the fastmap. */ + unsigned fastmap_accurate : 1; + + /* If set, `re_match_2' does not return information about + subexpressions. */ + unsigned no_sub : 1; + + /* If set, a beginning-of-line anchor doesn't match at the + beginning of the string. */ + unsigned not_bol : 1; + + /* Similarly for an end-of-line anchor. */ + unsigned not_eol : 1; + + /* If true, an anchor at a newline matches. */ + unsigned newline_anchor : 1; + + ---------- Footnotes ---------- + + (1) Regular expressions are also referred to as "patterns," hence +the name "pattern buffer." + + +File: regex.info, Node: GNU Regular Expression Compiling, Next: GNU Matching, Prev: GNU Pattern Buffers, Up: GNU Regex Functions + +GNU Regular Expression Compiling +-------------------------------- + + In GNU, you can both match and search for a given regular expression. +To do either, you must first compile it in a pattern buffer (*note GNU +Pattern Buffers::.). + + Regular expressions match according to the syntax with which they were +compiled; with GNU, you indicate what syntax you want by setting the +variable `re_syntax_options' (declared in `regex.h' and defined in +`regex.c') before calling the compiling function, `re_compile_pattern' +(see below). *Note Syntax Bits::, and *Note Predefined Syntaxes::. + + You can change the value of `re_syntax_options' at any time. +Usually, however, you set its value once and then never change it. + + `re_compile_pattern' takes a pattern buffer as an argument. You must +initialize the following fields: + +`translate initialization' +`translate' + Initialize this to point to a translate table if you want one, or + to zero if you don't. We explain translate tables in *Note GNU + Translate Tables::. + +`fastmap' + Initialize this to nonzero if you want a fastmap, or to zero if you + don't. + +`buffer' +`allocated' + If you want `re_compile_pattern' to allocate memory for the + compiled pattern, set both of these to zero. If you have an + existing block of memory (allocated with `malloc') you want Regex + to use, set `buffer' to its address and `allocated' to its size (in + bytes). + + `re_compile_pattern' uses `realloc' to extend the space for the + compiled pattern as necessary. + + To compile a pattern buffer, use: + + char * + re_compile_pattern (const char *REGEX, const int REGEX_SIZE, + struct re_pattern_buffer *PATTERN_BUFFER) + +REGEX is the regular expression's address, REGEX_SIZE is its length, +and PATTERN_BUFFER is the pattern buffer's address. + + If `re_compile_pattern' successfully compiles the regular expression, +it returns zero and sets `*PATTERN_BUFFER' to the compiled pattern. It +sets the pattern buffer's fields as follows: + +`buffer' + to the compiled pattern. + +`used' + to the number of bytes the compiled pattern in `buffer' occupies. + +`syntax' + to the current value of `re_syntax_options'. + +`re_nsub' + to the number of subexpressions in REGEX. + +`fastmap_accurate' + to zero on the theory that the pattern you're compiling is + different than the one previously compiled into `buffer'; in that + case (since you can't make a fastmap without a compiled pattern), + `fastmap' would either contain an incompatible fastmap, or nothing + at all. + + If `re_compile_pattern' can't compile REGEX, it returns an error +string corresponding to one of the errors listed in *Note POSIX Regular +Expression Compiling::. + + +File: regex.info, Node: GNU Matching, Next: GNU Searching, Prev: GNU Regular Expression Compiling, Up: GNU Regex Functions + +GNU Matching +------------ + + Matching the GNU way means trying to match as much of a string as +possible starting at a position within it you specify. Once you've +compiled a pattern into a pattern buffer (*note GNU Regular Expression +Compiling::.), you can ask the matcher to match that pattern against a +string using: + + int + re_match (struct re_pattern_buffer *PATTERN_BUFFER, + const char *STRING, const int SIZE, + const int START, struct re_registers *REGS) + +PATTERN_BUFFER is the address of a pattern buffer containing a compiled +pattern. STRING is the string you want to match; it can contain +newline and null characters. SIZE is the length of that string. START +is the string index at which you want to begin matching; the first +character of STRING is at index zero. *Note Using Registers::, for a +explanation of REGS; you can safely pass zero. + + `re_match' matches the regular expression in PATTERN_BUFFER against +the string STRING according to the syntax in PATTERN_BUFFERS's `syntax' +field. (*Note GNU Regular Expression Compiling::, for how to set it.) +The function returns -1 if the compiled pattern does not match any part +of STRING and -2 if an internal error happens; otherwise, it returns +how many (possibly zero) characters of STRING the pattern matched. + + An example: suppose PATTERN_BUFFER points to a pattern buffer +containing the compiled pattern for `a*', and STRING points to `aaaaab' +(whereupon SIZE should be 6). Then if START is 2, `re_match' returns 3, +i.e., `a*' would have matched the last three `a's in STRING. If START +is 0, `re_match' returns 5, i.e., `a*' would have matched all the `a's +in STRING. If START is either 5 or 6, it returns zero. + + If START is not between zero and SIZE, then `re_match' returns -1. + + +File: regex.info, Node: GNU Searching, Next: Matching/Searching with Split Data, Prev: GNU Matching, Up: GNU Regex Functions + +GNU Searching +------------- + + "Searching" means trying to match starting at successive positions +within a string. The function `re_search' does this. + + Before calling `re_search', you must compile your regular expression. +*Note GNU Regular Expression Compiling::. + + Here is the function declaration: + + int + re_search (struct re_pattern_buffer *PATTERN_BUFFER, + const char *STRING, const int SIZE, + const int START, const int RANGE, + struct re_registers *REGS) + +whose arguments are the same as those to `re_match' (*note GNU +Matching::.) except that the two arguments START and RANGE replace +`re_match''s argument START. + + If RANGE is positive, then `re_search' attempts a match starting +first at index START, then at START + 1 if that fails, and so on, up to +START + RANGE; if RANGE is negative, then it attempts a match starting +first at index START, then at START -1 if that fails, and so on. + + If START is not between zero and SIZE, then `re_search' returns -1. +When RANGE is positive, `re_search' adjusts RANGE so that START + RANGE +- 1 is between zero and SIZE, if necessary; that way it won't search +outside of STRING. Similarly, when RANGE is negative, `re_search' +adjusts RANGE so that START + RANGE + 1 is between zero and SIZE, if +necessary. + + If the `fastmap' field of PATTERN_BUFFER is zero, `re_search' matches +starting at consecutive positions; otherwise, it uses `fastmap' to make +the search more efficient. *Note Searching with Fastmaps::. + + If no match is found, `re_search' returns -1. If a match is found, +it returns the index where the match began. If an internal error +happens, it returns -2. + + +File: regex.info, Node: Matching/Searching with Split Data, Next: Searching with Fastmaps, Prev: GNU Searching, Up: GNU Regex Functions + +Matching and Searching with Split Data +-------------------------------------- + + Using the functions `re_match_2' and `re_search_2', you can match or +search in data that is divided into two strings. + + The function: + + int + re_match_2 (struct re_pattern_buffer *BUFFER, + const char *STRING1, const int SIZE1, + const char *STRING2, const int SIZE2, + const int START, + struct re_registers *REGS, + const int STOP) + +is similar to `re_match' (*note GNU Matching::.) except that you pass +*two* data strings and sizes, and an index STOP beyond which you don't +want the matcher to try matching. As with `re_match', if it succeeds, +`re_match_2' returns how many characters of STRING it matched. Regard +STRING1 and STRING2 as concatenated when you set the arguments START and +STOP and use the contents of REGS; `re_match_2' never returns a value +larger than SIZE1 + SIZE2. + + The function: + + int + re_search_2 (struct re_pattern_buffer *BUFFER, + const char *STRING1, const int SIZE1, + const char *STRING2, const int SIZE2, + const int START, const int RANGE, + struct re_registers *REGS, + const int STOP) + +is similarly related to `re_search'. + + +File: regex.info, Node: Searching with Fastmaps, Next: GNU Translate Tables, Prev: Matching/Searching with Split Data, Up: GNU Regex Functions + +Searching with Fastmaps +----------------------- + + If you're searching through a long string, you should use a fastmap. +Without one, the searcher tries to match at consecutive positions in the +string. Generally, most of the characters in the string could not start +a match. It takes much longer to try matching at a given position in +the string than it does to check in a table whether or not the +character at that position could start a match. A "fastmap" is such a +table. + + More specifically, a fastmap is an array indexed by the characters in +your character set. Under the ASCII encoding, therefore, a fastmap has +256 elements. If you want the searcher to use a fastmap with a given +pattern buffer, you must allocate the array and assign the array's +address to the pattern buffer's `fastmap' field. You either can +compile the fastmap yourself or have `re_search' do it for you; when +`fastmap' is nonzero, it automatically compiles a fastmap the first +time you search using a particular compiled pattern. + + To compile a fastmap yourself, use: + + int + re_compile_fastmap (struct re_pattern_buffer *PATTERN_BUFFER) + +PATTERN_BUFFER is the address of a pattern buffer. If the character C +could start a match for the pattern, `re_compile_fastmap' makes +`PATTERN_BUFFER->fastmap[C]' nonzero. It returns 0 if it can compile a +fastmap and -2 if there is an internal error. For example, if `|' is +the alternation operator and PATTERN_BUFFER holds the compiled pattern +for `a|b', then `re_compile_fastmap' sets `fastmap['a']' and +`fastmap['b']' (and no others). + + `re_search' uses a fastmap as it moves along in the string: it checks +the string's characters until it finds one that's in the fastmap. Then +it tries matching at that character. If the match fails, it repeats +the process. So, by using a fastmap, `re_search' doesn't waste time +trying to match at positions in the string that couldn't start a match. + + If you don't want `re_search' to use a fastmap, store zero in the +`fastmap' field of the pattern buffer before calling `re_search'. + + Once you've initialized a pattern buffer's `fastmap' field, you need +never do so again--even if you compile a new pattern in it--provided +the way the field is set still reflects whether or not you want a +fastmap. `re_search' will still either do nothing if `fastmap' is null +or, if it isn't, compile a new fastmap for the new pattern. + + +File: regex.info, Node: GNU Translate Tables, Next: Using Registers, Prev: Searching with Fastmaps, Up: GNU Regex Functions + +GNU Translate Tables +-------------------- + + If you set the `translate' field of a pattern buffer to a translate +table, then the GNU Regex functions to which you've passed that pattern +buffer use it to apply a simple transformation to all the regular +expression and string characters at which they look. + + A "translate table" is an array indexed by the characters in your +character set. Under the ASCII encoding, therefore, a translate table +has 256 elements. The array's elements are also characters in your +character set. When the Regex functions see a character C, they use +`translate[C]' in its place, with one exception: the character after a +`\' is not translated. (This ensures that, the operators, e.g., `\B' +and `\b', are always distinguishable.) + + For example, a table that maps all lowercase letters to the +corresponding uppercase ones would cause the matcher to ignore +differences in case.(1) Such a table would map all characters except +lowercase letters to themselves, and lowercase letters to the +corresponding uppercase ones. Under the ASCII encoding, here's how you +could initialize such a table (we'll call it `case_fold'): + + for (i = 0; i < 256; i++) + case_fold[i] = i; + for (i = 'a'; i <= 'z'; i++) + case_fold[i] = i - ('a' - 'A'); + + You tell Regex to use a translate table on a given pattern buffer by +assigning that table's address to the `translate' field of that buffer. +If you don't want Regex to do any translation, put zero into this +field. You'll get weird results if you change the table's contents +anytime between compiling the pattern buffer, compiling its fastmap, and +matching or searching with the pattern buffer. + + ---------- Footnotes ---------- + + (1) A table that maps all uppercase letters to the corresponding +lowercase ones would work just as well for this purpose. + + +File: regex.info, Node: Using Registers, Next: Freeing GNU Pattern Buffers, Prev: GNU Translate Tables, Up: GNU Regex Functions + +Using Registers +--------------- + + A group in a regular expression can match a (posssibly empty) +substring of the string that regular expression as a whole matched. +The matcher remembers the beginning and end of the substring matched by +each group. + + To find out what they matched, pass a nonzero REGS argument to a GNU +matching or searching function (*note GNU Matching::. and *Note GNU +Searching::), i.e., the address of a structure of this type, as defined +in `regex.h': + + struct re_registers + { + unsigned num_regs; + regoff_t *start; + regoff_t *end; + }; + + Except for (possibly) the NUM_REGS'th element (see below), the Ith +element of the `start' and `end' arrays records information about the +Ith group in the pattern. (They're declared as C pointers, but this is +only because not all C compilers accept zero-length arrays; +conceptually, it is simplest to think of them as arrays.) + + The `start' and `end' arrays are allocated in various ways, depending +on the value of the `regs_allocated' field in the pattern buffer passed +to the matcher. + + The simplest and perhaps most useful is to let the matcher +(re)allocate enough space to record information for all the groups in +the regular expression. If `regs_allocated' is `REGS_UNALLOCATED', the +matcher allocates 1 + RE_NSUB (another field in the pattern buffer; +*note GNU Pattern Buffers::.). The extra element is set to -1, and +sets `regs_allocated' to `REGS_REALLOCATE'. Then on subsequent calls +with the same pattern buffer and REGS arguments, the matcher +reallocates more space if necessary. + + It would perhaps be more logical to make the `regs_allocated' field +part of the `re_registers' structure, instead of part of the pattern +buffer. But in that case the caller would be forced to initialize the +structure before passing it. Much existing code doesn't do this +initialization, and it's arguably better to avoid it anyway. + + `re_compile_pattern' sets `regs_allocated' to `REGS_UNALLOCATED', so +if you use the GNU regular expression functions, you get this behavior +by default. + + xx document re_set_registers + + POSIX, on the other hand, requires a different interface: the caller +is supposed to pass in a fixed-length array which the matcher fills. +Therefore, if `regs_allocated' is `REGS_FIXED' the matcher simply fills +that array. + + The following examples illustrate the information recorded in the +`re_registers' structure. (In all of them, `(' represents the +open-group and `)' the close-group operator. The first character in +the string STRING is at index 0.) + + * If the regular expression has an I-th group not contained within + another group that matches a substring of STRING, then the + function sets `REGS->start[I]' to the index in STRING where the + substring matched by the I-th group begins, and `REGS->end[I]' to + the index just beyond that substring's end. The function sets + `REGS->start[0]' and `REGS->end[0]' to analogous information about + the entire pattern. + + For example, when you match `((a)(b))' against `ab', you get: + + * 0 in `REGS->start[0]' and 2 in `REGS->end[0]' + + * 0 in `REGS->start[1]' and 2 in `REGS->end[1]' + + * 0 in `REGS->start[2]' and 1 in `REGS->end[2]' + + * 1 in `REGS->start[3]' and 2 in `REGS->end[3]' + + * If a group matches more than once (as it might if followed by, + e.g., a repetition operator), then the function reports the + information about what the group *last* matched. + + For example, when you match the pattern `(a)*' against the string + `aa', you get: + + * 0 in `REGS->start[0]' and 2 in `REGS->end[0]' + + * 1 in `REGS->start[1]' and 2 in `REGS->end[1]' + + * If the I-th group does not participate in a successful match, + e.g., it is an alternative not taken or a repetition operator + allows zero repetitions of it, then the function sets + `REGS->start[I]' and `REGS->end[I]' to -1. + + For example, when you match the pattern `(a)*b' against the string + `b', you get: + + * 0 in `REGS->start[0]' and 1 in `REGS->end[0]' + + * -1 in `REGS->start[1]' and -1 in `REGS->end[1]' + + * If the I-th group matches a zero-length string, then the function + sets `REGS->start[I]' and `REGS->end[I]' to the index just beyond + that zero-length string. + + For example, when you match the pattern `(a*)b' against the string + `b', you get: + + * 0 in `REGS->start[0]' and 1 in `REGS->end[0]' + + * 0 in `REGS->start[1]' and 0 in `REGS->end[1]' + + * If an I-th group contains a J-th group in turn not contained + within any other group within group I and the function reports a + match of the I-th group, then it records in `REGS->start[J]' and + `REGS->end[J]' the last match (if it matched) of the J-th group. + + For example, when you match the pattern `((a*)b)*' against the + string `abb', group 2 last matches the empty string, so you get + what it previously matched: + + * 0 in `REGS->start[0]' and 3 in `REGS->end[0]' + + * 2 in `REGS->start[1]' and 3 in `REGS->end[1]' + + * 2 in `REGS->start[2]' and 2 in `REGS->end[2]' + + When you match the pattern `((a)*b)*' against the string `abb', + group 2 doesn't participate in the last match, so you get: + + * 0 in `REGS->start[0]' and 3 in `REGS->end[0]' + + * 2 in `REGS->start[1]' and 3 in `REGS->end[1]' + + * 0 in `REGS->start[2]' and 1 in `REGS->end[2]' + + * If an I-th group contains a J-th group in turn not contained + within any other group within group I and the function sets + `REGS->start[I]' and `REGS->end[I]' to -1, then it also sets + `REGS->start[J]' and `REGS->end[J]' to -1. + + For example, when you match the pattern `((a)*b)*c' against the + string `c', you get: + + * 0 in `REGS->start[0]' and 1 in `REGS->end[0]' + + * -1 in `REGS->start[1]' and -1 in `REGS->end[1]' + + * -1 in `REGS->start[2]' and -1 in `REGS->end[2]' + + +File: regex.info, Node: Freeing GNU Pattern Buffers, Prev: Using Registers, Up: GNU Regex Functions + +Freeing GNU Pattern Buffers +--------------------------- + + To free any allocated fields of a pattern buffer, you can use the +POSIX function described in *Note Freeing POSIX Pattern Buffers::, +since the type `regex_t'--the type for POSIX pattern buffers--is +equivalent to the type `re_pattern_buffer'. After freeing a pattern +buffer, you need to again compile a regular expression in it (*note GNU +Regular Expression Compiling::.) before passing it to a matching or +searching function. + + +File: regex.info, Node: POSIX Regex Functions, Next: BSD Regex Functions, Prev: GNU Regex Functions, Up: Programming with Regex + +POSIX Regex Functions +===================== + + If you're writing code that has to be POSIX compatible, you'll need +to use these functions. Their interfaces are as specified by POSIX, +draft 1003.2/D11.2. + +* Menu: + +* POSIX Pattern Buffers:: The regex_t type. +* POSIX Regular Expression Compiling:: regcomp () +* POSIX Matching:: regexec () +* Reporting Errors:: regerror () +* Using Byte Offsets:: The regmatch_t type. +* Freeing POSIX Pattern Buffers:: regfree () + + +File: regex.info, Node: POSIX Pattern Buffers, Next: POSIX Regular Expression Compiling, Up: POSIX Regex Functions + +POSIX Pattern Buffers +--------------------- + + To compile or match a given regular expression the POSIX way, you +must supply a pattern buffer exactly the way you do for GNU (*note GNU +Pattern Buffers::.). POSIX pattern buffers have type `regex_t', which +is equivalent to the GNU pattern buffer type `re_pattern_buffer'. + + +File: regex.info, Node: POSIX Regular Expression Compiling, Next: POSIX Matching, Prev: POSIX Pattern Buffers, Up: POSIX Regex Functions + +POSIX Regular Expression Compiling +---------------------------------- + + With POSIX, you can only search for a given regular expression; you +can't match it. To do this, you must first compile it in a pattern +buffer, using `regcomp'. + + To compile a pattern buffer, use: + + int + regcomp (regex_t *PREG, const char *REGEX, int CFLAGS) + +PREG is the initialized pattern buffer's address, REGEX is the regular +expression's address, and CFLAGS is the compilation flags, which Regex +considers as a collection of bits. Here are the valid bits, as defined +in `regex.h': + +`REG_EXTENDED' + says to use POSIX Extended Regular Expression syntax; if this isn't + set, then says to use POSIX Basic Regular Expression syntax. + `regcomp' sets PREG's `syntax' field accordingly. + +`REG_ICASE' + says to ignore case; `regcomp' sets PREG's `translate' field to a + translate table which ignores case, replacing anything you've put + there before. + +`REG_NOSUB' + says to set PREG's `no_sub' field; *note POSIX Matching::., for + what this means. + +`REG_NEWLINE' + says that a: + + * match-any-character operator (*note Match-any-character + Operator::.) doesn't match a newline. + + * nonmatching list not containing a newline (*note List + Operators::.) matches a newline. + + * match-beginning-of-line operator (*note + Match-beginning-of-line Operator::.) matches the empty string + immediately after a newline, regardless of how `REG_NOTBOL' + is set (*note POSIX Matching::., for an explanation of + `REG_NOTBOL'). + + * match-end-of-line operator (*note Match-beginning-of-line + Operator::.) matches the empty string immediately before a + newline, regardless of how `REG_NOTEOL' is set (*note POSIX + Matching::., for an explanation of `REG_NOTEOL'). + + If `regcomp' successfully compiles the regular expression, it returns +zero and sets `*PATTERN_BUFFER' to the compiled pattern. Except for +`syntax' (which it sets as explained above), it also sets the same +fields the same way as does the GNU compiling function (*note GNU +Regular Expression Compiling::.). + + If `regcomp' can't compile the regular expression, it returns one of +the error codes listed here. (Except when noted differently, the +syntax of in all examples below is basic regular expression syntax.) + +`REG_BADRPT' + For example, the consecutive repetition operators `**' in `a**' + are invalid. As another example, if the syntax is extended + regular expression syntax, then the repetition operator `*' with + nothing on which to operate in `*' is invalid. + +`REG_BADBR' + For example, the COUNT `-1' in `a\{-1' is invalid. + +`REG_EBRACE' + For example, `a\{1' is missing a close-interval operator. + +`REG_EBRACK' + For example, `[a' is missing a close-list operator. + +`REG_ERANGE' + For example, the range ending point `z' that collates lower than + does its starting point `a' in `[z-a]' is invalid. Also, the + range with the character class `[:alpha:]' as its starting point in + `[[:alpha:]-|]'. + +`REG_ECTYPE' + For example, the character class name `foo' in `[[:foo:]' is + invalid. + +`REG_EPAREN' + For example, `a\)' is missing an open-group operator and `\(a' is + missing a close-group operator. + +`REG_ESUBREG' + For example, the back reference `\2' that refers to a nonexistent + subexpression in `\(a\)\2' is invalid. + +`REG_EEND' + Returned when a regular expression causes no other more specific + error. + +`REG_EESCAPE' + For example, the trailing backslash `\' in `a\' is invalid, as is + the one in `\'. + +`REG_BADPAT' + For example, in the extended regular expression syntax, the empty + group `()' in `a()b' is invalid. + +`REG_ESIZE' + Returned when a regular expression needs a pattern buffer larger + than 65536 bytes. + +`REG_ESPACE' + Returned when a regular expression makes Regex to run out of + memory. + + +File: regex.info, Node: POSIX Matching, Next: Reporting Errors, Prev: POSIX Regular Expression Compiling, Up: POSIX Regex Functions + +POSIX Matching +-------------- + + Matching the POSIX way means trying to match a null-terminated string +starting at its first character. Once you've compiled a pattern into a +pattern buffer (*note POSIX Regular Expression Compiling::.), you can +ask the matcher to match that pattern against a string using: + + int + regexec (const regex_t *PREG, const char *STRING, + size_t NMATCH, regmatch_t PMATCH[], int EFLAGS) + +PREG is the address of a pattern buffer for a compiled pattern. STRING +is the string you want to match. + + *Note Using Byte Offsets::, for an explanation of PMATCH. If you +pass zero for NMATCH or you compiled PREG with the compilation flag +`REG_NOSUB' set, then `regexec' will ignore PMATCH; otherwise, you must +allocate it to have at least NMATCH elements. `regexec' will record +NMATCH byte offsets in PMATCH, and set to -1 any unused elements up to +PMATCH`[NMATCH]' - 1. + + EFLAGS specifies "execution flags"--namely, the two bits `REG_NOTBOL' +and `REG_NOTEOL' (defined in `regex.h'). If you set `REG_NOTBOL', then +the match-beginning-of-line operator (*note Match-beginning-of-line +Operator::.) always fails to match. This lets you match against pieces +of a line, as you would need to if, say, searching for repeated +instances of a given pattern in a line; it would work correctly for +patterns both with and without match-beginning-of-line operators. +`REG_NOTEOL' works analogously for the match-end-of-line operator +(*note Match-end-of-line Operator::.); it exists for symmetry. + + `regexec' tries to find a match for PREG in STRING according to the +syntax in PREG's `syntax' field. (*Note POSIX Regular Expression +Compiling::, for how to set it.) The function returns zero if the +compiled pattern matches STRING and `REG_NOMATCH' (defined in +`regex.h') if it doesn't. + + +File: regex.info, Node: Reporting Errors, Next: Using Byte Offsets, Prev: POSIX Matching, Up: POSIX Regex Functions + +Reporting Errors +---------------- + + If either `regcomp' or `regexec' fail, they return a nonzero error +code, the possibilities for which are defined in `regex.h'. *Note +POSIX Regular Expression Compiling::, and *Note POSIX Matching::, for +what these codes mean. To get an error string corresponding to these +codes, you can use: + + size_t + regerror (int ERRCODE, + const regex_t *PREG, + char *ERRBUF, + size_t ERRBUF_SIZE) + +ERRCODE is an error code, PREG is the address of the pattern buffer +which provoked the error, ERRBUF is the error buffer, and ERRBUF_SIZE +is ERRBUF's size. + + `regerror' returns the size in bytes of the error string +corresponding to ERRCODE (including its terminating null). If ERRBUF +and ERRBUF_SIZE are nonzero, it also returns in ERRBUF the first +ERRBUF_SIZE - 1 characters of the error string, followed by a null. +eRRBUF_SIZE must be a nonnegative number less than or equal to the size +in bytes of ERRBUF. + + You can call `regerror' with a null ERRBUF and a zero ERRBUF_SIZE to +determine how large ERRBUF need be to accommodate `regerror''s error +string. + + +File: regex.info, Node: Using Byte Offsets, Next: Freeing POSIX Pattern Buffers, Prev: Reporting Errors, Up: POSIX Regex Functions + +Using Byte Offsets +------------------ + + In POSIX, variables of type `regmatch_t' hold analogous information, +but are not identical to, GNU's registers (*note Using Registers::.). +To get information about registers in POSIX, pass to `regexec' a +nonzero PMATCH of type `regmatch_t', i.e., the address of a structure +of this type, defined in `regex.h': + + typedef struct + { + regoff_t rm_so; + regoff_t rm_eo; + } regmatch_t; + + When reading in *Note Using Registers::, about how the matching +function stores the information into the registers, substitute PMATCH +for REGS, `PMATCH[I]->rm_so' for `REGS->start[I]' and +`PMATCH[I]->rm_eo' for `REGS->end[I]'. + + +File: regex.info, Node: Freeing POSIX Pattern Buffers, Prev: Using Byte Offsets, Up: POSIX Regex Functions + +Freeing POSIX Pattern Buffers +----------------------------- + + To free any allocated fields of a pattern buffer, use: + + void + regfree (regex_t *PREG) + +PREG is the pattern buffer whose allocated fields you want freed. +`regfree' also sets PREG's `allocated' and `used' fields to zero. +After freeing a pattern buffer, you need to again compile a regular +expression in it (*note POSIX Regular Expression Compiling::.) before +passing it to the matching function (*note POSIX Matching::.). + + +File: regex.info, Node: BSD Regex Functions, Prev: POSIX Regex Functions, Up: Programming with Regex + +BSD Regex Functions +=================== + + If you're writing code that has to be Berkeley UNIX compatible, +you'll need to use these functions whose interfaces are the same as +those in Berkeley UNIX. + +* Menu: + +* BSD Regular Expression Compiling:: re_comp () +* BSD Searching:: re_exec () + + +File: regex.info, Node: BSD Regular Expression Compiling, Next: BSD Searching, Up: BSD Regex Functions + +BSD Regular Expression Compiling +-------------------------------- + + With Berkeley UNIX, you can only search for a given regular +expression; you can't match one. To search for it, you must first +compile it. Before you compile it, you must indicate the regular +expression syntax you want it compiled according to by setting the +variable `re_syntax_options' (declared in `regex.h' to some syntax +(*note Regular Expression Syntax::.). + + To compile a regular expression use: + + char * + re_comp (char *REGEX) + +REGEX is the address of a null-terminated regular expression. +`re_comp' uses an internal pattern buffer, so you can use only the most +recently compiled pattern buffer. This means that if you want to use a +given regular expression that you've already compiled--but it isn't the +latest one you've compiled--you'll have to recompile it. If you call +`re_comp' with the null string (*not* the empty string) as the +argument, it doesn't change the contents of the pattern buffer. + + If `re_comp' successfully compiles the regular expression, it returns +zero. If it can't compile the regular expression, it returns an error +string. `re_comp''s error messages are identical to those of +`re_compile_pattern' (*note GNU Regular Expression Compiling::.). + + +File: regex.info, Node: BSD Searching, Prev: BSD Regular Expression Compiling, Up: BSD Regex Functions + +BSD Searching +------------- + + Searching the Berkeley UNIX way means searching in a string starting +at its first character and trying successive positions within it to +find a match. Once you've compiled a pattern using `re_comp' (*note +BSD Regular Expression Compiling::.), you can ask Regex to search for +that pattern in a string using: + + int + re_exec (char *STRING) + +STRING is the address of the null-terminated string in which you want +to search. + + `re_exec' returns either 1 for success or 0 for failure. It +automatically uses a GNU fastmap (*note Searching with Fastmaps::.). + + +File: regex.info, Node: Copying, Next: Index, Prev: Programming with Regex, Up: Top + +GNU GENERAL PUBLIC LICENSE +************************** + + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +Preamble +======== + + The licenses for most software are designed to take away your freedom +to share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it in +new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 1. This License applies to any program or other work which contains a + notice placed by the copyright holder saying it may be distributed + under the terms of this General Public License. The "Program", + below, refers to any such program or work, and a "work based on + the Program" means either the Program or any derivative work under + copyright law: that is to say, a work containing the Program or a + portion of it, either verbatim or with modifications and/or + translated into another language. (Hereinafter, translation is + included without limitation in the term "modification".) Each + licensee is addressed as "you". + + Activities other than copying, distribution and modification are + not covered by this License; they are outside its scope. The act + of running the Program is not restricted, and the output from the + Program is covered only if its contents constitute a work based on + the Program (independent of having been made by running the + Program). Whether that is true depends on what the Program does. + + 2. You may copy and distribute verbatim copies of the Program's + source code as you receive it, in any medium, provided that you + conspicuously and appropriately publish on each copy an appropriate + copyright notice and disclaimer of warranty; keep intact all the + notices that refer to this License and to the absence of any + warranty; and give any other recipients of the Program a copy of + this License along with the Program. + + You may charge a fee for the physical act of transferring a copy, + and you may at your option offer warranty protection in exchange + for a fee. + + 3. You may modify your copy or copies of the Program or any portion + of it, thus forming a work based on the Program, and copy and + distribute such modifications or work under the terms of Section 1 + above, provided that you also meet all of these conditions: + + a. You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b. You must cause any work that you distribute or publish, that + in whole or in part contains or is derived from the Program + or any part thereof, to be licensed as a whole at no charge + to all third parties under the terms of this License. + + c. If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display + an announcement including an appropriate copyright notice and + a notice that there is no warranty (or else, saying that you + provide a warranty) and that users may redistribute the + program under these conditions, and telling the user how to + view a copy of this License. (Exception: if the Program + itself is interactive but does not normally print such an + announcement, your work based on the Program is not required + to print an announcement.) + + These requirements apply to the modified work as a whole. If + identifiable sections of that work are not derived from the + Program, and can be reasonably considered independent and separate + works in themselves, then this License, and its terms, do not + apply to those sections when you distribute them as separate + works. But when you distribute the same sections as part of a + whole which is a work based on the Program, the distribution of + the whole must be on the terms of this License, whose permissions + for other licensees extend to the entire whole, and thus to each + and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or + contest your rights to work written entirely by you; rather, the + intent is to exercise the right to control the distribution of + derivative or collective works based on the Program. + + In addition, mere aggregation of another work not based on the + Program with the Program (or with a work based on the Program) on + a volume of a storage or distribution medium does not bring the + other work under the scope of this License. + + 4. You may copy and distribute the Program (or a work based on it, + under Section 2) in object code or executable form under the terms + of Sections 1 and 2 above provided that you also do one of the + following: + + a. Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Sections 1 and 2 above on a medium customarily used for + software interchange; or, + + b. Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a + medium customarily used for software interchange; or, + + c. Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with + such an offer, in accord with Subsection b above.) + + The source code for a work means the preferred form of the work for + making modifications to it. For an executable work, complete + source code means all the source code for all modules it contains, + plus any associated interface definition files, plus the scripts + used to control compilation and installation of the executable. + However, as a special exception, the source code distributed need + not include anything that is normally distributed (in either + source or binary form) with the major components (compiler, + kernel, and so on) of the operating system on which the executable + runs, unless that component itself accompanies the executable. + + If distribution of executable or object code is made by offering + access to copy from a designated place, then offering equivalent + access to copy the source code from the same place counts as + distribution of the source code, even though third parties are not + compelled to copy the source along with the object code. + + 5. You may not copy, modify, sublicense, or distribute the Program + except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense or distribute the Program is + void, and will automatically terminate your rights under this + License. However, parties who have received copies, or rights, + from you under this License will not have their licenses + terminated so long as such parties remain in full compliance. + + 6. You are not required to accept this License, since you have not + signed it. However, nothing else grants you permission to modify + or distribute the Program or its derivative works. These actions + are prohibited by law if you do not accept this License. + Therefore, by modifying or distributing the Program (or any work + based on the Program), you indicate your acceptance of this + License to do so, and all its terms and conditions for copying, + distributing or modifying the Program or works based on it. + + 7. Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the + original licensor to copy, distribute or modify the Program + subject to these terms and conditions. You may not impose any + further restrictions on the recipients' exercise of the rights + granted herein. You are not responsible for enforcing compliance + by third parties to this License. + + 8. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent + issues), conditions are imposed on you (whether by court order, + agreement or otherwise) that contradict the conditions of this + License, they do not excuse you from the conditions of this + License. If you cannot distribute so as to satisfy simultaneously + your obligations under this License and any other pertinent + obligations, then as a consequence you may not distribute the + Program at all. For example, if a patent license would not permit + royalty-free redistribution of the Program by all those who + receive copies directly or indirectly through you, then the only + way you could satisfy both it and this License would be to refrain + entirely from distribution of the Program. + + If any portion of this section is held invalid or unenforceable + under any particular circumstance, the balance of the section is + intended to apply and the section as a whole is intended to apply + in other circumstances. + + It is not the purpose of this section to induce you to infringe any + patents or other property right claims or to contest validity of + any such claims; this section has the sole purpose of protecting + the integrity of the free software distribution system, which is + implemented by public license practices. Many people have made + generous contributions to the wide range of software distributed + through that system in reliance on consistent application of that + system; it is up to the author/donor to decide if he or she is + willing to distribute software through any other system and a + licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed + to be a consequence of the rest of this License. + + 9. If the distribution and/or use of the Program is restricted in + certain countries either by patents or by copyrighted interfaces, + the original copyright holder who places the Program under this + License may add an explicit geographical distribution limitation + excluding those countries, so that distribution is permitted only + in or among countries not thus excluded. In such case, this + License incorporates the limitation as if written in the body of + this License. + + 10. The Free Software Foundation may publish revised and/or new + versions of the General Public License from time to time. Such + new versions will be similar in spirit to the present version, but + may differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the + Program specifies a version number of this License which applies + to it and "any later version", you have the option of following + the terms and conditions either of that version or of any later + version published by the Free Software Foundation. If the Program + does not specify a version number of this License, you may choose + any version ever published by the Free Software Foundation. + + 11. If you wish to incorporate parts of the Program into other free + programs whose distribution conditions are different, write to the + author to ask for permission. For software which is copyrighted + by the Free Software Foundation, write to the Free Software + Foundation; we sometimes make exceptions for this. Our decision + will be guided by the two goals of preserving the free status of + all derivatives of our free software and of promoting the sharing + and reuse of software generally. + + NO WARRANTY + + 12. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO + WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE + LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT + HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT + WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT + NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE + QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE + PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY + SERVICING, REPAIR OR CORRECTION. + + 13. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY + MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE + LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, + INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR + INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF + DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU + OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY + OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + +Appendix: How to Apply These Terms to Your New Programs +======================================================= + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + ONE LINE TO GIVE THE PROGRAM'S NAME AND A BRIEF IDEA OF WHAT IT DOES. + Copyright (C) 19YY NAME OF AUTHOR + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Also add information on how to contact you by electronic and paper +mail. + + If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19YY NAME OF AUTHOR + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + + The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and `show +c'; they could even be mouse-clicks or menu items--whatever suits your +program. + + You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the program, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + SIGNATURE OF TY COON, 1 April 1989 + Ty Coon, President of Vice + + This General Public License does not permit incorporating your +program into proprietary programs. If your program is a subroutine +library, you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +GNU Library General Public License instead of this License. + + +File: regex.info, Node: Index, Prev: Copying, Up: Top + +Index +***** + +* Menu: + +* $: Match-end-of-line Operator. +* (: Grouping Operators. +* ): Grouping Operators. +* *: Match-zero-or-more Operator. +* +: Match-one-or-more Operator. +* -: List Operators. +* .: Match-any-character Operator. +* :] in regex: Character Class Operators. +* ?: Match-zero-or-one Operator. +* {: Interval Operators. +* }: Interval Operators. +* [: in regex: Character Class Operators. +* [^: List Operators. +* [: List Operators. +* \': Match-end-of-buffer Operator. +* \<: Match-beginning-of-word Operator. +* \>: Match-end-of-word Operator. +* \{: Interval Operators. +* \}: Interval Operators. +* \b: Match-word-boundary Operator. +* \B: Match-within-word Operator. +* \s: Match-syntactic-class Operator. +* \S: Match-not-syntactic-class Operator. +* \w: Match-word-constituent Operator. +* \W: Match-non-word-constituent Operator. +* \`: Match-beginning-of-buffer Operator. +* \: List Operators. +* ]: List Operators. +* ^: List Operators. +* allocated initialization: GNU Regular Expression Compiling. +* alternation operator: Alternation Operator. +* alternation operator and ^: Match-beginning-of-line Operator. +* anchoring: Anchoring Operators. +* anchors: Match-end-of-line Operator. +* anchors: Match-beginning-of-line Operator. +* Awk: Predefined Syntaxes. +* back references: Back-reference Operator. +* backtracking: Match-zero-or-more Operator. +* backtracking: Alternation Operator. +* beginning-of-line operator: Match-beginning-of-line Operator. +* bracket expression: List Operators. +* buffer field, set by re_compile_pattern: GNU Regular Expression Compiling. +* buffer initialization: GNU Regular Expression Compiling. +* character classes: Character Class Operators. +* Egrep: Predefined Syntaxes. +* Emacs: Predefined Syntaxes. +* end in struct re_registers: Using Registers. +* end-of-line operator: Match-end-of-line Operator. +* fastmap initialization: GNU Regular Expression Compiling. +* fastmaps: Searching with Fastmaps. +* fastmap_accurate field, set by re_compile_pattern: GNU Regular Expression Compiling. +* Grep: Predefined Syntaxes. +* grouping: Grouping Operators. +* ignoring case: POSIX Regular Expression Compiling. +* interval expression: Interval Operators. +* matching list: List Operators. +* matching newline: List Operators. +* matching with GNU functions: GNU Matching. +* newline_anchor field in pattern buffer: Match-beginning-of-line Operator. +* nonmatching list: List Operators. +* not_bol field in pattern buffer: Match-beginning-of-line Operator. +* num_regs in struct re_registers: Using Registers. +* open-group operator and ^: Match-beginning-of-line Operator. +* or operator: Alternation Operator. +* parenthesizing: Grouping Operators. +* pattern buffer initialization: GNU Regular Expression Compiling. +* pattern buffer, definition of: GNU Pattern Buffers. +* POSIX Awk: Predefined Syntaxes. +* range argument to re_search: GNU Searching. +* regex.c: Overview. +* regex.h: Overview. +* regexp anchoring: Anchoring Operators. +* regmatch_t: Using Byte Offsets. +* regs_allocated: Using Registers. +* REGS_FIXED: Using Registers. +* REGS_REALLOCATE: Using Registers. +* REGS_UNALLOCATED: Using Registers. +* regular expressions, syntax of: Regular Expression Syntax. +* REG_EXTENDED: POSIX Regular Expression Compiling. +* REG_ICASE: POSIX Regular Expression Compiling. +* REG_NEWLINE: POSIX Regular Expression Compiling. +* REG_NOSUB: POSIX Regular Expression Compiling. +* RE_BACKSLASH_ESCAPE_IN_LIST: Syntax Bits. +* RE_BK_PLUS_QM: Syntax Bits. +* RE_CHAR_CLASSES: Syntax Bits. +* RE_CONTEXT_INDEP_ANCHORS: Syntax Bits. +* RE_CONTEXT_INDEP_ANCHORS (and ^): Match-beginning-of-line Operator. +* RE_CONTEXT_INDEP_OPS: Syntax Bits. +* RE_CONTEXT_INVALID_OPS: Syntax Bits. +* RE_DOT_NEWLINE: Syntax Bits. +* RE_DOT_NOT_NULL: Syntax Bits. +* RE_INTERVALS: Syntax Bits. +* RE_LIMITED_OPS: Syntax Bits. +* RE_NEWLINE_ALT: Syntax Bits. +* RE_NO_BK_BRACES: Syntax Bits. +* RE_NO_BK_PARENS: Syntax Bits. +* RE_NO_BK_REFS: Syntax Bits. +* RE_NO_BK_VBAR: Syntax Bits. +* RE_NO_EMPTY_RANGES: Syntax Bits. +* re_nsub field, set by re_compile_pattern: GNU Regular Expression Compiling. +* re_pattern_buffer definition: GNU Pattern Buffers. +* re_registers: Using Registers. +* re_syntax_options initialization: GNU Regular Expression Compiling. +* RE_UNMATCHED_RIGHT_PAREN_ORD: Syntax Bits. +* searching with GNU functions: GNU Searching. +* start argument to re_search: GNU Searching. +* start in struct re_registers: Using Registers. +* struct re_pattern_buffer definition: GNU Pattern Buffers. +* subexpressions: Grouping Operators. +* syntax field, set by re_compile_pattern: GNU Regular Expression Compiling. +* syntax bits: Syntax Bits. +* syntax initialization: GNU Regular Expression Compiling. +* syntax of regular expressions: Regular Expression Syntax. +* translate initialization: GNU Regular Expression Compiling. +* used field, set by re_compile_pattern: GNU Regular Expression Compiling. +* word boundaries, matching: Match-word-boundary Operator. +* \: The Backslash Character. +* \(: Grouping Operators. +* \): Grouping Operators. +* \|: Alternation Operator. +* ^: Match-beginning-of-line Operator. +* |: Alternation Operator. + + + +Tag Table: +Node: Top1064 +Node: Overview4562 +Node: Regular Expression Syntax6746 +Node: Syntax Bits7916 +Node: Predefined Syntaxes14018 +Node: Collating Elements vs. Characters17872 +Node: The Backslash Character18835 +Node: Common Operators21992 +Node: Match-self Operator23445 +Node: Match-any-character Operator23941 +Node: Concatenation Operator24520 +Node: Repetition Operators25017 +Node: Match-zero-or-more Operator25436 +Node: Match-one-or-more Operator27483 +Node: Match-zero-or-one Operator28341 +Node: Interval Operators29196 +Node: Alternation Operator30991 +Node: List Operators32489 +Node: Character Class Operators35272 +Node: Range Operator36901 +Node: Grouping Operators38930 +Node: Back-reference Operator40251 +Node: Anchoring Operators43073 +Node: Match-beginning-of-line Operator43447 +Node: Match-end-of-line Operator44779 +Node: GNU Operators45518 +Node: Word Operators45767 +Node: Non-Emacs Syntax Tables46391 +Node: Match-word-boundary Operator47465 +Node: Match-within-word Operator47858 +Node: Match-beginning-of-word Operator48255 +Node: Match-end-of-word Operator48588 +Node: Match-word-constituent Operator48908 +Node: Match-non-word-constituent Operator49234 +Node: Buffer Operators49545 +Node: Match-beginning-of-buffer Operator49952 +Node: Match-end-of-buffer Operator50264 +Node: GNU Emacs Operators50558 +Node: Syntactic Class Operators50901 +Node: Emacs Syntax Tables51307 +Node: Match-syntactic-class Operator51963 +Node: Match-not-syntactic-class Operator52560 +Node: What Gets Matched?53150 +Node: Programming with Regex53799 +Node: GNU Regex Functions54237 +Node: GNU Pattern Buffers55078 +Node: GNU Regular Expression Compiling58303 +Node: GNU Matching61181 +Node: GNU Searching63101 +Node: Matching/Searching with Split Data64913 +Node: Searching with Fastmaps66369 +Node: GNU Translate Tables68921 +Node: Using Registers70892 +Node: Freeing GNU Pattern Buffers77000 +Node: POSIX Regex Functions77593 +Node: POSIX Pattern Buffers78266 +Node: POSIX Regular Expression Compiling78709 +Node: POSIX Matching82836 +Node: Reporting Errors84791 +Node: Using Byte Offsets86048 +Node: Freeing POSIX Pattern Buffers86861 +Node: BSD Regex Functions87467 +Node: BSD Regular Expression Compiling87886 +Node: BSD Searching89258 +Node: Copying89960 +Node: Index109122 + +End Tag Table diff --git a/gnu/lib/libregex/doc/regex.texi b/gnu/lib/libregex/doc/regex.texi new file mode 100644 index 0000000000..d93953ece2 --- /dev/null +++ b/gnu/lib/libregex/doc/regex.texi @@ -0,0 +1,3138 @@ +\input texinfo +@c %**start of header +@setfilename regex.info +@settitle Regex +@c %**end of header + +@c \\{fill-paragraph} works better (for me, anyway) if the text in the +@c source file isn't indented. +@paragraphindent 2 + +@c Define a new index for our magic constants. +@defcodeindex cn + +@c Put everything in one index (arbitrarily chosen to be the concept index). +@syncodeindex cn cp +@syncodeindex ky cp +@syncodeindex pg cp +@syncodeindex tp cp +@syncodeindex vr cp + +@c Here is what we use in the Info `dir' file: +@c * Regex: (regex). Regular expression library. + + +@ifinfo +This file documents the GNU regular expression library. + +Copyright (C) 1992, 1993 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + +@ignore +Permission is granted to process this file through TeX and print the +results, provided the printed document carries a copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). +@end ignore + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +section entitled ``GNU General Public License'' is included exactly as +in the original, and provided that the entire resulting derived work is +distributed under the terms of a permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that the section entitled ``GNU General Public License'' may be +included in a translation approved by the Free Software Foundation +instead of in the original English. +@end ifinfo + + +@titlepage + +@title Regex +@subtitle edition 0.12a +@subtitle 19 September 1992 +@author Kathryn A. Hargreaves +@author Karl Berry + +@page + +@vskip 0pt plus 1filll +Copyright @copyright{} 1992 Free Software Foundation. + +Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +section entitled ``GNU General Public License'' is included exactly as +in the original, and provided that the entire resulting derived work is +distributed under the terms of a permission notice identical to this +one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that the section entitled ``GNU General Public License'' may be +included in a translation approved by the Free Software Foundation +instead of in the original English. + +@end titlepage + + +@ifinfo +@node Top, Overview, (dir), (dir) +@top Regular Expression Library + +This manual documents how to program with the GNU regular expression +library. This is edition 0.12a of the manual, 19 September 1992. + +The first part of this master menu lists the major nodes in this Info +document, including the index. The rest of the menu lists all the +lower level nodes in the document. + +@menu +* Overview:: +* Regular Expression Syntax:: +* Common Operators:: +* GNU Operators:: +* GNU Emacs Operators:: +* What Gets Matched?:: +* Programming with Regex:: +* Copying:: Copying and sharing Regex. +* Index:: General index. + --- The Detailed Node Listing --- + +Regular Expression Syntax + +* Syntax Bits:: +* Predefined Syntaxes:: +* Collating Elements vs. Characters:: +* The Backslash Character:: + +Common Operators + +* Match-self Operator:: Ordinary characters. +* Match-any-character Operator:: . +* Concatenation Operator:: Juxtaposition. +* Repetition Operators:: * + ? @{@} +* Alternation Operator:: | +* List Operators:: [...] [^...] +* Grouping Operators:: (...) +* Back-reference Operator:: \digit +* Anchoring Operators:: ^ $ + +Repetition Operators + +* Match-zero-or-more Operator:: * +* Match-one-or-more Operator:: + +* Match-zero-or-one Operator:: ? +* Interval Operators:: @{@} + +List Operators (@code{[} @dots{} @code{]} and @code{[^} @dots{} @code{]}) + +* Character Class Operators:: [:class:] +* Range Operator:: start-end + +Anchoring Operators + +* Match-beginning-of-line Operator:: ^ +* Match-end-of-line Operator:: $ + +GNU Operators + +* Word Operators:: +* Buffer Operators:: + +Word Operators + +* Non-Emacs Syntax Tables:: +* Match-word-boundary Operator:: \b +* Match-within-word Operator:: \B +* Match-beginning-of-word Operator:: \< +* Match-end-of-word Operator:: \> +* Match-word-constituent Operator:: \w +* Match-non-word-constituent Operator:: \W + +Buffer Operators + +* Match-beginning-of-buffer Operator:: \` +* Match-end-of-buffer Operator:: \' + +GNU Emacs Operators + +* Syntactic Class Operators:: + +Syntactic Class Operators + +* Emacs Syntax Tables:: +* Match-syntactic-class Operator:: \sCLASS +* Match-not-syntactic-class Operator:: \SCLASS + +Programming with Regex + +* GNU Regex Functions:: +* POSIX Regex Functions:: +* BSD Regex Functions:: + +GNU Regex Functions + +* GNU Pattern Buffers:: The re_pattern_buffer type. +* GNU Regular Expression Compiling:: re_compile_pattern () +* GNU Matching:: re_match () +* GNU Searching:: re_search () +* Matching/Searching with Split Data:: re_match_2 (), re_search_2 () +* Searching with Fastmaps:: re_compile_fastmap () +* GNU Translate Tables:: The `translate' field. +* Using Registers:: The re_registers type and related fns. +* Freeing GNU Pattern Buffers:: regfree () + +POSIX Regex Functions + +* POSIX Pattern Buffers:: The regex_t type. +* POSIX Regular Expression Compiling:: regcomp () +* POSIX Matching:: regexec () +* Reporting Errors:: regerror () +* Using Byte Offsets:: The regmatch_t type. +* Freeing POSIX Pattern Buffers:: regfree () + +BSD Regex Functions + +* BSD Regular Expression Compiling:: re_comp () +* BSD Searching:: re_exec () +@end menu +@end ifinfo +@node Overview, Regular Expression Syntax, Top, Top +@chapter Overview + +A @dfn{regular expression} (or @dfn{regexp}, or @dfn{pattern}) is a text +string that describes some (mathematical) set of strings. A regexp +@var{r} @dfn{matches} a string @var{s} if @var{s} is in the set of +strings described by @var{r}. + +Using the Regex library, you can: + +@itemize @bullet + +@item +see if a string matches a specified pattern as a whole, and + +@item +search within a string for a substring matching a specified pattern. + +@end itemize + +Some regular expressions match only one string, i.e., the set they +describe has only one member. For example, the regular expression +@samp{foo} matches the string @samp{foo} and no others. Other regular +expressions match more than one string, i.e., the set they describe has +more than one member. For example, the regular expression @samp{f*} +matches the set of strings made up of any number (including zero) of +@samp{f}s. As you can see, some characters in regular expressions match +themselves (such as @samp{f}) and some don't (such as @samp{*}); the +ones that don't match themselves instead let you specify patterns that +describe many different strings. + +To either match or search for a regular expression with the Regex +library functions, you must first compile it with a Regex pattern +compiling function. A @dfn{compiled pattern} is a regular expression +converted to the internal format used by the library functions. Once +you've compiled a pattern, you can use it for matching or searching any +number of times. + +The Regex library consists of two source files: @file{regex.h} and +@file{regex.c}. +@pindex regex.h +@pindex regex.c +Regex provides three groups of functions with which you can operate on +regular expressions. One group---the @sc{gnu} group---is more powerful +but not completely compatible with the other two, namely the @sc{posix} +and Berkeley @sc{unix} groups; its interface was designed specifically +for @sc{gnu}. The other groups have the same interfaces as do the +regular expression functions in @sc{posix} and Berkeley +@sc{unix}. + +We wrote this chapter with programmers in mind, not users of +programs---such as Emacs---that use Regex. We describe the Regex +library in its entirety, not how to write regular expressions that a +particular program understands. + + +@node Regular Expression Syntax, Common Operators, Overview, Top +@chapter Regular Expression Syntax + +@cindex regular expressions, syntax of +@cindex syntax of regular expressions + +@dfn{Characters} are things you can type. @dfn{Operators} are things in +a regular expression that match one or more characters. You compose +regular expressions from operators, which in turn you specify using one +or more characters. + +Most characters represent what we call the match-self operator, i.e., +they match themselves; we call these characters @dfn{ordinary}. Other +characters represent either all or parts of fancier operators; e.g., +@samp{.} represents what we call the match-any-character operator +(which, no surprise, matches (almost) any character); we call these +characters @dfn{special}. Two different things determine what +characters represent what operators: + +@enumerate +@item +the regular expression syntax your program has told the Regex library to +recognize, and + +@item +the context of the character in the regular expression. +@end enumerate + +In the following sections, we describe these things in more detail. + +@menu +* Syntax Bits:: +* Predefined Syntaxes:: +* Collating Elements vs. Characters:: +* The Backslash Character:: +@end menu + + +@node Syntax Bits, Predefined Syntaxes, , Regular Expression Syntax +@section Syntax Bits + +@cindex syntax bits + +In any particular syntax for regular expressions, some characters are +always special, others are sometimes special, and others are never +special. The particular syntax that Regex recognizes for a given +regular expression depends on the value in the @code{syntax} field of +the pattern buffer of that regular expression. + +You get a pattern buffer by compiling a regular expression. @xref{GNU +Pattern Buffers}, and @ref{POSIX Pattern Buffers}, for more information +on pattern buffers. @xref{GNU Regular Expression Compiling}, @ref{POSIX +Regular Expression Compiling}, and @ref{BSD Regular Expression +Compiling}, for more information on compiling. + +Regex considers the value of the @code{syntax} field to be a collection +of bits; we refer to these bits as @dfn{syntax bits}. In most cases, +they affect what characters represent what operators. We describe the +meanings of the operators to which we refer in @ref{Common Operators}, +@ref{GNU Operators}, and @ref{GNU Emacs Operators}. + +For reference, here is the complete list of syntax bits, in alphabetical +order: + +@table @code + +@cnindex RE_BACKSLASH_ESCAPE_IN_LIST +@item RE_BACKSLASH_ESCAPE_IN_LISTS +If this bit is set, then @samp{\} inside a list (@pxref{List Operators} +quotes (makes ordinary, if it's special) the following character; if +this bit isn't set, then @samp{\} is an ordinary character inside lists. +(@xref{The Backslash Character}, for what `\' does outside of lists.) + +@cnindex RE_BK_PLUS_QM +@item RE_BK_PLUS_QM +If this bit is set, then @samp{\+} represents the match-one-or-more +operator and @samp{\?} represents the match-zero-or-more operator; if +this bit isn't set, then @samp{+} represents the match-one-or-more +operator and @samp{?} represents the match-zero-or-one operator. This +bit is irrelevant if @code{RE_LIMITED_OPS} is set. + +@cnindex RE_CHAR_CLASSES +@item RE_CHAR_CLASSES +If this bit is set, then you can use character classes in lists; if this +bit isn't set, then you can't. + +@cnindex RE_CONTEXT_INDEP_ANCHORS +@item RE_CONTEXT_INDEP_ANCHORS +If this bit is set, then @samp{^} and @samp{$} are special anywhere outside +a list; if this bit isn't set, then these characters are special only in +certain contexts. @xref{Match-beginning-of-line Operator}, and +@ref{Match-end-of-line Operator}. + +@cnindex RE_CONTEXT_INDEP_OPS +@item RE_CONTEXT_INDEP_OPS +If this bit is set, then certain characters are special anywhere outside +a list; if this bit isn't set, then those characters are special only in +some contexts and are ordinary elsewhere. Specifically, if this bit +isn't set then @samp{*}, and (if the syntax bit @code{RE_LIMITED_OPS} +isn't set) @samp{+} and @samp{?} (or @samp{\+} and @samp{\?}, depending +on the syntax bit @code{RE_BK_PLUS_QM}) represent repetition operators +only if they're not first in a regular expression or just after an +open-group or alternation operator. The same holds for @samp{@{} (or +@samp{\@{}, depending on the syntax bit @code{RE_NO_BK_BRACES}) if +it is the beginning of a valid interval and the syntax bit +@code{RE_INTERVALS} is set. + +@cnindex RE_CONTEXT_INVALID_OPS +@item RE_CONTEXT_INVALID_OPS +If this bit is set, then repetition and alternation operators can't be +in certain positions within a regular expression. Specifically, the +regular expression is invalid if it has: + +@itemize @bullet + +@item +a repetition operator first in the regular expression or just after a +match-beginning-of-line, open-group, or alternation operator; or + +@item +an alternation operator first or last in the regular expression, just +before a match-end-of-line operator, or just after an alternation or +open-group operator. + +@end itemize + +If this bit isn't set, then you can put the characters representing the +repetition and alternation characters anywhere in a regular expression. +Whether or not they will in fact be operators in certain positions +depends on other syntax bits. + +@cnindex RE_DOT_NEWLINE +@item RE_DOT_NEWLINE +If this bit is set, then the match-any-character operator matches +a newline; if this bit isn't set, then it doesn't. + +@cnindex RE_DOT_NOT_NULL +@item RE_DOT_NOT_NULL +If this bit is set, then the match-any-character operator doesn't match +a null character; if this bit isn't set, then it does. + +@cnindex RE_INTERVALS +@item RE_INTERVALS +If this bit is set, then Regex recognizes interval operators; if this bit +isn't set, then it doesn't. + +@cnindex RE_LIMITED_OPS +@item RE_LIMITED_OPS +If this bit is set, then Regex doesn't recognize the match-one-or-more, +match-zero-or-one or alternation operators; if this bit isn't set, then +it does. + +@cnindex RE_NEWLINE_ALT +@item RE_NEWLINE_ALT +If this bit is set, then newline represents the alternation operator; if +this bit isn't set, then newline is ordinary. + +@cnindex RE_NO_BK_BRACES +@item RE_NO_BK_BRACES +If this bit is set, then @samp{@{} represents the open-interval operator +and @samp{@}} represents the close-interval operator; if this bit isn't +set, then @samp{\@{} represents the open-interval operator and +@samp{\@}} represents the close-interval operator. This bit is relevant +only if @code{RE_INTERVALS} is set. + +@cnindex RE_NO_BK_PARENS +@item RE_NO_BK_PARENS +If this bit is set, then @samp{(} represents the open-group operator and +@samp{)} represents the close-group operator; if this bit isn't set, then +@samp{\(} represents the open-group operator and @samp{\)} represents +the close-group operator. + +@cnindex RE_NO_BK_REFS +@item RE_NO_BK_REFS +If this bit is set, then Regex doesn't recognize @samp{\}@var{digit} as +the back reference operator; if this bit isn't set, then it does. + +@cnindex RE_NO_BK_VBAR +@item RE_NO_BK_VBAR +If this bit is set, then @samp{|} represents the alternation operator; +if this bit isn't set, then @samp{\|} represents the alternation +operator. This bit is irrelevant if @code{RE_LIMITED_OPS} is set. + +@cnindex RE_NO_EMPTY_RANGES +@item RE_NO_EMPTY_RANGES +If this bit is set, then a regular expression with a range whose ending +point collates lower than its starting point is invalid; if this bit +isn't set, then Regex considers such a range to be empty. + +@cnindex RE_UNMATCHED_RIGHT_PAREN_ORD +@item RE_UNMATCHED_RIGHT_PAREN_ORD +If this bit is set and the regular expression has no matching open-group +operator, then Regex considers what would otherwise be a close-group +operator (based on how @code{RE_NO_BK_PARENS} is set) to match @samp{)}. + +@end table + + +@node Predefined Syntaxes, Collating Elements vs. Characters, Syntax Bits, Regular Expression Syntax +@section Predefined Syntaxes + +If you're programming with Regex, you can set a pattern buffer's +(@pxref{GNU Pattern Buffers}, and @ref{POSIX Pattern Buffers}) +@code{syntax} field either to an arbitrary combination of syntax bits +(@pxref{Syntax Bits}) or else to the configurations defined by Regex. +These configurations define the syntaxes used by certain +programs---@sc{gnu} Emacs, +@cindex Emacs +@sc{posix} Awk, +@cindex POSIX Awk +traditional Awk, +@cindex Awk +Grep, +@cindex Grep +@cindex Egrep +Egrep---in addition to syntaxes for @sc{posix} basic and extended +regular expressions. + +The predefined syntaxes--taken directly from @file{regex.h}---are: + +@example +#define RE_SYNTAX_EMACS 0 + +#define RE_SYNTAX_AWK \ + (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \ + | RE_UNMATCHED_RIGHT_PAREN_ORD) + +#define RE_SYNTAX_POSIX_AWK \ + (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS) + +#define RE_SYNTAX_GREP \ + (RE_BK_PLUS_QM | RE_CHAR_CLASSES \ + | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \ + | RE_NEWLINE_ALT) + +#define RE_SYNTAX_EGREP \ + (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \ + | RE_NEWLINE_ALT | RE_NO_BK_PARENS \ + | RE_NO_BK_VBAR) + +#define RE_SYNTAX_POSIX_EGREP \ + (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES) + +/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */ +#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC + +#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC + +/* Syntax bits common to both basic and extended POSIX regex syntax. */ +#define _RE_SYNTAX_POSIX_COMMON \ + (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \ + | RE_INTERVALS | RE_NO_EMPTY_RANGES) + +#define RE_SYNTAX_POSIX_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM) + +/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes + RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this + isn't minimal, since other operators, such as \`, aren't disabled. */ +#define RE_SYNTAX_POSIX_MINIMAL_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) + +#define RE_SYNTAX_POSIX_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_UNMATCHED_RIGHT_PAREN_ORD) + +/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS + replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added. */ +#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) +@end example + +@node Collating Elements vs. Characters, The Backslash Character, Predefined Syntaxes, Regular Expression Syntax +@section Collating Elements vs.@: Characters + +@sc{posix} generalizes the notion of a character to that of a +collating element. It defines a @dfn{collating element} to be ``a +sequence of one or more bytes defined in the current collating sequence +as a unit of collation.'' + +This generalizes the notion of a character in +two ways. First, a single character can map into two or more collating +elements. For example, the German +@tex +`\ss' +@end tex +@ifinfo +``es-zet'' +@end ifinfo +collates as the collating element @samp{s} followed by another collating +element @samp{s}. Second, two or more characters can map into one +collating element. For example, the Spanish @samp{ll} collates after +@samp{l} and before @samp{m}. + +Since @sc{posix}'s ``collating element'' preserves the essential idea of +a ``character,'' we use the latter, more familiar, term in this document. + +@node The Backslash Character, , Collating Elements vs. Characters, Regular Expression Syntax +@section The Backslash Character + +@cindex \ +The @samp{\} character has one of four different meanings, depending on +the context in which you use it and what syntax bits are set +(@pxref{Syntax Bits}). It can: 1) stand for itself, 2) quote the next +character, 3) introduce an operator, or 4) do nothing. + +@enumerate +@item +It stands for itself inside a list +(@pxref{List Operators}) if the syntax bit +@code{RE_BACKSLASH_ESCAPE_IN_LISTS} is not set. For example, @samp{[\]} +would match @samp{\}. + +@item +It quotes (makes ordinary, if it's special) the next character when you +use it either: + +@itemize @bullet +@item +outside a list,@footnote{Sometimes +you don't have to explicitly quote special characters to make +them ordinary. For instance, most characters lose any special meaning +inside a list (@pxref{List Operators}). In addition, if the syntax bits +@code{RE_CONTEXT_INVALID_OPS} and @code{RE_CONTEXT_INDEP_OPS} +aren't set, then (for historical reasons) the matcher considers special +characters ordinary if they are in contexts where the operations they +represent make no sense; for example, then the match-zero-or-more +operator (represented by @samp{*}) matches itself in the regular +expression @samp{*foo} because there is no preceding expression on which +it can operate. It is poor practice, however, to depend on this +behavior; if you want a special character to be ordinary outside a list, +it's better to always quote it, regardless.} or + +@item +inside a list and the syntax bit @code{RE_BACKSLASH_ESCAPE_IN_LISTS} is set. + +@end itemize + +@item +It introduces an operator when followed by certain ordinary +characters---sometimes only when certain syntax bits are set. See the +cases @code{RE_BK_PLUS_QM}, @code{RE_NO_BK_BRACES}, @code{RE_NO_BK_VAR}, +@code{RE_NO_BK_PARENS}, @code{RE_NO_BK_REF} in @ref{Syntax Bits}. Also: + +@itemize @bullet +@item +@samp{\b} represents the match-word-boundary operator +(@pxref{Match-word-boundary Operator}). + +@item +@samp{\B} represents the match-within-word operator +(@pxref{Match-within-word Operator}). + +@item +@samp{\<} represents the match-beginning-of-word operator @* +(@pxref{Match-beginning-of-word Operator}). + +@item +@samp{\>} represents the match-end-of-word operator +(@pxref{Match-end-of-word Operator}). + +@item +@samp{\w} represents the match-word-constituent operator +(@pxref{Match-word-constituent Operator}). + +@item +@samp{\W} represents the match-non-word-constituent operator +(@pxref{Match-non-word-constituent Operator}). + +@item +@samp{\`} represents the match-beginning-of-buffer +operator and @samp{\'} represents the match-end-of-buffer operator +(@pxref{Buffer Operators}). + +@item +If Regex was compiled with the C preprocessor symbol @code{emacs} +defined, then @samp{\s@var{class}} represents the match-syntactic-class +operator and @samp{\S@var{class}} represents the +match-not-syntactic-class operator (@pxref{Syntactic Class Operators}). + +@end itemize + +@item +In all other cases, Regex ignores @samp{\}. For example, +@samp{\n} matches @samp{n}. + +@end enumerate + +@node Common Operators, GNU Operators, Regular Expression Syntax, Top +@chapter Common Operators + +You compose regular expressions from operators. In the following +sections, we describe the regular expression operators specified by +@sc{posix}; @sc{gnu} also uses these. Most operators have more than one +representation as characters. @xref{Regular Expression Syntax}, for +what characters represent what operators under what circumstances. + +For most operators that can be represented in two ways, one +representation is a single character and the other is that character +preceded by @samp{\}. For example, either @samp{(} or @samp{\(} +represents the open-group operator. Which one does depends on the +setting of a syntax bit, in this case @code{RE_NO_BK_PARENS}. Why is +this so? Historical reasons dictate some of the varying +representations, while @sc{posix} dictates others. + +Finally, almost all characters lose any special meaning inside a list +(@pxref{List Operators}). + +@menu +* Match-self Operator:: Ordinary characters. +* Match-any-character Operator:: . +* Concatenation Operator:: Juxtaposition. +* Repetition Operators:: * + ? @{@} +* Alternation Operator:: | +* List Operators:: [...] [^...] +* Grouping Operators:: (...) +* Back-reference Operator:: \digit +* Anchoring Operators:: ^ $ +@end menu + +@node Match-self Operator, Match-any-character Operator, , Common Operators +@section The Match-self Operator (@var{ordinary character}) + +This operator matches the character itself. All ordinary characters +(@pxref{Regular Expression Syntax}) represent this operator. For +example, @samp{f} is always an ordinary character, so the regular +expression @samp{f} matches only the string @samp{f}. In +particular, it does @emph{not} match the string @samp{ff}. + +@node Match-any-character Operator, Concatenation Operator, Match-self Operator, Common Operators +@section The Match-any-character Operator (@code{.}) + +@cindex @samp{.} + +This operator matches any single printing or nonprinting character +except it won't match a: + +@table @asis +@item newline +if the syntax bit @code{RE_DOT_NEWLINE} isn't set. + +@item null +if the syntax bit @code{RE_DOT_NOT_NULL} is set. + +@end table + +The @samp{.} (period) character represents this operator. For example, +@samp{a.b} matches any three-character string beginning with @samp{a} +and ending with @samp{b}. + +@node Concatenation Operator, Repetition Operators, Match-any-character Operator, Common Operators +@section The Concatenation Operator + +This operator concatenates two regular expressions @var{a} and @var{b}. +No character represents this operator; you simply put @var{b} after +@var{a}. The result is a regular expression that will match a string if +@var{a} matches its first part and @var{b} matches the rest. For +example, @samp{xy} (two match-self operators) matches @samp{xy}. + +@node Repetition Operators, Alternation Operator, Concatenation Operator, Common Operators +@section Repetition Operators + +Repetition operators repeat the preceding regular expression a specified +number of times. + +@menu +* Match-zero-or-more Operator:: * +* Match-one-or-more Operator:: + +* Match-zero-or-one Operator:: ? +* Interval Operators:: @{@} +@end menu + +@node Match-zero-or-more Operator, Match-one-or-more Operator, , Repetition Operators +@subsection The Match-zero-or-more Operator (@code{*}) + +@cindex @samp{*} + +This operator repeats the smallest possible preceding regular expression +as many times as necessary (including zero) to match the pattern. +@samp{*} represents this operator. For example, @samp{o*} +matches any string made up of zero or more @samp{o}s. Since this +operator operates on the smallest preceding regular expression, +@samp{fo*} has a repeating @samp{o}, not a repeating @samp{fo}. So, +@samp{fo*} matches @samp{f}, @samp{fo}, @samp{foo}, and so on. + +Since the match-zero-or-more operator is a suffix operator, it may be +useless as such when no regular expression precedes it. This is the +case when it: + +@itemize @bullet +@item +is first in a regular expression, or + +@item +follows a match-beginning-of-line, open-group, or alternation +operator. + +@end itemize + +@noindent +Three different things can happen in these cases: + +@enumerate +@item +If the syntax bit @code{RE_CONTEXT_INVALID_OPS} is set, then the +regular expression is invalid. + +@item +If @code{RE_CONTEXT_INVALID_OPS} isn't set, but +@code{RE_CONTEXT_INDEP_OPS} is, then @samp{*} represents the +match-zero-or-more operator (which then operates on the empty string). + +@item +Otherwise, @samp{*} is ordinary. + +@end enumerate + +@cindex backtracking +The matcher processes a match-zero-or-more operator by first matching as +many repetitions of the smallest preceding regular expression as it can. +Then it continues to match the rest of the pattern. + +If it can't match the rest of the pattern, it backtracks (as many times +as necessary), each time discarding one of the matches until it can +either match the entire pattern or be certain that it cannot get a +match. For example, when matching @samp{ca*ar} against @samp{caaar}, +the matcher first matches all three @samp{a}s of the string with the +@samp{a*} of the regular expression. However, it cannot then match the +final @samp{ar} of the regular expression against the final @samp{r} of +the string. So it backtracks, discarding the match of the last @samp{a} +in the string. It can then match the remaining @samp{ar}. + + +@node Match-one-or-more Operator, Match-zero-or-one Operator, Match-zero-or-more Operator, Repetition Operators +@subsection The Match-one-or-more Operator (@code{+} or @code{\+}) + +@cindex @samp{+} + +If the syntax bit @code{RE_LIMITED_OPS} is set, then Regex doesn't recognize +this operator. Otherwise, if the syntax bit @code{RE_BK_PLUS_QM} isn't +set, then @samp{+} represents this operator; if it is, then @samp{\+} +does. + +This operator is similar to the match-zero-or-more operator except that +it repeats the preceding regular expression at least once; +@pxref{Match-zero-or-more Operator}, for what it operates on, how some +syntax bits affect it, and how Regex backtracks to match it. + +For example, supposing that @samp{+} represents the match-one-or-more +operator; then @samp{ca+r} matches, e.g., @samp{car} and +@samp{caaaar}, but not @samp{cr}. + +@node Match-zero-or-one Operator, Interval Operators, Match-one-or-more Operator, Repetition Operators +@subsection The Match-zero-or-one Operator (@code{?} or @code{\?}) +@cindex @samp{?} + +If the syntax bit @code{RE_LIMITED_OPS} is set, then Regex doesn't +recognize this operator. Otherwise, if the syntax bit +@code{RE_BK_PLUS_QM} isn't set, then @samp{?} represents this operator; +if it is, then @samp{\?} does. + +This operator is similar to the match-zero-or-more operator except that +it repeats the preceding regular expression once or not at all; +@pxref{Match-zero-or-more Operator}, to see what it operates on, how +some syntax bits affect it, and how Regex backtracks to match it. + +For example, supposing that @samp{?} represents the match-zero-or-one +operator; then @samp{ca?r} matches both @samp{car} and @samp{cr}, but +nothing else. + +@node Interval Operators, , Match-zero-or-one Operator, Repetition Operators +@subsection Interval Operators (@code{@{} @dots{} @code{@}} or @code{\@{} @dots{} @code{\@}}) + +@cindex interval expression +@cindex @samp{@{} +@cindex @samp{@}} +@cindex @samp{\@{} +@cindex @samp{\@}} + +If the syntax bit @code{RE_INTERVALS} is set, then Regex recognizes +@dfn{interval expressions}. They repeat the smallest possible preceding +regular expression a specified number of times. + +If the syntax bit @code{RE_NO_BK_BRACES} is set, @samp{@{} represents +the @dfn{open-interval operator} and @samp{@}} represents the +@dfn{close-interval operator} ; otherwise, @samp{\@{} and @samp{\@}} do. + +Specifically, supposing that @samp{@{} and @samp{@}} represent the +open-interval and close-interval operators; then: + +@table @code +@item @{@var{count}@} +matches exactly @var{count} occurrences of the preceding regular +expression. + +@item @{@var{min,}@} +matches @var{min} or more occurrences of the preceding regular +expression. + +@item @{@var{min, max}@} +matches at least @var{min} but no more than @var{max} occurrences of +the preceding regular expression. + +@end table + +The interval expression (but not necessarily the regular expression that +contains it) is invalid if: + +@itemize @bullet +@item +@var{min} is greater than @var{max}, or + +@item +any of @var{count}, @var{min}, or @var{max} are outside the range +zero to @code{RE_DUP_MAX} (which symbol @file{regex.h} +defines). + +@end itemize + +If the interval expression is invalid and the syntax bit +@code{RE_NO_BK_BRACES} is set, then Regex considers all the +characters in the would-be interval to be ordinary. If that bit +isn't set, then the regular expression is invalid. + +If the interval expression is valid but there is no preceding regular +expression on which to operate, then if the syntax bit +@code{RE_CONTEXT_INVALID_OPS} is set, the regular expression is invalid. +If that bit isn't set, then Regex considers all the characters---other +than backslashes, which it ignores---in the would-be interval to be +ordinary. + + +@node Alternation Operator, List Operators, Repetition Operators, Common Operators +@section The Alternation Operator (@code{|} or @code{\|}) + +@kindex | +@kindex \| +@cindex alternation operator +@cindex or operator + +If the syntax bit @code{RE_LIMITED_OPS} is set, then Regex doesn't +recognize this operator. Otherwise, if the syntax bit +@code{RE_NO_BK_VBAR} is set, then @samp{|} represents this operator; +otherwise, @samp{\|} does. + +Alternatives match one of a choice of regular expressions: +if you put the character(s) representing the alternation operator between +any two regular expressions @var{a} and @var{b}, the result matches +the union of the strings that @var{a} and @var{b} match. For +example, supposing that @samp{|} is the alternation operator, then +@samp{foo|bar|quux} would match any of @samp{foo}, @samp{bar} or +@samp{quux}. + +@ignore +@c Nobody needs to disallow empty alternatives any more. +If the syntax bit @code{RE_NO_EMPTY_ALTS} is set, then if either of the regular +expressions @var{a} or @var{b} is empty, the +regular expression is invalid. More precisely, if this syntax bit is +set, then the alternation operator can't: + +@itemize @bullet +@item +be first or last in a regular expression; + +@item +follow either another alternation operator or an open-group operator +(@pxref{Grouping Operators}); or + +@item +precede a close-group operator. + +@end itemize + +@noindent +For example, supposing @samp{(} and @samp{)} represent the open and +close-group operators, then @samp{|foo}, @samp{foo|}, @samp{foo||bar}, +@samp{foo(|bar)}, and @samp{(foo|)bar} would all be invalid. +@end ignore + +The alternation operator operates on the @emph{largest} possible +surrounding regular expressions. (Put another way, it has the lowest +precedence of any regular expression operator.) +Thus, the only way you can +delimit its arguments is to use grouping. For example, if @samp{(} and +@samp{)} are the open and close-group operators, then @samp{fo(o|b)ar} +would match either @samp{fooar} or @samp{fobar}. (@samp{foo|bar} would +match @samp{foo} or @samp{bar}.) + +@cindex backtracking +The matcher usually tries all combinations of alternatives so as to +match the longest possible string. For example, when matching +@samp{(fooq|foo)*(qbarquux|bar)} against @samp{fooqbarquux}, it cannot +take, say, the first (``depth-first'') combination it could match, since +then it would be content to match just @samp{fooqbar}. + +@comment xx something about leftmost-longest + + +@node List Operators, Grouping Operators, Alternation Operator, Common Operators +@section List Operators (@code{[} @dots{} @code{]} and @code{[^} @dots{} @code{]}) + +@cindex matching list +@cindex @samp{[} +@cindex @samp{]} +@cindex @samp{^} +@cindex @samp{-} +@cindex @samp{\} +@cindex @samp{[^} +@cindex nonmatching list +@cindex matching newline +@cindex bracket expression + +@dfn{Lists}, also called @dfn{bracket expressions}, are a set of one or +more items. An @dfn{item} is a character, +@ignore +(These get added when they get implemented.) +a collating symbol, an equivalence class expression, +@end ignore +a character class expression, or a range expression. The syntax bits +affect which kinds of items you can put in a list. We explain the last +two items in subsections below. Empty lists are invalid. + +A @dfn{matching list} matches a single character represented by one of +the list items. You form a matching list by enclosing one or more items +within an @dfn{open-matching-list operator} (represented by @samp{[}) +and a @dfn{close-list operator} (represented by @samp{]}). + +For example, @samp{[ab]} matches either @samp{a} or @samp{b}. +@samp{[ad]*} matches the empty string and any string composed of just +@samp{a}s and @samp{d}s in any order. Regex considers invalid a regular +expression with a @samp{[} but no matching +@samp{]}. + +@dfn{Nonmatching lists} are similar to matching lists except that they +match a single character @emph{not} represented by one of the list +items. You use an @dfn{open-nonmatching-list operator} (represented by +@samp{[^}@footnote{Regex therefore doesn't consider the @samp{^} to be +the first character in the list. If you put a @samp{^} character first +in (what you think is) a matching list, you'll turn it into a +nonmatching list.}) instead of an open-matching-list operator to start a +nonmatching list. + +For example, @samp{[^ab]} matches any character except @samp{a} or +@samp{b}. + +If the @code{posix_newline} field in the pattern buffer (@pxref{GNU +Pattern Buffers} is set, then nonmatching lists do not match a newline. + +Most characters lose any special meaning inside a list. The special +characters inside a list follow. + +@table @samp +@item ] +ends the list if it's not the first list item. So, if you want to make +the @samp{]} character a list item, you must put it first. + +@item \ +quotes the next character if the syntax bit @code{RE_BACKSLASH_ESCAPE_IN_LISTS} is +set. + +@ignore +Put these in if they get implemented. + +@item [. +represents the open-collating-symbol operator (@pxref{Collating Symbol +Operators}). + +@item .] +represents the close-collating-symbol operator. + +@item [= +represents the open-equivalence-class operator (@pxref{Equivalence Class +Operators}). + +@item =] +represents the close-equivalence-class operator. + +@end ignore + +@item [: +represents the open-character-class operator (@pxref{Character Class +Operators}) if the syntax bit @code{RE_CHAR_CLASSES} is set and what +follows is a valid character class expression. + +@item :] +represents the close-character-class operator if the syntax bit +@code{RE_CHAR_CLASSES} is set and what precedes it is an +open-character-class operator followed by a valid character class name. + +@item - +represents the range operator (@pxref{Range Operator}) if it's +not first or last in a list or the ending point of a range. + +@end table + +@noindent +All other characters are ordinary. For example, @samp{[.*]} matches +@samp{.} and @samp{*}. + +@menu +* Character Class Operators:: [:class:] +* Range Operator:: start-end +@end menu + +@ignore +(If collating symbols and equivalence class expressions get implemented, +then add this.) + +node Collating Symbol Operators +subsubsection Collating Symbol Operators (@code{[.} @dots{} @code{.]}) + +If the syntax bit @code{XX} is set, then you can represent +collating symbols inside lists. You form a @dfn{collating symbol} by +putting a collating element between an @dfn{open-collating-symbol +operator} and an @dfn{close-collating-symbol operator}. @samp{[.} +represents the open-collating-symbol operator and @samp{.]} represents +the close-collating-symbol operator. For example, if @samp{ll} is a +collating element, then @samp{[[.ll.]]} would match @samp{ll}. + +node Equivalence Class Operators +subsubsection Equivalence Class Operators (@code{[=} @dots{} @code{=]}) +@cindex equivalence class expression in regex +@cindex @samp{[=} in regex +@cindex @samp{=]} in regex + +If the syntax bit @code{XX} is set, then Regex recognizes equivalence class +expressions inside lists. A @dfn{equivalence class expression} is a set +of collating elements which all belong to the same equivalence class. +You form an equivalence class expression by putting a collating +element between an @dfn{open-equivalence-class operator} and a +@dfn{close-equivalence-class operator}. @samp{[=} represents the +open-equivalence-class operator and @samp{=]} represents the +close-equivalence-class operator. For example, if @samp{a} and @samp{A} +were an equivalence class, then both @samp{[[=a=]]} and @samp{[[=A=]]} +would match both @samp{a} and @samp{A}. If the collating element in an +equivalence class expression isn't part of an equivalence class, then +the matcher considers the equivalence class expression to be a collating +symbol. + +@end ignore + +@node Character Class Operators, Range Operator, , List Operators +@subsection Character Class Operators (@code{[:} @dots{} @code{:]}) + +@cindex character classes +@cindex @samp{[:} in regex +@cindex @samp{:]} in regex + +If the syntax bit @code{RE_CHARACTER_CLASSES} is set, then Regex +recognizes character class expressions inside lists. A @dfn{character +class expression} matches one character from a given class. You form a +character class expression by putting a character class name between an +@dfn{open-character-class operator} (represented by @samp{[:}) and a +@dfn{close-character-class operator} (represented by @samp{:]}). The +character class names and their meanings are: + +@table @code + +@item alnum +letters and digits + +@item alpha +letters + +@item blank +system-dependent; for @sc{gnu}, a space or tab + +@item cntrl +control characters (in the @sc{ascii} encoding, code 0177 and codes +less than 040) + +@item digit +digits + +@item graph +same as @code{print} except omits space + +@item lower +lowercase letters + +@item print +printable characters (in the @sc{ascii} encoding, space +tilde---codes 040 through 0176) + +@item punct +neither control nor alphanumeric characters + +@item space +space, carriage return, newline, vertical tab, and form feed + +@item upper +uppercase letters + +@item xdigit +hexadecimal digits: @code{0}--@code{9}, @code{a}--@code{f}, @code{A}--@code{F} + +@end table + +@noindent +These correspond to the definitions in the C library's @file{} +facility. For example, @samp{[:alpha:]} corresponds to the standard +facility @code{isalpha}. Regex recognizes character class expressions +only inside of lists; so @samp{[[:alpha:]]} matches any letter, but +@samp{[:alpha:]} outside of a bracket expression and not followed by a +repetition operator matches just itself. + +@node Range Operator, , Character Class Operators, List Operators +@subsection The Range Operator (@code{-}) + +Regex recognizes @dfn{range expressions} inside a list. They represent +those characters +that fall between two elements in the current collating sequence. You +form a range expression by putting a @dfn{range operator} between two +@ignore +(If these get implemented, then substitute this for ``characters.'') +of any of the following: characters, collating elements, collating symbols, +and equivalence class expressions. The starting point of the range and +the ending point of the range don't have to be the same kind of item, +e.g., the starting point could be a collating element and the ending +point could be an equivalence class expression. If a range's ending +point is an equivalence class, then all the collating elements in that +class will be in the range. +@end ignore +characters.@footnote{You can't use a character class for the starting +or ending point of a range, since a character class is not a single +character.} @samp{-} represents the range operator. For example, +@samp{a-f} within a list represents all the characters from @samp{a} +through @samp{f} +inclusively. + +If the syntax bit @code{RE_NO_EMPTY_RANGES} is set, then if the range's +ending point collates less than its starting point, the range (and the +regular expression containing it) is invalid. For example, the regular +expression @samp{[z-a]} would be invalid. If this bit isn't set, then +Regex considers such a range to be empty. + +Since @samp{-} represents the range operator, if you want to make a +@samp{-} character itself +a list item, you must do one of the following: + +@itemize @bullet +@item +Put the @samp{-} either first or last in the list. + +@item +Include a range whose starting point collates strictly lower than +@samp{-} and whose ending point collates equal or higher. Unless a +range is the first item in a list, a @samp{-} can't be its starting +point, but @emph{can} be its ending point. That is because Regex +considers @samp{-} to be the range operator unless it is preceded by +another @samp{-}. For example, in the @sc{ascii} encoding, @samp{)}, +@samp{*}, @samp{+}, @samp{,}, @samp{-}, @samp{.}, and @samp{/} are +contiguous characters in the collating sequence. You might think that +@samp{[)-+--/]} has two ranges: @samp{)-+} and @samp{--/}. Rather, it +has the ranges @samp{)-+} and @samp{+--}, plus the character @samp{/}, so +it matches, e.g., @samp{,}, not @samp{.}. + +@item +Put a range whose starting point is @samp{-} first in the list. + +@end itemize + +For example, @samp{[-a-z]} matches a lowercase letter or a hyphen (in +English, in @sc{ascii}). + + +@node Grouping Operators, Back-reference Operator, List Operators, Common Operators +@section Grouping Operators (@code{(} @dots{} @code{)} or @code{\(} @dots{} @code{\)}) + +@kindex ( +@kindex ) +@kindex \( +@kindex \) +@cindex grouping +@cindex subexpressions +@cindex parenthesizing + +A @dfn{group}, also known as a @dfn{subexpression}, consists of an +@dfn{open-group operator}, any number of other operators, and a +@dfn{close-group operator}. Regex treats this sequence as a unit, just +as mathematics and programming languages treat a parenthesized +expression as a unit. + +Therefore, using @dfn{groups}, you can: + +@itemize @bullet +@item +delimit the argument(s) to an alternation operator (@pxref{Alternation +Operator}) or a repetition operator (@pxref{Repetition +Operators}). + +@item +keep track of the indices of the substring that matched a given group. +@xref{Using Registers}, for a precise explanation. +This lets you: + +@itemize @bullet +@item +use the back-reference operator (@pxref{Back-reference Operator}). + +@item +use registers (@pxref{Using Registers}). + +@end itemize + +@end itemize + +If the syntax bit @code{RE_NO_BK_PARENS} is set, then @samp{(} represents +the open-group operator and @samp{)} represents the +close-group operator; otherwise, @samp{\(} and @samp{\)} do. + +If the syntax bit @code{RE_UNMATCHED_RIGHT_PAREN_ORD} is set and a +close-group operator has no matching open-group operator, then Regex +considers it to match @samp{)}. + + +@node Back-reference Operator, Anchoring Operators, Grouping Operators, Common Operators +@section The Back-reference Operator (@dfn{\}@var{digit}) + +@cindex back references + +If the syntax bit @code{RE_NO_BK_REF} isn't set, then Regex recognizes +back references. A back reference matches a specified preceding group. +The back reference operator is represented by @samp{\@var{digit}} +anywhere after the end of a regular expression's @w{@var{digit}-th} +group (@pxref{Grouping Operators}). + +@var{digit} must be between @samp{1} and @samp{9}. The matcher assigns +numbers 1 through 9 to the first nine groups it encounters. By using +one of @samp{\1} through @samp{\9} after the corresponding group's +close-group operator, you can match a substring identical to the +one that the group does. + +Back references match according to the following (in all examples below, +@samp{(} represents the open-group, @samp{)} the close-group, @samp{@{} +the open-interval and @samp{@}} the close-interval operator): + +@itemize @bullet +@item +If the group matches a substring, the back reference matches an +identical substring. For example, @samp{(a)\1} matches @samp{aa} and +@samp{(bana)na\1bo\1} matches @samp{bananabanabobana}. Likewise, +@samp{(.*)\1} matches any (newline-free if the syntax bit +@code{RE_DOT_NEWLINE} isn't set) string that is composed of two +identical halves; the @samp{(.*)} matches the first half and the +@samp{\1} matches the second half. + +@item +If the group matches more than once (as it might if followed +by, e.g., a repetition operator), then the back reference matches the +substring the group @emph{last} matched. For example, +@samp{((a*)b)*\1\2} matches @samp{aabababa}; first @w{group 1} (the +outer one) matches @samp{aab} and @w{group 2} (the inner one) matches +@samp{aa}. Then @w{group 1} matches @samp{ab} and @w{group 2} matches +@samp{a}. So, @samp{\1} matches @samp{ab} and @samp{\2} matches +@samp{a}. + +@item +If the group doesn't participate in a match, i.e., it is part of an +alternative not taken or a repetition operator allows zero repetitions +of it, then the back reference makes the whole match fail. For example, +@samp{(one()|two())-and-(three\2|four\3)} matches @samp{one-and-three} +and @samp{two-and-four}, but not @samp{one-and-four} or +@samp{two-and-three}. For example, if the pattern matches +@samp{one-and-}, then its @w{group 2} matches the empty string and its +@w{group 3} doesn't participate in the match. So, if it then matches +@samp{four}, then when it tries to back reference @w{group 3}---which it +will attempt to do because @samp{\3} follows the @samp{four}---the match +will fail because @w{group 3} didn't participate in the match. + +@end itemize + +You can use a back reference as an argument to a repetition operator. For +example, @samp{(a(b))\2*} matches @samp{a} followed by two or more +@samp{b}s. Similarly, @samp{(a(b))\2@{3@}} matches @samp{abbbb}. + +If there is no preceding @w{@var{digit}-th} subexpression, the regular +expression is invalid. + + +@node Anchoring Operators, , Back-reference Operator, Common Operators +@section Anchoring Operators + +@cindex anchoring +@cindex regexp anchoring + +These operators can constrain a pattern to match only at the beginning or +end of the entire string or at the beginning or end of a line. + +@menu +* Match-beginning-of-line Operator:: ^ +* Match-end-of-line Operator:: $ +@end menu + + +@node Match-beginning-of-line Operator, Match-end-of-line Operator, , Anchoring Operators +@subsection The Match-beginning-of-line Operator (@code{^}) + +@kindex ^ +@cindex beginning-of-line operator +@cindex anchors + +This operator can match the empty string either at the beginning of the +string or after a newline character. Thus, it is said to @dfn{anchor} +the pattern to the beginning of a line. + +In the cases following, @samp{^} represents this operator. (Otherwise, +@samp{^} is ordinary.) + +@itemize @bullet + +@item +It (the @samp{^}) is first in the pattern, as in @samp{^foo}. + +@cnindex RE_CONTEXT_INDEP_ANCHORS @r{(and @samp{^})} +@item +The syntax bit @code{RE_CONTEXT_INDEP_ANCHORS} is set, and it is outside +a bracket expression. + +@cindex open-group operator and @samp{^} +@cindex alternation operator and @samp{^} +@item +It follows an open-group or alternation operator, as in @samp{a\(^b\)} +and @samp{a\|^b}. @xref{Grouping Operators}, and @ref{Alternation +Operator}. + +@end itemize + +These rules imply that some valid patterns containing @samp{^} cannot be +matched; for example, @samp{foo^bar} if @code{RE_CONTEXT_INDEP_ANCHORS} +is set. + +@vindex not_bol @r{field in pattern buffer} +If the @code{not_bol} field is set in the pattern buffer (@pxref{GNU +Pattern Buffers}), then @samp{^} fails to match at the beginning of the +string. @xref{POSIX Matching}, for when you might find this useful. + +@vindex newline_anchor @r{field in pattern buffer} +If the @code{newline_anchor} field is set in the pattern buffer, then +@samp{^} fails to match after a newline. This is useful when you do not +regard the string to be matched as broken into lines. + + +@node Match-end-of-line Operator, , Match-beginning-of-line Operator, Anchoring Operators +@subsection The Match-end-of-line Operator (@code{$}) + +@kindex $ +@cindex end-of-line operator +@cindex anchors + +This operator can match the empty string either at the end of +the string or before a newline character in the string. Thus, it is +said to @dfn{anchor} the pattern to the end of a line. + +It is always represented by @samp{$}. For example, @samp{foo$} usually +matches, e.g., @samp{foo} and, e.g., the first three characters of +@samp{foo\nbar}. + +Its interaction with the syntax bits and pattern buffer fields is +exactly the dual of @samp{^}'s; see the previous section. (That is, +``beginning'' becomes ``end'', ``next'' becomes ``previous'', and +``after'' becomes ``before''.) + + +@node GNU Operators, GNU Emacs Operators, Common Operators, Top +@chapter GNU Operators + +Following are operators that @sc{gnu} defines (and @sc{posix} doesn't). + +@menu +* Word Operators:: +* Buffer Operators:: +@end menu + +@node Word Operators, Buffer Operators, , GNU Operators +@section Word Operators + +The operators in this section require Regex to recognize parts of words. +Regex uses a syntax table to determine whether or not a character is +part of a word, i.e., whether or not it is @dfn{word-constituent}. + +@menu +* Non-Emacs Syntax Tables:: +* Match-word-boundary Operator:: \b +* Match-within-word Operator:: \B +* Match-beginning-of-word Operator:: \< +* Match-end-of-word Operator:: \> +* Match-word-constituent Operator:: \w +* Match-non-word-constituent Operator:: \W +@end menu + +@node Non-Emacs Syntax Tables, Match-word-boundary Operator, , Word Operators +@subsection Non-Emacs Syntax Tables + +A @dfn{syntax table} is an array indexed by the characters in your +character set. In the @sc{ascii} encoding, therefore, a syntax table +has 256 elements. Regex always uses a @code{char *} variable +@code{re_syntax_table} as its syntax table. In some cases, it +initializes this variable and in others it expects you to initialize it. + +@itemize @bullet +@item +If Regex is compiled with the preprocessor symbols @code{emacs} and +@code{SYNTAX_TABLE} both undefined, then Regex allocates +@code{re_syntax_table} and initializes an element @var{i} either to +@code{Sword} (which it defines) if @var{i} is a letter, number, or +@samp{_}, or to zero if it's not. + +@item +If Regex is compiled with @code{emacs} undefined but @code{SYNTAX_TABLE} +defined, then Regex expects you to define a @code{char *} variable +@code{re_syntax_table} to be a valid syntax table. + +@item +@xref{Emacs Syntax Tables}, for what happens when Regex is compiled with +the preprocessor symbol @code{emacs} defined. + +@end itemize + +@node Match-word-boundary Operator, Match-within-word Operator, Non-Emacs Syntax Tables, Word Operators +@subsection The Match-word-boundary Operator (@code{\b}) + +@cindex @samp{\b} +@cindex word boundaries, matching + +This operator (represented by @samp{\b}) matches the empty string at +either the beginning or the end of a word. For example, @samp{\brat\b} +matches the separate word @samp{rat}. + +@node Match-within-word Operator, Match-beginning-of-word Operator, Match-word-boundary Operator, Word Operators +@subsection The Match-within-word Operator (@code{\B}) + +@cindex @samp{\B} + +This operator (represented by @samp{\B}) matches the empty string within +a word. For example, @samp{c\Brat\Be} matches @samp{crate}, but +@samp{dirty \Brat} doesn't match @samp{dirty rat}. + +@node Match-beginning-of-word Operator, Match-end-of-word Operator, Match-within-word Operator, Word Operators +@subsection The Match-beginning-of-word Operator (@code{\<}) + +@cindex @samp{\<} + +This operator (represented by @samp{\<}) matches the empty string at the +beginning of a word. + +@node Match-end-of-word Operator, Match-word-constituent Operator, Match-beginning-of-word Operator, Word Operators +@subsection The Match-end-of-word Operator (@code{\>}) + +@cindex @samp{\>} + +This operator (represented by @samp{\>}) matches the empty string at the +end of a word. + +@node Match-word-constituent Operator, Match-non-word-constituent Operator, Match-end-of-word Operator, Word Operators +@subsection The Match-word-constituent Operator (@code{\w}) + +@cindex @samp{\w} + +This operator (represented by @samp{\w}) matches any word-constituent +character. + +@node Match-non-word-constituent Operator, , Match-word-constituent Operator, Word Operators +@subsection The Match-non-word-constituent Operator (@code{\W}) + +@cindex @samp{\W} + +This operator (represented by @samp{\W}) matches any character that is +not word-constituent. + + +@node Buffer Operators, , Word Operators, GNU Operators +@section Buffer Operators + +Following are operators which work on buffers. In Emacs, a @dfn{buffer} +is, naturally, an Emacs buffer. For other programs, Regex considers the +entire string to be matched as the buffer. + +@menu +* Match-beginning-of-buffer Operator:: \` +* Match-end-of-buffer Operator:: \' +@end menu + + +@node Match-beginning-of-buffer Operator, Match-end-of-buffer Operator, , Buffer Operators +@subsection The Match-beginning-of-buffer Operator (@code{\`}) + +@cindex @samp{\`} + +This operator (represented by @samp{\`}) matches the empty string at the +beginning of the buffer. + +@node Match-end-of-buffer Operator, , Match-beginning-of-buffer Operator, Buffer Operators +@subsection The Match-end-of-buffer Operator (@code{\'}) + +@cindex @samp{\'} + +This operator (represented by @samp{\'}) matches the empty string at the +end of the buffer. + + +@node GNU Emacs Operators, What Gets Matched?, GNU Operators, Top +@chapter GNU Emacs Operators + +Following are operators that @sc{gnu} defines (and @sc{posix} doesn't) +that you can use only when Regex is compiled with the preprocessor +symbol @code{emacs} defined. + +@menu +* Syntactic Class Operators:: +@end menu + + +@node Syntactic Class Operators, , , GNU Emacs Operators +@section Syntactic Class Operators + +The operators in this section require Regex to recognize the syntactic +classes of characters. Regex uses a syntax table to determine this. + +@menu +* Emacs Syntax Tables:: +* Match-syntactic-class Operator:: \sCLASS +* Match-not-syntactic-class Operator:: \SCLASS +@end menu + +@node Emacs Syntax Tables, Match-syntactic-class Operator, , Syntactic Class Operators +@subsection Emacs Syntax Tables + +A @dfn{syntax table} is an array indexed by the characters in your +character set. In the @sc{ascii} encoding, therefore, a syntax table +has 256 elements. + +If Regex is compiled with the preprocessor symbol @code{emacs} defined, +then Regex expects you to define and initialize the variable +@code{re_syntax_table} to be an Emacs syntax table. Emacs' syntax +tables are more complicated than Regex's own (@pxref{Non-Emacs Syntax +Tables}). @xref{Syntax, , Syntax, emacs, The GNU Emacs User's Manual}, +for a description of Emacs' syntax tables. + +@node Match-syntactic-class Operator, Match-not-syntactic-class Operator, Emacs Syntax Tables, Syntactic Class Operators +@subsection The Match-syntactic-class Operator (@code{\s}@var{class}) + +@cindex @samp{\s} + +This operator matches any character whose syntactic class is represented +by a specified character. @samp{\s@var{class}} represents this operator +where @var{class} is the character representing the syntactic class you +want. For example, @samp{w} represents the syntactic +class of word-constituent characters, so @samp{\sw} matches any +word-constituent character. + +@node Match-not-syntactic-class Operator, , Match-syntactic-class Operator, Syntactic Class Operators +@subsection The Match-not-syntactic-class Operator (@code{\S}@var{class}) + +@cindex @samp{\S} + +This operator is similar to the match-syntactic-class operator except +that it matches any character whose syntactic class is @emph{not} +represented by the specified character. @samp{\S@var{class}} represents +this operator. For example, @samp{w} represents the syntactic class of +word-constituent characters, so @samp{\Sw} matches any character that is +not word-constituent. + + +@node What Gets Matched?, Programming with Regex, GNU Emacs Operators, Top +@chapter What Gets Matched? + +Regex usually matches strings according to the ``leftmost longest'' +rule; that is, it chooses the longest of the leftmost matches. This +does not mean that for a regular expression containing subexpressions +that it simply chooses the longest match for each subexpression, left to +right; the overall match must also be the longest possible one. + +For example, @samp{(ac*)(c*d[ac]*)\1} matches @samp{acdacaaa}, not +@samp{acdac}, as it would if it were to choose the longest match for the +first subexpression. + + +@node Programming with Regex, Copying, What Gets Matched?, Top +@chapter Programming with Regex + +Here we describe how you use the Regex data structures and functions in +C programs. Regex has three interfaces: one designed for @sc{gnu}, one +compatible with @sc{posix} and one compatible with Berkeley @sc{unix}. + +@menu +* GNU Regex Functions:: +* POSIX Regex Functions:: +* BSD Regex Functions:: +@end menu + + +@node GNU Regex Functions, POSIX Regex Functions, , Programming with Regex +@section GNU Regex Functions + +If you're writing code that doesn't need to be compatible with either +@sc{posix} or Berkeley @sc{unix}, you can use these functions. They +provide more options than the other interfaces. + +@menu +* GNU Pattern Buffers:: The re_pattern_buffer type. +* GNU Regular Expression Compiling:: re_compile_pattern () +* GNU Matching:: re_match () +* GNU Searching:: re_search () +* Matching/Searching with Split Data:: re_match_2 (), re_search_2 () +* Searching with Fastmaps:: re_compile_fastmap () +* GNU Translate Tables:: The `translate' field. +* Using Registers:: The re_registers type and related fns. +* Freeing GNU Pattern Buffers:: regfree () +@end menu + + +@node GNU Pattern Buffers, GNU Regular Expression Compiling, , GNU Regex Functions +@subsection GNU Pattern Buffers + +@cindex pattern buffer, definition of +@tindex re_pattern_buffer @r{definition} +@tindex struct re_pattern_buffer @r{definition} + +To compile, match, or search for a given regular expression, you must +supply a pattern buffer. A @dfn{pattern buffer} holds one compiled +regular expression.@footnote{Regular expressions are also referred to as +``patterns,'' hence the name ``pattern buffer.''} + +You can have several different pattern buffers simultaneously, each +holding a compiled pattern for a different regular expression. + +@file{regex.h} defines the pattern buffer @code{struct} as follows: + +@example + /* Space that holds the compiled pattern. It is declared as + `unsigned char *' because its elements are + sometimes used as array indexes. */ + unsigned char *buffer; + + /* Number of bytes to which `buffer' points. */ + unsigned long allocated; + + /* Number of bytes actually used in `buffer'. */ + unsigned long used; + + /* Syntax setting with which the pattern was compiled. */ + reg_syntax_t syntax; + + /* Pointer to a fastmap, if any, otherwise zero. re_search uses + the fastmap, if there is one, to skip over impossible + starting points for matches. */ + char *fastmap; + + /* Either a translate table to apply to all characters before + comparing them, or zero for no translation. The translation + is applied to a pattern when it is compiled and to a string + when it is matched. */ + char *translate; + + /* Number of subexpressions found by the compiler. */ + size_t re_nsub; + + /* Zero if this pattern cannot match the empty string, one else. + Well, in truth it's used only in `re_search_2', to see + whether or not we should use the fastmap, so we don't set + this absolutely perfectly; see `re_compile_fastmap' (the + `duplicate' case). */ + unsigned can_be_null : 1; + + /* If REGS_UNALLOCATED, allocate space in the `regs' structure + for `max (RE_NREGS, re_nsub + 1)' groups. + If REGS_REALLOCATE, reallocate space if necessary. + If REGS_FIXED, use what's there. */ +#define REGS_UNALLOCATED 0 +#define REGS_REALLOCATE 1 +#define REGS_FIXED 2 + unsigned regs_allocated : 2; + + /* Set to zero when `regex_compile' compiles a pattern; set to one + by `re_compile_fastmap' if it updates the fastmap. */ + unsigned fastmap_accurate : 1; + + /* If set, `re_match_2' does not return information about + subexpressions. */ + unsigned no_sub : 1; + + /* If set, a beginning-of-line anchor doesn't match at the + beginning of the string. */ + unsigned not_bol : 1; + + /* Similarly for an end-of-line anchor. */ + unsigned not_eol : 1; + + /* If true, an anchor at a newline matches. */ + unsigned newline_anchor : 1; + +@end example + + +@node GNU Regular Expression Compiling, GNU Matching, GNU Pattern Buffers, GNU Regex Functions +@subsection GNU Regular Expression Compiling + +In @sc{gnu}, you can both match and search for a given regular +expression. To do either, you must first compile it in a pattern buffer +(@pxref{GNU Pattern Buffers}). + +@cindex syntax initialization +@vindex re_syntax_options @r{initialization} +Regular expressions match according to the syntax with which they were +compiled; with @sc{gnu}, you indicate what syntax you want by setting +the variable @code{re_syntax_options} (declared in @file{regex.h} and +defined in @file{regex.c}) before calling the compiling function, +@code{re_compile_pattern} (see below). @xref{Syntax Bits}, and +@ref{Predefined Syntaxes}. + +You can change the value of @code{re_syntax_options} at any time. +Usually, however, you set its value once and then never change it. + +@cindex pattern buffer initialization +@code{re_compile_pattern} takes a pattern buffer as an argument. You +must initialize the following fields: + +@table @code + +@item translate @r{initialization} + +@item translate +@vindex translate @r{initialization} +Initialize this to point to a translate table if you want one, or to +zero if you don't. We explain translate tables in @ref{GNU Translate +Tables}. + +@item fastmap +@vindex fastmap @r{initialization} +Initialize this to nonzero if you want a fastmap, or to zero if you +don't. + +@item buffer +@itemx allocated +@vindex buffer @r{initialization} +@vindex allocated @r{initialization} +@findex malloc +If you want @code{re_compile_pattern} to allocate memory for the +compiled pattern, set both of these to zero. If you have an existing +block of memory (allocated with @code{malloc}) you want Regex to use, +set @code{buffer} to its address and @code{allocated} to its size (in +bytes). + +@code{re_compile_pattern} uses @code{realloc} to extend the space for +the compiled pattern as necessary. + +@end table + +To compile a pattern buffer, use: + +@findex re_compile_pattern +@example +char * +re_compile_pattern (const char *@var{regex}, const int @var{regex_size}, + struct re_pattern_buffer *@var{pattern_buffer}) +@end example + +@noindent +@var{regex} is the regular expression's address, @var{regex_size} is its +length, and @var{pattern_buffer} is the pattern buffer's address. + +If @code{re_compile_pattern} successfully compiles the regular +expression, it returns zero and sets @code{*@var{pattern_buffer}} to the +compiled pattern. It sets the pattern buffer's fields as follows: + +@table @code +@item buffer +@vindex buffer @r{field, set by @code{re_compile_pattern}} +to the compiled pattern. + +@item used +@vindex used @r{field, set by @code{re_compile_pattern}} +to the number of bytes the compiled pattern in @code{buffer} occupies. + +@item syntax +@vindex syntax @r{field, set by @code{re_compile_pattern}} +to the current value of @code{re_syntax_options}. + +@item re_nsub +@vindex re_nsub @r{field, set by @code{re_compile_pattern}} +to the number of subexpressions in @var{regex}. + +@item fastmap_accurate +@vindex fastmap_accurate @r{field, set by @code{re_compile_pattern}} +to zero on the theory that the pattern you're compiling is different +than the one previously compiled into @code{buffer}; in that case (since +you can't make a fastmap without a compiled pattern), +@code{fastmap} would either contain an incompatible fastmap, or nothing +at all. + +@c xx what else? +@end table + +If @code{re_compile_pattern} can't compile @var{regex}, it returns an +error string corresponding to one of the errors listed in @ref{POSIX +Regular Expression Compiling}. + + +@node GNU Matching, GNU Searching, GNU Regular Expression Compiling, GNU Regex Functions +@subsection GNU Matching + +@cindex matching with GNU functions + +Matching the @sc{gnu} way means trying to match as much of a string as +possible starting at a position within it you specify. Once you've compiled +a pattern into a pattern buffer (@pxref{GNU Regular Expression +Compiling}), you can ask the matcher to match that pattern against a +string using: + +@findex re_match +@example +int +re_match (struct re_pattern_buffer *@var{pattern_buffer}, + const char *@var{string}, const int @var{size}, + const int @var{start}, struct re_registers *@var{regs}) +@end example + +@noindent +@var{pattern_buffer} is the address of a pattern buffer containing a +compiled pattern. @var{string} is the string you want to match; it can +contain newline and null characters. @var{size} is the length of that +string. @var{start} is the string index at which you want to +begin matching; the first character of @var{string} is at index zero. +@xref{Using Registers}, for a explanation of @var{regs}; you can safely +pass zero. + +@code{re_match} matches the regular expression in @var{pattern_buffer} +against the string @var{string} according to the syntax in +@var{pattern_buffers}'s @code{syntax} field. (@xref{GNU Regular +Expression Compiling}, for how to set it.) The function returns +@math{-1} if the compiled pattern does not match any part of +@var{string} and @math{-2} if an internal error happens; otherwise, it +returns how many (possibly zero) characters of @var{string} the pattern +matched. + +An example: suppose @var{pattern_buffer} points to a pattern buffer +containing the compiled pattern for @samp{a*}, and @var{string} points +to @samp{aaaaab} (whereupon @var{size} should be 6). Then if @var{start} +is 2, @code{re_match} returns 3, i.e., @samp{a*} would have matched the +last three @samp{a}s in @var{string}. If @var{start} is 0, +@code{re_match} returns 5, i.e., @samp{a*} would have matched all the +@samp{a}s in @var{string}. If @var{start} is either 5 or 6, it returns +zero. + +If @var{start} is not between zero and @var{size}, then +@code{re_match} returns @math{-1}. + + +@node GNU Searching, Matching/Searching with Split Data, GNU Matching, GNU Regex Functions +@subsection GNU Searching + +@cindex searching with GNU functions + +@dfn{Searching} means trying to match starting at successive positions +within a string. The function @code{re_search} does this. + +Before calling @code{re_search}, you must compile your regular +expression. @xref{GNU Regular Expression Compiling}. + +Here is the function declaration: + +@findex re_search +@example +int +re_search (struct re_pattern_buffer *@var{pattern_buffer}, + const char *@var{string}, const int @var{size}, + const int @var{start}, const int @var{range}, + struct re_registers *@var{regs}) +@end example + +@noindent +@vindex start @r{argument to @code{re_search}} +@vindex range @r{argument to @code{re_search}} +whose arguments are the same as those to @code{re_match} (@pxref{GNU +Matching}) except that the two arguments @var{start} and @var{range} +replace @code{re_match}'s argument @var{start}. + +If @var{range} is positive, then @code{re_search} attempts a match +starting first at index @var{start}, then at @math{@var{start} + 1} if +that fails, and so on, up to @math{@var{start} + @var{range}}; if +@var{range} is negative, then it attempts a match starting first at +index @var{start}, then at @math{@var{start} -1} if that fails, and so +on. + +If @var{start} is not between zero and @var{size}, then @code{re_search} +returns @math{-1}. When @var{range} is positive, @code{re_search} +adjusts @var{range} so that @math{@var{start} + @var{range} - 1} is +between zero and @var{size}, if necessary; that way it won't search +outside of @var{string}. Similarly, when @var{range} is negative, +@code{re_search} adjusts @var{range} so that @math{@var{start} + +@var{range} + 1} is between zero and @var{size}, if necessary. + +If the @code{fastmap} field of @var{pattern_buffer} is zero, +@code{re_search} matches starting at consecutive positions; otherwise, +it uses @code{fastmap} to make the search more efficient. +@xref{Searching with Fastmaps}. + +If no match is found, @code{re_search} returns @math{-1}. If +a match is found, it returns the index where the match began. If an +internal error happens, it returns @math{-2}. + + +@node Matching/Searching with Split Data, Searching with Fastmaps, GNU Searching, GNU Regex Functions +@subsection Matching and Searching with Split Data + +Using the functions @code{re_match_2} and @code{re_search_2}, you can +match or search in data that is divided into two strings. + +The function: + +@findex re_match_2 +@example +int +re_match_2 (struct re_pattern_buffer *@var{buffer}, + const char *@var{string1}, const int @var{size1}, + const char *@var{string2}, const int @var{size2}, + const int @var{start}, + struct re_registers *@var{regs}, + const int @var{stop}) +@end example + +@noindent +is similar to @code{re_match} (@pxref{GNU Matching}) except that you +pass @emph{two} data strings and sizes, and an index @var{stop} beyond +which you don't want the matcher to try matching. As with +@code{re_match}, if it succeeds, @code{re_match_2} returns how many +characters of @var{string} it matched. Regard @var{string1} and +@var{string2} as concatenated when you set the arguments @var{start} and +@var{stop} and use the contents of @var{regs}; @code{re_match_2} never +returns a value larger than @math{@var{size1} + @var{size2}}. + +The function: + +@findex re_search_2 +@example +int +re_search_2 (struct re_pattern_buffer *@var{buffer}, + const char *@var{string1}, const int @var{size1}, + const char *@var{string2}, const int @var{size2}, + const int @var{start}, const int @var{range}, + struct re_registers *@var{regs}, + const int @var{stop}) +@end example + +@noindent +is similarly related to @code{re_search}. + + +@node Searching with Fastmaps, GNU Translate Tables, Matching/Searching with Split Data, GNU Regex Functions +@subsection Searching with Fastmaps + +@cindex fastmaps +If you're searching through a long string, you should use a fastmap. +Without one, the searcher tries to match at consecutive positions in the +string. Generally, most of the characters in the string could not start +a match. It takes much longer to try matching at a given position in the +string than it does to check in a table whether or not the character at +that position could start a match. A @dfn{fastmap} is such a table. + +More specifically, a fastmap is an array indexed by the characters in +your character set. Under the @sc{ascii} encoding, therefore, a fastmap +has 256 elements. If you want the searcher to use a fastmap with a +given pattern buffer, you must allocate the array and assign the array's +address to the pattern buffer's @code{fastmap} field. You either can +compile the fastmap yourself or have @code{re_search} do it for you; +when @code{fastmap} is nonzero, it automatically compiles a fastmap the +first time you search using a particular compiled pattern. + +To compile a fastmap yourself, use: + +@findex re_compile_fastmap +@example +int +re_compile_fastmap (struct re_pattern_buffer *@var{pattern_buffer}) +@end example + +@noindent +@var{pattern_buffer} is the address of a pattern buffer. If the +character @var{c} could start a match for the pattern, +@code{re_compile_fastmap} makes +@code{@var{pattern_buffer}->fastmap[@var{c}]} nonzero. It returns +@math{0} if it can compile a fastmap and @math{-2} if there is an +internal error. For example, if @samp{|} is the alternation operator +and @var{pattern_buffer} holds the compiled pattern for @samp{a|b}, then +@code{re_compile_fastmap} sets @code{fastmap['a']} and +@code{fastmap['b']} (and no others). + +@code{re_search} uses a fastmap as it moves along in the string: it +checks the string's characters until it finds one that's in the fastmap. +Then it tries matching at that character. If the match fails, it +repeats the process. So, by using a fastmap, @code{re_search} doesn't +waste time trying to match at positions in the string that couldn't +start a match. + +If you don't want @code{re_search} to use a fastmap, +store zero in the @code{fastmap} field of the pattern buffer before +calling @code{re_search}. + +Once you've initialized a pattern buffer's @code{fastmap} field, you +need never do so again---even if you compile a new pattern in +it---provided the way the field is set still reflects whether or not you +want a fastmap. @code{re_search} will still either do nothing if +@code{fastmap} is null or, if it isn't, compile a new fastmap for the +new pattern. + +@node GNU Translate Tables, Using Registers, Searching with Fastmaps, GNU Regex Functions +@subsection GNU Translate Tables + +If you set the @code{translate} field of a pattern buffer to a translate +table, then the @sc{gnu} Regex functions to which you've passed that +pattern buffer use it to apply a simple transformation +to all the regular expression and string characters at which they look. + +A @dfn{translate table} is an array indexed by the characters in your +character set. Under the @sc{ascii} encoding, therefore, a translate +table has 256 elements. The array's elements are also characters in +your character set. When the Regex functions see a character @var{c}, +they use @code{translate[@var{c}]} in its place, with one exception: the +character after a @samp{\} is not translated. (This ensures that, the +operators, e.g., @samp{\B} and @samp{\b}, are always distinguishable.) + +For example, a table that maps all lowercase letters to the +corresponding uppercase ones would cause the matcher to ignore +differences in case.@footnote{A table that maps all uppercase letters to +the corresponding lowercase ones would work just as well for this +purpose.} Such a table would map all characters except lowercase letters +to themselves, and lowercase letters to the corresponding uppercase +ones. Under the @sc{ascii} encoding, here's how you could initialize +such a table (we'll call it @code{case_fold}): + +@example +for (i = 0; i < 256; i++) + case_fold[i] = i; +for (i = 'a'; i <= 'z'; i++) + case_fold[i] = i - ('a' - 'A'); +@end example + +You tell Regex to use a translate table on a given pattern buffer by +assigning that table's address to the @code{translate} field of that +buffer. If you don't want Regex to do any translation, put zero into +this field. You'll get weird results if you change the table's contents +anytime between compiling the pattern buffer, compiling its fastmap, and +matching or searching with the pattern buffer. + +@node Using Registers, Freeing GNU Pattern Buffers, GNU Translate Tables, GNU Regex Functions +@subsection Using Registers + +A group in a regular expression can match a (posssibly empty) substring +of the string that regular expression as a whole matched. The matcher +remembers the beginning and end of the substring matched by +each group. + +To find out what they matched, pass a nonzero @var{regs} argument to a +@sc{gnu} matching or searching function (@pxref{GNU Matching} and +@ref{GNU Searching}), i.e., the address of a structure of this type, as +defined in @file{regex.h}: + +@c We don't bother to include this directly from regex.h, +@c since it changes so rarely. +@example +@tindex re_registers +@vindex num_regs @r{in @code{struct re_registers}} +@vindex start @r{in @code{struct re_registers}} +@vindex end @r{in @code{struct re_registers}} +struct re_registers +@{ + unsigned num_regs; + regoff_t *start; + regoff_t *end; +@}; +@end example + +Except for (possibly) the @var{num_regs}'th element (see below), the +@var{i}th element of the @code{start} and @code{end} arrays records +information about the @var{i}th group in the pattern. (They're declared +as C pointers, but this is only because not all C compilers accept +zero-length arrays; conceptually, it is simplest to think of them as +arrays.) + +The @code{start} and @code{end} arrays are allocated in various ways, +depending on the value of the @code{regs_allocated} +@vindex regs_allocated +field in the pattern buffer passed to the matcher. + +The simplest and perhaps most useful is to let the matcher (re)allocate +enough space to record information for all the groups in the regular +expression. If @code{regs_allocated} is @code{REGS_UNALLOCATED}, +@vindex REGS_UNALLOCATED +the matcher allocates @math{1 + @var{re_nsub}} (another field in the +pattern buffer; @pxref{GNU Pattern Buffers}). The extra element is set +to @math{-1}, and sets @code{regs_allocated} to @code{REGS_REALLOCATE}. +@vindex REGS_REALLOCATE +Then on subsequent calls with the same pattern buffer and @var{regs} +arguments, the matcher reallocates more space if necessary. + +It would perhaps be more logical to make the @code{regs_allocated} field +part of the @code{re_registers} structure, instead of part of the +pattern buffer. But in that case the caller would be forced to +initialize the structure before passing it. Much existing code doesn't +do this initialization, and it's arguably better to avoid it anyway. + +@code{re_compile_pattern} sets @code{regs_allocated} to +@code{REGS_UNALLOCATED}, +so if you use the GNU regular expression +functions, you get this behavior by default. + +xx document re_set_registers + +@sc{posix}, on the other hand, requires a different interface: the +caller is supposed to pass in a fixed-length array which the matcher +fills. Therefore, if @code{regs_allocated} is @code{REGS_FIXED} +@vindex REGS_FIXED +the matcher simply fills that array. + +The following examples illustrate the information recorded in the +@code{re_registers} structure. (In all of them, @samp{(} represents the +open-group and @samp{)} the close-group operator. The first character +in the string @var{string} is at index 0.) + +@c xx i'm not sure this is all true anymore. + +@itemize @bullet + +@item +If the regular expression has an @w{@var{i}-th} +group not contained within another group that matches a +substring of @var{string}, then the function sets +@code{@w{@var{regs}->}start[@var{i}]} to the index in @var{string} where +the substring matched by the @w{@var{i}-th} group begins, and +@code{@w{@var{regs}->}end[@var{i}]} to the index just beyond that +substring's end. The function sets @code{@w{@var{regs}->}start[0]} and +@code{@w{@var{regs}->}end[0]} to analogous information about the entire +pattern. + +For example, when you match @samp{((a)(b))} against @samp{ab}, you get: + +@itemize +@item +0 in @code{@w{@var{regs}->}start[0]} and 2 in @code{@w{@var{regs}->}end[0]} + +@item +0 in @code{@w{@var{regs}->}start[1]} and 2 in @code{@w{@var{regs}->}end[1]} + +@item +0 in @code{@w{@var{regs}->}start[2]} and 1 in @code{@w{@var{regs}->}end[2]} + +@item +1 in @code{@w{@var{regs}->}start[3]} and 2 in @code{@w{@var{regs}->}end[3]} +@end itemize + +@item +If a group matches more than once (as it might if followed by, +e.g., a repetition operator), then the function reports the information +about what the group @emph{last} matched. + +For example, when you match the pattern @samp{(a)*} against the string +@samp{aa}, you get: + +@itemize +@item +0 in @code{@w{@var{regs}->}start[0]} and 2 in @code{@w{@var{regs}->}end[0]} + +@item +1 in @code{@w{@var{regs}->}start[1]} and 2 in @code{@w{@var{regs}->}end[1]} +@end itemize + +@item +If the @w{@var{i}-th} group does not participate in a +successful match, e.g., it is an alternative not taken or a +repetition operator allows zero repetitions of it, then the function +sets @code{@w{@var{regs}->}start[@var{i}]} and +@code{@w{@var{regs}->}end[@var{i}]} to @math{-1}. + +For example, when you match the pattern @samp{(a)*b} against +the string @samp{b}, you get: + +@itemize +@item +0 in @code{@w{@var{regs}->}start[0]} and 1 in @code{@w{@var{regs}->}end[0]} + +@item +@math{-1} in @code{@w{@var{regs}->}start[1]} and @math{-1} in @code{@w{@var{regs}->}end[1]} +@end itemize + +@item +If the @w{@var{i}-th} group matches a zero-length string, then the +function sets @code{@w{@var{regs}->}start[@var{i}]} and +@code{@w{@var{regs}->}end[@var{i}]} to the index just beyond that +zero-length string. + +For example, when you match the pattern @samp{(a*)b} against the string +@samp{b}, you get: + +@itemize +@item +0 in @code{@w{@var{regs}->}start[0]} and 1 in @code{@w{@var{regs}->}end[0]} + +@item +0 in @code{@w{@var{regs}->}start[1]} and 0 in @code{@w{@var{regs}->}end[1]} +@end itemize + +@ignore +The function sets @code{@w{@var{regs}->}start[0]} and +@code{@w{@var{regs}->}end[0]} to analogous information about the entire +pattern. + +For example, when you match the pattern @samp{(a*)} against the empty +string, you get: + +@itemize +@item +0 in @code{@w{@var{regs}->}start[0]} and 0 in @code{@w{@var{regs}->}end[0]} + +@item +0 in @code{@w{@var{regs}->}start[1]} and 0 in @code{@w{@var{regs}->}end[1]} +@end itemize +@end ignore + +@item +If an @w{@var{i}-th} group contains a @w{@var{j}-th} group +in turn not contained within any other group within group @var{i} and +the function reports a match of the @w{@var{i}-th} group, then it +records in @code{@w{@var{regs}->}start[@var{j}]} and +@code{@w{@var{regs}->}end[@var{j}]} the last match (if it matched) of +the @w{@var{j}-th} group. + +For example, when you match the pattern @samp{((a*)b)*} against the +string @samp{abb}, @w{group 2} last matches the empty string, so you +get what it previously matched: + +@itemize +@item +0 in @code{@w{@var{regs}->}start[0]} and 3 in @code{@w{@var{regs}->}end[0]} + +@item +2 in @code{@w{@var{regs}->}start[1]} and 3 in @code{@w{@var{regs}->}end[1]} + +@item +2 in @code{@w{@var{regs}->}start[2]} and 2 in @code{@w{@var{regs}->}end[2]} +@end itemize + +When you match the pattern @samp{((a)*b)*} against the string +@samp{abb}, @w{group 2} doesn't participate in the last match, so you +get: + +@itemize +@item +0 in @code{@w{@var{regs}->}start[0]} and 3 in @code{@w{@var{regs}->}end[0]} + +@item +2 in @code{@w{@var{regs}->}start[1]} and 3 in @code{@w{@var{regs}->}end[1]} + +@item +0 in @code{@w{@var{regs}->}start[2]} and 1 in @code{@w{@var{regs}->}end[2]} +@end itemize + +@item +If an @w{@var{i}-th} group contains a @w{@var{j}-th} group +in turn not contained within any other group within group @var{i} +and the function sets +@code{@w{@var{regs}->}start[@var{i}]} and +@code{@w{@var{regs}->}end[@var{i}]} to @math{-1}, then it also sets +@code{@w{@var{regs}->}start[@var{j}]} and +@code{@w{@var{regs}->}end[@var{j}]} to @math{-1}. + +For example, when you match the pattern @samp{((a)*b)*c} against the +string @samp{c}, you get: + +@itemize +@item +0 in @code{@w{@var{regs}->}start[0]} and 1 in @code{@w{@var{regs}->}end[0]} + +@item +@math{-1} in @code{@w{@var{regs}->}start[1]} and @math{-1} in @code{@w{@var{regs}->}end[1]} + +@item +@math{-1} in @code{@w{@var{regs}->}start[2]} and @math{-1} in @code{@w{@var{regs}->}end[2]} +@end itemize + +@end itemize + +@node Freeing GNU Pattern Buffers, , Using Registers, GNU Regex Functions +@subsection Freeing GNU Pattern Buffers + +To free any allocated fields of a pattern buffer, you can use the +@sc{posix} function described in @ref{Freeing POSIX Pattern Buffers}, +since the type @code{regex_t}---the type for @sc{posix} pattern +buffers---is equivalent to the type @code{re_pattern_buffer}. After +freeing a pattern buffer, you need to again compile a regular expression +in it (@pxref{GNU Regular Expression Compiling}) before passing it to +a matching or searching function. + + +@node POSIX Regex Functions, BSD Regex Functions, GNU Regex Functions, Programming with Regex +@section POSIX Regex Functions + +If you're writing code that has to be @sc{posix} compatible, you'll need +to use these functions. Their interfaces are as specified by @sc{posix}, +draft 1003.2/D11.2. + +@menu +* POSIX Pattern Buffers:: The regex_t type. +* POSIX Regular Expression Compiling:: regcomp () +* POSIX Matching:: regexec () +* Reporting Errors:: regerror () +* Using Byte Offsets:: The regmatch_t type. +* Freeing POSIX Pattern Buffers:: regfree () +@end menu + + +@node POSIX Pattern Buffers, POSIX Regular Expression Compiling, , POSIX Regex Functions +@subsection POSIX Pattern Buffers + +To compile or match a given regular expression the @sc{posix} way, you +must supply a pattern buffer exactly the way you do for @sc{gnu} +(@pxref{GNU Pattern Buffers}). @sc{posix} pattern buffers have type +@code{regex_t}, which is equivalent to the @sc{gnu} pattern buffer +type @code{re_pattern_buffer}. + + +@node POSIX Regular Expression Compiling, POSIX Matching, POSIX Pattern Buffers, POSIX Regex Functions +@subsection POSIX Regular Expression Compiling + +With @sc{posix}, you can only search for a given regular expression; you +can't match it. To do this, you must first compile it in a +pattern buffer, using @code{regcomp}. + +@ignore +Before calling @code{regcomp}, you must initialize this pattern buffer +as you do for @sc{gnu} (@pxref{GNU Regular Expression Compiling}). See +below, however, for how to choose a syntax with which to compile. +@end ignore + +To compile a pattern buffer, use: + +@findex regcomp +@example +int +regcomp (regex_t *@var{preg}, const char *@var{regex}, int @var{cflags}) +@end example + +@noindent +@var{preg} is the initialized pattern buffer's address, @var{regex} is +the regular expression's address, and @var{cflags} is the compilation +flags, which Regex considers as a collection of bits. Here are the +valid bits, as defined in @file{regex.h}: + +@table @code + +@item REG_EXTENDED +@vindex REG_EXTENDED +says to use @sc{posix} Extended Regular Expression syntax; if this isn't +set, then says to use @sc{posix} Basic Regular Expression syntax. +@code{regcomp} sets @var{preg}'s @code{syntax} field accordingly. + +@item REG_ICASE +@vindex REG_ICASE +@cindex ignoring case +says to ignore case; @code{regcomp} sets @var{preg}'s @code{translate} +field to a translate table which ignores case, replacing anything you've +put there before. + +@item REG_NOSUB +@vindex REG_NOSUB +says to set @var{preg}'s @code{no_sub} field; @pxref{POSIX Matching}, +for what this means. + +@item REG_NEWLINE +@vindex REG_NEWLINE +says that a: + +@itemize @bullet + +@item +match-any-character operator (@pxref{Match-any-character +Operator}) doesn't match a newline. + +@item +nonmatching list not containing a newline (@pxref{List +Operators}) matches a newline. + +@item +match-beginning-of-line operator (@pxref{Match-beginning-of-line +Operator}) matches the empty string immediately after a newline, +regardless of how @code{REG_NOTBOL} is set (@pxref{POSIX Matching}, for +an explanation of @code{REG_NOTBOL}). + +@item +match-end-of-line operator (@pxref{Match-beginning-of-line +Operator}) matches the empty string immediately before a newline, +regardless of how @code{REG_NOTEOL} is set (@pxref{POSIX Matching}, +for an explanation of @code{REG_NOTEOL}). + +@end itemize + +@end table + +If @code{regcomp} successfully compiles the regular expression, it +returns zero and sets @code{*@var{pattern_buffer}} to the compiled +pattern. Except for @code{syntax} (which it sets as explained above), it +also sets the same fields the same way as does the @sc{gnu} compiling +function (@pxref{GNU Regular Expression Compiling}). + +If @code{regcomp} can't compile the regular expression, it returns one +of the error codes listed here. (Except when noted differently, the +syntax of in all examples below is basic regular expression syntax.) + +@table @code + +@comment repetitions +@item REG_BADRPT +For example, the consecutive repetition operators @samp{**} in +@samp{a**} are invalid. As another example, if the syntax is extended +regular expression syntax, then the repetition operator @samp{*} with +nothing on which to operate in @samp{*} is invalid. + +@item REG_BADBR +For example, the @var{count} @samp{-1} in @samp{a\@{-1} is invalid. + +@item REG_EBRACE +For example, @samp{a\@{1} is missing a close-interval operator. + +@comment lists +@item REG_EBRACK +For example, @samp{[a} is missing a close-list operator. + +@item REG_ERANGE +For example, the range ending point @samp{z} that collates lower than +does its starting point @samp{a} in @samp{[z-a]} is invalid. Also, the +range with the character class @samp{[:alpha:]} as its starting point in +@samp{[[:alpha:]-|]}. + +@item REG_ECTYPE +For example, the character class name @samp{foo} in @samp{[[:foo:]} is +invalid. + +@comment groups +@item REG_EPAREN +For example, @samp{a\)} is missing an open-group operator and @samp{\(a} +is missing a close-group operator. + +@item REG_ESUBREG +For example, the back reference @samp{\2} that refers to a nonexistent +subexpression in @samp{\(a\)\2} is invalid. + +@comment unfinished business + +@item REG_EEND +Returned when a regular expression causes no other more specific error. + +@item REG_EESCAPE +For example, the trailing backslash @samp{\} in @samp{a\} is invalid, as is the +one in @samp{\}. + +@comment kitchen sink +@item REG_BADPAT +For example, in the extended regular expression syntax, the empty group +@samp{()} in @samp{a()b} is invalid. + +@comment internal +@item REG_ESIZE +Returned when a regular expression needs a pattern buffer larger than +65536 bytes. + +@item REG_ESPACE +Returned when a regular expression makes Regex to run out of memory. + +@end table + + +@node POSIX Matching, Reporting Errors, POSIX Regular Expression Compiling, POSIX Regex Functions +@subsection POSIX Matching + +Matching the @sc{posix} way means trying to match a null-terminated +string starting at its first character. Once you've compiled a pattern +into a pattern buffer (@pxref{POSIX Regular Expression Compiling}), you +can ask the matcher to match that pattern against a string using: + +@findex regexec +@example +int +regexec (const regex_t *@var{preg}, const char *@var{string}, + size_t @var{nmatch}, regmatch_t @var{pmatch}[], int @var{eflags}) +@end example + +@noindent +@var{preg} is the address of a pattern buffer for a compiled pattern. +@var{string} is the string you want to match. + +@xref{Using Byte Offsets}, for an explanation of @var{pmatch}. If you +pass zero for @var{nmatch} or you compiled @var{preg} with the +compilation flag @code{REG_NOSUB} set, then @code{regexec} will ignore +@var{pmatch}; otherwise, you must allocate it to have at least +@var{nmatch} elements. @code{regexec} will record @var{nmatch} byte +offsets in @var{pmatch}, and set to @math{-1} any unused elements up to +@math{@var{pmatch}@code{[@var{nmatch}]} - 1}. + +@var{eflags} specifies @dfn{execution flags}---namely, the two bits +@code{REG_NOTBOL} and @code{REG_NOTEOL} (defined in @file{regex.h}). If +you set @code{REG_NOTBOL}, then the match-beginning-of-line operator +(@pxref{Match-beginning-of-line Operator}) always fails to match. +This lets you match against pieces of a line, as you would need to if, +say, searching for repeated instances of a given pattern in a line; it +would work correctly for patterns both with and without +match-beginning-of-line operators. @code{REG_NOTEOL} works analogously +for the match-end-of-line operator (@pxref{Match-end-of-line +Operator}); it exists for symmetry. + +@code{regexec} tries to find a match for @var{preg} in @var{string} +according to the syntax in @var{preg}'s @code{syntax} field. +(@xref{POSIX Regular Expression Compiling}, for how to set it.) The +function returns zero if the compiled pattern matches @var{string} and +@code{REG_NOMATCH} (defined in @file{regex.h}) if it doesn't. + +@node Reporting Errors, Using Byte Offsets, POSIX Matching, POSIX Regex Functions +@subsection Reporting Errors + +If either @code{regcomp} or @code{regexec} fail, they return a nonzero +error code, the possibilities for which are defined in @file{regex.h}. +@xref{POSIX Regular Expression Compiling}, and @ref{POSIX Matching}, for +what these codes mean. To get an error string corresponding to these +codes, you can use: + +@findex regerror +@example +size_t +regerror (int @var{errcode}, + const regex_t *@var{preg}, + char *@var{errbuf}, + size_t @var{errbuf_size}) +@end example + +@noindent +@var{errcode} is an error code, @var{preg} is the address of the pattern +buffer which provoked the error, @var{errbuf} is the error buffer, and +@var{errbuf_size} is @var{errbuf}'s size. + +@code{regerror} returns the size in bytes of the error string +corresponding to @var{errcode} (including its terminating null). If +@var{errbuf} and @var{errbuf_size} are nonzero, it also returns in +@var{errbuf} the first @math{@var{errbuf_size} - 1} characters of the +error string, followed by a null. +@var{errbuf_size} must be a nonnegative number less than or equal to the +size in bytes of @var{errbuf}. + +You can call @code{regerror} with a null @var{errbuf} and a zero +@var{errbuf_size} to determine how large @var{errbuf} need be to +accommodate @code{regerror}'s error string. + +@node Using Byte Offsets, Freeing POSIX Pattern Buffers, Reporting Errors, POSIX Regex Functions +@subsection Using Byte Offsets + +In @sc{posix}, variables of type @code{regmatch_t} hold analogous +information, but are not identical to, @sc{gnu}'s registers (@pxref{Using +Registers}). To get information about registers in @sc{posix}, pass to +@code{regexec} a nonzero @var{pmatch} of type @code{regmatch_t}, i.e., +the address of a structure of this type, defined in +@file{regex.h}: + +@tindex regmatch_t +@example +typedef struct +@{ + regoff_t rm_so; + regoff_t rm_eo; +@} regmatch_t; +@end example + +When reading in @ref{Using Registers}, about how the matching function +stores the information into the registers, substitute @var{pmatch} for +@var{regs}, @code{@w{@var{pmatch}[@var{i}]->}rm_so} for +@code{@w{@var{regs}->}start[@var{i}]} and +@code{@w{@var{pmatch}[@var{i}]->}rm_eo} for +@code{@w{@var{regs}->}end[@var{i}]}. + +@node Freeing POSIX Pattern Buffers, , Using Byte Offsets, POSIX Regex Functions +@subsection Freeing POSIX Pattern Buffers + +To free any allocated fields of a pattern buffer, use: + +@findex regfree +@example +void +regfree (regex_t *@var{preg}) +@end example + +@noindent +@var{preg} is the pattern buffer whose allocated fields you want freed. +@code{regfree} also sets @var{preg}'s @code{allocated} and @code{used} +fields to zero. After freeing a pattern buffer, you need to again +compile a regular expression in it (@pxref{POSIX Regular Expression +Compiling}) before passing it to the matching function (@pxref{POSIX +Matching}). + + +@node BSD Regex Functions, , POSIX Regex Functions, Programming with Regex +@section BSD Regex Functions + +If you're writing code that has to be Berkeley @sc{unix} compatible, +you'll need to use these functions whose interfaces are the same as those +in Berkeley @sc{unix}. + +@menu +* BSD Regular Expression Compiling:: re_comp () +* BSD Searching:: re_exec () +@end menu + +@node BSD Regular Expression Compiling, BSD Searching, , BSD Regex Functions +@subsection BSD Regular Expression Compiling + +With Berkeley @sc{unix}, you can only search for a given regular +expression; you can't match one. To search for it, you must first +compile it. Before you compile it, you must indicate the regular +expression syntax you want it compiled according to by setting the +variable @code{re_syntax_options} (declared in @file{regex.h} to some +syntax (@pxref{Regular Expression Syntax}). + +To compile a regular expression use: + +@findex re_comp +@example +char * +re_comp (char *@var{regex}) +@end example + +@noindent +@var{regex} is the address of a null-terminated regular expression. +@code{re_comp} uses an internal pattern buffer, so you can use only the +most recently compiled pattern buffer. This means that if you want to +use a given regular expression that you've already compiled---but it +isn't the latest one you've compiled---you'll have to recompile it. If +you call @code{re_comp} with the null string (@emph{not} the empty +string) as the argument, it doesn't change the contents of the pattern +buffer. + +If @code{re_comp} successfully compiles the regular expression, it +returns zero. If it can't compile the regular expression, it returns +an error string. @code{re_comp}'s error messages are identical to those +of @code{re_compile_pattern} (@pxref{GNU Regular Expression +Compiling}). + +@node BSD Searching, , BSD Regular Expression Compiling, BSD Regex Functions +@subsection BSD Searching + +Searching the Berkeley @sc{unix} way means searching in a string +starting at its first character and trying successive positions within +it to find a match. Once you've compiled a pattern using @code{re_comp} +(@pxref{BSD Regular Expression Compiling}), you can ask Regex +to search for that pattern in a string using: + +@findex re_exec +@example +int +re_exec (char *@var{string}) +@end example + +@noindent +@var{string} is the address of the null-terminated string in which you +want to search. + +@code{re_exec} returns either 1 for success or 0 for failure. It +automatically uses a @sc{gnu} fastmap (@pxref{Searching with Fastmaps}). + + +@node Copying, Index, Programming with Regex, Top +@appendix GNU GENERAL PUBLIC LICENSE +@center Version 2, June 1991 + +@display +Copyright @copyright{} 1989, 1991 Free Software Foundation, Inc. +675 Mass Ave, Cambridge, MA 02139, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@unnumberedsec Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software---to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + +@iftex +@unnumberedsec TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +@end iftex +@ifinfo +@center TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +@end ifinfo + +@enumerate +@item +This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The ``Program'', below, +refers to any such program or work, and a ``work based on the Program'' +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term ``modification''.) Each licensee is addressed as ``you''. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + +@item +You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +@item +You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +@enumerate a +@item +You must cause the modified files to carry prominent notices +stating that you changed the files and the date of any change. + +@item +You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any +part thereof, to be licensed as a whole at no charge to all third +parties under the terms of this License. + +@item +If the modified program normally reads commands interactively +when run, you must cause it, when started running for such +interactive use in the most ordinary way, to print or display an +announcement including an appropriate copyright notice and a +notice that there is no warranty (or else, saying that you provide +a warranty) and that users may redistribute the program under +these conditions, and telling the user how to view a copy of this +License. (Exception: if the Program itself is interactive but +does not normally print such an announcement, your work based on +the Program is not required to print an announcement.) +@end enumerate + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +@item +You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + +@enumerate a +@item +Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections +1 and 2 above on a medium customarily used for software interchange; or, + +@item +Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your +cost of physically performing source distribution, a complete +machine-readable copy of the corresponding source code, to be +distributed under the terms of Sections 1 and 2 above on a medium +customarily used for software interchange; or, + +@item +Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is +allowed only for noncommercial distribution and only if you +received the program in object code or executable form with such +an offer, in accord with Subsection b above.) +@end enumerate + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +@item +You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +@item +You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +@item +Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +@item +If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +@item +If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +@item +The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and ``any +later version'', you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +@item +If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +@iftex +@heading NO WARRANTY +@end iftex +@ifinfo +@center NO WARRANTY +@end ifinfo + +@item +BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM ``AS IS'' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + +@item +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. +@end enumerate + +@iftex +@heading END OF TERMS AND CONDITIONS +@end iftex +@ifinfo +@center END OF TERMS AND CONDITIONS +@end ifinfo + +@page +@unnumberedsec Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the ``copyright'' line and a pointer to where the full notice is found. + +@smallexample +@var{one line to give the program's name and a brief idea of what it does.} +Copyright (C) 19@var{yy} @var{name of author} + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +@end smallexample + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + +@smallexample +Gnomovision version 69, Copyright (C) 19@var{yy} @var{name of author} +Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. +This is free software, and you are welcome to redistribute it +under certain conditions; type `show c' for details. +@end smallexample + +The hypothetical commands @samp{show w} and @samp{show c} should show +the appropriate parts of the General Public License. Of course, the +commands you use may be called something other than @samp{show w} and +@samp{show c}; they could even be mouse-clicks or menu items---whatever +suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a ``copyright disclaimer'' for the program, if +necessary. Here is a sample; alter the names: + +@example +Yoyodyne, Inc., hereby disclaims all copyright interest in the program +`Gnomovision' (which makes passes at compilers) written by James Hacker. + +@var{signature of Ty Coon}, 1 April 1989 +Ty Coon, President of Vice +@end example + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + + +@node Index, , Copying, Top +@unnumbered Index + +@printindex cp + +@contents + +@bye diff --git a/gnu/lib/libregex/doc/texinfo.tex b/gnu/lib/libregex/doc/texinfo.tex new file mode 100644 index 0000000000..d10917e237 --- /dev/null +++ b/gnu/lib/libregex/doc/texinfo.tex @@ -0,0 +1,3941 @@ +%% TeX macros to handle texinfo files + +% Copyright (C) 1985, 86, 88, 90, 91, 92, 1993 Free Software Foundation, Inc. + +%This texinfo.tex file is free software; you can redistribute it and/or +%modify it under the terms of the GNU General Public License as +%published by the Free Software Foundation; either version 2, or (at +%your option) any later version. + +%This texinfo.tex file is distributed in the hope that it will be +%useful, but WITHOUT ANY WARRANTY; without even the implied warranty +%of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%General Public License for more details. + +%You should have received a copy of the GNU General Public License +%along with this texinfo.tex file; see the file COPYING. If not, write +%to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, +%USA. + + +%In other words, you are welcome to use, share and improve this program. +%You are forbidden to forbid anyone else to use, share and improve +%what you give them. Help stamp out software-hoarding! + +\def\texinfoversion{2.104} +\message{Loading texinfo package [Version \texinfoversion]:} +\message{} + +% Print the version number if in a .fmt file. +\everyjob{\message{[Texinfo version \texinfoversion]}\message{}} + +% Save some parts of plain tex whose names we will redefine. + +\let\ptexlbrace=\{ +\let\ptexrbrace=\} +\let\ptexdots=\dots +\let\ptexdot=\. +\let\ptexstar=\* +\let\ptexend=\end +\let\ptexbullet=\bullet +\let\ptexb=\b +\let\ptexc=\c +\let\ptexi=\i +\let\ptext=\t +\let\ptexl=\l +\let\ptexL=\L + +\def\tie{\penalty 10000\ } % Save plain tex definition of ~. + +\message{Basics,} +\chardef\other=12 + +% If this character appears in an error message or help string, it +% starts a new line in the output. +\newlinechar = `^^J + +% Ignore a token. +% +\def\gobble#1{} + +\hyphenation{ap-pen-dix} +\hyphenation{mini-buf-fer mini-buf-fers} +\hyphenation{eshell} + +% Margin to add to right of even pages, to left of odd pages. +\newdimen \bindingoffset \bindingoffset=0pt +\newdimen \normaloffset \normaloffset=\hoffset +\newdimen\pagewidth \newdimen\pageheight +\pagewidth=\hsize \pageheight=\vsize + +% Sometimes it is convenient to have everything in the transcript file +% and nothing on the terminal. We don't just call \tracingall here, +% since that produces some useless output on the terminal. +% +\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}% +\def\loggingall{\tracingcommands2 \tracingstats2 + \tracingpages1 \tracingoutput1 \tracinglostchars1 + \tracingmacros2 \tracingparagraphs1 \tracingrestores1 + \showboxbreadth\maxdimen\showboxdepth\maxdimen +}% + +%---------------------Begin change----------------------- +% +%%%% For @cropmarks command. +% Dimensions to add cropmarks at corners Added by P. A. MacKay, 12 Nov. 1986 +% +\newdimen\cornerlong \newdimen\cornerthick +\newdimen \topandbottommargin +\newdimen \outerhsize \newdimen \outervsize +\cornerlong=1pc\cornerthick=.3pt % These set size of cropmarks +\outerhsize=7in +%\outervsize=9.5in +% Alternative @smallbook page size is 9.25in +\outervsize=9.25in +\topandbottommargin=.75in +% +%---------------------End change----------------------- + +% \onepageout takes a vbox as an argument. Note that \pagecontents +% does insertions itself, but you have to call it yourself. +\chardef\PAGE=255 \output={\onepageout{\pagecontents\PAGE}} +\def\onepageout#1{\hoffset=\normaloffset +\ifodd\pageno \advance\hoffset by \bindingoffset +\else \advance\hoffset by -\bindingoffset\fi +{\escapechar=`\\\relax % makes sure backslash is used in output files. +\shipout\vbox{{\let\hsize=\pagewidth \makeheadline} \pagebody{#1}% +{\let\hsize=\pagewidth \makefootline}}}% +\advancepageno \ifnum\outputpenalty>-20000 \else\dosupereject\fi} + +%%%% For @cropmarks command %%%% + +% Here is a modification of the main output routine for Near East Publications +% This provides right-angle cropmarks at all four corners. +% The contents of the page are centerlined into the cropmarks, +% and any desired binding offset is added as an \hskip on either +% site of the centerlined box. (P. A. MacKay, 12 November, 1986) +% +\def\croppageout#1{\hoffset=0pt % make sure this doesn't mess things up +{\escapechar=`\\\relax % makes sure backslash is used in output files. + \shipout + \vbox to \outervsize{\hsize=\outerhsize + \vbox{\line{\ewtop\hfill\ewtop}} + \nointerlineskip + \line{\vbox{\moveleft\cornerthick\nstop} + \hfill + \vbox{\moveright\cornerthick\nstop}} + \vskip \topandbottommargin + \centerline{\ifodd\pageno\hskip\bindingoffset\fi + \vbox{ + {\let\hsize=\pagewidth \makeheadline} + \pagebody{#1} + {\let\hsize=\pagewidth \makefootline}} + \ifodd\pageno\else\hskip\bindingoffset\fi} + \vskip \topandbottommargin plus1fill minus1fill + \boxmaxdepth\cornerthick + \line{\vbox{\moveleft\cornerthick\nsbot} + \hfill + \vbox{\moveright\cornerthick\nsbot}} + \nointerlineskip + \vbox{\line{\ewbot\hfill\ewbot}} + }} + \advancepageno + \ifnum\outputpenalty>-20000 \else\dosupereject\fi} +% +% Do @cropmarks to get crop marks +\def\cropmarks{\let\onepageout=\croppageout } + +\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}} +{\catcode`\@ =11 +\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi +\dimen@=\dp#1 \unvbox#1 +\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi +\ifr@ggedbottom \kern-\dimen@ \vfil \fi} +} + +% +% Here are the rules for the cropmarks. Note that they are +% offset so that the space between them is truly \outerhsize or \outervsize +% (P. A. MacKay, 12 November, 1986) +% +\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong} +\def\nstop{\vbox + {\hrule height\cornerthick depth\cornerlong width\cornerthick}} +\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong} +\def\nsbot{\vbox + {\hrule height\cornerlong depth\cornerthick width\cornerthick}} + +% Parse an argument, then pass it to #1. The argument is the rest of +% the input line (except we remove a trailing comment). #1 should be a +% macro which expects an ordinary undelimited TeX argument. +% +\def\parsearg#1{% + \let\next = #1% + \begingroup + \obeylines + \futurelet\temp\parseargx +} + +% If the next token is an obeyed space (from an @example environment or +% the like), remove it and recurse. Otherwise, we're done. +\def\parseargx{% + % \obeyedspace is defined far below, after the definition of \sepspaces. + \ifx\obeyedspace\temp + \expandafter\parseargdiscardspace + \else + \expandafter\parseargline + \fi +} + +% Remove a single space (as the delimiter token to the macro call). +{\obeyspaces % + \gdef\parseargdiscardspace {\futurelet\temp\parseargx}} + +{\obeylines % + \gdef\parseargline#1^^M{% + \endgroup % End of the group started in \parsearg. + % + % First remove any @c comment, then any @comment. + % Result of each macro is put in \toks0. + \argremovec #1\c\relax % + \expandafter\argremovecomment \the\toks0 \comment\relax % + % + % Call the caller's macro, saved as \next in \parsearg. + \expandafter\next\expandafter{\the\toks0}% + }% +} + +% Since all \c{,omment} does is throw away the argument, we can let TeX +% do that for us. The \relax here is matched by the \relax in the call +% in \parseargline; it could be more or less anything, its purpose is +% just to delimit the argument to the \c. +\def\argremovec#1\c#2\relax{\toks0 = {#1}} +\def\argremovecomment#1\comment#2\relax{\toks0 = {#1}} + +% \argremovec{,omment} might leave us with trailing spaces, though; e.g., +% @end itemize @c foo +% will have two active spaces as part of the argument with the +% `itemize'. Here we remove all active spaces from #1, and assign the +% result to \toks0. +% +% This loses if there are any *other* active characters besides spaces +% in the argument -- _ ^ +, for example -- since they get expanded. +% Fortunately, Texinfo does not define any such commands. (If it ever +% does, the catcode of the characters in questionwill have to be changed +% here.) But this means we cannot call \removeactivespaces as part of +% \argremovec{,omment}, since @c uses \parsearg, and thus the argument +% that \parsearg gets might well have any character at all in it. +% +\def\removeactivespaces#1{% + \begingroup + \ignoreactivespaces + \edef\temp{#1}% + \global\toks0 = \expandafter{\temp}% + \endgroup +} + +% Change the active space to expand to nothing. +% +\begingroup + \obeyspaces + \gdef\ignoreactivespaces{\obeyspaces\let =\empty} +\endgroup + + +\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} + +%% These are used to keep @begin/@end levels from running away +%% Call \inENV within environments (after a \begingroup) +\newif\ifENV \ENVfalse \def\inENV{\ifENV\relax\else\ENVtrue\fi} +\def\ENVcheck{% +\ifENV\errmessage{Still within an environment. Type Return to continue.} +\endgroup\fi} % This is not perfect, but it should reduce lossage + +% @begin foo is the same as @foo, for now. +\newhelp\EMsimple{Type to continue.} + +\outer\def\begin{\parsearg\beginxxx} + +\def\beginxxx #1{% +\expandafter\ifx\csname #1\endcsname\relax +{\errhelp=\EMsimple \errmessage{Undefined command @begin #1}}\else +\csname #1\endcsname\fi} + +% @end foo executes the definition of \Efoo. +% +\def\end{\parsearg\endxxx} +\def\endxxx #1{% + \removeactivespaces{#1}% + \edef\endthing{\the\toks0}% + % + \expandafter\ifx\csname E\endthing\endcsname\relax + \expandafter\ifx\csname \endthing\endcsname\relax + % There's no \foo, i.e., no ``environment'' foo. + \errhelp = \EMsimple + \errmessage{Undefined command `@end \endthing'}% + \else + \unmatchedenderror\endthing + \fi + \else + % Everything's ok; the right environment has been started. + \csname E\endthing\endcsname + \fi +} + +% There is an environment #1, but it hasn't been started. Give an error. +% +\def\unmatchedenderror#1{% + \errhelp = \EMsimple + \errmessage{This `@end #1' doesn't have a matching `@#1'}% +} + +% Define the control sequence \E#1 to give an unmatched @end error. +% +\def\defineunmatchedend#1{% + \expandafter\def\csname E#1\endcsname{\unmatchedenderror{#1}}% +} + + +% Single-spacing is done by various environments. + +\newskip\singlespaceskip \singlespaceskip = \baselineskip +\def\singlespace{% +{\advance \baselineskip by -\singlespaceskip +\kern \baselineskip}% +\baselineskip=\singlespaceskip +} + +%% Simple single-character @ commands + +% @@ prints an @ +% Kludge this until the fonts are right (grr). +\def\@{{\tt \char '100}} + +% This is turned off because it was never documented +% and you can use @w{...} around a quote to suppress ligatures. +%% Define @` and @' to be the same as ` and ' +%% but suppressing ligatures. +%\def\`{{`}} +%\def\'{{'}} + +% Used to generate quoted braces. + +\def\mylbrace {{\tt \char '173}} +\def\myrbrace {{\tt \char '175}} +\let\{=\mylbrace +\let\}=\myrbrace + +% @: forces normal size whitespace following. +\def\:{\spacefactor=1000 } + +% @* forces a line break. +\def\*{\hfil\break\hbox{}\ignorespaces} + +% @. is an end-of-sentence period. +\def\.{.\spacefactor=3000 } + +% @w prevents a word break. Without the \leavevmode, @w at the +% beginning of a paragraph, when TeX is still in vertical mode, would +% produce a whole line of output instead of starting the paragraph. +\def\w#1{\leavevmode\hbox{#1}} + +% @group ... @end group forces ... to be all on one page, by enclosing +% it in a TeX vbox. We use \vtop instead of \vbox to construct the box +% to keep its height that of a normal line. According to the rules for +% \topskip (p.114 of the TeXbook), the glue inserted is +% max (\topskip - \ht (first item), 0). If that height is large, +% therefore, no glue is inserted, and the space between the headline and +% the text is small, which looks bad. +% +\def\group{\begingroup + \ifnum\catcode13=\active \else + \errhelp = \groupinvalidhelp + \errmessage{@group invalid in context where filling is enabled}% + \fi + % + % The \vtop we start below produces a box with normal height and large + % depth; thus, TeX puts \baselineskip glue before it, and (when the + % next line of text is done) \lineskip glue after it. (See p.82 of + % the TeXbook.) But the next line of text also gets us \parskip glue. + % Final result: space below is slightly more than space above. + \def\Egroup{% + \egroup % End the \vtop. + \endgroup % End the \group. + }% + % + \vtop\bgroup + % We have to put a strut on the last line in case the @group is in + % the midst of an example, rather than completely enclosing it. + % Otherwise, the interline space between the last line of the group + % and the first line afterwards is too small. But we can't put the + % strut in \Egroup, since there it would be on a line by itself. + % Hence this just inserts a strut at the beginning of each line. + \everypar = {\strut}% + % + % We do @comment here in case we are called inside an environment, + % such as @example, where each end-of-line in the input causes an + % end-of-line in the output. We don't want the end-of-line after + % the `@group' to put extra space in the output. Since @group + % should appear on a line by itself (according to the Texinfo + % manual), we don't worry about eating any user text. + \comment +} +% +% TeX puts in an \escapechar (i.e., `@') at the beginning of the help +% message, so this ends up printing `@group can only ...'. +% +\newhelp\groupinvalidhelp{% +group can only be used in environments such as @example,^^J% +where each line of input produces a line of output.} + +% @need space-in-mils +% forces a page break if there is not space-in-mils remaining. + +\newdimen\mil \mil=0.001in + +\def\need{\parsearg\needx} + +% Old definition--didn't work. +%\def\needx #1{\par % +%% This method tries to make TeX break the page naturally +%% if the depth of the box does not fit. +%{\baselineskip=0pt% +%\vtop to #1\mil{\vfil}\kern -#1\mil\penalty 10000 +%\prevdepth=-1000pt +%}} + +\def\needx#1{% + % Go into vertical mode, so we don't make a big box in the middle of a + % paragraph. + \par + % + % Don't add any leading before our big empty box, but allow a page + % break, since the best break might be right here. + \allowbreak + \nointerlineskip + \vtop to #1\mil{\vfil}% + % + % TeX does not even consider page breaks if a penalty added to the + % main vertical list is 10000 or more. But in order to see if the + % empty box we just added fits on the page, we must make it consider + % page breaks. On the other hand, we don't want to actually break the + % page after the empty box. So we use a penalty of 9999. + % + % There is an extremely small chance that TeX will actually break the + % page at this \penalty, if there are no other feasible breakpoints in + % sight. (If the user is using lots of big @group commands, which + % almost-but-not-quite fill up a page, TeX will have a hard time doing + % good page breaking, for example.) However, I could not construct an + % example where a page broke at this \penalty; if it happens in a real + % document, then we can reconsider our strategy. + \penalty9999 + % + % Back up by the size of the box, whether we did a page break or not. + \kern -#1\mil + % + % Do not allow a page break right after this kern. + \nobreak +} + +% @br forces paragraph break + +\let\br = \par + +% @dots{} output some dots + +\def\dots{$\ldots$} + +% @page forces the start of a new page + +\def\page{\par\vfill\supereject} + +% @exdent text.... +% outputs text on separate line in roman font, starting at standard page margin + +% This records the amount of indent in the innermost environment. +% That's how much \exdent should take out. +\newskip\exdentamount + +% This defn is used inside fill environments such as @defun. +\def\exdent{\parsearg\exdentyyy} +\def\exdentyyy #1{{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}} + +% This defn is used inside nofill environments such as @example. +\def\nofillexdent{\parsearg\nofillexdentyyy} +\def\nofillexdentyyy #1{{\advance \leftskip by -\exdentamount +\leftline{\hskip\leftskip{\rm#1}}}} + +%\hbox{{\rm#1}}\hfil\break}} + +% @include file insert text of that file as input. + +\def\include{\parsearg\includezzz} +%Use \input\thisfile to avoid blank after \input, which may be an active +%char (in which case the blank would become the \input argument). +%The grouping keeps the value of \thisfile correct even when @include +%is nested. +\def\includezzz #1{\begingroup +\def\thisfile{#1}\input\thisfile +\endgroup} + +\def\thisfile{} + +% @center line outputs that line, centered + +\def\center{\parsearg\centerzzz} +\def\centerzzz #1{{\advance\hsize by -\leftskip +\advance\hsize by -\rightskip +\centerline{#1}}} + +% @sp n outputs n lines of vertical space + +\def\sp{\parsearg\spxxx} +\def\spxxx #1{\par \vskip #1\baselineskip} + +% @comment ...line which is ignored... +% @c is the same as @comment +% @ignore ... @end ignore is another way to write a comment + +\def\comment{\catcode 64=\other \catcode 123=\other \catcode 125=\other% +\parsearg \commentxxx} + +\def\commentxxx #1{\catcode 64=0 \catcode 123=1 \catcode 125=2 } + +\let\c=\comment + +% Prevent errors for section commands. +% Used in @ignore and in failing conditionals. +\def\ignoresections{% +\let\chapter=\relax +\let\unnumbered=\relax +\let\top=\relax +\let\unnumberedsec=\relax +\let\unnumberedsection=\relax +\let\unnumberedsubsec=\relax +\let\unnumberedsubsection=\relax +\let\unnumberedsubsubsec=\relax +\let\unnumberedsubsubsection=\relax +\let\section=\relax +\let\subsec=\relax +\let\subsubsec=\relax +\let\subsection=\relax +\let\subsubsection=\relax +\let\appendix=\relax +\let\appendixsec=\relax +\let\appendixsection=\relax +\let\appendixsubsec=\relax +\let\appendixsubsection=\relax +\let\appendixsubsubsec=\relax +\let\appendixsubsubsection=\relax +\let\contents=\relax +\let\smallbook=\relax +\let\titlepage=\relax +} + +% Used in nested conditionals, where we have to parse the Texinfo source +% and so want to turn off most commands, in case they are used +% incorrectly. +% +\def\ignoremorecommands{% + \let\defcv = \relax + \let\deffn = \relax + \let\deffnx = \relax + \let\defindex = \relax + \let\defivar = \relax + \let\defmac = \relax + \let\defmethod = \relax + \let\defop = \relax + \let\defopt = \relax + \let\defspec = \relax + \let\deftp = \relax + \let\deftypefn = \relax + \let\deftypefun = \relax + \let\deftypevar = \relax + \let\deftypevr = \relax + \let\defun = \relax + \let\defvar = \relax + \let\defvr = \relax + \let\ref = \relax + \let\xref = \relax + \let\printindex = \relax + \let\pxref = \relax + \let\settitle = \relax + \let\include = \relax +} + +% Ignore @ignore ... @end ignore. +% +\def\ignore{\doignore{ignore}} + +% Also ignore @ifinfo, @menu, and @direntry text. +% +\def\ifinfo{\doignore{ifinfo}} +\def\menu{\doignore{menu}} +\def\direntry{\doignore{direntry}} + +% Ignore text until a line `@end #1'. +% +\def\doignore#1{\begingroup + % Don't complain about control sequences we have declared \outer. + \ignoresections + % + % Define a command to swallow text until we reach `@end #1'. + \long\def\doignoretext##1\end #1{\enddoignore}% + % + % Make sure that spaces turn into tokens that match what \doignoretext wants. + \catcode32 = 10 + % + % And now expand that command. + \doignoretext +} + +% What we do to finish off ignored text. +% +\def\enddoignore{\endgroup\ignorespaces}% + +\newif\ifwarnedobs\warnedobsfalse +\def\obstexwarn{% + \ifwarnedobs\relax\else + % We need to warn folks that they may have trouble with TeX 3.0. + % This uses \immediate\write16 rather than \message to get newlines. + \immediate\write16{} + \immediate\write16{***WARNING*** for users of Unix TeX 3.0!} + \immediate\write16{This manual trips a bug in TeX version 3.0 (tex hangs).} + \immediate\write16{If you are running another version of TeX, relax.} + \immediate\write16{If you are running Unix TeX 3.0, kill this TeX process.} + \immediate\write16{ Then upgrade your TeX installation if you can.} + \immediate\write16{If you are stuck with version 3.0, run the} + \immediate\write16{ script ``tex3patch'' from the Texinfo distribution} + \immediate\write16{ to use a workaround.} + \immediate\write16{} + \warnedobstrue + \fi +} + +% **In TeX 3.0, setting text in \nullfont hangs tex. For a +% workaround (which requires the file ``dummy.tfm'' to be installed), +% uncomment the following line: +%%%%%\font\nullfont=dummy\let\obstexwarn=\relax + +% Ignore text, except that we keep track of conditional commands for +% purposes of nesting, up to an `@end #1' command. +% +\def\nestedignore#1{% + \obstexwarn + % We must actually expand the ignored text to look for the @end + % command, so that nested ignore constructs work. Thus, we put the + % text into a \vbox and then do nothing with the result. To minimize + % the change of memory overflow, we follow the approach outlined on + % page 401 of the TeXbook: make the current font be a dummy font. + % + \setbox0 = \vbox\bgroup + % Don't complain about control sequences we have declared \outer. + \ignoresections + % + % Define `@end #1' to end the box, which will in turn undefine the + % @end command again. + \expandafter\def\csname E#1\endcsname{\egroup\ignorespaces}% + % + % We are going to be parsing Texinfo commands. Most cause no + % trouble when they are used incorrectly, but some commands do + % complicated argument parsing or otherwise get confused, so we + % undefine them. + % + % We can't do anything about stray @-signs, unfortunately; + % they'll produce `undefined control sequence' errors. + \ignoremorecommands + % + % Set the current font to be \nullfont, a TeX primitive, and define + % all the font commands to also use \nullfont. We don't use + % dummy.tfm, as suggested in the TeXbook, because not all sites + % might have that installed. Therefore, math mode will still + % produce output, but that should be an extremely small amount of + % stuff compared to the main input. + % + \nullfont + \let\tenrm = \nullfont \let\tenit = \nullfont \let\tensl = \nullfont + \let\tenbf = \nullfont \let\tentt = \nullfont \let\smallcaps = \nullfont + \let\tensf = \nullfont + % + % Don't complain when characters are missing from the fonts. + \tracinglostchars = 0 + % + % Don't bother to do space factor calculations. + \frenchspacing + % + % Don't report underfull hboxes. + \hbadness = 10000 + % + % Do minimal line-breaking. + \pretolerance = 10000 + % + % Do not execute instructions in @tex + \def\tex{\doignore{tex}} +} + +% @set VAR sets the variable VAR to an empty value. +% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE. +% +% Since we want to separate VAR from REST-OF-LINE (which might be +% empty), we can't just use \parsearg; we have to insert a space of our +% own to delimit the rest of the line, and then take it out again if we +% didn't need it. +% +\def\set{\parsearg\setxxx} +\def\setxxx#1{\setyyy#1 \endsetyyy} +\def\setyyy#1 #2\endsetyyy{% + \def\temp{#2}% + \ifx\temp\empty \global\expandafter\let\csname SET#1\endcsname = \empty + \else \setzzz{#1}#2\endsetzzz % Remove the trailing space \setxxx inserted. + \fi +} +\def\setzzz#1#2 \endsetzzz{\expandafter\xdef\csname SET#1\endcsname{#2}} + +% @clear VAR clears (i.e., unsets) the variable VAR. +% +\def\clear{\parsearg\clearxxx} +\def\clearxxx#1{\global\expandafter\let\csname SET#1\endcsname=\relax} + +% @value{foo} gets the text saved in variable foo. +% +\def\value#1{\expandafter + \ifx\csname SET#1\endcsname\relax + {\{No value for ``#1''\}} + \else \csname SET#1\endcsname \fi} + +% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined +% with @set. +% +\def\ifset{\parsearg\ifsetxxx} +\def\ifsetxxx #1{% + \expandafter\ifx\csname SET#1\endcsname\relax + \expandafter\ifsetfail + \else + \expandafter\ifsetsucceed + \fi +} +\def\ifsetsucceed{\conditionalsucceed{ifset}} +\def\ifsetfail{\nestedignore{ifset}} +\defineunmatchedend{ifset} + +% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been +% defined with @set, or has been undefined with @clear. +% +\def\ifclear{\parsearg\ifclearxxx} +\def\ifclearxxx #1{% + \expandafter\ifx\csname SET#1\endcsname\relax + \expandafter\ifclearsucceed + \else + \expandafter\ifclearfail + \fi +} +\def\ifclearsucceed{\conditionalsucceed{ifclear}} +\def\ifclearfail{\nestedignore{ifclear}} +\defineunmatchedend{ifclear} + +% @iftex always succeeds; we read the text following, through @end +% iftex). But `@end iftex' should be valid only after an @iftex. +% +\def\iftex{\conditionalsucceed{iftex}} +\defineunmatchedend{iftex} + +% We can't just want to start a group at @iftex (for example) and end it +% at @end iftex, since then @set commands inside the conditional have no +% effect (they'd get reverted at the end of the group). So we must +% define \Eiftex to redefine itself to be its previous value. (We can't +% just define it to fail again with an ``unmatched end'' error, since +% the @ifset might be nested.) +% +\def\conditionalsucceed#1{% + \edef\temp{% + % Remember the current value of \E#1. + \let\nece{prevE#1} = \nece{E#1}% + % + % At the `@end #1', redefine \E#1 to be its previous value. + \def\nece{E#1}{\let\nece{E#1} = \nece{prevE#1}}% + }% + \temp +} + +% We need to expand lots of \csname's, but we don't want to expand the +% control sequences after we've constructed them. +% +\def\nece#1{\expandafter\noexpand\csname#1\endcsname} + +% @asis just yields its argument. Used with @table, for example. +% +\def\asis#1{#1} + +% @math means output in math mode. +% We don't use $'s directly in the definition of \math because control +% sequences like \math are expanded when the toc file is written. Then, +% we read the toc file back, the $'s will be normal characters (as they +% should be, according to the definition of Texinfo). So we must use a +% control sequence to switch into and out of math mode. +% +% This isn't quite enough for @math to work properly in indices, but it +% seems unlikely it will ever be needed there. +% +\let\implicitmath = $ +\def\math#1{\implicitmath #1\implicitmath} + +% @bullet and @minus need the same treatment as @math, just above. +\def\bullet{\implicitmath\ptexbullet\implicitmath} +\def\minus{\implicitmath-\implicitmath} + +\def\node{\ENVcheck\parsearg\nodezzz} +\def\nodezzz#1{\nodexxx [#1,]} +\def\nodexxx[#1,#2]{\gdef\lastnode{#1}} +\let\nwnode=\node +\let\lastnode=\relax + +\def\donoderef{\ifx\lastnode\relax\else +\expandafter\expandafter\expandafter\setref{\lastnode}\fi +\let\lastnode=\relax} + +\def\unnumbnoderef{\ifx\lastnode\relax\else +\expandafter\expandafter\expandafter\unnumbsetref{\lastnode}\fi +\let\lastnode=\relax} + +\def\appendixnoderef{\ifx\lastnode\relax\else +\expandafter\expandafter\expandafter\appendixsetref{\lastnode}\fi +\let\lastnode=\relax} + +\let\refill=\relax + +% @setfilename is done at the beginning of every texinfo file. +% So open here the files we need to have open while reading the input. +% This makes it possible to make a .fmt file for texinfo. +\def\setfilename{% + \readauxfile + \opencontents + \openindices + \fixbackslash % Turn off hack to swallow `\input texinfo'. + \global\let\setfilename=\comment % Ignore extra @setfilename cmds. + \comment % Ignore the actual filename. +} + +\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend} + +\def\inforef #1{\inforefzzz #1,,,,**} +\def\inforefzzz #1,#2,#3,#4**{See Info file \file{\ignorespaces #3{}}, + node \samp{\ignorespaces#1{}}} + +\message{fonts,} + +% Font-change commands. + +% Texinfo supports the sans serif font style, which plain TeX does not. +% So we set up a \sf analogous to plain's \rm, etc. +\newfam\sffam +\def\sf{\fam=\sffam \tensf} +\let\li = \sf % Sometimes we call it \li, not \sf. + +%% Try out Computer Modern fonts at \magstephalf +\let\mainmagstep=\magstephalf + +\ifx\bigger\relax +\let\mainmagstep=\magstep1 +\font\textrm=cmr12 +\font\texttt=cmtt12 +\else +\font\textrm=cmr10 scaled \mainmagstep +\font\texttt=cmtt10 scaled \mainmagstep +\fi +% Instead of cmb10, you many want to use cmbx10. +% cmbx10 is a prettier font on its own, but cmb10 +% looks better when embedded in a line with cmr10. +\font\textbf=cmb10 scaled \mainmagstep +\font\textit=cmti10 scaled \mainmagstep +\font\textsl=cmsl10 scaled \mainmagstep +\font\textsf=cmss10 scaled \mainmagstep +\font\textsc=cmcsc10 scaled \mainmagstep +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep + +% A few fonts for @defun, etc. +\font\defbf=cmbx10 scaled \magstep1 %was 1314 +\font\deftt=cmtt10 scaled \magstep1 +\def\df{\let\tentt=\deftt \let\tenbf = \defbf \bf} + +% Fonts for indices and small examples. +% We actually use the slanted font rather than the italic, +% because texinfo normally uses the slanted fonts for that. +% Do not make many font distinctions in general in the index, since they +% aren't very useful. +\font\ninett=cmtt9 +\font\indrm=cmr9 +\font\indit=cmsl9 +\let\indsl=\indit +\let\indtt=\ninett +\let\indsf=\indrm +\let\indbf=\indrm +\let\indsc=\indrm +\font\indi=cmmi9 +\font\indsy=cmsy9 + +% Fonts for headings +\font\chaprm=cmbx12 scaled \magstep2 +\font\chapit=cmti12 scaled \magstep2 +\font\chapsl=cmsl12 scaled \magstep2 +\font\chaptt=cmtt12 scaled \magstep2 +\font\chapsf=cmss12 scaled \magstep2 +\let\chapbf=\chaprm +\font\chapsc=cmcsc10 scaled\magstep3 +\font\chapi=cmmi12 scaled \magstep2 +\font\chapsy=cmsy10 scaled \magstep3 + +\font\secrm=cmbx12 scaled \magstep1 +\font\secit=cmti12 scaled \magstep1 +\font\secsl=cmsl12 scaled \magstep1 +\font\sectt=cmtt12 scaled \magstep1 +\font\secsf=cmss12 scaled \magstep1 +\font\secbf=cmbx12 scaled \magstep1 +\font\secsc=cmcsc10 scaled\magstep2 +\font\seci=cmmi12 scaled \magstep1 +\font\secsy=cmsy10 scaled \magstep2 + +% \font\ssecrm=cmbx10 scaled \magstep1 % This size an font looked bad. +% \font\ssecit=cmti10 scaled \magstep1 % The letters were too crowded. +% \font\ssecsl=cmsl10 scaled \magstep1 +% \font\ssectt=cmtt10 scaled \magstep1 +% \font\ssecsf=cmss10 scaled \magstep1 + +%\font\ssecrm=cmb10 scaled 1315 % Note the use of cmb rather than cmbx. +%\font\ssecit=cmti10 scaled 1315 % Also, the size is a little larger than +%\font\ssecsl=cmsl10 scaled 1315 % being scaled magstep1. +%\font\ssectt=cmtt10 scaled 1315 +%\font\ssecsf=cmss10 scaled 1315 + +%\let\ssecbf=\ssecrm + +\font\ssecrm=cmbx12 scaled \magstephalf +\font\ssecit=cmti12 scaled \magstephalf +\font\ssecsl=cmsl12 scaled \magstephalf +\font\ssectt=cmtt12 scaled \magstephalf +\font\ssecsf=cmss12 scaled \magstephalf +\font\ssecbf=cmbx12 scaled \magstephalf +\font\ssecsc=cmcsc10 scaled \magstep1 +\font\sseci=cmmi12 scaled \magstephalf +\font\ssecsy=cmsy10 scaled \magstep1 +% The smallcaps and symbol fonts should actually be scaled \magstep1.5, +% but that is not a standard magnification. + +% Fonts for title page: +\font\titlerm = cmbx12 scaled \magstep3 +\let\authorrm = \secrm + +% In order for the font changes to affect most math symbols and letters, +% we have to define the \textfont of the standard families. Since +% texinfo doesn't allow for producing subscripts and superscripts, we +% don't bother to reset \scriptfont and \scriptscriptfont (which would +% also require loading a lot more fonts). +% +\def\resetmathfonts{% + \textfont0 = \tenrm \textfont1 = \teni \textfont2 = \tensy + \textfont\itfam = \tenit \textfont\slfam = \tensl \textfont\bffam = \tenbf + \textfont\ttfam = \tentt \textfont\sffam = \tensf +} + + +% The font-changing commands redefine the meanings of \tenSTYLE, instead +% of just \STYLE. We do this so that font changes will continue to work +% in math mode, where it is the current \fam that is relevant in most +% cases, not the current. Plain TeX does, for example, +% \def\bf{\fam=\bffam \tenbf} By redefining \tenbf, we obviate the need +% to redefine \bf itself. +\def\textfonts{% + \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl + \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc + \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy + \resetmathfonts} +\def\chapfonts{% + \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl + \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc + \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy + \resetmathfonts} +\def\secfonts{% + \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl + \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc + \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy + \resetmathfonts} +\def\subsecfonts{% + \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl + \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc + \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy + \resetmathfonts} +\def\indexfonts{% + \let\tenrm=\indrm \let\tenit=\indit \let\tensl=\indsl + \let\tenbf=\indbf \let\tentt=\indtt \let\smallcaps=\indsc + \let\tensf=\indsf \let\teni=\indi \let\tensy=\indsy + \resetmathfonts} + +% Set up the default fonts, so we can use them for creating boxes. +% +\textfonts + +% Count depth in font-changes, for error checks +\newcount\fontdepth \fontdepth=0 + +% Fonts for short table of contents. +\font\shortcontrm=cmr12 +\font\shortcontbf=cmbx12 +\font\shortcontsl=cmsl12 + +%% Add scribe-like font environments, plus @l for inline lisp (usually sans +%% serif) and @ii for TeX italic + +% \smartitalic{ARG} outputs arg in italics, followed by an italic correction +% unless the following character is such as not to need one. +\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else\/\fi\fi\fi} +\def\smartitalic#1{{\sl #1}\futurelet\next\smartitalicx} + +\let\i=\smartitalic +\let\var=\smartitalic +\let\dfn=\smartitalic +\let\emph=\smartitalic +\let\cite=\smartitalic + +\def\b#1{{\bf #1}} +\let\strong=\b + +% We can't just use \exhyphenpenalty, because that only has effect at +% the end of a paragraph. Restore normal hyphenation at the end of the +% group within which \nohyphenation is presumably called. +% +\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation} +\def\restorehyphenation{\hyphenchar\font = `- } + +\def\t#1{% + {\tt \nohyphenation \rawbackslash \frenchspacing #1}% + \null +} +\let\ttfont = \t +%\def\samp #1{`{\tt \rawbackslash \frenchspacing #1}'\null} +\def\samp #1{`\tclose{#1}'\null} +\def\key #1{{\tt \nohyphenation \uppercase{#1}}\null} +\def\ctrl #1{{\tt \rawbackslash \hat}#1} + +\let\file=\samp + +% @code is a modification of @t, +% which makes spaces the same size as normal in the surrounding text. +\def\tclose#1{% + {% + % Change normal interword space to be same as for the current font. + \spaceskip = \fontdimen2\font + % + % Switch to typewriter. + \tt + % + % But `\ ' produces the large typewriter interword space. + \def\ {{\spaceskip = 0pt{} }}% + % + % Turn off hyphenation. + \nohyphenation + % + \rawbackslash + \frenchspacing + #1% + }% + \null +} +\let\code=\tclose +%\let\exp=\tclose %Was temporary + +% @kbd is like @code, except that if the argument is just one @key command, +% then @kbd has no effect. + +\def\xkey{\key} +\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}% +\ifx\one\xkey\ifx\threex\three \key{#2}% +\else\tclose{\look}\fi +\else\tclose{\look}\fi} + +% Typeset a dimension, e.g., `in' or `pt'. The only reason for the +% argument is to make the input look right: @dmn{pt} instead of +% @dmn{}pt. +% +\def\dmn#1{\thinspace #1} + +\def\kbd#1{\def\look{#1}\expandafter\kbdfoo\look??\par} + +\def\l#1{{\li #1}\null} % + +\def\r#1{{\rm #1}} % roman font +% Use of \lowercase was suggested. +\def\sc#1{{\smallcaps#1}} % smallcaps font +\def\ii#1{{\it #1}} % italic font + +\message{page headings,} + +\newskip\titlepagetopglue \titlepagetopglue = 1.5in +\newskip\titlepagebottomglue \titlepagebottomglue = 2pc + +% First the title page. Must do @settitle before @titlepage. +\def\titlefont#1{{\titlerm #1}} + +\newif\ifseenauthor +\newif\iffinishedtitlepage + +\def\shorttitlepage{\parsearg\shorttitlepagezzz} +\def\shorttitlepagezzz #1{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}% + \endgroup\page\hbox{}\page} + +\def\titlepage{\begingroup \parindent=0pt \textfonts + \let\subtitlerm=\tenrm +% I deinstalled the following change because \cmr12 is undefined. +% This change was not in the ChangeLog anyway. --rms. +% \let\subtitlerm=\cmr12 + \def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}% + % + \def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines}% + % + % Leave some space at the very top of the page. + \vglue\titlepagetopglue + % + % Now you can print the title using @title. + \def\title{\parsearg\titlezzz}% + \def\titlezzz##1{\leftline{\titlefont{##1}} + % print a rule at the page bottom also. + \finishedtitlepagefalse + \vskip4pt \hrule height 4pt \vskip4pt}% + % No rule at page bottom unless we print one at the top with @title. + \finishedtitlepagetrue + % + % Now you can put text using @subtitle. + \def\subtitle{\parsearg\subtitlezzz}% + \def\subtitlezzz##1{{\subtitlefont \rightline{##1}}}% + % + % @author should come last, but may come many times. + \def\author{\parsearg\authorzzz}% + \def\authorzzz##1{\ifseenauthor\else\vskip 0pt plus 1filll\seenauthortrue\fi + {\authorfont \leftline{##1}}}% + % + % Most title ``pages'' are actually two pages long, with space + % at the top of the second. We don't want the ragged left on the second. + \let\oldpage = \page + \def\page{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + \oldpage + \let\page = \oldpage + \hbox{}}% +% \def\page{\oldpage \hbox{}} +} + +\def\Etitlepage{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + % It is important to do the page break before ending the group, + % because the headline and footline are only empty inside the group. + % If we use the new definition of \page, we always get a blank page + % after the title page, which we certainly don't want. + \oldpage + \endgroup + \HEADINGSon +} + +\def\finishtitlepage{% + \vskip4pt \hrule height 2pt + \vskip\titlepagebottomglue + \finishedtitlepagetrue +} + +%%% Set up page headings and footings. + +\let\thispage=\folio + +\newtoks \evenheadline % Token sequence for heading line of even pages +\newtoks \oddheadline % Token sequence for heading line of odd pages +\newtoks \evenfootline % Token sequence for footing line of even pages +\newtoks \oddfootline % Token sequence for footing line of odd pages + +% Now make Tex use those variables +\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline + \else \the\evenheadline \fi}} +\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline + \else \the\evenfootline \fi}\HEADINGShook} +\let\HEADINGShook=\relax + +% Commands to set those variables. +% For example, this is what @headings on does +% @evenheading @thistitle|@thispage|@thischapter +% @oddheading @thischapter|@thispage|@thistitle +% @evenfooting @thisfile|| +% @oddfooting ||@thisfile + +\def\evenheading{\parsearg\evenheadingxxx} +\def\oddheading{\parsearg\oddheadingxxx} +\def\everyheading{\parsearg\everyheadingxxx} + +\def\evenfooting{\parsearg\evenfootingxxx} +\def\oddfooting{\parsearg\oddfootingxxx} +\def\everyfooting{\parsearg\everyfootingxxx} + +{\catcode`\@=0 % + +\gdef\evenheadingxxx #1{\evenheadingyyy #1@|@|@|@|\finish} +\gdef\evenheadingyyy #1@|#2@|#3@|#4\finish{% +\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\oddheadingxxx #1{\oddheadingyyy #1@|@|@|@|\finish} +\gdef\oddheadingyyy #1@|#2@|#3@|#4\finish{% +\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\everyheadingxxx #1{\everyheadingyyy #1@|@|@|@|\finish} +\gdef\everyheadingyyy #1@|#2@|#3@|#4\finish{% +\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}} +\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\evenfootingxxx #1{\evenfootingyyy #1@|@|@|@|\finish} +\gdef\evenfootingyyy #1@|#2@|#3@|#4\finish{% +\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\oddfootingxxx #1{\oddfootingyyy #1@|@|@|@|\finish} +\gdef\oddfootingyyy #1@|#2@|#3@|#4\finish{% +\global\oddfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\everyfootingxxx #1{\everyfootingyyy #1@|@|@|@|\finish} +\gdef\everyfootingyyy #1@|#2@|#3@|#4\finish{% +\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}} +\global\oddfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} +% +}% unbind the catcode of @. + +% @headings double turns headings on for double-sided printing. +% @headings single turns headings on for single-sided printing. +% @headings off turns them off. +% @headings on same as @headings double, retained for compatibility. +% @headings after turns on double-sided headings after this page. +% @headings doubleafter turns on double-sided headings after this page. +% @headings singleafter turns on single-sided headings after this page. +% By default, they are off. + +\def\headings #1 {\csname HEADINGS#1\endcsname} + +\def\HEADINGSoff{ +\global\evenheadline={\hfil} \global\evenfootline={\hfil} +\global\oddheadline={\hfil} \global\oddfootline={\hfil}} +\HEADINGSoff +% When we turn headings on, set the page number to 1. +% For double-sided printing, put current file name in lower left corner, +% chapter name on inside top of right hand pages, document +% title on inside top of left hand pages, and page numbers on outside top +% edge of all pages. +\def\HEADINGSdouble{ +%\pagealignmacro +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +} +% For single-sided printing, chapter title goes across top left of page, +% page number on top right. +\def\HEADINGSsingle{ +%\pagealignmacro +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +} +\def\HEADINGSon{\HEADINGSdouble} + +\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex} +\let\HEADINGSdoubleafter=\HEADINGSafter +\def\HEADINGSdoublex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +} + +\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex} +\def\HEADINGSsinglex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +} + +% Subroutines used in generating headings +% Produces Day Month Year style of output. +\def\today{\number\day\space +\ifcase\month\or +January\or February\or March\or April\or May\or June\or +July\or August\or September\or October\or November\or December\fi +\space\number\year} + +% Use this if you want the Month Day, Year style of output. +%\def\today{\ifcase\month\or +%January\or February\or March\or April\or May\or June\or +%July\or August\or September\or October\or November\or December\fi +%\space\number\day, \number\year} + +% @settitle line... specifies the title of the document, for headings +% It generates no output of its own + +\def\thistitle{No Title} +\def\settitle{\parsearg\settitlezzz} +\def\settitlezzz #1{\gdef\thistitle{#1}} + +\message{tables,} + +% @tabs -- simple alignment + +% These don't work. For one thing, \+ is defined as outer. +% So these macros cannot even be defined. + +%\def\tabs{\parsearg\tabszzz} +%\def\tabszzz #1{\settabs\+#1\cr} +%\def\tabline{\parsearg\tablinezzz} +%\def\tablinezzz #1{\+#1\cr} +%\def\&{&} + +% Tables -- @table, @ftable, @vtable, @item(x), @kitem(x), @xitem(x). + +% default indentation of table text +\newdimen\tableindent \tableindent=.8in +% default indentation of @itemize and @enumerate text +\newdimen\itemindent \itemindent=.3in +% margin between end of table item and start of table text. +\newdimen\itemmargin \itemmargin=.1in + +% used internally for \itemindent minus \itemmargin +\newdimen\itemmax + +% Note @table, @vtable, and @vtable define @item, @itemx, etc., with +% these defs. +% They also define \itemindex +% to index the item name in whatever manner is desired (perhaps none). + +\def\internalBitem{\smallbreak \parsearg\itemzzz} +\def\internalBitemx{\par \parsearg\itemzzz} + +\def\internalBxitem "#1"{\def\xitemsubtopix{#1} \smallbreak \parsearg\xitemzzz} +\def\internalBxitemx "#1"{\def\xitemsubtopix{#1} \par \parsearg\xitemzzz} + +\def\internalBkitem{\smallbreak \parsearg\kitemzzz} +\def\internalBkitemx{\par \parsearg\kitemzzz} + +\def\kitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \lastfunction}}% + \itemzzz {#1}} + +\def\xitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \xitemsubtopic}}% + \itemzzz {#1}} + +\def\itemzzz #1{\begingroup % + \advance\hsize by -\rightskip + \advance\hsize by -\tableindent + \setbox0=\hbox{\itemfont{#1}}% + \itemindex{#1}% + \nobreak % This prevents a break before @itemx. + % + % Be sure we are not still in the middle of a paragraph. + \parskip=0in + \par + % + % If the item text does not fit in the space we have, put it on a line + % by itself, and do not allow a page break either before or after that + % line. We do not start a paragraph here because then if the next + % command is, e.g., @kindex, the whatsit would get put into the + % horizontal list on a line by itself, resulting in extra blank space. + \ifdim \wd0>\itemmax + \setbox0=\hbox{\hskip \leftskip \hskip -\tableindent \unhbox0}\box0 + \nobreak + \else + % The item text fits into the space. Start a paragraph, so that the + % following text (if any) will end up on the same line. Since that + % text will be indented by \tableindent, we make the item text be in + % a zero-width box. + \noindent + \rlap{\hskip -\tableindent\box0}% + \fi + \endgroup +} + +\def\item{\errmessage{@item while not in a table}} +\def\itemx{\errmessage{@itemx while not in a table}} +\def\kitem{\errmessage{@kitem while not in a table}} +\def\kitemx{\errmessage{@kitemx while not in a table}} +\def\xitem{\errmessage{@xitem while not in a table}} +\def\xitemx{\errmessage{@xitemx while not in a table}} + +%% Contains a kludge to get @end[description] to work +\def\description{\tablez{\dontindex}{1}{}{}{}{}} + +\def\table{\begingroup\inENV\obeylines\obeyspaces\tablex} +{\obeylines\obeyspaces% +\gdef\tablex #1^^M{% +\tabley\dontindex#1 \endtabley}} + +\def\ftable{\begingroup\inENV\obeylines\obeyspaces\ftablex} +{\obeylines\obeyspaces% +\gdef\ftablex #1^^M{% +\tabley\fnitemindex#1 \endtabley +\def\Eftable{\endgraf\endgroup\afterenvbreak}% +\let\Etable=\relax}} + +\def\vtable{\begingroup\inENV\obeylines\obeyspaces\vtablex} +{\obeylines\obeyspaces% +\gdef\vtablex #1^^M{% +\tabley\vritemindex#1 \endtabley +\def\Evtable{\endgraf\endgroup\afterenvbreak}% +\let\Etable=\relax}} + +\def\dontindex #1{} +\def\fnitemindex #1{\doind {fn}{\code{#1}}}% +\def\vritemindex #1{\doind {vr}{\code{#1}}}% + +{\obeyspaces % +\gdef\tabley#1#2 #3 #4 #5 #6 #7\endtabley{\endgroup% +\tablez{#1}{#2}{#3}{#4}{#5}{#6}}} + +\def\tablez #1#2#3#4#5#6{% +\aboveenvbreak % +\begingroup % +\def\Edescription{\Etable}% Neccessary kludge. +\let\itemindex=#1% +\ifnum 0#3>0 \advance \leftskip by #3\mil \fi % +\ifnum 0#4>0 \tableindent=#4\mil \fi % +\ifnum 0#5>0 \advance \rightskip by #5\mil \fi % +\def\itemfont{#2}% +\itemmax=\tableindent % +\advance \itemmax by -\itemmargin % +\advance \leftskip by \tableindent % +\exdentamount=\tableindent +\parindent = 0pt +\parskip = \smallskipamount +\ifdim \parskip=0pt \parskip=2pt \fi% +\def\Etable{\endgraf\endgroup\afterenvbreak}% +\let\item = \internalBitem % +\let\itemx = \internalBitemx % +\let\kitem = \internalBkitem % +\let\kitemx = \internalBkitemx % +\let\xitem = \internalBxitem % +\let\xitemx = \internalBxitemx % +} + +% This is the counter used by @enumerate, which is really @itemize + +\newcount \itemno + +\def\itemize{\parsearg\itemizezzz} + +\def\itemizezzz #1{% + \begingroup % ended by the @end itemsize + \itemizey {#1}{\Eitemize} +} + +\def\itemizey #1#2{% +\aboveenvbreak % +\itemmax=\itemindent % +\advance \itemmax by -\itemmargin % +\advance \leftskip by \itemindent % +\exdentamount=\itemindent +\parindent = 0pt % +\parskip = \smallskipamount % +\ifdim \parskip=0pt \parskip=2pt \fi% +\def#2{\endgraf\endgroup\afterenvbreak}% +\def\itemcontents{#1}% +\let\item=\itemizeitem} + +% Set sfcode to normal for the chars that usually have another value. +% These are `.?!:;,' +\def\frenchspacing{\sfcode46=1000 \sfcode63=1000 \sfcode33=1000 + \sfcode58=1000 \sfcode59=1000 \sfcode44=1000 } + +% \splitoff TOKENS\endmark defines \first to be the first token in +% TOKENS, and \rest to be the remainder. +% +\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}% + +% Allow an optional argument of an uppercase letter, lowercase letter, +% or number, to specify the first label in the enumerated list. No +% argument is the same as `1'. +% +\def\enumerate{\parsearg\enumeratezzz} +\def\enumeratezzz #1{\enumeratey #1 \endenumeratey} +\def\enumeratey #1 #2\endenumeratey{% + \begingroup % ended by the @end enumerate + % + % If we were given no argument, pretend we were given `1'. + \def\thearg{#1}% + \ifx\thearg\empty \def\thearg{1}\fi + % + % Detect if the argument is a single token. If so, it might be a + % letter. Otherwise, the only valid thing it can be is a number. + % (We will always have one token, because of the test we just made. + % This is a good thing, since \splitoff doesn't work given nothing at + % all -- the first parameter is undelimited.) + \expandafter\splitoff\thearg\endmark + \ifx\rest\empty + % Only one token in the argument. It could still be anything. + % A ``lowercase letter'' is one whose \lccode is nonzero. + % An ``uppercase letter'' is one whose \lccode is both nonzero, and + % not equal to itself. + % Otherwise, we assume it's a number. + % + % We need the \relax at the end of the \ifnum lines to stop TeX from + % continuing to look for a . + % + \ifnum\lccode\expandafter`\thearg=0\relax + \numericenumerate % a number (we hope) + \else + % It's a letter. + \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax + \lowercaseenumerate % lowercase letter + \else + \uppercaseenumerate % uppercase letter + \fi + \fi + \else + % Multiple tokens in the argument. We hope it's a number. + \numericenumerate + \fi +} + +% An @enumerate whose labels are integers. The starting integer is +% given in \thearg. +% +\def\numericenumerate{% + \itemno = \thearg + \startenumeration{\the\itemno}% +} + +% The starting (lowercase) letter is in \thearg. +\def\lowercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more lowercase letters in @enumerate; get a bigger + alphabet}% + \fi + \char\lccode\itemno + }% +} + +% The starting (uppercase) letter is in \thearg. +\def\uppercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more uppercase letters in @enumerate; get a bigger + alphabet} + \fi + \char\uccode\itemno + }% +} + +% Call itemizey, adding a period to the first argument and supplying the +% common last two arguments. Also subtract one from the initial value in +% \itemno, since @item increments \itemno. +% +\def\startenumeration#1{% + \advance\itemno by -1 + \itemizey{#1.}\Eenumerate\flushcr +} + +% @alphaenumerate and @capsenumerate are abbreviations for giving an arg +% to @enumerate. +% +\def\alphaenumerate{\enumerate{a}} +\def\capsenumerate{\enumerate{A}} +\def\Ealphaenumerate{\Eenumerate} +\def\Ecapsenumerate{\Eenumerate} + +% Definition of @item while inside @itemize. + +\def\itemizeitem{% +\advance\itemno by 1 +{\let\par=\endgraf \smallbreak}% +\ifhmode \errmessage{\in hmode at itemizeitem}\fi +{\parskip=0in \hskip 0pt +\hbox to 0pt{\hss \itemcontents\hskip \itemmargin}% +\vadjust{\penalty 1200}}% +\flushcr} + +\message{indexing,} +% Index generation facilities + +% Define \newwrite to be identical to plain tex's \newwrite +% except not \outer, so it can be used within \newindex. +{\catcode`\@=11 +\gdef\newwrite{\alloc@7\write\chardef\sixt@@n}} + +% \newindex {foo} defines an index named foo. +% It automatically defines \fooindex such that +% \fooindex ...rest of line... puts an entry in the index foo. +% It also defines \fooindfile to be the number of the output channel for +% the file that accumulates this index. The file's extension is foo. +% The name of an index should be no more than 2 characters long +% for the sake of vms. + +\def\newindex #1{ +\expandafter\newwrite \csname#1indfile\endcsname% Define number for output file +\openout \csname#1indfile\endcsname \jobname.#1 % Open the file +\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex +\noexpand\doindex {#1}} +} + +% @defindex foo == \newindex{foo} + +\def\defindex{\parsearg\newindex} + +% Define @defcodeindex, like @defindex except put all entries in @code. + +\def\newcodeindex #1{ +\expandafter\newwrite \csname#1indfile\endcsname% Define number for output file +\openout \csname#1indfile\endcsname \jobname.#1 % Open the file +\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex +\noexpand\docodeindex {#1}} +} + +\def\defcodeindex{\parsearg\newcodeindex} + +% @synindex foo bar makes index foo feed into index bar. +% Do this instead of @defindex foo if you don't want it as a separate index. +\def\synindex #1 #2 {% +\expandafter\let\expandafter\synindexfoo\expandafter=\csname#2indfile\endcsname +\expandafter\let\csname#1indfile\endcsname=\synindexfoo +\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex +\noexpand\doindex {#2}}% +} + +% @syncodeindex foo bar similar, but put all entries made for index foo +% inside @code. +\def\syncodeindex #1 #2 {% +\expandafter\let\expandafter\synindexfoo\expandafter=\csname#2indfile\endcsname +\expandafter\let\csname#1indfile\endcsname=\synindexfoo +\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex +\noexpand\docodeindex {#2}}% +} + +% Define \doindex, the driver for all \fooindex macros. +% Argument #1 is generated by the calling \fooindex macro, +% and it is "foo", the name of the index. + +% \doindex just uses \parsearg; it calls \doind for the actual work. +% This is because \doind is more useful to call from other macros. + +% There is also \dosubind {index}{topic}{subtopic} +% which makes an entry in a two-level index such as the operation index. + +\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer} +\def\singleindexer #1{\doind{\indexname}{#1}} + +% like the previous two, but they put @code around the argument. +\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer} +\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}} + +\def\indexdummies{% +\def\_{{\realbackslash _}}% +\def\w{\realbackslash w }% +\def\bf{\realbackslash bf }% +\def\rm{\realbackslash rm }% +\def\sl{\realbackslash sl }% +\def\sf{\realbackslash sf}% +\def\tt{\realbackslash tt}% +\def\gtr{\realbackslash gtr}% +\def\less{\realbackslash less}% +\def\hat{\realbackslash hat}% +\def\char{\realbackslash char}% +\def\TeX{\realbackslash TeX}% +\def\dots{\realbackslash dots }% +\def\copyright{\realbackslash copyright }% +\def\tclose##1{\realbackslash tclose {##1}}% +\def\code##1{\realbackslash code {##1}}% +\def\samp##1{\realbackslash samp {##1}}% +\def\t##1{\realbackslash r {##1}}% +\def\r##1{\realbackslash r {##1}}% +\def\i##1{\realbackslash i {##1}}% +\def\b##1{\realbackslash b {##1}}% +\def\cite##1{\realbackslash cite {##1}}% +\def\key##1{\realbackslash key {##1}}% +\def\file##1{\realbackslash file {##1}}% +\def\var##1{\realbackslash var {##1}}% +\def\kbd##1{\realbackslash kbd {##1}}% +\def\dfn##1{\realbackslash dfn {##1}}% +\def\emph##1{\realbackslash emph {##1}}% +} + +% \indexnofonts no-ops all font-change commands. +% This is used when outputting the strings to sort the index by. +\def\indexdummyfont#1{#1} +\def\indexdummytex{TeX} +\def\indexdummydots{...} + +\def\indexnofonts{% +\let\w=\indexdummyfont +\let\t=\indexdummyfont +\let\r=\indexdummyfont +\let\i=\indexdummyfont +\let\b=\indexdummyfont +\let\emph=\indexdummyfont +\let\strong=\indexdummyfont +\let\cite=\indexdummyfont +\let\sc=\indexdummyfont +%Don't no-op \tt, since it isn't a user-level command +% and is used in the definitions of the active chars like <, >, |... +%\let\tt=\indexdummyfont +\let\tclose=\indexdummyfont +\let\code=\indexdummyfont +\let\file=\indexdummyfont +\let\samp=\indexdummyfont +\let\kbd=\indexdummyfont +\let\key=\indexdummyfont +\let\var=\indexdummyfont +\let\TeX=\indexdummytex +\let\dots=\indexdummydots +} + +% To define \realbackslash, we must make \ not be an escape. +% We must first make another character (@) an escape +% so we do not become unable to do a definition. + +{\catcode`\@=0 \catcode`\\=\other +@gdef@realbackslash{\}} + +\let\indexbackslash=0 %overridden during \printindex. + +\def\doind #1#2{% +{\count10=\lastpenalty % +{\indexdummies % Must do this here, since \bf, etc expand at this stage +\escapechar=`\\% +{\let\folio=0% Expand all macros now EXCEPT \folio +\def\rawbackslashxx{\indexbackslash}% \indexbackslash isn't defined now +% so it will be output as is; and it will print as backslash in the indx. +% +% Now process the index-string once, with all font commands turned off, +% to get the string to sort the index by. +{\indexnofonts +\xdef\temp1{#2}% +}% +% Now produce the complete index entry. We process the index-string again, +% this time with font commands expanded, to get what to print in the index. +\edef\temp{% +\write \csname#1indfile\endcsname{% +\realbackslash entry {\temp1}{\folio}{#2}}}% +\temp }% +}\penalty\count10}} + +\def\dosubind #1#2#3{% +{\count10=\lastpenalty % +{\indexdummies % Must do this here, since \bf, etc expand at this stage +\escapechar=`\\% +{\let\folio=0% +\def\rawbackslashxx{\indexbackslash}% +% +% Now process the index-string once, with all font commands turned off, +% to get the string to sort the index by. +{\indexnofonts +\xdef\temp1{#2 #3}% +}% +% Now produce the complete index entry. We process the index-string again, +% this time with font commands expanded, to get what to print in the index. +\edef\temp{% +\write \csname#1indfile\endcsname{% +\realbackslash entry {\temp1}{\folio}{#2}{#3}}}% +\temp }% +}\penalty\count10}} + +% The index entry written in the file actually looks like +% \entry {sortstring}{page}{topic} +% or +% \entry {sortstring}{page}{topic}{subtopic} +% The texindex program reads in these files and writes files +% containing these kinds of lines: +% \initial {c} +% before the first topic whose initial is c +% \entry {topic}{pagelist} +% for a topic that is used without subtopics +% \primary {topic} +% for the beginning of a topic that is used with subtopics +% \secondary {subtopic}{pagelist} +% for each subtopic. + +% Define the user-accessible indexing commands +% @findex, @vindex, @kindex, @cindex. + +\def\findex {\fnindex} +\def\kindex {\kyindex} +\def\cindex {\cpindex} +\def\vindex {\vrindex} +\def\tindex {\tpindex} +\def\pindex {\pgindex} + +\def\cindexsub {\begingroup\obeylines\cindexsub} +{\obeylines % +\gdef\cindexsub "#1" #2^^M{\endgroup % +\dosubind{cp}{#2}{#1}}} + +% Define the macros used in formatting output of the sorted index material. + +% This is what you call to cause a particular index to get printed. +% Write +% @unnumbered Function Index +% @printindex fn + +\def\printindex{\parsearg\doprintindex} + +\def\doprintindex#1{% + \tex + \dobreak \chapheadingskip {10000} + \catcode`\%=\other\catcode`\&=\other\catcode`\#=\other + \catcode`\$=\other\catcode`\_=\other + \catcode`\~=\other + % + % The following don't help, since the chars were translated + % when the raw index was written, and their fonts were discarded + % due to \indexnofonts. + %\catcode`\"=\active + %\catcode`\^=\active + %\catcode`\_=\active + %\catcode`\|=\active + %\catcode`\<=\active + %\catcode`\>=\active + % % + \def\indexbackslash{\rawbackslashxx} + \indexfonts\rm \tolerance=9500 \advance\baselineskip -1pt + \begindoublecolumns + % + % See if the index file exists and is nonempty. + \openin 1 \jobname.#1s + \ifeof 1 + % \enddoublecolumns gets confused if there is no text in the index, + % and it loses the chapter title and the aux file entries for the + % index. The easiest way to prevent this problem is to make sure + % there is some text. + (Index is nonexistent) + \else + % + % If the index file exists but is empty, then \openin leaves \ifeof + % false. We have to make TeX try to read something from the file, so + % it can discover if there is anything in it. + \read 1 to \temp + \ifeof 1 + (Index is empty) + \else + \input \jobname.#1s + \fi + \fi + \closein 1 + \enddoublecolumns + \Etex +} + +% These macros are used by the sorted index file itself. +% Change them to control the appearance of the index. + +% Same as \bigskipamount except no shrink. +% \balancecolumns gets confused if there is any shrink. +\newskip\initialskipamount \initialskipamount 12pt plus4pt + +\def\initial #1{% +{\let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt +\ifdim\lastskip<\initialskipamount +\removelastskip \penalty-200 \vskip \initialskipamount\fi +\line{\secbf#1\hfill}\kern 2pt\penalty10000}} + +% This typesets a paragraph consisting of #1, dot leaders, and then #2 +% flush to the right margin. It is used for index and table of contents +% entries. The paragraph is indented by \leftskip. +% +\def\entry #1#2{\begingroup + % + % Start a new paragraph if necessary, so our assignments below can't + % affect previous text. + \par + % + % Do not fill out the last line with white space. + \parfillskip = 0in + % + % No extra space above this paragraph. + \parskip = 0in + % + % Do not prefer a separate line ending with a hyphen to fewer lines. + \finalhyphendemerits = 0 + % + % \hangindent is only relevant when the entry text and page number + % don't both fit on one line. In that case, bob suggests starting the + % dots pretty far over on the line. Unfortunately, a large + % indentation looks wrong when the entry text itself is broken across + % lines. So we use a small indentation and put up with long leaders. + % + % \hangafter is reset to 1 (which is the value we want) at the start + % of each paragraph, so we need not do anything with that. + \hangindent=2em + % + % When the entry text needs to be broken, just fill out the first line + % with blank space. + \rightskip = 0pt plus1fil + % + % Start a ``paragraph'' for the index entry so the line breaking + % parameters we've set above will have an effect. + \noindent + % + % Insert the text of the index entry. TeX will do line-breaking on it. + #1% + % + % If we must, put the page number on a line of its own, and fill out + % this line with blank space. (The \hfil is overwhelmed with the + % fill leaders glue in \indexdotfill if the page number does fit.) + \hfil\penalty50 + \null\nobreak\indexdotfill % Have leaders before the page number. + % + % The `\ ' here is removed by the implicit \unskip that TeX does as + % part of (the primitive) \par. Without it, a spurious underfull + % \hbox ensues. + \ #2% The page number ends the paragraph. + \par +\endgroup} + +% Like \dotfill except takes at least 1 em. +\def\indexdotfill{\cleaders + \hbox{$\mathsurround=0pt \mkern1.5mu . \mkern1.5mu$}\hskip 1em plus 1fill} + +\def\primary #1{\line{#1\hfil}} + +\newskip\secondaryindent \secondaryindent=0.5cm + +\def\secondary #1#2{ +{\parfillskip=0in \parskip=0in +\hangindent =1in \hangafter=1 +\noindent\hskip\secondaryindent\hbox{#1}\indexdotfill #2\par +}} + +%% Define two-column mode, which is used in indexes. +%% Adapted from the TeXbook, page 416. +\catcode `\@=11 + +\newbox\partialpage + +\newdimen\doublecolumnhsize + +\def\begindoublecolumns{\begingroup + % Grab any single-column material above us. + \output = {\global\setbox\partialpage + =\vbox{\unvbox255\kern -\topskip \kern \baselineskip}}% + \eject + % + % Now switch to the double-column output routine. + \output={\doublecolumnout}% + % + % Change the page size parameters. We could do this once outside this + % routine, in each of @smallbook, @afourpaper, and the default 8.5x11 + % format, but then we repeat the same computation. Repeating a couple + % of assignments once per index is clearly meaningless for the + % execution time, so we may as well do it once. + % + % First we halve the line length, less a little for the gutter between + % the columns. We compute the gutter based on the line length, so it + % changes automatically with the paper format. The magic constant + % below is chosen so that the gutter has the same value (well, +- < + % 1pt) as it did when we hard-coded it. + % + % We put the result in a separate register, \doublecolumhsize, so we + % can restore it in \pagesofar, after \hsize itself has (potentially) + % been clobbered. + % + \doublecolumnhsize = \hsize + \advance\doublecolumnhsize by -.04154\hsize + \divide\doublecolumnhsize by 2 + \hsize = \doublecolumnhsize + % + % Double the \vsize as well. (We don't need a separate register here, + % since nobody clobbers \vsize.) + \vsize = 2\vsize + \doublecolumnpagegoal +} + +\def\enddoublecolumns{\eject \endgroup \pagegoal=\vsize \unvbox\partialpage} + +\def\doublecolumnsplit{\splittopskip=\topskip \splitmaxdepth=\maxdepth + \global\dimen@=\pageheight \global\advance\dimen@ by-\ht\partialpage + \global\setbox1=\vsplit255 to\dimen@ \global\setbox0=\vbox{\unvbox1} + \global\setbox3=\vsplit255 to\dimen@ \global\setbox2=\vbox{\unvbox3} + \ifdim\ht0>\dimen@ \setbox255=\vbox{\unvbox0\unvbox2} \global\setbox255=\copy5 \fi + \ifdim\ht2>\dimen@ \setbox255=\vbox{\unvbox0\unvbox2} \global\setbox255=\copy5 \fi +} +\def\doublecolumnpagegoal{% + \dimen@=\vsize \advance\dimen@ by-2\ht\partialpage \global\pagegoal=\dimen@ +} +\def\pagesofar{\unvbox\partialpage % + \hsize=\doublecolumnhsize % have to restore this since output routine + \wd0=\hsize \wd2=\hsize \hbox to\pagewidth{\box0\hfil\box2}} +\def\doublecolumnout{% + \setbox5=\copy255 + {\vbadness=10000 \doublecolumnsplit} + \ifvbox255 + \setbox0=\vtop to\dimen@{\unvbox0} + \setbox2=\vtop to\dimen@{\unvbox2} + \onepageout\pagesofar \unvbox255 \penalty\outputpenalty + \else + \setbox0=\vbox{\unvbox5} + \ifvbox0 + \dimen@=\ht0 \advance\dimen@ by\topskip \advance\dimen@ by-\baselineskip + \divide\dimen@ by2 \splittopskip=\topskip \splitmaxdepth=\maxdepth + {\vbadness=10000 + \loop \global\setbox5=\copy0 + \setbox1=\vsplit5 to\dimen@ + \setbox3=\vsplit5 to\dimen@ + \ifvbox5 \global\advance\dimen@ by1pt \repeat + \setbox0=\vbox to\dimen@{\unvbox1} + \setbox2=\vbox to\dimen@{\unvbox3} + \global\setbox\partialpage=\vbox{\pagesofar} + \doublecolumnpagegoal + } + \fi + \fi +} + +\catcode `\@=\other +\message{sectioning,} +% Define chapters, sections, etc. + +\newcount \chapno +\newcount \secno \secno=0 +\newcount \subsecno \subsecno=0 +\newcount \subsubsecno \subsubsecno=0 + +% This counter is funny since it counts through charcodes of letters A, B, ... +\newcount \appendixno \appendixno = `\@ +\def\appendixletter{\char\the\appendixno} + +\newwrite \contentsfile +% This is called from \setfilename. +\def\opencontents{\openout \contentsfile = \jobname.toc} + +% Each @chapter defines this as the name of the chapter. +% page headings and footings can use it. @section does likewise + +\def\thischapter{} \def\thissection{} +\def\seccheck#1{\if \pageno<0 % +\errmessage{@#1 not allowed after generating table of contents}\fi +% +} + +\def\chapternofonts{% +\let\rawbackslash=\relax% +\let\frenchspacing=\relax% +\def\result{\realbackslash result} +\def\equiv{\realbackslash equiv} +\def\expansion{\realbackslash expansion} +\def\print{\realbackslash print} +\def\TeX{\realbackslash TeX} +\def\dots{\realbackslash dots} +\def\copyright{\realbackslash copyright} +\def\tt{\realbackslash tt} +\def\bf{\realbackslash bf } +\def\w{\realbackslash w} +\def\less{\realbackslash less} +\def\gtr{\realbackslash gtr} +\def\hat{\realbackslash hat} +\def\char{\realbackslash char} +\def\tclose##1{\realbackslash tclose {##1}} +\def\code##1{\realbackslash code {##1}} +\def\samp##1{\realbackslash samp {##1}} +\def\r##1{\realbackslash r {##1}} +\def\b##1{\realbackslash b {##1}} +\def\key##1{\realbackslash key {##1}} +\def\file##1{\realbackslash file {##1}} +\def\kbd##1{\realbackslash kbd {##1}} +% These are redefined because @smartitalic wouldn't work inside xdef. +\def\i##1{\realbackslash i {##1}} +\def\cite##1{\realbackslash cite {##1}} +\def\var##1{\realbackslash var {##1}} +\def\emph##1{\realbackslash emph {##1}} +\def\dfn##1{\realbackslash dfn {##1}} +} + +\newcount\absseclevel % used to calculate proper heading level +\newcount\secbase\secbase=0 % @raise/lowersections modify this count + +% @raisesections: treat @section as chapter, @subsection as section, etc. +\def\raisesections{\global\advance\secbase by -1} +\let\up=\raisesections % original BFox name + +% @lowersections: treat @chapter as section, @section as subsection, etc. +\def\lowersections{\global\advance\secbase by 1} +\let\down=\lowersections % original BFox name + +% Choose a numbered-heading macro +% #1 is heading level if unmodified by @raisesections or @lowersections +% #2 is text for heading +\def\numhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 +\ifcase\absseclevel + \chapterzzz{#2} +\or + \seczzz{#2} +\or + \numberedsubseczzz{#2} +\or + \numberedsubsubseczzz{#2} +\else + \ifnum \absseclevel<0 + \chapterzzz{#2} + \else + \numberedsubsubseczzz{#2} + \fi +\fi +} + +% like \numhead, but chooses appendix heading levels +\def\apphead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 +\ifcase\absseclevel + \appendixzzz{#2} +\or + \appendixsectionzzz{#2} +\or + \appendixsubseczzz{#2} +\or + \appendixsubsubseczzz{#2} +\else + \ifnum \absseclevel<0 + \appendixzzz{#2} + \else + \appendixsubsubseczzz{#2} + \fi +\fi +} + +% like \numhead, but chooses numberless heading levels +\def\unnmhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 +\ifcase\absseclevel + \unnumberedzzz{#2} +\or + \unnumberedseczzz{#2} +\or + \unnumberedsubseczzz{#2} +\or + \unnumberedsubsubseczzz{#2} +\else + \ifnum \absseclevel<0 + \unnumberedzzz{#2} + \else + \unnumberedsubsubseczzz{#2} + \fi +\fi +} + + +\def\thischaptername{No Chapter Title} +\outer\def\chapter{\parsearg\chapteryyy} +\def\chapteryyy #1{\numhead0{#1}} % normally numhead0 calls chapterzzz +\def\chapterzzz #1{\seccheck{chapter}% +\secno=0 \subsecno=0 \subsubsecno=0 +\global\advance \chapno by 1 \message{Chapter \the\chapno}% +\chapmacro {#1}{\the\chapno}% +\gdef\thissection{#1}% +\gdef\thischaptername{#1}% +% We don't substitute the actual chapter name into \thischapter +% because we don't want its macros evaluated now. +\xdef\thischapter{Chapter \the\chapno: \noexpand\thischaptername}% +{\chapternofonts% +\edef\temp{{\realbackslash chapentry {#1}{\the\chapno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\donoderef % +\global\let\section = \numberedsec +\global\let\subsection = \numberedsubsec +\global\let\subsubsection = \numberedsubsubsec +}} + +\outer\def\appendix{\parsearg\appendixyyy} +\def\appendixyyy #1{\apphead0{#1}} % normally apphead0 calls appendixzzz +\def\appendixzzz #1{\seccheck{appendix}% +\secno=0 \subsecno=0 \subsubsecno=0 +\global\advance \appendixno by 1 \message{Appendix \appendixletter}% +\chapmacro {#1}{Appendix \appendixletter}% +\gdef\thissection{#1}% +\gdef\thischaptername{#1}% +\xdef\thischapter{Appendix \appendixletter: \noexpand\thischaptername}% +{\chapternofonts% +\edef\temp{{\realbackslash chapentry + {#1}{Appendix \appendixletter}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\appendixnoderef % +\global\let\section = \appendixsec +\global\let\subsection = \appendixsubsec +\global\let\subsubsection = \appendixsubsubsec +}} + +\outer\def\top{\parsearg\unnumberedyyy} +\outer\def\unnumbered{\parsearg\unnumberedyyy} +\def\unnumberedyyy #1{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz +\def\unnumberedzzz #1{\seccheck{unnumbered}% +\secno=0 \subsecno=0 \subsubsecno=0 +% +% This used to be simply \message{#1}, but TeX fully expands the +% argument to \message. Therefore, if #1 contained @-commands, TeX +% expanded them. For example, in `@unnumbered The @cite{Book}', TeX +% expanded @cite (which turns out to cause errors because \cite is meant +% to be executed, not expanded). +% +% Anyway, we don't want the fully-expanded definition of @cite to appear +% as a result of the \message, we just want `@cite' itself. We use +% \the to achieve this: TeX expands \the only once, +% simply yielding the contents of the . +\toks0 = {#1}\message{(\the\toks0)}% +% +\unnumbchapmacro {#1}% +\gdef\thischapter{#1}\gdef\thissection{#1}% +{\chapternofonts% +\edef\temp{{\realbackslash unnumbchapentry {#1}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\unnumbnoderef % +\global\let\section = \unnumberedsec +\global\let\subsection = \unnumberedsubsec +\global\let\subsubsection = \unnumberedsubsubsec +}} + +\outer\def\numberedsec{\parsearg\secyyy} +\def\secyyy #1{\numhead1{#1}} % normally calls seczzz +\def\seczzz #1{\seccheck{section}% +\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 % +\gdef\thissection{#1}\secheading {#1}{\the\chapno}{\the\secno}% +{\chapternofonts% +\edef\temp{{\realbackslash secentry % +{#1}{\the\chapno}{\the\secno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\donoderef % +\penalty 10000 % +}} + +\outer\def\appenixsection{\parsearg\appendixsecyyy} +\outer\def\appendixsec{\parsearg\appendixsecyyy} +\def\appendixsecyyy #1{\apphead1{#1}} % normally calls appendixsectionzzz +\def\appendixsectionzzz #1{\seccheck{appendixsection}% +\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 % +\gdef\thissection{#1}\secheading {#1}{\appendixletter}{\the\secno}% +{\chapternofonts% +\edef\temp{{\realbackslash secentry % +{#1}{\appendixletter}{\the\secno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\appendixnoderef % +\penalty 10000 % +}} + +\outer\def\unnumberedsec{\parsearg\unnumberedsecyyy} +\def\unnumberedsecyyy #1{\unnmhead1{#1}} % normally calls unnumberedseczzz +\def\unnumberedseczzz #1{\seccheck{unnumberedsec}% +\plainsecheading {#1}\gdef\thissection{#1}% +{\chapternofonts% +\edef\temp{{\realbackslash unnumbsecentry{#1}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\unnumbnoderef % +\penalty 10000 % +}} + +\outer\def\numberedsubsec{\parsearg\numberedsubsecyyy} +\def\numberedsubsecyyy #1{\numhead2{#1}} % normally calls numberedsubseczzz +\def\numberedsubseczzz #1{\seccheck{subsection}% +\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 % +\subsecheading {#1}{\the\chapno}{\the\secno}{\the\subsecno}% +{\chapternofonts% +\edef\temp{{\realbackslash subsecentry % +{#1}{\the\chapno}{\the\secno}{\the\subsecno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\donoderef % +\penalty 10000 % +}} + +\outer\def\appendixsubsec{\parsearg\appendixsubsecyyy} +\def\appendixsubsecyyy #1{\apphead2{#1}} % normally calls appendixsubseczzz +\def\appendixsubseczzz #1{\seccheck{appendixsubsec}% +\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 % +\subsecheading {#1}{\appendixletter}{\the\secno}{\the\subsecno}% +{\chapternofonts% +\edef\temp{{\realbackslash subsecentry % +{#1}{\appendixletter}{\the\secno}{\the\subsecno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\appendixnoderef % +\penalty 10000 % +}} + +\outer\def\unnumberedsubsec{\parsearg\unnumberedsubsecyyy} +\def\unnumberedsubsecyyy #1{\unnmhead2{#1}} %normally calls unnumberedsubseczzz +\def\unnumberedsubseczzz #1{\seccheck{unnumberedsubsec}% +\plainsecheading {#1}\gdef\thissection{#1}% +{\chapternofonts% +\edef\temp{{\realbackslash unnumbsubsecentry{#1}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\unnumbnoderef % +\penalty 10000 % +}} + +\outer\def\numberedsubsubsec{\parsearg\numberedsubsubsecyyy} +\def\numberedsubsubsecyyy #1{\numhead3{#1}} % normally numberedsubsubseczzz +\def\numberedsubsubseczzz #1{\seccheck{subsubsection}% +\gdef\thissection{#1}\global\advance \subsubsecno by 1 % +\subsubsecheading {#1} + {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}% +{\chapternofonts% +\edef\temp{{\realbackslash subsubsecentry % + {#1} + {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno} + {\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\donoderef % +\penalty 10000 % +}} + +\outer\def\appendixsubsubsec{\parsearg\appendixsubsubsecyyy} +\def\appendixsubsubsecyyy #1{\apphead3{#1}} % normally appendixsubsubseczzz +\def\appendixsubsubseczzz #1{\seccheck{appendixsubsubsec}% +\gdef\thissection{#1}\global\advance \subsubsecno by 1 % +\subsubsecheading {#1} + {\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}% +{\chapternofonts% +\edef\temp{{\realbackslash subsubsecentry{#1}% + {\appendixletter} + {\the\secno}{\the\subsecno}{\the\subsubsecno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\appendixnoderef % +\penalty 10000 % +}} + +\outer\def\unnumberedsubsubsec{\parsearg\unnumberedsubsubsecyyy} +\def\unnumberedsubsubsecyyy #1{\unnmhead3{#1}} %normally unnumberedsubsubseczzz +\def\unnumberedsubsubseczzz #1{\seccheck{unnumberedsubsubsec}% +\plainsecheading {#1}\gdef\thissection{#1}% +{\chapternofonts% +\edef\temp{{\realbackslash unnumbsubsubsecentry{#1}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\unnumbnoderef % +\penalty 10000 % +}} + +% These are variants which are not "outer", so they can appear in @ifinfo. +% Actually, they should now be obsolete; ordinary section commands should work. +\def\infotop{\parsearg\unnumberedzzz} +\def\infounnumbered{\parsearg\unnumberedzzz} +\def\infounnumberedsec{\parsearg\unnumberedseczzz} +\def\infounnumberedsubsec{\parsearg\unnumberedsubseczzz} +\def\infounnumberedsubsubsec{\parsearg\unnumberedsubsubseczzz} + +\def\infoappendix{\parsearg\appendixzzz} +\def\infoappendixsec{\parsearg\appendixseczzz} +\def\infoappendixsubsec{\parsearg\appendixsubseczzz} +\def\infoappendixsubsubsec{\parsearg\appendixsubsubseczzz} + +\def\infochapter{\parsearg\chapterzzz} +\def\infosection{\parsearg\sectionzzz} +\def\infosubsection{\parsearg\subsectionzzz} +\def\infosubsubsection{\parsearg\subsubsectionzzz} + +% These macros control what the section commands do, according +% to what kind of chapter we are in (ordinary, appendix, or unnumbered). +% Define them by default for a numbered chapter. +\global\let\section = \numberedsec +\global\let\subsection = \numberedsubsec +\global\let\subsubsection = \numberedsubsubsec + +% Define @majorheading, @heading and @subheading + +% NOTE on use of \vbox for chapter headings, section headings, and +% such: +% 1) We use \vbox rather than the earlier \line to permit +% overlong headings to fold. +% 2) \hyphenpenalty is set to 10000 because hyphenation in a +% heading is obnoxious; this forbids it. +% 3) Likewise, headings look best if no \parindent is used, and +% if justification is not attempted. Hence \raggedright. + + +\def\majorheading{\parsearg\majorheadingzzz} +\def\majorheadingzzz #1{% +{\advance\chapheadingskip by 10pt \chapbreak }% +{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\penalty 200} + +\def\chapheading{\parsearg\chapheadingzzz} +\def\chapheadingzzz #1{\chapbreak % +{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\penalty 200} + +\def\heading{\parsearg\secheadingi} + +\def\subheading{\parsearg\subsecheadingi} + +\def\subsubheading{\parsearg\subsubsecheadingi} + +% These macros generate a chapter, section, etc. heading only +% (including whitespace, linebreaking, etc. around it), +% given all the information in convenient, parsed form. + +%%% Args are the skip and penalty (usually negative) +\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi} + +\def\setchapterstyle #1 {\csname CHAPF#1\endcsname} + +%%% Define plain chapter starts, and page on/off switching for it +% Parameter controlling skip before chapter headings (if needed) + +\newskip \chapheadingskip \chapheadingskip = 30pt plus 8pt minus 4pt + +\def\chapbreak{\dobreak \chapheadingskip {-4000}} +\def\chappager{\par\vfill\supereject} +\def\chapoddpage{\chappager \ifodd\pageno \else \hbox to 0pt{} \chappager\fi} + +\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname} + +\def\CHAPPAGoff{ +\global\let\pchapsepmacro=\chapbreak +\global\let\pagealignmacro=\chappager} + +\def\CHAPPAGon{ +\global\let\pchapsepmacro=\chappager +\global\let\pagealignmacro=\chappager +\global\def\HEADINGSon{\HEADINGSsingle}} + +\def\CHAPPAGodd{ +\global\let\pchapsepmacro=\chapoddpage +\global\let\pagealignmacro=\chapoddpage +\global\def\HEADINGSon{\HEADINGSdouble}} + +\CHAPPAGon + +\def\CHAPFplain{ +\global\let\chapmacro=\chfplain +\global\let\unnumbchapmacro=\unnchfplain} + +\def\chfplain #1#2{% + \pchapsepmacro + {% + \chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #2\enspace #1}% + }% + \bigskip + \penalty5000 +} + +\def\unnchfplain #1{% +\pchapsepmacro % +{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\penalty 10000 % +} +\CHAPFplain % The default + +\def\unnchfopen #1{% +\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\penalty 10000 % +} + +\def\chfopen #1#2{\chapoddpage {\chapfonts +\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}% +\par\penalty 5000 % +} + +\def\CHAPFopen{ +\global\let\chapmacro=\chfopen +\global\let\unnumbchapmacro=\unnchfopen} + +% Parameter controlling skip before section headings. + +\newskip \subsecheadingskip \subsecheadingskip = 17pt plus 8pt minus 4pt +\def\subsecheadingbreak{\dobreak \subsecheadingskip {-500}} + +\newskip \secheadingskip \secheadingskip = 21pt plus 8pt minus 4pt +\def\secheadingbreak{\dobreak \secheadingskip {-1000}} + +% @paragraphindent is defined for the Info formatting commands only. +\let\paragraphindent=\comment + +% Section fonts are the base font at magstep2, which produces +% a size a bit more than 14 points in the default situation. + +\def\secheading #1#2#3{\secheadingi {#2.#3\enspace #1}} +\def\plainsecheading #1{\secheadingi {#1}} +\def\secheadingi #1{{\advance \secheadingskip by \parskip % +\secheadingbreak}% +{\secfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}% +\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000 } + + +% Subsection fonts are the base font at magstep1, +% which produces a size of 12 points. + +\def\subsecheading #1#2#3#4{\subsecheadingi {#2.#3.#4\enspace #1}} +\def\subsecheadingi #1{{\advance \subsecheadingskip by \parskip % +\subsecheadingbreak}% +{\subsecfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}% +\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000 } + +\def\subsubsecfonts{\subsecfonts} % Maybe this should change: + % Perhaps make sssec fonts scaled + % magstep half +\def\subsubsecheading #1#2#3#4#5{\subsubsecheadingi {#2.#3.#4.#5\enspace #1}} +\def\subsubsecheadingi #1{{\advance \subsecheadingskip by \parskip % +\subsecheadingbreak}% +{\subsubsecfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}% +\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000} + + +\message{toc printing,} + +% Finish up the main text and prepare to read what we've written +% to \contentsfile. + +\newskip\contentsrightmargin \contentsrightmargin=1in +\def\startcontents#1{% + \pagealignmacro + \immediate\closeout \contentsfile + \ifnum \pageno>0 + \pageno = -1 % Request roman numbered pages. + \fi + % Don't need to put `Contents' or `Short Contents' in the headline. + % It is abundantly clear what they are. + \unnumbchapmacro{#1}\def\thischapter{}% + \begingroup % Set up to handle contents files properly. + \catcode`\\=0 \catcode`\{=1 \catcode`\}=2 \catcode`\@=11 + \raggedbottom % Worry more about breakpoints than the bottom. + \advance\hsize by -\contentsrightmargin % Don't use the full line length. +} + + +% Normal (long) toc. +\outer\def\contents{% + \startcontents{Table of Contents}% + \input \jobname.toc + \endgroup + \vfill \eject +} + +% And just the chapters. +\outer\def\summarycontents{% + \startcontents{Short Contents}% + % + \let\chapentry = \shortchapentry + \let\unnumbchapentry = \shortunnumberedentry + % We want a true roman here for the page numbers. + \secfonts + \let\rm=\shortcontrm \let\bf=\shortcontbf \let\sl=\shortcontsl + \rm + \advance\baselineskip by 1pt % Open it up a little. + \def\secentry ##1##2##3##4{} + \def\unnumbsecentry ##1##2{} + \def\subsecentry ##1##2##3##4##5{} + \def\unnumbsubsecentry ##1##2{} + \def\subsubsecentry ##1##2##3##4##5##6{} + \def\unnumbsubsubsecentry ##1##2{} + \input \jobname.toc + \endgroup + \vfill \eject +} +\let\shortcontents = \summarycontents + +% These macros generate individual entries in the table of contents. +% The first argument is the chapter or section name. +% The last argument is the page number. +% The arguments in between are the chapter number, section number, ... + +% Chapter-level things, for both the long and short contents. +\def\chapentry#1#2#3{\dochapentry{#2\labelspace#1}{#3}} + +% See comments in \dochapentry re vbox and related settings +\def\shortchapentry#1#2#3{% + \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno{#3}}% +} + +% Typeset the label for a chapter or appendix for the short contents. +% The arg is, e.g. `Appendix A' for an appendix, or `3' for a chapter. +% We could simplify the code here by writing out an \appendixentry +% command in the toc file for appendices, instead of using \chapentry +% for both, but it doesn't seem worth it. +\setbox0 = \hbox{\shortcontrm Appendix } +\newdimen\shortappendixwidth \shortappendixwidth = \wd0 + +\def\shortchaplabel#1{% + % We typeset #1 in a box of constant width, regardless of the text of + % #1, so the chapter titles will come out aligned. + \setbox0 = \hbox{#1}% + \dimen0 = \ifdim\wd0 > \shortappendixwidth \shortappendixwidth \else 0pt \fi + % + % This space should be plenty, since a single number is .5em, and the + % widest letter (M) is 1em, at least in the Computer Modern fonts. + % (This space doesn't include the extra space that gets added after + % the label; that gets put in in \shortchapentry above.) + \advance\dimen0 by 1.1em + \hbox to \dimen0{#1\hfil}% +} + +\def\unnumbchapentry#1#2{\dochapentry{#1}{#2}} +\def\shortunnumberedentry#1#2{\tocentry{#1}{\doshortpageno{#2}}} + +% Sections. +\def\secentry#1#2#3#4{\dosecentry{#2.#3\labelspace#1}{#4}} +\def\unnumbsecentry#1#2{\dosecentry{#1}{#2}} + +% Subsections. +\def\subsecentry#1#2#3#4#5{\dosubsecentry{#2.#3.#4\labelspace#1}{#5}} +\def\unnumbsubsecentry#1#2{\dosubsecentry{#1}{#2}} + +% And subsubsections. +\def\subsubsecentry#1#2#3#4#5#6{% + \dosubsubsecentry{#2.#3.#4.#5\labelspace#1}{#6}} +\def\unnumbsubsubsecentry#1#2{\dosubsubsecentry{#1}{#2}} + + +% This parameter controls the indentation of the various levels. +\newdimen\tocindent \tocindent = 3pc + +% Now for the actual typesetting. In all these, #1 is the text and #2 is the +% page number. +% +% If the toc has to be broken over pages, we would want to be at chapters +% if at all possible; hence the \penalty. +\def\dochapentry#1#2{% + \penalty-300 \vskip\baselineskip + \begingroup + \chapentryfonts + \tocentry{#1}{\dopageno{#2}}% + \endgroup + \nobreak\vskip .25\baselineskip +} + +\def\dosecentry#1#2{\begingroup + \secentryfonts \leftskip=\tocindent + \tocentry{#1}{\dopageno{#2}}% +\endgroup} + +\def\dosubsecentry#1#2{\begingroup + \subsecentryfonts \leftskip=2\tocindent + \tocentry{#1}{\dopageno{#2}}% +\endgroup} + +\def\dosubsubsecentry#1#2{\begingroup + \subsubsecentryfonts \leftskip=3\tocindent + \tocentry{#1}{\dopageno{#2}}% +\endgroup} + +% Final typesetting of a toc entry; we use the same \entry macro as for +% the index entries, but we want to suppress hyphenation here. (We +% can't do that in the \entry macro, since index entries might consist +% of hyphenated-identifiers-that-do-not-fit-on-a-line-and-nothing-else.) +% +\def\tocentry#1#2{\begingroup + \hyphenpenalty = 10000 + \entry{#1}{#2}% +\endgroup} + +% Space between chapter (or whatever) number and the title. +\def\labelspace{\hskip1em \relax} + +\def\dopageno#1{{\rm #1}} +\def\doshortpageno#1{{\rm #1}} + +\def\chapentryfonts{\secfonts \rm} +\def\secentryfonts{\textfonts} +\let\subsecentryfonts = \textfonts +\let\subsubsecentryfonts = \textfonts + + +\message{environments,} + +% Since these characters are used in examples, it should be an even number of +% \tt widths. Each \tt character is 1en, so two makes it 1em. +% Furthermore, these definitions must come after we define our fonts. +\newbox\dblarrowbox \newbox\longdblarrowbox +\newbox\pushcharbox \newbox\bullbox +\newbox\equivbox \newbox\errorbox + +\let\ptexequiv = \equiv + +%{\tentt +%\global\setbox\dblarrowbox = \hbox to 1em{\hfil$\Rightarrow$\hfil} +%\global\setbox\longdblarrowbox = \hbox to 1em{\hfil$\mapsto$\hfil} +%\global\setbox\pushcharbox = \hbox to 1em{\hfil$\dashv$\hfil} +%\global\setbox\equivbox = \hbox to 1em{\hfil$\ptexequiv$\hfil} +% Adapted from the manmac format (p.420 of TeXbook) +%\global\setbox\bullbox = \hbox to 1em{\kern.15em\vrule height .75ex width .85ex +% depth .1ex\hfil} +%} + +\def\point{$\star$} + +\def\result{\leavevmode\raise.15ex\hbox to 1em{\hfil$\Rightarrow$\hfil}} +\def\expansion{\leavevmode\raise.1ex\hbox to 1em{\hfil$\mapsto$\hfil}} +\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}} + +\def\equiv{\leavevmode\lower.1ex\hbox to 1em{\hfil$\ptexequiv$\hfil}} + +% Adapted from the TeXbook's \boxit. +{\tentt \global\dimen0 = 3em}% Width of the box. +\dimen2 = .55pt % Thickness of rules +% The text. (`r' is open on the right, `e' somewhat less so on the left.) +\setbox0 = \hbox{\kern-.75pt \tensf error\kern-1.5pt} + +\global\setbox\errorbox=\hbox to \dimen0{\hfil + \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right. + \advance\hsize by -2\dimen2 % Rules. + \vbox{ + \hrule height\dimen2 + \hbox{\vrule width\dimen2 \kern3pt % Space to left of text. + \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below. + \kern3pt\vrule width\dimen2}% Space to right. + \hrule height\dimen2} + \hfil} + +% The @error{} command. +\def\error{\leavevmode\lower.7ex\copy\errorbox} + +% @tex ... @end tex escapes into raw Tex temporarily. +% One exception: @ is still an escape character, so that @end tex works. +% But \@ or @@ will get a plain tex @ character. + +\def\tex{\begingroup +\catcode `\\=0 \catcode `\{=1 \catcode `\}=2 +\catcode `\$=3 \catcode `\&=4 \catcode `\#=6 +\catcode `\^=7 \catcode `\_=8 \catcode `\~=13 \let~=\tie +\catcode `\%=14 +\catcode 43=12 +\catcode`\"=12 +\catcode`\==12 +\catcode`\|=12 +\catcode`\<=12 +\catcode`\>=12 +\escapechar=`\\ +% +\let\{=\ptexlbrace +\let\}=\ptexrbrace +\let\.=\ptexdot +\let\*=\ptexstar +\let\dots=\ptexdots +\def\@{@}% +\let\bullet=\ptexbullet +\let\b=\ptexb \let\c=\ptexc \let\i=\ptexi \let\t=\ptext \let\l=\ptexl +\let\L=\ptexL +% +\let\Etex=\endgroup} + +% Define @lisp ... @endlisp. +% @lisp does a \begingroup so it can rebind things, +% including the definition of @endlisp (which normally is erroneous). + +% Amount to narrow the margins by for @lisp. +\newskip\lispnarrowing \lispnarrowing=0.4in + +% This is the definition that ^M gets inside @lisp +% phr: changed space to \null, to avoid overfull hbox problems. +{\obeyspaces% +\gdef\lisppar{\null\endgraf}} + +% Make each space character in the input produce a normal interword +% space in the output. Don't allow a line break at this space, as this +% is used only in environments like @example, where each line of input +% should produce a line of output anyway. +% +{\obeyspaces % +\gdef\sepspaces{\obeyspaces\let =\tie}} + +% Define \obeyedspace to be our active space, whatever it is. This is +% for use in \parsearg. +{\sepspaces % +\global\let\obeyedspace= } + +% This space is always present above and below environments. +\newskip\envskipamount \envskipamount = 0pt + +% Make spacing and below environment symmetrical. +\def\aboveenvbreak{{\advance\envskipamount by \parskip +\endgraf \ifdim\lastskip<\envskipamount +\removelastskip \penalty-50 \vskip\envskipamount \fi}} + +\let\afterenvbreak = \aboveenvbreak + +% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins. +\let\nonarrowing=\relax + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \cartouche: draw rectangle w/rounded corners around argument +\font\circle=lcircle10 +\newdimen\circthick +\newdimen\cartouter\newdimen\cartinner +\newskip\normbskip\newskip\normpskip\newskip\normlskip +\circthick=\fontdimen8\circle +% +\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth +\def\ctr{{\hskip 6pt\circle\char'010}} +\def\cbl{{\circle\char'012\hskip -6pt}} +\def\cbr{{\hskip 6pt\circle\char'011}} +\def\carttop{\hbox to \cartouter{\hskip\lskip + \ctl\leaders\hrule height\circthick\hfil\ctr + \hskip\rskip}} +\def\cartbot{\hbox to \cartouter{\hskip\lskip + \cbl\leaders\hrule height\circthick\hfil\cbr + \hskip\rskip}} +% +\newskip\lskip\newskip\rskip + +\long\def\cartouche{% +\begingroup + \lskip=\leftskip \rskip=\rightskip + \leftskip=0pt\rightskip=0pt %we want these *outside*. + \cartinner=\hsize \advance\cartinner by-\lskip + \advance\cartinner by-\rskip + \cartouter=\hsize + \advance\cartouter by 18pt % allow for 3pt kerns on either +% side, and for 6pt waste from +% each corner char + \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip + % Flag to tell @lisp, etc., not to narrow margin. + \let\nonarrowing=\comment + \vbox\bgroup + \baselineskip=0pt\parskip=0pt\lineskip=0pt + \carttop + \hbox\bgroup + \hskip\lskip + \vrule\kern3pt + \vbox\bgroup + \hsize=\cartinner + \kern3pt + \begingroup + \baselineskip=\normbskip + \lineskip=\normlskip + \parskip=\normpskip + \vskip -\parskip +\def\Ecartouche{% + \endgroup + \kern3pt + \egroup + \kern3pt\vrule + \hskip\rskip + \egroup + \cartbot + \egroup +\endgroup +}} + + +% This macro is called at the beginning of all the @example variants, +% inside a group. +\def\nonfillstart{% + \aboveenvbreak + \inENV % This group ends at the end of the body + \hfuzz = 12pt % Don't be fussy + \sepspaces % Make spaces be word-separators rather than space tokens. + \singlespace % single space lines + \let\par = \lisppar % don't ignore blank lines + \obeylines % each line of input is a line of output + \parskip = 0pt + \parindent = 0pt + \emergencystretch = 0pt % don't try to avoid overfull boxes + % @cartouche defines \nonarrowing to inhibit narrowing + % at next level down. + \ifx\nonarrowing\relax + \advance \leftskip by \lispnarrowing + \exdentamount=\lispnarrowing + \let\exdent=\nofillexdent + \let\nonarrowing=\relax + \fi +} + +\def\Elisp{\endgroup\afterenvbreak}% + +\def\lisp{\begingroup + \nonfillstart + \def\Elisp{\endgroup\afterenvbreak}% + \tt + \rawbackslash % output the \ character from the current font + \gobble +} + +% Define the \E... control sequence only if we are inside the +% environment, so the error checking in \end will work. +% +% We must call \lisp last in the definition, since it reads the +% return following the @example (or whatever) command. +% +\def\example{\begingroup \def\Eexample{\Elisp\endgroup}\lisp} +\def\smallexample{\begingroup \def\Esmallexample{\Elisp\endgroup}\lisp} + +% Macro for 9 pt. examples, necessary to print with 5" lines. From +% Pavel@xerox. This is not used for @smallexamples unless the +% @smallbook command is given. +% +\def\smalllispx{\begingroup + \nonfillstart + \def\Esmalllisp{\endgroup\afterenvbreak}% + % + % Smaller interline space and fonts for small examples. + \baselineskip 10pt + \indexfonts \tt + \rawbackslash % output the \ character from the current font + \gobble +} + +% This is @display; same as @lisp except use roman font. +% +\def\display{\begingroup + \nonfillstart + \def\Edisplay{\endgroup\afterenvbreak}% + \gobble +} + +% This is @format; same as @display except don't narrow margins. +% +\def\format{\begingroup + \let\nonarrowing = t + \nonfillstart + \def\Eformat{\endgroup\afterenvbreak} + \gobble +} + +% @flushleft (same as @format) and @flushright. +% +\def\flushleft{\begingroup + \let\nonarrowing = t + \nonfillstart + \def\Eflushleft{\endgroup\afterenvbreak}% + \gobble +} +\def\flushright{\begingroup + \let\nonarrowing = t + \nonfillstart + \def\Eflushright{\endgroup\afterenvbreak}% + \advance\leftskip by 0pt plus 1fill + \gobble} + +% @quotation does normal linebreaking and narrows the margins. +% +\def\quotation{% +\begingroup\inENV %This group ends at the end of the @quotation body +{\parskip=0pt % because we will skip by \parskip too, later +\aboveenvbreak}% +\singlespace +\parindent=0pt +\def\Equotation{\par\endgroup\afterenvbreak}% +% @cartouche defines \nonarrowing to inhibit narrowing +% at next level down. +\ifx\nonarrowing\relax +\advance \leftskip by \lispnarrowing +\advance \rightskip by \lispnarrowing +\exdentamount=\lispnarrowing +\let\nonarrowing=\relax +\fi} + +\message{defuns,} +% Define formatter for defuns +% First, allow user to change definition object font (\df) internally +\def\setdeffont #1 {\csname DEF#1\endcsname} + +\newskip\defbodyindent \defbodyindent=.4in +\newskip\defargsindent \defargsindent=50pt +\newskip\deftypemargin \deftypemargin=12pt +\newskip\deflastargmargin \deflastargmargin=18pt + +\newcount\parencount +% define \functionparens, which makes ( and ) and & do special things. +% \functionparens affects the group it is contained in. +\def\activeparens{% +\catcode`\(=\active \catcode`\)=\active \catcode`\&=\active +\catcode`\[=\active \catcode`\]=\active} + +% Make control sequences which act like normal parenthesis chars. +\let\lparen = ( \let\rparen = ) + +{\activeparens % Now, smart parens don't turn on until &foo (see \amprm) + +% Be sure that we always have a definition for `(', etc. For example, +% if the fn name has parens in it, \boldbrax will not be in effect yet, +% so TeX would otherwise complain about undefined control sequence. +\global\let(=\lparen \global\let)=\rparen +\global\let[=\lbrack \global\let]=\rbrack + +\gdef\functionparens{\boldbrax\let&=\amprm\parencount=0 } +\gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} + +% Definitions of (, ) and & used in args for functions. +% This is the definition of ( outside of all parentheses. +\gdef\oprm#1 {{\rm\char`\(}#1 \bf \let(=\opnested % +\global\advance\parencount by 1 } +% +% This is the definition of ( when already inside a level of parens. +\gdef\opnested{\char`\(\global\advance\parencount by 1 } +% +\gdef\clrm{% Print a paren in roman if it is taking us back to depth of 0. +% also in that case restore the outer-level definition of (. +\ifnum \parencount=1 {\rm \char `\)}\sl \let(=\oprm \else \char `\) \fi +\global\advance \parencount by -1 } +% If we encounter &foo, then turn on ()-hacking afterwards +\gdef\amprm#1 {{\rm\}\let(=\oprm \let)=\clrm\ } +% +\gdef\normalparens{\boldbrax\let&=\ampnr} +} % End of definition inside \activeparens +%% These parens (in \boldbrax) actually are a little bolder than the +%% contained text. This is especially needed for [ and ] +\def\opnr{{\sf\char`\(}} \def\clnr{{\sf\char`\)}} \def\ampnr{\&} +\def\lbrb{{\bf\char`\[}} \def\rbrb{{\bf\char`\]}} + +% First, defname, which formats the header line itself. +% #1 should be the function name. +% #2 should be the type of definition, such as "Function". + +\def\defname #1#2{% +% Get the values of \leftskip and \rightskip as they were +% outside the @def... +\dimen2=\leftskip +\advance\dimen2 by -\defbodyindent +\dimen3=\rightskip +\advance\dimen3 by -\defbodyindent +\noindent % +\setbox0=\hbox{\hskip \deflastargmargin{\rm #2}\hskip \deftypemargin}% +\dimen0=\hsize \advance \dimen0 by -\wd0 % compute size for first line +\dimen1=\hsize \advance \dimen1 by -\defargsindent %size for continuations +\parshape 2 0in \dimen0 \defargsindent \dimen1 % +% Now output arg 2 ("Function" or some such) +% ending at \deftypemargin from the right margin, +% but stuck inside a box of width 0 so it does not interfere with linebreaking +{% Adjust \hsize to exclude the ambient margins, +% so that \rightline will obey them. +\advance \hsize by -\dimen2 \advance \hsize by -\dimen3 +\rlap{\rightline{{\rm #2}\hskip \deftypemargin}}}% +% Make all lines underfull and no complaints: +\tolerance=10000 \hbadness=10000 +\advance\leftskip by -\defbodyindent +\exdentamount=\defbodyindent +{\df #1}\enskip % Generate function name +} + +% Actually process the body of a definition +% #1 should be the terminating control sequence, such as \Edefun. +% #2 should be the "another name" control sequence, such as \defunx. +% #3 should be the control sequence that actually processes the header, +% such as \defunheader. + +\def\defparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2{\begingroup\obeylines\activeparens\spacesplit#3}% +\parindent=0in +\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup % +\catcode 61=\active % +\obeylines\activeparens\spacesplit#3} + +\def\defmethparsebody #1#2#3#4 {\begingroup\inENV % +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2##1 {\begingroup\obeylines\activeparens\spacesplit{#3{##1}}}% +\parindent=0in +\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup\obeylines\activeparens\spacesplit{#3{#4}}} + +\def\defopparsebody #1#2#3#4#5 {\begingroup\inENV % +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2##1 ##2 {\def#4{##1}% +\begingroup\obeylines\activeparens\spacesplit{#3{##2}}}% +\parindent=0in +\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup\obeylines\activeparens\spacesplit{#3{#5}}} + +% These parsing functions are similar to the preceding ones +% except that they do not make parens into active characters. +% These are used for "variables" since they have no arguments. + +\def\defvarparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2{\begingroup\obeylines\spacesplit#3}% +\parindent=0in +\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup % +\catcode 61=\active % +\obeylines\spacesplit#3} + +\def\defvrparsebody #1#2#3#4 {\begingroup\inENV % +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2##1 {\begingroup\obeylines\spacesplit{#3{##1}}}% +\parindent=0in +\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup\obeylines\spacesplit{#3{#4}}} + +% This seems to work right in all cases. +\let\deftpparsebody=\defvrparsebody +% This fails to work. When given `@deftp {Data Type} foo_t', +% it thinks the type name is just `f'. +%%% This is the same as all the others except for the last line. We need +%%% to parse the arguments differently for @deftp, since the ``attributes'' +%%% there are optional. +%%% +%%\def\deftpparsebody #1#2#3#4 {\begingroup\inENV % +%%\medbreak % +%%% Define the end token that this defining construct specifies +%%% so that it will exit this group. +%%\def#1{\endgraf\endgroup\medbreak}% +%%\def#2##1 {\begingroup\obeylines\spacesplit{#3{##1}}}% +%%\parindent=0in +%%\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent +%%\exdentamount=\defbodyindent +%%\begingroup\obeylines\parsetpheaderline{#3{#4}}} + +%%{\obeylines % +%% % Parse the type name and any attributes (field names, etc.). +%% % #1 is the beginning of the macro call that will produce the output, +%% % i.e., \deftpheader{CLASS}; this is passed from \deftpparsebody. +%% % #2 is the type name, e.g., `struct termios'. +%% % #3 is the (possibly empty) attribute list. +%% % +%% \gdef\parsetpheaderline#1#2#3^^M{% +%% \endgroup % Started in \deftpparsebody. +%% % +%% % If the attribute list is in fact empty, there will be no space after +%% % #2; so we can't put a space in our TeX parameter list. But if it +%% % isn't empty, then #3 will begin with an unwanted space. +%% \def\theargs{\ignorespaces #3}% +%% % +%% % Call the macro to produce the output. +%% #1{#2}\theargs % +%% }% +%%} + +\def\defopvarparsebody #1#2#3#4#5 {\begingroup\inENV % +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2##1 ##2 {\def#4{##1}% +\begingroup\obeylines\spacesplit{#3{##2}}}% +\parindent=0in +\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup\obeylines\spacesplit{#3{#5}}} + +% Split up #2 at the first space token. +% call #1 with two arguments: +% the first is all of #2 before the space token, +% the second is all of #2 after that space token. +% If #2 contains no space token, all of it is passed as the first arg +% and the second is passed as empty. + +{\obeylines +\gdef\spacesplit#1#2^^M{\endgroup\spacesplitfoo{#1}#2 \relax\spacesplitfoo}% +\long\gdef\spacesplitfoo#1#2 #3#4\spacesplitfoo{% +\ifx\relax #3% +#1{#2}{}\else #1{#2}{#3#4}\fi}} + +% So much for the things common to all kinds of definitions. + +% Define @defun. + +% First, define the processing that is wanted for arguments of \defun +% Use this to expand the args and terminate the paragraph they make up + +\def\defunargs #1{\functionparens \sl +% Expand, preventing hyphenation at `-' chars. +% Note that groups don't affect changes in \hyphenchar. +\hyphenchar\tensl=0 +#1% +\hyphenchar\tensl=45 +\ifnum\parencount=0 \else \errmessage{unbalanced parens in @def arguments}\fi% +\interlinepenalty=10000 +\advance\rightskip by 0pt plus 1fil +\endgraf\penalty 10000\vskip -\parskip\penalty 10000% +} + +\def\deftypefunargs #1{% +% Expand, preventing hyphenation at `-' chars. +% Note that groups don't affect changes in \hyphenchar. +\functionparens +\code{#1}% +\interlinepenalty=10000 +\advance\rightskip by 0pt plus 1fil +\endgraf\penalty 10000\vskip -\parskip\penalty 10000% +} + +% Do complete processing of one @defun or @defunx line already parsed. + +% @deffn Command forward-char nchars + +\def\deffn{\defmethparsebody\Edeffn\deffnx\deffnheader} + +\def\deffnheader #1#2#3{\doind {fn}{\code{#2}}% +\begingroup\defname {#2}{#1}\defunargs{#3}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @defun == @deffn Function + +\def\defun{\defparsebody\Edefun\defunx\defunheader} + +\def\defunheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index +\begingroup\defname {#1}{Function}% +\defunargs {#2}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @deftypefun int foobar (int @var{foo}, float @var{bar}) + +\def\deftypefun{\defparsebody\Edeftypefun\deftypefunx\deftypefunheader} + +% #1 is the data type. #2 is the name and args. +\def\deftypefunheader #1#2{\deftypefunheaderx{#1}#2 \relax} +% #1 is the data type, #2 the name, #3 the args. +\def\deftypefunheaderx #1#2 #3\relax{% +\doind {fn}{\code{#2}}% Make entry in function index +\begingroup\defname {\code{#1} #2}{Function}% +\deftypefunargs {#3}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @deftypefn {Library Function} int foobar (int @var{foo}, float @var{bar}) + +\def\deftypefn{\defmethparsebody\Edeftypefn\deftypefnx\deftypefnheader} + +% #1 is the classification. #2 is the data type. #3 is the name and args. +\def\deftypefnheader #1#2#3{\deftypefnheaderx{#1}{#2}#3 \relax} +% #1 is the classification, #2 the data type, #3 the name, #4 the args. +\def\deftypefnheaderx #1#2#3 #4\relax{% +\doind {fn}{\code{#3}}% Make entry in function index +\begingroup\defname {\code{#2} #3}{#1}% +\deftypefunargs {#4}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @defmac == @deffn Macro + +\def\defmac{\defparsebody\Edefmac\defmacx\defmacheader} + +\def\defmacheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index +\begingroup\defname {#1}{Macro}% +\defunargs {#2}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @defspec == @deffn Special Form + +\def\defspec{\defparsebody\Edefspec\defspecx\defspecheader} + +\def\defspecheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index +\begingroup\defname {#1}{Special Form}% +\defunargs {#2}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% This definition is run if you use @defunx +% anywhere other than immediately after a @defun or @defunx. + +\def\deffnx #1 {\errmessage{@deffnx in invalid context}} +\def\defunx #1 {\errmessage{@defunx in invalid context}} +\def\defmacx #1 {\errmessage{@defmacx in invalid context}} +\def\defspecx #1 {\errmessage{@defspecx in invalid context}} +\def\deftypefnx #1 {\errmessage{@deftypefnx in invalid context}} +\def\deftypeunx #1 {\errmessage{@deftypeunx in invalid context}} + +% @defmethod, and so on + +% @defop {Funny Method} foo-class frobnicate argument + +\def\defop #1 {\def\defoptype{#1}% +\defopparsebody\Edefop\defopx\defopheader\defoptype} + +\def\defopheader #1#2#3{% +\dosubind {fn}{\code{#2}}{on #1}% Make entry in function index +\begingroup\defname {#2}{\defoptype{} on #1}% +\defunargs {#3}\endgroup % +} + +% @defmethod == @defop Method + +\def\defmethod{\defmethparsebody\Edefmethod\defmethodx\defmethodheader} + +\def\defmethodheader #1#2#3{% +\dosubind {fn}{\code{#2}}{on #1}% entry in function index +\begingroup\defname {#2}{Method on #1}% +\defunargs {#3}\endgroup % +} + +% @defcv {Class Option} foo-class foo-flag + +\def\defcv #1 {\def\defcvtype{#1}% +\defopvarparsebody\Edefcv\defcvx\defcvarheader\defcvtype} + +\def\defcvarheader #1#2#3{% +\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index +\begingroup\defname {#2}{\defcvtype{} of #1}% +\defvarargs {#3}\endgroup % +} + +% @defivar == @defcv {Instance Variable} + +\def\defivar{\defvrparsebody\Edefivar\defivarx\defivarheader} + +\def\defivarheader #1#2#3{% +\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index +\begingroup\defname {#2}{Instance Variable of #1}% +\defvarargs {#3}\endgroup % +} + +% These definitions are run if you use @defmethodx, etc., +% anywhere other than immediately after a @defmethod, etc. + +\def\defopx #1 {\errmessage{@defopx in invalid context}} +\def\defmethodx #1 {\errmessage{@defmethodx in invalid context}} +\def\defcvx #1 {\errmessage{@defcvx in invalid context}} +\def\defivarx #1 {\errmessage{@defivarx in invalid context}} + +% Now @defvar + +% First, define the processing that is wanted for arguments of @defvar. +% This is actually simple: just print them in roman. +% This must expand the args and terminate the paragraph they make up +\def\defvarargs #1{\normalparens #1% +\interlinepenalty=10000 +\endgraf\penalty 10000\vskip -\parskip\penalty 10000} + +% @defvr Counter foo-count + +\def\defvr{\defvrparsebody\Edefvr\defvrx\defvrheader} + +\def\defvrheader #1#2#3{\doind {vr}{\code{#2}}% +\begingroup\defname {#2}{#1}\defvarargs{#3}\endgroup} + +% @defvar == @defvr Variable + +\def\defvar{\defvarparsebody\Edefvar\defvarx\defvarheader} + +\def\defvarheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index +\begingroup\defname {#1}{Variable}% +\defvarargs {#2}\endgroup % +} + +% @defopt == @defvr {User Option} + +\def\defopt{\defvarparsebody\Edefopt\defoptx\defoptheader} + +\def\defoptheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index +\begingroup\defname {#1}{User Option}% +\defvarargs {#2}\endgroup % +} + +% @deftypevar int foobar + +\def\deftypevar{\defvarparsebody\Edeftypevar\deftypevarx\deftypevarheader} + +% #1 is the data type. #2 is the name. +\def\deftypevarheader #1#2{% +\doind {vr}{\code{#2}}% Make entry in variables index +\begingroup\defname {\code{#1} #2}{Variable}% +\interlinepenalty=10000 +\endgraf\penalty 10000\vskip -\parskip\penalty 10000 +\endgroup} + +% @deftypevr {Global Flag} int enable + +\def\deftypevr{\defvrparsebody\Edeftypevr\deftypevrx\deftypevrheader} + +\def\deftypevrheader #1#2#3{\doind {vr}{\code{#3}}% +\begingroup\defname {\code{#2} #3}{#1} +\interlinepenalty=10000 +\endgraf\penalty 10000\vskip -\parskip\penalty 10000 +\endgroup} + +% This definition is run if you use @defvarx +% anywhere other than immediately after a @defvar or @defvarx. + +\def\defvrx #1 {\errmessage{@defvrx in invalid context}} +\def\defvarx #1 {\errmessage{@defvarx in invalid context}} +\def\defoptx #1 {\errmessage{@defoptx in invalid context}} +\def\deftypevarx #1 {\errmessage{@deftypevarx in invalid context}} +\def\deftypevrx #1 {\errmessage{@deftypevrx in invalid context}} + +% Now define @deftp +% Args are printed in bold, a slight difference from @defvar. + +\def\deftpargs #1{\bf \defvarargs{#1}} + +% @deftp Class window height width ... + +\def\deftp{\deftpparsebody\Edeftp\deftpx\deftpheader} + +\def\deftpheader #1#2#3{\doind {tp}{\code{#2}}% +\begingroup\defname {#2}{#1}\deftpargs{#3}\endgroup} + +% This definition is run if you use @deftpx, etc +% anywhere other than immediately after a @deftp, etc. + +\def\deftpx #1 {\errmessage{@deftpx in invalid context}} + +\message{cross reference,} +% Define cross-reference macros +\newwrite \auxfile + +\newif\ifhavexrefs % True if xref values are known. +\newif\ifwarnedxrefs % True if we warned once that they aren't known. + +% \setref{foo} defines a cross-reference point named foo. + +\def\setref#1{% +%\dosetq{#1-title}{Ytitle}% +\dosetq{#1-pg}{Ypagenumber}% +\dosetq{#1-snt}{Ysectionnumberandtype}} + +\def\unnumbsetref#1{% +%\dosetq{#1-title}{Ytitle}% +\dosetq{#1-pg}{Ypagenumber}% +\dosetq{#1-snt}{Ynothing}} + +\def\appendixsetref#1{% +%\dosetq{#1-title}{Ytitle}% +\dosetq{#1-pg}{Ypagenumber}% +\dosetq{#1-snt}{Yappendixletterandtype}} + +% \xref, \pxref, and \ref generate cross-references to specified points. +% For \xrefX, #1 is the node name, #2 the name of the Info +% cross-reference, #3 the printed node name, #4 the name of the Info +% file, #5 the name of the printed manual. All but the node name can be +% omitted. +% +\def\pxref#1{see \xrefX[#1,,,,,,,]} +\def\xref#1{See \xrefX[#1,,,,,,,]} +\def\ref#1{\xrefX[#1,,,,,,,]} +\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup% +\def\printedmanual{\ignorespaces #5}% +\def\printednodename{\ignorespaces #3}% +% +\setbox1=\hbox{\printedmanual}% +\setbox0=\hbox{\printednodename}% +\ifdim \wd0=0pt% +\def\printednodename{\ignorespaces #1}% +%%% Uncommment the following line to make the actual chapter or section title +%%% appear inside the square brackets. +%\def\printednodename{#1-title}% +\fi% +% +% +% If we use \unhbox0 and \unhbox1 to print the node names, TeX does +% not insert empty discretionaries after hyphens, which means that it +% will not find a line break at a hyphen in a node names. Since some +% manuals are best written with fairly long node names, containing +% hyphens, this is a loss. Therefore, we simply give the text of +% the node name again, so it is as if TeX is seeing it for the first +% time. +\ifdim \wd1>0pt +section ``\printednodename'' in \cite{\printedmanual}% +\else% +\turnoffactive% +\refx{#1-snt}{} [\printednodename], page\tie\refx{#1-pg}{}% +\fi +\endgroup} + +% \dosetq is the interface for calls from other macros + +% Use \turnoffactive so that punctuation chars such as underscore +% work in node names. +\def\dosetq #1#2{{\let\folio=0 \turnoffactive% +\edef\next{\write\auxfile{\internalsetq {#1}{#2}}}% +\next}} + +% \internalsetq {foo}{page} expands into +% CHARACTERS 'xrdef {foo}{...expansion of \Ypage...} +% When the aux file is read, ' is the escape character + +\def\internalsetq #1#2{'xrdef {#1}{\csname #2\endcsname}} + +% Things to be expanded by \internalsetq + +\def\Ypagenumber{\folio} + +\def\Ytitle{\thischapter} + +\def\Ynothing{} + +\def\Ysectionnumberandtype{% +\ifnum\secno=0 Chapter\xreftie\the\chapno % +\else \ifnum \subsecno=0 Section\xreftie\the\chapno.\the\secno % +\else \ifnum \subsubsecno=0 % +Section\xreftie\the\chapno.\the\secno.\the\subsecno % +\else % +Section\xreftie\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno % +\fi \fi \fi } + +\def\Yappendixletterandtype{% +\ifnum\secno=0 Appendix\xreftie'char\the\appendixno{}% +\else \ifnum \subsecno=0 Section\xreftie'char\the\appendixno.\the\secno % +\else \ifnum \subsubsecno=0 % +Section\xreftie'char\the\appendixno.\the\secno.\the\subsecno % +\else % +Section\xreftie'char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno % +\fi \fi \fi } + +\gdef\xreftie{'tie} + +% Use TeX 3.0's \inputlineno to get the line number, for better error +% messages, but if we're using an old version of TeX, don't do anything. +% +\ifx\inputlineno\thisisundefined + \let\linenumber = \empty % Non-3.0. +\else + \def\linenumber{\the\inputlineno:\space} +\fi + +% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME. +% If its value is nonempty, SUFFIX is output afterward. + +\def\refx#1#2{% + \expandafter\ifx\csname X#1\endcsname\relax + % If not defined, say something at least. + $\langle$un\-de\-fined$\rangle$% + \ifhavexrefs + \message{\linenumber Undefined cross reference `#1'.}% + \else + \ifwarnedxrefs\else + \global\warnedxrefstrue + \message{Cross reference values unknown; you must run TeX again.}% + \fi + \fi + \else + % It's defined, so just use it. + \csname X#1\endcsname + \fi + #2% Output the suffix in any case. +} + +% Read the last existing aux file, if any. No error if none exists. + +% This is the macro invoked by entries in the aux file. +\def\xrdef #1#2{ +{\catcode`\'=\other\expandafter \gdef \csname X#1\endcsname {#2}}} + +\def\readauxfile{% +\begingroup +\catcode `\^^@=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\^^C=\other +\catcode `\^^D=\other +\catcode `\^^E=\other +\catcode `\^^F=\other +\catcode `\^^G=\other +\catcode `\^^H=\other +\catcode `\ =\other +\catcode `\^^L=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode 26=\other +\catcode `\^^[=\other +\catcode `\^^\=\other +\catcode `\^^]=\other +\catcode `\^^^=\other +\catcode `\^^_=\other +\catcode `\@=\other +\catcode `\^=\other +\catcode `\~=\other +\catcode `\[=\other +\catcode `\]=\other +\catcode`\"=\other +\catcode`\_=\other +\catcode`\|=\other +\catcode`\<=\other +\catcode`\>=\other +\catcode `\$=\other +\catcode `\#=\other +\catcode `\&=\other +% `\+ does not work, so use 43. +\catcode 43=\other +% the aux file uses ' as the escape. +% Turn off \ as an escape so we do not lose on +% entries which were dumped with control sequences in their names. +% For example, 'xrdef {$\leq $-fun}{page ...} made by @defun ^^ +% Reference to such entries still does not work the way one would wish, +% but at least they do not bomb out when the aux file is read in. +\catcode `\{=1 \catcode `\}=2 +\catcode `\%=\other +\catcode `\'=0 +\catcode `\\=\other +\openin 1 \jobname.aux +\ifeof 1 \else \closein 1 \input \jobname.aux \global\havexrefstrue +\global\warnedobstrue +\fi +% Open the new aux file. Tex will close it automatically at exit. +\openout \auxfile=\jobname.aux +\endgroup} + + +% Footnotes. + +\newcount \footnoteno + +% The trailing space in the following definition for supereject is +% vital for proper filling; pages come out unaligned when you do a +% pagealignmacro call if that space before the closing brace is +% removed. +\def\supereject{\par\penalty -20000\footnoteno =0 } + +% @footnotestyle is meaningful for info output only.. +\let\footnotestyle=\comment + +\let\ptexfootnote=\footnote + +{\catcode `\@=11 +% +% Auto-number footnotes. Otherwise like plain. +\gdef\footnote{% + \global\advance\footnoteno by \@ne + \edef\thisfootno{$^{\the\footnoteno}$}% + % + % In case the footnote comes at the end of a sentence, preserve the + % extra spacing after we do the footnote number. + \let\@sf\empty + \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi + % + % Remove inadvertent blank space before typesetting the footnote number. + \unskip + \thisfootno\@sf + \footnotezzz +}% + +% Don't bother with the trickery in plain.tex to not require the +% footnote text as a parameter. Our footnotes don't need to be so general. +% +\long\gdef\footnotezzz#1{\insert\footins{% + % We want to typeset this text as a normal paragraph, even if the + % footnote reference occurs in (for example) a display environment. + % So reset some parameters. + \interlinepenalty\interfootnotelinepenalty + \splittopskip\ht\strutbox % top baseline for broken footnotes + \splitmaxdepth\dp\strutbox + \floatingpenalty\@MM + \leftskip\z@skip + \rightskip\z@skip + \spaceskip\z@skip + \xspaceskip\z@skip + \parindent\defaultparindent + % + % Hang the footnote text off the number. + \hang + \textindent{\thisfootno}% + % + % Don't crash into the line above the footnote text. Since this + % expands into a box, it must come within the paragraph, lest it + % provide a place where TeX can split the footnote. + \footstrut + #1\strut}% +} + +}%end \catcode `\@=11 + +% Set the baselineskip to #1, and the lineskip and strut size +% correspondingly. There is no deep meaning behind these magic numbers +% used as factors; they just match (closely enough) what Knuth defined. +% +\def\lineskipfactor{.1} +\def\strutheightpercent{.71} +\def\strutdepthpercent{.29} +% +\def\setleading#1{% + \baselineskip = #1\relax + \normalbaselineskip = \baselineskip + \lineskip = \lineskipfactor\baselineskip + \setbox\strutbox =\hbox{% + \vrule width0pt height\strutheightpercent\baselineskip + depth \strutdepthpercent \baselineskip + }% +} + +% @| inserts a changebar to the left of the current line. It should +% surround any changed text. This approach does *not* work if the +% change spans more than two lines of output. To handle that, we would +% have adopt a much more difficult approach (putting marks into the main +% vertical list for the beginning and end of each change). +% +\def\|{% + % \vadjust can only be used in horizontal mode. + \leavevmode + % + % Append this vertical mode material after the current line in the output. + \vadjust{% + % We want to insert a rule with the height and depth of the current + % leading; that is exactly what \strutbox is supposed to record. + \vskip-\baselineskip + % + % \vadjust-items are inserted at the left edge of the type. So + % the \llap here moves out into the left-hand margin. + \llap{% + % + % For a thicker or thinner bar, change the `1pt'. + \vrule height\baselineskip width1pt + % + % This is the space between the bar and the text. + \hskip 12pt + }% + }% +} + +% For a final copy, take out the rectangles +% that mark overfull boxes (in case you have decided +% that the text looks ok even though it passes the margin). +% +\def\finalout{\overfullrule=0pt} + + +% End of control word definitions. + +\message{and turning on texinfo input format.} + +\def\openindices{% + \newindex{cp}% + \newcodeindex{fn}% + \newcodeindex{vr}% + \newcodeindex{tp}% + \newcodeindex{ky}% + \newcodeindex{pg}% +} + +% Set some numeric style parameters, for 8.5 x 11 format. + +%\hsize = 6.5in +\newdimen\defaultparindent \defaultparindent = 15pt +\parindent = \defaultparindent +\parskip 18pt plus 1pt +\setleading{15pt} +\advance\topskip by 1.2cm + +% Prevent underfull vbox error messages. +\vbadness=10000 + +% Following George Bush, just get rid of widows and orphans. +\widowpenalty=10000 +\clubpenalty=10000 + +% Use TeX 3.0's \emergencystretch to help line breaking, but if we're +% using an old version of TeX, don't do anything. We want the amount of +% stretch added to depend on the line length, hence the dependence on +% \hsize. This makes it come to about 9pt for the 8.5x11 format. +% +\ifx\emergencystretch\thisisundefined + % Allow us to assign to \emergencystretch anyway. + \def\emergencystretch{\dimen0}% +\else + \emergencystretch = \hsize + \divide\emergencystretch by 45 +\fi + +% Use @smallbook to reset parameters for 7x9.5 format (or else 7x9.25) +\def\smallbook{ + +% These values for secheadingskip and subsecheadingskip are +% experiments. RJC 7 Aug 1992 +\global\secheadingskip = 17pt plus 6pt minus 3pt +\global\subsecheadingskip = 14pt plus 6pt minus 3pt + +\global\lispnarrowing = 0.3in +\setleading{12pt} +\advance\topskip by -1cm +\global\parskip 3pt plus 1pt +\global\hsize = 5in +\global\vsize=7.5in +\global\tolerance=700 +\global\hfuzz=1pt +\global\contentsrightmargin=0pt + +\global\pagewidth=\hsize +\global\pageheight=\vsize + +\global\let\smalllisp=\smalllispx +\global\let\smallexample=\smalllispx +\global\def\Esmallexample{\Esmalllisp} +} + +% Use @afourpaper to print on European A4 paper. +\def\afourpaper{ +\global\tolerance=700 +\global\hfuzz=1pt +\setleading{12pt} +\global\parskip 15pt plus 1pt + +\global\vsize= 53\baselineskip +\advance\vsize by \topskip +%\global\hsize= 5.85in % A4 wide 10pt +\global\hsize= 6.5in +\global\outerhsize=\hsize +\global\advance\outerhsize by 0.5in +\global\outervsize=\vsize +\global\advance\outervsize by 0.6in + +\global\pagewidth=\hsize +\global\pageheight=\vsize +} + +% Define macros to output various characters with catcode for normal text. +\catcode`\"=\other +\catcode`\~=\other +\catcode`\^=\other +\catcode`\_=\other +\catcode`\|=\other +\catcode`\<=\other +\catcode`\>=\other +\catcode`\+=\other +\def\normaldoublequote{"} +\def\normaltilde{~} +\def\normalcaret{^} +\def\normalunderscore{_} +\def\normalverticalbar{|} +\def\normalless{<} +\def\normalgreater{>} +\def\normalplus{+} + +% This macro is used to make a character print one way in ttfont +% where it can probably just be output, and another way in other fonts, +% where something hairier probably needs to be done. +% +% #1 is what to print if we are indeed using \tt; #2 is what to print +% otherwise. Since all the Computer Modern typewriter fonts have zero +% interword stretch (and shrink), and it is reasonable to expect all +% typewriter fonts to have this, we can check that font parameter. +% +\def\ifusingtt#1#2{\ifdim \fontdimen3\the\font=0pt #1\else #2\fi} + +% Turn off all special characters except @ +% (and those which the user can use as if they were ordinary). +% Most of these we simply print from the \tt font, but for some, we can +% use math or other variants that look better in normal text. + +\catcode`\"=\active +\def\activedoublequote{{\tt \char '042}} +\let"=\activedoublequote +\catcode`\~=\active +\def~{{\tt \char '176}} +\chardef\hat=`\^ +\catcode`\^=\active +\def^{{\tt \hat}} + +\catcode`\_=\active +\def_{\ifusingtt\normalunderscore\_} +% Subroutine for the previous macro. +\def\_{\lvvmode \kern.06em \vbox{\hrule width.3em height.1ex}} + +% \lvvmode is equivalent in function to \leavevmode. +% Using \leavevmode runs into trouble when written out to +% an index file due to the expansion of \leavevmode into ``\unhbox +% \voidb@x'' ---which looks to TeX like ``\unhbox \voidb\x'' due to our +% magic tricks with @. +\def\lvvmode{\vbox to 0pt{}} + +\catcode`\|=\active +\def|{{\tt \char '174}} +\chardef \less=`\< +\catcode`\<=\active +\def<{{\tt \less}} +\chardef \gtr=`\> +\catcode`\>=\active +\def>{{\tt \gtr}} +\catcode`\+=\active +\def+{{\tt \char 43}} +%\catcode 27=\active +%\def^^[{$\diamondsuit$} + +% Used sometimes to turn off (effectively) the active characters +% even after parsing them. +\def\turnoffactive{\let"=\normaldoublequote +\let~=\normaltilde +\let^=\normalcaret +\let_=\normalunderscore +\let|=\normalverticalbar +\let<=\normalless +\let>=\normalgreater +\let+=\normalplus} + +% Set up an active definition for =, but don't enable it most of the time. +{\catcode`\==\active +\global\def={{\tt \char 61}}} + +\catcode`\@=0 + +% \rawbackslashxx output one backslash character in current font +\global\chardef\rawbackslashxx=`\\ +%{\catcode`\\=\other +%@gdef@rawbackslashxx{\}} + +% \rawbackslash redefines \ as input to do \rawbackslashxx. +{\catcode`\\=\active +@gdef@rawbackslash{@let\=@rawbackslashxx }} + +% \normalbackslash outputs one backslash in fixed width font. +\def\normalbackslash{{\tt\rawbackslashxx}} + +% Say @foo, not \foo, in error messages. +\escapechar=`\@ + +% \catcode 17=0 % Define control-q +\catcode`\\=\active + +% If a .fmt file is being used, we don't want the `\input texinfo' to show up. +% That is what \eatinput is for; after that, the `\' should revert to printing +% a backslash. +% +@gdef@eatinput input texinfo{@fixbackslash} +@global@let\ = @eatinput + +% On the other hand, perhaps the file did not have a `\input texinfo'. Then +% the first `\{ in the file would cause an error. This macro tries to fix +% that, assuming it is called before the first `\' could plausibly occur. +% +@gdef@fixbackslash{@ifx\@eatinput @let\ = @normalbackslash @fi} + +%% These look ok in all fonts, so just make them not special. The @rm below +%% makes sure that the current font starts out as the newly loaded cmr10 +@catcode`@$=@other @catcode`@%=@other @catcode`@&=@other @catcode`@#=@other + +@textfonts +@rm + +@c Local variables: +@c page-delimiter: "^\\\\message" +@c End: diff --git a/gnu/lib/libregex/doc/xregex.texi b/gnu/lib/libregex/doc/xregex.texi new file mode 100644 index 0000000000..9292b356ef --- /dev/null +++ b/gnu/lib/libregex/doc/xregex.texi @@ -0,0 +1,3021 @@ +\input texinfo +@c %**start of header +@setfilename regex.info +@settitle Regex +@c %**end of header + +@c \\{fill-paragraph} works better (for me, anyway) if the text in the +@c source file isn't indented. +@paragraphindent 2 + +@c Define a new index for our magic constants. +@defcodeindex cn + +@c Put everything in one index (arbitrarily chosen to be the concept index). +@syncodeindex cn cp +@syncodeindex ky cp +@syncodeindex pg cp +@syncodeindex tp cp +@syncodeindex vr cp + +@c Here is what we use in the Info `dir' file: +@c * Regex: (regex). Regular expression library. + + +@ifinfo +This file documents the GNU regular expression library. + +Copyright (C) 1992, 1993 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + +@ignore +Permission is granted to process this file through TeX and print the +results, provided the printed document carries a copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). +@end ignore + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +section entitled ``GNU General Public License'' is included exactly as +in the original, and provided that the entire resulting derived work is +distributed under the terms of a permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that the section entitled ``GNU General Public License'' may be +included in a translation approved by the Free Software Foundation +instead of in the original English. +@end ifinfo + + +@titlepage + +@title Regex +@subtitle edition 0.12a +@subtitle 19 September 1992 +@author Kathryn A. Hargreaves +@author Karl Berry + +@page + +@vskip 0pt plus 1filll +Copyright @copyright{} 1992 Free Software Foundation. + +Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +section entitled ``GNU General Public License'' is included exactly as +in the original, and provided that the entire resulting derived work is +distributed under the terms of a permission notice identical to this +one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that the section entitled ``GNU General Public License'' may be +included in a translation approved by the Free Software Foundation +instead of in the original English. + +@end titlepage + + +@ifinfo +@node Top, Overview, (dir), (dir) +@top Regular Expression Library + +This manual documents how to program with the GNU regular expression +library. This is edition 0.12a of the manual, 19 September 1992. + +The first part of this master menu lists the major nodes in this Info +document, including the index. The rest of the menu lists all the +lower level nodes in the document. + +@menu +* Overview:: +* Regular Expression Syntax:: +* Common Operators:: +* GNU Operators:: +* GNU Emacs Operators:: +* What Gets Matched?:: +* Programming with Regex:: +* Copying:: Copying and sharing Regex. +* Index:: General index. + --- The Detailed Node Listing --- + +Regular Expression Syntax + +* Syntax Bits:: +* Predefined Syntaxes:: +* Collating Elements vs. Characters:: +* The Backslash Character:: + +Common Operators + +* Match-self Operator:: Ordinary characters. +* Match-any-character Operator:: . +* Concatenation Operator:: Juxtaposition. +* Repetition Operators:: * + ? @{@} +* Alternation Operator:: | +* List Operators:: [...] [^...] +* Grouping Operators:: (...) +* Back-reference Operator:: \digit +* Anchoring Operators:: ^ $ + +Repetition Operators + +* Match-zero-or-more Operator:: * +* Match-one-or-more Operator:: + +* Match-zero-or-one Operator:: ? +* Interval Operators:: @{@} + +List Operators (@code{[} @dots{} @code{]} and @code{[^} @dots{} @code{]}) + +* Character Class Operators:: [:class:] +* Range Operator:: start-end + +Anchoring Operators + +* Match-beginning-of-line Operator:: ^ +* Match-end-of-line Operator:: $ + +GNU Operators + +* Word Operators:: +* Buffer Operators:: + +Word Operators + +* Non-Emacs Syntax Tables:: +* Match-word-boundary Operator:: \b +* Match-within-word Operator:: \B +* Match-beginning-of-word Operator:: \< +* Match-end-of-word Operator:: \> +* Match-word-constituent Operator:: \w +* Match-non-word-constituent Operator:: \W + +Buffer Operators + +* Match-beginning-of-buffer Operator:: \` +* Match-end-of-buffer Operator:: \' + +GNU Emacs Operators + +* Syntactic Class Operators:: + +Syntactic Class Operators + +* Emacs Syntax Tables:: +* Match-syntactic-class Operator:: \sCLASS +* Match-not-syntactic-class Operator:: \SCLASS + +Programming with Regex + +* GNU Regex Functions:: +* POSIX Regex Functions:: +* BSD Regex Functions:: + +GNU Regex Functions + +* GNU Pattern Buffers:: The re_pattern_buffer type. +* GNU Regular Expression Compiling:: re_compile_pattern () +* GNU Matching:: re_match () +* GNU Searching:: re_search () +* Matching/Searching with Split Data:: re_match_2 (), re_search_2 () +* Searching with Fastmaps:: re_compile_fastmap () +* GNU Translate Tables:: The `translate' field. +* Using Registers:: The re_registers type and related fns. +* Freeing GNU Pattern Buffers:: regfree () + +POSIX Regex Functions + +* POSIX Pattern Buffers:: The regex_t type. +* POSIX Regular Expression Compiling:: regcomp () +* POSIX Matching:: regexec () +* Reporting Errors:: regerror () +* Using Byte Offsets:: The regmatch_t type. +* Freeing POSIX Pattern Buffers:: regfree () + +BSD Regex Functions + +* BSD Regular Expression Compiling:: re_comp () +* BSD Searching:: re_exec () +@end menu +@end ifinfo +@node Overview, Regular Expression Syntax, Top, Top +@chapter Overview + +A @dfn{regular expression} (or @dfn{regexp}, or @dfn{pattern}) is a text +string that describes some (mathematical) set of strings. A regexp +@var{r} @dfn{matches} a string @var{s} if @var{s} is in the set of +strings described by @var{r}. + +Using the Regex library, you can: + +@itemize @bullet + +@item +see if a string matches a specified pattern as a whole, and + +@item +search within a string for a substring matching a specified pattern. + +@end itemize + +Some regular expressions match only one string, i.e., the set they +describe has only one member. For example, the regular expression +@samp{foo} matches the string @samp{foo} and no others. Other regular +expressions match more than one string, i.e., the set they describe has +more than one member. For example, the regular expression @samp{f*} +matches the set of strings made up of any number (including zero) of +@samp{f}s. As you can see, some characters in regular expressions match +themselves (such as @samp{f}) and some don't (such as @samp{*}); the +ones that don't match themselves instead let you specify patterns that +describe many different strings. + +To either match or search for a regular expression with the Regex +library functions, you must first compile it with a Regex pattern +compiling function. A @dfn{compiled pattern} is a regular expression +converted to the internal format used by the library functions. Once +you've compiled a pattern, you can use it for matching or searching any +number of times. + +The Regex library consists of two source files: @file{regex.h} and +@file{regex.c}. +@pindex regex.h +@pindex regex.c +Regex provides three groups of functions with which you can operate on +regular expressions. One group---the @sc{gnu} group---is more powerful +but not completely compatible with the other two, namely the @sc{posix} +and Berkeley @sc{unix} groups; its interface was designed specifically +for @sc{gnu}. The other groups have the same interfaces as do the +regular expression functions in @sc{posix} and Berkeley +@sc{unix}. + +We wrote this chapter with programmers in mind, not users of +programs---such as Emacs---that use Regex. We describe the Regex +library in its entirety, not how to write regular expressions that a +particular program understands. + + +@node Regular Expression Syntax, Common Operators, Overview, Top +@chapter Regular Expression Syntax + +@cindex regular expressions, syntax of +@cindex syntax of regular expressions + +@dfn{Characters} are things you can type. @dfn{Operators} are things in +a regular expression that match one or more characters. You compose +regular expressions from operators, which in turn you specify using one +or more characters. + +Most characters represent what we call the match-self operator, i.e., +they match themselves; we call these characters @dfn{ordinary}. Other +characters represent either all or parts of fancier operators; e.g., +@samp{.} represents what we call the match-any-character operator +(which, no surprise, matches (almost) any character); we call these +characters @dfn{special}. Two different things determine what +characters represent what operators: + +@enumerate +@item +the regular expression syntax your program has told the Regex library to +recognize, and + +@item +the context of the character in the regular expression. +@end enumerate + +In the following sections, we describe these things in more detail. + +@menu +* Syntax Bits:: +* Predefined Syntaxes:: +* Collating Elements vs. Characters:: +* The Backslash Character:: +@end menu + + +@node Syntax Bits, Predefined Syntaxes, , Regular Expression Syntax +@section Syntax Bits + +@cindex syntax bits + +In any particular syntax for regular expressions, some characters are +always special, others are sometimes special, and others are never +special. The particular syntax that Regex recognizes for a given +regular expression depends on the value in the @code{syntax} field of +the pattern buffer of that regular expression. + +You get a pattern buffer by compiling a regular expression. @xref{GNU +Pattern Buffers}, and @ref{POSIX Pattern Buffers}, for more information +on pattern buffers. @xref{GNU Regular Expression Compiling}, @ref{POSIX +Regular Expression Compiling}, and @ref{BSD Regular Expression +Compiling}, for more information on compiling. + +Regex considers the value of the @code{syntax} field to be a collection +of bits; we refer to these bits as @dfn{syntax bits}. In most cases, +they affect what characters represent what operators. We describe the +meanings of the operators to which we refer in @ref{Common Operators}, +@ref{GNU Operators}, and @ref{GNU Emacs Operators}. + +For reference, here is the complete list of syntax bits, in alphabetical +order: + +@table @code + +@cnindex RE_BACKSLASH_ESCAPE_IN_LIST +@item RE_BACKSLASH_ESCAPE_IN_LISTS +If this bit is set, then @samp{\} inside a list (@pxref{List Operators} +quotes (makes ordinary, if it's special) the following character; if +this bit isn't set, then @samp{\} is an ordinary character inside lists. +(@xref{The Backslash Character}, for what `\' does outside of lists.) + +@cnindex RE_BK_PLUS_QM +@item RE_BK_PLUS_QM +If this bit is set, then @samp{\+} represents the match-one-or-more +operator and @samp{\?} represents the match-zero-or-more operator; if +this bit isn't set, then @samp{+} represents the match-one-or-more +operator and @samp{?} represents the match-zero-or-one operator. This +bit is irrelevant if @code{RE_LIMITED_OPS} is set. + +@cnindex RE_CHAR_CLASSES +@item RE_CHAR_CLASSES +If this bit is set, then you can use character classes in lists; if this +bit isn't set, then you can't. + +@cnindex RE_CONTEXT_INDEP_ANCHORS +@item RE_CONTEXT_INDEP_ANCHORS +If this bit is set, then @samp{^} and @samp{$} are special anywhere outside +a list; if this bit isn't set, then these characters are special only in +certain contexts. @xref{Match-beginning-of-line Operator}, and +@ref{Match-end-of-line Operator}. + +@cnindex RE_CONTEXT_INDEP_OPS +@item RE_CONTEXT_INDEP_OPS +If this bit is set, then certain characters are special anywhere outside +a list; if this bit isn't set, then those characters are special only in +some contexts and are ordinary elsewhere. Specifically, if this bit +isn't set then @samp{*}, and (if the syntax bit @code{RE_LIMITED_OPS} +isn't set) @samp{+} and @samp{?} (or @samp{\+} and @samp{\?}, depending +on the syntax bit @code{RE_BK_PLUS_QM}) represent repetition operators +only if they're not first in a regular expression or just after an +open-group or alternation operator. The same holds for @samp{@{} (or +@samp{\@{}, depending on the syntax bit @code{RE_NO_BK_BRACES}) if +it is the beginning of a valid interval and the syntax bit +@code{RE_INTERVALS} is set. + +@cnindex RE_CONTEXT_INVALID_OPS +@item RE_CONTEXT_INVALID_OPS +If this bit is set, then repetition and alternation operators can't be +in certain positions within a regular expression. Specifically, the +regular expression is invalid if it has: + +@itemize @bullet + +@item +a repetition operator first in the regular expression or just after a +match-beginning-of-line, open-group, or alternation operator; or + +@item +an alternation operator first or last in the regular expression, just +before a match-end-of-line operator, or just after an alternation or +open-group operator. + +@end itemize + +If this bit isn't set, then you can put the characters representing the +repetition and alternation characters anywhere in a regular expression. +Whether or not they will in fact be operators in certain positions +depends on other syntax bits. + +@cnindex RE_DOT_NEWLINE +@item RE_DOT_NEWLINE +If this bit is set, then the match-any-character operator matches +a newline; if this bit isn't set, then it doesn't. + +@cnindex RE_DOT_NOT_NULL +@item RE_DOT_NOT_NULL +If this bit is set, then the match-any-character operator doesn't match +a null character; if this bit isn't set, then it does. + +@cnindex RE_INTERVALS +@item RE_INTERVALS +If this bit is set, then Regex recognizes interval operators; if this bit +isn't set, then it doesn't. + +@cnindex RE_LIMITED_OPS +@item RE_LIMITED_OPS +If this bit is set, then Regex doesn't recognize the match-one-or-more, +match-zero-or-one or alternation operators; if this bit isn't set, then +it does. + +@cnindex RE_NEWLINE_ALT +@item RE_NEWLINE_ALT +If this bit is set, then newline represents the alternation operator; if +this bit isn't set, then newline is ordinary. + +@cnindex RE_NO_BK_BRACES +@item RE_NO_BK_BRACES +If this bit is set, then @samp{@{} represents the open-interval operator +and @samp{@}} represents the close-interval operator; if this bit isn't +set, then @samp{\@{} represents the open-interval operator and +@samp{\@}} represents the close-interval operator. This bit is relevant +only if @code{RE_INTERVALS} is set. + +@cnindex RE_NO_BK_PARENS +@item RE_NO_BK_PARENS +If this bit is set, then @samp{(} represents the open-group operator and +@samp{)} represents the close-group operator; if this bit isn't set, then +@samp{\(} represents the open-group operator and @samp{\)} represents +the close-group operator. + +@cnindex RE_NO_BK_REFS +@item RE_NO_BK_REFS +If this bit is set, then Regex doesn't recognize @samp{\}@var{digit} as +the back reference operator; if this bit isn't set, then it does. + +@cnindex RE_NO_BK_VBAR +@item RE_NO_BK_VBAR +If this bit is set, then @samp{|} represents the alternation operator; +if this bit isn't set, then @samp{\|} represents the alternation +operator. This bit is irrelevant if @code{RE_LIMITED_OPS} is set. + +@cnindex RE_NO_EMPTY_RANGES +@item RE_NO_EMPTY_RANGES +If this bit is set, then a regular expression with a range whose ending +point collates lower than its starting point is invalid; if this bit +isn't set, then Regex considers such a range to be empty. + +@cnindex RE_UNMATCHED_RIGHT_PAREN_ORD +@item RE_UNMATCHED_RIGHT_PAREN_ORD +If this bit is set and the regular expression has no matching open-group +operator, then Regex considers what would otherwise be a close-group +operator (based on how @code{RE_NO_BK_PARENS} is set) to match @samp{)}. + +@end table + + +@node Predefined Syntaxes, Collating Elements vs. Characters, Syntax Bits, Regular Expression Syntax +@section Predefined Syntaxes + +If you're programming with Regex, you can set a pattern buffer's +(@pxref{GNU Pattern Buffers}, and @ref{POSIX Pattern Buffers}) +@code{syntax} field either to an arbitrary combination of syntax bits +(@pxref{Syntax Bits}) or else to the configurations defined by Regex. +These configurations define the syntaxes used by certain +programs---@sc{gnu} Emacs, +@cindex Emacs +@sc{posix} Awk, +@cindex POSIX Awk +traditional Awk, +@cindex Awk +Grep, +@cindex Grep +@cindex Egrep +Egrep---in addition to syntaxes for @sc{posix} basic and extended +regular expressions. + +The predefined syntaxes--taken directly from @file{regex.h}---are: + +@example +[[[ syntaxes ]]] +@end example + +@node Collating Elements vs. Characters, The Backslash Character, Predefined Syntaxes, Regular Expression Syntax +@section Collating Elements vs.@: Characters + +@sc{posix} generalizes the notion of a character to that of a +collating element. It defines a @dfn{collating element} to be ``a +sequence of one or more bytes defined in the current collating sequence +as a unit of collation.'' + +This generalizes the notion of a character in +two ways. First, a single character can map into two or more collating +elements. For example, the German +@tex +`\ss' +@end tex +@ifinfo +``es-zet'' +@end ifinfo +collates as the collating element @samp{s} followed by another collating +element @samp{s}. Second, two or more characters can map into one +collating element. For example, the Spanish @samp{ll} collates after +@samp{l} and before @samp{m}. + +Since @sc{posix}'s ``collating element'' preserves the essential idea of +a ``character,'' we use the latter, more familiar, term in this document. + +@node The Backslash Character, , Collating Elements vs. Characters, Regular Expression Syntax +@section The Backslash Character + +@cindex \ +The @samp{\} character has one of four different meanings, depending on +the context in which you use it and what syntax bits are set +(@pxref{Syntax Bits}). It can: 1) stand for itself, 2) quote the next +character, 3) introduce an operator, or 4) do nothing. + +@enumerate +@item +It stands for itself inside a list +(@pxref{List Operators}) if the syntax bit +@code{RE_BACKSLASH_ESCAPE_IN_LISTS} is not set. For example, @samp{[\]} +would match @samp{\}. + +@item +It quotes (makes ordinary, if it's special) the next character when you +use it either: + +@itemize @bullet +@item +outside a list,@footnote{Sometimes +you don't have to explicitly quote special characters to make +them ordinary. For instance, most characters lose any special meaning +inside a list (@pxref{List Operators}). In addition, if the syntax bits +@code{RE_CONTEXT_INVALID_OPS} and @code{RE_CONTEXT_INDEP_OPS} +aren't set, then (for historical reasons) the matcher considers special +characters ordinary if they are in contexts where the operations they +represent make no sense; for example, then the match-zero-or-more +operator (represented by @samp{*}) matches itself in the regular +expression @samp{*foo} because there is no preceding expression on which +it can operate. It is poor practice, however, to depend on this +behavior; if you want a special character to be ordinary outside a list, +it's better to always quote it, regardless.} or + +@item +inside a list and the syntax bit @code{RE_BACKSLASH_ESCAPE_IN_LISTS} is set. + +@end itemize + +@item +It introduces an operator when followed by certain ordinary +characters---sometimes only when certain syntax bits are set. See the +cases @code{RE_BK_PLUS_QM}, @code{RE_NO_BK_BRACES}, @code{RE_NO_BK_VAR}, +@code{RE_NO_BK_PARENS}, @code{RE_NO_BK_REF} in @ref{Syntax Bits}. Also: + +@itemize @bullet +@item +@samp{\b} represents the match-word-boundary operator +(@pxref{Match-word-boundary Operator}). + +@item +@samp{\B} represents the match-within-word operator +(@pxref{Match-within-word Operator}). + +@item +@samp{\<} represents the match-beginning-of-word operator @* +(@pxref{Match-beginning-of-word Operator}). + +@item +@samp{\>} represents the match-end-of-word operator +(@pxref{Match-end-of-word Operator}). + +@item +@samp{\w} represents the match-word-constituent operator +(@pxref{Match-word-constituent Operator}). + +@item +@samp{\W} represents the match-non-word-constituent operator +(@pxref{Match-non-word-constituent Operator}). + +@item +@samp{\`} represents the match-beginning-of-buffer +operator and @samp{\'} represents the match-end-of-buffer operator +(@pxref{Buffer Operators}). + +@item +If Regex was compiled with the C preprocessor symbol @code{emacs} +defined, then @samp{\s@var{class}} represents the match-syntactic-class +operator and @samp{\S@var{class}} represents the +match-not-syntactic-class operator (@pxref{Syntactic Class Operators}). + +@end itemize + +@item +In all other cases, Regex ignores @samp{\}. For example, +@samp{\n} matches @samp{n}. + +@end enumerate + +@node Common Operators, GNU Operators, Regular Expression Syntax, Top +@chapter Common Operators + +You compose regular expressions from operators. In the following +sections, we describe the regular expression operators specified by +@sc{posix}; @sc{gnu} also uses these. Most operators have more than one +representation as characters. @xref{Regular Expression Syntax}, for +what characters represent what operators under what circumstances. + +For most operators that can be represented in two ways, one +representation is a single character and the other is that character +preceded by @samp{\}. For example, either @samp{(} or @samp{\(} +represents the open-group operator. Which one does depends on the +setting of a syntax bit, in this case @code{RE_NO_BK_PARENS}. Why is +this so? Historical reasons dictate some of the varying +representations, while @sc{posix} dictates others. + +Finally, almost all characters lose any special meaning inside a list +(@pxref{List Operators}). + +@menu +* Match-self Operator:: Ordinary characters. +* Match-any-character Operator:: . +* Concatenation Operator:: Juxtaposition. +* Repetition Operators:: * + ? @{@} +* Alternation Operator:: | +* List Operators:: [...] [^...] +* Grouping Operators:: (...) +* Back-reference Operator:: \digit +* Anchoring Operators:: ^ $ +@end menu + +@node Match-self Operator, Match-any-character Operator, , Common Operators +@section The Match-self Operator (@var{ordinary character}) + +This operator matches the character itself. All ordinary characters +(@pxref{Regular Expression Syntax}) represent this operator. For +example, @samp{f} is always an ordinary character, so the regular +expression @samp{f} matches only the string @samp{f}. In +particular, it does @emph{not} match the string @samp{ff}. + +@node Match-any-character Operator, Concatenation Operator, Match-self Operator, Common Operators +@section The Match-any-character Operator (@code{.}) + +@cindex @samp{.} + +This operator matches any single printing or nonprinting character +except it won't match a: + +@table @asis +@item newline +if the syntax bit @code{RE_DOT_NEWLINE} isn't set. + +@item null +if the syntax bit @code{RE_DOT_NOT_NULL} is set. + +@end table + +The @samp{.} (period) character represents this operator. For example, +@samp{a.b} matches any three-character string beginning with @samp{a} +and ending with @samp{b}. + +@node Concatenation Operator, Repetition Operators, Match-any-character Operator, Common Operators +@section The Concatenation Operator + +This operator concatenates two regular expressions @var{a} and @var{b}. +No character represents this operator; you simply put @var{b} after +@var{a}. The result is a regular expression that will match a string if +@var{a} matches its first part and @var{b} matches the rest. For +example, @samp{xy} (two match-self operators) matches @samp{xy}. + +@node Repetition Operators, Alternation Operator, Concatenation Operator, Common Operators +@section Repetition Operators + +Repetition operators repeat the preceding regular expression a specified +number of times. + +@menu +* Match-zero-or-more Operator:: * +* Match-one-or-more Operator:: + +* Match-zero-or-one Operator:: ? +* Interval Operators:: @{@} +@end menu + +@node Match-zero-or-more Operator, Match-one-or-more Operator, , Repetition Operators +@subsection The Match-zero-or-more Operator (@code{*}) + +@cindex @samp{*} + +This operator repeats the smallest possible preceding regular expression +as many times as necessary (including zero) to match the pattern. +@samp{*} represents this operator. For example, @samp{o*} +matches any string made up of zero or more @samp{o}s. Since this +operator operates on the smallest preceding regular expression, +@samp{fo*} has a repeating @samp{o}, not a repeating @samp{fo}. So, +@samp{fo*} matches @samp{f}, @samp{fo}, @samp{foo}, and so on. + +Since the match-zero-or-more operator is a suffix operator, it may be +useless as such when no regular expression precedes it. This is the +case when it: + +@itemize @bullet +@item +is first in a regular expression, or + +@item +follows a match-beginning-of-line, open-group, or alternation +operator. + +@end itemize + +@noindent +Three different things can happen in these cases: + +@enumerate +@item +If the syntax bit @code{RE_CONTEXT_INVALID_OPS} is set, then the +regular expression is invalid. + +@item +If @code{RE_CONTEXT_INVALID_OPS} isn't set, but +@code{RE_CONTEXT_INDEP_OPS} is, then @samp{*} represents the +match-zero-or-more operator (which then operates on the empty string). + +@item +Otherwise, @samp{*} is ordinary. + +@end enumerate + +@cindex backtracking +The matcher processes a match-zero-or-more operator by first matching as +many repetitions of the smallest preceding regular expression as it can. +Then it continues to match the rest of the pattern. + +If it can't match the rest of the pattern, it backtracks (as many times +as necessary), each time discarding one of the matches until it can +either match the entire pattern or be certain that it cannot get a +match. For example, when matching @samp{ca*ar} against @samp{caaar}, +the matcher first matches all three @samp{a}s of the string with the +@samp{a*} of the regular expression. However, it cannot then match the +final @samp{ar} of the regular expression against the final @samp{r} of +the string. So it backtracks, discarding the match of the last @samp{a} +in the string. It can then match the remaining @samp{ar}. + + +@node Match-one-or-more Operator, Match-zero-or-one Operator, Match-zero-or-more Operator, Repetition Operators +@subsection The Match-one-or-more Operator (@code{+} or @code{\+}) + +@cindex @samp{+} + +If the syntax bit @code{RE_LIMITED_OPS} is set, then Regex doesn't recognize +this operator. Otherwise, if the syntax bit @code{RE_BK_PLUS_QM} isn't +set, then @samp{+} represents this operator; if it is, then @samp{\+} +does. + +This operator is similar to the match-zero-or-more operator except that +it repeats the preceding regular expression at least once; +@pxref{Match-zero-or-more Operator}, for what it operates on, how some +syntax bits affect it, and how Regex backtracks to match it. + +For example, supposing that @samp{+} represents the match-one-or-more +operator; then @samp{ca+r} matches, e.g., @samp{car} and +@samp{caaaar}, but not @samp{cr}. + +@node Match-zero-or-one Operator, Interval Operators, Match-one-or-more Operator, Repetition Operators +@subsection The Match-zero-or-one Operator (@code{?} or @code{\?}) +@cindex @samp{?} + +If the syntax bit @code{RE_LIMITED_OPS} is set, then Regex doesn't +recognize this operator. Otherwise, if the syntax bit +@code{RE_BK_PLUS_QM} isn't set, then @samp{?} represents this operator; +if it is, then @samp{\?} does. + +This operator is similar to the match-zero-or-more operator except that +it repeats the preceding regular expression once or not at all; +@pxref{Match-zero-or-more Operator}, to see what it operates on, how +some syntax bits affect it, and how Regex backtracks to match it. + +For example, supposing that @samp{?} represents the match-zero-or-one +operator; then @samp{ca?r} matches both @samp{car} and @samp{cr}, but +nothing else. + +@node Interval Operators, , Match-zero-or-one Operator, Repetition Operators +@subsection Interval Operators (@code{@{} @dots{} @code{@}} or @code{\@{} @dots{} @code{\@}}) + +@cindex interval expression +@cindex @samp{@{} +@cindex @samp{@}} +@cindex @samp{\@{} +@cindex @samp{\@}} + +If the syntax bit @code{RE_INTERVALS} is set, then Regex recognizes +@dfn{interval expressions}. They repeat the smallest possible preceding +regular expression a specified number of times. + +If the syntax bit @code{RE_NO_BK_BRACES} is set, @samp{@{} represents +the @dfn{open-interval operator} and @samp{@}} represents the +@dfn{close-interval operator} ; otherwise, @samp{\@{} and @samp{\@}} do. + +Specifically, supposing that @samp{@{} and @samp{@}} represent the +open-interval and close-interval operators; then: + +@table @code +@item @{@var{count}@} +matches exactly @var{count} occurrences of the preceding regular +expression. + +@item @{@var{min,}@} +matches @var{min} or more occurrences of the preceding regular +expression. + +@item @{@var{min, max}@} +matches at least @var{min} but no more than @var{max} occurrences of +the preceding regular expression. + +@end table + +The interval expression (but not necessarily the regular expression that +contains it) is invalid if: + +@itemize @bullet +@item +@var{min} is greater than @var{max}, or + +@item +any of @var{count}, @var{min}, or @var{max} are outside the range +zero to @code{RE_DUP_MAX} (which symbol @file{regex.h} +defines). + +@end itemize + +If the interval expression is invalid and the syntax bit +@code{RE_NO_BK_BRACES} is set, then Regex considers all the +characters in the would-be interval to be ordinary. If that bit +isn't set, then the regular expression is invalid. + +If the interval expression is valid but there is no preceding regular +expression on which to operate, then if the syntax bit +@code{RE_CONTEXT_INVALID_OPS} is set, the regular expression is invalid. +If that bit isn't set, then Regex considers all the characters---other +than backslashes, which it ignores---in the would-be interval to be +ordinary. + + +@node Alternation Operator, List Operators, Repetition Operators, Common Operators +@section The Alternation Operator (@code{|} or @code{\|}) + +@kindex | +@kindex \| +@cindex alternation operator +@cindex or operator + +If the syntax bit @code{RE_LIMITED_OPS} is set, then Regex doesn't +recognize this operator. Otherwise, if the syntax bit +@code{RE_NO_BK_VBAR} is set, then @samp{|} represents this operator; +otherwise, @samp{\|} does. + +Alternatives match one of a choice of regular expressions: +if you put the character(s) representing the alternation operator between +any two regular expressions @var{a} and @var{b}, the result matches +the union of the strings that @var{a} and @var{b} match. For +example, supposing that @samp{|} is the alternation operator, then +@samp{foo|bar|quux} would match any of @samp{foo}, @samp{bar} or +@samp{quux}. + +@ignore +@c Nobody needs to disallow empty alternatives any more. +If the syntax bit @code{RE_NO_EMPTY_ALTS} is set, then if either of the regular +expressions @var{a} or @var{b} is empty, the +regular expression is invalid. More precisely, if this syntax bit is +set, then the alternation operator can't: + +@itemize @bullet +@item +be first or last in a regular expression; + +@item +follow either another alternation operator or an open-group operator +(@pxref{Grouping Operators}); or + +@item +precede a close-group operator. + +@end itemize + +@noindent +For example, supposing @samp{(} and @samp{)} represent the open and +close-group operators, then @samp{|foo}, @samp{foo|}, @samp{foo||bar}, +@samp{foo(|bar)}, and @samp{(foo|)bar} would all be invalid. +@end ignore + +The alternation operator operates on the @emph{largest} possible +surrounding regular expressions. (Put another way, it has the lowest +precedence of any regular expression operator.) +Thus, the only way you can +delimit its arguments is to use grouping. For example, if @samp{(} and +@samp{)} are the open and close-group operators, then @samp{fo(o|b)ar} +would match either @samp{fooar} or @samp{fobar}. (@samp{foo|bar} would +match @samp{foo} or @samp{bar}.) + +@cindex backtracking +The matcher usually tries all combinations of alternatives so as to +match the longest possible string. For example, when matching +@samp{(fooq|foo)*(qbarquux|bar)} against @samp{fooqbarquux}, it cannot +take, say, the first (``depth-first'') combination it could match, since +then it would be content to match just @samp{fooqbar}. + +@comment xx something about leftmost-longest + + +@node List Operators, Grouping Operators, Alternation Operator, Common Operators +@section List Operators (@code{[} @dots{} @code{]} and @code{[^} @dots{} @code{]}) + +@cindex matching list +@cindex @samp{[} +@cindex @samp{]} +@cindex @samp{^} +@cindex @samp{-} +@cindex @samp{\} +@cindex @samp{[^} +@cindex nonmatching list +@cindex matching newline +@cindex bracket expression + +@dfn{Lists}, also called @dfn{bracket expressions}, are a set of one or +more items. An @dfn{item} is a character, +@ignore +(These get added when they get implemented.) +a collating symbol, an equivalence class expression, +@end ignore +a character class expression, or a range expression. The syntax bits +affect which kinds of items you can put in a list. We explain the last +two items in subsections below. Empty lists are invalid. + +A @dfn{matching list} matches a single character represented by one of +the list items. You form a matching list by enclosing one or more items +within an @dfn{open-matching-list operator} (represented by @samp{[}) +and a @dfn{close-list operator} (represented by @samp{]}). + +For example, @samp{[ab]} matches either @samp{a} or @samp{b}. +@samp{[ad]*} matches the empty string and any string composed of just +@samp{a}s and @samp{d}s in any order. Regex considers invalid a regular +expression with a @samp{[} but no matching +@samp{]}. + +@dfn{Nonmatching lists} are similar to matching lists except that they +match a single character @emph{not} represented by one of the list +items. You use an @dfn{open-nonmatching-list operator} (represented by +@samp{[^}@footnote{Regex therefore doesn't consider the @samp{^} to be +the first character in the list. If you put a @samp{^} character first +in (what you think is) a matching list, you'll turn it into a +nonmatching list.}) instead of an open-matching-list operator to start a +nonmatching list. + +For example, @samp{[^ab]} matches any character except @samp{a} or +@samp{b}. + +If the @code{posix_newline} field in the pattern buffer (@pxref{GNU +Pattern Buffers} is set, then nonmatching lists do not match a newline. + +Most characters lose any special meaning inside a list. The special +characters inside a list follow. + +@table @samp +@item ] +ends the list if it's not the first list item. So, if you want to make +the @samp{]} character a list item, you must put it first. + +@item \ +quotes the next character if the syntax bit @code{RE_BACKSLASH_ESCAPE_IN_LISTS} is +set. + +@ignore +Put these in if they get implemented. + +@item [. +represents the open-collating-symbol operator (@pxref{Collating Symbol +Operators}). + +@item .] +represents the close-collating-symbol operator. + +@item [= +represents the open-equivalence-class operator (@pxref{Equivalence Class +Operators}). + +@item =] +represents the close-equivalence-class operator. + +@end ignore + +@item [: +represents the open-character-class operator (@pxref{Character Class +Operators}) if the syntax bit @code{RE_CHAR_CLASSES} is set and what +follows is a valid character class expression. + +@item :] +represents the close-character-class operator if the syntax bit +@code{RE_CHAR_CLASSES} is set and what precedes it is an +open-character-class operator followed by a valid character class name. + +@item - +represents the range operator (@pxref{Range Operator}) if it's +not first or last in a list or the ending point of a range. + +@end table + +@noindent +All other characters are ordinary. For example, @samp{[.*]} matches +@samp{.} and @samp{*}. + +@menu +* Character Class Operators:: [:class:] +* Range Operator:: start-end +@end menu + +@ignore +(If collating symbols and equivalence class expressions get implemented, +then add this.) + +node Collating Symbol Operators +subsubsection Collating Symbol Operators (@code{[.} @dots{} @code{.]}) + +If the syntax bit @code{XX} is set, then you can represent +collating symbols inside lists. You form a @dfn{collating symbol} by +putting a collating element between an @dfn{open-collating-symbol +operator} and an @dfn{close-collating-symbol operator}. @samp{[.} +represents the open-collating-symbol operator and @samp{.]} represents +the close-collating-symbol operator. For example, if @samp{ll} is a +collating element, then @samp{[[.ll.]]} would match @samp{ll}. + +node Equivalence Class Operators +subsubsection Equivalence Class Operators (@code{[=} @dots{} @code{=]}) +@cindex equivalence class expression in regex +@cindex @samp{[=} in regex +@cindex @samp{=]} in regex + +If the syntax bit @code{XX} is set, then Regex recognizes equivalence class +expressions inside lists. A @dfn{equivalence class expression} is a set +of collating elements which all belong to the same equivalence class. +You form an equivalence class expression by putting a collating +element between an @dfn{open-equivalence-class operator} and a +@dfn{close-equivalence-class operator}. @samp{[=} represents the +open-equivalence-class operator and @samp{=]} represents the +close-equivalence-class operator. For example, if @samp{a} and @samp{A} +were an equivalence class, then both @samp{[[=a=]]} and @samp{[[=A=]]} +would match both @samp{a} and @samp{A}. If the collating element in an +equivalence class expression isn't part of an equivalence class, then +the matcher considers the equivalence class expression to be a collating +symbol. + +@end ignore + +@node Character Class Operators, Range Operator, , List Operators +@subsection Character Class Operators (@code{[:} @dots{} @code{:]}) + +@cindex character classes +@cindex @samp{[:} in regex +@cindex @samp{:]} in regex + +If the syntax bit @code{RE_CHARACTER_CLASSES} is set, then Regex +recognizes character class expressions inside lists. A @dfn{character +class expression} matches one character from a given class. You form a +character class expression by putting a character class name between an +@dfn{open-character-class operator} (represented by @samp{[:}) and a +@dfn{close-character-class operator} (represented by @samp{:]}). The +character class names and their meanings are: + +@table @code + +@item alnum +letters and digits + +@item alpha +letters + +@item blank +system-dependent; for @sc{gnu}, a space or tab + +@item cntrl +control characters (in the @sc{ascii} encoding, code 0177 and codes +less than 040) + +@item digit +digits + +@item graph +same as @code{print} except omits space + +@item lower +lowercase letters + +@item print +printable characters (in the @sc{ascii} encoding, space +tilde---codes 040 through 0176) + +@item punct +neither control nor alphanumeric characters + +@item space +space, carriage return, newline, vertical tab, and form feed + +@item upper +uppercase letters + +@item xdigit +hexadecimal digits: @code{0}--@code{9}, @code{a}--@code{f}, @code{A}--@code{F} + +@end table + +@noindent +These correspond to the definitions in the C library's @file{} +facility. For example, @samp{[:alpha:]} corresponds to the standard +facility @code{isalpha}. Regex recognizes character class expressions +only inside of lists; so @samp{[[:alpha:]]} matches any letter, but +@samp{[:alpha:]} outside of a bracket expression and not followed by a +repetition operator matches just itself. + +@node Range Operator, , Character Class Operators, List Operators +@subsection The Range Operator (@code{-}) + +Regex recognizes @dfn{range expressions} inside a list. They represent +those characters +that fall between two elements in the current collating sequence. You +form a range expression by putting a @dfn{range operator} between two +@ignore +(If these get implemented, then substitute this for ``characters.'') +of any of the following: characters, collating elements, collating symbols, +and equivalence class expressions. The starting point of the range and +the ending point of the range don't have to be the same kind of item, +e.g., the starting point could be a collating element and the ending +point could be an equivalence class expression. If a range's ending +point is an equivalence class, then all the collating elements in that +class will be in the range. +@end ignore +characters.@footnote{You can't use a character class for the starting +or ending point of a range, since a character class is not a single +character.} @samp{-} represents the range operator. For example, +@samp{a-f} within a list represents all the characters from @samp{a} +through @samp{f} +inclusively. + +If the syntax bit @code{RE_NO_EMPTY_RANGES} is set, then if the range's +ending point collates less than its starting point, the range (and the +regular expression containing it) is invalid. For example, the regular +expression @samp{[z-a]} would be invalid. If this bit isn't set, then +Regex considers such a range to be empty. + +Since @samp{-} represents the range operator, if you want to make a +@samp{-} character itself +a list item, you must do one of the following: + +@itemize @bullet +@item +Put the @samp{-} either first or last in the list. + +@item +Include a range whose starting point collates strictly lower than +@samp{-} and whose ending point collates equal or higher. Unless a +range is the first item in a list, a @samp{-} can't be its starting +point, but @emph{can} be its ending point. That is because Regex +considers @samp{-} to be the range operator unless it is preceded by +another @samp{-}. For example, in the @sc{ascii} encoding, @samp{)}, +@samp{*}, @samp{+}, @samp{,}, @samp{-}, @samp{.}, and @samp{/} are +contiguous characters in the collating sequence. You might think that +@samp{[)-+--/]} has two ranges: @samp{)-+} and @samp{--/}. Rather, it +has the ranges @samp{)-+} and @samp{+--}, plus the character @samp{/}, so +it matches, e.g., @samp{,}, not @samp{.}. + +@item +Put a range whose starting point is @samp{-} first in the list. + +@end itemize + +For example, @samp{[-a-z]} matches a lowercase letter or a hyphen (in +English, in @sc{ascii}). + + +@node Grouping Operators, Back-reference Operator, List Operators, Common Operators +@section Grouping Operators (@code{(} @dots{} @code{)} or @code{\(} @dots{} @code{\)}) + +@kindex ( +@kindex ) +@kindex \( +@kindex \) +@cindex grouping +@cindex subexpressions +@cindex parenthesizing + +A @dfn{group}, also known as a @dfn{subexpression}, consists of an +@dfn{open-group operator}, any number of other operators, and a +@dfn{close-group operator}. Regex treats this sequence as a unit, just +as mathematics and programming languages treat a parenthesized +expression as a unit. + +Therefore, using @dfn{groups}, you can: + +@itemize @bullet +@item +delimit the argument(s) to an alternation operator (@pxref{Alternation +Operator}) or a repetition operator (@pxref{Repetition +Operators}). + +@item +keep track of the indices of the substring that matched a given group. +@xref{Using Registers}, for a precise explanation. +This lets you: + +@itemize @bullet +@item +use the back-reference operator (@pxref{Back-reference Operator}). + +@item +use registers (@pxref{Using Registers}). + +@end itemize + +@end itemize + +If the syntax bit @code{RE_NO_BK_PARENS} is set, then @samp{(} represents +the open-group operator and @samp{)} represents the +close-group operator; otherwise, @samp{\(} and @samp{\)} do. + +If the syntax bit @code{RE_UNMATCHED_RIGHT_PAREN_ORD} is set and a +close-group operator has no matching open-group operator, then Regex +considers it to match @samp{)}. + + +@node Back-reference Operator, Anchoring Operators, Grouping Operators, Common Operators +@section The Back-reference Operator (@dfn{\}@var{digit}) + +@cindex back references + +If the syntax bit @code{RE_NO_BK_REF} isn't set, then Regex recognizes +back references. A back reference matches a specified preceding group. +The back reference operator is represented by @samp{\@var{digit}} +anywhere after the end of a regular expression's @w{@var{digit}-th} +group (@pxref{Grouping Operators}). + +@var{digit} must be between @samp{1} and @samp{9}. The matcher assigns +numbers 1 through 9 to the first nine groups it encounters. By using +one of @samp{\1} through @samp{\9} after the corresponding group's +close-group operator, you can match a substring identical to the +one that the group does. + +Back references match according to the following (in all examples below, +@samp{(} represents the open-group, @samp{)} the close-group, @samp{@{} +the open-interval and @samp{@}} the close-interval operator): + +@itemize @bullet +@item +If the group matches a substring, the back reference matches an +identical substring. For example, @samp{(a)\1} matches @samp{aa} and +@samp{(bana)na\1bo\1} matches @samp{bananabanabobana}. Likewise, +@samp{(.*)\1} matches any (newline-free if the syntax bit +@code{RE_DOT_NEWLINE} isn't set) string that is composed of two +identical halves; the @samp{(.*)} matches the first half and the +@samp{\1} matches the second half. + +@item +If the group matches more than once (as it might if followed +by, e.g., a repetition operator), then the back reference matches the +substring the group @emph{last} matched. For example, +@samp{((a*)b)*\1\2} matches @samp{aabababa}; first @w{group 1} (the +outer one) matches @samp{aab} and @w{group 2} (the inner one) matches +@samp{aa}. Then @w{group 1} matches @samp{ab} and @w{group 2} matches +@samp{a}. So, @samp{\1} matches @samp{ab} and @samp{\2} matches +@samp{a}. + +@item +If the group doesn't participate in a match, i.e., it is part of an +alternative not taken or a repetition operator allows zero repetitions +of it, then the back reference makes the whole match fail. For example, +@samp{(one()|two())-and-(three\2|four\3)} matches @samp{one-and-three} +and @samp{two-and-four}, but not @samp{one-and-four} or +@samp{two-and-three}. For example, if the pattern matches +@samp{one-and-}, then its @w{group 2} matches the empty string and its +@w{group 3} doesn't participate in the match. So, if it then matches +@samp{four}, then when it tries to back reference @w{group 3}---which it +will attempt to do because @samp{\3} follows the @samp{four}---the match +will fail because @w{group 3} didn't participate in the match. + +@end itemize + +You can use a back reference as an argument to a repetition operator. For +example, @samp{(a(b))\2*} matches @samp{a} followed by two or more +@samp{b}s. Similarly, @samp{(a(b))\2@{3@}} matches @samp{abbbb}. + +If there is no preceding @w{@var{digit}-th} subexpression, the regular +expression is invalid. + + +@node Anchoring Operators, , Back-reference Operator, Common Operators +@section Anchoring Operators + +@cindex anchoring +@cindex regexp anchoring + +These operators can constrain a pattern to match only at the beginning or +end of the entire string or at the beginning or end of a line. + +@menu +* Match-beginning-of-line Operator:: ^ +* Match-end-of-line Operator:: $ +@end menu + + +@node Match-beginning-of-line Operator, Match-end-of-line Operator, , Anchoring Operators +@subsection The Match-beginning-of-line Operator (@code{^}) + +@kindex ^ +@cindex beginning-of-line operator +@cindex anchors + +This operator can match the empty string either at the beginning of the +string or after a newline character. Thus, it is said to @dfn{anchor} +the pattern to the beginning of a line. + +In the cases following, @samp{^} represents this operator. (Otherwise, +@samp{^} is ordinary.) + +@itemize @bullet + +@item +It (the @samp{^}) is first in the pattern, as in @samp{^foo}. + +@cnindex RE_CONTEXT_INDEP_ANCHORS @r{(and @samp{^})} +@item +The syntax bit @code{RE_CONTEXT_INDEP_ANCHORS} is set, and it is outside +a bracket expression. + +@cindex open-group operator and @samp{^} +@cindex alternation operator and @samp{^} +@item +It follows an open-group or alternation operator, as in @samp{a\(^b\)} +and @samp{a\|^b}. @xref{Grouping Operators}, and @ref{Alternation +Operator}. + +@end itemize + +These rules imply that some valid patterns containing @samp{^} cannot be +matched; for example, @samp{foo^bar} if @code{RE_CONTEXT_INDEP_ANCHORS} +is set. + +@vindex not_bol @r{field in pattern buffer} +If the @code{not_bol} field is set in the pattern buffer (@pxref{GNU +Pattern Buffers}), then @samp{^} fails to match at the beginning of the +string. @xref{POSIX Matching}, for when you might find this useful. + +@vindex newline_anchor @r{field in pattern buffer} +If the @code{newline_anchor} field is set in the pattern buffer, then +@samp{^} fails to match after a newline. This is useful when you do not +regard the string to be matched as broken into lines. + + +@node Match-end-of-line Operator, , Match-beginning-of-line Operator, Anchoring Operators +@subsection The Match-end-of-line Operator (@code{$}) + +@kindex $ +@cindex end-of-line operator +@cindex anchors + +This operator can match the empty string either at the end of +the string or before a newline character in the string. Thus, it is +said to @dfn{anchor} the pattern to the end of a line. + +It is always represented by @samp{$}. For example, @samp{foo$} usually +matches, e.g., @samp{foo} and, e.g., the first three characters of +@samp{foo\nbar}. + +Its interaction with the syntax bits and pattern buffer fields is +exactly the dual of @samp{^}'s; see the previous section. (That is, +``beginning'' becomes ``end'', ``next'' becomes ``previous'', and +``after'' becomes ``before''.) + + +@node GNU Operators, GNU Emacs Operators, Common Operators, Top +@chapter GNU Operators + +Following are operators that @sc{gnu} defines (and @sc{posix} doesn't). + +@menu +* Word Operators:: +* Buffer Operators:: +@end menu + +@node Word Operators, Buffer Operators, , GNU Operators +@section Word Operators + +The operators in this section require Regex to recognize parts of words. +Regex uses a syntax table to determine whether or not a character is +part of a word, i.e., whether or not it is @dfn{word-constituent}. + +@menu +* Non-Emacs Syntax Tables:: +* Match-word-boundary Operator:: \b +* Match-within-word Operator:: \B +* Match-beginning-of-word Operator:: \< +* Match-end-of-word Operator:: \> +* Match-word-constituent Operator:: \w +* Match-non-word-constituent Operator:: \W +@end menu + +@node Non-Emacs Syntax Tables, Match-word-boundary Operator, , Word Operators +@subsection Non-Emacs Syntax Tables + +A @dfn{syntax table} is an array indexed by the characters in your +character set. In the @sc{ascii} encoding, therefore, a syntax table +has 256 elements. Regex always uses a @code{char *} variable +@code{re_syntax_table} as its syntax table. In some cases, it +initializes this variable and in others it expects you to initialize it. + +@itemize @bullet +@item +If Regex is compiled with the preprocessor symbols @code{emacs} and +@code{SYNTAX_TABLE} both undefined, then Regex allocates +@code{re_syntax_table} and initializes an element @var{i} either to +@code{Sword} (which it defines) if @var{i} is a letter, number, or +@samp{_}, or to zero if it's not. + +@item +If Regex is compiled with @code{emacs} undefined but @code{SYNTAX_TABLE} +defined, then Regex expects you to define a @code{char *} variable +@code{re_syntax_table} to be a valid syntax table. + +@item +@xref{Emacs Syntax Tables}, for what happens when Regex is compiled with +the preprocessor symbol @code{emacs} defined. + +@end itemize + +@node Match-word-boundary Operator, Match-within-word Operator, Non-Emacs Syntax Tables, Word Operators +@subsection The Match-word-boundary Operator (@code{\b}) + +@cindex @samp{\b} +@cindex word boundaries, matching + +This operator (represented by @samp{\b}) matches the empty string at +either the beginning or the end of a word. For example, @samp{\brat\b} +matches the separate word @samp{rat}. + +@node Match-within-word Operator, Match-beginning-of-word Operator, Match-word-boundary Operator, Word Operators +@subsection The Match-within-word Operator (@code{\B}) + +@cindex @samp{\B} + +This operator (represented by @samp{\B}) matches the empty string within +a word. For example, @samp{c\Brat\Be} matches @samp{crate}, but +@samp{dirty \Brat} doesn't match @samp{dirty rat}. + +@node Match-beginning-of-word Operator, Match-end-of-word Operator, Match-within-word Operator, Word Operators +@subsection The Match-beginning-of-word Operator (@code{\<}) + +@cindex @samp{\<} + +This operator (represented by @samp{\<}) matches the empty string at the +beginning of a word. + +@node Match-end-of-word Operator, Match-word-constituent Operator, Match-beginning-of-word Operator, Word Operators +@subsection The Match-end-of-word Operator (@code{\>}) + +@cindex @samp{\>} + +This operator (represented by @samp{\>}) matches the empty string at the +end of a word. + +@node Match-word-constituent Operator, Match-non-word-constituent Operator, Match-end-of-word Operator, Word Operators +@subsection The Match-word-constituent Operator (@code{\w}) + +@cindex @samp{\w} + +This operator (represented by @samp{\w}) matches any word-constituent +character. + +@node Match-non-word-constituent Operator, , Match-word-constituent Operator, Word Operators +@subsection The Match-non-word-constituent Operator (@code{\W}) + +@cindex @samp{\W} + +This operator (represented by @samp{\W}) matches any character that is +not word-constituent. + + +@node Buffer Operators, , Word Operators, GNU Operators +@section Buffer Operators + +Following are operators which work on buffers. In Emacs, a @dfn{buffer} +is, naturally, an Emacs buffer. For other programs, Regex considers the +entire string to be matched as the buffer. + +@menu +* Match-beginning-of-buffer Operator:: \` +* Match-end-of-buffer Operator:: \' +@end menu + + +@node Match-beginning-of-buffer Operator, Match-end-of-buffer Operator, , Buffer Operators +@subsection The Match-beginning-of-buffer Operator (@code{\`}) + +@cindex @samp{\`} + +This operator (represented by @samp{\`}) matches the empty string at the +beginning of the buffer. + +@node Match-end-of-buffer Operator, , Match-beginning-of-buffer Operator, Buffer Operators +@subsection The Match-end-of-buffer Operator (@code{\'}) + +@cindex @samp{\'} + +This operator (represented by @samp{\'}) matches the empty string at the +end of the buffer. + + +@node GNU Emacs Operators, What Gets Matched?, GNU Operators, Top +@chapter GNU Emacs Operators + +Following are operators that @sc{gnu} defines (and @sc{posix} doesn't) +that you can use only when Regex is compiled with the preprocessor +symbol @code{emacs} defined. + +@menu +* Syntactic Class Operators:: +@end menu + + +@node Syntactic Class Operators, , , GNU Emacs Operators +@section Syntactic Class Operators + +The operators in this section require Regex to recognize the syntactic +classes of characters. Regex uses a syntax table to determine this. + +@menu +* Emacs Syntax Tables:: +* Match-syntactic-class Operator:: \sCLASS +* Match-not-syntactic-class Operator:: \SCLASS +@end menu + +@node Emacs Syntax Tables, Match-syntactic-class Operator, , Syntactic Class Operators +@subsection Emacs Syntax Tables + +A @dfn{syntax table} is an array indexed by the characters in your +character set. In the @sc{ascii} encoding, therefore, a syntax table +has 256 elements. + +If Regex is compiled with the preprocessor symbol @code{emacs} defined, +then Regex expects you to define and initialize the variable +@code{re_syntax_table} to be an Emacs syntax table. Emacs' syntax +tables are more complicated than Regex's own (@pxref{Non-Emacs Syntax +Tables}). @xref{Syntax, , Syntax, emacs, The GNU Emacs User's Manual}, +for a description of Emacs' syntax tables. + +@node Match-syntactic-class Operator, Match-not-syntactic-class Operator, Emacs Syntax Tables, Syntactic Class Operators +@subsection The Match-syntactic-class Operator (@code{\s}@var{class}) + +@cindex @samp{\s} + +This operator matches any character whose syntactic class is represented +by a specified character. @samp{\s@var{class}} represents this operator +where @var{class} is the character representing the syntactic class you +want. For example, @samp{w} represents the syntactic +class of word-constituent characters, so @samp{\sw} matches any +word-constituent character. + +@node Match-not-syntactic-class Operator, , Match-syntactic-class Operator, Syntactic Class Operators +@subsection The Match-not-syntactic-class Operator (@code{\S}@var{class}) + +@cindex @samp{\S} + +This operator is similar to the match-syntactic-class operator except +that it matches any character whose syntactic class is @emph{not} +represented by the specified character. @samp{\S@var{class}} represents +this operator. For example, @samp{w} represents the syntactic class of +word-constituent characters, so @samp{\Sw} matches any character that is +not word-constituent. + + +@node What Gets Matched?, Programming with Regex, GNU Emacs Operators, Top +@chapter What Gets Matched? + +Regex usually matches strings according to the ``leftmost longest'' +rule; that is, it chooses the longest of the leftmost matches. This +does not mean that for a regular expression containing subexpressions +that it simply chooses the longest match for each subexpression, left to +right; the overall match must also be the longest possible one. + +For example, @samp{(ac*)(c*d[ac]*)\1} matches @samp{acdacaaa}, not +@samp{acdac}, as it would if it were to choose the longest match for the +first subexpression. + + +@node Programming with Regex, Copying, What Gets Matched?, Top +@chapter Programming with Regex + +Here we describe how you use the Regex data structures and functions in +C programs. Regex has three interfaces: one designed for @sc{gnu}, one +compatible with @sc{posix} and one compatible with Berkeley @sc{unix}. + +@menu +* GNU Regex Functions:: +* POSIX Regex Functions:: +* BSD Regex Functions:: +@end menu + + +@node GNU Regex Functions, POSIX Regex Functions, , Programming with Regex +@section GNU Regex Functions + +If you're writing code that doesn't need to be compatible with either +@sc{posix} or Berkeley @sc{unix}, you can use these functions. They +provide more options than the other interfaces. + +@menu +* GNU Pattern Buffers:: The re_pattern_buffer type. +* GNU Regular Expression Compiling:: re_compile_pattern () +* GNU Matching:: re_match () +* GNU Searching:: re_search () +* Matching/Searching with Split Data:: re_match_2 (), re_search_2 () +* Searching with Fastmaps:: re_compile_fastmap () +* GNU Translate Tables:: The `translate' field. +* Using Registers:: The re_registers type and related fns. +* Freeing GNU Pattern Buffers:: regfree () +@end menu + + +@node GNU Pattern Buffers, GNU Regular Expression Compiling, , GNU Regex Functions +@subsection GNU Pattern Buffers + +@cindex pattern buffer, definition of +@tindex re_pattern_buffer @r{definition} +@tindex struct re_pattern_buffer @r{definition} + +To compile, match, or search for a given regular expression, you must +supply a pattern buffer. A @dfn{pattern buffer} holds one compiled +regular expression.@footnote{Regular expressions are also referred to as +``patterns,'' hence the name ``pattern buffer.''} + +You can have several different pattern buffers simultaneously, each +holding a compiled pattern for a different regular expression. + +@file{regex.h} defines the pattern buffer @code{struct} as follows: + +@example +[[[ pattern_buffer ]]] +@end example + + +@node GNU Regular Expression Compiling, GNU Matching, GNU Pattern Buffers, GNU Regex Functions +@subsection GNU Regular Expression Compiling + +In @sc{gnu}, you can both match and search for a given regular +expression. To do either, you must first compile it in a pattern buffer +(@pxref{GNU Pattern Buffers}). + +@cindex syntax initialization +@vindex re_syntax_options @r{initialization} +Regular expressions match according to the syntax with which they were +compiled; with @sc{gnu}, you indicate what syntax you want by setting +the variable @code{re_syntax_options} (declared in @file{regex.h} and +defined in @file{regex.c}) before calling the compiling function, +@code{re_compile_pattern} (see below). @xref{Syntax Bits}, and +@ref{Predefined Syntaxes}. + +You can change the value of @code{re_syntax_options} at any time. +Usually, however, you set its value once and then never change it. + +@cindex pattern buffer initialization +@code{re_compile_pattern} takes a pattern buffer as an argument. You +must initialize the following fields: + +@table @code + +@item translate @r{initialization} + +@item translate +@vindex translate @r{initialization} +Initialize this to point to a translate table if you want one, or to +zero if you don't. We explain translate tables in @ref{GNU Translate +Tables}. + +@item fastmap +@vindex fastmap @r{initialization} +Initialize this to nonzero if you want a fastmap, or to zero if you +don't. + +@item buffer +@itemx allocated +@vindex buffer @r{initialization} +@vindex allocated @r{initialization} +@findex malloc +If you want @code{re_compile_pattern} to allocate memory for the +compiled pattern, set both of these to zero. If you have an existing +block of memory (allocated with @code{malloc}) you want Regex to use, +set @code{buffer} to its address and @code{allocated} to its size (in +bytes). + +@code{re_compile_pattern} uses @code{realloc} to extend the space for +the compiled pattern as necessary. + +@end table + +To compile a pattern buffer, use: + +@findex re_compile_pattern +@example +char * +re_compile_pattern (const char *@var{regex}, const int @var{regex_size}, + struct re_pattern_buffer *@var{pattern_buffer}) +@end example + +@noindent +@var{regex} is the regular expression's address, @var{regex_size} is its +length, and @var{pattern_buffer} is the pattern buffer's address. + +If @code{re_compile_pattern} successfully compiles the regular +expression, it returns zero and sets @code{*@var{pattern_buffer}} to the +compiled pattern. It sets the pattern buffer's fields as follows: + +@table @code +@item buffer +@vindex buffer @r{field, set by @code{re_compile_pattern}} +to the compiled pattern. + +@item used +@vindex used @r{field, set by @code{re_compile_pattern}} +to the number of bytes the compiled pattern in @code{buffer} occupies. + +@item syntax +@vindex syntax @r{field, set by @code{re_compile_pattern}} +to the current value of @code{re_syntax_options}. + +@item re_nsub +@vindex re_nsub @r{field, set by @code{re_compile_pattern}} +to the number of subexpressions in @var{regex}. + +@item fastmap_accurate +@vindex fastmap_accurate @r{field, set by @code{re_compile_pattern}} +to zero on the theory that the pattern you're compiling is different +than the one previously compiled into @code{buffer}; in that case (since +you can't make a fastmap without a compiled pattern), +@code{fastmap} would either contain an incompatible fastmap, or nothing +at all. + +@c xx what else? +@end table + +If @code{re_compile_pattern} can't compile @var{regex}, it returns an +error string corresponding to one of the errors listed in @ref{POSIX +Regular Expression Compiling}. + + +@node GNU Matching, GNU Searching, GNU Regular Expression Compiling, GNU Regex Functions +@subsection GNU Matching + +@cindex matching with GNU functions + +Matching the @sc{gnu} way means trying to match as much of a string as +possible starting at a position within it you specify. Once you've compiled +a pattern into a pattern buffer (@pxref{GNU Regular Expression +Compiling}), you can ask the matcher to match that pattern against a +string using: + +@findex re_match +@example +int +re_match (struct re_pattern_buffer *@var{pattern_buffer}, + const char *@var{string}, const int @var{size}, + const int @var{start}, struct re_registers *@var{regs}) +@end example + +@noindent +@var{pattern_buffer} is the address of a pattern buffer containing a +compiled pattern. @var{string} is the string you want to match; it can +contain newline and null characters. @var{size} is the length of that +string. @var{start} is the string index at which you want to +begin matching; the first character of @var{string} is at index zero. +@xref{Using Registers}, for a explanation of @var{regs}; you can safely +pass zero. + +@code{re_match} matches the regular expression in @var{pattern_buffer} +against the string @var{string} according to the syntax in +@var{pattern_buffers}'s @code{syntax} field. (@xref{GNU Regular +Expression Compiling}, for how to set it.) The function returns +@math{-1} if the compiled pattern does not match any part of +@var{string} and @math{-2} if an internal error happens; otherwise, it +returns how many (possibly zero) characters of @var{string} the pattern +matched. + +An example: suppose @var{pattern_buffer} points to a pattern buffer +containing the compiled pattern for @samp{a*}, and @var{string} points +to @samp{aaaaab} (whereupon @var{size} should be 6). Then if @var{start} +is 2, @code{re_match} returns 3, i.e., @samp{a*} would have matched the +last three @samp{a}s in @var{string}. If @var{start} is 0, +@code{re_match} returns 5, i.e., @samp{a*} would have matched all the +@samp{a}s in @var{string}. If @var{start} is either 5 or 6, it returns +zero. + +If @var{start} is not between zero and @var{size}, then +@code{re_match} returns @math{-1}. + + +@node GNU Searching, Matching/Searching with Split Data, GNU Matching, GNU Regex Functions +@subsection GNU Searching + +@cindex searching with GNU functions + +@dfn{Searching} means trying to match starting at successive positions +within a string. The function @code{re_search} does this. + +Before calling @code{re_search}, you must compile your regular +expression. @xref{GNU Regular Expression Compiling}. + +Here is the function declaration: + +@findex re_search +@example +int +re_search (struct re_pattern_buffer *@var{pattern_buffer}, + const char *@var{string}, const int @var{size}, + const int @var{start}, const int @var{range}, + struct re_registers *@var{regs}) +@end example + +@noindent +@vindex start @r{argument to @code{re_search}} +@vindex range @r{argument to @code{re_search}} +whose arguments are the same as those to @code{re_match} (@pxref{GNU +Matching}) except that the two arguments @var{start} and @var{range} +replace @code{re_match}'s argument @var{start}. + +If @var{range} is positive, then @code{re_search} attempts a match +starting first at index @var{start}, then at @math{@var{start} + 1} if +that fails, and so on, up to @math{@var{start} + @var{range}}; if +@var{range} is negative, then it attempts a match starting first at +index @var{start}, then at @math{@var{start} -1} if that fails, and so +on. + +If @var{start} is not between zero and @var{size}, then @code{re_search} +returns @math{-1}. When @var{range} is positive, @code{re_search} +adjusts @var{range} so that @math{@var{start} + @var{range} - 1} is +between zero and @var{size}, if necessary; that way it won't search +outside of @var{string}. Similarly, when @var{range} is negative, +@code{re_search} adjusts @var{range} so that @math{@var{start} + +@var{range} + 1} is between zero and @var{size}, if necessary. + +If the @code{fastmap} field of @var{pattern_buffer} is zero, +@code{re_search} matches starting at consecutive positions; otherwise, +it uses @code{fastmap} to make the search more efficient. +@xref{Searching with Fastmaps}. + +If no match is found, @code{re_search} returns @math{-1}. If +a match is found, it returns the index where the match began. If an +internal error happens, it returns @math{-2}. + + +@node Matching/Searching with Split Data, Searching with Fastmaps, GNU Searching, GNU Regex Functions +@subsection Matching and Searching with Split Data + +Using the functions @code{re_match_2} and @code{re_search_2}, you can +match or search in data that is divided into two strings. + +The function: + +@findex re_match_2 +@example +int +re_match_2 (struct re_pattern_buffer *@var{buffer}, + const char *@var{string1}, const int @var{size1}, + const char *@var{string2}, const int @var{size2}, + const int @var{start}, + struct re_registers *@var{regs}, + const int @var{stop}) +@end example + +@noindent +is similar to @code{re_match} (@pxref{GNU Matching}) except that you +pass @emph{two} data strings and sizes, and an index @var{stop} beyond +which you don't want the matcher to try matching. As with +@code{re_match}, if it succeeds, @code{re_match_2} returns how many +characters of @var{string} it matched. Regard @var{string1} and +@var{string2} as concatenated when you set the arguments @var{start} and +@var{stop} and use the contents of @var{regs}; @code{re_match_2} never +returns a value larger than @math{@var{size1} + @var{size2}}. + +The function: + +@findex re_search_2 +@example +int +re_search_2 (struct re_pattern_buffer *@var{buffer}, + const char *@var{string1}, const int @var{size1}, + const char *@var{string2}, const int @var{size2}, + const int @var{start}, const int @var{range}, + struct re_registers *@var{regs}, + const int @var{stop}) +@end example + +@noindent +is similarly related to @code{re_search}. + + +@node Searching with Fastmaps, GNU Translate Tables, Matching/Searching with Split Data, GNU Regex Functions +@subsection Searching with Fastmaps + +@cindex fastmaps +If you're searching through a long string, you should use a fastmap. +Without one, the searcher tries to match at consecutive positions in the +string. Generally, most of the characters in the string could not start +a match. It takes much longer to try matching at a given position in the +string than it does to check in a table whether or not the character at +that position could start a match. A @dfn{fastmap} is such a table. + +More specifically, a fastmap is an array indexed by the characters in +your character set. Under the @sc{ascii} encoding, therefore, a fastmap +has 256 elements. If you want the searcher to use a fastmap with a +given pattern buffer, you must allocate the array and assign the array's +address to the pattern buffer's @code{fastmap} field. You either can +compile the fastmap yourself or have @code{re_search} do it for you; +when @code{fastmap} is nonzero, it automatically compiles a fastmap the +first time you search using a particular compiled pattern. + +To compile a fastmap yourself, use: + +@findex re_compile_fastmap +@example +int +re_compile_fastmap (struct re_pattern_buffer *@var{pattern_buffer}) +@end example + +@noindent +@var{pattern_buffer} is the address of a pattern buffer. If the +character @var{c} could start a match for the pattern, +@code{re_compile_fastmap} makes +@code{@var{pattern_buffer}->fastmap[@var{c}]} nonzero. It returns +@math{0} if it can compile a fastmap and @math{-2} if there is an +internal error. For example, if @samp{|} is the alternation operator +and @var{pattern_buffer} holds the compiled pattern for @samp{a|b}, then +@code{re_compile_fastmap} sets @code{fastmap['a']} and +@code{fastmap['b']} (and no others). + +@code{re_search} uses a fastmap as it moves along in the string: it +checks the string's characters until it finds one that's in the fastmap. +Then it tries matching at that character. If the match fails, it +repeats the process. So, by using a fastmap, @code{re_search} doesn't +waste time trying to match at positions in the string that couldn't +start a match. + +If you don't want @code{re_search} to use a fastmap, +store zero in the @code{fastmap} field of the pattern buffer before +calling @code{re_search}. + +Once you've initialized a pattern buffer's @code{fastmap} field, you +need never do so again---even if you compile a new pattern in +it---provided the way the field is set still reflects whether or not you +want a fastmap. @code{re_search} will still either do nothing if +@code{fastmap} is null or, if it isn't, compile a new fastmap for the +new pattern. + +@node GNU Translate Tables, Using Registers, Searching with Fastmaps, GNU Regex Functions +@subsection GNU Translate Tables + +If you set the @code{translate} field of a pattern buffer to a translate +table, then the @sc{gnu} Regex functions to which you've passed that +pattern buffer use it to apply a simple transformation +to all the regular expression and string characters at which they look. + +A @dfn{translate table} is an array indexed by the characters in your +character set. Under the @sc{ascii} encoding, therefore, a translate +table has 256 elements. The array's elements are also characters in +your character set. When the Regex functions see a character @var{c}, +they use @code{translate[@var{c}]} in its place, with one exception: the +character after a @samp{\} is not translated. (This ensures that, the +operators, e.g., @samp{\B} and @samp{\b}, are always distinguishable.) + +For example, a table that maps all lowercase letters to the +corresponding uppercase ones would cause the matcher to ignore +differences in case.@footnote{A table that maps all uppercase letters to +the corresponding lowercase ones would work just as well for this +purpose.} Such a table would map all characters except lowercase letters +to themselves, and lowercase letters to the corresponding uppercase +ones. Under the @sc{ascii} encoding, here's how you could initialize +such a table (we'll call it @code{case_fold}): + +@example +for (i = 0; i < 256; i++) + case_fold[i] = i; +for (i = 'a'; i <= 'z'; i++) + case_fold[i] = i - ('a' - 'A'); +@end example + +You tell Regex to use a translate table on a given pattern buffer by +assigning that table's address to the @code{translate} field of that +buffer. If you don't want Regex to do any translation, put zero into +this field. You'll get weird results if you change the table's contents +anytime between compiling the pattern buffer, compiling its fastmap, and +matching or searching with the pattern buffer. + +@node Using Registers, Freeing GNU Pattern Buffers, GNU Translate Tables, GNU Regex Functions +@subsection Using Registers + +A group in a regular expression can match a (posssibly empty) substring +of the string that regular expression as a whole matched. The matcher +remembers the beginning and end of the substring matched by +each group. + +To find out what they matched, pass a nonzero @var{regs} argument to a +@sc{gnu} matching or searching function (@pxref{GNU Matching} and +@ref{GNU Searching}), i.e., the address of a structure of this type, as +defined in @file{regex.h}: + +@c We don't bother to include this directly from regex.h, +@c since it changes so rarely. +@example +@tindex re_registers +@vindex num_regs @r{in @code{struct re_registers}} +@vindex start @r{in @code{struct re_registers}} +@vindex end @r{in @code{struct re_registers}} +struct re_registers +@{ + unsigned num_regs; + regoff_t *start; + regoff_t *end; +@}; +@end example + +Except for (possibly) the @var{num_regs}'th element (see below), the +@var{i}th element of the @code{start} and @code{end} arrays records +information about the @var{i}th group in the pattern. (They're declared +as C pointers, but this is only because not all C compilers accept +zero-length arrays; conceptually, it is simplest to think of them as +arrays.) + +The @code{start} and @code{end} arrays are allocated in various ways, +depending on the value of the @code{regs_allocated} +@vindex regs_allocated +field in the pattern buffer passed to the matcher. + +The simplest and perhaps most useful is to let the matcher (re)allocate +enough space to record information for all the groups in the regular +expression. If @code{regs_allocated} is @code{REGS_UNALLOCATED}, +@vindex REGS_UNALLOCATED +the matcher allocates @math{1 + @var{re_nsub}} (another field in the +pattern buffer; @pxref{GNU Pattern Buffers}). The extra element is set +to @math{-1}, and sets @code{regs_allocated} to @code{REGS_REALLOCATE}. +@vindex REGS_REALLOCATE +Then on subsequent calls with the same pattern buffer and @var{regs} +arguments, the matcher reallocates more space if necessary. + +It would perhaps be more logical to make the @code{regs_allocated} field +part of the @code{re_registers} structure, instead of part of the +pattern buffer. But in that case the caller would be forced to +initialize the structure before passing it. Much existing code doesn't +do this initialization, and it's arguably better to avoid it anyway. + +@code{re_compile_pattern} sets @code{regs_allocated} to +@code{REGS_UNALLOCATED}, +so if you use the GNU regular expression +functions, you get this behavior by default. + +xx document re_set_registers + +@sc{posix}, on the other hand, requires a different interface: the +caller is supposed to pass in a fixed-length array which the matcher +fills. Therefore, if @code{regs_allocated} is @code{REGS_FIXED} +@vindex REGS_FIXED +the matcher simply fills that array. + +The following examples illustrate the information recorded in the +@code{re_registers} structure. (In all of them, @samp{(} represents the +open-group and @samp{)} the close-group operator. The first character +in the string @var{string} is at index 0.) + +@c xx i'm not sure this is all true anymore. + +@itemize @bullet + +@item +If the regular expression has an @w{@var{i}-th} +group not contained within another group that matches a +substring of @var{string}, then the function sets +@code{@w{@var{regs}->}start[@var{i}]} to the index in @var{string} where +the substring matched by the @w{@var{i}-th} group begins, and +@code{@w{@var{regs}->}end[@var{i}]} to the index just beyond that +substring's end. The function sets @code{@w{@var{regs}->}start[0]} and +@code{@w{@var{regs}->}end[0]} to analogous information about the entire +pattern. + +For example, when you match @samp{((a)(b))} against @samp{ab}, you get: + +@itemize +@item +0 in @code{@w{@var{regs}->}start[0]} and 2 in @code{@w{@var{regs}->}end[0]} + +@item +0 in @code{@w{@var{regs}->}start[1]} and 2 in @code{@w{@var{regs}->}end[1]} + +@item +0 in @code{@w{@var{regs}->}start[2]} and 1 in @code{@w{@var{regs}->}end[2]} + +@item +1 in @code{@w{@var{regs}->}start[3]} and 2 in @code{@w{@var{regs}->}end[3]} +@end itemize + +@item +If a group matches more than once (as it might if followed by, +e.g., a repetition operator), then the function reports the information +about what the group @emph{last} matched. + +For example, when you match the pattern @samp{(a)*} against the string +@samp{aa}, you get: + +@itemize +@item +0 in @code{@w{@var{regs}->}start[0]} and 2 in @code{@w{@var{regs}->}end[0]} + +@item +1 in @code{@w{@var{regs}->}start[1]} and 2 in @code{@w{@var{regs}->}end[1]} +@end itemize + +@item +If the @w{@var{i}-th} group does not participate in a +successful match, e.g., it is an alternative not taken or a +repetition operator allows zero repetitions of it, then the function +sets @code{@w{@var{regs}->}start[@var{i}]} and +@code{@w{@var{regs}->}end[@var{i}]} to @math{-1}. + +For example, when you match the pattern @samp{(a)*b} against +the string @samp{b}, you get: + +@itemize +@item +0 in @code{@w{@var{regs}->}start[0]} and 1 in @code{@w{@var{regs}->}end[0]} + +@item +@math{-1} in @code{@w{@var{regs}->}start[1]} and @math{-1} in @code{@w{@var{regs}->}end[1]} +@end itemize + +@item +If the @w{@var{i}-th} group matches a zero-length string, then the +function sets @code{@w{@var{regs}->}start[@var{i}]} and +@code{@w{@var{regs}->}end[@var{i}]} to the index just beyond that +zero-length string. + +For example, when you match the pattern @samp{(a*)b} against the string +@samp{b}, you get: + +@itemize +@item +0 in @code{@w{@var{regs}->}start[0]} and 1 in @code{@w{@var{regs}->}end[0]} + +@item +0 in @code{@w{@var{regs}->}start[1]} and 0 in @code{@w{@var{regs}->}end[1]} +@end itemize + +@ignore +The function sets @code{@w{@var{regs}->}start[0]} and +@code{@w{@var{regs}->}end[0]} to analogous information about the entire +pattern. + +For example, when you match the pattern @samp{(a*)} against the empty +string, you get: + +@itemize +@item +0 in @code{@w{@var{regs}->}start[0]} and 0 in @code{@w{@var{regs}->}end[0]} + +@item +0 in @code{@w{@var{regs}->}start[1]} and 0 in @code{@w{@var{regs}->}end[1]} +@end itemize +@end ignore + +@item +If an @w{@var{i}-th} group contains a @w{@var{j}-th} group +in turn not contained within any other group within group @var{i} and +the function reports a match of the @w{@var{i}-th} group, then it +records in @code{@w{@var{regs}->}start[@var{j}]} and +@code{@w{@var{regs}->}end[@var{j}]} the last match (if it matched) of +the @w{@var{j}-th} group. + +For example, when you match the pattern @samp{((a*)b)*} against the +string @samp{abb}, @w{group 2} last matches the empty string, so you +get what it previously matched: + +@itemize +@item +0 in @code{@w{@var{regs}->}start[0]} and 3 in @code{@w{@var{regs}->}end[0]} + +@item +2 in @code{@w{@var{regs}->}start[1]} and 3 in @code{@w{@var{regs}->}end[1]} + +@item +2 in @code{@w{@var{regs}->}start[2]} and 2 in @code{@w{@var{regs}->}end[2]} +@end itemize + +When you match the pattern @samp{((a)*b)*} against the string +@samp{abb}, @w{group 2} doesn't participate in the last match, so you +get: + +@itemize +@item +0 in @code{@w{@var{regs}->}start[0]} and 3 in @code{@w{@var{regs}->}end[0]} + +@item +2 in @code{@w{@var{regs}->}start[1]} and 3 in @code{@w{@var{regs}->}end[1]} + +@item +0 in @code{@w{@var{regs}->}start[2]} and 1 in @code{@w{@var{regs}->}end[2]} +@end itemize + +@item +If an @w{@var{i}-th} group contains a @w{@var{j}-th} group +in turn not contained within any other group within group @var{i} +and the function sets +@code{@w{@var{regs}->}start[@var{i}]} and +@code{@w{@var{regs}->}end[@var{i}]} to @math{-1}, then it also sets +@code{@w{@var{regs}->}start[@var{j}]} and +@code{@w{@var{regs}->}end[@var{j}]} to @math{-1}. + +For example, when you match the pattern @samp{((a)*b)*c} against the +string @samp{c}, you get: + +@itemize +@item +0 in @code{@w{@var{regs}->}start[0]} and 1 in @code{@w{@var{regs}->}end[0]} + +@item +@math{-1} in @code{@w{@var{regs}->}start[1]} and @math{-1} in @code{@w{@var{regs}->}end[1]} + +@item +@math{-1} in @code{@w{@var{regs}->}start[2]} and @math{-1} in @code{@w{@var{regs}->}end[2]} +@end itemize + +@end itemize + +@node Freeing GNU Pattern Buffers, , Using Registers, GNU Regex Functions +@subsection Freeing GNU Pattern Buffers + +To free any allocated fields of a pattern buffer, you can use the +@sc{posix} function described in @ref{Freeing POSIX Pattern Buffers}, +since the type @code{regex_t}---the type for @sc{posix} pattern +buffers---is equivalent to the type @code{re_pattern_buffer}. After +freeing a pattern buffer, you need to again compile a regular expression +in it (@pxref{GNU Regular Expression Compiling}) before passing it to +a matching or searching function. + + +@node POSIX Regex Functions, BSD Regex Functions, GNU Regex Functions, Programming with Regex +@section POSIX Regex Functions + +If you're writing code that has to be @sc{posix} compatible, you'll need +to use these functions. Their interfaces are as specified by @sc{posix}, +draft 1003.2/D11.2. + +@menu +* POSIX Pattern Buffers:: The regex_t type. +* POSIX Regular Expression Compiling:: regcomp () +* POSIX Matching:: regexec () +* Reporting Errors:: regerror () +* Using Byte Offsets:: The regmatch_t type. +* Freeing POSIX Pattern Buffers:: regfree () +@end menu + + +@node POSIX Pattern Buffers, POSIX Regular Expression Compiling, , POSIX Regex Functions +@subsection POSIX Pattern Buffers + +To compile or match a given regular expression the @sc{posix} way, you +must supply a pattern buffer exactly the way you do for @sc{gnu} +(@pxref{GNU Pattern Buffers}). @sc{posix} pattern buffers have type +@code{regex_t}, which is equivalent to the @sc{gnu} pattern buffer +type @code{re_pattern_buffer}. + + +@node POSIX Regular Expression Compiling, POSIX Matching, POSIX Pattern Buffers, POSIX Regex Functions +@subsection POSIX Regular Expression Compiling + +With @sc{posix}, you can only search for a given regular expression; you +can't match it. To do this, you must first compile it in a +pattern buffer, using @code{regcomp}. + +@ignore +Before calling @code{regcomp}, you must initialize this pattern buffer +as you do for @sc{gnu} (@pxref{GNU Regular Expression Compiling}). See +below, however, for how to choose a syntax with which to compile. +@end ignore + +To compile a pattern buffer, use: + +@findex regcomp +@example +int +regcomp (regex_t *@var{preg}, const char *@var{regex}, int @var{cflags}) +@end example + +@noindent +@var{preg} is the initialized pattern buffer's address, @var{regex} is +the regular expression's address, and @var{cflags} is the compilation +flags, which Regex considers as a collection of bits. Here are the +valid bits, as defined in @file{regex.h}: + +@table @code + +@item REG_EXTENDED +@vindex REG_EXTENDED +says to use @sc{posix} Extended Regular Expression syntax; if this isn't +set, then says to use @sc{posix} Basic Regular Expression syntax. +@code{regcomp} sets @var{preg}'s @code{syntax} field accordingly. + +@item REG_ICASE +@vindex REG_ICASE +@cindex ignoring case +says to ignore case; @code{regcomp} sets @var{preg}'s @code{translate} +field to a translate table which ignores case, replacing anything you've +put there before. + +@item REG_NOSUB +@vindex REG_NOSUB +says to set @var{preg}'s @code{no_sub} field; @pxref{POSIX Matching}, +for what this means. + +@item REG_NEWLINE +@vindex REG_NEWLINE +says that a: + +@itemize @bullet + +@item +match-any-character operator (@pxref{Match-any-character +Operator}) doesn't match a newline. + +@item +nonmatching list not containing a newline (@pxref{List +Operators}) matches a newline. + +@item +match-beginning-of-line operator (@pxref{Match-beginning-of-line +Operator}) matches the empty string immediately after a newline, +regardless of how @code{REG_NOTBOL} is set (@pxref{POSIX Matching}, for +an explanation of @code{REG_NOTBOL}). + +@item +match-end-of-line operator (@pxref{Match-beginning-of-line +Operator}) matches the empty string immediately before a newline, +regardless of how @code{REG_NOTEOL} is set (@pxref{POSIX Matching}, +for an explanation of @code{REG_NOTEOL}). + +@end itemize + +@end table + +If @code{regcomp} successfully compiles the regular expression, it +returns zero and sets @code{*@var{pattern_buffer}} to the compiled +pattern. Except for @code{syntax} (which it sets as explained above), it +also sets the same fields the same way as does the @sc{gnu} compiling +function (@pxref{GNU Regular Expression Compiling}). + +If @code{regcomp} can't compile the regular expression, it returns one +of the error codes listed here. (Except when noted differently, the +syntax of in all examples below is basic regular expression syntax.) + +@table @code + +@comment repetitions +@item REG_BADRPT +For example, the consecutive repetition operators @samp{**} in +@samp{a**} are invalid. As another example, if the syntax is extended +regular expression syntax, then the repetition operator @samp{*} with +nothing on which to operate in @samp{*} is invalid. + +@item REG_BADBR +For example, the @var{count} @samp{-1} in @samp{a\@{-1} is invalid. + +@item REG_EBRACE +For example, @samp{a\@{1} is missing a close-interval operator. + +@comment lists +@item REG_EBRACK +For example, @samp{[a} is missing a close-list operator. + +@item REG_ERANGE +For example, the range ending point @samp{z} that collates lower than +does its starting point @samp{a} in @samp{[z-a]} is invalid. Also, the +range with the character class @samp{[:alpha:]} as its starting point in +@samp{[[:alpha:]-|]}. + +@item REG_ECTYPE +For example, the character class name @samp{foo} in @samp{[[:foo:]} is +invalid. + +@comment groups +@item REG_EPAREN +For example, @samp{a\)} is missing an open-group operator and @samp{\(a} +is missing a close-group operator. + +@item REG_ESUBREG +For example, the back reference @samp{\2} that refers to a nonexistent +subexpression in @samp{\(a\)\2} is invalid. + +@comment unfinished business + +@item REG_EEND +Returned when a regular expression causes no other more specific error. + +@item REG_EESCAPE +For example, the trailing backslash @samp{\} in @samp{a\} is invalid, as is the +one in @samp{\}. + +@comment kitchen sink +@item REG_BADPAT +For example, in the extended regular expression syntax, the empty group +@samp{()} in @samp{a()b} is invalid. + +@comment internal +@item REG_ESIZE +Returned when a regular expression needs a pattern buffer larger than +65536 bytes. + +@item REG_ESPACE +Returned when a regular expression makes Regex to run out of memory. + +@end table + + +@node POSIX Matching, Reporting Errors, POSIX Regular Expression Compiling, POSIX Regex Functions +@subsection POSIX Matching + +Matching the @sc{posix} way means trying to match a null-terminated +string starting at its first character. Once you've compiled a pattern +into a pattern buffer (@pxref{POSIX Regular Expression Compiling}), you +can ask the matcher to match that pattern against a string using: + +@findex regexec +@example +int +regexec (const regex_t *@var{preg}, const char *@var{string}, + size_t @var{nmatch}, regmatch_t @var{pmatch}[], int @var{eflags}) +@end example + +@noindent +@var{preg} is the address of a pattern buffer for a compiled pattern. +@var{string} is the string you want to match. + +@xref{Using Byte Offsets}, for an explanation of @var{pmatch}. If you +pass zero for @var{nmatch} or you compiled @var{preg} with the +compilation flag @code{REG_NOSUB} set, then @code{regexec} will ignore +@var{pmatch}; otherwise, you must allocate it to have at least +@var{nmatch} elements. @code{regexec} will record @var{nmatch} byte +offsets in @var{pmatch}, and set to @math{-1} any unused elements up to +@math{@var{pmatch}@code{[@var{nmatch}]} - 1}. + +@var{eflags} specifies @dfn{execution flags}---namely, the two bits +@code{REG_NOTBOL} and @code{REG_NOTEOL} (defined in @file{regex.h}). If +you set @code{REG_NOTBOL}, then the match-beginning-of-line operator +(@pxref{Match-beginning-of-line Operator}) always fails to match. +This lets you match against pieces of a line, as you would need to if, +say, searching for repeated instances of a given pattern in a line; it +would work correctly for patterns both with and without +match-beginning-of-line operators. @code{REG_NOTEOL} works analogously +for the match-end-of-line operator (@pxref{Match-end-of-line +Operator}); it exists for symmetry. + +@code{regexec} tries to find a match for @var{preg} in @var{string} +according to the syntax in @var{preg}'s @code{syntax} field. +(@xref{POSIX Regular Expression Compiling}, for how to set it.) The +function returns zero if the compiled pattern matches @var{string} and +@code{REG_NOMATCH} (defined in @file{regex.h}) if it doesn't. + +@node Reporting Errors, Using Byte Offsets, POSIX Matching, POSIX Regex Functions +@subsection Reporting Errors + +If either @code{regcomp} or @code{regexec} fail, they return a nonzero +error code, the possibilities for which are defined in @file{regex.h}. +@xref{POSIX Regular Expression Compiling}, and @ref{POSIX Matching}, for +what these codes mean. To get an error string corresponding to these +codes, you can use: + +@findex regerror +@example +size_t +regerror (int @var{errcode}, + const regex_t *@var{preg}, + char *@var{errbuf}, + size_t @var{errbuf_size}) +@end example + +@noindent +@var{errcode} is an error code, @var{preg} is the address of the pattern +buffer which provoked the error, @var{errbuf} is the error buffer, and +@var{errbuf_size} is @var{errbuf}'s size. + +@code{regerror} returns the size in bytes of the error string +corresponding to @var{errcode} (including its terminating null). If +@var{errbuf} and @var{errbuf_size} are nonzero, it also returns in +@var{errbuf} the first @math{@var{errbuf_size} - 1} characters of the +error string, followed by a null. +@var{errbuf_size} must be a nonnegative number less than or equal to the +size in bytes of @var{errbuf}. + +You can call @code{regerror} with a null @var{errbuf} and a zero +@var{errbuf_size} to determine how large @var{errbuf} need be to +accommodate @code{regerror}'s error string. + +@node Using Byte Offsets, Freeing POSIX Pattern Buffers, Reporting Errors, POSIX Regex Functions +@subsection Using Byte Offsets + +In @sc{posix}, variables of type @code{regmatch_t} hold analogous +information, but are not identical to, @sc{gnu}'s registers (@pxref{Using +Registers}). To get information about registers in @sc{posix}, pass to +@code{regexec} a nonzero @var{pmatch} of type @code{regmatch_t}, i.e., +the address of a structure of this type, defined in +@file{regex.h}: + +@tindex regmatch_t +@example +typedef struct +@{ + regoff_t rm_so; + regoff_t rm_eo; +@} regmatch_t; +@end example + +When reading in @ref{Using Registers}, about how the matching function +stores the information into the registers, substitute @var{pmatch} for +@var{regs}, @code{@w{@var{pmatch}[@var{i}]->}rm_so} for +@code{@w{@var{regs}->}start[@var{i}]} and +@code{@w{@var{pmatch}[@var{i}]->}rm_eo} for +@code{@w{@var{regs}->}end[@var{i}]}. + +@node Freeing POSIX Pattern Buffers, , Using Byte Offsets, POSIX Regex Functions +@subsection Freeing POSIX Pattern Buffers + +To free any allocated fields of a pattern buffer, use: + +@findex regfree +@example +void +regfree (regex_t *@var{preg}) +@end example + +@noindent +@var{preg} is the pattern buffer whose allocated fields you want freed. +@code{regfree} also sets @var{preg}'s @code{allocated} and @code{used} +fields to zero. After freeing a pattern buffer, you need to again +compile a regular expression in it (@pxref{POSIX Regular Expression +Compiling}) before passing it to the matching function (@pxref{POSIX +Matching}). + + +@node BSD Regex Functions, , POSIX Regex Functions, Programming with Regex +@section BSD Regex Functions + +If you're writing code that has to be Berkeley @sc{unix} compatible, +you'll need to use these functions whose interfaces are the same as those +in Berkeley @sc{unix}. + +@menu +* BSD Regular Expression Compiling:: re_comp () +* BSD Searching:: re_exec () +@end menu + +@node BSD Regular Expression Compiling, BSD Searching, , BSD Regex Functions +@subsection BSD Regular Expression Compiling + +With Berkeley @sc{unix}, you can only search for a given regular +expression; you can't match one. To search for it, you must first +compile it. Before you compile it, you must indicate the regular +expression syntax you want it compiled according to by setting the +variable @code{re_syntax_options} (declared in @file{regex.h} to some +syntax (@pxref{Regular Expression Syntax}). + +To compile a regular expression use: + +@findex re_comp +@example +char * +re_comp (char *@var{regex}) +@end example + +@noindent +@var{regex} is the address of a null-terminated regular expression. +@code{re_comp} uses an internal pattern buffer, so you can use only the +most recently compiled pattern buffer. This means that if you want to +use a given regular expression that you've already compiled---but it +isn't the latest one you've compiled---you'll have to recompile it. If +you call @code{re_comp} with the null string (@emph{not} the empty +string) as the argument, it doesn't change the contents of the pattern +buffer. + +If @code{re_comp} successfully compiles the regular expression, it +returns zero. If it can't compile the regular expression, it returns +an error string. @code{re_comp}'s error messages are identical to those +of @code{re_compile_pattern} (@pxref{GNU Regular Expression +Compiling}). + +@node BSD Searching, , BSD Regular Expression Compiling, BSD Regex Functions +@subsection BSD Searching + +Searching the Berkeley @sc{unix} way means searching in a string +starting at its first character and trying successive positions within +it to find a match. Once you've compiled a pattern using @code{re_comp} +(@pxref{BSD Regular Expression Compiling}), you can ask Regex +to search for that pattern in a string using: + +@findex re_exec +@example +int +re_exec (char *@var{string}) +@end example + +@noindent +@var{string} is the address of the null-terminated string in which you +want to search. + +@code{re_exec} returns either 1 for success or 0 for failure. It +automatically uses a @sc{gnu} fastmap (@pxref{Searching with Fastmaps}). + + +@node Copying, Index, Programming with Regex, Top +@appendix GNU GENERAL PUBLIC LICENSE +@center Version 2, June 1991 + +@display +Copyright @copyright{} 1989, 1991 Free Software Foundation, Inc. +675 Mass Ave, Cambridge, MA 02139, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@unnumberedsec Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software---to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + +@iftex +@unnumberedsec TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +@end iftex +@ifinfo +@center TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +@end ifinfo + +@enumerate +@item +This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The ``Program'', below, +refers to any such program or work, and a ``work based on the Program'' +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term ``modification''.) Each licensee is addressed as ``you''. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + +@item +You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +@item +You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +@enumerate a +@item +You must cause the modified files to carry prominent notices +stating that you changed the files and the date of any change. + +@item +You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any +part thereof, to be licensed as a whole at no charge to all third +parties under the terms of this License. + +@item +If the modified program normally reads commands interactively +when run, you must cause it, when started running for such +interactive use in the most ordinary way, to print or display an +announcement including an appropriate copyright notice and a +notice that there is no warranty (or else, saying that you provide +a warranty) and that users may redistribute the program under +these conditions, and telling the user how to view a copy of this +License. (Exception: if the Program itself is interactive but +does not normally print such an announcement, your work based on +the Program is not required to print an announcement.) +@end enumerate + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +@item +You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + +@enumerate a +@item +Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections +1 and 2 above on a medium customarily used for software interchange; or, + +@item +Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your +cost of physically performing source distribution, a complete +machine-readable copy of the corresponding source code, to be +distributed under the terms of Sections 1 and 2 above on a medium +customarily used for software interchange; or, + +@item +Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is +allowed only for noncommercial distribution and only if you +received the program in object code or executable form with such +an offer, in accord with Subsection b above.) +@end enumerate + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +@item +You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +@item +You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +@item +Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +@item +If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +@item +If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +@item +The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and ``any +later version'', you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +@item +If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +@iftex +@heading NO WARRANTY +@end iftex +@ifinfo +@center NO WARRANTY +@end ifinfo + +@item +BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM ``AS IS'' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + +@item +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. +@end enumerate + +@iftex +@heading END OF TERMS AND CONDITIONS +@end iftex +@ifinfo +@center END OF TERMS AND CONDITIONS +@end ifinfo + +@page +@unnumberedsec Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the ``copyright'' line and a pointer to where the full notice is found. + +@smallexample +@var{one line to give the program's name and a brief idea of what it does.} +Copyright (C) 19@var{yy} @var{name of author} + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +@end smallexample + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + +@smallexample +Gnomovision version 69, Copyright (C) 19@var{yy} @var{name of author} +Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. +This is free software, and you are welcome to redistribute it +under certain conditions; type `show c' for details. +@end smallexample + +The hypothetical commands @samp{show w} and @samp{show c} should show +the appropriate parts of the General Public License. Of course, the +commands you use may be called something other than @samp{show w} and +@samp{show c}; they could even be mouse-clicks or menu items---whatever +suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a ``copyright disclaimer'' for the program, if +necessary. Here is a sample; alter the names: + +@example +Yoyodyne, Inc., hereby disclaims all copyright interest in the program +`Gnomovision' (which makes passes at compilers) written by James Hacker. + +@var{signature of Ty Coon}, 1 April 1989 +Ty Coon, President of Vice +@end example + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + + +@node Index, , Copying, Top +@unnumbered Index + +@printindex cp + +@contents + +@bye diff --git a/bin/ed/regex.c b/gnu/lib/libregex/regex.c similarity index 100% rename from bin/ed/regex.c rename to gnu/lib/libregex/regex.c diff --git a/bin/ed/regex.h b/gnu/lib/libregex/regex.h similarity index 100% rename from bin/ed/regex.h rename to gnu/lib/libregex/regex.h diff --git a/gnu/lib/libregex/test/ChangeLog b/gnu/lib/libregex/test/ChangeLog new file mode 100644 index 0000000000..f0265bb26a --- /dev/null +++ b/gnu/lib/libregex/test/ChangeLog @@ -0,0 +1,77 @@ +Thu Mar 25 21:23:43 1993 Jim Blandy (jimb@totoro.cs.oberlin.edu) + + * debugmalloc.c: #include , and remove declaration of + memcpy. + +Sun Dec 13 20:59:32 1992 Jim Blandy (jimb@totoro.cs.oberlin.edu) + + * tregress.c (test_regress): Add regression test for matching + "[a-a]" against "a" with the upcase translation map. + + * iregex.c (print_regs): Don't print a newline after the register + contents. + (main): Instead, write out newlines here after printing match and + search results; this way, we get a newline whether or not the + pattern matched. + +Fri Dec 11 03:30:50 1992 Jim Blandy (jimb@totoro.cs.oberlin.edu) + + * tregress.c (test_regress): Add new test to catch bug fixed by + change to regex.c today. + + * Makefile.in (dregex.o): Depend on `../regex.[ch]', not `regex.[ch]'. + +Sun Nov 15 07:51:40 1992 Karl Berry (karl@cs.umb.edu) + + * debugmalloc.c (memcpy): Declare; also, include . + + * psx-interf.c (fill_pmatch): Declare offsets as `regoff_t' + instead of `off_t'. + +Thu Nov 12 11:29:58 1992 Karl Berry (karl@cs.umb.edu) + + * iregex.c (main): Remove unused variable `c'; initialize + the char array in C code; only call print_regs if the match and + search succeeded. + (strlen): Declare. + + * tregress.c (test_regress): Bug from enami. + +Tue Nov 10 10:36:53 1992 Karl Berry (karl@cs.umb.edu) + + * tregress.c (test_regress): Remove Emacs 19 diff bug from rms, as + it was never the right thing to test anyway, and the test itself + had bugs in it. + +Mon Nov 9 10:09:40 1992 Karl Berry (karl@cs.umb.edu) + + * tregress.c (test_regress): Bug from meyering. + +Thu Sep 24 10:48:34 1992 Karl Berry (karl@cs.umb.edu) + + * Makefile.in: avoid $< (except in implicit rule). + +Sat Sep 19 15:38:29 1992 Karl Berry (karl@hayley) + + * Makefile.in (TAGS): include regex.c and regex.h. + +Wed Sep 16 09:29:27 1992 Karl Berry (karl@hayley) + + * xmalloc.c (xmalloc): use char *, not void *, as some compilers + bomb out on the latter. + + * Makefile.in (LOADLIBES): use LIBS instead, as that what's + Autoconf wants to define. + + * other.c: remove tests for ^/$ around newlines. + +Tue Sep 15 11:01:15 1992 Karl Berry (karl@hayley) + + * fileregex.c (main): call re_search_2 instead of re_search. + + * Makefile.in (regex.o): make target dregex.o, so VPATH doesn't + find ../regex.o. + +Sun Sep 13 06:50:03 1992 Karl Berry (karl@hayley) + + * Created. diff --git a/gnu/lib/libregex/test/Makefile b/gnu/lib/libregex/test/Makefile new file mode 100644 index 0000000000..5a8656a76e --- /dev/null +++ b/gnu/lib/libregex/test/Makefile @@ -0,0 +1,169 @@ +# Generated automatically from Makefile.in by configure. +# Makefile for regex testing. +# +# Copyright (C) 1992 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +CPPFLAGS = +CFLAGS = -g +LDFLAGS = + +srcdir = . +VPATH = .:../. + +CC = gcc +DEFS = -DHAVE_STRING_H=1 +LIBS = $(LOADLIBES) + +ETAGS = etags +SHELL = /bin/sh + +debug = -DDEBUG +ALL_CPPFLAGS = -I. -I$(srcdir) -I../$(srcdir) $(DEFS) $(CPPFLAGS) $(debug) + +.c.o: + $(CC) $(ALL_CPPFLAGS) $(CFLAGS) -c $< + + +# Define this as `../regex.o' to get the optimized version. +regex_o = dregex.o +test_h = test.h +test_o = test.o bsd-interf.o other.o tregress.o psx-basic.o psx-extend.o \ + psx-generic.o psx-group.o psx-interf.o psx-interv.o +common_o = printchar.o upcase.o xmalloc.o $(malloc) + +# We have a lot of mallocs we can try when we run afoul of strange bugs. +malloc = +#malloc = # the libc malloc +#malloc = g++malloc.o +#malloc = debugmalloc.o +#malloc = emacsmalloc.o +emacsmallocflags = -Drcheck -Dbotch=abort -DUSG + +# default is to do nothing. +default: + +all: regex syntax + +regex: $(regex_o) $(common_o) $(test_o) main.o + $(CC) -o $@ $(LDFLAGS) $^ $(LIBS) + +# As long as we're doing tests, we enable debugging. +dregex.o: ../regex.c ../regex.h + rm -f $@ + $(CC) $(ALL_CPPFLAGS) $(CFLAGS) -c ../$(srcdir)/regex.c + mv regex.o $@ + +# iregex is the interactive regex. +iregex: $(common_o) $(regex_o) iregex.o + $(CC) -o $@ $(LDFLAGS) $^ $(LIBS) + +# fileregex searches for an r.e. in every line of a given file. +fileregex_o = fileregex.o printchar.o $(regex_o) +fileregex: $(fileregex_o) + $(CC) -o $@ $(LDFLAGS) $(fileregex_o) $(LIBS) + +# cppregex is regex with a preprocessed regex.c. Useful when the +# problem is inside some macro. +cppregex: regexcpp.o $(common_o) $(test_o) main.o + $(CC) -o $@ $(LDFLAGS) $^ $(LIBS) + +regexcpp.o: regexcpp.c + +regexcpp.c: regex.c regexcpp.sed + rm -f regexcpp.c + $(CC) -E $(ALL_CPPFLAGS) ../$(srcdir)/regex.c \ + | egrep -v '^#|^ *$$' \ + | sed -f regexcpp.sed \ + > regexcpp.c + chmod a-w regexcpp.c + +# Have to give this malloc special flags. +emacsmalloc.o: emacsmalloc.c + $(CC) -c $(CFLAGS) $(ALL_CPPFLAGS) $(emacsmallocflags) \ + ../$(srcdir)/test/emacsmalloc.c + +syntax: syntax.o + $(CC) $(CFLAGS) -o $@ syntax.o + +syntax.c: syntax.skel bits + sed '/\[\[\[replace.*\]\]\]/r bits' syntax.skel > $@ + +bits: regex.h + sed -n 1,/RE_SYNTAX_EMACS/p ../$(srcdir)/regex.h \ + | grep "#define RE_.*1" \ + | sed 's/^#define \(RE_[A-Z_]*\) .*/ TEST_BIT (\1);/' > $@ + +check: regex + ./regex + +TAGS: regex.c regex.h *.h *.c + $(ETAGS) -t $^ + +depend: + gcc -MM $(ALL_CPPFLAGS) *.c > /tmp/depend +.PHONY: depend + +install: +.PHONY: install + +clean mostlyclean: + rm -f *.o regex cppregex iregex fileregex regexcpp.c syntax + +distclean: clean + rm -f bits syntax.c Makefile + +extraclean: distclean + rm -f *~* *\#* patch* *.orig *.rej *.bak core a.out + +realclean: distclean + rm -f TAGS + +Makefile: Makefile.in ../config.status + (cd ..; sh config.status) + +# Prevent GNU make 3 from overflowing arg limit on system V. +.NOEXPORT: + +# Assumes $(distdir) is the place to put our files. +distfiles = ChangeLog TAGS *.in *.c *.h regexcpp.sed syntax.skel +dist: Makefile TAGS + mkdir $(distdir) + ln $(distfiles) $(distdir) + +# Automatically-generated dependencies below here. +alloca.o : alloca.c +bsd-interf.o : bsd-interf.c +debugmalloc.o : debugmalloc.c +emacsmalloc.o : emacsmalloc.c getpagesize.h +fileregex.o : fileregex.c .././regex.h +g++malloc.o : g++malloc.c //usr/include/stdio.h getpagesize.h +iregex.o : iregex.c .././regex.h +main.o : main.c test.h .././regex.h +malloc-test.o : malloc-test.c +other.o : other.c test.h .././regex.h +printchar.o : printchar.c +psx-basic.o : psx-basic.c test.h .././regex.h +psx-extend.o : psx-extend.c test.h .././regex.h +psx-generic.o : psx-generic.c test.h .././regex.h +psx-group.o : psx-group.c test.h .././regex.h +psx-interf.o : psx-interf.c test.h .././regex.h +psx-interv.o : psx-interv.c test.h .././regex.h +syntax.o : syntax.c .././regex.h +test.o : test.c test.h .././regex.h +tregress.o : tregress.c test.h .././regex.h +upcase.o : upcase.c +xmalloc.o : xmalloc.c diff --git a/gnu/lib/libregex/test/Makefile.in b/gnu/lib/libregex/test/Makefile.in new file mode 100644 index 0000000000..b6a413384f --- /dev/null +++ b/gnu/lib/libregex/test/Makefile.in @@ -0,0 +1,168 @@ +# Makefile for regex testing. +# +# Copyright (C) 1992 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +CPPFLAGS = +CFLAGS = -g +LDFLAGS = + +srcdir = @srcdir@ +VPATH = @srcdir@:../@srcdir@ + +CC = @CC@ +DEFS = @DEFS@ +LIBS = @LIBS@ $(LOADLIBES) + +ETAGS = etags +SHELL = /bin/sh + +debug = -DDEBUG +ALL_CPPFLAGS = -I. -I$(srcdir) -I../$(srcdir) $(DEFS) $(CPPFLAGS) $(debug) + +.c.o: + $(CC) $(ALL_CPPFLAGS) $(CFLAGS) -c $< + + +# Define this as `../regex.o' to get the optimized version. +regex_o = dregex.o +test_h = test.h +test_o = test.o bsd-interf.o other.o tregress.o psx-basic.o psx-extend.o \ + psx-generic.o psx-group.o psx-interf.o psx-interv.o +common_o = printchar.o upcase.o xmalloc.o $(malloc) + +# We have a lot of mallocs we can try when we run afoul of strange bugs. +malloc = @ALLOCA@ +#malloc = # the libc malloc +#malloc = g++malloc.o +#malloc = debugmalloc.o +#malloc = emacsmalloc.o +emacsmallocflags = -Drcheck -Dbotch=abort -DUSG + +# default is to do nothing. +default: + +all: regex syntax + +regex: $(regex_o) $(common_o) $(test_o) main.o + $(CC) -o $@ $(LDFLAGS) $^ $(LIBS) + +# As long as we're doing tests, we enable debugging. +dregex.o: ../regex.c ../regex.h + rm -f $@ + $(CC) $(ALL_CPPFLAGS) $(CFLAGS) -c ../$(srcdir)/regex.c + mv regex.o $@ + +# iregex is the interactive regex. +iregex: $(common_o) $(regex_o) iregex.o + $(CC) -o $@ $(LDFLAGS) $^ $(LIBS) + +# fileregex searches for an r.e. in every line of a given file. +fileregex_o = fileregex.o printchar.o $(regex_o) +fileregex: $(fileregex_o) + $(CC) -o $@ $(LDFLAGS) $(fileregex_o) $(LIBS) + +# cppregex is regex with a preprocessed regex.c. Useful when the +# problem is inside some macro. +cppregex: regexcpp.o $(common_o) $(test_o) main.o + $(CC) -o $@ $(LDFLAGS) $^ $(LIBS) + +regexcpp.o: regexcpp.c + +regexcpp.c: regex.c regexcpp.sed + rm -f regexcpp.c + $(CC) -E $(ALL_CPPFLAGS) ../$(srcdir)/regex.c \ + | egrep -v '^#|^ *$$' \ + | sed -f regexcpp.sed \ + > regexcpp.c + chmod a-w regexcpp.c + +# Have to give this malloc special flags. +emacsmalloc.o: emacsmalloc.c + $(CC) -c $(CFLAGS) $(ALL_CPPFLAGS) $(emacsmallocflags) \ + ../$(srcdir)/test/emacsmalloc.c + +syntax: syntax.o + $(CC) $(CFLAGS) -o $@ syntax.o + +syntax.c: syntax.skel bits + sed '/\[\[\[replace.*\]\]\]/r bits' syntax.skel > $@ + +bits: regex.h + sed -n 1,/RE_SYNTAX_EMACS/p ../$(srcdir)/regex.h \ + | grep "#define RE_.*1" \ + | sed 's/^#define \(RE_[A-Z_]*\) .*/ TEST_BIT (\1);/' > $@ + +check: regex + ./regex + +TAGS: regex.c regex.h *.h *.c + $(ETAGS) -t $^ + +depend: + gcc -MM $(ALL_CPPFLAGS) *.c > /tmp/depend +.PHONY: depend + +install: +.PHONY: install + +clean mostlyclean: + rm -f *.o regex cppregex iregex fileregex regexcpp.c syntax + +distclean: clean + rm -f bits syntax.c Makefile + +extraclean: distclean + rm -f *~* *\#* patch* *.orig *.rej *.bak core a.out + +realclean: distclean + rm -f TAGS + +Makefile: Makefile.in ../config.status + (cd ..; sh config.status) + +# Prevent GNU make 3 from overflowing arg limit on system V. +.NOEXPORT: + +# Assumes $(distdir) is the place to put our files. +distfiles = ChangeLog TAGS *.in *.c *.h regexcpp.sed syntax.skel +dist: Makefile TAGS + mkdir $(distdir) + ln $(distfiles) $(distdir) + +# Automatically-generated dependencies below here. +alloca.o : alloca.c +bsd-interf.o : bsd-interf.c +debugmalloc.o : debugmalloc.c +emacsmalloc.o : emacsmalloc.c getpagesize.h +fileregex.o : fileregex.c .././regex.h +g++malloc.o : g++malloc.c //usr/include/stdio.h getpagesize.h +iregex.o : iregex.c .././regex.h +main.o : main.c test.h .././regex.h +malloc-test.o : malloc-test.c +other.o : other.c test.h .././regex.h +printchar.o : printchar.c +psx-basic.o : psx-basic.c test.h .././regex.h +psx-extend.o : psx-extend.c test.h .././regex.h +psx-generic.o : psx-generic.c test.h .././regex.h +psx-group.o : psx-group.c test.h .././regex.h +psx-interf.o : psx-interf.c test.h .././regex.h +psx-interv.o : psx-interv.c test.h .././regex.h +syntax.o : syntax.c .././regex.h +test.o : test.c test.h .././regex.h +tregress.o : tregress.c test.h .././regex.h +upcase.o : upcase.c +xmalloc.o : xmalloc.c diff --git a/gnu/lib/libregex/test/TAGS b/gnu/lib/libregex/test/TAGS new file mode 100644 index 0000000000..d3aad750dc --- /dev/null +++ b/gnu/lib/libregex/test/TAGS @@ -0,0 +1,373 @@ + +.././regex.c,4137 +#define AT_STRINGS_BEG(3078,98376 +#define AT_STRINGS_END(3079,98449 +#define AT_WORD_BOUNDARY(3093,99002 +#define BUF_PUSH(887,24995 +#define BUF_PUSH_2(895,25208 +#define BUF_PUSH_3(904,25437 +#define DEBUG_POP(2336,74614 +#define DEBUG_PRINT1(471,14296 +#define DEBUG_PRINT1(785,21263 +#define DEBUG_PRINT2(472,14342 +#define DEBUG_PRINT3(473,14398 +#define DEBUG_PRINT3(787,21316 +#define DEBUG_PRINT4(474,14462 +#define DEBUG_PRINT_COMPILED_PATTERN(475,14534 +#define DEBUG_PRINT_COMPILED_PATTERN(789,21386 +#define DEBUG_PRINT_DOUBLE_STRING(477,14637 +#define DEBUG_PUSH(2338,74684 +#define DEBUG_STATEMENT(470,14267 +#define DOUBLE_FAIL_STACK(2299,73230 +#define EVER_MATCHED_SOMETHING(3028,96680 +#define EXTEND_BUFFER(941,26834 +#define EXTRACT_NUMBER(403,12499 +#define EXTRACT_NUMBER(422,12960 +#define EXTRACT_NUMBER_AND_INCR(430,13181 +#define EXTRACT_NUMBER_AND_INCR(448,13583 +#define FAIL_STACK_EMPTY(2271,72289 +#define FAIL_STACK_FULL(2273,72404 +#define FAIL_STACK_PTR_EMPTY(2272,72344 +#define FAIL_STACK_TOP(2274,72473 +#define FIRST_STRING_P(221,5848 +#define FREE_VAR(3100,99186 +#define FREE_VARIABLES(3101,99240 +#define FREE_VARIABLES(3116,99751 +#define GET_BUFFER_SPACE(882,24802 +#define GET_UNSIGNED_NUMBER(1017,29312 +#define INIT_FAIL_STACK(2279,72612 +#define INSERT_JUMP(923,26079 +#define INSERT_JUMP2(927,26236 +#define ISALNUM(147,3407 +#define ISALPHA(148,3455 +#define ISBLANK(135,3062 +#define ISBLANK(137,3116 +#define ISCNTRL(149,3503 +#define ISDIGIT(146,3359 +#define ISGRAPH(140,3185 +#define ISGRAPH(142,3239 +#define ISLOWER(150,3551 +#define ISPRINT(145,3311 +#define ISPUNCT(151,3599 +#define ISSPACE(152,3647 +#define ISUPPER(153,3695 +#define ISXDIGIT(154,3743 +#define IS_ACTIVE(3026,96578 +#define IS_CHAR_CLASS(1035,29793 +#define MATCHED_SOMETHING(3027,96621 +#define MAX(233,6292 +#define MIN(234,6334 +#define PATFETCH(852,23769 +#define PATFETCH_RAW(860,24020 +#define POINTER_TO_OFFSET(3050,97433 +#define POP_FAILURE_ITEM(2331,74426 +#define POP_FAILURE_POINT(2461,79538 +#define PREFETCH(3064,97916 +#define PUSH_FAILURE_ITEM(2327,74253 +#define PUSH_FAILURE_POINT(2352,75048 +#define PUSH_PATTERN_OP(2317,73841 +#define REGEX_REALLOCATE(185,4875 +#define REGEX_REALLOCATE(210,5495 +#define REGEX_TALLOC(227,6137 +#define REG_MATCH_NULL_STRING_P(3025,96511 +#define REG_UNSET(3055,97649 +#define RETALLOC(226,6058 +#define SET_LIST_BIT(1011,29089 +#define SET_REGS_MATCHED(3034,96936 +#define SIGN_EXTEND_CHAR(166,4109 +#define SIGN_EXTEND_CHAR(169,4217 +#define STORE_JUMP(915,25800 +#define STORE_JUMP2(919,25917 +#define STORE_NUMBER(384,11919 +#define STORE_NUMBER_AND_INCR(394,12242 +#define STREQ(231,6244 +#define SYNTAX(120,2790 +#define TALLOC(225,6003 +#define TRANSLATE(873,24503 +#define WORDCHAR_P(3086,98755 +alt_match_null_string_p 4466,149039 +#define assert(782,21217 +at_begline_loc_p 2131,67979 +at_endline_loc_p 2150,68557 +#define bcmp(54,1656 +bcmp_translate 4591,151831 +#define bcopy(57,1726 +typedef char boolean;236,6377 +#define bzero(60,1793 +common_op_match_null_string_p 4503,149895 +compile_range 2200,69997 +} compile_stack_elt_t;990,28602 +} compile_stack_type;998,28748 +extract_number 411,12714 +extract_number_and_incr 438,13370 +} fail_stack_type;2269,72269 +group_in_compile_stack 2172,69174 +group_match_null_string_p 4357,145267 +init_syntax_once 94,2365 +insert_op1 2091,67107 +insert_op2 2110,67475 +#define isascii(131,3018 +typedef int pattern_offset_t;981,28388 +print_compiled_pattern 726,19792 +print_double_string 753,20605 +print_fastmap 486,14835 +print_partial_compiled_pattern 518,15475 +re_comp 4650,153479 +re_compile_fastmap 2532,82428 +re_compile_pattern 4617,152520 +re_exec 4688,154373 +re_match 3136,100557 +re_match_2 3161,101399 +} re_opcode_t;378,11781 +re_search 2844,90872 +re_search_2 2877,91998 +re_set_registers 2817,90247 +re_set_syntax 808,22087 +regcomp 4736,155972 +regerror 4876,160188 +regex_compile 1062,30922 +regexec 4811,158371 +regfree 4920,161247 +} register_info_type;3023,96488 +typedef unsigned regnum_t;974,28172 +store_op1 2063,66535 +store_op2 2076,66768 +typedef const unsigned 2262,72103 + +.././regex.h,230 +#define _RE_ARGS(394,14981 +#define _RE_ARGS(398,15036 +} reg_errcode_t;270,10874 +typedef unsigned reg_syntax_t;38,1503 +typedef struct re_pattern_buffer regex_t;346,13556 +} regmatch_t;382,14634 +typedef int regoff_t;354,13814 + +getpagesize.h,84 +#define getpagesize(12,137 +#define getpagesize(15,191 +#define getpagesize(20,302 + +test.h,436 +#define BRACES_TO_OPS(107,3169 +#define INVALID_PATTERN(110,3328 +#define MATCH_SELF(114,3429 +#define PARENS_TO_OPS(108,3248 +#define SAFE_STRLEN(14,201 +#define TEST_POSITIONED_MATCH(116,3470 +#define TEST_REGISTERS(104,3011 +#define TEST_REGISTERS_2(97,2703 +#define TEST_SEARCH(127,3875 +#define TEST_SEARCH_2(123,3720 +#define TEST_TRUNCATED_MATCH(120,3608 +typedef enum { false = 0, true = 1 } boolean;16,255 +} test_type;33,572 + +alloca.c,128 +alloca 141,3996 +find_stack_direction 85,2553 +} header;127,3538 +typedef void *pointer;51,1721 +typedef char *pointer;53,1778 + +bsd-interf.c,51 +test_berk_search 8,106 +test_bsd_interface 33,738 + +debugmalloc.c,395 +#define TRACE(8,143 +#define TRACE1(9,197 +#define TRACE2(10,254 +#define TRACE3(11,319 +#define TRACE4(12,392 +#define USER_ALLOC(61,1440 +typedef char *address;15,480 +} *chunk;54,1225 +chunk_delete 115,2778 +chunk_insert 96,2294 +chunk_to_mem 79,1916 +free 261,5604 +free_list_available 175,3947 +malloc 203,4343 +mem_to_chunk 68,1703 +realloc 242,5309 +validate_list 153,3478 +xsbrk 21,545 + +emacsmalloc.c,574 +#define ASSERT(178,5884 +#define ASSERT(181,5985 +#define CHAIN(166,5430 +#define bcmp(73,2821 +#define bcopy(72,2777 +#define bzero(74,2868 +calloc 603,15983 +free 484,13255 +get_lim_data 736,18517 +get_lim_data 752,18767 +get_lim_data 759,18860 +getpool 374,10263 +malloc 413,11133 +malloc_init 218,6863 +malloc_mem_free 707,17940 +malloc_mem_used 688,17683 +malloc_stats 663,17320 +malloc_usable_size 233,7147 +memalign 618,16164 +morecore 244,7380 +realloc 541,14424 +#define start_of_data(110,3486 +#define start_of_data(115,3546 +sys_sbrk 815,20804 +valloc 645,17031 + +fileregex.c,13 +main 11,156 + +g++malloc.c,1543 +#define UPDATE_STATS(33,1090 +#define UPDATE_STATS(35,1131 +static inline int aligned_OK(343,11189 +void* calloc(1039,28692 +void cfree(1048,28894 +static inline void* chunk2mem(619,19336 +#define clear_inuse(592,18767 +static inline void consollink(716,21398 +static void do_free_stats(544,18016 +static void do_malloc_stats(534,17741 +766,22304 +extern 762,22235 + for 1260,34165 +void free(1028,28553 +static inline void frontlink(732,21717 +static unsigned int gcd(557,18251 + if 1212,32427 + if 1216,32582 + if 1220,32737 + if 1224,32880 + if 1229,33094 + if 1233,33251 + if 1238,33463 + if 1242,33609 + if 1247,33739 +#define inuse(590,18680 +static inline unsigned int lcm(580,18540 +void* malloc(939,26370 +static mchunkptr malloc_find_space(858,24561 +void malloc_stats(1201,32256 +unsigned int malloc_usable_size(1054,28936 +static volatile void malloc_user_error(286,9757 +static void malloc_user_error(288,9804 +typedef struct malloc_bin* mbinptr;320,10636 +typedef struct malloc_chunk* mchunkptr;309,10247 +static inline mchunkptr mem2chunk(643,19759 +void* memalign(1118,30363 +#define next_chunk(600,18910 +#define prev_chunk(604,19023 +void* realloc(1071,29263 +static inline unsigned int request2size(335,10993 +mchunkptr sanity_check(628,19486 +#define set_inuse(591,18723 +static inline void set_size(609,19149 +static inline mbinptr size2bin(499,16914 +static inline void split(685,20463 +static 768,22312 +static inline void unlink(671,20263 +void* valloc(1194,32107 +typedef volatile void 760,22184 +764,22271 + +iregex.c,54 +main 20,390 +print_regs 141,2638 +scanstring 87,1839 + +main.c,13 +main 12,242 + +malloc-test.c,112 +#define BITS_BLOCK(12,168 +#define BITS_MASK(13,228 +} bits_list_type;6,56 +init_bits_list 16,311 +main(32,621 + +other.c,18 +test_others 6,96 + +printchar.c,15 +printchar 2,5 + +psx-basic.c,23 +test_posix_basic 7,84 + +psx-extend.c,26 +test_posix_extended 7,88 + +psx-generic.c,26 +test_posix_generic 8,117 + +psx-group.c,20 +test_grouping 7,92 + +psx-interf.c,416 +fill_pmatch 174,4802 +get_error_string 18,260 +init_pattern_buffer 49,1434 +test_compile 67,1925 +test_eflags 245,6876 +test_error_code_allocation 562,16619 +test_error_code_message 524,15247 +test_ignore_case 303,8525 +test_newline 330,9199 +test_nsub 117,3319 +test_pmatch 188,5121 +test_posix_interface 614,18719 +test_posix_match 359,9938 +test_regcomp 138,3725 +test_regerror 592,17621 +test_regexec 394,10783 + +psx-interv.c,21 +test_intervals 6,93 + +test.c,607 +#define SET_FASTMAP(447,13999 +#define bcmp(18,362 +#define bcopy(19,415 +#define bzero(20,473 +compile_and_print_pattern 666,19653 +concat 97,2673 +delimiters_to_ops 571,17477 +general_test 115,2996 +invalid_pattern 542,16821 +#define memcmp(26,611 +#define memcpy(27,660 +print_pattern_info 635,18998 +set_all_registers 58,1390 +test_all_registers 506,15567 +test_case_fold 682,19993 +test_fastmap 460,14363 +test_fastmap_search 474,14668 +test_match 776,22235 +test_match_2 766,22040 +test_match_n_times 715,20798 +test_search_return 408,13011 +valid_nonposix_pattern 646,19239 +valid_pattern 557,17182 + +tregress.c,208 +#define SIMPLE_MATCH(74,1463 +#define SIMPLE_NONMATCH(75,1528 +do_match 78,1599 +itoa 10,199 +simple_compile 44,882 +simple_fail 21,353 +simple_fastmap 55,1115 +simple_search 100,2020 +test_regress 124,2513 + +upcase.c,0 + +xmalloc.c,14 +xmalloc 9,87 diff --git a/gnu/lib/libregex/test/alloca.c b/gnu/lib/libregex/test/alloca.c new file mode 100644 index 0000000000..c1ff22227f --- /dev/null +++ b/gnu/lib/libregex/test/alloca.c @@ -0,0 +1,194 @@ +/* + alloca -- (mostly) portable public-domain implementation -- D A Gwyn + + last edit: 86/05/30 rms + include config.h, since on VMS it renames some symbols. + Use xmalloc instead of malloc. + + This implementation of the PWB library alloca() function, + which is used to allocate space off the run-time stack so + that it is automatically reclaimed upon procedure exit, + was inspired by discussions with J. Q. Johnson of Cornell. + + It should work under any C implementation that uses an + actual procedure stack (as opposed to a linked list of + frames). There are some preprocessor constants that can + be defined when compiling for your specific system, for + improved efficiency; however, the defaults should be okay. + + The general concept of this implementation is to keep + track of all alloca()-allocated blocks, and reclaim any + that are found to be deeper in the stack than the current + invocation. This heuristic does not reclaim storage as + soon as it becomes invalid, but it will do so eventually. + + As a special case, alloca(0) reclaims storage without + allocating any. It is a good idea to use alloca(0) in + your main control loop, etc. to force garbage collection. +*/ +#ifndef lint +static char SCCSid[] = "@(#)alloca.c 1.1"; /* for the "what" utility */ +#endif + +#ifdef emacs +#include "config.h" +#ifdef static +/* actually, only want this if static is defined as "" + -- this is for usg, in which emacs must undefine static + in order to make unexec workable + */ +#ifndef STACK_DIRECTION +you +lose +-- must know STACK_DIRECTION at compile-time +#endif /* STACK_DIRECTION undefined */ +#endif /* static */ +#endif /* emacs */ + +#ifndef alloca /* If compiling with GCC, this file's not needed. */ + +#ifdef __STDC__ +typedef void *pointer; /* generic pointer type */ +#else +typedef char *pointer; /* generic pointer type */ +#endif + +#define NULL 0 /* null pointer constant */ + +extern void free(); +extern pointer xmalloc(); + +/* + Define STACK_DIRECTION if you know the direction of stack + growth for your system; otherwise it will be automatically + deduced at run-time. + + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown +*/ + +#ifndef STACK_DIRECTION +#define STACK_DIRECTION 0 /* direction unknown */ +#endif + +#if STACK_DIRECTION != 0 + +#define STACK_DIR STACK_DIRECTION /* known at compile-time */ + +#else /* STACK_DIRECTION == 0; need run-time code */ + +static int stack_dir; /* 1 or -1 once known */ +#define STACK_DIR stack_dir + +static void +find_stack_direction (/* void */) +{ + static char *addr = NULL; /* address of first + `dummy', once known */ + auto char dummy; /* to get stack address */ + + if (addr == NULL) + { /* initial entry */ + addr = &dummy; + + find_stack_direction (); /* recurse once */ + } + else /* second entry */ + if (&dummy > addr) + stack_dir = 1; /* stack grew upward */ + else + stack_dir = -1; /* stack grew downward */ +} + +#endif /* STACK_DIRECTION == 0 */ + +/* + An "alloca header" is used to: + (a) chain together all alloca()ed blocks; + (b) keep track of stack depth. + + It is very important that sizeof(header) agree with malloc() + alignment chunk size. The following default should work okay. +*/ + +#ifndef ALIGN_SIZE +#define ALIGN_SIZE sizeof(double) +#endif + +typedef union hdr +{ + char align[ALIGN_SIZE]; /* to force sizeof(header) */ + struct + { + union hdr *next; /* for chaining headers */ + char *deep; /* for stack depth measure */ + } h; +} header; + +/* + alloca( size ) returns a pointer to at least `size' bytes of + storage which will be automatically reclaimed upon exit from + the procedure that called alloca(). Originally, this space + was supposed to be taken from the current stack frame of the + caller, but that method cannot be made to work for some + implementations of C, for example under Gould's UTX/32. +*/ + +static header *last_alloca_header = NULL; /* -> last alloca header */ + +pointer +alloca (size) /* returns pointer to storage */ + unsigned size; /* # bytes to allocate */ +{ + auto char probe; /* probes stack depth: */ + register char *depth = &probe; + +#if STACK_DIRECTION == 0 + if (STACK_DIR == 0) /* unknown growth direction */ + find_stack_direction (); +#endif + + /* Reclaim garbage, defined as all alloca()ed storage that + was allocated from deeper in the stack than currently. */ + + { + register header *hp; /* traverses linked list */ + + for (hp = last_alloca_header; hp != NULL;) + if ((STACK_DIR > 0 && hp->h.deep > depth) + || (STACK_DIR < 0 && hp->h.deep < depth)) + { + register header *np = hp->h.next; + + free ((pointer) hp); /* collect garbage */ + + hp = np; /* -> next header */ + } + else + break; /* rest are not deeper */ + + last_alloca_header = hp; /* -> last valid storage */ + } + + if (size == 0) + return NULL; /* no allocation required */ + + /* Allocate combined header + user data storage. */ + + { + register pointer new = xmalloc (sizeof (header) + size); + /* address of header */ + + ((header *)new)->h.next = last_alloca_header; + ((header *)new)->h.deep = depth; + + last_alloca_header = (header *)new; + + /* User storage begins just after header. */ + + return (pointer)((char *)new + sizeof(header)); + } +} + +#endif /* no alloca */ diff --git a/gnu/lib/libregex/test/bsd-interf.c b/gnu/lib/libregex/test/bsd-interf.c new file mode 100644 index 0000000000..56f9e2a2fe --- /dev/null +++ b/gnu/lib/libregex/test/bsd-interf.c @@ -0,0 +1,38 @@ +/* bsd-interf.c: test BSD interface. */ + +#ifndef _POSIX_SOURCE /* whole file */ + +#include "test.h" + +void +test_berk_search (pattern, string) + const char *pattern; + char *string; +{ + const char *return_value = re_comp (pattern); + + if (return_value != 0) + { + printf ("This didn't compile: `%s'.\n", pattern); + printf (" The error message was: `%s'.\n", return_value); + } + else + if (test_should_match && re_exec (string) != strlen (string)) + { + printf ("Should have matched but didn't:\n"); + printf (" The pattern was: %s.\n", pattern); + if (string) + printf (" The string was: `%s'.'n", string); + else + printf (" The string was empty.\n"); + } +} + + +void +test_bsd_interface () +{ + test_berk_search ("a", "ab"); +} + +#endif /* _POSIX_SOURCE */ diff --git a/gnu/lib/libregex/test/debugmalloc.c b/gnu/lib/libregex/test/debugmalloc.c new file mode 100644 index 0000000000..5c468e2124 --- /dev/null +++ b/gnu/lib/libregex/test/debugmalloc.c @@ -0,0 +1,273 @@ +/* debugmalloc.c: a malloc for debugging purposes. */ + +#include +#include +#include + +static unsigned trace = 0; +#define TRACE(s) if (trace) fprintf (stderr, "%s", s) +#define TRACE1(s, e1) if (trace) fprintf (stderr, s, e1) +#define TRACE2(s, e1, e2) if (trace) fprintf (stderr, s, e1, e2) +#define TRACE3(s, e1, e2, e3) if (trace) fprintf (stderr, s, e1, e2, e3) +#define TRACE4(s, e1, e2, e3, e4) \ + if (trace) fprintf (stderr, s, e1, e2, e3, e4) + +typedef char *address; + + +/* Wrap our calls to sbrk. */ + +address +xsbrk (incr) + int incr; +{ + extern char *sbrk (); + address ret = sbrk (incr); + + if (ret == (address) -1) + { + perror ("sbrk"); /* Actually, we should return NULL, not quit. */ + abort (); + } + + return ret; +} + + + +typedef struct chunk_struct +{ + /* This is the size (in bytes) that has actually been actually + allocated, not the size that the user requested. */ + unsigned alloc_size; + + /* This is the size the user requested. */ + unsigned user_size; + + /* Points to the next block in one of the lists. */ + struct chunk_struct *next; + + /* Now comes the user's memory. */ + address user_mem; + + /* After the user's memory is a constant. */ +} *chunk; + +#define MALLOC_OVERHEAD 16 + +/* We might play around with the `user_size' field, but the amount of + memory that is actually available in the chunk is always the size + allocated minus the overhead. */ +#define USER_ALLOC(c) ((c)->alloc_size - MALLOC_OVERHEAD) + +/* Given a pointer to a malloc-allocated block, the beginning of the + chunk should always be MALLOC_OVERHEAD - 4 bytes back, since the only + overhead after the user memory is the constant. */ + +chunk +mem_to_chunk (mem) + address mem; +{ + return (chunk) (mem - (MALLOC_OVERHEAD - 4)); +} + + +/* The other direction is even easier, since the user's memory starts at + the `user_mem' member in the chunk. */ + +address +chunk_to_mem (c) + chunk c; +{ + return (address) &(c->user_mem); +} + + + +/* We keep both all the allocated chunks and all the free chunks on + lists. Since we put the next pointers in the chunk structure, we + don't need a separate chunk_list structure. */ +chunk alloc_list = NULL, free_list = NULL; + + +/* We always append the new chunk at the beginning of the list. */ + +void +chunk_insert (chunk_list, new_c) + chunk *chunk_list; + chunk new_c; +{ + chunk c = *chunk_list; /* old beginning of list */ + + TRACE3 (" Inserting 0x%x at the beginning of 0x%x, before 0x%x.\n", + new_c, chunk_list, c); + + *chunk_list = new_c; + new_c->next = c; +} + + +/* Thus, removing an element means we have to search until we find it. + Have to delete before we insert, since insertion changes the next + pointer, which we need to put it on the other list. */ + +void +chunk_delete (chunk_list, dead_c) + chunk *chunk_list; + chunk dead_c; +{ + chunk c = *chunk_list; + chunk prev_c = NULL; + + TRACE2 (" Deleting 0x%x from 0x%x:", dead_c, chunk_list); + + while (c != dead_c && c != NULL) + { + TRACE1 (" 0x%x", c); + prev_c = c; + c = c->next; + } + + if (c == NULL) + { + fprintf (stderr, "Chunk at 0x%x not found on list.\n", dead_c); + abort (); + } + + if (prev_c == NULL) + { + TRACE1 (".\n Setting head to 0x%x.\n", c->next); + *chunk_list = c->next; + } + else + { + TRACE2 (".\n Linking next(0x%x) to 0x%x.\n", prev_c, c->next); + prev_c->next = c->next; + } +} + + +/* See if a list is hunky-dory. */ + +void +validate_list (chunk_list) + chunk *chunk_list; +{ + chunk c; + + TRACE1 (" Validating list at 0x%x:", chunk_list); + + for (c = *chunk_list; c != NULL; c = c->next) + { + assert (c->user_size < c->alloc_size); + assert (memcmp (chunk_to_mem (c) + c->user_size, "Karl", 4)); + TRACE2 (" 0x%x/%d", c, c->user_size); + } + + TRACE (".\n"); +} + + +/* See if we have a free chunk of a given size. We'll take the first + one that is big enough. */ + +chunk +free_list_available (needed) + unsigned needed; +{ + chunk c; + + TRACE1 (" Checking free list for %d bytes:", needed); + + if (free_list == NULL) + { + return NULL; + } + + c = free_list; + + while (c != NULL && USER_ALLOC (c) < needed) + { + TRACE2 (" 0x%x/%d", c, USER_ALLOC (c)); + c = c->next; + } + + TRACE1 ("\n Returning 0x%x.\n", c); + return c; +} + + + + +address +malloc (n) + unsigned n; +{ + address new_mem; + chunk c; + + TRACE1 ("Mallocing %d bytes.\n", n); + + validate_list (&free_list); + validate_list (&alloc_list); + + c = free_list_available (n); + + if (c == NULL) + { /* Nothing suitable on free list. Allocate a new chunk. */ + TRACE (" not on free list.\n"); + c = (chunk) xsbrk (n + MALLOC_OVERHEAD); + c->alloc_size = n + MALLOC_OVERHEAD; + } + else + { /* Found something on free list. Don't split it, just use as is. */ + TRACE (" found on free list.\n"); + chunk_delete (&free_list, c); + } + + /* If we took this from the free list, then the user size might be + different now, and consequently the constant at the end might be in + the wrong place. */ + c->user_size = n; + new_mem = chunk_to_mem (c); + memcpy (new_mem + n, "Karl", 4); + chunk_insert (&alloc_list, c); + + TRACE2 ("Malloc returning 0x%x (chunk 0x%x).\n", new_mem, c); + return new_mem; +} + + +address +realloc (mem, n) + address mem; + unsigned n; +{ + void free (); + chunk c = mem_to_chunk (mem); + address new_mem; + + TRACE3 ("Reallocing %d bytes at 0x%x (chunk 0x%x).\n", n, mem, c); + + new_mem = malloc (n); + memcpy (new_mem, mem, c->user_size); + free (mem); + + return new_mem; +} + + +void +free (mem) + address mem; +{ + chunk c = mem_to_chunk (mem); + + TRACE2 ("Freeing memory at 0x%x (chunk at 0x%x).\n", mem, c); + + validate_list (&free_list); + validate_list (&alloc_list); + + chunk_delete (&alloc_list, c); + chunk_insert (&free_list, c); +} diff --git a/gnu/lib/libregex/test/emacsmalloc.c b/gnu/lib/libregex/test/emacsmalloc.c new file mode 100644 index 0000000000..6eee1fae1a --- /dev/null +++ b/gnu/lib/libregex/test/emacsmalloc.c @@ -0,0 +1,844 @@ +/* dynamic memory allocation for GNU. + Copyright (C) 1985, 1987 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +In other words, you are welcome to use, share and improve this program. +You are forbidden to forbid anyone else to use, share and improve +what you give them. Help stamp out software-hoarding! */ + + +/* + * @(#)nmalloc.c 1 (Caltech) 2/21/82 + * + * U of M Modified: 20 Jun 1983 ACT: strange hacks for Emacs + * + * Nov 1983, Mike@BRL, Added support for 4.1C/4.2 BSD. + * + * This is a very fast storage allocator. It allocates blocks of a small + * number of different sizes, and keeps free lists of each size. Blocks + * that don't exactly fit are passed up to the next larger size. In this + * implementation, the available sizes are (2^n)-4 (or -16) bytes long. + * This is designed for use in a program that uses vast quantities of + * memory, but bombs when it runs out. To make it a little better, it + * warns the user when he starts to get near the end. + * + * June 84, ACT: modified rcheck code to check the range given to malloc, + * rather than the range determined by the 2-power used. + * + * Jan 85, RMS: calls malloc_warning to issue warning on nearly full. + * No longer Emacs-specific; can serve as all-purpose malloc for GNU. + * You should call malloc_init to reinitialize after loading dumped Emacs. + * Call malloc_stats to get info on memory stats if MSTATS turned on. + * realloc knows how to return same block given, just changing its size, + * if the power of 2 is correct. + */ + +/* + * nextf[i] is the pointer to the next free block of size 2^(i+3). The + * smallest allocatable block is 8 bytes. The overhead information will + * go in the first int of the block, and the returned pointer will point + * to the second. + * +#ifdef MSTATS + * nmalloc[i] is the difference between the number of mallocs and frees + * for a given block size. +#endif MSTATS + */ + +#ifdef emacs +/* config.h specifies which kind of system this is. */ +#include "config.h" +#include +#else + +/* Determine which kind of system this is. */ +#include +#include + +#include +#define bcopy(s,d,n) memcpy ((d), (s), (n)) +#define bcmp(s1,s2,n) memcmp ((s1), (s2), (n)) +#define bzero(s,n) memset ((s), 0, (n)) + +#ifndef SIGTSTP +#ifndef VMS +#ifndef USG +#define USG +#endif +#endif /* not VMS */ +#else /* SIGTSTP */ +#ifdef SIGIO +#define BSD4_2 +#endif /* SIGIO */ +#endif /* SIGTSTP */ + +#endif /* not emacs */ + +/* Define getpagesize () if the system does not. */ +#include "getpagesize.h" + +#ifdef BSD +#ifdef BSD4_1 +#include /* warn the user when near the end */ +#else /* if 4.2 or newer */ +#include +#include +#endif /* if 4.2 or newer */ +#endif + +#ifdef VMS +#include "vlimit.h" +#endif + +extern char *start_of_data (); + +#ifdef BSD +#ifndef DATA_SEG_BITS +#define start_of_data() &etext +#endif +#endif + +#ifndef emacs +#define start_of_data() &etext +#endif + +#define ISALLOC ((char) 0xf7) /* magic byte that implies allocation */ +#define ISFREE ((char) 0x54) /* magic byte that implies free block */ + /* this is for error checking only */ +#define ISMEMALIGN ((char) 0xd6) /* Stored before the value returned by + memalign, with the rest of the word + being the distance to the true + beginning of the block. */ + +extern char etext; + +/* These two are for user programs to look at, when they are interested. */ + +unsigned int malloc_sbrk_used; /* amount of data space used now */ +unsigned int malloc_sbrk_unused; /* amount more we can have */ + +/* start of data space; can be changed by calling init_malloc */ +static char *data_space_start; + +#ifdef MSTATS +static int nmalloc[30]; +static int nmal, nfre; +#endif /* MSTATS */ + +/* If range checking is not turned on, all we have is a flag indicating + whether memory is allocated, an index in nextf[], and a size field; to + realloc() memory we copy either size bytes or 1<<(index+3) bytes depending + on whether the former can hold the exact size (given the value of + 'index'). If range checking is on, we always need to know how much space + is allocated, so the 'size' field is never used. */ + +struct mhead { + char mh_alloc; /* ISALLOC or ISFREE */ + char mh_index; /* index in nextf[] */ +/* Remainder are valid only when block is allocated */ + unsigned short mh_size; /* size, if < 0x10000 */ +#ifdef rcheck + unsigned mh_nbytes; /* number of bytes allocated */ + int mh_magic4; /* should be == MAGIC4 */ +#endif /* rcheck */ +}; + +/* Access free-list pointer of a block. + It is stored at block + 4. + This is not a field in the mhead structure + because we want sizeof (struct mhead) + to describe the overhead for when the block is in use, + and we do not want the free-list pointer to count in that. */ + +#define CHAIN(a) \ + (*(struct mhead **) (sizeof (char *) + (char *) (a))) + +#ifdef rcheck + +/* To implement range checking, we write magic values in at the beginning and + end of each allocated block, and make sure they are undisturbed whenever a + free or a realloc occurs. */ +/* Written in each of the 4 bytes following the block's real space */ +#define MAGIC1 0x55 +/* Written in the 4 bytes before the block's real space */ +#define MAGIC4 0x55555555 +#define ASSERT(p) if (!(p)) botch("p"); else +#define EXTRA 4 /* 4 bytes extra for MAGIC1s */ +#else +#define ASSERT(p) if (!(p)) abort (); else +#define EXTRA 0 +#endif /* rcheck */ + + +/* nextf[i] is free list of blocks of size 2**(i + 3) */ + +static struct mhead *nextf[30]; + +/* busy[i] is nonzero while allocation of block size i is in progress. */ + +static char busy[30]; + +/* Number of bytes of writable memory we can expect to be able to get */ +static unsigned int lim_data; + +/* Level number of warnings already issued. + 0 -- no warnings issued. + 1 -- 75% warning already issued. + 2 -- 85% warning already issued. +*/ +static int warnlevel; + +/* Function to call to issue a warning; + 0 means don't issue them. */ +static void (*warnfunction) (); + +/* nonzero once initial bunch of free blocks made */ +static int gotpool; + +char *_malloc_base; + +static void getpool (); + +/* Cause reinitialization based on job parameters; + also declare where the end of pure storage is. */ +void +malloc_init (start, warnfun) + char *start; + void (*warnfun) (); +{ + if (start) + data_space_start = start; + lim_data = 0; + warnlevel = 0; + warnfunction = warnfun; +} + +/* Return the maximum size to which MEM can be realloc'd + without actually requiring copying. */ + +int +malloc_usable_size (mem) + char *mem; +{ + struct mhead *p + = (struct mhead *) (mem - ((sizeof (struct mhead) + 7) & ~7)); + int blocksize = 8 << p->mh_index; + + return blocksize - sizeof (struct mhead) - EXTRA; +} + +static void +morecore (nu) /* ask system for more memory */ + register int nu; /* size index to get more of */ +{ + char *sbrk (); + register char *cp; + register int nblks; + register unsigned int siz; + int oldmask; + +#ifdef BSD +#ifndef BSD4_1 + int newmask = -1; + /* Blocking these signals interferes with debugging, at least on BSD on + the HP 9000/300. */ +#ifdef SIGTRAP + newmask &= ~(1 << SIGTRAP); +#endif +#ifdef SIGILL + newmask &= ~(1 << SIGILL); +#endif +#ifdef SIGTSTP + newmask &= ~(1 << SIGTSTP); +#endif +#ifdef SIGSTOP + newmask &= ~(1 << SIGSTOP); +#endif + oldmask = sigsetmask (newmask); +#endif +#endif + + if (!data_space_start) + { + data_space_start = start_of_data (); + } + + if (lim_data == 0) + get_lim_data (); + + /* On initial startup, get two blocks of each size up to 1k bytes */ + if (!gotpool) + { getpool (); getpool (); gotpool = 1; } + + /* Find current end of memory and issue warning if getting near max */ + +#ifndef VMS + /* Maximum virtual memory on VMS is difficult to calculate since it + * depends on several dynmacially changing things. Also, alignment + * isn't that important. That is why much of the code here is ifdef'ed + * out for VMS systems. + */ + cp = sbrk (0); + siz = cp - data_space_start; + + if (warnfunction) + switch (warnlevel) + { + case 0: + if (siz > (lim_data / 4) * 3) + { + warnlevel++; + (*warnfunction) ("Warning: past 75% of memory limit"); + } + break; + case 1: + if (siz > (lim_data / 20) * 17) + { + warnlevel++; + (*warnfunction) ("Warning: past 85% of memory limit"); + } + break; + case 2: + if (siz > (lim_data / 20) * 19) + { + warnlevel++; + (*warnfunction) ("Warning: past 95% of memory limit"); + } + break; + } + + if ((int) cp & 0x3ff) /* land on 1K boundaries */ + sbrk (1024 - ((int) cp & 0x3ff)); +#endif /* not VMS */ + + /* Take at least 2k, and figure out how many blocks of the desired size + we're about to get */ + nblks = 1; + if ((siz = nu) < 8) + nblks = 1 << ((siz = 8) - nu); + + if ((cp = sbrk (1 << (siz + 3))) == (char *) -1) + { +#ifdef BSD +#ifndef BSD4_1 + sigsetmask (oldmask); +#endif +#endif + return; /* no more room! */ + } + malloc_sbrk_used = siz; + malloc_sbrk_unused = lim_data - siz; + +#ifndef VMS + if ((int) cp & 7) + { /* shouldn't happen, but just in case */ + cp = (char *) (((int) cp + 8) & ~7); + nblks--; + } +#endif /* not VMS */ + + /* save new header and link the nblks blocks together */ + nextf[nu] = (struct mhead *) cp; + siz = 1 << (nu + 3); + while (1) + { + ((struct mhead *) cp) -> mh_alloc = ISFREE; + ((struct mhead *) cp) -> mh_index = nu; + if (--nblks <= 0) break; + CHAIN ((struct mhead *) cp) = (struct mhead *) (cp + siz); + cp += siz; + } + CHAIN ((struct mhead *) cp) = 0; + +#ifdef BSD +#ifndef BSD4_1 + sigsetmask (oldmask); +#endif +#endif +} + +static void +getpool () +{ + register int nu; + char * sbrk (); + register char *cp = sbrk (0); + + if ((int) cp & 0x3ff) /* land on 1K boundaries */ + sbrk (1024 - ((int) cp & 0x3ff)); + + /* Record address of start of space allocated by malloc. */ + if (_malloc_base == 0) + _malloc_base = cp; + + /* Get 2k of storage */ + + cp = sbrk (04000); + if (cp == (char *) -1) + return; + + /* Divide it into an initial 8-word block + plus one block of size 2**nu for nu = 3 ... 10. */ + + CHAIN (cp) = nextf[0]; + nextf[0] = (struct mhead *) cp; + ((struct mhead *) cp) -> mh_alloc = ISFREE; + ((struct mhead *) cp) -> mh_index = 0; + cp += 8; + + for (nu = 0; nu < 7; nu++) + { + CHAIN (cp) = nextf[nu]; + nextf[nu] = (struct mhead *) cp; + ((struct mhead *) cp) -> mh_alloc = ISFREE; + ((struct mhead *) cp) -> mh_index = nu; + cp += 8 << nu; + } +} + +char * +malloc (n) /* get a block */ + unsigned n; +{ + register struct mhead *p; + register unsigned int nbytes; + register int nunits = 0; + + /* Figure out how many bytes are required, rounding up to the nearest + multiple of 8, then figure out which nestf[] area to use. + Both the beginning of the header and the beginning of the + block should be on an eight byte boundary. */ + nbytes = (n + ((sizeof *p + 7) & ~7) + EXTRA + 7) & ~7; + { + register unsigned int shiftr = (nbytes - 1) >> 2; + + while (shiftr >>= 1) + nunits++; + } + + /* In case this is reentrant use of malloc from signal handler, + pick a block size that no other malloc level is currently + trying to allocate. That's the easiest harmless way not to + interfere with the other level of execution. */ + while (busy[nunits]) nunits++; + busy[nunits] = 1; + + /* If there are no blocks of the appropriate size, go get some */ + /* COULD SPLIT UP A LARGER BLOCK HERE ... ACT */ + if (nextf[nunits] == 0) + morecore (nunits); + + /* Get one block off the list, and set the new list head */ + if ((p = nextf[nunits]) == 0) + { + busy[nunits] = 0; + return 0; + } + nextf[nunits] = CHAIN (p); + busy[nunits] = 0; + + /* Check for free block clobbered */ + /* If not for this check, we would gobble a clobbered free chain ptr */ + /* and bomb out on the NEXT allocate of this size block */ + if (p -> mh_alloc != ISFREE || p -> mh_index != nunits) +#ifdef rcheck + botch ("block on free list clobbered"); +#else /* not rcheck */ + abort (); +#endif /* not rcheck */ + + /* Fill in the info, and if range checking, set up the magic numbers */ + p -> mh_alloc = ISALLOC; +#ifdef rcheck + p -> mh_nbytes = n; + p -> mh_magic4 = MAGIC4; + { + /* Get the location n after the beginning of the user's space. */ + register char *m = (char *) p + ((sizeof *p + 7) & ~7) + n; + + *m++ = MAGIC1, *m++ = MAGIC1, *m++ = MAGIC1, *m = MAGIC1; + } +#else /* not rcheck */ + p -> mh_size = n; +#endif /* not rcheck */ +#ifdef MSTATS + nmalloc[nunits]++; + nmal++; +#endif /* MSTATS */ + return (char *) p + ((sizeof *p + 7) & ~7); +} + +free (mem) + char *mem; +{ + register struct mhead *p; + { + register char *ap = mem; + + if (ap == 0) + return; + + p = (struct mhead *) (ap - ((sizeof *p + 7) & ~7)); + if (p -> mh_alloc == ISMEMALIGN) + { + ap -= p->mh_size; + p = (struct mhead *) (ap - ((sizeof *p + 7) & ~7)); + } + +#ifndef rcheck + if (p -> mh_alloc != ISALLOC) + abort (); + +#else rcheck + if (p -> mh_alloc != ISALLOC) + { + if (p -> mh_alloc == ISFREE) + botch ("free: Called with already freed block argument\n"); + else + botch ("free: Called with bad argument\n"); + } + + ASSERT (p -> mh_magic4 == MAGIC4); + ap += p -> mh_nbytes; + ASSERT (*ap++ == MAGIC1); ASSERT (*ap++ == MAGIC1); + ASSERT (*ap++ == MAGIC1); ASSERT (*ap == MAGIC1); +#endif /* rcheck */ + } + { + register int nunits = p -> mh_index; + + ASSERT (nunits <= 29); + p -> mh_alloc = ISFREE; + + /* Protect against signal handlers calling malloc. */ + busy[nunits] = 1; + /* Put this block on the free list. */ + CHAIN (p) = nextf[nunits]; + nextf[nunits] = p; + busy[nunits] = 0; + +#ifdef MSTATS + nmalloc[nunits]--; + nfre++; +#endif /* MSTATS */ + } +} + +char * +realloc (mem, n) + char *mem; + register unsigned n; +{ + register struct mhead *p; + register unsigned int tocopy; + register unsigned int nbytes; + register int nunits; + + if (mem == 0) + return malloc (n); + p = (struct mhead *) (mem - ((sizeof *p + 7) & ~7)); + nunits = p -> mh_index; + ASSERT (p -> mh_alloc == ISALLOC); +#ifdef rcheck + ASSERT (p -> mh_magic4 == MAGIC4); + { + register char *m = mem + (tocopy = p -> mh_nbytes); + ASSERT (*m++ == MAGIC1); ASSERT (*m++ == MAGIC1); + ASSERT (*m++ == MAGIC1); ASSERT (*m == MAGIC1); + } +#else /* not rcheck */ + if (p -> mh_index >= 13) + tocopy = (1 << (p -> mh_index + 3)) - ((sizeof *p + 7) & ~7); + else + tocopy = p -> mh_size; +#endif /* not rcheck */ + + /* See if desired size rounds to same power of 2 as actual size. */ + nbytes = (n + ((sizeof *p + 7) & ~7) + EXTRA + 7) & ~7; + + /* If ok, use the same block, just marking its size as changed. */ + if (nbytes > (4 << nunits) && nbytes <= (8 << nunits)) + { +#ifdef rcheck + register char *m = mem + tocopy; + *m++ = 0; *m++ = 0; *m++ = 0; *m++ = 0; + p-> mh_nbytes = n; + m = mem + n; + *m++ = MAGIC1; *m++ = MAGIC1; *m++ = MAGIC1; *m++ = MAGIC1; +#else /* not rcheck */ + p -> mh_size = n; +#endif /* not rcheck */ + return mem; + } + + if (n < tocopy) + tocopy = n; + { + register char *new; + + if ((new = malloc (n)) == 0) + return 0; + bcopy (mem, new, tocopy); + free (mem); + return new; + } +} + +/* This is in case something linked with Emacs calls calloc. */ + +char * +calloc (num, size) + unsigned num, size; +{ + register char *mem; + + num *= size; + mem = malloc (num); + if (mem != 0) + bzero (mem, num); + return mem; +} + +#ifndef VMS + +char * +memalign (alignment, size) + unsigned alignment, size; +{ + register char *ptr = malloc (size + alignment); + register char *aligned; + register struct mhead *p; + + if (ptr == 0) + return 0; + /* If entire block has the desired alignment, just accept it. */ + if (((int) ptr & (alignment - 1)) == 0) + return ptr; + /* Otherwise, get address of byte in the block that has that alignment. */ + aligned = (char *) (((int) ptr + alignment - 1) & -alignment); + + /* Store a suitable indication of how to free the block, + so that free can find the true beginning of it. */ + p = (struct mhead *) (aligned - ((7 + sizeof (struct mhead)) & ~7)); + p -> mh_size = aligned - ptr; + p -> mh_alloc = ISMEMALIGN; + return aligned; +} + +#ifndef HPUX +/* This runs into trouble with getpagesize on HPUX. + Patching out seems cleaner than the ugly fix needed. */ +char * +valloc (size) +{ + return memalign (getpagesize (), size); +} +#endif /* not HPUX */ +#endif /* not VMS */ + +#ifdef MSTATS +/* Return statistics describing allocation of blocks of size 2**n. */ + +struct mstats_value + { + int blocksize; + int nfree; + int nused; + }; + +struct mstats_value +malloc_stats (size) + int size; +{ + struct mstats_value v; + register int i; + register struct mhead *p; + + v.nfree = 0; + + if (size < 0 || size >= 30) + { + v.blocksize = 0; + v.nused = 0; + return v; + } + + v.blocksize = 1 << (size + 3); + v.nused = nmalloc[size]; + + for (p = nextf[size]; p; p = CHAIN (p)) + v.nfree++; + + return v; +} +int +malloc_mem_used () +{ + int i; + int size_used; + + size_used = 0; + + for (i = 0; i < 30; i++) + { + int allocation_size = 1 << (i + 3); + struct mhead *p; + + size_used += nmalloc[i] * allocation_size; + } + + return size_used; +} + +int +malloc_mem_free () +{ + int i; + int size_unused; + + size_unused = 0; + + for (i = 0; i < 30; i++) + { + int allocation_size = 1 << (i + 3); + struct mhead *p; + + for (p = nextf[i]; p ; p = CHAIN (p)) + size_unused += allocation_size; + } + + return size_unused; +} +#endif /* MSTATS */ + +/* + * This function returns the total number of bytes that the process + * will be allowed to allocate via the sbrk(2) system call. On + * BSD systems this is the total space allocatable to stack and + * data. On USG systems this is the data space only. + */ + +#ifdef USG + +get_lim_data () +{ + extern long ulimit (); + +#ifdef ULIMIT_BREAK_VALUE + lim_data = ULIMIT_BREAK_VALUE; +#else + lim_data = ulimit (3, 0); +#endif + + lim_data -= (long) data_space_start; +} + +#else /* not USG */ +#if defined (BSD4_1) || defined (VMS) + +get_lim_data () +{ + lim_data = vlimit (LIM_DATA, -1); +} + +#else /* not BSD4_1 and not VMS */ + +get_lim_data () +{ + struct rlimit XXrlimit; + + getrlimit (RLIMIT_DATA, &XXrlimit); +#ifdef RLIM_INFINITY + lim_data = XXrlimit.rlim_cur & RLIM_INFINITY; /* soft limit */ +#else + lim_data = XXrlimit.rlim_cur; /* soft limit */ +#endif +} + +#endif /* not BSD4_1 and not VMS */ +#endif /* not USG */ + +#ifdef VMS +/* There is a problem when dumping and restoring things on VMS. Calls + * to SBRK don't necessarily result in contiguous allocation. Dumping + * doesn't work when it isn't. Therefore, we make the initial + * allocation contiguous by allocating a big chunk, and do SBRKs from + * there. Once Emacs has dumped there is no reason to continue + * contiguous allocation, malloc doesn't depend on it. + * + * There is a further problem of using brk and sbrk while using VMS C + * run time library routines malloc, calloc, etc. The documentation + * says that this is a no-no, although I'm not sure why this would be + * a problem. In any case, we remove the necessity to call brk and + * sbrk, by calling calloc (to assure zero filled data) rather than + * sbrk. + * + * VMS_ALLOCATION_SIZE is the size of the allocation array. This + * should be larger than the malloc size before dumping. Making this + * too large will result in the startup procedure slowing down since + * it will require more space and time to map it in. + * + * The value for VMS_ALLOCATION_SIZE in the following define was determined + * by running emacs linked (and a large allocation) with the debugger and + * looking to see how much storage was used. The allocation was 201 pages, + * so I rounded it up to a power of two. + */ +#ifndef VMS_ALLOCATION_SIZE +#define VMS_ALLOCATION_SIZE (512*256) +#endif + +/* Use VMS RTL definitions */ +#undef sbrk +#undef brk +#undef malloc +int vms_out_initial = 0; +char vms_initial_buffer[VMS_ALLOCATION_SIZE]; +static char *vms_current_brk = &vms_initial_buffer; +static char *vms_end_brk = &vms_initial_buffer[VMS_ALLOCATION_SIZE-1]; + +#include + +char * +sys_sbrk (incr) + int incr; +{ + char *sbrk(), *temp, *ptr; + + if (vms_out_initial) + { + /* out of initial allocation... */ + if (!(temp = malloc (incr))) + temp = (char *) -1; + } + else + { + /* otherwise, go out of our area */ + ptr = vms_current_brk + incr; /* new current_brk */ + if (ptr <= vms_end_brk) + { + temp = vms_current_brk; + vms_current_brk = ptr; + } + else + { + vms_out_initial = 1; /* mark as out of initial allocation */ + if (!(temp = malloc (incr))) + temp = (char *) -1; + } + } + return temp; +} +#endif /* VMS */ diff --git a/gnu/lib/libregex/test/fileregex.c b/gnu/lib/libregex/test/fileregex.c new file mode 100644 index 0000000000..2c27a0f5dd --- /dev/null +++ b/gnu/lib/libregex/test/fileregex.c @@ -0,0 +1,77 @@ +#include +#include +#include "regex.h" + +#define BYTEWIDTH 8 + +/* Sorry, but this is just a test program. */ +#define LINE_MAX 500 + +int +main (argc, argv) + int argc; + char *argv[]; +{ + FILE *f; + char *filename; + char pat[500]; /* Sorry for that maximum size, too. */ + char line[LINE_MAX]; + struct re_pattern_buffer buf; + char fastmap[(1 << BYTEWIDTH)]; + const char *compile_ret; + unsigned lineno = 1; + unsigned nfound = 0; + + /* Actually, it might be useful to allow the data file to be standard + input, and to specify the pattern on the command line. */ + if (argc != 2) + { + fprintf (stderr, "Usage: %s .\n", argv[0]); + exit (1); + } + + filename = argv[1]; + f = fopen (filename, "r"); + if (f == NULL) + perror (filename); + + buf.allocated = 0; + buf.buffer = NULL; + buf.fastmap = fastmap; + + printf ("Pattern = ", pat); + gets (pat); + + if (feof (stdin)) + { + putchar ('\n'); + exit (0); + } + + compile_ret = re_compile_pattern (pat, strlen (pat), &buf); + if (compile_ret != NULL) + { + fprintf (stderr, "%s: %s\n", pat, compile_ret); + exit (1); + } + + while (fgets (line, LINE_MAX, f) != NULL) + { + size_t len = strlen (line); + struct re_registers regs; + int search_ret + = re_search_2 (&buf, NULL, 0, line, len, 0, len, ®s, len); + + if (search_ret == -2) + { + fprintf (stderr, "%s:%d: re_search failed.\n", filename, lineno); + exit (1); + } + + nfound += search_ret != -1; + lineno++; + } + + printf ("Matches found: %u (out of %u lines).\n", nfound, lineno - 1); + return 0; +} diff --git a/gnu/lib/libregex/test/g++malloc.c b/gnu/lib/libregex/test/g++malloc.c new file mode 100644 index 0000000000..d55ce45643 --- /dev/null +++ b/gnu/lib/libregex/test/g++malloc.c @@ -0,0 +1,1288 @@ +#define inline + +/* +Copyright (C) 1989 Free Software Foundation + written by Doug Lea (dl@oswego.edu) + +This file is part of GNU CC. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY. No author or distributor +accepts responsibility to anyone for the consequences of using it +or for whether it serves any particular purpose or works at all, +unless he says so in writing. Refer to the GNU CC General Public +License for full details. + +Everyone is granted permission to copy, modify and redistribute +GNU CC, but only under the conditions described in the +GNU CC General Public License. A copy of this license is +supposed to have been given to you along with GNU CC so you +can know your rights and responsibilities. It should be in a +file named COPYING. Among other things, the copyright notice +and this notice must be preserved on all copies. +*/ + + + +#ifndef NO_LIBGXX_MALLOC /* ignore whole file otherwise */ + +/* compile with -DMALLOC_STATS to collect statistics */ +/* collecting statistics slows down malloc by at least 15% */ + +#ifdef MALLOC_STATS +#define UPDATE_STATS(ARGS) {ARGS;} +#else +#define UPDATE_STATS(ARGS) +#endif + +/* History + + + Tue Jan 16 04:54:27 1990 Doug Lea (dl at g.oswego.edu) + + version 1 released in libg++ + + Sun Jan 21 05:52:47 1990 Doug Lea (dl at g.oswego.edu) + + bins are now own struct for, sanity. + + new victim search strategy: scan up and consolidate. + Both faster and less fragmentation. + + refined when to scan bins for consolidation, via consollink, etc. + + realloc: always try to expand chunk, avoiding some fragmentation. + + changed a few inlines into macros + + hardwired SBRK_UNIT to 4096 for uniformity across systems + + Tue Mar 20 14:18:23 1990 Doug Lea (dl at g.oswego.edu) + + calloc and cfree now correctly parameterized. + + Sun Apr 1 10:00:48 1990 Doug Lea (dl at g.oswego.edu) + + added memalign and valloc. + + Sun Jun 24 05:46:48 1990 Doug Lea (dl at g.oswego.edu) + + #include gepagesize.h only ifndef sun + cache pagesize after first call + + Wed Jul 25 08:35:19 1990 Doug Lea (dl at g.oswego.edu) + + No longer rely on a `designated victim': + + 1. It sometimes caused splits of large chunks + when smaller ones would do, leading to + bad worst-case fragmentation. + + 2. Scanning through the av array fast anyway, + so the overhead isn't worth it. + + To compensate, several other minor changes: + + 1. Unusable chunks are checked for consolidation during + searches inside bins, better distributing chunks + across bins. + + 2. Chunks are returned when found in malloc_find_space, + rather than finishing cleaning everything up, to + avoid wasted iterations due to (1). +*/ + +/* + A version of malloc/free/realloc tuned for C++ applications. + + Here's what you probably want to know first: + + In various tests, this appears to be about as fast as, + and usually substantially less memory-wasteful than BSD/GNUemacs malloc. + + Generally, it is slower (by perhaps 20%) than bsd-style malloc + only when bsd malloc would waste a great deal of space in + fragmented blocks, which this malloc recovers; or when, by + chance or design, nearly all requests are near the bsd malloc + power-of-2 allocation bin boundaries, and as many chunks are + used as are allocated. + + It uses more space than bsd malloc only when, again by chance + or design, only bsdmalloc bin-sized requests are malloced, or when + little dynamic space is malloced, since this malloc may grab larger + chunks from the system at a time than bsd. + + In other words, this malloc seems generally superior to bsd + except perhaps for programs that are specially tuned to + deal with bsdmalloc's characteristics. But even here, the + performance differences are slight. + + + This malloc, like any other, is a compromised design. + + + Chunks of memory are maintained using a `boundary tag' method as + described in e.g., Knuth or Standish. This means that the size of + the chunk is stored both in the front of the chunk and at the end. + This makes consolidating fragmented chunks into bigger chunks very fast. + The size field is also used to hold bits representing whether a + chunk is free or in use. + + Malloced chunks have space overhead of 8 bytes: The preceding + and trailing size fields. When they are freed, the list pointer + fields are also needed. + + Available chunks are kept in doubly linked lists. The lists are + maintained in an array of bins using a power-of-two method, except + that instead of 32 bins (one for each 1 << i), there are 128: each + power of two is split in quarters. The use of very fine bin sizes + closely approximates the use of one bin per actually used size, + without necessitating the overhead of locating such bins. It is + especially desirable in common C++ applications where large numbers + of identically-sized blocks are malloced/freed in some dynamic + manner, and then later are all freed. The finer bin sizes make + finding blocks fast, with little wasted overallocation. The + consolidation methods ensure that once the collection of blocks is + no longer useful, fragments are gathered into bigger chunks awaiting new + roles. + + The bins av[i] serve as heads of the lists. Bins contain a dummy + header for the chunk lists, and a `dirty' field used to indicate + whether the list may need to be scanned for consolidation. + + On allocation, the bin corresponding to the request size is + scanned, and if there is a chunk with size >= requested, it + is split, if too big, and used. Chunks on the list which are + too small are examined for consolidation during this traversal. + + If no chunk exists in the list bigger bins are scanned in search of + a victim. + + If no victim can be found, then smaller bins are examined for + consolidation in order to construct a victim. + + Finally, if consolidation fails to come up with a usable chunk, + more space is obtained from the system. + + After a split, the remainder is placed on + the back of the appropriate bin list. (All freed chunks are placed + on fronts of lists. All remaindered or consolidated chunks are + placed on the rear. Correspondingly, searching within a bin + starts at the front, but finding victims is from the back. All + of this approximates the effect of having 2 kinds of lists per + bin: returned chunks vs unallocated chunks, but without the overhead + of maintaining 2 lists.) + + Deallocation (free) consists only of placing the chunk on + a list. + + Reallocation proceeds in the usual way. If a chunk can be extended, + it is, else a malloc-copy-free sequence is taken. + + memalign requests more than enough space from malloc, finds a + spot within that chunk that meets the alignment request, and + then possibly frees the leading and trailing space. Overreliance + on memalign is a sure way to fragment space. + + + Some other implementation matters: + + 8 byte alignment is currently hardwired into the design. Calling + memalign will return a chunk that is both 8-byte aligned, and + meets the requested alignment. + + The basic overhead of a used chunk is 8 bytes: 4 at the front and + 4 at the end. + + When a chunk is free, 8 additional bytes are needed for free list + pointers. Thus, the minimum allocatable size is 16 bytes. + + The existence of front and back overhead permits some reasonably + effective fence-bashing checks: The front and back fields must + be identical. This is checked only within free() and realloc(). + The checks are fast enough to be made non-optional. + + The overwriting of parts of freed memory with the freelist pointers + can also be very effective (albeit in an annoying way) in helping + users track down dangling pointers. + + User overwriting of freed space will often result in crashes + within malloc or free. + + These routines are also tuned to C++ in that free(0) is a noop and + a failed malloc automatically calls (*new_handler)(). + + malloc(0) returns a pointer to something of the minimum allocatable size. + + Additional memory is gathered from the system (via sbrk) in a + way that allows chunks obtained across different sbrk calls to + be consolidated, but does not require contiguous memory: Thus, + it should be safe to intersperse mallocs with other sbrk calls. + + This malloc is NOT designed to work in multiprocessing applications. + No semaphores or other concurrency control are provided to ensure + that multiple malloc or free calls don't run at the same time, + which could be disasterous. + + VERY heavy use of inlines is made, for clarity. If this malloc + is ported via a compiler without inlining capabilities, all + inlines should be transformed into macros -- making them non-inline + makes malloc at least twice as slow. + + +*/ + + +/* preliminaries */ + +#ifdef __cplusplus +#include +#else +#include "//usr/include/stdio.h" /* needed for error reporting */ +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef USG +extern void* memset(void*, int, int); +extern void* memcpy(void*, const void*, int); +/*inline void bzero(void* s, int l) { memset(s, 0, l); }*/ +#else +/*extern void bzero(void*, unsigned int);*/ +#endif + +/*extern void bcopy(void*, void*, unsigned int);*/ + +extern void* sbrk(unsigned int); + +/* Put this in instead of commmented out stuff above. */ +#define bcopy(s,d,n) memcpy((d),(s),(n)) +#define bcmp(s1,s2,n) memcmp((s1),(s2),(n)) +#define bzero(s,n) memset((s),0,(n)) + + +#ifdef __GNUC__ +extern volatile void abort(); +#else +extern void abort(); +#endif + +#ifdef __cplusplus +}; /* end of extern "C" */ +#endif + + +/* A good multiple to call sbrk with */ + +#define SBRK_UNIT 4096 + + + +/* how to die on detected error */ + +#ifdef __GNUC__ +static volatile void malloc_user_error() +#else +static void malloc_user_error() +#endif +{ + fputs("malloc/free/realloc: clobbered space detected\n", stderr); abort(); +} + + + +/* Basic overhead for each malloc'ed chunk */ + + +struct malloc_chunk +{ + unsigned int size; /* Size in bytes, including overhead. */ + /* Or'ed with INUSE if in use. */ + + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; + +}; + +typedef struct malloc_chunk* mchunkptr; + +struct malloc_bin +{ + struct malloc_chunk hd; /* dummy list header */ + unsigned int dirty; /* True if maybe consolidatable */ + /* Wasting a word here makes */ + /* sizeof(bin) a power of 2, */ + /* which makes size2bin() faster */ +}; + +typedef struct malloc_bin* mbinptr; + + +/* sizes, alignments */ + + +#define SIZE_SZ (sizeof(unsigned int)) +#define MALLOC_MIN_OVERHEAD (SIZE_SZ + SIZE_SZ) +#define MALLOC_ALIGN_MASK (MALLOC_MIN_OVERHEAD - 1) + +#define MINSIZE (sizeof(struct malloc_chunk) + SIZE_SZ) /* MUST == 16! */ + + +/* pad request bytes into a usable size */ + +static inline unsigned int request2size(unsigned int request) +{ + return (request == 0) ? MINSIZE : + ((request + MALLOC_MIN_OVERHEAD + MALLOC_ALIGN_MASK) + & ~(MALLOC_ALIGN_MASK)); +} + + +static inline int aligned_OK(void* m) +{ + return ((unsigned int)(m) & (MALLOC_ALIGN_MASK)) == 0; +} + + +/* size field or'd with INUSE when in use */ +#define INUSE 0x1 + + + +/* the bins, initialized to have null double linked lists */ + +#define MAXBIN 120 /* 1 more than needed for 32 bit addresses */ + +#define FIRSTBIN (&(av[0])) + +static struct malloc_bin av[MAXBIN] = +{ + { { 0, &(av[0].hd), &(av[0].hd) }, 0 }, + { { 0, &(av[1].hd), &(av[1].hd) }, 0 }, + { { 0, &(av[2].hd), &(av[2].hd) }, 0 }, + { { 0, &(av[3].hd), &(av[3].hd) }, 0 }, + { { 0, &(av[4].hd), &(av[4].hd) }, 0 }, + { { 0, &(av[5].hd), &(av[5].hd) }, 0 }, + { { 0, &(av[6].hd), &(av[6].hd) }, 0 }, + { { 0, &(av[7].hd), &(av[7].hd) }, 0 }, + { { 0, &(av[8].hd), &(av[8].hd) }, 0 }, + { { 0, &(av[9].hd), &(av[9].hd) }, 0 }, + + { { 0, &(av[10].hd), &(av[10].hd) }, 0 }, + { { 0, &(av[11].hd), &(av[11].hd) }, 0 }, + { { 0, &(av[12].hd), &(av[12].hd) }, 0 }, + { { 0, &(av[13].hd), &(av[13].hd) }, 0 }, + { { 0, &(av[14].hd), &(av[14].hd) }, 0 }, + { { 0, &(av[15].hd), &(av[15].hd) }, 0 }, + { { 0, &(av[16].hd), &(av[16].hd) }, 0 }, + { { 0, &(av[17].hd), &(av[17].hd) }, 0 }, + { { 0, &(av[18].hd), &(av[18].hd) }, 0 }, + { { 0, &(av[19].hd), &(av[19].hd) }, 0 }, + + { { 0, &(av[20].hd), &(av[20].hd) }, 0 }, + { { 0, &(av[21].hd), &(av[21].hd) }, 0 }, + { { 0, &(av[22].hd), &(av[22].hd) }, 0 }, + { { 0, &(av[23].hd), &(av[23].hd) }, 0 }, + { { 0, &(av[24].hd), &(av[24].hd) }, 0 }, + { { 0, &(av[25].hd), &(av[25].hd) }, 0 }, + { { 0, &(av[26].hd), &(av[26].hd) }, 0 }, + { { 0, &(av[27].hd), &(av[27].hd) }, 0 }, + { { 0, &(av[28].hd), &(av[28].hd) }, 0 }, + { { 0, &(av[29].hd), &(av[29].hd) }, 0 }, + + { { 0, &(av[30].hd), &(av[30].hd) }, 0 }, + { { 0, &(av[31].hd), &(av[31].hd) }, 0 }, + { { 0, &(av[32].hd), &(av[32].hd) }, 0 }, + { { 0, &(av[33].hd), &(av[33].hd) }, 0 }, + { { 0, &(av[34].hd), &(av[34].hd) }, 0 }, + { { 0, &(av[35].hd), &(av[35].hd) }, 0 }, + { { 0, &(av[36].hd), &(av[36].hd) }, 0 }, + { { 0, &(av[37].hd), &(av[37].hd) }, 0 }, + { { 0, &(av[38].hd), &(av[38].hd) }, 0 }, + { { 0, &(av[39].hd), &(av[39].hd) }, 0 }, + + { { 0, &(av[40].hd), &(av[40].hd) }, 0 }, + { { 0, &(av[41].hd), &(av[41].hd) }, 0 }, + { { 0, &(av[42].hd), &(av[42].hd) }, 0 }, + { { 0, &(av[43].hd), &(av[43].hd) }, 0 }, + { { 0, &(av[44].hd), &(av[44].hd) }, 0 }, + { { 0, &(av[45].hd), &(av[45].hd) }, 0 }, + { { 0, &(av[46].hd), &(av[46].hd) }, 0 }, + { { 0, &(av[47].hd), &(av[47].hd) }, 0 }, + { { 0, &(av[48].hd), &(av[48].hd) }, 0 }, + { { 0, &(av[49].hd), &(av[49].hd) }, 0 }, + + { { 0, &(av[50].hd), &(av[50].hd) }, 0 }, + { { 0, &(av[51].hd), &(av[51].hd) }, 0 }, + { { 0, &(av[52].hd), &(av[52].hd) }, 0 }, + { { 0, &(av[53].hd), &(av[53].hd) }, 0 }, + { { 0, &(av[54].hd), &(av[54].hd) }, 0 }, + { { 0, &(av[55].hd), &(av[55].hd) }, 0 }, + { { 0, &(av[56].hd), &(av[56].hd) }, 0 }, + { { 0, &(av[57].hd), &(av[57].hd) }, 0 }, + { { 0, &(av[58].hd), &(av[58].hd) }, 0 }, + { { 0, &(av[59].hd), &(av[59].hd) }, 0 }, + + { { 0, &(av[60].hd), &(av[60].hd) }, 0 }, + { { 0, &(av[61].hd), &(av[61].hd) }, 0 }, + { { 0, &(av[62].hd), &(av[62].hd) }, 0 }, + { { 0, &(av[63].hd), &(av[63].hd) }, 0 }, + { { 0, &(av[64].hd), &(av[64].hd) }, 0 }, + { { 0, &(av[65].hd), &(av[65].hd) }, 0 }, + { { 0, &(av[66].hd), &(av[66].hd) }, 0 }, + { { 0, &(av[67].hd), &(av[67].hd) }, 0 }, + { { 0, &(av[68].hd), &(av[68].hd) }, 0 }, + { { 0, &(av[69].hd), &(av[69].hd) }, 0 }, + + { { 0, &(av[70].hd), &(av[70].hd) }, 0 }, + { { 0, &(av[71].hd), &(av[71].hd) }, 0 }, + { { 0, &(av[72].hd), &(av[72].hd) }, 0 }, + { { 0, &(av[73].hd), &(av[73].hd) }, 0 }, + { { 0, &(av[74].hd), &(av[74].hd) }, 0 }, + { { 0, &(av[75].hd), &(av[75].hd) }, 0 }, + { { 0, &(av[76].hd), &(av[76].hd) }, 0 }, + { { 0, &(av[77].hd), &(av[77].hd) }, 0 }, + { { 0, &(av[78].hd), &(av[78].hd) }, 0 }, + { { 0, &(av[79].hd), &(av[79].hd) }, 0 }, + + { { 0, &(av[80].hd), &(av[80].hd) }, 0 }, + { { 0, &(av[81].hd), &(av[81].hd) }, 0 }, + { { 0, &(av[82].hd), &(av[82].hd) }, 0 }, + { { 0, &(av[83].hd), &(av[83].hd) }, 0 }, + { { 0, &(av[84].hd), &(av[84].hd) }, 0 }, + { { 0, &(av[85].hd), &(av[85].hd) }, 0 }, + { { 0, &(av[86].hd), &(av[86].hd) }, 0 }, + { { 0, &(av[87].hd), &(av[87].hd) }, 0 }, + { { 0, &(av[88].hd), &(av[88].hd) }, 0 }, + { { 0, &(av[89].hd), &(av[89].hd) }, 0 }, + + { { 0, &(av[90].hd), &(av[90].hd) }, 0 }, + { { 0, &(av[91].hd), &(av[91].hd) }, 0 }, + { { 0, &(av[92].hd), &(av[92].hd) }, 0 }, + { { 0, &(av[93].hd), &(av[93].hd) }, 0 }, + { { 0, &(av[94].hd), &(av[94].hd) }, 0 }, + { { 0, &(av[95].hd), &(av[95].hd) }, 0 }, + { { 0, &(av[96].hd), &(av[96].hd) }, 0 }, + { { 0, &(av[97].hd), &(av[97].hd) }, 0 }, + { { 0, &(av[98].hd), &(av[98].hd) }, 0 }, + { { 0, &(av[99].hd), &(av[99].hd) }, 0 }, + + { { 0, &(av[100].hd), &(av[100].hd) }, 0 }, + { { 0, &(av[101].hd), &(av[101].hd) }, 0 }, + { { 0, &(av[102].hd), &(av[102].hd) }, 0 }, + { { 0, &(av[103].hd), &(av[103].hd) }, 0 }, + { { 0, &(av[104].hd), &(av[104].hd) }, 0 }, + { { 0, &(av[105].hd), &(av[105].hd) }, 0 }, + { { 0, &(av[106].hd), &(av[106].hd) }, 0 }, + { { 0, &(av[107].hd), &(av[107].hd) }, 0 }, + { { 0, &(av[108].hd), &(av[108].hd) }, 0 }, + { { 0, &(av[109].hd), &(av[109].hd) }, 0 }, + + { { 0, &(av[110].hd), &(av[110].hd) }, 0 }, + { { 0, &(av[111].hd), &(av[111].hd) }, 0 }, + { { 0, &(av[112].hd), &(av[112].hd) }, 0 }, + { { 0, &(av[113].hd), &(av[113].hd) }, 0 }, + { { 0, &(av[114].hd), &(av[114].hd) }, 0 }, + { { 0, &(av[115].hd), &(av[115].hd) }, 0 }, + { { 0, &(av[116].hd), &(av[116].hd) }, 0 }, + { { 0, &(av[117].hd), &(av[117].hd) }, 0 }, + { { 0, &(av[118].hd), &(av[118].hd) }, 0 }, + { { 0, &(av[119].hd), &(av[119].hd) }, 0 } +}; + +/* + indexing into bins +*/ + +static inline mbinptr size2bin(unsigned int sz) +{ + mbinptr b = av; + while (sz >= (MINSIZE * 2)) { b += 4; sz >>= 1; } /* find power of 2 */ + b += (sz - MINSIZE) >> 2; /* find quadrant */ + return b; +} + + + +/* counts maintained if MALLOC_STATS defined */ + +#ifdef MALLOC_STATS + +static unsigned int sbrked_mem; +static unsigned int requested_mem; +static unsigned int malloced_mem; +static unsigned int freed_mem; +static unsigned int max_used_mem; + +static unsigned int n_sbrks; +static unsigned int n_mallocs; +static unsigned int n_frees; +static unsigned int n_reallocs; +static unsigned int n_reallocs_with_copy; +static unsigned int n_avail; +static unsigned int max_inuse; + +static unsigned int n_malloc_chunks; +static unsigned int n_malloc_bins; + +static unsigned int n_split; +static unsigned int n_consol; + + +static void do_malloc_stats(const mchunkptr p) +{ + ++n_mallocs; + if ((n_mallocs-n_frees) > max_inuse) + max_inuse = n_mallocs - n_frees; + malloced_mem += (p->size & ~(INUSE)); + if (malloced_mem - freed_mem > max_used_mem) + max_used_mem = malloced_mem - freed_mem; +} + +static void do_free_stats(const mchunkptr p) +{ + ++n_frees; + freed_mem += (p->size & ~(INUSE)); +} + +#endif + + + +/* Utilities needed below for memalign */ +/* This is redundant with libg++ support, but not if used stand-alone */ + +static unsigned int gcd(unsigned int a, unsigned int b) +{ + unsigned int tmp; + + if (b > a) + { + tmp = a; a = b; b = tmp; + } + for(;;) + { + if (b == 0) + return a; + else if (b == 1) + return b; + else + { + tmp = b; + b = a % b; + a = tmp; + } + } +} + +static inline unsigned int lcm(unsigned int x, unsigned int y) +{ + return x / gcd(x, y) * y; +} + + + +/* maintaining INUSE via size field */ + + +#define inuse(p) ((p)->size & INUSE) +#define set_inuse(p) ((p)->size |= INUSE) +#define clear_inuse(b) ((p)->size &= ~INUSE) + + +/* operations on malloc_chunk addresses */ + + +/* return ptr to next physical malloc_chunk */ + +#define next_chunk(p) ((mchunkptr)((char*)(p) + (p)->size)) + +/* return ptr to previous physical malloc_chunk */ + +#define prev_chunk(p) ((mchunkptr)((char*)(p)-((((int*)(p))[-1]) & ~(INUSE)))) + +/* place size at front and back of chunk */ + + +static inline void set_size(mchunkptr p, unsigned int sz) +{ + p->size = *((int*)((char*)(p) + sz - SIZE_SZ)) = sz; +} + + + + +/* conversion from malloc headers to user pointers, and back */ + +static inline void* chunk2mem(mchunkptr p) +{ + void *mem; + set_inuse(p); +mem = (void*)((char*)(p) + SIZE_SZ); + return mem; +} + +/* xxxx my own */ +mchunkptr sanity_check(void* mem) +{ + mchunkptr p = (mchunkptr)((char*)(mem) - SIZE_SZ); + + /* a quick sanity check */ + unsigned int sz = p->size & ~(INUSE); + if (p->size == sz || sz != *((int*)((char*)(p) + sz - SIZE_SZ))) + malloc_user_error(); + + return p; +} + + + + +static inline mchunkptr mem2chunk(void* mem) +{ + mchunkptr p = (mchunkptr)((char*)(mem) - SIZE_SZ); + + /* a quick sanity check */ + unsigned int sz = p->size & ~(INUSE); + if (p->size == sz || sz != *((int*)((char*)(p) + sz - SIZE_SZ))) + malloc_user_error(); + + p->size = sz; /* clears INUSE */ + return p; +} + + + +/* maintaining bins & pointers */ + + +/* maximum bin actually used */ + +static mbinptr malloc_maxbin = FIRSTBIN; + + +/* operations on lists inside bins */ + + +/* take a chunk off a list */ + +static inline void unlink(mchunkptr p) +{ + mchunkptr b = p->bk; + mchunkptr f = p->fd; + + f->bk = b; b->fd = f; + + UPDATE_STATS (--n_avail); +} + + + +/* split a chunk and place on the back of a list */ + +static inline void split(mchunkptr p, unsigned int offset) +{ + unsigned int room = p->size - offset; + if (room >= MINSIZE) + { + mbinptr bn = size2bin(room); /* new bin */ + mchunkptr h = &(bn->hd); /* its head */ + mchunkptr b = h->bk; /* old back element */ + mchunkptr t = (mchunkptr)((char*)(p) + offset); /* remaindered chunk */ + + /* set size */ + t->size = *((int*)((char*)(t) + room - SIZE_SZ)) = room; + + /* link up */ + t->bk = b; t->fd = h; h->bk = b->fd = t; + + /* adjust maxbin (h == b means was empty) */ + if (h == b && bn > malloc_maxbin) malloc_maxbin = bn; + + /* adjust size of chunk to be returned */ + p->size = *((int*)((char*)(p) + offset - SIZE_SZ)) = offset; + + UPDATE_STATS ((++n_split, ++n_avail)); + } +} + + + +/* place a consolidated chunk on the back of a list */ +/* like above, except no split */ + +static inline void consollink(mchunkptr p) +{ + mbinptr bn = size2bin(p->size); + mchunkptr h = &(bn->hd); + mchunkptr b = h->bk; + + p->bk = b; p->fd = h; h->bk = b->fd = p; + + if (h == b && bn > malloc_maxbin) malloc_maxbin = bn; + + UPDATE_STATS(++n_avail); +} + + +/* place a freed chunk on the front of a list */ + +static inline void frontlink(mchunkptr p) +{ + mbinptr bn = size2bin(p->size); + mchunkptr h = &(bn->hd); + mchunkptr f = h->fd; + + p->bk = h; p->fd = f; f->bk = h->fd = p; + + if (h == f && bn > malloc_maxbin) malloc_maxbin = bn; + + bn->dirty = 1; + + UPDATE_STATS(++n_avail); +} + + + +/* Dealing with sbrk */ + + +/* To link consecutive sbrk regions when possible */ + +static int* last_sbrk_end; + + +/* who to call when sbrk returns failure */ + +#ifndef NO_NEW_HANDLER +typedef volatile void (*vfp)(); +#ifdef __cplusplus +extern "C" vfp __new_handler; +#else +extern vfp __new_handler; +#endif +#endif + +static mchunkptr malloc_from_sys(unsigned nb) +{ + mchunkptr p; + unsigned int sbrk_size; + int* ip; + + /* Minimally, we need to pad with enough space */ + /* to place dummy size/use fields to ends if needed */ + + sbrk_size = ((nb + SBRK_UNIT - 1 + SIZE_SZ + SIZE_SZ) + / SBRK_UNIT) * SBRK_UNIT; + + ip = (int*)(sbrk(sbrk_size)); + if ((char*)ip == (char*)(-1)) /* sbrk returns -1 on failure */ + { +#ifndef NO_NEW_HANDLER + (*__new_handler) (); +#endif + return 0; + } + + UPDATE_STATS ((++n_sbrks, sbrked_mem += sbrk_size)); + + + if (last_sbrk_end != &ip[-1]) + { + /* It's either first time through or someone else called sbrk. */ + /* Arrange end-markers at front & back */ + + /* Shouldn't be necessary, but better to be safe */ + while (!aligned_OK(ip)) { ++ip; sbrk_size -= SIZE_SZ; } + + + /* Mark the front as in use to prevent merging. */ + /* Note we can get away with only 1 word, not MINSIZE overhead here */ + + *ip++ = SIZE_SZ | INUSE; + + p = (mchunkptr)ip; + set_size(p,sbrk_size - (SIZE_SZ + SIZE_SZ)); + + } + else + { + mchunkptr l; + + /* We can safely make the header start at end of prev sbrked chunk. */ + /* We will still have space left at the end from a previous call */ + /* to place the end marker, below */ + + p = (mchunkptr)(last_sbrk_end); + set_size(p, sbrk_size); + + + /* Even better, maybe we can merge with last fragment: */ + + l = prev_chunk(p); + if (!inuse(l)) + { + unlink(l); + set_size(l, p->size + l->size); + p = l; + } + + } + + /* mark the end of sbrked space as in use to prevent merging */ + + last_sbrk_end = (int*)((char*)p + p->size); + *last_sbrk_end = SIZE_SZ | INUSE; + + UPDATE_STATS((++n_avail, ++n_malloc_chunks)); + + /* make it safe to unlink in malloc */ + UPDATE_STATS(++n_avail); + p->fd = p->bk = p; + + return p; +} + + + +/* Consolidate dirty bins. */ +/* Stop if found a chunk big enough to satisfy current malloc request */ + +/* (It requires much less bookkeeping to consolidate entire bins */ +/* at once than to keep records of which chunks might be */ +/* consolidatable. So long as the lists are short, which we */ +/* try to ensure via small bin ranges, there is little wasted effort.) */ + +static mchunkptr malloc_find_space(unsigned int nb) +{ + mbinptr b; + + /* first, re-adjust max used bin */ + + while (malloc_maxbin >= FIRSTBIN && + malloc_maxbin->hd.bk == &(malloc_maxbin->hd)) + { + malloc_maxbin->dirty = 0; + --malloc_maxbin; + } + + for (b = malloc_maxbin; b >= FIRSTBIN; --b) + { + UPDATE_STATS(++n_malloc_bins); + + if (b->dirty) + { + mchunkptr h = &(b->hd); /* head of list */ + mchunkptr p = h->fd; /* chunk traverser */ + + while (p != h) + { + mchunkptr nextp = p->fd; /* save, in case of relinks */ + int consolidated = 0; /* only unlink/relink if consolidated */ + + mchunkptr t; + + UPDATE_STATS(++n_malloc_chunks); + + while (!inuse(t = prev_chunk(p))) /* consolidate backward */ + { + if (!consolidated) { consolidated = 1; unlink(p); } + if (t == nextp) nextp = t->fd; + unlink(t); + set_size(t, t->size + p->size); + p = t; + UPDATE_STATS (++n_consol); + } + + while (!inuse(t = next_chunk(p))) /* consolidate forward */ + { + if (!consolidated) { consolidated = 1; unlink(p); } + if (t == nextp) nextp = t->fd; + unlink(t); + set_size(p, p->size + t->size); + UPDATE_STATS (++n_consol); + } + + if (consolidated) + { + if (p->size >= nb) + { + /* make it safe to unlink in malloc */ + UPDATE_STATS(++n_avail); + p->fd = p->bk = p; + return p; + } + else + consollink(p); + } + + p = nextp; + + } + + b->dirty = 0; + + } + } + + /* nothing available - sbrk some more */ + + return malloc_from_sys(nb); +} + + + +/* Finally, the user-level functions */ + +void* malloc(unsigned int bytes) +{ + unsigned int nb = request2size(bytes); /* padded request size */ + mbinptr b = size2bin(nb); /* corresponding bin */ + mchunkptr hd = &(b->hd); /* head of its list */ + mchunkptr p = hd->fd; /* chunk traverser */ + + UPDATE_STATS((requested_mem+=bytes, ++n_malloc_bins)); + + /* Try a (near) exact match in own bin */ + /* clean out unusable but consolidatable chunks in bin while traversing */ + + while (p != hd) + { + UPDATE_STATS(++n_malloc_chunks); + if (p->size >= nb) + goto found; + else /* try to consolidate; same code as malloc_find_space */ + { + mchunkptr nextp = p->fd; /* save, in case of relinks */ + int consolidated = 0; /* only unlink/relink if consolidated */ + + mchunkptr t; + + while (!inuse(t = prev_chunk(p))) /* consolidate backward */ + { + if (!consolidated) { consolidated = 1; unlink(p); } + if (t == nextp) nextp = t->fd; + unlink(t); + set_size(t, t->size + p->size); + p = t; + UPDATE_STATS (++n_consol); + } + + while (!inuse(t = next_chunk(p))) /* consolidate forward */ + { + if (!consolidated) { consolidated = 1; unlink(p); } + if (t == nextp) nextp = t->fd; + unlink(t); + set_size(p, p->size + t->size); + UPDATE_STATS (++n_consol); + } + + if (consolidated) + { + if (p->size >= nb) + { + /* make it safe to unlink again below */ + UPDATE_STATS(++n_avail); + p->fd = p->bk = p; + goto found; + } + else + consollink(p); + } + + p = nextp; + + } + } + + b->dirty = 0; /* true if got here */ + + /* Scan bigger bins for a victim */ + + while (++b <= malloc_maxbin) + { + UPDATE_STATS(++n_malloc_bins); + if ((p = b->hd.bk) != &(b->hd)) /* no need to check size */ + goto found; + } + + /* Consolidate or sbrk */ + + p = malloc_find_space(nb); + + if (p == 0) return 0; /* allocation failure */ + + found: /* Use what we found */ + + unlink(p); + split(p, nb); + UPDATE_STATS(do_malloc_stats(p)); + return chunk2mem(p); +} + + + + +void free(void* mem) +{ + if (mem != 0) + { + mchunkptr p = mem2chunk(mem); + UPDATE_STATS(do_free_stats(p)); + frontlink(p); + } +} + + +void* calloc(unsigned int n, unsigned int elem_size) +{ + unsigned int sz = n * elem_size; + void* p = malloc(sz); + bzero(p, sz); + return p; +}; + +/* This is here for compatibility with older systems */ +void cfree(void *mem) +{ + free(mem); +} + + +unsigned int malloc_usable_size(void* mem) +{ + if (mem == 0) + return 0; + else + { + mchunkptr p = (mchunkptr)((char*)(mem) - SIZE_SZ); + unsigned int sz = p->size & ~(INUSE); + if (p->size == sz || sz != *((int*)((char*)(p) + sz - SIZE_SZ))) + return 0; + else + return sz - MALLOC_MIN_OVERHEAD; + } +} + + + +void* realloc(void* mem, unsigned int bytes) +{ + if (mem == 0) + return malloc(bytes); + else + { + unsigned int nb = request2size(bytes); + mchunkptr p = mem2chunk(mem); + unsigned int oldsize = p->size; + int room; + mchunkptr nxt; + + UPDATE_STATS((++n_reallocs, requested_mem += bytes-oldsize)); + + /* try to expand (even if already big enough), to clean up chunk */ + + while (!inuse(nxt = next_chunk(p))) + { + UPDATE_STATS ((malloced_mem += nxt->size, ++n_consol)); + unlink(nxt); + set_size(p, p->size + nxt->size); + } + + room = p->size - nb; + if (room >= 0) + { + split(p, nb); + UPDATE_STATS(malloced_mem -= room); + return chunk2mem(p); + } + else /* do the obvious */ + { + void* newmem; + set_inuse(p); /* don't let malloc consolidate us yet! */ + newmem = malloc(nb); + bcopy(mem, newmem, oldsize - SIZE_SZ); + free(mem); + UPDATE_STATS(++n_reallocs_with_copy); + return newmem; + } + } +} + + + +/* return a pointer to space with at least the alignment requested */ + +void* memalign(unsigned int alignment, unsigned int bytes) +{ + mchunkptr p; + unsigned int nb = request2size(bytes); + + /* find an alignment that both we and the user can live with: */ + /* least common multiple guarantees mutual happiness */ + unsigned int align = lcm(alignment, MALLOC_MIN_OVERHEAD); + unsigned int mask = align - 1; + + /* call malloc with worst case padding to hit alignment; */ + /* we will give back extra */ + + unsigned int req = nb + align + MINSIZE; + void* m = malloc(req); + + if (m == 0) return m; + + p = mem2chunk(m); + + /* keep statistics on track */ + + UPDATE_STATS(--n_mallocs); + UPDATE_STATS(malloced_mem -= p->size); + UPDATE_STATS(requested_mem -= req); + UPDATE_STATS(requested_mem += bytes); + + if (((int)(m) & (mask)) != 0) /* misaligned */ + { + + /* find an aligned spot inside chunk */ + + mchunkptr ap = (mchunkptr)(( ((int)(m) + mask) & -align) - SIZE_SZ); + + unsigned int gap = (unsigned int)(ap) - (unsigned int)(p); + unsigned int room; + + /* we need to give back leading space in a chunk of at least MINSIZE */ + + if (gap < MINSIZE) + { + /* This works since align >= MINSIZE */ + /* and we've malloc'd enough total room */ + + ap = (mchunkptr)( (int)(ap) + align ); + gap += align; + } + + if (gap + nb > p->size) /* can't happen unless chunk sizes corrupted */ + malloc_user_error(); + + room = p->size - gap; + + /* give back leader */ + set_size(p, gap); + consollink(p); + + /* use the rest */ + p = ap; + set_size(p, room); + } + + /* also give back spare room at the end */ + + split(p, nb); + UPDATE_STATS(do_malloc_stats(p)); + return chunk2mem(p); + +} + +#ifndef sun +#include "getpagesize.h" +#endif + +static unsigned int malloc_pagesize = 0; + +void* valloc(unsigned int bytes) +{ + if (malloc_pagesize == 0) malloc_pagesize = getpagesize(); + return memalign (malloc_pagesize, bytes); +} + + +void malloc_stats() +{ +#ifndef MALLOC_STATS +} +#else + int i; + mchunkptr p; + double nm = (double)(n_mallocs + n_reallocs); + + fprintf(stderr, "\nmalloc statistics\n\n"); + + if (n_mallocs != 0) + fprintf(stderr, "requests = %10u total size = %10u\tave = %10u\n", + n_mallocs, requested_mem, requested_mem/n_mallocs); + + if (n_mallocs != 0) + fprintf(stderr, "mallocs = %10u total size = %10u\tave = %10u\n", + n_mallocs, malloced_mem, malloced_mem/n_mallocs); + + if (n_frees != 0) + fprintf(stderr, "frees = %10u total size = %10u\tave = %10u\n", + n_frees, freed_mem, freed_mem/n_frees); + + if (n_mallocs-n_frees != 0) + fprintf(stderr, "in use = %10u total size = %10u\tave = %10u\n", + n_mallocs-n_frees, malloced_mem-freed_mem, + (malloced_mem-freed_mem) / (n_mallocs-n_frees)); + + if (max_inuse != 0) + fprintf(stderr, "max in use= %10u total size = %10u\tave = %10u\n", + max_inuse, max_used_mem, max_used_mem / max_inuse); + + if (n_avail != 0) + fprintf(stderr, "available = %10u total size = %10u\tave = %10u\n", + n_avail, sbrked_mem - (malloced_mem-freed_mem), + (sbrked_mem - (malloced_mem-freed_mem)) / n_avail); + + if (n_sbrks != 0) + fprintf(stderr, "sbrks = %10u total size = %10u\tave = %10u\n\n", + n_sbrks, sbrked_mem, sbrked_mem/ n_sbrks); + + if (n_reallocs != 0) + fprintf(stderr, "reallocs = %10u with copy = %10u\n\n", + n_reallocs, n_reallocs_with_copy); + + + if (nm != 0) + { + fprintf(stderr, "chunks scanned per malloc = %6.3f\n", + n_malloc_chunks / nm); + fprintf(stderr, "bins scanned per malloc = %6.3f\n", + n_malloc_bins / nm); + fprintf(stderr, "splits per malloc = %6.3f\n", + n_split / nm); + fprintf(stderr, "consolidations per malloc = %6.3f\n", + n_consol / nm); + } + + fprintf(stderr, "\nfree chunks:\n"); + for (i = 0; i < MAXBIN; ++i) + { + p = av[i].hd.fd; + if (p != &(av[i].hd)) + { + unsigned int count = 1; + unsigned int sz = p->size; + for (p = p->fd; p != &(av[i].hd); p = p->fd) + { + if (p->size == sz) + ++count; + else + { + fprintf(stderr, "\tsize = %10u count = %5u\n", sz, count); + count = 1; + sz = p->size; + } + } + + fprintf(stderr, "\tsize = %10u count = %5u\n", sz, count); + + } + } +} +#endif /* MALLOC_STATS */ + +#endif /* NO_LIBGXX_MALLOC */ + + diff --git a/gnu/lib/libregex/test/getpagesize.h b/gnu/lib/libregex/test/getpagesize.h new file mode 100644 index 0000000000..32adae61ef --- /dev/null +++ b/gnu/lib/libregex/test/getpagesize.h @@ -0,0 +1,25 @@ +#ifdef BSD +#ifndef BSD4_1 +#define HAVE_GETPAGESIZE +#endif +#endif + +#ifndef HAVE_GETPAGESIZE + +#include + +#ifdef EXEC_PAGESIZE +#define getpagesize() EXEC_PAGESIZE +#else +#ifdef NBPG +#define getpagesize() NBPG * CLSIZE +#ifndef CLSIZE +#define CLSIZE 1 +#endif /* no CLSIZE */ +#else /* no NBPG */ +#define getpagesize() NBPC +#endif /* no NBPG */ +#endif /* no EXEC_PAGESIZE */ + +#endif /* not HAVE_GETPAGESIZE */ + diff --git a/gnu/lib/libregex/test/iregex.c b/gnu/lib/libregex/test/iregex.c new file mode 100644 index 0000000000..2346d441fc --- /dev/null +++ b/gnu/lib/libregex/test/iregex.c @@ -0,0 +1,164 @@ +/* Main program for interactive testing. For maximum output, compile + this and regex.c with -DDEBUG. */ + +#include +#include +#include "regex.h" + +/* Don't bother to guess about vs , etc. */ +extern int strlen (); + +#define BYTEWIDTH 8 + +extern void printchar (); +extern char upcase[]; + +static void scanstring (); +static void print_regs (); + +int +main (argc, argv) + int argc; + char **argv; +{ + int i; + struct re_pattern_buffer buf; + char fastmap[(1 << BYTEWIDTH)]; + + /* Allow a command argument to specify the style of syntax. You can + use the `syntax' program to decode integer syntax values. */ + if (argc > 1) + re_set_syntax (atoi (argv[1])); + + buf.allocated = 0; + buf.buffer = NULL; + buf.fastmap = fastmap; + buf.translate = upcase; + + for (;;) + { + char pat[500], str[500]; + struct re_registers regs; + + /* Some C compilers don't like `char pat[500] = ""'. */ + pat[0] = 0; + + printf ("Pattern (%s) = ", pat); + gets (pat); + scanstring (pat); + + if (feof (stdin)) + { + putchar ('\n'); + exit (0); + } + + if (*pat) + { + re_compile_pattern (pat, strlen (pat), &buf); + re_compile_fastmap (&buf); +#ifdef DEBUG + print_compiled_pattern (&buf); +#endif + } + + printf ("String = "); + gets (str); /* Now read the string to match against */ + scanstring (str); + + i = re_match (&buf, str, strlen (str), 0, ®s); + printf ("Match value %d.\t", i); + if (i >= 0) + print_regs (regs); + putchar ('\n'); + + i = re_search (&buf, str, strlen (str), 0, strlen (str), ®s); + printf ("Search value %d.\t", i); + if (i >= 0) + print_regs (regs); + putchar ('\n'); + } + + /* We never get here, but what the heck. */ + return 0; +} + +void +scanstring (s) + char *s; +{ + char *write = s; + + while (*s != '\0') + { + if (*s == '\\') + { + s++; + + switch (*s) + { + case '\0': + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + *write = *s++ - '0'; + + if ('0' <= *s && *s <= '9') + { + *write = (*write << 3) + (*s++ - '0'); + if ('0' <= *s && *s <= '9') + *write = (*write << 3) + (*s++ - '0'); + } + write++; + break; + + case 'n': + *write++ = '\n'; + s++; + break; + + case 't': + *write++ = '\t'; + s++; + break; + + default: + *write++ = *s++; + break; + } + } + else + *write++ = *s++; + } + + *write++ = '\0'; +} + +/* Print REGS in human-readable form. */ + +void +print_regs (regs) + struct re_registers regs; +{ + int i, end; + + printf ("Registers: "); + + if (regs.num_regs == 0 || regs.start[0] == -1) + { + printf ("(none)"); + } + else + { + /* Find the last register pair that matched. */ + for (end = regs.num_regs - 1; end >= 0; end--) + if (regs.start[end] != -1) + break; + + printf ("[%d ", regs.start[0]); + for (i = 1; i <= end; i++) + printf ("(%d %d) ", regs.start[i], regs.end[i]); + printf ("%d]", regs.end[0]); + } +} diff --git a/gnu/lib/libregex/test/main.c b/gnu/lib/libregex/test/main.c new file mode 100644 index 0000000000..28ae315285 --- /dev/null +++ b/gnu/lib/libregex/test/main.c @@ -0,0 +1,49 @@ +/* Main routine for running various tests. Meant only to be linked with + all the auxiliary test source files, with `test' undefined. */ + +#include "test.h" + +test_type t = all_test; + + +/* Use this to run the tests we've thought of. */ + +int +main () +{ + switch (t) + { + case all_test: + test_regress (); + test_others (); + test_posix_basic (); + test_posix_extended (); + test_posix_interface (); + break; + + case other_test: + test_others (); + break; + + case posix_basic_test: + test_posix_basic (); + break; + + case posix_extended_test: + test_posix_extended (); + break; + + case posix_interface_test: + test_posix_interface (); + break; + + case regress_test: + test_regress (); + break; + + default: + fprintf (stderr, "Unknown test %d.\n", t); + } + + return 0; +} diff --git a/gnu/lib/libregex/test/malloc-test.c b/gnu/lib/libregex/test/malloc-test.c new file mode 100644 index 0000000000..7e27a15a89 --- /dev/null +++ b/gnu/lib/libregex/test/malloc-test.c @@ -0,0 +1,47 @@ + + +typedef struct { + unsigned *bits; + unsigned size; +} bits_list_type; + +#define BYTEWIDTH 8 +#define NULL 0 + +#define BITS_BLOCK_SIZE (sizeof (unsigned) * BYTEWIDTH) +#define BITS_BLOCK(position) ((position) / BITS_BLOCK_SIZE) +#define BITS_MASK(position) (1 << ((position) % BITS_BLOCK_SIZE)) + +static unsigned +init_bits_list (bits_list_ptr) + bits_list_type *bits_list_ptr; +{ + bits_list_ptr->bits = NULL; + bits_list_ptr->bits = (unsigned *) malloc (sizeof (unsigned)); + + if (bits_list_ptr->bits == NULL) + return 0; + + bits_list_ptr->bits[0] = (unsigned)0; + bits_list_ptr->size = BITS_BLOCK_SIZE; + + return 1; +} + + +main() +{ + bits_list_type dummy; + bits_list_type dummy_1; + bits_list_type dummy_2; + bits_list_type dummy_3; + + init_bits_list (&dummy); +printf("init 1\n"); + init_bits_list (&dummy_1); +printf("init 2\n"); + init_bits_list (&dummy_2); +printf("init 3\n"); + init_bits_list (&dummy_3); +printf("init 4\n"); +} diff --git a/gnu/lib/libregex/test/other.c b/gnu/lib/libregex/test/other.c new file mode 100644 index 0000000000..d2ceb38448 --- /dev/null +++ b/gnu/lib/libregex/test/other.c @@ -0,0 +1,503 @@ +/* other.c: test (not exhaustively) non-POSIX regular expressions. */ + +#include "test.h" + +void +test_others () +{ + struct re_registers regs; + + printf ("\nStarting non-POSIX tests.\n"); + t = other_test; + + test_should_match = true; + + /* The big question: does the group participate in the match, or match + the empty string? */ + re_set_syntax (RE_NO_BK_PARENS); + test_match ("(a*)*ab", "ab"); + TEST_REGISTERS ("(a*)*ab", "ab", 0, 2, 0, 0, -1, -1); + test_match ("(a*)*", ""); + TEST_REGISTERS ("(a*)*ab", "ab", 0, 0, 0, 0, -1, -1); + + /* This tests finding the highest and lowest active registers. */ + test_match ("(a(b)c(d(e)f)g)h(i(j)k(l(m)n)o)\\1\\2\\3\\4\\5\\6\\7\\8", + "abcdefghijklmnoabcdefgbdefeijklmnojlmnm"); + + /* Test that \< and \> match at the beginning and end of the string. */ + test_match ("\\", "abc"); + + /* May as well test \` and \' while we're at it. */ + test_match ("\\`abc\\'", "abc"); + +#if 0 + /* Test backreferencing and the fastmap -- which doesn't work. */ + test_fastmap ("(a)*\\1", "a", 0, 0); +#endif + + /* But at least we shouldn't search improperly. */ + test_search_return (-1, "(a)\\1", ""); + + re_set_syntax (RE_SYNTAX_EMACS); + + MATCH_SELF("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + MATCH_SELF ("a^"); + MATCH_SELF ("a^b"); + MATCH_SELF ("$a"); + MATCH_SELF ("a$b"); + + re_set_syntax (RE_BACKSLASH_ESCAPE_IN_LISTS); + test_match ("[\\^a]", "a"); + test_match ("[\\^a]", "^"); + + /* These op characters should be ordinary if RE_CONTEXT_INVALID_OPS + isn't set. */ + re_set_syntax (RE_NO_BK_VBAR | RE_NO_BK_BRACES | RE_INTERVALS + | RE_NO_BK_PARENS); + MATCH_SELF ("*"); + test_match ("a|*", "*"); + test_match ("(*)", "*"); + + MATCH_SELF ("+"); + test_match ("a|+", "+"); + test_match ("(+)", "+"); + + MATCH_SELF ("?"); + test_match ("a|?", "?"); + test_match ("(?)", "?"); + + MATCH_SELF ("{1}"); + test_match ("a|{1}", "a"); + test_match ("a|{1}", "{1}"); + test_match ("({1})", "{1}"); + + test_match ("\\{", "{"); + + + re_set_syntax (RE_LIMITED_OPS); + MATCH_SELF ("|"); + MATCH_SELF ("a|"); + MATCH_SELF ("a|"); + MATCH_SELF ("a||"); + MATCH_SELF ("a||"); + MATCH_SELF ("(|)"); + + re_set_syntax (RE_SYNTAX_EMACS); + TEST_SEARCH ("^a", "b\na", 0, 3); + TEST_SEARCH ("b$", "b\na", 0, 3); + +#if 0 + /* Newline is no longer special for anchors (16 Sep 92). --karl */ + test_match_2 ("a\n^b", "a", "\nb"); + test_match_2 ("a$\nb", "a\n", "b"); +#endif + + /* Test grouping. */ + re_set_syntax (RE_NO_BK_PARENS); + + test_match ("()", ""); + test_fastmap ("()", "", 0, 0); + TEST_REGISTERS ("()", "", 0, 0, 0, 0, -1, -1); + + test_match ("((((((((()))))))))", ""); + test_fastmap ("((((((((()))))))))", "", 0, 0); + test_match ("a()b", "ab"); + TEST_REGISTERS ("a()b", "ab", 0, 2, 1, 1, -1, -1); + + test_match ("(((((((((())))))))))", ""); + test_fastmap ("(((((((((())))))))))", "", 0, 0); + + test_match ("()*", ""); + TEST_REGISTERS ("()*", "", 0, 0, 0, 0, -1, -1); /* empty string */ + test_match ("(())*", ""); + + re_set_syntax (RE_CONTEXT_INDEP_OPS); + test_match ("*", ""); + + re_set_syntax (RE_INTERVALS | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES); + test_match ("{1}", ""); /* Should remain an interval. */ + MATCH_SELF ("{1"); /* Not a valid interval. */ + + re_set_syntax (RE_NEWLINE_ALT); + test_match ("a\nb", "a"); + test_match ("a\nb", "b"); + + re_set_syntax (RE_NO_BK_VBAR | RE_NO_BK_PARENS); + test_match ("^a", "a"); + test_match ("(^a)", "a"); + test_match ("(a|^b)", "b"); + test_match ("a$", "a"); + test_match ("(a$)", "a"); + test_match ("a$|b", "a"); + + /* You should be able to have empty alternatives if RE_NO_EMPTY_ALTS + isn't set. */ + re_set_syntax (RE_NO_BK_VBAR | RE_NO_BK_PARENS); + + test_match ("|", ""); + test_match ("^|a", ""); + test_match ("^|a", "a"); + test_match ("a|", ""); + test_match ("a|", "a"); + test_match ("a|$", ""); + test_match ("a|$", "a"); + test_match ("a||b", "a"); + test_match ("a||b", ""); + test_match ("a||b", "b"); + test_match ("(|a)", ""); + test_match ("(|a)", "a"); + test_match ("(a|)", ""); + test_match ("(a|)", "a"); + + TEST_SEARCH ("a|$", "xa", 0, 2); + TEST_SEARCH ("a|$", "x", 0, 1); + TEST_SEARCH ("$|b", "x", 0, 1); + TEST_SEARCH ("$|b", "xb", 0, 2); + TEST_SEARCH ("c(a|$)", "xca", 0, 3); + TEST_SEARCH ("c(a|$)", "xc", 0, 2); + TEST_SEARCH ("c($|b)", "xcb", 0, 3); + TEST_SEARCH ("c($|b)", "xc", 0, 2); + TEST_SEARCH ("c($|b$)", "xcb", 0, 3); + TEST_SEARCH ("c($|b$)", "xc", 0, 2); + TEST_SEARCH ("c(a$|$)", "xca", 0, 3); + TEST_SEARCH ("c(a$|$)", "xc", 0, 2); + TEST_SEARCH ("(a$|b$)|$", "x", 0, 1); + TEST_SEARCH ("(a$|b$)|$", "xa", 0, 2); + TEST_SEARCH ("(a$|b$)|$", "xb", 0, 2); + TEST_SEARCH ("(a$|$)|c$", "x", 0, 1); + TEST_SEARCH ("(a$|$)|c$", "xa", 0, 2); + TEST_SEARCH ("(a$|$)|c$", "xc", 0, 2); + TEST_SEARCH ("($|b$)|c$", "x", 0, 1); + TEST_SEARCH ("($|b$)|c$", "xb", 0, 2); + TEST_SEARCH ("($|b$)|c$", "xc", 0, 2); + TEST_SEARCH ("c$|(a$|$)", "x", 0, 1); + TEST_SEARCH ("c$|(a$|$)", "xa", 0, 2); + TEST_SEARCH ("c$|(a$|$)", "xc", 0, 2); + TEST_SEARCH ("c$|($|b$)", "x", 0, 1); + TEST_SEARCH ("c$|($|b$)", "xb", 0, 2); + TEST_SEARCH ("c$|($|b$)", "xc", 0, 2); + TEST_SEARCH ("$|(a$|b$)", "x", 0, 1); + TEST_SEARCH ("$|(a$|b$)", "xa", 0, 2); + TEST_SEARCH ("$|(a$|b$)", "xb", 0, 2); + TEST_SEARCH ("c(a$|b$)|$", "x", 0, 1); + TEST_SEARCH ("c(a$|b$)|$", "xca", 0, 3); + TEST_SEARCH ("c(a$|b$)|$", "xcb", 0, 3); + TEST_SEARCH ("c(a$|$)|d$", "xc", 0, 2); + TEST_SEARCH ("c(a$|$)|d$", "xca", 0, 3); + TEST_SEARCH ("c(a$|$)|d$", "xd", 0, 2); + TEST_SEARCH ("c($|b$)|d$", "xc", 0, 2); + TEST_SEARCH ("c($|b$)|d$", "xcb", 0, 3); + TEST_SEARCH ("c($|b$)|d$", "xd", 0, 2); + TEST_SEARCH ("d(c$|e((a$|$)))", "xdc", 0, 3); + TEST_SEARCH ("d(c$|e((a$|$)))", "xde", 0, 3); + TEST_SEARCH ("d(c$|e((a$|$)))", "xdea", 0, 4); + TEST_SEARCH ("d(c$|e(($|b$)))", "xdc", 0, 3); + TEST_SEARCH ("d(c$|e(($|b$)))", "xde", 0, 3); + TEST_SEARCH ("d(c$|e(($|b$)))", "xdeb", 0, 4); + TEST_SEARCH ("d($|e((a$|b$)))", "xd", 0, 2); + TEST_SEARCH ("d($|e((a$|b$)))", "xdea", 0, 4); + TEST_SEARCH ("d($|e((a$|b$)))", "xdeb", 0, 4); + TEST_SEARCH ("a(b$|c$)|$", "x", 0, 1); + TEST_SEARCH ("a(b$|c$)|$", "xab", 0, 3); + TEST_SEARCH ("a(b$|c$)|$", "xac", 0, 3); + TEST_SEARCH ("a(b$|$)|d$", "xa", 0, 2); + TEST_SEARCH ("a(b$|$)|d$", "xab", 0, 3); + TEST_SEARCH ("a(b$|$)|d$", "xd", 0, 2); + TEST_SEARCH ("a($|c$)|d$", "xa", 0, 2); + TEST_SEARCH ("a($|c$)|d$", "xac", 0, 3); + TEST_SEARCH ("a($|c$)|d$", "xd", 0, 2); + TEST_SEARCH ("d$|a(b$|$)", "xd", 0, 2); + TEST_SEARCH ("d$|a(b$|$)", "xa", 0, 2); + TEST_SEARCH ("d$|a(b$|$)", "xab", 0, 3); + TEST_SEARCH ("d$|a($|c$)", "xd", 0, 2); + TEST_SEARCH ("d$|a($|c$)", "xa", 0, 2); + TEST_SEARCH ("d$|a($|c$)", "xac", 0, 3); + TEST_SEARCH ("$|a(b$|c$)", "x", 0, 1); + TEST_SEARCH ("$|a(b$|c$)", "xab", 0, 3); + TEST_SEARCH ("$|a(b$|c$)", "xac", 0, 3); + TEST_SEARCH ("(a)(b$|c$)|d$", "xab", 0, 3); + TEST_SEARCH ("(a)(b$|c$)|d$", "xac", 0, 3); + TEST_SEARCH ("(a)(b$|c$)|d$", "xd", 0, 2); + TEST_SEARCH ("(a)(b$|$)|d$", "xa", 0, 2); + TEST_SEARCH ("(a)(b$|$)|d$", "xab", 0, 3); + TEST_SEARCH ("(a)(b$|$)|d$", "xd", 0, 2); + TEST_SEARCH ("(a)($|c$)|d$", "xa", 0, 2); + TEST_SEARCH ("(a)($|c$)|d$", "xac", 0, 3); + TEST_SEARCH ("(a)($|c$)|d$", "xd", 0, 2); + TEST_SEARCH ("d$|(a)(b$|$)", "xd", 0, 2); + TEST_SEARCH ("d$|(a)(b$|$)", "xa", 0, 2); + TEST_SEARCH ("d$|(a)(b$|$)", "xab", 0, 3); + TEST_SEARCH ("d$|(a)($|c$)", "xd", 0, 2); + TEST_SEARCH ("d$|(a)($|c$)", "xa", 0, 2); + TEST_SEARCH ("d$|(a)($|c$)", "xac", 0, 3); + TEST_SEARCH ("$|(a)(b$|c$)", "x", 0, 1); + TEST_SEARCH ("$|(a)(b$|c$)", "xab", 0, 3); + TEST_SEARCH ("$|(a)(b$|c$)", "xac", 0, 3); + TEST_SEARCH ("d$|(c$|(a$|$))", "x", 0, 1); + TEST_SEARCH ("d$|(c$|(a$|$))", "xd", 0, 2); + TEST_SEARCH ("d$|(c$|(a$|$))", "xc", 0, 2); + TEST_SEARCH ("d$|(c$|(a$|$))", "xa", 0, 2); + TEST_SEARCH ("d$|(c$|($|b$))", "x", 0, 1); + TEST_SEARCH ("d$|(c$|($|b$))", "xd", 0, 2); + TEST_SEARCH ("d$|(c$|($|b$))", "xc", 0, 2); + TEST_SEARCH ("d$|(c$|($|b$))", "xb", 0, 2); + TEST_SEARCH ("d$|($|(a$|b$))", "x", 0, 1); + TEST_SEARCH ("d$|($|(a$|b$))", "xd", 0, 2); + TEST_SEARCH ("d$|($|(a$|b$))", "xa", 0, 2); + TEST_SEARCH ("d$|($|(a$|b$))", "xb", 0, 2); + TEST_SEARCH ("$|(c$|(a$|b$))", "x", 0, 1); + TEST_SEARCH ("$|(c$|(a$|b$))", "xc", 0, 2); + TEST_SEARCH ("$|(c$|(a$|b$))", "xa", 0, 2); + TEST_SEARCH ("$|(c$|(a$|b$))", "xb", 0, 2); + TEST_SEARCH ("d$|c(a$|$)", "xd", 0, 2); + TEST_SEARCH ("d$|c(a$|$)", "xc", 0, 2); + TEST_SEARCH ("d$|c(a$|$)", "xca", 0, 3); + TEST_SEARCH ("d$|c($|b$)", "xd", 0, 2); + TEST_SEARCH ("d$|c($|b$)", "xc", 0, 2); + TEST_SEARCH ("d$|c($|b$)", "xcb", 0, 3); + TEST_SEARCH ("$|c(a$|b$)", "x", 0, 1); + TEST_SEARCH ("$|c(a$|b$)", "xca", 0, 3); + TEST_SEARCH ("$|c(a$|b$)", "xcb", 0, 3); + TEST_SEARCH ("e(d$|c((a$|$)))", "xed", 0, 3); + TEST_SEARCH ("e(d$|c((a$|$)))", "xec", 0, 3); + TEST_SEARCH ("e(d$|c((a$|$)))", "xeca", 0, 3); + TEST_SEARCH ("e(d$|c(($|b$)))", "xed", 0, 3); + TEST_SEARCH ("e(d$|c(($|b$)))", "xec", 0, 3); + TEST_SEARCH ("e(d$|c(($|b$)))", "xecb", 0, 4); + TEST_SEARCH ("e($|c((a$|b$)))", "xe", 0, 2); + TEST_SEARCH ("e($|c((a$|b$)))", "xeca", 0, 4); + TEST_SEARCH ("e($|c((a$|b$)))", "xecb", 0, 4); + TEST_SEARCH ("ed$|(c((a$|$)))", "xed", 0, 3); + TEST_SEARCH ("ed$|(c((a$|$)))", "xc", 0, 2); + TEST_SEARCH ("ed$|(c((a$|$)))", "xca", 0, 3); + TEST_SEARCH ("ed$|(c(($|b$)))", "xed", 0, 3); + TEST_SEARCH ("ed$|(c(($|b$)))", "xc", 0, 2); + TEST_SEARCH ("ed$|(c(($|b$)))", "xcb", 0, 3); + TEST_SEARCH ("$|(c((a$|b$)))", "x", 0, 1); + TEST_SEARCH ("$|(c((a$|b$)))", "xca", 0, 3); + TEST_SEARCH ("$|(c((a$|b$)))", "xcb", 0, 3); + TEST_SEARCH ("d$|($|(a|b)$)", "x", 0, 1); + TEST_SEARCH ("d$|($|(a|b)$)", "xa", 0, 2); + TEST_SEARCH ("d$|($|(a|b)$)", "xb", 0, 2); + TEST_SEARCH ("$|(c$|(a|b)$)", "x", 0, 1); + TEST_SEARCH ("$|(c$|(a|b)$)", "xc", 0, 2); + TEST_SEARCH ("$|(c$|(a|b)$)", "xa", 0, 2); + TEST_SEARCH ("$|(c$|(a|b)$)", "xb", 0, 2); + + re_set_syntax (0); + test_match ("[^\n]", "a"); + test_match ("[^a]", "\n"); + + TEST_SEARCH ("^a", "b\na", 0, 3); + TEST_SEARCH ("b$", "b\na", 0, 3); + + test_case_fold ("[!-`]", "A"); + test_case_fold ("[!-`]", "a"); + + re_set_syntax (RE_CONTEXT_INDEP_OPS | RE_NO_BK_VBAR | RE_NO_BK_PARENS + | RE_NO_BK_BRACES | RE_INTERVALS); + valid_nonposix_pattern ("()^a"); + valid_nonposix_pattern ("()\\1^a"); + + /* Per Cederqvist (cedar@lysator.liu.se) bug. */ + + re_set_syntax (RE_SYNTAX_EMACS); + + /* One `a' before the \n and 638 a's after it. */ + test_search_return (0, "\\(.*\\)\n\\(\\(.\\|\n\\)*\\)$", "a\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + + /* No a's before the \n and 639 a's after it. */ + test_search_return (0, "\\(.*\\)\n\\(\\(.\\|\n\\)*\\)$", "\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + + /* One `a' before the \n and 639 a's after it. */ + test_search_return (0, "\\(.*\\)\n\\(\\(.\\|\n\\)*\\)$", "a\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + + /* No a's before the \n and 640 a's after it. */ + test_search_return (0, "\\(.*\\)\n\\(\\(.\\|\n\\)*\\)$", "\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + + re_set_syntax (RE_NO_BK_VBAR | RE_NO_BK_PARENS); + TEST_SEARCH ("^(^a)", "ab", 0, 2); + TEST_SEARCH ("(a$)$", "ba", 0, 2); + test_match ("a|$b", "$b"); + + /* Mike's curiosity item. */ + re_set_syntax (RE_NO_BK_VBAR | RE_NO_BK_PARENS); + test_all_registers ("(foo|foobar)(foo|bar)*\\1(foo|bar)*", + "foobarfoobar", "", + 0, 12, 0, 3, 3, 6, 9, 12, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1); + + /* Another one from Mike. */ + test_match ("(foo|foobarfoo)(bar)*", "foobarfoo"); + + /* And another. */ + test_match("(foo|foobar)(bar|barfoo)?\\1", "foobarfoobar"); + + re_set_syntax (RE_NO_BK_PARENS | RE_INTERVALS | RE_NO_BK_VBAR + | RE_NO_BK_BRACES); /* xx get new ones from ext.*/ + test_match ("((a{0,}{0,0}()\\3\\b\\B\\<\\>\\`\\')|b)*", "bb"); + test_all_registers ("((a{0,}{0,0}()\\3\\b\\B\\<\\>\\`\\')|b)*", "", "bb", + 0, 2, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1); + + test_match ("((a+?*{0,}{0,0}()\\3\\b\\B\\<\\>\\`\\')|b)", "b"); + test_all_registers ("((a+?*{0,}{0,0}()\\3\\b\\B\\<\\>\\`\\')|b)", "", "b", + 0, 1, 0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1); + + /* Valid anchoring. */ + /* See generic_test.c and extended_test.c for more search + tests. xx Not sure all these tests are represented in the + search tests. */ + + re_set_syntax (RE_NO_BK_PARENS | RE_NO_BK_VBAR); + valid_nonposix_pattern + ("(((((((((((((((((((((((((((((((((^a)))))))))))))))))))))))))))))))))"); + valid_nonposix_pattern + ("(((((((((((((((((((((((((((((((((a$)))))))))))))))))))))))))))))))))"); + valid_nonposix_pattern ("\\b\\B\\<\\>\\`\\'^a"); + valid_nonposix_pattern ("a$\\b\\B\\<\\>\\`\\'"); + valid_nonposix_pattern ("(^a)"); + valid_nonposix_pattern ("(a$)"); + valid_nonposix_pattern ("(^a)b"); + valid_nonposix_pattern ("b(a$)"); + valid_nonposix_pattern ("(^a|^b)c"); + valid_nonposix_pattern ("c(a$|b$)"); + valid_nonposix_pattern ("(^a|^b)|^c"); + valid_nonposix_pattern ("(a$|b$)|c$"); + valid_nonposix_pattern ("^c|(^a|^b)"); + valid_nonposix_pattern ("c$|(a$|b$)"); + valid_nonposix_pattern ("(^a|^b)c|^d"); + valid_nonposix_pattern ("c(a$|b$)|d$"); + valid_nonposix_pattern ("(((^a|^b))c|^d)e"); + valid_nonposix_pattern ("(c((a|b))|d)e$"); + valid_nonposix_pattern ("^d(c|e((a|b)))"); + valid_nonposix_pattern ("d(c$|e((a$|b$)))"); + valid_nonposix_pattern ("(((^a|^b))c)|^de"); + valid_nonposix_pattern ("(((a|b))c$)|de$"); + + valid_nonposix_pattern ("((a$)$)$"); + valid_nonposix_pattern ("^(^(^a))"); + + valid_nonposix_pattern ("^de|^(c((a|b)))"); + valid_nonposix_pattern ("^de|(^c((a|b)))"); + valid_nonposix_pattern ("de$|(c((a|b)$))"); + valid_nonposix_pattern ("de$|(c((a|b))$)"); + valid_nonposix_pattern ("de$|(c((a|b)))$"); + + valid_nonposix_pattern ("^a(b|c)|^d"); + valid_nonposix_pattern ("a(b$|c$)|d$"); + valid_nonposix_pattern ("^d|^a(b|c)"); + valid_nonposix_pattern ("d$|a(b$|c$)"); + valid_nonposix_pattern ("^d|^(b|c)a"); + valid_nonposix_pattern ("d$|(b|c)a$"); + valid_nonposix_pattern ("^(a)(b|c)|^d"); + valid_nonposix_pattern ("(a)(b|c)$|d$"); + valid_nonposix_pattern ("(^a)(b|c)|^d"); + valid_nonposix_pattern ("(a)(b$|c$)|d$"); + valid_nonposix_pattern ("^d|^(b|c)(a)"); + valid_nonposix_pattern ("d$|(b|c)(a)$"); + valid_nonposix_pattern ("^d|(^b|^c)(a)"); + valid_nonposix_pattern ("d$|(b|c)(a$)"); + valid_nonposix_pattern ("^d|^(a)(b|c)"); + valid_nonposix_pattern ("^d|(^a)(b|c)"); + valid_nonposix_pattern ("d$|(a)(b$|c$)"); + valid_nonposix_pattern ("((^a|^b)|^c)|^d"); + valid_nonposix_pattern ("d$|(c$|(a$|b$))"); + + + /* Tests shouldn't match. */ + test_should_match = false; + + /* Test that RE_CONTEXT_INVALID_OPS has precedence over + RE_CONTEXT_INDEP_OPS. */ + + re_set_syntax (RE_CONTEXT_INDEP_OPS | RE_CONTEXT_INVALID_OPS + | RE_NO_BK_VBAR | RE_NO_BK_PARENS + | RE_NO_BK_BRACES | RE_INTERVALS); + INVALID_PATTERN ("*"); + INVALID_PATTERN ("^*"); + INVALID_PATTERN ("a|*"); + INVALID_PATTERN ("(*)"); + + INVALID_PATTERN ("^+"); + INVALID_PATTERN ("+"); + INVALID_PATTERN ("a|+"); + INVALID_PATTERN ("(+)"); + + INVALID_PATTERN ("^?"); + INVALID_PATTERN ("?"); + INVALID_PATTERN ("a|?"); + INVALID_PATTERN ("(?)"); + + INVALID_PATTERN ("^{1}"); + INVALID_PATTERN ("{1}"); + INVALID_PATTERN ("a|{1}"); + INVALID_PATTERN ("({1})"); + +#if 0 + /* No longer have this syntax option -- POSIX says empty alternatives + are undefined as of draft 11.2. */ + + /* You can't have empty alternatives if RE_NO_EMPTY_ALTS is set. */ + + re_set_syntax (RE_NO_BK_VBAR | RE_NO_BK_PARENS | RE_NO_EMPTY_ALTS); + + INVALID_PATTERN ("|"); + INVALID_PATTERN ("^|a"); + INVALID_PATTERN ("a|"); + INVALID_PATTERN ("a||"); + INVALID_PATTERN ("a||b"); + INVALID_PATTERN ("(|a)"); + INVALID_PATTERN ("(a|)"); + INVALID_PATTERN ("(a|)"); + + + /* Test above with `\(' and `\)'. */ + re_set_syntax (RE_NO_BK_VBAR | RE_NO_EMPTY_ALTS); + INVALID_PATTERN ("\\(|a\\)"); + INVALID_PATTERN ("\\(a|\\)"); + + re_set_syntax (RE_NO_BK_VBAR | RE_NO_BK_PARENS | RE_NO_EMPTY_ALTS); + INVALID_PATTERN ("(|)()$|d$"); +#endif + + /* Test grouping. */ + test_match ("()", "a"); + + /* Test backslashed intervals that are CONTEXTly invalid if have + nothing on which to operate. */ + + re_set_syntax (RE_INTERVALS | RE_CONTEXT_INVALID_OPS); + INVALID_PATTERN ("\\{1\\}"); + + re_set_syntax (0); + test_match ("z-a", "a"); + + re_set_syntax (RE_BK_PLUS_QM); + INVALID_PATTERN ("a*\\"); + + re_set_syntax (0); + INVALID_PATTERN ("a*\\"); + + re_set_syntax (RE_BACKSLASH_ESCAPE_IN_LISTS); + INVALID_PATTERN ("[\\"); + +#if 0 + /* Empty groups are always ok now. (13 Sep 92) */ + re_set_syntax (RE_NO_BK_VBAR | RE_NO_BK_PARENS | RE_NO_EMPTY_GROUPS); + INVALID_PATTERN ("(|)()$|d$"); +#endif + + printf ("\nFinished non-POSIX tests.\n"); +} + + + +/* +Local variables: +make-backup-files: t +version-control: t +trim-versions-without-asking: nil +End: +*/ diff --git a/gnu/lib/libregex/test/printchar.c b/gnu/lib/libregex/test/printchar.c new file mode 100644 index 0000000000..1b756f441b --- /dev/null +++ b/gnu/lib/libregex/test/printchar.c @@ -0,0 +1,14 @@ +void +printchar (c) + char c; +{ + if (c < 040 || c >= 0177) + { + putchar ('\\'); + putchar (((c >> 6) & 3) + '0'); + putchar (((c >> 3) & 7) + '0'); + putchar ((c & 7) + '0'); + } + else + putchar (c); +} diff --git a/gnu/lib/libregex/test/psx-basic.c b/gnu/lib/libregex/test/psx-basic.c new file mode 100644 index 0000000000..52535b6b30 --- /dev/null +++ b/gnu/lib/libregex/test/psx-basic.c @@ -0,0 +1,253 @@ +/* psx-basic.c: Test POSIX basic regular expressions. */ + +#include "test.h" + + +void +test_posix_basic () +{ + /* Intervals can only match up to RE_DUP_MAX occurences of anything. */ + char dup_max_plus_one[6]; + sprintf (dup_max_plus_one, "%d", RE_DUP_MAX + 1); + + printf ("\nStarting POSIX basic tests.\n"); + t = posix_basic_test; + + re_set_syntax (RE_SYNTAX_POSIX_MINIMAL_BASIC); + + test_posix_generic (); + + printf ("\nContinuing POSIX basic tests.\n"); + +/* Grouping tests that are not the same. */ + + test_should_match = false; + invalid_pattern (REG_EPAREN, PARENS_TO_OPS ("a)")); + + test_should_match = true; + /* Special characters. */ + MATCH_SELF ("*"); + test_match ("\\(*\\)", "*"); + test_match ("\\(^*\\)", "*"); + test_match ("**", "***"); + test_match ("***", "****"); + + MATCH_SELF ("{"); /* of extended... */ + MATCH_SELF ("()"); /* also non-Posix. */ + MATCH_SELF ("a+"); + MATCH_SELF ("a?"); + MATCH_SELF ("a|b"); + MATCH_SELF ("a|"); /* No alternations, */ + MATCH_SELF ("|a"); /* so OK if empty. */ + MATCH_SELF ("a||"); + test_match ("\\(|a\\)", "|a"); + test_match ("\\(a|\\)", "a|"); + test_match ("a\\+", "a+"); + test_match ("a\\?", "a?"); + test_match ("a\\|b", "a|b"); + test_match ("^*", "*"); + test_match ("^+", "+"); + test_match ("^?", "?"); + test_match ("^{", "{"); + /* Valid subexpressions + (empty) in basic only. */ + test_match ("\\(\\)", ""); + + test_match ("a\\(\\)", "a"); + test_match ("\\(\\)b", "b"); + test_match ("a\\(\\)b", "ab"); + TEST_REGISTERS ("a\\(\\)b", "ab", 0, 2, 1, 1, -1, -1); + + test_match ("\\(\\)*", ""); + test_match ("\\(\\(\\)\\)*", ""); + /* Valid back references. */ + + /* N.B.: back references to subexpressions that include a * are + undefined in the spec. The tests are in here to see if we handle + the situation consistently, but if it fails any of them, it doesn't + matter. */ + + test_match ("\\(\\)\\1", ""); + TEST_REGISTERS ("\\(\\)\\1", "", 0, 0, 0, 0, -1, -1); + + test_match ("\\(\\(\\)\\)\\(\\)\\2", ""); + + test_match ("\\(a\\)\\1", "aa"); + TEST_REGISTERS ("\\(a\\)\\1", "aa", 0, 2, 0, 1, -1, -1); + TEST_REGISTERS ("\\(a\\)\\1", "xaax", 1, 3, 1, 2, -1, -1); + + test_match ("\\(\\(a\\)\\)\\1", "aa"); + test_match ("\\(a\\)\\(b\\)\\2\\1", "abba"); + + test_match ("\\(a\\)*\\1", "aa"); + TEST_REGISTERS ("\\(a\\)*\\1", "aa", 0, 2, 0, 1, -1, -1); + TEST_REGISTERS ("\\(a\\)*\\1", "xaax", 0, 0, -1, -1, -1, -1); + + test_match ("\\(\\(a\\)\\2b\\)*", "aab"); + TEST_REGISTERS ("\\(\\(a\\)\\2b\\)*", "aab", 0, 3, 0, 3, 0, 1); + TEST_REGISTERS ("\\(\\(a\\)\\2b\\)*", "xaabx", 0, 0, -1, -1, -1, -1); + + test_match ("\\(a*\\)*\\1", ""); + test_match ("\\(a*\\)*\\1", "aa"); + TEST_REGISTERS ("\\(a*\\)*\\1", "aa", 0, 2, 0, 1, -1, -1); + TEST_REGISTERS ("\\(a*\\)*\\1", "xaax", 0, 0, 0, 0, -1, -1); + + test_match ("\\(a*\\)*\\1", ""); + test_match ("\\(a*\\)*\\1", "aa"); + test_match ("\\(\\(a*\\)*\\)*\\1", "aa"); + test_match ("\\(ab*\\)*\\1", "abab"); + TEST_REGISTERS ("\\(ab*\\)*\\1", "abab", 0, 4, 0, 2, -1, -1); + TEST_REGISTERS ("\\(ab*\\)*\\1", "xababx", 0, 0, -1, -1, -1, -1); + + test_match ("\\(a*\\)ab\\1", "aaba"); + TEST_REGISTERS ("\\(a*\\)ab\\1", "aaba", 0, 4, 0, 1, -1, -1); + TEST_REGISTERS ("\\(a*\\)ab\\1", "xaabax", 1, 5, 1, 2, -1, -1); + + test_match ("\\(a*\\)*ab\\1", "aaba"); + TEST_REGISTERS ("\\(a*\\)*ab\\1", "aaba", 0, 4, 0, 1, -1, -1); + TEST_REGISTERS ("\\(a*\\)*ab\\1", "xaabax", 1, 5, 1, 2, -1, -1); + + test_match ("\\(\\(a*\\)b\\)*\\2", "abb"); + TEST_REGISTERS ("\\(\\(a*\\)b\\)*\\2", "abb", 0, 3, 2, 3, 2, 2); + TEST_REGISTERS ("\\(\\(a*\\)b\\)*\\2", "xabbx", 0, 0, -1, -1, -1, -1); + + /* Different from above. */ + test_match ("\\(\\(a*\\)b*\\)*\\2", "aa"); + TEST_REGISTERS ("\\(\\(a*\\)b*\\)*\\2", "aa", 0, 2, 0, 1, 0, 1); + TEST_REGISTERS ("\\(\\(a*\\)b*\\)*\\2", "xaax", 0, 0, 0, 0, 0, 0); + + test_match ("\\(\\(a*\\)b*\\)*\\2", "aba"); + TEST_REGISTERS ("\\(\\(a*\\)b*\\)*\\2", "aba", 0, 3, 0, 2, 0, 1); + TEST_REGISTERS ("\\(\\(a*\\)b*\\)*\\2", "xabax", 0, 0, 0, 0, 0, 0); + + test_match ("\\(\\(a*\\)b\\)*\\2", "aababa"); + TEST_REGISTERS ("\\(\\(a*\\)b\\)*\\2", "aababa", 0, 6, 3, 5, 3, 4); + TEST_REGISTERS ("\\(\\(a*\\)b\\)*\\2", "xaababax", 0, 0, -1, -1, -1, -1); + + test_match ("\\(\\(a*\\)b*\\)*\\2", "aabaa"); + TEST_REGISTERS ("\\(\\(a*\\)b*\\)*\\2", "aabaa", 0, 5, 0, 3, 0, 2); + TEST_REGISTERS ("\\(\\(a*\\)b*\\)*\\2", "xaabaax", 0, 0, 0, 0, 0, 0); + + test_match ("\\(\\(a*\\)b*\\)*\\2", "aabbaa"); + TEST_REGISTERS ("\\(\\(a*\\)b*\\)*\\2", "aabbaa", 0, 6, 0, 4, 0, 2); + TEST_REGISTERS ("\\(\\(a*\\)b*\\)*\\2", "xaabbaax", 0, 0, 0, 0, 0, 0); + + test_match ("\\(\\(a*\\)b*\\)*\\2", "abaabaa"); + TEST_REGISTERS ("\\(\\(a*\\)b*\\)*\\2", "abaabaa", 0, 7, 2, 5, 2, 4); + TEST_REGISTERS ("\\(\\(a*\\)b*\\)*\\2", "xaababaax", 0, 0, 0, 0, 0, 0); + + test_match ("\\(\\(a*\\)b*\\)*a\\2", "aabaaa"); + TEST_REGISTERS ("\\(\\(a*\\)b*a\\)*\\2", "aabaaa", 0, 6, 0, 3, 0, 2); + TEST_REGISTERS ("\\(\\(a*\\)b*a\\)*\\2", "xaabaax", 0, 0, -1, -1, -1, -1); + + test_match ("\\(\\(a*\\)b*\\)*\\2a", "aabaaa"); + TEST_REGISTERS ("\\(\\(a*\\)b*\\)*\\2a", "aabaaa", 0, 6, 0, 3, 0, 2); + TEST_REGISTERS ("\\(\\(a*\\)b*\\)*\\2a", "xaabaaax", 1, 7, 1, 4, 1, 3); + + test_match ("\\(\\(a*\\)b\\)*\\2\\1", "abaabaaaab"); + TEST_REGISTERS ("\\(\\(a*\\)b\\)*\\2\\1", "abaabaaaab", 0, 10, 2, 5, 2, 4); + /* We are matching the empty string here. */ + TEST_REGISTERS ("\\(\\(a*\\)b\\)*\\2\\1", "xabaabaaaabx", 0, 0, -1, -1, -1, -1); + + test_match ("\\(a*b\\)\\1", "abab"); + test_match ("\\(a\\)\\1\\1", "aaa"); + test_match ("\\(a\\(c\\)d\\)\\1\\2", "acdacdc"); + + test_match ("\\(a\\)\\1*", "aaa"); + TEST_REGISTERS ("\\(a\\)\\1*", "aaa", 0, 3, 0, 1, -1, -1); + TEST_REGISTERS ("\\(a\\)\\1*", "xaaax", 1, 4, 1, 2, -1, -1); + + test_match ("\\(a\\)\\{1,3\\}b\\1", "aba"); + TEST_REGISTERS ("\\(a\\)\\{1,3\\}b\\1", "aba", 0, 3, 0, 1, -1, -1); + TEST_REGISTERS ("\\(a\\)\\{1,3\\}b\\1", "xabax", 1, 4, 1, 2, -1, -1); + + test_match ("\\(\\(a\\)\\2\\)*", "aaaa"); /* rms? */ + TEST_REGISTERS ("\\(\\(a*b\\)\\2\\)*", "bbabab", 0, 6, 2, 6, 2, 4); /* rms? */ + + test_match ("\\(\\(a\\)\\1\\)*", "a1a1"); + + test_match ("\\(\\(a\\)\\2\\)\\1", "aaaa"); + + test_match ("\\(\\(a*\\)\\2\\)\\1", "aaaa"); + TEST_REGISTERS ("\\(\\(a*\\)\\2\\)\\1", "aaaa", 0, 4, 0, 2, 0, 1); + TEST_REGISTERS ("\\(\\(a*\\)\\2\\)\\1", "xaaaax", 0, 0, 0, 0, 0, 0); + + test_match ("\\{1\\}", "{1}"); + test_match ("^\\{1\\}", "{1}"); + + test_match ("\\(a\\)\\1\\{1,2\\}", "aaa"); + TEST_REGISTERS ("\\(a\\)\\1\\{1,2\\}", "aaa", 0, 3, 0, 1, -1, -1); + TEST_REGISTERS ("\\(a\\)\\1\\{1,2\\}", "xaaax", 1, 4, 1, 2, -1, -1); + + + /* Per POSIX D11.1 p. 109, leftmost longest match. */ + + test_match (PARENS_TO_OPS ("(.*).*\\1"), "abcabc"); + + + /* Per POSIX D11.1, p. 125, leftmost longest match. */ + + test_match (PARENS_TO_OPS ("(ac*)c*d[ac]*\\1"), "acdacaaa"); + TEST_REGISTERS (PARENS_TO_OPS ("(ac*)c*d[ac]*\\1"), "acdacaaa", + 0, 8, 0, 1, -1, -1); + + /* Anchors become ordinary, sometimes. */ + MATCH_SELF ("a^"); + MATCH_SELF ("$a"); + MATCH_SELF ("$^"); + test_fastmap ("$a^", "$", 0, 0); + test_match ("$^*", "$^^"); + test_match ("\\($^\\)", "$^"); + test_match ("$*", "$$"); + /* xx -- known bug, solution pending test_match ("^^$", "^"); */ + test_match ("$\\{0,\\}", "$$"); + TEST_SEARCH ("^$*", "$$", 0, 2); + TEST_SEARCH ("^$\\{0,\\}", "$$", 0, 2); + MATCH_SELF ("2^10"); + MATCH_SELF ("$HOME"); + MATCH_SELF ("$1.35"); + + + /* Basic regular expressions, continued; these don't match their strings. */ + test_should_match = false; + + invalid_pattern (REG_EESCAPE, "\\(a\\"); + /* Invalid back references. */ + test_match ("\\(a\\)\\1", "ab"); + test_match ("\\(a\\)\\1\\1", "aab"); + test_match ("\\(a\\)\\(b\\)\\2\\1", "abab"); + test_match ("\\(a\\(c\\)d\\)\\1\\2", "acdc"); + test_match ("\\(a*b\\)\\1", "abaab"); + test_match ("\\(a\\)\\1*", "aaaaaaaaaab"); + test_match ("\\(\\(a\\)\\1\\)*", "aaa"); + invalid_pattern (REG_ESUBREG, "\\1"); + invalid_pattern (REG_ESUBREG, "\\(a\\)\\2"); + test_match ("\\(\\(a\\)\\2\\)*", "abaa"); + test_match ("\\(\\(a\\)\\1\\)*", "a"); + test_match ("\\(\\(a\\)\\2\\)\\1", "abaa"); + test_match ("\\(\\(a*\\)\\2\\)\\1", "abaa"); + /* Invalid intervals. */ + invalid_pattern (REG_EBRACE, "a\\{"); + + invalid_pattern (REG_BADBR, "a\\{-1"); + invalid_pattern (REG_BADBR, concat ("a\\{", (char *)dup_max_plus_one)); + invalid_pattern (REG_BADBR, concat (concat ("a\\{", (char *)dup_max_plus_one), ",")); + invalid_pattern (REG_BADBR, "a\\{1,0"); + + invalid_pattern (REG_EBRACE, "a\\{1"); + invalid_pattern (REG_EBRACE, "a\\{0,"); + invalid_pattern (REG_EBRACE, "a\\{0,1"); + invalid_pattern (REG_EBRACE, "a\\{0,1}"); + + printf ("\nFinished POSIX basic tests.\n"); +} + + + +/* +Local variables: +make-backup-files: t +version-control: t +trim-versions-without-asking: nil +End: +*/ diff --git a/gnu/lib/libregex/test/psx-extend.c b/gnu/lib/libregex/test/psx-extend.c new file mode 100644 index 0000000000..6f02d67b82 --- /dev/null +++ b/gnu/lib/libregex/test/psx-extend.c @@ -0,0 +1,1244 @@ +/* psx-extend.c: Test POSIX extended regular expressions. */ + +#include "test.h" + + +void +test_posix_extended () +{ + /* Intervals can only match up to RE_DUP_MAX occurences of anything. */ + char dup_max_plus_one[6]; + sprintf (dup_max_plus_one, "%d", RE_DUP_MAX + 1); + + + printf ("\nStarting POSIX extended tests.\n"); + t = posix_extended_test; + + re_set_syntax (RE_SYNTAX_POSIX_MINIMAL_EXTENDED); + + test_posix_generic (); + + printf ("\nContinuing POSIX extended tests.\n"); + + /* Grouping tests that differ from basic's. */ + + test_should_match = true; + MATCH_SELF ("a)"); + + /* Valid use of special characters. */ + test_match ("\\(a", "(a"); + test_match ("a\\+", "a+"); + test_match ("a\\?", "a?"); + test_match ("\\{a", "{a"); + test_match ("\\|a", "|a"); + test_match ("a\\|b", "a|b"); + test_match ("a\\|?", "a"); + test_match ("a\\|?", "a|"); + test_match ("a\\|*", "a"); + test_match ("a\\|*", "a||"); + test_match ("\\(*\\)", ")"); + test_match ("\\(*\\)", "(()"); + test_match ("a\\|+", "a|"); + test_match ("a\\|+", "a||"); + test_match ("\\(+\\)", "()"); + test_match ("\\(+\\)", "(()"); + test_match ("a\\||b", "a|"); + test_match ("\\(?\\)", ")"); + test_match ("\\(?\\)", "()"); + + test_match ("a+", "a"); + test_match ("a+", "aa"); + test_match ("a?", ""); + test_match ("a?", "a"); + + /* Bracket expressions. */ + test_match ("[(]", "("); + test_match ("[+]", "+"); + test_match ("[?]", "?"); + test_match ("[{]", "{"); + test_match ("[|]", "|"); + /* Subexpressions. */ + test_match ("(a+)*", ""); + test_match ("(a+)*", "aa"); + test_match ("(a?)*", ""); + test_match ("(a?)*", "aa"); + /* (No) back references. */ + test_match ("(a)\\1", "a1"); + /* Invalid as intervals, + but are valid patterns. */ + MATCH_SELF ("{"); + test_match ("^{", "{"); + test_match ("a|{", "{"); + test_match ("({)", "{"); + MATCH_SELF ("a{"); + MATCH_SELF ("a{}"); + MATCH_SELF ("a{-1"); + MATCH_SELF ("a{-1}"); + MATCH_SELF ("a{0"); + MATCH_SELF ("a{0,"); + MATCH_SELF (concat ("a{", dup_max_plus_one)); + MATCH_SELF (concat (concat ("a{", dup_max_plus_one), ",")); + MATCH_SELF ("a{1,0"); + MATCH_SELF ("a{1,0}"); + MATCH_SELF ("a{0,1"); + test_match ("[a{0,1}]", "}"); + test_match ("a{1,3}{-1}", "aaa{-1}"); + test_match (concat ("a{1,3}{", dup_max_plus_one), + concat ("aaa{", dup_max_plus_one)); + test_match ("a{1,3}{2,1}", "aaa{2,1}"); + test_match ("a{1,3}{1,2", "aaa{1,2"); + /* Valid consecutive repetitions. */ + test_match ("a*+", "a"); + test_match ("a*?", "a"); + test_match ("a++", "a"); + test_match ("a+*", "a"); + test_match ("a+?", "a"); + test_match ("a??", "a"); + test_match ("a?*", "a"); + test_match ("a?+", "a"); + + test_match ("a{2}?", ""); + test_match ("a{2}?", "aa"); + test_match ("a{2}+", "aa"); + test_match ("a{2}{2}", "aaaa"); + + test_match ("a{1}?*", ""); + test_match ("a{1}?*", "aa"); + + test_match ("(a?){0,3}b", "aaab"); + test_fastmap ("(a?){0,3}b", "ab", 0, 0); + test_match ("(a+){0,3}b", "b"); + test_fastmap ("(a+){0,3}b", "ab", 0, 0); + test_match ("(a+){0,3}b", "ab"); + test_fastmap ("(a+){0,3}b", "ab", 0, 0); + test_match ("(a+){1,3}b", "aaab"); + test_match ("(a?){1,3}b", "aaab"); + + test_match ("\\\\{1}", "\\"); /* Extended only. */ + + test_match ("(a?)?", "a"); + test_match ("(a?b)?c", "abc"); + test_match ("(a+)*b", "b"); + /* Alternatives. */ + test_match ("a|b", "a"); + test_match ("a|b", "b"); + test_fastmap ("a|b", "ab", 0, 0); + + TEST_SEARCH ("a|b", "cb", 0, 2); + TEST_SEARCH ("a|b", "cb", 0, 2); + + test_match ("(a|b|c)", "a"); + test_match ("(a|b|c)", "b"); + test_match ("(a|b|c)", "c"); + + test_match ("(a|b|c)*", "abccba"); + + test_match ("(a(b*))|c", "a"); /* xx do registers. */ + test_match ("(a(b*))|c", "ab"); + test_match ("(a(b*))|c", "c"); + + test_fastmap ("(a+?*|b)", "ab", 0, 0); + test_match ("(a+?*|b)", "b"); + TEST_REGISTERS ("(a+?*|b)", "b", 0, 1, 0, 1, -1, -1); + + test_fastmap ("(a+?*|b)*", "ab", 0, 0); + test_match ("(a+?*|b)*", "bb"); + TEST_REGISTERS ("(a+?*|b)*", "bb", 0, 2, 1, 2, -1, -1); + + test_fastmap ("(a*|b)*", "ab", 0, 0); + test_match ("(a*|b)*", "bb"); + TEST_REGISTERS ("(a*|b)*", "bb", 0, 2, 1, 2, -1, -1); + + test_fastmap ("((a*)|b)*", "ab", 0, 0); + test_match ("((a*)|b)*", "bb"); + TEST_REGISTERS ("((a*)|b)*", "bb", 0, 2, 1, 2, 1, 1); + + test_fastmap ("(a{0,}|b)*", "ab", 0, 0); + test_match ("(a{0,}|b)*", "bb"); + TEST_REGISTERS ("(a{0,}|b)*", "bb", 0, 2, 1, 2, -1, -1); + + test_fastmap ("((a{0,})|b)*", "ab", 0, 0); + test_match ("((a{0,})|b)*", "bb"); + TEST_REGISTERS ("((a{0,})|b)*", "bb", 0, 2, 1, 2, 1, 1); + + /* With c's */ + test_fastmap ("(a+?*|b)c", "abc", 0, 0); + test_match ("(a+?*|b)c", "bc"); + TEST_REGISTERS ("(a+?*|b)c", "bc", 0, 2, 0, 1, -1, -1); + + test_fastmap ("(a+?*|b)*c", "abc", 0, 0); + test_match ("(a+?*|b)*c", "bbc"); + TEST_REGISTERS ("(a+?*|b)*c", "bbc", 0, 3, 1, 2, -1, -1); + + test_fastmap ("(a*|b)*c", "abc", 0, 0); + test_match ("(a*|b)*c", "bbc"); + TEST_REGISTERS ("(a*|b)*c", "bbc", 0, 3, 1, 2, -1, -1); + + test_fastmap ("((a*)|b)*c", "abc", 0, 0); + test_match ("((a*)|b)*c", "bbc"); + TEST_REGISTERS ("((a*)|b)*c", "bbc", 0, 3, 1, 2, 1, 1); + + test_fastmap ("(a{0,}|b)*c", "abc", 0, 0); + test_match ("(a{0,}|b)*c", "bbc"); + TEST_REGISTERS ("(a{0,}|b)*c", "bbc", 0, 3, 1, 2, -1, -1); + + test_fastmap ("((a{0,})|b)*c", "abc", 0, 0); + test_match ("((a{0,})|b)*c", "bbc"); + TEST_REGISTERS ("((a{0,})|b)*c", "bbc", 0, 3, 1, 2, 1, 1); + + + test_fastmap ("((a{0,}\\b\\<)|b)", "ab", 0, 0); + test_match ("((a{0,}\\b\\<)|b)", "b"); + TEST_REGISTERS ("((a{0,}\\b\\<)|b)", "b", + 0, 1, 0, 1, 0, 0); + + test_fastmap ("((a{0,}\\b\\<)|b)*", "ab", 0, 0); + test_match ("((a{0,}\\b\\<)|b)*", "b"); + TEST_REGISTERS ("((a{0,}\\b\\<)|b)*", "b", + 0, 1, 0, 1, 0, 0); + + test_fastmap ("((a+?*{0,1}\\b\\<)|b)", "ab", 0, 0); + test_match ("((a+?*{0,1}\\b\\<)|b)", "b"); + TEST_REGISTERS ("((a+?*{0,1}\\b\\<)|b)", "b", + 0, 1, 0, 1, 0, 0); + + test_fastmap ("((a+?*{0,2}\\b\\<)|b)", "ab", 0, 0); + test_match ("((a+?*{0,2}\\b\\<)|b)", "b"); + TEST_REGISTERS ("((a+?*{0,2}\\b\\<)|b)", "b", + 0, 1, 0, 1, 0, 0); + + + test_fastmap ("((a+?*{0,4095}\\b\\<)|b)", "ab", 0, 0); + test_match ("((a+?*{0,4095}\\b\\<)|b)", "b"); + TEST_REGISTERS ("((a+?*{0,4095}\\b\\<)|b)", "b", + 0, 1, 0, 1, 0, 0); + + test_fastmap ("((a+?*{0,5119}\\b\\<)|b)", "ab", 0, 0); + test_match ("((a+?*{0,5119}\\b\\<)|b)", "b"); + TEST_REGISTERS ("((a+?*{0,5119}\\b\\<)|b)", "b", + 0, 1, 0, 1, 0, 0); + + test_fastmap ("((a+?*{0,6143}\\b\\<)|b)", "ab", 0, 0); + test_match ("((a+?*{0,6143}\\b\\<)|b)", "b"); + TEST_REGISTERS ("((a+?*{0,6143}\\b\\<)|b)", "b", + 0, 1, 0, 1, 0, 0); + + test_fastmap ("((a+?*{0,8191}\\b\\<)|b)", "ab", 0, 0); + test_match ("((a+?*{0,8191}\\b\\<)|b)", "b"); + TEST_REGISTERS ("((a+?*{0,8191}\\b\\<)|b)", "b", + 0, 1, 0, 1, 0, 0); + + test_fastmap ("((a+?*{0,16383}\\b\\<)|b)", "ab", 0, 0); + test_match ("((a+?*{0,16383}\\b\\<)|b)", "b"); + TEST_REGISTERS ("((a+?*{0,16383}\\b\\<)|b)", "b", + 0, 1, 0, 1, 0, 0); + + + test_fastmap ("((a+?*{0,}\\b\\<)|b)", "ab", 0, 0); + test_match ("((a+?*{0,}\\b\\<)|b)", "b"); + TEST_REGISTERS ("((a+?*{0,}\\b\\<)|b)", "b", + 0, 1, 0, 1, 0, 0); + + test_fastmap ("((a+?*{0,}\\b\\<)|b)*", "ab", 0, 0); + test_match ("((a+?*{0,}\\b\\<)|b)*", "b"); + TEST_REGISTERS ("((a+?*{0,}\\b\\<)|b)*", "b", + 0, 1, 0, 1, 0, 0); + + test_fastmap ("((a+?*{0,}\\b\\<)|b)*", "ab", 0, 0); + test_match ("((a+?*{0,}\\b\\<)|b)*", "bb"); + TEST_REGISTERS ("((a+?*{0,}\\b\\<)|b)*", "bb", + 0, 2, 1, 2, 0, 0); + + + /* `*' after group. */ + test_match ("(a*|b*)*c", "c"); + TEST_REGISTERS ("(a*|b*)*c", "c", 0, 1, 0, 0, -1, -1); + + test_match ("(a*|b*)*c", "ac"); + TEST_REGISTERS ("(a*|b*)*c", "ac", 0, 2, 0, 1, -1, -1); + + test_match ("(a*|b*)*c", "aac"); + TEST_REGISTERS ("(a*|b*)*c", "aac", 0, 3, 0, 2, -1, -1); + + test_match ("(a*|b*)*c", "bbc"); + TEST_REGISTERS ("(a*|b*)*c", "bbc", 0, 3, 0, 2, -1, -1); + + test_match ("(a*|b*)*c", "abc"); + TEST_REGISTERS ("(a*|b*)*c", "abc", 0, 3, 1, 2, -1, -1); + + /* No `*' after group. */ + test_match ("(a*|b*)c", "c"); + TEST_REGISTERS ("(a*|b*)c", "c", 0, 1, 0, 0, -1, -1); + + test_match ("(a*|b*)c", "ac"); + TEST_REGISTERS ("(a*|b*)c", "ac", 0, 2, 0, 1, -1, -1); + + test_match ("(a*|b*)c", "bc"); + TEST_REGISTERS ("(a*|b*)c", "bc", 0, 2, 0, 1, -1, -1); + + test_match ("(a*|b*)c", "aac"); + TEST_REGISTERS ("(a*|b*)c", "aac", 0, 3, 0, 2, -1, -1); + + /* Same as above, but with no `*'s in alternatives. + + test_match ("(a|b)*c", "c"); /* `*' after group. */ + TEST_REGISTERS ("(a|b)*c", "c", 0, 1, -1, -1, -1, -1); + + test_match ("(a|b)*c", "ac"); + TEST_REGISTERS ("(a|b)*c", "ac", 0, 2, 0, 1, -1, -1); + + test_match ("(a|b)*c", "bc"); + TEST_REGISTERS ("(a|b)*c", "bc", 0, 2, 0, 1, -1, -1); + + test_match ("(a|b)*c", "abc"); + TEST_REGISTERS ("(a|b)*c", "abc", 0, 3, 1, 2, -1, -1); + + + test_match ("(a*|b*)c", "bbc"); + TEST_REGISTERS ("(a*|b*)c", "bbc", 0, 3, 0, 2, -1, -1); + + /* Complicated second alternative. */ + + test_match ("(a*|(b*)*)*c", "bc"); + TEST_REGISTERS ("(a*|(b*)*)*c", "bc", 0, 2, 0, 1, 0, 1); + + test_match ("(a*|(b*|c*)*)*d", "bd"); + TEST_REGISTERS ("(a*|(b*|c*)*)*d", "bd", 0, 2, 0, 1, 0, 1); + + test_match ("(a*|(b*|c*)*)*d", "bbd"); + TEST_REGISTERS ("(a*|(b*|c*)*)*d", "bbd", 0, 3, 0, 2, 0, 2); + + test_match ("(a*|(b*|c*)*)*d", "cd"); + TEST_REGISTERS ("(a*|(b*|c*)*)*d", "cd", 0, 2, 0, 1, 0, 1); + + test_match ("(a*|(b*|c*)*)*d", "ccd"); + TEST_REGISTERS ("(a*|(b*|c*)*)*d", "ccd", 0, 3, 0, 2, 0, 2); + + test_match ("(a*|b*|c*)*d", "aad"); + TEST_REGISTERS ("(a*|b*|c*)*d", "aad", 0, 3, 0, 2, 0, 2); + + test_match ("(a*|b*|c*)*d", "bbd"); + TEST_REGISTERS ("(a*|b*|c*)*d", "bbd", 0, 3, 0, 2, 0, 2); + + test_match ("(a*|b*|c*)*d", "ccd"); + TEST_REGISTERS ("(a*|b*|c*)*d", "ccd", 0, 3, 0, 2, 0, 2); + + /* Valid anchoring. */ + valid_pattern ("a^"); + valid_pattern ("a^b"); + valid_pattern ("$a"); + valid_pattern ("a$b"); + valid_pattern ("foo^bar"); + valid_pattern ("foo$bar"); + valid_pattern ("(^)"); + valid_pattern ("($)"); + valid_pattern ("(^$)"); + + /* These are the same (but valid) as those (invalid) in other_test.c. */ + valid_pattern + ("(((((((((((((((((((((((((((((((((a^)))))))))))))))))))))))))))))))))"); + valid_pattern + ("((((((((((((((((((((((((((((((((($a)))))))))))))))))))))))))))))))))"); + valid_pattern ("\\(^a\\)"); + valid_pattern ("a\\|^b"); + valid_pattern ("\\w^a"); + valid_pattern ("\\W^a"); + valid_pattern ("(a^)"); + valid_pattern ("($a)"); + valid_pattern ("a(^b)"); + valid_pattern ("a$(b)"); + valid_pattern ("(a)^b"); + valid_pattern ("(a)$b"); + valid_pattern ("(a)(^b)"); + valid_pattern ("(a$)(b)"); + valid_pattern ("(a|b)^c"); + valid_pattern ("(a|b)$c"); + valid_pattern ("(a$|b)c"); + valid_pattern ("(a|b$)c"); + valid_pattern ("a(b|^c)"); + valid_pattern ("a(^b|c)"); + valid_pattern ("a$(b|c)"); + valid_pattern ("(a)(^b|c)"); + valid_pattern ("(a)(b|^c)"); + valid_pattern ("(b$|c)(a)"); + valid_pattern ("(b|c$)(a)"); + valid_pattern ("(a(^b|c))"); + valid_pattern ("(a(b|^c))"); + valid_pattern ("((b$|c)a)"); + valid_pattern ("((b|c$)a)"); + valid_pattern ("((^a|^b)^c)"); + valid_pattern ("(c$(a$|b$))"); + valid_pattern ("((^a|^b)^c)"); + valid_pattern ("((a$|b$)c)"); + valid_pattern ("(c$(a$|b$))"); + valid_pattern ("((^a|^b)|^c)^d"); + valid_pattern ("((a$|b$)|c$)d$"); + valid_pattern ("d$(c$|(a$|b$))"); + valid_pattern ("((^a|^b)|^c)(^d)"); + valid_pattern ("((a$|b$)|c$)(d$)"); + valid_pattern ("(d$)((a$|b$)|c$)"); + valid_pattern ("((^a|^b)|^c)((^d))"); + valid_pattern ("((a$|b$)|c$)((d$))"); + valid_pattern ("((d$))((a$|b$)|c$)"); + valid_pattern ("(((^a|^b))c|^d)^e"); + valid_pattern ("(((a$|b$))c|d$)$e$"); + valid_pattern ("e$(d$|c((a$|b$)))"); + valid_pattern ("(^a)((^b))"); + valid_pattern ("(a$)((b$))"); + valid_pattern ("((^a))(^b)"); + valid_pattern ("((a$))(b$)"); + valid_pattern ("((^a))((^b))"); + valid_pattern ("((a$))((b$))"); + valid_pattern ("((^a)^b)"); + valid_pattern ("((a$)b$)"); + valid_pattern ("(b$(a$))"); + valid_pattern ("(((^a)b)^c)"); + valid_pattern ("(((a$)b)c$)"); + valid_pattern ("(c$(b(a$)))"); + valid_pattern ("(((^a)b)c)^d"); + valid_pattern ("(((a$)b)c)d$"); + valid_pattern ("d$(c(b(a$)))"); + valid_pattern (".^a"); + valid_pattern ("a$."); + valid_pattern ("[a]^b"); + valid_pattern ("b$[a]"); + valid_pattern ("\\(a$\\)"); + valid_pattern ("a$\\|b"); + valid_pattern ("(^a|^b)^c"); + valid_pattern ("c$(a$|b$)"); + valid_pattern ("(^a|^b)^|^c"); + valid_pattern ("(a$|b$)$|$c$"); + valid_pattern ("(a$|$b$)$|c$"); + valid_pattern ("($a$|b$)$|c$"); + valid_pattern ("$(a$|b$)$|c$"); + valid_pattern ("^c|d(^a|^b)"); + valid_pattern ("(^a|^b)|d^c"); + valid_pattern ("c$|(a$|b$)d"); + valid_pattern ("c$d|(a$|b$)"); + valid_pattern ("c(^a|^b)|^d"); + valid_pattern ("(a$|b$)c|d$"); + valid_pattern ("c(((^a|^b))|^d)e"); + valid_pattern ("(c((^a|^b))|^d)e"); + valid_pattern ("((c(^a|^b))|^d)e"); + valid_pattern ("(((^a|^b))|c^d)e"); + valid_pattern ("(((^a|^b))|^d)^e"); + valid_pattern ("(c$((a|b))|d)e$"); + valid_pattern ("(c((a$|b$))|d)e$"); + valid_pattern ("(c((a|b)$)|d)e$"); + valid_pattern ("(c((a|b))|d$)e$"); + valid_pattern ("^d(^c|e((a|b)))"); + valid_pattern ("^d(c|^e((a|b)))"); + valid_pattern ("^d(c|e(^(a|b)))"); + valid_pattern ("^d(c|e((^a|b)))"); + valid_pattern ("^d(c|e((a|^b)))"); + valid_pattern ("^d(c|e((a|b^)))"); + valid_pattern ("^d(c|e((a|b)^))"); + valid_pattern ("^d(c|e((a|b))^)"); + valid_pattern ("^d(c|e((a|b)))^"); + valid_pattern ("d$(c$|e((a$|b$)))"); + valid_pattern ("d(c$|e$((a$|b$)))"); + valid_pattern ("(((^a|^b))^c)|^de"); + valid_pattern ("(((^a|^b))c)|^d^e"); + valid_pattern ("(((a$|b))c$)|de$"); + valid_pattern ("(((a|b$))c$)|de$"); + valid_pattern ("(((a|b))c$)|d$e$"); + valid_pattern ("^d^e|^(c((a|b)))"); + valid_pattern ("^de|^(c^((a|b)))"); + valid_pattern ("^de|^(c(^(a|b)))"); + valid_pattern ("^de|^(c((^a|b)))"); + valid_pattern ("^de|^(c((a|^b)))"); + valid_pattern ("^de|(^c(^(a|b)))"); + valid_pattern ("^de|(^c((^a|b)))"); + valid_pattern ("^de|(^c((a|^b)))"); + valid_pattern ("de$|(c($(a|b)$))"); + valid_pattern ("de$|(c$((a|b)$))"); + valid_pattern ("de$|($c((a|b)$))"); + valid_pattern ("de$|$(c((a|b)$))"); + valid_pattern ("de$|(c($(a|b))$)"); + valid_pattern ("de$|(c$((a|b))$)"); + valid_pattern ("de$|$(c((a|b))$)"); + valid_pattern ("de$|(c($(a|b)))$"); + valid_pattern ("de$|(c$((a|b)))$"); + valid_pattern ("de$|($c((a|b)))$"); + valid_pattern ("de$|$(c((a|b)))$"); + valid_pattern ("^a(^b|c)|^d"); + valid_pattern ("^a(b|^c)|^d"); + valid_pattern ("^a(b|c^)|^d"); + valid_pattern ("^a(b|c)^|^d"); + valid_pattern ("a$(b$|c$)|d$"); + valid_pattern ("^d|^a(^b|c)"); + valid_pattern ("^d|^a(b|^c)"); + valid_pattern ("d$|a$(b$|c$)"); + valid_pattern ("^d|^(b|c)^a"); + valid_pattern ("d$|(b|c$)a$"); + valid_pattern ("d$|(b$|c)a$"); + valid_pattern ("^(a)^(b|c)|^d"); + valid_pattern ("^(a)(^b|c)|^d"); + valid_pattern ("^(a)(b|^c)|^d"); + valid_pattern ("(a)$(b|c)$|d$"); + valid_pattern ("(a$)(b|c)$|d$"); + valid_pattern ("(^a)(^b|c)|^d"); + valid_pattern ("(^a)(b|^c)|^d"); + valid_pattern ("(a)$(b$|c$)|d$"); + valid_pattern ("(a$)(b$|c$)|d$"); + valid_pattern ("^d|^(b|c)^(a)"); + valid_pattern ("^d|^(b|c)(^a)"); + valid_pattern ("d$|(b|c$)(a)$"); + valid_pattern ("d$|(b$|c)(a)$"); + valid_pattern ("^d|(^b|^c)^(a)"); + valid_pattern ("^d|(^b|^c)(^a)"); + valid_pattern ("d$|(b|c)$(a$)"); + valid_pattern ("d$|(b|c$)(a$)"); + valid_pattern ("d$|(b$|c)(a$)"); + valid_pattern ("^d|^(a)^(b|c)"); + valid_pattern ("^d|^(a)(^b|c)"); + valid_pattern ("^d|^(a)(b|^c)"); + valid_pattern ("^d|(^a)^(b|c)"); + valid_pattern ("^d|(^a)(^b|c)"); + valid_pattern ("^d|(^a)(b|^c)"); + valid_pattern ("d$|(a)$(b$|c$)"); + valid_pattern ("d$|(a$)(b$|c$)"); + valid_pattern ("((e^a|^b)|^c)|^d"); + valid_pattern ("((^a|e^b)|^c)|^d"); + valid_pattern ("((^a|^b)|e^c)|^d"); + valid_pattern ("((^a|^b)|^c)|e^d"); + valid_pattern ("d$e|(c$|(a$|b$))"); + valid_pattern ("d$|(c$e|(a$|b$))"); + valid_pattern ("d$|(c$|(a$e|b$))"); + valid_pattern ("d$|(c$|(a$|b$e))"); + valid_pattern ("d$|(c$|(a$|b$)e)"); + valid_pattern ("d$|(c$|(a$|b$))e"); + valid_pattern ("(a|b)^|c"); + valid_pattern ("(a|b)|c^"); + valid_pattern ("$(a|b)|c"); + valid_pattern ("(a|b)|$c"); + valid_pattern ("(a^|^b)|^c"); + valid_pattern ("(^a|b^)|^c"); + valid_pattern ("(^a|^b)|c^"); + valid_pattern ("($a|b$)|c$"); + valid_pattern ("(a$|$b)|c$"); + valid_pattern ("(a$|b$)|$c"); + valid_pattern ("c^|(^a|^b)"); + valid_pattern ("^c|(a^|^b)"); + valid_pattern ("^c|(^a|b^)"); + valid_pattern ("$c|(a$|b$)"); + valid_pattern ("c$|($a|b$)"); + valid_pattern ("c$|(a$|$b)"); + valid_pattern ("c^|^(a|b)"); + valid_pattern ("^c|(a|b)^"); + valid_pattern ("$c|(a|b)$"); + valid_pattern ("c$|$(a|b)"); + valid_pattern ("(a^|^b)c|^d"); + valid_pattern ("(^a|b^)c|^d"); + valid_pattern ("(^a|^b)c|d^"); + valid_pattern ("(^a|^b)^c|^d"); + valid_pattern ("(a|b)c$|$d"); + valid_pattern ("(a|b)$c$|d$"); + valid_pattern ("(a|b)$c$|d$"); + valid_pattern ("(a|b$)c$|d$"); + valid_pattern ("(a$|b)c$|d$"); + valid_pattern ("($a|b)c$|d$"); + valid_pattern ("$(a|b)c$|d$"); + valid_pattern ("^d|^c^(a|b)"); + valid_pattern ("^d|^c(^a|b)"); + valid_pattern ("^d|^c(a|^b)"); + valid_pattern ("^d|^c(a|b^)"); + valid_pattern ("^d|^c(a|b)^"); + valid_pattern ("$d|c(a$|b$)"); + valid_pattern ("d$|c($a$|b$)"); + valid_pattern ("d$|c$(a$|b$)"); + valid_pattern ("d$|$c(a$|b$)"); + + valid_pattern ("(((a^|^b))c|^d)e"); + valid_pattern ("(((^a|b^))c|^d)e"); + valid_pattern ("(((^a|^b))^c|^d)e"); + valid_pattern ("((^(a|b))c|d^)e"); + valid_pattern ("(^((a|b))c|^d)^e"); + valid_pattern ("(^((a|b)^)c|^d)e"); + valid_pattern ("(^((a^|b))c|^d)e"); + valid_pattern ("(^((a|b^))c|^d)e"); + valid_pattern ("(^((a|b)^)c|^d)e"); + valid_pattern ("(^((a|b))^c|^d)e"); + valid_pattern ("(^((a|b))c^|^d)e"); + valid_pattern ("(^((a|b))c|^d^)e"); + valid_pattern ("(^((a|b))c|^d)^e"); + valid_pattern ("(((a|b))c|d)$e$"); + valid_pattern ("(((a|b))c|d$)e$"); + valid_pattern ("(((a|b))c|$d)e$"); + valid_pattern ("(((a|b))c$|d)e$"); + valid_pattern ("(((a|b))$c|d)e$"); + valid_pattern ("(((a|b)$)c|d)e$"); + valid_pattern ("(((a|b$))c|d)e$"); + valid_pattern ("(((a$|b))c|d)e$"); + valid_pattern ("((($a|b))c|d)e$"); + valid_pattern ("(($(a|b))c|d)e$"); + valid_pattern ("($((a|b))c|d)e$"); + valid_pattern ("$(((a|b))c|d)e$"); + valid_pattern ("(^((a|b)^)c|^d)e"); + valid_pattern ("(^((a|b))^c|^d)e"); + valid_pattern ("(^((a|b))c|^d^)e"); + valid_pattern ("(^((a|b))c|^d)^e"); + + valid_pattern ("^e(^d|c((a|b)))"); + valid_pattern ("^e(d|^c((a|b)))"); + valid_pattern ("^e(d|c^((a|b)))"); + valid_pattern ("^e(d|c(^(a|b)))"); + valid_pattern ("^e(d|c((^a|b)))"); + valid_pattern ("^e(d|c((a|^b)))"); + valid_pattern ("^e(d|c((a|b^)))"); + valid_pattern ("^e(d|c((a|b)^))"); + valid_pattern ("^e(d|c((a|b))^)"); + valid_pattern ("^e(d|c((a|b)))^"); + valid_pattern ("e$(d$|c((a$|b$)))"); + valid_pattern ("e(d$|c$((a$|b$)))"); + valid_pattern ("e(d$|c($(a$|b$)))"); + valid_pattern ("e(d$|c(($a$|b$)))"); + valid_pattern ("e$(d$|c((a|b)$))"); + valid_pattern ("e($d$|c((a|b)$))"); + valid_pattern ("e(d$|$c((a|b)$))"); + valid_pattern ("e(d$|c$((a|b)$))"); + valid_pattern ("e(d$|c($(a|b)$))"); + valid_pattern ("e(d$|c(($a|b)$))"); + valid_pattern ("e(d$|c((a|$b)$))"); + valid_pattern ("e(d$|c((a$|$b$)))"); + + valid_pattern ("e$(d$|c((a|b))$)"); + valid_pattern ("e($d$|c((a|b))$)"); + valid_pattern ("e(d$|$c((a|b))$)"); + valid_pattern ("e(d$|c$((a|b))$)"); + valid_pattern ("e(d$|c($(a|b))$)"); + valid_pattern ("e(d$|c(($a|b))$)"); + valid_pattern ("e(d$|c((a|$b))$)"); + valid_pattern ("e$(d$|c((a|b)))$"); + valid_pattern ("e($d$|c((a|b)))$"); + valid_pattern ("e(d$|$c((a|b)))$"); + valid_pattern ("e(d$|c$((a|b)))$"); + valid_pattern ("e(d$|c($(a|b)))$"); + valid_pattern ("e(d$|c(($a|b)))$"); + valid_pattern ("e(d$|c((a|$b)))$"); + valid_pattern ("(((^a|^b)^)c)|^de"); + valid_pattern ("(((^a|^b))^c)|^de"); + valid_pattern ("(((^a|^b))c)^|^de"); + valid_pattern ("$(((a|b))c$)|de$"); + valid_pattern ("($((a|b))c$)|de$"); + valid_pattern ("(($(a|b))c$)|de$"); + valid_pattern ("((($a|b))c$)|de$"); + valid_pattern ("(((a|$b))c$)|de$"); + valid_pattern ("(((a|b)$)c$)|de$"); + valid_pattern ("(((a|b))$c$)|de$"); + valid_pattern ("$(((a|b))c)$|de$"); + valid_pattern ("($((a|b))c)$|de$"); + valid_pattern ("(($(a|b))c)$|de$"); + valid_pattern ("((($a|b))c)$|de$"); + valid_pattern ("(((a|$b))c)$|de$"); + valid_pattern ("(((a|b)$)c)$|de$"); + valid_pattern ("(((a|b))$c)$|de$"); + valid_pattern ("^ed|^(c((a|b)))^"); + valid_pattern ("^ed|^(c((a|b))^)"); + valid_pattern ("^ed|^(c((a|b)^))"); + valid_pattern ("^ed|^(c((a|b^)))"); + valid_pattern ("^ed|^(c((a^|b)))"); + valid_pattern ("^ed|^(c((^a|b)))"); + valid_pattern ("^ed|^(c(^(a|b)))"); + valid_pattern ("^ed|^(c^((a|b)))"); + valid_pattern ("^ed|(^c((a|b)))^"); + valid_pattern ("^ed|(^c((a|b))^)"); + valid_pattern ("^ed|(^c((a|b)^))"); + valid_pattern ("^ed|(^c((a|b^)))"); + valid_pattern ("^ed|(^c((a|^b)))"); + valid_pattern ("^ed|(^c((a^|b)))"); + valid_pattern ("^ed|(^c((^a|b)))"); + valid_pattern ("^ed|(^c(^(a|b)))"); + valid_pattern ("^ed|(^c(^(a|b)))"); + valid_pattern ("^ed|(^c^((a|b)))"); + valid_pattern ("ed$|$(c((a|b)))$"); + valid_pattern ("ed$|($c((a|b)))$"); + valid_pattern ("ed$|(c$((a|b)))$"); + valid_pattern ("ed$|(c($(a|b)))$"); + valid_pattern ("ed$|(c(($a|b)))$"); + valid_pattern ("ed$|(c((a|$b)))$"); + valid_pattern ("ed$|$(c((a|b))$)"); + valid_pattern ("ed$|($c((a|b))$)"); + valid_pattern ("ed$|(c$((a|b))$)"); + valid_pattern ("ed$|(c($(a|b))$)"); + valid_pattern ("ed$|(c(($a|b))$)"); + valid_pattern ("ed$|(c((a|$b))$)"); + valid_pattern ("ed$|$(c((a|b)$))"); + valid_pattern ("ed$|($c((a|b)$))"); + valid_pattern ("ed$|(c$((a|b)$))"); + valid_pattern ("ed$|(c($(a|b)$))"); + valid_pattern ("ed$|(c(($a|b)$))"); + valid_pattern ("ed$|(c((a|$b)$))"); + valid_pattern ("ed$|$(c((a|b)$))"); + valid_pattern ("ed$|($c((a|b)$))"); + valid_pattern ("ed$|(c$((a|b)$))"); + valid_pattern ("ed$|(c($(a|b)$))"); + valid_pattern ("ed$|(c(($a|b)$))"); + valid_pattern ("ed$|(c((a|$b)$))"); + valid_pattern ("ed$|$(c((a|b)$))"); + valid_pattern ("ed$|($c((a|b)$))"); + valid_pattern ("ed$|(c$((a|b)$))"); + valid_pattern ("ed$|(c($(a|b)$))"); + valid_pattern ("ed$|(c(($a|b)$))"); + valid_pattern ("ed$|(c((a|$b)$))"); + valid_pattern ("ed$|$(c((a|b)$))"); + valid_pattern ("ed$|($c((a|b)$))"); + valid_pattern ("ed$|(c$((a|b)$))"); + valid_pattern ("ed$|(c($(a|b)$))"); + valid_pattern ("ed$|(c(($a|b)$))"); + valid_pattern ("ed$|(c((a|$b)$))"); + valid_pattern ("ed$|$(c((a|b)$))"); + valid_pattern ("ed$|($c((a|b)$))"); + valid_pattern ("ed$|(c$((a|b)$))"); + valid_pattern ("ed$|(c($(a|b)$))"); + valid_pattern ("ed$|(c(($a|b)$))"); + valid_pattern ("ed$|(c((a|$b)$))"); + valid_pattern ("ed$|$(c((a$|b$)))"); + valid_pattern ("ed$|($c((a$|b$)))"); + valid_pattern ("ed$|(c$((a$|b$)))"); + valid_pattern ("ed$|(c($(a$|b$)))"); + valid_pattern ("ed$|(c(($a$|b$)))"); + valid_pattern ("ed$|(c((a$|$b$)))"); + valid_pattern ("^a(b|c)^|^d"); + valid_pattern ("^a(b|c^)|^d"); + valid_pattern ("^a(b|^c)|^d"); + valid_pattern ("^a(b^|c)|^d"); + valid_pattern ("^a(^b|c)|^d"); + valid_pattern ("^a^(b|c)|^d"); + valid_pattern ("$a(b$|c$)|d$"); + valid_pattern ("a$(b$|c$)|d$"); + valid_pattern ("a($b$|c$)|d$"); + valid_pattern ("a(b$|$c$)|d$"); + valid_pattern ("a(b$|c$)|$d$"); + valid_pattern ("^(a^)(b|c)|^d"); + valid_pattern ("^(a)^(b|c)|^d"); + valid_pattern ("^(a)(^b|c)|^d"); + valid_pattern ("^(a)(b^|c)|^d"); + valid_pattern ("^(a)(b|^c)|^d"); + valid_pattern ("^(a)(b|c^)|^d"); + valid_pattern ("^(a)(b|c)^|^d"); + valid_pattern ("(^a^)(b|c)|^d"); + valid_pattern ("(^a)^(b|c)|^d"); + valid_pattern ("(^a)(^b|c)|^d"); + valid_pattern ("(^a)(b^|c)|^d"); + valid_pattern ("(^a)(b|^c)|^d"); + valid_pattern ("(^a)(b|c^)|^d"); + valid_pattern ("(^a)(b|c)^|^d"); + + valid_pattern ("(a)(b$|c$)d$"); + valid_pattern ("(a)(b|$c)$|d$"); + valid_pattern ("(a)($b|c)$|d$"); + valid_pattern ("(a)$(b|c)$|d$"); + valid_pattern ("(a$)(b|c)$|d$"); + valid_pattern ("($a)(b|c)$|d$"); + valid_pattern ("$(a)(b|c)$|d$"); + valid_pattern ("(b|c)($a)$|d$"); + valid_pattern ("(b|c)$(a)$|d$"); + valid_pattern ("(b|c$)(a)$|d$"); + valid_pattern ("(b|$c)(a)$|d$"); + valid_pattern ("(b$|c)(a)$|d$"); + valid_pattern ("($b|c)(a)$|d$"); + valid_pattern ("$(b|c)(a)$|d$"); + valid_pattern ("(b|c)($a$)|d$"); + valid_pattern ("(b|c)$(a$)|d$"); + valid_pattern ("(b|c$)(a$)|d$"); + valid_pattern ("(b|$c)(a$)|d$"); + valid_pattern ("(b$|c)(a$)|d$"); + valid_pattern ("($b|c)(a$)|d$"); + valid_pattern ("$(b|c)(a$)|d$"); + valid_pattern ("(a)$(b$|c$)|d$"); + valid_pattern ("(a$)(b$|c$)|d$"); + valid_pattern ("($a)(b$|c$)|d$"); + valid_pattern ("$(a)(b$|c$)|d$"); + valid_pattern ("^d|^(b^|c)(a)"); + valid_pattern ("^d|^(b|c^)(a)"); + valid_pattern ("^d|^(b|c)^(a)"); + valid_pattern ("^d|^(b|c)(^a)"); + valid_pattern ("^d|^(b|c)(a^)"); + valid_pattern ("^d|^(b|c)(a)^"); + valid_pattern ("^d|(^b|^c^)(a)"); + valid_pattern ("^d|(^b|^c)^(a)"); + valid_pattern ("^d|(^b|^c)(^a)"); + valid_pattern ("^d|(^b|^c)(a^)"); + valid_pattern ("^d|(^b|^c)(a)^"); + valid_pattern ("d$|(b|c)($a$)"); + valid_pattern ("d$|(b|c)$(a$)"); + valid_pattern ("d$|(b|c$)(a$)"); + valid_pattern ("d$|(b$|c)(a$)"); + valid_pattern ("d$|($b|c)(a$)"); + valid_pattern ("d$|$(b|c)(a$)"); + valid_pattern ("d$|(b|c)($a)$"); + valid_pattern ("d$|(b|c)$(a)$"); + valid_pattern ("d$|(b|c$)(a)$"); + valid_pattern ("d$|(b$|c)(a)$"); + valid_pattern ("d$|($b|c)(a)$"); + valid_pattern ("d$|$(b|c)(a)$"); + valid_pattern ("^d|^(a^)(b|c)"); + valid_pattern ("^d|^(a)^(b|c)"); + valid_pattern ("^d|^(a)(^b|c)"); + valid_pattern ("^d|^(a)(b^|c)"); + valid_pattern ("^d|^(a)(b|^c)"); + valid_pattern ("^d|^(a)(b|c^)"); + valid_pattern ("^d|^(a)(b|c)^"); + valid_pattern ("^d|(^a^)(b|c)"); + valid_pattern ("^d|(^a)^(b|c)"); + valid_pattern ("^d|(^a)(^b|c)"); + valid_pattern ("^d|(^a)(b^|c)"); + valid_pattern ("^d|(^a)(b|^c)"); + valid_pattern ("^d|(^a)(b|c^)"); + valid_pattern ("^d|(^a)(b|c)^"); + valid_pattern ("d$|(a)$(b$|c$)"); + valid_pattern ("d$|(a$)(b$|c$)"); + valid_pattern ("d$|($a)(b$|c$)"); + valid_pattern ("d$|$(a)(b$|c$)"); + valid_pattern ("d$|(a)(b|$c)$"); + valid_pattern ("d$|(a)($b|c)$"); + valid_pattern ("d$|(a)$(b|c)$"); + valid_pattern ("d$|(a$)(b|c)$"); + valid_pattern ("d$|($a)(b|c)$"); + valid_pattern ("d$|$(a)(b|c)$"); + valid_pattern ("((^a|^b)|^c)|^d^"); + valid_pattern ("((^a|^b)|^c)^|^d"); + valid_pattern ("((^a|^b)|^c^)|^d"); + valid_pattern ("((^a|^b)^|^c)|^d"); + valid_pattern ("((^a|^b^)|^c)|^d"); + valid_pattern ("((^a^|^b)|^c)|^d"); + valid_pattern ("((a|b)|c)|$d$"); + valid_pattern ("((a|b)|$c)|d$"); + valid_pattern ("((a|$b)|c)|d$"); + valid_pattern ("(($a|b)|c)|d$"); + valid_pattern ("($(a|b)|c)|d$"); + valid_pattern ("$((a|b)|c)|d$"); + valid_pattern ("^d^|(c|(a|b))"); + valid_pattern ("^d|(c^|(a|b))"); + valid_pattern ("^d|(c|(a^|b))"); + valid_pattern ("^d|(c|(a|b^))"); + valid_pattern ("^d|(c|(a|b)^)"); + valid_pattern ("^d|(c|(a|b))^"); + valid_pattern ("d$|(c$|(a$|$b$))"); + valid_pattern ("d$|(c$|($a$|b$))"); + valid_pattern ("d$|($c$|(a$|b$))"); + valid_pattern ("d$|$(c$|(a$|b$))"); + valid_pattern ("$d$|(c$|(a$|b$))"); + valid_pattern ("d$|(c$|(a|$b)$)"); + valid_pattern ("d$|(c$|($a|b)$)"); + valid_pattern ("d$|($c$|(a|b)$)"); + valid_pattern ("d$|$(c$|(a|b)$)"); + valid_pattern ("$d$|(c$|(a|b)$)"); + valid_pattern ("d$|(c$|(a|$b))$"); + valid_pattern ("d$|(c$|($a|b))$"); + valid_pattern ("d$|($c$|(a|b))$"); + valid_pattern ("d$|$(c$|(a|b))$"); + valid_pattern ("$d$|(c$|(a|b))$"); + valid_pattern ("^c^|(^a|^b)"); + valid_pattern ("^c|(^a^|^b)"); + valid_pattern ("^c|(^a|^b^)"); + valid_pattern ("^c|(^a|^b)^"); + valid_pattern ("c$|(a$|$b$)"); + valid_pattern ("c$|($a$|b$)"); + valid_pattern ("c$|$(a$|b$)"); + valid_pattern ("$c$|(a$|b$)"); + valid_pattern ("^d^(c|e((a|b)))"); + valid_pattern ("^d(^c|e((a|b)))"); + valid_pattern ("^d(c^|e((a|b)))"); + valid_pattern ("^d(c|^e((a|b)))"); + valid_pattern ("^d(c|e^((a|b)))"); + valid_pattern ("^d(c|e(^(a|b)))"); + valid_pattern ("^d(c|e((^a|b)))"); + valid_pattern ("^d(c|e((a|^b)))"); + valid_pattern ("^d(c|e((a|b^)))"); + valid_pattern ("^d(c|e((a|b)^))"); + valid_pattern ("^d(c|e((a|b))^)"); + valid_pattern ("^d(c|e((a|b)))^"); + valid_pattern ("d(c$|e($(a$|b$)))"); + valid_pattern ("d(c$|e$((a$|b$)))"); + valid_pattern ("d(c$|$e((a$|b$)))"); + valid_pattern ("d($c$|e((a$|b$)))"); + valid_pattern ("d$(c$|e((a$|b$)))"); + valid_pattern ("$d(c$|e((a$|b$)))"); + valid_pattern ("^d|^a^(b|c)"); + valid_pattern ("^d|^a(^b|c)"); + valid_pattern ("^d|^a(b^|c)"); + valid_pattern ("^d|^a(b|^c)"); + valid_pattern ("^d|^a(b|c^)"); + valid_pattern ("^d|^a(b|c)^"); + valid_pattern ("d$|a($b$|c$)"); + valid_pattern ("d$|a$(b$|c$)"); + valid_pattern ("d$|$a(b$|c$)"); + valid_pattern ("$d$|a(b$|c$)"); + valid_pattern ("^d|^(b^|c)a"); + valid_pattern ("^d|^(b|c^)a"); + valid_pattern ("^d|^(b|c)^a"); + valid_pattern ("^d|^(b|c)a^"); + valid_pattern ("d$|(b|c)$a$"); + valid_pattern ("d$|(b|c$)a$"); + valid_pattern ("d$|(b|$c)a$"); + valid_pattern ("d$|(b$|c)a$"); + valid_pattern ("d$|($b|c)a$"); + valid_pattern ("d$|$(b|c)a$"); + valid_pattern ("$d$|(b|c)a$"); + + /* xx Do these use all the valid_nonposix_pattern ones in other_test.c? */ + + TEST_SEARCH ("(^a|^b)c", "ac", 0, 2); + TEST_SEARCH ("(^a|^b)c", "bc", 0, 2); + TEST_SEARCH ("c(a$|b$)", "ca", 0, 2); + TEST_SEARCH ("c(a$|b$)", "cb", 0, 2); + TEST_SEARCH ("^(a|b)|^c", "ad", 0, 2); + TEST_SEARCH ("^(a|b)|^c", "bd", 0, 2); + TEST_SEARCH ("(a|b)$|c$", "da", 0, 2); + TEST_SEARCH ("(a|b)$|c$", "db", 0, 2); + TEST_SEARCH ("(a|b)$|c$", "dc", 0, 2); + TEST_SEARCH ("(^a|^b)|^c", "ad", 0, 2); + TEST_SEARCH ("(^a|^b)|^c", "bd", 0, 2); + TEST_SEARCH ("(^a|^b)|^c", "cd", 0, 2); + TEST_SEARCH ("(a$|b$)|c$", "da", 0, 2); + TEST_SEARCH ("(a$|b$)|c$", "db", 0, 2); + TEST_SEARCH ("(a$|b$)|c$", "dc", 0, 2); + TEST_SEARCH ("^c|(^a|^b)", "ad", 0, 2); + TEST_SEARCH ("^c|(^a|^b)", "bd", 0, 2); + TEST_SEARCH ("^c|(^a|^b)", "cd", 0, 2); + TEST_SEARCH ("c$|(a$|b$)", "da", 0, 2); + TEST_SEARCH ("c$|(a$|b$)", "db", 0, 2); + TEST_SEARCH ("c$|(a$|b$)", "dc", 0, 2); + TEST_SEARCH ("^c|^(a|b)", "ad", 0, 2); + TEST_SEARCH ("^c|^(a|b)", "bd", 0, 2); + TEST_SEARCH ("^c|^(a|b)", "cd", 0, 2); + TEST_SEARCH ("c$|(a|b)$", "da", 0, 2); + TEST_SEARCH ("c$|(a|b)$", "db", 0, 2); + TEST_SEARCH ("c$|(a|b)$", "dc", 0, 2); + TEST_SEARCH ("(^a|^b)c|^d", "ace", 0, 3); + TEST_SEARCH ("(^a|^b)c|^d", "bce", 0, 3); + TEST_SEARCH ("(^a|^b)c|^d", "de", 0, 2); + TEST_SEARCH ("(a|b)c$|d$", "eac", 0, 3); + TEST_SEARCH ("(a|b)c$|d$", "ebc", 0, 3); + TEST_SEARCH ("(a|b)c$|d$", "ed", 0, 3); + TEST_SEARCH ("^d|^c(a|b)", "cae", 0, 3); + TEST_SEARCH ("^d|^c(a|b)", "cbe", 0, 3); + TEST_SEARCH ("^d|^c(a|b)", "de", 0, 3); + TEST_SEARCH ("d$|c(a$|b$)", "eca", 0, 3); + TEST_SEARCH ("d$|c(a$|b$)", "ecb", 0, 3); + TEST_SEARCH ("d$|c(a$|b$)", "ed", 0, 3); + + TEST_SEARCH ("(((^a|^b))c|^d)e", "acef", 0, 4); + TEST_SEARCH ("(((^a|^b))c|^d)e", "bcef", 0, 4); + TEST_SEARCH ("(((^a|^b))c|^d)e", "def", 0, 3); + + TEST_SEARCH ("((^(a|b))c|^d)e", "acef", 0, 4); + TEST_SEARCH ("((^(a|b))c|^d)e", "bcef", 0, 4); + TEST_SEARCH ("((^(a|b))c|^d)e", "def", 0, 3); + + TEST_SEARCH ("(^((a|b))c|^d)e", "acef", 0, 4); + TEST_SEARCH ("(^((a|b))c|^d)e", "bcef", 0, 4); + TEST_SEARCH ("(^((a|b))c|^d)e", "def", 0, 3); + + TEST_SEARCH ("(((a|b))c|d)e$", "face", 0, 4); + TEST_SEARCH ("(((a|b))c|d)e$", "fbce", 0, 4); + TEST_SEARCH ("(((a|b))c|d)e$", "fde", 0, 3); + + TEST_SEARCH ("^e(d|c((a|b)))", "edf", 0, 3); + TEST_SEARCH ("^e(d|c((a|b)))", "ecaf", 0, 4); + TEST_SEARCH ("^e(d|c((a|b)))", "ecbf", 0, 4); + + TEST_SEARCH ("e(d$|c((a$|b$)))", "fed", 0, 3); + TEST_SEARCH ("e(d$|c((a$|b$)))", "feca", 0, 4); + TEST_SEARCH ("e(d$|c((a$|b$)))", "fecb", 0, 4); + + TEST_SEARCH ("e(d$|c((a|b)$))", "fed", 0, 3); + TEST_SEARCH ("e(d$|c((a|b)$))", "feca", 0, 4); + TEST_SEARCH ("e(d$|c((a|b)$))", "fecb", 0, 4); + + TEST_SEARCH ("e(d$|c((a|b))$)", "fed", 0, 3); + TEST_SEARCH ("e(d$|c((a|b))$)", "feca", 0, 3); + TEST_SEARCH ("e(d$|c((a|b))$)", "fecb", 0, 3); + + TEST_SEARCH ("e(d$|c((a|b)))$", "fed", 0, 3); + TEST_SEARCH ("e(d$|c((a|b)))$", "feca", 0, 3); + TEST_SEARCH ("e(d$|c((a|b)))$", "fecb", 0, 3); + + TEST_SEARCH ("(((^a|^b))c)|^de", "acf", 0, 3); + TEST_SEARCH ("(((^a|^b))c)|^de", "bcf", 0, 3); + TEST_SEARCH ("(((^a|^b))c)|^de", "def", 0, 3); + + TEST_SEARCH ("(((a|b))c$)|de$", "fac", 0, 3); + TEST_SEARCH ("(((a|b))c$)|de$", "fbc", 0, 3); + TEST_SEARCH ("(((a|b))c$)|de$", "fde", 0, 3); + + TEST_SEARCH ("(((a|b))c)$|de$", "fac", 0, 3); + TEST_SEARCH ("(((a|b))c)$|de$", "fbc", 0, 3); + TEST_SEARCH ("(((a|b))c)$|de$", "fde", 0, 3); + + TEST_SEARCH ("^ed|^(c((a|b)))", "edf", 0, 3); + TEST_SEARCH ("^ed|^(c((a|b)))", "caf", 0, 3); + TEST_SEARCH ("^ed|^(c((a|b)))", "cbf", 0, 3); + + TEST_SEARCH ("^ed|(^c((a|b)))", "edf", 0, 3); + TEST_SEARCH ("^ed|(^c((a|b)))", "caf", 0, 3); + TEST_SEARCH ("^ed|(^c((a|b)))", "cbf", 0, 3); + + TEST_SEARCH ("ed$|(c((a|b)))$", "fed", 0, 3); + TEST_SEARCH ("ed$|(c((a|b)))$", "fca", 0, 3); + TEST_SEARCH ("ed$|(c((a|b)))$", "fcb", 0, 3); + + TEST_SEARCH ("ed$|(c((a|b))$)", "fed", 0, 3); + TEST_SEARCH ("ed$|(c((a|b))$)", "fca", 0, 3); + TEST_SEARCH ("ed$|(c((a|b))$)", "fcb", 0, 3); + + TEST_SEARCH ("ed$|(c((a|b)$))", "fed", 0, 3); + TEST_SEARCH ("ed$|(c((a|b)$))", "fca", 0, 3); + TEST_SEARCH ("ed$|(c((a|b)$))", "fcb", 0, 3); + + TEST_SEARCH ("ed$|(c((a$|b$)))", "fed", 0, 3); + TEST_SEARCH ("ed$|(c((a$|b$)))", "fca", 0, 3); + TEST_SEARCH ("ed$|(c((a$|b$)))", "fcb", 0, 3); + + TEST_SEARCH ("^a(b|c)|^d", "abe", 0, 3); + TEST_SEARCH ("^a(b|c)|^d", "ace", 0, 3); + TEST_SEARCH ("^a(b|c)|^d", "df", 0, 2); + + TEST_SEARCH ("a(b$|c$)|d$", "fab", 0, 3); + TEST_SEARCH ("a(b$|c$)|d$", "fac", 0, 3); + TEST_SEARCH ("a(b$|c$)|d$", "fd", 0, 2); + + TEST_SEARCH ("^(a)(b|c)|^d", "abe", 0, 3); + TEST_SEARCH ("^(a)(b|c)|^d", "ace", 0, 3); + TEST_SEARCH ("^(a)(b|c)|^d", "df", 0, 2); + + TEST_SEARCH ("(^a)(b|c)|^d", "abe", 0, 3); + TEST_SEARCH ("(^a)(b|c)|^d", "ace", 0, 3); + TEST_SEARCH ("(^a)(b|c)|^d", "df", 0, 2); + + TEST_SEARCH ("(a)(b|c)$|d$", "fab", 0, 3); + TEST_SEARCH ("(a)(b|c)$|d$", "fac", 0, 3); + TEST_SEARCH ("(a)(b|c)$|d$", "fd", 0, 2); + + TEST_SEARCH ("(b|c)(a)$|d$", "fba", 0, 3); + TEST_SEARCH ("(b|c)(a)$|d$", "fca", 0, 3); + TEST_SEARCH ("(b|c)(a)$|d$", "fd", 0, 2); + + TEST_SEARCH ("(b|c)(a$)|d$", "fba", 0, 3); + TEST_SEARCH ("(b|c)(a$)|d$", "fca", 0, 3); + TEST_SEARCH ("(b|c)(a$)|d$", "fd", 0, 2); + + TEST_SEARCH ("(a)(b$|c$)|d$", "fab", 0, 3); + TEST_SEARCH ("(a)(b$|c$)|d$", "fac", 0, 3); + TEST_SEARCH ("(a)(b$|c$)|d$", "fd", 0, 2); + + TEST_SEARCH ("^d|^(b|c)(a)", "df", 0, 2); + TEST_SEARCH ("^d|^(b|c)(a)", "baf", 0, 3); + TEST_SEARCH ("^d|^(b|c)(a)", "caf", 0, 3); + + TEST_SEARCH ("^d|(^b|^c)(a)", "df", 0, 2); + TEST_SEARCH ("^d|(^b|^c)(a)", "baf", 0, 3); + TEST_SEARCH ("^d|(^b|^c)(a)", "caf", 0, 3); + + TEST_SEARCH ("d$|(b|c)(a$)", "fd", 0, 2); + TEST_SEARCH ("d$|(b|c)(a$)", "fba", 0, 3); + TEST_SEARCH ("d$|(b|c)(a$)", "fca", 0, 3); + + TEST_SEARCH ("d$|(b|c)(a)$", "fd", 0, 2); + TEST_SEARCH ("d$|(b|c)(a)$", "fba", 0, 3); + TEST_SEARCH ("d$|(b|c)(a)$", "fca", 0, 3); + + TEST_SEARCH ("d$|(b|c)(a$)", "fd", 0, 2); + TEST_SEARCH ("d$|(b|c)(a$)", "fba", 0, 3); + TEST_SEARCH ("d$|(b|c)(a$)", "fca", 0, 3); + + TEST_SEARCH ("^d|^(a)(b|c)", "df", 0, 2); + TEST_SEARCH ("^d|^(a)(b|c)", "abf", 0, 3); + TEST_SEARCH ("^d|^(a)(b|c)", "acf", 0, 3); + + TEST_SEARCH ("^d|(^a)(b|c)", "df", 0, 2); + TEST_SEARCH ("^d|(^a)(b|c)", "abf", 0, 3); + TEST_SEARCH ("^d|(^a)(b|c)", "acf", 0, 3); + + TEST_SEARCH ("d$|(a)(b$|c$)", "fd", 0, 2); + TEST_SEARCH ("d$|(a)(b$|c$)", "fab", 0, 3); + TEST_SEARCH ("d$|(a)(b$|c$)", "fac", 0, 3); + + TEST_SEARCH ("d$|(a)(b|c)$", "fd", 0, 2); + TEST_SEARCH ("d$|(a)(b|c)$", "fab", 0, 3); + TEST_SEARCH ("d$|(a)(b|c)$", "fac", 0, 3); + + TEST_SEARCH ("((^a|^b)|^c)|^d", "ae", 0, 2); + TEST_SEARCH ("((^a|^b)|^c)|^d", "be", 0, 2); + TEST_SEARCH ("((^a|^b)|^c)|^d", "ce", 0, 2); + TEST_SEARCH ("((^a|^b)|^c)|^d", "de", 0, 2); + + TEST_SEARCH ("((a|b)|c)|d$", "ed", 0, 2); + TEST_SEARCH ("((a|b)|c)|d$", "ea", 0, 2); + TEST_SEARCH ("((a|b)|c)|d$", "eb", 0, 2); + TEST_SEARCH ("((a|b)|c)|d$", "ec", 0, 2); + + TEST_SEARCH ("^d|(c|(a|b))", "de", 0, 2); + + TEST_SEARCH ("d$|(c$|(a$|b$))", "ed", 0, 2); + TEST_SEARCH ("d$|(c$|(a$|b$))", "ec", 0, 2); + TEST_SEARCH ("d$|(c$|(a$|b$))", "ea", 0, 2); + TEST_SEARCH ("d$|(c$|(a$|b$))", "eb", 0, 2); + + TEST_SEARCH ("d$|(c$|(a|b)$)", "ed", 0, 2); + TEST_SEARCH ("d$|(c$|(a|b)$)", "ec", 0, 2); + TEST_SEARCH ("d$|(c$|(a|b)$)", "ea", 0, 2); + TEST_SEARCH ("d$|(c$|(a|b)$)", "eb", 0, 2); + + TEST_SEARCH ("d$|(c$|(a|b))$", "ed", 0, 2); + TEST_SEARCH ("d$|(c$|(a|b))$", "ec", 0, 2); + TEST_SEARCH ("d$|(c$|(a|b))$", "ea", 0, 2); + TEST_SEARCH ("d$|(c$|(a|b))$", "eb", 0, 2); + + test_match ("a|^b", "b"); + test_match ("a|b$", "b"); + test_match ("^b|a", "b"); + test_match ("b$|a", "b"); + test_match ("(^a)", "a"); + test_match ("(a$)", "a"); + TEST_SEARCH ("c|^ab", "aba", 0, 3); + TEST_SEARCH ("c|ba$", "aba", 0, 3); + TEST_SEARCH ("^ab|c", "aba", 0, 3); + TEST_SEARCH ("ba$|c", "aba", 0, 3); + TEST_SEARCH ("(^a)", "ab", 0, 2); + TEST_SEARCH ("(a$)", "ba", 0, 2); + + TEST_SEARCH ("(^a$)", "a", 0, 1); + TEST_SEARCH ("(^a)", "ab", 0, 2); + TEST_SEARCH ("(b$)", "ab", 0, 2); + + /* Backtracking. */ + /* Per POSIX D11.1 p. 108, leftmost longest match. */ + test_match ("(wee|week)(knights|night)", "weeknights"); + + test_match ("(fooq|foo)qbar", "fooqbar"); + test_match ("(fooq|foo)(qbarx|bar)", "fooqbarx"); + + /* Take first alternative that does the longest match. */ + test_all_registers ("(fooq|(foo)|(fo))((qbarx)|(oqbarx)|bar)", "fooqbarx", + "", 0, 8, 0, 3, 0, 3, -1, -1, 3, 8, 3, 8, -1, -1, -1, -1, -1, -1, + -1, -1); + + test_match ("(fooq|foo)*qbar", "fooqbar"); + test_match ("(fooq|foo)*(qbar)", "fooqbar"); + test_match ("(fooq|foo)*(qbar)*", "fooqbar"); + + test_match ("(fooq|fo|o)*qbar", "fooqbar"); + test_match ("(fooq|fo|o)*(qbar)", "fooqbar"); + test_match ("(fooq|fo|o)*(qbar)*", "fooqbar"); + + test_match ("(fooq|fo|o)*(qbar|q)*", "fooqbar"); + test_match ("(fooq|foo)*(qbarx|bar)", "fooqbarx"); + test_match ("(fooq|foo)*(qbarx|bar)*", "fooqbarx"); + + test_match ("(fooq|fo|o)+(qbar|q)+", "fooqbar"); + test_match ("(fooq|foo)+(qbarx|bar)", "fooqbarx"); + test_match ("(fooq|foo)+(qbarx|bar)+", "fooqbarx"); + + /* Per Mike Haertel. */ + test_match ("(foo|foobarfoo)(bar)*", "foobarfoo"); + + /* Combination. */ + test_match ("[ab]?c", "ac"); + test_match ("[ab]*c", "ac"); + test_match ("[ab]+c", "ac"); + test_match ("(a|b)?c", "ac"); + test_match ("(a|b)*c", "ac"); + test_match ("(a|b)+c", "ac"); + test_match ("(a*c)?b", "b"); + test_match ("(a*c)+b", "aacb"); + /* Registers. */ + /* Per David A. Willcox. */ + test_match ("a((b)|(c))d", "acd"); + test_all_registers ("a((b)|(c))d", "acd", "", 0, 3, 1, 2, -1, -1, 1, 2, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); + + + /* Extended regular expressions, continued; these don't match their strings. */ + test_should_match = false; + +#if 0 + /* Invalid use of special characters. */ + /* These are not invalid anymore, since POSIX says the behavior is + undefined, and we prefer context-independent to context-invalid. */ + invalid_pattern (REG_BADRPT, "*"); + invalid_pattern (REG_BADRPT, "a|*"); + invalid_pattern (REG_BADRPT, "(*)"); + invalid_pattern (REG_BADRPT, "^*"); + invalid_pattern (REG_BADRPT, "+"); + invalid_pattern (REG_BADRPT, "a|+"); + invalid_pattern (REG_BADRPT, "(+)"); + invalid_pattern (REG_BADRPT, "^+"); + + invalid_pattern (REG_BADRPT, "?"); + invalid_pattern (REG_BADRPT, "a|?"); + invalid_pattern (REG_BADRPT, "(?)"); + invalid_pattern (REG_BADRPT, "^?"); + + invalid_pattern (REG_BADPAT, "|"); + invalid_pattern (REG_BADPAT, "a|"); + invalid_pattern (REG_BADPAT, "a||"); + invalid_pattern (REG_BADPAT, "(|a)"); + invalid_pattern (REG_BADPAT, "(a|)"); + + invalid_pattern (REG_BADPAT, PARENS_TO_OPS ("(|)")); + + invalid_pattern (REG_BADRPT, "{1}"); + invalid_pattern (REG_BADRPT, "a|{1}"); + invalid_pattern (REG_BADRPT, "^{1}"); + invalid_pattern (REG_BADRPT, "({1})"); + + invalid_pattern (REG_BADPAT, "|b"); + + invalid_pattern (REG_BADRPT, "^{0,}*"); + invalid_pattern (REG_BADRPT, "$*"); + invalid_pattern (REG_BADRPT, "${0,}*"); +#endif /* 0 */ + + invalid_pattern (REG_EESCAPE, "\\"); + + test_match ("a?b", "a"); + + + test_match ("a+", ""); + test_match ("a+b", "a"); + test_match ("a?", "b"); + +#if 0 + /* We make empty groups valid now, since they are undefined in POSIX. + (13 Sep 92) */ + /* Subexpressions. */ + invalid_pattern (REG_BADPAT, "()"); + invalid_pattern (REG_BADPAT, "a()"); + invalid_pattern (REG_BADPAT, "()b"); + invalid_pattern (REG_BADPAT, "a()b"); + invalid_pattern (REG_BADPAT, "()*"); + invalid_pattern (REG_BADPAT, "(()*"); +#endif + /* Invalid intervals. */ + test_match ("a{2}*", "aaa"); + test_match ("a{2}?", "aaa"); + test_match ("a{2}+", "aaa"); + test_match ("a{2}{2}", "aaa"); + test_match ("a{1}{1}{2}", "aaa"); + test_match ("a{1}{1}{2}", "a"); + /* Invalid alternation. */ + test_match ("a|b", "c"); + + TEST_SEARCH ("c|^ba", "aba", 0, 3); + TEST_SEARCH ("c|ab$", "aba", 0, 3); + TEST_SEARCH ("^ba|c", "aba", 0, 3); + TEST_SEARCH ("ab$|c", "aba", 0, 3); + /* Invalid anchoring. */ + TEST_SEARCH ("(^a)", "ba", 0, 2); + TEST_SEARCH ("(b$)", "ba", 0, 2); + + printf ("\nFinished POSIX extended tests.\n"); +} + + + +/* +Local variables: +make-backup-files: t +version-control: t +trim-versions-without-asking: nil +End: +*/ diff --git a/gnu/lib/libregex/test/psx-generic.c b/gnu/lib/libregex/test/psx-generic.c new file mode 100644 index 0000000000..340e938755 --- /dev/null +++ b/gnu/lib/libregex/test/psx-generic.c @@ -0,0 +1,336 @@ +/* psx-generic.c: test POSIX re's independent of us using basic or + extended syntax. */ + +#include "test.h" + + +void +test_posix_generic () +{ + int omit_generic_tests = 0; /* reset in debugger to skip */ + + if (omit_generic_tests) + return; + /* Tests somewhat in the order of P1003.2. */ + + /* Both posix basic and extended; should match. */ + + printf ("\nStarting generic POSIX tests.\n"); + test_grouping (); + test_intervals (); + + test_should_match = true; + /* Ordinary characters. */ + printf ("\nContinuing generic POSIX tests.\n"); + + MATCH_SELF (""); + test_fastmap ("", "", 0, 0); + test_fastmap_search ("", "", "", 0, 0, 2, 0, 0); + TEST_REGISTERS ("", "", 0, 0, -1, -1, -1, -1); + TEST_SEARCH ("", "", 0, 0); + TEST_SEARCH_2 ("", "", "", 0, 1, 0); + + MATCH_SELF ("abc"); + test_fastmap ("abc", "a", 0, 0); + TEST_REGISTERS ("abc", "abc", 0, 3, -1, -1, -1, -1); + TEST_REGISTERS ("abc", "xabcx", 1, 4, -1, -1, -1, -1); + + test_match ("\\a","a"); + test_match ("\\0", "0"); + + TEST_SEARCH ("a", "ab", 0, 2); + TEST_SEARCH ("b", "ab", 0, 2); + TEST_SEARCH ("a", "ab", 1, -2); + TEST_SEARCH_2 ("a", "a", "b", 0, 2, 2); + TEST_SEARCH_2 ("b", "a", "b", 0, 2, 2); + TEST_SEARCH_2 ("a", "a", "b", 1, -2, 2); + + test_match ("\n", "\n"); + test_match ("a\n", "a\n"); + test_match ("\nb", "\nb"); + test_match ("a\nb", "a\nb"); + + TEST_SEARCH ("b", "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 236, -237); + /* Valid use of special characters. */ + test_match ("a*", "aa"); + test_fastmap ("a*", "a", 0, 0); + TEST_REGISTERS ("a*", "aa", 0, 2, -1, -1, -1, -1); + + test_match ("a*b", "aab"); + test_fastmap ("a*b", "ab", 0, 0); + + test_match ("a*ab", "aab"); + TEST_REGISTERS ("a*a", "aa", 0, 2, -1, -1, -1, -1); + TEST_REGISTERS ("a*a", "xaax", 1, 3, -1, -1, -1, -1); + + test_match ("\\{", "{"); + test_match ("\\^", "^"); + test_match ("\\.", "."); + test_match ("\\*", "*"); + test_match ("\\[", "["); + test_match ("\\$", "$"); + test_match ("\\\\", "\\"); + + test_match ("ab*", "a"); + test_match ("ab*", "abb"); + + /* Valid consecutive repetitions. */ + test_match ("a**", "a"); + /* Valid period. */ + test_match (".", "a"); + TEST_REGISTERS (".", "a", 0, 1, -1, -1, -1, -1); + test_match (".", "\004"); + test_match (".", "\n"); + /* Valid bracket expressions. */ + test_match ("[ab]", "a"); + test_match ("[ab]", "b"); + test_fastmap ("[ab]", "ab", 0, 0); + TEST_REGISTERS ("[ab]", "a", 0, 1, -1, -1, -1, -1); + TEST_REGISTERS ("[ab]", "xax", 1, 2, -1, -1, -1, -1); + + test_fastmap ("[^ab]", "ab", 1, 1); + test_match ("[^ab]", "c"); + test_match ("[^a]", "\n"); + + test_match ("[a]*a", "aa"); + + test_match ("[[]", "["); + test_match ("[]]", "]"); + test_match ("[.]", "."); + test_match ("[*]", "*"); + test_match ("[\\]", "\\"); + test_match ("[\\(]", "("); + test_match ("[\\)]", ")"); + test_match ("[^]]", "a"); + test_match ("[a^]", "^"); + test_match ("[a$]", "$"); + test_match ("[]a]", "]"); + test_match ("[a][]]", "a]"); + test_match ("[\n]", "\n"); + test_match ("[^a]", "\n"); + test_match ("[a-]", "a"); + + TEST_REGISTERS ("\\`[ \t\n]*", " karl (Karl Berry)", 0, 1, -1, -1, -1, -1); + TEST_REGISTERS ("[ \t\n]*\\'", " karl (Karl Berry)", 18, 18, -1, -1, -1, -1); + + /* Collating, noncollating, + equivalence classes aren't + implemented yet. */ + + + /* Character classes. */ + test_match ("[:alpha:]", "p"); + test_match ("[[:alpha:]]", "a"); + test_match ("[[:alpha:]]", "z"); + test_match ("[[:alpha:]]", "A"); + test_match ("[[:alpha:]]", "Z"); + test_match ("[[:upper:]]", "A"); + test_match ("[[:upper:]]", "Z"); + test_match ("[[:lower:]]", "a"); + test_match ("[[:lower:]]", "z"); + + test_match ("[[:digit:]]", "0"); + test_match ("[[:digit:]]", "9"); + test_fastmap ("[[:digit:]]", "0123456789", 0, 0); + + test_match ("[[:alnum:]]", "0"); + test_match ("[[:alnum:]]", "9"); + test_match ("[[:alnum:]]", "a"); + test_match ("[[:alnum:]]", "z"); + test_match ("[[:alnum:]]", "A"); + test_match ("[[:alnum:]]", "Z"); + test_match ("[[:xdigit:]]", "0"); + test_match ("[[:xdigit:]]", "9"); + test_match ("[[:xdigit:]]", "A"); + test_match ("[[:xdigit:]]", "F"); + test_match ("[[:xdigit:]]", "a"); + test_match ("[[:xdigit:]]", "f"); + test_match ("[[:space:]]", " "); + test_match ("[[:print:]]", " "); + test_match ("[[:print:]]", "~"); + test_match ("[[:punct:]]", ","); + test_match ("[[:graph:]]", "!"); + test_match ("[[:graph:]]", "~"); + test_match ("[[:cntrl:]]", "\177"); + test_match ("[[:digit:]a]", "a"); + test_match ("[[:digit:]a]", "2"); + test_match ("[a[:digit:]]", "a"); + test_match ("[a[:digit:]]", "2"); + test_match ("[[:]", "["); + test_match ("[:]", ":"); + test_match ("[[:a]", "["); + test_match ("[[:alpha:a]", "["); + /* Valid ranges. */ + test_match ("[a-a]", "a"); + test_fastmap ("[a-a]", "a", 0, 0); + TEST_REGISTERS ("[a-a]", "xax", 1, 2, -1, -1, -1, -1); + + test_match ("[a-z]", "z"); + test_fastmap ("[a-z]", "abcdefghijklmnopqrstuvwxyz", 0, 0); + test_match ("[-a]", "-"); /* First */ + test_match ("[-a]", "a"); + test_match ("[a-]", "-"); /* Last */ + test_match ("[a-]", "a"); + test_match ("[--@]", "@"); /* First and starting point. */ + + test_match ("[%--a]", "%"); /* Ending point. */ + test_match ("[%--a]", "-"); /* Ditto. */ + + test_match ("[a%--]", "%"); /* Both ending point and last. */ + test_match ("[a%--]", "-"); + test_match ("[%--a]", "a"); /* Ending point only. */ + test_match ("[a-c-f]", "e"); /* Piggyback. */ + + test_match ("[)-+--/]", "*"); + test_match ("[)-+--/]", ","); + test_match ("[)-+--/]", "/"); + test_match ("[[:digit:]-]", "-"); + /* Concatenation ????*/ + test_match ("[ab][cd]", "ac"); + test_fastmap ("[ab][cd]", "ab", 0, 0); + TEST_REGISTERS ("[ab][cd]", "ad", 0, 2, -1, -1, -1, -1); + TEST_REGISTERS ("[ab][cd]", "xadx", 1, 3, -1, -1, -1, -1); + + /* Valid expression anchoring. */ + test_match ("^a", "a"); + test_fastmap ("^a", "a", 0, 0); + TEST_REGISTERS ("^a", "ax", 0, 1, -1, -1, -1, -1); + + test_match ("^", ""); + TEST_REGISTERS ("^", "", 0, 0, -1, -1, -1, -1); + test_match ("$", ""); + TEST_REGISTERS ("$", "", 0, 0, -1, -1, -1, -1); + + test_match ("a$", "a"); + test_fastmap ("a$", "a", 0, 0); + TEST_REGISTERS ("a$", "xa", 1, 2, -1, -1, -1, -1); + + test_match ("^ab$", "ab"); + test_fastmap ("^ab$", "a", 0, 0); + TEST_REGISTERS ("^a$", "a", 0, 1, -1, -1, -1, -1); + + test_fastmap ("^$", "", 0, 0); + test_match ("^$", ""); + TEST_REGISTERS ("^$", "", 0, 0, -1, -1, -1, -1); + + TEST_SEARCH (PARENS_TO_OPS ("(^a)"), "ab", 0, 2); + TEST_SEARCH (PARENS_TO_OPS ("(a$)"), "ba", 0, 2); + TEST_SEARCH (PARENS_TO_OPS ("^(^a)"), "ab", 0, 2); + TEST_SEARCH (PARENS_TO_OPS ("(a$)$"), "ba", 0, 2); + + /* Two strings. */ + test_match_2 ("ab", "a", "b"); + TEST_REGISTERS_2 ("ab", "a", "b", 0, 2, -1, -1, -1, -1); + + test_match_2 ("a", "", "a"); + test_match_2 ("a", "a", ""); + test_match_2 ("ab", "a", "b"); + /* (start)pos. */ + TEST_POSITIONED_MATCH ("b", "ab", 1); + /* mstop. */ + TEST_TRUNCATED_MATCH ("a", "ab", 1); + + + /* Both basic and extended, continued; should not match. */ + + test_should_match = false; + /* Ordinary characters. */ + test_match ("abc", "ab"); + + TEST_SEARCH ("c", "ab", 0, 2); + TEST_SEARCH ("c", "ab", 0, 2); + TEST_SEARCH ("c", "ab", 1, -2); + TEST_SEARCH ("c", "ab", 0, 10); + TEST_SEARCH ("c", "ab", 1, -10); + TEST_SEARCH_2 ("c", "a", "b", 0, 2, 2); + TEST_SEARCH_2 ("c", "a", "b", 0, 2, 2); + TEST_SEARCH_2 ("c", "a", "b", 0, 2, 2); + TEST_SEARCH_2 ("c", "a", "b", 1, -2, 2); + TEST_SEARCH_2 ("c", "a", "b", 1, -2, 2); + + TEST_SEARCH ("c", "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 236, -237); + + /* Invalid use of special characters. */ + invalid_pattern (REG_EESCAPE, "\\"); + invalid_pattern (REG_EESCAPE, "a\\"); + invalid_pattern (REG_EESCAPE, "a*\\"); + /* Invalid period. */ + test_match (".", ""); + /* Invalid bracket expressions. */ + test_match ("[ab]", "c"); + test_match ("[^b]", "b"); + test_match ("[^]]", "]"); + + invalid_pattern (REG_EBRACK, "["); + invalid_pattern (REG_EBRACK, "[^"); + invalid_pattern (REG_EBRACK, "[a"); + invalid_pattern (REG_EBRACK, "[]"); + invalid_pattern (REG_EBRACK, "[]a"); + invalid_pattern (REG_EBRACK, "a[]a"); + + + test_match ("[:alpha:]", "q"); /* Character classes. */ + test_match ("[[:alpha:]]", "2"); + test_match ("[[:upper:]]", "a"); + test_match ("[[:lower:]]", "A"); + test_match ("[[:digit:]]", "a"); + test_match ("[[:alnum:]]", ":"); + test_match ("[[:xdigit:]]", "g"); + test_match ("[[:space:]]", "a"); + test_match ("[[:print:]]", "\177"); + test_match ("[[:punct:]]", "a"); + test_match ("[[:graph:]]", " "); + test_match ("[[:cntrl:]]", "a"); + invalid_pattern (REG_EBRACK, "[[:"); + invalid_pattern (REG_EBRACK, "[[:alpha:"); + invalid_pattern (REG_EBRACK, "[[:alpha:]"); + invalid_pattern (REG_ECTYPE, "[[::]]"); + invalid_pattern (REG_ECTYPE, "[[:a:]]"); + invalid_pattern (REG_ECTYPE, "[[:alpo:]]"); + invalid_pattern (REG_ECTYPE, "[[:a:]"); + + test_match ("[a-z]", "2"); /* Invalid ranges. */ + test_match ("[^-a]", "-"); + test_match ("[^a-]", "-"); + test_match ("[)-+--/]", "."); + invalid_pattern (REG_ERANGE, "[z-a]"); /* Empty */ + invalid_pattern (REG_ERANGE, "[a--]"); /* Empty */ + invalid_pattern (REG_ERANGE, "[[:digit:]-9]"); + invalid_pattern (REG_ERANGE, "[a-[:alpha:]]"); + invalid_pattern (REG_ERANGE, "[a-"); + invalid_pattern (REG_EBRACK, "[a-z"); + + test_match ("[ab][cd]", "ae"); /* Concatenation. */ + test_match ("b*c", "b"); /* Star. */ + + /* Invalid anchoring. */ + test_match ("^", "a"); + test_match ("^a", "ba"); + test_match ("$", "b"); + test_match ("a$", "ab"); + test_match ("^$", "a"); + test_match ("^ab$", "a"); + + TEST_SEARCH ("^a", "b\na", 0, 3); + TEST_SEARCH ("b$", "b\na", 0, 3); + + test_match_2 ("^a", "\n", "a"); + test_match_2 ("a$", "a", "\n"); + + TEST_SEARCH (PARENS_TO_OPS ("(^a)"), "ba", 0, 2); + TEST_SEARCH (PARENS_TO_OPS ("(a$)"), "ab", 0, 2); + TEST_SEARCH (PARENS_TO_OPS ("^(^a)"), "ba", 0, 2); + TEST_SEARCH (PARENS_TO_OPS ("(a$)$"), "ab", 0, 2); + + printf ("\nFinished generic POSIX tests.\n"); +} + + + +/* +Local variables: +make-backup-files: t +version-control: t +trim-versions-without-asking: nil +End: +*/ diff --git a/gnu/lib/libregex/test/psx-group.c b/gnu/lib/libregex/test/psx-group.c new file mode 100644 index 0000000000..08ae8a28d3 --- /dev/null +++ b/gnu/lib/libregex/test/psx-group.c @@ -0,0 +1,440 @@ +/* psx-group.c: test POSIX grouping, both basic and extended. */ + +#include "test.h" + + +void +test_grouping () +{ + printf ("\nStarting POSIX grouping tests.\n"); + + test_should_match = true; + + test_fastmap (PARENS_TO_OPS ("(a)"), "a", 0, 0); + test_match (PARENS_TO_OPS ("(a)"), "a"); + TEST_REGISTERS (PARENS_TO_OPS ("(a)"), "a", 0, 1, 0, 1, -1, -1); + TEST_REGISTERS (PARENS_TO_OPS ("(a)"), "xax", 1, 2, 1, 2, -1, -1); + + test_match (PARENS_TO_OPS ("((a))"), "a"); + test_fastmap (PARENS_TO_OPS ("((a))"), "a", 0, 0); + TEST_REGISTERS (PARENS_TO_OPS ("((a))"), "a", 0, 1, 0, 1, 0, 1); + TEST_REGISTERS (PARENS_TO_OPS ("((a))"), "xax", 1, 2, 1, 2, 1, 2); + + test_fastmap (PARENS_TO_OPS ("(a)(b)"), "a", 0, 0); + test_match (PARENS_TO_OPS ("(a)(b)"), "ab"); + TEST_REGISTERS (PARENS_TO_OPS ("(a)(b)"), "ab", 0, 2, 0, 1, 1, 2); + + TEST_REGISTERS (PARENS_TO_OPS ("(a)(b)"), "xabx", 1, 3, 1, 2, 2, 3); + + test_all_registers (PARENS_TO_OPS ("((a)(b))"), "ab", "", 0, 2, 0, 2, 0, 1, + 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); + + + /* Test that we simply ignore groups past the 255th. */ + test_match (PARENS_TO_OPS ("((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((a))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))"), "a"); + + + /* Per POSIX D11.1, p. 125. */ + + test_fastmap (PARENS_TO_OPS ("(a)*"), "a", 0, 0); + test_match (PARENS_TO_OPS ("(a)*"), ""); + TEST_REGISTERS (PARENS_TO_OPS ("(a)*"), "", 0, 0, -1, -1, -1, -1); + TEST_REGISTERS (PARENS_TO_OPS ("(a)*"), "aa", 0, 2, 1, 2, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(a*)"), "a", 0, 0); + test_match (PARENS_TO_OPS ("(a*)"), ""); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)"), "", 0, 0, 0, 0, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(a*)"), "a", 0, 0); + test_match (PARENS_TO_OPS ("(a*)"), "a"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)"), "a", 0, 1, 0, 1, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(a*)b"), "ab", 0, 0); + test_match (PARENS_TO_OPS ("(a*)b"), "b"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)b"), "b", 0, 1, 0, 0, -1, -1); + + test_match (PARENS_TO_OPS ("(a*)b"), "ab"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)b"), "ab", 0, 2, 0, 1, -1, -1); + + test_fastmap (PARENS_TO_OPS ("((a*)b)*"), "ab", 0, 0); + test_match (PARENS_TO_OPS ("((a*)b)*"), ""); + TEST_REGISTERS (PARENS_TO_OPS ("((a*)b)*"), "", 0, 0, -1, -1, -1, -1); + + test_match (PARENS_TO_OPS ("((a*)b)*"), "ab"); + TEST_REGISTERS (PARENS_TO_OPS ("((a*)b)*"), "ab", 0, 2, 0, 2, 0, 1); + + test_match (PARENS_TO_OPS ("((a*)b)*"), "abb"); + TEST_REGISTERS (PARENS_TO_OPS ("((a*)b)*"), "abb", 0, 3, 2, 3, 2, 2); + + test_match (PARENS_TO_OPS ("((a*)b)*"), "aabab"); + TEST_REGISTERS (PARENS_TO_OPS ("((a*)b)*"), "aabab", 0, 5, 3, 5, 3, 4); + + test_match (PARENS_TO_OPS ("((a*)b)*"), "abbab"); + TEST_REGISTERS (PARENS_TO_OPS ("((a*)b)*"), "abbab", 0, 5, 3, 5, 3, 4); + + TEST_REGISTERS (PARENS_TO_OPS ("((a*)b)*"), "xabbabx", 0, 0, -1, -1, -1, -1); + + test_match (PARENS_TO_OPS ("((a*)b)*"), "abaabaaaab"); + TEST_REGISTERS (PARENS_TO_OPS ("((a*)b)*"), "abaabaaab", 0, 9, 5, 9, 5, 8); + + test_fastmap (PARENS_TO_OPS ("(ab)*"), "a", 0, 0); + test_match (PARENS_TO_OPS ("(ab)*"), ""); + TEST_REGISTERS (PARENS_TO_OPS ("(ab)*"), "", 0, 0, -1, -1, -1, -1); + + test_match (PARENS_TO_OPS ("(ab)*"), "abab"); + TEST_REGISTERS (PARENS_TO_OPS ("(ab)*"), "abab", 0, 4, 2, 4, -1, -1); + + /* We match the empty string here. */ + TEST_REGISTERS (PARENS_TO_OPS ("(ab)*"), "xababx", 0, 0, -1, -1, -1, -1); + + /* Per David A. Willcox. */ + TEST_REGISTERS (PARENS_TO_OPS ("a(b*)c"), "ac", 0, 2, 1, 1, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(a)*b"), "ab", 0, 0); + test_match (PARENS_TO_OPS ("(a)*b"), "b"); + TEST_REGISTERS (PARENS_TO_OPS ("(a)*b"), "b", 0, 1, -1, -1, -1, -1); + + test_match (PARENS_TO_OPS ("(a)*b"), "ab"); + TEST_REGISTERS (PARENS_TO_OPS ("(a)*b"), "ab", 0, 2, 0, 1, -1, -1); + + test_match_2 (PARENS_TO_OPS ("(a)*b"), "a", "ab"); + TEST_REGISTERS_2 (PARENS_TO_OPS ("(a)*b"), "a", "ab", 0, 3, 1, 2, -1, -1); + + test_match (PARENS_TO_OPS ("(a)*b"), "aab"); + TEST_REGISTERS (PARENS_TO_OPS ("(a)*b"), "aab", 0, 3, 1, 2, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(a)*a"), "a", 0, 0); + test_match (PARENS_TO_OPS ("(a)*a"), "a"); + TEST_REGISTERS (PARENS_TO_OPS ("(a)*a"), "a", 0, 1, -1, -1, -1, -1); + + TEST_REGISTERS (PARENS_TO_OPS ("(a*)*"), "", 0, 0, 0, 0, 0, 0); + + test_match (PARENS_TO_OPS ("((a*))*"), ""); + TEST_REGISTERS (PARENS_TO_OPS ("((a*))*"), "", 0, 0, 0, 0, 0, 0); + test_match (PARENS_TO_OPS ("((a*))*"), "aa"); + + test_fastmap (PARENS_TO_OPS ("(a*)*b"), "ab", 0, 0); + test_match (PARENS_TO_OPS ("(a*)*b"), "b"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)*b"), "b", 0, 1, 0, 0, -1, -1); + + TEST_REGISTERS (PARENS_TO_OPS ("(a*)*b"), "xbx", 1, 2, 1, 1, -1, -1); + + test_match (PARENS_TO_OPS ("(a*)*b"), "ab"); /* Per rms. */ + TEST_REGISTERS (PARENS_TO_OPS ("(a*)*b"), "ab", 0, 2, 0, 1, -1, -1); + + TEST_REGISTERS (PARENS_TO_OPS ("(a*)*b"), "xabx", 1, 3, 1, 2, -1, -1); + + /* Test register restores. */ + test_match (PARENS_TO_OPS ("(a*)*b"), "aab"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)*b"), "aab", 0, 3, 0, 2, -1, -1); + + TEST_REGISTERS_2 (PARENS_TO_OPS ("(a*)*b"), "a", "ab", 0, 3, 0, 2, -1, -1); + + /* We are matching the empty string, with backtracking. */ + test_fastmap (PARENS_TO_OPS ("(a*)a"), "a", 0, 0); + test_match (PARENS_TO_OPS ("(a*)a"), "a"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)a"), "a", 0, 1, 0, 0, -1, -1); + + test_match (PARENS_TO_OPS ("(a*)a"), "aa"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)a"), "aa", 0, 2, 0, 1, -1, -1); + + /* We are matching the empty string, with backtracking. */ +/*fails test_match (PARENS_TO_OPS ("(a*)*a"), "a"); */ + test_match (PARENS_TO_OPS ("(a*)*a"), "aa"); + /* Match the empty string. */ + TEST_REGISTERS (PARENS_TO_OPS ("(a*)*a"), "a", 0, 1, 0, 0, -1, -1); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)*a"), "xax", 1, 2, 1, 1, -1, -1); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)*a"), "aa", 0, 2, 0, 1, -1, -1); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)*a"), "xaax", 1, 3, 1, 2, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(a)*ab"), "a", 0 , 0); + test_match (PARENS_TO_OPS ("(a)*ab"), "ab"); + TEST_REGISTERS (PARENS_TO_OPS ("(a)*ab"), "ab", 0, 2, -1, -1, -1, -1); + + test_match (PARENS_TO_OPS ("(a)*ab"), "aab"); + TEST_REGISTERS (PARENS_TO_OPS ("(a)*ab"), "aab", 0, 3, 0, 1, -1, -1); + + TEST_REGISTERS (PARENS_TO_OPS("(a)*ab"), "xaabx", 1, 4, 1, 2, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(a*)ab"), "a", 0 , 0); + test_match (PARENS_TO_OPS ("(a*)ab"), "ab"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)ab"), "ab", 0, 2, 0, 0, -1, -1); + + test_match (PARENS_TO_OPS ("(a*)ab"), "aab"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)ab"), "aab", 0, 3, 0, 1, -1, -1); + + TEST_REGISTERS (PARENS_TO_OPS ("(a*)ab"), "xaabx", 1, 4, 1, 2, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(a*)*ab"), "a", 0 , 0); + test_match (PARENS_TO_OPS ("(a*)*ab"), "ab"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)*ab"), "ab", 0, 2, 0, 0, -1, -1); + + test_match (PARENS_TO_OPS ("(a*)*ab"), "aab"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)*ab"), "aab", 0, 3, 0, 1, -1, -1); + + TEST_REGISTERS (PARENS_TO_OPS("(a*)*ab"), "xaabx", 1, 4, 1, 2, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(a*)*b*c"), "abc", 0, 0); + test_match (PARENS_TO_OPS ("(a*)*b*c"), "c"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)*b*c"), "c", 0, 1, 0, 0, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(a)*(ab)*"), "a", 0, 0); + test_match (PARENS_TO_OPS ("(a)*(ab)*"), "ab"); + /* Register 1 doesn't match at all (vs. matching the empty string) + because of backtracking, hence -1's. */ + TEST_REGISTERS (PARENS_TO_OPS ("(a)*(ab)*"), "ab", 0, 2, -1, -1, 0, 2); + + test_match (PARENS_TO_OPS ("(a*)*(ab)*"), "ab"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)*(ab)*"), "ab", 0, 2, 0, 0, 0, 2); + + test_fastmap (PARENS_TO_OPS ("(a*b)*"), "ab", 0, 0); + test_match (PARENS_TO_OPS ("(a*b)*"), ""); + TEST_REGISTERS (PARENS_TO_OPS ("(a*b)*"), "", 0, 0, -1, -1, -1, -1); + + test_match (PARENS_TO_OPS ("(a*b)*"), "b"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*b)*"), "b", 0, 1, 0, 1, -1, -1); + + test_match (PARENS_TO_OPS ("(a*b)*"), "baab"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*b)*"), "baab", 0, 4, 1, 4, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(a*b*)*"), "ab", 0, 0); + test_match (PARENS_TO_OPS ("(a*b*)*"), ""); + TEST_REGISTERS (PARENS_TO_OPS ("(a*b*)*"), "", 0, 0, 0, 0, -1, -1); + + test_match (PARENS_TO_OPS ("(a*b*)*"), "a"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*b*)*"), "a", 0, 1, 0, 1, -1, -1); + + test_match (PARENS_TO_OPS ("(a*b*)*"), "ba"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*b*)*"), "ba", 0, 2, 1, 2, -1, -1); + + test_match (PARENS_TO_OPS ("(a*b*)*"), "ab"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*b*)*"), "ab", 0, 2, 0, 2, -1, -1); + + test_match (PARENS_TO_OPS ("(a*b*)*"), "aa"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*b*)*"), "aa", 0, 2, 0, 2, -1, -1); + + test_match (PARENS_TO_OPS ("(a*b*)*"), "bb"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*b*)*"), "bb", 0, 2, 0, 2, -1, -1); + + test_match (PARENS_TO_OPS ("(a*b*)*"), "aba"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*b*)*"), "aba", 0, 3, 2, 3, -1, -1); + + test_match (PARENS_TO_OPS ("(a*b*)b"), "b"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*b*)b"), "b", 0, 1, 0, 0, -1, -1); + + test_fastmap (PARENS_TO_OPS ("((a*)*(b*)*)*"), "ab", 0, 0); + test_match (PARENS_TO_OPS ("((a*)*(b*)*)*"), ""); + test_all_registers (PARENS_TO_OPS ("((a*)*(b*)*)*"), "", "", 0, 0, 0, 0, + 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); + + test_match (PARENS_TO_OPS ("((a*)*(b*)*)*"), "aba"); + /* Perhaps register 3 should be 3/3 here? Not sure if standard + specifies this. xx*/ + test_all_registers (PARENS_TO_OPS ("((a*)*(b*)*)*"), "aba", "", 0, 3, 2, 3, + 2, 3, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); + + test_fastmap (PARENS_TO_OPS ("((a*)(b*))*"), "ab", 0, 0); + test_match (PARENS_TO_OPS ("((a*)(b*))*"), ""); + + test_all_registers (PARENS_TO_OPS ("((a*)(b*))*"), "", "", 0, 0, 0, 0, + 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); + + test_match (PARENS_TO_OPS ("(c(c(a)*(b)*)*)*"), ""); + + test_match (PARENS_TO_OPS ("((a*)(b*))*"), "aba"); + test_all_registers (PARENS_TO_OPS ("((a*)(b*))*"), "aba", "", 0, 3, 2, 3, + 2, 3, 3, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); + + test_fastmap (PARENS_TO_OPS ("((a)*(b)*)*"), "ab", 0, 0); + test_match (PARENS_TO_OPS ("((a)*(b)*)*"), ""); + test_all_registers (PARENS_TO_OPS ("((a)*(b)*)*"), "", "", 0, 0, 0, 0, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); + + test_match (PARENS_TO_OPS ("((a)*(b)*)*"), "aba"); + + test_all_registers (PARENS_TO_OPS ("((a)*(b)*)*"), "aba", "", 0, 3, 2, 3, + 2, 3, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(c(a)*(b)*)*"), "c", 0, 0); + test_match (PARENS_TO_OPS ("(c(a)*(b)*)*"), ""); + test_all_registers (PARENS_TO_OPS ("(c(a)*(b)*)*"), "", "", 0, 0, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); + + test_match (PARENS_TO_OPS ("(c(a)*(b)*)*"), "c"); + test_all_registers (PARENS_TO_OPS ("(c(a)*(b)*)*"), "c", "", 0, 1, 0, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); + + test_fastmap (PARENS_TO_OPS ("c((a)*(b)*)*"), "c", 0, 0); + test_match (PARENS_TO_OPS ("c((a)*(b)*)*"), "c"); + test_all_registers (PARENS_TO_OPS ("c((a)*(b)*)*"), "c", "", 0, 1, 1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(((a)*(b)*)*)*"), "ab", 0, 0); + test_match (PARENS_TO_OPS ("(((a)*(b)*)*)*"), ""); + test_all_registers (PARENS_TO_OPS ("(((a)*(b)*)*)*"), "", "", 0, 0, 0, 0, + 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); + + test_match (PARENS_TO_OPS ("(c(c(a)*(b)*)*)*"), ""); + test_fastmap (PARENS_TO_OPS ("(c(c(a)*(b)*)*)*"), "c", 0, 0); + + test_all_registers (PARENS_TO_OPS ("(c(c(a)*(b)*)*)*"), "", "", 0, 0, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); + + test_fastmap (PARENS_TO_OPS ("((a)*b)*"), "ab", 0, 0); + test_match (PARENS_TO_OPS ("((a)*b)*"), ""); + + test_match (PARENS_TO_OPS ("((a)*b)*"), ""); + TEST_REGISTERS (PARENS_TO_OPS ("((a)*b)*"), "", 0, 0, -1, -1, -1, -1); + + test_match (PARENS_TO_OPS ("((a)*b)*"), "abb"); + TEST_REGISTERS (PARENS_TO_OPS ("((a)*b)*"), "abb", 0, 3, 2, 3, 0, 1); /*zz*/ + + test_match (PARENS_TO_OPS ("((a)*b)*"), "abbab"); + TEST_REGISTERS (PARENS_TO_OPS ("((a)*b)*"), "abbab", 0, 5, 3, 5, 3, 4); + + /* We match the empty string here. */ + TEST_REGISTERS (PARENS_TO_OPS ("((a)*b)*"), "xabbabx", 0, 0, -1, -1, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(a*)*"), "a", 0, 0); + test_match (PARENS_TO_OPS ("(a*)*"), ""); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)*"), "", 0, 0, 0, 0, -1, -1); + + test_match (PARENS_TO_OPS ("(a*)*"), "aa"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*)*"), "aa", 0, 2, 0, 2, -1, -1); + + test_fastmap (PARENS_TO_OPS ("((a*)*)*"), "a", 0, 0); + test_match (PARENS_TO_OPS ("((a*)*)*"), ""); + TEST_REGISTERS (PARENS_TO_OPS ("((a*)*)*"), "", 0, 0, 0, 0, 0, 0); + + test_match (PARENS_TO_OPS ("((a*)*)*"), "a"); + TEST_REGISTERS (PARENS_TO_OPS ("((a*)*)*"), "a", 0, 1, 0, 1, 0, 1); + + test_fastmap (PARENS_TO_OPS ("(ab*)*"), "a", 0, 0); + test_match (PARENS_TO_OPS ("(ab*)*"), ""); + TEST_REGISTERS (PARENS_TO_OPS ("(ab*)*"), "", 0, 0, -1, -1, -1, -1); + + test_match (PARENS_TO_OPS ("(ab*)*"), "aa"); + TEST_REGISTERS (PARENS_TO_OPS ("(ab*)*"), "aa", 0, 2, 1, 2, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(ab*)*c"), "ac", 0, 0); + test_match (PARENS_TO_OPS ("(ab*)*c"), "c"); + TEST_REGISTERS (PARENS_TO_OPS ("(ab*)*c"), "c", 0, 1, -1, -1, -1, -1); + + test_match (PARENS_TO_OPS ("(ab*)*c"), "abbac"); + TEST_REGISTERS (PARENS_TO_OPS ("(ab*)*c"), "abbac", 0, 5, 3, 4, -1, -1); + + test_match (PARENS_TO_OPS ("(ab*)*c"), "abac"); + TEST_REGISTERS (PARENS_TO_OPS ("(ab*)*c"), "abac", 0, 4, 2, 3, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(a*b)*c"), "abc", 0, 0); + test_match (PARENS_TO_OPS ("(a*b)*c"), "c"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*b)*c"), "c", 0, 1, -1, -1, -1, -1); + + test_match (PARENS_TO_OPS ("(a*b)*c"), "bbc"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*b)*c"), "bbc", 0, 3, 1, 2, -1, -1); + + test_match (PARENS_TO_OPS ("(a*b)*c"), "aababc"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*b)*c"), "aababc", 0, 6, 3, 5, -1, -1); + + test_match (PARENS_TO_OPS ("(a*b)*c"), "aabaabc"); + TEST_REGISTERS (PARENS_TO_OPS ("(a*b)*c"), "aabaabc", 0, 7, 3, 6, -1, -1); + + test_fastmap (PARENS_TO_OPS ("((a*)b*)"), "ab", 0, 0); + test_match (PARENS_TO_OPS ("((a*)b*)"), ""); + TEST_REGISTERS (PARENS_TO_OPS ("((a*)b*)"), "", 0, 0, 0, 0, 0, 0); + + test_match (PARENS_TO_OPS ("((a*)b*)"), "a"); + TEST_REGISTERS (PARENS_TO_OPS ("((a*)b*)"), "a", 0, 1, 0, 1, 0, 1); + + test_match (PARENS_TO_OPS ("((a*)b*)"), "b"); + TEST_REGISTERS (PARENS_TO_OPS ("((a*)b*)"), "b", 0, 1, 0, 1, 0, 0); + + test_fastmap (PARENS_TO_OPS ("((a)*b*)"), "ab", 0, 0); + test_match (PARENS_TO_OPS ("((a)*b*)"), ""); + TEST_REGISTERS (PARENS_TO_OPS ("((a)*b*)"), "", 0, 0, 0, 0, -1, -1); + + test_match (PARENS_TO_OPS ("((a)*b*)"), "a"); + TEST_REGISTERS (PARENS_TO_OPS ("((a)*b*)"), "a", 0, 1, 0, 1, 0, 1); + + test_match (PARENS_TO_OPS ("((a)*b*)"), "b"); + TEST_REGISTERS (PARENS_TO_OPS ("((a)*b*)"), "b", 0, 1, 0, 1, -1, -1); + + test_match (PARENS_TO_OPS ("((a)*b*)"), "ab"); + TEST_REGISTERS (PARENS_TO_OPS ("((a)*b*)"), "ab", 0, 2, 0, 2, 0, 1); + + test_fastmap (PARENS_TO_OPS ("((a*)b*)c"), "abc", 0, 0); + test_match (PARENS_TO_OPS ("((a*)b*)c"), "c"); + TEST_REGISTERS (PARENS_TO_OPS ("((a*)b*)c"), "c", 0, 1, 0, 0, 0, 0); + + test_fastmap (PARENS_TO_OPS ("((a)*b*)c"), "abc", 0, 0); + test_match (PARENS_TO_OPS ("((a)*b*)c"), "c"); + TEST_REGISTERS (PARENS_TO_OPS ("((a)*b*)c"), "c", 0, 1, 0, 0, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(a*b*)*"), "ab", 0, 0); + test_match (PARENS_TO_OPS ("(a*b*)*"), ""); + TEST_REGISTERS (PARENS_TO_OPS ("(a*b*)*"), "", 0, 0, 0, 0, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(((a*))((b*)))*"), "ab", 0, 0); + test_match (PARENS_TO_OPS ("(((a*))((b*)))*"), ""); + test_all_registers (PARENS_TO_OPS ("(((a*))((b*)))*"), "", "", 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1); + + test_fastmap (PARENS_TO_OPS ("(c*((a*))d*((b*))e*)*"), "abcde", 0, 0); + test_match (PARENS_TO_OPS ("(c*((a*))d*((b*))e*)*"), ""); + test_all_registers (PARENS_TO_OPS ("(c*((a*))d*((b*))e*)*"), "", "", 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1); + + test_fastmap (PARENS_TO_OPS ("((a)*b)*c"), "abc", 0, 0); + test_match (PARENS_TO_OPS ("((a)*b)*c"), "c"); + TEST_REGISTERS (PARENS_TO_OPS ("((a)*b)*c"), "c", 0, 1, -1, -1, -1, -1); + + test_match (PARENS_TO_OPS ("(ab)*"), ""); + test_match (PARENS_TO_OPS ("((ab)*)"), ""); + test_match (PARENS_TO_OPS ("(((ab)*))"), ""); + test_match (PARENS_TO_OPS ("((((ab)*)))"), ""); + test_match (PARENS_TO_OPS ("(((((ab)*))))"), ""); + test_match (PARENS_TO_OPS ("((((((ab)*)))))"), ""); + test_match (PARENS_TO_OPS ("(((((((ab)*))))))"), ""); + test_match (PARENS_TO_OPS ("((((((((ab)*)))))))"), ""); + test_match (PARENS_TO_OPS ("(((((((((ab)*))))))))"), ""); + + + test_fastmap (PARENS_TO_OPS ("(((((((((ab)*))))))))"), "a", 0, 0); + test_match (PARENS_TO_OPS ("((((((((((ab)*)))))))))"), ""); + test_match (PARENS_TO_OPS ("(((((((((ab)*))))))))"), ""); + test_all_registers (PARENS_TO_OPS ("(((((((((ab)*))))))))"), "", NULL, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1); + + test_match (PARENS_TO_OPS ("(((((((((ab)*))))))))"), "abab"); + test_all_registers (PARENS_TO_OPS ("(((((((((ab)*))))))))"), "abab", NULL, + 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 2, 4); + + + test_should_match = false; + + invalid_pattern (REG_EPAREN, PARENS_TO_OPS ("(a")); + + test_match (PARENS_TO_OPS ("(a)"), ""); + test_match (PARENS_TO_OPS ("((a))"), "b"); + test_match (PARENS_TO_OPS ("(a)(b)"), "ac"); + test_match (PARENS_TO_OPS ("(ab)*"), "acab"); + test_match (PARENS_TO_OPS ("(a*)*b"), "c"); + test_match (PARENS_TO_OPS ("(a*b)*"), "baa"); + test_match (PARENS_TO_OPS ("(a*b)*"), "baabc"); + test_match (PARENS_TO_OPS ("(a*b*)*"), "c"); + test_match (PARENS_TO_OPS ("((a*)*(b*)*)*"), "c"); + test_match (PARENS_TO_OPS ("(a*)*"), "ab"); + test_match (PARENS_TO_OPS ("((a*)*)*"), "ab"); + test_match (PARENS_TO_OPS ("((a*)*)*"), "b"); + test_match (PARENS_TO_OPS ("(ab*)*"), "abc"); + test_match (PARENS_TO_OPS ("(ab*)*c"), "abbad"); + test_match (PARENS_TO_OPS ("(a*c)*b"), "aacaacd"); + test_match (PARENS_TO_OPS ("(a*)"), "b"); + test_match (PARENS_TO_OPS ("((a*)b*)"), "c"); + + /* Expression anchoring. */ + TEST_SEARCH (PARENS_TO_OPS ("(^b)"), "ab", 0, 2); + TEST_SEARCH (PARENS_TO_OPS ("(a$)"), "ab", 0, 2); + + printf ("\nFinished POSIX grouping tests.\n"); +} diff --git a/gnu/lib/libregex/test/psx-interf.c b/gnu/lib/libregex/test/psx-interf.c new file mode 100644 index 0000000000..8312d5e7d7 --- /dev/null +++ b/gnu/lib/libregex/test/psx-interf.c @@ -0,0 +1,624 @@ +/* psx-interf.c: test POSIX interface. */ + +#include +#include + +#include "test.h" + +#define ERROR_CODE_LENGTH 20 +#define TEST_ERRBUF_SIZE 15 + + +void test_compile (); + + +/* ANSWER should be at least ERROR_CODE_LENGTH long. */ + +static char * +get_error_string (error_code, answer) + int error_code; + char answer[]; +{ + switch (error_code) + { + case 0: strcpy (answer, "No error"); break; + case REG_NOMATCH: strcpy (answer, "REG_NOMATCH"); break; + case REG_BADPAT: strcpy (answer, "REG_BADPAT"); break; + case REG_EPAREN: strcpy (answer, "REG_EPAREN"); break; + case REG_ESPACE: strcpy (answer, "REG_ESPACE"); break; + case REG_ECOLLATE: strcpy (answer, "REG_ECOLLATE"); break; + case REG_ECTYPE: strcpy (answer, "REG_ECTYPE"); break; + case REG_EESCAPE: strcpy (answer, "REG_EESCAPE"); break; + case REG_ESUBREG: strcpy (answer, "REG_ESUBREG"); break; + case REG_EBRACK: strcpy (answer, "REG_EBRACK"); break; + case REG_EBRACE: strcpy (answer, "REG_EBRACE"); break; + case REG_BADBR: strcpy (answer, "REG_BADBR"); break; + case REG_ERANGE: strcpy (answer, "REG_ERANGE"); break; + case REG_BADRPT: strcpy (answer, "REG_BADRPT"); break; + case REG_EEND: strcpy (answer, "REG_EEND"); break; + default: strcpy (answer, "Bad error code"); + } + return answer; +} + + +/* I don't think we actually need to initialize all these things. + --karl */ + +void +init_pattern_buffer (pattern_buffer_ptr) + regex_t *pattern_buffer_ptr; +{ + pattern_buffer_ptr->buffer = NULL; + pattern_buffer_ptr->allocated = 0; + pattern_buffer_ptr->used = 0; + pattern_buffer_ptr->fastmap = NULL; + pattern_buffer_ptr->fastmap_accurate = 0; + pattern_buffer_ptr->translate = NULL; + pattern_buffer_ptr->can_be_null = 0; + pattern_buffer_ptr->re_nsub = 0; + pattern_buffer_ptr->no_sub = 0; + pattern_buffer_ptr->not_bol = 0; + pattern_buffer_ptr->not_eol = 0; +} + + +void +test_compile (valid_pattern, error_code_expected, pattern, + pattern_buffer_ptr, cflags) + unsigned valid_pattern; + int error_code_expected; + const char *pattern; + regex_t *pattern_buffer_ptr; + int cflags; +{ + int error_code_returned; + boolean error = false; + char errbuf[TEST_ERRBUF_SIZE]; + + init_pattern_buffer (pattern_buffer_ptr); + error_code_returned = regcomp (pattern_buffer_ptr, pattern, cflags); + + if (valid_pattern && error_code_returned) + { + printf ("\nShould have been a valid pattern but wasn't.\n"); + regerror (error_code_returned, pattern_buffer_ptr, errbuf, + TEST_ERRBUF_SIZE); + printf ("%s", errbuf); + error = true; + } + + if (!valid_pattern && !error_code_returned) + { + printf ("\n\nInvalid pattern compiled as valid:\n"); + error = true; + } + + if (error_code_returned != error_code_expected) + { + char expected_error_string[ERROR_CODE_LENGTH]; + char returned_error_string[ERROR_CODE_LENGTH]; + + get_error_string (error_code_expected, expected_error_string), + get_error_string (error_code_returned, returned_error_string); + + printf (" Expected error code %s but got `%s'.\n", + expected_error_string, returned_error_string); + + error = true; + } + + if (error) + print_pattern_info (pattern, pattern_buffer_ptr); +} + + +static void +test_nsub (sub_count, pattern, cflags) + unsigned sub_count; + char *pattern; + int cflags; + +{ + regex_t pattern_buffer; + + test_compile (1, 0, pattern, &pattern_buffer, cflags); + + if (pattern_buffer.re_nsub != sub_count) + { + printf ("\nShould have counted %d subexpressions but counted %d \ +instead.\n", sub_count, pattern_buffer.re_nsub); + } + + regfree (&pattern_buffer); +} + + +static void +test_regcomp () +{ + regex_t pattern_buffer; + int cflags = 0; + + + printf ("\nStarting regcomp tests.\n"); + + cflags = 0; + test_compile (0, REG_ESUBREG, "\\(a\\)\\2", &pattern_buffer, cflags); + test_compile (0, REG_EBRACE, "a\\{", &pattern_buffer, cflags); + test_compile (0, REG_BADBR, "a\\{-1\\}", &pattern_buffer, cflags); + test_compile (0, REG_EBRACE, "a\\{", &pattern_buffer, cflags); + test_compile (0, REG_EBRACE, "a\\{1", &pattern_buffer, cflags); + + cflags = REG_EXTENDED; + test_compile (0, REG_ECTYPE, "[[:alpo:]]", &pattern_buffer, cflags); + test_compile (0, REG_EESCAPE, "\\", &pattern_buffer, cflags); + test_compile (0, REG_EBRACK, "[a", &pattern_buffer, cflags); + test_compile (0, REG_EPAREN, "(", &pattern_buffer, cflags); + test_compile (0, REG_ERANGE, "[z-a]", &pattern_buffer, cflags); + + test_nsub (1, "(a)", cflags); + test_nsub (2, "((a))", cflags); + test_nsub (2, "(a)(b)", cflags); + + cflags = REG_EXTENDED | REG_NOSUB; + test_nsub (1, "(a)", cflags); + + regfree (&pattern_buffer); + + printf ("\nFinished regcomp tests.\n"); +} + + +static void +fill_pmatch (pmatch, start0, end0, start1, end1, start2, end2) + regmatch_t pmatch[]; + regoff_t start0, end0, start1, end1, start2, end2; +{ + pmatch[0].rm_so = start0; + pmatch[0].rm_eo = end0; + pmatch[1].rm_so = start1; + pmatch[1].rm_eo = end1; + pmatch[2].rm_so = start2; + pmatch[2].rm_eo = end2; +} + + +static void +test_pmatch (pattern, string, nmatch, pmatch, correct_pmatch, cflags) + char *pattern; + char *string; + unsigned nmatch; + regmatch_t pmatch[]; + regmatch_t correct_pmatch[]; + int cflags; +{ + regex_t pattern_buffer; + unsigned this_match; + int error_code_returned; + boolean found_nonmatch = false; + + test_compile (1, 0, pattern, &pattern_buffer, cflags); + error_code_returned = regexec (&pattern_buffer, string, nmatch, pmatch, 0); + + if (error_code_returned == REG_NOMATCH) + printf ("Matching failed in test_pmatch.\n"); + else + { + for (this_match = 0; this_match < nmatch; this_match++) + { + if (pmatch[this_match].rm_so != correct_pmatch[this_match].rm_so) + { + if (found_nonmatch == false) + printf ("\n"); + + printf ("Pmatch start %d wrong: was %d when should have \ +been %d.\n", this_match, pmatch[this_match].rm_so, + correct_pmatch[this_match].rm_so); + found_nonmatch = true; + } + if (pmatch[this_match].rm_eo != correct_pmatch[this_match].rm_eo) + { + if (found_nonmatch == false) + printf ("\n"); + + printf ("Pmatch end %d wrong: was %d when should have been \ +%d.\n", this_match, pmatch[this_match].rm_eo, + correct_pmatch[this_match].rm_eo); + found_nonmatch = true; + } + } + + if (found_nonmatch) + { + printf (" The number of pmatches requested was: %d.\n", nmatch); + printf (" The string to match was: `%s'.\n", string); + print_pattern_info (pattern, &pattern_buffer); + } + } /* error_code_returned == REG_NOMATCH */ + + regfree (&pattern_buffer); +} + + +static void +test_eflags (must_match_bol, must_match_eol, pattern, string, cflags, eflags) + boolean must_match_bol; + boolean must_match_eol; + char *pattern; + char *string; + int cflags; + int eflags; +{ + regex_t pattern_buffer; + int error_code_returned; + boolean was_error = false; + + test_compile (1, 0, pattern, &pattern_buffer, cflags); + error_code_returned = regexec (&pattern_buffer, string, 0, 0, eflags); + + if (error_code_returned == REG_NOMATCH) + { + /* If wasn't true that both 1) the anchored part of the pattern + had to match this string and 2) this string was a proper + substring... */ + + if (!( (must_match_bol && (eflags & REG_NOTBOL)) + || (must_match_eol && (eflags & REG_NOTEOL)) )) + { + printf ("\nEflags test failed: didn't match when should have.\n"); + was_error = true; + } + } + else /* We got a match. */ + { + /* If wasn't true that either 1) the anchored part of the pattern + didn't have to match this string or 2) this string wasn't a + proper substring... */ + + if ((must_match_bol == (eflags & REG_NOTBOL)) + || (must_match_eol == (eflags & REG_NOTEOL))) + { + printf ("\nEflags test failed: matched when shouldn't have.\n"); + was_error = true; + } + } + + if (was_error) + { + printf (" The string to match was: `%s'.\n", string); + print_pattern_info (pattern, &pattern_buffer); + + if (eflags & REG_NOTBOL) + printf (" The eflag REG_BOL was set.\n"); + if (eflags & REG_NOTEOL) + printf (" The eflag REG_EOL was set.\n"); + } + + regfree (&pattern_buffer); +} + + +static void +test_ignore_case (should_match, pattern, string, cflags) + boolean should_match; + char *pattern; + char *string; + int cflags; +{ + regex_t pattern_buffer; + int error_code_returned; + + test_compile (1, 0, pattern, &pattern_buffer, cflags); + error_code_returned = regexec (&pattern_buffer, string, 0, 0, 0); + + if (should_match && error_code_returned == REG_NOMATCH) + { + printf ("\nIgnore-case test failed:\n"); + printf (" The string to match was: `%s'.\n", string); + print_pattern_info (pattern, &pattern_buffer); + + if (cflags & REG_ICASE) + printf (" The cflag REG_ICASE was set.\n"); + } + + regfree (&pattern_buffer); +} + + +static void +test_newline (should_match, pattern, string, cflags) + boolean should_match; + char *pattern; + char *string; + int cflags; +{ + regex_t pattern_buffer; + int error_code_returned; + + test_compile (1, 0, pattern, &pattern_buffer, cflags); + error_code_returned = regexec (&pattern_buffer, string, 0, 0, 0); + + if (should_match && error_code_returned == REG_NOMATCH) + { + printf ("\nNewline test failed:\n"); + printf (" The string to match was: `%s'.\n", string); + print_pattern_info (pattern, &pattern_buffer); + + if (cflags & REG_NEWLINE) + printf (" The cflag REG_NEWLINE was set.\n"); + else + printf (" The cflag REG_NEWLINE wasn't set.\n"); + } + + regfree (&pattern_buffer); +} + + +static void +test_posix_match (should_match, pattern, string, cflags) + boolean should_match; + char *pattern; + char *string; + int cflags; +{ + regex_t pattern_buffer; + int error_code_returned; + boolean was_error = false; + + test_compile (1, 0, pattern, &pattern_buffer, cflags); + error_code_returned = regexec (&pattern_buffer, string, 0, 0, 0); + + if (should_match && error_code_returned == REG_NOMATCH) + { + printf ("\nShould have matched but didn't:\n"); + was_error = true; + } + else if (!should_match && error_code_returned != REG_NOMATCH) + { + printf ("\nShould not have matched but did:\n"); + was_error = true; + } + + if (was_error) + { + printf (" The string to match was: `%s'.\n", string); + print_pattern_info (pattern, &pattern_buffer); + } + + regfree (&pattern_buffer); +} + + +static void +test_regexec () +{ + regmatch_t pmatch[3]; + regmatch_t correct_pmatch[3]; + int cflags = 0; + int eflags = 0; + + printf ("\nStarting regexec tests.\n"); + + cflags = REG_NOSUB; /* shouldn't look at any of pmatch. */ + test_pmatch ("a", "a", 0, pmatch, correct_pmatch, cflags); + + /* Ask for less `pmatch'es than there are pattern subexpressions. + (Shouldn't look at pmatch[2]. */ + cflags = REG_EXTENDED; + fill_pmatch (correct_pmatch, 0, 1, 0, 1, 100, 101); + test_pmatch ("((a))", "a", 2, pmatch, correct_pmatch, cflags); + + /* Ask for same number of `pmatch'es as there are pattern subexpressions. */ + cflags = REG_EXTENDED; + fill_pmatch(correct_pmatch, 0, 1, 0, 1, -1, -1); + test_pmatch ("(a)", "a", 2, pmatch, correct_pmatch, cflags); + + /* Ask for more `pmatch'es than there are pattern subexpressions. */ + cflags = REG_EXTENDED; + fill_pmatch (correct_pmatch, 0, 1, -1, -1, -1, -1); + test_pmatch ("a", "a", 2, pmatch, correct_pmatch, cflags); + + eflags = REG_NOTBOL; + test_eflags (true, false, "^a", "a", cflags, eflags); + test_eflags (true, false, "(^a)", "a", cflags, eflags); + test_eflags (true, false, "a|^b", "b", cflags, eflags); + test_eflags (true, false, "^b|a", "b", cflags, eflags); + + eflags = REG_NOTEOL; + test_eflags (false, true, "a$", "a", cflags, eflags); + test_eflags (false, true, "(a$)", "a", cflags, eflags); + test_eflags (false, true, "a|b$", "b", cflags, eflags); + test_eflags (false, true, "b$|a", "b", cflags, eflags); + + eflags = REG_NOTBOL | REG_NOTEOL; + test_eflags (true, true, "^a$", "a", cflags, eflags); + test_eflags (true, true, "(^a$)", "a", cflags, eflags); + test_eflags (true, true, "a|(^b$)", "b", cflags, eflags); + test_eflags (true, true, "(^b$)|a", "b", cflags, eflags); + + cflags = REG_ICASE; + test_ignore_case (true, "a", "a", cflags); + test_ignore_case (true, "A", "A", cflags); + test_ignore_case (true, "A", "a", cflags); + test_ignore_case (true, "a", "A", cflags); + + test_ignore_case (true, "@", "@", cflags); + test_ignore_case (true, "\\[", "[", cflags); + test_ignore_case (true, "`", "`", cflags); + test_ignore_case (true, "{", "{", cflags); + + test_ignore_case (true, "[!-`]", "A", cflags); + test_ignore_case (true, "[!-`]", "a", cflags); + + cflags = 0; + test_ignore_case (false, "a", "a", cflags); + test_ignore_case (false, "A", "A", cflags); + test_ignore_case (false, "A", "a", cflags); + test_ignore_case (false, "a", "A", cflags); + + test_ignore_case (true, "@", "@", cflags); + test_ignore_case (true, "\\[", "[", cflags); + test_ignore_case (true, "`", "`", cflags); + test_ignore_case (true, "{", "{", cflags); + + test_ignore_case (true, "[!-`]", "A", cflags); + test_ignore_case (false, "[!-`]", "a", cflags); + + + /* Test newline stuff. */ + cflags = REG_EXTENDED | REG_NEWLINE; + test_newline (true, "\n", "\n", cflags); + test_newline (true, "a\n", "a\n", cflags); + test_newline (true, "\nb", "\nb", cflags); + test_newline (true, "a\nb", "a\nb", cflags); + + test_newline (false, ".", "\n", cflags); + test_newline (false, "[^a]", "\n", cflags); + + test_newline (true, "\n^a", "\na", cflags); + test_newline (true, "\n(^a|b)", "\na", cflags); + test_newline (true, "a$\n", "a\n", cflags); + test_newline (true, "(a$|b)\n", "a\n", cflags); + test_newline (true, "(a$|b|c)\n", "a\n", cflags); + test_newline (true, "((a$|b|c)$)\n", "a\n", cflags); + test_newline (true, "((a$|b|c)$)\n", "b\n", cflags); + test_newline (true, "(a$|b)\n|a\n", "a\n", cflags); + + test_newline (true, "^a", "\na", cflags); + test_newline (true, "a$", "a\n", cflags); + + /* Now test normal behavior. */ + cflags = REG_EXTENDED; + test_newline (true, "\n", "\n", cflags); + test_newline (true, "a\n", "a\n", cflags); + test_newline (true, "\nb", "\nb", cflags); + test_newline (true, "a\nb", "a\nb", cflags); + + test_newline (true, ".", "\n", cflags); + test_newline (true, "[^a]", "\n", cflags); + + test_newline (false, "\n^a", "\na", cflags); + test_newline (false, "a$\n", "a\n", cflags); + + test_newline (false, "^a", "\na", cflags); + test_newline (false, "a$", "a\n", cflags); + + + /* Test that matches whole string only. */ + cflags = 0; + test_posix_match (true, "a", "a", cflags); + + /* Tests that match substrings. */ + test_posix_match (true, "a", "ab", cflags); + test_posix_match (true, "b", "ab", cflags); + + /* Test that doesn't match. */ + test_posix_match (false, "a", "b", cflags); + + printf ("\nFinished regexec tests.\n"); +} + + +static void +test_error_code_message (error_code, expected_error_message) + int error_code; + char *expected_error_message; +{ + char returned_error_message[TEST_ERRBUF_SIZE]; + char error_code_string[ERROR_CODE_LENGTH]; + size_t expected_error_message_length = strlen (expected_error_message) + 1; + size_t returned_error_message_length = regerror (error_code, 0, + returned_error_message, + TEST_ERRBUF_SIZE); + + if (returned_error_message_length != expected_error_message_length) + { + printf ("\n\n Testing returned error codes, with expected error \ +message `%s':\n", expected_error_message); + + printf ("\n\n and returned error message `%s':\n", + returned_error_message); + printf (" should have returned a length of %d but returned %d.\n", + expected_error_message_length, returned_error_message_length); + } + + if (strncmp (expected_error_message, returned_error_message, + TEST_ERRBUF_SIZE - 1) != 0) + { + + get_error_string (error_code, error_code_string), + printf ("\n\n With error code %s (%d), expected error message:\n", + error_code_string, error_code); + + printf (" `%s'\n", expected_error_message); + printf (" but got:\n"); + printf (" `%s'\n", returned_error_message); + } +} + + +static void +test_error_code_allocation (error_code, expected_error_message) + int error_code; + char *expected_error_message; +{ + char *returned_error_message = NULL; + char error_code_string[ERROR_CODE_LENGTH]; + size_t returned_error_message_length = regerror (error_code, 0, + returned_error_message, + (size_t)0); + + returned_error_message = xmalloc (returned_error_message_length + 1); + + regerror (error_code, 0, returned_error_message, + returned_error_message_length); + + if (strcmp (expected_error_message, returned_error_message) != 0) + { + get_error_string (error_code, error_code_string), + + printf ("\n\n Testing error code allocation,\n"); + printf ("with error code %s (%d), expected error message:\n", + error_code_string, error_code); + printf (" `%s'\n", expected_error_message); + printf (" but got:\n"); + printf (" `%s'\n", returned_error_message); + } +} + + +static void +test_regerror () +{ + test_error_code_message (REG_NOMATCH, "No match"); + test_error_code_message (REG_BADPAT, "Invalid regular expression"); + test_error_code_message (REG_ECOLLATE, "Invalid collation character"); + test_error_code_message (REG_ECTYPE, "Invalid character class name"); + test_error_code_message (REG_EESCAPE, "Trailing backslash"); + test_error_code_message (REG_ESUBREG, "Invalid back reference"); + test_error_code_message (REG_EBRACK, "Unmatched [ or [^"); + test_error_code_message (REG_EPAREN, "Unmatched ( or \\("); + test_error_code_message (REG_EBRACE, "Unmatched \\{"); + test_error_code_message (REG_BADBR, "Invalid content of \\{\\}"); + test_error_code_message (REG_ERANGE, "Invalid range end"); + test_error_code_message (REG_ESPACE, "Memory exhausted"); + test_error_code_message (REG_BADRPT, "Invalid preceding regular expression"); + test_error_code_message (REG_EEND, "Premature end of regular expression"); + test_error_code_message (REG_ESIZE, "Regular expression too big"); + test_error_code_allocation (REG_ERPAREN, "Unmatched ) or \\)"); +} + + +void +test_posix_interface () +{ + printf ("\nStarting POSIX interface tests.\n"); + t = posix_interface_test; + + test_regcomp (); + test_regexec (); + test_regerror (); + + printf ("\nFinished POSIX interface tests.\n"); +} diff --git a/gnu/lib/libregex/test/psx-interv.c b/gnu/lib/libregex/test/psx-interv.c new file mode 100644 index 0000000000..6725c38d00 --- /dev/null +++ b/gnu/lib/libregex/test/psx-interv.c @@ -0,0 +1,140 @@ +/* psx-interv.c: test POSIX intervals, both basic and extended. */ + +#include "test.h" + +void +test_intervals () +{ + printf ("\nStarting POSIX interval tests.\n"); + + test_should_match = true; + /* Valid intervals. */ + test_match (BRACES_TO_OPS (PARENS_TO_OPS ("(a{1,2}b)*")), "abaab"); + test_fastmap (BRACES_TO_OPS (PARENS_TO_OPS ("(a{1,2}b)*")), "a", 0, 0); + TEST_REGISTERS (BRACES_TO_OPS (PARENS_TO_OPS ("(a{1,2}b)*")), + "abaab", 0, 5, 2, 5, -1, -1); + + test_match (BRACES_TO_OPS ("a{0}"), ""); + test_fastmap (BRACES_TO_OPS ("a{0}"), "", 0, 0); + TEST_REGISTERS (BRACES_TO_OPS ("a{0}"), "", 0, 0, -1, -1, -1, -1); + TEST_REGISTERS (BRACES_TO_OPS ("a{0}"), "x", 0, 0, -1, -1, -1, -1); + + test_match (BRACES_TO_OPS ("a{0,}"), ""); + test_match (BRACES_TO_OPS ("a{0,}"), "a"); + test_fastmap (BRACES_TO_OPS ("a{0,}"), "a", 0, 0); + TEST_REGISTERS (BRACES_TO_OPS ("a{0,}"), "a", 0, 1, -1, -1, -1, -1); + TEST_REGISTERS (BRACES_TO_OPS ("a{0,}"), "xax", 0, 0, -1, -1, -1, -1); + + test_match (BRACES_TO_OPS ("a{1}"), "a"); + test_match (BRACES_TO_OPS ("a{1,}"), "a"); + test_match (BRACES_TO_OPS ("a{1,}"), "aa"); + test_match (BRACES_TO_OPS ("a{0,0}"), ""); + test_match (BRACES_TO_OPS ("a{0,1}"), ""); + test_match (BRACES_TO_OPS ("a{0,1}"), "a"); + test_match (BRACES_TO_OPS ("a{1,3}"), "a"); + test_match (BRACES_TO_OPS ("a{1,3}"), "aa"); + test_match (BRACES_TO_OPS ("a{1,3}"), "aaa"); + TEST_REGISTERS (BRACES_TO_OPS ("a{1,3}"), "aaa", 0, 3, -1, -1, -1, -1); + TEST_REGISTERS (BRACES_TO_OPS ("a{1,3}"), "xaaax", 1, 4, -1, -1, -1, -1); + + test_match (BRACES_TO_OPS ("a{0,3}b"), "b"); + test_match (BRACES_TO_OPS ("a{0,3}b"), "aaab"); + test_fastmap (BRACES_TO_OPS ("a{0,3}b"), "ab", 0, 0); + TEST_REGISTERS (BRACES_TO_OPS ("a{0,3}b"), "b", 0, 1, -1, -1, -1, -1); + TEST_REGISTERS (BRACES_TO_OPS ("a{0,3}b"), "xbx", 1, 2, -1, -1, -1, -1); + + test_match (BRACES_TO_OPS ("a{1,3}b"), "ab"); + test_match (BRACES_TO_OPS ("a{1,3}b"), "aaab"); + test_match (BRACES_TO_OPS ("ab{1,3}c"), "abbbc"); + + test_match (BRACES_TO_OPS (PARENS_TO_OPS ("(a){0,3}b")), "b"); + test_fastmap (BRACES_TO_OPS (PARENS_TO_OPS ("(a){0,3}b")), "ab", 0, 0); + TEST_REGISTERS (BRACES_TO_OPS (PARENS_TO_OPS ("(a){0,3}b")), "b", 0, 1, -1, -1, -1, -1); + TEST_REGISTERS (BRACES_TO_OPS (PARENS_TO_OPS ("(a){0,3}b")), "ab", 0, 2, 0, 1, -1, -1); + TEST_REGISTERS (BRACES_TO_OPS (PARENS_TO_OPS ("(a){0,3}b")), "xabx", 1, 3, 1, 2, -1, -1); + + test_match (BRACES_TO_OPS (PARENS_TO_OPS ("(a){1,3}b")), "ab"); + test_match (BRACES_TO_OPS (PARENS_TO_OPS ("(a){1,3}b")), "aaab"); + TEST_REGISTERS (BRACES_TO_OPS (PARENS_TO_OPS ("(a){1,3}b")), "aaab", 0, 4, 2, 3, -1, -1); + TEST_REGISTERS (BRACES_TO_OPS (PARENS_TO_OPS ("(a){1,3}b")), "xaaabx", 1, 5, 3, 4, -1, -1); + + test_match (BRACES_TO_OPS (PARENS_TO_OPS ("(a*){0,3}b")), "aaaab"); + test_fastmap (BRACES_TO_OPS (PARENS_TO_OPS ("(a*){0,3}b")), "ab", 0, 0); + TEST_REGISTERS (BRACES_TO_OPS (PARENS_TO_OPS ("(a*){0,3}b")), "aaaab", 0, 5, 4, 4, -1, -1); + + test_match (BRACES_TO_OPS (PARENS_TO_OPS ("(a*){1,3}b")), "b"); + test_match (BRACES_TO_OPS (PARENS_TO_OPS ("(a*){1,3}b")), "aaab"); + test_fastmap (BRACES_TO_OPS (PARENS_TO_OPS ("(a*){1,3}b")), "ab", 0, 0); + + test_match (BRACES_TO_OPS (PARENS_TO_OPS ("(a*){1,1}ab")), "aaaab"); + TEST_REGISTERS (BRACES_TO_OPS (PARENS_TO_OPS ("(a*){1,1}ab")), "aaaab", 0, 5, 0, 3, -1, -1); + + test_match (BRACES_TO_OPS (".{0,3}b"), "b"); + test_match (BRACES_TO_OPS (".{0,3}b"), "ab"); + + test_match (BRACES_TO_OPS ("[a]{0,3}b"), "b"); + test_match (BRACES_TO_OPS ("[a]{0,3}b"), "aaab"); + test_fastmap (BRACES_TO_OPS ("[a]{0,3}b"), "ab", 0, 0); + test_match (BRACES_TO_OPS ("[^a]{0,3}b"), "bcdb"); + test_match (BRACES_TO_OPS ("ab{0,3}c"), "abbbc"); + test_match (BRACES_TO_OPS ("[[:digit:]]{0,3}d"), "123d"); + test_fastmap (BRACES_TO_OPS ("[[:digit:]]{0,3}d"), "0123456789d", 0, 0); + + test_match (BRACES_TO_OPS ("\\*{0,3}a"), "***a"); + test_match (BRACES_TO_OPS (".{0,3}b"), "aaab"); + test_match (BRACES_TO_OPS ("a{0,3}a"), "aaa"); + /* Backtracking. */ + test_fastmap (BRACES_TO_OPS (PARENS_TO_OPS ("(a{1,})*a")), "a", 0, 0); + test_match (BRACES_TO_OPS (PARENS_TO_OPS ("(a{1,})*a")), "a"); + TEST_REGISTERS (BRACES_TO_OPS (PARENS_TO_OPS ("(a{1,})*a")), "a", 0, 1, -1, -1, -1, -1); + + test_fastmap (BRACES_TO_OPS (PARENS_TO_OPS ("(a{2,})*aa")), "aa", 0, 0); + test_match (BRACES_TO_OPS (PARENS_TO_OPS ("(a{2,})*aa")), "aa"); + TEST_REGISTERS (BRACES_TO_OPS (PARENS_TO_OPS ("(a{2,})*aa")), "aa", 0, 2, -1, -1, -1, -1); + + test_match (BRACES_TO_OPS ("a{2}*"), ""); + test_match (BRACES_TO_OPS ("a{2}*"), "aa"); + + test_match (BRACES_TO_OPS ("a{1}*"), ""); + test_match (BRACES_TO_OPS ("a{1}*"), "a"); + test_match (BRACES_TO_OPS ("a{1}*"), "aa"); + + test_match (BRACES_TO_OPS ("a{1}{1}"), "a"); + + test_match (BRACES_TO_OPS ("a{1}{1}{1}"), "a"); + test_match (BRACES_TO_OPS ("a{1}{1}{2}"), "aa"); + + test_match (BRACES_TO_OPS ("a{1}{1}*"), ""); + test_match (BRACES_TO_OPS ("a{1}{1}*"), "a"); + test_match (BRACES_TO_OPS ("a{1}{1}*"), "aa"); + test_match (BRACES_TO_OPS ("a{1}{1}*"), "aaa"); + + test_match (BRACES_TO_OPS ("a{1}{2}"), "aa"); + test_match (BRACES_TO_OPS ("a{2}{1}"), "aa"); + + + test_should_match = false; + + test_match (BRACES_TO_OPS ("a{0}"), "a"); + test_match (BRACES_TO_OPS ("a{0,}"), "b"); + test_match (BRACES_TO_OPS ("a{1}"), ""); + test_match (BRACES_TO_OPS ("a{1}"), "aa"); + test_match (BRACES_TO_OPS ("a{1,}"), ""); + test_match (BRACES_TO_OPS ("a{1,}"), "b"); + test_match (BRACES_TO_OPS ("a{0,0}"), "a"); + test_match (BRACES_TO_OPS ("a{0,1}"), "aa"); + test_match (BRACES_TO_OPS ("a{0,1}"), "b"); + test_match (BRACES_TO_OPS ("a{1,3}"), ""); + test_match (BRACES_TO_OPS ("a{1,3}"), "aaaa"); + test_match (BRACES_TO_OPS ("a{1,3}"), "b"); + test_match (BRACES_TO_OPS (PARENS_TO_OPS ("(a){1,3}b")), "aaaab"); + test_match (BRACES_TO_OPS (PARENS_TO_OPS ("(a*){1,3}b")), "bb"); + test_match (BRACES_TO_OPS ("[a]{0,3}"), "aaaa"); + test_match (BRACES_TO_OPS ("[^a]{0,3}b"), "ab"); + test_match (BRACES_TO_OPS ("ab{0,3}c"), "abababc"); + test_match (BRACES_TO_OPS ("[:alpha:]{0,3}d"), "123d"); + test_match (BRACES_TO_OPS ("\\^{1,3}a"), "a"); + test_match (BRACES_TO_OPS (".{0,3}b"), "aaaab"); + + printf ("\nFinished POSIX interval tests.\n"); +} diff --git a/gnu/lib/libregex/test/regexcpp.sed b/gnu/lib/libregex/test/regexcpp.sed new file mode 100644 index 0000000000..082c136081 --- /dev/null +++ b/gnu/lib/libregex/test/regexcpp.sed @@ -0,0 +1,8 @@ +/;..*$/s/;/;\ +/g +/{ .*$/s/{/{\ +/g +/ \?[^'] /s/?/?\ +/g +/ : /s/:/:\ +/g diff --git a/gnu/lib/libregex/test/syntax.skel b/gnu/lib/libregex/test/syntax.skel new file mode 100644 index 0000000000..a3fbf64c59 --- /dev/null +++ b/gnu/lib/libregex/test/syntax.skel @@ -0,0 +1,74 @@ +/* Print which syntax bits are set. */ + +#include +#include +#include "regex.h" + +/* It's coincidental that these two are currently the same. */ +#define LONGEST_BIT_NAME "RE_UNMATCHED_RIGHT_PAREN_ORD" +#define LAST_BIT RE_UNMATCHED_RIGHT_PAREN_ORD + +/* Sum of above, when printed. Assigned in main. */ +static unsigned longest; + + +static void +test_bit (syntax, bit, name) + reg_syntax_t syntax; + unsigned bit; + char *name; +{ + char padding[100], test_str[100]; + int padding_count; + + sprintf (test_str, "%s (%d=0x%x)", name, bit, bit); + padding_count = longest - strlen (test_str); + + padding[padding_count] = 0; + while (padding_count--) + { + padding[padding_count] = ' '; + } + + printf ("%s%s (%d=0x%x): %c\n", + name, padding, bit, bit, syntax & bit ? 'y' : 'n'); +} + + +/* Macro to abbreviate the constant arguments. */ +#define TEST_BIT(bit) test_bit (syntax, bit, #bit) + +int +main (argc, argv) + int argc; + char *argv[]; +{ + reg_syntax_t syntax; + char syntax_str[1000], test_str[100]; + + switch (argc) + { + case 1: + printf ("Syntax? "); + scanf ("%s", syntax_str); + break; + + case 2: + strcpy (syntax_str, argv[1]); + break; + + default: + fprintf (stderr, "Usage: syntax [syntax].\n"); + exit (1); + } + + sscanf (syntax_str, "%i", &syntax); + + /* Figure out the longest name, so we can align the output nicely. */ + sprintf (test_str, "%s (%d=0x%x)", LONGEST_BIT_NAME, LAST_BIT, LAST_BIT); + longest = strlen (test_str); + + /* [[[replace with bit tests]]] */ + + return 0; +} diff --git a/gnu/lib/libregex/test/test.c b/gnu/lib/libregex/test/test.c new file mode 100644 index 0000000000..a8de23ef74 --- /dev/null +++ b/gnu/lib/libregex/test/test.c @@ -0,0 +1,782 @@ +/* test.c: testing routines for regex.c. */ + +#include + +#ifdef STDC_HEADERS +#include +#else +char *malloc (); +char *realloc (); +#endif + +/* Just to be complete, we make both the system V/ANSI and the BSD + versions of the string functions available. */ +#if USG || STDC_HEADERS +#include +#define index strchr +#define rindex strrchr +#define bcmp(s1, s2, len) memcmp ((s1), (s2), (len)) +#define bcopy(from, to, len) memcpy ((to), (from), (len)) +#define bzero(s, len) memset ((s), 0, (len)) +#else +#include +#define strchr index +#define strrchr rindex +#ifndef NEED_MEMORY_H +#define memcmp(s1, s2, n) bcmp ((s1), (s2), (n)) +#define memcpy(to, from, len) bcopy ((from), (to), (len)) +#endif +extern char *strtok (); +extern char *strstr (); +#endif /* not USG or STDC_HEADERS */ + +/* SunOS 4.1 declares memchr in , not . I don't + understand why. */ +#if NEED_MEMORY_H +#include +#endif + +#include "test.h" + +#define BYTEWIDTH 8 + +extern void print_partial_compiled_pattern (); +extern void print_compiled_pattern (); +extern void print_double_string (); + +/* If nonzero, the results of every test are displayed. */ +boolean verbose = false; + +/* If nonzero, don't do register testing. */ +boolean omit_register_tests = true; + +/* Says whether the current test should match or fail to match. */ +boolean test_should_match; + + +static void +set_all_registers (start0, end0, start1, end1, + start2, end2, start3, end3, + start4, end4, start5, end5, + start6, end6, start7, end7, + start8, end8, start9, end9, regs) + + int start0; int end0; int start1; int end1; + int start2; int end2; int start3; int end3; + int start4; int end4; int start5; int end5; + int start6; int end6; int start7; int end7; + int start8; int end8; int start9; int end9; + struct re_registers *regs; + + { + unsigned r; + + regs->start[0] = start0; regs->end[0] = end0; + regs->start[1] = start1; regs->end[1] = end1; + regs->start[2] = start2; regs->end[2] = end2; + regs->start[3] = start3; regs->end[3] = end3; + regs->start[4] = start4; regs->end[4] = end4; + regs->start[5] = start5; regs->end[5] = end5; + regs->start[6] = start6; regs->end[6] = end6; + regs->start[7] = start7; regs->end[7] = end7; + regs->start[8] = start8; regs->end[8] = end8; + regs->start[9] = start9; regs->end[9] = end9; + for (r = 10; r < regs->num_regs; r++) + { + regs->start[r] = -1; + regs->end[r] = -1; + } + } + + + +/* Return the concatenation of S1 and S2. This would be a prime place + to use varargs. */ + +char * +concat (s1, s2) + char *s1; + char *s2; +{ + char *answer = xmalloc (strlen (s1) + strlen (s2) + 1); + + strcpy (answer, s1); + strcat (answer, s2); + + return answer; +} + + +#define OK_TO_SEARCH (nonconst_buf.fastmap_accurate && (str1 || str2)) + +/* We ignore the `can_be_null' argument. Should just be removed. */ + +void +general_test (pattern_should_be_valid, match_whole_string, + pat, str1, str2, start, range, end, correct_fastmap, + correct_regs, can_be_null) + unsigned pattern_should_be_valid; + unsigned match_whole_string; + const char *pat; + char *str1, *str2; + int start, range, end; + char *correct_fastmap; + struct re_registers *correct_regs; + int can_be_null; +{ + struct re_pattern_buffer nonconst_buf; + struct re_pattern_buffer old_buf; + struct re_registers regs; + const char *r; + char fastmap[1 << BYTEWIDTH]; + unsigned *regs_correct = NULL; + unsigned all_regs_correct = 1; + boolean fastmap_internal_error = false; + unsigned match = 0; + unsigned match_1 = 0; + unsigned match_2 = 0; + unsigned invalid_pattern = 0; + boolean internal_error_1 = false; + boolean internal_error_2 = false; + + + nonconst_buf.allocated = 8; + nonconst_buf.buffer = xmalloc (nonconst_buf.allocated); + nonconst_buf.fastmap = fastmap; + nonconst_buf.translate = 0; + + assert (pat != NULL); + r = re_compile_pattern (pat, strlen (pat), &nonconst_buf); + + /* Kludge: if we are doing POSIX testing, we really should have + called regcomp, not re_compile_pattern. As it happens, the only + way in which it matters is that re_compile_pattern sets the + newline/anchor field for matching (part of what happens when + REG_NEWLINE is given to regcomp). We have to undo that for POSIX + matching. */ + if (t == posix_basic_test || t == posix_extended_test) + nonconst_buf.newline_anchor = 0; + + invalid_pattern = r != NULL; + + if (!r) + { + int r; + + if (!pattern_should_be_valid) + printf ("\nShould have been an invalid pattern but wasn't:\n"); + else + { + fastmap_internal_error = (re_compile_fastmap (&nonconst_buf) == -2); + + if (correct_fastmap) + nonconst_buf.fastmap_accurate = + memcmp (nonconst_buf.fastmap, correct_fastmap, 1 << BYTEWIDTH) + == 0; + + if (OK_TO_SEARCH) + { + old_buf = nonconst_buf; + old_buf.buffer = (unsigned char *) xmalloc (nonconst_buf.used); + memcpy (old_buf.buffer, nonconst_buf.buffer, nonconst_buf.used); + + /* If only one string is null, call re_match or re_search, + which is what the user would probably do. */ + if (str1 == NULL && str2 != NULL + || str2 == NULL && str1 != NULL) + { + char *the_str = str1 == NULL ? str2 : str1; + + match_1 + = match_whole_string + ? (r = re_match (&nonconst_buf, the_str, + strlen (the_str), start, ®s)) + == strlen (the_str) + : (r = re_search (&nonconst_buf, + the_str, strlen (the_str), + start, range, ®s)) + >= 0; + + if (r == -2) + internal_error_1 = true; + } + else + match_1 = 1; + + /* Also call with re_match_2 or re_search_2, as they might + do this. (Also can check calling with either string1 + or string2 or both null.) */ + if (match_whole_string) + { + r = re_match_2 (&nonconst_buf, + str1, SAFE_STRLEN (str1), + str2, SAFE_STRLEN (str2), + start, ®s, end); + match_2 = r == SAFE_STRLEN (str1) + SAFE_STRLEN (str2); + } + else + { + r = re_search_2 (&nonconst_buf, + str1, SAFE_STRLEN (str1), + str2, SAFE_STRLEN (str2), + start, range, ®s, end); + match_2 = r >= 0; + } + + if (r == -2) + internal_error_2 = true; + + match = match_1 & match_2; + + if (correct_regs) + { + unsigned reg; + if (regs_correct != NULL) + free (regs_correct); + + regs_correct + = (unsigned *) xmalloc (regs.num_regs * sizeof (unsigned)); + + for (reg = 0; + reg < regs.num_regs && reg < correct_regs->num_regs; + reg++) + { + regs_correct[reg] + = (regs.start[reg] == correct_regs->start[reg] + && regs.end[reg] == correct_regs->end[reg]) +#ifdef EMPTY_REGS_CONFUSED + /* There is confusion in the standard about + the registers in some patterns which can + match either the empty string or not match. + For example, in `((a*))*' against the empty + string, the two registers can either match + the empty string (be 0/0), or not match + (because of the outer *) (be -1/-1). (Or + one can do one and one can do the other.) */ + || (regs.start[reg] == -1 && regs.end[reg] == -1 + && correct_regs->start[reg] + == correct_regs->end[reg]) +#endif + ; + + all_regs_correct &= regs_correct[reg]; + } + } + } /* OK_TO_SEARCH */ + } + } + + if (fastmap_internal_error) + printf ("\n\nInternal error in re_compile_fastmap:"); + + if (internal_error_1) + { + if (!fastmap_internal_error) + printf ("\n"); + + printf ("\nInternal error in re_match or re_search:"); + } + + if (internal_error_2) + { + if (!internal_error_1) + printf ("\n"); + + printf ("\nInternal error in re_match_2 or re_search_2:"); + } + + if ((OK_TO_SEARCH && ((match && !test_should_match) + || (!match && test_should_match)) + || (correct_regs && !all_regs_correct)) + || !nonconst_buf.fastmap_accurate + || invalid_pattern + || !pattern_should_be_valid + || internal_error_1 || internal_error_2 + || verbose) + { + if (OK_TO_SEARCH && match && !test_should_match) + { + printf ("\n\nMatched but shouldn't have:\n"); + if (match_1) + printf ("The single match/search succeeded.\n"); + + if (match_2) + printf ("The double match/search succeeded.\n"); + } + else if (OK_TO_SEARCH && !match && test_should_match) + { + printf ("\n\nDidn't match but should have:\n"); + if (!match_1) + printf ("The single match/search failed.\n"); + + if (!match_2) + printf ("The double match/search failed.\n"); + } + else if (invalid_pattern && pattern_should_be_valid) + printf ("\n\nInvalid pattern (%s):\n", r); + else if (!nonconst_buf.fastmap_accurate && pattern_should_be_valid) + printf ("\n\nIncorrect fastmap:\n"); + else if (OK_TO_SEARCH && correct_regs && !all_regs_correct) + printf ("\n\nNot all registers were correct:\n"); + else if (verbose) + printf ("\n\nTest was OK:\n"); + + + if ((!(invalid_pattern && !pattern_should_be_valid)) || verbose) + printf (" Pattern: `%s'.\n", pat); + + if (pattern_should_be_valid || verbose + || internal_error_1 || internal_error_2) + { + printf(" Strings: "); + printf ("`%s' and ", str1 == NULL ? "NULL" : str1); + printf ("`%s'.\n", str2 == NULL ? "NULL" : str2); + + if ((OK_TO_SEARCH || verbose || internal_error_1 || internal_error_2) + && !invalid_pattern) + { + if (memcmp (old_buf.buffer, nonconst_buf.buffer, + nonconst_buf.used) != 0 + && !invalid_pattern) + { + printf(" (%s)\n", r ? r : "Valid regular expression"); + printf ("\n Compiled pattern before matching: "); + print_compiled_pattern (&old_buf); + printf ("\n Compiled pattern after matching: "); + } + else + printf ("\n Compiled pattern: "); + + print_compiled_pattern (&nonconst_buf); + } + + if (correct_fastmap && (!nonconst_buf.fastmap_accurate || verbose)) + { + printf ("\n The fastmap should have been: "); + print_fastmap (correct_fastmap); + + printf ("\n Fastmap: "); + print_fastmap (fastmap); + + printf ("\n Compiled pattern before matching: "); + print_compiled_pattern (&nonconst_buf); + } + + if ((!all_regs_correct || verbose) && correct_regs) + { + unsigned this_reg; + printf ("\n Incorrect registers:"); + + for (this_reg = 0; this_reg < regs.num_regs; this_reg++) + { + if (!regs_correct[this_reg]) + { + printf ("\n Register %d's start was %2d. ", this_reg, + regs.start[this_reg]); + printf ("\tIt should have been %d.\n", + correct_regs->start[this_reg]); + printf (" Register %d's end was %2d. ", this_reg, + regs.end[this_reg]); + printf ("\tIt should have been %d.\n", + correct_regs->end[this_reg]); + } + } + } + } + } + + if (nonconst_buf.buffer != NULL) + free (nonconst_buf.buffer); + + if (OK_TO_SEARCH) + { + free (old_buf.buffer); + + if (correct_regs) + free (regs_correct); + + } + + nonconst_buf.buffer = old_buf.buffer = NULL; + regs_correct = NULL; + regs.start = regs.end = NULL; + +} /* general_test */ + + +void +test_search_return (match_start_wanted, pattern, string) + int match_start_wanted; + const char *pattern; + char *string; +{ + struct re_pattern_buffer buf; + char fastmap[1 << BYTEWIDTH]; + const char *compile_return; + int match_start; + static num_times_called = 0; + + num_times_called++; + buf.allocated = 1; + buf.buffer = xmalloc (buf.allocated); + + assert (pattern != NULL); + buf.translate = 0; + compile_return = re_compile_pattern (pattern, strlen (pattern), &buf); + + if (compile_return) + { + printf ("\n\nInvalid pattern in test_match_start:\n"); + printf ("%s\n", compile_return); + } + else + { + buf.fastmap = fastmap; + match_start = re_search (&buf, string, strlen (string), + 0, strlen (string), 0); + + if (match_start != match_start_wanted) + printf ("\nWanted search to start at %d but started at %d.\n", + match_start, match_start_wanted); + } + free (buf.buffer); + buf.buffer = NULL; +} + + +#define SET_FASTMAP() \ + { \ + unsigned this_char; \ + \ + memset (correct_fastmap, invert, (1 << BYTEWIDTH)); \ + \ + for (this_char = 0; this_char < strlen (fastmap_string); this_char++)\ + correct_fastmap[fastmap_string[this_char]] = !invert; \ + correct_fastmap['\n'] = match_newline; \ + } + + +void +test_fastmap (pat, fastmap_string, invert, match_newline) + const char *pat; + char *fastmap_string; + unsigned invert; + unsigned match_newline; +{ + char correct_fastmap[(1 << BYTEWIDTH)]; + + SET_FASTMAP (); + general_test (1, 0, pat, NULL, NULL, -1, 0, -1, correct_fastmap, 0, -1); +} + + +void +test_fastmap_search (pat, str, fastmap_string, invert, match_newline, + can_be_null, start0, end0) + const char *pat; + char *str; + char *fastmap_string; + unsigned invert; + unsigned match_newline; + int can_be_null; + int start0; + int end0; +{ + char correct_fastmap[(1 << BYTEWIDTH)]; + struct re_registers correct_regs; + + correct_regs.num_regs = RE_NREGS; + correct_regs.start = (int *) xmalloc (RE_NREGS * sizeof (int)); + correct_regs.end = (int *) xmalloc (RE_NREGS * sizeof (int)); + + set_all_registers (start0, end0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, &correct_regs); + SET_FASTMAP (); + general_test (1, 0, pat, str, NULL, 0, SAFE_STRLEN (str), SAFE_STRLEN (str), + correct_fastmap, &correct_regs, can_be_null); + + free (correct_regs.start); + free (correct_regs.end); +} + + + + +void +test_all_registers (pat, str1, str2, + start0, end0, start1, end1, + start2, end2, start3, end3, + start4, end4, start5, end5, + start6, end6, start7, end7, + start8, end8, start9, end9) + char *pat; char *str1; char *str2; + int start0; int end0; int start1; int end1; + int start2; int end2; int start3; int end3; + int start4; int end4; int start5; int end5; + int start6; int end6; int start7; int end7; + int start8; int end8; int start9; int end9; +{ + struct re_registers correct_regs; + + if (omit_register_tests) return; + + correct_regs.num_regs = RE_NREGS; + correct_regs.start = (int *) xmalloc (RE_NREGS * sizeof (int)); + correct_regs.end = (int *) xmalloc (RE_NREGS * sizeof (int)); + + set_all_registers (start0, end0, start1, end1, start2, end2, start3, end3, + start4, end4, start5, end5, start6, end6, start7, end7, + start8, end8, start9, end9, &correct_regs); + + general_test (1, 0, pat, str1, str2, 0, + SAFE_STRLEN (str1) + SAFE_STRLEN (str2), + SAFE_STRLEN (str1) + SAFE_STRLEN (str2), + NULL, &correct_regs, -1); + + free (correct_regs.start); + free (correct_regs.end); +} + + +void +invalid_pattern (error_code_expected, pattern) + int error_code_expected; + char *pattern; +{ + regex_t pattern_buffer; + int cflags + = re_syntax_options == RE_SYNTAX_POSIX_EXTENDED + || re_syntax_options == RE_SYNTAX_POSIX_MINIMAL_EXTENDED + ? REG_EXTENDED : 0; + + test_compile (0, error_code_expected, pattern, &pattern_buffer, cflags); +} + + +void +valid_pattern (pattern) + char *pattern; +{ + regex_t pattern_buffer; + int cflags + = re_syntax_options == RE_SYNTAX_POSIX_EXTENDED + || re_syntax_options == RE_SYNTAX_POSIX_MINIMAL_EXTENDED + ? REG_EXTENDED : 0; + + test_compile (1, 0, pattern, &pattern_buffer, cflags); +} + + +char * +delimiters_to_ops (source, left_delimiter, right_delimiter) + char *source; + char left_delimiter; + char right_delimiter; +{ + static char *answer = NULL; + char *tmp = NULL; + boolean double_size = false; + unsigned source_char; + unsigned answer_char = 0; + + assert (source != NULL); + + switch (left_delimiter) + { + case '(': if (!(re_syntax_options & RE_NO_BK_PARENS)) + double_size = true; + break; + case '{': if (!(re_syntax_options & RE_NO_BK_BRACES)) + double_size = true; + break; + default: printf ("Found strange delimiter %c in delimiter_to_ops.\n", + left_delimiter); + printf ("The source was `%s'\n", source); + exit (0); + } + + if (answer == source) + { + tmp = (char *) xmalloc (strlen (source) + 1); + strcpy (tmp, source); + source = tmp; + } + + if (answer) + { + free (answer); + answer = NULL; + } + + answer = (char *) xmalloc ((double_size + ? strlen (source) << 1 + : strlen (source)) + + 1); + if (!double_size) + strcpy (answer, source); + else + { + for (source_char = 0; source_char < strlen (source); source_char++) + { + if (source[source_char] == left_delimiter + || source[source_char] == right_delimiter) + answer[answer_char++] = '\\'; + + answer[answer_char++] = source[source_char]; + } + answer[answer_char] = 0; + } + + return answer; +} + + +void +print_pattern_info (pattern, pattern_buffer_ptr) + const char *pattern; + regex_t *pattern_buffer_ptr; +{ + printf (" Pattern: `%s'.\n", pattern); + printf (" Compiled pattern: "); + print_compiled_pattern (pattern_buffer_ptr); +} + + +void +valid_nonposix_pattern (pattern) + char *pattern; +{ + struct re_pattern_buffer nonconst_buf; + + nonconst_buf.allocated = 0; + nonconst_buf.buffer = NULL; + nonconst_buf.translate = NULL; + + assert (pattern != NULL); + + if (re_compile_pattern (pattern, strlen (pattern), &nonconst_buf)) + { + printf ("Couldn't compile the pattern.\n"); + print_pattern_info (pattern, &nonconst_buf); + } +} + + +void +compile_and_print_pattern (pattern) + char *pattern; +{ + struct re_pattern_buffer nonconst_buf; + + nonconst_buf.allocated = 0; + nonconst_buf.buffer = NULL; + + if (re_compile_pattern (pattern, strlen (pattern), &nonconst_buf)) + printf ("Couldn't compile the pattern.\n"); + + print_pattern_info (pattern, &nonconst_buf); +} + + +void +test_case_fold (pattern, string) + const char *pattern; + char* string; +{ + struct re_pattern_buffer nonconst_buf; + const char *ret; + + init_pattern_buffer (&nonconst_buf); + nonconst_buf.translate = upcase; + + assert (pattern != NULL); + ret = re_compile_pattern (pattern, strlen (pattern), &nonconst_buf); + + if (ret) + { + printf ("\nShould have been a valid pattern but wasn't.\n"); + print_pattern_info (pattern, &nonconst_buf); + } + else + { + if (test_should_match + && re_match (&nonconst_buf, string, strlen (string), 0, 0) + != strlen (string)) + { + printf ("Match failed for case fold.\n"); + printf (" Pattern: `%s'.\n", pattern); + printf (" String: `%s'.\n", string == NULL ? "NULL" : string); + } + } +} + + +void +test_match_n_times (n, pattern, string) + unsigned n; + char* pattern; + char* string; +{ + struct re_pattern_buffer buf; + const char *r; + unsigned match = 0; + unsigned this_match; + + buf.allocated = 0; + buf.buffer = NULL; + buf.translate = 0; + + assert (pattern != NULL); + + r = re_compile_pattern (pattern, strlen (pattern), &buf); + if (r) + { + printf ("Didn't compile.\n"); + printf (" Pattern: %s.\n", pattern); + } + else + { + for (this_match = 1; this_match <= n; this_match++) + match = (re_match (&buf, string, strlen (string), + 0, 0) + == strlen (string)); + + if (match && !test_should_match) + printf ("\n\nMatched but shouldn't have:\n"); + else if (!match && test_should_match) + printf ("\n\nDidn't match but should have:\n"); + + if ((match && !test_should_match) || (!match && test_should_match)) + { + printf(" The string to match was: "); + if (string) + printf ("`%s' and ", string); + else + printf ("`'"); + + printf (" Pattern: %s.\n", pattern); + printf (" Compiled pattern: %s.\n", pattern); + print_compiled_pattern (&buf); + } + } +} + + +void +test_match_2 (pat, str1, str2) + const char *pat; + char *str1; + char *str2; +{ + general_test (1, 1, pat, str1, str2, 0, 1, + SAFE_STRLEN (str1) + SAFE_STRLEN (str2), NULL, 0, -1); +} + +void +test_match (pat, str) + const char *pat; + char *str; +{ + test_match_2 (pat, str, NULL); + test_match_2 (pat, NULL, str); +} diff --git a/gnu/lib/libregex/test/test.h b/gnu/lib/libregex/test/test.h new file mode 100644 index 0000000000..fb67126547 --- /dev/null +++ b/gnu/lib/libregex/test/test.h @@ -0,0 +1,141 @@ +/* test.h: for Regex testing. */ + +#ifndef TEST_H +#define TEST_H + +#include +#include + +#include +#include "regex.h" + + +/* A strlen that works even on a null pointer. */ +#define SAFE_STRLEN(s) (s == NULL ? 0 : strlen (s)) + +typedef enum { false = 0, true = 1 } boolean; + +extern boolean test_should_match; +extern boolean omit_register_tests; +extern void *xmalloc (); + +/* Defined in upcase.c. */ +extern char upcase[]; + +typedef enum +{ + all_test, + other_test, + posix_basic_test, + posix_extended_test, + posix_interface_test, + regress_test +} test_type; + +extern test_type t; + + +#if __STDC__ + +extern char *concat (char *, char *); + +extern void general_test (unsigned pattern_should_be_valid, + unsigned match_whole_string, + const char *pat, char *str1, char *str2, + int start, int range, int end, + char *correct_fastmap, + struct re_registers *correct_regs, int can_be_null); + + +extern void init_pattern_buffer (regex_t *pattern_buffer_ptr); + +extern void test_compile (unsigned valid_pattern, int error_code_expected, + const char *pattern, regex_t *pattern_buffer_ptr, + int cflags); + +extern char *delimiter_to_ops (char *source, char left_delimiter, + char right_delimiter); + + +extern void test_search_return (int, const char *, char *); + +extern void test_berk_search (const char *pattern, char *string); + +extern void test_fastmap (const char *pat, char *fastmap_string, unsigned invert, + unsigned match_newline); + +extern void test_fastmap_search (const char *pat, char *str, char *fastmap_string, + unsigned invert, unsigned match_newline, + int can_be_null, int start0, int end0); + +extern void test_all_registers (char *pat, char *str1, char *str2, + int start0, int end0, int start1, int end1, + int start2, int end2, int start3, int end3, + int start4, int end4, int start5, int end5, + int start6, int end6, int start7, int end7, + int start8, int end8, int start9, int end9); + +extern void print_pattern_info (const char *pattern, regex_t *pattern_buffer_ptr); +extern void compile_and_print_pattern (char *pattern); + +extern void test_case_fold (const char *pattern, char* string); + +extern void test_posix_generic (); + +extern void test_grouping (); + +extern void invalid_pattern (int error_code_expected, char *pattern); +extern void valid_nonposix_pattern (char *pattern); +extern void valid_pattern (char *pattern); + +extern void test_match_2 (const char *pat, char *str1, char *str2); +extern void test_match (const char *pat, char *str); + +#endif /* __STDC__ */ + + +#define TEST_REGISTERS_2(pat, str1, str2, start0, end0, start1, end1, start2, end2)\ + if (!omit_register_tests) \ + test_all_registers (pat, str1, str2, start0, end0, start1, end1, \ + start2, end2, -1, -1, -1, -1, -1, -1, -1, -1,\ + -1, -1, -1, -1, -1, -1) \ + + +#define TEST_REGISTERS(pat, str, start0, end0, start1, end1, start2, end2) \ + TEST_REGISTERS_2 (pat, str, NULL, start0, end0, start1, end1, start2, end2)\ + +#define BRACES_TO_OPS(string) ((char *) delimiters_to_ops (string, '{', '}')) +#define PARENS_TO_OPS(string) ((char *) delimiters_to_ops (string, '(', ')')) + +#define INVALID_PATTERN(pat) \ + general_test (0, 0, pat, NULL, NULL, -1, 0, -1, NULL, 0, -1) + + +#define MATCH_SELF(p) test_match (p, p) + +#define TEST_POSITIONED_MATCH(pat, str, start) \ + general_test (1, 0, pat, str, NULL, start, 1, SAFE_STRLEN (str), \ + NULL, 0, -1) + +#define TEST_TRUNCATED_MATCH(pat, str, end) \ + general_test (1, 0, pat, str, NULL, 0, 1, end, NULL, 0, -1) + +#define TEST_SEARCH_2(pat, str1, str2, start, range, one_past_end) \ + general_test (1, 0, pat, str1, str2, start, range, one_past_end, \ + NULL, 0, -1) + +#define TEST_SEARCH(pat, str, start, range) \ + { \ + TEST_SEARCH_2 (pat, str, NULL, start, range, SAFE_STRLEN (str)); \ + TEST_SEARCH_2 (pat, NULL, str, start, range, SAFE_STRLEN (str)); \ + } + +#endif /* TEST_H */ + +/* +Local variables: +make-backup-files: t +version-control: t +trim-versions-without-asking: nil +End: +*/ diff --git a/gnu/lib/libregex/test/tregress.c b/gnu/lib/libregex/test/tregress.c new file mode 100644 index 0000000000..7858cac150 --- /dev/null +++ b/gnu/lib/libregex/test/tregress.c @@ -0,0 +1,464 @@ +/* tregress.c: reported bugs. The `t' just makes the filename not have + a common prefix with `regex.c', so completion works better. */ + +#include "test.h" + + +boolean pause_at_error = true; + +char * +itoa (i) + int i; +{ + char *a = xmalloc (21); /* sign + 19 digits (enough for 64 bits) + null */ + + sprintf (a, "%d", i); + return a; +} + + +static void +simple_fail (routine, pat, buf, str, ret) + const char *routine; + const char *pat; + struct re_pattern_buffer *buf; + const char *str; + char *ret; +{ + fprintf (stderr, "Failed %s (return = %s).\n", routine, ret); + if (str && *str) fprintf (stderr, " String = %s\n", str); + fprintf (stderr, " Pattern = %s\n", pat); + print_compiled_pattern (buf); + + if (pause_at_error) + { + fprintf (stderr, "RET to continue: "); + (void) getchar (); + } +} + + +/* Abbreviate the most common calls. */ + +static void +simple_compile (pat, buf) + const char *pat; + struct re_pattern_buffer *buf; +{ + const char *ret = re_compile_pattern (pat, strlen (pat), buf); + + if (ret != NULL) simple_fail ("compile", pat, buf, NULL, ret); +} + + +static void +simple_fastmap (pat) + const char *pat; +{ + struct re_pattern_buffer buf; + char fastmap[256]; + int ret; + + buf.allocated = 0; + buf.buffer = buf.translate = NULL; + buf.fastmap = fastmap; + + simple_compile (pat, &buf); + + ret = re_compile_fastmap (&buf); + + if (ret != 0) simple_fail ("fastmap compile", pat, &buf, NULL, itoa (ret)); +} + + +#define SIMPLE_MATCH(pat, str) do_match (pat, str, strlen (str)) +#define SIMPLE_NONMATCH(pat, str) do_match (pat, str, -1) + +static void +do_match (pat, str, expected) + const char *pat, *str; + int expected; +{ + int ret; + unsigned len; + struct re_pattern_buffer buf; + + buf.allocated = 0; + buf.buffer = buf.translate = buf.fastmap = NULL; + + simple_compile (pat, &buf); + + len = strlen (str); + + ret = re_match_2 (&buf, NULL, 0, str, len, 0, NULL, len); + + if (ret != expected) simple_fail ("match", pat, &buf, str, itoa (ret)); +} + + +static void +simple_search (pat, str, correct_startpos) + const char *pat, *str; + int correct_startpos; +{ + int ret; + unsigned len; + struct re_pattern_buffer buf; + + buf.allocated = 0; + buf.buffer = buf.translate = buf.fastmap = NULL; + + simple_compile (pat, &buf); + + len = strlen (str); + + ret = re_search_2 (&buf, NULL, 0, str, len, 0, len, NULL, len); + + if (ret != correct_startpos) + simple_fail ("match", pat, &buf, str, itoa (ret)); +} + +/* Past bugs people have reported. */ + +void +test_regress () +{ + extern char upcase[]; + struct re_pattern_buffer buf; + unsigned len; + struct re_registers regs; + int ret; + char *fastmap = xmalloc (256); + + buf.translate = NULL; + buf.fastmap = NULL; + buf.allocated = 0; + buf.buffer = NULL; + + printf ("\nStarting regression tests.\n"); + t = regress_test; + + test_should_match = true; + re_set_syntax (RE_SYNTAX_EMACS); + + /* enami@sys.ptg.sony.co.jp 10 Nov 92 15:19:02 JST */ + buf.translate = upcase; + SIMPLE_MATCH ("[A-[]", "A"); + buf.translate = NULL; + + /* meyering@cs.utexas.edu Nov 6 22:34:41 1992 */ + simple_search ("\\w+", "a", 0); + + /* jimb@occs.cs.oberlin.edu 10 Sep 92 00:42:33 */ + buf.translate = upcase; + SIMPLE_MATCH ("[\001-\377]", "\001"); + SIMPLE_MATCH ("[\001-\377]", "a"); + SIMPLE_MATCH ("[\001-\377]", "\377"); + buf.translate = NULL; + + /* mike@skinner.cs.uoregon.edu 1 Sep 92 01:45:22 */ + SIMPLE_MATCH ("^^$", "^"); + + /* pclink@qld.tne.oz.au Sep 7 22:42:36 1992 */ + re_set_syntax (RE_INTERVALS); + SIMPLE_MATCH ("^a\\{3\\}$", "aaa"); + SIMPLE_NONMATCH ("^a\\{3\\}$", "aa"); + re_set_syntax (RE_SYNTAX_EMACS); + + /* pclink@qld.tne.oz.au, 31 Aug 92. (conjecture) */ + re_set_syntax (RE_INTERVALS); + simple_search ("a\\{1,3\\}b", "aaab", 0); + simple_search ("a\\{1,3\\}b", "aaaab", 1); + re_set_syntax (RE_SYNTAX_EMACS); + + /* trq@dionysos.thphys.ox.ac.uk, 31 Aug 92. (simplified) */ + simple_fastmap ("^.*\n[ ]*"); + + /* wind!greg@plains.NoDak.edu, 25 Aug 92. (simplified) */ + re_set_syntax (RE_INTERVALS); + SIMPLE_MATCH ("[a-zA-Z]*.\\{5\\}", "xN0000"); + SIMPLE_MATCH ("[a-zA-Z]*.\\{5\\}$", "systemxN0000"); + SIMPLE_MATCH ("\\([a-zA-Z]*\\).\\{5\\}$", "systemxN0000"); + re_set_syntax (RE_SYNTAX_EMACS); + + /* jimb, 18 Aug 92. Don't use \000, so `strlen' (in our testing + routines) will work. (This still tickles the bug jimb reported.) */ + SIMPLE_MATCH ("[\001-\377]", "\001"); + SIMPLE_MATCH ("[\001-\377]", "a"); + SIMPLE_MATCH ("[\001-\377]", "\377"); + + /* jimb, 13 Aug 92. */ + SIMPLE_MATCH ("[\001-\177]", "\177"); + + /* Tests based on bwoelfel's below. */ + SIMPLE_MATCH ("\\(a\\|ab\\)*", "aab"); + SIMPLE_MATCH ("\\(a\\|ab\\)+", "aab"); + SIMPLE_MATCH ("\\(a*\\|ab\\)+", "aab"); + SIMPLE_MATCH ("\\(a+\\|ab\\)+", "aab"); + SIMPLE_MATCH ("\\(a?\\|ab\\)+", "aab"); + + /* bwoelfel@widget.seas.upenn.edu, 25 Jul 92. */ + SIMPLE_MATCH ("^\\([ab]+\\|bc\\)+", "abc"); + + /* jla, 3 Jul 92. Core dump in re_search_2. */ + buf.fastmap = fastmap; + buf.translate = upcase; +#define DATEDUMP_PATTERN " *[0-9]*:" + if (re_compile_pattern (DATEDUMP_PATTERN, strlen (DATEDUMP_PATTERN), &buf) + != NULL) + printf ("date dump compile failed.\n"); + regs.num_regs = 0; + regs.start = regs.end = NULL; + if (re_search_2 (&buf, NULL, 0, "Thu Jul 2 18:34:18 1992", + 24, 3, 21, ®s, 24) != 10) + printf ("date dump search failed.\n"); + buf.fastmap = 0; + buf.translate = 0; + + + /* rms, 4 Jul 1992. Pattern is much slower in Emacs 19. Fastmap + should be only a backslash. */ +#define BEGINEND_PATTERN "\\(\\\\begin\\s *{\\)\\|\\(\\\\end\\s *{\\)" + test_fastmap (BEGINEND_PATTERN, "\\", false, 0); + + + /* kaoru@is.s.u-tokyo.ac.jp, 27 Jun 1992. Code for [a-z] (in regex.c) + should translate the whole set. */ + buf.translate = upcase; +#define CASE_SET_PATTERN "[ -`]" + if (re_compile_pattern (CASE_SET_PATTERN, strlen (CASE_SET_PATTERN), &buf) + != NULL) + printf ("case set compile failed.\n"); + if (re_match_2 (&buf, "K", 1, "", 0, 0, NULL, 1) != 1) + printf ("case set match failed.\n"); + +#define CASE_SET_PATTERN2 "[`-|]" + if (re_compile_pattern (CASE_SET_PATTERN2, strlen (CASE_SET_PATTERN2), &buf) + != NULL) + printf ("case set2 compile failed.\n"); + if (re_match_2 (&buf, "K", 1, "", 0, 0, NULL, 1) != 1) + printf ("case set2 match failed.\n"); + + buf.translate = NULL; + + + /* jimb, 27 Jun 92. Problems with gaps in the string. */ +#define GAP_PATTERN "x.*y.*z" + if (re_compile_pattern (GAP_PATTERN, strlen (GAP_PATTERN), &buf) != NULL) + printf ("gap didn't compile.\n"); + if (re_match_2 (&buf, "x-", 2, "y-z-", 4, 0, NULL, 6) != 5) + printf ("gap match failed.\n"); + + + /* jimb, 19 Jun 92. Since `beginning of word' matches at the + beginning of the string, then searching ought to find it there. + If `re_compile_fastmap' is not called, then it works ok. */ + buf.fastmap = fastmap; +#define BOW_BEG_PATTERN "\\<" + if (re_compile_pattern (BOW_BEG_PATTERN, strlen (BOW_BEG_PATTERN), &buf) + != NULL) + printf ("begword-begstring didn't compile.\n"); + if (re_search (&buf, "foo", 3, 0, 3, NULL) != 0) + printf ("begword-begstring search failed.\n"); + + /* Same bug report, different null-matching pattern. */ +#define EMPTY_ANCHOR_PATTERN "^$" + if (re_compile_pattern (EMPTY_ANCHOR_PATTERN, strlen (EMPTY_ANCHOR_PATTERN), + &buf) != NULL) + printf ("empty anchor didn't compile.\n"); + if (re_search (&buf, "foo\n\nbar", 8, 0, 8, NULL) != 4) + printf ("empty anchor search failed.\n"); + + /* jimb@occs.cs.oberlin.edu, 21 Apr 92. After we first allocate + registers for a particular re_pattern_buffer, we might have to + reallocate more registers on subsequent calls -- and we should be + reusing the same memory. */ +#define ALLOC_REG_PATTERN "\\(abc\\)" + free (buf.fastmap); + buf.fastmap = 0; + if (re_compile_pattern (ALLOC_REG_PATTERN, strlen (ALLOC_REG_PATTERN), &buf) + != NULL) + printf ("register allocation didn't compile.\n"); + if (re_match (&buf, "abc", 3, 0, ®s) != 3) + printf ("register allocation didn't match.\n"); + if (regs.start[1] != 0 || regs.end[1] != 3) + printf ("register allocation reg #1 wrong.\n"); + + { + int *old_regstart = regs.start; + int *old_regend = regs.end; + + if (re_match (&buf, "abc", 3, 0, ®s) != 3) + printf ("register reallocation didn't match.\n"); + if (regs.start[1] != 0 || regs.end[1] != 3 + || old_regstart[1] != 0 || old_regend[1] != 3 + || regs.start != old_regstart || regs.end != old_regend) + printf ("register reallocation registers wrong.\n"); + } + + /* jskudlarek@std.MENTORG.COM, 21 Apr 92 (string-match). */ +#define JSKUD_PATTERN "[^/]+\\(/[^/.]+\\)?/[0-9]+$" + if (re_compile_pattern (JSKUD_PATTERN, strlen (JSKUD_PATTERN), &buf) != NULL) + printf ("jskud test didn't compile.\n"); + if (re_search (&buf, "a/1", 3, 0, 3, ®s) != 0) + printf ("jskud test didn't match.\n"); + if (regs.start[1] != -1 || regs.end[1] != -1) + printf ("jskud test, reg #1 wrong.\n"); + + /* jla's bug (with string-match), 5 Feb 92. */ + TEST_SEARCH ("\\`[ \t\n]*", "jla@challenger (Joseph Arceneaux)", 0, 100); + + /* jwz@lucid.com, 8 March 1992 (re-search-forward). (His is the + second.) These are not supposed to match. */ +#if 0 + /* This one fails quickly, because we can change the maybe_pop_jump + from the + to a pop_failure_pop, because of the c's. */ + TEST_SEARCH ("^\\(To\\|CC\\):\\([^c]*\\)+co", +"To: hbs%titanic@lucid.com (Harlan Sexton)\n\ +Cc: eb@thalidomide, jlm@thalidomide\n\ +Subject: Re: so is this really as horrible an idea as it seems to me?\n\ +In-Reply-To: Harlan Sexton's message of Sun 8-Mar-92 11:00:06 PST <9203081900.AA24794@titanic.lucid>\n\ +References: <9203080736.AA05869@thalidomide.lucid>\n\ + <9203081900.AA24794@titanic.lucid>", 0, 5000); + + /* This one takes a long, long time to complete, because we have to + keep the failure points around because we might backtrack. */ + TEST_SEARCH ("^\\(To\\|CC\\):\\(.*\n.*\\)+co", + /* "X-Windows: The joke that kills.\n\ +FCC: /u/jwz/VM/inbox\n\ +From: Jamie Zawinski \n\ */ +"To: hbs%titanic@lucid.com (Harlan Sexton)\n\ +Cc: eb@thalidomide, jlm@thalidomide\n\ +Subject: Re: so is this really as horrible an idea as it seems to me?\n\ +In-Reply-To: Harlan Sexton's message of Sun 8-Mar-92 11:00:06 PST <9203081900.AA24794@titanic.lucid>\n\ +References: <9203080736.AA05869@thalidomide.lucid>\n\ + <9203081900.AA24794@titanic.lucid>", 0, 5000); +#endif /* 0 [failed searches] */ + + + /* macrakis' bugs. */ + buf.translate = upcase; /* message of 24 Jan 91 */ + if (re_compile_pattern ("[!-`]", 5, &buf) != NULL) + printf ("Range test didn't compile.\n"); + if (re_match (&buf, "A", 1, 0, NULL) != 1) + printf ("Range test #1 didn't match.\n"); + if (re_match (&buf, "a", 1, 0, NULL) != 1) + printf ("Range test #2 didn't match.\n"); + + buf.translate = 0; +#define FAO_PATTERN "\\(f\\(.\\)o\\)+" + if (re_compile_pattern (FAO_PATTERN, strlen (FAO_PATTERN), &buf) != NULL) + printf ("faofdx test didn't compile.\n"); + if (re_search (&buf, "faofdx", 6, 0, 6, ®s) != 0) + printf ("faofdx test didn't match.\n"); + if (regs.start[1] != 0 || regs.end[1] != 3) + printf ("faofdx test, reg #1 wrong.\n"); + if (regs.start[2] != 1 || regs.end[2] != 2) + printf ("faofdx test, reg #2 wrong.\n"); + + TEST_REGISTERS ("\\(a\\)*a", "aaa", 0, 3, 1, 2, -1, -1); + test_fastmap ("^\\([^ \n]+:\n\\)+\\([^ \n]+:\\)", " \n", 1, 0); + + /* 40 lines, 48 a's in each line. */ + test_match ("^\\([^ \n]+:\n\\)+\\([^ \n]+:\\)", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:"); + + /* 640 a's followed by one b, twice. */ + test_match ("\\(.*\\)\\1", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"); + + /* 640 a's followed by two b's, twice. */ + test_match ("\\(.*\\)\\1", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabb"); + + + /* Dave G. bug: Reference to a subexpression which didn't match. + Should fail. */ + re_set_syntax (RE_NO_BK_PARENS | RE_NO_BK_VBAR); + test_match ("(ooooooooooone())-annnnnnnnnnnd-(twooooooooooo\\2)", + "ooooooooooone-annnnnnnnnnnd-twooooooooooo"); + test_match ("(o|t)", "o"); + test_match ("(o()|t)", "o"); + test_match ("(o|t)", "o"); + test_match ("(ooooooooooooooo|tttttttttttttttt())", "ooooooooooooooo"); + test_match ("(o|t())", "o"); + test_match ("(o()|t())", "o"); + test_match ("(ooooooooooooooooooooooooone()|twooooooooooooooooooooooooo())", "ooooooooooooooooooooooooone"); + test_match ("(o()|t())-a-(t\\2|f\\3)", "o-a-t"); + test_match ("(o()|t())-a-(t\\2|f\\3)", "t-a-f"); + + test_should_match = 0; + test_match ("(foo(bar)|second)\\2", "second"); + test_match ("(o()|t())-a-(t\\2|f\\3)", "t-a-t"); + test_match ("(o()|t())-a-(t\\2|f\\3)", "o-a-f"); + + re_set_syntax (RE_SYNTAX_EMACS); + test_match ("\\(foo\\(bar\\)\\|second\\)\\2", "secondbar"); + test_match ("\\(one\\(\\)\\|two\\(\\)\\)-and-\\(three\\2\\|four\\3\\)", + "one-and-four"); + test_match ("\\(one\\(\\)\\|two\\(\\)\\)-and-\\(three\\2\\|four\\3\\)", + "two-and-three"); + + test_should_match = 1; + re_set_syntax (RE_SYNTAX_EMACS); + test_match ("\\(one\\(\\)\\|two\\(\\)\\)-and-\\(three\\2\\|four\\3\\)", + "one-and-three"); + test_match ("\\(one\\(\\)\\|two\\(\\)\\)-and-\\(three\\2\\|four\\3\\)", + "two-and-four"); + + TEST_REGISTERS (":\\(.*\\)", ":/", 0, 2, 1, 2, -1, -1); + + /* Bug with `upcase' translation table, from Nico Josuttis + */ + test_should_match = 1; + test_case_fold ("[a-a]", "a"); + + printf ("\nFinished regression tests.\n"); +} + + + +/* +Local variables: +make-backup-files: t +version-control: t +trim-versions-without-asking: nil +End: +*/ diff --git a/gnu/lib/libregex/test/upcase.c b/gnu/lib/libregex/test/upcase.c new file mode 100644 index 0000000000..5147b812aa --- /dev/null +++ b/gnu/lib/libregex/test/upcase.c @@ -0,0 +1,39 @@ +/* Indexed by a character, gives the upper case equivalent of the + character. */ + +char upcase[0400] = + { 000, 001, 002, 003, 004, 005, 006, 007, + 010, 011, 012, 013, 014, 015, 016, 017, + 020, 021, 022, 023, 024, 025, 026, 027, + 030, 031, 032, 033, 034, 035, 036, 037, + 040, 041, 042, 043, 044, 045, 046, 047, + 050, 051, 052, 053, 054, 055, 056, 057, + 060, 061, 062, 063, 064, 065, 066, 067, + 070, 071, 072, 073, 074, 075, 076, 077, + 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107, + 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, + 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, + 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137, + 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107, + 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, + 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, + 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177, + 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, + 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, + 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, + 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, + 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, + 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, + 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, + 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, + 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, + 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, + 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377 + }; + + diff --git a/gnu/lib/libregex/test/xmalloc.c b/gnu/lib/libregex/test/xmalloc.c new file mode 100644 index 0000000000..88be1a668b --- /dev/null +++ b/gnu/lib/libregex/test/xmalloc.c @@ -0,0 +1,21 @@ +#include +extern char *malloc (); + +#ifndef NULL +#define NULL 0 +#endif + +void * +xmalloc (size) + unsigned size; +{ + char *new_mem = malloc (size); + + if (new_mem == NULL) + { + fprintf (stderr, "xmalloc: request for %u bytes failed.\n", size); + abort (); + } + + return new_mem; +} diff --git a/gnu/libexec/uucp/COPYING b/gnu/libexec/uucp/COPYING new file mode 100644 index 0000000000..e77696ae8d --- /dev/null +++ b/gnu/libexec/uucp/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/gnu/libexec/uucp/ChangeLog b/gnu/libexec/uucp/ChangeLog new file mode 100644 index 0000000000..48a52b75a2 --- /dev/null +++ b/gnu/libexec/uucp/ChangeLog @@ -0,0 +1,3152 @@ +Sat Feb 13 15:57:30 1993 Ian Lance Taylor (ian@comton.airs.com) + + * Released version 1.04. + + * unix/detach.c: Andrew A. Chernov: Don't check return of setsid. + +Sun Jan 31 01:45:56 1993 Ian Lance Taylor (ian@comton.airs.com) + + * cu.c (main): Pass "cu" to uuconf_init. + + * protz.c (fzprocess): Restore ZPAD char before calling getinsync. + +Sat Jan 30 22:19:26 1993 Ian Lance Taylor (ian@comton.airs.com) + + * Makefile.in (doc-dist): New target. + +Wed Jan 27 22:55:26 1993 Ian Lance Taylor (ian@comton.airs.com) + + * protg.c (fgstart): Set iGremote_segsize when using + remote-packet-size. + +Tue Jan 26 01:01:34 1993 Ian Lance Taylor (ian@comton.airs.com) + + * proti.c (fiprocess_data): always send an ACK after receiving + half a window, rather than sometimes resending a packet. Half a + window of short packets can arrive very quickly. + + * tstuu.c (main, cread, fsend): rewrote communication routines to + avoid deadlock. + +Sun Jan 24 01:02:47 1993 Ian Lance Taylor (ian@comton.airs.com) + + * trans.c (ufailed): don't report statistics if no bytes + transferred. + + * Makefile.in (install): simplified somewhat. + (dist): distribute the sample directory. + +Sat Jan 23 19:47:12 1993 Ian Lance Taylor (ian@comton.airs.com) + + * configure.in, conf.h.in, tli.c: Karl Swarz: check for and use + . + +Fri Jan 22 00:09:37 1993 Ian Lance Taylor (ian@comton.airs.com) + + * send.c (flocal_send_request): Alan Judge: don't send C in option + string when faking an E command as an S command. + +Thu Jan 21 00:09:31 1993 Ian Lance Taylor (ian@comton.airs.com) + + * uux.c (main): don't use E command if forwarding. + +Wed Jan 20 00:22:38 1993 Ian Lance Taylor (ian@comton.airs.com) + + * send.c (fsend_exec_file_init), rec.c (frec_file_end), uux.c + (main): Chip Salzenberg: always put the C line last in an + execution file, to support Fredmail. + +Tue Jan 19 00:09:43 1993 Ian Lance Taylor (ian@comton.airs.com) + + * trans.h, trans.c (ftcharge, floop, fgot_data): rewrote timing + code. + + * trans.h, trans.c, send.c, rec.c, xcmd.c, protf.c, protz.c + (fqueue_local, fqueue_remote, fqueue_send, fqueue_receive): added + boolean return value and qdaemon argument. + +Mon Jan 18 00:01:46 1993 Ian Lance Taylor (ian@comton.airs.com) + + * uucico.c (fdo_call, faccept_call): Ted Lindgreen, Chip + Salzenberg: wait for remote hangup string before hanging up. + + * proti.c (fiprocess_data, fiprocess_packet): stop scanning input + buffer after a CLOSE packet. + +Sat Jan 16 22:44:28 1993 Ian Lance Taylor (ian@comton.airs.com) + + * system.h, uucico.c (main), uuxqt.c (main), unix/init.c: Ted + Lindgreen: eliminated INIT_DAEMON. + + * log.c (ulog): don't log SIGINT if fLog_sighup is FALSE. + + * unix/move.c (fsysdep_move_file), unix/xqtsub.c + (fsysdep_move_uuxqt_files): the system call rename seems to fail + on some systems for arbitrary reasons, so always try to copy the + file by hand, not just if we get EXDEV. + + * policy.h, unix/pause.c: Gregory Gulik: added HAVE_HUNDREDTHS_NAP + configuration parameter. + +Wed Jan 6 21:06:45 1993 Ian Lance Taylor (ian@comton.airs.com) + + * unix/serial.c (fsserial_lockfile): create HDB lock files when + using HAVE_COHERENT_LOCKING. + unix/cohtty.c (fscoherent_disable_tty): consistently return FALSE + on error. + + * unix/cusub.c (fsysdep_terminal_raw): Andrew A. Chernov: if + POSIX_TERMIOS, turn of IEXTEN flag. + +Sat Jan 2 23:19:27 1993 Ian Lance Taylor (ian@comton.airs.com) + + * protg.c (fgprocess_data): treat a duplicate RR as an RJ. + +Fri Jan 1 11:17:30 1993 Ian Lance Taylor (ian@comton.airs.com) + + * policy.h, unix/proctm.c: Steven S. Dick: use sysconf + (_SC_CLK_TCK) for TIMES_TICK if possible. + + * uuconf/diacod.c: Gregory Gulik: accept an empty dialcode string. + + * system.h, uucico.c (main), uucp.c (main), uux.c (main), + unix/run.c: Karsten Thygesen: removed ffork argument from + fsysdep_run. + +Wed Dec 30 00:21:55 1992 Ian Lance Taylor (ian@comton.airs.com) + + * unix/link.c: Andrey G Blochintsev: don't fail just because + destination directories do not exist. + + * send.c (flocal_send_open_file): Scott Ballantyne: record file + name when logging send of execution command. + + * protz.c: Chip Salzenberg: reformatted to 80 columns. + +Tue Dec 29 23:50:52 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuconv.c (uvwrite_time): scott@geom.umn.edu: handle midnight + more correctly. + +Fri Dec 18 00:49:16 1992 Ian Lance Taylor (ian@comton.airs.com) + + * system.h, uucp.c (uccopy), uux.c (main), cu.c (icuput, icutake), + unix/ufopen.c (esysdep_user_fopen): Doug Evans: open files used + for %put and %take using esysdep_user_fopen, rather than with + privileges of uucp. Added frd and fbinary arguments to + esysdep_user_fopen. + +Thu Dec 17 00:04:53 1992 Ian Lance Taylor (ian@comton.airs.com) + + * unix/picksb.c (zsysdep_uupick): Peter Wemm: allocation error. + + * uupick.c (main): Peter Wemm: pass INIT_GETCWD to + usysdep_initialize; really quit if 'q' is typed. + + * uulog.c (main): Peter Wemm: always canonicalize system name, not + just if using HDB_LOGGING. + + * uudefs.h, log.c (ustats), trans.c (ufailed), send.c + (fsend_await_confirm), rec.c (frec_file_end): Peter Wemm: added + fmaster argument to ustats, used only in HDB_LOGGING. + +Wed Dec 16 23:35:51 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uustat.c (main): Marc Unangst: forgot to call strtol for -y. + + * policy.h, sysh.unx: Brian J. Murrell: yet another configuration + parameter: HAVE_BROKEN_SETREUID. + +Tue Dec 15 00:13:04 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuconv.c (uvwrite_taylor_system): mnichols@pacesetter.com: use + command-path rather than path. + + * trans.c (floop): Marc Unangst: don't clear frequested_hangup if + we didn't manage to hang up. + + * uucp.h, rec.c (fremote_send_file_init): Oleg Girko: patches to + make code compile if USE_STDIO is 0. + + * unix/proctm.c: Tim Peiffer: reverse sense of TIMES_TICK check in + hopes of avoiding ISC preprocessor bug. + + * unix/fsusg.h, unix/fsusg.c, unix/bytfre.c, system.h, conf.h.in, + configure.in, unix/Makefile.in, unix/MANIFEST: use new disk space + checking routines from GNU fileutils 3.4. + * unix/opensr.c (zsysdep_receive_temp): don't check free space + here any more. + * policy.h, trans.h, trans.c, rec.c, uucico.c, uudefs.h: Chip + Salzenberg: check amount of remaining space on disk every + FREE_SPACE_DELTA bytes, and abort the file transfer if disk space + gets too low. + +Wed Dec 2 00:24:12 1992 Ian Lance Taylor (ian@comton.airs.com) + + * policy.h, unix/serial.c (fsserial_set): Frank Conrad: added + HAVE_PARITY_BUG parameter for the Sony NEWS. + +Mon Nov 30 00:06:59 1992 Ian Lance Taylor (ian@comton.airs.com) + + * lib/spool.c (fspool_file): Andrew Chernov: accept any + alphanumeric character in the name, because it could be a grade + from another system. + +Sun Nov 29 22:36:47 1992 Ian Lance Taylor (ian@comton.airs.com) + + * lib/buffer.c (ubuffree): scott@geom.umn.edu, Richard Gumpertz: + use a temporary variable to hold the offsetof result. + + * configure.in: scott@geom.umn.edu: define HAVE_SYS_SELECT_H + correctly. + + * protg.c (fgsend_control): Niels Baggesen: report all non-RR + packets if DEBUG_ABNORMAL. + + * unix/cusub.c (uscu_child): Ed Carp: apparently the read and + write calls can get EAGAIN on some systems. + + * unix/status.c (fsysdep_get_status, fsysdep_set_status): Chip + Salzenberg: map status values when using SPOOLDIR_HDB. + + * rec.c (fremote_send_reply): do file restart correctly for E + commands. + +Sun Nov 22 15:09:43 1992 Ian Lance Taylor (ian@comton.airs.com) + + * protz.c: Chip Salzenberg: always do bitwise operations on + unsigned values. + + * getopt.h: Chip Salzenberg: don't rely on __STDC__. + +Thu Nov 19 00:13:46 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuconf/freblk.c: Niels Baggesen: loop over the right list. + + * uulog.c (main): Peter Wemm: added -D, -F and -S options, made -f + take an argument and default to showing 10 current lines. + (ulusage): added new options and missing old ones. + +Wed Nov 18 22:26:36 1992 Ian Lance Taylor (ian@comton.airs.com) + + * rec.c (frec_file_end): Andrey G Blochintsev: call + fsysdep_remember_reception as soon as the file has been moved to + the final destination; write fake execution file via a temporary + file to prevent uuxqt from getting at it early. + * trans.c (usent_receive_ack): don't call + fsysdep_remember_reception here. + + * unix/tmpfil.c (ZDIGS): don't use '.', since we use it to + separate parts of the file name. + +Sun Nov 15 15:31:49 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uustat.c (fsquery_show, csunits_show): Marc Unangst, Chip + Salzenberg: line up uustat -q output. + + * sysh.unx, ftw.c (ftw_dir, ftw), srmdir.c (isremove_dir), walk.c + (iswalk_dir): Marc Unangst: stat argument to function argument to + ftw is const. + + * unix/serial.c (fsserial_set): Mike Bernson: set CSIZE correctly + when changing parity. + + * uux.c (main): Andrew A. Chernov: check for executions which name + the local system, to handle dumb mailers. + + * uucp.h: Doug Evans: #undef strerror if HAVE_STRERROR is 0, to + avoid macro definition on Xenix. + + * unix/serial.c (fsserial_set): Peter Wemm: only check CRTSCTS if + HAVE_POSIX_TERMIOS. + + * cu.c (main): Peter Wemm: use alternates for systems if a call + fails. + + * tstuu.c (uprepare_test): Gerben Wierda: set execute bits for + Chat1 and Chat2. + + * trans.c (floop): Marc Unangst: don't hang up when requested + unless the send queue is empty. + + * uuxqt.c (iqrequestor): Marc Boucher: new function to accept R + command with two arguments, as generated by UUPC. + + * uucico.c (faccept_call): Christian Seyb: don't free the system + info until after writing the status. + + * configure.in: Marc Boucher: check -lsocket and -lnsl together. + + * unix/portnm.c: Stephen J. Walick: it's types.tcp.h, not + tcp.types.h. + + * configure.in: Brian Campbell: check for /usr/bin/mailx. + +Sat Nov 14 11:11:04 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuconf/hlocnm.c (uuconf_hdb_login_localname): Christian Seyb: + check for _uuconf_unset as well as NULL. + + * conn.c (fconn_dial): initialize *ptdialerfound. + + * many files: rearranged header files to include "sysdep.h" before + system header files. Also eliminated various pedantic warnings, + and made _uuconf_unset char * to avoid possible alignment + problems. + +Tue Nov 10 00:16:35 1992 Ian Lance Taylor (ian@comton.airs.com) + + * trans.h, uucico.c (fcall, faccept_call), trans.c (uclear_queue, + floop): Stephen J. Walick: move clean up from end of floop into + uclear_queue, and call it instead of just doing + usysdep_get_work_free. + + * unix/serial.c (fsserial_lockfile): Marc Unangst: bad #endif + location for HAVE_SVR4_LOCKFILES. + (fsserial_init): Doug Evans: null terminate the device name. + +Sun Nov 8 10:58:59 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uucico.c (fcall, faccept_call): Stephen J. Walick: call + usysdep_get_work_free here. + trans.c (floop): don't call usysdep_get_work free here. + +Sun Nov 1 17:05:07 1992 Ian Lance Taylor (ian@comton.airs.com) + + * Released gamma version 1.04. + + * configure.in: check that sys/select.h and sys/time.h work + together, since that's how they are currently used. + + * cu.c, uustat.c, uuconf/diacod.c: add casts to eliminate + warnings. + + * configure.in: don't add strlwr to LIBOBJS. + + * policy.h, unix/cohtty.c: Bob Hemedinger: finish Coherent style + locking. + +Wed Oct 28 00:20:15 1992 Ian Lance Taylor (ian@comton.airs.com) + + * tstuu.c: Ralf Stephan: check HAVE_POLL_H and HAVE_STROPTS_H. + + * Nickolay Saukh: accept SVR4 style R request file position. + uudefs.h: added ipos field to struct scmd. + lib/parse.c: accept SVR4 style R request with file position to + start from. + send.c (fremote_rec_file_init): start transferring file from + requested position. + uucp.c, uux.c, uuxqt.c, xcmd.c: initialize ipos field. + +Sun Oct 25 10:39:23 1992 Ian Lance Taylor (ian@comton.airs.com) + + * unix/serial.c (fsysdep_conn_write, fsysdep_conn_io): T. William + Wells: take special care to ensure we don't write after SIGHUP. + + * policy.h, sysh.unx, unix/MANIFEST, unix/Makefile.in, + unix/serial.c (fsserial_lockfile), unix/cohtty.c (new file): Bob + Hemedinger: added HAVE_COHERENT_LOCKFILES. + + * unix/cusub.c (uscu_child): Igor V. Semenyuk: accept a 0 return + from read until we have read some data at some point. + +Thu Oct 22 10:38:32 1992 Ian Lance Taylor (ian@comton.airs.com) + + * proti.c: various tweaks for bad connections. + + * uucp.h: T. William Wells: rename strcasecmp and strncasecmp, if + the system doesn't provide them, to avoid the ANSI C name space. + + * lib/buffer.c: Bob Hemedinger: put ab in union so that offsetof + will not take the address of an array. + + * uuxqt.c (uqdo_xqt_file): Bob Hemedinger: don't take address of + array. + +Wed Oct 21 00:05:31 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uustat.c (fsnotify): Gert Doering: if the file appears to be + binary, don't include it in any mail message. + + * unix/mkdir.c: Michael Yu.Yaroslavtsev: check whether directory + already exists before spawning /bin/mkdir. + + * proti.c: Michael Yu.Yaroslavtsev: iIsendpos and iIrecpos should + be long. + + * send.c (flocal_send_await_reply): Gert Doering: improved error + messages. + + * tli.c, unix/detach.c: include "sysdep.h" before . + + * configure.in, conf.h.in: added some system specific checks + provided by autoconf. + + * tstuu.c, unix/serial.c: Merlyn LeRoy: check for ENODATA as well + as EAGAIN and EWOULDBLOCK. + + * uucico.c (faccept_call): Zacharias J. Beckman: if calling back, + clear status first. + + * uucico.c (fdo_call, faccept_call): Hans-Dieter Doll: avoid + overflow when turning ulimit value into bytes. + +Tue Oct 20 23:12:26 1992 Ian Lance Taylor (ian@comton.airs.com) + + * serial.c (fsmodem_carrier): Hans-Dieter Doll: use IS68K LNOMDM + bit if available. + + * chat.c (fcsend): Hans-Dieter Doll: advance z after EOT. + + * cu.c: T. William Wells: beep on connected and disconnected + messages (only if ANSI_C, to use \a). + + * unix/run.c: Peter Wemm: pass fsetuid as TRUE to ixsspawn. + +Sun Oct 18 13:58:17 1992 Ian Lance Taylor (ian@comton.airs.com) + + * policy.h, unix/serial.c (fsmodem_close): Stephen J. Walick: + added HAVE_RESET_BUG for SCO Xenix. + + * configure.in: Igor V. Semenyuk: avoid looking in -linet for + getline, since ISC has a different function there by that name. + + * unix/ufopen.c: Igor V. Semenyuk: handle unsigned uid_t. + +Sat Oct 17 11:00:30 1992 Ian Lance Taylor (ian@comton.airs.com) + + * conf.h.in, configure.in, uucp.h, unix/serial.c + (fsserial_lockfile), lib/MANIFEST: eliminated strlwr. + +Fri Oct 16 01:10:56 1992 Ian Lance Taylor (ian@comton.airs.com) + + * Igor V. Semenyuk: uuchk.c (ukshow): print max-remote-debug + correctly. + lib/debug.c (idebug_parse): accept DEBUG_NONE. + +Thu Oct 15 00:49:58 1992 Ian Lance Taylor (ian@comton.airs.com) + + * unix/cusub.c (fsysdep_terminal_puts): don't modify zalc before + freeing it up. + + * protg.c (fgcheck_errors, fggot_ack, fgprocess_data): Mark E. + Mallett: better handling of error decay. + +Wed Oct 14 22:09:20 1992 Ian Lance Taylor (ian@comton.airs.com) + + * unix/lock.c: Tomi Vainio: make sure SEEK_SET is defined. + + * tcp.c (ftcp_dial): print a better error message if gethostbyname + doesn't set errno. + + * Stephen J. Walick: configure.in, conf.h.in: check for + . + tcp.c, unix/opensr.c: include if available. + lib/debug.c, unix/portnm.c, uuconf/int.c, uuconf/llocnm.c, + uuconf/time.c: cast more arguments to eliminate more warnings. + +Tue Oct 13 00:25:03 1992 Ian Lance Taylor (ian@comton.airs.com) + + * prot.h, proti.c (fistart, fijstart), protj.c, uucico.c, tstuu.c + (uprepare_test), Makefile.in, MANIFEST: added 'j' protocol. + +Sun Oct 11 23:45:20 1992 Ian Lance Taylor (ian@comton.airs.com) + + * policy.h, unix/serial.c (fsserial_set): added HAVE_STRIP_BUG to + policy.h to get around stupid Ultrix bug. + + * sysh.unx, unix/cusub.c, unix/serial.c (fsserial_open): for + HAVE_BSD_TTY, keep tchars and ltchars in the sterminal structure, + and in fsserial_open disable all interrupt characters. + +Sat Oct 10 01:18:31 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuconf/tinit.c (itunknown): Gert Doering: don't save "unknown" + with the other arguments. + +Fri Oct 9 00:56:43 1992 Ian Lance Taylor (ian@comton.airs.com) + + * unix/lock.c: check for running process before doing kill. + +Thu Oct 8 00:20:12 1992 Ian Lance Taylor (ian@comton.airs.com) + + * chat.c, protf.c, send.c, rec.c, unix/locfil.c: Stephen J. + Walick: cast arguments to strtol and strcspn to avoid warnings. + + * uustat.c (fsnotify): Marc Boucher: don't free string from + uuconf_localname, and only prepend remote system name to execution + requests, not to local UUCP commands. + + * unix/lock.c (fsdo_lock): Marc Boucher: set fret to TRUE before + going around the loop again. + + * uucico.c: Marc Boucher: use 'a' protocol before 'g'. + + * unix/spool.c (zsfind_file): Matthias Zepf: fixed typos for + SPOOLDIR_BSD*. + +Wed Oct 7 00:03:08 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuname.c (main): Marc Boucher: reverse sense of -a, and do not + display aliases by default. + + * uucico.c (fdo_call): Marc Boucher: some systems only provide 14 + characters in the Shere line. + + * tstuu.c (main): Marc Boucher: add support for STREAMS ptys. + +Tue Oct 6 23:16:15 1992 Ian Lance Taylor (ian@comton.airs.com) + + * policy.h: Marc Boucher: improve comments to describe SVR4. + + * chat.c (fcsend, fcecho_send, fcecho_send_strip, + fcecho_send_nostrip): Marc Boucher: don't send CR after BREAK or + EOT, and let chat-seven-bit apply to echo checking. + + * uuname.c (main): Andreas Vogel: usysdep_exit (TRUE) rather than + usysdep_exit (EXIT_SUCCESS). + +Mon Oct 5 22:59:51 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sysh.unx, unix/serial.c (fsserial_init): Marc Boucher: avoid + freeing unallocated string. + + * unix/serial.c (fsmodem_carrier): Peter Wemm: eliminated useless + undeclared variable which only appeared if HAVE_CLOCAL_BUG. + + * cu.c (main): don't require carrier when opening a direct line. + (fcudo_cmd, fcudo_subcmd, uculist_fns, icuunrecogfn): T. William + Wells: give reasonable error messages. + +Sun Oct 4 00:03:10 1992 Ian Lance Taylor (ian@comton.airs.com) + + * */Makefile.in: T. William Wells: use ar qc rather than ar rc. + + * many: T. William Wells: renamed isysdep_* functions to + ixsysdep_*, and renamed isfork, isspawn, and isswait similarly, to + avoid ANSI C namespace restrictions. + + * uucp.h: T. William Wells: default size_t to unsigned, not int. + + * configure.in: T. William Wells: new definition for + AC_RETSIGTYPE. + + * configure.in: T. William Wells: test for sh builtin echo. + conf.h.in: default ECHO_PROGRAM to undefined. + + * proti.c (fiprocess_data, fiprocess_packet): fix confusion + between iIremote_winsize and iIrequest_winsize. + + * proti.c (fiwindow_wait, fisenddata): wait for a window opening + before sending SPOS. + + * proti.c (fiprocess_data): don't send a NAK for a duplicate of + the most recent packet. + + * configure.in: Stephen J. Walick: don't use AC_PREFIX, check for + /usr/bin/mail. + + * system.h, sysh.unx, send.c (flocal_send_file_init, + fsend_exec_file_init), rec.c (flocal_rec_file_init, + fremote_send_file_init, frec_file_end), xcmd.c + (fremote_xcmd_init), uuxqt.c (uqdo_xqt_file, uqcleanup), uux.c + (main, uxadd_send_file), uucp.c (main, uccopy), uustat.c + (fsworkfile_show, fsexecutions, fsnotify), unix/filnam.c + (zsfile_name, zsysdep_data_file_name, zsysdep_xqt_file_name), + unix/jobid.c (zsfile_to_jobid, zsjobid_to_file), unix/splcmd.c + (zsysdep_spool_commands), unix/splnam.c (zsysdep_spool_file_name), + spool.c (zsfind_file), statsb.c (fskill_or_rejuv, + isysdep_work_time), work.c (fswork_file, fsysdep_get_work, + zsysdep_jobid, bsgrade): Marc Unangst, Brian Murrell: Corrected + support for SPOOLDIR_SVR4, since SVR4 doesn't use grades in file + names. Changed flocal argument to pseq argument in + zsysdep_spool_file_name, and changed flocal argument to bgrade + argument in zfind_file. Added fxqt argument to + zsysdep_data_file_name. Added bsgrade function. Added bgrade + argument to zsfile_to_jobid, and pbgrade argument to + zsjobid_to_file. + +Sat Oct 3 11:03:13 1992 Ian Lance Taylor (ian@comton.airs.com) + + * MANIFEST, Makefile.in, lib/MANIFEST, lib/Makefile.in, + lib/parse.c: moved parse.c from main directory to lib. + + * system.h, unix/size.c, unix/Makefile.in, unix/MANIFEST: moved + csysdep_size into its own file, made it return -1 if the file does + not exist or -2 on other errors. + uustat.c (fsworkfile_show): handle errors from csysdep_size. + send.c (flocal_send_file_init): handle errors from csysdep_size, + removed unneeded calls to fsysdep_file_exists. + + * trans.c (flocal_poll_file), tcp.c (ftcp_dial): Bob Cunningham: + declare functions consistently static. + + * Makefile.in: Marc Unangst: don't run config.status + unnecessarily. + + * configure.in: Marc Unangst: check for socket and t_open in + -lsocket, -lnsl and -lxti. + + * uuconf/cmdarg.c: check first character to avoid calls to + strcmp or strcasecmp. + +Thu Oct 1 23:44:24 1992 Ian Lance Taylor (ian@comton.airs.com) + + * trans.h, uucico.c (fdo_call, faccept_call), parse.c + (fparse_cmd), send.c (flocal_send_request): Gert Doering: SVR4 + UUCP uses a dummy string between the notify field and the size, + for some reason. + + * tstuu.c (main, uprepare_test): added -n switch to not destroy + existing configuration files. + +Fri Sep 25 00:16:35 1992 Ian Lance Taylor (ian@comton.airs.com) + + * protg.c (fgsenddata): T. William Wells: clear bytes correctly so + that resending a packet doesn't get a completely incorrect size. + + * send.c (usadd_exec_line): Stephen J. Walick: don't send trailing + spaces on the created execute file, because it confuses Waffle. + +Thu Sep 24 00:25:18 1992 Ian Lance Taylor (ian@comton.airs.com) + + * unix/jobid.c (zsjobid_to_file): Franc,ois Pinard: if the job ID + is too short, return NULL rather than dumping core. + unix/statsb.c (fskill_or_rejuv, isysdep_work_time): handle a NULL + return from zsjobid_to_file. + +Mon Sep 21 09:01:02 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuconf/init.c, uuconf/syssub.c: Lele Gaifax: moved + declaration of _uuconf_unset from syssub.c to addstr.c because + NeXT linker does not pull in object files solely because of + variable declarations. + + * sysh.unx: Lele Gaifax: typo in ftw declaration. + + * lib/Makefile.in, unix/Makefile.in: Lele Gaifax: bug in clean + target. + +Thu Sep 17 01:01:13 1992 Ian Lance Taylor (ian@comton.airs.com) + + * Released beta version 1.04. + +Wed Sep 16 01:02:55 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uux.c (main): null terminate the options list for an 'E' + command. + + * ustat.c (fsexecutions): allow privileged users to kill remote + execution files, and handle local executions correctly. + + * uuconf/hinit.c: added parens to avoid warning. + + * unix/splcmd.c: cast to avoid warning. + + * unix/serial.c (fsmodem_close): fixed HAVE_SYSV_TERMIO typo. + + * trans.c (uqueue_receive, floop, fgot_data): improved timing code + to make fewer system calls. + + * send.c (fsend_exec_file_init, fsend_exec_file): handle separate + E file correctly, and make a good statistics file entry for it. + + * Makefile.in, unix/Makefile.in, uuconf/Makefile.in, + lib/Makefile.in: use -I flags to permit compilation in a separate + directory. Set up clean targets per GNU standards. + +Tue Sep 15 00:07:09 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uucico.c (zget_uucp_cmd): can't set size_t variable to -1. + + * Makefile.in (install): don't install info files. Added new + targets info and install-info. + +Mon Sep 14 13:19:42 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuxqt.c (main): Gregory Bond: canonicalize the system name given + by the -s argument. + + * system.h, uuconf.h, uucico.c (faccept_call), unix/unknwn.c, + unix/Makefile.in, unix/MANIFEST, uuconf/syshdr.unx, + uuconf/remunk.c, uuconf/hrmunk.c, uuconf/Makefile.in, + uuconf/MANIFEST: support HDB remote.unknown shell script. + + * protg.c (igchecksum, igchecksum2): Inspired by Mark Pizzolato, + put in new, improved checksum routines. + + * uuxqt.c (uqdo_xqt_file): make sure the execution file still + exists after locking it. + + * unix/lock.c (fsdo_lock): don't fail if the lock file is removed + between the link and the open. + + * unix/xqtsub.c (fsysdep_execute, fsysdep_lock_uuxqt_dir, + fsysdep_unlock_uuxqt_dir, fsysdep_move_uuxqt_files): use .Xqtdir + for first uuxqt execution, not .Xqtdir0000. + +Sun Sep 13 11:51:22 1992 Ian Lance Taylor (ian@comton.airs.com) + + * trans.h, uucico.c (fdo_call, faccept_call), send.c + (flocal_send_request), rec.c (flocal_rec_send_request) parse.c + (fparse_cmd): send file size in hex for SVR4 compatibility. + Required new FEATURE_V103 for 1.03 backward compatibility, since + 1.03 requires decimal size. + + * various: eliminated remaining calls to alloca. + + * tcp.c (ftcp_open), tli.c (ftli_open): set FD_CLOEXEC for sockets + and TLI descriptors. + + * tcp.c (ftcp_open): switch to real user ID before binding the + socket when running as a server. This will permit uucico invoked + by root to open privileged TCP ports. Don't switch to real ID if + effective ID is already root, to permit an suid root program to be + invoked by anybody. + + * uuxqt.c (uqdo_xqt_file): removed special case for system which + does not permit any commands: unnecessary and unusual. + + * uucico.c (fconn_call): Ed Carp: clear the SIGHUP signal + indication before opening the modem. + + * trans.h, trans.c (fqueue, fcheck_queue, floop, fgot_data), + send.c (fsend_await_confirm), rec.c (frec_file_send_confirm), + uucico.c (fcall, faccept_call): recheck the work queue every 10 + minutes. Honor CYM from the remote system. Send CYM if we have + something to do. + +Sat Sep 12 15:47:52 1992 Ian Lance Taylor (ian@comton.airs.com) + + * Makefile.in: use $(MAKE) instead of make for recursive calls. + + * system.h, uucp.c (main), uux.c (main), unix/ufopen.c, + unix/MANIFEST, unix/Makefile.in: added esysdep_user_open to open a + file with user permissions. + +Fri Sep 11 00:27:32 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uudefs.h, copy.c: added fcopy_open_file. + + * policy.h: added HAVE_SAVED_SETUID. + + * configure.in, conf.h.in: check for setreuid. + +Tue Sep 8 00:11:10 1992 Ian Lance Taylor (ian@comton.airs.com) + + * protf.c (ffsendcmd), prott.c (ftsendcmd): eliminate calls to + alloca. + + * uucico.c (main), uuxqt.c (main), uux.c (main), uucp.c (main), + uustat.c (main), uuchk.c (main), uuconv.c (main), uuname.c (main), + uulog.c (main), uupick.c (main), cu.c (main), lib/getop1.c, + lib/Makefile.in, lib/MANIFEST: added getopt_long, and changed all + calls to getopt to call getopt_long instead. + +Mon Sep 7 22:26:51 1992 Ian Lance Taylor (ian@comton.airs.com) + + * getopt.h, lib/getopt.c, lib/Makefile.in: bring getopt up to + glibc 1.04; call malloc instead of alloca in exchange. + + * system.h, uucico.c (main), uuxqt.c (main), uux.c (main), uucp.c + (main), uustat.c (main), cu.c (main), uuname.c (main), unix/init.c + (usysdep_initialize): added INIT_SUID, for old systems which don't + do setuid correctly for root. + + * cu.c, unix/cusub.c: various minor improvements. + +Sun Sep 6 20:25:20 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uux.c (uxcopy_stdin): use getchar rather than fread to avoid + SVR4 bug. + + * uucico.c (fsend_uucp_cmd): Niels Baggesen: report message when + DEBUG_HANDSHAKE. + + * protg.c (fgsend_control): Niels Baggesen: report sending an RJ + when DEBUG_ABNORMAL. + +Tue Aug 25 00:07:20 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuconf/time.c: Zacharias Beckman: let user defined time tables + override the defaults. + +Mon Aug 24 00:25:23 1992 Ian Lance Taylor (ian@comton.airs.com) + + * system.h, uuxqt.c (uqdo_xqt_file), unix/xqtsub.c + (zsysdep_xqt_local_file): Jarmo Raiha: expand ~name in uuxqt.c. + + * send.c (fremote_rec_reply): SVR4 sends the size of the file with + the RY string, so we do too. We don't look for it, though. + + * uustat.c, uustat.1: Don Phillips: removed all printing of years + and seconds. Hope nobody complains. + + * uucico.c (fdo_call): don't set the status to TALKING until we + see the Shere string. + + * configure.in, conf.h.in, unix/wldcrd.c: if the system has glob, + use it for wildcards. If it doesn't, quote special characters in + the wildcard string. + + * uucico.c (fdo_call): Zacharias Beckman: don't report ``Login + successful'' until we see the Shere string. + + * prot.c (fsend_data): Don Lewis: bug in crec calculation. + + * uustat.c (fsworkfile_show, usworkfile_header, fsnotify): Don + Lewis: show poll files. + + * unix/init.c: check LOGNAME and USER environment variables before + invoking getlogin. + + * unix/serial.c: Brian Campbell: check for B57600, B76800 and + B115200 in baud rate table. + +Sun Aug 23 13:05:28 1992 Ian Lance Taylor (ian@comton.airs.com) + + * chat.c (fcsend), tstuu.c (uchild): Chip Salzenberg: call sleep + (2) instead of sleep (1). Hopefully this won't break any chat + scripts. + + * system.h, parse.c, trans.c (fqueue, flocal_poll_file), uustat.c + (fsworkfiles_system, fsquery_system), unix/work.c + (fsysdep_get_work, fsysdep_get_work_init): don't delete poll files + immediately, but instead return a 'P' command and delete them when + the command is passed to fsysdep_did_work. + + * tstuu.c (uprepare_test): change ``call-request'' to ``request''. + + * uuconf/iniglb.c (_uuconf_itimetable): return CMDTABRET_KEEP so + we don't lose the timetable name and definition. + + * uuconf.h, send.c (fremote_rec_file_init), rec.c + (fremote_send_file_init), uuchk.c (ukshow), uuconv.c + (uvwrite_taylor_system, uvwrite_hdb_system), uuconf/tsinfo.c + (iirequest), uuconf/hsinfo.c, uuconf/hunk.c, uuconf/syssub.c: + added ``send-request'' and ``receive-request'' commands, + eliminated ``call-request'' and ``called-request'' commands. + + * uux.c (main): make sure we are permitted to transfer files + before queuing requests. + + * uuconf.h, uucico.c (fcall), uuconf/tsinfo.c, uuconf/syssub.c: + David Nugent: added ``success-wait'' command for systems, to set a + minimum time between successful calls. + + * send.c (fremote_rec_file_init): Don Phillips: let a request only + specify the file base name in the TO argument. + + * uucico.c (main): Don Lewis: don't exit with success just because + we were able to start uuxqt. + + * unix/serial.c (fsmodem_close, fsserial_read): always drop DTR + when closing a modem connection. Also, retry if we time out when + setting MIN. + +Sat Aug 22 22:31:34 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuconf/time.c: Stephen Walick: don't require a comma between + time strings, since HDB doesn't seem to. + + * protg.c (fgcheck_errors): added "error-decay" protocol parameter + to decay errors as packets are successfully received. + + * uustat.c (fsmachines), uustat.1: Chris Lewis: don't display the + year or seconds for uustat -m. Probably uustat -q should be + changed as well. + + * tstuu.c: Larry Fahnoe: don't report EWOULDBLOCK errors when + writing to a pty. Also removed functions which are now in lib. + + * MANIFEST, Makefile.in, uusched.in: added a simple uusched shell + script. + + * parse.c: Heiko Rupp: don't die if there is trailing garbage in + an 'R' command. + + * policy.h, system.h, sysh.unx, send.c, rec.c, uuxqt.c, uux.c, + unix/filnam.c, unix/init.c, unix/jobid.c, unix/splnam.c, + unix/spool.c, unix/statsb.c, unix/tmpfil.c, unix/work.c, + unix/xqtfil.c: Brian J. Murrell and Don Phillips: added + SPOOLDIR_SVR4. + +Thu Aug 20 00:06:32 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sysh.unx: Chiaki Ishikawa: some systems define some but not all + of the S_ file mode bits. + + * uuchk.c (ikshow_port): Chiaki Ishikawa: display lockname. + +Wed Aug 19 22:41:39 1992 Ian Lance Taylor (ian@comton.airs.com) + + * log.c (ustats): Scott Blachowicz: avoid overflow when reporting + bytes per second. + + * unix/lock.c (fsdo_lock): Chip Salzenberg: sometimes other + programs create lock files that uucp can't write. + + * trans.h, system.h, trans.c (floop, fgot_data, usent_receive_ack, + uwindow_acked), send.c (flocal_send_await_reply, + flocal_send_fail), rec.c (fremote_send_fail_send, + frec_file_send_confirm), prote.c, protf.c, protg.c, proti.c, + prott.c, protz.c (calls to fgot_data), unix/recep.c, + unix/MANIFEST, unix/Makefile.in: keep trace of whether we have + already received a file, in case the other side never sees our + ack. Added new SN8 rejection, meaning that the file has already + been received. + +Sat Aug 15 11:50:32 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuconf/time.c (itadd_span): Don Lewis: fixed bug if later span + overlapped two or more earlier spans. + +Thu Aug 13 00:19:50 1992 Ian Lance Taylor (ian@comton.airs.com) + + * system.h, rec.c (fremote_send_file_init, fremote_send_reply), + uucico.c (fdo_call, faccept_call), uucp.c (main), uux.c (main), + unix/opensr.c (zsysdep_receive_temp, esysdep_open_receive): + implemented file restart. + +Wed Aug 12 23:32:05 1992 Ian Lance Taylor (ian@comton.airs.com) + + * proti.c (fiprocess_data): ensure that the first argument to + fgot_data is always > 0 if the second argument is > 0. + +Mon Aug 10 22:43:40 1992 Ian Lance Taylor (ian@comton.airs.com) + + * trans.c (floop, ustats_failed): handle half-duplex connections + and failed calls correctly. + +Sun Aug 9 17:56:32 1992 Ian Lance Taylor (ian@comton.airs.com) + + * proti.c (firesend, fisenddata, ficheck_errors): made several + changes to improve performance on a lossy line: can now shrink + packet size using SYNC packets, avoids multiple bad header errors + in a sequence of INTRO characters, avoids letting one side lock up + if a NAK is lost. + + * configure.in: set HAVE_LONG_FILE_NAMES to 0 if + cross-configuring. + + * tstuu.c: changed -p option to be mod 1000, not mod 100. + + * MANIFEST, Makefile.in, prot.h, uucico.c, protz.c, trans.c: Doug + Evans: added Doug Evans's zmodem implementation as protocol 'a'. + +Wed Aug 5 22:28:14 1992 Ian Lance Taylor (ian@comton.airs.com) + + * policy.h, uuconf.h, uucico.c (fcall), uuconf/tsinfo.c, + uuconf/hsinfo.c, uuconf/syssub.c: added "max-retries" command for + systems, eliminated CMAXRETRIES configuration parameter, set + max_retries to 0 for HDB if retry time given, (from Chris Lewis) + call once a day even if max_retries has been exceeded. + + * prot.h, uucico.c (fdo_call, faccept_call), prott.c, prote.c, + proti.c, protg.c, protf.c: added pzlog argument to pfstart + protocol entry point, changed handshake successful message to + display it. + +Tue Aug 4 00:04:31 1992 Ian Lance Taylor (ian@comton.airs.com) + + * prot.h, uucico.c, protg.c (fbiggstart, cGshort_packets): Chip + Salzenberg: added support for 'G' protocol. Added "short-packets" + protocol parameter for 'g' and 'G' protocols. + + * uuconf.h, rec.c (flocal_rec_file_init), uucp.c, uux.c, uuxqt.c, + uuchk.c, uuconv.c, uuconf/local.c, uuconf/tsinfo.c, + uuconf/syssub.c: support UUCP forwarding. Added "forward-from", + "forward-to", and "forward" commands for systems. + + * unix/spawn.c: don't close the file descriptor after dupping it. + +Sun Aug 2 23:04:18 1992 Ian Lance Taylor (ian@comton.airs.com) + + * trans.c (fremote_hangup_reply): don't hangup if a file transfer + is in progress. + + * send.c (flocal_send_cancelled): don't pass a NULL buffer to + pfsenddata. + +Sun Jul 26 13:28:27 1992 Ian Lance Taylor (ian@comton.airs.com) + + * unix/work.c (fsysdep_get_work_init): return TRUE if there is no + work directory. + + * configure.in, sysh.unx: don't run any programs in configure if + we are cross-configuring; this applies to HAVE_FTIME and + HAVE_RESTARTABLE_SYSCALLS. The code can cope with the buggy + ftime. If we are cross-configuring, HAVE_RESTARTABLE_SYSCALLS is + set to -1, and sysh.unx guesses that if the system has sigvec but + not sigaction or SV_INTERRUPT it is on 4.2BSD and system calls are + automatically restarted. + + * configure.in, conf.h.in, tstuu.c, unix/serial.c: removed + COMBINED_UNBLOCK configuration parameter, and changed the code + which sets O_NONBLOCK and O_NDELAY to drop back to using just + O_NONBLOCK if it gets an EINVAL error. + + * configure.in, conf.h.in, uucp.h, protg.c (fgsenddata), cu.c + (icutake), chat.c (icexpect), lib/MANIFEST: removed all calls to + memmove, avoiding the SCO bug and making the 'g' protocol slightly + more efficient. + +Sat Jul 25 14:20:30 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uucp.h, uudefs.h, many other files: broke part of uucp.h out + into uudefs.h, stopped including uuconf.h in uucp.h, fixed up .c + files to include uudefs.h and uuconf.h as necessary. + + * uuconf/syshdr.unx, uuconf/callin.c, uuconf/diacod.c + uuconf/hdial.c, uuconf/hdnams.c, uuconf/hport.c, uuconf/hsinfo.c, + uuconf/hsnams.c uuconf/rdlocs.c, uuconf/tcalou.c, uuconf/tdial.c, + uuconf/tdnams.c, uuconf/tport.c, uuconf/vport.c, uuconf/vsinfo.c, + uuconf/vsnams.c: changed uuconf library to not return an error if + a configuration file does not exist; it now acts as though + whatever it is is not found. + + * tstuu.c (main): use perror if execl fails. + + * configure.in, conf.h.in, uucp.h, uuconf.h, sysh.unx, conn.h, + MANIFEST, Makefile.in, tli.c, chat.c (ccescape), conn.c + (fconn_init), tcp.c, uucico.c (faccept_call), uuconv.c, uuchk.c, + lib/MANIFEST, lib/Makefile.in, lib/escape.c, unix/cusub.c, + unix/serial.c, uuconf/hport.c, uuconf/tportc.c: added support for + TLI connections. Moved ccescape from chat.c to cescape in + lib/escape.c. Made all connections on Unix use the same + system dependent structure. + +Tue Jul 21 22:08:10 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uucp.h, trans.h, uucico.c (fdo_call, faccept_call), uuxqt.c + (uqdo_xqt_file), uucp.c (main), uux.c (main), uustat.c + (fsworkfile_show), parse.c (fparse_cmd), trans.c (fqueue, + fgot_data, ftadd_cmd), send.c, rec.c, xcmd.c, protf.c + (ffprocess_data), proti.c (fiprocess_data), tstuu.c + (uprepare_tests), unix/splcmd.c (zsysdep_spool_commands), + unix/statsb.c (fskill_or_rejuv), unix/work.c (fsysdep_get_work): + added E request to send file executions which only require reading + from standard input. + +Sat Jul 18 20:22:50 1992 Ian Lance Taylor (ian@comton.airs.com) + + * proti.c, Makefile.in, MANIFEST, prot.h, system.h, trans.h, + uucico.c, prote.c, protf.c, protg.c, prott.c, trans.c, send.c, + rec.c, xcmd.c, unix/opensr.c: added 'i' protocol. Added local and + remote channel arguments to protocol sendcmd and senddata entry + points. Cleaned up send and receive state machines. Removed + pfgone argument from esysdep_open_send. + +Fri Jul 17 09:41:05 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuxqt.c (uqdo_xqt_file): only report base name of execution + file, not full name. + +Thu Jul 16 00:45:06 1992 Ian Lance Taylor (ian@comton.airs.com) + + * lib/crc.c: unroll the loop a bit. + + * configure.in, conf.h.in, unix/init.c: updated to autoconf 0.120. + +Wed Jul 15 14:45:32 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuconf.h, uuconv.c, uuconf/uucnfi.h, uuconf/reliab.c, + uuconf/tportc.c, uuconf/tdialc.c, uuconf/diasub.c, uuconf/hport.c, + uuconf/prtsub.c, uuconf/vsinfo.c: added UUCONF_RELIABLE_FULLDUPLEX + and "half-duplex" command for ports and dialers. + +Mon Jul 13 16:53:04 1992 Ian Lance Taylor (ian@comton.airs.com) + + * prot.h, lib/crc.c, lib/Makefile.in, lib/MANIFEST: added icrc + function to compute 32 bit CRC (from Gary S. Brown, via Doug + Evans). + +Sun Jul 12 21:40:15 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuconv.c (uvwrite_time): Chris Lewis: don't output two commas in + a row. + + * uuconv.c (uvwrite_taylor_system, uvwrite_taylor_port): Chris + Lewis: generate command "protocol", not "protocols". + +Sat Jul 11 17:09:09 1992 Ian Lance Taylor (ian@comton.airs.com) + + * xcmd.c (fremote_xcmd_init): Chris Lewis: use qdaemon->puuconf, + since puuconf is not defined. + + * uuconf/syshdr.unx, uuconf/hinit.c (uuconf_hdb_init): Chris + Lewis: added HDB_SEPARATOR to insert between oldconfiglib and + strings in HDB Sysfiles. + + * uuconf/syshdr.unx: Chris Lewis: define strerror as a macro. + + * uuconf/freblk.c, uuconf/free.c: Chris Lewis: don't define as + void when ! UUCONF_ANSI_C. + +Thu Jul 9 09:17:55 1992 Ian Lance Taylor (ian@comton.airs.com) + + * prot.h, uucico.c (fdo_call, faccept_call), prote.c (festart), + protf.c (ffstart), protg.c (fgstart), prott.c (ftstart): no need + to pass fmaster as a separate argument to protocol start routine. + + * protf.c (ffawait_ack, ffawait_cksum): don't try to resend if we + don't have a file. + +Wed Jul 8 14:28:23 1992 Ian Lance Taylor (ian@comton.airs.com) + + * unix/srmdir.c (fsysdep_rmdir), unix/walk.c (usysdep_walk_tree): + cast to char * to avoid warning. + + * cu.c (main): don't compare boolean to NULL. + + * unix/serial.c (isblocksigs), unix/signal.c (usset_signal): use + extra parens to avoid bug in SCO 3.2.2 sys/signal.h header file. + + * sysh.unx: always define struct ssysdep_tcp, for the benefit of + systems for which HAVE_TCP is 0. + + * MANIFEST, Makefile.in, unix/Makefile.in, uuconf/Makefile.in, + lib/Makefile.in: updated automatic distribution code for multiple + directories. + + * unix/cusub.c, unix/serial.c: don't clobber CR when using TERMIO + or TERMIOS, and default MIN to 1 to the convenience of cu. + + * Makefile.in, uucp.h, system.h, prot.h, trans.h, uucico.c, + trans.c, send.c, rec.c, xcmd.c, prot.c, protg.c, protf.c, prote.c, + prott.c, log.c, file.c, unix/opensr.c, unix/work.c: rewrote file + transfer internals to support bidirectional transfers. Keep queue + of jobs to do, and support connections. Added new files trans.h, + trans.c, send.c, rec.c, xcmd.c, and removed old file file.c. + +Mon Jun 29 15:14:15 1992 Ian Lance Taylor (ian@comton.airs.com) + + * Makefile.in: Stephen J. Walick: copy uustat.1 to + uustat.$(manext), not uucp.($manext). Also try to create + $(infodir). + + * chat.c (fcsend, fcprogram): check for NULL return from + uuconf_callout. + +Thu Jun 18 22:37:28 1992 Ian Lance Taylor (ian@comton.airs.com) + + * configure.in, Makefile.in: updated to autoconf 0.118. + +Wed Jun 17 14:22:11 1992 Ian Lance Taylor (ian@comton.airs.com) + + * unix/serial.c (fsserial_init): add /dev if necessary to device + as well as to port name. + + * cu.c (main): set zdevice to NULL when faking line. + + * cu.c (main), uucp.c (main), uux.c (main), uuxqt.c (main): don't + call zsysdep_localname until we've called usysdep_initialize. + +Tue Jun 16 17:42:50 1992 Ian Lance Taylor (ian@comton.airs.com) + + * unix/signal.c (usset_signal): set SA_INTERRUPT to force system + calls to be interrupted on SunOS. + +Mon Jun 15 15:10:24 1992 Ian Lance Taylor (ian@comton.airs.com) + + * everything: integrated uuconf library. Split out lib and unix + libraries. Made many changes, including defaults for port and + dialer files, better handling of changed local name, better + handling of HDB Permissions, new zbufalc routines to manage + strings on the heap. Incorporated uuconv. + +Wed Jun 10 23:51:03 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuconf.h, uuconf/Makefile.in, uuconf/locnm.c, uuconf/llocnm.c, + uuconf/hlocnm.c, uuconf/tlocnm.c: renamed uuconf_localname to + uuconf_login_localname and added new uuconf_localname which + doesn't need to read system information. + +Tue Jun 9 14:19:20 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuconf.h, uuconf/Makefile.in, uuconf/local.c: wrote + uuconf_system_local. + +Mon Jun 8 14:14:30 1992 Ian Lance Taylor (ian@comton.airs.com) + + * policy.h: changed description of LOCKDIR, which now need not + always be defined. + + * uuconf.h, uuconf/uucnfi.h, uuconf/lckdir.c, uuconf/iniglb.c, + uuconf/tinit.c, uuconf/Makefile.in: added uuconf_lockdir, and + ``lockdir'' command to config. + +Sat Jun 6 22:07:58 1992 Ian Lance Taylor (ian@comton.airs.com) + + * configure.in: updated to autoconf 0.115, added code to set + LIBOBJS. + + * uuconf/Makefile.in, uuconf/uucnfi.h: removed references to + routines now in lib/, changed to include regular UUCP header + files. + +Fri Jun 5 15:31:29 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuconf.h, uuconf/uucnfi.h, uuconf/syssub.c, uuconf/uuconv.c: + always set zpubdir for every system, changed uuconf_zpubdir to + const char *. + +Wed Jun 3 15:15:32 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuconf.h, uuconf/Makefile.in, uuconf/deblev.c, uuconf/maxuxq.c, + uuconf/pubdir.c, uuconf/spool.c: wrote uuconf_debuglevel, + uuconf_maxuuxqts, uuconf_pubdir, uuconf_spooldir. + + * configure.in: updated to autoconf 0.114. + + * uuconf/tportc.c: default TCP ports to being fully reliable. + +Mon Jun 1 17:03:22 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuconf.h, uuconf/prtsub.c: removed uuconf_psysdep from + uuconf_port. + +Sun May 31 00:07:40 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuconf.h, uuconf/Makefile.in, uuconf/diacod.c: wrote + uuconf_dialcode. + + * uuconf.h, uuconf/Makefile.in, uuconf/logfil.c, uuconf/debfil.c, + uuconf/stafil.c: wrote uuconf_logfile, uuconf_debugfile, + uuconf_statsfile. + + * uuconf.h, uuconf/Makefile.in, uuconf/callin.c: wrote + uuconf_callin. + + * uuconf/chatc.c, uuconf/time.c: Jean Mehat: only call tolower if + isupper is true. + + * uuconf.h, uuconf/Makefile.in, uuconf/val.c, uuconf/tval.c: wrote + uuconf_validate, uuconf_taylor_validate. + +Sat May 30 12:37:02 1992 Ian Lance Taylor (ian@comton.airs.com) + + * system.h, sys1.unx: changed zsysdep_local_name to + zsysdep_localname, and made it fatal out rather than return NULL. + + * uuconf.h, uuconf/Makefile.in, uuconf/uucnfi.h, uuconf/iniglb.c, + uuconf/rdlocs.c, uuconf/locnm.c, uuconf/tlocnm.c, uuconf/hlocnm.c: + wrote uuconf_localname, uuconf_taylor_localname, + uuconf_hdb_localname. + + * uuconf.h, uuconf/Makefile.in, uuconf/uucnfi.h, uuconf/iniglb.c, + uuconf/tinit.c, uuconf/tsinfo.c, uuconf/hunk.c, uuconf/unk.c: + wrote uuconf_system_unknown, uuconf_hdb_system_unknown, + uuconf_taylor_system_unknown. + + * log.c, time.c: always include in uucp.h. + + * configure.in, conf.h.in: check for size_t, renamed checks for + time_t. + + * configure.in, conf.h.in: check for . + +Fri May 29 00:03:05 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sysinf.c (ztranslate_system): Jac Kersing: must xstrdup the + argument, since it points to a buffer that will be reused. + +Thu May 28 12:42:20 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sys3.unx (zsysdep_real_file_name): Ted Lindgreen: check return + value of zstilde_expand. + + * copy.c, sys1.unx (usysdep_detach), sys2.unx (fsserial_close), + sys3.unx, sys5.unx, sys7.unx: opening /dev/tty in usysdep_detach + confuses the NeXT, so instead we just call TIOCNOTTY on 0. In + fsserial_close we call TIOCNOTTY on the port before closing it, to + make sure that we have ditched it under BSD. Also added O_NOCTTY + to every open call other than opening a port, although there are + still several fopen calls which should probably have it somehow. + + * system.h, uucico.c (fcall), uustat.c (fsquery_system, + fsquery_show), sys3.unx (fsysdep_get_status), sys7.unx + (zsysdep_all_status): Bob Izenberg: changed output of uustat -q to + count number of commands rather than number of files being + transferred, and to not report a non-existent status. Added + pfnone argument to fsysdep_get_status, and changed all calls. + + * uucico.c, sys1.unx, sys2.unx, sys3.unx, sys5.unx, sys6.unx, + sys7.unx: Rolf Nerstheimer: cast a bunch of arguments to open, + creat, stat and chmod to avoid compiler warnings. + + * uucp.h, log.c (ulog), port.c (fport_close), prot.c (fgetcmd): + Chip Salzenberg: don't log a SIGHUP signal while we're closing + down the connection, since the other side might hang up faster + than we do (we still react to it correctly, we just don't put it + in the log file). + + * sys1.unx (usysdep_detach), tcp.c (ftcp_open): Petri Helenius: + update the process ID we log after a fork. + + * Makefile.in, sys1.unx: Chip Salzenberg: changed LIBDIR to + SBINDIR. + + * uucp.c (main, uccopy): Andreas Vogel: check local-receive of the + correct system, rather than always using sLocalsys. + + * configure.in, conf.h.in, sys2.unx, tstuu.c: Rob Janssen: look + for , and include it if it exists and we are using + select. + + * protg.c: Rob Janssen: rearrange macros to avoid bug in XENIX + compiler. + + * configure.in: Scott Blachowicz: check WIFEXITED before assuming + HAVE_UNION_WAIT, to avoid problems on HP/UX. + + * configure.in, conf.h.in, sysh.unx, sys1.unx: John Theus: use + sv_onstack instead of sv_flags in the sigvec structure on 4.2BSD. + +Wed May 27 23:23:39 1992 Ian Lance Taylor (ian@comton.airs.com) + + * policy.h, sys2.unx (fsysdep_modem_no_carrier): Scott Reynolds: + added HAVE_CLOCAL_BUG compilation parameter to work around + problems on some serial ports. + +Tue May 26 15:50:17 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uustat.c, uustat.1: added a bunch of options to support uuclean: + -e, -i, -K, -M, -N, -W, -Q. + + * system.h, sys7.unx (fsysdep_privileged, fskill_or_rejuv): added + fsysdep_privileged function. + +Thu May 21 13:30:21 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuxqt.c (uqdo_xqt_file): processing of execution file has to be + case significant; this will change handling of "n" flag, which was + not correctly handled before. + +Wed May 20 14:22:12 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sys1.unx (usysdep_detach): close the statistics file when + detaching. + + * policy.h, sys3.unx (fsdo_lock, fsdo_unlock), sys7.unx + (fsysdep_lock_status): force LOCKDIR to always be defined. + + * uucp.h: put in an extern for alloca. + + * sysh.unx, sys1.unx, sys5.unx, sys6.unx: defined all the ?_OK + macros in sysh.unx, which means that must be included + before "sysdep.h" when they are both included. + + * sys2.unx (fsserial_set): corrected case in termio switch + expression. + + * chat.c (fcsend): simplified expression for old compilers. + + * sys1.unx (rmdir): wrote rmdir replacement which invokes + /bin/rmdir for old systems. + + * configure.in, conf.h.in, Makefile.in: updated for autoconf + 0.112, added checks for ftw, ftw.h, and rmdir. + + * sys1.unx: added extern for ctime, removed externs for functions + returning int, protected externs with ifndefs. + + * uucp.h, prot.h, system.h, uucico.c (fuucp), uuxqt.c + (uqdo_xqt_file), prot.c (freceive_file), file.c (freceived_file), + sys3.unx (fsysdep_move_file, fsysdep_change_mode), sys4.unx + (zsysdep_save_temp_file): changed fsysdep_move_file to not set the + file mode, and added fsysdep_change_mode to do it instead. + + * system.h, uucp.c (main, ucdirfile, uccopy), sys6.unx + (usysdep_walk_tree, isdir, ftw, do_ftw): added -R option to uucp + to recursively copy directories. + +Tue May 19 18:29:32 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sys3.unx: changed zsysdep_in_dir to always append the filename + to the directory, even if the directory did not already exist. + + * sysh.unx, sys1.unx, sys3.unx, sys4.unx, sys5.unx: renamed + fsdirectory_exists to fsysdep_directory. + +Mon May 18 14:49:35 1992 Ian Lance Taylor (ian@comton.airs.com) + + * system.h, uucp.c (main), sys6.unx (zsysdep_uuto): added -t + option to uucp to emulate uuto, wrote zsysdep_uuto to do Unix + dependent destination translation for uuto, added -p option to + uucp as synonym for -C for uuto compatibility. + +Sun May 17 22:04:09 1992 Ian Lance Taylor (ian@comton.airs.com) + + * protg.c (fgexchange_init): permit a second INITB to override the + segment size given in the first INITB. + +Tue May 5 16:03:22 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uucico.c (main, fdo_call), uucico.8: Chip Salzenberg: added -c + option to uucico to not warn if invoked when the system may not be + called. + +Tue Apr 28 15:05:01 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sysh.unx, sys2.unx (fsserial_open, fsblock): preserve file + status flags. + + * protg.c (fgwait_for_packet): Heiko Rupp: only send RJ packet if + there are no unacknowledged packets. + +Mon Apr 27 18:56:42 1992 Ian Lance Taylor (ian@comton.airs.com) + + * system.h: added several routines for cu. + + * cu.c, cu.h, sys8.unx: checked into RCS. + + * uux.c (main): Jose Manas: dumb bug when checking against + calloc_args. + +Fri Apr 24 20:32:06 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sys1.unx: changed HAVE_LONG_NAMES to HAVE_LONG_FILENAMES for new + version of autoconf. + + * sys7.unx: check UTIME_NULL_MISSING with #if rather than #ifdef. + + * sys3.unx: check FS_* macros with #if rather than #ifdef. + + * uucp.h, sysh.unx: changed standard type definitions for new + version of autoconf. + + * sysh.unx, sys1.unx, sys2.unx, tstuu.c: changed SIGtype to + RETSIGTYPE for new version of autoconf. + + * sys1.unx, tstuu.c: make include of optional. + + * sys2.unx: get the right versions of major and minor. + +Wed Apr 22 11:19:11 1992 Ian Lance Taylor (ian@comton.airs.com) + + * protg.c (fgsenddata, fggot_ack): Michael Haberler: the slow + start after error code was essentially shrinking the window size. + + * sysh.unx, system.h, sys1.unx (usysdep_initialize), uuchk.c, + uucico.c, uucp.c, uulog.c, uuname.c, uustat.c, uux.c, uuxqt.c: + changed usysdep_initialize to take a single argument with bit + flags, added INIT_NOCHDIR as one of the flags. + + * uucp.h, log.c (ulog): added pfLstart and pfLend functions for + ulog, so that cu can use them to restore the terminal settings. + + * bnu.c (ubnu_read_systems, fbnu_read_dialer_info), v2.c + (uv2_read_systems): Michael Richardson: don't core dump if no chat + script. + +Tue Apr 21 00:19:47 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uucico.c (faccept_call): Chris Lewis: a successful call in + should clear the number of retries. + + * sys2.unx (fsserial_set): set LLITOUT if going to CBREAK mode. + + * port.h, prote.c (festart), protf.c (ffstart), protg.c (fgstart), + prott.c (ftstart), port.c (fport_set), sys2.unx + (fsysdep_stdin_set, fsysdep_modem_set, fsysdep_direct_set, + fsserial_set): gave fport_set independent control over output + parity generation, input parity checking, and XON/XOFF + handshaking, all to support cu. + +Mon Apr 20 11:47:23 1992 Ian Lance Taylor (ian@comton.airs.com) + + * port.h, uucico.c (fdo_call), port.c (fport_dial, fmodem_dial), + tcp.c (ftcp_dial): added separate zphone argument to fport_dial to + support cu. + +Thu Apr 16 01:15:42 1992 Ian Lance Taylor (ian@comton.airs.com) + + * bnu.c (ubadd_perm, ubadd_perm_alternate): Chris Lewis: handle a + combination of Permissions entries which specify just LOGNAME with + entries that specify both MACHINE and LOGNAME. + +Wed Apr 15 16:11:48 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sys1.unx (usysdep_initialize, zsysdep_login_name): John Theus: + don't die if can't get login name, unless it's really needed. + +Tue Apr 14 12:39:18 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uucico.c (main, fcall): Petri Helenius: must relock system after + detaching from terminal when trying different alternates. + + * system.h, uucico.c (fuucp), uustat.c (fsworkfiles_system, + fsquery_system), sys4.unx (fsysdep_get_work_init, + fsysdep_get_work): Marty Shannon: uustat would remove empty + command files. + + * bnu.c (ubadd_perm_alternate): John Harkin: permit ALIAS in + Permissions. + + * Makefile.in: John Harkin: add sys?.c dependencies to sys?.o to + work around old makes which don't handle transitive .SUFFIXES. + + * sys2.unx: cast some function calls to void. + + * time.c (qttime_parse): cast to void warning. + + * sys1.unx (iswait): cast waitpid argument to avoid warning. + + * configure.in, policy.h, uucp.h, sys7.unx, tstuu.c: Zacharias + Beckman: minor touchups for NeXT. + + * sys1.unx (usysdep_initialize), sys6.unx (zsysdep_add_cwd), uux.c + (main): Jarmo Raiha: heuristic for whether to get the current + directory can fail. + + * sys1.unx: pass argument to uudir, cast sigemptyset calls to + void. + + * uucp.texi: Harlan Stenn: correct case of references. + +Tue Apr 7 01:02:17 1992 Ian Lance Taylor (ian@comton.airs.com) + + * Released version 1.03. + +Mon Apr 6 15:49:08 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uucico.c (faccept_call): Marc Boucher: set *pqsys to NULL. + + * bnu.c (ubnu_read_systems, fbnu_find_port): Erik Forsberg: + support multiple character modem classes. + +Fri Apr 3 00:37:25 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sys2.unx: Petri Helenius: only clear known bits in termio or + termios structure; didn't change HAVE_BSD_TTY handling--maybe next + version. + + * configure.in: test TIMES_DECLARATION_OK correctly. + + * Makefile.in: update version to 1.03, remove distclean, add + mostlyclean per GNU standards. + + * sys1.unx, chat.c: minor cleanups for gcc 2.1. + +Thu Apr 2 17:51:36 1992 Ian Lance Taylor (ian@comton.airs.com) + + * tstuu.c: conditionally declare times. + + * uucp.h, prot.c, sysinf.c, prtinf.c: added gcc 2.0 format + checking to ulog, and fixed a few problems it discovered. + +Wed Apr 1 16:21:08 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sys3.unx (esysdep_open_receive): David J. MacKenzie: some + USG_STATFS systems use 512 as the block size of f_bfree, despite + the existence of f_bsize. + + * port.c (fport_open): initialize stdin port. + + * policy.h, log.c: added CLOSE_LOGFILES configuration parameter. + + * sys2.unx: T. William Wells: handle a system without or + . + + * configure.in: Franc,ois Pinard: warn if none of napms, nap, + usleep, poll or select are available, since \p will sleep for a + full second. + + * Makefile.in: Gerben Wierda: fixed uninstall to set file owner + and mode correctly. Also changed install to handle uucp.info-4 + and uustat.1. + + * MANIFEST: added uucp.info-4 and uustat.1. + + * uustat.1: Wrote. + + * uucico.8, uuxqt.8, uucp.1, uux.1: updated -x switch, cleaned up + a bit. + +Tue Mar 31 14:40:06 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sys1.unx (usysdep_initialize): use $PWD to get the current + working directory if it's defined and correct. + + * sys1.unx (usysdep_initialize): Brian Antoine: use name from + getpwname rather than getlogin. + + * uucp.texi: David J. MacKenzie: put in a number of corrections. + Also split sys file and config file nodes, and rearranged several + nodes. + + * protg.c (fgsenddata): Niels Baggesen: packet to retransmit did + not get reset correctly. + +Mon Mar 30 10:03:28 1992 Ian Lance Taylor (ian@comton.airs.com) + + * tcp.c (ftcp_reset): Petri Helenius: TCP server never started + uuxqt, because it exited in ftcp_reset. + + * policy.h, sysh.unx, sys2.unx (fsserial_lockfile): added + HAVE_SVR4_LOCKFILES configuration parameter. + + * sys3.unx (esysdep_open_receive): Niels Baggesen: USG statfs has + an f_bsize field. + +Sun Mar 29 23:04:20 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uucp.h, sysinf.c, prot.c, prote.c, protf.c, protg.c, prott.c: + Niels Baggesen: added new debugging types abnormal and uucp-proto. + + * uucico.c (fuucp), prot.c (freceive_file), file.c + (fstore_recfile): Dirk Musstopf: if a file receive fails before it + starts, perhaps because the file was too large, remember to remove + the temporary file. + + * sys2.unx (fsserial_lock, fsserial_open, fsserial_write, + fsserial_io): always block and unblock the read and write + descriptors together. + +Sat Mar 28 14:40:50 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uustat.c: allow multiple systems and users to be specified at + once; likewise for kills and rejuvenates. Allow old and young to + be combined with systems and users. As suggested by Niels + Baggesen, make machine status output more columnar. + + * uucp.h, uucico.c, config.c, sys3.unx: Michael I Bushnell: + renamed enum tstatus to tstatus_type to avoid conflict with + on some systems. + + * config.c, sysinf.c, prtinf.c, chat.c: David J. MacKenzie: allow + backslash newline quoting in all TAYLOR_CONFIG configuration + files. + + * chat.c (fchat): David J. MacKenzie: handle empty subexpect + strings correctly. + + * uucico.c (main, fcall): Petri Helenius: must dump controlling + terminal before going to next alternate. Also fixed David J. + MacKenzie bug in which a signal did not prevent the next + alternates from being tried. Also made sure qtime was always + freed up. + + * uucp.h, uucico.c (fdo_call), sysinf.c (tialternate), uuchk.c + (ukshow): Franc,ois Pinard: allow a name to be given to an + alternate, and display the name when placing a call. + + * chat.c (fcprogram), port.c (fport_open, fport_close): David J. + MacKenzie: send port device rather than port name to a chat + program using \Y; make sure port device is reset if port open + fails and when port is closed. + + * uucico.c (fuucp), log.c (ulog, ustats, ustats_close): close log + and statistics file every time master and slave switch roles. + +Fri Mar 27 00:31:23 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uucico.c (fdo_call): Mark Mallett: minor cleanup. + + * uuname.c (main): Franc,ois Pinard: output aliases, added -a + switch. + + * uucico.8, uuxqt.8, uux.1, uucp.1: David J. MacKenzie: changed + .TP5 to .TP 5; also updated to 1.03. + + * tstuu.c: Roberto Biancardi: if SIGCHLD is not defined, define it + as SIGCLD. + + * config.c: David J. MacKenzie: cMaxuuxqts is independent of + HAVE_TAYLOR_CONFIG. + + * uucp.h: Gerben Wierda: don't always declare bzero. + + * sys7.unx (ussettime, fsysdep_lock_status): Niels Baggesen, + Gerben Wierda: minor patches. + + * sys2.unx: Gerben Wierda: minor cleanups. + + * uucp.h: Niels Baggesen: simplified debugging message macros to + avoid broken compilers. + + * sys2.unx: don't use TIOCEXCL locking. + + * sys2.unx: rework HAVE_UNBLOCKED_WRITES == 0 to work even if + writes are unblocked. Correct initialization of fwrite_blocking. + + * Makefile.in, configure.in: David J. MacKenzie: various cleanups. + Changed default newconfigdir definition. Supported compilation in + a different directory. Used symbolic links if available. Changed + default infordir definition per Franc,ois Pinard. + + * policy.h: David J. MacKenzie: various cleanups. + +Thu Mar 26 12:17:41 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sys3.unx: reduced race condition in fsdo_lock. + + * sys1.unx: Gerben Wierda: various cleanups. Also don't set + sa_flags to SV_INTERRUPT per Chip Salzenberg. + +Wed Mar 25 22:20:24 1992 Ian Lance Taylor (ian@comton.airs.com) + + * configure.in: Overhauled for readability and functionality as + suggested by T. William Wells and others. Added bug checks, + including for SCO memmove and ftime. + +Tue Mar 24 12:18:56 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sysinf.c (uiread_systems): fixed handling of alternates in + file-wide defaults. + +Wed Mar 18 01:01:25 1992 Ian Lance Taylor (ian@comton.airs.com) + + * config.c (tprocess_one_cmd): handle CMDTABTYPE_FULLSTRING + correctly if there are no arguments. + + * Released beta version 1.03 + + * sys1.unx (usysdep_detach): open the controlling terminal in non + delay mode since it might be a modem. + +Tue Mar 17 00:01:53 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uucico.c (fdo_call, faccept_call): T. William Wells: set current + time in status file when call completes. + + * sys1.unx (iswait), sys2.unx (fsserial_read, fsserial_write, + fsserial_io): log signals when they occur, even if we continue + some sort of loop, rather than waiting for the next ulog call. + + * sys2.unx (fsserial_lock, fsserial_open): don't block when + opening the write descriptor. + +Mon Mar 16 00:14:43 1992 Ian Lance Taylor (ian@comton.airs.com) + + * system.h, uuxqt.c (uqdo_xqt_file), sys5.unx (fsysdep_execute): + pass command to fsysdep_execute as first element of argument + array. + + * tcp.c: declare _exit. + + * uucp.h: move definition of const before use for non ANSI C. + + * uucp.h, sys1.unx: undefine remove in uucp.h if the system does + not have it to avoid conflict with macro definitions. + + * uucico.c, uuxqt.c, protf.c, prott.c, prote.c, config.c, chat.c, + port.c, sys2.unx: miscellaneous cleanups. + + * tcp.c (ftcp_open): cast argument to bzero. + + * time.c (qtimegrade_parse): cast argument to qttime_parse to + long. + + * file.c: changed iRecmode to unsigned int. + + * configure.in, uucp.h: on SCO 3.2.2 sig_atomic_t is defined in + but not . + + * sys1.unx: undefined remove before the function definition to + avoid trouble on systems for which it is a macro. + + * Makefile.in: removed dependencies of getopt.o. + + * sys1.unx, sys7.unx, tstuu.c: adjusted external declarations. + + * getopt.h, getopt.c: get new versions from glibc 1.01. + + * sys1.unx: don't declare sigemptyset. + + * version.c: updated to beta 1.03. + + * chat.c (fcsend): Scott Ballantyne: go ahead and send a character + for an illegal escape sequence rather than failing out. + + * uuxqt.c (uqdo_xqt_file), sys5.unx (zsysdep_find_command): cast + result of alloca. + + * protg.c (fgprocess_data): Niels Baggesen: improved debugging + information. Also tweaked fgprocess_data code to use memchr to + find the next DLE rather than searching for it by hand. + + * uucico.c (faccept_call, fuucp): accept SVR4 -U flag giving + maximum file transfer size; accept and ignore SVR4 -R flag meaning + that the system supports file restart. + +Sun Mar 15 00:21:56 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sysinf.c (titime, titimegrade): permit a retry time to be + specified as an optional additional argument. + + * uucico.c (zget_uucp_cmd, zget_typed_line): turn off DEBUG_PORT + when doing DEBUG_HANDSHAKE. + + * policy.h, sysh.unx, sys1.unx, sys2.unx (fsblock_writes, + fsserial_write, fsserial_io): added configuration parameters + HAVE_UNBLOCKED_WRITES and SINGLE_WRITE. Also blocked signals + while clearing afSignal in fsysdep_modem_close. + + * chat.c (icexpect, fcsend): turn off DEBUG_PORT while doing chat + script debugging. + + * sysh.unx, sys2.unx (fsserial_lock, fsserial_open, + fsserial_write, fsserial_io, fsysdep_tcp_read, fsysdep_tcp_write, + fsysdep_tcp_io): T. William Wells: some systems don't support + unblocked writes, so don't use them. + + * port.c (fport_read, fport_write): show calls to fport_read and + fport_write under DEBUG_PORT. + + * bnu.c (fbnu_find_port): Scott Ballantyne: accept "Any" as a + Device speed. + +Sat Mar 14 20:52:11 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uucp.h, system.h, sysh.unx, uucico.c (main, zget_typed_line), + uuxqt.c (main), uucp.c (main), uux.c (main, uxcopy_stdin), tcp.c + (ftcp_open), log.c (ulog, ulog_close), sys1.unx (ussignal), + sys2.unx (fsserial_close, fsysdep_modem_end_dial, fsserial_read, + fsserial_write, fsserial_io, fsysdep_tcp_read, fsysdep_tcp_write, + fsysdep_tcp_io): T. William Wells and Chip Salzenberg: keep an + array of signals so that a new signal doesn't obliterate our + knowledge of an old signal. Johan Vromans: if we get SIGINT + continue the current session but don't start any new ones. + + * sysh.unx, sys1.unx (isspawn, espopen, iswait, fsysdep_mail, + fsysdep_run, getcwd, mkdir), sys2.unx (fsrun_chat), sys3.unx + (fsysdep_wildcard_start), sys5.unx (fsysdep_execute), sys7.unx + (fsysdep_lock_status), uuxqt.c (uqdo_xqt_file), tcp.c (ftcp_open), + tstuu.c (uchild): added function isspawn, espopen and iswait and + channeled all execs of new processes and waits through them. + +Fri Mar 13 18:00:04 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sysinf.c (uset_system_defaults): Chip Salzenberg: changed + default login script timeout to 10 seconds. + + * prot.h, prot.c (freceive_data, breceive_char), protg.c, protf.c, + prott.c, prote.c: changed breceive_char to go through + freceive_data rather than calling fport_read directly. Added an + freport argument to freceive_data, and change all old calls to + pass it in as FALSE. + +Thu Mar 12 14:49:59 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uucp.h: added a padding byte to scmd structure, since at least + one compiler needs it. + + * uucp.c (main): use fake local name (from ``myname'' command) + when generating an execution request intended for the local + system. + + * sysh.unx: corrected readdir prototype. + + * sys2.unx: moved local header files ahead of sleep routine + determination. + + * General overhaul to change debugging system. Debugging is now + done by type rather than by number. iDebug is now interpreted as + a bit sequence. DEBUG may only be 0 (no checks or debugging), 1 + (checks, no debugging) or 2 (checks and debugging). The debugging + names are parsed by idebug_parse and tidebug_parse in config.c. + The debugging types are additive. Many source files changed. + Inspired by Michael Richardson, Johan Vromans and Peter da Silva. + +Wed Mar 11 12:01:03 1992 Ian Lance Taylor (ian@comton.airs.com) + + * policy.h, uuxqt.c (uqdo_xqt_file): Chip Salzenberg: support + Internet mail addresses in uuxqt replies (added configuration + parameter HAVE_INTERNET_MAIL to control this). + + * sys7.unx (fskill_or_rejuv): permit uucp user to delete any job. + + * uucp.h, system.h, sysh.unx, config.c, uuxqt.c (main, uqabort), + sys5.unx (isysdep_lock_uuxqt, fsysdep_unlock_uuxqt), bnu.c + (ubnu_read_sysfiles): Marty Shannon: added max-uuxqts command, + along with support for BNU Maxuuxqts, to limit number of + concurrent uuxqt processes. + + * chat.c (icexpect, fcsend), uucico.c (zget_uucp_cmd, + zget_typed_line): improved debugging by avoiding incredibly long + lines. + + * system.h, sys5.unx (fsysdep_execute), uuxqt.c (uqdo_xqt_file): + Jon Zeef: if a temporary failure occurs, retry the execution + later. + +Tue Mar 10 12:40:30 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sysh.unx, sys1.unx (isfork), sys2.unx, sys5.unx, tcp.c: + Franc,ois Pinard: retry fork several times before giving up. + + * uucp.h, prot.c (fploop, fgot_data), file.c (usendfile_error, + urecfile_error, frecfile_rewind): Niels Baggesen: if we can't read + or write a file, treat it as a temporary error rather than a + permanent error; if we get an error on write, drop the connection + rather they try to continue. + + * uucp.h, system.h, sysh.unx, uucico.c (fuucp), prot.c + (fsend_file, freceive_file), file.c (fsent_file, usendfile_error, + freceived_file, urecfile_error, fmail_transfer), sys1.unx + (usmake_spool_dir), sys4.unx (zsysdep_save_temp_file): if a file + send fails, save the file away rather than lose it forever. + + * uucico.c (main): don't run uuxqt if we got a SIGTERM. + + * tcp.c (ftcp_open): Petri Helenius: have server fork twice to + avoid zombies. + + * port.h, prtinf.c, v2.c, bnu.c (fbnu_find_port), uucico.c + (fdo_call, faccept_call), uuchk.c (fkshow_port): added protocol + command for ports, mostly to support BNU. Also modified uuchk to + make the absence of any matching port or dialer more obvious. + + * sys3.unx (esysdep_open_receive): check size of destination file + system as well as temporary file system; handle f_bsize field + under FS_MNTENT. + + * configure.in, sysh.unx: test for including and + in the same file, setting new configuration + parameter HAVE_TERMIOS_AND_SYS_IOCTL_H accordingly; handle it in + sysh.unx. + +Mon Mar 9 00:06:12 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sys2.unx (fsserial_close): Franc,ois Pinard: sleep for a second + after closing the serial port to give it a chance to settle. + + * sysh.unx (fsetterminfodrain), sys2.unx (fsserial_close, + fsserial_reset, fsserial_set): wait for terminal output to drain + before closing it, resetting it, or changing its parameters. + + * uucico.c (zget_uucp_cmd): Ted Lindgreen: strip parity bit from + initial handshake strings. + + * system.h, sys3.unx (esysdep_open_send), uucico.c (fuucp): Ted + Lindgreen: don't send a mail message if a file to send has been + removed, since it might have been sent in a previous session. + + * uuchk.c (ukshow): Zacharias Beckman: put list of permitted + programs and execution path on separate lines. + + * uucico.c (fdo_call, faccept_call): only look for hangup string + in debugging mode, since there's nothing to be done with it + anyhow. + + * uucico.c (faccept_call): Ted Lindgreen: report the minimum + transfer grade requested during an incoming call in the log file. + + * uucp.h, uutime.h, config.c, uucico.c (fcall), time.c + (ftimespan_match, btimegrade, cmax_size_now): added a new status + type for ``wrong time to call''. If a system can never be called, + this status type is not used (if an attempt is made to call the + system, the status is left unchanged). + +Sun Mar 8 11:41:45 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uucico.c (main, flogin_prompt, faccept_call): Ted Lindgreen: if + we were asked to call a single system, or if a single system + called in, then start uuxqt with -s for just that system. + + * uucico.c (main): Ted Lindgreen: ignore the -u option. + + * tstuu.c: Ted Lindgreen: don't include if it's not + there. Also removed the ``ignore this error'' message from the + chat scripts since it's no longer marked as an error. + + * sys2.unx (fsserial_set): Ted Lindgreen: if CRTSCTS is defined + and turned on, then don't turn on IXOFF. + + * uucp.h, log.c, uucico.c (fdo_call, faccept_call): Ted Lindgreen: + report the port name and (for incoming calls) the login name in + the log file. + +Sat Mar 7 10:00:47 1992 Ian Lance Taylor (ian@comton.airs.com) + + * port.h, prtinf.c, sys2.unx (fsserial_lockfile, fsserial_lock): + Peter da Silva: added ``lockname'' command to ports to permit + specification of the file name to use when locking. + + * sys1.unx (usysdep_detach): let setpgrp fail silently. + + * sys2.unx: always include if it's present on the + system. + + * time.c (btimegrade, cmax_size_now): removed extraneous + semicolons. + + * sys2.unx (fsserial_lock, fsserial_open, fsserial_close): support + TIOCEXCL locking. + + * sys2.unx (fsserial_open): preserve unknown bits in c_cflag when + using HAVE_SYSV_TERMIO or HAVE_POSIX_TERMIOS. + + * prot.h: never included more than once. + +Fri Mar 6 21:53:28 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uucp.h: Eric Ziegast: some systems don't define EXIT_SUCCESS or + EXIT_FAILURE in stdlib.h. + + * uucp.h, uutime.h, uucico.c (fuucp), sysinf.c (uinittimetables, + uaddtimetable), uuchk.c (main, ukshow_size, ukshow_time, + qcompress_span), time.c (all new): rewrote time routines + completely for consistency and simplicity. Fixed bug causing + incorrect maximum possible transfer size. Added new file + uutime.h. + +Wed Mar 4 10:06:13 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sys2.unx (fsserial_lockfile, fsserial_lock, fsysdep_modem_open, + fsysdep_direct_open, fsysdep_modem_close, fsysdep_direct_close): + Petri Helenius: if the open failed on a serial port, the lock + files were not removed. + + * config.c (igradecmp): the local variables in igradecmp have to + be integers; signed characters might not work correctly (although + they would in all normal cases). + + * sys4.unx (fsysdep_has_work): Johan Vromans: set *pbgrade + correctly if we still have work left over that we haven't looked + at yet. + + * tstuu.c (main, uchoose, fwriteable): Roberto Biancardi: use poll + if we haven't got select. + + * uucico.c (zget_uucp_cmd): Michael Haberler: some systems send \n + after Shere, rather than a null byte. + +Tue Mar 3 01:03:22 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uuxqt.c (main, uqdo_xqt_file): permit local executions, don't + get grade out of system dependent file name. + + * sys4.unx (fsysdep_get_work): Bob Denny: warn if we can't open a + command file. + + * v2.c (uv2_read_systems): Jeff Putsch: infinite loop when reading + time string. + + * uucp.h, sys1.unx, sys2.unx, sys3.unx, tstuu.c, configure.in: + Thomas Fischer: some NeXT compatibility stuff: removed externs of + sleep and alarm, included in uucp.h. + + * uucp.h, port.h, uucico.c (zget_uucp_cmd, zget_typed_line), + port.c (cdebug_char, udebug_buffer), chat.c (icexpect, fcsend, + fcphone), log.c (ulog): Michael Richardson: added LOG_DEBUG_START, + LOG_DEBUG_CONTINUE and LOG_DEBUG_END to allow a debugging line in + the log file to be built character by character. Used this new + feature in chat script debugging, rather than having each + character appear on a separate line. Added fPort_debug variable + to control port debugging. + + * uustat.c (fsquery, fsquery_system, fsquery_show): handle + execution files queued up for the local system correctly when + using -q option (they still don't show up on any other option). + + * uucp.texi, protg.c (fgstart, fgshutdown): Aleksey P. Rudnev: + added remote-window and remote-packet-size 'g' protocol + parameters. Reset the parameters to their default values in + fgshutdown. + + * sysh.unx, sys2.unx (fsserial_read, usalarm), sys1.unx + (usset_signal, usysdep_detach), uucico.c (main): overhauled + fsserial_read yet again. The timeout passed in is now an absolute + bound. A special SIGALRM handler does some wierd stuff to avoid + any possible race. + + * config.c (uread_config), sysinf.c (uiread_systems, + fcallout_login, fcheck_login), prtinf.c (ffind_port, + fread_dialer_info), chat.c (fctranslate), uucico.c (faccept_call): + T. William Wells: when using HAVE_TAYLOR_CONFIG in combination + with V2 or BNU configuration files, don't complain if the + HAVE_TAYLOR_CONFIG files are missing. + +Mon Mar 2 10:21:36 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sys2.unx (fsserial_read): T. William Wells: don't arbitrarily + extend read timeout. + + * uux.c (main): check iSignal before entering fread, since the + user may have hit ^C earlier in the program. + +Sun Mar 1 23:39:33 1992 Ian Lance Taylor (ian@comton.airs.com) + + * policy.h, uucp.h, sysh.unx, sys2.unx (fsserial_lock, + fsysdep_modem_close, fsysdep_direct_close), util.c (strlwr), + configure.in: Marc Unangst: added HAVE_SCO_LOCKFILES configuration + parameter to force lock file names to lower case. + +Fri Feb 28 00:07:12 1992 Ian Lance Taylor (ian@comton.airs.com) + + * system.h, uucico.c (faccept_call, fdo_xcmd), uuxqt.c + (uqdo_xqt_file), uux.c (main), uucp.c (main, ucspool_cmds), + sys3.unx (zsysdep_spool_cmds), uux.1, uucp.1: added -j switch to + uucp and uux to display the jobid of the spooled job. + + * uucp.h, system.h, sysh.unx, uucico.c (fuucp, fdo_xcmd, + fok_to_send, fok_to_receive), uuxqt.c (uqdo_xqt_files), uux.c + (main, uxcopy_stdin), uucp.c (main), file.c (freceived_file), + config.c (fin_directory_list), sys1.unx (fsysdep_file_exists, + fsuser_access, fsysdep_in_directory), sys3.unx (esysdep_open_send, + fsysdep_move_file), sys5.unx (fsysdep_xqt_check_file): Chip + Salzenberg: recheck file access permissions before sending, to try + to avoid symbolic link games. Check that the user has search + access on all directories down to the file, and read or write + access to the file or directory itself. Check in uucp and uux as + well as uucico, to provide early messages. Check the standard + input file in uuxqt. Be more careful about creating files in the + spool directory. This eliminates all security problems I know of, + except for a very short race in fsysdep_move_file. + + * sys3.unx (esysdep_open_send): give an error message if we try to + send a directory (used to just fail silently). + + * system.h, sysh.unx, sys1.unx (usysdep_detach, ussignal, + fsysdep_catch, usysdep_start_catch), sys2.unx + (fsysdep_modem_end_dial, fsserial_read), uux.c (main): T. William + Wells: fsysdep_catch obviously must be a macro, since it calls + setjmp. Also TIOCNOTTY sets the process group to 0, so we don't + have to fork before calling it. + +Thu Feb 27 00:08:09 1992 Ian Lance Taylor (ian@comton.airs.com) + + * sys1.unx, sys6.unx, sys7.unx: added some extern definitions. + + * configure.in, uucp.h, system.h, sysh.unx, uucico.c (main, + ucatch, uabort, zget_typed_line, zget_uucp_cmd), uuxqt.c (main, + uqcatch, uqabort), uux.c (main, uxcatch, uxrecord_file, uxabort), + uucp.c (main, uccatch, ucrecord_file, ucabort), uustat.c (main, + uscatch), uulog.c (main, ulcatch), uuname.c (main, uncatch), + uucheck.c (main, ukcatch), log.c (ulog_fatal_fn, ulog, + ulog_close), tstuu.c (main, uchild, uprepare_test), sys1.unx + (usysdep_initialize, usysdep_detach, usysdep_signal, + fsysdep_catch, usysdep_end_catch, ussignal, fsset_signal, + fsysdep_run, raise), sys2.unx (usalarm, usysdep_pause, + fsserial_lock, fsserial_open, fsysdep_stdin_close, + fsysdep_modem_close, fsysdep_modem_end_dial, fsserial_read, + fsserial_write, fsserial_io), sys5.unx (fsysdep_execute): T. + William Wells: overhaul to support detaching from the terminal and + completely reliable signals. uucico now calls usysdep_detach at + various points; new option -D prevents detaching. The signal + handling code all goes through usysdep_signal, fsysdep_catch and + usysdep_end_catch. A signal now just sets iSignal, which is + checked at various points, notably in the port routines and in the + main loops in uucico and uuxqt. + +Tue Feb 25 10:59:23 1992 Ian Lance Taylor (ian@comton.airs.com) + + * protg.c (fgwait_for_packet): Bob Denny: reset the count of + timeouts only when data is recognized, so that we aren't fooled by + a sequence of imperfect echoes. + + * sys5.unx (zsysdep_get_xqt): Bob Denny: don't warn if opendir + gets ENOENT. I think POSIX requires ENOTDIR, but what can you do? + +Mon Feb 24 14:37:10 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uucico.c (main, uusage): don't treat an extra argument as a port + name. + + * sys3.unx (esysdep_truncate): Roberto Biancardi: support F_CHSIZE + and F_FREESP in esysdep_truncate. + + * configure.in, sys2.unx (fsserial_read, usysdep_pause): Roberto + Biancardi: use poll to sleep less than a second if we haven't got + anything else. + + * v2.c (uv2_read_systems, fv2_find_port), bnu.c + (ubnu_read_systems, fbnu_find_port, fbnu_read_dialer_info), + uustat.c (fsworkfile_show): Roberto Biancardi: skip spaces and + tabs after doing a strtok ((char *) NULL, ""). + + * copy.c (fcopy_file), sys1.unx (esysdep_fopen), sys2.unx, + sys3.unx (esysdep_open_receive, esysdep_truncate, fsdo_lock, + fscmd_seq), sys5.unx (fsysdep_execute), sys7.unx, tstuu.c: John + Theus: some systems use instead of . Also + changed the code to call creat instead of open when appropriate. + Should now work on V7, with the exception of O_NONBLOCK and + O_NDELAY in sys2.unx and tstuu.c. + + * uucp.h: John Theus: if we don't have vprintf, ulog is defined + without an ellipsis, so don't declare it with one. + +Sun Feb 23 14:45:53 1992 Ian Lance Taylor (ian@comton.airs.com) + + * uucp.h, system.h, bnu.c (ubnu_read_systems), config.c + (fin_directory_list), sys1.unx (fsysdep_in_directory), sys5.unx + (fsysdep_xqt_check_file), uucp.c (main), uuxqt.c (uqdo_xqt_file), + uucico.c (fdo_xcmd, fok_to_send, fok_to_receive), tstuu.c + (uprepare_test): only permit files to be received into directories + that are world writeable. Check for this in fsysdep_in_directory, + with a new argument. Changed calls appropriately. In tstuu + create /usr/tmp/tstuu as world writeable. + + * bnu.c (ubadd_perm_alternate): Doug Evans: after all that, I got + it wrong: WRITE only applies to remote requests. + + * uucp.h, uucico.c (fuucp, fdo_xcmd, fok_to_send, fok_to_receive), + uuxqt.c (uqdo_xqt_file), uuchk.c (ukshow), sysinf.c + (uset_system_defaults, tialternate), sys5.unx + (fsysdep_xqt_check_file), bnu.c (ubadd_perm_alternate): fixed READ + and WRITE handling to match BNU semantics. Added + zcalled_local_send, zcalled_local_receive, zcalled_remote_send and + zcalled_remote_receive fields to ssysteminfo structure for this, + and handled them in all the appropriate places. + +Sat Feb 22 22:30:59 1992 Ian Lance Taylor (ian@comton.airs.com) + + * Complete overhaul of configuration to use automatic shell + script. Eliminated conf.h, now generated by configure. Renamed + Makefile to Makefile.in. Added policy.h for administrative + decisions and other choices which can not be made automatically. + Many changes to many source files, none having to do with code. + +Thu Feb 20 17:57:55 1992 Ian Lance Taylor (ian at comton.airs.com) + + * uucico.c (fdo_call): Chip Salzenberg: some systems truncate the + Shere= machine name to 7 characters. + +Wed Feb 19 14:36:31 1992 Ian Lance Taylor (ian at comton.airs.com) + + * sys7.unx (fskill_or_rejuv): make sure that only the submitter or + the superuser is permitted to cancel (or rejuvenate) a request. + + * system.h, sysh.unx, sys3.unx (zsfile_to_jobid, zsjobid_to_file), + sys4.unx (zsysdep_jobid), sys7.unx, uustat.c, Makefile, MANIFEST: + wrote uustat. Changed CSEQLEN in sys3.unx from 5 to 4. Added + several new system dependent functions, mostly in sys7.unx. + + * system.h, sys1.unx, log.c, file.c, chat.c, protg.c, uucico.c: + rearranged the time functions for the convenience of uustat. Made + isysdep_time take an optional pimicros arguments. Renamed + usysdep_full_time to isysdep_process_time, and made clear that it + need only work within a single process. Changed usysdep_localtime + to take a time returned by isysdep_time rather than always use the + current time. Changed the calls as appropriate. + +Tue Feb 18 14:03:19 1992 Ian Lance Taylor (ian at comton.airs.com) + + * uuxqt.c (main): pass fdaemon argument correctly to + usysdep_initialize. + +Mon Feb 17 17:09:16 1992 Ian Lance Taylor (ian at comton.airs.com) + + * uuxqt.c (uqdo_xqt_file): T. William Wells: make sure sh uses + absolute path of command, rather than relying on PATH. + + * sys5.unx (zsysdep_find_command): Michael Nolan: allow full + command name to be specified by remote system, not just basename, + if command is not in path. + + * log.c (ulog): don't use headers when outputting to terminal. + + * sys2.unx (fsrun_chat): Bob Denny: log chat program messages as + LOG_NORMAL, not LOG_ERROR. + +Fri Feb 14 00:17:57 1992 Ian Lance Taylor (ian at comton.airs.com) + + * uucico.c (ucatch), uuxqt.c (uqcatch): Neils Baggesen: under + HAVE_BNU_LOGGING, don't lose the system name when dieing. + + * uulog.c, Makefile, MANIFEST: wrote uulog. + + * uuname.c, Makefile, MANIFEST: wrote uuname. + + * bnu.c (ubadd_perm_alternate): T. William Wells: must xstrdup the + system name before calling uadd_validate. + + * log.c (ulog_close): Micheal Nolan: don't refer to eLdebug if the + DEBUG configuration parameter is 0. + + * uucp.c (main): Niels Baggesen: abtname must be copied into + memory, or it will be overwritten by the next file to be copied. + +Sun Feb 9 00:12:58 1992 Ian Lance Taylor (ian at comton.airs.com) + + * uucico.c (fuucp), prot.c (fsend_file, freceive_file): Bob Denny: + call fmail_transfer before calling fsysdep_did_work, because the + latter frees up strings used by the former. + + * sysh.unx, sys1.unx (mkdir), uudir.c (new file), Makefile: added + HAVE_MKDIR configuration parameter for systems without the mkdir + system call. The emulation function in sys1.unx invokes the new + suid program uudir which sets its uid to uucp and invokes + /bin/mkdir. Added rules to create uudir to Makefile. + +Sat Feb 8 14:25:50 1992 Ian Lance Taylor (ian at comton.airs.com) + + * sysh.unx, sys1.unx (opendir, readdir, closedir), sys4.unx, + sys5.unx: added HAVE_OLD_DIRECTORIES configuration parameter to + support systems without opendir/readdir/closedir which use + original Unix directory format. + + * sysh.unx, sys1.unx (dup2): added HAVE_DUP2 configuration + parameter and dup2 emulation function. + + * sys1.unx (zsysdep_local_name): put utsname structure on stack + rather than making it static. + + * sysh.unx, sys1.unx (usysdep_initialize, getcwd): if we have + neither getcwd nor getwd, fork /bin/pwd to get the current working + directory. + + * system.h, uucico.c (main), uuxqt.c (main), uux.c (main), uucp.c + (main), uuchk.c (main), sys1.unx (usysdep_initialize), sys6.unx + (fsysdep_needs_cwd, zsysdep_add_cwd): because getting the current + working directory can be expensive on Unix, since some + implementation of getcwd fork a shell to execute pwd, only try to + get the cwd if it is going to be needed by uux or uucp. + + * uucico.c (main), uuxqt.c (main), uux.c (main), uucp.c (main), + uuchk.c (main), log.c (ulog): handle all possible signals raised + by abort, namely SIGABRT, SIGILL and SIGIOT. In ulog always call + abort rather than raise (SIGABRT). + + * sys4.unx (usysdep_get_work_free): the qSwork_file information + was freed up incorrectly if a file transfer failed. + + * sysh.unx, sys2.unx: Archaic Zilog System III computers use + setret and longret rather than setjmp and longjmp, so I added a + HAVE_SETRET configuration option. + + * prott.c (ftstart, ftsenddata): shifts of integers by more than + 15 are not portable. + + * prot.c (fsend_file, freceive_file, fploop, fgot_data): I ran + into an old compiler which couldn't handle the calls to pffile, so + I simplified them to use a variable. + + * port.c (fmodem_dial): cast result of alloca. + + * getopt.h, getopt.c: Jay Vassos-Libove: renamed getopt and + related variables by macro defining them to gnu_*. This avoids + conflicts with system header files and system libraries. + +Fri Feb 7 12:08:42 1992 Ian Lance Taylor (ian at comton.airs.com) + + * everything: added HAVE_STRING_H and HAVE_STRINGS_H. Removed + include of in every source file and put it in uucp.h. + Since I had to change everything anyhow, added 1992 to the + copyright date. + + * uucico.c (fcall): Bob Denny: retry time not reached is not + really an error, so just make a normal log entry for it. + +Sun Feb 2 01:38:47 1992 Ian Lance Taylor (ian at comton.airs.com) + + * uucp.c (main): Get the file name for the destination of a local + copy using zsysdep_real_file_name rather than zsysdep_in_dir, + since the latter doesn't get the basename of the argument. + + * sys3.unx (fsysdep_get_status): Niels Baggesen: cast enum to int + before comparison. + + * system.h, uucp.c (main), uux.c (main), sys6.unx (fsysdep_access, + fsysdep_daemon_access): Niels Baggesen: check user access to file + since programs are running setuid. Previously uucp and uux + permitted a file readable only by uucp to be transferred to + another system by user request! + + * chat.c (fchat): Michael Nolan: portions of a chat string might + be separated by more than just a single space if they are read + from a V2 or BNU configuration file. + +Fri Jan 31 19:51:57 1992 Ian Lance Taylor (ian at comton.airs.com) + + * protg.c: Chip Salzenberg: change default window size to 7. + + * sys3.unx (fsdo_unlock): Michael Nolan: cast result of alloca to + (char *), not that it really matters. + + * log.c (ulog): Michael Nolan: if SIGABRT is not defined, just + call abort. + +Thu Jan 30 18:19:33 1992 Ian Lance Taylor (ian at comton.airs.com) + + * bnu.c (ubadd_perm): Michael Nolan: debugging check was done + wrong for entry with LOGNAME but no MACHINE. + +Wed Jan 29 13:28:59 1992 Ian Lance Taylor (ian at comton.airs.com) + + * uucico.c (zget_uucp_cmd): Patrick Smith: only wait a short time + for the hangup string. + + * sys4.unx (iswork_cmp): Patrick Smith: fixed casts to not cast + away const. + +Tue Jan 28 11:06:34 1992 Ian Lance Taylor (ian at comton.airs.com) + + * sys1.unx, sys3.unx, tstuu.c: Jay Vassos-Libove: removed some + declarations of system functions that conflict with system header + files on BSD/386 alpha. + + * Makefile: Jeff Ross: make sure the uninstall target restores the + original file owner and mode. + + * protg.c (fgsendcmd): the previous patch wasn't really correct. + +Mon Jan 27 22:30:47 1992 Ian Lance Taylor (ian at comton.airs.com) + + * log.c (ustats): Marty Shannon: don't report a failed transfer + under USE_BNU_LOGGING. + + * sys3.unx (zsysdep_real_file_name): Marty Shannon: a trailing '/' + on the name means that it is a directory, even if the directory + does not already exist. + + * uucico.c (fuucp): Marty Shannon: the -f flag indicating that + directories should not be created was not being handled correctly. + + * uucico.c (fcall): Chip Salzenberg: set .Status correctly if + wrong time to call. + + * protg.c (fgsendcmd): John Antypas: didn't handle null byte at + end of command which was exactly a power of two in length + correctly. + +Tue Jan 21 14:37:10 1992 Ian Lance Taylor (ian at comton.airs.com) + + * Released version 1.02. + + * system.h, uucico.c (main), uux.c (main), uucp.c (main, + zcone_system), sys1.unx (fsysdep_run): Chip Salzenberg: have uucp + and uux start up uucico -s system rather than uucico -r1. + +Mon Jan 20 11:45:38 1992 Ian Lance Taylor (ian at comton.airs.com) + + * sys1.unx (fsysdep_make_dirs): don't try to create a directory + with no name. + + * version.c: change version number to 1.02. + + * uucico.8, uuxqt.8, uucp.1, uux.1: change version number to 1.02. + + * MANIFEST: removed texinfo.tex; it's twice the size of any other + file, so I think it's just to large to distribute. + + * uucico.c (fcall, fdo_call): Marty Shannon: update the time in + the .Status file if it's the wrong time to call, and upon + receiving a call. + +Sun Jan 19 13:29:23 1992 Ian Lance Taylor (ian at comton.airs.com) + + * protg.c (fgsendcmd, fgsenddata): Dave Platt: if the remote UUCP + accepts packets larger than 64 bytes, assume it can handle + differing packet sizes, so if we have a small amount of data to + send, use a small packet. Besides the two routines mentioned, + also made minor changes to other routines to get the packet length + out of the packet data rather than always assuming the same packet + size. + + * conf.h, uucp.h: Matthew Lyle: some systems don't declare errno + in , so I added HAVE_ERRNO_DECLARATION. + + * conf.h, uucp.h, util.c (bsearch): added HAVE_BSEARCH + configuration parameter. + +Sat Jan 18 17:45:28 1992 Ian Lance Taylor (ian at comton.airs.com) + + * tstuu.c (utransfer): Mike Park: don't sleep when the input + buffer is full; it's too slow. + + * Makefile: when making a distribution,change the mode of separate + copies of the configuration files Makefile, conf.h and sysh.unx. + + * uucico.c (faccept_call): Marty Shannon: update .Status file on + incoming calls. + + * uucp.h, prot.h, uucico.c (fuucp), prot.c (fsend_file, + fpsendfile_confirm, freceive_file, fprecfile_confirm, fxcmd, + ustats_failed), file.c (fsent_file, usendfile_error, + freceived_file, urecfile_error, frecfile_rewind, fmail_transfer): + reworked calls to fsydep_did_work and sending of mail messages to + be more sensible. Now sends mail to requestor if request fails + permanently and does not remove file if request fails only + temporarily. + +Thu Jan 16 11:33:08 1992 Ian Lance Taylor (ian at comton.airs.com) + + * protg.c (fgsendcmd, fgsenddata): zero out unused bytes in short + packets. + + * prot.c (zgetcmd), protf.c (ffsendcmd), prott.c (ftsendcmd), + prote.c (fesendcmd): Niels Baggesen: added some debugging + messages. + + * protg.c (fgsendcmd): corrected misspelling in debugging message. + + * log.c (ustats): Niels Baggesen: add FAILED to end of xferstats + line if appropriate. + + * uuxqt.c (uqdo_xqt_file): Niels Baggesen: was checking strcmp + return value incorrectly, so messages to other systems were not + being sent. + + * sys2.unx, tstuu.c: Mike Park: ioctl is sometimes declared + varadic, so we can't declare it. + +Wed Jan 15 02:03:43 1992 Ian Lance Taylor (ian at comton.airs.com) + + * sys1.unx: put \n at end of fsysdep_run error message. + + * sysh.unx, sys1.unx, sys2.unx, tstuu.c: Mike Park: on some + systems includes but can only be + included once; added HAVE_SYS_TIME_AND_TIME_H to sysh.unx. + + * tstuu.c: Mike Park: removed prototype for times since some + systems don't have clock_t. + + * conf.h, sys2.unx, util.c: Mike Park: some systems don't have + . Every macro used from it already had a check to make + sure it was defined anyhow. + + * tstuu.c (uprepare_test): Mike Park: sh on NeXT interprets a + leading ~, so we have to quote the argument to system(3). + Incredible. + + * conf.h, uucp.h: Mike Park: if HAVE_ALLOCA is 0 when compiling + with gcc, don't define alloca as a macro. This will let the NeXT + define it in some header file. + + * sysh.unx, sys2.unx, sys5.unx, tstuu.c: Mike Park: handle + HAVE_UNION_WAIT completely. Assume that system(3) returns an int + which should be put into the w_status field. Define macros for + union wait if they are not already defined. Move all this stuff + into sysh.unx rather than duplicating it in three different files. + + * conf.h, uucp.h, sysh.unx, config.c, bnu.c, v2.c, uucico.c, + uuxqt.c, uux.c, uucp.c, uuchk.c, Makefile: set directory to look + for configuration files in in Makefile rather than in sysh.unx. + This forced a number of changes, as now all new style + configuration files are looked up using NEWCONFIGLIB. Old style + configuration files are looked up using OLDCONFIGLIB. + +Mon Jan 13 00:35:43 1992 Ian Lance Taylor (ian at comton.airs.com) + + * sys3.unx: David Nugent: don't declare chmod, since it may be + prototyped to take an argument that is smaller than an int. + + * uucico.c (faccept_call): Chip Salzenberg: only declare sportinfo + if it will be used (if HAVE_TAYLOR_CONFIG is true). + + * sys3.unx (isysdep_get_sequence): Chip Salzenberg: avoid use + before set warning from gcc. + + * protf.c (ffprocess_data): Chip Salzenberg: avoid use before set + warning from gcc. + + * sysh.unx, sys2.unx (fsserial_read, usysdep_pause): Chip + Salzenberg: added HAVE_USLEEP configuration parameter for the AIX + usleep routine. + + * uuchk.c, prtinf.c, config.c: Chip Salzenberg: strcmp is a macro + on AIX, so avoid declaring it and undef it in config.c where we + want to declare it because we want to take its address. + + * uucp.h, sys3.unx (fsysdep_get_status, fsysdep_set_status): Chip + Salzenberg: handle out of range status codes in status files. + + * Makefile, sysh.unx: defined LIBDIR in the Makefile, rather than + forcing the user to define it in two different places. + + * sys.unx, tstuu.c: Chip Salzenberg: can't declare execl, since it + is varadic. + + * sys1.unx, sys2.unx, sys3.unx, sys5.unx, tstuu.c: David Nugent: + can't declare open or fcntl, since they may use ... in header file + prototype; added declaration for popen; added casts of first mkdir + argument to char *. + + * sysh.unx, tstuu.c (uchild): Mike Park: added HAVE_WAITPID and + HAVE_WAIT4 configuration parameters to allow the use of wait4 as + found on the NeXT. + + * tstuu.c (uprepare_test): Mike Park: use IPUBLIC_DIRECTORY_MODE + rather than S_IRWXU | S_IRWXG | S_IRWXO. + + * sysinf.c (tisystem): Mike Park: ulog was being passed the wrong + number of arguments. + +Sun Jan 12 14:32:47 1992 Ian Lance Taylor (ian at comton.airs.com) + + * Eliminated CONFIG, INSTALL and THANKS. They are now included in + uucp.texi. Changed README and MANIFEST accordingly. Added + uucp.info* and texinfo.tex to MANIFEST. + + * Makefile, uucp.texi: renamed taylor.texi to uucp.texi. + + * uucico.c (fcall, fdo_call): John Antypas: pass in sportinfo + structure for fdo_call to use for an unknown port. + + * log.c (ulog): allocate enough bytes to name file if + HAVE_BNU_LOGGING is in use but zLogfile has no %s. + +Sat Jan 11 12:11:56 1992 Ian Lance Taylor (ian at comton.airs.com) + + * Makefile: changed to correspond to GNU standards, according to + standards.text of 24 Nov 91. + + * Makefile: Franc,ois Pinard: use $(INSTALL_PROGRAM) and + $(INSTALL) rather than cp to install the programs. + + * time.c (ftime_now), sys1.unx (usysdep_localtime): John Antypas: + use memcpy to get the result of localtime rather than relying on + structure assignment. + + * sysh.unx, prtinf.c: Hannu Strang: changed definition of + SYSDEP_STDIN_CMDS to pass structure instead of address of + structure to avoid bug in AIX compiler which causes it to fail to + recognize an address constant containing the -> operator. + +Tue Jan 7 10:22:43 1992 Ian Lance Taylor (ian at comton.airs.com) + + * Released beta 1.02. + + * protg.c (fgcheck_errors): discount out of order packets in the + total error count, and allow a negative error count to mean that + any number of errors are accepted. + + * sysinf.c (tadd_proto_param): Niels Baggesen: allocate number of + protocol parameters based on *pc, not sIhold.cproto_params. + +Sat Jan 4 16:42:21 1992 Ian Lance Taylor (ian at comton.airs.com) + + * log.c (ulog): tweaked HAVE_V2_LOGGING slightly. + + * v2.c (uv2_read_systems): set chat script correctly. + + * uucp.h, sys3.unx: avoid redefining SEEK_SET if it appears in + but not . + + * chat.c (fcsend): made fcsend into a static function. + + * uucp.h, uucico.c, uuxqt.c, uux.c, uucp.c, uuchk.c, version.c: + changed abProgram and abVersion from const char [] to char [] + because the Microsoft compiler on SCO can't handle const char [] + arrays correctly. + + * uux.c (main): allocate enough space for log message. + + * sys1.unx (usysdep_localtime): to get the current time, we can't + call usysdep_full_time if the latter uses times. + + * sys1.unx, sys3.unx: added a couple more extern definitions for + SCO 3.2.2. + + * uucico.c (main): start uuxqt even if a call fails. + + * sysh.unx, system.h, uuxqt.c (uqdo_xqt_file), sys5.unx + (fsysdep_xqt_check_file): Chip Salzenberg: added configuration + option ALLOW_FILENAME_ARGUMENTS to permit arguments that look like + filenames, to allow undoing the patch I just made. + +Fri Jan 3 00:44:59 1992 Ian Lance Taylor (ian at comton.airs.com) + + * system.h, uuxqt.c (uqdo_xqt_file), sys5.unx + (fsysdep_xqt_check_file): David J. Fiander: make sure that if an + argument looks like a filename we are permitted to access it. + + * sys3.unx (fsdo_lock): remove temporary file if link fails in + fsdo_lock. + +Thu Jan 2 00:01:53 1992 Ian Lance Taylor (ian at comton.airs.com) + + * protg.c (fgstart, fgshutdown, fgprocess_data): count remote + rejections separately from resent packets when counting errors. + +Tue Dec 31 14:31:38 1991 Ian Lance Taylor (ian at comton.airs.com) + + * protg.c (fgstart): Franc,ois Pinard: forgot to initialize + cGdelayed_packets. + + * prot.h, uucico.c, prote.c: added the 'e' protocol, creating the + new file prote.c + + * prot.c (freceive_data), protf.c (fffile), prott.c (ftfile): + changed pffile protocol entry point to pass number of bytes being + sent; fixed bug in freceive_data which caused to ask for the wrong + number of bytes when the buffer was empty. + +Mon Dec 30 23:16:48 1991 Ian Lance Taylor (ian at comton.airs.com) + + * sys2.unx (fsserial_open): Chip Salzenberg: don't turn on IXON + and IXOFF initially; after all, the initialization packets might + contain an XOFF character. + +Sun Dec 29 00:00:42 1991 Ian Lance Taylor (ian at comton.airs.com) + + * uucp.h, prot.c (fploop): John Theus: check for EOF before + reading from file to work around bug in Tektronix library. + + * protg.c (fprocess_data): don't send RR packets when an error + occurs; the other side will probably ignore them, and it may + confuse some Telebit modems. + + * sys1.unx (usysdep_localtime): don't take the address of a cast + value. + + * log.c (zldate_and_time): wasn't allocating enough buffer space. + +Sat Dec 28 01:09:58 1991 Ian Lance Taylor (ian at comton.airs.com) + + * uuxqt.c (uqdo_xqt_file): forgot to initialize zmail. + + * uucp.h, time.c, copy.c, sys1.unx, sys2.unx, sys3.unx, sys4.unx, + sys5.unx, config.c, log.c, uuxqt.c, uux.c, tstuu.c: added a bunch + of externs to decrease the number of implicit function + definitions. + + * system.h, sys1.unx (usysdep_localtime), log.c (zldate_and_time): + Lele Gaifax: put the full year in the log file, using the format + YYYY-MM-DD HH:MM:SS.HH (ending in hundredths of seconds). + + * config.c (uprocesscmds): Terry Gardner: allow a # to be quoted + in a configuration file. Also made uprocesscmds reentrant. + + * sysh.unx, sys2.unx (fsrun_chat), sys5.unx (fsysdep_execute), + tstuu.c (uchild): Monty Solomon: added HAVE_UNION_WAIT + configuration option to pass a variable of type union wait to the + wait and waitpid system calls. + + * sysh.unx, sys1.unx (usysdep_initialize): John Theus: added + HAVE_GETWD to use getwd instead of getcwd. + + * sysh.unx, sys1.unx (usysdep_full_time): added HAVE_FTIME + configuration option. + + * tstuu.c (uchild): use TIMES_TICK from sysh.unx rather than + CLK_TCK. + + * conf.h, uucp.h, util.c, getopt.c, tstuu.c: added HAVE_STRCHR and + HAVE_INDEX to conf.h. + +Fri Dec 27 01:00:41 1991 Ian Lance Taylor (ian at comton.airs.com) + + * uucico.c (fuucp): set fmasterdone correctly when running as a + slave. + + * port.c (cpshow, upshow, fport_read, fport_write, fport_io): + cleaned up debugging code by isolating out upshow and by making + cpshow handle backslash. + + * tstuu.c (uprepare_test): create spool directories by hand, to + make sure test 6 can be done and to test creating needed + directories. + + * conf.h, uucp.h, bnu.c, v2.c, chat.c, protg.c, prott.c, sysinf.c, + tcp.c, getopt.c, tstuu.c, util.c: added HAVE_MEMFNS and HAVE_BFNS + to conf.h. Changed memset calls to use bzero. + + * protg.c (fgcheck_errors, fgprocess_data): added protocol + parameter ``errors'' to set maximum number of errors permitted. + Also made fgprocess_data only reply once per batch of data. + +Thu Dec 26 17:54:54 1991 Ian Lance Taylor (ian at comton.airs.com) + + * tcp.c (ftcp_dial, itcp_port_number): Monty Solomon: cast + arguments to avoid prototype errors on NeXT. + +Mon Dec 23 00:16:19 1991 Ian Lance Taylor (ian at comton.airs.com) + + * uucp.h, sysinf.c, uucico.c (main, flogin_prompt, faccept_call), + uuchk.c (main): David Nugent: allow debugging level to be set for + a specific system. + +Sun Dec 22 15:51:10 1991 Ian Lance Taylor (ian at comton.airs.com) + + * conf.h, uucp.c, sysh.unx, tcp.c, sys1.unx, sys2.unx, sys3.unx, + sys5.unx, sys6.unx, tstuu.c: Monty Solomon: added HAVE_UNISTD_H to + conf.h for systems which don't have . Also added + externs for all functions from , which required adding + definitions for pid_t, uid_t and off_t to sysh.unx. + + * config.c, prtinf.c, time.c, uuchk.c: added externs for + strcasecmp or strncasecmp, to avoid implicit function definitions + now that I took the prototypes out of uucp.h. + + * sys3.unx (fsysdep_get_status): Franc,ois Pinard: the code added + to avoid scanf had a stupid bug. + + * uucp.h: Monty Solomon: removed prototypes for strcasecmp and + strncasecmp from uucp.h, since they may be in string.h. + +Sat Dec 21 16:04:58 1991 Ian Lance Taylor (ian at comton.airs.com) + + * uucp.h, uucico.c (ucatch), prot.c (fpsendfile_confirm, + fprecfile_confirm, ustats_failed), file.c (fsent_file, + freceived_file), log.c (ustats): Terry Gardner: record failed file + transfers in the statistics file. + + * uucico.c: change protocol ordering to 't', 'g', 'f'. + + * uucico.c (faccept_call), tstuu.c (uprepare_test): John Theus: + don't warn if the port file doesn't exist when the slave mode + uucico looks up the port. + + * sys1.unx, sys5.unx: moved fsysdep_file_exists from sys5.unx to + sys1.unx so that uucico can call it. + + * uux.c: Fran,cois Pinard: remove parentheses from ZSHELLSEPS so + that they may be used to quote arguments as documented. This + means that may not be used to start subshell, but that is no great + loss. + + * uux.c (main): use ulog to report illegal grade error message. + + * sys1.unx (fsysdep_run): use the real program name from abProgram + in the error messages in fsysdep_run. + +Thu Dec 19 19:02:28 1991 Ian Lance Taylor (ian at comton.airs.com) + + * uucico.c (fdo_call, faccept_call): Terry Gardner: put the length + of the conversation in the ``Call complete'' log file message. + + * uux.c: added space and tab to ZSHELLSEPS, because otherwise the + command was parsed to include whitespace. + + * protg.c, protf.c: Oleg Tabarovsky: added statistical logging + messages to the 'g' and 'f' protocols. These go to the main log + file right now, but I'm not sure if that's appropriate. + + * sys2.unx (fsserial_set): don't change terminal settings if we + don't have to. + + * port.c (fport_io): add complete diagnostics for fport_io so we + can see every byte that goes in or out. + + * uucico.c (fuucp): don't give user name in errors produced by + getting the next command. + + * uuxqt.c (main): don't process execute files for unknown systems. + This is not unreasonable, I hope, and it avoids errors caused by + an uninitialized sUnknown structure. + + * sys4.unx (fsysdep_get_work_init): sort the previously found + files all together so we can correctly check new files using + bsearch. + + * protf.c (pfprocess, pfprocess_data, pfwait): Franc,ois Pinard: + don't kill 'f' protocol because of an illegal byte. Also slightly + optimized the protocol to wait for up to seven characters at a + time rather than just one. + +Wed Dec 18 00:12:42 1991 Ian Lance Taylor (ian at comton.airs.com) + + * sysh.unx, sys2.unx, tstuu.c: Terry Gardner: added + USE_FOR_UNBLOCKED configuration parameter to support systems that + don't permit O_NONBLOCK and O_NDELAY to both be set. + + * tstuu.c (uchild): give the uucico processes a chance to die on + their own rather than killing them immediately. + + * uuxqt.c (main, uqdo_xqt_file): David Nugent: keep rescanning the + list of execute files until nothing can be done. Also, don't + remove the execute file if we get some sort of internal error. + + * sys4.unx (fsysdep_get_work, usysdep_get_work_free): David + Nugent: after we've processed all the work files we found + initially, rescan the work directory to pick up any that may have + come in in the meantime. Also, reset iSwork_file to 0 in + usysdep_get_work_free so that we can handle more than one system. + + * tstuu.c, uucico.c (main, fwait_for_calls, flogin_prompt): added + -l option to uucico to prompt for the login name and password once + and then exit. + +Tue Dec 17 00:24:41 1991 Ian Lance Taylor (ian at comton.airs.com) + + * uucp.h, uucico.c, uuxqt.c, uux.c, uucp.c, config.c + (uread_config), log.c (ulog): eliminated ulog_program and added + abProgram and ulog_to_file. Made log messages output to stderr + more Unix like. + + * log.c (ulog): use a fixed number of fields in a log file message + by replacing a missing system or user with '-'. + + * port.h, chat.c (renamed fchat_send to fcsend), bnu.c + (fbnu_read_dialer_info), prtinf.c (tpcomplete), sys2.unx + (fsysdep_modem_close), uuchk.c (ukshow_dialer): T. William Wells: + change the modem complete and abort strings into chat scripts. + + * sys2.unx (fsserial_open): on BSD start in RAW mode to avoid + dropping characters when we switch over. I originally thought + being able to use XON/XOFF was worth the risk; I no longer think + so. + + * tstuu.c (uprepare_test): have shell script sleep before printing + the login name to make sure the second system has finished + flushing its input buffer. + + * protg.c (fginit_sendbuffers), prott.c (ftsendcmd): David Nugent: + avoid sending confidential information by zeroing out memory + buffers before using them. + + * sysinf.c (tirequest, titransfer), prtinf.c (tpseven_bit, + tpreliable, tpdtr_toggle): Marc Unangst: several functions did not + accept true and false as boolean strings, contradicting the + documentation. + + * uucp.h, system.h, sysh.unx, sys1.unx (usysdep_full_time), file.c + (fstore_sendfile, fsent_file, fstore_recfile, freceived_file), + log.c (ustats): generate statistics in microseconds instead of + seconds for more accurate time keeping. + + * sys2.unx (fsserial_open): David Nugent: flush pending input + when a serial port is opened. This will clear out a + NO\sCARRIER string left by a previous dropped connection. + +Mon Dec 16 11:26:17 1991 Ian Lance Taylor (ian at comton.airs.com) + + * uucico.c (main), uuxqt.c (main), tstuu.c (main, uchild): David + Nugent: ignore SIGHUP in uucico and uuxqt, so that they are + unaffected by the parent shell closing down and by the remote + terminal dropping the connection. + + * bnu.c (ubnu_read_sysfiles, ubnu_read_systems, fbnu_find_port, + fbnu_read_dialer_info): Mike Bernson: ignore lines that begin with + whitespace, fix compilation error. + +Sat Dec 14 20:59:10 1991 Ian Lance Taylor (ian at comton.airs.com) + + * sys2.unx (fsserial_open): don't turn on ISTRIP initially. + + * uucp.h, sysinf.c, chat.c (icexpect), uuchk.c (ukshow_chat): + added chat-seven-bit command to allow control over whether + parity bits are stripped out of chat scripts. + + * uucp.h, port.h, chat.c (fchat, fcprogram), + config.c (tprocess_one_cmd), prtinf.c, + sysinf.c (tichat, tprocess_chat_cmd), + bnu.c (fbnu_read_dialer_info), port.c (fpdo_dial), + uucico.c (fdo_call, faccept_call) uuchk.c (ukshow_chat): + changed processing of chat commands to go through + tprocess_chat_cmd, avoiding repetition. All chat script + information is now held in an schat_info structure. + Eliminated the fchat_program function, renaming it fcprogram + and making it static to chat.c (it is now called via fchat). + Added CMDTABTYPE_PREFIX. Added INIT_CHAT macro to initialize + chat script information. Added TTYPE_CMDTABTYPE and + CARGS_CMDTABTYPE to eliminate hex constants in + tprocess_one_cmd. + + * sys5.unx (zsysdep_get_xqt): Oleg Tabarovsky: don't stop + processing files just because opendir failed on one; it could just + be because we don't have read permission. + +Fri Dec 13 17:43:52 1991 Ian Lance Taylor (ian at comton.airs.com) + + * config.c (uprocesscmds): don't continually allocate and free the + array of arguments. + +Thu Dec 12 12:46:01 1991 Ian Lance Taylor (ian at comton.airs.com) + + * prot.c (fgetcmd): Franc,ois Pinard: don't bother to give an + error if the final HY doesn't come in; apparently the MtXinu UUCP + doesn't send it. + + * chat.c (icexpect, fchat_send): Franc,ois Pinard: add some chat + script debugging messages. + + * log.c (ulog): David Nugent: move ERROR: from the start of the + line to after the date and time; this makes it easier to use awk + on log files. + + * sys3.unx (fsdo_lock), sys1.unx (usysdep_initialize): do locking + using link rather than O_CREAT | O_EXCL to avoid race conditions + and to safely run as the superuser. + + * sys3.unx (fsysdep_move_file): fcopyfile now creates the + destination file with IPRIVATE_MODE, so we don't need to chmod to + it. + + * sys1.unx (usysdep_initialize, fsysdep_other_config): set the + GID as well as the UID, in case anybody wants to run this as a + setgid package. + +Wed Dec 11 10:03:22 1991 Ian Lance Taylor (ian at comton.airs.com) + + * conf.h, uucp.h, util.c (strtol): Mark Powell: added my own + version of strtol to util.c, for systems which lack it. + + * protg.c (fgstart, fgexchange_init): if we start resending + packets during initialization, don't forget which packets we have + already seen; otherwise the other side may assume we've already + seen them while we're looking for them. + +Tue Dec 10 15:42:41 1991 Ian Lance Taylor (ian at comton.airs.com) + + * conf.h, sysh.unx, log.c (ulog, ustats), tstuu.c (uprepare_test): + Arne Ludwig: merged in Arne Ludwig's patches to support V2 and BNU + style logging, with some minor additions and changes. + + * sys1.unx, sys3.unx, sys5.unx, uux.c, uucp.c, uucico.c, copy.c, + uucp.h, system.h: create directories when necessary rather than + assuming that they exist. Added fmkdirs argument to esysdep_fopen + and fcopy_file, changing all calls. Added fpublic argument to + fsysdep_make_dirs, changing all calls. Moved fsysdep_make_dirs + and fsdirectory_exists from sys3.unx to sys1.unx. Added checks + for ENOENT in several places in sys3.unx and sys5.unx. + + * log.c, port.c (fport_open), sys2.unx (fsserial_open): added + ulog_device routine to record device name. This is currently only + used for the BNU statistics file, but more uses might arise later. + + * file.c, log.c, uucp.h: moved statistics file routines from + file.c to log.c in preparation for supporting BNU log file + routines. + +Mon Dec 9 12:00:52 1991 Ian Lance Taylor (ian at comton.airs.com) + + * bnu.c (ubnu_read_systems): Arne Ludwig: the device entry for a + system can be followed by a comma and a list of protocols. + + * sysh.unx, sys3.unx (fsdo_lock): Richard Todd: add + HAVE_V2_LOCKFILES, in which the process ID is written out in + binary. + + * uuxqt.c (uqdo_xqt_file): Richard Todd: the requestor address is + relative to the requesting system. + + * config.c (uprocesscmds, umulti_pushback): Richard Todd: each + line pushed back because of "#" is local to a particular + smulti_file structure. + + * prtinf.c (asPdialer_cmds): Richard Todd: exit the current dialer + if the special command "#" is seen. A similar thing should be put + in for ports, but it's marginally more complex. + + * config.c (uprocesscmds): Richard Todd: don't warn if the special + "#" command is unrecognized. + +Sat Dec 7 13:05:40 1991 Ian Lance Taylor (ian at comton.airs.com) + + * config.c (uprocesscmds): Franc,ois Pinard: don't limit the + number of arguments to a command! + + * chat.c (fchat): handle a chat script which consists only of a + single string. + +Fri Dec 6 16:11:29 1991 Ian Lance Taylor (ian at comton.airs.com) + + * sys5.unx (fsysdep_execute): David J. Fiander: if execve fails + with ENOEXEC, try using /bin/sh with a quoted argument. + + * uux.c (main): split arguments the way /bin/sh does. If any + shell metacharacters appears, request uuxqt to execute the command + using /bin/sh. + + * tstuu.c (uprepare_test): allow the uux to test to send a failure + message. + + * uuxqt.c (uqdo_xqt_file): don't send mail on successful execution + unless specifically requested; pay attention to the 'n' line + which requests mail on success; ignore the 'Z' line because it + now specifies the default action. + + * sys1.unx (usysdep_initialize), sys6.unx (zsysdep_add_cwd): + Franc,ois Pinard: getcwd may legitimately fail, so only give an + error if we really need the result. + + * chat.c (ccescape): Franc,ois Pinard: ccescape should never + return a negative number, since the callers don't know how to deal + with that. + +Mon Dec 2 16:26:16 1991 Ian Lance Taylor (ian at comton.airs.com) + + * bnu.c (ubnu_read_systems): Dave Buck: time strings with grades + were parsed in an endless loop! + + * sys3.unx (fsdo_lock, fsdo_unlock): the alloca when using LOCKDIR + was one byte too small. + + * config.c (tprocess_one_cmd): pass 10 to strtol rather than 0 to + avoid surprises if a leading zero is used. + + * prtinf.c (tpproto_param, tpdialer_proto_param): Niels Baggesen: + the ``protocol-parameter'' command didn't work for ports or + dialers. + +Sun Dec 1 09:46:12 1991 Ian Lance Taylor (ian at comton.airs.com) + + * tstuu.c: don't use the fd_set typedef at all. + + * tstuu.c (uprepare_test): don't read V2 or BNU configuration + files while testing. + + * bnu.c, v2.c, config.c (uread_config): David Nugent: even if the + code was compiled with HAVE_TAYLOR_CONFIG, read the V2 and BNU + configuration files if the code was compiled to support them. + + * uuchk.c (fkshow_port): Bob Izenberg: report dialer/token pairs + correctly. + +Sat Nov 30 17:40:00 1991 Ian Lance Taylor (ian at comton.airs.com) + + * tstuu.c: Bob Izenberg: copied over conditional definitions of + EAGAIN and EWOULDBLOCK from sys2.unx. + + * bnu.c (fbnu_read_dialer_info): Niels Baggesen: accept dialers + with no substitutions. + + * bnu.c (ubnu_read_systems, ubadd_perm): Niels Baggesen: don't + free up zline in ubadd_perm; in fact, changed the calling sequence + to not even pass zline in at all. + + * bnu.c (ubadd_perm): Niels Baggesen: didn't handle multiple + MACHINE= and multiple LOGNAME= values in a single Permissions + line, because it was clobbering the machine name while processing + the first logname. + + * bnu.c: Made zread and zwrite elements of sperm structure const + char * to avoid warning. + + * copy.c, sys1.unx, sys2.unx, sys3.unx, sys5.unx, tstuu.c: Niels + Baggesen: don't multiply include . Eventually there + should be a macro controlling whether it gets included at all, for + non-POSIX systems. + + * sys3.unx (fsysdep_get_status, isysdep_get_sequence): Marty + Shannon: accept a truncated status file. I also eliminated scanf + calls in sys3.unx, since that was the only place it was called; + this was to make the executables smaller for systems which cared. + + * bnu.c (ubnu_read_sysfiles): Marty Shannon: accept comment + characters in Sysfiles. + + * sysh.unx, sys3.unx: Marty Shannon: added HAVE_RENAME, put a fake + rename system call in sys3.unx. + + * prtinf.c (ffind_port): Marty Shannon: failed to handle multiple + ports in the port file since I forgot to reset my pointer + variable. + + * bnu.c (ubnu_read_systems): Marty Shannon: don't initialize the + auto array abpubdir, since old cc didn't permit initialization of + auto aggregates. + +Mon Nov 25 20:56:39 1991 Ian Lance Taylor (ian at comton.airs.com) + + * tstuu.c: Bob Denny: add definitions for FD_SET, FD_ZERO and + FD_ISSET. + + * config.c: Bob Denny: add explicit externs for strcmp and + strcasecmp. + + * sys2.unx: the fread_blocking and fwrite_blocking fields were + not getting initialized correctly in the TCP support routines. + + * sysh.unx, sys2.unx, sys5.unx, tstuu.c: Marty Shannon: added + configuration option HAVE_SYSWAIT_H. + + * bnu.c (fbnu_find_port, fbnu_read_dialer_info), v2.c + (fv2_find_port): Marty Shannon: the ireliable field of ports + and dialers was not getting initialized. + +Sun Nov 24 15:06:37 1991 Ian Lance Taylor (ian at comton.airs.com) + + * tcp.c (itcp_port_number): Michael Haberler: wasn't calling + htons if passed a numeric string. + +Sat Nov 23 13:43:52 1991 Ian Lance Taylor (ian at comton.airs.com) + + * Released version 1.01 to alt.sources and uunet + diff --git a/gnu/libexec/uucp/Makefile b/gnu/libexec/uucp/Makefile new file mode 100644 index 0000000000..9b5dcced6f --- /dev/null +++ b/gnu/libexec/uucp/Makefile @@ -0,0 +1,9 @@ +# This is the Makefile for Taylor UUCP +# $Id: Makefile,v 1.1 1993/08/05 18:22:22 conklin Exp $ + +SUBDIR= libunix libuucp libuuconf \ + cu sample \ + uuchk uucico uuconv uucp uulog uuname uupick uusched \ + uustat uuto uux uuxqt + +.include diff --git a/gnu/libexec/uucp/Makefile.inc b/gnu/libexec/uucp/Makefile.inc new file mode 100644 index 0000000000..b9bac708a1 --- /dev/null +++ b/gnu/libexec/uucp/Makefile.inc @@ -0,0 +1,31 @@ +.if exists(${.CURDIR}/../libunix/obj) +LIBUNIX= $(.CURDIR)/../libunix/obj/libunix.a +.else +LIBUNIX= $(.CURDIR)/../libunix/libunix.a +.endif + +.if exists(${.CURDIR}/../libuuconf/obj) +LIBUUCONF= $(.CURDIR)/../libuuconf/obj/libuuconf.a +.else +LIBUUCONF= $(.CURDIR)/../libuuconf/libuuconf.a +.endif + +.if exists(${.CURDIR}/../libuucp/obj) +LIBUUCP= $(.CURDIR)/../libuucp/obj/libuucp.a +.else +LIBUUCP= $(.CURDIR)/../libuucp/libuucp.a +.endif + +VERSION= 1.04 +owner= uucp +group= wheel +bindir= /usr/bin +sbindir= /usr/libexec/uucp + +# The directory to look in for new style configuration files (when +# using HAVE_TAYLOR_CONFIG). +newconfigdir= /etc/uucp + +# The directory to look in for BNU (when using HAVE_BNU_CONFIG) or +# V2 (when using HAVE_V2_CONFIG) style configuration files. +oldconfigdir= /etc/uucp diff --git a/gnu/libexec/uucp/README b/gnu/libexec/uucp/README new file mode 100644 index 0000000000..bfcd46c3f2 --- /dev/null +++ b/gnu/libexec/uucp/README @@ -0,0 +1,207 @@ +This is the README file for version 1.04 of the Taylor UUCP package. + +It was written by Ian Lance Taylor. I can be reached at ian@airs.com, +or, equivalently, uunet!cygint!airs!ian, or c/o Cygnus Support, 4th +Floor, Building 200, 1 Kendall Square, Cambridge MA, 02139, USA. + +There is a mailing list for discussion of the package. To join (or +get off) the list, send mail to taylor-uucp-request@gnu.ai.mit.edu. +Mail to this address is answered by a person, not a program. When +joining the list, make sure you include the address at which you want +to receive mail in the body of your message. To send a message to the +list, send it to taylor-uucp@gnu.ai.mit.edu. + +This package is covered by the Gnu Public License. See the file +COPYING for details. If you would like to do something with this +package that you feel is reasonable but you feel is prohibited by the +license, contact me to see if we can work it out. + +WHAT IT IS + +This is the complete source code for a Unix UUCP package. It provides +everything you need to make a UUCP connection. It includes versions +of uucico, uusched, uuxqt, uux, uucp, uustat, uulog, uuname, uuto, +uupick, and cu, as well as uuchk (a program to check configuration +files), uuconv (a program to convert from one type of configuration +file to another) and tstuu (a test harness for the package). + +The Free Software Foundation plans to make this their standard UUCP +package. + +The package currently supports the 'f', 'g' (in all window and packet +sizes), 'G', 't' and 'e' protocols, as well a Zmodem protocol and two +new bidirectional protocols. If you have a Berkeley sockets library, +it can make TCP connections. If you have TLI libraries, it can make +TLI connections. It supports a new configuration file mechanism which +I like (but other people dislike). + +The package has a few advantages over regular UUCP: + + You get the source code. + + It uses significantly less CPU time than many UUCP packages. + + You can specify a chat script to run when a system calls in, + allowing adjustment of modem parameters on a per system basis. + + You can specify failure strings for chat scripts, allowing the + chat script to fail immediately if the modem returns ``BUSY''. + + If you are talking to another instance of the package, you can use + the new bidirectional protocol for rapid data transfer in both + directions at once. You can also restrict file transfers by size + based on the time of day and who placed the call. + +On the other hand: + + It only runs on Unix. The code is carefully divided into system + dependent and system independent portions, so it should be + possible to port it to other systems. It would not be trivial. + + You don't get uuclean, uusend, uuq, uusnap, uumonitor, uutry, + uupoll, etc. If you have current copies of these programs, you + may be able to use them. Shell scripts versions of uuclean and + uutry are provided, with most, if not all, of the functionality of + the usual programs. I believe the supplied uustat program allows + you to do everything that uuq, uusnap and uumonitor do. uupoll + could be written as a shell script. + + The package does not read modemcap or acucap files, although you + can use V2 configuration files with a BNU Dialers file or a dialer + file written in my new configuration file format. + + The package cannot use SCO dialer programs directly, although it + can with a simple shell script interface. + +If you start using this package, I suggest that you join the mailing +list (see above) to keep up to date on patches and new versions. I am +also open to suggestions for improvements and modifications. + +CHANGES SINCE 1.03 + +For a complete list, see ChangeLog. + +IMPORTANT: the default when talking to another version of 1.04 is to +use the new bidirectional 'i' protocol. If you are using a +half-duplex modem, such as a Telebit T2500, you will want to either +mark the port as half-duplex with the ``half-duplex'' command, or +force use of the 'g' protocol by using the ``protocol'' command in the +sys or port file or by adding ``,g'' after the port name in the +Systems or L.sys or Devices file. + + As usual, many bugs were fixed. + + Bidirectional transfers are supported with the new 'i' protocol; + it requires an eight-bit clear datapath. + + New programs: uusched, cu, uuto and uupick. + + The 'G' protocol and a new Zmodem protocol were added. + + A number of uustat options were added to support uuclean, and a + sample uuclean shell script was added to the contrib directory. + The uustat output formats were changed slightly. + + A protocol extension eliminates transfer of the command file for + simple commands, such as rmail or rnews, when talking to another + version of 1.04. + + Some TLI support was added. + + UUCP forwarding was added, along with the ``forward-to'', + ``forward-from'' and ``forward'' commands. + + If a file transfer fails in the middle, the retry will now start + from where it left off. The implementation is compatible with + SVR4. + + The work queue is checked every 10 minutes during a conversation; + if there is new work and a bidirectional protocol is not in use, + the receiving uucico requests the sender to transfer control. + + The amount of free disk space is checked periodically as a file is + received, and if it drops too low the call is aborted. + + The UUCP configuration file reading routines were moved into a + standalone library, uuconf. All known bugs in V2 and HDB + configuration file reading were fixed. + + The ``half-duplex'' command was added for the port and dialer + files. + + The ``max-retries'', ``success-wait'', ``send-request'' and + ``receive-request'' commands were added for the sys file. The + ``call-request'' and ``called-request'' commands were eliminated + (they did not work correctly anyhow). + + \d in chat scripts now calls sleep (2) rather than sleep (1), so + it will sleep longer (on some systems sleep(1) may delay much less + than one second). + + SPOOLDIR_SVR4 was added for SVR4 style spool directories. + + Defaults are now permitted in the port and dialer files. + + The ALIAS field is supported in the HDB Permissions file. + +DOCUMENTATION + +The documentation is in the file uucp.texi, which is a Texinfo file. +Texinfo is a format used by the Free Software Foundation. You can +print the documentation using TeX in combination with the file +texinfo.tex. DVI, PostScript and info versions of the documentation +are available in a separate package, uucp-doc-1.04.tar.Z. + +See the TODO file for things which should be done. Please feel free +to do them, although you may want to check with me first. Send me +suggestions for new things to do. + +The compilation instructions are in uucp.texi. Here is a summary. + + Edit Makefile.in to set installation directories. + + Type ``sh configure''. You can pass a number of arguments in the + environment (using bash or sh, enter something like ``CC=gcc + configure''; using csh, enter something like ``setenv CC gcc; sh + configure''): + CC: C compiler to use; default is gcc if it exists, else cc + CFLAGS: Flags to pass to $CC when compiling; default -g + LDFLAGS: Flags to pass to $CC when only linking; default none + LIBS: Library arguments to pass to $CC; default none + INSTALL: Install program; default install -c or cp + INSTALLDATA: Install data; default install -c -m 0644 or cp + The configure script will compile a number of test programs to see + what is available on your system, so if your system is at all + unusual you will need to pass in $CC and $LIBS correctly. + + The configure script will create conf.h from conf.h.in and + Makefile from Makefile.in. It will also create config.status, + which is a shell script which actually creates the files. Please + report any configuration problems, so that they can be fixed in + later versions. + + Igor V. Semenyuk provided this (lightly edited) note about ISC + Unix 3.0. The configure script will default to passing -posix to + gcc. However, using -posix changes the environment to POSIX, and + on ISC 3.0, at least, the default for POSIX_NO_TRUNC is 1. This + means nothing for uucp, but can lead to a problem when uuxqt + executes rmail. IDA sendmail has dbm configuration files named + mailertable.{dir,pag}. Notice these names are 15 characters long. + When uuxqt compiled with -posix executes rmail, which in turn + executes sendmail, the later is run under POSIX environment too! + This leads to sendmail bombing out with 'error opening 'M' + database: name too long' (mailertable.dir). It's rather obscure + behaviour, and it took me a day to find out the cause. I don't + use -posix, instead I run gcc with -D_POSIX_SOURCE, and add + -lcposix to LIBS. + + Examine conf.h and Makefile to make sure they're right. + + Edit policy.h for your local system. + + Type ``make''. + + Use ``uuchk'' to check configuration files. You can use + ``uuconv'' to convert between configuration file formats. + + Type ``make install'' to install. diff --git a/gnu/libexec/uucp/TODO b/gnu/libexec/uucp/TODO new file mode 100644 index 0000000000..a1cc643ab5 --- /dev/null +++ b/gnu/libexec/uucp/TODO @@ -0,0 +1,573 @@ +This is a list of things to do for the Taylor UUCP package. Please +feel free to work on any of them. You may want to check with me first +to make sure that nobody else is working on them as well. + +Some of these are my thoughts, but most are suggestions from other +people; I have tried to give credit. They are in the order I received +them; the missing numbers have already been implemented. + +Just because something is on the list doesn't mean that I necessarily +think it is a good idea. It does mean that I think it's worth +thinking about. + +2. + +John Cowan says: + +>I think you should accept a broader range of time specifications. +>Consider using getdate() (from your handy Usenet news source code) +>with its high-powered yacc parser. + +Of course, getdate() accepts a single date, but we want a range. A +better syntax would be certainly be nice. + +9. + +Gordon Burditt warns about modifications +to the TZ environment variable, to fool uucico into dialing out at an +inappropriate time. + +10. + +Gordon Burditt says: + +>(4) Less important, because few people will have this problem, is a +>port-specific dialcodes file. Why? Well, one system I had was connected +>to 2 inside lines "dial 9 for outside line", and one outside line (which +>doesn't want the 9). A number of the systems called were "inside", so +>you didn't add the 9 on those lines dialing from inside, but you did add +>"390" to the 4-digit number if you dialed it via "outside". Also not +>unheard of are systems with 2 outside lines that are local to different +>area codes, or one local outside line and one WATS line (which MUST +>have an area code). +>Example: +> inside-line Dialcodes outside-line Dialcodes +> pbx "" pbx "390" +> local "9" local "" +> nyc "9-1212" nyc "1212" + +12. + +Ralf E. Stranzenbach says: + +>It would be nice to also have the option of running a shell script each time +>uucico connects/disconnects a systen. I do not mean shell scripts for dial/in. +>I would like to do some accounting and batching when the connection +>establishes. + +13. + +les@chinet.chi.il.us (Leslie Mikesell) writes: + +>>local-send /usr/spool/uucppublic !/usr/spool/uucpublic/private +>> +>>The directories are searched from left to right, and the last one to +>>match determines whether the file may be sent or not. This is +>>slightly more general than NOWRITE, since it permits a public +>>directory within a private directory within a public directory, +>>although probably nobody will ever want that. +> +>Interesting... The obvious enhancement is to generalize to shell-like +>wild cards for the READ/WRITE/COMMANDS entries. + +14. + +Should there be a way for chat scripts to specify the parity to +generate? I don't think there's much point to specifying what parity +to accept. + +17. + +The -b and -s switches to uux are not implemented by uuxqt. + +18. + +If we are supposed to call a system back, we should do it immediately +rather than merely queuing up an empty command file. + +22. + +Add an ftp port type which uses anonymous ftp rather than any of the +UUCP protocols to do file transfers. This would allow ftp work to be +done late at night, and allow neighbors of cooperative Internet sites +to use UUCP forwarding for anonymous FTP. + +31. + +David Nugent: add a -C option to uucico to only call the system if +there is work to do. + +32. + +It would be nice if uucico could sleep until a line was available. +This is complicated by the possibility of wanting to wait for any of +several different lines, and one would really want some sort of +semaphore function to do it right. If the available lines could be +sorted, then each could be assigned to a byte in a line lock file. +Looking for a line could be done by sleeping on a read lock on all +possible lines. Once it came through, write locks would be attempted. +If they all failed, somebody else snuck in, so you would sleep on a +read lock again. This isn't great because a process could be starved, +but it might be better than nothing. + +This could be tied in to uucp and uux, such that they wouldn't +actually fire up uucico unless a line was known to be available; an +additional switch would be used to fire up uucico anyhow (or one could +switch the default behaviour and the switch). + +So how do you sort the lines? You could just use the index in the +port (or Devices) file, but what if multiple ports used the same +physical device? Hmmm. + +43. + +David Nugent: it would be nice to be able to set debugging, log, and +statistics files on a site by site basis. +Brian Murrell: heck, set those files on a port by port basis as well. + +74. + +Yanek Martinson: allow each system to independently choose whether to +permit shell execution. + +81. + +Marty Shannon: log reason for dial failure (chat-fail string) in +.Status file. + +83. + +Switch between 'M' and 'S' correctly in the BNU log file output. + +86. + +Les Mikesell: allow a separate program to be specified to handle the +communications with a particular system. + +105. + +T. William Wells: close and open the Debug file after each file +transfer. Alternatively, cycle through a series of Debug file names +every 1000 lines or so. + +106. + +Marty Shannon: add a time command for ports, to specify when they may +be used. + +115. + +T. William Wells: new options for uustat: + -i display job ids only +Also, there should perhaps be a configuration option to request uustat +to only display jobs submitted by the user running uustat, except for +root and uucp. + +117. + +Marc Unangst: provide some way to change the debugging level of a +running uucico. T. William Wells suggests having it read a file to +change arbitrary configuration information, although obviously one has +to be careful of what gets changed while a connection is active. + +120. + +Jarmo Raiha: new chat-fail commands: one to not update the status file +and require a retry wait, and one to permit the string to occur a few +times before reporting an error. + +124. + +Peter da Silva: perhaps there should be a ``chat-end-program'' command +to let a program be run after the initial handshake has been completed +and the protocol has been selected and turned on. This would let +people run stty to change their terminal parameters. + +128. + +Richard Stallman: have an interactive program to set up a chat script. +It would let you type directly to the port, recording what you type as +send strings and recording what comes back from the other side as +expect strings. + +129. + +Use POSIX fcntl locks when possible instead of creating a lock file. + +130. + +Chip Salzenberg: BSD lets you override the timeout for a particular +expect string by using a trailing ~. + +138. + +T. William Wells: BNU apparently uses a file named A.whatever to hold +the line number reached in current C. file processing. This is a +hack, and won't work right with size control anyhow, but +fsysdep_did_work could, for example, clobber the first byte in the +line to a # or something to mark that it had been finished. Still a +hack, but a better one. + +139. + +Patrick Smith: incorporate patches to generate full debugging traces +with less debugging file overhead. The debugging file repeats too +much information at great length right now--not good. + +141. + +Franc,ois Pinard: batch up pauses and delays in chat scripts and do +them all at once in a single system call. This is particularly useful +for pauses on systems which don't support subsecond sleeps. For +everything else it's a fairly minor optimization. + +142. + +Franc,ois Pinard: give uustat an option to requeue jobs to another +system. This only makes a lot of sense for rmail executions, but it's +fairly easy to do for any type of command. I think uucico does all +the file checking needed to ensure that this doesn't break security, +but that should be double-checked. + +144. + +T. William Wells: add a -g option to uucico to permit specifying the +maximum grade to be transferred at that time. This could restrict the +timegrade command further, but should not be permitted to override it. + +145. + +T. William Wells: if uucico or uuxqt get started with bad arguments, +put an indication in the log file since stderr may be /dev/null. + +146. + +Richard Todd: it would be nice to sometimes be able to request the +other side to turn on debugging. + +147. + +Bart Schaefer: some more possible options for uucico: + -R reverse roles (hangup immediately). Not too exciting. + some method to restrict calling to particular systems. + +148. + +Jarmo Raiha: some method to control the work queue at the remote end. +This could get awfully general, though. + +149. + +The interaction of the time command and defaults can be confusing, +since any time command in the actual system entry, even a fairly +specific one, will wipe out the default entry. Not sure what can be +done about this. + +150. + +Jarmo Raiha: should there be some way to specify modem initialization +strings when uucico is hanging on a port with -l or -e? This would +presumably require a new type of chat script associated with a dialer. + +151. + +Petri Helenius: log complete CONNECT string reported by modem, so that +the baud rate is recorded in the log file. + +152. + +Marc Evans: let the protocol selection be based on the CONNECT string, +so that different protocols could be selected based on what type of +connection was made. + +153. + +Chris Lewis: provide a signal to get a core dump even on systems which +won't do core dumps if the uid is not the euid. One could catch a +signal, call setuid (getuid ()), and then raise the signal again. +Unfortunately the core dump has to wind up in a directory which is +world writable, so that the process is able to create the core file, +but is not world readable, since that would permit anybody to read the +core dump file and extract private information from it. + +154. + +Les Mikesell: write a new version of dial.o, with provisions for +running a chat script. + +155. + +Scott Blachowicz: perhaps there should be some way to telling uucico +to not log certain errors. This could get fairly complex, though. + +156. + +Franc,ois Pinard: have uustat -m report the time of the last +successful conversation when reporting a failure. + +158. + +Thomas Fischer: should there be a way to completely disable an entry +in the sys, port or dial file? Such as a ``disable'' command? + +159. + +Petri Helenius: when uuxqt -s is invoked, lock uuxqt for the system so +that only one uuxqt is invoked per system. If the -c option is used, +don't lock on a per system basis, and ignore any per system locks +(regardless of -s). If neither option is used, respect existing +system and command locks, and do any other type of file. + +161. + +Scott Blachowicz: provide some sort of include mechanism for the +configuration files. + +162. + +Chris Lewis: add uuxqtpolicy command, probably in config, supporting +the following values which determine when uuxqt should be run: + - never (let cron or something else worry about it) + - perinvocation (when uucico exits for good - current behaviour) + - persite (when uucico terminates a conversation - HDBish) + - periodic (per 5 or 10 incoming X. files - BSDish) + - perturnaround? + +163. + +Sort jobs in the send queue by size. Pretty easy. + +164. + +Ed Carp: preserve files if uuxqt execution fails. + +165. + +Marc Sheldon: use exit codes from in uux and uucp. + +166. + +Chip Salzenberg: allow chat failure strings to specify a retry time. + +167. + +Gregory Bond: allow a dialer sequence for a TCP port, so you can make +a TCP connection to a modem and then dial out. + +168. + +Jose A. Manas: allow a maximum connect time, after which we try to +hang up the connection. This requires a protocol extension, since +there's no way to force the other side to hang up. The best we can do +without an extension is refuse to send any new jobs ourselves. Of +course, we could just drop the connection. + +169. + +Franc,ois Pinard: when given uustat -k00FC, check each possible job ID +and use it if there is an unambiguous one. + +170. + +T. William Wells: if ! HAVE_SETREUID && ! HAVE_SAVED_SETUID, fork a +subprocesses to revoke setuid and read the file over a pipe. + +171. + +Provide some option to have the internal uuconf functions not start +with an underscore. + +172. + +T. William Wells: have some way to configure the parity for cu. + +173. + +Gert Doering: uuchk should display unknown system information. + +175. + +T. William Wells: +Cu will not let itself be interrupted before the connection is +established. If the chat script doesn't write something, cu does +something odd, I've forgotten exactly what. Cu takes an +inordinate amount of time after the line drops to exit. Somebody, +cu, I think, but maybe uucico, drops dtr twice sometimes. Again, +somebody will attempt to write after a hangup signal has been +received. Once a hangup has been received, I/O should not be +attempted. Among other things this will save the bacon of those +who have brain damaged serial drivers (FAS, sigh, is among them) +that don't handle output properly on a dropped line. + +Me: +Note that sometimes you do want to write to a line after receiving a +hangup signal. For example, you might want to use ATZ to reset a +modem. + +176. + +Hans-Dieter Doll: provide some way (another escape sequence) to pass +the protocol to a chat-program. Or, allow the protocol as an argument +to the chat script command, which is more general, but maybe a bit too +fancy. + +177. + +Nickolay Saukh: use a default port for cu, you can just do ``cu +number''. + +178. + +Don Phillips: should there be some way to restrict of grade of +transfers even when the other system places the call? + +179. + +Nickolay Saukh: add something to chat scripts to specify the timeout +for an expect string, e.g. AT\c OK\W3 to wait for 3 seconds. Except +that perhaps the unit should not be seconds. Berkeley apparently uses +~number, not \W number, but I don't see any reason to prevent use of +the ~ character in an expect string. + +180. + +Nickolay Saukh: if we have received a partial file, request the remote +system to start sending from that point. We currently accept SVR4 +style remote file positioning requests, but we do not generate them. + +181. + +Mark Powell: provide some way to restrict file transfer by size as +well as grade? One way would be to let uux select the grade based on +the file size. + +182. + +Mark Powell: permit using multiple timetables in a single time +statement. + +183. + +Optionally check for interrupts in fcopy_file, since it can take a +long time to copy a file named in a uucp request. + +184. + +Ian Moran: if an attempt is made to a copy a file to a directory which +denies write permission, perhaps the file should be saved somewhere. +It must be saved in a private location, though. + +185. + +A syntax error in a command received from the remote system should not +hold up the queue. Unfortunately, I don't know what can be done +except deny the command and report it. Reporting a garbled command +error should report the command correctly, rather than just the first +character. + +186. + +Franc,ois Pinard: have an option to control nostop vs. stop on the cu +command line. + +187. + +Fix the notion of %nostop to be SVID compatible. + +188. + +Frank Conrad: provide a means to set the strip mode for a port, to +make it easy to use it from cu. + +189. + +Marc Unangst: there should be a way to specify that a system should +only be called if there are jobs of a certain grade, but if the system +is called then jobs of any grade should be transferred. This +basically means splitting the ``timegrade'' command into two commands: +``place-call-timegrade'' and ``transfer-timegrade''. Or maybe another +optional argument to ``timegrade'': + timegrade grade time-string [retry] [transfer-any] +not to mention + time time-string [retry] [transfer-any] +Or maybe a separate command for a system or port like + transfer-any BOOL + +190. + +Chip Salzenberg: it would be really nice if uucico could automatically +figure out when it could use an E command, so that uux didn't have to +generate it and so that uucico could use with other versions of uux. +Unfortunately, it would require uucico to read the execution file to +see if it were suitable; this would be complex, but it would probably +be worth it since normally the execution file would wind up not being +sent. Of course, the current method works too; it's just harder to +combine with other versions of UUCP. + +191. + +Brian J. Murrell: should there be a way to cu a specific alternate? + +192. + +Andrew A. Chernov: Perhaps cu -pport system should be able to try +different alternates for the system, because there might be different +phone numbers to try. + +193. + +Brian J. Murrell: it would be nice to be able to ^C a cu chat script +if you know it's going to fail. Right now you have to use ^\. + +194. + +Steven S. Dick: have some way to force uucico off the phone at a +certain time. If that is done, it might be cool to have some way to +predict how long a file transfer will take, and not do it if it will +take too long. But, if doing file restart, you can just quit and then +pick it up later. + +195. + +Franc,ois Pinard: if the disk fills up, or some other error occurs, +while receiving a file, perhaps it would make sense to turn the +connection around immediately and see if the other side had anything +to do, and then try again later. This would require a protocol +extension. I don't know if it's worth it. The code should be checked +to see how well it handles a disk full situation. + +196. + +For real adjustability, provide some mechanism for picking the lead +characters to use for the shell scripts, between : and #!. + +197. + +Try alternate IP addresses if there are any. + +198. + +Lele Gaifax: mention the device in Stats, and provide some way to +associate the entry in Log with the entry in Stats. + +199. + +Michael Richardson: provide some way to turn on parity for the login +chat, since some systems apparently require it. Provide some way for +cu to control parity after connecting. + +200. + +Chip Salzenberg: add max-remote-debug to config. + +201. + +Gert Doering: change the timeout message in chat scripts to reflect +which chat script timed out (dialer or login). + +202. + +Bill Foote: have uuchk check whether a system is defined more than +once. + +203. diff --git a/gnu/libexec/uucp/VERSION b/gnu/libexec/uucp/VERSION new file mode 100644 index 0000000000..99fd8f0eef --- /dev/null +++ b/gnu/libexec/uucp/VERSION @@ -0,0 +1,4 @@ +Version 1.04 + +a complete, unmodified version of this program is available from +prep.ai.mit.edu. diff --git a/gnu/libexec/uucp/common_sources/chat.c b/gnu/libexec/uucp/common_sources/chat.c new file mode 100644 index 0000000000..86a68d9b94 --- /dev/null +++ b/gnu/libexec/uucp/common_sources/chat.c @@ -0,0 +1,1429 @@ +/* chat.c + Chat routine for the UUCP package. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char chat_rcsid[] = "$Id: chat.c,v 1.1 1993/08/04 19:30:29 jtc Exp $"; +#endif + +#include +#include + +#include "uudefs.h" +#include "uuconf.h" +#include "conn.h" +#include "prot.h" +#include "system.h" + +/* Local functions. */ + +static int icexpect P((struct sconnection *qconn, int cstrings, + char **azstrings, size_t *aclens, + int ctimeout, boolean fstrip)); +static boolean fcsend P((struct sconnection *qconn, pointer puuconf, + const char *zsend, + const struct uuconf_system *qsys, + const struct uuconf_dialer *qdial, + const char *zphone, + boolean ftranslate, boolean fstrip)); +static boolean fcecho_send_strip P((struct sconnection *qconn, + const char *z, size_t clen)); +static boolean fcecho_send_nostrip P((struct sconnection *qconn, + const char *z, size_t clen)); +static boolean fcecho_send P((struct sconnection *qconn, const char *z, + size_t clen, boolean fstrip)); +static boolean fcphone P((struct sconnection *qconn, + pointer puuconf, + const struct uuconf_dialer *qdial, + const char *zphone, + boolean (*pfwrite) P((struct sconnection *qc, + const char *zwrite, + size_t cwrite)), + boolean ftranslate, boolean *pfquote)); +static boolean fctranslate P((pointer puuconf, const char *zphone, + const char **pzprefix, + const char **pzsuffix)); +static boolean fcprogram P((struct sconnection *qconn, pointer puuconf, + char **pzprogram, + const struct uuconf_system *qsys, + const struct uuconf_dialer *qdial, + const char *zphone, const char *zport, + long ibaud)); + +/* Run a chat script with the other system. The chat script is a + series of expect send pairs. We wait for the expect string to show + up, and then we send the send string. The chat string for a system + holds the expect and send strings separated by a single space. */ + +boolean +fchat (qconn, puuconf, qchat, qsys, qdial, zphone, ftranslate, zport, ibaud) + struct sconnection *qconn; + pointer puuconf; + const struct uuconf_chat *qchat; + const struct uuconf_system *qsys; + const struct uuconf_dialer *qdial; + const char *zphone; + boolean ftranslate; + const char *zport; + long ibaud; +{ + int cstrings; + char **azstrings; + size_t *aclens; + char **pzchat; + char *zbuf; + size_t cbuflen; + boolean fret; + int i; + + /* First run the program, if any. */ + if (qchat->uuconf_pzprogram != NULL) + { + if (! fcprogram (qconn, puuconf, qchat->uuconf_pzprogram, qsys, qdial, + zphone, zport, ibaud)) + return FALSE; + } + + /* If there's no chat script, we're done. */ + if (qchat->uuconf_pzchat == NULL) + return TRUE; + + if (qchat->uuconf_pzfail == NULL) + { + cstrings = 1; + azstrings = (char **) xmalloc (sizeof (char *)); + aclens = (size_t *) xmalloc (sizeof (size_t)); + } + else + { + char **pz; + + /* We leave string number 0 for the chat script. */ + cstrings = 1; + for (pz = qchat->uuconf_pzfail; *pz != NULL; pz++) + ++cstrings; + + azstrings = (char **) xmalloc (cstrings * sizeof (char *)); + aclens = (size_t *) xmalloc (cstrings * sizeof (size_t)); + + /* Get the strings into the array, and handle all the escape + characters. */ + for (cstrings = 1, pz = qchat->uuconf_pzfail; + *pz != NULL; + cstrings++, pz++) + { + azstrings[cstrings] = zbufcpy (*pz); + aclens[cstrings] = cescape (azstrings[cstrings]); + } + } + + cbuflen = 0; + zbuf = NULL; + fret = TRUE; + + pzchat = qchat->uuconf_pzchat; + + while (*pzchat != NULL) + { + size_t clen; + + /* Loop over subexpects and subsends. */ + while (TRUE) + { + /* Copy the expect string into the buffer so that we can + modify it in cescape. */ + clen = strlen (*pzchat); + if (clen >= cbuflen) + { + ubuffree (zbuf); + zbuf = zbufalc (clen + 1); + cbuflen = clen; + } + memcpy (zbuf, *pzchat, clen + 1); + + azstrings[0] = zbuf; + if (azstrings[0][0] == '-') + ++azstrings[0]; + aclens[0] = cescape (azstrings[0]); + + if (aclens[0] == 0 + || (aclens[0] == 2 + && strcmp (azstrings[0], "\"\"") == 0)) + { + /* There is no subexpect sequence. If there is a + subsend sequence we move on to it. Otherwise we let + this expect succeed. This is somewhat inconsistent, + but it seems to be the traditional approach. */ + if (pzchat[1] == NULL || pzchat[1][0] != '-') + break; + } + else + { + int istr; + + istr = icexpect (qconn, cstrings, azstrings, aclens, + qchat->uuconf_ctimeout, + qchat->uuconf_fstrip); + + /* If we found the string, break out of the + subexpect/subsend loop. */ + if (istr == 0) + break; + + /* If we got an error, return FALSE. */ + if (istr < -1) + { + fret = FALSE; + break; + } + + /* If we found a failure string, log it and get out. */ + if (istr > 0) + { + ulog (LOG_ERROR, "Chat script failed: Got \"%s\"", + qchat->uuconf_pzfail[istr - 1]); + fret = FALSE; + break; + } + + /* We timed out; look for a send subsequence. If none, + the chat script has failed. */ + if (pzchat[1] == NULL || pzchat[1][0] != '-') + { + ulog (LOG_ERROR, "Timed out in chat script"); + fret = FALSE; + break; + } + } + + /* Send the send subsequence without the leading '-'. A + \"\" will send nothing. An empty string will send a + carriage return. */ + ++pzchat; + if (! fcsend (qconn, puuconf, *pzchat + 1, qsys, qdial, zphone, + ftranslate, qchat->uuconf_fstrip)) + { + fret = FALSE; + break; + } + + /* If there is no expect subsequence, we are done. */ + if (pzchat[1] == NULL || pzchat[1][0] != '-') + break; + + /* Move on to next expect subsequence. */ + ++pzchat; + } + + if (! fret) + break; + + /* Move on to the send string. If there is none, we have + succeeded. */ + do + { + ++pzchat; + } + while (*pzchat != NULL && (*pzchat)[0] == '-'); + + if (*pzchat == NULL) + break; + + if (**pzchat != '\0') + { + if (! fcsend (qconn, puuconf, *pzchat, qsys, qdial, zphone, + ftranslate, qchat->uuconf_fstrip)) + { + fret = FALSE; + break; + } + } + + ++pzchat; + } + + ubuffree (zbuf); + for (i = 1; i < cstrings; i++) + ubuffree (azstrings[i]); + xfree ((pointer) azstrings); + xfree ((pointer) aclens); + + return fret; +} + +/* Read characters and wait for one of a set of memory strings to come + in. This returns the index into the array of the string that + arrives, or -1 on timeout, or -2 on error. */ + +static int +icexpect (qconn, cstrings, azstrings, aclens, ctimeout, fstrip) + struct sconnection *qconn; + int cstrings; + char **azstrings; + size_t *aclens; + int ctimeout; + boolean fstrip; +{ + int i; + size_t cmax; + char *zhave; + size_t chave; + long iendtime; +#if DEBUG > 1 + int cchars; + int iolddebug; +#endif + + cmax = aclens[0]; + for (i = 1; i < cstrings; i++) + if (cmax < aclens[i]) + cmax = aclens[i]; + + zhave = zbufalc (cmax); + chave = 0; + + iendtime = ixsysdep_time ((long *) NULL) + ctimeout; + +#if DEBUG > 1 + cchars = 0; + iolddebug = iDebug; + if (FDEBUGGING (DEBUG_CHAT)) + { + udebug_buffer ("icexpect: Looking for", azstrings[0], + aclens[0]); + ulog (LOG_DEBUG_START, "icexpect: Got \""); + iDebug &=~ (DEBUG_INCOMING | DEBUG_PORT); + } +#endif + + while (TRUE) + { + int bchar; + + /* If we have no more time, get out. */ + if (ctimeout <= 0) + { +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_CHAT)) + { + ulog (LOG_DEBUG_END, "\" (timed out)"); + iDebug = iolddebug; + } +#endif + ubuffree (zhave); + return -1; + } + + /* Read one character at a time. We could use a more complex + algorithm to read in larger batches, but it's probably not + worth it. If the buffer is full, shift it left; we already + know that no string matches, and the buffer holds the largest + string, so this can't lose a match. */ + if (chave >= cmax) + { + size_t imove; + + for (imove = 0; imove < cmax - 1; imove++) + zhave[imove] = zhave[imove + 1]; + --chave; + } + + /* The timeout/error return values from breceive_char are the + same as for this function. */ + bchar = breceive_char (qconn, ctimeout, TRUE); + if (bchar < 0) + { +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_CHAT)) + { + /* If there was an error, it will probably be logged in + the middle of our string, but this is only debugging + so it's not a big deal. */ + ulog (LOG_DEBUG_END, "\" (%s)", + bchar == -1 ? "timed out" : "error"); + iDebug = iolddebug; + } +#endif + ubuffree (zhave); + return bchar; + } + + /* Strip the parity bit if desired. */ + if (fstrip) + bchar &= 0x7f; + + zhave[chave] = (char) bchar; + ++chave; + +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_CHAT)) + { + char ab[5]; + + ++cchars; + if (cchars > 60) + { + ulog (LOG_DEBUG_END, "\""); + ulog (LOG_DEBUG_START, "icexpect: Got \""); + cchars = 0; + } + (void) cdebug_char (ab, bchar); + ulog (LOG_DEBUG_CONTINUE, "%s", ab); + } +#endif + + /* See if any of the strings can be found in the buffer. Since + we read one character at a time, the string can only be found + at the end of the buffer. */ + for (i = 0; i < cstrings; i++) + { + if (aclens[i] <= chave + && memcmp (zhave + chave - aclens[i], azstrings[i], + aclens[i]) == 0) + { +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_CHAT)) + { + if (i == 0) + ulog (LOG_DEBUG_END, "\" (found it)"); + else + { + ulog (LOG_DEBUG_END, "\""); + udebug_buffer ("icexpect: Found", azstrings[i], + aclens[i]); + } + iDebug = iolddebug; + } +#endif + ubuffree (zhave); + return i; + } + } + + ctimeout = (int) (iendtime - ixsysdep_time ((long *) NULL)); + } +} + +#if DEBUG > 1 + +/* Debugging function for fcsend. This takes the fquote variable, the + length of the string (0 if this an informational string which can + be printed directly) and the string itself. It returns the new + value for fquote. The fquote variable is TRUE if the debugging + output is in the middle of a quoted string. */ + +static size_t cCsend_chars; +static int iColddebug; + +static boolean fcsend_debug P((boolean, size_t, const char *)); + +static boolean +fcsend_debug (fquote, clen, zbuf) + boolean fquote; + size_t clen; + const char *zbuf; +{ + size_t cwas; + + if (! FDEBUGGING (DEBUG_CHAT)) + return TRUE; + + cwas = cCsend_chars; + if (clen > 0) + cCsend_chars += clen; + else + cCsend_chars += strlen (zbuf); + if (cCsend_chars > 60 && cwas > 10) + { + ulog (LOG_DEBUG_END, "%s", fquote ? "\"" : ""); + fquote = FALSE; + ulog (LOG_DEBUG_START, "fcsend: Writing"); + cCsend_chars = 0; + } + + if (clen == 0) + { + ulog (LOG_DEBUG_CONTINUE, "%s %s", fquote ? "\"" : "", zbuf); + return FALSE; + } + else + { + int i; + + if (! fquote) + ulog (LOG_DEBUG_CONTINUE, " \""); + for (i = 0; i < clen; i++) + { + char ab[5]; + + (void) cdebug_char (ab, zbuf[i]); + ulog (LOG_DEBUG_CONTINUE, "%s", ab); + } + + return TRUE; + } +} + +/* Finish up the debugging information for fcsend. */ + +static void ucsend_debug_end P((boolean, boolean)); + +static void +ucsend_debug_end (fquote, ferr) + boolean fquote; + boolean ferr; +{ + if (! FDEBUGGING (DEBUG_CHAT)) + return; + + if (fquote) + ulog (LOG_DEBUG_CONTINUE, "\""); + + if (ferr) + ulog (LOG_DEBUG_CONTINUE, " (error)"); + + ulog (LOG_DEBUG_END, "%s", ""); + + iDebug = iColddebug; +} + +#else /* DEBUG <= 1 */ + +/* Use macro definitions to make fcsend look neater. */ + +#define fcsend_debug(fquote, clen, zbuf) TRUE + +#define ucsend_debug_end(fquote, ferror) + +#endif /* DEBUG <= 1 */ + +/* Send a string out. This has to parse escape sequences as it goes. + Note that it handles the dialer escape sequences (\e, \E, \D, \T) + although they make no sense for chatting with a system. */ + +static boolean +fcsend (qconn, puuconf, z, qsys, qdial, zphone, ftranslate, fstrip) + struct sconnection *qconn; + pointer puuconf; + const char *z; + const struct uuconf_system *qsys; + const struct uuconf_dialer *qdial; + const char *zphone; + boolean ftranslate; + boolean fstrip; +{ + boolean fnocr; + boolean (*pfwrite) P((struct sconnection *, const char *, size_t)); + char *zcallout_login; + char *zcallout_pass; + boolean fquote; + + if (strcmp (z, "\"\"") == 0) + return TRUE; + + fnocr = FALSE; + pfwrite = fconn_write; + zcallout_login = NULL; + zcallout_pass = NULL; + +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_CHAT)) + { + ulog (LOG_DEBUG_START, "fcsend: Writing"); + fquote = FALSE; + cCsend_chars = 0; + iColddebug = iDebug; + iDebug &=~ (DEBUG_OUTGOING | DEBUG_PORT); + } +#endif + + while (*z != '\0') + { + const char *zlook; + boolean fsend; + char bsend; + + zlook = z + strcspn ((char *) z, "\\BE"); + + if (zlook > z) + { + size_t c; + + c = zlook - z; + fquote = fcsend_debug (fquote, c, z); + if (! (*pfwrite) (qconn, z, c)) + { + ucsend_debug_end (fquote, TRUE); + return FALSE; + } + } + + if (*zlook == '\0') + break; + + z = zlook; + + fsend = FALSE; + switch (*z) + { + case 'B': + if (strncmp (z, "BREAK", 5) == 0) + { + fquote = fcsend_debug (fquote, (size_t) 0, "break"); + if (! fconn_break (qconn)) + { + ucsend_debug_end (fquote, TRUE); + return FALSE; + } + fnocr = TRUE; + z += 5; + } + else + { + fsend = TRUE; + bsend = 'B'; + ++z; + } + break; + case 'E': + if (strncmp (z, "EOT", 3) == 0) + { + fsend = TRUE; + bsend = '\004'; + fnocr = TRUE; + z += 3; + } + else + { + fsend = TRUE; + bsend = 'E'; + ++z; + } + break; + case '\\': + ++z; + switch (*z) + { + case '-': + fsend = TRUE; + bsend = '-'; + break; + case 'b': + fsend = TRUE; + bsend = '\b'; + break; + case 'c': + fnocr = TRUE; + break; + case 'd': + fquote = fcsend_debug (fquote, (size_t) 0, "sleep"); + usysdep_sleep (2); + break; + case 'e': + fquote = fcsend_debug (fquote, (size_t) 0, "echo-check-off"); + pfwrite = fconn_write; + break; + case 'E': + fquote = fcsend_debug (fquote, (size_t) 0, "echo-check-on"); + if (fstrip) + pfwrite = fcecho_send_strip; + else + pfwrite = fcecho_send_nostrip; + break; + case 'K': + fquote = fcsend_debug (fquote, (size_t) 0, "break"); + if (! fconn_break (qconn)) + { + ucsend_debug_end (fquote, TRUE); + return FALSE; + } + break; + case 'n': + fsend = TRUE; + bsend = '\n'; + break; + case 'N': + fsend = TRUE; + bsend = '\0'; + break; + case 'p': + fquote = fcsend_debug (fquote, (size_t) 0, "pause"); + usysdep_pause (); + break; + case 'r': + fsend = TRUE; + bsend = '\r'; + break; + case 's': + fsend = TRUE; + bsend = ' '; + break; + case 't': + fsend = TRUE; + bsend = '\t'; + break; + case '\0': + --z; + /* Fall through. */ + case '\\': + fsend = TRUE; + bsend = '\\'; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + fsend = TRUE; + bsend = *z - '0'; + if (z[1] >= '0' && z[1] <= '7') + bsend = (char) (8 * bsend + *++z - '0'); + if (z[1] >= '0' && z[1] <= '7') + bsend = (char) (8 * bsend + *++z - '0'); + break; + case 'x': + fsend = TRUE; + bsend = 0; + while (isxdigit (BUCHAR (z[1]))) + { + if (isdigit (BUCHAR (z[1]))) + bsend = (char) (16 * bsend + *++z - '0'); + else if (isupper (BUCHAR (z[1]))) + bsend = (char) (16 * bsend + *++z - 'A'); + else + bsend = (char) (16 * bsend + *++z - 'a'); + } + break; + case 'L': + { + const char *zlog; + + if (qsys == NULL) + { + ucsend_debug_end (fquote, TRUE); + ulog (LOG_ERROR, "Illegal use of \\L"); + return FALSE; + } + zlog = qsys->uuconf_zcall_login; + if (zlog == NULL) + { + ucsend_debug_end (fquote, TRUE); + ulog (LOG_ERROR, "No login defined"); + return FALSE; + } + if (zlog[0] == '*' && zlog[1] == '\0') + { + if (zcallout_login == NULL) + { + int iuuconf; + + iuuconf = uuconf_callout (puuconf, qsys, + &zcallout_login, + &zcallout_pass); + if (iuuconf == UUCONF_NOT_FOUND + || zcallout_login == NULL) + { + ucsend_debug_end (fquote, TRUE); + ulog (LOG_ERROR, "No login defined"); + return FALSE; + } + else if (iuuconf != UUCONF_SUCCESS) + { + ucsend_debug_end (fquote, TRUE); + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + } + zlog = zcallout_login; + } + fquote = fcsend_debug (fquote, (size_t) 0, "login"); + fquote = fcsend_debug (fquote, strlen (zlog), zlog); + if (! (*pfwrite) (qconn, zlog, strlen (zlog))) + { + ucsend_debug_end (fquote, TRUE); + return FALSE; + } + } + break; + case 'P': + { + const char *zpass; + + if (qsys == NULL) + { + ucsend_debug_end (fquote, TRUE); + ulog (LOG_ERROR, "Illegal use of \\P"); + return FALSE; + } + zpass = qsys->uuconf_zcall_password; + if (zpass == NULL) + { + ucsend_debug_end (fquote, TRUE); + ulog (LOG_ERROR, "No password defined"); + return FALSE; + } + if (zpass[0] == '*' && zpass[1] == '\0') + { + if (zcallout_pass == NULL) + { + int iuuconf; + + iuuconf = uuconf_callout (puuconf, qsys, + &zcallout_login, + &zcallout_pass); + if (iuuconf == UUCONF_NOT_FOUND + || zcallout_pass == NULL) + { + ucsend_debug_end (fquote, TRUE); + ulog (LOG_ERROR, "No password defined"); + return FALSE; + } + else if (iuuconf != UUCONF_SUCCESS) + { + ucsend_debug_end (fquote, TRUE); + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + } + zpass = zcallout_pass; + } + fquote = fcsend_debug (fquote, (size_t) 0, "password"); + fquote = fcsend_debug (fquote, strlen (zpass), zpass); + if (! (*pfwrite) (qconn, zpass, strlen (zpass))) + { + ucsend_debug_end (fquote, TRUE); + return FALSE; + } + } + break; + case 'D': + if (qdial == NULL || zphone == NULL) + { + ucsend_debug_end (fquote, TRUE); + ulog (LOG_ERROR, "Illegal use of \\D"); + return FALSE; + } + fquote = fcsend_debug (fquote, (size_t) 0, "\\D"); + if (! fcphone (qconn, puuconf, qdial, zphone, pfwrite, + ftranslate, &fquote)) + { + ucsend_debug_end (fquote, TRUE); + return FALSE; + } + break; + case 'T': + if (qdial == NULL || zphone == NULL) + { + ucsend_debug_end (fquote, TRUE); + ulog (LOG_ERROR, "Illegal use of \\T"); + return FALSE; + } + fquote = fcsend_debug (fquote, (size_t) 0, "\\T"); + if (! fcphone (qconn, puuconf, qdial, zphone, pfwrite, TRUE, + &fquote)) + { + ucsend_debug_end (fquote, TRUE); + return FALSE; + } + break; + case 'M': + if (qdial == NULL) + { + ucsend_debug_end (fquote, TRUE); + ulog (LOG_ERROR, "Illegal use of \\M"); + return FALSE; + } + fquote = fcsend_debug (fquote, (size_t) 0, "ignore-carrier"); + if (! fconn_carrier (qconn, FALSE)) + { + ucsend_debug_end (fquote, TRUE); + return FALSE; + } + break; + case 'm': + if (qdial == NULL) + { + ucsend_debug_end (fquote, TRUE); + ulog (LOG_ERROR, "Illegal use of \\m"); + return FALSE; + } + if (qdial->uuconf_fcarrier) + { + fquote = fcsend_debug (fquote, (size_t) 0, "need-carrier"); + if (! fconn_carrier (qconn, TRUE)) + { + ucsend_debug_end (fquote, TRUE); + return FALSE; + } + } + break; + default: + /* This error message will screw up any debugging + information, but it's easily avoidable. */ + ulog (LOG_ERROR, + "Unrecognized escape sequence \\%c in send string", + *z); + fsend = TRUE; + bsend = *z; + break; + } + ++z; + break; +#if DEBUG > 0 + default: + ulog (LOG_FATAL, "fcsend: Can't happen"); + break; +#endif + } + + if (fsend) + { + fquote = fcsend_debug (fquote, (size_t) 1, &bsend); + if (! (*pfwrite) (qconn, &bsend, (size_t) 1)) + { + ucsend_debug_end (fquote, TRUE); + return FALSE; + } + } + } + + xfree ((pointer) zcallout_login); + xfree ((pointer) zcallout_pass); + + /* Output a final carriage return, unless there was a \c. Don't + bother to check for an echo. */ + if (! fnocr) + { + char b; + + b = '\r'; + fquote = fcsend_debug (fquote, (size_t) 1, &b); + if (! fconn_write (qconn, &b, (size_t) 1)) + { + ucsend_debug_end (fquote, TRUE); + return FALSE; + } + } + + ucsend_debug_end (fquote, FALSE); + + return TRUE; +} + +/* Write out a phone number with optional dialcode translation. The + pfquote argument is only used for debugging. */ + +static boolean +fcphone (qconn, puuconf, qdial, zphone, pfwrite, ftranslate, pfquote) + struct sconnection *qconn; + pointer puuconf; + const struct uuconf_dialer *qdial; + const char *zphone; + boolean (*pfwrite) P((struct sconnection *qc, const char *zwrite, + size_t cwrite)); + boolean ftranslate; + boolean *pfquote; +{ + const char *zprefix, *zsuffix; + + if (ftranslate) + { + if (! fctranslate (puuconf, zphone, &zprefix, &zsuffix)) + return FALSE; + } + else + { + zprefix = zphone; + zsuffix = NULL; + } + + while (zprefix != NULL) + { + while (TRUE) + { + const char *z; + const char *zstr; + + z = zprefix + strcspn ((char *) zprefix, "=-"); + if (z > zprefix) + { + size_t clen; + + clen = z - zprefix; + *pfquote = fcsend_debug (*pfquote, clen, zprefix); + if (! (*pfwrite) (qconn, zprefix, clen)) + return FALSE; + } + + if (*z == '=') + zstr = qdial->uuconf_zdialtone; + else if (*z == '-') + zstr = qdial->uuconf_zpause; + else /* *z == '\0' */ + break; + + if (zstr != NULL) + { + *pfquote = fcsend_debug (*pfquote, strlen (zstr), zstr); + if (! (*pfwrite) (qconn, zstr, strlen (zstr))) + return FALSE; + } + + zprefix = z + 1; + } + + zprefix = zsuffix; + zsuffix = NULL; + } + + return TRUE; +} + +/* Given a phone number, run it through dial code translation + returning two strings. */ + +static boolean +fctranslate (puuconf, zphone, pzprefix, pzsuffix) + pointer puuconf; + const char *zphone; + const char **pzprefix; + const char **pzsuffix; +{ + int iuuconf; + char *zdialcode, *zto; + const char *zfrom; + char *ztrans; + + *pzprefix = zphone; + *pzsuffix = NULL; + + zdialcode = zbufalc (strlen (zphone) + 1); + zfrom = zphone; + zto = zdialcode; + while (*zfrom != '\0' && isalpha (BUCHAR (*zfrom))) + *zto++ = *zfrom++; + *zto = '\0'; + + if (*zdialcode == '\0') + { + ubuffree (zdialcode); + return TRUE; + } + + iuuconf = uuconf_dialcode (puuconf, zdialcode, &ztrans); + + ubuffree (zdialcode); + + if (iuuconf == UUCONF_NOT_FOUND) + return TRUE; + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + else + { + /* We really should figure out a way to free up ztrans here. */ + *pzprefix = ztrans; + *pzsuffix = zfrom; + return TRUE; + } +} + +/* Write out a string making sure the each character is echoed back. + There are two versions of this function, one which strips the + parity bit from the characters and one which does not. This is so + that I can use a single function pointer in fcsend, and to avoid + using any static variables so that I can put chat scripts in a + library some day. */ + +static boolean +fcecho_send_strip (qconn, zwrite, cwrite) + struct sconnection *qconn; + const char *zwrite; + size_t cwrite; +{ + return fcecho_send (qconn, zwrite, cwrite, TRUE); +} + +static boolean +fcecho_send_nostrip (qconn, zwrite, cwrite) + struct sconnection *qconn; + const char *zwrite; + size_t cwrite; +{ + return fcecho_send (qconn, zwrite, cwrite, FALSE); +} + +static boolean +fcecho_send (qconn, zwrite, cwrite, fstrip) + struct sconnection *qconn; + const char *zwrite; + size_t cwrite; + boolean fstrip; +{ + const char *zend; + + zend = zwrite + cwrite; + + for (; zwrite < zend; zwrite++) + { + int b; + char bwrite; + + bwrite = *zwrite; + if (! fconn_write (qconn, &bwrite, (size_t) 1)) + return FALSE; + if (fstrip) + bwrite &= 0x7f; + do + { + /* We arbitrarily wait five seconds for the echo. */ + b = breceive_char (qconn, 5, TRUE); + /* Now b == -1 on timeout, -2 on error. */ + if (b < 0) + { + if (b == -1) + ulog (LOG_ERROR, "Character not echoed"); + return FALSE; + } + if (fstrip) + b &= 0x7f; + } + while (b != BUCHAR (bwrite)); + } + + return TRUE; +} + +/* Run a chat program. Expand any escape sequences and call a system + dependent program to run it. */ + +static boolean +fcprogram (qconn, puuconf, pzprogram, qsys, qdial, zphone, zport, ibaud) + struct sconnection *qconn; + pointer puuconf; + char **pzprogram; + const struct uuconf_system *qsys; + const struct uuconf_dialer *qdial; + const char *zphone; + const char *zport; + long ibaud; +{ + size_t cargs; + char **pzpass, **pzarg; + char **pz; + char *zcallout_login; + char *zcallout_pass; + boolean fret; + + cargs = 1; + for (pz = pzprogram; *pz != NULL; pz++) + ++cargs; + + pzpass = (char **) xmalloc (cargs * sizeof (char *)); + + zcallout_login = NULL; + zcallout_pass = NULL; + fret = TRUE; + + /* Copy the string into memory expanding escape sequences. */ + for (pz = pzprogram, pzarg = pzpass; *pz != NULL; pz++, pzarg++) + { + const char *zfrom; + size_t calc, clen; + char *zto; + + if (strchr (*pz, '\\') == NULL) + { + *pzarg = zbufcpy (*pz); + continue; + } + + *pzarg = NULL; + zto = NULL; + calc = 0; + clen = 0; + + for (zfrom = *pz; *zfrom != '\0'; zfrom++) + { + const char *zadd = NULL; + size_t cadd; + char abadd[15]; + + if (*zfrom != '\\') + { + if (clen + 2 > calc) + { + char *znew; + + calc = clen + 50; + znew = zbufalc (calc); + memcpy (znew, *pzarg, clen); + ubuffree (*pzarg); + *pzarg = znew; + zto = znew + clen; + } + *zto++ = *zfrom; + ++clen; + continue; + } + + ++zfrom; + switch (*zfrom) + { + case '\0': + --zfrom; + /* Fall through. */ + case '\\': + zadd = "\\"; + break; + case 'L': + { + const char *zlog; + + if (qsys == NULL) + { + ulog (LOG_ERROR, "chat-program: Illegal use of \\L"); + fret = FALSE; + break; + } + zlog = qsys->uuconf_zcall_login; + if (zlog == NULL) + { + ulog (LOG_ERROR, "chat-program: No login defined"); + fret = FALSE; + break; + } + if (zlog[0] == '*' && zlog[1] == '\0') + { + if (zcallout_login == NULL) + { + int iuuconf; + + iuuconf = uuconf_callout (puuconf, qsys, + &zcallout_login, + &zcallout_pass); + if (iuuconf == UUCONF_NOT_FOUND + || zcallout_login == NULL) + { + ulog (LOG_ERROR, + "chat-program: No login defined"); + fret = FALSE; + break; + } + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + fret = FALSE; + break; + } + } + zlog = zcallout_login; + } + zadd = zlog; + } + break; + case 'P': + { + const char *zpass; + + if (qsys == NULL) + { + ulog (LOG_ERROR, "chat-program: Illegal use of \\P"); + fret = FALSE; + break; + } + zpass = qsys->uuconf_zcall_password; + if (zpass == NULL) + { + ulog (LOG_ERROR, "chat-program: No password defined"); + fret = FALSE; + break; + } + if (zpass[0] == '*' && zpass[1] == '\0') + { + if (zcallout_pass == NULL) + { + int iuuconf; + + iuuconf = uuconf_callout (puuconf, qsys, + &zcallout_login, + &zcallout_pass); + if (iuuconf == UUCONF_NOT_FOUND + || zcallout_pass == NULL) + { + ulog (LOG_ERROR, + "chat-program: No password defined"); + fret = FALSE; + break; + } + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + fret = FALSE; + break; + } + } + zpass = zcallout_pass; + } + zadd = zpass; + } + break; + case 'D': + if (qdial == NULL || zphone == NULL) + { + ulog (LOG_ERROR, "chat-program: Illegal use of \\D"); + fret = FALSE; + break; + } + zadd = zphone; + break; + case 'T': + { + const char *zprefix, *zsuffix; + + if (qdial == NULL || zphone == NULL) + { + ulog (LOG_ERROR, "chat-program: Illegal use of \\T"); + fret = FALSE; + break; + } + + if (! fctranslate (puuconf, zphone, &zprefix, &zsuffix)) + { + fret = FALSE; + break; + } + + if (zsuffix == NULL) + zadd = zprefix; + else + { + size_t cprefix; + + cprefix = strlen (zprefix); + if (clen + cprefix + 1 > calc) + { + char *znew; + + calc = clen + cprefix + 20; + znew = zbufalc (calc); + memcpy (znew, *pzarg, clen); + ubuffree (*pzarg); + *pzarg = znew; + zto = znew + clen; + } + memcpy (zto, zprefix, cprefix); + zto += cprefix; + clen += cprefix; + zadd = zsuffix; + } + } + break; + case 'Y': + if (zLdevice == NULL && zport == NULL) + { + ulog (LOG_ERROR, "chat-program: Illegal use of \\Y"); + fret = FALSE; + break; + } + /* zLdevice will generally make more sense than zport, but + it might not be set yet. */ + zadd = zLdevice; + if (zadd == NULL) + zadd = zport; + break; + case 'Z': + if (qsys == NULL) + { + ulog (LOG_ERROR, "chat-program: Illegal use of \\Z"); + fret = FALSE; + break; + } + zadd = qsys->uuconf_zname; + break; + case 'S': + { + if (ibaud == 0) + { + ulog (LOG_ERROR, "chat-program: Illegal use of \\S"); + fret = FALSE; + break; + } + sprintf (abadd, "%ld", ibaud); + zadd = abadd; + } + break; + default: + { + ulog (LOG_ERROR, + "chat-program: Unrecognized escape sequence \\%c", + *zfrom); + abadd[0] = *zfrom; + abadd[1] = '\0'; + zadd = abadd; + } + break; + } + + if (! fret) + break; + + cadd = strlen (zadd); + if (clen + cadd + 1 > calc) + { + char *znew; + + calc = clen + cadd + 20; + znew = zbufalc (calc); + memcpy (znew, *pzarg, clen); + ubuffree (*pzarg); + *pzarg = znew; + zto = znew + clen; + } + memcpy (zto, zadd, cadd + 1); + zto += cadd; + clen += cadd; + } + + if (! fret) + break; + + *zto++ = '\0'; + ++clen; + } + + *pzarg = NULL; + + if (fret) + fret = fconn_run_chat (qconn, pzpass); + + for (pz = pzpass; *pz != NULL; pz++) + ubuffree (*pz); + xfree ((pointer) pzpass); + xfree ((pointer) zcallout_login); + xfree ((pointer) zcallout_pass); + + return fret; +} diff --git a/gnu/libexec/uucp/common_sources/conf.h b/gnu/libexec/uucp/common_sources/conf.h new file mode 100644 index 0000000000..c0eacf68f8 --- /dev/null +++ b/gnu/libexec/uucp/common_sources/conf.h @@ -0,0 +1,444 @@ +/* conf.h. Generated automatically by configure. */ +/* Configuration header file for Taylor UUCP. -*- C -*- */ + +/* Set MAIL_PROGRAM to a program which takes a mail address as an + argument and accepts a mail message to send to that address on + stdin (e.g. "/bin/mail"). */ +#define MAIL_PROGRAM "/usr/bin/mail" + +/* Set ECHO_PROGRAM to a program which echoes its arguments; if echo + is a shell builtin you can just use "echo". */ +#define ECHO_PROGRAM "echo" + +/* The following macros indicate what header files you have. Set the + macro to 1 if you have the corresponding header file, or 0 if you + do not. */ +#define HAVE_STDDEF_H 1 /* */ +#define HAVE_STRING_H 1 /* */ +#define HAVE_STRINGS_H 1 /* */ +#define HAVE_UNISTD_H 1 /* */ +#define HAVE_STDLIB_H 1 /* */ +#define HAVE_LIMITS_H 1 /* */ +#define HAVE_TIME_H 1 /* */ +#define HAVE_SYS_WAIT_H 1 /* */ +#define HAVE_SYS_IOCTL_H 1 /* */ +#define HAVE_DIRENT_H 1 /* */ +#define HAVE_MEMORY_H 1 /* */ +#define HAVE_SYS_PARAM_H 1 /* */ +#define HAVE_UTIME_H 1 /* */ +#define HAVE_FCNTL_H 1 /* */ +#define HAVE_SYS_FILE_H 1 /* */ +#define HAVE_SYS_TIMES_H 1 /* */ +#define HAVE_LIBC_H 0 /* */ +#define HAVE_SYSEXITS_H 1 /* */ +#define HAVE_POLL_H 0 /* */ +#define HAVE_TIUSER_H 0 /* */ +#define HAVE_XTI_H 0 /* */ +#define HAVE_SYS_TLI_H 0 /* */ +#define HAVE_STROPTS_H 0 /* */ +#define HAVE_FTW_H 0 /* */ +#define HAVE_GLOB_H 1 /* */ +#define HAVE_SYS_SELECT_H 0 /* */ +#define HAVE_SYS_TYPES_TCP_H 0 /* */ + +/* If major and minor are not defined in , but are in + , set MAJOR_IN_MKDEV to 1. If they are in + , set MAJOR_IN_SYSMACROS to 1. */ +#define MAJOR_IN_MKDEV 0 +#define MAJOR_IN_SYSMACROS 0 + +/* If the macro offsetof is not defined in , you may give it + a definition here. If you do not, the code will use a definition + (in uucp.h) that should be fairly portable. */ +/* #define offsetof */ + +/* Set RETSIGTYPE to the return type of a signal handler. On newer + systems this will be void; some older systems use int. */ +#define RETSIGTYPE void + +/* Set HAVE_SYS_TIME_AND_TIME_H to 1 if and can both + be included in a single source file; if you don't have either or both of + them, it doesn't matter what you set this to. */ +#define HAVE_SYS_TIME_AND_TIME_H 1 + +/* Set HAVE_TERMIOS_AND_SYS_IOCTL_H to 1 if and + can both be included in a single source file; if you don't have either + or both of them, it doesn't matter what you set this to. */ +#define HAVE_TERMIOS_AND_SYS_IOCTL_H 1 + +/* If you are configuring by hand, you should set one of the terminal + driver options in policy.h. If you are autoconfiguring, the script + will check whether your system defines CBREAK, which is a terminal + setting; if your system supports CBREAK, and you don't set a terminal + driver in policy.h, the code will assume that you have a BSD style + terminal driver. */ +#define HAVE_CBREAK 1 + +/* The package needs several standard types. If you are using the + configure script, it will look in standard places for these types, + and give default definitions for them here if it doesn't find them. + The default definitions should work on most systems, but you may + want to check them. If you are configuring by hand, you will have + to figure out whether the types are defined on your system, and + what they should be defined to. + + Any type that is not defined on your system should get a macro + definition. The definition should be of the name of the type in + all capital letters. For example, #define PID_T int. If the type + is defined in a standard header file, the macro name should not be + defined. */ + +/* The type pid_t is used to hold a process ID number. It is normally + defined in . This is the type returned by the + functions fork or getpid. Usually int will work fine. */ +#undef PID_T + +/* The type uid_t is used to hold a user ID number. It is normally + defined in . This is the type returned by the getuid + function. Usually int will work fine. */ +#undef UID_T + +/* The type gid_t is used to hold a group ID number. It is sometimes + defined in . This is the type returned by the getgid + function. Usually int will work fine. */ +#undef GID_T + +/* The type off_t is used to hold an offset in a file. It is sometimes + defined in . This is the type of the second argument to + the lseek function. Usually long will work fine. */ +#undef OFF_T + +/* Set HAVE_SIG_ATOMIC_T_IN_SIGNAL_H if the type sig_atomic_t is defined + in as required by ANSI C. */ +#define HAVE_SIG_ATOMIC_T_IN_SIGNAL_H 0 + +/* Set HAVE_SIG_ATOMIC_T_IN_TYPES_H if the type sig_atomic_t is defined + in . This is ignored if HAVE_SIG_ATOMIC_T_IN_SIGNAL_H is + set to 1. */ +#define HAVE_SIG_ATOMIC_T_IN_TYPES_H 0 + +/* The type sig_atomic_t is used to hold a value which may be + referenced in a single atomic operation. If it is not defined in + either or , you may want to give it a + definition here. If you don't, the code will use char. If your + compiler does not support sig_atomic_t, there is no type which is + really correct; fortunately, for this package it does not really + matter very much. */ +#undef SIG_ATOMIC_T + +/* Set HAVE_SIZE_T_IN_STDDEF_H to 1 if the type size_t is defined in + as required by ANSI C. */ +#define HAVE_SIZE_T_IN_STDDEF_H 1 + +/* Set HAVE_SIZE_T_IN_TYPES_H to 1 if the type size_t is defined in + . This is ignored if HAVE_SIZE_T_IN_STDDEF_H is set + to 1. */ +#define HAVE_SIZE_T_IN_TYPES_H 1 + +/* The type size_t is used to hold the size of an object. In + particular, an argument of this type is passed as the size argument + to the malloc and realloc functions. If size_t is not defined in + either or , you may want to give it a + definition here. If you don't, the code will use unsigned. */ +#undef SIZE_T + +/* Set HAVE_TIME_T_IN_TIME_H to 1 if the type time_t is defined in + , as required by the ANSI C standard. */ +#define HAVE_TIME_T_IN_TIME_H 1 + +/* Set HAVE_TIME_T_IN_TYPES_H to 1 if the type time_t is defined in + . This is ignored if HAVE_TIME_T_IN_TIME_H is set to + 1. */ +#define HAVE_TIME_T_IN_TYPES_H 1 + +/* When Taylor UUCP is talking to another instance of itself, it will + tell the other side the size of a file before it is transferred. + If the package can determine how much disk space is available, it + will use this information to avoid filling up the disk. Define one + of the following macros to tell the code how to determine the + amount of available disk space. It is possible that none of these + are appropriate; it will do no harm to use none of them, but, of + course, nothing will then prevent the package from filling up the + disk. Note that this space check is only useful when talking to + another instance of Taylor UUCP. + + STAT_STATVFS statvfs function + STAT_STATFS2_BSIZE two argument statfs function with f_bsize field + STAT_STATFS2_FSIZE two argument statfs function with f_fsize field + STAT_STATFS2_FS_DATA two argument statfs function with fd_req field + STAT_STATFS4 four argument statfs function + STAT_USTAT the ustat function with 512 byte blocks. */ +#define STAT_STATVFS 0 +#define STAT_STATFS2_BSIZE 0 +#define STAT_STATFS2_FSIZE 1 +#define STAT_STATFS2_FS_DATA 0 +#define STAT_STATFS4 0 +#define STAT_USTAT 0 + +/* Set HAVE_VOID to 1 if the compiler supports declaring functions with + a return type of void and casting values to void. */ +#define HAVE_VOID 1 + +/* Set HAVE_UNSIGNED_CHAR to 1 if the compiler supports the type unsigned + char. */ +#define HAVE_UNSIGNED_CHAR 1 + +/* Set HAVE_ERRNO_DECLARATION to 1 if errno is declared in . */ +#define HAVE_ERRNO_DECLARATION 1 + +/* There are now a number of functions to check for. For each of + these, the macro HAVE_FUNC should be set to 1 if your system has + FUNC. For example, HAVE_VFPRINTF should be set to 1 if your system + has vfprintf, 0 otherwise. */ + +/* Taylor UUCP will take advantage of the following functions if they + are available, but knows how to deal with their absence. */ +#define HAVE_VFPRINTF 1 +#define HAVE_FTRUNCATE 1 +#define HAVE_LTRUNC 0 +#define HAVE_WAITPID 1 +#define HAVE_WAIT4 1 +#define HAVE_GLOB 1 +#define HAVE_SETREUID 1 + +/* There are several functions which are replaced in the subdirectory + lib. If they are missing, the configure script will automatically + add them to lib/Makefile to force them to be recompiled. If you + are configuring by hand, you will have to do this yourself. The + string @LIBOBJS@ in lib/Makefile.in should be replaced by a list of + object files in lib/Makefile. The following comments tell you + which object file names to add (they are generally fairly obvious, + given that the file names have no more than six characters before + the period). */ + +/* For each of these functions, if it does not exist, the indicated + object file should be added to lib/Makefile. */ +#define HAVE_BSEARCH 1 /* bsrch.o */ +#define HAVE_GETLINE 0 /* getlin.o */ +#define HAVE_MEMCHR 1 /* memchr.o */ +#define HAVE_STRDUP 1 /* strdup.o */ +#define HAVE_STRSTR 1 /* strstr.o */ +#define HAVE_STRTOL 1 /* strtol.o */ + +/* If neither of these functions exists, you should add bzero.o to + lib/Makefile. */ +#define HAVE_BZERO 1 +#define HAVE_MEMSET 1 + +/* If neither of these functions exists, you should add memcmp.o to + lib/Makefile. */ +#define HAVE_MEMCMP 1 +#define HAVE_BCMP 1 + +/* If neither of these functions exists, you should add memcpy.o to + lib/Makefile. */ +#define HAVE_MEMCPY 1 +#define HAVE_BCOPY 1 + +/* If neither of these functions exists, you should add strcas.o to + lib/Makefile. */ +#define HAVE_STRCASECMP 1 +#define HAVE_STRICMP 0 + +/* If neither of these functions exists, you should add strncs.o to + lib/Makefile. */ +#define HAVE_STRNCASECMP 1 +#define HAVE_STRNICMP 0 + +/* If neither of these functions exists, you should add strchr.o to + lib/Makefile. */ +#define HAVE_STRCHR 1 +#define HAVE_INDEX 1 + +/* If neither of these functions exists, you should add strrch.o to + lib/Makefile. */ +#define HAVE_STRRCHR 1 +#define HAVE_RINDEX 1 + +/* There are also Unix specific functions which are replaced in the + subdirectory unix. If they are missing, the configure script will + automatically add them to unix/Makefile to force them to be + recompiled. If you are configuring by hand, you will have to do + this yourself. The string @UNIXOBJS@ in unix/Makefile.in should be + replaced by a list of object files in unix/Makefile. The following + comments tell you which object file names to add. */ + +/* For each of these functions, if it does not exist, the indicated + object file should be added to unix/Makefile. */ +#define HAVE_OPENDIR 1 /* dirent.o */ +#define HAVE_DUP2 1 /* dup2.o */ +#define HAVE_FTW 0 /* ftw.o */ +#define HAVE_REMOVE 1 /* remove.o */ +#define HAVE_RENAME 1 /* rename.o */ +#define HAVE_STRERROR 1 /* strerr.o */ + +/* The code needs to know how to create directories. If you have the + mkdir function, set HAVE_MKDIR to 1 and replace @UUDIR@ in + Makefile.in with '# ' (the configure script will set @UUDIR@ + according to the variable UUDIR). Otherwise, set HAVE_MKDIR to 0, + remove @UUDIR@ from Makefile.in, set MKDIR_PROGRAM to the name of + the program which will create a directory named on the command line + (e.g., "/bin/mkdir"), and add mkdir.o to the @UNIXOBJS@ string in + unix/Makefile.in. */ +#define HAVE_MKDIR 1 +#define MKDIR_PROGRAM unused + +/* The code also needs to know how to remove directories. If you have + the rmdir function, set HAVE_RMDIR to 1. Otherwise, set + RMDIR_PROGRAM to the name of the program which will remove a + directory named on the command line (e.g., "/bin/rmdir") and add + rmdir.o to the @UNIXOBJS@ string in unix/Makefile.in. */ +#define HAVE_RMDIR 1 +#define RMDIR_PROGRAM unused + +/* The code needs to know to how to get the name of the current + directory. If getcwd is available it will be used, otherwise if + getwd is available it will be used. Otherwise, set PWD_PROGRAM to + the name of the program which will print the name of the current + working directory (e.g., "/bin/pwd") and add getcwd.o to the + @UNIXOBJS@ string in unix/Makefile.in. */ +#define HAVE_GETCWD 1 +#define HAVE_GETWD 1 +#define PWD_PROGRAM unused + +/* If you have either sigsetjmp or setret, it will be used instead of + setjmp. These functions will only be used if your system restarts + system calls after interrupts (see HAVE_RESTARTABLE_SYSCALLS, + below). */ +#define HAVE_SIGSETJMP 0 +#define HAVE_SETRET 0 + +/* The code needs to know what function to use to set a signal + handler. If will try to use each of the following functions in + turn. If none are available, it will use signal, which is assumed + to always exist. */ +#define HAVE_SIGACTION 1 +#define HAVE_SIGVEC 1 +#define HAVE_SIGSET 0 + +/* If the code is going to use sigvec (HAVE_SIGACTION is 0 and + HAVE_SIGVEC is 1), then HAVE_SIGVEC_SV_FLAGS must be set to 1 if + the sigvec structure contains the sv_flags field, or 0 if the + sigvec structure contains the sv_onstack field. If the code is not + going to use sigvec, it doesn't matter what this is set to. */ +#define HAVE_SIGVEC_SV_FLAGS 1 + +/* The code will try to use each of the following functions in turn + when blocking signals from delivery. If none are available, a + relatively unimportant race condition will exist. */ +#define HAVE_SIGPROCMASK 1 +#define HAVE_SIGBLOCK 1 +#define HAVE_SIGHOLD 0 + +/* If you have either of the following functions, it will be used to + determine the number of file descriptors which may be open. + Otherwise, the code will use OPEN_MAX if defined, then NOFILE if + defined, then 20. */ +#define HAVE_GETDTABLESIZE 1 +#define HAVE_SYSCONF 0 + +/* The code will use one of the following functions when detaching + from a terminal. One of these must exist. */ +#define HAVE_SETPGRP 1 +#define HAVE_SETSID 1 + +/* If you do not specify the local node name in the main configuration + file, Taylor UUCP will try to use each of the following functions + in turn. If neither is available, you must specify the local node + name in the configuration file. */ +#define HAVE_GETHOSTNAME 1 +#define HAVE_UNAME 0 + +/* The code will try to use each of the following functions in turn to + determine the current time. If none are available, it will use + time, which is assumed to always exist. */ +#define HAVE_GETTIMEOFDAY 1 +#define HAVE_FTIME 0 + +/* If neither gettimeofday nor ftime is available, the code will use + times (if available) to measure a span of time. See also the + discussion of TIMES_TICK in policy.h. */ +#define HAVE_TIMES 1 + +/* When a chat script requests a pause of less than a second with \p, + Taylor UUCP will try to use each of the following functions in + turn. If none are available, it will sleep for a full second. + Also, the (non-portable) tstuu program requires either select or + poll. */ +#define HAVE_NAPMS 0 +#define HAVE_NAP 0 +#define HAVE_USLEEP 1 +#define HAVE_POLL 0 +#define HAVE_SELECT 1 + +/* If the getgrent function is available, it will be used to determine + all the groups a user belongs to when checking file access + permissions. */ +#define HAVE_GETGRENT 1 + +/* If the socket function is available, TCP support code will be + compiled in. */ +#define HAVE_SOCKET 1 + +/* If the t_open function is available, TLI support code will be + compiled in. This may require adding a library, such as -lnsl or + -lxti, to the Makefile variables LIBS. */ +#define HAVE_T_OPEN 0 + +/* That's the end of the list of the functions. Now there are a few + last miscellaneous items. */ + +/* On some systems the following functions are declared in such a way + that the code cannot make a simple extern. On other systems, these + functions are not declared at all, and the extern is required. If + a declaration of the function, as shown, compiles on your system, + set the value to 1. Not all functions declared externally are + listed here, only the ones with which I have had trouble. */ +/* extern long times (); */ +#define TIMES_DECLARATION_OK 0 +/* extern struct passwd *getpwnam (); */ +#define GETPWNAM_DECLARATION_OK 1 +/* extern struct passwd *getpwuid (); */ +#define GETPWUID_DECLARATION_OK 0 +/* extern struct group *getgrent (); */ +#define GETGRENT_DECLARATION_OK 1 + +/* Set HAVE_BSD_PGRP to 1 if your getpgrp call takes 1 argument and + your setpgrp calls takes 2 arguments (on System V they generally + take no arguments). You can safely set this to 1 on System V, + provided the call will compile without any errors. */ +#define HAVE_BSD_PGRP 0 + +/* Set HAVE_UNION_WAIT to 1 if union wait is defined in the header + file . */ +#define HAVE_UNION_WAIT 1 + +/* Set HAVE_LONG_FILE_NAMES to 1 if the system supports file names + longer than 14 characters. */ +#define HAVE_LONG_FILE_NAMES 1 + +/* If slow system calls are restarted after interrupts, set + HAVE_RESTARTABLE_SYSCALLS to 1. This is ignored if HAVE_SIGACTION + is 1 or if HAVE_SIGVEC is 1 and HAVE_SIGVEC_SV_FLAGS is 1 and + SV_INTERRUPT is defined in . In both of these cases + system calls can be prevented from restarting. */ +#define HAVE_RESTARTABLE_SYSCALLS 1 + +/* Some systems supposedly need the following macros to be defined. + These are handled by the configure script (it will turn #undef into + #define when appropriate, which is why the peculiar #ifndef #undef + construction is used). If you are configuring by hand, you may add + appropriate definitions here, or just add them to CFLAGS when + running make. */ +#ifndef _ALL_SOURCE +#undef _ALL_SOURCE +#endif +#ifndef _POSIX_SOURCE +#undef _POSIX_SOURCE +#endif +#ifndef _MINIX +#undef _MINIX +#endif +#ifndef _POSIX_1_SOURCE +#undef _POSIX_1_SOURCE +#endif diff --git a/gnu/libexec/uucp/common_sources/conn.c b/gnu/libexec/uucp/common_sources/conn.c new file mode 100644 index 0000000000..df35e8297f --- /dev/null +++ b/gnu/libexec/uucp/common_sources/conn.c @@ -0,0 +1,552 @@ +/* conn.c + Connection routines for the Taylor UUCP package. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char conn_rcsid[] = "$Id: conn.c,v 1.1 1993/08/04 19:30:39 jtc Exp $"; +#endif + +#include + +#include "uudefs.h" +#include "uuconf.h" +#include "conn.h" + +static boolean fcdo_dial P((struct sconnection *qconn, pointer puuconf, + struct uuconf_dialer *qdialer, + const char *zphone, boolean ftranslate)); + +/* Create a new connection. This relies on system dependent functions + to set the qcmds and psysdep fields. If qport is NULL, it opens a + standard input port. */ + +boolean +fconn_init (qport, qconn) + struct uuconf_port *qport; + struct sconnection *qconn; +{ + qconn->qport = qport; + switch (qport == NULL ? UUCONF_PORTTYPE_STDIN : qport->uuconf_ttype) + { + case UUCONF_PORTTYPE_STDIN: + return fsysdep_stdin_init (qconn); + case UUCONF_PORTTYPE_MODEM: + return fsysdep_modem_init (qconn); + case UUCONF_PORTTYPE_DIRECT: + return fsysdep_direct_init (qconn); +#if HAVE_TCP + case UUCONF_PORTTYPE_TCP: + return fsysdep_tcp_init (qconn); +#endif +#if HAVE_TLI + case UUCONF_PORTTYPE_TLI: + return fsysdep_tli_init (qconn); +#endif + default: + ulog (LOG_ERROR, "Unknown port type"); + return FALSE; + } +} + +/* Connection dispatch routines. */ + +/* Free a connection. */ + +void +uconn_free (qconn) + struct sconnection *qconn; +{ + (*qconn->qcmds->pufree) (qconn); +} + +/* Lock a connection. */ + +boolean +fconn_lock (qconn, fin) + struct sconnection *qconn; + boolean fin; +{ + boolean (*pflock) P((struct sconnection *, boolean)); + + pflock = qconn->qcmds->pflock; + if (pflock == NULL) + return TRUE; + return (*pflock) (qconn, fin); +} + +/* Unlock a connection. */ + +boolean +fconn_unlock (qconn) + struct sconnection *qconn; +{ + boolean (*pfunlock) P((struct sconnection *)); + + pfunlock = qconn->qcmds->pfunlock; + if (pfunlock == NULL) + return TRUE; + return (*pfunlock) (qconn); +} + +/* Open a connection. */ + +boolean +fconn_open (qconn, ibaud, ihighbaud, fwait) + struct sconnection *qconn; + long ibaud; + long ihighbaud; + boolean fwait; +{ + boolean fret; + +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_PORT)) + { + char abspeed[20]; + + if (ibaud == (long) 0) + strcpy (abspeed, "default speed"); + else + sprintf (abspeed, "speed %ld", ibaud); + + if (qconn->qport == NULL) + ulog (LOG_DEBUG, "fconn_open: Opening stdin port (%s)", + abspeed); + else if (qconn->qport->uuconf_zname == NULL) + ulog (LOG_DEBUG, "fconn_open: Opening unnamed port (%s)", + abspeed); + else + ulog (LOG_DEBUG, "fconn_open: Opening port %s (%s)", + qconn->qport->uuconf_zname, abspeed); + } +#endif + + /* If the system provides a range of baud rates, we select the + highest baud rate supported by the port. */ + if (ihighbaud != 0 && qconn->qport != NULL) + { + struct uuconf_port *qport; + + qport = qconn->qport; + ibaud = ihighbaud; + if (qport->uuconf_ttype == UUCONF_PORTTYPE_MODEM) + { + if (qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud != 0) + { + if (qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud < ibaud) + ibaud = qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud; + } + else if (qport->uuconf_u.uuconf_smodem.uuconf_ibaud != 0) + ibaud = qport->uuconf_u.uuconf_smodem.uuconf_ibaud; + } + else if (qport->uuconf_ttype == UUCONF_PORTTYPE_DIRECT) + { + if (qport->uuconf_u.uuconf_sdirect.uuconf_ibaud != 0) + ibaud = qport->uuconf_u.uuconf_sdirect.uuconf_ibaud; + } + } + + /* This will normally be overridden by the port specific open + routine. */ + if (qconn->qport == NULL) + ulog_device ("stdin"); + else + ulog_device (qconn->qport->uuconf_zname); + + fret = (*qconn->qcmds->pfopen) (qconn, ibaud, fwait); + + if (! fret) + ulog_device ((const char *) NULL); + + return fret; +} + +/* Close a connection. */ + +boolean +fconn_close (qconn, puuconf, qdialer, fsuccess) + struct sconnection *qconn; + pointer puuconf; + struct uuconf_dialer *qdialer; + boolean fsuccess; +{ + boolean fret; + + DEBUG_MESSAGE0 (DEBUG_PORT, "fconn_close: Closing connection"); + + /* Don't report hangup signals while we're closing. */ + fLog_sighup = FALSE; + + fret = (*qconn->qcmds->pfclose) (qconn, puuconf, qdialer, fsuccess); + + /* Make sure any signal reporting has been done before we set + fLog_sighup back to TRUE. */ + ulog (LOG_ERROR, (const char *) NULL); + fLog_sighup = TRUE; + + ulog_device ((const char *) NULL); + + return fret; +} + +/* Reset the connection. */ + +boolean +fconn_reset (qconn) + struct sconnection *qconn; +{ + DEBUG_MESSAGE0 (DEBUG_PORT, "fconn_reset: Resetting connection"); + + return (*qconn->qcmds->pfreset) (qconn); +} + +/* Dial out on the connection. */ + +boolean +fconn_dial (qconn, puuconf, qsys, zphone, qdialer, ptdialerfound) + struct sconnection *qconn; + pointer puuconf; + const struct uuconf_system *qsys; + const char *zphone; + struct uuconf_dialer *qdialer; + enum tdialerfound *ptdialerfound; +{ + struct uuconf_dialer sdialer; + enum tdialerfound tfound; + boolean (*pfdial) P((struct sconnection *, pointer, + const struct uuconf_system *, const char *, + struct uuconf_dialer *, enum tdialerfound *)); + + if (qdialer == NULL) + qdialer = &sdialer; + if (ptdialerfound == NULL) + ptdialerfound = &tfound; + + qdialer->uuconf_zname = NULL; + *ptdialerfound = DIALERFOUND_FALSE; + + pfdial = qconn->qcmds->pfdial; + if (pfdial == NULL) + return TRUE; + return (*pfdial) (qconn, puuconf, qsys, zphone, qdialer, ptdialerfound); +} + +/* Read data from the connection. */ + +boolean +fconn_read (qconn, zbuf, pclen, cmin, ctimeout, freport) + struct sconnection *qconn; + char *zbuf; + size_t *pclen; + size_t cmin; + int ctimeout; + boolean freport; +{ + boolean fret; + + fret = (*qconn->qcmds->pfread) (qconn, zbuf, pclen, cmin, ctimeout, + freport); + +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_INCOMING)) + udebug_buffer ("fconn_read: Read", zbuf, *pclen); + else if (FDEBUGGING (DEBUG_PORT)) + ulog (LOG_DEBUG, "fconn_read: Read %lu", (unsigned long) *pclen); +#endif + + return fret; +} + +/* Write data to the connection. */ + +boolean +fconn_write (qconn, zbuf, clen) + struct sconnection *qconn; + const char *zbuf; + size_t clen; +{ +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_OUTGOING)) + udebug_buffer ("fconn_write: Writing", zbuf, clen); + else if (FDEBUGGING (DEBUG_PORT)) + ulog (LOG_DEBUG, "fconn_write: Writing %lu", (unsigned long) clen); +#endif + + return (*qconn->qcmds->pfwrite) (qconn, zbuf, clen); +} + +/* Read and write data. */ + +boolean +fconn_io (qconn, zwrite, pcwrite, zread, pcread) + struct sconnection *qconn; + const char *zwrite; + size_t *pcwrite; + char *zread; + size_t *pcread; +{ + boolean fret; +#if DEBUG > 1 + size_t cwrite = *pcwrite; + size_t cread = *pcread; + + if (cread == 0 || cwrite == 0) + ulog (LOG_FATAL, "fconn_io: cread %lu; cwrite %lu", + (unsigned long) cread, (unsigned long) cwrite); +#endif + +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_OUTGOING)) + udebug_buffer ("fconn_io: Writing", zwrite, cwrite); +#endif + + fret = (*qconn->qcmds->pfio) (qconn, zwrite, pcwrite, zread, pcread); + + DEBUG_MESSAGE4 (DEBUG_PORT, + "fconn_io: Wrote %lu of %lu, read %lu of %lu", + (unsigned long) *pcwrite, (unsigned long) cwrite, + (unsigned long) *pcread, (unsigned long) cread); + +#if DEBUG > 1 + if (*pcread > 0 && FDEBUGGING (DEBUG_INCOMING)) + udebug_buffer ("fconn_io: Read", zread, *pcread); +#endif + + return fret; +} + +/* Send a break character to a connection. Some port types may not + support break characters, in which case we just return TRUE. */ + +boolean +fconn_break (qconn) + struct sconnection *qconn; +{ + boolean (*pfbreak) P((struct sconnection *)); + + pfbreak = *qconn->qcmds->pfbreak; + if (pfbreak == NULL) + return TRUE; + + DEBUG_MESSAGE0 (DEBUG_PORT, "fconn_break: Sending break character"); + + return (*pfbreak) (qconn); +} + +/* Change the setting of a connection. Some port types may not + support this, in which case we just return TRUE. */ + +boolean +fconn_set (qconn, tparity, tstrip, txonxoff) + struct sconnection *qconn; + enum tparitysetting tparity; + enum tstripsetting tstrip; + enum txonxoffsetting txonxoff; +{ + boolean (*pfset) P((struct sconnection *, enum tparitysetting, + enum tstripsetting, enum txonxoffsetting)); + + pfset = qconn->qcmds->pfset; + if (pfset == NULL) + return TRUE; + + DEBUG_MESSAGE3 (DEBUG_PORT, + "fconn_set: Changing setting to %d, %d, %d", + (int) tparity, (int) tstrip, (int) txonxoff); + + return (*pfset) (qconn, tparity, tstrip, txonxoff); +} + +/* Require or ignore carrier on a connection. */ + +boolean +fconn_carrier (qconn, fcarrier) + struct sconnection *qconn; + boolean fcarrier; +{ + boolean (*pfcarrier) P((struct sconnection *, boolean)); + + pfcarrier = qconn->qcmds->pfcarrier; + if (pfcarrier == NULL) + return TRUE; + return (*pfcarrier) (qconn, fcarrier); +} + +/* Run a chat program on a connection. */ + +boolean +fconn_run_chat (qconn, pzprog) + struct sconnection *qconn; + char **pzprog; +{ + return (*qconn->qcmds->pfchat) (qconn, pzprog); +} + +/* Get the baud rate of a connection. */ + +long +iconn_baud (qconn) + struct sconnection *qconn; +{ + long (*pibaud) P((struct sconnection *)); + + pibaud = qconn->qcmds->pibaud; + if (pibaud == NULL) + return 0; + return (*pibaud) (qconn); +} + +/* Modem dialing routines. */ + +/*ARGSUSED*/ +boolean +fmodem_dial (qconn, puuconf, qsys, zphone, qdialer, ptdialerfound) + struct sconnection *qconn; + pointer puuconf; + const struct uuconf_system *qsys; + const char *zphone; + struct uuconf_dialer *qdialer; + enum tdialerfound *ptdialerfound; +{ + *ptdialerfound = DIALERFOUND_FALSE; + + if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_pzdialer != NULL) + { + char **pz; + boolean ffirst; + + /* The pzdialer field is a sequence of dialer/token pairs. The + dialer portion names a dialer to use. The token portion is + what \D and \T in the chat script expand to. If there is no + token for the last dialer, the phone number for the system is + used. */ + ffirst = TRUE; + pz = qconn->qport->uuconf_u.uuconf_smodem.uuconf_pzdialer; + while (*pz != NULL) + { + int iuuconf; + struct uuconf_dialer *q; + struct uuconf_dialer s; + const char *ztoken; + boolean ftranslate; + + if (! ffirst) + q = &s; + else + q = qdialer; + + iuuconf = uuconf_dialer_info (puuconf, *pz, q); + if (iuuconf == UUCONF_NOT_FOUND) + { + ulog (LOG_ERROR, "%s: Dialer not found", *pz); + return FALSE; + } + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + + ++pz; + ztoken = *pz; + + ftranslate = FALSE; + if (ztoken == NULL + || strcmp (ztoken, "\\D") == 0) + ztoken = zphone; + else if (strcmp (ztoken, "\\T") == 0) + { + ztoken = zphone; + ftranslate = TRUE; + } + + if (! fcdo_dial (qconn, puuconf, q, ztoken, ftranslate)) + { + (void) uuconf_dialer_free (puuconf, q); + if (! ffirst) + (void) uuconf_dialer_free (puuconf, qdialer); + return FALSE; + } + + if (ffirst) + { + *ptdialerfound = DIALERFOUND_FREE; + ffirst = FALSE; + } + else + (void) uuconf_dialer_free (puuconf, q); + + if (*pz != NULL) + ++pz; + } + + return TRUE; + } + else if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_qdialer != NULL) + { + struct uuconf_dialer *q; + + q = qconn->qport->uuconf_u.uuconf_smodem.uuconf_qdialer; + *qdialer = *q; + *ptdialerfound = DIALERFOUND_TRUE; + return fcdo_dial (qconn, puuconf, q, zphone, FALSE); + } + else + { + ulog (LOG_ERROR, "No dialer information"); + return FALSE; + } +} + +/* Actually use a dialer. We set up the modem (which may include + opening the dialer device), run the chat script, and finish dealing + with the modem. */ + +static boolean +fcdo_dial (qconn, puuconf, qdial, zphone, ftranslate) + struct sconnection *qconn; + pointer puuconf; + struct uuconf_dialer *qdial; + const char *zphone; + boolean ftranslate; +{ + const char *zname; + + if (! fsysdep_modem_begin_dial (qconn, qdial)) + return FALSE; + + if (qconn->qport == NULL) + zname = NULL; + else + zname = qconn->qport->uuconf_zname; + + if (! fchat (qconn, puuconf, &qdial->uuconf_schat, + (const struct uuconf_system *) NULL, qdial, + zphone, ftranslate, zname, iconn_baud (qconn))) + return FALSE; + + return fsysdep_modem_end_dial (qconn, qdial); +} diff --git a/gnu/libexec/uucp/common_sources/conn.h b/gnu/libexec/uucp/common_sources/conn.h new file mode 100644 index 0000000000..59d4881b07 --- /dev/null +++ b/gnu/libexec/uucp/common_sources/conn.h @@ -0,0 +1,312 @@ +/* conn.h + Header file for routines which manipulate connections. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#ifndef CONN_H + +#define CONN_H + +#if ANSI_C +/* These structures are used in prototypes but are not defined in this + header file. */ +struct uuconf_system; +struct uuconf_dialer; +struct uuconf_chat; +#endif + +/* This structure represents a connection. */ + +struct sconnection +{ + /* Pointer to command table for this type of connection. */ + const struct sconncmds *qcmds; + /* Pointer to system dependent information. */ + pointer psysdep; + /* Pointer to system independent information. */ + struct uuconf_port *qport; +}; + +/* Whether fconn_dial got a dialer. */ + +enum tdialerfound +{ + /* Did not find a dialer. */ + DIALERFOUND_FALSE, + /* Found a dialer which does not need to be freed. */ + DIALERFOUND_TRUE, + /* Found a dialer which does need to be freed. */ + DIALERFOUND_FREE +}; + +/* Parity settings to pass to fconn_set. */ + +enum tparitysetting +{ + /* Do not change output parity generation. */ + PARITYSETTING_DEFAULT, + /* No parity (all eight output bits used). */ + PARITYSETTING_NONE, + /* Even parity. */ + PARITYSETTING_EVEN, + /* Odd parity. */ + PARITYSETTING_ODD, + /* Mark parity. */ + PARITYSETTING_MARK, + /* Space parity. */ + PARITYSETTING_SPACE +}; + +/* Type of strip control argument to fconn_set. */ + +enum tstripsetting +{ + /* Do not change the stripping of input characters. */ + STRIPSETTING_DEFAULT, + /* Do not strip input characters to seven bits. */ + STRIPSETTING_EIGHTBITS, + /* Strip input characters to seven bits. */ + STRIPSETTING_SEVENBITS +}; + +/* Type of XON/XOFF control argument to fconn_set. */ + +enum txonxoffsetting +{ + /* Do not change XON/XOFF handshake setting. */ + XONXOFF_DEFAULT, + /* Do not do XON/XOFF handshaking. */ + XONXOFF_OFF, + /* Do XON/XOFF handshaking. */ + XONXOFF_ON +}; + +/* A command table holds the functions which implement actions for + each different kind of connection. */ + +struct sconncmds +{ + /* Free up a connection. */ + void (*pufree) P((struct sconnection *qconn)); + /* Lock the connection. The fin argument is TRUE if the connection + is to be used for an incoming call. May be NULL. */ + boolean (*pflock) P((struct sconnection *qconn, boolean fin)); + /* Unlock the connection. May be NULL. */ + boolean (*pfunlock) P((struct sconnection *qconn)); + /* Open the connection. */ + boolean (*pfopen) P((struct sconnection *qconn, long ibaud, + boolean fwait)); + /* Close the connection. */ + boolean (*pfclose) P((struct sconnection *qconn, + pointer puuconf, + struct uuconf_dialer *qdialer, + boolean fsuccess)); + /* Reset the connection so that another call may be accepted. */ + boolean (*pfreset) P((struct sconnection *qconn)); + /* Dial a number on a connection. This set *qdialer to the dialer + used, if any, and sets *ptdialerfound appropriately. The qsys + and zphone arguments are for the chat script. This field may be + NULL. */ + boolean (*pfdial) P((struct sconnection *qconn, pointer puuconf, + const struct uuconf_system *qsys, + const char *zphone, + struct uuconf_dialer *qdialer, + enum tdialerfound *ptdialerfound)); + /* Read data from a connection, with a timeout in seconds. When + called *pclen is the length of the buffer; on successful return + *pclen is the number of bytes read into the buffer. The cmin + argument is the minimum number of bytes to read before returning + ahead of a timeout. */ + boolean (*pfread) P((struct sconnection *qconn, char *zbuf, size_t *pclen, + size_t cmin, int ctimeout, boolean freport)); + /* Write data to the connection. */ + boolean (*pfwrite) P((struct sconnection *qconn, const char *zbuf, + size_t clen)); + /* Read and write data to the connection. This reads and writes + data until either all passed in data has been written or the read + buffer has been filled. When called *pcread is the size of the + read buffer and *pcwrite is the number of bytes to write; on + successful return *pcread is the number of bytes read and + *pcwrite is the number of bytes written. */ + boolean (*pfio) P((struct sconnection *qconn, const char *zwrite, + size_t *pcwrite, char *zread, size_t *pcread)); + /* Send a break character. This field may be NULL. */ + boolean (*pfbreak) P((struct sconnection *qconn)); + /* Change the connection setting. This field may be NULL. */ + boolean (*pfset) P((struct sconnection *qconn, + enum tparitysetting tparity, + enum tstripsetting tstrip, + enum txonxoffsetting txonxoff)); + /* Require or ignore carrer. This field may be NULL. */ + boolean (*pfcarrier) P((struct sconnection *qconn, + boolean fcarrier)); + /* Run a chat program on a connection. */ + boolean (*pfchat) P((struct sconnection *qconn, char **pzprog)); + /* Get the baud rate of a connection. This field may be NULL. */ + long (*pibaud) P((struct sconnection *qconn)); +}; + +/* Connection functions. */ + +/* Initialize a connection. This must be called before any of the + other connection functions are called. It initializes the fields + of qconn. It returns FALSE on error. */ +extern boolean fconn_init P((struct uuconf_port *qport, + struct sconnection *qconn)); + +/* Free up connection data. */ +extern void uconn_free P((struct sconnection *qconn)); + +/* Lock a connection. The fin argument is TRUE if the port is to be + used for an incoming call; certains type of Unix locking need this + information because they need to open the port. */ +extern boolean fconn_lock P((struct sconnection *qconn, boolean fin)); + +/* Unlock a connection. */ +extern boolean fconn_unlock P((struct sconnection *qconn)); + +/* Open a connection. If ibaud is 0, the natural baud rate of the + port is used. If ihighbaud is not 0, fconn_open chooses the + highest supported baud rate between ibaud and ihighbaud. If fwait + is TRUE, this should wait for an incoming call. */ +extern boolean fconn_open P((struct sconnection *qconn, long ibaud, + long ihighbaud, boolean fwait)); + +/* Close a connection. The fsuccess argument is TRUE if the + conversation completed normally, FALSE if it is being aborted. */ +extern boolean fconn_close P((struct sconnection *qconn, + pointer puuconf, + struct uuconf_dialer *qdialer, + boolean fsuccess)); + +/* Reset a connection such that another call may be accepted. */ +extern boolean fconn_reset P((struct sconnection *q)); + +/* Dial out on a connection. The qsys and zphone arguments are for + the chat scripts; zphone is the phone number to dial. If qdialer + is not NULL, *qdialer will be set to the dialer information used if + any; *ptdialerfound will be set appropriately. */ +extern boolean fconn_dial P((struct sconnection *q, pointer puuconf, + const struct uuconf_system *qsys, + const char *zphone, + struct uuconf_dialer *qdialer, + enum tdialerfound *ptdialerfound)); + +/* Read from a connection. + zbuf -- buffer to read bytes into + *pclen on call -- length of zbuf + *pclen on successful return -- number of bytes read + cmin -- minimum number of bytes to read before returning ahead of timeout + ctimeout -- timeout in seconds, 0 if none + freport -- whether to report errors. */ +extern boolean fconn_read P((struct sconnection *qconn, char *zbuf, + size_t *pclen, size_t cmin, + int ctimeout, boolean freport)); + +/* Write to a connection. */ +extern boolean fconn_write P((struct sconnection *qconn, const char *zbuf, + size_t cbytes)); + +/* Read and write to a connection. This reads and writes data until + either all passed-in data has been written or the read buffer is + full. + zwrite -- buffer to write bytes from + *pcwrite on call -- number of bytes to write + *pcwrite on successful return -- number of bytes written + zread -- buffer to read bytes into + *pcread on call -- size of read buffer + *pcread on successful return -- number of bytes read. */ +extern boolean fconn_io P((struct sconnection *qconn, const char *zwrite, + size_t *pcwrite, char *zread, size_t *pcread)); + +/* Send a break character to a connection. */ +extern boolean fconn_break P((struct sconnection *qconn)); + +/* Change the settings of a connection. This allows independent + control over the parity of output characters, whether to strip + input characters, and whether to do XON/XOFF handshaking. There is + no explicit control over parity checking of input characters. This + function returns FALSE on error. Attempts to set values not + supported by the hardware are silently ignored. */ +extern boolean fconn_set P((struct sconnection *qconn, + enum tparitysetting tparity, + enum tstripsetting tstrip, + enum txonxoffsetting txonxoff)); + +/* Get the baud rate of a connection. */ +extern long iconn_baud P((struct sconnection *qconn)); + +/* Do a chat script with a system. */ +extern boolean fchat P((struct sconnection *qconn, pointer puuconf, + const struct uuconf_chat *qchat, + const struct uuconf_system *qsys, + const struct uuconf_dialer *qdialer, + const char *zphone, boolean ftranslate, + const char *zport, long ibaud)); + +/* Tell the connection to either require or ignore carrier as fcarrier + is TRUE or FALSE respectively. This is called with fcarrier TRUE + when \m is encountered in a chat script, and with fcarrier FALSE + when \M is encountered. */ +extern boolean fconn_carrier P((struct sconnection *qconn, + boolean fcarrier)); + +/* Run a chat program on a connection. */ +extern boolean fconn_run_chat P((struct sconnection *qconn, + char **pzprog)); + +/* Dialing out on a modem is partially system independent. This is + the modem dialing routine. */ +extern boolean fmodem_dial P((struct sconnection *qconn, pointer puuconf, + const struct uuconf_system *qsys, + const char *zphone, + struct uuconf_dialer *qdialer, + enum tdialerfound *ptdialerfound)); + +/* Begin dialing out. This should open the dialer device if there is + one, toggle DTR if requested and possible, and tell the port to + ignore carrier. It should return FALSE on error. */ +extern boolean fsysdep_modem_begin_dial P((struct sconnection *qconn, + struct uuconf_dialer *qdial)); + +/* Finish dialing out on a modem. This should close the dialer device + if there is one. If the dialer and the port both support carrier, + the connection should be told to pay attention to carrier. If it + is possible to wait for carrier to come on, and the dialer and the + port both the port support carrier, it should wait until carrier + comes on. */ +extern boolean fsysdep_modem_end_dial P((struct sconnection *qconn, + struct uuconf_dialer *qdial)); + +/* System dependent initialization routines. */ +extern boolean fsysdep_stdin_init P((struct sconnection *qconn)); +extern boolean fsysdep_modem_init P((struct sconnection *qconn)); +extern boolean fsysdep_direct_init P((struct sconnection *qconn)); +#if HAVE_TCP +extern boolean fsysdep_tcp_init P((struct sconnection *qconn)); +#endif +#if HAVE_TLI +extern boolean fsysdep_tli_init P((struct sconnection *qconn)); +#endif + +#endif /* ! defined (CONN_H) */ diff --git a/gnu/libexec/uucp/common_sources/copy.c b/gnu/libexec/uucp/common_sources/copy.c new file mode 100644 index 0000000000..6956bb3d7c --- /dev/null +++ b/gnu/libexec/uucp/common_sources/copy.c @@ -0,0 +1,202 @@ +/* copy.c + Copy one file to another for the UUCP package. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char copy_rcsid[] = "$Id: copy.c,v 1.1 1993/08/04 19:30:44 jtc Exp $"; +#endif + +#include "uudefs.h" +#include "system.h" +#include "sysdep.h" + +#include +#include + +/* Copy one file to another. */ + +#if USE_STDIO + +boolean +fcopy_file (zfrom, zto, fpublic, fmkdirs) + const char *zfrom; + const char *zto; + boolean fpublic; + boolean fmkdirs; +{ + FILE *efrom; + boolean fret; + + efrom = fopen (zfrom, BINREAD); + if (efrom == NULL) + { + ulog (LOG_ERROR, "fopen (%s): %s", zfrom, strerror (errno)); + return FALSE; + } + + fret = fcopy_open_file (efrom, zto, fpublic, fmkdirs); + (void) fclose (efrom); + return fret; +} + +boolean +fcopy_open_file (efrom, zto, fpublic, fmkdirs) + FILE *efrom; + const char *zto; + boolean fpublic; + boolean fmkdirs; +{ + FILE *eto; + char ab[8192]; + int c; + + eto = esysdep_fopen (zto, fpublic, FALSE, fmkdirs); + if (eto == NULL) + return FALSE; + + while ((c = fread (ab, sizeof (char), sizeof ab, efrom)) != 0) + { + if (fwrite (ab, sizeof (char), (size_t) c, eto) != c) + { + ulog (LOG_ERROR, "fwrite: %s", strerror (errno)); + (void) fclose (eto); + (void) remove (zto); + return FALSE; + } + } + + if (fclose (eto) != 0) + { + ulog (LOG_ERROR, "fclose: %s", strerror (errno)); + (void) remove (zto); + return FALSE; + } + + return TRUE; +} + +#else /* ! USE_STDIO */ + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +boolean +fcopy_file (zfrom, zto, fpublic, fmkdirs) + const char *zfrom; + const char *zto; + boolean fpublic; + boolean fmkdirs; +{ + int ofrom; + boolean fret; + + ofrom = open (zfrom, O_RDONLY | O_NOCTTY, 0); + if (ofrom < 0) + { + ulog (LOG_ERROR, "open (%s): %s", zfrom, strerror (errno)); + return FALSE; + } + + fret = fcopy_open_file (ofrom, zto, fpublic, fmkdirs); + (void) close (ofrom); + return fret; +} + +boolean +fcopy_open_file (ofrom, zto, fpublic, fmkdirs) + int ofrom; + const char *zto; + boolean fpublic; + boolean fmkdirs; +{ + int oto; + char ab[8192]; + int c; + + /* These file mode arguments are from the UNIX version of sysdep.h; + each system dependent header file will need their own + definitions. */ + oto = creat (zto, fpublic ? IPUBLIC_FILE_MODE : IPRIVATE_FILE_MODE); + if (oto < 0) + { + if (errno == ENOENT && fmkdirs) + { + if (! fsysdep_make_dirs (zto, fpublic)) + return FALSE; + oto = creat (zto, + fpublic ? IPUBLIC_FILE_MODE : IPRIVATE_FILE_MODE); + } + if (oto < 0) + { + ulog (LOG_ERROR, "open (%s): %s", zto, strerror (errno)); + return FALSE; + } + } + + while ((c = read (ofrom, ab, sizeof ab)) > 0) + { + if (write (oto, ab, (size_t) c) != c) + { + ulog (LOG_ERROR, "write: %s", strerror (errno)); + (void) close (oto); + (void) remove (zto); + return FALSE; + } + } + + if (close (oto) < 0) + { + ulog (LOG_ERROR, "close: %s", strerror (errno)); + (void) remove (zto); + return FALSE; + } + + if (c < 0) + { + ulog (LOG_ERROR, "read: %s", strerror (errno)); + (void) remove (zto); + return FALSE; + } + + return TRUE; +} + +#endif /* ! USE_STDIO */ diff --git a/gnu/libexec/uucp/common_sources/cu.h b/gnu/libexec/uucp/common_sources/cu.h new file mode 100644 index 0000000000..5a514ee3bf --- /dev/null +++ b/gnu/libexec/uucp/common_sources/cu.h @@ -0,0 +1,80 @@ +/* cu.h + Header file for cu. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +/* The user settable variables supported by cu. */ + +/* The escape character used to introduce a special command. The + escape character is the first character of this string. */ +extern const char *zCuvar_escape; + +/* Whether to delay for a second before printing the host name after + seeing an escape character. */ +extern boolean fCuvar_delay; + +/* The input characters which finish a line. The escape character is + only recognized following one of these characters. */ +extern const char *zCuvar_eol; + +/* Whether to transfer binary data (nonprintable characters other than + newline and tab) when sending a file. If this is FALSE, then + newline is changed to carriage return. */ +extern boolean fCuvar_binary; + +/* A prefix string to use before sending a binary character from a + file; this is only used if fCuvar_binary is TRUE. */ +extern const char *zCuvar_binary_prefix; + +/* Whether to check for echoes of characters sent when sending a file. + This is ignored if fCuvar_binary is TRUE. */ +extern boolean fCuvar_echocheck; + +/* A character to look for after each newline is sent when sending a + file. The character is the first character in this string, except + that a '\0' means that no echo check is done. */ +extern const char *zCuvar_echonl; + +/* The timeout to use when looking for an character. */ +extern int cCuvar_timeout; + +/* The character to use to kill a line if an echo check fails. The + first character in this string is sent. */ +extern const char *zCuvar_kill; + +/* The number of times to try resending a line if the echo check keeps + failing. */ +extern int cCuvar_resend; + +/* The string to send at the end of a file sent with ~>. */ +extern const char *zCuvar_eofwrite; + +/* The string to look for to finish a file received with ~<. For tip + this is a collection of single characters, but I don't want to do + that because it means that there are characters which cannot be + received. */ +extern const char *zCuvar_eofread; + +/* Whether to provide verbose information when sending or receiving a + file. */ +extern boolean fCuvar_verbose; diff --git a/gnu/libexec/uucp/common_sources/getopt.h b/gnu/libexec/uucp/common_sources/getopt.h new file mode 100644 index 0000000000..1a70e02771 --- /dev/null +++ b/gnu/libexec/uucp/common_sources/getopt.h @@ -0,0 +1,120 @@ +/* Declarations for getopt. + Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + This file was modified slightly by Ian Lance Taylor, November 1992, + for Taylor UUCP. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Ian Lance Taylor added the following defines for + Taylor UUCP. This avoids reported conflicts with system getopt + definitions. */ +#define getopt gnu_getopt +#define optarg gnu_optarg +#define optind gnu_optind +#define opterr gnu_opterr + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ + const char *name; + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +enum _argtype +{ + no_argument, + required_argument, + optional_argument +}; + +extern int getopt P((int argc, char *const *argv, const char *shortopts)); +extern int getopt_long P((int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind)); +extern int getopt_long_only P((int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind)); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal P((int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only)); + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ diff --git a/gnu/libexec/uucp/common_sources/log.c b/gnu/libexec/uucp/common_sources/log.c new file mode 100644 index 0000000000..b55ca8b10b --- /dev/null +++ b/gnu/libexec/uucp/common_sources/log.c @@ -0,0 +1,699 @@ +/* log.c + Routines to add entries to the log files. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char log_rcsid[] = "$Id: log.c,v 1.1 1993/08/04 19:30:50 jtc Exp $"; +#endif + +#include + +#if ANSI_C +#include +#endif + +#if HAVE_TIME_H +#include +#endif + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" + +/* Local functions. */ + +static const char *zldate_and_time P((void)); + +/* Log file name. */ +static const char *zLogfile; + +/* The function to call when a LOG_FATAL error occurs. */ +static void (*pfLfatal) P((void)); + +/* Whether to go to a file. */ +static boolean fLfile; + +/* ID number. */ +static int iLid; + +/* The current user name. */ +static char *zLuser; + +/* The current system name. */ +static char *zLsystem; + +/* The current device name. */ +char *zLdevice; + +/* The open log file. */ +static FILE *eLlog; + +/* Whether we have tried to open the log file. We need this because + we don't want to keep trying to open the log file if we failed the + first time. It can't be static because under HAVE_HDB_LOGGING we + may have to write to various different log files. */ +static boolean fLlog_tried; + +#if DEBUG > 1 +/* Debugging file name. */ +static const char *zLdebugfile; + +/* The open debugging file. */ +static FILE *eLdebug; + +/* Whether we've tried to open the debugging file. */ +static boolean fLdebug_tried; + +/* Whether we've written out any debugging information. */ +static boolean fLdebugging; +#endif + +/* Statistics file name. */ +static const char *zLstatsfile; + +/* The open statistics file. */ +static FILE *eLstats; + +/* Whether we've tried to open the statistics file. */ +static boolean fLstats_tried; + +/* The array of signals. The elements are only set to TRUE by the + default signal handler. They are only set to FALSE if we don't + care whether we got the signal or not. */ +volatile sig_atomic_t afSignal[INDEXSIG_COUNT]; + +/* The array of signals to log. The elements are only set to TRUE by + the default signal handler. They are set to FALSE when the signal + is logged in ulog. This means that if a signal comes in at just + the right time we won't log it (or, rather, we'll log it once + instead of twice), but that is not a catatrophe. */ +volatile sig_atomic_t afLog_signal[INDEXSIG_COUNT]; + +/* Flag that indicates SIGHUP is worth logging. */ +boolean fLog_sighup = TRUE; + +/* Signal names to use when logging signals. */ +static const char * const azSignal_names[INDEXSIG_COUNT] = INDEXSIG_NAMES; + +/* If not NULL, ulog calls this function before outputting anything. + This is used to support cu. */ +void (*pfLstart) P((void)); + +/* If not NULL, ulog calls this function after outputting everything. + This is used to support cu. */ +void (*pfLend) P((void)); + +/* Set the function to call on a LOG_FATAL error. */ + +void +ulog_fatal_fn (pfn) + void (*pfn) P((void)); +{ + pfLfatal = pfn; +} + +/* Decide whether to send log message to the file or not. */ + +void +ulog_to_file (puuconf, ffile) + pointer puuconf; + boolean ffile; +{ + int iuuconf; + + iuuconf = uuconf_logfile (puuconf, &zLogfile); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + +#if DEBUG > 1 + iuuconf = uuconf_debugfile (puuconf, &zLdebugfile); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); +#endif + + iuuconf = uuconf_statsfile (puuconf, &zLstatsfile); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + fLfile = ffile; +} + +/* Set the ID number. This will be called by the usysdep_initialize + if there is something sensible to set it to. */ + +void +ulog_id (i) + int i; +{ + iLid = i; +} + +/* Set the user we are making log entries for. The arguments will be + copied into memory. */ + +void +ulog_user (zuser) + const char *zuser; +{ + ubuffree (zLuser); + zLuser = zbufcpy (zuser); +} + +/* Set the system name we are making log entries for. The name is copied + into memory. */ + +void +ulog_system (zsystem) + const char *zsystem; +{ + if (zsystem == NULL + || zLsystem == NULL + || strcmp (zsystem, zLsystem) != 0) + { + ubuffree (zLsystem); + zLsystem = zbufcpy (zsystem); +#if HAVE_HDB_LOGGING + /* Under HDB logging we now must write to a different log file. */ + ulog_close (); +#endif /* HAVE_HDB_LOGGING */ + } +} + +/* Set the device name. This is copied into memory. */ + +void +ulog_device (zdevice) + const char *zdevice; +{ + ubuffree (zLdevice); + zLdevice = zbufcpy (zdevice); +} + +/* Make a log entry. We make a token concession to non ANSI_C systems, + but it clearly won't always work. */ + +#if ! ANSI_C +#undef HAVE_VFPRINTF +#endif + +/*VARARGS2*/ +#if HAVE_VFPRINTF +void +ulog (enum tlog ttype, const char *zmsg, ...) +#else +void +ulog (ttype, zmsg, a, b, c, d, f, g, h, i, j) + enum tlog ttype; + const char *zmsg; +#endif +{ +#if HAVE_VFPRINTF + va_list parg; +#endif + FILE *e, *edebug; + boolean fstart, fend; + const char *zhdr, *zstr; + + /* Log any received signal. We do it this way to avoid calling ulog + from the signal handler. A few routines call ulog to get this + message out with zmsg == NULL. */ + { + static boolean fdoing_sigs; + + if (! fdoing_sigs) + { + int isig; + + fdoing_sigs = TRUE; + for (isig = 0; isig < INDEXSIG_COUNT; isig++) + { + if (afLog_signal[isig]) + { + afLog_signal[isig] = FALSE; + + /* Apparently SunOS sends SIGINT rather than SIGHUP + when hanging up, so we don't log either signal if + fLog_sighup is FALSE. */ + if ((isig != INDEXSIG_SIGHUP && isig != INDEXSIG_SIGINT) + || fLog_sighup) + ulog (LOG_ERROR, "Got %s signal", azSignal_names[isig]); + } + } + fdoing_sigs = FALSE; + } + } + + if (zmsg == NULL) + return; + +#if DEBUG > 1 + /* If we've had a debugging file open in the past, then we want to + write all log file entries to the debugging file even if it's + currently closed. */ + if (fLfile + && eLdebug == NULL + && ! fLdebug_tried + && (fLdebugging || (int) ttype >= (int) LOG_DEBUG)) + { + fLdebug_tried = TRUE; + eLdebug = esysdep_fopen (zLdebugfile, FALSE, TRUE, TRUE); + fLdebugging = TRUE; + } +#endif /* DEBUG > 1 */ + + if (! fLfile) + e = stderr; +#if DEBUG > 1 + else if ((int) ttype >= (int) LOG_DEBUG) + { + e = eLdebug; + + /* If we can't open the debugging file, don't output any + debugging messages. */ + if (e == NULL) + return; + } +#endif /* DEBUG > 1 */ + else + { + if (eLlog == NULL && ! fLlog_tried) + { + fLlog_tried = TRUE; +#if ! HAVE_HDB_LOGGING + eLlog = esysdep_fopen (zLogfile, TRUE, TRUE, TRUE); +#else /* HAVE_HDB_LOGGING */ + { + const char *zsys; + char *zfile; + + /* We want to write to .Log/program/system, e.g. + .Log/uucico/uunet. The system name may not be set. */ + if (zLsystem == NULL) + zsys = "ANY"; + else + zsys = zLsystem; + + zfile = zbufalc (strlen (zLogfile) + + strlen (abProgram) + + strlen (zsys) + + 1); + sprintf (zfile, zLogfile, abProgram, zsys); + eLlog = esysdep_fopen (zfile, TRUE, TRUE, TRUE); + ubuffree (zfile); + } +#endif /* HAVE_HDB_LOGGING */ + + if (eLlog == NULL) + { + /* We can't open the log file. We don't even have a + safe way to report this problem, since we may not be + able to write to stderr (it may, for example, be + attached to the incoming call). */ + if (pfLfatal != NULL) + (*pfLfatal) (); + usysdep_exit (FALSE); + } + } + + e = eLlog; + + /* eLlog might be NULL here because we might try to open the log + file recursively via esysdep_fopen. */ + if (e == NULL) + return; + } + + if (pfLstart != NULL) + (*pfLstart) (); + + edebug = NULL; +#if DEBUG > 1 + if ((int) ttype < (int) LOG_DEBUG) + edebug = eLdebug; +#endif + + fstart = TRUE; + fend = TRUE; + + switch (ttype) + { + case LOG_NORMAL: + zhdr = ""; + break; + case LOG_ERROR: + zhdr = "ERROR: "; + break; + case LOG_FATAL: + zhdr = "FATAL: "; + break; +#if DEBUG > 1 + case LOG_DEBUG: + zhdr = "DEBUG: "; + break; + case LOG_DEBUG_START: + zhdr = "DEBUG: "; + fend = FALSE; + break; + case LOG_DEBUG_CONTINUE: + zhdr = NULL; + fstart = FALSE; + fend = FALSE; + break; + case LOG_DEBUG_END: + zhdr = NULL; + fstart = FALSE; + break; +#endif + default: + zhdr = "???: "; + break; + } + + if (fstart) + { + if (! fLfile) + { + fprintf (e, "%s: ", abProgram); + if (edebug != NULL) + fprintf (edebug, "%s: ", abProgram); + } + else + { +#if HAVE_TAYLOR_LOGGING + fprintf (e, "%s ", abProgram); + if (edebug != NULL) + fprintf (edebug, "%s ", abProgram); +#else /* ! HAVE_TAYLOR_LOGGING */ + fprintf (e, "%s ", zLuser == NULL ? "uucp" : zLuser); + if (edebug != NULL) + fprintf (edebug, "%s ", zLuser == NULL ? "uucp" : zLuser); +#endif /* HAVE_TAYLOR_LOGGING */ + + fprintf (e, "%s ", zLsystem == NULL ? "-" : zLsystem); + if (edebug != NULL) + fprintf (edebug, "%s ", zLsystem == NULL ? "-" : zLsystem); + +#if HAVE_TAYLOR_LOGGING + fprintf (e, "%s ", zLuser == NULL ? "-" : zLuser); + if (edebug != NULL) + fprintf (edebug, "%s ", zLuser == NULL ? "-" : zLuser); +#endif /* HAVE_TAYLOR_LOGGING */ + + zstr = zldate_and_time (); + fprintf (e, "(%s", zstr); + if (edebug != NULL) + fprintf (edebug, "(%s", zstr); + + if (iLid != 0) + { +#if ! HAVE_HDB_LOGGING +#if HAVE_TAYLOR_LOGGING + fprintf (e, " %d", iLid); + if (edebug != NULL) + fprintf (edebug, " %d", iLid); +#else /* ! HAVE_TAYLOR_LOGGING */ + fprintf (e, "-%d", iLid); + if (edebug != NULL) + fprintf (edebug, "-%d", iLid); +#endif /* ! HAVE_TAYLOR_LOGGING */ +#else /* HAVE_HDB_LOGGING */ + + /* I assume that the second number here is meant to be + some sort of file sequence number, and that it should + correspond to the sequence number in the statistics + file. I don't have any really convenient way to do + this, so I won't unless somebody thinks it's very + important. */ + fprintf (e, ",%d,%d", iLid, 0); + if (edebug != NULL) + fprintf (edebug, ",%d,%d", iLid, 0); +#endif /* HAVE_HDB_LOGGING */ + } + + fprintf (e, ") "); + if (edebug != NULL) + fprintf (edebug, ") "); + + fprintf (e, "%s", zhdr); + if (edebug != NULL) + fprintf (edebug, "%s", zhdr); + } + } + +#if HAVE_VFPRINTF + va_start (parg, zmsg); + vfprintf (e, zmsg, parg); + va_end (parg); + if (edebug != NULL) + { + va_start (parg, zmsg); + vfprintf (edebug, zmsg, parg); + va_end (parg); + } +#else /* ! HAVE_VFPRINTF */ + fprintf (e, zmsg, a, b, c, d, f, g, h, i, j); + if (edebug != NULL) + fprintf (edebug, zmsg, a, b, c, d, f, g, h, i, j); +#endif /* ! HAVE_VFPRINTF */ + + if (fend) + { + fprintf (e, "\n"); + if (edebug != NULL) + fprintf (edebug, "\n"); + } + + (void) fflush (e); + if (edebug != NULL) + (void) fflush (edebug); + + if (pfLend != NULL) + (*pfLend) (); + + if (ttype == LOG_FATAL) + { + if (pfLfatal != NULL) + (*pfLfatal) (); + usysdep_exit (FALSE); + } + +#if CLOSE_LOGFILES + ulog_close (); +#endif +} + +/* Log a uuconf error. */ + +void +ulog_uuconf (ttype, puuconf, iuuconf) + enum tlog ttype; + pointer puuconf; + int iuuconf; +{ + char ab[512]; + + (void) uuconf_error_string (puuconf, iuuconf, ab, sizeof ab); + ulog (ttype, "%s", ab); +} + +/* Close the log file. There's nothing useful we can do with errors, + so we don't check for them. */ + +void +ulog_close () +{ + /* Make sure we logged any signal we received. */ + ulog (LOG_ERROR, (const char *) NULL); + + if (eLlog != NULL) + { + (void) fclose (eLlog); + eLlog = NULL; + fLlog_tried = FALSE; + } + +#if DEBUG > 1 + if (eLdebug != NULL) + { + (void) fclose (eLdebug); + eLdebug = NULL; + fLdebug_tried = FALSE; + } +#endif +} + +/* Add an entry to the statistics file. We may eventually want to put + failed file transfers in here, but we currently do not. */ + +/*ARGSUSED*/ +void +ustats (fsucceeded, zuser, zsystem, fsent, cbytes, csecs, cmicros, fmaster) + boolean fsucceeded; + const char *zuser; + const char *zsystem; + boolean fsent; + long cbytes; + long csecs; + long cmicros; + boolean fmaster; +{ + long cbps; + + /* The seconds and microseconds are now counted independently, so + they may be out of synch. */ + if (cmicros < 0) + { + csecs -= ((- cmicros) / 1000000L) + 1; + cmicros = 1000000L - ((- cmicros) % 1000000L); + } + if (cmicros >= 1000000L) + { + csecs += cmicros / 10000000L; + cmicros = cmicros % 1000000L; + } + + /* On a system which can determine microseconds we might very well + have both csecs == 0 and cmicros == 0. */ + if (csecs == 0 && cmicros < 1000) + cbps = 0; + else + { + long cmillis; + + /* This computation will not overflow provided csecs < 2147483 + and cbytes and cbps both fit in a long. */ + cmillis = csecs * 1000 + cmicros / 1000; + cbps = ((cbytes / cmillis) * 1000 + + ((cbytes % cmillis) * 1000) / cmillis); + } + + if (eLstats == NULL) + { + if (fLstats_tried) + return; + fLstats_tried = TRUE; + eLstats = esysdep_fopen (zLstatsfile, TRUE, TRUE, TRUE); + if (eLstats == NULL) + return; + } + +#if HAVE_TAYLOR_LOGGING + fprintf (eLstats, + "%s %s (%s) %s%s %ld bytes in %ld.%03ld seconds (%ld bytes/sec)\n", + zuser, zsystem, zldate_and_time (), + fsucceeded ? "" : "failed after ", + fsent ? "sent" : "received", + cbytes, csecs, cmicros / 1000, cbps); +#endif /* HAVE_TAYLOR_LOGGING */ +#if HAVE_V2_LOGGING + fprintf (eLstats, + "%s %s (%s) (%ld) %s %s %ld bytes %ld seconds\n", + zuser, zsystem, zldate_and_time (), + (long) time ((time_t *) NULL), + fsent ? "sent" : "received", + fsucceeded ? "data" : "failed after", + cbytes, csecs + cmicros / 500000); +#endif /* HAVE_V2_LOGGING */ +#if HAVE_HDB_LOGGING + { + static int iseq; + + /* I don't know what the 'C' means. The sequence number should + probably correspond to the sequence number in the log file, but + that is currently always 0; using this fake sequence number + will still at least reveal which transfers are from different + calls. We don't report a failed data transfer with this + format. */ + if (! fsucceeded) + return; + ++iseq; + fprintf (eLstats, + "%s!%s %c (%s) (C,%d,%d) [%s] %s %ld / %ld.%03ld secs, %ld %s\n", + zsystem, zuser, fmaster ? 'M' : 'S', zldate_and_time (), + iLid, iseq, zLdevice == NULL ? "unknown" : zLdevice, + fsent ? "->" : "<-", + cbytes, csecs, cmicros / 1000, cbps, + "bytes/sec"); + } +#endif /* HAVE_HDB_LOGGING */ + + (void) fflush (eLstats); + +#if CLOSE_LOGFILES + ustats_close (); +#endif +} + +/* Close the statistics file. */ + +void +ustats_close () +{ + if (eLstats != NULL) + { + if (fclose (eLstats) != 0) + ulog (LOG_ERROR, "fclose: %s", strerror (errno)); + eLstats = NULL; + fLstats_tried = FALSE; + } +} + +/* Return the date and time in a form used for a log entry. */ + +static const char * +zldate_and_time () +{ + long isecs, imicros; + struct tm s; +#if HAVE_TAYLOR_LOGGING + static char ab[sizeof "1991-12-31 12:00:00.00"]; +#endif +#if HAVE_V2_LOGGING + static char ab[sizeof "12/31-12:00"]; +#endif +#if HAVE_HDB_LOGGING + static char ab[sizeof "12/31-12:00:00"]; +#endif + + isecs = ixsysdep_time (&imicros); + usysdep_localtime (isecs, &s); + +#if HAVE_TAYLOR_LOGGING + sprintf (ab, "%04d-%02d-%02d %02d:%02d:%02d.%02d", + s.tm_year + 1900, s.tm_mon + 1, s.tm_mday, s.tm_hour, + s.tm_min, s.tm_sec, (int) (imicros / 10000)); +#endif +#if HAVE_V2_LOGGING + sprintf (ab, "%d/%d-%02d:%02d", s.tm_mon + 1, s.tm_mday, + s.tm_hour, s.tm_min); +#endif +#if HAVE_HDB_LOGGING + sprintf (ab, "%d/%d-%02d:%02d:%02d", s.tm_mon + 1, s.tm_mday, + s.tm_hour, s.tm_min, s.tm_sec); +#endif + + return ab; +} diff --git a/gnu/libexec/uucp/common_sources/policy.h b/gnu/libexec/uucp/common_sources/policy.h new file mode 100644 index 0000000000..4c829bf42d --- /dev/null +++ b/gnu/libexec/uucp/common_sources/policy.h @@ -0,0 +1,521 @@ +/* policy.h + Configuration file for policy decisions. To be edited on site. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +/* This header file contains macro definitions which must be set by + each site before compilation. The first few are system + characteristics that can not be easily discovered by the + configuration script. Most are configuration decisions that must + be made by the local administrator. */ + +/* System characteristics. */ + +/* This code tries to use several ANSI C features, including + prototypes, stdarg.h, the const qualifier and the types void + (including void * pointers) and unsigned char. By default it will + use these features if the compiler defines __STDC__. If your + compiler supports these features but does not define __STDC__, you + should set ANSI_C to 1. If your compiler does not support these + features but defines __STDC__ (no compiler should do this, in my + opinion), you should set ANSI_C to 0. In most cases (or if you're + not sure) just leave the line below commented out. */ +/* #define ANSI_C 1 */ + +/* Set USE_STDIO to 1 if data files should be read using the stdio + routines (fopen, fread, etc.) rather than the UNIX unbuffered I/O + calls (open, read, etc.). Unless you know your stdio is really + rotten, you should leave this as 1. */ +#define USE_STDIO 1 + +/* Exactly one of the following macros must be set to 1. Many modern + systems support more than one of these choices through some form of + compilation environment, in which case the setting will depend on + the compilation environment you use. If you have a reasonable + choice between options, I suspect that TERMIO or TERMIOS will be + more efficient than TTY, but I have not done any head to head + comparisons. + + If you don't set any of these macros, the code below will guess. + It will doubtless be wrong on some systems. + + HAVE_BSD_TTY -- Use the 4.2BSD tty routines + HAVE_SYSV_TERMIO -- Use the System V termio routines + HAVE_POSIX_TERMIOS -- Use the POSIX termios routines + */ +#define HAVE_BSD_TTY 0 +#define HAVE_SYSV_TERMIO 0 +#define HAVE_POSIX_TERMIOS 1 + +/* This code tries to guess which terminal driver to use if you did + not make a choice above. It is in this file to make it easy to + figure out what's happening if something goes wrong. */ + +#if HAVE_BSD_TTY + HAVE_SYSV_TERMIO + HAVE_POSIX_TERMIOS == 0 +#if HAVE_CBREAK +#undef HAVE_BSD_TTY +#define HAVE_BSD_TTY 1 +#else +#undef HAVE_SYSV_TERMIO +#define HAVE_SYSV_TERMIO 1 +#endif +#endif + +/* On some systems a write to a serial port will block even if the + file descriptor has been set to not block. File transfer can be + more efficient if the package knows that a write to the serial port + will not block; however, if the write does block unexpectedly then + data loss is possible at high speeds. + + If writes to a serial port always block even when requested not to, + you should set HAVE_UNBLOCKED_WRITES to 0; otherwise you should set + it to 1. In general on System V releases without STREAMS-based + ttys (e.g., before SVR4) HAVE_UNBLOCKED_WRITES should be 0 and on + BSD or SVR4 it should be 1. + + If HAVE_UNBLOCKED_WRITES is set to 1 when it should be 0 you may + see an unexpectedly large number of transmission errors, or, if you + have hardware handshaking, transfer times may be lower than + expected (but then, they always are). If HAVE_UNBLOCKED_WRITES is + set to 0 when it should be 1, file transfer will use more CPU time + than necessary. If you are unsure, setting HAVE_UNBLOCKED_WRITES + to 0 should always be safe. */ +#define HAVE_UNBLOCKED_WRITES 1 + +/* When the code does do a blocking write, it wants to write the + largest amount of data which the kernel will accept as a single + unit. On BSD this is typically the value of OBUFSIZ in + , usually 100. On System V before SVR4 this is + typically the size of a clist, CLSIZE in , which is + usually 64. On SVR4, which uses STREAMS-based ttys, 2048 is + reasonable. Define SINGLE_WRITE to the correct value for your + system. If SINGLE_WRITE is too large, data loss may occur. If + SINGLE_WRITE is too small, file transfer will use more CPU time + than necessary. If you have no idea, 64 should work on most modern + systems. */ +#define SINGLE_WRITE 100 + +/* Some tty drivers, such as those from SCO and AT&T's Unix PC, have a + bug in the implementation of ioctl() that causes CLOCAL to be + ineffective until the port is opened a second time. If + HAVE_CLOCAL_BUG is set to 1, code will be added to do this second + open on the port. Set this if you are getting messages that say + "Line disconnected" while in the dial chat script after only + writing the first few characters to the port. This bug causes the + resetting of CLOCAL to have no effect, so the "\m" (require + carrier) escape sequence won't function properly in dialer chat + scripts. */ +#define HAVE_CLOCAL_BUG 0 + +/* On some systems, such as SCO Xenix, resetting DTR on a port + apparently prevents getty from working on the port, and thus + prevents anybody from dialing in. If HAVE_RESET_BUG is set to 1, + DTR will not be reset when a serial port is closed. */ +#define HAVE_RESET_BUG 0 + +/* The Sony NEWS reportedly handles no parity by clearing both the odd + and even parity bits in the sgtty structure, unlike most BSD based + systems in which no parity is indicated by setting both the odd and + even parity bits. Setting HAVE_PARITY_BUG to 1 will handle this + correctly. */ +#define HAVE_PARITY_BUG 0 + +#if HAVE_BSD_TTY +#ifdef sony +#undef HAVE_PARITY_BUG +#define HAVE_PARITY_BUG 1 +#endif +#endif + +/* On Ultrix 4.0, at least, setting CBREAK causes input characters to + be stripped, regardless of the setting of LPASS8 and LLITOUT. This + can be worked around by using the termio call to reset ISTRIP. + This probably does not apply to any other operating system. + Setting HAVE_STRIP_BUG to 1 will use this workaround. */ +#define HAVE_STRIP_BUG 0 + +#if HAVE_BSD_TTY +#ifdef ultrix +#undef HAVE_STRIP_BUG +#define HAVE_STRIP_BUG 1 +#endif +#endif + +/* TIMES_TICK is the fraction of a second which times(2) returns (for + example, if times returns 100ths of a second TIMES_TICK should be + set to 100). On a true POSIX system (one which has the sysconf + function and also has _SC_CLK_TCK defined in ) TIMES_TICK + may simply be left as 0. On some systems the environment variable + HZ is what you want for TIMES_TICK, but on some other systems HZ + has the wrong value; check the man page. If you leave this set to + 0, the code will try to guess; it will doubtless be wrong on some + non-POSIX systems. If TIMES_TICK is wrong the code may report + incorrect file transfer times in the statistics file, but on many + systems times(2) will actually not be used and this value will not + matter at all. */ +#define TIMES_TICK 0 + +/* If your system does not support saved set user ID, set + HAVE_SAVED_SETUID to 0. However, this is ignored if your system + has the setreuid function. Most modern Unixes have one or the + other. If your system has the setreuid function, don't worry about + this define, or about the following discussion. + + If you set HAVE_SAVED_SETUID to 0, you will not be able to use uucp + to transfer files that the uucp user can not read. Basically, you + will only be able to use uucp on world-readable files. If you set + HAVE_SAVED_SETUID to 1, but your system does not have saved set + user ID, uucp will fail with an error message whenever anybody + other than the uucp user uses it. */ +#define HAVE_SAVED_SETUID 1 + +/* On some systems, such as the DG Aviion and, possibly, the RS/6000, + the setreuid function is broken. It should be possible to use + setreuid to swap the real and effective user ID's, but on some + systems it will not change the real user ID (I believe this is due + to a misreading of the POSIX standard). On such a system you must + set HAVE_BROKEN_SETREUID to 1; if you do not, you will get error + messages from setreuid. Systems on which setreuid exists but is + broken pretty much always have saved setuid. */ +#define HAVE_BROKEN_SETREUID 0 + +/* On the 3B2, and possibly other systems, nap takes an argument in + hundredths of a second rather than milliseconds. I don't know of + any way to test for this. Set HAVE_HUNDREDTHS_NAP to 1 if this is + true on your system. This does not matter if your system does not + have the nap function. */ +#define HAVE_HUNDREDTHS_NAP 0 + +/* Set PS_PROGRAM to the program to run to get a process status, + including the arguments to pass it. This is used by ``uustat -p''. + Set HAVE_PS_MULTIPLE to 1 if a comma separated list of process + numbers may be appended (e.g. ``ps -flp1,10,100''). Otherwise ps + will be invoked several times, with a single process number append + each time. The default definitions should work on most systems, + although some (such as the NeXT) will complain about the 'p' + option; for those, use the second set of definitions. The third + set of definitions are appropriate for System V. To use the second + or third set of definitions, change the ``#if 1'' to ``#if 0'' and + change the appropriate ``#if 0'' to ``#if 1''. */ +#if 1 +#define PS_PROGRAM "/bin/ps -lp" +#define HAVE_PS_MULTIPLE 0 +#endif +#if 0 +#define PS_PROGRAM "/bin/ps -l" +#define HAVE_PS_MULTIPLE 0 +#endif +#if 0 +#define PS_PROGRAM "/bin/ps -flp" +#define HAVE_PS_MULTIPLE 1 +#endif + +/* If you use other programs that also lock devices, such as cu or + uugetty, the other programs and UUCP must agree on whether a device + is locked. This is typically done by creating a lock file in a + specific directory; the lock files are generally named + LCK..something or LK.something. If the LOCKDIR macro is defined, + these lock files will be placed in the named directory; otherwise + they will be placed in the default spool directory. On some HDB + systems the lock files are placed in /etc/locks. On some they are + placed in /usr/spool/locks. On the NeXT they are placed in + /usr/spool/uucp/LCK. */ +/* #define LOCKDIR "/usr/spool/uucp" */ +/* #define LOCKDIR "/etc/locks" */ +/* #define LOCKDIR "/usr/spool/locks" */ +/* #define LOCKDIR "/usr/spool/uucp/LCK" */ +#define LOCKDIR "/var/spool/lock" + +/* You must also specify the format of the lock files by setting + exactly one of the following macros to 1. Check an existing lock + file to decide which of these choices is more appropriate. + + The HDB style is to write the locking process ID in ASCII, passed + to ten characters, followed by a newline. + + The V2 style is to write the locking process ID as four binary + bytes in the host byte order. Many BSD derived systems use this + type of lock file, including the NeXT. + + SCO lock files are similar to HDB lock files, but always lock the + lowercase version of the tty (i.e., LCK..tty2a is created if you + are locking tty2A). They are appropriate if you are using Taylor + UUCP on an SCO Unix, SCO Xenix, or SCO Open Desktop system. + + SVR4 lock files are also similar to HDB lock files, but they use a + different naming convention. The filenames are LK.xxx.yyy.zzz, + where xxx is the major device number of the device holding the + special device file, yyy is the major device number of the port + device itself, and zzz is the minor device number of the port + device. + + Coherent use a completely different method of terminal locking. + See unix/cohtty for details. For locks other than for terminals, + HDB type lock files are used. */ +#define HAVE_V2_LOCKFILES 0 +#define HAVE_HDB_LOCKFILES 1 +#define HAVE_SCO_LOCKFILES 0 +#define HAVE_SVR4_LOCKFILES 0 +#define HAVE_COHERENT_LOCKFILES 0 + +/* If your system supports Internet mail addresses (which look like + user@host.domain rather than system!user), HAVE_INTERNET_MAIL + should be set to 1. This is checked by uuxqt when sending error + (or success, if requested) notifications to the person who + submitted the job. */ +#define HAVE_INTERNET_MAIL 1 + +/* Adminstrative decisions. */ + +/* Set USE_RCS_ID to 1 if you want the RCS ID strings compiled into + the executable. Leaving them out will decrease the executable + size. Leaving them in will make it easier to determine which + version you are running. */ +#define USE_RCS_ID 1 + +/* DEBUG controls how much debugging information is compiled into the + code. If DEBUG is defined as 0, no sanity checks will be done and + no debugging messages will be compiled in. If DEBUG is defined as + 1 sanity checks will be done but there will still be no debugging + messages. If DEBUG is 2 than debugging messages will be compiled + in. When initially testing, DEBUG should be 2, and you should + probably leave it at 2 unless a small reduction in the executable + file size will be very helpful. */ +#define DEBUG 2 + +/* Set the default grade to use for a uucp command if the -g option is + not used. The grades, from highest to lowest, are 0 to 9, A to Z, + a to z. */ +#define BDEFAULT_UUCP_GRADE ('N') + +/* Set the default grade to use for a uux command if the -g option is + not used. */ +#define BDEFAULT_UUX_GRADE ('N') + +/* To compile in use of the new style of configuration files described + in the documentation, set HAVE_TAYLOR_CONFIG to 1. */ +#define HAVE_TAYLOR_CONFIG 1 + +/* To compile in use of V2 style configuration files (L.sys, L-devices + and so on), set HAVE_V2_CONFIG to 1. To compile in use of HDB + style configuration files (Systems, Devices and so on) set + HAVE_HDB_CONFIG to 1. The files will be looked up in the + oldconfigdir directory as defined in the Makefile. + + You may set any or all of HAVE_TAYLOR_CONFIG, HAVE_V2_CONFIG and + HAVE_HDB_CONFIG to 1 (you must set at least one of the macros). + When looking something up (a system, a port, etc.) the new style + configuration files will be read first, followed by the V2 + configuration files, followed by the HDB configuration files. */ +#define HAVE_V2_CONFIG 1 +#define HAVE_HDB_CONFIG 1 + +/* Exactly one of the following macros must be set to 1. The exact + format of the spool directories is explained in unix/spool.c. + + SPOOLDIR_V2 -- Use a Version 2 (original UUCP) style spool directory + SPOOLDIR_BSD42 -- Use a BSD 4.2 style spool directory + SPOOLDIR_BSD43 -- Use a BSD 4.3 style spool directory + SPOOLDIR_HDB -- Use a HDB (BNU) style spool directory + SPOOLDIR_ULTRIX -- Use an Ultrix style spool directory + SPOOLDIR_SVR4 -- Use a System V Release 4 spool directory + SPOOLDIR_TAYLOR -- Use a new style spool directory + + If you are not worried about compatibility with a currently running + UUCP, use SPOOLDIR_TAYLOR. */ +#define SPOOLDIR_V2 0 +#define SPOOLDIR_BSD42 0 +#define SPOOLDIR_BSD43 0 +#define SPOOLDIR_HDB 0 +#define SPOOLDIR_ULTRIX 0 +#define SPOOLDIR_SVR4 0 +#define SPOOLDIR_TAYLOR 1 + +/* You must select which type of logging you want by setting exactly + one of the following to 1. These control output to the log file + and to the statistics file. + + If you define HAVE_TAYLOR_LOGGING, each line in the log file will + look something like this: + + uucico uunet uucp (1991-12-10 09:04:34.45 16390) Receiving uunet/D./D.uunetSwJ72 + + and each line in the statistics file will look something like this: + + uucp uunet (1991-12-10 09:04:40.20) received 2371 bytes in 5 seconds (474 bytes/sec) + + If you define HAVE_V2_LOGGING, each line in the log file will look + something like this: + + uucico uunet uucp (12/10-09:04 16390) Receiving uunet/D./D.uunetSwJ72 + + and each line in the statistics file will look something like this: + + uucp uunet (12/10-09:04 16390) (692373862) received data 2371 bytes 5 seconds + + If you define HAVE_HDB_LOGGING, each program will by default use a + separate log file. For uucico talking to uunet, for example, it + will be /usr/spool/uucp/.Log/uucico/uunet. Each line will look + something like this: + + uucp uunet (12/10-09:04:22,16390,1) Receiving uunet/D./D.uunetSwJ72 + + and each line in the statistics file will look something like this: + + uunet!uucp M (12/10-09:04:22) (C,16390,1) [ttyXX] <- 2371 / 5.000 secs, 474 bytes/sec + + The main reason to prefer one format over another is that you may + have shell scripts which expect the files to have a particular + format. If you have none, choose whichever format you find more + appealing. */ +#define HAVE_TAYLOR_LOGGING 1 +#define HAVE_V2_LOGGING 0 +#define HAVE_HDB_LOGGING 0 + +/* If you would like the log, debugging and statistics files to be + closed after each message, set CLOSE_LOGFILES to 1. This will + permit the log files to be easily moved. If a log file does not + exist when a new message is written out, it will be created. + Setting CLOSE_LOGFILES to 1 will obviously require slightly more + processing time. */ +#define CLOSE_LOGFILES 0 + +/* The name of the default spool directory. If HAVE_TAYLOR_CONFIG is + set to 1, this may be overridden by the ``spool'' command in the + configuration file. */ +#define SPOOLDIR "/var/spool/uucp" + +/* The name of the default public directory. If HAVE_TAYLOR_CONFIG is + set to 1, this may be overridden by the ``pubdir'' command in the + configuration file. Also, a particular system may be given a + specific public directory by using the ``pubdir'' command in the + system file. */ +#define PUBDIR "/var/spool/uucppublic" + +/* The default command path. This is a space separated list of + directories. Remote command executions requested by uux are looked + up using this path. If you are using HAVE_TAYLOR_CONFIG, the + command path may be overridden for a particular system. For most + systems, you should just make sure that the programs rmail and + rnews can be found using this path. */ +#define CMDPATH "/bin /usr/bin /usr/local/bin" + +/* The default amount of free space to require for systems that do not + specify an amount with the ``free-space'' command. This is only + used when talking to another instance of Taylor UUCP; if accepting + a file would not leave at least this many bytes free on the disk, + it will be refused. */ +#define DEFAULT_FREE_SPACE (50000) + +/* While a file is being received, Taylor UUCP will periodically check + to see if there is enough free space remaining on the disk. If + there is not enough space available on the disk (as determined by + DEFAULT_FREE_SPACE, above, or the ``free-space'' command for the + system) the communication will be aborted. The disk will be + checked each time FREE_SPACE_DELTA bytes are received. Lower + values of FREE_SPACE_DELTA are less likely to fill up the disk, but + will also waste more time checking the amount of free space. To + avoid checking the disk while the file is being received, set + FREE_SPACE_DELTA to 0. */ +#define FREE_SPACE_DELTA (10240) + +/* It is possible for an execute job to request to be executed using + sh(1), rather than execve(2). This is such a security risk, it is + being disabled by default; to allow such jobs, set the following + macro to 1. */ +#define ALLOW_SH_EXECUTION 0 + +/* If a command executed on behalf of a remote system takes a filename + as an argument, a security breach may be possible (note that on my + system neither of the default commands, rmail and rnews, take + filename arguments). If you set ALLOW_FILENAME_ARGUMENTS to 0, all + arguments to a command will be checked; if any argument + 1) starts with ../ + 2) contains the string /../ + 3) begins with a / but does not name a file that may be sent or + received (according to the specified ``remote-send'' and + ``remote-receive'') + the command will be rejected. By default, any argument is + permitted. */ +#define ALLOW_FILENAME_ARGUMENTS 1 + +#if HAVE_TAYLOR_LOGGING + +/* The default log file when using HAVE_TAYLOR_LOGGING. When using + HAVE_TAYLOR_CONFIG, this may be overridden by the ``logfile'' + command in the configuration file. */ +#define LOGFILE "/var/spool/uucp/Log" + +/* The default statistics file when using HAVE_TAYLOR_LOGGING. When + using HAVE_TAYLOR_CONFIG, this may be overridden by the + ``statfile'' command in the configuration file. */ +#define STATFILE "/var/spool/uucp/Stats" + +/* The default debugging file when using HAVE_TAYLOR_LOGGING. When + using HAVE_TAYLOR_CONFIG, this may be overridden by the + ``debugfile'' command in the configuration file. */ +#define DEBUGFILE "/var/spool/uucp/Debug" + +#endif /* HAVE_TAYLOR_LOGGING */ + +#if HAVE_V2_LOGGING + +/* The default log file when using HAVE_V2_LOGGING. When using + HAVE_TAYLOR_CONFIG, this may be overridden by the ``logfile'' + command in the configuration file. */ +#define LOGFILE "/var/spool/uucp/LOGFILE" + +/* The default statistics file when using HAVE_V2_LOGGING. When using + HAVE_TAYLOR_CONFIG, this may be overridden by the ``statfile'' + command in the configuration file. */ +#define STATFILE "/var/spool/uucp/SYSLOG" + +/* The default debugging file when using HAVE_V2_LOGGING. When using + HAVE_TAYLOR_CONFIG, this may be overridden by the ``debugfile'' + command in the configuration file. */ +#define DEBUGFILE "/var/spool/uucp/DEBUG" + +#endif /* HAVE_V2_LOGGING */ + +#if HAVE_HDB_LOGGING + +/* The default log file when using HAVE_HDB_LOGGING. When using + HAVE_TAYLOR_CONFIG, this may be overridden by the ``logfile'' + command in the configuration file. The first %s in the string will + be replaced by the program name (e.g. uucico); the second %s will + be replaced by the system name (if there is no appropriate system, + "ANY" will be used). No other '%' character may appear in the + string. */ +#define LOGFILE "/var/spool/uucp/.Log/%s/%s" + +/* The default statistics file when using HAVE_HDB_LOGGING. When using + HAVE_TAYLOR_CONFIG, this may be overridden by the ``statfile'' + command in the configuration file. */ +#define STATFILE "/var/spool/uucp/.Admin/xferstats" + +/* The default debugging file when using HAVE_HDB_LOGGING. When using + HAVE_TAYLOR_CONFIG, this may be overridden by the ``debugfile'' + command in the configuration file. */ +#define DEBUGFILE "/var/spool/uucp/.Admin/audit.local" + +#endif /* HAVE_HDB_LOGGING */ diff --git a/gnu/libexec/uucp/common_sources/prot.c b/gnu/libexec/uucp/common_sources/prot.c new file mode 100644 index 0000000000..4cc224ef0f --- /dev/null +++ b/gnu/libexec/uucp/common_sources/prot.c @@ -0,0 +1,237 @@ +/* prot.c + Protocol support routines to move commands and data around. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char prot_rcsid[] = "$Id: prot.c,v 1.1 1993/08/04 19:30:55 jtc Exp $"; +#endif + +#include + +#include "uudefs.h" +#include "system.h" +#include "conn.h" +#include "prot.h" + +/* Variables visible to the protocol-specific routines. */ + +/* Buffer to hold received data. */ +char abPrecbuf[CRECBUFLEN]; + +/* Index of start of data in abPrecbuf. */ +int iPrecstart; + +/* Index of end of data (first byte not included in data) in abPrecbuf. */ +int iPrecend; + +/* We want to output and input at the same time, if supported on this + machine. If we have something to send, we send it all while + accepting a large amount of data. Once we have sent everything we + look at whatever we have received. If data comes in faster than we + can send it, we may run out of buffer space. */ + +boolean +fsend_data (qconn, zsend, csend, fdoread) + struct sconnection *qconn; + const char *zsend; + size_t csend; + boolean fdoread; +{ + if (! fdoread) + return fconn_write (qconn, zsend, csend); + + while (csend > 0) + { + size_t crec, csent; + + if (iPrecend < iPrecstart) + crec = iPrecstart - iPrecend - 1; + else + { + crec = CRECBUFLEN - iPrecend; + if (iPrecstart == 0) + --crec; + } + + csent = csend; + + if (! fconn_io (qconn, zsend, &csent, abPrecbuf + iPrecend, &crec)) + return FALSE; + + csend -= csent; + zsend += csent; + + iPrecend = (iPrecend + crec) % CRECBUFLEN; + } + + return TRUE; +} + +/* Read data from the other system when we have nothing to send. The + argument cneed is the amount of data the caller wants, and ctimeout + is the timeout in seconds. The function sets *pcrec to the amount + of data which was actually received, which may be less than cneed + if there isn't enough room in the receive buffer. If no data is + received before the timeout expires, *pcrec will be returned as 0. + If an error occurs, the function returns FALSE. If the freport + argument is FALSE, no error should be reported. */ + +boolean +freceive_data (qconn, cneed, pcrec, ctimeout, freport) + struct sconnection *qconn; + size_t cneed; + size_t *pcrec; + int ctimeout; + boolean freport; +{ + /* Set *pcrec to the maximum amount of data we can read. fconn_read + expects *pcrec to be the buffer size, and sets it to the amount + actually received. */ + if (iPrecend < iPrecstart) + *pcrec = iPrecstart - iPrecend - 1; + else + { + *pcrec = CRECBUFLEN - iPrecend; + if (iPrecstart == 0) + --(*pcrec); + } + +#if DEBUG > 0 + /* If we have no room in the buffer, we're in trouble. The + protocols must be written to ensure that this can't happen. */ + if (*pcrec == 0) + ulog (LOG_FATAL, "freceive_data: No room in buffer"); +#endif + + /* If we don't have room for all the data the caller wants, we + simply have to expect less. We'll get the rest later. */ + if (*pcrec < cneed) + cneed = *pcrec; + + if (! fconn_read (qconn, abPrecbuf + iPrecend, pcrec, cneed, ctimeout, + freport)) + return FALSE; + + iPrecend = (iPrecend + *pcrec) % CRECBUFLEN; + + return TRUE; +} + +/* Read a single character. Get it out of the receive buffer if it's + there, otherwise ask freceive_data for at least one character. + This is used because as a protocol is shutting down freceive_data + may read ahead and eat characters that should be read outside the + protocol routines. We call freceive_data rather than fconn_read + with an argument of 1 so that we can get all the available data in + a single system call. The ctimeout argument is the timeout in + seconds; the freport argument is FALSE if no error should be + reported. This returns a character, or -1 on timeout or -2 on + error. */ + +int +breceive_char (qconn, ctimeout, freport) + struct sconnection *qconn; + int ctimeout; + boolean freport; +{ + char b; + + if (iPrecstart == iPrecend) + { + size_t crec; + + if (! freceive_data (qconn, sizeof (char), &crec, ctimeout, freport)) + return -2; + if (crec == 0) + return -1; + } + + b = abPrecbuf[iPrecstart]; + iPrecstart = (iPrecstart + 1) % CRECBUFLEN; + return BUCHAR (b); +} + +/* Send mail about a file transfer. We send to the given mailing + address if there is one, otherwise to the user. */ + +boolean +fmail_transfer (fsuccess, zuser, zmail, zwhy, zfromfile, zfromsys, + ztofile, ztosys, zsaved) + boolean fsuccess; + const char *zuser; + const char *zmail; + const char *zwhy; + const char *zfromfile; + const char *zfromsys; + const char *ztofile; + const char *ztosys; + const char *zsaved; +{ + const char *zsendto; + const char *az[20]; + int i; + + if (zmail != NULL && *zmail != '\0') + zsendto = zmail; + else + zsendto = zuser; + + i = 0; + az[i++] = "The file\n\t"; + if (zfromsys != NULL) + { + az[i++] = zfromsys; + az[i++] = "!"; + } + az[i++] = zfromfile; + if (fsuccess) + az[i++] = "\nwas successfully transferred to\n\t"; + else + az[i++] = "\ncould not be transferred to\n\t"; + if (ztosys != NULL) + { + az[i++] = ztosys; + az[i++] = "!"; + } + az[i++] = ztofile; + az[i++] = "\nas requested by\n\t"; + az[i++] = zuser; + if (! fsuccess) + { + az[i++] = "\nfor the following reason:\n\t"; + az[i++] = zwhy; + az[i++] = "\n"; + } + if (zsaved != NULL) + { + az[i++] = zsaved; + az[i++] = "\n"; + } + + return fsysdep_mail (zsendto, + fsuccess ? "UUCP succeeded" : "UUCP failed", + i, az); +} diff --git a/gnu/libexec/uucp/common_sources/prot.h b/gnu/libexec/uucp/common_sources/prot.h new file mode 100644 index 0000000000..4e2bb584d8 --- /dev/null +++ b/gnu/libexec/uucp/common_sources/prot.h @@ -0,0 +1,250 @@ +/* prot.h + Protocol header file. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +/* We need the definition of uuconf_cmdtab to declare the protocol + parameter arrays. */ +#ifndef UUCONF_H +#include "uuconf.h" +#endif + +#if ANSI_C +/* These structures are used in prototypes but are not defined in this + header file. */ +struct sdaemon; +struct sconnection; +struct stransfer; +#endif + +/* The sprotocol structure holds information and functions for a specific + protocol (e.g. the 'g' protocol). */ + +struct sprotocol +{ + /* The name of the protocol (e.g. 'g'). */ + char bname; + /* Reliability requirements, an or of UUCONF_RELIABLE_xxx defines + from uuconf.h. */ + int ireliable; + /* The maximum number of channels this protocol can support. */ + int cchans; + /* Protocol parameter commands. */ + struct uuconf_cmdtab *qcmds; + /* A routine to start the protocol. If *pzlog is set to be + non-NULL, it is an informative message to be logged; it should + then be passed to ubuffree. */ + boolean (*pfstart) P((struct sdaemon *qdaemon, char **pzlog)); + /* Shutdown the protocol. */ + boolean (*pfshutdown) P((struct sdaemon *qdaemon)); + /* Send a command to the other side. */ + boolean (*pfsendcmd) P((struct sdaemon *qdaemon, const char *z, + int ilocal, int iremote)); + /* Get buffer to space to fill with data. This should set *pcdata + to the amount of data desired. */ + char *(*pzgetspace) P((struct sdaemon *qdaemon, size_t *pcdata)); + /* Send data to the other side. The argument z must be a return + value of pzgetspace. The ipos argument is the file position, and + is ignored by most protocols. */ + boolean (*pfsenddata) P((struct sdaemon *qdaemon, char *z, size_t c, + int ilocal, int iremote, long ipos)); + /* Wait for data to come in and call fgot_data with it until + fgot_data sets *pfexit. */ + boolean (*pfwait) P((struct sdaemon *qdaemon)); + /* Handle any file level actions that need to be taken. If a file + transfer is starting rather than ending, fstart is TRUE. If the + file is being sent rather than received, fsend is TRUE. If + fstart and fsend are both TRUE, cbytes holds the size of the + file. If *pfhandled is set to TRUE, then the protocol routine + has taken care of queueing up qtrans for the next action. */ + boolean (*pffile) P((struct sdaemon *qdaemon, struct stransfer *qtrans, + boolean fstart, boolean fsend, long cbytes, + boolean *pfhandled)); +}; + +/* Send data to the other system. If the fread argument is TRUE, this + will also receive data into the receive buffer abPrecbuf; fread is + passed as TRUE if the protocol expects data to be coming back, to + make sure the input buffer does not fill up. Returns FALSE on + error. */ +extern boolean fsend_data P((struct sconnection *qconn, + const char *zsend, size_t csend, + boolean fdoread)); + +/* Receive data from the other system when there is no data to send. + The cneed argument is the amount of data desired and the ctimeout + argument is the timeout in seconds. This will set *pcrec to the + amount of data received. It will return FALSE on error. If a + timeout occurs, it will return TRUE with *pcrec set to zero. */ +extern boolean freceive_data P((struct sconnection *qconn, size_t cneed, + size_t *pcrec, int ctimeout, + boolean freport)); + +/* Get one character from the remote system, going through the + procotol buffering. The ctimeout argument is the timeout in + seconds, and the freport argument is TRUE if errors should be + reported (when closing a connection it is pointless to report + errors). This returns a character or -1 on a timeout or -2 on an + error. */ +extern int breceive_char P((struct sconnection *qconn, + int ctimeout, boolean freport)); + +/* Compute a 32 bit CRC of a data buffer, given an initial CRC. */ +extern unsigned long icrc P((const char *z, size_t c, unsigned long ick)); + +/* The initial CRC value to use for a new buffer. */ +#if ANSI_C +#define ICRCINIT (0xffffffffUL) +#else +#define ICRCINIT ((unsigned long) 0xffffffffL) +#endif + +/* The size of the receive buffer. */ +#define CRECBUFLEN (16384) + +/* Buffer to hold received data. */ +extern char abPrecbuf[CRECBUFLEN]; + +/* Index of start of data in abPrecbuf. */ +extern int iPrecstart; + +/* Index of end of data (first byte not included in data) in abPrecbuf. */ +extern int iPrecend; + +/* There are a couple of variables and functions that are shared by + the 'i' and 'j' protocols (the 'j' protocol is just a wrapper + around the 'i' protocol). These belong in a separate header file, + protij.h, but I don't want to create one for just a couple of + things. */ + +/* An escape sequence of characters for the 'j' protocol to avoid + (protocol parameter ``avoid''). */ +extern const char *zJavoid_parameter; + +/* Timeout to use when sending the 'i' protocol SYNC packet (protocol + parameter ``sync-timeout''). */ +extern int cIsync_timeout; + +/* Shared startup routine for the 'i' and 'j' protocols. */ +extern boolean fijstart P((struct sdaemon *qdaemon, char **pzlog, + int imaxpacksize, + boolean (*pfsend) P((struct sconnection *qconn, + const char *zsend, + size_t csend, + boolean fdoread)), + boolean (*pfreceive) P((struct sconnection *qconn, + size_t cneed, + size_t *pcrec, + int ctimeout, + boolean freport)))); + +/* Prototypes for 'g' protocol functions. */ + +extern struct uuconf_cmdtab asGproto_params[]; +extern boolean fgstart P((struct sdaemon *qdaemon, char **pzlog)); +extern boolean fbiggstart P((struct sdaemon *qdaemon, char **pzlog)); +extern boolean fgshutdown P((struct sdaemon *qdaemon)); +extern boolean fgsendcmd P((struct sdaemon *qdaemon, const char *z, + int ilocal, int iremote)); +extern char *zggetspace P((struct sdaemon *qdaemon, size_t *pcdata)); +extern boolean fgsenddata P((struct sdaemon *qdaemon, char *z, size_t c, + int ilocal, int iremote, long ipos)); +extern boolean fgwait P((struct sdaemon *qdaemon)); + +/* Prototypes for 'f' protocol functions. */ + +extern struct uuconf_cmdtab asFproto_params[]; +extern boolean ffstart P((struct sdaemon *qdaemon, char **pzlog)); +extern boolean ffshutdown P((struct sdaemon *qdaemon)); +extern boolean ffsendcmd P((struct sdaemon *qdaemon, const char *z, + int ilocal, int iremote)); +extern char *zfgetspace P((struct sdaemon *qdaemon, size_t *pcdata)); +extern boolean ffsenddata P((struct sdaemon *qdaemon, char *z, size_t c, + int ilocal, int iremote, long ipos)); +extern boolean ffwait P((struct sdaemon *qdaemon)); +extern boolean fffile P((struct sdaemon *qdaemon, struct stransfer *qtrans, + boolean fstart, boolean fsend, long cbytes, + boolean *pfhandled)); + +/* Prototypes for 't' protocol functions. */ + +extern struct uuconf_cmdtab asTproto_params[]; +extern boolean ftstart P((struct sdaemon *qdaemon, char **pzlog)); +extern boolean ftshutdown P((struct sdaemon *qdaemon)); +extern boolean ftsendcmd P((struct sdaemon *qdaemon, const char *z, + int ilocal, int iremote)); +extern char *ztgetspace P((struct sdaemon *qdaemon, size_t *pcdata)); +extern boolean ftsenddata P((struct sdaemon *qdaemon, char *z, size_t c, + int ilocal, int iremote, long ipos)); +extern boolean ftwait P((struct sdaemon *qdaemon)); +extern boolean ftfile P((struct sdaemon *qdaemon, struct stransfer *qtrans, + boolean fstart, boolean fsend, long cbytes, + boolean *pfhandled)); + +/* Prototypes for 'e' protocol functions. */ + +extern struct uuconf_cmdtab asEproto_params[]; +extern boolean festart P((struct sdaemon *qdaemon, char **pzlog)); +extern boolean feshutdown P((struct sdaemon *qdaemon)); +extern boolean fesendcmd P((struct sdaemon *qdaemon, const char *z, + int ilocal, int iremote)); +extern char *zegetspace P((struct sdaemon *qdaemon, size_t *pcdata)); +extern boolean fesenddata P((struct sdaemon *qdaemon, char *z, size_t c, + int ilocal, int iremote, long ipos)); +extern boolean fewait P((struct sdaemon *qdaemon)); +extern boolean fefile P((struct sdaemon *qdaemon, struct stransfer *qtrans, + boolean fstart, boolean fsend, long cbytes, + boolean *pfhandled)); + +/* Prototypes for 'i' protocol functions. */ + +extern struct uuconf_cmdtab asIproto_params[]; +extern boolean fistart P((struct sdaemon *qdaemon, char **pzlog)); +extern boolean fishutdown P((struct sdaemon *qdaemon)); +extern boolean fisendcmd P((struct sdaemon *qdaemon, const char *z, + int ilocal, int iremote)); +extern char *zigetspace P((struct sdaemon *qdaemon, size_t *pcdata)); +extern boolean fisenddata P((struct sdaemon *qdaemon, char *z, size_t c, + int ilocal, int iremote, long ipos)); +extern boolean fiwait P((struct sdaemon *qdaemon)); + +/* Prototypes for 'j' protocol functions. The 'j' protocol mostly + uses the 'i' protocol functions, but it has a couple of functions + of its own. */ +extern boolean fjstart P((struct sdaemon *qdaemon, char **pzlog)); +extern boolean fjshutdown P((struct sdaemon *qdaemon)); + +/* Prototypes for 'a' protocol functions (these use 'z' as the second + character because 'a' is a modified Zmodem protocol). */ +extern struct uuconf_cmdtab asZproto_params[]; +extern boolean fzstart P((struct sdaemon *qdaemon, char **pzlog)); +extern boolean fzshutdown P((struct sdaemon *qdaemon)); +extern boolean fzsendcmd P((struct sdaemon *qdaemon, const char *z, + int ilocal, int iremote)); +extern char *zzgetspace P((struct sdaemon *qdaemon, size_t *pcdata)); +extern boolean fzsenddata P((struct sdaemon *qdaemon, char *z, size_t c, + int ilocal, int iremote, long ipos)); +extern boolean fzwait P((struct sdaemon *qdaemon)); +extern boolean fzfile P((struct sdaemon *qdaemon, struct stransfer *qtrans, + boolean fstart, boolean fsend, long cbytes, + boolean *pfhandled)); diff --git a/gnu/libexec/uucp/common_sources/sysdep.h b/gnu/libexec/uucp/common_sources/sysdep.h new file mode 100644 index 0000000000..47675ac238 --- /dev/null +++ b/gnu/libexec/uucp/common_sources/sysdep.h @@ -0,0 +1,530 @@ +/* sysh.unx -*- C -*- + The header file for the UNIX system dependent routines. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#ifndef SYSH_UNX_H + +#define SYSH_UNX_H + +#if ANSI_C +/* These structures are used in prototypes but are not defined in this + header file. */ +struct uuconf_system; +struct sconnection; +#endif + +/* Make sure the defines do not conflict. These are in this file + because they are Unix dependent. */ +#if HAVE_V2_LOCKFILES + HAVE_HDB_LOCKFILES + HAVE_SCO_LOCKFILES + HAVE_SVR4_LOCKFILES + HAVE_COHERENT_LOCKFILES != 1 + #error LOCKFILES define not set or duplicated +#endif + +/* SCO and SVR4 lockfiles are basically just like HDB lockfiles. */ +#if HAVE_SCO_LOCKFILES || HAVE_SVR4_LOCKFILES +#undef HAVE_HDB_LOCKFILES +#define HAVE_HDB_LOCKFILES 1 +#endif + +#if HAVE_BSD_TTY + HAVE_SYSV_TERMIO + HAVE_POSIX_TERMIOS != 1 + #error Terminal driver define not set or duplicated +#endif + +#if SPOOLDIR_V2 + SPOOLDIR_BSD42 + SPOOLDIR_BSD43 + SPOOLDIR_HDB + SPOOLDIR_ULTRIX + SPOOLDIR_SVR4 + SPOOLDIR_TAYLOR != 1 + #error Spool directory define not set or duplicated +#endif + +/* If setreuid is broken, don't use it. */ +#if HAVE_BROKEN_SETREUID +#undef HAVE_SETREUID +#define HAVE_SETREUID 0 +#endif + +/* Get some standard types from the configuration header file. */ +#ifdef PID_T +typedef PID_T pid_t; +#endif + +#ifdef UID_T +typedef UID_T uid_t; +#endif + +#ifdef GID_T +typedef GID_T gid_t; +#endif + +#ifdef OFF_T +typedef OFF_T off_t; +#endif + +/* On Unix, binary files are the same as text files. */ +#define BINREAD "r" +#define BINWRITE "w" + +/* If we have sigaction, we can force system calls to not be + restarted. */ +#if HAVE_SIGACTION +#undef HAVE_RESTARTABLE_SYSCALLS +#define HAVE_RESTARTABLE_SYSCALLS 0 +#endif + +/* If we have sigvec, and we have HAVE_SIGVEC_SV_FLAGS, and + SV_INTERRUPT is defined, we can force system calls to not be + restarted (signal.h is included by uucp.h before this point, so + SV_INTERRUPT will be defined by now if it it ever is). */ +#if HAVE_SIGVEC && HAVE_SIGVEC_SV_FLAGS +#ifdef SV_INTERRUPT +#undef HAVE_RESTARTABLE_SYSCALLS +#define HAVE_RESTARTABLE_SYSCALLS 0 +#endif +#endif + +/* If we were cross-configured, we will have a value of -1 for + HAVE_RESTARTABLE_SYSCALLS. In this case, we try to guess what the + correct value should be. Yuck. If we have sigvec, but neither of + the above cases applied (which we know because they would have + changed HAVE_RESTARTABLE_SYSCALLS) then we are probably on 4.2BSD + and system calls are automatically restarted. Otherwise, assume + that they are not. */ +#if HAVE_RESTARTABLE_SYSCALLS == -1 +#undef HAVE_RESTARTABLE_SYSCALLS +#if HAVE_SIGVEC +#define HAVE_RESTARTABLE_SYSCALLS 1 +#else +#define HAVE_RESTARTABLE_SYSCALLS 0 +#endif +#endif /* HAVE_RESTARTABLE_SYSCALLS == -1 */ + +/* We don't handle sigset in combination with restartable system + calls, so we check for it although this combination will never + happen. */ +#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && HAVE_SIGSET +#if HAVE_RESTARTABLE_SYSCALLS +#undef HAVE_SIGSET +#define HAVE_SIGSET 0 +#endif +#endif + +/* If we don't have restartable system calls, we can ignore + fsysdep_catch, usysdep_start_catch and usysdep_end_catch. + Otherwise fsysdep_catch has to do a setjmp. */ + +#if ! HAVE_RESTARTABLE_SYSCALLS + +#define fsysdep_catch() (TRUE) +#define usysdep_start_catch() +#define usysdep_end_catch() +#define CATCH_PROTECT + +#else /* HAVE_RESTARTABLE_SYSCALLS */ + +#if HAVE_SETRET && ! HAVE_SIGSETJMP +#include +#define setjmp setret +#define longjmp longret +#define jmp_buf ret_buf +#else /* ! HAVE_SETRET || HAVE_SIGSETJMP */ +#include +#if HAVE_SIGSETJMP +#undef setjmp +#undef longjmp +#undef jmp_buf +#define setjmp(s) sigsetjmp ((s), TRUE) +#define longjmp siglongjmp +#define jmp_buf sigjmp_buf +#endif /* HAVE_SIGSETJMP */ +#endif /* ! HAVE_SETRET || HAVE_SIGSETJMP */ + +extern volatile sig_atomic_t fSjmp; +extern volatile jmp_buf sSjmp_buf; + +#define fsysdep_catch() (setjmp (sSjmp_buf) == 0) + +#define usysdep_start_catch() (fSjmp = TRUE) + +#define usysdep_end_catch() (fSjmp = FALSE) + +#define CATCH_PROTECT volatile + +#endif /* HAVE_RESTARTABLE_SYSCALLS */ + +/* Get definitions for the terminal driver. */ + +#if HAVE_BSD_TTY +#include +struct sbsd_terminal +{ + struct sgttyb stty; + struct tchars stchars; + struct ltchars sltchars; +}; +typedef struct sbsd_terminal sterminal; +#define fgetterminfo(o, q) \ + (ioctl ((o), TIOCGETP, &(q)->stty) == 0 \ + && ioctl ((o), TIOCGETC, &(q)->stchars) == 0 \ + && ioctl ((o), TIOCGLTC, &(q)->sltchars) == 0) +#define fsetterminfo(o, q) \ + (ioctl ((o), TIOCSETN, &(q)->stty) == 0 \ + && ioctl ((o), TIOCSETC, &(q)->stchars) == 0 \ + && ioctl ((o), TIOCSLTC, &(q)->sltchars) == 0) +#define fsetterminfodrain(o, q) \ + (ioctl ((o), TIOCSETP, &(q)->stty) == 0 \ + && ioctl ((o), TIOCSETC, &(q)->stchars) == 0 \ + && ioctl ((o), TIOCSLTC, &(q)->sltchars) == 0) +#endif /* HAVE_BSD_TTY */ + +#if HAVE_SYSV_TERMIO +#include +typedef struct termio sterminal; +#define fgetterminfo(o, q) (ioctl ((o), TCGETA, (q)) == 0) +#define fsetterminfo(o, q) (ioctl ((o), TCSETA, (q)) == 0) +#define fsetterminfodrain(o, q) (ioctl ((o), TCSETAW, (q)) == 0) +#endif /* HAVE_SYSV_TERMIO */ + +#if HAVE_POSIX_TERMIOS +#include +typedef struct termios sterminal; +#define fgetterminfo(o, q) (tcgetattr ((o), (q)) == 0) +#define fsetterminfo(o, q) (tcsetattr ((o), TCSANOW, (q)) == 0) +#define fsetterminfodrain(o, q) (tcsetattr ((o), TCSADRAIN, (q)) == 0) + +/* On some systems it is not possible to include both + and in the same source files; I don't really know why. + On such systems, we pretend that we don't have . */ +#if ! HAVE_TERMIOS_AND_SYS_IOCTL_H +#undef HAVE_SYS_IOCTL_H +#define HAVE_SYS_IOCTL_H 0 +#endif + +#endif /* HAVE_POSIX_TERMIOS */ + +/* The root directory (this is needed by the system independent stuff + as the default for local-send). */ +#define ZROOTDIR "/" + +/* The name of the execution directory within the spool directory + (this is need by the system independent uuxqt.c). */ +#define XQTDIR ".Xqtdir" + +/* The name of the directory in which we preserve file transfers that + failed. */ +#define PRESERVEDIR ".Preserve" + +/* The length of the sequence number used in a file name. */ +#define CSEQLEN (4) + +/* Get some standard definitions. Avoid including the files more than + once--some might have been included by uucp.h. */ +#if USE_STDIO && HAVE_UNISTD_H +#include +#endif +#if ! USE_TYPES_H +#include +#endif +#include + +/* Get definitions for the file permission bits. */ + +#ifndef S_IRWXU +#define S_IRWXU 0700 +#endif +#ifndef S_IRUSR +#define S_IRUSR 0400 +#endif +#ifndef S_IWUSR +#define S_IWUSR 0200 +#endif +#ifndef S_IXUSR +#define S_IXUSR 0100 +#endif + +#ifndef S_IRWXG +#define S_IRWXG 0070 +#endif +#ifndef S_IRGRP +#define S_IRGRP 0040 +#endif +#ifndef S_IWGRP +#define S_IWGRP 0020 +#endif +#ifndef S_IXGRP +#define S_IXGRP 0010 +#endif + +#ifndef S_IRWXO +#define S_IRWXO 0007 +#endif +#ifndef S_IROTH +#define S_IROTH 0004 +#endif +#ifndef S_IWOTH +#define S_IWOTH 0002 +#endif +#ifndef S_IXOTH +#define S_IXOTH 0001 +#endif + +#ifndef S_ISDIR +#ifdef S_IFDIR +#define S_ISDIR(i) (((i) & S_IFMT) == S_IFDIR) +#else /* ! defined (S_IFDIR) */ +#define S_ISDIR(i) (((i) & 0170000) == 040000) +#endif /* ! defined (S_IFDIR) */ +#endif /* ! defined (S_ISDIR) */ + +/* We need the access macros. */ +#ifndef R_OK +#define R_OK 4 +#define W_OK 2 +#define X_OK 1 +#define F_OK 0 +#endif /* ! defined (R_OK) */ + +/* We create files with these modes (should this be configurable?). */ +#define IPRIVATE_FILE_MODE (S_IRUSR | S_IWUSR) +#define IPUBLIC_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) + +/* We create directories with this mode (should this be configurable?). */ +#define IDIRECTORY_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) +#define IPUBLIC_DIRECTORY_MODE (S_IRWXU | S_IRWXG | S_IRWXO) + +#if ! HAVE_OPENDIR + +/* Define some structures to use if we don't have opendir, etc. These + will only work if we have the old Unix filesystem, with a 2 byte + inode and a 14 byte filename. */ + +#include + +struct dirent +{ + char d_name[DIRSIZ + 1]; +}; + +typedef struct +{ + int o; + struct dirent s; +} DIR; + +extern DIR *opendir P((const char *zdir)); +extern struct dirent *readdir P((DIR *)); +extern int closedir P((DIR *)); + +#endif /* ! HAVE_OPENDIR */ + +#if ! HAVE_FTW_H + +/* If there is no , define the ftw constants. */ + +#define FTW_F (0) +#define FTW_D (1) +#define FTW_DNR (2) +#define FTW_NS (3) + +#endif /* ! HAVE_FTW_H */ + +/* This structure holds the system dependent information we keep for a + connection. This is used by the TCP and TLI code. */ + +struct ssysdep_conn +{ + /* File descriptor. */ + int o; + /* Device name. */ + char *zdevice; + /* File status flags. */ + int iflags; + /* File status flags for descriptor 1 (-1 if not standard input). */ + int istdout_flags; + /* Hold the real descriptor when using a dialer device. */ + int ohold; + /* TRUE if this is a terminal and the remaining fields are valid. */ + boolean fterminal; + /* TRUE if this is a TLI descriptor. */ + boolean ftli; + /* Baud rate. */ + long ibaud; + /* Original terminal settings. */ + sterminal sorig; + /* Current terminal settings. */ + sterminal snew; +#if HAVE_COHERENT_LOCKFILES + /* On Coherent we need to hold on to the real port name which will + be used to enable the port. Ick. */ + char *zenable; +#endif +}; + +/* These functions do I/O and chat scripts to a port. They are called + by the TCP and TLI routines. */ +extern boolean fsysdep_conn_read P((struct sconnection *qconn, + char *zbuf, size_t *pclen, + size_t cmin, int ctimeout, + boolean freport)); +extern boolean fsysdep_conn_write P((struct sconnection *qconn, + const char *zbuf, size_t clen)); +extern boolean fsysdep_conn_io P((struct sconnection *qconn, + const char *zwrite, size_t *pcwrite, + char *zread, size_t *pcread)); +extern boolean fsysdep_conn_chat P((struct sconnection *qconn, + char **pzprog)); + +/* Set a signal handler. */ +extern void usset_signal P((int isig, RETSIGTYPE (*pfn) P((int)), + boolean fforce, boolean *pfignored)); + +/* Default signal handler. This sets the appropriate element of the + afSignal array. If system calls are automatically restarted, it + may do a longjmp to an fsysdep_catch. */ +extern RETSIGTYPE ussignal P((int isig)); + +/* Try to fork, repeating several times. */ +extern pid_t ixsfork P((void)); + +/* Spawn a job. Returns the process ID of the spawned job or -1 on + error. The following macros may be passed in aidescs. */ + +/* Set descriptor to /dev/null. */ +#define SPAWN_NULL (-1) +/* Set element of aidescs to a pipe for caller to read from. */ +#define SPAWN_READ_PIPE (-2) +/* Set element of aidescs to a pipe for caller to write to. */ +#define SPAWN_WRITE_PIPE (-3) + +extern pid_t ixsspawn P((const char **pazargs, int *aidescs, + boolean fkeepuid, boolean fkeepenv, + const char *zchdir, boolean fnosigs, + boolean fshell, const char *zpath, + const char *zuu_machine, + const char *zuu_user)); + +/* Do a form of popen using ixsspawn. */ +extern FILE *espopen P((const char **pazargs, boolean frd, + pid_t *pipid)); + +/* Wait for a particular process to finish, returning the exit status. + The process ID should be pid_t, but we can't put that in a + prototype. */ +extern int ixswait P((unsigned long ipid, const char *zreport)); + +/* Find a spool file in the spool directory. For a local file, the + bgrade argument is the grade of the file. This is needed for + SPOOLDIR_SVR4. */ +extern char *zsfind_file P((const char *zsimple, const char *zsystem, + int bgrade)); + +/* Return the grade given a sequence number. */ +extern char bsgrade P((pointer pseq)); + +/* Lock a string. */ +extern boolean fsdo_lock P((const char *, boolean fspooldir, + boolean *pferr)); + +/* Unlock a string. */ +extern boolean fsdo_unlock P((const char *, boolean fspooldir)); + +/* Check access for a particular user name, or NULL to check access + for any user. */ +extern boolean fsuser_access P((const struct stat *, int imode, + const char *zuser)); + +/* Stick two directories and a file name together. */ +extern char *zsappend3 P((const char *zdir1, const char *zdir2, + const char *zfile)); + +/* Stick three directories and a file name together. */ +extern char *zsappend4 P((const char *zdir1, const char *zdir2, + const char *zdir3, const char *zfile)); + +/* Get a temporary file name. */ +extern char *zstemp_file P((const struct uuconf_system *qsys)); + +/* Get a command file name. */ +extern char *zscmd_file P((const struct uuconf_system *qsys, int bgrade)); + +/* Get a jobid from a system, a file name, and a grade. */ +extern char *zsfile_to_jobid P((const struct uuconf_system *qsys, + const char *zfile, + int bgrade)); + +/* Get a file name from a jobid. This also returns the associated system + in *pzsystem and the grade in *pbgrade. */ +extern char *zsjobid_to_file P((const char *zid, char **pzsystem, + char *pbgrade)); + +/* See whether there is a spool directory for a system when using + SPOOLDIR_ULTRIX. */ +extern boolean fsultrix_has_spool P((const char *zsystem)); + +#if HAVE_COHERENT_LOCKFILES +/* Lock a coherent tty. */ +extern boolean lockttyexist P((const char *z)); +extern boolean fscoherent_disable_tty P((const char *zdevice, + char **pzenable)); +#endif + +/* Some replacements for standard Unix functions. */ + +#if ! HAVE_DUP2 +extern int dup2 P((int oold, int onew)); +#endif + +#if ! HAVE_FTW +extern int ftw P((const char *zdir, + int (*pfn) P((const char *zfile, + const struct stat *qstat, + int iflag)), + int cdescriptors)); +#endif + +#if ! HAVE_GETCWD && ! HAVE_GETWD +extern char *getcwd P((char *zbuf, size_t cbuf)); +#endif + +#if ! HAVE_MKDIR +extern int mkdir P((const char *zdir, int imode)); +#endif + +#if ! HAVE_RENAME +extern int rename P((const char *zold, const char *znew)); +#endif + +#if ! HAVE_RMDIR +extern int rmdir P((const char *zdir)); +#endif + +/* The working directory from which the program was run (this is set + by usysdep_initialize if called with INIT_GETCWD). */ +extern char *zScwd; + +/* The spool directory name. */ +extern const char *zSspooldir; + +/* The lock directory name. */ +extern const char *zSlockdir; + +/* The local UUCP name (needed for some spool directory stuff). */ +extern const char *zSlocalname; + +#endif /* ! defined (SYSH_UNX_H) */ diff --git a/gnu/libexec/uucp/common_sources/system.h b/gnu/libexec/uucp/common_sources/system.h new file mode 100644 index 0000000000..aa9d2a41a4 --- /dev/null +++ b/gnu/libexec/uucp/common_sources/system.h @@ -0,0 +1,950 @@ +/* system.h + Header file for system dependent stuff in the Taylor UUCP package. + This file is not itself system dependent. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#ifndef SYSTEM_H + +#define SYSTEM_H + +#if ANSI_C +/* These structures are used in prototypes but are not defined in this + header file. */ +struct tm; +struct uuconf_system; +struct uuconf_port; +struct sconnection; +struct sstatus; +struct scmd; +#endif + +/* Any function which returns an error should also report an error + message, unless otherwise indicated. + + Any function that returns a char *, rather than a const char *, is + returning a pointer to a buffer allocated by zbufalc which must be + freed using ubuffree, unless otherwise indicated. */ + +/* The maximum length of a remote system name. */ +extern size_t cSysdep_max_name_len; + +/* Initialize. If something goes wrong, this routine should just + exit. The flag argument is 0, or a combination of any of the + following flags. */ + +/* This program needs to know the current working directory. This is + used because on Unix it can be expensive to determine the current + working directory (some versions of getcwd fork a process), but in + most cases we don't need to know it. However, we are going to + chdir to the spool directory (unless INIT_CHDIR is set), so we have + to get the cwd now if we are ever going to get it. Both uucp and + uux use the function fsysdep_needs_cwd to determine whether they + will need the current working directory, and pass the argument to + usysdep_initialize appropriately. There's probably a cleaner way + to handle this, but this will suffice for now. */ +#define INIT_GETCWD (01) + +/* This program should not chdir to the spool directory. This may + only make sense on Unix. It is set by cu. */ +#define INIT_NOCHDIR (02) + +/* This program needs special access to the spool directories. That + means, on Unix, this program is normally installed setuid. */ +#define INIT_SUID (04) + +extern void usysdep_initialize P((pointer puuconf, int iflags)); + +/* Exit the program. The fsuccess argument indicates whether to + return an indication of success or failure to the outer + environment. This routine should not return. */ +extern void usysdep_exit P((boolean fsuccess)); + +/* Called when a non-standard configuration file is being used, to + avoid handing out privileged access. If it returns FALSE, default + configuration file will be used. This is called before the + usysdep_initialize function is called. */ +extern boolean fsysdep_other_config P((const char *)); + +/* Detach from the controlling terminal. This probably only makes + sense on Unix. It is called by uucico to try to get the modem port + as a controlling terminal. It is also called by uucico before it + starts up uuxqt, so that uuxqt will be a complete daemon. */ +extern void usysdep_detach P((void)); + +/* Get the local node name if it is not specified in the configuration + files. Returns NULL on error; otherwise the return value should + point to a static buffer. */ +extern const char *zsysdep_localname P((void)); + +/* Get the login name. This is used when uucico is started up with no + arguments in slave mode, which causes it to assume that somebody + has logged in. It also used by uucp and uux for recording the user + name. This may not return NULL. The return value should point to + a static buffer. */ +extern const char *zsysdep_login_name P((void)); + +/* Set a signal handler for a signal. If the signal occurs, the + appropriate element of afSignal should be set to the signal number + (see the declaration of afSignal in uucp.h). This routine might be + able to just use signal, but Unix requires more complex handling. + This is called before usysdep_initialize. */ +extern void usysdep_signal P((int isig)); + +/* Catch a signal. This is actually defined as a macro in the system + dependent header file, and the prototype here just indicates how it + should be called. It is called before a routine which must exit if + a signal occurs, and is expected to set do a setjmp (which is why + it must be a macro). It is actually only called in one place in + the system independent code, before the call to read stdin in uux. + This is needed to handle 4.2 BSD restartable system calls, which + require a longjmp. On systems which don't need to do + setjmp/longjmp around system calls, this can be redefined in + sysdep.h to TRUE. It should return TRUE if the routine should + proceed, or FALSE if a signal occurred. After having this return + TRUE, usysdep_start_catch should be used to start catching the + signal; this basically tells the signal handler that it's OK to do + the longjmp, if fsysdep_catch did not already do so. */ +#ifndef fsysdep_catch +extern boolean fsysdep_catch P((void)); +#endif + +/* Start catching a signal. This is called after fsysdep_catch to + tell the signal handler to go ahead and do the longjmp. This may + be implemented as a macro in sysdep.h. */ +#ifndef usysdep_start_catch +extern void usysdep_start_catch P((void)); +#endif + +/* Stop catching a signal. This is called when it is no longer + necessary for fsysdep_catch to handle signals. This may be + implemented as a macro in sysdep.h. */ +#ifndef usysdep_end_catch +extern void usysdep_end_catch P((void)); +#endif + +/* Link two files. On Unix this should attempt the link. If it + succeeds it should return TRUE with *pfworked set to TRUE. If the + link fails because it must go across a device, it should return + TRUE with *pfworked set to FALSE. If the link fails for some other + reason, it should log an error message and return FALSE. On a + system which does not support links to files, this should just + return TRUE with *pfworked set to FALSE. */ +extern boolean fsysdep_link P((const char *zfrom, const char *zto, + boolean *pfworked)); + +/* Get the port name. This is used when uucico is started up in slave + mode to figure out which port was used to call in so that it can + determine any appropriate protocol parameters. This may return + NULL if the port cannot be determined, which will just mean that no + protocol parameters are applied. The name returned should be the + sort of name that would appear in the port file. This should set + *pftcp_port to TRUE if it can determine that the port is a TCP + connection rather than a normal serial port. The return value (if + not NULL) should point to a static buffer. */ +extern const char *zsysdep_port_name P((boolean *pftcp_port)); + +/* Expand a file name on the local system. On Unix, if the zfile + argument begins with ~user/ it goes in that users home directory, + and if it begins with ~/ it goes in the public directory (the + public directory is passed to this routine, since each system may + have its own public directory). Similar conventions may be + desirable on other systems. This should always return an absolute + path name, probably in the public directory. It should return NULL + on error; otherwise the return value should be allocated using + zbufcpy or zbufalc. */ +extern char *zsysdep_local_file P((const char *zname, + const char *zpubdir)); + +/* Return whether a file name is in a directory, and check for read or + write access. This should check whether zfile is within zdir (or + is zdir itself). If it is not, it should return FALSE. If zfile + is in zdir, then fcheck indicates whether further checking should + be done. If fcheck is FALSE, no further checking is done. + Otherwise, if freadable is TRUE the user zuser should have search + access to all directories from zdir down to zfile and should have + read access on zfile itself (if zfile does not exist, or is not a + regular file, this function may return FALSE but does not have to). + If freadable is FALSE, the user zuser should have search access to + all directories from zdir down to zfile and should have write + access on zfile (which may be a directory, or may not actually + exist, which is acceptable). The zuser argument may be NULL, in + which case the check should be made for any user, not just zuser. + There is no way for this function to return error. */ +extern boolean fsysdep_in_directory P((const char *zfile, + const char *zdir, + boolean fcheck, + boolean freadable, + const char *zuser)); + +/* Return TRUE if a file exists, FALSE otherwise. There is no way to + return error. */ +extern boolean fsysdep_file_exists P((const char *zfile)); + +/* Start up a program. The code expects fsysdep_run to return after + doing a fork, but at least for now everything will work fine if it + does not (on a system which does not support forking). The three + string arguments may be catenated together to form the program to + execute; I did it this way to make it easy to call execl(2), and + because I never needed more than two arguments. The program will + always be "uucico" or "uuxqt". The return value will be passed + directly to usysdep_exit, and should be TRUE on success, FALSE on + error. */ +extern boolean fsysdep_run P((const char *zprogram, const char *zarg1, + const char *zarg2)); + +/* Send a mail message. This function will be passed an array of + strings. All necessary newlines are already included; the strings + should simply be concatenated together to form the mail message. + It should return FALSE on error, although the return value is often + ignored. */ +extern boolean fsysdep_mail P((const char *zto, const char *zsubject, + int cstrs, const char **paz)); + +/* Get the time in seconds since some epoch. The actual epoch is + unimportant, so long as the time values are consistent across + program executions and the value is never negative. If the + pimicros argument is not NULL, it should be set to the number of + microseconds (if this is not available, *pimicros should be set to + zero). */ +extern long ixsysdep_time P((long *pimicros)); + +/* Get the time in seconds and microseconds (millionths of a second) + since some epoch. The actual epoch is not important, and it may + change in between program invocations; this is provided because on + Unix the times function may be used. If microseconds can not be + determined, *pimicros can just be set to zero. */ +extern long ixsysdep_process_time P((long *pimicros)); + +/* Parse the value returned by ixsysdep_time into a struct tm. I + assume that this structure is defined in . This is + basically just localtime, except that the ANSI function takes a + time_t which may not be what is returned by ixsysdep_time. */ +extern void usysdep_localtime P((long itime, struct tm *q)); + +/* Sleep for a number of seconds. */ +extern void usysdep_sleep P((int cseconds)); + +/* Pause for half a second, or 1 second if subsecond sleeps are not + possible. */ +extern void usysdep_pause P((void)); + +/* Lock a remote system. This should return FALSE if the system is + already locked (no error should be reported). */ +extern boolean fsysdep_lock_system P((const struct uuconf_system *qsys)); + +/* Unlock a remote system. This should return FALSE on error + (although the return value is generally ignored). */ +extern boolean fsysdep_unlock_system P((const struct uuconf_system *qsys)); + +/* Get the conversation sequence number for a remote system, and + increment it for next time. This should return -1 on error. */ +extern long ixsysdep_get_sequence P((const struct uuconf_system *qsys)); + +/* Get the status of a remote system. This should return FALSE on + error. Otherwise it should set *qret to the status. If no status + information is available, this should set *qret to sensible values + and return TRUE. If pfnone is not NULL, then it should be set to + TRUE if no status information was available or FALSE otherwise. */ +extern boolean fsysdep_get_status P((const struct uuconf_system *qsys, + struct sstatus *qret, + boolean *pfnone)); + +/* Set the status of a remote system. This should return FALSE on + error. The system will be locked before this call is made. */ +extern boolean fsysdep_set_status P((const struct uuconf_system *qsys, + const struct sstatus *qset)); + +/* See whether a remote system is permitted to log in. This is just + to support the remote.unknown shell script for HDB. The zscript + argument is the script name, as return by uuconf_remote_unknown. + The zsystem argument is the name given by the remote system. If + the system is not permitted to log in, this function should log an + error and return FALSE. */ +extern boolean fsysdep_unknown_caller P((const char *zscript, + const char *zsystem)); + +/* Check whether there is work for a remote system. It should return + TRUE if there is work, FALSE otherwise; there is no way to indicate + an error. */ +extern boolean fsysdep_has_work P((const struct uuconf_system *qsys)); + +/* Initialize the work scan. This will be called before + fsysdep_get_work. The bgrade argument is the minimum grade of + execution files that should be considered (e.g. a bgrade of 'd' + will allow all grades from 'A' to 'Z' and 'a' to 'd'). This + function should return FALSE on error. */ +extern boolean fsysdep_get_work_init P((const struct uuconf_system *qsys, + int bgrade)); + +/* Get the next command to be executed for a remote system. The + bgrade argument will be the same as for fsysdep_get_work_init; + probably only one of these functions will use it, namely the + function for which it is more convenient. This should return FALSE + on error. The structure pointed to by qcmd should be filled in. + The strings may point into a static buffer; they will be copied out + if necessary. If there is no more work, this should set qcmd->bcmd + to 'H' and return TRUE. This should set qcmd->pseq to something + which can be passed to fsysdep_did_work to remove the job from the + queue when it has been completed. This may set qcmd->bcmd to 'P' + to represent a poll file; the main code will just pass the pseq + element of such a structure to fsysdep_did_work if the system is + called. */ +extern boolean fsysdep_get_work P((const struct uuconf_system *qsys, + int bgrade, struct scmd *qcmd)); + +/* Remove a job from the work queue. This must also remove the + temporary file used for a send command, if there is one. It should + return FALSE on error. */ +extern boolean fsysdep_did_work P((pointer pseq)); + +/* Save the temporary file for a send command. This function should + return a string that will be put into a mail message. On success + this string should say something like ``The file has been saved as + ...''. On failure it could say something like ``The file could not + be saved because ...''. If there is no temporary file, or for some + reason it's not appropriate to include a message, this function + should just return NULL. This function is used when a file send + fails for some reason, to make sure that we don't completely lost + the file. */ +extern const char *zsysdep_save_temp_file P((pointer pseq)); + +/* Cleanup anything left over by fsysdep_get_work_init and + fsysdep_get_work. This may be called even though + fsysdep_get_work_init has not been. */ +extern void usysdep_get_work_free P((const struct uuconf_system *qsys)); + +/* Add a base name to a file if it is a directory. If zfile names a + directory, then return a string naming a file within the directory + with the base file name of zname. This should return NULL on + error. */ +extern char *zsysdep_add_base P((const char *zfile, + const char *zname)); + +/* Get a file name from the spool directory. This should return NULL + on error. The pseq argument is TRUE if the file was found from + searching the work directory; this is, unfortunately, needed to + support SVR4 spool directories. */ +extern char *zsysdep_spool_file_name P((const struct uuconf_system *qsys, + const char *zfile, + pointer pseq)); + +/* Make necessary directories. This should create all non-existent + directories for a file. If the fpublic argument is TRUE, anybody + should be permitted to create and remove files in the directory; + otherwise anybody can list the directory, but only the UUCP system + can create and remove files. It should return FALSE on error. */ +extern boolean fsysdep_make_dirs P((const char *zfile, boolean fpublic)); + +/* Create a stdio file, setting appropriate protection. If the + fpublic argument is TRUE, the file is made publically accessible; + otherwise it is treated as a private data file. If the fappend + argument is TRUE, the file is opened in append mode; otherwise any + previously existing file of the same name is removed. If the + fmkdirs argument is TRUE, then any necessary directories should + also be created. On a system in which file protections are + unimportant and the necessary directories exist, this may be + implemented as + + fopen (zfile, fappend ? "a" : "w"); + + */ +extern FILE *esysdep_fopen P((const char *zfile, boolean fpublic, + boolean fappend, boolean fmkdirs)); + +/* Open a file, using the access permission of the user who invoked + the program. The frd argument is TRUE if the file should be opened + for reading, and the fbinary argument is TRUE if the file should be + opened as a binary file (this is ignored on Unix, since there all + files are binary files). This returns an openfile_t, not a FILE *. + This is supposed to be able to open a file even if it can not be + read by the uucp user. This is not possible on some older Unix + systems. */ +extern openfile_t esysdep_user_fopen P((const char *zfile, + boolean frd, boolean fbinary)); + +/* Open a file to send to another system; the qsys argument is the + system the file is being sent to. If fcheck is TRUE, it should + make sure that the file is readable by zuser (if zuser is NULL the + file must be readable by anybody). This is to eliminate a window + between fsysdep_in_directory and esysdep_open_send. If an error + occurs, it should return EFILECLOSED. */ +extern openfile_t esysdep_open_send P((const struct uuconf_system *qsys, + const char *zname, + boolean fcheck, + const char *zuser)); + +/* Return a temporary file name to receive into. This file will be + opened by esysdep_open_receive. The qsys argument is the system + the file is coming from, the zto argument is the name the file will + have after it has been fully received, and the ztemp argument, if + it is not NULL, is from the command sent by the remote system. The + return value must be freed using ubuffree. The function should + return NULL on error. */ +extern char *zsysdep_receive_temp P((const struct uuconf_system *qsys, + const char *zfile, + const char *ztemp)); + +/* Open a file to receive from another system. The zreceive argument + is the return value of zsysdep_receive_temp with the same qsys, + zfile and ztemp arguments. If the function can determine that this + file has already been partially received, it should set *pcrestart + to the number of bytes that have been received. If the file has + not been partially received, *pcrestart should be set to -1. The + function should return EFILECLOSED on error. After the file is + written, fsysdep_move_file will be called to move the file to its + final destination, and to set the correct file mode. */ +extern openfile_t esysdep_open_receive P((const struct uuconf_system *qsys, + const char *zto, + const char *ztemp, + const char *zreceive, + long *pcrestart)); + +/* Move a file. This is used to move a received file to its final + location. The zto argument is the file to create. The zorig + argument is the name of the file to move. If fmkdirs is TRUE, then + any necessary directories are created; fpublic indicates whether + they should be publically writeable or not. If fcheck is TRUE, + this should make sure the directory is writeable by the user zuser + (if zuser is NULL, then it must be writeable by any user); this is + to avoid a window of vulnerability between fsysdep_in_directory and + fsysdep_move_file. This function should return FALSE on error; the + zorig file should be removed even if an error occurs. */ +extern boolean fsysdep_move_file P((const char *zorig, const char *zto, + boolean fmkdirs, boolean fpublic, + boolean fcheck, const char *zuser)); + +/* Change the mode of a file. The imode argument is a Unix mode. + This should return FALSE on error. */ +extern boolean fsysdep_change_mode P((const char *zfile, + unsigned int imode)); + +/* Truncate a file which we are receiving into. This may be done by + closing the original file, removing it and reopening it. This + should return FALSE on error. */ +extern openfile_t esysdep_truncate P((openfile_t e, const char *zname)); + +/* It is possible for the acknowledgement of a received file to be + lost. The sending system will then now know that the file was + correctly received, and will send it again. This can be a problem + particularly with protocols which support channels, since they may + send several small files in a single window, all of which may be + received correctly although the sending system never sees the + acknowledgement. If these files involve an execution, the + execution will happen twice, which will be bad. + + This function is called when a file is completely received. It is + supposed to try and remember the reception, in case the connection + is lost. It is passed the system, the file name to receive to, and + the temporary file name from the sending system. It should return + FALSE on error. */ +extern boolean fsysdep_remember_reception P((const struct uuconf_system *qsys, + const char *zto, + const char *ztemp)); + +/* This function is called to see if a file has already been received + successfully. It gets the same arguments as + fsysdep_remember_reception. It should return TRUE if the file was + already received, FALSE otherwise. There is no way to report + error. */ +extern boolean fsysdep_already_received P((const struct uuconf_system *qsys, + const char *zto, + const char *ztemp)); + +/* This function is called when it is no longer necessary to remember + that a file has been received. This will be called when the + protocol knows that the receive message has been acknowledged. It + gets the same arguments as fsysdep_remember_reception. it should + return FALSE on error. */ +extern boolean fsysdep_forget_reception P((const struct uuconf_system *qsys, + const char *zto, + const char *ztemp)); + +/* Start expanding a wildcarded file name. This should return FALSE + on error; otherwise subsequent calls to zsysdep_wildcard should + return file names. */ +extern boolean fsysdep_wildcard_start P((const char *zfile)); + +/* Get the next wildcard name. This should return NULL when there are + no more names to return. The return value should be freed using + ubuffree. The argument should be the same as that to + fsysdep_wildcard_start. There is no way to return error. */ +extern char *zsysdep_wildcard P((const char *zfile)); + +/* Finish getting wildcard names. This may be called before or after + zsysdep_wildcard has returned NULL. It should return FALSE on + error. */ +extern boolean fsysdep_wildcard_end P((void)); + +/* Prepare to execute a bunch of file transfer requests. This should + make an entry in the spool directory so that the next time uucico + is started up it will transfer these files. The bgrade argument + specifies the grade of the commands. The commands themselves are + in the pascmds array, which has ccmds entries. The function should + return NULL on error, or the jobid on success. The jobid is a + string that may be printed or passed to fsysdep_kill_job and + related functions, but is otherwise uninterpreted. */ +extern char *zsysdep_spool_commands P((const struct uuconf_system *qsys, + int bgrade, int ccmds, + const struct scmd *pascmds)); + +/* Get a file name to use for a data file to be copied to another + system. The ztname, zdname and zxname arguments will all either be + NULL or point to an array of CFILE_NAME_LEN characters in length. + The ztname array should be set to a temporary file name that could + be passed to zsysdep_spool_file_name to retrieve the return value + of this function; this will be appropriate for the temporary name + in a send request. The zdname array should be set to a data file + name that is appropriate for the spool directory of the other + system; this will be appropriate for the name of the destination + file in a send request of a data file for an execution of some + sort. The zxname array should be set to an execute file name that + is appropriate for the other system. The zlocalname argument is + the name of the local system as seen by the remote system, the + bgrade argument is the grade, and fxqt is TRUE if this file is + going to become an execution file. This should return NULL on + error. */ +#define CFILE_NAME_LEN (15) + +extern char *zsysdep_data_file_name P((const struct uuconf_system *qsys, + const char *zlocalname, + int bgrade, boolean fxqt, + char *ztname, char *zdname, + char *zxname)); + +/* Get a name for a local execute file. This is used by uux for a + local command with remote files. Returns NULL on error. */ +extern char *zsysdep_xqt_file_name P((void)); + +/* Beginning getting execute files. To get a list of execute files, + first fsysdep_get_xqt_init is called, then zsysdep_get_xqt is + called several times until it returns NULL, then finally + usysdep_get_xqt_free is called. */ +extern boolean fsysdep_get_xqt_init P((void)); + +/* Get the next execute file. This should return NULL when finished + (with *pferr set to FALSE). On an error this should return NULL + with *pferr set to TRUE. This should set *pzsystem to the name of + the system for which the execute file was created. Both the return + value and *pzsystem should be freed using ubuffree. */ +extern char *zsysdep_get_xqt P((char **pzsystem, + boolean *pferr)); + +/* Clean up after getting execute files. */ +extern void usysdep_get_xqt_free P((void)); + +/* Get the absolute pathname of a command to execute. This is given + the legal list of commands (which may be the special case "ALL") + and the path. It must return an absolute pathname to the command. + If it gets an error it should set *pferr to TRUE and return NULL; + if the command is not found it should set *pferr to FALSE and + return NULL. */ +extern char *zsysdep_find_command P((const char *zcmd, char **pzcmds, + char **pzpath, boolean *pferr)); + +/* Expand file names for uuxqt. This exists because uuxqt on Unix has + to expand file names which begin with a ~. It does not want to + expand any other type of file name, and it turns a double ~ into a + single one without expanding. If this returns NULL, the file does + not need to be changed; otherwise it returns a zbufalc'ed string. + There is no way to report error. */ +extern char *zsysdep_xqt_local_file P((const struct uuconf_system *qsys, + const char *zfile)); + +#if ! ALLOW_FILENAME_ARGUMENTS +/* Check an argument to an execution command to make sure that it + doesn't refer to a file name that may not be accessed. This should + check the argument to see if it is a filename. If it is, it should + either reject it out of hand or it should call fin_directory_list + on the file with both qsys->zremote_receive and qsys->zremote_send. + If the file is rejected, it should log an error and return FALSE. + Otherwise it should return TRUE. */ +extern boolean fsysdep_xqt_check_file P((const struct uuconf_system *qsys, + const char *zfile)); +#endif /* ! ALLOW_FILENAME_ARGUMENTS */ + +/* Run an execute file. The arguments are: + + qsys -- system for which execute file was created + zuser -- user who requested execution + pazargs -- list of arguments to command (element 0 is command) + zfullcmd -- command and arguments stuck together in one string + zinput -- file name for standard input (may be NULL) + zoutput -- file name for standard output (may be NULL) + fshell -- if TRUE, use /bin/sh to execute file + ilock -- return value of ixsysdep_lock_uuxqt + pzerror -- set to name of standard error file + pftemp -- set to TRUE if error is temporary, FALSE otherwise + + If fshell is TRUE, the command should be executed with /bin/sh + (obviously, this can only really be done on Unix systems). If an + error occurs this should return FALSE and set *pftemp + appropriately. *pzerror should be freed using ubuffree. */ +extern boolean fsysdep_execute P((const struct uuconf_system *qsys, + const char *zuser, + const char **pazargs, + const char *zfullcmd, + const char *zinput, + const char *zoutput, + boolean fshell, + int ilock, + char **pzerror, + boolean *pftemp)); + +/* Lock for uuxqt execution. If the cmaxuuxqts argument is not zero, + this should make sure that no more than cmaxuuxqts uuxqt processes + are running at once. Also, only one uuxqt may execute a particular + command (specified by the -c option) at a time. If zcmd is not + NULL, it is a command that must be locked. This should return a + nonnegative number which will be passed to other routines, + including fsysdep_unlock_uuxqt, or -1 on error. */ +extern int ixsysdep_lock_uuxqt P((const char *zcmd, + int cmaxuuxqts)); + +/* Unlock a uuxqt process. This is passed the return value of + ixsysdep_lock_uuxqt, as well as the arguments passed to + ixsysdep_lock_uuxqt. It may return FALSE on error, but at present + the return value is ignored. */ +extern boolean fsysdep_unlock_uuxqt P((int iseq, const char *zcmd, + int cmaxuuxqts)); + +/* See whether a particular uuxqt command is locked. This should + return TRUE if the command is locked (because ixsysdep_lock_uuxqt + was called with it as an argument), FALSE otherwise. There is no + way to return error. */ +extern boolean fsysdep_uuxqt_locked P((const char *zcmd)); + +/* Lock an execute file in order to execute it. This should return + FALSE if the execute file is already locked. There is no way to + return error. */ +extern boolean fsysdep_lock_uuxqt_file P((const char *zfile)); + +/* Unlock an execute file. This should return FALSE on error. */ +extern boolean fsysdep_unlock_uuxqt_file P((const char *zfile)); + +/* Lock the execution directory. The ilock argument is the return + value of ixsysdep_lock_uuxqt. This should return FALSE if the + directory is already locked. There is no way to return error. */ +extern boolean fsysdep_lock_uuxqt_dir P((int ilock)); + +/* Remove all files in the execution directory, and unlock it. This + should return FALSE on error. */ +extern boolean fsysdep_unlock_uuxqt_dir P((int ilock)); + +/* Move files into or out of the execution directory. The code will + already have checked that all the files exist. The elements in the + pzfrom array will be complete filenames, and the elements in the + pzto array will be either NULL (in which case the file should not + be moved) or simple base names. If fto is TRUE, the files in + pzfrom should be moved to pzto; otherwise, the files in pzto should + be moved to pzfrom (this is used if a temporary failure occurs, in + which case the execution will be retried later). If pzinput and + *pzinput are not NULL, then it is the name of the standard input + file; if it is the same as any element of pzfrom, then *pzinput + should be set to the zbufcpy of the corresponding pzto value, if + any. */ +extern boolean fsysdep_move_uuxqt_files P((int cfiles, + const char *const *pzfrom, + const char *const *pzto, + boolean fto, int ilock, + char **pzinput)); + +/* Expand a file name on the local system, defaulting to the current + directory. This is just like zsysdep_local_file, except that + relative files are placed in the working directory the program + started in rather than in the public directory. This should return + NULL on error. */ +extern char *zsysdep_local_file_cwd P((const char *zname, + const char *zpubdir)); + +/* Add the working directory to a file name. The named file is + actually on a remote system. If the file already has a directory, + it should not be changed. This should return NULL on error. */ +extern char *zsysdep_add_cwd P((const char *zfile)); + +/* See whether a file name will need the current working directory + when zsysdep_local_file_cwd or zsysdep_add_cwd is called on it. + This will be called before usysdep_initialize. It should just + check whether the argument is an absolute path. See the comment + above usysdep_initialize in this file for an explanation of why + things are done this way. */ +extern boolean fsysdep_needs_cwd P((const char *zfile)); + +/* Get the base name of a file. The file will be a local file name, + and this function should return the base file name, ideally in a + form which will make sense on most systems; it will be used if the + destination of a uucp is a directory. */ +extern char *zsysdep_base_name P((const char *zfile)); + +/* Return a filename within a directory. */ +extern char *zsysdep_in_dir P((const char *zdir, const char *zfile)); + +/* Get the mode of a file. This should return a Unix style file mode. + It should return 0 on error. */ +extern unsigned int ixsysdep_file_mode P((const char *zfile)); + +/* See whether the user has access to a file. This is called by uucp + and uux to prevent copying of a file which uucp can read but the + user cannot. If access is denied, this should log an error message + and return FALSE. */ +extern boolean fsysdep_access P((const char *zfile)); + +/* See whether the daemon has access to a file. This is called by + uucp and uux when a file is queued up for transfer without being + copied into the spool directory. It is merely an early error + check, as the daemon would of course discover the error itself when + it tried the transfer. If access would be denied, this should log + an error message and return FALSE. */ +extern boolean fsysdep_daemon_access P((const char *zfile)); + +/* Translate a destination from system!user to a place in the public + directory where uupick will get the file. On Unix this produces + system!~/receive/user/localname, and that's probably what it has to + produce on any other system as well. Returns NULL on a usage + error, or otherwise returns string allocated by zbufcpy. */ +extern char *zsysdep_uuto P((const char *zdest, + const char *zlocalname)); + +/* Return TRUE if a pathname exists and is a directory. */ +extern boolean fsysdep_directory P((const char *zpath)); + +/* Walk a directory tree. The zdir argument is the directory to walk. + The pufn argument is a function to call on each regular file in the + tree. The first argument to pufn should be the full filename; the + second argument to pufn should be the filename relative to zdir; + the third argument to pufn should be the pinfo argument to + usysdep_walk_tree. The usysdep_walk_tree function should return + FALSE on error. */ +extern boolean usysdep_walk_tree P((const char *zdir, + void (*pufn) P((const char *zfull, + const char *zrelative, + pointer pinfo)), + pointer pinfo)); + +/* Return the jobid of a work file, given the sequence value. On + error this should log an error and return NULL. The jobid is a + string which may be printed out and read in and passed to + fsysdep_kill_job, etc., but is not otherwise interpreted. */ +extern char *zsysdep_jobid P((const struct uuconf_system *qsys, + pointer pseq)); + +/* See whether the current user is permitted to kill jobs submitted by + another user. This should return TRUE if permission is granted, + FALSE otherwise. */ +extern boolean fsysdep_privileged P((void)); + +/* Kill a job, given the jobid. This should remove all associated + files and in general eliminate the job completely. On error it + should log an error message and return FALSE. */ +extern boolean fsysdep_kill_job P((pointer puuconf, + const char *zjobid)); + +/* Rejuvenate a job, given the jobid. If possible, this should update + the time associated with the job such that it will not be + eliminated by uustat -K or similar programs that check the creation + time. This should affect the return value of ixsysdep_work_time. + On error it should log an error message and return FALSE. */ +extern boolean fsysdep_rejuvenate_job P((pointer puuconf, + const char *zjobid)); + +/* Get the time a job was queued, given the sequence number. There is + no way to indicate error. The return value must use the same epoch + as ixsysdep_time. */ +extern long ixsysdep_work_time P((const struct uuconf_system *qsys, + pointer pseq)); + +/* Get the time a file was created. This is called by uustat on + execution files. There is no way to indicate error. The return + value must use the same epoch as ixsysdep_time. */ +extern long ixsysdep_file_time P((const char *zfile)); + +/* Get the size in bytes of a file. If this file does not exist, this + should not give an error message, but should return -1. If some + other error occurs, this should return -2. */ +extern long csysdep_size P((const char *zfile)); + +/* Return the amount of free space on the containing the given file + name (the file may or may not exist). If the amount of free space + cannot be determined, the function should return -1. */ +extern long csysdep_bytes_free P((const char *zfile)); + +/* Start getting status information for all systems with available + status information. There may be status information for unknown + systems, which is why this series of functions is used. The phold + argument is used to pass information around, to possibly avoid the + use of static variables. On error this should log an error and + return FALSE. */ +extern boolean fsysdep_all_status_init P((pointer *phold)); + +/* Get status information for the next system. This should return the + system name and fill in the qstat argument. The phold argument + will be that set by fsysdep_all_status_init. On error this should + log an error, set *pferr to TRUE, and return NULL. */ +extern char *zsysdep_all_status P((pointer phold, boolean *pferr, + struct sstatus *qstat)); + +/* Free up anything allocated by fsysdep_all_status_init and + zsysdep_all_status. The phold argument is that set by + fsysdep_all_status_init. */ +extern void usysdep_all_status_free P((pointer phold)); + +/* Display the process status of all processes holding lock files. + This is uustat -p. The return value is passed to usysdep_exit. */ +extern boolean fsysdep_lock_status P((void)); + +/* Return TRUE if the user has legitimate access to the port. This is + used by cu to control whether the user can open a port directly, + rather than merely being able to dial out on it. Opening a port + directly allows the modem to be reprogrammed. */ +extern boolean fsysdep_port_access P((struct uuconf_port *qport)); + +/* Return whether the given port could be named by the given line. On + Unix, the line argument would be something like "ttyd0", and this + function should return TRUE if the named port is "/dev/ttyd0". */ +extern boolean fsysdep_port_is_line P((struct uuconf_port *qport, + const char *zline)); + +/* Set the terminal into raw mode. In this mode no input characters + should be treated specially, and characters should be made + available as they are typed. The original terminal mode should be + saved, so that it can be restored by fsysdep_terminal_restore. If + flocalecho is TRUE, then local echoing should still be done; + otherwise echoing should be disabled. This function returns FALSE + on error. */ +extern boolean fsysdep_terminal_raw P((boolean flocalecho)); + +/* Restore the terminal back to the original setting, before + fsysdep_terminal_raw was called. Returns FALSE on error. */ +extern boolean fsysdep_terminal_restore P((void)); + +/* Read a line from the terminal. The fsysdep_terminal_raw function + will have been called. This should print the zprompt argument + (unless it is NULL) and return the line, allocated by zbufcpy, or + NULL on error. */ +extern char *zsysdep_terminal_line P((const char *zprompt)); + +/* Write a line to the terminal, ending with a newline. This is + basically just puts (zline, stdout), except that the terminal will + be in raw mode, so on ASCII Unix systems the line needs to end with + \r\n. */ +extern boolean fsysdep_terminal_puts P((const char *zline)); + +/* If faccept is TRUE, permit the user to generate signals from the + terminal. If faccept is FALSE, turn signals off again. After + fsysdep_terminal_raw is called, signals should be off. Return + FALSE on error. */ +extern boolean fsysdep_terminal_signals P((boolean faccept)); + +/* The cu program expects the system dependent code to handle the + details of copying data from the communications port to the + terminal. This should be set up by fsysdep_cu_init, and done while + fsysdep_cu is called. It is permissible to do it on a continual + basis (on Unix a subprocess handles it) so long as the copying can + be stopped by the fsysdep_cu_copy function. + + The fsysdep_cu_init function does any system dependent + initialization needed for this. */ +extern boolean fsysdep_cu_init P((struct sconnection *qconn)); + +/* Copy all data from the communications port to the terminal, and all + data from the terminal to the communications port. Keep this up + until the escape character *zCuvar_escape is seen. Set *pbcmd to + the character following the escape character; after the escape + character, zlocalname should be printed, possibly after a delay. + If two escape characters are entered in sequence, this function + should send a single escape character to the port, and not return. + Returns FALSE on error. */ +extern boolean fsysdep_cu P((struct sconnection *qconn, + char *pbcmd, + const char *zlocalname)); + +/* If fcopy is TRUE, start copying data from the communications port + to the terminal. If fcopy is FALSE, stop copying data. This + function may be called several times during a cu session. It + should return FALSE on error. */ +extern boolean fsysdep_cu_copy P((boolean fcopy)); + +/* Stop copying data from the communications port to the terminal, and + generally clean up after fsysdep_cu_init and fsysdep_cu. Returns + FALSE on error. */ +extern boolean fsysdep_cu_finish P((void)); + +/* Run a shell command. If zcmd is NULL, or *zcmd == '\0', just + start up a shell. The second argument is one of the following + values. This should return FALSE on error. */ +enum tshell_cmd +{ + /* Attach stdin and stdout to the terminal. */ + SHELL_NORMAL, + /* Attach stdout to the communications port, stdin to the terminal. */ + SHELL_STDOUT_TO_PORT, + /* Attach stdin to the communications port, stdout to the terminal. */ + SHELL_STDIN_FROM_PORT, + /* Attach both stdin and stdout to the communications port. */ + SHELL_STDIO_ON_PORT +}; + +extern boolean fsysdep_shell P((struct sconnection *qconn, + const char *zcmd, + enum tshell_cmd tcmd)); + +/* Change directory. If zdir is NULL, or *zdir == '\0', change to the + user's home directory. Return FALSE on error. */ +extern boolean fsysdep_chdir P((const char *zdir)); + +/* Suspend the current process. This is only expected to work on Unix + versions that support SIGTSTP. In general, people can just shell + out. */ +extern boolean fsysdep_suspend P((void)); + +/* Start getting files for uupick. The zsystem argument may be NULL + to get files from all systems, or it may specify a particular + system. The zpubdir argument is the public directory to use. This + returns FALSE on error. */ +extern boolean fsysdep_uupick_init P((const char *zsystem, + const char *zpubdir)); + +/* Get the next file for uupick. This returns the basic file name. + It sets *pzfull to the full name, and *pzfrom to the name of the + system which sent this file over; both should be freed using + ubuffree. *pzfull should be passed to ubuffree after it is no + longer needed. The zsystem and zpubdir arguments should be the + same as the arguments to fsysdep_uupick_init. This returns NULL + when all files been returned. */ +extern char *zsysdep_uupick P((const char *zsystem, const char *zpubdir, + char **pzfrom, char **pzfull)); + +/* Clean up after getting files for uupick. */ +extern boolean fsysdep_uupick_free P((const char *zsystem, + const char *zpubdir)); + +/* Translate a local file name for uupick. On Unix this is just like + zsysdep_local_file_cwd except that a file beginning with ~/ is + placed in the user's home directory rather than in the public + directory. */ +extern char *zsysdep_uupick_local_file P((const char *zfile)); + +/* Remove a directory and all the files in it. */ +extern boolean fsysdep_rmdir P((const char *zdir)); + +#endif /* ! defined (SYSTEM_H) */ diff --git a/gnu/libexec/uucp/common_sources/tcp.c b/gnu/libexec/uucp/common_sources/tcp.c new file mode 100644 index 0000000000..c38760da85 --- /dev/null +++ b/gnu/libexec/uucp/common_sources/tcp.c @@ -0,0 +1,470 @@ +/* tcp.c + Code to handle TCP connections. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char tcp_rcsid[] = "$Id: tcp.c,v 1.1 1993/08/04 19:31:06 jtc Exp $"; +#endif + +#if HAVE_TCP + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "conn.h" +#include "system.h" + +#include + +#if HAVE_SYS_TYPES_TCP_H +#include +#endif +#include +#include +#include + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +/* This code handles TCP connections. It assumes a Berkeley socket + interface. */ + +/* The normal "uucp" port number. */ +#define IUUCP_PORT (540) + +/* Local functions. */ +static void utcp_free P((struct sconnection *qconn)); +static boolean ftcp_open P((struct sconnection *qconn, long ibaud, + boolean fwait)); +static boolean ftcp_close P((struct sconnection *qconn, + pointer puuconf, + struct uuconf_dialer *qdialer, + boolean fsuccess)); +static boolean ftcp_reset P((struct sconnection *qconn)); +static boolean ftcp_dial P((struct sconnection *qconn, pointer puuconf, + const struct uuconf_system *qsys, + const char *zphone, + struct uuconf_dialer *qdialer, + enum tdialerfound *ptdialer)); +static int itcp_port_number P((const char *zport)); + +/* The command table for a TCP connection. */ +static const struct sconncmds stcpcmds = +{ + utcp_free, + NULL, /* pflock */ + NULL, /* pfunlock */ + ftcp_open, + ftcp_close, + ftcp_reset, + ftcp_dial, + fsysdep_conn_read, + fsysdep_conn_write, + fsysdep_conn_io, + NULL, /* pfbreak */ + NULL, /* pfset */ + NULL, /* pfcarrier */ + fsysdep_conn_chat, + NULL /* pibaud */ +}; + +/* Initialize a TCP connection. */ + +boolean +fsysdep_tcp_init (qconn) + struct sconnection *qconn; +{ + struct ssysdep_conn *q; + + q = (struct ssysdep_conn *) xmalloc (sizeof (struct ssysdep_conn)); + q->o = -1; + q->zdevice = NULL; + q->iflags = -1; + q->istdout_flags = -1; + q->fterminal = FALSE; + q->ftli = FALSE; + q->ibaud = 0; + + qconn->psysdep = (pointer) q; + qconn->qcmds = &stcpcmds; + return TRUE; +} + +/* Free a TCP connection. */ + +static void +utcp_free (qconn) + struct sconnection *qconn; +{ + xfree (qconn->psysdep); +} + +/* Open a TCP connection. If the fwait argument is TRUE, we are + running as a server. Otherwise we are just trying to reach another + system. */ + +static boolean +ftcp_open (qconn, ibaud, fwait) + struct sconnection *qconn; + long ibaud; + boolean fwait; +{ + struct ssysdep_conn *qsysdep; + struct sockaddr_in s; + const char *zport; + uid_t iuid, ieuid; + + ulog_device ("TCP"); + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + + qsysdep->o = socket (AF_INET, SOCK_STREAM, 0); + if (qsysdep->o < 0) + { + ulog (LOG_ERROR, "socket: %s", strerror (errno)); + return FALSE; + } + + if (fcntl (qsysdep->o, F_SETFD, + fcntl (qsysdep->o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + (void) close (qsysdep->o); + qsysdep->o = -1; + return FALSE; + } + + qsysdep->iflags = fcntl (qsysdep->o, F_GETFL, 0); + if (qsysdep->iflags < 0) + { + ulog (LOG_ERROR, "fcntl: %s", strerror (errno)); + (void) close (qsysdep->o); + qsysdep->o = -1; + return FALSE; + } + + /* If we aren't waiting for a connection, we're done. */ + if (! fwait) + return TRUE; + + /* Run as a server and wait for a new connection. The code in + uucico.c has already detached us from our controlling terminal. + From this point on if the server gets an error we exit; we only + return if we have received a connection. It would be more robust + to respawn the server if it fails; someday. */ + bzero ((pointer) &s, sizeof s); + s.sin_family = AF_INET; + zport = qconn->qport->uuconf_u.uuconf_stcp.uuconf_zport; + s.sin_port = itcp_port_number (zport); + s.sin_addr.s_addr = htonl (INADDR_ANY); + + /* Swap to our real user ID when doing the bind call. This will + permit the server to use privileged TCP ports when invoked by + root. We only swap if our effective user ID is not root, so that + the program can also be made suid root in order to get privileged + ports when invoked by anybody. */ + iuid = getuid (); + ieuid = geteuid (); + if (ieuid != 0) + { +#if HAVE_SETREUID + /* Swap the effective user id and the real user id. We can then + swap them back again when we want to return to the uucp + user's permissions. */ + if (setreuid (ieuid, iuid) < 0) + { + ulog (LOG_ERROR, "setreuid (%ld, %ld): %s", + (long) ieuid, (long) iuid, strerror (errno)); + (void) close (qsysdep->o); + qsysdep->o = -1; + return FALSE; + } +#else /* ! HAVE_SETREUID */ +#if HAVE_SAVED_SETUID + /* Set the effective user id to the real user id. Since the + effective user id is the saved setuid we will able to set + back to it later. If the real user id is root we will not be + able to switch back and forth, but that doesn't matter since + we only want to switch once. */ + if (setuid (iuid) < 0) + { + ulog (LOG_ERROR, "setuid (%ld): %s", (long) iuid, + strerror (errno)); + (void) close (qsysdep->o); + qsysdep->o = -1; + return FALSE; + } +#else /* ! HAVE_SAVED_SETUID */ + /* There's no way to switch between real permissions and + effective permissions. Just try the bind with the uucp + permissions. */ +#endif /* ! HAVE_SAVED_SETUID */ +#endif /* ! HAVE_SETREUID */ + } + + if (bind (qsysdep->o, (struct sockaddr *) &s, sizeof s) < 0) + ulog (LOG_FATAL, "bind: %s", strerror (errno)); + + /* Now swap back to the uucp user ID. */ + if (ieuid != 0) + { +#if HAVE_SETREUID + if (setreuid (iuid, ieuid) < 0) + { + ulog (LOG_ERROR, "setreuid (%ld, %ld): %s", + (long) iuid, (long) ieuid, strerror (errno)); + (void) close (qsysdep->o); + qsysdep->o = -1; + return FALSE; + } +#else /* ! HAVE_SETREUID */ +#if HAVE_SAVED_SETUID + /* Set ourselves back to our original effective user id. */ + if (setuid ((uid_t) ieuid) < 0) + { + ulog (LOG_ERROR, "setuid (%ld): %s", (long) ieuid, + strerror (errno)); + (void) close (qsysdep->o); + qsysdep->o = -1; + return FALSE; + } +#else /* ! HAVE_SAVED_SETUID */ + /* We didn't switch, no need to switch back. */ +#endif /* ! HAVE_SAVED_SETUID */ +#endif /* ! HAVE_SETREUID */ + } + + if (listen (qsysdep->o, 5) < 0) + ulog (LOG_FATAL, "listen: %s", strerror (errno)); + + while (! FGOT_SIGNAL ()) + { + size_t clen; + int onew; + pid_t ipid; + + DEBUG_MESSAGE0 (DEBUG_PORT, + "ftcp_open: Waiting for connections"); + + clen = sizeof s; + onew = accept (qsysdep->o, (struct sockaddr *) &s, &clen); + if (onew < 0) + ulog (LOG_FATAL, "accept: %s", strerror (errno)); + + DEBUG_MESSAGE0 (DEBUG_PORT, + "ftcp_open: Got connection; forking"); + + ipid = ixsfork (); + if (ipid < 0) + ulog (LOG_FATAL, "fork: %s", strerror (errno)); + if (ipid == 0) + { + (void) close (qsysdep->o); + qsysdep->o = onew; + + /* Now we fork and let our parent die, so that we become + a child of init. This lets the main server code wait + for its child and then continue without accumulating + zombie children. */ + ipid = ixsfork (); + if (ipid < 0) + { + ulog (LOG_ERROR, "fork: %s", strerror (errno)); + _exit (EXIT_FAILURE); + } + + if (ipid != 0) + _exit (EXIT_SUCCESS); + + ulog_id (getpid ()); + + return TRUE; + } + + (void) close (onew); + + /* Now wait for the child. */ + (void) ixswait ((unsigned long) ipid, (const char *) NULL); + } + + /* We got a signal. */ + usysdep_exit (FALSE); + + /* Avoid compiler warnings. */ + return FALSE; +} + +/* Close the port. */ + +/*ARGSUSED*/ +static boolean +ftcp_close (qconn, puuconf, qdialer, fsuccess) + struct sconnection *qconn; + pointer puuconf; + struct uuconf_dialer *qdialer; + boolean fsuccess; +{ + struct ssysdep_conn *qsysdep; + boolean fret; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + fret = TRUE; + if (qsysdep->o >= 0 && close (qsysdep->o) < 0) + { + ulog (LOG_ERROR, "close: %s", strerror (errno)); + fret = FALSE; + } + qsysdep->o = -1; + return fret; +} + +/* Reset the port. This will be called by a child which was forked + off in ftcp_open, above. We don't want uucico to continue looping + and giving login prompts, so we pretend that we received a SIGINT + signal. This should probably be handled more cleanly. The signal + will not be recorded in the log file because we don't set + afLog_signal[INDEXSIG_SIGINT]. */ + +/*ARGSUSED*/ +static boolean +ftcp_reset (qconn) + struct sconnection *qconn; +{ + afSignal[INDEXSIG_SIGINT] = TRUE; + return TRUE; +} + +/* Dial out on a TCP port, so to speak: connect to a remote computer. */ + +/*ARGSUSED*/ +static boolean +ftcp_dial (qconn, puuconf, qsys, zphone, qdialer, ptdialer) + struct sconnection *qconn; + pointer puuconf; + const struct uuconf_system *qsys; + const char *zphone; + struct uuconf_dialer *qdialer; + enum tdialerfound *ptdialer; +{ + struct ssysdep_conn *qsysdep; + const char *zhost; + struct hostent *q; + struct sockaddr_in s; + const char *zport; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + + *ptdialer = DIALERFOUND_FALSE; + + zhost = zphone; + if (zhost == NULL) + { + if (qsys == NULL) + { + ulog (LOG_ERROR, "No address for TCP connection"); + return FALSE; + } + zhost = qsys->uuconf_zname; + } + + errno = 0; + q = gethostbyname ((char *) zhost); + if (q == NULL) + { + if (errno == 0) + ulog (LOG_ERROR, "%s: unknown host name", zhost); + else + ulog (LOG_ERROR, "gethostbyname (%s): %s", zhost, strerror (errno)); + return FALSE; + } + + s.sin_family = q->h_addrtype; + zport = qconn->qport->uuconf_u.uuconf_stcp.uuconf_zport; + s.sin_port = itcp_port_number (zport); + memcpy (&s.sin_addr.s_addr, q->h_addr, (size_t) q->h_length); + + if (connect (qsysdep->o, (struct sockaddr *) &s, sizeof s) < 0) + { + ulog (LOG_ERROR, "connect: %s", strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/* Get the port number given a name. The argument will almost always + be "uucp" so we cache that value. The return value is always in + network byte order. This returns -1 on error. */ + +static int +itcp_port_number (zname) + const char *zname; +{ + boolean fuucp; + static int iuucp; + int i; + char *zend; + struct servent *q; + + fuucp = strcmp (zname, "uucp") == 0; + if (fuucp && iuucp != 0) + return iuucp; + + /* Try it as a number first. */ + i = strtol ((char *) zname, &zend, 10); + if (i != 0 && *zend == '\0') + return htons (i); + + q = getservbyname ((char *) zname, (char *) "tcp"); + if (q == NULL) + { + /* We know that the "uucp" service should be 540, even if isn't + in /etc/services. */ + if (fuucp) + { + iuucp = htons (IUUCP_PORT); + return iuucp; + } + ulog (LOG_ERROR, "getservbyname (%s): %s", zname, strerror (errno)); + return -1; + } + + if (fuucp) + iuucp = q->s_port; + + return q->s_port; +} + +#endif /* HAVE_TCP */ diff --git a/gnu/libexec/uucp/common_sources/tli.c b/gnu/libexec/uucp/common_sources/tli.c new file mode 100644 index 0000000000..6958b80b47 --- /dev/null +++ b/gnu/libexec/uucp/common_sources/tli.c @@ -0,0 +1,644 @@ +/* tli.c + Code to handle TLI connections. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char tli_rcsid[] = "$Id: tli.c,v 1.1 1993/08/04 19:31:09 jtc Exp $"; +#endif + +#if HAVE_TLI + +#include "sysdep.h" +#include "uudefs.h" +#include "uuconf.h" +#include "conn.h" +#include "system.h" + +#include + +#if HAVE_SYS_IOCTL_H +#include +#endif + +#if HAVE_TIUSER_H +#include +#else +#if HAVE_XTI_H +#include +#else +#if HAVE_SYS_TLI_H +#include +#endif +#endif +#endif + +#if HAVE_STROPTS_H +#include +#endif + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +/* The arguments to t_alloca have two different names. I want the + SVID ones, not the XPG3 ones. */ +#ifndef T_BIND +#define T_BIND T_BIND_STR +#endif +#ifndef T_CALL +#define T_CALL T_CALL_STR +#endif + +/* Hopefully these externs will not cause any trouble. This is how + they are shown in the SVID. */ +extern int t_errno; +extern char *t_errlist[]; +extern int t_nerr; + +#ifndef t_alloc +extern pointer t_alloc (); +#endif + +/* This code handles TLI connections. It's Unix specific. It's + largely based on code from Unix Network Programming, by W. Richard + Stevens. */ + +/* Local functions. */ +static const char *ztlierror P((void)); +static void utli_free P((struct sconnection *qconn)); +static boolean ftli_push P((struct sconnection *qconn)); +static boolean ftli_open P((struct sconnection *qconn, long ibaud, + boolean fwait)); +static boolean ftli_close P((struct sconnection *qconn, + pointer puuconf, + struct uuconf_dialer *qdialer, + boolean fsuccess)); +static boolean ftli_reset P((struct sconnection *qconn)); +static boolean ftli_dial P((struct sconnection *qconn, pointer puuconf, + const struct uuconf_system *qsys, + const char *zphone, + struct uuconf_dialer *qdialer, + enum tdialerfound *ptdialer)); + +/* The command table for a TLI connection. */ +static const struct sconncmds stlicmds = +{ + utli_free, + NULL, /* pflock */ + NULL, /* pfunlock */ + ftli_open, + ftli_close, + ftli_reset, + ftli_dial, + fsysdep_conn_read, + fsysdep_conn_write, + fsysdep_conn_io, + NULL, /* pfbreak */ + NULL, /* pfset */ + NULL, /* pfcarrier */ + fsysdep_conn_chat, + NULL /* pibaud */ +}; + +/* Get a TLI error string. */ + +static const char * +ztlierror () +{ + if (t_errno == TSYSERR) + return strerror (errno); + if (t_errno < 0 || t_errno >= t_nerr) + return "Unknown TLI error"; + return t_errlist[t_errno]; +} + +/* Initialize a TLI connection. */ + +boolean +fsysdep_tli_init (qconn) + struct sconnection *qconn; +{ + struct ssysdep_conn *q; + + q = (struct ssysdep_conn *) xmalloc (sizeof (struct ssysdep_conn)); + q->o = -1; + q->zdevice = NULL; + q->iflags = -1; + q->istdout_flags = -1; + q->fterminal = FALSE; + q->ftli = TRUE; + q->ibaud = 0; + + qconn->psysdep = (pointer) q; + qconn->qcmds = &stlicmds; + return TRUE; +} + +/* Free a TLI connection. */ + +static void +utli_free (qconn) + struct sconnection *qconn; +{ + xfree (qconn->psysdep); +} + +/* Push all desired modules onto a TLI stream. If the user requests a + STREAMS connection without giving a list of modules, we just push + tirdwr. If the I_PUSH ioctl is not defined on this system, we just + ignore any list of modules. */ + +static boolean +ftli_push (qconn) + struct sconnection *qconn; +{ +#ifdef I_PUSH + + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + + if (qconn->qport->uuconf_u.uuconf_stli.uuconf_pzpush != NULL) + { + char **pz; + + for (pz = qconn->qport->uuconf_u.uuconf_stli.uuconf_pzpush; + *pz != NULL; + pz++) + { + if (ioctl (qsysdep->o, I_PUSH, *pz) < 0) + { + ulog (LOG_ERROR, "ioctl (I_PUSH, %s): %s", *pz, + strerror (errno)); + return FALSE; + } + } + } + else if (qconn->qport->uuconf_u.uuconf_stli.uuconf_fstream) + { + if (ioctl (qsysdep->o, I_PUSH, "tirdwr") < 0) + { + ulog (LOG_ERROR, "ioctl (I_PUSH, tirdwr): %s", + strerror (errno)); + return FALSE; + } + } + + /* If we have just put the connection into stream mode, we must turn + off the TLI flag to avoid using TLI calls on it. */ + if (qconn->qport->uuconf_u.uuconf_stli.uuconf_fstream) + qsysdep->ftli = FALSE; + +#endif /* defined (I_PUSH) */ + + return TRUE; +} + +/* Open a TLI connection. If the fwait argument is TRUE, we are + running as a server. Otherwise we are just trying to reach another + system. */ + +static boolean +ftli_open (qconn, ibaud, fwait) + struct sconnection *qconn; + long ibaud; + boolean fwait; +{ + struct ssysdep_conn *qsysdep; + const char *zdevice; + char *zfreedev; + const char *zservaddr; + char *zfreeaddr; + struct t_bind *qtbind; + struct t_call *qtcall; + + /* Unlike most other device types, we don't bother to call + ulog_device here, because fconn_open calls it with the name of + the port anyhow. */ + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + + zdevice = qconn->qport->uuconf_u.uuconf_stli.uuconf_zdevice; + if (zdevice == NULL) + zdevice = qconn->qport->uuconf_zname; + + zfreedev = NULL; + if (*zdevice != '/') + { + zfreedev = zbufalc (sizeof "/dev/" + strlen (zdevice)); + sprintf (zfreedev, "/dev/%s", zdevice); + zdevice = zfreedev; + } + + qsysdep->o = t_open (zdevice, O_RDWR, (struct t_info *) NULL); + if (qsysdep->o < 0) + { + ulog (LOG_ERROR, "t_open (%s): %s", zdevice, ztlierror ()); + ubuffree (zfreedev); + return FALSE; + } + + if (fcntl (qsysdep->o, F_SETFD, + fcntl (qsysdep->o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + ubuffree (zfreedev); + (void) t_close (qsysdep->o); + qsysdep->o = -1; + return FALSE; + } + + qsysdep->iflags = fcntl (qsysdep->o, F_GETFL, 0); + if (qsysdep->iflags < 0) + { + ulog (LOG_ERROR, "fcntl: %s", strerror (errno)); + ubuffree (zfreedev); + (void) t_close (qsysdep->o); + qsysdep->o = -1; + return FALSE; + } + + /* If we aren't waiting for a connection, we can bind to any local + address, and then we're finished. */ + if (! fwait) + { + ubuffree (zfreedev); + if (t_bind (qsysdep->o, (struct t_bind *) NULL, + (struct t_bind *) NULL) < 0) + { + ulog (LOG_ERROR, "t_bind: %s", ztlierror ()); + (void) t_close (qsysdep->o); + qsysdep->o = -1; + return FALSE; + } + return TRUE; + } + + /* Run as a server and wait for a new connection. The code in + uucico.c has already detached us from our controlling terminal. + From this point on if the server gets an error we exit; we only + return if we have received a connection. It would be more robust + to respawn the server if it fails; someday. */ + qtbind = (struct t_bind *) t_alloc (qsysdep->o, T_BIND, T_ALL); + if (qtbind == NULL) + ulog (LOG_FATAL, "t_alloc (T_BIND): %s", ztlierror ()); + + zservaddr = qconn->qport->uuconf_u.uuconf_stli.uuconf_zservaddr; + if (zservaddr == NULL) + ulog (LOG_FATAL, "Can't run as TLI server; no server address"); + + zfreeaddr = zbufcpy (zservaddr); + qtbind->addr.len = cescape (zfreeaddr); + if (qtbind->addr.len > qtbind->addr.maxlen) + ulog (LOG_FATAL, "%s: TLI server address too long (max %d)", + zservaddr, qtbind->addr.maxlen); + memcpy (qtbind->addr.buf, zfreeaddr, qtbind->addr.len); + ubuffree (zfreeaddr); + + qtbind->qlen = 5; + + if (t_bind (qsysdep->o, qtbind, (struct t_bind *) NULL) < 0) + ulog (LOG_FATAL, "t_bind (%s): %s", zservaddr, ztlierror ()); + + (void) t_free ((pointer) qtbind, T_BIND); + + qtcall = (struct t_call *) t_alloc (qsysdep->o, T_CALL, T_ALL); + if (qtcall == NULL) + ulog (LOG_FATAL, "t_alloc (T_CALL): %s", ztlierror ()); + + while (! FGOT_SIGNAL ()) + { + int onew; + pid_t ipid; + + DEBUG_MESSAGE0 (DEBUG_PORT, + "ftli_open: Waiting for connections"); + + if (t_listen (qsysdep->o, qtcall) < 0) + ulog (LOG_FATAL, "t_listen: %s", ztlierror ()); + + onew = t_open (zdevice, O_RDWR, (struct t_info *) NULL); + if (onew < 0) + ulog (LOG_FATAL, "t_open (%s): %s", zdevice, ztlierror ()); + + if (fcntl (onew, F_SETFD, + fcntl (onew, F_GETFD, 0) | FD_CLOEXEC) < 0) + ulog (LOG_FATAL, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + + if (t_bind (onew, (struct t_bind *) NULL, (struct t_bind *) NULL) < 0) + ulog (LOG_FATAL, "t_bind: %s", ztlierror ()); + + if (t_accept (qsysdep->o, onew, qtcall) < 0) + { + /* We may have received a disconnect. */ + if (t_errno != TLOOK) + ulog (LOG_FATAL, "t_accept: %s", ztlierror ()); + if (t_rcvdis (qsysdep->o, (struct t_discon *) NULL) < 0) + ulog (LOG_FATAL, "t_rcvdis: %s", ztlierror ()); + (void) t_close (onew); + continue; + } + + DEBUG_MESSAGE0 (DEBUG_PORT, + "ftli_open: Got connection; forking"); + + ipid = ixsfork (); + if (ipid < 0) + ulog (LOG_FATAL, "fork: %s", strerror (errno)); + if (ipid == 0) + { + ulog_close (); + + (void) t_close (qsysdep->o); + qsysdep->o = onew; + + /* Push any desired modules. */ + if (! ftli_push (qconn)) + _exit (EXIT_FAILURE); + + /* Now we fork and let our parent die, so that we become + a child of init. This lets the main server code wait + for its child and then continue without accumulating + zombie children. */ + ipid = ixsfork (); + if (ipid < 0) + { + ulog (LOG_ERROR, "fork: %s", strerror (errno)); + _exit (EXIT_FAILURE); + } + + if (ipid != 0) + _exit (EXIT_SUCCESS); + + ulog_id (getpid ()); + + return TRUE; + } + + (void) t_close (onew); + + /* Now wait for the child. */ + (void) ixswait ((unsigned long) ipid, (const char *) NULL); + } + + /* We got a signal. */ + usysdep_exit (FALSE); + + /* Avoid compiler warnings. */ + return FALSE; +} + +/* Close the port. */ + +/*ARGSUSED*/ +static boolean +ftli_close (qconn, puuconf, qdialer, fsuccess) + struct sconnection *qconn; + pointer puuconf; + struct uuconf_dialer *qdialer; + boolean fsuccess; +{ + struct ssysdep_conn *qsysdep; + boolean fret; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + + fret = TRUE; + if (qsysdep->o >= 0) + { + if (qsysdep->ftli) + { + if (t_close (qsysdep->o) < 0) + { + ulog (LOG_ERROR, "t_close: %s", ztlierror ()); + fret = FALSE; + } + } + else + { + if (close (qsysdep->o) < 0) + { + ulog (LOG_ERROR, "close: %s", strerror (errno)); + fret = FALSE; + } + } + + qsysdep->o = -1; + } + + return fret; +} + +/* Reset the port. This will be called by a child which was forked + off in ftli_open, above. We don't want uucico to continue looping + and giving login prompts, so we pretend that we received a SIGINT + signal. This should probably be handled more cleanly. The signal + will not be recorded in the log file because we don't set + afLog_signal[INDEXSIG_SIGINT]. */ + +/*ARGSUSED*/ +static boolean +ftli_reset (qconn) + struct sconnection *qconn; +{ + afSignal[INDEXSIG_SIGINT] = TRUE; + return TRUE; +} + +/* Dial out on a TLI port, so to speak: connect to a remote computer. */ + +/*ARGSUSED*/ +static boolean +ftli_dial (qconn, puuconf, qsys, zphone, qdialer, ptdialerfound) + struct sconnection *qconn; + pointer puuconf; + const struct uuconf_system *qsys; + const char *zphone; + struct uuconf_dialer *qdialer; + enum tdialerfound *ptdialerfound; +{ + struct ssysdep_conn *qsysdep; + char **pzdialer; + const char *zaddr; + struct t_call *qtcall; + char *zescape; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + + *ptdialerfound = DIALERFOUND_FALSE; + + pzdialer = qconn->qport->uuconf_u.uuconf_stli.uuconf_pzdialer; + if (*pzdialer == NULL) + pzdialer = NULL; + + /* If the first dialer is "TLI" or "TLIS", we use the first token + (pzdialer[1]) as the address to connect to. */ + zaddr = zphone; + if (pzdialer != NULL + && (strcmp (pzdialer[0], "TLI") == 0 + || strcmp (pzdialer[0], "TLIS") == 0)) + { + if (pzdialer[1] == NULL) + ++pzdialer; + else + { + if (strcmp (pzdialer[1], "\\D") != 0 + && strcmp (pzdialer[1], "\\T") != 0) + zaddr = pzdialer[1]; + pzdialer += 2; + } + } + + if (zaddr == NULL) + { + ulog (LOG_ERROR, "No address for TLI connection"); + return FALSE; + } + + qtcall = (struct t_call *) t_alloc (qsysdep->o, T_CALL, T_ADDR); + if (qtcall == NULL) + { + ulog (LOG_ERROR, "t_alloc (T_CALL): %s", ztlierror ()); + return FALSE; + } + + zescape = zbufcpy (zaddr); + qtcall->addr.len = cescape (zescape); + if (qtcall->addr.len > qtcall->addr.maxlen) + { + ulog (LOG_ERROR, "%s: TLI address too long (max %d)", zaddr, + qtcall->addr.maxlen); + ubuffree (zescape); + return FALSE; + } + memcpy (qtcall->addr.buf, zescape, qtcall->addr.len); + ubuffree (zescape); + + if (t_connect (qsysdep->o, qtcall, (struct t_call *) NULL) < 0) + { + if (t_errno != TLOOK) + ulog (LOG_ERROR, "t_connect: %s", ztlierror ()); + else + { + if (t_rcvdis (qsysdep->o, (struct t_discon *) NULL) < 0) + ulog (LOG_ERROR, "t_rcvdis: %s", ztlierror ()); + else + ulog (LOG_ERROR, "Connection refused"); + } + return FALSE; + } + + /* We've connected to the remote. Push any desired modules. */ + if (! ftli_push (qconn)) + return FALSE; + + /* Handle the rest of the dialer sequence. This is similar to + fmodem_dial, and they should, perhaps, be combined somehow. */ + if (pzdialer != NULL) + { + boolean ffirst; + + ffirst = TRUE; + while (*pzdialer != NULL) + { + int iuuconf; + struct uuconf_dialer *q; + struct uuconf_dialer s; + const char *ztoken; + boolean ftranslate; + + if (! ffirst) + q = &s; + else + q = qdialer; + + iuuconf = uuconf_dialer_info (puuconf, *pzdialer, q); + if (iuuconf == UUCONF_NOT_FOUND) + { + ulog (LOG_ERROR, "%s: Dialer not found", *pzdialer); + return FALSE; + } + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + + ++pzdialer; + ztoken = *pzdialer; + + ftranslate = FALSE; + if (ztoken == NULL + || strcmp (ztoken, "\\D") == 0) + ztoken = zphone; + else if (strcmp (ztoken, "\\T") == 0) + { + ztoken = zphone; + ftranslate = TRUE; + } + + if (! fchat (qconn, puuconf, &q->uuconf_schat, + (const struct uuconf_system *) NULL, q, + zphone, ftranslate, qconn->qport->uuconf_zname, + (long) 0)) + { + (void) uuconf_dialer_free (puuconf, q); + if (! ffirst) + (void) uuconf_dialer_free (puuconf, qdialer); + return FALSE; + } + + if (ffirst) + { + *ptdialerfound = DIALERFOUND_FREE; + ffirst = FALSE; + } + else + (void) uuconf_dialer_free (puuconf, q); + + if (*pzdialer != NULL) + ++pzdialer; + } + } + + return TRUE; +} + +#endif /* HAVE_TLI */ diff --git a/gnu/libexec/uucp/common_sources/trans.h b/gnu/libexec/uucp/common_sources/trans.h new file mode 100644 index 0000000000..79c380ea48 --- /dev/null +++ b/gnu/libexec/uucp/common_sources/trans.h @@ -0,0 +1,268 @@ +/* trans.h + Header file for file and command transfer routines. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +/* The maximum possible number of channels. */ +#define IMAX_CHAN (16) + +/* The ifeatures field of the sdaemon structure is an or of the + following values. These values are sent during the uucico + handshake, and MUST NOT CHANGE. */ + +/* File size negotiation. */ +#define FEATURE_SIZES (01) + +/* File transfer restart. */ +#define FEATURE_RESTART (02) + +/* The E (execute) command. */ +#define FEATURE_EXEC (04) + +/* Version 1.03: requires decimal size in S and R command. Needless + to say, this should not be used by any new programs. */ +#define FEATURE_V103 (010) + +/* SVR4 UUCP: expects dummy string between notify field and size field + in send command. There is probably some meaning to this string, + but I don't know what it is. If I ever find out, this flag will + still be used to indicate it. */ +#define FEATURE_SVR4 (020) + +/* This structure is used to hold information concerning the + communication link established with the remote system. */ + +struct sdaemon +{ + /* Global uuconf pointer. */ + pointer puuconf; + /* Remote system information. */ + const struct uuconf_system *qsys; + /* Local name being used. */ + const char *zlocalname; + /* Connection structure. */ + struct sconnection *qconn; + /* Protocol being used. */ + const struct sprotocol *qproto; + /* The largest file size permitted for a local request. */ + long clocal_size; + /* The largest file size permitted for a remote request. */ + long cremote_size; + /* The largest file size that may ever be transferred. */ + long cmax_ever; + /* The remote system ulimit. */ + long cmax_receive; + /* Features supported by the remote side. */ + int ifeatures; + /* TRUE if we should request the remote side to hang up. */ + boolean frequest_hangup; + /* TRUE if the remote side requested a hangup. */ + boolean fhangup_requested; + /* TRUE if we are hanging up. */ + boolean fhangup; + /* TRUE if the local system is currently the master. */ + boolean fmaster; + /* TRUE if the local system placed the call. */ + boolean fcaller; + /* UUCONF_RELIABLE_* flags for the connection. */ + int ireliable; + /* If fcaller is FALSE, the lowest grade which may be transferred + during this call. */ + char bgrade; +}; + +/* This structure is used to hold a file or command transfer which is + in progress. */ + +struct stransfer +{ + /* Next file transfer in queue. */ + struct stransfer *qnext; + /* Previous file transfer in queue. */ + struct stransfer *qprev; + /* Points to the queue this structure is on. */ + struct stransfer **pqqueue; + /* The function to call to send some data. */ + boolean (*psendfn) P((struct stransfer *qtrans, struct sdaemon *qdaemon)); + /* The function to call when data is received. */ + boolean (*precfn) P((struct stransfer *qtrans, struct sdaemon *qdaemon, + const char *zdata, size_t cdata)); + /* Type specific information. */ + pointer pinfo; + /* TRUE if we are sending the file e (this is used to avoid a call + to psendfn). */ + boolean fsendfile; + /* TRUE if we are receiving the file e (this is used to avoid a call + to precfn). */ + boolean frecfile; + /* The file to read or write. */ + openfile_t e; + /* The position we are at in the file. */ + long ipos; + /* TRUE if we are waiting for a command string. */ + boolean fcmd; + /* The command string we have so far. */ + char *zcmd; + /* The length of the command string we have so far. */ + size_t ccmd; + /* Local destination number. */ + int ilocal; + /* Remote destination number. */ + int iremote; + /* The command. */ + struct scmd s; + /* A message to log when work starts. */ + char *zlog; + /* The process time; imicros can be negative. */ + long isecs; + long imicros; + /* Number of bytes sent or received. */ + long cbytes; +}; + +/* Reasons that a file transfer might fail. */ + +enum tfailure +{ + /* No failure. */ + FAILURE_NONE, + /* No permission for operation. */ + FAILURE_PERM, + /* Can't open necessary file. */ + FAILURE_OPEN, + /* Not enough space to receive file. */ + FAILURE_SIZE, + /* File was received in a previous conversation. */ + FAILURE_RECEIVED +}; + +/* The main loop which talks to the remote system, passing transfer + requests and file back and forth. */ +extern boolean floop P((struct sdaemon *qdaemon)); + +/* Allocate a new transfer structure. */ +extern struct stransfer *qtransalc P((struct scmd *qcmd)); + +/* Free a transfer structure. */ +extern void utransfree P((struct stransfer *qtrans)); + +/* Queue up local requests. If pfany is not NULL, this sets *pfany to + TRUE if there are, in fact, any local requests which can be done at + this point. */ +extern boolean fqueue P((struct sdaemon *qdaemon, boolean *pfany)); + +/* Clear away any queued requests. This may be called more than once + at the end of a call. */ +extern void uclear_queue P((struct sdaemon *qdaemon)); + +/* Queue a new transfer request made by the local system. */ +extern boolean fqueue_local P((struct sdaemon *qdaemon, + struct stransfer *qtrans)); + +/* Queue a new transfer request made by the remote system. */ +extern boolean fqueue_remote P((struct sdaemon *qdaemon, + struct stransfer *qtrans)); + +/* Queue a transfer request which wants to send something. */ +extern boolean fqueue_send P((struct sdaemon *qdaemon, + struct stransfer *qtrans)); + +/* Queue a transfer request which wants to receiving something. */ +extern boolean fqueue_receive P((struct sdaemon *qdaemon, + struct stransfer *qtrans)); + +/* Prepare to send a file by local or remote request. */ +extern boolean flocal_send_file_init P((struct sdaemon *qdaemon, + struct scmd *qcmd)); +extern boolean fremote_send_file_init P((struct sdaemon *qdaemon, + struct scmd *qcmd, + int iremote)); + +/* Prepare to receive a file by local or remote request. */ +extern boolean flocal_rec_file_init P((struct sdaemon *qdaemon, + struct scmd *qcmd)); +extern boolean fremote_rec_file_init P((struct sdaemon *qdaemon, + struct scmd *qcmd, + int iremote)); + +/* Prepare to request work by local or remote request. */ +extern boolean flocal_xcmd_init P((struct sdaemon *qdaemon, + struct scmd *qcmd)); +extern boolean fremote_xcmd_init P((struct sdaemon *qdaemon, + struct scmd *qcmd, + int iremote)); + +/* We have lost the connection; record any in progress file transfers + in the statistics file and discard any temporary files. */ +extern void ufailed P((struct sdaemon *qdaemon)); + +/* Check that there is enough disk space for a file receive. Return + FALSE if there is not. */ +extern boolean frec_check_free P((struct stransfer *qtrans, + long cfree_space)); + +/* Discard the temporary file being used to receive a file, if + appropriate. */ +extern boolean frec_discard_temp P((struct sdaemon *qdaemon, + struct stransfer *qtrans)); + +/* Handle data received by a protocol. This is called by the protocol + specific routines as data comes in. The data is passed as two + buffers because that is convenient for packet based protocols, but + normally csecond will be 0. The ilocal argument is the local + channel number, and the iremote argument is the remote channel + number. Either may be -1, if the protocol does not have channels. + The ipos argument is the position in the file, if the protocol + knows it; for most protocols, this will be -1. The fallacked + argument should be set to TRUE if the remote has acknowledged all + outstanding data; see uwindow_acked, below, for details. This will + set *pfexit to TRUE if there is something for the main loop to do. + A file is complete is when a zero length buffer is passed (cfirst + == 0). A command is complete when data containing a null byte is + passed. This will return FALSE on error. If the protocol pfwait + entry point should exit and let the top level loop continue, + *pfexit will be set to TRUE (if pfexit is not NULL). This will not + set *pfexit to FALSE, so the caller must do that. */ +extern boolean fgot_data P((struct sdaemon *qdaemon, + const char *zfirst, size_t cfirst, + const char *zsecond, size_t csecond, + int ilocal, int iremote, + long ipos, boolean fallacked, + boolean *pfexit)); + +/* This routine is called when an ack is sent for a file receive. */ +extern void usent_receive_ack P((struct sdaemon *qdaemon, + struct stransfer *qtrans)); + +/* A protocol may call this routine to indicate the packets have been + acknowledged by the remote system. If the fallacked argument is + TRUE, then all outstanding packets have been acknowledged; for + convenience, this may also be indicated by passing fallacked as + TRUE to fgot_data, above. Otherwise this routine should be called + each time a complete window is acked by the remote system. The + transfer code uses this information to keep track of when an + acknowledgement of a file receive has been seen by the other side, + so that file receives may be handled cleanly if the connection is + lost. */ +extern void uwindow_acked P((struct sdaemon *qdaemon, + boolean fallacked)); diff --git a/gnu/libexec/uucp/common_sources/util.c b/gnu/libexec/uucp/common_sources/util.c new file mode 100644 index 0000000000..2ddc2608dd --- /dev/null +++ b/gnu/libexec/uucp/common_sources/util.c @@ -0,0 +1,144 @@ +/* util.c + A couple of UUCP utility functions. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char util_rcsid[] = "$Id: util.c,v 1.1 1993/08/04 19:31:13 jtc Exp $"; +#endif + +#include + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" + +/* Get information for an unknown system. This will leave the name + allocated on the heap. We could fix this by breaking the + abstraction and adding the name to qsys->palloc. It makes sure the + name is not too long, but takes no other useful action. */ + +boolean +funknown_system (puuconf, zsystem, qsys) + pointer puuconf; + const char *zsystem; + struct uuconf_system *qsys; +{ + char *z; + int iuuconf; + + if (strlen (zsystem) <= cSysdep_max_name_len) + z = zbufcpy (zsystem); + else + { + char **pznames, **pz; + boolean ffound; + + z = zbufalc (cSysdep_max_name_len + 1); + memcpy (z, zsystem, cSysdep_max_name_len); + z[cSysdep_max_name_len] = '\0'; + + iuuconf = uuconf_system_names (puuconf, &pznames, TRUE); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + ffound = FALSE; + for (pz = pznames; *pz != NULL; pz++) + { + if (strcmp (*pz, z) == 0) + ffound = TRUE; + xfree ((pointer) *pz); + } + xfree ((pointer) pznames); + + if (ffound) + { + ubuffree (z); + return FALSE; + } + } + + iuuconf = uuconf_system_unknown (puuconf, qsys); + if (iuuconf == UUCONF_NOT_FOUND) + { + ubuffree (z); + return FALSE; + } + else if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + for (; qsys != NULL; qsys = qsys->uuconf_qalternate) + qsys->uuconf_zname = z; + + return TRUE; +} + +/* See whether a file is in a directory list, and make sure the user + has appropriate access. */ + +boolean +fin_directory_list (zfile, pzdirs, zpubdir, fcheck, freadable, zuser) + const char *zfile; + char **pzdirs; + const char *zpubdir; + boolean fcheck; + boolean freadable; + const char *zuser; +{ + boolean fmatch; + char **pz; + + fmatch = FALSE; + + for (pz = pzdirs; *pz != NULL; pz++) + { + char *zuse; + + if (pz[0][0] == '!') + { + zuse = zsysdep_local_file (*pz + 1, zpubdir); + if (zuse == NULL) + return FALSE; + + if (fsysdep_in_directory (zfile, zuse, FALSE, + FALSE, (const char *) NULL)) + fmatch = FALSE; + } + else + { + zuse = zsysdep_local_file (*pz, zpubdir); + if (zuse == NULL) + return FALSE; + + if (fsysdep_in_directory (zfile, zuse, fcheck, + freadable, zuser)) + fmatch = TRUE; + } + + ubuffree (zuse); + } + + return fmatch; +} diff --git a/gnu/libexec/uucp/common_sources/uuconf.h b/gnu/libexec/uucp/common_sources/uuconf.h new file mode 100644 index 0000000000..4bf6bccbf3 --- /dev/null +++ b/gnu/libexec/uucp/common_sources/uuconf.h @@ -0,0 +1,1496 @@ +/* uuconf.h + Header file for UUCP configuration routines. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The use of an object file which uses material from this header + file, and from no other portion of the uuconf library, is + unrestricted, as described in paragraph 4 of section 5 of version 2 + of the GNU Library General Public License (this sentence is merely + informative, and does not modify the License in any way). + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#ifndef UUCONF_H + +#define UUCONF_H + +#include + +/* The macro UUCONF_ANSI_C may be used to override __STDC__. */ +#ifndef UUCONF_ANSI_C +#ifdef __STDC__ +#define UUCONF_ANSI_C 1 +#else /* ! defined (__STDC__) */ +#define UUCONF_ANSI_C 0 +#endif /* ! defined (__STDC__) */ +#endif /* ! defined (UUCONF_ANSI_C) */ + +#if UUCONF_ANSI_C +#define UUCONF_CONST const +typedef void *UUCONF_POINTER; +#include +typedef size_t UUCONF_SIZE_T; +#else +#define UUCONF_CONST +typedef char *UUCONF_POINTER; +typedef unsigned int UUCONF_SIZE_T; +#endif + +/* The field names of each of the following structures begin with + "uuconf_". This is to avoid any conflicts with user defined + macros. The first character following the "uuconf_" string + indicates the type of the field. + + z -- a string (char *) + c -- a count (normally int) + i -- an integer value (normally int) + f -- a boolean value (normally int) + b -- a single character value (char or int) + t -- an enum (enum XX) + s -- a structure (struct XX) + u -- a union (union XX) + q -- a pointer to a structure (struct XX *) + p -- a pointer to something other than a string + */ + +/* The information which is kept for a chat script. */ + +struct uuconf_chat +{ + /* The script itself. This is a NULL terminated list of expect/send + pairs. The first string is an expect string. A string starting + with a '-' indicates subsend string; the following strings which + start with '-' are subexpect/subsend strings. This field may be + NULL, in which case there is no chat script (but pzprogram may + hold a program to run). */ + char **uuconf_pzchat; + /* The chat program to run. This is a NULL terminated list of + arguments; element 0 if the program. May be NULL, in which case + there is no program. */ + char **uuconf_pzprogram; + /* The timeout in seconds to use for expect strings in the chat + script. */ + int uuconf_ctimeout; + /* The NULL terminated list of failure strings. If any of these + strings appear, the chat script is aborted. May be NULL, in + which case there are no failure strings. */ + char **uuconf_pzfail; + /* Non-zero if incoming characters should be stripped to seven bits + (by anding with 0x7f). */ + int uuconf_fstrip; +}; + +/* The information which is kept for a time specification. This is a + linked list of structures. Each element of the list represents a + span of time, giving a starting time and an ending time. The time + only depends on the day of the week, not on the day of the month or + of the year. The time is only specified down to the minute, not + down to the second or below. The list is sorted by starting time. + + The starting and ending time are expressed in minutes since the + beginning of the week, which is considered to be 12 midnight on + Sunday. Thus 60 is 1 am on Sunday, 1440 (== 60 * 24) is 12 + midnight on Monday, and the largest possible value is 10080 (== 60 + * 24 * 7) which is 12 midnight on the following Sunday. + + Each span of time has a value associated with it. This is the + lowest grade or the largest file size that may be transferred + during that time, depending on the source of the time span. When + time specifications overlap, the value used for the overlap is the + higher grade or the smaller file size. Thus specifying + ``call-timegrade z Any'' and ``call-timegrade Z Mo'' means that + only grade Z or higher may be sent on Monday, since Z is the higer + grade of the overlapping spans. The final array wil have no + overlaps. + + Each span also has a retry time associated with it. This permits + different retry times to be used at different times of day. The + retry time is only relevant if the span came from a ``time'' or + ``timegrade'' command for a system. */ + +struct uuconf_timespan +{ + /* Next element in list. */ + struct uuconf_timespan *uuconf_qnext; + /* Starting minute (-1 at the end of the array). */ + int uuconf_istart; + /* Ending minute. */ + int uuconf_iend; + /* Value for this span (lowest grade or largest file that may be + transferred at this time). */ + long uuconf_ival; + /* Retry time. */ + int uuconf_cretry; +}; + +/* The information which is kept for protocol parameters. Protocol + parameter information is stored as an array of the following + structures. */ + +struct uuconf_proto_param +{ + /* The name of the protocol to which this entry applies. This is + '\0' for the last element of the array. */ + int uuconf_bproto; + /* Specific entries for this protocol. This points to an array + ending in an element with a uuconf_cargs field of 0. */ + struct uuconf_proto_param_entry *uuconf_qentries; +}; + +/* Each particular protocol parameter entry is one of the following + structures. */ + +struct uuconf_proto_param_entry +{ + /* The number of arguments to the ``protocol-parameter'' command + (not counting ``protocol-parameter'' itself). This is 0 for the + last element of the array. */ + int uuconf_cargs; + /* The actual arguments to the ``protocol-parameter'' command; this + is an array with cargs entries. */ + char **uuconf_pzargs; +}; + +/* The information which is kept for a system. The zname and zalias + fields will be the same for all alternates. Every other fields is + specific to the particular alternate in which it appears (although + most will be the same for all alternates). */ + +struct uuconf_system +{ + /* The name of the system. */ + char *uuconf_zname; + /* A list of aliases for the system. This is a NULL terminated list + of strings. May be NULL, in which case there are no aliases. */ + char **uuconf_pzalias; + /* A linked list of alternate call in or call out information. Each + alternative way to call this system occupies an element of this + list. May be NULL, in which case there are no alternates. */ + struct uuconf_system *uuconf_qalternate; + /* The name for this particular alternate. May be NULL, in which + case this alternate does not have a name. */ + char *uuconf_zalternate; + /* If non-zero, this alternate may be used for calling out. */ + int uuconf_fcall; + /* If non-zero, this alternate may be used for accepting a call. */ + int uuconf_fcalled; + /* The times at which this system may be called. The ival field of + each uuconf_timespan structure is the lowest grade which may be + transferred at that time. The cretry field is the number of + minutes to wait before retrying the call, or 0 if it was not + specified. May be NULL, in which case the system may never be + called. */ + struct uuconf_timespan *uuconf_qtimegrade; + /* The times at which to request a particular grade of the system + when calling it, and the grades to request. The ival field of + each uuconf_timespan structure is the lowest grade which the + other system should transfer at that time. May be NULL, in which + case there are no grade restrictions. */ + struct uuconf_timespan *uuconf_qcalltimegrade; + /* The maximum number of times to retry calling this system. If + this is 0, there is no limit. */ + int uuconf_cmax_retries; + /* The number of minutes to wait between successful calls to a + system. */ + int uuconf_csuccess_wait; + /* The size restrictions by time for local requests during a locally + placed call. The ival field of each uuconf_timespan structure is + the size in bytes of the largest file which may be transferred at + that time. May be NULL, in which case there are no size + restrictions. */ + struct uuconf_timespan *uuconf_qcall_local_size; + /* The size restrictions by time for remote requests during a + locally placed call. May be NULL. */ + struct uuconf_timespan *uuconf_qcall_remote_size; + /* The size restrictions by time for local requests during a + remotely placed call. May be NULL. */ + struct uuconf_timespan *uuconf_qcalled_local_size; + /* The size restrictions by time for remote requests during a + remotely placed call. May be NULL. */ + struct uuconf_timespan *uuconf_qcalled_remote_size; + /* Baud rate, or speed. Zero means any baud rate. If ihighbaud is + non-zero, this is the low baud rate of a range. */ + long uuconf_ibaud; + /* If non-zero, ibaud is the low baud rate of a range and ihighbaud + is the high baud rate. */ + long uuconf_ihighbaud; + /* Port name to use. May be NULL. If an HDB configuration file + contains a modem class (alphabetic characters preceeding the baud + rate), the class is appended to the port name. */ + char *uuconf_zport; + /* Specific port information, if the system entry includes port + information. May be NULL. */ + struct uuconf_port *uuconf_qport; + /* Phone number to call, or address to use for a TCP connection. + May be NULL, in which case a dialer script may not use \D or \T + for this system, and a TCP port will use the system name. */ + char *uuconf_zphone; + /* Chat script to use when logging in to the system. */ + struct uuconf_chat uuconf_schat; + /* Login name to use for \L in the login chat script. This should + normally be accessed via uuconf_callout. If it is "*", + uuconf_callout will look it up in the call out file. This may be + NULL, in which case the login script may not use \L. */ + char *uuconf_zcall_login; + /* Password to use for \P in the login chat script. This should + normally be accessed via uuconf_callout. If it is "*", + uuconf_callout will look it up in the call out file. This may be + NULL, in which case the login script may not use \P. */ + char *uuconf_zcall_password; + /* The login name this system must use when calling in. This may be + different for different alternates. This should only be examined + if uuconf_fcalled is TRUE. If this is NULL or "ANY" then + uuconf_validate must be called to make sure that whatever login + name was used is permitted for this machine. */ + char *uuconf_zcalled_login; + /* If non-zero, then when this system calls in the call should not + be allowed to proceed and the system should be called back. */ + int uuconf_fcallback; + /* If non-zero, then conversation sequence numbers should be used + with this system. */ + int uuconf_fsequence; + /* A list of protocols to use with this system. Each protocol has a + single character name. May be NULL, in which case any known + protocol may be used. */ + char *uuconf_zprotocols; + /* Array of protocol parameters. Ends in an entry with a + uuconf_bproto field of '\0'. May be NULL. */ + struct uuconf_proto_param *uuconf_qproto_params; + /* Chat script to run when called by this system. */ + struct uuconf_chat uuconf_scalled_chat; + /* Debugging level to set during a conversation. May be NULL. */ + char *uuconf_zdebug; + /* Maximum remote debugging level this system may request. May be + NULL. */ + char *uuconf_zmax_remote_debug; + /* Non-zero if the remote system may request us to send files from + the local system to the remote. */ + int uuconf_fsend_request; + /* Non-zero if the remote system may request us to receive files + from the remote system to the local. */ + int uuconf_frec_request; + /* Non-zero if local requests are permitted when calling this + system. */ + int uuconf_fcall_transfer; + /* Non-zero if local requests are permitted when this system calls + in. */ + int uuconf_fcalled_transfer; + /* NULL terminated list of directories from which files may be sent + by local request. */ + char **uuconf_pzlocal_send; + /* NULL terminated list of directories from which files may be sent + by remote request. */ + char **uuconf_pzremote_send; + /* NULL terminated list of directories into which files may be + received by local request. */ + char **uuconf_pzlocal_receive; + /* NULL terminated list of directories into which files may be + received by remote request. */ + char **uuconf_pzremote_receive; + /* Path to use for command execution. This is a NULL terminated + list of directories. */ + char **uuconf_pzpath; + /* NULL terminated List of commands that may be executed. */ + char **uuconf_pzcmds; + /* Amount of free space to leave when accepting a file from this + system, in bytes. */ + long uuconf_cfree_space; + /* NULL terminated list of systems that this system may forward + from. May be NULL if there are no systems from which files may + be forwarded. The list may include "ANY". */ + char **uuconf_pzforward_from; + /* NULL terminated list of systems that this system may forward to. + May be NULL if there are no systems to which files may be + forwarded. The list may include "ANY". */ + char **uuconf_pzforward_to; + /* The public directory to use for this sytem. */ + const char *uuconf_zpubdir; + /* The local name to use for this remote system. May be NULL if the + usual local name should be used. */ + char *uuconf_zlocalname; + /* Memory allocation block for the system. */ + UUCONF_POINTER uuconf_palloc; +}; + +/* Types of ports. */ + +enum uuconf_porttype +{ + /* Unknown port type. A port of this type should never be returned + by the uuconf functions. */ + UUCONF_PORTTYPE_UNKNOWN, + /* Read from standard input and write to standard output. Not + normally used. */ + UUCONF_PORTTYPE_STDIN, + /* A modem port. */ + UUCONF_PORTTYPE_MODEM, + /* A direct connect port. */ + UUCONF_PORTTYPE_DIRECT, + /* A TCP port. Not supported on all systems. */ + UUCONF_PORTTYPE_TCP, + /* A TLI port. Not supported on all systems. */ + UUCONF_PORTTYPE_TLI +}; + +/* Additional information for a stdin port (there is none). */ + +struct uuconf_stdin_port +{ + int uuconf_idummy; +}; + +/* Additional information for a modem port. */ + +struct uuconf_modem_port +{ + /* The device name. May be NULL, in which case the port name is + used instead. */ + char *uuconf_zdevice; + /* The device name to send the dialer chat script to. May be NULL, + in which case the chat script is sent to the usual device. */ + char *uuconf_zdial_device; + /* The default baud rate (speed). If zero, there is no default. */ + long uuconf_ibaud; + /* The low baud rate, if a range is used. If zero, a range is not + used and ihighbaud should be ignored. */ + long uuconf_ilowbaud; + /* The high baud rate, if ilowbaud is non-zero. */ + long uuconf_ihighbaud; + /* Non-zero if the port supports carrier detect. */ + int uuconf_fcarrier; + /* A NULL terminated sequence of dialer/token pairs (element 0 is a + dialer name, element 1 is a token, etc.) May be NULL, in which + case qdialer should not be NULL. */ + char **uuconf_pzdialer; + /* Specific dialer information. Only used if pzdialer is NULL. */ + struct uuconf_dialer *uuconf_qdialer; +}; + +/* Additional information for a direct connect port. */ + +struct uuconf_direct_port +{ + /* The device name. May be NULL, in which case the port name is + used instead. */ + char *uuconf_zdevice; + /* The baud rate (speed). */ + long uuconf_ibaud; +}; + +/* Additional information for a TCP port. */ + +struct uuconf_tcp_port +{ + /* The TCP port number to use. May be a name or a number. May be + NULL, in which case "uucp" is looked up using getservbyname. */ + char *uuconf_zport; +}; + +/* Additional information for a TLI port. */ + +struct uuconf_tli_port +{ + /* Device name to open. May be NULL, in which case the port name is + used. */ + char *uuconf_zdevice; + /* Whether this port should be turned into a stream, permitting the + read and write calls instead of the t_rcv and t_send calls. */ + int uuconf_fstream; + /* A NULL terminated list of modules to push after making the + connection. May be NULL, in which case if fstream is non-zero, + then "tirdwr" is pushed onto the stream, and otherwise nothing is + pushed. */ + char **uuconf_pzpush; + /* A NULL terminated sequence of dialer/token pairs (element 0 is a + dialer name, element 1 is a token, etc.) May be NULL. If + element 0 is TLI or TLIS, element 1 is used as the address to + connect to; otherwise uuconf_zphone from the system information + is used. */ + char **uuconf_pzdialer; + /* Address to use when operating as a server. This may contain + escape sequences. */ + char *uuconf_zservaddr; +}; + +/* Information kept for a port. */ + +struct uuconf_port +{ + /* The name of the port. */ + char *uuconf_zname; + /* The type of the port. */ + enum uuconf_porttype uuconf_ttype; + /* The list of protocols supported by the port. The name of each + protocol is a single character. May be NULL, in which case any + protocol is permitted. */ + char *uuconf_zprotocols; + /* Array of protocol parameters. Ends in an entry with a + uuconf_bproto field of '\0'. May be NULL. */ + struct uuconf_proto_param *uuconf_qproto_params; + /* The set of reliability bits. */ + int uuconf_ireliable; + /* The lock file name to use. */ + char *uuconf_zlockname; + /* Memory allocation block for the port. */ + UUCONF_POINTER uuconf_palloc; + /* The type specific information. */ + union + { + struct uuconf_stdin_port uuconf_sstdin; + struct uuconf_modem_port uuconf_smodem; + struct uuconf_direct_port uuconf_sdirect; + struct uuconf_tcp_port uuconf_stcp; + struct uuconf_tli_port uuconf_stli; + } uuconf_u; +}; + +/* Information kept about a dialer. */ + +struct uuconf_dialer +{ + /* The name of the dialer. */ + char *uuconf_zname; + /* The chat script to use when dialing out. */ + struct uuconf_chat uuconf_schat; + /* The string to send when a `=' appears in the phone number. */ + char *uuconf_zdialtone; + /* The string to send when a `-' appears in the phone number. */ + char *uuconf_zpause; + /* Non-zero if the dialer supports carrier detect. */ + int uuconf_fcarrier; + /* The number of seconds to wait for carrier after the chat script + is complete. Only used if fcarrier is non-zero. Only supported + on some systems. */ + int uuconf_ccarrier_wait; + /* If non-zero, DTR should be toggled before dialing. Only + supported on some systems. */ + int uuconf_fdtr_toggle; + /* If non-zero, sleep for 1 second after toggling DTR. Ignored if + fdtr_toggle is zero. */ + int uuconf_fdtr_toggle_wait; + /* The chat script to use when a call is complete. */ + struct uuconf_chat uuconf_scomplete; + /* The chat script to use when a call is aborted. */ + struct uuconf_chat uuconf_sabort; + /* Array of protocol parameters. Ends in an entry with a + uuconf_bproto field of '\0'. May be NULL. */ + struct uuconf_proto_param *uuconf_qproto_params; + /* The set of reliability bits. */ + int uuconf_ireliable; + /* Memory allocation block for the dialer. */ + UUCONF_POINTER uuconf_palloc; +}; + +/* Reliability bits for the ireliable field of ports and dialers. + These bits are used to decide which protocol to run. A given + protocol will have a set of these bits, and each of them must be + turned on for the port before we will permit that protocol to be + used. This will be overridden by the zprotocols field. */ + +/* Whether a set of reliability bits is given. If this bit is not + set, then there is no reliability information. */ +#define UUCONF_RELIABLE_SPECIFIED (01) + +/* Set if the connection is eight bit transparent. */ +#define UUCONF_RELIABLE_EIGHT (02) + +/* Set if the connection is error-free. */ +#define UUCONF_RELIABLE_RELIABLE (04) + +/* Set if the connection is end-to-end reliable (e.g. TCP). */ +#define UUCONF_RELIABLE_ENDTOEND (010) + +/* Set if the connection is full-duplex; that is, no time consuming + line turnaround is required before sending data in the reverse + direction. If the connection is truly half-duplex, in the sense + that communication can only flow in one direction, UUCP can not be + used. */ +#define UUCONF_RELIABLE_FULLDUPLEX (020) + +/* UUCP grades range from 0 to 9, A to Z, a to z in order from highest + to lowest (work of higher grades is done before work of lower + grades). */ + +/* The highest grade. */ +#define UUCONF_GRADE_HIGH ('0') + +/* The lowest grade. */ +#define UUCONF_GRADE_LOW ('z') + +/* Whether a character is a legal grade (requires ). */ +#define UUCONF_GRADE_LEGAL(b) (isalnum ((unsigned) (b))) + +/* Return < 0 if the first grade should be done before the second + grade, == 0 if they are the same, or > 0 if the first grade should + be done after the second grade. On an ASCII system, this can just + be b1 - b2. */ +#define UUCONF_GRADE_CMP(b1, b2) (uuconf_grade_cmp ((b1), (b2))) + +/* Most of the uuconf functions returns an error code. A value of + zero (UUCONF_SUCCESS) indicates success. */ + +/* If this bit is set in the returned error code, then the + uuconf_errno function may be used to obtain the errno value as set + by the function which caused the failure. */ +#define UUCONF_ERROR_ERRNO (0x100) + +/* If this bit is set in the returned error code, then the + uuconf_filename function may be used to get the name of a file + associated with the error. */ +#define UUCONF_ERROR_FILENAME (0x200) + +/* If this bit is set in the returned error code, then the + uuconf_lineno function may be used to get a line number associated + with the error; normally if this is set UUCONF_ERROR_FILENAME will + also be set. */ +#define UUCONF_ERROR_LINENO (0x400) + +/* There are two UUCONF_CMDTABRET bits that may be set in the return + value of uuconf_cmd_line or uuconf_cmd_args, described below. They + do not indicate an error, but instead give instructions to the + calling function, often uuconf_cmd_file. They may also be set in + the return value of a user function listed in a uuconf_cmdtab + table, in which case they will be honored by uuconf_cmd_file. */ + +/* This bit means that the memory occupied by the arguments passed to + the function should be preserved, and not overwritten or freed. It + refers only to the contents of the arguments; the contents of the + argv array itself may always be destroyed. If this bit is set in + the return value of uuconf_cmd_line or uuconf_cmd_args, it must be + honored. It will be honored by uuconf_cmd_file. This may be + combined with an error code or with UUCONF_CMDTABRET_EXIT, although + neither uuconf_cmd_file or uuconf_cmd_line will do so. */ +#define UUCONF_CMDTABRET_KEEP (0x800) + +/* This bit means that uuconf_cmd_file should exit, rather than go on + to read and process the next line. If uuconf_cmd_line or + uuconf_cmd_args encounter an error, the return value will have this + bit set along with the error code. A user function may set this + bit with or without an error; the return value of the user function + will be returned by uuconf_cmd_file, except that the + UUCONF_CMDTABRET_KEEP and UUCONF_CMDTABRET_EXIT bits will be + cleared. */ +#define UUCONF_CMDTABRET_EXIT (0x1000) + +/* This macro may be used to extract the specific error value. */ +#define UUCONF_ERROR_VALUE(i) ((i) & 0xff) + +/* UUCONF_ERROR_VALUE will return one of the following values. */ + +/* Function succeeded. */ +#define UUCONF_SUCCESS (0) +/* Named item not found. */ +#define UUCONF_NOT_FOUND (1) +/* A call to fopen failed. */ +#define UUCONF_FOPEN_FAILED (2) +/* A call to fseek failed. */ +#define UUCONF_FSEEK_FAILED (3) +/* A call to malloc or realloc failed. */ +#define UUCONF_MALLOC_FAILED (4) +/* Syntax error in file. */ +#define UUCONF_SYNTAX_ERROR (5) +/* Unknown command. */ +#define UUCONF_UNKNOWN_COMMAND (6) + +#if UUCONF_ANSI_C + +/* For each type of configuration file (Taylor, V2, HDB), there are + separate routines to read various sorts of information. There are + also generic routines, which call on the appropriate type specific + routines. The library can be compiled to read any desired + combination of the configuration file types. This affects only the + generic routines, as it determines which type specific routines + they call. Thus, on a system which, for example, does not have any + V2 configuration files, there is no need to include the overhead of + the code to parse the files and the time to look for them. + However, a program which specifically wants to be able to parse + them can call the V2 specific routines. + + The uuconf functions all take as an argument a pointer to uuconf + global information. This must be initialized by any the + initialization routines (the generic one and the three file type + specific ones) before any of the other uuconf functions may be + called. */ + +/* Initialize the configuration file reading routines. The ppglobal + argument should point to a generic pointer (a void *, or, on older + compilers, a char *) which will be initialized and may then be + passed to the other uuconf routines. The zprogram argument is the + name of the program for which files should be read. A NULL is + taken as "uucp", and reads the standard UUCP configuration files. + The only other common argument is "cu", but any string is + permitted. The zname argument is the name of the Taylor UUCP + config file; if it is NULL, the default config file will be read. + If not reading Taylor UUCP configuration information, the argument + is ignored. This function must be called before any of the other + uuconf functions. + + Note that if the zname argument is obtained from the user running + the program, the program should be careful to revoke any special + privileges it may have (e.g. on Unix call setuid (getuid ()) and + setgid (getgid ())). Otherwise various sorts of spoofing become + possible. */ +extern int uuconf_init (void **uuconf_ppglobal, + const char *uuconf_zprogram, + const char *uuconf_zname); + +/* Adjust the configuration file global pointer for a new thread. The + library is fully reentrant (with the exception of the function + uuconf_error_string, which calls strerror, which on some systems is + not reentrant), provided that each new thread that wishes to call + the library calls this function and uses the new global pointer + value. The ppglobal argument should be set to the address of the + global pointer set by any of the init functions; it will be + modified to become a new global pointer. */ +extern int uuconf_init_thread (void **uuconf_ppglobal); + +/* Get the names of all known systems. This sets sets *ppzsystems to + point to an array of system names. The list of names is NULL + terminated. The array is allocated using malloc, as is each + element of the array, and they may all be passed to free when they + are no longer needed. If the falias argument is 0, the list will + not include any aliases; otherwise, it will. */ +extern int uuconf_system_names (void *uuconf_pglobal, + char ***uuconf_ppzsystems, + int uuconf_falias); + +/* Get the information for the system zsystem. This sets the fields + in *qsys. This will work whether zsystem is the official name of + the system or merely an alias. */ +extern int uuconf_system_info (void *uuconf_pglobal, + const char *uuconf_zsystem, + struct uuconf_system *uuconf_qsys); + +/* Get information for an unknown (anonymous) system. The + uuconf_zname field of the returned system information will be NULL. + If no information is available for unknown systems, this will + return UUCONF_NOT_FOUND. This does not run the HDB remote.unknown + shell script. */ +extern int uuconf_system_unknown (void *uuconf_pglobal, + struct uuconf_system *uuconf_qsys); + +/* Get information for the local system. Normally the local system + name should first be looked up using uuconf_system_info. If that + returns UUCONF_NOT_FOUND, this function may be used to get an + appropriate set of defaults. The uuconf_zname field of the + returned system information may be NULL. */ +extern int uuconf_system_local (void *uuconf_pglobal, + struct uuconf_system *uuconf_qsys); + +/* Free the memory occupied by system information returned by + uuconf_system_info, uuconf_system_unknown, uuconf_system_local, or + any of the configuration file type specific routines described + below. After this is called, the contents of the structure shall + not be referred to. */ +extern int uuconf_system_free (void *uuconf_pglobal, + struct uuconf_system *uuconf_qsys); + +#ifdef __OPTIMIZE__ +#define uuconf_system_free(qglob, q) \ + (uuconf_free_block ((q)->uuconf_palloc), UUCONF_SUCCESS) +#endif + +/* Find a matching port. This will consider each port in turn. + + If the zname argument is not NULL, the port's uuconf_zname field + must match it. + + If the ibaud argument is not zero and the ihighbaud argument is + zero, the port's baud rate, if defined, must be the same (if the + port has a range of baud rates, ibaud must be within the range). + If ibaud and ihighbaud are both not zero, the port's baud rate, if + defined, must be between ibaud and ihighbaud inclusive (if the port + has a range of baud rates, the ranges must intersect). If the port + has no baud rate, either because it is a type of port for which + baud rate is not defined (e.g. a TCP port) or because the + uuconf_ibaud field is 0, the ibaud and ihighbaud arguments are + ignored. + + If the pifn argument is not NULL, the port is passed to pifn, along + with the pinfo argument (which is otherwise ignored). If pifn + returns UUCONF_SUCCESS, the port matches. If pifn returns + UUCONF_NOT_FOUND, a new port is sought. Otherwise the return value + of pifn is returned from uuconf_find_port. The pifn function may + be used to further restrict the port, such as by modem class or + device name. It may also be used to lock the port, if appropriate; + in this case, if the lock fails, pifn may return UUCONF_NOT_FOUND + to force uuconf_find_port to continue searching for a port. + + If the port matches, the information is set into uuconf_qport, and + uuconf_find_port returns UUCONF_SUCCESS. */ +extern int uuconf_find_port (void *uuconf_pglobal, + const char *uuconf_zname, + long uuconf_ibaud, + long uuconf_ihighbaud, + int (*uuconf_pifn) (struct uuconf_port *, + void *uuconf_pinfo), + void *uuconf_pinfo, + struct uuconf_port *uuconf_qport); + +/* Free the memory occupied by system information returned by + uuconf_find_port (or any of the configuration file specific + routines described below). After this is called, the contents of + the structure shall not be referred to. */ +extern int uuconf_port_free (void *uuconf_pglobal, + struct uuconf_port *uuconf_qport); + +#ifdef __OPTIMIZE__ +#define uuconf_port_free(qglob, q) \ + (uuconf_free_block ((q)->uuconf_palloc), UUCONF_SUCCESS) +#endif + +/* Get the names of all known dialers. This sets sets *ppzdialers to + point to an array of dialer names. The list of names is NULL + terminated. The array is allocated using malloc, as is each + element of the array, and they may all be passed to free when they + are no longer needed. */ +extern int uuconf_dialer_names (void *uuconf_pglobal, + char ***uuconf_ppzdialers); + +/* Get the information for the dialer zdialer. This sets the fields + in *qdialer. */ +extern int uuconf_dialer_info (void *uuconf_pglobal, + const char *uuconf_zdialer, + struct uuconf_dialer *uuconf_qdialer); + +/* Free the memory occupied by system information returned by + uuconf_dialer_info (or any of the configuration file specific + routines described below). After this is called, the contents of + the structure shall not be referred to. */ +extern int uuconf_dialer_free (void *uuconf_pglobal, + struct uuconf_dialer *uuconf_qsys); + +#ifdef __OPTIMIZE__ +#define uuconf_dialer_free(qglob, q) \ + (uuconf_free_block ((q)->uuconf_palloc), UUCONF_SUCCESS) +#endif + +/* Get the local node name. If the node name is not specified + (because no ``nodename'' command appeared in the config file) this + will return UUCONF_NOT_FOUND, and some system dependent function + must be used to determine the node name. Otherwise it will return + a pointer to a constant string, which should not be freed. */ +extern int uuconf_localname (void *uuconf_pglobal, + const char **pzname); + +/* Get the local node name that should be used, given a login name. + This function will check for any special local name that may be + associated with the login name zlogin (as set by the ``myname'' + command in a Taylor configuration file, or the MYNAME field in a + Permissions entry). This will set *pzname to the node name. If no + node name can be determined, *pzname will be set to NULL and the + function will return UUCONF_NOT_FOUND; in this case some system + dependent function must be used to determine the node name. If the + function returns UUCONF_SUCCESS, *pzname will be point to an + malloced buffer. */ +extern int uuconf_login_localname (void *uuconf_pglobal, + const char *uuconf_zlogin, + char **pzname); + +/* Get the name of the UUCP spool directory. This will set *pzspool + to a constant string, which should not be freed. */ +extern int uuconf_spooldir (void *uuconf_pglobal, + const char **uuconf_pzspool); + +/* Get the name of the default UUCP public directory. This will set + *pzpub to a constant string, which should not be freed. Note that + particular systems may use a different public directory. */ +extern int uuconf_pubdir (void *uuconf_pglobal, + const char **uuconf_pzpub); + +/* Get the name of the UUCP lock directory. This will set *pzlock to + a constant string, which should not be freed. */ +extern int uuconf_lockdir (void *uuconf_pglobal, + const char **uuconf_pzlock); + +/* Get the name of the UUCP log file. This will set *pzlog to a + constant string, which should not be freed. */ +extern int uuconf_logfile (void *uuconf_pglobal, + const char **uuconf_pzlog); + +/* Get the name of the UUCP statistics file. This will set *pzstats + to a constant string, which should not be freed. */ +extern int uuconf_statsfile (void *uuconf_pglobal, + const char **uuconf_pzstats); + +/* Get the name of the UUCP debugging file. This will set *pzdebug to + a constant string, which should not be freed. */ +extern int uuconf_debugfile (void *uuconf_pglobal, + const char **uuconf_pzdebug); + +/* Get the default debugging level to use. This basically gets the + argument of the ``debug'' command from the Taylor UUCP config file. + It will set *pzdebug to a constant string, which should not be + freed. */ +extern int uuconf_debuglevel (void *uuconf_pglobal, + const char **uuconf_pzdebug); + +/* Get the maximum number of simultaneous uuxqt executions. This will + set *pcmaxuuxqt to the number. Zero indicates no maximum. */ +extern int uuconf_maxuuxqts (void *uuconf_pglobal, + int *uuconf_pcmaxuuxqt); + +/* Check a login name and password. This checks the Taylor UUCP + password file (not /etc/passwd). It will work even if + uuconf_taylor_init was not called. If the login name exists and + the password is correct, this returns UUCONF_SUCCESS. If the login + does not exist, or the password is wrong, this returns + UUCONF_NOT_FOUND. Other errors are also possible. */ +extern int uuconf_callin (void *uuconf_pglobal, + const char *uuconf_zlogin, + const char *uuconf_zpassword); + +/* Get the callout login name and password for a system. This will + set both *pzlog and *pzpass to a string allocated by malloc, or to + NULL if the value is not found. If neither value is found, the + function will return UUCONF_NOT_FOUND. */ +extern int uuconf_callout (void *uuconf_pglobal, + const struct uuconf_system *uuconf_qsys, + char **uuconf_pzlog, + char **uuconf_pzpass); + +/* See if a login name is permitted for a system. This will return + UUCONF_SUCCESS if it is permitted or UUCONF_NOT_FOUND if it is + invalid. This simply calls uuconf_taylor_validate or returns + UUCONF_SUCCESS, depending on the value of HAVE_TAYLOR_CONFIG. */ +extern int uuconf_validate (void *uuconf_pglobal, + const struct uuconf_system *uuconf_qsys, + const char *uuconf_zlogin); + +/* Get the name of the HDB remote.unknown shell script, if using + HAVE_HDB_CONFIG. This does not actually run the shell script. If + the function returns UUCONF_SUCCESS, the name will be in *pzname, + which will point to an malloced buffer. If it returns + UUCONF_NOT_FOUND, then there is no script to run. */ +extern int uuconf_remote_unknown (void *uuconf_pglobal, + char **pzname); + +/* Translate a dial code. This sets *pznum to an malloced string. + This will look up the entire zdial string in the dialcode file, so + for normal use the alphabetic prefix should be separated. */ +extern int uuconf_dialcode (void *uuconf_pglobal, + const char *uuconf_zdial, + char **uuconf_pznum); + +/* Compare two grades, returning < 0 if b1 should be executed before + b2, == 0 if they are the same, or > 0 if b1 should be executed + after b2. This can not fail, and does not return a standard uuconf + error code; it is normally called via the macro UUCONF_GRADE_CMP, + defined above. */ +extern int uuconf_grade_cmp (int uuconf_b1, int uuconf_b2); + +#else /* ! UUCONF_ANSI_C */ + +extern int uuconf_init (); +extern int uuconf_init_thread (); +extern int uuconf_system_names (); +extern int uuconf_system_info (); +extern int uuconf_system_unknown (); +extern int uuconf_system_local (); +extern int uuconf_system_free (); +extern int uuconf_find_port (); +extern int uuconf_port_free (); +extern int uuconf_dialer_names (); +extern int uuconf_dialer_info (); +extern int uuconf_dialer_free (); +extern int uuconf_localname (); +extern int uuconf_login_localname (); +extern int uuconf_spooldir (); +extern int uuconf_lockdir (); +extern int uuconf_pubdir (); +extern int uuconf_logfile (); +extern int uuconf_statsfile (); +extern int uuconf_debugfile (); +extern int uuconf_debuglevel (); +extern int uuconf_maxuuxqts (); +extern int uuconf_callin (); +extern int uuconf_callout (); +extern int uuconf_remote_unknown (); +extern int uuconf_validate (); +extern int uuconf_grade_cmp (); + +#ifdef __OPTIMIZE__ +#define uuconf_system_free(qglob, q) \ + (uuconf_free_block ((q)->uuconf_palloc), UUCONF_SUCCESS) +#define uuconf_port_free(qglob, q) \ + (uuconf_free_block ((q)->uuconf_palloc), UUCONF_SUCCESS) +#define uuconf_dialer_free(qglob, q) \ + (uuconf_free_block ((q)->uuconf_palloc), UUCONF_SUCCESS) +#endif + +#endif /* ! UUCONF_ANSI_C */ + +#if UUCONF_ANSI_C + +/* Initialize the Taylor UUCP configuration file reading routines. + This must be called before calling any of the Taylor UUCP + configuration file specific routines. The ppglobal argument should + point to a generic pointer. Moreover, before calling this function + the pointer either must be set to NULL, or must have been passed to + one of the other uuconf init routines. The zprogram argument is + the name of the program for which files should be read. If NULL, + it is taken as "uucp", which means to read the standard UUCP files. + The zname argument is the name of the config file. If it is NULL, + the default config file will be used. + + Note that if the zname argument is obtained from the user running + the program, the program should be careful to revoke any special + privileges it may have (e.g. on Unix call setuid (getuid ()) and + setgid (getgid ())). Otherwise various sorts of spoofing become + possible. */ +extern int uuconf_taylor_init (void **uuconf_pglobal, + const char *uuconf_zprogram, + const char *uuconf_zname); + +/* Get the names of all systems listed in the Taylor UUCP + configuration files. This sets *ppzsystems to point to an array of + system names. The list of names is NULL terminated. The array is + allocated using malloc, as is each element of the array. If the + falias argument is 0, the list will not include any aliases; + otherwise, it will. */ +extern int uuconf_taylor_system_names (void *uuconf_pglobal, + char ***uuconf_ppzsystems, + int uuconf_falias); + +/* Get the information for system zsystem from the Taylor UUCP + configuration files. This will set *qsys. */ +extern int uuconf_taylor_system_info (void *uuconf_pglobal, + const char *uuconf_zsystem, + struct uuconf_system *uuconf_qsys); + +/* Get information for an unknown (anonymous) system. This returns + the values set by the ``unknown'' command in the main configuration + file. If the ``unknown'' command was not used, this will return + UUCONF_NOT_FOUND. */ +extern int uuconf_taylor_system_unknown (void *uuconf_pglobal, + struct uuconf_system *uuconf_qsys); + +/* Find a port from the Taylor UUCP configuration files. The + arguments and return values are identical to those of + uuconf_find_port. */ +extern int uuconf_taylor_find_port (void *uuconf_pglobal, + const char *uuconf_zname, + long uuconf_ibaud, + long uuconf_ihighbaud, + int (*uuconf_pifn) (struct uuconf_port *, + void *uuconf_pinfo), + void *uuconf_pinfo, + struct uuconf_port *uuconf_qport); + +/* Get the names of all dialers listed in the Taylor UUCP + configuration files. This sets *ppzdialers to point to an array of + dialer names. The list of names is NULL terminated. The array is + allocated using malloc, as is each element of the array. */ +extern int uuconf_taylor_dialer_names (void *uuconf_pglobal, + char ***uuconf_ppzdialers); + +/* Get the information for the dialer zdialer from the Taylor UUCP + configuration files. This sets the fields in *qdialer. */ +extern int uuconf_taylor_dialer_info (void *uuconf_pglobal, + const char *uuconf_zdialer, + struct uuconf_dialer *uuconf_qdialer); + +/* Get the local node name that should be used, given a login name, + considering only the ``myname'' command in the Taylor UUCP + configuration files. If the function returns UUCONF_SUCCESS, + *pzname will point to an malloced buffer. */ +extern int uuconf_taylor_login_localname (void *uuconf_pglobal, + const char *uuconf_zlogin, + char **pzname); + +/* Get the callout login name and password for a system from the + Taylor UUCP configuration files. This will set both *pzlog and + *pzpass to a string allocated by malloc, or to NULL if the value is + not found. If neither value is found, the function will return + UUCONF_NOT_FOUND. */ +extern int uuconf_taylor_callout (void *uuconf_pglobal, + const struct uuconf_system *uuconf_qsys, + char **uuconf_pzlog, + char **uuconf_pzpass); + +/* See if a login name is permitted for a system. This will return + UUCONF_SUCCESS if it is permitted or UUCONF_NOT_FOUND if it is + invalid. This checks whether the login name appears in a + called-login command with a list of system which does not include + the system qsys. */ +extern int uuconf_taylor_validate (void *uuconf_pglobal, + const struct uuconf_system *uuconf_qsys, + const char *uuconf_zlogin); + +#else /* ! UUCONF_ANSI_C */ + +extern int uuconf_taylor_init (); +extern int uuconf_taylor_system_names (); +extern int uuconf_taylor_system_info (); +extern int uuconf_taylor_system_unknown (); +extern int uuconf_taylor_find_port (); +extern int uuconf_taylor_dialer_names (); +extern int uuconf_taylor_dialer_info (); +extern int uuconf_taylor_login_localname (); +extern int uuconf_taylor_callout (); +extern int uuconf_taylor_validate (); + +#endif /* ! UUCONF_ANSI_C */ + +#if UUCONF_ANSI_C + +/* Initialize the V2 configuration file reading routines. This must + be called before any of the other V2 routines are called. The + ppglobal argument should point to a generic pointer. Moreover, + before calling this function the pointer either must be set to + NULL, or must have been passed to one of the other uuconf init + routines. */ +extern int uuconf_v2_init (void **uuconf_ppglobal); + +/* Get the names of all systems listed in the V2 configuration files. + This sets *ppzsystems to point to an array of system names. The + list of names is NULL terminated. The array is allocated using + malloc, as is each element of the array. If the falias argument is + 0, the list will not include any aliases; otherwise, it will. */ +extern int uuconf_v2_system_names (void *uuconf_pglobal, + char ***uuconf_ppzsystems, + int uuconf_falias); + +/* Get the information for system zsystem from the V2 configuration + files. This will set *qsys. */ +extern int uuconf_v2_system_info (void *uuconf_pglobal, + const char *uuconf_zsystem, + struct uuconf_system *uuconf_qsys); + +/* Find a port from the V2 configuration files. The arguments and + return values are identical to those of uuconf_find_port. */ +extern int uuconf_v2_find_port (void *uuconf_pglobal, + const char *uuconf_zname, + long uuconf_ibaud, + long uuconf_ihighbaud, + int (*uuconf_pifn) (struct uuconf_port *, + void *uuconf_pinfo), + void *uuconf_pinfo, + struct uuconf_port *uuconf_qport); + +#else /* ! UUCONF_ANSI_C */ + +extern int uuconf_v2_init (); +extern int uuconf_v2_system_names (); +extern int uuconf_v2_system_info (); +extern int uuconf_v2_find_port (); + +#endif /* ! UUCONF_ANSI_C */ + +#if UUCONF_ANSI_C + +/* Initialize the HDB configuration file reading routines. This + should be called before any of the other HDB routines are called. + The ppglobal argument should point to a generic pointer. Moreover, + before calling this function the pointer either must be set to + NULL, or must have been passed to one of the other uuconf init + routines. The zprogram argument is used to match against a + "services" string in Sysfiles. A NULL or "uucp" argument is taken + as "uucico". */ +extern int uuconf_hdb_init (void **uuconf_ppglobal, + const char *uuconf_zprogram); + +/* Get the names of all systems listed in the HDB configuration files. + This sets *ppzsystems to point to an array of system names. The + list of names is NULL terminated. The array is allocated using + malloc, as is each element of the array. If the falias argument is + 0, the list will not include any aliases; otherwise, it will (an + alias is created by using the ALIAS= keyword in the Permissions + file). */ +extern int uuconf_hdb_system_names (void *uuconf_pglobal, + char ***uuconf_ppzsystems, + int uuconf_falias); + +/* Get the information for system zsystem from the HDB configuration + files. This will set *qsys. */ +extern int uuconf_hdb_system_info (void *uuconf_pglobal, + const char *uuconf_zsystem, + struct uuconf_system *uuconf_qsys); + + +/* Get information for an unknown (anonymous) system. If no + information is available for unknown systems, this will return + UUCONF_NOT_FOUND. This does not run the remote.unknown shell + script. */ +extern int uuconf_hdb_system_unknown (void *uuconf_pglobal, + struct uuconf_system *uuconf_qsys); + +/* Find a port from the HDB configuration files. The arguments and + return values are identical to those of uuconf_find_port. */ +extern int uuconf_hdb_find_port (void *uuconf_pglobal, + const char *uuconf_zname, + long uuconf_ibaud, + long uuconf_ihighbaud, + int (*uuconf_pifn) (struct uuconf_port *, + void *uuconf_pinfo), + void *uuconf_pinfo, + struct uuconf_port *uuconf_qport); + +/* Get the names of all dialers listed in the HDB configuration files. + This sets *ppzdialers to point to an array of dialer names. The + list of names is NULL terminated. The array is allocated using + malloc, as is each element of the array. */ +extern int uuconf_hdb_dialer_names (void *uuconf_pglobal, + char ***uuconf_ppzdialers); + +/* Get the information for the dialer zdialer from the HDB + configuration files. This sets the fields in *qdialer. */ +extern int uuconf_hdb_dialer_info (void *uuconf_pglobal, + const char *uuconf_zdialer, + struct uuconf_dialer *uuconf_qdialer); + +/* Get the local node name that should be used, given a login name, + considering only the MYNAME field in the HDB Permissions file. If + the function returns UUCONF_SUCCESS, *pzname will point to an + malloced buffer. */ +extern int uuconf_hdb_login_localname (void *uuconf_pglobal, + const char *uuconf_zlogin, + char **pzname); + +/* Get the name of the HDB remote.unknown shell script. This does not + actually run the shell script. If the function returns + UUCONF_SUCCESS, the name will be in *pzname, which will point to an + malloced buffer. */ +extern int uuconf_hdb_remote_unknown (void *uuconf_pglobal, + char **pzname); + +#else /* ! UUCONF_ANSI_C */ + +extern int uuconf_hdb_init (); +extern int uuconf_hdb_system_names (); +extern int uuconf_hdb_system_info (); +extern int uuconf_hdb_system_unknown (); +extern int uuconf_hdb_find_port (); +extern int uuconf_hdb_dialer_names (); +extern int uuconf_hdb_dialer_info (); +extern int uuconf_hdb_localname (); +extern int uuconf_hdb_remote_unknown (); + +#endif /* ! UUCONF_ANSI_C */ + +#if UUCONF_ANSI_C + +/* This function will set an appropriate error message into the buffer + zbuf, given a uuconf error code. The buffer will always be null + terminated, and will never be accessed beyond the length cbuf. + This function will return the number of characters needed for the + complete message, including the null byte. If this is less than + the cbytes argument, the buffer holds a truncated string. */ +extern int uuconf_error_string (void *uuconf_pglobal, int ierror, + char *zbuf, UUCONF_SIZE_T cbuf); + +/* If UUCONF_ERROR_ERRNO is set in a return value, this function may + be used to retrieve the errno value. This will be the value of + errno as set by the system function which failed. However, some + system functions, notably some stdio routines, may not set errno, + in which case the value will be meaningless. This function does + not return a uuconf error code, and it cannot fail. */ +extern int uuconf_error_errno (void *uuconf_pglobal); + +/* If UUCONF_ERROR_FILENAME is set in a return value, this function + may be used to retrieve the file name. This function does not + return a uuconf error code, and it cannot fail. The string that it + returns a pointer to is not guaranteed to remain allocated across + the next call to a uuconf function (other than one of the three + error retrieving functions). */ +extern const char *uuconf_error_filename (void *uuconf_pglobal); + +/* If UUCONF_ERROR_LINENO is set in a return value, this function may + be used to retrieve the line number. This function does not return + a uuconf error code, and it cannot fail. */ +extern int uuconf_error_lineno (void *uuconf_pglobal); + +#else /* ! UUCONF_ANSI_C */ + +extern int uuconf_error_string (); +extern int uuconf_error_errno (); +extern UUCONF_CONST char *uuconf_error_filename (); +extern int uuconf_error_lineno (); + +#endif /* ! UUCONF_ANSI_C */ + +/* The uuconf package also provides a few functions which can accept + commands and parcel them out according to a table. These are + publically visible, partially in the hopes that they will be + useful, but mostly because the rest of the Taylor UUCP package uses + them. */ + +/* The types of entries allowed in a command table (struct + uuconf_cmdtab). Each type defines how a particular command is + interpreted. Each type will either assign a value to a variable or + call a function. In all cases, a line of input is parsed into + separate fields, separated by whitespace; comments beginning with + '#' are discarded, except that a '#' preceeded by a backslash is + retained. The first field is taken as the command to execute, and + the remaining fields are its arguments. */ + +/* A boolean value. Used for a command which accepts a single + argument, which must begin with 'y', 'Y', 't', or 'T' for true (1) + or 'n', 'N', 'f', or 'F' for false (0). The corresponding variable + must be an int. */ +#define UUCONF_CMDTABTYPE_BOOLEAN (0x12) + +/* An integer value. Used for a command which accepts a single + argument, which must be an integer. The corresponding variable + must be an int. */ +#define UUCONF_CMDTABTYPE_INT (0x22) + +/* A long value. Used for a command which accepts a single value, + which must be an integer. The corresponding variable must be a + long. */ +#define UUCONF_CMDTABTYPE_LONG (0x32) + +/* A string value. Used for a command which accepts a string + argument. If there is no argument, the variable will be set to + point to a zero byte. Otherwise the variable will be set to point + to the string. The corresponding variable must be a char *. The + memory pointed to by the variable after it is set must not be + modified. */ +#define UUCONF_CMDTABTYPE_STRING (0x40) + +/* A full string value. Used for a command which accepts a series of + string arguments separated by whitespace. The corresponding + variable must be a char **. It will be set to an NULL terminated + array of the arguments. The memory occupied by the array itself, + and by the strings within it, must not be modified. */ +#define UUCONF_CMDTABTYPE_FULLSTRING (0x50) + +/* A function. If this command is encountered, the command and its + arguments are passed to the corresponding function. They are + passed as an array of strings, in which the first string is the + command itself, along with a count of strings. This value may be + or'red with a specific number of required arguments; + UUCONF_CMDTABTYPE_FN | 1 accepts no additional arguments besides + the command itself, UUCONF_CMDTABTYPE_FN | 2 accepts 1 argument, + etc. UUCONF_CMDTABTYPE_FN | 0, accepts any number of additional + arguments. */ +#define UUCONF_CMDTABTYPE_FN (0x60) + +/* A prefix function. The string in the table is a prefix; if a + command is encountered with the same prefix, the corresponding + function will be called as for UUCONF_CMDTABTYPE_FN. The number of + arguments may be or'red in as with UUCONF_CMDTABTYPE_FN. */ +#define UUCONF_CMDTABTYPE_PREFIX (0x70) + +/* This macro will return the particular type of a CMDTABTYPE. */ +#define UUCONF_TTYPE_CMDTABTYPE(i) ((i) & 0x70) + +/* This macro will return the required number of arguments of a + CMDTABTYPE. If it is zero, there is no restriction. */ +#define UUCONF_CARGS_CMDTABTYPE(i) ((i) & 0x0f) + +/* When a function is called via UUCONF_CMDTABTYPE_FN or + UUCONF_CMDTABTYPE_PREFIX, it may return any uuconf error code (see + above). However, it will normally return one of the following: + + UUCONF_CMDTABRET_CONTINUE: Take no special action. In particular, + the arguments passed to the function may be overwritten or freed. + + UUCONF_CMDTABRET_KEEP: The memory occupied by the arguments passed + to the function must be preserved. Continue processing commands. + + UUCONF_CMDTABRET_EXIT: If reading commands from a file, stop + processing. The arguments passed to the function may be + overwritten or freed. + + UUCONF_CMDTABRET_KEEP_AND_EXIT: Stop processing any file. The + memory occupied by the arguments passed to the function must be + preserved. + + These values are interpreted by uuconf_cmd_file. The + uuconf_cmd_line and uuconf_cmd_args functions may return + UUCONF_CMDTABRET_KEEP. It they get an error, they will return an + error code with UUCONF_CMDTABRET_EXIT set. Also, of course, they + may return any value that is returned by one of the user functions + in the uuconf_cmdtab table. */ + +/* UUCONF_CMDTABRET_KEEP and UUCONF_CMDTABRET_EXIT are defined above, + with the error codes. */ + +#define UUCONF_CMDTABRET_CONTINUE UUCONF_SUCCESS +#define UUCONF_CMDTABRET_KEEP_AND_EXIT \ + (UUCONF_CMDTABRET_KEEP | UUCONF_CMDTABRET_EXIT) + +/* When a function is called via CMDTABTYPE_FN or CMDTABTYPE_PREFIX, + it is passed five arguments. This is the type of a pointer to such + a function. The uuconf global information structure is passed in + for convenience in calling another uuconf function. The arguments + to the command are passed in (the command itself is the first + argument) along with a count and the value of the pvar field from + the uuconf_cmdtab structure in which the function pointer was + found. The pinfo argument to the function is taken from the + argument to uuconf_cmd_*. */ + +#if UUCONF_ANSI_C +typedef int (*uuconf_cmdtabfn) (void *uuconf_pglobal, + int uuconf_argc, + char **uuconf_argv, + void *uuconf_pvar, + void *uuconf_pinfo); +#else +typedef int (*uuconf_cmdtabfn) (); +#endif + +/* A table of commands is an array of the following structures. The + final element of the table should have uuconf_zcmd == NULL. */ + +struct uuconf_cmdtab +{ + /* Command name. */ + UUCONF_CONST char *uuconf_zcmd; + /* Command type (one of CMDTABTYPE_*). */ + int uuconf_itype; + /* If not CMDTABTYPE_FN or CMDTABTYPE_PREFIX, the address of the + associated variable. Otherwise, a pointer value to pass to the + function pifn. */ + UUCONF_POINTER uuconf_pvar; + /* The function to call if CMDTABTYPE_FN or CMDTABTYPE_PREFIX. */ + uuconf_cmdtabfn uuconf_pifn; +}; + +/* Bit flags to pass to uuconf_processcmds. */ + +/* If set, case is significant when checking commands. Normally case + is ignored. */ +#define UUCONF_CMDTABFLAG_CASE (0x1) + +/* If set, a backslash at the end of a line may be used to include the + next physical line in the logical line. */ +#define UUCONF_CMDTABFLAG_BACKSLASH (0x2) + +#if UUCONF_ANSI_C + +/* Read commands from a file, look them up in a table, and take the + appropriate action. This continues reading lines from the file + until EOF, or until a function returns with UUCONF_CMDTABRET_EXIT + set, or until an error occurs. The qtab argument must point to a + table of struct uuconf_cmdtab; the last element in the table should + have uuconf_zcmd == NULL. When a UUCONF_CMDTABTYPE_FN or + UUCONF_CMDTABTYPE_PREFIX command is found, the pinfo argument will + be passed to the called function. If an a command is found that is + not in the table, then if pfiunknownfn is NULL the unknown command + is ignored; otherwise it is passed to pfiunknownfn, which should + return a uuconf return code which is handled as for any other + function (the pvar argument to pfiunknownfn will always be NULL). + The iflags argument is any combination of the above + UUCONF_CMDTABFLAG bits. The pblock argument may also be a memory + block, as returned by uuconf_malloc_block (described below), in + which case all memory preserved because of UUCONF_CMDTABRET_KEEP + will be added to the block so that it may be freed later; it may + also be NULL, in which case any such memory is permanently lost. + + This function initially sets the internal line number to 0, and + then increments it as each line is read. It is permitted for any + called function to use the uuconf_lineno function to obtain it. If + this function is called when not at the start of a file, the value + returned by uuconf_lineno (which is, in any case, only valid if an + error code with UUCONF_ERROR_LINENO set is returned) must be + adjusted by the caller. + + This returns a normal uuconf return value, as described above. */ +extern int uuconf_cmd_file (void *uuconf_pglobal, + FILE *uuconf_e, + const struct uuconf_cmdtab *uuconf_qtab, + void *uuconf_pinfo, + uuconf_cmdtabfn uuconf_pfiunknownfn, + int uuconf_iflags, + void *pblock); + +/* This utility function is just like uuconf_cmd_file, except that it + only operates on a single string. If a function is called via + qtab, its return value will be the return value of this function. + UUCONF_CMDTABFLAG_BACKSLASH is ignored in iflags. The string z is + modified in place. The return value may include the + UUCONF_CMDTABRET_KEEP and, on error, the UUCONF_CMDTABRET_EXIT + bits, which should be honored by the calling code. */ +extern int uuconf_cmd_line (void *uuconf_pglobal, + char *uuconf_z, + const struct uuconf_cmdtab *uuconf_qtab, + void *uuconf_pinfo, + uuconf_cmdtabfn uuconf_pfiunknownfn, + int uuconf_iflags, + void *pblock); + +/* This utility function is just like uuconf_cmd_line, except it is + given a list of already parsed arguments. */ +extern int uuconf_cmd_args (void *uuconf_pglobal, + int uuconf_cargs, + char **uuconf_pzargs, + const struct uuconf_cmdtab *uuconf_qtab, + void *uuconf_pinfo, + uuconf_cmdtabfn uuconf_pfiunknownfn, + int uuconf_iflags, + void *pblock); + +#else /* ! UUCONF_ANSI_C */ + +extern int uuconf_cmd_file (); +extern int uuconf_cmd_line (); +extern int uuconf_cmd_args (); + +#endif /* ! UUCONF_ANSI_C */ + +#if UUCONF_ANSI_C + +/* The uuconf_cmd_file function may allocate memory permanently, as + for setting a UUCONF_CMDTABTYPE_STRING value, in ways which are + difficult to free up. A memory block may be used to record all + allocated memory, so that it can all be freed up at once at some + later time. These functions do not take a uuconf global pointer, + and are independent of the rest of the uuconf library. */ + +/* Allocate a block of memory. If this returns NULL, then malloc + returned NULL, and errno is whatever malloc set it to. */ +extern void *uuconf_malloc_block (void); + +/* Allocate memory within a memory block. If this returns NULL, then + malloc returned NULL, and errno is whatever malloc set it to. */ +extern void *uuconf_malloc (void *uuconf_pblock, + UUCONF_SIZE_T uuconf_cbytes); + +/* Add a block returned by the generic malloc routine to a memory + block. This returns zero on success, non-zero on failure. If this + fails (returns non-zero), then malloc returned NULL, and errno is + whatever malloc set it to. */ +extern int uuconf_add_block (void *uuconf_pblock, void *uuconf_padd); + +/* Free a value returned by uuconf_malloc from a memory block. In the + current implementation, this will normally not do anything, but it + doesn't hurt. No errors can occur. */ +extern void uuconf_free (void *uuconf_pblock, void *uuconf_pfree); + +/* Free an entire memory block, including all values returned by + uuconf_malloc from it and all values added to it with + uuconf_add_block. No errors can occur. */ +extern void uuconf_free_block (void *uuconf_pblock); + +#else /* ! UUCONF_ANSI_C */ + +extern UUCONF_POINTER uuconf_malloc_block (); +extern UUCONF_POINTER uuconf_malloc (); +extern int uuconf_add_block (); +extern /* void */ uuconf_free (); +extern /* void */ uuconf_free_block (); + +#endif /* ! UUCONF_ANSI_C */ + +#endif /* ! defined (UUCONF_H) */ diff --git a/gnu/libexec/uucp/common_sources/uucp.h b/gnu/libexec/uucp/common_sources/uucp.h new file mode 100644 index 0000000000..8df3ec4c3e --- /dev/null +++ b/gnu/libexec/uucp/common_sources/uucp.h @@ -0,0 +1,367 @@ +/* uucp.h + Header file for the UUCP package. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +/* Get the system configuration parameters. */ +#include "conf.h" +#include "policy.h" + +/* Get a definition for ANSI_C if we weren't given one. */ +#ifndef ANSI_C +#ifdef __STDC__ +#define ANSI_C 1 +#else /* ! defined (__STDC__) */ +#define ANSI_C 0 +#endif /* ! defined (__STDC__) */ +#endif /* ! defined (ANSI_C) */ + +/* Pass this definition into uuconf.h. */ +#define UUCONF_ANSI_C ANSI_C + +/* We always include some standard header files. We need + to define sig_atomic_t. */ +#if HAVE_STDDEF_H +#include +#endif +#include +#include + +/* On some systems we need to get sig_atomic_t or + size_t or time_t. */ +#if ! HAVE_SIG_ATOMIC_T_IN_SIGNAL_H && HAVE_SIG_ATOMIC_T_IN_TYPES_H +#define USE_TYPES_H 1 +#else +#if ! HAVE_SIZE_T_IN_STDDEF_H && HAVE_SIZE_T_IN_TYPES_H +#define USE_TYPES_H 1 +#else +#if ! HAVE_TIME_T_IN_TIME_H && HAVE_TIME_T_IN_TYPES_H +#define USE_TYPES_H 1 +#endif +#endif +#endif + +#ifndef USE_TYPES_H +#define USE_TYPES_H 0 +#endif + +#if USE_TYPES_H +#include +#endif + +/* Make sure we have sig_atomic_t. */ +#if ! HAVE_SIG_ATOMIC_T_IN_SIGNAL_H && ! HAVE_SIG_ATOMIC_T_IN_TYPES_H +#ifndef SIG_ATOMIC_T +/* There is no portable definition for sig_atomic_t. */ +#define SIG_ATOMIC_T char +#endif /* ! defined (SIG_ATOMIC_T) */ +typedef SIG_ATOMIC_T sig_atomic_t; +#endif /* ! HAVE_SIG_ATOMIC_T_IN_SIGNAL_H && ! HAVE_SIG_ATOMIC_T_IN_TYPES_H */ + +/* Make sure we have size_t. We use int as the default because the + main use of this type is to provide an argument to malloc and + realloc. On a system which does not define size_t, int is + certainly the correct type to use. */ +#if ! HAVE_SIZE_T_IN_STDDEF_H && ! HAVE_SIZE_T_IN_TYPES_H +#ifndef SIZE_T +#define SIZE_T unsigned +#endif /* ! defined (SIZE_T) */ +typedef SIZE_T size_t; +#endif /* ! HAVE_SIZE_T_IN_STDDEF_H && ! HAVE_SIZE_T_IN_TYPES_H */ + +/* Make sure we have time_t. We use long as the default. We don't + bother to let conf.h override this, since on a system which doesn't + define time_t long must be correct. */ +#if ! HAVE_TIME_T_IN_TIME_H && ! HAVE_TIME_T_IN_TYPES_H +typedef long time_t; +#endif + +/* Set up some definitions for both ANSI C and Classic C. + + P() -- for function prototypes (e.g. extern int foo P((int)) ). + pointer -- for a generic pointer (i.e. void *). + constpointer -- for a generic pointer to constant data. + BUCHAR -- to convert a character to unsigned. */ +#if ANSI_C +#if ! HAVE_VOID || ! HAVE_UNSIGNED_CHAR + #error ANSI C compiler without void or unsigned char +#endif +#define P(x) x +typedef void *pointer; +typedef const void *constpointer; +#define BUCHAR(b) ((unsigned char) (b)) +#else /* ! ANSI_C */ +/* Handle uses of const, volatile and void in Classic C. */ +#define const +#define volatile +#if ! HAVE_VOID +#define void int +#endif +#define P(x) () +typedef char *pointer; +typedef const char *constpointer; +#if HAVE_UNSIGNED_CHAR +#define BUCHAR(b) ((unsigned char) (b)) +#else /* ! HAVE_UNSIGNED_CHAR */ +/* This should work on most systems, but not necessarily all. */ +#define BUCHAR(b) ((b) & 0xff) +#endif /* ! HAVE_UNSIGNED_CHAR */ +#endif /* ! ANSI_C */ + +/* Make sure we have a definition for offsetof. */ +#ifndef offsetof +#define offsetof(type, field) \ + ((size_t) ((char *) &(((type *) 0)->field) - (char *) (type *) 0)) +#endif + +/* Only use inline with gcc. */ +#ifndef __GNUC__ +#define __inline__ +#endif + +/* Get the string functions, which are used throughout the code. */ +#if HAVE_MEMORY_H +#include +#else +/* We really need a definition for memchr, and this should not + conflict with anything in . I hope. */ +extern pointer memchr (); +#endif + +#if HAVE_STRING_H +#include +#else /* ! HAVE_STRING_H */ +#if HAVE_STRINGS_H +#include +#else /* ! HAVE_STRINGS_H */ +extern char *strcpy (), *strncpy (), *strchr (), *strrchr (), *strtok (); +extern char *strcat (), *strerror (), *strstr (); +extern size_t strlen (), strspn (), strcspn (); +#if ! HAVE_MEMORY_H +extern pointer memcpy (), memchr (); +#endif /* ! HAVE_MEMORY_H */ +#endif /* ! HAVE_STRINGS_H */ +#endif /* ! HAVE_STRING_H */ + +/* Get what we need from . */ +#if HAVE_STDLIB_H +#include +#else /* ! HAVE_STDLIB_H */ +extern pointer malloc (), realloc (), bsearch (); +extern long strtol (); +extern char *getenv (); +#endif /* ! HAVE_STDLIB_H */ + +/* NeXT uses to declare a bunch of functions. */ +#if HAVE_LIBC_H +#include +#endif + +/* Make sure we have the EXIT_ macros. */ +#ifndef EXIT_SUCCESS +#define EXIT_SUCCESS (0) +#endif +#ifndef EXIT_FAILURE +#define EXIT_FAILURE (1) +#endif + +/* If we need to declare errno, do so. I don't want to always do + this, because some system might theoretically have a different + declaration for errno. On a POSIX system this is sure to work. */ +#if ! HAVE_ERRNO_DECLARATION +extern int errno; +#endif + +/* If the system has the socket call, guess that we can compile the + TCP code. */ +#define HAVE_TCP HAVE_SOCKET + +/* If the system has the t_open call, guess that we can compile the + TLI code. */ +#define HAVE_TLI HAVE_T_OPEN + +/* The boolean type holds boolean values. */ +typedef int boolean; +#undef TRUE +#undef FALSE +#define TRUE (1) +#define FALSE (0) + +/* The openfile_t type holds an open file. This depends on whether we + are using stdio or not. */ +#if USE_STDIO + +typedef FILE *openfile_t; +#define EFILECLOSED ((FILE *) NULL) +#define ffileisopen(e) ((e) != NULL) +#define ffileeof(e) feof (e) +#define cfileread(e, z, c) fread ((z), 1, (c), (e)) +#define ffilereaderror(e, c) ferror (e) +#define cfilewrite(e, z, c) fwrite ((z), 1, (c), (e)) +#ifdef SEEK_SET +#define ffileseek(e, i) (fseek ((e), (long) (i), SEEK_SET) == 0) +#define ffilerewind(e) (fseek ((e), (long) 0, SEEK_SET) == 0) +#else +#define ffileseek(e, i) (fseek ((e), (long) (i), 0) == 0) +#define ffilerewind(e) (fseek ((e), (long) 0, 0) == 0) +#endif +#define ffileclose(e) (fclose (e) == 0) + +#else /* ! USE_STDIO */ + +#if HAVE_UNISTD_H +#include +#endif + +typedef int openfile_t; +#define EFILECLOSED (-1) +#define ffileisopen(e) ((e) >= 0) +#define ffileeof(e) (FALSE) +#define cfileread(e, z, c) read ((e), (z), (c)) +#define ffilereaderror(e, c) ((c) < 0) +#define cfilewrite(e, z, c) write ((e), (z), (c)) +#ifdef SEEK_SET +#define ffileseek(e, i) (lseek ((e), (long) i, SEEK_SET) >= 0) +#define ffilerewind(e) (lseek ((e), (long) 0, SEEK_SET) >= 0) +#else +#define ffileseek(e, i) (lseek ((e), (long) i, 0) >= 0) +#define ffilerewind(e) (lseek ((e), (long) 0, 0) >= 0) +#endif +#define ffileclose(e) (close (e) >= 0) + +#endif /* ! USE_STDIO */ + +/* A prototype for main to avoid warnings from gcc 2.0 + -Wmissing-prototype option. */ +extern int main P((int argc, char **argv)); + +/* Some standard routines which we only define if they are not present + on the system we are compiling on. */ + +#if ! HAVE_GETLINE +/* Read a line from a file. */ +extern int getline P((char **pz, size_t *pc, FILE *e)); +#endif + +#if ! HAVE_REMOVE +/* Erase a file. */ +#undef remove +extern int remove P((const char *zfile)); +#endif + +#if ! HAVE_STRDUP +/* Copy a string into memory. */ +extern char *strdup P((const char *z)); +#endif + +#if ! HAVE_STRSTR +/* Look for one string within another. */ +extern char *strstr P((const char *zouter, const char *zinner)); +#endif + +#if ! HAVE_STRCASECMP +#if HAVE_STRICMP +#define strcasecmp stricmp +#else /* ! HAVE_STRICMP */ +/* Rename strcasecmp to avoid ANSI C name space. */ +#define strcasecmp xstrcasecmp +extern int strcasecmp P((const char *z1, const char *z2)); +#endif /* ! HAVE_STRICMP */ +#endif /* ! HAVE_STRCASECMP */ + +#if ! HAVE_STRNCASECMP +#if HAVE_STRNICMP +#define strncasecmp strnicmp +#else /* ! HAVE_STRNICMP */ +/* Rename strncasecmp to avoid ANSI C name space. */ +#define strncasecmp xstrncasecmp +extern int strncasecmp P((const char *z1, const char *z2, size_t clen)); +#endif /* ! HAVE_STRNICMP */ +#endif /* ! HAVE_STRNCASECMP */ + +#if ! HAVE_STRERROR +/* Get a string corresponding to an error message. */ +#undef strerror +extern char *strerror P((int ierr)); +#endif + +/* Get the appropriate definitions for memcmp, memcpy, memchr and + bzero. */ +#if ! HAVE_MEMCMP +#if HAVE_BCMP +#define memcmp(p1, p2, c) bcmp ((p1), (p2), (c)) +#else /* ! HAVE_BCMP */ +extern int memcmp P((constpointer p1, constpointer p2, size_t c)); +#endif /* ! HAVE_BCMP */ +#endif /* ! HAVE_MEMCMP */ + +#if ! HAVE_MEMCPY +#if HAVE_BCOPY +#define memcpy(pto, pfrom, c) bcopy ((pfrom), (pto), (c)) +#else /* ! HAVE_BCOPY */ +extern pointer memcpy P((pointer pto, constpointer pfrom, size_t c)); +#endif /* ! HAVE_BCOPY */ +#endif /* ! HAVE_MEMCPY */ + +#if ! HAVE_MEMCHR +extern pointer memchr P((constpointer p, int b, size_t c)); +#endif + +#if ! HAVE_BZERO +#if HAVE_MEMSET +#define bzero(p, c) memset ((p), 0, (c)) +#else /* ! HAVE_MEMSET */ +extern void bzero P((pointer p, int c)); +#endif /* ! HAVE_MEMSET */ +#endif /* ! HAVE_BZERO */ + +/* Look up a character in a string. */ +#if ! HAVE_STRCHR +#if HAVE_INDEX +#define strchr index +extern char *index (); +#else /* ! HAVE_INDEX */ +extern char *strchr P((const char *z, int b)); +#endif /* ! HAVE_INDEX */ +#endif /* ! HAVE_STRCHR */ + +#if ! HAVE_STRRCHR +#if HAVE_RINDEX +#define strrchr rindex +extern char *rindex (); +#else /* ! HAVE_RINDEX */ +extern char *strrchr P((const char *z, int b)); +#endif /* ! HAVE_RINDEX */ +#endif /* ! HAVE_STRRCHR */ + +/* Turn a string into a long integer. */ +#if ! HAVE_STRTOL +extern long strtol P((const char *, char **, int)); +#endif + +/* Lookup a key in a sorted array. */ +#if ! HAVE_BSEARCH +extern pointer bsearch P((constpointer pkey, constpointer parray, + size_t celes, size_t cbytes, + int (*pficmp) P((constpointer, constpointer)))); +#endif diff --git a/gnu/libexec/uucp/common_sources/uudefs.h b/gnu/libexec/uucp/common_sources/uudefs.h new file mode 100644 index 0000000000..47d2c89896 --- /dev/null +++ b/gnu/libexec/uucp/common_sources/uudefs.h @@ -0,0 +1,445 @@ +/* uudefs.h + Miscellaneous definitions for the UUCP package. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#if ANSI_C +/* These structures are used in prototypes but are not defined in this + header file. */ +struct uuconf_system; +struct uuconf_timespan; +#endif + +/* The tlog enumeration holds the different types of logging. */ +enum tlog +{ + /* Normal log entry. */ + LOG_NORMAL, + /* Error log entry. */ + LOG_ERROR, + /* Fatal log entry. */ + LOG_FATAL +#if DEBUG > 1 + , + /* Debugging log entry. */ + LOG_DEBUG, + /* Start debugging log entry. */ + LOG_DEBUG_START, + /* Continue debugging log entry. */ + LOG_DEBUG_CONTINUE, + /* End debugging log entry. */ + LOG_DEBUG_END +#endif +}; + +/* The tstatus_type enumeration holds the kinds of status information + we put in the status file. The order of entries here corresponds + to the order of entries in the azStatus array. */ +enum tstatus_type +{ + /* Conversation complete. */ + STATUS_COMPLETE, + /* Port unavailable. */ + STATUS_PORT_FAILED, + /* Dial failed. */ + STATUS_DIAL_FAILED, + /* Login failed. */ + STATUS_LOGIN_FAILED, + /* Handshake failed. */ + STATUS_HANDSHAKE_FAILED, + /* Failed after logging in. */ + STATUS_FAILED, + /* Talking to remote system. */ + STATUS_TALKING, + /* Wrong time to call. */ + STATUS_WRONG_TIME, + /* Number of status values. */ + STATUS_VALUES +}; + +/* An array to convert status entries to strings. If more status entries + are added, this array must be extended. */ +extern const char *azStatus[]; + +/* The sstatus structure holds the contents of a system status file. */ +struct sstatus +{ + /* Current status of conversation. */ + enum tstatus_type ttype; + /* Number of failed retries. */ + int cretries; + /* Time of last call in seconds since epoch (determined by + ixsysdep_time). */ + long ilast; + /* Number of seconds until a retry is permitted. */ + int cwait; +}; + +/* How long we have to wait for the next call, given the number of retries + we have already made. This should probably be configurable. */ +#define CRETRY_WAIT(c) ((c) * 10 * 60) + +/* The scmd structure holds a complete UUCP command. */ +struct scmd +{ + /* Command ('S' for send, 'R' for receive, 'X' for execute, 'E' for + simple execution, 'H' for hangup, 'Y' for hangup confirm, 'N' for + hangup deny). */ + char bcmd; + /* At least one compiler needs an explicit padding byte here. */ + char bdummy; + /* Sequence handle for fsysdep_did_work. */ + pointer pseq; + /* File name to transfer from. */ + const char *zfrom; + /* File name to transfer to. */ + const char *zto; + /* User who requested transfer. */ + const char *zuser; + /* Options. */ + const char *zoptions; + /* Temporary file name ('S' and 'E'). */ + const char *ztemp; + /* Mode to give newly created file ('S' and 'E'). */ + unsigned int imode; + /* User to notify on remote system (optional; 'S' and 'E'). */ + const char *znotify; + /* File size (-1 if not supplied) ('S', 'E' and 'R'). */ + long cbytes; + /* Command to execute ('E'). */ + const char *zcmd; + /* Position to restart from ('R'). */ + long ipos; +}; + +#if DEBUG > 1 + +/* We allow independent control over several different types of + debugging output, using a bit string with individual bits dedicated + to particular debugging types. */ + +/* The bit string is stored in iDebug. */ +extern int iDebug; + +/* Debug abnormal events. */ +#define DEBUG_ABNORMAL (01) +/* Debug chat scripts. */ +#define DEBUG_CHAT (02) +/* Debug initial handshake. */ +#define DEBUG_HANDSHAKE (04) +/* Debug UUCP protocol. */ +#define DEBUG_UUCP_PROTO (010) +/* Debug protocols. */ +#define DEBUG_PROTO (020) +/* Debug port actions. */ +#define DEBUG_PORT (040) +/* Debug configuration files. */ +#define DEBUG_CONFIG (0100) +/* Debug spool directory actions. */ +#define DEBUG_SPOOLDIR (0200) +/* Debug executions. */ +#define DEBUG_EXECUTE (0400) +/* Debug incoming data. */ +#define DEBUG_INCOMING (01000) +/* Debug outgoing data. */ +#define DEBUG_OUTGOING (02000) + +/* Maximum possible value for iDebug. */ +#define DEBUG_MAX (03777) + +/* Intializer for array of debug names. The index of the name in the + array is the corresponding bit position in iDebug. We only check + for prefixes, so these names only need to be long enough to + distinguish each name from every other. The last entry must be + NULL. The string "all" is also recognized to turn on all + debugging. */ +#define DEBUG_NAMES \ + { "a", "ch", "h", "u", "pr", "po", "co", "s", "e", "i", "o", NULL } + +/* The prefix to use to turn off all debugging. */ +#define DEBUG_NONE "n" + +/* Check whether a particular type of debugging is being done. */ +#define FDEBUGGING(i) ((iDebug & (i)) != 0) + +/* These macros are used to output debugging information. I use + several different macros depending on the number of arguments + because no macro can take a variable number of arguments and I + don't want to use double parentheses. */ +#define DEBUG_MESSAGE0(i, z) \ + do { if (FDEBUGGING (i)) ulog (LOG_DEBUG, (z)); } while (0) +#define DEBUG_MESSAGE1(i, z, a1) \ + do { if (FDEBUGGING (i)) ulog (LOG_DEBUG, (z), (a1)); } while (0) +#define DEBUG_MESSAGE2(i, z, a1, a2) \ + do { if (FDEBUGGING (i)) ulog (LOG_DEBUG, (z), (a1), (a2)); } while (0) +#define DEBUG_MESSAGE3(i, z, a1, a2, a3) \ + do \ + { \ + if (FDEBUGGING (i)) \ + ulog (LOG_DEBUG, (z), (a1), (a2), (a3)); \ + } \ + while (0) +#define DEBUG_MESSAGE4(i, z, a1, a2, a3, a4) \ + do \ + { \ + if (FDEBUGGING (i)) \ + ulog (LOG_DEBUG, (z), (a1), (a2), (a3), (a4)); \ + } \ + while (0) + +#else /* DEBUG <= 1 */ + +/* If debugging information is not being compiled, provide versions of + the debugging macros which just disappear. */ +#define DEBUG_MESSAGE0(i, z) +#define DEBUG_MESSAGE1(i, z, a1) +#define DEBUG_MESSAGE2(i, z, a1, a2) +#define DEBUG_MESSAGE3(i, z, a1, a2, a3) +#define DEBUG_MESSAGE4(i, z, a1, a2, a3, a4) + +#endif /* DEBUG <= 1 */ + +/* Functions. */ + +/* Given an unknown system name, return information for an unknown + system. If unknown systems are not permitted, this returns FALSE. + Otherwise, it translates the name as necessary for the spool + directory, and fills in *qsys. */ +extern boolean funknown_system P((pointer puuconf, const char *zsystem, + struct uuconf_system *qsys)); + +/* See whether a file belongs in the spool directory. */ +extern boolean fspool_file P((const char *zfile)); + +/* See if the current time matches a time span. If not, return FALSE. + Otherwise, return TRUE and set *pival and *pcretry to the values + from the matching element of the span. */ +extern boolean ftimespan_match P((const struct uuconf_timespan *qspan, + long *pival, int *pcretry)); + +/* Determine the maximum size that may ever be transferred, given a + timesize span. If there are any time gaps larger than 1 hour not + described by the timesize span, this returns -1. Otherwise it + returns the largest size that may be transferred at some time. */ +extern long cmax_size_ever P((const struct uuconf_timespan *qtimesize)); + +/* Send mail about a file transfer. */ +extern boolean fmail_transfer P((boolean fok, const char *zuser, + const char *zmail, const char *zwhy, + const char *zfrom, const char *zfromsys, + const char *zto, const char *ztosys, + const char *zsaved)); + +/* See whether a file is in one of a list of directories. The zpubdir + argument is used to pass the directory names to zsysdep_local_file. + If fcheck is FALSE, this does not check accessibility. Otherwise, + if freadable is TRUE, the user zuser must have read access to the + file and all appropriate directories; if freadable is FALSE zuser + must have write access to the appropriate directories. The zuser + argument may be NULL, in which case all users must have the + appropriate access (this is used for a remote request). */ +extern boolean fin_directory_list P((const char *zfile, + char **pzdirs, + const char *zpubdir, + boolean fcheck, + boolean freadable, + const char *zuser)); + +/* Parse a command string. */ +extern boolean fparse_cmd P((char *zcmd, struct scmd *qcmd)); + +/* Make a log entry. */ +#ifdef __GNUC__ +#define GNUC_VERSION __GNUC__ +#else +#define GNUC_VERSION 0 +#endif + +#if ANSI_C && HAVE_VFPRINTF +extern void ulog P((enum tlog ttype, const char *zfmt, ...)) +#if GNUC_VERSION > 1 + __attribute__ ((format (printf, 2, 3))) +#endif + ; +#else +extern void ulog (); +#endif + +#undef GNUC_VERSION + +/* Report an error returned by one of the uuconf routines. */ +extern void ulog_uuconf P((enum tlog ttype, pointer puuconf, + int iuuconf)); + +/* Set the function to call if a fatal error occurs. */ +extern void ulog_fatal_fn P((void (*pfn) P((void)))); + +/* If ffile is TRUE, send log entries to the log file rather than to + stderr. */ +extern void ulog_to_file P((pointer puuconf, boolean ffile)); + +/* Set the ID number used by the logging functions. */ +extern void ulog_id P((int iid)); + +/* Set the system name used by the logging functions. */ +extern void ulog_system P((const char *zsystem)); + +/* Set the system and user name used by the logging functions. */ +extern void ulog_user P((const char *zuser)); + +/* Set the device name used by the logging functions. */ +extern void ulog_device P((const char *zdevice)); + +/* Close the log file. */ +extern void ulog_close P((void)); + +/* Make an entry in the statistics file. */ +extern void ustats P((boolean fsucceeded, const char *zuser, + const char *zsystem, boolean fsent, + long cbytes, long csecs, long cmicros, + boolean fmaster)); + +/* Close the statistics file. */ +extern void ustats_close P((void)); + +#if DEBUG > 1 +/* A debugging routine to output a buffer. This outputs zhdr, the + buffer length clen, and the contents of the buffer in quotation + marks. */ +extern void udebug_buffer P((const char *zhdr, const char *zbuf, + size_t clen)); + +/* A debugging routine to make a readable version of a character. + This takes a buffer at least 5 bytes long, and returns the length + of the string it put into it (not counting the null byte). */ +extern size_t cdebug_char P((char *z, int ichar)); + +/* Parse a debugging option string. This can either be a number or a + comma separated list of debugging names. This returns a value for + iDebug. */ +extern int idebug_parse P((const char *)); + +#endif /* DEBUG <= 1 */ + +/* Copy one file to another. */ +extern boolean fcopy_file P((const char *zfrom, const char *zto, + boolean fpublic, boolean fmkdirs)); + +/* Copy an open file to another. */ +extern boolean fcopy_open_file P((openfile_t efrom, const char *zto, + boolean fpublic, boolean fmkdirs)); + +/* Translate escape sequences in a buffer, leaving the result in the + same buffer and returning the length. */ +extern size_t cescape P((char *zbuf)); + +/* Get a buffer to hold a string of a given size. The buffer should + be freed with ubuffree. */ +extern char *zbufalc P((size_t csize)); + +/* Call zbufalc to allocate a buffer and copy a string into it. */ +extern char *zbufcpy P((const char *z)); + +/* Free up a buffer returned by zbufalc or zbufcpy. */ +extern void ubuffree P((char *z)); + +/* Allocate memory without fail. */ +extern pointer xmalloc P((size_t)); + +/* Realloc memory without fail. */ +extern pointer xrealloc P((pointer, size_t)); + +/* Free memory (accepts NULL pointers, which some libraries erroneously + do not). */ +extern void xfree P((pointer)); + +/* Global variables. */ + +/* The name of the program being run. This is statically initialized, + although it should perhaps be set from argv[0]. */ +extern char abProgram[]; + +/* When a signal occurs, the signal handlers sets the appropriate + element of the arrays afSignal and afLog_signal to TRUE. The + afSignal array is used to check whether a signal occurred. The + afLog_signal array tells ulog to log the signal; ulog will clear + the element after logging it, which means that if a signal comes in + at just the right moment it will not be logged. It will always be + recorded in afSignal, though. At the moment we handle 5 signals: + SIGHUP, SIGINT, SIGQUIT, SIGTERM and SIGPIPE (the Unix code also + handles SIGALRM). If we want to handle more, the afSignal array + must be extended; I see little point to handling any of the other + ANSI C or POSIX signals, as they are either unlikely to occur + (SIGABRT, SIGUSR1) or nearly impossible to handle cleanly (SIGILL, + SIGSEGV). SIGHUP is only logged if fLog_sighup is TRUE. */ +#define INDEXSIG_SIGHUP (0) +#define INDEXSIG_SIGINT (1) +#define INDEXSIG_SIGQUIT (2) +#define INDEXSIG_SIGTERM (3) +#define INDEXSIG_SIGPIPE (4) +#define INDEXSIG_COUNT (5) + +extern volatile sig_atomic_t afSignal[INDEXSIG_COUNT]; +extern volatile sig_atomic_t afLog_signal[INDEXSIG_COUNT]; +extern boolean fLog_sighup; + +/* The names of the signals to use in error messages, as an + initializer for an array. */ +#define INDEXSIG_NAMES \ + { "hangup", "interrupt", "quit", "termination", "SIGPIPE" } + +/* Check to see whether we've received a signal. It would be nice if + we could use a single variable for this, but we sometimes want to + clear our knowledge of a signal and that would cause race + conditions (clearing a single element of the array is not a race + assuming that we don't care about a particular signal, even if it + occurs after we've examined the array). */ +#define FGOT_SIGNAL() \ + (afSignal[INDEXSIG_SIGHUP] || afSignal[INDEXSIG_SIGINT] \ + || afSignal[INDEXSIG_SIGQUIT] || afSignal[INDEXSIG_SIGTERM] \ + || afSignal[INDEXSIG_SIGPIPE]) + +/* If we get a SIGINT in uucico, we continue the current communication + session but don't start any new ones. This macros checks for any + signal other than SIGINT, which means we should get out + immediately. */ +#define FGOT_QUIT_SIGNAL() \ + (afSignal[INDEXSIG_SIGHUP] || afSignal[INDEXSIG_SIGQUIT] \ + || afSignal[INDEXSIG_SIGTERM] || afSignal[INDEXSIG_SIGPIPE]) + +/* File being sent. */ +extern openfile_t eSendfile; + +/* File being received. */ +extern openfile_t eRecfile; + +/* Device name to log. This is set by fconn_open. It may be NULL. */ +extern char *zLdevice; + +/* If not NULL, ulog calls this function before outputting anything. + This is used to support cu. */ +extern void (*pfLstart) P((void)); + +/* If not NULL, ulog calls this function after outputting everything. + This is used to support cu. */ +extern void (*pfLend) P((void)); diff --git a/gnu/libexec/uucp/contrib/Dial.Hayes b/gnu/libexec/uucp/contrib/Dial.Hayes new file mode 100644 index 0000000000..32eef82d34 --- /dev/null +++ b/gnu/libexec/uucp/contrib/Dial.Hayes @@ -0,0 +1,108 @@ +#!xchat +# @(#) dial.hayes V1.1 Tue Sep 1 13:59:58 1992 (Bob Denny) +# +# xchat script for dialing a vanilla Hayes modem +# +# Usage: +# xchat dial.hayes telno +# +# where telno is the telephone number, subject to pause and wait +# character modification. +# +# Uncomment the first two lines after "start:" to get debugging +# in file "Dial.Log" +# +# Flush input, zero counter, set telephone number if supplied, +# else fail if no telephone number given. +# +start: +### dbgfile Dial.Log +### dbgset 15 + zero + flush + ifnstr notelno 0 + telno 0 + goto initmodem +# +# Missing telephone number. +# +notelno: + logerr No telephone number given + failed +# +# Reset the modem to nonvolatile profile. +# Allow 3 sec. for response, as some modems are slow to reset. +# +initmodem: + count + ifgtr cantinit 4 + send ATZ\r + timeout initmodem 3000 + expect setupmodem OK +# +# No response from modem +# +cantinit: + logerr Can't wake modem + failed +# +# Send the stuff needed to initialize the modem to the modes +# needed for the particular modem flavor. The string below +# is provided as a vanilla example. Allow 2 sec. for the +# modem to respond to this command. +# +setupmodem: + sleep 1000 + send ATM0S7=90S11=120\r + timeout setupfail 2000 + expect setupfail ERROR + expect dialnumber OK +# +# Modem barfed or died on setup command. +# +setupfail: + logerr Error in modem setup string + failed +# +# Dial the supplied number. Handle the various errors that +# can come back from the modem by logging the error. +# +dialnumber: + sleep 1000 + send ATDT + dial + send \r + flush + timeout timeout 90000 + expect connected CONNECT + expect busy BUSY + expect nocarrier NO CARRIER + expect noanswer NO ANSWER + expect nodialtone NO DIALTONE +# +# Success! +# +connected: + success +# +# Handle modem dial failures +# +timeout: + logerr Modem or carrier timeout. + failed +busy: + logerr BUSY + failed +nocarrier: + logerr NO CARRIER + failed +noanswer: + logerr NO ANSWER + failed +nodialtone: + logerr NO DIALTONE + failed +# +# end +# + diff --git a/gnu/libexec/uucp/contrib/Hangup.Hayes b/gnu/libexec/uucp/contrib/Hangup.Hayes new file mode 100644 index 0000000000..c111c00fca --- /dev/null +++ b/gnu/libexec/uucp/contrib/Hangup.Hayes @@ -0,0 +1,57 @@ +#!xchat +# @(#) Hangup.Hayes V1.1 Tue Sep 1 14:04:25 1992 (Bob Denny) +# +# xchat script for hanging up a Hayes-type modem. When used with Taylor +# UUCP, this script should be run as the dialer-complete and dialer-abort +# script with xchat. +# +# Usage: +# xchat Hangup.Hayes [ x ] +# +# where 'x' is any string. If it is present, this script will log the +# modem reset as an ABORT reset, otherwise it will not log anything. +# +# Uncomment the lines starting with '###' to get debugging log. +# +start: +### dbgfile Hangup.Log +### dbgset 15 + zero + sleep 2000 # Wait for trailing garbage + flush # Toss it out + ifnstr wakemodem 0 # No abort indicator + log Hangup on abort +# +# Get modem's attention via Hayes 'escape' protocol. +# +wakemodem: + sleep 4000 + send +++ + sleep 4000 + send \r + timeout reset 2000 + expect reset OK +# +# We're (probably) in command mode. Use ATZ (reset) to hang up +# as some modems don't behave well with ATH0 command. +# +reset: + send ATZ\r + timeout silent 5000 + expect done OK +# +# Finished, modem is back in initial state. +# +done: + success +# +# No response to escape protocol. Log the error and force DTR low +# in an attempt to get control of the modem. Then send ATZ just to +# make sure. +# +silent: + logerr Hangup: no response from modem + hangup # Force DTR low as last gasp + send ATZ\r + sleep 5000 + failed diff --git a/gnu/libexec/uucp/contrib/Login.LAT b/gnu/libexec/uucp/contrib/Login.LAT new file mode 100644 index 0000000000..d557f97e21 --- /dev/null +++ b/gnu/libexec/uucp/contrib/Login.LAT @@ -0,0 +1,137 @@ +#!xchat +# @(#) login.LAT V1.2 Tue Sep 1 13:25:28 1992 +# +# xchat script for logging into a VMS system through a LAT +# terminal server port. If no VMS password parameter supplied, +# skips password phase of VMS login. If LAT-password supplied, +# will log into LAT server using that password. NOTE: does not +# handle the situation where a LAT password is needed but no +# VMS password is needed. +# +# Usage: +# xchat login.LAT sysname username [ password [ LAT-password ] ] +# +# History: +# rbd Fri Aug 14 13:37:06 1992 +# Changes for Lantronix ETS-16. It says "type help at the Local> +# prompt..." then it gives the prompt for real! Prompt may need +# to be something other than "Local>". We match the real Local> +# prompt by matching the leading newline! +# +# rbd Tue Sep 1 13:04:32 1992 +# Remove absolute path name from log file. Now defaults per config. +# +start: + dbgfile Login.Log + dbgset 15 + sleep 2000 # Wait 2 seconds + flush # Flush typeahead + ifnstr svrstart 3 # Skip server password if not given +# +# Starting point if server password supplied. Handle situation +# where the server line may have been left waiting for username +# or at local> prompt. +# +getsvrpwp: + zero +l0: + count # Get server's password prompt + ifgtr deadmux 5 # die if 5 cr's don't do it + send \r + timeout l0 1000 # Wait and try again + expect dosvrpw ssword> + expect svrlogin ername> + expect connect \nLocal> +# +# Send server's password. Fail if don't get Username +# or Local> prompt. +# +dosvrpw: + zero +l2: + sendstr 3 + send \r + timeout badsvrpw 5000 # Die if invalid + expect svrlogin ername> + expect connect \nLocal> +# +# Starting point if NO server password supplied. Handle situation +# where the server line may have been left at local> prompt. +# +svrstart: + zero +l1: + count # Get username> or local> prompt + ifgtr deadmux 5 # Die if 5 cr's don't do it + send \r + timeout l1 1000 # Wait and try again + expect svrlogin ername> + expect connect \nLocal> +# +# Server asked for a username. Just give 'uucp'. +# +svrlogin: + send uucp\r + timeout deadmux 2000 + expect connect \nLocal> +# +# At this point, we have the Local> prompt. Send the connect +# command for the specified LAT host service name, and wait for +# VMS "Username:" prompt. Die after 10 seconds. +# +connect: + send c\s + sendstr 0 + send \r + timeout nologin 10000 + expect gotlogin ername: +# +# Got VMS "Username:" prompt. Send the username. If a password +# was given, wait for the "Password:" prompt. Die after 10 seconds. +# if no password was given, we're done! +# +gotlogin: + sendstr 1 + send \r + ifnstr done 2 + timeout nopasswd 10000 + expect gotpasswd ssword: +# +# Got VMS "Password:" prompt. Send the password and we're done! +# +gotpasswd: + sendstr 2 + send \r +# +# Success! +# +done: + success +# +# ERROR HANDLERS +# +# +# LAT server appears dead. Fail. +# +deadmux: + logerr No response from LAT server + failed +# +# The server password was bad. Fail. +# +badsvrpw: + logerr Invalid LAT server password + failed +# +# VMS system appears to be dead. Fail. +# +nologin: + logerr No VMS Username: prompt + failed +# +# Failed to get "Password:" prompt. Fail. +# +nopasswd: + logerr No VMS Password: prompt. Invalid password? + failed + diff --git a/gnu/libexec/uucp/contrib/Login.PortSel b/gnu/libexec/uucp/contrib/Login.PortSel new file mode 100644 index 0000000000..d8c3a6643a --- /dev/null +++ b/gnu/libexec/uucp/contrib/Login.PortSel @@ -0,0 +1,133 @@ +#!xchat +# @(#) Login.PortSelUnix V1.0 Tue Sep 1 14:57:05 1992 (Bob Denny) +# +# NOTE: Untested with xchat V1.1. Taken from DECUS UUCP. +# +# From: "Kent C. Brodie" +# uucp: {uunet!marque,csd4.milw.wisc.edu}!moocow!brodie +# special script for "uwmcsd4", have to go through a port selector (and then +# log in via standard Unix procedures). +# +# Also included is the ability to wait in the port selector queue. +# Be forwarned that the debug log can get pretty big depending on +# how many times you "wait" in the queue. +# (C) 1989 Kent C. Brodie - Medical College of Wisconsin + +# P0 is systemname , P1 is username, P2 is password. + + zero + +# send a CR to get the selector's attention. Sleep a little bit +# due to large login text of selector. It sends "Which System?" +# when it's ready. + +getprtslct: + count + ifgtr noprtslct 6 + break + send \r + sleep 2000 + flush + expect prtslctok ystem? + timeout getprtslct 15000 + +noprtslct: + logerr Sent cr, no "Which System?" from port selector + failed + +# Send the system name. We either get "OK" (connected), or we +# get "No ports available, would you like to wait?" (wait in queue) + +prtslctok: + zero + sendstr 0 + send \r + expect connected OK + expect prtslctwait wait? + timeout noconnect 10000 + +# Usually we get "nn Your place in queue" messages. JUST in case we +# get a free port right away, check for 'Are you ready?' as well. + +prtslctwait: + zero + send Y\r + expect prtslctque queue + expect prtslctrdy ready? + timeout prtwaitbad 70000 + +prtwaitbad: + logerr Sent "Y" to wait in queue, did not get valid response. + failed + +# Here's where we wait in the queue. The port selector sends us a status +# message about once a minute. We either get "nn Your place in queue" +# or we get "System Available. Are you Ready?". +# If something goes wrong, we time out waiting for either response. +# The reason we don't sleep for 40-50 seconds is because as SOON as the +# port is ready, it informs us. If we wait too long, it drops us. +# This setup is laid out for a maximum of 20 "tries" which is ABOUT +# 20 minutes. Note: This constant retrying can make log files +# kind of big.... + +prtslctque: + count + ifgtr prtslcttry 20 + expect prtslctque queue + expect prtslctrdy ready? + timeout noportwait 70000 + +prtslcttry: + logerr Too many (20) wait/retries -- queue too busy. + failed + +prtslctrdy: + send Y\r + expect connected OK + timeout noconnect 20000 + + +noportwait: + logerr Timed out awaiting place in port queue + failed + +noconnect: + logerr Sent system name, no "OK" from selector + failed + +# standard Unix login stuff. Send cr, expect "ogin:", if no, send a break +# (which tells Unix to try the next bit rate) and try again. + +connected: + send \r + zero + goto waitlogin + +sendbreak: + count + ifgtr nolgi 6 + flush + break + +waitlogin: + expect gotlogin ogin: + timeout sendbreak 5000 + +nolgi: + logerr No login: prompt + failed + +gotlogin: + sendstr 1 + send \r + expect gotword word: + timeout nopwd 10000 + +nopwd: + logerr No password: prompt + failed + +gotword: + sendstr 2 + send \r + success diff --git a/gnu/libexec/uucp/contrib/Login.VMS b/gnu/libexec/uucp/contrib/Login.VMS new file mode 100644 index 0000000000..d6196cb2aa --- /dev/null +++ b/gnu/libexec/uucp/contrib/Login.VMS @@ -0,0 +1,96 @@ +#!xchat +# @(#) Login.VMS V1.1 Tue Sep 1 13:24:54 1992 (Bob Denny) +# +# +# xchat script for logging into a VMS system. If no VMS password +# parameter supplied, skips password phase of VMS login. If syspass +# parameter given, will go through steps needed to log into a VMS +# system where a "system password" was set on the port. +# +# Cannot handle situation where system password is required but +# no password needed. +# +# +# Usage: +# xchat Login.VMS username [ password [ syspass ] ] +# +# Uncomment the lines starting with "###" to get debug logging. +# +start: +### dbgfile Login.Log +### dbgset 15 + sleep 2000 # Wait 2 seconds + zero + flush # Flush typeahead + ifnstr login 2 # Skip sys passwd if not given +# +# Need system password. Send to get bell. +# Try 5 times at 2 sec. intervals. Skip to do +# username if we see "Username:". +# +syspass: + count + ifgtr nobell 5 # Fail after 5 tries + send \r + timeout syspass 2000 # Wait 2 sec. and try again + expect gotbell \007 + expect gotlogin Username: +# +# Got the bell. Send the system password. Repeat 5 times +# at 2 sec. intervals. Fail if we don't get Username: +# +gotbell: + zero + sleep 2000 +l1: + count + ifgtr nologin 5 # Fail after 5 tries + sendstr 2 + send \r + timeout l1 2000 # Wait 2 sec. and try again + expect gotlogin Username: +# +# Start here if no system password supplied. +# Send until we get Username: Try 5 times at 2 sec. intervals. +# +login: + count + ifgtr nologin 5 # Fail after 5 tries + send \r + timeout login 2000 # Wait 2 sec. and try again + expect gotlogin Username: +# +# Got VMS "Username:" prompt. Send the username. If a password +# was given, wait for the "Password:" prompt. Die after 10 seconds. +# if no password was given, we're done! +# +gotlogin: + sendstr 0 + send \r + ifnstr done 1 + timeout nopasswd 10000 + expect gotpasswd Password: +# +# Got VMS "Password:" prompt. Send the password and we're done! +# +gotpasswd: + sendstr 1 + send \r +# +# Success! +# +done: + success +# +# ERROR HANDLERS +# +nobell: + logerr No VMS system password prompt (bell) + failed +nologin: + logerr No VMS Username: prompt + failed +nopasswd: + logerr No VMS Password: prompt. + failed + diff --git a/gnu/libexec/uucp/contrib/Makefile.uurt b/gnu/libexec/uucp/contrib/Makefile.uurt new file mode 100644 index 0000000000..235fcca889 --- /dev/null +++ b/gnu/libexec/uucp/contrib/Makefile.uurt @@ -0,0 +1,30 @@ +# +# Makefile for uurate 1.0 +# + +# Where uurate is installed +BIN=/usr/local/bin + +# Flags to use when compiling uurate +CFLAGS=-I.. + +CC=cc +SHELL=/bin/sh +PROGS=uurate + +#----------- + +all: $(PROGS) + +install: $(PROGS) + @for i in $(PROGS) ; do \ + echo "Install $$i into $(BIN)..." ; \ + cp $$i $(BIN) ; \ + echo "Set ownership and protection..." ; \ + /bin/chmod 0555 $(BIN)/$$i ; \ + /bin/chown bin $(BIN)/$$i ; \ + /bin/chgrp bin $(BIN)/$$i ; \ + done + +clean: + rm -f $(PROGS) core diff --git a/gnu/libexec/uucp/contrib/Makefile.xchat b/gnu/libexec/uucp/contrib/Makefile.xchat new file mode 100644 index 0000000000..5e9aaa8ffe --- /dev/null +++ b/gnu/libexec/uucp/contrib/Makefile.xchat @@ -0,0 +1,31 @@ +# +# Makefile for xchat 1.1 +# +# Bob Denny - Tue Sep 1 15:58:22 1992 +# +CC=cc +SHELL=/bin/sh +BIN=/usr/local/lib/uucp +PROGS=xchat + +#----------- + +all: $(PROGS) + +install: $(PROGS) + @for i in $(PROGS) ; do \ + echo "Install $$i into $(BIN)..." ; \ + cp $$i $(BIN) ; \ + echo "Set ownership and protection..." ; \ + /bin/chmod 0555 $(BIN)/$$i ; \ + /bin/chown bin $(BIN)/$$i ; \ + /bin/chgrp bin $(BIN)/$$i ; \ + done + +clean: + rm -f $(PROGS) core + + + + + diff --git a/gnu/libexec/uucp/contrib/README b/gnu/libexec/uucp/contrib/README new file mode 100644 index 0000000000..c4105ed038 --- /dev/null +++ b/gnu/libexec/uucp/contrib/README @@ -0,0 +1,46 @@ +This is the README file for the Taylor UUCP contrib directory. + +This directory contains contributed shell scripts and programs that +you may find useful. + +xchat.c, xchat.man, README-XCHAT, xc-conf.h-dist, Makefile.xchat: + A program by Bob Denny that may be invoked by the ``chat-program'' + command for any of the various types of chat scripts. It is + driven by scripts which are written in its own little language. + It is a powerful program that can add a lot of flexibility to your + chat scripts. + +Dial.Hayes, Hangup.Hayes, Login.LAT, Login.PortSel, Login.VMS: + Sample scripts for xchat. + +uurate.c, uurate.man, README-UURATE, Makefile.uurt: + A nifty little program by Bob Denny which analyzes the Log and + Stats file and prints various sorts of reports. + +uutraf: + Another program to produce neat reports from your log files, this + one a perl script by Johan Vromans. + +savelog.sh, savelog.man: + A handy shell script to rename a log file and cycle old versions + through a set of names, throwing away the oldest one. It will + also optionally compress the old log files. I believe that this + is originally from smail. It was written by Ronald S. Karr and + Landon Curt Noll, and was given to me by Bob Denny. + +uureroute: + A perl script reroute all mail queued up for one host to another. + Written by Bill Campbell and contributed by Francois Pinard. + +stats.sh: + A gawk script by Zacharias Beckman which reads the Stats file and + prints the last 80 lines as a nicely formatted table. + +uuq.sh: + A uuq workalike shell script by Zacharias Beckman. + +tstout.c: + A program to remove a user from utmp and wtmp, essentially logging + them out. I put this together from BSD code. I need it to use + tstuu with the system UUCP on Ultrix 4.0, for reasons that escape + me. Most people will have little use for this. diff --git a/gnu/libexec/uucp/contrib/README-UURATE b/gnu/libexec/uucp/contrib/README-UURATE new file mode 100644 index 0000000000..2cc361ce26 --- /dev/null +++ b/gnu/libexec/uucp/contrib/README-UURATE @@ -0,0 +1,20 @@ +uurate V1.2 - Gather and display Taylor UUCP traffic statistics + +Bob Denny (denny@alisa.com) - Thu Sep 3 19:47:41 1992 + +See the man page for documentation. + +Installation: +------------ + +(1) Copy Makefile.uurt to Makefile. + +(2) Edit Makefile: set BIN where you want uurate to be installed, and + set CFLAGS to point to the directory containing the UUCP sources + (this is .. by default). + +(3) Type ``make'' to compile the program. + +(4) Type ``make install'' to install the program. + +(5) Install the man page if you want. I didn't put that into the Makefile. diff --git a/gnu/libexec/uucp/contrib/README-XCHAT b/gnu/libexec/uucp/contrib/README-XCHAT new file mode 100644 index 0000000000..5f93a284bd --- /dev/null +++ b/gnu/libexec/uucp/contrib/README-XCHAT @@ -0,0 +1,42 @@ +This is xchat V1.1 (Tue Sep 1 15:50:56 1992) + +Introduction: +------------ + +Xchat is a general-purpose dialing and login program designed for use +with Taylor UUCP as a "chat-program", taking the place (or augmenting) +the built-in chat scripting facility. It provides the ability to +closely control timeouts, multiple simultaneous expect strings with +separate actions, extended terminal control, modem command character +pacing, and more. + +When used in conjunction with Taylor UUCP's configuration features, +xchat can provide you the ability to manage the most intricate login, +dial and hangup needs. The scripts are written in a shell-like (well, +sort-of) style with labels, commands, and parameters, easing the task +of writing procedures for complex terminal communications situations. + +Installation: +------------ + +(1) Copy xc-conf.h-dist to xc-conf.h, then edit xc-conf.h to reflect + your condifuration. A description of the settings is in that file. + +(2) Copy Makefile.xchat to Makefile and edit it to set BIN to where + you want xchat installed. + +(2) Do a 'make' to build xchat. + +(3) Do a 'make install' to install it. + +(4) Format and print xchat.8, and install it if you want. + +(5) Print out copies of the scripts in the ./scripts subdirectory. + +(6) Read xchat.8 and the scripts together. + + +Author: +------ + +Robert B. Denny (denny@alisa.com) diff --git a/gnu/libexec/uucp/contrib/savelog.man b/gnu/libexec/uucp/contrib/savelog.man new file mode 100644 index 0000000000..919b94f4ec --- /dev/null +++ b/gnu/libexec/uucp/contrib/savelog.man @@ -0,0 +1,130 @@ +.\" @(#)man/man8/savelog.an 1.2 24 Oct 1990 05:18:46 +.de pP +.if n .sp 1 +.if t .sp .4 +.. +.de tP +.pP +.ta \\n(pDu +.ti -\\n(pDu +.. +.TH SAVELOG X_MAN8_EXT_X "31 January 1988" "Local" +.SH NAME +savelog \- cycle and truncate log files +.SH SYNOPSIS +.na +.B X_UTIL_BIN_DIR_X/savelog +[ +.B \-m +.I mode +] [ +.B \-u +.I user +] [ +.B \-g +.I group +] [ +.B \-c +.I cycle +] [ +.B \-t +] [ +.B \-l +] +.I logfile +.br +.ad +.SH DESCRIPTION +The +.I savelog +command renames and optionally compresses a log file and cycles it +through a set of names based on the original log file, removing the +last name in the cycle. +.SH OPTIONS +The +.I savelog +command accepts the following options: +.TP +\fB\-m\fP \fImode\fP +Change the permissions mode for renamed log files to +.IR mode . +By default the mode is unchanged. +.TP +\fB\-u\fP \fIuser\fP +Change the owner for renamed log files to +.IR user . +By default the owner is unchanged. +.TP +\fB\-g\fP \fIgroup\fP +Change the group for renamed log files to +.IR group . +By default the group is unchanged. +.TP +\fB\-c\fP \fIcycle\fP +Save +.I cycle +versions of the logfile, where +.I cycle +is a decimal number. The default value is 7. +.TP +.B \-l +Do not compress log files. By default, a compression program is used, +if one is available. +.TP +.B \-t +Ensure that a new logfile exists when the savelog operation is +complete. Use of +.BR \-m , +.BR \-u +or +.BR \-g +imply this, ensuring that the logfile will have the designated mode. +.SH "OPERATION" +The given logfile is cycled through files named: +.RS + +OLD/\fIfile\fP.\fInumber\fP + +.RE +where +.I file +is the basename for the logfile and where +.I number +ranges from 0 to one less then the +.I cycle +count specified for the command. +The +.I OLD +dirctory is created, as necessary, and is under the same directory as +the logfile itself. +.PP +This cycle operation is accomplished by renaming the file numbered +.IR cycle -2 +to a file numbered +.IR cycle -1 +and so on until the file numbered 0 is renamed to the file numbered 1. +If compression is being used, the first cycle file is compressed after +being renamed to cycle 1. After the cycle files are moved through the +various names, the filefile itself is moved to the cycle 0 file. +This cycle normally occurs once every time +.I savelog +is executed. +If the log file does not exist, savelog ignores it and does +not cycle the OLD files. +.PP +If compression is being used, then compressed log files will have an +additional suffix appropriate for the compression program that is +used. +.SH "SEE ALSO" +.IR smail (X_MAN5_EXT_X) +and +.IR smail (X_MAN8_EXT_X). +.SH COPYRIGHT +Copyright(C)1987, 1988 Ronald S. Karr and Landon Curt Noll +.br +See a file COPYING, +distributed with the source code, +or type +.I "smail \-bc" +for distribution rights and restrictions +associated with this software. diff --git a/gnu/libexec/uucp/contrib/savelog.sh b/gnu/libexec/uucp/contrib/savelog.sh new file mode 100644 index 0000000000..64c989f292 --- /dev/null +++ b/gnu/libexec/uucp/contrib/savelog.sh @@ -0,0 +1,247 @@ +#! /bin/sh +# @(#)util/savelog.sh 1.4 26 Oct 1991 22:49:39 +# +# savelog - save a log file +# +# Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll +# +# See the file COPYING, distributed with smail, for restriction +# and warranty information. +# +# usage: savelog [-m mode] [-u user] [-g group] [-t] [-c cycle] [-l] file... +# +# -m mode - chmod log files to mode +# -u user - chown log files to user +# -g group - chgrp log files to group +# -c cycle - save cycle versions of the logfile (default: 7) +# -t - touch file +# -l - don't compress any log files (default: compress) +# file - log file names +# +# The savelog command saves and optionally compresses old copies of files +# into an 'dir'/OLD sub-directory. The 'dir' directory is determined from +# the directory of each 'file'. +# +# Older version of 'file' are named: +# +# OLD/'file'. +# +# where is the version number, 0 being the newest. By default, +# version numbers > 0 are compressed (unless -l prevents it). The +# version number 0 is never compressed on the off chance that a process +# still has 'file' opened for I/O. +# +# If the 'file' does not exist or if it is zero length, no further processing +# is performed. However if -t was also given, it will be created. +# +# For files that do exist and have lengths greater than zero, the following +# actions are performed. +# +# 1) Version numered files are cycled. That is version 6 is moved to +# version 7, version is moved to becomes version 6, ... and finally +# version 0 is moved to version 1. Both compressed names and +# uncompressed names are cycled, regardless of -t. Missing version +# files are ignored. +# +# 2) The new OLD/file.1 is compressed and is changed subject to +# the -m, -u and -g flags. This step is skipped if the -t flag +# was given. +# +# 3) The main file is moved to OLD/file.0. +# +# 4) If the -m, -u, -g or -t flags are given, then file is created +# (as an empty file) subject to the given flags. +# +# 5) The new OLD/file.0 is chanegd subject to the -m, -u and -g flags. +# +# Note: If the OLD sub-directory does not exist, it will be created +# with mode 0755. +# +# Note: If no -m, -u or -g flag is given, then the primary log file is +# not created. +# +# Note: Since the version numbers start with 0, version number +# is never formed. The count must be at least 2. +# +# Bugs: If a process is still writing to the file.0 and savelog +# moved it to file.1 and compresses it, data could be lost. +# Smail does not have this problem in general because it +# restats files often. + +# common location +PATH="X_UTIL_PATH_X:X_SECURE_PATH_X"; export PATH +COMPRESS="X_COMPRESS_X" +COMP_FLAG="X_COMP_FLAG_X" +DOT_Z="X_DOT_Z_X" +CHOWN="X_CHOWN_X" +GETOPT="X_UTIL_BIN_DIR_X/getopt" + +# parse args +exitcode=0 # no problems to far +prog=$0 +mode= +user= +group= +touch= +count=7 +set -- `$GETOPT m:u:g:c:lt $*` +if [ $# -eq 0 -o $? -ne 0 ]; then + echo "usage: $prog [-m mode][-u user][-g group][-t][-c cycle][-l] file ..." 1>&2 + exit 1 +fi +for i in $*; do + case $i in + -m) mode=$2; shift 2;; + -u) user=$2; shift 2;; + -g) group=$2; shift 2;; + -c) count=$2; shift 2;; + -t) touch=1; shift;; + -l) COMPRESS=""; shift;; + --) shift; break;; + esac +done +if [ "$count" -lt 2 ]; then + echo "$prog: count must be at least 2" 1>&2 + exit 2 +fi + +# cycle thru filenames +while [ $# -gt 0 ]; do + + # get the filename + filename=$1 + shift + + # catch bogus files + if [ -b "$filename" -o -c "$filename" -o -d "$filename" ]; then + echo "$prog: $filename is not a regular file" 1>&2 + exitcode=3 + continue + fi + + # if not a file or empty, do nothing major + if [ ! -s $filename ]; then + # if -t was given and it does not exist, create it + if [ ! -z "$touch" -a ! -f $filename ]; then + touch $filename + if [ "$?" -ne 0 ]; then + echo "$prog: could not touch $filename" 1>&2 + exitcode=4 + continue + fi + if [ ! -z "$user" ]; then + $CHOWN $user $filename + fi + if [ ! -z "$group" ]; then + chgrp $group $filename + fi + if [ ! -z "$mode" ]; then + chmod $mode $filename + fi + fi + continue + fi + + # be sure that the savedir exists and is writable + savedir=`expr "$filename" : '\(.*\)/'` + if [ -z "$savedir" ]; then + savedir=./OLD + else + savedir=$savedir/OLD + fi + if [ ! -s $savedir ]; then + mkdir $savedir + if [ "$?" -ne 0 ]; then + echo "$prog: could not mkdir $savedir" 1>&2 + exitcode=5 + continue + fi + chmod 0755 $savedir + fi + if [ ! -d $savedir ]; then + echo "$prog: $savedir is not a directory" 1>&2 + exitcode=6 + continue + fi + if [ ! -w $savedir ]; then + echo "$prog: directory $savedir is not writable" 1>&2 + exitcode=7 + continue + fi + + # deterine our uncompressed file names + newname=`expr "$filename" : '.*/\(.*\)'` + if [ -z "$newname" ]; then + newname=$savedir/$filename + else + newname=$savedir/$newname + fi + + # cycle the old compressed log files + cycle=`expr $count - 1` + rm -f $newname.$cycle $newname.$cycle$DOT_Z + while [ "$cycle" -gt 1 ]; do + # --cycle + oldcycle=$cycle + cycle=`expr $cycle - 1` + # cycle log + if [ -f $newname.$cycle$DOT_Z ]; then + mv -f $newname.$cycle$DOT_Z $newname.$oldcycle$DOT_Z + fi + if [ -f $newname.$cycle ]; then + # file was not compressed for some reason move it anyway + mv -f $newname.$cycle $newname.$oldcycle + fi + done + + # compress the old uncompressed log if needed + if [ -f $newname.0 ]; then + if [ -z "$COMPRESS" ]; then + newfile=$newname.1 + mv $newname.0 $newfile + else + newfile=$newname.1$DOT_Z + $COMPRESS $COMP_FLAG < $newname.0 > $newfile + rm -f $newname.0 + fi + if [ ! -z "$user" ]; then + $CHOWN $user $newfile + fi + if [ ! -z "$group" ]; then + chgrp $group $newfile + fi + if [ ! -z "$mode" ]; then + chmod $mode $newfile + fi + fi + + # move the file into the file.0 holding place + mv -f $filename $newname.0 + + # replace file if needed + if [ ! -z "$touch" -o ! -z "$user" -o \ + ! -z "$group" -o ! -z "$mode" ]; then + touch $filename + fi + if [ ! -z "$user" ]; then + $CHOWN $user $filename + fi + if [ ! -z "$group" ]; then + chgrp $group $filename + fi + if [ ! -z "$mode" ]; then + chmod $mode $filename + fi + + # fix the permissions on the holding place file.0 file + if [ ! -z "$user" ]; then + $CHOWN $user $newname.0 + fi + if [ ! -z "$group" ]; then + chgrp $group $newname.0 + fi + if [ ! -z "$mode" ]; then + chmod $mode $newname.0 + fi +done +exit $exitcode diff --git a/gnu/libexec/uucp/contrib/stats.sh b/gnu/libexec/uucp/contrib/stats.sh new file mode 100644 index 0000000000..ac1d0f556e --- /dev/null +++ b/gnu/libexec/uucp/contrib/stats.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# +# uuspeed - a script to parse a Taylor UUCP Stats file into pretty results. +# Zacharias J. Beckman. + +grep bytes /usr/spool/uucp/Stats | grep -v 'bytes 0.00 secs' | grep -v 'failed after' | tail -80 | \ +gawk ' + BEGIN { + printf(" UUCP transmission history:\n"); + format=" %8d bytes %8s(%8s) in %7.2f sec = %5.0f baud, %4.1fK / min\n"; + average=0.01; + samples=0; + } + + { + if ($6 > 100) { + printf (format, $6, $5, $2, $9, $6/$9*10, ($6/$9*60)/1000); + + average += ($6/$9*10); + samples += 1; + } + } + + END { + printf (" average speed %d baud\n", average/samples); + } +' diff --git a/gnu/libexec/uucp/contrib/tstout.c b/gnu/libexec/uucp/contrib/tstout.c new file mode 100644 index 0000000000..dd82633c7a --- /dev/null +++ b/gnu/libexec/uucp/contrib/tstout.c @@ -0,0 +1,158 @@ +/* tstout.c + Put together by Ian Lance Taylor + + This program is used to logout a program run by the tstuu program. + I needed this because on Ultrix 4.0 I can't get the uucp program + to run without invoking it via /bin/login and having it start up + as a shell. If I don't do it this way, it gets a SIGSEGV trap + for some reason. Most systems probably don't need to do things + this way. It will only work on BSD systems anyhow, I suspect. + + The code for this comes from "UNIX Network Programming" by W. + Richard Stevens, Prentice-Hall 1990. Most of it is from 4.3BSD, as + noted in the comments. + + This program must run suid to root. + */ + +#include +#include + +#include +#include +#include +#include +#include + +static int logout P((const char *zdev)); +static void logwtmp P((const char *zdev, const char *zname, + const char *zhost)); + +int +main (argc, argv) + int argc; + char **argv; +{ + char *z; + + if (argc != 2 + || strncmp (argv[1], "/dev/", sizeof "/dev/" - 1) != 0) + { + fprintf (stderr, "Usage: tstout device\n"); + exit (EXIT_FAILURE); + } + + z = argv[1] + 5; + + if (logout (z)) + logwtmp (z, "", ""); + + chmod (argv[1], 0666); + chown (argv[1], 0, 0); + + *z = 'p'; + chmod (argv[1], 0666); + chown (argv[1], 0, 0); + + exit (EXIT_SUCCESS); +} + +/* + * Copyright (c) 1988 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)logout.c 5.2 (Berkeley) 2/17/89"; +#endif /* LIBC_SCCS and not lint */ + +#define UTMPFILE "/etc/utmp" + +/* 0 on failure, 1 on success */ + +static int +logout(line) + register const char *line; +{ + register FILE *fp; + struct utmp ut; + int rval; + time_t time(); + + if (!(fp = fopen(UTMPFILE, "r+"))) + return(0); + rval = 0; + while (fread((char *)&ut, sizeof(struct utmp), 1, fp) == 1) { + if (!ut.ut_name[0] || + strncmp(ut.ut_line, line, sizeof(ut.ut_line))) + continue; + bzero(ut.ut_name, sizeof(ut.ut_name)); + bzero(ut.ut_host, sizeof(ut.ut_host)); + (void)time((time_t *)&ut.ut_time); + (void)fseek(fp, (long)-sizeof(struct utmp), L_INCR); + (void)fwrite((char *)&ut, sizeof(struct utmp), 1, fp); + (void)fseek(fp, (long)0, L_INCR); + rval = 1; + } + (void)fclose(fp); + return(rval); +} + +/* + * Copyright (c) 1988 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)logwtmp.c 5.2 (Berkeley) 9/20/88"; +#endif /* LIBC_SCCS and not lint */ + +#define WTMPFILE "/usr/adm/wtmp" + +static void +logwtmp(line, name, host) + const char *line, *name, *host; +{ + struct utmp ut; + struct stat buf; + int fd; + time_t time(); + char *strncpy(); + + if ((fd = open(WTMPFILE, O_WRONLY|O_APPEND, 0)) < 0) + return; + if (!fstat(fd, &buf)) { + (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line)); + (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name)); + (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host)); + (void)time((time_t *)&ut.ut_time); + if (write(fd, (char *)&ut, sizeof(struct utmp)) != + sizeof(struct utmp)) + (void)ftruncate(fd, buf.st_size); + } + (void)close(fd); +} diff --git a/gnu/libexec/uucp/contrib/uuclean b/gnu/libexec/uucp/contrib/uuclean new file mode 100644 index 0000000000..e9c631c86c --- /dev/null +++ b/gnu/libexec/uucp/contrib/uuclean @@ -0,0 +1,23 @@ +# This is a sample uuclean shell script +# Copyright (C) 1992 Ian Lance Taylor +# Do whatever you like with this script. +# +# Set some variables +bindir=/usr/local/bin +spooldir=/usr/spool/uucp +# +# Warn about all mail over two days old +$(bindir)/uustat -c rmail -o 48 -N -Q -W"Unable to deliver; will try up to one week" +# Return all mail over a week old +$(bindir)/uustat -c rmail -o 168 -K -M -N -Q -W"Could not be delivered for over one week" +# Throw away other requests over a week old +$(bindir)/uustat -o 168 -K -M -N -Q -W"Over one week old" +# Throw away any executions over three days old +$(bindir)/uustat -o 72 -M -N -Q -W"Unable to execute for three days" +# +# Now delete any old spool files +find $(spooldir) -ctime +8 -name '[CDX].*' -print -exec rm -f \{\} \; +# Delete any old temporary files +find $(spooldir) -atime +1 -ctime +1 -name 'TM.*' -print -exec rm -f \{\} \; +# Delete any old preserved files +find $(spooldir)/.Preserve -atime +14 -ctime +14 -print -exec rm -f \{\} \; diff --git a/gnu/libexec/uucp/contrib/uuq.sh b/gnu/libexec/uucp/contrib/uuq.sh new file mode 100644 index 0000000000..a5d88e9522 --- /dev/null +++ b/gnu/libexec/uucp/contrib/uuq.sh @@ -0,0 +1,125 @@ +#!/bin/sh +# +# uuq - a script to examine and display the Taylor spool directory contents. +# note - uses the uuname script or similar functionality. +# Zacharias Beckman + +SPOOLDIR="/usr/spool/uucp" +SYSTEMS=`uuname` +TMPFILE="/tmp/uuq.tmp" +FORSYSTEM="" +DELETE="" +LONG=0 +SINGLE=0 + +while [ "$1" != "" ] +do + case $1 in + -l) LONG=1 + shift + ;; + -s) shift + SYSTEMS=$argv[1] + SINGLE=1 + shift + ;; + -d) shift + DELETE=$argv[1] + shift + ;; + -h) echo "uuq: usage uuq [options]" + echo " -l long listing (may take a while)" + echo " -s n run uuq only for system n" + echo " -d n delete item n from the queue (required -s)" + exit 1 + ;; + *) echo "uuq: invalid option" + exit 1 + ;; + esac +done + +if [ "${DELETE}" != "" ] && [ ${SINGLE} != 1 ] ; then + echo "uuq: you must specify a system to delete the job from:" + echo " uuq -s wizard -d D.0004" + exit 1 +fi + +cd ${SPOOLDIR} + +# if we are deleting a job, then do that first and exit without showing +# any other queue information + +if [ "${DELETE}" != "" ] ; then + if [ -d ${SYSTEMS}/D. ] ; then + cd ${SYSTEMS}/C. + PACKET=${DELETE} + if [ -f ${PACKET} ] ; then + EXFILE=../D.X/`awk '{if (NR == 2) print $2}' ${PACKET}` + DFILE=../D./`awk '{if (NR == 1) print $2}' ${PACKET}` + echo "deleting job ${PACKET}" + rm ${PACKET} + rm ${EXFILE} + rm ${DFILE} + else + echo "uuq: job ${PACKET} not found" + exit 1 + fi + else + echo "uuq: system ${SYSTEMS} not found" + fi + + exit 1 +fi + +# use the 'uuname' script to obtain a list of systems for the 'sys' file, +# then step through each directory looking for appropriate information. + +if [ ${LONG} -gt 0 ] ; then + echo "system" + echo -n "job# act size command" +fi + +for DESTSYSTEM in ${SYSTEMS} ; do + # if there is an existing directory for the named system, cd into it and + # "do the right thing." + + if [ -d ${DESTSYSTEM} ] ; then + cd ${DESTSYSTEM}/C. + + PACKET=`ls` + + if [ "${PACKET}" != "" ] ; then + # if a long listing has been required, extra information is printed + + echo "" + echo "${DESTSYSTEM}:" + + # now each packet must be examined and appropriate information is + # printed for this system + + if [ ${LONG} -gt 0 ] ; then + for PACKET in * ; do + EXFILE=../D.X/`awk '{if (NR == 2) print $2}' ${PACKET}` + DFILE=../D./`awk '{if (NR == 1) print $2}' ${PACKET}` + echo -n "${PACKET} " > ${TMPFILE} + gawk '{if (NR == 2) printf(" %s ", $1);}' ${PACKET} >> ${TMPFILE} + ls -l ${DFILE}|awk '{printf("%-10d ", $4)}' >> ${TMPFILE} + if [ -f ${EXFILE} ] ; then + gawk '/U / {printf("(%s)", $2);}\ + /C / {print substr($0,2,length($0));}' ${EXFILE} >> ${TMPFILE} + else + echo "---" >> ${TMPFILE} + fi + + cat ${TMPFILE} + done + cat ${SPOOLDIR}/.Status/${DESTSYSTEM} + else + ls + fi + fi + fi + + cd ${SPOOLDIR} +done diff --git a/gnu/libexec/uucp/contrib/uurate.c b/gnu/libexec/uucp/contrib/uurate.c new file mode 100644 index 0000000000..ceab41c53b --- /dev/null +++ b/gnu/libexec/uucp/contrib/uurate.c @@ -0,0 +1,657 @@ +/* + * @(#)uurate.c 1.2 - Thu Sep 3 18:32:46 1992 + * + * This program digests log and stats files in the "Taylor" format + * and outputs various statistical data to standard out. + * + * Author: + * Bob Denny (denny@alisa.com) + * Fri Feb 7 13:38:36 1992 + * + * Original author: + * Mark Pizzolato mark@infopiz.UUCP + * + * Edits: + * Bob Denny - Fri Feb 7 15:04:54 1992 + * Heavy rework for Taylor UUCP. This was the (very old) uurate from + * DECUS UUCP, which had a single logfile for activity and stats. + * Personally, I would have done things differently, with tables + * and case statements, but in the interest of time, I preserved + * Mark Pizzolato's techniques and style. + * + * Bob Denny - Sun Aug 30 14:18:50 1992 + * Changes to report format suggested by Francois Pinard and others. + * Add summary report, format from uutraf.pl (perl script), again + * thanks to Francois. Integrate and checkout with 1.03 of Taylor UUCP. + */ + +char version[] = "@(#) Taylor UUCP Log File Summary Filter, Version 1.2"; + +#include /* Character Classification */ +#include +#include + +#include "uucp.h" + + +#define _DEBUG_ 0 + +/* + * Direction of Calling and Data Transmission + */ +#define IN 0 /* Inbound */ +#define OUT 1 /* Outbound */ + +/* + * Data structures used to collect information + */ +struct File_Stats + { + int files; /* Files Transferred */ + unsigned long bytes; /* Data Size Transferred*/ + double time; /* Transmission Time */ + }; + +struct Phone_Call + { + int calls; /* Call Count */ + int succs; /* Successful calls */ + double connect_time; /* Connect Time Spent */ + struct File_Stats flow[2]; /* Rcvd & Sent Data */ + }; + +struct Execution_Command + { + struct Execution_Command *next; + char Commandname[64]; + int count; + }; + +struct Host_entry + { + struct Host_entry *next; + char Hostname[32]; + struct Execution_Command *cmds; /* Local Activities */ + struct Phone_Call call[2]; /* In & Out Activities */ + }; + +/* + * Stuff for getopt() + */ +extern int optind; /* GETOPT : Option Index */ +extern char *optarg; /* GETOPT : Option Value */ +extern void *calloc(); + +static void fmtime(); +static void fmbytes(); + +/* + * Default files to read. Taken from Taylor compile-time configuration. + * Must look like an argvec, hence the dummy argv[0]. + */ +static char *(def_logs[3]) = { "", LOGFILE, STATFILE }; + +/* + * Misc. strings for reports + */ +static char *(file_hdr[2]) = { "\nReceived file statistics:\n", + "\nSent file statistics\n" }; + +/* + * BEGIN EXECUTION + */ +main(argc, argv) +int argc; +char *argv[]; +{ + char c; + char *p, *s; + struct Host_entry *hosts = NULL; + struct Host_entry *cur = NULL; + struct Host_entry *e; + struct Execution_Command *cmd; + struct Execution_Command *ec; + char Hostname[64]; + FILE *Log = NULL; + char logline[1024]; + char *logmsg; + int sent; + int called; + int show_files = 0; /* I prefer boolean, but... */ + int show_calls = 0; + int show_commands = 0; + int show_efficiency = 0; + int show_summary = 0; + int have_files = 0; + int have_calls = 0; + int have_commands = 0; + int use_stdin = 0; + Hostname[0] = '\0'; + + /* + * I wish the compiler had the #error directive! + */ +#if !HAVE_TAYLOR_LOGGING + fprintf(stderr, "uurate cannot be used with your configuration of\n"); + fprintf(stderr, "Taylor UUCP. To use uurate you must be using the\n"); + fprintf(stderr, "TAYLOR_LOGGING configuration.\n"); + exit(1); +#endif + + /* + * Process the command line arguments + */ + while((c = getopt(argc, argv, "h:cfexai")) != EOF) + { + switch(c) + { + case 'h': + strcpy(Hostname, optarg); + break; + case 'c': + show_calls = 1; + break; + case 'f': + show_files = 1; + break; + case 'x': + show_commands = 1; + break; + case 'e': + show_efficiency = 1; + break; + case 'a': + show_calls = show_files = show_commands = show_efficiency = 1; + break; + case 'i': + use_stdin = 1; + break; + default : + goto usage; + } + } + + /* + * If no report switches given, show summary report. + */ + if (show_calls == 0 && show_files == 0 + && show_efficiency == 0 && show_commands == 0) + show_summary = 1; + + /* + * Adjust argv and argc to account for the args processed above. + */ + argc -= (optind - 1); + argv += (optind - 1); + + /* + * If further args present, Assume rest are logfiles for us to process, + * otherwise, take input from Log and Stat files provided in the + * compilation environment of Taylor UUCP. If -i was given, Log already + * points to stdin and no file args are accepted. + */ + if(argc == 1) /* No file arguments */ + { + if (use_stdin) /* If -i, read from stdin */ + { + argc = 2; + Log = stdin; + } + else /* Read from current logs */ + { + argc = 3; /* Bash argvec to default log/stat files */ + argv = &def_logs[0]; + } + } + else if (use_stdin) /* File args with -i is an error */ + { + fprintf(stderr, "uurate (error): file args given with '-i'\n"); + goto usage; + } + +#if _DEBUG_ + printf("\n"); +#endif + + /* + * MAIN LOGFILE PROCESSING LOOP + */ + while (argc > 1) + { + + if (!use_stdin && (Log = fopen(argv[1], "r")) == NULL) + { + perror(argv[1]); + return; + } + +#if _DEBUG_ + printf("Reading %s...\n", (use_stdin ? "stdin" : argv[1])); +#endif + + /* + * Read each line of the logfile and collect information + */ + while (fgets(logline, sizeof(logline), Log)) + { + /* + * The host name of the other end of the connection is + * always the second field of the log line, whether we + * are reading a Log file or a Stats file. Set 'p' to + * point to the second field, null-terminated. Skip + * the line if something is funny. + */ + if (NULL == (p = strchr(logline, ' '))) + continue; + ++p; + if (NULL != (s = strchr(p, ' '))) + *s = '\0'; + for (s = p; *s; ++s) + if (isupper(*s)) + *s = tolower(*s); + /* + * Skip this line if we got -h and + * this line does not contain that host name. + */ + if (Hostname[0] != '\0') + if (0 != strcmp(p, Hostname)) + continue; + /* + * We are within a call block now. If this line is a file + * transfer record, determine the direction. If not then + * skip the line if it is not interesting. + */ + if ((s = strchr(++s, ')')) == NULL) + continue; + logmsg = s + 2; /* Message is 2 characters after ')' */ + if (0 == strncmp(logmsg, "sent", 4)) + sent = OUT; + else + if (0 == strncmp(logmsg, "received", 8)) + sent = IN; + else + if ((0 != strncmp(logmsg, "Call complete", 13)) && + (0 != strncmp(logmsg, "Calling system", 14)) && + (0 != strncmp(logmsg, "Incoming call", 13)) && + (0 != strncmp(logmsg, "Executing", 9))) + continue; + /* + * Find the Host_entry for this host, or create a new + * one and link it on to the list. + */ + if ((cur == NULL) || (0 != strcmp(p, cur->Hostname))) + { + for (cur = hosts; cur != NULL ; cur = cur->next) + if (0 == strcmp(cur->Hostname, p)) + break; + if (cur == NULL) + { + cur = (struct Host_entry *)calloc(1, sizeof(*hosts)); + strcpy(cur->Hostname, p); + if (hosts == NULL) + hosts = cur; + else + { + for (e = hosts; e->next != NULL; e = e->next); + e->next = cur; + } + } + } + /* + * OK, if this is a uuxqt record, find the Execution_Command + * structure for the command being executed, or create a new + * one. Then count an execution of this command. + */ + if (0 == strncmp(logmsg, "Executing", 9)) + { + if (NULL == (p = strchr(logmsg, '('))) + continue; + if ((s = strpbrk(++p, " )")) == NULL) + continue; + *s = '\0'; + for (cmd = cur->cmds; cmd != NULL; cmd = cmd->next) + if (0 == strcmp(cmd->Commandname, p)) + break; + if (cmd == NULL) + { + cmd = (struct Execution_Command *)calloc(1, sizeof(*cmd)); + strcpy(cmd->Commandname, p); + if (cur->cmds == NULL) + cur->cmds = cmd; + else + { + for (ec = cur->cmds; ec->next != NULL; ec = ec->next); + ec->next = cmd; + } + } + ++cmd->count; + have_commands = 1; + continue; + } + /* + * Count start of outgoing call. + */ + if (0 == strncmp(logmsg, "Calling system", 14)) + { + called = OUT; + cur->call[called].calls += 1; + have_calls = 1; + continue; + } + /* + * Count start of incoming call. + */ + if (0 == strncmp(logmsg, "Incoming call", 13)) + { + called = IN; + cur->call[called].calls += 1; + have_calls = 1; + continue; + } + /* + * Handle end of call. Pick up the connect time. + */ + if (0 == strncmp(logmsg, "Call complete", 13)) + { + cur->call[called].succs += 1; + if (NULL == (s = strchr(logmsg, '('))) + continue; + cur->call[called].connect_time += atof(s+1); + continue; + } + /* + * If we reached here, this must have been a file transfer + * record. Count it in the field corresponding to the + * direction of the transfer. Count bytes transferred and + * the time to transfer as well. + */ + have_files = 1; + cur->call[called].flow[sent].files += 1; + if (NULL == (s = strchr(logmsg, ' '))) + continue; + cur->call[called].flow[sent].bytes += atol(++s); + if (NULL == (s = strchr(s, ' '))) + continue; + if (NULL == (s = strpbrk(s, "0123456789"))) + continue; + cur->call[called].flow[sent].time += atof(s); + } + argc -= 1; + argv += 1; + if(Log != stdin) + fclose(Log); + } + + /* + * *********** + * * REPORTS * + * *********** + */ + + /* + * Truncate the Hostnames to 8 characters at most. + */ + for (cur = hosts; cur != NULL; cur = cur->next) + cur->Hostname[8] = '\0'; + +#if _DEBUG_ + printf("\n"); +#endif + + /* + * Summary report + * + * I know, this code could be tightened (rbd)... + */ + if(show_summary) + { + char t1[32], t2[32], t3[32], t4[32], t5[32]; + long ib, ob, b, rf, sf; + long t_ib=0, t_ob=0, t_b=0, t_rf=0, t_sf=0; + double it, ot, ir, or; + double t_it=0.0, t_ot=0.0; + int nhosts = 0; + + printf("\n\ + Remote ------- Bytes -------- --- Time ---- -- Avg CPS -- -- Files --\n"); + printf("\ + Host Rcvd Sent Total Rcvd Sent Rcvd Sent Rcvd Sent\n"); + printf("\ +-------- ------- ------- ------- ------ ------ ------ ------ ----- -----\n"); + for (cur = hosts; cur != NULL; cur = cur->next) + { + ib = (cur->call[IN].flow[IN].bytes + + cur->call[OUT].flow[IN].bytes); + fmbytes(ib, t1); + t_ib += ib; + + ob = (cur->call[IN].flow[OUT].bytes + + cur->call[OUT].flow[OUT].bytes); + fmbytes(ob, t2); + t_ob += ob; + + b = ib + ob; + fmbytes(b, t3); + t_b += b; + + it = cur->call[IN].flow[IN].time + + cur->call[OUT].flow[IN].time; + fmtime(it, t4); + t_it += it; + + ot = cur->call[IN].flow[OUT].time + + cur->call[OUT].flow[OUT].time; + fmtime(ot, t5); + t_ot += ot; + + rf = cur->call[IN].flow[IN].files + + cur->call[OUT].flow[IN].files; + t_rf += rf; + + sf = cur->call[IN].flow[OUT].files + + cur->call[OUT].flow[OUT].files; + t_sf += sf; + + ir = (it == 0.0) ? 0.0 : (ib / it); + or = (ot == 0.0) ? 0.0 : (ob / ot); + + printf("%-8s %7s %7s %7s %6s %6s %6.1f %6.1f %5d %5d\n", + cur->Hostname, + t1, t2, t3, t4, t5, + ir, or, rf, sf); + } + + if(nhosts > 1) + { + fmbytes(t_ib, t1); + fmbytes(t_ob, t2); + fmbytes(t_b, t3); + fmtime(t_it, t4); + fmtime(t_ot, t5); + ir = (t_it == 0.0) ? 0.0 : (t_ib / t_it); + or = (t_ot == 0.0) ? 0.0 : (t_ob / t_ot); + + printf("\ +-------- ------- ------- ------- ------ ------ ------ ------ ----- -----\n"); + printf("\ +Totals %7s %7s %7s %6s %6s %6.1f %6.1f %5d %5d\n", + t1, t2, t3, t4, t5, + ir, or, t_rf, t_sf); + } + } + + + /* + * Call statistics report + */ + if(show_calls && have_calls) + { + char t1[32], t2[32]; + + printf("\nCall statistics:\n"); + printf("\ + sysname callto failto totime callfm failfm fmtime\n"); + printf("\ + -------- ------ ------ -------- ------ ------ --------\n"); + for (cur = hosts; cur != NULL; cur = cur->next) + { + fmtime(cur->call[OUT].connect_time, t1); + fmtime(cur->call[IN].connect_time, t2), + printf(" %-8s %6d %6d %8s %6d %6d %8s\n", + cur->Hostname, + cur->call[OUT].calls, + cur->call[OUT].calls - cur->call[OUT].succs, + t1, + cur->call[IN].calls, + cur->call[IN].calls - cur->call[IN].succs, + t2); + } + } + + /* + * File statistics report + */ + if(show_files && have_files) + { + char t1[32], t2[32]; + + for (sent = IN; sent <= OUT; ++sent) + { + printf(file_hdr[sent]); + printf(" sysname files bytes xfr time byte/s\n"); + printf(" -------- ------ -------- -------- ------\n"); + for (cur = hosts; cur != NULL; cur = cur->next) + { + double rate; + double time; + + time = cur->call[IN].flow[sent].time + + cur->call[OUT].flow[sent].time; + if (time == 0.0) + continue; + rate = (cur->call[IN].flow[sent].bytes + + cur->call[OUT].flow[sent].bytes) / time; + fmbytes((cur->call[IN].flow[sent].bytes + + cur->call[OUT].flow[sent].bytes), t1); + fmtime((cur->call[IN].flow[sent].time + + cur->call[OUT].flow[sent].time), t2); + printf(" %-8s %6d %8s %8s %6.1f\n", + cur->Hostname, + cur->call[IN].flow[sent].files + + cur->call[OUT].flow[sent].files, + t1, t2, rate); + } + } + } + + /* + * Efficiency report + */ + if (show_efficiency && have_files) + { + char t1[32], t2[32], t3[32]; + double total, flow; + + printf("\nEfficiency:\n"); + printf(" sysname conntime flowtime ovhdtime eff. %%\n"); + printf(" -------- -------- -------- -------- ------\n"); + for (cur = hosts; cur != NULL; cur = cur->next) + { + total = cur->call[IN].connect_time + cur->call[OUT].connect_time; + flow = cur->call[IN].flow[IN].time + cur->call[IN].flow[OUT].time + + cur->call[OUT].flow[IN].time + cur->call[OUT].flow[OUT].time; + fmtime(total, t1); + fmtime(flow, t2); + fmtime((total-flow), t3); + printf(" %-8s %8s %8s %8s %5.1f%%\n", + cur->Hostname, t1, t2, t3, ((flow / total) * 100.0)); + } + } + + /* + * Command execution report + */ + if (show_commands & have_commands) + { + printf("\nCommand executions:\n"); + printf(" sysname rmail rnews other\n"); + printf(" -------- ------ ------ ------\n"); + for (cur = hosts; cur != NULL; cur = cur->next) + { + int rmail, rnews, other; + + if (cur->cmds == NULL) + continue; + rmail = rnews = other = 0; + for (cmd = cur->cmds; cmd != NULL; cmd = cmd->next) + { + if (strcmp(cmd->Commandname, "rmail") == 0) + rmail += cmd->count; + else if (strcmp(cmd->Commandname, "rnews") == 0) + rnews += cmd->count; + else + other += cmd->count; + } + printf(" %-8s %6d %6d %6d\n", cur->Hostname, + rmail, rnews, other); + } + } + return; + + usage: + fprintf(stderr, + "Usage uurate [-cfexai] [-h hostname] [logfile ... logfile]\n"); + fprintf(stderr,"where:\t-c\tReport call statistics\n"); + fprintf(stderr, "\t-f\tReport file transfer statistics\n"); + fprintf(stderr, "\t-e\tReport efficiency statistics\n"); + fprintf(stderr, "\t-x\tReport command execution statistics\n"); + fprintf(stderr, "\t-a\tAll of the above reports\n"); + fprintf(stderr, "\t-h host\tReport activities involving ONLY host\n"); + fprintf(stderr, "\t-i\tRead log info from standard input\n"); + fprintf(stderr, + "If no report options given, a compact summary report is given.\n"); + fprintf(stderr, + "If neither -i nor logfiles given, defaults to reading from\n"); + fprintf(stderr, "%s and %s\n\n", LOGFILE, STATFILE); +} + +/* + * fmtime() - Format time in hours & minutes; + */ +static void fmtime(dsec, buf) + double dsec; + char *buf; +{ + long hrs, min, lsec; + + lsec = dsec; + hrs = lsec / 3600L; + min = (lsec - (hrs * 3600L)) / 60L; + + sprintf(buf, "%02ld:%02ld", hrs, min); +} + +/* + * fmbytes - Format size in bytes + */ +static void fmbytes(n, buf) + unsigned long n; + char *buf; +{ + char t; + double s = n; + + if(s >= 10239897.6) /* More than 9999.9K ? */ + { + s = (double)n / 1048576.0; /* Yes, display in Megabytes */ + t = 'M'; + } + else + { + s = (double)n / 1024.0; /* Display in Kilobytes */ + t = 'K'; + } + + sprintf(buf, "%.1f%c", s, t); +} + diff --git a/gnu/libexec/uucp/contrib/uurate.man b/gnu/libexec/uucp/contrib/uurate.man new file mode 100644 index 0000000000..9f33ef3038 --- /dev/null +++ b/gnu/libexec/uucp/contrib/uurate.man @@ -0,0 +1,217 @@ +.TH uurate 1 +.SH NAME +uurate \- Report Taylor UUCP statistics +.SH SYNOPSIS +.BR uurate " [ " "\-cfexai" " ] [ " "\-h " +.I host +.RI " ] [ " "logfile..." " ] " +.PP +or simply, +.PP +.B uurate +.PP +for a traffic summary report. +.SH DESCRIPTION +The +.I uurate +command provides tabular summary reports on the operation of the +Taylor UUCP system. Data is taken from the currently active log +files, standard input, or from a list of log files given on the +command line. Output is in the form of tabular reports summarizing +call, file transfer, and command execution +.RI "(" "uuxqt" ")" +activity. +.PP +The log files given to +.I uurate +must be in the ``Taylor'' format. Also, note that call and file +transfer activities are logged in separate files, nominally called +.I Log +and +.I Stats, +respectively. For reports to be meaningful, the +.I Log +and +.I Stats +files should be given to +.I uurate +together, and cover the same time period. +.PP +If neither the +.B \-i +option nor any +.I logfile +options are given, +.I uurate +defaults to taking its input from the current Taylor +.I Log +and +.I Stats +files, as defined at compilation time. +This is the normal mode of operation. +.PP +The reporting options described below can be used to select +the set of reports desired. If no options are given, the +.B call +and +.B file +reports are displayed. If there is no relevant data for a particular +report or host, that report or host will be supressed. +.SH OPTIONS +The following options may be given to +.I uurate: +.TP 5 +.B \-c +Report on call statistics. Requires data from a +.I Log +file. +.TP 5 +.B \-f +Report on file transfer statistics. Requires data from a +.I Stats +file. +.TP 5 +.B \-e +Report on efficiency (total connect time versus time spent transferring +files). Requires data from both a +.I Log +and a +.I Stats +file, and they must span the same time period. +.TP 5 +.B \-x +Report on remote execution requests (e.g., +.IR rmail ")." +Requires data from a +.I Log +file. +.TP 5 +.B \-a +All reports. Identical to +.B \-cfex. +.TP 5 +.BI "\-h " "host" +Restrict report output to +.I host. +.SH "DESCRIPTION OF REPORTS" +There are four reports available: the call, file transfer, efficiency, +and remote execution reports. Each may be selected by a command line +option. All reports may be selected via the option +.B \-a. +If no report selection options are given, +.I uurate +displays a compact traffic summary report (see below). +.SS "Summary report" +If no report options are given, +.I uurate +displays a traffic summary report. This is particularly useful in daily +.I cron +jobs which report on errors and the like. Traffic statistics for each +active system is reported on a single line. If more than one system was +active, a 'totals' line is included at the end of the report. +.SS "Call report" +The call report gives statistics on inbound and outbound calls for +each active host system. The fields are described below: +.br +.nf +.in +.5i +.ta 1.0i +.BR "sysname " "UUCP node name of neighbor host system" +.BR "callto " "Outbound calls attempted to that system" +.BR "failto " "Failed outbound calls to that system" +.BR "totime " "Connect time (sec.) on outbound calls" +.BR "callfm " "Inbound calls attempted by that system" +.BR "failfm " "Failed inbound calls from that system" +.BR "fmtime " "Connect time (sec.) on inbound calls" +.in -.5 +.SS "File transfer reports" +The file transfer reports give statistics on inbound and +outbound file transfers (regardless of which end initiated the transfer) +for each active host system. There are two reports, one for files +sent to the remote system and one for files received from the remote +system. The fields in each report are described below: +.br +.nf +.in +.5i +.ta 1.0i +.BR "sysname " "UUCP node name of neighbor host system" +.BR "files " "Number of files transferred" +.BR "bytes " "Total size (bytes) of files transferred" +.BR "seconds " "Total time (sec.) to transfer files" +.BR "byte/sec " "Average transfer rate (bytes/sec)" +.in -.5 +.SS "Efficiency report" +The efficiency report describes the utilization of the links +to each active remote system, giving the ratio of total connect time +to the time spent actually transferring files. +The fields are described below: +.br +.nf +.in +.5i +.ta 1.0i +.BR "sysname " "UUCP node name of neighbor host system" +.BR "conntime " "Total connect time for that system" +.BR "flowtime " "Total file transfer time for that system" +.BR "ovhdtime " "Connect time not used to transfer files" +.BR "effcy (%) " "Ratio of connect time to transfer time" +.in -.5 +.SS "Remote execution report" +The remote execution report describes remotely +requested command executions from each active host system. +Executions of +.I rmail +and +.I rnews +are the most common, and are detailed separately. The fields +are described below: +.br +.nf +.in +.5i +.ta 1.0i +.BR "sysname " "UUCP node name of neighbor host system" +.BR "rmail " "Number of rmail requests from that system" +.BR "rnews " "Number of rnews requests from that system" +.BR "other " "Number of other requests from that system" +.in -.5i +.SS FILES +The file names below may be changed at compilation time or by the +configuration file, so these are only approximations. +.br +.nf +.in +.5in +.ta 2.0i +.IR "/usr/spool/uucp/Log " "Taylor format call/execution log" +.IR "/usr/spool/uucp/Stats " "Taylor format file transfer log" +.SS "SEE ALSO" +.IR uucico "(8)" +.SS BUGS +Does not understand older (V2, BNU) logging formats. Anyone care to +volunteer to add this? I don't use the stuff myself. +.PP +The entries that Taylor UUCP makes in the log file for incoming calls +don't have a host name. This confuses +.I uurate +into thinking that the calls came in for system "-". This may require +a change to Taylor logging. +.PP +Should check the configuration file to locate the currently active +.I Log +and +.I Stats +files when using them for default inputs. Instead, it uses the +compile-time settings only. +.PP +Should report packet protocol error statistics by host and +protocol type. +.SS AUTHOR +Robert B. Denny (denny@alisa.com) +.br +Loosely based on the DECUS UUCP program +.I uurate +by Mark Pizzolato. + + + + + + diff --git a/gnu/libexec/uucp/contrib/uureroute b/gnu/libexec/uucp/contrib/uureroute new file mode 100644 index 0000000000..3eeb654e1e --- /dev/null +++ b/gnu/libexec/uucp/contrib/uureroute @@ -0,0 +1,91 @@ +#!/usr/local/bin/perl +eval ' exec /usr/local/bin/perl $0 "$@" ' + if $running_under_some_shell; + +# From a script by +# Newsgroups: comp.sources.misc +# Subject: v28i073: uureroute - Reroute HDB queued mail, Part01/01 +# Date: 26 Feb 92 02:28:37 GMT +# +# This is a Honey DanBer specific routine written in perl to reroute all +# mail queued up for a specific host. It needs to be run as "root" since +# uucp will not allow itself to remove others requests. +# +# Revision *** 92/21/09: Francois Pinard +# 1. adapted for Taylor UUCP +# +# Revision 1.3 91/10/08 09:01:21 src +# 1. Rewritten in perl +# 2. Add -v option for debugging. +# +# Revision 1.2 91/10/07 23:57:42 root +# 1. Fix mail program path. +# 2. Truncate directory name to 7 characters + +($progname = $0) =~ s!.*/!!; # save this very early + +$USAGE = " +# Reroute uucp mail +# +# Usage: $progname [-v] host [host...] +# +# Options Argument Description +# -v Verbose (doesn't execute /bin/sh) +# +"; + +$UUSTAT = "/usr/local/bin/uustat"; +$SHELL = "/bin/sh"; +$SMAIL = "/bin/smail"; + +sub usage +{ + die join ("\n", @_) . "\n$USAGE\n"; +} + +do "getopts.pl"; + +&usage ("Invalid Option") unless do Getopts ("vV"); + +$verbose = ($opt_v ? '-v' : ()); +$suffix = ($verbose ? '' : $$); + +&usage ("No system specified") if $#ARGV < 0; + +if (!$verbose) +{ + open (SHELL, "| $SHELL"); + select SHELL; +} + +while ($system = shift) +{ + $sysprefix = substr ($system, 0, 7); + $directory = "/usr/spool/uucp/$sysprefix"; + open (UUSTAT, "$UUSTAT -s $system -c rmail |"); + print "set -ex\n"; + while () + { + ($jobid, ) = split; + ($cfile) = substr ($jobid, length ($jobid) - 5); + $cfilename = "$directory/C./C.$cfile"; + open (CFILE, $cfilename) || die "Cannot open $cfilename\n"; + $_ = ; + close CFILE; + if (/^E D\.(....) [^ ]+ [^ ]+ -CR D\.\1 0666 [^ ]+ 0 rmail (.*)/) + { + $datafile = "$directory/D./D.$1"; + $address = $2; + } + else + { + print STDERR; + die "Cannot parse previous line from $cfilename\n"; + } + print "$SMAIL -R $system!$address < $datafile && $UUSTAT -k $jobid\n"; + } + close UUSTAT; +} +close SHELL unless $verbose; + +exit 0; diff --git a/gnu/libexec/uucp/contrib/uusnap.c b/gnu/libexec/uucp/contrib/uusnap.c new file mode 100644 index 0000000000..0f878c1e05 --- /dev/null +++ b/gnu/libexec/uucp/contrib/uusnap.c @@ -0,0 +1,321 @@ +/* uusnap.c + (c) 1992 Heiko W.Rupp hwr@pilhuhn.ka.sub.org + uusnap is a tool to display the activities of the connected + systems. + + Put a file uusnap.systems in NEWCONFIGDIR (see Makefile), in which + the systems, you want to monitor are listed, one on a single line. + The sequence of the files there determine the sequence of the + listing. + + At the moment it only works with taylor config and taylor dirs + + compile it form the Makefile or: + cc -c -g -pipe -O -I. -I. -DNEWCONFIGLIB=\"/usr/local/lib/uucp\" uusnap.c + cc -o uusnap uusnap.o + For this, uusnap.[ch] must be in the same directory as uucp.h and so. + + uusnap must have read access to SPOOLDIR/.Status in order to work. +*/ + +#define MAXSYS 30 /* maximum number of systems */ +#define WAIT_NORMAL 10 /* wait period if noone is talking */ +#define WAIT_TALKING 2 /* refresh display every second if */ + /* someone is talking with us */ + +#include "uucp.h" +#if USE_RCS_ID +char uusnap_rcsid[] = "$Id: uusnap.c,v 1.1 1993/08/04 19:31:43 jtc Exp $"; +#endif + +#include +#include +#include +#include + +extern char *ctime(time_t*); + +struct sysInfo { + char sysname[10]; /* name of the system to watch */ + char *statfile; /* name of its status file */ + char *spooldir; /* root of its spooldir */ + int in; /* number of unprocessed in-files */ + int out; /* number of files to send them */ + time_t last; /* last poll time */ + time_t next; /* time of next poll */ + time_t lastidir; /* time of last in-spooldir access */ + time_t lastodir; /* time of last outgoing spd acc */ + time_t laststat; /* time of last status file access */ + int status; /* status of the system */ + int num_retries; /* number of retries */ +}; + +struct sysInfo Systems[MAXSYS]; + + +/* I have extend the system status. If time for the specified system + is Never, I say so. To get this to work, one also should extend + uucico.c. It is not important to do this. With the normal uucico, + one only gets no status. +*/ + +const char *azStatus[] = /* Status codes as defined by uucico */ +{ /* listing them here instead of */ + "Conversation complete", /* including the appropriate file */ + "Port unavailable", /* reduces the size of the executable */ + "Dial failed", + "Login failed", + "Handshake failed", + "Call failed", + "Talking", + "Wrong time to call", + "Time to call = Never !" +}; + +main() +{ + int i; + i=get_systems(); + display_info(i); + + exit(0); +} + +int +get_systems() +{ + char filename[1024]; + char fn[1024]; + char line[80]; + FILE *fp; + int i=0; + int j; + struct stat stbuf; + struct sysInfo sys; + + strcpy(filename,NEWCONFIGLIB); + strcat(filename,"/uusnap.systems"); + if ((fp=fopen(filename,"r"))!=NULL) { + while (fgets(line,80,fp)!=NULL) { + *(rindex(line,'\n'))='\0'; + strcpy(sys.sysname,line); /* get the name of the system */ + strcpy(fn,SPOOLDIR); /* get the name of the statusfile */ + strcat(fn,"/.Status/"); + strcat(fn,line); + sys.statfile=malloc(strlen(fn)+1); + strcpy(sys.statfile,fn); + strcpy(fn,SPOOLDIR); /* get the name of the spooldir */ + strcat(fn,"/"); + strcat(fn,line); + sys.spooldir=malloc(strlen(fn)+1); + strcpy(sys.spooldir,fn); + sys.laststat=0; + sys.lastidir=sys.lastodir=0; + Systems[i]=sys; /* get_stat_for_system needs it */ + get_stat_for_system(i); /* now get the system status */ + get_inq_num(i,TRUE); /* number of unprocessed files */ + get_outq_num(i,TRUE); /* number of files to send */ + i++; + } + fclose(fp); + } + else { + fprintf(stderr,"Can't open %s \n",filename); + exit(1); + } + return i; +} + + + +display_info(int numSys) +{ + char *filename; + int sysnum; + FILE *fp; + char contentline[80]; + char isTalking=FALSE; + struct stat stbuf; + struct sysInfo sys; + time_t time; + + filename = (char*)malloc(1024); + if (filename == NULL) { + fprintf(stderr, "Can't malloc 1024 bytes"); + exit(1); + } + + while(TRUE) { + display_headline(); + for (sysnum=0;sysnum sys.laststat) { + get_stat_for_system(sysnum); + } + if(display_status_line(sysnum)==1) + isTalking=TRUE; + } + if (isTalking) { + sleep(WAIT_TALKING); + isTalking = FALSE; + } + else + sleep(WAIT_NORMAL); /* wait a bit */ + } + return 0; +} + +int +display_status_line(int sn) +{ + char *time_s; + + int sys_stat,num_retries,wait; + int i; + time_t last_time; + time_t next_time; + + struct sysInfo sys; + + sys = Systems[sn]; + + printf("%10s ",sys.sysname); + get_inq_num(sn); + if (sys.in==0) + printf(" "); + else + printf("%3d ",sys.in); + get_outq_num(sn); + if (sys.out==0) + printf(" "); + else + printf("%3d ",sys.out); + time_s = ctime(&sys.last); + time_s = time_s + 11; + *(time_s+8)='\0'; + printf("%8s ",time_s); /* time of last poll */ + time_s = ctime(&sys.next); + time_s = time_s + 11; + *(time_s+8)='\0'; + if (sys.last == sys.next) + printf(" "); + else + printf("%8s ",time_s); /* time of next poll */ + if (sys.num_retries==0) + printf(" "); + else + printf("%2d ",sys.num_retries); + if (sys_stat==6) /* system is talking */ + printf("\E[7m"); /* reverse video on */ + printf("%s",azStatus[sys.status]); + if (sys.status==6) { + printf("\E[m\n"); /* reverse video off */ + return 1; + } + else { + printf("\n"); + return 0; + } +} + + +display_headline() +{ + printf("\E[;H\E[2J"); /* clear screen */ + printf("\E[7muusnap (press CTRL-C to escape)\E[m \n\n"); + printf(" System #in #out last next #ret Status\n"); + return 0; +} + +get_inq_num(int num,char firstTime) +{ + int i=0; + char filename[1024]; + struct stat stbuf; + DIR *dirp; + + strcpy(filename,Systems[num].spooldir); + strcat(filename,"/X./."); + stat(filename,&stbuf); + if ((stbuf.st_mtime > Systems[num].lastidir) || (firstTime)) { + if ((dirp=opendir(filename))!=NULL) { + while(readdir(dirp)) + i++; + closedir(dirp); + stat(filename,&stbuf); + Systems[num].lastidir=stbuf.st_mtime; + } + else { + fprintf(stderr,"Can't open %s \n",filename); + exit(1); + } + if (i>=2) + i-=2; /* correct . and .. */ + Systems[num].in=i; + } + return 0; +} + +get_outq_num(int sys,char firstTime) +{ + int i=0; + char filename[1024]; + struct stat stbuf; + DIR *dirp; + + strcpy(filename,Systems[sys].spooldir); + strcat(filename,"/C./."); + stat(filename,&stbuf); + if ((stbuf.st_mtime > Systems[sys].lastodir) || (firstTime)) { + if ((dirp=opendir(filename))!=NULL) { + while(readdir(dirp)) + i++; + closedir(dirp); + stat(filename,&stbuf); + Systems[sys].lastodir=stbuf.st_mtime; + } + else { + fprintf(stderr,"Can't open %s \n",filename); + exit(1); + } + if (i>=2) + i-=2; /* correct . and .. */ + Systems[sys].out=i; + } + return 0; +} + +get_stat_for_system(int i) +{ + char fn[80]; + struct sysInfo sys; + struct stat stbuf; + FILE *fp; + time_t wait; + + sys = Systems[i]; + stat(sys.statfile,&stbuf); + if (stbuf.st_atime > sys.laststat) { + if ((fp=fopen(sys.statfile,"r"))!=NULL) { + fgets(fn,80,fp); + fclose(fp); + sscanf(fn,"%d %d %ld %d", + &sys.status, + &sys.num_retries, + &sys.last, + &wait); + sys.next=sys.last+wait; + } + else { + sys.status=0; + sys.num_retries=0; + sys.last=0; + sys.next=0; + } + stat(sys.statfile,&stbuf); + sys.laststat=stbuf.st_atime; + } + Systems[i] = sys; + return 0; +} diff --git a/gnu/libexec/uucp/contrib/uutraf b/gnu/libexec/uucp/contrib/uutraf new file mode 100644 index 0000000000..8b56d0f6b4 --- /dev/null +++ b/gnu/libexec/uucp/contrib/uutraf @@ -0,0 +1,203 @@ +#!/usr/local/bin/perl +# uutraf.pl -- UUCP Traffic Analyzer +# SCCS Status : @(#)@ uutraf 1.7 +# Author : Johan Vromans +# Created On : *** +# Last Modified By: Johan Vromans +# Last Modified On: Wed Feb 26 08:52:56 1992 +# Update Count : 4 +# Status : OK +# Requires: : Perl V4 or later + +# Reads UUCP syslog, and generates a report from it. +# +# Created by Johan Vromans +# Loosely based on an idea by Greg Hackney (hack@texbell.swbt.com) + +# Usage: uutraf [-taylor|-hdb|-bnu|-bsd] [syslog] + +# Logfile formats: +# +# BSD: +# +# jv mhres (2/23-5:18) (698818735) received 135 b 2 secs +# root mhres (2/23-5:19) (698818742) sent 2365 b 3 secs, Pk: 38, Rxmt: 0 +# +# HDB: +# +# uunet!uucp M (12/10-09:04:22) (C,16390,1) [ttyXX] <- 2371 / 5.000 secs, \ +# 474 bytes/sec +# +# Taylor: +# +# jv mhres (1992-02-24 20:49:04.06) sent 16234 bytes in 148.780 seconds \ +# (109 bytes/sec) +# jv mhres (1992-02-24 21:04:05.76) received 449 bytes in 6.550 seconds \ +# (68 bytes/sec) + +$uucp_type = "gnu"; + +%hosts = (); # hosts seen +%bytes_in = (); # of bytes received from host +%bytes_out = (); # of bytes sent to host +%secs_in = (); # of seconds connect for recving +%secs_out = (); # of seconds connect for sending +%files_in = (); # of input requests +%files_out = (); # of output requests + +# read info, break the lines and tally + +if ( $ARGV[0] =~ /^-/ ) { + ($uucp_type = substr (shift (@ARGV), 1)) =~ tr/A-Z/a-z/; +} + +if ( $uucp_type eq "taylor" || $uucp_type eq "gnu" ) { + @ARGV = ("/usr/spool/uucp/Stats") unless $#ARGV >= 0; + $pat = "^[^ ]+ ([^ ]+) \\(([-0-9:\\/ .]+)\\) " . + "(sent|received) (\\d+) bytes in (\\d+)\\.(\\d+) seconds"; + $uucp_type = 0; + $recv = "received"; +} +elsif ( $uucp_type eq "hdb" || $uucp_type eq "bnu" ) { + @ARGV = ("/usr/spool/uucp/.Admin/xferstats") unless $#ARGV >= 0; + $pat = "^([^!]+)![^(]+\\(([-0-9:\\/]+)\\).+([<>])-? " . + "(\\d+) \\/ (\\d+)\\.(\\d+) secs"; + $uucp_type = 1; + $recv = "<"; +} +elsif ( $uucp_type eq "bsd" || $uucp_type eq "v7" ) { + @ARGV = ("/usr/spool/uucp/SYSLOG") unless $#ARGV >= 0; + $pat = "^[^ ]+ ([^ ]+) \\(([-0-9:\\/]+)\\) \\([^)]+\\) " . + "(sent|received) (\\d+) b (\\d+) secs"; + $uucp_type = 2; + $recv = "received"; +} +else { + die ("Unknown UUCP type: $uucp_type\n"); +} + +$garbage = 0; + +while ( <> ) { + unless ( /$pat/o ) { + print STDERR "Possible garbage: $_"; + if ( $garbage++ > 10 ) { + die ("Too much garbage; wrong UUCP type?\n"); + } + next; + } + + # gather timestamps + $last_date = $2; + $first_date = $last_date unless defined $first_date; + + # initialize new hosts + unless ( defined $hosts{$1} ) { + $hosts{$1} = $files_in{$1} = $files_out{$1} = + $bytes_in{$1} = $bytes_out{$1} = + $secs_in{$1} = $secs_out{$1} = 0; + } + + # Taylor and HDB have milliseconds, BSD has not. + $secs = ($uucp_type == 2) ? ($5 + ($5 == 0 ? 0.5 : 0)) : ($5 + $6/1000); + + # tally + if ( $3 eq $recv ) { # recv + $bytes_in{$1} += $4; + $files_in{$1}++; + $secs_in{$1} += $secs; + } + else { # xmit + $bytes_out{$1} += $4; + $files_out{$1}++; + $secs_out{$1} += $secs; + } + $garbage = 0; +} + +@hosts = keys (%hosts); +die ("No info found, stopped\n") if $#hosts < 0; + +################ report section ################ + +$thishost = &gethostname(); +$thishost = (defined $thishost) ? "on node $thishost" : "report"; + +if ( $uucp_type eq 0 ) { # Taylor UUCP + substr ($first_date, 16) = ""; + substr ($last_date, 16) = ""; +} + +format std_head = +@||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +"UUCP traffic $thishost from $first_date to $last_date" + +Remote -----------K-Bytes----------- ----Hours---- --Avg CPS-- --Files-- + Host Recv Sent Total Recv Sent Recv Sent Recv Sent +. +format std_out = +@<<<<<<< @>>>>>>>> @>>>>>>>> @>>>>>>>> @>>>>> @>>>>> @>>>> @>>>> @>>> @>>> +$Zhost, $Zi_bytes, $Zo_bytes, $Zt_bytes, $Zi_hrs, $Zo_hrs, $Zi_acps, $Zo_acps, $Zi_count, $Zo_count +. + +$^ = "std_head"; +$~ = "std_out"; + +&print_dashes (); + +reset "T"; # reset totals + +foreach $host (@hosts) { + &print_line ($host, $bytes_in{$host}, $bytes_out{$host}, + $secs_in{$host}, $secs_out{$host}, + $files_in{$host}, $files_out{$host}); + +} + +&print_dashes (); +&print_line ("Total", $Ti_bytes, $To_bytes, + $Ti_secs, $To_secs, $Ti_count, $To_count); + +################ that's it ################ + +sub print_line { + reset "Z"; # reset print fields + local ($Zhost, + $Zi_bytes, $Zo_bytes, + $Zi_secs, $Zo_secs, + $Zi_count, $Zo_count) = @_; + $Ti_bytes += $Zi_bytes; + $To_bytes += $Zo_bytes; + $Zt_bytes = $Zi_bytes + $Zo_bytes; + $Tt_bytes += $Zt_bytes; + $Zi_acps = ($Zi_secs > 0) ? sprintf ("%.0f", $Zi_bytes/$Zi_secs) : "0"; + $Zo_acps = ($Zo_secs > 0) ? sprintf ("%.0f", $Zo_bytes/$Zo_secs) : "0"; + $Zi_bytes = sprintf ("%.1f", $Zi_bytes/1000); + $Zo_bytes = sprintf ("%.1f", $Zo_bytes/1000); + $Zt_bytes = sprintf ("%.1f", $Zt_bytes/1000); + $Zi_hrs = sprintf ("%.1f", $Zi_secs/3600); + $Zo_hrs = sprintf ("%.1f", $Zo_secs/3600); + $Ti_secs += $Zi_secs; + $To_secs += $Zo_secs; + $Ti_count += $Zi_count; + $To_count += $Zo_count; + write; +} + +sub print_dashes { + $Zhost = $Zi_bytes = $Zo_bytes = $Zt_bytes = + $Zi_hrs = $Zo_hrs = $Zi_acps = $Zo_acps = $Zi_count = $Zo_count = + "------------"; + write; + # easy, isn't it? +} + +################ missing ################ + +sub gethostname { + $ENV{"SHELL"} = "/bin/sh"; + $try = `uuname -l 2>/dev/null`; + chop $try; + return $+ if $try =~ /^[-.\w]+$/; + return undef; +} diff --git a/gnu/libexec/uucp/contrib/uutry b/gnu/libexec/uucp/contrib/uutry new file mode 100644 index 0000000000..bc0cf299e9 --- /dev/null +++ b/gnu/libexec/uucp/contrib/uutry @@ -0,0 +1,43 @@ +#!/bin/sh +# +# This script was hacked together by Marc Evans (marc@Synergytics.Com) +# I claim no copyright to it and don't really care what people do +# with it, hence, it is public domain. I take no responsibility for +# for happens if you use this script, providing no warentee. This +# section of the comments may be removed if you so desire. +# +# Usage: +# uutry [-x#] systemname +# where '-x#' has the value [0-9], higher values providing more detail + +# +# The following variables should be gropped from the configuration +# files rather then being hard coded here. +# +Spool=/usr/spool/uucp +Lib=/usr/lib/uucp +Status=$Spool/.Status +Debug=$Spool/Debug +Uucico=$lib/uucico +# +# Default option values +# +x="-x5" +s="" + +for i in $* ; do + case $i in + -x*) x="$i" ;; + *) s="$i" ;; + esac +done + +if [ $s != "" ]; then + rm -f $Status/$s + $Uucico -r1 $x -s$s & + >$Debug + tail -f $Debug +else + echo "Usage: uutry systemname" + exit 1 +fi diff --git a/gnu/libexec/uucp/contrib/xc-conf.h-dist b/gnu/libexec/uucp/contrib/xc-conf.h-dist new file mode 100644 index 0000000000..8810dd78d9 --- /dev/null +++ b/gnu/libexec/uucp/contrib/xc-conf.h-dist @@ -0,0 +1,38 @@ +/* + * ************* + * * XC-CONF.H * + * ************* + * + * Configuration file for xchat 1.1. Edit this file prior to make-ing + * xchat. + * + * History: + * Bob Denny - Tue Sep 1 11:42:54 1992 + */ + +/* + * Edit this to reflect the relative location of xchat sources to + * the main Taylor UUCP source directory. As distributed, xchat + * is in the ./contrib sub-directory under the main Taylor UUCP + * directory. Therefore, Taylor's conf.h is in our parent directory. + */ +#include "../conf.h" + +/* + * The following definition establishes the default path to the + * scripts used by xchat. You may lleave this blank (""), but + * the command line given to xchat (e.g., in the 'sys' file entry) + * must specify a full (absolute) path name to the script to be + * executed. Normally, this is the same place you put your config + * and system files for UUCP. + */ +#define SCRIPT_DIR "/usr/local/conf/uucp/" /* MUST HAVE TRAILING "/" */ + +/* + * The following definition establishes the default path to the + * log files that are produced by the 'dbgfile' statement. Normally + * this is the same location you configured Taylor UUCP to put its + * log files. + */ +#define LOG_DIR "/usr/spool/uucp/" /* MUST HAVE TRAILING "/" */ + diff --git a/gnu/libexec/uucp/contrib/xchat.c b/gnu/libexec/uucp/contrib/xchat.c new file mode 100644 index 0000000000..cfb4d35966 --- /dev/null +++ b/gnu/libexec/uucp/contrib/xchat.c @@ -0,0 +1,1444 @@ +/* + * *********** + * * XCHAT.C * + * *********** + * + * Extended chat processor for Taylor UUCP. See accompanying documentation. + * + * Written by: + * Bob Denny (denny@alisa.com) + * Based on code in DECUS UUCP (for VAX/VMS) + * + * History: + * Version 1.0 shipped with Taylor 1.03. No configuration info inside. + * + * Bob Denny - Sun Aug 30 18:41:30 1992 + * V1.1 - long overdue changes for other systems. Rip out interval + * timer code, use timer code from Taylor UUCP, use select() + * for timed reads. Use Taylor UUCP "conf.h" file to set + * configuration for this program. Add defaulting of script + * and log file paths. + * + * Bugs: + * Does not support BSD terminal I/O. Anyone care to add it? + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xc-conf.h" + +/* + * Pick a timing routine to use, as done in Taylor UUCP. + */ +#if HAVE_USLEEP || HAVE_NAP || HAVE_NAPMS || HAVE_POLL +#define USE_SELECT_TIMER 0 +#else +#define USE_SELECT_TIMER HAVE_SELECT +#if USE_SELECT_TIMER +#include +#endif +#endif + +#if HAVE_USLEEP || HAVE_NAP || HAVE_NAPMS +#undef HAVE_POLL +#define HAVE_POLL 0 +#endif + +#if HAVE_USLEEP || HAVE_NAP +#undef HAVE_NAPMS +#define HAVE_NAPMS 0 +#endif + +#if HAVE_USLEEP +#undef HAVE_NAP +#define HAVE_NAP 0 +#endif + +static int ttblind(); +static int ttcd(); + +/* script entry -- "compiled" form of dial, hangup, or login script */ + +struct script { + struct script *next; /* pointer to next entry, or null */ + int opcode; /* numeric opcode */ + char *strprm; /* pointer to string param */ + long intprm; /* integer parameter */ + char *newstate; /* new state name */ +}; + +/* opcode definition array element -- one for each possible opcode */ + +struct script_opdef { + char *opname; + int opcode; /* numeric opcode -- same as array index */ + int prmtype; /* one of SC_NONE, SC_STR, SC_XSTR, SC_INT */ + int newstate; /* one of SC_NONE, SC_NWST */ +}; + + /* values for opcode */ + +#define SC_LABEL 0 /* "label" (state name) */ +#define SC_CDLY 1 /* set char output delay in msec */ +#define SC_PCHR 2 /* pause char for dial string (from P in input) */ +#define SC_PTIM 3 /* seconds to allow for pause char */ +#define SC_WCHR 4 /* wait char for dial string (from W in input) */ +#define SC_WTIM 5 /* seconds to allow for wait char */ +#define SC_ZERO 6 /* zero counter */ +#define SC_INCR 7 /* increment counter */ +#define SC_IFGT 8 /* change state if counter > int param */ +#define SC_WAIT 9 /* wait for int param seconds */ +#define SC_GOTO 10 /* unconditional change to new state */ +#define SC_SEND 11 /* send strparam (after sprintf substitutions) */ +#define SC_BRK 12 /* send a break */ +#define SC_HANG 13 /* drop DTR */ +#define SC_DIAL 14 /* send telno string (after subst PCHR & WCHR) */ +#define SC_DTIM 15 /* time in msec per digit (for timeout calculations) */ + /* default = 100 (one tenth second) */ +#define SC_CTIM 16 /* additional time (in seconds) to wait for carrier */ + /* default = 45 seconds */ +#define SC_EXIT 17 /* script done, success */ +#define SC_FAIL 18 /* script done, failure */ +#define SC_LOG 19 /* write strparam to uucp.log */ +#define SC_LOGE 20 /* write strparam to uucp.log w/error ind */ +#define SC_DBG 21 /* write strparam to debug log if debug lvl = LGI */ +#define SC_DBGE 22 /* write strparam to debug log if debug lvl = LGIE */ +#define SC_DBST 23 /* 'or' intparam into debug mask */ +#define SC_DBCL 24 /* 'bicl' intparam into debug mask */ +#define SC_TIMO 25 /* newstate if no match in intparam secs */ + /* (uses calculated dial time if intparam is 0) */ +#define SC_XPCT 26 /* wait for strparam, goto _newstate if found */ +#define SC_CARR 27 /* goto _newstate if carrier detected */ +#define SC_FLSH 28 /* flush typeahead buffer */ +#define SC_IFBL 29 /* change state if controller is blind w/o CD */ +#define SC_IFBG 30 /* chg state if ctlr is blind and counter > intprm */ +#define SC_SNDP 31 /* send parameter n */ +#define SC_IF1P 32 /* if parameter n present */ +#define SC_IF0P 33 /* if parameter n absent */ +#define SC_DBOF 34 /* open debugging file */ +#define SC_TELN 35 /* Set telno from parameter n */ +#define SC_7BIT 36 /* Set port to 7-bit stripping */ +#define SC_8BIT 37 /* Set port for 8-bit characters */ +#define SC_PNON 38 /* Set port for 8-bit, no parity */ +#define SC_PEVN 39 /* Set port for 7-bit, even parity */ +#define SC_PODD 40 /* Set port for 7-bit, odd parity */ +#define SC_HUPS 41 /* Change state on HUP signal */ +#define SC_END 42 /* end of array */ + + /* values for prmtype, prm2type */ + +#define SC_NONE 0 /* no parameter */ +#define SC_STR 1 /* simple string */ +#define SC_INT 2 /* integer */ +#define SC_NWST 3 /* new state name */ +#define SC_XSTR 4 /* translated string */ + +/* opcode definition table for dial/login/hangup scripts */ + +static struct script_opdef sc_opdef[] = + { + {"label", SC_LABEL, SC_NONE, SC_NONE}, + {"chrdly", SC_CDLY, SC_INT, SC_NONE}, + {"pchar", SC_PCHR, SC_STR, SC_NONE}, + {"ptime", SC_PTIM, SC_INT, SC_NONE}, + {"wchar", SC_WCHR, SC_STR, SC_NONE}, + {"wtime", SC_WTIM, SC_INT, SC_NONE}, + {"zero", SC_ZERO, SC_NONE, SC_NONE}, + {"count", SC_INCR, SC_NONE, SC_NONE}, + {"ifgtr", SC_IFGT, SC_INT, SC_NWST}, + {"sleep", SC_WAIT, SC_INT, SC_NONE}, + {"goto", SC_GOTO, SC_NONE, SC_NWST}, + {"send", SC_SEND, SC_XSTR, SC_NONE}, + {"break", SC_BRK, SC_NONE, SC_NONE}, + {"hangup", SC_HANG, SC_NONE, SC_NONE}, + {"7bit", SC_7BIT, SC_NONE, SC_NONE}, + {"8bit", SC_8BIT, SC_NONE, SC_NONE}, + {"nopar", SC_PNON, SC_NONE, SC_NONE}, + {"evenpar", SC_PEVN, SC_NONE, SC_NONE}, + {"oddpar", SC_PODD, SC_NONE, SC_NONE}, + {"telno", SC_TELN, SC_INT, SC_NONE}, + {"dial", SC_DIAL, SC_NONE, SC_NONE}, + {"dgttime", SC_DTIM, SC_INT, SC_NONE}, + {"ctime", SC_CTIM, SC_INT, SC_NONE}, + {"success", SC_EXIT, SC_NONE, SC_NONE}, + {"failed", SC_FAIL, SC_NONE, SC_NONE}, + {"log", SC_LOG, SC_XSTR, SC_NONE}, + {"logerr", SC_LOGE, SC_XSTR, SC_NONE}, + {"debug", SC_DBG, SC_XSTR, SC_NONE}, + {"debuge", SC_DBGE, SC_XSTR, SC_NONE}, + {"dbgset", SC_DBST, SC_INT, SC_NONE}, + {"dbgclr", SC_DBCL, SC_INT, SC_NONE}, + {"dbgfile", SC_DBOF, SC_XSTR, SC_NONE}, + {"timeout", SC_TIMO, SC_INT, SC_NWST}, + {"expect", SC_XPCT, SC_XSTR, SC_NWST}, + {"ifcarr", SC_CARR, SC_NONE, SC_NWST}, + {"ifhang", SC_HUPS, SC_NONE, SC_NWST}, + {"flush", SC_FLSH, SC_NONE, SC_NONE}, + {"ifblind", SC_IFBL, SC_NONE, SC_NWST}, + {"ifblgtr", SC_IFBG, SC_INT, SC_NWST}, + {"sendstr", SC_SNDP, SC_INT, SC_NONE}, + {"ifstr", SC_IF1P, SC_INT, SC_NWST}, + {"ifnstr", SC_IF0P, SC_INT, SC_NWST}, + {"table end", SC_END, SC_NONE, SC_NONE} + }; + +#define SUCCESS 0 +#define FAIL 1 +#define ERROR -1 +#define MAX_SCLINE 255 /* max length of a line in a script file */ +#define MAX_EXPCT 127 /* max length of an expect string */ +#define CTL_DELIM " \t\n\r" /* Delimiters for tokens */ +#define SAME 0 /* if (strcmp(a,b) == SAME) ... */ +#define SLOP 10 /* Slop space on arrays */ +#define MAX_STRING 200 /* Max length string to send/expect */ + +#define DEBUG_LEVEL(level) \ + (Debug & (1 << level)) + +#define DB_LOG 0 /* error messages and a copy of the LOGFILE output */ +#define DB_LGIE 1 /* dial,login,init trace -- errors only */ +#define DB_LGI 2 /* dial,login,init trace -- nonerrors (incl chr I/O) */ +#define DB_LGII 3 /* script processing internals */ + +#define TRUE 1 +#define FALSE 0 + +#define NONE 0 +#define EVEN 1 +#define ODD 2 + +#define logit(m, p1) fprintf(stderr, "%s %s\n", m, p1) + +static char **paramv; /* Parameter vector */ +static int paramc; /* Parameter count */ +static char telno[64]; /* Telephone number w/meta-chars */ +static int Debug; +static int fShangup = FALSE; /* TRUE if HUP signal received */ +static FILE *dbf = NULL; +static struct termio old, new; + +extern int usignal(); +extern int uhup(); + +static struct siglist +{ + int signal; + int (*o_catcher) (); + int (*n_catcher) (); +} sigtbl[] = { + { SIGHUP, NULL, uhup }, + { SIGINT, NULL, usignal }, + { SIGIOT, NULL, usignal }, + { SIGQUIT, NULL, usignal }, + { SIGTERM, NULL, usignal }, + { SIGALRM, NULL, usignal }, + { 0, NULL, NULL } /* Table end */ + }; + +extern struct script *read_script(); +extern void msleep(); +extern char xgetc(); +extern void charlog(); +extern void setup_tty(); +extern void restore_tty(); +extern void ttoslow(); +extern void ttflui(); +extern void tthang(); +extern void ttbreak(); +extern void tt7bit(); +extern void ttpar(); +extern void DEBUG(); + +extern void *malloc(); + + +/* + * ********************************** + * * BEGIN EXECUTION - MAIN PROGRAM * + * ********************************** + * + * This program is called by Taylor UUCP with a list of + * arguments in argc/argv, and stdin/stdout mapped to the + * tty device, and stderr mapped to the Taylor logfile, where + * anything written to stdout will be logged as an error. + * + */ +int main(argc, argv) +int argc; +char *argv[]; +{ + int i, stat; + FILE *sf; + char sfname[256]; + struct script *script; + struct siglist *sigs; + + /* + * The following is needed because my cpp does not have the + * #error directive... + */ +#if ! HAVE_SELECT + no_select_sorry(); /* Sad way to fail make */ +#endif + + paramv = &argv[2]; /* Parameters start at 2nd arg */ + paramc = argc - 2; /* Number of live parameters */ + + telno[0] = '\0'; + + if (argc < 2) + { + fprintf(stderr, "%s: no script file supplied\n", argv[0]); + exit(FAIL); + } + + /* + * If the script file argument begins with '/', then we assume + * it is an absolute pathname, otherwise, we prepend the + * SCRIPT_DIR path. + */ + *sfname = '\0'; /* Empty name string */ + if(argv[1][0] != '/') /* If relative path */ + strcat(sfname, SCRIPT_DIR); /* Prepend the default dir. */ + strcat(sfname, argv[1]); /* Add the script file name */ + + /* + * Now open the script file. + */ + if ((sf = fopen(sfname, "r")) == NULL) + { + fprintf(stderr, "%s: Failed to open script %s\n", argv[0], sfname); + perror(" "); + exit(FAIL); + } + + /* + * COMPILE SCRIPT + */ + if ((script = read_script(sf)) == NULL) + { + fprintf(stderr, "%s: script error in \"%s\"\n", argv[0], argv[1]); + exit(FAIL); + } + + /* + * Set up a signal catcher so the line can be returned to + * it's current state if something nasty happens. + */ + sigs = &sigtbl[0]; + while(sigs->signal) + { + sigs->o_catcher = (int (*) ())signal(sigs->signal, sigs->n_catcher); + sigs += 1; + } + + /* + * Save current tty settings, then set up raw, single + * character input processing, with 7-bit stripping. + */ + setup_tty(); + + /* + * EXECUTE SCRIPT + */ + if ((stat = do_script(script)) != SUCCESS) + fprintf(stderr, "%s: script %s failed.\n", argv[0], argv[1]); + + /* + * Clean up and exit. + */ + restore_tty(); +#ifdef FIXSIGS + sigs = &sigtbl[0]; + while(sigs->signal) + if(sigs->o_catcher != -1) + signal(sigs->signal, sigs->o_catcher); +#endif + exit(stat); +} + +/* + * deal_script - deallocate a script and all strings it points to + */ +int deal_script(loc) +struct script *loc; +{ + /* + * If pointer is null, just exit + */ + if (loc == (struct script *)NULL) + return SUCCESS; + + /* + * Deallocate the rest of the script + */ + deal_script(loc->next); + + /* + * Deallocate the string parameter, if any + */ + if (loc->strprm != (char *)NULL) + free(loc->strprm); + + /* + * Deallocate the new state name parameter, if any + */ + if (loc->newstate != (char *)NULL) + free(loc->newstate); + + /* + * Deallocate this entry + */ + free(loc); + + return SUCCESS; +} + + +/* + * read_script + * + * Read & compile a script, return pointer to first entry, or null if bad + */ +struct script *read_script(fd) + FILE *fd; +{ + struct script *this = NULL; + struct script *prev = NULL; + struct script *first = NULL; + long len, i; + char inpline[MAX_SCLINE]; + char inpcopy[MAX_SCLINE]; + char *c, *cln, *opc, *cp; + + /* + * MAIN COMPILATION LOOP + */ + while ((c = fgets(inpline, (sizeof inpline - 1), fd)) != (char *)NULL) + { + /* + * Skip comments and blank lines + */ + if (*c == '#' || *c == '\n') + continue; + + /* + * Get rid of the trailing newline, and copy the string + */ + inpline[strlen(inpline)-1] = '\0'; + strcpy(inpcopy, inpline); + + /* + * Look for text starting in the first col (a label) + */ + if ((!isspace(inpline[0])) && + (cln = strchr (inpline, ':')) != (char *)NULL) { + this = (struct script *)malloc (sizeof (struct script)); + if (prev != (struct script *)NULL) + prev->next = this; + prev = this; + if (first == (struct script *)NULL) + first = this; + this->next = (struct script *)NULL; + this->opcode = SC_LABEL; + len = cln - c; + this->strprm = (char *)malloc(len+1); + strncpy(this->strprm, c, len); + (this->strprm)[len] = '\0'; + this->intprm = 0; + this->newstate = (char *)NULL; + c = cln + 1; + } + + /* + * Now handle the opcode. Fold it to lower case. + */ + opc = strtok(c, CTL_DELIM); + if (opc == (char *)NULL) /* If no opcode... */ + continue; /* ...read the next line */ + cp = opc; + while(*cp) + tolower(*cp++); + + /* + * If we have an opcode but we haven't seen anything + * else (like a label) yet, i.e., this is the first + * entry, and there was no label. We need to + * cobble up a label so that read_script is happy + */ + if (first == (struct script *)NULL) + { + this = (struct script *)malloc (sizeof (struct script)); + prev = this; + first = this; + this->next = (struct script *)NULL; + this->opcode = SC_LABEL; + this->strprm = (char *)malloc(2); + strcpy(this->strprm, ":"); + this->intprm = 0; + this->newstate = (char *)NULL; + } + + /* + * Find opcode - ndex through the opcode definition table + */ + for (i=1; sc_opdef[i].opcode != SC_END; i++) + if (strcmp(opc, sc_opdef[i].opname) == SAME) + break; + if ((sc_opdef[i].opcode) == SC_END) + { + logit ("Bad opcode in script", opc); + deal_script(first); + return (struct script *)NULL; + } + + /* + * Found opcode. Allocate a new command node and initialize + */ + this = (struct script *)malloc(sizeof (struct script)); + prev->next = this; + prev = this; + this->next = (struct script *)NULL; + this->opcode = sc_opdef[i].opcode; + this->strprm = (char *)NULL; + this->intprm = 0; + this->newstate = (char *)NULL; + + /* + * Pick up new state parameter, if any + */ + if (sc_opdef[i].newstate == SC_NWST) + { + c = strtok((char *)NULL, CTL_DELIM); + if (c == (char *)NULL) + { + logit("Missing new state", opc); + deal_script(first); + return (struct script *)NULL; + } + else + { + this->newstate = (char *)malloc(strlen(c)+1); + strcpy(this->newstate, c); + } + } + + /* + * Pick up the string or integer parameter. Handle missing + * parameter gracefully. + */ + switch (sc_opdef[i].prmtype) + { + /* + * INT parameter - convert and store in node + */ + case SC_INT: + c = strtok((char *)NULL, CTL_DELIM); + if (c == (char *)NULL) + { + logit("Missing script param", opc); + deal_script(first); + return (struct script *)NULL; + } + /* + * If this is the parameter to DBST or DBCL, force + * base-10 conversion, else convert per parameter. + */ + if (sc_opdef[i].opcode == SC_DBST || + sc_opdef[i].opcode == SC_DBCL) + this->intprm = strtol(c, (char **)NULL, 0); + else + this->intprm = strtol(c, (char **)NULL, 10); + break; + + /* + * STR/XSTR strings. + */ + case SC_STR: + case SC_XSTR: + c = strtok((char *)NULL, CTL_DELIM); + if (c == (char *)NULL) + { + logit("Missing script param", opc); + deal_script(first); + return (struct script *)NULL; + } + /* + * For XSTR opcode, use c to find out where + * the string param begins in the copy of the + * input line, and pick up all that's left of + * the line (to allow imbedded blanks, etc.). + */ + if (sc_opdef[i].prmtype == SC_XSTR) + c = &inpcopy[0] + (c - &inpline[0]); + + /* + * Allocate a buffer for the string parameter + */ + this->strprm = (char *)malloc(strlen(c)+1); + + /* + * For XSTR, Translate the string and store its + * length. Note that, after escape sequences are + * compressed, the resulting string may well be a + * few bytes shorter than the input string (whose + * length was the basis for the malloc above), + * but it will never be longer. + */ + if (sc_opdef[i].prmtype == SC_XSTR) + { + this->intprm = xlat_str(this->strprm, c); + this->strprm[this->intprm] = '\0'; + } + else + strcpy(this->strprm, c); + break; + + } + } + + /* + * EOF + */ + return first; +} + + +/* + * xlat_str + * + * Translate embedded escape characters in a "send" or "expect" string. + * + * Called by read_script(), above. + * + * Returns the actual length of the resulting string. Note that imbedded + * nulls (specified by \000 in the input) ARE allowed in the result. + */ +xlat_str(out, in) + char *out, *in; +{ + register int i = 0, j = 0; + int byte, k; + + while (in[i]) + { + if (in[i] != '\\') + { + out[j++] = in[i++]; + } + else + { + switch (in[++i]) + { + case 'd': /* EOT */ + out[j++] = 0x04; + break; + case 'N': /* null */ + out[j++] = 0x00; + break; + case 'n': /* line feed */ + out[j++] = 0x0a; + break; + case 'r': /* carriage return */ + out[j++] = 0x0d; + break; + case 's': /* space */ + out[j++] = ' '; + break; + case 't': /* tab */ + out[j++] = '\t'; + break; + case '-': /* hyphen */ + out[j++] = '-'; + break; + case '\\': /* back slash */ + out[j++] = '\\'; + break; + case '0': /* '\nnn' format */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + byte = in[i] - '0'; + k = 0; + + while (3 > ++k) + if ((in[i+1] < '0') || (in[i+1] > '7')) + break; + else + { + byte = (byte<<3) + in[i+1] - '0'; + ++i; + } + out[j++] = byte; + break; + default: /* don't know so skip it */ + break; + } + ++i; + } + } + return j; +} + + +/* find a state within a script */ + +struct script * + find_state(begin, newstate) +struct script *begin; +char *newstate; +{ + struct script *here; + + for (here=begin; here != (struct script *)NULL; here=here->next) { + if (here->opcode == SC_LABEL && + strcmp(here->strprm, newstate) == SAME) + return here; + } + return (struct script *)NULL; +} + + +/* + * do_script() - execute a script + */ +int do_script(begin) + struct script *begin; +{ + struct script *curstate, *newstate, *curscr; + int dbgsave; + char tempstr[MAX_SCLINE]; + char dfname[256]; + char *c, chr; + int prmlen; + int dbfd; + + time_t sc_carrtime = 45000; /* time to wf carr after dial */ + time_t sc_chrdly = 100; /* delay time for ttoslow */ + time_t sc_ptime = 2000; /* time to allow for pause char */ + time_t sc_wtime = 10000; /* time to allow for wait char */ + time_t sc_dtime = 100; /* time to allow for each digit */ + time_t sc_dtmo; /* total time to dial number */ + int sc_counter; /* random counter */ + char sc_pchar = ','; /* modem pause character */ + char sc_wchar = 'W'; /* modem wait-for-dialtone character */ + time_t sc_begwait; /* time at beg of wait */ + time_t sc_secs; /* timeout period */ + + int expcnt; + int expin; + static char expbuf[MAX_EXPCT]; + + dbgsave = Debug; + curstate = begin; + + if (curstate == (struct script *)NULL) + return SUCCESS; + + _newstate: + /* + * do all of curstate's actions. Enter with curstate pointing + * to a label entry + */ + expin = 0; + + for (curscr = curstate->next; /* point to 1st scr after label */ + (curscr != (struct script *)NULL) && /* do until end of scr */ + (curscr->opcode != SC_LABEL); /* or next label */ + curscr = curscr->next) + { + expcnt = 0; + switch (curscr->opcode) + { + case SC_LABEL: + logit("Script proc err", curstate->strprm); + return FAIL; + + case SC_FLSH: + DEBUG(DB_LGII, "Flushing typeahead buffer\n", 0); + ttflui(); + break; + + case SC_CDLY: + sc_chrdly = curscr->intprm; + DEBUG(DB_LGII, "Set chrdly to %d\n", sc_chrdly); + break; + + case SC_PCHR: + sc_pchar = *(curscr->strprm); + DEBUG(DB_LGII, "Set pause char to %c\n", sc_pchar); + break; + + case SC_PTIM: + sc_ptime = curscr->intprm; + DEBUG(DB_LGII, "Set pause time to %d\n", sc_ptime); + break; + + case SC_WCHR: + sc_wchar = *(curscr->strprm); + DEBUG(DB_LGII, "Set wait char to %c\n", sc_wchar); + break; + + case SC_WTIM: + sc_wtime = curscr->intprm; + DEBUG(DB_LGII, "Set wait time to %d\n", sc_wtime); + break; + + case SC_ZERO: + sc_counter = 0; + DEBUG(DB_LGII, "Set counter to %d\n", sc_counter); + break; + + case SC_INCR: + sc_counter++; + DEBUG(DB_LGII, "Incr counter to %d\n", sc_counter); + break; + + case SC_WAIT: + DEBUG(DB_LGII, "Sleeping %d tenth-secs\n", curscr->intprm); + msleep(curscr->intprm); + break; + + case SC_DTIM: + sc_dtime = curscr->intprm; + DEBUG(DB_LGII, "Digit time is %d\n", sc_dtime); + break; + + case SC_CTIM: + sc_carrtime = curscr->intprm; + DEBUG(DB_LGII, "Carrier time is %d\n", sc_carrtime); + break; + + case SC_EXIT: + Debug = dbgsave; + DEBUG(DB_LGI, "Script ended successfully\n", 0); + return SUCCESS; + + case SC_FAIL: + Debug = dbgsave; + if (DEBUG_LEVEL(DB_LGI) && dbf != NULL) + fprintf(dbf, "Script failed\n"); + else if (expin) + charlog(expbuf, expin, DB_LOG, + "Script failed. Last received data"); + return FAIL; + + case SC_LOG: + logit(curscr->strprm, ""); + break; + + case SC_LOGE: + logit("ERROR: ", curscr->strprm); + break; + + case SC_DBOF: + /* + * If the debug file name does not begin with "/", then + * we prepend the LOG_DIR to the string. Then CREATE the + * file. This WIPES OUT previous logs. + */ + *dfname = '\0'; /* Zero name string */ + if(curscr->strprm[0] != '/') + strcat(dfname, LOG_DIR); /* Prepend default directory */ + strcat(dfname, curscr->strprm); /* Add given string */ + DEBUG(DB_LGII, "Open debug file %s\n", dfname); + if ((dbfd = creat (dfname, 0600)) <= 0) + { + logit("Failed to create debug log %s", dfname); + perror(""); + return FAIL; + } + if ((dbf = fdopen(dbfd, "w")) == NULL) + { + logit("Failed to open debug log fildes.", ""); + perror(""); + return FAIL; + } + break; + + case SC_DBG: + DEBUG(DB_LGI, "<%s>\n", curscr->strprm); + break; + + case SC_DBGE: + DEBUG(DB_LGIE, "ERROR: <%s>\n", curscr->strprm); + break; + + case SC_DBST: + Debug |= curscr->intprm; + DEBUG(DB_LGII, "Debug mask set to %04o (octal)\n", Debug); + break; + + case SC_DBCL: + Debug &= ~(curscr->intprm); + DEBUG(DB_LGII, "Debug mask set to %04o (octal)\n", Debug); + break; + + case SC_BRK: + DEBUG(DB_LGI, "Sending break\n", 0); + ttbreak(); + break; + + case SC_HANG: + DEBUG(DB_LGI, "Dropping DTR\n", 0); + tthang(); + break; + + case SC_7BIT: + DEBUG(DB_LGI, "Enabling 7-bit stripping\n", 0); + tt7bit(TRUE); + break; + + case SC_8BIT: + DEBUG(DB_LGI, "Disabling 7-bit stripping\n", 0); + tt7bit(FALSE); + break; + + case SC_PNON: + DEBUG(DB_LGI, "Setting 8-bit, no parity\n", 0); + ttpar(NONE); + break; + + case SC_PEVN: + DEBUG(DB_LGI, "Setting 7-bit, even parity\n", 0); + ttpar(EVEN); + break; + + case SC_PODD: + DEBUG(DB_LGI, "Setting 7-bit, odd parity\n", 0); + ttpar(ODD); + break; + + case SC_IFBL: + if (ttblind()) + { + DEBUG(DB_LGI, "Blind mux,\n", 0); + goto _chgstate; + } + break; + + case SC_IFBG: + if (ttblind() && sc_counter > curscr->intprm) + { + DEBUG(DB_LGI, "Blind mux & ctr > %d\n", + curscr->intprm); + goto _chgstate; + } + break; + + case SC_IFGT: + if (sc_counter > curscr->intprm) + { + DEBUG(DB_LGI, "Counter > %d\n", curscr->intprm); + goto _chgstate; + } + break; + + case SC_GOTO: + _chgstate: + DEBUG(DB_LGI, "Changing to state %s\n", + curscr->newstate); + curstate = find_state(begin, curscr->newstate); + if (curstate == NULL) + { + logit("New state not found", + curscr->newstate); + return FAIL; + } + goto _newstate; + + case SC_SEND: + ttoslow(curscr->strprm, curscr->intprm, sc_chrdly); + break; + + case SC_TELN: + if (curscr->intprm > paramc - 1) + { + sprintf(tempstr, "telno - param #%d", curscr->intprm); + logit(tempstr, " not present"); + return FAIL; + } + strcpy(telno, paramv[curscr->intprm]); + DEBUG(DB_LGII, "telno set to %s\n", telno); + break; + + case SC_SNDP: + if (curscr->intprm > paramc - 1) + { + sprintf(tempstr, "sendstr - param #%d", curscr->intprm); + logit(tempstr, " not present"); + return FAIL; + } + prmlen = xlat_str(tempstr, paramv[curscr->intprm]); + ttoslow(tempstr, prmlen, sc_chrdly); + break; + + case SC_IF1P: + if (curscr->intprm < paramc) + goto _chgstate; + break; + + case SC_IF0P: + if (curscr->intprm >= paramc) + goto _chgstate; + break; + + case SC_DIAL: + if(telno[0] == '\0') + { + logit("telno not set", ""); + return(FAIL); + } + /* + * Compute and set a default timeout for the 'timeout' + * command. Some parameters in this computation may be + * changed by the script. See the man page xchat(8) for + * details. + */ + sc_dtmo = (sc_dtime+sc_chrdly)*strlen(telno) + + sc_carrtime; + c=strcpy(tempstr, telno); + for (; *c!='\0'; c++) + { + if (*c == 'W') + { + *c = sc_wchar; + sc_dtmo += sc_wtime; + } + else if (*c == 'P') + { + *c = sc_pchar; + sc_dtmo += sc_ptime; + } + } + DEBUG(DB_LGI, "Dialing, default timeout is %d millisecs\n", sc_dtmo); + ttoslow(tempstr, 0, sc_chrdly); + break; + + case SC_TIMO: /* these are "expects", don't bother */ + case SC_XPCT: /* with them yet, other than noting that */ + case SC_CARR: /* they exist */ + expcnt++; + break; + } + + } + + /* we've done the current state's actions, now do its expects, if any */ + + if (expcnt == 0) + { + if (curscr != (struct script *)NULL && + (curscr->opcode == SC_LABEL)) + { + curstate = curscr; + DEBUG(DB_LGI, "Fell through to state %s\n", + curstate->strprm); + goto _newstate; + } + else + { + logit("No way out of state", curstate->strprm); + return FAIL; + } + } + + time(&sc_begwait); /* log time at beg of expect */ + DEBUG(DB_LGI, "Doing expects for state %s\n", curstate->strprm); + charlog((char *)NULL, 0, DB_LGI, "Received"); + + while (1) + { + chr = xgetc(1); /* Returns upon char input or 1 sec. tmo */ + + charlog(&chr, 1, DB_LGI, (char *)NULL); + + if (chr != EOF) + { + if (expin < MAX_EXPCT) + { + expbuf[expin++] = chr & 0x7f; + } + else + { + strncpy(expbuf, &expbuf[1], MAX_EXPCT-1); + expbuf[MAX_EXPCT-1] = chr & 0x7f; + } + } + + /* for each entry in the current state... */ + + for (curscr = curstate->next; + (curscr != (struct script *)NULL) && + (curscr->opcode != SC_LABEL); + curscr = curscr->next) + { + + switch (curscr->opcode) + { + case SC_TIMO: + sc_secs = curscr->intprm; + if (sc_secs == 0) + sc_secs = sc_dtmo; + sc_secs /= 1000; + if (time(NULL)-sc_begwait > sc_secs) + { + DEBUG(DB_LGI, + "\nTimed out (%d secs)\n", sc_secs); + goto _chgstate; + } + break; + + case SC_CARR: + if (ttcd()) + { + DEBUG(DB_LGI, "\nGot carrier\n", 0); + goto _chgstate; + } + break; + + case SC_HUPS: + if (fShangup) + { + DEBUG(DB_LGI, "\nGot data set hangup\n", 0); + goto _chgstate; + } + break; + + case SC_XPCT: + if ((expin >= curscr->intprm) && + (strncmp(curscr->strprm, + &expbuf[expin - curscr->intprm], + curscr->intprm) == SAME)) + { + charlog(curscr->strprm, curscr->intprm, + DB_LGI, "Matched"); + goto _chgstate; + } + break; + + } + } + } +} + +/* + * SIGNAL HANDLERS + */ + +/* + * usignal - generic signal catcher + */ +static int usignal(isig) + int isig; +{ + DEBUG(DB_LOG, "Caught signal %d. Exiting...\n", isig); + restore_tty(); + exit(FAIL); +} + +/* + * uhup - HUP catcher + */ +static int uhup(isig) + int isig; +{ + DEBUG(DB_LOG, "Data set hangup.\n"); + fShangup = TRUE; +} + +/* + * TERMINAL I/O ROUTINES + */ + +/* + * xgetc - get a character with timeout + * + * Assumes that stdin is opened on a terminal or TCP socket + * with O_NONBLOCK. + */ +static char xgetc(tmo) +int tmo; /* Timeout, seconds */ +{ + char c; + struct timeval s; + int f = 1; /* Select on stdin */ + int result; + + if(read(0, &c, 1) <= 0) /* If no data available */ + { + s.tv_sec = (long)tmo; + s.tv_usec = 0L; + if(select (1, &f, (int *) NULL, &f, &s) == 1) + read(0, &c, 1); + else + c = '\377'; + } + + return(c); +} + +/* + * Pause for an interval in milliseconds + */ +void msleep(msec) +long msec; +{ + +#if HAVE_USLEEP + if(msec == 0) /* Skip all of this if delay = 0 */ + return; + usleep (msec * (long)1000); +#endif /* HAVE_USLEEP */ + +#if HAVE_NAPMS + if(msec == 0) /* Skip all of this if delay = 0 */ + return; + napms (msec); +#endif /* HAVE_NAPMS */ + +#if HAVE_NAP + if(msec == 0) /* Skip all of this if delay = 0 */ + return; + nap (msec); +#endif /* HAVE_NAP */ + +#if HAVE_POLL + struct pollfd sdummy; + + if(msec == 0) + return; + /* + * We need to pass an unused pollfd structure because poll checks + * the address before checking the number of elements. + */ + poll (&sdummy, 0, msec); +#endif /* HAVE_POLL */ + +#if USE_SELECT_TIMER + struct timeval s; + + if(msec == 0) + return; + s.tv_sec = msec / 1000L; + s.tv_usec = (msec % 1000L) * 1000L; + select (0, (int *) NULL, (int *) NULL, (int *) NULL, &s); +#endif /* USE_SELECT_TIMER */ + +#if ! HAVE_NAPMS && ! HAVE_NAP && ! HAVE_USLEEP && \ + ! HAVE_POLL && ! USE_SELECT_TIMER + if(msec == 0) + return; + sleep (1); /* Sleep for a whole second (UGH!) */ +#endif /* HAVE_ and USE_ nothing */ +} + +/* + * Debugging output + */ +static void DEBUG(level, msg1, msg2) +int level; +char *msg1, *msg2; +{ + if ((dbf != NULL) && DEBUG_LEVEL(level)) + fprintf(dbf, msg1, msg2); +} + +/* + * charlog - log a string of characters + * + * SPECIAL CASE: msg=NULL, len=1 and msg[0]='\377' gets logged + * when read does its 1 sec. timeout. Log "<1 sec.>" + * so user can see elapsed time + */ +static void charlog(buf, len, mask, msg) +char *buf; +int len, mask; +char *msg; +{ + char tbuf[256]; + + if (DEBUG_LEVEL(mask) && dbf != NULL) + { + if(msg == (char *)NULL) + msg = ""; + strncpy(tbuf, buf, len); + tbuf[len] = '\0'; + if(len == 1 && tbuf[0] == '\377') + strcpy(tbuf, "<1 sec.>"); + fprintf(dbf, "%s %s\n", msg, tbuf); + } +} + +/* + * setup_tty() + * + * Save current tty settings, then set up raw, single + * character input processing, with 7-bit stripping. + */ +static void setup_tty() +{ + register int i; + + ioctl(0, TCGETA, &old); + + new = old; + + for(i = 0; i < 7; i++) + new.c_cc[i] = '\0'; + new.c_cc[VMIN] = 0; /* MIN = 0, use requested count */ + new.c_cc[VTIME] = 10; /* TIME = 1 sec. */ + new.c_iflag = ISTRIP; /* Raw mode, 7-bit stripping */ + new.c_lflag = 0; /* No special line discipline */ + + ioctl(0, TCSETA, &new); +} + +/* + * restore_tty() - restore signal handlers and tty modes on exit. + */ +static void restore_tty(sig) +int sig; +{ + ioctl(0, TCSETA, &old); + return; +} + +/* + * ttoslow() - Send characters with pacing delays + */ +static void ttoslow(s, len, delay) + char *s; + int len; + time_t delay; +{ + int i; + + if (len == 0) + len = strlen(s); + + charlog (s, len, DB_LGI, "Sending slowly"); + + for (i = 0; i < len; i++, s++) + { + write(1, s, 1); + msleep(delay); + } +} + +/* + * ttflui - flush input buffer + */ +static void ttflui() +{ + if(isatty(0)) + (void) ioctl ( 0, TCFLSH, 0); +} + +/* + * ttcd - Test if carrier is present + * + * NOT IMPLEMENTED. I don't know how!!! + */ +static int ttcd() +{ + return TRUE; +} + +/* + * tthang - Force DTR low for 1-2 sec. + */ +static void tthang() +{ + if(!isatty()) + return; + +#ifdef TCCLRDTR + (void) ioctl (1, TCCLRDTR, 0); + sleep (2); + (void) ioctl (1, TCSETDTR, 0); +#endif + + return; +} + +/* + * ttbreak - Send a "break" on the line + */ +static void ttbreak() +{ + (void) ioctl (1, TCSBRK, 0); +} + +/* + * ttblind - return TRUE if tty is "blind" + * + * NOT IMPLEMENTED - Don't know how!!! + */ +static int ttblind() +{ + return FALSE; +} + +/* + * tt7bit - enable/disable 7-bit stripping on line + */ +static void tt7bit(enable) + int enable; +{ + if(enable) + new.c_iflag |= ISTRIP; + else + new.c_iflag &= ~ISTRIP; + + ioctl(0, TCSETA, &new); +} + +/* + * ttpar - Set parity mode on line. Ignore parity errors on input. + */ +static void ttpar(mode) + int mode; +{ + switch(mode) + { + case NONE: + new.c_iflag &= ~(INPCK | IGNPAR); + new.c_cflag &= ~(CSIZE | PARENB | PARODD); + new.c_cflag |= CS8; + break; + + case EVEN: + new.c_iflag |= (INPCK | IGNPAR); + new.c_cflag &= ~(CSIZE | PARODD); + new.c_cflag |= (CS7 | PARENB); + + break; + + case ODD: + new.c_iflag |= (INPCK | IGNPAR); + new.c_cflag &= ~(CSIZE); + new.c_cflag |= (CS7 | PARENB | PARODD); + break; + } + + ioctl(0, TCSETA, &new); +} + + + + + + + diff --git a/gnu/libexec/uucp/contrib/xchat.man b/gnu/libexec/uucp/contrib/xchat.man new file mode 100644 index 0000000000..c980e202fc --- /dev/null +++ b/gnu/libexec/uucp/contrib/xchat.man @@ -0,0 +1,614 @@ +.TH xchat 8 +.SH NAME +xchat - Extended chat processor +.SH SYNOPSIS +.BI "xchat " "scriptfile" +.RI " [ " parameter... " ] " +.PP +where +.I scriptfile +is the name of a file containing an +.I xchat +script. If +.I scriptfile +begins with ``/'', then it is assumed to be a full path name for the +script file. If not, a configuration-dependent default directory path +(usually +.B "/usr/local/conf/uucp/" +) is prepended to the script file name. Normally, the default path +is the same as that for the Taylor UUCP configuration files. +.SH DESCRIPTION +.I Xchat +is a general-purpose dialing and login program designed for use +with Taylor UUCP as a ``chat-program'', taking the place (or +augmenting) the built-in chat scripting facility. It provides the +ability to closely control timeouts, multiple simultaneous ``expect'' +strings with separate actions, extended terminal control, modem +command character pacing, and more. +.PP +When used in conjunction with Taylor UUCP's +configuration features, +.I xchat +can provide you the ability to manage the most intricate login, +dial and hangup needs. The scripts are written in a shell-like (well, +sort-of) style with labels, commands, and parameters, easing the task +of writing procedures for complex terminal communications situations. +.PP +Because +.I xchat +assumes that it is connected to the terminal device via stdin/stdout, +you can easily debug scripts by invoking it from the shell and +responding to the script from the keyboard. A debug logging facility +is included, with the debug output going to a separate user-specified +file. This makes it easy to debug connection problems without wading +through large +.I uucico +log and debug files. +.PP +Formally, a script describes a state machine; +.I xchat +interprets the script and does what the state machine +tells it to. This section will be much easier to understand +if you obtain listings of the script files supplied with +.I xchat. +.SH "SCRIPT FILE FORMAT" +Script files are ordinary text files containing comments, labels, +and statements. Blank lines are ignored. +Comments are denoted by leading ``#'' +characters. Some statements (those which do not end with an +``extended string'' argument; see below) can also have trailing +comments. +.PP +.I Labels +begin in column one and are ended by colons (:). A label +specifies a state name. All lines between a pair of labels are +the statements for a single state. +.PP +Processing always begins at the head of the script (no leading +state name is necessary). +.PP +.I Statements +are divided into two categories, ``action'' and ``expect''. +When a state is entered, all of its actions are performed in the +order in which they appear in the file. +.PP +A +.I transition +to another state may occur for any of three reasons: +.IP (1) 5 +One of the actions may cause a transition to +another state, in which case the rest of the +current state's actions are skipped. +Processing resumes with the first action +statement of the new state. +.IP (2) 5 +If none of the actions cause a state +transition, and there are no expects in the +state, processing ``falls through'' to the next +state in the file. +.IP (3) 5 +If none of the actions cause a state +transition, but there are expects in the +state, the state machine pauses until one of +the expects is ``satisfied''. It then transitions +to the state named in the expect +statement. +.PP +Finally, there are two action statements which, when executed, +cause the script to exit. +.SH "SCRIPT FILE STATEMENTS" +This section describes all of the statements that may appear in script +files, except for a few special action statements. Those are described +in a later section, ``Overriding Defaults''. +.PP +Some statements accept one or two arguments, referred to in the +following descriptions as +.IR int ", " ns ", " str ", or " +.IR xstr ", to" +indicate whether the argument is an integer, a new state name, a +string, or an ``extended string'' (described in a later section). +.PP +For all statements that accept two arguments, the first is the +name of a new state, and the second specifies a condition or +reason for changing to the new state. +.SS "Termination And Informational Statements" +These statements are used to place entries into the Taylor UUCP +.I Log +file, and to cause +.I xchat +to exit with successful or failure status. It is also possible to open a +separate +.I debug +log file and control the level of tracing and error reporting that will go +into that log file. This is very useful in debugging +.I xchat +scripts. +.br +.ta 1.0i 1.5i 2.0i +.TP 2.0i +.B failed +Exit script with ``failed'' status. This causes +.I xchat +to exit with status 0. +.TP 2.0i +.B success +Exit script with ``success'' status. This causes +.I xchat +to exit with status 1. +.TP 2.0i +.BI "log " xstr +Send informational message +.I xstr +to standard error. When used with Taylor UUCP, this is the +.I Log +file for the +.I uucico +program. +.TP 2.0i +.BI "logerr " xstr +Send message +.I xstr +to standard error, with ``ERROR:'' indicator. When used +with Taylor UUCP, this is the +.I Log +file for the +.I uucico +program. +.TP 2.0i +.BI "dbgfile " xstr +Open script debugging file +.I xstr. +If +.I xstr +begins with ``/'', it is assumed to be an absolute path name for the +debugging file. If not, then a configuration-dependent default directory +path (usually +.B "/usr/spool/uucp" +) is prepended to +.I xstr. +Normally the default path is that of the directory where Taylor UUCP +puts its log files. +The debugging file is used to capture a detailed log of the data sent +and received, errors encountered, and a trace of script execution. +The various types of logging are controlled by the +.I "debug mask," +described next. +.B Note: +A new log file is created each time +.I xchat +runs. Use the +.B log +and +.B loge +commands to log +continuous information onto standard out, which is connected +to the Taylor UUCP +.I Log +file when +.I xchat +is run by the Taylor +.I uucico. +.TP 2.0i +.BI "dbgset " int +Set the bits specified in +.I int +in the debugging mask. The value in +.I int +is ``or''ed into the mask. Set bit 0 (value \= 1) for error messages, +bit 1 (value \= 2) for dial, login and init errors, bit 2 (value \= 4) +for dial, login and init trace with character I/O, and bit 3 (value \= 8) +for script processing internals. Normally, you will just turn it all on +with a value of 15. +.TP 2.0i +.BI "dbgclr " int +Clear the bits specified in +.I int +from the debugging mask. +.TP 2.0i +.BI "debug " xstr +Write +.I +xstr +into the debug log. The entry will be enclosed in angle brackets. +.TP 2.0i +.BI "debuge " xstr +Write +.I xstr +into the debug log with ``ERROR: '' prepended. The entry will be enclosed +in angle brackets. +.SS "Sending Data" +These statements are used to transmit data to standard out (the tty or TCP +port when used with Taylor UUCP). +.I +No implied carriage returns are sent. +You must include a \\r if you want a carriage return in the string +sent by the +.B send +command. If you want a return sent after +.B dial +or +.B sendstr, +you must send it with a separate +.B send +command. +.TP 2.0i +.B dial +Send the string previously set by the +.B telno +command to the serial port. +.B W +and +.B P +characters in the phone number are +converted as described under +.B +Dial Strings, +below. This statement also sets a default +timeout value, as described under the +.B timeout +statement. +.TP 2.0i +.BI "send " xstr +Send the string +.I xstr +to the serial port. +.TP 2.0i +.BI "sendstr " int +The argument of this statement is a digit from 0 +through 7. Send the corresponding string +parameter as passed to +.I xchat +following the script file name. The parameter is interpreted +as an extended string. +.SS "Special Terminal Control Statements" +These statements are used to cause the terminal port to perform some special action, or to change the mode of the port. +.I +The modes of the port are restored to their original settings +.I +by xchat before it exits. +.TP 2.0i +.B flush +Flush the terminal port's input buffer. +.TP 2.0i +.B break +Send a break signal. +.TP 2.0i +.B hangup +Momentarily drop Data Terminal Ready (DTR) on the +serial port, causing the modem to hang up. (Not +usually needed, since +.I uucico +does this at the end of each call.) +.TP 2.0i +.B 7bit +Change the port to strip incoming characters to 7 bits. +.I +This is the default mode. +This mode +is implied when the port has parity enabled, since parity characters +are 7-bits wide. +.TP 2.0i +.B 8bit +Change the port to allow incoming 8-bit characters to be passed +to the script processor. This mode has no effect if parity is +enabled, since parity characters are 7-bits wide. +.TP 2.0i +.B nopar +Change the port to 8-bits, no parity. +.I +This is the default mode. +.TP 2.0i +.B evenpar +Change the port to 7-bits, even parity. +.I +Incoming characters with parity errors are discarded. +.TP 2.0i +.B oddpar +Change the port to 7-bits, odd parity. +.I +Incoming characters with parity errors are discarded. +.SS "Counting, Branching, Timing and Testing Statements" +These statements are used to control the flow of the +.I xchat +script itself, including branching, delays, and counter manipulation. +.TP 2.0i +.BI "sleep " int +Delay for +.I int +milliseconds. +.TP 2.0i +.B zero +Clear the counter. +.TP 2.0i +.B count +Add one to the counter. +.TP 2.0i +.BI "ifgtr " "ns int" +Go to state +.I ns +if counter greater than +.I int. +.TP 2.0i +.BI "goto " ns +Go to state +.I ns +unconditionally. +.TP 2.0i +.BI "ifstr " "ns int" +Go to state +.I ns +if string parameter +.I int +is nonempty. +.TP 2.0i +.BI "ifnstr " "ns int" +Go to state +.I ns +if string parameter +.I int +is empty. +.TP 2.0i +.BI "ifblind " ns +Change to state +.I ns +if the port is ``blind'' without carrier (CD) asserted. +.I +This is not yet implemented, the test always fails. +.TP 2.0i +.BI "ifblgtr " "ns int" +Change to state +.I ns +if the port is ``blind'' without carrier (CD) asserted, and counter +is greater then +.I int. +.I +This is not yet implemented, the test always fails. +.SS "Expect Statements" +Expect statements are usually the last statements that appear in a +given state, though in fact they can appear anywhere within the +state. Even if they appear at the beginning, the script processor +always does all of the action statements first. As a practical +matter, the order of these statements is not significant; they are +all interpreted ``in parallel''. +.TP 2.0i +.BI "expect " "ns xstr" +Change to state +.I ns +if the string specified by +.I xstr +is received from standard input (usually the serial port). +Case is significant, but high-order bits are not +checked. +.TP 2.0i +.BI "ifcarr " ns +Change to state +.I ns +if Carrier Detect (CD) is true. +.I +Not currently implemented. Always changes state. +.TP 2.0i +.BI "ifhang " ns +Change to state +.I ns +if a data set hangup occurs (SIGHUP signal received). +.TP 2.0i +.BI "timeout " "ns int" +Change to state +.I ns +if the time (in milliseconds) +given by +.I int +has elapsed without satisfying any +expects. If the time specified is 0, a default +timeout value (calculated from the length and +other characteristics of the most recent dial +string) is used. +.SH "SCRIPT PROCESSING DETAILS" +.SS "Extended Strings" +In the statements that accept string arguments, the strings are +interpreted as +.I +extended strings. +Extended strings begin with +the first nonblank character and continue, including all imbedded +and trailing blanks and other whitespace, until (but not +including) the end of the line in the script file. (There is no +provision for line continuation.) No trailing spaces should be +present between the last ``desired'' character of the string and the +end of the line, as they will be included in the stored string and +sent or expected, just as they appear in the script file. And, +obviously, no trailing comments are permitted! They will just be +stored as part of the string. +.PP +Within an extended string, the following ``escape sequences'' will +be converted as indicated before being sent or expected: +.br +.nf +.in +0.5i +\fB\\d\fR EOT character (control-D) +\fB\\N\fR null character +\fB\\n\fR line feed +\fB\\r\fR carriage return +\fB\\s\fR space +\fB\\t\fR tab +\fB\\\-\fR hyphen +\fB\\\\\fR backslash +\fB\\ooo\fR character with value ooo (in octal) +.in -0.5i +.fi +.PP +Since extended strings in scripts can include embedded spaces, +tabs, etc., these escape sequences are only required in strings +appearing in systems entries, though they may be used in script +files to improve readability. +.PP +The octal-character specification (\\ooo) may have from one to +three octal digits; it is terminated either after the third digit +or when a non-octal character is encountered. But if you want to +specify one of these followed by something that happens to be a +valid octal character (for example, a control-A followed by a 7) +make sure to include all three digits after the \\ . So \\0017 +would become a control-A followed by the Ascii character ``7'', but +\\17 or \\017 would become a control-Y (decimal value 25). \\1S +would convert to a control-A followed by an ``S''. +.PP +Extended strings are stored without a trailing carriage return +unless one is explicitly present in the string (via \\r). +.SS "String Parameters" +The +.B sendstr +statement sends (after conversion from extended string +format) one of the parameters given on the +.I xchat +command line following the script file name. +The parameter is selected by the integer +argument of the statement. +.PP +This allows ``generic'' script files to serve +for many different systems; the string parameters +provide the phone number, username, password, etc. Character +substitutions described under ``extended strings'' above are +performed on these strings. +.PP +The ifstr and ifnstr statements allow further generality in script +files, by testing whether a particular parameter is present in the +systems entry. For example, a single script can be +used both for those systems that require a password and +those that do not. The password is specified as the last argument +in the +.xchat +command; the script can test for this +parameter's existence and skip the password sequence if +the parameter is empty. +.SS "``Wait'' And ``Pause'' Characters In Dial Strings" +An additional conversion is performed on dial strings. Dial strings +are interpreted as extended strings. Then the characters +.B W +and +.B P +within a dial string are interpreted as ``wait for dial +tone'' and ``pause'', and may be converted to other characters. By +default, +.B W +is left alone, and +.B P +is converted to a comma (,); +these are appropriate for Hayes-type modems. The script may +specify other substitutions (see below). +.PP +.B NOTE: +The Taylor UUCP documentation states that the ``wait'' and ``pause'' +characters are ``='' and ``-'', respectively. These are actual characters +understood by some modems. When using +.I xchat +you should put +.B W +and +.B P +in the dial strings you specify in the Taylor configuration files. +This way, the +.I xchat +processor can make the substitution appropriate for the particular +modem in use. Make a separate +.I xchat +script for each modem type, e.g., +.I "dial.hayes" +and specify the translation there. This way, the phone number strings +in the Taylor configuration files can be used with a variety of modems. +.SS "Default Timeouts For Dial Strings" +When a +.B dial +statement is executed, a default timeout value is set. +This is the timeout value used by a subsequent timeout statement +if the statement specifies a timeout value of 0. +.PP +The default timeout is given by: +.br +.nf +.in +2 +\fIctime\fR + (\fIndigits\fR * \fIdgttime\fR) + (\fInwchar\fR * \fIwtime\fR) + (\fInpchar\fR * \fI ptime\fR) +.in -2 +.fi +.PP +where +.I +ndigits, nwchar, +and +.I npchar +are the number of digits, wait characters, and pause characters in +the dial string, and +.I ctime, dgttime, wtime, +and +.I ptime +are 45 seconds, 0.1 seconds, 10 seconds, and 2 seconds, +respectively. +All of these times may be changed as specified below under +``Overriding Defaults.'' +.SS "Trailing Carriage Returns Not Assumed" +In the +.B dial +and +.B sendstr +statements, the dial string or +parameter is sent with no trailing carriage return; +if a carriage return must be sent after one of these, a separate +send statement must provide it. +.SH "OVERRIDING DEFAULTS" +The script processor sets several default values. The following +statements, which override these defaults, may be useful in +certain circumstances. +.TP 2.0i +.BI "chrdly " int +Since many modems cannot accept dialing commands +at full ``computer speed'', the script processor +sends all strings with a brief inter-character +delay. This statement specifies the delay time, +in milliseconds. The default is 100 (0.1 second). +.TP 2.0i +.BI "pchar " str +Specifies the character to which +.BR P s +in the +dial string should be converted. Default is +``,'', for use with Hayes-type modems. +.TP 2.0i +.BI "ptime " int +Specifies the time, in milliseconds, to allow in +the default timeout for each pause character in +the dial string. Default is 2000 (2 seconds). +.TP 2.0i +.BI "wchar " str +Specifies the character to which +.BR W s +in the +dial string should be converted. Default is +``W'', for Hayes modems. +.TP 2.0i +.BI "wtime " int +Specifies the time, in milliseconds, to allow in +the default timeout for each wait-for-dialtone +character in the dial string. Default is 10000 +(10 seconds). +.TP 2.0i +.BI "dgttime " int +Specifies the time, in milliseconds, to allow in +the default timeout for each digit character in +the dial string. Default is 100 (0.1 second). +.TP 2.0i +.BI "ctime " int +Specifies the time, in milliseconds, to allow in +the default timeout for carrier to appear after +the dial string is sent. Default is 45000 (45 +seconds). +.SH "SEE ALSO" +uucico(8) for Taylor UUCP, and documentation for Taylor UUCP. +.SH AUTHOR +Robert B. Denny (denny@alisa.com) +.SH HISTORY +This program is an adaptation of the dial/login script processing +code that is a part of DECUS UUCP for VAX/VMS, written by Jamie +Hanrahan, et. al. +.SH BUGS +This version (1.1) does not support BSD terminal facilities. Anyone +volunteer to add this? + diff --git a/gnu/libexec/uucp/cu/Makefile b/gnu/libexec/uucp/cu/Makefile new file mode 100644 index 0000000000..5b1ba03d85 --- /dev/null +++ b/gnu/libexec/uucp/cu/Makefile @@ -0,0 +1,16 @@ +# Makefile for cu +# $Id: Makefile,v 1.2 1993/08/05 16:14:45 jtc Exp $ + +BINDIR= $(bindir) +BINOWN= $(owner) +BINMODE= 4555 + +PROG= cu +SRCS= cu.c prot.c log.c chat.c conn.c tcp.c tli.c copy.c +LDADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +DPADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +CFLAGS+= -I$(.CURDIR)/../common_sources\ + -DVERSION=\"$(VERSION)\" + +.include +.PATH: $(.CURDIR)/../common_sources diff --git a/gnu/libexec/uucp/cu/cu.1 b/gnu/libexec/uucp/cu/cu.1 new file mode 100644 index 0000000000..56409a0042 --- /dev/null +++ b/gnu/libexec/uucp/cu/cu.1 @@ -0,0 +1,286 @@ +''' $Id: cu.1,v 1.1 1993/08/04 19:31:53 jtc Exp $ +.TH cu 1 "Taylor UUCP 1.04" +.SH NAME +cu \- Call up another system +.SH SYNOPSIS +.B cu +[ options ] [ system | phone | "dir" ] +.SH DESCRIPTION +The +.I cu +command is used to call up another system and act as a dial in +terminal. It can also do simple file transfers with no error +checking. + +.I cu +takes a single argument, besides the options. If the argument is the +string "dir" cu will make a direct connection to the port. This may +only be used by users with write access to the port, as it permits +reprogramming the modem. + +Otherwise, if the argument begins with a digit, it is taken to be a +phone number to call. Otherwise, it is taken to be the name of a +system to call. The +.B \-z +option may be used to name a system beginning with a digit, and the +.B \-c +option may be used to name a phone number that does not begin with a +digit. + +.I cu +locates a port to use in the UUCP configuration files. If a simple +system name is given, it will select a port appropriate for that +system. The +.B \-p, \-l +and +.B \-s +options may be used to control the port selection. + +When a connection is made to the remote system, +.I cu +forks into two processes. One reads from the port and writes to the +terminal, while the other reads from the terminal and writes to the +port. + +.I cu +provides several commands that may be used during the conversation. +The commands all begin with an escape character, initially +.B ~ +(tilde). The escape character is only recognized at the beginning of +a line. To send an escape character to the remote system at the start +of a line, it must be entered twice. All commands are either a single +character or a word beginning with +.B % +(percent sign). + +.I cu +recognizes the following commands: + +.TP 5 +.B ~. +Terminate the conversation. +.TP 5 +.B ~! command +Run command in a shell. If command is empty, starts up a shell. +.TP 5 +.B ~$ command +Run command, sending the standard output to the remote system. +.TP 5 +.B ~| command +Run command, taking the standard input from the remote system. +.TP 5 +.B ~+ command +Run command, taking the standard input from the remote system and +sending the standard output to the remote system. +.TP 5 +.B ~#, ~%break +Send a break signal, if possible. +.TP 5 +.B ~c directory, ~%cd directory +Change the local directory. +.TP 5 +.B ~> file +Send a file to the remote system. This just dumps the file over the +communication line. It is assumed that the remote system is expecting +it. +.TP 5 +.B ~< +Receive a file from the remote system. This prompts for the local +file name and for the remote command to execute to begin the file +transfer. It continues accepting data until the contents of the +.B eofread +variable are seen. +.TP 5 +.B ~p from to, ~%put from to +Send a file to a remote Unix system. This runs the appropriate +commands on the remote system. +.TP 5 +.B ~t from to, ~%take from to +Retrieve a file from a remote Unix system. This runs the appropriate +commands on the remote system. +.TP 5 +.B ~s variable value +Set a +.I cu +variable to the given value. If value is not given, the variable is +set to +.B true. +.TP 5 +.B ~! variable +Set a +.I cu +variable to +.B false. +.TP 5 +.B ~z +Suspend the cu session. This is only supported on some systems. On +systems for which ^Z may be used to suspend a job, +.B ~^Z +will also suspend the session. +.TP 5 +.B ~%nostop +Turn off XON/XOFF handling. +.TP 5 +.B ~%stop +Turn on XON/XOFF handling. +.TP 5 +.B ~v +List all the variables and their values. +.TP 5 +.B ~? +List all commands. + +.I cu +also supports several variables. They may be listed with the +.B ~v +command, and set with the +.B ~s +or +.B ~! +commands. + +.TP 5 +.B escape +The escape character. Initially +.B ~ +(tilde). +.TP 5 +.B delay +If this variable is true, +.I cu +will delay for a second after recognizing the escape character before +printing the name of the local system. The default is true. +.TP 5 +.B eol +The list of characters which are considered to finish a line. The +escape character is only recognized after one of these is seen. The +default is carriage return, ^U, ^C, ^O, ^D, ^S, ^Q, ^R. +.TP 5 +.B binary +Whether to transfer binary data when sending a file. If this is +false, then newlines in the file being sent are converted to carriage +returns. The default is false. +.TP 5 +.B binary-prefix +A string used before sending a binary character in a file transfer, if +the +.B binary +variable is true. The default is ^Z. +.TP 5 +.B echo-check +Whether to check file transfers by examining what the remote system +echoes back. This probably doesn't work very well. The default is +false. +.TP 5 +.B echonl +The character to look for after sending each line in a file. The +default is carriage return. +.TP 5 +.B timeout +The timeout to use, in seconds, when looking for a character, either +when doing echo checking or when looking for the +.B echonl +character. The default is 30. +.TP 5 +.B kill +The character to use delete a line if the echo check fails. The +default is ^U. +.TP 5 +.B resend +The number of times to resend a line if the echo check continues to +fail. The default is 10. +.TP 5 +.B eofwrite +The string to write after sending a file with the +.B ~> +command. The default is ^D. +.TP 5 +.B eofread +The string to look for when receiving a file with the +.B ~< +command. The default is $, which is intended to be a typical shell +prompt. +.TP 5 +.B verbose +Whether to print accumulated information during a file transfer. The +default is true. +.SH OPTIONS +The following options may be given to +.I cu. +.TP 5 +.B \-e +Use even parity. +.TP 5 +.B \-o +Use odd parity. If both +.B \-e +and +.B \-o +are used, no parity is used. Otherwise the default parity of the line +is used. +.TP 5 +.B \-h +Echo characters locally (half-duplex mode). +.TP 5 +.B \-z system +The system to call. +.TP 5 +.B \-c phone-number +The phone number to call. +.TP 5 +.B \-p port +Name the port to use. +.TP 5 +.B \-a port +Equivalent to +.B \-p port. +.TP 5 +.B \-l line +Name the line to use by giving a device name. This may be used to +dial out on ports that are not listed in the UUCP configuration files. +Write access to the device is required. +.TP 5 +.B \-s speed +The speed (baud rate) to use. +.TP 5 +.B \-# +Where # is a number, equivalent to +.B \-s #. +.TP 5 +.B \-n +Prompt for the phone number to use. +.TP 5 +.B \-d +Enter debugging mode. Equivalent to +.B \-x all. +.TP 5 +.B \-x type +Turn on particular debugging types. The following types are +recognized: abnormal, chat, handshake, uucp-proto, proto, port, +config, spooldir, execute, incoming, outgoing. Only abnormal, chat, +handshake, port, config, incoming and outgoing are meaningful for +.I cu. + +Multiple types may be given, separated by commas, and the +.B \-x +option may appear multiple times. A number may also be given, which +will turn on that many types from the foregoing list; for example, +.B \-x 2 +is equivalent to +.B \-x abnormal,chat. +.B \-x all +may be used to turn on all debugging options. +.TP 5 +.B \-I file +Set configuration file to use. This option may not be available, +depending upon how +.I cu +was compiled. +.SH BUGS +This program does not work very well. +.SH FILES +The file name may be changed at compilation time, so this is only an +approximation. + +.br +/usr/lib/uucp/config - Configuration file. diff --git a/gnu/libexec/uucp/cu/cu.c b/gnu/libexec/uucp/cu/cu.c new file mode 100644 index 0000000000..72d60a62ec --- /dev/null +++ b/gnu/libexec/uucp/cu/cu.c @@ -0,0 +1,2068 @@ +/* cu.c + Call up a remote system. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char cu_rcsid[] = "$Id: cu.c,v 1.1 1993/08/04 19:31:54 jtc Exp $"; +#endif + +#include "cu.h" +#include "uudefs.h" +#include "uuconf.h" +#include "conn.h" +#include "prot.h" +#include "system.h" +#include "sysdep.h" +#include "getopt.h" + +#include +#include +#include + +/* Here are the user settable variables. The user is permitted to + change these while running the program, using ~s. */ + +/* The escape character used to introduce a special command. The + escape character is the first character of this string. */ +const char *zCuvar_escape = "~"; + +/* Whether to delay for a second before printing the host name after + seeing an escape character. */ +boolean fCuvar_delay = TRUE; + +/* The input characters which finish a line. The escape character is + only recognized following one of these characters. The default is + carriage return, ^U, ^C, ^O, ^D, ^S, ^Q, ^R, which I got from the + Ultrix /etc/remote file. */ +const char *zCuvar_eol = "\r\025\003\017\004\023\021\022"; + +/* Whether to transfer binary data (nonprintable characters other than + newline and tab) when sending a file. If this is FALSE, then + newline is changed to carriage return. */ +boolean fCuvar_binary = FALSE; + +/* A prefix string to use before sending a binary character from a + file; this is only used if fCuvar_binary is TRUE. The default is + ^Z. */ +const char *zCuvar_binary_prefix = "\026"; + +/* Whether to check for echoes of characters sent when sending a file. + This is ignored if fCuvar_binary is TRUE. */ +boolean fCuvar_echocheck = FALSE; + +/* A character to look for after each newline is sent when sending a + file. The character is the first character in this string, except + that a '\0' means that no echo check is done. */ +const char *zCuvar_echonl = "\r"; + +/* The timeout to use when looking for an character. */ +int cCuvar_timeout = 30; + +/* The character to use to kill a line if an echo check fails. The + first character in this string is sent. The default is ^U. */ +const char *zCuvar_kill = "\025"; + +/* The number of times to try resending a line if the echo check keeps + failing. */ +int cCuvar_resend = 10; + +/* The string to send at the end of a file sent with ~>. The default + is ^D. */ +const char *zCuvar_eofwrite = "\004"; + +/* The string to look for to finish a file received with ~<. For tip + this is a collection of single characters, but I don't want to do + that because it means that there are characters which cannot be + received. The default is a guess at a typical shell prompt. */ +const char *zCuvar_eofread = "$"; + +/* Whether to provide verbose information when sending or receiving a + file. */ +boolean fCuvar_verbose = TRUE; + +/* The table used to give a value to a variable, and to print all the + variable values. */ + +static const struct uuconf_cmdtab asCuvars[] = +{ + { "escape", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_escape, NULL }, + { "delay", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fCuvar_delay, NULL }, + { "eol", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_eol, NULL }, + { "binary", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fCuvar_binary, NULL }, + { "binary-prefix", UUCONF_CMDTABTYPE_STRING, + (pointer) &zCuvar_binary_prefix, NULL }, + { "echocheck", UUCONF_CMDTABTYPE_BOOLEAN, + (pointer) &fCuvar_echocheck, NULL }, + { "echonl", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_echonl, NULL }, + { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cCuvar_timeout, NULL }, + { "kill", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_kill, NULL }, + { "resend", UUCONF_CMDTABTYPE_INT, (pointer) &cCuvar_resend, NULL }, + { "eofwrite", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_eofwrite, NULL }, + { "eofread", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_eofread, NULL }, + { "verbose", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fCuvar_verbose, NULL }, + { NULL, 0, NULL, NULL} +}; + +/* The program name. */ +char abProgram[] = "cu"; + +/* The string printed at the initial connect. */ +#if ANSI_C +#define ZCONNMSG "\aConnected." +#else +#define ZCONNMSG "Connected." +#endif + +/* The string printed when disconnecting. */ +#if ANSI_C +#define ZDISMSG "\aDisconnected." +#else +#define ZDISMSG "Disconnected." +#endif + +/* Local variables. */ + +/* The string we print when the user is once again connected to the + port after transferring a file or taking some other action. */ +static const char abCuconnected[] +#if ANSI_C + = "\a[connected]"; +#else + = "[connected]"; +#endif + +/* Global uuconf pointer. */ +static pointer pCuuuconf; + +/* Connection. */ +static struct sconnection *qCuconn; + +/* Whether to close the connection. */ +static boolean fCuclose_conn; + +/* Dialer used to dial out. */ +static struct uuconf_dialer *qCudialer; + +/* Whether we need to restore the terminal. */ +static boolean fCurestore_terminal; + +/* Whether we are doing local echoing. */ +static boolean fCulocalecho; + +/* Whether we need to call fsysdep_cu_finish. */ +static boolean fCustarted; + +/* A structure used to pass information to icuport_lock. */ +struct sconninfo +{ + boolean fmatched; + boolean flocked; + struct sconnection *qconn; + const char *zline; +}; + +/* Local functions. */ + +static void ucuusage P((void)); +static void ucuabort P((void)); +static void uculog_start P((void)); +static void uculog_end P((void)); +static int icuport_lock P((struct uuconf_port *qport, pointer pinfo)); +static boolean fcudo_cmd P((pointer puuconf, struct sconnection *qconn, + int bcmd)); +static boolean fcuset_var P((pointer puuconf, char *zline)); +static int icuunrecogvar P((pointer puuconf, int argc, char **argv, + pointer pvar, pointer pinfo)); +static int icuunrecogfn P((pointer puuconf, int argc, char **argv, + pointer pvar, pointer pinfo)); +static void uculist_vars P((void)); +static void uculist_fns P((const char *zescape)); +static boolean fcudo_subcmd P((pointer puuconf, struct sconnection *qconn, + char *zline)); +static boolean fcusend_buf P((struct sconnection *qconn, const char *zbuf, + size_t cbuf)); + +#define ucuputs(zline) \ + do { if (! fsysdep_terminal_puts (zline)) ucuabort (); } while (0) + +/* Long getopt options. */ +static const struct option asCulongopts[] = { { NULL, 0, NULL, 0 } }; + +int +main (argc, argv) + int argc; + char **argv; +{ + /* -c: phone number. */ + char *zphone = NULL; + /* -e: even parity. */ + boolean feven = FALSE; + /* -l: line. */ + char *zline = NULL; + /* -n: prompt for phone number. */ + boolean fprompt = FALSE; + /* -o: odd parity. */ + boolean fodd = FALSE; + /* -p: port name. */ + const char *zport = NULL; + /* -s: speed. */ + long ibaud = 0L; + /* -t: map cr to crlf. */ + boolean fmapcr = FALSE; + /* -z: system. */ + const char *zsystem = NULL; + /* -I: configuration file name. */ + const char *zconfig = NULL; + int iopt; + pointer puuconf; + int iuuconf; + const char *zlocalname; + int i; + struct uuconf_system ssys; + const struct uuconf_system *qsys = NULL; + boolean flooped; + struct uuconf_port sport; + struct sconnection sconn; + struct sconninfo sinfo; + long ihighbaud; + struct uuconf_dialer sdialer; + struct uuconf_dialer *qdialer; + char bcmd; + + /* We want to accept -# as a speed. It's easiest to look through + the arguments, replace -# with -s#, and let getopt handle it. */ + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '-' + && isdigit (BUCHAR (argv[i][1]))) + { + size_t clen; + char *z; + + clen = strlen (argv[i]); + z = zbufalc (clen + 2); + z[0] = '-'; + z[1] = 's'; + memcpy (z + 2, argv[i] + 1, clen); + argv[i] = z; + } + } + + while ((iopt = getopt_long (argc, argv, "a:c:dehnI:l:op:s:tx:z:", + asCulongopts, (int *) NULL)) != EOF) + { + switch (iopt) + { + case 'c': + /* Phone number. */ + zphone = optarg; + break; + + case 'd': + /* Set debugging level to maximum. */ +#if DEBUG > 1 + iDebug = DEBUG_MAX; +#endif + break; + + case 'e': + /* Even parity. */ + feven = TRUE; + break; + + case 'h': + /* Local echo. */ + fCulocalecho = TRUE; + break; + + case 'n': + /* Prompt for phone number. */ + fprompt = TRUE; + break; + + case 'l': + /* Line name. */ + zline = optarg; + break; + + case 'o': + /* Odd parity. */ + fodd = TRUE; + break; + + case 'p': + case 'a': + /* Port name (-a is for compatibility). */ + zport = optarg; + break; + + case 's': + /* Speed. */ + ibaud = strtol (optarg, (char **) NULL, 10); + break; + + case 't': + /* Map cr to crlf. */ + fmapcr = TRUE; + break; + + case 'z': + /* System name. */ + zsystem = optarg; + break; + + case 'I': + /* Configuration file name. */ + if (fsysdep_other_config (optarg)) + zconfig = optarg; + break; + + case 'x': +#if DEBUG > 1 + /* Set debugging level. */ + iDebug |= idebug_parse (optarg); +#endif + break; + + case 0: + /* Long option found and flag set. */ + break; + + default: + ucuusage (); + break; + } + } + + /* There can be one more argument, which is either a system name, a + phone number, or "dir". We decide which it is based on the first + character. To call a UUCP system whose name begins with a digit, + or one which is named "dir", you must use -z. */ + if (optind != argc) + { + if (optind != argc - 1 + || zsystem != NULL + || zphone != NULL) + ucuusage (); + if (strcmp (argv[optind], "dir") != 0) + { + if (isdigit (BUCHAR (argv[optind][0]))) + zphone = argv[optind]; + else + zsystem = argv[optind]; + } + } + + /* If the user doesn't give a system, port, line or speed, then + there's no basis on which to select a port. */ + if (zsystem == NULL + && zport == NULL + && zline == NULL + && ibaud == 0L) + ucuusage (); + + if (fprompt) + { + size_t cphone; + + printf ("Phone number: "); + (void) fflush (stdout); + zphone = NULL; + cphone = 0; + if (getline (&zphone, &cphone, stdin) <= 0 + || *zphone == '\0') + { + fprintf (stderr, "%s: No phone number entered\n", abProgram); + exit (EXIT_FAILURE); + } + } + + iuuconf = uuconf_init (&puuconf, "cu", zconfig); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + pCuuuconf = puuconf; + +#if DEBUG > 1 + { + const char *zdebug; + + iuuconf = uuconf_debuglevel (puuconf, &zdebug); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + if (zdebug != NULL) + iDebug |= idebug_parse (zdebug); + } +#endif + + usysdep_initialize (puuconf, INIT_NOCHDIR | INIT_SUID); + + iuuconf = uuconf_localname (puuconf, &zlocalname); + if (iuuconf == UUCONF_NOT_FOUND) + { + zlocalname = zsysdep_localname (); + if (zlocalname == NULL) + exit (EXIT_FAILURE); + } + else if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + ulog_fatal_fn (ucuabort); + pfLstart = uculog_start; + pfLend = uculog_end; + +#ifdef SIGINT + usysdep_signal (SIGINT); +#endif +#ifdef SIGHUP + usysdep_signal (SIGHUP); +#endif +#ifdef SIGQUIT + usysdep_signal (SIGQUIT); +#endif +#ifdef SIGTERM + usysdep_signal (SIGTERM); +#endif +#ifdef SIGPIPE + usysdep_signal (SIGPIPE); +#endif + + if (zsystem != NULL) + { + iuuconf = uuconf_system_info (puuconf, zsystem, &ssys); + if (iuuconf != UUCONF_SUCCESS) + { + if (iuuconf != UUCONF_NOT_FOUND) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + ulog (LOG_FATAL, "%s: System not found", zsystem); + } + qsys = &ssys; + } + + /* This loop is used if a system is specified. It loops over the + various alternates until it finds one for which the dial + succeeds. This is an ugly spaghetti construction, and it should + be broken up into different functions someday. */ + flooped = FALSE; + while (TRUE) + { + enum tparitysetting tparity; + enum tstripsetting tstrip; + + /* The uuconf_find_port function only selects directly on a port + name and a speed. To select based on the line name, we use a + function. If we can't find any defined port, and the user + specified a line name but did not specify a port name or a + system or a phone number, then we fake a direct port with + that line name (we don't fake a port if a system or phone + number were given because if we fake a port we have no way to + place a call; perhaps we should automatically look up a + particular dialer). This permits users to say cu -lttyd0 + without having to put ttyd0 in the ports file, provided they + have read and write access to the port. */ + sinfo.fmatched = FALSE; + sinfo.flocked = FALSE; + sinfo.qconn = &sconn; + sinfo.zline = zline; + if (zport != NULL || zline != NULL || ibaud != 0L) + { + iuuconf = uuconf_find_port (puuconf, zport, ibaud, 0L, + icuport_lock, (pointer) &sinfo, + &sport); + if (iuuconf != UUCONF_SUCCESS) + { + if (iuuconf != UUCONF_NOT_FOUND) + { + if (sinfo.flocked) + { + (void) fconn_unlock (&sconn); + uconn_free (&sconn); + } + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + } + if (zline == NULL + || zport != NULL + || zphone != NULL + || qsys != NULL) + { + if (sinfo.fmatched) + ulog (LOG_FATAL, "All matching ports in use"); + else + ulog (LOG_FATAL, "No matching ports"); + } + + sport.uuconf_zname = zline; + sport.uuconf_ttype = UUCONF_PORTTYPE_DIRECT; + sport.uuconf_zprotocols = NULL; + sport.uuconf_qproto_params = NULL; + sport.uuconf_ireliable = 0; + sport.uuconf_zlockname = NULL; + sport.uuconf_palloc = NULL; + sport.uuconf_u.uuconf_sdirect.uuconf_zdevice = NULL; + sport.uuconf_u.uuconf_sdirect.uuconf_ibaud = ibaud; + + if (! fsysdep_port_access (&sport)) + ulog (LOG_FATAL, "%s: Permission denied", zline); + + if (! fconn_init (&sport, &sconn)) + ucuabort (); + + if (! fconn_lock (&sconn, FALSE)) + ulog (LOG_FATAL, "%s: Line in use", zline); + + qCuconn = &sconn; + } + ihighbaud = 0L; + } + else + { + for (; qsys != NULL; qsys = qsys->uuconf_qalternate) + { + if (! qsys->uuconf_fcall) + continue; + if (qsys->uuconf_qport != NULL) + { + if (fconn_init (qsys->uuconf_qport, &sconn)) + { + if (fconn_lock (&sconn, FALSE)) + { + qCuconn = &sconn; + break; + } + uconn_free (&sconn); + } + } + else + { + sinfo.fmatched = FALSE; + sinfo.flocked = FALSE; + sinfo.qconn = &sconn; + iuuconf = uuconf_find_port (puuconf, qsys->uuconf_zport, + qsys->uuconf_ibaud, + qsys->uuconf_ihighbaud, + icuport_lock, + (pointer) &sinfo, + &sport); + if (iuuconf == UUCONF_SUCCESS) + break; + if (iuuconf != UUCONF_NOT_FOUND) + { + if (sinfo.flocked) + { + (void) fconn_unlock (&sconn); + uconn_free (&sconn); + } + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + } + } + } + + if (qsys == NULL) + { + const char *zrem; + + if (flooped) + zrem = "remaining "; + else + zrem = ""; + if (sinfo.fmatched) + ulog (LOG_FATAL, "%s: All %smatching ports in use", + zsystem, zrem); + else + ulog (LOG_FATAL, "%s: No %smatching ports", zsystem, zrem); + } + + ibaud = qsys->uuconf_ibaud; + ihighbaud = qsys->uuconf_ihighbaud; + } + + /* Here we have locked a connection to use. */ + if (! fconn_open (&sconn, ibaud, ihighbaud, FALSE)) + ucuabort (); + + fCuclose_conn = TRUE; + + if (FGOT_SIGNAL ()) + ucuabort (); + + /* Set up the connection. */ + if (fodd && feven) + { + tparity = PARITYSETTING_NONE; + tstrip = STRIPSETTING_SEVENBITS; + } + else if (fodd) + { + tparity = PARITYSETTING_ODD; + tstrip = STRIPSETTING_SEVENBITS; + } + else if (feven) + { + tparity = PARITYSETTING_EVEN; + tstrip = STRIPSETTING_SEVENBITS; + } + else + { + tparity = PARITYSETTING_DEFAULT; + tstrip = STRIPSETTING_DEFAULT; + } + + if (! fconn_set (&sconn, tparity, tstrip, XONXOFF_ON)) + ucuabort (); + + if (qsys != NULL) + zphone = qsys->uuconf_zphone; + + if (qsys != NULL || zphone != NULL) + { + enum tdialerfound tdialer; + + if (! fconn_dial (&sconn, puuconf, qsys, zphone, &sdialer, + &tdialer)) + { + if (zport != NULL + || zline != NULL + || ibaud != 0L + || qsys == NULL) + ucuabort (); + + if (qsys->uuconf_qalternate == NULL) + ulog (LOG_FATAL, "%s: No remaining alternates", zsystem); + + fCuclose_conn = FALSE; + (void) fconn_close (&sconn, pCuuuconf, qCudialer, FALSE); + qCuconn = NULL; + (void) fconn_unlock (&sconn); + uconn_free (&sconn); + + /* Loop around and try another alternate. */ + flooped = TRUE; + continue; + } + if (tdialer == DIALERFOUND_FALSE) + qdialer = NULL; + else + qdialer = &sdialer; + } + else + { + /* If no system or phone number was specified, we connect + directly to the modem. We only permit this if the user + has access to the port, since it permits various + shenanigans such as reprogramming the automatic + callbacks. */ + if (! fsysdep_port_access (sconn.qport)) + ulog (LOG_FATAL, "Access to port denied"); + qdialer = NULL; + if (! fconn_carrier (&sconn, FALSE)) + ulog (LOG_FATAL, "Can't turn off carrier"); + } + + break; + } + + qCudialer = qdialer; + + if (FGOT_SIGNAL ()) + ucuabort (); + + /* Here we have connected, and can start the main cu protocol. The + program spends most of its time in system dependent code, and + only comes out when a special command is received from the + terminal. */ + printf ("%s\n", ZCONNMSG); + + if (! fsysdep_terminal_raw (fCulocalecho)) + ucuabort (); + + fCurestore_terminal = TRUE; + + if (! fsysdep_cu_init (&sconn)) + ucuabort (); + + fCustarted = TRUE; + + while (fsysdep_cu (&sconn, &bcmd, zlocalname)) + if (! fcudo_cmd (puuconf, &sconn, bcmd)) + break; + + fCustarted = FALSE; + if (! fsysdep_cu_finish ()) + ucuabort (); + + fCurestore_terminal = FALSE; + (void) fsysdep_terminal_restore (); + + (void) fconn_close (&sconn, puuconf, qdialer, TRUE); + (void) fconn_unlock (&sconn); + uconn_free (&sconn); + + printf ("\n%s\n", ZDISMSG); + + ulog_close (); + + usysdep_exit (TRUE); + + /* Avoid errors about not returning a value. */ + return 0; +} + +/* Print a usage message and die. */ + +static void +ucuusage () +{ + fprintf (stderr, + "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n", + VERSION); + fprintf (stderr, + "Usage: cu [options] [system or phone-number]\n"); + fprintf (stderr, + " -a port, -p port: Use named port\n"); + fprintf (stderr, + " -l line: Use named device (e.g. tty0)\n"); + fprintf (stderr, + " -s speed, -#: Use given speed\n"); + fprintf (stderr, + " -c phone: Phone number to call\n"); + fprintf (stderr, + " -z system: System to call\n"); + fprintf (stderr, + " -e: Set even parity\n"); + fprintf (stderr, + " -o: Set odd parity\n"); + fprintf (stderr, + " -h: Echo locally\n"); + fprintf (stderr, + " -t: Map carriage return to carriage return/linefeed\n"); + fprintf (stderr, + " -n: Prompt for phone number\n"); + fprintf (stderr, + " -d: Set maximum debugging level\n"); + fprintf (stderr, + " -x debug: Set debugging type\n"); +#if HAVE_TAYLOR_CONFIG + fprintf (stderr, + " -I file: Set configuration file to use\n"); +#endif /* HAVE_TAYLOR_CONFIG */ + + exit (EXIT_FAILURE); +} + +/* This function is called when a fatal error occurs. */ + +static void +ucuabort () +{ + if (fCustarted) + { + fCustarted = FALSE; + (void) fsysdep_cu_finish (); + } + + if (fCurestore_terminal) + { + fCurestore_terminal = FALSE; + (void) fsysdep_terminal_restore (); + } + + if (qCuconn != NULL) + { + struct sconnection *qconn; + + if (fCuclose_conn) + { + fCuclose_conn = FALSE; + (void) fconn_close (qCuconn, pCuuuconf, qCudialer, FALSE); + } + qconn = qCuconn; + qCuconn = NULL; + (void) fconn_unlock (qconn); + uconn_free (qconn); + } + + ulog_close (); + + printf ("\n%s\n", ZDISMSG); + + usysdep_exit (FALSE); +} + +/* This variable is just used to communicate between uculog_start and + uculog_end. */ +static boolean fCulog_restore; + +/* This function is called by ulog before it output anything. We use + it to restore the terminal, if necessary. ulog is only called for + errors or debugging in cu, so it's not too costly to do this. If + we didn't do it, then at least on Unix each line would leave the + cursor in the same column rather than wrapping back to the start, + since CRMOD will not be on. */ + +static void +uculog_start () +{ + if (! fCurestore_terminal) + fCulog_restore = FALSE; + else + { + fCulog_restore = TRUE; + fCurestore_terminal = FALSE; + if (! fsysdep_terminal_restore ()) + ucuabort (); + } +} + +/* This function is called by ulog after everything is output. It + sets the terminal back, if necessary. */ + +static void +uculog_end () +{ + if (fCulog_restore) + { + if (! fsysdep_terminal_raw (fCulocalecho)) + ucuabort (); + fCurestore_terminal = TRUE; + } +} + +/* Check to see if this port has the desired line, to handle the -l + option. If it does, or if no line was specified, set up a + connection and lock it. */ + +static int +icuport_lock (qport, pinfo) + struct uuconf_port *qport; + pointer pinfo; +{ + struct sconninfo *q = (struct sconninfo *) pinfo; + + if (q->zline != NULL + && ! fsysdep_port_is_line (qport, q->zline)) + return UUCONF_NOT_FOUND; + + q->fmatched = TRUE; + + if (! fconn_init (qport, q->qconn)) + return UUCONF_NOT_FOUND; + else if (! fconn_lock (q->qconn, FALSE)) + { + uconn_free (q->qconn); + return UUCONF_NOT_FOUND; + } + else + { + qCuconn = q->qconn; + q->flocked = TRUE; + return UUCONF_SUCCESS; + } +} + +/* Execute a cu escape command. Return TRUE if the connection should + continue, or FALSE if the connection should be terminated. */ + +static boolean +fcudo_cmd (puuconf, qconn, bcmd) + pointer puuconf; + struct sconnection *qconn; + int bcmd; +{ + char *zline; + char *z; + char abescape[5]; + boolean fret; + size_t clen; + char abbuf[100]; + + /* Some commands take a string up to the next newline character. */ + switch (bcmd) + { + default: + zline = NULL; + break; + case '!': + case '$': + case '|': + case '+': + case '%': + case 'c': + case '>': + case '<': + case 'p': + case 't': + case 's': + { + zline = zsysdep_terminal_line ((const char *) NULL); + if (zline == NULL) + ucuabort (); + zline[strcspn (zline, "\n")] = '\0'; + } + break; + } + + switch (bcmd) + { + default: + if (! isprint (*zCuvar_escape)) + sprintf (abescape, "\\%03o", (unsigned int) *zCuvar_escape); + else + { + abescape[0] = *zCuvar_escape; + abescape[1] = '\0'; + } + sprintf (abbuf, "[Unrecognized. Use %s%s to send %s]", + abescape, abescape, abescape); + ucuputs (abbuf); + return TRUE; + + case '.': + /* Hangup. */ + return FALSE; + + case '!': + case '$': + case '|': + case '+': + /* Shell out. */ + if (! fsysdep_cu_copy (FALSE) + || ! fsysdep_terminal_restore ()) + ucuabort (); + fCurestore_terminal = FALSE; + { + enum tshell_cmd t; + + switch (bcmd) + { + default: + case '!': t = SHELL_NORMAL; break; + case '$': t = SHELL_STDOUT_TO_PORT; break; + case '|': t = SHELL_STDIN_FROM_PORT; break; + case '+': t = SHELL_STDIO_ON_PORT; break; + } + + (void) fsysdep_shell (qconn, zline, t); + } + if (! fsysdep_cu_copy (TRUE) + || ! fsysdep_terminal_raw (fCulocalecho)) + ucuabort (); + fCurestore_terminal = TRUE; + ubuffree (zline); + return TRUE; + + case '%': + fret = fcudo_subcmd (puuconf, qconn, zline); + ubuffree (zline); + return fret; + + case '#': + if (! fconn_break (qconn)) + ucuabort (); + return TRUE; + + case 'c': + (void) fsysdep_chdir (zline); + ubuffree (zline); + return TRUE; + + case '>': + case '<': + case 'p': + case 't': + clen = strlen (zline); + z = zbufalc (clen + 3); + z[0] = bcmd; + z[1] = ' '; + memcpy (z + 2, zline, clen + 1); + ubuffree (zline); + fret = fcudo_subcmd (puuconf, qconn, z); + ubuffree (z); + return fret; + + case 'z': + if (! fsysdep_cu_copy (FALSE) + || ! fsysdep_terminal_restore ()) + ucuabort (); + fCurestore_terminal = FALSE; + if (! fsysdep_suspend ()) + ucuabort (); + if (! fsysdep_cu_copy (TRUE) + || ! fsysdep_terminal_raw (fCulocalecho)) + ucuabort (); + fCurestore_terminal = TRUE; + return TRUE; + + case 's': + fret = fcuset_var (puuconf, zline); + ubuffree (zline); + return fret; + + case 'v': + uculist_vars (); + return TRUE; + + case '?': + if (! isprint (*zCuvar_escape)) + sprintf (abescape, "\\%03o", (unsigned int) *zCuvar_escape); + else + { + abescape[0] = *zCuvar_escape; + abescape[1] = '\0'; + } + ucuputs (""); + ucuputs ("[Escape sequences]"); + sprintf (abbuf, + "[%s. hangup] [%s!CMD run shell]", + abescape, abescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%s$CMD stdout to remote] [%s|CMD stdin from remote]", + abescape, abescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%s+CMD stdin and stdout to remote]", + abescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%s# send break] [%scDIR change directory]", + abescape, abescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%s> send file] [%s< receive file]", + abescape, abescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%spFROM TO send to Unix] [%stFROM TO receive from Unix]", + abescape, abescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%ssVAR VAL set variable] [%ssVAR set boolean]", + abescape, abescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%ss!VAR unset boolean] [%sv list variables]", + abescape, abescape); + ucuputs (abbuf); +#ifdef SIGTSTP + sprintf (abbuf, + "[%sz suspend]", + abescape); + ucuputs (abbuf); +#endif + uculist_fns (abescape); + return TRUE; + } +} + +/* List ~% functions. */ + +static void +uculist_fns (zescape) + const char *zescape; +{ + char abbuf[100]; + + sprintf (abbuf, + "[%s%%break send break] [%s%%cd DIR change directory]", + zescape, zescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%s%%put FROM TO send file] [%s%%take FROM TO receive file]", + zescape, zescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%s%%nostop no XON/XOFF] [%s%%stop use XON/XOFF]", + zescape, zescape); + ucuputs (abbuf); +} + +/* Set a variable. */ + +static boolean +fcuset_var (puuconf, zline) + pointer puuconf; + char *zline; +{ + char *zvar, *zval; + char *azargs[2]; + char azbool[2]; + int iuuconf; + + zvar = strtok (zline, "= \t"); + if (zvar == NULL) + { + ucuputs (abCuconnected); + return TRUE; + } + + zval = strtok ((char *) NULL, " \t"); + + if (zval == NULL) + { + azargs[0] = zvar; + if (azargs[0][0] != '!') + azbool[0] = 't'; + else + { + ++azargs[0]; + azbool[0] = 'f'; + } + azbool[1] = '\0'; + azargs[1] = azbool; + } + else + { + azargs[0] = zvar; + azargs[1] = zval; + } + + iuuconf = uuconf_cmd_args (puuconf, 2, azargs, asCuvars, + (pointer) NULL, icuunrecogvar, 0, + (pointer) NULL); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + + return TRUE; +} + +/* Warn about an unknown variable. */ + +/*ARGSUSED*/ +static int +icuunrecogvar (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + char abescape[5]; + + if (! isprint (*zCuvar_escape)) + sprintf (abescape, "\\%03o", (unsigned int) *zCuvar_escape); + else + { + abescape[0] = *zCuvar_escape; + abescape[1] = '\0'; + } + ulog (LOG_ERROR, "%s: unknown variable (%sv lists variables)", + argv[0], abescape); + return UUCONF_CMDTABRET_CONTINUE; +} + +/* List all the variables with their values. */ + +static void +uculist_vars () +{ + const struct uuconf_cmdtab *q; + char abbuf[100]; + + ucuputs (""); + for (q = asCuvars; q->uuconf_zcmd != NULL; q++) + { + switch (UUCONF_TTYPE_CMDTABTYPE (q->uuconf_itype)) + { + case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_BOOLEAN): + if (*(boolean *) q->uuconf_pvar) + sprintf (abbuf, "%s true", q->uuconf_zcmd); + else + sprintf (abbuf, "%s false", q->uuconf_zcmd); + break; + + case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_INT): + sprintf (abbuf, "%s %d", q->uuconf_zcmd, *(int *) q->uuconf_pvar); + break; + + case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_LONG): + sprintf (abbuf, "%s %ld", q->uuconf_zcmd, + *(long *) q->uuconf_pvar); + break; + + case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_STRING): + case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_FULLSTRING): + { + const char *z; + char abchar[5]; + size_t clen; + + sprintf (abbuf, "%s ", q->uuconf_zcmd); + clen = strlen (abbuf); + for (z = *(const char **) q->uuconf_pvar; *z != '\0'; z++) + { + int cchar; + + if (! isprint (*z)) + { + sprintf (abchar, "\\%03o", (unsigned int) *z); + cchar = 4; + } + else + { + abchar[0] = *z; + abchar[1] = '\0'; + cchar = 1; + } + if (clen + cchar < sizeof (abbuf)) + strcat (abbuf, abchar); + clen += cchar; + } + } + break; + + default: + sprintf (abbuf, "%s [unprintable type]", q->uuconf_zcmd); + break; + } + + ucuputs (abbuf); + } +} + +/* Subcommands. These are commands that begin with ~%. */ + +/* This variable is only used so that we can pass a non-NULL address + in pvar. It is never assigned to or examined. */ + +static char bCutype; + +/* The command table for the subcommands. */ + +static int icubreak P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int icudebug P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int icuchdir P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int icuput P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int icutake P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int icunostop P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); + +static const struct uuconf_cmdtab asCucmds[] = +{ + { "break", UUCONF_CMDTABTYPE_FN | 1, NULL, icubreak }, + { "b", UUCONF_CMDTABTYPE_FN | 1, NULL, icubreak }, + { "cd", UUCONF_CMDTABTYPE_FN | 0, NULL, icuchdir }, + { "d", UUCONF_CMDTABTYPE_FN | 1, NULL, icudebug }, + { "put", UUCONF_CMDTABTYPE_FN | 0, NULL, icuput }, + { "take", UUCONF_CMDTABTYPE_FN | 0, NULL, icutake }, + { "nostop", UUCONF_CMDTABTYPE_FN | 1, NULL, icunostop }, + { "stop", UUCONF_CMDTABTYPE_FN | 1, &bCutype, icunostop }, + { ">", UUCONF_CMDTABTYPE_FN | 0, &bCutype, icuput }, + { "<", UUCONF_CMDTABTYPE_FN | 0, &bCutype, icutake }, + { "p", UUCONF_CMDTABTYPE_FN | 0, NULL, icuput }, + { "t", UUCONF_CMDTABTYPE_FN | 0, NULL, icutake }, + { NULL, 0, NULL, NULL } +}; + +/* Do a subcommand. This is called by commands beginning with ~%. */ + +static boolean +fcudo_subcmd (puuconf, qconn, zline) + pointer puuconf; + struct sconnection *qconn; + char *zline; +{ + char *azargs[3]; + int iarg; + int iuuconf; + + for (iarg = 0; iarg < 3; iarg++) + { + azargs[iarg] = strtok (iarg == 0 ? zline : (char *) NULL, " \t\n"); + if (azargs[iarg] == NULL) + break; + } + + if (iarg == 0) + { + ucuputs (abCuconnected); + return TRUE; + } + + iuuconf = uuconf_cmd_args (puuconf, iarg, azargs, asCucmds, + (pointer) qconn, icuunrecogfn, + 0, (pointer) NULL); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + + return TRUE; +} + +/* Warn about an unknown function. */ + +/*ARGSUSED*/ +static int +icuunrecogfn (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + char abescape[5]; + + if (! isprint (*zCuvar_escape)) + sprintf (abescape, "\\%03o", (unsigned int) *zCuvar_escape); + else + { + abescape[0] = *zCuvar_escape; + abescape[1] = '\0'; + } + if (argv[0][0] == '?') + uculist_fns (abescape); + else + ulog (LOG_ERROR, "%s: unknown (%s%%? lists choices)", + argv[0], abescape); + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Send a break. */ + +/*ARGSUSED*/ +static int +icubreak (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sconnection *qconn = (struct sconnection *) pinfo; + + if (! fconn_break (qconn)) + ucuabort (); + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Change directories. */ + +/*ARGSUSED*/ +static int +icuchdir (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + const char *zarg; + + if (argc <= 1) + zarg = NULL; + else + zarg = argv[1]; + (void) fsysdep_chdir (zarg); + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Toggle debugging. */ + +/*ARGSUSED*/ +static int +icudebug (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ +#if DEBUG > 1 + if (iDebug != 0) + iDebug = 0; + else + iDebug = DEBUG_MAX; +#else + ucuputs ("[compiled without debugging]"); +#endif + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Control whether the port does xon/xoff handshaking. If pvar is not + NULL, this is "stop"; otherwise it is "nostop". */ + +/*ARGSUSED*/ +static int +icunostop (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sconnection *qconn = (struct sconnection *) pinfo; + + if (! fconn_set (qconn, PARITYSETTING_DEFAULT, STRIPSETTING_DEFAULT, + pvar == NULL ? XONXOFF_OFF : XONXOFF_ON)) + ucuabort (); + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Send a file to the remote system. The first argument is the file + to send. If that argument is not present, it is prompted for. The + second argument is to file name to use on the remote system. If + that argument is not present, the basename of the local filename is + used. If pvar is not NULL, then this is ~>, which is used to send + a command to a non-Unix system. We treat is the same as ~%put, + except that we assume the user has already entered the appropriate + command (for ~%put, we force ``cat >to'' to the other side). */ + +/*ARGSUSED*/ +static int +icuput (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sconnection *qconn = (struct sconnection *) pinfo; + char *zfrom; + char *zto = NULL; + char *zalc; + openfile_t e; + int cline; + char *zbuf; + size_t cbuf; + + if (argc > 1) + zfrom = zbufcpy (argv[1]); + else + { + zfrom = zsysdep_terminal_line ("File to send: "); + if (zfrom == NULL) + ucuabort (); + zfrom[strcspn (zfrom, " \t\n")] = '\0'; + + if (*zfrom == '\0') + { + ubuffree (zfrom); + ucuputs (abCuconnected); + return UUCONF_CMDTABRET_CONTINUE; + } + } + + if (pvar == NULL) + { + if (argc > 2) + zto = zbufcpy (argv[2]); + else + { + char *zbase; + char *zprompt; + + zbase = zsysdep_base_name (zfrom); + if (zbase == NULL) + ucuabort (); + + zprompt = zbufalc (sizeof "Remote file name []: " + + strlen (zbase)); + sprintf (zprompt, "Remote file name [%s]: ", zbase); + zto = zsysdep_terminal_line (zprompt); + ubuffree (zprompt); + if (zto == NULL) + ucuabort (); + + zto[strcspn (zto, " \t\n")] = '\0'; + if (*zto != '\0') + ubuffree (zbase); + else + { + ubuffree (zto); + zto = zbase; + } + } + } + + e = esysdep_user_fopen (zfrom, TRUE, fCuvar_binary); + if (! ffileisopen (e)) + { + const char *zerrstr; + + if (pvar == NULL) + ubuffree (zto); + zerrstr = strerror (errno); + zalc = zbufalc (strlen (zfrom) + sizeof ": " + strlen (zerrstr)); + sprintf (zalc, "%s: %s", zfrom, zerrstr); + ubuffree (zfrom); + ucuputs (zalc); + ubuffree (zalc); + ucuputs (abCuconnected); + return UUCONF_CMDTABRET_CONTINUE; + } + + ubuffree (zfrom); + + /* Tell the system dependent layer to stop copying data from the + port to the terminal. We want to read the echoes ourself. Also + permit the local user to generate signals. */ + if (! fsysdep_cu_copy (FALSE) + || ! fsysdep_terminal_signals (TRUE)) + ucuabort (); + + /* If pvar is NULL, then we are sending a file to a Unix system. We + send over the command "cat > TO" to prepare it to receive. If + pvar is not NULL, the user is assumed to have set up whatever + action was needed to receive the file. */ + if (pvar == NULL) + { + boolean fret; + + zalc = zbufalc (sizeof "cat > \n" + strlen (zto)); + sprintf (zalc, "cat > %s\n", zto); + ubuffree (zto); + fret = fcusend_buf (qconn, zalc, strlen (zalc)); + ubuffree (zalc); + if (! fret) + { + (void) ffileclose (e); + if (! fsysdep_cu_copy (TRUE) + || ! fsysdep_terminal_signals (FALSE)) + ucuabort (); + ucuputs (abCuconnected); + return UUCONF_CMDTABRET_CONTINUE; + } + } + + cline = 0; + + zbuf = NULL; + cbuf = 0; + + while (TRUE) + { + char abbuf[512]; + size_t c; + +#if USE_STDIO + if (fCuvar_binary) +#endif + { + if (ffileeof (e)) + break; + c = cfileread (e, abbuf, sizeof abbuf); + if (ffilereaderror (e, c)) + { + ucuputs ("[file read error]"); + break; + } + if (c == 0) + break; + zbuf = abbuf; + } +#if USE_STDIO + else + { + if (getline (&zbuf, &cbuf, e) <= 0) + { + xfree ((pointer) zbuf); + break; + } + c = strlen (zbuf); + } +#endif + + if (fCuvar_verbose) + { + ++cline; + printf ("%d ", cline); + (void) fflush (stdout); + } + + if (! fcusend_buf (qconn, zbuf, c)) + { + if (! fCuvar_binary) + xfree ((pointer) zbuf); + (void) fclose (e); + if (! fsysdep_cu_copy (TRUE) + || ! fsysdep_terminal_signals (FALSE)) + ucuabort (); + ucuputs (abCuconnected); + return UUCONF_CMDTABRET_CONTINUE; + } + } + + (void) ffileclose (e); + + if (pvar == NULL) + { + char beof; + + beof = '\004'; + if (! fconn_write (qconn, &beof, 1)) + ucuabort (); + } + else + { + if (*zCuvar_eofwrite != '\0') + { + if (! fconn_write (qconn, zCuvar_eofwrite, + strlen (zCuvar_eofwrite))) + ucuabort (); + } + } + + if (fCuvar_verbose) + ucuputs (""); + + ucuputs ("[file transfer complete]"); + + if (! fsysdep_cu_copy (TRUE) + || ! fsysdep_terminal_signals (FALSE)) + ucuabort (); + + ucuputs (abCuconnected); + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Get a file from the remote side. This is ~%take, or ~t, or ~<. + The first two are assumed to be taking the file from a Unix system, + so we force the command "cat FROM; echo */ + +/*ARGSUSED*/ +static int +icutake (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sconnection *qconn = (struct sconnection *) pinfo; + const char *zeof; + char *zfrom, *zto, *zcmd; + char *zalc; + openfile_t e; + char bcr; + size_t ceoflen; + char *zlook = NULL; + size_t ceofhave; + boolean ferr; + + if (argc > 1) + zfrom = zbufcpy (argv[1]); + else + { + zfrom = zsysdep_terminal_line ("Remote file to retreive: "); + if (zfrom == NULL) + ucuabort (); + zfrom[strcspn (zfrom, " \t\n")] = '\0'; + if (*zfrom == '\0') + { + ubuffree (zfrom); + ucuputs (abCuconnected); + return UUCONF_CMDTABRET_CONTINUE; + } + } + + if (argc > 2) + zto = zbufcpy (argv[2]); + else + { + char *zbase; + char *zprompt; + + zbase = zsysdep_base_name (zfrom); + if (zbase == NULL) + ucuabort (); + + zprompt = zbufalc (sizeof "Local file name []: " + strlen (zbase)); + sprintf (zprompt, "Local file name [%s]: ", zbase); + zto = zsysdep_terminal_line (zprompt); + ubuffree (zprompt); + if (zto == NULL) + ucuabort (); + + zto[strcspn (zto, " \t\n")] = '\0'; + if (*zto != '\0') + ubuffree (zbase); + else + { + ubuffree (zto); + zto = zbase; + } + } + + if (pvar != NULL) + { + zcmd = zsysdep_terminal_line ("Remote command to execute: "); + if (zcmd == NULL) + ucuabort (); + zcmd[strcspn (zcmd, "\n")] = '\0'; + zeof = zCuvar_eofread; + } + else + { + zcmd = zbufalc (sizeof "cat ; echo; echo ////cuend////" + + strlen (zfrom)); + sprintf (zcmd, "cat %s; echo; echo ////cuend////", zfrom); + zeof = "\n////cuend////\n"; + } + + ubuffree (zfrom); + + e = esysdep_user_fopen (zto, FALSE, fCuvar_binary); + if (! ffileisopen (e)) + { + const char *zerrstr; + + ubuffree (zcmd); + zerrstr = strerror (errno); + zalc = zbufalc (strlen (zto) + sizeof ": " + strlen (zerrstr)); + sprintf (zalc, "%s: %s\n", zto, zerrstr); + ucuputs (zalc); + ubuffree (zalc); + ucuputs (abCuconnected); + ubuffree (zto); + return UUCONF_CMDTABRET_CONTINUE; + } + + ubuffree (zto); + + if (! fsysdep_cu_copy (FALSE) + || ! fsysdep_terminal_signals (TRUE)) + ucuabort (); + + if (! fconn_write (qconn, zcmd, strlen (zcmd))) + ucuabort (); + bcr = '\r'; + if (! fconn_write (qconn, &bcr, 1)) + ucuabort (); + + ubuffree (zcmd); + + /* Eliminated any previously echoed data to avoid confusion. */ + iPrecstart = 0; + iPrecend = 0; + + /* If we're dealing with a Unix system, we can reliably discard the + command. Otherwise, the command will probably wind up in the + file; too bad. */ + if (pvar == NULL) + { + int b; + + while ((b = breceive_char (qconn, cCuvar_timeout, TRUE)) != '\n') + { + if (b == -2) + ucuabort (); + if (b < 0) + { + ucuputs ("[timed out waiting for newline]"); + ucuputs (abCuconnected); + return UUCONF_CMDTABRET_CONTINUE; + } + } + } + + ceoflen = strlen (zeof); + zlook = zbufalc (ceoflen); + ceofhave = 0; + ferr = FALSE; + + while (TRUE) + { + int b; + + if (FGOT_SIGNAL ()) + { + /* Make sure the signal is logged. */ + ulog (LOG_ERROR, (const char *) NULL); + ucuputs ("[file receive aborted]"); + /* Reset the SIGINT flag so that it does not confuse us in + the future. */ + afSignal[INDEXSIG_SIGINT] = FALSE; + break; + } + + b = breceive_char (qconn, cCuvar_timeout, TRUE); + if (b == -2) + ucuabort (); + if (b < 0) + { + if (ceofhave > 0) + (void) fwrite (zlook, sizeof (char), ceofhave, e); + ucuputs ("[timed out]"); + break; + } + + if (ceoflen == 0) + { + if (cfilewrite (e, &b, 1) != 1) + { + ferr = TRUE; + break; + } + } + else + { + zlook[ceofhave] = b; + ++ceofhave; + if (ceofhave == ceoflen) + { + size_t cmove; + char *zmove; + + if (memcmp (zeof, zlook, ceoflen) == 0) + { + ucuputs ("[file transfer complete]"); + break; + } + + if (cfilewrite (e, zlook, 1) != 1) + { + ferr = TRUE; + break; + } + + zmove = zlook; + for (cmove = ceoflen - 1, zmove = zlook; + cmove > 0; + cmove--, zmove++) + zmove[0] = zmove[1]; + + --ceofhave; + } + } + } + + ubuffree (zlook); + + if (! ffileclose (e)) + ferr = TRUE; + if (ferr) + ucuputs ("[file write error]"); + + if (! fsysdep_cu_copy (TRUE) + || ! fsysdep_terminal_signals (FALSE)) + ucuabort (); + + ucuputs (abCuconnected); + + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Send a buffer to the remote system. If fCuvar_binary is FALSE, + each buffer passed in will be a single line; in this case we can + check the echoed characters and kill the line if they do not match. + This returns FALSE if an echo check fails. If a port error + occurrs, it calls ucuabort. */ + +static boolean +fcusend_buf (qconn, zbufarg, cbufarg) + struct sconnection *qconn; + const char *zbufarg; + size_t cbufarg; +{ + const char *zbuf; + size_t cbuf; + int ctries; + size_t cbplen; + char *zsendbuf; + + zbuf = zbufarg; + cbuf = cbufarg; + ctries = 0; + + if (fCuvar_binary) + cbplen = strlen (zCuvar_binary_prefix); + else + cbplen = 1; + zsendbuf = zbufalc (64 * (cbplen + 1)); + + /* Loop while we still have characters to send. The value of cbuf + will be reset to cbufarg if an echo failure occurs while sending + a line in non-binary mode. */ + while (cbuf > 0) + { + int csend; + char *zput; + const char *zget; + boolean fnl; + int i; + + if (FGOT_SIGNAL ()) + { + /* Make sure the signal is logged. */ + ubuffree (zsendbuf); + ulog (LOG_ERROR, (const char *) NULL); + ucuputs ("[file send aborted]"); + /* Reset the SIGINT flag so that it does not confuse us in + the future. */ + afSignal[INDEXSIG_SIGINT] = FALSE; + return FALSE; + } + + /* Discard anything we've read from the port up to now, to avoid + confusing the echo checking. */ + iPrecstart = 0; + iPrecend = 0; + + /* Send all characters up to a newline before actually sending + the newline. This makes it easier to handle the special + newline echo checking. Send up to 64 characters at a time + before doing echo checking. */ + if (*zbuf == '\n') + csend = 1; + else + { + const char *znl; + + znl = memchr (zbuf, '\n', cbuf); + if (znl == NULL) + csend = cbuf; + else + csend = znl - zbuf; + if (csend > 64) + csend = 64; + } + + /* Translate this part of the buffer. If we are not in binary + mode, we translate \n to \r, and ignore any nonprintable + characters. */ + zput = zsendbuf; + fnl = FALSE; + for (i = 0, zget = zbuf; i < csend; i++, zget++) + { + if (isprint (*zget) + || *zget == '\t') + *zput++ = *zget; + else if (*zget == '\n') + { + if (fCuvar_binary) + *zput++ = '\n'; + else + *zput++ = '\r'; + fnl = TRUE; + } + else if (fCuvar_binary) + { + strcpy (zput, zCuvar_binary_prefix); + zput += cbplen; + *zput++ = *zget; + } + } + + zbuf += csend; + cbuf -= csend; + + if (zput == zsendbuf) + continue; + + /* Send the data over the port. */ + if (! fsend_data (qconn, zsendbuf, (size_t) (zput - zsendbuf), TRUE)) + ucuabort (); + + /* We do echo checking if requested, unless we are in binary + mode. Echo checking of a newline is different from checking + of normal characters; when we send a newline we look for + *zCuvar_echonl. */ + if ((fCuvar_echocheck && ! fCuvar_binary) + || (fnl && *zCuvar_echonl != '\0')) + { + long iend; + + iend = ixsysdep_time ((long *) NULL) + (long) cCuvar_timeout; + for (zget = zsendbuf; zget < zput; zget++) + { + int bread; + int bwant; + + if (fCuvar_binary ? *zget == '\n' : *zget == '\r') + { + bwant = *zCuvar_echonl; + if (bwant == '\0') + continue; + } + else + { + if (! fCuvar_echocheck || ! isprint (*zget)) + continue; + bwant = *zget; + } + + do + { + if (FGOT_SIGNAL ()) + { + /* Make sure the signal is logged. */ + ubuffree (zsendbuf); + ulog (LOG_ERROR, (const char *) NULL); + ucuputs ("[file send aborted]"); + /* Reset the SIGINT flag so that it does not + confuse us in the future. */ + afSignal[INDEXSIG_SIGINT] = FALSE; + return FALSE; + } + + bread = breceive_char (qconn, + iend - ixsysdep_time ((long *) NULL), + TRUE); + if (bread < 0) + { + if (bread == -2) + ucuabort (); + + /* If we timed out, and we're not in binary + mode, we kill the line and try sending it + again from the beginning. */ + if (! fCuvar_binary && *zCuvar_kill != '\0') + { + ++ctries; + if (ctries < cCuvar_resend) + { + if (fCuvar_verbose) + { + printf ("R "); + (void) fflush (stdout); + } + if (! fsend_data (qconn, zCuvar_kill, 1, + TRUE)) + ucuabort (); + zbuf = zbufarg; + cbuf = cbufarg; + break; + } + } + ubuffree (zsendbuf); + ucuputs ("[timed out looking for echo]"); + return FALSE; + } + } + while (bread != *zget); + + if (bread < 0) + break; + } + } + } + + ubuffree (zsendbuf); + + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/MANIFEST b/gnu/libexec/uucp/libunix/MANIFEST new file mode 100644 index 0000000000..d64e799220 --- /dev/null +++ b/gnu/libexec/uucp/libunix/MANIFEST @@ -0,0 +1,76 @@ +Makefile.in +MANIFEST +access.c +addbas.c +app3.c +app4.c +basnam.c +bytfre.c +chmod.c +cohtty.c +cwd.c +cusub.c +detach.c +dirent.c +dup2.c +efopen.c +epopen.c +exists.c +filnam.c +fsusg.c +fsusg.h +ftw.c +getcwd.c +indir.c +init.c +isdir.c +isfork.c +iswait.c +jobid.c +lcksys.c +link.c +locfil.c +lock.c +loctim.c +mail.c +mkdir.c +mkdirs.c +mode.c +move.c +opensr.c +pause.c +picksb.c +portnm.c +proctm.c +recep.c +remove.c +rename.c +rmdir.c +run.c +seq.c +serial.c +signal.c +sindir.c +size.c +sleep.c +splcmd.c +splnam.c +spool.c +spawn.c +srmdir.c +statsb.c +status.c +strerr.c +time.c +tmpfil.c +trunc.c +uacces.c +ufopen.c +ultspl.c +unknwn.c +uuto.c +walk.c +wldcrd.c +work.c +xqtfil.c +xqtsub.c diff --git a/gnu/libexec/uucp/libunix/Makefile b/gnu/libexec/uucp/libunix/Makefile new file mode 100644 index 0000000000..a2cb537acb --- /dev/null +++ b/gnu/libexec/uucp/libunix/Makefile @@ -0,0 +1,22 @@ +# This subdirectory contains Unix specific support functions. +# $Id: Makefile,v 1.2 1993/08/05 16:14:51 jtc Exp $ + +LIB= unix +SRCS= access.c addbas.c app3.c app4.c basnam.c bytfre.c cwd.c \ + chmod.c cohtty.c cusub.c detach.c efopen.c epopen.c exists.c \ + filnam.c fsusg.c indir.c init.c isdir.c isfork.c iswait.c \ + jobid.c lcksys.c link.c locfil.c lock.c loctim.c mail.c \ + mkdirs.c mode.c move.c opensr.c pause.c picksb.c portnm.c \ + proctm.c recep.c run.c seq.c serial.c signal.c sindir.c size.c \ + sleep.c spawn.c splcmd.c splnam.c spool.c srmdir.c statsb.c \ + status.c time.c tmpfil.c trunc.c uacces.c ufopen.c ultspl.c \ + unknwn.c uuto.c walk.c wldcrd.c work.c xqtfil.c xqtsub.c ftw.c +CFLAGS+= -I$(.CURDIR)/../common_sources \ + -DOWNER=\"$(owner)\" -DSBINDIR=\"$(sbindir)\" + +NOMAN= noman +NOPROFILE= noprofile + +install: + +.include diff --git a/gnu/libexec/uucp/libunix/access.c b/gnu/libexec/uucp/libunix/access.c new file mode 100644 index 0000000000..c2c0eef210 --- /dev/null +++ b/gnu/libexec/uucp/libunix/access.c @@ -0,0 +1,83 @@ +/* access.c + Check access to files by the user and by the daemon. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +/* See if the user has access to a file, to prevent the setuid uucp + and uux programs handing out unauthorized access. */ + +boolean +fsysdep_access (zfile) + const char *zfile; +{ + if (access (zfile, R_OK) == 0) + return TRUE; + ulog (LOG_ERROR, "%s: %s", zfile, strerror (errno)); + return FALSE; +} + +/* See if the daemon has access to a file. This is called if a file + is not being transferred to the spool directory, since if the + daemon does not have access the later transfer will fail. We + assume that the daemon will have the same euid (or egid) as the one + we are running under. If our uid (gid) and euid (egid) are the + same, we assume that we have access. Note that is not important + for security, since the check will be (implicitly) done again when + the daemon tries to transfer the file. This routine should work + whether the UUCP programs are installed setuid or setgid. */ + +boolean +fsysdep_daemon_access (zfile) + const char *zfile; +{ + struct stat s; + uid_t ieuid, iuid, iegid, igid; + boolean fok; + + ieuid = geteuid (); + if (ieuid == 0) + return TRUE; + iuid = getuid (); + iegid = getegid (); + igid = getgid (); + + /* If our effective uid and gid are the same as our real uid and + gid, we assume the daemon will have access to the file. */ + if (ieuid == iuid && iegid == igid) + return TRUE; + + if (stat ((char *) zfile, &s) != 0) + { + ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno)); + return FALSE; + } + + /* If our euid is not our uid, but it is the file's uid, see if the + owner has read access. Otherwise, if our egid is not our gid, + but it is the file's gid, see if the group has read access. + Otherwise, see if the world has read access. We know from the + above check that at least one of our euid and egid are different, + so that is the only one we want to check. This check could fail + if the UUCP programs were both setuid and setgid, but why would + they be? */ + if (ieuid != iuid && ieuid == s.st_uid) + fok = (s.st_mode & S_IRUSR) != 0; + else if (iegid != igid && iegid == s.st_gid) + fok = (s.st_mode & S_IRGRP) != 0; + else + fok = (s.st_mode & S_IROTH) != 0; + + if (! fok) + { + ulog (LOG_ERROR, "%s: cannot be read by daemon", zfile); + return FALSE; + } + + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/addbas.c b/gnu/libexec/uucp/libunix/addbas.c new file mode 100644 index 0000000000..8597918a3c --- /dev/null +++ b/gnu/libexec/uucp/libunix/addbas.c @@ -0,0 +1,50 @@ +/* addbas.c + If we have a directory, add in a base name. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +/* If we have a directory, add a base name. */ + +char * +zsysdep_add_base (zfile, zname) + const char *zfile; + const char *zname; +{ + size_t clen; + const char *zlook; + char *zfree; + char *zret; + +#if DEBUG > 0 + if (*zfile != '/') + ulog (LOG_FATAL, "zsysdep_add_base: %s: Can't happen", zfile); +#endif + + clen = strlen (zfile); + + if (zfile[clen - 1] != '/') + { + if (! fsysdep_directory (zfile)) + return zbufcpy (zfile); + zfree = NULL; + } + else + { + /* Trim out the trailing '/'. */ + zfree = zbufcpy (zfile); + zfree[clen - 1] = '\0'; + zfile = zfree; + } + + zlook = strrchr (zname, '/'); + if (zlook != NULL) + zname = zlook + 1; + + zret = zsysdep_in_dir (zfile, zname); + ubuffree (zfree); + return zret; +} diff --git a/gnu/libexec/uucp/libunix/app3.c b/gnu/libexec/uucp/libunix/app3.c new file mode 100644 index 0000000000..5c0b589385 --- /dev/null +++ b/gnu/libexec/uucp/libunix/app3.c @@ -0,0 +1,29 @@ +/* app3.c + Stick two directories and a file name together. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" + +char * +zsappend3 (zdir1, zdir2, zfile) + const char *zdir1; + const char *zdir2; + const char *zfile; +{ + size_t cdir1, cdir2, cfile; + char *zret; + + cdir1 = strlen (zdir1); + cdir2 = strlen (zdir2); + cfile = strlen (zfile); + zret = zbufalc (cdir1 + cdir2 + cfile + 3); + memcpy (zret, zdir1, cdir1); + memcpy (zret + cdir1 + 1, zdir2, cdir2); + memcpy (zret + cdir1 + cdir2 + 2, zfile, cfile); + zret[cdir1] = '/'; + zret[cdir1 + cdir2 + 1] = '/'; + zret[cdir1 + cdir2 + cfile + 2] = '\0'; + return zret; +} diff --git a/gnu/libexec/uucp/libunix/app4.c b/gnu/libexec/uucp/libunix/app4.c new file mode 100644 index 0000000000..a3b3787f68 --- /dev/null +++ b/gnu/libexec/uucp/libunix/app4.c @@ -0,0 +1,33 @@ +/* app4.c + Stick three directories and a file name together. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" + +char * +zsappend4 (zdir1, zdir2, zdir3, zfile) + const char *zdir1; + const char *zdir2; + const char *zdir3; + const char *zfile; +{ + size_t cdir1, cdir2, cdir3, cfile; + char *zret; + + cdir1 = strlen (zdir1); + cdir2 = strlen (zdir2); + cdir3 = strlen (zdir3); + cfile = strlen (zfile); + zret = zbufalc (cdir1 + cdir2 + cdir3 + cfile + 4); + memcpy (zret, zdir1, cdir1); + memcpy (zret + cdir1 + 1, zdir2, cdir2); + memcpy (zret + cdir1 + cdir2 + 2, zdir3, cdir3); + memcpy (zret + cdir1 + cdir2 + cdir3 + 3, zfile, cfile); + zret[cdir1] = '/'; + zret[cdir1 + cdir2 + 1] = '/'; + zret[cdir1 + cdir2 + cdir3 + 2] = '/'; + zret[cdir1 + cdir2 + cdir3 + cfile + 3] = '\0'; + return zret; +} diff --git a/gnu/libexec/uucp/libunix/basnam.c b/gnu/libexec/uucp/libunix/basnam.c new file mode 100644 index 0000000000..c61fcaa8de --- /dev/null +++ b/gnu/libexec/uucp/libunix/basnam.c @@ -0,0 +1,22 @@ +/* basnam.c + Get the base name of a file. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +/* Get the base name of a file name. */ + +char * +zsysdep_base_name (zfile) + const char *zfile; +{ + const char *z; + + z = strrchr (zfile, '/'); + if (z != NULL) + return zbufcpy (z + 1); + return zbufcpy (zfile); +} diff --git a/gnu/libexec/uucp/libunix/bytfre.c b/gnu/libexec/uucp/libunix/bytfre.c new file mode 100644 index 0000000000..568eebe030 --- /dev/null +++ b/gnu/libexec/uucp/libunix/bytfre.c @@ -0,0 +1,19 @@ +/* bytfre.c + Get the number of bytes free on a file system. */ + +#include "uucp.h" + +#include "system.h" +#include "sysdep.h" +#include "fsusg.h" + +long +csysdep_bytes_free (zfile) + const char *zfile; +{ + struct fs_usage s; + + if (get_fs_usage ((char *) zfile, (char *) NULL, &s) < 0) + return -1; + return s.fsu_bavail * (long) 512; +} diff --git a/gnu/libexec/uucp/libunix/chmod.c b/gnu/libexec/uucp/libunix/chmod.c new file mode 100644 index 0000000000..cf69f3eb01 --- /dev/null +++ b/gnu/libexec/uucp/libunix/chmod.c @@ -0,0 +1,25 @@ +/* chmod.c + Change the mode of a file. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +/* Change the mode of a file. */ + +boolean +fsysdep_change_mode (zfile, imode) + const char *zfile; + unsigned int imode; +{ + if (chmod ((char *) zfile, imode) < 0) + { + ulog (LOG_ERROR, "chmod (%s): %s", zfile, strerror (errno)); + return FALSE; + } + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/cohtty.c b/gnu/libexec/uucp/libunix/cohtty.c new file mode 100644 index 0000000000..a7aec1cae3 --- /dev/null +++ b/gnu/libexec/uucp/libunix/cohtty.c @@ -0,0 +1,244 @@ +/* Coherent tty locking support. This file was contributed by Bob + Hemedinger of Mark Williams Corporation and + lightly edited by Ian Lance Taylor. */ + +/* The bottom part of this file is lock.c. + * This is a hacked lock.c. A full lock.c can be found in the libmisc sources + * under /usr/src/misc.tar.Z. + * + * These are for checking for the existence of locks: + * lockexist(resource) + * lockttyexist(ttyname) + */ + +#include "uucp.h" + +#if HAVE_COHERENT_LOCKFILES + +/* cohtty.c: Given a serial device name, read /etc/ttys and determine if + * the device is already enabled. If it is, disable the + * device and return a string so that it can be re-enabled + * at the completion of the uucico session as part of the + * function that resets the serial device before uucico + * terminates. + * + */ + +#include "uudefs.h" +#include "sysdep.h" + +#include +#include + +/* fscoherent_disable_tty() is a COHERENT specific function. It takes the name + * of a serial device and then scans /etc/ttys for a match. If it finds one, + * it checks the first field of the entry. If it is a '1', then it will disable + * the port and set a flag. The flag will be checked later when uucico wants to + * reset the serial device to see if the device needs to be re-enabled. + */ + +boolean +fscoherent_disable_tty (zdevice, pzenable) + const char *zdevice; + char **pzenable; +{ + + +struct ttyentry{ /* this is an /etc/ttys entry */ + char enable_disable[1]; + char remote_local[1]; + char baud_rate[1]; + char tty_device[16]; +}; + +struct ttyentry sought_tty; + +int x,y,z; /* dummy */ +FILE * infp; /* this will point to /etc/ttys */ +char disable_command[66]; /* this will be the disable command + * passed to the system. + */ +char enable_device[16]; /* this will hold our device name + * to enable. + */ + + *pzenable = NULL; + + strcpy(enable_device,""); /* initialize our strings */ + strcpy(sought_tty.tty_device,""); + + if( (infp = fopen("/etc/ttys","r")) == NULL){ + ulog(LOG_ERROR,"Error: check_disable_tty: failed to open /etc/ttys\n"); + return FALSE; + } + + while (NULL !=(fgets(&sought_tty, sizeof (sought_tty), infp ))){ + sought_tty.tty_device[strlen(sought_tty.tty_device) -1] = '\0'; + strcpy(enable_device,sought_tty.tty_device); + + /* we must strip away the suffix to the com port name or + * we will never find a match. For example, if we are passed + * /dev/com4l to call out with and the port is already enabled, + * 9/10 the port enabled will be com4r. After we strip away the + * suffix of the port found in /etc/ttys, then we can test + * if the base port name appears in the device name string + * passed to us. + */ + + for(z = strlen(sought_tty.tty_device) ; z > 0 ; z--){ + if(isdigit(sought_tty.tty_device[z])){ + break; + } + } + y = strlen(sought_tty.tty_device); + for(x = z+1 ; x <= y; x++){ + sought_tty.tty_device[x] = '\0'; + } + + +/* ulog(LOG_NORMAL,"found device {%s}\n",sought_tty.tty_device); */ + if(strstr(zdevice, sought_tty.tty_device)){ + if(sought_tty.enable_disable[0] == '1'){ + ulog(LOG_NORMAL, "coh_tty: Disabling device %s {%s}\n", + zdevice, sought_tty.tty_device); + sprintf(disable_command, "/etc/disable %s",enable_device); + { + pid_t ipid; + const char *azargs[3]; + int aidescs[3]; + + azargs[0] = "/etc/disable"; + azargs[1] = enable_device; + azargs[2] = NULL; + aidescs[0] = SPAWN_NULL; + aidescs[1] = SPAWN_NULL; + aidescs[2] = SPAWN_NULL; + ipid = ixsspawn (azargs, aidescs, TRUE, + FALSE, + (const char *) NULL, TRUE, + TRUE, + (const char *) NULL, + (const char *) NULL, + (const char *) NULL); + if (ipid < 0) + x = 1; + else + x = ixswait ((unsigned long) ipid, + (const char *) NULL); + } + *pzenable = zbufalc (sizeof "/dev/" + + strlen (enable_device)); + sprintf(*pzenable,"/dev/%s", enable_device); +/* ulog(LOG_NORMAL,"Enable string is {%s}",*pzenable); */ + return(x==0? TRUE : FALSE); /* disable either failed + or succeded */ + }else{ + return FALSE; /* device in tty entry not enabled */ + } + } + } + return FALSE; /* no ttys entry found */ +} + +/* The following is COHERENT 4.0 specific. It is used to test for any + * existing lockfiles on a port which would have been created by init + * when a user logs into a port. + */ + +#define LOCKSIG 9 /* Significant Chars of Lockable Resources. */ +#define LOKFLEN 64 /* Max Length of UUCP Lock File Name. */ + +#define LOCKPRE "LCK.." +#define PIDLEN 6 /* Maximum length of string representing a pid. */ + +#ifndef LOCKDIR +#define LOCKDIR SPOOLDIR +#endif + +/* There is a special version of DEVMASK for the PE multiport driver + * because of the peculiar way it uses the minor device number. For + * all other drivers, the lower 5 bits describe the physical port-- + * the upper 3 bits give attributes for the port. + */ + +#define PE_DRIVER 21 /* Major device number for the PE driver. */ +#define PE_DEVMASK 0x3f /* PE driver minor device mask. */ +#define DEVMASK 0x1f /* Minor device mask. */ + +/* + * Generates a resource name for locking, based on the major number + * and the lower 4 bits of the minor number of the tty device. + * + * Builds the name in buff as two "." separated decimal numbers. + * Returns NULL on failure, buff on success. + */ +static char * +gen_res_name(path, buff) +char *path; +char *buff; +{ + struct stat sbuf; + int status; + + if (0 != (status = stat(path, &sbuf))) { + /* Can't stat the file. */ + return (NULL); + } + + if (PE_DRIVER == major(sbuf.st_rdev)) { + sprintf(buff, "%d.%d", major(sbuf.st_rdev), + PE_DEVMASK & minor(sbuf.st_rdev)); + } else { + sprintf(buff, "%d.%d", major(sbuf.st_rdev), + DEVMASK & minor(sbuf.st_rdev)); + } + + return(buff); +} /* gen_res_name */ + +/* + * lockexist(resource) char *resource; + * + * Test for existance of a lock on the given resource. + * + * Returns: (1) Resource is locked. + * (0) Resource is not locked. + */ + +static boolean +lockexist(resource) +const char *resource; +{ + char lockfn[LOKFLEN]; + + if ( resource == NULL ) + return(0); + sprintf(lockfn, "%s/%s%.*s", LOCKDIR, LOCKPRE, LOCKSIG, resource); + + return (!access(lockfn, AEXISTS)); +} /* lockexist() */ + +/* + * lockttyexist(ttyname) char *ttyname; + * + * Test for existance of a lock on the given tty. + * + * Returns: (1) Resource is locked. + * (0) Resource is not locked. + */ +boolean +lockttyexist(ttyn) +const char *ttyn; +{ + char resource[LOKFLEN]; + char filename[LOKFLEN]; + + sprintf(filename, "/dev/%s", ttyn); + if (NULL == gen_res_name(filename, resource)){ + return(0); /* Non-existent tty can not be locked :-) */ + } + + return(lockexist(resource)); +} /* lockttyexist() */ + +#endif /* HAVE_COHERENT_LOCKFILES */ diff --git a/gnu/libexec/uucp/libunix/cusub.c b/gnu/libexec/uucp/libunix/cusub.c new file mode 100644 index 0000000000..4cee888d71 --- /dev/null +++ b/gnu/libexec/uucp/libunix/cusub.c @@ -0,0 +1,1163 @@ +/* cusub.c + System dependent routines for cu. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char cusub_rcsid[] = "$Id: cusub.c,v 1.1 1993/08/04 19:32:09 jtc Exp $"; +#endif + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" +#include "cu.h" +#include "conn.h" +#include "prot.h" + +#include + +/* Get definitions for EAGAIN, EWOULDBLOCK and ENODATA. */ +#ifndef EAGAIN +#ifndef EWOULDBLOCK +#define EAGAIN (-1) +#define EWOULDBLOCK (-1) +#else /* defined (EWOULDBLOCK) */ +#define EAGAIN EWOULDBLOCK +#endif /* defined (EWOULDBLOCK) */ +#else /* defined (EAGAIN) */ +#ifndef EWOULDBLOCK +#define EWOULDBLOCK EAGAIN +#endif /* ! defined (EWOULDBLOCK) */ +#endif /* defined (EAGAIN) */ + +#ifndef ENODATA +#define ENODATA EAGAIN +#endif + +/* Local variables. */ + +/* The EOF character, as set by fsysdep_terminal_raw. */ +static char bSeof; + +/* The SUSP character, as set by fsysdep_terminal_raw. */ +static char bStstp; + +/* Local functions. */ + +static const char *zsport_line P((const struct uuconf_port *qport)); +static void uscu_child P((struct sconnection *qconn, int opipe)); +static RETSIGTYPE uscu_alarm P((int isig)); +static int cscu_escape P((char *pbcmd, const char *zlocalname)); +static RETSIGTYPE uscu_alarm_kill P((int isig)); + +/* Return the device name for a port, or NULL if none. */ + +static const char * +zsport_line (qport) + const struct uuconf_port *qport; +{ + const char *zline; + + if (qport == NULL) + return NULL; + + switch (qport->uuconf_ttype) + { + default: + case UUCONF_PORTTYPE_STDIN: + return NULL; + case UUCONF_PORTTYPE_MODEM: + zline = qport->uuconf_u.uuconf_smodem.uuconf_zdevice; + break; + case UUCONF_PORTTYPE_DIRECT: + zline = qport->uuconf_u.uuconf_sdirect.uuconf_zdevice; + break; + case UUCONF_PORTTYPE_TCP: + case UUCONF_PORTTYPE_TLI: + return NULL; + } + + if (zline == NULL) + zline = qport->uuconf_zname; + return zline; +} + +/* Check whether the user has legitimate access to a port. */ + +boolean +fsysdep_port_access (qport) + struct uuconf_port *qport; +{ + const char *zline; + char *zfree; + boolean fret; + + zline = zsport_line (qport); + if (zline == NULL) + return TRUE; + + zfree = NULL; + if (*zline != '/') + { + zfree = zbufalc (sizeof "/dev/" + strlen (zline)); + sprintf (zfree, "/dev/%s", zline); + zline = zfree; + } + + fret = access (zline, R_OK | W_OK) == 0; + ubuffree (zfree); + return fret; +} + +/* Return whether the given port is named by the given line. */ + +boolean +fsysdep_port_is_line (qport, zline) + struct uuconf_port *qport; + const char *zline; +{ + const char *zpline; + char *zfree1, *zfree2; + boolean fret; + + zpline = zsport_line (qport); + if (zpline == NULL) + return FALSE; + + if (strcmp (zline, zpline) == 0) + return TRUE; + + zfree1 = NULL; + zfree2 = NULL; + if (*zline != '/') + { + zfree1 = zbufalc (sizeof "/dev/" + strlen (zline)); + sprintf (zfree1, "/dev/%s", zline); + zline = zfree1; + } + if (*zpline != '/') + { + zfree2 = zbufalc (sizeof "/dev/" + strlen (zpline)); + sprintf (zfree2, "/dev/%s", zpline); + zpline = zfree2; + } + + fret = strcmp (zline, zpline) == 0; + ubuffree (zfree1); + ubuffree (zfree2); + return fret; +} + +/* The cu program wants the system dependent layer to handle the + details of copying data from the communications port to the + terminal. This copying need only be done while executing + fsysdep_cu. On Unix, however, we set up a subprocess to do it all + the time. This subprocess must be controllable via the + fsysdep_cu_copy function. + + We keep a pipe open to the subprocess. When we want it to stop we + send it a signal, and then wait for it to write a byte to us over + the pipe. */ + +/* The subprocess pid. */ +static volatile pid_t iSchild; + +/* The pipe from the subprocess. */ +static int oSpipe; + +/* When we tell the child to stop, it sends this. */ +#define CHILD_STOPPED ('S') + +/* When we tell the child to start, it sends this. */ +#define CHILD_STARTED ('G') + +/* Initialize the subprocess, and have it start copying data. */ + +boolean +fsysdep_cu_init (qconn) + struct sconnection *qconn; +{ + int ai[2]; + + /* Write out anything we may have buffered up during the chat + script. We do this before forking the child only to make it easy + to move the child into a separate executable. */ + while (iPrecend != iPrecstart) + { + char *z; + int c; + + z = abPrecbuf + iPrecstart; + if (iPrecend > iPrecstart) + c = iPrecend - iPrecstart; + else + c = CRECBUFLEN - iPrecstart; + + iPrecstart = (iPrecstart + c) % CRECBUFLEN; + + while (c > 0) + { + int cwrote; + + cwrote = write (1, z, c); + if (cwrote <= 0) + { + if (cwrote < 0) + ulog (LOG_ERROR, "write: %s", strerror (errno)); + else + ulog (LOG_ERROR, "Line disconnected"); + return FALSE; + } + c -= cwrote; + z += cwrote; + } + } + + if (pipe (ai) < 0) + { + ulog (LOG_ERROR, "pipe: %s", strerror (errno)); + return FALSE; + } + + iSchild = ixsfork (); + if (iSchild < 0) + { + ulog (LOG_ERROR, "fork: %s", strerror (errno)); + return FALSE; + } + + if (iSchild == 0) + { + (void) close (ai[0]); + uscu_child (qconn, ai[1]); + /*NOTREACHED*/ + } + + (void) close (ai[1]); + + oSpipe = ai[0]; + + return TRUE; +} + +/* Copy all data from the terminal to the communications port. If we + see an escape character following a newline character, read the + next character and return it. */ + +boolean +fsysdep_cu (qconn, pbcmd, zlocalname) + struct sconnection *qconn; + char *pbcmd; + const char *zlocalname; +{ + boolean fstart; + char b; + int c; + + fstart = TRUE; + + while (TRUE) + { + if (fsysdep_catch ()) + usysdep_start_catch (); + else + { + ulog (LOG_ERROR, (const char *) NULL); + return FALSE; + } + + c = read (0, &b, 1); + + usysdep_end_catch (); + + if (c <= 0) + break; + + if (fstart && b == *zCuvar_escape) + { + c = cscu_escape (pbcmd, zlocalname); + if (c <= 0) + break; + if (*pbcmd != b) + { + write (1, pbcmd, 1); + + /* For Unix, we let the eof character be the same as + '.', and we let the suspend character (if any) be the + same as 'z'. */ + if (*pbcmd == bSeof) + *pbcmd = '.'; + if (*pbcmd == bStstp) + *pbcmd = 'z'; + return TRUE; + } + } + if (! fconn_write (qconn, &b, (size_t) 1)) + return FALSE; + fstart = strchr (zCuvar_eol, b) != NULL; + } + + if (c < 0) + { + if (errno != EINTR) + ulog (LOG_ERROR, "read: %s", strerror (errno)); + else + ulog (LOG_ERROR, (const char *) NULL); + return FALSE; + } + + /* I'm not sure what's best in this case. */ + ulog (LOG_ERROR, "End of file on terminal"); + return FALSE; +} + +/* A SIGALRM handler that sets fScu_alarm and optionally longjmps. */ + +volatile sig_atomic_t fScu_alarm; + +static RETSIGTYPE +uscu_alarm (isig) + int isig; +{ +#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET + (void) signal (isig, uscu_alarm); +#endif + + fScu_alarm = TRUE; + +#if HAVE_RESTARTABLE_SYSCALLS + if (fSjmp) + longjmp (sSjmp_buf, 1); +#endif +} + +/* We've just seen an escape character. We print the host name, + optionally after a 1 second delay. We read the next character from + the terminal and return it. The 1 second delay on the host name is + mostly to be fancy; it lets ~~ look smoother. */ + +static int +cscu_escape (pbcmd, zlocalname) + char *pbcmd; + const char *zlocalname; +{ + CATCH_PROTECT int c; + + write (1, zCuvar_escape, 1); + + fScu_alarm = FALSE; + usset_signal (SIGALRM, uscu_alarm, TRUE, (boolean *) NULL); + + if (fsysdep_catch ()) + { + usysdep_start_catch (); + alarm (1); + } + + c = 0; + + while (TRUE) + { + if (fScu_alarm) + { + char b; + + fScu_alarm = FALSE; + b = '['; + write (1, &b, 1); + write (1, zlocalname, strlen (zlocalname)); + b = ']'; + write (1, &b, 1); + } + + if (c <= 0) + c = read (0, pbcmd, 1); + if (c >= 0 || errno != EINTR) + { + usysdep_end_catch (); + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + alarm (0); + return c; + } + } +} + +/* A SIGALRM handler which does nothing but send a signal to the child + process and schedule another alarm. POSIX.1 permits kill and alarm + from a signal handler. The reference to static data may or may not + be permissible. */ + +static volatile sig_atomic_t iSsend_sig; + +static RETSIGTYPE +uscu_alarm_kill (isig) + int isig; +{ +#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET + (void) signal (isig, uscu_alarm_kill); +#endif + + (void) kill (iSchild, iSsend_sig); + + alarm (1); +} + +/* Start or stop copying data from the communications port to the + terminal. We send a signal to the child process to tell it what to + do. Unfortunately, there are race conditions in the child, so we + keep sending it a signal once a second until it responds. We send + SIGUSR1 to make it start copying, and SIGUSR2 to make it stop. */ + +boolean +fsysdep_cu_copy (fcopy) + boolean fcopy; +{ + int ierr; + int c; + + usset_signal (SIGALRM, uscu_alarm_kill, TRUE, (boolean *) NULL); + if (fcopy) + iSsend_sig = SIGUSR1; + else + iSsend_sig = SIGUSR2; + + uscu_alarm_kill (SIGALRM); + + alarm (1); + + while (TRUE) + { + char b; + + c = read (oSpipe, &b, 1); + +#if DEBUG > 1 + if (c > 0) + DEBUG_MESSAGE1 (DEBUG_INCOMING, + "fsysdep_cu_copy: Got '%d'", b); +#endif + + if ((c < 0 && errno != EINTR) + || c == 0 + || (c > 0 && b == (fcopy ? CHILD_STARTED : CHILD_STOPPED))) + break; + + /* If none of the above conditions were true, then we either got + an EINTR error, in which case we probably timed out and the + SIGALRM handler resent the signal, or we read the wrong + character, in which case we will just read again from the + pipe. */ + } + + ierr = errno; + + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + alarm (0); + + if (c > 0) + return TRUE; + + if (c == 0) + ulog (LOG_ERROR, "EOF on child pipe"); + else + ulog (LOG_ERROR, "read: %s", strerror (ierr)); + + return FALSE; +} + +/* Shut down cu by killing the child process. */ + +boolean +fsysdep_cu_finish () +{ + (void) close (oSpipe); + + /* We hit the child with SIGTERM, give it two seconds to die, and + then send a SIGKILL. */ + if (kill (iSchild, SIGTERM) < 0) + { + /* Don't give an error if the child has already died. */ + if (errno != ESRCH) + ulog (LOG_ERROR, "kill: %s", strerror (errno)); + } + + usset_signal (SIGALRM, uscu_alarm_kill, TRUE, (boolean *) NULL); + iSsend_sig = SIGKILL; + alarm (2); + + (void) ixswait ((unsigned long) iSchild, "child"); + + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + alarm (0); + + return TRUE; +} + +/* Code for the child process. */ + +/* This signal handler just records the signal. In this case we only + care about which signal we received most recently. */ + +static volatile sig_atomic_t iSchild_sig; + +static RETSIGTYPE +uscu_child_handler (isig) + int isig; +{ +#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET + (void) signal (isig, uscu_child_handler); +#endif + + iSchild_sig = isig; + +#if HAVE_RESTARTABLE_SYSCALLS + if (fSjmp) + longjmp (sSjmp_buf, 1); +#endif /* HAVE_RESTARTABLE_SYSCALLS */ +} + +/* The child process. This copies the port to the terminal, except + when it is stopped by a signal. It would be reasonable to write a + separate program for this, probably passing it the port on stdin. + This would reduce the memory requirements, since we wouldn't need a + second process holding all the configuration stuff, and also let it + work reasonably on 680x0 versions of MINIX. */ + +static void +uscu_child (qconn, opipe) + struct sconnection *qconn; + int opipe; +{ + CATCH_PROTECT int oport; + CATCH_PROTECT boolean fstopped, fgot; + CATCH_PROTECT int cwrite; + CATCH_PROTECT char abbuf[1024]; + + /* It would be nice if we could just use fsserial_read, but that + will log signals that we don't want logged. There should be a + generic way to extract the file descriptor from the port. */ + if (qconn->qport == NULL) + oport = 0; + else + { + switch (qconn->qport->uuconf_ttype) + { +#if DEBUG > 0 + default: + ulog (LOG_FATAL, "uscu_child: Can't happen"); + oport = -1; + break; +#endif + case UUCONF_PORTTYPE_STDIN: + oport = 0; + break; + case UUCONF_PORTTYPE_MODEM: + case UUCONF_PORTTYPE_DIRECT: + case UUCONF_PORTTYPE_TCP: + case UUCONF_PORTTYPE_TLI: + oport = ((struct ssysdep_conn *) qconn->psysdep)->o; + break; + } + } + + usset_signal (SIGUSR1, uscu_child_handler, TRUE, (boolean *) NULL); + usset_signal (SIGUSR2, uscu_child_handler, TRUE, (boolean *) NULL); + usset_signal (SIGINT, SIG_IGN, TRUE, (boolean *) NULL); + usset_signal (SIGQUIT, SIG_IGN, TRUE, (boolean *) NULL); + usset_signal (SIGPIPE, SIG_DFL, TRUE, (boolean *) NULL); + usset_signal (SIGTERM, uscu_child_handler, TRUE, (boolean *) NULL); + + fstopped = FALSE; + fgot = FALSE; + iSchild_sig = 0; + cwrite = 0; + + if (fsysdep_catch ()) + usysdep_start_catch (); + + while (TRUE) + { + int isig; + int c; + + /* There is a race condition here between checking the signal + and receiving a new and possibly different one. This is + solved by having the parent resend the signal until it gets a + response. */ + isig = iSchild_sig; + iSchild_sig = 0; + if (isig != 0) + { + char b; + + if (isig == SIGTERM) + exit (EXIT_SUCCESS); + + if (isig == SIGUSR1) + { + fstopped = FALSE; + b = CHILD_STARTED; + } + else + { + fstopped = TRUE; + b = CHILD_STOPPED; + cwrite = 0; + } + + c = write (opipe, &b, 1); + + /* Apparently on some systems we can get EAGAIN here. */ + if (c < 0 && + (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA)) + c = 0; + + if (c <= 0) + { + /* Should we give an error message here? */ + (void) kill (getppid (), SIGHUP); + exit (EXIT_FAILURE); + } + } + + if (fstopped) + pause (); + else if (cwrite > 0) + { + char *zbuf; + + zbuf = abbuf; + while (cwrite > 0) + { + c = write (1, zbuf, cwrite); + + /* Apparently on some systems we can get EAGAIN here. */ + if (c < 0 && + (errno == EAGAIN + || errno == EWOULDBLOCK + || errno == ENODATA)) + c = 0; + + if (c < 0 && errno == EINTR) + break; + if (c <= 0) + { + /* Should we give an error message here? */ + (void) kill (getppid (), SIGHUP); + exit (EXIT_FAILURE); + } + cwrite -= c; + zbuf += c; + } + } + else + { + /* On some systems apparently read will return 0 until + something has been written to the port. We therefore + accept a 0 return until after we have managed to read + something. Setting errno to 0 apparently avoids a + problem on Coherent. */ + errno = 0; + c = read (oport, abbuf, sizeof abbuf); + + /* Apparently on some systems we can get EAGAIN here. */ + if (c < 0 && + (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA)) + c = 0; + + if ((c == 0 && fgot) + || (c < 0 && errno != EINTR)) + { + /* This can be a normal way to exit, depending on just + how the connection is dropped. */ + (void) kill (getppid (), SIGHUP); + exit (EXIT_SUCCESS); + } + if (c > 0) + { + fgot = TRUE; + cwrite = c; + } + } + } +} + +/* Terminal control routines. */ + +/* Whether file descriptor 0 is attached to a terminal or not. */ +static boolean fSterm; + +/* Whether we are doing local echoing. */ +static boolean fSlocalecho; + +/* The original state of the terminal. */ +static sterminal sSterm_orig; + +/* The new state of the terminal. */ +static sterminal sSterm_new; + +#if ! HAVE_BSD_TTY +#ifdef SIGTSTP +/* Whether SIGTSTP is being ignored. */ +static boolean fStstp_ignored; +#endif +#endif + +/* Set the terminal into raw mode. */ + +boolean +fsysdep_terminal_raw (flocalecho) + boolean flocalecho; +{ + fSlocalecho = flocalecho; + + /* This defaults may be overriden below. */ + bSeof = '\004'; + bStstp = '\032'; + + if (! fgetterminfo (0, &sSterm_orig)) + { + fSterm = FALSE; + return TRUE; + } + + fSterm = TRUE; + + sSterm_new = sSterm_orig; + +#if HAVE_BSD_TTY + + /* We use CBREAK mode rather than RAW mode, because RAW mode turns + off all output processing, which we don't want to do. This means + that we have to disable the interrupt characters, which we do by + setting them to -1. */ + bSeof = sSterm_orig.stchars.t_eofc; + + sSterm_new.stchars.t_intrc = -1; + sSterm_new.stchars.t_quitc = -1; + sSterm_new.stchars.t_startc = -1; + sSterm_new.stchars.t_stopc = -1; + sSterm_new.stchars.t_eofc = -1; + sSterm_new.stchars.t_brkc = -1; + + bStstp = sSterm_orig.sltchars.t_suspc; + + sSterm_new.sltchars.t_suspc = -1; + sSterm_new.sltchars.t_dsuspc = -1; + sSterm_new.sltchars.t_rprntc = -1; + sSterm_new.sltchars.t_flushc = -1; + sSterm_new.sltchars.t_werasc = -1; + sSterm_new.sltchars.t_lnextc = -1; + + if (! flocalecho) + { + sSterm_new.stty.sg_flags |= (CBREAK | ANYP); + sSterm_new.stty.sg_flags &=~ (ECHO | CRMOD | TANDEM); + } + else + { + sSterm_new.stty.sg_flags |= (CBREAK | ANYP | ECHO); + sSterm_new.stty.sg_flags &=~ (CRMOD | TANDEM); + } + +#endif /* HAVE_BSD_TTY */ + +#if HAVE_SYSV_TERMIO + + bSeof = sSterm_new.c_cc[VEOF]; + if (! flocalecho) + sSterm_new.c_lflag &=~ (ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHONL); + else + sSterm_new.c_lflag &=~ (ICANON | ISIG); + sSterm_new.c_iflag &=~ (INLCR | IGNCR | ICRNL); + sSterm_new.c_oflag &=~ (OPOST); + sSterm_new.c_cc[VMIN] = 1; + sSterm_new.c_cc[VTIME] = 0; + +#endif /* HAVE_SYSV_TERMIO */ + +#if HAVE_POSIX_TERMIOS + + bSeof = sSterm_new.c_cc[VEOF]; + bStstp = sSterm_new.c_cc[VSUSP]; + if (! flocalecho) + sSterm_new.c_lflag &=~ + (ICANON | IEXTEN | ISIG | ECHO | ECHOE | ECHOK | ECHONL); + else + sSterm_new.c_lflag &=~ (ICANON | IEXTEN | ISIG); + sSterm_new.c_iflag &=~ (INLCR | IGNCR | ICRNL); + sSterm_new.c_oflag &=~ (OPOST); + sSterm_new.c_cc[VMIN] = 1; + sSterm_new.c_cc[VTIME] = 0; + +#endif /* HAVE_POSIX_TERMIOS */ + + if (! fsetterminfo (0, &sSterm_new)) + { + ulog (LOG_ERROR, "Can't set terminal settings: %s", strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/* Restore the terminal to its original setting. */ + +boolean +fsysdep_terminal_restore () +{ + if (! fSterm) + return TRUE; + + if (! fsetterminfo (0, &sSterm_orig)) + { + ulog (LOG_ERROR, "Can't restore terminal: %s", strerror (errno)); + return FALSE; + } + return TRUE; +} + +/* Read a line from the terminal. This will be called after + fsysdep_terminal_raw has been called. */ + +char * +zsysdep_terminal_line (zprompt) + const char *zprompt; +{ + CATCH_PROTECT size_t cbuf = 0; + CATCH_PROTECT char *zbuf = NULL; + CATCH_PROTECT size_t cgot = 0; + + if (zprompt != NULL && *zprompt != '\0') + (void) write (1, zprompt, strlen (zprompt)); + + /* Forgot about any previous SIGINT or SIGQUIT signals we may have + received. We don't worry about the race condition here, since we + can't get these signals from the terminal at the moment and it's + not too likely that somebody else will be sending them to us. */ + afSignal[INDEXSIG_SIGINT] = 0; + afSignal[INDEXSIG_SIGQUIT] = 0; + + if (! fsysdep_terminal_restore ()) + return NULL; + + if (fsysdep_catch ()) + { + usysdep_start_catch (); + cbuf = 0; + zbuf = NULL; + cgot = 0; + } + + while (TRUE) + { + char b; + int c; + + if (afSignal[INDEXSIG_SIGINT] + || afSignal[INDEXSIG_SIGQUIT]) + { + usysdep_end_catch (); + /* Make sure the signal is logged. */ + ulog (LOG_ERROR, (const char *) NULL); + /* Return an empty string. */ + cgot = 0; + break; + } + + /* There's a race here between checking the signals and calling + read. It just means that the user will have to hit ^C more + than once. */ + + c = read (0, &b, 1); + if (c < 0) + { + if (errno == EINTR) + continue; + usysdep_end_catch (); + ulog (LOG_ERROR, "read: %s", strerror (errno)); + (void) fsysdep_terminal_raw (fSlocalecho); + return NULL; + } + if (c == 0) + { + /* I'm not quite sure what to do here. */ + usysdep_end_catch (); + ulog (LOG_ERROR, "EOF on terminal"); + (void) fsysdep_terminal_raw (fSlocalecho); + return NULL; + } + + if (cgot >= cbuf) + { + char *znew; + + cbuf += 64; + znew = zbufalc (cbuf); + if (zbuf != NULL) + { + memcpy (znew, zbuf, cgot); + ubuffree (zbuf); + } + zbuf = znew; + } + + zbuf[cgot] = b; + + ++cgot; + + if (b == '\n') + { + usysdep_end_catch (); + break; + } + } + + if (cgot >= cbuf) + { + char *znew; + + ++cbuf; + znew = zbufalc (cbuf); + if (zbuf != NULL) + { + memcpy (znew, zbuf, cgot); + ubuffree (zbuf); + } + zbuf = znew; + } + + zbuf[cgot] = '\0'; + + if (! fsysdep_terminal_raw (fSlocalecho)) + return NULL; + + return zbuf; +} + +/* Write a line to the terminal with a trailing newline. */ + +boolean +fsysdep_terminal_puts (zline) + const char *zline; +{ + char *zalc, *zprint; + size_t clen; + + if (zline == NULL) + { + zalc = zbufalc (2); + clen = 0; + } + else + { + clen = strlen (zline); + zalc = zbufalc (clen + 2); + memcpy (zalc, zline, clen); + } + + if (fSterm) + { + zalc[clen] = '\r'; + ++clen; + } + zalc[clen] = '\n'; + ++clen; + + zprint = zalc; + while (clen > 0) + { + int c; + + c = write (1, zprint, clen); + if (c <= 0) + { + ubuffree (zalc); + ulog (LOG_ERROR, "write: %s", strerror (errno)); + return FALSE; + } + clen -= c; + zprint += c; + } + + ubuffree (zalc); + + return TRUE; +} + +/* Allow or disallow signals from the terminal. */ + +boolean +fsysdep_terminal_signals (faccept) + boolean faccept; +{ +#if HAVE_BSD_TTY + + if (faccept) + { + sSterm_new.stchars.t_intrc = sSterm_orig.stchars.t_intrc; + sSterm_new.stchars.t_quitc = sSterm_orig.stchars.t_quitc; + } + else + { + sSterm_new.stchars.t_intrc = -1; + sSterm_new.stchars.t_quitc = -1; + } + +#else /* ! HAVE_BSD_TTY */ + + if (faccept) + sSterm_new.c_lflag |= ISIG; + else + sSterm_new.c_lflag &=~ ISIG; + +#ifdef SIGTSTP + /* We only want to get SIGINT and SIGQUIT, not SIGTSTP. This + function will be called with faccept TRUE before it is called + with faccept FALSE, so fStstp_ignored will be correctly + initialized. */ + if (faccept) + usset_signal (SIGTSTP, SIG_IGN, FALSE, &fStstp_ignored); + else if (! fStstp_ignored) + usset_signal (SIGTSTP, SIG_DFL, TRUE, (boolean *) NULL); +#endif + +#endif /* ! HAVE_BSD_TTY */ + + if (! fsetterminfo (0, &sSterm_new)) + { + ulog (LOG_ERROR, "Can't set terminal: %s", strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/* Start up a command, or possibly just a shell. Optionally attach + stdin or stdout to the port. We attach directly to the port, + rather than copying the data ourselves. */ + +boolean +fsysdep_shell (qconn, zcmd, tcmd) + struct sconnection *qconn; + const char *zcmd; + enum tshell_cmd tcmd; +{ + const char *azargs[4]; + int oread, owrite; + int aidescs[3]; + pid_t ipid; + + azargs[0] = "/bin/sh"; + if (zcmd == NULL || *zcmd == '\0') + azargs[1] = NULL; + else + { + azargs[1] = "-c"; + azargs[2] = zcmd; + azargs[3] = NULL; + } + + if (qconn->qport == NULL) + { + oread = 0; + owrite = 1; + } + else + { + switch (qconn->qport->uuconf_ttype) + { + default: + oread = owrite = -1; + break; + case UUCONF_PORTTYPE_STDIN: + oread = 0; + owrite = 1; + break; + case UUCONF_PORTTYPE_MODEM: + case UUCONF_PORTTYPE_DIRECT: + case UUCONF_PORTTYPE_TCP: + case UUCONF_PORTTYPE_TLI: + oread = owrite = ((struct ssysdep_conn *) qconn->psysdep)->o; + break; + } + } + + aidescs[0] = 0; + aidescs[1] = 1; + aidescs[2] = 2; + + if (tcmd == SHELL_STDIN_FROM_PORT || tcmd == SHELL_STDIO_ON_PORT) + aidescs[0] = oread; + if (tcmd == SHELL_STDOUT_TO_PORT || tcmd == SHELL_STDIO_ON_PORT) + aidescs[1] = owrite; + + ipid = ixsspawn (azargs, aidescs, FALSE, TRUE, (const char *) NULL, + FALSE, FALSE, (const char *) NULL, + (const char *) NULL, (const char *) NULL); + if (ipid < 0) + { + ulog (LOG_ERROR, "ixsspawn (/bin/sh): %s", strerror (errno)); + return FALSE; + } + + return ixswait ((unsigned long) ipid, "shell") == 0; +} + +/* Change directories. */ + +boolean +fsysdep_chdir (zdir) + const char *zdir; +{ + if (zdir == NULL || *zdir == '\0') + { + zdir = getenv ("HOME"); + if (zdir == NULL) + { + ulog (LOG_ERROR, "HOME not defined"); + return FALSE; + } + } + if (chdir (zdir) < 0) + { + ulog (LOG_ERROR, "chdir (%s): %s", zdir, strerror (errno)); + return FALSE; + } + return TRUE; +} + +/* Suspend the current process. */ + +boolean +fsysdep_suspend () +{ +#ifndef SIGTSTP + return fsysdep_terminal_puts ("[process suspension not supported]"); +#else + return kill (getpid (), SIGTSTP) == 0; +#endif +} diff --git a/gnu/libexec/uucp/libunix/cwd.c b/gnu/libexec/uucp/libunix/cwd.c new file mode 100644 index 0000000000..433025db6c --- /dev/null +++ b/gnu/libexec/uucp/libunix/cwd.c @@ -0,0 +1,55 @@ +/* cwd.c + Routines dealing with the current working directory. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +/* See whether running this file through zsysdep_add_cwd would require + knowing the current working directory. This is used to avoid + determining the cwd if it will not be needed. */ + +boolean +fsysdep_needs_cwd (zfile) + const char *zfile; +{ + return *zfile != '/' && *zfile != '~'; +} + +/* Expand a local file, putting relative pathnames in the current + working directory. Note that ~/file is placed in the public + directory, rather than in the user's home directory. This is + consistent with other UUCP packages. */ + +char * +zsysdep_local_file_cwd (zfile, zpubdir) + const char *zfile; + const char *zpubdir; +{ + if (*zfile == '/') + return zbufcpy (zfile); + else if (*zfile == '~') + return zsysdep_local_file (zfile, zpubdir); + else + return zsysdep_add_cwd (zfile); +} + +/* Add the current working directory to a remote file name. */ + +char * +zsysdep_add_cwd (zfile) + const char *zfile; +{ + if (*zfile == '/' || *zfile == '~') + return zbufcpy (zfile); + + if (zScwd == NULL) + { + ulog (LOG_ERROR, "Can't determine current directory"); + return NULL; + } + + return zsysdep_in_dir (zScwd, zfile); +} diff --git a/gnu/libexec/uucp/libunix/detach.c b/gnu/libexec/uucp/libunix/detach.c new file mode 100644 index 0000000000..41e1969b5e --- /dev/null +++ b/gnu/libexec/uucp/libunix/detach.c @@ -0,0 +1,186 @@ +/* detach.c + Detach from the controlling terminal. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "system.h" +#include "sysdep.h" + +#include + +#if HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef TIOCNOTTY +#define HAVE_TIOCNOTTY 1 +#else +#define HAVE_TIOCNOTTY 0 +#endif + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +/* Detach from the controlling terminal. This is called by uucico if + it is calling out to another system, so that it can receive SIGHUP + signals from the port it calls out on. It is also called by uucico + just before it starts uuxqt, so that uuxqt is completely + independent of the terminal. */ + +void +usysdep_detach () +{ +#if ! HAVE_BSD_PGRP || ! HAVE_TIOCNOTTY + + pid_t igrp; + + /* First make sure we are not a process group leader. If we have + TIOCNOTTY, this doesn't matter, since TIOCNOTTY sets our process + group to 0 anyhow. */ + +#if HAVE_BSD_PGRP + igrp = getpgrp (0); +#else + igrp = getpgrp (); +#endif + + if (igrp == getpid ()) + { + boolean fignored; + pid_t ipid; + + /* Ignore SIGHUP, since our process group leader is about to + die. */ + usset_signal (SIGHUP, SIG_IGN, FALSE, &fignored); + + ipid = ixsfork (); + if (ipid < 0) + ulog (LOG_FATAL, "fork: %s", strerror (errno)); + + if (ipid != 0) + _exit (EXIT_SUCCESS); + + /* We'll always wind up as a child of process number 1, right? + Right? We have to wait for our parent to die before + reenabling SIGHUP. */ + while (getppid () != 1) + sleep (1); + + ulog_id (getpid ()); + + /* Restore SIGHUP catcher if it wasn't being ignored. */ + if (! fignored) + usset_signal (SIGHUP, ussignal, TRUE, (boolean *) NULL); + } + +#endif /* ! HAVE_BSD_PGRP || ! HAVE_TIOCNOTTY */ + +#if HAVE_TIOCNOTTY + /* Lose the original controlling terminal. If standard input has + been reopened to /dev/null, this will do no harm. If another + port has been opened to become the controlling terminal, it + should have been detached when it was closed. */ + (void) ioctl (0, TIOCNOTTY, (char *) NULL); +#endif + + /* Close stdin, stdout and stderr and reopen them on /dev/null, to + make sure we have no connection at all to the terminal. */ + (void) close (0); + (void) close (1); + (void) close (2); + if (open ((char *) "/dev/null", O_RDONLY) != 0 + || open ((char *) "/dev/null", O_WRONLY) != 1 + || open ((char *) "/dev/null", O_WRONLY) != 2) + ulog (LOG_FATAL, "open (/dev/null): %s", strerror (errno)); + +#if HAVE_BSD_PGRP + + /* Make sure our process group ID is set to 0. On BSD TIOCNOTTY + should already have set it 0, so this will do no harm. On System + V we presumably did not execute the TIOCNOTTY call, but the + System V setpgrp will detach the controlling terminal anyhow. + This lets us use the same code on both BSD and System V, provided + it compiles correctly, which life easier for the configure + script. We don't output an error if we got EPERM because some + BSD variants don't permit this usage of setpgrp (which means they + don't provide any way to pick up a new controlling terminal). */ + + if (setpgrp (0, 0) < 0) + { + if (errno != EPERM) + ulog (LOG_ERROR, "setpgrp: %s", strerror (errno)); + } + +#else /* ! HAVE_BSD_PGRP */ + +#if HAVE_SETSID + + /* Under POSIX the setsid call creates a new session for which we + are the process group leader. It also detaches us from our + controlling terminal. I'm using the BSD setpgrp call first + because they should be equivalent for my purposes, but it turns + out that on Ultrix 4.0 setsid prevents us from ever acquiring + another controlling terminal (it does not change our process + group, and Ultrix 4.0 prevents us from setting our process group + to 0). */ + (void) setsid (); + +#else /* ! HAVE_SETSID */ + +#if HAVE_SETPGRP + + /* Now we assume we have the System V setpgrp, which takes no + arguments, and we couldn't compile the HAVE_BSD_PGRP code above + because there was a prototype somewhere in scope. On System V + setpgrp makes us the leader of a new process group and also + detaches the controlling terminal. */ + + if (setpgrp () < 0) + ulog (LOG_ERROR, "setpgrp: %s", strerror (errno)); + +#else /* ! HAVE_SETPGRP */ + + #error Must detach from controlling terminal + +#endif /* HAVE_SETPGRP */ +#endif /* ! HAVE_SETSID */ +#endif /* ! HAVE_BSD_PGRP */ + + /* At this point we have completely detached from our controlling + terminal. The next terminal device we open will probably become + our controlling terminal. */ +} diff --git a/gnu/libexec/uucp/libunix/dirent.c b/gnu/libexec/uucp/libunix/dirent.c new file mode 100644 index 0000000000..83db496cab --- /dev/null +++ b/gnu/libexec/uucp/libunix/dirent.c @@ -0,0 +1,123 @@ +/* dirent.c + Replacements for opendir, readdir and closedir for the original + Unix filesystem only. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "sysdep.h" + +#include + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +/* Simple emulations of opendir/readdir/closedir for systems which + have the original format of Unix directories. It's probably better + to get Doug Gwyn's public domain set of emulation functions. */ + +DIR * +opendir (zdir) + const char *zdir; +{ + int o; + struct stat s; + DIR *qret; + + o = open ((char *) zdir, O_RDONLY | O_NOCTTY, 0); + if (o < 0) + return NULL; + if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0 + || fstat (o, &s) < 0) + { + int isave; + + isave = errno; + (void) close (o); + errno = isave; + return NULL; + } + if (! S_ISDIR (s.st_mode)) + { + (void) close (o); + errno = ENOTDIR; + return NULL; + } + qret = (DIR *) xmalloc (sizeof (DIR)); + qret->o = o; + return qret; +} + +struct dirent * +readdir (q) + DIR *q; +{ + struct direct sdir; + int cgot; + + do + { + cgot = read (q->o, &sdir, sizeof (struct direct)); + if (cgot <= 0) + return NULL; + if (cgot != sizeof (struct direct)) + { + errno = ENOENT; + return NULL; + } + } + while (sdir.d_ino == 0); + + strncpy (q->s.d_name, sdir.d_name, DIRSIZ); + q->s.d_name[DIRSIZ] = '\0'; + return &q->s; +} + +int +closedir (q) + DIR *q; +{ + int o; + + o = q->o; + xfree (q); + return close (o); +} diff --git a/gnu/libexec/uucp/libunix/dup2.c b/gnu/libexec/uucp/libunix/dup2.c new file mode 100644 index 0000000000..6a7359fe92 --- /dev/null +++ b/gnu/libexec/uucp/libunix/dup2.c @@ -0,0 +1,69 @@ +/* dup2.c + The Unix dup2 function, for systems which only have dup. + + Copyright (C) 1985, 1986, 1987, 1988, 1990 Free Software Foundation, Inc. + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" +#include "sysdep.h" + +#include + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +/* I basically took this from the emacs 18.57 distribution, although I + cleaned it up a bit and made it POSIX compliant. */ + +int +dup2 (oold, onew) + int oold; + int onew; +{ + if (oold == onew) + return onew; + (void) close (onew); + +#ifdef F_DUPFD + return fcntl (oold, F_DUPFD, onew); +#else + { + int onext, oret, isave; + + onext = dup (oold); + if (onext == onew) + return onext; + if (onext < 0) + return -1; + oret = dup2 (oold, onew); + isave = errno; + (void) close (onext); + errno = isave; + return oret; + } +#endif +} diff --git a/gnu/libexec/uucp/libunix/efopen.c b/gnu/libexec/uucp/libunix/efopen.c new file mode 100644 index 0000000000..7e360b6168 --- /dev/null +++ b/gnu/libexec/uucp/libunix/efopen.c @@ -0,0 +1,132 @@ +/* efopen.c + Open a stdio file with appropriate permissions. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef O_APPEND +#ifdef FAPPEND +#define O_APPEND FAPPEND +#endif +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +FILE * +esysdep_fopen (zfile, fpublic, fappend, fmkdirs) + const char *zfile; + boolean fpublic; + boolean fappend; + boolean fmkdirs; +{ + int imode; + int o; + FILE *e; + + if (fpublic) + imode = IPUBLIC_FILE_MODE; + else + imode = IPRIVATE_FILE_MODE; + + if (! fappend) + o = creat ((char *) zfile, imode); + else + { +#ifdef O_CREAT + o = open ((char *) zfile, + O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, + imode); +#else + o = open ((char *) zfile, O_WRONLY | O_NOCTTY); + if (o < 0 && errno == ENOENT) + o = creat ((char *) zfile, imode); +#endif /* ! defined (O_CREAT) */ + } + + if (o < 0) + { + if (errno == ENOENT && fmkdirs) + { + if (! fsysdep_make_dirs (zfile, fpublic)) + return NULL; + if (! fappend) + o = creat ((char *) zfile, imode); + else + { +#ifdef O_CREAT + o = open ((char *) zfile, + O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, + imode); +#else + o = creat ((char *) zfile, imode); +#endif + } + } + if (o < 0) + { + ulog (LOG_ERROR, "open (%s): %s", zfile, strerror (errno)); + return NULL; + } + } + +#ifndef O_CREAT +#ifdef O_APPEND + if (fappend) + { + if (fcntl (o, F_SETFL, O_APPEND) < 0) + { + ulog (LOG_ERROR, "fcntl (%s, O_APPEND): %s", zfile, + strerror (errno)); + (void) close (o); + return NULL; + } + } +#endif /* defined (O_APPEND) */ +#endif /* ! defined (O_CREAT) */ + + if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (%s, FD_CLOEXEC): %s", zfile, + strerror (errno)); + (void) close (o); + return NULL; + } + + if (fappend) + e = fdopen (o, (char *) "a"); + else + e = fdopen (o, (char *) "w"); + + if (e == NULL) + { + ulog (LOG_ERROR, "fdopen: %s", strerror (errno)); + (void) close (o); + } + + return e; +} diff --git a/gnu/libexec/uucp/libunix/epopen.c b/gnu/libexec/uucp/libunix/epopen.c new file mode 100644 index 0000000000..dec1b3999d --- /dev/null +++ b/gnu/libexec/uucp/libunix/epopen.c @@ -0,0 +1,85 @@ +/* epopen.c + A version of popen that goes through ixsspawn. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "sysdep.h" + +#include + +/* A version of popen that goes through ixsspawn. This actually takes + an array of arguments rather than a string, and takes a boolean + read/write value rather than a string. It sets *pipid to the + process ID of the child. */ + +FILE * +espopen (pazargs, frd, pipid) + const char **pazargs; + boolean frd; + pid_t *pipid; +{ + int aidescs[3]; + pid_t ipid; + FILE *eret; + + if (frd) + { + aidescs[0] = SPAWN_NULL; + aidescs[1] = SPAWN_READ_PIPE; + } + else + { + aidescs[0] = SPAWN_WRITE_PIPE; + aidescs[1] = SPAWN_NULL; + } + aidescs[2] = SPAWN_NULL; + + ipid = ixsspawn (pazargs, aidescs, FALSE, FALSE, + (const char *) NULL, FALSE, TRUE, + (const char *) NULL, (const char *) NULL, + (const char *) NULL); + if (ipid < 0) + return NULL; + + if (frd) + eret = fdopen (aidescs[1], (char *) "r"); + else + eret = fdopen (aidescs[0], (char *) "w"); + if (eret == NULL) + { + int ierr; + + ierr = errno; + (void) close (frd ? aidescs[1] : aidescs[0]); + (void) kill (ipid, SIGKILL); + (void) ixswait ((unsigned long) ipid, (const char *) NULL); + errno = ierr; + return NULL; + } + + *pipid = ipid; + + return eret; +} diff --git a/gnu/libexec/uucp/libunix/exists.c b/gnu/libexec/uucp/libunix/exists.c new file mode 100644 index 0000000000..9473922f0d --- /dev/null +++ b/gnu/libexec/uucp/libunix/exists.c @@ -0,0 +1,16 @@ +/* exists.c + Check whether a file exists. */ + +#include "uucp.h" + +#include "sysdep.h" +#include "system.h" + +boolean +fsysdep_file_exists (zfile) + const char *zfile; +{ + struct stat s; + + return stat ((char *) zfile, &s) == 0; +} diff --git a/gnu/libexec/uucp/libunix/filnam.c b/gnu/libexec/uucp/libunix/filnam.c new file mode 100644 index 0000000000..62054767b8 --- /dev/null +++ b/gnu/libexec/uucp/libunix/filnam.c @@ -0,0 +1,376 @@ +/* filnam.c + Get names to use for UUCP files. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" + +#include + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +/* We need a definition for SEEK_SET. */ + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +/* External functions. */ +#ifndef lseek +extern off_t lseek (); +#endif + +#define ZCHARS \ + "0123456789ABCDEFGHIJKLMNOPQRTSUVWXYZabcdefghijklmnopqrstuvwxyz" + +/* Local functions. */ + +static boolean fscmd_seq P((const char *zsystem, char *zseq)); +static char *zsfile_name P((int btype, const char *zsystem, + const char *zlocalname, int bgrade, + boolean fxqt, char *ztname, char *zdname, + char *zxname)); + +/* Get a new command sequence number (this is not a sequence number to + be used for communicating with another system, but a sequence + number to be used when generating the name of a command file). + The sequence number is placed into zseq, which should be five + characters long. */ + +static boolean +fscmd_seq (zsystem, zseq) + const char *zsystem; + char *zseq; +{ + boolean ferr; + char *zfree; + const char *zfile; + int o; + int i; + + /* Lock the sequence file. This may not be correct for all systems, + but it only matters if the system UUCP and this UUCP are running + at the same time. */ + while (! fsdo_lock ("LCK..SEQ", TRUE, &ferr)) + { + if (ferr || FGOT_SIGNAL ()) + return FALSE; + sleep (5); + } + + zfree = NULL; + +#if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 + zfile = "SEQF"; +#endif +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 + zfree = zsysdep_in_dir (".Sequence", zsystem); + zfile = zfree; +#endif +#if SPOOLDIR_ULTRIX + if (! fsultrix_has_spool (zsystem)) + zfile = "sys/DEFAULT/.SEQF"; + else + { + zfree = zsappend3 ("sys", zsystem, ".SEQF"); + zfile = zfree; + } +#endif /* SPOOLDIR_ULTRIX */ +#if SPOOLDIR_TAYLOR + zfree = zsysdep_in_dir (zsystem, "SEQF"); + zfile = zfree; +#endif /* SPOOLDIR_TAYLOR */ + +#ifdef O_CREAT + o = open ((char *) zfile, O_RDWR | O_CREAT | O_NOCTTY, IPUBLIC_FILE_MODE); +#else + o = open ((char *) zfile, O_RDWR | O_NOCTTY); + if (o < 0 && errno == ENOENT) + { + o = creat ((char *) zfile, IPUBLIC_FILE_MODE); + if (o >= 0) + { + (void) close (o); + o = open ((char *) zfile, O_RDWR | O_NOCTTY); + } + } +#endif + + if (o < 0) + { + if (errno == ENOENT) + { + if (! fsysdep_make_dirs (zfile, FALSE)) + { + (void) fsdo_unlock ("LCK..SEQ", TRUE); + return FALSE; + } +#ifdef O_CREAT + o = open ((char *) zfile, + O_RDWR | O_CREAT | O_NOCTTY, + IPUBLIC_FILE_MODE); +#else + o = creat ((char *) zfile, IPUBLIC_FILE_MODE); + if (o >= 0) + { + (void) close (o); + o = open ((char *) zfile, O_RDWR | O_NOCTTY); + } +#endif + } + if (o < 0) + { + ulog (LOG_ERROR, "open (%s): %s", zfile, strerror (errno)); + (void) fsdo_unlock ("LCK..SEQ", TRUE); + return FALSE; + } + } + + if (read (o, zseq, CSEQLEN) != CSEQLEN) + strcpy (zseq, "0000"); + zseq[CSEQLEN] = '\0'; + + /* We must add one to the sequence number and return the new value. + On Ultrix, arbitrary characters are allowed in the sequence + number. On other systems, the sequence number apparently must be + in hex. */ +#if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 || SPOOLDIR_HDB || SPOOLDIR_SVR4 + i = (int) strtol (zseq, (char **) NULL, 16); + ++i; + if (i > 0xffff) + i = 0; + /* The sprintf argument has CSEQLEN built into it. */ + sprintf (zseq, "%04x", (unsigned int) i); +#endif +#if SPOOLDIR_ULTRIX || SPOOLDIR_TAYLOR + for (i = CSEQLEN - 1; i >= 0; i--) + { + const char *zdig; + + zdig = strchr (ZCHARS, zseq[i]); + if (zdig == NULL || zdig[0] == '\0' || zdig[1] == '\0') + zseq[i] = '0'; + else + { + zseq[i] = zdig[1]; + break; + } + } +#endif /* SPOOLDIR_ULTRIX || SPOOLDIR_TAYLOR */ + + if (lseek (o, (off_t) 0, SEEK_SET) < 0 + || write (o, zseq, CSEQLEN) != CSEQLEN + || close (o) < 0) + { + ulog (LOG_ERROR, "lseek or write or close: %s", strerror (errno)); + (void) close (o); + (void) fsdo_unlock ("LCK..SEQ", TRUE); + return FALSE; + } + + (void) fsdo_unlock ("LCK..SEQ", TRUE); + + return TRUE; +} + +/* Get the name of a command or data file for a remote system. The + btype argument should be C for a command file or D for a data file. + If the grade of a data file is X, it is assumed that this is going + to become an execute file on some other system. The zsystem + argument is the system that the file will be transferred to. The + ztname argument will be set to a file name that could be passed to + zsysdep_spool_file_name. The zdname argument, if not NULL, will be + set to a data file name appropriate for the remote system. The + zxname argument, if not NULL, will be set to the name of an execute + file on the remote system. None of the names will be more than 14 + characters long. */ + +/*ARGSUSED*/ +static char * +zsfile_name (btype, zsystem, zlocalname, bgrade, fxqt, ztname, zdname, zxname) + int btype; + const char *zsystem; + const char *zlocalname; + int bgrade; + boolean fxqt; + char *ztname; + char *zdname; + char *zxname; +{ + char abseq[CSEQLEN + 1]; + char absimple[11 + CSEQLEN]; + char *zname; + + if (zlocalname == NULL) + zlocalname = zSlocalname; + + while (TRUE) + { + if (! fscmd_seq (zsystem, abseq)) + return NULL; + + if (btype == 'C') + { +#if ! SPOOLDIR_TAYLOR + sprintf (absimple, "C.%.7s%c%s", zsystem, bgrade, abseq); +#else + sprintf (absimple, "C.%c%s", bgrade, abseq); +#endif + } + else if (btype == 'D') + { + /* This name doesn't really matter that much; it's just the + name we use on the local system. The name we use on the + remote system, which we return in zdname, should contain + our system name so that remote UUCP's running SPOOLDIR_V2 + and the like can distinguish while files come from which + systems. */ +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 + sprintf (absimple, "D.%.7s%c%s", zsystem, bgrade, abseq); +#else /* ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 */ +#if ! SPOOLDIR_TAYLOR + sprintf (absimple, "D.%.7s%c%s", zlocalname, bgrade, abseq); +#else /* SPOOLDIR_TAYLOR */ + if (fxqt) + sprintf (absimple, "D.X%s", abseq); + else + sprintf (absimple, "D.%s", abseq); +#endif /* SPOOLDIR_TAYLOR */ +#endif /* ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 */ + } +#if DEBUG > 0 + else + ulog (LOG_FATAL, "zsfile_name: Can't happen"); +#endif + + zname = zsfind_file (absimple, zsystem, bgrade); + if (zname == NULL) + return NULL; + + if (! fsysdep_file_exists (zname)) + break; + + ubuffree (zname); + } + + if (ztname != NULL) + strcpy (ztname, absimple); + + if (zdname != NULL) + sprintf (zdname, "D.%.7s%c%s", zlocalname, bgrade, abseq); + + if (zxname != NULL) + sprintf (zxname, "X.%.7s%c%s", zlocalname, bgrade, abseq); + + return zname; +} + +/* Return a name to use for a data file to be copied to another + system. The name returned will be for a real file. The zlocalname + argument is the local name as seen by the remote system, the bgrade + argument is the file grade, and the fxqt argument is TRUE if this + file will become an execution file. The ztname argument, if not + NULL, will be set to a name that could be passed to + zsysdep_spool_file_name to get back the return value of this + function. The zdname argument, if not NULL, will be set to a name + that the file could be given on another system. The zxname + argument, if not NULL, will be set to a name for an execute file on + another system. */ + +char * +zsysdep_data_file_name (qsys, zlocalname, bgrade, fxqt, ztname, zdname, + zxname) + const struct uuconf_system *qsys; + const char *zlocalname; + int bgrade; + boolean fxqt; + char *ztname; + char *zdname; + char *zxname; +{ + return zsfile_name ('D', qsys->uuconf_zname, zlocalname, bgrade, fxqt, + ztname, zdname, zxname); +} + +/* Get a command file name. */ + +char * +zscmd_file (qsys, bgrade) + const struct uuconf_system *qsys; + int bgrade; +{ + return zsfile_name ('C', qsys->uuconf_zname, (const char *) NULL, + bgrade, FALSE, (char *) NULL, (char *) NULL, + (char *) NULL); +} + +/* Return a name for an execute file to be created locally. This is + used by uux to execute a command locally with remote files. */ + +char * +zsysdep_xqt_file_name () +{ + char abseq[CSEQLEN + 1]; + char absx[11 + CSEQLEN]; + char *zname; + + while (TRUE) + { + if (! fscmd_seq (zSlocalname, abseq)) + return NULL; + + sprintf (absx, "X.%.7sX%s", zSlocalname, abseq); + + zname = zsfind_file (absx, zSlocalname, -1); + if (zname == NULL) + return NULL; + + if (! fsysdep_file_exists (zname)) + break; + + ubuffree (zname); + } + + return zname; +} diff --git a/gnu/libexec/uucp/libunix/fsusg.c b/gnu/libexec/uucp/libunix/fsusg.c new file mode 100644 index 0000000000..e2b40a8ad5 --- /dev/null +++ b/gnu/libexec/uucp/libunix/fsusg.c @@ -0,0 +1,231 @@ +/* fsusage.c -- return space usage of mounted filesystems + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file was modified slightly by Ian Lance Taylor, December 1992, + for use with Taylor UUCP. */ + +#include "uucp.h" +#include "sysdep.h" +#include "fsusg.h" + +int statfs (); + +#if STAT_STATFS2_BSIZE +#ifndef _IBMR2 /* 4.3BSD, SunOS 4, HP-UX, AIX PS/2. */ +#include +#endif +#endif + +#if STAT_STATFS2_FSIZE /* 4.4BSD. */ +#include +#endif + +#if STAT_STATFS2_FS_DATA /* Ultrix. */ +#include +#include +#endif + +#if STAT_USTAT /* SVR2 and others. */ +#include +#endif + +#if STAT_STATFS4 /* SVR3, Dynix, Irix. */ +#include +#endif +#ifdef _AIX +#ifdef _IBMR2 /* AIX RS6000. */ +#include +#endif +#endif + +#ifdef _AIX +#ifdef _I386 /* AIX PS/2. */ +#include +#include +#endif +#endif + +#if STAT_STATVFS /* SVR4. */ +#include +int statvfs (); +#endif + +#define STAT_NONE 0 + +#if ! STAT_STATVFS +#if ! STAT_STATFS2_BSIZE +#if ! STAT_STATFS2_FSIZE +#if ! STAT_STATFS2_FS_DATA +#if ! STAT_STATFS4 +#if ! STAT_USTAT +#undef STAT_NONE +#define STAT_NONE 1 +#endif +#endif +#endif +#endif +#endif +#endif + +#if ! STAT_NONE + +/* Return the number of TOSIZE-byte blocks used by + BLOCKS FROMSIZE-byte blocks, rounding up. */ + +static long +adjust_blocks (blocks, fromsize, tosize) + long blocks; + int fromsize, tosize; +{ + if (fromsize == tosize) /* E.g., from 512 to 512. */ + return blocks; + else if (fromsize > tosize) /* E.g., from 2048 to 512. */ + return blocks * (fromsize / tosize); + else /* E.g., from 256 to 512. */ + return (blocks + 1) / (tosize / fromsize); +} + +#endif + +/* Fill in the fields of FSP with information about space usage for + the filesystem on which PATH resides. + DISK is the device on which PATH is mounted, for space-getting + methods that need to know it. + Return 0 if successful, -1 if not. */ + +int +get_fs_usage (path, disk, fsp) + char *path, *disk; + struct fs_usage *fsp; +{ +#if STAT_NONE + return -1; +#endif + +#if STAT_STATFS2_FS_DATA /* Ultrix. */ + struct fs_data fsd; + + if (statfs (path, &fsd) != 1) + return -1; +#define convert_blocks(b) adjust_blocks ((b), 1024, 512) + fsp->fsu_blocks = convert_blocks (fsd.fd_req.btot); + fsp->fsu_bfree = convert_blocks (fsd.fd_req.bfree); + fsp->fsu_bavail = convert_blocks (fsd.fd_req.bfreen); + fsp->fsu_files = fsd.fd_req.gtot; + fsp->fsu_ffree = fsd.fd_req.gfree; +#endif + +#if STAT_STATFS2_BSIZE /* 4.3BSD, SunOS 4, HP-UX, AIX. */ + struct statfs fsd; + + if (statfs (path, &fsd) < 0) + return -1; +#define convert_blocks(b) adjust_blocks ((b), fsd.f_bsize, 512) +#endif + +#if STAT_STATFS2_FSIZE /* 4.4BSD. */ + struct statfs fsd; + + if (statfs (path, &fsd) < 0) + return -1; +#define convert_blocks(b) adjust_blocks ((b), fsd.f_fsize, 512) +#endif + +#if STAT_STATFS4 /* SVR3, Dynix, Irix. */ + struct statfs fsd; + + if (statfs (path, &fsd, sizeof fsd, 0) < 0) + return -1; + /* Empirically, the block counts on most SVR3 and SVR3-derived + systems seem to always be in terms of 512-byte blocks, + no matter what value f_bsize has. */ +#define convert_blocks(b) (b) +#ifndef _SEQUENT_ /* _SEQUENT_ is DYNIX/ptx. */ +#define f_bavail f_bfree +#endif +#endif + +#if STAT_STATVFS /* SVR4. */ + struct statvfs fsd; + + if (statvfs (path, &fsd) < 0) + return -1; + /* f_frsize isn't guaranteed to be supported. */ +#define convert_blocks(b) \ + adjust_blocks ((b), fsd.f_frsize ? fsd.f_frsize : fsd.f_bsize, 512) +#endif + +#if STAT_USTAT + { + struct stat sstat; + struct ustat s; + + if (stat (path, &sstat) < 0 + || ustat (sstat.st_dev, &s) < 0) + return -1; + fsp->fsu_blocks = -1; + fsp->fsu_bfree = f_tfree; + fsp->fsu_bavail = f_tfree; + fsp->fsu_files = -1; + fsp->fsu_ffree = -1; + } +#endif + +#if ! STAT_STATFS2_FS_DATA /* ! Ultrix */ +#if ! STAT_USTAT +#if ! STAT_NONE + fsp->fsu_blocks = convert_blocks (fsd.f_blocks); + fsp->fsu_bfree = convert_blocks (fsd.f_bfree); + fsp->fsu_bavail = convert_blocks (fsd.f_bavail); + fsp->fsu_files = fsd.f_files; + fsp->fsu_ffree = fsd.f_ffree; +#endif +#endif +#endif + + return 0; +} + +#ifdef _AIX +#ifdef _I386 +/* AIX PS/2 does not supply statfs. */ + +int +statfs (path, fsb) + char *path; + struct statfs *fsb; +{ + struct stat stats; + struct dustat fsd; + + if (stat (path, &stats)) + return -1; + if (dustat (stats.st_dev, 0, &fsd, sizeof (fsd))) + return -1; + fsb->f_type = 0; + fsb->f_bsize = fsd.du_bsize; + fsb->f_blocks = fsd.du_fsize - fsd.du_isize; + fsb->f_bfree = fsd.du_tfree; + fsb->f_bavail = fsd.du_tfree; + fsb->f_files = (fsd.du_isize - 2) * fsd.du_inopb; + fsb->f_ffree = fsd.du_tinode; + fsb->f_fsid.val[0] = fsd.du_site; + fsb->f_fsid.val[1] = fsd.du_pckno; + return 0; +} +#endif +#endif /* _AIX && _I386 */ diff --git a/gnu/libexec/uucp/libunix/fsusg.h b/gnu/libexec/uucp/libunix/fsusg.h new file mode 100644 index 0000000000..8d4d054cb5 --- /dev/null +++ b/gnu/libexec/uucp/libunix/fsusg.h @@ -0,0 +1,31 @@ +/* fsusage.h -- declarations for filesystem space usage info + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This files was modified slightly by Ian Lance Taylor for use with + Taylor UUCP. */ + +/* Space usage statistics for a filesystem. Blocks are 512-byte. */ +struct fs_usage +{ + long fsu_blocks; /* Total blocks. */ + long fsu_bfree; /* Free blocks available to superuser. */ + long fsu_bavail; /* Free blocks available to non-superuser. */ + long fsu_files; /* Total file nodes. */ + long fsu_ffree; /* Free file nodes. */ +}; + +extern int get_fs_usage P((char *path, char *disk, struct fs_usage *fsp)); diff --git a/gnu/libexec/uucp/libunix/ftw.c b/gnu/libexec/uucp/libunix/ftw.c new file mode 100644 index 0000000000..c3372b53ca --- /dev/null +++ b/gnu/libexec/uucp/libunix/ftw.c @@ -0,0 +1,250 @@ +/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. +This file is part of the GNU C Library. +Contributed by Ian Lance Taylor (ian@airs.com). + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. + +Modified by Ian Lanc Taylor for Taylor UUCP, June 1992. */ + +#include "uucp.h" + +#include "sysdep.h" + +#include + +#if HAVE_LIMITS_H +#include +#endif + +#if HAVE_SYS_PARAM_H +#include +#endif + +#if HAVE_OPENDIR +#if HAVE_DIRENT_H +#include +#else /* ! HAVE_DIRENT_H */ +#include +#define dirent direct +#endif /* ! HAVE_DIRENT_H */ +#endif /* HAVE_OPENDIR */ + +#if HAVE_FTW_H +#include +#endif + +#ifndef PATH_MAX +#ifdef MAXPATHLEN +#define PATH_MAX MAXPATHLEN +#else +#define PATH_MAX 1024 +#endif +#endif + +/* Traverse one level of a directory tree. */ + +static int +ftw_dir (dirs, level, descriptors, dir, len, func) + DIR **dirs; + int level; + int descriptors; + char *dir; + size_t len; + int (*func) P((const char *file, const struct stat *status, int flag)); +{ + int got; + struct dirent *entry; + + got = 0; + + errno = 0; + + while ((entry = readdir (dirs[level])) != NULL) + { + size_t namlen; + struct stat s; + int flag, ret, newlev; + + ++got; + + namlen = strlen (entry->d_name); + if (entry->d_name[0] == '.' + && (namlen == 1 || + (namlen == 2 && entry->d_name[1] == '.'))) + { + errno = 0; + continue; + } + + if (namlen + len + 1 > PATH_MAX) + { +#ifdef ENAMETOOLONG + errno = ENAMETOOLONG; +#else + errno = ENOMEM; +#endif + return -1; + } + + dir[len] = '/'; + memcpy ((dir + len + 1), entry->d_name, namlen + 1); + + if (stat (dir, &s) < 0) + { + if (errno != EACCES) + return -1; + flag = FTW_NS; + } + else if (S_ISDIR (s.st_mode)) + { + newlev = (level + 1) % descriptors; + + if (dirs[newlev] != NULL) + closedir (dirs[newlev]); + + dirs[newlev] = opendir (dir); + if (dirs[newlev] != NULL) + flag = FTW_D; + else + { + if (errno != EACCES) + return -1; + flag = FTW_DNR; + } + } + else + flag = FTW_F; + + ret = (*func) (dir, &s, flag); + + if (flag == FTW_D) + { + if (ret == 0) + ret = ftw_dir (dirs, newlev, descriptors, dir, + namlen + len + 1, func); + if (dirs[newlev] != NULL) + { + int save; + + save = errno; + closedir (dirs[newlev]); + errno = save; + dirs[newlev] = NULL; + } + } + + if (ret != 0) + return ret; + + if (dirs[level] == NULL) + { + int skip; + + dir[len] = '\0'; + dirs[level] = opendir (dir); + if (dirs[level] == NULL) + return -1; + skip = got; + while (skip-- != 0) + { + errno = 0; + if (readdir (dirs[level]) == NULL) + return errno == 0 ? 0 : -1; + } + } + + errno = 0; + } + + return errno == 0 ? 0 : -1; +} + +/* Call a function on every element in a directory tree. */ + +int +ftw (dir, func, descriptors) + const char *dir; + int (*func) P((const char *file, const struct stat *status, int flag)); + int descriptors; +{ + DIR **dirs; + int c; + DIR **p; + size_t len; + char buf[PATH_MAX + 1]; + struct stat s; + int flag, ret; + + if (descriptors <= 0) + descriptors = 1; + + dirs = (DIR **) malloc (descriptors * sizeof (DIR *)); + if (dirs == NULL) + return -1; + c = descriptors; + p = dirs; + while (c-- != 0) + *p++ = NULL; + + len = strlen (dir); + memcpy (buf, dir, len + 1); + + if (stat (dir, &s) < 0) + { + if (errno != EACCES) + { + free ((pointer) dirs); + return -1; + } + flag = FTW_NS; + } + else if (S_ISDIR (s.st_mode)) + { + dirs[0] = opendir (dir); + if (dirs[0] != NULL) + flag = FTW_D; + else + { + if (errno != EACCES) + { + free ((pointer) dirs); + return -1; + } + flag = FTW_DNR; + } + } + else + flag = FTW_F; + + ret = (*func) (buf, &s, flag); + + if (flag == FTW_D) + { + if (ret == 0) + ret = ftw_dir (dirs, 0, descriptors, buf, len, func); + if (dirs[0] != NULL) + { + int save; + + save = errno; + closedir (dirs[0]); + errno = save; + } + } + + free ((pointer) dirs); + return ret; +} diff --git a/gnu/libexec/uucp/libunix/getcwd.c b/gnu/libexec/uucp/libunix/getcwd.c new file mode 100644 index 0000000000..d3623bd2cd --- /dev/null +++ b/gnu/libexec/uucp/libunix/getcwd.c @@ -0,0 +1,59 @@ +/* getcwd.c + Replacement for the getcwd function that just calls /bin/pwd. */ + +#include "uucp.h" + +#include "sysdep.h" + +#include + +char * +getcwd (zbuf, cbuf) + char *zbuf; + size_t cbuf; +{ + const char *azargs[2]; + FILE *e; + pid_t ipid; + int cread; + int ierr; + + azargs[0] = PWD_PROGRAM; + azargs[1] = NULL; + e = espopen (azargs, TRUE, &ipid); + if (e == NULL) + return NULL; + + ierr = 0; + + cread = fread (zbuf, sizeof (char), cbuf, e); + if (cread == 0) + ierr = errno; + + (void) fclose (e); + + if (ixswait ((unsigned long) ipid, (const char *) NULL) != 0) + { + ierr = EACCES; + cread = 0; + } + + if (cread != 0) + { + if (zbuf[cread - 1] == '\n') + zbuf[cread - 1] = '\0'; + else + { + ierr = ERANGE; + cread = 0; + } + } + + if (cread == 0) + { + errno = ierr; + return NULL; + } + + return zbuf; +} diff --git a/gnu/libexec/uucp/libunix/indir.c b/gnu/libexec/uucp/libunix/indir.c new file mode 100644 index 0000000000..2484ec23f8 --- /dev/null +++ b/gnu/libexec/uucp/libunix/indir.c @@ -0,0 +1,133 @@ +/* indir.c + See if a file is in a directory. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +/* See whether a file is in a directory, and optionally check access. */ + +boolean +fsysdep_in_directory (zfile, zdir, fcheck, freadable, zuser) + const char *zfile; + const char *zdir; + boolean fcheck; + boolean freadable; + const char *zuser; +{ + size_t c; + char *zcopy, *zslash; + struct stat s; + + if (*zfile != '/') + return FALSE; + c = strlen (zdir); + if (c > 0 && zdir[c - 1] == '/') + c--; + if (strncmp (zfile, zdir, c) != 0 + || (zfile[c] != '/' && zfile[c] != '\0')) + return FALSE; + if (strstr (zfile + c, "/../") != NULL) + return FALSE; + + /* If we're not checking access, get out now. */ + if (! fcheck) + return TRUE; + + zcopy = zbufcpy (zfile); + + /* Start checking directories after zdir. Otherwise, we would + require that all directories down to /usr/spool/uucppublic be + publically searchable; they probably are but it should not be a + requirement. */ + zslash = zcopy + c; + do + { + char b; + struct stat shold; + + b = *zslash; + *zslash = '\0'; + + shold = s; + if (stat (zcopy, &s) != 0) + { + if (errno != ENOENT) + { + ulog (LOG_ERROR, "stat (%s): %s", zcopy, strerror (errno)); + ubuffree (zcopy); + return FALSE; + } + + /* If this is the top directory, any problems will be caught + later when we try to open it. */ + if (zslash == zcopy + c) + { + ubuffree (zcopy); + return TRUE; + } + + /* Go back and check the last directory for read or write + access. */ + s = shold; + break; + } + + /* If this is not a directory, get out of the loop. */ + if (! S_ISDIR (s.st_mode)) + break; + + /* Make sure the directory is searchable. */ + if (! fsuser_access (&s, X_OK, zuser)) + { + ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES)); + ubuffree (zcopy); + return FALSE; + } + + /* If we've reached the end of the string, get out. */ + if (b == '\0') + break; + + *zslash = b; + } + while ((zslash = strchr (zslash + 1, '/')) != NULL); + + /* At this point s holds a stat on the last component of the path. + We must check it for readability or writeability. */ + if (! fsuser_access (&s, freadable ? R_OK : W_OK, zuser)) + { + ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES)); + ubuffree (zcopy); + return FALSE; + } + + ubuffree (zcopy); + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/init.c b/gnu/libexec/uucp/libunix/init.c new file mode 100644 index 0000000000..d4a1377628 --- /dev/null +++ b/gnu/libexec/uucp/libunix/init.c @@ -0,0 +1,394 @@ +/* init.c + Initialize the system dependent routines. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" +#include "sysdep.h" + +#include +#include + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#if ! HAVE_GETHOSTNAME && HAVE_UNAME +#include +#endif + +/* Use getcwd in preference to getwd; if we have neither, we will be + using a getcwd replacement. */ +#if HAVE_GETCWD +#undef HAVE_GETWD +#define HAVE_GETWD 0 +#else /* ! HAVE_GETCWD */ +#if ! HAVE_GETWD +#undef HAVE_GETCWD +#define HAVE_GETCWD 1 +#endif /* ! HAVE_GETWD */ +#endif /* ! HAVE_GETCWD */ + +#if HAVE_GETWD +/* Get a value for MAXPATHLEN. */ +#if HAVE_SYS_PARAMS_H +#include +#endif + +#if HAVE_LIMITS_H +#include +#endif + +#ifndef MAXPATHLEN +#ifdef PATH_MAX +#define MAXPATHLEN PATH_MAX +#else /* ! defined (PATH_MAX) */ +#define MAXPATHLEN 1024 +#endif /* ! defined (PATH_MAX) */ +#endif /* ! defined (MAXPATHLEN) */ +#endif /* HAVE_GETWD */ + +/* External functions. */ +#ifndef getlogin +extern char *getlogin (); +#endif +#if GETPWNAM_DECLARATION_OK +#ifndef getpwnam +extern struct passwd *getpwnam (); +#endif +#endif +#if GETPWUID_DECLARATION_OK +#ifndef getpwuid +extern struct passwd *getpwuid (); +#endif +#endif +#if HAVE_GETCWD +#ifndef getcwd +extern char *getcwd (); +#endif +#endif +#if HAVE_GETWD +#ifndef getwd +extern char *getwd (); +#endif +#endif +#if HAVE_SYSCONF +#ifndef sysconf +extern long sysconf (); +#endif +#endif + +/* Initialize the system dependent routines. We will probably be running + suid to uucp, so we make sure that nothing is obviously wrong. We + save the login name since we will be losing the real uid. */ +static char *zSlogin; + +/* The UUCP spool directory. */ +const char *zSspooldir; + +/* The UUCP lock directory. */ +const char *zSlockdir; + +/* The local UUCP name. */ +const char *zSlocalname; + +/* We save the current directory since we will do a chdir to the + spool directory. */ +char *zScwd; + +/* The maximum length of a system name is controlled by the type of spool + directory we use. */ +#if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 || SPOOLDIR_ULTRIX +size_t cSysdep_max_name_len = 7; +#endif +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 +size_t cSysdep_max_name_len = 14; +#endif +#if SPOOLDIR_TAYLOR +#if HAVE_LONG_FILE_NAMES +size_t cSysdep_max_name_len = 255; +#else /* ! HAVE_LONG_FILE_NAMES */ +size_t cSysdep_max_name_len = 14; +#endif /* ! HAVE_LONG_FILE_NAMES */ +#endif /* SPOOLDIR_TAYLOR */ + +/* Initialize the system dependent routines. */ + +void +usysdep_initialize (puuconf,iflags) + pointer puuconf; + int iflags; +{ + int cdescs; + int o; + int iuuconf; + char *z; + struct passwd *q; + + ulog_id (getpid ()); + + /* Close everything but stdin, stdout and stderr. */ +#if HAVE_GETDTABLESIZE + cdescs = getdtablesize (); +#else +#if HAVE_SYSCONF + cdescs = sysconf (_SC_OPEN_MAX); +#else +#ifdef OPEN_MAX + cdescs = OPEN_MAX; +#else +#ifdef NOFILE + cdescs = NOFILE; +#else + cdescs = 20; +#endif /* ! defined (NOFILE) */ +#endif /* ! defined (OPEN_MAX) */ +#endif /* ! HAVE_SYSCONF */ +#endif /* ! HAVE_GETDTABLESIZE */ + + for (o = 3; o < cdescs; o++) + (void) close (o); + + /* Make sure stdin, stdout and stderr are open. */ + if (fcntl (0, F_GETFD, 0) < 0 + && open ((char *) "/dev/null", O_RDONLY, 0) != 0) + exit (EXIT_FAILURE); + if (fcntl (1, F_GETFD, 0) < 0 + && open ((char *) "/dev/null", O_WRONLY, 0) != 1) + exit (EXIT_FAILURE); + if (fcntl (2, F_GETFD, 0) < 0 + && open ((char *) "/dev/null", O_WRONLY, 0) != 2) + exit (EXIT_FAILURE); + + iuuconf = uuconf_spooldir (puuconf, &zSspooldir); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + iuuconf = uuconf_lockdir (puuconf, &zSlockdir); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + iuuconf = uuconf_localname (puuconf, &zSlocalname); + if (iuuconf == UUCONF_NOT_FOUND) + { +#if HAVE_GETHOSTNAME + char ab[256]; + + if (gethostname (ab, sizeof ab - 1) < 0) + ulog (LOG_FATAL, "gethostname: %s", strerror (errno)); + ab[sizeof ab - 1] = '\0'; + ab[strcspn (ab, ".")] = '\0'; + zSlocalname = zbufcpy (ab); +#else /* ! HAVE_GETHOSTNAME */ +#if HAVE_UNAME + struct utsname s; + + if (uname (&s) < 0) + ulog (LOG_FATAL, "uname: %s", strerror (errno)); + zSlocalname = zbufcpy (s.nodename); +#else /* ! HAVE_UNAME */ + ulog (LOG_FATAL, "Don't know how to get local node name"); +#endif /* ! HAVE_UNAME */ +#endif /* ! HAVE_GETHOSTNAME */ + } + else if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + /* We always set our file modes to exactly what we want. */ + umask (0); + + /* Get the login name, making sure that it matches the uid. Many + systems truncate the getlogin return value to 8 characters, but + keep the full name in the password file, so we prefer the name in + the password file. */ + z = getenv ("LOGNAME"); + if (z == NULL) + z = getenv ("USER"); + if (z == NULL) + z = getlogin (); + if (z == NULL) + q = NULL; + else + { + q = getpwnam (z); + if (q != NULL) + z = q->pw_name; + } + if (q == NULL || q->pw_uid != getuid ()) + { + q = getpwuid (getuid ()); + if (q == NULL) + z = NULL; + else + z = q->pw_name; + } + if (z != NULL) + zSlogin = zbufcpy (z); + + /* On some old systems, an suid program run by root is started with + an euid of 0. If this happens, we look up the uid we should have + and set ourselves to it manually. This means that on such a + system root will not be able to uucp or uux files that are not + readable by uucp. */ + if ((iflags & INIT_SUID) != 0 + && geteuid () == 0) + { + q = getpwnam (OWNER); + if (q != NULL) + setuid (q->pw_uid); + } + + if ((iflags & INIT_GETCWD) != 0) + { + const char *zenv; + struct stat senv, sdot; + + /* Get the current working directory. We have to get it now, + since we're about to do a chdir. We use PWD if it's defined + and if it really names the working directory, since if it's + not the same as whatever getcwd returns it's probably more + appropriate. */ + zenv = getenv ("PWD"); + if (zenv != NULL + && stat ((char *) zenv, &senv) == 0 + && stat ((char *) ".", &sdot) == 0 + && senv.st_ino == sdot.st_ino + && senv.st_dev == sdot.st_dev) + zScwd = zbufcpy (zenv); + else + { + +#if HAVE_GETCWD + { + size_t c; + + c = 128; + while (TRUE) + { + zScwd = (char *) xmalloc (c); + if (getcwd (zScwd, c) != NULL) + break; + xfree ((pointer) zScwd); + zScwd = NULL; + if (errno != ERANGE) + break; + c <<= 1; + } + } +#endif /* HAVE_GETCWD */ + +#if HAVE_GETWD + zScwd = (char *) xmalloc (MAXPATHLEN); + if (getwd (zScwd) == NULL) + { + xfree ((pointer) zScwd); + zScwd = NULL; + } +#endif /* HAVE_GETWD */ + + if (zScwd != NULL) + zScwd = (char *) xrealloc ((pointer) zScwd, + strlen (zScwd) + 1); + } + } + + if ((iflags & INIT_NOCHDIR) == 0) + { + /* Connect to the spool directory, and create it if it doesn't + exist. */ + if (chdir (zSspooldir) < 0) + { + if (errno == ENOENT + && mkdir ((char *) zSspooldir, IDIRECTORY_MODE) < 0) + ulog (LOG_FATAL, "mkdir (%s): %s", zSspooldir, + strerror (errno)); + if (chdir (zSspooldir) < 0) + ulog (LOG_FATAL, "chdir (%s): %s", zSspooldir, + strerror (errno)); + } + } +} + +/* Exit the program. */ + +void +usysdep_exit (fsuccess) + boolean fsuccess; +{ + exit (fsuccess ? EXIT_SUCCESS : EXIT_FAILURE); +} + +/* This is called when a non-standard configuration file is used, to + make sure the program doesn't hand out privileged file access. + This means that to test non-standard configuration files, you + should be logged in as uucp. This is called before + usysdep_initialize. It ensures that someone can't simply use an + alternate configuration file to steal UUCP transfers from other + systems. This will still permit people to set up their own + configuration file and pretend to be whatever system they choose. + The only real security is to use a high level of protection on the + modem ports. */ + +/*ARGSUSED*/ +boolean fsysdep_other_config (z) + const char *z; +{ + (void) setuid (getuid ()); + (void) setgid (getgid ()); + return TRUE; +} + +/* Get the node name to use if it was not specified in the configuration + file. */ + +const char * +zsysdep_localname () +{ + return zSlocalname; +} + +/* Get the login name. We actually get the login name in + usysdep_initialize, because after that we may switch away from the + real uid. */ + +const char * +zsysdep_login_name () +{ + if (zSlogin == NULL) + ulog (LOG_FATAL, "Can't get login name"); + return zSlogin; +} diff --git a/gnu/libexec/uucp/libunix/isdir.c b/gnu/libexec/uucp/libunix/isdir.c new file mode 100644 index 0000000000..fc95e5275a --- /dev/null +++ b/gnu/libexec/uucp/libunix/isdir.c @@ -0,0 +1,18 @@ +/* isdir.c + See whether a file exists and is a directory. */ + +#include "uucp.h" + +#include "system.h" +#include "sysdep.h" + +boolean +fsysdep_directory (z) + const char *z; +{ + struct stat s; + + if (stat ((char *) z, &s) < 0) + return FALSE; + return S_ISDIR (s.st_mode); +} diff --git a/gnu/libexec/uucp/libunix/isfork.c b/gnu/libexec/uucp/libunix/isfork.c new file mode 100644 index 0000000000..f067d07552 --- /dev/null +++ b/gnu/libexec/uucp/libunix/isfork.c @@ -0,0 +1,25 @@ +/* isfork.c + Retry fork several times before giving up. */ + +#include "uucp.h" + +#include "sysdep.h" + +#include + +pid_t +ixsfork () +{ + int i; + pid_t iret; + + for (i = 0; i < 10; i++) + { + iret = fork (); + if (iret >= 0 || errno != EAGAIN) + return iret; + sleep (5); + } + + return iret; +} diff --git a/gnu/libexec/uucp/libunix/iswait.c b/gnu/libexec/uucp/libunix/iswait.c new file mode 100644 index 0000000000..d2610aa1f8 --- /dev/null +++ b/gnu/libexec/uucp/libunix/iswait.c @@ -0,0 +1,159 @@ +/* iswait.c + Wait for a process to finish. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" + +#include + +#if HAVE_SYS_WAIT_H +#include +#endif + +/* We use a typedef wait_status for wait (waitpid, wait4) to put + results into. We define the POSIX examination functions we need if + they are not already defined (if they aren't defined, I assume that + we have a standard wait status). */ + +#if HAVE_UNION_WAIT +typedef union wait wait_status; +#ifndef WIFEXITED +#define WIFEXITED(u) ((u).w_termsig == 0) +#endif +#ifndef WEXITSTATUS +#define WEXITSTATUS(u) ((u).w_retcode) +#endif +#ifndef WTERMSIG +#define WTERMSIG(u) ((u).w_termsig) +#endif +#else /* ! HAVE_UNION_WAIT */ +typedef int wait_status; +#ifndef WIFEXITED +#define WIFEXITED(i) (((i) & 0xff) == 0) +#endif +#ifndef WEXITSTATUS +#define WEXITSTATUS(i) (((i) >> 8) & 0xff) +#endif +#ifndef WTERMSIG +#define WTERMSIG(i) ((i) & 0x7f) +#endif +#endif /* ! HAVE_UNION_WAIT */ + +/* Wait for a particular process to finish. The ipid argument should + be pid_t, but then we couldn't have a prototype. If the zreport + argument is not NULL, then a wait error will be logged, and if the + exit status is non-zero it will be logged with zreport as the + header of the log message. If the zreport argument is NULL, no + errors will be logged. This function returns the exit status if + the process exited normally, or -1 on error or if the process was + killed by a signal (I don't just always return the exit status + because then the calling code would have to prepared to handle + union wait status vs. int status, and none of the callers care + which signal killed the program anyhow). + + This functions keeps waiting until the process finished, even if it + is interrupted by a signal. I think this is right for all uses. + The controversial one would be when called from uuxqt to wait for a + requested process. Hitting uuxqt with SIGKILL will approximate the + actions taken if we return from here with an error anyhow. If we + do get a signal, we call ulog with a NULL argument to get it in the + log file at about the right time. */ + +int +ixswait (ipid, zreport) + unsigned long ipid; + const char *zreport; +{ + wait_status istat; + +#if HAVE_WAITPID + while (waitpid ((pid_t) ipid, (pointer) &istat, 0) < 0) + { + if (errno != EINTR) + { + if (zreport != NULL) + ulog (LOG_ERROR, "waitpid: %s", strerror (errno)); + return -1; + } + ulog (LOG_ERROR, (const char *) NULL); + } +#else /* ! HAVE_WAITPID */ +#if HAVE_WAIT4 + while (wait4 ((pid_t) ipid, (pointer) &istat, 0, + (struct rusage *) NULL) < 0) + { + if (errno != EINTR) + { + if (zreport != NULL) + ulog (LOG_ERROR, "wait4: %s", strerror (errno)); + return -1; + } + ulog (LOG_ERROR, (const char *) NULL); + } +#else /* ! HAVE_WAIT4 */ + pid_t igot; + + /* We could theoretically get the wrong child here if we're in some + kind of weird pipeline, so we don't give any error messages for + it. */ + while ((igot = wait ((pointer) &istat)) != (pid_t) ipid) + { + if (igot < 0) + { + if (errno != EINTR) + { + if (zreport != NULL) + ulog (LOG_ERROR, "wait: %s", strerror (errno)); + return -1; + } + ulog (LOG_ERROR, (const char *) NULL); + } + } +#endif /* ! HAVE_WAIT4 */ +#endif /* ! HAVE_WAITPID */ + + DEBUG_MESSAGE2 (DEBUG_EXECUTE, "%s %d", + WIFEXITED (istat) ? "Exit status" : "Signal", + WIFEXITED (istat) ? WEXITSTATUS (istat) : WTERMSIG (istat)); + + if (WIFEXITED (istat) && WEXITSTATUS (istat) == 0) + return 0; + + if (zreport != NULL) + { + if (! WIFEXITED (istat)) + ulog (LOG_ERROR, "%s: Got signal %d", zreport, WTERMSIG (istat)); + else + ulog (LOG_ERROR, "%s: Exit status %d", zreport, + WEXITSTATUS (istat)); + } + + if (WIFEXITED (istat)) + return WEXITSTATUS (istat); + else + return -1; +} diff --git a/gnu/libexec/uucp/libunix/jobid.c b/gnu/libexec/uucp/libunix/jobid.c new file mode 100644 index 0000000000..7f22f1d37d --- /dev/null +++ b/gnu/libexec/uucp/libunix/jobid.c @@ -0,0 +1,101 @@ +/* jobid.c + Convert file names to jobids and vice versa. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uuconf.h" +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +/* Translate a file name and an associated system into a job id. + These job ids are used by uustat. We use the system name attached + to the grade and sequence number. This won't work correctly if the + file name was actually created by some other version of uucp that + uses a different length for the sequence number. Too bad. */ + +char * +zsfile_to_jobid (qsys, zfile, bgrade) + const struct uuconf_system *qsys; + const char *zfile; + int bgrade; +{ + size_t clen; + char *zret; + + clen = strlen (qsys->uuconf_zname); + zret = zbufalc (clen + CSEQLEN + 2); + memcpy (zret, qsys->uuconf_zname, clen); + zret[clen] = bgrade; + memcpy (zret + clen + 1, zfile + strlen (zfile) - CSEQLEN, CSEQLEN + 1); + return zret; +} + +/* Turn a job id back into a file name. */ + +char * +zsjobid_to_file (zid, pzsystem, pbgrade) + const char *zid; + char **pzsystem; + char *pbgrade; +{ + size_t clen; + const char *zend; + char *zsys; + char abname[CSEQLEN + 11]; + char *zret; + + clen = strlen (zid); + if (clen <= CSEQLEN) + { + ulog (LOG_ERROR, "%s: Bad job id", zid); + return NULL; + } + + zend = zid + clen - CSEQLEN - 1; + + zsys = zbufalc (clen - CSEQLEN); + memcpy (zsys, zid, clen - CSEQLEN - 1); + zsys[clen - CSEQLEN - 1] = '\0'; + + /* This must correspond to zsfile_name. */ +#if ! SPOOLDIR_TAYLOR + sprintf (abname, "C.%.7s%s", zsys, zend); +#else + sprintf (abname, "C.%s", zend); +#endif + + zret = zsfind_file (abname, zsys, *zend); + + if (zret != NULL && pzsystem != NULL) + *pzsystem = zsys; + else + ubuffree (zsys); + + if (pbgrade != NULL) + *pbgrade = *zend; + + return zret; +} diff --git a/gnu/libexec/uucp/libunix/lcksys.c b/gnu/libexec/uucp/libunix/lcksys.c new file mode 100644 index 0000000000..4ece16afe7 --- /dev/null +++ b/gnu/libexec/uucp/libunix/lcksys.c @@ -0,0 +1,41 @@ +/* lcksys.c + Lock and unlock a remote system. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" + +/* Lock a remote system. */ + +boolean +fsysdep_lock_system (qsys) + const struct uuconf_system *qsys; +{ + char *z; + boolean fret; + + z = zbufalc (strlen (qsys->uuconf_zname) + sizeof "LCK.."); + sprintf (z, "LCK..%.8s", qsys->uuconf_zname); + fret = fsdo_lock (z, FALSE, (boolean *) NULL); + ubuffree (z); + return fret; +} + +/* Unlock a remote system. */ + +boolean +fsysdep_unlock_system (qsys) + const struct uuconf_system *qsys; +{ + char *z; + boolean fret; + + z = zbufalc (strlen (qsys->uuconf_zname) + sizeof "LCK.."); + sprintf (z, "LCK..%.8s", qsys->uuconf_zname); + fret = fsdo_unlock (z, FALSE); + ubuffree (z); + return fret; +} diff --git a/gnu/libexec/uucp/libunix/link.c b/gnu/libexec/uucp/libunix/link.c new file mode 100644 index 0000000000..4550c76c94 --- /dev/null +++ b/gnu/libexec/uucp/libunix/link.c @@ -0,0 +1,38 @@ +/* link.c + Link two files. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +boolean +fsysdep_link (zfrom, zto, pfworked) + const char *zfrom; + const char *zto; + boolean *pfworked; +{ + *pfworked = FALSE; + if (link (zfrom, zto) == 0) + { + *pfworked = TRUE; + return TRUE; + } + if (errno == ENOENT) + { + if (! fsysdep_make_dirs (zto, TRUE)) + return FALSE; + if (link (zfrom, zto) == 0) + { + *pfworked = TRUE; + return TRUE; + } + } + if (errno == EXDEV) + return TRUE; + ulog (LOG_ERROR, "link (%s, %s): %s", zfrom, zto, strerror (errno)); + return FALSE; +} diff --git a/gnu/libexec/uucp/libunix/locfil.c b/gnu/libexec/uucp/libunix/locfil.c new file mode 100644 index 0000000000..0e05af9bce --- /dev/null +++ b/gnu/libexec/uucp/libunix/locfil.c @@ -0,0 +1,95 @@ +/* locfil.c + Expand a file name on the local system. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +#if GETPWNAM_DECLARATION_OK +#ifndef getpwnam +extern struct passwd *getpwnam (); +#endif +#endif + +/* Turn a file name into an absolute path, by doing tilde expansion + and moving any other type of file into the public directory. */ + +char * +zsysdep_local_file (zfile, zpubdir) + const char *zfile; + const char *zpubdir; +{ + const char *zdir; + + if (*zfile == '/') + return zbufcpy (zfile); + + if (*zfile != '~') + zdir = zpubdir; + else + { + if (zfile[1] == '\0') + return zbufcpy (zpubdir); + + if (zfile[1] == '/') + { + zdir = zpubdir; + zfile += 2; + } + else + { + size_t cuserlen; + char *zcopy; + struct passwd *q; + + ++zfile; + cuserlen = strcspn ((char *) zfile, "/"); + zcopy = zbufalc (cuserlen + 1); + memcpy (zcopy, zfile, cuserlen); + zcopy[cuserlen] = '\0'; + + q = getpwnam (zcopy); + if (q == NULL) + { + ulog (LOG_ERROR, "User %s not found", zcopy); + ubuffree (zcopy); + return NULL; + } + ubuffree (zcopy); + + if (zfile[cuserlen] == '\0') + return zbufcpy (q->pw_dir); + + zdir = q->pw_dir; + zfile += cuserlen + 1; + } + } + + return zsysdep_in_dir (zdir, zfile); +} diff --git a/gnu/libexec/uucp/libunix/lock.c b/gnu/libexec/uucp/libunix/lock.c new file mode 100644 index 0000000000..c43e31dfd0 --- /dev/null +++ b/gnu/libexec/uucp/libunix/lock.c @@ -0,0 +1,477 @@ +/* lock.c + Lock and unlock a file name. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char lock_rcsid[] = "$Id: lock.c,v 1.1 1993/08/04 19:32:33 jtc Exp $"; +#endif + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +/* Lock something. If the fspooldir argument is TRUE, the argument is + a file name relative to the spool directory; otherwise the argument + is a simple file name which should be created in the system lock + directory (under HDB this is /etc/locks). */ + +boolean +fsdo_lock (zlock, fspooldir, pferr) + const char *zlock; + boolean fspooldir; + boolean *pferr; +{ + char *zfree; + const char *zpath, *zslash; + size_t cslash; + pid_t ime; + char *ztempfile; + char abtempfile[sizeof "TMP1234567890"]; + int o; +#if HAVE_V2_LOCKFILES + int i; +#else + char ab[12]; +#endif + int cwrote; + const char *zerr; + boolean fret; + + if (pferr != NULL) + *pferr = TRUE; + + if (fspooldir) + { + zfree = NULL; + zpath = zlock; + } + else + { + zfree = zsysdep_in_dir (zSlockdir, zlock); + zpath = zfree; + } + + ime = getpid (); + + /* We do the actual lock by creating a file and then linking it to + the final file name we want. This avoids race conditions due to + one process checking the file before we have finished writing it, + and also works even if we are somehow running as root. + + First, create the file in the right directory (we must create the + file in the same directory since otherwise we might attempt a + cross-device link). */ + zslash = strrchr (zpath, '/'); + if (zslash == NULL) + cslash = 0; + else + cslash = zslash - zpath + 1; + + sprintf (abtempfile, "TMP%010lx", (unsigned long) ime); + ztempfile = zbufalc (cslash + sizeof abtempfile); + memcpy (ztempfile, zpath, cslash); + memcpy (ztempfile + cslash, abtempfile, sizeof abtempfile); + + o = creat (ztempfile, IPUBLIC_FILE_MODE); + if (o < 0) + { + if (errno == ENOENT) + { + if (! fsysdep_make_dirs (ztempfile, FALSE)) + { + ubuffree (zfree); + ubuffree (ztempfile); + return FALSE; + } + o = creat (ztempfile, IPUBLIC_FILE_MODE); + } + if (o < 0) + { + ulog (LOG_ERROR, "creat (%s): %s", ztempfile, strerror (errno)); + ubuffree (zfree); + ubuffree (ztempfile); + return FALSE; + } + } + +#if HAVE_V2_LOCKFILES + i = ime; + cwrote = write (o, &i, sizeof i); +#else + sprintf (ab, "%10d\n", (int) ime); + cwrote = write (o, ab, strlen (ab)); +#endif + + zerr = NULL; + if (cwrote < 0) + zerr = "write"; + if (close (o) < 0) + zerr = "close"; + if (zerr != NULL) + { + ulog (LOG_ERROR, "%s (%s): %s", zerr, ztempfile, strerror (errno)); + (void) remove (ztempfile); + ubuffree (zfree); + ubuffree (ztempfile); + return FALSE; + } + + /* Now try to link the file we just created to the lock file that we + want. If it fails, try reading the existing file to make sure + the process that created it still exists. We do this in a loop + to make it easy to retry if the old locking process no longer + exists. */ + fret = TRUE; + if (pferr != NULL) + *pferr = FALSE; + o = -1; + zerr = NULL; + + while (link (ztempfile, zpath) != 0) + { + int cgot; + int ipid; + boolean freadonly; + + fret = FALSE; + + if (errno != EEXIST) + { + ulog (LOG_ERROR, "link (%s, %s): %s", ztempfile, zpath, + strerror (errno)); + if (pferr != NULL) + *pferr = TRUE; + break; + } + + freadonly = FALSE; + o = open ((char *) zpath, O_RDWR | O_NOCTTY, 0); + if (o < 0) + { + if (errno == EACCES) + { + freadonly = TRUE; + o = open ((char *) zpath, O_RDONLY, 0); + } + if (o < 0) + { + if (errno == ENOENT) + { + /* The file was presumably removed between the link + and the open. Try the link again. */ + fret = TRUE; + continue; + } + zerr = "open"; + break; + } + } + + /* The race starts here. See below for a discussion. */ + +#if HAVE_V2_LOCKFILES + cgot = read (o, &i, sizeof i); +#else + cgot = read (o, ab, sizeof ab - 1); +#endif + + if (cgot < 0) + { + zerr = "read"; + break; + } + +#if HAVE_V2_LOCKFILES + ipid = i; +#else + ab[cgot] = '\0'; + ipid = strtol (ab, (char **) NULL, 10); +#endif + + /* On NFS, the link might have actually succeeded even though we + got a failure return. This can happen if the original + acknowledgement was lost or delayed and the operation was + retried. In this case the pid will be our own. This + introduces a rather improbable race condition: if a stale + lock was left with our process ID in it, and another process + just did the kill, below, but has not yet changed the lock + file to hold its own process ID, we could start up and make + it all the way to here and think we have the lock. I'm not + going to worry about this possibility. */ + if (ipid == ime) + { + fret = TRUE; + break; + } + + /* If the process still exists, we will get EPERM rather than + ESRCH. We then return FALSE to indicate that we cannot make + the lock. */ + if (kill (ipid, 0) == 0 || errno == EPERM) + break; + + ulog (LOG_ERROR, "Found stale lock %s held by process %d", + zpath, ipid); + + /* This is a stale lock, created by a process that no longer + exists. + + Now we could remove the file (and, if the file mode disallows + writing, that's what we have to do), but we try to avoid + doing so since it causes a race condition. If we remove the + file, and are interrupted any time after we do the read until + we do the remove, another process could get in, open the + file, find that it was a stale lock, remove the file and + create a new one. When we regained control we would remove + the file the other process just created. + + These files are being generated partially for the benefit of + cu, and it would be nice to avoid the race however cu avoids + it, so that the programs remain compatible. Unfortunately, + nobody seems to know how cu avoids the race, or even if it + tries to avoid it at all. + + There are a few ways to avoid the race. We could use kernel + locking primitives, but they may not be available. We could + link to a special file name, but if that file were left lying + around then no stale lock could ever be broken (Henry Spencer + would think this was a good thing). + + Instead I've implemented the following procedure: seek to the + start of the file, write our pid into it, sleep for five + seconds, and then make sure our pid is still there. Anybody + who checks the file while we're asleep will find our pid + there and fail the lock. The only race will come from + another process which has done the read by the time we do our + write. That process will then have five seconds to do its + own write. When we wake up, we'll notice that our pid is no + longer in the file, and retry the lock from the beginning. + + This relies on the atomicity of write(2). If it possible for + the writes of two processes to be interleaved, the two + processes could livelock. POSIX unfortunately leaves this + case explicitly undefined; however, given that the write is + of less than a disk block, it's difficult to imagine an + interleave occurring. + + Note that this is still a race. If it takes the second + process more than five seconds to do the kill, the lseek, and + the write, both processes will think they have the lock. + Perhaps the length of time to sleep should be configurable. + Even better, perhaps I should add a configuration option to + use a permanent lock file, which eliminates any race and + forces the installer to be aware of the existence of the + permanent lock file. + + We stat the file after the sleep, to make sure some other + program hasn't deleted it for us. */ + if (freadonly) + { + (void) close (o); + o = -1; + (void) remove (zpath); + continue; + } + + if (lseek (o, (off_t) 0, SEEK_SET) != 0) + { + zerr = "lseek"; + break; + } + +#if HAVE_V2_LOCKFILES + i = ime; + cwrote = write (o, &i, sizeof i); +#else + sprintf (ab, "%10d\n", (int) ime); + cwrote = write (o, ab, strlen (ab)); +#endif + + if (cwrote < 0) + { + zerr = "write"; + break; + } + + (void) sleep (5); + + if (lseek (o, (off_t) 0, SEEK_SET) != 0) + { + zerr = "lseek"; + break; + } + +#if HAVE_V2_LOCKFILES + cgot = read (o, &i, sizeof i); +#else + cgot = read (o, ab, sizeof ab - 1); +#endif + + if (cgot < 0) + { + zerr = "read"; + break; + } + +#if HAVE_V2_LOCKFILES + ipid = i; +#else + ab[cgot] = '\0'; + ipid = strtol (ab, (char **) NULL, 10); +#endif + + if (ipid == ime) + { + struct stat sfile, sdescriptor; + + /* It looks like we have the lock. Do the final stat + check. */ + if (stat ((char *) zpath, &sfile) < 0) + { + if (errno != ENOENT) + { + zerr = "stat"; + break; + } + /* Loop around and try again. */ + } + else + { + if (fstat (o, &sdescriptor) < 0) + { + zerr = "fstat"; + break; + } + + if (sfile.st_ino == sdescriptor.st_ino + && sfile.st_dev == sdescriptor.st_dev) + { + /* Close the file before assuming we've succeeded to + pick up any trailing errors. */ + if (close (o) < 0) + { + zerr = "close"; + break; + } + + o = -1; + + /* We have the lock. */ + fret = TRUE; + break; + } + } + } + + /* Loop around and try the lock again. We keep doing this until + the lock file holds a pid that exists. */ + (void) close (o); + o = -1; + fret = TRUE; + } + + if (zerr != NULL) + { + ulog (LOG_ERROR, "%s (%s): %s", zerr, zpath, strerror (errno)); + if (pferr != NULL) + *pferr = TRUE; + } + + if (o >= 0) + (void) close (o); + + ubuffree (zfree); + + /* It would be nice if we could leave the temporary file around for + future calls, but considering that we create lock files in + various different directories it's probably more trouble than + it's worth. */ + if (remove (ztempfile) != 0) + ulog (LOG_ERROR, "remove (%s): %s", ztempfile, strerror (errno)); + + ubuffree (ztempfile); + + return fret; +} + +/* Unlock something. The fspooldir argument is as in fsdo_lock. */ + +boolean +fsdo_unlock (zlock, fspooldir) + const char *zlock; + boolean fspooldir; +{ + char *zfree; + const char *zpath; + + if (fspooldir) + { + zfree = NULL; + zpath = zlock; + } + else + { + zfree = zsysdep_in_dir (zSlockdir, zlock); + zpath = zfree; + } + + if (remove (zpath) == 0 + || errno == ENOENT) + { + ubuffree (zfree); + return TRUE; + } + else + { + ulog (LOG_ERROR, "remove (%s): %s", zpath, strerror (errno)); + ubuffree (zfree); + return FALSE; + } +} diff --git a/gnu/libexec/uucp/libunix/loctim.c b/gnu/libexec/uucp/libunix/loctim.c new file mode 100644 index 0000000000..da5f32e2d2 --- /dev/null +++ b/gnu/libexec/uucp/libunix/loctim.c @@ -0,0 +1,25 @@ +/* loctim.c + Turn a time epoch into a struct tm. This is trivial on Unix. */ + +#include "uucp.h" + +#if HAVE_TIME_H +#include +#endif + +#include "system.h" + +#ifndef localtime +extern struct tm *localtime (); +#endif + +void +usysdep_localtime (itime, q) + long itime; + struct tm *q; +{ + time_t i; + + i = (time_t) itime; + *q = *localtime (&i); +} diff --git a/gnu/libexec/uucp/libunix/mail.c b/gnu/libexec/uucp/libunix/mail.c new file mode 100644 index 0000000000..74c1aa95ad --- /dev/null +++ b/gnu/libexec/uucp/libunix/mail.c @@ -0,0 +1,85 @@ +/* mail.c + Send mail to a user. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +#if HAVE_TIME_H +#include +#endif + +#ifndef ctime +extern char *ctime (); +#endif + +/* Mail a message to a user. */ + +boolean +fsysdep_mail (zto, zsubject, cstrs, paz) + const char *zto; + const char *zsubject; + int cstrs; + const char **paz; +{ + const char *az[3]; + FILE *e; + pid_t ipid; + time_t itime; + int i; + + az[0] = MAIL_PROGRAM; + az[1] = zto; + az[2] = NULL; + + e = espopen (az, FALSE, &ipid); + if (e == NULL) + { + ulog (LOG_ERROR, "espopen (%s): %s", MAIL_PROGRAM, + strerror (errno)); + return FALSE; + } + + fprintf (e, "Subject: %s\n", zsubject); + fprintf (e, "To: %s\n", zto); + + fprintf (e, "\n"); + + (void) time (&itime); + /* Remember that ctime includes a \n, so this skips a line. */ + fprintf (e, "Message from UUCP on %s %s\n", zSlocalname, + ctime (&itime)); + + for (i = 0; i < cstrs; i++) + fputs (paz[i], e); + + (void) fclose (e); + + return ixswait ((unsigned long) ipid, MAIL_PROGRAM) == 0; +} diff --git a/gnu/libexec/uucp/libunix/mkdir.c b/gnu/libexec/uucp/libunix/mkdir.c new file mode 100644 index 0000000000..f59ad5dfd6 --- /dev/null +++ b/gnu/libexec/uucp/libunix/mkdir.c @@ -0,0 +1,58 @@ +/* mkdir.c + Create a directory. We must go through a subsidiary program to + force our real uid to be the uucp owner before invoking the setuid + /bin/mkdir program. */ + +#include "uucp.h" + +#include "sysdep.h" + +#include + +int +mkdir (zdir, imode) + const char *zdir; + int imode; +{ + struct stat s; + const char *azargs[3]; + int aidescs[3]; + pid_t ipid; + + /* Make sure the directory does not exist, since we will otherwise + get the wrong errno value. */ + if (stat (zdir, &s) == 0) + { + errno = EEXIST; + return -1; + } + + /* /bin/mkdir will create the directory with mode 777, so we set our + umask to get the mode we want. */ + (void) umask ((~ imode) & (S_IRWXU | S_IRWXG | S_IRWXO)); + + azargs[0] = UUDIR_PROGRAM; + azargs[1] = zdir; + azargs[2] = NULL; + aidescs[0] = SPAWN_NULL; + aidescs[1] = SPAWN_NULL; + aidescs[2] = SPAWN_NULL; + + ipid = ixsspawn (azargs, aidescs, FALSE, FALSE, (const char *) NULL, + TRUE, FALSE, (const char *) NULL, + (const char *) NULL, (const char *) NULL); + + (void) umask (0); + + if (ipid < 0) + return -1; + + if (ixswait ((unsigned long) ipid, (const char *) NULL) != 0) + { + /* Make up an errno value. */ + errno = EACCES; + return -1; + } + + return 0; +} diff --git a/gnu/libexec/uucp/libunix/mkdirs.c b/gnu/libexec/uucp/libunix/mkdirs.c new file mode 100644 index 0000000000..a4e0b67bb8 --- /dev/null +++ b/gnu/libexec/uucp/libunix/mkdirs.c @@ -0,0 +1,49 @@ +/* mkdirs.c + Create any directories needed for a file name. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +boolean +fsysdep_make_dirs (zfile, fpublic) + const char *zfile; + boolean fpublic; +{ + char *zcopy, *z; + int imode; + + zcopy = zbufcpy (zfile); + + if (fpublic) + imode = IPUBLIC_DIRECTORY_MODE; + else + imode = IDIRECTORY_MODE; + + for (z = zcopy; *z != '\0'; z++) + { + if (*z == '/' && z != zcopy) + { + *z = '\0'; + if (! fsysdep_directory (zcopy)) + { + if (mkdir (zcopy, imode) != 0) + { + ulog (LOG_ERROR, "mkdir (%s): %s", zcopy, + strerror (errno)); + ubuffree (zcopy); + return FALSE; + } + } + *z = '/'; + } + } + + ubuffree (zcopy); + + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/mode.c b/gnu/libexec/uucp/libunix/mode.c new file mode 100644 index 0000000000..53f74ec81c --- /dev/null +++ b/gnu/libexec/uucp/libunix/mode.c @@ -0,0 +1,33 @@ +/* mode.c + Get the Unix file mode of a file. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +unsigned int +ixsysdep_file_mode (zfile) + const char *zfile; +{ + struct stat s; + + if (stat ((char *) zfile, &s) != 0) + { + ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno)); + return 0; + } + +#if S_IRWXU != 0700 + #error Files modes need to be translated +#endif + + /* We can't return 0, since that indicate an error. */ + if ((s.st_mode & 0777) == 0) + return 0400; + + return s.st_mode & 0777; +} diff --git a/gnu/libexec/uucp/libunix/move.c b/gnu/libexec/uucp/libunix/move.c new file mode 100644 index 0000000000..ccfe6d4d72 --- /dev/null +++ b/gnu/libexec/uucp/libunix/move.c @@ -0,0 +1,176 @@ +/* move.c + Move a file. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +/* Move (rename) a file from one name to another. This routine will + optionally create necessary directories, and fpublic indicates + whether the new directories should be publically accessible or not. + If fcheck is true, it will try to determine whether the named user + has write access to the new file. */ + +boolean +fsysdep_move_file (zorig, zto, fmkdirs, fpublic, fcheck, zuser) + const char *zorig; + const char *zto; + boolean fmkdirs; + boolean fpublic; + boolean fcheck; + const char *zuser; +{ + struct stat s; + int o; + + DEBUG_MESSAGE2 (DEBUG_SPOOLDIR, + "fsysdep_move_file: Moving %s to %s", zorig, zto); + + /* Optionally make sure that zuser has write access on the + directory. */ + if (fcheck) + { + char *zcopy; + char *zslash; + + zcopy = zbufcpy (zto); + zslash = strrchr (zcopy, '/'); + if (zslash == zcopy) + zslash[1] = '\0'; + else + *zslash = '\0'; + + if (stat (zcopy, &s) != 0) + { + ulog (LOG_ERROR, "stat (%s): %s", zcopy, strerror (errno)); + (void) remove (zorig); + ubuffree (zcopy); + return FALSE; + } + if (! fsuser_access (&s, W_OK, zuser)) + { + ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES)); + (void) remove (zorig); + ubuffree (zcopy); + return FALSE; + } + ubuffree (zcopy); + + /* A malicious user now has a few milliseconds to change a + symbolic link to a directory uucp has write permission on but + the user does not (the obvious choice being /usr/lib/uucp). + The only certain method I can come up with to close this race + is to fork an suid process which takes on the users identity + and does the actual copy. This is sufficiently high overhead + that I'm not going to do it. */ + } + + /* We try to use rename to move the file. */ + + if (rename (zorig, zto) == 0) + return TRUE; + + if (fmkdirs && errno == ENOENT) + { + if (! fsysdep_make_dirs (zto, fpublic)) + { + (void) remove (zorig); + return FALSE; + } + if (rename (zorig, zto) == 0) + return TRUE; + } + +#if HAVE_RENAME + /* On some systems the system call rename seems to fail for + arbitrary reasons. To get around this, we always try to copy the + file by hand if the rename failed. */ + errno = EXDEV; +#endif + + /* If we can't link across devices, we must copy the file by hand. */ + if (errno != EXDEV) + { + ulog (LOG_ERROR, "rename (%s, %s): %s", zorig, zto, + strerror (errno)); + (void) remove (zorig); + return FALSE; + } + + /* Copy the file. */ + if (stat ((char *) zorig, &s) < 0) + { + ulog (LOG_ERROR, "stat (%s): %s", zorig, strerror (errno)); + (void) remove (zorig); + return FALSE; + } + + /* Make sure the file gets the right mode by creating it before we + call fcopy_file. */ + (void) remove (zto); + o = creat ((char *) zto, s.st_mode); + if (o < 0) + { + if (fmkdirs && errno == ENOENT) + { + if (! fsysdep_make_dirs (zto, fpublic)) + { + (void) remove (zorig); + return FALSE; + } + o = creat ((char *) zto, s.st_mode); + } + if (o < 0) + { + ulog (LOG_ERROR, "creat (%s): %s", zto, strerror (errno)); + (void) remove (zorig); + return FALSE; + } + } + (void) close (o); + + if (! fcopy_file (zorig, zto, fpublic, fmkdirs)) + { + (void) remove (zorig); + return FALSE; + } + + if (remove (zorig) != 0) + ulog (LOG_ERROR, "remove (%s): %s", zorig, strerror (errno)); + + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/opensr.c b/gnu/libexec/uucp/libunix/opensr.c new file mode 100644 index 0000000000..3a8ca7a8b8 --- /dev/null +++ b/gnu/libexec/uucp/libunix/opensr.c @@ -0,0 +1,244 @@ +/* opensr.c + Open files for sending and receiving. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" +#include "sysdep.h" + +#include + +#if HAVE_TIME_H +#include +#endif + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +#ifndef time +extern time_t time (); +#endif + +/* Open a file to send to another system, and return the mode and + the size. */ + +openfile_t +esysdep_open_send (qsys, zfile, fcheck, zuser) + const struct uuconf_system *qsys; + const char *zfile; + boolean fcheck; + const char *zuser; +{ + struct stat s; + openfile_t e; + int o; + + if (fsysdep_directory (zfile)) + { + ulog (LOG_ERROR, "%s: is a directory", zfile); + return EFILECLOSED; + } + +#if USE_STDIO + e = fopen (zfile, BINREAD); + if (e == NULL) + { + ulog (LOG_ERROR, "fopen (%s): %s", zfile, strerror (errno)); + return NULL; + } + o = fileno (e); +#else + e = open ((char *) zfile, O_RDONLY | O_NOCTTY, 0); + if (e == -1) + { + ulog (LOG_ERROR, "open (%s): %s", zfile, strerror (errno)); + return -1; + } + o = e; +#endif + + if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + (void) ffileclose (e); + return EFILECLOSED; + } + + if (fstat (o, &s) == -1) + { + ulog (LOG_ERROR, "fstat: %s", strerror (errno)); + s.st_mode = 0666; + } + + /* We have to recheck the file permission, although we probably + checked it already, because otherwise there would be a window in + which somebody could change the contents of a symbolic link to + point to some file which was only readable by uucp. */ + if (fcheck) + { + if (! fsuser_access (&s, R_OK, zuser)) + { + ulog (LOG_ERROR, "%s: %s", zfile, strerror (EACCES)); + (void) ffileclose (e); + return EFILECLOSED; + } + } + + return e; +} + +/* Get a temporary file name to receive into. We use the ztemp + argument to pick the file name, so that we relocate the file if the + transmission is aborted. */ + +char * +zsysdep_receive_temp (qsys, zto, ztemp) + const struct uuconf_system *qsys; + const char *zto; + const char *ztemp; +{ + if (ztemp != NULL + && *ztemp == 'D' + && strcmp (ztemp, "D.0") != 0) + return zsappend3 (".Temp", qsys->uuconf_zname, ztemp); + else + return zstemp_file (qsys); +} + +/* Open a temporary file to receive into. This should, perhaps, check + that we have write permission on the receiving directory, but it + doesn't. */ + +openfile_t +esysdep_open_receive (qsys, zto, ztemp, zreceive, pcrestart) + const struct uuconf_system *qsys; + const char *zto; + const char *ztemp; + const char *zreceive; + long *pcrestart; +{ + int o; + openfile_t e; + + /* If we used the ztemp argument in zsysdep_receive_temp, above, + then we will have a name consistent across conversations. In + that case, we may have already received some portion of this + file. */ + o = -1; + *pcrestart = -1; + if (ztemp != NULL + && *ztemp == 'D' + && strcmp (ztemp, "D.0") != 0) + { + o = open ((char *) zreceive, O_WRONLY); + if (o >= 0) + { + struct stat s; + + /* For safety, we insist on the file being less than 1 week + old. This can still catch people, unfortunately. I + don't know of any good solution to the problem of old + files hanging around. If anybody has a file they want + restarted, and they know about this issue, they can touch + it to bring it up to date. */ + if (fstat (o, &s) < 0 + || s.st_mtime + 7 * 24 * 60 * 60 < time ((time_t *) NULL)) + { + (void) close (o); + o = -1; + } + else + { + DEBUG_MESSAGE1 (DEBUG_SPOOLDIR, + "esysdep_open_receive: Reusing %s", + zreceive); + *pcrestart = (long) s.st_size; + } + } + } + + if (o < 0) + o = creat ((char *) zreceive, IPRIVATE_FILE_MODE); + + if (o < 0) + { + if (errno == ENOENT) + { + if (! fsysdep_make_dirs (zreceive, FALSE)) + return EFILECLOSED; + o = creat ((char *) zreceive, IPRIVATE_FILE_MODE); + } + if (o < 0) + { + ulog (LOG_ERROR, "creat (%s): %s", zreceive, strerror (errno)); + return EFILECLOSED; + } + } + + if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + (void) close (o); + (void) remove (zreceive); + return EFILECLOSED; + } + +#if USE_STDIO + e = fdopen (o, (char *) BINWRITE); + + if (e == NULL) + { + ulog (LOG_ERROR, "fdopen (%s): %s", zreceive, strerror (errno)); + (void) close (o); + (void) remove (zreceive); + return EFILECLOSED; + } +#else + e = o; +#endif + + return e; +} diff --git a/gnu/libexec/uucp/libunix/pause.c b/gnu/libexec/uucp/libunix/pause.c new file mode 100644 index 0000000000..e774e0897b --- /dev/null +++ b/gnu/libexec/uucp/libunix/pause.c @@ -0,0 +1,96 @@ +/* pause.c + Pause for half a second. */ + +#include "uucp.h" + +#include "sysdep.h" +#include "system.h" + +/* Pick a timing routine to use. I somewhat arbitrarily picked usleep + above nap above napms above poll above select. */ +#if HAVE_USLEEP || HAVE_NAP || HAVE_NAPMS || HAVE_POLL +#undef HAVE_SELECT +#define HAVE_SELECT 0 +#endif + +#if HAVE_USLEEP || HAVE_NAP || HAVE_NAPMS +#undef HAVE_POLL +#define HAVE_POLL 0 +#endif + +#if HAVE_USLEEP || HAVE_NAP +#undef HAVE_NAPMS +#define HAVE_NAPMS 0 +#endif + +#if HAVE_USLEEP +#undef HAVE_NAP +#define HAVE_NAP 0 +#endif + +#if HAVE_SELECT +#include +#if HAVE_SYS_SELECT_H +#include +#endif +#endif + +#if HAVE_POLL +#if HAVE_STROPTS_H +#include +#endif +#if HAVE_POLL_H +#include +#endif +#if ! HAVE_STROPTS_H && ! HAVE_POLL_H +/* We need a definition for struct pollfd, although it doesn't matter + what it contains. */ +struct pollfd +{ + int idummy; +}; +#endif /* ! HAVE_STROPTS_H && ! HAVE_POLL_H */ +#endif /* HAVE_POLL */ + +#if HAVE_TIME_H +#if HAVE_SYS_TIME_AND_TIME_H || ! USE_SELECT_TIMER +#include +#endif +#endif + +void +usysdep_pause () +{ +#if HAVE_NAPMS + napms (500); +#endif /* HAVE_NAPMS */ +#if HAVE_NAP +#if HAVE_HUNDREDTHS_NAP + nap (50L); +#else + nap (500L); +#endif /* ! HAVE_HUNDREDTHS_NAP */ +#endif /* HAVE_NAP */ +#if HAVE_USLEEP + usleep (500 * (long) 1000); +#endif /* HAVE_USLEEP */ +#if HAVE_POLL + struct pollfd sdummy; + + /* We need to pass an unused pollfd structure because poll checks + the address before checking the number of elements. */ + poll (&sdummy, 0, 500); +#endif /* HAVE_POLL */ +#if HAVE_SELECT + struct timeval s; + + s.tv_sec = 0; + s.tv_usec = 500 * (long) 1000; + select (0, (pointer) NULL, (pointer) NULL, (pointer) NULL, &s); +#endif /* USE_SELECT_TIMER */ +#if ! HAVE_NAPMS && ! HAVE_NAP && ! HAVE_USLEEP +#if ! USE_SELECT_TIMER && ! HAVE_POLL + sleep (1); +#endif /* ! USE_SELECT_TIMER && ! HAVE_POLL */ +#endif /* ! HAVE_NAPMS && ! HAVE_NAP && ! HAVE_USLEEP */ +} diff --git a/gnu/libexec/uucp/libunix/picksb.c b/gnu/libexec/uucp/libunix/picksb.c new file mode 100644 index 0000000000..3fe5c6f585 --- /dev/null +++ b/gnu/libexec/uucp/libunix/picksb.c @@ -0,0 +1,230 @@ +/* picksb.c + System dependent routines for uupick. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char picksb_rcsid[] = "$Id: picksb.c,v 1.1 1993/08/04 19:32:42 jtc Exp $"; +#endif + +#include "uudefs.h" +#include "system.h" +#include "sysdep.h" + +#include +#include + +#if HAVE_OPENDIR +#if HAVE_DIRENT_H +#include +#else /* ! HAVE_DIRENT_H */ +#include +#define dirent direct +#endif /* ! HAVE_DIRENT_H */ +#endif /* HAVE_OPENDIR */ + +#if GETPWUID_DECLARATION_OK +#ifndef getpwuid +extern struct passwd *getpwuid (); +#endif +#endif + +/* Local variables. */ + +/* Directory of ~/receive/USER. */ +static DIR *qStopdir; + +/* Name of ~/receive/USER. */ +static char *zStopdir; + +/* Directory of ~/receive/USER/SYSTEM. */ +static DIR *qSsysdir; + +/* Name of system. */ +static char *zSsysdir; + +/* Prepare to get a list of all the file to uupick for this user. */ + +/*ARGSUSED*/ +boolean +fsysdep_uupick_init (zsystem, zpubdir) + const char *zsystem; + const char *zpubdir; +{ + const char *zuser; + + zuser = zsysdep_login_name (); + + zStopdir = (char *) xmalloc (strlen (zpubdir) + + sizeof "/receive/" + + strlen (zuser)); + sprintf (zStopdir, "%s/receive/%s", zpubdir, zuser); + + qStopdir = opendir (zStopdir); + if (qStopdir == NULL && errno != ENOENT) + { + ulog (LOG_ERROR, "opendir (%s): %s", zStopdir, + strerror (errno)); + return FALSE; + } + + qSsysdir = NULL; + + return TRUE; +} + +/* Return the next file from the uupick directories. */ + +/*ARGSUSED*/ +char * +zsysdep_uupick (zsysarg, zpubdir, pzfrom, pzfull) + const char *zsysarg; + const char *zpubdir; + char **pzfrom; + char **pzfull; +{ + struct dirent *qentry; + + while (TRUE) + { + while (qSsysdir == NULL) + { + const char *zsystem; + char *zdir; + + if (qStopdir == NULL) + return NULL; + + if (zsysarg != NULL) + { + closedir (qStopdir); + qStopdir = NULL; + zsystem = zsysarg; + } + else + { + do + { + qentry = readdir (qStopdir); + if (qentry == NULL) + { + closedir (qStopdir); + qStopdir = NULL; + return NULL; + } + } + while (strcmp (qentry->d_name, ".") == 0 + || strcmp (qentry->d_name, "..") == 0); + + zsystem = qentry->d_name; + } + + zdir = zbufalc (strlen (zStopdir) + strlen (zsystem) + sizeof "/"); + sprintf (zdir, "%s/%s", zStopdir, zsystem); + + qSsysdir = opendir (zdir); + if (qSsysdir == NULL) + { + if (errno != ENOENT && errno != ENOTDIR) + ulog (LOG_ERROR, "opendir (%s): %s", zdir, strerror (errno)); + } + else + { + ubuffree (zSsysdir); + zSsysdir = zbufcpy (zsystem); + } + + ubuffree (zdir); + } + + qentry = readdir (qSsysdir); + if (qentry == NULL) + { + closedir (qSsysdir); + qSsysdir = NULL; + continue; + } + + if (strcmp (qentry->d_name, ".") == 0 + || strcmp (qentry->d_name, "..") == 0) + continue; + + *pzfrom = zbufcpy (zSsysdir); + *pzfull = zsappend3 (zStopdir, zSsysdir, qentry->d_name); + return zbufcpy (qentry->d_name); + } +} + +/*ARGSUSED*/ +boolean +fsysdep_uupick_free (zsystem, zpubdir) + const char *zsystem; + const char *zpubdir; +{ + xfree ((pointer) zStopdir); + if (qStopdir != NULL) + { + closedir (qStopdir); + qStopdir = NULL; + } + ubuffree (zSsysdir); + zSsysdir = NULL; + if (qSsysdir != NULL) + { + closedir (qSsysdir); + qSsysdir = NULL; + } + + return TRUE; +} + +/* Expand a local file name for uupick. */ + +char * +zsysdep_uupick_local_file (zfile) + const char *zfile; +{ + struct passwd *q; + + /* If this does not start with a simple ~, pass it to + zsysdep_local_file_cwd; as it happens, zsysdep_local_file_cwd + only uses the zpubdir argument if the file starts with a simple + ~, so it doesn't really matter what we pass for zpubdir. */ + if (zfile[0] != '~' + || (zfile[1] != '/' && zfile[1] != '\0')) + return zsysdep_local_file_cwd (zfile, (const char *) NULL); + + q = getpwuid (getuid ()); + if (q == NULL) + { + ulog (LOG_ERROR, "Can't get home directory"); + return NULL; + } + + if (zfile[1] == '\0') + return zbufcpy (q->pw_dir); + + return zsysdep_in_dir (q->pw_dir, zfile + 2); +} diff --git a/gnu/libexec/uucp/libunix/portnm.c b/gnu/libexec/uucp/libunix/portnm.c new file mode 100644 index 0000000000..9eda4ab012 --- /dev/null +++ b/gnu/libexec/uucp/libunix/portnm.c @@ -0,0 +1,51 @@ +/* portnm.c + Get the port name of stdin. */ + +#include "uucp.h" + +#include "sysdep.h" +#include "system.h" + +#if HAVE_TCP +#if HAVE_SYS_TYPES_TCP_H +#include +#endif +#include +#endif + +#ifndef ttyname +extern char *ttyname (); +#endif + +/* Get the port name of standard input. I assume that Unix systems + generally support ttyname. If they don't, this function can just + return NULL. It uses getsockname to see whether standard input is + a TCP connection. */ + +const char * +zsysdep_port_name (ftcp_port) + boolean *ftcp_port; +{ + const char *z; + + *ftcp_port = FALSE; + +#if HAVE_TCP + { + size_t clen; + struct sockaddr s; + + clen = sizeof (struct sockaddr); + if (getsockname (0, &s, &clen) == 0) + *ftcp_port = TRUE; + } +#endif /* HAVE_TCP */ + + z = ttyname (0); + if (z == NULL) + return NULL; + if (strncmp (z, "/dev/", sizeof "/dev/" - 1) == 0) + return z + sizeof "/dev/" - 1; + else + return z; +} diff --git a/gnu/libexec/uucp/libunix/proctm.c b/gnu/libexec/uucp/libunix/proctm.c new file mode 100644 index 0000000000..55cf96f0c9 --- /dev/null +++ b/gnu/libexec/uucp/libunix/proctm.c @@ -0,0 +1,197 @@ +/* proctm.c + Get the time spent in the process. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "sysdep.h" +#include "system.h" + +#if HAVE_SYS_PARAM_H +#include +#endif + +#if HAVE_LIMITS_H +#include +#endif + +/* Prefer gettimeofday to ftime to times. */ + +#if HAVE_GETTIMEOFDAY || HAVE_FTIME +#undef HAVE_TIMES +#define HAVE_TIMES 0 +#endif + +#if HAVE_GETTIMEOFDAY +#undef HAVE_FTIME +#define HAVE_FTIME 0 +#endif + +#if HAVE_TIME_H && (HAVE_SYS_TIME_AND_TIME_H || ! HAVE_GETTIMEOFDAY) +#include +#endif + +#if HAVE_GETTIMEOFDAY +#include +#endif + +#if HAVE_FTIME +#include +#endif + +#if HAVE_TIMES + +#if HAVE_SYS_TIMES_H +#include +#endif + +#if TIMES_DECLARATION_OK +/* We use a macro to protect this because times really returns clock_t + and on some systems, such as Ultrix 4.0, clock_t is int. We don't + leave it out entirely because on some systems, such as System III, + the declaration is necessary for correct compilation. */ +#ifndef times +extern long times (); +#endif +#endif /* TIMES_DECLARATION_OK */ + +#ifdef _SC_CLK_TCK +#define HAVE_SC_CLK_TCK 1 +#else +#define HAVE_SC_CLK_TCK 0 +#endif + +/* TIMES_TICK may have been set in policy.h, or we may be able to get + it using sysconf. If neither is the case, try to find a useful + definition from the system header files. */ +#if TIMES_TICK == 0 && (! HAVE_SYSCONF || ! HAVE_SC_CLK_TCK) +#ifdef CLK_TCK +#undef TIMES_TICK +#define TIMES_TICK CLK_TCK +#else /* ! defined (CLK_TCK) */ +#ifdef HZ +#undef TIMES_TICK +#define TIMES_TICK HZ +#endif /* defined (HZ) */ +#endif /* ! defined (CLK_TCK) */ +#endif /* TIMES_TICK == 0 && (! HAVE_SYSCONF || ! HAVE_SC_CLK_TCK) */ + +#endif /* HAVE_TIMES */ + +#ifndef time +extern time_t time (); +#endif +#if HAVE_SYSCONF +#ifndef sysconf +extern long sysconf (); +#endif +#endif + +/* Get the time in seconds and microseconds; this need only work + within the process when called from the system independent code. + It is also called by ixsysdep_time. */ + +long +ixsysdep_process_time (pimicros) + long *pimicros; +{ +#if HAVE_GETTIMEOFDAY + struct timeval stime; + struct timezone stz; + + (void) gettimeofday (&stime, &stz); + if (pimicros != NULL) + *pimicros = (long) stime.tv_usec; + return (long) stime.tv_sec; +#endif /* HAVE_GETTIMEOFDAY */ + +#if HAVE_FTIME + static boolean fbad; + + if (! fbad) + { + struct timeb stime; + static struct timeb slast; + + (void) ftime (&stime); + + /* On some systems, such as SCO 3.2.2, ftime can go backwards in + time. If we detect this, we switch to using time. */ + if (slast.time != 0 + && (stime.time < slast.time + || (stime.time == slast.time && + stime.millitm < slast.millitm))) + fbad = TRUE; + else + { + slast = stime; + if (pimicros != NULL) + *pimicros = (long) stime.millitm * (long) 1000; + return (long) stime.time; + } + } + + if (pimicros != NULL) + *pimicros = 0; + return (long) time ((time_t *) NULL); +#endif /* HAVE_FTIME */ + +#if HAVE_TIMES + struct tms s; + long i; + static int itick; + + if (itick == 0) + { +#if TIMES_TICK == 0 +#if HAVE_SYSCONF && HAVE_SC_CLK_TCK + itick = (int) sysconf (_SC_CLK_TCK); +#else /* ! HAVE_SYSCONF || ! HAVE_SC_CLK_TCK */ + const char *z; + + z = getenv ("HZ"); + if (z != NULL) + itick = (int) strtol (z, (char **) NULL, 10); + + /* If we really couldn't get anything, just use 60. */ + if (itick == 0) + itick = 60; +#endif /* ! HAVE_SYSCONF || ! HAVE_SC_CLK_TCK */ +#else /* TIMES_TICK != 0 */ + itick = TIMES_TICK; +#endif /* TIMES_TICK == 0 */ + } + + i = (long) times (&s); + if (pimicros != NULL) + *pimicros = (i % (long) itick) * ((long) 1000000 / (long) itick); + return i / (long) itick; +#endif /* HAVE_TIMES */ + +#if ! HAVE_GETTIMEOFDAY && ! HAVE_FTIME && ! HAVE_TIMES + if (pimicros != NULL) + *pimicros = 0; + return (long) time ((time_t *) NULL); +#endif /* ! HAVE_GETTIMEOFDAY && ! HAVE_FTIME && ! HAVE_TIMES */ +} diff --git a/gnu/libexec/uucp/libunix/recep.c b/gnu/libexec/uucp/libunix/recep.c new file mode 100644 index 0000000000..84a211a7a9 --- /dev/null +++ b/gnu/libexec/uucp/libunix/recep.c @@ -0,0 +1,197 @@ +/* recep.c + See whether a file has already been received. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" + +#include + +#if HAVE_TIME_H +#include +#endif + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +static char *zsreceived_name P((const struct uuconf_system *qsys, + const char *ztemp)); + +/* These routines are used to see whether we have already received a + file in a previous UUCP connection. It is possible for the + acknowledgement of a received file to be lost. The sending system + will then now know that the file was correctly received, and will + send it again. This can be a problem particularly with protocols + which support channels, since they may send several small files in + a single window, all of which may be received correctly although + the sending system never sees the acknowledgement. If these files + involve an execution, the execution will happen twice, which will + be bad. + + We use a simple system. For each file we want to remember, we + create an empty file names .Received/SYS/TEMP, where SYS is the + name of the system and TEMP is the name of the temporary file used + by the sender. If no temporary file is used by the sender, we + don't remember that we received the file. This is not perfect, but + execution files will always have a temporary file, so the most + important case is handled. Also, any file received from Taylor + UUCP 1.04 or greater will always have a temporary file. */ + +/* Return the name we are going use for the marker, or NULL if we have + no name. */ + +static char * +zsreceived_name (qsys, ztemp) + const struct uuconf_system *qsys; + const char *ztemp; +{ + if (ztemp != NULL + && *ztemp == 'D' + && strcmp (ztemp, "D.0") != 0) + return zsappend3 (".Received", qsys->uuconf_zname, ztemp); + else + return NULL; +} + +/* Remember that we have already received a file. */ + +/*ARGSUSED*/ +boolean +fsysdep_remember_reception (qsys, zto, ztemp) + const struct uuconf_system *qsys; + const char *zto; + const char *ztemp; +{ + char *zfile; + int o; + + zfile = zsreceived_name (qsys, ztemp); + if (zfile == NULL) + return TRUE; + o = creat (zfile, IPUBLIC_FILE_MODE); + if (o < 0) + { + if (errno == ENOENT) + { + if (fsysdep_make_dirs (zfile, TRUE)) + { + ubuffree (zfile); + return FALSE; + } + o = creat (zfile, IPUBLIC_FILE_MODE); + } + if (o < 0) + { + ulog (LOG_ERROR, "creat (%s): %s", zfile, strerror (errno)); + ubuffree (zfile); + return FALSE; + } + } + + ubuffree (zfile); + + /* We don't have to actually put anything in the file; we just use + the name. This is more convenient than keeping a file with a + list of names. */ + if (close (o) < 0) + { + ulog (LOG_ERROR, "fsysdep_remember_reception: close: %s", + strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/* See if we have already received a file. Note that don't delete the + marker file here, because we need to know that the sending system + has received our denial first. This function returns TRUE if the + file has already been received, FALSE if it has not. */ + +/*ARGSUSED*/ +boolean +fsysdep_already_received (qsys, zto, ztemp) + const struct uuconf_system *qsys; + const char *zto; + const char *ztemp; +{ + char *zfile; + struct stat s; + boolean fret; + + zfile = zsreceived_name (qsys, ztemp); + if (zfile == NULL) + return FALSE; + if (stat (zfile, &s) < 0) + { + if (errno != ENOENT) + ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno)); + ubuffree (zfile); + return FALSE; + } + + /* Ignore the file (return FALSE) if it is over one week old. */ + fret = s.st_mtime + 7 * 24 * 60 * 60 >= time ((time_t *) NULL); + + if (fret) + DEBUG_MESSAGE1 (DEBUG_SPOOLDIR, "fsysdep_already_received: Found %s", + zfile); + + ubuffree (zfile); + + return fret; +} + +/* Forget that we have received a file. */ + +/*ARGSUSED*/ +boolean +fsysdep_forget_reception (qsys, zto, ztemp) + const struct uuconf_system *qsys; + const char *zto; + const char *ztemp; +{ + char *zfile; + + zfile = zsreceived_name (qsys, ztemp); + if (zfile == NULL) + return TRUE; + if (remove (zfile) < 0 + && errno != ENOENT) + { + ulog (LOG_ERROR, "remove (%s): %s", zfile, strerror (errno)); + ubuffree (zfile); + return FALSE; + } + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/remove.c b/gnu/libexec/uucp/libunix/remove.c new file mode 100644 index 0000000000..b695888ffa --- /dev/null +++ b/gnu/libexec/uucp/libunix/remove.c @@ -0,0 +1,13 @@ +/* remove.c + Remove a file (Unix specific implementation). */ + +#include "uucp.h" + +#include "sysdep.h" + +int +remove (z) + const char *z; +{ + return unlink (z); +} diff --git a/gnu/libexec/uucp/libunix/rename.c b/gnu/libexec/uucp/libunix/rename.c new file mode 100644 index 0000000000..0947ef5cfa --- /dev/null +++ b/gnu/libexec/uucp/libunix/rename.c @@ -0,0 +1,27 @@ +/* rename.c + Rename a file to a new name (Unix specific implementation). */ + +#include "uucp.h" + +#include "sysdep.h" + +#include + +/* This implementation will not work on directories, but fortunately + we never want to rename directories. */ + +int +rename (zfrom, zto) + const char *zfrom; + const char *zto; +{ + if (link (zfrom, zto) < 0) + { + if (errno != EEXIST) + return -1; + if (unlink (zto) < 0 + || link (zfrom, zto) < 0) + return -1; + } + return unlink (zfrom); +} diff --git a/gnu/libexec/uucp/libunix/rmdir.c b/gnu/libexec/uucp/libunix/rmdir.c new file mode 100644 index 0000000000..12a7b9e450 --- /dev/null +++ b/gnu/libexec/uucp/libunix/rmdir.c @@ -0,0 +1,43 @@ +/* rmdir.c + Remove a directory on a system which doesn't have the rmdir system + call. This is only called by uupick, which is not setuid, so we + don't have to worry about the problems of invoking the setuid + /bin/rmdir program. */ + +#include "uucp.h" + +#include "sysdep.h" + +#include + +int +rmdir (zdir) + const char *zdir; +{ + const char *azargs[3]; + int aidescs[3]; + pid_t ipid; + + azargs[0] = RMDIR_PROGRAM; + azargs[1] = zdir; + azargs[2] = NULL; + aidescs[0] = SPAWN_NULL; + aidescs[1] = SPAWN_NULL; + aidescs[2] = SPAWN_NULL; + + ipid = ixsspawn (azargs, aidescs, TRUE, FALSE, (const char *) NULL, + TRUE, TRUE, (const char *) NULL, + (const char *) NULL, (const char *) NULL); + + if (ipid < 0) + return -1; + + if (ixswait ((unsigned long) ipid, (const char *) NULL) != 0) + { + /* Make up an errno value. */ + errno = EBUSY; + return -1; + } + + return 0; +} diff --git a/gnu/libexec/uucp/libunix/run.c b/gnu/libexec/uucp/libunix/run.c new file mode 100644 index 0000000000..e219196283 --- /dev/null +++ b/gnu/libexec/uucp/libunix/run.c @@ -0,0 +1,75 @@ +/* run.c + Run a program. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +/* Start up a new program and end the current one. We don't have to + worry about SIGHUP because the current process is either not a + process group leader (uucp, uux) or it does not have a controlling + terminal (uucico). */ + +boolean +fsysdep_run (zprogram, zarg1, zarg2) + const char *zprogram; + const char *zarg1; + const char *zarg2; +{ + char *zlib; + const char *azargs[4]; + int aidescs[3]; + pid_t ipid; + + zlib = zbufalc (sizeof SBINDIR + sizeof "/" + strlen (zprogram)); + sprintf (zlib, "%s/%s", SBINDIR, zprogram); + + azargs[0] = zlib; + azargs[1] = zarg1; + azargs[2] = zarg2; + azargs[3] = NULL; + + aidescs[0] = SPAWN_NULL; + aidescs[1] = SPAWN_NULL; + aidescs[2] = SPAWN_NULL; + + /* We pass fsetuid and fshell as TRUE, which permits uucico and + uuxqt to be replaced by (non-setuid) shell scripts. */ + ipid = ixsspawn (azargs, aidescs, TRUE, FALSE, (const char *) NULL, + FALSE, TRUE, (const char *) NULL, + (const char *) NULL, (const char *) NULL); + ubuffree (zlib); + if (ipid < 0) + { + ulog (LOG_ERROR, "ixsspawn: %s", strerror (errno)); + return FALSE; + } + + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/seq.c b/gnu/libexec/uucp/libunix/seq.c new file mode 100644 index 0000000000..2e01233022 --- /dev/null +++ b/gnu/libexec/uucp/libunix/seq.c @@ -0,0 +1,126 @@ +/* seq.c + Get and increment the conversation sequence number for a system. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" + +#include + +/* Get the current conversation sequence number for a remote system, + and increment it for next time. The conversation sequence number + is kept in a file named for the system in the directory .Sequence + in the spool directory. This is not compatible with other versions + of UUCP, but it makes more sense to me. The sequence file is only + used if specified in the information for that system. */ + +long +ixsysdep_get_sequence (qsys) + const struct uuconf_system *qsys; +{ + FILE *e; + char *zname; + struct stat s; + long iseq; + + /* This will only be called when the system is locked anyhow, so there + is no need to use a separate lock for the conversation sequence + file. */ + zname = zsysdep_in_dir (".Sequence", qsys->uuconf_zname); + + iseq = 0; + if (stat (zname, &s) == 0) + { + boolean fok; + char *zline; + size_t cline; + + /* The file should only be readable and writable by uucp. */ + if ((s.st_mode & (S_IRWXG | S_IRWXO)) != 0) + { + ulog (LOG_ERROR, + "Bad file protection for conversation sequence file"); + ubuffree (zname); + return -1; + } + + e = fopen (zname, "r+"); + if (e == NULL) + { + ulog (LOG_ERROR, "fopen (%s): %s", zname, strerror (errno)); + ubuffree (zname); + return -1; + } + + ubuffree (zname); + + fok = TRUE; + zline = NULL; + cline = 0; + if (getline (&zline, &cline, e) <= 0) + fok = FALSE; + else + { + char *zend; + + iseq = strtol (zline, &zend, 10); + if (zend == zline) + fok = FALSE; + } + + xfree ((pointer) zline); + + if (! fok) + { + ulog (LOG_ERROR, "Bad format for conversation sequence file"); + (void) fclose (e); + return -1; + } + + rewind (e); + } + else + { + e = esysdep_fopen (zname, FALSE, FALSE, TRUE); + ubuffree (zname); + if (e == NULL) + return -1; + } + + ++iseq; + + fprintf (e, "%ld", iseq); + + if (fclose (e) != 0) + { + ulog (LOG_ERROR, "fclose: %s", strerror (errno)); + return -1; + } + + return iseq; +} diff --git a/gnu/libexec/uucp/libunix/serial.c b/gnu/libexec/uucp/libunix/serial.c new file mode 100644 index 0000000000..2b02cd5aaa --- /dev/null +++ b/gnu/libexec/uucp/libunix/serial.c @@ -0,0 +1,2977 @@ +/* serial.c + The serial port communication routines for Unix. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char serial_rcsid[] = "$Id: serial.c,v 1.1 1993/08/04 19:32:52 jtc Exp $"; +#endif + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" +#include "conn.h" +#include "sysdep.h" + +#include +#include + +#if HAVE_SYS_PARAM_H +#include +#endif + +#if HAVE_LIMITS_H +#include +#endif + +#if HAVE_TLI +#if HAVE_TIUSER_H +#include +#else /* ! HAVE_TIUSER_H */ +#if HAVE_XTI_H +#include +#endif /* HAVE_XTI_H */ +#endif /* ! HAVE_TIUSER_H */ +#endif /* HAVE_TLI */ + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +#if HAVE_SYS_IOCTL_H +#include +#endif + +#if HAVE_BSD_TTY +#include +#if HAVE_SYS_SELECT_H +#include +#endif +#endif + +#if HAVE_TIME_H +#if HAVE_SYS_TIME_AND_TIME_H || ! HAVE_BSD_TTY +#include +#endif +#endif + +#if HAVE_STRIP_BUG && HAVE_BSD_TTY +#include +#endif + +#if HAVE_SVR4_LOCKFILES +/* Get the right definitions for major and minor. */ +#if MAJOR_IN_MKDEV +#include +#endif /* MAJOR_IN_MKDEV */ +#if MAJOR_IN_SYSMACROS +#include +#endif /* MAJOR_IN_SYSMACROS */ +#if ! MAJOR_IN_MKDEV && ! MAJOR_IN_SYSMACROS +#ifndef major +#define major(i) (((i) >> 8) & 0xff) +#endif +#ifndef minor +#define minor(i) ((i) & 0xff) +#endif +#endif /* ! MAJOR_IN_MKDEV && ! MAJOR_IN_SYSMACROS */ +#endif /* HAVE_SVR4_LOCKFILES */ + +/* Get definitions for both O_NONBLOCK and O_NDELAY. */ +#ifndef O_NDELAY +#ifdef FNDELAY +#define O_NDELAY FNDELAY +#else /* ! defined (FNDELAY) */ +#define O_NDELAY 0 +#endif /* ! defined (FNDELAY) */ +#endif /* ! defined (O_NDELAY) */ + +#ifndef O_NONBLOCK +#ifdef FNBLOCK +#define O_NONBLOCK FNBLOCK +#else /* ! defined (FNBLOCK) */ +#define O_NONBLOCK 0 +#endif /* ! defined (FNBLOCK) */ +#endif /* ! defined (O_NONBLOCK) */ + +#if O_NDELAY == 0 && O_NONBLOCK == 0 + #error No way to do nonblocking I/O +#endif + +/* Get definitions for EAGAIN, EWOULDBLOCK and ENODATA. */ +#ifndef EAGAIN +#ifndef EWOULDBLOCK +#define EAGAIN (-1) +#define EWOULDBLOCK (-1) +#else /* defined (EWOULDBLOCK) */ +#define EAGAIN EWOULDBLOCK +#endif /* defined (EWOULDBLOCK) */ +#else /* defined (EAGAIN) */ +#ifndef EWOULDBLOCK +#define EWOULDBLOCK EAGAIN +#endif /* ! defined (EWOULDBLOCK) */ +#endif /* defined (EAGAIN) */ + +#ifndef ENODATA +#define ENODATA EAGAIN +#endif + +/* Make sure we have a definition for MAX_INPUT. */ +#ifndef MAX_INPUT +#define MAX_INPUT (256) +#endif + +/* If we have the TIOCSINUSE ioctl call, we use it to lock a terminal. + Otherwise, if we have the TIOCEXCL ioctl call, we have to open the + terminal before we know that it is unlocked. */ +#ifdef TIOCSINUSE +#define HAVE_TIOCSINUSE 1 +#else +#ifdef TIOCEXCL +#define HAVE_TIOCEXCL 1 +#endif +#endif + +#if HAVE_TLI +extern int t_errno; +extern char *t_errlist[]; +extern int t_nerr; +#endif + +/* Determine bits to clear for the various terminal control fields for + HAVE_SYSV_TERMIO and HAVE_POSIX_TERMIOS. */ +#if HAVE_SYSV_TERMIO +#define ICLEAR_IFLAG (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK \ + | ISTRIP | INLCR | IGNCR | ICRNL | IUCLC \ + | IXON | IXANY | IXOFF) +#define ICLEAR_OFLAG (OPOST | OLCUC | ONLCR | OCRNL | ONOCR | ONLRET \ + | OFILL | OFDEL | NLDLY | CRDLY | TABDLY | BSDLY \ + | VTDLY | FFDLY) +#define ICLEAR_CFLAG (CBAUD | CLOCAL | CSIZE | PARENB | PARODD) +#define ISET_CFLAG (CS8 | CREAD | HUPCL) +#define ICLEAR_LFLAG (ISIG | ICANON | XCASE | ECHO | ECHOE | ECHOK \ + | ECHONL | NOFLSH) +#endif +#if HAVE_POSIX_TERMIOS +#define ICLEAR_IFLAG (BRKINT | ICRNL | IGNBRK | IGNCR | IGNPAR \ + | INLCR | INPCK | ISTRIP | IXOFF | IXON \ + | PARMRK) +#define ICLEAR_OFLAG (OPOST) +#define ICLEAR_CFLAG (CLOCAL | CSIZE | PARENB | PARODD) +#define ISET_CFLAG (CS8 | CREAD | HUPCL) +#define ICLEAR_LFLAG (ECHO | ECHOE | ECHOK | ECHONL | ICANON | IEXTEN \ + | ISIG | NOFLSH | TOSTOP) +#endif + +/* Local functions. */ + +static RETSIGTYPE usalarm P((int isig)); +static boolean fsserial_init P((struct sconnection *qconn, + const struct sconncmds *qcmds, + const char *zdevice)); +static void usserial_free P((struct sconnection *qconn)); +static boolean fsserial_lockfile P((boolean flok, + const struct sconnection *)); +static boolean fsserial_lock P((struct sconnection *qconn, + boolean fin)); +static boolean fsserial_unlock P((struct sconnection *qconn)); +static boolean fsserial_open P((struct sconnection *qconn, long ibaud, + boolean fwait)); +static boolean fsstdin_open P((struct sconnection *qconn, long ibaud, + boolean fwait)); +static boolean fsmodem_open P((struct sconnection *qconn, long ibaud, + boolean fwait)); +static boolean fsdirect_open P((struct sconnection *qconn, long ibaud, + boolean fwait)); +static boolean fsblock P((struct ssysdep_conn *q, boolean fblock)); +static boolean fsserial_close P((struct ssysdep_conn *q)); +static boolean fsstdin_close P((struct sconnection *qconn, + pointer puuconf, + struct uuconf_dialer *qdialer, + boolean fsuccess)); +static boolean fsmodem_close P((struct sconnection *qconn, + pointer puuconf, + struct uuconf_dialer *qdialer, + boolean fsuccess)); +static boolean fsdirect_close P((struct sconnection *qconn, + pointer puuconf, + struct uuconf_dialer *qdialer, + boolean fsuccess)); +static boolean fsserial_reset P((struct sconnection *qconn)); +static boolean fsstdin_reset P((struct sconnection *qconn)); +static boolean fsstdin_read P((struct sconnection *qconn, + char *zbuf, size_t *pclen, size_t cmin, + int ctimeout, boolean freport)); +static boolean fsstdin_write P((struct sconnection *qconn, + const char *zwrite, size_t cwrite)); +static boolean fsserial_break P((struct sconnection *qconn)); +static boolean fsstdin_break P((struct sconnection *qconn)); +static boolean fsserial_set P((struct sconnection *qconn, + enum tparitysetting tparity, + enum tstripsetting tstrip, + enum txonxoffsetting txonxoff)); +static boolean fsstdin_set P((struct sconnection *qconn, + enum tparitysetting tparity, + enum tstripsetting tstrip, + enum txonxoffsetting txonxoff)); +static boolean fsmodem_carrier P((struct sconnection *qconn, + boolean fcarrier)); +static boolean fsrun_chat P((int oread, int owrite, char **pzprog)); +static boolean fsstdin_chat P((struct sconnection *qconn, + char **pzprog)); +static long isserial_baud P((struct sconnection *qconn)); + +/* The command table for standard input ports. */ + +static const struct sconncmds sstdincmds = +{ + usserial_free, + NULL, /* pflock */ + NULL, /* pfunlock */ + fsstdin_open, + fsstdin_close, + fsstdin_reset, + NULL, /* pfdial */ + fsstdin_read, + fsstdin_write, + fsysdep_conn_io, + fsstdin_break, + fsstdin_set, + NULL, /* pfcarrier */ + fsstdin_chat, + isserial_baud +}; + +/* The command table for modem ports. */ + +static const struct sconncmds smodemcmds = +{ + usserial_free, + fsserial_lock, + fsserial_unlock, + fsmodem_open, + fsmodem_close, + fsserial_reset, + fmodem_dial, + fsysdep_conn_read, + fsysdep_conn_write, + fsysdep_conn_io, + fsserial_break, + fsserial_set, + fsmodem_carrier, + fsysdep_conn_chat, + isserial_baud +}; + +/* The command table for direct ports. */ + +static const struct sconncmds sdirectcmds = +{ + usserial_free, + fsserial_lock, + fsserial_unlock, + fsdirect_open, + fsdirect_close, + fsserial_reset, + NULL, /* pfdial */ + fsysdep_conn_read, + fsysdep_conn_write, + fsysdep_conn_io, + fsserial_break, + fsserial_set, + NULL, /* pfcarrier */ + fsysdep_conn_chat, + isserial_baud +}; + +/* If the system will let us set both O_NDELAY and O_NONBLOCK, we do + so. This is because some ancient drivers on some systems appear to + look for one but not the other. Some other systems will give an + EINVAL error if we attempt to set both, so we use a static global + to hold the value we want to set. If we get EINVAL, we change the + global and try again (if some system gives an error other than + EINVAL, the code will have to be modified). */ +static int iSunblock = O_NDELAY | O_NONBLOCK; + +/* This code handles SIGALRM. See the discussion above + fsysdep_conn_read. Normally we ignore SIGALRM, but the handler + will temporarily be set to this function, which should set fSalarm + and then either longjmp or schedule another SIGALRM. fSalarm is + never referred to outside of this file, but we don't make it static + to try to fool compilers which don't understand volatile. */ + +volatile sig_atomic_t fSalarm; + +static RETSIGTYPE +usalarm (isig) + int isig; +{ +#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET + (void) signal (isig, usalarm); +#endif + + fSalarm = TRUE; + +#if HAVE_RESTARTABLE_SYSCALLS + longjmp (sSjmp_buf, 1); +#else + alarm (1); +#endif +} + +/* We need a simple routine to block SIGINT, SIGQUIT, SIGTERM and + SIGPIPE and another to restore the original state. When these + functions are called (in fsysdep_modem_close) SIGHUP is being + ignored. The routines are isblocksigs, which returns a value of + type HELD_SIG_MASK and usunblocksigs which takes a single argument + of type HELD_SIG_MASK. */ + +#if HAVE_SIGPROCMASK + +/* Use the POSIX sigprocmask call. */ + +#define HELD_SIG_MASK sigset_t + +static sigset_t isblocksigs P((void)); + +static sigset_t +isblocksigs () +{ + sigset_t sblock, sold; + + /* These expressions need an extra set of parentheses to avoid a bug + in SCO 3.2.2. */ + (void) (sigemptyset (&sblock)); + (void) (sigaddset (&sblock, SIGINT)); + (void) (sigaddset (&sblock, SIGQUIT)); + (void) (sigaddset (&sblock, SIGTERM)); + (void) (sigaddset (&sblock, SIGPIPE)); + + (void) sigprocmask (SIG_BLOCK, &sblock, &sold); + return sold; +} + +#define usunblocksigs(s) \ + ((void) sigprocmask (SIG_SETMASK, &(s), (sigset_t *) NULL)) + +#else /* ! HAVE_SIGPROCMASK */ +#if HAVE_SIGBLOCK + +/* Use the BSD sigblock and sigsetmask calls. */ + +#define HELD_SIG_MASK int + +#ifndef sigmask +#define sigmask(i) (1 << ((i) - 1)) +#endif + +#define isblocksigs() \ + sigblock (sigmask (SIGINT) | sigmask (SIGQUIT) \ + | sigmask (SIGTERM) | sigmask (SIGPIPE)) + +#define usunblocksigs(i) ((void) sigsetmask (i)) + +#else /* ! HAVE_SIGBLOCK */ + +#if HAVE_SIGHOLD + +/* Use the SVR3 sighold and sigrelse calls. */ + +#define HELD_SIG_MASK int + +static int isblocksigs P((void)); + +static int +isblocksigs () +{ + sighold (SIGINT); + sighold (SIGQUIT); + sighold (SIGTERM); + sighold (SIGPIPE); + return 0; +} + +static void usunblocksigs P((int)); + +/*ARGSUSED*/ +static void +usunblocksigs (i) + int i; +{ + sigrelse (SIGINT); + sigrelse (SIGQUIT); + sigrelse (SIGTERM); + sigrelse (SIGPIPE); +} + +#else /* ! HAVE_SIGHOLD */ + +/* We have no way to block signals. This system will suffer from a + race condition in fsysdep_modem_close. */ + +#define HELD_SIG_MASK int + +#define isblocksigs() 0 + +#define usunblocksigs(i) + +#endif /* ! HAVE_SIGHOLD */ +#endif /* ! HAVE_SIGBLOCK */ +#endif /* ! HAVE_SIGPROCMASK */ + +/* Initialize a connection for use on a serial port. */ + +static boolean +fsserial_init (qconn, qcmds, zdevice) + struct sconnection *qconn; + const struct sconncmds *qcmds; + const char *zdevice; +{ + struct ssysdep_conn *q; + + q = (struct ssysdep_conn *) xmalloc (sizeof (struct ssysdep_conn)); + if (zdevice == NULL + && qconn->qport != NULL + && qconn->qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN) + zdevice = qconn->qport->uuconf_zname; + if (zdevice == NULL) + q->zdevice = NULL; + else if (*zdevice == '/') + q->zdevice = zbufcpy (zdevice); + else + { + size_t clen; + + clen = strlen (zdevice); + q->zdevice = zbufalc (sizeof "/dev/" + clen); + memcpy (q->zdevice, "/dev/", sizeof "/dev/" - 1); + memcpy (q->zdevice + sizeof "/dev/" - 1, zdevice, clen); + q->zdevice[sizeof "/dev/" + clen - 1] = '\0'; + } + q->o = -1; + q->ftli = FALSE; + qconn->psysdep = (pointer) q; + qconn->qcmds = qcmds; + return TRUE; +} + +/* Initialize a connection for use on standard input. */ + +boolean +fsysdep_stdin_init (qconn) + struct sconnection *qconn; +{ + return fsserial_init (qconn, &sstdincmds, (const char *) NULL); +} + +/* Initialize a connection for use on a modem port. */ + +boolean +fsysdep_modem_init (qconn) + struct sconnection *qconn; +{ + return fsserial_init (qconn, &smodemcmds, + qconn->qport->uuconf_u.uuconf_smodem.uuconf_zdevice); +} + +/* Initialize a connection for use on a direct port. */ + +boolean +fsysdep_direct_init (qconn) + struct sconnection *qconn; +{ + return fsserial_init (qconn, &sdirectcmds, + qconn->qport->uuconf_u.uuconf_sdirect.uuconf_zdevice); +} + +/* Free up a serial port. */ + +static void +usserial_free (qconn) + struct sconnection *qconn; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + ubuffree (qsysdep->zdevice); + xfree ((pointer) qsysdep); + qconn->psysdep = NULL; +} + +/* This routine is used for both locking and unlocking. It is the + only routine which knows how to translate a device name into the + name of a lock file. If it can't figure out a name, it does + nothing and returns TRUE. */ + +static boolean +fsserial_lockfile (flok, qconn) + boolean flok; + const struct sconnection *qconn; +{ + struct ssysdep_conn *qsysdep; + const char *z; + char *zalc; + boolean fret; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + if (qconn->qport == NULL) + z = NULL; + else + z = qconn->qport->uuconf_zlockname; + zalc = NULL; + if (z == NULL) + { +#if ! HAVE_SVR4_LOCKFILES + { + const char *zbase; + size_t clen; + + zbase = strrchr (qsysdep->zdevice, '/') + 1; + clen = strlen (zbase); + zalc = zbufalc (sizeof "LCK.." + clen); + memcpy (zalc, "LCK..", sizeof "LCK.." - 1); + memcpy (zalc + sizeof "LCK.." - 1, zbase, clen + 1); +#if HAVE_SCO_LOCKFILES + { + char *zl; + + for (zl = zalc + sizeof "LCK.." - 1; *zl != '\0'; zl++) + if (isupper (*zl)) + *zl = tolower (*zl); + } +#endif + z = zalc; + } +#else /* ! HAVE_SVR4_LOCKFILES */ +#if HAVE_SVR4_LOCKFILES + { + struct stat s; + + if (stat (qsysdep->zdevice, &s) != 0) + { + ulog (LOG_ERROR, "stat (%s): %s", qsysdep->zdevice, + strerror (errno)); + return FALSE; + } + zalc = zbufalc (sizeof "LK.123.123.123"); + sprintf (zalc, "LK.%03d.%03d.%03d", major (s.st_dev), + major (s.st_rdev), minor (s.st_rdev)); + z = zalc; + } +#else /* ! HAVE_SVR4_LOCKFILES */ + z = strrchr (qsysdep->zdevice, '/') + 1; +#endif /* ! HAVE_SVR4_LOCKFILES */ +#endif /* ! HAVE_SVR4_LOCKFILES */ + } + + if (flok) + fret = fsdo_lock (z, FALSE, (boolean *) NULL); + else + fret = fsdo_unlock (z, FALSE); + +#if HAVE_COHERENT_LOCKFILES + if (fret) + { + if (flok) + { + if (lockttyexist (z)) + { + ulog (LOG_NORMAL, "%s: port already locked"); + fret = FALSE; + } + else + fret = fscoherent_disable_tty (z, &qsysdep->zenable); + } + else + { + fret = TRUE; + if (qsysdep->zenable != NULL) + { + const char *azargs[3]; + int aidescs[3]; + pid_t ipid; + + azargs[0] = "/etc/enable"; + azargs[1] = qsysdep->zenable; + azargs[2] = NULL; + aidescs[0] = SPAWN_NULL; + aidescs[1] = SPAWN_NULL; + aidescs[2] = SPAWN_NULL; + + ipid = ixsspawn (azargs, aidescs, TRUE, FALSE, + (const char *) NULL, TRUE, TRUE, + (const char *) NULL, (const char *) NULL, + (const char *) NULL); + if (ipid < 0) + { + ulog (LOG_ERROR, "ixsspawn (/etc/enable %s): %s", + qsysdep->zenable, strerror (errno)); + fret = FALSE; + } + else + { + if (ixswait ((unsigned long) ipid, (const char *) NULL) + == 0) + fret = TRUE; + else + fret = FALSE; + } + ubuffree (qsysdep->zenable); + qsysdep->zenable = NULL; + } + } + } +#endif /* HAVE_COHERENT_LOCKFILES */ + + ubuffree (zalc); + return fret; +} + +/* If we can mark a modem line in use, then when we lock a port we + must open it and mark it in use. We can't wait until the actual + open because we can't fail out if it is locked then. */ + +static boolean +fsserial_lock (qconn, fin) + struct sconnection *qconn; + boolean fin; +{ + if (! fsserial_lockfile (TRUE, qconn)) + return FALSE; + +#if HAVE_TIOCSINUSE || HAVE_TIOCEXCL + /* Open the line and try to mark it in use. */ + { + struct ssysdep_conn *qsysdep; + int iflag; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + + if (fin) + iflag = 0; + else + iflag = iSunblock; + + qsysdep->o = open (qsysdep->zdevice, O_RDWR | iflag); + if (qsysdep->o < 0) + { +#if O_NONBLOCK != 0 + if (! fin && iSunblock != O_NONBLOCK && errno == EINVAL) + { + iSunblock = O_NONBLOCK; + qsysdep->o = open (qsysdep->zdevice, + O_RDWR | O_NONBLOCK); + } +#endif + if (qsysdep->o < 0) + { + if (errno != EBUSY) + ulog (LOG_ERROR, "open (%s): %s", qsysdep->zdevice, + strerror (errno)); + (void) fsserial_lockfile (FALSE, qconn); + return FALSE; + } + } + +#if HAVE_TIOCSINUSE + /* If we can't mark it in use, return FALSE to indicate that the + lock failed. */ + if (ioctl (qsysdep->o, TIOCSINUSE, 0) < 0) + { + if (errno != EALREADY) + ulog (LOG_ERROR, "ioctl (TIOCSINUSE): %s", strerror (errno)); +#ifdef TIOCNOTTY + (void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL); +#endif + (void) close (qsysdep->o); + qsysdep->o = -1; + (void) fsserial_lockfile (FALSE, qconn); + return FALSE; + } +#endif + + if (fcntl (qsysdep->o, F_SETFD, + fcntl (qsysdep->o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); +#ifdef TIOCNOTTY + (void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL); +#endif + (void) close (qsysdep->o); + qsysdep->o = -1; + (void) fsserial_lockfile (FALSE, qconn); + return FALSE; + } + +#ifdef TIOCSCTTY + /* On BSD 4.4, make it our controlling terminal. */ + (void) ioctl (qsysdep->o, TIOCSCTTY, 0); +#endif + } +#endif /* HAVE_TIOCSINUSE || HAVE_TIOCEXCL */ + + return TRUE; +} + +/* Unlock a modem or direct port. */ + +static boolean +fsserial_unlock (qconn) + struct sconnection *qconn; +{ + boolean fret; + struct ssysdep_conn *qsysdep; + + fret = TRUE; + + /* The file may have been opened by fsserial_lock, so close it here + if necessary. */ + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + if (qsysdep->o >= 0) + { +#ifdef TIOCNOTTY + (void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL); +#endif + if (close (qsysdep->o) < 0) + { + ulog (LOG_ERROR, "close: %s", strerror (errno)); + fret = FALSE; + } + qsysdep->o = -1; + } + + if (! fsserial_lockfile (FALSE, qconn)) + fret = FALSE; + + return fret; +} + +/* Open a serial line. This sets the terminal settings. We begin in + seven bit mode and let the protocol change if necessary. */ + +#if HAVE_POSIX_TERMIOS +typedef speed_t baud_code; +#else +typedef int baud_code; +#endif + +static struct sbaud_table +{ + baud_code icode; + long ibaud; +} asSbaud_table[] = +{ + { B50, 50 }, + { B75, 75 }, + { B110, 110 }, + { B134, 134 }, + { B150, 150 }, + { B200, 200 }, + { B300, 300 }, + { B600, 600 }, + { B1200, 1200 }, + { B1800, 1800 }, + { B2400, 2400 }, + { B4800, 4800 }, + { B9600, 9600 }, +#ifdef B19200 + { B19200, 19200 }, +#else /* ! defined (B19200) */ +#ifdef EXTA + { EXTA, 19200 }, +#endif /* EXTA */ +#endif /* ! defined (B19200) */ +#ifdef B38400 + { B38400, 38400 }, +#else /* ! defined (B38400) */ +#ifdef EXTB + { EXTB, 38400 }, +#endif /* EXTB */ +#endif /* ! defined (B38400) */ +#ifdef B57600 + { B57600, 57600 }, +#endif +#ifdef B76800 + { B76800, 76800 }, +#endif +#ifdef B115200 + { B115200, 115200 }, +#endif + { B0, 0 } +}; + +#define CBAUD_TABLE (sizeof asSbaud_table / sizeof asSbaud_table[0]) + +#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS +/* Hold the MIN value for the terminal to avoid setting it + unnecessarily. */ +static int cSmin; +#endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */ + +static boolean +fsserial_open (qconn, ibaud, fwait) + struct sconnection *qconn; + long ibaud; + boolean fwait; +{ + struct ssysdep_conn *q; + baud_code ib; + + q = (struct ssysdep_conn *) qconn->psysdep; + + if (q->zdevice != NULL) + ulog_device (strrchr (q->zdevice, '/') + 1); + else + { + const char *zport; + boolean fdummy; + +#if DEBUG > 0 + if (qconn->qport != NULL && + qconn->qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN) + ulog (LOG_FATAL, "fsserial_open: Can't happen"); +#endif + zport = zsysdep_port_name (&fdummy); + if (zport != NULL) + ulog_device (zport); + } + + ib = B0; + if (ibaud != 0) + { + int i; + + for (i = 0; i < CBAUD_TABLE; i++) + if (asSbaud_table[i].ibaud == ibaud) + break; + if (i >= CBAUD_TABLE) + { + ulog (LOG_ERROR, "Unsupported baud rate %ld", ibaud); + return FALSE; + } + ib = asSbaud_table[i].icode; + } + + /* The port may have already been opened by the locking routine. */ + if (q->o < 0) + { + int iflag; + + if (fwait) + iflag = 0; + else + iflag = iSunblock; + + q->o = open (q->zdevice, O_RDWR | iflag); + if (q->o < 0) + { +#if O_NONBLOCK != 0 + if (! fwait && iSunblock != O_NONBLOCK && errno == EINVAL) + { + iSunblock = O_NONBLOCK; + q->o = open (q->zdevice, O_RDWR | O_NONBLOCK); + } +#endif + if (q->o < 0) + { + ulog (LOG_ERROR, "open (%s): %s", q->zdevice, + strerror (errno)); + return FALSE; + } + } + + if (fcntl (q->o, F_SETFD, fcntl (q->o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + return FALSE; + } + +#ifdef TIOCSCTTY + /* On BSD 4.4, make it our controlling terminal. */ + (void) ioctl (q->o, TIOCSCTTY, 0); +#endif + } + + /* Get the port flags, and make sure the ports are blocking. */ + + q->iflags = fcntl (q->o, F_GETFL, 0); + if (q->iflags < 0) + { + ulog (LOG_ERROR, "fcntl: %s", strerror (errno)); + return FALSE; + } + q->istdout_flags = -1; + + if (! fgetterminfo (q->o, &q->sorig)) + { + q->fterminal = FALSE; + return TRUE; + } + + q->fterminal = TRUE; + + q->snew = q->sorig; + +#if HAVE_BSD_TTY + + q->snew.stty.sg_flags = RAW | ANYP; + if (ibaud == 0) + ib = q->snew.stty.sg_ospeed; + else + { + q->snew.stty.sg_ispeed = ib; + q->snew.stty.sg_ospeed = ib; + } + + /* We don't want to receive any interrupt characters. */ + q->snew.stchars.t_intrc = -1; + q->snew.stchars.t_quitc = -1; + q->snew.stchars.t_eofc = -1; + q->snew.stchars.t_brkc = -1; + q->snew.sltchars.t_suspc = -1; + q->snew.sltchars.t_rprntc = -1; + q->snew.sltchars.t_dsuspc = -1; + q->snew.sltchars.t_flushc = -1; + q->snew.sltchars.t_werasc = -1; + q->snew.sltchars.t_lnextc = -1; + +#ifdef NTTYDISC + /* We want to use the ``new'' terminal driver so that we can use the + local mode bits to control XON/XOFF. */ + { + int iparam; + + if (ioctl (q->o, TIOCGETD, &iparam) >= 0 + && iparam != NTTYDISC) + { + iparam = NTTYDISC; + (void) ioctl (q->o, TIOCSETD, &iparam); + } + } +#endif + +#ifdef TIOCHPCL + /* When the file is closed, hang up the line. This is a safety + measure in case the program crashes. */ + (void) ioctl (q->o, TIOCHPCL, 0); +#endif + +#ifdef TIOCFLUSH + { + int iparam; + + /* Flush pending input. */ +#ifdef FREAD + iparam = FREAD; +#else + iparam = 0; +#endif + (void) ioctl (q->o, TIOCFLUSH, &iparam); + } +#endif /* TIOCFLUSH */ + +#endif /* HAVE_BSD_TTY */ + +#if HAVE_SYSV_TERMIO + + if (ibaud == 0) + ib = q->snew.c_cflag & CBAUD; + + q->snew.c_iflag &=~ ICLEAR_IFLAG; + q->snew.c_oflag &=~ ICLEAR_OFLAG; + q->snew.c_cflag &=~ ICLEAR_CFLAG; + q->snew.c_cflag |= (ib | ISET_CFLAG); + q->snew.c_lflag &=~ ICLEAR_LFLAG; + cSmin = 1; + q->snew.c_cc[VMIN] = cSmin; + q->snew.c_cc[VTIME] = 0; + +#ifdef TCFLSH + /* Flush pending input. */ + (void) ioctl (q->o, TCFLSH, 0); +#endif + +#endif /* HAVE_SYSV_TERMIO */ + +#if HAVE_POSIX_TERMIOS + + if (ibaud == 0) + ib = cfgetospeed (&q->snew); + + q->snew.c_iflag &=~ ICLEAR_IFLAG; + q->snew.c_oflag &=~ ICLEAR_OFLAG; + q->snew.c_cflag &=~ ICLEAR_CFLAG; + q->snew.c_cflag |= ISET_CFLAG; + q->snew.c_lflag &=~ ICLEAR_LFLAG; + cSmin = 1; + q->snew.c_cc[VMIN] = cSmin; + q->snew.c_cc[VTIME] = 0; + + (void) cfsetospeed (&q->snew, ib); + (void) cfsetispeed (&q->snew, ib); + + /* Flush pending input. */ + (void) tcflush (q->o, TCIFLUSH); + +#endif /* HAVE_POSIX_TERMIOS */ + + if (! fsetterminfo (q->o, &q->snew)) + { + ulog (LOG_ERROR, "Can't set terminal settings: %s", strerror (errno)); + return FALSE; + } + + if (ibaud != 0) + q->ibaud = ibaud; + else + { + int i; + + q->ibaud = (long) 1200; + for (i = 0; i < CBAUD_TABLE; i++) + { + if (asSbaud_table[i].icode == ib) + { + q->ibaud = asSbaud_table[i].ibaud; + break; + } + } + + DEBUG_MESSAGE1 (DEBUG_PORT, + "fsserial_open: Baud rate is %ld", q->ibaud); + } + + return TRUE; +} + +/* Open a standard input port. The code alternates q->o between 0 and + 1 as appropriate. It is always 0 before any call to fsblock. */ + +static boolean +fsstdin_open (qconn, ibaud, fwait) + struct sconnection *qconn; + long ibaud; + boolean fwait; +{ + struct ssysdep_conn *q; + + q = (struct ssysdep_conn *) qconn->psysdep; + q->o = 0; + if (! fsserial_open (qconn, ibaud, fwait)) + return FALSE; + q->istdout_flags = fcntl (1, F_GETFL, 0); + if (q->istdout_flags < 0) + { + ulog (LOG_ERROR, "fcntl: %s", strerror (errno)); + return FALSE; + } + return TRUE; +} + +/* Open a modem port. */ + +static boolean +fsmodem_open (qconn, ibaud, fwait) + struct sconnection *qconn; + long ibaud; + boolean fwait; +{ + if (ibaud == (long) 0) + ibaud = qconn->qport->uuconf_u.uuconf_smodem.uuconf_ibaud; + return fsserial_open (qconn, ibaud, fwait); +} + +/* Open a direct port. */ + +static boolean +fsdirect_open (qconn, ibaud, fwait) + struct sconnection *qconn; + long ibaud; + boolean fwait; +{ + if (ibaud == (long) 0) + ibaud = qconn->qport->uuconf_u.uuconf_sdirect.uuconf_ibaud; + return fsserial_open (qconn, ibaud, fwait); +} + +/* Change the blocking status of the port. We keep track of the + current blocking status to avoid calling fcntl unnecessarily; fcntl + turns out to be surprisingly expensive, at least on Ultrix. */ + +static boolean +fsblock (qs, fblock) + struct ssysdep_conn *qs; + boolean fblock; +{ + int iwant; + int isys; + + if (fblock) + iwant = qs->iflags &~ (O_NDELAY | O_NONBLOCK); + else + iwant = qs->iflags | iSunblock; + + if (iwant == qs->iflags) + return TRUE; + + isys = fcntl (qs->o, F_SETFL, iwant); + if (isys < 0) + { +#if O_NONBLOCK != 0 + if (! fblock && iSunblock != O_NONBLOCK && errno == EINVAL) + { + iSunblock = O_NONBLOCK; + iwant = qs->iflags | O_NONBLOCK; + isys = fcntl (qs->o, F_SETFL, iwant); + } +#endif + if (isys < 0) + { + ulog (LOG_ERROR, "fcntl: %s", strerror (errno)); + return FALSE; + } + } + + qs->iflags = iwant; + + if (qs->istdout_flags >= 0) + { + if (fblock) + iwant = qs->istdout_flags &~ (O_NDELAY | O_NONBLOCK); + else + iwant = qs->istdout_flags | iSunblock; + + if (fcntl (1, F_SETFL, iwant) < 0) + { + /* We don't bother to fix up iSunblock here, since we + succeeded above. */ + ulog (LOG_ERROR, "fcntl: %s", strerror (errno)); + return FALSE; + } + + qs->istdout_flags = iwant; + } + + return TRUE; +} + +/* Close a serial port. */ + +static boolean +fsserial_close (q) + struct ssysdep_conn *q; +{ + if (q->o >= 0) + { + /* Use a 30 second timeout to avoid hanging while draining + output. */ + if (q->fterminal) + { + fSalarm = FALSE; + + if (fsysdep_catch ()) + { + usysdep_start_catch (); + usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL); + (void) alarm (30); + + (void) fsetterminfodrain (q->o, &q->sorig); + } + + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + (void) alarm (0); + usysdep_end_catch (); + + /* If we timed out, use the non draining call. Hopefully + this can't hang. */ + if (fSalarm) + (void) fsetterminfo (q->o, &q->sorig); + } + +#ifdef TIOCNOTTY + /* We don't want this as our controlling terminal any more, so + get rid of it. This is necessary because we don't want to + open /dev/tty, since that can confuse the serial port locking + on some computers. */ + (void) ioctl (q->o, TIOCNOTTY, (char *) NULL); +#endif + + (void) close (q->o); + q->o = -1; + + /* Sleep to give the terminal a chance to settle, in case we are + about to call out again. */ + sleep (2); + } + + return TRUE; +} + +/* Close a stdin port. */ + +/*ARGSUSED*/ +static boolean +fsstdin_close (qconn, puuconf, qdialer, fsuccess) + struct sconnection *qconn; + pointer puuconf; + struct uuconf_dialer *qdialer; + boolean fsuccess; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + (void) close (1); + (void) close (2); + qsysdep->o = 0; + return fsserial_close (qsysdep); +} + +/* Close a modem port. */ + +static boolean +fsmodem_close (qconn, puuconf, qdialer, fsuccess) + struct sconnection *qconn; + pointer puuconf; + struct uuconf_dialer *qdialer; + boolean fsuccess; +{ + struct ssysdep_conn *qsysdep; + boolean fret; + struct uuconf_dialer sdialer; + const struct uuconf_chat *qchat; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + + fret = TRUE; + + /* Figure out the dialer so that we can run the complete or abort + chat scripts. */ + if (qdialer == NULL) + { + if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_pzdialer != NULL) + { + const char *zdialer; + int iuuconf; + + zdialer = qconn->qport->uuconf_u.uuconf_smodem.uuconf_pzdialer[0]; + iuuconf = uuconf_dialer_info (puuconf, zdialer, &sdialer); + if (iuuconf == UUCONF_SUCCESS) + qdialer = &sdialer; + else + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + fret = FALSE; + } + } + else + qdialer = qconn->qport->uuconf_u.uuconf_smodem.uuconf_qdialer; + } + + /* Get the complete or abort chat script to use. */ + qchat = NULL; + if (qdialer != NULL) + { + if (fsuccess) + qchat = &qdialer->uuconf_scomplete; + else + qchat = &qdialer->uuconf_sabort; + } + + if (qchat != NULL + && (qchat->uuconf_pzprogram != NULL + || qchat->uuconf_pzchat != NULL)) + { + boolean fsighup_ignored; + HELD_SIG_MASK smask; + int i; + sig_atomic_t afhold[INDEXSIG_COUNT]; + + /* We're no longer interested in carrier. */ + (void) fsmodem_carrier (qconn, FALSE); + + /* The port I/O routines check whether any signal has been + received, and abort if one has. While we are closing down + the modem, we don't care if we received a signal in the past, + but we do care if we receive a new signal (otherwise it would + be difficult to kill a uucico which was closing down a + modem). We never care if we get SIGHUP at this point. So we + turn off SIGHUP, remember what signals we've already seen, + and clear our notion of what signals we've seen. We have to + block the signals while we remember and clear the array, + since we might otherwise miss a signal which occurred between + the copy and the clear (old systems can't block signals; they + will just have to suffer the race). */ + usset_signal (SIGHUP, SIG_IGN, FALSE, &fsighup_ignored); + smask = isblocksigs (); + for (i = 0; i < INDEXSIG_COUNT; i++) + { + afhold[i] = afSignal[i]; + afSignal[i] = FALSE; + } + usunblocksigs (smask); + + if (! fchat (qconn, puuconf, qchat, (const struct uuconf_system *) NULL, + (const struct uuconf_dialer *) NULL, (const char *) NULL, + FALSE, qconn->qport->uuconf_zname, + qsysdep->ibaud)) + fret = FALSE; + + /* Restore the old signal array and the SIGHUP handler. It is + not necessary to block signals here, since all we are doing + is exactly what the signal handler itself would do if the + signal occurred. */ + for (i = 0; i < INDEXSIG_COUNT; i++) + if (afhold[i]) + afSignal[i] = TRUE; + if (! fsighup_ignored) + usset_signal (SIGHUP, ussignal, TRUE, (boolean *) NULL); + } + + if (qdialer != NULL + && qdialer == &sdialer) + (void) uuconf_dialer_free (puuconf, &sdialer); + +#if ! HAVE_RESET_BUG + /* Reset the terminal to make sure we drop DTR. It should be + dropped when we close the descriptor, but that doesn't seem to + happen on some systems. Use a 30 second timeout to avoid hanging + while draining output. */ + if (qsysdep->fterminal) + { +#if HAVE_BSD_TTY + qsysdep->snew.stty.sg_ispeed = B0; + qsysdep->snew.stty.sg_ospeed = B0; +#endif +#if HAVE_SYSV_TERMIO + qsysdep->snew.c_cflag = (qsysdep->snew.c_cflag &~ CBAUD) | B0; +#endif +#if HAVE_POSIX_TERMIOS + (void) cfsetospeed (&qsysdep->snew, B0); +#endif + + fSalarm = FALSE; + + if (fsysdep_catch ()) + { + usysdep_start_catch (); + usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL); + (void) alarm (30); + + (void) fsetterminfodrain (qsysdep->o, &qsysdep->snew); + } + + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + (void) alarm (0); + usysdep_end_catch (); + + /* Let the port settle. */ + sleep (2); + } +#endif /* ! HAVE_RESET_BUG */ + + if (! fsserial_close (qsysdep)) + fret = FALSE; + + return fret; +} + +/* Close a direct port. */ + +/*ARGSUSED*/ +static boolean +fsdirect_close (qconn, puuconf, qdialer, fsuccess) + struct sconnection *qconn; + pointer puuconf; + struct uuconf_dialer *qdialer; + boolean fsuccess; +{ + return fsserial_close ((struct ssysdep_conn *) qconn->psysdep); +} + +/* Reset a serial port by hanging up. */ + +static boolean +fsserial_reset (qconn) + struct sconnection *qconn; +{ + struct ssysdep_conn *q; + sterminal sbaud; + + q = (struct ssysdep_conn *) qconn->psysdep; + + if (! q->fterminal) + return TRUE; + + sbaud = q->snew; + +#if HAVE_BSD_TTY + sbaud.stty.sg_ispeed = B0; + sbaud.stty.sg_ospeed = B0; +#endif +#if HAVE_SYSV_TERMIO + sbaud.c_cflag = (sbaud.c_cflag &~ CBAUD) | B0; +#endif +#if HAVE_POSIX_TERMIOS + if (cfsetospeed (&sbaud, B0) < 0) + { + ulog (LOG_ERROR, "Can't set baud rate: %s", strerror (errno)); + return FALSE; + } +#endif + + if (! fsetterminfodrain (q->o, &sbaud)) + { + ulog (LOG_ERROR, "Can't hangup terminal: %s", strerror (errno)); + return FALSE; + } + + /* Give the terminal a chance to settle. */ + sleep (2); + + if (! fsetterminfo (q->o, &q->snew)) + { + ulog (LOG_ERROR, "Can't reopen terminal: %s", strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/* Reset a standard input port. */ + +static boolean +fsstdin_reset (qconn) + struct sconnection *qconn; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + qsysdep->o = 0; + return fsserial_reset (qconn); +} + +/* Begin dialing out on a modem port. This opens the dialer device if + there is one. */ + +boolean +fsysdep_modem_begin_dial (qconn, qdial) + struct sconnection *qconn; + struct uuconf_dialer *qdial; +{ + struct ssysdep_conn *qsysdep; + const char *z; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + +#ifdef TIOCMODEM + /* If we can tell the modem to obey modem control, do so. */ + { + int iperm; + + iperm = 0; + (void) ioctl (qsysdep->o, TIOCMODEM, &iperm); + } +#endif /* TIOCMODEM */ + + /* If we supposed to toggle DTR, do so. */ + + if (qdial->uuconf_fdtr_toggle) + { +#ifdef TIOCCDTR + (void) ioctl (qsysdep->o, TIOCCDTR, 0); + sleep (2); + (void) ioctl (qsysdep->o, TIOCSDTR, 0); +#else /* ! defined (TIOCCDTR) */ + (void) fconn_reset (qconn); +#endif /* ! defined (TIOCCDTR) */ + + if (qdial->uuconf_fdtr_toggle_wait) + sleep (2); + } + + if (! fsmodem_carrier (qconn, FALSE)) + return FALSE; + + /* Open the dial device if there is one. */ + z = qconn->qport->uuconf_u.uuconf_smodem.uuconf_zdial_device; + if (z != NULL) + { + char *zfree; + int o; + + qsysdep->ohold = qsysdep->o; + + zfree = NULL; + if (*z != '/') + { + zfree = zbufalc (sizeof "/dev/" + strlen (z)); + sprintf (zfree, "/dev/%s", z); + z = zfree; + } + + o = open ((char *) z, O_RDWR | O_NOCTTY); + if (o < 0) + { + ulog (LOG_ERROR, "open (%s): %s", z, strerror (errno)); + ubuffree (zfree); + return FALSE; + } + ubuffree (zfree); + + if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + (void) close (o); + return FALSE; + } + + qsysdep->o = o; + } + + return TRUE; +} + +/* Tell the port to require or not require carrier. On BSD this uses + TIOCCAR and TIOCNCAR, which I assume are generally supported (it + can also use the LNOMDM bit supported by IS68K Unix). On System V + it resets or sets CLOCAL. We only require carrier if the port + supports it. This will only be called with fcarrier TRUE if the + dialer supports carrier. */ + +static boolean +fsmodem_carrier (qconn, fcarrier) + struct sconnection *qconn; + boolean fcarrier; +{ + register struct ssysdep_conn *q; + + q = (struct ssysdep_conn *) qconn->psysdep; + + if (! q->fterminal) + return TRUE; + + if (fcarrier) + { + if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_fcarrier) + { +#ifdef TIOCCAR + /* Tell the modem to pay attention to carrier. */ + if (ioctl (q->o, TIOCCAR, 0) < 0) + { + ulog (LOG_ERROR, "ioctl (TIOCCAR): %s", strerror (errno)); + return FALSE; + } +#endif /* TIOCCAR */ + +#if HAVE_BSD_TTY +#ifdef LNOMDM + /* IS68K Unix uses a local LNOMDM bit. */ + { + int iparam; + + iparam = LNOMDM; + if (ioctl (q->o, TIOCLBIC, &iparam) < 0) + { + ulog (LOG_ERROR, "ioctl (TIOCLBIC, LNOMDM): %s", + strerror (errno)); + return FALSE; + } + } +#endif /* LNOMDM */ +#endif /* HAVE_BSD_TTY */ + +#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS + /* Put the modem into nonlocal mode. */ + q->snew.c_cflag &=~ CLOCAL; + if (! fsetterminfo (q->o, &q->snew)) + { + ulog (LOG_ERROR, "Can't clear CLOCAL: %s", strerror (errno)); + return FALSE; + } +#endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */ + } + } + else + { +#ifdef TIOCNCAR + /* Tell the modem to ignore carrier. */ + if (ioctl (q->o, TIOCNCAR, 0) < 0) + { + ulog (LOG_ERROR, "ioctl (TIOCNCAR): %s", strerror (errno)); + return FALSE; + } +#endif /* TIOCNCAR */ + +#if HAVE_BSD_TTY +#ifdef LNOMDM + /* IS68K Unix uses a local LNOMDM bit. */ + { + int iparam; + + iparam = LNOMDM; + if (ioctl (q->o, TIOCLBIS, &iparam) < 0) + { + ulog (LOG_ERROR, "ioctl (TIOCLBIS, LNOMDM): %s", + strerror (errno)); + return FALSE; + } + } +#endif /* LNOMDM */ +#endif /* HAVE_BSD_TTY */ + +#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS + /* Put the modem into local mode (ignore carrier) to start the chat + script. */ + q->snew.c_cflag |= CLOCAL; + if (! fsetterminfo (q->o, &q->snew)) + { + ulog (LOG_ERROR, "Can't set CLOCAL: %s", strerror (errno)); + return FALSE; + } + +#if HAVE_CLOCAL_BUG + /* On SCO and AT&T UNIX PC you have to reopen the port. */ + { + int onew; + + onew = open (q->zdevice, O_RDWR); + if (onew < 0) + { + ulog (LOG_ERROR, "open (%s): %s", q->zdevice, strerror (errno)); + return FALSE; + } + + if (fcntl (onew, F_SETFD, + fcntl (onew, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + (void) close (onew); + return FALSE; + } + + (void) close (q->o); + q->o = onew; + } +#endif /* HAVE_CLOCAL_BUG */ + +#endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */ + } + + return TRUE; +} + +/* Finish dialing out on a modem by closing any dialer device and waiting + for carrier. */ + +boolean +fsysdep_modem_end_dial (qconn, qdial) + struct sconnection *qconn; + struct uuconf_dialer *qdial; +{ + struct ssysdep_conn *q; + + q = (struct ssysdep_conn *) qconn->psysdep; + + if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_zdial_device != NULL) + { + (void) close (q->o); + q->o = q->ohold; + } + + if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_fcarrier + && qdial->uuconf_fcarrier) + { + /* Tell the port that we need carrier. */ + if (! fsmodem_carrier (qconn, TRUE)) + return FALSE; + +#ifdef TIOCWONLINE + + /* We know how to wait for carrier, so do so. */ + + /* If we already got a signal, just quit now. */ + if (FGOT_QUIT_SIGNAL ()) + return FALSE; + + /* This bit of code handles signals just like fsysdep_conn_read + does. See that function for a longer explanation. */ + + /* Use fsysdep_catch to handle a longjmp from the signal + handler. */ + + fSalarm = FALSE; + + if (fsysdep_catch ()) + { + /* Start catching SIGALRM; normally we ignore it. */ + usysdep_start_catch (); + usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL); + (void) alarm (qdial->uuconf_ccarrier_wait); + + /* We really don't care if we get an error, since that will + probably just mean that TIOCWONLINE isn't supported in + which case there's nothing we can do anyhow. If we get + SIGINT we want to keep waiting for carrier, because + SIGINT just means don't start any new sessions. We don't + handle SIGINT correctly if we do a longjmp in the signal + handler; too bad. */ + while (ioctl (q->o, TIOCWONLINE, 0) < 0 + && errno == EINTR) + { + /* Log the signal. */ + ulog (LOG_ERROR, (const char *) NULL); + if (FGOT_QUIT_SIGNAL () || fSalarm) + break; + } + } + + /* Turn off the pending SIGALRM and ignore SIGALARM again. */ + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + (void) alarm (0); + usysdep_end_catch (); + + /* If we got a random signal, just return FALSE. */ + if (FGOT_QUIT_SIGNAL ()) + return FALSE; + + /* If we timed out, give an error. */ + if (fSalarm) + { + ulog (LOG_ERROR, "Timed out waiting for carrier"); + return FALSE; + } + +#endif /* TIOCWONLINE */ + } + + return TRUE; +} + +/* Read data from a connection, with a timeout. This routine handles + all types of connections, including TLI. + + This function should return when we have read cmin characters or + the timeout has occurred. We have to work a bit to get Unix to do + this efficiently on a terminal. The simple implementation + schedules a SIGALRM signal and then calls read; if there is a + single character available, the call to read will return + immediately, so there must be a loop which terminates when the + SIGALRM is delivered or the correct number of characters has been + read. This can be very inefficient with a fast CPU or a low baud + rate (or both!), since each call to read may return only one or two + characters. + + Under POSIX or System V, we can specify a minimum number of + characters to read, so there is no serious trouble. + + Under BSD, we figure out how many characters we have left to read, + how long it will take for them to arrive at the current baud rate, + and sleep that long. + + Doing this with a timeout and avoiding all possible race conditions + get very hairy, though. Basically, we're going to schedule a + SIGALRM for when the timeout expires. I don't really want to do a + longjmp in the SIGALRM handler, though, because that may lose data. + Therefore, I have the signal handler set a variable. However, this + means that there will be a span of time between the time the code + checks the variable and the time it calls the read system call; if + the SIGALRM occurs during that time, the read might hang forever. + To avoid this, the SIGALRM handler not only sets a global variable, + it also schedules another SIGALRM for one second in the future + (POSIX specifies that a signal handler is permitted to safely call + alarm). To avoid getting a continual sequence of SIGALRM + interrupts, we change the signal handler to ignore SIGALRM when + we're about to exit the function. This means that every time we + execute fsysdep_conn_read we make at least five system calls. It's + the best I've been able to come up with, though. + + When fsysdep_conn_read finishes, there will be no SIGALRM scheduled + and SIGALRM will be ignored. */ + +boolean +fsysdep_conn_read (qconn, zbuf, pclen, cmin, ctimeout, freport) + struct sconnection *qconn; + char *zbuf; + size_t *pclen; + size_t cmin; + int ctimeout; + boolean freport; +{ + CATCH_PROTECT size_t cwant; + boolean fret; + register struct ssysdep_conn * const q + = (struct ssysdep_conn *) qconn->psysdep; + + cwant = *pclen; + *pclen = 0; + + /* Guard against a bad timeout. We return TRUE when a timeout + expires. It is possible to get a negative timeout here because + the calling code does not check user supplied timeouts for + plausibility. */ + if (ctimeout <= 0) + return TRUE; + + /* We want to do a blocking read. */ + if (! fsblock (q, TRUE)) + return FALSE; + + fSalarm = FALSE; + + /* We're going to set up an alarm signal to last for the entire + read. If the read system call cannot be interrupted, the signal + handler will do a longjmp causing fsysdep_catch (a macro) to + return FALSE. We handle that here. If read can be interrupted, + fsysdep_catch will be defined to TRUE. */ + if (fsysdep_catch ()) + { + /* Prepare to catch SIGALRM and schedule the signal. */ + usysdep_start_catch (); + usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL); + alarm (ctimeout); + } + else + { + /* We caught a signal. We don't actually have to do anything, + as all the appropriate checks are made at the start of the + following loop. */ + } + + fret = FALSE; + + while (TRUE) + { + int cgot; + +#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS + /* If we can tell the terminal not to return until we have a + certain number of characters, do so. */ + if (q->fterminal) + { + int csetmin; + + /* I'm not that confident about setting MIN to values larger + than 127, although up to 255 would probably work. */ + if (cmin < 127) + csetmin = cmin; + else + csetmin = 127; + + if (csetmin != cSmin) + { + q->snew.c_cc[VMIN] = csetmin; + while (! fsetterminfo (q->o, &q->snew)) + { + if (errno != EINTR + || FGOT_QUIT_SIGNAL ()) + { + int ierr; + + /* We turn off the signal before reporting the + error to minimize any problems with + interrupted system calls. */ + ierr = errno; + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + alarm (0); + usysdep_end_catch (); + ulog (LOG_ERROR, "Can't set MIN for terminal: %s", + strerror (ierr)); + return FALSE; + } + + if (fSalarm) + { + ulog (LOG_ERROR, + "Timed out when setting MIN to %d; retrying", + csetmin); + fSalarm = FALSE; + alarm (ctimeout); + } + } + cSmin = csetmin; + } + } +#endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */ + + /* If we've received a signal, get out now. */ + if (FGOT_QUIT_SIGNAL ()) + break; + + /* If we've already gotten a SIGALRM, get out with whatever + we've accumulated. */ + if (fSalarm) + { + fret = TRUE; + break; + } + + /* Right here is the race condition which we avoid by having the + SIGALRM handler schedule another SIGALRM. */ +#if HAVE_TLI + if (q->ftli) + { + int iflags; + + cgot = t_rcv (q->o, zbuf, cwant, &iflags); + if (cgot < 0 && t_errno != TSYSERR) + { + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + alarm (0); + usysdep_end_catch (); + + if (freport) + ulog (LOG_ERROR, "t_rcv: %s", + (t_errno >= 0 && t_errno < t_nerr + ? t_errlist[t_errno] + : "unknown TLI error")); + + return FALSE; + } + } + else +#endif + cgot = read (q->o, zbuf, cwant); + + /* If the read returned an error, check for signals. */ + if (cgot < 0) + { + if (errno == EINTR) + { + /* Log the signal. */ + ulog (LOG_ERROR, (const char *) NULL); + } + if (fSalarm) + { + fret = TRUE; + break; + } + if (FGOT_QUIT_SIGNAL ()) + break; + } + + /* If read returned an error, get out. We just ignore EINTR + here, since it must be from some signal we don't care about. + If the read returned 0 then the line must have been hung up + (normally we would have received SIGHUP, but we can't count + on that). We turn off the signals before calling ulog to + reduce problems with interrupted system calls. */ + if (cgot <= 0) + { + if (cgot < 0 && errno == EINTR) + cgot = 0; + else + { + int ierr; + + ierr = errno; + + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + alarm (0); + usysdep_end_catch (); + + if (freport) + { + if (cgot == 0) + ulog (LOG_ERROR, "Line disconnected"); + else + ulog (LOG_ERROR, "read: %s", strerror (ierr)); + } + + return FALSE; + } + } + + cwant -= cgot; + if (cgot >= cmin) + cmin = 0; + else + cmin -= cgot; + zbuf += cgot; + *pclen += cgot; + + /* If we have enough data, get out now. */ + if (cmin == 0) + { + fret = TRUE; + break; + } + +#if HAVE_BSD_TTY + /* We still want more data, so sleep long enough for the rest of + it to arrive. We don't this for System V or POSIX because + setting MIN is good enough (we can't sleep longer than it + takes to get MAX_INPUT characters anyhow). + + The baud rate is approximately 10 times the number of + characters which will arrive in one second, so the number of + milliseconds to sleep == + characters * (milliseconds / character) == + characters * (1000 * (seconds / character)) == + characters * (1000 * (1 / (baud / 10))) == + characters * (10000 / baud) + + We arbitrarily reduce the sleep amount by 10 milliseconds to + attempt to account for the amount of time it takes to set up + the sleep. This is how long it takes to get half a character + at 19200 baud. We then don't bother to sleep for less than + 10 milliseconds. We don't sleep if the read was interrupted. + + We use select to sleep. It would be easy to use poll as + well, but it's unlikely that any system with BSD ttys would + have poll but not select. Using select avoids hassles with + the pending SIGALRM; if it hits the select will be + interrupted, and otherwise the select will not affect it. */ + +#if ! HAVE_SELECT + #error This code requires select; feel free to extend it +#endif + + if (q->fterminal && cmin > 1 && cgot > 0) + { + int csleepchars; + int isleep; + + /* We don't try to read all the way up to MAX_INPUT, + since that might drop a character. */ + if (cmin <= MAX_INPUT - 10) + csleepchars = cmin; + else + csleepchars = MAX_INPUT - 10; + + isleep = (int) (((long) csleepchars * 10000L) / q->ibaud); + isleep -= 10; + + if (isleep > 10) + { + struct timeval s; + + s.tv_sec = isleep / 1000; + s.tv_usec = (isleep % 1000) * 1000; + + /* Some versions of select take a pointer to an int, + while some take a pointer to an fd_set. I just cast + the arguments to a generic pointer, and assume that + any machine which distinguishes int * from fd_set * + (I would be amazed if there are any such machines) + have an appropriate prototype somewhere or other. */ + (void) select (0, (pointer) NULL, (pointer) NULL, + (pointer) NULL, &s); + + /* Here either the select finished sleeping or we got a + SIGALRM. If the latter occurred, fSalarm was set to + TRUE; it will be checked at the top of the loop. */ + } + } +#endif /* HAVE_BSD_TTY */ + } + + /* Turn off the pending SIGALRM and return. */ + + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + alarm (0); + usysdep_end_catch (); + + return fret; +} + +/* Read from a stdin port. */ + +static boolean +fsstdin_read (qconn, zbuf, pclen, cmin, ctimeout, freport) + struct sconnection *qconn; + char *zbuf; + size_t *pclen; + size_t cmin; + int ctimeout; + boolean freport; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + qsysdep->o = 0; + return fsysdep_conn_read (qconn, zbuf, pclen, cmin, ctimeout, freport); +} + +/* Write data to a connection. This routine handles all types of + connections, including TLI. */ + +boolean +fsysdep_conn_write (qconn, zwrite, cwrite) + struct sconnection *qconn; + const char *zwrite; + size_t cwrite; +{ + struct ssysdep_conn *q; + int czero; + + q = (struct ssysdep_conn *) qconn->psysdep; + + /* We want blocking writes here. */ + if (! fsblock (q, TRUE)) + return FALSE; + + czero = 0; + + while (cwrite > 0) + { + int cdid; + + /* Loop until we don't get an interrupt. */ + while (TRUE) + { + /* If we've received a signal, don't continue. */ + if (FGOT_QUIT_SIGNAL ()) + return FALSE; + +#if HAVE_TLI + if (q->ftli) + { + cdid = t_snd (q->o, zwrite, cwrite, 0); + if (cdid < 0 && t_errno != TSYSERR) + { + ulog (LOG_ERROR, "t_snd: %s", + (t_errno >= 0 && t_errno < t_nerr + ? t_errlist[t_errno] + : "unknown TLI error")); + return FALSE; + } + } + else +#endif + cdid = write (q->o, zwrite, cwrite); + + if (cdid >= 0) + break; + if (errno != EINTR) + break; + + /* We were interrupted by a signal. Log it. */ + ulog (LOG_ERROR, (const char *) NULL); + } + + if (cdid < 0) + { + if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENODATA) + { + ulog (LOG_ERROR, "write: %s", strerror (errno)); + return FALSE; + } + cdid = 0; + } + + if (cdid == 0) + { + /* On some systems write will return 0 if carrier is lost. + If we fail to write anything ten times in a row, we + assume that this has happened. This is hacked in like + this because there seems to be no reliable way to tell + exactly why the write returned 0. */ + ++czero; + if (czero >= 10) + { + ulog (LOG_ERROR, "Line disconnected"); + return FALSE; + } + } + else + { + czero = 0; + + cwrite -= cdid; + zwrite += cdid; + } + } + + return TRUE; +} + +/* Write to a stdin port. */ + +static boolean +fsstdin_write (qconn, zwrite, cwrite) + struct sconnection *qconn; + const char *zwrite; + size_t cwrite; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + qsysdep->o = 0; + if (! fsblock (qsysdep, TRUE)) + return FALSE; + qsysdep->o = 1; + return fsysdep_conn_write (qconn, zwrite, cwrite); +} + +/* The fsysdep_conn_io routine is supposed to both read and write data + until it has either filled its read buffer or written out all the + data it was given. This lets us write out large packets without + losing incoming data. It handles all types of connections, + including TLI. */ + +boolean +fsysdep_conn_io (qconn, zwrite, pcwrite, zread, pcread) + struct sconnection *qconn; + const char *zwrite; + size_t *pcwrite; + char *zread; + size_t *pcread; +{ + struct ssysdep_conn *q; + size_t cwrite, cread; + int czero; + + q = (struct ssysdep_conn *) qconn->psysdep; + + cwrite = *pcwrite; + *pcwrite = 0; + cread = *pcread; + *pcread = 0; + + czero = 0; + + while (TRUE) + { + int cgot, cdid; + size_t cdo; + + /* This used to always use nonblocking writes, but it turns out + that some systems don't support them on terminals. + + The current algorithm is: + loop: + unblocked read + if read buffer full, return + if nothing to write, return + if HAVE_UNBLOCKED_WRITES + write all data + else + write up to SINGLE_WRITE bytes + if all data written, return + if no data written + blocked write of up to SINGLE_WRITE bytes + + This algorithm should work whether the system supports + unblocked writes on terminals or not. If the system supports + unblocked writes but HAVE_UNBLOCKED_WRITES is 0, then it will + call write more often than it needs to. If the system does + not support unblocked writes but HAVE_UNBLOCKED_WRITES is 1, + then the write may hang so long that incoming data is lost. + This is actually possible at high baud rates on any system + when a blocking write is done; there is no solution, except + hardware handshaking. */ + + /* If we are running on standard input, we switch the file + descriptors by hand. */ + if (q->istdout_flags >= 0) + q->o = 0; + + /* Do an unblocked read. */ + if (! fsblock (q, FALSE)) + return FALSE; + + /* Loop until we get something (error or data) other than an + acceptable EINTR. */ + while (TRUE) + { + /* If we've received a signal, don't continue. */ + if (FGOT_QUIT_SIGNAL ()) + return FALSE; + +#if HAVE_TLI + if (q->ftli) + { + int iflags; + + cgot = t_rcv (q->o, zread, cread, &iflags); + if (cgot < 0) + { + if (t_errno == TNODATA) + errno = EAGAIN; + else if (t_errno != TSYSERR) + { + ulog (LOG_ERROR, "t_rcv: %s", + (t_errno >= 0 && t_errno < t_nerr + ? t_errlist[t_errno] + : "unknown TLI error")); + return FALSE; + } + } + } + else +#endif + cgot = read (q->o, zread, cread); + + if (cgot >= 0) + break; + if (errno != EINTR) + break; + + /* We got interrupted by a signal. Log it. */ + ulog (LOG_ERROR, (const char *) NULL); + } + + if (cgot < 0) + { + if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENODATA) + { + ulog (LOG_ERROR, "read: %s", strerror (errno)); + return FALSE; + } + cgot = 0; + } + + cread -= cgot; + zread += cgot; + *pcread += cgot; + + /* If we've filled the read buffer, or we have nothing left to + write, return out. */ + if (cread == 0 || cwrite == 0) + return TRUE; + + /* The port is currently unblocked. Do a write. */ + cdo = cwrite; + +#if ! HAVE_UNBLOCKED_WRITES + if (q->fterminal && cdo > SINGLE_WRITE) + cdo = SINGLE_WRITE; +#endif + + if (q->istdout_flags >= 0) + q->o = 1; + + /* Loop until we get something besides EINTR. */ + while (TRUE) + { + /* If we've received a signal, don't continue. */ + if (FGOT_QUIT_SIGNAL ()) + return FALSE; + +#if HAVE_TLI + if (q->ftli) + { + cdid = t_snd (q->o, zwrite, cdo, 0); + if (cdid < 0) + { + if (t_errno == TFLOW) + errno = EAGAIN; + else if (t_errno != TSYSERR) + { + ulog (LOG_ERROR, "t_snd: %s", + (t_errno >= 0 && t_errno < t_nerr + ? t_errlist[t_errno] + : "unknown TLI error")); + return FALSE; + } + } + } + else +#endif + cdid = write (q->o, zwrite, cdo); + + if (cdid >= 0) + break; + if (errno != EINTR) + break; + + /* We got interrupted by a signal. Log it. */ + ulog (LOG_ERROR, (const char *) NULL); + } + + if (cdid < 0) + { + if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENODATA) + { + ulog (LOG_ERROR, "write: %s", strerror (errno)); + return FALSE; + } + cdid = 0; + } + + if (cdid > 0) + { + /* We wrote some data. If we wrote everything, return out. + Otherwise loop around and do another read. */ + cwrite -= cdid; + zwrite += cdid; + *pcwrite += cdid; + + if (cwrite == 0) + return TRUE; + + czero = 0; + } + else + { + /* We didn't write any data. Do a blocking write. */ + + if (q->istdout_flags >= 0) + q->o = 0; + + if (! fsblock (q, TRUE)) + return FALSE; + + cdo = cwrite; + if (cdo > SINGLE_WRITE) + cdo = SINGLE_WRITE; + + DEBUG_MESSAGE1 (DEBUG_PORT, + "fsysdep_conn_io: Blocking write of %lud", + (unsigned long) cdo); + + if (q->istdout_flags >= 0) + q->o = 1; + + /* Loop until we get something besides EINTR. */ + while (TRUE) + { + /* If we've received a signal, don't continue. */ + if (FGOT_QUIT_SIGNAL ()) + return FALSE; + +#if HAVE_TLI + if (q->ftli) + { + cdid = t_snd (q->o, zwrite, cdo, 0); + if (cdid < 0 && t_errno != TSYSERR) + { + ulog (LOG_ERROR, "t_snd: %s", + (t_errno >= 0 && t_errno < t_nerr + ? t_errlist[t_errno] + : "unknown TLI error")); + return FALSE; + } + } + else +#endif + cdid = write (q->o, zwrite, cdo); + + if (cdid >= 0) + break; + if (errno != EINTR) + break; + + /* We got interrupted by a signal. Log it. */ + ulog (LOG_ERROR, (const char *) NULL); + } + + if (cdid < 0) + { + ulog (LOG_ERROR, "write: %s", strerror (errno)); + return FALSE; + } + + if (cdid == 0) + { + /* On some systems write will return 0 if carrier is + lost. If we fail to write anything ten times in a + row, we assume that this has happened. This is + hacked in like this because there seems to be no + reliable way to tell exactly why the write returned + 0. */ + ++czero; + if (czero >= 10) + { + ulog (LOG_ERROR, "Line disconnected"); + return FALSE; + } + } + else + { + cwrite -= cdid; + zwrite += cdid; + *pcwrite += cdid; + czero = 0; + } + } + } +} + +/* Send a break character to a serial port. */ + +static boolean +fsserial_break (qconn) + struct sconnection *qconn; +{ + struct ssysdep_conn *q; + + q = (struct ssysdep_conn *) qconn->psysdep; + +#if HAVE_BSD_TTY + (void) ioctl (q->o, TIOCSBRK, 0); + sleep (2); + (void) ioctl (q->o, TIOCCBRK, 0); + return TRUE; +#endif /* HAVE_BSD_TTY */ +#if HAVE_SYSV_TERMIO + (void) ioctl (q->o, TCSBRK, 0); + return TRUE; +#endif /* HAVE_SYSV_TERMIO */ +#if HAVE_POSIX_TERMIOS + return tcsendbreak (q->o, 0) == 0; +#endif /* HAVE_POSIX_TERMIOS */ +} + +/* Send a break character to a stdin port. */ + +static boolean +fsstdin_break (qconn) + struct sconnection *qconn; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + qsysdep->o = 1; + return fsserial_break (qconn); +} + +/* Change the setting of a serial port. */ + +/*ARGSUSED*/ +static boolean +fsserial_set (qconn, tparity, tstrip, txonxoff) + struct sconnection *qconn; + enum tparitysetting tparity; + enum tstripsetting tstrip; + enum txonxoffsetting txonxoff; +{ + register struct ssysdep_conn *q; + boolean fchanged, fdo; + int iset = 0; + int iclear = 0; + + q = (struct ssysdep_conn *) qconn->psysdep; + + if (! q->fterminal) + return TRUE; + + fchanged = FALSE; + + /* Set the parity for output characters. */ + +#if HAVE_BSD_TTY + + /* This will also cause parity detection on input characters. */ + + fdo = FALSE; + switch (tparity) + { + case PARITYSETTING_DEFAULT: + break; + case PARITYSETTING_NONE: +#if HAVE_PARITY_BUG + /* The Sony NEWS mishandles this for some reason. */ + iset = 0; + iclear = ANYP; +#else + iset = ANYP; + iclear = 0; +#endif + fdo = TRUE; + break; + case PARITYSETTING_EVEN: + iset = EVENP; + iclear = ODDP; + fdo = TRUE; + break; + case PARITYSETTING_ODD: + iset = ODDP; + iclear = EVENP; + fdo = TRUE; + break; + case PARITYSETTING_MARK: + case PARITYSETTING_SPACE: + /* Not supported. */ + break; + } + + if (fdo) + { + if ((q->snew.stty.sg_flags & iset) != iset + || (q->snew.stty.sg_flags & iclear) != 0) + { + q->snew.stty.sg_flags |= iset; + q->snew.stty.sg_flags &=~ iclear; + fchanged = TRUE; + } + } + +#else /* ! HAVE_BSD_TTY */ + + fdo = FALSE; + switch (tparity) + { + case PARITYSETTING_DEFAULT: + break; + case PARITYSETTING_NONE: + iset = CS8; + iclear = PARENB | PARODD | (CSIZE &~ CS8); + fdo = TRUE; + break; + case PARITYSETTING_EVEN: + iset = PARENB | CS7; + iclear = PARODD | (CSIZE &~ CS7); + fdo = TRUE; + break; + case PARITYSETTING_ODD: + iset = PARENB | PARODD | CS7; + iclear = CSIZE &~ CS7; + fdo = TRUE; + break; + case PARITYSETTING_MARK: + case PARITYSETTING_SPACE: + /* Not supported. */ + break; + } + + if (fdo) + { + if ((q->snew.c_cflag & iset) != iset + || (q->snew.c_cflag & iclear) != 0) + { + q->snew.c_cflag |= iset; + q->snew.c_cflag &=~ iclear; + fchanged = TRUE; + } + } + +#endif /* ! HAVE_BSD_TTY */ + + /* Set whether input characters are stripped to seven bits. */ + +#if HAVE_BSD_TTY + +#ifdef LPASS8 + { + int i; + + i = LPASS8; + if (tstrip == STRIPSETTING_EIGHTBITS) + { + i = LPASS8; + (void) ioctl (q->o, TIOCLBIS, &i); + } + else if (tstrip == STRIPSETTING_SEVENBITS) + { + i = LPASS8; + (void) ioctl (q->o, TIOCLBIC, &i); + } + } +#endif + +#else /* ! HAVE_BSD_TTY */ + + fdo = FALSE; + switch (tstrip) + { + case STRIPSETTING_DEFAULT: + break; + case STRIPSETTING_EIGHTBITS: + iset = 0; + iclear = ISTRIP; + fdo = TRUE; + break; + case STRIPSETTING_SEVENBITS: + iset = ISTRIP; + iclear = 0; + fdo = TRUE; + break; + } + + if (fdo) + { + if ((q->snew.c_iflag & iset) != iset + || (q->snew.c_iflag & iclear) != 0) + { + q->snew.c_iflag |= iset; + q->snew.c_iflag &=~ iclear; + fchanged = TRUE; + } + } + +#endif /* ! HAVE_BSD_TTY */ + + /* Set XON/XOFF handshaking. */ + +#if HAVE_BSD_TTY + + fdo = FALSE; + switch (txonxoff) + { + case XONXOFF_DEFAULT: + break; + case XONXOFF_OFF: + iset = RAW; + iclear = TANDEM | CBREAK; + fdo = TRUE; + break; + case XONXOFF_ON: + iset = CBREAK | TANDEM; + iclear = RAW; + fdo = TRUE; + break; + } + + if (fdo) + { + if ((q->snew.stty.sg_flags & iset) != iset + || (q->snew.stty.sg_flags & iclear) != 0) + { + q->snew.stty.sg_flags |= iset; + q->snew.stty.sg_flags &=~ iclear; + fchanged = TRUE; + } + } + +#else /* ! HAVE_BSD_TTY */ + + fdo = FALSE; + switch (txonxoff) + { + case XONXOFF_DEFAULT: + break; + case XONXOFF_OFF: + iset = 0; + iclear = IXON | IXOFF; + fdo = TRUE; + break; + case XONXOFF_ON: +#ifdef CRTSCTS +#if HAVE_POSIX_TERMIOS + /* This is system dependent, but I haven't figured out a good + way around it yet. If we are doing hardware flow control, we + don't send XON/XOFF characters but we do recognize them. */ + if ((q->snew.c_cflag & CRTSCTS) != 0) + { + iset = IXON; + iclear = IXOFF; + fdo = TRUE; + break; + } +#endif /* HAVE_POSIX_TERMIOS */ +#endif /* defined (CRTSCTS) */ + iset = IXON | IXOFF; + iclear = 0; + fdo = TRUE; + break; + } + + if (fdo) + { + if ((q->snew.c_iflag & iset) != iset + || (q->snew.c_iflag & iclear) != 0) + { + q->snew.c_iflag |= iset; + q->snew.c_iflag &=~ iclear; + fchanged = TRUE; + } + } + +#endif /* ! HAVE_BSD_TTY */ + + if (fchanged) + { + if (! fsetterminfodrain (q->o, &q->snew)) + { + ulog (LOG_ERROR, "Can't change terminal settings: %s", + strerror (errno)); + return FALSE; + } + } + +#if HAVE_BSD_TTY + if (txonxoff == XONXOFF_ON + && (q->snew.stty.sg_flags & ANYP) == ANYP) + { + int i; + + /* At least on Ultrix, we seem to have to set LLITOUT and + LPASS8. This shouldn't foul things up anywhere else. As far + as I can tell, this has to be done after setting the terminal + into cbreak mode, not before. */ +#ifndef LLITOUT +#define LLITOUT 0 +#endif +#ifndef LPASS8 +#define LPASS8 0 +#endif +#ifndef LAUTOFLOW +#define LAUTOFLOW 0 +#endif + i = LLITOUT | LPASS8 | LAUTOFLOW; + (void) ioctl (q->o, TIOCLBIS, &i); + +#if HAVE_STRIP_BUG + /* Ultrix 4.0 has a peculiar problem: setting CBREAK always + causes input characters to be stripped. I hope this does not + apply to other BSD systems. It is possible to work around + this by using the termio call. I wish this sort of stuff was + not necessary!!! */ + { + struct termio s; + + if (ioctl (q->o, TCGETA, &s) >= 0) + { + s.c_iflag &=~ ISTRIP; + (void) ioctl (q->o, TCSETA, &s); + } + } +#endif /* HAVE_STRIP_BUG */ + } +#endif /* HAVE_BSD_TTY */ + + return TRUE; +} + +/* Change settings of a stdin port. */ + +static boolean +fsstdin_set (qconn, tparity, tstrip, txonxoff) + struct sconnection *qconn; + enum tparitysetting tparity; + enum tstripsetting tstrip; + enum txonxoffsetting txonxoff; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + qsysdep->o = 0; + return fsserial_set (qconn, tparity, tstrip, txonxoff); +} + +/* Run a chat program. */ + +static boolean +fsrun_chat (oread, owrite, pzprog) + int oread; + int owrite; + char **pzprog; +{ + int aidescs[3]; + FILE *e; + pid_t ipid; + char *z; + size_t c; + + aidescs[0] = oread; + aidescs[1] = owrite; + aidescs[2] = SPAWN_READ_PIPE; + + /* Pass fkeepuid, fkeepenv and fshell as TRUE. This puts the + responsibility of maintaing security on the chat program. */ + ipid = ixsspawn ((const char **) pzprog, aidescs, TRUE, TRUE, + (const char *) NULL, FALSE, TRUE, (const char *) NULL, + (const char *) NULL, (const char *) NULL); + if (ipid < 0) + { + ulog (LOG_ERROR, "ixsspawn (%s): %s", pzprog[0], strerror (errno)); + return FALSE; + } + + e = fdopen (aidescs[2], (char *) "r"); + if (e == NULL) + { + ulog (LOG_ERROR, "fdopen: %s", strerror (errno)); + (void) close (aidescs[2]); + (void) kill (ipid, SIGKILL); + (void) ixswait ((unsigned long) ipid, (const char *) NULL); + return FALSE; + } + + /* The FILE e now is attached to stderr of the program. Forward + every line the program outputs to the log file. */ + z = NULL; + c = 0; + while (getline (&z, &c, e) > 0) + { + size_t clen; + + clen = strlen (z); + if (z[clen - 1] == '\n') + z[clen - 1] = '\0'; + if (*z != '\0') + ulog (LOG_NORMAL, "chat: %s", z); + } + + xfree ((pointer) z); + (void) fclose (e); + + return ixswait ((unsigned long) ipid, "Chat program") == 0; +} + +/* Run a chat program on a stdin port. */ + +/*ARGSUSED*/ +static boolean +fsstdin_chat (qconn, pzprog) + struct sconnection *qconn; + char **pzprog; +{ + return fsrun_chat (0, 1, pzprog); +} + +/* Run a chat program on any general type of connection. */ + +boolean +fsysdep_conn_chat (qconn, pzprog) + struct sconnection *qconn; + char **pzprog; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + return fsrun_chat (qsysdep->o, qsysdep->o, pzprog); +} + +/* Return baud rate of a serial port. */ + +static long +isserial_baud (qconn) + struct sconnection *qconn; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + return qsysdep->ibaud; +} diff --git a/gnu/libexec/uucp/libunix/signal.c b/gnu/libexec/uucp/libunix/signal.c new file mode 100644 index 0000000000..33e24a7245 --- /dev/null +++ b/gnu/libexec/uucp/libunix/signal.c @@ -0,0 +1,208 @@ +/* signal.c + Signal handling routines. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +/* Signal handling routines. When we catch a signal, we want to set + the appropriate elements of afSignal and afLog_signal to TRUE. If + we are on a system which restarts system calls, we may also want to + longjmp out. On a system which does not restart system calls, + these signal handling routines are well-defined by ANSI C. */ + +#if HAVE_RESTARTABLE_SYSCALLS +volatile sig_atomic_t fSjmp; +volatile jmp_buf sSjmp_buf; +#endif /* HAVE_RESTARTABLE_SYSCALLS */ + +/* Some systems, such as SunOS, have a SA_INTERRUPT bit that must be + set in the sigaction structure to force system calls to be + interrupted. */ +#ifndef SA_INTERRUPT +#define SA_INTERRUPT 0 +#endif + +/* The SVR3 sigset function can be called just like signal, unless + system calls are restarted which is extremely unlikely; we prevent + this case in sysh.unx. */ +#if HAVE_SIGSET && ! HAVE_SIGACTION && ! HAVE_SIGVEC +#define signal sigset +#endif + +/* The sigvec structure changed from 4.2BSD to 4.3BSD. These macros + make the 4.3 code backward compatible. */ +#ifndef SV_INTERRUPT +#define SV_INTERRUPT 0 +#endif +#if ! HAVE_SIGVEC_SV_FLAGS +#define sv_flags sv_onstack +#endif + +/* Catch a signal. Reinstall the signal handler if necessary, set the + appropriate variables, and do a longjmp if necessary. */ + +RETSIGTYPE +ussignal (isig) + int isig; +{ + int iindex; + +#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET + (void) signal (isig, ussignal); +#endif + + switch (isig) + { + default: iindex = INDEXSIG_SIGHUP; break; +#ifdef SIGINT + case SIGINT: iindex = INDEXSIG_SIGINT; break; +#endif +#ifdef SIGQUIT + case SIGQUIT: iindex = INDEXSIG_SIGQUIT; break; +#endif +#ifdef SIGTERM + case SIGTERM: iindex = INDEXSIG_SIGTERM; break; +#endif +#ifdef SIGPIPE + case SIGPIPE: iindex = INDEXSIG_SIGPIPE; break; +#endif + } + + afSignal[iindex] = TRUE; + afLog_signal[iindex] = TRUE; + +#if HAVE_RESTARTABLE_SYSCALLS + if (fSjmp) + longjmp (sSjmp_buf, 1); +#endif /* HAVE_RESTARTABLE_SYSCALLS */ +} + +/* Prepare to catch a signal. This is basically the ANSI C routine + signal, but it uses sigaction or sigvec instead if they are + available. If fforce is FALSE, we do not set the signal if it is + currently being ignored. If pfignored is not NULL and fforce is + FALSE, then *pfignored will be set to TRUE if the signal was + previously being ignored (if fforce is TRUE the value returned in + *pfignored is meaningless). If we can't change the signal handler + we give a fatal error. */ + +void +usset_signal (isig, pfn, fforce, pfignored) + int isig; + RETSIGTYPE (*pfn) P((int)); + boolean fforce; + boolean *pfignored; +{ +#if HAVE_SIGACTION + + struct sigaction s; + + if (! fforce) + { + (void) (sigemptyset (&s.sa_mask)); + if (sigaction (isig, (struct sigaction *) NULL, &s) != 0) + ulog (LOG_FATAL, "sigaction (%d): %s", isig, strerror (errno)); + + if (s.sa_handler == SIG_IGN) + { + if (pfignored != NULL) + *pfignored = TRUE; + return; + } + + if (pfignored != NULL) + *pfignored = FALSE; + } + + s.sa_handler = pfn; + (void) (sigemptyset (&s.sa_mask)); + s.sa_flags = SA_INTERRUPT; + + if (sigaction (isig, &s, (struct sigaction *) NULL) != 0) + ulog (LOG_FATAL, "sigaction (%d): %s", isig, strerror (errno)); + +#else /* ! HAVE_SIGACTION */ +#if HAVE_SIGVEC + + struct sigvec s; + + if (! fforce) + { + if (sigvec (isig, (struct sigvec *) NULL, &s) != 0) + ulog (LOG_FATAL, "sigvec (%d): %s", isig, strerror (errno)); + + if (s.sv_handler == SIG_IGN) + { + if (pfignored != NULL) + *pfignored = TRUE; + return; + } + + if (pfignored != NULL) + *pfignored = FALSE; + } + + s.sv_handler = pfn; + s.sv_mask = 0; + s.sv_flags = SV_INTERRUPT; + + if (sigvec (isig, &s, (struct sigvec *) NULL) != 0) + ulog (LOG_FATAL, "sigvec (%d): %s", isig, strerror (errno)); + +#else /* ! HAVE_SIGVEC */ + + if (! fforce) + { + if (signal (isig, SIG_IGN) == SIG_IGN) + { + if (pfignored != NULL) + *pfignored = TRUE; + return; + } + + if (pfignored != NULL) + *pfignored = FALSE; + } + + (void) signal (isig, pfn); + +#endif /* ! HAVE_SIGVEC */ +#endif /* ! HAVE_SIGACTION */ +} + +/* The routine called by the system independent code, which always + uses the same signal handler. */ + +void +usysdep_signal (isig) + int isig; +{ + usset_signal (isig, ussignal, FALSE, (boolean *) NULL); +} diff --git a/gnu/libexec/uucp/libunix/sindir.c b/gnu/libexec/uucp/libunix/sindir.c new file mode 100644 index 0000000000..d987508189 --- /dev/null +++ b/gnu/libexec/uucp/libunix/sindir.c @@ -0,0 +1,26 @@ +/* sindir.c + Stick a directory and file name together. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +char * +zsysdep_in_dir (zdir, zfile) + const char *zdir; + const char *zfile; +{ + size_t cdir, cfile; + char *zret; + + cdir = strlen (zdir); + cfile = strlen (zfile); + zret = zbufalc (cdir + cfile + 2); + memcpy (zret, zdir, cdir); + memcpy (zret + cdir + 1, zfile, cfile); + zret[cdir] = '/'; + zret[cdir + cfile + 1] = '\0'; + return zret; +} diff --git a/gnu/libexec/uucp/libunix/size.c b/gnu/libexec/uucp/libunix/size.c new file mode 100644 index 0000000000..8d021db3cd --- /dev/null +++ b/gnu/libexec/uucp/libunix/size.c @@ -0,0 +1,27 @@ +/* size.c + Get the size in bytes of a file. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +long +csysdep_size (zfile) + const char *zfile; +{ + struct stat s; + + if (stat ((char *) zfile, &s) < 0) + { + if (errno == ENOENT) + return -1; + ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno)); + return -2; + } + + return s.st_size; +} diff --git a/gnu/libexec/uucp/libunix/sleep.c b/gnu/libexec/uucp/libunix/sleep.c new file mode 100644 index 0000000000..b232f9674f --- /dev/null +++ b/gnu/libexec/uucp/libunix/sleep.c @@ -0,0 +1,14 @@ +/* sleep.c + Sleep for a number of seconds. */ + +#include "uucp.h" + +#include "sysdep.h" +#include "system.h" + +void +usysdep_sleep (c) + int c; +{ + (void) sleep (c); +} diff --git a/gnu/libexec/uucp/libunix/spawn.c b/gnu/libexec/uucp/libunix/spawn.c new file mode 100644 index 0000000000..7ab080d1a9 --- /dev/null +++ b/gnu/libexec/uucp/libunix/spawn.c @@ -0,0 +1,398 @@ +/* spawn.c + Spawn a program securely. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" + +#include + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +#ifndef environ +extern char **environ; +#endif + +/* Spawn a child in a fairly secure fashion. This returns the process + ID of the child or -1 on error. It takes far too many arguments: + + pazargs -- arguments (element 0 is command) + aidescs -- file descriptors for stdin, stdout and stderr + fkeepuid -- TRUE if euid should be left unchanged + fkeepenv -- TRUE if environment should be left unmodified + zchdir -- directory to chdir to + fnosigs -- TRUE if child should ignore SIGHUP, SIGINT and SIGQUIT + fshell -- TRUE if should try /bin/sh if execve gets ENOEXEC + zpath -- value for environment variable PATH + zuu_machine -- value for environment variable UU_MACHINE + zuu_user -- value for environment variable UU_USER + + The aidescs array is three elements long. 0 is stdin, 1 is stdout + and 2 is stderr. The array may contain either file descriptor + numbers to dup appropriately, or one of the following: + + SPAWN_NULL -- set descriptor to /dev/null + SPAWN_READ_PIPE -- set aidescs element to pipe for parent to read + SPAWN_WRITE_PIPE -- set aidescs element to pipe for parent to write + + If fkeepenv is FALSE, a standard environment is created. The + environment arguments (zpath, zuu_machine and zuu_user) are only + used if fkeepenv is FALSE; any of them may be NULL. + + This routine expects that all file descriptors have been set to + close-on-exec, so it doesn't have to worry about closing them + explicitly. It sets the close-on-exec flag for the new pipe + descriptors it returns. */ + +pid_t +ixsspawn (pazargs, aidescs, fkeepuid, fkeepenv, zchdir, fnosigs, fshell, + zpath, zuu_machine, zuu_user) + const char **pazargs; + int aidescs[3]; + boolean fkeepuid; + boolean fkeepenv; + const char *zchdir; + boolean fnosigs; + boolean fshell; + const char *zpath; + const char *zuu_machine; + const char *zuu_user; +{ + char *zshcmd; + int i; + char *azenv[9]; + char **pazenv; + boolean ferr; + int ierr = 0; + int onull; + int aichild_descs[3]; + int cpar_close; + int aipar_close[4]; + int cchild_close; + int aichild_close[3]; + pid_t iret = 0; + const char *zcmd; + + /* If we might have to use the shell, allocate enough space for the + quoted command before forking. Otherwise the allocation would + modify the data segment and we could not safely use vfork. */ + zshcmd = NULL; + if (fshell) + { + size_t clen; + + clen = 0; + for (i = 0; pazargs[i] != NULL; i++) + clen += strlen (pazargs[i]); + zshcmd = zbufalc (2 * clen + i); + } + + /* Set up a standard environment. This is again done before forking + because it will modify the data segment. */ + if (fkeepenv) + pazenv = environ; + else + { + const char *zterm, *ztz; + char *zspace; + int ienv; + + if (zpath == NULL) + zpath = CMDPATH; + + azenv[0] = zbufalc (sizeof "PATH=" + strlen (zpath)); + sprintf (azenv[0], "PATH=%s", zpath); + zspace = azenv[0] + sizeof "PATH=" - 1; + while ((zspace = strchr (zspace, ' ')) != NULL) + *zspace = ':'; + + azenv[1] = zbufalc (sizeof "HOME=" + strlen (zSspooldir)); + sprintf (azenv[1], "HOME=%s", zSspooldir); + + zterm = getenv ("TERM"); + if (zterm == NULL) + zterm = "unknown"; + azenv[2] = zbufalc (sizeof "TERM=" + strlen (zterm)); + sprintf (azenv[2], "TERM=%s", zterm); + + azenv[3] = zbufcpy ("SHELL=/bin/sh"); + + azenv[4] = zbufalc (sizeof "USER=" + strlen (OWNER)); + sprintf (azenv[4], "USER=%s", OWNER); + + ienv = 5; + + ztz = getenv ("TZ"); + if (ztz != NULL) + { + azenv[ienv] = zbufalc (sizeof "TZ=" + strlen (ztz)); + sprintf (azenv[ienv], "TZ=%s", ztz); + ++ienv; + } + + if (zuu_machine != NULL) + { + azenv[ienv] = zbufalc (sizeof "UU_MACHINE=" + + strlen (zuu_machine)); + sprintf (azenv[ienv], "UU_MACHINE=%s", zuu_machine); + ++ienv; + } + + if (zuu_user != NULL) + { + azenv[ienv] = zbufalc (sizeof "UU_USER=" + + strlen (zuu_user)); + sprintf (azenv[ienv], "UU_USER=%s", zuu_user); + ++ienv; + } + + azenv[ienv] = NULL; + pazenv = azenv; + } + + /* Set up any needed pipes. */ + + ferr = FALSE; + onull = -1; + cpar_close = 0; + cchild_close = 0; + + for (i = 0; i < 3; i++) + { + if (aidescs[i] == SPAWN_NULL) + { + if (onull < 0) + { + onull = open ((char *) "/dev/null", O_RDWR); + if (onull < 0 + || fcntl (onull, F_SETFD, + fcntl (onull, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ierr = errno; + (void) close (onull); + ferr = TRUE; + break; + } + aipar_close[cpar_close] = onull; + ++cpar_close; + } + aichild_descs[i] = onull; + } + else if (aidescs[i] != SPAWN_READ_PIPE + && aidescs[i] != SPAWN_WRITE_PIPE) + aichild_descs[i] = aidescs[i]; + else + { + int aipipe[2]; + + if (pipe (aipipe) < 0) + { + ierr = errno; + ferr = TRUE; + break; + } + + if (aidescs[i] == SPAWN_READ_PIPE) + { + aidescs[i] = aipipe[0]; + aichild_close[cchild_close] = aipipe[0]; + aichild_descs[i] = aipipe[1]; + aipar_close[cpar_close] = aipipe[1]; + } + else + { + aidescs[i] = aipipe[1]; + aichild_close[cchild_close] = aipipe[1]; + aichild_descs[i] = aipipe[0]; + aipar_close[cpar_close] = aipipe[0]; + } + + ++cpar_close; + ++cchild_close; + + if (fcntl (aidescs[i], F_SETFD, + fcntl (aidescs[i], F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ierr = errno; + ferr = TRUE; + break; + } + } + } + +#if DEBUG > 1 + if (! ferr && FDEBUGGING (DEBUG_EXECUTE)) + { + ulog (LOG_DEBUG_START, "Forking %s", pazargs[0]); + for (i = 1; pazargs[i] != NULL; i++) + ulog (LOG_DEBUG_CONTINUE, " %s", pazargs[i]); + ulog (LOG_DEBUG_END, "%s", ""); + } +#endif + + if (! ferr) + { + /* This should really be vfork if available. */ + iret = ixsfork (); + if (iret < 0) + { + ferr = TRUE; + ierr = errno; + } + } + + if (ferr) + { + for (i = 0; i < cchild_close; i++) + (void) close (aichild_close[i]); + iret = -1; + } + + if (iret != 0) + { + /* The parent. Close the child's ends of the pipes and return + the process ID, or an error. */ + for (i = 0; i < cpar_close; i++) + (void) close (aipar_close[i]); + ubuffree (zshcmd); + if (! fkeepenv) + { + char **pz; + + for (pz = azenv; *pz != NULL; pz++) + ubuffree (*pz); + } + errno = ierr; + return iret; + } + + /* The child. */ + +#ifdef STDIN_FILENO +#if STDIN_FILENO != 0 || STDOUT_FILENO != 1 || STDERR_FILENO != 2 + #error The following code makes invalid assumptions +#endif +#endif + + for (i = 0; i < 3; i++) + { + if (aichild_descs[i] != i) + (void) dup2 (aichild_descs[i], i); + /* This should only be necessary if aichild_descs[i] == i, but + some systems copy the close-on-exec flag for a dupped + descriptor, which is wrong according to POSIX. */ + (void) fcntl (i, F_SETFD, fcntl (i, F_GETFD, 0) &~ FD_CLOEXEC); + } + + zcmd = pazargs[0]; + pazargs[0] = strrchr (zcmd, '/'); + if (pazargs[0] == NULL) + pazargs[0] = zcmd; + else + ++pazargs[0]; + + if (! fkeepuid) + { + (void) setuid (getuid ()); + (void) setgid (getgid ()); + } + + if (zchdir != NULL) + (void) chdir (zchdir); + + if (fnosigs) + { +#ifdef SIGHUP + (void) signal (SIGHUP, SIG_IGN); +#endif +#ifdef SIGINT + (void) signal (SIGINT, SIG_IGN); +#endif +#ifdef SIGQUIT + (void) signal (SIGQUIT, SIG_IGN); +#endif + } + + (void) execve ((char *) zcmd, (char **) pazargs, pazenv); + + /* The exec failed. If permitted, try using /bin/sh to execute a + shell script. */ + + if (errno == ENOEXEC && fshell) + { + char *zto; + const char *azshargs[4]; + + pazargs[0] = zcmd; + zto = zshcmd; + for (i = 0; pazargs[i] != NULL; i++) + { + const char *zfrom; + + for (zfrom = pazargs[i]; *zfrom != '\0'; zfrom++) + { + /* Some versions of /bin/sh appear to have a bug such + that quoting a '/' sometimes causes an error. I + don't know exactly when this happens (I can recreate + it on Ultrix 4.0), but in any case it is harmless to + not quote a '/'. */ + if (*zfrom != '/') + *zto++ = '\\'; + *zto++ = *zfrom; + } + *zto++ = ' '; + } + *(zto - 1) = '\0'; + + azshargs[0] = "sh"; + azshargs[1] = "-c"; + azshargs[2] = zshcmd; + azshargs[3] = NULL; + + (void) execve ((char *) "/bin/sh", (char **) azshargs, pazenv); + } + + _exit (EXIT_FAILURE); + + /* Avoid compiler warning. */ + return -1; +} diff --git a/gnu/libexec/uucp/libunix/splcmd.c b/gnu/libexec/uucp/libunix/splcmd.c new file mode 100644 index 0000000000..9f6616a36d --- /dev/null +++ b/gnu/libexec/uucp/libunix/splcmd.c @@ -0,0 +1,115 @@ +/* splcmd.c + Spool a command. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" + +#include +#include + +/* Given a set of commands to execute for a remote system, create a + command file holding them. This creates a single command file + holding all the commands passed in. It returns a jobid. */ + +char * +zsysdep_spool_commands (qsys, bgrade, ccmds, pascmds) + const struct uuconf_system *qsys; + int bgrade; + int ccmds; + const struct scmd *pascmds; +{ + char *z; + FILE *e; + int i; + const struct scmd *q; + char *zjobid; + +#if DEBUG > 0 + if (! UUCONF_GRADE_LEGAL (bgrade)) + ulog (LOG_FATAL, "Bad grade %d", bgrade); +#endif + + z = zscmd_file (qsys, bgrade); + if (z == NULL) + return NULL; + + e = esysdep_fopen (z, FALSE, FALSE, TRUE); + if (e == NULL) + { + ubuffree (z); + return NULL; + } + + for (i = 0, q = pascmds; i < ccmds; i++, q++) + { + switch (q->bcmd) + { + case 'S': + fprintf (e, "S %s %s %s -%s %s 0%o %s\n", q->zfrom, q->zto, + q->zuser, q->zoptions, q->ztemp, q->imode, + q->znotify == NULL ? (const char *) "" : q->znotify); + break; + case 'R': + fprintf (e, "R %s %s %s -%s\n", q->zfrom, q->zto, q->zuser, + q->zoptions); + break; + case 'X': + fprintf (e, "X %s %s %s -%s\n", q->zfrom, q->zto, q->zuser, + q->zoptions); + break; + case 'E': + fprintf (e, "E %s %s %s -%s %s 0%o %s 0 %s\n", q->zfrom, q->zto, + q->zuser, q->zoptions, q->ztemp, q->imode, + q->znotify, q->zcmd); + break; + default: + ulog (LOG_ERROR, + "zsysdep_spool_commands: Unrecognized type %d", + q->bcmd); + (void) fclose (e); + (void) remove (z); + ubuffree (z); + return NULL; + } + } + + if (fclose (e) != 0) + { + ulog (LOG_ERROR, "fclose: %s", strerror (errno)); + (void) remove (z); + ubuffree (z); + return NULL; + } + + zjobid = zsfile_to_jobid (qsys, z, bgrade); + if (zjobid == NULL) + (void) remove (z); + ubuffree (z); + return zjobid; +} diff --git a/gnu/libexec/uucp/libunix/splnam.c b/gnu/libexec/uucp/libunix/splnam.c new file mode 100644 index 0000000000..06ce3605ce --- /dev/null +++ b/gnu/libexec/uucp/libunix/splnam.c @@ -0,0 +1,19 @@ +/* splnam.c + Get the full name of a file in the spool directory. */ + +#include "uucp.h" + +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" + +/* Get the real name of a spool file. */ + +char * +zsysdep_spool_file_name (qsys, zfile, pseq) + const struct uuconf_system *qsys; + const char *zfile; + pointer pseq; +{ + return zsfind_file (zfile, qsys->uuconf_zname, bsgrade (pseq)); +} diff --git a/gnu/libexec/uucp/libunix/spool.c b/gnu/libexec/uucp/libunix/spool.c new file mode 100644 index 0000000000..f28229fe4e --- /dev/null +++ b/gnu/libexec/uucp/libunix/spool.c @@ -0,0 +1,420 @@ +/* spool.c + Find a file in the spool directory. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char spool_rcsid[] = "$Id: spool.c,v 1.1 1993/08/04 19:33:02 jtc Exp $"; +#endif + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +/* There are several types of files that go in the spool directory, + and they go into various different subdirectories. Whenever the + system name LOCAL appears below, it means whatever the local system + name is. + + Command files + These contain instructions for uucico indicating what files to transfer + to and from what systems. Each line of a work file is a command + beginning with S, R or X. + #if ! SPOOLDIR_TAYLOR + They are named C.ssssssgqqqq, where ssssss is the system name to + transfer to or from, g is the grade and qqqq is the sequence number. + #if SPOOLDIR_V2 + They are put in the spool directory. + #elif SPOOLDIR_BSD42 || SPOOLDIR_BSD43 + They are put in the directory "C.". + #elif SPOOLDIR_HDB + They are put in a directory named for the system for which they were + created. + #elif SPOOLDIR_ULTRIX + If the directory sys/ssssss exists, they are put in the directory + sys/ssssss/C; otherwise, they are put in the directory sys/DEFAULT/C. + #endif + #elif SPOOLDIR_SVR4 + They are put in the directory sys/g, where sys is the system name + and g is the grade. + #endif + #else SPOOLDIR_TAYLOR + They are named C.gqqqq, where g is the grade and qqqq is the sequence + number, and are placed in the directory ssssss/C. where ssssss is + the system name to transfer to or from. + #endif + + Data files + There are files to be transferred to other systems. Some files to + be transferred may not be in the spool directory, depending on how + uucp was invoked. Data files are named in work files, so it is + never necessary to look at them directly (except to remove old ones); + it is only necessary to create them. These means that the many + variations in naming are inconsequential. + #if ! SPOOLDIR_TAYLOR + They are named D.ssssssgqqqq where ssssss is a system name (which + may be LOCAL for locally initiated transfers or a remote system for + remotely initiated transfers, except that HDB appears to use the + system the file is being transferred to), g is the grade and qqqq + is the sequence number. Some systems use a trailing subjob ID + number, but we currently do not. The grade is not important, and + some systems do not use it. If the data file is to become an + execution file on another system the grade (if present) will be + 'X'. Otherwise Ultrix appears to use 'b'; the uux included with + gnuucp 1.0 appears to use 'S'; SCO does not appear to use a grade, + although it does use a subjob ID number. + #if SPOOLDIR_V2 + They are put in the spool directory. + #elif SPOOLDIR_BSD42 + If the name begins with D.LOCAL, the file is put in the directory + D.LOCAL. Otherwise the file is put in the directory D.. + #elif SPOOLDIR_BSD43 + If the name begins with D.LOCALX, the file is put in the directory + D.LOCALX. Otherwise if the name begins with D.LOCAL, the file is + put in the directory D.LOCAL Otherwise the file is put in the + directory "D.". + #elif SPOOLDIR_HDB + They are put in a directory named for the system for which they + were created. + #elif SPOOLDIR_ULTRIX + Say the file is being transferred to system REMOTE. If the + directory sys/REMOTE exists, then if the file begins with D.LOCALX + it is put in sys/REMOTE/D.LOCALX, if the file begins with D.LOCAL + it is put in sys/REMOTE/D.LOCAL, and otherwise it is put in + "sys/REMOTE/D.". If the directory sys/REMOTE does not exist, the + same applies except that DEFAULT is used instead of REMOTE. + #elif SPOOLDIR_SVR4 + They are put in the directory sys/g, where sys is the system name + and g is the grade. + #endif + #else SPOOLDIR_TAYLOR + If the file is to become an executable file on another system it is + named D.Xqqqq, otherwise it is named D.qqqq where in both cases + qqqq is a sequence number. If the corresponding C. file is in + directory ssssss/C., a D.X file is placed in ssssss/D.X and a D. + file is placed in "ssssss/D.". + #endif + + Execute files + These are files that specify programs to be executed. They are + created by uux, perhaps as run on another system. These names are + important, because a file transfer done to an execute file name + causes an execution to occur. The name is X.ssssssgqqqq, where + ssssss is the requesting system, g is the grade, and qqqq is a + sequence number. + #if SPOOLDIR_V2 || SPOOLDIR_BSD42 + These files are placed in the spool directory. + #elif SPOOLDIR_BSD43 + These files are placed in the directory X.. + #elif SPOOLDIR_HDB || SPOOLDIR_SVR4 + These files are put in a directory named for the system for which + the files were created. + #elif SPOOLDIR_ULTRIX + If there is a spool directory (sys/ssssss) for the requesting + system, the files are placed in sys/ssssss/X.; otherwise, the files + are placed in "sys/DEFAULT/X.". + #elif SPOOLDIR_TAYLOR + The system name is automatically truncated to seven characters when + a file is created. The files are placed in the subdirectory X. of + a directory named for the system for which the files were created. + #endif + + Temporary receive files + These are used when receiving files from another system. They are + later renamed to the final name. The actual name is unimportant, + although it generally begins with TM.. + #if SPOOLDIR_V2 || SPOOLDIR_BSD42 + These files are placed in the spool directory. + #elif SPOOLDIR_BSD43 || SPOOLDIR_ULTRIX || SPOOLDIR_TAYLOR + These files are placed in the directory .Temp. + #elif SPOOLDIR_HDB || SPOOLDIR_SVR4 + These files are placed in a directory named for the system for + which they were created. + #endif + + System status files + These are used to record when the last call was made to the system + and what the status is. They are used to prevent frequent recalls + to a system which is not responding. I will not attempt to + recreate the format of these exactly, since they are not all that + important. They will be put in the directory .Status, as in HDB, + and they use the system name as the name of the file. + + Sequence file + This is used to generate a unique sequence number. It contains an + ASCII number. + #if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 + The file is named SEQF and is kept in the spool directory. + #elif SPOOLDIR_HDB || SPOOLDIR_SVR4 + A separate sequence file is kept for each system in the directory + .Sequence with the name of the system. + #elif SPOOLDIR_ULTRIX + Each system with a file sys/ssssss has a sequence file in + sys/ssssss/.SEQF. Other systems use sys/DEFAULT/.SEQF. + #else SPOOLDIR_TAYLOR + A sequence file named SEQF is kept in the directory ssssss for each + system. + #endif + */ + +/* Given the name of a file as specified in a UUCP command, and the + system for which this file has been created, return where to find + it in the spool directory. The file will begin with C. (a command + file), D. (a data file) or X. (an execution file). Under + SPOOLDIR_SVR4 we need to know the grade of the file created by the + local system; this is the bgrade argument, which is -1 for a file + from a remote system. */ + +/*ARGSUSED*/ +char * +zsfind_file (zsimple, zsystem, bgrade) + const char *zsimple; + const char *zsystem; + int bgrade; +{ + if (! fspool_file (zsimple)) + { + ulog (LOG_ERROR, "Unrecognized file name %s", zsimple); + return NULL; + } + +#if ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 && ! SPOOLDIR_TAYLOR + if (*zsimple == 'X') + { + size_t clen; + + /* Files beginning with X. are execute files. It is important + for security reasons that we know the system which created + the X. file. This is easy under SPOOLDIR_HDB or + SPOOLDIR_SVR4 SPOOLDIR_TAYLOR, because the file will be in a + directory named for the system. Under other schemes, we must + get the system name from the X. file name. To prevent + security violations, we set the system name directly here; + this will cause problems if the maximum file name length is + too short, but hopefully no problem will occur since any + System V systems will be using HDB or SVR4 or TAYLOR. */ + clen = strlen (zsimple); + if (clen <= 7 || strncmp (zsimple + 2, zsystem, clen - 7) != 0) + { + static char *zbuf; + static size_t cbuf; + size_t cwant; + + cwant = strlen (zsystem) + 8; + if (cwant > cbuf) + { + zbuf = (char *) xrealloc ((pointer) zbuf, cwant); + cbuf = cwant; + } + sprintf (zbuf, "X.%s%s", zsystem, + clen < 5 ? zsimple : zsimple + clen - 5); + zsimple = zbuf; + } + } +#endif /* ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 && ! SPOOLDIR_TAYLOR */ + +#if SPOOLDIR_V2 + /* V2 never uses subdirectories. */ + return zbufcpy (zsimple); +#endif /* SPOOLDIR_V2 */ + +#if SPOOLDIR_HDB + /* HDB always uses the system name as a directory. */ + return zsysdep_in_dir (zsystem, zsimple); +#endif /* SPOOLDIR_HDB */ + +#if SPOOLDIR_SVR4 + /* SVR4 uses grade directories within the system directory for local + command and data files. */ + if (bgrade < 0 || *zsimple == 'X') + return zsysdep_in_dir (zsystem, zsimple); + else + { + char abgrade[2]; + + abgrade[0] = bgrade; + abgrade[1] = '\0'; + return zsappend3 (zsystem, abgrade, zsimple); + } +#endif /* SPOOLDIR_SVR4 */ + +#if ! SPOOLDIR_V2 && ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 + switch (*zsimple) + { + case 'C': +#if SPOOLDIR_BSD42 || SPOOLDIR_BSD43 + return zsysdep_in_dir ("C.", zsimple); +#endif /* SPOOLDIR_BSD42 || SPOOLDIR_BSD43 */ +#if SPOOLDIR_ULTRIX + if (fsultrix_has_spool (zsystem)) + return zsappend4 ("sys", zsystem, "C.", zsimple); + else + return zsappend4 ("sys", "DEFAULT", "C.", zsimple); +#endif /* SPOOLDIR_ULTRIX */ +#if SPOOLDIR_TAYLOR + return zsappend3 (zsystem, "C.", zsimple); +#endif /* SPOOLDIR_TAYLOR */ + + case 'D': +#if SPOOLDIR_BSD42 || SPOOLDIR_BSD43 + { + size_t c; + boolean ftruncated; + + /* D.LOCAL in D.LOCAL/, others in D./. If BSD43, D.LOCALX in + D.LOCALX/. */ + ftruncated = TRUE; + if (strncmp (zsimple + 2, zSlocalname, strlen (zSlocalname)) == 0) + { + c = strlen (zSlocalname); + ftruncated = FALSE; + } + else if (strncmp (zsimple + 2, zSlocalname, 7) == 0) + c = 7; + else if (strncmp (zsimple + 2, zSlocalname, 6) == 0) + c = 6; + else + c = 0; +#if SPOOLDIR_BSD43 + if (c > 0 && zsimple[c + 2] == 'X') + c++; +#endif /* SPOOLDIR_BSD43 */ + if (c > 0) + { + char *zalloc; + + zalloc = zbufalc (c + 3); + memcpy (zalloc, zsimple, c + 2); + zalloc[c + 2] = '\0'; + + /* If we truncated the system name, and there is no existing + directory with the truncated name, then just use D.. */ + if (! ftruncated || fsysdep_directory (zalloc)) + { + char *zret; + + zret = zsysdep_in_dir (zalloc, zsimple); + ubuffree (zalloc); + return zret; + } + ubuffree (zalloc); + } + return zsysdep_in_dir ("D.", zsimple); + } +#endif /* SPOOLDIR_BSD42 || SPOOLDIR_BSD43 */ +#if SPOOLDIR_ULTRIX + { + size_t c; + boolean ftruncated; + char *zfree; + const char *zdir; + char *zret; + + /* D.LOCALX in D.LOCALX/, D.LOCAL in D.LOCAL/, others in D./. */ + ftruncated = TRUE; + if (strncmp (zsimple + 2, zSlocalname, strlen (zSlocalname)) == 0) + { + c = strlen (zSlocalname); + ftruncated = FALSE; + } + else if (strncmp (zsimple + 2, zSlocalname, 7) == 0) + c = 7; + else if (strncmp (zsimple + 2, zSlocalname, 6) == 0) + c = 6; + else + c = 0; + if (c > 0 && zsimple[c + 2] == 'X') + ++c; + if (c > 0) + { + zfree = zbufalc (c + 3); + memcpy (zfree, zsimple, c + 2); + zfree[c + 2] = '\0'; + zdir = zfree; + + /* If we truncated the name, and there is no directory for + the truncated name, then don't use it. */ + if (ftruncated) + { + char *zlook; + + zlook = zsappend3 ("sys", + (fsultrix_has_spool (zsystem) + ? zsystem + : "DEFAULT"), + zdir); + if (! fsysdep_directory (zlook)) + zdir = "D."; + ubuffree (zlook); + } + } + else + { + zfree = NULL; + zdir = "D."; + } + + zret = zsappend4 ("sys", + (fsultrix_has_spool (zsystem) + ? zsystem + : "DEFAULT"), + zdir, + zsimple); + ubuffree (zfree); + return zret; + } +#endif /* SPOOLDIR_ULTRIX */ +#if SPOOLDIR_TAYLOR + if (zsimple[2] == 'X') + return zsappend3 (zsystem, "D.X", zsimple); + else + return zsappend3 (zsystem, "D.", zsimple); +#endif /* SPOOLDIR_TAYLOR */ + + + case 'X': +#if SPOOLDIR_BSD42 + return zbufcpy (zsimple); +#endif +#if SPOOLDIR_BSD43 + return zsysdep_in_dir ("X.", zsimple); +#endif +#if SPOOLDIR_ULTRIX + return zsappend4 ("sys", + (fsultrix_has_spool (zsystem) + ? zsystem + : "DEFAULT"), + "X.", + zsimple); +#endif +#if SPOOLDIR_TAYLOR + return zsappend3 (zsystem, "X.", zsimple); +#endif + } + + /* This is just to avoid warnings; it will never be executed. */ + return NULL; +#endif /* ! SPOOLDIR_V2 && ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 */ +} diff --git a/gnu/libexec/uucp/libunix/srmdir.c b/gnu/libexec/uucp/libunix/srmdir.c new file mode 100644 index 0000000000..28487ef309 --- /dev/null +++ b/gnu/libexec/uucp/libunix/srmdir.c @@ -0,0 +1,112 @@ +/* srmdir.c + Remove a directory and all its contents. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +#if HAVE_FTW_H +#include +#endif + +static int isremove_dir P((const char *, const struct stat *, int)); + +/* Keep a list of directories to be removed. */ + +struct sdirlist +{ + struct sdirlist *qnext; + char *zdir; +}; + +static struct sdirlist *qSdirlist; + +/* Remove a directory and all files in it. */ + +boolean +fsysdep_rmdir (zdir) + const char *zdir; +{ + boolean fret; + struct sdirlist *q; + + qSdirlist = NULL; + + fret = TRUE; + if (ftw ((char *) zdir, isremove_dir, 5) != 0) + { + ulog (LOG_ERROR, "ftw: %s", strerror (errno)); + fret = FALSE; + } + + q = qSdirlist; + while (q != NULL) + { + struct sdirlist *qnext; + + if (rmdir (q->zdir) != 0) + { + ulog (LOG_ERROR, "rmdir (%s): %s", q->zdir, strerror (errno)); + fret = FALSE; + } + ubuffree (q->zdir); + qnext = q->qnext; + xfree ((pointer) q); + q = qnext; + } + + return fret; +} + +/* Remove a file in a directory. */ + +/*ARGSUSED*/ +static int +isremove_dir (zfile, qstat, iflag) + const char *zfile; + const struct stat *qstat; + int iflag; +{ + if (iflag == FTW_D || iflag == FTW_DNR) + { + struct sdirlist *q; + + q = (struct sdirlist *) xmalloc (sizeof (struct sdirlist)); + q->qnext = qSdirlist; + q->zdir = zbufcpy (zfile); + qSdirlist = q; + } + else + { + if (remove (zfile) != 0) + ulog (LOG_ERROR, "remove (%s): %s", zfile, strerror (errno)); + } + + return 0; +} diff --git a/gnu/libexec/uucp/libunix/statsb.c b/gnu/libexec/uucp/libunix/statsb.c new file mode 100644 index 0000000000..79a14c0e64 --- /dev/null +++ b/gnu/libexec/uucp/libunix/statsb.c @@ -0,0 +1,572 @@ +/* statsb.c + System dependent routines for uustat. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char statsb_rcsid[] = "$Id: statsb.c,v 1.1 1993/08/04 19:33:06 jtc Exp $"; +#endif + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" + +#include + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#if HAVE_OPENDIR +#if HAVE_DIRENT_H +#include +#else /* ! HAVE_DIRENT_H */ +#include +#define dirent direct +#endif /* ! HAVE_DIRENT_H */ +#endif /* HAVE_OPENDIR */ + +#if HAVE_TIME_H +#include +#endif + +#if HAVE_UTIME_H +#include +#endif + +/* Local functions. */ + +static int ussettime P((const char *z, time_t inow)); +static boolean fskill_or_rejuv P((pointer puuconf, const char *zid, + boolean fkill)); + +/* See whether the user is permitted to kill arbitrary jobs. This is + true only for root and uucp. We check for uucp by seeing if the + real user ID and the effective user ID are the same; this works + because we should be suid to uucp, so our effective user ID will + always be uucp while our real user ID will be whoever ran the + program. */ + +boolean +fsysdep_privileged () +{ + uid_t iuid; + + iuid = getuid (); + return iuid == 0 || iuid == geteuid (); +} + +/* Set file access time to the present. On many systems this could be + done by passing NULL to utime, but on some that doesn't work. This + routine is not time critical, so we never rely on NULL. */ + +static int +ussettime(z, inow) + const char *z; + time_t inow; +{ +#if HAVE_UTIME_H + struct utimbuf s; + + s.actime = inow; + s.modtime = inow; + return utime ((char *) z, &s); +#else + time_t ai[2]; + + ai[0] = inow; + ai[1] = inow; + return utime ((char *) z, ai); +#endif +} + +/* Kill a job, given the jobid. */ + +boolean +fsysdep_kill_job (puuconf, zid) + pointer puuconf; + const char *zid; +{ + return fskill_or_rejuv (puuconf, zid, TRUE); +} + +/* Rejuvenate a job, given the jobid. */ + +boolean +fsysdep_rejuvenate_job (puuconf, zid) + pointer puuconf; + const char *zid; +{ + return fskill_or_rejuv (puuconf, zid, FALSE); +} + +/* Kill or rejuvenate a job, given the jobid. */ + +static boolean +fskill_or_rejuv (puuconf, zid, fkill) + pointer puuconf; + const char *zid; + boolean fkill; +{ + char *zfile; + char *zsys; + char bgrade; + time_t inow = 0; + int iuuconf; + struct uuconf_system ssys; + FILE *e; + boolean fret; + char *zline; + size_t cline; + int isys; + + zfile = zsjobid_to_file (zid, &zsys, &bgrade); + if (zfile == NULL) + return FALSE; + + if (! fkill) + inow = time ((time_t *) NULL); + + iuuconf = uuconf_system_info (puuconf, zsys, &ssys); + if (iuuconf == UUCONF_NOT_FOUND) + { + if (! funknown_system (puuconf, zsys, &ssys)) + { + ulog (LOG_ERROR, "%s: Bad job id", zid); + ubuffree (zfile); + ubuffree (zsys); + return FALSE; + } + } + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + ubuffree (zfile); + ubuffree (zsys); + return FALSE; + } + + e = fopen (zfile, "r"); + if (e == NULL) + { + if (errno == ENOENT) + ulog (LOG_ERROR, "%s: Job not found", zid); + else + ulog (LOG_ERROR, "fopen (%s): %s", zfile, strerror (errno)); + (void) uuconf_system_free (puuconf, &ssys); + ubuffree (zfile); + ubuffree (zsys); + return FALSE; + } + + /* Now we have to read through the file to identify any temporary + files. */ + fret = TRUE; + zline = NULL; + cline = 0; + while (getline (&zline, &cline, e) > 0) + { + struct scmd s; + + if (! fparse_cmd (zline, &s)) + { + ulog (LOG_ERROR, "Bad line in command file %s", zfile); + fret = FALSE; + continue; + } + + /* You are only permitted to delete a job if you submitted it or + if you are root or uucp. */ + if (strcmp (s.zuser, zsysdep_login_name ()) != 0 + && ! fsysdep_privileged ()) + { + ulog (LOG_ERROR, "%s: Not submitted by you", zid); + xfree ((pointer) zline); + (void) fclose (e); + (void) uuconf_system_free (puuconf, &ssys); + ubuffree (zfile); + ubuffree (zsys); + return FALSE; + } + + if (s.bcmd == 'S' || s.bcmd == 'E') + { + char *ztemp; + + ztemp = zsfind_file (s.ztemp, ssys.uuconf_zname, bgrade); + if (ztemp == NULL) + fret = FALSE; + else + { + if (fkill) + isys = remove (ztemp); + else + isys = ussettime (ztemp, inow); + + if (isys != 0 && errno != ENOENT) + { + ulog (LOG_ERROR, "%s (%s): %s", + fkill ? "remove" : "utime", ztemp, + strerror (errno)); + fret = FALSE; + } + + ubuffree (ztemp); + } + } + } + + xfree ((pointer) zline); + (void) fclose (e); + (void) uuconf_system_free (puuconf, &ssys); + ubuffree (zsys); + + if (fkill) + isys = remove (zfile); + else + isys = ussettime (zfile, inow); + + if (isys != 0 && errno != ENOENT) + { + ulog (LOG_ERROR, "%s (%s): %s", fkill ? "remove" : "utime", + zfile, strerror (errno)); + fret = FALSE; + } + + ubuffree (zfile); + + return fret; +} + +/* Get the time a work job was queued. */ + +long +ixsysdep_work_time (qsys, pseq) + const struct uuconf_system *qsys; + pointer pseq; +{ + char *zjobid, *zfile; + long iret; + + zjobid = zsysdep_jobid (qsys, pseq); + zfile = zsjobid_to_file (zjobid, (char **) NULL, (char *) NULL); + if (zfile == NULL) + return 0; + ubuffree (zjobid); + iret = ixsysdep_file_time (zfile); + ubuffree (zfile); + return iret; +} + +/* Get the time a file was created (actually, the time it was last + modified). */ + +long +ixsysdep_file_time (zfile) + const char *zfile; +{ + struct stat s; + + if (stat ((char *) zfile, &s) < 0) + { + if (errno != ENOENT) + ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno)); + return ixsysdep_time ((long *) NULL); + } + + return (long) s.st_mtime; +} + +/* Start getting the status files. */ + +boolean +fsysdep_all_status_init (phold) + pointer *phold; +{ + DIR *qdir; + + qdir = opendir ((char *) ".Status"); + if (qdir == NULL) + { + ulog (LOG_ERROR, "opendir (.Status): %s", strerror (errno)); + return FALSE; + } + + *phold = (pointer) qdir; + return TRUE; +} + +/* Get the next status file. */ + +char * +zsysdep_all_status (phold, pferr, qstat) + pointer phold; + boolean *pferr; + struct sstatus *qstat; +{ + DIR *qdir = (DIR *) phold; + struct dirent *qentry; + + while (TRUE) + { + errno = 0; + qentry = readdir (qdir); + if (qentry == NULL) + { + if (errno == 0) + *pferr = FALSE; + else + { + ulog (LOG_ERROR, "readdir: %s", strerror (errno)); + *pferr = TRUE; + } + return NULL; + } + + if (qentry->d_name[0] != '.') + { + struct uuconf_system ssys; + + /* Hack seriously; fsysdep_get_status only looks at the + zname element of the qsys argument, so if we fake that we + can read the status file. This should really be done + differently. */ + ssys.uuconf_zname = qentry->d_name; + if (fsysdep_get_status (&ssys, qstat, (boolean *) NULL)) + return zbufcpy (qentry->d_name); + + /* If fsysdep_get_status fails, it will output an error + message. We just continue with the next entry, so that + most of the status files will be displayed. */ + } + } +} + +/* Finish getting the status file. */ + +void +usysdep_all_status_free (phold) + pointer phold; +{ + DIR *qdir = (DIR *) phold; + + (void) closedir (qdir); +} + +/* Get the status of all processes holding lock files. We do this by + invoking ps after we've figured out the process entries to use. */ + +boolean +fsysdep_lock_status () +{ + DIR *qdir; + struct dirent *qentry; + int calc; + int *pai; + int cgot; + int aidescs[3]; + char *zcopy, *ztok; + int cargs, iarg; + char **pazargs; + + qdir = opendir ((char *) zSlockdir); + if (qdir == NULL) + { + ulog (LOG_ERROR, "opendir (%s): %s", zSlockdir, strerror (errno)); + return FALSE; + } + + /* We look for entries that start with "LCK.." and ignore everything + else. This won't find all possible lock files, but it should + find all the locks on terminals and systems. */ + + calc = 0; + pai = NULL; + cgot = 0; + while ((qentry = readdir (qdir)) != NULL) + { + char *zname; + int o; +#if HAVE_V2_LOCKFILES + int i; +#else + char ab[12]; +#endif + int cread; + int ierr; + int ipid; + + if (strncmp (qentry->d_name, "LCK..", sizeof "LCK.." - 1) != 0) + continue; + + zname = zsysdep_in_dir (zSlockdir, qentry->d_name); + o = open ((char *) zname, O_RDONLY | O_NOCTTY, 0); + if (o < 0) + { + if (errno != ENOENT) + ulog (LOG_ERROR, "open (%s): %s", zname, strerror (errno)); + ubuffree (zname); + continue; + } + +#if HAVE_V2_LOCKFILES + cread = read (o, &i, sizeof i); +#else + cread = read (o, ab, sizeof ab - 1); +#endif + + ierr = errno; + (void) close (o); + + if (cread < 0) + { + ulog (LOG_ERROR, "read %s: %s", zname, strerror (ierr)); + ubuffree (zname); + continue; + } + + ubuffree (zname); + +#if HAVE_V2_LOCKFILES + ipid = i; +#else + ab[cread] = '\0'; + ipid = strtol (ab, (char **) NULL, 10); +#endif + + printf ("%s: %d\n", qentry->d_name, ipid); + + if (cgot >= calc) + { + calc += 10; + pai = (int *) xrealloc ((pointer) pai, calc * sizeof (int)); + } + + pai[cgot] = ipid; + ++cgot; + } + + if (cgot == 0) + return TRUE; + + aidescs[0] = SPAWN_NULL; + aidescs[1] = 1; + aidescs[2] = 2; + + /* Parse PS_PROGRAM into an array of arguments. */ + zcopy = zbufcpy (PS_PROGRAM); + + cargs = 0; + for (ztok = strtok (zcopy, " \t"); + ztok != NULL; + ztok = strtok ((char *) NULL, " \t")) + ++cargs; + + pazargs = (char **) xmalloc ((cargs + 1) * sizeof (char *)); + + memcpy (zcopy, PS_PROGRAM, sizeof PS_PROGRAM); + for (ztok = strtok (zcopy, " \t"), iarg = 0; + ztok != NULL; + ztok = strtok ((char *) NULL, " \t"), ++iarg) + pazargs[iarg] = ztok; + pazargs[iarg] = NULL; + +#if ! HAVE_PS_MULTIPLE + /* We have to invoke ps multiple times. */ + { + int i; + char *zlast, *zset; + + zlast = pazargs[cargs - 1]; + zset = zbufalc (strlen (zlast) + 20); + for (i = 0; i < cgot; i++) + { + pid_t ipid; + + sprintf (zset, "%s%d", zlast, pai[i]); + pazargs[cargs - 1] = zset; + + ipid = ixsspawn ((const char **) pazargs, aidescs, FALSE, FALSE, + (const char *) NULL, FALSE, TRUE, + (const char *) NULL, (const char *) NULL, + (const char *) NULL); + if (ipid < 0) + ulog (LOG_ERROR, "ixsspawn: %s", strerror (errno)); + else + (void) ixswait ((unsigned long) ipid, PS_PROGRAM); + } + ubuffree (zset); + } +#else + { + char *zlast; + int i; + pid_t ipid; + + zlast = zbufalc (strlen (pazargs[cargs - 1]) + cgot * 20 + 1); + strcpy (zlast, pazargs[cargs - 1]); + for (i = 0; i < cgot; i++) + { + char ab[20]; + + sprintf (ab, "%d", pai[i]); + strcat (zlast, ab); + if (i + 1 < cgot) + strcat (zlast, ","); + } + pazargs[cargs - 1] = zlast; + + ipid = ixsspawn ((const char **) pazargs, aidescs, FALSE, FALSE, + (const char *) NULL, FALSE, TRUE, + (const char *) NULL, (const char *) NULL, + (const char *) NULL); + if (ipid < 0) + ulog (LOG_ERROR, "ixsspawn: %s", strerror (errno)); + else + (void) ixswait ((unsigned long) ipid, PS_PROGRAM); + ubuffree (zlast); + } +#endif + + ubuffree (zcopy); + xfree ((pointer) pazargs); + + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/status.c b/gnu/libexec/uucp/libunix/status.c new file mode 100644 index 0000000000..f403068a70 --- /dev/null +++ b/gnu/libexec/uucp/libunix/status.c @@ -0,0 +1,212 @@ +/* status.c + Routines to get and set the status for a system. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" + +#include + +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 + +/* If we are using HDB spool layout, store status using HDB status + values. SVR4 is a variant of HDB. */ + +#define MAP_STATUS 1 + +static const int aiMapstatus[] = +{ + 0, 13, 7, 6, 4, 20, 3, 2 +}; +#define CMAPENTRIES (sizeof (aiMapstatus) / sizeof (aiMapstatus[0])) + +#else /* ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 */ + +#define MAP_STATUS 0 + +#endif /* ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 */ + +/* Get the status of a system. This assumes that we are in the spool + directory. */ + +boolean +fsysdep_get_status (qsys, qret, pfnone) + const struct uuconf_system *qsys; + struct sstatus *qret; + boolean *pfnone; +{ + char *zname; + FILE *e; + char *zline; + char *zend, *znext; + boolean fbad; + int istat; + + if (pfnone != NULL) + *pfnone = FALSE; + + zname = zsysdep_in_dir (".Status", qsys->uuconf_zname); + e = fopen (zname, "r"); + if (e == NULL) + { + if (errno != ENOENT) + { + ulog (LOG_ERROR, "fopen (%s): %s", zname, strerror (errno)); + ubuffree (zname); + return FALSE; + } + zline = NULL; + } + else + { + size_t cline; + + zline = NULL; + cline = 0; + if (getline (&zline, &cline, e) <= 0) + { + xfree ((pointer) zline); + zline = NULL; + } + (void) fclose (e); + } + + if (zline == NULL) + { + /* There is either no status file for this system, or it's been + truncated, so fake a good status. */ + qret->ttype = STATUS_COMPLETE; + qret->cretries = 0; + qret->ilast = 0; + qret->cwait = 0; + if (pfnone != NULL) + *pfnone = TRUE; + ubuffree (zname); + return TRUE; + } + + /* It turns out that scanf is not used much in this program, so for + the benefit of small computers we avoid linking it in. This is + basically + + sscanf (zline, "%d %d %ld %d", &qret->ttype, &qret->cretries, + &qret->ilast, &qret->cwait); + + except that it's done with strtol. */ + + fbad = FALSE; + istat = (int) strtol (zline, &zend, 10); + if (zend == zline) + fbad = TRUE; + +#if MAP_STATUS + /* On some systems it may be appropriate to map system dependent status + values on to our status values. */ + { + int i; + + for (i = 0; i < CMAPENTRIES; ++i) + { + if (aiMapstatus[i] == istat) + { + istat = i; + break; + } + } + } +#endif /* MAP_STATUS */ + + if (istat < 0 || istat >= (int) STATUS_VALUES) + istat = (int) STATUS_COMPLETE; + qret->ttype = (enum tstatus_type) istat; + znext = zend; + qret->cretries = (int) strtol (znext, &zend, 10); + if (zend == znext) + fbad = TRUE; + znext = zend; + qret->ilast = strtol (znext, &zend, 10); + if (zend == znext) + fbad = TRUE; + znext = zend; + qret->cwait = (int) strtol (znext, &zend, 10); + if (zend == znext) + fbad = TRUE; + + xfree ((pointer) zline); + + if (fbad) + { + ulog (LOG_ERROR, "%s: Bad status file format", zname); + ubuffree (zname); + return FALSE; + } + + ubuffree (zname); + + return TRUE; +} + +/* Set the status of a remote system. This assumes the system is + locked when this is called, and that the program is in the spool + directory. */ + +boolean +fsysdep_set_status (qsys, qset) + const struct uuconf_system *qsys; + const struct sstatus *qset; +{ + char *zname; + FILE *e; + int istat; + + zname = zsysdep_in_dir (".Status", qsys->uuconf_zname); + + e = esysdep_fopen (zname, TRUE, FALSE, TRUE); + ubuffree (zname); + if (e == NULL) + return FALSE; + istat = (int) qset->ttype; + +#if MAP_STATUS + /* On some systems it may be appropriate to map istat onto a system + dependent number. */ + if (istat >= 0 && istat < CMAPENTRIES) + istat = aiMapstatus[istat]; +#endif /* MAP_STATUS */ + + fprintf (e, "%d %d %ld %d %s %s\n", istat, qset->cretries, + qset->ilast, qset->cwait, azStatus[(int) qset->ttype], + qsys->uuconf_zname); + if (fclose (e) != 0) + { + ulog (LOG_ERROR, "fclose: %s", strerror (errno)); + return FALSE; + } + + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/strerr.c b/gnu/libexec/uucp/libunix/strerr.c new file mode 100644 index 0000000000..d2a6c2128d --- /dev/null +++ b/gnu/libexec/uucp/libunix/strerr.c @@ -0,0 +1,22 @@ +/* strerr.c + Return a string for a Unix errno value. */ + +#include "uucp.h" + +#include + +#ifndef sys_nerr +extern int sys_nerr; +#endif +#ifndef sys_errlist +extern char *sys_errlist[]; +#endif + +char * +strerror (ierr) + int ierr; +{ + if (ierr >= 0 && ierr < sys_nerr) + return sys_errlist[ierr]; + return (char *) "unknown error"; +} diff --git a/gnu/libexec/uucp/libunix/time.c b/gnu/libexec/uucp/libunix/time.c new file mode 100644 index 0000000000..d0462433a7 --- /dev/null +++ b/gnu/libexec/uucp/libunix/time.c @@ -0,0 +1,32 @@ +/* time.c + Get the current time. */ + +#include "uucp.h" + +#if HAVE_TIME_H +#include +#endif + +#include "system.h" + +#ifndef time +extern time_t time (); +#endif + +/* Get the time in seconds since the epoch, with optional + microseconds. We use ixsysdep_process_time to get the microseconds + if it will work (it won't if it uses times, since that returns a + time based only on the process). */ + +long +ixsysdep_time (pimicros) + long *pimicros; +{ +#if HAVE_GETTIMEOFDAY || HAVE_FTIME + return ixsysdep_process_time (pimicros); +#else + if (pimicros != NULL) + *pimicros = 0; + return (long) time ((time_t *) NULL); +#endif +} diff --git a/gnu/libexec/uucp/libunix/tmpfil.c b/gnu/libexec/uucp/libunix/tmpfil.c new file mode 100644 index 0000000000..2dac002438 --- /dev/null +++ b/gnu/libexec/uucp/libunix/tmpfil.c @@ -0,0 +1,83 @@ +/* tmpfil.c + Get a temporary file name. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uuconf.h" +#include "system.h" +#include "sysdep.h" + +#define ZDIGS \ + "0123456789abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-" +#define CDIGS (sizeof ZDIGS - 1) + +/*ARGSUSED*/ +char * +zstemp_file (qsys) + const struct uuconf_system *qsys; +{ + static int icount; + const char *const zdigs = ZDIGS; + char ab[14]; + pid_t ime; + int iset; + + ab[0] = 'T'; + ab[1] = 'M'; + ab[2] = '.'; + + ime = getpid (); + iset = 3; + while (ime > 0 && iset < 10) + { + ab[iset] = zdigs[ime % CDIGS]; + ime /= CDIGS; + ++iset; + } + + ab[iset] = '.'; + ++iset; + + ab[iset] = zdigs[icount / CDIGS]; + ++iset; + ab[iset] = zdigs[icount % CDIGS]; + ++iset; + + ab[iset] = '\0'; + + ++icount; + if (icount >= CDIGS * CDIGS) + icount = 0; + +#if SPOOLDIR_V2 || SPOOLDIR_BSD42 + return zbufcpy (ab); +#endif +#if SPOOLDIR_BSD43 || SPOOLDIR_ULTRIX || SPOOLDIR_TAYLOR + return zsysdep_in_dir (".Temp", ab); +#endif +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 + return zsysdep_in_dir (qsys->uuconf_zname, ab); +#endif +} diff --git a/gnu/libexec/uucp/libunix/trunc.c b/gnu/libexec/uucp/libunix/trunc.c new file mode 100644 index 0000000000..c93e82e394 --- /dev/null +++ b/gnu/libexec/uucp/libunix/trunc.c @@ -0,0 +1,157 @@ +/* trunc.c + Truncate a file to zero length. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +/* External functions. */ +#ifndef lseek +extern off_t lseek (); +#endif + +/* Truncate a file to zero length. If this fails, it closes and + removes the file. We support a number of different means of + truncation, which is probably a waste of time since this function + is currently only called when the 'f' protocol resends a file. */ + +#if HAVE_FTRUNCATE +#undef HAVE_LTRUNC +#define HAVE_LTRUNC 0 +#endif + +#if ! HAVE_FTRUNCATE && ! HAVE_LTRUNC +#ifdef F_CHSIZE +#define HAVE_F_CHSIZE 1 +#else /* ! defined (F_CHSIZE) */ +#ifdef F_FREESP +#define HAVE_F_FREESP 1 +#endif /* defined (F_FREESP) */ +#endif /* ! defined (F_CHSIZE) */ +#endif /* ! HAVE_FTRUNCATE && ! HAVE_LTRUNC */ + +openfile_t +esysdep_truncate (e, zname) + openfile_t e; + const char *zname; +{ + int o; + +#if HAVE_FTRUNCATE || HAVE_LTRUNC || HAVE_F_CHSIZE || HAVE_F_FREESP + int itrunc; + + if (! ffilerewind (e)) + { + ulog (LOG_ERROR, "rewind: %s", strerror (errno)); + (void) ffileclose (e); + (void) remove (zname); + return EFILECLOSED; + } + +#if USE_STDIO + o = fileno (e); +#else + o = e; +#endif + +#if HAVE_FTRUNCATE + itrunc = ftruncate (o, 0); +#endif +#if HAVE_LTRUNC + itrunc = ltrunc (o, (long) 0, SEEK_SET); +#endif +#if HAVE_F_CHSIZE + itrunc = fcntl (o, F_CHSIZE, (off_t) 0); +#endif +#if HAVE_F_FREESP + /* This selection is based on an implementation of ftruncate by + kucharsk@Solbourne.com (William Kucharski). */ + { + struct flock fl; + + fl.l_whence = 0; + fl.l_len = 0; + fl.l_start = 0; + fl.l_type = F_WRLCK; + + itrunc = fcntl (o, F_FREESP, &fl); + } +#endif + + if (itrunc != 0) + { +#if HAVE_FTRUNCATE + ulog (LOG_ERROR, "ftruncate: %s", strerror (errno)); +#endif +#ifdef HAVE_LTRUNC + ulog (LOG_ERROR, "ltrunc: %s", strerror (errno)); +#endif +#ifdef HAVE_F_CHSIZE + ulog (LOG_ERROR, "fcntl (F_CHSIZE): %s", strerror (errno)); +#endif +#ifdef HAVE_F_FREESP + ulog (LOG_ERROR, "fcntl (F_FREESP): %s", strerror (errno)); +#endif + + (void) ffileclose (e); + (void) remove (zname); + return EFILECLOSED; + } + + return e; +#else /* ! (HAVE_FTRUNCATE || HAVE_LTRUNC || HAVE_F_CHSIZE || HAVE_F_FREESP) */ + (void) ffileclose (e); + (void) remove (zname); + + o = creat ((char *) zname, IPRIVATE_FILE_MODE); + + if (o == -1) + { + ulog (LOG_ERROR, "creat (%s): %s", zname, strerror (errno)); + return EFILECLOSED; + } + + if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + (void) close (o); + return EFILECLOSED; + } + +#if USE_STDIO + e = fdopen (o, (char *) BINWRITE); + + if (e == NULL) + { + ulog (LOG_ERROR, "fdopen (%s): %s", zname, strerror (errno)); + (void) close (o); + (void) remove (zname); + return NULL; + } +#else /* ! USE_STDIO */ + e = o; +#endif /* ! USE_STDIO */ + + return e; +#endif /* ! (HAVE_FTRUNCATE || HAVE_LTRUNC || HAVE_F_CHSIZE || HAVE_F_FREESP) */ +} diff --git a/gnu/libexec/uucp/libunix/uacces.c b/gnu/libexec/uucp/libunix/uacces.c new file mode 100644 index 0000000000..c92c78eae3 --- /dev/null +++ b/gnu/libexec/uucp/libunix/uacces.c @@ -0,0 +1,205 @@ +/* uacces.c + Check access to a file by user name. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" + +#include +#include + +#if HAVE_GETGRENT +#include +#if GETGRENT_DECLARATION_OK +#ifndef getgrent +extern struct group *getgrent (); +#endif +#endif +#endif /* HAVE_GETGRENT */ + +#if GETPWNAM_DECLARATION_OK +#ifndef getpwnam +extern struct passwd *getpwnam (); +#endif +#endif + +/* Do access(2) on a stat structure, except that the user name is + provided. If the user name in zuser is NULL, require the file to + be accessible to the world. Return TRUE if access is permitted, + FALSE otherwise. This does not log an error message. */ + +boolean +fsuser_access (q, imode, zuser) + const struct stat *q; + int imode; + const char *zuser; +{ + static char *zuser_hold; + static uid_t iuid_hold; + static gid_t igid_hold; + static int cgroups_hold; + static gid_t *paigroups_hold; + int ir, iw, ix, iand; + + if (imode == F_OK) + return TRUE; + + if (zuser != NULL) + { + /* We keep static variables around for the last user we did, to + avoid looking up a user multiple times. */ + if (zuser_hold == NULL || strcmp (zuser_hold, zuser) != 0) + { + struct passwd *qpwd; + + if (zuser_hold != NULL) + { + ubuffree (zuser_hold); + zuser_hold = NULL; + cgroups_hold = 0; + xfree ((pointer) paigroups_hold); + paigroups_hold = NULL; + } + + qpwd = getpwnam ((char *) zuser); + if (qpwd == NULL) + { + /* Check this as a remote request. */ + zuser = NULL; + } + else + { +#if HAVE_GETGRENT + struct group *qg; +#endif + + zuser_hold = zbufcpy (zuser); + + iuid_hold = qpwd->pw_uid; + igid_hold = qpwd->pw_gid; + +#if HAVE_GETGRENT + /* Get the list of groups for this user. This is + definitely more appropriate for BSD than for System + V. It may just be a waste of time, and perhaps it + should be configurable. */ + setgrent (); + while ((qg = getgrent ()) != NULL) + { + const char **pz; + + if (qg->gr_gid == igid_hold) + continue; + for (pz = (const char **) qg->gr_mem; *pz != NULL; pz++) + { + if ((*pz)[0] == *zuser + && strcmp (*pz, zuser) == 0) + { + paigroups_hold = ((gid_t *) + (xrealloc + ((pointer) paigroups_hold, + ((cgroups_hold + 1) + * sizeof (gid_t))))); + paigroups_hold[cgroups_hold] = qg->gr_gid; + ++cgroups_hold; + break; + } + } + } + endgrent (); +#endif + } + } + } + + + /* Now do the actual access check. */ + + if (zuser != NULL) + { + /* The superuser can do anything. */ + if (iuid_hold == 0) + return TRUE; + + /* If this is the uid we're running under, there's no point to + checking access further, because when we actually try the + operation the system will do the checking for us. */ + if (iuid_hold == geteuid ()) + return TRUE; + } + + ir = S_IROTH; + iw = S_IWOTH; + ix = S_IXOTH; + + if (zuser != NULL) + { + if (iuid_hold == q->st_uid) + { + ir = S_IRUSR; + iw = S_IWUSR; + ix = S_IXUSR; + } + else + { + boolean fgroup; + + fgroup = FALSE; + if (igid_hold == q->st_gid) + fgroup = TRUE; + else + { + int i; + + for (i = 0; i < cgroups_hold; i++) + { + if (paigroups_hold[i] == q->st_gid) + { + fgroup = TRUE; + break; + } + } + } + + if (fgroup) + { + ir = S_IRGRP; + iw = S_IWGRP; + ix = S_IXGRP; + } + } + } + + iand = 0; + if ((imode & R_OK) != 0) + iand |= ir; + if ((imode & W_OK) != 0) + iand |= iw; + if ((imode & X_OK) != 0) + iand |= ix; + + return (q->st_mode & iand) == iand; +} diff --git a/gnu/libexec/uucp/libunix/ufopen.c b/gnu/libexec/uucp/libunix/ufopen.c new file mode 100644 index 0000000000..5a7b6f22b0 --- /dev/null +++ b/gnu/libexec/uucp/libunix/ufopen.c @@ -0,0 +1,218 @@ +/* ufopen.c + Open a file with the permissions of the invoking user. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +/* Local functions. */ + +static boolean fsuser_perms P((uid_t *pieuid)); +static boolean fsuucp_perms P((long ieuid)); + +/* Switch to permissions of the invoking user. */ + +static boolean +fsuser_perms (pieuid) + uid_t *pieuid; +{ + uid_t ieuid, iuid; + + ieuid = geteuid (); + iuid = getuid (); + if (pieuid != NULL) + *pieuid = ieuid; + +#if HAVE_SETREUID + /* Swap the effective user id and the real user id. We can then + swap them back again when we want to return to the uucp user's + permissions. */ + if (setreuid (ieuid, iuid) < 0) + { + ulog (LOG_ERROR, "setreuid (%ld, %ld): %s", + (long) ieuid, (long) iuid, strerror (errno)); + return FALSE; + } +#else /* ! HAVE_SETREUID */ +#if HAVE_SAVED_SETUID + /* Set the effective user id to the real user id. Since the + effective user id is saved (it's the saved setuid) we will able + to set back to it later. If the real user id is root we will not + be able to switch back and forth, so don't even try. */ + if (iuid != 0) + { + if (setuid (iuid) < 0) + { + ulog (LOG_ERROR, "setuid (%ld): %s", (long) iuid, strerror (errno)); + return FALSE; + } + } +#else /* ! HAVE_SAVED_SETUID */ + /* There's no way to switch between real permissions and effective + permissions. Just try to open the file with the uucp + permissions. */ +#endif /* ! HAVE_SAVED_SETUID */ +#endif /* ! HAVE_SETREUID */ + + return TRUE; +} + +/* Restore the uucp permissions. */ + +/*ARGSUSED*/ +static boolean +fsuucp_perms (ieuid) + long ieuid; +{ +#if HAVE_SETREUID + /* Swap effective and real user id's back to what they were. */ + if (! fsuser_perms ((uid_t *) NULL)) + return FALSE; +#else /* ! HAVE_SETREUID */ +#if HAVE_SAVED_SETUID + /* Set ourselves back to our original effective user id. */ + if (setuid ((uid_t) ieuid) < 0) + { + ulog (LOG_ERROR, "setuid (%ld): %s", (long) ieuid, strerror (errno)); + /* Is this error message helpful or confusing? */ + if (errno == EPERM) + ulog (LOG_ERROR, + "Probably HAVE_SAVED_SETUID in policy.h should be set to 0"); + return FALSE; + } +#else /* ! HAVE_SAVED_SETUID */ + /* We didn't switch, no need to switch back. */ +#endif /* ! HAVE_SAVED_SETUID */ +#endif /* ! HAVE_SETREUID */ + + return TRUE; +} + +/* Open a file with the permissions of the invoking user. Ignore the + fbinary argument since Unix has no distinction between text and + binary files. */ + +/*ARGSUSED*/ +openfile_t +esysdep_user_fopen (zfile, frd, fbinary) + const char *zfile; + boolean frd; + boolean fbinary; +{ + uid_t ieuid; + openfile_t e; + const char *zerr; + int o = 0; + + if (! fsuser_perms (&ieuid)) + return EFILECLOSED; + + zerr = NULL; + +#if USE_STDIO + e = fopen (zfile, frd ? "r" : "w"); + if (e == NULL) + zerr = "fopen"; + else + o = fileno (e); +#else + if (frd) + { + e = open ((char *) zfile, O_RDONLY | O_NOCTTY, 0); + zerr = "open"; + } + else + { + e = creat ((char *) zfile, IPUBLIC_FILE_MODE); + zerr = "creat"; + } + if (e >= 0) + { + o = e; + zerr = NULL; + } +#endif + + if (! fsuucp_perms ((long) ieuid)) + { + if (ffileisopen (e)) + (void) ffileclose (e); + return EFILECLOSED; + } + + if (zerr != NULL) + { + ulog (LOG_ERROR, "%s (%s): %s", zerr, zfile, strerror (errno)); +#if ! HAVE_SETREUID + /* Are these error messages helpful or confusing? */ +#if HAVE_SAVED_SETUID + if (errno == EACCES && getuid () == 0) + ulog (LOG_ERROR, + "The superuser may only transfer files that are readable by %s", + OWNER); +#else + if (errno == EACCES) + ulog (LOG_ERROR, + "You may only transfer files that are readable by %s", OWNER); +#endif +#endif /* ! HAVE_SETREUID */ + return EFILECLOSED; + } + + if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + (void) ffileclose (e); + return EFILECLOSED; + } + + return e; +} diff --git a/gnu/libexec/uucp/libunix/ultspl.c b/gnu/libexec/uucp/libunix/ultspl.c new file mode 100644 index 0000000000..34921d2280 --- /dev/null +++ b/gnu/libexec/uucp/libunix/ultspl.c @@ -0,0 +1,21 @@ +/* ultspl.c + See whether there is an Ultrix spool directory for a system. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +boolean +fsultrix_has_spool (zsystem) + const char *zsystem; +{ + char *z; + boolean fret; + + z = zsysdep_in_dir ("sys", zsystem); + fret = fsysdep_directory (z); + ubuffree (z); + return fret; +} diff --git a/gnu/libexec/uucp/libunix/unknwn.c b/gnu/libexec/uucp/libunix/unknwn.c new file mode 100644 index 0000000000..76f5345947 --- /dev/null +++ b/gnu/libexec/uucp/libunix/unknwn.c @@ -0,0 +1,43 @@ +/* unknwn.c + Check remote.unknown shell script. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +/* Run the remote.unknown shell script. If it succeeds, we return + FALSE because that means that the system is not permitted to log + in. If the execution fails, we return TRUE. */ + +boolean +fsysdep_unknown_caller (zscript, zsystem) + const char *zscript; + const char *zsystem; +{ + const char *azargs[3]; + int aidescs[3]; + pid_t ipid; + + azargs[0] = zscript; + azargs[1] = zsystem; + azargs[2] = NULL; + + aidescs[0] = SPAWN_NULL; + aidescs[1] = SPAWN_NULL; + aidescs[2] = SPAWN_NULL; + + ipid = ixsspawn (azargs, aidescs, TRUE, TRUE, (const char *) NULL, FALSE, + TRUE, (const char *) NULL, (const char *) NULL, + (const char *) NULL); + if (ipid < 0) + { + ulog (LOG_ERROR, "ixsspawn: %s", strerror (errno)); + return FALSE; + } + + return ixswait ((unsigned long) ipid, (const char *) NULL) != 0; +} diff --git a/gnu/libexec/uucp/libunix/uuto.c b/gnu/libexec/uucp/libunix/uuto.c new file mode 100644 index 0000000000..debba9d6fd --- /dev/null +++ b/gnu/libexec/uucp/libunix/uuto.c @@ -0,0 +1,31 @@ +/* uuto.c + Translate a destination for uuto. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +/* Translate a uuto destination for Unix. */ + +char * +zsysdep_uuto (zdest, zlocalname) + const char *zdest; + const char *zlocalname; +{ + const char *zexclam; + char *zto; + + zexclam = strrchr (zdest, '!'); + if (zexclam == NULL) + return NULL; + zto = (char *) zbufalc (zexclam - zdest + + sizeof "!~/receive///" + + strlen (zexclam) + + strlen (zlocalname)); + memcpy (zto, zdest, (size_t) (zexclam - zdest)); + sprintf (zto + (zexclam - zdest), "!~/receive/%s/%s/", + zexclam + 1, zlocalname); + return zto; +} diff --git a/gnu/libexec/uucp/libunix/walk.c b/gnu/libexec/uucp/libunix/walk.c new file mode 100644 index 0000000000..ab96123127 --- /dev/null +++ b/gnu/libexec/uucp/libunix/walk.c @@ -0,0 +1,59 @@ +/* walk.c + Walk a directory tree. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#if HAVE_FTW_H +#include +#endif + +static int iswalk_dir P((const char *zname, const struct stat *qstat, + int iflag)); + +/* Walk a directory tree. */ + +static size_t cSlen; +static void (*puSfn) P((const char *zfull, const char *zrelative, + pointer pinfo)); +static pointer pSinfo; + +boolean +usysdep_walk_tree (zdir, pufn, pinfo) + const char *zdir; + void (*pufn) P((const char *zfull, const char *zrelative, + pointer pinfo)); + pointer pinfo; +{ + cSlen = strlen (zdir) + 1; + puSfn = pufn; + pSinfo = pinfo; + return ftw ((char *) zdir, iswalk_dir, 5) == 0; +} + +/* Pass a file found in the directory tree to the system independent + function. */ + +/*ARGSUSED*/ +static int +iswalk_dir (zname, qstat, iflag) + const char *zname; + const struct stat *qstat; + int iflag; +{ + char *zcopy; + + if (iflag != FTW_F) + return 0; + + zcopy = zbufcpy (zname + cSlen); + + (*puSfn) (zname, zcopy, pSinfo); + + ubuffree (zcopy); + + return 0; +} diff --git a/gnu/libexec/uucp/libunix/wldcrd.c b/gnu/libexec/uucp/libunix/wldcrd.c new file mode 100644 index 0000000000..cfbd15eb84 --- /dev/null +++ b/gnu/libexec/uucp/libunix/wldcrd.c @@ -0,0 +1,212 @@ +/* wldcrd.c + Expand wildcards. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include +#include + +#if HAVE_GLOB && ! HAVE_GLOB_H +#undef HAVE_GLOB +#define HAVE_GLOB 0 +#endif + +#if HAVE_GLOB +#include +#endif + +/* Local variables to hold the wildcard in progress. */ + +#if HAVE_GLOB +static glob_t sSglob; +static int iSglob; +#else +static char *zSwildcard_alloc; +static char *zSwildcard; +#endif + +/* Start getting a wildcarded file spec. Use the glob function if it + is available, and otherwise use the shell. */ + +boolean +fsysdep_wildcard_start (zfile) + const char *zfile; +{ +#if HAVE_GLOB + +#if DEBUG > 0 + if (*zfile != '/') + ulog (LOG_FATAL, "fsysdep_wildcard: %s: Can't happen", zfile); +#endif + + if (glob (zfile, 0, (int (*) ()) NULL, &sSglob) != 0) + sSglob.gl_pathc = 0; + iSglob = 0; + return TRUE; + +#else /* ! HAVE_GLOB */ + + char *zcmd, *zto; + const char *zfrom; + size_t c; + const char *azargs[4]; + FILE *e; + pid_t ipid; + +#if DEBUG > 0 + if (*zfile != '/') + ulog (LOG_FATAL, "fsysdep_wildcard: %s: Can't happen", zfile); +#endif + + zSwildcard_alloc = NULL; + zSwildcard = NULL; + + zcmd = zbufalc (sizeof ECHO_PROGRAM + sizeof " " + 2 * strlen (zfile)); + memcpy (zcmd, ECHO_PROGRAM, sizeof ECHO_PROGRAM - 1); + zto = zcmd + sizeof ECHO_PROGRAM - 1; + *zto++ = ' '; + zfrom = zfile; + while (*zfrom != '\0') + { + /* To avoid shell trickery, we quote all characters except + letters, digits, and wildcard specifiers. We don't quote '/' + to avoid an Ultrix sh bug. */ + if (! isalnum (*zfrom) + && *zfrom != '*' + && *zfrom != '?' + && *zfrom != '[' + && *zfrom != ']' + && *zfrom != '/') + *zto++ = '\\'; + *zto++ = *zfrom++; + } + *zto = '\0'; + + azargs[0] = "/bin/sh"; + azargs[1] = "-c"; + azargs[2] = zcmd; + azargs[3] = NULL; + + ubuffree (zcmd); + + e = espopen (azargs, TRUE, &ipid); + if (e == NULL) + { + ulog (LOG_ERROR, "espopen: %s", strerror (errno)); + return FALSE; + } + + zSwildcard_alloc = NULL; + c = 0; + if (getline (&zSwildcard_alloc, &c, e) <= 0) + { + xfree ((pointer) zSwildcard_alloc); + zSwildcard_alloc = NULL; + } + + if (ixswait ((unsigned long) ipid, ECHO_PROGRAM) != 0) + { + xfree ((pointer) zSwildcard_alloc); + return FALSE; + } + + if (zSwildcard_alloc == NULL) + return FALSE; + + DEBUG_MESSAGE1 (DEBUG_EXECUTE, + "fsysdep_wildcard_start: got \"%s\"", + zSwildcard_alloc); + + zSwildcard = zSwildcard_alloc; + + return TRUE; + +#endif /* ! HAVE_GLOB */ +} + +/* Get the next wildcard spec. */ + +/*ARGSUSED*/ +char * +zsysdep_wildcard (zfile) + const char *zfile; +{ +#if HAVE_GLOB + + char *zret; + + if (iSglob >= sSglob.gl_pathc) + return NULL; + zret = zbufcpy (sSglob.gl_pathv[iSglob]); + ++iSglob; + return zret; + +#else /* ! HAVE_GLOB */ + + char *zret; + + if (zSwildcard_alloc == NULL || zSwildcard == NULL) + return NULL; + + zret = zSwildcard; + + while (*zSwildcard != '\0' && ! isspace (BUCHAR (*zSwildcard))) + ++zSwildcard; + + if (*zSwildcard != '\0') + { + *zSwildcard = '\0'; + ++zSwildcard; + while (*zSwildcard != '\0' && isspace (BUCHAR (*zSwildcard))) + ++zSwildcard; + } + + if (*zSwildcard == '\0') + zSwildcard = NULL; + + return zbufcpy (zret); + +#endif /* ! HAVE_GLOB */ +} + +/* Finish up getting wildcard specs. */ + +boolean +fsysdep_wildcard_end () +{ +#if HAVE_GLOB + globfree (&sSglob); + return TRUE; +#else /* ! HAVE_GLOB */ + xfree ((pointer) zSwildcard_alloc); + zSwildcard_alloc = NULL; + zSwildcard = NULL; + return TRUE; +#endif /* ! HAVE_GLOB */ +} diff --git a/gnu/libexec/uucp/libunix/work.c b/gnu/libexec/uucp/libunix/work.c new file mode 100644 index 0000000000..3d055c282e --- /dev/null +++ b/gnu/libexec/uucp/libunix/work.c @@ -0,0 +1,765 @@ +/* work.c + Routines to read command files. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char work_rcsid[] = "$Id: work.c,v 1.1 1993/08/04 19:33:20 jtc Exp $"; +#endif + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" +#include "sysdep.h" + +#include +#include + +#if HAVE_OPENDIR +#if HAVE_DIRENT_H +#include +#else /* ! HAVE_DIRENT_H */ +#include +#define dirent direct +#endif /* ! HAVE_DIRENT_H */ +#endif /* HAVE_OPENDIR */ + +/* Local functions. */ + +static char *zswork_directory P((const char *zsystem)); +static boolean fswork_file P((const char *zsystem, const char *zfile, + char *pbgrade)); +static int iswork_cmp P((constpointer pkey, constpointer pdatum)); + +/* These functions can support multiple actions going on at once. + This allows the UUCP package to send and receive multiple files at + the same time. This is a very flexible feature, but I'm not sure + it will actually be used all that much. + + The ssfile structure holds a command file name and all the lines + read in from that command file. The union within the ssline + structure initially holds a line from the file and then holds a + pointer back to the ssfile structure; a pointer to this union is + used as a sequence pointer. The ztemp entry of the ssline + structure holds the name of a temporary file to delete, if any. */ + +#define CFILELINES (10) + +struct ssline +{ + char *zline; + struct ssfile *qfile; + char *ztemp; +}; + +struct ssfile +{ + char *zfile; + int clines; + int cdid; + struct ssline aslines[CFILELINES]; +}; + +/* Static variables for the work scan. */ + +static char **azSwork_files; +static size_t cSwork_files; +static size_t iSwork_file; +static struct ssfile *qSwork_file; + +/* Given a system name, return a directory to search for work. */ + +static char * +zswork_directory (zsystem) + const char *zsystem; +{ +#if SPOOLDIR_V2 + return zbufcpy ("."); +#endif /* SPOOLDIR_V2 */ +#if SPOOLDIR_BSD42 || SPOOLDIR_BSD43 + return zbufcpy ("C."); +#endif /* SPOOLDIR_BSD42 || SPOOLDIR_BSD43 */ +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 + return zbufcpy (zsystem); +#endif /* SPOOLDIR_HDB || SPOOLDIR_SVR4 */ +#if SPOOLDIR_ULTRIX + return zsappend3 ("sys", + (fsultrix_has_spool (zsystem) + ? zsystem + : "DEFAULT"), + "C."); +#endif /* SPOOLDIR_ULTRIX */ +#if SPOOLDIR_TAYLOR + return zsysdep_in_dir (zsystem, "C."); +#endif /* SPOOLDIR_TAYLOR */ +} + +/* See whether a file name from the directory returned by + zswork_directory is really a command for a particular system. + Return the command grade. */ + +/*ARGSUSED*/ +static boolean +fswork_file (zsystem, zfile, pbgrade) + const char *zsystem; + const char *zfile; + char *pbgrade; +{ +#if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 || SPOOLDIR_ULTRIX + int cfilesys, csys; + + /* The file name should be C.ssssssgqqqq, where g is exactly one + letter and qqqq is exactly four numbers. The system name may be + truncated to six or seven characters. The system name of the + file must match the system name we're looking for, since there + could be work files for several systems in one directory. */ + if (zfile[0] != 'C' || zfile[1] != '.') + return FALSE; + csys = strlen (zsystem); + cfilesys = strlen (zfile) - 7; + if (csys != cfilesys + && (csys < 6 || (cfilesys != 6 && cfilesys != 7))) + return FALSE; + *pbgrade = zfile[cfilesys + 2]; + return strncmp (zfile + 2, zsystem, cfilesys) == 0; +#endif /* V2 || BSD42 || BSD43 || ULTRIX */ +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 + int clen; + + /* The HDB file name should be C.ssssssgqqqq where g is exactly one + letter and qqqq is exactly four numbers or letters. We don't + check the system name, because it is guaranteed by the directory + we are looking in and some versions of uucp set it to the local + system rather than the remote one. I'm not sure of the exact + format of the SVR4 file name, but it does not include the grade + at all. */ + if (zfile[0] != 'C' || zfile[1] != '.') + return FALSE; + clen = strlen (zfile); + if (clen < 7) + return FALSE; +#if ! SPOOLDIR_SVR4 + *pbgrade = zfile[clen - 5]; +#endif + return TRUE; +#endif /* SPOOLDIR_HDB || SPOOLDIR_SVR4 */ +#if SPOOLDIR_TAYLOR + /* We don't keep the system name in the file name, since that + forces truncation. Our file names are always C.gqqqq. */ + *pbgrade = zfile[2]; + return (zfile[0] == 'C' + && zfile[1] == '.' + && strlen (zfile) == 7); +#endif /* SPOOLDIR_TAYLOR */ +} + +/* A comparison function to look through the list of file names. */ + +static int +iswork_cmp (pkey, pdatum) + constpointer pkey; + constpointer pdatum; +{ + const char * const *pzkey = (const char * const *) pkey; + const char * const *pzdatum = (const char * const *) pdatum; + + return strcmp (*pzkey, *pzdatum); +} + +/* See whether there is any work to do for a particular system. */ + +boolean +fsysdep_has_work (qsys) + const struct uuconf_system *qsys; +{ + char *zdir; + DIR *qdir; + struct dirent *qentry; +#if SPOOLDIR_SVR4 + DIR *qgdir; + struct dirent *qgentry; +#endif + + zdir = zswork_directory (qsys->uuconf_zname); + if (zdir == NULL) + return FALSE; + qdir = opendir ((char *) zdir); + if (qdir == NULL) + { + ubuffree (zdir); + return FALSE; + } + +#if SPOOLDIR_SVR4 + qgdir = qdir; + while ((qgentry = readdir (qgdir)) != NULL) + { + char *zsub; + + if (qgentry->d_name[0] == '.' + || qgentry->d_name[1] != '\0') + continue; + zsub = zsysdep_in_dir (zdir, qgentry->d_name); + qdir = opendir (zsub); + ubuffree (zsub); + if (qdir == NULL) + continue; +#endif + + while ((qentry = readdir (qdir)) != NULL) + { + char bgrade; + + if (fswork_file (qsys->uuconf_zname, qentry->d_name, &bgrade)) + { + closedir (qdir); +#if SPOOLDIR_SVR4 + closedir (qgdir); +#endif + ubuffree (zdir); + return TRUE; + } + } + +#if SPOOLDIR_SVR4 + closedir (qdir); + } + qdir = qgdir; +#endif + + closedir (qdir); + ubuffree (zdir); + return FALSE; +} + +/* Initialize the work scan. We have to read all the files in the + work directory, so that we can sort them by work grade. The bgrade + argument is the minimum grade to consider. We don't want to return + files that we have already considered; usysdep_get_work_free will + clear the data out when we are done with the system. This returns + FALSE on error. */ + +#define CWORKFILES (10) + +boolean +fsysdep_get_work_init (qsys, bgrade) + const struct uuconf_system *qsys; + int bgrade; +{ + char *zdir; + DIR *qdir; + struct dirent *qentry; + size_t chad; + size_t callocated; +#if SPOOLDIR_SVR4 + DIR *qgdir; + struct dirent *qgentry; +#endif + + zdir = zswork_directory (qsys->uuconf_zname); + if (zdir == NULL) + return FALSE; + + qdir = opendir (zdir); + if (qdir == NULL) + { + boolean fret; + + if (errno == ENOENT) + fret = TRUE; + else + { + ulog (LOG_ERROR, "opendir (%s): %s", zdir, strerror (errno)); + fret = FALSE; + } + ubuffree (zdir); + return fret; + } + + chad = cSwork_files; + callocated = cSwork_files; + + /* Sort the files we already know about so that we can check the new + ones with bsearch. It would be faster to use a hash table, and + the code should be probably be changed. The sort done at the end + of this function does not suffice because it only includes the + files added last time, and does not sort the entire array. Some + (bad) qsort implementations are very slow when given a sorted + array, which causes particularly bad effects here. */ + if (chad > 0) + qsort ((pointer) azSwork_files, chad, sizeof (char *), iswork_cmp); + +#if SPOOLDIR_SVR4 + qgdir = qdir; + while ((qgentry = readdir (qgdir)) != NULL) + { + char *zsub; + + if (qgentry->d_name[0] == '.' + || qgentry->d_name[1] != '\0' + || UUCONF_GRADE_CMP (bgrade, qgentry->d_name[0]) < 0) + continue; + zsub = zsysdep_in_dir (zdir, qgentry->d_name); + qdir = opendir (zsub); + if (qdir == NULL) + { + if (errno != ENOTDIR && errno != ENOENT) + { + ulog (LOG_ERROR, "opendir (%s): %s", zsub, + strerror (errno)); + ubuffree (zsub); + return FALSE; + } + ubuffree (zsub); + continue; + } + ubuffree (zsub); +#endif + + while ((qentry = readdir (qdir)) != NULL) + { + char bfilegrade; + char *zname; + +#if ! SPOOLDIR_SVR4 + zname = zbufcpy (qentry->d_name); +#else + zname = zsysdep_in_dir (qgentry->d_name, qentry->d_name); + bfilegrade = qgentry->d_name[0]; +#endif + + if (! fswork_file (qsys->uuconf_zname, qentry->d_name, + &bfilegrade) + || UUCONF_GRADE_CMP (bgrade, bfilegrade) < 0 + || (azSwork_files != NULL + && bsearch ((pointer) &zname, + (pointer) azSwork_files, + chad, sizeof (char *), + iswork_cmp) != NULL)) + ubuffree (zname); + else + { + DEBUG_MESSAGE1 (DEBUG_SPOOLDIR, + "fsysdep_get_work_init: Found %s", + zname); + + if (cSwork_files >= callocated) + { + callocated += CWORKFILES; + azSwork_files = + (char **) xrealloc ((pointer) azSwork_files, + callocated * sizeof (char *)); + } + + azSwork_files[cSwork_files] = zname; + ++cSwork_files; + } + } + +#if SPOOLDIR_SVR4 + closedir (qdir); + } + qdir = qgdir; +#endif + + closedir (qdir); + ubuffree (zdir); + + /* Sorting the files alphabetically will get the grades in the + right order, since all the file prefixes are the same. */ + + if (cSwork_files > chad) + qsort ((pointer) (azSwork_files + chad), cSwork_files - chad, + sizeof (char *), iswork_cmp); + + return TRUE; +} + +/* Get the next work entry for a system. This must parse the next + line in the next work file. The type of command is set into + qcmd->bcmd; if there are no more commands we call + fsysdep_get_work_init to rescan, in case any came in since the last + call. If there are still no commands, qcmd->bcmd is set to 'H'. + Each field in the structure is set to point to a spot in an + malloced string. The only time we use the grade here is when + calling fsysdep_get_work_init to rescan. */ + +boolean +fsysdep_get_work (qsys, bgrade, qcmd) + const struct uuconf_system *qsys; + int bgrade; + struct scmd *qcmd; +{ + char *zdir; + + if (qSwork_file != NULL && qSwork_file->cdid >= qSwork_file->clines) + qSwork_file = NULL; + + if (azSwork_files == NULL) + { + qcmd->bcmd = 'H'; + return TRUE; + } + + zdir = NULL; + + /* This loop continues until a line is returned. */ + while (TRUE) + { + /* This loop continues until a file is opened and read in. */ + while (qSwork_file == NULL) + { + FILE *e; + struct ssfile *qfile; + int iline, callocated; + char *zline; + size_t cline; + char *zname; + + /* Read all the lines of a command file into memory. */ + do + { + if (iSwork_file >= cSwork_files) + { + /* Rescan the work directory. */ + if (! fsysdep_get_work_init (qsys, bgrade)) + { + ubuffree (zdir); + return FALSE; + } + if (iSwork_file >= cSwork_files) + { + qcmd->bcmd = 'H'; + ubuffree (zdir); + return TRUE; + } + } + + if (zdir == NULL) + { + zdir = zswork_directory (qsys->uuconf_zname); + if (zdir == NULL) + return FALSE; + } + + zname = zsysdep_in_dir (zdir, azSwork_files[iSwork_file]); + + ++iSwork_file; + + e = fopen (zname, "r"); + if (e == NULL) + { + ulog (LOG_ERROR, "fopen (%s): %s", zname, + strerror (errno)); + ubuffree (zname); + } + } + while (e == NULL); + + qfile = (struct ssfile *) xmalloc (sizeof (struct ssfile)); + callocated = CFILELINES; + iline = 0; + + zline = NULL; + cline = 0; + while (getline (&zline, &cline, e) > 0) + { + if (iline >= callocated) + { + /* The sizeof (struct ssfile) includes CFILELINES + entries already, so using callocated * sizeof + (struct ssline) will give us callocated * + CFILELINES entries. */ + qfile = + ((struct ssfile *) + xrealloc ((pointer) qfile, + (sizeof (struct ssfile) + + (callocated * sizeof (struct ssline))))); + callocated += CFILELINES; + } + qfile->aslines[iline].zline = zbufcpy (zline); + qfile->aslines[iline].qfile = NULL; + qfile->aslines[iline].ztemp = NULL; + iline++; + } + + xfree ((pointer) zline); + + if (fclose (e) != 0) + ulog (LOG_ERROR, "fclose: %s", strerror (errno)); + + if (iline == 0) + { + /* There were no lines in the file; this is a poll file, + for which we return a 'P' command. */ + qfile->aslines[0].zline = zbufcpy ("P"); + qfile->aslines[0].qfile = NULL; + qfile->aslines[0].ztemp = NULL; + iline = 1; + } + + qfile->zfile = zname; + qfile->clines = iline; + qfile->cdid = 0; + qSwork_file = qfile; + } + + /* This loop continues until all the lines from the current file + are used up, or a line is returned. */ + while (TRUE) + { + int iline; + + if (qSwork_file->cdid >= qSwork_file->clines) + { + /* We don't want to free qSwork_file here, since it must + remain until all the lines have been completed. It + is freed in fsysdep_did_work. */ + qSwork_file = NULL; + /* Go back to the main loop which finds another file. */ + break; + } + + iline = qSwork_file->cdid; + ++qSwork_file->cdid; + + /* Now parse the line into a command. */ + if (! fparse_cmd (qSwork_file->aslines[iline].zline, qcmd)) + { + ulog (LOG_ERROR, "Bad line in command file %s", + qSwork_file->zfile); + ubuffree (qSwork_file->aslines[iline].zline); + qSwork_file->aslines[iline].zline = NULL; + continue; + } + + qSwork_file->aslines[iline].qfile = qSwork_file; + qcmd->pseq = (pointer) (&qSwork_file->aslines[iline]); + + if (qcmd->bcmd == 'S' || qcmd->bcmd == 'E') + { + char *zreal; + + zreal = zsysdep_spool_file_name (qsys, qcmd->ztemp, + qcmd->pseq); + if (zreal == NULL) + { + ubuffree (qSwork_file->aslines[iline].zline); + qSwork_file->aslines[iline].zline = NULL; + ubuffree (zdir); + return FALSE; + } + qSwork_file->aslines[iline].ztemp = zreal; + } + + ubuffree (zdir); + return TRUE; + } + } +} + +/* When a command has been complete, fsysdep_did_work is called. The + sequence entry was set above to be the address of an aslines + structure whose pfile entry points to the ssfile corresponding to + this file. We can then check whether all the lines have been + completed (they will have been if the pfile entry is NULL) and + remove the file if they have been. This means that we only remove + a command file if we manage to complete every transfer it specifies + in a single UUCP session. I don't know if this is how regular UUCP + works. */ + +boolean +fsysdep_did_work (pseq) + pointer pseq; +{ + struct ssfile *qfile; + struct ssline *qline; + int i; + + qline = (struct ssline *) pseq; + + ubuffree (qline->zline); + qline->zline = NULL; + + qfile = qline->qfile; + qline->qfile = NULL; + + /* Remove the temporary file, if there is one. It really doesn't + matter if this fails, and not checking the return value lets us + attempt to remove D.0 or whatever an unused temporary file is + called without complaining. */ + if (qline->ztemp != NULL) + { + (void) remove (qline->ztemp); + ubuffree (qline->ztemp); + qline->ztemp = NULL; + } + + /* If not all the lines have been returned from fsysdep_get_work, + we can't remove the file yet. */ + if (qfile->cdid < qfile->clines) + return TRUE; + + /* See whether all the commands have been completed. */ + for (i = 0; i < qfile->clines; i++) + if (qfile->aslines[i].qfile != NULL) + return TRUE; + + /* All commands have finished. */ + if (remove (qfile->zfile) != 0) + { + ulog (LOG_ERROR, "remove (%s): %s", qfile->zfile, + strerror (errno)); + return FALSE; + } + + ubuffree (qfile->zfile); + xfree ((pointer) qfile); + + if (qfile == qSwork_file) + qSwork_file = NULL; + + return TRUE; +} + +/* Free up the results of a work scan, when we're done with this + system. */ + +/*ARGSUSED*/ +void +usysdep_get_work_free (qsys) + const struct uuconf_system *qsys; +{ + if (azSwork_files != NULL) + { + size_t i; + + for (i = 0; i < cSwork_files; i++) + ubuffree ((pointer) azSwork_files[i]); + xfree ((pointer) azSwork_files); + azSwork_files = NULL; + cSwork_files = 0; + iSwork_file = 0; + } + if (qSwork_file != NULL) + { + int i; + + ubuffree (qSwork_file->zfile); + for (i = 0; i < qSwork_file->cdid; i++) + { + ubuffree (qSwork_file->aslines[i].zline); + ubuffree (qSwork_file->aslines[i].ztemp); + } + for (i = qSwork_file->cdid; i < qSwork_file->clines; i++) + ubuffree (qSwork_file->aslines[i].zline); + xfree ((pointer) qSwork_file); + qSwork_file = NULL; + } +} + +/* Save the temporary file used by a send command, and return an + informative message to mail to the requestor. This is called when + a file transfer failed, to make sure that the potentially valuable + file is not completely lost. */ + +const char * +zsysdep_save_temp_file (pseq) + pointer pseq; +{ + struct ssline *qline = (struct ssline *) pseq; + char *zto, *zslash; + size_t cwant; + static char *zbuf; + static int cbuf; + + if (! fsysdep_file_exists (qline->ztemp)) + return NULL; + + zslash = strrchr (qline->ztemp, '/'); + if (zslash == NULL) + zslash = qline->ztemp; + else + ++zslash; + + zto = zbufalc (sizeof PRESERVEDIR + sizeof "/" + strlen (zslash)); + sprintf (zto, "%s/%s", PRESERVEDIR, zslash); + + if (! fsysdep_move_file (qline->ztemp, zto, TRUE, FALSE, FALSE, + (const char *) NULL)) + { + ubuffree (zto); + return "Could not move file to preservation directory"; + } + + cwant = sizeof "File saved as\n\t/" + strlen (zSspooldir) + strlen (zto); + if (cwant > cbuf) + { + ubuffree (zbuf); + zbuf = zbufalc (cwant); + cbuf = cwant; + } + + sprintf (zbuf, "File saved as\n\t%s/%s", zSspooldir, zto); + ubuffree (zto); + return zbuf; +} + +/* Get the jobid of a work file. This is needed by uustat. */ + +char * +zsysdep_jobid (qsys, pseq) + const struct uuconf_system *qsys; + pointer pseq; +{ + return zsfile_to_jobid (qsys, ((struct ssline *) pseq)->qfile->zfile, + bsgrade (pseq)); +} + +/* Get the grade of a work file. The pseq argument can be NULL when + this is called from zsysdep_spool_file_name, and simply means that + this is a remote file; returning -1 will cause zsfind_file to do + the right thing. */ + +char +bsgrade (pseq) + pointer pseq; +{ + const char *zfile; + char bgrade; + + if (pseq == NULL) + return -1; + + zfile = ((struct ssline *) pseq)->qfile->zfile; + +#if ! SPOOLDIR_SVR4 + bgrade = zfile[strlen (zfile) - CSEQLEN - 1]; +#else + bgrade = *(strchr (zfile, '/') + 1); +#endif + + return bgrade; +} diff --git a/gnu/libexec/uucp/libunix/xqtfil.c b/gnu/libexec/uucp/libunix/xqtfil.c new file mode 100644 index 0000000000..2cdcc185a0 --- /dev/null +++ b/gnu/libexec/uucp/libunix/xqtfil.c @@ -0,0 +1,265 @@ +/* xqtfil.c + Routines to read execute files. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char xqtfil_rcsid[] = "$Id: xqtfil.c,v 1.1 1993/08/04 19:33:21 jtc Exp $"; +#endif + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include + +#if HAVE_OPENDIR +#if HAVE_DIRENT_H +#include +#else /* ! HAVE_DIRENT_H */ +#include +#define dirent direct +#endif /* ! HAVE_DIRENT_H */ +#endif /* HAVE_OPENDIR */ + +/* Under the V2 or BSD42 spool directory scheme, all execute files are + in the main spool directory. Under the BSD43 scheme, they are all + in the directory X.. Under the HDB or SVR4 scheme, they are in + directories named after systems. Under the ULTRIX scheme, they are + in X. subdirectories of subdirectories of sys. Under the TAYLOR + scheme, they are all in the subdirectory X. of a directory named + after the system. + + This means that for HDB, ULTRIX, SVR4 or TAYLOR, we have to search + directories of directories. */ + +#if SPOOLDIR_V2 || SPOOLDIR_BSD42 +#define ZDIR "." +#define SUBDIRS 0 +#endif +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 || SPOOLDIR_TAYLOR +#define ZDIR "." +#define SUBDIRS 1 +#endif +#if SPOOLDIR_ULTRIX +#define ZDIR "sys" +#define SUBDIRS 1 +#endif +#if SPOOLDIR_BSD43 +#define ZDIR "X." +#define SUBDIRS 0 +#endif + +/* Static variables for the execute file scan. */ + +static DIR *qSxqt_topdir; +#if ! SUBDIRS +static const char *zSdir; +#else /* SUBDIRS */ +static char *zSdir; +static DIR *qSxqt_dir; +static char *zSsystem; +#endif /* SUBDIRS */ + +/* Initialize the scan for execute files. The function + usysdep_get_xqt_free will clear the data out when we are done with + the system. This returns FALSE on error. */ + +/*ARGSUSED*/ +boolean +fsysdep_get_xqt_init () +{ + usysdep_get_xqt_free (); + + qSxqt_topdir = opendir ((char *) ZDIR); + if (qSxqt_topdir == NULL) + { + if (errno == ENOENT) + return TRUE; + ulog (LOG_ERROR, "opendir (%s): %s", ZDIR, strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/* Return the name of the next execute file to read and process. If + this returns NULL, *pferr must be checked. If will be TRUE on + error, FALSE if there are no more files. On a successful return + *pzsystem will be set to the system for which the execute file was + created. */ + +char * +zsysdep_get_xqt (pzsystem, pferr) + char **pzsystem; + boolean *pferr; +{ + *pferr = FALSE; + + if (qSxqt_topdir == NULL) + return NULL; + + /* This loop continues until we find a file. */ + while (TRUE) + { + DIR *qdir; + struct dirent *q; + +#if ! SUBDIRS + zSdir = ZDIR; + qdir = qSxqt_topdir; +#else /* SUBDIRS */ + /* This loop continues until we find a subdirectory to read. */ + while (qSxqt_dir == NULL) + { + struct dirent *qtop; + + qtop = readdir (qSxqt_topdir); + if (qtop == NULL) + { + (void) closedir (qSxqt_topdir); + qSxqt_topdir = NULL; + return NULL; + } + + /* No system name may start with a dot (this is enforced by + tisystem in sysinf.c). This allows us to quickly skip + impossible directories. */ + if (qtop->d_name[0] == '.') + continue; + + DEBUG_MESSAGE1 (DEBUG_SPOOLDIR, + "zsysdep_get_xqt: Found %s in top directory", + qtop->d_name); + + ubuffree (zSdir); + +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 + zSdir = zbufcpy (qtop->d_name); +#endif +#if SPOOLDIR_ULTRIX + zSdir = zsappend3 ("sys", qtop->d_name, "X."); +#endif +#if SPOOLDIR_TAYLOR + zSdir = zsysdep_in_dir (qtop->d_name, "X."); +#endif + + ubuffree (zSsystem); + zSsystem = zbufcpy (qtop->d_name); + + qSxqt_dir = opendir (zSdir); + + if (qSxqt_dir == NULL + && errno != ENOTDIR + && errno != ENOENT) + ulog (LOG_ERROR, "opendir (%s): %s", zSdir, strerror (errno)); + } + + qdir = qSxqt_dir; +#endif /* SUBDIRS */ + + q = readdir (qdir); + +#if DEBUG > 1 + if (q != NULL) + DEBUG_MESSAGE2 (DEBUG_SPOOLDIR, + "zsysdep_get_xqt: Found %s in subdirectory %s", + q->d_name, zSdir); +#endif + + /* If we've found an execute file, return it. We have to get + the system name, which is easy for HDB or TAYLOR. For other + spool directory schemes, we have to pull it out of the X. + file name; this would be insecure, except that zsfind_file + clobbers the file name to include the real system name. */ + if (q != NULL + && q->d_name[0] == 'X' + && q->d_name[1] == '.') + { + char *zret; + +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 || SPOOLDIR_TAYLOR + *pzsystem = zbufcpy (zSsystem); +#else + { + size_t clen; + + clen = strlen (q->d_name) - 7; + *pzsystem = zbufalc (clen + 1); + memcpy (*pzsystem, q->d_name + 2, clen); + (*pzsystem)[clen] = '\0'; + } +#endif + + zret = zsysdep_in_dir (zSdir, q->d_name); +#if DEBUG > 1 + DEBUG_MESSAGE2 (DEBUG_SPOOLDIR, + "zsysdep_get_xqt: Returning %s (system %s)", + zret, *pzsystem); +#endif + return zret; + } + + /* If we've reached the end of the directory, then if we are + using subdirectories loop around to read the next one, + otherwise we are finished. */ + if (q == NULL) + { + (void) closedir (qdir); +#if SUBDIRS + qSxqt_dir = NULL; + continue; +#else + qSxqt_topdir = NULL; + return NULL; +#endif + } + } +} + +/* Free up the results of an execute file scan, when we're done with + this system. */ + +/*ARGSUSED*/ +void +usysdep_get_xqt_free () +{ + if (qSxqt_topdir != NULL) + { + (void) closedir (qSxqt_topdir); + qSxqt_topdir = NULL; + } +#if SUBDIRS + if (qSxqt_dir != NULL) + { + (void) closedir (qSxqt_dir); + qSxqt_dir = NULL; + } + ubuffree (zSdir); + zSdir = NULL; + ubuffree (zSsystem); + zSsystem = NULL; +#endif +} diff --git a/gnu/libexec/uucp/libunix/xqtsub.c b/gnu/libexec/uucp/libunix/xqtsub.c new file mode 100644 index 0000000000..53e290a0d1 --- /dev/null +++ b/gnu/libexec/uucp/libunix/xqtsub.c @@ -0,0 +1,698 @@ +/* xqtsub.c + System dependent functions used only by uuxqt. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char xqtsub_rcsid[] = "$Id: xqtsub.c,v 1.1 1993/08/04 19:33:22 jtc Exp $"; +#endif + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" +#include "sysdep.h" + +#include +#include + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +#if HAVE_OPENDIR +#if HAVE_DIRENT_H +#include +#else /* ! HAVE_DIRENT_H */ +#include +#define dirent direct +#endif /* ! HAVE_DIRENT_H */ +#endif /* HAVE_OPENDIR */ + +/* Get a value for EX_TEMPFAIL. */ + +#if HAVE_SYSEXITS_H +#include +#endif + +#ifndef EX_TEMPFAIL +#define EX_TEMPFAIL 75 +#endif + +/* Get the full pathname of the command to execute, given the list of + permitted commands and the allowed path. */ + +char * +zsysdep_find_command (zcmd, pzcmds, pzpath, pferr) + const char *zcmd; + char **pzcmds; + char **pzpath; + boolean *pferr; +{ + char **pz; + + *pferr = FALSE; + + for (pz = pzcmds; *pz != NULL; pz++) + { + char *zslash; + + if (strcmp (*pz, "ALL") == 0) + break; + + zslash = strrchr (*pz, '/'); + if (zslash != NULL) + ++zslash; + else + zslash = *pz; + if (strcmp (zslash, zcmd) == 0 + || strcmp (*pz, zcmd) == 0) + { + /* If we already have an absolute path, we can get out + immediately. */ + if (**pz == '/') + return zbufcpy (*pz); + break; + } + } + + /* If we didn't find this command, get out. */ + if (*pz == NULL) + return NULL; + + /* We didn't find an absolute pathname, so we must look through + the path. */ + for (pz = pzpath; *pz != NULL; pz++) + { + char *zname; + struct stat s; + + zname = zsysdep_in_dir (*pz, zcmd); + if (stat (zname, &s) == 0) + return zname; + } + + *pferr = FALSE; + return NULL; +} + +/* Expand a local filename for uuxqt. This is special because uuxqt + only wants to expand filenames that start with ~ (it does not want + to prepend the current directory to other names) and if the ~ is + double, it is turned into a single ~. This returns NULL to + indicate that no change was required; it has no way to return + error. */ + +char * +zsysdep_xqt_local_file (qsys, zfile) + const struct uuconf_system *qsys; + const char *zfile; +{ + if (*zfile != '~') + return NULL; + if (zfile[1] == '~') + { + size_t clen; + char *zret; + + clen = strlen (zfile); + zret = zbufalc (clen); + memcpy (zret, zfile + 1, clen); + return zret; + } + return zsysdep_local_file (zfile, qsys->uuconf_zpubdir); +} + +#if ! ALLOW_FILENAME_ARGUMENTS + +/* Check to see whether an argument specifies a file name; if it does, + make sure that the file may legally be sent and/or received. For + Unix, we do not permit any occurrence of "/../" in the name, nor + may it start with "../". Otherwise, if it starts with "/" we check + against the list of permitted files. */ + +boolean +fsysdep_xqt_check_file (qsys, zfile) + const struct uuconf_system *qsys; + const char *zfile; +{ + size_t clen; + + clen = strlen (zfile); + if ((clen == sizeof "../" - 1 + && strcmp (zfile, "../") == 0) + || (clen >= sizeof "/.." - 1 + && strcmp (zfile + clen - (sizeof "/.." - 1), "/..") == 0) + || strstr (zfile, "/../") != NULL + || (*zfile == '/' + && (! fin_directory_list (zfile, qsys->uuconf_pzremote_send, + qsys->uuconf_zpubdir, TRUE, FALSE, + (const char *) NULL) + || ! fin_directory_list (zfile, qsys->uuconf_pzremote_receive, + qsys->uuconf_zpubdir, TRUE, FALSE, + (const char *) NULL)))) + { + ulog (LOG_ERROR, "Not permitted to refer to file \"%s\"", zfile); + return FALSE; + } + + return TRUE; +} + +#endif /* ! ALLOW_FILENAME_ARGUMENTS */ + +/* Invoke the command specified by an execute file. */ + +/*ARGSUSED*/ +boolean +fsysdep_execute (qsys, zuser, pazargs, zfullcmd, zinput, zoutput, + fshell, iseq, pzerror, pftemp) + const struct uuconf_system *qsys; + const char *zuser; + const char **pazargs; + const char *zfullcmd; + const char *zinput; + const char *zoutput; + boolean fshell; + int iseq; + char **pzerror; + boolean *pftemp; +{ + int aidescs[3]; + boolean ferr; + pid_t ipid; + int ierr; + char abxqtdir[sizeof XQTDIR + 4]; + const char *zxqtdir; + int istat; + char *zpath; +#if ALLOW_SH_EXECUTION + const char *azshargs[4]; +#endif + + *pzerror = NULL; + *pftemp = FALSE; + + aidescs[0] = SPAWN_NULL; + aidescs[1] = SPAWN_NULL; + aidescs[2] = SPAWN_NULL; + + ferr = FALSE; + + if (zinput != NULL) + { + aidescs[0] = open ((char *) zinput, O_RDONLY | O_NOCTTY, 0); + if (aidescs[0] < 0) + { + ulog (LOG_ERROR, "open (%s): %s", zinput, strerror (errno)); + ferr = TRUE; + } + else if (fcntl (aidescs[0], F_SETFD, + fcntl (aidescs[0], F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + ferr = TRUE; + } + } + + if (! ferr && zoutput != NULL) + { + aidescs[1] = creat ((char *) zoutput, IPRIVATE_FILE_MODE); + if (aidescs[1] < 0) + { + ulog (LOG_ERROR, "creat (%s): %s", zoutput, strerror (errno)); + *pftemp = TRUE; + ferr = TRUE; + } + else if (fcntl (aidescs[1], F_SETFD, + fcntl (aidescs[1], F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + ferr = TRUE; + } + } + + if (! ferr) + { + *pzerror = zstemp_file (qsys); + aidescs[2] = creat (*pzerror, IPRIVATE_FILE_MODE); + if (aidescs[2] < 0) + { + if (errno == ENOENT) + { + if (! fsysdep_make_dirs (*pzerror, FALSE)) + { + *pftemp = TRUE; + ferr = TRUE; + } + else + aidescs[2] = creat (*pzerror, IPRIVATE_FILE_MODE); + } + if (! ferr && aidescs[2] < 0) + { + ulog (LOG_ERROR, "creat (%s): %s", *pzerror, strerror (errno)); + *pftemp = TRUE; + ferr = TRUE; + } + } + if (! ferr + && fcntl (aidescs[2], F_SETFD, + fcntl (aidescs[2], F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + ferr = TRUE; + } + } + + if (iseq == 0) + zxqtdir = XQTDIR; + else + { + sprintf (abxqtdir, "%s%04d", XQTDIR, iseq); + zxqtdir = abxqtdir; + } + + if (ferr) + { + if (aidescs[0] != SPAWN_NULL) + (void) close (aidescs[0]); + if (aidescs[1] != SPAWN_NULL) + (void) close (aidescs[1]); + if (aidescs[2] != SPAWN_NULL) + (void) close (aidescs[2]); + ubuffree (*pzerror); + return FALSE; + } + +#if ALLOW_SH_EXECUTION + if (fshell) + { + azshargs[0] = "/bin/sh"; + azshargs[1] = "-c"; + azshargs[2] = zfullcmd; + azshargs[3] = NULL; + pazargs = azshargs; + } +#else + fshell = FALSE; +#endif + + if (qsys->uuconf_pzpath == NULL) + zpath = NULL; + else + { + size_t c; + char **pz; + + c = 0; + for (pz = qsys->uuconf_pzpath; *pz != NULL; pz++) + c += strlen (*pz) + 1; + zpath = zbufalc (c); + *zpath = '\0'; + for (pz = qsys->uuconf_pzpath; *pz != NULL; pz++) + { + strcat (zpath, *pz); + if (pz[1] != NULL) + strcat (zpath, ":"); + } + } + + /* Pass zchdir as zxqtdir, fnosigs as TRUE, fshell as TRUE if we + aren't already using the shell. */ + ipid = ixsspawn (pazargs, aidescs, FALSE, FALSE, zxqtdir, TRUE, + ! fshell, zpath, qsys->uuconf_zname, zuser); + + ierr = errno; + + ubuffree (zpath); + + if (aidescs[0] != SPAWN_NULL) + (void) close (aidescs[0]); + if (aidescs[1] != SPAWN_NULL) + (void) close (aidescs[1]); + if (aidescs[2] != SPAWN_NULL) + (void) close (aidescs[2]); + + if (ipid < 0) + { + ulog (LOG_ERROR, "ixsspawn: %s", strerror (ierr)); + *pftemp = TRUE; + return FALSE; + } + + istat = ixswait ((unsigned long) ipid, "Execution"); + + if (istat == EX_TEMPFAIL) + *pftemp = TRUE; + + return istat == 0; +} + +/* Lock a uuxqt process. */ + +int +ixsysdep_lock_uuxqt (zcmd, cmaxuuxqts) + const char *zcmd; + int cmaxuuxqts; +{ + char ab[sizeof "LCK.XQT.9999"]; + int i; + + if (cmaxuuxqts <= 0 || cmaxuuxqts >= 10000) + cmaxuuxqts = 9999; + for (i = 0; i < cmaxuuxqts; i++) + { + sprintf (ab, "LCK.XQT.%d", i); + if (fsdo_lock (ab, TRUE, (boolean *) NULL)) + break; + } + if (i >= cmaxuuxqts) + return -1; + + if (zcmd != NULL) + { + char abcmd[sizeof "LXQ.123456789"]; + + sprintf (abcmd, "LXQ.%.9s", zcmd); + abcmd[strcspn (abcmd, " \t/")] = '\0'; + if (! fsdo_lock (abcmd, TRUE, (boolean *) NULL)) + { + (void) fsdo_unlock (ab, TRUE); + return -1; + } + } + + return i; +} + +/* Unlock a uuxqt process. */ + +boolean +fsysdep_unlock_uuxqt (iseq, zcmd, cmaxuuxqts) + int iseq; + const char *zcmd; + int cmaxuuxqts; +{ + char ab[sizeof "LCK.XQT.9999"]; + boolean fret; + + fret = TRUE; + + sprintf (ab, "LCK.XQT.%d", iseq); + if (! fsdo_unlock (ab, TRUE)) + fret = FALSE; + + if (zcmd != NULL) + { + char abcmd[sizeof "LXQ.123456789"]; + + sprintf (abcmd, "LXQ.%.9s", zcmd); + abcmd[strcspn (abcmd, " \t/")] = '\0'; + if (! fsdo_unlock (abcmd, TRUE)) + fret = FALSE; + } + + return fret; +} + +/* See whether a particular uuxqt command is locked (this depends on + the implementation of fsdo_lock). */ + +boolean +fsysdep_uuxqt_locked (zcmd) + const char *zcmd; +{ + char ab[sizeof "LXQ.123456789"]; + struct stat s; + + sprintf (ab, "LXQ.%.9s", zcmd); + return stat (ab, &s) == 0; +} + +/* Lock a particular execute file. */ + +boolean +fsysdep_lock_uuxqt_file (zfile) + const char *zfile; +{ + char *zcopy, *z; + boolean fret; + + zcopy = zbufcpy (zfile); + + z = strrchr (zcopy, '/'); + if (z == NULL) + *zcopy = 'L'; + else + *(z + 1) = 'L'; + + fret = fsdo_lock (zcopy, TRUE, (boolean *) NULL); + ubuffree (zcopy); + return fret; +} + +/* Unlock a particular execute file. */ + +boolean +fsysdep_unlock_uuxqt_file (zfile) + const char *zfile; +{ + char *zcopy, *z; + boolean fret; + + zcopy = zbufcpy (zfile); + + z = strrchr (zcopy, '/'); + if (z == NULL) + *zcopy = 'L'; + else + *(z + 1) = 'L'; + + fret = fsdo_unlock (zcopy, TRUE); + ubuffree (zcopy); + return fret; +} + +/* Lock the execute directory. Since we use a different directory + depending on which LCK.XQT.dddd file we got, there is actually no + need to create a lock file. We do make sure that the directory + exists, though. */ + +boolean +fsysdep_lock_uuxqt_dir (iseq) + int iseq; +{ + const char *zxqtdir; + char abxqtdir[sizeof XQTDIR + 4]; + + if (iseq == 0) + zxqtdir = XQTDIR; + else + { + sprintf (abxqtdir, "%s%04d", XQTDIR, iseq); + zxqtdir = abxqtdir; + } + + if (mkdir (zxqtdir, S_IRWXU) < 0 + && errno != EEXIST) + { + ulog (LOG_ERROR, "mkdir (%s): %s", zxqtdir, strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/* Unlock the execute directory and clear it out. The lock is + actually the LCK.XQT.dddd file, so we don't unlock it, but we do + remove all the files. */ + +boolean +fsysdep_unlock_uuxqt_dir (iseq) + int iseq; +{ + const char *zxqtdir; + char abxqtdir[sizeof XQTDIR + 4]; + DIR *qdir; + + if (iseq == 0) + zxqtdir = XQTDIR; + else + { + sprintf (abxqtdir, "%s%04d", XQTDIR, iseq); + zxqtdir = abxqtdir; + } + + qdir = opendir ((char *) zxqtdir); + if (qdir != NULL) + { + struct dirent *qentry; + + while ((qentry = readdir (qdir)) != NULL) + { + char *z; + + if (strcmp (qentry->d_name, ".") == 0 + || strcmp (qentry->d_name, "..") == 0) + continue; + z = zsysdep_in_dir (zxqtdir, qentry->d_name); + if (remove (z) < 0) + { + int ierr; + + ierr = errno; + if (! fsysdep_directory (z)) + ulog (LOG_ERROR, "remove (%s): %s", z, + strerror (ierr)); + else + (void) fsysdep_rmdir (z); + } + ubuffree (z); + } + + closedir (qdir); + } + + return TRUE; +} + +/* Move files into the execution directory. */ + +boolean +fsysdep_move_uuxqt_files (cfiles, pzfrom, pzto, fto, iseq, pzinput) + int cfiles; + const char *const *pzfrom; + const char *const *pzto; + boolean fto; + int iseq; + char **pzinput; +{ + char *zinput; + const char *zxqtdir; + char abxqtdir[sizeof XQTDIR + 4]; + int i; + + if (pzinput == NULL) + zinput = NULL; + else + zinput = *pzinput; + + if (iseq == 0) + zxqtdir = XQTDIR; + else + { + sprintf (abxqtdir, "%s%04d", XQTDIR, iseq); + zxqtdir = abxqtdir; + } + + for (i = 0; i < cfiles; i++) + { + const char *zfrom, *zto; + char *zfree; + + if (pzto[i] == NULL) + continue; + + zfree = zsysdep_in_dir (zxqtdir, pzto[i]); + + zfrom = pzfrom[i]; + zto = zfree; + + if (zinput != NULL && strcmp (zinput, zfrom) == 0) + { + *pzinput = zbufcpy (zto); + zinput = NULL; + } + + if (! fto) + { + const char *ztemp; + + ztemp = zfrom; + zfrom = zto; + zto = ztemp; + (void) chmod (zfrom, IPRIVATE_FILE_MODE); + } + + if (rename (zfrom, zto) < 0) + { +#if HAVE_RENAME + /* On some systems the system call rename seems to fail for + arbitrary reasons. To get around this, we always try to + copy the file by hand if the rename failed. */ + errno = EXDEV; +#endif + + if (errno != EXDEV) + { + ulog (LOG_ERROR, "rename (%s, %s): %s", zfrom, zto, + strerror (errno)); + ubuffree (zfree); + break; + } + + if (! fcopy_file (zfrom, zto, FALSE, FALSE)) + { + ubuffree (zfree); + break; + } + if (remove (zfrom) < 0) + ulog (LOG_ERROR, "remove (%s): %s", zfrom, + strerror (errno)); + } + + if (fto) + (void) chmod (zto, IPUBLIC_FILE_MODE); + + ubuffree (zfree); + } + + if (i < cfiles) + { + if (fto) + (void) fsysdep_move_uuxqt_files (i, pzfrom, pzto, FALSE, iseq, + (char **) NULL); + return FALSE; + } + + return TRUE; +} diff --git a/gnu/libexec/uucp/libuuconf/COPYING.LIB b/gnu/libexec/uucp/libuuconf/COPYING.LIB new file mode 100644 index 0000000000..eb685a5ec9 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/COPYING.LIB @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/gnu/libexec/uucp/libuuconf/MANIFEST b/gnu/libexec/uucp/libuuconf/MANIFEST new file mode 100644 index 0000000000..8d1eb36466 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/MANIFEST @@ -0,0 +1,92 @@ +README +COPYING.LIB +MANIFEST +Makefile.in +alloc.h +syshdr.unx +uucnfi.h +addblk.c +addstr.c +allblk.c +alloc.c +base.c +bool.c +callin.c +calout.c +chatc.c +cmdarg.c +cmdfil.c +cmdlin.c +debfil.c +deblev.c +diacod.c +dial.c +diasub.c +dnams.c +errno.c +errstr.c +filnam.c +freblk.c +fredia.c +free.c +freprt.c +fresys.c +grdcmp.c +hdial.c +hdnams.c +hinit.c +hlocnm.c +hport.c +hrmunk.c +hsinfo.c +hsnams.c +hsys.c +hunk.c +iniglb.c +init.c +int.c +lckdir.c +lineno.c +llocnm.c +local.c +locnm.c +logfil.c +maxuxq.c +mrgblk.c +paramc.c +port.c +pubdir.c +prtsub.c +rdlocs.c +rdperm.c +reliab.c +remunk.c +sinfo.c +snams.c +split.c +spool.c +stafil.c +syssub.c +tcalou.c +tdial.c +tdialc.c +tdnams.c +tgcmp.c +thread.c +time.c +tinit.c +tlocnm.c +tport.c +tportc.c +tsinfo.c +tsnams.c +tsys.c +tval.c +ugtlin.c +unk.c +val.c +vinit.c +vport.c +vsinfo.c +vsnams.c +vsys.c diff --git a/gnu/libexec/uucp/libuuconf/Makefile b/gnu/libexec/uucp/libuuconf/Makefile new file mode 100644 index 0000000000..066e928aaf --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/Makefile @@ -0,0 +1,26 @@ +# This is the Makefile for the Taylor UUCP uuconf library +# $Id: Makefile,v 1.2 1993/08/05 16:14:55 jtc Exp $ + +LIB= uuconf +SRCS= addblk.c addstr.c allblk.c alloc.c base.c bool.c callin.c \ + calout.c chatc.c cmdarg.c cmdfil.c cmdlin.c debfil.c deblev.c \ + diacod.c dial.c diasub.c dnams.c errno.c errstr.c filnam.c \ + freblk.c fredia.c free.c freprt.c fresys.c grdcmp.c hdial.c \ + hdnams.c hinit.c hlocnm.c hport.c hrmunk.c hsinfo.c hsnams.c \ + hsys.c hunk.c iniglb.c init.c int.c lckdir.c lineno.c llocnm.c \ + local.c locnm.c logfil.c maxuxq.c mrgblk.c paramc.c port.c \ + prtsub.c pubdir.c rdlocs.c rdperm.c reliab.c remunk.c sinfo.c \ + snams.c split.c spool.c stafil.c syssub.c tcalou.c tdial.c \ + tdialc.c tdnams.c tgcmp.c thread.c time.c tinit.c tlocnm.c \ + tport.c tportc.c tsinfo.c tsnams.c tsys.c tval.c ugtlin.c \ + unk.c val.c vinit.c vport.c vsinfo.c vsnams.c vsys.c +CFLAGS+= -I$(.CURDIR)/../common_sources \ + -DNEWCONFIGLIB=\"$(newconfigdir)\"\ + -DOLDCONFIGLIB=\"$(oldconfigdir)\" + +NOMAN= noman +NOPROFILE= noprofile + +install: + +.include diff --git a/gnu/libexec/uucp/libuuconf/README b/gnu/libexec/uucp/libuuconf/README new file mode 100644 index 0000000000..64a5eecf03 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/README @@ -0,0 +1,113 @@ +This is the README file for the beta release of the uuconf library. + +It was written by Ian Lance Taylor. I can be reached at ian@airs.com, +or, equivalently, uunet!airs!ian, or c/o Infinity Development Systems, +P.O. Box 520, Waltham MA, 02254. + +This package is covered by the Gnu Library General Public License. +See the file COPYING.LIB for details. If you would like to do +something with this package that you feel is reasonable but you feel +is prohibited by the license, contact me to see if we can work it out. + +WHAT IT IS + +This is a beta release of the uuconf library. The uuconf library +provides a set of functions which can be used to read UUCP +configuration files. V2, HDB, and Taylor UUCP configuration files are +supported. + +Also included are two programs, uuchk and uuconv. uuchk will read +configuration files and display the information it finds in a verbose +format. This can be helpful to ensure that your configuration files +are set up as you expect. uuconv can be used to convert configuration +files from one type to another. This is particularly helpful for +people installing Taylor UUCP on a existing system who want to take +advantage of the additional functionality provided by the Taylor UUCP +configuration files. + +This is strictly a beta release. The library provides all the +information needed for uuchk and uuconv, but does not yet provide +everything needed for uucp or cu. I am releasing it now to get +feedback and to provide the uuconv program to people using Taylor +UUCP. + +This may well be the only time this library is release independently. +This library will be provided with Taylor UUCP, and future releases of +the library will probably only occur as part of the complete Taylor +UUCP package. + +HOW TO USE IT + +Configure and optionally install the package as described in INSTALL. + +The functions provided by the library are described in uuconf.h. At +the moment there is no additional documentation. + +Programs which use the library should include uuconf.h, and should not +include any of the other header files. The functions listed in +uuconf.h all begin with the string "uuconf_". The internal library +functions all begin with the string "_uuconf_". The internal library +functions should not be called by a program which uses the library, as +they may change in future releases. The uuchk program is an example +of program which uses the library; uuconv is not, as it relies upon +internal data structures. + +The uuchk program takes a single optional option, -I, which may be +used to specify an alternate Taylor UUCP main configuration file. The +default configuration file is $(newconfigdir)/config ($(newconfigdir) +is defined in Makefile). For example: + uuchk + uuchk -I /usr/tmp/tstuu/Config1 + +The uuconv program requires two options: -i to specify the input type +and -o to specify the output type. Both options take a string +argument, which must be one of "v2", "hdb", or "taylor". uuconv also +takes an optional -I option, which is the same as the -I option to +uuchk. The conversion is not intended to be perfect, and the results +should be manually inspected. In particular, the dialcode file is not +converted (as the format is the same for all three configuration file +types, it may simply be copied to the appropriate new name). uuconv +will create new files in the current working directory. For example: + uuconv -i hdb -o taylor + uuconv -i taylor -I /usr/tmp/tstuu/Config1 -o v2 + +NOTES + +The initial underscore on the internal library functions is required +by the GNU standards. As ANSI C reserves external identifiers with an +initial underscore for the implementation, it is possible, though +unlikely, that this will cause problems on other implementations; no +workaround is currently provided for such problems. + +The library functions rely upon the following functions: + + fclose fopen free fseek + ftell getc isalpha isdigit + islower isspace isupper malloc + realloc rewind strchr strcmp + strcspn strlen strncmp strspn + tolower toupper + +and the following header files: + + ctype.h errno.h stdio.h + +If the following functions cannot be found by the configure script, +replacements will be used (the replacement for strerror is Unix +dependent): + + getline memcpy strcasecmp strdup + strerror strncasecmp strtol + +If the following header files are found, they will be included: + + libc.h limits.h memory.h stddef.h + stdlib.h string.h strings.h sys/types.h + +The following functions are required on Unix only: + + fcntl fileno + +The following headers are used, if found, on Unix only: + + fcntl.h sys/file.h diff --git a/gnu/libexec/uucp/libuuconf/addblk.c b/gnu/libexec/uucp/libuuconf/addblk.c new file mode 100644 index 0000000000..6244dad875 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/addblk.c @@ -0,0 +1,56 @@ +/* addblk.c + Add an malloc block to a memory block. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_addblk_rcsid[] = "$Id: addblk.c,v 1.1 1993/08/04 19:33:31 jtc Exp $"; +#endif + +#include "alloc.h" + +/* Add a memory buffer allocated by malloc to a memory block. This is + used by the uuconf_cmd functions so that they don't have to + constantly copy data into memory. Returns 0 on success, non 0 on + failure. */ + +int +uuconf_add_block (pblock, padd) + pointer pblock; + pointer padd; +{ + struct sblock *q = (struct sblock *) pblock; + struct sadded *qnew; + + qnew = (struct sadded *) uuconf_malloc (pblock, sizeof (struct sadded)); + if (qnew == NULL) + return 1; + + qnew->qnext = q->qadded; + qnew->padded = padd; + q->qadded = qnew; + + return 0; +} diff --git a/gnu/libexec/uucp/libuuconf/addstr.c b/gnu/libexec/uucp/libuuconf/addstr.c new file mode 100644 index 0000000000..8498d502c9 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/addstr.c @@ -0,0 +1,139 @@ +/* addstr.c + Add a string to a list of strings. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_addstr_rcsid[] = "$Id: addstr.c,v 1.1 1993/08/04 19:33:32 jtc Exp $"; +#endif + +#include + +/* When setting system information, we need to be able to distinguish + between a value that is not set and a value that has been set to + NULL. We do this by initializing the value to point to the + variable _uuconf_unset, and then correcting it in the function + _uuconf_isystem_basic_default. This variable is declared in this + file because some linkers will apparently not pull in an object + file which merely declarates a variable. This functions happens to + be pulled in by almost everything. */ + +char *_uuconf_unset; + +/* Add a string to a list of strings. The list is maintained as an + array of elements ending in NULL. The total number of available + slots is always a multiple of CSLOTS, so by counting the current + number of elements we can tell whether a new slot is needed. If + the fcopy argument is TRUE, the new string is duplicated into + memory. If the fcheck argument is TRUE, this does not add a string + that is already in the list. The pblock argument may be used to do + the allocations within a memory block. This returns a standard + uuconf error code. */ + +#define CSLOTS (8) + +int +_uuconf_iadd_string (qglobal, zadd, fcopy, fcheck, ppzstrings, pblock) + struct sglobal *qglobal; + char *zadd; + boolean fcopy; + boolean fcheck; + char ***ppzstrings; + pointer pblock; +{ + char **pz; + size_t c; + + if (fcheck && *ppzstrings != NULL) + { + for (pz = *ppzstrings; *pz != NULL; pz++) + if (strcmp (zadd, *pz) == 0) + return UUCONF_SUCCESS; + } + + if (fcopy) + { + size_t clen; + char *znew; + + clen = strlen (zadd) + 1; + znew = (char *) uuconf_malloc (pblock, clen); + if (znew == NULL) + { + if (qglobal != NULL) + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + memcpy ((pointer) znew, (pointer) zadd, clen); + zadd = znew; + } + + pz = *ppzstrings; + if (pz == NULL || pz == (char **) &_uuconf_unset) + { + pz = (char **) uuconf_malloc (pblock, CSLOTS * sizeof (char *)); + if (pz == NULL) + { + if (qglobal != NULL) + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + *ppzstrings = pz; + } + else + { + c = 0; + while (*pz != NULL) + { + ++pz; + ++c; + } + + if ((c + 1) % CSLOTS == 0) + { + char **pznew; + + pznew = (char **) uuconf_malloc (pblock, + ((c + 1 + CSLOTS) + * sizeof (char *))); + if (pznew == NULL) + { + if (qglobal != NULL) + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + memcpy ((pointer) pznew, (pointer) *ppzstrings, + c * sizeof (char *)); + uuconf_free (pblock, *ppzstrings); + *ppzstrings = pznew; + pz = pznew + c; + } + } + + pz[0] = zadd; + pz[1] = NULL; + + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/allblk.c b/gnu/libexec/uucp/libuuconf/allblk.c new file mode 100644 index 0000000000..b3dd7e0a76 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/allblk.c @@ -0,0 +1,51 @@ +/* allblk.c + Allocate a memory block. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_allblk_rcsid[] = "$Id: allblk.c,v 1.1 1993/08/04 19:33:33 jtc Exp $"; +#endif + +#include "alloc.h" + +/* Allocate a new memory block. If this fails, uuconf_errno will be + set, and the calling routine may return UUCONF_MALLOC_FAILED | + UUCONF_ERROR_ERRNO. */ + +pointer +uuconf_malloc_block () +{ + struct sblock *qret; + + qret = (struct sblock *) malloc (sizeof (struct sblock)); + if (qret == NULL) + return NULL; + qret->qnext = NULL; + qret->ifree = 0; + qret->plast = NULL; + qret->qadded = NULL; + return (pointer) qret; +} diff --git a/gnu/libexec/uucp/libuuconf/alloc.c b/gnu/libexec/uucp/libuuconf/alloc.c new file mode 100644 index 0000000000..2808c62088 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/alloc.c @@ -0,0 +1,82 @@ +/* alloc.c + Allocate within a memory block. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_alloc_rcsid[] = "$Id: alloc.c,v 1.1 1993/08/04 19:33:34 jtc Exp $"; +#endif + +#include "alloc.h" + +/* Allocate some memory out of a memory block. If the memory block is + NULL, this just calls malloc; this is convenient for a number of + routines. If this fails, uuconf_errno will be set, and the calling + routine may return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO. */ + +pointer +uuconf_malloc (pblock, c) + pointer pblock; + size_t c; +{ + struct sblock *q = (struct sblock *) pblock; + pointer pret; + + if (c == 0) + return NULL; + + if (q == NULL) + return malloc (c); + + /* Make sure that c is aligned to a double boundary. */ + c = ((c + sizeof (double) - 1) / sizeof (double)) * sizeof (double); + + while (q->ifree + c > CALLOC_SIZE) + { + if (q->qnext != NULL) + q = q->qnext; + else + { + if (c > CALLOC_SIZE) + q->qnext = (struct sblock *) malloc (sizeof (struct sblock) + + c - CALLOC_SIZE); + else + q->qnext = (struct sblock *) malloc (sizeof (struct sblock)); + if (q->qnext == NULL) + return NULL; + q = q->qnext; + q->qnext = NULL; + q->ifree = 0; + q->qadded = NULL; + break; + } + } + + pret = q->u.ab + q->ifree; + q->ifree += c; + q->plast = pret; + + return pret; +} diff --git a/gnu/libexec/uucp/libuuconf/alloc.h b/gnu/libexec/uucp/libuuconf/alloc.h new file mode 100644 index 0000000000..c5c9cad8e3 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/alloc.h @@ -0,0 +1,71 @@ +/* alloc.h + Header file for uuconf memory allocation routines. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +/* This header file is private to the uuconf memory allocation + routines, and should not be included by any other files. */ + +/* We want to be able to keep track of allocated memory blocks, so + that we can free them up later. This will let us free up all the + memory allocated to hold information for a system, for example. We + do this by allocating large chunks and doling them out. Calling + uuconf_malloc_block will return a pointer to a magic cookie which + can then be passed to uuconf_malloc and uuconf_free. Passing the + pointer to uuconf_free_block will free all memory allocated for + that block. */ + +/* We allocate this much space in each block. On most systems, this + will make the actual structure 1024 bytes, which may be convenient + for some types of memory allocators. */ +#define CALLOC_SIZE (1008) + +/* This is the actual structure of a block. */ +struct sblock +{ + /* Next block in linked list. */ + struct sblock *qnext; + /* Index of next free spot. */ + size_t ifree; + /* Last value returned by uuconf_malloc for this block. */ + pointer plast; + /* List of additional memory blocks. */ + struct sadded *qadded; + /* Buffer of data. We put it in a union with a double to make sure + it is adequately aligned. */ + union + { + char ab[CALLOC_SIZE]; + double l; + } u; +}; + +/* There is a linked list of additional memory blocks inserted by + uuconf_add_block. */ +struct sadded +{ + /* The next in the list. */ + struct sadded *qnext; + /* The added block. */ + pointer padded; +}; diff --git a/gnu/libexec/uucp/libuuconf/base.c b/gnu/libexec/uucp/libuuconf/base.c new file mode 100644 index 0000000000..c40f660523 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/base.c @@ -0,0 +1,54 @@ +/* base.c + Subroutine to turn a cmdtab_offset table into a uuconf_cmdtab table. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_base_rcsid[] = "$Id: base.c,v 1.1 1993/08/04 19:33:38 jtc Exp $"; +#endif + +/* This turns a cmdtab_offset table into a uuconf_cmdtab table. Each + offset is adjusted by a base value. */ + +void +_uuconf_ucmdtab_base (qoff, celes, pbase, qset) + register const struct cmdtab_offset *qoff; + size_t celes; + char *pbase; + register struct uuconf_cmdtab *qset; +{ + register size_t i; + + for (i = 0; i < celes; i++, qoff++, qset++) + { + qset->uuconf_zcmd = qoff->zcmd; + qset->uuconf_itype = qoff->itype; + if (qoff->ioff == (size_t) -1) + qset->uuconf_pvar = NULL; + else + qset->uuconf_pvar = pbase + qoff->ioff; + qset->uuconf_pifn = qoff->pifn; + } +} diff --git a/gnu/libexec/uucp/libuuconf/bool.c b/gnu/libexec/uucp/libuuconf/bool.c new file mode 100644 index 0000000000..c1bbde1326 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/bool.c @@ -0,0 +1,64 @@ +/* bool.c + Parse a boolean string into a variable. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_bool_rcsid[] = "$Id: bool.c,v 1.1 1993/08/04 19:33:40 jtc Exp $"; +#endif + +/* Parse a boolean string into a variable. This is called by + uuconf_cmd_args, as well as other functions. The parsing is done + in a single place to make it easy to change. This should return an + error code, including both UUCONF_CMDTABRET_KEEP and + UUCONF_CMDTABRET_EXIT if appropriate. */ + +/*ARGSIGNORED*/ +int +_uuconf_iboolean (qglobal, zval, pi) + struct sglobal *qglobal; + const char *zval; + boolean *pi; +{ + switch (*zval) + { + case 'y': + case 'Y': + case 't': + case 'T': + *pi = TRUE; + break; + case 'n': + case 'N': + case 'f': + case 'F': + *pi = FALSE; + break; + default: + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; + } + + return UUCONF_CMDTABRET_CONTINUE; +} diff --git a/gnu/libexec/uucp/libuuconf/callin.c b/gnu/libexec/uucp/libuuconf/callin.c new file mode 100644 index 0000000000..4df26ddfdd --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/callin.c @@ -0,0 +1,142 @@ +/* callin.c + Check a login name and password against the UUCP password file. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_callin_rcsid[] = "$Id: callin.c,v 1.1 1993/08/04 19:33:42 jtc Exp $"; +#endif + +#include + +static int iplogin P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); + +/* Check a login name and password against the UUCP password file. + This looks at the Taylor UUCP password file, but will work even if + uuconf_taylor_init was not called. */ + +int +uuconf_callin (pglobal, zlogin, zpassword) + pointer pglobal; + const char *zlogin; + const char *zpassword; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + int iret; + char **pz; + struct uuconf_cmdtab as[2]; + char *zfilepass; + + /* If we have no password file names, fill in the default name. */ + if (qglobal->qprocess->pzpwdfiles == NULL) + { + char ab[sizeof NEWCONFIGLIB + sizeof PASSWDFILE - 1]; + + memcpy ((pointer) ab, (pointer) NEWCONFIGLIB, + sizeof NEWCONFIGLIB - 1); + memcpy ((pointer) (ab + sizeof NEWCONFIGLIB - 1), (pointer) PASSWDFILE, + sizeof PASSWDFILE); + iret = _uuconf_iadd_string (qglobal, ab, TRUE, FALSE, + &qglobal->qprocess->pzpwdfiles, + qglobal->pblock); + if (iret != UUCONF_SUCCESS) + return iret; + } + + as[0].uuconf_zcmd = zlogin; + as[0].uuconf_itype = UUCONF_CMDTABTYPE_FN | 2; + as[0].uuconf_pvar = (pointer) &zfilepass; + as[0].uuconf_pifn = iplogin; + + as[1].uuconf_zcmd = NULL; + + zfilepass = NULL; + + iret = UUCONF_SUCCESS; + + for (pz = qglobal->qprocess->pzpwdfiles; *pz != NULL; pz++) + { + FILE *e; + + e = fopen (*pz, "r"); + if (e == NULL) + { + if (FNO_SUCH_FILE ()) + continue; + qglobal->ierrno = errno; + iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + iret = uuconf_cmd_file (pglobal, e, as, (pointer) NULL, + (uuconf_cmdtabfn) NULL, + UUCONF_CMDTABFLAG_CASE, (pointer) NULL); + (void) fclose (e); + + if (iret != UUCONF_SUCCESS || zfilepass != NULL) + break; + } + + if (iret != UUCONF_SUCCESS) + { + qglobal->zfilename = *pz; + iret |= UUCONF_ERROR_FILENAME; + } + else if (zfilepass == NULL + || strcmp (zfilepass, zpassword) != 0) + iret = UUCONF_NOT_FOUND; + + if (zfilepass != NULL) + free ((pointer) zfilepass); + + return iret; +} + +/* This is called if it is the name we are looking for. The pvar + argument points to zfilepass, and we set it to the password. */ + +static int +iplogin (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + char **pzpass = (char **) pvar; + + *pzpass = strdup (argv[1]); + if (*pzpass == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + + return UUCONF_CMDTABRET_EXIT; +} diff --git a/gnu/libexec/uucp/libuuconf/calout.c b/gnu/libexec/uucp/libuuconf/calout.c new file mode 100644 index 0000000000..957833ebc3 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/calout.c @@ -0,0 +1,93 @@ +/* calout.c + Find callout login name and password for a system. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_calout_rcsid[] = "$Id: calout.c,v 1.1 1993/08/04 19:33:45 jtc Exp $"; +#endif + +#include + +/* Find callout login name and password for a system. */ + +/*ARGSUSED*/ +int +uuconf_callout (pglobal, qsys, pzlog, pzpass) + pointer pglobal; + const struct uuconf_system *qsys; + char **pzlog; + char **pzpass; +{ +#if HAVE_TAYLOR_CONFIG + + return uuconf_taylor_callout (pglobal, qsys, pzlog, pzpass); + +#else /* ! HAVE_TAYLOR_CONFIG */ + + struct sglobal *qglobal = (struct sglobal *) pglobal; + + *pzlog = NULL; + *pzpass = NULL; + + if (qsys->uuconf_zcall_login == NULL + && qsys->uuconf_zcall_password == NULL) + return UUCONF_NOT_FOUND; + + if ((qsys->uuconf_zcall_login != NULL + && strcmp (qsys->uuconf_zcall_login, "*") == 0) + || (qsys->uuconf_zcall_password != NULL + && strcmp (qsys->uuconf_zcall_password, "*") == 0)) + return UUCONF_NOT_FOUND; + + if (qsys->uuconf_zcall_login != NULL) + { + *pzlog = strdup (qsys->uuconf_zcall_login); + if (*pzlog == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + } + + if (qsys->uuconf_zcall_password != NULL) + { + *pzpass = strdup (qsys->uuconf_zcall_password); + if (*pzpass == NULL) + { + qglobal->ierrno = errno; + if (*pzlog != NULL) + { + free ((pointer) *pzlog); + *pzlog = NULL; + } + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + } + + return UUCONF_SUCCESS; + +#endif /* ! HAVE_TAYLOR_CONFIG */ +} diff --git a/gnu/libexec/uucp/libuuconf/chatc.c b/gnu/libexec/uucp/libuuconf/chatc.c new file mode 100644 index 0000000000..f29481b59a --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/chatc.c @@ -0,0 +1,202 @@ +/* chatc.c + Subroutines to handle chat script commands. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_chatc_rcsid[] = "$Id: chatc.c,v 1.1 1993/08/04 19:33:48 jtc Exp $"; +#endif + +#include +#include + +static int icchat P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); +static int icchat_fail P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); +static int icunknown P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); + +/* The chat script commands. */ + +static const struct cmdtab_offset asChat_cmds[] = +{ + { "chat", UUCONF_CMDTABTYPE_FN, + offsetof (struct uuconf_chat, uuconf_pzchat), icchat }, + { "chat-program", UUCONF_CMDTABTYPE_FULLSTRING, + offsetof (struct uuconf_chat, uuconf_pzprogram), NULL }, + { "chat-timeout", UUCONF_CMDTABTYPE_INT, + offsetof (struct uuconf_chat, uuconf_ctimeout), NULL }, + { "chat-fail", UUCONF_CMDTABTYPE_FN | 2, + offsetof (struct uuconf_chat, uuconf_pzfail), icchat_fail }, + { "chat-seven-bit", UUCONF_CMDTABTYPE_BOOLEAN, + offsetof (struct uuconf_chat, uuconf_fstrip), NULL }, + { NULL, 0, 0, NULL } +}; + +#define CCHAT_CMDS (sizeof asChat_cmds / sizeof asChat_cmds[0]) + +/* Handle a chat script command. The chat script commands are entered + as UUCONF_CMDTABTYPE_PREFIX, and the commands are routed to this + function. We copy the command table onto the stack and repoint it + at qchat in order to make the function reentrant. The return value + can include UUCONF_CMDTABRET_KEEP, but should not include + UUCONF_CMDTABRET_EXIT. */ + +int +_uuconf_ichat_cmd (qglobal, argc, argv, qchat, pblock) + struct sglobal *qglobal; + int argc; + char **argv; + struct uuconf_chat *qchat; + pointer pblock; +{ + char *zchat; + struct uuconf_cmdtab as[CCHAT_CMDS]; + int iret; + + /* This is only invoked when argv[0] will contain the string "chat"; + the specific chat script command comes after that point. */ + for (zchat = argv[0]; *zchat != '\0'; zchat++) + if ((*zchat == 'c' || *zchat == 'C') + && strncasecmp (zchat, "chat", sizeof "chat" - 1) == 0) + break; + if (*zchat == '\0') + return UUCONF_SYNTAX_ERROR; + argv[0] = zchat; + + _uuconf_ucmdtab_base (asChat_cmds, CCHAT_CMDS, (char *) qchat, as); + + iret = uuconf_cmd_args ((pointer) qglobal, argc, argv, as, pblock, + icunknown, 0, pblock); + return iret &~ UUCONF_CMDTABRET_EXIT; +} + +/* Handle the "chat" command. This breaks up substrings in expect + strings, and sticks the arguments into a NULL terminated array. */ + +static int +icchat (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + char ***ppz = (char ***) pvar; + pointer pblock = pinfo; + int i; + + *ppz = NULL; + + for (i = 1; i < argc; i += 2) + { + char *z, *zdash; + int iret; + + /* Break the expect string into substrings. */ + z = argv[i]; + zdash = strchr (z, '-'); + while (zdash != NULL) + { + *zdash = '\0'; + iret = _uuconf_iadd_string (qglobal, z, TRUE, FALSE, ppz, + pblock); + if (iret != UUCONF_SUCCESS) + return iret; + *zdash = '-'; + z = zdash; + zdash = strchr (z + 1, '-'); + } + + iret = _uuconf_iadd_string (qglobal, z, FALSE, FALSE, ppz, pblock); + if (iret != UUCONF_SUCCESS) + return iret; + + /* Add the send string without breaking it up. If it starts + with a dash we must replace it with an escape sequence, to + prevent it from being interpreted as a subsend. */ + + if (i + 1 < argc) + { + if (argv[i + 1][0] != '-') + iret = _uuconf_iadd_string (qglobal, argv[i + 1], FALSE, + FALSE, ppz, pblock); + else + { + size_t clen; + + clen = strlen (argv[i + 1]); + z = uuconf_malloc (pblock, clen + 2); + if (z == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + z[0] = '\\'; + memcpy ((pointer) (z + 1), (pointer) argv[i + 1], clen + 1); + iret = _uuconf_iadd_string (qglobal, z, FALSE, FALSE, ppz, + pblock); + } + if (iret != UUCONF_SUCCESS) + return iret; + } + } + + return UUCONF_CMDTABRET_KEEP; +} + +/* Add a new chat failure string. */ + +/*ARGSUSED*/ +static int +icchat_fail (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + char ***ppz = (char ***) pvar; + pointer pblock = pinfo; + + return _uuconf_iadd_string (qglobal, argv[1], TRUE, FALSE, ppz, pblock); +} + +/* Return a syntax error for an unknown command. */ + +/*ARGSUSED*/ +static int +icunknown (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + return UUCONF_SYNTAX_ERROR; +} diff --git a/gnu/libexec/uucp/libuuconf/cmdarg.c b/gnu/libexec/uucp/libuuconf/cmdarg.c new file mode 100644 index 0000000000..2020ea91a9 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/cmdarg.c @@ -0,0 +1,185 @@ +/* cmdarg.c + Look up a command with arguments in a command table. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_cmdarg_rcsid[] = "$Id: cmdarg.c,v 1.1 1993/08/04 19:33:50 jtc Exp $"; +#endif + +#include + +#undef strcmp +#if HAVE_STRCASECMP +#undef strcasecmp +#endif +extern int strcmp (), strcasecmp (); + +/* Look up a command with arguments in a table and execute it. */ + +int +uuconf_cmd_args (pglobal, cargs, pzargs, qtab, pinfo, pfiunknown, iflags, + pblock) + pointer pglobal; + int cargs; + char **pzargs; + const struct uuconf_cmdtab *qtab; + pointer pinfo; + int (*pfiunknown) P((pointer, int, char **, pointer, pointer)); + int iflags; + pointer pblock; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + int bfirstu, bfirstl; + int (*pficmp) P((const char *, const char *)); + register const struct uuconf_cmdtab *q; + int itype; + int callowed; + + bfirstu = bfirstl = pzargs[0][0]; + if ((iflags & UUCONF_CMDTABFLAG_CASE) != 0) + pficmp = strcmp; + else + { + if (islower (bfirstu)) + bfirstu = toupper (bfirstu); + if (isupper (bfirstl)) + bfirstl = tolower (bfirstl); + pficmp = strcasecmp; + } + + itype = 0; + + for (q = qtab; q->uuconf_zcmd != NULL; q++) + { + int bfirst; + + bfirst = q->uuconf_zcmd[0]; + if (bfirst != bfirstu && bfirst != bfirstl) + continue; + + itype = UUCONF_TTYPE_CMDTABTYPE (q->uuconf_itype); + if (itype != UUCONF_CMDTABTYPE_PREFIX) + { + if ((*pficmp) (q->uuconf_zcmd, pzargs[0]) == 0) + break; + } + else + { + size_t clen; + + clen = strlen (q->uuconf_zcmd); + if ((iflags & UUCONF_CMDTABFLAG_CASE) != 0) + { + if (strncmp (q->uuconf_zcmd, pzargs[0], clen) == 0) + break; + } + else + { + if (strncasecmp (q->uuconf_zcmd, pzargs[0], clen) == 0) + break; + } + } + } + + if (q->uuconf_zcmd == NULL) + { + if (pfiunknown == NULL) + return UUCONF_CMDTABRET_CONTINUE; + return (*pfiunknown) (pglobal, cargs, pzargs, (pointer) NULL, pinfo); + } + + callowed = UUCONF_CARGS_CMDTABTYPE (q->uuconf_itype); + if (callowed != 0 && callowed != cargs) + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; + + switch (itype) + { + case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_STRING): + if (cargs == 1) + *(char **) q->uuconf_pvar = (char *) ""; + else if (cargs == 2) + *(char **) q->uuconf_pvar = pzargs[1]; + else + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; + + return UUCONF_CMDTABRET_KEEP; + + case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_INT): + return _uuconf_iint (qglobal, pzargs[1], q->uuconf_pvar, TRUE); + + case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_LONG): + return _uuconf_iint (qglobal, pzargs[1], q->uuconf_pvar, FALSE); + + case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_BOOLEAN): + return _uuconf_iboolean (qglobal, pzargs[1], (int *) q->uuconf_pvar); + + case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_FULLSTRING): + if (cargs == 1) + { + char ***ppz = (char ***) q->uuconf_pvar; + int iret; + + *ppz = NULL; + iret = _uuconf_iadd_string (qglobal, (char *) NULL, FALSE, FALSE, + ppz, pblock); + if (iret != UUCONF_SUCCESS) + return iret | UUCONF_CMDTABRET_EXIT; + + return UUCONF_CMDTABRET_CONTINUE; + } + else + { + char ***ppz = (char ***) q->uuconf_pvar; + int i; + + *ppz = NULL; + for (i = 1; i < cargs; i++) + { + int iret; + + iret = _uuconf_iadd_string (qglobal, pzargs[i], FALSE, FALSE, + ppz, pblock); + if (iret != UUCONF_SUCCESS) + { + *ppz = NULL; + return iret | UUCONF_CMDTABRET_EXIT; + } + } + + return UUCONF_CMDTABRET_KEEP; + } + + case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_FN): + case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_PREFIX): + return (*q->uuconf_pifn) (pglobal, cargs, pzargs, q->uuconf_pvar, + pinfo); + + default: + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; + } + + /*NOTREACHED*/ +} diff --git a/gnu/libexec/uucp/libuuconf/cmdfil.c b/gnu/libexec/uucp/libuuconf/cmdfil.c new file mode 100644 index 0000000000..333a4bf376 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/cmdfil.c @@ -0,0 +1,103 @@ +/* cmdfil.c + Read and parse commands from a file. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_cmdfil_rcsid[] = "$Id: cmdfil.c,v 1.1 1993/08/04 19:33:51 jtc Exp $"; +#endif + +#include + +/* Read and parse commands from a file, updating uuconf_lineno as + appropriate. */ + +int +uuconf_cmd_file (pglobal, e, qtab, pinfo, pfiunknown, iflags, pblock) + pointer pglobal; + FILE *e; + const struct uuconf_cmdtab *qtab; + pointer pinfo; + int (*pfiunknown) P((pointer, int, char **, pointer, pointer)); + int iflags; + pointer pblock; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + boolean fcont; + char *zline; + size_t cline; + int iret; + + fcont = (iflags & UUCONF_CMDTABFLAG_BACKSLASH) != 0; + + zline = NULL; + cline = 0; + + iret = UUCONF_SUCCESS; + + qglobal->ilineno = 0; + + while ((fcont + ? _uuconf_getline (qglobal, &zline, &cline, e) + : getline (&zline, &cline, e)) > 0) + { + ++qglobal->ilineno; + + iret = uuconf_cmd_line (pglobal, zline, qtab, pinfo, pfiunknown, + iflags, pblock); + + if ((iret & UUCONF_CMDTABRET_KEEP) != 0) + { + iret &=~ UUCONF_CMDTABRET_KEEP; + + if (pblock != NULL) + { + if (uuconf_add_block (pblock, zline) != 0) + { + qglobal->ierrno = errno; + iret = (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_ERROR_LINENO); + break; + } + } + + zline = NULL; + cline = 0; + } + + if ((iret & UUCONF_CMDTABRET_EXIT) != 0) + { + iret &=~ UUCONF_CMDTABRET_EXIT; + if (iret != UUCONF_SUCCESS) + iret |= UUCONF_ERROR_LINENO; + break; + } + + iret = UUCONF_SUCCESS; + } + + return iret; +} diff --git a/gnu/libexec/uucp/libuuconf/cmdlin.c b/gnu/libexec/uucp/libuuconf/cmdlin.c new file mode 100644 index 0000000000..63e9564673 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/cmdlin.c @@ -0,0 +1,142 @@ +/* cmdlin.c + Parse a command line. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_cmdlin_rcsid[] = "$Id: cmdlin.c,v 1.1 1993/08/04 19:33:52 jtc Exp $"; +#endif + +#include +#include + +/* Parse a command line into fields and process it via a command + table. The command table functions may keep the memory allocated + for the line, but they may not keep the memory allocated for the + argv list. This function strips # comments. */ + +#define CSTACK (16) + +int +uuconf_cmd_line (pglobal, zline, qtab, pinfo, pfiunknown, iflags, pblock) + pointer pglobal; + char *zline; + const struct uuconf_cmdtab *qtab; + pointer pinfo; + int (*pfiunknown) P((pointer, int, char **, pointer, pointer)); + int iflags; + pointer pblock; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + char *z; + int cargs; + char *azargs[CSTACK]; + char **pzargs; + int iret; + + /* Any # not preceeded by a backslash starts a comment. */ + z = zline; + while ((z = strchr (z, '#')) != NULL) + { + if (z == zline || *(z - 1) != '\\') + { + *z = '\0'; + break; + } + /* Remove the backslash. */ + while ((*(z - 1) = *z) != '\0') + ++z; + } + + /* Parse the first CSTACK arguments by hand to avoid malloc. */ + + z = zline; + cargs = 0; + pzargs = azargs; + while (TRUE) + { + while (*z != '\0' && isspace (BUCHAR (*z))) + ++z; + + if (*z == '\0') + break; + + if (cargs >= CSTACK) + { + char **pzsplit; + size_t csplit; + int cmore; + + pzsplit = NULL; + csplit = 0; + cmore = _uuconf_istrsplit (z, '\0', &pzsplit, &csplit); + if (cmore < 0) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + + pzargs = (char **) malloc ((cmore + CSTACK) * sizeof (char *)); + if (pzargs == NULL) + { + qglobal->ierrno = errno; + free ((pointer) pzsplit); + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + + memcpy ((pointer) pzargs, (pointer) azargs, + CSTACK * sizeof (char *)); + memcpy ((pointer) (pzargs + CSTACK), (pointer) pzsplit, + cmore * sizeof (char *)); + cargs = cmore + CSTACK; + + free ((pointer) pzsplit); + + break; + } + + azargs[cargs] = z; + ++cargs; + + while (*z != '\0' && ! isspace (BUCHAR (*z))) + z++; + + if (*z == '\0') + break; + + *z++ = '\0'; + } + + if (cargs <= 0) + return UUCONF_CMDTABRET_CONTINUE; + + iret = uuconf_cmd_args (pglobal, cargs, pzargs, qtab, pinfo, pfiunknown, + iflags, pblock); + + if (pzargs != azargs) + free ((pointer) pzargs); + + return iret; +} diff --git a/gnu/libexec/uucp/libuuconf/debfil.c b/gnu/libexec/uucp/libuuconf/debfil.c new file mode 100644 index 0000000000..3be5381e5d --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/debfil.c @@ -0,0 +1,43 @@ +/* debfil.c + Get the name of the UUCP debugging file. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_debfil_rcsid[] = "$Id: debfil.c,v 1.1 1993/08/04 19:33:53 jtc Exp $"; +#endif + +/* Get the name of the UUCP debugging file. */ + +int +uuconf_debugfile (pglobal, pzdebug) + pointer pglobal; + const char **pzdebug; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + + *pzdebug = qglobal->qprocess->zdebugfile; + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/deblev.c b/gnu/libexec/uucp/libuuconf/deblev.c new file mode 100644 index 0000000000..40cf763b10 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/deblev.c @@ -0,0 +1,43 @@ +/* deblev.c + Get the UUCP debugging level. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_deblev_rcsid[] = "$Id: deblev.c,v 1.1 1993/08/04 19:33:54 jtc Exp $"; +#endif + +/* Get the UUCP debugging level. */ + +int +uuconf_debuglevel (pglobal, pzdebug) + pointer pglobal; + const char **pzdebug; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + + *pzdebug = qglobal->qprocess->zdebug; + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/diacod.c b/gnu/libexec/uucp/libuuconf/diacod.c new file mode 100644 index 0000000000..823239d953 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/diacod.c @@ -0,0 +1,129 @@ +/* diacod.c + Translate a dialcode. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_diacod_rcsid[] = "$Id: diacod.c,v 1.1 1993/08/04 19:33:55 jtc Exp $"; +#endif + +#include + +static int idcode P((pointer pglobal, int argc, char **argv, + pointer pinfo, pointer pvar)); + +/* Get the name of the UUCP log file. */ + +int +uuconf_dialcode (pglobal, zdial, pznum) + pointer pglobal; + const char *zdial; + char **pznum; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct uuconf_cmdtab as[2]; + char **pz; + int iret; + + as[0].uuconf_zcmd = zdial; + as[0].uuconf_itype = UUCONF_CMDTABTYPE_FN | 0; + as[0].uuconf_pvar = (pointer) pznum; + as[0].uuconf_pifn = idcode; + + as[1].uuconf_zcmd = NULL; + + *pznum = NULL; + + iret = UUCONF_SUCCESS; + + for (pz = qglobal->qprocess->pzdialcodefiles; *pz != NULL; pz++) + { + FILE *e; + + e = fopen (*pz, "r"); + if (e == NULL) + { + if (FNO_SUCH_FILE ()) + continue; + qglobal->ierrno = errno; + iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + iret = uuconf_cmd_file (pglobal, e, as, (pointer) NULL, + (uuconf_cmdtabfn) NULL, 0, (pointer) NULL); + (void) fclose (e); + + if (iret != UUCONF_SUCCESS || *pznum != NULL) + break; + } + + if (iret != UUCONF_SUCCESS) + { + qglobal->zfilename = *pz; + iret |= UUCONF_ERROR_FILENAME; + } + else if (*pznum == NULL) + iret = UUCONF_NOT_FOUND; + + return iret; +} + +/* This is called if the dialcode is found. It copies the number into + the heap and gets out of reading the file. */ + +/*ARGSUSED*/ +static int +idcode (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + char **pznum = (char **) pvar; + + if (argc == 1) + { + *pznum = malloc (1); + if (*pznum != NULL) + **pznum = '\0'; + } + else if (argc == 2) + *pznum = strdup (argv[1]); + else + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; + + if (*pznum == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + + return UUCONF_CMDTABRET_EXIT; +} diff --git a/gnu/libexec/uucp/libuuconf/dial.c b/gnu/libexec/uucp/libuuconf/dial.c new file mode 100644 index 0000000000..efdef538c3 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/dial.c @@ -0,0 +1,61 @@ +/* dial.c + Find a dialer. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_dial_rcsid[] = "$Id: dial.c,v 1.1 1993/08/04 19:33:57 jtc Exp $"; +#endif + +/* Find a dialer by name. */ + +int +uuconf_dialer_info (pglobal, zdialer, qdialer) + pointer pglobal; + const char *zdialer; + struct uuconf_dialer *qdialer; +{ +#if HAVE_HDB_CONFIG + struct sglobal *qglobal = (struct sglobal *) pglobal; +#endif + int iret; + +#if HAVE_TAYLOR_CONFIG + iret = uuconf_taylor_dialer_info (pglobal, zdialer, qdialer); + if (iret != UUCONF_NOT_FOUND) + return iret; +#endif + +#if HAVE_HDB_CONFIG + if (qglobal->qprocess->fhdb) + { + iret = uuconf_hdb_dialer_info (pglobal, zdialer, qdialer); + if (iret != UUCONF_NOT_FOUND) + return iret; + } +#endif + + return UUCONF_NOT_FOUND; +} diff --git a/gnu/libexec/uucp/libuuconf/diasub.c b/gnu/libexec/uucp/libuuconf/diasub.c new file mode 100644 index 0000000000..7979d0ebd2 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/diasub.c @@ -0,0 +1,63 @@ +/* diasub.c + Dialer information subroutines. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_diasub_rcsid[] = "$Id: diasub.c,v 1.1 1993/08/04 19:33:58 jtc Exp $"; +#endif + +/* Clear the information in a dialer. */ + +#define INIT_CHAT(q) \ + ((q)->uuconf_pzchat = NULL, \ + (q)->uuconf_pzprogram = NULL, \ + (q)->uuconf_ctimeout = 60, \ + (q)->uuconf_pzfail = NULL, \ + (q)->uuconf_fstrip = TRUE) + +void +_uuconf_uclear_dialer (qdialer) + struct uuconf_dialer *qdialer; +{ + qdialer->uuconf_zname = NULL; + INIT_CHAT (&qdialer->uuconf_schat); + qdialer->uuconf_zdialtone = (char *) ","; + qdialer->uuconf_zpause = (char *) ","; + qdialer->uuconf_fcarrier = TRUE; + qdialer->uuconf_ccarrier_wait = 60; + qdialer->uuconf_fdtr_toggle = FALSE; + qdialer->uuconf_fdtr_toggle_wait = FALSE; + INIT_CHAT (&qdialer->uuconf_scomplete); + INIT_CHAT (&qdialer->uuconf_sabort); + qdialer->uuconf_qproto_params = NULL; + /* Note that we do not set RELIABLE_SPECIFIED; this just sets + defaults, so that ``seven-bit true'' does not imply ``reliable + false''. */ + qdialer->uuconf_ireliable = (UUCONF_RELIABLE_RELIABLE + | UUCONF_RELIABLE_EIGHT + | UUCONF_RELIABLE_FULLDUPLEX); + qdialer->uuconf_palloc = NULL; +} diff --git a/gnu/libexec/uucp/libuuconf/dnams.c b/gnu/libexec/uucp/libuuconf/dnams.c new file mode 100644 index 0000000000..6a10ada811 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/dnams.c @@ -0,0 +1,103 @@ +/* dnams.c + Get all known dialer names. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_dnams_rcsid[] = "$Id: dnams.c,v 1.1 1993/08/04 19:33:59 jtc Exp $"; +#endif + +/* Get all known dialer names. */ + +int +uuconf_dialer_names (pglobal, ppzdialers) + pointer pglobal; + char ***ppzdialers; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + char **pztaylor; + char **pzhdb; + int iret; + + *ppzdialers = NULL; + pztaylor = NULL; + pzhdb = NULL; + +#if HAVE_TAYLOR_CONFIG + iret = uuconf_taylor_dialer_names (pglobal, &pztaylor); + if (iret != UUCONF_SUCCESS) + return iret; +#endif + +#if HAVE_HDB_CONFIG + if (qglobal->qprocess->fhdb) + { + iret = uuconf_hdb_dialer_names (pglobal, &pzhdb); + if (iret != UUCONF_SUCCESS) + return iret; + } +#endif + + if (pzhdb == NULL) + *ppzdialers = pztaylor; + else if (pztaylor == NULL) + *ppzdialers = pzhdb; + else + { + char **pz; + + iret = UUCONF_SUCCESS; + + for (pz = pztaylor; *pz != NULL; pz++) + { + iret = _uuconf_iadd_string (qglobal, *pz, FALSE, TRUE, + ppzdialers, (pointer) NULL); + if (iret != UUCONF_SUCCESS) + break; + } + + if (iret == UUCONF_SUCCESS) + { + for (pz = pzhdb; *pz != NULL; pz++) + { + iret = _uuconf_iadd_string (qglobal, *pz, FALSE, TRUE, + ppzdialers, (pointer) NULL); + if (iret != UUCONF_SUCCESS) + break; + } + } + + if (pztaylor != NULL) + free ((pointer) pztaylor); + if (pzhdb != NULL) + free ((pointer) pzhdb); + } + + if (iret == UUCONF_SUCCESS && *ppzdialers == NULL) + iret = _uuconf_iadd_string (qglobal, (char *) NULL, FALSE, FALSE, + ppzdialers, (pointer) NULL); + + return iret; +} diff --git a/gnu/libexec/uucp/libuuconf/errno.c b/gnu/libexec/uucp/libuuconf/errno.c new file mode 100644 index 0000000000..d1def82ca3 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/errno.c @@ -0,0 +1,46 @@ +/* errno.c + Return the saved errno value. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_errno_rcsid[] = "$Id: errno.c,v 1.1 1993/08/04 19:34:01 jtc Exp $"; +#endif + +#include + +/* Return the saved errno value. */ + +int +uuconf_error_errno (pglobal) + pointer pglobal; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + + if (qglobal == NULL) + return errno; + else + return qglobal->ierrno; +} diff --git a/gnu/libexec/uucp/libuuconf/errstr.c b/gnu/libexec/uucp/libuuconf/errstr.c new file mode 100644 index 0000000000..2bed403f25 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/errstr.c @@ -0,0 +1,241 @@ +/* errstr.c + Return a string for a uuconf error. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_errstr_rcsid[] = "$Id: errstr.c,v 1.1 1993/08/04 19:34:02 jtc Exp $"; +#endif + +static char *zeprint_num P((char *zbuf, size_t cbuf, int ival)); + +/* Return an error string for a uuconf error. This does not return a + uuconf error code, but instead returns the total buffer length. */ + +int +uuconf_error_string (pglobal, ierr, zbuf, cbuf) + pointer pglobal; + int ierr; + char *zbuf; + size_t cbuf; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + const char *zfile; + size_t cfile; + const char *zlineno; + char ablineno[100]; + size_t clineno; + const char *zmsg; + char abmsg[100]; + size_t cmsg; + const char *zerrno; + size_t cerrno; + size_t cret; + size_t ccopy; + + /* The format of the message is + + filename:lineno: message: errno + + If there is no filename, the trailing colon is not output. If + there is no linenumber, the trailing colon is not output. If + there is no filename, the linenumber is not output, and neither + is the space before message. If there is no errno, the + preceeding colon and space are not output. */ + + /* Get the filename to put in the error message, if any. */ + if ((ierr & UUCONF_ERROR_FILENAME) == 0 + || qglobal == NULL + || qglobal->zfilename == NULL) + { + zfile = ""; + cfile = 0; + } + else + { + zfile = qglobal->zfilename; + cfile = strlen (zfile) + 1; + } + + /* Get the line number to put in the error message, if any. */ + if (cfile == 0 + || (ierr & UUCONF_ERROR_LINENO) == 0 + || qglobal == NULL + || qglobal->ilineno <= 0) + { + zlineno = ""; + clineno = 0; + } + else + { + zlineno = zeprint_num (ablineno, sizeof ablineno, qglobal->ilineno); + clineno = strlen (zlineno) + 1; + } + + /* Get the main message. */ + switch (UUCONF_ERROR_VALUE (ierr)) + { + case UUCONF_SUCCESS: + zmsg = "no error"; + break; + case UUCONF_NOT_FOUND: + zmsg = "not found"; + break; + case UUCONF_FOPEN_FAILED: + zmsg = "fopen"; + break; + case UUCONF_FSEEK_FAILED: + zmsg = "fseek"; + break; + case UUCONF_MALLOC_FAILED: + zmsg = "malloc"; + break; + case UUCONF_SYNTAX_ERROR: + zmsg = "syntax error"; + break; + default: + zmsg = zeprint_num (abmsg, sizeof abmsg, UUCONF_ERROR_VALUE (ierr)); + zmsg -= sizeof "error " - 1; + memcpy ((pointer) zmsg, (pointer) "error ", sizeof "error " - 1); + break; + } + + cmsg = strlen (zmsg); + if (cfile > 0) + ++cmsg; + + /* Get the errno string. Note that strerror is not necessarily + reentrant. */ + if ((ierr & UUCONF_ERROR_ERRNO) == 0 + || qglobal == NULL) + { + zerrno = ""; + cerrno = 0; + } + else + { + zerrno = strerror (qglobal->ierrno); + cerrno = strlen (zerrno) + 2; + } + + cret = cfile + clineno + cmsg + cerrno + 1; + + if (cbuf == 0) + return cret; + + /* Leave room for the null byte. */ + --cbuf; + + if (cfile > 0) + { + ccopy = cfile - 1; + if (ccopy > cbuf) + ccopy = cbuf; + memcpy ((pointer) zbuf, (pointer) zfile, ccopy); + zbuf += ccopy; + cbuf -= ccopy; + if (cbuf > 0) + { + *zbuf++ = ':'; + --cbuf; + } + } + + if (clineno > 0) + { + ccopy = clineno - 1; + if (ccopy > cbuf) + ccopy = cbuf; + memcpy ((pointer) zbuf, (pointer) zlineno, ccopy); + zbuf += ccopy; + cbuf -= ccopy; + if (cbuf > 0) + { + *zbuf++ = ':'; + --cbuf; + } + } + + if (cbuf > 0 && cfile > 0) + { + *zbuf++ = ' '; + --cbuf; + --cmsg; + } + ccopy = cmsg; + if (ccopy > cbuf) + ccopy = cbuf; + memcpy ((pointer) zbuf, (pointer) zmsg, ccopy); + zbuf += ccopy; + cbuf -= ccopy; + + if (cerrno > 0) + { + if (cbuf > 0) + { + *zbuf++ = ':'; + --cbuf; + } + if (cbuf > 0) + { + *zbuf++ = ' '; + --cbuf; + } + ccopy = cerrno - 2; + if (ccopy > cbuf) + ccopy = cbuf; + memcpy ((pointer) zbuf, (pointer) zerrno, ccopy); + zbuf += ccopy; + cbuf -= ccopy; + } + + *zbuf = '\0'; + + return cret; +} + +/* Turn a number into a string. This should really call sprintf, but + since nothing else in the uuconf library calls any print routine, + it's more interesting to not call it here either. */ + +static char * +zeprint_num (ab, c, i) + char *ab; + size_t c; + register int i; +{ + register char *z; + + z = ab + c; + *--z = '\0'; + do + { + *--z = i % 10 + '0'; + i /= 10; + } + while (i != 0); + + return z; +} diff --git a/gnu/libexec/uucp/libuuconf/filnam.c b/gnu/libexec/uucp/libuuconf/filnam.c new file mode 100644 index 0000000000..ab1b76a51d --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/filnam.c @@ -0,0 +1,44 @@ +/* filnam.c + Return the saved file name. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_filnam_rcsid[] = "$Id: filnam.c,v 1.1 1993/08/04 19:34:03 jtc Exp $"; +#endif + +/* Return the saved file name. */ + +const char * +uuconf_error_filename (pglobal) + pointer pglobal; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + + if (qglobal == NULL) + return ""; + else + return qglobal->zfilename; +} diff --git a/gnu/libexec/uucp/libuuconf/freblk.c b/gnu/libexec/uucp/libuuconf/freblk.c new file mode 100644 index 0000000000..cb27d115c0 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/freblk.c @@ -0,0 +1,63 @@ +/* freblk.c + Free up an entire memory block. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_freblk_rcsid[] = "$Id: freblk.c,v 1.1 1993/08/04 19:34:05 jtc Exp $"; +#endif + +#include "alloc.h" + +/* Free up an entire memory block. */ + +#if UUCONF_ANSI_C +void +#endif +uuconf_free_block (pblock) + pointer pblock; +{ + struct sblock *q = (struct sblock *) pblock; + struct sblock *qloop; + + /* We have to free the added blocks first because the list may link + into blocks that are earlier on the list. */ + for (qloop = q; qloop != NULL; qloop = qloop->qnext) + { + struct sadded *qadd; + + for (qadd = qloop->qadded; qadd != NULL; qadd = qadd->qnext) + free (qadd->padded); + } + + while (q != NULL) + { + struct sblock *qnext; + + qnext = q->qnext; + free ((pointer) q); + q = qnext; + } +} diff --git a/gnu/libexec/uucp/libuuconf/fredia.c b/gnu/libexec/uucp/libuuconf/fredia.c new file mode 100644 index 0000000000..60b00b7cf8 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/fredia.c @@ -0,0 +1,44 @@ +/* fredia.c + Free dialer information. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_fredia_rcsid[] = "$Id: fredia.c,v 1.1 1993/08/04 19:34:08 jtc Exp $"; +#endif + +/* Free the memory allocated for a dialer. */ + +#undef uuconf_dialer_free + +/*ARGSUSED*/ +int +uuconf_dialer_free (pglobal, qdialer) + pointer pglobal; + struct uuconf_dialer *qdialer; +{ + uuconf_free_block (qdialer->uuconf_palloc); + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/free.c b/gnu/libexec/uucp/libuuconf/free.c new file mode 100644 index 0000000000..91916f1d3c --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/free.c @@ -0,0 +1,68 @@ +/* free.c + Free a buffer from within a memory block. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_free_rcsid[] = "$Id: free.c,v 1.1 1993/08/04 19:34:10 jtc Exp $"; +#endif + +#include "alloc.h" + +/* Free memory allocated by uuconf_malloc. If the memory block is + NULL, this just calls free; this is convenient for a number of + routines. Otherwise, this will only do something if this was the + last buffer allocated for one of the memory blocks in the list; in + other cases, the memory is lost until the entire memory block is + freed. */ + +#if UUCONF_ANSI_C +void +#endif +uuconf_free (pblock, pbuf) + pointer pblock; + pointer pbuf; +{ + struct sblock *q = (struct sblock *) pblock; + + if (pbuf == NULL) + return; + + if (q == NULL) + { + free (pbuf); + return; + } + + for (; q != NULL; q = q->qnext) + { + if (q->plast == pbuf) + { + q->ifree = (char *) pbuf - q->u.ab; + /* We could reset q->plast here, but it doesn't matter. */ + return; + } + } +} diff --git a/gnu/libexec/uucp/libuuconf/freprt.c b/gnu/libexec/uucp/libuuconf/freprt.c new file mode 100644 index 0000000000..2906b9dc83 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/freprt.c @@ -0,0 +1,44 @@ +/* freprt.c + Free port information. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_freprt_rcsid[] = "$Id: freprt.c,v 1.1 1993/08/04 19:34:12 jtc Exp $"; +#endif + +/* Free the memory allocated for a port. */ + +#undef uuconf_port_free + +/*ARGSUSED*/ +int +uuconf_port_free (pglobal, qport) + pointer pglobal; + struct uuconf_port *qport; +{ + uuconf_free_block (qport->uuconf_palloc); + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/fresys.c b/gnu/libexec/uucp/libuuconf/fresys.c new file mode 100644 index 0000000000..a73f4c1bc8 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/fresys.c @@ -0,0 +1,44 @@ +/* fresys.c + Free system information. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_fresys_rcsid[] = "$Id: fresys.c,v 1.1 1993/08/04 19:34:13 jtc Exp $"; +#endif + +/* Free the memory allocated for a system. */ + +#undef uuconf_system_free + +/*ARGSUSED*/ +int +uuconf_system_free (pglobal, qsys) + pointer pglobal; + struct uuconf_system *qsys; +{ + uuconf_free_block (qsys->uuconf_palloc); + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/grdcmp.c b/gnu/libexec/uucp/libuuconf/grdcmp.c new file mode 100644 index 0000000000..8b02ff9963 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/grdcmp.c @@ -0,0 +1,76 @@ +/* grdcmp.c + Compare two grades. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_grdcmp_rcsid[] = "$Id: grdcmp.c,v 1.1 1993/08/04 19:34:14 jtc Exp $"; +#endif + +#include + +/* Compare two grades, returning < 0 if b1 should be executed before + b2, == 0 if they are the same, or > 0 if b1 should be executed + after b2. This can not fail, and does not return a standard uuconf + error code. + + This implementation assumes that the upper case letters are in + sequence, and that the lower case letters are in sequence. */ + +int +uuconf_grade_cmp (barg1, barg2) + int barg1; + int barg2; +{ + int b1, b2; + + /* Make sure the arguments are unsigned. */ + b1 = (int) BUCHAR (barg1); + b2 = (int) BUCHAR (barg2); + + if (isdigit (b1)) + { + if (isdigit (b2)) + return b1 - b2; + else + return -1; + } + else if (isupper (b1)) + { + if (isdigit (b2)) + return 1; + else if (isupper (b2)) + return b1 - b2; + else + return -1; + } + else + { + if (! islower (b2)) + return 1; + else + return b1 - b2; + } +} diff --git a/gnu/libexec/uucp/libuuconf/hdial.c b/gnu/libexec/uucp/libuuconf/hdial.c new file mode 100644 index 0000000000..666cc7f62e --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/hdial.c @@ -0,0 +1,187 @@ +/* hdial.c + Find a dialer in the HDB configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_hdial_rcsid[] = "$Id: hdial.c,v 1.1 1993/08/04 19:34:15 jtc Exp $"; +#endif + +#include +#include + +/* Find a dialer in the HDB configuration files by name. */ + +int +uuconf_hdb_dialer_info (pglobal, zname, qdialer) + pointer pglobal; + const char *zname; + struct uuconf_dialer *qdialer; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + char **pz; + char *zline; + size_t cline; + char **pzsplit; + size_t csplit; + int iret; + + zline = NULL; + cline = 0; + pzsplit = NULL; + csplit = 0; + + iret = UUCONF_NOT_FOUND; + + for (pz = qglobal->qprocess->pzhdb_dialers; *pz != NULL; pz++) + { + FILE *e; + int cchars; + + qglobal->ilineno = 0; + + e = fopen (*pz, "r"); + if (e == NULL) + { + if (FNO_SUCH_FILE ()) + continue; + qglobal->ierrno = errno; + iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + while ((cchars = _uuconf_getline (qglobal, &zline, &cline, e)) > 0) + { + int ctoks; + pointer pblock; + + ++qglobal->ilineno; + + --cchars; + if (zline[cchars] == '\n') + zline[cchars] = '\0'; + if (isspace (BUCHAR (zline[0])) || zline[0] == '#') + continue; + + ctoks = _uuconf_istrsplit (zline, '\0', &pzsplit, &csplit); + if (ctoks < 0) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + if (ctoks < 1) + continue; + + if (strcmp (zname, pzsplit[0]) != 0) + continue; + + /* We found the dialer. */ + pblock = uuconf_malloc_block (); + if (pblock == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + if (uuconf_add_block (pblock, zline) != 0) + { + qglobal->ierrno = errno; + uuconf_free_block (pblock); + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + zline = NULL; + + _uuconf_uclear_dialer (qdialer); + qdialer->uuconf_zname = pzsplit[0]; + qdialer->uuconf_palloc = pblock; + + if (ctoks > 1) + { + /* The second field is characters to send instead of "=" + and "-" in phone numbers. */ + if (strcmp (pzsplit[1], "\"\"") == 0) + { + char *zsubs; + char bnext; + + zsubs = pzsplit[1]; + bnext = *zsubs; + while (bnext != '\0') + { + if (bnext == '=') + qdialer->uuconf_zdialtone = zsubs + 1; + else if (bnext == '-') + qdialer->uuconf_zpause = zsubs + 1; + if (zsubs[1] == '\0') + break; + zsubs += 2; + bnext = *zsubs; + *zsubs = '\0'; + } + } + + /* Any remaining fields form a chat script. */ + if (ctoks > 2) + { + pzsplit[1] = (char *) "chat"; + iret = _uuconf_ichat_cmd (qglobal, ctoks - 1, + pzsplit + 1, + &qdialer->uuconf_schat, + pblock); + iret &=~ UUCONF_CMDTABRET_KEEP; + if (iret != UUCONF_SUCCESS) + { + uuconf_free_block (pblock); + break; + } + } + } + + iret = UUCONF_SUCCESS; + break; + } + + (void) fclose (e); + + if (iret != UUCONF_NOT_FOUND) + break; + } + + if (zline != NULL) + free ((pointer) zline); + if (pzsplit != NULL) + free ((pointer) pzsplit); + + if (iret != UUCONF_SUCCESS && iret != UUCONF_NOT_FOUND) + { + qglobal->zfilename = *pz; + iret |= UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; + } + + return iret; +} diff --git a/gnu/libexec/uucp/libuuconf/hdnams.c b/gnu/libexec/uucp/libuuconf/hdnams.c new file mode 100644 index 0000000000..0806059214 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/hdnams.c @@ -0,0 +1,109 @@ +/* hdnams.c + Get all known dialer names from the HDB configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_hdnams_rcsid[] = "$Id: hdnams.c,v 1.1 1993/08/04 19:34:16 jtc Exp $"; +#endif + +#include +#include + +/* Get all the dialer names from the HDB Dialers file. */ + +int +uuconf_hdb_dialer_names (pglobal, ppzdialers) + pointer pglobal; + char ***ppzdialers; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + int iret; + char *zline; + size_t cline; + char **pz; + + *ppzdialers = NULL; + + iret = UUCONF_SUCCESS; + + zline = NULL; + cline = 0; + + for (pz = qglobal->qprocess->pzhdb_dialers; *pz != NULL; pz++) + { + FILE *e; + + e = fopen (*pz, "r"); + if (e == NULL) + { + if (FNO_SUCH_FILE ()) + continue; + qglobal->ierrno = errno; + iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + qglobal->ilineno = 0; + + while (_uuconf_getline (qglobal, &zline, &cline, e) > 0) + { + ++qglobal->ilineno; + + /* Lines beginning with whitespace are treated as comments. + No dialer name can contain a '#', which is another + comment character, so eliminating the first '#' does no + harm and catches comments. */ + zline[strcspn (zline, " \t#\n")] = '\0'; + if (*zline == '\0') + continue; + + iret = _uuconf_iadd_string (qglobal, zline, TRUE, TRUE, + ppzdialers, (pointer) NULL); + if (iret != UUCONF_SUCCESS) + { + iret |= UUCONF_ERROR_LINENO; + break; + } + } + + (void) fclose (e); + } + + if (zline != NULL) + free ((pointer) zline); + + if (iret != UUCONF_SUCCESS) + { + qglobal->zfilename = *pz; + return iret | UUCONF_ERROR_FILENAME; + } + + if (*ppzdialers == NULL) + iret = _uuconf_iadd_string (qglobal, (char *) NULL, FALSE, FALSE, + ppzdialers, (pointer) NULL); + + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/hinit.c b/gnu/libexec/uucp/libuuconf/hinit.c new file mode 100644 index 0000000000..2b1c1cc6fa --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/hinit.c @@ -0,0 +1,295 @@ +/* hinit.c + Initialize for reading HDB configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_hinit_rcsid[] = "$Id: hinit.c,v 1.1 1993/08/04 19:34:17 jtc Exp $"; +#endif + +#include +#include + +/* Avoid replicating OLDCONFIGLIB several times if not necessary. */ +static const char abHoldconfiglib[] = OLDCONFIGLIB; + +/* Initialize the routines which read HDB configuration files. */ + +int +uuconf_hdb_init (ppglobal, zprogram) + pointer *ppglobal; + const char *zprogram; +{ + struct sglobal **pqglobal = (struct sglobal **) ppglobal; + int iret; + struct sglobal *qglobal; + pointer pblock; + char abdialcodes[sizeof OLDCONFIGLIB + sizeof HDB_DIALCODES - 1]; + char *zsys; + FILE *e; + + if (*pqglobal == NULL) + { + iret = _uuconf_iinit_global (pqglobal); + if (iret != UUCONF_SUCCESS) + return iret; + } + + qglobal = *pqglobal; + pblock = qglobal->pblock; + + if (zprogram == NULL + || strcmp (zprogram, "uucp") == 0) + zprogram = "uucico"; + + /* Add the Dialcodes file to the global list. */ + memcpy ((pointer) abdialcodes, (pointer) abHoldconfiglib, + sizeof OLDCONFIGLIB - 1); + memcpy ((pointer) (abdialcodes + sizeof OLDCONFIGLIB - 1), + (pointer) HDB_DIALCODES, sizeof HDB_DIALCODES); + iret = _uuconf_iadd_string (qglobal, abdialcodes, TRUE, FALSE, + &qglobal->qprocess->pzdialcodefiles, + pblock); + if (iret != UUCONF_SUCCESS) + return iret; + + /* Read the Sysfiles file. We allocate the name on the heap rather + than the stack so that we can return it in + qerr->uuconf_zfilename. */ + + zsys = uuconf_malloc (pblock, + sizeof OLDCONFIGLIB + sizeof HDB_SYSFILES - 1); + if (zsys == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + memcpy ((pointer) zsys, (pointer) abHoldconfiglib, sizeof OLDCONFIGLIB - 1); + memcpy ((pointer) (zsys + sizeof OLDCONFIGLIB - 1), (pointer) HDB_SYSFILES, + sizeof HDB_SYSFILES); + + iret = UUCONF_SUCCESS; + + e = fopen (zsys, "r"); + if (e == NULL) + uuconf_free (pblock, zsys); + else + { + char *zline; + size_t cline; + char **pzargs; + size_t cargs; + char **pzcolon; + size_t ccolon; + int cchars; + + zline = NULL; + cline = 0; + pzargs = NULL; + cargs = 0; + pzcolon = NULL; + ccolon = 0; + + qglobal->ilineno = 0; + + while (iret == UUCONF_SUCCESS + && (cchars = _uuconf_getline (qglobal, &zline, &cline, e)) > 0) + { + int ctypes, cnames; + int i; + + ++qglobal->ilineno; + + --cchars; + if (zline[cchars] == '\n') + zline[cchars] = '\0'; + if (isspace (BUCHAR (zline[0])) || zline[0] == '#') + continue; + + ctypes = _uuconf_istrsplit (zline, '\0', &pzargs, &cargs); + if (ctypes < 0) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + if (ctypes == 0) + continue; + + if (strncmp (pzargs[0], "service=", sizeof "service=" - 1) != 0) + { + iret = UUCONF_SYNTAX_ERROR; + break; + } + pzargs[0] += sizeof "service=" - 1; + + cnames = _uuconf_istrsplit (pzargs[0], ':', &pzcolon, &ccolon); + if (cnames < 0) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + for (i = 0; i < cnames; i++) + if (strcmp (zprogram, pzcolon[i]) == 0) + break; + + if (i >= cnames) + continue; + + for (i = 1; i < ctypes && iret == UUCONF_SUCCESS; i++) + { + char ***ppz; + int cfiles, ifile; + + if (strncmp (pzargs[i], "systems=", sizeof "systems=" - 1) + == 0) + { + ppz = &qglobal->qprocess->pzhdb_systems; + pzargs[i] += sizeof "systems=" - 1; + } + else if (strncmp (pzargs[i], "devices=", sizeof "devices=" - 1) + == 0) + { + ppz = &qglobal->qprocess->pzhdb_devices; + pzargs[i] += sizeof "devices=" - 1; + } + else if (strncmp (pzargs[i], "dialers=", sizeof "dialers=" - 1) + == 0) + { + ppz = &qglobal->qprocess->pzhdb_dialers; + pzargs[i] += sizeof "dialers=" - 1; + } + else + { + iret = UUCONF_SYNTAX_ERROR; + break; + } + + cfiles = _uuconf_istrsplit (pzargs[i], ':', &pzcolon, &ccolon); + if (cfiles < 0) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + for (ifile = 0; + ifile < cfiles && iret == UUCONF_SUCCESS; + ifile++) + { + /* Looking for a leading '/' is Unix dependent, and + should probably be changed. */ + if (pzcolon[ifile][0] == '/') + iret = _uuconf_iadd_string (qglobal, pzcolon[ifile], TRUE, + FALSE, ppz, pblock); + else + { + char *zdir; + size_t clen; + + clen = strlen (pzcolon[ifile]); + zdir = (char *) uuconf_malloc (pblock, + (sizeof OLDCONFIGLIB + + sizeof HDB_SEPARATOR + + clen + - 1)); + if (zdir == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + memcpy ((pointer) zdir, (pointer) abHoldconfiglib, + sizeof OLDCONFIGLIB - 1); + memcpy ((pointer) (zdir + sizeof OLDCONFIGLIB - 1), + HDB_SEPARATOR, sizeof HDB_SEPARATOR - 1); + memcpy ((pointer) (zdir + + sizeof OLDCONFIGLIB - 1 + + sizeof HDB_SEPARATOR - 1), + (pointer) pzcolon[ifile], clen + 1); + iret = _uuconf_iadd_string (qglobal, zdir, FALSE, FALSE, + ppz, pblock); + } + } + } + } + + (void) fclose (e); + if (zline != NULL) + free ((pointer) zline); + if (pzargs != NULL) + free ((pointer) pzargs); + if (pzcolon != NULL) + free ((pointer) pzcolon); + + if (iret != UUCONF_SUCCESS) + { + qglobal->zfilename = zsys; + return iret | UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; + } + } + + if (qglobal->qprocess->pzhdb_systems == NULL) + { + char ab[sizeof OLDCONFIGLIB + sizeof HDB_SYSTEMS - 1]; + + memcpy ((pointer) ab, (pointer) abHoldconfiglib, + sizeof OLDCONFIGLIB - 1); + memcpy ((pointer) (ab + sizeof OLDCONFIGLIB - 1), + (pointer) HDB_SYSTEMS, sizeof HDB_SYSTEMS); + iret = _uuconf_iadd_string (qglobal, ab, TRUE, FALSE, + &qglobal->qprocess->pzhdb_systems, + pblock); + } + if (qglobal->qprocess->pzhdb_devices == NULL && iret == UUCONF_SUCCESS) + { + char ab[sizeof OLDCONFIGLIB + sizeof HDB_DEVICES - 1]; + + memcpy ((pointer) ab, (pointer) abHoldconfiglib, + sizeof OLDCONFIGLIB - 1); + memcpy ((pointer) (ab + sizeof OLDCONFIGLIB - 1), + (pointer) HDB_DEVICES, sizeof HDB_DEVICES); + iret = _uuconf_iadd_string (qglobal, ab, TRUE, FALSE, + &qglobal->qprocess->pzhdb_devices, + pblock); + } + if (qglobal->qprocess->pzhdb_dialers == NULL && iret == UUCONF_SUCCESS) + { + char ab[sizeof OLDCONFIGLIB + sizeof HDB_DIALERS - 1]; + + memcpy ((pointer) ab, (pointer) abHoldconfiglib, + sizeof OLDCONFIGLIB - 1); + memcpy ((pointer) (ab + sizeof OLDCONFIGLIB - 1), + (pointer) HDB_DIALERS, sizeof HDB_DIALERS); + iret = _uuconf_iadd_string (qglobal, ab, TRUE, FALSE, + &qglobal->qprocess->pzhdb_dialers, + pblock); + } + + return iret; +} diff --git a/gnu/libexec/uucp/libuuconf/hlocnm.c b/gnu/libexec/uucp/libuuconf/hlocnm.c new file mode 100644 index 0000000000..9af4d7e7a5 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/hlocnm.c @@ -0,0 +1,84 @@ +/* hlocnm.c + Get the local name to use from the HDB configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_hlocnm_rcsid[] = "$Id: hlocnm.c,v 1.1 1993/08/04 19:34:18 jtc Exp $"; +#endif + +#include + +/* Get the local name to use, based on the login name, from the HDB + configuration files. */ + +int +uuconf_hdb_login_localname (pglobal, zlogin, pzname) + pointer pglobal; + const char *zlogin; + char **pzname; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct shpermissions *qperm; + + if (! qglobal->qprocess->fhdb_read_permissions) + { + int iret; + + iret = _uuconf_ihread_permissions (qglobal); + if (iret != UUCONF_SUCCESS) + return iret; + } + + for (qperm = qglobal->qprocess->qhdb_permissions; + qperm != NULL; + qperm = qperm->qnext) + { + if (qperm->zmyname != NULL + && qperm->zmyname != (char *) &_uuconf_unset + && qperm->pzlogname != NULL + && qperm->pzlogname != (char **) &_uuconf_unset) + { + char **pz; + + for (pz = qperm->pzlogname; *pz != NULL; pz++) + { + if (strcmp (*pz, zlogin) == 0) + { + *pzname = strdup (qperm->zmyname); + if (*pzname == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + return UUCONF_SUCCESS; + } + } + } + } + + *pzname = NULL; + return UUCONF_NOT_FOUND; +} diff --git a/gnu/libexec/uucp/libuuconf/hport.c b/gnu/libexec/uucp/libuuconf/hport.c new file mode 100644 index 0000000000..690de91fc2 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/hport.c @@ -0,0 +1,368 @@ +/* hport.c + Find a port in the HDB configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_hport_rcsid[] = "$Id: hport.c,v 1.1 1993/08/04 19:34:20 jtc Exp $"; +#endif + +#include +#include + +/* Find a port in the HDB configuration files by name, baud rate, and + special purpose function. */ + +int +uuconf_hdb_find_port (pglobal, zname, ibaud, ihighbaud, pifn, pinfo, qport) + pointer pglobal; + const char *zname; + long ibaud; + long ihighbaud; + int (*pifn) P((struct uuconf_port *, pointer)); + pointer pinfo; + struct uuconf_port *qport; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + char *zline; + size_t cline; + char **pzsplit; + size_t csplit; + int iret; + char **pz; + + zline = NULL; + cline = 0; + pzsplit = NULL; + csplit = 0; + + iret = UUCONF_NOT_FOUND; + + for (pz = qglobal->qprocess->pzhdb_devices; *pz != NULL; pz++) + { + FILE *e; + int cchars; + + qglobal->ilineno = 0; + + e = fopen (*pz, "r"); + if (e == NULL) + { + if (FNO_SUCH_FILE ()) + continue; + qglobal->ierrno = errno; + iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + iret = UUCONF_NOT_FOUND; + + while ((cchars = _uuconf_getline (qglobal, &zline, &cline, e)) > 0) + { + int ctoks; + char *z, *zprotos, *zport; + long ilow, ihigh; + pointer pblock; + + ++qglobal->ilineno; + + iret = UUCONF_NOT_FOUND; + + --cchars; + if (zline[cchars] == '\n') + zline[cchars] = '\0'; + if (isspace (BUCHAR (zline[0])) || zline[0] == '#') + continue; + + ctoks = _uuconf_istrsplit (zline, '\0', &pzsplit, &csplit); + if (ctoks < 0) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + /* An entry in Devices is + + type device dial-device baud dialer-token pairs + + The type (normally "ACU") is treated as the name. */ + + /* If there aren't enough entries, ignore the line; this + should probably do something more useful. */ + if (ctoks < 4) + continue; + + /* There may be a comma separated list of protocols after + the name. */ + zprotos = strchr (pzsplit[0], ','); + if (zprotos != NULL) + { + *zprotos = '\0'; + ++zprotos; + } + + zport = pzsplit[0]; + + /* Get any modem class, and pick up the baud rate while + we're at it. The modem class will be appended to the + name, so we need to get it before we see if we've found + the port with the right name. */ + z = pzsplit[3]; + if (strcasecmp (z, "Any") == 0 + || strcmp (z, "-") == 0) + { + ilow = 0L; + ihigh = 0L; + } + else + { + char *zend; + + while (*z != '\0' && ! isdigit (BUCHAR (*z))) + ++z; + + ilow = strtol (z, &zend, 10); + if (*zend == '-') + ihigh = strtol (zend + 1, (char **) NULL, 10); + else + ihigh = ilow; + + if (z != pzsplit[3]) + { + size_t cclass, cport; + + cclass = z - pzsplit[3]; + cport = strlen (pzsplit[0]); + zport = malloc (cport + cclass + 1); + if (zport == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + memcpy ((pointer) zport, (pointer) pzsplit[0], cport); + memcpy ((pointer) (zport + cport), (pointer) pzsplit[3], + cclass); + zport[cport + cclass] = '\0'; + } + } + + /* Make sure the name and baud rate match any argument. */ + if ((zname != NULL + && strcmp (zport, zname) != 0) + || (ibaud != 0 + && ilow != 0 + && (ilow > ibaud || ihigh < ibaud))) + { + if (zport != pzsplit[0]) + free ((pointer) zport); + continue; + } + + /* Some systems permit ,M after the device name. This means + to open the port with O_NDELAY and then change it. We + just ignore this flag, although perhaps we should record + it somewhere. */ + pzsplit[1][strcspn (pzsplit[1], ",")] = '\0'; + + /* Now we must construct the port information, so that we + can pass it to pifn. The port type is determined by its + name, unfortunately. The name "Direct" is used for a + direct port, "TCP" for a TCP port, and anything else for + a modem port. */ + pblock = NULL; + _uuconf_uclear_port (qport); + qport->uuconf_zname = zport; + qport->uuconf_zprotocols = zprotos; + if (strcmp (pzsplit[0], "Direct") == 0) + { + qport->uuconf_ttype = UUCONF_PORTTYPE_DIRECT; + qport->uuconf_u.uuconf_sdirect.uuconf_zdevice = pzsplit[1]; + qport->uuconf_u.uuconf_sdirect.uuconf_ibaud = ilow; + } + else if (strcmp (pzsplit[0], "TCP") == 0) + { + /* For a TCP port, the device name is taken as the TCP + port to use. */ + qport->uuconf_ttype = UUCONF_PORTTYPE_TCP; + qport->uuconf_ireliable + = (UUCONF_RELIABLE_ENDTOEND | UUCONF_RELIABLE_RELIABLE + | UUCONF_RELIABLE_EIGHT | UUCONF_RELIABLE_FULLDUPLEX + | UUCONF_RELIABLE_SPECIFIED); + qport->uuconf_u.uuconf_stcp.uuconf_zport = pzsplit[1]; + } + else if (ctoks >= 5 + && (strcmp (pzsplit[4], "TLI") == 0 + || strcmp (pzsplit[4], "TLIS") == 0)) + { + size_t c; + char **pzd; + + qport->uuconf_ttype = UUCONF_PORTTYPE_TLI; + qport->uuconf_u.uuconf_stli.uuconf_zdevice = pzsplit[1]; + qport->uuconf_u.uuconf_stli.uuconf_fstream + = strcmp (pzsplit[4], "TLIS") == 0; + qport->uuconf_u.uuconf_stli.uuconf_pzpush = NULL; + pblock = uuconf_malloc_block (); + if (pblock == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + c = (ctoks - 4) * sizeof (char *); + pzd = (char **) uuconf_malloc (pblock, c + sizeof (char *)); + if (pzd == NULL) + { + qglobal->ierrno = errno; + uuconf_free_block (pblock); + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + memcpy ((pointer) pzd, (pointer) (pzsplit + 4), c); + pzd[ctoks - 4] = NULL; + qport->uuconf_u.uuconf_stli.uuconf_pzdialer = pzd; + qport->uuconf_u.uuconf_stli.uuconf_zservaddr = NULL; + qport->uuconf_ireliable + = (UUCONF_RELIABLE_ENDTOEND | UUCONF_RELIABLE_RELIABLE + | UUCONF_RELIABLE_EIGHT | UUCONF_RELIABLE_FULLDUPLEX + | UUCONF_RELIABLE_SPECIFIED); + } + else + { + qport->uuconf_ttype = UUCONF_PORTTYPE_MODEM; + qport->uuconf_u.uuconf_smodem.uuconf_zdevice = pzsplit[1]; + if (strcmp (pzsplit[2], "-") != 0) + qport->uuconf_u.uuconf_smodem.uuconf_zdial_device = + pzsplit[2]; + else + qport->uuconf_u.uuconf_smodem.uuconf_zdial_device = NULL; + if (ilow == ihigh) + { + qport->uuconf_u.uuconf_smodem.uuconf_ibaud = ilow; + qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud = 0L; + qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud = 0L; + } + else + { + qport->uuconf_u.uuconf_smodem.uuconf_ibaud = 0L; + qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud = ilow; + qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud = ihigh; + } + qport->uuconf_u.uuconf_smodem.uuconf_fcarrier = TRUE; + if (ctoks < 5) + qport->uuconf_u.uuconf_smodem.uuconf_pzdialer = NULL; + else + { + size_t c; + char **pzd; + + pblock = uuconf_malloc_block (); + if (pblock == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + c = (ctoks - 4) * sizeof (char *); + pzd = (char **) uuconf_malloc (pblock, c + sizeof (char *)); + if (pzd == NULL) + { + qglobal->ierrno = errno; + uuconf_free_block (pblock); + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + memcpy ((pointer) pzd, (pointer) (pzsplit + 4), c); + pzd[ctoks - 4] = NULL; + + qport->uuconf_u.uuconf_smodem.uuconf_pzdialer = pzd; + } + qport->uuconf_u.uuconf_smodem.uuconf_qdialer = NULL; + } + + if (pifn != NULL) + { + iret = (*pifn) (qport, pinfo); + if (iret != UUCONF_SUCCESS) + { + if (zport != pzsplit[0]) + free ((pointer) zport); + if (pblock != NULL) + uuconf_free_block (pblock); + if (iret != UUCONF_NOT_FOUND) + break; + continue; + } + } + + /* This is the port we want. */ + if (pblock == NULL) + { + pblock = uuconf_malloc_block (); + if (pblock == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + } + + if (uuconf_add_block (pblock, zline) != 0 + || (zport != pzsplit[0] + && uuconf_add_block (pblock, zport) != 0)) + { + qglobal->ierrno = errno; + uuconf_free_block (pblock); + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + zline = NULL; + + qport->uuconf_palloc = pblock; + + break; + } + + (void) fclose (e); + + if (iret != UUCONF_NOT_FOUND) + break; + } + + if (zline != NULL) + free ((pointer) zline); + if (pzsplit != NULL) + free ((pointer) pzsplit); + + if (iret != UUCONF_SUCCESS && iret != UUCONF_NOT_FOUND) + { + qglobal->zfilename = *pz; + iret |= UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; + } + + return iret; +} diff --git a/gnu/libexec/uucp/libuuconf/hrmunk.c b/gnu/libexec/uucp/libuuconf/hrmunk.c new file mode 100644 index 0000000000..e7516f7e78 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/hrmunk.c @@ -0,0 +1,55 @@ +/* remunk.c + Get the name of the HDB remote.unknown shell script. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_hrmunk_rcsid[] = "$Id: hrmunk.c,v 1.1 1993/08/04 19:34:22 jtc Exp $"; +#endif + +#include + +/* Get the name of the HDB remote.unknown shell script. */ + +int +uuconf_hdb_remote_unknown (pglobal, pzname) + pointer pglobal; + char **pzname; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + size_t csize; + + csize = sizeof OLDCONFIGLIB + sizeof HDB_REMOTE_UNKNOWN - 1; + *pzname = malloc (csize); + if (*pzname == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + memcpy (*pzname, OLDCONFIGLIB, sizeof OLDCONFIGLIB - 1); + memcpy (*pzname + sizeof OLDCONFIGLIB - 1, HDB_REMOTE_UNKNOWN, + sizeof HDB_REMOTE_UNKNOWN); + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/hsinfo.c b/gnu/libexec/uucp/libuuconf/hsinfo.c new file mode 100644 index 0000000000..83129a3e70 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/hsinfo.c @@ -0,0 +1,625 @@ +/* hsinfo.c + Get information about a system from the HDB configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_hsinfo_rcsid[] = "$Id: hsinfo.c,v 1.1 1993/08/04 19:34:24 jtc Exp $"; +#endif + +#include +#include + +static int ihadd_machine_perm P((struct sglobal *qglobal, + struct uuconf_system *qsys, + struct shpermissions *qperm)); +static int ihadd_logname_perm P((struct sglobal *qglobal, + struct uuconf_system *qsys, + struct shpermissions *qperm)); + +/* Get the information for a particular system from the HDB + configuration files. This does not make sure that all the default + values are set. */ + +int +_uuconf_ihdb_system_internal (qglobal, zsystem, qsys) + struct sglobal *qglobal; + const char *zsystem; + struct uuconf_system *qsys; +{ + int iret; + struct shpermissions *qperm; + char *zline; + size_t cline; + char **pzsplit; + size_t csplit; + char **pzcomma; + size_t ccomma; + pointer pblock; + char **pz; + boolean ffound_machine, ffound_login; + struct shpermissions *qother_machine; + struct uuconf_system *qalt; + + if (! qglobal->qprocess->fhdb_read_permissions) + { + iret = _uuconf_ihread_permissions (qglobal); + if (iret != UUCONF_SUCCESS) + return iret; + } + + /* First look through the Permissions information to see if this is + an alias for some system. I assume that an alias applies to the + first name in the corresponding MACHINE entry. */ + + for (qperm = qglobal->qprocess->qhdb_permissions; + qperm != NULL; + qperm = qperm->qnext) + { + if (qperm->pzalias == NULL + || qperm->pzmachine == NULL + || qperm->pzalias == (char **) &_uuconf_unset + || qperm->pzmachine == (char **) &_uuconf_unset) + continue; + + for (pz = qperm->pzalias; *pz != NULL; pz++) + { + if (strcmp (*pz, zsystem) == 0) + { + zsystem = qperm->pzmachine[0]; + break; + } + } + if (*pz != NULL) + break; + } + + zline = NULL; + cline = 0; + pzsplit = NULL; + csplit = 0; + pzcomma = NULL; + ccomma = 0; + + pblock = NULL; + + iret = UUCONF_SUCCESS; + + for (pz = qglobal->qprocess->pzhdb_systems; *pz != NULL; pz++) + { + FILE *e; + int cchars; + + e = fopen (*pz, "r"); + if (e == NULL) + { + if (FNO_SUCH_FILE ()) + continue; + qglobal->ierrno = errno; + iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + qglobal->ilineno = 0; + + while ((cchars = _uuconf_getline (qglobal, &zline, &cline, e)) > 0) + { + int ctoks, ctimes, i; + struct uuconf_system *qset; + char *z, *zretry; + int cretry; + + ++qglobal->ilineno; + + --cchars; + if (zline[cchars] == '\n') + zline[cchars] = '\0'; + if (isspace (BUCHAR (zline[0])) || zline[0] == '#') + continue; + + ctoks = _uuconf_istrsplit (zline, '\0', &pzsplit, &csplit); + if (ctoks < 0) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + /* If this isn't the system we're looking for, keep reading + the file. */ + if (ctoks < 1 + || strcmp (zsystem, pzsplit[0]) != 0) + continue; + + /* If this is the first time we've found the system, we want + to set *qsys directly. Otherwise, we allocate a new + alternate. */ + if (pblock == NULL) + { + pblock = uuconf_malloc_block (); + if (pblock == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + _uuconf_uclear_system (qsys); + qsys->uuconf_palloc = pblock; + qset = qsys; + } + else + { + struct uuconf_system **pq; + + qset = ((struct uuconf_system *) + uuconf_malloc (pblock, sizeof (struct uuconf_system))); + if (qset == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + _uuconf_uclear_system (qset); + for (pq = &qsys->uuconf_qalternate; + *pq != NULL; + pq = &(*pq)->uuconf_qalternate) + ; + *pq = qset; + } + + /* Add this line to the memory block we are building for the + system. */ + if (uuconf_add_block (pblock, zline) != 0) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + zline = NULL; + cline = 0; + + /* The format of a line in Systems is + system time device speed phone chat + For example, + airs Any ACU 9600 5551212 ogin: foo pass: bar + */ + + /* Get the system name. */ + + qset->uuconf_zname = pzsplit[0]; + qset->uuconf_fcall = TRUE; + qset->uuconf_fcalled = FALSE; + + if (ctoks < 2) + continue; + + /* A time string is "time/grade,time/grade;retry". A + missing grade is taken as BGRADE_LOW. */ + zretry = strchr (pzsplit[1], ';'); + if (zretry == NULL) + cretry = 0; + else + { + *zretry = '\0'; + cretry = (int) strtol (zretry + 1, (char **) NULL, 10); + } + + ctimes = _uuconf_istrsplit (pzsplit[1], ',', &pzcomma, &ccomma); + if (ctimes < 0) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + for (i = 0; i < ctimes; i++) + { + char *zslash; + char bgrade; + + z = pzcomma[i]; + zslash = strchr (z, '/'); + if (zslash == NULL) + bgrade = UUCONF_GRADE_LOW; + else + { + *zslash = '\0'; + bgrade = zslash[1]; + if (! UUCONF_GRADE_LEGAL (bgrade)) + bgrade = UUCONF_GRADE_LOW; + } + + iret = _uuconf_itime_parse (qglobal, z, (long) bgrade, + cretry, _uuconf_itime_grade_cmp, + &qset->uuconf_qtimegrade, + pblock); + if (iret != UUCONF_SUCCESS) + break; + } + + if (iret != UUCONF_SUCCESS) + break; + + if (ctoks < 3) + continue; + + /* Pick up the device name. It can be followed by a comma + and a list of protocols. */ + qset->uuconf_zport = pzsplit[2]; + z = strchr (pzsplit[2], ','); + if (z != NULL) + { + qset->uuconf_zprotocols = z + 1; + *z = '\0'; + } + + if (ctoks < 4) + continue; + + /* The speed entry can be a numeric speed, or a range of + speeds, or "Any", or "-". If it starts with a letter, + the initial nonnumeric prefix is a modem class, which + gets appended to the port name. */ + z = pzsplit[3]; + if (strcasecmp (z, "Any") != 0 + && strcmp (z, "-") != 0) + { + char *zend; + + while (*z != '\0' && ! isdigit (BUCHAR (*z))) + ++z; + + qset->uuconf_ibaud = strtol (z, &zend, 10); + if (*zend == '-') + qset->uuconf_ihighbaud = strtol (zend + 1, (char **) NULL, + 10); + + if (z != pzsplit[3]) + { + size_t cport, cclass; + + cport = strlen (pzsplit[2]); + cclass = z - pzsplit[3]; + qset->uuconf_zport = uuconf_malloc (pblock, + cport + cclass + 1); + if (qset->uuconf_zport == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + memcpy ((pointer) qset->uuconf_zport, (pointer) pzsplit[2], + cport); + memcpy ((pointer) (qset->uuconf_zport + cport), + (pointer) pzsplit[3], cclass); + qset->uuconf_zport[cport + cclass] = '\0'; + } + } + + if (ctoks < 5) + continue; + + /* Get the phone number. */ + qset->uuconf_zphone = pzsplit[4]; + + if (ctoks < 6) + continue; + + /* Get the chat script. We just hand this off to the chat + script processor, so that it will parse subsend and + subexpect strings correctly. */ + pzsplit[4] = (char *) "chat"; + iret = _uuconf_ichat_cmd (qglobal, ctoks - 4, pzsplit + 4, + &qset->uuconf_schat, pblock); + iret &=~ UUCONF_CMDTABRET_KEEP; + if (iret != UUCONF_SUCCESS) + break; + } + + (void) fclose (e); + + if (iret != UUCONF_SUCCESS) + break; + } + + if (zline != NULL) + free ((pointer) zline); + if (pzsplit != NULL) + free ((pointer) pzsplit); + if (pzcomma != NULL) + free ((pointer) pzcomma); + + if (iret != UUCONF_SUCCESS) + { + qglobal->zfilename = *pz; + return iret | UUCONF_ERROR_FILENAME; + } + + if (pblock == NULL) + return UUCONF_NOT_FOUND; + + /* Now we have to put in the Permissions information. The relevant + Permissions entries are those with this system in the MACHINE + list and (if this system does not have a VALIDATE entry) those + with a LOGNAME list but no MACHINE list. If no entry is found + with this system in the MACHINE list, then we must look for an + entry with "OTHER" in the MACHINE list. */ + ffound_machine = FALSE; + ffound_login = FALSE; + qother_machine = NULL; + for (qperm = qglobal->qprocess->qhdb_permissions; + qperm != NULL; + qperm = qperm->qnext) + { + boolean fmachine; + + /* MACHINE=OTHER is recognized specially. It appears that OTHER + need only be recognized by itself, not when combined with + other machine names. */ + if (qother_machine == NULL + && qperm->pzmachine != NULL + && qperm->pzmachine != (char **) &_uuconf_unset + && qperm->pzmachine[0][0] == 'O' + && strcmp (qperm->pzmachine[0], "OTHER") == 0) + qother_machine = qperm; + + /* If this system is named in a MACHINE entry, we must add the + appropriate information to every alternate that could be used + for calling out. */ + fmachine = FALSE; + if (! ffound_machine + && qperm->pzmachine != NULL + && qperm->pzmachine != (char **) &_uuconf_unset) + { + for (pz = qperm->pzmachine; *pz != NULL; pz++) + { + if ((*pz)[0] == zsystem[0] + && strcmp (*pz, zsystem) == 0) + { + for (qalt = qsys; + qalt != NULL; + qalt = qalt->uuconf_qalternate) + { + if (qalt->uuconf_fcall) + { + iret = ihadd_machine_perm (qglobal, qalt, qperm); + if (iret != UUCONF_SUCCESS) + return iret; + } + } + + fmachine = TRUE; + ffound_machine = TRUE; + + break; + } + } + } + + /* A LOGNAME line applies to this machine if it is listed in the + corresponding VALIDATE entry, or if it is not listed in any + VALIDATE entry. On this pass through the Permissions entry + we pick up the information if the system appears in a + VALIDATE entry; if it does not, we make another pass to put + in all the LOGNAME lines. */ + if (qperm->pzlogname != NULL + && qperm->pzlogname != (char **) &_uuconf_unset + && qperm->pzvalidate != NULL + && qperm->pzvalidate != (char **) &_uuconf_unset) + { + for (pz = qperm->pzvalidate; *pz != NULL; ++pz) + if ((*pz)[0] == zsystem[0] + && strcmp (*pz, zsystem) == 0) + break; + if (*pz != NULL) + { + for (pz = qperm->pzlogname; *pz != NULL; ++pz) + { + /* If this LOGNAME line is also a matching MACHINE + line, we can add the LOGNAME permissions to the + first alternate. Otherwise, we must create a new + alternate. We cannot put a LOGNAME line in the + first alternate if MACHINE does not match, + because certain permissions (e.g. READ) may be + specified by both types of lines, and we must use + LOGNAME entries only when accepting calls and + MACHINE entries only when placing calls. */ + if (fmachine + && (qsys->uuconf_zcalled_login == NULL + || (qsys->uuconf_zcalled_login + == (char *) &_uuconf_unset))) + { + qsys->uuconf_zcalled_login = *pz; + iret = ihadd_logname_perm (qglobal, qsys, qperm); + } + else + { + struct uuconf_system *qnew; + struct uuconf_system **pq; + + qnew = ((struct uuconf_system *) + uuconf_malloc (pblock, + sizeof (struct uuconf_system))); + if (qnew == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + + *qnew = *qsys; + qnew->uuconf_qalternate = NULL; + for (pq = &qsys->uuconf_qalternate; + *pq != NULL; + pq = &(*pq)->uuconf_qalternate) + ; + *pq = qnew; + + qnew->uuconf_zcalled_login = *pz; + qnew->uuconf_fcall = FALSE; + iret = ihadd_logname_perm (qglobal, qnew, qperm); + } + + if (iret != UUCONF_SUCCESS) + return iret; + } + + ffound_login = TRUE; + } + } + } + + /* If we didn't find an entry for the machine, we must use the + MACHINE=OTHER entry, if any. */ + if (! ffound_machine && qother_machine != NULL) + { + for (qalt = qsys; qalt != NULL; qalt = qalt->uuconf_qalternate) + { + if (qalt->uuconf_fcall) + { + iret = ihadd_machine_perm (qglobal, qalt, qother_machine); + if (iret != UUCONF_SUCCESS) + return iret; + } + } + } + + /* If this system was not listed in any VALIDATE entry, then we must + add a called-login for each LOGNAME entry in Permissions. */ + if (! ffound_login) + { + for (qperm = qglobal->qprocess->qhdb_permissions; + qperm != NULL; + qperm = qperm->qnext) + { + if (qperm->pzlogname == NULL + || qperm->pzlogname == (char **) &_uuconf_unset) + continue; + + for (pz = qperm->pzlogname; *pz != NULL; pz++) + { + struct uuconf_system *qnew; + struct uuconf_system **pq; + + qnew = ((struct uuconf_system *) + uuconf_malloc (pblock, + sizeof (struct uuconf_system))); + if (qnew == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + + *qnew = *qsys; + qnew->uuconf_qalternate = NULL; + for (pq = &qsys->uuconf_qalternate; + *pq != NULL; + pq = &(*pq)->uuconf_qalternate) + ; + *pq = qnew; + + /* We recognize LOGNAME=OTHER specially, although this + appears to be an SCO innovation. */ + if (strcmp (*pz, "OTHER") == 0) + qnew->uuconf_zcalled_login = (char *) "ANY"; + else + qnew->uuconf_zcalled_login = *pz; + qnew->uuconf_fcall = FALSE; + iret = ihadd_logname_perm (qglobal, qnew, qperm); + if (iret != UUCONF_SUCCESS) + return iret; + } + } + } + + /* HDB permits local requests to receive to any directory, which is + not the default put in by _uuconf_isystem_basic_default. We set + it here instead. */ + for (qalt = qsys; qalt != NULL; qalt = qalt->uuconf_qalternate) + { + iret = _uuconf_iadd_string (qglobal, (char *) ZROOTDIR, + FALSE, FALSE, + &qalt->uuconf_pzlocal_receive, + pblock); + if (iret != UUCONF_SUCCESS) + return iret; + } + + /* HDB does not have a maximum number of retries if a retry time is + given in the time field. */ + if (qsys->uuconf_qtimegrade != NULL + && qsys->uuconf_qtimegrade != (struct uuconf_timespan *) &_uuconf_unset + && qsys->uuconf_qtimegrade->uuconf_cretry > 0) + qsys->uuconf_cmax_retries = 0; + + return UUCONF_SUCCESS; +} + +/* Add the settings of a MACHINE line in Permissions to a system. */ + +/*ARGSIGNORED*/ +static int +ihadd_machine_perm (qglobal, qsys, qperm) + struct sglobal *qglobal; + struct uuconf_system *qsys; + struct shpermissions *qperm; +{ + if (qperm->frequest >= 0) + qsys->uuconf_fsend_request = qperm->frequest; + else + qsys->uuconf_fsend_request = FALSE; + qsys->uuconf_pzremote_send = qperm->pzread; + qsys->uuconf_pzremote_receive = qperm->pzwrite; + qsys->uuconf_pzcmds = qperm->pzcommands; + qsys->uuconf_zlocalname = qperm->zmyname; + qsys->uuconf_zpubdir = qperm->zpubdir; + qsys->uuconf_pzalias = qperm->pzalias; + + return UUCONF_SUCCESS; +} + +/* Add the settings of a LOGNAME line in Permissions to a system. */ + +/*ARGSIGNORED*/ +static int +ihadd_logname_perm (qglobal, qsys, qperm) + struct sglobal *qglobal; + struct uuconf_system *qsys; + struct shpermissions *qperm; +{ + qsys->uuconf_fcalled = TRUE; + if (qperm->frequest >= 0) + qsys->uuconf_fsend_request = qperm->frequest; + else + qsys->uuconf_fsend_request = FALSE; + qsys->uuconf_fcalled_transfer = qperm->fsendfiles; + qsys->uuconf_pzremote_send = qperm->pzread; + qsys->uuconf_pzremote_receive = qperm->pzwrite; + qsys->uuconf_fcallback = qperm->fcallback; + qsys->uuconf_zlocalname = qperm->zmyname; + qsys->uuconf_zpubdir = qperm->zpubdir; + + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/hsnams.c b/gnu/libexec/uucp/libuuconf/hsnams.c new file mode 100644 index 0000000000..de003a3ec5 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/hsnams.c @@ -0,0 +1,142 @@ +/* hsnams.c + Get all known system names from the HDB configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_hsnams_rcsid[] = "$Id: hsnams.c,v 1.1 1993/08/04 19:34:26 jtc Exp $"; +#endif + +#include +#include + +/* Get all the system names from the HDB Systems file. We have to + read the Permissions file in order to support aliases. */ + +int +uuconf_hdb_system_names (pglobal, ppzsystems, falias) + pointer pglobal; + char ***ppzsystems; + int falias; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + int iret; + char *zline; + size_t cline; + char **pz; + + *ppzsystems = NULL; + + iret = UUCONF_SUCCESS; + + zline = NULL; + cline = 0; + + for (pz = qglobal->qprocess->pzhdb_systems; *pz != NULL; pz++) + { + FILE *e; + + e = fopen (*pz, "r"); + if (e == NULL) + { + if (FNO_SUCH_FILE ()) + continue; + qglobal->ierrno = errno; + iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + qglobal->ilineno = 0; + + while (_uuconf_getline (qglobal, &zline, &cline, e) > 0) + { + ++qglobal->ilineno; + + /* Lines beginning with whitespace are treated as comments. + No system name can contain a '#', which is another + comment character, so eliminating the first '#' does no + harm and catches comments. */ + zline[strcspn (zline, " \t#\n")] = '\0'; + if (*zline == '\0') + continue; + + iret = _uuconf_iadd_string (qglobal, zline, TRUE, TRUE, + ppzsystems, (pointer) NULL); + if (iret != UUCONF_SUCCESS) + { + iret |= UUCONF_ERROR_LINENO; + break; + } + } + + (void) fclose (e); + } + + if (zline != NULL) + free ((pointer) zline); + + if (iret != UUCONF_SUCCESS) + { + qglobal->zfilename = *pz; + return iret | UUCONF_ERROR_FILENAME; + } + + /* If we are supposed to return aliases, we must read the + Permissions file. */ + if (falias) + { + struct shpermissions *q; + + if (! qglobal->qprocess->fhdb_read_permissions) + { + iret = _uuconf_ihread_permissions (qglobal); + if (iret != UUCONF_SUCCESS) + return iret; + } + + for (q = qglobal->qprocess->qhdb_permissions; + q != NULL; + q = q->qnext) + { + pz = q->pzalias; + if (pz == NULL || pz == (char **) &_uuconf_unset) + continue; + + for (; *pz != NULL; pz++) + { + iret = _uuconf_iadd_string (qglobal, *pz, TRUE, TRUE, + ppzsystems, (pointer) NULL); + if (iret != UUCONF_SUCCESS) + return iret; + } + } + } + + if (*ppzsystems == NULL) + iret = _uuconf_iadd_string (qglobal, (char *) NULL, FALSE, FALSE, + ppzsystems, (pointer) NULL); + + return iret; +} diff --git a/gnu/libexec/uucp/libuuconf/hsys.c b/gnu/libexec/uucp/libuuconf/hsys.c new file mode 100644 index 0000000000..676c20ff0e --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/hsys.c @@ -0,0 +1,49 @@ +/* hsys.c + User function to get a system from the HDB configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_hsys_rcsid[] = "$Id: hsys.c,v 1.1 1993/08/04 19:34:29 jtc Exp $"; +#endif + +/* Get system information from the HDB configuration files. This is a + wrapper for the internal function which makes sure that every field + gets a default value. */ + +int +uuconf_hdb_system_info (pglobal, zsystem, qsys) + pointer pglobal; + const char *zsystem; + struct uuconf_system *qsys; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + int iret; + + iret = _uuconf_ihdb_system_internal (qglobal, zsystem, qsys); + if (iret != UUCONF_SUCCESS) + return iret; + return _uuconf_isystem_basic_default (qglobal, qsys); +} diff --git a/gnu/libexec/uucp/libuuconf/hunk.c b/gnu/libexec/uucp/libuuconf/hunk.c new file mode 100644 index 0000000000..8e997d0b71 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/hunk.c @@ -0,0 +1,142 @@ +/* hunk.c + Get information about an unknown system from the HDB Permissions file. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_hunk_rcsid[] = "$Id: hunk.c,v 1.1 1993/08/04 19:34:31 jtc Exp $"; +#endif + +#include + +/* Get information about an unknown system from the HDB Permissions + file. This doesn't run the remote.unknown shell script, because + that's too system dependent. */ + +int +uuconf_hdb_system_unknown (pglobal, qsys) + pointer pglobal; + struct uuconf_system *qsys; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + int iret; + boolean ffirst; + struct shpermissions *qperm; + struct uuconf_system *qalt; + + if (! qglobal->qprocess->fhdb_read_permissions) + { + iret = _uuconf_ihread_permissions (qglobal); + if (iret != UUCONF_SUCCESS) + return iret; + } + + _uuconf_uclear_system (qsys); + qsys->uuconf_palloc = uuconf_malloc_block (); + if (qsys->uuconf_palloc == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + + ffirst = TRUE; + + for (qperm = qglobal->qprocess->qhdb_permissions; + qperm != NULL; + qperm = qperm->qnext) + { + char **pz; + + if (qperm->pzlogname == NULL + || qperm->pzlogname == (char **) &_uuconf_unset) + continue; + + for (pz = qperm->pzlogname; *pz != NULL; pz++) + { + if (ffirst) + { + qalt = qsys; + ffirst = FALSE; + } + else + { + struct uuconf_system **pq; + + qalt = ((struct uuconf_system *) + uuconf_malloc (qsys->uuconf_palloc, + sizeof (struct uuconf_system))); + if (qalt == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + + _uuconf_uclear_system (qalt); + for (pq = &qsys->uuconf_qalternate; + *pq != NULL; + pq = &(*pq)->uuconf_qalternate) + ; + *pq = qalt; + } + + /* We recognize LOGNAME=OTHER specially, although this + appears to be an SCO innovation. */ + if (strcmp (*pz, "OTHER") == 0) + qalt->uuconf_zcalled_login = (char *) "ANY"; + else + qalt->uuconf_zcalled_login = *pz; + qalt->uuconf_fcall = FALSE; + qsys->uuconf_fcalled = TRUE; + if (qperm->frequest >= 0) + qsys->uuconf_fsend_request = qperm->frequest; + else + qsys->uuconf_fsend_request = FALSE; + qsys->uuconf_fcalled_transfer = qperm->fsendfiles; + qsys->uuconf_pzremote_send = qperm->pzread; + qsys->uuconf_pzremote_receive = qperm->pzwrite; + qsys->uuconf_fcallback = qperm->fcallback; + qsys->uuconf_zlocalname = qperm->zmyname; + qsys->uuconf_zpubdir = qperm->zpubdir; + } + } + + if (ffirst) + return UUCONF_NOT_FOUND; + + /* HDB permits local requests to receive to any directory, which is + not the default put in by _uuconf_isystem_basic_default. We set + it here instead. */ + for (qalt = qsys; qalt != NULL; qalt = qalt->uuconf_qalternate) + { + iret = _uuconf_iadd_string (qglobal, (char *) ZROOTDIR, + FALSE, FALSE, + &qalt->uuconf_pzlocal_receive, + qsys->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + return iret; + } + + return _uuconf_isystem_basic_default (qglobal, qsys); +} diff --git a/gnu/libexec/uucp/libuuconf/iniglb.c b/gnu/libexec/uucp/libuuconf/iniglb.c new file mode 100644 index 0000000000..2422656efc --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/iniglb.c @@ -0,0 +1,177 @@ +/* iniglb.c + Initialize the global information structure. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_iniglb_rcsid[] = "$Id: iniglb.c,v 1.1 1993/08/04 19:34:33 jtc Exp $"; +#endif + +#include + +/* Initialize the global information structure. */ + +int +_uuconf_iinit_global (pqglobal) + struct sglobal **pqglobal; +{ + pointer pblock; + register struct sprocess *qprocess; + char *azargs[3]; + int iret; + + pblock = uuconf_malloc_block (); + if (pblock == NULL) + return UUCONF_MALLOC_FAILED; + + *pqglobal = (struct sglobal *) uuconf_malloc (pblock, + sizeof (struct sglobal)); + if (*pqglobal == NULL) + { + uuconf_free_block (pblock); + return UUCONF_MALLOC_FAILED; + } + + (*pqglobal)->qprocess = ((struct sprocess *) + uuconf_malloc (pblock, + sizeof (struct sprocess))); + if ((*pqglobal)->qprocess == NULL) + { + uuconf_free_block (pblock); + *pqglobal = NULL; + return UUCONF_MALLOC_FAILED; + } + + (*pqglobal)->pblock = pblock; + (*pqglobal)->ierrno = 0; + (*pqglobal)->ilineno = 0; + (*pqglobal)->zfilename = NULL; + + qprocess = (*pqglobal)->qprocess; + + qprocess->zlocalname = NULL; + qprocess->zspooldir = SPOOLDIR; + qprocess->zpubdir = PUBDIR; +#ifdef LOCKDIR + qprocess->zlockdir = LOCKDIR; +#else + qprocess->zlockdir = SPOOLDIR; +#endif + qprocess->zlogfile = LOGFILE; + qprocess->zstatsfile = STATFILE; + qprocess->zdebugfile = DEBUGFILE; + qprocess->zdebug = ""; + qprocess->cmaxuuxqts = 0; + qprocess->fv2 = TRUE; + qprocess->fhdb = TRUE; + qprocess->pzdialcodefiles = NULL; + qprocess->pztimetables = NULL; + qprocess->zconfigfile = NULL; + qprocess->pzsysfiles = NULL; + qprocess->pzportfiles = NULL; + qprocess->pzdialfiles = NULL; + qprocess->pzpwdfiles = NULL; + qprocess->pzcallfiles = NULL; + qprocess->qunknown = NULL; + qprocess->fread_syslocs = FALSE; + qprocess->qsyslocs = NULL; + qprocess->qvalidate = NULL; + qprocess->fuses_myname = FALSE; + qprocess->zv2systems = NULL; + qprocess->zv2devices = NULL; + qprocess->zv2userfile = NULL; + qprocess->zv2cmds = NULL; + qprocess->pzhdb_systems = NULL; + qprocess->pzhdb_devices = NULL; + qprocess->pzhdb_dialers = NULL; + qprocess->fhdb_read_permissions = FALSE; + qprocess->qhdb_permissions = NULL; + + azargs[0] = NULL; + azargs[1] = (char *) "Evening"; + azargs[2] = (char *) "Wk1705-0755,Sa,Su"; + iret = _uuconf_itimetable ((pointer) *pqglobal, 3, azargs, + (pointer) NULL, (pointer) NULL); + if (UUCONF_ERROR_VALUE (iret) == UUCONF_SUCCESS) + { + azargs[1] = (char *) "Night"; + azargs[2] = (char *) "Wk2305-0755,Sa,Su2305-1655"; + iret = _uuconf_itimetable ((pointer) *pqglobal, 3, azargs, + (pointer) NULL, (pointer) NULL); + } + if (UUCONF_ERROR_VALUE (iret) == UUCONF_SUCCESS) + { + azargs[1] = (char *) "NonPeak"; + azargs[2] = (char *) "Wk1805-0655,Sa,Su"; + iret = _uuconf_itimetable ((pointer) *pqglobal, 3, azargs, + (pointer) NULL, (pointer) NULL); + } + if (UUCONF_ERROR_VALUE (iret) != UUCONF_SUCCESS) + { + uuconf_free_block (pblock); + *pqglobal = NULL; + + /* Strip off any special bits, since there's no global + structure. */ + return UUCONF_ERROR_VALUE (iret); + } + + return UUCONF_SUCCESS; +} + +/* Add a timetable. This is also called by the Taylor UUCP + initialization code, as well as by the Taylor UUCP sys file code + (although the latter is obsolete). There's no point in putting + this in a separate file, since everything must call + _uuconf_init_global. There is a race condition here if this is + called by two different threads on a sys file command, but the sys + file command is obsolete anyhow. */ + +/*ARGSUSED*/ +int +_uuconf_itimetable (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + int iret; + + iret = _uuconf_iadd_string (qglobal, argv[1], FALSE, FALSE, + &qglobal->qprocess->pztimetables, + qglobal->pblock); + if (iret != UUCONF_SUCCESS) + return iret | UUCONF_CMDTABRET_EXIT; + + iret = _uuconf_iadd_string (qglobal, argv[2], FALSE, FALSE, + &qglobal->qprocess->pztimetables, + qglobal->pblock); + if (iret != UUCONF_SUCCESS) + return iret | UUCONF_CMDTABRET_EXIT; + + return UUCONF_CMDTABRET_KEEP; +} diff --git a/gnu/libexec/uucp/libuuconf/init.c b/gnu/libexec/uucp/libuuconf/init.c new file mode 100644 index 0000000000..ef252a7824 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/init.c @@ -0,0 +1,74 @@ +/* init.c + Initialize for reading UUCP configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_init_rcsid[] = "$Id: init.c,v 1.1 1993/08/04 19:34:35 jtc Exp $"; +#endif + +/* Initialize the UUCP configuration file reading routines. This is + just a generic routine which calls the type specific routines. */ + +/*ARGSUSED*/ +int +uuconf_init (ppglobal, zprogram, zname) + pointer *ppglobal; + const char *zprogram; + const char *zname; +{ + struct sglobal **pqglob = (struct sglobal **) ppglobal; + int iret; + + iret = UUCONF_NOT_FOUND; + + *pqglob = NULL; + +#if HAVE_TAYLOR_CONFIG + iret = uuconf_taylor_init (ppglobal, zprogram, zname); + if (iret != UUCONF_SUCCESS) + return iret; +#endif + +#if HAVE_V2_CONFIG + if (*pqglob == NULL || (*pqglob)->qprocess->fv2) + { + iret = uuconf_v2_init (ppglobal); + if (iret != UUCONF_SUCCESS) + return iret; + } +#endif + +#if HAVE_HDB_CONFIG + if (*pqglob == NULL || (*pqglob)->qprocess->fhdb) + { + iret = uuconf_hdb_init (ppglobal, zprogram); + if (iret != UUCONF_SUCCESS) + return iret; + } +#endif + + return iret; +} diff --git a/gnu/libexec/uucp/libuuconf/int.c b/gnu/libexec/uucp/libuuconf/int.c new file mode 100644 index 0000000000..b0047ee6c0 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/int.c @@ -0,0 +1,59 @@ +/* int.c + Parse a string into an int or a long. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_int_rcsid[] = "$Id: int.c,v 1.1 1993/08/04 19:34:37 jtc Exp $"; +#endif + +/* Parse a string into a variable. This is called by uuconf_cmd_args, + as well as other functions. The parsing is done in a single place + to make it easy to change. This should return an error code, + including both UUCONF_CMDTABRET_KEEP and UUCONF_CMDTABRET_EXIT if + appropriate. */ + +/*ARGSIGNORED*/ +int +_uuconf_iint (qglobal, zval, p, fint) + struct sglobal *qglobal; + const char *zval; + pointer p; + boolean fint; +{ + long i; + char *zend; + + i = strtol ((char *) zval, &zend, 10); + if (*zend != '\0') + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; + + if (fint) + *(int *) p = (int) i; + else + *(long *) p = i; + + return UUCONF_CMDTABRET_CONTINUE; +} diff --git a/gnu/libexec/uucp/libuuconf/lckdir.c b/gnu/libexec/uucp/libuuconf/lckdir.c new file mode 100644 index 0000000000..52d1a9fc73 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/lckdir.c @@ -0,0 +1,43 @@ +/* lckdir.c + Get the name of the UUCP lock directory. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_lckdir_rcsid[] = "$Id: lckdir.c,v 1.1 1993/08/04 19:34:39 jtc Exp $"; +#endif + +/* Get the name of the UUCP lock directory. */ + +int +uuconf_lockdir (pglobal, pzlock) + pointer pglobal; + const char **pzlock; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + + *pzlock = qglobal->qprocess->zlockdir; + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/lineno.c b/gnu/libexec/uucp/libuuconf/lineno.c new file mode 100644 index 0000000000..77839f6ede --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/lineno.c @@ -0,0 +1,44 @@ +/* lineno.c + Return the saved line number. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_lineno_rcsid[] = "$Id: lineno.c,v 1.1 1993/08/04 19:34:41 jtc Exp $"; +#endif + +/* Return the saved line number. */ + +int +uuconf_error_lineno (pglobal) + pointer pglobal; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + + if (qglobal == NULL) + return 0; + else + return qglobal->ilineno; +} diff --git a/gnu/libexec/uucp/libuuconf/llocnm.c b/gnu/libexec/uucp/libuuconf/llocnm.c new file mode 100644 index 0000000000..272d6237e9 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/llocnm.c @@ -0,0 +1,70 @@ +/* llocnm.c + Get the local name to use, given a login name. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_llocnm_rcsid[] = "$Id: llocnm.c,v 1.1 1993/08/04 19:34:43 jtc Exp $"; +#endif + +#include + +/* Get the local name to use, given a login name. */ + +int +uuconf_login_localname (pglobal, zlogin, pzname) + pointer pglobal; + const char *zlogin; + char **pzname; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + int iret; + +#if HAVE_TAYLOR_CONFIG + iret = uuconf_taylor_login_localname (pglobal, zlogin, pzname); + if (iret != UUCONF_NOT_FOUND) + return iret; +#endif + +#if HAVE_HDB_CONFIG + iret = uuconf_hdb_login_localname (pglobal, zlogin, pzname); + if (iret != UUCONF_NOT_FOUND) + return iret; +#endif + + if (qglobal->qprocess->zlocalname != NULL) + { + *pzname = strdup ((char *) qglobal->qprocess->zlocalname); + if (*pzname == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + return UUCONF_SUCCESS; + } + + *pzname = NULL; + return UUCONF_NOT_FOUND; +} diff --git a/gnu/libexec/uucp/libuuconf/local.c b/gnu/libexec/uucp/libuuconf/local.c new file mode 100644 index 0000000000..33adda40b0 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/local.c @@ -0,0 +1,70 @@ +/* local.c + Get default information for the local system. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_local_rcsid[] = "$Id: local.c,v 1.1 1993/08/04 19:34:45 jtc Exp $"; +#endif + +#include + +/* Get default information about the local system. */ + +int +uuconf_system_local (pglobal, qsys) + pointer pglobal; + struct uuconf_system *qsys; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + int iret; + + _uuconf_uclear_system (qsys); + qsys->uuconf_palloc = uuconf_malloc_block (); + if (qsys->uuconf_palloc == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + + qsys->uuconf_zname = (char *) qglobal->qprocess->zlocalname; + + /* By default, we permit the local system to forward to and from any + system. */ + iret = _uuconf_iadd_string (qglobal, (char *) "ANY", FALSE, FALSE, + &qsys->uuconf_pzforward_from, + qsys->uuconf_palloc); + if (iret == UUCONF_SUCCESS) + iret = _uuconf_iadd_string (qglobal, (char *) "ANY", FALSE, FALSE, + &qsys->uuconf_pzforward_to, + qsys->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + { + uuconf_free_block (qsys->uuconf_palloc); + return iret; + } + + return _uuconf_isystem_basic_default (qglobal, qsys); +} diff --git a/gnu/libexec/uucp/libuuconf/locnm.c b/gnu/libexec/uucp/libuuconf/locnm.c new file mode 100644 index 0000000000..1bc165b73f --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/locnm.c @@ -0,0 +1,46 @@ +/* locnm.c + Get the local node name. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_locnm_rcsid[] = "$Id: locnm.c,v 1.1 1993/08/04 19:34:47 jtc Exp $"; +#endif + +/* Get the local node name. */ + +int +uuconf_localname (pglobal, pzname) + pointer pglobal; + const char **pzname; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + + *pzname = qglobal->qprocess->zlocalname; + if (*pzname != NULL) + return UUCONF_SUCCESS; + else + return UUCONF_NOT_FOUND; +} diff --git a/gnu/libexec/uucp/libuuconf/logfil.c b/gnu/libexec/uucp/libuuconf/logfil.c new file mode 100644 index 0000000000..6d76764faa --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/logfil.c @@ -0,0 +1,43 @@ +/* logfil.c + Get the name of the UUCP log file. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_logfil_rcsid[] = "$Id: logfil.c,v 1.1 1993/08/04 19:34:48 jtc Exp $"; +#endif + +/* Get the name of the UUCP log file. */ + +int +uuconf_logfile (pglobal, pzlog) + pointer pglobal; + const char **pzlog; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + + *pzlog = qglobal->qprocess->zlogfile; + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/maxuxq.c b/gnu/libexec/uucp/libuuconf/maxuxq.c new file mode 100644 index 0000000000..8b5e9e7064 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/maxuxq.c @@ -0,0 +1,86 @@ +/* maxuxq.c + Get the maximum number of simultaneous uuxqt executions. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_maxuxq_rcsid[] = "$Id: maxuxq.c,v 1.1 1993/08/04 19:34:49 jtc Exp $"; +#endif + +/* Get the maximum number of simultaneous uuxqt executions. When + using TAYLOR_CONFIG, this is from the ``max-uuxqts'' command in + config. Otherwise, when using HDB_CONFIG, we read the file + Maxuuxqts. */ + +int +uuconf_maxuuxqts (pglobal, pcmax) + pointer pglobal; + int *pcmax; +{ +#if HAVE_TAYLOR_CONFIG + { + struct sglobal *qglobal = (struct sglobal *) pglobal; + + *pcmax = qglobal->qprocess->cmaxuuxqts; + return UUCONF_SUCCESS; + } +#else /* ! HAVE_TAYLOR_CONFIG */ +#if HAVE_HDB_CONFIG + { + char ab[sizeof OLDCONFIGLIB + sizeof HDB_MAXUUXQTS - 1]; + FILE *e; + + *pcmax = 0; + + memcpy ((pointer) ab, (constpointer) OLDCONFIGLIB, + sizeof OLDCONFIGLIB - 1); + memcpy ((pointer) (ab + sizeof OLDCONFIGLIB - 1), + (constpointer) HDB_MAXUUXQTS, sizeof HDB_MAXUUXQTS); + e = fopen (ab, "r"); + if (e != NULL) + { + char *z; + size_t c; + + z = NULL; + c = 0; + if (getline (&z, &c, e) > 0) + { + *pcmax = (int) strtol (z, (char **) NULL, 10); + if (*pcmax < 0) + *pcmax = 0; + free ((pointer) z); + } + (void) fclose (e); + } + + return UUCONF_SUCCESS; + } +#else /* ! HAVE_HDB_CONFIG */ + *pcmax = 0; + return UUCONF_SUCCESS; +#endif /* ! HAVE_HDB_CONFIG */ +#endif /* ! HAVE_TAYLOR_CONFIG */ +} diff --git a/gnu/libexec/uucp/libuuconf/mrgblk.c b/gnu/libexec/uucp/libuuconf/mrgblk.c new file mode 100644 index 0000000000..4b2ea1c574 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/mrgblk.c @@ -0,0 +1,50 @@ +/* mrgblk.c + Merge two memory blocks together. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_mrgblk_rcsid[] = "$Id: mrgblk.c,v 1.1 1993/08/04 19:34:50 jtc Exp $"; +#endif + +#include "alloc.h" + +/* Merge one memory block into another one, returning the combined + memory block. */ + +pointer +_uuconf_pmalloc_block_merge (p1, p2) + pointer p1; + pointer p2; +{ + struct sblock *q1 = (struct sblock *) p1; + struct sblock *q2 = (struct sblock *) p2; + struct sblock **pq; + + for (pq = &q1; *pq != NULL; pq = &(*pq)->qnext) + ; + *pq = q2; + return (pointer) q1; +} diff --git a/gnu/libexec/uucp/libuuconf/paramc.c b/gnu/libexec/uucp/libuuconf/paramc.c new file mode 100644 index 0000000000..bcc9a0be7b --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/paramc.c @@ -0,0 +1,175 @@ +/* paramc.c + Handle protocol-parameter commands. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_paramc_rcsid[] = "$Id: paramc.c,v 1.1 1993/08/04 19:34:51 jtc Exp $"; +#endif + +#include + +/* Handle protocol-parameter commands by inserting them into an array + of structures. The return value may include UUCONF_CMDTABRET_KEEP + and UUCONF_CMDTABRET_EXIT, if appropriate. */ + +int +_uuconf_iadd_proto_param (qglobal, argc, argv, pqparam, pblock) + struct sglobal *qglobal; + int argc; + char **argv; + struct uuconf_proto_param **pqparam; + pointer pblock; +{ + struct uuconf_proto_param *q; + size_t c; + struct uuconf_proto_param_entry *qentry; + + if (argc < 2) + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; + + /* The first argument is the protocol character. */ + if (argv[0][1] != '\0') + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; + + if (*pqparam == NULL) + { + *pqparam = ((struct uuconf_proto_param *) + uuconf_malloc (pblock, + 2 * sizeof (struct uuconf_proto_param))); + if (*pqparam == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + (*pqparam)[1].uuconf_bproto = '\0'; + q = *pqparam; + q->uuconf_bproto = argv[0][0]; + q->uuconf_qentries = NULL; + } + else + { + c = 0; + for (q = *pqparam; q->uuconf_bproto != '\0'; q++) + { + if (q->uuconf_bproto == argv[0][0]) + break; + ++c; + } + + if (q->uuconf_bproto == '\0') + { + struct uuconf_proto_param *qnew; + + qnew = ((struct uuconf_proto_param *) + uuconf_malloc (pblock, + ((c + 2) + * sizeof (struct uuconf_proto_param)))); + if (qnew == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + + memcpy ((pointer) qnew, (pointer) *pqparam, + c * sizeof (struct uuconf_proto_param)); + qnew[c + 1].uuconf_bproto = '\0'; + + uuconf_free (pblock, *pqparam); + *pqparam = qnew; + + q = qnew + c; + q->uuconf_bproto = argv[0][0]; + q->uuconf_qentries = NULL; + } + } + + if (q->uuconf_qentries == NULL) + { + qentry = ((struct uuconf_proto_param_entry *) + uuconf_malloc (pblock, + 2 * sizeof (struct uuconf_proto_param_entry))); + if (qentry == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + + qentry[1].uuconf_cargs = 0; + q->uuconf_qentries = qentry; + } + else + { + struct uuconf_proto_param_entry *qnewent; + + c = 0; + for (qentry = q->uuconf_qentries; qentry->uuconf_cargs != 0; qentry++) + ++c; + + qnewent = ((struct uuconf_proto_param_entry *) + uuconf_malloc (pblock, + ((c + 2) * + sizeof (struct uuconf_proto_param_entry)))); + if (qnewent == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + + memcpy ((pointer) qnewent, (pointer) q->uuconf_qentries, + c * sizeof (struct uuconf_proto_param_entry)); + qnewent[c + 1].uuconf_cargs = 0; + + uuconf_free (pblock, q->uuconf_qentries); + q->uuconf_qentries = qnewent; + + qentry = qnewent + c; + } + + qentry->uuconf_cargs = argc - 1; + qentry->uuconf_pzargs = (char **) uuconf_malloc (pblock, + ((argc - 1) + * sizeof (char *))); + if (qentry->uuconf_pzargs == NULL) + { + qglobal->ierrno = errno; + qentry->uuconf_cargs = 0; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + memcpy ((pointer) qentry->uuconf_pzargs, (pointer) (argv + 1), + (argc - 1) * sizeof (char *)); + + return UUCONF_CMDTABRET_KEEP; +} diff --git a/gnu/libexec/uucp/libuuconf/port.c b/gnu/libexec/uucp/libuuconf/port.c new file mode 100644 index 0000000000..586c226944 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/port.c @@ -0,0 +1,77 @@ +/* port.c + Find a port. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_port_rcsid[] = "$Id: port.c,v 1.1 1993/08/04 19:34:52 jtc Exp $"; +#endif + +/* Find a port by name, baud rate, and special purpose function. */ + +int +uuconf_find_port (pglobal, zname, ibaud, ihighbaud, pifn, pinfo, qport) + pointer pglobal; + const char *zname; + long ibaud; + long ihighbaud; + int (*pifn) P((struct uuconf_port *, pointer)); + pointer pinfo; + struct uuconf_port *qport; +{ +#if HAVE_V2_CONFIG || HAVE_HDB_CONFIG + struct sglobal *qglobal = (struct sglobal *) pglobal; +#endif + int iret; + +#if HAVE_TAYLOR_CONFIG + iret = uuconf_taylor_find_port (pglobal, zname, ibaud, ihighbaud, pifn, + pinfo, qport); + if (iret != UUCONF_NOT_FOUND) + return iret; +#endif + +#if HAVE_V2_CONFIG + if (qglobal->qprocess->fv2) + { + iret = uuconf_v2_find_port (pglobal, zname, ibaud, ihighbaud, pifn, + pinfo, qport); + if (iret != UUCONF_NOT_FOUND) + return iret; + } +#endif + +#if HAVE_HDB_CONFIG + if (qglobal->qprocess->fhdb) + { + iret = uuconf_hdb_find_port (pglobal, zname, ibaud, ihighbaud, pifn, + pinfo, qport); + if (iret != UUCONF_NOT_FOUND) + return iret; + } +#endif + + return UUCONF_NOT_FOUND; +} diff --git a/gnu/libexec/uucp/libuuconf/prtsub.c b/gnu/libexec/uucp/libuuconf/prtsub.c new file mode 100644 index 0000000000..682f38e687 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/prtsub.c @@ -0,0 +1,54 @@ +/* prtsub.c + Port information subroutines. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_prtsub_rcsid[] = "$Id: prtsub.c,v 1.1 1993/08/04 19:34:53 jtc Exp $"; +#endif + +/* Clear the information in a port. This can only clear the type + independent information; the port type specific information is + cleared when the type of the port is set. */ + +void +_uuconf_uclear_port (qport) + struct uuconf_port *qport; +{ + qport->uuconf_zname = NULL; + qport->uuconf_ttype = UUCONF_PORTTYPE_UNKNOWN; + qport->uuconf_zprotocols = NULL; + qport->uuconf_qproto_params = NULL; + + /* Note that we do not set RELIABLE_SPECIFIED; this just sets + defaults, so that ``seven-bit true'' does not imply ``reliable + false''. */ + qport->uuconf_ireliable = (UUCONF_RELIABLE_RELIABLE + | UUCONF_RELIABLE_EIGHT + | UUCONF_RELIABLE_FULLDUPLEX); + + qport->uuconf_zlockname = NULL; + qport->uuconf_palloc = NULL; +} diff --git a/gnu/libexec/uucp/libuuconf/pubdir.c b/gnu/libexec/uucp/libuuconf/pubdir.c new file mode 100644 index 0000000000..0c4714d466 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/pubdir.c @@ -0,0 +1,43 @@ +/* pubdir.c + Get the name of the UUCP public directory. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_pubdir_rcsid[] = "$Id: pubdir.c,v 1.1 1993/08/04 19:34:54 jtc Exp $"; +#endif + +/* Get the name of the UUCP public directory. */ + +int +uuconf_pubdir (pglobal, pzpub) + pointer pglobal; + const char **pzpub; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + + *pzpub = qglobal->qprocess->zpubdir; + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/rdlocs.c b/gnu/libexec/uucp/libuuconf/rdlocs.c new file mode 100644 index 0000000000..ba8365fd7b --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/rdlocs.c @@ -0,0 +1,305 @@ +/* rdlocs.c + Get the locations of systems in the Taylor UUCP configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_rdlocs_rcsid[] = "$Id: rdlocs.c,v 1.1 1993/08/04 19:34:55 jtc Exp $"; +#endif + +#include + +static int itsystem P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); +static int itcalled_login P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); +static int itmyname P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); + +/* This code scans through the Taylor UUCP system files in order to + locate each system and to gather the login restrictions (since this + information is held in additional arguments to the "called-login" + command, it can appear anywhere in the systems files). It also + records whether any "myname" appears, as an optimization for + uuconf_taylor_localname. + + This table is used to dispatch the appropriate commands. Most + commands are simply ignored. Note that this is a uuconf_cmdtab, + not a cmdtab_offset. */ + +static const struct uuconf_cmdtab asTcmds[] = +{ + { "system", UUCONF_CMDTABTYPE_FN | 2, NULL, itsystem }, + { "alias", UUCONF_CMDTABTYPE_FN | 2, (pointer) asTcmds, itsystem }, + { "called-login", UUCONF_CMDTABTYPE_FN | 0, NULL, itcalled_login }, + { "myname", UUCONF_CMDTABTYPE_FN | 2, NULL, itmyname }, + { NULL, 0, NULL, NULL } +}; + +/* This structure is used to pass information into the command table + functions. */ + +struct sinfo +{ + /* The sys file name. */ + const char *zname; + /* The open sys file. */ + FILE *e; + /* The list of locations we are building. */ + struct stsysloc *qlocs; + /* The list of validation restrictions we are building. */ + struct svalidate *qvals; +}; + +/* Look through the sys files to find the location and names of all + the systems. Since we're scanning the sys files, we also record + the validation information specified by the additional arguments to + the called-login command. We don't use uuconf_cmd_file to avoid + the overhead of breaking the line up into arguments if not + necessary. */ + +int +_uuconf_iread_locations (qglobal) + struct sglobal *qglobal; +{ + char *zline; + size_t cline; + struct sinfo si; + int iret; + char **pz; + + if (qglobal->qprocess->fread_syslocs) + return UUCONF_SUCCESS; + + zline = NULL; + cline = 0; + + si.qlocs = NULL; + si.qvals = NULL; + + iret = UUCONF_SUCCESS; + + for (pz = qglobal->qprocess->pzsysfiles; *pz != NULL; pz++) + { + FILE *e; + int cchars; + + qglobal->ilineno = 0; + + e = fopen (*pz, "r"); + if (e == NULL) + { + if (FNO_SUCH_FILE ()) + continue; + qglobal->ierrno = errno; + iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; + break; + } + +#ifdef CLOSE_ON_EXEC + CLOSE_ON_EXEC (e); +#endif + + si.zname = *pz; + si.e = e; + + while ((cchars = _uuconf_getline (qglobal, &zline, &cline, e)) > 0) + { + char *zcmd; + + ++qglobal->ilineno; + + zcmd = zline + strspn (zline, " \t"); + if (strncasecmp (zcmd, "system", sizeof "system" - 1) == 0 + || strncasecmp (zcmd, "alias", sizeof "alias" - 1) == 0 + || strncasecmp (zcmd, "called-login", + sizeof "called-login" - 1) == 0 + || strncasecmp (zcmd, "myname", sizeof "myname" - 1) == 0) + { + iret = uuconf_cmd_line ((pointer) qglobal, zline, asTcmds, + (pointer) &si, (uuconf_cmdtabfn) NULL, + 0, qglobal->pblock); + if ((iret & UUCONF_CMDTABRET_KEEP) != 0) + { + iret &=~ UUCONF_CMDTABRET_KEEP; + zline = NULL; + cline = 0; + } + if (iret != UUCONF_SUCCESS) + { + iret &=~ UUCONF_CMDTABRET_EXIT; + break; + } + } + } + + if (iret != UUCONF_SUCCESS) + break; + } + + if (zline != NULL) + free ((pointer) zline); + + if (iret != UUCONF_SUCCESS) + { + qglobal->zfilename = *pz; + iret |= UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; + if (UUCONF_ERROR_VALUE (iret) != UUCONF_MALLOC_FAILED) + qglobal->qprocess->fread_syslocs = TRUE; + } + else + { + qglobal->qprocess->qsyslocs = si.qlocs; + qglobal->qprocess->qvalidate = si.qvals; + qglobal->qprocess->fread_syslocs = TRUE; + } + + return iret; +} + +/* Handle a "system" or "alias" command by recording the file and + location. If pvar is not NULL, this is an "alias" command. */ + +/*ARGSUSED*/ +static int +itsystem (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct sinfo *qinfo = (struct sinfo *) pinfo; + struct stsysloc *q; + size_t csize; + + q = (struct stsysloc *) uuconf_malloc (qglobal->pblock, + sizeof (struct stsysloc)); + if (q == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + + csize = strlen (argv[1]) + 1; + q->zname = uuconf_malloc (qglobal->pblock, csize); + if (q->zname == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + + q->qnext = qinfo->qlocs; + memcpy ((pointer) q->zname, (pointer) argv[1], csize); + q->falias = pvar != NULL; + q->zfile = qinfo->zname; + q->e = qinfo->e; + q->iloc = ftell (qinfo->e); + q->ilineno = qglobal->ilineno; + + qinfo->qlocs = q; + + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Handle the "called-login" command. This just records any extra + arguments, so that uuconf_validate can check them later if + necessary. */ + +/*ARGSUSED*/ +static int +itcalled_login (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct sinfo *qinfo = (struct sinfo *) pinfo; + register struct svalidate *qval; + int i; + + if (argc <= 2) + return UUCONF_CMDTABRET_CONTINUE; + + for (qval = qinfo->qvals; qval != NULL; qval = qval->qnext) + if (strcmp (argv[1], qval->zlogname) == 0) + break; + + if (qval == NULL) + { + qval = (struct svalidate *) uuconf_malloc (qglobal->pblock, + sizeof (struct svalidate)); + if (qval == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + + qval->qnext = qinfo->qvals; + qval->zlogname = argv[1]; + qval->pzmachines = NULL; + + qinfo->qvals = qval; + } + + for (i = 2; i < argc; i++) + { + int iret; + + iret = _uuconf_iadd_string (qglobal, argv[i], FALSE, TRUE, + &qval->pzmachines, qglobal->pblock); + if (iret != UUCONF_SUCCESS) + return iret | UUCONF_CMDTABRET_EXIT; + } + + return UUCONF_CMDTABRET_KEEP; +} + +/* Handle the "myname" command by simply recording that it appears. + This information is used by uuconf_taylor_localname. */ + +/*ARGSUSED*/ +static int +itmyname (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + + qglobal->qprocess->fuses_myname = TRUE; + return UUCONF_CMDTABRET_CONTINUE; +} diff --git a/gnu/libexec/uucp/libuuconf/rdperm.c b/gnu/libexec/uucp/libuuconf/rdperm.c new file mode 100644 index 0000000000..dd481c1a8e --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/rdperm.c @@ -0,0 +1,446 @@ +/* rdperm.c + Read the HDB Permissions file. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_rdperm_rcsid[] = "$Id: rdperm.c,v 1.1 1993/08/04 19:34:57 jtc Exp $"; +#endif + +#include +#include + +static int ihcolon P((pointer pglobal, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int ihsendfiles P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); +static int ihunknownperm P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); +static int ihadd_norw P((struct sglobal *qglobal, char ***ppz, char **pzno)); + +/* These routines reads in the HDB Permissions file. We store the + entries in a linked list of shpermissions structures, so we only + have to actually read the file once. */ + +/* This command table and static structure are used to parse a line + from Permissions. The entries are parsed as follows: + + Multiple strings separated by colons: LOGNAME, MACHINE, READ, + WRITE, NOREAD, NOWRITE, COMMANDS, VALIDATE, ALIAS. + + Boolean values: REQUEST, CALLBACK. + + Simple strings: MYNAME, PUBDIR. + + "Yes" or "call": SENDFILES. + + The NOREAD and NOWRITE entries are merged into the READ and WRITE + entries, rather than being permanently stored. They are handled + specially in the uuconf_cmdtab table. */ + +static const struct cmdtab_offset asHperm_cmds[] = +{ + { "NOREAD", UUCONF_CMDTABTYPE_FN | 2, (size_t) -1, ihcolon }, + { "NOWRITE", UUCONF_CMDTABTYPE_FN | 2, (size_t) -1, ihcolon }, + { "LOGNAME", UUCONF_CMDTABTYPE_FN | 2, + offsetof (struct shpermissions, pzlogname), ihcolon }, + { "MACHINE", UUCONF_CMDTABTYPE_FN | 2, + offsetof (struct shpermissions, pzmachine), ihcolon }, + { "REQUEST", UUCONF_CMDTABTYPE_BOOLEAN, + offsetof (struct shpermissions, frequest), NULL }, + { "SENDFILES", UUCONF_CMDTABTYPE_FN | 2, + offsetof (struct shpermissions, fsendfiles), ihsendfiles }, + { "READ", UUCONF_CMDTABTYPE_FN | 2, + offsetof (struct shpermissions, pzread), ihcolon }, + { "WRITE", UUCONF_CMDTABTYPE_FN | 2, + offsetof (struct shpermissions, pzwrite), ihcolon }, + { "CALLBACK", UUCONF_CMDTABTYPE_BOOLEAN, + offsetof (struct shpermissions, fcallback), NULL }, + { "COMMANDS", UUCONF_CMDTABTYPE_FN | 2, + offsetof (struct shpermissions, pzcommands), ihcolon }, + { "VALIDATE", UUCONF_CMDTABTYPE_FN | 2, + offsetof (struct shpermissions, pzvalidate), ihcolon }, + { "MYNAME", UUCONF_CMDTABTYPE_STRING, + offsetof (struct shpermissions, zmyname), NULL }, + { "PUBDIR", UUCONF_CMDTABTYPE_STRING, + offsetof (struct shpermissions, zpubdir), NULL }, + { "ALIAS", UUCONF_CMDTABTYPE_FN | 2, + offsetof (struct shpermissions, pzalias), ihcolon }, + { NULL, 0, 0, NULL } +}; + +#define CHPERM_CMDS (sizeof asHperm_cmds / sizeof asHperm_cmds[0]) + +/* Actually read the Permissions file into a linked list of + structures. */ + +int +_uuconf_ihread_permissions (qglobal) + struct sglobal *qglobal; +{ + char *zperm; + FILE *e; + int iret; + struct uuconf_cmdtab as[CHPERM_CMDS]; + char **pznoread, **pznowrite; + struct shpermissions shperm; + char *zline; + size_t cline; + char **pzsplit; + size_t csplit; + int cchars; + struct shpermissions *qlist, **pq; + + if (qglobal->qprocess->fhdb_read_permissions) + return UUCONF_SUCCESS; + + zperm = (char *) uuconf_malloc (qglobal->pblock, + (sizeof OLDCONFIGLIB + + sizeof HDB_PERMISSIONS - 1)); + if (zperm == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + + memcpy ((pointer) zperm, (pointer) OLDCONFIGLIB, + sizeof OLDCONFIGLIB - 1); + memcpy ((pointer) (zperm + sizeof OLDCONFIGLIB - 1), + (pointer) HDB_PERMISSIONS, sizeof HDB_PERMISSIONS); + + e = fopen (zperm, "r"); + if (e == NULL) + { + uuconf_free (qglobal->pblock, zperm); + qglobal->qprocess->fhdb_read_permissions = TRUE; + return UUCONF_SUCCESS; + } + + _uuconf_ucmdtab_base (asHperm_cmds, CHPERM_CMDS, (char *) &shperm, as); + as[0].uuconf_pvar = (pointer) &pznoread; + as[1].uuconf_pvar = (pointer) &pznowrite; + + zline = NULL; + cline = 0; + pzsplit = NULL; + csplit = 0; + + qlist = NULL; + pq = &qlist; + + qglobal->ilineno = 0; + + iret = UUCONF_SUCCESS; + + while ((cchars = _uuconf_getline (qglobal, &zline, &cline, e)) > 0) + { + int centries; + struct shpermissions *qnew; + int i; + + ++qglobal->ilineno; + + --cchars; + if (zline[cchars] == '\n') + zline[cchars] = '\0'; + if (isspace (BUCHAR (zline[0])) || zline[0] == '#') + continue; + + centries = _uuconf_istrsplit (zline, '\0', &pzsplit, &csplit); + if (centries < 0) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + if (centries == 0) + continue; + + shperm.pzlogname = (char **) &_uuconf_unset; + shperm.pzmachine = (char **) &_uuconf_unset; + shperm.frequest = -1; + shperm.fsendfiles = -1; + shperm.pzread = (char **) &_uuconf_unset; + shperm.pzwrite = (char **) &_uuconf_unset; + shperm.fcallback = -1; + shperm.pzcommands = (char **) &_uuconf_unset; + shperm.pzvalidate = (char **) &_uuconf_unset; + shperm.zmyname = (char *) &_uuconf_unset; + shperm.zpubdir = (char *) &_uuconf_unset; + shperm.pzalias = (char **) &_uuconf_unset; + pznoread = (char **) &_uuconf_unset; + pznowrite = (char **) &_uuconf_unset; + + for (i = 0; i < centries; i++) + { + char *zeq; + char *azargs[2]; + + zeq = strchr (pzsplit[i], '='); + if (zeq == NULL) + { + iret = UUCONF_SYNTAX_ERROR; + qglobal->qprocess->fhdb_read_permissions = TRUE; + break; + } + *zeq = '\0'; + + azargs[0] = pzsplit[i]; + azargs[1] = zeq + 1; + + iret = uuconf_cmd_args (qglobal, 2, azargs, as, (pointer) NULL, + ihunknownperm, 0, qglobal->pblock); + if ((iret & UUCONF_CMDTABRET_KEEP) != 0) + { + iret &=~ UUCONF_CMDTABRET_KEEP; + + if (uuconf_add_block (qglobal->pblock, zline) != 0) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + zline = NULL; + cline = 0; + } + if ((iret & UUCONF_CMDTABRET_EXIT) != 0) + { + iret &=~ UUCONF_CMDTABRET_EXIT; + break; + } + } + + if (iret != UUCONF_SUCCESS) + break; + + if (shperm.pzmachine == (char **) &_uuconf_unset + && shperm.pzlogname == (char **) &_uuconf_unset) + { + iret = UUCONF_SYNTAX_ERROR; + qglobal->qprocess->fhdb_read_permissions = TRUE; + break; + } + + /* Attach any NOREAD or NOWRITE entries to the corresponding + READ or WRITE entries in the format expected for the + pzlocal_receive, etc., fields in uuconf_system. */ + if (pznoread != NULL) + { + iret = ihadd_norw (qglobal, &shperm.pzread, pznoread); + if (iret != UUCONF_SUCCESS) + break; + uuconf_free (qglobal->pblock, pznoread); + } + + if (pznowrite != NULL) + { + iret = ihadd_norw (qglobal, &shperm.pzwrite, pznowrite); + if (iret != UUCONF_SUCCESS) + break; + uuconf_free (qglobal->pblock, pznowrite); + } + + qnew = ((struct shpermissions *) + uuconf_malloc (qglobal->pblock, + sizeof (struct shpermissions))); + if (qnew == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + *qnew = shperm; + *pq = qnew; + pq = &qnew->qnext; + *pq = NULL; + } + + (void) fclose (e); + + if (zline != NULL) + free ((pointer) zline); + if (pzsplit != NULL) + free ((pointer) pzsplit); + + if (iret == UUCONF_SUCCESS) + { + qglobal->qprocess->qhdb_permissions = qlist; + qglobal->qprocess->fhdb_read_permissions = TRUE; + } + else + { + qglobal->zfilename = zperm; + iret |= UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; + } + + return iret; +} + +/* Split the argument into colon separated strings, and assign a NULL + terminated array of strings to pvar. */ + +/*ARGSUSED*/ +static int +ihcolon (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + char ***ppz = (char ***) pvar; + char **pzsplit; + size_t csplit; + int centries; + int i; + int iret; + + *ppz = NULL; + + pzsplit = NULL; + csplit = 0; + + centries = _uuconf_istrsplit (argv[1], ':', &pzsplit, &csplit); + if (centries < 0) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + + if (centries == 0) + { + if (pzsplit != NULL) + free ((pointer) pzsplit); + return UUCONF_CMDTABRET_CONTINUE; + } + + iret = UUCONF_SUCCESS; + + for (i = 0; i < centries; i++) + { + iret = _uuconf_iadd_string (qglobal, pzsplit[i], FALSE, FALSE, + ppz, qglobal->pblock); + if (iret != UUCONF_SUCCESS) + { + iret |= UUCONF_CMDTABRET_EXIT; + break; + } + } + + free ((pointer) pzsplit); + + return UUCONF_CMDTABRET_KEEP; +} + +/* Handle the SENDFILES parameter, which can take "yes" or "call" or + "no" as an argument. The string "call" is equivalent to "no". */ + +/*ARGSUSED*/ +static int +ihsendfiles (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + int *pi = (int *) pvar; + + switch (argv[1][0]) + { + case 'C': + case 'c': + case 'N': + case 'n': + *pi = FALSE; + break; + case 'Y': + case 'y': + *pi = TRUE; + break; + default: + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; + } + + return UUCONF_CMDTABRET_CONTINUE; +} + +/* If there is an unknown Permissions entry, return a syntax error. + This should probably be more clever. */ + +/*ARGSUSED*/ +static int +ihunknownperm (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; +} + +/* Add a NOREAD or NOWRITE entry to a READ or WRITE entry. */ + +static int +ihadd_norw (qglobal, ppz, pzno) + struct sglobal *qglobal; + char ***ppz; + char **pzno; +{ + register char **pz; + + if (pzno == (char **) &_uuconf_unset) + return UUCONF_SUCCESS; + + for (pz = pzno; *pz != NULL; pz++) + { + size_t csize; + char *znew; + int iret; + + csize = strlen (*pz) + 1; + znew = (char *) uuconf_malloc (qglobal->pblock, csize + 1); + if (znew == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + znew[0] = '!'; + memcpy ((pointer) (znew + 1), (pointer) *pz, csize); + iret = _uuconf_iadd_string (qglobal, znew, FALSE, FALSE, ppz, + qglobal->pblock); + if (iret != UUCONF_SUCCESS) + return iret; + } + + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/reliab.c b/gnu/libexec/uucp/libuuconf/reliab.c new file mode 100644 index 0000000000..13517d49f3 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/reliab.c @@ -0,0 +1,123 @@ +/* reliab.c + Subroutines to handle reliability commands for ports and dialers. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_reliab_rcsid[] = "$Id: reliab.c,v 1.1 1993/08/04 19:34:58 jtc Exp $"; +#endif + +/* Handle the "seven-bit" command for a port or a dialer. The pvar + argument points to an integer which should be set to hold + reliability information. */ + +/*ARGSUSED*/ +int +_uuconf_iseven_bit (pglobal,argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + int *pi = (int *) pvar; + int fval; + int iret; + + iret = _uuconf_iboolean (qglobal, argv[1], &fval); + if ((iret &~ UUCONF_CMDTABRET_KEEP) != UUCONF_SUCCESS) + return iret; + + *pi |= UUCONF_RELIABLE_SPECIFIED; + if (fval) + *pi &=~ UUCONF_RELIABLE_EIGHT; + else + *pi |= UUCONF_RELIABLE_EIGHT; + + return iret; +} + +/* Handle the "reliable" command for a port or a dialer. The pvar + argument points to an integer which should be set to hold + reliability information. */ + +/*ARGSUSED*/ +int +_uuconf_ireliable (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + int *pi = (int *) pvar; + int fval; + int iret; + + iret = _uuconf_iboolean (qglobal, argv[1], &fval); + if ((iret &~ UUCONF_CMDTABRET_KEEP) != UUCONF_SUCCESS) + return iret; + + *pi |= UUCONF_RELIABLE_SPECIFIED; + if (fval) + *pi |= UUCONF_RELIABLE_RELIABLE; + else + *pi &=~ UUCONF_RELIABLE_RELIABLE; + + return iret; +} + +/* Handle the "half-duplex" command for a port or a dialer. The pvar + argument points to an integer which should be set to hold + reliability information. */ + +/*ARGSUSED*/ +int +_uuconf_ihalf_duplex (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + int *pi = (int *) pvar; + int fval; + int iret; + + iret = _uuconf_iboolean (qglobal, argv[1], &fval); + if ((iret &~ UUCONF_CMDTABRET_KEEP) != UUCONF_SUCCESS) + return iret; + + *pi |= UUCONF_RELIABLE_SPECIFIED; + if (fval) + *pi &=~ UUCONF_RELIABLE_FULLDUPLEX; + else + *pi |= UUCONF_RELIABLE_FULLDUPLEX; + + return iret; +} diff --git a/gnu/libexec/uucp/libuuconf/remunk.c b/gnu/libexec/uucp/libuuconf/remunk.c new file mode 100644 index 0000000000..85297b0212 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/remunk.c @@ -0,0 +1,45 @@ +/* remunk.c + Get the name of the remote.unknown shell script. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_remunk_rcsid[] = "$Id: remunk.c,v 1.1 1993/08/04 19:34:59 jtc Exp $"; +#endif + +/* Get the name of the remote.unknown shell script. */ + +/*ARGSUSED*/ +int +uuconf_remote_unknown (pglobal, pzname) + pointer pglobal; + char **pzname; +{ +#if HAVE_TAYLOR_CONFIG || ! HAVE_HDB_CONFIG + return UUCONF_NOT_FOUND; +#else + return uuconf_hdb_remote_unknown (pglobal, pzname); +#endif +} diff --git a/gnu/libexec/uucp/libuuconf/sinfo.c b/gnu/libexec/uucp/libuuconf/sinfo.c new file mode 100644 index 0000000000..f73851f84a --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/sinfo.c @@ -0,0 +1,112 @@ +/* sinfo.c + Get information about a system. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_sinfo_rcsid[] = "$Id: sinfo.c,v 1.1 1993/08/04 19:35:00 jtc Exp $"; +#endif + +/* Get information about a particular system. We combine the + definitions for this system from each type of configuration file, + by passing what we have so far into each one. */ + +int +uuconf_system_info (pglobal, zsystem, qsys) + pointer pglobal; + const char *zsystem; + struct uuconf_system *qsys; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + int iret; + boolean fgot; + + fgot = FALSE; + +#if HAVE_TAYLOR_CONFIG + iret = _uuconf_itaylor_system_internal (qglobal, zsystem, qsys); + if (iret == UUCONF_SUCCESS) + fgot = TRUE; + else if (iret != UUCONF_NOT_FOUND) + return iret; +#endif + +#if HAVE_V2_CONFIG + if (qglobal->qprocess->fv2) + { + struct uuconf_system *q; + struct uuconf_system sv2; + + if (fgot) + q = &sv2; + else + q = qsys; + iret = _uuconf_iv2_system_internal (qglobal, zsystem, q); + if (iret == UUCONF_SUCCESS) + { + if (fgot) + { + iret = _uuconf_isystem_default (qglobal, qsys, &sv2, TRUE); + if (iret != UUCONF_SUCCESS) + return iret; + } + fgot = TRUE; + } + else if (iret != UUCONF_NOT_FOUND) + return iret; + } +#endif + +#if HAVE_HDB_CONFIG + if (qglobal->qprocess->fhdb) + { + struct uuconf_system *q; + struct uuconf_system shdb; + + if (fgot) + q = &shdb; + else + q = qsys; + iret = _uuconf_ihdb_system_internal (qglobal, zsystem, q); + if (iret == UUCONF_SUCCESS) + { + if (fgot) + { + iret = _uuconf_isystem_default (qglobal, qsys, &shdb, TRUE); + if (iret != UUCONF_SUCCESS) + return iret; + } + fgot = TRUE; + } + else if (iret != UUCONF_NOT_FOUND) + return iret; + } +#endif + + if (! fgot) + return UUCONF_NOT_FOUND; + + return _uuconf_isystem_basic_default (qglobal, qsys); +} diff --git a/gnu/libexec/uucp/libuuconf/snams.c b/gnu/libexec/uucp/libuuconf/snams.c new file mode 100644 index 0000000000..d239b10e08 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/snams.c @@ -0,0 +1,133 @@ +/* snams.c + Get all known system names. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_snams_rcsid[] = "$Id: snams.c,v 1.1 1993/08/04 19:35:01 jtc Exp $"; +#endif + +/* Get all known system names. */ + +int +uuconf_system_names (pglobal, ppzsystems, falias) + pointer pglobal; + char ***ppzsystems; + int falias; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + char **pztaylor; + char **pzv2; + char **pzhdb; + int iret; + + *ppzsystems = NULL; + pztaylor = NULL; + pzv2 = NULL; + pzhdb = NULL; + +#if HAVE_TAYLOR_CONFIG + iret = uuconf_taylor_system_names (pglobal, &pztaylor, falias); + if (iret != UUCONF_SUCCESS) + return iret; +#endif + +#if HAVE_V2_CONFIG + if (qglobal->qprocess->fv2) + { + iret = uuconf_v2_system_names (pglobal, &pzv2, falias); + if (iret != UUCONF_SUCCESS) + return iret; + } +#endif + +#if HAVE_HDB_CONFIG + if (qglobal->qprocess->fhdb) + { + iret = uuconf_hdb_system_names (pglobal, &pzhdb, falias); + if (iret != UUCONF_SUCCESS) + return iret; + } +#endif + + if (pzv2 == NULL && pzhdb == NULL) + *ppzsystems = pztaylor; + else if (pztaylor == NULL && pzhdb == NULL) + *ppzsystems = pzv2; + else if (pztaylor == NULL && pzv2 == NULL) + *ppzsystems = pzhdb; + else + { + char **pz; + + iret = UUCONF_SUCCESS; + + if (pztaylor != NULL) + { + for (pz = pztaylor; *pz != NULL; pz++) + { + iret = _uuconf_iadd_string (qglobal, *pz, FALSE, TRUE, + ppzsystems, (pointer) NULL); + if (iret != UUCONF_SUCCESS) + break; + } + } + + if (pzv2 != NULL && iret == UUCONF_SUCCESS) + { + for (pz = pzv2; *pz != NULL; pz++) + { + iret = _uuconf_iadd_string (qglobal, *pz, FALSE, TRUE, + ppzsystems, (pointer) NULL); + if (iret != UUCONF_SUCCESS) + break; + } + } + + if (pzhdb != NULL && iret == UUCONF_SUCCESS) + { + for (pz = pzhdb; *pz != NULL; pz++) + { + iret = _uuconf_iadd_string (qglobal, *pz, FALSE, TRUE, + ppzsystems, (pointer) NULL); + if (iret != UUCONF_SUCCESS) + break; + } + } + + if (pztaylor != NULL) + free ((pointer) pztaylor); + if (pzv2 != NULL) + free ((pointer) pzv2); + if (pzhdb != NULL) + free ((pointer) pzhdb); + } + + if (iret == UUCONF_SUCCESS && *ppzsystems == NULL) + iret = _uuconf_iadd_string (qglobal, (char *) NULL, FALSE, FALSE, + ppzsystems, (pointer) NULL); + + return iret; +} diff --git a/gnu/libexec/uucp/libuuconf/split.c b/gnu/libexec/uucp/libuuconf/split.c new file mode 100644 index 0000000000..76cc5e8d36 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/split.c @@ -0,0 +1,106 @@ +/* split.c + Split a string into tokens. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_split_rcsid[] = "$Id: split.c,v 1.1 1993/08/04 19:35:02 jtc Exp $"; +#endif + +#include + +/* Split a string into tokens. The bsep argument is the separator to + use. If it is the null byte, white space is used as the separator, + and leading white space is discarded. Otherwise, each occurrence + of the separator character delimits a field (and thus some fields + may be empty). The array and size arguments may be used to reuse + the same memory. This function is not tied to uuconf; the only way + it can fail is if malloc or realloc fails. */ + +int +_uuconf_istrsplit (zline, bsep, ppzsplit, pcsplit) + register char *zline; + int bsep; + char ***ppzsplit; + size_t *pcsplit; +{ + size_t i; + + i = 0; + + while (TRUE) + { + if (bsep == '\0') + { + while (isspace (BUCHAR (*zline))) + ++zline; + if (*zline == '\0') + break; + } + + if (i >= *pcsplit) + { + char **pznew; + size_t cnew; + + if (*pcsplit == 0) + { + cnew = 8; + pznew = (char **) malloc (cnew * sizeof (char *)); + } + else + { + cnew = *pcsplit * 2; + pznew = (char **) realloc ((pointer) *ppzsplit, + cnew * sizeof (char *)); + } + if (pznew == NULL) + return -1; + *ppzsplit = pznew; + *pcsplit = cnew; + } + + (*ppzsplit)[i] = zline; + ++i; + + if (bsep == '\0') + { + while (*zline != '\0' && ! isspace (BUCHAR (*zline))) + ++zline; + } + else + { + while (*zline != '\0' && *zline != bsep) + ++zline; + } + + if (*zline == '\0') + break; + + *zline++ = '\0'; + } + + return i; +} diff --git a/gnu/libexec/uucp/libuuconf/spool.c b/gnu/libexec/uucp/libuuconf/spool.c new file mode 100644 index 0000000000..e9c13f072c --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/spool.c @@ -0,0 +1,43 @@ +/* spool.c + Get the name of the UUCP spool directory. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_spool_rcsid[] = "$Id: spool.c,v 1.1 1993/08/04 19:35:03 jtc Exp $"; +#endif + +/* Get the name of the UUCP spool directory. */ + +int +uuconf_spooldir (pglobal, pzspool) + pointer pglobal; + const char **pzspool; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + + *pzspool = qglobal->qprocess->zspooldir; + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/stafil.c b/gnu/libexec/uucp/libuuconf/stafil.c new file mode 100644 index 0000000000..bcea0bffd6 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/stafil.c @@ -0,0 +1,43 @@ +/* stafil.c + Get the name of the UUCP statistics file. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_stafil_rcsid[] = "$Id: stafil.c,v 1.1 1993/08/04 19:35:04 jtc Exp $"; +#endif + +/* Get the name of the UUCP statistics file. */ + +int +uuconf_statsfile (pglobal, pzstats) + pointer pglobal; + const char **pzstats; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + + *pzstats = qglobal->qprocess->zstatsfile; + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/syshdr.h b/gnu/libexec/uucp/libuuconf/syshdr.h new file mode 100644 index 0000000000..8ff18a6ca4 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/syshdr.h @@ -0,0 +1,106 @@ +/* syshdr.unx -*- C -*- + Unix system header for the uuconf library. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +/* The root directory (used when setting local-send and local-receive + values). */ +#define ZROOTDIR "/" + +/* The current directory (used by uuconv as a prefix for the newly + created file names). */ +#define ZCURDIR "." + +/* The names of the Taylor UUCP configuration files. These are + appended to NEWCONFIGLIB which is defined in Makefile. */ +#define CONFIGFILE "/config" +#define SYSFILE "/sys" +#define PORTFILE "/port" +#define DIALFILE "/dial" +#define CALLFILE "/call" +#define PASSWDFILE "/passwd" +#define DIALCODEFILE "/dialcode" + +/* The names of the various V2 configuration files. These are + appended to OLDCONFIGLIB which is defined in Makefile. */ +#define V2_SYSTEMS "/L.sys" +#define V2_DEVICES "/L-devices" +#define V2_USERFILE "/USERFILE" +#define V2_CMDS "/L.cmds" +#define V2_DIALCODES "/L-dialcodes" + +/* The names of the HDB configuration files. These are appended to + OLDCONFIGLIB which is defined in Makefile. */ +#define HDB_SYSFILES "/Sysfiles" +#define HDB_SYSTEMS "/Systems" +#define HDB_PERMISSIONS "/Permissions" +#define HDB_DEVICES "/Devices" +#define HDB_DIALERS "/Dialers" +#define HDB_DIALCODES "/Dialcodes" +#define HDB_MAXUUXQTS "/Maxuuxqts" +#define HDB_REMOTE_UNKNOWN "/remote.unknown" + +/* A string which is inserted between the value of OLDCONFIGLIB + (defined in the Makefile) and any names specified in the HDB + Sysfiles file. */ +#define HDB_SEPARATOR "/" + +/* A macro to check whether fopen failed because the file did not + exist. */ +#define FNO_SUCH_FILE() (errno == ENOENT) + +#if ! HAVE_STRERROR + +/* We need a definition for strerror; normally the function in the + unix directory is used, but we want to be independent of that + library. This macro evaluates its argument multiple times. */ +extern int sys_nerr; +extern char *sys_errlist[]; + +#define strerror(ierr) \ + ((ierr) >= 0 && (ierr) < sys_nerr ? sys_errlist[ierr] : "unknown error") + +#endif /* ! HAVE_STRERROR */ + +/* We want to be able to mark the Taylor UUCP system files as close on + exec. */ +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +#define CLOSE_ON_EXEC(e) \ + do \ + { \ + int cle_i = fileno (e); \ + \ + fcntl (cle_i, F_SETFD, fcntl (cle_i, F_GETFD, 0) | FD_CLOEXEC); \ + } \ + while (0) diff --git a/gnu/libexec/uucp/libuuconf/syssub.c b/gnu/libexec/uucp/libuuconf/syssub.c new file mode 100644 index 0000000000..f9b52c13fe --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/syssub.c @@ -0,0 +1,458 @@ +/* syssub.c + System information subroutines. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_syssub_rcsid[] = "$Id: syssub.c,v 1.1 1993/08/04 19:35:06 jtc Exp $"; +#endif + +#include + +/* This macro operates on every string (char *) field in struct + uuconf_system. */ +#define SYSTEM_STRINGS(OP) \ + do \ + { \ + OP (uuconf_zname); \ + OP (uuconf_zalternate); \ + OP (uuconf_zdebug); \ + OP (uuconf_zmax_remote_debug); \ + OP (uuconf_zphone); \ + OP (uuconf_zcall_login); \ + OP (uuconf_zcall_password); \ + OP (uuconf_zcalled_login); \ + OP (uuconf_zprotocols); \ + OP (uuconf_zpubdir); \ + OP (uuconf_zlocalname); \ + } \ + while (0) + +/* This macro operates on every string array (char **) field in struct + uuconf_system. */ +#define SYSTEM_STRING_ARRAYS(OP) \ + do \ + { \ + OP (uuconf_pzalias); \ + OP (uuconf_pzlocal_send); \ + OP (uuconf_pzremote_send); \ + OP (uuconf_pzlocal_receive); \ + OP (uuconf_pzremote_receive); \ + OP (uuconf_pzpath); \ + OP (uuconf_pzcmds); \ + OP (uuconf_pzforward_from); \ + OP (uuconf_pzforward_to); \ + OP (uuconf_schat.uuconf_pzchat); \ + OP (uuconf_schat.uuconf_pzprogram); \ + OP (uuconf_schat.uuconf_pzfail); \ + OP (uuconf_scalled_chat.uuconf_pzchat); \ + OP (uuconf_scalled_chat.uuconf_pzprogram); \ + OP (uuconf_scalled_chat.uuconf_pzfail); \ + } \ + while (0) + +/* This macro operations on every timespan pointer (struct + uuconf_timespan *) in struct uuconf_system. */ +#define SYSTEM_TIMESPANS(OP) \ + do \ + { \ + OP (uuconf_qtimegrade); \ + OP (uuconf_qcalltimegrade); \ + OP (uuconf_qcall_local_size); \ + OP (uuconf_qcall_remote_size); \ + OP (uuconf_qcalled_local_size); \ + OP (uuconf_qcalled_remote_size); \ + } \ + while (0) + +/* This macro operates on every boolean value (of type int, although + some type int are not boolean) field in uuconf_system. */ +#define SYSTEM_BOOLEANS(OP) \ + do \ + { \ + OP (uuconf_fcall); \ + OP (uuconf_fcalled); \ + OP (uuconf_fcallback); \ + OP (uuconf_fsequence); \ + OP (uuconf_fsend_request); \ + OP (uuconf_frec_request); \ + OP (uuconf_fcall_transfer); \ + OP (uuconf_fcalled_transfer); \ + OP (uuconf_schat.uuconf_fstrip); \ + OP (uuconf_scalled_chat.uuconf_fstrip); \ + } \ + while (0) + +/* This macro operates on every generic integer (type int or long) in + uuconf_system. */ +#define SYSTEM_INTEGERS(OP) \ + do \ + { \ + OP (uuconf_cmax_retries); \ + OP (uuconf_csuccess_wait); \ + OP (uuconf_ibaud); \ + OP (uuconf_ihighbaud); \ + OP (uuconf_cfree_space); \ + OP (uuconf_schat.uuconf_ctimeout); \ + OP (uuconf_scalled_chat.uuconf_ctimeout); \ + } \ + while (0) + +/* There is no macro for uuconf_qalternate, uuconf_zport, + uuconf_qport, uuconf_qproto_params, or uuconf_palloc. */ + +/* Clear the contents of a struct uuconf_system. */ + +void +_uuconf_uclear_system (q) + struct uuconf_system *q; +{ +#define CLEAR(x) q->x = (char *) &_uuconf_unset + SYSTEM_STRINGS (CLEAR); +#undef CLEAR +#define CLEAR(x) q->x = (char **) &_uuconf_unset + SYSTEM_STRING_ARRAYS (CLEAR); +#undef CLEAR +#define CLEAR(x) q->x = (struct uuconf_timespan *) &_uuconf_unset + SYSTEM_TIMESPANS (CLEAR); +#undef CLEAR +#define CLEAR(x) q->x = -1 + SYSTEM_BOOLEANS (CLEAR); + SYSTEM_INTEGERS (CLEAR); +#undef CLEAR + q->uuconf_qalternate = NULL; + q->uuconf_zport = (char *) &_uuconf_unset; + q->uuconf_qport = (struct uuconf_port *) &_uuconf_unset; + q->uuconf_qproto_params = (struct uuconf_proto_param *) &_uuconf_unset; + q->uuconf_palloc = NULL; +} + +/* Default the contents of one struct uuconf_system to the contents of + another. This default alternate by alternate. Any additional + alternates in q default to the last alternate of qdefault. If the + faddalternates arguments is TRUE, additional alternates or qdefault + are added to q; these alternates are copies of the first alternate + of q, and defaults are set from the additional alternates of + qdefault. */ + +int +_uuconf_isystem_default (qglobal, qset, qdefault, faddalternates) + struct sglobal *qglobal; + struct uuconf_system *qset; + struct uuconf_system *qdefault; + boolean faddalternates; +{ + struct uuconf_system *qalt; + + if (qset->uuconf_palloc != qdefault->uuconf_palloc) + qset->uuconf_palloc = + _uuconf_pmalloc_block_merge (qset->uuconf_palloc, + qdefault->uuconf_palloc); + + /* If we are adding alternates from the default, make sure we have + at least as many alternates in qset as we do in qdefault. Each + new alternate we create gets initialized to the first alternate + of the system. */ + if (faddalternates) + { + struct uuconf_system **pq, *qdef; + + for (qdef = qdefault, pq = &qset; + qdef != NULL; + qdef = qdef->uuconf_qalternate, pq = &(*pq)->uuconf_qalternate) + { + if (*pq == NULL) + { + *pq = ((struct uuconf_system *) + uuconf_malloc (qset->uuconf_palloc, + sizeof (struct uuconf_system))); + if (*pq == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + **pq = *qset; + (*pq)->uuconf_qalternate = NULL; + } + } + } + + for (qalt = qset; qalt != NULL; qalt = qalt->uuconf_qalternate) + { +#define DEFAULT(x) \ + if (qalt->x == (char *) &_uuconf_unset) qalt->x = qdefault->x + SYSTEM_STRINGS (DEFAULT); +#undef DEFAULT +#define DEFAULT(x) \ + if (qalt->x == (char **) &_uuconf_unset) qalt->x = qdefault->x + SYSTEM_STRING_ARRAYS (DEFAULT); +#undef DEFAULT +#define DEFAULT(x) \ + if (qalt->x == (struct uuconf_timespan *) &_uuconf_unset) \ + qalt->x = qdefault->x + SYSTEM_TIMESPANS (DEFAULT); +#undef DEFAULT +#define DEFAULT(x) if (qalt->x < 0) qalt->x = qdefault->x + SYSTEM_BOOLEANS (DEFAULT); + SYSTEM_INTEGERS (DEFAULT); +#undef DEFAULT + + /* We only copy over zport if both zport and qport are NULL, + because otherwise a default zport would override a specific + qport. */ + if (qalt->uuconf_zport == (char *) &_uuconf_unset + && qalt->uuconf_qport == (struct uuconf_port *) &_uuconf_unset) + qalt->uuconf_zport = qdefault->uuconf_zport; + if (qalt->uuconf_qport == (struct uuconf_port *) &_uuconf_unset) + qalt->uuconf_qport = qdefault->uuconf_qport; + + if (qalt->uuconf_qproto_params + == (struct uuconf_proto_param *) &_uuconf_unset) + qalt->uuconf_qproto_params = qdefault->uuconf_qproto_params; + + if (qdefault->uuconf_qalternate != NULL) + qdefault = qdefault->uuconf_qalternate; + } + + return UUCONF_SUCCESS; +} + +/* Put in the basic defaults. This ensures that the fields are valid + on every uuconf_system structure. */ + +int +_uuconf_isystem_basic_default (qglobal, q) + struct sglobal *qglobal; + register struct uuconf_system *q; +{ + int iret; + + iret = UUCONF_SUCCESS; + + for (; q != NULL && iret == UUCONF_SUCCESS; q = q->uuconf_qalternate) + { + /* The default of 26 allowable retries is traditional. */ + if (q->uuconf_cmax_retries < 0) + q->uuconf_cmax_retries = 26; + if (q->uuconf_schat.uuconf_pzchat == (char **) &_uuconf_unset) + { + q->uuconf_schat.uuconf_pzchat = NULL; + iret = _uuconf_iadd_string (qglobal, (char *) "\"\"", FALSE, + FALSE, + &q->uuconf_schat.uuconf_pzchat, + q->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + return iret; + iret = _uuconf_iadd_string (qglobal, (char *) "\\r\\c", FALSE, + FALSE, + &q->uuconf_schat.uuconf_pzchat, + q->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + return iret; + iret = _uuconf_iadd_string (qglobal, (char *) "ogin:", FALSE, + FALSE, + &q->uuconf_schat.uuconf_pzchat, + q->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + return iret; + iret = _uuconf_iadd_string (qglobal, (char *) "-BREAK", FALSE, + FALSE, + &q->uuconf_schat.uuconf_pzchat, + q->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + return iret; + iret = _uuconf_iadd_string (qglobal, (char *) "-ogin:", FALSE, + FALSE, + &q->uuconf_schat.uuconf_pzchat, + q->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + return iret; + iret = _uuconf_iadd_string (qglobal, (char *) "-BREAK", FALSE, + FALSE, + &q->uuconf_schat.uuconf_pzchat, + q->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + return iret; + iret = _uuconf_iadd_string (qglobal, (char *) "-ogin:", FALSE, + FALSE, + &q->uuconf_schat.uuconf_pzchat, + q->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + return iret; + iret = _uuconf_iadd_string (qglobal, (char *) "\\L", FALSE, + FALSE, + &q->uuconf_schat.uuconf_pzchat, + q->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + return iret; + iret = _uuconf_iadd_string (qglobal, (char *) "word:", FALSE, + FALSE, + &q->uuconf_schat.uuconf_pzchat, + q->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + return iret; + iret = _uuconf_iadd_string (qglobal, (char *) "\\P", FALSE, + FALSE, + &q->uuconf_schat.uuconf_pzchat, + q->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + return iret; + } + if (q->uuconf_schat.uuconf_ctimeout < 0) + q->uuconf_schat.uuconf_ctimeout = 10; + if (q->uuconf_schat.uuconf_fstrip < 0) + q->uuconf_schat.uuconf_fstrip = TRUE; + if (q->uuconf_scalled_chat.uuconf_ctimeout < 0) + q->uuconf_scalled_chat.uuconf_ctimeout = 60; + if (q->uuconf_scalled_chat.uuconf_fstrip < 0) + q->uuconf_scalled_chat.uuconf_fstrip = TRUE; + if (q->uuconf_fsend_request < 0) + q->uuconf_fsend_request = TRUE; + if (q->uuconf_frec_request < 0) + q->uuconf_frec_request = TRUE; + if (q->uuconf_fcall_transfer < 0) + q->uuconf_fcall_transfer = TRUE; + if (q->uuconf_fcalled_transfer < 0) + q->uuconf_fcalled_transfer = TRUE; + if (q->uuconf_pzlocal_send == (char **) &_uuconf_unset) + { + q->uuconf_pzlocal_send = NULL; + iret = _uuconf_iadd_string (qglobal, (char *) ZROOTDIR, FALSE, + FALSE, &q->uuconf_pzlocal_send, + q->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + return iret; + } + if (q->uuconf_pzremote_send == (char **) &_uuconf_unset) + { + q->uuconf_pzremote_send = NULL; + iret = _uuconf_iadd_string (qglobal, (char *) "~", FALSE, FALSE, + &q->uuconf_pzremote_send, + q->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + return iret; + } + if (q->uuconf_pzlocal_receive == (char **) &_uuconf_unset) + { + q->uuconf_pzlocal_receive = NULL; + iret = _uuconf_iadd_string (qglobal, (char *) "~", FALSE, FALSE, + &q->uuconf_pzlocal_receive, + q->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + return iret; + } + if (q->uuconf_pzremote_receive == (char **) &_uuconf_unset) + { + q->uuconf_pzremote_receive = NULL; + iret = _uuconf_iadd_string (qglobal, (char *) "~", FALSE, FALSE, + &q->uuconf_pzremote_receive, + q->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + return iret; + } + + if (q->uuconf_pzpath == (char **) &_uuconf_unset) + { + char *zdup; + char **pz; + size_t csplit; + int c; + + zdup = (char *) uuconf_malloc (q->uuconf_palloc, sizeof CMDPATH); + if (zdup == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + + memcpy ((pointer) zdup, (pointer) CMDPATH, sizeof CMDPATH); + pz = NULL; + csplit = 0; + if ((c = _uuconf_istrsplit (zdup, '\0', &pz, &csplit)) < 0) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + q->uuconf_pzpath = (char **) uuconf_malloc (q->uuconf_palloc, + ((c + 1) + * sizeof (char *))); + if (q->uuconf_pzpath == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + memcpy ((pointer) q->uuconf_pzpath, (pointer) pz, + c * sizeof (char *)); + q->uuconf_pzpath[c] = NULL; + free ((pointer) pz); + } + + if (q->uuconf_pzcmds == (char **) &_uuconf_unset) + { + q->uuconf_pzcmds = ((char **) + uuconf_malloc (q->uuconf_palloc, + 3 * sizeof (const char *))); + if (q->uuconf_pzcmds == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + q->uuconf_pzcmds[0] = (char *) "rnews"; + q->uuconf_pzcmds[1] = (char *) "rmail"; + q->uuconf_pzcmds[2] = NULL; + } + + if (q->uuconf_cfree_space < 0) + q->uuconf_cfree_space = DEFAULT_FREE_SPACE; + + if (q->uuconf_zpubdir == (const char *) &_uuconf_unset) + q->uuconf_zpubdir = qglobal->qprocess->zpubdir; + +#define SET(x) if (q->x == (char *) &_uuconf_unset) q->x = NULL + SYSTEM_STRINGS(SET); +#undef SET +#define SET(x) if (q->x == (char **) &_uuconf_unset) q->x = NULL + SYSTEM_STRING_ARRAYS(SET); +#undef SET +#define SET(x) \ + if (q->x == (struct uuconf_timespan *) &_uuconf_unset) q->x = NULL + SYSTEM_TIMESPANS (SET); +#undef SET +#define SET(x) if (q->x < 0) q->x = 0 + SYSTEM_BOOLEANS (SET); + SYSTEM_INTEGERS (SET); +#undef SET + + if (q->uuconf_zport == (char *) &_uuconf_unset) + q->uuconf_zport = NULL; + if (q->uuconf_qport == (struct uuconf_port *) &_uuconf_unset) + q->uuconf_qport = NULL; + if (q->uuconf_qproto_params + == (struct uuconf_proto_param *) &_uuconf_unset) + q->uuconf_qproto_params = NULL; + } + + return iret; +} diff --git a/gnu/libexec/uucp/libuuconf/tcalou.c b/gnu/libexec/uucp/libuuconf/tcalou.c new file mode 100644 index 0000000000..8eb627eb33 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/tcalou.c @@ -0,0 +1,201 @@ +/* tcalou.c + Find callout login name and password from Taylor UUCP configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_tcalou_rcsid[] = "$Id: tcalou.c,v 1.1 1993/08/04 19:35:07 jtc Exp $"; +#endif + +#include + +static int icsys P((pointer pglobal, int argc, char **argv, pointer pvar, + pointer pinfo)); + +/* Find the callout login name and password for a system from the + Taylor UUCP configuration files. */ + +int +uuconf_taylor_callout (pglobal, qsys, pzlog, pzpass) + pointer pglobal; + const struct uuconf_system *qsys; + char **pzlog; + char **pzpass; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + boolean flookup; + struct uuconf_cmdtab as[2]; + char **pz; + int iret; + pointer pinfo; + + *pzlog = NULL; + *pzpass = NULL; + + flookup = FALSE; + + if (qsys->uuconf_zcall_login != NULL) + { + if (strcmp (qsys->uuconf_zcall_login, "*") == 0) + flookup = TRUE; + else + { + *pzlog = strdup (qsys->uuconf_zcall_login); + if (*pzlog == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + } + } + + if (qsys->uuconf_zcall_password != NULL) + { + if (strcmp (qsys->uuconf_zcall_password, "*") == 0) + flookup = TRUE; + else + { + *pzpass = strdup (qsys->uuconf_zcall_password); + if (*pzpass == NULL) + { + qglobal->ierrno = errno; + if (*pzlog != NULL) + { + free ((pointer) *pzlog); + *pzlog = NULL; + } + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + } + } + + if (! flookup) + { + if (*pzlog == NULL && *pzpass == NULL) + return UUCONF_NOT_FOUND; + return UUCONF_SUCCESS; + } + + as[0].uuconf_zcmd = qsys->uuconf_zname; + as[0].uuconf_itype = UUCONF_CMDTABTYPE_FN | 3; + if (*pzlog == NULL) + as[0].uuconf_pvar = (pointer) pzlog; + else + as[0].uuconf_pvar = NULL; + as[0].uuconf_pifn = icsys; + + as[1].uuconf_zcmd = NULL; + + if (*pzpass == NULL) + pinfo = (pointer) pzpass; + else + pinfo = NULL; + + iret = UUCONF_SUCCESS; + + for (pz = qglobal->qprocess->pzcallfiles; *pz != NULL; pz++) + { + FILE *e; + + e = fopen (*pz, "r"); + if (e == NULL) + { + if (FNO_SUCH_FILE ()) + continue; + qglobal->ierrno = errno; + iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + iret = uuconf_cmd_file (pglobal, e, as, pinfo, + (uuconf_cmdtabfn) NULL, 0, + qsys->uuconf_palloc); + (void) fclose (e); + + if (iret != UUCONF_SUCCESS) + break; + if (*pzlog != NULL) + break; + } + + if (iret != UUCONF_SUCCESS) + { + qglobal->zfilename = *pz; + return iret | UUCONF_ERROR_FILENAME; + } + + if (*pzlog == NULL && *pzpass == NULL) + return UUCONF_NOT_FOUND; + + return UUCONF_SUCCESS; +} + +/* Copy the login name and password onto the heap and set the + pointers. The pzlog argument is passed in pvar, and the pzpass + argument is passed in pinfo. */ + +static int +icsys (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + char **pzlog = (char **) pvar; + char **pzpass = (char **) pinfo; + + if (pzlog != NULL) + { + *pzlog = strdup (argv[1]); + if (*pzlog == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + } + + if (pzpass != NULL) + { + *pzpass = strdup (argv[2]); + if (*pzpass == NULL) + { + qglobal->ierrno = errno; + if (pzlog != NULL) + { + free ((pointer) *pzlog); + *pzlog = NULL; + } + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + } + + return UUCONF_CMDTABRET_EXIT; +} diff --git a/gnu/libexec/uucp/libuuconf/tdial.c b/gnu/libexec/uucp/libuuconf/tdial.c new file mode 100644 index 0000000000..f13c92998a --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/tdial.c @@ -0,0 +1,227 @@ +/* tdial.c + Find a dialer in the Taylor UUCP configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_tdial_rcsid[] = "$Id: tdial.c,v 1.1 1993/08/04 19:35:08 jtc Exp $"; +#endif + +#include + +static int iddialer P((pointer pglobal, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int idunknown P((pointer pglobal, int argc, char **argv, pointer pvar, + pointer pinfo)); + +/* Find a dialer in the Taylor UUCP configuration files by name. */ + +int +uuconf_taylor_dialer_info (pglobal, zname, qdialer) + pointer pglobal; + const char *zname; + struct uuconf_dialer *qdialer; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + FILE *e; + pointer pblock; + int iret; + char **pz; + + e = NULL; + pblock = NULL; + iret = UUCONF_NOT_FOUND; + + for (pz = qglobal->qprocess->pzdialfiles; *pz != NULL; pz++) + { + struct uuconf_cmdtab as[2]; + char *zdialer; + struct uuconf_dialer sdefault; + int ilineno; + + e = fopen (*pz, "r"); + if (e == NULL) + { + if (FNO_SUCH_FILE ()) + continue; + qglobal->ierrno = errno; + iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + qglobal->ilineno = 0; + + /* Gather the default information from the top of the file. We + do this by handling the "dialer" command ourselves and + passing every other command to _uuconf_idialer_cmd via + idunknown. The value of zdialer will be an malloc block. */ + as[0].uuconf_zcmd = "dialer"; + as[0].uuconf_itype = UUCONF_CMDTABTYPE_FN | 2; + as[0].uuconf_pvar = (pointer) &zdialer; + as[0].uuconf_pifn = iddialer; + + as[1].uuconf_zcmd = NULL; + + pblock = uuconf_malloc_block (); + if (pblock == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + _uuconf_uclear_dialer (&sdefault); + sdefault.uuconf_palloc = pblock; + zdialer = NULL; + iret = uuconf_cmd_file (pglobal, e, as, (pointer) &sdefault, + idunknown, UUCONF_CMDTABFLAG_BACKSLASH, + pblock); + + /* Now skip until we find a dialer with a matching name. */ + while (iret == UUCONF_SUCCESS + && zdialer != NULL + && strcmp (zname, zdialer) != 0) + { + free ((pointer) zdialer); + zdialer = NULL; + ilineno = qglobal->ilineno; + iret = uuconf_cmd_file (pglobal, e, as, (pointer) NULL, + (uuconf_cmdtabfn) NULL, + UUCONF_CMDTABFLAG_BACKSLASH, + pblock); + qglobal->ilineno += ilineno; + } + + if (iret != UUCONF_SUCCESS) + { + if (zdialer != NULL) + free ((pointer) zdialer); + break; + } + + if (zdialer != NULL) + { + size_t csize; + + /* We've found the dialer we're looking for. Read the rest + of the commands for it. */ + as[0].uuconf_pvar = NULL; + + *qdialer = sdefault; + csize = strlen (zdialer) + 1; + qdialer->uuconf_zname = uuconf_malloc (pblock, csize); + if (qdialer->uuconf_zname == NULL) + { + qglobal->ierrno = errno; + free ((pointer) zdialer); + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + memcpy ((pointer) qdialer->uuconf_zname, (pointer) zdialer, + csize); + free ((pointer) zdialer); + + ilineno = qglobal->ilineno; + iret = uuconf_cmd_file (pglobal, e, as, qdialer, idunknown, + UUCONF_CMDTABFLAG_BACKSLASH, pblock); + qglobal->ilineno += ilineno; + break; + } + + (void) fclose (e); + e = NULL; + uuconf_free_block (pblock); + pblock = NULL; + + iret = UUCONF_NOT_FOUND; + } + + if (e != NULL) + (void) fclose (e); + if (iret != UUCONF_SUCCESS && pblock != NULL) + uuconf_free_block (pblock); + + if (iret != UUCONF_SUCCESS && iret != UUCONF_NOT_FOUND) + { + qglobal->zfilename = *pz; + iret |= UUCONF_ERROR_FILENAME; + } + + return iret; +} + +/* Handle a "dialer" command. This copies the string onto the heap + and returns the pointer in *pvar, unless pvar is NULL. It returns + UUCONF_CMDTABRET_EXIT to force _uuconf_icmd_file_internal to stop + reading and return to the code above, which will then check the + dialer name just read to see if it matches. */ + +/*ARGSUSED*/ +static int +iddialer (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + char **pz = (char **) pvar; + + if (pz != NULL) + { + size_t csize; + + csize = strlen (argv[1]) + 1; + *pz = malloc (csize); + if (*pz == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + memcpy ((pointer) *pz, (pointer) argv[1], csize); + } + return UUCONF_CMDTABRET_EXIT; +} + +/* Handle an unknown command by passing it on to _uuconf_idialer_cmd, + which will parse it into the dialer structure. */ + +/*ARGSUSED*/ +static int +idunknown (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct uuconf_dialer *qdialer = (struct uuconf_dialer *) pinfo; + + return _uuconf_idialer_cmd (qglobal, argc, argv, qdialer); +} diff --git a/gnu/libexec/uucp/libuuconf/tdialc.c b/gnu/libexec/uucp/libuuconf/tdialc.c new file mode 100644 index 0000000000..e70090f3bb --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/tdialc.c @@ -0,0 +1,211 @@ +/* tdialc.c + Handle a Taylor UUCP dialer command. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_tdialc_rcsid[] = "$Id: tdialc.c,v 1.1 1993/08/04 19:35:09 jtc Exp $"; +#endif + +static int idchat P((pointer pglobal, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int iddtr_toggle P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); +static int idcomplete P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); +static int idproto_param P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); +static int idcunknown P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); + +/* The command table for dialer commands. The "dialer" command is + handled specially. */ +static const struct cmdtab_offset asDialer_cmds[] = +{ + { "chat", UUCONF_CMDTABTYPE_PREFIX | 0, + offsetof (struct uuconf_dialer, uuconf_schat), idchat }, + { "dialtone", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_dialer, uuconf_zdialtone), NULL }, + { "pause", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_dialer, uuconf_zpause), NULL }, + { "carrier", UUCONF_CMDTABTYPE_BOOLEAN, + offsetof (struct uuconf_dialer, uuconf_fcarrier), NULL }, + { "carrier-wait", UUCONF_CMDTABTYPE_INT, + offsetof (struct uuconf_dialer, uuconf_ccarrier_wait), NULL }, + { "dtr-toggle", UUCONF_CMDTABTYPE_FN | 0, (size_t) -1, iddtr_toggle }, + { "complete", UUCONF_CMDTABTYPE_FN | 2, + offsetof (struct uuconf_dialer, uuconf_scomplete), idcomplete }, + { "complete-chat", UUCONF_CMDTABTYPE_PREFIX, + offsetof (struct uuconf_dialer, uuconf_scomplete), idchat }, + { "abort", UUCONF_CMDTABTYPE_FN | 2, + offsetof (struct uuconf_dialer, uuconf_sabort), idcomplete }, + { "abort-chat", UUCONF_CMDTABTYPE_PREFIX, + offsetof (struct uuconf_dialer, uuconf_sabort), idchat }, + { "protocol-parameter", UUCONF_CMDTABTYPE_FN | 0, + offsetof (struct uuconf_dialer, uuconf_qproto_params), idproto_param }, + { "seven-bit", UUCONF_CMDTABTYPE_FN | 2, + offsetof (struct uuconf_dialer, uuconf_ireliable), _uuconf_iseven_bit }, + { "reliable", UUCONF_CMDTABTYPE_FN | 2, + offsetof (struct uuconf_dialer, uuconf_ireliable), _uuconf_ireliable }, + { "half-duplex", UUCONF_CMDTABTYPE_FN | 2, + offsetof (struct uuconf_dialer, uuconf_ireliable), + _uuconf_ihalf_duplex }, + { NULL, 0, 0, NULL } +}; + +#define CDIALER_CMDS (sizeof asDialer_cmds / sizeof asDialer_cmds[0]) + +/* Handle a command passed to a dialer from a Taylor UUCP + configuration file. This can be called when reading the dialer + file, the port file, or the sys file. The return value may have + UUCONF_CMDTABRET_KEEP set, but not UUCONF_CMDTABRET_EXIT. It + assigns values to the elements of qdialer. The first time this is + called, qdialer->uuconf_palloc should be set. This will not set + qdialer->uuconf_zname. */ + +int +_uuconf_idialer_cmd (qglobal, argc, argv, qdialer) + struct sglobal *qglobal; + int argc; + char **argv; + struct uuconf_dialer *qdialer; +{ + struct uuconf_cmdtab as[CDIALER_CMDS]; + int iret; + + _uuconf_ucmdtab_base (asDialer_cmds, CDIALER_CMDS, (char *) qdialer, as); + + iret = uuconf_cmd_args ((pointer) qglobal, argc, argv, as, + (pointer) qdialer, idcunknown, 0, + qdialer->uuconf_palloc); + + return iret &~ UUCONF_CMDTABRET_EXIT; +} + +/* Reroute a chat script command. */ + +static int +idchat (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct uuconf_chat *qchat = (struct uuconf_chat *) pvar; + struct uuconf_dialer *qdialer = (struct uuconf_dialer *) pinfo; + + return _uuconf_ichat_cmd (qglobal, argc, argv, qchat, + qdialer->uuconf_palloc); +} + +/* Handle the "dtr-toggle" command, which may take two arguments. */ + +/*ARGSUSED*/ +static int +iddtr_toggle (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct uuconf_dialer *qdialer = (struct uuconf_dialer *) pinfo; + int iret; + + if (argc < 2 || argc > 3) + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; + + iret = _uuconf_iboolean (qglobal, argv[1], &qdialer->uuconf_fdtr_toggle); + if ((iret &~ UUCONF_CMDTABRET_KEEP) != UUCONF_SUCCESS) + return iret; + + if (argc < 3) + return iret; + + iret |= _uuconf_iboolean (qglobal, argv[2], + &qdialer->uuconf_fdtr_toggle_wait); + + return iret; +} + +/* Handle the "complete" and "abort" commands. These just turn a + string into a trivial chat script. */ + +/*ARGSUSED*/ +static int +idcomplete (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct uuconf_chat *qchat = (struct uuconf_chat *) pvar; + struct uuconf_dialer *qdialer = (struct uuconf_dialer *) pinfo; + char *azargs[3]; + + azargs[0] = (char *) "complete-chat"; + azargs[1] = (char *) "\"\""; + azargs[2] = (char *) argv[1]; + + return _uuconf_ichat_cmd (qglobal, 3, azargs, qchat, + qdialer->uuconf_palloc); +} + +/* Handle the "protocol-parameter" command. */ + +static int +idproto_param (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct uuconf_proto_param **pqparam = (struct uuconf_proto_param **) pvar; + struct uuconf_dialer *qdialer = (struct uuconf_dialer *) pinfo; + + return _uuconf_iadd_proto_param (qglobal, argc - 1, argv + 1, pqparam, + qdialer->uuconf_palloc); +} + +/* Give an error for an unknown dialer command. */ + +/*ARGSUSED*/ +static int +idcunknown (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; +} diff --git a/gnu/libexec/uucp/libuuconf/tdnams.c b/gnu/libexec/uucp/libuuconf/tdnams.c new file mode 100644 index 0000000000..d9e4b0d7e3 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/tdnams.c @@ -0,0 +1,119 @@ +/* tdnams.c + Get all known dialer names from the Taylor UUCP configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_tdnams_rcsid[] = "$Id: tdnams.c,v 1.1 1993/08/04 19:35:10 jtc Exp $"; +#endif + +#include + +static int indialer P((pointer pglobal, int argc, char **argv, pointer pvar, + pointer pinfo)); + +/* Get the names of all the dialers from the Taylor UUCP configuration + files. */ + +int +uuconf_taylor_dialer_names (pglobal, ppzdialers) + pointer pglobal; + char ***ppzdialers; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct uuconf_cmdtab as[2]; + char **pz; + int iret; + + *ppzdialers = NULL; + + as[0].uuconf_zcmd = "dialer"; + as[0].uuconf_itype = UUCONF_CMDTABTYPE_FN | 2; + as[0].uuconf_pvar = (pointer) ppzdialers; + as[0].uuconf_pifn = indialer; + + as[1].uuconf_zcmd = NULL; + + iret = UUCONF_SUCCESS; + + for (pz = qglobal->qprocess->pzdialfiles; *pz != NULL; pz++) + { + FILE *e; + + e = fopen (*pz, "r"); + if (e == NULL) + { + if (FNO_SUCH_FILE ()) + continue; + qglobal->ierrno = errno; + iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + iret = uuconf_cmd_file (pglobal, e, as, (pointer) NULL, + (uuconf_cmdtabfn) NULL, + UUCONF_CMDTABFLAG_BACKSLASH, + (pointer) NULL); + + (void) fclose (e); + + if (iret != UUCONF_SUCCESS) + break; + } + + if (iret != UUCONF_SUCCESS) + { + qglobal->zfilename = *pz; + return iret | UUCONF_ERROR_FILENAME; + } + + if (*ppzdialers == NULL) + iret = _uuconf_iadd_string (qglobal, (char *) NULL, FALSE, FALSE, + ppzdialers, (pointer) NULL); + + return UUCONF_SUCCESS; +} + +/* Add a dialer name to the list. */ + +/*ARGSUSED*/ +static int +indialer (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + char ***ppzdialers = (char ***) pvar; + int iret; + + iret = _uuconf_iadd_string (qglobal, argv[1], TRUE, TRUE, ppzdialers, + (pointer) NULL); + if (iret != UUCONF_SUCCESS) + iret |= UUCONF_CMDTABRET_EXIT; + return iret; +} diff --git a/gnu/libexec/uucp/libuuconf/tgcmp.c b/gnu/libexec/uucp/libuuconf/tgcmp.c new file mode 100644 index 0000000000..9bc8de2b5f --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/tgcmp.c @@ -0,0 +1,42 @@ +/* tgcmp.c + A comparison function for grades for _uuconf_time_parse. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_tgcmp_rcsid[] = "$Id: tgcmp.c,v 1.1 1993/08/04 19:35:11 jtc Exp $"; +#endif + +/* A comparison function to pass to _uuconf_itime_parse. This + compares grades. We can't just pass uuconf_grade_cmp, since + _uuconf_itime_parse wants a function takes longs as arguments. */ + +int +_uuconf_itime_grade_cmp (i1, i2) + long i1; + long i2; +{ + return UUCONF_GRADE_CMP ((int) i1, (int) i2); +} diff --git a/gnu/libexec/uucp/libuuconf/thread.c b/gnu/libexec/uucp/libuuconf/thread.c new file mode 100644 index 0000000000..92f100bba2 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/thread.c @@ -0,0 +1,70 @@ +/* thread.c + Initialize for a new thread. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_thread_rcsid[] = "$Id: thread.c,v 1.1 1993/08/04 19:35:11 jtc Exp $"; +#endif + +#include + +/* Initialize for a new thread, by allocating a new sglobal structure + which points to the same sprocess structure. */ + +int +uuconf_init_thread (ppglobal) + pointer *ppglobal; +{ + struct sglobal **pqglob = (struct sglobal **) ppglobal; + pointer pblock; + struct sglobal *qnew; + + pblock = uuconf_malloc_block (); + if (pblock == NULL) + { + (*pqglob)->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + + qnew = (struct sglobal *) uuconf_malloc (pblock, + sizeof (struct sglobal)); + if (qnew == NULL) + { + (*pqglob)->ierrno = errno; + uuconf_free_block (pblock); + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + + qnew->pblock = pblock; + qnew->ierrno = 0; + qnew->ilineno = 0; + qnew->zfilename = NULL; + qnew->qprocess = (*pqglob)->qprocess; + + *pqglob = qnew; + + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/time.c b/gnu/libexec/uucp/libuuconf/time.c new file mode 100644 index 0000000000..6f316828a3 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/time.c @@ -0,0 +1,406 @@ +/* time.c + Parse a time string into a uuconf_timespan structure. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_time_rcsid[] = "$Id: time.c,v 1.1 1993/08/04 19:35:12 jtc Exp $"; +#endif + +#include +#include + +static int itadd_span P((struct sglobal *qglobal, int istart, int iend, + long ival, int cretry, + int (*picmp) P((long, long)), + struct uuconf_timespan **pqspan, + pointer pblock)); +static int itnew P((struct sglobal *qglobal, struct uuconf_timespan **pqset, + struct uuconf_timespan *qnext, int istart, int iend, + long ival, int cretry, pointer pblock)); + +/* An array of weekday abbreviations. The code below assumes that + each one starts with a lower case letter. */ + +static const struct +{ + const char *zname; + int imin; + int imax; +} asTdays[] = +{ + { "any", 0, 6 }, + { "wk", 1, 5 }, + { "su", 0, 0 }, + { "mo", 1, 1 }, + { "tu", 2, 2 }, + { "we", 3, 3 }, + { "th", 4, 4 }, + { "fr", 5, 5 }, + { "sa", 6, 6 }, + { "never", -1, -2 }, + { NULL, 0, 0 } +}; + +/* Parse a time string and add it to a span list. This function is + given the value, the retry time, and the comparison function to + use. */ + +int +_uuconf_itime_parse (qglobal, ztime, ival, cretry, picmp, pqspan, pblock) + struct sglobal *qglobal; + char *ztime; + long ival; + int cretry; + int (*picmp) P((long, long)); + struct uuconf_timespan **pqspan; + pointer pblock; +{ + struct uuconf_timespan *qlist; + char bfirst; + const char *z; + + qlist = *pqspan; + if (qlist == (struct uuconf_timespan *) &_uuconf_unset) + qlist = NULL; + + /* Expand the string using a timetable. Keep rechecking the string + until it does not match. */ + while (TRUE) + { + char **pz; + char *zfound; + + bfirst = *ztime; + if (isupper (BUCHAR (bfirst))) + bfirst = tolower (BUCHAR (bfirst)); + + zfound = NULL; + pz = qglobal->qprocess->pztimetables; + + /* We want the last timetable to have been defined with this + name, so we always look through the entire table. */ + while (*pz != NULL) + { + if ((bfirst == (*pz)[0] + || (isupper (BUCHAR ((*pz)[0])) + && bfirst == tolower (BUCHAR ((*pz)[0])))) + && strcasecmp (ztime, *pz) == 0) + zfound = pz[1]; + pz += 2; + } + if (zfound == NULL) + break; + ztime = zfound; + } + + /* Look through the time string. */ + z = ztime; + while (*z != '\0') + { + int iday; + boolean afday[7]; + int istart, iend; + + if (*z == ',' || *z == '|') + ++z; + if (*z == '\0' || *z == ';') + break; + + for (iday = 0; iday < 7; iday++) + afday[iday] = FALSE; + + /* Get the days. */ + do + { + bfirst = *z; + if (isupper (BUCHAR (bfirst))) + bfirst = tolower (BUCHAR (bfirst)); + for (iday = 0; asTdays[iday].zname != NULL; iday++) + { + size_t clen; + + if (bfirst != asTdays[iday].zname[0]) + continue; + + clen = strlen (asTdays[iday].zname); + if (strncasecmp (z, asTdays[iday].zname, clen) == 0) + { + int iset; + + for (iset = asTdays[iday].imin; + iset <= asTdays[iday].imax; + iset++) + afday[iset] = TRUE; + z += clen; + break; + } + } + if (asTdays[iday].zname == NULL) + return UUCONF_SYNTAX_ERROR; + } + while (isalpha (BUCHAR (*z))); + + /* Get the hours. */ + if (! isdigit (BUCHAR (*z))) + { + istart = 0; + iend = 24 * 60; + } + else + { + char *zendnum; + + istart = (int) strtol ((char *) z, &zendnum, 10); + if (*zendnum != '-' || ! isdigit (BUCHAR (zendnum[1]))) + return UUCONF_SYNTAX_ERROR; + z = zendnum + 1; + iend = (int) strtol ((char *) z, &zendnum, 10); + z = zendnum; + + istart = (istart / 100) * 60 + istart % 100; + iend = (iend / 100) * 60 + iend % 100; + } + + /* Add the times we've found onto the list. */ + for (iday = 0; iday < 7; iday++) + { + if (afday[iday]) + { + int iminute, iret; + + iminute = iday * 24 * 60; + if (istart < iend) + iret = itadd_span (qglobal, iminute + istart, + iminute + iend, ival, cretry, picmp, + &qlist, pblock); + else + { + /* Wrap around midnight. */ + iret = itadd_span (qglobal, iminute, iminute + iend, + ival, cretry, picmp, &qlist, pblock); + if (iret == UUCONF_SUCCESS) + iret = itadd_span (qglobal, iminute + istart, + iminute + 24 * 60, ival, cretry, + picmp, &qlist, pblock); + } + + if (iret != UUCONF_SUCCESS) + return iret; + } + } + } + + *pqspan = qlist; + + return UUCONF_SUCCESS; +} + +/* Add a time span to an existing list of time spans. We keep the + list sorted by time to make this operation easier. This modifies + the existing list, and returns the modified version. It takes a + comparison function which should return < 0 if the first argument + should take precedence over the second argument and == 0 if they + are the same (for grades this is igradecmp; for sizes it is minus + (the binary operator)). */ + +static int +itadd_span (qglobal, istart, iend, ival, cretry, picmp, pqspan, pblock) + struct sglobal *qglobal; + int istart; + int iend; + long ival; + int cretry; + int (*picmp) P((long, long)); + struct uuconf_timespan **pqspan; + pointer pblock; +{ + struct uuconf_timespan **pq; + int iret; + + /* istart < iend */ + for (pq = pqspan; *pq != NULL; pq = &(*pq)->uuconf_qnext) + { + int icmp; + + /* Invariant: PREV (*pq) == NULL || PREV (*pq)->iend <= istart */ + /* istart < iend && (*pq)->istart < (*pq)->iend */ + + if (iend <= (*pq)->uuconf_istart) + { + /* istart < iend <= (*pq)->istart < (*pq)->iend */ + /* No overlap, and we're at the right spot. See if we can + combine these spans. */ + if (iend == (*pq)->uuconf_istart + && cretry == (*pq)->uuconf_cretry + && (*picmp) (ival, (*pq)->uuconf_ival) == 0) + { + (*pq)->uuconf_istart = istart; + return UUCONF_SUCCESS; + } + /* We couldn't combine them. */ + break; + } + + if ((*pq)->uuconf_iend <= istart) + { + /* (*pq)->istart < (*pq)->iend <= istart < iend */ + /* No overlap. Try attaching this span. */ + if ((*pq)->uuconf_iend == istart + && (*pq)->uuconf_cretry == cretry + && ((*pq)->uuconf_qnext == NULL + || iend <= (*pq)->uuconf_qnext->uuconf_istart) + && (*picmp) (ival, (*pq)->uuconf_ival) == 0) + { + (*pq)->uuconf_iend = iend; + return UUCONF_SUCCESS; + } + /* Couldn't attach; keep looking for the right spot. We + might be able to combine part of the new span onto an + existing span, but it's probably not worth it. */ + continue; + } + + /* istart < iend + && (*pq)->istart < (*pq)->iend + && istart < (*pq)->iend + && (*pq)->istart < iend */ + /* Overlap. */ + + icmp = (*picmp) (ival, (*pq)->uuconf_ival); + + if (icmp == 0) + { + /* Just expand the old span to include the new span. */ + if (istart < (*pq)->uuconf_istart) + (*pq)->uuconf_istart = istart; + if ((*pq)->uuconf_iend >= iend) + return UUCONF_SUCCESS; + if ((*pq)->uuconf_qnext == NULL + || iend <= (*pq)->uuconf_qnext->uuconf_istart) + { + (*pq)->uuconf_iend = iend; + return UUCONF_SUCCESS; + } + /* The span we are adding overlaps the next span as well. + Expand the old span up to the next old span, and keep + trying to add the new span. */ + (*pq)->uuconf_iend = (*pq)->uuconf_qnext->uuconf_istart; + istart = (*pq)->uuconf_iend; + } + else if (icmp < 0) + { + /* Replace the old span with the new span. */ + if ((*pq)->uuconf_istart < istart) + { + /* Save the initial portion of the old span. */ + iret = itnew (qglobal, pq, *pq, (*pq)->uuconf_istart, istart, + (*pq)->uuconf_ival, (*pq)->uuconf_cretry, + pblock); + if (iret != UUCONF_SUCCESS) + return iret; + pq = &(*pq)->uuconf_qnext; + } + if (iend < (*pq)->uuconf_iend) + { + /* Save the final portion of the old span. */ + iret = itnew (qglobal, &(*pq)->uuconf_qnext, + (*pq)->uuconf_qnext, iend, (*pq)->uuconf_iend, + (*pq)->uuconf_ival, (*pq)->uuconf_cretry, + pblock); + if (iret != UUCONF_SUCCESS) + return iret; + } + (*pq)->uuconf_ival = ival; + (*pq)->uuconf_istart = istart; + (*pq)->uuconf_cretry = cretry; + if ((*pq)->uuconf_qnext == NULL + || iend <= (*pq)->uuconf_qnext->uuconf_istart) + { + (*pq)->uuconf_iend = iend; + return UUCONF_SUCCESS; + } + /* Move this span up to the next one, and keep trying to add + the new span. */ + (*pq)->uuconf_iend = (*pq)->uuconf_qnext->uuconf_istart; + istart = (*pq)->uuconf_iend; + } + else + { + /* Leave the old span untouched. */ + if (istart < (*pq)->uuconf_istart) + { + /* Put in the initial portion of the new span. */ + iret = itnew (qglobal, pq, *pq, istart, (*pq)->uuconf_istart, + ival, cretry, pblock); + if (iret != UUCONF_SUCCESS) + return iret; + pq = &(*pq)->uuconf_qnext; + } + if (iend <= (*pq)->uuconf_iend) + return UUCONF_SUCCESS; + /* Keep trying to add the new span. */ + istart = (*pq)->uuconf_iend; + } + } + + /* This is the spot for the new span, and there's no overlap. */ + + return itnew (qglobal, pq, *pq, istart, iend, ival, cretry, pblock); +} + +/* A utility function to create a new uuconf_timespan structure. */ + +static int +itnew (qglobal, pqset, qnext, istart, iend, ival, cretry, pblock) + struct sglobal *qglobal; + struct uuconf_timespan **pqset; + struct uuconf_timespan *qnext; + int istart; + int iend; + long ival; + int cretry; + pointer pblock; +{ + register struct uuconf_timespan *qnew; + + qnew = ((struct uuconf_timespan *) + uuconf_malloc (pblock, sizeof (struct uuconf_timespan))); + if (qnew == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + + qnew->uuconf_qnext = qnext; + qnew->uuconf_istart = istart; + qnew->uuconf_iend = iend; + qnew->uuconf_ival = ival; + qnew->uuconf_cretry = cretry; + + *pqset = qnew; + + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/tinit.c b/gnu/libexec/uucp/libuuconf/tinit.c new file mode 100644 index 0000000000..829fd12285 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/tinit.c @@ -0,0 +1,370 @@ +/* tinit.c + Initialize for reading Taylor UUCP configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_tinit_rcsid[] = "$Id: tinit.c,v 1.1 1993/08/04 19:35:14 jtc Exp $"; +#endif + +#include + +/* Local functions. */ + +static int itset_default P((struct sglobal *qglobal, char ***ppzvar, + const char *zfile)); +static int itadd P((pointer pglobal, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int itunknown P((pointer pglobal, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int itprogram P((pointer pglobal, int argc, char **argv, pointer pvar, + pointer pinfo)); + +static const struct cmdtab_offset asCmds[] = +{ + { "nodename", UUCONF_CMDTABTYPE_STRING, + offsetof (struct sprocess, zlocalname), NULL }, + { "hostname", UUCONF_CMDTABTYPE_STRING, + offsetof (struct sprocess, zlocalname), NULL }, + { "uuname", UUCONF_CMDTABTYPE_STRING, + offsetof (struct sprocess, zlocalname), NULL }, + { "spool", UUCONF_CMDTABTYPE_STRING, + offsetof (struct sprocess, zspooldir), NULL }, + { "pubdir", UUCONF_CMDTABTYPE_STRING, + offsetof (struct sprocess, zpubdir), NULL }, + { "lockdir", UUCONF_CMDTABTYPE_STRING, + offsetof (struct sprocess, zlockdir), NULL }, + { "logfile", UUCONF_CMDTABTYPE_STRING, + offsetof (struct sprocess, zlogfile), NULL }, + { "statfile", UUCONF_CMDTABTYPE_STRING, + offsetof (struct sprocess, zstatsfile), NULL }, + { "debugfile", UUCONF_CMDTABTYPE_STRING, + offsetof (struct sprocess, zdebugfile), NULL }, + { "debug", UUCONF_CMDTABTYPE_STRING, + offsetof (struct sprocess, zdebug), NULL }, + { "max-uuxqts", UUCONF_CMDTABTYPE_INT, + offsetof (struct sprocess, cmaxuuxqts), NULL }, + { "sysfile", UUCONF_CMDTABTYPE_FN | 0, + offsetof (struct sprocess, pzsysfiles), itadd }, + { "portfile", UUCONF_CMDTABTYPE_FN | 0, + offsetof (struct sprocess, pzportfiles), itadd }, + { "dialfile", UUCONF_CMDTABTYPE_FN | 0, + offsetof (struct sprocess, pzdialfiles), itadd }, + { "dialcodefile", UUCONF_CMDTABTYPE_FN | 0, + offsetof (struct sprocess, pzdialcodefiles), itadd }, + { "callfile", UUCONF_CMDTABTYPE_FN | 0, + offsetof (struct sprocess, pzcallfiles), itadd }, + { "passwdfile", UUCONF_CMDTABTYPE_FN | 0, + offsetof (struct sprocess, pzpwdfiles), itadd }, + { "unknown", UUCONF_CMDTABTYPE_FN, offsetof (struct sprocess, qunknown), + itunknown }, + { "v2-files", UUCONF_CMDTABTYPE_BOOLEAN, + offsetof (struct sprocess, fv2), NULL }, + { "hdb-files", UUCONF_CMDTABTYPE_BOOLEAN, + offsetof (struct sprocess, fhdb), NULL }, + { "bnu-files", UUCONF_CMDTABTYPE_BOOLEAN, + offsetof (struct sprocess, fhdb), NULL }, + { "timetable", UUCONF_CMDTABTYPE_FN | 3, + offsetof (struct sprocess, pztimetables), _uuconf_itimetable }, + { NULL, 0, 0, NULL } +}; + +#define CCMDS (sizeof asCmds / sizeof asCmds[0]) + +/* This structure is used to pass information into the command table + functions. */ + +struct sinfo +{ + /* The program name. */ + const char *zname; + /* A pointer to the command table being used, passed to isystem so + it can call uuconf_cmd_args. */ + struct uuconf_cmdtab *qcmds; +}; + +/* Initialize the routines which read the Taylor UUCP configuration + files. */ + +int +uuconf_taylor_init (ppglobal, zprogram, zname) + pointer *ppglobal; + const char *zprogram; + const char *zname; +{ + struct sglobal **pqglobal = (struct sglobal **) ppglobal; + int iret; + char *zcopy; + struct sglobal *qglobal; + boolean fdefault; + FILE *e; + struct sinfo si; + + if (*pqglobal == NULL) + { + iret = _uuconf_iinit_global (pqglobal); + if (iret != UUCONF_SUCCESS) + return iret; + } + + qglobal = *pqglobal; + + if (zname != NULL) + { + size_t csize; + + csize = strlen (zname) + 1; + zcopy = uuconf_malloc (qglobal->pblock, csize); + if (zcopy == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + memcpy ((pointer) zcopy, (pointer) zname, csize); + fdefault = FALSE; + } + else + { + zcopy = uuconf_malloc (qglobal->pblock, + sizeof NEWCONFIGLIB + sizeof CONFIGFILE - 1); + if (zcopy == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + memcpy ((pointer) zcopy, (pointer) NEWCONFIGLIB, + sizeof NEWCONFIGLIB - 1); + memcpy ((pointer) (zcopy + sizeof NEWCONFIGLIB - 1), + (pointer) CONFIGFILE, sizeof CONFIGFILE); + fdefault = TRUE; + } + + qglobal->qprocess->zconfigfile = zcopy; + + e = fopen (zcopy, "r"); + if (e == NULL) + { + if (! fdefault) + { + qglobal->ierrno = errno; + qglobal->zfilename = zcopy; + return (UUCONF_FOPEN_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_ERROR_FILENAME); + } + + /* There is no config file, so just use the default values. */ + } + else + { + struct uuconf_cmdtab as[CCMDS]; + + _uuconf_ucmdtab_base (asCmds, CCMDS, (char *) qglobal->qprocess, + as); + + if (zprogram == NULL) + zprogram = "uucp"; + + si.zname = zprogram; + si.qcmds = as; + iret = uuconf_cmd_file (qglobal, e, as, (pointer) &si, itprogram, + UUCONF_CMDTABFLAG_BACKSLASH, + qglobal->pblock); + + (void) fclose (e); + + if (iret != UUCONF_SUCCESS) + { + qglobal->zfilename = zcopy; + return iret | UUCONF_ERROR_FILENAME; + } + } + + /* Get the defaults for the file names. */ + + iret = itset_default (qglobal, &qglobal->qprocess->pzsysfiles, SYSFILE); + if (iret != UUCONF_SUCCESS) + return iret; + iret = itset_default (qglobal, &qglobal->qprocess->pzportfiles, PORTFILE); + if (iret != UUCONF_SUCCESS) + return iret; + iret = itset_default (qglobal, &qglobal->qprocess->pzdialfiles, DIALFILE); + if (iret != UUCONF_SUCCESS) + return iret; + iret = itset_default (qglobal, &qglobal->qprocess->pzdialcodefiles, + DIALCODEFILE); + if (iret != UUCONF_SUCCESS) + return iret; + iret = itset_default (qglobal, &qglobal->qprocess->pzpwdfiles, PASSWDFILE); + if (iret != UUCONF_SUCCESS) + return iret; + iret = itset_default (qglobal, &qglobal->qprocess->pzcallfiles, CALLFILE); + if (iret != UUCONF_SUCCESS) + return iret; + + return UUCONF_SUCCESS; +} + +/* Add new strings to a variable. */ + +/*ARGSUSED*/ +static int +itadd (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + char ***ppz = (char ***) pvar; + int i; + int iret; + + if (argc == 1) + { + iret = _uuconf_iadd_string (qglobal, NULL, FALSE, FALSE, ppz, + qglobal->pblock); + if (iret != UUCONF_SUCCESS) + return iret; + } + else + { + for (i = 1; i < argc; i++) + { + iret = _uuconf_iadd_string (qglobal, argv[i], TRUE, FALSE, ppz, + qglobal->pblock); + if (iret != UUCONF_SUCCESS) + return iret; + } + } + + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Handle an "unknown" command. We accumulate this into a linked + list, and only parse them later in uuconf_unknown_system_info. */ + +/*ARGSUSED*/ +static int +itunknown (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct sunknown **pq = (struct sunknown **) pvar; + struct sunknown *q; + + q = (struct sunknown *) uuconf_malloc (qglobal->pblock, + sizeof (struct sunknown)); + if (q == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + q->qnext = NULL; + q->ilineno = qglobal->ilineno; + q->cargs = argc - 1; + q->pzargs = (char **) uuconf_malloc (qglobal->pblock, + (argc - 1) * sizeof (char *)); + if (q->pzargs == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + memcpy ((pointer) q->pzargs, (pointer) (argv + 1), + (argc - 1) * sizeof (char *)); + + while (*pq != NULL) + pq = &(*pq)->qnext; + + *pq = q; + + return UUCONF_CMDTABRET_KEEP; +} + +/* If we encounter an unknown command, see if it is the program with + which we were invoked. If it was, pass the remaining arguments + back through the table. */ + +/*ARGSUSED*/ +static int +itprogram (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct sinfo *qinfo = (struct sinfo *) pinfo; + + if (argc <= 1 + || strcasecmp (qinfo->zname, argv[0]) != 0) + return UUCONF_CMDTABRET_CONTINUE; + + return uuconf_cmd_args (pglobal, argc - 1, argv + 1, qinfo->qcmds, + (pointer) NULL, (uuconf_cmdtabfn) NULL, 0, + qglobal->pblock); +} + +/* If a filename was not set by the configuration file, add in the + default value. */ + +static int +itset_default (qglobal, ppzvar, zfile) + struct sglobal *qglobal; + char ***ppzvar; + const char *zfile; +{ + size_t clen; + char *zadd; + + if (*ppzvar != NULL) + return UUCONF_SUCCESS; + + clen = strlen (zfile); + zadd = (char *) uuconf_malloc (qglobal->pblock, + sizeof NEWCONFIGLIB + clen); + if (zadd == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + + memcpy ((pointer) zadd, (pointer) NEWCONFIGLIB, sizeof NEWCONFIGLIB - 1); + memcpy ((pointer) (zadd + sizeof NEWCONFIGLIB - 1), (pointer) zfile, + clen + 1); + + return _uuconf_iadd_string (qglobal, zadd, FALSE, FALSE, ppzvar, + qglobal->pblock); +} diff --git a/gnu/libexec/uucp/libuuconf/tlocnm.c b/gnu/libexec/uucp/libuuconf/tlocnm.c new file mode 100644 index 0000000000..bf09446e8f --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/tlocnm.c @@ -0,0 +1,112 @@ +/* tlocnm.c + Get the local name to use from the Taylor UUCP configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_tlocnm_rcsid[] = "$Id: tlocnm.c,v 1.1 1993/08/04 19:35:14 jtc Exp $"; +#endif + +#include + +/* Get the local name to use, based on the login name, from the Taylor + UUCP configuration files. This could probably be done in a + slightly more intelligent fashion, but no matter what it has to + read the systems files. */ + +int +uuconf_taylor_login_localname (pglobal, zlogin, pzname) + pointer pglobal; + const char *zlogin; + char **pzname; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + char **pznames, **pz; + int iret; + + if (! qglobal->qprocess->fread_syslocs) + { + iret = _uuconf_iread_locations (qglobal); + if (iret != UUCONF_SUCCESS) + return iret; + } + + /* As a simple optimization, if there is no "myname" command we can + simply return immediately. */ + if (! qglobal->qprocess->fuses_myname) + { + *pzname = NULL; + return UUCONF_NOT_FOUND; + } + + iret = uuconf_taylor_system_names (pglobal, &pznames, 0); + if (iret != UUCONF_SUCCESS) + return iret; + + *pzname = NULL; + iret = UUCONF_NOT_FOUND; + + for (pz = pznames; *pz != NULL; pz++) + { + struct uuconf_system ssys; + struct uuconf_system *qsys; + + iret = uuconf_system_info (pglobal, *pz, &ssys); + if (iret != UUCONF_SUCCESS) + break; + + for (qsys = &ssys; qsys != NULL; qsys = qsys->uuconf_qalternate) + { + if (qsys->uuconf_zlocalname != NULL + && qsys->uuconf_fcalled + && qsys->uuconf_zcalled_login != NULL + && strcmp (qsys->uuconf_zcalled_login, zlogin) == 0) + { + *pzname = strdup (qsys->uuconf_zlocalname); + if (*pzname != NULL) + iret = UUCONF_SUCCESS; + else + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + break; + } + } + + (void) uuconf_system_free (pglobal, &ssys); + + if (qsys != NULL) + break; + + iret = UUCONF_NOT_FOUND; + } + + for (pz = pznames; *pz != NULL; pz++) + free ((pointer) *pz); + free ((pointer) pznames); + + return iret; +} diff --git a/gnu/libexec/uucp/libuuconf/tport.c b/gnu/libexec/uucp/libuuconf/tport.c new file mode 100644 index 0000000000..0a94a81c11 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/tport.c @@ -0,0 +1,295 @@ +/* tport.c + Find a port in the Taylor UUCP configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_tport_rcsid[] = "$Id: tport.c,v 1.1 1993/08/04 19:35:15 jtc Exp $"; +#endif + +#include + +static int ipport P((pointer pglobal, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int ipunknown P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); + +/* Find a port in the Taylor UUCP configuration files by name, baud + rate, and special purpose function. */ + +int +uuconf_taylor_find_port (pglobal, zname, ibaud, ihighbaud, pifn, pinfo, + qport) + pointer pglobal; + const char *zname; + long ibaud; + long ihighbaud; + int (*pifn) P((struct uuconf_port *, pointer)); + pointer pinfo; + struct uuconf_port *qport; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + FILE *e; + pointer pblock; + char *zfree; + int iret; + char **pz; + + if (ihighbaud == 0L) + ihighbaud = ibaud; + + e = NULL; + pblock = NULL; + zfree = NULL; + iret = UUCONF_NOT_FOUND; + + for (pz = qglobal->qprocess->pzportfiles; *pz != NULL; pz++) + { + struct uuconf_cmdtab as[2]; + char *zport; + struct uuconf_port sdefault; + int ilineno; + + e = fopen (*pz, "r"); + if (e == NULL) + { + if (FNO_SUCH_FILE ()) + continue; + qglobal->ierrno = errno; + iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + qglobal->ilineno = 0; + + /* Gather the default information from the top of the file. We + do this by handling the "port" command ourselves and passing + every other command to _uuconf_iport_cmd via ipunknown. The + value of zport will be an malloc block. */ + as[0].uuconf_zcmd = "port"; + as[0].uuconf_itype = UUCONF_CMDTABTYPE_FN | 2; + as[0].uuconf_pvar = (pointer) &zport; + as[0].uuconf_pifn = ipport; + + as[1].uuconf_zcmd = NULL; + + pblock = uuconf_malloc_block (); + if (pblock == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + _uuconf_uclear_port (&sdefault); + sdefault.uuconf_palloc = pblock; + zport = NULL; + iret = uuconf_cmd_file (pglobal, e, as, (pointer) &sdefault, + ipunknown, UUCONF_CMDTABFLAG_BACKSLASH, + pblock); + if (iret != UUCONF_SUCCESS) + { + zfree = zport; + break; + } + + /* Now skip until we find a port with a matching name. If the + zname argument is NULL, we will have to read every port. */ + iret = UUCONF_NOT_FOUND; + while (zport != NULL) + { + uuconf_cmdtabfn piunknown; + boolean fmatch; + + if (zname == NULL || strcmp (zname, zport) == 0) + { + piunknown = ipunknown; + *qport = sdefault; + qport->uuconf_zname = zport; + zfree = zport; + fmatch = TRUE; + } + else + { + piunknown = NULL; + free ((pointer) zport); + fmatch = FALSE; + } + + zport = NULL; + ilineno = qglobal->ilineno; + iret = uuconf_cmd_file (pglobal, e, as, (pointer) qport, + piunknown, UUCONF_CMDTABFLAG_BACKSLASH, + pblock); + qglobal->ilineno += ilineno; + if (iret != UUCONF_SUCCESS) + break; + iret = UUCONF_NOT_FOUND; + + /* We may have just gathered information about a port. See + if it matches the name, the baud rate and the special + function. */ + if (fmatch) + { + if (ibaud != 0) + { + if (qport->uuconf_ttype == UUCONF_PORTTYPE_MODEM) + { + long imbaud, imhigh, imlow; + + imbaud = qport->uuconf_u.uuconf_smodem.uuconf_ibaud; + imhigh = qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud; + imlow = qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud; + + if (imbaud == 0 && imlow == 0) + ; + else if (ibaud <= imbaud && imbaud <= ihighbaud) + ; + else if (imlow != 0 + && imlow <= ihighbaud + && imhigh >= ibaud) + ; + else + fmatch = FALSE; + } + else if (qport->uuconf_ttype == UUCONF_PORTTYPE_DIRECT) + { + long idbaud; + + idbaud = qport->uuconf_u.uuconf_sdirect.uuconf_ibaud; + if (idbaud != 0 && idbaud != ibaud) + fmatch = FALSE; + } + } + } + + if (fmatch) + { + if (pifn != NULL) + { + iret = (*pifn) (qport, pinfo); + if (iret == UUCONF_NOT_FOUND) + fmatch = FALSE; + else if (iret != UUCONF_SUCCESS) + break; + } + } + + if (fmatch) + { + if (uuconf_add_block (pblock, zfree) == 0) + { + zfree = NULL; + iret = UUCONF_SUCCESS; + } + else + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + break; + } + + if (zfree != NULL) + { + free ((pointer) zfree); + zfree = NULL; + } + } + + (void) fclose (e); + e = NULL; + + if (iret != UUCONF_NOT_FOUND) + break; + + uuconf_free_block (pblock); + pblock = NULL; + } + + if (e != NULL) + (void) fclose (e); + if (zfree != NULL) + free ((pointer) zfree); + if (iret != UUCONF_SUCCESS && pblock != NULL) + uuconf_free_block (pblock); + + if (iret != UUCONF_SUCCESS && iret != UUCONF_NOT_FOUND) + { + qglobal->zfilename = *pz; + iret |= UUCONF_ERROR_FILENAME; + } + + return iret; +} + +/* Handle a "port" command. This copies the string onto the heap and + returns the pointer in *pvar. It returns UUCONF_CMDTABRET_EXIT to + force uuconf_cmd_file to stop reading and return to the code above, + which will then check the port just read to see if it matches. */ + +/*ARGSUSED*/ +static int +ipport (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + char **pz = (char **) pvar; + size_t csize; + + csize = strlen (argv[1]) + 1; + *pz = malloc (csize); + if (*pz == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + memcpy ((pointer) *pz, (pointer) argv[1], csize); + return UUCONF_CMDTABRET_EXIT; +} + +/* Handle an unknown command by passing it on to _uuconf_iport_cmd, + which will parse it into the port structure. */ + +/*ARGSUSED*/ +static int +ipunknown (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct uuconf_port *qport = (struct uuconf_port *) pinfo; + + return _uuconf_iport_cmd (qglobal, argc, argv, qport); +} diff --git a/gnu/libexec/uucp/libuuconf/tportc.c b/gnu/libexec/uucp/libuuconf/tportc.c new file mode 100644 index 0000000000..7b796eacb0 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/tportc.c @@ -0,0 +1,465 @@ +/* tportc.c + Handle a Taylor UUCP port command. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_tportc_rcsid[] = "$Id: tportc.c,v 1.1 1993/08/04 19:35:16 jtc Exp $"; +#endif + +#include + +static int ipproto_param P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); +static int ipbaud_range P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); +static int ipdialer P((pointer pglobal, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int ipcunknown P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); + +/* The string names of the port types. This array corresponds to the + uuconf_porttype enumeration. */ + +static const char * const azPtype_names[] = +{ + NULL, + "stdin", + "modem", + "direct", + "tcp", + "tli" +}; + +#define CPORT_TYPES (sizeof azPtype_names / sizeof azPtype_names[0]) + +/* The command table for generic port commands. The "port" and "type" + commands are handled specially. */ +static const struct cmdtab_offset asPort_cmds[] = +{ + { "protocol", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_port, uuconf_zprotocols), NULL }, + { "protocol-parameter", UUCONF_CMDTABTYPE_FN | 0, + offsetof (struct uuconf_port, uuconf_qproto_params), ipproto_param }, + { "seven-bit", UUCONF_CMDTABTYPE_FN | 2, + offsetof (struct uuconf_port, uuconf_ireliable), _uuconf_iseven_bit }, + { "reliable", UUCONF_CMDTABTYPE_FN | 2, + offsetof (struct uuconf_port, uuconf_ireliable), _uuconf_ireliable }, + { "half-duplex", UUCONF_CMDTABTYPE_FN | 2, + offsetof (struct uuconf_port, uuconf_ireliable), + _uuconf_ihalf_duplex }, + { "lockname", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_port, uuconf_zlockname), NULL }, + { NULL, 0, 0, NULL } +}; + +#define CPORT_CMDS (sizeof asPort_cmds / sizeof asPort_cmds[0]) + +/* The stdin port command table. */ +static const struct cmdtab_offset asPstdin_cmds[] = +{ + { NULL, 0, 0, NULL } +}; + +#define CSTDIN_CMDS (sizeof asPstdin_cmds / sizeof asPstdin_cmds[0]) + +/* The modem port command table. */ +static const struct cmdtab_offset asPmodem_cmds[] = +{ + { "device", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_port, uuconf_u.uuconf_smodem.uuconf_zdevice), + NULL }, + { "baud", UUCONF_CMDTABTYPE_LONG, + offsetof (struct uuconf_port, uuconf_u.uuconf_smodem.uuconf_ibaud), + NULL }, + { "speed", UUCONF_CMDTABTYPE_LONG, + offsetof (struct uuconf_port, uuconf_u.uuconf_smodem.uuconf_ibaud), + NULL }, + { "baud-range", UUCONF_CMDTABTYPE_FN | 3, + offsetof (struct uuconf_port, uuconf_u.uuconf_smodem), ipbaud_range }, + { "speed-range", UUCONF_CMDTABTYPE_FN | 3, + offsetof (struct uuconf_port, uuconf_u.uuconf_smodem), ipbaud_range }, + { "carrier", UUCONF_CMDTABTYPE_BOOLEAN, + offsetof (struct uuconf_port, uuconf_u.uuconf_smodem.uuconf_fcarrier), + NULL }, + { "dial-device", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_port, + uuconf_u.uuconf_smodem.uuconf_zdial_device), + NULL }, + { "dialer", UUCONF_CMDTABTYPE_FN | 0, + offsetof (struct uuconf_port, uuconf_u.uuconf_smodem), ipdialer }, + { "dialer-sequence", UUCONF_CMDTABTYPE_FULLSTRING, + offsetof (struct uuconf_port, uuconf_u.uuconf_smodem.uuconf_pzdialer), + NULL }, + { NULL, 0, 0, NULL } +}; + +#define CMODEM_CMDS (sizeof asPmodem_cmds / sizeof asPmodem_cmds[0]) + +/* The direct port command table. */ +static const struct cmdtab_offset asPdirect_cmds[] = +{ + { "device", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_port, uuconf_u.uuconf_sdirect.uuconf_zdevice), + NULL }, + { "baud", UUCONF_CMDTABTYPE_LONG, + offsetof (struct uuconf_port, uuconf_u.uuconf_sdirect.uuconf_ibaud), + NULL }, + { "speed", UUCONF_CMDTABTYPE_LONG, + offsetof (struct uuconf_port, uuconf_u.uuconf_sdirect.uuconf_ibaud), + NULL }, + { NULL, 0, 0, NULL } +}; + +#define CDIRECT_CMDS (sizeof asPdirect_cmds / sizeof asPdirect_cmds[0]) + +/* The TCP port command table. */ +static const struct cmdtab_offset asPtcp_cmds[] = +{ + { "service", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_port, uuconf_u.uuconf_stcp.uuconf_zport), + NULL }, + { NULL, 0, 0, NULL } +}; + +#define CTCP_CMDS (sizeof asPtcp_cmds / sizeof asPtcp_cmds[0]) + +/* The TLI port command table. */ +static const struct cmdtab_offset asPtli_cmds[] = +{ + { "device", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_port, uuconf_u.uuconf_stli.uuconf_zdevice), + NULL }, + { "stream", UUCONF_CMDTABTYPE_BOOLEAN, + offsetof (struct uuconf_port, uuconf_u.uuconf_stli.uuconf_fstream), + NULL }, + { "push", UUCONF_CMDTABTYPE_FULLSTRING, + offsetof (struct uuconf_port, uuconf_u.uuconf_stli.uuconf_pzpush), + NULL }, + { "dialer-sequence", UUCONF_CMDTABTYPE_FULLSTRING, + offsetof (struct uuconf_port, uuconf_u.uuconf_stli.uuconf_pzdialer), + NULL }, + { "server-address", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_port, uuconf_u.uuconf_stli.uuconf_zservaddr), + NULL }, + { NULL, 0, 0, NULL } +}; + +#define CTLI_CMDS (sizeof asPtli_cmds / sizeof asPtli_cmds[0]) + +#undef max +#define max(i1, i2) ((i1) > (i2) ? (i1) : (i2)) +#define CCMDS \ + max (max (max (CPORT_CMDS, CSTDIN_CMDS), CMODEM_CMDS), \ + max (max (CDIRECT_CMDS, CTCP_CMDS), CTLI_CMDS)) + +/* Handle a command passed to a port from a Taylor UUCP configuration + file. This can be called when reading either the port file or the + sys file. The return value may have UUCONF_CMDTABRET_KEEP set, but + not UUCONF_CMDTABRET_EXIT. It assigns values to the elements of + qport. The first time this is called, qport->uuconf_zname and + qport->uuconf_palloc should be set and qport->uuconf_ttype should + be UUCONF_PORTTYPE_UNKNOWN. */ + +int +_uuconf_iport_cmd (qglobal, argc, argv, qport) + struct sglobal *qglobal; + int argc; + char **argv; + struct uuconf_port *qport; +{ + boolean fgottype; + const struct cmdtab_offset *qcmds; + size_t ccmds; + struct uuconf_cmdtab as[CCMDS]; + int i; + int iret; + + fgottype = strcasecmp (argv[0], "type") == 0; + + if (fgottype || qport->uuconf_ttype == UUCONF_PORTTYPE_UNKNOWN) + { + enum uuconf_porttype ttype; + + /* We either just got a "type" command, or this is an + uninitialized port. If the first command to a port is not + "type", it is assumed to be a modem port. This + implementation will actually permit "type" at any point, but + will effectively discard any type specific information that + appears before the "type" command. This supports defaults, + in that the default may be of a specific type while future + ports in the same file may be of other types. */ + if (! fgottype) + ttype = UUCONF_PORTTYPE_MODEM; + else + { + if (argc != 2) + return UUCONF_SYNTAX_ERROR; + + for (i = 0; i < CPORT_TYPES; i++) + if (azPtype_names[i] != NULL + && strcasecmp (argv[1], azPtype_names[i]) == 0) + break; + + if (i >= CPORT_TYPES) + return UUCONF_SYNTAX_ERROR; + + ttype = (enum uuconf_porttype) i; + } + + qport->uuconf_ttype = ttype; + + switch (ttype) + { + default: + case UUCONF_PORTTYPE_STDIN: + break; + case UUCONF_PORTTYPE_MODEM: + qport->uuconf_u.uuconf_smodem.uuconf_zdevice = NULL; + qport->uuconf_u.uuconf_smodem.uuconf_zdial_device = NULL; + qport->uuconf_u.uuconf_smodem.uuconf_ibaud = 0L; + qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud = 0L; + qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud = 0L; + qport->uuconf_u.uuconf_smodem.uuconf_fcarrier = TRUE; + qport->uuconf_u.uuconf_smodem.uuconf_pzdialer = NULL; + qport->uuconf_u.uuconf_smodem.uuconf_qdialer = NULL; + break; + case UUCONF_PORTTYPE_DIRECT: + qport->uuconf_u.uuconf_sdirect.uuconf_zdevice = NULL; + qport->uuconf_u.uuconf_sdirect.uuconf_ibaud = -1; + break; + case UUCONF_PORTTYPE_TCP: + qport->uuconf_u.uuconf_stcp.uuconf_zport = (char *) "uucp"; + qport->uuconf_ireliable = (UUCONF_RELIABLE_SPECIFIED + | UUCONF_RELIABLE_ENDTOEND + | UUCONF_RELIABLE_RELIABLE + | UUCONF_RELIABLE_EIGHT + | UUCONF_RELIABLE_FULLDUPLEX); + break; + case UUCONF_PORTTYPE_TLI: + qport->uuconf_u.uuconf_stli.uuconf_zdevice = NULL; + qport->uuconf_u.uuconf_stli.uuconf_fstream = FALSE; + qport->uuconf_u.uuconf_stli.uuconf_pzpush = NULL; + qport->uuconf_u.uuconf_stli.uuconf_pzdialer = NULL; + qport->uuconf_u.uuconf_stli.uuconf_zservaddr = NULL; + qport->uuconf_ireliable = (UUCONF_RELIABLE_SPECIFIED + | UUCONF_RELIABLE_ENDTOEND + | UUCONF_RELIABLE_RELIABLE + | UUCONF_RELIABLE_EIGHT + | UUCONF_RELIABLE_FULLDUPLEX); + break; + } + + if (fgottype) + return UUCONF_CMDTABRET_CONTINUE; + } + + /* See if this command is one of the generic ones. */ + qcmds = asPort_cmds; + ccmds = CPORT_CMDS; + + for (i = 0; i < CPORT_CMDS - 1; i++) + if (strcasecmp (argv[0], asPort_cmds[i].zcmd) == 0) + break; + + if (i >= CPORT_CMDS - 1) + { + /* It's not a generic command, so we must check the type + specific commands. */ + switch (qport->uuconf_ttype) + { + case UUCONF_PORTTYPE_STDIN: + qcmds = asPstdin_cmds; + ccmds = CSTDIN_CMDS; + break; + case UUCONF_PORTTYPE_MODEM: + qcmds = asPmodem_cmds; + ccmds = CMODEM_CMDS; + break; + case UUCONF_PORTTYPE_DIRECT: + qcmds = asPdirect_cmds; + ccmds = CDIRECT_CMDS; + break; + case UUCONF_PORTTYPE_TCP: + qcmds = asPtcp_cmds; + ccmds = CTCP_CMDS; + break; + case UUCONF_PORTTYPE_TLI: + qcmds = asPtli_cmds; + ccmds = CTLI_CMDS; + break; + default: + return UUCONF_SYNTAX_ERROR; + } + } + + /* Copy the command table onto the stack and modify it to point to + qport. */ + _uuconf_ucmdtab_base (qcmds, ccmds, (char *) qport, as); + + iret = uuconf_cmd_args ((pointer) qglobal, argc, argv, as, + (pointer) qport, ipcunknown, 0, + qport->uuconf_palloc); + + return iret &~ UUCONF_CMDTABRET_EXIT; +} + +/* Handle the "protocol-parameter" command. */ + +static int +ipproto_param (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct uuconf_proto_param **pqparam = (struct uuconf_proto_param **) pvar; + struct uuconf_port *qport = (struct uuconf_port *) pinfo; + + return _uuconf_iadd_proto_param (qglobal, argc - 1, argv + 1, pqparam, + qport->uuconf_palloc); +} + +/* Handle the "baud-range" command. */ + +/*ARGSUSED*/ +static int +ipbaud_range (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct uuconf_modem_port *qmodem = (struct uuconf_modem_port *) pvar; + int iret; + + iret = _uuconf_iint (qglobal, argv[1], + (pointer) &qmodem->uuconf_ilowbaud, FALSE); + if ((iret &~ UUCONF_CMDTABRET_KEEP) != UUCONF_SUCCESS) + return iret; + + iret |= _uuconf_iint (qglobal, argv[2], + (pointer) &qmodem->uuconf_ihighbaud, FALSE); + + return iret; +} + +/* Handle the "dialer" command. If there is one argument, this names + a dialer. Otherwise, the remaining arguments form a command + describing the dialer. */ + +static int +ipdialer (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct uuconf_modem_port *qmodem = (struct uuconf_modem_port *) pvar; + struct uuconf_port *qport = (struct uuconf_port *) pinfo; + int iret; + + if (argc < 2) + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; + + if (argc > 2) + { + if (qmodem->uuconf_qdialer == NULL) + { + struct uuconf_dialer *qnew; + size_t clen; + + qnew = ((struct uuconf_dialer *) + uuconf_malloc (qport->uuconf_palloc, + sizeof (struct uuconf_dialer))); + if (qnew == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + + _uuconf_uclear_dialer (qnew); + + clen = strlen (qport->uuconf_zname); + qnew->uuconf_zname = (char *) uuconf_malloc (qport->uuconf_palloc, + (clen + + sizeof " dialer")); + if (qnew->uuconf_zname == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + + memcpy ((pointer) qnew->uuconf_zname, + (pointer) qport->uuconf_zname, clen); + memcpy ((pointer) (qnew->uuconf_zname + clen), (pointer) " dialer", + sizeof " dialer"); + + qnew->uuconf_palloc = qport->uuconf_palloc; + + qmodem->uuconf_qdialer = qnew; + } + + iret = _uuconf_idialer_cmd (qglobal, argc - 1, argv + 1, + qmodem->uuconf_qdialer); + if ((iret &~ UUCONF_CMDTABRET_KEEP) != UUCONF_SUCCESS) + iret |= UUCONF_CMDTABRET_EXIT; + return iret; + } + else + { + qmodem->uuconf_pzdialer = NULL; + iret = _uuconf_iadd_string (qglobal, argv[1], TRUE, FALSE, + &qmodem->uuconf_pzdialer, + qport->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + iret |= UUCONF_CMDTABRET_EXIT; + return iret; + } +} + +/* Give an error for an unknown port command. */ + +/*ARGSUSED*/ +static int +ipcunknown (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; +} diff --git a/gnu/libexec/uucp/libuuconf/tsinfo.c b/gnu/libexec/uucp/libuuconf/tsinfo.c new file mode 100644 index 0000000000..5e31ca2c2d --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/tsinfo.c @@ -0,0 +1,922 @@ +/* tsinfo.c + Get information about a system from the Taylor UUCP configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_tsinfo_rcsid[] = "$Id: tsinfo.c,v 1.1 1993/08/04 19:35:18 jtc Exp $"; +#endif + +#include +#include + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +static void uiset_call P((struct uuconf_system *qsys)); +static int iisizecmp P((long i1, long i2)); + +/* Local functions needed to parse the system information file. */ + +#define CMDTABFN(z) \ + static int z P((pointer, int, char **, pointer, pointer)) + +CMDTABFN (iisystem); +CMDTABFN (iialias); +CMDTABFN (iialternate); +CMDTABFN (iidefault_alternates); +CMDTABFN (iitime); +CMDTABFN (iitimegrade); +CMDTABFN (iisize); +CMDTABFN (iibaud_range); +CMDTABFN (iiport); +CMDTABFN (iichat); +CMDTABFN (iicalled_login); +CMDTABFN (iiproto_param); +CMDTABFN (iirequest); +CMDTABFN (iitransfer); +CMDTABFN (iiforward); +CMDTABFN (iiunknown); + +#undef CMDTABFN + +/* We have to pass a fair amount of information in and out of the + various system commands. Using global variables would make the + code non-reentrant, so we instead pass a pointer to single + structure as the pinfo argument to the system commands. */ + +struct sinfo +{ + /* The system information we're building up. */ + struct uuconf_system *qsys; + /* Whether any alternates have been used. */ + boolean falternates; + /* A list of the previous alternates. */ + struct uuconf_system salternate; + /* Whether to use extra alternates from the file wide defaults. */ + int fdefault_alternates; +}; + +/* The command table for system commands. */ +static const struct cmdtab_offset asIcmds[] = +{ + { "system", UUCONF_CMDTABTYPE_FN | 2, (size_t) -1, iisystem }, + { "alias", UUCONF_CMDTABTYPE_FN | 2, (size_t) -1, iialias }, + { "alternate", UUCONF_CMDTABTYPE_FN | 0, (size_t) -1, iialternate }, + { "default-alternates", UUCONF_CMDTABTYPE_FN | 2, (size_t) -1, + iidefault_alternates }, + { "time", UUCONF_CMDTABTYPE_FN | 0, + offsetof (struct uuconf_system, uuconf_qtimegrade), iitime }, + { "timegrade", UUCONF_CMDTABTYPE_FN | 0, + offsetof (struct uuconf_system, uuconf_qtimegrade), iitimegrade }, + { "max-retries", UUCONF_CMDTABTYPE_INT, + offsetof (struct uuconf_system, uuconf_cmax_retries), NULL }, + { "success-wait", UUCONF_CMDTABTYPE_INT, + offsetof (struct uuconf_system, uuconf_csuccess_wait), NULL }, + { "call-timegrade", UUCONF_CMDTABTYPE_FN | 3, + offsetof (struct uuconf_system, uuconf_qcalltimegrade), iitimegrade }, + { "call-local-size", UUCONF_CMDTABTYPE_FN | 3, + offsetof (struct uuconf_system, uuconf_qcall_local_size), iisize }, + { "call-remote-size", UUCONF_CMDTABTYPE_FN | 3, + offsetof (struct uuconf_system, uuconf_qcall_remote_size), iisize }, + { "called-local-size", UUCONF_CMDTABTYPE_FN | 3, + offsetof (struct uuconf_system, uuconf_qcalled_local_size), iisize }, + { "called-remote-size", UUCONF_CMDTABTYPE_FN | 3, + offsetof (struct uuconf_system, uuconf_qcalled_remote_size), iisize }, + { "timetable", UUCONF_CMDTABTYPE_FN | 3, (size_t) -1, _uuconf_itimetable }, + { "baud", UUCONF_CMDTABTYPE_LONG, + offsetof (struct uuconf_system, uuconf_ibaud), NULL }, + { "speed", UUCONF_CMDTABTYPE_LONG, + offsetof (struct uuconf_system, uuconf_ibaud), NULL }, + { "baud-range", UUCONF_CMDTABTYPE_FN | 3, 0, iibaud_range }, + { "speed-range", UUCONF_CMDTABTYPE_FN | 3, 0, iibaud_range }, + { "port", UUCONF_CMDTABTYPE_FN | 0, (size_t) -1, iiport }, + { "phone", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_system, uuconf_zphone), NULL }, + { "address", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_system, uuconf_zphone), NULL }, + { "chat", UUCONF_CMDTABTYPE_PREFIX | 0, + offsetof (struct uuconf_system, uuconf_schat), iichat }, + { "call-login", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_system, uuconf_zcall_login), NULL }, + { "call-password", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_system, uuconf_zcall_password), NULL }, + { "called-login", UUCONF_CMDTABTYPE_FN | 0, + offsetof (struct uuconf_system, uuconf_zcalled_login), iicalled_login }, + { "callback", UUCONF_CMDTABTYPE_BOOLEAN, + offsetof (struct uuconf_system, uuconf_fcallback), NULL }, + { "sequence", UUCONF_CMDTABTYPE_BOOLEAN, + offsetof (struct uuconf_system, uuconf_fsequence), NULL }, + { "protocol", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_system, uuconf_zprotocols), NULL }, + { "protocol-parameter", UUCONF_CMDTABTYPE_FN | 0, + offsetof (struct uuconf_system, uuconf_qproto_params), iiproto_param }, + { "called-chat", UUCONF_CMDTABTYPE_PREFIX | 0, + offsetof (struct uuconf_system, uuconf_scalled_chat), iichat }, + { "debug", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_system, uuconf_zdebug), NULL }, + { "max-remote-debug", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_system, uuconf_zmax_remote_debug), NULL }, + { "send-request", UUCONF_CMDTABTYPE_BOOLEAN, + offsetof (struct uuconf_system, uuconf_fsend_request), NULL }, + { "receive-request", UUCONF_CMDTABTYPE_BOOLEAN, + offsetof (struct uuconf_system, uuconf_frec_request), NULL }, + { "request", UUCONF_CMDTABTYPE_FN | 2, (size_t) -1, iirequest }, + { "call-transfer", UUCONF_CMDTABTYPE_BOOLEAN, + offsetof (struct uuconf_system, uuconf_fcall_transfer), NULL }, + { "called-transfer", UUCONF_CMDTABTYPE_BOOLEAN, + offsetof (struct uuconf_system, uuconf_fcalled_transfer), NULL }, + { "transfer", UUCONF_CMDTABTYPE_FN | 2, (size_t) -1, iitransfer }, + { "local-send", UUCONF_CMDTABTYPE_FULLSTRING, + offsetof (struct uuconf_system, uuconf_pzlocal_send), NULL }, + { "remote-send", UUCONF_CMDTABTYPE_FULLSTRING, + offsetof (struct uuconf_system, uuconf_pzremote_send), NULL }, + { "local-receive", UUCONF_CMDTABTYPE_FULLSTRING, + offsetof (struct uuconf_system, uuconf_pzlocal_receive), NULL }, + { "remote-receive", UUCONF_CMDTABTYPE_FULLSTRING, + offsetof (struct uuconf_system, uuconf_pzremote_receive), NULL }, + { "command-path", UUCONF_CMDTABTYPE_FULLSTRING, + offsetof (struct uuconf_system, uuconf_pzpath), NULL }, + { "commands", UUCONF_CMDTABTYPE_FULLSTRING, + offsetof (struct uuconf_system, uuconf_pzcmds), NULL }, + { "free-space", UUCONF_CMDTABTYPE_LONG, + offsetof (struct uuconf_system, uuconf_cfree_space), NULL }, + { "forward-from", UUCONF_CMDTABTYPE_FULLSTRING, + offsetof (struct uuconf_system, uuconf_pzforward_from), NULL }, + { "forward-to", UUCONF_CMDTABTYPE_FULLSTRING, + offsetof (struct uuconf_system, uuconf_pzforward_to), NULL }, + { "forward", UUCONF_CMDTABTYPE_FN | 0, (size_t) -1, iiforward }, + { "pubdir", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_system, uuconf_zpubdir), NULL }, + { "myname", UUCONF_CMDTABTYPE_STRING, + offsetof (struct uuconf_system, uuconf_zlocalname), NULL }, + { NULL, 0, 0, NULL } +}; + +#define CSYSTEM_CMDS (sizeof asIcmds / sizeof asIcmds[0]) + +/* Get information about the system zsystem from the Taylor UUCP + configuration files. Sets *qsys. This does not ensure that all + default information is set. */ + +int +_uuconf_itaylor_system_internal (qglobal, zsystem, qsys) + struct sglobal *qglobal; + const char *zsystem; + struct uuconf_system *qsys; +{ + int iret; + struct stsysloc *qloc; + struct uuconf_cmdtab as[CSYSTEM_CMDS]; + struct sinfo si; + struct uuconf_system sdefaults; + + if (! qglobal->qprocess->fread_syslocs) + { + iret = _uuconf_iread_locations (qglobal); + if (iret != UUCONF_SUCCESS) + return iret; + } + + /* Find the system in the list of locations. */ + for (qloc = qglobal->qprocess->qsyslocs; qloc != NULL; qloc = qloc->qnext) + if (qloc->zname[0] == zsystem[0] + && strcmp (qloc->zname, zsystem) == 0) + break; + if (qloc == NULL) + return UUCONF_NOT_FOUND; + + /* If this is an alias, then the real system is the next non-alias + in the list. */ + while (qloc->falias) + { + qloc = qloc->qnext; + if (qloc == NULL) + return UUCONF_NOT_FOUND; + } + + _uuconf_ucmdtab_base (asIcmds, CSYSTEM_CMDS, (char *) qsys, as); + + rewind (qloc->e); + + /* Read the file wide defaults from the start of the file. */ + _uuconf_uclear_system (qsys); + + si.qsys = qsys; + si.falternates = FALSE; + si.fdefault_alternates = TRUE; + qsys->uuconf_palloc = uuconf_malloc_block (); + if (qsys->uuconf_palloc == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + + iret = uuconf_cmd_file ((pointer) qglobal, qloc->e, as, (pointer) &si, + iiunknown, UUCONF_CMDTABFLAG_BACKSLASH, + qsys->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + { + qglobal->zfilename = qloc->zfile; + return iret | UUCONF_ERROR_FILENAME; + } + + if (! si.falternates) + uiset_call (qsys); + else + { + /* Attach the final alternate. */ + iret = iialternate ((pointer) qglobal, 0, (char **) NULL, + (pointer) NULL, (pointer) &si); + if (iret != UUCONF_SUCCESS) + return iret; + } + + /* Save off the defaults. */ + sdefaults = *qsys; + + /* Advance to the information for the system we want. */ + if (fseek (qloc->e, qloc->iloc, SEEK_SET) != 0) + { + qglobal->ierrno = errno; + qglobal->zfilename = qloc->zfile; + return (UUCONF_FSEEK_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_ERROR_FILENAME); + } + + /* Read in the system we want. */ + _uuconf_uclear_system (qsys); + qsys->uuconf_zname = (char *) qloc->zname; + qsys->uuconf_palloc = sdefaults.uuconf_palloc; + + si.falternates = FALSE; + + iret = uuconf_cmd_file (qglobal, qloc->e, as, (pointer) &si, iiunknown, + UUCONF_CMDTABFLAG_BACKSLASH, qsys->uuconf_palloc); + qglobal->ilineno += qloc->ilineno; + + if (iret == UUCONF_SUCCESS) + { + if (! si.falternates) + uiset_call (qsys); + else + iret = iialternate ((pointer) qglobal, 0, (char **) NULL, + (pointer) NULL, (pointer) &si); + } + + /* Merge in the defaults. */ + if (iret == UUCONF_SUCCESS) + iret = _uuconf_isystem_default (qglobal, qsys, &sdefaults, + si.fdefault_alternates); + + /* The first alternate is always available for calling in. */ + if (iret == UUCONF_SUCCESS) + qsys->uuconf_fcalled = TRUE; + + if (iret != UUCONF_SUCCESS) + { + qglobal->zfilename = qloc->zfile; + iret |= UUCONF_ERROR_FILENAME; + } + + return iret; +} + +/* Set the fcall and fcalled field for the system. This marks a + particular alternate for use when calling out or calling in. This + is where we implement the semantics described in the documentation: + a change to a relevant field implies that the alternate is used. + If all the relevant fields are unchanged, the alternate is not + used. */ + +static void +uiset_call (qsys) + struct uuconf_system *qsys; +{ + qsys->uuconf_fcall = + (qsys->uuconf_qtimegrade != (struct uuconf_timespan *) &_uuconf_unset + || qsys->uuconf_zport != (char *) &_uuconf_unset + || qsys->uuconf_qport != (struct uuconf_port *) &_uuconf_unset + || qsys->uuconf_ibaud >= 0 + || qsys->uuconf_zphone != (char *) &_uuconf_unset + || qsys->uuconf_schat.uuconf_pzchat != (char **) &_uuconf_unset + || qsys->uuconf_schat.uuconf_pzprogram != (char **) &_uuconf_unset); + + qsys->uuconf_fcalled = + qsys->uuconf_zcalled_login != (char *) &_uuconf_unset; +} + +/* Handle the "system" command. Because we skip directly to the + system we want to read, a "system" command means we've reached the + end of it. */ + +static int +iisystem (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + return UUCONF_CMDTABRET_EXIT; +} + +/* Handle the "alias" command. */ + +/*ARGSUSED*/ +static int +iialias (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct sinfo *qinfo = (struct sinfo *) pinfo; + int iret; + + iret = _uuconf_iadd_string (qglobal, argv[1], TRUE, FALSE, + &qinfo->qsys->uuconf_pzalias, + qinfo->qsys->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + iret |= UUCONF_CMDTABRET_EXIT; + return iret; +} + +/* Handle the "alternate" command. The information just read is in + sIhold. If this is the first "alternate" command for this system, + we save off the current information in sIalternate. Otherwise we + default this information to sIalternate, and then add it to the end + of the list of alternates in sIalternate. */ + +static int +iialternate (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct sinfo *qinfo = (struct sinfo *) pinfo; + + uiset_call (qinfo->qsys); + + if (! qinfo->falternates) + { + qinfo->salternate = *qinfo->qsys; + qinfo->falternates = TRUE; + } + else + { + int iret; + struct uuconf_system *qnew, **pq; + + iret = _uuconf_isystem_default (qglobal, qinfo->qsys, + &qinfo->salternate, FALSE); + if (iret != UUCONF_SUCCESS) + return iret | UUCONF_CMDTABRET_EXIT; + qnew = ((struct uuconf_system *) + uuconf_malloc (qinfo->qsys->uuconf_palloc, + sizeof (struct uuconf_system))); + if (qnew == NULL) + { + qglobal->ierrno = errno;; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + *qnew = *qinfo->qsys; + for (pq = &qinfo->salternate.uuconf_qalternate; + *pq != NULL; + pq = &(*pq)->uuconf_qalternate) + ; + *pq = qnew; + } + + /* If this is the last alternate command, move the information back + to qinfo->qsys. */ + if (argc == 0) + *qinfo->qsys = qinfo->salternate; + else + { + _uuconf_uclear_system (qinfo->qsys); + qinfo->qsys->uuconf_zname = qinfo->salternate.uuconf_zname; + qinfo->qsys->uuconf_palloc = qinfo->salternate.uuconf_palloc; + if (argc > 1) + { + qinfo->qsys->uuconf_zalternate = argv[1]; + return UUCONF_CMDTABRET_KEEP; + } + } + + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Handle the "default-alternates" command. This just takes a boolean + argument which is used to set the fdefault_alternates field of the + sinfo structure. */ + +/*ARGSUSED*/ +static int +iidefault_alternates (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct sinfo *qinfo = (struct sinfo *) pinfo; + + return _uuconf_iboolean (qglobal, argv[1], &qinfo->fdefault_alternates); +} + +/* Handle the "time" command. We do this by turning it into a + "timegrade" command with a grade of BGRADE_LOW. The first argument + is a time string, and the optional second argument is the retry + time. */ + +/*ARGSUSED*/ +static int +iitime (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + char *aznew[4]; + char ab[2]; + + if (argc != 2 && argc != 3) + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; + + aznew[0] = argv[0]; + ab[0] = UUCONF_GRADE_LOW; + ab[1] = '\0'; + aznew[1] = ab; + aznew[2] = argv[1]; + if (argc > 2) + aznew[3] = argv[2]; + + return iitimegrade (pglobal, argc + 1, aznew, pvar, pinfo); +} + +/* Handle the "timegrade" command by calling _uuconf_itime_parse with + appropriate ival (the work grade) and cretry (the retry time) + arguments. */ + +static int +iitimegrade (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct uuconf_timespan **pqspan = (struct uuconf_timespan **) pvar; + struct sinfo *qinfo = (struct sinfo *) pinfo; + int cretry; + int iret; + + if (argc < 3 || argc > 4) + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; + + if (argv[1][1] != '\0' || ! UUCONF_GRADE_LEGAL (argv[1][0])) + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; + + if (argc == 3) + cretry = 0; + else + { + iret = _uuconf_iint (qglobal, argv[3], (pointer) &cretry, TRUE); + if (iret != UUCONF_SUCCESS) + return iret; + } + + iret = _uuconf_itime_parse (qglobal, argv[2], (long) argv[1][0], + cretry, _uuconf_itime_grade_cmp, pqspan, + qinfo->qsys->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + iret |= UUCONF_CMDTABRET_EXIT; + return iret; +} + +/* Handle the "baud-range" command, also known as "speed-range". */ + +static int +iibaud_range (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct uuconf_system *qsys = (struct uuconf_system *) pvar; + int iret; + + iret = _uuconf_iint (qglobal, argv[1], (pointer) &qsys->uuconf_ibaud, + FALSE); + if (iret != UUCONF_SUCCESS) + return iret; + return _uuconf_iint (qglobal, argv[2], (pointer) &qsys->uuconf_ihighbaud, + FALSE); +} + +/* Handle one of the size commands ("call-local-size", etc.). The + first argument is a number of bytes, and the second argument is a + time string. The pvar argument points to the string array to which + we add this new string. */ + +/*ARGSUSED*/ +static int +iisize (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct uuconf_timespan **pqspan = (struct uuconf_timespan **) pvar; + struct sinfo *qinfo = (struct sinfo *) pinfo; + long ival; + int iret; + + iret = _uuconf_iint (qglobal, argv[1], (pointer) &ival, FALSE); + if (iret != UUCONF_SUCCESS) + return iret; + + iret = _uuconf_itime_parse (qglobal, argv[2], ival, 0, iisizecmp, + pqspan, qinfo->qsys->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + iret |= UUCONF_CMDTABRET_EXIT; + return iret; +} + +/* A comparison function for sizes to pass to _uuconf_itime_parse. */ + +static int +iisizecmp (i1, i2) + long i1; + long i2; +{ + /* We can't just return i1 - i2 because that would be a long. */ + if (i1 < i2) + return -1; + else if (i1 == i2) + return 0; + else + return 1; +} + +/* Handle the "port" command. If there is one argument, this names a + port. Otherwise, the remaining arguments form a command describing + the port. */ + +/*ARGSUSED*/ +static int +iiport (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct sinfo *qinfo = (struct sinfo *) pinfo; + + if (argc < 2) + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; + else if (argc == 2) + { + qinfo->qsys->uuconf_zport = argv[1]; + return UUCONF_CMDTABRET_KEEP; + } + else + { + int iret; + + if (qinfo->qsys->uuconf_qport + == (struct uuconf_port *) &_uuconf_unset) + { + struct uuconf_port *qnew; + + qnew = ((struct uuconf_port *) + uuconf_malloc (qinfo->qsys->uuconf_palloc, + sizeof (struct uuconf_port))); + if (qnew == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + + _uuconf_uclear_port (qnew); + + if (qinfo->qsys->uuconf_zname == NULL) + qnew->uuconf_zname = (char *) "default system file port"; + else + { + char *zname; + size_t clen; + + clen = strlen (qinfo->qsys->uuconf_zname); + zname = (char *) uuconf_malloc (qinfo->qsys->uuconf_palloc, + clen + sizeof "system port"); + if (zname == NULL) + { + qglobal->ierrno = errno; + return (UUCONF_MALLOC_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_CMDTABRET_EXIT); + } + + memcpy ((pointer) zname, (pointer) "system ", + sizeof "system " - 1); + memcpy ((pointer) (zname + sizeof "system " - 1), + (pointer) qinfo->qsys->uuconf_zname, + clen); + memcpy ((pointer) (zname + sizeof "system " - 1 + clen), + (pointer) " port", sizeof " port"); + + qnew->uuconf_zname = zname; + } + + qnew->uuconf_palloc = qinfo->qsys->uuconf_palloc; + + qinfo->qsys->uuconf_qport = qnew; + } + + iret = _uuconf_iport_cmd (qglobal, argc - 1, argv + 1, + qinfo->qsys->uuconf_qport); + if (UUCONF_ERROR_VALUE (iret) != UUCONF_SUCCESS) + iret |= UUCONF_CMDTABRET_EXIT; + return iret; + } +} + +/* Handle the "chat" and "called-chat" set of commands. These just + hand off to the generic chat script function. */ + +static int +iichat (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct sinfo *qinfo = (struct sinfo *) pinfo; + struct uuconf_chat *qchat = (struct uuconf_chat *) pvar; + int iret; + + iret = _uuconf_ichat_cmd (qglobal, argc, argv, qchat, + qinfo->qsys->uuconf_palloc); + if (UUCONF_ERROR_VALUE (iret) != UUCONF_SUCCESS) + iret |= UUCONF_CMDTABRET_EXIT; + return iret; +} + +/* Handle the "called-login" command. This only needs to be in a + function because there can be additional arguments listing the + remote systems which are permitted to use this login name. The + additional arguments are not actually handled here; they are + handled by uuconf_taylor_system_names, which already has to go + through all the system files. */ + +/*ARGSUSED*/ +static int +iicalled_login (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + char **pz = (char **) pvar; + + if (argc < 2) + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; + *pz = argv[1]; + return UUCONF_CMDTABRET_KEEP; +} + +/* Handle the "protocol-parameter" command. This just hands off to + the generic protocol parameter handler. */ + +static int +iiproto_param (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct uuconf_proto_param **pqparam = (struct uuconf_proto_param **) pvar; + struct sinfo *qinfo = (struct sinfo *) pinfo; + + if (*pqparam == (struct uuconf_proto_param *) &_uuconf_unset) + *pqparam = NULL; + return _uuconf_iadd_proto_param (qglobal, argc - 1, argv + 1, pqparam, + qinfo->qsys->uuconf_palloc); +} + +/* Handle the "request" command. This is equivalent to specifying + both "call-request" and "called-request". */ + +/*ARGSUSED*/ +static int +iirequest (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct sinfo *qinfo = (struct sinfo *) pinfo; + int iret; + + iret = _uuconf_iboolean (qglobal, argv[1], + &qinfo->qsys->uuconf_fsend_request); + if (UUCONF_ERROR_VALUE (iret) == UUCONF_SUCCESS) + qinfo->qsys->uuconf_frec_request = qinfo->qsys->uuconf_fsend_request; + + return iret; +} + +/* Handle the "transfer" command. This is equivalent to specifying + both "call-transfer" and "called-transfer". */ + +/*ARGSUSED*/ +static int +iitransfer (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct sinfo *qinfo = (struct sinfo *) pinfo; + int iret; + + iret = _uuconf_iboolean (qglobal, argv[1], + &qinfo->qsys->uuconf_fcall_transfer); + if (UUCONF_ERROR_VALUE (iret) == UUCONF_SUCCESS) + qinfo->qsys->uuconf_fcalled_transfer = qinfo->qsys->uuconf_fcall_transfer; + + return iret; +} + +/* Handle the "forward" command. This is equivalent to specifying + both "forward-from" and "forward-to". */ + +/*ARGSUSED*/ +static int +iiforward (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct sinfo *qinfo = (struct sinfo *) pinfo; + struct uuconf_system *qsys; + int i; + int iret; + + qsys = qinfo->qsys; + qsys->uuconf_pzforward_from = NULL; + qsys->uuconf_pzforward_to = NULL; + for (i = 1; i < argc; i++) + { + iret = _uuconf_iadd_string (qglobal, argv[i], FALSE, FALSE, + &qsys->uuconf_pzforward_to, + qsys->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + return iret | UUCONF_CMDTABRET_KEEP | UUCONF_CMDTABRET_EXIT; + iret = _uuconf_iadd_string (qglobal, argv[i], FALSE, FALSE, + &qsys->uuconf_pzforward_from, + qsys->uuconf_palloc); + if (iret != UUCONF_SUCCESS) + return iret | UUCONF_CMDTABRET_KEEP | UUCONF_CMDTABRET_EXIT; + } + + return UUCONF_CMDTABRET_KEEP; +} + +/* Handle an unknown command. This should probably be done more + intelligently. */ + +/*ARGSUSED*/ +static int +iiunknown (pglobal, argc, argv, pvar, pinfo) + pointer pglobal; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; +} + +/* Return information for an unknown system. It would be better to + put this in a different file, but it would require breaking several + functions out of this file. Perhaps I will do it sometime. */ + +int +uuconf_taylor_system_unknown (pglobal, qsys) + pointer pglobal; + struct uuconf_system *qsys; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct uuconf_cmdtab as[CSYSTEM_CMDS]; + struct sinfo si; + struct sunknown *q; + int iret; + + if (qglobal->qprocess->qunknown == NULL) + return UUCONF_NOT_FOUND; + + _uuconf_ucmdtab_base (asIcmds, CSYSTEM_CMDS, (char *) qsys, as); + + _uuconf_uclear_system (qsys); + + si.qsys = qsys; + si.falternates = FALSE; + si.fdefault_alternates = TRUE; + qsys->uuconf_palloc = uuconf_malloc_block (); + if (qsys->uuconf_palloc == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + + for (q = qglobal->qprocess->qunknown; q != NULL; q = q->qnext) + { + iret = uuconf_cmd_args (pglobal, q->cargs, q->pzargs, as, + (pointer) &si, iiunknown, + UUCONF_CMDTABFLAG_BACKSLASH, + qsys->uuconf_palloc); + iret &=~ UUCONF_CMDTABRET_KEEP; + if (UUCONF_ERROR_VALUE (iret) != UUCONF_SUCCESS) + { + qglobal->zfilename = qglobal->qprocess->zconfigfile; + qglobal->ilineno = q->ilineno; + return ((iret &~ UUCONF_CMDTABRET_EXIT) + | UUCONF_ERROR_FILENAME + | UUCONF_ERROR_LINENO); + } + if ((iret & UUCONF_CMDTABRET_EXIT) != 0) + break; + } + + if (! si.falternates) + uiset_call (qsys); + else + { + iret = iialternate (pglobal, 0, (char **) NULL, (pointer) NULL, + (pointer) &si); + if (iret != UUCONF_SUCCESS) + return iret; + } + + /* The first alternate is always available for calling in. */ + qsys->uuconf_fcalled = TRUE; + + return _uuconf_isystem_basic_default (qglobal, qsys); +} diff --git a/gnu/libexec/uucp/libuuconf/tsnams.c b/gnu/libexec/uucp/libuuconf/tsnams.c new file mode 100644 index 0000000000..c0f96e53f6 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/tsnams.c @@ -0,0 +1,84 @@ +/* tsnams.c + Get all known system names from the Taylor UUCP configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_tsnams_rcsid[] = "$Id: tsnams.c,v 1.1 1993/08/04 19:35:19 jtc Exp $"; +#endif + +/* Get all the system names from the Taylor UUCP configuration files. + These were actually already recorded by uuconf_taylor_init, so this + function is pretty simple. */ + +int +uuconf_taylor_system_names (pglobal, ppzsystems, falias) + pointer pglobal; + char ***ppzsystems; + int falias; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + int iret; + register struct stsysloc *q; + char **pz; + int c, i; + + if (! qglobal->qprocess->fread_syslocs) + { + iret = _uuconf_iread_locations (qglobal); + if (iret != UUCONF_SUCCESS) + return iret; + } + + *ppzsystems = NULL; + c = 0; + + for (q = qglobal->qprocess->qsyslocs; q != NULL; q = q->qnext) + { + if (! falias && q->falias) + continue; + + iret = _uuconf_iadd_string (qglobal, (char *) q->zname, TRUE, FALSE, + ppzsystems, (pointer) NULL); + if (iret != UUCONF_SUCCESS) + return iret; + ++c; + } + + /* The order of the qSyslocs list is reversed from the list in the + configuration files. Reverse the returned list in order to make + uuname output more intuitive. */ + pz = *ppzsystems; + for (i = c / 2 - 1; i >= 0; i--) + { + char *zhold; + + zhold = pz[i]; + pz[i] = pz[c - i - 1]; + pz[c - i - 1] = zhold; + } + + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/tsys.c b/gnu/libexec/uucp/libuuconf/tsys.c new file mode 100644 index 0000000000..6a1f662109 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/tsys.c @@ -0,0 +1,49 @@ +/* tsys.c + User function to get a system from the Taylor UUCP configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_tsys_rcsid[] = "$Id: tsys.c,v 1.1 1993/08/04 19:35:20 jtc Exp $"; +#endif + +/* Get system information from the Taylor UUCP configuration files. + This is a wrapper for the internal function which makes sure that + every field gets a default value. */ + +int +uuconf_taylor_system_info (pglobal, zsystem, qsys) + pointer pglobal; + const char *zsystem; + struct uuconf_system *qsys; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + int iret; + + iret = _uuconf_itaylor_system_internal (qglobal, zsystem, qsys); + if (iret != UUCONF_SUCCESS) + return iret; + return _uuconf_isystem_basic_default (qglobal, qsys); +} diff --git a/gnu/libexec/uucp/libuuconf/tval.c b/gnu/libexec/uucp/libuuconf/tval.c new file mode 100644 index 0000000000..be8769612d --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/tval.c @@ -0,0 +1,71 @@ +/* tval.c + Validate a login name for a system using Taylor UUCP files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_tval_rcsid[] = "$Id: tval.c,v 1.1 1993/08/04 19:35:21 jtc Exp $"; +#endif + +/* Validate a login name for a system using Taylor UUCP configuration + files. This assumes that the zcalled_login field is either NULL or + "ANY". If makes sure that the login name does not appear in some + other "called-login" command listing systems not including this + one. */ + +int +uuconf_taylor_validate (pglobal, qsys, zlogin) + pointer pglobal; + const struct uuconf_system *qsys; + const char *zlogin; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + struct svalidate *q; + + if (! qglobal->qprocess->fread_syslocs) + { + int iret; + + iret = _uuconf_iread_locations (qglobal); + if (iret != UUCONF_SUCCESS) + return iret; + } + + for (q = qglobal->qprocess->qvalidate; q != NULL; q = q->qnext) + { + if (strcmp (q->zlogname, zlogin) == 0) + { + char **pz; + + for (pz = q->pzmachines; *pz != NULL; pz++) + if (strcmp (*pz, qsys->uuconf_zname) == 0) + return UUCONF_SUCCESS; + + return UUCONF_NOT_FOUND; + } + } + + return UUCONF_SUCCESS; +} diff --git a/gnu/libexec/uucp/libuuconf/ugtlin.c b/gnu/libexec/uucp/libuuconf/ugtlin.c new file mode 100644 index 0000000000..88ca350738 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/ugtlin.c @@ -0,0 +1,110 @@ +/* ugtlin.c + Read a line with backslash continuations. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_ugtlin_rcsid[] = "$Id: ugtlin.c,v 1.1 1993/08/04 19:35:22 jtc Exp $"; +#endif + +/* Read a line from a file with backslash continuations. This updates + the qglobal->ilineno count for each additional line it reads. */ + +int +_uuconf_getline (qglobal, pzline, pcline, e) + struct sglobal *qglobal; + char **pzline; + size_t *pcline; + FILE *e; +{ + int ctot; + char *zline; + size_t cline; + + ctot = -1; + + zline = NULL; + cline = 0; + + while (TRUE) + { + int cchars; + + if (ctot < 0) + cchars = getline (pzline, pcline, e); + else + cchars = getline (&zline, &cline, e); + if (cchars < 0) + { + if (zline != NULL) + free ((pointer) zline); + if (ctot >= 0) + return ctot; + else + return cchars; + } + + if (ctot < 0) + ctot = cchars; + else + { + if (*pcline <= ctot + cchars) + { + char *znew; + + if (*pcline > 0) + znew = (char *) realloc ((pointer) *pzline, + (size_t) (ctot + cchars + 1)); + else + znew = (char *) malloc ((size_t) (ctot + cchars + 1)); + if (znew == NULL) + { + free ((pointer) zline); + return -1; + } + *pzline = znew; + *pcline = ctot + cchars + 1; + } + + memcpy ((pointer) ((*pzline) + ctot), (pointer) zline, + (size_t) (cchars + 1)); + ctot += cchars; + } + + if (ctot < 2 + || (*pzline)[ctot - 1] != '\n' + || (*pzline)[ctot - 2] != '\\') + { + if (zline != NULL) + free ((pointer) zline); + return ctot; + } + + ++qglobal->ilineno; + + ctot -= 2; + (*pzline)[ctot] = '\0'; + } +} diff --git a/gnu/libexec/uucp/libuuconf/unk.c b/gnu/libexec/uucp/libuuconf/unk.c new file mode 100644 index 0000000000..565dd270f2 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/unk.c @@ -0,0 +1,70 @@ +/* unk.c + Get information about an unknown system. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_unk_rcsid[] = "$Id: unk.c,v 1.1 1993/08/04 19:35:22 jtc Exp $"; +#endif + +#include + +/* Get information about an unknown system. If we are using + HAVE_TAYLOR_CONFIG, we just use it. Otherwise if we are using + HAVE_HDB_CONFIG, we use it. Otherwise we return a default system. + This isn't right for HAVE_V2_CONFIG, because it is possible to + specify default directories to read and write in USERFILE. + However, I'm not going to bother to write that code unless somebody + actually wants it. */ + +/*ARGSUSED*/ +int +uuconf_system_unknown (pglobal, qsys) + pointer pglobal; + struct uuconf_system *qsys; +{ +#if HAVE_TAYLOR_CONFIG + return uuconf_taylor_system_unknown (pglobal, qsys); +#else /* ! HAVE_TAYLOR_CONFIG */ +#if HAVE_HDB_CONFIG + return uuconf_hdb_system_unknown (pglobal, qsys); +#else /* ! HAVE_HDB_CONFIG */ +#if HAVE_V2_CONFIG + struct sglobal *qglobal = (struct sglobal *) pglobal; + + _uuconf_uclear_system (qsys); + qsys->uuconf_palloc = uuconf_malloc_block (); + if (qsys->uuconf_palloc == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + return _uuconf_isystem_basic_default (qglobal, qsys); +#else /* ! HAVE_V2_CONFIG */ + return UUCONF_NOT_FOUND; +#endif /* ! HAVE_V2_CONFIG */ +#endif /* ! HAVE_HDB_CONFIG */ +#endif /* ! HAVE_TAYLOR_CONFIG */ +} diff --git a/gnu/libexec/uucp/libuuconf/uucnfi.h b/gnu/libexec/uucp/libuuconf/uucnfi.h new file mode 100644 index 0000000000..9ce6a62dc7 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/uucnfi.h @@ -0,0 +1,368 @@ +/* uucnfi.h + Internal header file for the uuconf package. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +/* This is the internal header file for the uuconf package. It should + not be included by anything other than the uuconf code itself. */ + +/* Get all the general definitions. */ +#include "uucp.h" + +/* Get the uuconf header file itself. */ +#include "uuconf.h" + +/* We need the system dependent header file. */ +#include "syshdr.h" + +/* This is the generic information structure. This holds all the + per-thread global information needed by the uuconf code. The + per-process global information is held in an sprocess structure, + which this structure points to. This permits the code to not have + any global variables at all. */ + +struct sglobal +{ + /* A pointer to the per-process global information. */ + struct sprocess *qprocess; + /* A memory block in which all the memory for these fields is + allocated. */ + pointer pblock; + /* The value of errno after an error. */ + int ierrno; + /* The filename for which an error occurred. */ + const char *zfilename; + /* The line number at which an error occurred. */ + int ilineno; +}; + +/* This is the per-process information structure. This essentially + holds all the global variables used by uuconf. */ + +struct sprocess +{ + /* The name of the local machine. This will be NULL if it is not + specified in a configuration file. */ + const char *zlocalname; + /* The spool directory. */ + const char *zspooldir; + /* The default public directory. */ + const char *zpubdir; + /* The lock directory. */ + const char *zlockdir; + /* The log file. */ + const char *zlogfile; + /* The statistics file. */ + const char *zstatsfile; + /* The debugging file. */ + const char *zdebugfile; + /* The default debugging level. */ + const char *zdebug; + /* The maximum number of simultaneously executing uuxqts. */ + int cmaxuuxqts; + /* Whether we are reading the V2 configuration files. */ + boolean fv2; + /* Whether we are reading the HDB configuration files. */ + boolean fhdb; + /* The names of the dialcode files. */ + char **pzdialcodefiles; + /* Timetables. These are in pairs. The first element is the name, + the second is the time string. */ + char **pztimetables; + + /* Taylor UUCP config file name. */ + char *zconfigfile; + /* Taylor UUCP sys file names. */ + char **pzsysfiles; + /* Taylor UUCP port file names. */ + char **pzportfiles; + /* Taylor UUCP dial file names. */ + char **pzdialfiles; + /* Taylor UUCP passwd file names. */ + char **pzpwdfiles; + /* Taylor UUCP call file names. */ + char **pzcallfiles; + /* List of "unknown" commands from config file. */ + struct sunknown *qunknown; + /* Whether the Taylor UUCP system information locations have been + read. */ + boolean fread_syslocs; + /* Taylor UUCP system information locations. */ + struct stsysloc *qsyslocs; + /* Taylor UUCP validation restrictions. */ + struct svalidate *qvalidate; + /* Whether the "myname" command is used in a Taylor UUCP file. */ + boolean fuses_myname; + + /* V2 system file name (L.sys). */ + char *zv2systems; + /* V2 device file name (L-devices). */ + char *zv2devices; + /* V2 user permissions file name (USERFILE). */ + char *zv2userfile; + /* V2 user permitted commands file (L.cmds). */ + char *zv2cmds; + + /* HDB system file names (Systems). */ + char **pzhdb_systems; + /* HDB device file names (Devices). */ + char **pzhdb_devices; + /* HDB dialer file names (Dialers). */ + char **pzhdb_dialers; + /* Whether the HDB Permissions file has been read. */ + boolean fhdb_read_permissions; + /* The HDB Permissions file entries. */ + struct shpermissions *qhdb_permissions; +}; + +/* This structure is used to hold the "unknown" commands from the + Taylor UUCP config file before they have been parsed. */ + +struct sunknown +{ + /* Next element in linked list. */ + struct sunknown *qnext; + /* Line number in config file. */ + int ilineno; + /* Number of arguments. */ + int cargs; + /* Arguments. */ + char **pzargs; +}; + +/* This structure is used to hold the locations of systems within the + Taylor UUCP sys files. */ + +struct stsysloc +{ + /* Next element in linked list. */ + struct stsysloc *qnext; + /* System name. */ + const char *zname; + /* Whether system is an alias or a real system. If this is an + alias, the real system is the next entry in the linked list which + is not an alias. */ + boolean falias; + /* File name (one of the sys files). */ + const char *zfile; + /* Open file. */ + FILE *e; + /* Location within file (from ftell). */ + long iloc; + /* Line number within file. */ + int ilineno; +}; + +/* This structure is used to hold validation restrictions. This is a + list of machines which are permitted to use a particular login + name. If a machine logs in, and there is no called login entry for + it, the login name and machine name must be passed to + uuconf_validate to confirm that either there is no entry for this + login name or that the machine name appears on the entry. */ + +struct svalidate +{ + /* Next element in linked list. */ + struct svalidate *qnext; + /* Login name. */ + const char *zlogname; + /* NULL terminated list of machine names. */ + char **pzmachines; +}; + +/* This structure is used to hold a linked list of HDB Permissions + file entries. */ + +struct shpermissions +{ + /* Next entry in linked list. */ + struct shpermissions *qnext; + /* NULL terminated array of LOGNAME values. */ + char **pzlogname; + /* NULL terminated array of MACHINE values. */ + char **pzmachine; + /* Boolean REQUEST value. */ + int frequest; + /* Boolean SENDFILES value ("call" is taken as "no"). */ + int fsendfiles; + /* NULL terminated array of READ values. */ + char **pzread; + /* NULL terminated array of WRITE values. */ + char **pzwrite; + /* Boolean CALLBACK value. */ + int fcallback; + /* NULL terminated array of COMMANDS values. */ + char **pzcommands; + /* NULL terminated array of VALIDATE values. */ + char **pzvalidate; + /* String MYNAME value. */ + char *zmyname; + /* String PUBDIR value. */ + const char *zpubdir; + /* NULL terminated array of ALIAS values. */ + char **pzalias; +}; + +/* This structure is used to build reentrant uuconf_cmdtab tables. + The ioff field is either (size_t) -1 or an offsetof macro. The + table is then copied into a uuconf_cmdtab, except that offsets of + (size_t) -1 are converted to pvar elements of NULL, and other + offsets are converted to an offset off some base address. */ + +struct cmdtab_offset +{ + const char *zcmd; + int itype; + size_t ioff; + uuconf_cmdtabfn pifn; +}; + +/* A value in a uuconf_system structure which holds the address of + this special variable is known to be uninitialized. */ +extern char *_uuconf_unset; + +/* Internal function to read a system from the Taylor UUCP + configuration files. This does not apply the basic defaults. */ +extern int _uuconf_itaylor_system_internal P((struct sglobal *qglobal, + const char *zsystem, + struct uuconf_system *qsys)); + +/* Read the system locations and validation information from the + Taylor UUCP configuration files. This sets the qsyslocs, + qvalidate, and fread_syslocs elements of the global structure. */ +extern int _uuconf_iread_locations P((struct sglobal *qglobal)); + +/* Process a command for a port from a Taylor UUCP file. */ +extern int _uuconf_iport_cmd P((struct sglobal *qglobal, int argc, + char **argv, struct uuconf_port *qport)); + +/* Process a command for a dialer from a Taylor UUCP file. */ +extern int _uuconf_idialer_cmd P((struct sglobal *qglobal, int argc, + char **argv, + struct uuconf_dialer *qdialer)); + +/* Process a command for a chat script from a Taylor UUCP file; this + is also called for HDB or V2 files, with a made up command. */ +extern int _uuconf_ichat_cmd P((struct sglobal *qglobal, int argc, + char **argv, struct uuconf_chat *qchat, + pointer pblock)); + +/* Process a protocol-parameter command from a Taylor UUCP file. */ +extern int _uuconf_iadd_proto_param P((struct sglobal *qglobal, + int argc, char **argv, + struct uuconf_proto_param **pq, + pointer pblock)); + +/* Handle a "seven-bit", "reliable", or "half-duplex" command from a + Taylor UUCP port or dialer file. The pvar field should point to + the ireliable element of the structure. */ +extern int _uuconf_iseven_bit P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); +extern int _uuconf_ireliable P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); +extern int _uuconf_ihalf_duplex P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); + +/* Internal function to read a system from the V2 configuration files. + This does not apply the basic defaults. */ +extern int _uuconf_iv2_system_internal P((struct sglobal *qglobal, + const char *zsystem, + struct uuconf_system *qsys)); + +/* Internal function to read a system from the HDB configuration + files. This does not apply the basic defaults. */ +extern int _uuconf_ihdb_system_internal P((struct sglobal *qglobal, + const char *zsystem, + struct uuconf_system *qsys)); + +/* Read the HDB Permissions file. */ +extern int _uuconf_ihread_permissions P((struct sglobal *qglobal)); + +/* Initialize the global information structure. */ +extern int _uuconf_iinit_global P((struct sglobal **pqglobal)); + +/* Clear system information. */ +extern void _uuconf_uclear_system P((struct uuconf_system *qsys)); + +/* Default unset aspects of one system to the contents of another. */ +extern int _uuconf_isystem_default P((struct sglobal *qglobal, + struct uuconf_system *q, + struct uuconf_system *qdefault, + boolean faddalternates)); + +/* Put in the basic system defaults. */ +extern int _uuconf_isystem_basic_default P((struct sglobal *qglobal, + struct uuconf_system *qsys)); + +/* Clear port information. */ +extern void _uuconf_uclear_port P((struct uuconf_port *qport)); + +/* Clear dialer information. */ +extern void _uuconf_uclear_dialer P((struct uuconf_dialer *qdialer)); + +/* Add a timetable. */ +extern int _uuconf_itimetable P((pointer pglobal, int argc, char **argv, + pointer pvar, pointer pinfo)); + +/* Parse a time string. */ +extern int _uuconf_itime_parse P((struct sglobal *qglobal, char *ztime, + long ival, int cretry, + int (*picmp) P((long, long)), + struct uuconf_timespan **pqspan, + pointer pblock)); + +/* A grade comparison function to pass to _uuconf_itime_parse. */ +extern int _uuconf_itime_grade_cmp P((long, long)); + +/* Add a string to a NULL terminated list of strings. */ +extern int _uuconf_iadd_string P((struct sglobal *qglobal, + char *zadd, boolean fcopy, + boolean fdupcheck, char ***ppzstrings, + pointer pblock)); + +/* Parse a string into a boolean value. */ +extern int _uuconf_iboolean P((struct sglobal *qglobal, const char *zval, + int *pi)); + +/* Parse a string into an integer value. The argument p is either an + int * or a long *, according to the argument fint. */ +extern int _uuconf_iint P((struct sglobal *qglobal, const char *zval, + pointer p, boolean fint)); + +/* Turn a cmdtab_offset table into a uuconf_cmdtab table. */ +extern void _uuconf_ucmdtab_base P((const struct cmdtab_offset *qoff, + size_t celes, char *pbase, + struct uuconf_cmdtab *qset)); + +/* Merge two memory blocks into one. This cannot fail. */ +extern pointer _uuconf_pmalloc_block_merge P((pointer, pointer)); + +/* A wrapper for getline that continues lines if they end in a + backslash. It needs qglobal so that it can increment ilineno + correctly. */ +extern int _uuconf_getline P((struct sglobal *qglobal, + char **, size_t *, FILE *)); + +/* Split a string into tokens. */ +extern int _uuconf_istrsplit P((char *zline, int bsep, + char ***ppzsplit, size_t *csplit)); diff --git a/gnu/libexec/uucp/libuuconf/val.c b/gnu/libexec/uucp/libuuconf/val.c new file mode 100644 index 0000000000..a4eb30fb67 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/val.c @@ -0,0 +1,46 @@ +/* val.c + Validate a login name for a system. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_val_rcsid[] = "$Id: val.c,v 1.1 1993/08/04 19:35:25 jtc Exp $"; +#endif + +/* Validate a login name for a system. */ + +/*ARGSUSED*/ +int +uuconf_validate (pglobal, qsys, zlogin) + pointer pglobal; + const struct uuconf_system *qsys; + const char *zlogin; +{ +#if HAVE_TAYLOR_CONFIG + return uuconf_taylor_validate (pglobal, qsys, zlogin); +#else + return UUCONF_SUCCESS; +#endif +} diff --git a/gnu/libexec/uucp/libuuconf/vinit.c b/gnu/libexec/uucp/libuuconf/vinit.c new file mode 100644 index 0000000000..20ff5bcb1b --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/vinit.c @@ -0,0 +1,112 @@ +/* vinit.c + Initialize for reading V2 configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_vinit_rcsid[] = "$Id: vinit.c,v 1.1 1993/08/04 19:35:25 jtc Exp $"; +#endif + +#include + +static int ivinlib P((struct sglobal *qglobal, const char *z, size_t csize, + char **pz)); + +/* Return an allocated buffer holding a file name in OLDCONFIGLIB. + The c argument is the size of z including the trailing null byte, + since this is convenient for both the caller and this function. */ + +static int +ivinlib (qglobal, z, c, pz) + struct sglobal *qglobal; + const char *z; + size_t c; + char **pz; +{ + char *zalc; + + zalc = uuconf_malloc (qglobal->pblock, sizeof OLDCONFIGLIB - 1 + c); + if (zalc == NULL) + { + qglobal->ierrno = errno; + return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + + memcpy ((pointer) zalc, (pointer) OLDCONFIGLIB, + sizeof OLDCONFIGLIB - 1); + memcpy ((pointer) (zalc + sizeof OLDCONFIGLIB - 1), (pointer) z, c); + + *pz = zalc; + + return UUCONF_SUCCESS; +} + +/* Initialize the routines which read V2 configuration files. The + only thing we do here is allocate the file names. */ + +int +uuconf_v2_init (ppglobal) + pointer *ppglobal; +{ + struct sglobal **pqglobal = (struct sglobal **) ppglobal; + int iret; + struct sglobal *qglobal; + char *zdialcodes; + + if (*pqglobal == NULL) + { + iret = _uuconf_iinit_global (pqglobal); + if (iret != UUCONF_SUCCESS) + return iret; + } + + qglobal = *pqglobal; + + iret = ivinlib (qglobal, V2_SYSTEMS, sizeof V2_SYSTEMS, + &qglobal->qprocess->zv2systems); + if (iret != UUCONF_SUCCESS) + return iret; + iret = ivinlib (qglobal, V2_DEVICES, sizeof V2_DEVICES, + &qglobal->qprocess->zv2devices); + if (iret != UUCONF_SUCCESS) + return iret; + iret = ivinlib (qglobal, V2_USERFILE, sizeof V2_USERFILE, + &qglobal->qprocess->zv2userfile); + if (iret != UUCONF_SUCCESS) + return iret; + iret = ivinlib (qglobal, V2_CMDS, sizeof V2_CMDS, + &qglobal->qprocess->zv2cmds); + if (iret != UUCONF_SUCCESS) + return iret; + + iret = ivinlib (qglobal, V2_DIALCODES, sizeof V2_DIALCODES, + &zdialcodes); + if (iret != UUCONF_SUCCESS) + return iret; + + return _uuconf_iadd_string (qglobal, zdialcodes, FALSE, FALSE, + &qglobal->qprocess->pzdialcodefiles, + qglobal->pblock); +} diff --git a/gnu/libexec/uucp/libuuconf/vport.c b/gnu/libexec/uucp/libuuconf/vport.c new file mode 100644 index 0000000000..b9382a3861 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/vport.c @@ -0,0 +1,251 @@ +/* vport.c + Find a port in the V2 configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_vport_rcsid[] = "$Id: vport.c,v 1.1 1993/08/04 19:35:27 jtc Exp $"; +#endif + +#include +#include + +/* Find a port in the V2 configuration files by name, baud rate, and + special purpose function. */ + +int +uuconf_v2_find_port (pglobal, zname, ibaud, ihighbaud, pifn, pinfo, qport) + pointer pglobal; + const char *zname; + long ibaud; + long ihighbaud; + int (*pifn) P((struct uuconf_port *, pointer)); + pointer pinfo; + struct uuconf_port *qport; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + FILE *e; + char *zline; + size_t cline; + char **pzsplit; + size_t csplit; + int iret; + int cchars; + + e = fopen (qglobal->qprocess->zv2devices, "r"); + if (e == NULL) + { + if (FNO_SUCH_FILE ()) + return UUCONF_NOT_FOUND; + qglobal->ierrno = errno; + qglobal->zfilename = qglobal->qprocess->zv2devices; + return (UUCONF_FOPEN_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_ERROR_FILENAME); + } + + zline = NULL; + cline = 0; + pzsplit = NULL; + csplit = 0; + + iret = UUCONF_NOT_FOUND; + + qglobal->ilineno = 0; + + while ((cchars = getline (&zline, &cline, e)) > 0) + { + int ctoks; + char *zend; + long ilow, ihigh; + pointer pblock; + + ++qglobal->ilineno; + + iret = UUCONF_NOT_FOUND; + + --cchars; + if (zline[cchars] == '\n') + zline[cchars] = '\0'; + zline[strcspn (zline, "#")] = '\0'; + + ctoks = _uuconf_istrsplit (zline, '\0', &pzsplit, &csplit); + if (ctoks < 0) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + /* An entry in L-devices is + + type device dial-device baud dialer + + The type (normally "ACU") is treated as the name. */ + + /* If there aren't enough entries, ignore the line; this + should probably do something more useful. */ + if (ctoks < 4) + continue; + + /* Make sure the name matches any argument. */ + if (zname != NULL + && strcmp (pzsplit[0], zname) != 0) + continue; + + /* Get the baud rate. */ + ilow = strtol (pzsplit[3], &zend, 10); + if (*zend == '-') + ihigh = strtol (zend + 1, (char **) NULL, 10); + else + ihigh = ilow; + + /* Make sure the baud rate matches any argument. */ + if (ibaud != 0 + && ilow != 0 + && (ilow > ibaud || ihigh < ibaud)) + continue; + + /* Now we must construct the port information, so that we can + pass it to pifn. The port type is determined by it's name, + unfortunately. The name "DIR" is used for a direct port, and + anything else for a modem port. */ + pblock = NULL; + _uuconf_uclear_port (qport); + qport->uuconf_zname = pzsplit[0]; + if (strcmp (pzsplit[0], "DIR") == 0) + { + qport->uuconf_ttype = UUCONF_PORTTYPE_DIRECT; + qport->uuconf_u.uuconf_sdirect.uuconf_zdevice = pzsplit[1]; + qport->uuconf_u.uuconf_sdirect.uuconf_ibaud = ilow; + } + else + { + qport->uuconf_ttype = UUCONF_PORTTYPE_MODEM; + qport->uuconf_u.uuconf_smodem.uuconf_zdevice = pzsplit[1]; + if (strcmp (pzsplit[2], "-") != 0) + qport->uuconf_u.uuconf_smodem.uuconf_zdial_device = pzsplit[2]; + else + qport->uuconf_u.uuconf_smodem.uuconf_zdial_device = NULL; + if (ilow == ihigh) + { + qport->uuconf_u.uuconf_smodem.uuconf_ibaud = ilow; + qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud = 0L; + qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud = 0L; + } + else + { + qport->uuconf_u.uuconf_smodem.uuconf_ibaud = 0L; + qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud = ilow; + qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud = ihigh; + } + qport->uuconf_u.uuconf_smodem.uuconf_fcarrier = TRUE; + if (ctoks < 5) + qport->uuconf_u.uuconf_smodem.uuconf_pzdialer = NULL; + else + { + size_t c; + char **pzd; + + /* We support dialer/token pairs, although normal V2 + doesn't. */ + pblock = uuconf_malloc_block (); + if (pblock == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + c = (ctoks - 4) * sizeof (char *); + pzd = (char **) uuconf_malloc (pblock, c + sizeof (char *)); + if (pzd == NULL) + { + qglobal->ierrno = errno; + uuconf_free_block (pblock); + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + memcpy ((pointer) pzd, (pointer) (pzsplit + 4), c); + pzd[ctoks - 4] = NULL; + + qport->uuconf_u.uuconf_smodem.uuconf_pzdialer = pzd; + } + qport->uuconf_u.uuconf_smodem.uuconf_qdialer = NULL; + } + + if (pifn != NULL) + { + iret = (*pifn) (qport, pinfo); + if (iret != UUCONF_SUCCESS) + { + if (pblock != NULL) + uuconf_free_block (pblock); + if (iret != UUCONF_NOT_FOUND) + break; + continue; + } + } + + /* This is the port we want. */ + if (pblock == NULL) + { + pblock = uuconf_malloc_block (); + if (pblock == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + } + + if (uuconf_add_block (pblock, zline) != 0) + { + qglobal->ierrno = errno; + uuconf_free_block (pblock); + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + zline = NULL; + + qport->uuconf_palloc = pblock; + + break; + } + + (void) fclose (e); + + if (zline != NULL) + free ((pointer) zline); + if (pzsplit != NULL) + free ((pointer) pzsplit); + + if (iret != UUCONF_SUCCESS && iret != UUCONF_NOT_FOUND) + { + qglobal->zfilename = qglobal->qprocess->zv2devices; + iret |= UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; + } + + return iret; +} diff --git a/gnu/libexec/uucp/libuuconf/vsinfo.c b/gnu/libexec/uucp/libuuconf/vsinfo.c new file mode 100644 index 0000000000..c528ce3501 --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/vsinfo.c @@ -0,0 +1,575 @@ +/* vsinfo.c + Get information about a system from the V2 configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_vsinfo_rcsid[] = "$Id: vsinfo.c,v 1.1 1993/08/04 19:35:28 jtc Exp $"; +#endif + +#include +#include + +/* Get the information for a particular system from the V2 + configuration files. This does not make sure that all the default + values are set. */ + +int +_uuconf_iv2_system_internal (qglobal, zsystem, qsys) + struct sglobal *qglobal; + const char *zsystem; + struct uuconf_system *qsys; +{ + char *zline; + size_t cline; + char **pzsplit; + size_t csplit; + char **pzcomma; + size_t ccomma; + FILE *e; + int cchars; + pointer pblock; + int iret; + + e = fopen (qglobal->qprocess->zv2systems, "r"); + if (e == NULL) + { + if (FNO_SUCH_FILE ()) + return UUCONF_NOT_FOUND; + qglobal->ierrno = errno; + qglobal->zfilename = qglobal->qprocess->zv2systems; + return (UUCONF_FOPEN_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_ERROR_FILENAME); + } + + zline = NULL; + cline = 0; + pzsplit = NULL; + csplit = 0; + pzcomma = NULL; + ccomma = 0; + + pblock = NULL; + iret = UUCONF_SUCCESS; + + qglobal->ilineno = 0; + + while ((cchars = getline (&zline, &cline, e)) > 0) + { + int ctoks, ctimes, i; + struct uuconf_system *qset; + char *z, *zretry; + int cretry; + + ++qglobal->ilineno; + + --cchars; + if (zline[cchars] == '\n') + zline[cchars] = '\0'; + zline[strcspn (zline, "#")] = '\0'; + + ctoks = _uuconf_istrsplit (zline, '\0', &pzsplit, &csplit); + if (ctoks < 0) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + /* If this isn't the system we're looking for, keep reading + the file. */ + if (ctoks < 1 + || strcmp (zsystem, pzsplit[0]) != 0) + continue; + + /* If this is the first time we've found the system, we want + to set *qsys directly. Otherwise, we allocate a new + alternate. */ + if (pblock == NULL) + { + pblock = uuconf_malloc_block (); + if (pblock == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + _uuconf_uclear_system (qsys); + qsys->uuconf_palloc = pblock; + qset = qsys; + } + else + { + struct uuconf_system **pq; + + qset = ((struct uuconf_system *) + uuconf_malloc (pblock, sizeof (struct uuconf_system))); + if (qset == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + _uuconf_uclear_system (qset); + for (pq = &qsys->uuconf_qalternate; + *pq != NULL; + pq = &(*pq)->uuconf_qalternate) + ; + *pq = qset; + } + + /* Add this line to the memory block we are building for the + system. */ + if (uuconf_add_block (pblock, zline) != 0) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + zline = NULL; + cline = 0; + + /* The format of a line in Systems is + system time device speed phone chat + For example, + airs Any ACU 9600 5551212 ogin: foo pass: bar + */ + + /* Get the system name. */ + + qset->uuconf_zname = pzsplit[0]; + qset->uuconf_fcall = TRUE; + qset->uuconf_fcalled = TRUE; + + if (ctoks < 2) + continue; + + /* A time string is "time/grade,time/grade;retry". A missing + grade is taken as BGRADE_LOW. On some versions the retry + time is actually separated by a comma, which won't work right + here. */ + zretry = strchr (pzsplit[1], ';'); + if (zretry == NULL) + cretry = 0; + else + { + *zretry = '\0'; + cretry = (int) strtol (zretry + 1, (char **) NULL, 10); + } + + ctimes = _uuconf_istrsplit (pzsplit[1], ',', &pzcomma, &ccomma); + if (ctimes < 0) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + for (i = 0; i < ctimes; i++) + { + char *zslash; + char bgrade; + + z = pzcomma[i]; + zslash = strchr (z, '/'); + if (zslash == NULL) + bgrade = UUCONF_GRADE_LOW; + else + { + *zslash = '\0'; + bgrade = zslash[1]; + if (! UUCONF_GRADE_LEGAL (bgrade)) + bgrade = UUCONF_GRADE_LOW; + } + + iret = _uuconf_itime_parse (qglobal, z, (long) bgrade, cretry, + _uuconf_itime_grade_cmp, + &qset->uuconf_qtimegrade, + pblock); + if (iret != UUCONF_SUCCESS) + break; + } + + if (iret != UUCONF_SUCCESS) + break; + + if (ctoks < 3) + continue; + + /* Pick up the device name. It can be followed by a comma and a + list of protocols (this is not actually supported by most V2 + systems, but it should be compatible). */ + qset->uuconf_zport = pzsplit[2]; + z = strchr (pzsplit[2], ','); + if (z != NULL) + { + qset->uuconf_zprotocols = z + 1; + *z = '\0'; + } + + /* If the port is "TCP", we set up a system specific port. The + baud rate becomes the service number and the phone number + becomes the address (still stored in qsys->zphone). */ + if (strcmp (qset->uuconf_zport, "TCP") == 0) + { + qset->uuconf_zport = NULL; + qset->uuconf_qport = ((struct uuconf_port *) + uuconf_malloc (pblock, + sizeof (struct uuconf_port))); + if (qset->uuconf_qport == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + _uuconf_uclear_port (qset->uuconf_qport); + qset->uuconf_qport->uuconf_zname = (char *) "TCP"; + qset->uuconf_qport->uuconf_ttype = UUCONF_PORTTYPE_TCP; + qset->uuconf_qport->uuconf_ireliable + = (UUCONF_RELIABLE_ENDTOEND | UUCONF_RELIABLE_RELIABLE + | UUCONF_RELIABLE_EIGHT | UUCONF_RELIABLE_FULLDUPLEX + | UUCONF_RELIABLE_SPECIFIED); + if (ctoks < 4) + qset->uuconf_qport->uuconf_u.uuconf_stcp.uuconf_zport + = (char *) "uucp"; + else + qset->uuconf_qport->uuconf_u.uuconf_stcp.uuconf_zport + = pzsplit[3]; + } + + if (ctoks < 4) + continue; + + qset->uuconf_ibaud = strtol (pzsplit[3], (char **) NULL, 10); + + if (ctoks < 5) + continue; + + /* Get the phone number. */ + qset->uuconf_zphone = pzsplit[4]; + + if (ctoks < 6) + continue; + + /* Get the chat script. We just hand this off to the chat + script processor, so that it will parse subsend and + subexpect strings correctly. */ + pzsplit[4] = (char *) "chat"; + iret = _uuconf_ichat_cmd (qglobal, ctoks - 4, pzsplit + 4, + &qset->uuconf_schat, pblock); + iret &=~ UUCONF_CMDTABRET_KEEP; + if (iret != UUCONF_SUCCESS) + break; + } + + (void) fclose (e); + + if (pzcomma != NULL) + free ((pointer) pzcomma); + + if (iret != UUCONF_SUCCESS) + { + if (zline != NULL) + free ((pointer) zline); + if (pzsplit != NULL) + free ((pointer) pzsplit); + qglobal->zfilename = qglobal->qprocess->zv2systems; + return iret | UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; + } + + if (pblock == NULL) + { + if (zline != NULL) + free ((pointer) zline); + if (pzsplit != NULL) + free ((pointer) pzsplit); + return UUCONF_NOT_FOUND; + } + + /* Now read USERFILE and L.cmds to get permissions. We can't fully + handle USERFILE since that specifies permissions based on local + users which we do not support. */ + { + e = fopen (qglobal->qprocess->zv2userfile, "r"); + if (e != NULL) + { + char **pzlocal, **pzremote; + boolean fdefault_callback; + char *zdefault_login; + struct uuconf_system *q; + + pzlocal = NULL; + pzremote = NULL; + fdefault_callback = FALSE; + zdefault_login = NULL; + + qglobal->ilineno = 0; + + while ((cchars = getline (&zline, &cline, e)) > 0) + { + int ctoks; + char *zcomma; + boolean fcallback; + char **pzlist, **pznew; + + ++qglobal->ilineno; + + --cchars; + if (zline[cchars] == '\n') + zline[cchars] = '\0'; + zline[strcspn (zline, "#")] = '\0'; + + ctoks = _uuconf_istrsplit (zline, '\0', &pzsplit, &csplit); + if (ctoks < 0) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + + if (ctoks == 0) + continue; + + /* The first field is username,machinename */ + zcomma = strchr (pzsplit[0], ','); + if (zcomma == NULL) + continue; + + *zcomma++ = '\0'; + + /* The rest of the line is the list of directories, except + that if the first directory is "c" we must call the + system back. */ + fcallback = FALSE; + pzlist = pzsplit + 1; + --ctoks; + if (ctoks > 0 + && pzsplit[1][0] == 'c' + && pzsplit[1][1] == '\0') + { + fcallback = TRUE; + pzlist = pzsplit + 2; + --ctoks; + } + + /* Now pzsplit[0] is the user name, zcomma is the system + name, fcallback indicates whether a call back is + required, ctoks is the number of directories and pzlist + points to the directories. If the system name matches, + then the user name is the name that the system must use + to log in, and the list of directories is what may be + transferred in by either local or remote request. + Otherwise, if no system name matches, then the first + line with no user name gives the list of directories + that may be transferred by local request, and the first + line with no system name gives the list of directories + that may be transferred by remote request. */ + if ((pzsplit[0][0] != '\0' || pzlocal != NULL) + && (zcomma[0] != '\0' || pzremote != NULL) + && strcmp (zcomma, zsystem) != 0) + continue; + + /* NULL terminate the list of directories. */ + pznew = (char **) uuconf_malloc (pblock, + (ctoks + 1) * sizeof (char *)); + if (pznew == NULL) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + memcpy ((pointer) pznew, (pointer) pzlist, + ctoks * sizeof (char *)); + pznew[ctoks] = NULL; + + if (uuconf_add_block (pblock, zline) != 0) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + break; + } + zline = NULL; + cline = 0; + + if (pzsplit[0][0] == '\0') + { + pzlocal = pznew; + fdefault_callback = fcallback; + } + else if (zcomma[0] == '\0') + { + pzremote = pznew; + zdefault_login = pzsplit[0]; + } + else + { + /* Both the login name and the machine name were + listed; require the machine to be logged in under + this name. This is not fully backward compatible, + and perhaps should be changed. On the other hand, + it is more useful. */ + for (q = qsys; q != NULL; q = q->uuconf_qalternate) + { + q->uuconf_zcalled_login = pzsplit[0]; + q->uuconf_fcallback = fcallback; + q->uuconf_pzlocal_send = pznew; + q->uuconf_pzlocal_receive = pznew; + q->uuconf_pzremote_send = pznew; + q->uuconf_pzremote_receive = pznew; + } + + break; + } + } + + (void) fclose (e); + + if (iret != UUCONF_SUCCESS) + { + if (zline != NULL) + free ((pointer) zline); + if (pzsplit != NULL) + free ((pointer) pzsplit); + qglobal->zfilename = qglobal->qprocess->zv2userfile; + return iret | UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; + } + + if (qsys->uuconf_pzlocal_send == (char **) &_uuconf_unset + && pzlocal != NULL) + { + for (q = qsys; q != NULL; q = q->uuconf_qalternate) + { + q->uuconf_fcallback = fdefault_callback; + q->uuconf_pzlocal_send = pzlocal; + q->uuconf_pzlocal_receive = pzlocal; + } + } + + if (qsys->uuconf_pzremote_send == (char **) &_uuconf_unset + && pzremote != NULL) + { + for (q = qsys; q != NULL; q = q->uuconf_qalternate) + { + q->uuconf_zcalled_login = zdefault_login; + q->uuconf_pzremote_send = pzremote; + q->uuconf_pzremote_receive = pzremote; + } + } + } + } + + /* Now we must read L.cmds to determine which commands may be + executed. */ + { + e = fopen (qglobal->qprocess->zv2cmds, "r"); + if (e != NULL) + { + qglobal->ilineno = 0; + + if (getline (&zline, &cline, e) > 0) + { + ++qglobal->ilineno; + + zline[strcspn (zline, "#\n")] = '\0'; + if (strncmp (zline, "PATH=", sizeof "PATH=" - 1) == 0) + { + int ctoks; + char **pznew; + + zline += sizeof "PATH=" - 1; + ctoks = _uuconf_istrsplit (zline, ':', &pzsplit, &csplit); + if (ctoks < 0) + { + qglobal->ierrno = errno; + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + + pznew = NULL; + if (iret == UUCONF_SUCCESS) + { + pznew = ((char **) + uuconf_malloc (pblock, + (ctoks + 1) * sizeof (char *))); + if (pznew == NULL) + iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; + } + if (iret == UUCONF_SUCCESS) + { + memcpy ((pointer) pznew, (pointer) pzsplit, + ctoks * sizeof (char *)); + pznew[ctoks] = NULL; + qsys->uuconf_pzpath = pznew; + zline = NULL; + cline = 0; + } + + if (getline (&zline, &cline, e) < 0) + { + if (zline != NULL) + { + free ((pointer) zline); + zline = NULL; + } + } + else + ++qglobal->ilineno; + } + } + + if (iret == UUCONF_SUCCESS && zline != NULL) + { + while (TRUE) + { + zline[strcspn (zline, "#\n")] = '\0'; + iret = _uuconf_iadd_string (qglobal, zline, TRUE, FALSE, + &qsys->uuconf_pzcmds, + pblock); + if (iret != UUCONF_SUCCESS) + break; + if (getline (&zline, &cline, e) < 0) + break; + ++qglobal->ilineno; + } + } + + (void) fclose (e); + + if (iret != UUCONF_SUCCESS) + { + qglobal->zfilename = qglobal->qprocess->zv2cmds; + iret |= UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; + } + } + } + + if (zline != NULL) + free ((pointer) zline); + if (pzsplit != NULL) + free ((pointer) pzsplit); + + return iret; +} diff --git a/gnu/libexec/uucp/libuuconf/vsnams.c b/gnu/libexec/uucp/libuuconf/vsnams.c new file mode 100644 index 0000000000..0bf04b581d --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/vsnams.c @@ -0,0 +1,106 @@ +/* vsnams.c + Get all known system names from the V2 configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_vsnams_rcsid[] = "$Id: vsnams.c,v 1.1 1993/08/04 19:35:29 jtc Exp $"; +#endif + +#include + +/* Get all the system names from the V2 L.sys file. This code does + not support aliases, although some V2 versions do have an L-aliases + file. */ + +/*ARGSUSED*/ +int +uuconf_v2_system_names (pglobal, ppzsystems, falias) + pointer pglobal; + char ***ppzsystems; + int falias; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + FILE *e; + int iret; + char *zline; + size_t cline; + + *ppzsystems = NULL; + + e = fopen (qglobal->qprocess->zv2systems, "r"); + if (e == NULL) + { + if (FNO_SUCH_FILE ()) + return _uuconf_iadd_string (qglobal, (char *) NULL, FALSE, FALSE, + ppzsystems, (pointer) NULL); + qglobal->ierrno = errno; + qglobal->zfilename = qglobal->qprocess->zv2systems; + return (UUCONF_FOPEN_FAILED + | UUCONF_ERROR_ERRNO + | UUCONF_ERROR_FILENAME); + } + + qglobal->ilineno = 0; + iret = UUCONF_SUCCESS; + + zline = NULL; + cline = 0; + while (getline (&zline, &cline, e) > 0) + { + char *zname; + + ++qglobal->ilineno; + + /* Skip leading whitespace to get to the system name. Then cut + the system name off at the first whitespace, comment, or + newline. */ + zname = zline + strspn (zline, " \t"); + zname[strcspn (zname, " \t#\n")] = '\0'; + if (*zname == '\0') + continue; + + iret = _uuconf_iadd_string (qglobal, zname, TRUE, TRUE, ppzsystems, + (pointer) NULL); + if (iret != UUCONF_SUCCESS) + break; + } + + (void) fclose (e); + if (zline != NULL) + free ((pointer) zline); + + if (iret != UUCONF_SUCCESS) + { + qglobal->zfilename = qglobal->qprocess->zv2systems; + return iret | UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; + } + + if (*ppzsystems == NULL) + iret = _uuconf_iadd_string (qglobal, (char *) NULL, FALSE, FALSE, + ppzsystems, (pointer) NULL); + + return iret; +} diff --git a/gnu/libexec/uucp/libuuconf/vsys.c b/gnu/libexec/uucp/libuuconf/vsys.c new file mode 100644 index 0000000000..c0280cce3f --- /dev/null +++ b/gnu/libexec/uucp/libuuconf/vsys.c @@ -0,0 +1,49 @@ +/* vsys.c + User function to get a system from the V2 configuration files. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP uuconf library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char _uuconf_vsys_rcsid[] = "$Id: vsys.c,v 1.1 1993/08/04 19:35:30 jtc Exp $"; +#endif + +/* Get system information from the V2 configuration files. This is a + wrapper for the internal function which makes sure that every field + gets a default value. */ + +int +uuconf_v2_system_info (pglobal, zsystem, qsys) + pointer pglobal; + const char *zsystem; + struct uuconf_system *qsys; +{ + struct sglobal *qglobal = (struct sglobal *) pglobal; + int iret; + + iret = _uuconf_iv2_system_internal (qglobal, zsystem, qsys); + if (iret != UUCONF_SUCCESS) + return iret; + return _uuconf_isystem_basic_default (qglobal, qsys); +} diff --git a/gnu/libexec/uucp/libuucp/MANIFEST b/gnu/libexec/uucp/libuucp/MANIFEST new file mode 100644 index 0000000000..093924858e --- /dev/null +++ b/gnu/libexec/uucp/libuucp/MANIFEST @@ -0,0 +1,27 @@ +Makefile.in +MANIFEST +bsrch.c +buffer.c +bzero.c +crc.c +debug.c +escape.c +getlin.c +getopt.c +getop1.c +memchr.c +memcmp.c +memcpy.c +parse.c +spool.c +status.c +strcas.c +strchr.c +strdup.c +strncs.c +strrch.c +strstr.c +strtol.c +xfree.c +xmall.c +xreall.c diff --git a/gnu/libexec/uucp/libuucp/Makefile b/gnu/libexec/uucp/libuucp/Makefile new file mode 100644 index 0000000000..179bfef71d --- /dev/null +++ b/gnu/libexec/uucp/libuucp/Makefile @@ -0,0 +1,14 @@ +# This is the Makefile for the libuucp subdirectory of Taylor UUCP +# $Id: Makefile,v 1.1 1993/08/05 18:26:29 conklin Exp $ + +LIB= uucp +SRCS= buffer.c crc.c debug.c escape.c getopt.c getop1.c parse.c \ + spool.c status.c xfree.c xmall.c xreall.c getlin.c +CFLAGS+= -I$(.CURDIR)/../common_sources + +NOMAN= noman +NOPROFILE= noprofile + +install: + +.include diff --git a/gnu/libexec/uucp/libuucp/bsrch.c b/gnu/libexec/uucp/libuucp/bsrch.c new file mode 100644 index 0000000000..3b1a61c170 --- /dev/null +++ b/gnu/libexec/uucp/libuucp/bsrch.c @@ -0,0 +1,54 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. + +This file was modified slightly by Ian Lance Taylor, May 1992, for +Taylor UUCP. */ + +#include "uucp.h" + +/* Perform a binary search for KEY in BASE which has NMEMB elements + of SIZE bytes each. The comparisons are done by (*COMPAR)(). */ +pointer +bsearch (key, base, nmemb, size, compar) + register constpointer key; + register constpointer base; + size_t nmemb; + register size_t size; + register int (*compar) P((constpointer, constpointer)); +{ + register size_t l, u, idx; + register constpointer p; + register int comparison; + + l = 0; + u = nmemb; + while (l < u) + { + idx = (l + u) / 2; + p = (constpointer) (((const char *) base) + (idx * size)); + comparison = (*compar)(key, p); + if (comparison < 0) + u = idx; + else if (comparison > 0) + l = idx + 1; + else + return (pointer) p; + } + + return NULL; +} diff --git a/gnu/libexec/uucp/libuucp/buffer.c b/gnu/libexec/uucp/libuucp/buffer.c new file mode 100644 index 0000000000..c44fa45139 --- /dev/null +++ b/gnu/libexec/uucp/libuucp/buffer.c @@ -0,0 +1,109 @@ +/* buffer.c + Manipulate buffers used to hold strings. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of Taylor UUCP. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" + +/* We keep a linked list of buffers. The union is a hack because the + default definition of offsetof, in uucp.h, takes the address of the + field, and some C compilers will not let you take the address of an + array. */ + +struct sbuf +{ + struct sbuf *qnext; + size_t c; + union + { + char ab[4]; + char bdummy; + } + u; +}; + +static struct sbuf *qBlist; + +/* Get a buffer of a given size. The buffer is returned with the + ubuffree function. */ + +char * +zbufalc (c) + size_t c; +{ + register struct sbuf *q; + + if (qBlist == NULL) + { + q = (struct sbuf *) xmalloc (sizeof (struct sbuf) + c - 4); + q->c = c; + } + else + { + q = qBlist; + qBlist = q->qnext; + if (q->c < c) + { + q = (struct sbuf *) xrealloc ((pointer) q, + sizeof (struct sbuf) + c - 4); + q->c = c; + } + } + return q->u.ab; +} + +/* Get a buffer holding a given string. */ + +char * +zbufcpy (z) + const char *z; +{ + size_t csize; + char *zret; + + if (z == NULL) + return NULL; + csize = strlen (z) + 1; + zret = zbufalc (csize); + memcpy (zret, z, csize); + return zret; +} + +/* Free up a buffer back onto the linked list. */ + +void +ubuffree (z) + char *z; +{ + size_t ioff; + struct sbuf *q; + + if (z == NULL) + return; + ioff = offsetof (struct sbuf, u); + q = (struct sbuf *) (pointer) (z - ioff); + q->qnext = qBlist; + qBlist = q; +} diff --git a/gnu/libexec/uucp/libuucp/bzero.c b/gnu/libexec/uucp/libuucp/bzero.c new file mode 100644 index 0000000000..098e551572 --- /dev/null +++ b/gnu/libexec/uucp/libuucp/bzero.c @@ -0,0 +1,15 @@ +/* bzero.c + Zero out a buffer. */ + +#include "uucp.h" + +void +bzero (parg, c) + pointer parg; + int c; +{ + char *p = (char *) parg; + + while (c-- != 0) + *p++ = 0; +} diff --git a/gnu/libexec/uucp/libuucp/crc.c b/gnu/libexec/uucp/libuucp/crc.c new file mode 100644 index 0000000000..fc9687b57c --- /dev/null +++ b/gnu/libexec/uucp/libuucp/crc.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + */ + +/* Modified slightly by Ian Lance Taylor, ian@airs.com, for use with + Taylor UUCP. */ + +#include "uucp.h" +#include "prot.h" + +/* First, the polynomial itself and its table of feedback terms. The */ +/* polynomial is */ +/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ +/* Note that we take it "backwards" and put the highest-order term in */ +/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ +/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ +/* the MSB being 1. */ + +/* Note that the usual hardware shift register implementation, which */ +/* is what we're using (we're merely optimizing it by doing eight-bit */ +/* chunks at a time) shifts bits into the lowest-order term. In our */ +/* implementation, that means shifting towards the right. Why do we */ +/* do it this way? Because the calculated CRC must be transmitted in */ +/* order from highest-order term to lowest-order term. UARTs transmit */ +/* characters in order from LSB to MSB. By storing the CRC this way, */ +/* we hand it to the UART in the order low-byte to high-byte; the UART */ +/* sends each low-bit to hight-bit; and the result is transmission bit */ +/* by bit from highest- to lowest-order term without requiring any bit */ +/* shuffling on our part. Reception works similarly. */ + +/* The feedback terms table consists of 256, 32-bit entries. Notes: */ +/* */ +/* The table can be generated at runtime if desired; code to do so */ +/* is shown later. It might not be obvious, but the feedback */ +/* terms simply represent the results of eight shift/xor opera- */ +/* tions for all combinations of data and CRC register values. */ +/* [this code is no longer present--ian] */ +/* */ +/* The values must be right-shifted by eight bits by the "updcrc" */ +/* logic; the shift must be unsigned (bring in zeroes). On some */ +/* hardware you could probably optimize the shift in assembler by */ +/* using byte-swap instructions. */ + +static const unsigned long aicrc32tab[] = { /* CRC polynomial 0xedb88320 */ +0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, +0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, +0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, +0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, +0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, +0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, +0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, +0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, +0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, +0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, +0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, +0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, +0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, +0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, +0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, +0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, +0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, +0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, +0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, +0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, +0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, +0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, +0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, +0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, +0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, +0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, +0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, +0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, +0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, +0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, +0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, +0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL +}; + +/* + * IUPDC32 macro derived from article Copyright (C) 1986 Stephen Satchell. + * NOTE: First argument must be in range 0 to 255. + * Second argument is referenced twice. + * + * Programmers may incorporate any or all code into their programs, + * giving proper credit within the source. Publication of the + * source routines is permitted so long as proper credit is given + * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg, + * Omen Technology. + */ + +#define IUPDC32(b, ick) \ + (aicrc32tab[((int) (ick) ^ (b)) & 0xff] ^ (((ick) >> 8) & 0x00ffffffL)) + +unsigned long +icrc (z, c, ick) + const char *z; + size_t c; + unsigned long ick; +{ + while (c > 4) + { + ick = IUPDC32 (*z++, ick); + ick = IUPDC32 (*z++, ick); + ick = IUPDC32 (*z++, ick); + ick = IUPDC32 (*z++, ick); + c -= 4; + } + while (c-- != 0) + ick = IUPDC32 (*z++, ick); + return ick; +} diff --git a/gnu/libexec/uucp/libuucp/debug.c b/gnu/libexec/uucp/libuucp/debug.c new file mode 100644 index 0000000000..86f784154a --- /dev/null +++ b/gnu/libexec/uucp/libuucp/debug.c @@ -0,0 +1,165 @@ +/* debug.c + UUCP debugging functions. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include + +#include "uudefs.h" + +/* The debugging level. */ +int iDebug; + +/* Parse a debugging string. This may be a simple number, which sets + the given number of bits in iDebug, or it may be a series of single + letters. */ + +static const char * const azDebug_names[] = DEBUG_NAMES; + +int +idebug_parse (z) + const char *z; +{ + char *zend; + int i, iret; + char *zcopy, *ztok; + + if (strncasecmp (z, DEBUG_NONE, sizeof DEBUG_NONE - 1) == 0) + return 0; + + i = (int) strtol ((char *) z, &zend, 0); + if (*zend == '\0') + { + if (i > 15) + i = 15; + else if (i < 0) + i = 0; + return (1 << i) - 1; + } + + zcopy = zbufcpy (z); + + iret = 0; + + for (ztok = strtok (zcopy, ","); + ztok != NULL; + ztok = strtok ((char *) NULL, ",")) + { + if (strcasecmp (ztok, "all") == 0) + { + iret = DEBUG_MAX; + break; + } + for (i = 0; azDebug_names[i] != NULL; i++) + { + if (strncasecmp (ztok, azDebug_names[i], + strlen (azDebug_names[i])) == 0) + { + iret |= 1 << i; + break; + } + } + if (azDebug_names[i] == NULL) + ulog (LOG_ERROR, "Unrecognized debugging option \"%s\"", + ztok); + } + + ubuffree (zcopy); + + return iret; +} + +/* A debugging routine used when displaying buffers. */ + +size_t +cdebug_char (z, ichar) + char *z; + int ichar; +{ + char b; + + if (isprint (BUCHAR (ichar)) + && ichar != '\"' + && ichar != '\\') + { + *z++ = (char) ichar; + *z = '\0'; + return 1; + } + + *z++ = '\\'; + + switch (ichar) + { + case '\n': + b = 'n'; + break; + case '\r': + b = 'r'; + break; + case '\"': + b = '\"'; + break; + case '\\': + b = '\\'; + break; + default: + sprintf (z, "%03o", (unsigned int) BUCHAR (ichar)); + return strlen (z) + 1; + } + + *z++ = b; + *z = '\0'; + return 2; +} + +/* Display a buffer when debugging. */ + +void +udebug_buffer (zhdr, zbuf, clen) + const char *zhdr; + const char *zbuf; + size_t clen; +{ + char *z, *zalc; + int i; + + zalc = zbufalc (clen * 4 + 1); + + z = zalc; + for (i = 0; i < clen && i < 80; i++) + z += cdebug_char (z, zbuf[i]); + if (i < clen) + { + *z++ = '.'; + *z++ = '.'; + *z++ = '.'; + } + *z = '\0'; + + ulog (LOG_DEBUG, "%s %lu \"%s\"", zhdr, (unsigned long) clen, zalc); + + ubuffree (zalc); +} diff --git a/gnu/libexec/uucp/libuucp/escape.c b/gnu/libexec/uucp/libuucp/escape.c new file mode 100644 index 0000000000..646b787d6e --- /dev/null +++ b/gnu/libexec/uucp/libuucp/escape.c @@ -0,0 +1,98 @@ +/* escape.c + Translate escape sequences. */ + +#include "uucp.h" + +#include + +#include "uudefs.h" + +size_t +cescape (z) + char *z; +{ + char *zto, *zfrom; + + zto = z; + zfrom = z; + while (*zfrom != '\0') + { + if (*zfrom != '\\') + { + *zto++ = *zfrom++; + continue; + } + ++zfrom; + switch (*zfrom) + { + case '-': + *zto++ = '-'; + break; + case 'b': + *zto++ = '\b'; + break; + case 'n': + *zto++ = '\n'; + break; + case 'N': + *zto++ = '\0'; + break; + case 'r': + *zto++ = '\r'; + break; + case 's': + *zto++ = ' '; + break; + case 't': + *zto++ = '\t'; + break; + case '\0': + --zfrom; + /* Fall through. */ + case '\\': + *zto++ = '\\'; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + int i; + + i = *zfrom - '0'; + if (zfrom[1] >= '0' && zfrom[1] <= '7') + i = 8 * i + *++zfrom - '0'; + if (zfrom[1] >= '0' && zfrom[1] <= '7') + i = 8 * i + *++zfrom - '0'; + *zto++ = (char) i; + } + break; + case 'x': + { + int i; + + i = 0; + while (isxdigit (BUCHAR (zfrom[1]))) + { + if (isdigit (BUCHAR (zfrom[1]))) + i = 16 * i + *++zfrom - '0'; + else if (isupper (BUCHAR (zfrom[1]))) + i = 16 * i + *++zfrom - 'A' + 10; + else + i = 16 * i + *++zfrom - 'a' + 10; + } + *zto++ = (char) i; + } + break; + default: + ulog (LOG_ERROR, "Unrecognized escape sequence \\%c", + *zfrom); + *zto++ = *zfrom; + break; + } + + ++zfrom; + } + + *zto = '\0'; + + return (size_t) (zto - z); +} diff --git a/gnu/libexec/uucp/libuucp/getlin.c b/gnu/libexec/uucp/libuucp/getlin.c new file mode 100644 index 0000000000..1c204e74ee --- /dev/null +++ b/gnu/libexec/uucp/libuucp/getlin.c @@ -0,0 +1,81 @@ +/* getlin.c + Replacement for getline. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of Taylor UUCP. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +/* Read a line from a file, returning the number of characters read. + This should really return ssize_t. Returns -1 on error. */ + +#define CGETLINE_DEFAULT (63) + +int +getline (pzline, pcline, e) + char **pzline; + size_t *pcline; + FILE *e; +{ + char *zput, *zend; + int bchar; + + if (*pzline == NULL) + { + *pzline = (char *) malloc (CGETLINE_DEFAULT); + if (*pzline == NULL) + return -1; + *pcline = CGETLINE_DEFAULT; + } + + zput = *pzline; + zend = *pzline + *pcline - 1; + + while ((bchar = getc (e)) != EOF) + { + if (zput >= zend) + { + size_t cnew; + char *znew; + + cnew = *pcline * 2 + 1; + znew = (char *) realloc ((pointer) *pzline, cnew); + if (znew == NULL) + return -1; + zput = znew + *pcline - 1; + zend = znew + cnew - 1; + *pzline = znew; + *pcline = cnew; + } + + *zput++ = bchar; + + if (bchar == '\n') + break; + } + + if (zput == *pzline) + return -1; + + *zput = '\0'; + return zput - *pzline; +} diff --git a/gnu/libexec/uucp/libuucp/getop1.c b/gnu/libexec/uucp/libuucp/getop1.c new file mode 100644 index 0000000000..c3ebc08bb3 --- /dev/null +++ b/gnu/libexec/uucp/libuucp/getop1.c @@ -0,0 +1,144 @@ +/* Getopt for GNU. + Copyright (C) 1987, 88, 89, 90, 91, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + This file was modified slightly by Ian Lance Taylor, June 1992, for + Taylor UUCP. */ + +#include "uucp.h" + +#include "getopt.h" + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + +#ifdef TEST + +#include + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == EOF) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/gnu/libexec/uucp/libuucp/getopt.c b/gnu/libexec/uucp/libuucp/getopt.c new file mode 100644 index 0000000000..f97c64eda5 --- /dev/null +++ b/gnu/libexec/uucp/libuucp/getopt.c @@ -0,0 +1,621 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + This file was modified slightly by Ian Lance Taylor, June 1992, for + Taylor UUCP. */ + +#include "uucp.h" +#include "uudefs.h" + +/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a + long-named option. Because this is not POSIX.2 compliant, it is + being phased out. */ +#undef GETOPT_COMPAT + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = 0; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +#define my_index strchr +#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n)) + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (argv) + char **argv; +{ + size_t nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); + char **temp = (char **) malloc (nonopts_size); + + if (temp == NULL) + abort (); + + /* Interchange the two blocks of data in ARGV. */ + + my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size); + my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt], + (optind - last_nonopt) * sizeof (char *)); + my_bcopy ((char *) temp, + (char *) &argv[first_nonopt + optind - last_nonopt], + nonopts_size); + + xfree (temp); + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + int option_index; + + optarg = 0; + + /* Initialize the internal data when the first call is made. + Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + if (optind == 0) + { + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (getenv ("POSIXLY_CORRECT") != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + } + + if (nextchar == NULL || *nextchar == '\0') + { + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Now skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + optind++; + last_nonopt = optind; + } + + /* Special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Start decoding its characters. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + if (longopts != NULL + && ((argv[optind][0] == '-' + && (argv[optind][1] == '-' || long_only)) +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + )) + { + const struct option *p; + char *s = nextchar; + int exact = 0; + int ambig = 0; + const struct option *pfound = NULL; + int indfound = 0; + + while (*s && *s != '=') + s++; + + /* Test all options for either exact match or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; + p++, option_index++) + if (!strncmp (p->name, nextchar, (size_t) (s - nextchar))) + { + if (s - nextchar == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*s) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = s + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return '?'; + } + } + + /* Look at and handle the next option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (c < 040 || c >= 0177) + fprintf (stderr, "%s: unrecognized option, character code 0%o\n", + argv[0], (unsigned int) c); + else + fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); + } + return '?'; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = 0; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + fprintf (stderr, "%s: option `-%c' requires an argument\n", + argv[0], c); + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/gnu/libexec/uucp/libuucp/memchr.c b/gnu/libexec/uucp/libuucp/memchr.c new file mode 100644 index 0000000000..e1f399afeb --- /dev/null +++ b/gnu/libexec/uucp/libuucp/memchr.c @@ -0,0 +1,149 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. + Based on strlen implemention by Torbjorn Granlund (tege@sics.se), + with help from Dan Sahlin (dan@sics.se) and + commentary by Jim Blandy (jimb@ai.mit.edu); + adaptation to memchr suggested by Dick Karpinski (dick@cca.ucsf.edu), + and implemented by Roland McGrath (roland@ai.mit.edu). + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. + +This file was modified slightly by Ian Lance Taylor, May 1992, for +Taylor UUCP. It assumes 32 bit longs. I'm willing to trust that any +system which does not have 32 bit longs will have its own +implementation of memchr. */ + +#include "uucp.h" + +/* Search no more than N bytes of S for C. */ + +pointer +memchr (s, c, n) + constpointer s; + int c; + size_t n; +{ + const char *char_ptr; + const unsigned long int *longword_ptr; + unsigned long int longword, magic_bits, charmask; + + c = BUCHAR (c); + + /* Handle the first few characters by reading one character at a time. + Do this until CHAR_PTR is aligned on a 4-byte border. */ + for (char_ptr = s; n > 0 && ((unsigned long int) char_ptr & 3) != 0; + --n, ++char_ptr) + if (BUCHAR (*char_ptr) == c) + return (pointer) char_ptr; + + longword_ptr = (unsigned long int *) char_ptr; + + /* Bits 31, 24, 16, and 8 of this number are zero. Call these bits + the "holes." Note that there is a hole just to the left of + each byte, with an extra at the end: + + bits: 01111110 11111110 11111110 11111111 + bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD + + The 1-bits make sure that carries propagate to the next 0-bit. + The 0-bits provide holes for carries to fall into. */ + magic_bits = 0x7efefeff; + + /* Set up a longword, each of whose bytes is C. */ + charmask = c | (c << 8); + charmask |= charmask << 16; + + /* Instead of the traditional loop which tests each character, + we will test a longword at a time. The tricky part is testing + if *any of the four* bytes in the longword in question are zero. */ + while (n >= 4) + { + /* We tentatively exit the loop if adding MAGIC_BITS to + LONGWORD fails to change any of the hole bits of LONGWORD. + + 1) Is this safe? Will it catch all the zero bytes? + Suppose there is a byte with all zeros. Any carry bits + propagating from its left will fall into the hole at its + least significant bit and stop. Since there will be no + carry from its most significant bit, the LSB of the + byte to the left will be unchanged, and the zero will be + detected. + + 2) Is this worthwhile? Will it ignore everything except + zero bytes? Suppose every byte of LONGWORD has a bit set + somewhere. There will be a carry into bit 8. If bit 8 + is set, this will carry into bit 16. If bit 8 is clear, + one of bits 9-15 must be set, so there will be a carry + into bit 16. Similarly, there will be a carry into bit + 24. If one of bits 24-30 is set, there will be a carry + into bit 31, so all of the hole bits will be changed. + + The one misfire occurs when bits 24-30 are clear and bit + 31 is set; in this case, the hole at bit 31 is not + changed. If we had access to the processor carry flag, + we could close this loophole by putting the fourth hole + at bit 32! + + So it ignores everything except 128's, when they're aligned + properly. + + 3) But wait! Aren't we looking for C, not zero? + Good point. So what we do is XOR LONGWORD with a longword, + each of whose bytes is C. This turns each byte that is C + into a zero. */ + + longword = *longword_ptr++ ^ charmask; + + /* Add MAGIC_BITS to LONGWORD. */ + if ((((longword + magic_bits) + + /* Set those bits that were unchanged by the addition. */ + ^ ~longword) + + /* Look at only the hole bits. If any of the hole bits + are unchanged, most likely one of the bytes was a + zero. */ + & ~magic_bits) != 0) + { + /* Which of the bytes was C? If none of them were, it was + a misfire; continue the search. */ + + const char *cp = (const char *) (longword_ptr - 1); + + if (BUCHAR (cp[0]) == c) + return (pointer) cp; + if (BUCHAR (cp[1]) == c) + return (pointer) &cp[1]; + if (BUCHAR (cp[2]) == c) + return (pointer) &cp[2]; + if (BUCHAR (cp[3]) == c) + return (pointer) &cp[3]; + } + + n -= 4; + } + + char_ptr = (const char *) longword_ptr; + + while (n-- > 0) + { + if (BUCHAR (*char_ptr) == c) + return (pointer) char_ptr; + else + ++char_ptr; + } + + return NULL; +} diff --git a/gnu/libexec/uucp/libuucp/memcmp.c b/gnu/libexec/uucp/libuucp/memcmp.c new file mode 100644 index 0000000000..b61578a4af --- /dev/null +++ b/gnu/libexec/uucp/libuucp/memcmp.c @@ -0,0 +1,19 @@ +/* memcmp.c + Compare two memory buffers. */ + +#include "uucp.h" + +int +memcmp (p1arg, p2arg, c) + constpointer p1arg; + constpointer p2arg; + size_t c; +{ + const char *p1 = (const char *) p1arg; + const char *p2 = (const char *) p2arg; + + while (c-- != 0) + if (*p1++ != *p2++) + return BUCHAR (*--p1) - BUCHAR (*--p2); + return 0; +} diff --git a/gnu/libexec/uucp/libuucp/memcpy.c b/gnu/libexec/uucp/libuucp/memcpy.c new file mode 100644 index 0000000000..2258123752 --- /dev/null +++ b/gnu/libexec/uucp/libuucp/memcpy.c @@ -0,0 +1,18 @@ +/* memcpy.c + Copy one memory buffer to another. */ + +#include "uucp.h" + +pointer +memcpy (ptoarg, pfromarg, c) + pointer ptoarg; + constpointer pfromarg; + size_t c; +{ + char *pto = (char *) ptoarg; + const char *pfrom = (const char *) pfromarg; + + while (c-- != 0) + *pto++ = *pfrom++; + return ptoarg; +} diff --git a/gnu/libexec/uucp/libuucp/parse.c b/gnu/libexec/uucp/libuucp/parse.c new file mode 100644 index 0000000000..6b928a4eed --- /dev/null +++ b/gnu/libexec/uucp/libuucp/parse.c @@ -0,0 +1,207 @@ +/* parse.c + Parse a UUCP command string. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char parse_rcsid[] = "$Id: parse.c,v 1.1 1993/08/04 19:35:49 jtc Exp $"; +#endif + +#include "uudefs.h" + +/* Parse a UUCP command string into an scmd structure. This is called + by the 'g' protocol and the UNIX command file reading routines. It + destroys the string it is passed, and the scmd string pointers are + left pointing into it. For the convenience of the Unix work file + routines, it will parse "P" into a simple 'P' command (representing + a poll file). It returns TRUE if the string is successfully + parsed, FALSE otherwise. */ + +boolean +fparse_cmd (zcmd, qcmd) + char *zcmd; + struct scmd *qcmd; +{ + char *z, *zend; + + z = strtok (zcmd, " \t\n"); + if (z == NULL) + return FALSE; + + qcmd->bcmd = *z; + if (qcmd->bcmd != 'S' + && qcmd->bcmd != 'R' + && qcmd->bcmd != 'X' + && qcmd->bcmd != 'E' + && qcmd->bcmd != 'H' + && qcmd->bcmd != 'P') + return FALSE; + + qcmd->pseq = NULL; + qcmd->zfrom = NULL; + qcmd->zto = NULL; + qcmd->zuser = NULL; + qcmd->zoptions = NULL; + qcmd->ztemp = NULL; + qcmd->imode = 0666; + qcmd->znotify = NULL; + qcmd->cbytes = -1; + qcmd->zcmd = NULL; + qcmd->ipos = 0; + + /* Handle hangup commands specially. If it's just "H", return + the command 'H' to indicate a hangup request. If it's "HY" + return 'Y' and if it's "HN" return 'N'. */ + if (qcmd->bcmd == 'H') + { + if (z[1] != '\0') + { + if (z[1] == 'Y') + qcmd->bcmd = 'Y'; + else if (z[1] == 'N') + qcmd->bcmd = 'N'; + else + return FALSE; + } + + return TRUE; + } + if (qcmd->bcmd == 'P') + return TRUE; + + if (z[1] != '\0') + return FALSE; + + z = strtok ((char *) NULL, " \t\n"); + if (z == NULL) + return FALSE; + qcmd->zfrom = z; + + z = strtok ((char *) NULL, " \t\n"); + if (z == NULL) + return FALSE; + qcmd->zto = z; + + z = strtok ((char *) NULL, " \t\n"); + if (z == NULL) + return FALSE; + qcmd->zuser = z; + + z = strtok ((char *) NULL, " \t\n"); + if (z == NULL || *z != '-') + return FALSE; + qcmd->zoptions = z + 1; + + if (qcmd->bcmd == 'X') + return TRUE; + + if (qcmd->bcmd == 'R') + { + z = strtok ((char *) NULL, " \t\n"); + if (z != NULL) + { + if (strcmp (z, "dummy") != 0) + { + /* This may be the maximum number of bytes the remote + system wants to receive, if it using Taylor UUCP size + negotiation. */ + qcmd->cbytes = strtol (z, &zend, 0); + if (*zend != '\0') + qcmd->cbytes = -1; + } + else + { + /* This is from an SVR4 system, and may include the + position at which to start sending the file. The + next fields are the mode bits, the remote owner (?), + the remote temporary file name, and finally the + restart position. */ + if (strtok ((char *) NULL, " \t\n") != NULL + && strtok ((char *) NULL, " \t\n") != NULL + && strtok ((char *) NULL, " \t\n") != NULL) + { + z = strtok ((char *) NULL, " \t\n"); + if (z != NULL) + { + qcmd->ipos = strtol (z, &zend, 0); + if (*zend != '\0') + qcmd->ipos = 0; + } + } + } + } + + return TRUE; + } + + z = strtok ((char *) NULL, " \t\n"); + if (z == NULL) + return FALSE; + qcmd->ztemp = z; + + z = strtok ((char *) NULL, " \t\n"); + if (z == NULL) + return FALSE; + qcmd->imode = (int) strtol (z, &zend, 8); + if (*zend != '\0') + return FALSE; + + z = strtok ((char *) NULL, " \t\n"); + if (qcmd->bcmd == 'E' && z == NULL) + return FALSE; + qcmd->znotify = z; + + /* SVR4 UUCP will send the string "dummy" after the notify string + but before the size. I do not know when it sends anything other + than "dummy". Fortunately, it doesn't really hurt to not get the + file size. */ + if (z != NULL && strcmp (z, "dummy") == 0) + z = strtok ((char *) NULL, " \t\n"); + + if (z != NULL) + { + z = strtok ((char *) NULL, " \t\n"); + if (z != NULL) + { + qcmd->cbytes = strtol (z, &zend, 0); + if (*zend != '\0') + qcmd->cbytes = -1; + } + else if (qcmd->bcmd == 'E') + return FALSE; + + if (z != NULL) + { + z = strtok ((char *) NULL, ""); + if (z != NULL) + z[strcspn (z, "\n")] = '\0'; + if (qcmd->bcmd == 'E' && z == NULL) + return FALSE; + qcmd->zcmd = z; + } + } + + return TRUE; +} diff --git a/gnu/libexec/uucp/libuucp/spool.c b/gnu/libexec/uucp/libuucp/spool.c new file mode 100644 index 0000000000..52223fb585 --- /dev/null +++ b/gnu/libexec/uucp/libuucp/spool.c @@ -0,0 +1,30 @@ +/* spool.c + See whether a filename is legal for the spool directory. */ + +#include "uucp.h" + +#include + +#include "uudefs.h" + +/* See whether a file is a spool file. Spool file names are specially + crafted to hand around to other UUCP packages. They always begin + with 'C', 'D' or 'X', and the second character is always a period. + The remaining characters may be any printable characters, since + they may include a grade set by another system. */ + +boolean +fspool_file (zfile) + const char *zfile; +{ + const char *z; + + if (*zfile != 'C' && *zfile != 'D' && *zfile != 'X') + return FALSE; + if (zfile[1] != '.') + return FALSE; + for (z = zfile + 2; *z != '\0'; z++) + if (*z == '/' || ! isprint (BUCHAR (*z)) || isspace (BUCHAR (*z))) + return FALSE; + return TRUE; +} diff --git a/gnu/libexec/uucp/libuucp/status.c b/gnu/libexec/uucp/libuucp/status.c new file mode 100644 index 0000000000..bee5f83dd0 --- /dev/null +++ b/gnu/libexec/uucp/libuucp/status.c @@ -0,0 +1,20 @@ +/* status.c + Strings for status codes. */ + +#include "uucp.h" + +#include "uudefs.h" + +/* Status strings. These must match enum tstatus_type. */ + +const char *azStatus[] = +{ + "Conversation complete", + "Port unavailable", + "Dial failed", + "Login failed", + "Handshake failed", + "Call failed", + "Talking", + "Wrong time to call" +}; diff --git a/gnu/libexec/uucp/libuucp/strcas.c b/gnu/libexec/uucp/libuucp/strcas.c new file mode 100644 index 0000000000..4bb2251129 --- /dev/null +++ b/gnu/libexec/uucp/libuucp/strcas.c @@ -0,0 +1,33 @@ +/* strcas.c + Compare two strings case insensitively. */ + +#include "uucp.h" +#include + +int +strcasecmp (z1, z2) + const char *z1; + const char *z2; +{ + char b1, b2; + + while ((b1 = *z1++) != '\0') + { + b2 = *z2++; + if (b2 == '\0') + return 1; + if (b1 != b2) + { + if (isupper (BUCHAR (b1))) + b1 = tolower (BUCHAR (b1)); + if (isupper (BUCHAR (b2))) + b2 = tolower (BUCHAR (b2)); + if (b1 != b2) + return b1 - b2; + } + } + if (*z2 == '\0') + return 0; + else + return -1; +} diff --git a/gnu/libexec/uucp/libuucp/strchr.c b/gnu/libexec/uucp/libuucp/strchr.c new file mode 100644 index 0000000000..ca8d8e9b1b --- /dev/null +++ b/gnu/libexec/uucp/libuucp/strchr.c @@ -0,0 +1,16 @@ +/* strchr.c + Look for a character in a string. This works for a null byte. */ + +#include "uucp.h" + +char * +strchr (z, b) + const char *z; + int b; +{ + b = (char) b; + while (*z != b) + if (*z++ == '\0') + return NULL; + return (char *) z; +} diff --git a/gnu/libexec/uucp/libuucp/strdup.c b/gnu/libexec/uucp/libuucp/strdup.c new file mode 100644 index 0000000000..231e35b3f6 --- /dev/null +++ b/gnu/libexec/uucp/libuucp/strdup.c @@ -0,0 +1,18 @@ +/* strdup.c + Duplicate a string into memory. */ + +#include "uucp.h" + +char * +strdup (z) + const char *z; +{ + size_t csize; + char *zret; + + csize = strlen (z) + 1; + zret = malloc (csize); + if (zret != NULL) + memcpy (zret, z, csize); + return zret; +} diff --git a/gnu/libexec/uucp/libuucp/strncs.c b/gnu/libexec/uucp/libuucp/strncs.c new file mode 100644 index 0000000000..6959d625d6 --- /dev/null +++ b/gnu/libexec/uucp/libuucp/strncs.c @@ -0,0 +1,39 @@ +/* strncs.c + Compare two strings case insensitively up to a point. */ + +#include "uucp.h" +#include + +int +strncasecmp (z1, z2, c) + const char *z1; + const char *z2; + size_t c; +{ + char b1, b2; + + if (c == 0) + return 0; + while ((b1 = *z1++) != '\0') + { + b2 = *z2++; + if (b2 == '\0') + return 1; + if (b1 != b2) + { + if (isupper (BUCHAR (b1))) + b1 = tolower (BUCHAR (b1)); + if (isupper (BUCHAR (b2))) + b2 = tolower (BUCHAR (b2)); + if (b1 != b2) + return b1 - b2; + } + --c; + if (c == 0) + return 0; + } + if (*z2 == '\0') + return 0; + else + return -1; +} diff --git a/gnu/libexec/uucp/libuucp/strrch.c b/gnu/libexec/uucp/libuucp/strrch.c new file mode 100644 index 0000000000..a88e4b4358 --- /dev/null +++ b/gnu/libexec/uucp/libuucp/strrch.c @@ -0,0 +1,24 @@ +/* strrch.c + Look for the last occurrence of a character in a string. This is + supposed to work for a null byte, although we never actually call + it with one. */ + +#include "uucp.h" + +char * +strrchr (z, b) + const char *z; + int b; +{ + char *zret; + + b = (char) b; + zret = NULL; + do + { + if (*z == b) + zret = (char *) z; + } + while (*z++ != '\0'); + return zret; +} diff --git a/gnu/libexec/uucp/libuucp/strstr.c b/gnu/libexec/uucp/libuucp/strstr.c new file mode 100644 index 0000000000..111460b11d --- /dev/null +++ b/gnu/libexec/uucp/libuucp/strstr.c @@ -0,0 +1,55 @@ +/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. + +This file was modified slightly by Ian Lance Taylor, May 1992, for +Taylor UUCP. */ + +#include "uucp.h" + +/* Return the first ocurrence of NEEDLE in HAYSTACK. */ +char * +strstr (haystack, needle) + const char *const haystack; + const char *const needle; +{ + register const char *const needle_end = strchr(needle, '\0'); + register const char *const haystack_end = strchr(haystack, '\0'); + register const size_t needle_len = needle_end - needle; + register const size_t needle_last = needle_len - 1; + register const char *begin; + + if (needle_len == 0) + return (char *) haystack_end; + if ((size_t) (haystack_end - haystack) < needle_len) + return NULL; + + for (begin = &haystack[needle_last]; begin < haystack_end; ++begin) + { + register const char *n = &needle[needle_last]; + register const char *h = begin; + do + if (*h != *n) + goto loop; + while (--n >= needle && --h >= haystack); + + return (char *) h; + loop:; + } + + return NULL; +} diff --git a/gnu/libexec/uucp/libuucp/strtol.c b/gnu/libexec/uucp/libuucp/strtol.c new file mode 100644 index 0000000000..f663994bf9 --- /dev/null +++ b/gnu/libexec/uucp/libuucp/strtol.c @@ -0,0 +1,175 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. + +This file was modified slightly by Ian Lance Taylor, May 1992, for +Taylor UUCP. */ + +#include "uucp.h" + +#include +#include + +#if HAVE_LIMITS_H +#include +#else +#define ULONG_MAX 4294967295 +#define LONG_MIN (- LONG_MAX - 1) +#define LONG_MAX 2147483647 +#endif + +#ifndef UNSIGNED +#define UNSIGNED 0 +#endif + +/* Convert NPTR to an `unsigned long int' or `long int' in base BASE. + If BASE is 0 the base is determined by the presence of a leading + zero, indicating octal or a leading "0x" or "0X", indicating hexadecimal. + If BASE is < 2 or > 36, it is reset to 10. + If ENDPTR is not NULL, a pointer to the character after the last + one converted is stored in *ENDPTR. */ +#if UNSIGNED +unsigned long int +#define strtol strtoul +#else +long int +#endif +strtol (nptr, endptr, base) + const char *nptr; + char **endptr; + int base; +{ + int negative; + register unsigned long int cutoff; + register unsigned int cutlim; + register unsigned long int i; + register const char *s; + register unsigned int c; + const char *save; + int overflow; + + if (base < 0 || base == 1 || base > 36) + base = 10; + + s = nptr; + + /* Skip white space. */ + while (isspace(BUCHAR (*s))) + ++s; + if (*s == '\0') + goto noconv; + + /* Check for a sign. */ + if (*s == '-') + { + negative = 1; + ++s; + } + else if (*s == '+') + { + negative = 0; + ++s; + } + else + negative = 0; + + if (base == 16 + && s[0] == '0' + && (s[1] == 'x' || s[1] == 'X')) + s += 2; + + /* If BASE is zero, figure it out ourselves. */ + if (base == 0) + if (*s == '0') + { + if (s[1] == 'x' || s[1] == 'X') + { + s += 2; + base = 16; + } + else + base = 8; + } + else + base = 10; + + /* Save the pointer so we can check later if anything happened. */ + save = s; + + cutoff = ULONG_MAX / (unsigned long int) base; + cutlim = ULONG_MAX % (unsigned long int) base; + + overflow = 0; + i = 0; + for (c = BUCHAR (*s); c != '\0'; c = BUCHAR (*++s)) + { + if (isdigit(c)) + c -= '0'; + else if (islower(c)) + c = c - 'a' + 10; + else if (isupper(c)) + c = c - 'A' + 10; + else + break; + if (c >= base) + break; + /* Check for overflow. */ + if (i > cutoff || (i == cutoff && c > cutlim)) + overflow = 1; + else + { + i *= (unsigned long int) base; + i += c; + } + } + + /* Check if anything actually happened. */ + if (s == save) + goto noconv; + + /* Store in ENDPTR the address of one character + past the last character we converted. */ + if (endptr != NULL) + *endptr = (char *) s; + +#if !UNSIGNED + /* Check for a value that is within the range of + `unsigned long int', but outside the range of `long int'. */ + if (i > (negative ? + - (unsigned long int) LONG_MIN : (unsigned long int) LONG_MAX)) + overflow = 1; +#endif + + if (overflow) + { + errno = ERANGE; +#if UNSIGNED + return ULONG_MAX; +#else + return negative ? LONG_MIN : LONG_MAX; +#endif + } + + /* Return the result of the appropriate sign. */ + return (negative ? - i : i); + + noconv: + /* There was no number to convert. */ + if (endptr != NULL) + *endptr = (char *) nptr; + return 0L; +} diff --git a/gnu/libexec/uucp/libuucp/xfree.c b/gnu/libexec/uucp/libuucp/xfree.c new file mode 100644 index 0000000000..239b015db2 --- /dev/null +++ b/gnu/libexec/uucp/libuucp/xfree.c @@ -0,0 +1,15 @@ +/* xfree.c + Some versions of free (like the one in SCO Unix 3.2.2) don't handle + null pointers correctly, so we go through our own routine. */ + +#include "uucp.h" + +#include "uudefs.h" + +void +xfree (p) + pointer p; +{ + if (p != NULL) + free (p); +} diff --git a/gnu/libexec/uucp/libuucp/xmall.c b/gnu/libexec/uucp/libuucp/xmall.c new file mode 100644 index 0000000000..4aac23748c --- /dev/null +++ b/gnu/libexec/uucp/libuucp/xmall.c @@ -0,0 +1,18 @@ +/* xmalloc.c + Allocate a block of memory without fail. */ + +#include "uucp.h" + +#include "uudefs.h" + +pointer +xmalloc (c) + size_t c; +{ + pointer pret; + + pret = malloc (c); + if (pret == NULL && c != 0) + ulog (LOG_FATAL, "Out of memory"); + return pret; +} diff --git a/gnu/libexec/uucp/libuucp/xreall.c b/gnu/libexec/uucp/libuucp/xreall.c new file mode 100644 index 0000000000..36ae313a50 --- /dev/null +++ b/gnu/libexec/uucp/libuucp/xreall.c @@ -0,0 +1,23 @@ +/* xreall.c + Realloc a block of memory without fail. Supposedly some versions of + realloc can't handle a NULL first argument, so we check for that + here. */ + +#include "uucp.h" + +#include "uudefs.h" + +pointer +xrealloc (p, c) + pointer p; + size_t c; +{ + pointer pret; + + if (p == NULL) + return xmalloc (c); + pret = realloc (p, c); + if (pret == NULL && c != 0) + ulog (LOG_FATAL, "Out of memory"); + return pret; +} diff --git a/gnu/libexec/uucp/sample/Makefile b/gnu/libexec/uucp/sample/Makefile new file mode 100644 index 0000000000..0cd2f1ec29 --- /dev/null +++ b/gnu/libexec/uucp/sample/Makefile @@ -0,0 +1,15 @@ +# $Id: Makefile,v 1.1 1993/08/05 18:22:22 conklin Exp $ + +FILES= call config dial dialcode passwd port sys1 sys2 +NOOBJ= noobj + +BINOWN= $(owner) +BINGRP= $(group) + +all clean cleandir depend lint tags: + +install: + install -c -o ${BINOWN} -g ${BINGRP} -m 440 ${FILES} \ + ${DESTDIR}/etc/uucp + +.include diff --git a/gnu/libexec/uucp/sample/call b/gnu/libexec/uucp/sample/call new file mode 100644 index 0000000000..de4190ce61 --- /dev/null +++ b/gnu/libexec/uucp/sample/call @@ -0,0 +1,20 @@ +# This is an example of call, the call out password file for Taylor +# UUCP. To use it, you must compile the package with +# HAVE_TAYLOR_CONFIG set to 1 in policy.h (that is the default), copy +# this file to newconfigdir as set in Makefile.in (the default is +# /usr/local/conf/uucp), and edit it as appropriate for your system. + +# Everything after a '#' character is a comment. To uncomment any of +# the sample lines below, just delete the '#'. + +# This file is used when the ``call-login'' or ``call-password'' +# commands are used in the sys file with a "*" argument (e.g., +# ``call-login *''). The system name is looked up in this file, and +# the login name and password are used. + +# The point of this is that the sys file may then be publically +# readable, while still concealing the login names and passwords used +# to connect to the remote system. + +# The format is just system-name login-name password. +uunet Uairs foobar diff --git a/gnu/libexec/uucp/sample/config b/gnu/libexec/uucp/sample/config new file mode 100644 index 0000000000..e7d683bb03 --- /dev/null +++ b/gnu/libexec/uucp/sample/config @@ -0,0 +1,88 @@ +# This is an example of config, the main configuration file for Taylor +# UUCP. To use it, you must compile the package with +# HAVE_TAYLOR_CONFIG set to 1 in policy.h (that is the default), copy +# this file to newconfigdir as set in Makefile.in (the default is +# /usr/local/conf/uucp), and edit it as appropriate for your system. + +# You need not use this file at all; all the important commands have +# defaults which will be used if this file can not be found. + +# Everything after a '#' character is a comment. To uncomment any of +# the sample lines below, just delete the '#'. + +# You must choose a UUCP name. If your system is going to be +# communicating with other systems outside your organization, the name +# must be unique in the entire world. The usual method is to pick a +# name, and then search the UUCP maps (in the newsgroup +# comp.mail.maps) to see whether it has already been taken. See the +# README posting in comp.mail.maps for more information. If the name +# of your system as returned by "uuname -n" or "hostname" is the name +# you want to use, you do not need to set the name in this file. +# Otherwise uncomment and edit the following line. +# nodename uucp # The UUCP name of this system + +# The default spool directory is set in policy.h (the default is +# /usr/spool/uucp). All UUCP jobs and status information are kept in +# the spool directory. If you wish to change it, use the spool +# command. +# spool /usr/spool/uucp # The UUCP spool directory + +# The default public directory is set in policy.h (the default is +# /usr/spool/uucppublic). Remote systems may refer to a file in this +# directory using "~/FILE". By default, the public directory is the +# only directory which remote systems may transfer files in and out +# of. If you wish to change the public directory, use the pubdir +# command. +# pubdir /usr/spool/uucppublic # The UUCP public directory + +# The names of the UUCP log files are set in policy.h. The default +# names depend on the logging option you have chosen. If +# HAVE_TAYLOR_LOGGING is set in policy.h, the default log file name is +# /usr/spool/uucp/Log, the default statistics file name is +# /usr/spool/uucp/Stats, and the default debugging file name is +# /usr/spool/uucp/Debug. These file names may be set by the following +# commands. +# logfile /usr/spool/uucp/Log # The UUCP log file +# statfile /usr/spool/uucp/Stats # The UUCP statistics file +# debugfile /usr/spool/uucp/Debug # The UUCP debugging file + +# uuxqt is the program which executes UUCP requests from other +# systems. Normally one is started after each run of uucico, the +# communications daemon. You may control the maximum number of uuxqt +# programs run at the same time with the following command. The +# default is to have no maximum. +# max-uuxqts 1 # The maximum number of uuxqts + +# There are several files that uucico uses. By default it looks for +# them in newconfigdir, as set in Makefile.in. You may name one or +# more of each type of file using the following commands. +# sysfile FILES # Default "sys" +# portfile FILES # Default "port" +# dialfile FILES # Default "dial" +# dialcodefile FILES # Default "dialcode" +# callfile FILES # Default "call" +# passwdfile FILES # Default "passwd" + +# The ``timetable'' command may be used to declare timetables. These +# may then be referred to in time strings in the other files. +# timetable Day Wk0905-1655 + +# The ``unknown'' command is followed by any command which may appear +# in a sys file. These commands are taken together to describe what +# is permitted to a system which is not listed in any sys file. If +# the ``unknown'' command, then unknown systems are not permitted to +# connect. + +# Here is an example which permits unknown systems to download files +# from /usr/spool/anonymous, and to upload them to +# /usr/spool/anonymous/upload. +# +# No commands may be executed (the list of permitted commands is empty) +# unknown commands +# The public directory is /usr/spool/anonymous +# unknown pubdir /usr/spool/anonymous +# Only files in the public directory may be sent; users may not download +# files from the upload directory +# unknown remote-send ~ !~/upload +# May only upload files into /usr/spool/anonymous/upload +# unknown remote-receive ~/upload diff --git a/gnu/libexec/uucp/sample/dial b/gnu/libexec/uucp/sample/dial new file mode 100644 index 0000000000..f0d4bdd8aa --- /dev/null +++ b/gnu/libexec/uucp/sample/dial @@ -0,0 +1,35 @@ +# This is an example of dial, the dialer configuration file for Taylor +# UUCP. To use it, you must compile the package with +# HAVE_TAYLOR_CONFIG set to 1 in policy.h (that is the default), copy +# this file to newconfigdir as set in Makefile.in (the default is +# /usr/local/conf/uucp), and edit it as appropriate for your system. + +# Everything after a '#' character is a comment. To uncomment any of +# the sample lines below, just delete the '#'. + +# All dialers named in the port (or sys) file must be described in the +# dial file. It is also possible to describe a dialer directly in the +# port (or sys) file. + +# This is a typical Hayes modem definition. +dialer hayes + +# The chat script used to dial the phone. +# This means: +# 1) expect nothing (i.e., continue with step 2) +# 2) send "ATZ", then a carriage return, then sleep for 1 to 2 +# seconds. The \c means to not send a final carriage return. +# 3) wait until the modem echoes "OK" +# 4) send "ATDT", then the telephone number (after translating any +# dialcodes). +# 5) wait until the modem echoes "CONNECT" +chat "" ATZ\r\d\c OK ATDT\T CONNECT + +# If we get "BUSY" or "NO CARRIER" during the dial chat script we +# abort the dial immediately. +chat-fail BUSY +chat-fail NO\sCARRIER + +# When the call is over, we make sure we hangup the modem. +complete \d\d+++\d\dATH\r\c +abort \d\d+++\d\dATH\r\c diff --git a/gnu/libexec/uucp/sample/dialcode b/gnu/libexec/uucp/sample/dialcode new file mode 100644 index 0000000000..710a07bf73 --- /dev/null +++ b/gnu/libexec/uucp/sample/dialcode @@ -0,0 +1,19 @@ +# This is an example of dialcode, the dialcode configuration file for +# Taylor UUCP. To use it, you must compile the package with +# HAVE_TAYLOR_CONFIG set to 1 in policy.h (that is the default), copy +# this file to newconfigdir as set in Makefile.in (the default is +# /usr/local/conf/uucp), and edit it as appropriate for your system. + +# Everything after a '#' character is a comment. To uncomment any of +# the sample lines below, just delete the '#'. + +# The dialcode file is used if \T is used in the dialer chat script +# and the telephone number begins with alphabetic characters. The +# alphabetic characters are looked up and translated in dialcode. + +# Here are a couple of sample dialcodes. +MA 617 +CA 415 + +# For example, if the phone number (from the sys file) is MA7389449, +# then the string sent to the modem will be 6177389449. diff --git a/gnu/libexec/uucp/sample/passwd b/gnu/libexec/uucp/sample/passwd new file mode 100644 index 0000000000..2b04e13a34 --- /dev/null +++ b/gnu/libexec/uucp/sample/passwd @@ -0,0 +1,18 @@ +# This is an example of passwd, the call in password file for Taylor +# UUCP. To use it, you must compile the package with +# HAVE_TAYLOR_CONFIG set to 1 in policy.h (that is the default), copy +# this file to newconfigdir as set in Makefile.in (the default is +# /usr/local/conf/uucp), and edit it as appropriate for your system. + +# Everything after a '#' character is a comment. To uncomment any of +# the sample lines below, just delete the '#'. + +# This file is used when uucico is invoked with the -l or -e argument. +# uucico will then prompt for a login name and password. The login +# name is looked up in this file to check the password (the system +# password file, /etc/passwd, is not checked). This permits uucico to +# completely take over a port, allowing UUCP access to remote systems +# but not permitting remote users to actually log in to the system. + +# The format is just login-name password. +Uairs foobar diff --git a/gnu/libexec/uucp/sample/port b/gnu/libexec/uucp/sample/port new file mode 100644 index 0000000000..8e481869b5 --- /dev/null +++ b/gnu/libexec/uucp/sample/port @@ -0,0 +1,41 @@ +# This is an example of port, the port configuration file for Taylor +# UUCP. To use it, you must compile the package with +# HAVE_TAYLOR_CONFIG set to 1 in policy.h (that is the default), copy +# this file to newconfigdir as set in Makefile.in (the default is +# /usr/local/conf/uucp), and edit it as appropriate for your system. + +# Everything after a '#' character is a comment. To uncomment any of +# the sample lines below, just delete the '#'. + +# All ports named in the sys file must be described in a port file. +# It is also possible to describe the port directly in the sys file. + +# Commands that appears before the first ``port'' command are defaults +# for all ports that appear later in the file. In this case all ports +# will default to being modems (other possible types are direct, tcp +# and tli). +type modem + +# Now we describe two ports. + +# This is the name of the port. This name may be used in the sys file +# to select the port, or the sys file may just specify a baud rate in +# which case the first matching unlocked port will be used. +port port1 + +# This is the device name to open to dial out. +device /dev/ttyd0 + +# This is the dialer to use, as described in the dialer file. +dialer hayes + +# This is the baud rate to dial out at. +speed 2400 + +# Here is a second port. This is like the first, except that it uses +# a different device. It also permits a range of speeds, which is +# mainly useful if the system specifies a particular baud rate. +port port2 +device /dev/ttyd1 +dialer hayes +speed-range 2400 9600 diff --git a/gnu/libexec/uucp/sample/sys1 b/gnu/libexec/uucp/sample/sys1 new file mode 100644 index 0000000000..fa9e7709e1 --- /dev/null +++ b/gnu/libexec/uucp/sample/sys1 @@ -0,0 +1,44 @@ +# This is an example of a sys file, the file(s) which describe remote +# systems for Taylor UUCP. To use it, you must compile the package +# with HAVE_TAYLOR_CONFIG set to 1 in policy.h (that is the default), +# copy this file to newconfigdir as set in Makefile.in (the default is +# /usr/local/conf/uucp), and edit it as appropriate for your system. + +# If you do not use the ``unknown'' command in the config file, then +# each system that you communicate with must be listed in a sys file. + +# Everything after a '#' character is a comment. To uncomment any of +# the sample lines below, just delete the '#'. + +# This is a sample sys file that might be used in a leaf system. A +# leaf system is one that only contacts one other system. sys2 +# provides another example. + +# The name of the remote system that we call. +system uunet + +# The login name and password are kept in the callout password file +# (by default this is the file "call" in newconfigdir). +call-login * +call-password * + +# We can send anything at any time. +time any + +# During the day we only accept grade 'Z' or above; at other times +# (not mentioned here) we accept all grades. uunet queues up news +# at grade 'd', which is lower than 'Z'. +call-timegrade Z Wk0755-2305,Su1655-2305 + +# The phone number to call. +phone 7389449 + +# uunet tends to be slow, so we increase the timeout +chat-timeout 120 + +# The port we use to dial out. +port serial + +# Increase the timeout and the number of retries. +protocol-parameter g timeout 20 +protocol-parameter g retries 10 diff --git a/gnu/libexec/uucp/sample/sys2 b/gnu/libexec/uucp/sample/sys2 new file mode 100644 index 0000000000..856529a500 --- /dev/null +++ b/gnu/libexec/uucp/sample/sys2 @@ -0,0 +1,51 @@ +# This is an example of a sys file, the file(s) which describe remote +# systems for Taylor UUCP. To use it, you must compile the package +# with HAVE_TAYLOR_CONFIG set to 1 in policy.h (that is the default), +# copy this file to newconfigdir as set in Makefile.in (the default is +# /usr/local/conf/uucp), and edit it as appropriate for your system. + +# If you do not use the ``unknown'' command in the config file, then +# each system that you communicate with must be listed in a sys file. + +# Everything after a '#' character is a comment. To uncomment any of +# the sample lines below, just delete the '#'. + +# This is a sample sys file that might be used by a system that +# contacts a couple of other systems, both of which are treated the +# same. sys1 provides another example. + +# Commands that appear before the first ``system'' commands are +# defaults for all systems listed in the file. + +# Get the login name and password to use from the call-out file. By +# default this is the file "call" in newconfigdir. +call-login * +call-password * + +# The systems must use a particular login +called-login Ulocal + +# Permit local users to send any world readable file +local-send / + +# Permit local uses to request into any world writable directory +local-receive / + +# Call at any time +time any + +# Use port1, then port2 +port port1 + +alternate + +port port2 + +# Now define the systems themselves. Because of all the defaults we +# used, there is very little to specify for the systems themselves. + +system comton +phone 5551212 + +system bugs +phone 5552424 diff --git a/gnu/libexec/uucp/tstuu.c b/gnu/libexec/uucp/tstuu.c new file mode 100644 index 0000000000..d30549907d --- /dev/null +++ b/gnu/libexec/uucp/tstuu.c @@ -0,0 +1,1588 @@ +/* tstuu.c + Test the uucp package on a UNIX system. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char tstuu_rcsid[] = "$Id: tstuu.c,v 1.2 1993/08/04 19:29:58 jtc Exp $"; +#endif + +#include "sysdep.h" +#include "system.h" +#include "getopt.h" + +#include +#include +#include + +#if HAVE_SYS_TIMES_H +#include +#endif + +#if HAVE_SYS_IOCTL_H +#include +#endif + +#if HAVE_SELECT +#include +#if HAVE_SYS_SELECT_H +#include +#endif +#endif + +#if HAVE_POLL +#if HAVE_STROPTS_H +#include +#endif +#if HAVE_POLL_H +#include +#endif +#endif + +#if HAVE_FCNTL_H +#include +#else +#if HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#if HAVE_TIME_H && (HAVE_SYS_TIME_AND_TIME_H || ! HAVE_SELECT) +#include +#endif + +#if HAVE_SYS_WAIT_H +#include +#endif + +#if HAVE_UNION_WAIT +typedef union wait wait_status; +#else +typedef int wait_status; +#endif + +#if HAVE_STREAMS_PTYS +#include +extern char *ptsname (); +#endif + +/* Get definitions for both O_NONBLOCK and O_NDELAY. */ + +#ifndef O_NDELAY +#ifdef FNDELAY +#define O_NDELAY FNDELAY +#else /* ! defined (FNDELAY) */ +#define O_NDELAY 0 +#endif /* ! defined (FNDELAY) */ +#endif /* ! defined (O_NDELAY) */ + +#ifndef O_NONBLOCK +#ifdef FNBLOCK +#define O_NONBLOCK FNBLOCK +#else /* ! defined (FNBLOCK) */ +#define O_NONBLOCK 0 +#endif /* ! defined (FNBLOCK) */ +#endif /* ! defined (O_NONBLOCK) */ + +#if O_NDELAY == 0 && O_NONBLOCK == 0 + #error No way to do nonblocking I/O +#endif + +/* Get definitions for EAGAIN, EWOULDBLOCK and ENODATA. */ +#ifndef EAGAIN +#ifndef EWOULDBLOCK +#define EAGAIN (-1) +#define EWOULDBLOCK (-1) +#else /* defined (EWOULDBLOCK) */ +#define EAGAIN EWOULDBLOCK +#endif /* defined (EWOULDBLOCK) */ +#else /* defined (EAGAIN) */ +#ifndef EWOULDBLOCK +#define EWOULDBLOCK EAGAIN +#endif /* ! defined (EWOULDBLOCK) */ +#endif /* defined (EAGAIN) */ + +#ifndef ENODATA +#define ENODATA EAGAIN +#endif + +/* Make sure we have a CLK_TCK definition, even if it makes no sense. + This is in case TIMES_TICK is defined as CLK_TCK. */ +#ifndef CLK_TCK +#define CLK_TCK (60) +#endif + +/* Don't try too hard to get a TIMES_TICK value; it doesn't matter + that much. */ +#if TIMES_TICK == 0 +#undef TIMES_TICK +#define TIMES_TICK CLK_TCK +#endif + +#if TIMES_DECLARATION_OK +extern long times (); +#endif + +#ifndef SIGCHLD +#define SIGCHLD SIGCLD +#endif + +#if 1 +#define ZUUCICO_CMD "login uucp" +#define UUCICO_EXECL "/bin/login", "login", "uucp" +#else +#define ZUUCICO_CMD "su - nuucp" +#define UUCICO_EXECL "/bin/su", "su", "-", "nuucp" +#endif + +#if ! HAVE_SELECT && ! HAVE_POLL + #error You need select or poll +#endif + +#if ! HAVE_REMOVE +#undef remove +#define remove unlink +#endif + +/* Buffer chain to hold data read from a uucico. */ + +#define BUFCHARS (512) + +struct sbuf +{ + struct sbuf *qnext; + int cstart; + int cend; + char ab[BUFCHARS]; +}; + +/* Local functions. */ + +static void umake_file P((const char *zfile, int cextra)); +static void uprepare_test P((boolean fmake, int itest, + boolean fcall_uucico, + const char *zsys)); +static void ucheck_file P((const char *zfile, const char *zerr, + int cextra)); +static void ucheck_test P((int itest, boolean fcall_uucico)); +static RETSIGTYPE uchild P((int isig)); +static int cpshow P((char *z, int bchar)); +static void uchoose P((int *po1, int *po2)); +static long cread P((int o, struct sbuf **)); +static boolean fsend P((int o, int oslave, struct sbuf **)); +static boolean fwritable P((int o)); +static void xsystem P((const char *zcmd)); +static FILE *xfopen P((const char *zname, const char *zmode)); + +static char *zDebug; +static int iTest; +static boolean fCall_uucico; +static int iPercent; +static pid_t iPid1, iPid2; +static int cFrom1, cFrom2; +static char abLogout1[sizeof "tstout /dev/ptyp0"]; +static char abLogout2[sizeof "tstout /dev/ptyp0"]; +static char *zProtocols; + +int +main (argc, argv) + int argc; + char **argv; +{ + int iopt; + const char *zcmd1, *zcmd2; + const char *zsys; + boolean fmake = TRUE; + int omaster1, oslave1, omaster2, oslave2; + char abpty1[sizeof "/dev/ptyp0"]; + char abpty2[sizeof "/dev/ptyp0"]; + struct sbuf *qbuf1, *qbuf2; + + zcmd1 = NULL; + zcmd2 = NULL; + zsys = "test2"; + + while ((iopt = getopt (argc, argv, "c:np:s:t:ux:1:2:")) != EOF) + { + switch (iopt) + { + case 'c': + zProtocols = optarg; + break; + case 'n': + fmake = FALSE; + break; + case 'p': + iPercent = (int) strtol (optarg, (char **) NULL, 10); + srand ((unsigned int) ixsysdep_time ((long *) NULL)); + break; + case 's': + zsys = optarg; + break; + case 't': + iTest = (int) strtol (optarg, (char **) NULL, 10); + break; + case 'u': + fCall_uucico = TRUE; + break; + case 'x': + zDebug = optarg; + break; + case '1': + zcmd1 = optarg; + break; + case '2': + zcmd2 = optarg; + break; + default: + fprintf (stderr, + "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n", + VERSION); + fprintf (stderr, + "Usage: tstuu [-xn] [-t #] [-u] [-1 cmd] [-2 cmd]\n"); + exit (EXIT_FAILURE); + } + } + + if (fCall_uucico && zcmd2 == NULL) + zcmd2 = ZUUCICO_CMD; + + uprepare_test (fmake, iTest, fCall_uucico, zsys); + + (void) remove ("/usr/tmp/tstuu/spool1/core"); + (void) remove ("/usr/tmp/tstuu/spool2/core"); + + omaster1 = -1; + oslave1 = -1; + omaster2 = -1; + oslave2 = -1; + +#if ! HAVE_STREAMS_PTYS + + { + char *zptyname; + const char *zpty; + + zptyname = abpty1; + + for (zpty = "pqrs"; *zpty != '\0'; ++zpty) + { + int ipty; + + for (ipty = 0; ipty < 16; ipty++) + { + int om, os; + FILE *e; + + sprintf (zptyname, "/dev/pty%c%c", *zpty, + "0123456789abcdef"[ipty]); + om = open (zptyname, O_RDWR); + if (om < 0) + continue; + zptyname[5] = 't'; + os = open (zptyname, O_RDWR); + if (os < 0) + { + (void) close (om); + continue; + } + + if (omaster1 == -1) + { + omaster1 = om; + oslave1 = os; + + e = fopen ("/usr/tmp/tstuu/pty1", "w"); + if (e == NULL) + { + perror ("fopen"); + exit (EXIT_FAILURE); + } + fprintf (e, "%s", zptyname + 5); + if (fclose (e) != 0) + { + perror ("fclose"); + exit (EXIT_FAILURE); + } + + zptyname = abpty2; + } + else + { + omaster2 = om; + oslave2 = os; + + e = fopen ("/usr/tmp/tstuu/pty2", "w"); + if (e == NULL) + { + perror ("fopen"); + exit (EXIT_FAILURE); + } + fprintf (e, "%s", zptyname + 5); + if (fclose (e) != 0) + { + perror ("fclose"); + exit (EXIT_FAILURE); + } + break; + } + } + + if (omaster1 != -1 && omaster2 != -1) + break; + } + } + +#else /* HAVE_STREAMS_PTYS */ + + { + int ipty; + + for (ipty = 0; ipty < 2; ipty++) + { + int om, os; + FILE *e; + char *znam; + struct termio stio; + + om = open ((char *) "/dev/ptmx", O_RDWR); + if (om < 0) + break; + znam = ptsname (om); + if (znam == NULL) + break; + if (unlockpt (om) != 0 + || grantpt (om) != 0) + break; + + os = open (znam, O_RDWR); + if (os < 0) + { + (void) close (om); + om = -1; + break; + } + + if (ioctl (os, I_PUSH, "ptem") < 0 + || ioctl(os, I_PUSH, "ldterm") < 0) + { + perror ("ioctl"); + exit (EXIT_FAILURE); + } + + /* Can this really be right? */ + memset (&stio, 0, sizeof (stio)); + stio.c_cflag = B9600 | CS8 | CREAD | HUPCL; + + if (ioctl(os, TCSETA, &stio) < 0) + { + perror ("TCSETA"); + exit (EXIT_FAILURE); + } + + if (omaster1 == -1) + { + strcpy (abpty1, znam); + omaster1 = om; + oslave1 = os; + e = fopen ("/usr/tmp/tstuu/pty1", "w"); + if (e == NULL) + { + perror ("fopen"); + exit (EXIT_FAILURE); + } + fprintf (e, "%s", znam + 5); + if (fclose (e) != 0) + { + perror ("fclose"); + exit (EXIT_FAILURE); + } + } + else + { + strcpy (abpty2, znam); + omaster2 = om; + oslave2 = os; + e = fopen ("/usr/tmp/tstuu/pty2", "w"); + if (e == NULL) + { + perror ("fopen"); + exit (EXIT_FAILURE); + } + fprintf (e, "%s", znam + 5); + if (fclose (e) != 0) + { + perror ("fclose"); + exit (EXIT_FAILURE); + } + } + } + } + +#endif /* HAVE_STREAMS_PTYS */ + + if (omaster2 == -1) + { + fprintf (stderr, "No pseudo-terminals available\n"); + exit (EXIT_FAILURE); + } + + /* Make sure we can or these into an int for the select call. Most + systems could use 31 instead of 15, but it should never be a + problem. */ + if (omaster1 > 15 || omaster2 > 15) + { + fprintf (stderr, "File descriptors are too large\n"); + exit (EXIT_FAILURE); + } + + /* Prepare to log out the command if it is a login command. On + Ultrix 4.0 uucico can only be run from login for some reason. */ + + if (zcmd1 == NULL + || strncmp (zcmd1, "login", sizeof "login" - 1) != 0) + abLogout1[0] = '\0'; + else + sprintf (abLogout1, "tstout %s", abpty1); + + if (zcmd2 == NULL + || strncmp (zcmd2, "login", sizeof "login" - 1) != 0) + abLogout2[0] = '\0'; + else + sprintf (abLogout2, "tstout %s", abpty2); + + iPid1 = fork (); + if (iPid1 < 0) + { + perror ("fork"); + exit (EXIT_FAILURE); + } + else if (iPid1 == 0) + { + if (close (0) < 0 + || close (1) < 0 + || close (omaster1) < 0 + || close (omaster2) < 0 + || close (oslave2) < 0) + perror ("close"); + + if (dup2 (oslave1, 0) < 0 + || dup2 (oslave1, 1) < 0) + perror ("dup2"); + + if (close (oslave1) < 0) + perror ("close"); + + if (zDebug != NULL) + fprintf (stderr, "About to exec first process\n"); + + if (zcmd1 != NULL) + exit (system ((char *) zcmd1)); + else + { + (void) execl ("uucico", "uucico", "-I", "/usr/tmp/tstuu/Config1", + "-q", "-S", zsys, "-pstdin", (const char *) NULL); + perror ("execl failed"); + exit (EXIT_FAILURE); + } + } + + iPid2 = fork (); + if (iPid2 < 0) + { + perror ("fork"); + kill (iPid1, SIGTERM); + exit (EXIT_FAILURE); + } + else if (iPid2 == 0) + { + if (close (0) < 0 + || close (1) < 0 + || close (omaster1) < 0 + || close (oslave1) < 0 + || close (omaster2) < 0) + perror ("close"); + + if (dup2 (oslave2, 0) < 0 + || dup2 (oslave2, 1) < 0) + perror ("dup2"); + + if (close (oslave2) < 0) + perror ("close"); + + if (zDebug != NULL) + fprintf (stderr, "About to exec second process\n"); + + if (fCall_uucico) + { + (void) execl (UUCICO_EXECL, (const char *) NULL); + perror ("execl failed"); + exit (EXIT_FAILURE); + } + else if (zcmd2 != NULL) + exit (system ((char *) zcmd2)); + else + { + (void) execl ("uucico", "uucico", "-I", "/usr/tmp/tstuu/Config2", + "-lq", (const char *)NULL); + perror ("execl failed"); + exit (EXIT_FAILURE); + } + } + + signal (SIGCHLD, uchild); + + if (fcntl (omaster1, F_SETFL, O_NDELAY | O_NONBLOCK) < 0 + && errno == EINVAL) + (void) fcntl (omaster1, F_SETFL, O_NONBLOCK); + if (fcntl (omaster2, F_SETFL, O_NDELAY | O_NONBLOCK) < 0 + && errno == EINVAL) + (void) fcntl (omaster2, F_SETFL, O_NONBLOCK); + + qbuf1 = NULL; + qbuf2 = NULL; + + while (TRUE) + { + int o1, o2; + boolean fcont; + + o1 = omaster1; + o2 = omaster2; + uchoose (&o1, &o2); + + if (o1 == -1 && o2 == -1) + { + if (zDebug != NULL) + fprintf (stderr, "Five second pause\n"); + continue; + } + + if (o1 != -1) + cFrom1 += cread (omaster1, &qbuf1); + + if (o2 != -1) + cFrom2 += cread (omaster2, &qbuf2); + + do + { + fcont = FALSE; + + if (qbuf1 != NULL + && fwritable (omaster2) + && fsend (omaster2, oslave2, &qbuf1)) + fcont = TRUE; + + if (qbuf2 != NULL + && fwritable (omaster1) + && fsend (omaster1, oslave1, &qbuf2)) + fcont = TRUE; + + if (! fcont + && (qbuf1 != NULL || qbuf2 != NULL)) + { + long cgot1, cgot2; + + cgot1 = cread (omaster1, &qbuf1); + cFrom1 += cgot1; + cgot2 = cread (omaster2, &qbuf2); + cFrom2 += cgot2; + fcont = TRUE; + } + } + while (fcont); + } + + /*NOTREACHED*/ +} + +/* When a child dies, kill them both. */ + +static RETSIGTYPE +uchild (isig) + int isig; +{ + struct tms sbase, s1, s2; + + signal (SIGCHLD, SIG_DFL); + + /* Give the processes a chance to die on their own. */ + sleep (2); + + (void) kill (iPid1, SIGTERM); + (void) kill (iPid2, SIGTERM); + + (void) times (&sbase); + +#if HAVE_WAITPID + (void) waitpid (iPid1, (pointer) NULL, 0); +#else /* ! HAVE_WAITPID */ +#if HAVE_WAIT4 + (void) wait4 (iPid1, (pointer) NULL, 0, (struct rusage *) NULL); +#else /* ! HAVE_WAIT4 */ + (void) wait ((wait_status *) NULL); +#endif /* ! HAVE_WAIT4 */ +#endif /* ! HAVE_WAITPID */ + + (void) times (&s1); + +#if HAVE_WAITPID + (void) waitpid (iPid2, (pointer) NULL, 0); +#else /* ! HAVE_WAITPID */ +#if HAVE_WAIT4 + (void) wait4 (iPid2, (wait_status *) NULL, 0, (struct rusage *) NULL); +#else /* ! HAVE_WAIT4 */ + (void) wait ((wait_status *) NULL); +#endif /* ! HAVE_WAIT4 */ +#endif /* ! HAVE_WAITPID */ + + (void) times (&s2); + + fprintf (stderr, + " First child: user: %g; system: %g\n", + (double) (s1.tms_cutime - sbase.tms_cutime) / (double) TIMES_TICK, + (double) (s1.tms_cstime - sbase.tms_cstime) / (double) TIMES_TICK); + fprintf (stderr, + "Second child: user: %g; system: %g\n", + (double) (s2.tms_cutime - s1.tms_cutime) / (double) TIMES_TICK, + (double) (s2.tms_cstime - s1.tms_cstime) / (double) TIMES_TICK); + + ucheck_test (iTest, fCall_uucico); + + if (abLogout1[0] != '\0') + { + if (zDebug != NULL) + fprintf (stderr, "Executing %s\n", abLogout1); + (void) system (abLogout1); + } + if (abLogout2[0] != '\0') + { + if (zDebug != NULL) + fprintf (stderr, "Executing %s\n", abLogout2); + (void) system (abLogout2); + } + + fprintf (stderr, "Wrote %d bytes from 1 to 2\n", cFrom1); + fprintf (stderr, "Wrote %d bytes from 2 to 1\n", cFrom2); + + if (access ("/usr/tmp/tstuu/spool1/core", R_OK) == 0) + fprintf (stderr, "core file 1 exists\n"); + if (access ("/usr/tmp/tstuu/spool2/core", R_OK) == 0) + fprintf (stderr, "core file 2 exists\n"); + + exit (EXIT_SUCCESS); +} + +/* Open a file without error. */ + +static FILE * +xfopen (zname, zmode) + const char *zname; + const char *zmode; +{ + FILE *eret; + + eret = fopen (zname, zmode); + if (eret == NULL) + { + perror (zname); + exit (EXIT_FAILURE); + } + return eret; +} + +/* Close a file without error. */ + +static void xfclose P((FILE *e)); + +static void +xfclose (e) + FILE *e; +{ + if (fclose (e) != 0) + { + perror ("fclose"); + exit (EXIT_FAILURE); + } +} + +/* Create a test file. */ + +static void +umake_file (z, c) + const char *z; + int c; +{ + int i; + FILE *e; + + e = xfopen (z, "w"); + + for (i = 0; i < 256; i++) + { + int i2; + + for (i2 = 0; i2 < 256; i2++) + putc (i, e); + } + + for (i = 0; i < c; i++) + putc (i, e); + + xfclose (e); +} + +/* Check a test file. */ + +static void +ucheck_file (z, zerr, c) + const char *z; + const char *zerr; + int c; +{ + int i; + FILE *e; + + e = xfopen (z, "r"); + + for (i = 0; i < 256; i++) + { + int i2; + + for (i2 = 0; i2 < 256; i2++) + { + int bread; + + bread = getc (e); + if (bread == EOF) + { + fprintf (stderr, + "%s: Unexpected EOF at position %d,%d\n", + zerr, i, i2); + xfclose (e); + return; + } + if (bread != i) + fprintf (stderr, + "%s: At position %d,%d got %d expected %d\n", + zerr, i, i2, bread, i); + } + } + + for (i = 0; i < c; i++) + { + int bread; + + bread = getc (e); + if (bread == EOF) + { + fprintf (stderr, "%s: Unexpected EOF at extra %d\n", zerr, i); + xfclose (e); + return; + } + if (bread != i) + fprintf (stderr, "%s: At extra %d got %d expected %d\n", + zerr, i, bread, i); + } + + if (getc (e) != EOF) + fprintf (stderr, "%s: File is too long", zerr); + + xfclose (e); +} + +/* Prepare all the configuration files for testing. */ + +static void +uprepare_test (fmake, itest, fcall_uucico, zsys) + boolean fmake; + int itest; + boolean fcall_uucico; + const char *zsys; +{ + FILE *e; + const char *zuucp1, *zuucp2; + const char *zuux1, *zuux2; + char ab[1000]; + const char *zfrom; + const char *zto; + +/* We must make /usr/tmp/tstuu world writeable or we won't be able to + receive files into it. */ + (void) umask (0); + +#ifndef S_IWOTH +#define S_IWOTH 02 +#endif + + if (mkdir ((char *) "/usr/tmp/tstuu", + IPUBLIC_DIRECTORY_MODE | S_IWOTH) != 0 + && errno != EEXIST) + { + perror ("mkdir"); + exit (EXIT_FAILURE); + } + + if (mkdir ((char *) "/usr/tmp/tstuu/spool1", IPUBLIC_DIRECTORY_MODE) != 0 + && errno != EEXIST) + { + perror ("mkdir"); + exit (EXIT_FAILURE); + } + + if (mkdir ((char *) "/usr/tmp/tstuu/spool2", IPUBLIC_DIRECTORY_MODE) != 0 + && errno != EEXIST) + { + perror ("mkdir"); + exit (EXIT_FAILURE); + } + + if (fmake) + { + e = xfopen ("/usr/tmp/tstuu/Config1", "w"); + + fprintf (e, "# First test configuration file\n"); + fprintf (e, "nodename test1\n"); + fprintf (e, "spool /usr/tmp/tstuu/spool1\n"); + fprintf (e, "lockdir /usr/tmp/tstuu/spool1\n"); + fprintf (e, "sysfile /usr/tmp/tstuu/System1\n"); + fprintf (e, "sysfile /usr/tmp/tstuu/System1.2\n"); + fprintf (e, "portfile /usr/tmp/tstuu/Port1\n"); + (void) remove ("/usr/tmp/tstuu/Log1"); +#if ! HAVE_HDB_LOGGING + fprintf (e, "logfile /usr/tmp/tstuu/Log1\n"); +#else + fprintf (e, "%s\n", "logfile /usr/tmp/tstuu/Log1/%s/%s"); +#endif + fprintf (e, "statfile /usr/tmp/tstuu/Stats1\n"); + fprintf (e, "debugfile /usr/tmp/tstuu/Debug1\n"); + fprintf (e, "callfile /usr/tmp/tstuu/Call1\n"); + fprintf (e, "pubdir /usr/tmp/tstuu\n"); +#if HAVE_V2_CONFIG + fprintf (e, "v2-files no\n"); +#endif +#if HAVE_HDB_CONFIG + fprintf (e, "hdb-files no\n"); +#endif + if (zDebug != NULL) + fprintf (e, "debug %s\n", zDebug); + + xfclose (e); + + e = xfopen ("/usr/tmp/tstuu/System1", "w"); + + fprintf (e, "# This file is ignored, to test multiple system files\n"); + fprintf (e, "time never\n"); + + xfclose (e); + + e = xfopen ("/usr/tmp/tstuu/System1.2", "w"); + + fprintf (e, "# First test system file\n"); + fprintf (e, "time any\n"); + fprintf (e, "port stdin\n"); + fprintf (e, "# That was the defaults\n"); + fprintf (e, "system %s\n", zsys); + if (! fcall_uucico) + { + FILE *eprog; + + eprog = xfopen ("/usr/tmp/tstuu/Chat1", "w"); + + /* Wait for the other side to open the port and flush input. */ + fprintf (eprog, "sleep 2\n"); + fprintf (eprog, + "echo password $1 speed $2 1>&2\n"); + fprintf (eprog, "echo test1\n"); + fprintf (eprog, "exit 0\n"); + + xfclose (eprog); + + if (chmod ("/usr/tmp/tstuu/Chat1", + S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) + { + perror ("chmod (/usr/tmp/tstuu/Chat1)"); + exit (EXIT_FAILURE); + } + + fprintf (e, "chat-program /usr/tmp/tstuu/Chat1 \\P \\S\n"); + + fprintf (e, "chat word: \\P\n"); + fprintf (e, "chat-fail login;\n"); + fprintf (e, "call-login *\n"); + fprintf (e, "call-password *\n"); + } + else + fprintf (e, "chat \"\"\n"); + fprintf (e, "call-transfer yes\n"); + fprintf (e, "commands cat\n"); + if (! fcall_uucico && iPercent == 0) + { + fprintf (e, "protocol-parameter g window 7\n"); + fprintf (e, "protocol-parameter g packet-size 4096\n"); + fprintf (e, "protocol-parameter j avoid \\377\n"); + } + if (zProtocols != NULL) + fprintf (e, "protocol %s\n", zProtocols); + + xfclose (e); + + e = xfopen ("/usr/tmp/tstuu/Port1", "w"); + + fprintf (e, "port stdin\n"); + fprintf (e, "type stdin\n"); + fprintf (e, "pty true\n"); + + xfclose (e); + + e = xfopen ("/usr/tmp/tstuu/Call1", "w"); + + fprintf (e, "Call out password file\n"); + fprintf (e, "%s test1 pass1\n", zsys); + + xfclose (e); + + if (! fcall_uucico) + { + FILE *eprog; + + e = xfopen ("/usr/tmp/tstuu/Config2", "w"); + + fprintf (e, "# Second test configuration file\n"); + fprintf (e, "nodename test2\n"); + fprintf (e, "spool /usr/tmp/tstuu/spool2\n"); + fprintf (e, "lockdir /usr/tmp/tstuu/spool2\n"); + fprintf (e, "sysfile /usr/tmp/tstuu/System2\n"); + (void) remove ("/usr/tmp/tstuu/Log2"); +#if ! HAVE_HDB_LOGGING + fprintf (e, "logfile /usr/tmp/tstuu/Log2\n"); +#else + fprintf (e, "%s\n", "logfile /usr/tmp/tstuu/Log2/%s/%s"); +#endif + fprintf (e, "statfile /usr/tmp/tstuu/Stats2\n"); + fprintf (e, "debugfile /usr/tmp/tstuu/Debug2\n"); + fprintf (e, "passwdfile /usr/tmp/tstuu/Pass2\n"); + fprintf (e, "pubdir /usr/tmp/tstuu\n"); +#if HAVE_V2_CONFIG + fprintf (e, "v2-files no\n"); +#endif +#if HAVE_HDB_CONFIG + fprintf (e, "hdb-files no\n"); +#endif + if (zDebug != NULL) + fprintf (e, "debug %s\n", zDebug); + + xfclose (e); + + e = xfopen ("/usr/tmp/tstuu/System2", "w"); + + fprintf (e, "# Second test system file\n"); + fprintf (e, "system test1\n"); + fprintf (e, "called-login test1\n"); + fprintf (e, "request true\n"); + fprintf (e, "commands cat\n"); + if (zProtocols != NULL) + fprintf (e, "protocol %s\n", zProtocols); + + eprog = xfopen ("/usr/tmp/tstuu/Chat2", "w"); + + fprintf (eprog, + "echo port $1 1>&2\n"); + fprintf (eprog, "exit 0\n"); + + xfclose (eprog); + + if (chmod ("/usr/tmp/tstuu/Chat2", + S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) + { + perror ("chmod (/usr/tmp/tstuu/Chat2"); + exit (EXIT_FAILURE); + } + + fprintf (e, "called-chat-program /bin/sh /usr/tmp/tstuu/Chat2 \\Y\n"); + fprintf (e, "time any\n"); + + xfclose (e); + + e = xfopen ("/usr/tmp/tstuu/Pass2", "w"); + + fprintf (e, "# Call in password file\n"); + fprintf (e, "test1 pass1\n"); + + xfclose (e); + } + } + + zuucp1 = "./uucp -I /usr/tmp/tstuu/Config1 -r"; + zuux1 = "./uux -I /usr/tmp/tstuu/Config1 -r"; + + if (fcall_uucico) + { + zuucp2 = "/usr/bin/uucp -r"; + zuux2 = "/usr/bin/uux -r"; + } + else + { + zuucp2 = "./uucp -I /usr/tmp/tstuu/Config2 -r"; + zuux2 = "./uux -I /usr/tmp/tstuu/Config2 -r"; + } + + /* Test transferring a file from the first system to the second. */ + if (itest == 0 || itest == 1) + { + zfrom = "/usr/tmp/tstuu/from1"; + if (fcall_uucico) + zto = "/usr/spool/uucppublic/to1"; + else + zto = "/usr/tmp/tstuu/to1"; + + (void) remove (zto); + umake_file (zfrom, 0); + + sprintf (ab, "%s %s %s!%s", zuucp1, zfrom, zsys, zto); + xsystem (ab); + } + + /* Test having the first system request a file from the second. */ + if (itest == 0 || itest == 2) + { + if (fcall_uucico) + zfrom = "/usr/spool/uucppublic/from2"; + else + zfrom = "/usr/tmp/tstuu/from2"; + zto = "/usr/tmp/tstuu/to2"; + + (void) remove (zto); + umake_file (zfrom, 3); + + sprintf (ab, "%s %s!%s %s", zuucp1, zsys, zfrom, zto); + xsystem (ab); + } + + /* Test having the second system send a file to the first. */ + if (itest == 0 || itest == 3) + { + if (fcall_uucico) + zfrom = "/usr/spool/uucppublic/from3"; + else + zfrom = "/usr/tmp/tstuu/from3"; + zto = "/usr/tmp/tstuu/to3"; + + (void) remove (zto); + umake_file (zfrom, 5); + + sprintf (ab, "%s -c \\~/from3 test1!~/to3", zuucp2); + xsystem (ab); + } + + /* Test having the second system request a file from the first. */ + if (itest == 0 || itest == 4) + { + zfrom = "/usr/tmp/tstuu/from4"; + if (fcall_uucico) + zto = "/usr/spool/uucppublic/to4"; + else + zto = "/usr/tmp/tstuu/to4"; + + (void) remove (zto); + umake_file (zfrom, 7); + + sprintf (ab, "%s test1!%s %s", zuucp2, zfrom, zto); + xsystem (ab); + } + + /* Test having the second system make an execution request. */ + if (itest == 0 || itest == 5) + { + zfrom = "/usr/tmp/tstuu/from5"; + if (fcall_uucico) + zto = "/usr/spool/uucppublic/to5"; + else + zto = "/usr/tmp/tstuu/to5"; + + (void) remove (zto); + umake_file (zfrom, 11); + + sprintf (ab, "%s test1!cat '<%s' '>%s'", zuux2, zfrom, zto); + xsystem (ab); + } + + /* Test having the first system request a wildcard. */ + if (itest == 0 || itest == 6) + { + const char *zfrom1, *zfrom2; + + if (fcall_uucico) + { + zfrom = "/usr/spool/uucppublic/to6\\*"; + zfrom1 = "/usr/spool/uucppublic/to6.1"; + zfrom2 = "/usr/spool/uucppublic/to6.2"; + } + else + { + zfrom = "/usr/tmp/tstuu/spool2/to6\\*"; + zfrom1 = "/usr/tmp/tstuu/spool2/to6.1"; + zfrom2 = "/usr/tmp/tstuu/spool2/to6.2"; + } + + umake_file (zfrom1, 100); + umake_file (zfrom2, 101); + (void) remove ("/usr/tmp/tstuu/to6.1"); + (void) remove ("/usr/tmp/tstuu/to6.2"); + + sprintf (ab, "%s %s!%s /usr/tmp/tstuu", zuucp1, zsys, zfrom); + xsystem (ab); + } + + /* Test having the second system request a wildcard. */ + if (itest == 0 || itest == 7) + { + const char *zto1, *zto2; + + if (fcall_uucico) + { + zto = "/usr/spool/uucppublic"; + zto1 = "/usr/spool/uucppublic/to7.1"; + zto2 = "/usr/spool/uucppublic/to7.2"; + } + else + { + zto = "/usr/tmp/tstuu"; + zto1 = "/usr/tmp/tstuu/to7.1"; + zto2 = "/usr/tmp/tstuu/to7.2"; + } + + umake_file ("/usr/tmp/tstuu/spool1/to7.1", 150); + umake_file ("/usr/tmp/tstuu/spool1/to7.2", 155); + (void) remove (zto1); + (void) remove (zto2); + + sprintf (ab, "%s test1!/usr/tmp/tstuu/spool1/to7.\\* %s", zuucp2, + zto); + xsystem (ab); + } + + /* Test an E command. This runs cat, discarding the output. */ + if ((itest == 0 || itest == 8) && ! fcall_uucico) + { + umake_file ("/usr/tmp/tstuu/from8", 30); + sprintf (ab, "%s - test2!cat < /usr/tmp/tstuu/from8", zuux1); + xsystem (ab); + } +} + +/* Try to make sure the file transfers were successful. */ + +static void +ucheck_test (itest, fcall_uucico) + int itest; + boolean fcall_uucico; +{ + if (itest == 0 || itest == 1) + { + if (fcall_uucico) + ucheck_file ("/usr/spool/uucppublic/to1", "test 1", 0); + else + ucheck_file ("/usr/tmp/tstuu/to1", "test 1", 0); + } + + if (itest == 0 || itest == 2) + ucheck_file ("/usr/tmp/tstuu/to2", "test 2", 3); + + if (itest == 0 || itest == 3) + ucheck_file ("/usr/tmp/tstuu/to3", "test 3", 5); + + if (itest == 0 || itest == 4) + { + if (fcall_uucico) + ucheck_file ("/usr/spool/uucppublic/to4", "test 4", 7); + else + ucheck_file ("/usr/tmp/tstuu/to4", "test 4", 7); + } + + if (itest == 0 || itest == 6) + { + ucheck_file ("/usr/tmp/tstuu/to6.1", "test 6.1", 100); + ucheck_file ("/usr/tmp/tstuu/to6.2", "test 6.2", 101); + } + + if (itest == 0 || itest == 7) + { + const char *zto1, *zto2; + + if (fcall_uucico) + { + zto1 = "/usr/spool/uucppublic/to7.1"; + zto2 = "/usr/spool/uucppublic/to7.2"; + } + else + { + zto1 = "/usr/tmp/tstuu/to7.1"; + zto2 = "/usr/tmp/tstuu/to7.2"; + } + + ucheck_file (zto1, "test 7.1", 150); + ucheck_file (zto2, "test 7.2", 155); + } +} + +/* A debugging routine used when displaying buffers. */ + +static int +cpshow (z, ichar) + char *z; + int ichar; +{ + if (isprint (BUCHAR (ichar)) && ichar != '\"') + { + *z = (char) ichar; + return 1; + } + + *z++ = '\\'; + + switch (ichar) + { + case '\n': + *z = 'n'; + return 2; + case '\r': + *z = 'r'; + return 2; + case '\"': + *z = '\"'; + return 2; + default: + sprintf (z, "%03o", (unsigned int)(ichar & 0xff)); + return strlen (z) + 1; + } +} + +/* Pick one of two file descriptors which is ready for reading, or + return in five seconds. If the argument is ready for reading, + leave it alone; otherwise set it to -1. */ + +static void +uchoose (po1, po2) + int *po1; + int *po2; +{ +#if HAVE_SELECT + + int iread; + struct timeval stime; + + iread = (1 << *po1) | (1 << *po2); + stime.tv_sec = 5; + stime.tv_usec = 0; + + if (select ((*po1 > *po2 ? *po1 : *po2) + 1, (pointer) &iread, + (pointer) NULL, (pointer) NULL, &stime) < 0) + { + perror ("select"); + uchild (SIGCHLD); + } + + if ((iread & (1 << *po1)) == 0) + *po1 = -1; + + if ((iread & (1 << *po2)) == 0) + *po2 = -1; + +#else /* ! HAVE_SELECT */ + +#if HAVE_POLL + + struct pollfd as[2]; + + as[0].fd = *po1; + as[0].events = POLLIN; + as[1].fd = *po2; + as[1].events = POLLIN; + + if (poll (as, 2, 5 * 1000) < 0) + { + perror ("poll"); + uchild (SIGCHLD); + } + + if ((as[0].revents & POLLIN) == 0) + *po1 = -1; + + if ((as[1].revents & POLLIN) == 0) + *po2 = -1; + +#endif /* HAVE_POLL */ +#endif /* ! HAVE_SELECT */ +} + +/* Read some data from a file descriptor. This keeps reading until + one of the reads gets no data. */ + +static long +cread (o, pqbuf) + int o; + struct sbuf **pqbuf; +{ + long ctotal; + + while (*pqbuf != NULL && (*pqbuf)->qnext != NULL) + pqbuf = &(*pqbuf)->qnext; + + ctotal = 0; + + while (TRUE) + { + int cgot; + + if (*pqbuf != NULL + && (*pqbuf)->cend >= sizeof (*pqbuf)->ab) + pqbuf = &(*pqbuf)->qnext; + + if (*pqbuf == NULL) + { + *pqbuf = (struct sbuf *) malloc (sizeof (struct sbuf)); + if (*pqbuf == NULL) + { + fprintf (stderr, "Out of memory\n"); + uchild (SIGCHLD); + } + (*pqbuf)->qnext = NULL; + (*pqbuf)->cstart = 0; + (*pqbuf)->cend = 0; + } + + cgot = read (o, (*pqbuf)->ab + (*pqbuf)->cend, + (sizeof (*pqbuf)->ab) - (*pqbuf)->cend); + if (cgot < 0) + { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA) + cgot = 0; + else + { + perror ("read"); + uchild (SIGCHLD); + } + } + + if (cgot == 0) + return ctotal; + + ctotal += cgot; + + if (zDebug != NULL) + { + char abshow[325]; + char *zfrom; + char *zshow; + int i; + + zfrom = (*pqbuf)->ab + (*pqbuf)->cend; + zshow = abshow; + for (i = 0; i < cgot && i < 80; i++, zfrom++) + zshow += cpshow (zshow, *zfrom); + if (i < cgot) + { + *zshow++ = '.'; + *zshow++ = '.'; + *zshow++ = '.'; + } + *zshow = '\0'; + fprintf (stderr, "Read from %d: %d \"%s\"\n", o, cgot, abshow); + fflush (stderr); + } + + if (iPercent > 0) + { + int i; + int c; + + c = 0; + for (i = 0; i < cgot; i++) + { + if (rand () % 1000 < iPercent) + { + ++(*pqbuf)->ab[(*pqbuf)->cend + i]; + ++c; + } + } + if (zDebug != NULL && c > 0) + fprintf (stderr, "Clobbered %d bytes\n", c); + } + + (*pqbuf)->cend += cgot; + + if (ctotal > 256) + return ctotal; + } +} + +/* Write data to a file descriptor until one of the writes gets no + data. */ + +static boolean +fsend (o, oslave, pqbuf) + int o; + int oslave; + struct sbuf **pqbuf; +{ + long ctotal; + + ctotal = 0; + while (*pqbuf != NULL) + { + int cwrite, cwrote; + + if ((*pqbuf)->cstart >= (*pqbuf)->cend) + { + struct sbuf *qfree; + + qfree = *pqbuf; + *pqbuf = (*pqbuf)->qnext; + free ((pointer) qfree); + continue; + } + +#ifdef FIONREAD + { + long cunread; + + if (ioctl (oslave, FIONREAD, &cunread) < 0) + { + perror ("FIONREAD"); + uchild (SIGCHLD); + } + if (zDebug != NULL) + fprintf (stderr, "%ld unread\n", cunread); + cwrite = 256 - cunread; + if (cwrite <= 0) + break; + } +#else /* ! FIONREAD */ + if (! fwritable (o)) + break; + cwrite = 1; +#endif /* ! FIONREAD */ + + if (cwrite > (*pqbuf)->cend - (*pqbuf)->cstart) + cwrite = (*pqbuf)->cend - (*pqbuf)->cstart; + + cwrote = write (o, (*pqbuf)->ab + (*pqbuf)->cstart, cwrite); + if (cwrote < 0) + { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA) + cwrote = 0; + else + { + perror ("write"); + uchild (SIGCHLD); + } + } + + if (cwrote == 0) + break; + + ctotal += cwrote; + (*pqbuf)->cstart += cwrote; + } + + if (zDebug != NULL && ctotal > 0) + fprintf (stderr, "Wrote %ld to %d\n", ctotal, o); + + return ctotal > 0; +} + +/* Check whether a file descriptor can be written to. */ + +static boolean +fwritable (o) + int o; +{ +#if HAVE_SELECT + + int iwrite; + struct timeval stime; + int cfds; + + iwrite = 1 << o; + + stime.tv_sec = 0; + stime.tv_usec = 0; + + cfds = select (o + 1, (pointer) NULL, (pointer) &iwrite, + (pointer) NULL, &stime); + if (cfds < 0) + { + perror ("select"); + uchild (SIGCHLD); + } + + return cfds > 0; + +#else /* ! HAVE_SELECT */ + +#if HAVE_POLL + + struct pollfd s; + int cfds; + + s.fd = o; + s.events = POLLOUT; + + cfds = poll (&s, 1, 0); + if (cfds < 0) + { + perror ("poll"); + uchild (SIGCHLD); + } + + return cfds > 0; + +#endif /* HAVE_POLL */ +#endif /* ! HAVE_SELECT */ +} + +/* A version of the system command that checks for errors. */ + +static void +xsystem (zcmd) + const char *zcmd; +{ + int istat; + + istat = system ((char *) zcmd); + if (istat != 0) + { + fprintf (stderr, "Command failed with status %d\n", istat); + fprintf (stderr, "%s\n", zcmd); + exit (EXIT_FAILURE); + } +} diff --git a/gnu/libexec/uucp/uuchk/Makefile b/gnu/libexec/uucp/uuchk/Makefile new file mode 100644 index 0000000000..0aaa00f4cd --- /dev/null +++ b/gnu/libexec/uucp/uuchk/Makefile @@ -0,0 +1,16 @@ +# Makefile for uuchk +# $Id: Makefile,v 1.2 1993/08/05 16:15:02 jtc Exp $ + +BINDIR= $(sbindir) +BINOWN= $(owner) + +PROG= uuchk +SRCS= uuchk.c +LDADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +DPADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +CFLAGS+= -I$(.CURDIR)/../common_sources\ + -DVERSION=\"$(VERSION)\" + +NOMAN= noman + +.include diff --git a/gnu/libexec/uucp/uuchk/uuchk.c b/gnu/libexec/uucp/uuchk/uuchk.c new file mode 100644 index 0000000000..41021defb8 --- /dev/null +++ b/gnu/libexec/uucp/uuchk/uuchk.c @@ -0,0 +1,856 @@ +/* uuchk.c + Display what we think the permissions of systems are. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char uuchk_rcsid[] = "$Id: uuchk.c,v 1.1 1993/08/04 19:36:14 jtc Exp $"; +#endif + +#include "getopt.h" + +#include "uuconf.h" + +/* Local functions. */ + +static void ukusage P((void)); +static void ukshow P((const struct uuconf_system *qsys, + pointer puuconf)); +static int ikshow_port P((struct uuconf_port *qport, pointer pinfo)); +static void ukshow_dialer P((struct uuconf_dialer *qdial)); +static void ukshow_chat P((const struct uuconf_chat *qchat, + const char *zhdr)); +static void ukshow_size P((struct uuconf_timespan *q, boolean fcall, + boolean flocal)); +static void ukshow_proto_params P((struct uuconf_proto_param *pas, + int cindent)); +static void ukshow_time P((const struct uuconf_timespan *)); +static struct uuconf_timespan *qcompress_span P((struct uuconf_timespan *)); +static void ukuuconf_error P((pointer puuconf, int iret)); + +/* Structure used to pass uuconf pointer into ikshow_port and also let + it record whether any ports were found. */ +struct sinfo +{ + /* The uuconf global pointer. */ + pointer puuconf; + /* The system. */ + const struct uuconf_system *qsys; + /* Whether any ports were seen. */ + boolean fgot; +}; + +/* Long getopt options. */ +static const struct option asKlongopts[] = { { NULL, 0, NULL, 0 } }; + +int +main (argc, argv) + int argc; + char **argv; +{ + int iopt; + /* The configuration file name. */ + const char *zconfig = NULL; + int iret; + pointer puuconf; + char **pzsystems; + + while ((iopt = getopt_long (argc, argv, "I:x:", asKlongopts, + (int *) NULL)) != EOF) + { + switch (iopt) + { + case 'I': + /* Set the configuration file name. */ + zconfig = optarg; + break; + + case 'x': + /* Set the debugging level. There is actually no debugging + information for this program. */ + break; + + case 0: + /* Long option found and flag set. */ + break; + + default: + ukusage (); + break; + } + } + + if (optind != argc) + ukusage (); + + iret = uuconf_init (&puuconf, (const char *) NULL, zconfig); + if (iret != UUCONF_SUCCESS) + ukuuconf_error (puuconf, iret); + + iret = uuconf_system_names (puuconf, &pzsystems, FALSE); + if (iret != UUCONF_SUCCESS) + ukuuconf_error (puuconf, iret); + + while (*pzsystems != NULL) + { + struct uuconf_system ssys; + + iret = uuconf_system_info (puuconf, *pzsystems, &ssys); + if (iret != UUCONF_SUCCESS) + ukuuconf_error (puuconf, iret); + else + ukshow (&ssys, puuconf); + (void) uuconf_system_free (puuconf, &ssys); + ++pzsystems; + if (*pzsystems != NULL) + printf ("\n"); + } + + exit (EXIT_SUCCESS); + + /* Avoid errors about not returning a value. */ + return 0; +} + +/* Print a usage message and die. */ + +static void +ukusage () +{ + fprintf (stderr, + "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n", + VERSION); + fprintf (stderr, + "Usage: uuchk [-I file]\n"); + fprintf (stderr, + " -I file: Set configuration file to use\n"); + exit (EXIT_FAILURE); +} + +/* Dump out the information for a system. */ + +static void +ukshow (qsys, puuconf) + const struct uuconf_system *qsys; + pointer puuconf; +{ + char **pz; + int i; + int iret; + + printf ("System: %s", qsys->uuconf_zname); + if (qsys->uuconf_pzalias != NULL) + { + printf (" ("); + for (pz = qsys->uuconf_pzalias; *pz != NULL; pz++) + { + printf ("%s", *pz); + if (pz[1] != NULL) + printf (" "); + } + printf (")"); + } + printf ("\n"); + + for (i = 0; qsys != NULL; qsys = qsys->uuconf_qalternate, i++) + { + boolean fcall, fcalled; + struct uuconf_timespan *qtime, *qspan; + + if (i != 0 || qsys->uuconf_qalternate != NULL) + { + printf ("Alternate %d", i); + if (qsys->uuconf_zalternate != NULL) + printf (" (%s)", qsys->uuconf_zalternate); + printf ("\n"); + } + + /* See if this alternate could be used when calling out. */ + fcall = qsys->uuconf_fcall; + if (qsys->uuconf_qtimegrade == NULL) + fcall = FALSE; + + /* See if this alternate could be used when calling in. */ + fcalled = qsys->uuconf_fcalled; + + if (! fcall && ! fcalled) + { + printf (" This alternate is never used\n"); + continue; + } + + if (fcalled) + { + if (qsys->uuconf_zcalled_login != NULL + && strcmp (qsys->uuconf_zcalled_login, "ANY") != 0) + { + if (i == 0 && qsys->uuconf_qalternate == NULL) + printf (" Caller must log in as %s\n", + qsys->uuconf_zcalled_login); + else + printf (" When called using login name %s\n", + qsys->uuconf_zcalled_login); + } + else + printf (" When called using any login name\n"); + + if (qsys->uuconf_zlocalname != NULL) + printf (" Will use %s as name of local system\n", + qsys->uuconf_zlocalname); + } + + if (fcalled && qsys->uuconf_fcallback) + { + printf (" If called, will call back\n"); + fcalled = FALSE; + } + + if (fcall) + { + struct sinfo si; + + if (i == 0 && qsys->uuconf_qalternate == NULL) + printf (" Call out"); + else + printf (" This alternate applies when calling"); + + if (qsys->uuconf_zport != NULL || qsys->uuconf_qport != NULL) + { + printf (" using "); + if (qsys->uuconf_zport != NULL) + printf ("port %s", qsys->uuconf_zport); + else + printf ("a specially defined port"); + if (qsys->uuconf_ibaud != 0) + { + printf (" at speed %ld", qsys->uuconf_ibaud); + if (qsys->uuconf_ihighbaud != 0) + printf (" to %ld", qsys->uuconf_ihighbaud); + } + printf ("\n"); + } + else if (qsys->uuconf_ibaud != 0) + { + printf (" at speed %ld", qsys->uuconf_ibaud); + if (qsys->uuconf_ihighbaud != 0) + printf (" to %ld", qsys->uuconf_ihighbaud); + printf ("\n"); + } + else + printf (" using any port\n"); + + si.puuconf = puuconf; + si.qsys = qsys; + si.fgot = FALSE; + + if (qsys->uuconf_qport != NULL) + { + printf (" The port is defined as:\n"); + (void) ikshow_port (qsys->uuconf_qport, (pointer) &si); + } + else + { + struct uuconf_port sdummy; + + printf (" The possible ports are:\n"); + iret = uuconf_find_port (puuconf, qsys->uuconf_zport, + qsys->uuconf_ibaud, + qsys->uuconf_ihighbaud, + ikshow_port, (pointer) &si, + &sdummy); + if (iret != UUCONF_NOT_FOUND) + ukuuconf_error (puuconf, iret); + if (! si.fgot) + printf (" *** There are no matching ports\n"); + } + + if (qsys->uuconf_zphone != NULL) + { + if ((qsys->uuconf_zport != NULL + && strcmp (qsys->uuconf_zport, "TCP") == 0) + || (qsys->uuconf_qport != NULL + && (qsys->uuconf_qport->uuconf_ttype + == UUCONF_PORTTYPE_TCP + || qsys->uuconf_qport->uuconf_ttype + == UUCONF_PORTTYPE_TLI))) + printf (" Remote address %s\n", qsys->uuconf_zphone); + else + printf (" Phone number %s\n", qsys->uuconf_zphone); + } + + ukshow_chat (&qsys->uuconf_schat, " Chat"); + + if (qsys->uuconf_zcall_login != NULL + || qsys->uuconf_zcall_password != NULL) + { + char *zlogin, *zpass; + + iret = uuconf_callout (puuconf, qsys, &zlogin, &zpass); + if (iret == UUCONF_NOT_FOUND) + printf (" Can not determine login name or password\n"); + else if (iret != UUCONF_SUCCESS) + ukuuconf_error (puuconf, iret); + else + { + if (zlogin != NULL) + { + printf (" Login name %s\n", zlogin); + free ((pointer) zlogin); + } + if (zpass != NULL) + { + printf (" Password %s\n", zpass); + free ((pointer) zpass); + } + } + } + + qtime = qcompress_span (qsys->uuconf_qtimegrade); + + for (qspan = qtime; qspan != NULL; qspan = qspan->uuconf_qnext) + { + printf (" "); + ukshow_time (qspan); + printf (" may call if "); + if ((char) qspan->uuconf_ival == UUCONF_GRADE_LOW) + printf ("any work"); + else + printf ("work grade %c or higher", (char) qspan->uuconf_ival); + if (qspan->uuconf_cretry != 0) + printf (" (retry %d)", qspan->uuconf_cretry); + printf ("\n"); + } + + if (qsys->uuconf_qcalltimegrade != NULL) + { + boolean fprint, fother; + + qtime = qcompress_span (qsys->uuconf_qcalltimegrade); + fprint = FALSE; + fother = FALSE; + if (qtime->uuconf_istart != 0) + fother = TRUE; + for (qspan = qtime; qspan != NULL; qspan = qspan->uuconf_qnext) + { + if ((char) qspan->uuconf_ival == UUCONF_GRADE_LOW) + { + fother = TRUE; + continue; + } + fprint = TRUE; + printf (" "); + ukshow_time (qspan); + printf (" may accept work grade %c or higher\n", + (char) qspan->uuconf_ival); + if (qspan->uuconf_qnext == NULL) + { + if (qspan->uuconf_iend != 7 * 24 * 60) + fother = TRUE; + } + else + { + if (qspan->uuconf_iend + != qspan->uuconf_qnext->uuconf_istart) + fother = TRUE; + } + } + if (fprint && fother) + printf (" (At other times may accept any work)\n"); + } + } + + if (qsys->uuconf_fsequence) + printf (" Sequence numbers are used\n"); + + if (fcalled) + ukshow_chat (&qsys->uuconf_scalled_chat, " When called, chat"); + + if (qsys->uuconf_zdebug != NULL) + printf (" Debugging level %s\n", qsys->uuconf_zdebug); + if (qsys->uuconf_zmax_remote_debug != NULL) + printf (" Max remote debugging level %s\n", + qsys->uuconf_zmax_remote_debug); + + if (fcall) + { + ukshow_size (qsys->uuconf_qcall_local_size, TRUE, TRUE); + ukshow_size (qsys->uuconf_qcall_remote_size, TRUE, FALSE); + } + if (fcalled) + { + ukshow_size (qsys->uuconf_qcalled_local_size, FALSE, TRUE); + ukshow_size (qsys->uuconf_qcalled_remote_size, FALSE, TRUE); + } + + if (fcall) + printf (" May %smake local requests when calling\n", + qsys->uuconf_fcall_transfer ? "" : "not "); + + if (fcalled) + printf (" May %smake local requests when called\n", + qsys->uuconf_fcalled_transfer ? "" : "not "); + + if (qsys->uuconf_fcall_transfer || qsys->uuconf_fcalled_transfer) + { + printf (" May send by local request:"); + for (pz = qsys->uuconf_pzlocal_send; *pz != NULL; pz++) + printf (" %s", *pz); + printf ("\n"); + } + if (! qsys->uuconf_fsend_request) + printf (" May not send files by remote request\n"); + else + { + printf (" May send by remote request:"); + for (pz = qsys->uuconf_pzremote_send; *pz != NULL; pz++) + printf (" %s", *pz); + printf ("\n"); + } + if (qsys->uuconf_fcall_transfer || qsys->uuconf_fcalled_transfer) + { + printf (" May accept by local request:"); + for (pz = qsys->uuconf_pzlocal_receive; *pz != NULL; pz++) + printf (" %s", *pz); + printf ("\n"); + } + if (! qsys->uuconf_frec_request) + printf (" May not receive files by remote request\n"); + else + { + printf (" May receive by remote request:"); + for (pz = qsys->uuconf_pzremote_receive; *pz != NULL; pz++) + printf (" %s", *pz); + printf ("\n"); + } + + printf (" May execute"); + for (pz = qsys->uuconf_pzcmds; *pz != NULL; pz++) + printf (" %s", *pz); + printf ("\n"); + + printf (" Execution path"); + for (pz = qsys->uuconf_pzpath; *pz != NULL; pz++) + printf (" %s" , *pz); + printf ("\n"); + + if (qsys->uuconf_cfree_space != 0) + printf (" Will leave %ld bytes available\n", qsys->uuconf_cfree_space); + + if (qsys->uuconf_zpubdir != NULL) + printf (" Public directory is %s\n", qsys->uuconf_zpubdir); + + if (qsys->uuconf_pzforward_from != NULL) + { + printf (" May forward from"); + for (pz = qsys->uuconf_pzforward_from; *pz != NULL; pz++) + printf (" %s", *pz); + printf ("\n"); + } + + if (qsys->uuconf_pzforward_to != NULL) + { + printf (" May forward to"); + for (pz = qsys->uuconf_pzforward_to; *pz != NULL; pz++) + printf (" %s", *pz); + printf ("\n"); + } + + if (qsys->uuconf_zprotocols != NULL) + printf (" Will use protocols %s\n", qsys->uuconf_zprotocols); + else + printf (" Will use any known protocol\n"); + + if (qsys->uuconf_qproto_params != NULL) + ukshow_proto_params (qsys->uuconf_qproto_params, 1); + } +} + +/* Show information about a port. */ + +/*ARGSUSED*/ +static int +ikshow_port (qport, pinfo) + struct uuconf_port *qport; + pointer pinfo; +{ + struct sinfo *qi = (struct sinfo *) pinfo; + char **pz; + struct uuconf_modem_port *qmodem; + struct uuconf_tli_port *qtli; + + qi->fgot = TRUE; + + printf (" Port name %s\n", qport->uuconf_zname); + switch (qport->uuconf_ttype) + { + case UUCONF_PORTTYPE_STDIN: + printf (" Port type stdin\n"); + break; + case UUCONF_PORTTYPE_DIRECT: + printf (" Port type direct\n"); + if (qport->uuconf_u.uuconf_sdirect.uuconf_zdevice != NULL) + printf (" Device %s\n", + qport->uuconf_u.uuconf_sdirect.uuconf_zdevice); + printf (" Speed %ld\n", qport->uuconf_u.uuconf_sdirect.uuconf_ibaud); + break; + case UUCONF_PORTTYPE_MODEM: + qmodem = &qport->uuconf_u.uuconf_smodem; + printf (" Port type modem\n"); + if (qmodem->uuconf_zdevice != NULL) + printf (" Device %s\n", qmodem->uuconf_zdevice); + if (qmodem->uuconf_zdial_device != NULL) + printf (" Dial device %s\n", qmodem->uuconf_zdial_device); + printf (" Speed %ld\n", qmodem->uuconf_ibaud); + if (qmodem->uuconf_ilowbaud != qmodem->uuconf_ihighbaud) + printf (" Speed range %ld to %ld\n", qmodem->uuconf_ilowbaud, + qmodem->uuconf_ihighbaud); + printf (" Carrier %savailable\n", + qmodem->uuconf_fcarrier ? "" : "not "); + if (qmodem->uuconf_qdialer != NULL) + { + printf (" Specially defined dialer\n"); + ukshow_dialer (qmodem->uuconf_qdialer); + } + else if (qmodem->uuconf_pzdialer != NULL + && qmodem->uuconf_pzdialer[0] != NULL) + { + struct uuconf_dialer sdial; + int iret; + + /* This might be a single dialer name, or it might be a + sequence of dialer/token pairs. */ + + if (qmodem->uuconf_pzdialer[1] == NULL + || qmodem->uuconf_pzdialer[2] == NULL) + { + iret = uuconf_dialer_info (qi->puuconf, + qmodem->uuconf_pzdialer[0], + &sdial); + if (iret == UUCONF_NOT_FOUND) + printf (" *** No dialer %s\n", qmodem->uuconf_pzdialer[0]); + else if (iret != UUCONF_SUCCESS) + ukuuconf_error (qi->puuconf, iret); + else + { + printf (" Dialer %s\n", qmodem->uuconf_pzdialer[0]); + ukshow_dialer (&sdial); + if (qmodem->uuconf_pzdialer[1] != NULL) + printf (" Token %s\n", qmodem->uuconf_pzdialer[1]); + } + } + else + { + pz = qmodem->uuconf_pzdialer; + while (*pz != NULL) + { + iret = uuconf_dialer_info (qi->puuconf, *pz, &sdial); + if (iret == UUCONF_NOT_FOUND) + printf (" *** No dialer %s\n", *pz); + else if (iret != UUCONF_SUCCESS) + ukuuconf_error (qi->puuconf, iret); + else + { + printf (" Dialer %s\n", *pz); + ukshow_dialer (&sdial); + } + + ++pz; + if (*pz != NULL) + { + printf (" Token %s\n", *pz); + ++pz; + } + } + } + } + else + printf (" *** No dialer information\n"); + break; + case UUCONF_PORTTYPE_TCP: + printf (" Port type tcp\n"); + printf (" TCP service %s\n", + qport->uuconf_u.uuconf_stcp.uuconf_zport); + break; + case UUCONF_PORTTYPE_TLI: + qtli = &qport->uuconf_u.uuconf_stli; + printf (" Port type TLI%s\n", + qtli->uuconf_fstream ? "S" : ""); + if (qtli->uuconf_zdevice != NULL) + printf (" Device %s\n", qtli->uuconf_zdevice); + if (qtli->uuconf_pzpush != NULL) + { + printf (" Push"); + for (pz = qtli->uuconf_pzpush; *pz != NULL; pz++) + printf (" %s", *pz); + printf ("\n"); + } + if (qtli->uuconf_pzdialer != NULL + && qtli->uuconf_pzdialer[0] != NULL) + { + printf (" Dialer sequence"); + for (pz = qtli->uuconf_pzdialer; *pz != NULL; pz++) + printf (" %s", *pz); + printf ("\n"); + } + if (qtli->uuconf_zservaddr != NULL) + printf (" Server address %s\n", qtli->uuconf_zservaddr); + break; + default: + fprintf (stderr, " CAN'T HAPPEN\n"); + break; + } + + if (qport->uuconf_zprotocols != NULL) + printf (" Will use protocols %s\n", qport->uuconf_zprotocols); + + if (qport->uuconf_zlockname != NULL) + printf (" Will use lockname %s\n", qport->uuconf_zlockname); + + if (qport->uuconf_qproto_params != NULL) + ukshow_proto_params (qport->uuconf_qproto_params, 3); + + /* Return NOT_FOUND to force find_port to continue searching. */ + return UUCONF_NOT_FOUND; +} + +/* Show information about a dialer. */ + +static void +ukshow_dialer (q) + struct uuconf_dialer *q; +{ + ukshow_chat (&q->uuconf_schat, " Chat"); + printf (" Wait for dialtone %s\n", q->uuconf_zdialtone); + printf (" Pause while dialing %s\n", q->uuconf_zpause); + printf (" Carrier %savailable\n", q->uuconf_fcarrier ? "" : "not "); + if (q->uuconf_fcarrier) + printf (" Wait %d seconds for carrier\n", q->uuconf_ccarrier_wait); + if (q->uuconf_fdtr_toggle) + { + printf (" Toggle DTR"); + if (q->uuconf_fdtr_toggle_wait) + printf (" and wait"); + printf ("\n"); + } + ukshow_chat (&q->uuconf_scomplete, " When complete chat"); + ukshow_chat (&q->uuconf_sabort, " When aborting chat"); + if (q->uuconf_qproto_params != NULL) + ukshow_proto_params (q->uuconf_qproto_params, 4); +} + +/* Show a chat script. */ + +static void +ukshow_chat (qchat, zhdr) + const struct uuconf_chat *qchat; + const char *zhdr; +{ + char **pz; + + if (qchat->uuconf_pzprogram != NULL) + { + printf ("%s program", zhdr); + for (pz = qchat->uuconf_pzprogram; *pz != NULL; pz++) + printf (" %s", *pz); + printf ("\n"); + } + + if (qchat->uuconf_pzchat != NULL) + { + + printf ("%s script", zhdr); + for (pz = qchat->uuconf_pzchat; *pz != NULL; pz++) + { + if ((*pz)[0] != '-' || pz == qchat->uuconf_pzchat) + printf (" "); + printf ("%s", *pz); + } + printf ("\n"); + printf ("%s script timeout %d\n", zhdr, qchat->uuconf_ctimeout); + if (qchat->uuconf_pzfail != NULL) + { + printf ("%s failure strings", zhdr); + for (pz = qchat->uuconf_pzfail; *pz != NULL; pz++) + printf (" %s", *pz); + printf ("\n"); + } + if (qchat->uuconf_fstrip) + printf ("%s script incoming bytes stripped to seven bits\n", zhdr); + } +} + +/* Show a size/time restriction. */ + +static void +ukshow_size (qspan, fcall, flocal) + struct uuconf_timespan *qspan; + boolean fcall; + boolean flocal; +{ + struct uuconf_timespan *q; + boolean fother; + + qspan = qcompress_span (qspan); + if (qspan == NULL) + return; + + printf (" If call%s the following applies to a %s request:\n", + fcall ? "ing" : "ed", flocal ? "local" : "remote"); + + fother = FALSE; + if (qspan->uuconf_istart >= 60) + fother = TRUE; + + for (q = qspan; q != NULL; q = q->uuconf_qnext) + { + printf (" "); + ukshow_time (q); + printf (" may transfer files %ld bytes or smaller\n", q->uuconf_ival); + if (q->uuconf_qnext == NULL) + { + if (q->uuconf_iend <= 6 * 24 * 60 + 23 * 60) + fother = TRUE; + } + else + { + if (q->uuconf_iend + 60 <= q->uuconf_qnext->uuconf_istart) + fother = TRUE; + } + } + + if (fother) + printf (" (At other times may send files of any size)\n"); +} + +/* Show protocol parameters. */ + +static void +ukshow_proto_params (pas, cindent) + struct uuconf_proto_param *pas; + int cindent; +{ + struct uuconf_proto_param *q; + + for (q = pas; q->uuconf_bproto != '\0'; q++) + { + int i; + struct uuconf_proto_param_entry *qe; + + for (i = 0; i < cindent; i++) + printf (" "); + printf ("For protocol %c will use the following parameters\n", + q->uuconf_bproto); + for (qe = q->uuconf_qentries; qe->uuconf_cargs > 0; qe++) + { + int ia; + + for (i = 0; i < cindent; i++) + printf (" "); + for (ia = 0; ia < qe->uuconf_cargs; ia++) + printf (" %s", qe->uuconf_pzargs[ia]); + printf ("\n"); + } + } +} + +/* Display a time span. */ + +static void +ukshow_time (q) + const struct uuconf_timespan *q; +{ + int idaystart, idayend; + int ihourstart, ihourend; + int iminutestart, iminuteend; + const char * const zdays = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat\0Sun"; + + if (q->uuconf_istart == 0 && q->uuconf_iend == 7 * 24 * 60) + { + printf ("At any time"); + return; + } + + idaystart = q->uuconf_istart / (24 * 60); + ihourstart = (q->uuconf_istart % (24 * 60)) / 60; + iminutestart = q->uuconf_istart % 60; + idayend = q->uuconf_iend / (24 * 60); + ihourend = (q->uuconf_iend % (24 * 60)) / 60; + iminuteend = q->uuconf_iend % 60; + + if (idaystart == idayend) + printf ("%s from %02d:%02d to %02d:%02d", zdays + idaystart * 4, + ihourstart, iminutestart, ihourend, iminuteend); + else + printf ("From %s %02d:%02d to %s %02d:%02d", + zdays + idaystart * 4, ihourstart, iminutestart, + zdays + idayend * 4, ihourend, iminuteend); +} + +/* Compress a time span by merging any two adjacent spans with + identical values. This isn't necessary for uucico, but it looks + nicer when printed out. */ + +static struct uuconf_timespan * +qcompress_span (qlist) + struct uuconf_timespan *qlist; +{ + struct uuconf_timespan **pq; + + pq = &qlist; + while (*pq != NULL) + { + if ((*pq)->uuconf_qnext != NULL + && (*pq)->uuconf_iend == (*pq)->uuconf_qnext->uuconf_istart + && (*pq)->uuconf_ival == (*pq)->uuconf_qnext->uuconf_ival) + { + struct uuconf_timespan *qnext; + + qnext = (*pq)->uuconf_qnext; + (*pq)->uuconf_qnext = qnext->uuconf_qnext; + (*pq)->uuconf_iend = qnext->uuconf_iend; + } + else + pq = &(*pq)->uuconf_qnext; + } + + return qlist; +} + +/* Display a uuconf error and exit. */ + +static void +ukuuconf_error (puuconf, iret) + pointer puuconf; + int iret; +{ + char ab[512]; + + (void) uuconf_error_string (puuconf, iret, ab, sizeof ab); + if ((iret & UUCONF_ERROR_FILENAME) == 0) + fprintf (stderr, "uuchk: %s\n", ab); + else + fprintf (stderr, "uuchk:%s\n", ab); + exit (EXIT_FAILURE); +} diff --git a/gnu/libexec/uucp/uucico/Makefile b/gnu/libexec/uucp/uucico/Makefile new file mode 100644 index 0000000000..d78d45c4f6 --- /dev/null +++ b/gnu/libexec/uucp/uucico/Makefile @@ -0,0 +1,20 @@ +# Makefile for uucico +# $Id: Makefile,v 1.1 1993/08/05 18:27:08 conklin Exp $ + +BINDIR= $(sbindir) +BINOWN= $(owner) +BINMODE= 4555 + +PROG= uucico +SRCS= uucico.c trans.c send.c rec.c xcmd.c prot.c protg.c protf.c \ + prott.c prote.c proti.c protj.c protz.c time.c chat.c \ + conn.c copy.c log.c tcp.c tli.c util.c +LDADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +DPADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +CFLAGS+= -I$(.CURDIR)/../common_sources\ + -DVERSION=\"$(VERSION)\" + +MAN8= uucico.8 + +.include +.PATH: $(.CURDIR)/../common_sources diff --git a/gnu/libexec/uucp/uucico/prote.c b/gnu/libexec/uucp/uucico/prote.c new file mode 100644 index 0000000000..2d2e77e1d8 --- /dev/null +++ b/gnu/libexec/uucp/uucico/prote.c @@ -0,0 +1,387 @@ +/* prote.c + The 'e' protocol. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char prote_rcsid[] = "$Id: prote.c,v 1.1 1993/08/04 19:36:18 jtc Exp $"; +#endif + +#include "uudefs.h" +#include "uuconf.h" +#include "conn.h" +#include "trans.h" +#include "system.h" +#include "prot.h" + +/* This implementation is based on my implementation of the 't' + protocol, which is fairly similar. The main difference between the + protocols seems to be that 't' breaks the file into packets and + transmits the size of the packet with each packet, whereas 'e' + sends the size of the entire file and then sends all the data in a + single enormous packet. + + The 'e' protocol does no error checking whatsoever and thus + requires an end-to-end verified eight bit communication line, such + as is provided by TCP. Using it with a modem is inadvisable, since + errors can occur between the modem and the computer. */ + +/* The buffer size we use. */ +#define CEBUFSIZE (CRECBUFLEN / 2) + +/* The size of the initial file size message. */ +#define CEFRAMELEN (20) + +/* A pointer to the buffer we will use. */ +static char *zEbuf; + +/* True if we are receiving a file. */ +static boolean fEfile; + +/* The number of bytes we have left to send or receive. */ +static long cEbytes; + +/* The timeout we use. */ +static int cEtimeout = 120; + +struct uuconf_cmdtab asEproto_params[] = +{ + { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cEtimeout, NULL }, + { NULL, 0, NULL, NULL } +}; + +/* Local function. */ + +static boolean feprocess_data P((struct sdaemon *qdaemon, boolean *pfexit, + size_t *pcneed)); + +/* Start the protocol. */ + +boolean +festart (qdaemon, pzlog) + struct sdaemon *qdaemon; + char **pzlog; +{ + *pzlog = NULL; + if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE, + STRIPSETTING_EIGHTBITS, XONXOFF_OFF)) + return FALSE; + zEbuf = (char *) xmalloc (CEBUFSIZE); + fEfile = FALSE; + usysdep_sleep (2); + return TRUE; +} + +/* Stop the protocol. */ + +/*ARGSUSED*/ +boolean +feshutdown (qdaemon) + struct sdaemon *qdaemon; +{ + xfree ((pointer) zEbuf); + zEbuf = NULL; + cEtimeout = 120; + return TRUE; +} + +/* Send a command string. We send everything up to and including the + null byte. */ + +/*ARGSUSED*/ +boolean +fesendcmd (qdaemon, z, ilocal, iremote) + struct sdaemon *qdaemon; + const char *z; + int ilocal; + int iremote; +{ + DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fesendcmd: Sending command \"%s\"", z); + + return fsend_data (qdaemon->qconn, z, strlen (z) + 1, TRUE); +} + +/* Get space to be filled with data. We provide a buffer which has + 20 bytes at the start available to hold the length. */ + +/*ARGSUSED*/ +char * +zegetspace (qdaemon, pclen) + struct sdaemon *qdaemon; + size_t *pclen; +{ + *pclen = CEBUFSIZE; + return zEbuf; +} + +/* Send out some data. We are allowed to modify the 20 bytes + preceding the buffer. This allows us to send the entire block with + header bytes in a single call. */ + +/*ARGSIGNORED*/ +boolean +fesenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos) + struct sdaemon *qdaemon; + char *zdata; + size_t cdata; + int ilocal; + int iremote; + long ipos; +{ +#if DEBUG > 0 + /* Keep track of the number of bytes we send out to make sure it all + adds up. */ + cEbytes -= cdata; + if (cEbytes < 0) + { + ulog (LOG_ERROR, "Protocol 'e' internal error"); + return FALSE; + } +#endif + + /* We pass FALSE to fsend_data since we don't expect the other side + to be sending us anything just now. */ + return fsend_data (qdaemon->qconn, zdata, cdata, FALSE); +} + +/* Process data and return the amount we need in *pfneed. */ + +static boolean +feprocess_data (qdaemon, pfexit, pcneed) + struct sdaemon *qdaemon; + boolean *pfexit; + size_t *pcneed; +{ + int cinbuf, cfirst, clen; + + *pfexit = FALSE; + + cinbuf = iPrecend - iPrecstart; + if (cinbuf < 0) + cinbuf += CRECBUFLEN; + + if (! fEfile) + { + /* We are not receiving a file. Commands continue up to a null + byte. */ + while (cinbuf > 0) + { + char *pnull; + + cfirst = CRECBUFLEN - iPrecstart; + if (cfirst > cinbuf) + cfirst = cinbuf; + + pnull = memchr (abPrecbuf + iPrecstart, '\0', (size_t) cfirst); + if (pnull != NULL) + cfirst = pnull - (abPrecbuf + iPrecstart) + 1; + + DEBUG_MESSAGE1 (DEBUG_PROTO, + "feprocess_data: Got %d command bytes", + cfirst); + + if (! fgot_data (qdaemon, abPrecbuf + iPrecstart, + (size_t) cfirst, (const char *) NULL, (size_t) 0, + -1, -1, (long) -1, TRUE, pfexit)) + return FALSE; + + iPrecstart = (iPrecstart + cfirst) % CRECBUFLEN; + + if (*pfexit) + return TRUE; + + cinbuf = iPrecend - iPrecstart; + if (cinbuf < 0) + cinbuf += CRECBUFLEN; + } + + if (pcneed != NULL) + *pcneed = 1; + + return TRUE; + } + + /* Here we are receiving a file. We want cEbytes in total. If we + don't have cEbytes yet, we have to get it first. */ + + if (cEbytes == -1) + { + char ab[CEFRAMELEN + 1]; + + if (cinbuf < CEFRAMELEN) + { + if (pcneed != NULL) + *pcneed = CEFRAMELEN - cinbuf; + return TRUE; + } + + cfirst = CRECBUFLEN - iPrecstart; + if (cfirst >= CEFRAMELEN) + memcpy (ab, abPrecbuf + iPrecstart, (size_t) CEFRAMELEN); + else + { + memcpy (ab, abPrecbuf + iPrecstart, (size_t) cfirst); + memcpy (ab + cfirst, abPrecbuf, (size_t) CEFRAMELEN - cfirst); + } + + ab[CEFRAMELEN] = '\0'; + cEbytes = strtol (ab, (char **) NULL, 10); + + iPrecstart = (iPrecstart + CEFRAMELEN) % CRECBUFLEN; + + cinbuf = iPrecend - iPrecstart; + if (cinbuf < 0) + cinbuf += CRECBUFLEN; + } + + /* Here we can read real data for the file. */ + + while (cinbuf > 0) + { + clen = cinbuf; + if ((long) clen > cEbytes) + clen = (int) cEbytes; + + cfirst = CRECBUFLEN - iPrecstart; + if (cfirst > clen) + cfirst = clen; + + DEBUG_MESSAGE1 (DEBUG_PROTO, + "feprocess_data: Got %d data bytes", + clen); + + if (! fgot_data (qdaemon, abPrecbuf + iPrecstart, + (size_t) cfirst, abPrecbuf, (size_t) (clen - cfirst), + -1, -1, (long) -1, TRUE, pfexit)) + return FALSE; + + iPrecstart = (iPrecstart + clen) % CRECBUFLEN; + cEbytes -= clen; + + if (cEbytes == 0) + { + if (! fgot_data (qdaemon, abPrecbuf, (size_t) 0, + (const char *) NULL, (size_t) 0, + -1, -1, (long) -1, TRUE, pfexit)) + return FALSE; + if (*pfexit) + return TRUE; + } + + cinbuf -= clen; + } + + if (pcneed != NULL) + { + if (cEbytes > CRECBUFLEN / 2) + *pcneed = CRECBUFLEN / 2; + else + *pcneed = (int) cEbytes; + } + + return TRUE; +} + +/* Wait for data to come in and process it until we've reached the end + of a command or a file. */ + +boolean +fewait (qdaemon) + struct sdaemon *qdaemon; +{ + while (TRUE) + { + boolean fexit; + size_t cneed, crec; + + if (! feprocess_data (qdaemon, &fexit, &cneed)) + return FALSE; + if (fexit) + return TRUE; + + if (! freceive_data (qdaemon->qconn, cneed, &crec, cEtimeout, TRUE)) + return FALSE; + + if (crec == 0) + { + ulog (LOG_ERROR, "Timed out waiting for data"); + return FALSE; + } + } +} + +/* File level routine, to handle transferring the amount of data and + to set fEfile correctly. */ + +boolean +fefile (qdaemon, qtrans, fstart, fsend, cbytes, pfhandled) + struct sdaemon *qdaemon; + struct stransfer *qtrans; + boolean fstart; + boolean fsend; + long cbytes; + boolean *pfhandled; +{ + *pfhandled = FALSE; + + if (fstart) + { + if (fsend) + { + char ab[CEFRAMELEN]; + + DEBUG_MESSAGE1 (DEBUG_PROTO, + "Protocol 'e' starting to send %ld bytes", + cbytes); + + bzero (ab, (size_t) CEFRAMELEN); + sprintf (ab, "%ld", cbytes); + if (! fsend_data (qdaemon->qconn, ab, (size_t) CEFRAMELEN, TRUE)) + return FALSE; + cEbytes = cbytes; + } + else + { + cEbytes = -1; + fEfile = TRUE; + } + } + else + { + if (! fsend) + fEfile = FALSE; +#if DEBUG > 0 + if (cEbytes != 0) + { + ulog (LOG_ERROR, + "Protocol 'e' internal error: %ld bytes left over", + cEbytes); + return FALSE; + } +#endif + } + + return TRUE; +} diff --git a/gnu/libexec/uucp/uucico/protf.c b/gnu/libexec/uucp/uucico/protf.c new file mode 100644 index 0000000000..3cdacef25e --- /dev/null +++ b/gnu/libexec/uucp/uucico/protf.c @@ -0,0 +1,842 @@ +/* protf.c + The 'f' protocol. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char protf_rcsid[] = "$Id: protf.c,v 1.1 1993/08/04 19:36:19 jtc Exp $"; +#endif + +#include +#include + +#include "uudefs.h" +#include "uuconf.h" +#include "conn.h" +#include "trans.h" +#include "system.h" +#include "prot.h" + +/* This implementation is based on code by Piet Beertema, CWI, + Amsterdam, Sep 1984. + + This code implements the 'f' protocol, which requires a + flow-controlled error-free seven-bit data path. It does check for + errors, but only at the end of each file transmission, so a noisy + line without error correcting modems will be unusable. + + The conversion to seven bit data is done as follows, where b + represents the character to convert: + + 0 <= b <= 037: 0172, b + 0100 (0100 to 0137) + 040 <= b <= 0171: b ( 040 to 0171) + 0172 <= b <= 0177: 0173, b - 0100 ( 072 to 077) + 0200 <= b <= 0237: 0174, b - 0100 (0100 to 0137) + 0240 <= b <= 0371: 0175, b - 0200 ( 040 to 0171) + 0372 <= b <= 0377: 0176, b - 0300 ( 072 to 077) + + This causes all output bytes to be in the range 040 to 0176; these + are the printable ASCII characters. */ + +/* This structure is used to hold information when dealing with the + end of file acknowledgement. */ + +struct sfinfo +{ + /* The functions from the generic code. */ + boolean (*psendfn) P((struct stransfer *qtrans, struct sdaemon *qdaemon)); + boolean (*precfn) P((struct stransfer *qtrans, struct sdaemon *qdaemon, + const char *zdata, size_t cdata)); + /* The info pointer from the generic code. */ + pointer pinfo; + /* The character to send after receiving the checksum. */ + char bsend; +}; + +/* Internal functions. */ +static boolean ffprocess_data P((struct sdaemon *qdaemon, + boolean *pfexit, size_t *pcneed)); +static boolean ffawait_ack P((struct stransfer *qtrans, + struct sdaemon *qdaemon, + const char *zdata, size_t cdata)); +static boolean ffawait_cksum P((struct stransfer *qtrans, + struct sdaemon *qdaemon, + const char *zdata, size_t cdata)); +static boolean ffsend_ack P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); + +/* The size of the buffer we allocate to store outgoing data in. */ +#define CFBUFSIZE (256) + +/* The timeout to wait for data to arrive before giving up. */ +static int cFtimeout = 120; + +/* The maximum number of retries. */ +static int cFmaxretries = 2; + +/* The buffer we allocate for outgoing data. */ +static char *zFbuf; + +/* TRUE if we are receiving a file rather than a command. */ +static boolean fFfile; + +/* The checksum so far. */ +static unsigned int iFcheck; + +/* The last special byte (0172 to 0176) or 0 if none. */ +static char bFspecial; + +/* The number of times we have retried this file. */ +static int cFretries; + +/* Whether this file has been acknowledged. */ +static boolean fFacked; + +struct uuconf_cmdtab asFproto_params[] = +{ + { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cFtimeout, NULL }, + { "retries", UUCONF_CMDTABTYPE_INT, (pointer) &cFmaxretries, NULL }, + { NULL, 0, NULL, NULL } +}; + +/* Statistics. */ + +/* The number of data bytes sent in files. */ +static long cFsent_data; + +/* The number of actual bytes sent in files. */ +static long cFsent_bytes; + +/* The number of data bytes received in files. */ +static long cFrec_data; + +/* The number of actual bytes received in files. */ +static long cFrec_bytes; + +/* The number of file retries when sending. */ +static long cFsend_retries; + +/* The number of file retries when receiving. */ +static long cFrec_retries; + +/* Start the protocol. */ + +boolean +ffstart (qdaemon, pzlog) + struct sdaemon *qdaemon; + char **pzlog; +{ + *pzlog = NULL; + + cFsent_data = 0; + cFsent_bytes = 0; + cFrec_data = 0; + cFrec_bytes = 0; + cFsend_retries = 0; + cFrec_retries = 0; + + /* Use XON/XOFF handshaking. */ + if (! fconn_set (qdaemon->qconn, PARITYSETTING_DEFAULT, + STRIPSETTING_SEVENBITS, XONXOFF_ON)) + return FALSE; + + /* We sleep to allow the other side to reset the terminal; this is + what Mr. Beertema's code does. */ + usysdep_sleep (2); + + return TRUE; +} + +/* Shutdown the protocol. */ + +/*ARGSIGNORED*/ +boolean +ffshutdown (qdaemon) + struct sdaemon *qdaemon; +{ + xfree ((pointer) zFbuf); + zFbuf = NULL; + ulog (LOG_NORMAL, + "Protocol 'f': sent %ld bytes for %ld, received %ld bytes for %ld", + cFsent_bytes, cFsent_data, cFrec_bytes, cFrec_data); + if (cFsend_retries != 0 || cFrec_retries != 0) + ulog (LOG_NORMAL, "Protocol 'f' file retries: %ld sending, %ld receiving", + cFsend_retries, cFrec_retries); + cFtimeout = 120; + cFmaxretries = 2; + return TRUE; +} + +/* Send a command string. We just send the string followed by a carriage + return. */ + +/*ARGSUSED*/ +boolean +ffsendcmd (qdaemon, z, ilocal, iremote) + struct sdaemon *qdaemon; + const char *z; + int ilocal; + int iremote; +{ + size_t clen; + char *zalc; + boolean fret; + + DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "ffsendcmd: Sending command \"%s\"", z); + + clen = strlen (z); + zalc = zbufalc (clen + 2); + memcpy (zalc, z, clen); + zalc[clen] = '\r'; + zalc[clen + 1] = '\0'; + fret = fsend_data (qdaemon->qconn, zalc, clen + 1, TRUE); + ubuffree (zalc); + return fret; +} + +/* Get space to be filled with data. We allocate the space from the + heap. */ + +/*ARGSIGNORED*/ +char * +zfgetspace (qdaemon, pclen) + struct sdaemon *qdaemon; + size_t *pclen; +{ + *pclen = CFBUFSIZE; + if (zFbuf == NULL) + zFbuf = (char *) xmalloc (CFBUFSIZE); + return zFbuf; +} + +/* Send out a data packet. We have to encode the data into seven bits + and accumulate a checksum. */ + +/*ARGSIGNORED*/ +boolean +ffsenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos) + struct sdaemon *qdaemon; + char *zdata; + size_t cdata; + int ilocal; + int iremote; + long ipos; +{ + char ab[CFBUFSIZE * 2]; + char *ze; + register unsigned int itmpchk; + + cFsent_data += cdata; + + ze = ab; + itmpchk = iFcheck; + while (cdata-- > 0) + { + register int b; + + /* Rotate the checksum left. */ + if ((itmpchk & 0x8000) == 0) + itmpchk <<= 1; + else + { + itmpchk <<= 1; + ++itmpchk; + } + + /* Add the next byte into the checksum. */ + b = *zdata++ & 0xff; + itmpchk += b; + + /* Encode the byte. */ + if (b <= 0177) + { + if (b <= 037) + { + *ze++ = '\172'; + *ze++ = (char) (b + 0100); + } + else if (b <= 0171) + *ze++ = (char) b; + else + { + *ze++ = '\173'; + *ze++ = (char) (b - 0100); + } + } + else + { + if (b <= 0237) + { + *ze++ = '\174'; + *ze++ = (char) (b - 0100); + } + else if (b <= 0371) + { + *ze++ = '\175'; + *ze++ = (char) (b - 0200); + } + else + { + *ze++ = '\176'; + *ze++ = (char) (b - 0300); + } + } + } + + iFcheck = itmpchk; + + cFsent_bytes += ze - ab; + + /* Passing FALSE tells fsend_data not to bother looking for incoming + information, since we really don't expect any. */ + return fsend_data (qdaemon->qconn, ab, (size_t) (ze - ab), FALSE); +} + +/* Process data and return the amount of data we are looking for in + *pcneed. The 'f' protocol doesn't really reveal this, but when + transferring file we know that we need at least seven characters + for the checksum. */ + +static boolean +ffprocess_data (qdaemon, pfexit, pcneed) + struct sdaemon *qdaemon; + boolean *pfexit; + size_t *pcneed; +{ + int i; + register unsigned int itmpchk; + + *pfexit = FALSE; + if (pcneed != NULL) + *pcneed = 1; + + if (! fFfile) + { + /* A command continues until a '\r' character, which we turn + into '\0' before calling fgot_data. */ + while (iPrecstart != iPrecend) + { + for (i = iPrecstart; i < CRECBUFLEN && i != iPrecend; i++) + { + if (abPrecbuf[i] == '\r') + { + int istart; + + DEBUG_MESSAGE1 (DEBUG_PROTO, + "ffprocess_data: Got %d command bytes", + i - iPrecstart + 1); + + abPrecbuf[i] = '\0'; + istart = iPrecstart; + iPrecstart = (i + 1) % CRECBUFLEN; + if (pcneed != NULL) + *pcneed = 0; + return fgot_data (qdaemon, abPrecbuf + istart, + (size_t) (i - istart + 1), + (const char *) NULL, (size_t) 0, + -1, -1, (long) -1, TRUE, pfexit); + } + } + + DEBUG_MESSAGE1 (DEBUG_PROTO, + "ffprocess_data: Got %d command bytes", + i - iPrecstart); + + if (! fgot_data (qdaemon, abPrecbuf + iPrecstart, + (size_t) (i - iPrecstart), + (const char *) NULL, (size_t) 0, + -1, -1, (long) -1, TRUE, pfexit)) + return FALSE; + + iPrecstart = i % CRECBUFLEN; + } + + return TRUE; + } + + /* Here the data is destined for a file, and we must decode it. */ + + itmpchk = iFcheck; + + while (iPrecstart != iPrecend) + { + char *zstart, *zto, *zfrom; + int c; + + zto = zfrom = zstart = abPrecbuf + iPrecstart; + + c = iPrecend - iPrecstart; + if (c < 0) + c = CRECBUFLEN - iPrecstart; + + while (c-- != 0) + { + int b; + + b = *zfrom++ & 0xff; + if (b < 040 || b > 0176) + { + ulog (LOG_ERROR, "Illegal byte %d", b); + continue; + } + + /* Characters >= 0172 are always special characters. The + only legal pair of consecutive special characters + are 0176 0176 which immediately precede the four + digit checksum. */ + if (b >= 0172) + { + if (bFspecial != 0) + { + if (bFspecial != 0176 || b != 0176) + { + ulog (LOG_ERROR, "Illegal bytes %d %d", + bFspecial, b); + bFspecial = 0; + continue; + } + + /* Pass any initial data. */ + if (zto != zstart) + { + /* Don't count the checksum in the received bytes. */ + cFrec_bytes += zfrom - zstart - 2; + cFrec_data += zto - zstart; + if (! fgot_data (qdaemon, zstart, + (size_t) (zto - zstart), + (const char *) NULL, (size_t) 0, + -1, -1, (long) -1, TRUE, pfexit)) + return FALSE; + } + + /* The next characters we want to read are the + checksum, so skip the second 0176. */ + iPrecstart = (iPrecstart + zfrom - zstart) % CRECBUFLEN; + + iFcheck = itmpchk; + + /* Tell fgot_data that we've read the entire file by + passing 0 length data. This will wind up calling + fffile to verify the checksum. We set *pcneed to + 0 because we don't want to read any more data + from the port, since we may have already read the + checksum. */ + if (pcneed != NULL) + *pcneed = 0; + return fgot_data (qdaemon, (const char *) NULL, + (size_t) 0, (const char *) NULL, + (size_t) 0, -1, -1, (long) -1, + TRUE, pfexit); + } + + /* Here we have encountered a special character that + does not follow another special character. */ + bFspecial = (char) b; + } + else + { + int bnext; + + /* Here we have encountered a nonspecial character. */ + + switch (bFspecial) + { + default: + bnext = b; + break; + case 0172: + bnext = b - 0100; + break; + case 0173: + case 0174: + bnext = b + 0100; + break; + case 0175: + bnext = b + 0200; + break; + case 0176: + bnext = b + 0300; + break; + } + + *zto++ = (char) bnext; + bFspecial = 0; + + /* Rotate the checksum left. */ + if ((itmpchk & 0x8000) == 0) + itmpchk <<= 1; + else + { + itmpchk <<= 1; + ++itmpchk; + } + + /* Add the next byte into the checksum. */ + itmpchk += bnext; + } + } + + if (zto != zstart) + { + DEBUG_MESSAGE1 (DEBUG_PROTO, + "ffprocess_data: Got %d bytes", + zto - zstart); + + cFrec_data += zto - zstart; + if (! fgot_data (qdaemon, zstart, (size_t) (zto - zstart), + (const char *) NULL, (size_t) 0, + -1, -1, (long) -1, TRUE, pfexit)) + return FALSE; + } + + cFrec_bytes += zfrom - zstart; + + iPrecstart = (iPrecstart + zfrom - zstart) % CRECBUFLEN; + } + + iFcheck = itmpchk; + + if (pcneed != NULL) + { + /* At this point we may have seen the first 0176 in the checksum + but not the second. The checksum is at least seven + characters long (0176 0176 a b c d \r). This won't help + much, but reading seven characters is a lot better than + reading two, which is what I saw in a 2400 baud log file. */ + if (bFspecial == 0176) + *pcneed = 6; + else + *pcneed = 7; + } + + return TRUE; +} + +/* Wait for data to come in and process it until we've finished a + command or a file. */ + +boolean +ffwait (qdaemon) + struct sdaemon *qdaemon; +{ + while (TRUE) + { + boolean fexit; + size_t cneed, crec; + + if (! ffprocess_data (qdaemon, &fexit, &cneed)) + return FALSE; + if (fexit) + return TRUE; + + if (cneed > 0) + { + /* We really want to do something like get all available + characters, then sleep for half a second and get all + available characters again, and keep this up until we + don't get anything after sleeping. */ + if (! freceive_data (qdaemon->qconn, cneed, &crec, cFtimeout, TRUE)) + return FALSE; + if (crec == 0) + { + ulog (LOG_ERROR, "Timed out waiting for data"); + return FALSE; + } + } + } +} + +/* File level operations. Reset the checksums when starting to send + or receive a file, and output the checksum when we've finished + sending a file. */ + +/*ARGSUSED*/ +boolean +fffile (qdaemon, qtrans, fstart, fsend, cbytes, pfhandled) + struct sdaemon *qdaemon; + struct stransfer *qtrans; + boolean fstart; + boolean fsend; + long cbytes; + boolean *pfhandled; +{ + DEBUG_MESSAGE3 (DEBUG_PROTO, "fffile: fstart %s; fsend %s; fFacked %s", + fstart ? "true" : "false", fsend ? "true" : "false", + fFacked ? "true" : "false"); + + *pfhandled = FALSE; + + if (fstart) + { + iFcheck = 0xffff; + cFretries = 0; + fFacked = FALSE; + if (! fsend) + { + bFspecial = 0; + fFfile = TRUE; + } + return TRUE; + } + else + { + struct sfinfo *qinfo; + + /* We need to handle the checksum and the acknowledgement. If + we get a successful ACK, we set fFacked to TRUE and call the + send or receive function by hand. This will wind up calling + here again, so if fFacked is TRUE we just return out and let + the send or receive function do whatever it does. This is a + bit of a hack. */ + if (fFacked) + { + fFacked = FALSE; + return TRUE; + } + + if (fsend) + { + char ab[sizeof "\176\176ABCD\r"]; + + /* Send the final checksum. */ + sprintf (ab, "\176\176%04x\r", iFcheck & 0xffff); + if (! fsend_data (qdaemon->qconn, ab, (size_t) 7, TRUE)) + return FALSE; + + /* Now wait for the acknowledgement. */ + fFfile = FALSE; + qinfo = (struct sfinfo *) xmalloc (sizeof (struct sfinfo)); + qinfo->psendfn = qtrans->psendfn; + qinfo->precfn = qtrans->precfn; + qinfo->pinfo = qtrans->pinfo; + qtrans->psendfn = NULL; + qtrans->precfn = ffawait_ack; + qtrans->pinfo = (pointer) qinfo; + qtrans->fcmd = TRUE; + + *pfhandled = TRUE; + + return fqueue_receive (qdaemon, qtrans); + } + else + { + /* Wait for the checksum. */ + fFfile = FALSE; + qinfo = (struct sfinfo *) xmalloc (sizeof (struct sfinfo)); + qinfo->psendfn = qtrans->psendfn; + qinfo->precfn = qtrans->precfn; + qinfo->pinfo = qtrans->pinfo; + qtrans->psendfn = NULL; + qtrans->precfn = ffawait_cksum; + qtrans->pinfo = (pointer) qinfo; + qtrans->fcmd = TRUE; + + *pfhandled = TRUE; + + return fqueue_receive (qdaemon, qtrans); + } + } +} + +/* Wait for the ack after sending a file and the checksum. */ + +static boolean +ffawait_ack (qtrans, qdaemon, zdata, cdata) + struct stransfer *qtrans; + struct sdaemon *qdaemon; + const char *zdata; + size_t cdata; +{ + struct sfinfo *qinfo = (struct sfinfo *) qtrans->pinfo; + + qtrans->precfn = NULL; + + /* An R means to retry sending the file. */ + if (*zdata == 'R') + { + if (! ffileisopen (qtrans->e)) + { + ulog (LOG_ERROR, "Request to resent non-file"); + return FALSE; + } + + ++cFretries; + if (cFretries > cFmaxretries) + { + ulog (LOG_ERROR, "Too many retries"); + return FALSE; + } + + ulog (LOG_NORMAL, "Resending file"); + if (! ffilerewind (qtrans->e)) + { + ulog (LOG_ERROR, "rewind: %s", strerror (errno)); + return FALSE; + } + qtrans->ipos = (long) 0; + + iFcheck = 0xffff; + ++cFsend_retries; + + qtrans->psendfn = qinfo->psendfn; + qtrans->precfn = qinfo->precfn; + qtrans->pinfo = qinfo->pinfo; + xfree ((pointer) qinfo); + qtrans->fsendfile = TRUE; + + return fqueue_send (qdaemon, qtrans); + } + + if (*zdata != 'G') + { + DEBUG_MESSAGE1 (DEBUG_PROTO, "fffile: Got \"%s\"", zdata); + ulog (LOG_ERROR, "File send failed"); + return FALSE; + } + + qtrans->psendfn = qinfo->psendfn; + qtrans->precfn = qinfo->precfn; + qtrans->pinfo = qinfo->pinfo; + xfree ((pointer) qinfo); + + /* Now call the send function by hand after setting fFacked to TRUE. + Since fFacked is true fffile will simply return out, and the send + function can do whatever it what was going to do. */ + fFacked = TRUE; + return (*qtrans->psendfn) (qtrans, qdaemon); +} + +/* This function is called when the checksum arrives. */ + +/*ARGSUSED*/ +static boolean +ffawait_cksum (qtrans, qdaemon, zdata, cdata) + struct stransfer *qtrans; + struct sdaemon *qdaemon; + const char *zdata; + size_t cdata; +{ + struct sfinfo *qinfo = (struct sfinfo *) qtrans->pinfo; + unsigned int icheck; + + qtrans->precfn = NULL; + + if (! isxdigit (zdata[0]) + || ! isxdigit (zdata[1]) + || ! isxdigit (zdata[2]) + || ! isxdigit (zdata[3]) + || zdata[4] != '\0') + { + ulog (LOG_ERROR, "Bad checksum format"); + xfree (qtrans->pinfo); + return FALSE; + } + + icheck = (unsigned int) strtol ((char *) zdata, (char **) NULL, 16); + + if (icheck != (iFcheck & 0xffff)) + { + DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, + "Checksum failed; calculated 0x%x, got 0x%x", + iFcheck & 0xffff, icheck); + + if (! ffileisopen (qtrans->e)) + { + ulog (LOG_ERROR, "Failed to get non-file"); + return FALSE; + } + + ++cFretries; + if (cFretries > cFmaxretries) + { + ulog (LOG_ERROR, "Too many retries"); + qinfo->bsend = 'Q'; + } + else + { + ulog (LOG_NORMAL, "File being resent"); + + /* This bit of code relies on the receive code setting + qtrans->s.ztemp to the full name of the temporary file + being used. */ + qtrans->e = esysdep_truncate (qtrans->e, qtrans->s.ztemp); + if (! ffileisopen (qtrans->e)) + return FALSE; + qtrans->ipos = (long) 0; + + iFcheck = 0xffff; + bFspecial = 0; + fFfile = TRUE; + ++cFrec_retries; + + /* Send an R to tell the other side to resend the file. */ + qinfo->bsend = 'R'; + } + } + else + { + /* Send a G to tell the other side the file was received + correctly. */ + qinfo->bsend = 'G'; + } + + qtrans->psendfn = ffsend_ack; + + return fqueue_send (qdaemon, qtrans); +} + +/* Send the acknowledgement, and then possible wait for the resent + file. */ + +static boolean +ffsend_ack (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + struct sfinfo *qinfo = (struct sfinfo *) qtrans->pinfo; + char ab[2]; + + ab[0] = qinfo->bsend; + ab[1] = '\0'; + if (! ffsendcmd (qdaemon, ab, 0, 0)) + return FALSE; + + qtrans->psendfn = qinfo->psendfn; + qtrans->precfn = qinfo->precfn; + qtrans->pinfo = qinfo->pinfo; + xfree ((pointer) qinfo); + + if (ab[0] == 'Q') + return FALSE; + if (ab[0] == 'R') + { + qtrans->frecfile = TRUE; + return fqueue_receive (qdaemon, qtrans); + } + + fFacked = TRUE; + return (*qtrans->precfn) (qtrans, qdaemon, (const char *) NULL, + (size_t) 0); +} diff --git a/gnu/libexec/uucp/uucico/protg.c b/gnu/libexec/uucp/uucico/protg.c new file mode 100644 index 0000000000..e9eaeb0490 --- /dev/null +++ b/gnu/libexec/uucp/uucico/protg.c @@ -0,0 +1,1933 @@ +/* protg.c + The 'g' protocol. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char protg_rcsid[] = "$Id: protg.c,v 1.1 1993/08/04 19:36:20 jtc Exp $"; +#endif + +#include +#include + +#include "uudefs.h" +#include "uuconf.h" +#include "conn.h" +#include "trans.h" +#include "system.h" +#include "prot.h" + +/* Each 'g' protocol packet begins with six bytes. They are: + + + + is the ASCII DLE character (^P or '\020'). + if 1 <= <= 8, the packet is followed by 2 ** (k + 4) bytes of data; + if == 9, these six bytes are a complete control packet; + other value of are illegal. + is the low byte of a checksum. + is the high byte of a checksum. + is a control byte (see below). + is ^ ^ ^ . + + The control byte is divided into three bitfields: + + t t x x x y y y + + The two bit field tt is the packet type. + The three bit field xxx is the control type for a control packet, or + the sequence number for a data packet. + The three bit field yyy is a value for a control packet, or the + sequence number of the last packet received for a data packet. + + For all successfully recieved packets, the control byte is stored + into iGpacket_control. */ + +/* Names for the bytes in the frame header. */ +#define IFRAME_DLE (0) +#define IFRAME_K (1) +#define IFRAME_CHECKLOW (2) +#define IFRAME_CHECKHIGH (3) +#define IFRAME_CONTROL (4) +#define IFRAME_XOR (5) + +/* Length of the frame header. */ +#define CFRAMELEN (6) + +/* Macros to break apart the control bytes. */ +#define CONTROL_TT(b) ((int)(((b) >> 6) & 03)) +#define CONTROL_XXX(b) ((int)(((b) >> 3) & 07)) +#define CONTROL_YYY(b) ((int)((b) & 07)) + +/* DLE value. */ +#define DLE ('\020') + +/* Get the length of a packet given a pointer to the header. */ +#define CPACKLEN(z) ((size_t) (1 << ((z)[IFRAME_K] + 4))) + +/* field value for a control message. */ +#define KCONTROL (9) + +/* Get the next sequence number given a sequence number. */ +#define INEXTSEQ(i) ((i + 1) & 07) + +/* Compute i1 - i2 modulo 8. */ +#define CSEQDIFF(i1, i2) (((i1) + 8 - (i2)) & 07) + +/* Packet types. These are from the tt field. + CONTROL -- control packet + ALTCHAN -- alternate channel; not used by UUCP + DATA -- full data segment + SHORTDATA -- less than full data segment (all the bytes specified by + the packet length are always transferred). Let be the number + of bytes in the data segment not to be used. If <= 0x7f, the first + byte of the data segment is and the data follows. If > 0x7f, + the first byte of the data segment is 0x80 | ( & 0x7f), the second + byte of the data segment is >> 7, and the data follows. The + maximum possible data segment size is 2**12, so this handles all + possible cases. */ +#define CONTROL (0) +#define ALTCHAN (1) +#define DATA (2) +#define SHORTDATA (3) + +/* Control types. These are from the xxx field if the type (tt field) + is CONTROL. + + CLOSE -- close the connection + RJ -- reject; packet yyy last to be received correctly + SRJ -- selective reject; reject only packet yyy (not used by UUCP) + RR -- receiver ready; packet yyy received correctly + INITC -- third step of initialization; yyy holds window size + INITB -- second step of initialization; yyy holds maximum value - 1 + INITA -- first step of initialization; yyy holds window size. + + The yyy value for RR is the same as the yyy value for an ordinary + data packet. */ +#define CLOSE (1) +#define RJ (2) +#define SRJ (3) +#define RR (4) +#define INITC (5) +#define INITB (6) +#define INITA (7) + +/* Maximum amount of data in a single packet. This is set by the + field in the header; the amount of data in a packet is + 2 ** ( + 4). ranges from 1 to 8. */ + +#define CMAXDATAINDEX (8) + +#define CMAXDATA (1 << (CMAXDATAINDEX + 4)) + +/* Maximum window size. */ +#define CMAXWINDOW (7) + +/* Defaults for the protocol parameters. These may all be changed by + using the ``protocol-parameter g'' command, so there is no + particular reason to change the values given here. */ + +/* The desired window size. This is what we tell the other system to + use. It must be between 1 and 7, and there's no reason to use less + than 7. Protocol parameter ``window''. */ +#define IWINDOW (7) + +/* The desired packet size. Many implementations only support 64 byte + packets. Protocol parameter ``packet-size''. */ +#define IPACKSIZE (64) + +/* The number of times to retry the exchange of INIT packets when + starting the protocol. Protocol parameter ``startup-retries''. */ +#define CSTARTUP_RETRIES (8) + +/* The timeout to use when waiting for an INIT packet when starting up + the protocol. Protocol parameter ``init-timeout''. */ +#define CEXCHANGE_INIT_TIMEOUT (10) + +/* The number of times to retry sending and waiting for a single INIT + packet when starting the protocol. This controls a single INIT + packet, while CSTARTUP_RETRIES controls how many times to try the + entire INIT sequence. Protocol parameter ``init-retries''. */ +#define CEXCHANGE_INIT_RETRIES (4) + +/* The timeout to use when waiting for a packet. Protocol parameter + ``timeout''. */ +#define CTIMEOUT (10) + +/* The number of times to retry waiting for a packet. Each time the + timeout fails we send a copy of our last data packet or a reject + message for the packet we expect from the other side, depending on + whether we are waiting for an acknowledgement or a data packet. + This is the number of times we try doing that and then waiting + again. Protocol parameter ``retries''. */ +#define CRETRIES (6) + +/* If we see more than this much unrecognized data, we drop the + connection. This must be larger than a single packet size, which + means it must be larger than 4096 (the largest possible packet + size). Protocol parameter ``garbage''. */ +#define CGARBAGE (10000) + +/* If we see more than this many protocol errors, we drop the + connection. Protocol parameter ``errors''. */ +#define CERRORS (100) + +/* Default decay rate. Each time we send or receive this many packets + succesfully, we decrement the error level by one (protocol + parameter ``error-decay''). */ +#define CERROR_DECAY (10) + +/* If this value is non-zero, it will be used as the remote window + size regardless of what the other side requested. This can be + useful for dealing with some particularly flawed packages. This + default value should always be 0, and protocol parameter + ``remote-window'' should be used for the affected systems. */ +#define IREMOTE_WINDOW (0) + +/* If this value is non-zero, it will be used as the packet size to + send to the remote system regardless of what it requested. It's + difficult to imagine any circumstances where you would want to set + this. Protocol parameter ``remote-packet-size''. */ +#define IREMOTE_PACKSIZE (0) + +/* Local variables. */ + +/* Next sequence number to send. */ +static int iGsendseq; + +/* Last sequence number that has been acked. */ +static int iGremote_ack; + +/* Last sequence number to be retransmitted. */ +static int iGretransmit_seq; + +/* Last sequence number we have received. */ +static int iGrecseq; + +/* Last sequence number we have acked. */ +static int iGlocal_ack; + +/* Window size to request (protocol parameter ``window''). */ +static int iGrequest_winsize = IWINDOW; + +/* Packet size to request (protocol parameter ``packet-size''). */ +static int iGrequest_packsize = IPACKSIZE; + +/* Remote window size (set during handshake). */ +static int iGremote_winsize; + +/* Forced remote window size (protocol parameter ``remote-window''). */ +static int iGforced_remote_winsize = IREMOTE_WINDOW; + +/* Remote segment size (set during handshake). This is one less than + the value in a packet header. */ +static int iGremote_segsize; + +/* Remote packet size (set based on iGremote_segsize). */ +static size_t iGremote_packsize; + +/* Forced remote packet size (protocol parameter + ``remote-packet-size''). */ +static int iGforced_remote_packsize = IREMOTE_PACKSIZE; + +/* Recieved control byte. */ +static int iGpacket_control; + +/* Number of times to retry the initial handshake. Protocol parameter + ``startup-retries''. */ +static int cGstartup_retries = CSTARTUP_RETRIES; + +/* Number of times to retry sending an initial control packet. + Protocol parameter ``init-retries''. */ +static int cGexchange_init_retries = CEXCHANGE_INIT_RETRIES; + +/* Timeout (seconds) for receiving an initial control packet. + Protocol parameter ``init-timeout''. */ +static int cGexchange_init_timeout = CEXCHANGE_INIT_TIMEOUT; + +/* Timeout (seconds) for receiving a data packet. Protocol parameter + ``timeout''. */ +static int cGtimeout = CTIMEOUT; + +/* Maximum number of timeouts when receiving a data packet or + acknowledgement. Protocol parameter ``retries''. */ +static int cGretries = CRETRIES; + +/* Amount of garbage data we are prepared to see before giving up. + Protocol parameter ``garbage''. */ +static int cGgarbage_data = CGARBAGE; + +/* Maximum number of errors we are prepared to see before giving up. + Protocol parameter ``errors''. */ +static int cGmax_errors = CERRORS; + +/* Each time we receive this many packets succesfully, we decrement + the error level by one (protocol parameter ``error-decay''). */ +static int cGerror_decay = CERROR_DECAY; + +/* Whether to use shorter packets when possible. Protocol parameter + ``short-packets''. */ +static boolean fGshort_packets = TRUE; + +/* Protocol parameter commands. */ +struct uuconf_cmdtab asGproto_params[] = +{ + { "window", UUCONF_CMDTABTYPE_INT, (pointer) &iGrequest_winsize, NULL }, + { "packet-size", UUCONF_CMDTABTYPE_INT, (pointer) &iGrequest_packsize, + NULL }, + { "startup-retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGstartup_retries, + NULL }, + { "init-timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cGexchange_init_timeout, + NULL }, + { "init-retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGexchange_init_retries, + NULL }, + { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cGtimeout, NULL }, + { "retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGretries, NULL }, + { "garbage", UUCONF_CMDTABTYPE_INT, (pointer) &cGgarbage_data, NULL }, + { "errors", UUCONF_CMDTABTYPE_INT, (pointer) &cGmax_errors, NULL }, + { "error-decay", UUCONF_CMDTABTYPE_INT, (pointer) &cGerror_decay, NULL }, + { "remote-window", UUCONF_CMDTABTYPE_INT, + (pointer) &iGforced_remote_winsize, NULL }, + { "remote-packet-size", UUCONF_CMDTABTYPE_INT, + (pointer) &iGforced_remote_packsize, NULL }, + { "short-packets", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fGshort_packets, + NULL }, + { NULL, 0, NULL, NULL } +}; + +/* Statistics. */ + +/* Number of packets we have sent. */ +static long cGsent_packets; + +/* Number of packets we have resent (these are not included in + cGsent_packets). */ +static long cGresent_packets; + +/* Number of packets we have delayed sending (these should not be + counted in cGresent_packets). */ +static long cGdelayed_packets; + +/* Number of packets we have received. */ +static long cGrec_packets; + +/* Number of packets rejected because the header was bad. */ +static long cGbad_hdr; + +/* Number of packets rejected because the checksum was bad. */ +static long cGbad_checksum; + +/* Number of packets received out of order. */ +static long cGbad_order; + +/* Number of packets rejected by receiver (number of RJ packets + received). */ +static long cGremote_rejects; + +/* The error level. This is the total number of errors as adjusted by + cGerror_decay. */ +static long cGerror_level; + +/* Each time we send an RJ, we can expect several out of order of + packets, because the other side will probably have sent a full + window by the time it sees the RJ. This variable keeps track of + the number of out of order packets we expect to see. We don't + count expected out of order packets against the error level. This + is reset to 0 when an in order packet is received. */ +static int cGexpect_bad_order; + +#if DEBUG > 1 +/* Control packet names used for debugging. */ +static const char * const azGcontrol[] = +{"?0?", "CLOSE", "RJ", "SRJ", "RR", "INITC", "INITB", "INITA"}; +#endif + +/* Local functions. */ +static boolean fgexchange_init P((struct sdaemon *qdaemon, int ictl, + int ival, int *piset)); +static boolean fgsend_control P((struct sdaemon *qdaemon, int ictl, + int ival)); +static char *zgadjust_ack P((int iseq)); +static boolean fgwait_for_packet P((struct sdaemon *qdaemon, + boolean freturncontrol, int ctimeout, + int cretries)); +static boolean fgsend_acks P((struct sdaemon *qdaemon)); +static boolean fggot_ack P((struct sdaemon *qdaemon, int iack)); +static boolean fgprocess_data P((struct sdaemon *qdaemon, boolean fdoacks, + boolean freturncontrol, + boolean *pfexit, size_t *pcneed, + boolean *pffound)); +static boolean fginit_sendbuffers P((boolean fallocate)); +static boolean fgcheck_errors P((struct sdaemon *qdaemon)); +static int igchecksum P((const char *zdata, size_t clen)); +static int igchecksum2 P((const char *zfirst, size_t cfirst, + const char *zsecond, size_t csecond)); + +/* Start the protocol. This requires a three way handshake. Both sides + must send and receive an INITA packet, an INITB packet, and an INITC + packet. The INITA and INITC packets contain the window size, and the + INITB packet contains the packet size. */ + +boolean +fgstart (qdaemon, pzlog) + struct sdaemon *qdaemon; + char **pzlog; +{ + int iseg; + int i; + boolean fgota, fgotb; + + *pzlog = NULL; + + /* The 'g' protocol requires a full eight bit interface. */ + if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE, + STRIPSETTING_EIGHTBITS, XONXOFF_OFF)) + return FALSE; + + iGsendseq = 1; + iGremote_ack = 0; + iGretransmit_seq = -1; + iGrecseq = 0; + iGlocal_ack = 0; + cGsent_packets = 0; + cGresent_packets = 0; + cGdelayed_packets = 0; + cGrec_packets = 0; + cGbad_hdr = 0; + cGbad_checksum = 0; + cGbad_order = 0; + cGremote_rejects = 0; + cGerror_level = 0; + cGexpect_bad_order = 0; + + /* We must determine the segment size based on the packet size + which may have been modified by a protocol parameter command. + A segment size of 2^n is passed as n - 5. */ + i = iGrequest_packsize; + iseg = -1; + while (i > 0) + { + ++iseg; + i >>= 1; + } + iseg -= 5; + if (iseg < 0 || iseg > 7) + { + ulog (LOG_ERROR, "Illegal packet size %d for '%c' protocol", + iGrequest_packsize, qdaemon->qproto->bname); + iseg = 1; + } + + fgota = FALSE; + fgotb = FALSE; + for (i = 0; i < cGstartup_retries; i++) + { + if (fgota) + { + if (! fgsend_control (qdaemon, INITA, iGrequest_winsize)) + return FALSE; + } + else + { + if (! fgexchange_init (qdaemon, INITA, iGrequest_winsize, + &iGremote_winsize)) + continue; + } + fgota = TRUE; + + if (fgotb) + { + if (! fgsend_control (qdaemon, INITB, iseg)) + return FALSE; + } + else + { + if (! fgexchange_init (qdaemon, INITB, iseg, &iGremote_segsize)) + continue; + } + fgotb = TRUE; + + if (! fgexchange_init (qdaemon, INITC, iGrequest_winsize, + &iGremote_winsize)) + continue; + + /* We have succesfully connected. Determine the remote packet + size. */ + iGremote_packsize = 1 << (iGremote_segsize + 5); + + /* If the user requested us to force specific remote window and + packet sizes, do so now. */ + if (iGforced_remote_winsize > 0 + && iGforced_remote_winsize <= CMAXWINDOW) + iGremote_winsize = iGforced_remote_winsize; + + if (iGforced_remote_packsize >= 32 + && iGforced_remote_packsize <= 4096) + { + /* Force the value to a power of two. */ + i = iGforced_remote_packsize; + iseg = -1; + while (i > 0) + { + ++iseg; + i >>= 1; + } + iGremote_packsize = 1 << iseg; + iGremote_segsize = iseg - 5; + } + + /* Set up packet buffers to use. We don't do this until we know + the maximum packet size we are going to send. */ + if (! fginit_sendbuffers (TRUE)) + return FALSE; + + *pzlog = zbufalc (sizeof "protocol '' packet size window " + 50); + sprintf (*pzlog, "protocol '%c' packet size %d window %d", + qdaemon->qproto->bname, (int) iGremote_packsize, + (int) iGremote_winsize); + + return TRUE; + } + + DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL, + "fgstart: Protocol startup failed"); + + return FALSE; +} + +/* The 'G' protocol is identical to the 'g' protocol, except that + short packets are never supported. */ + +boolean +fbiggstart (qdaemon, pzlog) + struct sdaemon *qdaemon; + char **pzlog; +{ + fGshort_packets = FALSE; + return fgstart (qdaemon, pzlog); +} + +/* Exchange initialization messages with the other system. + + A problem: + + We send INITA; it gets received + We receive INITA + We send INITB; it gets garbled + We receive INITB + + We have seen and sent INITB, so we start to send INITC. The other + side as sent INITB but not seen it, so it times out and resends + INITB. We will continue sending INITC and the other side will + continue sending INITB until both sides give up and start again + with INITA. + + It might seem as though if we are sending INITC and receive INITB, + we should resend our INITB, but this could cause infinite echoing + of INITB on a long-latency line. Rather than risk that, I have + implemented a fast drop-back procedure. If we are sending INITB and + receive INITC, the other side has gotten ahead of us. We immediately + fail and begin again with INITA. For the other side, if we are + sending INITC and see INITA, we also immediately fail back to INITA. + + Unfortunately, this doesn't work for the other case, in which we + are sending INITB but the other side has not yet seen INITA. As + far as I can see, if this happens we just have to wait until we + time out and resend INITA. */ + +static boolean +fgexchange_init (qdaemon, ictl, ival, piset) + struct sdaemon *qdaemon; + int ictl; + int ival; + int *piset; +{ + int i; + + /* The three-way handshake should be independent of who initializes + it, but it seems that some versions of uucico assume that the + caller sends first and the callee responds. This only matters if + we are the callee and the first packet is garbled. If we send a + packet, the other side will assume that we must have seen the + packet they sent and will never time out and send it again. + Therefore, if we are the callee we don't send a packet the first + time through the loop. This can still fail, but should usually + work, and, after all, if the initialization packets are received + correctly there will be no problem no matter what we do. */ + for (i = 0; i < cGexchange_init_retries; i++) + { + long itime; + int ctimeout; + + if (qdaemon->fcaller || i > 0) + { + if (! fgsend_control (qdaemon, ictl, ival)) + return FALSE; + } + + itime = ixsysdep_time ((long *) NULL); + ctimeout = cGexchange_init_timeout; + + do + { + long inewtime; + + /* We pass 0 as the retry count to fgwait_for_packet because + we want to handle retries here and because if it retried + it would send a packet, which would be bad. */ + if (! fgwait_for_packet (qdaemon, TRUE, ctimeout, 0)) + break; + + if (CONTROL_TT (iGpacket_control) == CONTROL) + { + if (CONTROL_XXX (iGpacket_control) == ictl) + { + *piset = CONTROL_YYY (iGpacket_control); + + /* If we didn't already send our initialization + packet, send it now. */ + if (! qdaemon->fcaller && i == 0) + { + if (! fgsend_control (qdaemon, ictl, ival)) + return FALSE; + } + + return TRUE; + } + + /* If the other side is farther along than we are, + we have lost a packet. Fail immediately back to + INITA (but don't fail if we are already doing INITA, + since that would count against cStart_retries more + than it should). */ + if (CONTROL_XXX (iGpacket_control) < ictl && ictl != INITA) + return FALSE; + + /* If we are sending INITC and we receive an INITA, the other + side has failed back (we know this because we have + seen an INITB from them). Fail back ourselves to + start the whole handshake over again. */ + if (CONTROL_XXX (iGpacket_control) == INITA && ictl == INITC) + return FALSE; + + /* As a special hack, if we are sending INITC and we + receive INITB, we update the segment size from the + packet. This permits a second INITB to override the + first one. It would be nice to do this in a cleaner + way. */ + if (CONTROL_XXX (iGpacket_control) == INITB && ictl == INITC) + iGremote_segsize = CONTROL_YYY (iGpacket_control); + } + + inewtime = ixsysdep_time ((long *) NULL); + ctimeout -= inewtime - itime; + } + while (ctimeout > 0); + } + + return FALSE; +} + +/* Shut down the protocol. */ + +boolean +fgshutdown (qdaemon) + struct sdaemon *qdaemon; +{ + (void) fgsend_control (qdaemon, CLOSE, 0); + (void) fgsend_control (qdaemon, CLOSE, 0); + (void) fginit_sendbuffers (FALSE); + + /* The count of sent packets may not be accurate, because some of + them may have not been sent yet if the connection failed in the + middle (the ones that counted for cGdelayed_packets). I don't + think it's worth being precise. */ + ulog (LOG_NORMAL, + "Protocol '%c' packets: sent %ld, resent %ld, received %ld", + qdaemon->qproto->bname, cGsent_packets, + cGresent_packets - cGdelayed_packets, cGrec_packets); + if (cGbad_hdr != 0 + || cGbad_checksum != 0 + || cGbad_order != 0 + || cGremote_rejects != 0) + ulog (LOG_NORMAL, + "Errors: header %ld, checksum %ld, order %ld, remote rejects %ld", + cGbad_hdr, cGbad_checksum, cGbad_order, cGremote_rejects); + + /* Reset all the parameters to their default values, so that the + protocol parameters used for this connection do not affect the + next one. */ + iGrequest_winsize = IWINDOW; + iGrequest_packsize = IPACKSIZE; + cGstartup_retries = CSTARTUP_RETRIES; + cGexchange_init_timeout = CEXCHANGE_INIT_TIMEOUT; + cGexchange_init_retries = CEXCHANGE_INIT_RETRIES; + cGtimeout = CTIMEOUT; + cGretries = CRETRIES; + cGgarbage_data = CGARBAGE; + cGmax_errors = CERRORS; + cGerror_decay = CERROR_DECAY; + iGforced_remote_winsize = IREMOTE_WINDOW; + iGforced_remote_packsize = IREMOTE_PACKSIZE; + fGshort_packets = TRUE; + + return TRUE; +} + +/* Send a command string. We send packets containing the string until + the entire string has been sent. Each packet is full. */ + +/*ARGSUSED*/ +boolean +fgsendcmd (qdaemon, z, ilocal, iremote) + struct sdaemon *qdaemon; + const char *z; + int ilocal; + int iremote; +{ + size_t clen; + boolean fagain; + + DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fgsendcmd: Sending command \"%s\"", z); + + clen = strlen (z); + + do + { + char *zpacket; + size_t cdummy; + + zpacket = zggetspace (qdaemon, &cdummy); + + if (clen < iGremote_packsize) + { + size_t csize; + + /* If the remote packet size is larger than 64 (the default, + which may indicate an older UUCP package), try to fit + this command into a smaller packet. We still always send + a complete packet, though. */ + if (iGremote_packsize <= 64 || ! fGshort_packets) + csize = iGremote_packsize; + else + { + csize = 32; + while (csize <= clen) + csize <<= 1; + } + + memcpy (zpacket, z, clen); + bzero (zpacket + clen, csize - clen); + fagain = FALSE; + + if (! fgsenddata (qdaemon, zpacket, csize, 0, 0, (long) 0)) + return FALSE; + } + else + { + memcpy (zpacket, z, iGremote_packsize); + z += iGremote_packsize; + clen -= iGremote_packsize; + fagain = TRUE; + + if (! fgsenddata (qdaemon, zpacket, iGremote_packsize, + 0, 0, (long) 0)) + return FALSE; + } + } + while (fagain); + + return TRUE; +} + +/* We keep an array of buffers to retransmit as necessary. Rather + than waste static space on large buffer sizes, we allocate the + buffers once we know how large the other system expects them to be. + The sequence numbers used in the 'g' protocol are only three bits + long, so we allocate eight buffers and maintain a correspondence + between buffer index and sequence number. This always wastes some + buffer space, but it's easy to implement. + + We leave room at the front of the buffer for the frame header and + two additional bytes. The two extra bytes are used for short + packets, which essentially use a longer header and shorter data. + We do this to avoid moving the data. We zero out any unused bytes + before the frame, so we can locate the real header given a buffer + by finding the first non-zero byte (which will be one of the first + three bytes in the buffer). */ + +#define CSENDBUFFERS (CMAXWINDOW + 1) + +static char *azGsendbuffers[CSENDBUFFERS]; + +static boolean +fginit_sendbuffers (fallocate) + boolean fallocate; +{ + int i; + + /* Free up any remaining old buffers. */ + for (i = 0; i < CSENDBUFFERS; i++) + { + xfree ((pointer) azGsendbuffers[i]); + if (fallocate) + { + azGsendbuffers[i] = (char *) malloc (CFRAMELEN + 2 + + iGremote_packsize); + if (azGsendbuffers[i] == NULL) + return FALSE; + + /* This bzero might not seem necessary, since before we send + out each packet we zero out any non-data bytes. However, + if we receive an SRJ at the start of the conversation, we + will send out the packet before it has been set to + anything, thus sending the contents of our heap. We + avoid this by using bzero. */ + bzero (azGsendbuffers[i], CFRAMELEN + 2 + iGremote_packsize); + } + else + azGsendbuffers[i] = NULL; + } + return TRUE; +} + +/* Allocate a packet to send out. The return value of this function + must be filled in and passed to fgsenddata, or discarded. This + will ensure that the buffers and iGsendseq stay in synch. Set + *pclen to the amount of data to place in the buffer. */ + +/*ARGSUSED*/ +char * +zggetspace (qdaemon, pclen) + struct sdaemon *qdaemon; + size_t *pclen; +{ + *pclen = iGremote_packsize; + return azGsendbuffers[iGsendseq] + CFRAMELEN + 2; +} + +/* Send out a data packet. This computes the checksum, sets up the + header, and sends the packet out. The argument zdata should point + to the return value of zggetspace. */ + +/*ARGSIGNORED*/ +boolean +fgsenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos) + struct sdaemon *qdaemon; + char *zdata; + size_t cdata; + int ilocal; + int iremote; + long ipos; +{ + char *z; + int itt, iseg; + size_t csize; + int iclr1, iclr2; + unsigned short icheck; + + /* Set the initial length bytes. See the description at the definition + of SHORTDATA, above. */ + itt = DATA; + csize = iGremote_packsize; + iseg = iGremote_segsize + 1; + +#if DEBUG > 0 + if (cdata > csize) + ulog (LOG_FATAL, "fgsend_packet: Packet size too large"); +#endif + + iclr1 = -1; + iclr2 = -2; + if (cdata < csize) + { + /* If the remote packet size is larger than 64, the default, we + can assume they can handle a smaller packet as well, which + will be more efficient to send. */ + if (iGremote_packsize > 64 && fGshort_packets) + { + /* The packet size is 1 << (iseg + 4). */ + iseg = 1; + csize = 32; + while (csize < cdata) + { + csize <<= 1; + ++iseg; + } + } + + if (csize != cdata) + { + size_t cshort; + + /* We have to add bytes which indicate how short the packet + is. We do this by pushing the header backward, which we + can do because we allocated two extra bytes for this + purpose. */ + iclr2 = 0; + itt = SHORTDATA; + cshort = csize - cdata; + if (cshort <= 127) + { + --zdata; + zdata[0] = (char) cshort; + zdata[-1] = '\0'; + bzero (zdata + cdata + 1, cshort - 1); + } + else + { + zdata -= 2; + zdata[0] = (char) (0x80 | (cshort & 0x7f)); + zdata[1] = (char) (cshort >> 7); + bzero (zdata + cdata + 2, cshort - 2); + iclr1 = 0; + } + } + } + + z = zdata - CFRAMELEN; + + /* Zero out the preceding bytes, in case the last time this buffer + was used those bytes were used. We need to zero out the initial + bytes so that we can find the true start of the packet in + zgadjust_ack. */ + z[iclr1] = '\0'; + z[iclr2] = '\0'; + + z[IFRAME_DLE] = DLE; + z[IFRAME_K] = (char) iseg; + + icheck = (unsigned short) igchecksum (zdata, csize); + + /* We're just about ready to go. Wait until there is room in the + receiver's window for us to send the packet. We do this now so + that we send the correct value for the last packet received. + Note that if iGsendseq == iGremote_ack, this means that the + sequence numbers are actually 8 apart, since the packet could not + have been acknowledged before it was sent; this can happen when + the window size is 7. */ + while (iGsendseq == iGremote_ack + || CSEQDIFF (iGsendseq, iGremote_ack) > iGremote_winsize) + { + if (! fgwait_for_packet (qdaemon, TRUE, cGtimeout, cGretries)) + return FALSE; + } + + /* Ack all packets up to the next one, since the UUCP protocol + requires that all packets be acked in order. */ + while (CSEQDIFF (iGrecseq, iGlocal_ack) > 1) + { + iGlocal_ack = INEXTSEQ (iGlocal_ack); + if (! fgsend_control (qdaemon, RR, iGlocal_ack)) + return FALSE; + } + iGlocal_ack = iGrecseq; + + z[IFRAME_CONTROL] = (char) ((itt << 6) | (iGsendseq << 3) | iGrecseq); + + iGsendseq = INEXTSEQ (iGsendseq); + + icheck = ((unsigned short) + ((0xaaaa - (icheck ^ (z[IFRAME_CONTROL] & 0xff))) & 0xffff)); + z[IFRAME_CHECKLOW] = (char) (icheck & 0xff); + z[IFRAME_CHECKHIGH] = (char) (icheck >> 8); + + z[IFRAME_XOR] = (char) (z[IFRAME_K] ^ z[IFRAME_CHECKLOW] + ^ z[IFRAME_CHECKHIGH] ^ z[IFRAME_CONTROL]); + + /* If we're waiting for acks of retransmitted packets, then don't + send this packet yet. The other side may not be ready for it + yet. Instead, code in fggot_ack will send the outstanding + packets when an ack is received. */ + ++cGsent_packets; + + if (iGretransmit_seq != -1) + { + ++cGdelayed_packets; + return TRUE; + } + + DEBUG_MESSAGE2 (DEBUG_PROTO, + "fgsenddata: Sending packet %d (%d bytes)", + CONTROL_XXX (z[IFRAME_CONTROL]), cdata); + + return fsend_data (qdaemon->qconn, z, CFRAMELEN + csize, TRUE); +} + +/* Recompute the control byte and checksum of a packet so that it + includes the correct packet acknowledgement. This is called when a + packet is retransmitted to make sure the retransmission does not + confuse the other side. It returns a pointer to the start of the + packet, skipping the bytes that may be unused at the start of + azGsendbuffers[iseq]. */ + +static char * +zgadjust_ack (iseq) + int iseq; +{ + register char *z; + unsigned short icheck; + + z = azGsendbuffers[iseq]; + if (*z == '\0') + ++z; + if (*z == '\0') + ++z; + + /* If the received packet number is the same, there is nothing + to do. */ + if (CONTROL_YYY (z[IFRAME_CONTROL]) == iGrecseq) + return z; + + /* Get the old checksum. */ + icheck = (unsigned short) (((z[IFRAME_CHECKHIGH] & 0xff) << 8) + | (z[IFRAME_CHECKLOW] & 0xff)); + icheck = ((unsigned short) + (((0xaaaa - icheck) ^ (z[IFRAME_CONTROL] & 0xff)) & 0xffff)); + + /* Update the control byte. */ + z[IFRAME_CONTROL] = (char) ((z[IFRAME_CONTROL] &~ 07) | iGrecseq); + + /* Create the new checksum. */ + icheck = ((unsigned short) + ((0xaaaa - (icheck ^ (z[IFRAME_CONTROL] & 0xff))) & 0xffff)); + z[IFRAME_CHECKLOW] = (char) (icheck & 0xff); + z[IFRAME_CHECKHIGH] = (char) (icheck >> 8); + + /* Update the XOR byte. */ + z[IFRAME_XOR] = (char) (z[IFRAME_K] ^ z[IFRAME_CHECKLOW] + ^ z[IFRAME_CHECKHIGH] ^ z[IFRAME_CONTROL]); + + return z; +} + +/* Send a control packet. These are fairly simple to construct. It + seems reasonable to me that we should be able to send a control + packet at any time, even if the receive window is closed. In + particular, we don't want to delay when sending a CLOSE control + message. If I'm wrong, it can be changed easily enough. */ + +static boolean +fgsend_control (qdaemon, ixxx, iyyy) + struct sdaemon *qdaemon; + int ixxx; + int iyyy; +{ + char ab[CFRAMELEN]; + int ictl; + unsigned short icheck; + +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_PROTO) || + (FDEBUGGING (DEBUG_ABNORMAL) && ixxx != RR)) + ulog (LOG_DEBUG, "fgsend_control: Sending control %s %d", + azGcontrol[ixxx], iyyy); +#endif + + ab[IFRAME_DLE] = DLE; + ab[IFRAME_K] = KCONTROL; + + ictl = (CONTROL << 6) | (ixxx << 3) | iyyy; + icheck = (unsigned short) (0xaaaa - ictl); + ab[IFRAME_CHECKLOW] = (char) (icheck & 0xff); + ab[IFRAME_CHECKHIGH] = (char) (icheck >> 8); + + ab[IFRAME_CONTROL] = (char) ictl; + + ab[IFRAME_XOR] = (char) (ab[IFRAME_K] ^ ab[IFRAME_CHECKLOW] + ^ ab[IFRAME_CHECKHIGH] ^ ab[IFRAME_CONTROL]); + + return fsend_data (qdaemon->qconn, ab, (size_t) CFRAMELEN, TRUE); +} + +/* Wait for data to come in. This continues processing until a + complete file or command has been received. */ + +boolean +fgwait (qdaemon) + struct sdaemon *qdaemon; +{ + return fgwait_for_packet (qdaemon, FALSE, cGtimeout, cGretries); +} + +/* Get a packet. This is called when we have nothing to send, but + want to wait for a packet to come in. If freturncontrol is TRUE, + this will return after getting any control packet. Otherwise, it + will continue to receive packets until a complete file or a + complete command has been received. The timeout and the number of + retries are specified as arguments. The function returns FALSE if + an error occurs or if cretries timeouts of ctimeout seconds were + exceeded. */ + +static boolean +fgwait_for_packet (qdaemon, freturncontrol, ctimeout, cretries) + struct sdaemon *qdaemon; + boolean freturncontrol; + int ctimeout; + int cretries; +{ + int ctimeouts; + int cgarbage; + int cshort; + + ctimeouts = 0; + cgarbage = 0; + cshort = 0; + + while (TRUE) + { + boolean fexit; + size_t cneed; + boolean ffound; + size_t crec; + + if (! fgprocess_data (qdaemon, TRUE, freturncontrol, &fexit, + &cneed, &ffound)) + return FALSE; + + if (fexit) + return TRUE; + + DEBUG_MESSAGE1 (DEBUG_PROTO, + "fgwait_for_packet: Need %lu bytes", + (unsigned long) cneed); + + if (ffound) + { + ctimeouts = 0; + cgarbage = 0; + } + else + { + if (cgarbage > cGgarbage_data) + { + ulog (LOG_ERROR, "Too much unrecognized data"); + return FALSE; + } + } + + if (! freceive_data (qdaemon->qconn, cneed, &crec, ctimeout, TRUE)) + return FALSE; + + cgarbage += crec; + + if (crec != 0) + { + /* If we don't get enough data twice in a row, we may have + dropped some data and still be looking for the end of a + large packet. Incrementing iPrecstart will force + fgprocess_data to skip that packet and look through the + rest of the data. In some situations, this will be a + mistake. */ + if (crec >= cneed) + cshort = 0; + else + { + ++cshort; + if (cshort > 1) + { + iPrecstart = (iPrecstart + 1) % CRECBUFLEN; + cshort = 0; + } + } + } + else + { + /* The read timed out. If we have an unacknowledged packet, + send it again. Otherwise, send an RJ with the last + packet we received correctly. */ + ++ctimeouts; + if (ctimeouts > cretries) + { + if (cretries > 0) + ulog (LOG_ERROR, "Timed out waiting for packet"); + return FALSE; + } + + if (INEXTSEQ (iGremote_ack) != iGsendseq) + { + int inext; + char *zsend; + + inext = INEXTSEQ (iGremote_ack); + + DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL, + "fgwait_for_packet: Resending packet %d", + inext); + + ++cGresent_packets; + zsend = zgadjust_ack (inext); + if (! fsend_data (qdaemon->qconn, zsend, + CFRAMELEN + CPACKLEN (zsend), TRUE)) + return FALSE; + iGretransmit_seq = inext; + } + else + { + /* Send all pending acks first, to avoid confusing + the other side. */ + if (iGlocal_ack != iGrecseq) + { + if (! fgsend_acks (qdaemon)) + return FALSE; + } + if (! fgsend_control (qdaemon, RJ, iGrecseq)) + return FALSE; + } + } + } +} + +/* Send acks for all packets we haven't acked yet. */ + +static boolean +fgsend_acks (qdaemon) + struct sdaemon *qdaemon; +{ + while (iGlocal_ack != iGrecseq) + { + iGlocal_ack = INEXTSEQ (iGlocal_ack); + if (! fgsend_control (qdaemon, RR, iGlocal_ack)) + return FALSE; + } + return TRUE; +} + +/* Handle an ack of a packet. According to Hanrahan's paper, this + acknowledges all previous packets. If this is an ack for a + retransmitted packet, continue by resending up to two more packets + following the retransmitted one. This should recover quickly from + a line glitch, while avoiding the problem of continual + retransmission. */ + +static boolean +fggot_ack (qdaemon, iack) + struct sdaemon *qdaemon; + int iack; +{ + int inext; + char *zsend; + + /* We only decrement the error level if we are not retransmitting + packets. We want to catch a sudden downgrade in line quality as + fast as possible. */ + if (cGerror_level > 0 + && iGretransmit_seq == -1 + && cGsent_packets % cGerror_decay == 0) + --cGerror_level; + cGexpect_bad_order = 0; + + /* Each time packet 0 is acknowledged, we call uwindow_acked since a + new window has been acked. */ + if (iack < iGremote_ack) + uwindow_acked (qdaemon, FALSE); + + iGremote_ack = iack; + + if (iGretransmit_seq == -1) + return TRUE; + + inext = INEXTSEQ (iGretransmit_seq); + if (inext == iGsendseq) + iGretransmit_seq = -1; + else + { + DEBUG_MESSAGE1 (DEBUG_PROTO, + "fggot_ack: Sending packet %d", inext); + + ++cGresent_packets; + zsend = zgadjust_ack (inext); + if (! fsend_data (qdaemon->qconn, zsend, CFRAMELEN + CPACKLEN (zsend), + TRUE)) + return FALSE; + inext = INEXTSEQ (inext); + if (inext == iGsendseq) + iGretransmit_seq = -1; + else + { + DEBUG_MESSAGE1 (DEBUG_PROTO, + "fggot_ack: Sending packet %d", inext); + + ++cGresent_packets; + zsend = zgadjust_ack (inext); + if (! fsend_data (qdaemon->qconn, zsend, + CFRAMELEN + CPACKLEN (zsend), TRUE)) + return FALSE; + iGretransmit_seq = inext; + } + } + + return TRUE; +} + +/* See if we've received more than the permitted number of errors. If + we receive a bad packet, we can expect a window full (less one) of + out of order packets to follow, so we discount cGbad_order + accordingly. */ + +static boolean +fgcheck_errors (qdaemon) + struct sdaemon *qdaemon; +{ + if (cGerror_level > cGmax_errors && cGmax_errors >= 0) + { + ulog (LOG_ERROR, "Too many '%c' protocol errors", + qdaemon->qproto->bname); + return FALSE; + } + + return TRUE; +} + +/* Process the receive buffer into a data packet, if possible. All + control packets are handled here. When a data packet is received, + fgprocess_data calls fgot_data with the data; if that sets its + pfexit argument to TRUE fgprocess_data will set *pfexit to TRUE and + return TRUE. Also, if the freturncontrol argument is TRUE + fgprocess_data will set *pfexit to TRUE and return TRUE. Otherwise + fgprocess_data will continue trying to process data. If some error + occurs, fgprocess_data will return FALSE. If there is not enough + data to form a complete packet, then *pfexit will be set to FALSE, + *pcneed will be set to the number of bytes needed to form a + complete packet (unless pcneed is NULL) and fgprocess_data will + return TRUE. If this function found a data packet, and pffound is + not NULL, it will set *pffound to TRUE; this can be used to tell + valid data from an endless stream of garbage and control packets. + If fdoacks is TRUE, received packets will be acknowledged; + otherwise they must be acknowledged later. */ + +static boolean +fgprocess_data (qdaemon, fdoacks, freturncontrol, pfexit, pcneed, pffound) + struct sdaemon *qdaemon; + boolean fdoacks; + boolean freturncontrol; + boolean *pfexit; + size_t *pcneed; + boolean *pffound; +{ + *pfexit = FALSE; + if (pffound != NULL) + *pffound = FALSE; + + while (iPrecstart != iPrecend) + { + char ab[CFRAMELEN]; + int i, iget, cwant; + unsigned short ihdrcheck, idatcheck; + const char *zfirst, *zsecond; + int cfirst, csecond; + boolean fduprr; + + /* Look for the DLE which must start a packet. */ + if (abPrecbuf[iPrecstart] != DLE) + { + char *zdle; + + cfirst = iPrecend - iPrecstart; + if (cfirst < 0) + cfirst = CRECBUFLEN - iPrecstart; + + zdle = memchr (abPrecbuf + iPrecstart, DLE, (size_t) cfirst); + + if (zdle == NULL) + { + iPrecstart = (iPrecstart + cfirst) % CRECBUFLEN; + continue; + } + + /* We don't need % CRECBUFLEN here because zdle - (abPrecbuf + + iPrecstart) < cfirst <= CRECBUFLEN - iPrecstart. */ + iPrecstart += zdle - (abPrecbuf + iPrecstart); + } + + /* Get the first six bytes into ab. */ + for (i = 0, iget = iPrecstart; + i < CFRAMELEN && iget != iPrecend; + i++, iget = (iget + 1) % CRECBUFLEN) + ab[i] = abPrecbuf[iget]; + + /* If there aren't six bytes, there is no packet. */ + if (i < CFRAMELEN) + { + if (pcneed != NULL) + *pcneed = CFRAMELEN - i; + return TRUE; + } + + /* Make sure these six bytes start a packet. The check on + IFRAME_DLE is basically a debugging check, since the above + code should have ensured that it will never fail. If this is + not the start of a packet, bump iPrecstart and loop around to + look for another DLE. */ + if (ab[IFRAME_DLE] != DLE + || ab[IFRAME_K] < 1 + || ab[IFRAME_K] > 9 + || ab[IFRAME_XOR] != (ab[IFRAME_K] ^ ab[IFRAME_CHECKLOW] + ^ ab[IFRAME_CHECKHIGH] ^ ab[IFRAME_CONTROL]) + || CONTROL_TT (ab[IFRAME_CONTROL]) == ALTCHAN) + { + ++cGbad_hdr; + ++cGerror_level; + + DEBUG_MESSAGE4 (DEBUG_PROTO | DEBUG_ABNORMAL, + "fgprocess_data: Bad header: K %d TT %d XOR byte %d calc %d", + ab[IFRAME_K] & 0xff, + CONTROL_TT (ab[IFRAME_CONTROL]), + ab[IFRAME_XOR] & 0xff, + (ab[IFRAME_K] + ^ ab[IFRAME_CHECKLOW] + ^ ab[IFRAME_CHECKHIGH] + ^ ab[IFRAME_CONTROL]) & 0xff); + + if (! fgcheck_errors (qdaemon)) + return FALSE; + + iPrecstart = (iPrecstart + 1) % CRECBUFLEN; + continue; + } + + /* The zfirst and cfirst pair point to the first set of data for + this packet; the zsecond and csecond point to the second set, + in case the packet wraps around the end of the buffer. */ + zfirst = abPrecbuf + iPrecstart + CFRAMELEN; + cfirst = 0; + zsecond = NULL; + csecond = 0; + + if (ab[IFRAME_K] == KCONTROL) + { + /* This is a control packet. It should not have any data. */ + if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL) + { + ++cGbad_hdr; + ++cGerror_level; + + DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL, + "fgprocess_data: Bad header: control packet with data"); + + if (! fgcheck_errors (qdaemon)) + return FALSE; + + iPrecstart = (iPrecstart + 1) % CRECBUFLEN; + continue; + } + + idatcheck = (unsigned short) (0xaaaa - ab[IFRAME_CONTROL]); + cwant = 0; + } + else + { + int cinbuf; + unsigned short icheck; + + /* This is a data packet. It should not be type CONTROL. */ + if (CONTROL_TT (ab[IFRAME_CONTROL]) == CONTROL) + { + ++cGbad_hdr; + ++cGerror_level; + + DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL, + "fgprocess_data: Bad header: data packet is type CONTROL"); + + if (! fgcheck_errors (qdaemon)) + return FALSE; + + iPrecstart = (iPrecstart + 1) % CRECBUFLEN; + continue; + } + + cinbuf = iPrecend - iPrecstart; + if (cinbuf < 0) + cinbuf += CRECBUFLEN; + cinbuf -= CFRAMELEN; + + /* Make sure we have enough data. If we don't, wait for + more. */ + + cwant = (int) CPACKLEN (ab); + if (cinbuf < cwant) + { + if (pcneed != NULL) + *pcneed = cwant - cinbuf; + return TRUE; + } + + /* Set up the data pointers and compute the checksum. */ + if (iPrecend >= iPrecstart) + cfirst = cwant; + else + { + cfirst = CRECBUFLEN - (iPrecstart + CFRAMELEN); + if (cfirst >= cwant) + cfirst = cwant; + else if (cfirst > 0) + { + zsecond = abPrecbuf; + csecond = cwant - cfirst; + } + else + { + /* Here cfirst is non-positive, so subtracting from + abPrecbuf will actually skip the appropriate number + of bytes at the start of abPrecbuf. */ + zfirst = abPrecbuf - cfirst; + cfirst = cwant; + } + } + + if (csecond == 0) + icheck = (unsigned short) igchecksum (zfirst, (size_t) cfirst); + else + icheck = (unsigned short) igchecksum2 (zfirst, (size_t) cfirst, + zsecond, + (size_t) csecond); + + idatcheck = ((unsigned short) + (((0xaaaa - (icheck ^ (ab[IFRAME_CONTROL] & 0xff))) + & 0xffff))); + } + + ihdrcheck = (unsigned short) (((ab[IFRAME_CHECKHIGH] & 0xff) << 8) + | (ab[IFRAME_CHECKLOW] & 0xff)); + + if (ihdrcheck != idatcheck) + { + DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, + "fgprocess_data: Bad checksum: header 0x%x, data 0x%x", + ihdrcheck, idatcheck); + + ++cGbad_checksum; + ++cGerror_level; + + if (! fgcheck_errors (qdaemon)) + return FALSE; + + /* If the checksum failed for a data packet, then if it was + the one we were expecting send an RJ, otherwise ignore + it. Previously if this code got the wrong packet number + it would send an RR, but that may confuse some Telebit + modems and it doesn't help in any case since the receiver + will probably just ignore the RR as a duplicate (that's + basically what this code does). If we totally missed the + packet we will time out and send an RJ in the function + fgwait_for_packet above. */ + if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL) + { + /* Make sure we've acked everything up to this point. */ + if (iGrecseq != iGlocal_ack) + { + if (! fgsend_acks (qdaemon)) + return FALSE; + } + + /* If this is the packet we wanted, tell the sender that + it failed. */ + if (CONTROL_XXX (ab[IFRAME_CONTROL]) == INEXTSEQ (iGrecseq)) + { + if (! fgsend_control (qdaemon, RJ, iGrecseq)) + return FALSE; + cGexpect_bad_order += iGrequest_winsize - 1; + } + } + + /* We can't skip the packet data after this, because if we + have lost incoming bytes the next DLE will be somewhere + in what we thought was the packet data. */ + iPrecstart = (iPrecstart + 1) % CRECBUFLEN; + continue; + } + + /* We have a packet; remove the processed bytes from the receive + buffer. */ + iPrecstart = (iPrecstart + cwant + CFRAMELEN) % CRECBUFLEN; + + /* Store the control byte for the handshake routines. */ + iGpacket_control = ab[IFRAME_CONTROL] & 0xff; + + /* Annoyingly, some UUCP packages appear to send an RR packet + rather than an RJ packet when they want a packet to be + resent. If we get a duplicate RR, we treat it as an RJ. */ + fduprr = FALSE; + if (CONTROL_TT (ab[IFRAME_CONTROL]) == CONTROL + && CONTROL_XXX (ab[IFRAME_CONTROL]) == RR + && iGremote_ack == CONTROL_YYY (ab[IFRAME_CONTROL]) + && INEXTSEQ (iGremote_ack) != iGsendseq) + { + DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL, + "fgprocess_data: Treating duplicate RR as RJ"); + fduprr = TRUE; + } + + /* Update the received sequence number from the yyy field of a + data packet or an RR control packet. If we've been delaying + sending packets until we received an ack, this may send out + some packets. */ + if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL + || CONTROL_XXX (ab[IFRAME_CONTROL]) == RR) + { + if (! fggot_ack (qdaemon, CONTROL_YYY (ab[IFRAME_CONTROL]))) + return FALSE; + } + + /* If this isn't a control message, make sure we have received + the expected packet sequence number, acknowledge the packet + if it's the right one, and process the data. */ + if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL) + { + if (CONTROL_XXX (ab[IFRAME_CONTROL]) != INEXTSEQ (iGrecseq)) + { + /* We got the wrong packet number. */ + DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, + "fgprocess_data: Got packet %d; expected %d", + CONTROL_XXX (ab[IFRAME_CONTROL]), + INEXTSEQ (iGrecseq)); + + if (cGexpect_bad_order > 0) + --cGexpect_bad_order; + else + { + ++cGbad_order; + ++cGerror_level; + if (! fgcheck_errors (qdaemon)) + return FALSE; + } + + /* This code used to send an RR to encourage the other + side to get back in synch, but that may confuse some + Telebit modems and does little good in any case, + since the other side will probably just ignore it + anyhow (that's what this code does). */ + continue; + } + + /* We got the packet we expected. */ + ++cGrec_packets; + if (cGerror_level > 0 + && cGrec_packets % cGerror_decay == 0) + --cGerror_level; + cGexpect_bad_order = 0; + + iGrecseq = INEXTSEQ (iGrecseq); + + DEBUG_MESSAGE1 (DEBUG_PROTO, + "fgprocess_data: Got packet %d", iGrecseq); + + /* Tell the caller that we found something. */ + if (pffound != NULL) + *pffound = TRUE; + + /* If we are supposed to do acknowledgements here, send back + an RR packet. */ + if (fdoacks) + { + if (! fgsend_acks (qdaemon)) + return FALSE; + } + + /* If this is a short data packet, adjust the data pointers + and lengths. */ + if (CONTROL_TT (ab[IFRAME_CONTROL]) == SHORTDATA) + { + int cshort, cmove; + + if ((zfirst[0] & 0x80) == 0) + { + cshort = zfirst[0] & 0xff; + cmove = 1; + } + else + { + int cbyte2; + + if (cfirst > 1) + cbyte2 = zfirst[1] & 0xff; + else + cbyte2 = zsecond[0] & 0xff; + cshort = (zfirst[0] & 0x7f) + (cbyte2 << 7); + cmove = 2; + } + + DEBUG_MESSAGE1 (DEBUG_PROTO, + "fgprocess_data: Packet short by %d", + cshort); + + /* Adjust the start of the buffer for the bytes used + by the count. */ + if (cfirst > cmove) + { + zfirst += cmove; + cfirst -= cmove; + } + else + { + zfirst = zsecond + (cmove - cfirst); + cfirst = csecond - (cmove - cfirst); + csecond = 0; + } + + /* Adjust the length of the buffer for the bytes we are + not supposed to consider. */ + cshort -= cmove; + if (csecond >= cshort) + csecond -= cshort; + else + { + cfirst -= cshort - csecond; + csecond = 0; + } + +#if DEBUG > 0 + /* This should not happen, but just in case. */ + if (cfirst < 0) + cfirst = 0; +#endif + } + + if (! fgot_data (qdaemon, zfirst, (size_t) cfirst, + zsecond, (size_t) csecond, + -1, -1, (long) -1, + INEXTSEQ (iGremote_ack) == iGsendseq, + pfexit)) + return FALSE; + + /* If fgot_data told us that we were finished, get out. */ + if (*pfexit) + return TRUE; + + /* If we've been asked to return control packets, get out + now. */ + if (freturncontrol) + { + *pfexit = TRUE; + return TRUE; + } + + continue; + } + + /* Handle control messages here. */ +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_PROTO) + || (FDEBUGGING (DEBUG_ABNORMAL) + && CONTROL_XXX (ab[IFRAME_CONTROL]) != RR)) + ulog (LOG_DEBUG, "fgprocess_data: Got control %s %d", + azGcontrol[CONTROL_XXX (ab[IFRAME_CONTROL])], + CONTROL_YYY (ab[IFRAME_CONTROL])); +#endif + + switch (CONTROL_XXX (ab[IFRAME_CONTROL])) + { + case CLOSE: + /* The other side has closed the connection. */ + if (fLog_sighup) + { + ulog (LOG_ERROR, "Received unexpected CLOSE packet"); + (void) fgsend_control (qdaemon, CLOSE, 0); + } + return FALSE; + case RR: + /* Acknowledge receipt of a packet. This was already handled + above, unless we are treating it as RJ. */ + if (! fduprr) + break; + /* Fall through. */ + case RJ: + /* The other side dropped a packet. Begin retransmission with + the packet following the one acknowledged. We don't + retransmit the packets immediately, but instead wait + for the first one to be acked. This prevents us from + sending an entire window several times if we get several + RJ packets. */ + iGremote_ack = CONTROL_YYY (ab[IFRAME_CONTROL]); + iGretransmit_seq = INEXTSEQ (iGremote_ack); + if (iGretransmit_seq == iGsendseq) + iGretransmit_seq = -1; + else + { + char *zpack; + + DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, + "fgprocess_data: Remote reject: next %d resending %d", + iGsendseq, iGretransmit_seq); + + ++cGresent_packets; + ++cGremote_rejects; + ++cGerror_level; + if (! fgcheck_errors (qdaemon)) + return FALSE; + zpack = zgadjust_ack (iGretransmit_seq); + if (! fsend_data (qdaemon->qconn, zpack, + CFRAMELEN + CPACKLEN (zpack), + TRUE)) + return FALSE; + } + break; + case SRJ: + /* Selectively reject a particular packet. This is not used + by UUCP, but it's easy to support. */ + DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL, + "fgprocess_data: Selective reject of %d", + CONTROL_YYY (ab[IFRAME_CONTROL])); + { + char *zpack; + + ++cGresent_packets; + ++cGremote_rejects; + ++cGerror_level; + zpack = zgadjust_ack (CONTROL_YYY (ab[IFRAME_CONTROL])); + if (! fsend_data (qdaemon->qconn, zpack, + CFRAMELEN + CPACKLEN (zpack), + TRUE)) + return FALSE; + } + break; + case INITC: + case INITB: + case INITA: + /* Ignore attempts to reinitialize. */ + break; + } + + /* If we've been asked to return control packets, get out. */ + if (freturncontrol) + { + *pfexit = TRUE; + return TRUE; + } + + /* Loop around to look for the next packet, if any. */ + } + + /* There is no data left in the receive buffer. */ + if (pcneed != NULL) + *pcneed = CFRAMELEN; + return TRUE; +} + +/* Compute the 'g' protocol checksum. This is unfortunately rather + awkward. This is the most time consuming code in the entire + program. It's also not a great checksum, since it can be fooled + by some single bit errors. */ + +/* Sorry about this knavery, but it speeds up the VAX code + significantly. It would be better to rewrite the whole routine in + assembler. */ +#ifdef __GNUC__ +#ifdef __vax__ +#define VAX_ASM 1 +#endif +#endif + +#if VAX_ASM +#define ROTATE(i) \ + asm ("cvtwl %1,%0\n\trotl $1,%0,%0" : "=g" (i) : "g" (i)) +#else +#define ROTATE(i) i += i + ((i & 0x8000) >> 15) +#endif + +#define ITERATION \ + /* Rotate ichk1 left. */ \ + ROTATE (ichk1); \ + \ + /* The guts of the checksum. */ \ + b = BUCHAR (*z++); \ + if (b != 0) \ + { \ + ichk1 &= 0xffff; \ + ichk1 += b; \ + ichk2 += ichk1 ^ c; \ + if ((ichk1 >> 16) != 0) \ + ichk1 ^= ichk2; \ + } \ + else \ + { \ + ichk2 += ichk1 ^ c; \ + ichk1 ^= ichk2; \ + } \ + \ + --c + +static int +igchecksum (z, c) + register const char *z; + register size_t c; +{ + register unsigned long ichk1, ichk2; + + ichk1 = 0xffff; + ichk2 = 0; + + do + { + register unsigned int b; + + ITERATION; + ITERATION; + ITERATION; + ITERATION; + } + while (c > 0); + + return ichk1 & 0xffff; +} + +/* We use a separate function compute the checksum if the block is + split around the end of the receive buffer since it occurs much + less frequently and the checksum is already high up in the + profiles. These functions are almost identical, and this one + actually only has a few more instructions in the inner loop. */ + +static int +igchecksum2 (zfirst, cfirst, zsecond, csecond) + const char *zfirst; + size_t cfirst; + const char *zsecond; + size_t csecond; +{ + register unsigned long ichk1, ichk2; + register const char *z; + register size_t c; + + z = zfirst; + c = cfirst + csecond; + + ichk1 = 0xffff; + ichk2 = 0; + + do + { + register unsigned int b; + + ITERATION; + + /* If the first buffer has been finished, switch to the second. */ + --cfirst; + if (cfirst == 0) + z = zsecond; + } + while (c > 0); + + return ichk1 & 0xffff; +} diff --git a/gnu/libexec/uucp/uucico/proti.c b/gnu/libexec/uucp/uucico/proti.c new file mode 100644 index 0000000000..606b2bd94f --- /dev/null +++ b/gnu/libexec/uucp/uucico/proti.c @@ -0,0 +1,1563 @@ +/* proti.c + The 'i' protocol. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char proti_rcsid[] = "$Id: proti.c,v 1.1 1993/08/04 19:36:22 jtc Exp $"; +#endif + +#include +#include + +#include "uudefs.h" +#include "uuconf.h" +#include "conn.h" +#include "trans.h" +#include "system.h" +#include "prot.h" + +/* The 'i' protocol is a simple sliding window protocol, created by + me. It is in many ways similar to the 'g' protocol. Several ideas + are also taken from the paper ``A High-Throughput Message Transport + System'' by P. Lauder. I don't know where the paper was published, + but the author's e-mail address is piers@cs.su.oz.au. However, I + haven't adopted his main idea, which is to dispense with windows + entirely. This is because some links still do require flow control + and, more importantly, because I want to have a limit to the amount + of data I must be able to resend upon request. To reduce the costs + of window acknowledgements, I use a large window and only require + an ack at the halfway point. + + Each packet starts with a header containing the following + information: + + Intro byte 8 bits byte 1 + Packet number 5 bits byte 2 + Local channel 3 bits + Packet ack 5 bits byte 3 + Remote channel 3 bits + Packet type 3 bits bytes 4-5 + Direction 1 bit + Data length 12 bits + Header check 8 bits byte 6 + + If the data length is not 0, this is followed by the data and a 32 + bit CRC checksum. + + The following packet types are defined: + + SYNC Initialize the connection + DATA Data packet + ACK Simple acknowledgement with no data + NAK Negative acknowledgement; requests resend of single packet + SPOS Set file position + CLOSE Close the connection + */ + +/* The offsets of the bytes in the packet header. */ + +#define IHDR_INTRO (0) +#define IHDR_LOCAL (1) +#define IHDR_REMOTE (2) +#define IHDR_CONTENTS1 (3) +#define IHDR_CONTENTS2 (4) +#define IHDR_CHECK (5) + +/* Macros to set and extract values of IHDR_LOCAL and IHDR_REMOTE. */ +#define IHDRWIN_SET(iseq, ichan) (((iseq) << 3) | (ichan)) +#define IHDRWIN_GETSEQ(ival) (((ival) >> 3) & 0x1f) +#define IHDRWIN_GETCHAN(ival) ((ival) & 0x07) + +/* Macros to set and extract values of IHDR_CONTENTS fields. */ +#define IHDRCON_SET1(ttype, fcaller, cbytes) \ + (((ttype) << 5) | ((fcaller) ? (1 << 4) : 0) | (((cbytes) >> 8) & 0x0f)) +#define IHDRCON_SET2(ttype, fcaller, cbytes) ((cbytes) & 0xff) +#define THDRCON_GETTYPE(i1, i2) (((i1) >> 5) & 0x07) +#define FHDRCON_GETCALLER(i1, i2) (((i1) & (1 << 4)) != 0) +#define CHDRCON_GETBYTES(i1, i2) ((((i1) & 0x0f) << 8) | ((i2) & 0xff)) + +/* Macros for the IHDR_CHECK field. */ +#define IHDRCHECK_VAL(zhdr) \ + ((zhdr[IHDR_LOCAL] \ + ^ zhdr[IHDR_REMOTE] \ + ^ zhdr[IHDR_CONTENTS1] \ + ^ zhdr[IHDR_CONTENTS2]) \ + & 0xff) + +/* Length of the packet header. */ +#define CHDRLEN (6) + +/* Amount of space to skip between start of packet and actual data. + This is used to make the actual data longword aligned, to encourage + good performance when copying data into the buffer. */ +#define CHDRSKIPLEN (CHDRLEN + (sizeof (long) - CHDRLEN % sizeof (long))) + +/* Amount of space to skip between memory buffer and header. */ +#define CHDROFFSET (CHDRSKIPLEN - CHDRLEN) + +/* Length of the trailing checksum. */ +#define CCKSUMLEN (4) + +/* Macros to set and get the checksum. These multiply evaluate their + arguments. */ +#define ICKSUM_GET(z) \ + ((((((((unsigned long) ((z)[0] & 0xff)) << 8) \ + | (unsigned long) ((z)[1] & 0xff)) << 8) \ + | (unsigned long) ((z)[2] & 0xff)) << 8) \ + | (unsigned long) ((z)[3] & 0xff)) +#define UCKSUM_SET(z, i) \ + (void) ((z)[0] = (((i) >> 24) & 0xff), \ + (z)[1] = (((i) >> 16) & 0xff), \ + (z)[2] = (((i) >> 8) & 0xff), \ + (z)[3] = ((i) & 0xff)) + +/* The header introduction character. */ +#define IINTRO ('\007') + +/* The packet types. */ + +#define DATA (0) +#define SYNC (1) +#define ACK (2) +#define NAK (3) +#define SPOS (4) +#define CLOSE (5) + +/* Largest possible packet size (plus 1). */ +#define IMAXPACKSIZE (1 << 12) + +/* Largest possible sequence number (plus 1). */ +#define IMAXSEQ 32 + +/* Get the next sequence number given a sequence number. */ +#define INEXTSEQ(i) ((i + 1) & (IMAXSEQ - 1)) + +/* Compute i1 - i2 in sequence space (i.e., the number of packets from + i2 to i1). */ +#define CSEQDIFF(i1, i2) (((i1) + IMAXSEQ - (i2)) & (IMAXSEQ - 1)) + +/* Largest possible channel number (plus 1). */ +#define IMAXICHAN (8) + +/* Default packet size to request (protocol parameter + ``packet-size''). */ +#define IREQUEST_PACKSIZE (1024) + +/* Default window size to request (protocol parameter ``window''). */ +#define IREQUEST_WINSIZE (16) + +/* Default timeout to use when sending the SYNC packet (protocol + parameter ``sync-timeout''). */ +#define CSYNC_TIMEOUT (10) + +/* Default number of times to retry sending the SYNC packet (protocol + parameter ``sync-retries''). */ +#define CSYNC_RETRIES (6) + +/* Default timeout to use when waiting for a packet (protocol + parameter ``timeout''). */ +#define CTIMEOUT (10) + +/* Default number of times to retry sending a packet before giving up + (protocol parameter ``retries''). */ +#define CRETRIES (6) + +/* Default maximum level of errors to accept before giving up + (protocol parameter ``errors''). */ +#define CERRORS (100) + +/* Default decay rate. Each time we receive this many packets + succesfully, we decrement the error level by one (protocol + parameter ``error-decay''). */ +#define CERROR_DECAY (10) + +/* The default list of characters to avoid: XON and XOFF. This string + is processed as an escape sequence. This is 'j' protocol parameter + ``avoid''; it is defined in this file because the 'i' and 'j' + protocols share protocol parameters. */ +#define ZAVOID "\\021\\023" + +/* Local variables. */ + +/* Packet size to request (protocol parameter ``packet-size''). */ +static int iIrequest_packsize = IREQUEST_PACKSIZE; + +/* Window size to request (protocol parameter ``window''). */ +static int iIrequest_winsize = IREQUEST_WINSIZE; + +/* Remote packet size (set from SYNC packet or from + iIforced_remote_packsize). */ +static int iIremote_packsize; + +/* Size which buffers were allocated for. */ +static int iIalc_packsize; + +/* Forced remote packet size, used if non-zero (protocol parameter + ``remote-packet-size''). */ +static int iIforced_remote_packsize = 0; + +/* Remote window size (set from SYNC packet or from + iIforced_remote_winsize). */ +static int iIremote_winsize; + +/* Forced remote window size, used if non-zero (protocol parameter + ``remote-window''). */ +static int iIforced_remote_winsize = 0; + +/* Timeout to use when sending the SYNC packet (protocol + parameter ``sync-timeout''). */ +int cIsync_timeout = CSYNC_TIMEOUT; + +/* Number of times to retry sending the SYNC packet (protocol + parameter ``sync-retries''). */ +static int cIsync_retries = CSYNC_RETRIES; + +/* Timeout to use when waiting for a packet (protocol parameter + ``timeout''). */ +static int cItimeout = CTIMEOUT; + +/* Number of times to retry sending a packet before giving up + (protocol parameter ``retries''). */ +static int cIretries = CRETRIES; + +/* Maximum level of errors to accept before giving up (protocol + parameter ``errors''). */ +static int cIerrors = CERRORS; + +/* Each time we receive this many packets succesfully, we decrement + the error level by one (protocol parameter ``error-decay''). */ +static int cIerror_decay = CERROR_DECAY; + +/* The set of characters to avoid (protocol parameter ``avoid''). + This is actually part of the 'j' protocol; it is defined in this + file because the 'i' and 'j' protocols use the same protocol + parameters. */ +const char *zJavoid_parameter = ZAVOID; + +/* Routine to use when sending data. This is a hook for the 'j' + protocol. */ +static boolean (*pfIsend) P((struct sconnection *qconn, const char *zsend, + size_t csend, boolean fdoread)); + +/* Routine to use to use when reading data. This is a hook for the + 'j' protocol. */ +static boolean (*pfIreceive) P((struct sconnection *qconn, size_t cneed, + size_t *pcrec, int ctimeout, + boolean freport)); + +/* Next sequence number to send. */ +static int iIsendseq; + +/* Last sequence number received. */ +static int iIrecseq; + +/* Last sequence number we have acknowledged. */ +static int iIlocal_ack; + +/* Last sequence number remote system has acknowledged. */ +static int iIremote_ack; + +/* File position we are sending from. */ +static long iIsendpos; + +/* File position we are receiving to. */ +static long iIrecpos; + +/* TRUE if closing the connection. */ +static boolean fIclosing; + +/* Array of sent packets indexed by packet number. */ +static char *azIsendbuffers[IMAXSEQ]; + +/* Array of received packets that we aren't ready to process yet, + indexed by packet number. */ +static char *azIrecbuffers[IMAXSEQ]; + +/* For each packet sequence number, record whether we sent a NAK for + the packet. */ +static boolean afInaked[IMAXSEQ]; + +/* Number of SYNC packets received (used only to detect whether one + was received). */ +static int cIsyncs; + +/* Number of packets sent. */ +static long cIsent_packets; + +/* Number of packets received. */ +static long cIreceived_packets; + +/* Number of packets resent. */ +static long cIresent_packets; + +/* Number of bad packet headers received. */ +static long cIbad_hdr; + +/* Number of out of order packets received. */ +static long cIbad_order; + +/* Number of bad checksums received. */ +static long cIbad_cksum; + +/* Number of packets rejected by remote system. */ +static long cIremote_rejects; + +/* Protocol parameter commands. */ + +struct uuconf_cmdtab asIproto_params[] = +{ + { "packet-size", UUCONF_CMDTABTYPE_INT, (pointer) &iIrequest_packsize, + NULL }, + { "window", UUCONF_CMDTABTYPE_INT, (pointer) &iIrequest_winsize, NULL }, + { "remote-packet-size", UUCONF_CMDTABTYPE_INT, + (pointer) &iIforced_remote_packsize, NULL }, + { "remote-window", UUCONF_CMDTABTYPE_INT, + (pointer) &iIforced_remote_winsize, NULL }, + { "sync-timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cIsync_timeout, + NULL }, + { "sync-retries", UUCONF_CMDTABTYPE_INT, (pointer) &cIsync_retries, + NULL }, + { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cItimeout, NULL }, + { "retries", UUCONF_CMDTABTYPE_INT, (pointer) &cIretries, NULL }, + { "errors", UUCONF_CMDTABTYPE_INT, (pointer) &cIerrors, NULL }, + { "error-decay", UUCONF_CMDTABTYPE_INT, (pointer) &cIerror_decay, NULL }, + /* The ``avoid'' protocol parameter is part of the 'j' protocol, but + it is convenient for the 'i' and 'j' protocols to share the same + protocol parameter table. */ + { "avoid", UUCONF_CMDTABTYPE_STRING, (pointer) &zJavoid_parameter, NULL }, + { NULL, 0, NULL, NULL } +}; + +/* Local functions. */ + +static boolean finak P((struct sdaemon *qdaemon, int iseq)); +static boolean firesend P((struct sdaemon *qdaemon)); +static boolean fiwindow_wait P((struct sdaemon *qdaemon)); +static boolean fiwait_for_packet P((struct sdaemon *qdaemon, + int ctimeout, int cretries, + boolean fone, boolean *ftimedout)); +static boolean ficheck_errors P((struct sdaemon *qdaemon)); +static boolean fiprocess_data P((struct sdaemon *qdaemon, + boolean *pfexit, boolean *pffound, + size_t *pcneed)); +static boolean fiprocess_packet P((struct sdaemon *qdaemon, + const char *zhdr, + const char *zfirst, int cfirst, + const char *zsecond, int csecond, + boolean *pfexit)); + +/* The 'i' protocol start routine. The work is done in a routine + which is also called by the 'j' protocol start routine. */ + +boolean +fistart (qdaemon, pzlog) + struct sdaemon *qdaemon; + char **pzlog; +{ + return fijstart (qdaemon, pzlog, IMAXPACKSIZE, fsend_data, freceive_data); +} + +/* Start the protocol. This routine is called by both the 'i' and 'j' + protocol start routines. We keep transmitting a SYNC packet + containing the window and packet size we would like to receive + until we receive a SYNC packet from the remote system. The first + two bytes of the data contents of a SYNC packet are the maximum + packet size we want to receive (high byte, low byte), and the next + byte is the maximum window size we want to use. */ + +boolean +fijstart (qdaemon, pzlog, imaxpacksize, pfsend, pfreceive) + struct sdaemon *qdaemon; + char **pzlog; + int imaxpacksize; + boolean (*pfsend) P((struct sconnection *qconn, const char *zsend, + size_t csend, boolean fdoread)); + boolean (*pfreceive) P((struct sconnection *qconn, size_t cneed, + size_t *pcrec, int ctimeout, boolean freport)); +{ + char ab[CHDRLEN + 3 + CCKSUMLEN]; + unsigned long icksum; + int ctries; + int csyncs; + + *pzlog = NULL; + + pfIsend = pfsend; + pfIreceive = pfreceive; + + if (iIforced_remote_packsize <= 0 + || iIforced_remote_packsize >= imaxpacksize) + iIforced_remote_packsize = 0; + else + iIremote_packsize = iIforced_remote_packsize; + iIalc_packsize = 0; + if (iIforced_remote_winsize <= 0 || iIforced_remote_winsize >= IMAXSEQ) + iIforced_remote_winsize = 0; + else + iIremote_winsize = iIforced_remote_winsize; + + iIsendseq = 1; + iIrecseq = 0; + iIlocal_ack = 0; + iIremote_ack = 0; + iIsendpos = 0; + iIrecpos = 0; + fIclosing = FALSE; + + cIsent_packets = 0; + cIreceived_packets = 0; + cIresent_packets = 0; + cIbad_hdr = 0; + cIbad_order = 0; + cIbad_cksum = 0; + cIremote_rejects = 0; + + ab[IHDR_INTRO] = IINTRO; + ab[IHDR_LOCAL] = ab[IHDR_REMOTE] = IHDRWIN_SET (0, 0); + ab[IHDR_CONTENTS1] = IHDRCON_SET1 (SYNC, qdaemon->fcaller, 3); + ab[IHDR_CONTENTS2] = IHDRCON_SET2 (SYNC, qdaemon->fcaller, 3); + ab[IHDR_CHECK] = IHDRCHECK_VAL (ab); + ab[CHDRLEN + 0] = (iIrequest_packsize >> 8) & 0xff; + ab[CHDRLEN + 1] = iIrequest_packsize & 0xff; + ab[CHDRLEN + 2] = iIrequest_winsize; + icksum = icrc (ab + CHDRLEN, 3, ICRCINIT); + UCKSUM_SET (ab + CHDRLEN + 3, icksum); + + /* The static cIsyncs is incremented each time a SYNC packet is + received. */ + csyncs = cIsyncs; + ctries = 0; + + while (TRUE) + { + boolean ftimedout; + + DEBUG_MESSAGE2 (DEBUG_PROTO, + "fistart: Sending SYNC packsize %d winsize %d", + iIrequest_packsize, iIrequest_winsize); + + if (! (*pfIsend) (qdaemon->qconn, ab, CHDRLEN + 3 + CCKSUMLEN, + TRUE)) + return FALSE; + + if (fiwait_for_packet (qdaemon, cIsync_timeout, 0, FALSE, + &ftimedout)) + { + if (csyncs != cIsyncs) + break; + } + else + { + if (! ftimedout) + return FALSE; + + ++ctries; + if (ctries > cIsync_retries) + { + ulog (LOG_ERROR, "Protocol startup failed"); + return FALSE; + } + } + } + + /* We got a SYNC packet; set up packet buffers to use. */ + if (iIremote_packsize > imaxpacksize) + iIremote_packsize = imaxpacksize; + do + { + int iseq; + + for (iseq = 0; iseq < IMAXSEQ; iseq++) + { + azIrecbuffers[iseq] = NULL; + afInaked[iseq] = FALSE; + azIsendbuffers[iseq] = (char *) malloc (iIremote_packsize + + CHDRSKIPLEN + + CCKSUMLEN); + if (azIsendbuffers[iseq] == NULL) + { + int ifree; + + for (ifree = 0; ifree < iseq; ifree++) + free ((pointer) azIsendbuffers[ifree]); + break; + } + } + + if (iseq >= IMAXSEQ) + { + *pzlog = zbufalc (sizeof "protocol 'i' packet size %d window %d" + + 50); + sprintf (*pzlog, "protocol '%c' packet size %d window %d", + qdaemon->qproto->bname, iIremote_packsize, + iIremote_winsize); + iIalc_packsize = iIremote_packsize; + + return TRUE; + } + + iIremote_packsize >>= 1; + } + while (iIremote_packsize > 200); + + ulog (LOG_ERROR, + "'%c' protocol startup failed; insufficient memory for packets", + qdaemon->qproto->bname); + + return FALSE; +} + +/* Shut down the protocol. We can be fairly informal about this, + since we know that the upper level protocol has already exchanged + hangup messages. If we didn't know that, we would have to make + sure that all packets before the CLOSE had been received. */ + +boolean +fishutdown (qdaemon) + struct sdaemon *qdaemon; +{ + char *z; + size_t clen; + + fIclosing = TRUE; + + z = zigetspace (qdaemon, &clen) - CHDRLEN; + + z[IHDR_INTRO] = IINTRO; + z[IHDR_LOCAL] = IHDRWIN_SET (iIsendseq, 0); + z[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0); + iIlocal_ack = iIrecseq; + z[IHDR_CONTENTS1] = IHDRCON_SET1 (CLOSE, qdaemon->fcaller, 0); + z[IHDR_CONTENTS2] = IHDRCON_SET2 (CLOSE, qdaemon->fcaller, 0); + z[IHDR_CHECK] = IHDRCHECK_VAL (z); + + DEBUG_MESSAGE0 (DEBUG_PROTO, "fishutdown: Sending CLOSE"); + + if (! (*pfIsend) (qdaemon->qconn, z, CHDRLEN, FALSE)) + return FALSE; + + ulog (LOG_NORMAL, + "Protocol '%c' packets: sent %ld, resent %ld, received %ld", + qdaemon->qproto->bname, cIsent_packets, cIresent_packets, + cIreceived_packets); + if (cIbad_hdr != 0 + || cIbad_cksum != 0 + || cIbad_order != 0 + || cIremote_rejects != 0) + ulog (LOG_NORMAL, + "Errors: header %ld, checksum %ld, order %ld, remote rejects %ld", + cIbad_hdr, cIbad_cksum, cIbad_order, cIremote_rejects); + + /* Reset the protocol parameters to their default values. */ + iIrequest_packsize = IREQUEST_PACKSIZE; + iIrequest_winsize = IREQUEST_WINSIZE; + iIforced_remote_packsize = 0; + iIforced_remote_winsize = 0; + cIsync_timeout = CSYNC_TIMEOUT; + cIsync_retries = CSYNC_RETRIES; + cItimeout = CTIMEOUT; + cIretries = CRETRIES; + cIerrors = CERRORS; + cIerror_decay = CERROR_DECAY; + zJavoid_parameter = ZAVOID; + + return TRUE; +} + +/* Send a command string. These are just sent as normal packets, + ending in a packet containing a null byte. */ + +boolean +fisendcmd (qdaemon, z, ilocal, iremote) + struct sdaemon *qdaemon; + const char *z; + int ilocal; + int iremote; +{ + size_t clen; + + DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fisendcmd: Sending command \"%s\"", z); + + clen = strlen (z); + + while (TRUE) + { + char *zpacket; + size_t csize; + + zpacket = zigetspace (qdaemon, &csize); + + if (clen < csize) + { + memcpy (zpacket, z, clen + 1); + return fisenddata (qdaemon, zpacket, clen + 1, ilocal, iremote, + (long) -1); + } + + memcpy (zpacket, z, csize); + z += csize; + clen -= csize; + + if (! fisenddata (qdaemon, zpacket, csize, ilocal, iremote, (long) -1)) + return FALSE; + } + /*NOTREACHED*/ +} + +/* Send a NAK. */ + +static boolean +finak (qdaemon, iseq) + struct sdaemon *qdaemon; + int iseq; +{ + char abnak[CHDRLEN]; + + abnak[IHDR_INTRO] = IINTRO; + abnak[IHDR_LOCAL] = IHDRWIN_SET (iseq, 0); + abnak[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0); + iIlocal_ack = iIrecseq; + abnak[IHDR_CONTENTS1] = IHDRCON_SET1 (NAK, qdaemon->fcaller, 0); + abnak[IHDR_CONTENTS2] = IHDRCON_SET2 (NAK, qdaemon->fcaller, 0); + abnak[IHDR_CHECK] = IHDRCHECK_VAL (abnak); + + afInaked[iseq] = TRUE; + + DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL, + "finak: Sending NAK %d", iseq); + + return (*pfIsend) (qdaemon->qconn, abnak, CHDRLEN, TRUE); +} + +/* Resend the latest packet the remote has not acknowledged. */ + +static boolean +firesend (qdaemon) + struct sdaemon *qdaemon; +{ + int iseq; + char *zhdr; + size_t clen; + + iseq = INEXTSEQ (iIremote_ack); + if (iseq == iIsendseq) + { + /* Everything has been ACKed and there is nothing to resend. */ + return TRUE; + } + + DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL, + "firesend: Resending packet %d", iseq); + + /* Update the received sequence number. */ + zhdr = azIsendbuffers[iseq] + CHDROFFSET; + if (IHDRWIN_GETSEQ (zhdr[IHDR_REMOTE]) != iIrecseq) + { + int iremote; + + iremote = IHDRWIN_GETCHAN (zhdr[IHDR_REMOTE]); + zhdr[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, iremote); + zhdr[IHDR_CHECK] = IHDRCHECK_VAL (zhdr); + iIlocal_ack = iIrecseq; + } + + ++cIresent_packets; + + clen = CHDRCON_GETBYTES (zhdr[IHDR_CONTENTS1], + zhdr[IHDR_CONTENTS2]); + + return (*pfIsend) (qdaemon->qconn, zhdr, + CHDRLEN + clen + (clen > 0 ? CCKSUMLEN : 0), + TRUE); +} + +/* Wait until there is an opening in the receive window of the remote + system. */ + +static boolean +fiwindow_wait (qdaemon) + struct sdaemon *qdaemon; +{ + /* iIsendseq is the sequence number we are sending, and iIremote_ack + is the last sequence number acknowledged by the remote. */ + while (CSEQDIFF (iIsendseq, iIremote_ack) > iIremote_winsize) + { + /* If a NAK is lost, it is possible for the other side to be + sending a stream of packets while we are waiting for an ACK. + This should be caught in fiprocess_data; if it is about to + send an ACK, but it has an unacknowledged packet to send, it + sends the entire packet. Hopefully that will trigger an ACK + or a NAK and get us going again. */ + DEBUG_MESSAGE0 (DEBUG_PROTO, "fiwindow_wait: Waiting for ACK"); + if (! fiwait_for_packet (qdaemon, cItimeout, cIretries, + TRUE, (boolean *) NULL)) + return FALSE; + } + + return TRUE; +} + +/* Get buffer space to use for packet data. We return a pointer to + the space to be used for the actual data, leaving room for the + header. */ + +/*ARGSUSED*/ +char * +zigetspace (qdaemon, pclen) + struct sdaemon *qdaemon; + size_t *pclen; +{ + *pclen = iIremote_packsize; + return azIsendbuffers[iIsendseq] + CHDRSKIPLEN; +} + +/* Send a data packet. The zdata argument will always point to value + returned by zigetspace, so we know that we have room before it for + the header information. */ + +boolean +fisenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos) + struct sdaemon *qdaemon; + char *zdata; + size_t cdata; + int ilocal; + int iremote; + long ipos; +{ + char *zhdr; + unsigned long icksum; + boolean fret; + +#if DEBUG > 0 + if (ilocal < 0 || ilocal >= IMAXICHAN + || iremote < 0 || iremote >= IMAXICHAN) + ulog (LOG_FATAL, "fisenddata: ilocal %d iremote %d", ilocal, iremote); +#endif + + /* If we are changing the file position, we must send an SPOS + packet. */ + if (ipos != iIsendpos && ipos != (long) -1) + { + int inext; + char *zspos; + + /* We need to get a buffer to hold the SPOS packet, and it needs + to be next sequence number. However, the data we have been + given is currently in the next sequence number buffer. So we + shuffle the buffers around. */ + inext = INEXTSEQ (iIsendseq); + zspos = azIsendbuffers[inext]; + azIsendbuffers[inext] = zdata - CHDRSKIPLEN; + azIsendbuffers[iIsendseq] = zspos; + zspos += CHDROFFSET; + + zspos[IHDR_INTRO] = IINTRO; + zspos[IHDR_LOCAL] = IHDRWIN_SET (iIsendseq, 0); + zspos[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0); + iIlocal_ack = iIrecseq; + zspos[IHDR_CONTENTS1] = IHDRCON_SET1 (SPOS, qdaemon->fcaller, + CCKSUMLEN); + zspos[IHDR_CONTENTS2] = IHDRCON_SET2 (SPOS, qdaemon->fcaller, + CCKSUMLEN); + zspos[IHDR_CHECK] = IHDRCHECK_VAL (zspos); + UCKSUM_SET (zspos + CHDRLEN, (unsigned long) ipos); + icksum = icrc (zspos + CHDRLEN, CCKSUMLEN, ICRCINIT); + UCKSUM_SET (zspos + CHDRLEN + CCKSUMLEN, icksum); + + /* Wait for an opening in the window. */ + if (iIremote_winsize > 0 + && CSEQDIFF (iIsendseq, iIremote_ack) > iIremote_winsize) + { + if (! fiwindow_wait (qdaemon)) + return FALSE; + } + + DEBUG_MESSAGE1 (DEBUG_PROTO, "fisenddata: Sending SPOS %ld", + ipos); + + if (! (*pfIsend) (qdaemon->qconn, zspos, + CHDRLEN + CCKSUMLEN + CCKSUMLEN, TRUE)) + return FALSE; + + iIsendseq = INEXTSEQ (iIsendseq); + iIsendpos = ipos; + } + + zhdr = zdata - CHDRLEN; + zhdr[IHDR_INTRO] = IINTRO; + zhdr[IHDR_LOCAL] = IHDRWIN_SET (iIsendseq, ilocal); + zhdr[IHDR_CONTENTS1] = IHDRCON_SET1 (DATA, qdaemon->fcaller, cdata); + zhdr[IHDR_CONTENTS2] = IHDRCON_SET2 (DATA, qdaemon->fcaller, cdata); + + /* Compute and set the checksum. */ + if (cdata > 0) + { + icksum = icrc (zdata, cdata, ICRCINIT); + UCKSUM_SET (zdata + cdata, icksum); + } + + /* Wait until there is an opening in the window (we hope to not have + to wait here at all, actually; ideally the window should be large + enough to avoid a wait). */ + if (iIremote_winsize > 0 + && CSEQDIFF (iIsendseq, iIremote_ack) > iIremote_winsize) + { + if (! fiwindow_wait (qdaemon)) + return FALSE; + } + + /* We only fill in IHDR_REMOTE now, since only now do know the + correct value of iIrecseq. */ + zhdr[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, iremote); + iIlocal_ack = iIrecseq; + zhdr[IHDR_CHECK] = IHDRCHECK_VAL (zhdr); + + DEBUG_MESSAGE2 (DEBUG_PROTO, "fisenddata: Sending packet %d (%d bytes)", + iIsendseq, (int) cdata); + + iIsendseq = INEXTSEQ (iIsendseq); + ++cIsent_packets; + + fret = (*pfIsend) (qdaemon->qconn, zhdr, + cdata + CHDRLEN + (cdata > 0 ? CCKSUMLEN : 0), + TRUE); + + iIsendpos += cdata; + + if (fret && iPrecstart != iPrecend) + { + boolean fexit; + + fret = fiprocess_data (qdaemon, &fexit, (boolean *) NULL, + (size_t *) NULL); + } + + return fret; +} + +/* Wait for data to come in. */ + +boolean +fiwait (qdaemon) + struct sdaemon *qdaemon; +{ + return fiwait_for_packet (qdaemon, cItimeout, cIretries, + FALSE, (boolean *) NULL); +} + +/* Wait for a packet. Either there is no data to send, or the remote + window is full. */ + +static boolean +fiwait_for_packet (qdaemon, ctimeout, cretries, fone, pftimedout) + struct sdaemon *qdaemon; + int ctimeout; + int cretries; + boolean fone; + boolean *pftimedout; +{ + int cshort; + int ctimeouts; + + if (pftimedout != NULL) + *pftimedout = FALSE; + + cshort = 0; + ctimeouts = 0; + + while (TRUE) + { + boolean fexit, ffound; + size_t cneed; + size_t crec; + + if (! fiprocess_data (qdaemon, &fexit, &ffound, &cneed)) + return FALSE; + + if (fexit || (fone && ffound)) + return TRUE; + + if (cneed == 0) + continue; + + DEBUG_MESSAGE1 (DEBUG_PROTO, "fiwait_for_packet: Need %d bytes", + (int) cneed); + + if (! (*pfIreceive) (qdaemon->qconn, cneed, &crec, ctimeout, TRUE)) + return FALSE; + + if (crec != 0) + { + /* If we didn't get enough data twice in a row, we may have + dropped some data and be waiting for the end of a large + packet. Incrementing iPrecstart will force + fiprocess_data to skip the current packet and try to find + the next one. */ + if (crec >= cneed) + cshort = 0; + else + { + ++cshort; + if (cshort > 1) + { + iPrecstart = (iPrecstart + 1) % CRECBUFLEN; + cshort = 0; + } + } + } + else + { + int i; + + /* We timed out on the read. */ + ++ctimeouts; + if (ctimeouts > cretries) + { + if (cretries > 0) + ulog (LOG_ERROR, "Timed out waiting for packet"); + if (pftimedout != NULL) + *pftimedout = TRUE; + return FALSE; + } + + /* Clear out the list of packets we have sent NAKs for. We + should have seen some sort of response by now. */ + for (i = 0; i < IMAXSEQ; i++) + afInaked[i] = FALSE; + + /* Send a NAK for the packet we want, and, if we have an + unacknowledged packet, send it again. */ + if (! finak (qdaemon, INEXTSEQ (iIrecseq)) + || ! firesend (qdaemon)) + return FALSE; + } + } + /*NOTREACHED*/ +} + +/* Make sure we haven't overflowed the permissible error level. */ + +static boolean +ficheck_errors (qdaemon) + struct sdaemon *qdaemon; +{ + if (cIerrors < 0) + return TRUE; + + if (((cIbad_order + cIbad_hdr + cIbad_cksum + cIremote_rejects) + - (cIreceived_packets / cIerror_decay)) + > cIerrors) + { + /* Try shrinking the packet size. */ + if (iIrequest_packsize > 400) + { + char absync[CHDRLEN + 3 + CCKSUMLEN]; + unsigned long icksum; + + iIrequest_packsize /= 2; + absync[IHDR_INTRO] = IINTRO; + absync[IHDR_LOCAL] = IHDRWIN_SET (0, 0); + absync[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0); + iIlocal_ack = iIrecseq; + absync[IHDR_CONTENTS1] = IHDRCON_SET1 (SYNC, qdaemon->fcaller, 3); + absync[IHDR_CONTENTS2] = IHDRCON_SET2 (SYNC, qdaemon->fcaller, 3); + absync[IHDR_CHECK] = IHDRCHECK_VAL (absync); + absync[CHDRLEN + 0] = (iIrequest_packsize >> 8) & 0xff; + absync[CHDRLEN + 1] = iIrequest_packsize & 0xff; + absync[CHDRLEN + 2] = iIrequest_winsize; + icksum = icrc (absync + CHDRLEN, 3, ICRCINIT); + UCKSUM_SET (absync + CHDRLEN + 3, icksum); + + cIerrors *= 2; + + DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, + "ficheck_errors: Sending SYNC packsize %d winsize %d", + iIrequest_packsize, iIrequest_winsize); + + return (*pfIsend) (qdaemon->qconn, absync, + CHDRLEN + 3 + CCKSUMLEN, TRUE); + } + + ulog (LOG_ERROR, "Too many '%c' protocol errors", + qdaemon->qproto->bname); + return FALSE; + } + + return TRUE; +} + +/* Process data waiting in the receive buffer, passing to the + fgot_data function. */ + +static boolean +fiprocess_data (qdaemon, pfexit, pffound, pcneed) + struct sdaemon *qdaemon; + boolean *pfexit; + boolean *pffound; + size_t *pcneed; +{ + boolean fbadhdr; + + if (pfexit != NULL) + *pfexit = FALSE; + if (pffound != NULL) + *pffound = FALSE; + + fbadhdr = FALSE; + + while (iPrecstart != iPrecend) + { + char ab[CHDRLEN]; + int cfirst, csecond; + char *zfirst, *zsecond; + int i; + int iget; + int ttype; + int iseq; + int csize; + int iack; + + /* If we're closing the connection, ignore any data remaining in + the input buffer. */ + if (fIclosing) + { + if (pfexit != NULL) + *pfexit = TRUE; + if (pcneed != NULL) + *pcneed = 0; + return TRUE; + } + + /* Look for the IINTRO character. */ + if (abPrecbuf[iPrecstart] != IINTRO) + { + char *zintro; + int cintro; + + cintro = iPrecend - iPrecstart; + if (cintro < 0) + cintro = CRECBUFLEN - iPrecstart; + + zintro = memchr (abPrecbuf + iPrecstart, IINTRO, (size_t) cintro); + + if (zintro == NULL) + { + iPrecstart = (iPrecstart + cintro) % CRECBUFLEN; + continue; + } + + /* We don't need % CRECBUFLEN here because zintro - (abPrecbuf + + iPrecstart) < cintro <= CRECBUFLEN - iPrecstart. */ + iPrecstart += zintro - (abPrecbuf + iPrecstart); + } + + /* Get the header into ab. */ + for (i = 0, iget = iPrecstart; + i < CHDRLEN && iget != iPrecend; + i++, iget = (iget + 1) % CRECBUFLEN) + ab[i] = abPrecbuf[iget]; + + if (i < CHDRLEN) + { + if (pcneed != NULL) + *pcneed = CHDRLEN - i; + return TRUE; + } + + if ((ab[IHDR_CHECK] & 0xff) != IHDRCHECK_VAL (ab) + || (FHDRCON_GETCALLER (ab[IHDR_CONTENTS1], ab[IHDR_CONTENTS2]) + ? qdaemon->fcaller : ! qdaemon->fcaller)) + { + /* We only report a single bad header message per call, to + avoid generating many errors if we get many INTRO bytes + in a row. */ + if (! fbadhdr) + { + DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL, + "fiprocess_data: Bad header"); + + ++cIbad_hdr; + if (! ficheck_errors (qdaemon)) + return FALSE; + + fbadhdr = TRUE; + } + + iPrecstart = (iPrecstart + 1) % CRECBUFLEN; + continue; + } + + zfirst = zsecond = NULL; + cfirst = csecond = 0; + + ttype = THDRCON_GETTYPE (ab[IHDR_CONTENTS1], ab[IHDR_CONTENTS2]); + if (ttype == DATA || ttype == SPOS || ttype == CLOSE) + iseq = IHDRWIN_GETSEQ (ab[IHDR_LOCAL]); + else + iseq = -1; + csize = CHDRCON_GETBYTES (ab[IHDR_CONTENTS1], ab[IHDR_CONTENTS2]); + + if (iseq != -1) + { + /* Make sure this packet is in our receive window. The last + packet we have acked is iIlocal_ack. */ + if (iIrequest_winsize > 0 + && CSEQDIFF (iseq, iIlocal_ack) > iIrequest_winsize) + { + DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL, + "fiprocess_data: Out of order packet %d", + iseq); + + ++cIbad_order; + if (! ficheck_errors (qdaemon)) + return FALSE; + + iPrecstart = (iPrecstart + 1) % CRECBUFLEN; + + continue; + } + } + + if (csize > 0) + { + int cinbuf; + char abcksum[CCKSUMLEN]; + unsigned long ickdata; + + cinbuf = iPrecend - iPrecstart; + if (cinbuf < 0) + cinbuf += CRECBUFLEN; + if (cinbuf < CHDRLEN + csize + CCKSUMLEN) + { + if (pcneed != NULL) + *pcneed = CHDRLEN + csize + CCKSUMLEN - cinbuf; + return TRUE; + } + + if (iPrecend > iPrecstart) + { + cfirst = csize; + zfirst = abPrecbuf + iPrecstart + CHDRLEN; + } + else + { + cfirst = CRECBUFLEN - (iPrecstart + CHDRLEN); + if (cfirst <= 0) + { + /* Here cfirst is non-positive, so subtracting from + abPrecbuf will actually skip the appropriate number + of bytes at the start of abPrecbuf. */ + zfirst = abPrecbuf - cfirst; + cfirst = csize; + } + else + { + if (cfirst >= csize) + cfirst = csize; + else + { + zsecond = abPrecbuf; + csecond = csize - cfirst; + } + zfirst = abPrecbuf + iPrecstart + CHDRLEN; + } + } + + /* Get the checksum into abcksum. */ + for (i = 0, iget = (iPrecstart + CHDRLEN + csize) % CRECBUFLEN; + i < CCKSUMLEN; + i++, iget = (iget + 1) % CRECBUFLEN) + abcksum[i] = abPrecbuf[iget]; + + ickdata = icrc (zfirst, (size_t) cfirst, ICRCINIT); + if (csecond > 0) + ickdata = icrc (zsecond, (size_t) csecond, ickdata); + + if (ICKSUM_GET (abcksum) != ickdata) + { + DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, + "fiprocess_data: Bad checksum; data %lu, frame %lu", + ickdata, ICKSUM_GET (abcksum)); + + ++cIbad_cksum; + if (! ficheck_errors (qdaemon)) + return FALSE; + + /* If this sequence number is in our receive window, + send a NAK. iIrecseq is the last sequence number we + have succesfully received. */ + if (iseq != -1 + && iseq != iIrecseq + && (iIrequest_winsize <= 0 + || CSEQDIFF (iseq, iIrecseq) <= iIrequest_winsize) + && azIrecbuffers[iseq] == NULL) + { + if (! finak (qdaemon, iseq)) + return FALSE; + } + + iPrecstart = (iPrecstart + 1) % CRECBUFLEN; + continue; + } + } + + /* Here we know that this is a valid packet, so we can adjust + iPrecstart accordingly. */ + if (csize == 0) + iPrecstart = (iPrecstart + CHDRLEN) % CRECBUFLEN; + else + { + iPrecstart = ((iPrecstart + CHDRLEN + csize + CCKSUMLEN) + % CRECBUFLEN); + ++cIreceived_packets; + } + + /* Get the ack from the packet, if appropriate. iIsendseq is + the next sequence number we are going to send, and + iIremote_ack is the last sequence number acknowledged by the + remote system. */ + iack = IHDRWIN_GETSEQ (ab[IHDR_REMOTE]); + if (iIremote_winsize > 0 + && iack != iIsendseq + && CSEQDIFF (iack, iIremote_ack) <= iIremote_winsize + && CSEQDIFF (iIsendseq, iack) <= iIremote_winsize) + { + /* Call uwindow_acked each time packet 0 is acked. */ + if (iack < iIremote_ack) + uwindow_acked (qdaemon, FALSE); + iIremote_ack = iack; + } + + if (iseq != -1) + { + afInaked[iseq] = FALSE; + + /* If we haven't handled all previous packets, we must save + off this packet and deal with it later. */ + if (iseq != INEXTSEQ (iIrecseq)) + { + if (iseq == iIrecseq + || (iIrequest_winsize > 0 + && CSEQDIFF (iseq, iIrecseq) > iIrequest_winsize)) + { + DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL, + "fiprocess_data: Ignoring out of order packet %d", + iseq); + continue; + } + else + { + DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL, + "fiprocess_data: Saving unexpected packet %d", + iseq); + + if (azIrecbuffers[iseq] == NULL) + { + azIrecbuffers[iseq] = zbufalc ((size_t) (CHDRLEN + + csize)); + memcpy (azIrecbuffers[iseq], ab, CHDRLEN); + if (csize > 0) + { + memcpy (azIrecbuffers[iseq] + CHDRLEN, zfirst, + (size_t) cfirst); + if (csecond > 0) + memcpy (azIrecbuffers[iseq] + CHDRLEN + cfirst, + zsecond, (size_t) csecond); + } + } + } + + /* Send NAK's for each packet between the last one we + received and this one, avoiding any packets for which + we've already sent NAK's or which we've already + received. */ + for (i = INEXTSEQ (iIrecseq); + i != iseq; + i = INEXTSEQ (i)) + { + if (! afInaked[i] + && azIrecbuffers[i] == NULL) + { + if (! finak (qdaemon, i)) + return FALSE; + } + } + + continue; + } + + iIrecseq = iseq; + } + + if (pffound != NULL) + *pffound = TRUE; + + if (! fiprocess_packet (qdaemon, ab, zfirst, cfirst, zsecond, csecond, + pfexit)) + return FALSE; + + if (iseq != -1) + { + int inext; + + /* If we've already received the next packet(s), process + them. */ + inext = INEXTSEQ (iIrecseq); + while (azIrecbuffers[inext] != NULL) + { + char *z; + int c; + + z = azIrecbuffers[inext]; + c = CHDRCON_GETBYTES (z[IHDR_CONTENTS1], z[IHDR_CONTENTS2]); + iIrecseq = inext; + if (! fiprocess_packet (qdaemon, z, z + CHDRLEN, c, + (char *) NULL, 0, pfexit)) + return FALSE; + ubuffree (azIrecbuffers[inext]); + azIrecbuffers[inext] = NULL; + inext = INEXTSEQ (inext); + } + } + + /* If we have received half of our window size or more since the + last ACK, send one now. Sending an ACK for half the window + at a time should significantly cut the acknowledgement + traffic when only one side is sending. We should normally + not have to send an ACK if we have data to send, since each + packet sent will ACK the most recently received packet. + However, it can happen if we receive a burst of short + packets, such as a set of command acknowledgements. */ + if (iIrequest_winsize > 0 + && CSEQDIFF (iIrecseq, iIlocal_ack) >= iIrequest_winsize / 2) + { + char aback[CHDRLEN]; + + aback[IHDR_INTRO] = IINTRO; + aback[IHDR_LOCAL] = IHDRWIN_SET (0, 0); + aback[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0); + iIlocal_ack = iIrecseq; + aback[IHDR_CONTENTS1] = IHDRCON_SET1 (ACK, qdaemon->fcaller, 0); + aback[IHDR_CONTENTS2] = IHDRCON_SET2 (ACK, qdaemon->fcaller, 0); + aback[IHDR_CHECK] = IHDRCHECK_VAL (aback); + + DEBUG_MESSAGE1 (DEBUG_PROTO, "fiprocess_data: Sending ACK %d", + iIrecseq); + + if (! (*pfIsend) (qdaemon->qconn, aback, CHDRLEN, TRUE)) + return FALSE; + } + } + + if (pcneed != NULL) + *pcneed = CHDRLEN; + + return TRUE; +} + +/* Process a single packet. */ + +static boolean +fiprocess_packet (qdaemon, zhdr, zfirst, cfirst, zsecond, csecond, pfexit) + struct sdaemon *qdaemon; + const char *zhdr; + const char *zfirst; + int cfirst; + const char *zsecond; + int csecond; + boolean *pfexit; +{ + int ttype; + + ttype = THDRCON_GETTYPE (zhdr[IHDR_CONTENTS1], zhdr[IHDR_CONTENTS2]); + switch (ttype) + { + case DATA: + { + int iseq; + boolean fret; + + iseq = IHDRWIN_GETSEQ (zhdr[IHDR_LOCAL]); + DEBUG_MESSAGE2 (DEBUG_PROTO, + "fiprocess_packet: Got DATA packet %d size %d", + iseq, cfirst + csecond); + fret = fgot_data (qdaemon, zfirst, (size_t) cfirst, + zsecond, (size_t) csecond, + IHDRWIN_GETCHAN (zhdr[IHDR_REMOTE]), + IHDRWIN_GETCHAN (zhdr[IHDR_LOCAL]), + iIrecpos, + INEXTSEQ (iIremote_ack) == iIsendseq, + pfexit); + iIrecpos += cfirst + csecond; + return fret; + } + + case SYNC: + { + int ipack, iwin; + + /* We accept a SYNC packet to adjust the packet and window + sizes at any time. */ + if (cfirst + csecond < 3) + { + ulog (LOG_ERROR, "Bad SYNC packet"); + return FALSE; + } + ipack = (zfirst[0] & 0xff) << 8; + if (cfirst > 1) + ipack |= zfirst[1] & 0xff; + else + ipack |= zsecond[0]; + if (cfirst > 2) + iwin = zfirst[2]; + else + iwin = zsecond[2 - cfirst]; + + DEBUG_MESSAGE2 (DEBUG_PROTO, + "fiprocess_packet: Got SYNC packsize %d winsize %d", + ipack, iwin); + + if (iIforced_remote_packsize == 0 + && (iIalc_packsize == 0 + || ipack <= iIalc_packsize)) + iIremote_packsize = ipack; + if (iIforced_remote_winsize == 0) + iIremote_winsize = iwin; + + /* We increment a static variable to tell the initialization + code that a SYNC was received, and we set *pfexit to TRUE + to get out to the initialization code (this will do no harm + if we are called from elsewhere). */ + ++cIsyncs; + *pfexit = TRUE; + return TRUE; + } + + case ACK: + /* There is nothing to do here, since the ack was already + handled in fiprocess_data. */ + DEBUG_MESSAGE1 (DEBUG_PROTO, + "fiprocess_packet: Got ACK %d", + IHDRWIN_GETSEQ (zhdr[IHDR_REMOTE])); + return TRUE; + + case NAK: + /* We must resend the requested packet. */ + { + int iseq; + char *zsend; + size_t clen; + + ++cIremote_rejects; + if (! ficheck_errors (qdaemon)) + return FALSE; + + iseq = IHDRWIN_GETSEQ (zhdr[IHDR_LOCAL]); + + /* The timeout code will send a NAK for the packet the remote + side wants. So we may see a NAK here for the packet we are + about to send. */ + if (iseq == iIsendseq + || (iIremote_winsize > 0 + && (CSEQDIFF (iseq, iIremote_ack) > iIremote_winsize + || CSEQDIFF (iIsendseq, iseq) > iIremote_winsize))) + { + DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL, + "fiprocess_packet: Ignoring out of order NAK %d", + iseq); + return TRUE; + } + + DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL, + "fiprocess_packet: Got NAK %d; resending packet", + iseq); + + /* Update the received sequence number. */ + zsend = azIsendbuffers[iseq] + CHDROFFSET; + if (IHDRWIN_GETSEQ (zsend[IHDR_REMOTE]) != iIrecseq) + { + int iremote; + + iremote = IHDRWIN_GETCHAN (zsend[IHDR_REMOTE]); + zsend[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, iremote); + zsend[IHDR_CHECK] = IHDRCHECK_VAL (zsend); + iIlocal_ack = iIrecseq; + } + + ++cIresent_packets; + + clen = CHDRCON_GETBYTES (zsend[IHDR_CONTENTS1], + zsend[IHDR_CONTENTS2]); + + return (*pfIsend) (qdaemon->qconn, zsend, + CHDRLEN + clen + (clen > 0 ? CCKSUMLEN : 0), + TRUE); + } + + case SPOS: + /* Set the file position. */ + { + char abpos[CCKSUMLEN]; + const char *zpos; + + if (cfirst >= CCKSUMLEN) + zpos = zfirst; + else + { + memcpy (abpos, zfirst, (size_t) cfirst); + memcpy (abpos + cfirst, zsecond, (size_t) (CCKSUMLEN - cfirst)); + zpos = abpos; + } + iIrecpos = (long) ICKSUM_GET (zpos); + DEBUG_MESSAGE1 (DEBUG_PROTO, + "fiprocess_packet: Got SPOS %ld", iIrecpos); + return TRUE; + } + + case CLOSE: + { + boolean fexpected; + + fexpected = ! fLog_sighup || fIclosing; + if (! fexpected) + ulog (LOG_ERROR, "Received unexpected CLOSE packet"); + else + DEBUG_MESSAGE0 (DEBUG_PROTO, "fiprocess_packet: Got CLOSE packet"); + + fIclosing = TRUE; + *pfexit = TRUE; + return fexpected; + } + + default: + /* Just ignore unrecognized packet types, for future protocol + enhancements. */ + DEBUG_MESSAGE1 (DEBUG_PROTO, "fiprocess_packet: Got packet type %d", + ttype); + return TRUE; + } + /*NOTREACHED*/ +} diff --git a/gnu/libexec/uucp/uucico/protj.c b/gnu/libexec/uucp/uucico/protj.c new file mode 100644 index 0000000000..bdf2b85611 --- /dev/null +++ b/gnu/libexec/uucp/uucico/protj.c @@ -0,0 +1,671 @@ +/* protj.c + The 'j' protocol. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char protj_rcsid[] = "$Id: protj.c,v 1.1 1993/08/04 19:36:23 jtc Exp $"; +#endif + +#include +#include + +#include "uudefs.h" +#include "conn.h" +#include "trans.h" +#include "system.h" +#include "prot.h" + +/* The 'j' protocol. + + The 'j' protocol is a wrapper around the 'i' protocol, which avoids + the use of certain characters, such as XON and XOFF. + + Each 'j' protocol packet begins with a '^' character, followed by a + two byte encoded size giving the total number of bytes in the + packet. The first byte is HIGH, the second byte is LOW, and the + number of bytes is (HIGH - 32) * 64 + (LOW - 32), where 32 <= HIGH + < 127 and 32 <= LOW < 96 (i.e., HIGH and LOW are printable ASCII + characters). This is followed by a '=' character. The next two + bytes are the number of data bytes in the packet, using the same + encoding. This is followed by a '@' character, and then that + number of data bytes. The remaining bytes in the packet are + indices of bytes which must be transformed, followed by a trailing + '~' character. The indices are encoded in the following overly + complex format. + + Each byte index is two bytes long. The first byte in the index is + INDEX-HIGH and the second is INDEX-LOW. If 32 <= INDEX-HIGH < 126, + the byte index refers to the byte at position (INDEX-HIGH - 32) * + 32 + INDEX-LOW % 32 in the actual data, where 32 <= INDEX-LOW < + 127. If 32 <= INDEX-LOW < 64, then 128 must be added to the + indexed byte. If 64 <= INDEX-LOW < 96, then the indexed byte must + be exclusive or'red with 32. If 96 <= INDEX-LOW < 127, both + operations must be performed. If INDEX-HIGH == 126, then the byte + index refers to the byte at position (INDEX-LOW - 32) * 32 + 31, + where 32 <= INDEX-LOW < 126. 128 must be added to the byte, and it + must be exclusive or'red with 32. This unfortunately requires a + special test (when encoding INDEX-LOW must be checked for 127; when + decoding INDEX-HIGH must be checked for 126). It does, however, + permit the byte indices field to consist exclusively of printable + ASCII characters. + + The maximum value for a byte index is (125 - 32) * 32 + 31 == 3007, + so the is the maximum number of data bytes permitted. Since it is + convenient to have each 'j' protocol packet correspond to each 'i' + protocol packet, we restrict the 'i' protocol accordingly. + + Note that this encoding method assumes that we can send all + printable ASCII characters. */ + +/* The first byte of each packet. I just picked these values + randomly, trying to get characters that were perhaps slightly less + likely to appear in normal text. */ +#define FIRST '\136' + +/* The fourth byte of each packet. */ +#define FOURTH '\075' + +/* The seventh byte of each packet. */ +#define SEVENTH '\100' + +/* The trailing byte of each packet. */ +#define TRAILER '\176' + +/* The length of the header. */ +#define CHDRLEN (7) + +/* Get a number of bytes encoded in a two byte length at the start of + a packet. */ +#define CGETLENGTH(b1, b2) (((b1) - 32) * 64 + ((b2) - 32)) + +/* Set the high and low bytes of a two byte length at the start of a + packet. */ +#define ISETLENGTH_FIRST(i) ((i) / 64 + 32) +#define ISETLENGTH_SECOND(i) ((i) % 64 + 32) + +/* The maximum packet size we support, as determined by the byte + indices. */ +#define IMAXPACKSIZE ((125 - 32) * 32 + 31) + +/* Amount to offset the bytes in the byte index by. */ +#define INDEX_OFFSET (32) + +/* Maximum value of INDEX-LOW, before offsetting. */ +#define INDEX_MAX_LOW (32) + +/* Maximum value of INDEX-HIGH, before offsetting. */ +#define INDEX_MAX_HIGH (94) + +/* The set of characters to avoid. */ +static char *zJavoid; + +/* The number of characters to avoid. */ +static size_t cJavoid; + +/* A buffer used when sending data. */ +static char *zJbuf; + +/* The end of the undecoded data in abPrecbuf. */ +static int iJrecend; + +/* Local functions. */ +static boolean fjsend_data P((struct sconnection *qconn, const char *zsend, + size_t csend, boolean fdoread)); +static boolean fjreceive_data P((struct sconnection *qconn, size_t cneed, + size_t *pcrec, int ctimeout, + boolean freport)); +static boolean fjprocess_data P((size_t *pcneed)); + +/* Start the protocol. We first send over the list of characters to + avoid as an escape sequence, starting with FIRST and ending with + TRAILER. There is no error checking done on this string. */ + +boolean +fjstart (qdaemon, pzlog) + struct sdaemon *qdaemon; + char **pzlog; +{ + size_t clen; + char *zsend; + int b; + size_t cbuf, cgot; + char *zbuf; + int i; + + /* Send the characters we want to avoid to the other side. */ + clen = strlen (zJavoid_parameter); + zsend = zbufalc (clen + 3); + zsend[0] = FIRST; + memcpy (zsend + 1, zJavoid_parameter, clen); + zsend[clen + 1] = TRAILER; + zsend[clen + 2] = '\0'; + if (! fsend_data (qdaemon->qconn, zsend, clen + 2, TRUE)) + { + ubuffree (zsend); + return FALSE; + } + ubuffree (zsend); + + /* Read the characters the other side wants to avoid. */ + while ((b = breceive_char (qdaemon->qconn, cIsync_timeout, TRUE)) + != FIRST) + { + if (b < 0) + { + if (b == -1) + ulog (LOG_ERROR, "Timed out in 'j' protocol startup"); + return FALSE; + } + } + + cbuf = 20; + zbuf = zbufalc (cbuf); + cgot = 0; + while ((b = breceive_char (qdaemon->qconn, cIsync_timeout, TRUE)) + != TRAILER) + { + if (b < 0) + { + ubuffree (zbuf); + if (b == -1) + ulog (LOG_ERROR, "Timed out in 'j' protocol startup"); + return FALSE; + } + if (cgot + 1 >= cbuf) + { + char *znew; + + cbuf += 20; + znew = zbufalc (cbuf); + memcpy (znew, zbuf, cgot); + ubuffree (zbuf); + zbuf = znew; + } + zbuf[cgot] = b; + ++cgot; + } + zbuf[cgot] = '\0'; + + /* Merge the local and remote avoid bytes into one list, translated + into bytes. */ + cgot = cescape (zbuf); + + clen = strlen (zJavoid_parameter); + zJavoid = zbufalc (clen + cgot + 1); + memcpy (zJavoid, zJavoid_parameter, clen + 1); + cJavoid = cescape (zJavoid); + + for (i = 0; i < cgot; i++) + { + if (memchr (zJavoid, zbuf[i], cJavoid) == NULL) + { + zJavoid[cJavoid] = zbuf[i]; + ++cJavoid; + } + } + + ubuffree (zbuf); + + /* We can't avoid ASCII printable characters, since the encoding + method assumes that they can always be sent. If it ever turns + out to be important, a different encoding method could be used, + perhaps keyed by a different FIRST character. */ + if (cJavoid == 0) + { + ulog (LOG_ERROR, "No characters to avoid in 'j' protocol"); + return FALSE; + } + for (i = 0; i < cJavoid; i++) + { + if (zJavoid[i] >= 32 && zJavoid[i] <= 126) + { + ulog (LOG_ERROR, "'j' protocol can't avoid character '\\%03o'", + zJavoid[i]); + return FALSE; + } + } + + /* If we are avoiding XON and XOFF, use XON/XOFF handshaking. */ + if (memchr (zJavoid, '\021', cJavoid) != NULL + && memchr (zJavoid, '\023', cJavoid) != NULL) + { + if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE, + STRIPSETTING_EIGHTBITS, XONXOFF_ON)) + return FALSE; + } + + /* Let the port settle. */ + usysdep_sleep (2); + + /* Allocate a buffer we use when sending data. We will probably + never actually need one this big; if this code is ported to a + computer with small amounts of memory, this should be changed to + increase the buffer size as needed. */ + zJbuf = zbufalc (CHDRLEN + IMAXPACKSIZE * 3 + 1); + zJbuf[0] = FIRST; + zJbuf[3] = FOURTH; + zJbuf[6] = SEVENTH; + + /* iJrecend is the end of the undecoded data, and iPrecend is the + end of the decoded data. At this point there is no decoded data, + and we must initialize the variables accordingly. */ + iJrecend = iPrecend; + iPrecend = iPrecstart; + + /* Now do the 'i' protocol startup. */ + return fijstart (qdaemon, pzlog, IMAXPACKSIZE, fjsend_data, + fjreceive_data); +} + +/* Shut down the protocol. */ + +boolean +fjshutdown (qdaemon) + struct sdaemon *qdaemon; +{ + boolean fret; + + fret = fishutdown (qdaemon); + ubuffree (zJavoid); + ubuffree (zJbuf); + return fret; +} + +/* Encode a packet of data and send it. This copies the data, which + is a waste of time, but calling fsend_data three times (for the + header, the body, and the trailer) would waste even more time. */ + +static boolean +fjsend_data (qconn, zsend, csend, fdoread) + struct sconnection *qconn; + const char *zsend; + size_t csend; + boolean fdoread; +{ + char *zput, *zindex; + const char *zfrom, *zend; + char bfirst, bsecond; + int iprecendhold; + boolean fret; + + zput = zJbuf + CHDRLEN; + zindex = zput + csend; + zfrom = zsend; + zend = zsend + csend; + + /* Optimize for the common case of avoiding two characters. */ + bfirst = zJavoid[0]; + if (cJavoid <= 1) + bsecond = bfirst; + else + bsecond = zJavoid[1]; + while (zfrom < zend) + { + char b; + boolean f128, f32; + int i, ihigh, ilow; + + b = *zfrom++; + if (b != bfirst && b != bsecond) + { + int ca; + char *za; + + if (cJavoid <= 2) + { + *zput++ = b; + continue; + } + + ca = cJavoid - 2; + za = zJavoid + 2; + while (ca-- != 0) + if (*za++ == b) + break; + + if (ca < 0) + { + *zput++ = b; + continue; + } + } + + if ((b & 0x80) == 0) + f128 = FALSE; + else + { + b &=~ 0x80; + f128 = TRUE; + } + if (b >= 32 && b != 127) + f32 = FALSE; + else + { + b ^= 0x20; + f32 = TRUE; + } + + /* We must now put the byte index into the buffer. The byte + index is encoded similarly to the length of the actual data, + but the byte index also encodes the operations that must be + performed on the byte. The first byte in the index is the + most significant bits. If we only had to subtract 128 from + the byte, we use the second byte directly. If we had to xor + the byte with 32, we add 32 to the second byte index. If we + had to perform both operations, we add 64 to the second byte + index. However, if we had to perform both operations, and + the second byte index was 31, then after adding 64 and + offsetting by 32 we would come up with 127, which we are not + permitted to use. Therefore, in this special case we set the + first byte of the index to 126 and put the original first + byte into the second byte position instead. This is why we + could not permit the high byte of the length of the actual + data to be 126. We can get away with the switch because both + the value of the second byte index (31) and the operations to + perform (both) are known. */ + i = zput - (zJbuf + CHDRLEN); + ihigh = i / INDEX_MAX_LOW; + ilow = i % INDEX_MAX_LOW; + + if (f128 && ! f32) + ; + else if (f32 && ! f128) + ilow += INDEX_MAX_LOW; + else + { + /* Both operations had to be performed. */ + if (ilow != INDEX_MAX_LOW - 1) + ilow += 2 * INDEX_MAX_LOW; + else + { + ilow = ihigh; + ihigh = INDEX_MAX_HIGH; + } + } + + *zindex++ = ihigh + INDEX_OFFSET; + *zindex++ = ilow + INDEX_OFFSET; + *zput++ = b; + } + + *zindex++ = TRAILER; + + /* Set the lengths into the buffer. zJbuf[0,3,6] were set when + zJbuf was allocated, and are never changed thereafter. */ + zJbuf[1] = ISETLENGTH_FIRST (zindex - zJbuf); + zJbuf[2] = ISETLENGTH_SECOND (zindex - zJbuf); + zJbuf[4] = ISETLENGTH_FIRST (csend); + zJbuf[5] = ISETLENGTH_SECOND (csend); + + /* Send the data over the line. We must preserve iPrecend as + discussed in fjreceive_data. */ + iprecendhold = iPrecend; + iPrecend = iJrecend; + fret = fsend_data (qconn, zJbuf, (size_t) (zindex - zJbuf), fdoread); + iJrecend = iPrecend; + iPrecend = iprecendhold; + + /* Process any bytes that may have been placed in abPrecbuf. */ + if (fret && iPrecend != iJrecend) + { + if (! fjprocess_data ((size_t *) NULL)) + return FALSE; + } + + return fret; +} + +/* Receive and decode data. This is called by fiwait_for_packet. We + need to be able to return decoded data between iPrecstart and + iPrecend, while not losing any undecoded partial packets we may + have read. We use iJrecend as a pointer to the end of the + undecoded data, and set iPrecend for the decoded data. iPrecend + points to the start of the undecoded data. */ + +static boolean +fjreceive_data (qconn, cineed, pcrec, ctimeout, freport) + struct sconnection *qconn; + size_t cineed; + size_t *pcrec; + int ctimeout; + boolean freport; +{ + int iprecendstart; + size_t cjneed; + size_t crec; + int cnew; + + iprecendstart = iPrecend; + + /* Figure out how many bytes we need to decode the next packet. */ + if (! fjprocess_data (&cjneed)) + return FALSE; + + /* As we long as we read some data but don't have enough to decode a + packet, we try to read some more. We decrease the timeout each + time so that we will not wait forever if the connection starts + dribbling data. */ + do + { + int iprecendhold; + size_t cneed; + + if (cjneed > cineed) + cneed = cjneed; + else + cneed = cineed; + + /* We are setting iPrecend to the end of the decoded data for + the 'i' protocol. When we do the actual read, we have to set + it to the end of the undecoded data so that any undecoded + data we have received is not overwritten. */ + iprecendhold = iPrecend; + iPrecend = iJrecend; + if (! freceive_data (qconn, cneed, &crec, ctimeout, freport)) + return FALSE; + iJrecend = iPrecend; + iPrecend = iprecendhold; + + /* Process any data we have received. This will set iPrecend to + the end of the new decoded data. */ + if (! fjprocess_data (&cjneed)) + return FALSE; + + cnew = iPrecend - iprecendstart; + if (cnew < 0) + cnew += CRECBUFLEN; + + if (cnew > cineed) + cineed = 0; + else + cineed -= cnew; + + --ctimeout; + } + while (cnew == 0 && crec > 0 && ctimeout > 0); + + DEBUG_MESSAGE1 (DEBUG_PROTO, "fjreceive_data: Got %d decoded bytes", + cnew); + + *pcrec = cnew; + return TRUE; +} + +/* Decode the data in the buffer, optionally returning the number of + bytes needed to complete the next packet. */ + +static boolean +fjprocess_data (pcneed) + size_t *pcneed; +{ + int istart; + + istart = iPrecend; + while (istart != iJrecend) + { + int i, iget; + char ab[CHDRLEN]; + int cpacket, cdata, chave; + int iindex, iendindex; + + /* Find the next occurrence of FIRST. If we have to skip some + garbage bytes to get to it, zero them out (so they don't + confuse the 'i' protocol) and advance iPrecend. This will + save us from looking at them again. */ + if (abPrecbuf[istart] != FIRST) + { + int cintro; + char *zintro; + size_t cskipped; + + cintro = iJrecend - istart; + if (cintro < 0) + cintro = CRECBUFLEN - istart; + + zintro = memchr (abPrecbuf + istart, FIRST, (size_t) cintro); + if (zintro == NULL) + { + bzero (abPrecbuf + istart, (size_t) cintro); + istart = (istart + cintro) % CRECBUFLEN; + iPrecend = istart; + continue; + } + + cskipped = zintro - (abPrecbuf + istart); + bzero (abPrecbuf + istart, cskipped); + istart += cskipped; + iPrecend = istart; + } + + for (i = 0, iget = istart; + i < CHDRLEN && iget != iJrecend; + ++i, iget = (iget + 1) % CRECBUFLEN) + ab[i] = abPrecbuf[iget]; + + if (i < CHDRLEN) + { + if (pcneed != NULL) + *pcneed = CHDRLEN - i; + return TRUE; + } + + cpacket = CGETLENGTH (ab[1], ab[2]); + cdata = CGETLENGTH (ab[4], ab[5]); + + /* Make sure the header has the right magic characters, that the + data is not larger than the packet, and that we have an even + number of byte index characters. */ + if (ab[3] != FOURTH + || ab[6] != SEVENTH + || cdata > cpacket - CHDRLEN - 1 + || (cpacket - cdata - CHDRLEN - 1) % 2 == 1) + { + istart = (istart + 1) % CRECBUFLEN; + continue; + } + + chave = iJrecend - istart; + if (chave < 0) + chave += CRECBUFLEN; + + if (chave < cpacket) + { + if (pcneed != NULL) + *pcneed = cpacket - chave; + return TRUE; + } + + /* Figure out where the byte indices start and end. */ + iindex = (istart + CHDRLEN + cdata) % CRECBUFLEN; + iendindex = (istart + cpacket - 1) % CRECBUFLEN; + + /* Make sure the magic trailer character is there. */ + if (abPrecbuf[iendindex] != TRAILER) + { + istart = (istart + 1) % CRECBUFLEN; + continue; + } + + /* We have a packet to decode. The decoding process is simpler + than the encoding process, since all we have to do is examine + the byte indices. We zero out the byte indices as we go, so + that they will not confuse the 'i' protocol. */ + while (iindex != iendindex) + { + int ihigh, ilow; + boolean f32, f128; + int iset; + + ihigh = abPrecbuf[iindex] - INDEX_OFFSET; + abPrecbuf[iindex] = 0; + iindex = (iindex + 1) % CRECBUFLEN; + ilow = abPrecbuf[iindex] - INDEX_OFFSET; + abPrecbuf[iindex] = 0; + iindex = (iindex + 1) % CRECBUFLEN; + + /* Now we must undo the encoding, by adding 128 and xoring + with 32 as appropriate. Which to do is encoded in the + low byte, except that if the high byte is the special + value 126, then the low byte is actually the high byte + and both operations are performed. */ + f128 = TRUE; + f32 = TRUE; + if (ihigh == INDEX_MAX_HIGH) + iset = ilow * INDEX_MAX_LOW + INDEX_MAX_LOW - 1; + else + { + iset = ihigh * INDEX_MAX_LOW + ilow % INDEX_MAX_LOW; + if (ilow < INDEX_MAX_LOW) + f32 = FALSE; + else if (ilow < 2 * INDEX_MAX_LOW) + f128 = FALSE; + } + + /* Now iset is the index from the start of the data to the + byte to modify; adjust it to an index in abPrecbuf. */ + iset = (istart + CHDRLEN + iset) % CRECBUFLEN; + + if (f128) + abPrecbuf[iset] |= 0x80; + if (f32) + abPrecbuf[iset] ^= 0x20; + } + + /* Zero out the header and trailer to avoid confusing the 'i' + protocol, and update iPrecend to the end of decoded data. */ + for (i = 0, iget = istart; + i < CHDRLEN && iget != iJrecend; + ++i, iget = (iget + 1) % CRECBUFLEN) + abPrecbuf[iget] = 0; + abPrecbuf[iendindex] = 0; + iPrecend = (iendindex + 1) % CRECBUFLEN; + istart = iPrecend; + } + + if (pcneed != NULL) + *pcneed = CHDRLEN + 1; + return TRUE; +} diff --git a/gnu/libexec/uucp/uucico/prott.c b/gnu/libexec/uucp/uucico/prott.c new file mode 100644 index 0000000000..477ccf4f49 --- /dev/null +++ b/gnu/libexec/uucp/uucico/prott.c @@ -0,0 +1,330 @@ +/* prott.c + The 't' protocol. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char prott_rcsid[] = "$Id: prott.c,v 1.1 1993/08/04 19:36:24 jtc Exp $"; +#endif + +#include "uudefs.h" +#include "uuconf.h" +#include "conn.h" +#include "trans.h" +#include "system.h" +#include "prot.h" + +/* This implementation is based on code written by Rick Adams. + + This code implements the 't' protocol, which does no error checking + whatsoever and thus requires an end-to-end verified eight bit + communication line, such as is provided by TCP. Using it with a + modem is unadvisable, since errors can occur between the modem and + the computer. */ + +/* The buffer size we use. */ +#define CTBUFSIZE (1024) + +/* The offset in the buffer to the data. */ +#define CTFRAMELEN (4) + +/* Commands are sent in multiples of this size. */ +#define CTPACKSIZE (512) + +/* A pointer to the buffer we will use. */ +static char *zTbuf; + +/* True if we are receiving a file. */ +static boolean fTfile; + +/* The timeout we use. */ +static int cTtimeout = 120; + +struct uuconf_cmdtab asTproto_params[] = +{ + { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cTtimeout, NULL }, + { NULL, 0, NULL, NULL } +}; + +/* Local function. */ + +static boolean ftprocess_data P((struct sdaemon *qdaemon, boolean *pfexit, + size_t *pcneed)); + +/* Start the protocol. */ + +boolean +ftstart (qdaemon, pzlog) + struct sdaemon *qdaemon; + char **pzlog; +{ + *pzlog = NULL; + if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE, + STRIPSETTING_EIGHTBITS, XONXOFF_OFF)) + return FALSE; + zTbuf = (char *) xmalloc (CTBUFSIZE + CTFRAMELEN); + /* The first two bytes of the buffer are always zero. */ + zTbuf[0] = 0; + zTbuf[1] = 0; + fTfile = FALSE; + usysdep_sleep (2); + return TRUE; +} + +/* Stop the protocol. */ + +/*ARGSUSED*/ +boolean +ftshutdown (qdaemon) + struct sdaemon *qdaemon; +{ + xfree ((pointer) zTbuf); + zTbuf = NULL; + cTtimeout = 120; + return TRUE; +} + +/* Send a command string. We send everything up to and including the + null byte. The number of bytes we send must be a multiple of + TPACKSIZE. */ + +/*ARGSUSED*/ +boolean +ftsendcmd (qdaemon, z, ilocal, iremote) + struct sdaemon *qdaemon; + const char *z; + int ilocal; + int iremote; +{ + size_t clen, csend; + char *zalc; + boolean fret; + + DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "ftsendcmd: Sending command \"%s\"", z); + + clen = strlen (z); + + /* We need to send the smallest multiple of CTPACKSIZE which is + greater than clen (not equal to clen, since we need room for the + null byte). */ + csend = ((clen / CTPACKSIZE) + 1) * CTPACKSIZE; + + zalc = zbufalc (csend); + memcpy (zalc, z, clen); + bzero (zalc + clen, csend - clen); + + fret = fsend_data (qdaemon->qconn, zalc, csend, TRUE); + ubuffree (zalc); + return fret; +} + +/* Get space to be filled with data. We provide a buffer which has + four bytes at the start available to hold the length. */ + +/*ARGSIGNORED*/ +char * +ztgetspace (qdaemon, pclen) + struct sdaemon *qdaemon; + size_t *pclen; +{ + *pclen = CTBUFSIZE; + return zTbuf + CTFRAMELEN; +} + +/* Send out some data. We are allowed to modify the four bytes + preceding the buffer. This allows us to send the entire block with + header bytes in a single call. */ + +/*ARGSIGNORED*/ +boolean +ftsenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos) + struct sdaemon *qdaemon; + char *zdata; + size_t cdata; + int ilocal; + int iremote; + long ipos; +{ + /* Here we do htonl by hand, since it doesn't exist everywhere. We + know that the amount of data cannot be greater than CTBUFSIZE, so + the first two bytes of this value will always be 0. They were + set to 0 in ftstart so we don't touch them here. This is useful + because we cannot portably right shift by 24 or 16, since we + might be dealing with sixteen bit integers. */ + zdata[-2] = (char) ((cdata >> 8) & 0xff); + zdata[-1] = (char) (cdata & 0xff); + + /* We pass FALSE to fsend_data since we don't expect the other side + to be sending us anything just now. */ + return fsend_data (qdaemon->qconn, zdata - CTFRAMELEN, cdata + CTFRAMELEN, + FALSE); +} + +/* Process data and return the amount we need in *pfneed. */ + +static boolean +ftprocess_data (qdaemon, pfexit, pcneed) + struct sdaemon *qdaemon; + boolean *pfexit; + size_t *pcneed; +{ + int cinbuf, cfirst, clen; + + *pfexit = FALSE; + + cinbuf = iPrecend - iPrecstart; + if (cinbuf < 0) + cinbuf += CRECBUFLEN; + + if (! fTfile) + { + /* We are not receiving a file. Commands are read in chunks of + CTPACKSIZE. */ + while (cinbuf >= CTPACKSIZE) + { + cfirst = CRECBUFLEN - iPrecstart; + if (cfirst > CTPACKSIZE) + cfirst = CTPACKSIZE; + + DEBUG_MESSAGE1 (DEBUG_PROTO, + "ftprocess_data: Got %d command bytes", + cfirst); + + if (! fgot_data (qdaemon, abPrecbuf + iPrecstart, + (size_t) cfirst, abPrecbuf, + (size_t) CTPACKSIZE - cfirst, + -1, -1, (long) -1, TRUE, pfexit)) + return FALSE; + + iPrecstart = (iPrecstart + CTPACKSIZE) % CRECBUFLEN; + + if (*pfexit) + return TRUE; + + cinbuf -= CTPACKSIZE; + } + + if (pcneed != NULL) + *pcneed = CTPACKSIZE - cinbuf; + + return TRUE; + } + + /* Here we are receiving a file. The data comes in blocks. The + first four bytes contain the length, followed by that amount of + data. */ + + while (cinbuf >= CTFRAMELEN) + { + /* The length is stored in network byte order, MSB first. */ + + clen = (((((((abPrecbuf[iPrecstart] & 0xff) << 8) + + (abPrecbuf[(iPrecstart + 1) % CRECBUFLEN] & 0xff)) << 8) + + (abPrecbuf[(iPrecstart + 2) % CRECBUFLEN] & 0xff)) << 8) + + (abPrecbuf[(iPrecstart + 3) % CRECBUFLEN] & 0xff)); + + if (cinbuf < clen + CTFRAMELEN) + { + if (pcneed != NULL) + *pcneed = clen + CTFRAMELEN - cinbuf; + return TRUE; + } + + iPrecstart = (iPrecstart + CTFRAMELEN) % CRECBUFLEN; + + cfirst = CRECBUFLEN - iPrecstart; + if (cfirst > clen) + cfirst = clen; + + DEBUG_MESSAGE1 (DEBUG_PROTO, + "ftprocess_data: Got %d data bytes", + clen); + + if (! fgot_data (qdaemon, abPrecbuf + iPrecstart, + (size_t) cfirst, abPrecbuf, (size_t) (clen - cfirst), + -1, -1, (long) -1, TRUE, pfexit)) + return FALSE; + + iPrecstart = (iPrecstart + clen) % CRECBUFLEN; + + if (*pfexit) + return TRUE; + + cinbuf -= clen + CTFRAMELEN; + } + + if (pcneed != NULL) + *pcneed = CTFRAMELEN - cinbuf; + + return TRUE; +} + +/* Wait for data to come in and process it until we've reached the end + of a command or a file. */ + +boolean +ftwait (qdaemon) + struct sdaemon *qdaemon; +{ + while (TRUE) + { + boolean fexit; + size_t cneed, crec; + + if (! ftprocess_data (qdaemon, &fexit, &cneed)) + return FALSE; + if (fexit) + return TRUE; + + if (! freceive_data (qdaemon->qconn, cneed, &crec, cTtimeout, TRUE)) + return FALSE; + + if (crec == 0) + { + ulog (LOG_ERROR, "Timed out waiting for data"); + return FALSE; + } + } +} + +/* File level routine, to set fTfile correctly. */ + +/*ARGSUSED*/ +boolean +ftfile (qdaemon, qtrans, fstart, fsend, cbytes, pfhandled) + struct sdaemon *qdaemon; + struct stransfer *qtrans; + boolean fstart; + boolean fsend; + long cbytes; + boolean *pfhandled; +{ + *pfhandled = FALSE; + + if (! fsend) + fTfile = fstart; + + return TRUE; +} diff --git a/gnu/libexec/uucp/uucico/protz.c b/gnu/libexec/uucp/uucico/protz.c new file mode 100644 index 0000000000..e593e07e5b --- /dev/null +++ b/gnu/libexec/uucp/uucico/protz.c @@ -0,0 +1,2626 @@ +/* protz.c Version 1.5, 92Apr24 */ +/* Modified by Ian Lance Taylor for Taylor UUCP 1.04 92Aug4. */ + +/* + * Doug Evans, dje@sspiff.UUCP or dje@ersys.edmonton.ab.ca + * + * This file provides the Zmodem protocol (by Chuck Forsberg) for + * Ian Taylor's UUCP package. + * + * It was originally developed to establish a uucp link between myself and my + * employer: Ivation Datasystems, Inc. of Ottawa. + * + * My thanks to Ivation for letting me release this to the public. Given that + * Zmodem is in the public domain, no additional copyrights have been added. + * + ***************************************************************************** + * + * It's been difficult fitting Zmodem into the UUCP world. I have been guided + * mostly by trying to plug it into Taylor UUCP. Where "the Zmodem way of doing + * things" conflicted with "the UUCP way of doing things", I have err'd on the + * side of UUCP. At the end of it all, I have achieved something that will plug + * into Taylor UUCP very easily, but some might argue that I have corrupted Z + * too much. At any rate, compatibility with sz/rz was sacrificed to achieve a + * clean UUCP protocol. Given that, I took the opportunity to start from + * scratch when defining protocol constants (EG: ZBIN). + * + * 1) I wasn't quite sure how to enhance Zmodem to handle send+receive in one + * session, so I added a 'g' protocol like initialization sequence. This + * also gets this stuff out of the way, in case we ever try to support + * full-duplex. + * + * Caller Callee + * ------ ------ + * ZINIT --> <-- ZINIT + * ZDATA (ZCRCF) --> <-- ZDATA (ZCRCF) + * ZACK --> <-- ZACK + * ZINITEND --> <-- ZINITEND + * + * ZINIT is a combination of ZRINIT and ZSINIT and is intended to exchange + * simple protocol information (flags) and the protocol version number. + * ZDATA is intended to include window size information as well as the + * "Myattn" string (although at the moment it doesn't contain anything). + * ZDATA may contain at most 1k bytes of data and is sent out as one ZCRCF + * packet. Two ack's (ZACK + ZINITEND) are needed to ensure both sides have + * received ZDATA. + * + * 2) I've hardcoded several protocol parameters, like 32 bit CRC's for data. + * Others are not supported (we don't need them). + * + * 3) ZHEX headers use 32 bit CRC's. + * + * 4) Zmodem sends the ZFILE message "in one chunk". If there are errors, the + * entire string is resent. I have continued this practice. All UUCP + * commands are sent "in one chunk". This can be changed down the road if + * necessary. + * + * 5) The ZEOF message has been replaced with a new ZCRCx value: ZCRCF. ZCRCF + * is identical to ZCRCW except that it indicates the end of the message. + * The protocol here is *not* a file transfer protocol. It is an end to end + * transport protocol (that preserves message boundaries). + * + * 6) Zmodem handles restarting a file transfer, but as best as I can tell UUCP + * does not. At least Taylor UUCP doesn't. And if UUCP does start handling + * file restart, can it be plugged into the existing Zmodem way with zero + * changes? Beats me. Therefore I have removed this part of the code. One + * can always put it back in if and when UUCP handles it. Ditto for other + * pieces of removed code: there's no point in overly complicating this code + * when supporting all the bells and whistles requires enhancements to UUCP + * itself. + * + * *** It is easier to put code back in in an upward compatible manner *** + * *** than it is to correct for misunderstood code or poorly merged *** + * *** (Zmodem vs UUCP) code. *** + * + * 7) For the character in the initial "protocol selection" sequence, I have + * chosen 'a'. I'm told 'z' is already in use for something that isn't + * Zmodem. It's entirely reasonable to believe that if Zmodem ever becomes a + * standard UUCP protocol, this won't be it (so I'll leave z/Z for them). + * Publicly, this is the 'a' protocol. Internally, it is refered to as 'z'. + * A little confusing, I know. Maybe in time I'll refer to it internally as + * 'a', or maybe in time this will be *the* 'z' protocol. + * + * 8) Since we are writing a transport protocol, which isn't supposed to know + * anything about what is being transfered or where it is coming from, the + * header data value has changed meaning. It no longer means "file position" + * but instead means "window position". It is a running counter of the bytes + * transfered. Each "message" begins on a 1k boundary so the count isn't a + * precise byte count. The counter wraps every 4 gigabytes, although this + * wrapping isn't supported yet. + * + * FIXME: At present the max data transfered per session is 4 gigabytes. + * + **************************************************************************** + * + * A typical message sequence is (master sending file to slave): + * + * Master Slave + * ------ ----- + * ZDATA (S, ZCRCF) --> + * <-- ZACK + * <-- ZDATA (SY, ZCRCF) + * ZACK --> + * ZDATA --> + * ... <-- ZACK/ZRPOS + * ZDATA (ZCRCF) --> + * <-- ZACK + * <-- ZDATA (CY, ZCRCF) + * ZACK --> + * + * A typical message sequence is (master receiving file from slave): + * + * Master Slave + * ------ ----- + * ZDATA (R, ZCRCF) --> + * <-- ZACK + * <-- ZDATA (RY, ZCRCF) + * ZACK --> + * <-- ZDATA + * ZACK/ZRPOS ... --> + * <-- ZDATA (ZCRCF) + * ZACK --> + * ZDATA (CY, ZCRCF) --> + * <-- ZACK + * + ***************************************************************************** + * + * Notes: + * 1) For future bidirectional concerns, keep packet types "unidirectional". + * Sender always uses: ZDATA, ZNAK + * Receiver always uses: ZRPOS, ZACK + * There is no intersection. + * + * I'm not sure if this is necessary or even useful, but it seems to be. + * + * 2) I use to store the byte count / 32 in the data header. This left 5 bits + * unused for future concerns. I removed this because of the following + * situation when sending a file: + * + * ZDATA (ZCRCG, xx bytes) - received ok + * ZDATA (ZCRCF, 0 bytes) - corrupted + * + * At this point the receiver would like to send back a ZRPOS with a value + * of the size of the file. However, it can't because the value is divided + * by 32, and it would have to round up to the next multiple of 32. This + * seemed a little ugly, so I went with using the entire header to store + * the byte count. + * + ***************************************************************************** + * + * Source version: + * + * 1.1,2,3 + * Protocol version 0 + * Early attempts, completely rewritten later. + * + * 1.4 Protocol version 1 + * Beta test sent to Ian for analysis 92Apr18. + * + * 1.5 Protocol version 1 + * Released 92Apr24. + * + ***************************************************************************** + * + * Protocol version: + * + * A version number is exchanged in the ZINIT message, so it is possible to + * correct or enhance the protocol, without breaking existing versions. + * The purpose of this section is to document these versions as they come out. + * Remember, this is the protocol version, not the source version. + * + * 0 Initial version. + * Zmodem controlled file transfer. This was more of a "plug Z + * into UUCP as is" port. + * + * 1 Complete rewrite. + * Made Z more of a transport protocol. UUCP now controls transfer and Z + * is on the same footing as the other UUCP protocols. + * Theoretically, there will be little pain when UUCP goes bidirectional. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char protz_rcsid[] = "$Id: protz.c,v 1.1 1993/08/04 19:36:25 jtc Exp $"; +#endif + +#include + +#include "uudefs.h" +#include "uuconf.h" +#include "conn.h" +#include "trans.h" +#include "system.h" +#include "prot.h" + +#define ZPROTOCOL_VERSION 1 + +/* + * Control message characters ... + */ + +#define ZPAD '*' /* Padding character begins frames */ +#define ZDLE 030 /* Ctrl-X Zmodem escape - `ala BISYNC DLE */ +#define ZBIN 'A' /* Binary frame indicator */ +#define ZHEX 'B' /* HEX frame indicator */ + +/* + * Frame types (see array "frametypes" in zm.c) ... + * + * Note that the numbers here have been reorganized, as we don't support + * all of them (nor do we need to). + * + * WARNING: The init sequence assumes ZINIT < ZDATA < ZACK < ZINITEND. + */ + +#define ZINIT 0 /* Init (contains protocol version, flags) */ +#define ZDATA 1 /* Data packet(s) follow */ +#define ZRPOS 2 /* Resume data trans at this position */ +#define ZACK 3 /* ACK to above */ +#define ZNAK 4 /* Last packet was garbled */ +#define Zreserved 5 /* reserved (for future concerns) */ +#define ZINITEND 6 /* end of init sequence */ +#define ZFIN 7 /* Finish session */ + +/* + * ZDLE sequences ... + * + * Note addition of ZCRCF: "end of message". + */ + +#define ZCRCE 'h' /* CRC next, frame ends, header packet follows */ +#define ZCRCG 'i' /* CRC next, frame continues nonstop */ +#define ZCRCQ 'j' /* CRC next, frame continues, ZACK expected */ +#define ZCRCW 'k' /* CRC next, ZACK expected, end of frame */ +#define ZCRCF 'l' /* CRC next, ZACK expected, end of message */ + +#define ZRUB0 'm' /* Translate to rubout 0177 */ +#define ZRUB1 'n' /* Translate to rubout 0377 */ + + +/* + * zdlread return values (internal) ... + * Other values are ZM_ERROR, ZM_TIMEOUT, ZM_RCDO. + */ + +#define GOTOR 0400 +#define GOTCRCE (ZCRCE | GOTOR) /* ZDLE-ZCRCE received */ +#define GOTCRCG (ZCRCG | GOTOR) /* ZDLE-ZCRCG received */ +#define GOTCRCQ (ZCRCQ | GOTOR) /* ZDLE-ZCRCQ received */ +#define GOTCRCW (ZCRCW | GOTOR) /* ZDLE-ZCRCW received */ +#define GOTCRCF (ZCRCF | GOTOR) /* ZDLE-ZCRCF received */ + +/* + * Byte positions within header array ... + */ + +#define ZF0 3 /* First flags byte */ +#define ZF1 2 +#define ZF2 1 +#define ZF3 0 + +#define ZP0 0 /* Low order 8 bits of position */ +#define ZP1 1 +#define ZP2 2 +#define ZP3 3 /* High order 8 bits of position */ + +/* + * Bit Masks for ZRQINIT flags byte ZF0 ... + */ + +#define TX_ESCCTL 1 /* Tx will escape control chars */ + +/* + * Possible errors when running ZMODEM ... + */ + +#define ZM_ERROR (-1) /* crc error, etc. */ +#define ZM_TIMEOUT (-2) +#define ZM_RCDO (-3) /* Carrier Lost */ + +/* + * ASCII characters ... + */ + +#define LF 012 +#define CR 015 +#define XON 021 +#define XOFF 023 + +#define XON_WAIT 10 /* seconds */ + +/* + * Packet sizes ... + * + * FIXME: CPACKETSIZE is hardcoded in a lot of places. + * It's not clear to me whether changing it's value would be a + * "good thing" or not. But of course that doesn't excuse the hardcoding. + */ + +#define CPACKETSIZE 1024 /* max packet size (data only) */ +#define CFRAMELEN 12 /* header size */ +#define CSUFFIXLEN 10 /* suffix at end of data packets */ +#define CEXCHANGE_INIT_RETRIES 4 + +/* The header CRC value. */ + +#if ANSI_C +#define IHDRCRC 0xDEBB20E3UL +#else +#define IHDRCRC ((unsigned long) 0xDEBB20E3L) +#endif + +/* packet buffer size */ +#define CPACKBUFSIZE (CFRAMELEN + 2 * CPACKETSIZE + CSUFFIXLEN + 42 /*slop*/) + +/* + * Data types ... + */ + +typedef unsigned char achdrval_t[4]; +typedef unsigned long hdrval_t; +typedef unsigned long winpos_t; + +/* + * Configurable parms ... + * + * FIXME: isn't used yet. It may not be needed. + */ + +#define CTIMEOUT 10 +#define CRETRIES 10 +#define CSTARTUP_RETRIES 4 +#define CGARBAGE 2400 +#define CSEND_WINDOW 16384 +#define FESCAPE_CONTROL FALSE + +static int cZtimeout = CTIMEOUT; /* (seconds) */ +static int cZretries = CRETRIES; +static int cZstartup_retries = CSTARTUP_RETRIES; +static int cZmax_garbage = CGARBAGE; /* max garbage before header */ +static int cZtx_window = CSEND_WINDOW; /* our transmission window */ +static int cZrx_buf_len = 0; /* our reception buffer size */ +static boolean fZesc_ctl = FESCAPE_CONTROL; /* escape control chars */ + +struct uuconf_cmdtab asZproto_params[] = +{ + {"timeout", UUCONF_CMDTABTYPE_INT, (pointer) & cZtimeout, NULL}, + {"retries", UUCONF_CMDTABTYPE_INT, (pointer) & cZretries, NULL}, + {"startup-retries", UUCONF_CMDTABTYPE_INT, + (pointer) & cZstartup_retries, NULL}, + {"garbage", UUCONF_CMDTABTYPE_INT, (pointer) & cZmax_garbage, NULL}, + {"send-window", UUCONF_CMDTABTYPE_INT, (pointer) & cZtx_window, NULL}, + {"escape-control", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) & fZesc_ctl, + NULL}, + {NULL, 0, NULL, NULL} +}; + +/* + * Variables for statistic gathering ... + * + * We use to record the number of "packets" + * sent/received. Packets is in double quotes because some of them aren't full. + */ + +static unsigned long cZheaders_sent; +static unsigned long cZheaders_received; +static unsigned long cZbytes_resent; +static unsigned long cZtimeouts; +static unsigned long cZerrors; + +/* + * Data buffers ... + */ + +static char *zZtx_buf; /* transmit buffer */ + +static char *zZtx_packet_buf; /* raw outgoing packet data */ +static char *zZrx_packet_buf; /* raw incoming packet data */ + +/* + * Transmitter state variables ... + */ + +static unsigned cZblklen; /* data length in sent/received packets */ +static unsigned cZtxwspac; /* spacing between ZCRCQ requests */ +/*static unsigned cZblklen_override;*//* override value for */ +static unsigned cZtxwcnt; /* counter used to space ack requests */ +static unsigned cZrxwcnt; /* counter used to watch receiver's buf size */ +static winpos_t wpZtxstart; /* when message started */ +static winpos_t wpZtxpos; /* transmitter position */ +static winpos_t wpZlastsync; /* last offset to which we got a ZRPOS */ +static winpos_t wpZlrxpos; /* receiver's last reported offset */ +static winpos_t wpZrxpos; /* receiver file position */ + +static int iZlast_tx_data_packet; /* type of last ZDATA packet (ZCRCx) */ +static int iZjunk_count; /* amount of garbage characters received */ +static int iZtleft; /* for dynamic packet resizing */ + +static int iZbeenhereb4; /* times we've been ZRPOS'd to same place */ + +/* + * Receiver state variables ... + */ + +static winpos_t wpZrxbytes; /* receiver byte count */ +static int iZlast_rx_data_packet; /* last successfully received ZCRCx packet */ + +/* + * Misc. globals ... + */ + +static char xon = XON; + +#ifdef DJE_TESTING +int uucptest = -1; +int uucptest2; +int uucptestseed; +#endif + +/* + * Kludge!!! + * See fzfinish_tx(). Basically the next two globals are used to record the + * fact that we got a ZDATA, but aren't quite ready to process it. + */ + +static int iZpkt_rcvd_kludge; /* -1 if not valid */ +static hdrval_t hvZpkt_hdrval_kludge; + +/* + * Packet types ... + */ + +static const char *azZframe_types[] = { + "Carrier Lost", /* -3 */ + "Timeout", /* -2 */ + "Error", /* -1 */ +#define FTOFFSET 3 + "ZINIT", + "ZDATA", + "ZRPOS", + "ZACK", + "ZNAK", + "Zreserved", + "ZINITEND", + "ZFIN", + "UNKNOWN!!!" +}; +#define FTNUMBER (sizeof(azZframe_types) / sizeof(char *)) + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif +#define ZZHEADER_NAME(itype) \ + azZframe_types[min((itype) + FTOFFSET, FTNUMBER - 1)] + +/* + * Local functions ... + */ + +static boolean fzsend_data P((struct sdaemon *qdaemon, char *zdata, + size_t cdata, boolean fendofmessage)); +static boolean fzprocess P((struct sdaemon *qdaemon)); +static boolean fzstart_proto P((struct sdaemon *qdaemon)); +static int izexchange_init P((struct sdaemon *qdaemon, int send_type, + achdrval_t send_val, achdrval_t recv_val)); +static boolean fzshutdown_proto P((struct sdaemon *qdaemon)); +static boolean fzstart_tx P((void)); +static boolean fzfinish_tx P((struct sdaemon *qdaemon, long *plredo)); +static boolean fzstart_rx P((void)); +static boolean fzfinish_rx P((struct sdaemon *qdaemon)); +static boolean fzsend_hdr P((struct sdaemon *qdaemon, int ipkttype, + int ihdrtype, hdrval_t hdrval, + boolean fcheckreceive)); +static boolean fzsend_data_packet P((struct sdaemon *qdaemon, char *zdata, + size_t cdata, int frameend, + boolean fcheckreceive)); +static int czbuild_header P((char *zresult, int ipkttype, int ihdrtype, + hdrval_t hdrval)); +static int czbuild_data_packet P((char *zresult, const char *zdata, + size_t cdata, int frameend)); +/* + * The rest of the functions do not follow Ian's naming style. I have left + * the names the same as the original zm source. Over time, they may change. + */ +static int izrecv_hdr P((struct sdaemon *qdaemon, achdrval_t hdr)); +static int zrbhdr32 P((struct sdaemon *qdaemon, achdrval_t hdr)); +static int zrhhdr P((struct sdaemon *qdaemon, achdrval_t hdr)); +static int zrdat32 P((struct sdaemon *qdaemon, char *buf, int length, + int *iprxcount)); +static int getinsync P((struct sdaemon *qdaemon, boolean flag)); +static char *zputhex P((char *p, int ch)); +static char *zputchar P((char *p, int ch)); +static int zgethex P((struct sdaemon *qdaemon)); +static int zdlread P((struct sdaemon *qdaemon)); +static int noxrd7 P((struct sdaemon *qdaemon)); +static int realreadchar P((struct sdaemon *qdaemon, int timeout)); +static boolean fzreceive_ready P((void)); +static void stohdr P((hdrval_t pos, achdrval_t hdr)); +static hdrval_t rclhdr P((achdrval_t hdr)); +static hdrval_t hvzencode_data_hdr P((winpos_t cbytes)); +static void zdecode_data_hdr P((hdrval_t hdrval, winpos_t *pcbytes)); +static winpos_t lzupdate_rxpos P((achdrval_t rx_hdr, winpos_t rxpos, + winpos_t lrxpos, winpos_t txpos)); + +/* + * This macro replaces readchar() because it achieves a noticable speed up. The + * readchar() function has been renamed realreadchar(). Thanks to Ian for + * running this stuff through a profiler to find this out. Ian suggests further + * speed ups may be obtained by doing a similar thing in zrdat32(). + */ + +/* Assign the next character to b. */ +#define READCHAR(qdaemon, b, i) \ + (iPrecstart != iPrecend \ + ? ((b) = BUCHAR (abPrecbuf[iPrecstart]), \ + iPrecstart = (iPrecstart + 1) % CRECBUFLEN) \ + : ((b) = realreadchar ((qdaemon), (i)))) + +/************************************************************************/ + + +/* + * Start the protocol ... + */ + +boolean +fzstart(qdaemon, pzlog) +struct sdaemon *qdaemon; +char **pzlog; +{ + *pzlog = zbufalc (sizeof "protocol 'a' starting: , , , , , " + 100); + sprintf (*pzlog, "protocol 'a' starting: %d, %d, %d, %d, %d, %d", + cZtimeout, cZretries, cZstartup_retries, + cZmax_garbage, cZtx_window, fZesc_ctl); + + if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE, + STRIPSETTING_EIGHTBITS, XONXOFF_OFF)) + return FALSE; + + /* + * For now, we place tight restrictions on the size of the transmit + * window. This might be relaxed in the future. If it is relaxed, + * some of these tests will stay, some will go. That is why it is + * coded like it is. + */ + + if (cZtx_window % 1024 != 0 || + cZtx_window < 4096 || cZtx_window > 65536 || + 65536 % cZtx_window != 0 + ) { + ulog (LOG_ERROR, + "fzstart: cZtx_window not one of 4096, 8192, 16384, 32768, 65536"); + return FALSE; + } + + zZtx_buf = (char *) xmalloc (CPACKETSIZE); + zZtx_packet_buf = (char *) xmalloc (CPACKBUFSIZE); + zZrx_packet_buf = (char *) xmalloc (CPACKBUFSIZE); + + iZlast_tx_data_packet = -1; + iZlast_rx_data_packet = -1; + + wpZtxpos = wpZlrxpos = wpZrxpos = wpZrxbytes = 0; + cZtxwspac = cZtx_window / 4; + + cZheaders_sent = cZheaders_received = cZbytes_resent = 0; + cZtimeouts = cZerrors = 0; + + iZpkt_rcvd_kludge = -1; + +#if 0 + /* + * We ensure is at least 4k, so the following is + * unnecessary. It can be put back in later if needed. + */ + if (cZblklen_override > cZtxwspac + || (!cZblklen_override && cZtxwspac < 1024)) + cZblklen_override = cZtxwspac; +#endif + +#ifdef DJE_TESTING + { + extern int uucptest,uucptest2,uucptestseed; + FILE *f; + + if (uucptest == -1) { + f = fopen ("/usr/local/src/bin/uucp/uucptest", "r"); + if (f != NULL) { + fscanf (f, "%d %d %d", + &uucptestseed, &uucptest, &uucptest2); + fclose (f); + } + srand (uucptestseed); + } + } +#endif + + /* + * Fire up the protocol (exchange init messages) ... + */ + + if (!fzstart_proto (qdaemon)) + return FALSE; + + return TRUE; +} + +/* + * Stop the protocol ... + */ + +boolean +fzshutdown(qdaemon) +struct sdaemon *qdaemon; +{ + (void) fzshutdown_proto (qdaemon); + + xfree ((pointer) zZtx_buf); + xfree ((pointer) zZtx_packet_buf); + xfree ((pointer) zZrx_packet_buf); + zZtx_buf = NULL; + zZtx_packet_buf = NULL; + zZrx_packet_buf = NULL; + + /* + * Print some informative statistics ... + * + * I use the word "messages" here instead of "headers" because the + * latter is jargonese. + */ + + ulog (LOG_NORMAL, + "Protocol 'a' messages: sent %lu, received %lu", + cZheaders_sent, cZheaders_received); + ulog (LOG_NORMAL, + "Protocol 'a' packets: sent %lu, received %lu", + wpZtxpos / 1024, wpZrxbytes / 1024); + if (cZbytes_resent != 0 || cZtimeouts != 0 || cZerrors != 0) + ulog (LOG_NORMAL, + "Protocol 'a' errors: bytes resent %lu, timeouts %lu, errors %lu", + cZbytes_resent, cZtimeouts, cZerrors); + + /* + * Reset all the parameters to their default values, so that the + * protocol parameters used for this connection do not affect the + * next one. + */ + + cZtimeout = CTIMEOUT; + cZretries = CRETRIES; + cZstartup_retries = CSTARTUP_RETRIES; + cZmax_garbage = CGARBAGE; + cZtx_window = CSEND_WINDOW; + fZesc_ctl = FESCAPE_CONTROL; + + cZheaders_sent = cZheaders_received = cZbytes_resent = 0; + cZtimeouts = cZerrors = 0; + + return TRUE; +} + +/* + * Send a command string ... + * We send everything up to and including the null byte. + * + * We assume the command will fit in the outgoing data buffer. + * FIXME: A valid assumption? + */ + +/*ARGSUSED*/ +boolean +fzsendcmd(qdaemon, z, ilocal, iremote) +struct sdaemon *qdaemon; +const char *z; +int ilocal; +int iremote; +{ + size_t n,clen; + long lredo; + char *zbuf; + + clen = strlen (z) + 1; + + DEBUG_MESSAGE1 (DEBUG_PROTO, "fzsendcmd: sending command %s", z); + + if (!fzstart_tx ()) /* must be called before zzgetspace() */ + return FALSE; + + if ((zbuf = zzgetspace (qdaemon, &n)) == NULL) + return FALSE; + +#if DEBUG > 0 + if (clen > n) + ulog (LOG_FATAL, "fzsendcmd: clen > n"); +#endif + + strcpy (zbuf, z); + + /* + * Send it out ... + */ + + do { + if (!fzsend_data (qdaemon, zbuf, clen, TRUE)) + return FALSE; + if (!fzfinish_tx (qdaemon, &lredo)) + return FALSE; + } while (lredo >= 0); + + return fzprocess (qdaemon); +} + +/* + * Allocate a packet to send out ... + * + * Note that 'z' has dynamic packet resizing and that will range + * from 32 to 1024, in multiples of 2. + */ + +/*ARGSUSED*/ +char * +zzgetspace(qdaemon, pclen) +struct sdaemon *qdaemon; +size_t *pclen; +{ + *pclen = cZblklen; + return zZtx_buf; +} + +/* + * Send a block of data ... + * + * If (cdata == 0) then the end of the file has been reached. + */ + +/*ARGSUSED*/ +boolean +fzsenddata(qdaemon, zdata, cdata, ilocal, iremote, ipos) +struct sdaemon *qdaemon; +char *zdata; +size_t cdata; +int ilocal; +int iremote; +long ipos; +{ + DEBUG_MESSAGE1 (DEBUG_PROTO, "fzsenddata: %d bytes", cdata); + + if (! fzsend_data (qdaemon, zdata, cdata, cdata == 0)) + return FALSE; + return fzprocess (qdaemon); +} + +/* + * Send a block of data (command or file) ... + */ + +/* This should buffer the data internally. Until it does, it needs to + be able to reset the file position when it is called. This is + really ugly. */ +extern struct stransfer *qTsend; + +static boolean +fzsend_data(qdaemon, zdata, cdata, fendofmessage) +struct sdaemon *qdaemon; +char *zdata; +size_t cdata; +boolean fendofmessage; +{ + size_t n; + + if (iZlast_tx_data_packet == -1 || iZlast_tx_data_packet == ZCRCW) { + cZtxwcnt = cZrxwcnt = 0; + iZjunk_count = 0; + if (!fzsend_hdr (qdaemon, ZBIN, ZDATA, + hvzencode_data_hdr (wpZtxpos), TRUE)) + return FALSE; + } + + n = cdata; + + if (fendofmessage) + iZlast_tx_data_packet = ZCRCF; + else if (iZjunk_count > 3) + iZlast_tx_data_packet = ZCRCW; + else if (wpZtxpos == wpZlastsync) + iZlast_tx_data_packet = ZCRCW; + else if (cZrx_buf_len && (cZrxwcnt += n) >= cZrx_buf_len) + iZlast_tx_data_packet = ZCRCW; + else if ((cZtxwcnt += n) >= cZtxwspac) { + iZlast_tx_data_packet = ZCRCQ; + cZtxwcnt = 0; + } else + iZlast_tx_data_packet = ZCRCG; + + if (++iZtleft > 3) { + iZtleft = 0; + if (cZblklen < 1024) + cZblklen *= 2; +#if 0 /* is currently unnecessary */ + if (cZblklen_override && cZblklen > cZblklen_override) + cZblklen = cZblklen_override; +#endif + if (cZblklen > 1024) + cZblklen = 1024; + if (cZrx_buf_len && cZblklen > cZrx_buf_len) + cZblklen = cZrx_buf_len; + } + +#if DEBUG > 1 + if (FDEBUGGING(DEBUG_PROTO)) { + const char *type; + + switch (iZlast_tx_data_packet) { + case ZCRCW: type = "ZCRCW"; break; + case ZCRCG: type = "ZCRCG"; break; + case ZCRCQ: type = "ZCRCQ"; break; + case ZCRCE: type = "ZCRCE"; break; + case ZCRCF: type = "ZCRCF"; break; + default : type = "UNKNOWN!!!"; break; + } + DEBUG_MESSAGE3 (DEBUG_PROTO, + "fzsend_data: %s, pos 0x%lx, %d bytes", + type, wpZtxpos, n); + } +#endif + + if (!fzsend_data_packet (qdaemon, zdata, n, iZlast_tx_data_packet, + TRUE)) + return FALSE; + + wpZtxpos += n; + + if (iZlast_tx_data_packet == ZCRCW) { + /* + * FIXME: Ideally this would be done in fzprocess. However, it + * is only called if there is data pending which there + * may not be yet. I could have patched fploop() a bit but + * for now, I've done it like this. + */ + switch (getinsync (qdaemon, FALSE)) { + case ZACK: + break; + case ZRPOS: + if (qTsend == NULL + || ! ffileisopen (qTsend->e)) { + ulog (LOG_ERROR, "Can't reset non-file"); + return FALSE; + } + iZlast_tx_data_packet = -1; /* trigger ZDATA */ + DEBUG_MESSAGE1 (DEBUG_PROTO, + "fzsend_data: Seeking to %ld", + (long) (wpZrxpos - wpZtxstart)); + if (!ffileseek (qTsend->e, wpZrxpos - wpZtxstart)) { + ulog (LOG_ERROR, "seek: %s", strerror (errno)); + return FALSE; + } + break; + default: + return FALSE; + } + return TRUE; + } + + /* + * If we've reached the maximum transmit window size, let the + * receiver catch up ... + * + * I use (cZtx_window - 2048) to play it safe. + */ + + while (wpZtxpos - wpZlrxpos >= cZtx_window - 2048) { + if (iZlast_tx_data_packet != ZCRCQ) { + if (!fzsend_data_packet (qdaemon, zdata, (size_t) 0, + iZlast_tx_data_packet = ZCRCQ, + TRUE)) + return FALSE; + } + /* + * FIXME: I'd rather not call ffileseek() in this file. When we + * start buffering the outgoing data, the following + * ffileseek() will disappear. + */ + switch (getinsync (qdaemon, TRUE)) { + case ZACK: + break; + case ZRPOS: + if (qTsend == NULL + || ! ffileisopen (qTsend->e)) { + ulog (LOG_ERROR, "Can't reset non-file"); + return FALSE; + } + iZlast_tx_data_packet = -1; /* trigger ZDATA */ + DEBUG_MESSAGE1 (DEBUG_PROTO, + "fzsend_data: Seeking to %ld", + (long) (wpZrxpos - wpZtxstart)); + if (!ffileseek (qTsend->e, wpZrxpos - wpZtxstart)) { + ulog (LOG_ERROR, "seek: %s", strerror (errno)); + return FALSE; + } + break; + default: + return FALSE; + } + } + + return TRUE; +} + +/* + * Process existing data ... + */ + +static boolean +fzprocess(qdaemon) +struct sdaemon *qdaemon; +{ + int c,ch; + + while (fzreceive_ready ()) { + READCHAR (qdaemon, ch, 1); + switch (ch) { + case ZPAD: + /* see if we're detecting ZRPOS packets quickly */ + DEBUG_MESSAGE0 (DEBUG_PROTO, + "fzprocess: possible ZRPOS packet"); + /* We just ate the ZPAD char that getinsync + expects, so put it back. */ + iPrecstart = ((iPrecstart + CRECBUFLEN - 1) + % CRECBUFLEN); + c = getinsync (qdaemon, TRUE); + if (c == ZACK) + break; + /* FIXME: sz does a TCFLSH here */ +#if 0 /* FIXME: Not sure if this is needed, or where to put it. */ + /* ZCRCE - dinna wanna starta ping-pong game */ + if (!fzsend_data_packet (qdaemon, zZtx_packet_buf, + 0, ZCRCE, TRUE)) + return FALSE; +#endif + if (c == ZRPOS) { + if (qTsend == NULL + || ! ffileisopen (qTsend->e)) { + ulog (LOG_ERROR, + "Attempt to back up non-file"); + return FALSE; + } + if (! ffileseek (qTsend->e, + wpZrxpos - wpZtxstart)) { + ulog (LOG_ERROR, + "seek: %s", strerror (errno)); + return FALSE; + } + iZlast_tx_data_packet = -1; /* trigger ZDATA */ + break; /* not returning is intentional */ + } + return FALSE; + case XOFF: + case XOFF | 0200: + READCHAR (qdaemon, ch, XON_WAIT); + break; + case CR: + break; + default: + iZjunk_count++; + break; + } + } + + return TRUE; +} + +/* + * Wait for data to come in. + * + * This continues processing until a complete file or command has been + * received. + */ + +boolean +fzwait(qdaemon) +struct sdaemon *qdaemon; +{ + int c,cerr,rxcount; + boolean fexit; + achdrval_t rx_hdr; + + if (!fzstart_rx ()) + return FALSE; + + cerr = cZretries; + + goto nxthdr; + + for (;;) { + if (!fzsend_hdr (qdaemon, ZHEX, ZRPOS, + hvzencode_data_hdr (wpZrxbytes), FALSE)) + return FALSE; +nxthdr: + c = izrecv_hdr (qdaemon, rx_hdr); + + switch (c) { + case ZM_TIMEOUT: + case ZNAK: + if (--cerr < 0) { + ulog (LOG_ERROR, "fzwait: retries exhausted"); + return FALSE; + } + continue; + case ZM_ERROR: + if (--cerr < 0) { + ulog (LOG_ERROR, "fzwait: retries exhausted"); + return FALSE; + } + /*fport_break ();*/ + continue; + case ZM_RCDO: + case ZFIN: + return FALSE; + case ZRPOS: + case ZACK: + goto nxthdr; /* ignore, partner is out of sync */ + case ZDATA: { + winpos_t rx_bytes; + + zdecode_data_hdr (rclhdr (rx_hdr), &rx_bytes); + DEBUG_MESSAGE2 (DEBUG_PROTO, + "fzwait: bytes(us,them) 0x%lx,0x%lx", + wpZrxbytes, rx_bytes); + if (rx_bytes != wpZrxbytes) { + if (--cerr < 0) { + ulog (LOG_ERROR, + "fzwait: retries exhausted"); + return FALSE; + } + (void) zrdat32 (qdaemon, zZrx_packet_buf, + 1024, &rxcount); + /*fport_break ();*/ + /* + * FIXME: Seems to me we should ignore this one + * and go for a timeout, the theory being + * that the appropriate ZRPOS has already + * been sent. We're obviously out of sync. + * /dje 92Mar10 + */ + continue; /* goto nxthdr? */ + } +moredata: + /* + * Do not call fgot_data() with (rxcount == 0) if it's + * not ZCRCF. fgot_data() will erroneously think this + * is the end of the message. + */ + c = zrdat32 (qdaemon, zZrx_packet_buf, 1024, + &rxcount); +#if DEBUG > 1 + if (FDEBUGGING(DEBUG_PROTO)) { + const char *msg; + + if (c < 0) { + msg = ZZHEADER_NAME(c); + } else { + switch (c) { + case GOTCRCW: msg = "ZCRCW"; break; + case GOTCRCG: msg = "ZCRCG"; break; + case GOTCRCQ: msg = "ZCRCQ"; break; + case GOTCRCE: msg = "ZCRCE"; break; + case GOTCRCF: msg = "ZCRCF"; break; + default : msg = NULL; break; + } + } + if (msg != NULL) + DEBUG_MESSAGE2 (DEBUG_PROTO, + "fzwait: zrdat32: %s, %d bytes", + msg, rxcount); + else + DEBUG_MESSAGE2 (DEBUG_PROTO, + "fzwait: zrdat32: %d, %d bytes", + c, rxcount); + } +#endif + switch (c) { + case ZM_ERROR: /* CRC error */ + cZerrors++; + if (--cerr < 0) { + ulog (LOG_ERROR, + "fzwait: retries exhausted"); + return FALSE; + } + /*fport_break ();*/ + continue; + case ZM_TIMEOUT: + cZtimeouts++; + if (--cerr < 0) { + ulog (LOG_ERROR, + "fzwait: retries exhausted"); + return FALSE; + } + continue; + case ZM_RCDO: + return FALSE; + case GOTCRCW: + iZlast_rx_data_packet = ZCRCW; + cerr = cZretries; + if (rxcount != 0 + && !fgot_data (qdaemon, zZrx_packet_buf, + (size_t) rxcount, + (const char *) NULL, + (size_t) 0, + -1, -1, (long) -1, + TRUE, &fexit)) + return FALSE; + wpZrxbytes += rxcount; + if (!fzsend_hdr (qdaemon, ZHEX, ZACK, + hvzencode_data_hdr (wpZrxbytes), + FALSE)) + return FALSE; + if (! fsend_data (qdaemon->qconn, &xon, + (size_t) 1, FALSE)) + return FALSE; + goto nxthdr; + case GOTCRCQ: + iZlast_rx_data_packet = ZCRCQ; + cerr = cZretries; + if (rxcount != 0 + && !fgot_data (qdaemon, zZrx_packet_buf, + (size_t) rxcount, + (const char *) NULL, + (size_t) 0, + -1, -1, (long) -1, + TRUE, &fexit)) + return FALSE; + wpZrxbytes += rxcount; + if (!fzsend_hdr (qdaemon, ZHEX, ZACK, + hvzencode_data_hdr (wpZrxbytes), + FALSE)) + return FALSE; + goto moredata; + case GOTCRCG: + iZlast_rx_data_packet = ZCRCG; + cerr = cZretries; + if (rxcount != 0 + && !fgot_data (qdaemon, zZrx_packet_buf, + (size_t) rxcount, + (const char *) NULL, + (size_t) 0, + -1, -1, (long) -1, + TRUE, &fexit)) + return FALSE; + wpZrxbytes += rxcount; + goto moredata; + case GOTCRCE: + iZlast_rx_data_packet = ZCRCE; + cerr = cZretries; + if (rxcount != 0 + && !fgot_data (qdaemon, zZrx_packet_buf, + (size_t) rxcount, + (const char *) NULL, + (size_t) 0, + -1, -1, (long) -1, + TRUE, &fexit)) + return FALSE; + wpZrxbytes += rxcount; + goto nxthdr; + case GOTCRCF: + iZlast_rx_data_packet = ZCRCF; + /* + * fzfinish_rx() must be called before + * fgot_data() because fgot_data() will send + * out a UUCP-command but the sender won't be + * ready for it until it receives our final + * ZACK. + */ + cerr = cZretries; + wpZrxbytes += rxcount; + if (!fzfinish_rx (qdaemon)) + return FALSE; + if (!fgot_data (qdaemon, zZrx_packet_buf, + (size_t) rxcount, + (const char *) NULL, + (size_t) 0, -1, -1, + (long) -1, TRUE, &fexit)) + return FALSE; + /* + * FIXME: Examine ? + * Or maybe ensure it's TRUE? + */ + return TRUE; + } + return FALSE; + } + default: + ulog (LOG_FATAL, "fzwait: received header %s", + ZZHEADER_NAME(c)); + return FALSE; + } + } + + return TRUE; +} + +/* + * File level routine. Called when initiating/terminating file transfers. + * + * When starting to send a file: (TRUE, TRUE, cbytes) + * When starting to receive a file: (TRUE, FALSE, -1) + * When send EOF, check resend: (FALSE, TRUE, -1) + * When receive EOF, check re-receive: (FALSE, FALSE, -1) + */ + +boolean +fzfile(qdaemon, qtrans, fstart, fsend, cbytes, pfhandled) +struct sdaemon *qdaemon; +struct stransfer *qtrans; +boolean fstart; +boolean fsend; +long cbytes; +boolean *pfhandled; +{ + long iredo; + + *pfhandled = FALSE; + + DEBUG_MESSAGE2 (DEBUG_PROTO, "fzfile: fstart=%d, fsend=%d", fstart, + fsend); + + if (fsend) { + if (fstart) + return fzstart_tx (); + if (! fzfinish_tx (qdaemon, &iredo)) + return FALSE; + if (iredo >= 0) { + if (! ffileisopen (qtrans->e)) { + ulog (LOG_ERROR, + "Attempt to back up non-file"); + return FALSE; + } + if (! ffileseek (qtrans->e, iredo)) { + ulog (LOG_ERROR, + "seek: %s", strerror (errno)); + return FALSE; + } + *pfhandled = TRUE; + qtrans->fsendfile = TRUE; + return fqueue_send (qdaemon, qtrans); + } + } + + return TRUE; +} + +/****************************************************************************/ + + +#if 0 /* not used, we only use 32 bit crc's */ +/* + * crctab calculated by Mark G. Mendel, Network Systems Corporation + */ + +static unsigned short crctab[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; +#endif /* crctab */ + +/* + * Copyright (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + */ + +/* First, the polynomial itself and its table of feedback terms. The */ +/* polynomial is */ +/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ +/* Note that we take it "backwards" and put the highest-order term in */ +/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ +/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ +/* the MSB being 1. */ + +/* Note that the usual hardware shift register implementation, which */ +/* is what we're using (we're merely optimizing it by doing eight-bit */ +/* chunks at a time) shifts bits into the lowest-order term. In our */ +/* implementation, that means shifting towards the right. Why do we */ +/* do it this way? Because the calculated CRC must be transmitted in */ +/* order from highest-order term to lowest-order term. UARTs transmit */ +/* characters in order from LSB to MSB. By storing the CRC this way, */ +/* we hand it to the UART in the order low-byte to high-byte; the UART */ +/* sends each low-bit to hight-bit; and the result is transmission bit */ +/* by bit from highest- to lowest-order term without requiring any bit */ +/* shuffling on our part. Reception works similarly. */ + +/* The feedback terms table consists of 256, 32-bit entries. Notes: */ +/* */ +/* The table can be generated at runtime if desired; code to do so */ +/* is shown later. It might not be obvious, but the feedback */ +/* terms simply represent the results of eight shift/xor opera- */ +/* tions for all combinations of data and CRC register values. */ +/* */ +/* The values must be right-shifted by eight bits by the "updcrc" */ +/* logic; the shift must be unsigned (bring in zeroes). On some */ +/* hardware you could probably optimize the shift in assembler by */ +/* using byte-swap instructions. */ + +static unsigned long crc_32_tab[] = { /* CRC polynomial 0xedb88320 */ + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, + 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, + 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, + 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, + 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, + 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, + 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, + 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, + 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, + 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, + 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, + 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, + 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, + 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, + 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, + 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, + 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, + 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, + 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, + 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, + 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, + 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, + 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, + 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, + 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, + 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, + 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, + 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, + 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, + 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, + 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, + 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, + 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, + 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, + 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, + 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, + 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, + 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, + 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, + 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, + 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, + 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, + 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, + 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, + 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, + 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, + 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, + 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, + 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, + 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, + 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, + 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL +}; + +/* + * updcrc macro derived from article Copyright (C) 1986 Stephen Satchell. + * NOTE: First argument must be in range 0 to 255. + * Second argument is referenced twice. + * + * Programmers may incorporate any or all code into their programs, + * giving proper credit within the source. Publication of the + * source routines is permitted so long as proper credit is given + * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg, + * Omen Technology. + */ + +#define updcrc(cp, crc) (crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp) + +#define UPDC32(b, crc) \ + (crc_32_tab[((unsigned)(crc) ^ (unsigned)(b)) & 0xff] \ + ^ (((crc) >> 8) & 0x00ffffffL)) + +/****************************************************************************/ + +/* + * This section contains the guts of the Zmodem protocol. The intention + * is to leave as much of it alone as possible at the start. Overtime it + * will be cleaned up (EG: I'd like to clean up the naming of the globals). + * Also, Zmodem has a different coding style. Over time this will be converted + * to the Taylor UUCP coding style. + */ + +/* + * Start the protocol (exchange init packets) ... + * + * UUCP can transfer files in both directions in one session. Therefore the + * init sequence is a little different. + * + * 1) ZINIT packets are exchanged + * - contains protocol version and protocol flags + * 2) ZDATA packets are exchanged + * - is intended to contain various numeric and string information + * 3) ZACK packets are exchanged + * 4) ZINITEND packets are exchanged + */ + +static boolean +fzstart_proto(qdaemon) +struct sdaemon *qdaemon; +{ + int i; + achdrval_t tx_hdr,rx_hdr; + + for (i = 0; i < cZstartup_retries; i++) { + stohdr (0L, tx_hdr); + tx_hdr[ZF0] = ZPROTOCOL_VERSION; + if (fZesc_ctl) + tx_hdr[ZF1] |= TX_ESCCTL; + switch (izexchange_init (qdaemon, ZINIT, tx_hdr, rx_hdr)) { + case -1: return FALSE; + case 0: continue; + case 1: break; + } +#if 0 /* can't work, but kept for documentation */ + if (rx_hdr[ZF0] == 0) { + ulog (LOG_ERROR, "Old protocol version, init failed"); + return FALSE; + } +#endif + fZesc_ctl = fZesc_ctl || (rx_hdr[ZF1] & TX_ESCCTL) != 0; + + stohdr (0L, tx_hdr); + switch (izexchange_init (qdaemon, ZDATA, tx_hdr, rx_hdr)) { + case -1: return FALSE; + case 0: continue; + case 1: break; + } + + stohdr (0L, tx_hdr); + switch (izexchange_init (qdaemon, ZACK, tx_hdr, rx_hdr)) { + case -1: return FALSE; + case 0: continue; + case 1: break; + } + + stohdr (0L, tx_hdr); + switch (izexchange_init (qdaemon, ZINITEND, tx_hdr, rx_hdr)) { + case -1: return FALSE; + case 0: continue; + case 1: break; + } + + DEBUG_MESSAGE0 (DEBUG_PROTO, + "fzstart_proto: Protocol started"); + return TRUE; + + /* FIXME: see protg.c regarding sequencing here. */ + } + + ulog (LOG_ERROR, "Protocol init failed"); + return FALSE; +} + +/* + * Exchange init messages. This is based on 'g'. + * See the comments concerning fgexchange_init() in protg.c. + * + * We return 1 for success, 0 for restart, -1 for comm failure (terminate). + */ + +static int +izexchange_init(qdaemon, send_type, send_val, recv_val) +struct sdaemon *qdaemon; +int send_type; +achdrval_t send_val; +achdrval_t recv_val; +{ + int i,recv_type,count; + + for (i = 0; i < CEXCHANGE_INIT_RETRIES; i++) { + if (!fzsend_hdr (qdaemon, send_type == ZDATA ? ZBIN : ZHEX, + send_type, rclhdr (send_val), FALSE)) + return -1; + + /* + * The ZDATA packet is intended to contain the string + * (eventually, if it's ever usable) and allow for anything + * else that will need to be thrown in. + */ + + if (send_type == ZDATA) { + count = czbuild_data_packet (zZtx_packet_buf, "", + (size_t) 1, ZCRCF); + if (!fsend_data (qdaemon->qconn, zZtx_packet_buf, + (size_t) count, FALSE)) + return -1; + } + + recv_type = izrecv_hdr (qdaemon, recv_val); + + switch (recv_type) { + case ZM_TIMEOUT: + case ZM_ERROR: + continue; + case ZM_RCDO: + case ZFIN: + return -1; + case ZINIT: + case ZACK: + case ZINITEND: + break; + case ZDATA: + if (zrdat32 (qdaemon, zZrx_packet_buf, 1024, &count) + == GOTCRCF) + break; + continue; + default: + continue; + } + + if (recv_type == send_type) + return 1; + + /* + * If the other side is farther along than we are, we have lost + * a packet. Fall immediately back to ZINIT (but don't fail + * if we are already doing ZINIT, since that would count + * against cStart_retries more than it should). + * + * FIXME: The ">" test is "<" in protg.c. Check who's right. + */ + + if (recv_type > send_type && send_type != ZINIT) + return 0; + + /* + * If we are sending ZINITEND and we receive an ZINIT, the + * other side has falled back (we know this because we have + * seen a ZINIT from them). Fall back ourselves to start + * the whole handshake over again. + */ + + if (recv_type == ZINIT && send_type == ZINITEND) + return 0; + } + + return 0; +} + +/* + * Shut down the protocol ... + */ + +static boolean +fzshutdown_proto(qdaemon) +struct sdaemon *qdaemon; +{ + (void) fzsend_hdr (qdaemon, ZHEX, ZFIN, 0L, FALSE); + return TRUE; +} + +/* + * Reset the transmitter side for sending a new message ... + */ + +static boolean +fzstart_tx() +{ + iZlast_tx_data_packet = -1; + + /* + * is set to -1L to suppress ZCRCW request otherwise + * triggered by (wpZlastsync == wpZtxpos). + */ + + cZblklen = 1024; + wpZlastsync = -1L; + iZbeenhereb4 = 0; + iZtleft = 0; + iZjunk_count = 0; + + wpZtxpos = (wpZtxpos + 1024L) & ~1023L; /* next packet boundary */ + wpZlrxpos = wpZrxpos = wpZtxpos; + + wpZtxstart = wpZtxpos; /* so we can compute the "file offset" */ + + return TRUE; +} + +/* + * Finish the sending of a message ... + * + * Basically, we wait for some indication that the receiver received our last + * message. If the receiver tells us to restart from some point, we set + * *plredo to that point. + * + * FIXME: This function is a major kludge at the moment. It is taken from + * getinsync(). It is necessary because I don't yet buffer outgoing data. + * It will go away when we do (buffer outgoing data). + */ + +static boolean +fzfinish_tx(qdaemon, plredo) +struct sdaemon *qdaemon; +long *plredo; +{ + int c,cerr,ctimeouts; + achdrval_t rx_hdr; + winpos_t rx_bytes; + + *plredo = -1; + cerr = cZretries; + ctimeouts = 0; + + DEBUG_MESSAGE4 (DEBUG_PROTO, + "fzfinish_tx: txpos=0x%lx, rxpos=0x%lx, lrxpos=0x%lx, rxbytes=0x%lx", + wpZtxpos, wpZrxpos, wpZlrxpos, wpZrxbytes); + + for (;;) { + c = izrecv_hdr (qdaemon, rx_hdr); + + switch (c) { + case ZRPOS: + wpZrxpos = lzupdate_rxpos (rx_hdr, wpZrxpos, + wpZlrxpos, wpZtxpos); + /* + * If the receiver sends a ZRPOS for the 1k block after + * the one we're currently at, we lost the final ZACK. + * We cheat and ignore this ZRPOS. Remember: the theory + * is that this entire function will go away when we + * begin buffering the outgoing data. Of course, one + * can reword the protocol definition and say this + * isn't cheating at all. + */ + if (((wpZtxpos + 1024) & ~1023) == wpZrxpos) + return TRUE; + cZbytes_resent += wpZtxpos - wpZrxpos; + wpZlrxpos = wpZtxpos = wpZrxpos; + if (wpZlastsync == wpZrxpos) { + if (++iZbeenhereb4 > 4) + if (cZblklen > 32) + cZblklen /= 2; + /* FIXME: shouldn't we reset iZbeenhereb4? */ + } + wpZlastsync = wpZrxpos; + iZlast_tx_data_packet = ZCRCW; /* force a timeout */ + *plredo = wpZrxpos - wpZtxstart; + return TRUE; + case ZACK: + wpZrxpos = lzupdate_rxpos (rx_hdr, wpZrxpos, + wpZlrxpos, wpZtxpos); + wpZlrxpos = wpZrxpos; + if (wpZtxpos == wpZrxpos) /* the ACK we want? */ + return TRUE; + break; + case ZDATA: + /* + * We cheat here and take advantage of UUCP's current + * half duplex nature. If we get a ZDATA starting on + * the next 1k boundary, we lost the ZACK. We cheat and + * tuck it away so that izrecv_hdr() can later detect + * it. Remember: see above. + */ + zdecode_data_hdr (rclhdr (rx_hdr), &rx_bytes); + if (((wpZrxbytes + 1024L) & ~1023L) == rx_bytes) { + iZpkt_rcvd_kludge = ZDATA; + hvZpkt_hdrval_kludge = rclhdr (rx_hdr); + return TRUE; + } + break; /* ignore, out of sync (old) */ + case ZNAK: + /* + * We cheat here and take advantage of UUCP's current + * half duplex nature. If we get a ZNAK starting on + * the next 1k boundary, we lost the ZACK. We cheat and + * throw the ZNAK away. Remember: see above. + * + * On the other hand, if (rx_bytes == wpZrxbytes) then + * the other side is also in fzfinish_tx(). He must + * have lost our ZACK, so we send him another. + */ + zdecode_data_hdr (rclhdr (rx_hdr), &rx_bytes); + if (((wpZrxbytes + 1024L) & ~1023L) == rx_bytes) + return TRUE; + if (rx_bytes == wpZrxbytes) { + if (!fzsend_hdr (qdaemon, ZHEX, ZACK, + hvzencode_data_hdr (wpZrxbytes), + TRUE)) + return FALSE; + } + break; /* ignore, out of sync (old) */ + case ZFIN: + case ZM_RCDO: + return FALSE; + case ZM_TIMEOUT: + if (--cerr < 0) { + ulog (LOG_ERROR, + "fzfinish_tx: retries exhausted"); + return FALSE; + } + /* + * Normally the sender doesn't send NAK's for timeouts. + * We have to here because of the following scenario: + * + * - We send ZDATA/ZCRCF + * - They send ZACK (corrupted) + * - They send ZDATA/ZCRCF (corrupted) + * + * At this point, both sides are in fzfinish_tx(). + * We only send ZNAK every second timeout to increase + * our timeout delay vs. our partner. This tries to + * avoid ZRPOS and ZNAK "passing in transit". + */ + if (++ctimeouts % 2 == 0) + if (!fzsend_hdr (qdaemon, ZHEX, ZNAK, + hvzencode_data_hdr (wpZtxpos), + TRUE)) + return FALSE; + break; + case ZM_ERROR: + default: + if (--cerr < 0) { + ulog (LOG_ERROR, + "fzfinish_tx: retries exhausted"); + return FALSE; + } + if (!fzsend_hdr (qdaemon, ZHEX, ZNAK, + hvzencode_data_hdr (wpZtxpos), + TRUE)) + return FALSE; + break; + } + } +} + +/* + * Initialize the receiver ... + */ + +static boolean +fzstart_rx() +{ + wpZrxbytes = (wpZrxbytes + 1024L) & ~1023L; /* next packet boundary */ + + return TRUE; +} + +/* + * Terminate the receiver ... + * + * Acknowledge the last packet received. + */ + +static boolean +fzfinish_rx(qdaemon) +struct sdaemon *qdaemon; +{ + DEBUG_MESSAGE0 (DEBUG_PROTO, "fzfinish_rx: message/file received"); + + return fzsend_hdr (qdaemon, ZHEX, ZACK, + hvzencode_data_hdr (wpZrxbytes), FALSE); +} + +/* + * Send a Zmodem header to our partner ... + */ + +static boolean +fzsend_hdr(qdaemon, ipkttype, ihdrtype, hdrval, fcheckreceive) +struct sdaemon *qdaemon; +int ipkttype; +int ihdrtype; +hdrval_t hdrval; +boolean fcheckreceive; +{ + int cpacketlen; + + DEBUG_MESSAGE2 (DEBUG_PROTO, "fzsend_hdr: %s, data = 0x%lx", + ZZHEADER_NAME(ihdrtype), hdrval); + + cpacketlen = czbuild_header (zZtx_packet_buf, ipkttype, + ihdrtype, hdrval); + +#ifdef DJE_TESTING +#if 0 + if (ihdrtype == ZACK && rand () % 100 < uucptest2) { + cZheaders_sent++; + return TRUE; + } +#else + if (ihdrtype == ZACK || ihdrtype == ZDATA) { + boolean fresult; + int old; + extern int uucptest,uucptest2; + + old = uucptest; + uucptest = uucptest2; + cZheaders_sent++; + fresult = fsend_data (qdaemon->qconn, zZtx_packet_buf, + (size_t) cpacketlen, fcheckreceive); + uucptest = old; + return fresult; + } +#endif +#endif + cZheaders_sent++; + return fsend_data (qdaemon->qconn, zZtx_packet_buf, + (size_t) cpacketlen, fcheckreceive); +} + +/* + * Send a data packet to our partner ... + * is one of ZCRCx. + */ + +static boolean +fzsend_data_packet(qdaemon, zdata, cdata, frameend, fcheckreceive) +struct sdaemon *qdaemon; +char *zdata; +size_t cdata; +int frameend; +boolean fcheckreceive; +{ + int cpacketlen; + + cpacketlen = czbuild_data_packet (zZtx_packet_buf, zdata, cdata, + frameend); + + return fsend_data (qdaemon->qconn, zZtx_packet_buf, + (size_t) cpacketlen, fcheckreceive); +} + +/* + * Build Zmodem headers ... + * + * Note that we use 32 bit CRC's for ZHEX headers. + * + * This function is a combination of zm fns: zsbhdr(), zsbh32(), and zshhdr(). + */ + +static int +czbuild_header(zresult, ipkttype, ihdrtype, hdrval) +char *zresult; +int ipkttype; +int ihdrtype; +hdrval_t hdrval; +{ + char *p; + int i; + unsigned long crc; + achdrval_t achdrval; + + p = zresult; + + switch (ipkttype) { + case ZBIN: + *p++ = ZPAD; + *p++ = ZDLE; + *p++ = ZBIN; + p = zputchar (p, ihdrtype); + crc = ICRCINIT; + crc = UPDC32 (ihdrtype, crc); + stohdr (hdrval, achdrval); + for (i = 0; i < 4; i++) { + p = zputchar (p, achdrval[i]); + crc = UPDC32 (achdrval[i], crc); + } + crc = ~crc; + for (i = 0; i < 4; i++) { + p = zputchar (p, (char) crc); + crc >>= 8; + } + break; + case ZHEX: /* build hex header */ + *p++ = ZPAD; + *p++ = ZPAD; + *p++ = ZDLE; + *p++ = ZHEX; + p = zputhex (p, ihdrtype); + crc = ICRCINIT; + crc = UPDC32 (ihdrtype, crc); + stohdr (hdrval, achdrval); + for (i = 0; i < 4; i++) { + p = zputhex (p, achdrval[i]); + crc = UPDC32 (achdrval[i], crc); + } + crc = ~crc; + for (i = 0; i < 4; i++) { + p = zputhex (p, (char) crc); + crc >>= 8; + } + *p++ = CR; + /* + * Uncork the remote in case a fake XOFF has stopped data flow. + */ + if (ihdrtype != ZFIN && ihdrtype != ZACK) /* FIXME: why? */ + *p++ = XON; + break; + default: + ulog (LOG_FATAL, "czbuild_header: ipkttype == %d", ipkttype); + break; + } + + return p - zresult; +} + +/* + * Build Zmodem data packets ... + * + * This function is zsdata() and zsda32() from the zm source. + */ + +static int +czbuild_data_packet(zresult, zdata, cdata, frameend) +char *zresult; +const char *zdata; +size_t cdata; +int frameend; +{ + char *p; + unsigned long crc; + + p = zresult; + + crc = ICRCINIT; + for ( ; cdata-- != 0; zdata++) { + char c; + + c = *zdata; + if (c & 0140) + *p++ = c; + else + p = zputchar (p, c); + crc = UPDC32 ((unsigned char) c, crc); + } + *p++ = ZDLE; + *p++ = frameend; + crc = UPDC32 (frameend, crc); + crc = ~crc; + for (cdata = 0; cdata < 4; cdata++) { + p = zputchar (p, (char) crc); + crc >>= 8; + } + if (frameend == ZCRCW || frameend == ZCRCE || frameend == ZCRCF) { + *p++ = CR; + *p++ = XON; + } + + return p - zresult; +} + +/* + * Read in a header ... + * + * This is function zgethdr() from the Zmodem source. + */ + +static int +izrecv_hdr(qdaemon, hdr) +struct sdaemon *qdaemon; +achdrval_t hdr; +{ + int c,cerr; + + /* + * Kludge alert! If another part of the program received a packet but + * wasn't ready to handle it, it is tucked away for us to handle now. + */ + + if (iZpkt_rcvd_kludge != -1) { + c = iZpkt_rcvd_kludge; + iZpkt_rcvd_kludge = -1; + stohdr (hvZpkt_hdrval_kludge, hdr); + DEBUG_MESSAGE2 (DEBUG_PROTO, + "izrecv_hdr: queued %s, data = 0x%lx", + ZZHEADER_NAME(c), rclhdr (hdr)); + cZheaders_received++; + return c; + } + + cerr = cZmax_garbage; /* Max bytes before start of frame */ + +again: + switch (c = noxrd7 (qdaemon)) { + case ZM_TIMEOUT: + case ZM_ERROR: + case ZM_RCDO: + goto fifi; + case ZPAD: /* This is what we want */ + break; + case CR: /* padding at end of previous header */ + default: + if (--cerr < 0) { + c = ZM_ERROR; + goto fifi; + } + goto again; + } + +splat: + switch (c = noxrd7 (qdaemon)) { + case ZPAD: + if (--cerr < 0) { + c = ZM_ERROR; + goto fifi; + } + goto splat; + case ZM_TIMEOUT: + case ZM_RCDO: + goto fifi; + case ZDLE: /* This is what we want */ + break; + default: + if (--cerr < 0) { + c = ZM_ERROR; + goto fifi; + } + goto again; + } + + switch (c = noxrd7 (qdaemon)) { + case ZM_TIMEOUT: + case ZM_RCDO: + goto fifi; + case ZBIN: + c = zrbhdr32 (qdaemon, hdr); + break; + case ZHEX: + c = zrhhdr (qdaemon, hdr); + break; + default: + if (--cerr < 0) { + c = ZM_ERROR; + goto fifi; + } + goto again; + } + +fifi: + switch (c) { + case ZM_TIMEOUT: + cZtimeouts++; + break; + case ZM_ERROR: + cZerrors++; + break; + case ZM_RCDO: + break; + default: + cZheaders_received++; + break; + } + DEBUG_MESSAGE2 (DEBUG_PROTO, "izrecv_hdr: %s, data = 0x%x", + ZZHEADER_NAME(c), rclhdr (hdr)); + + return c; +} + +/* + * Receive a binary style header (type and position) with 32 bit FCS ... + */ + +static int +zrbhdr32(qdaemon, hdr) +struct sdaemon *qdaemon; +achdrval_t hdr; +{ + int c,i,type; + unsigned long crc; + + if ((c = zdlread (qdaemon)) & ~0377) + return c; + type = c; + crc = ICRCINIT; + crc = UPDC32 (c, crc); + + for (i = 0; i < 4; i++) { + if ((c = zdlread (qdaemon)) & ~0377) + return c; + crc = UPDC32 (c, crc); + hdr[i] = (char) c; + } + for (i = 0; i < 4; i++) { + if ((c = zdlread (qdaemon)) & ~0377) + return c; + crc = UPDC32 (c, crc); + } + if (crc != IHDRCRC) + return ZM_ERROR; + + return type; +} + +/* + * Receive a hex style header (type and position) ... + */ + +static int +zrhhdr(qdaemon, hdr) +struct sdaemon *qdaemon; +achdrval_t hdr; +{ + int c,i,type; + unsigned long crc; + + if ((c = zgethex (qdaemon)) < 0) + return c; + type = c; + crc = ICRCINIT; + crc = UPDC32 (c, crc); + + for (i = 0; i < 4; i++) { + if ((c = zgethex (qdaemon)) < 0) + return c; + crc = UPDC32 (c, crc); + hdr[i] = (char) c; + } + for (i = 0; i < 4; i++) { + if ((c = zgethex (qdaemon)) < 0) + return c; + crc = UPDC32 (c, crc); + } + if (crc != IHDRCRC) + return ZM_ERROR; + + return type; +} + +/* + * Receive a data packet ... + */ + +static int +zrdat32(qdaemon, buf, length, iprxcount) +struct sdaemon *qdaemon; +char *buf; +int length; +int *iprxcount; +{ + int c,d; + unsigned long crc; + char *end; + + crc = ICRCINIT; + *iprxcount = 0; + end = buf + length; + while (buf <= end) { + if ((c = zdlread (qdaemon)) & ~0377) { +crcfoo: + switch (c) { + case GOTCRCE: + case GOTCRCG: + case GOTCRCQ: + case GOTCRCW: + case GOTCRCF: + d = c; + c &= 0377; + crc = UPDC32 (c, crc); + if ((c = zdlread (qdaemon)) & ~0377) + goto crcfoo; + crc = UPDC32 (c, crc); + if ((c = zdlread (qdaemon)) & ~0377) + goto crcfoo; + crc = UPDC32 (c, crc); + if ((c = zdlread (qdaemon)) & ~0377) + goto crcfoo; + crc = UPDC32 (c, crc); + if ((c = zdlread (qdaemon)) & ~0377) + goto crcfoo; + crc = UPDC32 (c, crc); + if (crc != IHDRCRC) + return ZM_ERROR; + *iprxcount = length - (end - buf); + return d; + case ZM_TIMEOUT: + case ZM_RCDO: + return c; + default: + return ZM_ERROR; + } + } + *buf++ = (char) c; + crc = UPDC32 (c, crc); + } + + return ZM_ERROR; /* bad packet, too long */ +} + +/* + * Respond to receiver's complaint, get back in sync with receiver ... + */ + +static int +getinsync(qdaemon, flag) +struct sdaemon *qdaemon; +boolean flag; +{ + int c,cerr; + achdrval_t rx_hdr; + + cerr = cZretries; + + for (;;) { + c = izrecv_hdr (qdaemon, rx_hdr); + + switch (c) { + case ZRPOS: + wpZrxpos = lzupdate_rxpos (rx_hdr, wpZrxpos, + wpZlrxpos, wpZtxpos); + cZbytes_resent += wpZtxpos - wpZrxpos; + wpZlrxpos = wpZtxpos = wpZrxpos; + if (wpZlastsync == wpZrxpos) { + if (++iZbeenhereb4 > 4) + if (cZblklen > 32) + cZblklen /= 2; + /* FIXME: shouldn't we reset iZbeenhereb4? */ + } + wpZlastsync = wpZrxpos; + return ZRPOS; + case ZACK: + wpZrxpos = lzupdate_rxpos (rx_hdr, wpZrxpos, + wpZlrxpos, wpZtxpos); + wpZlrxpos = wpZrxpos; + if (flag || wpZtxpos == wpZrxpos) + return ZACK; + break; + case ZNAK: { + winpos_t rx_bytes; + /* + * Our partner is in fzfinish_tx() and is waiting + * for ZACK ... + */ + zdecode_data_hdr (rclhdr (rx_hdr), &rx_bytes); + if (rx_bytes == wpZrxbytes) { + if (!fzsend_hdr (qdaemon, ZHEX, ZACK, + hvzencode_data_hdr (wpZrxbytes), + TRUE)) + return FALSE; + } + break; + } + case ZFIN: + case ZM_RCDO: + return c; + case ZM_TIMEOUT: + if (--cerr < 0) { + ulog (LOG_ERROR, + "getinsync: retries exhausted"); + return ZM_ERROR; + } + break; /* sender doesn't send ZNAK for timeout */ + case ZM_ERROR: + default: + if (--cerr < 0) { + ulog (LOG_ERROR, + "getinsync: retries exhausted"); + return ZM_ERROR; + } + if (!fzsend_hdr (qdaemon, ZHEX, ZNAK, + hvzencode_data_hdr (wpZtxpos), + TRUE)) + return ZM_ERROR; + break; + } + } +} + +/* + * Send a byte as two hex digits ... + */ + +static char * +zputhex(p, ch) +char *p; +int ch; +{ + static char digits[] = "0123456789abcdef"; + + *p++ = digits[(ch & 0xF0) >> 4]; + *p++ = digits[ch & 0xF]; + return p; +} + +/* + * Send character c with ZMODEM escape sequence encoding ... + * + * Escape XON, XOFF. + * FIXME: Escape CR following @ (Telenet net escape) ... disabled for now + * Will need to put back references to . + */ + +static char * +zputchar(p, ch) +char *p; +int ch; +{ + char c = ch; + + /* Quick check for non control characters */ + + if (c & 0140) { + *p++ = c; + } else { + switch (c & 0377) { + case ZDLE: + *p++ = ZDLE; + *p++ = c ^ 0100; + break; + case CR: +#if 0 + if (!fZesc_ctl && (lastsent & 0177) != '@') + goto sendit; +#endif + /* fall through */ + case 020: /* ^P */ + case XON: + case XOFF: + *p++ = ZDLE; + c ^= 0100; +/*sendit:*/ + *p++ = c; + break; + default: + if (fZesc_ctl && !(c & 0140)) { + *p++ = ZDLE; + c ^= 0100; + } + *p++ = c; + break; + } + } + + return p; +} + +/* + * Decode two lower case hex digits into an 8 bit byte value ... + */ + +static int +zgethex(qdaemon) +struct sdaemon *qdaemon; +{ + int c,n; + + if ((c = noxrd7 (qdaemon)) < 0) + return c; + n = c - '0'; + if (n > 9) + n -= ('a' - ':'); + if (n & ~0xF) + return ZM_ERROR; + if ((c = noxrd7 (qdaemon)) < 0) + return c; + c -= '0'; + if (c > 9) + c -= ('a' - ':'); + if (c & ~0xF) + return ZM_ERROR; + c += (n << 4); + + return c; +} + +/* + * Read a byte, checking for ZMODEM escape encoding ... + */ + +static int +zdlread(qdaemon) +struct sdaemon *qdaemon; +{ + int c; + +again: + READCHAR (qdaemon, c, cZtimeout); + if (c < 0) + return c; + if (c & 0140) /* quick check for non control characters */ + return c; + switch (c) { + case ZDLE: + break; + case XON: + goto again; + case XOFF: + READCHAR (qdaemon, c, XON_WAIT); + goto again; + default: + if (fZesc_ctl && !(c & 0140)) + goto again; + return c; + } + +again2: + READCHAR (qdaemon, c, cZtimeout); + if (c < 0) + return c; + switch (c) { + case ZCRCE: + case ZCRCG: + case ZCRCQ: + case ZCRCW: + case ZCRCF: + return c | GOTOR; + case ZRUB0: /* FIXME: This is never generated. */ + return 0177; + case ZRUB1: /* FIXME: This is never generated. */ + return 0377; + case XON: + goto again2; + case XOFF: + READCHAR (qdaemon, c, XON_WAIT); + goto again2; + default: + if (fZesc_ctl && !(c & 0140)) + goto again2; /* FIXME: why again2? */ + if ((c & 0140) == 0100) + return c ^ 0100; + break; + } + + return ZM_ERROR; +} + +/* + * Read a character from the modem line with timeout ... + * Eat parity bit, XON and XOFF characters. + */ + +static int +noxrd7(qdaemon) +struct sdaemon *qdaemon; +{ + int c; + + for (;;) { + READCHAR (qdaemon, c, cZtimeout); + if (c < 0) + return c; + switch (c &= 0177) { + case XON: + continue; + case XOFF: + READCHAR (qdaemon, c, XON_WAIT); + continue; + case CR: + case ZDLE: + return c; + default: + if (fZesc_ctl && !(c & 0140)) + continue; + return c; + } + } +} + +/* + * Read a character from the receive buffer, or from the line if empty ... + * + * is in seconds (maybe make it tenths of seconds like in Zmodem?) + */ + +static int +realreadchar(qdaemon, timeout) +struct sdaemon *qdaemon; +int timeout; +{ + int c; + + if ((c = breceive_char (qdaemon->qconn, timeout, TRUE)) >= 0) + return c; + + switch (c) { + case -1: + return ZM_TIMEOUT; + case -2: + return ZM_RCDO; + } + + ulog (LOG_FATAL, "realreadchar: breceive_char() returned %d", c); + return ZM_ERROR; +} + + +/* + * Check if the receive channel has any characters in it. + * + * At present we can only test the receive buffer. No mechanism is available + * to go to the hardware. This should not be a problem though, as long as all + * appropriate calls to fsend_data() set to TRUE. + */ + +static boolean +fzreceive_ready() +{ + return iPrecstart != iPrecend; +} + +/* + * Store integer value in an achdrval_t ... + */ + +static void +stohdr(val, hdr) +hdrval_t val; +achdrval_t hdr; +{ + hdr[ZP0] = (char) val; + hdr[ZP1] = (char) (val >> 8); + hdr[ZP2] = (char) (val >> 16); + hdr[ZP3] = (char) (val >> 24); +} + +/* + * Recover an integer from a header ... + */ + +static hdrval_t +rclhdr(hdr) +achdrval_t hdr; +{ + hdrval_t v; + + v = hdr[ZP3] & 0377; + v = (v << 8) | (hdr[ZP2] & 0377); + v = (v << 8) | (hdr[ZP1] & 0377); + v = (v << 8) | (hdr[ZP0] & 0377); + + return v; +} + +/* + * Encode a from the byte count ... + * + * We use to store the byte count / 32 and a message sequence number which + * made this function very useful. Don't remove it. + * FIXME: Well, maybe remove it later. + */ + +static hdrval_t +hvzencode_data_hdr(cbytes) +winpos_t cbytes; +{ + return (hdrval_t) cbytes; +} + +/* + * Decode a into a byte count ... + * + * We use to store the byte count / 32 and a message sequence number which + * made this function very useful. Don't remove it. + * FIXME: Well, maybe remove it later. + */ + +static void +zdecode_data_hdr(hdrval, pcbytes) +hdrval_t hdrval; +winpos_t *pcbytes; +{ + *pcbytes = hdrval; +} + +/* + * Update from the received data header value ... + * + * FIXME: Here is where we'd handle wrapping around at 4 gigabytes. + */ + +static winpos_t +lzupdate_rxpos(rx_hdr, rxpos, lrxpos, txpos) +achdrval_t rx_hdr; +winpos_t rxpos,lrxpos,txpos; +{ + winpos_t rx_pktpos; + + zdecode_data_hdr (rclhdr (rx_hdr), &rx_pktpos); + + DEBUG_MESSAGE4 (DEBUG_PROTO, + "lzupdate_rxpos: rx_pktpos=0x%lx, rxpos=0x%lx, lrxpos=0x%lx, txpos=0x%lx", + rx_pktpos, rxpos, lrxpos, txpos); + + /* + * Check if valid. It could be old. + */ + + if (rx_pktpos < wpZlrxpos + || rx_pktpos > ((wpZtxpos + 1024L) & ~1023L)) + return rxpos; + + return rx_pktpos; +} diff --git a/gnu/libexec/uucp/uucico/rec.c b/gnu/libexec/uucp/uucico/rec.c new file mode 100644 index 0000000000..160aab705a --- /dev/null +++ b/gnu/libexec/uucp/uucico/rec.c @@ -0,0 +1,1162 @@ +/* rec.c + Routines to receive a file. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char rec_rcsid[] = "$Id: rec.c,v 1.1 1993/08/04 19:36:28 jtc Exp $"; +#endif + +#include + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" +#include "prot.h" +#include "trans.h" + +/* If the other side does not tell us the size of a file it wants to + send us, we assume it is this long. This is only used for free + space checking. */ +#define CASSUMED_FILE_SIZE (10240) + +/* We keep this information in the pinfo field of the stransfer + structure. */ +struct srecinfo +{ + /* Local user to send mail to (may be NULL). */ + char *zmail; + /* Full file name. */ + char *zfile; + /* Temporary file name. */ + char *ztemp; + /* TRUE if this is a spool directory file. */ + boolean fspool; + /* TRUE if this was a local request. */ + boolean flocal; + /* TRUE if the file has been completely received. */ + boolean freceived; + /* TRUE if remote request has been replied to. */ + boolean freplied; + /* TRUE if we moved the file to the final destination. */ + boolean fmoved; +}; + +/* This structure is kept in the pinfo field if we are refusing a + remote request. */ +struct srecfailinfo +{ + /* Reason for refusal. */ + enum tfailure twhy; + /* TRUE if we have sent the reason for refusal. */ + boolean fsent; + /* TRUE if we have seen the end of the file. */ + boolean freceived; +}; + +/* Local functions. */ + +static void urrec_free P((struct stransfer *qtrans)); +static boolean flocal_rec_fail P((struct stransfer *qtrans, + struct scmd *qcmd, + const struct uuconf_system *qsys, + const char *zwhy)); +static boolean flocal_rec_send_request P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); +static boolean flocal_rec_await_reply P((struct stransfer *qtrans, + struct sdaemon *qdaemon, + const char *zdata, + size_t cdata)); +static boolean fremote_send_reply P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); +static boolean fremote_send_fail P((struct sdaemon *qdaemon, + struct scmd *qcmd, + enum tfailure twhy, + int iremote)); +static boolean fremote_send_fail_send P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); +static boolean fremote_discard P((struct stransfer *qtrans, + struct sdaemon *qdaemon, + const char *zdata, size_t cdata)); +static boolean frec_file_end P((struct stransfer *qtrans, + struct sdaemon *qdaemon, + const char *zdata, size_t cdata)); +static boolean frec_file_send_confirm P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); + +/* Free up a receive stransfer structure. */ + +static void +urrec_free (qtrans) + struct stransfer *qtrans; +{ + struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; + + if (qinfo != NULL) + { + ubuffree (qinfo->zmail); + ubuffree (qinfo->zfile); + ubuffree (qinfo->ztemp); + xfree (qtrans->pinfo); + } + + utransfree (qtrans); +} + +/* Set up a request for a file from the remote system. This may be + called before the remote system has been called. + + This is the order of function calls: + + flocal_rec_file_init --> fqueue_local + flocal_rec_send_request (send R ...) --> fqueue_receive + flocal_rec_await_reply (open file, call pffile) --> fqueue_receive + receive file + frec_file_end (close and move file, call pffile) --> fqueue_send + frec_file_send_confirm (send CY) + */ + +boolean +flocal_rec_file_init (qdaemon, qcmd) + struct sdaemon *qdaemon; + struct scmd *qcmd; +{ + const struct uuconf_system *qsys; + boolean fspool; + char *zfile; + struct srecinfo *qinfo; + struct stransfer *qtrans; + + qsys = qdaemon->qsys; + + /* Make sure we are permitted to transfer files. */ + if (qdaemon->fcaller + ? ! qsys->uuconf_fcall_transfer + : ! qsys->uuconf_fcalled_transfer) + { + /* This case will have been checked by uucp or uux, but it could + have changed. */ + if (! qsys->uuconf_fcall_transfer + && ! qsys->uuconf_fcalled_transfer) + return flocal_rec_fail ((struct stransfer *) NULL, qcmd, qsys, + "not permitted to request files"); + return TRUE; + } + + fspool = fspool_file (qcmd->zto); + + if (fspool) + { + pointer puuconf; + int iuuconf; + const char *zlocalname; + struct uuconf_system slocalsys; + + /* Normal users are not allowed to request files to be received + into the spool directory. To support uux forwarding, we use + the special option '9'. This permits a file to be received + into the spool directory for the local system only without + the usual checking. This is only done for local requests, of + course. */ + if (qcmd->zto[0] != 'D' + || strchr (qcmd->zoptions, '9') == NULL) + return flocal_rec_fail ((struct stransfer *) NULL, qcmd, qsys, + "not permitted to receive"); + + puuconf = qdaemon->puuconf; + iuuconf = uuconf_localname (puuconf, &zlocalname); + if (iuuconf == UUCONF_NOT_FOUND) + { + zlocalname = zsysdep_localname (); + if (zlocalname == NULL) + return FALSE; + } + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + + iuuconf = uuconf_system_info (puuconf, zlocalname, &slocalsys); + if (iuuconf == UUCONF_NOT_FOUND) + { + iuuconf = uuconf_system_local (puuconf, &slocalsys); + if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + } + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + + zfile = zsysdep_spool_file_name (&slocalsys, qcmd->zto, qcmd->pseq); + + (void) uuconf_system_free (puuconf, &slocalsys); + + if (zfile == NULL) + return FALSE; + } + else + { + zfile = zsysdep_add_base (qcmd->zto, qcmd->zfrom); + if (zfile == NULL) + return FALSE; + + /* Check permissions. */ + if (! fin_directory_list (zfile, qsys->uuconf_pzlocal_receive, + qsys->uuconf_zpubdir, TRUE, + FALSE, qcmd->zuser)) + { + ubuffree (zfile); + return flocal_rec_fail ((struct stransfer *) NULL, qcmd, qsys, + "not permitted to receive"); + } + + /* The 'f' option means that directories should not + be created if they do not already exist. */ + if (strchr (qcmd->zoptions, 'f') == NULL) + { + if (! fsysdep_make_dirs (zfile, TRUE)) + { + ubuffree (zfile); + return flocal_rec_fail ((struct stransfer *) NULL, qcmd, + qsys, "cannot create directories"); + } + } + } + + qinfo = (struct srecinfo *) xmalloc (sizeof (struct srecinfo)); + if (strchr (qcmd->zoptions, 'm') == NULL) + qinfo->zmail = NULL; + else + qinfo->zmail = zbufcpy (qcmd->zuser); + qinfo->zfile = zfile; + qinfo->ztemp = NULL; + qinfo->fspool = fspool; + qinfo->flocal = TRUE; + qinfo->freceived = FALSE; + qinfo->freplied = TRUE; + + qtrans = qtransalc (qcmd); + qtrans->psendfn = flocal_rec_send_request; + qtrans->pinfo = (pointer) qinfo; + + return fqueue_local (qdaemon, qtrans); +} + +/* Report an error for a local receive request. */ + +static boolean +flocal_rec_fail (qtrans, qcmd, qsys, zwhy) + struct stransfer *qtrans; + struct scmd *qcmd; + const struct uuconf_system *qsys; + const char *zwhy; +{ + if (zwhy != NULL) + { + ulog (LOG_ERROR, "%s: %s", qcmd->zfrom, zwhy); + (void) fmail_transfer (FALSE, qcmd->zuser, (const char *) NULL, zwhy, + qcmd->zfrom, qsys->uuconf_zname, + qcmd->zto, (const char *) NULL, + (const char *) NULL); + (void) fsysdep_did_work (qcmd->pseq); + } + if (qtrans != NULL) + urrec_free (qtrans); + return TRUE; +} + +/* This is called when we are ready to send the actual request to the + other system. */ + +static boolean +flocal_rec_send_request (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; + long cbytes, cbytes2; + size_t clen; + char *zsend; + boolean fret; + + qinfo->ztemp = zsysdep_receive_temp (qdaemon->qsys, qinfo->zfile, + (const char *) NULL); + if (qinfo->ztemp == NULL) + { + urrec_free (qtrans); + return FALSE; + } + + /* Check the amount of free space available for both the temporary + file and the real file. */ + cbytes = csysdep_bytes_free (qinfo->ztemp); + cbytes2 = csysdep_bytes_free (qinfo->zfile); + if (cbytes < cbytes2) + cbytes = cbytes2; + if (cbytes != -1) + { + cbytes -= qdaemon->qsys->uuconf_cfree_space; + if (cbytes < 0) + cbytes = 0; + } + + if (qdaemon->clocal_size != -1 + && (cbytes == -1 || qdaemon->clocal_size < cbytes)) + cbytes = qdaemon->clocal_size; + + /* We send the string + R from to user options + + We put a dash in front of options. If we are talking to a + counterpart, we also send the maximum size file we are prepared + to accept, as returned by esysdep_open_receive. */ + clen = (strlen (qtrans->s.zfrom) + strlen (qtrans->s.zto) + + strlen (qtrans->s.zuser) + strlen (qtrans->s.zoptions) + 30); + zsend = zbufalc (clen); + if ((qdaemon->ifeatures & FEATURE_SIZES) == 0) + sprintf (zsend, "R %s %s %s -%s", qtrans->s.zfrom, qtrans->s.zto, + qtrans->s.zuser, qtrans->s.zoptions); + else if ((qdaemon->ifeatures & FEATURE_V103) == 0) + sprintf (zsend, "R %s %s %s -%s 0x%lx", qtrans->s.zfrom, qtrans->s.zto, + qtrans->s.zuser, qtrans->s.zoptions, (unsigned long) cbytes); + else + sprintf (zsend, "R %s %s %s -%s %ld", qtrans->s.zfrom, qtrans->s.zto, + qtrans->s.zuser, qtrans->s.zoptions, cbytes); + + fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, zsend, qtrans->ilocal, + qtrans->iremote); + ubuffree (zsend); + if (! fret) + { + urrec_free (qtrans); + return FALSE; + } + + qtrans->fcmd = TRUE; + qtrans->precfn = flocal_rec_await_reply; + + return fqueue_receive (qdaemon, qtrans); +} + +/* This is called when a reply is received for the request. */ + +/*ARGSUSED*/ +static boolean +flocal_rec_await_reply (qtrans, qdaemon, zdata, cdata) + struct stransfer *qtrans; + struct sdaemon *qdaemon; + const char *zdata; + size_t cdata; +{ + struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; + long crestart; + const char *zlog; + + if (zdata[0] != 'R' + || (zdata[1] != 'Y' && zdata[1] != 'N')) + { + ulog (LOG_ERROR, "%s: bad response to receive request: \"%s\"", + qtrans->s.zfrom, zdata); + urrec_free (qtrans); + return FALSE; + } + + if (zdata[1] == 'N') + { + boolean fnever; + const char *zerr; + + fnever = TRUE; + if (zdata[2] == '2') + zerr = "no such file"; + else if (zdata[2] == '6') + { + /* We sent over the maximum file size we were prepared to + receive, and the remote system is telling us that the + file is larger than that. Try again later. It would be + better if we could know whether there will ever be enough + room. */ + zerr = "too large to receive now"; + fnever = FALSE; + } + else + zerr = "unknown reason"; + + if (fnever) + return flocal_rec_fail (qtrans, &qtrans->s, qdaemon->qsys, zerr); + + ulog (LOG_ERROR, "%s: %s", qtrans->s.zfrom, zerr); + + urrec_free (qtrans); + + return TRUE; + } + + /* The mode should have been sent as "RY 0%o". If it wasn't, we use + 0666. */ + qtrans->s.imode = (unsigned int) strtol ((char *) (zdata + 2), + (char **) NULL, 8); + if (qtrans->s.imode == 0) + qtrans->s.imode = 0666; + + /* Open the file to receive into. We just ignore any restart count, + since we have no way to tell it to the other side. SVR4 may have + some way to do this, but I don't know what it is. */ + qtrans->e = esysdep_open_receive (qdaemon->qsys, qinfo->zfile, + (const char *) NULL, qinfo->ztemp, + &crestart); + if (! ffileisopen (qtrans->e)) + return flocal_rec_fail (qtrans, &qtrans->s, qdaemon->qsys, + "cannot open file"); + + if (qinfo->fspool) + zlog = qtrans->s.zto; + else + zlog = qinfo->zfile; + qtrans->zlog = zbufalc (sizeof "Receiving " + strlen (zlog)); + sprintf (qtrans->zlog, "Receiving %s", zlog); + + if (qdaemon->qproto->pffile != NULL) + { + boolean fhandled; + + if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, TRUE, FALSE, + (long) -1, &fhandled)) + { + (void) ffileclose (qtrans->e); + return flocal_rec_fail (qtrans, &qtrans->s, qdaemon->qsys, + (const char *) NULL); + } + if (fhandled) + return TRUE; + } + + qtrans->frecfile = TRUE; + qtrans->psendfn = frec_file_send_confirm; + qtrans->precfn = frec_file_end; + + return fqueue_receive (qdaemon, qtrans); +} + +/* Make sure there is still enough disk space available to receive a + file. */ + +boolean +frec_check_free (qtrans, cfree_space) + struct stransfer *qtrans; + long cfree_space; +{ + struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; + long cfree1, cfree2; + + cfree1 = csysdep_bytes_free (qinfo->ztemp); + cfree2 = csysdep_bytes_free (qinfo->zfile); + if (cfree1 < cfree2) + cfree1 = cfree2; + if (cfree1 != -1 && cfree1 < cfree_space) + { + ulog (LOG_ERROR, "%s: too big to receive now", qinfo->zfile); + return FALSE; + } + + return TRUE; +} + +/* A remote request to send a file to the local system, meaning that + we are going to receive a file. + + If we are using a protocol which does not support multiple + channels, the remote system will not start sending us the file + until it has received our confirmation. In that case, the order of + functions is as follows: + + fremote_send_file_init (open file) --> fqueue_remote + fremote_send_reply (send SY, call pffile) --> fqueue_receive + receive file + frec_file_end (close and move file, call pffile) --> fqueue_send + frec_file_send_confirm (send CY) + + If the protocol supports multiple channels, then the remote system + will start sending the file immediately after the send request. + That means that the data may come in before remote_send_reply is + called, so frec_file_end may be called before fremote_send_reply. + Note that this means the pffile entry points may be called in + reverse order for such a protocol. + + If the send request is rejected, via fremote_send_fail, and the + protocol supports multiple channels, we must accept and discard + data until a zero byte buffer is received from the other side, + indicating that it has received our rejection. + + This code also handles execution requests, which are very similar + to send requests. */ + +boolean +fremote_send_file_init (qdaemon, qcmd, iremote) + struct sdaemon *qdaemon; + struct scmd *qcmd; + int iremote; +{ + const struct uuconf_system *qsys; + boolean fspool; + char *zfile; + openfile_t e; + char *ztemp; + long cbytes, cbytes2; + long crestart; + struct srecinfo *qinfo; + struct stransfer *qtrans; + const char *zlog; + + qsys = qdaemon->qsys; + + if (! qsys->uuconf_frec_request) + { + ulog (LOG_ERROR, "%s: not permitted to receive files from remote", + qcmd->zfrom); + return fremote_send_fail (qdaemon, qcmd, FAILURE_PERM, iremote); + } + + fspool = fspool_file (qcmd->zto); + + /* We don't accept remote command files. An execution request may + only send a simple data file. */ + if ((fspool && qcmd->zto[0] == 'C') + || (qcmd->bcmd == 'E' + && (! fspool || qcmd->zto[0] != 'D'))) + { + ulog (LOG_ERROR, "%s: not permitted to receive", qcmd->zfrom); + return fremote_send_fail (qdaemon, qcmd, FAILURE_PERM, iremote); + } + + /* See if we have already received this file in a previous + conversation. */ + if (fsysdep_already_received (qsys, qcmd->zto, qcmd->ztemp)) + return fremote_send_fail (qdaemon, qcmd, FAILURE_RECEIVED, iremote); + + if (fspool) + { + zfile = zsysdep_spool_file_name (qsys, qcmd->zto, (pointer) NULL); + if (zfile == NULL) + return FALSE; + } + else + { + zfile = zsysdep_local_file (qcmd->zto, qsys->uuconf_zpubdir); + if (zfile != NULL) + { + char *zadd; + + zadd = zsysdep_add_base (zfile, qcmd->zfrom); + ubuffree (zfile); + zfile = zadd; + } + if (zfile == NULL) + return FALSE; + + /* Check permissions. */ + if (! fin_directory_list (zfile, qsys->uuconf_pzremote_receive, + qsys->uuconf_zpubdir, TRUE, + FALSE, (const char *) NULL)) + { + ulog (LOG_ERROR, "%s: not permitted to receive", zfile); + ubuffree (zfile); + return fremote_send_fail (qdaemon, qcmd, FAILURE_PERM, iremote); + } + + if (strchr (qcmd->zoptions, 'f') == NULL) + { + if (! fsysdep_make_dirs (zfile, TRUE)) + { + ubuffree (zfile); + return fremote_send_fail (qdaemon, qcmd, FAILURE_OPEN, + iremote); + } + } + } + + ztemp = zsysdep_receive_temp (qsys, zfile, qcmd->ztemp); + + /* Adjust the number of bytes we are prepared to receive according + to the amount of free space we are supposed to leave available + and the maximum file size we are permitted to transfer. */ + cbytes = csysdep_bytes_free (ztemp); + cbytes2 = csysdep_bytes_free (zfile); + if (cbytes < cbytes2) + cbytes = cbytes2; + + if (cbytes != -1) + { + cbytes -= qsys->uuconf_cfree_space; + if (cbytes < 0) + cbytes = 0; + } + + if (qdaemon->cremote_size != -1 + && (cbytes == -1 || qdaemon->cremote_size < cbytes)) + cbytes = qdaemon->cremote_size; + + /* If the number of bytes we are prepared to receive is less than + the file size, we must fail. If the remote did not tell us the + file size, arbitrarily assumed that it is 10240 bytes. */ + if (cbytes != -1) + { + long csize; + + csize = qcmd->cbytes; + if (csize == -1) + csize = CASSUMED_FILE_SIZE; + if (cbytes < csize) + { + ulog (LOG_ERROR, "%s: too big to receive", zfile); + ubuffree (ztemp); + ubuffree (zfile); + return fremote_send_fail (qdaemon, qcmd, FAILURE_SIZE, iremote); + } + } + + /* Open the file to receive into. This may find an old copy of the + file, which will be used for file restart if the other side + supports it. */ + e = esysdep_open_receive (qsys, zfile, qcmd->ztemp, ztemp, &crestart); + if (! ffileisopen (e)) + { + ubuffree (ztemp); + ubuffree (zfile); + return fremote_send_fail (qdaemon, qcmd, FAILURE_OPEN, iremote); + } + + if (crestart > 0) + { + if ((qdaemon->ifeatures & FEATURE_RESTART) == 0) + crestart = -1; + else + { + DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, + "fremote_send_file_init: Restarting receive from %ld", + crestart); + if (! ffileseek (e, crestart)) + { + ulog (LOG_ERROR, "seek: %s", strerror (errno)); + (void) ffileclose (e); + ubuffree (ztemp); + ubuffree (zfile); + return FALSE; + } + } + } + + qinfo = (struct srecinfo *) xmalloc (sizeof (struct srecinfo)); + if (strchr (qcmd->zoptions, 'n') == NULL) + qinfo->zmail = NULL; + else + qinfo->zmail = zbufcpy (qcmd->znotify); + qinfo->zfile = zfile; + qinfo->ztemp = ztemp; + qinfo->fspool = fspool; + qinfo->flocal = FALSE; + qinfo->freceived = FALSE; + qinfo->freplied = FALSE; + + qtrans = qtransalc (qcmd); + qtrans->psendfn = fremote_send_reply; + qtrans->precfn = frec_file_end; + qtrans->iremote = iremote; + qtrans->pinfo = (pointer) qinfo; + qtrans->frecfile = TRUE; + qtrans->e = e; + if (crestart > 0) + qtrans->ipos = crestart; + + if (qcmd->bcmd == 'E') + zlog = qcmd->zcmd; + else + { + if (qinfo->fspool) + zlog = qcmd->zto; + else + zlog = qinfo->zfile; + } + qtrans->zlog = zbufalc (sizeof "Receiving " + strlen (zlog)); + sprintf (qtrans->zlog, "Receiving %s", zlog); + + return fqueue_remote (qdaemon, qtrans); +} + +/* Reply to a send request, and prepare to receive the file. */ + +static boolean +fremote_send_reply (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; + char ab[50]; + + ab[0] = qtrans->s.bcmd; + ab[1] = 'Y'; + if (qtrans->ipos <= 0) + ab[2] = '\0'; + else + sprintf (ab + 2, " 0x%lx", (unsigned long) qtrans->ipos); + + if (! (*qdaemon->qproto->pfsendcmd) (qdaemon, ab, qtrans->ilocal, + qtrans->iremote)) + { + (void) ffileclose (qtrans->e); + (void) remove (qinfo->ztemp); + urrec_free (qtrans); + return FALSE; + } + + qinfo->freplied = TRUE; + + if (qdaemon->qproto->pffile != NULL) + { + boolean fhandled; + + if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, TRUE, FALSE, + (long) -1, &fhandled)) + { + (void) ffileclose (qtrans->e); + (void) remove (qinfo->ztemp); + urrec_free (qtrans); + return FALSE; + } + if (fhandled) + return TRUE; + } + + /* If the file has been completely received, we just want to send + the final confirmation. Otherwise, we must wait for the file + first. */ + qtrans->psendfn = frec_file_send_confirm; + if (qinfo->freceived) + return fqueue_send (qdaemon, qtrans); + else + return fqueue_receive (qdaemon, qtrans); +} + +/* If we can't receive a file, queue up a response to the remote + system. */ + +static boolean +fremote_send_fail (qdaemon, qcmd, twhy, iremote) + struct sdaemon *qdaemon; + struct scmd *qcmd; + enum tfailure twhy; + int iremote; +{ + struct srecfailinfo *qinfo; + struct stransfer *qtrans; + + qinfo = (struct srecfailinfo *) xmalloc (sizeof (struct srecfailinfo)); + qinfo->twhy = twhy; + qinfo->fsent = FALSE; + + /* If the protocol does not support multiple channels (cchans <= 1), + then we have essentially already received the entire file. */ + qinfo->freceived = qdaemon->qproto->cchans <= 1; + + qtrans = qtransalc (qcmd); + qtrans->psendfn = fremote_send_fail_send; + qtrans->precfn = fremote_discard; + qtrans->iremote = iremote; + qtrans->pinfo = (pointer) qinfo; + + return fqueue_remote (qdaemon, qtrans); +} + +/* Send a failure string for a send command to the remote system; + this is called when we are ready to reply to the command. */ + +static boolean +fremote_send_fail_send (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + struct srecfailinfo *qinfo = (struct srecfailinfo *) qtrans->pinfo; + char ab[4]; + boolean fret; + + ab[0] = qtrans->s.bcmd; + ab[1] = 'N'; + + switch (qinfo->twhy) + { + case FAILURE_PERM: + ab[2] = '2'; + break; + case FAILURE_OPEN: + ab[2] = '4'; + break; + case FAILURE_SIZE: + ab[2] = '6'; + break; + case FAILURE_RECEIVED: + /* Remember this file as though we successfully received it; + when the other side acknowledges our rejection, we know that + we no longer have to remember that we received this file. */ + usent_receive_ack (qdaemon, qtrans); + ab[2] = '8'; + break; + default: + ab[2] = '\0'; + break; + } + + ab[3] = '\0'; + + fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, ab, qtrans->ilocal, + qtrans->iremote); + + qinfo->fsent = TRUE; + + /* Wait for the end of file marker if we haven't gotten it yet. */ + if (! qinfo->freceived) + { + if (! fqueue_receive (qdaemon, qtrans)) + fret = FALSE; + } + else + { + xfree (qtrans->pinfo); + utransfree (qtrans); + } + + return fret; +} + +/* Discard data until we reach the end of the file. This is used for + a protocol with multiple channels, since the remote system may + start sending the file before the confirmation is sent. If we + refuse the file, the remote system will get us back in synch by + sending an empty buffer, which is what we look for here. */ + +/*ARGSUSED*/ +static boolean +fremote_discard (qtrans, qdaemon, zdata, cdata) + struct stransfer *qtrans; + struct sdaemon *qdaemon; + const char *zdata; + size_t cdata; +{ + struct srecfailinfo *qinfo = (struct srecfailinfo *) qtrans->pinfo; + + DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, + "fremote_discard: Discarding %lu bytes", + (unsigned long) cdata); + + if (cdata != 0) + return TRUE; + + qinfo->freceived = TRUE; + + /* If we have already sent the denial, we are done. */ + if (qinfo->fsent) + { + xfree (qtrans->pinfo); + utransfree (qtrans); + } + + return TRUE; +} + +/* This is called when a file has been completely received. It sends + a response to the remote system. */ + +/*ARGSUSED*/ +static boolean +frec_file_end (qtrans, qdaemon, zdata, cdata) + struct stransfer *qtrans; + struct sdaemon *qdaemon; + const char *zdata; + size_t cdata; +{ + struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; + const char *zerr; + boolean fnever; + + DEBUG_MESSAGE3 (DEBUG_UUCP_PROTO, "frec_file_end: %s to %s (freplied %s)", + qtrans->s.zfrom, qtrans->s.zto, + qinfo->freplied ? "TRUE" : "FALSE"); + + if (qdaemon->qproto->pffile != NULL) + { + boolean fhandled; + + if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, FALSE, FALSE, + (long) -1, &fhandled)) + { + (void) ffileclose (qtrans->e); + (void) remove (qinfo->ztemp); + urrec_free (qtrans); + return FALSE; + } + if (fhandled) + return TRUE; + } + + qinfo->freceived = TRUE; + + fnever = FALSE; + + if (! ffileclose (qtrans->e)) + { + zerr = strerror (errno); + ulog (LOG_ERROR, "%s: close: %s", qtrans->s.zto, zerr); + } + else if (! fsysdep_move_file (qinfo->ztemp, qinfo->zfile, qinfo->fspool, + FALSE, ! qinfo->fspool, + (qinfo->flocal + ? qtrans->s.zuser + : (const char *) NULL))) + { + zerr = "could not move to final location"; + ulog (LOG_ERROR, "%s: %s", qinfo->zfile, zerr); + fnever = TRUE; + } + else + { + if (! qinfo->fspool) + { + unsigned int imode; + + /* Unless we can change the ownership of the file, the only + choice to make about these bits is whether to set the + execute bit or not. */ + if ((qtrans->s.imode & 0111) != 0) + imode = 0777; + else + imode = 0666; + (void) fsysdep_change_mode (qinfo->zfile, imode); + } + + zerr = NULL; + } + + if (zerr != NULL) + (void) remove (qinfo->ztemp); + + ustats (zerr == NULL, qtrans->s.zuser, qdaemon->qsys->uuconf_zname, + FALSE, qtrans->cbytes, qtrans->isecs, qtrans->imicros, + qdaemon->fmaster); + + if (zerr == NULL) + { + if (qinfo->zmail != NULL && *qinfo->zmail != '\0') + (void) fmail_transfer (TRUE, qtrans->s.zuser, qinfo->zmail, + (const char *) NULL, + qtrans->s.zfrom, qdaemon->qsys->uuconf_zname, + qtrans->s.zto, (const char *) NULL, + (const char *) NULL); + + if (qtrans->s.pseq != NULL) + (void) fsysdep_did_work (qtrans->s.pseq); + + if (! qinfo->flocal) + { + /* Remember that we have received this file, so that if the + connection drops at this point we won't receive it again. + We could check the return value here, but if we return + FALSE we couldn't do anything but drop the connection, + which would hardly be reasonable. Instead we trust that + the administrator will notice and handle any error + messages, which are very unlikely to occur if everything + is set up correctly. */ + (void) fsysdep_remember_reception (qdaemon->qsys, qtrans->s.zto, + qtrans->s.ztemp); + } + } + else + { + /* If the transfer failed, we send mail if it was requested + locally and if it can never succeed. */ + if (qinfo->flocal && fnever) + { + (void) fmail_transfer (FALSE, qtrans->s.zuser, qinfo->zmail, + zerr, qtrans->s.zfrom, + qdaemon->qsys->uuconf_zname, + qtrans->s.zto, (const char *) NULL, + (const char *) NULL); + (void) fsysdep_did_work (qtrans->s.pseq); + } + } + + /* If this is an execution request, we must create the execution + file itself. */ + if (qtrans->s.bcmd == 'E' && zerr == NULL) + { + char *zxqt, *zxqtfile, *ztemp; + FILE *e; + boolean fbad; + + /* We get an execution file name by simply replacing the leading + D in the received file name with an X. This pretty much + always has to work since we can always receive a file name + starting with X, so the system dependent code must be + prepared to see one. */ + zxqt = zbufcpy (qtrans->s.zto); + zxqt[0] = 'X'; + zxqtfile = zsysdep_spool_file_name (qdaemon->qsys, zxqt, + (pointer) NULL); + ubuffree (zxqt); + + if (zxqtfile == NULL) + { + urrec_free (qtrans); + return FALSE; + } + + /* We have to write via a temporary file, because otherwise + uuxqt might pick up the file before we have finished writing + it. */ + e = NULL; + ztemp = zsysdep_receive_temp (qdaemon->qsys, zxqtfile, "D.0"); + if (ztemp != NULL) + e = esysdep_fopen (ztemp, FALSE, FALSE, TRUE); + + if (e == NULL) + { + ubuffree (zxqtfile); + ubuffree (ztemp); + urrec_free (qtrans); + return FALSE; + } + + fprintf (e, "U %s %s\n", qtrans->s.zuser, qdaemon->qsys->uuconf_zname); + fprintf (e, "F %s\n", qtrans->s.zto); + fprintf (e, "I %s\n", qtrans->s.zto); + if (strchr (qtrans->s.zoptions, 'N') != NULL) + fprintf (e, "N\n"); + if (strchr (qtrans->s.zoptions, 'Z') != NULL) + fprintf (e, "Z\n"); + if (strchr (qtrans->s.zoptions, 'R') != NULL) + fprintf (e, "R %s\n", qtrans->s.znotify); + if (strchr (qtrans->s.zoptions, 'e') != NULL) + fprintf (e, "e\n"); + fprintf (e, "C %s\n", qtrans->s.zcmd); + + fbad = FALSE; + if (fclose (e) == EOF) + { + ulog (LOG_ERROR, "fclose: %s", strerror (errno)); + (void) remove (ztemp); + fbad = TRUE; + } + + if (! fbad) + { + if (! fsysdep_move_file (ztemp, zxqtfile, TRUE, FALSE, FALSE, + (const char *) NULL)) + fbad = TRUE; + } + + ubuffree (zxqtfile); + ubuffree (ztemp); + + if (fbad) + { + urrec_free (qtrans); + return FALSE; + } + } + + /* Prepare to send the completion string to the remote system. If + we have not yet replied to the remote send request, we leave the + transfer structure on the remote queue. Otherwise we add it to + the send queue. The psendfn field will already be set. */ + qinfo->fmoved = zerr == NULL; + if (qinfo->freplied) + return fqueue_send (qdaemon, qtrans); + + return TRUE; +} + +/* Send the final confirmation string to the remote system. */ + +static boolean +frec_file_send_confirm (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; + const char *zsend; + boolean fret; + + if (! qinfo->fmoved) + zsend = "CN5"; + else if (! qdaemon->frequest_hangup) + zsend = "CY"; + else + { +#if DEBUG > 0 + if (qdaemon->fmaster) + ulog (LOG_FATAL, "frec_file_send_confirm: Can't happen"); +#endif + + DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, + "frec_send_file_confirm: Requesting remote to transfer control"); + zsend = "CYM"; + } + + fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, zsend, + qtrans->ilocal, qtrans->iremote); + + /* Now, if that was a remote command, then when the confirmation + message is acked we no longer have to remember that we received + that file. */ + if (! qinfo->flocal && qinfo->fmoved) + usent_receive_ack (qdaemon, qtrans); + + urrec_free (qtrans); + return fret; +} + +/* Discard a temporary file if it is not useful. A temporary file is + useful if it could be used to restart a receive. This is called if + the connection is lost. It is only called if qtrans->frecfile is + TRUE. */ + +boolean +frec_discard_temp (qdaemon, qtrans) + struct sdaemon *qdaemon; + struct stransfer *qtrans; +{ + struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; + + if ((qdaemon->ifeatures & FEATURE_RESTART) == 0 + || qtrans->s.ztemp == NULL + || qtrans->s.ztemp[0] != 'D' + || strcmp (qtrans->s.ztemp, "D.0") == 0) + (void) remove (qinfo->ztemp); + return TRUE; +} diff --git a/gnu/libexec/uucp/uucico/send.c b/gnu/libexec/uucp/uucico/send.c new file mode 100644 index 0000000000..9337de39c6 --- /dev/null +++ b/gnu/libexec/uucp/uucico/send.c @@ -0,0 +1,1273 @@ +/* send.c + Routines to send a file. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char send_rcsid[] = "$Id: send.c,v 1.1 1993/08/04 19:36:29 jtc Exp $"; +#endif + +#include + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" +#include "prot.h" +#include "trans.h" + +/* We keep this information in the pinfo field of the stransfer + structure. */ +struct ssendinfo +{ + /* Local user to send mail to (may be NULL). */ + char *zmail; + /* Full file name. */ + char *zfile; + /* Number of bytes in file. */ + long cbytes; + /* TRUE if this was a local request. */ + boolean flocal; + /* TRUE if this is a spool directory file. */ + boolean fspool; + /* TRUE if the file has been completely sent. */ + boolean fsent; + /* Execution file for sending an unsupported E request. */ + char *zexec; +}; + +/* Local functions. */ + +static void usfree_send P((struct stransfer *qtrans)); +static boolean flocal_send_fail P((struct stransfer *qtrans, + struct scmd *qcmd, + const struct uuconf_system *qsys, + const char *zwhy)); +static boolean flocal_send_request P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); +static boolean flocal_send_await_reply P((struct stransfer *qtrans, + struct sdaemon *qdaemon, + const char *zdata, size_t cdata)); +static boolean flocal_send_cancelled P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); +static boolean flocal_send_open_file P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); +static boolean fremote_rec_fail P((struct sdaemon *qdaemon, + enum tfailure twhy, int iremote)); +static boolean fremote_rec_fail_send P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); +static boolean fremote_rec_reply P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); +static boolean fsend_file_end P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); +static boolean fsend_await_confirm P((struct stransfer *qtrans, + struct sdaemon *qdaemon, + const char *zdata, size_t cdata)); +static boolean fsend_exec_file_init P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); +static void usadd_exec_line P((char **pz, size_t *pcalc, size_t *pclen, + int bcmd, const char *z1, const char *z2)); +static boolean fsend_exec_file P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); + +/* Free up a send stransfer structure. */ + +static void +usfree_send (qtrans) + struct stransfer *qtrans; +{ + struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; + + if (qinfo != NULL) + { + ubuffree (qinfo->zmail); + ubuffree (qinfo->zfile); + ubuffree (qinfo->zexec); + xfree (qtrans->pinfo); + } + + utransfree (qtrans); +} + +/* Set up a local request to send a file. This may be called before + we have even tried to call the remote system. + + If we are using a traditional protocol, which doesn't support + channel numbers and doesn't permit the file to be sent until an + acknowledgement has been received, the sequence of function calls + looks like this: + + flocal_send_file_init --> fqueue_local + flocal_send_request (sends S request) --> fqueue_receive + flocal_send_await_reply (waits for SY) --> fqueue_send + flocal_send_open_file (opens file, calls pffile) --> fqueue_send + send file + fsend_file_end (calls pffile) --> fqueue_receive + fsend_await_confirm (waits for CY) + + If flocal_send_await_reply gets an SN, it deletes the request. If + the SY reply contains a file position at which to start sending, + flocal_send_await_reply sets qinfo->ipos. + + This gets more complex if the protocol supports channels. In that + case, we want to start sending the file data immediately, to avoid + the round trip delay between flocal_send_request and + flocal_send_await_reply. To do this, flocal_send_request calls + fqueue_send rather than fqueue_receive. The main execution + sequence looks like this: + + flocal_send_file_init --> fqueue_local + flocal_send_request (sends S request) --> fqueue_send + flocal_send_open_file (opens file, calls pffile) --> fqueue_send + send file + fsend_file_end (calls pffile) --> fqueue_receive + sometime: flocal_send_await_reply (waits for SY) + fsend_await_confirm (waits for CY) + + In this case flocal_send_await_reply must be run before + fsend_await_confirm; it may be run anytime after + flocal_send_request. + + If flocal_send_await_reply is called before the entire file has + been sent: if it gets an SN, it calls flocal_send_cancelled to send + an empty data block to inform the remote system that the file + transfer has stopped. If it gets a file position request, it must + adjust the file position accordingly. + + If flocal_send_await_reply is called after the entire file has been + sent: if it gets an SN, it can simply delete the request. It can + ignore any file position request. + + If the request is not deleted, flocal_send_await_reply must arrange + for the next string to be passed to fsend_await_confirm. + Presumably fsend_await_confirm will only be called after the entire + file has been sent. + + Just to make things even more complex, these same routines support + sending execution requests, since that is much like sending a file. + For an execution request, the bcmd character will be E rather than + S. If an execution request is being sent to a system which does + not support them, it must be sent as two S requests instead. The + second one will be the execution file, but no actual file is + created; instead the zexec and znext fields in the ssendinfo + structure are used. So if the bcmd character is E, then if the + zexec field is NULL, the data file is being sent, otherwise the + fake execution file is being sent. */ + +boolean +flocal_send_file_init (qdaemon, qcmd) + struct sdaemon *qdaemon; + struct scmd *qcmd; +{ + const struct uuconf_system *qsys; + boolean fspool; + char *zfile; + long cbytes; + struct ssendinfo *qinfo; + struct stransfer *qtrans; + + qsys = qdaemon->qsys; + + if (qdaemon->fcaller + ? ! qsys->uuconf_fcall_transfer + : ! qsys->uuconf_fcalled_transfer) + { + /* uux or uucp should have already made sure that the transfer + is possible, but it might have changed since then. */ + if (! qsys->uuconf_fcall_transfer + && ! qsys->uuconf_fcalled_transfer) + return flocal_send_fail ((struct stransfer *) NULL, qcmd, qsys, + "not permitted to transfer files"); + + /* We can't do the request now, but it may get done later. */ + return TRUE; + } + + /* The 'C' option means that the file has been copied to the spool + directory. */ + if (strchr (qcmd->zoptions, 'C') == NULL + && ! fspool_file (qcmd->zfrom)) + { + fspool = FALSE; + if (! fin_directory_list (qcmd->zfrom, + qsys->uuconf_pzlocal_send, + qsys->uuconf_zpubdir, TRUE, + TRUE, qcmd->zuser)) + return flocal_send_fail ((struct stransfer *) NULL, qcmd, qsys, + "not permitted to send"); + zfile = zbufcpy (qcmd->zfrom); + } + else + { + fspool = TRUE; + zfile = zsysdep_spool_file_name (qsys, qcmd->ztemp, qcmd->pseq); + if (zfile == NULL) + return FALSE; + } + + /* Make sure we meet any local size restrictions. The connection + may not have been opened at this point, so we can't check remote + size restrictions. */ + cbytes = csysdep_size (zfile); + if (cbytes < 0) + { + ubuffree (zfile); + if (cbytes != -1) + return FALSE; + /* A cbytes value of -1 means that the file does not exist. + This can happen legitimately if it has already been sent from + the spool directory. */ + if (! fspool) + return flocal_send_fail ((struct stransfer *) NULL, qcmd, qsys, + "does not exist"); + (void) fsysdep_did_work (qcmd->pseq); + return TRUE; + } + + if (qdaemon->clocal_size != -1 + && qdaemon->clocal_size < cbytes) + { + ubuffree (zfile); + + if (qdaemon->cmax_ever == -2) + { + long c1, c2; + + c1 = cmax_size_ever (qsys->uuconf_qcall_local_size); + c2 = cmax_size_ever (qsys->uuconf_qcalled_local_size); + if (c1 > c2) + qdaemon->cmax_ever = c1; + else + qdaemon->cmax_ever = c2; + } + + if (qdaemon->cmax_ever != -1 + && qdaemon->cmax_ever < qcmd->cbytes) + return flocal_send_fail ((struct stransfer *) NULL, qcmd, qsys, + "too large to send"); + + return TRUE; + } + + /* We are now prepared to send the command to the remote system. We + queue up a transfer request to send the command when we are + ready. */ + qinfo = (struct ssendinfo *) xmalloc (sizeof (struct ssendinfo)); + if (strchr (qcmd->zoptions, 'm') == NULL) + qinfo->zmail = NULL; + else + qinfo->zmail = zbufcpy (qcmd->zuser); + qinfo->zfile = zfile; + qinfo->cbytes = cbytes; + qinfo->flocal = TRUE; + qinfo->fspool = fspool; + qinfo->fsent = FALSE; + qinfo->zexec = NULL; + + qtrans = qtransalc (qcmd); + qtrans->psendfn = flocal_send_request; + qtrans->pinfo = (pointer) qinfo; + + return fqueue_local (qdaemon, qtrans); +} + +/* Clean up after a failing local send request. If zwhy is not NULL, + this reports an error to the log file and to the user. */ + +static boolean +flocal_send_fail (qtrans, qcmd, qsys, zwhy) + struct stransfer *qtrans; + struct scmd *qcmd; + const struct uuconf_system *qsys; + const char *zwhy; +{ + if (zwhy != NULL) + { + char *zfree; + + if (qcmd->bcmd != 'E') + zfree = NULL; + else + { + zfree = zbufalc (sizeof "Execution of \"\": " + + strlen (qcmd->zcmd) + + strlen (zwhy)); + sprintf (zfree, "Execution of \"%s\": %s", qcmd->zcmd, zwhy); + zwhy = zfree; + } + + ulog (LOG_ERROR, "%s: %s", qcmd->zfrom, zwhy); + (void) fmail_transfer (FALSE, qcmd->zuser, (const char *) NULL, + zwhy, qcmd->zfrom, (const char *) NULL, + qcmd->zto, qsys->uuconf_zname, + zsysdep_save_temp_file (qcmd->pseq)); + + ubuffree (zfree); + } + + (void) fsysdep_did_work (qcmd->pseq); + + if (qtrans != NULL) + usfree_send (qtrans); + + return TRUE; +} + +/* This is called when we are ready to send the request to the remote + system. We form the request and send it over. If the protocol + does not support multiple channels, we start waiting for the + response; otherwise we can start sending the file immediately. */ + +static boolean +flocal_send_request (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; + char *zsend; + const char *znotify; + char absize[20]; + boolean fret; + + /* Make sure the file meets any remote size restrictions. */ + if (qdaemon->cmax_receive != -1 + && qdaemon->cmax_receive < qinfo->cbytes) + return flocal_send_fail (qtrans, &qtrans->s, qdaemon->qsys, + "too large for receiver"); + + /* Construct the notify string to send. If we are going to send a + size or an execution command, it must be non-empty. */ + znotify = qtrans->s.znotify; + if (znotify == NULL) + znotify = ""; + if ((qdaemon->ifeatures & FEATURE_SIZES) != 0 + || (qtrans->s.bcmd == 'E' + && (qdaemon->ifeatures & FEATURE_EXEC) != 0)) + { + if (*znotify == '\0') + znotify = "\"\""; + } + else + { + /* We don't need a notify string. Some crufty UUCP code can't + handle a pair of double quotes. */ + if (strcmp (znotify, "\"\"") == 0) + znotify = ""; + } + + /* Construct the size string to send. */ + if ((qdaemon->ifeatures & FEATURE_SIZES) == 0 + && (qtrans->s.bcmd != 'E' + || (qdaemon->ifeatures & FEATURE_EXEC) == 0)) + absize[0] = '\0'; + else if ((qdaemon->ifeatures & FEATURE_V103) == 0) + sprintf (absize, "0x%lx", (unsigned long) qinfo->cbytes); + else + sprintf (absize, "%ld", qinfo->cbytes); + + zsend = zbufalc (strlen (qtrans->s.zfrom) + strlen (qtrans->s.zto) + + strlen (qtrans->s.zuser) + strlen (qtrans->s.zoptions) + + strlen (qtrans->s.ztemp) + strlen (znotify) + + strlen (absize) + + (qtrans->s.zcmd != NULL ? strlen (qtrans->s.zcmd) : 0) + + 50); + + /* If this an execution request and the other side supports + execution requests, we send an E command. Otherwise we send an S + command. The case of an execution request when we are sending + the fake execution file is handled just like an S request at this + point. */ + if (qtrans->s.bcmd == 'E' + && (qdaemon->ifeatures & FEATURE_EXEC) != 0) + { + /* Send the string + E zfrom zto zuser zoptions ztemp imode znotify size zcmd + to the remote system. We put a '-' in front of the (possibly + empty) options and a '0' in front of the mode. */ + sprintf (zsend, "E %s %s %s -%s %s 0%o %s %s %s", qtrans->s.zfrom, + qtrans->s.zto, qtrans->s.zuser, qtrans->s.zoptions, + qtrans->s.ztemp, qtrans->s.imode, znotify, absize, + qtrans->s.zcmd); + } + else + { + const char *zoptions, *zdummy; + + /* Send the string + S zfrom zto zuser zoptions ztemp imode znotify + to the remote system. We put a '-' in front of the (possibly + empty) options and a '0' in front of the mode. If size + negotiation is supported, we also send the size; in this case + if znotify is empty we must send it as "". If this is really + an execution request, we have to simplify the options string + to remove the various execution options which may confuse the + remote system. SVR4 expects a string "dummy" between the + notify string and the size; I don't know why. */ + if (qtrans->s.bcmd != 'E') + zoptions = qtrans->s.zoptions; + else if (strchr (qtrans->s.zoptions, 'C') != NULL) + { + /* This should set zoptions to "C", but at least one UUCP + program gets confused by it. That means that it will + fail in certain cases, but I suppose we might as well + kowtow to compatibility. This shouldn't matter to any + other program, I hope. */ + zoptions = ""; + } + else + zoptions = "c"; + + if ((qdaemon->ifeatures & FEATURE_SVR4) != 0) + zdummy = " dummy "; + else + zdummy = " "; + + sprintf (zsend, "S %s %s %s -%s %s 0%o %s%s%s", qtrans->s.zfrom, + qtrans->s.zto, qtrans->s.zuser, zoptions, + qtrans->s.ztemp, qtrans->s.imode, znotify, zdummy, + absize); + } + + fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, zsend, qtrans->ilocal, + qtrans->iremote); + ubuffree (zsend); + if (! fret) + { + usfree_send (qtrans); + return FALSE; + } + + /* If we are using a protocol which can make multiple channels, then + we can open and send the file whenever we are ready. This is + because we will be able to distinguish the response by the + channel it is directed to. This assumes that every protocol + which supports multiple channels also supports sending the file + position in mid-stream, since otherwise we would not be able to + restart files. */ + qtrans->fcmd = TRUE; + qtrans->psendfn = flocal_send_open_file; + qtrans->precfn = flocal_send_await_reply; + + if (qdaemon->qproto->cchans > 1) + return fqueue_send (qdaemon, qtrans); + else + return fqueue_receive (qdaemon, qtrans); +} + +/* This is called when a reply is received for the send request. As + described at length above, if the protocol supports multiple + channels we may be in the middle of sending the file, or we may + even finished sending the file. */ + +static boolean +flocal_send_await_reply (qtrans, qdaemon, zdata, cdata) + struct stransfer *qtrans; + struct sdaemon *qdaemon; + const char *zdata; + size_t cdata; +{ + struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; + char bcmd; + + if (qtrans->s.bcmd == 'E' + && (qdaemon->ifeatures & FEATURE_EXEC) != 0) + bcmd = 'E'; + else + bcmd = 'S'; + if (zdata[0] != bcmd + || (zdata[1] != 'Y' && zdata[1] != 'N')) + { + ulog (LOG_ERROR, "%s: Bad response to %c request: \"%s\"", + qtrans->s.zfrom, bcmd, zdata); + usfree_send (qtrans); + return FALSE; + } + + if (zdata[1] == 'N') + { + const char *zerr; + boolean fnever; + + fnever = TRUE; + if (zdata[2] == '2') + zerr = "permission denied by remote"; + else if (zdata[2] == '4') + { + zerr = "remote cannot create work files"; + fnever = FALSE; + } + else if (zdata[2] == '6') + { + zerr = "too large for remote now"; + fnever = FALSE; + } + else if (zdata[2] == '7') + { + /* The file is too large to ever send. */ + zerr = "too large for remote"; + } + else if (zdata[2] == '8') + { + /* The file was already received by the remote system. This + is not an error, it just means that the ack from the + remote was lost in the previous conversation, and there + is no need to resend the file. */ + zerr = NULL; + } + else + zerr = "unknown reason"; + + if (! fnever) + { + if (qtrans->s.bcmd == 'E') + ulog (LOG_ERROR, "Execution of \"%s\": %s", qtrans->s.zcmd, + zerr); + else + ulog (LOG_ERROR, "%s: %s", qtrans->s.zfrom, zerr); + } + else + { + if (! flocal_send_fail ((struct stransfer *) NULL, &qtrans->s, + qdaemon->qsys, zerr)) + return FALSE; + } + + /* If the protocol does not support multiple channels, we can + simply remove the transaction. Otherwise we must make sure + the remote side knows that we have finished sending the file + data. If we have already sent the entire file, there will be + no confusion. */ + if (qdaemon->qproto->cchans == 1 || qinfo->fsent) + { + usfree_send (qtrans); + return TRUE; + } + else + { + qtrans->psendfn = flocal_send_cancelled; + qtrans->precfn = NULL; + qtrans->fsendfile = FALSE; + return fqueue_send (qdaemon, qtrans); + } + } + + /* A number following the SY or EY is the file position to start + sending from. If we are already sending the file, we must set + the position accordingly. */ + if (zdata[2] != '\0') + { + long cskip; + + cskip = strtol ((char *) (zdata + 2), (char **) NULL, 0); + if (cskip > 0 && qtrans->ipos < cskip) + { + if (qtrans->fsendfile && ! qinfo->fsent) + { + if (! ffileseek (qtrans->e, cskip)) + { + ulog (LOG_ERROR, "seek: %s", strerror (errno)); + usfree_send (qtrans); + return FALSE; + } + } + qtrans->ipos = cskip; + } + } + + /* Now queue up to send the file or to wait for the confirmation. + We already set psendfn at the end of flocal_send_request. If the + protocol supports multiple channels, we have already called + fqueue_send; calling it again would move the request in the + queue, which would make the log file a bit confusing. */ + qtrans->precfn = fsend_await_confirm; + if (qinfo->fsent) + return fqueue_receive (qdaemon, qtrans); + else if (qdaemon->qproto->cchans <= 1) + return fqueue_send (qdaemon, qtrans); + else + return TRUE; +} + +/* Open the file, if any, and prepare to send it. */ + +static boolean +flocal_send_open_file (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; + const char *zuser; + + /* If this is not a fake execution file, open it. */ + if (qinfo->zexec == NULL) + { + /* If there is an ! in the user name, this is a remote request + queued up by fremote_xcmd_init. */ + zuser = qtrans->s.zuser; + if (strchr (zuser, '!') != NULL) + zuser = NULL; + + qtrans->e = esysdep_open_send (qdaemon->qsys, qinfo->zfile, + ! qinfo->fspool, zuser); + if (! ffileisopen (qtrans->e)) + { + (void) fmail_transfer (FALSE, qtrans->s.zuser, + (const char *) NULL, + "cannot open file", + qtrans->s.zfrom, (const char *) NULL, + qtrans->s.zto, + qdaemon->qsys->uuconf_zname, + zsysdep_save_temp_file (qtrans->s.pseq)); + (void) fsysdep_did_work (qtrans->s.pseq); + usfree_send (qtrans); + + /* Unfortunately, there is no way to cancel a file send + after we've already put it in progress. So we have to + return FALSE to drop the connection. */ + return FALSE; + } + } + + /* If flocal_send_await_reply has received a reply with a file + position, it will have set qtrans->ipos to the position at which + to start. */ + if (qtrans->ipos > 0) + { + if (qinfo->zexec != NULL) + { + if (qtrans->ipos > qtrans->cbytes) + qtrans->ipos = qtrans->cbytes; + } + else + { + if (! ffileseek (qtrans->e, qtrans->ipos)) + { + ulog (LOG_ERROR, "seek: %s", strerror (errno)); + usfree_send (qtrans); + return FALSE; + } + } + } + + /* We don't bother to log sending the execution file. */ + if (qinfo->zexec == NULL) + { + const char *zsend; + char *zalc; + + if (qtrans->s.bcmd != 'E') + { + zsend = qtrans->s.zfrom; + zalc = NULL; + } + else + { + zalc = zbufalc (strlen (qtrans->s.zcmd) + sizeof " ()" + + strlen (qtrans->s.zfrom)); + sprintf (zalc, "%s (%s)", qtrans->s.zcmd, qtrans->s.zfrom); + zsend = zalc; + } + qtrans->zlog = zbufalc (sizeof "Sending " + strlen (zsend)); + sprintf (qtrans->zlog, "Sending %s", zsend); + ubuffree (zalc); + } + + if (qdaemon->qproto->pffile != NULL) + { + boolean fhandled; + + if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, TRUE, TRUE, + qinfo->cbytes, &fhandled)) + { + usfree_send (qtrans); + return FALSE; + } + + if (fhandled) + return TRUE; + } + + if (qinfo->zexec != NULL) + qtrans->psendfn = fsend_exec_file; + else + { + qtrans->fsendfile = TRUE; + qtrans->psendfn = fsend_file_end; + } + + return fqueue_send (qdaemon, qtrans); +} + +/* Cancel a file send by sending an empty buffer. This is only called + for a protocol which supports multiple channels. It is needed + so that both systems agree as to when a channel is no longer + needed. */ + +static boolean +flocal_send_cancelled (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + char *zdata; + size_t cdata; + boolean fret; + + zdata = (*qdaemon->qproto->pzgetspace) (qdaemon, &cdata); + if (zdata == NULL) + { + usfree_send (qtrans); + return FALSE; + } + + fret = (*qdaemon->qproto->pfsenddata) (qdaemon, zdata, (size_t) 0, + qtrans->ilocal, qtrans->iremote, + qtrans->ipos); + usfree_send (qtrans); + return fret; +} + +/* A remote request to receive a file (meaning that we have to send a + file). The sequence of functions calls is as follows: + + fremote_rec_file_init (open file) --> fqueue_remote + fremote_rec_reply (send RY, call pffile) --> fqueue_send + send file + fsend_file_end (calls pffile) --> fqueue_receive + fsend_await_confirm (waits for CY) + */ + +boolean +fremote_rec_file_init (qdaemon, qcmd, iremote) + struct sdaemon *qdaemon; + struct scmd *qcmd; + int iremote; +{ + const struct uuconf_system *qsys; + char *zfile; + long cbytes; + unsigned int imode; + openfile_t e; + struct ssendinfo *qinfo; + struct stransfer *qtrans; + + qsys = qdaemon->qsys; + + if (! qsys->uuconf_fsend_request) + { + ulog (LOG_ERROR, "%s: not permitted to send files to remote", + qcmd->zfrom); + return fremote_rec_fail (qdaemon, FAILURE_PERM, iremote); + } + + if (fspool_file (qcmd->zfrom)) + { + ulog (LOG_ERROR, "%s: not permitted to send", qcmd->zfrom); + return fremote_rec_fail (qdaemon, FAILURE_PERM, iremote); + } + + zfile = zsysdep_local_file (qcmd->zfrom, qsys->uuconf_zpubdir); + if (zfile != NULL) + { + char *zbased; + + zbased = zsysdep_add_base (zfile, qcmd->zto); + ubuffree (zfile); + zfile = zbased; + } + if (zfile == NULL) + return fremote_rec_fail (qdaemon, FAILURE_PERM, iremote); + + if (! fin_directory_list (zfile, qsys->uuconf_pzremote_send, + qsys->uuconf_zpubdir, TRUE, TRUE, + (const char *) NULL)) + { + ulog (LOG_ERROR, "%s: not permitted to send", zfile); + ubuffree (zfile); + return fremote_rec_fail (qdaemon, FAILURE_PERM, iremote); + } + + /* If the file is larger than the amount of space the other side + reported, we can't send it. Should we adjust this check based on + the restart position? */ + cbytes = csysdep_size (zfile); + if (cbytes != -1 + && ((qcmd->cbytes != -1 && qcmd->cbytes < cbytes) + || (qdaemon->cremote_size != -1 + && qdaemon->cremote_size < cbytes) + || (qdaemon->cmax_receive != -1 + && qdaemon->cmax_receive < cbytes))) + { + ulog (LOG_ERROR, "%s: too large to send", zfile); + ubuffree (zfile); + return fremote_rec_fail (qdaemon, FAILURE_SIZE, iremote); + } + + imode = ixsysdep_file_mode (zfile); + + e = esysdep_open_send (qsys, zfile, TRUE, (const char *) NULL); + if (! ffileisopen (e)) + { + ubuffree (zfile); + return fremote_rec_fail (qdaemon, FAILURE_OPEN, iremote); + } + + /* If the remote requested that the file send start from a + particular position, arrange to do so. */ + if (qcmd->ipos > 0) + { + if (! ffileseek (e, qcmd->ipos)) + { + ulog (LOG_ERROR, "seek: %s", strerror (errno)); + ubuffree (zfile); + return FALSE; + } + } + + qinfo = (struct ssendinfo *) xmalloc (sizeof (struct ssendinfo)); + qinfo->zmail = NULL; + qinfo->zfile = zfile; + qinfo->cbytes = cbytes; + qinfo->flocal = FALSE; + qinfo->fspool = FALSE; + qinfo->fsent = FALSE; + qinfo->zexec = NULL; + + qtrans = qtransalc (qcmd); + qtrans->psendfn = fremote_rec_reply; + qtrans->iremote = iremote; + qtrans->pinfo = (pointer) qinfo; + qtrans->e = e; + qtrans->ipos = qcmd->ipos; + qtrans->s.imode = imode; + + return fqueue_remote (qdaemon, qtrans); +} + +/* Reply to a receive request from the remote system, and prepare to + start sending the file. */ + +static boolean +fremote_rec_reply (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; + char absend[50]; + + sprintf (absend, "RY 0%o 0x%lx", qtrans->s.imode, + (unsigned long) qinfo->cbytes); + if (! (*qdaemon->qproto->pfsendcmd) (qdaemon, absend, qtrans->ilocal, + qtrans->iremote)) + { + (void) ffileclose (qtrans->e); + usfree_send (qtrans); + return FALSE; + } + + qtrans->zlog = zbufalc (sizeof "Sending " + strlen (qtrans->s.zfrom)); + sprintf (qtrans->zlog, "Sending %s", qtrans->s.zfrom); + + if (qdaemon->qproto->pffile != NULL) + { + boolean fhandled; + + if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, TRUE, TRUE, + qinfo->cbytes, &fhandled)) + { + usfree_send (qtrans); + return FALSE; + } + + if (fhandled) + return TRUE; + } + + qtrans->fsendfile = TRUE; + qtrans->psendfn = fsend_file_end; + qtrans->precfn = fsend_await_confirm; + + return fqueue_send (qdaemon, qtrans); +} + +/* If we can't send a file as requested by the remote system, queue up + a failure reply which will be sent when possible. */ + +static boolean +fremote_rec_fail (qdaemon, twhy, iremote) + struct sdaemon *qdaemon; + enum tfailure twhy; + int iremote; +{ + enum tfailure *ptinfo; + struct stransfer *qtrans; + + ptinfo = (enum tfailure *) xmalloc (sizeof (enum tfailure)); + *ptinfo = twhy; + + qtrans = qtransalc ((struct scmd *) NULL); + qtrans->psendfn = fremote_rec_fail_send; + qtrans->iremote = iremote; + qtrans->pinfo = (pointer) ptinfo; + + return fqueue_remote (qdaemon, qtrans); +} + +/* Send a failure string for a receive command to the remote system; + this is called when we are ready to reply to the command. */ + +static boolean +fremote_rec_fail_send (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + enum tfailure *ptinfo = (enum tfailure *) qtrans->pinfo; + const char *z; + boolean fret; + + switch (*ptinfo) + { + case FAILURE_PERM: + case FAILURE_OPEN: + z = "RN2"; + break; + case FAILURE_SIZE: + z = "RN6"; + break; + default: + z = "RN"; + break; + } + + fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, z, qtrans->ilocal, + qtrans->iremote); + xfree (qtrans->pinfo); + utransfree (qtrans); + return fret; +} + +/* This is called when the main loop has finished sending a file. It + prepares to wait for a response from the remote system. Note that + if this is a local request and the protocol supports multiple + channels, we may not even have received a confirmation of the send + request. */ + +static boolean +fsend_file_end (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; + + if (qdaemon->qproto->pffile != NULL) + { + boolean fhandled; + + if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, FALSE, TRUE, + (long) -1, &fhandled)) + { + usfree_send (qtrans); + return FALSE; + } + + if (fhandled) + return TRUE; + } + + qinfo->fsent = TRUE; + + /* qtrans->precfn should have been set by a previous function. */ + qtrans->fcmd = TRUE; + return fqueue_receive (qdaemon, qtrans); +} + +/* Handle the confirmation string received after sending a file. */ + +/*ARGSUSED*/ +static boolean +fsend_await_confirm (qtrans, qdaemon, zdata, cdata) + struct stransfer *qtrans; + struct sdaemon *qdaemon; + const char *zdata; + size_t cdata; +{ + struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; + boolean fnever; + const char *zerr; + + if (qinfo->zexec == NULL) + (void) ffileclose (qtrans->e); + + fnever = FALSE; + if (zdata[0] != 'C' + || (zdata[1] != 'Y' && zdata[1] != 'N')) + { + zerr = "bad confirmation from remote"; + ulog (LOG_ERROR, "%s: %s \"%s\"", qtrans->s.zfrom, zerr, zdata); + } + else if (zdata[1] == 'N') + { + fnever = TRUE; + if (zdata[2] == '5') + { + zerr = "file could not be stored in final location"; + ulog (LOG_ERROR, "%s: %s", qtrans->s.zfrom, zerr); + } + else + { + zerr = "file send failed for unknown reason"; + ulog (LOG_ERROR, "%s: %s \"%s\"", qtrans->s.zfrom, zerr, zdata); + } + } + else + { + zerr = NULL; + + /* If we receive CYM, it means that the other side wants us to + hang up so that they can send us something. The + fhangup_requested field is checked in the main loop. */ + if (zdata[2] == 'M' && qdaemon->fmaster) + { + DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, + "fsend_await_confirm: Remote has requested transfer of control"); + qdaemon->fhangup_requested = TRUE; + } + } + + ustats (zerr == NULL, qtrans->s.zuser, qdaemon->qsys->uuconf_zname, + TRUE, qtrans->cbytes, qtrans->isecs, qtrans->imicros, + qdaemon->fmaster); + + if (zerr == NULL) + { + /* If this is an execution request, and the remote system + doesn't support execution requests, we have to set up the + fake execution file and loop around again. */ + if (qtrans->s.bcmd == 'E' + && (qdaemon->ifeatures & FEATURE_EXEC) == 0 + && qinfo->zexec == NULL) + return fsend_exec_file_init (qtrans, qdaemon); + + /* Send mail about the transfer if requested. */ + if (qinfo->zmail != NULL && *qinfo->zmail != '\0') + (void) fmail_transfer (TRUE, qtrans->s.zuser, qinfo->zmail, + (const char *) NULL, + qtrans->s.zfrom, (const char *) NULL, + qtrans->s.zto, qdaemon->qsys->uuconf_zname, + (const char *) NULL); + + if (qtrans->s.pseq != NULL) + (void) fsysdep_did_work (qtrans->s.pseq); + } + else + { + /* If the file send failed, we only try to save the file and + send mail if it was requested locally and it will never + succeed. We send mail to qinfo->zmail if set, otherwise to + qtrans->s.zuser. I hope this is reasonable. */ + if (fnever && qinfo->flocal) + { + (void) fmail_transfer (FALSE, qtrans->s.zuser, qinfo->zmail, + zerr, qtrans->s.zfrom, (const char *) NULL, + qtrans->s.zto, qdaemon->qsys->uuconf_zname, + zsysdep_save_temp_file (qtrans->s.pseq)); + (void) fsysdep_did_work (qtrans->s.pseq); + } + } + + usfree_send (qtrans); + + return TRUE; +} + +/* Prepare to send an execution file to a system which does not + support execution requests. We build the execution file in memory, + and then call flocal_send_request as though we were sending a real + file. Instead of sending a file, the code in flocal_send_open_file + will arrange to call fsend_exec_file which will send data out of + the buffer we have created. */ + +static boolean +fsend_exec_file_init (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; + char *zxqtfile; + char abtname[CFILE_NAME_LEN]; + char abxname[CFILE_NAME_LEN]; + char *z; + size_t calc, clen; + + z = NULL; + calc = 0; + clen = 0; + + usadd_exec_line (&z, &calc, &clen, 'U', qtrans->s.zuser, + qdaemon->zlocalname); + usadd_exec_line (&z, &calc, &clen, 'F', qtrans->s.zto, ""); + usadd_exec_line (&z, &calc, &clen, 'I', qtrans->s.zto, ""); + if (strchr (qtrans->s.zoptions, 'N') != NULL) + usadd_exec_line (&z, &calc, &clen, 'N', "", ""); + if (strchr (qtrans->s.zoptions, 'Z') != NULL) + usadd_exec_line (&z, &calc, &clen, 'Z', "", ""); + if (strchr (qtrans->s.zoptions, 'R') != NULL) + usadd_exec_line (&z, &calc, &clen, 'R', qtrans->s.znotify, ""); + if (strchr (qtrans->s.zoptions, 'e') != NULL) + usadd_exec_line (&z, &calc, &clen, 'e', "", ""); + usadd_exec_line (&z, &calc, &clen, 'C', qtrans->s.zcmd, ""); + + qinfo->zexec = z; + qinfo->cbytes = clen; + + zxqtfile = zsysdep_data_file_name (qdaemon->qsys, qdaemon->zlocalname, + BDEFAULT_UUX_GRADE, TRUE, abtname, + (char *) NULL, abxname); + if (zxqtfile == NULL) + { + usfree_send (qtrans); + return FALSE; + } + ubuffree (zxqtfile); + + ubuffree ((char *) qtrans->s.zfrom); + qtrans->s.zfrom = zbufcpy (abtname); + ubuffree ((char *) qtrans->s.zto); + qtrans->s.zto = zbufcpy (abxname); + ubuffree ((char *) qtrans->s.zoptions); + qtrans->s.zoptions = zbufcpy ("C"); + ubuffree ((char *) qtrans->s.ztemp); + qtrans->s.ztemp = zbufcpy (abtname); + + qtrans->psendfn = flocal_send_request; + qtrans->precfn = NULL; + qtrans->ipos = 0; + qtrans->cbytes = 0; + qtrans->isecs = 0; + qtrans->imicros = 0; + qinfo->fsent = FALSE; + + return fqueue_send (qdaemon, qtrans); +} + +/* Add a line to the fake execution file. */ + +static void +usadd_exec_line (pz, pcalc, pclen, bcmd, z1, z2) + char **pz; + size_t *pcalc; + size_t *pclen; + int bcmd; + const char *z1; + const char *z2; +{ + size_t c1, c2; + char *znew; + + c1 = strlen (z1); + c2 = strlen (z2); + + if (*pclen + c1 + c2 + 4 >= *pcalc) + { + *pcalc += c1 + c2 + 100; + znew = zbufalc (*pcalc); + if (*pclen > 0) + { + memcpy (znew, *pz, *pclen); + ubuffree (*pz); + } + *pz = znew; + } + + znew = *pz + *pclen; + *znew++ = bcmd; + if (*z1 != '\0') + { + *znew++ = ' '; + memcpy (znew, z1, c1); + znew += c1; + if (*z2 != '\0') + { + *znew++ = ' '; + memcpy (znew, z2, c2); + znew += c2; + } + } + + /* In some bizarre non-Unix case we might have to worry about the + newline here. We don't know how a newline is normally written + out to a file, but whatever is written to a file is what we will + normally transfer. If that is not simply \n then this fake + execution file will not look like other execution files. */ + *znew++ = '\n'; + + *pclen = znew - *pz; +} + +/* This routine is called to send the contents of the fake execution + file. Normally file data is sent by the floop routine in trans.c, + but since we don't have an actual file we must do it here. This + routine sends the complete buffer, followed by a zero length + packet, and then calls fsend_file_end. */ + +static boolean +fsend_exec_file (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; + char *zdata; + size_t cdata; + size_t csend; + + zdata = (*qdaemon->qproto->pzgetspace) (qdaemon, &cdata); + if (zdata == NULL) + { + usfree_send (qtrans); + return FALSE; + } + + csend = qinfo->cbytes - qtrans->ipos; + if (csend > cdata) + csend = cdata; + + memcpy (zdata, qinfo->zexec + qtrans->ipos, csend); + + if (! (*qdaemon->qproto->pfsenddata) (qdaemon, zdata, csend, + qtrans->ilocal, qtrans->iremote, + qtrans->ipos)) + { + usfree_send (qtrans); + return FALSE; + } + + qtrans->cbytes += csend; + qtrans->ipos += csend; + + if (csend == 0) + return fsend_file_end (qtrans, qdaemon); + + /* Leave the job on the send queue. */ + + return TRUE; +} diff --git a/gnu/libexec/uucp/uucico/time.c b/gnu/libexec/uucp/uucico/time.c new file mode 100644 index 0000000000..c082662c2b --- /dev/null +++ b/gnu/libexec/uucp/uucico/time.c @@ -0,0 +1,130 @@ +/* time.c + Routines to deal with UUCP time spans. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char time_rcsid[] = "$Id: time.c,v 1.1 1993/08/04 19:36:30 jtc Exp $"; +#endif + +#include + +#if HAVE_TIME_H +#include +#endif + +#include "uudefs.h" +#include "uuconf.h" + +/* External functions. */ +#ifndef time +extern time_t time (); +#endif +#ifndef localtime +extern struct tm *localtime (); +#endif + +/* See if the current time matches a time span. If it does, return + TRUE, set *pival to the value for the matching span, and set + *pcretry to the retry for the matching span. Otherwise return + FALSE. */ + +boolean +ftimespan_match (qspan, pival, pcretry) + const struct uuconf_timespan *qspan; + long *pival; + int *pcretry; +{ + time_t inow; + struct tm *qtm; + int itm; + const struct uuconf_timespan *q; + + if (qspan == NULL) + return FALSE; + + time (&inow); + qtm = localtime (&inow); + + /* Get the number of minutes since Sunday for the time. */ + itm = qtm->tm_wday * 24 * 60 + qtm->tm_hour * 60 + qtm->tm_min; + + for (q = qspan; q != NULL; q = q->uuconf_qnext) + { + if (q->uuconf_istart <= itm && itm <= q->uuconf_iend) + { + if (pival != NULL) + *pival = q->uuconf_ival; + if (pcretry != NULL) + *pcretry = q->uuconf_cretry; + return TRUE; + } + } + + return FALSE; +} + +/* Determine the maximum size that may ever be transferred, according + to a timesize span. This returns -1 if there is no limit. */ + +long +cmax_size_ever (qtimesize) + const struct uuconf_timespan *qtimesize; +{ + long imax; + const struct uuconf_timespan *q; + + if (qtimesize == NULL) + return -1; + + /* Look through the list of spans. If there is any gap larger than + 1 hour, we assume there are no restrictions. Otherwise we keep + track of the largest value we see. I picked 1 hour arbitrarily, + on the theory that a 1 hour span to transfer large files might + actually occur, and is probably not an accident. */ + if (qtimesize->uuconf_istart >= 60) + return -1; + + imax = -1; + + for (q = qtimesize; q != NULL; q = q->uuconf_qnext) + { + if (q->uuconf_qnext == NULL) + { + if (q->uuconf_iend <= 6 * 24 * 60 + 23 * 60) + return -1; + } + else + { + if (q->uuconf_iend + 60 <= q->uuconf_qnext->uuconf_istart) + return -1; + } + + if (imax < q->uuconf_ival) + imax = q->uuconf_ival; + } + + return imax; +} diff --git a/gnu/libexec/uucp/uucico/trans.c b/gnu/libexec/uucp/uucico/trans.c new file mode 100644 index 0000000000..065caa4ac7 --- /dev/null +++ b/gnu/libexec/uucp/uucico/trans.c @@ -0,0 +1,1439 @@ +/* trans.c + Routines to handle file transfers. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char trans_rcsid[] = "$Id: trans.c,v 1.1 1993/08/04 19:36:31 jtc Exp $"; +#endif + +#include + +#include "uudefs.h" +#include "uuconf.h" +#include "prot.h" +#include "system.h" +#include "trans.h" + +/* Local functions. */ + +static void utqueue P((struct stransfer **, struct stransfer *, + boolean fhead)); +static void utdequeue P((struct stransfer *)); +static void utchanalc P((struct sdaemon *qdaemon, struct stransfer *qtrans)); +__inline__ static struct stransfer *qtchan P((int ichan)); +__inline__ static void utchanfree P((struct stransfer *qtrans)); +static boolean ftcharge P((struct sdaemon *qdaemon, + struct stransfer *qtrans, + boolean fsend, boolean fforce)); +static boolean fcheck_queue P((struct sdaemon *qdaemon)); +static boolean ftadd_cmd P((struct sdaemon *qdaemon, const char *z, + size_t cdata, int iremote, boolean flast)); +static boolean fremote_hangup_reply P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); +static boolean flocal_poll_file P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); + +/* Queue of transfer structures that are ready to start which have + been requested by the local system. These are only permitted to + start when the local system is the master. */ +static struct stransfer *qTlocal; + +/* Queue of transfer structures that are ready to start which have + been requested by the remote system. These are responses to + commands received from the remote system, and should be started as + soon as possible. */ +static struct stransfer *qTremote; + +/* Queue of transfer structures that have been started and want to + send information. This should be static, but the 'a' protocol + looks at it, at least for now. */ +struct stransfer *qTsend; + +/* Queue of transfer structures that have been started and are waiting + to receive information. */ +static struct stransfer *qTreceive; + +/* Queue of free transfer structures. */ +static struct stransfer *qTavail; + +/* Array of transfer structures indexed by local channel number. This + is maintained for local jobs. */ +static struct stransfer *aqTchan[IMAX_CHAN + 1]; + +/* Number of local channel numbers currently allocated. */ +static int cTchans; + +/* Next channel number to allocate. */ +static int iTchan; + +/* Array of transfer structures indexed by remote channel number. + This is maintained for remote jobs. */ +static struct stransfer *aqTremote[IMAX_CHAN + 1]; + +/* A structure used to charge time to file transfers. */ +struct scharge +{ + /* The transfer we are currently charging. */ + struct stransfer *qtrans; + /* The time at the last update. */ + long isecs; + long imicros; +}; + +/* We are always charging one send and one receive. */ +static struct scharge sTsend; +static struct scharge sTreceive; + +/* The minimum amount of time, in seconds, to wait between times we + check the spool directory, if we are busy transferring data. If we + have nothing to do, we will check the spool directory regardless of + how long ago the last check was. This should probably be + configurable. */ +#define CCHECKWAIT (600) + +/* The time we last checked the spool directory for work. This is set + from the return value of ixsysdep_process_time, not ixsysdep_time, + for convenience in the routines which use it. */ +static long iTchecktime; + +/* The size of the command we have read so far in ftadd_cmd. */ +static size_t cTcmdlen; + +/* The structure we use when waiting for an acknowledgement of a + confirmed received file in fsent_receive_ack, and a list of those + structures. */ + +struct sreceive_ack +{ + struct sreceive_ack *qnext; + char *zto; + char *ztemp; + boolean fmarked; +}; + +static struct sreceive_ack *qTreceive_ack; + +/* Queue up a transfer structure before *pq. This puts it at the head + or the tail of the list headed by *pq. */ + +static void +utqueue (pq, q, fhead) + struct stransfer **pq; + struct stransfer *q; + boolean fhead; +{ + if (*pq == NULL) + { + *pq = q; + q->qprev = q->qnext = q; + } + else + { + q->qnext = *pq; + q->qprev = (*pq)->qprev; + q->qprev->qnext = q; + q->qnext->qprev = q; + if (fhead) + *pq = q; + } + q->pqqueue = pq; +} + +/* Dequeue a transfer structure. */ + +static void +utdequeue (q) + struct stransfer *q; +{ + if (q->pqqueue != NULL) + { + if (*(q->pqqueue) == q) + { + if (q->qnext == q) + *(q->pqqueue) = NULL; + else + *(q->pqqueue) = q->qnext; + } + q->pqqueue = NULL; + } + if (q->qprev != NULL) + q->qprev->qnext = q->qnext; + if (q->qnext != NULL) + q->qnext->qprev = q->qprev; + q->qprev = NULL; + q->qnext = NULL; +} + +/* Queue up a transfer structure requested by the local system. */ + +/*ARGSIGNORED*/ +boolean +fqueue_local (qdaemon, qtrans) + struct sdaemon *qdaemon; + struct stransfer *qtrans; +{ + utdequeue (qtrans); + utqueue (&qTlocal, qtrans, FALSE); + return TRUE; +} + +/* Queue up a transfer structure requested by the remote system. The + stransfer structure should have the iremote field set. We need to + record it, so that any subsequent data associated with this + channel can be routed to the right place. */ + +boolean +fqueue_remote (qdaemon, qtrans) + struct sdaemon *qdaemon; + struct stransfer *qtrans; +{ + DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fqueue_remote: Channel %d", + qtrans->iremote); + if (qtrans->iremote > 0) + aqTremote[qtrans->iremote] = qtrans; + utdequeue (qtrans); + utqueue (&qTremote, qtrans, FALSE); + + /* We just received data for this transfer, so start charging. */ + return ftcharge (qdaemon, qtrans, FALSE, FALSE); +} + +/* Queue up a transfer with something to send. */ + +boolean +fqueue_send (qdaemon, qtrans) + struct sdaemon *qdaemon; + struct stransfer *qtrans; +{ +#if DEBUG > 0 + if (qtrans->psendfn == NULL) + ulog (LOG_FATAL, "fqueue_send: Bad call"); +#endif + utdequeue (qtrans); + utqueue (&qTsend, qtrans, FALSE); + + /* Since we're now going to wait to send data, don't charge this + transfer for receive time. */ + if (qtrans == sTreceive.qtrans) + return ftcharge (qdaemon, (struct stransfer *) NULL, FALSE, FALSE); + return TRUE; +} + +/* Queue up a transfer with something to receive. */ + +boolean +fqueue_receive (qdaemon, qtrans) + struct sdaemon *qdaemon; + struct stransfer *qtrans; +{ +#if DEBUG > 0 + if (qtrans->precfn == NULL) + ulog (LOG_FATAL, "fqueue_receive: Bad call"); +#endif + utdequeue (qtrans); + utqueue (&qTreceive, qtrans, FALSE); + + /* Since we are now going to wait to receive data, don't charge this + transfer for send time. */ + if (qtrans == sTsend.qtrans) + return ftcharge (qdaemon, (struct stransfer *) NULL, TRUE, FALSE); + return TRUE; +} + +/* Get a new local channel number. */ + +static void +utchanalc (qdaemon, qtrans) + struct sdaemon *qdaemon; + struct stransfer *qtrans; +{ + do + { + ++iTchan; + if (iTchan > qdaemon->qproto->cchans) + iTchan = 1; + } + while (aqTchan[iTchan] != NULL); + + qtrans->ilocal = iTchan; + aqTchan[iTchan] = qtrans; + ++cTchans; +} + +/* Return the transfer for a channel number. */ + +__inline__ +static struct stransfer * +qtchan (ic) + int ic; +{ + return aqTchan[ic]; +} + +/* Clear the channel number for a transfer. */ + +__inline__ +static void +utchanfree (qt) + struct stransfer *qt; +{ + if (qt->ilocal != 0) + { + aqTchan[qt->ilocal] = NULL; + qt->ilocal = 0; + --cTchans; + } +} + +/* Allocate a new transfer structure. */ + +struct stransfer * +qtransalc (qcmd) + struct scmd *qcmd; +{ + register struct stransfer *q; + + q = qTavail; + if (q != NULL) + utdequeue (q); + else + q = (struct stransfer *) xmalloc (sizeof (struct stransfer)); + q->qnext = NULL; + q->qprev = NULL; + q->pqqueue = NULL; + q->psendfn = NULL; + q->precfn = NULL; + q->pinfo = NULL; + q->fsendfile = FALSE; + q->frecfile = FALSE; + q->e = EFILECLOSED; + q->ipos = 0; + q->fcmd = FALSE; + q->zcmd = NULL; + q->ccmd = 0; + q->ilocal = 0; + q->iremote = 0; + if (qcmd != NULL) + { + q->s = *qcmd; + q->s.zfrom = zbufcpy (qcmd->zfrom); + q->s.zto = zbufcpy (qcmd->zto); + q->s.zuser = zbufcpy (qcmd->zuser); + q->s.zoptions = zbufcpy (qcmd->zoptions); + q->s.ztemp = zbufcpy (qcmd->ztemp); + q->s.znotify = zbufcpy (qcmd->znotify); + q->s.zcmd = zbufcpy (qcmd->zcmd); + } + else + { + q->s.zfrom = NULL; + q->s.zto = NULL; + q->s.zuser = NULL; + q->s.zoptions = NULL; + q->s.ztemp = NULL; + q->s.znotify = NULL; + q->s.zcmd = NULL; + } + q->isecs = 0; + q->imicros = 0; + q->cbytes = 0; + + return q; +} + +/* Free a transfer structure. This does not free any pinfo + information that may have been allocated. */ + +void +utransfree (q) + struct stransfer *q; +{ + ubuffree (q->zcmd); + ubuffree ((char *) q->s.zfrom); + ubuffree ((char *) q->s.zto); + ubuffree ((char *) q->s.zuser); + ubuffree ((char *) q->s.zoptions); + ubuffree ((char *) q->s.ztemp); + ubuffree ((char *) q->s.znotify); + ubuffree ((char *) q->s.zcmd); + + utchanfree (q); + if (q->iremote > 0) + { + aqTremote[q->iremote] = NULL; + q->iremote = 0; + } + +#if DEBUG > 0 + q->zcmd = NULL; + q->s.zfrom = NULL; + q->s.zto = NULL; + q->s.zuser = NULL; + q->s.zoptions = NULL; + q->s.ztemp = NULL; + q->s.znotify = NULL; + q->s.zcmd = NULL; + q->psendfn = NULL; + q->precfn = NULL; +#endif + + /* Don't try to charge time to this structure any longer. */ + if (sTsend.qtrans == q) + sTsend.qtrans = NULL; + if (sTreceive.qtrans == q) + sTreceive.qtrans = NULL; + + utdequeue (q); + utqueue (&qTavail, q, FALSE); +} + +/* Handle timing of file tranfers. This is called when processing + starts for a transfer structure. All time up to the next call to + this function is charged to that transfer structure. Sending time + and receiving time are charged separately. Normally if we are + about to start charging the same structure we are already charging, + we do nothing; but if the fforce argument is TRUE, we charge the + time anyhow. */ + +static boolean +ftcharge (qdaemon, qtrans, fsend, fforce) + struct sdaemon *qdaemon; + struct stransfer *qtrans; + boolean fsend; + boolean fforce; +{ + struct scharge *qcharge, *qother; + long inextsecs, inextmicros; + + if (fsend) + { + qcharge = &sTsend; + qother = &sTreceive; + } + else + { + qcharge = &sTreceive; + qother = &sTsend; + } + + if (! fforce && qtrans == qcharge->qtrans) + return TRUE; + + inextsecs = ixsysdep_process_time (&inextmicros); + if (qcharge->qtrans != NULL) + { + qcharge->qtrans->isecs += inextsecs - qcharge->isecs; + qcharge->qtrans->imicros += inextmicros - qcharge->imicros; + + /* If we are charging the same structure for both send and + receive, update the time we are not currently charging so + that we don't charge twice for the same time. */ + if (qcharge->qtrans == qother->qtrans) + { + qother->isecs = inextsecs; + qother->imicros = inextmicros; + } + } + + qcharge->qtrans = qtrans; + qcharge->isecs = inextsecs; + qcharge->imicros = inextmicros; + + /* If enough time has elapsed since the last time we checked the + queue, check it again. We do this here because we have already + gone to the trouble of getting the time. */ + if (inextsecs - iTchecktime >= CCHECKWAIT) + { + if (! fcheck_queue (qdaemon)) + return FALSE; + } + + return TRUE; +} + + +/* Gather local commands and queue them up for later processing. Also + recompute time based control values. */ + +boolean +fqueue (qdaemon, pfany) + struct sdaemon *qdaemon; + boolean *pfany; +{ + const struct uuconf_system *qsys; + int bgrade; + struct uuconf_timespan *qlocal_size, *qremote_size; + + if (pfany != NULL) + *pfany = FALSE; + + qsys = qdaemon->qsys; + + /* If we are not the caller, the grade will be set during the + initial handshake. */ + if (! qdaemon->fcaller) + bgrade = qdaemon->bgrade; + else + { + long ival; + + if (! ftimespan_match (qsys->uuconf_qtimegrade, &ival, + (int *) NULL)) + bgrade = '\0'; + else + bgrade = (char) ival; + } + + /* Determine the maximum sizes we can send and receive. */ + if (qdaemon->fcaller) + { + qlocal_size = qsys->uuconf_qcall_local_size; + qremote_size = qsys->uuconf_qcall_remote_size; + } + else + { + qlocal_size = qsys->uuconf_qcalled_local_size; + qremote_size = qsys->uuconf_qcalled_remote_size; + } + + if (! ftimespan_match (qlocal_size, &qdaemon->clocal_size, (int *) NULL)) + qdaemon->clocal_size = (long) -1; + if (! ftimespan_match (qremote_size, &qdaemon->cremote_size, (int *) NULL)) + qdaemon->cremote_size = (long) -1; + + if (bgrade == '\0') + return TRUE; + + if (! fsysdep_get_work_init (qsys, bgrade)) + return FALSE; + + while (TRUE) + { + struct scmd s; + + if (! fsysdep_get_work (qsys, bgrade, &s)) + return FALSE; + + if (s.bcmd == 'H') + { + ulog_user ((const char *) NULL); + break; + } + + if (s.bcmd == 'P') + { + struct stransfer *qtrans; + + /* A poll file. */ + ulog_user ((const char *) NULL); + qtrans = qtransalc (&s); + qtrans->psendfn = flocal_poll_file; + if (! fqueue_local (qdaemon, qtrans)) + return FALSE; + continue; + } + + ulog_user (s.zuser); + + switch (s.bcmd) + { + case 'S': + case 'E': + if (! flocal_send_file_init (qdaemon, &s)) + return FALSE; + break; + case 'R': + if (! flocal_rec_file_init (qdaemon, &s)) + return FALSE; + break; + case 'X': + if (! flocal_xcmd_init (qdaemon, &s)) + return FALSE; + break; +#if DEBUG > 0 + default: + ulog (LOG_FATAL, "fqueue: Can't happen"); + break; +#endif + } + } + + if (pfany != NULL) + *pfany = qTlocal != NULL; + + iTchecktime = ixsysdep_process_time ((long *) NULL); + + return TRUE; +} + +/* Clear everything off the work queue. This is used when the call is + complete, or if the call is never made. */ + +void +uclear_queue (qdaemon) + struct sdaemon *qdaemon; +{ + int i; + + usysdep_get_work_free (qdaemon->qsys); + + qTlocal = NULL; + qTremote = NULL; + qTsend = NULL; + qTreceive = NULL; + cTchans = 0; + iTchan = 0; + sTsend.qtrans = NULL; + sTreceive.qtrans = NULL; + cTcmdlen = 0; + qTreceive_ack = NULL; + for (i = 0; i < IMAX_CHAN + 1; i++) + { + aqTchan[i] = NULL; + aqTremote[i] = NULL; + } +} + +/* Recheck the work queue during a conversation. This is only called + if it's been more than CCHECKWAIT seconds since the last time the + queue was checked. */ + +static boolean +fcheck_queue (qdaemon) + struct sdaemon *qdaemon; +{ + int cchans; + + /* Only check if we are the master, or if there are multiple + channels, or if we aren't already trying to get the other side to + hang up. Otherwise, there's nothing we can do with any new jobs + we might find. */ + if ((qdaemon->ireliable & UUCONF_RELIABLE_FULLDUPLEX) == 0) + cchans = 1; + else + cchans = qdaemon->qproto->cchans; + if (qdaemon->fmaster + || cchans > 1 + || ! qdaemon->frequest_hangup) + { + boolean fany; + + DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, + "fcheck_queue: Rechecking work queue"); + if (! fqueue (qdaemon, &fany)) + return FALSE; + + /* If we found something to do, and we're not the master, and we + don't have multiple channels to send new jobs over, try to + get the other side to hang up. */ + if (fany && ! qdaemon->fmaster && cchans <= 1) + qdaemon->frequest_hangup = TRUE; + } + + return TRUE; +} + +/* The main transfer loop. The uucico daemon spends essentially all + its time in this function. */ + +boolean +floop (qdaemon) + struct sdaemon *qdaemon; +{ + int cchans; + boolean fret; + + /* If we are using a half-duplex line, act as though we have only a + single channel; otherwise we might start a send and a receive at + the same time. */ + if ((qdaemon->ireliable & UUCONF_RELIABLE_FULLDUPLEX) == 0) + cchans = 1; + else + cchans = qdaemon->qproto->cchans; + + fret = TRUE; + + while (! qdaemon->fhangup) + { + register struct stransfer *q; + +#if DEBUG > 1 + /* If we're doing any debugging, close the log and debugging + files regularly. This will let people copy them off and + remove them while the conversation is in progresss. */ + if (iDebug != 0) + { + ulog_close (); + ustats_close (); + } +#endif + + if (qdaemon->fmaster) + { + boolean fhangup; + + /* We've managed to become the master, so we no longer want + to request a hangup. */ + qdaemon->frequest_hangup = FALSE; + + fhangup = FALSE; + + if (qdaemon->fhangup_requested + && qTsend == NULL) + { + /* The remote system has requested that we transfer + control by sending CYM after receiving a file. */ + DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, + "floop: Transferring control at remote request"); + fhangup = TRUE; + } + else if (qTremote == NULL + && qTlocal == NULL + && qTsend == NULL + && qTreceive == NULL) + { + /* We don't have anything to do. Try to find some new + jobs. If we can't, transfer control. */ + if (! fqueue (qdaemon, (boolean *) NULL)) + { + fret = FALSE; + break; + } + if (qTlocal == NULL) + { + DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, + "floop: No work for master"); + fhangup = TRUE; + } + } + + if (fhangup) + { + if (! (*qdaemon->qproto->pfsendcmd) (qdaemon, "H", 0, 0)) + { + fret = FALSE; + break; + } + qdaemon->fmaster = FALSE; + } + } + + /* If we are no long the master, clear any requested hangup. We + may have already hung up before checking this variable in the + block above. */ + if (! qdaemon->fmaster) + qdaemon->fhangup_requested = FALSE; + + /* Immediately queue up any remote jobs. We don't need local + channel numbers for them, since we can disambiguate based on + the remote channel number. */ + while (qTremote != NULL) + { + q = qTremote; + utdequeue (q); + utqueue (&qTsend, q, TRUE); + } + + /* If we are the master, or if we have multiple channels, try to + queue up additional local jobs. */ + if (qdaemon->fmaster || cchans > 1) + { + while (qTlocal != NULL && cTchans < cchans) + { + /* We have room for an additional channel. */ + q = qTlocal; + if (! fqueue_send (qdaemon, q)) + { + fret = FALSE; + break; + } + utchanalc (qdaemon, q); + } + if (! fret) + break; + } + + q = qTsend; + + if (q == NULL) + { + ulog_user ((const char *) NULL); + DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "floop: Waiting for data"); + if (! (*qdaemon->qproto->pfwait) (qdaemon)) + { + fret = FALSE; + break; + } + } + else + { + ulog_user (q->s.zuser); + + if (! q->fsendfile) + { + if (! ftcharge (qdaemon, q, TRUE, TRUE)) + { + fret = FALSE; + break; + } + + if (! (*q->psendfn) (q, qdaemon)) + { + fret = FALSE; + break; + } + } + else + { + if (! ftcharge (qdaemon, q, TRUE, FALSE)) + { + fret = FALSE; + break; + } + + if (q->zlog != NULL) + { + ulog (LOG_NORMAL, "%s", q->zlog); + ubuffree (q->zlog); + q->zlog = NULL; + } + + /* We can read the file in a tight loop until qTremote + changes or until we have transferred the entire file. + We can disregard any changes to qTlocal since we + already have something to send anyhow. */ + while (qTremote == NULL) + { + char *zdata; + size_t cdata; + long ipos; + + zdata = (*qdaemon->qproto->pzgetspace) (qdaemon, &cdata); + if (zdata == NULL) + { + fret = FALSE; + break; + } + + if (ffileeof (q->e)) + cdata = 0; + else + { + cdata = cfileread (q->e, zdata, cdata); + if (ffilereaderror (q->e, cdata)) + { + /* There is no way to report a file reading + error, so we just drop the connection. */ + ulog (LOG_ERROR, "read: %s", strerror (errno)); + fret = FALSE; + break; + } + } + + ipos = q->ipos; + q->ipos += cdata; + q->cbytes += cdata; + + if (! (*qdaemon->qproto->pfsenddata) (qdaemon, zdata, + cdata, q->ilocal, + q->iremote, ipos)) + { + fret = FALSE; + break; + } + + /* It is possible that this transfer has just been + cancelled. */ + if (q != qTsend || ! q->fsendfile) + break; + + if (cdata == 0) + { + /* We must update the time now, because this + call may make an entry in the statistics + file. */ + if (! ftcharge (qdaemon, q, TRUE, TRUE)) + fret = FALSE; + q->fsendfile = FALSE; + if (! (*q->psendfn) (q, qdaemon)) + fret = FALSE; + break; + } + } + + if (! fret) + break; + } + } + } + + ulog_user ((const char *) NULL); + + (void) (*qdaemon->qproto->pfshutdown) (qdaemon); + + if (fret) + uwindow_acked (qdaemon, TRUE); + else + ufailed (qdaemon); + + return fret; +} + +/* This is called by the protocol routines when they have received + some data. If pfexit is not NULL, *pfexit should be set to TRUE if + the protocol receive loop should exit back to the main floop + routine, above. It is only important to set *pfexit to TRUE if the + main loop called the pfwait entry point, so we need never set it to + TRUE if we just receive data for a file. This routine never sets + *pfexit to FALSE. */ + +boolean +fgot_data (qdaemon, zfirst, cfirst, zsecond, csecond, ilocal, iremote, ipos, + fallacked, pfexit) + struct sdaemon *qdaemon; + const char *zfirst; + size_t cfirst; + const char *zsecond; + size_t csecond; + int ilocal; + int iremote; + long ipos; + boolean fallacked; + boolean *pfexit; +{ + struct stransfer *q; + int cwrote; + boolean fret; + + if (fallacked && qTreceive_ack != NULL) + uwindow_acked (qdaemon, TRUE); + + /* Now we have to decide which transfer structure gets the data. If + ilocal is -1, it means that the protocol does not know where to + route the data. In that case we route it to the first transfer + that is waiting for data, or, if none, as a new command. If + ilocal is 0, we either select based on the remote channel number + or we have a new command. */ + if (ilocal == -1 && qTreceive != NULL) + q = qTreceive; + else if (ilocal == 0 && iremote > 0 && aqTremote[iremote] != NULL) + q = aqTremote[iremote]; + else if (ilocal <= 0) + { + const char *znull; + + ulog_user ((const char *) NULL); + + /* This data is part of a command. If there is no null + character in the data, this string will be continued by the + next packet. Otherwise this must be the last string in the + command, and we don't care about what comes after the null + byte. */ + znull = (const char *) memchr (zfirst, '\0', cfirst); + if (znull != NULL) + fret = ftadd_cmd (qdaemon, zfirst, (size_t) (znull - zfirst), + iremote, TRUE); + else + { + fret = ftadd_cmd (qdaemon, zfirst, cfirst, iremote, FALSE); + if (fret && csecond > 0) + { + znull = (const char *) memchr (zsecond, '\0', csecond); + if (znull != NULL) + fret = ftadd_cmd (qdaemon, zsecond, + (size_t) (znull - zsecond), iremote, TRUE); + else + fret = ftadd_cmd (qdaemon, zsecond, csecond, iremote, FALSE); + } + } + + if (pfexit != NULL && (qdaemon->fhangup || qTremote != NULL)) + *pfexit = TRUE; + + return fret; + } + else + { + /* Get the transfer structure this data is intended for. */ + + q = qtchan (ilocal); + } + +#if DEBUG > 0 + if (q == NULL || q->precfn == NULL) + { + ulog (LOG_ERROR, "Protocol error: %lu bytes remote %d local %d", + (unsigned long) (cfirst + csecond), + iremote, ilocal); + return FALSE; + } +#endif + + ulog_user (q->s.zuser); + + fret = TRUE; + + /* If we're receiving a command, then accumulate it up to the null + byte. */ + if (q->fcmd) + { + const char *znull; + + znull = NULL; + while (cfirst > 0) + { + size_t cnew; + char *znew; + + znull = (const char *) memchr (zfirst, '\0', cfirst); + if (znull != NULL) + cnew = znull - zfirst; + else + cnew = cfirst; + znew = zbufalc (q->ccmd + cnew + 1); + memcpy (znew, q->zcmd, q->ccmd); + memcpy (znew + q->ccmd, zfirst, cnew); + znew[q->ccmd + cnew] = '\0'; + ubuffree (q->zcmd); + q->zcmd = znew; + q->ccmd += cnew; + + if (znull != NULL) + break; + + zfirst = zsecond; + cfirst = csecond; + csecond = 0; + } + + if (znull != NULL) + { + char *zcmd; + size_t ccmd; + + if (! ftcharge (qdaemon, q, FALSE, TRUE)) + fret = FALSE; + zcmd = q->zcmd; + ccmd = q->ccmd; + q->fcmd = FALSE; + q->zcmd = NULL; + q->ccmd = 0; + if (! (*q->precfn) (q, qdaemon, zcmd, ccmd + 1)) + fret = FALSE; + ubuffree (zcmd); + } + else + { + if (! ftcharge (qdaemon, q, FALSE, FALSE)) + fret = FALSE; + } + + if (pfexit != NULL + && (qdaemon->fhangup + || qdaemon->fmaster + || qTsend != NULL)) + *pfexit = TRUE; + } + else if (! q->frecfile || cfirst == 0) + { + /* We're either not receiving a file or the file transfer is + complete. */ + if (! ftcharge (qdaemon, q, FALSE, TRUE)) + fret = FALSE; + q->frecfile = FALSE; + if (! (*q->precfn) (q, qdaemon, zfirst, cfirst)) + fret = FALSE; + if (fret && csecond > 0) + return fgot_data (qdaemon, zsecond, csecond, + (const char *) NULL, (size_t) 0, + ilocal, iremote, ipos + (long) cfirst, + FALSE, pfexit); + if (pfexit != NULL + && (qdaemon->fhangup + || qdaemon->fmaster + || qTsend != NULL)) + *pfexit = TRUE; + } + else + { + if (! ftcharge (qdaemon, q, FALSE, FALSE)) + fret = FALSE; + + if (q->zlog != NULL) + { + ulog (LOG_NORMAL, "%s", q->zlog); + ubuffree (q->zlog); + q->zlog = NULL; + } + + if (ipos != -1 && ipos != q->ipos) + { + DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, + "fgot_data: Seeking to %ld", ipos); + if (! ffileseek (q->e, ipos)) + { + ulog (LOG_ERROR, "seek: %s", strerror (errno)); + fret = FALSE; + } + q->ipos = ipos; + } + + if (fret) + { + while (cfirst > 0) + { + cwrote = cfilewrite (q->e, (char *) zfirst, cfirst); + if (cwrote == cfirst) + { +#if FREE_SPACE_DELTA > 0 + long cfree_space; + + /* Check that there is still enough space on the + disk. If there isn't, we drop the connection, + because we have no way to abort a file transfer + in progress. */ + cfree_space = qdaemon->qsys->uuconf_cfree_space; + if (cfree_space > 0 + && ((q->cbytes / FREE_SPACE_DELTA) + != (q->cbytes + cfirst) / FREE_SPACE_DELTA) + && ! frec_check_free (q, cfree_space)) + { + fret = FALSE; + break; + } +#endif + q->cbytes += cfirst; + q->ipos += cfirst; + } + else + { + if (cwrote < 0) + ulog (LOG_ERROR, "write: %s", strerror (errno)); + else + ulog (LOG_ERROR, + "Wrote %d to file when trying to write %lu", + cwrote, (unsigned long) cfirst); + + /* Any write error is almost certainly a temporary + condition, or else UUCP would not be functioning + at all. If we continue to accept the file, we + will wind up rejecting it at the end (what else + could we do?) and the remote system will throw + away the request. We're better off just dropping + the connection, which is what happens when we + return FALSE, and trying again later. */ + fret = FALSE; + break; + } + + zfirst = zsecond; + cfirst = csecond; + csecond = 0; + } + } + + if (pfexit != NULL && qdaemon->fhangup) + *pfexit = TRUE; + } + + return fret; +} + +/* Accumulate a string into a command. If the command is complete, + start up a new transfer. */ + +static boolean +ftadd_cmd (qdaemon, z, clen, iremote, flast) + struct sdaemon *qdaemon; + const char *z; + size_t clen; + int iremote; + boolean flast; +{ + static char *zbuf; + static size_t cbuf; + size_t cneed; + struct scmd s; + + cneed = cTcmdlen + clen + 1; + if (cneed > cbuf) + { + zbuf = (char *) xrealloc ((pointer) zbuf, cneed); + cbuf = cneed; + } + + memcpy (zbuf + cTcmdlen, z, clen); + zbuf[cTcmdlen + clen] = '\0'; + + if (! flast) + { + cTcmdlen += clen; + return TRUE; + } + + /* Don't save this string for next time. */ + cTcmdlen = 0; + + DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, + "ftadd_cmd: Got command \"%s\"", zbuf); + + if (! fparse_cmd (zbuf, &s)) + { + ulog (LOG_ERROR, "Received garbled command \"%s\"", zbuf); + return TRUE; + } + + if (s.bcmd != 'H' && s.bcmd != 'Y' && s.bcmd != 'N') + ulog_user (s.zuser); + else + ulog_user ((const char *) NULL); + + switch (s.bcmd) + { + case 'S': + case 'E': + return fremote_send_file_init (qdaemon, &s, iremote); + case 'R': + return fremote_rec_file_init (qdaemon, &s, iremote); + case 'X': + return fremote_xcmd_init (qdaemon, &s, iremote); + case 'H': + /* This is a remote request for a hangup. We close the log + files so that they may be moved at this point. */ + ulog_close (); + ustats_close (); + { + struct stransfer *q; + + q = qtransalc ((struct scmd *) NULL); + q->psendfn = fremote_hangup_reply; + q->iremote = iremote; + return fqueue_remote (qdaemon, q); + } + case 'N': + /* This means a hangup request is being denied; we just ignore + this and wait for further commands. */ + return TRUE; + case 'Y': + /* This is a remote confirmation of a hangup. We reconfirm. */ + if (qdaemon->fhangup) + return TRUE; +#if DEBUG > 0 + if (qdaemon->fmaster) + ulog (LOG_ERROR, "Got hangup reply as master"); +#endif + /* Don't check errors rigorously here, since the other side + might jump the gun and hang up. The fLog_sighup variable + will get set TRUE again when the port is closed. */ + fLog_sighup = FALSE; + (void) (*qdaemon->qproto->pfsendcmd) (qdaemon, "HY", 0, iremote); + qdaemon->fhangup = TRUE; + return TRUE; +#if DEBUG > 0 + default: + ulog (LOG_FATAL, "ftadd_cmd: Can't happen"); + return FALSE; +#endif + } +} + +/* The remote system is requesting a hang up. If we have something to + do, send an HN. Otherwise send two HY commands (the other side is + presumed to send an HY command between the first and second, but we + don't bother to wait for it) and hang up. */ + +static boolean +fremote_hangup_reply (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + boolean fret; + + utransfree (qtrans); + + if (qTremote == NULL + && qTlocal == NULL + && qTsend == NULL + && qTreceive == NULL) + { + if (! fqueue (qdaemon, (boolean *) NULL)) + return FALSE; + + if (qTlocal == NULL) + { + DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "fremote_hangup_reply: No work"); + fret = ((*qdaemon->qproto->pfsendcmd) (qdaemon, "HY", 0, 0) + && (*qdaemon->qproto->pfsendcmd) (qdaemon, "HY", 0, 0)); + qdaemon->fhangup = TRUE; + return fret; + } + } + + DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "fremote_hangup_reply: Found work"); + fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, "HN", 0, 0); + qdaemon->fmaster = TRUE; + return fret; +} + +/* As described in system.h, we need to keep track of which files have + been successfully received for which we do not know that the other + system has received our acknowledgement. This routine is called to + keep a list of such files. */ + +static struct sreceive_ack *qTfree_receive_ack; + +void +usent_receive_ack (qdaemon, qtrans) + struct sdaemon *qdaemon; + struct stransfer *qtrans; +{ + struct sreceive_ack *q; + + if (qTfree_receive_ack == NULL) + q = (struct sreceive_ack *) xmalloc (sizeof (struct sreceive_ack)); + else + { + q = qTfree_receive_ack; + qTfree_receive_ack = q->qnext; + } + + q->qnext = qTreceive_ack; + q->zto = zbufcpy (qtrans->s.zto); + q->ztemp = zbufcpy (qtrans->s.ztemp); + q->fmarked = FALSE; + + qTreceive_ack = q; +} + +/* This routine is called by the protocol code when either all + outstanding data has been acknowledged or one complete window has + passed. It may be called directly by the protocol, or it may be + called via fgot_data. If one complete window has passed, then all + unmarked receives are marked, and we know that all marked ones have + been acked. */ + +void +uwindow_acked (qdaemon, fallacked) + struct sdaemon *qdaemon; + boolean fallacked; +{ + register struct sreceive_ack **pq; + + pq = &qTreceive_ack; + while (*pq != NULL) + { + if (fallacked || (*pq)->fmarked) + { + struct sreceive_ack *q; + + q = *pq; + (void) fsysdep_forget_reception (qdaemon->qsys, q->zto, + q->ztemp); + ubuffree (q->zto); + ubuffree (q->ztemp); + *pq = q->qnext; + q->qnext = qTfree_receive_ack; + qTfree_receive_ack = q; + } + else + { + (*pq)->fmarked = TRUE; + pq = &(*pq)->qnext; + } + } +} + +/* This routine is called when an error occurred and we are crashing + out of the connection. It is used to report statistics on failed + transfers to the statistics file, and it also discards useless + temporary files for file receptions. Note that the number of bytes + we report as having been sent has little or nothing to do with the + number of bytes the remote site actually received. */ + +void +ufailed (qdaemon) + struct sdaemon *qdaemon; +{ + register struct stransfer *q; + + /* Update the transfer times, but avoid looking in the queue. */ + iTchecktime = ixsysdep_process_time ((long *) NULL); + (void) ftcharge (qdaemon, (struct stransfer *) NULL, TRUE, TRUE); + (void) ftcharge (qdaemon, (struct stransfer *) NULL, FALSE, TRUE); + + if (qTsend != NULL) + { + q = qTsend; + do + { + if ((q->fsendfile || q->frecfile) + && q->cbytes > 0) + ustats (FALSE, q->s.zuser, qdaemon->qsys->uuconf_zname, + q->fsendfile, q->cbytes, q->isecs, q->imicros, + FALSE); + if (q->frecfile) + (void) frec_discard_temp (qdaemon, q); + q = q->qnext; + } + while (q != qTsend); + } + + if (qTreceive != NULL) + { + q = qTreceive; + do + { + if ((q->fsendfile || q->frecfile) + && q->cbytes > 0) + ustats (FALSE, q->s.zuser, qdaemon->qsys->uuconf_zname, + q->fsendfile, q->cbytes, q->isecs, q->imicros, + FALSE); + if (q->frecfile) + (void) frec_discard_temp (qdaemon, q); + q = q->qnext; + } + while (q != qTreceive); + } +} + +/* When a local poll file is found, it is entered on the queue like + any other job. When it is pulled off the queue, this function is + called. It just calls fsysdep_did_work, which will remove the poll + file. This ensures that poll files are only removed if the system + is actually called. */ + +/*ARGSUSED*/ +static boolean +flocal_poll_file (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + boolean fret; + + fret = fsysdep_did_work (qtrans->s.pseq); + utransfree (qtrans); + return fret; +} diff --git a/gnu/libexec/uucp/uucico/uucico.8 b/gnu/libexec/uucp/uucico/uucico.8 new file mode 100644 index 0000000000..fba8bb588f --- /dev/null +++ b/gnu/libexec/uucp/uucico/uucico.8 @@ -0,0 +1,225 @@ +''' $Id: uucico.8,v 1.1 1993/08/04 19:36:33 jtc Exp $ +.TH uucico 8 "Taylor UUCP 1.04" +.SH NAME +uucico \- UUCP file transfer daemon +.SH SYNOPSIS +.B uucico +[ options ] +.SH DESCRIPTION +The +.I uucico +daemon processes file transfer requests queued by +.I uucp +(1) and +.I uux +(1). It is started when +.I uucp +or +.I uux +is run (unless they are given the +.B \-r +option). It is also typically started periodically using +entries in the +.I crontab +table(s). + +When invoked with the +.B \-r1 +option or the +.B \-s +or +.B \-S +option, the daemon will place a call to a remote system, running in +master mode. +Otherwise the daemon will start in slave mode, accepting a +call from a remote system. Typically a special login name will be set +up for UUCP which automatically invokes +.I uucico +when a call is made. + +When +.I uucico +terminates, it invokes the +.I uuxqt +(8) daemon, unless the +.B \-q +option is given; +.I uuxqt +(8) executes any work orders created by +.I uux +(1) on a remote system, and any work orders created locally which have +received remote files for which they were waiting. + +If a call fails, +.I uucico +will normally refuse to retry the +call until a certain (configurable) amount of time +has passed. This may be overriden by the +.B -f +or the +.B -S +option. + +The +.B \-l +or +.B \-e +option may be used to force +.I uucico +to produce its own prompts of "login: " and "Password:". When another +daemon calls in, it will see these prompts and log in as usual; the +login name and password will be checked against a separate list kept +specially for +.I uucico +rather than the +.I /etc/passwd +file. The +.B \-l +option will prompt once and then exit. The +.B \-e +option will prompt again after the first session is over; in this mode +.I uucico +will permanently control a port. + +If +.I uucico +receives a SIGQUIT, SIGTERM or SIGPIPE signal, it will cleanly abort +any current conversation with a remote system and exit. If it +receives a SIGHUP signal it will abort any current conversation, but +will continue to place calls to (if invoked with +.B \-r1) +and accept calls from (if invoked with +.B \-e) +other systems. If it receives a +SIGINT signal it will finish the current conversation, but will not +place or accept any more calls. +.SH OPTIONS +The following options may be given to +.I uucico. +.TP 5 +.B \-r1 +Start in master mode (call out to a system); implied by +.B \-s +or +.B \-S. +If no system is specified, call any system for which work is waiting +to be done. +.TP 5 +.B \-r0 +Start in slave mode. This is the default. +.TP 5 +.B \-s system +Call the named system. +.TP 5 +.B \-S system +Call the named system, ignoring any required wait. +.TP 5 +.B \-f +Ignore any required wait for any systems to be called. +.TP 5 +.B \-l +Prompt for login name and password using "login: " and "Password:". +This allows +.I uucico +to be easily run from +.I inetd +(8). The login name and password are checked against the UUCP +password file, which has no connection to the file +.I /etc/passwd. +.TP 5 +.B \-p port +Specify a port to call out on or to listen to. In slave mode, this +implies +.B \-e. +.TP 5 +.B \-e +Enter endless loop of login/password prompts and slave mode daemon +execution. The program will not stop by itself; you must use +.I kill +(1) to shut it down. +.TP 5 +.B \-w +After calling out (to a particular system when +.B \-s +or +.B \-S +is specifed, or to all systems which have work when +.B \-r1 +is specifed), begin an endless loop as with +.B \-e. +.TP 5 +.B \-q +Do not start the +.I uuxqt +(8) daemon when finished. +.TP 5 +.B \-c +If no calls are permitted at this time, then don't make the call, but +also do not put an error message in the log file and do not update the +system status (as reported by +.I uustat +(1)). This can be convenient for automated polling scripts, which may +want to simply attempt to call every system rather than worry about +which particular systems may be called at the moment. +.TP 5 +.B \-D +Do not detach from the controlling terminal. Normally +.I uucico +detaches from the terminal before each call out to another system and +before invoking +.I uuxqt. +This option prevents this. +.TP 5 +.B \-x type, \-X type +Turn on particular debugging types. The following types are +recognized: abnormal, chat, handshake, uucp-proto, proto, port, +config, spooldir, execute, incoming, outgoing. + +Multiple types may be given, separated by commas, and the +.B \-x +option may appear multiple times. A number may also be given, which +will turn on that many types from the foregoing list; for example, +.B \-x 2 +is equivalent to +.B \-x abnormal,chat. + +The debugging output is sent to the debugging file, usually one of +/usr/spool/uucp/Debug, /usr/spool/uucp/DEBUG, or +/usr/spool/uucp/.Admin/audit.local. +.TP 5 +.B \-I file +Set configuration file to use. This option may not be available, +depending upon how +.I uucico +was compiled. +.TP 5 +.B \-u login +This option is ignored. It is only included because some versions of +uucpd invoke +.I uucico +with it. +.SH FILES +The file names may be changed at compilation time or by the +configuration file, so these are only approximations. + +.br +/usr/lib/uucp/config - Configuration file. +.br +/usr/lib/uucp/passwd - Default UUCP password file. +.br +/usr/spool/uucp - +UUCP spool directory. +.br +/usr/spool/uucp/Log - +UUCP log file. +.br +/usr/spool/uucppublic - +Default UUCP public directory. +.br +/usr/spool/uucp/Debug - +Debugging file. +.SH SEE ALSO +kill(1), uucp(1), uux(1), uustat(1), uuxqt(8) +.SH AUTHOR +Ian Lance Taylor +(ian@airs.com or uunet!airs!ian) diff --git a/gnu/libexec/uucp/uucico/uucico.c b/gnu/libexec/uucp/uucico/uucico.c new file mode 100644 index 0000000000..ecd6c235c8 --- /dev/null +++ b/gnu/libexec/uucp/uucico/uucico.c @@ -0,0 +1,2618 @@ +/* uucico.c + This is the main UUCP communication program. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char uucico_rcsid[] = "$Id: uucico.c,v 1.1 1993/08/04 19:36:34 jtc Exp $"; +#endif + +#include + +#if HAVE_LIMITS_H +#include +#else +#define LONG_MAX 2147483647L +#endif + +#include "getopt.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "conn.h" +#include "prot.h" +#include "trans.h" +#include "system.h" + +/* The program name. */ +char abProgram[] = "uucico"; + +/* Define the known protocols. */ + +#define TCP_PROTO \ + (UUCONF_RELIABLE_ENDTOEND \ + | UUCONF_RELIABLE_RELIABLE \ + | UUCONF_RELIABLE_EIGHT) + +static const struct sprotocol asProtocols[] = +{ + { 't', TCP_PROTO, 1, + asTproto_params, ftstart, ftshutdown, ftsendcmd, ztgetspace, + ftsenddata, ftwait, ftfile }, + { 'e', TCP_PROTO, 1, + asEproto_params, festart, feshutdown, fesendcmd, zegetspace, + fesenddata, fewait, fefile }, + { 'i', UUCONF_RELIABLE_EIGHT, 7, + asIproto_params, fistart, fishutdown, fisendcmd, zigetspace, + fisenddata, fiwait, NULL }, + { 'a', UUCONF_RELIABLE_EIGHT, 1, + asZproto_params, fzstart, fzshutdown, fzsendcmd, zzgetspace, + fzsenddata, fzwait, fzfile }, + { 'g', UUCONF_RELIABLE_EIGHT, 1, + asGproto_params, fgstart, fgshutdown, fgsendcmd, zggetspace, + fgsenddata, fgwait, NULL }, + { 'G', UUCONF_RELIABLE_EIGHT, 1, + asGproto_params, fbiggstart, fgshutdown, fgsendcmd, zggetspace, + fgsenddata, fgwait, NULL }, + { 'j', UUCONF_RELIABLE_EIGHT, 7, + asIproto_params, fjstart, fjshutdown, fisendcmd, zigetspace, + fisenddata, fiwait, NULL }, + { 'f', UUCONF_RELIABLE_RELIABLE, 1, + asFproto_params, ffstart, ffshutdown, ffsendcmd, zfgetspace, + ffsenddata, ffwait, fffile }, +}; + +#define CPROTOCOLS (sizeof asProtocols / sizeof asProtocols[0]) + +/* Locked system. */ +static boolean fLocked_system; +static struct uuconf_system sLocked_system; + +/* Daemon structure holding information about the remote system (must + be global so the error handler can see it. */ +static struct sdaemon sDaemon; + +/* Open connection. */ +static struct sconnection *qConn; + +/* uuconf global pointer; need to close the connection after a fatal + error. */ +static pointer pUuconf; + +/* This structure is passed to iuport_lock via uuconf_find_port. */ +struct spass +{ + boolean fmatched; + boolean flocked; + struct sconnection *qconn; +}; + +/* Local functions. */ + +static void uusage P((void)); +static void uabort P((void)); +static boolean fcall P((pointer puuconf, + const struct uuconf_system *qsys, + struct uuconf_port *qport, boolean fifwork, + boolean fforce, boolean fdetach, + boolean ftimewarn)); +static boolean fconn_call P((struct sdaemon *qdaemon, + struct uuconf_port *qport, + struct sstatus *qstat, int cretry, + boolean *pfcalled)); +static boolean fdo_call P((struct sdaemon *qdaemon, + struct sstatus *qstat, + const struct uuconf_dialer *qdialer, + boolean *pfcalled, enum tstatus_type *pterr)); +static int iuport_lock P((struct uuconf_port *qport, pointer pinfo)); +static boolean flogin_prompt P((pointer puuconf, + struct sconnection *qconn)); +static boolean faccept_call P((pointer puuconf, const char *zlogin, + struct sconnection *qconn, + const char **pzsystem)); +static void uapply_proto_params P((pointer puuconf, int bproto, + struct uuconf_cmdtab *qcmds, + struct uuconf_proto_param *pas)); +static boolean fsend_uucp_cmd P((struct sconnection *qconn, + const char *z)); +static char *zget_uucp_cmd P((struct sconnection *qconn, + boolean frequired)); +static char *zget_typed_line P((struct sconnection *qconn)); + +/* Long getopt options. */ +static const struct option asLongopts[] = { { NULL, 0, NULL, 0 } }; + +int +main (argc, argv) + int argc; + char **argv; +{ + /* -c: Whether to warn if a call is attempted at a bad time. */ + boolean ftimewarn = TRUE; + /* -D: don't detach from controlling terminal. */ + boolean fdetach = TRUE; + /* -e: Whether to do an endless loop of accepting calls. */ + boolean fendless = FALSE; + /* -f: Whether to force a call despite status of previous call. */ + boolean fforce = FALSE; + /* -I file: configuration file name. */ + const char *zconfig = NULL; + /* -l: Whether to give a single login prompt. */ + boolean flogin = FALSE; + /* -P port: port to use; in master mode, call out on this port. In + slave mode, accept logins on this port. If port not specified, + then in master mode figure it out for each system, and in slave + mode use stdin and stdout. */ + const char *zport = NULL; + /* -q: Whether to start uuxqt when done. */ + boolean fuuxqt = TRUE; + /* -r1: Whether we are the master. */ + boolean fmaster = FALSE; + /* -s,-S system: system to call. */ + const char *zsystem = NULL; + /* -w: Whether to wait for a call after doing one. */ + boolean fwait = FALSE; + int iopt; + struct uuconf_port *qport; + struct uuconf_port sport; + boolean fret = TRUE; + pointer puuconf; + int iuuconf; +#if DEBUG > 1 + int iholddebug; +#endif + + while ((iopt = getopt_long (argc, argv, + "cDefI:lp:qr:s:S:u:x:X:w", + asLongopts, (int *) NULL)) != EOF) + { + switch (iopt) + { + case 'c': + /* Don't warn if a call is attempted at a bad time. */ + ftimewarn = FALSE; + break; + + case 'D': + /* Don't detach from controlling terminal. */ + fdetach = FALSE; + break; + + case 'e': + /* Do an endless loop of accepting calls. */ + fendless = TRUE; + break; + + case 'f': + /* Force a call even if it hasn't been long enough since the last + failed call. */ + fforce = TRUE; + break; + + case 'I': + /* Set configuration file name (default is in sysdep.h). */ + if (fsysdep_other_config (optarg)) + zconfig = optarg; + break; + + case 'l': + /* Prompt for login name and password. */ + flogin = TRUE; + break; + + case 'p': + /* Port to use */ + zport = optarg; + break; + + case 'q': + /* Don't start uuxqt. */ + fuuxqt = FALSE; + break; + + case 'r': + /* Set mode: -r1 for master, -r0 for slave (default) */ + if (strcmp (optarg, "1") == 0) + fmaster = TRUE; + else if (strcmp (optarg, "0") == 0) + fmaster = FALSE; + else + uusage (); + break; + + case 's': + /* Set system name */ + zsystem = optarg; + fmaster = TRUE; + break; + + case 'S': + /* Set system name and force call like -f */ + zsystem = optarg; + fforce = TRUE; + fmaster = TRUE; + break; + + case 'u': + /* Some versions of uucpd invoke uucico with a -u argument + specifying the login name. I'm told it is safe to ignore + this value, although perhaps we should use it rather than + zsysdep_login_name (). */ + break; + + case 'x': + case 'X': +#if DEBUG > 1 + /* Set debugging level */ + iDebug |= idebug_parse (optarg); +#endif + break; + + case 'w': + /* Call out and then wait for a call in */ + fwait = TRUE; + break; + + case 0: + /* Long option found, and flag value set. */ + break; + + default: + uusage (); + break; + } + } + + if (optind != argc) + uusage (); + + if (fwait && zport == NULL) + { + ulog (LOG_ERROR, "-w requires -e"); + uusage (); + } + + iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + pUuconf = puuconf; + +#if DEBUG > 1 + { + const char *zdebug; + + iuuconf = uuconf_debuglevel (puuconf, &zdebug); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + if (zdebug != NULL) + iDebug |= idebug_parse (zdebug); + } +#endif + + /* If a port was named, get its information. */ + if (zport == NULL) + qport = NULL; + else + { + iuuconf = uuconf_find_port (puuconf, zport, (long) 0, (long) 0, + (int (*) P((struct uuconf_port *, + pointer))) NULL, + (pointer) NULL, &sport); + if (iuuconf == UUCONF_NOT_FOUND) + ulog (LOG_FATAL, "%s: Port not found", zport); + else if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + qport = &sport; + } + +#ifdef SIGINT + usysdep_signal (SIGINT); +#endif +#ifdef SIGHUP + usysdep_signal (SIGHUP); +#endif +#ifdef SIGQUIT + usysdep_signal (SIGQUIT); +#endif +#ifdef SIGTERM + usysdep_signal (SIGTERM); +#endif +#ifdef SIGPIPE + usysdep_signal (SIGPIPE); +#endif + + usysdep_initialize (puuconf, INIT_SUID); + + ulog_to_file (puuconf, TRUE); + ulog_fatal_fn (uabort); + + if (fmaster) + { + if (zsystem != NULL) + { + /* A system was named. Call it. */ + iuuconf = uuconf_system_info (puuconf, zsystem, + &sLocked_system); + if (iuuconf == UUCONF_NOT_FOUND) + ulog (LOG_FATAL, "%s: System not found", zsystem); + else if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + /* Detach from the controlling terminal for the call. This + probably makes sense only on Unix. We want the modem + line to become the controlling terminal. */ + if (fdetach && + (qport == NULL + || qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN)) + usysdep_detach (); + + ulog_system (sLocked_system.uuconf_zname); + +#if DEBUG > 1 + iholddebug = iDebug; + if (sLocked_system.uuconf_zdebug != NULL) + iDebug |= idebug_parse (sLocked_system.uuconf_zdebug); +#endif + + if (! fsysdep_lock_system (&sLocked_system)) + { + ulog (LOG_ERROR, "System already locked"); + fret = FALSE; + } + else + { + fLocked_system = TRUE; + fret = fcall (puuconf, &sLocked_system, qport, FALSE, + fforce, fdetach, ftimewarn); + if (fLocked_system) + { + (void) fsysdep_unlock_system (&sLocked_system); + fLocked_system = FALSE; + } + } +#if DEBUG > 1 + iDebug = iholddebug; +#endif + ulog_system ((const char *) NULL); + (void) uuconf_system_free (puuconf, &sLocked_system); + } + else + { + char **pznames, **pz; + int c, i; + boolean fdidone; + + /* Call all systems which have work to do. */ + fret = TRUE; + fdidone = FALSE; + + iuuconf = uuconf_system_names (puuconf, &pznames, 0); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + /* Randomize the order in which we call the systems. */ + c = 0; + for (pz = pznames; *pz != NULL; pz++) + c++; + + srand ((unsigned int) ixsysdep_time ((long *) NULL)); + for (i = c - 1; i > 0; i--) + { + int iuse; + char *zhold; + + iuse = rand () % (i + 1); + zhold = pznames[i]; + pznames[i] = pznames[iuse]; + pznames[iuse] = zhold; + } + + for (pz = pznames; *pz != NULL && ! FGOT_SIGNAL (); pz++) + { + iuuconf = uuconf_system_info (puuconf, *pz, + &sLocked_system); + if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + xfree ((pointer) *pz); + continue; + } + + if (fsysdep_has_work (&sLocked_system)) + { + fdidone = TRUE; + + /* Detach from the controlling terminal. On Unix + this means that we will wind up forking a new + process for each system we call. */ + if (fdetach + && (qport == NULL + || qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN)) + usysdep_detach (); + + ulog_system (sLocked_system.uuconf_zname); + +#if DEBUG > 1 + iholddebug = iDebug; + if (sLocked_system.uuconf_zdebug != NULL) + iDebug |= idebug_parse (sLocked_system.uuconf_zdebug); +#endif + + if (! fsysdep_lock_system (&sLocked_system)) + { + ulog (LOG_ERROR, "System already locked"); + fret = FALSE; + } + else + { + fLocked_system = TRUE; + if (! fcall (puuconf, &sLocked_system, qport, TRUE, + fforce, fdetach, ftimewarn)) + fret = FALSE; + + /* Now ignore any SIGHUP that we got. */ + afSignal[INDEXSIG_SIGHUP] = FALSE; + + if (fLocked_system) + { + (void) fsysdep_unlock_system (&sLocked_system); + fLocked_system = FALSE; + } + } +#if DEBUG > 1 + iDebug = iholddebug; +#endif + ulog_system ((const char *) NULL); + } + + (void) uuconf_system_free (puuconf, &sLocked_system); + xfree ((pointer) *pz); + } + + xfree ((pointer) pznames); + + if (! fdidone) + ulog (LOG_NORMAL, "No work"); + } + + /* If requested, wait for calls after dialing out. */ + if (fwait) + { + fendless = TRUE; + fmaster = FALSE; + } + } + + if (! fmaster) + { + struct sconnection sconn; + boolean flocked; + + /* If a port was specified by name, we go into endless loop + mode. In this mode, we wait for calls and prompt them with + "login:" and "Password:", so that they think we are a regular + UNIX system. If we aren't in endless loop mode, we have been + called by some other system. If flogin is TRUE, we prompt + with "login:" and "Password:" a single time. */ + + fret = TRUE; + zsystem = NULL; + + if (! fconn_init (qport, &sconn)) + fret = FALSE; + + if (qport != NULL) + { + /* We are not using standard input. Detach from the + controlling terminal, so that the port we are about to + use becomes our controlling terminal. */ + if (fdetach + && qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN) + usysdep_detach (); + + /* If a port was given, we loop forever. */ + fendless = TRUE; + } + + if (fconn_lock (&sconn, TRUE)) + flocked = TRUE; + else + { + flocked = FALSE; + ulog (LOG_ERROR, "%s: Port already locked", + qport->uuconf_zname); + fret = FALSE; + } + + if (fret) + { + if (! fconn_open (&sconn, (long) 0, (long) 0, TRUE)) + fret = FALSE; + qConn = &sconn; + } + + if (fret) + { + if (fendless) + { + while (! FGOT_SIGNAL () + && flogin_prompt (puuconf, &sconn)) + { + /* Now ignore any SIGHUP that we got. */ + afSignal[INDEXSIG_SIGHUP] = FALSE; + + if (fLocked_system) + { + (void) fsysdep_unlock_system (&sLocked_system); + fLocked_system = FALSE; + } + if (! fconn_reset (&sconn)) + break; + } + fret = FALSE; + } + else + { + if (flogin) + fret = flogin_prompt (puuconf, &sconn); + else + { +#if DEBUG > 1 + iholddebug = iDebug; +#endif + fret = faccept_call (puuconf, zsysdep_login_name (), + &sconn, &zsystem); +#if DEBUG > 1 + iDebug = iholddebug; +#endif + } + } + } + + if (qConn != NULL) + { + if (! fconn_close (&sconn, puuconf, (struct uuconf_dialer *) NULL, + fret)) + fret = FALSE; + qConn = NULL; + } + + if (flocked) + (void) fconn_unlock (&sconn); + + if (fLocked_system) + { + (void) fsysdep_unlock_system (&sLocked_system); + fLocked_system = FALSE; + } + + uconn_free (&sconn); + } + + ulog_close (); + ustats_close (); + + /* If we got a SIGTERM, perhaps because the system is going down, + don't run uuxqt. We go ahead and run it for any other signal, + since I think they indicate more temporary conditions. */ + if (afSignal[INDEXSIG_SIGTERM]) + fuuxqt = FALSE; + + if (fuuxqt) + { + /* Detach from the controlling terminal before starting up uuxqt, + so that it runs as a true daemon. */ + if (fdetach) + usysdep_detach (); + if (zsystem == NULL) + { + if (! fsysdep_run ("uuxqt", (const char *) NULL, + (const char *) NULL)) + fret = FALSE; + } + else + { + if (! fsysdep_run ("uuxqt", "-s", zsystem)) + fret = FALSE; + } + } + + usysdep_exit (fret); + + /* Avoid complaints about not returning. */ + return 0; +} + +/* Print out a usage message. */ + +static void +uusage () +{ + fprintf (stderr, + "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n", + VERSION); + fprintf (stderr, + "Usage: uucico [options]\n"); + fprintf (stderr, + " -s,-S system: Call system (-S implies -f)\n"); + fprintf (stderr, + " -f: Force call despite system status\n"); + fprintf (stderr, + " -r state: 1 for master, 0 for slave (default)\n"); + fprintf (stderr, + " -p port: Specify port (implies -e)\n"); + fprintf (stderr, + " -l: prompt for login name and password\n"); + fprintf (stderr, + " -e: Endless loop of login prompts and daemon execution\n"); + fprintf (stderr, + " -w: After calling out, wait for incoming calls\n"); + fprintf (stderr, + " -q: Don't start uuxqt when done\n"); + fprintf (stderr, + " -x,-X debug: Set debugging level\n"); +#if HAVE_TAYLOR_CONFIG + fprintf (stderr, + " -I file: Set configuration file to use\n"); +#endif /* HAVE_TAYLOR_CONFIG */ + + exit (EXIT_FAILURE); +} + +/* This function is called when a LOG_FATAL error occurs. */ + +static void +uabort () +{ + if (fLocked_system) + ufailed (&sDaemon); + + ulog_user ((const char *) NULL); + + if (qConn != NULL) + { + (void) fconn_close (qConn, pUuconf, (struct uuconf_dialer *) NULL, + FALSE); + (void) fconn_unlock (qConn); + uconn_free (qConn); + } + + if (fLocked_system) + { + (void) fsysdep_unlock_system (&sLocked_system); + fLocked_system = FALSE; + } + + ulog_system ((const char *) NULL); + + ulog_close (); + ustats_close (); + + usysdep_exit (FALSE); +} + +/* Call another system, trying all the possible sets of calling + instructions. The qsys argument is the system to call. The qport + argument is the port to use, and may be NULL. If the fifwork + argument is TRUE, the call is only placed if there is work to be + done. If the fforce argument is TRUE, a call is forced even if not + enough time has passed since the last failed call. If the + ftimewarn argument is TRUE (the normal case), then a warning is + given if calls are not permitted at this time. */ + +static boolean +fcall (puuconf, qorigsys, qport, fifwork, fforce, fdetach, ftimewarn) + pointer puuconf; + const struct uuconf_system *qorigsys; + struct uuconf_port *qport; + boolean fifwork; + boolean fforce; + boolean fdetach; + boolean ftimewarn; +{ + struct sstatus sstat; + long inow; + boolean fbadtime, fnevertime; + const struct uuconf_system *qsys; + + if (! fsysdep_get_status (qorigsys, &sstat, (boolean *) NULL)) + return FALSE; + + /* Make sure it's been long enough since the last failed call, and + that we haven't exceeded the maximum number of retries. Even if + we are over the limit on retries, we permit a call to be made if + 24 hours have passed. This 24 hour limit is still controlled by + the retry time. */ + inow = ixsysdep_time ((long *) NULL); + if (! fforce) + { + if (qorigsys->uuconf_cmax_retries > 0 + && sstat.cretries >= qorigsys->uuconf_cmax_retries + && sstat.ilast + 24 * 60 * 60 < inow) + { + ulog (LOG_ERROR, "Too many retries"); + return FALSE; + } + + if (sstat.ttype == STATUS_COMPLETE + ? sstat.ilast + qorigsys->uuconf_csuccess_wait > inow + : sstat.ilast + sstat.cwait > inow) + { + ulog (LOG_NORMAL, "Retry time not reached"); + return FALSE; + } + } + + sDaemon.puuconf = puuconf; + sDaemon.qsys = NULL; + sDaemon.zlocalname = NULL; + sDaemon.qconn = NULL; + sDaemon.qproto = NULL; + sDaemon.clocal_size = -1; + sDaemon.cremote_size = -1; + sDaemon.cmax_ever = -2; + sDaemon.cmax_receive = -1; + sDaemon.ifeatures = 0; + sDaemon.frequest_hangup = FALSE; + sDaemon.fhangup_requested = FALSE; + sDaemon.fhangup = FALSE; + sDaemon.fmaster = TRUE; + sDaemon.fcaller = TRUE; + sDaemon.ireliable = 0; + sDaemon.bgrade = '\0'; + + fbadtime = TRUE; + fnevertime = TRUE; + + for (qsys = qorigsys; qsys != NULL; qsys = qsys->uuconf_qalternate) + { + int cretry; + boolean fany, fret, fcalled; + + if (FGOT_SIGNAL ()) + return FALSE; + + if (! qsys->uuconf_fcall || qsys->uuconf_qtimegrade == NULL) + continue; + + fnevertime = FALSE; + + /* Make sure this is a legal time to call. */ + if (! ftimespan_match (qsys->uuconf_qtimegrade, (long *) NULL, + &cretry)) + continue; + + sDaemon.qsys = qsys; + + /* Queue up any work there is to do. */ + if (! fqueue (&sDaemon, &fany)) + return FALSE; + + /* If we are only supposed to call if there is work, and there + isn't any work, check the next alternates. We can't give up + at this point because there might be some other alternates + with fewer restrictions on grade or file transfer size. */ + if (fifwork && ! fany) + { + uclear_queue (&sDaemon); + continue; + } + + fbadtime = FALSE; + + fret = fconn_call (&sDaemon, qport, &sstat, cretry, &fcalled); + + uclear_queue (&sDaemon); + + if (fret) + return TRUE; + if (fcalled) + return FALSE; + + /* Now we have to dump that port so that we can aquire a new + one. On Unix this means that we will fork and get a new + process ID, so we must unlock and relock the system. */ + if (fdetach) + { + (void) fsysdep_unlock_system (&sLocked_system); + fLocked_system = FALSE; + usysdep_detach (); + if (! fsysdep_lock_system (&sLocked_system)) + return FALSE; + fLocked_system = TRUE; + } + } + + if (fbadtime && ftimewarn) + { + ulog (LOG_NORMAL, "Wrong time to call"); + + /* Update the status, unless the system can never be called. If + the system can never be called, there is little point to + putting in a ``wrong time to call'' message. We don't change + the number of retries, although we do set the wait until the + next retry to 0. */ + if (! fnevertime) + { + sstat.ttype = STATUS_WRONG_TIME; + sstat.ilast = inow; + sstat.cwait = 0; + (void) fsysdep_set_status (qorigsys, &sstat); + } + } + + return FALSE; +} + +/* Find a port to use when calling a system, open a connection, and + dial the system. The actual call is done in fdo_call. This + routine is responsible for opening and closing the connection. */ + +static boolean +fconn_call (qdaemon, qport, qstat, cretry, pfcalled) + struct sdaemon *qdaemon; + struct uuconf_port *qport; + struct sstatus *qstat; + int cretry; + boolean *pfcalled; +{ + pointer puuconf; + const struct uuconf_system *qsys; + struct uuconf_port sport; + struct sconnection sconn; + enum tstatus_type terr; + boolean fret; + + puuconf = qdaemon->puuconf; + qsys = qdaemon->qsys; + + *pfcalled = FALSE; + + /* Ignore any SIGHUP signal we may have received up to this point. + This is needed on Unix because we may have gotten one from the + shell before we detached from the controlling terminal. */ + afSignal[INDEXSIG_SIGHUP] = FALSE; + + /* If no port was specified on the command line, use any port + defined for the system. To select the system port: 1) see if + port information was specified directly; 2) see if a port was + named; 3) get an available port given the baud rate. We don't + change the system status if a port is unavailable; i.e. we don't + force the system to wait for the retry time. */ + if (qport == NULL) + qport = qsys->uuconf_qport; + if (qport != NULL) + { + if (! fconn_init (qport, &sconn)) + return FALSE; + if (! fconn_lock (&sconn, FALSE)) + { + ulog (LOG_ERROR, "%s: Port already locked", + qport->uuconf_zname); + return FALSE; + } + } + else + { + struct spass s; + int iuuconf; + + s.fmatched = FALSE; + s.flocked = FALSE; + s.qconn = &sconn; + iuuconf = uuconf_find_port (puuconf, qsys->uuconf_zport, + qsys->uuconf_ibaud, + qsys->uuconf_ihighbaud, + iuport_lock, (pointer) &s, + &sport); + if (iuuconf == UUCONF_NOT_FOUND) + { + if (s.fmatched) + ulog (LOG_ERROR, "All matching ports in use"); + else + ulog (LOG_ERROR, "No matching ports"); + return FALSE; + } + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + if (s.flocked) + { + (void) fconn_unlock (&sconn); + uconn_free (&sconn); + } + return FALSE; + } + } + + if (! fconn_open (&sconn, qsys->uuconf_ibaud, qsys->uuconf_ihighbaud, + FALSE)) + { + terr = STATUS_PORT_FAILED; + fret = FALSE; + } + else + { + struct uuconf_dialer *qdialer; + struct uuconf_dialer sdialer; + enum tdialerfound tdialer; + + if (qsys->uuconf_zalternate == NULL) + ulog (LOG_NORMAL, "Calling system %s (port %s)", qsys->uuconf_zname, + zLdevice == NULL ? (char *) "unknown" : zLdevice); + else + ulog (LOG_NORMAL, "Calling system %s (alternate %s, port %s)", + qsys->uuconf_zname, qsys->uuconf_zalternate, + zLdevice == NULL ? (char *) "unknown" : zLdevice); + + qdialer = NULL; + + if (! fconn_dial (&sconn, puuconf, qsys, qsys->uuconf_zphone, + &sdialer, &tdialer)) + { + terr = STATUS_DIAL_FAILED; + fret = FALSE; + } + else + { + qdaemon->qconn = &sconn; + if (tdialer == DIALERFOUND_FALSE) + qdialer = NULL; + else + qdialer = &sdialer; + fret = fdo_call (qdaemon, qstat, qdialer, pfcalled, &terr); + } + + (void) fconn_close (&sconn, puuconf, qdialer, fret); + + if (tdialer == DIALERFOUND_FREE) + (void) uuconf_dialer_free (puuconf, &sdialer); + } + + if (! fret) + { + DEBUG_MESSAGE2 (DEBUG_HANDSHAKE, "Call failed: %d (%s)", + (int) terr, azStatus[(int) terr]); + qstat->ttype = terr; + qstat->cretries++; + qstat->ilast = ixsysdep_time ((long *) NULL); + if (cretry == 0) + qstat->cwait = CRETRY_WAIT (qstat->cretries); + else + qstat->cwait = cretry * 60; + (void) fsysdep_set_status (qsys, qstat); + } + + (void) fconn_unlock (&sconn); + uconn_free (&sconn); + + if (qport == NULL) + (void) uuconf_port_free (puuconf, &sport); + + return fret; +} + +/* Do the actual work of calling another system. The qsys argument is + the system to call, the qconn argument is the connection to use, + the qstat argument holds the current status of the ssystem, and the + qdialer argument holds the dialer being used (it may be NULL). If + we log in successfully, set *pfcalled to TRUE; this is used to + distinguish a failed dial from a failure during the call. If an + error occurs *pterr is set to the status type to record. */ + +static boolean +fdo_call (qdaemon, qstat, qdialer, pfcalled, pterr) + struct sdaemon *qdaemon; + struct sstatus *qstat; + const struct uuconf_dialer *qdialer; + boolean *pfcalled; + enum tstatus_type *pterr; +{ + pointer puuconf; + const struct uuconf_system *qsys; + struct sconnection *qconn; + const char *zport; + int iuuconf; + char *zstr; + long istart_time; + char *zlog; + + puuconf = qdaemon->puuconf; + qsys = qdaemon->qsys; + qconn = qdaemon->qconn; + + *pterr = STATUS_LOGIN_FAILED; + + if (qconn->qport == NULL) + zport = "unknown"; + else + zport = qconn->qport->uuconf_zname; + if (! fchat (qconn, puuconf, &qsys->uuconf_schat, qsys, + (const struct uuconf_dialer *) NULL, + (const char *) NULL, FALSE, zport, + iconn_baud (qconn))) + return FALSE; + + *pfcalled = TRUE; + istart_time = ixsysdep_time ((long *) NULL); + + *pterr = STATUS_HANDSHAKE_FAILED; + + /* We should now see "Shere" from the other system. Newer systems + send "Shere=foo" where foo is the remote name. */ + zstr = zget_uucp_cmd (qconn, TRUE); + if (zstr == NULL) + return FALSE; + + if (strncmp (zstr, "Shere", 5) != 0) + { + ulog (LOG_ERROR, "Bad initialization string"); + ubuffree (zstr); + return FALSE; + } + + ulog (LOG_NORMAL, "Login successful"); + + qstat->ttype = STATUS_TALKING; + qstat->ilast = ixsysdep_time ((long *) NULL); + qstat->cretries = 0; + qstat->cwait = 0; + if (! fsysdep_set_status (qsys, qstat)) + return FALSE; + + if (zstr[5] == '=') + { + const char *zheresys; + size_t clen; + int icmp; + + /* Some UUCP packages only provide seven characters in the Shere + machine name. Others only provide fourteen. */ + zheresys = zstr + 6; + clen = strlen (zheresys); + if (clen == 7 || clen == 14) + icmp = strncmp (zheresys, qsys->uuconf_zname, clen); + else + icmp = strcmp (zheresys, qsys->uuconf_zname); + if (icmp != 0) + { + if (qsys->uuconf_pzalias != NULL) + { + char **pz; + + for (pz = qsys->uuconf_pzalias; *pz != NULL; pz++) + { + if (clen == 7 || clen == 14) + icmp = strncmp (zheresys, *pz, clen); + else + icmp = strcmp (zheresys, *pz); + if (icmp == 0) + break; + } + } + if (icmp != 0) + { + ulog (LOG_ERROR, "Called wrong system (%s)", zheresys); + ubuffree (zstr); + return FALSE; + } + } + } +#if DEBUG > 1 + else if (zstr[5] != '\0') + DEBUG_MESSAGE1 (DEBUG_HANDSHAKE, + "fdo_call: Strange Shere: %s", zstr); +#endif + + ubuffree (zstr); + + /* We now send "S" name switches, where name is our UUCP name. If + we are using sequence numbers with this system, we send a -Q + argument with the sequence number. If the call-timegrade command + was used, we send a -p argument and a -vgrade= argument with the + grade to send us (we send both argument to make it more likely + that one is recognized). We always send a -N (for new) switch + indicating what new features we support. */ + { + long ival; + char bgrade; + char *zsend; + boolean fret; + + /* Determine the grade we should request of the other system. A + '\0' means that no restrictions have been made. */ + if (! ftimespan_match (qsys->uuconf_qcalltimegrade, &ival, + (int *) NULL)) + bgrade = '\0'; + else + bgrade = (char) ival; + + /* Determine the name we will call ourselves. */ + if (qsys->uuconf_zlocalname != NULL) + qdaemon->zlocalname = qsys->uuconf_zlocalname; + else + { + iuuconf = uuconf_localname (puuconf, &qdaemon->zlocalname); + if (iuuconf == UUCONF_NOT_FOUND) + { + qdaemon->zlocalname = zsysdep_localname (); + if (qdaemon->zlocalname == NULL) + return FALSE; + } + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + } + + zsend = zbufalc (strlen (qdaemon->zlocalname) + 70); + if (! qsys->uuconf_fsequence) + { + if (bgrade == '\0') + sprintf (zsend, "S%s -R -N0%o", qdaemon->zlocalname, + (unsigned int) (FEATURE_SIZES + | FEATURE_EXEC + | FEATURE_RESTART)); + else + sprintf (zsend, "S%s -p%c -vgrade=%c -R -N0%o", + qdaemon->zlocalname, bgrade, bgrade, + (unsigned int) (FEATURE_SIZES + | FEATURE_EXEC + | FEATURE_RESTART)); + } + else + { + long iseq; + + iseq = ixsysdep_get_sequence (qsys); + if (iseq < 0) + return FALSE; + if (bgrade == '\0') + sprintf (zsend, "S%s -Q%ld -R -N0%o", qdaemon->zlocalname, iseq, + (unsigned int) (FEATURE_SIZES + | FEATURE_EXEC + | FEATURE_RESTART)); + else + sprintf (zsend, "S%s -Q%ld -p%c -vgrade=%c -R -N0%o", + qdaemon->zlocalname, iseq, bgrade, bgrade, + (unsigned int) (FEATURE_SIZES + | FEATURE_EXEC + | FEATURE_RESTART)); + } + + fret = fsend_uucp_cmd (qconn, zsend); + ubuffree (zsend); + if (! fret) + return FALSE; + } + + /* Now we should see ROK or Rreason where reason gives a cryptic + reason for failure. If we are talking to a counterpart, we will + get back ROKN, possibly with a feature bitfield attached. */ + zstr = zget_uucp_cmd (qconn, TRUE); + if (zstr == NULL) + return FALSE; + + if (zstr[0] != 'R') + { + ulog (LOG_ERROR, "Bad reponse to handshake string (%s)", + zstr); + ubuffree (zstr); + return FALSE; + } + + if (strncmp (zstr + 1, "OKN", sizeof "OKN" - 1) == 0) + { + if (zstr[sizeof "ROKN" - 1] == '\0') + qdaemon->ifeatures |= FEATURE_SIZES | FEATURE_V103; + else + qdaemon->ifeatures |= (int) strtol (zstr + sizeof "ROKN" - 1, + (char **) NULL, 0); + } + else if (strncmp (zstr + 1, "OK", sizeof "OK" - 1) == 0) + { + if (zstr[sizeof "ROK" - 1] != '\0') + { + char *zopt; + + /* SVR4 UUCP returns options following the ROK string. */ + zopt = zstr + sizeof "ROK" - 1; + while (*zopt != '\0') + { + char b; + long c; + char *zend; + + b = *zopt++; + if (isspace (b) || b != '-') + continue; + switch (*zopt) + { + case 'R': + qdaemon->ifeatures |= (FEATURE_RESTART + | FEATURE_SVR4 + | FEATURE_SIZES); + break; + case 'U': + c = strtol (zopt, &zend, 0); + if (c > 0 && c <= LONG_MAX / (long) 512) + qdaemon->cmax_receive = c * (long) 512; + zopt = zend; + break; + } + while (*zopt != '\0' && ! isspace (*zopt)) + ++zopt; + } + } + } + else if (strcmp (zstr + 1, "CB") == 0) + { + ulog (LOG_NORMAL, "Remote system will call back"); + qstat->ttype = STATUS_COMPLETE; + (void) fsysdep_set_status (qsys, qstat); + ubuffree (zstr); + return TRUE; + } + else + { + ulog (LOG_ERROR, "Handshake failed (%s)", zstr + 1); + ubuffree (zstr); + return FALSE; + } + + ubuffree (zstr); + + /* The slave should now send \020Pprotos\0 where protos is a list of + supported protocols. Each protocol is a single character. */ + zstr = zget_uucp_cmd (qconn, TRUE); + if (zstr == NULL) + return FALSE; + + if (zstr[0] != 'P') + { + ulog (LOG_ERROR, "Bad protocol handshake (%s)", zstr); + ubuffree (zstr); + return FALSE; + } + + /* Determine the reliability characteristics of the connection by + combining information for the port and the dialer. If we have no + information, default to a reliable eight-bit full-duplex + connection. */ + if (qconn->qport != NULL + && (qconn->qport->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0) + qdaemon->ireliable = qconn->qport->uuconf_ireliable; + if (qdialer != NULL + && (qdialer->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0) + { + if (qdaemon->ireliable != 0) + qdaemon->ireliable &= qdialer->uuconf_ireliable; + else + qdaemon->ireliable = qdialer->uuconf_ireliable; + } + if (qdaemon->ireliable == 0) + qdaemon->ireliable = (UUCONF_RELIABLE_RELIABLE + | UUCONF_RELIABLE_EIGHT + | UUCONF_RELIABLE_FULLDUPLEX + | UUCONF_RELIABLE_SPECIFIED); + + /* Now decide which protocol to use. The system and the port may + have their own list of protocols. */ + { + int i; + char ab[5]; + + i = CPROTOCOLS; + if (qsys->uuconf_zprotocols != NULL + || (qconn->qport != NULL + && qconn->qport->uuconf_zprotocols != NULL)) + { + const char *zproto; + + if (qsys->uuconf_zprotocols != NULL) + zproto = qsys->uuconf_zprotocols; + else + zproto = qconn->qport->uuconf_zprotocols; + for (; *zproto != '\0'; zproto++) + { + if (strchr (zstr + 1, *zproto) != NULL) + { + for (i = 0; i < CPROTOCOLS; i++) + if (asProtocols[i].bname == *zproto) + break; + if (i < CPROTOCOLS) + break; + } + } + } + else + { + /* If neither the system nor the port specified a list of + protocols, we want only protocols that match the known + reliability of the dialer and the port. */ + for (i = 0; i < CPROTOCOLS; i++) + { + int ipr; + + ipr = asProtocols[i].ireliable; + if ((ipr & qdaemon->ireliable) != ipr) + continue; + if (strchr (zstr + 1, asProtocols[i].bname) != NULL) + break; + } + } + + ubuffree (zstr); + + if (i >= CPROTOCOLS) + { + (void) fsend_uucp_cmd (qconn, "UN"); + ulog (LOG_ERROR, "No mutually supported protocols"); + return FALSE; + } + + qdaemon->qproto = &asProtocols[i]; + + sprintf (ab, "U%c", qdaemon->qproto->bname); + if (! fsend_uucp_cmd (qconn, ab)) + return FALSE; + } + + /* Run any protocol parameter commands. */ + if (qdaemon->qproto->qcmds != NULL) + { + if (qsys->uuconf_qproto_params != NULL) + uapply_proto_params (puuconf, qdaemon->qproto->bname, + qdaemon->qproto->qcmds, + qsys->uuconf_qproto_params); + if (qconn->qport != NULL + && qconn->qport->uuconf_qproto_params != NULL) + uapply_proto_params (puuconf, qdaemon->qproto->bname, + qdaemon->qproto->qcmds, + qconn->qport->uuconf_qproto_params); + if (qdialer != NULL + && qdialer->uuconf_qproto_params != NULL) + uapply_proto_params (puuconf, qdaemon->qproto->bname, + qdaemon->qproto->qcmds, + qdialer->uuconf_qproto_params); + } + + /* Turn on the selected protocol. */ + if (! (*qdaemon->qproto->pfstart) (qdaemon, &zlog)) + return FALSE; + if (zlog == NULL) + { + zlog = zbufalc (sizeof "protocol ''" + 1); + sprintf (zlog, "protocol '%c'", qdaemon->qproto->bname); + } + ulog (LOG_NORMAL, "Handshake successful (%s)", zlog); + ubuffree (zlog); + + *pterr = STATUS_FAILED; + + { + boolean fret; + long iend_time; + + fret = floop (qdaemon); + + /* Now send the hangup message. As the caller, we send six O's + and expect to receive seven O's. We send the six O's twice to + help the other side. We don't worry about errors here. */ + if (fsend_uucp_cmd (qconn, "OOOOOO") + && fsend_uucp_cmd (qconn, "OOOOOO")) + { + int i, fdone; + + /* We look for the remote hangup string to ensure that the + modem has sent out our hangup string. This is only + necessary because some versions of UUCP complain if they + don't get the hangup string. The remote site should send 7 + O's, but some versions of UUCP only send 6. We look for + the string several times because supposedly some + implementations send some garbage after the last packet but + before the hangup string. */ + for (i = 0; i < 25; i++) + { + zstr = zget_uucp_cmd (qconn, FALSE); + if (zstr == NULL) + break; + fdone = strstr (zstr, "OOOOOO") != NULL; + ubuffree (zstr); + if (fdone) + break; + } + } + + iend_time = ixsysdep_time ((long *) NULL); + + ulog (LOG_NORMAL, "Call complete (%ld seconds)", + iend_time - istart_time); + + if (fret) + { + qstat->ttype = STATUS_COMPLETE; + qstat->ilast = iend_time; + (void) fsysdep_set_status (qsys, qstat); + } + + return fret; + } +} + +/* This routine is called via uuconf_find_port when a matching port is + found. It tries to lock the port. If it fails, it returns + UUCONF_NOT_FOUND to force uuconf_find_port to continue searching + for the next matching port. */ + +static int +iuport_lock (qport, pinfo) + struct uuconf_port *qport; + pointer pinfo; +{ + struct spass *q = (struct spass *) pinfo; + + q->fmatched = TRUE; + + if (! fconn_init (qport, q->qconn)) + return UUCONF_NOT_FOUND; + else if (! fconn_lock (q->qconn, FALSE)) + { + uconn_free (q->qconn); + return UUCONF_NOT_FOUND; + } + else + { + q->flocked = TRUE; + return UUCONF_SUCCESS; + } +} + +/* Prompt for a login name and a password, and run as the slave. */ + +static boolean +flogin_prompt (puuconf, qconn) + pointer puuconf; + struct sconnection *qconn; +{ + char *zuser, *zpass; + boolean fret; + int iuuconf; + + DEBUG_MESSAGE0 (DEBUG_HANDSHAKE, "flogin_prompt: Waiting for login"); + + zuser = NULL; + do + { + ubuffree (zuser); + if (! fconn_write (qconn, "login: ", sizeof "login: " - 1)) + return FALSE; + zuser = zget_typed_line (qconn); + } + while (zuser != NULL && *zuser == '\0'); + + if (zuser == NULL) + return TRUE; + + if (! fconn_write (qconn, "Password:", sizeof "Password:" - 1)) + { + ubuffree (zuser); + return FALSE; + } + + zpass = zget_typed_line (qconn); + if (zpass == NULL) + { + ubuffree (zuser); + return TRUE; + } + + fret = TRUE; + + iuuconf = uuconf_callin (puuconf, zuser, zpass); + ubuffree (zpass); + if (iuuconf == UUCONF_NOT_FOUND) + ulog (LOG_ERROR, "Bad login"); + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + fret = FALSE; + } + else + { +#if DEBUG > 1 + int iholddebug; +#endif + + /* We ignore the return value of faccept_call because we really + don't care whether the call succeeded or not. We are going + to reset the port anyhow. */ +#if DEBUG > 1 + iholddebug = iDebug; +#endif + (void) faccept_call (puuconf, zuser, qconn, (const char **) NULL); +#if DEBUG > 1 + iDebug = iholddebug; +#endif + } + + ubuffree (zuser); + + return fret; +} + +/* Accept a call from a remote system. If pqsys is not NULL, *pqsys + will be set to the system that called in if known. */ + +static boolean +faccept_call (puuconf, zlogin, qconn, pzsystem) + pointer puuconf; + const char *zlogin; + struct sconnection *qconn; + const char **pzsystem; +{ + long istart_time; + const char *zport; + struct uuconf_port *qport; + struct uuconf_port sport; + int iuuconf; + struct uuconf_dialer *qdialer; + struct uuconf_dialer sdialer; + boolean ftcp_port; + char *zsend, *zspace; + boolean fret; + char *zstr; + struct uuconf_system ssys; + const struct uuconf_system *qsys; + const struct uuconf_system *qany; + char *zloc; + struct sstatus sstat; + boolean fgotseq, fgotn; + int i; + char *zlog; + char *zgrade; + + if (pzsystem != NULL) + *pzsystem = NULL; + + ulog (LOG_NORMAL, "Incoming call (login %s port %s)", zlogin, + zLdevice == NULL ? (char *) "unknown" : zLdevice); + + istart_time = ixsysdep_time ((long *) NULL); + + /* Figure out protocol parameters determined by the port. If no + port was specified we're reading standard input, so try to get + the port name and read information from the port file. We only + use the port information to get protocol parameters; we don't + want to start treating the port as though it were a modem, for + example. */ + if (qconn->qport != NULL) + { + qport = qconn->qport; + zport = qport->uuconf_zname; + ftcp_port = FALSE; + } + else + { + zport = zsysdep_port_name (&ftcp_port); + if (zport == NULL) + { + qport = NULL; + zport = "unknown"; + } + else + { + iuuconf = uuconf_find_port (puuconf, zport, (long) 0, (long) 0, + (int (*) P((struct uuconf_port *, + pointer pinfo))) NULL, + (pointer) NULL, + &sport); + if (iuuconf == UUCONF_NOT_FOUND) + qport = NULL; + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + else + qport = &sport; + } + } + + /* If we've managed to figure out that this is a modem port, now try + to get protocol parameters from the dialer. */ + qdialer = NULL; + if (qport != NULL) + { + if (qport->uuconf_ttype == UUCONF_PORTTYPE_MODEM) + { + if (qport->uuconf_u.uuconf_smodem.uuconf_pzdialer != NULL) + { + const char *zdialer; + + zdialer = qport->uuconf_u.uuconf_smodem.uuconf_pzdialer[0]; + iuuconf = uuconf_dialer_info (puuconf, zdialer, &sdialer); + if (iuuconf == UUCONF_SUCCESS) + qdialer = &sdialer; + } + else + qdialer = qport->uuconf_u.uuconf_smodem.uuconf_qdialer; + } + else if (qport->uuconf_ttype == UUCONF_PORTTYPE_TCP + || (qport->uuconf_ttype == UUCONF_PORTTYPE_TLI + && (qport->uuconf_ireliable + & UUCONF_RELIABLE_SPECIFIED) == 0)) + ftcp_port = TRUE; + } + + sDaemon.puuconf = puuconf; + sDaemon.qsys = NULL; + sDaemon.zlocalname = NULL; + sDaemon.qconn = qconn; + sDaemon.qproto = NULL; + sDaemon.clocal_size = -1; + sDaemon.cremote_size = -1; + sDaemon.cmax_ever = -2; + sDaemon.cmax_receive = -1; + sDaemon.ifeatures = 0; + sDaemon.frequest_hangup = FALSE; + sDaemon.fhangup_requested = FALSE; + sDaemon.fhangup = FALSE; + sDaemon.fmaster = FALSE; + sDaemon.fcaller = FALSE; + sDaemon.ireliable = 0; + sDaemon.bgrade = UUCONF_GRADE_LOW; + + /* Get the local name to use. If uuconf_login_localname returns a + value, it is not always freed up, although it should be. */ + iuuconf = uuconf_login_localname (puuconf, zlogin, &zloc); + if (iuuconf == UUCONF_SUCCESS) + sDaemon.zlocalname = zloc; + else if (iuuconf == UUCONF_NOT_FOUND) + { + sDaemon.zlocalname = zsysdep_localname (); + if (sDaemon.zlocalname == NULL) + return FALSE; + } + else + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + + /* Tell the remote system who we are. */ + zsend = zbufalc (strlen (sDaemon.zlocalname) + sizeof "Shere="); + sprintf (zsend, "Shere=%s", sDaemon.zlocalname); + fret = fsend_uucp_cmd (qconn, zsend); + ubuffree (zsend); + if (! fret) + return FALSE; + + zstr = zget_uucp_cmd (qconn, TRUE); + if (zstr == NULL) + return FALSE; + + if (zstr[0] != 'S') + { + ulog (LOG_ERROR, "Bad introduction string"); + ubuffree (zstr); + return FALSE; + } + + zspace = strchr (zstr, ' '); + if (zspace != NULL) + *zspace = '\0'; + + iuuconf = uuconf_system_info (puuconf, zstr + 1, &ssys); + if (iuuconf == UUCONF_NOT_FOUND) + { + char *zscript; + + /* Run the remote.unknown script, if appropriate. */ + iuuconf = uuconf_remote_unknown (puuconf, &zscript); + if (iuuconf == UUCONF_SUCCESS) + { + if (! fsysdep_unknown_caller (zscript, zstr + 1)) + { + xfree ((pointer) zscript); + (void) fsend_uucp_cmd (qconn, "RYou are unknown to me"); + ubuffree (zstr); + return FALSE; + } + xfree ((pointer) zscript); + } + else if (iuuconf != UUCONF_NOT_FOUND) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + ubuffree (zstr); + return FALSE; + } + + if (! funknown_system (puuconf, zstr + 1, &ssys)) + { + (void) fsend_uucp_cmd (qconn, "RYou are unknown to me"); + ulog (LOG_ERROR, "Call from unknown system %s", zstr + 1); + ubuffree (zstr); + return FALSE; + } + } + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + ubuffree (zstr); + return FALSE; + } + + qany = NULL; + for (qsys = &ssys; qsys != NULL; qsys = qsys->uuconf_qalternate) + { + if (! qsys->uuconf_fcalled) + continue; + + if (qsys->uuconf_zcalled_login == NULL + || strcmp (qsys->uuconf_zcalled_login, "ANY") == 0) + { + if (qany == NULL) + qany = qsys; + } + else if (strcmp (qsys->uuconf_zcalled_login, zlogin) == 0) + break; + } + + if (qsys == NULL && qany != NULL) + { + iuuconf = uuconf_validate (puuconf, qany, zlogin); + if (iuuconf == UUCONF_SUCCESS) + qsys = qany; + else if (iuuconf != UUCONF_NOT_FOUND) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + ubuffree (zstr); + return FALSE; + } + } + + if (qsys == NULL) + { + (void) fsend_uucp_cmd (qconn, "RLOGIN"); + ulog (LOG_ERROR, "System %s used wrong login name %s", + zstr + 1, zlogin); + ubuffree (zstr); + return FALSE; + } + + sDaemon.qsys = qsys; + + if (pzsystem != NULL) + *pzsystem = zbufcpy (qsys->uuconf_zname); + + ulog_system (qsys->uuconf_zname); + +#if DEBUG > 1 + if (qsys->uuconf_zdebug != NULL) + iDebug |= idebug_parse (qsys->uuconf_zdebug); +#endif + + /* See if we are supposed to call the system back. This will queue + up an empty command. It would be better to actually call back + directly at this point as well. */ + if (qsys->uuconf_fcallback) + { + (void) fsend_uucp_cmd (qconn, "RCB"); + ulog (LOG_NORMAL, "Will call back"); + + /* Clear any existing status. */ + sstat.ttype = STATUS_COMPLETE; + sstat.cretries = 0; + sstat.ilast = ixsysdep_time ((long *) NULL); + sstat.cwait = 0; + (void) fsysdep_set_status (qsys, &sstat); + + ubuffree (zsysdep_spool_commands (qsys, UUCONF_GRADE_HIGH, 0, + (const struct scmd *) NULL)); + ubuffree (zstr); + return TRUE; + } + + /* We only permit one call at a time from a remote system. Lock it. */ + if (! fsysdep_lock_system (qsys)) + { + (void) fsend_uucp_cmd (qconn, "RLCK"); + ulog (LOG_ERROR, "System already locked"); + ubuffree (zstr); + return FALSE; + } + sLocked_system = *qsys; + fLocked_system = TRUE; + + /* Set the system status. We don't care what the status was before. + We also don't want to kill the conversation just because we can't + output the .Status file, so we ignore any errors. */ + sstat.ttype = STATUS_TALKING; + sstat.cretries = 0; + sstat.ilast = ixsysdep_time ((long *) NULL); + sstat.cwait = 0; + (void) fsysdep_set_status (qsys, &sstat); + + /* Check the arguments of the remote system, if any. */ + fgotseq = FALSE; + fgotn = FALSE; + if (zspace != NULL) + { + char **paz; + char **pzset; + + ++zspace; + + /* Break the introduction line up into arguments. */ + paz = (char **) xmalloc ((strlen (zspace) / 2 + 2) * sizeof (char *)); + pzset = paz; + *pzset++ = NULL; + while (TRUE) + { + while (*zspace != '\0' && isspace (BUCHAR (*zspace))) + ++zspace; + if (*zspace == '\0') + break; + *pzset++ = zspace; + ++zspace; + while (*zspace != '\0' && ! isspace (BUCHAR (*zspace))) + ++zspace; + if (*zspace == '\0') + break; + *zspace++ = '\0'; + } + + if (pzset != paz + 1) + { + int iopt; + + *pzset = NULL; + + /* We are going to use getopt to parse the arguments. We + must clear optind to force getopt to reinitialize, and + clear opterr to prevent getopt from printing an error + message. This approach assumes we are using the GNU + getopt, which is distributed with the program anyhow. */ + optind = 0; + opterr = 0; + + while ((iopt = getopt (pzset - paz, paz, + "N::p:Q:RU:v:x:")) != EOF) + { + long iseq; + long c; + char b; + int iwant; + + switch (iopt) + { + case 'N': + /* This is used to indicate support for Taylor UUCP + extensions. An plain -N mean support for size + negotiation. If -N is followed by a number (with + no intervening space), the number is a bit field + of feature flags as defined in trans.h. Note + that the argument may start with 0x for hex or 0 + for octal. */ + fgotn = TRUE; + if (optarg == NULL) + sDaemon.ifeatures |= FEATURE_SIZES | FEATURE_V103; + else + sDaemon.ifeatures |= (int) strtol (optarg, + (char **) NULL, + 0); + break; + + case 'p': + /* The argument is the lowest grade of work the + local system should send. */ + if (UUCONF_GRADE_LEGAL (optarg[0])) + sDaemon.bgrade = optarg[0]; + break; + + case 'Q': + /* The conversation sequence number. */ + iseq = strtol (optarg, (char **) NULL, 10); + if (qsys->uuconf_fsequence + && iseq != ixsysdep_get_sequence (qsys)) + { + (void) fsend_uucp_cmd (qconn, "RBADSEQ"); + ulog (LOG_ERROR, "Out of sequence call rejected"); + sstat.ttype = STATUS_FAILED; + (void) fsysdep_set_status (qsys, &sstat); + xfree ((pointer) paz); + ubuffree (zstr); + return FALSE; + } + fgotseq = TRUE; + break; + + case 'R': + /* The remote system supports file restart. */ + sDaemon.ifeatures |= FEATURE_RESTART; + break; + + case 'U': + /* The maximum file size the remote system is + prepared to received, in blocks where each block + is 512 bytes. */ + c = strtol (optarg, (char **) NULL, 0); + if (c > 0 && c < LONG_MAX / (long) 512) + sDaemon.cmax_receive = c * (long) 512; + break; + + case 'v': + /* -vgrade=X can be used to set the lowest grade of + work the local system should send. */ + if (strncmp (optarg, "grade=", sizeof "grade=" - 1) == 0) + { + b = optarg[sizeof "grade=" - 1]; + if (UUCONF_GRADE_LEGAL (b)) + sDaemon.bgrade = b; + } + break; + + case 'x': + iwant = (int) strtol (optarg, (char **) NULL, 10); +#if DEBUG > 1 + if (iwant <= 9) + iwant = (1 << iwant) - 1; + if (qsys->uuconf_zmax_remote_debug != NULL) + iwant &= idebug_parse (qsys->uuconf_zmax_remote_debug); + if ((iDebug | iwant) != iDebug) + { + iDebug |= iwant; + ulog (LOG_NORMAL, "Setting debugging mode to 0%o", + iDebug); + } +#endif + break; + + default: + break; + } + } + } + + xfree ((pointer) paz); + } + + ubuffree (zstr); + + if (qsys->uuconf_fsequence && ! fgotseq) + { + (void) fsend_uucp_cmd (qconn, "RBADSEQ"); + ulog (LOG_ERROR, "No sequence number (call rejected)"); + sstat.ttype = STATUS_FAILED; + (void) fsysdep_set_status (qsys, &sstat); + return FALSE; + } + + /* We recognized the system, and the sequence number (if any) was + OK. Send an ROK, and send a list of protocols. If we got the -N + switch, send ROKN to confirm it; if the -N switch was followed by + a feature bitfield, return our own feature bitfield. */ + { + char ab[20]; + const char *zreply; + + if (! fgotn) + { + if ((sDaemon.ifeatures & FEATURE_RESTART) == 0) + zreply = "ROK"; + else + { + /* We got -R without -N, so assume that this is SVR4 UUCP. + SVR4 UUCP expects ROK -R to signal support for file + restart. */ + sDaemon.ifeatures |= FEATURE_SVR4 | FEATURE_SIZES; + zreply = "ROK -R"; + } + } + else if ((sDaemon.ifeatures & FEATURE_V103) != 0) + zreply = "ROKN"; + else + { + sprintf (ab, "ROKN0%o", + (unsigned int) (FEATURE_SIZES + | FEATURE_EXEC + | FEATURE_RESTART)); + zreply = ab; + } + if (! fsend_uucp_cmd (qconn, zreply)) + { + sstat.ttype = STATUS_FAILED; + (void) fsysdep_set_status (qsys, &sstat); + return FALSE; + } + } + + /* Determine the reliability of the connection based on the + reliability of the port and the dialer. If we have no + information, default to a reliable eight-bit full-duplex + connection. */ + if (ftcp_port) + sDaemon.ireliable = (UUCONF_RELIABLE_SPECIFIED + | UUCONF_RELIABLE_ENDTOEND + | UUCONF_RELIABLE_RELIABLE + | UUCONF_RELIABLE_EIGHT + | UUCONF_RELIABLE_FULLDUPLEX); + else + { + if (qport != NULL + && (qport->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0) + sDaemon.ireliable = qport->uuconf_ireliable; + if (qdialer != NULL + && (qdialer->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0) + { + if (sDaemon.ireliable != 0) + sDaemon.ireliable &= qdialer->uuconf_ireliable; + else + sDaemon.ireliable = qdialer->uuconf_ireliable; + } + if (sDaemon.ireliable == 0) + sDaemon.ireliable = (UUCONF_RELIABLE_RELIABLE + | UUCONF_RELIABLE_EIGHT + | UUCONF_RELIABLE_FULLDUPLEX + | UUCONF_RELIABLE_SPECIFIED); + } + + if (qsys->uuconf_zprotocols != NULL || + (qport != NULL && qport->uuconf_zprotocols != NULL)) + { + const char *zprotos; + + if (qsys->uuconf_zprotocols != NULL) + zprotos = qsys->uuconf_zprotocols; + else + zprotos = qport->uuconf_zprotocols; + zsend = zbufalc (strlen (zprotos) + 2); + sprintf (zsend, "P%s", zprotos); + } + else + { + char *zset; + + zsend = zbufalc (CPROTOCOLS + 2); + zset = zsend; + *zset++ = 'P'; + + /* If the system did not specify a list of protocols, we want + only protocols that match the known reliability of the dialer + and the port. */ + for (i = 0; i < CPROTOCOLS; i++) + { + int ipr; + + ipr = asProtocols[i].ireliable; + if ((ipr & sDaemon.ireliable) != ipr) + continue; + *zset++ = asProtocols[i].bname; + } + *zset = '\0'; + } + + fret = fsend_uucp_cmd (qconn, zsend); + ubuffree (zsend); + if (! fret) + { + sstat.ttype = STATUS_FAILED; + (void) fsysdep_set_status (qsys, &sstat); + return FALSE; + } + + /* The master will now send back the selected protocol. */ + zstr = zget_uucp_cmd (qconn, TRUE); + if (zstr == NULL) + { + sstat.ttype = STATUS_FAILED; + (void) fsysdep_set_status (qsys, &sstat); + return FALSE; + } + + if (zstr[0] != 'U' || zstr[2] != '\0') + { + ulog (LOG_ERROR, "Bad protocol response string"); + sstat.ttype = STATUS_FAILED; + (void) fsysdep_set_status (qsys, &sstat); + ubuffree (zstr); + return FALSE; + } + + if (zstr[1] == 'N') + { + ulog (LOG_ERROR, "No supported protocol"); + sstat.ttype = STATUS_FAILED; + (void) fsysdep_set_status (qsys, &sstat); + ubuffree (zstr); + return FALSE; + } + + for (i = 0; i < CPROTOCOLS; i++) + if (asProtocols[i].bname == zstr[1]) + break; + + ubuffree (zstr); + + if (i >= CPROTOCOLS) + { + ulog (LOG_ERROR, "No supported protocol"); + sstat.ttype = STATUS_FAILED; + (void) fsysdep_set_status (qsys, &sstat); + return FALSE; + } + + sDaemon.qproto = &asProtocols[i]; + + /* Run the chat script for when a call is received. */ + if (! fchat (qconn, puuconf, &qsys->uuconf_scalled_chat, qsys, + (const struct uuconf_dialer *) NULL, (const char *) NULL, + FALSE, zport, iconn_baud (qconn))) + { + sstat.ttype = STATUS_FAILED; + sstat.ilast = ixsysdep_time ((long *) NULL); + (void) fsysdep_set_status (qsys, &sstat); + return FALSE; + } + + /* Run any protocol parameter commands. */ + if (sDaemon.qproto->qcmds != NULL) + { + if (qsys->uuconf_qproto_params != NULL) + uapply_proto_params (puuconf, sDaemon.qproto->bname, + sDaemon.qproto->qcmds, + qsys->uuconf_qproto_params); + if (qport != NULL + && qport->uuconf_qproto_params != NULL) + uapply_proto_params (puuconf, sDaemon.qproto->bname, + sDaemon.qproto->qcmds, + qport->uuconf_qproto_params); + if (qdialer != NULL + && qdialer->uuconf_qproto_params != NULL) + uapply_proto_params (puuconf, sDaemon.qproto->bname, + sDaemon.qproto->qcmds, + qdialer->uuconf_qproto_params); + } + + /* We don't need the dialer information any more. */ + if (qdialer == &sdialer) + (void) uuconf_dialer_free (puuconf, &sdialer); + + /* Get any jobs queued for the system, and turn on the selected + protocol. */ + if (! fqueue (&sDaemon, (boolean *) NULL) + || ! (*sDaemon.qproto->pfstart) (&sDaemon, &zlog)) + { + uclear_queue (&sDaemon); + sstat.ttype = STATUS_FAILED; + sstat.ilast = ixsysdep_time ((long *) NULL); + (void) fsysdep_set_status (qsys, &sstat); + return FALSE; + } + + if (zlog == NULL) + { + zlog = zbufalc (sizeof "protocol ''" + 1); + sprintf (zlog, "protocol '%c'", sDaemon.qproto->bname); + } + + zgrade = zbufalc (sizeof "grade " + 1); + if (sDaemon.bgrade == UUCONF_GRADE_LOW) + *zgrade = '\0'; + else + sprintf (zgrade, "grade %c ", sDaemon.bgrade); + + /* If we are using HAVE_HDB_LOGGING, then the previous ``incoming + call'' message went to the general log, since we didn't know the + system name at that point. In that case, we repeat the port and + login names. */ +#if HAVE_HDB_LOGGING + ulog (LOG_NORMAL, "Handshake successful (login %s port %s %s%s)", + zlogin, + zLdevice == NULL ? "unknown" : zLdevice, + zgrade, zlog); +#else /* ! HAVE_HDB_LOGGING */ + ulog (LOG_NORMAL, "Handshake successful (%s%s)", zgrade, zlog); +#endif /* ! HAVE_HDB_LOGGING */ + + ubuffree (zlog); + ubuffree (zgrade); + + { + long iend_time; + + fret = floop (&sDaemon); + + /* Hangup. As the answerer, we send seven O's and expect to + receive six O's. We send the seven O's twice to help the other + side. We don't worry about errors here. */ + if (fsend_uucp_cmd (qconn, "OOOOOOO") + && fsend_uucp_cmd (qconn, "OOOOOOO")) + { + int fdone; + + /* We look for the remote hangup string to ensure that the + modem has sent out our hangup string. This is only + necessary because some versions of UUCP complain if they + don't get the hangup string. We look for the string + several times because supposedly some implementations send + some garbage after the last packet but before the hangup + string. */ + for (i = 0; i < 25; i++) + { + zstr = zget_uucp_cmd (qconn, FALSE); + if (zstr == NULL) + break; + fdone = strstr (zstr, "OOOOOO") != NULL; + ubuffree (zstr); + if (fdone) + break; + } + } + + iend_time = ixsysdep_time ((long *) NULL); + + ulog (LOG_NORMAL, "Call complete (%ld seconds)", + iend_time - istart_time); + + uclear_queue (&sDaemon); + + if (fret) + sstat.ttype = STATUS_COMPLETE; + else + sstat.ttype = STATUS_FAILED; + sstat.ilast = iend_time; + (void) fsysdep_set_status (qsys, &sstat); + + (void) uuconf_system_free (puuconf, &ssys); + if (qport == &sport) + (void) uuconf_port_free (puuconf, &sport); + xfree ((pointer) zloc); + + return fret; + } +} + +/* Apply protocol parameters, once we know the protocol. */ + +static void +uapply_proto_params (puuconf, bproto, qcmds, pas) + pointer puuconf; + int bproto; + struct uuconf_cmdtab *qcmds; + struct uuconf_proto_param *pas; +{ + struct uuconf_proto_param *qp; + + for (qp = pas; qp->uuconf_bproto != '\0'; qp++) + { + if (qp->uuconf_bproto == bproto) + { + struct uuconf_proto_param_entry *qe; + + for (qe = qp->uuconf_qentries; qe->uuconf_cargs > 0; qe++) + { + int iuuconf; + + iuuconf = uuconf_cmd_args (puuconf, qe->uuconf_cargs, + qe->uuconf_pzargs, qcmds, + (pointer) NULL, + (uuconf_cmdtabfn) NULL, 0, + (pointer) NULL); + if (UUCONF_ERROR_VALUE (iuuconf) != UUCONF_SUCCESS) + { + ulog (LOG_ERROR, "Error in %c protocol parameters", + bproto); + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + } + } + + break; + } + } +} + +/* Send a string to the other system beginning with a DLE + character and terminated with a null byte. This is only + used when no protocol is in force. */ + +static boolean +fsend_uucp_cmd (qconn, z) + struct sconnection *qconn; + const char *z; +{ + size_t cwrite; + char *zalc; + boolean fret; + + DEBUG_MESSAGE1 (DEBUG_HANDSHAKE, "fsend_uucp_cmd: Sending \"%s\"", z); + + cwrite = strlen (z) + 2; + + zalc = zbufalc (cwrite); + zalc[0] = '\020'; + memcpy (zalc + 1, z, cwrite - 1); + + fret = fconn_write (qconn, zalc, cwrite); + ubuffree (zalc); + return fret; +} + +/* Get a UUCP command beginning with a DLE character and ending with a + null byte. This is only used when no protocol is in force. This + implementation has the potential of being seriously slow. It also + doesn't have any real error recovery. The frequired argument is + passed as TRUE if we need the string; we don't care that much if + we're closing down the connection anyhow. */ + +#define CTIMEOUT (120) +#define CSHORTTIMEOUT (10) +#define CINCREMENT (100) + +static char * +zget_uucp_cmd (qconn, frequired) + struct sconnection *qconn; + boolean frequired; +{ + char *zalc; + size_t calc; + size_t cgot; + boolean fintro; + long iendtime; + int ctimeout; +#if DEBUG > 1 + int cchars; + int iolddebug; +#endif + + iendtime = ixsysdep_time ((long *) NULL); + if (frequired) + iendtime += CTIMEOUT; + else + iendtime += CSHORTTIMEOUT; + +#if DEBUG > 1 + cchars = 0; + iolddebug = iDebug; + if (FDEBUGGING (DEBUG_HANDSHAKE)) + { + ulog (LOG_DEBUG_START, "zget_uucp_cmd: Got \""); + iDebug &=~ (DEBUG_INCOMING | DEBUG_PORT); + } +#endif + + zalc = NULL; + calc = 0; + cgot = 0; + fintro = FALSE; + while ((ctimeout = (int) (iendtime - ixsysdep_time ((long *) NULL))) > 0) + { + int b; + + b = breceive_char (qconn, ctimeout, frequired); + /* Now b == -1 on timeout, -2 on error. */ + if (b < 0) + { +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_HANDSHAKE)) + { + ulog (LOG_DEBUG_END, "\" (%s)", + b == -1 ? "timeout" : "error"); + iDebug = iolddebug; + } +#endif + if (b == -1 && frequired) + ulog (LOG_ERROR, "Timeout"); + ubuffree (zalc); + return NULL; + } + + /* Apparently some systems use parity on these strings, so we + strip the parity bit. This may need to be configurable at + some point, although only if system names can have eight bit + characters. */ + if (! isprint (BUCHAR (b))) + b &= 0x7f; + +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_HANDSHAKE)) + { + char ab[5]; + + ++cchars; + if (cchars > 60) + { + ulog (LOG_DEBUG_END, "\""); + ulog (LOG_DEBUG_START, "zget_uucp_cmd: Got \""); + cchars = 0; + } + (void) cdebug_char (ab, b); + ulog (LOG_DEBUG_CONTINUE, "%s", ab); + } +#endif + + if (! fintro) + { + if (b == '\020') + fintro = TRUE; + continue; + } + + /* If we see another DLE, something has gone wrong; continue + as though this were the first one we saw. */ + if (b == '\020') + { + cgot = 0; + continue; + } + + /* Some systems send a trailing \n on the Shere line. As far as + I can tell this line can never contain a \n, so this + modification should be safe enough. */ + if (b == '\r' || b == '\n') + b = '\0'; + + if (cgot >= calc) + { + char *znew; + + calc += CINCREMENT; + znew = zbufalc (calc); + memcpy (znew, zalc, cgot); + ubuffree (zalc); + zalc = znew; + } + + zalc[cgot] = (char) b; + ++cgot; + + if (b == '\0') + { +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_HANDSHAKE)) + { + ulog (LOG_DEBUG_END, "\""); + iDebug = iolddebug; + } +#endif + return zalc; + } + } + +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_HANDSHAKE)) + { + ulog (LOG_DEBUG_END, "\" (timeout)"); + iDebug = iolddebug; + } +#endif + + ubuffree (zalc); + + if (frequired) + ulog (LOG_ERROR, "Timeout"); + return NULL; +} + +/* Read a sequence of characters up to a newline or carriage return, and + return the line without the line terminating character. */ + +static char * +zget_typed_line (qconn) + struct sconnection *qconn; +{ + char *zalc; + size_t calc; + size_t cgot; + +#if DEBUG > 1 + int cchars; + int iolddebug; + + cchars = 0; + iolddebug = iDebug; + if (FDEBUGGING (DEBUG_CHAT)) + { + ulog (LOG_DEBUG_START, "zget_typed_line: Got \""); + iDebug &=~ (DEBUG_INCOMING | DEBUG_PORT); + } +#endif + + zalc = NULL; + calc = 0; + cgot = 0; + while (TRUE) + { + int b; + + b = breceive_char (qconn, CTIMEOUT, FALSE); + + /* Now b == -1 on timeout, -2 on error. */ + + if (b == -2 || FGOT_SIGNAL ()) + { +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_CHAT)) + { + ulog (LOG_DEBUG_END, "\" (error)"); + iDebug = iolddebug; + } +#endif + ubuffree (zalc); + return NULL; + } + + if (b == -1) + continue; + +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_CHAT)) + { + char ab[5]; + + ++cchars; + if (cchars > 60) + { + ulog (LOG_DEBUG_END, "\""); + ulog (LOG_DEBUG_START, "zget_typed_line: Got \""); + cchars = 0; + } + (void) cdebug_char (ab, b); + ulog (LOG_DEBUG_CONTINUE, "%s", ab); + } +#endif + + if (cgot >= calc) + { + char *znew; + + calc += CINCREMENT; + znew = zbufalc (calc); + memcpy (znew, zalc, cgot); + ubuffree (zalc); + zalc = znew; + } + + if (b == '\r' || b == '\n') + b = '\0'; + + zalc[cgot] = (char) b; + ++cgot; + + if (b == '\0') + { +#if DEBUG > 1 + if (FDEBUGGING (DEBUG_CHAT)) + { + ulog (LOG_DEBUG_END, "\""); + iDebug = iolddebug; + } +#endif + return zalc; + } + } +} diff --git a/gnu/libexec/uucp/uucico/xcmd.c b/gnu/libexec/uucp/uucico/xcmd.c new file mode 100644 index 0000000000..494fe127ab --- /dev/null +++ b/gnu/libexec/uucp/uucico/xcmd.c @@ -0,0 +1,396 @@ +/* xcmd.c + Routines to handle work requests. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char xcmd_rcsid[] = "$Id: xcmd.c,v 1.1 1993/08/04 19:36:35 jtc Exp $"; +#endif + +#include + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" +#include "prot.h" +#include "trans.h" + +/* Local functions. */ + +static boolean flocal_xcmd_request P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); +static boolean flocal_xcmd_await_reply P((struct stransfer *qtrans, + struct sdaemon *qdaemon, + const char *zdata, size_t cdata)); +static boolean fremote_xcmd_reply P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); + +/* Handle a local work request. We just set up the request for + transmission. */ + +boolean +flocal_xcmd_init (qdaemon, qcmd) + struct sdaemon *qdaemon; + struct scmd *qcmd; +{ + struct stransfer *qtrans; + + qtrans = qtransalc (qcmd); + qtrans->psendfn = flocal_xcmd_request; + + return fqueue_local (qdaemon, qtrans); +} + +/* Send the execution request to the remote system. */ + +static boolean +flocal_xcmd_request (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + size_t clen; + char *zsend; + boolean fret; + + ulog (LOG_NORMAL, "Requesting work: %s to %s", qtrans->s.zfrom, + qtrans->s.zto); + + /* We send the string + X from to user options + We put a dash in front of options. */ + clen = (strlen (qtrans->s.zfrom) + strlen (qtrans->s.zto) + + strlen (qtrans->s.zuser) + strlen (qtrans->s.zoptions) + 7); + zsend = zbufalc (clen); + sprintf (zsend, "X %s %s %s -%s", qtrans->s.zfrom, qtrans->s.zto, + qtrans->s.zuser, qtrans->s.zoptions); + + fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, zsend, qtrans->ilocal, + qtrans->iremote); + ubuffree (zsend); + if (! fret) + { + utransfree (qtrans); + return FALSE; + } + + qtrans->fcmd = TRUE; + qtrans->precfn = flocal_xcmd_await_reply; + + return fqueue_receive (qdaemon, qtrans); +} + +/* Get a reply to an execution request from the remote system. */ + +/*ARGSUSED*/ +static boolean +flocal_xcmd_await_reply (qtrans, qdaemon, zdata, cdata) + struct stransfer *qtrans; + struct sdaemon *qdaemon; + const char *zdata; + size_t cdata; +{ + qtrans->precfn = NULL; + + if (zdata[0] != 'X' + || (zdata[1] != 'Y' && zdata[1] != 'N')) + { + ulog (LOG_ERROR, "Bad response to work request"); + utransfree (qtrans); + return FALSE; + } + + if (zdata[1] == 'N') + { + ulog (LOG_ERROR, "%s: work request denied", qtrans->s.zfrom); + (void) fmail_transfer (FALSE, qtrans->s.zuser, (const char *) NULL, + "work request denied", + qtrans->s.zfrom, qdaemon->qsys->uuconf_zname, + qtrans->s.zto, (const char *) NULL, + (const char *) NULL); + } + + (void) fsysdep_did_work (qtrans->s.pseq); + utransfree (qtrans); + + return TRUE; +} + +/* Handle a remote work request. This just queues up the requests for + later processing. */ + +boolean +fremote_xcmd_init (qdaemon, qcmd, iremote) + struct sdaemon *qdaemon; + struct scmd *qcmd; + int iremote; +{ + const struct uuconf_system *qsys; + const char *zexclam; + const struct uuconf_system *qdestsys; + struct uuconf_system sdestsys; + char *zdestfile; + boolean fmkdirs; + struct stransfer *qtrans; + char *zuser; + char aboptions[5]; + char *zfrom; + boolean fret; + char *zfile; + + ulog (LOG_NORMAL, "Work requested: %s to %s", qcmd->zfrom, + qcmd->zto); + + qsys = qdaemon->qsys; + + zexclam = strchr (qcmd->zto, '!'); + if (zexclam == NULL + || zexclam == qcmd->zto + || strncmp (qdaemon->zlocalname, qcmd->zto, + (size_t) (zexclam - qcmd->zto)) == 0) + { + const char *zconst; + + /* The files are supposed to be copied to the local system. */ + qdestsys = NULL; + if (zexclam == NULL) + zconst = qcmd->zto; + else + zconst = zexclam + 1; + + zdestfile = zsysdep_local_file (zconst, qsys->uuconf_zpubdir); + if (zdestfile == NULL) + return FALSE; + + zuser = NULL; + fmkdirs = strchr (qcmd->zoptions, 'f') != NULL; + } + else + { + size_t clen; + char *zcopy; + int iuuconf; + char *zoptions; + + clen = zexclam - qcmd->zto; + zcopy = zbufalc (clen + 1); + memcpy (zcopy, qcmd->zto, clen); + zcopy[clen] = '\0'; + + iuuconf = uuconf_system_info (qdaemon->puuconf, zcopy, &sdestsys); + if (iuuconf == UUCONF_NOT_FOUND) + { + if (! funknown_system (qdaemon->puuconf, zcopy, &sdestsys)) + { + ulog (LOG_ERROR, "%s: System not found", zcopy); + ubuffree (zcopy); + qtrans = qtransalc (qcmd); + qtrans->psendfn = fremote_xcmd_reply; + qtrans->pinfo = (pointer) "XN"; + qtrans->iremote = iremote; + return fqueue_remote (qdaemon, qtrans); + } + } + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, qdaemon->puuconf, iuuconf); + ubuffree (zcopy); + return FALSE; + } + + ubuffree (zcopy); + + qdestsys = &sdestsys; + zdestfile = zbufcpy (zexclam + 1); + + zuser = zbufalc (strlen (qdestsys->uuconf_zname) + + strlen (qcmd->zuser) + sizeof "!"); + sprintf (zuser, "%s!%s", qdestsys->uuconf_zname, qcmd->zuser); + zoptions = aboptions; + *zoptions++ = 'C'; + if (strchr (qcmd->zoptions, 'd') != NULL) + *zoptions++ = 'd'; + if (strchr (qcmd->zoptions, 'm') != NULL) + *zoptions++ = 'm'; + *zoptions = '\0'; + fmkdirs = TRUE; + } + + /* At this point we prepare to confirm the remote request. We could + actually fork here and let the child spool up the requests. */ + qtrans = qtransalc (qcmd); + qtrans->psendfn = fremote_xcmd_reply; + qtrans->pinfo = (pointer) "XY"; + qtrans->iremote = iremote; + if (! fqueue_remote (qdaemon, qtrans)) + { + ubuffree (zdestfile); + ubuffree (zuser); + return FALSE; + } + + /* Now we have to process each source file. The source + specification may or may use wildcards. */ + zfrom = zsysdep_local_file (qcmd->zfrom, qsys->uuconf_zpubdir); + if (zfrom == NULL) + { + ubuffree (zdestfile); + ubuffree (zuser); + return FALSE; + } + + if (! fsysdep_wildcard_start (zfrom)) + { + ubuffree (zfrom); + ubuffree (zdestfile); + ubuffree (zuser); + return FALSE; + } + + fret = TRUE; + + while ((zfile = zsysdep_wildcard (zfrom)) != NULL) + { + char *zto; + char abtname[CFILE_NAME_LEN]; + + if (! fsysdep_file_exists (zfile)) + { + ulog (LOG_ERROR, "%s: no such file", zfile); + continue; + } + + /* Make sure the remote system is permitted to read the + specified file. */ + if (! fin_directory_list (zfile, qsys->uuconf_pzremote_send, + qsys->uuconf_zpubdir, TRUE, TRUE, + (const char *) NULL)) + { + ulog (LOG_ERROR, "%s: not permitted to send", zfile); + break; + } + + if (qdestsys != NULL) + { + /* We really should get the original grade here. */ + zto = zsysdep_data_file_name (qdestsys, qdaemon->zlocalname, + BDEFAULT_UUCP_GRADE, FALSE, + abtname, (char *) NULL, + (char *) NULL); + if (zto == NULL) + { + fret = FALSE; + break; + } + } + else + { + zto = zsysdep_add_base (zdestfile, zfile); + if (zto == NULL) + { + fret = FALSE; + break; + } + /* We only accept a local destination if the remote system + has the right to create files there. */ + if (! fin_directory_list (zto, qsys->uuconf_pzremote_receive, + qsys->uuconf_zpubdir, TRUE, FALSE, + (const char *) NULL)) + { + ulog (LOG_ERROR, "%s: not permitted to receive", zto); + ubuffree (zto); + break; + } + } + + /* Copy the file either to the final destination or to the + spool directory. */ + if (! fcopy_file (zfile, zto, qdestsys == NULL, fmkdirs)) + { + ubuffree (zto); + break; + } + + ubuffree (zto); + + /* If there is a destination system, queue it up. */ + if (qdestsys != NULL) + { + struct scmd ssend; + char *zjobid; + + ssend.bcmd = 'S'; + ssend.pseq = NULL; + ssend.zfrom = zfile; + ssend.zto = zdestfile; + ssend.zuser = zuser; + ssend.zoptions = aboptions; + ssend.ztemp = abtname; + ssend.imode = ixsysdep_file_mode (zfile); + ssend.znotify = ""; + ssend.cbytes = -1; + ssend.zcmd = NULL; + ssend.ipos = 0; + + zjobid = zsysdep_spool_commands (qdestsys, BDEFAULT_UUCP_GRADE, + 1, &ssend); + if (zjobid == NULL) + break; + ubuffree (zjobid); + } + + ubuffree (zfile); + } + + if (zfile != NULL) + ubuffree (zfile); + + (void) fsysdep_wildcard_end (); + + ubuffree (zdestfile); + if (qdestsys != NULL) + (void) uuconf_system_free (qdaemon->puuconf, &sdestsys); + + ubuffree (zfrom); + ubuffree (zuser); + + return fret; +} + +/* Reply to a remote work request. */ + +static boolean +fremote_xcmd_reply (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + boolean fret; + + fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, + (const char *) qtrans->pinfo, + qtrans->ilocal, + qtrans->iremote); + utransfree (qtrans); + return fret; +} diff --git a/gnu/libexec/uucp/uuconv/Makefile b/gnu/libexec/uucp/uuconv/Makefile new file mode 100644 index 0000000000..f06c3e274d --- /dev/null +++ b/gnu/libexec/uucp/uuconv/Makefile @@ -0,0 +1,17 @@ +# Makefile for uuconv +# $Id: Makefile,v 1.2 1993/08/05 16:15:08 jtc Exp $ + +BINDIR= $(sbindir) +BINOWN= $(owner) + +PROG= uuconv +SRCS= uuconv.c +LDADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +DPADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +CFLAGS+= -I$(.CURDIR)/../common_sources\ + -I$(.CURDIR)/../libuuconf\ + -DVERSION=\"$(VERSION)\" + +NOMAN= noman + +.include diff --git a/gnu/libexec/uucp/uuconv/uuconv.c b/gnu/libexec/uucp/uuconv/uuconv.c new file mode 100644 index 0000000000..82f4e35f93 --- /dev/null +++ b/gnu/libexec/uucp/uuconv/uuconv.c @@ -0,0 +1,2012 @@ +/* uuconv.c + Convert one type of UUCP configuration file to another. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucnfi.h" + +#if USE_RCS_ID +const char uuconv_rcsid[] = "$Id: uuconv.c,v 1.1 1993/08/04 19:36:39 jtc Exp $"; +#endif + +#include "getopt.h" + +/* Local functions. */ + +static void uvusage P((void)); +static void uvwrite_time P((FILE *e, struct uuconf_timespan *qtime)); +static void uvwrite_string P((FILE *e, const char *zarg, const char *zcmd)); +static void uvwrite_size P((FILE *e, struct uuconf_timespan *qsize, + const char *zcmd)); +static void uvwrite_boolean P((FILE *e, int iarg, const char *zcmd)); +static void uvwrite_string_array P((FILE *e, char **pz, const char *zcmd)); +static void uvwrite_chat_script P((FILE *e, char **pz)); +static void uvwrite_chat P((FILE *e, const struct uuconf_chat *qchat, + const struct uuconf_chat *qlast, + const char *zprefix, boolean fforce)); +static void uvwrite_proto_params P((FILE *e, + const struct uuconf_proto_param *qparam, + const char *zprefix)); +static void uvwrite_taylor_system P((FILE *e, + const struct uuconf_system *qsys)); +static void uvwrite_v2_system P((FILE *e, + const struct uuconf_system *qsys)); +static void uvwrite_hdb_system P((FILE *e, + const struct uuconf_system *qsys)); +static boolean fvperm_string_cmp P((const char *z1, const char *z2)); +static boolean fvperm_array_cmp P((const char **pz1, const char **pz2)); +static void uvadd_perm P((struct shpermissions *qadd)); +static void uvwrite_perms P((void)); +static void uvwrite_perm_array P((FILE *e, const char **pz, + const char *zcmd, size_t *pccol)); +static void uvwrite_perm_boolean P((FILE *e, int f, const char *zcmd, + size_t *pccol, boolean fsendfiles)); +static void uvwrite_perm_rw_array P((FILE *e, const char **pz, + const char *zcmd, size_t *pccol)); +static void uvwrite_perm_string P((FILE *e, const char *z, const char *zcmd, + size_t *pccol)); +static int ivwrite_taylor_port P((struct uuconf_port *qport, + pointer pinfo)); +static int ivwrite_v2_port P((struct uuconf_port *qport, pointer pinfo)); +static int ivwrite_hdb_port P((struct uuconf_port *qport, pointer pinfo)); +static void uvwrite_taylor_port P((FILE *e, struct uuconf_port *qport, + const char *zprefix)); +static void uvwrite_taylor_dialer P((FILE *e, struct uuconf_dialer *qdialer, + const char *zprefix)); +static void uvwrite_hdb_dialer P((FILE *e, struct uuconf_dialer *qdialer)); +static void uvuuconf_error P((pointer puuconf, int iret)); + +/* A list of Permissions entries built when writing out HDB system + information. */ +static struct shpermissions *qVperms; + +/* Type of configuration file. */ +enum tconfig +{ + CONFIG_TAYLOR, + CONFIG_V2, + CONFIG_HDB +}; + +/* Long getopt options. */ +static const struct option asVlongopts[] = { { NULL, 0, NULL, 0 } }; + +int +main (argc, argv) + int argc; + char **argv; +{ + /* -I: The configuration file name. */ + const char *zconfig = NULL; + /* -i: Input type. */ + const char *zinput = NULL; + /* -o: Output type. */ + const char *zoutput = NULL; + /* -p: Program name. */ + const char *zprogram = NULL; + int iopt; + enum tconfig tinput, toutput; + int iret; + pointer pinput; + + while ((iopt = getopt_long (argc, argv, "i:I:o:p:x:", asVlongopts, + (int *) NULL)) != EOF) + { + switch (iopt) + { + case 'i': + /* Input type. */ + zinput = optarg; + break; + + case 'o': + /* Output type. */ + zoutput = optarg; + break; + + case 'p': + /* Program name. */ + zprogram = optarg; + break; + + case 'I': + /* Set the configuration file name. */ + zconfig = optarg; + break; + + case 'x': + /* Set the debugging level. */ + break; + + case 0: + /* Long option found and flag set. */ + break; + + default: + uvusage (); + break; + } + } + + if (optind != argc + || zinput == NULL + || zoutput == NULL) + uvusage (); + + if (strcasecmp (zinput, "taylor") == 0) + tinput = CONFIG_TAYLOR; + else if (strcasecmp (zinput, "v2") == 0) + tinput = CONFIG_V2; + else if (strcasecmp (zinput, "hdb") == 0) + tinput = CONFIG_HDB; + else + { + uvusage (); + tinput = CONFIG_TAYLOR; + } + + if (strcasecmp (zoutput, "taylor") == 0) + toutput = CONFIG_TAYLOR; + else if (strcasecmp (zoutput, "v2") == 0) + toutput = CONFIG_V2; + else if (strcasecmp (zoutput, "hdb") == 0) + toutput = CONFIG_HDB; + else + { + uvusage (); + toutput = CONFIG_TAYLOR; + } + + if (tinput == toutput) + uvusage (); + + iret = UUCONF_SUCCESS; + + /* Initialize the input. */ + pinput = NULL; + switch (tinput) + { + case CONFIG_TAYLOR: + iret = uuconf_taylor_init (&pinput, zprogram, zconfig); + break; + case CONFIG_V2: + iret = uuconf_v2_init (&pinput); + break; + case CONFIG_HDB: + iret = uuconf_hdb_init (&pinput, zprogram); + break; + } + if (iret != UUCONF_SUCCESS) + { + uvuuconf_error (pinput, iret); + exit (EXIT_FAILURE); + } + + { + char **pzsystems; + char *zsys; + char abtaylor[sizeof ZCURDIR + sizeof SYSFILE - 1]; + char abv2[sizeof ZCURDIR + sizeof V2_SYSTEMS - 1]; + char abhdb[sizeof ZCURDIR + sizeof HDB_SYSTEMS - 1]; + FILE *esys; + char **pz; + + /* Get the list of systems. */ + switch (tinput) + { + case CONFIG_TAYLOR: + iret = uuconf_taylor_system_names (pinput, &pzsystems, FALSE); + break; + case CONFIG_V2: + iret = uuconf_v2_system_names (pinput, &pzsystems, FALSE); + break; + case CONFIG_HDB: + iret = uuconf_hdb_system_names (pinput, &pzsystems, FALSE); + break; + } + if (iret != UUCONF_SUCCESS) + uvuuconf_error (pinput, iret); + else + { + /* Open the sys file for the output type. */ + switch (toutput) + { + default: + case CONFIG_TAYLOR: + sprintf (abtaylor, "%s%s", ZCURDIR, SYSFILE); + zsys = abtaylor; + break; + case CONFIG_V2: + sprintf (abv2, "%s%s", ZCURDIR, V2_SYSTEMS); + zsys = abv2; + break; + case CONFIG_HDB: + sprintf (abhdb, "%s%s", ZCURDIR, HDB_SYSTEMS); + zsys = abhdb; + break; + } + esys = fopen (zsys, "w"); + if (esys == NULL) + { + fprintf (stderr, "uuchk:%s: ", zsys); + perror ("fopen"); + exit (EXIT_FAILURE); + } + + fprintf (esys, "# %s file automatically generated by uuconv.\n", + zsys); + + /* Read and write each system. We cheat and call the internal + routines, so that we can easily detect default information and + not write it out. This isn't necessary, but it makes the output + smaller and simpler. */ + for (pz = pzsystems; *pz != NULL; pz++) + { + struct uuconf_system ssys; + + switch (tinput) + { + case CONFIG_TAYLOR: + iret = _uuconf_itaylor_system_internal (pinput, *pz, &ssys); + break; + case CONFIG_V2: + iret = _uuconf_iv2_system_internal (pinput, *pz, &ssys); + break; + case CONFIG_HDB: + iret = _uuconf_ihdb_system_internal (pinput, *pz, &ssys); + break; + } + if (iret != UUCONF_SUCCESS) + uvuuconf_error (pinput, iret); + else + { + switch (toutput) + { + case CONFIG_TAYLOR: + uvwrite_taylor_system (esys, &ssys); + break; + case CONFIG_V2: + uvwrite_v2_system (esys, &ssys); + break; + case CONFIG_HDB: + uvwrite_hdb_system (esys, &ssys); + break; + } + if (toutput != CONFIG_HDB) + (void) uuconf_system_free (pinput, &ssys); + } + } + + if (toutput == CONFIG_HDB) + uvwrite_perms (); + + if (ferror (esys) + || fclose (esys) == EOF) + { + fprintf (stderr, "uuchk:%s: error during output\n", zsys); + exit (EXIT_FAILURE); + } + } + } + + { + /* Open the port file for the output type. */ + char *zport; + char abtaylor[sizeof ZCURDIR + sizeof PORTFILE - 1]; + char abv2[sizeof ZCURDIR + sizeof V2_DEVICES - 1]; + char abhdb[sizeof ZCURDIR + sizeof HDB_DEVICES - 1]; + FILE *eport; + int (*piportfn) P((struct uuconf_port *, pointer)); + struct uuconf_port sport; + + switch (toutput) + { + default: + case CONFIG_TAYLOR: + sprintf (abtaylor, "%s%s", ZCURDIR, PORTFILE); + zport = abtaylor; + piportfn = ivwrite_taylor_port; + break; + case CONFIG_V2: + sprintf (abv2, "%s%s", ZCURDIR, V2_DEVICES); + zport = abv2; + piportfn = ivwrite_v2_port; + break; + case CONFIG_HDB: + sprintf (abhdb, "%s%s", ZCURDIR, HDB_DEVICES); + zport = abhdb; + piportfn = ivwrite_hdb_port; + break; + } + eport = fopen (zport, "w"); + if (eport == NULL) + { + fprintf (stderr, "uuchk:%s: ", zport); + perror ("fopen"); + exit (EXIT_FAILURE); + } + + fprintf (eport, "# %s file automatically generated by uuconv.\n", zport); + + switch (tinput) + { + case CONFIG_TAYLOR: + iret = uuconf_taylor_find_port (pinput, (const char *) NULL, 0L, + 0L, piportfn, (pointer) eport, + &sport); + break; + case CONFIG_V2: + iret = uuconf_v2_find_port (pinput, (const char *) NULL, 0L, 0L, + piportfn, (pointer) eport, &sport); + break; + case CONFIG_HDB: + iret = uuconf_hdb_find_port (pinput, (const char *) NULL, 0L, 0L, + piportfn, (pointer) eport, &sport); + break; + } + + if (iret != UUCONF_NOT_FOUND) + uvuuconf_error (pinput, iret); + + if (ferror (eport) + || fclose (eport) == EOF) + { + fprintf (stderr, "uuchk:%s: error during output\n", zport); + exit (EXIT_FAILURE); + } + } + + /* V2 configuration files don't support dialers. */ + if (tinput != CONFIG_V2 && toutput != CONFIG_V2) + { + char **pzdialers; + char *zdialer; + char abtaylor[sizeof ZCURDIR + sizeof DIALFILE - 1]; + char abhdb[sizeof ZCURDIR + sizeof HDB_DIALERS - 1]; + FILE *edialer; + char **pz; + + /* Get the list of dialers. */ + switch (tinput) + { + default: + case CONFIG_TAYLOR: + iret = uuconf_taylor_dialer_names (pinput, &pzdialers); + break; + case CONFIG_HDB: + iret = uuconf_hdb_dialer_names (pinput, &pzdialers); + break; + } + if (iret != UUCONF_SUCCESS) + uvuuconf_error (pinput, iret); + else + { + /* Open the sys file for the output type. */ + switch (toutput) + { + default: + case CONFIG_TAYLOR: + sprintf (abtaylor, "%s%s", ZCURDIR, DIALFILE); + zdialer = abtaylor; + break; + case CONFIG_HDB: + sprintf (abhdb, "%s%s", ZCURDIR, HDB_DIALERS); + zdialer = abhdb; + break; + } + edialer = fopen (zdialer, "w"); + if (edialer == NULL) + { + fprintf (stderr, "uuchk:%s: ", zdialer); + perror ("fopen"); + exit (EXIT_FAILURE); + } + + fprintf (edialer, "# %s file automatically generated by uuconv.\n", + zdialer); + + /* Read and write each dialer. */ + for (pz = pzdialers; *pz != NULL; pz++) + { + struct uuconf_dialer sdialer; + + switch (tinput) + { + default: + case CONFIG_TAYLOR: + iret = uuconf_taylor_dialer_info (pinput, *pz, &sdialer); + break; + case CONFIG_HDB: + iret = uuconf_hdb_dialer_info (pinput, *pz, &sdialer); + break; + } + if (iret != UUCONF_SUCCESS) + uvuuconf_error (pinput, iret); + else + { + switch (toutput) + { + default: + case CONFIG_TAYLOR: + fprintf (edialer, "# Start of dialer %s\n", + sdialer.uuconf_zname); + fprintf (edialer, "dialer %s\n", sdialer.uuconf_zname); + uvwrite_taylor_dialer (edialer, &sdialer, ""); + break; + case CONFIG_HDB: + uvwrite_hdb_dialer (edialer, &sdialer); + break; + } + (void) uuconf_dialer_free (pinput, &sdialer); + } + } + + if (ferror (edialer) + || fclose (edialer) == EOF) + { + fprintf (stderr, "uuchk:%s: error during output\n", zdialer); + exit (EXIT_FAILURE); + } + } + } + + exit (EXIT_SUCCESS); +} + +/* Print out a usage message and die. */ + +static void +uvusage () +{ + fprintf (stderr, + "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n", + VERSION); + fprintf (stderr, + "Usage: uuconv -i input -o output [-p program] [-I file]\n"); + fprintf (stderr, + " -i input: Set input type (one of taylor, v2, hdb)\n"); + fprintf (stderr, + " -o output: Set output type (one of taylor, v2, hdb)\n"); + fprintf (stderr, + " -p program: Program to convert (e.g., uucp or cu)\n"); + fprintf (stderr, + " -I file: Set Taylor UUCP configuration file to use\n"); + exit (EXIT_FAILURE); +} + +/* Write out a timespan. */ + +static void +uvwrite_time (e, qtime) + FILE *e; + struct uuconf_timespan *qtime; +{ + if (qtime == NULL) + { + fprintf (e, "Never"); + return; + } + + if (qtime->uuconf_istart == 0 && qtime->uuconf_iend == 7 * 24 * 60) + { + fprintf (e, "Any"); + return; + } + + for (; qtime != NULL; qtime = qtime->uuconf_qnext) + { + int idaystart, idayend; + int ihourstart, ihourend; + int iminutestart, iminuteend; + const char * const zdays = "Su\0Mo\0Tu\0We\0Th\0Fr\0Sa"; + + idaystart = qtime->uuconf_istart / (24 * 60); + ihourstart = (qtime->uuconf_istart % (24 * 60)) / 60; + iminutestart = qtime->uuconf_istart % 60; + if (qtime->uuconf_iend >= 7 * 24 * 60) + qtime->uuconf_iend = 7 * 24 * 60 - 1; + idayend = qtime->uuconf_iend / (24 * 60); + ihourend = (qtime->uuconf_iend % (24 * 60)) / 60; + iminuteend = qtime->uuconf_iend % 60; + if (ihourend == 0 && iminuteend == 0) + --idayend; + + if (idaystart == idayend) + fprintf (e, "%s%02d%02d-%02d%02d", zdays + idaystart * 3, + ihourstart, iminutestart, ihourend, iminuteend); + else + { + int i; + + fprintf (e, "%s%02d%02d-0000", zdays + idaystart * 3, + ihourstart, iminutestart); + for (i = idaystart + 1; i < idayend; i++) + fprintf (e, ",%s", zdays + i * 3); + if (ihourend != 0 || iminuteend != 0) + fprintf (e, ",%s0000-%02d%02d", zdays + idayend * 3, ihourend, + iminuteend); + } + + if (qtime->uuconf_qnext != NULL) + fprintf (e, ","); + } +} + +/* Some subroutines used when writing out Taylor UUCP configuration + files. */ + +/* Write a command with a string argument. */ + +static void +uvwrite_string (e, zarg, zcmd) + FILE *e; + const char *zarg; + const char *zcmd; +{ + if (zarg != (const char *) &_uuconf_unset) + fprintf (e, "%s %s\n", zcmd, zarg == NULL ? (const char *) "" : zarg); +} + +/* Write out a size restriction command. */ + +static void +uvwrite_size (e, qtime, zcmd) + FILE *e; + struct uuconf_timespan *qtime; + const char *zcmd; +{ + if (qtime != (struct uuconf_timespan *) &_uuconf_unset) + { + for (; qtime != NULL; qtime = qtime->uuconf_qnext) + { + fprintf (e, "%s %ld", zcmd, qtime->uuconf_ival); + uvwrite_time (e, qtime); + fprintf (e, "\n"); + } + } +} + +/* Write out a boolean argument with a string command. If the value + is less than zero, than it was uninitialized and we don't write + anything. */ + +static void +uvwrite_boolean (e, fval, zcmd) + FILE *e; + int fval; + const char *zcmd; +{ + if (fval >= 0) + fprintf (e, "%s %s\n", zcmd, fval > 0 ? "true" : "false"); +} + +/* Write out a string array as a single command. */ + +static void +uvwrite_string_array (e, pz, zcmd) + FILE *e; + char **pz; + const char *zcmd; +{ + if (pz != (char **) &_uuconf_unset) + { + fprintf (e, "%s", zcmd); + if (pz != NULL) + for (; *pz != NULL; pz++) + fprintf (e, " %s", *pz); + fprintf (e, "\n"); + } +} + +/* Write out a chat script. Don't separate subsend/subexpect strings + by spaces. */ + +static void +uvwrite_chat_script (e, pzarg) + FILE *e; + char **pzarg; +{ + char **pz; + + if (pzarg == NULL || pzarg == (char **) &_uuconf_unset) + return; + + for (pz = pzarg; *pz != NULL; pz++) + { + if ((*pz)[0] != '-' && pz != pzarg) + fprintf (e, " "); + fprintf (e, *pz); + } +} + +/* Write out chat information. If the qlast argument is not NULL, + then only values that are different from qlast should be written. + The fforce argument is used to get around a peculiar problem: if + the ``chat'' command is used with no arguments for a system, then + uuconf_pzchat will be NULL (not &_uuconf_unset) and the default + chat script will not be used. We must distinguish this case from + the ``chat'' command not appearing at all for a port or dialer, in + which case the value will again be NULL. In the former case we + must output a ``chat'' command, in the latter case we would prefer + not to. */ + +static void +uvwrite_chat (e, q, qlast, zprefix, fforce) + FILE *e; + const struct uuconf_chat *q; + const struct uuconf_chat *qlast; + const char *zprefix; + boolean fforce; +{ + char **pz; + char ab[100]; + + if (q->uuconf_pzchat != (char **) &_uuconf_unset + && (qlast == NULL + ? (fforce || q->uuconf_pzchat != NULL) + : qlast->uuconf_pzchat != q->uuconf_pzchat)) + { + fprintf (e, "%schat ", zprefix); + uvwrite_chat_script (e, q->uuconf_pzchat); + fprintf (e, "\n"); + } + + if (q->uuconf_pzprogram != (char **) &_uuconf_unset + && (qlast == NULL + ? q->uuconf_pzprogram != NULL + : qlast->uuconf_pzprogram != q->uuconf_pzprogram)) + { + sprintf (ab, "%schat-program", zprefix); + uvwrite_string_array (e, q->uuconf_pzprogram, ab); + } + + if (q->uuconf_ctimeout >= 0 + && (qlast == NULL + || qlast->uuconf_ctimeout != q->uuconf_ctimeout)) + fprintf (e, "%schat-timeout %d\n", zprefix, q->uuconf_ctimeout); + + if (q->uuconf_pzfail != NULL + && q->uuconf_pzfail != (char **) &_uuconf_unset + && (qlast == NULL + || qlast->uuconf_pzfail != q->uuconf_pzfail)) + for (pz = q->uuconf_pzfail; *pz != NULL; pz++) + fprintf (e, "%schat-fail %s\n", zprefix, *pz); + + if (qlast == NULL || qlast->uuconf_fstrip != q->uuconf_fstrip) + { + sprintf (ab, "%schat-strip", zprefix); + uvwrite_boolean (e, q->uuconf_fstrip, ab); + } +} + +/* Write out protocol parameters to a Taylor UUCP file. */ + +static void +uvwrite_proto_params (e, qparams, zprefix) + FILE *e; + const struct uuconf_proto_param *qparams; + const char *zprefix; +{ + const struct uuconf_proto_param *qp; + + if (qparams == NULL + || qparams == (struct uuconf_proto_param *) &_uuconf_unset) + return; + + for (qp = qparams; qp->uuconf_bproto != '\0'; qp++) + { + const struct uuconf_proto_param_entry *qe; + + for (qe = qp->uuconf_qentries; qe->uuconf_cargs > 0; qe++) + { + int i; + + fprintf (e, "%sprotocol-parameter %c", zprefix, qp->uuconf_bproto); + for (i = 0; i < qe->uuconf_cargs; i++) + fprintf (e, " %s", qe->uuconf_pzargs[i]); + fprintf (e, "\n"); + } + } +} + +/* Write out Taylor UUCP system information. */ + +static void +uvwrite_taylor_system (e, q) + FILE *e; + const struct uuconf_system *q; +{ + char **pz; + const struct uuconf_system *qlast; + + fprintf (e, "# Start of system %s\n", q->uuconf_zname); + + fprintf (e, "system %s\n", q->uuconf_zname); + if (q->uuconf_pzalias != NULL + && q->uuconf_pzalias != (char **) &_uuconf_unset) + for (pz = q->uuconf_pzalias; *pz != NULL; pz++) + uvwrite_string (e, *pz, "alias"); + + for (qlast = NULL; q != NULL; qlast = q, q = q->uuconf_qalternate) + { + struct uuconf_timespan *qtime; + + if (qlast != NULL) + { + fprintf (e, "alternate"); + if (q->uuconf_zalternate != (char *) &_uuconf_unset + && q->uuconf_zalternate != NULL) + fprintf (e, " %s", q->uuconf_zalternate); + fprintf (e, "\n"); + } + +#define CHANGED(x) (qlast == NULL || qlast->x != q->x) + + if (CHANGED (uuconf_qtimegrade) + && (q->uuconf_qtimegrade + != (struct uuconf_timespan *) &_uuconf_unset)) + { + if (q->uuconf_qtimegrade == NULL) + fprintf (e, "time never\n"); + else + { + for (qtime = q->uuconf_qtimegrade; + qtime != NULL; + qtime = qtime->uuconf_qnext) + { + if ((char) qtime->uuconf_ival == UUCONF_GRADE_LOW) + fprintf (e, "time "); + else + fprintf (e, "timegrade %c ", (char) qtime->uuconf_ival); + uvwrite_time (e, qtime); + if (qtime->uuconf_cretry != 0) + fprintf (e, " %d", qtime->uuconf_cretry); + fprintf (e, "\n"); + } + } + } + + if (CHANGED (uuconf_qcalltimegrade) + && (q->uuconf_qcalltimegrade + != (struct uuconf_timespan *) &_uuconf_unset)) + { + for (qtime = q->uuconf_qcalltimegrade; + qtime != NULL; + qtime = qtime->uuconf_qnext) + { + fprintf (e, "call-timegrade %c ", (char) qtime->uuconf_ival); + uvwrite_time (e, qtime); + fprintf (e, "\n"); + } + } + + if (CHANGED (uuconf_qcall_local_size)) + uvwrite_size (e, q->uuconf_qcall_local_size, "call-local-size"); + + if (CHANGED (uuconf_qcall_remote_size)) + uvwrite_size (e, q->uuconf_qcall_remote_size, "call-remote-size"); + + if (CHANGED (uuconf_qcalled_local_size)) + uvwrite_size (e, q->uuconf_qcalled_local_size, "called-local-size"); + + if (CHANGED (uuconf_qcalled_remote_size)) + uvwrite_size (e, q->uuconf_qcalled_remote_size, "called-remote-size"); + + if (CHANGED (uuconf_ibaud) || CHANGED (uuconf_ihighbaud)) + { + if (q->uuconf_ibaud >= 0) + { + if (q->uuconf_ihighbaud > 0) + fprintf (e, "baud-range %ld %ld\n", q->uuconf_ibaud, + q->uuconf_ihighbaud); + else + fprintf (e, "baud %ld\n", q->uuconf_ibaud); + } + } + + if (CHANGED (uuconf_zport) || CHANGED (uuconf_qport)) + { + if (q->uuconf_zport != NULL + && q->uuconf_zport != (char *) &_uuconf_unset) + uvwrite_string (e, q->uuconf_zport, "port"); + else if (q->uuconf_qport != NULL + && (q->uuconf_qport + != (struct uuconf_port *) &_uuconf_unset)) + uvwrite_taylor_port (e, q->uuconf_qport, "port "); + } + + if (CHANGED (uuconf_zphone)) + { + const char *zcmd; + + if (q->uuconf_qport != NULL + && q->uuconf_qport != (struct uuconf_port *) &_uuconf_unset + && (q->uuconf_qport->uuconf_ttype == UUCONF_PORTTYPE_TCP + || q->uuconf_qport->uuconf_ttype == UUCONF_PORTTYPE_TLI)) + zcmd = "address"; + else + zcmd = "phone"; + uvwrite_string (e, q->uuconf_zphone, zcmd); + } + + uvwrite_chat (e, &q->uuconf_schat, + (qlast == NULL + ? (struct uuconf_chat *) NULL + : &qlast->uuconf_schat), + "", TRUE); + + if (CHANGED (uuconf_zcall_login)) + uvwrite_string (e, q->uuconf_zcall_login, "call-login"); + + if (CHANGED (uuconf_zcall_password)) + uvwrite_string (e, q->uuconf_zcall_password, "call-password"); + + if (CHANGED (uuconf_zcalled_login)) + uvwrite_string (e, q->uuconf_zcalled_login, "called-login"); + + if (CHANGED (uuconf_fcallback)) + uvwrite_boolean (e, q->uuconf_fcallback, "callback"); + + if (CHANGED (uuconf_fsequence)) + uvwrite_boolean (e, q->uuconf_fsequence, "sequence"); + + if (CHANGED (uuconf_zprotocols)) + uvwrite_string (e, q->uuconf_zprotocols, "protocol"); + + if (CHANGED (uuconf_qproto_params)) + uvwrite_proto_params (e, q->uuconf_qproto_params, ""); + + uvwrite_chat (e, &q->uuconf_scalled_chat, + (qlast == NULL + ? (struct uuconf_chat *) NULL + : &qlast->uuconf_scalled_chat), + "called-", FALSE); + + if (CHANGED (uuconf_zdebug)) + uvwrite_string (e, q->uuconf_zdebug, "debug"); + + if (CHANGED (uuconf_zmax_remote_debug)) + uvwrite_string (e, q->uuconf_zmax_remote_debug, "max-remote-debug"); + + if ((CHANGED (uuconf_fsend_request) + || CHANGED (uuconf_frec_request)) + && (q->uuconf_fsend_request >= 0 + || q->uuconf_frec_request >= 0)) + { + if (q->uuconf_fsend_request >= 0 + && (q->uuconf_fsend_request > 0 + ? q->uuconf_frec_request > 0 + : q->uuconf_frec_request == 0)) + uvwrite_boolean (e, q->uuconf_fsend_request, "request"); + else + { + uvwrite_boolean (e, q->uuconf_fsend_request, "send-request"); + uvwrite_boolean (e, q->uuconf_frec_request, + "receive-request"); + } + } + + if ((CHANGED (uuconf_fcall_transfer) + || CHANGED (uuconf_fcalled_transfer)) + && (q->uuconf_fcall_transfer >= 0 + || q->uuconf_fcalled_transfer >= 0)) + { + if (q->uuconf_fcall_transfer >= 0 + && (q->uuconf_fcall_transfer > 0 + ? q->uuconf_fcalled_transfer > 0 + : q->uuconf_fcalled_transfer == 0)) + uvwrite_boolean (e, q->uuconf_fcall_transfer, "transfer"); + else + { + uvwrite_boolean (e, q->uuconf_fcall_transfer, "call-transfer"); + uvwrite_boolean (e, q->uuconf_fcalled_transfer, + "called-transfer"); + } + } + + if (CHANGED (uuconf_pzlocal_send)) + uvwrite_string_array (e, q->uuconf_pzlocal_send, "local-send"); + + if (CHANGED (uuconf_pzremote_send)) + uvwrite_string_array (e, q->uuconf_pzremote_send, "remote-send"); + + if (CHANGED (uuconf_pzlocal_receive)) + uvwrite_string_array (e, q->uuconf_pzlocal_receive, "local-receive"); + + if (CHANGED (uuconf_pzremote_receive)) + uvwrite_string_array (e, q->uuconf_pzremote_receive, + "remote-receive"); + + if (CHANGED (uuconf_pzpath)) + uvwrite_string_array (e, q->uuconf_pzpath, "command-path"); + + if (CHANGED (uuconf_pzcmds)) + uvwrite_string_array (e, q->uuconf_pzcmds, "commands"); + + if (CHANGED (uuconf_cfree_space) + && q->uuconf_cfree_space >= 0) + fprintf (e, "free-space %ld\n", q->uuconf_cfree_space); + + if (CHANGED (uuconf_pzforward_from)) + uvwrite_string_array (e, q->uuconf_pzforward_from, "forward-from"); + + if (CHANGED (uuconf_pzforward_to)) + uvwrite_string_array (e, q->uuconf_pzforward_to, "forward-to"); + + if (CHANGED (uuconf_zpubdir)) + uvwrite_string (e, q->uuconf_zpubdir, "pubdir"); + + if (CHANGED (uuconf_zlocalname)) + uvwrite_string (e, q->uuconf_zlocalname, "myname"); + } +} + +/* Write out V2 system information. */ + +static void +uvwrite_v2_system (e, q) + FILE *e; + const struct uuconf_system *q; +{ + for (; q != NULL; q = q->uuconf_qalternate) + { + fprintf (e, "%s", q->uuconf_zname); + + if (q->uuconf_qtimegrade != (struct uuconf_timespan *) &_uuconf_unset) + { + fprintf (e, " "); + uvwrite_time (e, q->uuconf_qtimegrade); + + if (q->uuconf_zport != (char *) &_uuconf_unset + || q->uuconf_qport != (struct uuconf_port *) &_uuconf_unset) + { + struct uuconf_port *qp; + boolean ftcp; + + qp = q->uuconf_qport; + ftcp = (qp != (struct uuconf_port *) &_uuconf_unset + && qp != NULL + && qp->uuconf_ttype == UUCONF_PORTTYPE_TCP); + if (ftcp + || (q->uuconf_zport != NULL + && q->uuconf_zport != (char *) &_uuconf_unset)) + { + if (ftcp) + fprintf (e, " TCP"); + else + fprintf (e, " %s", q->uuconf_zport); + + if (ftcp || q->uuconf_ibaud >= 0) + { + fprintf (e, " "); + if (ftcp) + { + const char *zport; + + zport = qp->uuconf_u.uuconf_stcp.uuconf_zport; + if (zport == NULL) + zport = "uucp"; + fprintf (e, "%s", zport); + } + else + fprintf (e, "%ld", q->uuconf_ibaud); + + if (q->uuconf_zphone != (char *) &_uuconf_unset + && q->uuconf_zphone != NULL) + { + char **pzc; + + fprintf (e, " %s", q->uuconf_zphone); + pzc = q->uuconf_schat.uuconf_pzchat; + if (pzc != (char **) &_uuconf_unset + && pzc != NULL) + { + fprintf (e, " "); + uvwrite_chat_script (e, pzc); + } + } + } + } + } + } + + fprintf (e, "\n"); + + /* Here we should gather information to write out to USERFILE + and L.cmds, and perhaps some day we will. It's much more + likely to happen if somebody else does it, though. */ + } +} + +/* Write out HDB system information. */ + +static void +uvwrite_hdb_system (e, qsys) + FILE *e; + const struct uuconf_system *qsys; +{ + const struct uuconf_system *q; + struct shpermissions sperm; + char *azmachine[2]; + char *azlogname[2]; + + for (q = qsys; q != NULL; q = q->uuconf_qalternate) + { + if (q->uuconf_fcall) + { + fprintf (e, "%s", q->uuconf_zname); + + if (q->uuconf_qtimegrade + != (struct uuconf_timespan *) &_uuconf_unset) + { + const char *zport; + + fprintf (e, " "); + uvwrite_time (e, q->uuconf_qtimegrade); + + zport = q->uuconf_zport; + if (q->uuconf_qport != NULL + && q->uuconf_qport != (struct uuconf_port *) &_uuconf_unset + && q->uuconf_qport->uuconf_ttype == UUCONF_PORTTYPE_TCP) + zport = "TCP"; + if (zport != NULL && zport != (char *) &_uuconf_unset) + { + fprintf (e, " %s", zport); + if (q->uuconf_zprotocols != (char *) &_uuconf_unset + && q->uuconf_zprotocols != NULL) + fprintf (e, ",%s", q->uuconf_zprotocols); + + if (q->uuconf_ibaud >= 0 + || q->uuconf_zphone != (char *) &_uuconf_unset) + { + fprintf (e, " "); + if (q->uuconf_ibaud < 0) + fprintf (e, "Any"); + else + { + fprintf (e, "%ld", q->uuconf_ibaud); + if (q->uuconf_ihighbaud >= 0) + fprintf (e, "-%ld", q->uuconf_ihighbaud); + } + + if (q->uuconf_zphone != (char *) &_uuconf_unset + && q->uuconf_zphone != NULL) + { + char **pzc; + + fprintf (e, " %s", q->uuconf_zphone); + pzc = q->uuconf_schat.uuconf_pzchat; + if (pzc != (char **) &_uuconf_unset + && pzc != NULL) + { + fprintf (e, " "); + uvwrite_chat_script (e, pzc); + } + } + } + } + } + + fprintf (e, "\n"); + } + } + + /* Build a Permissions entry for this system. There will be only + one MACHINE entry for a given system. */ + + for (q = qsys; q != NULL; q = q->uuconf_qalternate) + if (q->uuconf_fcall) + break; + + if (q != NULL) + { + sperm.qnext = NULL; + sperm.pzlogname = NULL; + sperm.pzmachine = NULL; + sperm.frequest = -1; + sperm.fsendfiles = -1; + sperm.pzread = NULL; + sperm.pzwrite = NULL; + sperm.fcallback = -1; + sperm.pzcommands = NULL; + sperm.pzvalidate = NULL; + sperm.zmyname = NULL; + sperm.zpubdir = NULL; + sperm.pzalias = NULL; + + azmachine[0] = q->uuconf_zname; + azmachine[1] = NULL; + sperm.pzmachine = azmachine; + if (q->uuconf_fsend_request >= 0) + sperm.frequest = q->uuconf_fsend_request; + if (q->uuconf_pzremote_send != (char **) &_uuconf_unset + && q->uuconf_pzremote_send != NULL) + sperm.pzread = q->uuconf_pzremote_send; + if (q->uuconf_pzremote_receive != (char **) &_uuconf_unset + && q->uuconf_pzremote_receive != NULL) + sperm.pzwrite = q->uuconf_pzremote_receive; + if (q->uuconf_pzcmds != (char **) &_uuconf_unset + && q->uuconf_pzcmds != NULL) + sperm.pzcommands = q->uuconf_pzcmds; + if (q->uuconf_zlocalname != (char *) &_uuconf_unset + && q->uuconf_zlocalname != NULL) + sperm.zmyname = q->uuconf_zlocalname; + if (q->uuconf_zpubdir != (char *) &_uuconf_unset + && q->uuconf_zpubdir != NULL) + sperm.zpubdir = q->uuconf_zpubdir; + if (q->uuconf_pzalias != (char **) &_uuconf_unset + && q->uuconf_pzalias != NULL) + sperm.pzalias = q->uuconf_pzalias; + + if (q->uuconf_fcalled + && q->uuconf_zcalled_login != (char *) &_uuconf_unset + && q->uuconf_zcalled_login != NULL) + { + azlogname[0] = q->uuconf_zcalled_login; + azlogname[1] = NULL; + sperm.pzlogname = azlogname; + if (q->uuconf_fcalled_transfer >= 0) + sperm.fsendfiles = q->uuconf_fcalled_transfer; + if (q->uuconf_fcallback >= 0) + sperm.fcallback = q->uuconf_fcallback; + sperm.pzvalidate = azmachine; + } + + uvadd_perm (&sperm); + } + + /* Now add a Permissions entry for each alternative that is not used + for calling out. */ + for (q = qsys; q != NULL; q = q->uuconf_qalternate) + { + if (! q->uuconf_fcalled || q->uuconf_fcall) + continue; + + sperm.qnext = NULL; + sperm.pzlogname = NULL; + sperm.pzmachine = NULL; + sperm.frequest = -1; + sperm.fsendfiles = -1; + sperm.pzread = NULL; + sperm.pzwrite = NULL; + sperm.fcallback = -1; + sperm.pzcommands = NULL; + sperm.pzvalidate = NULL; + sperm.zmyname = NULL; + sperm.zpubdir = NULL; + sperm.pzalias = NULL; + + if (q->uuconf_zcalled_login != (char *) &_uuconf_unset + && q->uuconf_zcalled_login != NULL) + azlogname[0] = q->uuconf_zcalled_login; + else + azlogname[0] = (char *) "OTHER"; + azlogname[1] = NULL; + sperm.pzlogname = azlogname; + + if (q->uuconf_fsend_request >= 0) + sperm.frequest = q->uuconf_fsend_request; + if (q->uuconf_fcalled_transfer >= 0) + sperm.fsendfiles = q->uuconf_fcalled_transfer; + if (q->uuconf_pzremote_send != (char **) &_uuconf_unset + && q->uuconf_pzremote_send != NULL) + sperm.pzread = q->uuconf_pzremote_send; + if (q->uuconf_pzremote_receive != (char **) &_uuconf_unset + && q->uuconf_pzremote_receive != NULL) + sperm.pzwrite = q->uuconf_pzremote_receive; + if (q->uuconf_fcallback >= 0) + sperm.fcallback = q->uuconf_fcallback; + if (q->uuconf_zlocalname != (char *) &_uuconf_unset + && q->uuconf_zlocalname != NULL) + sperm.zmyname = q->uuconf_zlocalname; + if (q->uuconf_zpubdir != (char *) &_uuconf_unset + && q->uuconf_zpubdir != NULL) + sperm.zpubdir = q->uuconf_zpubdir; + + uvadd_perm (&sperm); + } +} + +/* Compare two strings from a Permissions entry, returning TRUE if + they are the same. */ +static boolean +fvperm_string_cmp (z1, z2) + const char *z1; + const char *z2; +{ + if (z1 == NULL + ? z2 != NULL + : z2 == NULL) + return FALSE; + + if (z1 == NULL) + return TRUE; + + return strcmp (z1, z2) == 0; +} + +/* Compare two arrays of strings from a Permissions entry, returning + TRUE if they are the same. */ + +static boolean +fvperm_array_cmp (pz1, pz2) + const char **pz1; + const char **pz2; +{ + if (pz1 == NULL + ? pz2 != NULL + : pz2 == NULL) + return FALSE; + + if (pz1 == NULL) + return TRUE; + + for (; *pz1 != NULL && *pz2 != NULL; pz1++, pz2++) + if (strcmp (*pz1, *pz2) != 0) + break; + + return *pz1 == NULL && *pz2 == NULL; +} + +/* Add a Permissions entry to a global list, combining entries where + possible. */ + +static void +uvadd_perm (qadd) + struct shpermissions *qadd; +{ + struct shpermissions *qlook; + struct shpermissions *qnew; + int iret; + + /* If there's no information, don't bother to add this entry. */ + if (qadd->pzlogname == NULL + && qadd->frequest < 0 + && qadd->fsendfiles < 0 + && qadd->pzread == NULL + && qadd->pzwrite == NULL + && qadd->fcallback < 0 + && qadd->pzcommands == NULL + && qadd->pzvalidate == NULL + && qadd->zmyname == NULL + && qadd->zpubdir == NULL + && qadd->pzalias == NULL) + return; + + for (qlook = qVperms; qlook != NULL; qlook = qlook->qnext) + { + /* See if we can merge qadd into qlook. */ + if (qadd->pzlogname == NULL + ? qlook->pzlogname != NULL + : qlook->pzlogname == NULL) + continue; + if (qadd->pzmachine == NULL + ? qlook->pzmachine != NULL + : qlook->pzmachine == NULL) + continue; + if (qadd->frequest != qlook->frequest + || qadd->fsendfiles != qlook->fsendfiles + || qadd->fcallback != qlook->fcallback) + continue; + if (! fvperm_string_cmp (qadd->zmyname, qlook->zmyname) + || ! fvperm_string_cmp (qadd->zpubdir, qlook->zpubdir)) + continue; + if (! fvperm_array_cmp ((const char **) qadd->pzread, + (const char **) qlook->pzread) + || ! fvperm_array_cmp ((const char **) qadd->pzwrite, + (const char **) qlook->pzwrite) + || ! fvperm_array_cmp ((const char **) qadd->pzcommands, + (const char **) qlook->pzcommands)) + continue; + + /* Merge qadd into qlook. */ + if (qadd->pzmachine != NULL) + { + iret = _uuconf_iadd_string ((struct sglobal *) NULL, + qadd->pzmachine[0], FALSE, + TRUE, &qlook->pzmachine, + (pointer) NULL); + if (iret != UUCONF_SUCCESS) + uvuuconf_error ((pointer) NULL, iret); + } + if (qadd->pzlogname != NULL) + { + iret = _uuconf_iadd_string ((struct sglobal *) NULL, + qadd->pzlogname[0], FALSE, + TRUE, &qlook->pzlogname, + (pointer) NULL); + if (iret != UUCONF_SUCCESS) + uvuuconf_error ((pointer) NULL, iret); + } + if (qadd->pzalias != NULL) + { + char **pz; + + for (pz = qadd->pzalias; *pz != NULL; pz++) + { + iret = _uuconf_iadd_string ((struct sglobal *) NULL, + *pz, FALSE, TRUE, + &qlook->pzalias, (pointer) NULL); + if (iret != UUCONF_SUCCESS) + uvuuconf_error ((pointer) NULL, iret); + } + } + + return; + } + + /* We must add qadd as a new entry on the list, which means we must + copy it into the heap. */ + + qnew = (struct shpermissions *) malloc (sizeof (struct shpermissions)); + if (qnew == NULL) + uvuuconf_error ((pointer) NULL, UUCONF_MALLOC_FAILED); + *qnew = *qadd; + if (qadd->pzmachine != NULL) + { + qnew->pzmachine = NULL; + iret = _uuconf_iadd_string ((struct sglobal *) NULL, + qadd->pzmachine[0], FALSE, + FALSE, &qnew->pzmachine, + (pointer) NULL); + if (iret != UUCONF_SUCCESS) + uvuuconf_error ((pointer) NULL, iret); + } + if (qadd->pzlogname != NULL) + { + qnew->pzlogname = NULL; + iret = _uuconf_iadd_string ((struct sglobal *) NULL, + qadd->pzlogname[0], FALSE, + FALSE, &qnew->pzlogname, + (pointer) NULL); + if (iret != UUCONF_SUCCESS) + uvuuconf_error ((pointer) NULL, iret); + } + if (qadd->pzvalidate != NULL) + qnew->pzvalidate = qnew->pzmachine; + + qnew->qnext = qVperms; + qVperms = qnew; +} + +/* Write out the Permissions entries. */ + +static void +uvwrite_perms () +{ + char ab[sizeof ZCURDIR + sizeof HDB_PERMISSIONS - 1]; + FILE *e; + struct shpermissions *q; + + sprintf (ab, "%s%s", ZCURDIR, HDB_PERMISSIONS); + e = fopen (ab, "w"); + if (e == NULL) + { + fprintf (stderr, "uuchk:%s: ", ab); + perror ("fopen"); + exit (EXIT_FAILURE); + } + + fprintf (e, "# Permissions file automatically generated by uuconv.\n"); + + for (q = qVperms; q != NULL; q = q->qnext) + { + size_t ccol; + + ccol = 0; + uvwrite_perm_array (e, (const char **) q->pzlogname, "LOGNAME", &ccol); + uvwrite_perm_array (e, (const char **) q->pzmachine, "MACHINE", &ccol); + uvwrite_perm_boolean (e, q->frequest, "REQUEST", &ccol, FALSE); + uvwrite_perm_boolean (e, q->fsendfiles, "SENDFILES", &ccol, TRUE); + uvwrite_perm_rw_array (e, (const char **) q->pzread, "READ", &ccol); + uvwrite_perm_rw_array (e, (const char **) q->pzwrite, "WRITE", &ccol); + uvwrite_perm_boolean (e, q->fcallback, "CALLBACK", &ccol, FALSE); + uvwrite_perm_array (e, (const char **) q->pzcommands, "COMMANDS", + &ccol); + uvwrite_perm_array (e, (const char **) q->pzvalidate, "VALIDATE", + &ccol); + uvwrite_perm_string (e, q->zmyname, "MYNAME", &ccol); + uvwrite_perm_string (e, q->zpubdir, "PUBDIR", &ccol); + uvwrite_perm_array (e, (const char **) q->pzalias, "ALIAS", &ccol); + + fprintf (e, "\n"); + } + + if (ferror (e) + || fclose (e) == EOF) + { + fprintf (stderr, "uuchk:%s: error during output\n", HDB_PERMISSIONS); + exit (EXIT_FAILURE); + } +} + +/* Write an array out to the Permissions file. */ + +static void +uvwrite_perm_array (e, pzarg, zcmd, pccol) + FILE *e; + const char **pzarg; + const char *zcmd; + size_t *pccol; +{ + size_t c; + const char **pz; + + if (pzarg == NULL) + return; + + c = strlen (zcmd) + 1; + + for (pz = pzarg; *pz != NULL; pz++) + c += strlen (*pz) + 1; + + if (*pccol > 20 && c + *pccol > 75) + { + fprintf (e, " \\\n"); + *pccol = c - 1; + } + else + { + if (*pccol != 0) + fprintf (e, " "); + *pccol += c; + } + + fprintf (e, "%s=", zcmd); + for (pz = pzarg; *pz != NULL; pz++) + { + if (pz != pzarg) + fprintf (e, ":"); + fprintf (e, "%s", *pz); + } +} + +/* Write a boolean value out to the Permissions file. This may be + either a yes/no boolean or a yes/call boolean (the latter is for + SENDFILES). */ + +static void +uvwrite_perm_boolean (e, f, zcmd, pccol, fsendfiles) + FILE *e; + int f; + const char *zcmd; + size_t *pccol; + boolean fsendfiles; +{ + const char *az[2]; + + if (f < 0) + return; + + if (f) + az[0] = "yes"; + else + az[0] = fsendfiles ? "call" : "no"; + az[1] = NULL; + + uvwrite_perm_array (e, az, zcmd, pccol); +} + +/* Write a set of READ or WRITE entries to the Permissions file. We + have to separate out all entries that start with '!'. */ + +static void +uvwrite_perm_rw_array (e, pzarg, zcmd, pccol) + FILE *e; + const char **pzarg; + const char *zcmd; + size_t *pccol; +{ + size_t c; + const char **pz, **pzcopy, **pzset; + + if (pzarg == NULL) + return; + + c = 0; + for (pz = pzarg; *pz != NULL; pz++) + c++; + + pzcopy = (const char **) malloc ((c + 1) * sizeof (char *)); + if (pzcopy == NULL) + uvuuconf_error ((pointer) NULL, UUCONF_MALLOC_FAILED); + + pzset = pzcopy; + for (pz = pzarg; *pz != NULL; pz++) + if ((*pz)[0] != '!') + *pzset++ = *pz; + *pzset = NULL; + + if (pzset != pzcopy) + uvwrite_perm_array (e, (const char **) pzcopy, zcmd, pccol); + + pzset = pzcopy; + for (pz = pzarg; *pz != NULL; pz++) + if ((*pz)[0] == '!') + *pzset++ = *pz; + *pzset = NULL; + + if (pzset != pzcopy) + { + char ab[20]; + + sprintf (ab, "NO%s", zcmd); + uvwrite_perm_array (e, (const char **) pzcopy, ab, pccol); + } +} + +/* Write a string out to the Permissions file. */ + +static void +uvwrite_perm_string (e, z, zcmd, pccol) + FILE *e; + const char *z; + const char *zcmd; + size_t *pccol; +{ + const char *az[2]; + + if (z == NULL) + return; + + az[0] = z; + az[1] = NULL; + + uvwrite_perm_array (e, az, zcmd, pccol); +} + +/* Write out a Taylor UUCP port. This is called via uuconf_find_port; + the pinfo argument is the port file. */ + +static int +ivwrite_taylor_port (qport, pinfo) + struct uuconf_port *qport; + pointer pinfo; +{ + FILE *e = (FILE *) pinfo; + + fprintf (e, "port %s\n", qport->uuconf_zname); + + uvwrite_taylor_port (e, qport, ""); + + /* Return UUCONF_NOT_FOUND to force uuconf_find_port to keep looking + for ports. */ + return UUCONF_NOT_FOUND; +} + +/* Write a port out to a Taylor UUCP configuration file. This doesn't + output the name, since it is called to output a specially defined + port in the sys file. */ + +static void +uvwrite_taylor_port (e, qport, zprefix) + FILE *e; + struct uuconf_port *qport; + const char *zprefix; +{ + const char *ztype; + char ab[100]; + + switch (qport->uuconf_ttype) + { + default: + case UUCONF_PORTTYPE_UNKNOWN: + fprintf (stderr, "uuconv: Bad port type\n"); + exit (EXIT_FAILURE); + break; + case UUCONF_PORTTYPE_STDIN: + ztype = "stdin"; + break; + case UUCONF_PORTTYPE_MODEM: + ztype = "modem"; + break; + case UUCONF_PORTTYPE_DIRECT: + ztype = "direct"; + break; + case UUCONF_PORTTYPE_TCP: + ztype = "tcp"; + break; + case UUCONF_PORTTYPE_TLI: + ztype = "tli"; + break; + } + + fprintf (e, "%stype %s\n", zprefix, ztype); + + if (qport->uuconf_zprotocols != NULL) + fprintf (e, "%sprotocol %s\n", zprefix, qport->uuconf_zprotocols); + + if (qport->uuconf_qproto_params != NULL) + uvwrite_proto_params (e, qport->uuconf_qproto_params, zprefix); + + if ((qport->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0) + { + sprintf (ab, "%sseven-bit", zprefix); + uvwrite_boolean (e, + ((qport->uuconf_ireliable & UUCONF_RELIABLE_EIGHT) + == 0), + ab); + sprintf (ab, "%sreliable", zprefix); + uvwrite_boolean (e, + ((qport->uuconf_ireliable & UUCONF_RELIABLE_RELIABLE) + != 0), + ab); + sprintf (ab, "%shalf-duplex", zprefix); + uvwrite_boolean (e, + ((qport->uuconf_ireliable & UUCONF_RELIABLE_FULLDUPLEX) + == 0), + ab); + } + + if (qport->uuconf_zlockname != NULL) + fprintf (e, "%slockname %s\n", zprefix, qport->uuconf_zlockname); + + switch (qport->uuconf_ttype) + { + default: + break; + case UUCONF_PORTTYPE_MODEM: + { + struct uuconf_modem_port *qm; + + qm = &qport->uuconf_u.uuconf_smodem; + if (qm->uuconf_zdevice != NULL) + fprintf (e, "%sdevice %s\n", zprefix, qm->uuconf_zdevice); + if (qm->uuconf_zdial_device != NULL) + fprintf (e, "%sdial-device %s\n", zprefix, qm->uuconf_zdial_device); + if (qm->uuconf_ibaud != 0) + fprintf (e, "%sbaud %ld\n", zprefix, qm->uuconf_ibaud); + if (qm->uuconf_ilowbaud != 0) + fprintf (e, "%sbaud-range %ld %ld\n", zprefix, qm->uuconf_ilowbaud, + qm->uuconf_ihighbaud); + if (! qm->uuconf_fcarrier) + fprintf (e, "%scarrier false\n", zprefix); + if (qm->uuconf_pzdialer != NULL) + { + if (qm->uuconf_pzdialer[1] == NULL) + fprintf (e, "%sdialer %s\n", zprefix, qm->uuconf_pzdialer[0]); + else + { + sprintf (ab, "%sdialer-sequence", zprefix); + uvwrite_string_array (e, qm->uuconf_pzdialer, zprefix); + } + } + if (qm->uuconf_qdialer != NULL) + { + sprintf (ab, "%sdialer ", zprefix); + uvwrite_taylor_dialer (e, qm->uuconf_qdialer, ab); + } + } + break; + case UUCONF_PORTTYPE_DIRECT: + { + struct uuconf_direct_port *qd; + + qd = &qport->uuconf_u.uuconf_sdirect; + if (qd->uuconf_zdevice != NULL) + fprintf (e, "%sdevice %s\n", zprefix, qd->uuconf_zdevice); + if (qd->uuconf_ibaud != 0) + fprintf (e, "%sbaud %ld\n", zprefix, qd->uuconf_ibaud); + } + break; + case UUCONF_PORTTYPE_TCP: + if (qport->uuconf_u.uuconf_stcp.uuconf_zport != NULL) + fprintf (e, "%sservice %s\n", zprefix, + qport->uuconf_u.uuconf_stcp.uuconf_zport); + break; + case UUCONF_PORTTYPE_TLI: + { + struct uuconf_tli_port *qt; + + qt = &qport->uuconf_u.uuconf_stli; + if (qt->uuconf_zdevice != NULL) + fprintf (e, "%sdevice %s\n", zprefix, qt->uuconf_zdevice); + sprintf (ab, "%sstream", zprefix); + uvwrite_boolean (e, qt->uuconf_fstream, ab); + if (qt->uuconf_pzpush != NULL) + { + sprintf (ab, "%spush", zprefix); + uvwrite_string_array (e, qt->uuconf_pzpush, ab); + } + if (qt->uuconf_pzdialer != NULL) + { + sprintf (ab, "%sdialer-sequence", zprefix); + uvwrite_string_array (e, qt->uuconf_pzdialer, ab); + } + if (qt->uuconf_zservaddr != NULL) + fprintf (e, "%sserver-address %s\n", zprefix, + qt->uuconf_zservaddr); + } + break; + } +} + +/* Write out a port to the V2 L-devices file. This is called via + uuconf_find_port. */ + +static int +ivwrite_v2_port (qport, pinfo) + struct uuconf_port *qport; + pointer pinfo; +{ + FILE *e = (FILE *) pinfo; + + if (qport->uuconf_ttype == UUCONF_PORTTYPE_DIRECT) + { + fprintf (e, "DIR %s - %ld direct", + qport->uuconf_u.uuconf_sdirect.uuconf_zdevice, + qport->uuconf_u.uuconf_sdirect.uuconf_ibaud); + } + else if (qport->uuconf_ttype == UUCONF_PORTTYPE_MODEM) + { + fprintf (e, "%s %s ", qport->uuconf_zname, + qport->uuconf_u.uuconf_smodem.uuconf_zdevice); + if (qport->uuconf_u.uuconf_smodem.uuconf_zdial_device != NULL) + fprintf (e, "%s", qport->uuconf_u.uuconf_smodem.uuconf_zdial_device); + else + fprintf (e, "-"); + fprintf (e, " "); + if (qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud != 0L) + fprintf (e, "%ld-%ld", + qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud, + qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud); + else if (qport->uuconf_u.uuconf_smodem.uuconf_ibaud != 0L) + fprintf (e, "%ld", qport->uuconf_u.uuconf_smodem.uuconf_ibaud); + else + fprintf (e, "Any"); + if (qport->uuconf_u.uuconf_smodem.uuconf_pzdialer != NULL) + fprintf (e, " %s", + qport->uuconf_u.uuconf_smodem.uuconf_pzdialer[0]); + } + else + { + fprintf (e, "# Ignoring port %s with unsupported type", + qport->uuconf_zname); + } + + fprintf (e, "\n"); + + /* Return UUCONF_NOT_FOUND to force uuconf_find_port to keep looking + for a port. */ + return UUCONF_NOT_FOUND; +} + +/* Write out a port to the HDB Devices file. This is called via + uuconf_find_port. */ + +static int +ivwrite_hdb_port (qport, pinfo) + struct uuconf_port *qport; + pointer pinfo; +{ + FILE *e = (FILE *) pinfo; + + if (qport->uuconf_ttype == UUCONF_PORTTYPE_DIRECT) + { + fprintf (e, "Direct"); + if (qport->uuconf_zprotocols != NULL) + fprintf (e, ",%s", qport->uuconf_zprotocols); + fprintf (e, " "); + if (qport->uuconf_u.uuconf_sdirect.uuconf_zdevice != NULL) + fprintf (e, "%s", qport->uuconf_u.uuconf_sdirect.uuconf_zdevice); + else + fprintf (e, "%s", qport->uuconf_zname); + fprintf (e, " - %ld", qport->uuconf_u.uuconf_sdirect.uuconf_ibaud); + } + else if (qport->uuconf_ttype == UUCONF_PORTTYPE_MODEM) + { + fprintf (e, "%s", qport->uuconf_zname); + if (qport->uuconf_zprotocols != NULL) + fprintf (e, ",%s", qport->uuconf_zprotocols); + fprintf (e, " "); + if (qport->uuconf_u.uuconf_smodem.uuconf_zdevice != NULL) + fprintf (e, "%s", qport->uuconf_u.uuconf_smodem.uuconf_zdevice); + else + fprintf (e, "%s", qport->uuconf_zname); + fprintf (e, " "); + if (qport->uuconf_u.uuconf_smodem.uuconf_zdial_device != NULL) + fprintf (e, "%s", qport->uuconf_u.uuconf_smodem.uuconf_zdial_device); + else + fprintf (e, "-"); + fprintf (e, " "); + if (qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud != 0L) + fprintf (e, "%ld-%ld", + qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud, + qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud); + else if (qport->uuconf_u.uuconf_smodem.uuconf_ibaud != 0L) + fprintf (e, "%ld", qport->uuconf_u.uuconf_smodem.uuconf_ibaud); + else + fprintf (e, "Any"); + if (qport->uuconf_u.uuconf_smodem.uuconf_pzdialer != NULL) + { + char **pz; + + for (pz = qport->uuconf_u.uuconf_smodem.uuconf_pzdialer; + *pz != NULL; + pz++) + fprintf (e, " %s", *pz); + } + } + else if (qport->uuconf_ttype == UUCONF_PORTTYPE_TCP) + { + fprintf (e, "TCP"); + if (qport->uuconf_zprotocols != NULL) + fprintf (e, ",%s", qport->uuconf_zprotocols); + fprintf (e, " "); + if (qport->uuconf_u.uuconf_stcp.uuconf_zport == NULL) + fprintf (e, "uucp"); + else + fprintf (e, "%s", qport->uuconf_u.uuconf_stcp.uuconf_zport); + fprintf (e, " - -"); + } + else if (qport->uuconf_ttype == UUCONF_PORTTYPE_TLI) + { + char **pz; + + fprintf (e, "%s", qport->uuconf_zname); + if (qport->uuconf_zprotocols != NULL) + fprintf (e, ",%s", qport->uuconf_zprotocols); + fprintf (e, " "); + if (qport->uuconf_u.uuconf_stli.uuconf_zdevice != NULL) + fprintf (e, "%s", qport->uuconf_u.uuconf_smodem.uuconf_zdevice); + else + fprintf (e, "-"); + fprintf (e, " - -"); + pz = qport->uuconf_u.uuconf_stli.uuconf_pzdialer; + if (pz == NULL + || *pz == NULL + || (strcmp (*pz, "TLI") != 0 + && strcmp (*pz, "TLIS") != 0)) + fprintf (e, " TLI%s \\D", + qport->uuconf_u.uuconf_stli.uuconf_fstream ? "S" : ""); + if (pz != NULL) + for (; *pz != NULL; pz++) + fprintf (e, " %s", *pz); + } + else + { + fprintf (e, "# Ignoring port %s with unsupported type", + qport->uuconf_zname); + } + + fprintf (e, "\n"); + + /* Return UUCONF_NOT_FOUND to force uuconf_find_port to keep looking + for a port. */ + return UUCONF_NOT_FOUND; +} + +/* Write a dialer out to a Taylor UUCP configuration file. This + doesn't output the name, since it is called to output a specially + defined dialer in the sys or port file. */ + +static void +uvwrite_taylor_dialer (e, qdialer, zprefix) + FILE *e; + struct uuconf_dialer *qdialer; + const char *zprefix; +{ + char ab[100]; + + /* Reset default values, so we don't output them unnecessarily. */ + if (qdialer->uuconf_schat.uuconf_ctimeout == 60) + qdialer->uuconf_schat.uuconf_ctimeout = -1; + if (qdialer->uuconf_schat.uuconf_fstrip) + qdialer->uuconf_schat.uuconf_fstrip = -1; + if (qdialer->uuconf_scomplete.uuconf_ctimeout == 60) + qdialer->uuconf_scomplete.uuconf_ctimeout = -1; + if (qdialer->uuconf_scomplete.uuconf_fstrip) + qdialer->uuconf_scomplete.uuconf_fstrip = -1; + if (qdialer->uuconf_sabort.uuconf_ctimeout == 60) + qdialer->uuconf_sabort.uuconf_ctimeout = -1; + if (qdialer->uuconf_sabort.uuconf_fstrip) + qdialer->uuconf_sabort.uuconf_fstrip = -1; + + uvwrite_chat (e, &qdialer->uuconf_schat, (struct uuconf_chat *) NULL, + zprefix, FALSE); + if (qdialer->uuconf_zdialtone != NULL + && strcmp (qdialer->uuconf_zdialtone, ",") != 0) + fprintf (e, "%sdialtone %s\n", zprefix, qdialer->uuconf_zdialtone); + if (qdialer->uuconf_zpause != NULL + && strcmp (qdialer->uuconf_zpause, ",") != 0) + fprintf (e, "%spause %s\n", zprefix, qdialer->uuconf_zpause); + if (! qdialer->uuconf_fcarrier) + fprintf (e, "%scarrier false\n", zprefix); + if (qdialer->uuconf_ccarrier_wait != 60) + fprintf (e, "%scarrier-wait %d\n", zprefix, + qdialer->uuconf_ccarrier_wait); + if (qdialer->uuconf_fdtr_toggle) + fprintf (e, "%sdtr-toggle %s %s\n", zprefix, + qdialer->uuconf_fdtr_toggle ? "true" : "false", + qdialer->uuconf_fdtr_toggle_wait ? "true" : "false"); + sprintf (ab, "%scomplete-", zprefix); + uvwrite_chat (e, &qdialer->uuconf_scomplete, (struct uuconf_chat *) NULL, + ab, FALSE); + sprintf (ab, "%sabort-", zprefix); + uvwrite_chat (e, &qdialer->uuconf_sabort, (struct uuconf_chat *) NULL, + ab, FALSE); + if (qdialer->uuconf_qproto_params != NULL) + uvwrite_proto_params (e, qdialer->uuconf_qproto_params, zprefix); + if ((qdialer->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0) + { + sprintf (ab, "%sseven-bit", zprefix); + uvwrite_boolean (e, + ((qdialer->uuconf_ireliable & UUCONF_RELIABLE_EIGHT) + == 0), + ab); + sprintf (ab, "%sreliable", zprefix); + uvwrite_boolean (e, + ((qdialer->uuconf_ireliable & UUCONF_RELIABLE_RELIABLE) + != 0), + ab); + sprintf (ab, "%shalf-duplex", zprefix); + uvwrite_boolean (e, + ((qdialer->uuconf_ireliable + & UUCONF_RELIABLE_FULLDUPLEX) == 0), + ab); + } +} + +/* Write a dialer out to an HDB configuration file. */ + +static void +uvwrite_hdb_dialer (e, qdialer) + FILE *e; + struct uuconf_dialer *qdialer; +{ + fprintf (e, "%s ", qdialer->uuconf_zname); + + if (qdialer->uuconf_zdialtone != NULL) + fprintf (e, "=%c", qdialer->uuconf_zdialtone[0]); + if (qdialer->uuconf_zpause != NULL) + fprintf (e, "-%c", qdialer->uuconf_zpause[0]); + + if (qdialer->uuconf_schat.uuconf_pzchat != NULL) + { + if (qdialer->uuconf_zdialtone == NULL + && qdialer->uuconf_zpause == NULL) + fprintf (e, "\"\""); + fprintf (e, " "); + uvwrite_chat_script (e, qdialer->uuconf_schat.uuconf_pzchat); + } + + fprintf (e, "\n"); +} + +/* Display a uuconf error and exit. */ + +static void +uvuuconf_error (puuconf, iret) + pointer puuconf; + int iret; +{ + char ab[512]; + + (void) uuconf_error_string (puuconf, iret, ab, sizeof ab); + if ((iret & UUCONF_ERROR_FILENAME) == 0) + fprintf (stderr, "uuconv: %s\n", ab); + else + fprintf (stderr, "uuconv:%s\n", ab); + if (UUCONF_ERROR_VALUE (iret) != UUCONF_FOPEN_FAILED) + exit (EXIT_FAILURE); +} diff --git a/gnu/libexec/uucp/uucp/Makefile b/gnu/libexec/uucp/uucp/Makefile new file mode 100644 index 0000000000..5e45460617 --- /dev/null +++ b/gnu/libexec/uucp/uucp/Makefile @@ -0,0 +1,16 @@ +# Makefile for uucp +# $Id: Makefile,v 1.2 1993/08/05 16:15:11 jtc Exp $ + +BINDIR= $(bindir) +BINOWN= $(owner) +BINMODE= 4555 + +PROG= uucp +SRCS= uucp.c util.c log.c copy.c +LDADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +DPADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +CFLAGS+= -I$(.CURDIR)/../common_sources\ + -DVERSION=\"$(VERSION)\" + +.include +.PATH: $(.CURDIR)/../common_sources diff --git a/gnu/libexec/uucp/uucp/uucp.1 b/gnu/libexec/uucp/uucp/uucp.1 new file mode 100644 index 0000000000..0fa81fe653 --- /dev/null +++ b/gnu/libexec/uucp/uucp/uucp.1 @@ -0,0 +1,175 @@ +''' $Id: uucp.1,v 1.1 1993/08/04 19:36:43 jtc Exp $ +.TH uucp 1 "Taylor UUCP 1.04" +.SH NAME +uucp \- Unix to Unix copy +.SH SYNOPSIS +.B uucp +[ options ] source-file destination-file +.PP +.B uucp +[ options ] source-file... destination-directory +.SH DESCRIPTION +The +.I uucp +command copies files between systems. Each +.I file +argument is either a pathname on the local machine or is of the form +.IP +system!path +.LP +which is interpreted as being on a remote system. +In the first form, the contents of the first file are copied to the +second. In the second form, each source file is copied into the +destination directory. + +A file be transferred to or from +.I system2 +via +.I system1 +by using +.IP +system1!system2!path. +.LP + +Any pathname that does not begin with / or ~ will be appended to the +current directory (unless the +.B \-W +option is used); this resulting path will not necessarily exist on a +remote system. A pathname beginning with a simple ~ starts at the +UUCP public directory; a pathname beginning with ~name starts at the +home directory of the named user. The ~ is interpreted on the +appropriate system. Note that some shells will interpret a simple ~ +to the local home directory before +.I uucp +sees it; to avoid this the ~ must be quoted. + +Shell metacharacters ? * [ ] are interpreted on the appropriate +system, assuming they are quoted to prevent the shell from +interpreting them first. + +The copy does not take place immediately, but is queued up for the +.I uucico +(8) daemon; the daemon is started immediately unless the +.B \-r +switch is given. In any case, the next time the remote system is called the +file(s) will be copied. +.SH OPTIONS +The following options may be given to +.I uucp. +.TP 5 +.B \-c +Do not copy local source files to the spool directory. If they are +removed before being processed by the +.I uucico +(8) daemon, the copy will fail. The files must be readable by the +.I uucico +(8) daemon, and by the invoking user. +.TP 5 +.B \-C +Copy local source files to the spool directory. This is the default. +.TP 5 +.B \-d +Create all necessary directories when doing the copy. This is the +default. +.TP 5 +.B \-f +If any necessary directories do not exist for the destination path, +abort the copy. +.TP 5 +.B \-g grade +Set the grade of the file transfer command. Jobs of a higher grade +are executed first. Grades run 0 ... 9 A ... Z a ... z from high to +low. +.TP 5 +.B \-m +Report completion or failure of the file transfer by +.I mail +(1). +.TP 5 +.B \-n user +Report completion or failure of the file transfer by +.I mail +(1) to the named +user on the remote system. +.TP 5 +.B \-r +Do not start +.I uucico +(8) daemon immediately; merely queue up the file transfer for later +execution. +.TP 5 +.B \-j +Print jobid on standard output. The job may be +later cancelled by passing the jobid to the +.B \-k +switch of +.I uustat +(1). +It is possible for some complex operations to produce more than one +jobid, in which case each will be printed on a separate line. For +example +.EX +uucp sys1!~user1/file1 sys2!~user2/file2 /usr/spool/uucppublic +.EE +will generate two separate jobs, one for the system +.I sys1 +and one for the system +.I sys2. +.TP 5 +.B \-W +Do not prepend remote relative path names with the current directory. +.TP 5 +.B \-x type +Turn on particular debugging types. The following types are +recognized: abnormal, chat, handshake, uucp-proto, proto, port, +config, spooldir, execute, incoming, outgoing. Only abnormal, config, +spooldir and execute are meaningful for +.I uucp. + +Multiple types may be given, separated by commas, and the +.B \-x +option may appear multiple times. A number may also be given, which +will turn on that many types from the foregoing list; for example, +.B \-x 2 +is equivalent to +.B \-x abnormal,chat. +.TP 5 +.B \-I file +Set configuration file to use. This option may not be available, +depending upon how +.I uucp +was compiled. +.SH FILES +The file names may be changed at compilation time or by the +configuration file, so these are only approximations. + +.br +/usr/lib/uucp/config - Configuration file. +.br +/usr/spool/uucp - +UUCP spool directory. +.br +/usr/spool/uucp/Log - +UUCP log file. +.br +/usr/spool/uucppublic - +Default UUCP public directory. +.SH SEE ALSO +mail(1), uux(1), uustat(1), uucico(8) +.SH BUGS +Some of the options are dependent on the capabilities of the +.I uucico +(8) daemon on the remote system. + +The +.I \-n +and +.I \-m +switches do not work when transferring a file from one remote system +to another. + +File modes are not preserved, except for the execute bit. The +resulting file is owned by the uucp user. +.SH AUTHOR +Ian Lance Taylor +(ian@airs.com or uunet!airs!ian) diff --git a/gnu/libexec/uucp/uucp/uucp.c b/gnu/libexec/uucp/uucp/uucp.c new file mode 100644 index 0000000000..93e9ff4484 --- /dev/null +++ b/gnu/libexec/uucp/uucp/uucp.c @@ -0,0 +1,1181 @@ +/* uucp.c + Prepare to copy a file to or from a remote system. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char uucp_rcsid[] = "$Id: uucp.c,v 1.1 1993/08/04 19:36:44 jtc Exp $"; +#endif + +#include +#include + +#include "getopt.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" + +/* Local functions. */ + +static void ucusage P((void)); +static void ucdirfile P((const char *zdir, const char *zfile, + pointer pinfo)); +static void uccopy P((const char *zfile, const char *zdest)); +static void ucadd_cmd P((const struct uuconf_system *qsys, + const struct scmd *qcmd, const char *zlog)); +static void ucspool_cmds P((boolean fjobid)); +static const char *zcone_system P((boolean *pfany)); +static void ucrecord_file P((const char *zfile)); +static void ucabort P((void)); + +/* The program name. */ +char abProgram[] = "uucp"; + +/* Long getopt options. */ +static const struct option asClongopts[] = { { NULL, 0, NULL, 0 } }; + +/* Local variables. There are a bunch of these, mostly set by the + options and the last (the destination) argument. These have file + scope so that they may be easily passed into uccopy; they could for + the most part also be wrapped up in a structure and passed in. */ + +/* The uuconf global pointer. */ +static pointer pCuuconf; + +/* TRUE if source files should be copied to the spool directory. */ +static boolean fCcopy = TRUE; + +/* Grade to use. */ +static char bCgrade = BDEFAULT_UUCP_GRADE; + +/* Whether to send mail to the requesting user when the copy is + complete. */ +static boolean fCmail = FALSE; + +/* User to notify on remote system. */ +static const char *zCnotify = ""; + +/* TRUE if remote files should be prefixed with the current working + directory. */ +static boolean fCexpand = TRUE; + +/* TRUE if necessary directories should be created on the destination + system. */ +static boolean fCmkdirs = TRUE; + +/* Local name. */ +static const char *zClocalname; + +/* User name. */ +static const char *zCuser = NULL; + +/* TRUE if this is a remote request. */ +static boolean fCremote = FALSE; + +/* TRUE if the destination is this system. */ +static boolean fClocaldest; + +/* Destination system. */ +static struct uuconf_system sCdestsys; + +/* Systems to forward to, if not NULL. */ +static char *zCforward; + +/* Options to use when sending a file. */ +static char abCsend_options[20]; + +/* Options to use when receiving a file. */ +static char abCrec_options[20]; + +/* TRUE if the current file being copied from is in the cwd. */ +static boolean fCneeds_cwd; + +/* The main program. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + /* -I: configuration file name. */ + const char *zconfig = NULL; + /* -j: output job id. */ + boolean fjobid = FALSE; + /* -r: don't start uucico when finished. */ + boolean fuucico = TRUE; + /* -R: copy directories recursively. */ + boolean frecursive = FALSE; + /* -s: report status to named file. */ + const char *zstatus_file = NULL; + /* -t: emulate uuto. */ + boolean fuuto = FALSE; + int iopt; + pointer puuconf; + int iuuconf; + int i; + boolean fgetcwd; + char *zexclam; + char *zdestfile; + const char *zdestsys; + char *zoptions; + boolean fexit; + + while ((iopt = getopt_long (argc, argv, "cCdfg:I:jmn:prRs:tu:Wx:", + asClongopts, (int *) NULL)) != EOF) + { + switch (iopt) + { + case 'c': + /* Do not copy local files to spool directory. */ + fCcopy = FALSE; + break; + + case 'p': + case 'C': + /* Copy local files to spool directory. */ + fCcopy = TRUE; + break; + + case 'd': + /* Create directories if necessary. */ + fCmkdirs = TRUE; + break; + + case 'f': + /* Do not create directories if they don't exist. */ + fCmkdirs = FALSE; + break; + + case 'g': + /* Set job grade. */ + bCgrade = optarg[0]; + break; + + case 'I': + /* Name configuration file. */ + if (fsysdep_other_config (optarg)) + zconfig = optarg; + break; + + case 'j': + /* Output job id. */ + fjobid = TRUE; + break; + + case 'm': + /* Mail to requesting user. */ + fCmail = TRUE; + break; + + case 'n': + /* Notify remote user. */ + zCnotify = optarg; + break; + + case 'r': + /* Don't start uucico when finished. */ + fuucico = FALSE; + break; + + case 'R': + /* Copy directories recursively. */ + frecursive = TRUE; + break; + + case 's': + /* Report status to named file. */ + zstatus_file = optarg; + break; + + case 't': + /* Emulate uuto. */ + fuuto = TRUE; + break; + + case 'u': + /* Set user name. */ + zCuser = optarg; + break; + + case 'W': + /* Expand only local file names. */ + fCexpand = FALSE; + break; + + case 'x': +#if DEBUG > 1 + /* Set debugging level. */ + iDebug |= idebug_parse (optarg); +#endif + break; + + case 0: + /* Long option found and flag set. */ + break; + + default: + ucusage (); + break; + } + } + + if (! UUCONF_GRADE_LEGAL (bCgrade)) + { + ulog (LOG_ERROR, "Ignoring illegal grade"); + bCgrade = BDEFAULT_UUCP_GRADE; + } + + /* The user name must contain a '!', which is treated as a remote + name, to avoid spoofing of other users (there is no advantage to + spoofing remote users, except to send them random bits of mail, + which you can do anyhow). */ + if (zCuser != NULL) + { + if (strchr (zCuser, '!') != NULL) + fCremote = TRUE; + else + { + ulog (LOG_ERROR, "Ignoring local user name"); + zCuser = NULL; + } + } + + if (argc - optind < 2) + ucusage (); + + iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + pCuuconf = puuconf; + +#if DEBUG > 1 + { + const char *zdebug; + + iuuconf = uuconf_debuglevel (puuconf, &zdebug); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + if (zdebug != NULL) + iDebug |= idebug_parse (zdebug); + } +#endif + + /* See if we are going to need to know the current directory. We + just check each argument to see whether it's an absolute + pathname. We actually aren't going to need the cwd if fCexpand + is FALSE and the file is remote, but so what. */ + fgetcwd = FALSE; + for (i = optind; i < argc; i++) + { + zexclam = strrchr (argv[i], '!'); + if (zexclam == NULL) + zexclam = argv[i]; + else + ++zexclam; + if (fsysdep_needs_cwd (zexclam)) + { + fgetcwd = TRUE; + break; + } + } + +#ifdef SIGINT + usysdep_signal (SIGINT); +#endif +#ifdef SIGHUP + usysdep_signal (SIGHUP); +#endif +#ifdef SIGQUIT + usysdep_signal (SIGQUIT); +#endif +#ifdef SIGTERM + usysdep_signal (SIGTERM); +#endif +#ifdef SIGPIPE + usysdep_signal (SIGPIPE); +#endif + + usysdep_initialize (puuconf, INIT_SUID | (fgetcwd ? INIT_GETCWD : 0)); + + ulog_fatal_fn (ucabort); + + if (zCuser == NULL) + zCuser = zsysdep_login_name (); + + iuuconf = uuconf_localname (puuconf, &zClocalname); + if (iuuconf == UUCONF_NOT_FOUND) + { + zClocalname = zsysdep_localname (); + if (zClocalname == NULL) + exit (EXIT_FAILURE); + } + else if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + /* If we are emulating uuto, translate the destination argument, and + notify the destination user. This had better not turn into + something that requires the current directory, or we may have + passed INIT_GETCWD incorrectly. */ + if (fuuto) + { + if (*zCnotify == '\0') + { + zexclam = strrchr (argv[argc - 1], '!'); + if (zexclam == NULL) + ucusage (); + zCnotify = zexclam + 1; + } + argv[argc - 1] = zsysdep_uuto (argv[argc - 1], zClocalname); + if (argv[argc - 1] == NULL) + ucusage (); + } + + /* Set up the file transfer options. */ + zoptions = abCsend_options; + if (fCcopy) + *zoptions++ = 'C'; + else + *zoptions++ = 'c'; + if (fCmkdirs) + *zoptions++ = 'd'; + else + *zoptions++ = 'f'; + if (fCmail) + *zoptions++ = 'm'; + if (*zCnotify != '\0') + *zoptions++ = 'n'; + *zoptions = '\0'; + + zoptions = abCrec_options; + if (fCmkdirs) + *zoptions++ = 'd'; + else + *zoptions++ = 'f'; + if (fCmail) + *zoptions++ = 'm'; + *zoptions = '\0'; + + zexclam = strchr (argv[argc - 1], '!'); + if (zexclam == NULL) + { + zdestsys = zClocalname; + zdestfile = argv[argc - 1]; + fClocaldest = TRUE; + } + else + { + size_t clen; + char *zcopy; + + clen = zexclam - argv[argc - 1]; + zcopy = zbufalc (clen + 1); + memcpy (zcopy, argv[argc - 1], clen); + zcopy[clen] = '\0'; + zdestsys = zcopy; + + zdestfile = zexclam + 1; + + fClocaldest = FALSE; + } + + iuuconf = uuconf_system_info (puuconf, zdestsys, &sCdestsys); + if (iuuconf != UUCONF_SUCCESS) + { + if (iuuconf != UUCONF_NOT_FOUND) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + if (fClocaldest) + { + iuuconf = uuconf_system_local (puuconf, &sCdestsys); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + sCdestsys.uuconf_zname = (char *) zClocalname; + } + else + { + if (! funknown_system (puuconf, zdestsys, &sCdestsys)) + ulog (LOG_FATAL, "%s: System not found", zdestsys); + } + } + + /* Here zdestfile is the destination file name following the + destination system name (if any); it may contain other systems to + forward the files through. Isolate the file from the list of + systems. */ + zexclam = strrchr (zdestfile, '!'); + if (zexclam == NULL) + zCforward = NULL; + else + { + size_t clen; + +#if DEBUG > 0 + if (fClocaldest) + ulog (LOG_FATAL, "Can't happen"); +#endif + clen = zexclam - zdestfile; + zCforward = zbufalc (clen + 1); + memcpy (zCforward, zdestfile, clen); + zCforward[clen] = '\0'; + zdestfile = zexclam + 1; + } + + /* Turn the destination into an absolute path, unless it is on a + remote system and -W was used. */ + if (fClocaldest) + zdestfile = zsysdep_local_file_cwd (zdestfile, sCdestsys.uuconf_zpubdir); + else if (fCexpand) + zdestfile = zsysdep_add_cwd (zdestfile); + if (zdestfile == NULL) + { + ulog_close (); + usysdep_exit (FALSE); + } + + /* Process each source argument. */ + for (i = optind; i < argc - 1 && ! FGOT_SIGNAL (); i++) + { + boolean flocal; + char *zfrom; + + fCneeds_cwd = FALSE; + + if (strchr (argv[i], '!') != NULL) + { + flocal = FALSE; + zfrom = zbufcpy (argv[i]); + } + else + { + /* This is a local file. Make sure we get it out of the + original directory. We don't support local wildcards, + leaving that to the shell. */ + flocal = TRUE; + if (fsysdep_needs_cwd (argv[i])) + fCneeds_cwd = TRUE; + zfrom = zsysdep_local_file_cwd (argv[i], + sCdestsys.uuconf_zpubdir); + if (zfrom == NULL) + ucabort (); + } + + if (! flocal || ! fsysdep_directory (zfrom)) + uccopy (zfrom, zdestfile); + else + { + char *zbase, *zindir; + + if (! frecursive) + ulog (LOG_FATAL, "%s: directory without -R", zfrom); + + zbase = zsysdep_base_name (zfrom); + if (zbase == NULL) + ucabort (); + zindir = zsysdep_in_dir (zdestfile, zbase); + ubuffree (zbase); + if (zindir == NULL) + ucabort (); + usysdep_walk_tree (zfrom, ucdirfile, zindir); + ubuffree (zindir); + } + + ubuffree (zfrom); + } + + /* See if we got an interrupt, presumably from the user. */ + if (FGOT_SIGNAL ()) + ucabort (); + + /* Now push out the actual commands, making log entries for them. */ + ulog_to_file (puuconf, TRUE); + ulog_user (zCuser); + + ucspool_cmds (fjobid); + + ulog_close (); + + if (! fuucico) + fexit = TRUE; + else + { + const char *zsys; + boolean fany; + + zsys = zcone_system (&fany); + if (zsys != NULL) + fexit = fsysdep_run ("uucico", "-s", zsys); + else if (fany) + fexit = fsysdep_run ("uucico", "-r1", (const char *) NULL); + else + fexit = TRUE; + } + + usysdep_exit (fexit); + + /* Avoid error about not returning. */ + return 0; +} + +static void +ucusage () +{ + fprintf (stderr, + "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n", + VERSION); + fprintf (stderr, + "Usage: uucp [options] file1 [file2 ...] dest\n"); + fprintf (stderr, + " -c: Do not copy local files to spool directory\n"); + fprintf (stderr, + " -C, -p: Copy local files to spool directory (default)\n"); + fprintf (stderr, + " -d: Create necessary directories (default)\n"); + fprintf (stderr, + " -f: Do not create directories (fail if they do not exist)\n"); + fprintf (stderr, + " -g grade: Set job grade (must be alphabetic)\n"); + fprintf (stderr, + " -m: Report status of copy by mail\n"); + fprintf (stderr, + " -n user: Report status of copy by mail to remote user\n"); + fprintf (stderr, + " -R: Copy directories recursively\n"); + fprintf (stderr, + " -r: Do not start uucico daemon\n"); + fprintf (stderr, + " -s file: Report completion status to file\n"); + fprintf (stderr, + " -j: Report job id\n"); + fprintf (stderr, + " -W: Do not add current directory to remote filenames\n"); + fprintf (stderr, + " -t: Emulate uuto\n"); + fprintf (stderr, + " -u name: Set user name\n"); + fprintf (stderr, + " -x debug: Set debugging level\n"); +#if HAVE_TAYLOR_CONFIG + fprintf (stderr, + " -I file: Set configuration file to use\n"); +#endif /* HAVE_TAYLOR_CONFIG */ + exit (EXIT_FAILURE); +} + +/* This is called for each file in a directory heirarchy. */ + +static void +ucdirfile (zfull, zrelative, pinfo) + const char *zfull; + const char *zrelative; + pointer pinfo; +{ + const char *zdestfile = (const char *) pinfo; + char *zto; + + zto = zsysdep_in_dir (zdestfile, zrelative); + if (zto == NULL) + ucabort (); + + uccopy (zfull, zto); + + ubuffree (zto); +} + +/* Handle the copying of one regular file. The zdest argument is the + destination file; if we are recursively copying a directory, it + will be extended by any subdirectory names. Note that zdest is an + absolute path. */ + +static void +uccopy (zfile, zdest) + const char *zfile; + const char *zdest; +{ + struct scmd s; + char *zexclam; + char *zto; + + zexclam = strchr (zfile, '!'); + + if (zexclam == NULL) + { + openfile_t efrom; + + /* Copy from a local file. Make sure the user has access to + this file, since we are running setuid. */ + if (! fsysdep_access (zfile)) + ucabort (); + + /* If this copy is being requested by a remote system, we may + transfer the file if it needs the current working directory + (meaning, I hope, that it is in the execution directory) or + it is on the permitted transfer list. Note that unlike most + of the other checks, this one is not double-checked by + uucico. */ + if (fCremote + && ! fCneeds_cwd + && ! fin_directory_list (zfile, sCdestsys.uuconf_pzremote_send, + sCdestsys.uuconf_zpubdir, TRUE, + TRUE, (const char *) NULL)) + ulog (LOG_FATAL, "Not permitted to send %s", zfile); + + if (fClocaldest) + { + boolean fok; + + /* Copy one local file to another. */ + + /* Check that we have permission to receive into the desired + directory. */ + if (fCremote) + fok = fin_directory_list (zdest, + sCdestsys.uuconf_pzremote_receive, + sCdestsys.uuconf_zpubdir, TRUE, + FALSE, (const char *) NULL); + else + fok = fin_directory_list (zdest, + sCdestsys.uuconf_pzlocal_receive, + sCdestsys.uuconf_zpubdir, TRUE, + FALSE, zCuser); + if (! fok) + ulog (LOG_FATAL, "Not permitted to receive to %s", zdest); + + zto = zsysdep_add_base (zdest, zfile); + if (zto == NULL) + ucabort (); + + efrom = esysdep_user_fopen (zfile, TRUE, TRUE); + if (! ffileisopen (efrom)) + ucabort (); + if (! fcopy_open_file (efrom, zto, FALSE, fCmkdirs)) + ucabort (); + (void) ffileclose (efrom); + ubuffree (zto); + } + else + { + const char *zloc; + char abtname[CFILE_NAME_LEN]; + unsigned int imode; + char *ztemp; + + /* Copy a local file to a remote file. We may have to + copy the local file to the spool directory. */ + imode = ixsysdep_file_mode (zfile); + if (imode == 0) + ucabort (); + + zloc = sCdestsys.uuconf_zlocalname; + if (zloc == NULL) + zloc = zClocalname; + + ztemp = zsysdep_data_file_name (&sCdestsys, zloc, bCgrade, + FALSE, abtname, (char *) NULL, + (char *) NULL); + if (ztemp == NULL) + ucabort (); + + if (! fCcopy) + { + /* If we are copying the file, we don't actually use the + temporary file; we still want to get a name for the + other system to use as a key for file restart. */ + ubuffree (ztemp); + + /* Make sure the daemon will be permitted to send + this file. */ + if (! fsysdep_daemon_access (zfile)) + ucabort (); + if (! fin_directory_list (zfile, sCdestsys.uuconf_pzlocal_send, + sCdestsys.uuconf_zpubdir, TRUE, TRUE, + (fCremote + ? (const char *) NULL + : zCuser))) + ulog (LOG_FATAL, "Not permitted to send %s", zfile); + } + else + { + efrom = esysdep_user_fopen (zfile, TRUE, TRUE); + if (! ffileisopen (efrom)) + ucabort (); + ucrecord_file (ztemp); + if (! fcopy_open_file (efrom, ztemp, FALSE, TRUE)) + ucabort (); + (void) ffileclose (efrom); + } + + if (zCforward == NULL) + { + /* We're not forwarding. Just send the file. */ + s.bcmd = 'S'; + s.pseq = NULL; + s.zfrom = zbufcpy (zfile); + s.zto = zbufcpy (zdest); + s.zuser = zCuser; + s.zoptions = abCsend_options; + s.ztemp = zbufcpy (abtname); + s.imode = imode; + s.znotify = zCnotify; + s.cbytes = -1; + s.zcmd = NULL; + s.ipos = 0; + + ucadd_cmd (&sCdestsys, &s, (const char *) NULL); + } + else + { + char *zbase; + char *zxqt; + char abxtname[CFILE_NAME_LEN]; + char abdname[CFILE_NAME_LEN]; + char abxname[CFILE_NAME_LEN]; + FILE *e; + char *zlog; + + /* We want to forward this file through sCdestsys to + some other system(s). We set up a remote execution + of uucp on sCdestsys to forward the file along. */ + zbase = zsysdep_base_name (zfile); + if (zbase == NULL) + ucabort (); + + zxqt = zsysdep_data_file_name (&sCdestsys, zloc, bCgrade, + TRUE, abxtname, abdname, + abxname); + if (zxqt == NULL) + ucabort (); + e = esysdep_fopen (zxqt, FALSE, FALSE, TRUE); + if (e == NULL) + ucabort (); + ucrecord_file (zxqt); + + fprintf (e, "U %s %s\n", zCuser, zloc); + fprintf (e, "F %s %s\n", abdname, zbase); + fprintf (e, "C uucp -C"); + if (fCmkdirs) + fprintf (e, " -d"); + else + fprintf (e, " -f"); + fprintf (e, " -g %c", bCgrade); + if (fCmail) + fprintf (e, " -m"); + if (*zCnotify != '\0') + fprintf (e, " -n %s", zCnotify); + if (! fCexpand) + fprintf (e, " -W"); + fprintf (e, " %s %s!%s\n", zbase, zCforward, zdest); + + ubuffree (zbase); + + if (fclose (e) != 0) + ulog (LOG_FATAL, "fclose: %s", strerror (errno)); + + /* Send the execution file. */ + s.bcmd = 'S'; + s.pseq = NULL; + s.zfrom = zbufcpy (abxtname); + s.zto = zbufcpy (abxname); + s.zuser = zCuser; + s.zoptions = "C"; + s.ztemp = s.zfrom; + s.imode = 0666; + s.znotify = NULL; + s.cbytes = -1; + s.zcmd = NULL; + s.ipos = 0; + + zlog = zbufalc (sizeof "Queuing uucp !" + strlen (zfile) + + strlen (zCforward) + strlen (zdest)); + sprintf (zlog, "Queuing uucp %s %s!%s", zfile, zCforward, + zdest); + + ucadd_cmd (&sCdestsys, &s, zlog); + + /* Send the data file. */ + s.bcmd = 'S'; + s.pseq = NULL; + s.zfrom = zbufcpy (zfile); + s.zto = zbufcpy (abdname); + s.zuser = zCuser; + s.zoptions = fCcopy ? "C" : "c"; + s.ztemp = zbufcpy (abtname); + s.imode = 0666; + s.znotify = NULL; + s.cbytes = -1; + s.zcmd = NULL; + s.ipos = 0; + + ucadd_cmd (&sCdestsys, &s, ""); + } + } + } + else + { + char *zfrom; + char *zforward; + size_t clen; + char *zcopy; + struct uuconf_system *qfromsys; + int iuuconf; + const char *zloc; + + /* Copy from a remote file. Get the file name after any systems + we may need to forward the file from. */ + zfrom = strrchr (zfile, '!'); + if (zfrom == zexclam) + zforward = NULL; + else + { + clen = zfrom - zexclam - 1; + zforward = zbufalc (clen + 1); + memcpy (zforward, zexclam + 1, clen); + } + + ++zfrom; + if (fCexpand) + { + /* Add the current directory to the filename if it's not + already there. */ + zfrom = zsysdep_add_cwd (zfrom); + if (zfrom == NULL) + ucabort (); + } + + /* Read the system information. */ + clen = zexclam - zfile; + zcopy = zbufalc (clen + 1); + memcpy (zcopy, zfile, clen); + zcopy[clen] = '\0'; + + qfromsys = ((struct uuconf_system *) + xmalloc (sizeof (struct uuconf_system))); + + iuuconf = uuconf_system_info (pCuuconf, zcopy, qfromsys); + if (iuuconf == UUCONF_NOT_FOUND) + { + if (! funknown_system (pCuuconf, zcopy, qfromsys)) + ulog (LOG_FATAL, "%s: System not found", zcopy); + } + else if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, pCuuconf, iuuconf); + ubuffree (zcopy); + + zloc = qfromsys->uuconf_zlocalname; + if (zloc == NULL) + zloc = zClocalname; + + if (zforward == NULL && fClocaldest) + { + boolean fok; + + /* The file is to come directly from qfromsys to the local + system. */ + + /* Check that we have permission to receive into the desired + directory. If we don't have permission, uucico will + fail. */ + if (fCremote) + fok = fin_directory_list (zdest, + qfromsys->uuconf_pzremote_receive, + qfromsys->uuconf_zpubdir, TRUE, + FALSE, (const char *) NULL); + else + fok = fin_directory_list (zdest, + qfromsys->uuconf_pzlocal_receive, + qfromsys->uuconf_zpubdir, TRUE, + FALSE, zCuser); + if (! fok) + ulog (LOG_FATAL, "Not permitted to receive to %s", zdest); + + /* If the remote filespec is wildcarded, we must generate an + 'X' request. We currently check for Unix shell + wildcards. Note that it should do no harm to mistake a + non-wildcard for a wildcard. */ + if (zfrom[strcspn (zfrom, "*?[")] != '\0') + { + s.bcmd = 'X'; + zto = zbufalc (strlen (zloc) + strlen (zdest) + sizeof "!"); + sprintf (zto, "%s!%s", zloc, zdest); + } + else + { + s.bcmd = 'R'; + zto = zbufcpy (zdest); + } + + s.pseq = NULL; + s.zfrom = zfrom; + s.zto = zto; + s.zuser = zCuser; + s.zoptions = abCrec_options; + s.ztemp = ""; + s.imode = 0; + s.znotify = ""; + s.cbytes = -1; + s.zcmd = NULL; + s.ipos = 0; + + ucadd_cmd (qfromsys, &s, (const char *) NULL); + } + else + { + char *zxqt; + char abtname[CFILE_NAME_LEN]; + char abxname[CFILE_NAME_LEN]; + FILE *e; + char *zcmd; + char *zlog; + + /* The file either comes from some other system through + qfromsys or is intended for some other system. Send an + execution request to qfromsys to handle everything. */ + zxqt = zsysdep_data_file_name (qfromsys, zloc, bCgrade, TRUE, + abtname, (char *) NULL, + abxname); + if (zxqt == NULL) + ucabort (); + e = esysdep_fopen (zxqt, FALSE, FALSE, TRUE); + if (e == NULL) + ucabort (); + ucrecord_file (zxqt); + + fprintf (e, "U %s %s\n", zCuser, zloc); + fprintf (e, "C uucp -C"); + if (fCmkdirs) + fprintf (e, " -d"); + else + fprintf (e, " -f"); + fprintf (e, " -g %c", bCgrade); + if (fCmail) + fprintf (e, " -m"); + if (*zCnotify != '\0') + fprintf (e, " -n %s", zCnotify); + if (! fCexpand) + fprintf (e, " -W"); + + clen = (strlen (zfrom) + strlen (zloc) + + strlen (sCdestsys.uuconf_zname) + strlen (zdest)); + if (zforward != NULL) + clen += strlen (zforward); + if (zCforward != NULL) + clen += strlen (zCforward); + zcmd = zbufalc (sizeof "! !!!" + clen); + *zcmd = '\0'; + if (zforward != NULL) + sprintf (zcmd + strlen (zcmd), "%s!", zforward); + sprintf (zcmd + strlen (zcmd), "%s %s!", zfrom, zloc); + if (! fClocaldest) + sprintf (zcmd + strlen (zcmd), "%s!", sCdestsys.uuconf_zname); + if (zCforward != NULL) + sprintf (zcmd + strlen (zcmd), "%s!", zCforward); + sprintf (zcmd + strlen (zcmd), "%s", zdest); + + fprintf (e, " %s\n", zcmd); + + if (fclose (e) != 0) + ulog (LOG_FATAL, "fclose: %s", strerror (errno)); + + /* Send the execution file. */ + s.bcmd = 'S'; + s.pseq = NULL; + s.zfrom = zbufcpy (abtname); + s.zto = zbufcpy (abxname); + s.zuser = zCuser; + s.zoptions = "C"; + s.ztemp = s.zfrom; + s.imode = 0666; + s.znotify = NULL; + s.cbytes = -1; + s.zcmd = NULL; + s.ipos = 0; + + zlog = zbufalc (sizeof "Queueing uucp " + strlen (zcmd)); + sprintf (zlog, "Queueing uucp %s", zcmd); + + ucadd_cmd (qfromsys, &s, zlog); + + ubuffree (zcmd); + ubuffree (zforward); + } + } +} + +/* We keep a list of jobs for each system. */ + +struct sjob +{ + struct sjob *qnext; + const struct uuconf_system *qsys; + int ccmds; + struct scmd *pascmds; + const char **pazlogs; +}; + +static struct sjob *qCjobs; + +static void +ucadd_cmd (qsys, qcmd, zlog) + const struct uuconf_system *qsys; + const struct scmd *qcmd; + const char *zlog; +{ + struct sjob *qjob; + + if (! qsys->uuconf_fcall_transfer + && ! qsys->uuconf_fcalled_transfer) + ulog (LOG_FATAL, "Not permitted to transfer files to or from %s", + qsys->uuconf_zname); + + for (qjob = qCjobs; qjob != NULL; qjob = qjob->qnext) + if (strcmp (qjob->qsys->uuconf_zname, qsys->uuconf_zname) == 0) + break; + + if (qjob == NULL) + { + qjob = (struct sjob *) xmalloc (sizeof (struct sjob)); + qjob->qnext = qCjobs; + qjob->qsys = qsys; + qjob->ccmds = 0; + qjob->pascmds = NULL; + qjob->pazlogs = NULL; + qCjobs = qjob; + } + + qjob->pascmds = ((struct scmd *) + xrealloc ((pointer) qjob->pascmds, + (qjob->ccmds + 1) * sizeof (struct scmd))); + qjob->pascmds[qjob->ccmds] = *qcmd; + qjob->pazlogs = ((const char **) + xrealloc ((pointer) qjob->pazlogs, + (qjob->ccmds + 1) * sizeof (const char *))); + qjob->pazlogs[qjob->ccmds] = zlog; + ++qjob->ccmds; +} + +static void +ucspool_cmds (fjobid) + boolean fjobid; +{ + struct sjob *qjob; + char *zjobid; + + for (qjob = qCjobs; qjob != NULL; qjob = qjob->qnext) + { + ulog_system (qjob->qsys->uuconf_zname); + zjobid = zsysdep_spool_commands (qjob->qsys, bCgrade, qjob->ccmds, + qjob->pascmds); + if (zjobid != NULL) + { + int i; + struct scmd *qcmd; + const char **pz; + + for (i = 0, qcmd = qjob->pascmds, pz = qjob->pazlogs; + i < qjob->ccmds; + i++, qcmd++, pz++) + { + if (*pz != NULL) + { + if (**pz != '\0') + ulog (LOG_NORMAL, "%s", *pz); + } + else if (qcmd->bcmd == 'S') + ulog (LOG_NORMAL, "Queuing send of %s to %s", + qcmd->zfrom, qcmd->zto); + else if (qcmd->bcmd == 'R') + ulog (LOG_NORMAL, "Queuing request of %s to %s", + qcmd->zfrom, qcmd->zto); + else + { + const char *zto; + + zto = strrchr (qcmd->zto, '!'); + if (zto != NULL) + ++zto; + else + zto = qcmd->zto; + ulog (LOG_NORMAL, "Queuing request of %s to %s", + qcmd->zfrom, zto); + } + } + + if (fjobid) + printf ("%s\n", zjobid); + + ubuffree (zjobid); + } + } +} + +/* Return the system name for which we have created commands, or NULL + if we've created commands for more than one system. Set *pfany to + FALSE if we didn't create work for any system. */ + +static const char * +zcone_system (pfany) + boolean *pfany; +{ + if (qCjobs == NULL) + { + *pfany = FALSE; + return NULL; + } + + *pfany = TRUE; + + if (qCjobs->qnext == NULL) + return qCjobs->qsys->uuconf_zname; + else + return NULL; +} + +/* Keep track of all files we have created so that we can delete them + if we get a signal. The argument will be on the heap. */ + +static int cCfiles; +static const char **pCaz; + +static void +ucrecord_file (zfile) + const char *zfile; +{ + pCaz = (const char **) xrealloc ((pointer) pCaz, + (cCfiles + 1) * sizeof (const char *)); + pCaz[cCfiles] = zfile; + ++cCfiles; +} + +/* Delete all the files we have recorded and exit. */ + +static void +ucabort () +{ + int i; + + for (i = 0; i < cCfiles; i++) + (void) remove (pCaz[i]); + ulog_close (); + usysdep_exit (FALSE); +} diff --git a/gnu/libexec/uucp/uulog/Makefile b/gnu/libexec/uucp/uulog/Makefile new file mode 100644 index 0000000000..321656f3dc --- /dev/null +++ b/gnu/libexec/uucp/uulog/Makefile @@ -0,0 +1,16 @@ +# Makefile for uulog +# $Id: Makefile,v 1.2 1993/08/05 16:15:14 jtc Exp $ + +BINDIR= $(bindir) + +PROG= uulog +SRCS= uulog.c log.c +LDADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +DPADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +CFLAGS+= -I$(.CURDIR)/../common_sources\ + -DVERSION=\"$(VERSION)\" + +NOMAN= noman + +.include +.PATH: $(.CURDIR)/../common_sources diff --git a/gnu/libexec/uucp/uulog/uulog.c b/gnu/libexec/uucp/uulog/uulog.c new file mode 100644 index 0000000000..9a58fffeb2 --- /dev/null +++ b/gnu/libexec/uucp/uulog/uulog.c @@ -0,0 +1,444 @@ +/* uulog.c + Display the UUCP log file. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char uulog_rcsid[] = "$Id: uulog.c,v 1.1 1993/08/04 19:36:47 jtc Exp $"; +#endif + +#include +#include + +#include "getopt.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" + +/* This is a pretty bad implementation of uulog, which I don't think + is a very useful program anyhow. It only takes a single -s and/or + -u switch. When using HAVE_HDB_LOGGING it requires a system. */ + +/* The program name. */ +char abProgram[] = "uulog"; + +/* Local functions. */ + +static void ulusage P((void)); + +/* Long getopt options. */ +static const struct option asLlongopts[] = { { NULL, 0, NULL, 0 } }; + +int +main (argc, argv) + int argc; + char **argv; +{ + /* -D: display Debug file */ + boolean fdebug = FALSE; + /* -f: keep displaying lines forever. */ + boolean fforever = FALSE; + /* -n lines: number of lines to display. */ + int cshow = 0; + /* -s: system name. */ + const char *zsystem = NULL; + /* -S: display Stats file */ + boolean fstats = FALSE; + /* -u: user name. */ + const char *zuser = NULL; + /* -I: configuration file name. */ + const char *zconfig = NULL; + /* -x: display uuxqt log file. */ + boolean fuuxqt = FALSE; + int i; + int iopt; + pointer puuconf; + int iuuconf; + const char *zlogfile; + const char *zstatsfile; + const char *zdebugfile; + const char *zfile; + FILE *e; + char **pzshow = NULL; + int ishow = 0; + size_t csystem = 0; + size_t cuser = 0; + char *zline; + size_t cline; + + /* Look for a straight number argument, and convert it to -n before + passing the arguments to getopt. */ + for (i = 0; i < argc; i++) + { + if (argv[i][0] == '-' && isdigit (argv[i][1])) + { + size_t clen; + char *znew; + + clen = strlen (argv[i]); + znew = zbufalc (clen + 2); + znew[0] = '-'; + znew[1] = 'n'; + memcpy (znew + 2, argv[i] + 1, clen); + argv[i] = znew; + } + } + + while ((iopt = getopt_long (argc, argv, "Df:FI:n:s:Su:xX:", asLlongopts, + (int *) NULL)) != EOF) + { + switch (iopt) + { + case 'D': + /* Show debugging file. */ + fdebug = TRUE; + break; + + case 'f': + /* Keep displaying lines forever for a particular system. */ + fforever = TRUE; + zsystem = optarg; + if (cshow == 0) + cshow = 10; + break; + + case 'F': + /* Keep displaying lines forever. */ + fforever = TRUE; + if (cshow == 0) + cshow = 10; + break; + + case 'I': + /* Configuration file name. */ + if (fsysdep_other_config (optarg)) + zconfig = optarg; + break; + + case 'n': + /* Number of lines to display. */ + cshow = (int) strtol (optarg, (char **) NULL, 10); + break; + + case 's': + /* System name. */ + zsystem = optarg; + break; + + case 'S': + /* Show statistics file. */ + fstats = TRUE; + break; + + case 'u': + /* User name. */ + zuser = optarg; + break; + + case 'x': + /* Display uuxqt log file. */ + fuuxqt = TRUE; + break; + + case 'X': +#if DEBUG > 1 + /* Set debugging level. */ + iDebug |= idebug_parse (optarg); +#endif + break; + + case 0: + /* Long option found and flag set. */ + break; + + default: + ulusage (); + break; + } + } + + if (optind != argc || (fstats && fdebug)) + ulusage (); + + iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + +#if DEBUG > 1 + { + const char *zdebug; + + iuuconf = uuconf_debuglevel (puuconf, &zdebug); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + if (zdebug != NULL) + iDebug |= idebug_parse (zdebug); + } +#endif + + iuuconf = uuconf_logfile (puuconf, &zlogfile); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + iuuconf = uuconf_statsfile (puuconf, &zstatsfile); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + iuuconf = uuconf_debugfile (puuconf, &zdebugfile); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + usysdep_initialize (puuconf, 0); + + if (zsystem != NULL) + { +#if HAVE_HDB_LOGGING + if (strcmp (zsystem, "ANY") != 0) +#endif + { + struct uuconf_system ssys; + + iuuconf = uuconf_system_info (puuconf, zsystem, &ssys); + if (iuuconf != UUCONF_SUCCESS) + { + if (iuuconf != UUCONF_NOT_FOUND) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + ulog (LOG_FATAL, "%s: System not found", zsystem); + } + zsystem = zbufcpy (ssys.uuconf_zname); + (void) uuconf_system_free (puuconf, &ssys); + } + } + + if (fstats) + zfile = zstatsfile; + else if (fdebug) + zfile = zdebugfile; + else + { +#if ! HAVE_HDB_LOGGING + zfile = zlogfile; +#else + const char *zprogram; + char *zalc; + + /* We need a system to find a HDB log file. */ + if (zsystem == NULL) + ulusage (); + + if (fuuxqt) + zprogram = "uuxqt"; + else + zprogram = "uucico"; + + zalc = zbufalc (strlen (zlogfile) + + strlen (zprogram) + + strlen (zsystem) + + 1); + sprintf (zalc, zlogfile, zprogram, zsystem); + zfile = zalc; + + if (strcmp (zsystem, "ANY") == 0) + zsystem = NULL; +#endif + } + + e = fopen (zfile, "r"); + if (e == NULL) + { + ulog (LOG_ERROR, "fopen (%s): %s", zfile, strerror (errno)); + usysdep_exit (FALSE); + } + + if (cshow > 0) + { + pzshow = (char **) xmalloc (cshow * sizeof (char *)); + for (ishow = 0; ishow < cshow; ishow++) + pzshow[ishow] = NULL; + ishow = 0; + } + + /* Read the log file and output the appropriate lines. */ + if (zsystem != NULL) + csystem = strlen (zsystem); + + if (zuser != NULL) + cuser = strlen (zuser); + + zline = NULL; + cline = 0; + + while (TRUE) + { + while (getline (&zline, &cline, e) > 0) + { + char *zluser, *zlsys, *znext; + size_t cluser, clsys; + + /* Skip any leading whitespace (not that there should be + any). */ + znext = zline + strspn (zline, " \t"); + + if (! fstats) + { +#if ! HAVE_TAYLOR_LOGGING + /* The user name is the first field on the line. */ + zluser = znext; + cluser = strcspn (znext, " \t"); +#endif + + /* Skip the first field. */ + znext += strcspn (znext, " \t"); + znext += strspn (znext, " \t"); + + /* The system is the second field on the line. */ + zlsys = znext; + clsys = strcspn (znext, " \t"); + + /* Skip the second field. */ + znext += clsys; + znext += strspn (znext, " \t"); + +#if HAVE_TAYLOR_LOGGING + /* The user is the third field on the line. */ + zluser = znext; + cluser = strcspn (znext, " \t"); +#endif + } + else + { +#if ! HAVE_HDB_LOGGING + /* The user name is the first field on the line, and the + system name is the second. */ + zluser = znext; + cluser = strcspn (znext, " \t"); + znext += cluser; + znext += strspn (znext, " \t"); + zlsys = znext; + clsys = strcspn (znext, " \t"); +#else + /* The first field is system!user. */ + zlsys = znext; + clsys = strcspn (znext, "!"); + znext += clsys + 1; + zlsys = znext; + clsys = strcspn (znext, " \t"); +#endif + } + + /* See if we should print this line. */ + if (zsystem != NULL + && (csystem != clsys + || strncmp (zsystem, zlsys, clsys) != 0)) + continue; + + if (zuser != NULL + && (cuser != cluser + || strncmp (zuser, zluser, cluser) != 0)) + continue; + + /* Output the line, or save it if we are outputting only a + particular number of lines. */ + if (cshow <= 0) + printf ("%s", zline); + else + { + ubuffree ((pointer) pzshow[ishow]); + pzshow[ishow] = zbufcpy (zline); + ishow = (ishow + 1) % cshow; + } + } + + /* Output the number of lines requested by the -n option. */ + if (cshow > 0) + { + for (i = 0; i < cshow; i++) + { + if (pzshow[ishow] != NULL) + printf ("%s", pzshow[ishow]); + ishow = (ishow + 1) % cshow; + } + } + + /* If -f was not specified, or an error occurred while reading + the file, get out. */ + if (! fforever || ferror (e)) + break; + + clearerr (e); + cshow = 0; + + /* Sleep 1 second before going around the loop again. */ + usysdep_sleep (1); + } + + (void) fclose (e); + + ulog_close (); + + usysdep_exit (TRUE); + + /* Avoid errors about not returning a value. */ + return 0; +} + +/* Print a usage message and die. */ + +static void +ulusage () +{ + fprintf (stderr, + "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n", + VERSION); + fprintf (stderr, + "Usage: uulog [-n #] [-sf system] [-u user] [-xDSF] [-I file] [-X debug]\n"); + fprintf (stderr, + " -n: show given number of lines from end of log\n"); + fprintf (stderr, + " -s: print entries for named system\n"); + fprintf (stderr, + " -f: follow entries for named system\n"); + fprintf (stderr, + " -u: print entries for named user\n"); +#if HAVE_HDB_LOGGING + fprintf (stderr, + " -x: print uuxqt log rather than uucico log\n"); +#else + fprintf (stderr, + " -F: follow entries for any system\n"); +#endif + fprintf (stderr, + " -S: show statistics file\n"); + fprintf (stderr, + " -D: show debugging file\n"); + fprintf (stderr, + " -X debug: Set debugging level (0 for none, 9 is max)\n"); +#if HAVE_TAYLOR_CONFIG + fprintf (stderr, + " -I file: Set configuration file to use\n"); +#endif /* HAVE_TAYLOR_CONFIG */ + exit (EXIT_FAILURE); +} diff --git a/gnu/libexec/uucp/uuname/Makefile b/gnu/libexec/uucp/uuname/Makefile new file mode 100644 index 0000000000..5e7b314ba1 --- /dev/null +++ b/gnu/libexec/uucp/uuname/Makefile @@ -0,0 +1,18 @@ +# Makefile for uuname +# $Id: Makefile,v 1.2 1993/08/05 16:15:16 jtc Exp $ + +BINDIR= $(bindir) +BINOWN= $(owner) +BINMODE= 4555 + +PROG= uuname +SRCS= uuname.c log.c +LDADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +DPADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +CFLAGS+= -I$(.CURDIR)/../common_sources\ + -DVERSION=\"$(VERSION)\" + +NOMAN= noman + +.include +.PATH: $(.CURDIR)/../common_sources diff --git a/gnu/libexec/uucp/uuname/uuname.c b/gnu/libexec/uucp/uuname/uuname.c new file mode 100644 index 0000000000..54a87dfa39 --- /dev/null +++ b/gnu/libexec/uucp/uuname/uuname.c @@ -0,0 +1,192 @@ +/* uuname.c + List the names of known remote UUCP sites. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char uuname_rcsid[] = "$Id: uuname.c,v 1.1 1993/08/04 19:36:52 jtc Exp $"; +#endif + +#include "getopt.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" + +/* The program name. */ +char abProgram[] = "uuname"; + +/* Local functions. */ + +static void unusage P((void)); +static void unuuconf_error P((pointer puuconf, int iuuconf)); + +/* Long getopt options. */ +static const struct option asNlongopts[] = { { NULL, 0, NULL, 0 } }; + +int +main (argc, argv) + int argc; + char **argv; +{ + /* -a: display aliases. */ + boolean falias = FALSE; + /* -l: if true, output local node name. */ + boolean flocal = FALSE; + /* -I: configuration file name. */ + const char *zconfig = NULL; + int iopt; + pointer puuconf; + int iuuconf; + + while ((iopt = getopt_long (argc, argv, "alI:x:", asNlongopts, + (int *) NULL)) != EOF) + { + switch (iopt) + { + case 'a': + /* Display aliases. */ + falias = TRUE; + break; + + case 'l': + /* Output local node name. */ + flocal = TRUE; + break; + + case 'I': + /* Configuration file name. */ + if (fsysdep_other_config (optarg)) + zconfig = optarg; + break; + + case 'x': +#if DEBUG > 1 + /* Set debugging level. */ + iDebug |= idebug_parse (optarg); +#endif + break; + + case 0: + /* Long option found and flag set. */ + break; + + default: + unusage (); + break; + } + } + + if (optind != argc) + unusage (); + + iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig); + if (iuuconf != UUCONF_SUCCESS) + unuuconf_error (puuconf, iuuconf); + +#if DEBUG > 1 + { + const char *zdebug; + + iuuconf = uuconf_debuglevel (puuconf, &zdebug); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + if (zdebug != NULL) + iDebug |= idebug_parse (zdebug); + } +#endif + + usysdep_initialize (puuconf, INIT_SUID); + + if (flocal) + { + const char *zlocalname; + + iuuconf = uuconf_localname (puuconf, &zlocalname); + if (iuuconf == UUCONF_NOT_FOUND) + { + zlocalname = zsysdep_localname (); + if (zlocalname == NULL) + usysdep_exit (FALSE); + } + else if (iuuconf != UUCONF_SUCCESS) + unuuconf_error (puuconf, iuuconf); + printf ("%s\n", zlocalname); + } + else + { + char **pznames, **pz; + + iuuconf = uuconf_system_names (puuconf, &pznames, falias); + if (iuuconf != UUCONF_SUCCESS) + unuuconf_error (puuconf, iuuconf); + + for (pz = pznames; *pz != NULL; pz++) + printf ("%s\n", *pz); + } + + usysdep_exit (TRUE); + + /* Avoid warnings about not returning a value. */ + return 0; +} + +/* Print a usage message and die. */ + +static void +unusage () +{ + fprintf (stderr, + "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n", + VERSION); + fprintf (stderr, + "Usage: uuname [-a] [-l] [-I file]\n"); + fprintf (stderr, + " -a: display aliases\n"); + fprintf (stderr, + " -l: print local name\n"); +#if HAVE_TAYLOR_CONFIG + fprintf (stderr, + " -I file: Set configuration file to use\n"); +#endif /* HAVE_TAYLOR_CONFIG */ + exit (EXIT_FAILURE); +} + +/* Display a uuconf error and exit. */ + +static void +unuuconf_error (puuconf, iret) + pointer puuconf; + int iret; +{ + char ab[512]; + + (void) uuconf_error_string (puuconf, iret, ab, sizeof ab); + if ((iret & UUCONF_ERROR_FILENAME) == 0) + fprintf (stderr, "uuname: %s\n", ab); + else + fprintf (stderr, "uuname:%s\n", ab); + exit (EXIT_FAILURE); +} diff --git a/gnu/libexec/uucp/uupick/Makefile b/gnu/libexec/uucp/uupick/Makefile new file mode 100644 index 0000000000..c9a431219d --- /dev/null +++ b/gnu/libexec/uucp/uupick/Makefile @@ -0,0 +1,16 @@ +# Makefile for uupick +# $Id: Makefile,v 1.2 1993/08/05 16:15:19 jtc Exp $ + +BINDIR= $(bindir) + +PROG= uupick +SRCS= uupick.c log.c copy.c +LDADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +DPADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +CFLAGS+= -I$(.CURDIR)/../common_sources\ + -DVERSION=\"$(VERSION)\" + +NOMAN= noman + +.include +.PATH: $(.CURDIR)/../common_sources diff --git a/gnu/libexec/uucp/uupick/uupick.c b/gnu/libexec/uucp/uupick/uupick.c new file mode 100644 index 0000000000..4e1f1b3aff --- /dev/null +++ b/gnu/libexec/uucp/uupick/uupick.c @@ -0,0 +1,323 @@ +/* uupick.c + Get files stored in the public directory by uucp -t. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char uupick_rcsid[] = "$Id: uupick.c,v 1.1 1993/08/04 19:36:56 jtc Exp $"; +#endif + +#include + +#include "getopt.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" + +/* Local functions. */ + +static void upmovedir P((const char *zfull, const char *zrelative, + pointer pinfo)); +static void upmove P((const char *zfrom, const char *zto)); + +/* The program name. */ +char abProgram[] = "uupick"; + +/* Long getopt options. */ +static const struct option asPlongopts[] = { { NULL, 0, NULL, 0 } }; + +/* Local functions. */ + +static void upusage P((void)); + +int +main (argc, argv) + int argc; + char **argv; +{ + /* -s: system name. */ + const char *zsystem = NULL; + /* -I: configuration file name. */ + const char *zconfig = NULL; + int iopt; + pointer puuconf; + int iuuconf; + struct uuconf_system ssys; + const char *zpubdir; + char *zfile, *zfrom, *zfull; + char *zallsys; + char ab[1000]; + boolean fquit; + + while ((iopt = getopt_long (argc, argv, "I:s:x:", asPlongopts, + (int *) NULL)) != EOF) + { + switch (iopt) + { + case 's': + /* System name to get files from. */ + zsystem = optarg; + break; + + case 'I': + /* Name configuration file. */ + if (fsysdep_other_config (optarg)) + zconfig = optarg; + break; + + case 'x': +#if DEBUG > 1 + /* Set debugging level. */ + iDebug |= idebug_parse (optarg); +#endif + break; + + case 0: + /* Long option found and flag set. */ + break; + + default: + upusage (); + break; + } + } + + if (argc != optind) + upusage (); + + iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + usysdep_initialize (puuconf, INIT_GETCWD | INIT_NOCHDIR); + + zpubdir = NULL; + if (zsystem != NULL) + { + iuuconf = uuconf_system_info (puuconf, zsystem, &ssys); + if (iuuconf == UUCONF_SUCCESS) + { + zpubdir = zbufcpy (ssys.uuconf_zpubdir); + (void) uuconf_system_free (puuconf, &ssys); + } + else if (iuuconf != UUCONF_NOT_FOUND) + (void) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + } + if (zpubdir == NULL) + { + iuuconf = uuconf_pubdir (puuconf, &zpubdir); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + } + + if (! fsysdep_uupick_init (zsystem, zpubdir)) + usysdep_exit (FALSE); + + zallsys = NULL; + fquit = FALSE; + + while (! fquit + && ((zfile = zsysdep_uupick (zsystem, zpubdir, &zfrom, &zfull)) + != NULL)) + { + boolean fdir; + char *zto, *zlocal; + FILE *e; + boolean fcontinue; + + fdir = fsysdep_directory (zfull); + + do + { + fcontinue = FALSE; + + if (zallsys == NULL + || strcmp (zallsys, zfrom) != 0) + { + if (zallsys != NULL) + { + ubuffree (zallsys); + zallsys = NULL; + } + + printf ("from %s: %s %s ?\n", zfrom, fdir ? "dir" : "file", + zfile); + + if (fgets (ab, sizeof ab, stdin) == NULL) + break; + } + + if (ab[0] == 'q') + { + fquit = TRUE; + break; + } + + switch (ab[0]) + { + case '\n': + break; + + case 'd': + if (fdir) + (void) fsysdep_rmdir (zfull); + else + { + if (remove (zfull) != 0) + ulog (LOG_ERROR, "remove (%s): %s", zfull, + strerror(errno)); + } + break; + + case 'm': + case 'a': + zto = ab + 1 + strspn (ab + 1, " \t"); + zto[strcspn (zto, " \t\n")] = '\0'; + zlocal = zsysdep_uupick_local_file (zto); + if (zlocal == NULL) + usysdep_exit (FALSE); + zto = zsysdep_in_dir (zlocal, zfile); + ubuffree (zlocal); + if (zto == NULL) + usysdep_exit (FALSE); + if (! fdir) + upmove (zfull, zto); + else + { + usysdep_walk_tree (zfull, upmovedir, (pointer) zto); + (void) fsysdep_rmdir (zfull); + } + ubuffree (zto); + + if (ab[0] == 'a') + { + zallsys = zbufcpy (zfrom); + ab[0] = 'm'; + } + + break; + + case 'p': + if (fdir) + ulog (LOG_ERROR, "Can't print directory"); + else + { + e = fopen (zfull, "r"); + if (e == NULL) + ulog (LOG_ERROR, "fopen (%s): %s", zfull, + strerror (errno)); + else + { + while (fgets (ab, sizeof ab, e) != NULL) + (void) fputs (ab, stdout); + (void) fclose (e); + } + } + fcontinue = TRUE; + break; + + case '!': + (void) system (ab + 1); + fcontinue = TRUE; + break; + + default: + printf ("uupick commands:\n"); + printf ("q: quit\n"); + printf (": skip file\n"); + printf ("m [dir]: move file to directory\n"); + printf ("a [dir]: move all files from this system to directory\n"); + printf ("p: list file to stdout\n"); + printf ("! command: shell escape\n"); + fcontinue = TRUE; + break; + } + } + while (fcontinue); + + ubuffree (zfull); + ubuffree (zfrom); + ubuffree (zfile); + } + + (void) fsysdep_uupick_free (zsystem, zpubdir); + + usysdep_exit (TRUE); + + /* Avoid error about not returning. */ + return 0; +} + +/* Print usage message. */ + +static void +upusage () +{ + fprintf (stderr, + "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n", + VERSION); + fprintf (stderr, + "Usage: uupick [-s system] [-I config] [-x debug]\n"); + fprintf (stderr, + " -s system: Only consider files from named system\n"); + fprintf (stderr, + " -x debug: Set debugging level\n"); +#if HAVE_TAYLOR_CONFIG + fprintf (stderr, + " -I file: Set configuration file to use\n"); +#endif /* HAVE_TAYLOR_CONFIG */ + exit (EXIT_FAILURE); +} + +/* This routine is called by usysdep_walk_tree when moving the + contents of an entire directory. */ + +static void +upmovedir (zfull, zrelative, pinfo) + const char *zfull; + const char *zrelative; + pointer pinfo; +{ + const char *ztodir = (const char *) pinfo; + char *zto; + + zto = zsysdep_in_dir (ztodir, zrelative); + if (zto == NULL) + usysdep_exit (FALSE); + upmove (zfull, zto); + ubuffree (zto); +} + +/* Move a file. */ + +static void +upmove (zfrom, zto) + const char *zfrom; + const char *zto; +{ + (void) fsysdep_move_file (zfrom, zto, TRUE, TRUE, FALSE, + (const char *) NULL); +} diff --git a/gnu/libexec/uucp/uusched/Makefile b/gnu/libexec/uucp/uusched/Makefile new file mode 100644 index 0000000000..6135e5380e --- /dev/null +++ b/gnu/libexec/uucp/uusched/Makefile @@ -0,0 +1,18 @@ +# Makefile for uusched +# $Id: Makefile,v 1.1 1993/08/05 18:27:55 conklin Exp $ + +BINDIR= $(bindir) + +PROG= uusched +SRCS= +NOMAN= +STRIP= + +uusched: uusched.in + sed -e "s|@BINDIR@|$(bindir)|g" -e "s|@SBINDIR@|$(sbindir)|g" \ + $(.CURDIR)/uusched.in > $(.TARGET) + +depend: + + +.include diff --git a/gnu/libexec/uucp/uusched/uusched.in b/gnu/libexec/uucp/uusched/uusched.in new file mode 100644 index 0000000000..e539b902fb --- /dev/null +++ b/gnu/libexec/uucp/uusched/uusched.in @@ -0,0 +1,13 @@ +: +# uusched +# Call all systems which have work in a random order +# +# Copyright (C) 1992 Ian Lance Taylor +# +# Please feel free do whatever you like with this exciting shell +# script. +# +# This is pretty trivial, since all the functionality was moved into +# uucico itself. +# +@SBINDIR@/uucico -r1 $* diff --git a/gnu/libexec/uucp/uustat/Makefile b/gnu/libexec/uucp/uustat/Makefile new file mode 100644 index 0000000000..7451292cea --- /dev/null +++ b/gnu/libexec/uucp/uustat/Makefile @@ -0,0 +1,17 @@ +# Makefile for uustat +# $Id: Makefile,v 1.2 1993/08/05 16:15:22 jtc Exp $ + +BINDIR= $(bindir) +BINOWN= $(owner) +BINMODE= 4555 + +PROG= uustat +SRCS= uustat.c util.c log.c copy.c +LDADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +DPADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +CFLAGS+= -I$(.CURDIR)/../common_sources\ + -DOWNER=\"$(owner)\"\ + -DVERSION=\"$(VERSION)\" + +.include +.PATH: $(.CURDIR)/../common_sources diff --git a/gnu/libexec/uucp/uustat/uustat.1 b/gnu/libexec/uucp/uustat/uustat.1 new file mode 100644 index 0000000000..b818521676 --- /dev/null +++ b/gnu/libexec/uucp/uustat/uustat.1 @@ -0,0 +1,380 @@ +''' $Id: uustat.1,v 1.1 1993/08/04 19:37:04 jtc Exp $ +.TH uustat 1 "Taylor UUCP 1.04" +.SH NAME +uustat \- UUCP status inquiry and control +.SH SYNOPSIS +.B uustat \-a +.PP +.B uustat +[ +.B \-eKiMNQ ] [ +.B \-sS +system ] [ +.B \-uU +user ] [ +.B \-cC +command ] [ +.B \-o +hours ] [ +.B \-y +hours ] [ +.B \-B +lines ] +.PP +.B uustat +[ +.B \-k +jobid ] [ +.B \-r +jobid ] +.PP +.B uustat \-q +.PP +.B uustat \-m +.PP +.B uustat \-p +.SH DESCRIPTION +The +.I uustat +command can display various types of status information about the UUCP +system. It can also be used to cancel or rejuvenate requests made by +.I uucp +(1) or +.I uux +(1). + +By default +.I uustat +displays all jobs queued up for the invoking user, as if given the +.B \-u +option with the appropriate argument. + +If any of the +.B \-a, +.B \-e, +.B \-s, +.B \-S, +.B \-u, +.B \-U, +.B \-c, +.B \-C, +.B \-o, +.B \-y +options are given, then all jobs which match the combined +specifications are displayed. + +The +.B \-K +option may be used to kill off a selected group of jobs, such as all +jobs more than 7 days old. +.SH OPTIONS +The following options may be given to +.I uustat. +.TP 5 +.B \-a +List all queued file transfer requests. +.TP 5 +.B \-e +List queued execution requests rather than queued file transfer +requests. Queued execution requests are processed by +.I uuxqt +(8) rather than +.I uucico +(8). Queued execution requests may be waiting for some file to be +transferred from a remote system. They are created by an invocation +of +.I uux +(1). +.TP 5 +.B \-s system +List all jobs queued up for the named system. This option may be +specified multiple times, in which case all jobs for all the systems +will be listed. +.TP 5 +.B \-S system +List all jobs queued for systems other than the one named. This +option may be specified multiple times, in which case no jobs from any +of the specified systems will be listed. This option may not be used +with +.B \-s. +.TP 5 +.B \-u user +List all jobs queued up for the named user. This option may be +specified multiple times, in which case all jobs for all the users +will be listed. +.TP 5 +.B \-U user +List all jobs queued up for users other than the one named. This +option may be specified multiple times, in which case no jobs from any +of the specified users will be listed. This option may not be used +with +.B \-u. +.TP 5 +.B \-c command +List all jobs requesting the execution of the named command. If +.B command +is +.I ALL +this will list all jobs requesting the execution of some command (as +opposed to simply requesting a file transfer). This option may be +specified multiple times, in which case all jobs requesting any of the +commands will be listed. +.TP 5 +.B \-C command +List all jobs requesting execution of some command other than the +named command, or, if +.B command +is +.I ALL, +list all jobs that simply request a file transfer (as opposed to +requesting the execution of some command). This option may be +specified multiple times, in which case no job requesting one of the +specified commands will be listed. This option may not be used with +.B \-c. +.TP 5 +.B \-o hours +List all queued jobs older than the given number of hours. +.TP 5 +.B \-y hours +List all queued jobs younger than the given number of hours. +.TP 5 +.B \-k jobid +Kill the named job. The job id is shown by the default output format, +as well as by the +.B \-j +option to +.I uucp +(1) or +.I uux +(1). A job may only be killed by the user who created the job, or by +the UUCP administrator or the superuser. The +.B \-k +option may be used multiple times on the command line to kill several +jobs. +.TP 5 +.B \-r jobid +Rejuvenate the named job. This will mark it as having been invoked at +the current time, affecting the output of the +.B \-o +or +.B \-y +options and preserving it from any automated cleanup daemon. The job +id is shown by the default output format, as well as by the +.B \-j +option to +.I uucp +(1) or +.I uux +(1). A job may only be rejuvenated by the user who created the job, +or by the UUCP administrator or the superuser. The +.B \-r +option may be used multiple times on the command line to rejuvenate +several jobs. +.TP 5 +.B \-q +Display the status of commands, executions and conversations for all +remote systems for which commands or executions are queued. +.TP 5 +.B \-m +Display the status of conversations for all remote systems. +.TP 5 +.B \-p +Display the status of all processes holding UUCP locks on systems or +ports. +.TP 5 +.B \-i +For each listed job, prompt whether to kill the job or not. If the +first character of the input line is +.I y +or +.I Y +the job will be killed. +.TP 5 +.B \-K +Automatically kill each listed job. This can be useful for automatic +cleanup scripts, in conjunction with the +.B \-M +and +.B \-N +options. +.TP 5 +.B \-M +For each listed job, send mail to the UUCP administrator. If the job +is killed (due to +.B \-K +or +.B \-i +with an affirmative response) the mail will indicate that. A comment +specified by the +.B \-W +option may be included. If the job is an execution, the initial +portion of its standard input will be included in the mail message; +the number of lines to include may be set with the +.B \-B +option (the default is 100). If the standard input contains null +characters, it is assumed to be a binary file and is not included. +.TP 5 +.B \-N +For each listed job, send mail to the user who requested the job. The +mail is identical to that sent by the +.B \-M +option. +.TP 5 +.B \-W +Specify a comment to be included in mail sent with the +.B \-M +or +.B \-N +options. +.TP 5 +.B \-Q +Do not actually list the job, but only take any actions indicated by +the +.B \-i, +.B \-K, +.B \-M, +.B \-N +options. +.TP 5 +.B \-x type +Turn on particular debugging types. The following types are +recognized: abnormal, chat, handshake, uucp-proto, proto, port, +config, spooldir, execute, incoming, outgoing. Only abnormal, config, +spooldir and execute are meaningful for +.I uustat. + +Multiple types may be given, separated by commas, and the +.B \-x +option may appear multiple times. A number may also be given, which +will turn on that many types from the foregoing list; for example, +.B \-x 2 +is equivalent to +.B \-x abnormal,chat. +.TP 5 +.B \-I file +Set configuration file to use. This option may not be available, +depending upon how +.I uustat +was compiled. +.SH EXAMPLES +.EX +uustat -a +.EE +Display status of all jobs. A sample output line is as follows: +.EX +bugsA027h bugs ian 04-01 13:50 Executing rmail ian@airs.com (sending 1283 bytes) +.EE +The format is +.EX +jobid system user queue-date command (size) +.EE +The jobid may be passed to the +.B \-k +or +.B \-r +options. +The size indicates how much data is to be transferred to the remote +system, and is absent for a file receive request. +The +.B \-s, +.B \-S, +.B \-u, +.B \-U, +.B \-c, +.B \-C, +.B \-o, +and +.B \-y +options may be used to control which jobs are listed. + +.EX +uustat -e +.EE +Display status of queued up execution requests. A sample output line +is as follows: +.EX +bugs bugs!ian 05-20 12:51 rmail ian +.EE +The format is +.EX +system requestor queue-date command +.EE +The +.B \-s, +.B \-S, +.B \-u, +.B \-U, +.B \-c, +.B \-C, +.B \-o, +and +.B \-y +options may be used to control which requests are listed. + +.EX +uustat -q +.EE +Display status for all systems with queued up commands. A sample +output line is as follows: +.EX +bugs 4C (1 hour) 0X (0 secs) 04-01 14:45 Dial failed +.EE +This indicates the system, the number of queued commands, the age of +the oldest queued command, the number of queued local executions, the +age of the oldest queued execution, the date of the last conversation, +and the status of that conversation. + +.EX +uustat -m +.EE +Display conversation status for all remote systems. A sample output +line is as follows: +.EX +bugs 04-01 15:51 Conversation complete +.EE +This indicates the system, the date of the last conversation, and the +status of that conversation. If the last conversation failed, +.I uustat +will indicate how many attempts have been made to call the system. If +the retry period is currently preventing calls to that system, +.I uustat +also displays the time when the next call will be permitted. + +.EX +uustat -p +.EE +Display the status of all processes holding UUCP locks. The output +format is system dependent, as +.I uustat +simply invokes +.I ps +(1) on each process holding a lock. + +.EX +uustat -c rmail -o 168 -K -Q -M -N -W"Queued for over 1 week" +.EE +This will kill all +.I rmail +commands that have been queued up waiting for delivery for over 1 week +(168 hours). For each such command, mail will be sent both to the +UUCP administrator and to the user who requested the rmail execution. +The mail message sent will include the string given by the +.B \-W +option. The +.B \-Q +option prevents any of the jobs from being listed on the terminal, so +any output from the program will be error messages. +.SH FILES +The file names may be changed at compilation time or by the +configuration file, so these are only approximations. + +.br +/usr/lib/uucp/config - Configuration file. +.br +/usr/spool/uucp - +UUCP spool directory. +.SH SEE ALSO +ps(1), rmail(1), uucp(1), uux(1), uucico(8), uuxqt(8) +.SH AUTHOR +Ian Lance Taylor +(ian@airs.com or uunet!airs!ian) diff --git a/gnu/libexec/uucp/uustat/uustat.c b/gnu/libexec/uucp/uustat/uustat.c new file mode 100644 index 0000000000..3c1352668c --- /dev/null +++ b/gnu/libexec/uucp/uustat/uustat.c @@ -0,0 +1,2241 @@ +/* uustat.c + UUCP status program + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char uustat_rcsid[] = "$Id: uustat.c,v 1.1 1993/08/04 19:37:05 jtc Exp $"; +#endif + +#include +#include + +#if HAVE_TIME_H +#include +#endif + +#include "getopt.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" + +/* The uustat program permits various listings and manipulations of + files in the spool directory. This implementation supports the + following switches: + + -a list all jobs + -Blines number of lines of standard input to mail + -ccommand list only executions of specified command + -Ccommand list only jobs other than executions of specified command + -e list execute jobs rather than command requests + -i ask user whether to kill each listed job + -Ifile set configuration file name + -kjobid kill job with specified ID + -K kill each listed job + -m report status for all remote machines + -M mail uucp about each job killed with -K + -N mail requestor about each job killed with -K + -ohour report jobs older than specified number of hours + -p do "ps -flp" on all processes holding lock files (Unix specific) + -q list number of jobs for all systems + -Q don't list jobs, just do -K processing + -rjobid rejuvenate job with specified ID + -ssystem report on all jobs for specified system + -Ssystem report on all jobs other than for specified system + -uuser report on all jobs for specified user + -Uuser report on all jobs other than for specified user + -Wcomment comment to include in mail messages + -xdebug set debugging level + -yhour report jobs younger than specified number of hours */ + +/* The program name. */ +char abProgram[] = "uustat"; + +/* What to do with a job that matches the selection criteria; these + values may be or'red together. */ +#define JOB_SHOW (01) +#define JOB_INQUIRE (02) +#define JOB_KILL (04) +#define JOB_MAIL (010) +#define JOB_NOTIFY (020) + +/* This structure is used to accumulate all the lines in a single + command file, so that they can all be displayed at once and so that + executions can be displayed reasonably. */ + +struct scmdlist +{ + struct scmdlist *qnext; + struct scmd s; + long itime; +}; + +/* Local functions. */ + +static void ususage P((void)); +static boolean fsxqt_file_read P((pointer puuconf, const char *zfile)); +static void usxqt_file_free P((void)); +static int isxqt_cmd P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int isxqt_file P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int isxqt_user P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); +static boolean fsworkfiles P((pointer puuconf, int icmd, int csystems, + char **pazsystems, boolean fnotsystems, + int cusers, char **pazusers, + boolean fnotusers, long iold, long iyoung, + int ccommands, char **pazcommands, + boolean fnotcommands, const char *zcomment, + int cstdin)); +static boolean fsworkfiles_system P((pointer puuconf,int icmd, + const struct uuconf_system *qsys, + int cusers, char **pazusers, + boolean fnotusers, long iold, + long iyoung, int ccommands, + char **pazcommands, + boolean fnotcommands, + const char *zcomment, int cstdin)); +static boolean fsworkfile_show P((pointer puuconf, int icmd, + const struct uuconf_system *qsys, + const struct scmd *qcmd, + long itime, int ccommands, + char **pazcommands, boolean fnotcommands, + const char *zcomment, int cstdin)); +static void usworkfile_header P((const struct uuconf_system *qsys, + const struct scmd *qcmd, + const char *zjobid, + long itime, boolean ffirst)); +static boolean fsexecutions P((pointer puuconf, int icmd, int csystems, + char **pazsystems, boolean fnotsystems, + int cusers, char **pazusers, + boolean fnotusers, long iold, long iyoung, + int ccommands, char **pazcommands, + boolean fnotcommands, const char *zcomment, + int cstdin)); +static boolean fsnotify P((pointer puuconf, int icmd, const char *zcomment, + int cstdin, boolean fkilled, const char *zcmd, + struct scmdlist *qcmd, const char *zid, + const char *zuser, + const struct uuconf_system *qsys, + const char *zstdin, pointer pstdinseq, + const char *zrequestor)); +static boolean fsquery P((pointer puuconf)); +static int csunits_show P((long idiff)); +static boolean fsmachines P((void)); + +/* Long getopt options. */ +static const struct option asSlongopts[] = { { NULL, 0, NULL, 0 } }; + +int +main (argc, argv) + int argc; + char **argv; +{ + /* -a: list all jobs. */ + boolean fall = FALSE; + /* -B lines: number of lines of standard input to mail. */ + int cstdin = 100; + /* -c,-C command: list only specified command. */ + int ccommands = 0; + char **pazcommands = NULL; + boolean fnotcommands = FALSE; + /* -e: list execute jobs. */ + boolean fexecute = FALSE; + /* -k jobid: kill specified job. */ + int ckills = 0; + char **pazkills = NULL; + /* -m: report machine status. */ + boolean fmachine = FALSE; + /* -o hour: report jobs older than given number of hours. */ + int ioldhours = -1; + /* -p: report status of jobs holding lock files. */ + boolean fps = FALSE; + /* -q: list number of jobs for each system. */ + boolean fquery = FALSE; + /* -r jobid: rejuvenate specified job. */ + int crejuvs = 0; + char **pazrejuvs = NULL; + /* -s,-S system: list all jobs for specified system. */ + int csystems = 0; + char **pazsystems = NULL; + boolean fnotsystems = FALSE; + /* -u,-U user: list all jobs for specified user. */ + int cusers = 0; + char **pazusers = NULL; + boolean fnotusers = FALSE; + /* -W comment: comment to include in mail messages. */ + const char *zcomment = NULL; + /* -y hour: report jobs younger than given number of hours. */ + int iyounghours = -1; + /* -I file: set configuration file. */ + const char *zconfig = NULL; + /* -Q, -i, -K, -M, -N: what to do with each job. */ + int icmd = JOB_SHOW; + int ccmds; + int iopt; + pointer puuconf; + int iuuconf; + long iold; + long iyoung; + const char *azoneuser[1]; + boolean fret; + + while ((iopt = getopt_long (argc, argv, + "aB:c:C:eiI:k:KmMNo:pqQr:s:S:u:U:W:x:y:", + asSlongopts, (int *) NULL)) != EOF) + { + switch (iopt) + { + case 'a': + /* List all jobs. */ + fall = TRUE; + break; + + case 'B': + /* Number of lines of standard input to mail. */ + cstdin = (int) strtol (optarg, (char **) NULL, 10); + break; + + case 'C': + /* List jobs for other than specified command. */ + fnotcommands = TRUE; + /* Fall through. */ + case 'c': + /* List specified command. */ + ++ccommands; + pazcommands = (char **) xrealloc ((pointer) pazcommands, + ccommands * sizeof (char *)); + pazcommands[ccommands - 1] = optarg; + break; + + case 'e': + /* List execute jobs. */ + fexecute = TRUE; + break; + + case 'i': + /* Prompt the user whether to kill each job. */ + icmd |= JOB_INQUIRE; + break; + + case 'I': + /* Set configuration file name. */ + if (fsysdep_other_config (optarg)) + zconfig = optarg; + break; + + case 'k': + /* Kill specified job. */ + ++ckills; + pazkills = (char **) xrealloc ((pointer) pazkills, + ckills * sizeof (char *)); + pazkills[ckills - 1] = optarg; + break; + + case 'K': + /* Kill each listed job. */ + icmd |= JOB_KILL; + break; + + case 'm': + /* Report machine status. */ + fmachine = TRUE; + break; + + case 'M': + /* Mail to uucp action taken on each job. */ + icmd |= JOB_MAIL; + break; + + case 'N': + /* Mail to requestor action taken on each job. */ + icmd |= JOB_NOTIFY; + break; + + case 'o': + /* Report old jobs. */ + ioldhours = (int) strtol (optarg, (char **) NULL, 10); + break; + + case 'p': + /* Get status of processes holding locks. */ + fps = TRUE; + break; + + case 'q': + /* List number of jobs for each system. */ + fquery = TRUE; + break; + + case 'Q': + /* Don't list jobs, just do -K processing. */ + icmd &=~ JOB_SHOW; + break; + + case 'r': + /* Rejuvenate specified job. */ + ++crejuvs; + pazrejuvs = (char **) xrealloc ((pointer) pazrejuvs, + crejuvs * sizeof (char *)); + pazrejuvs[crejuvs - 1] = optarg; + break; + + case 'S': + /* List jobs for other than specified system. */ + fnotsystems = TRUE; + /* Fall through. */ + case 's': + /* List jobs for specified system. */ + ++csystems; + pazsystems = (char **) xrealloc ((pointer) pazsystems, + csystems * sizeof (char *)); + pazsystems[csystems - 1] = optarg; + break; + + case 'U': + /* List jobs for other than specified user. */ + fnotusers = TRUE; + /* Fall through. */ + case 'u': + /* List jobs for specified user. */ + ++cusers; + pazusers = (char **) xrealloc ((pointer) pazusers, + cusers * sizeof (char *)); + pazusers[cusers - 1] = optarg; + break; + + case 'W': + /* Comment to include in mail messages. */ + zcomment = optarg; + break; + + case 'x': +#if DEBUG > 1 + /* Set debugging level. */ + iDebug |= idebug_parse (optarg); +#endif + break; + + case 'y': + /* List jobs younger than given number of hours. */ + iyounghours = (int) strtol (optarg, (char **) NULL, 10); + break; + + case 0: + /* Long option found and flag set. */ + break; + + default: + ususage (); + break; + } + } + + if (optind != argc) + ususage (); + + /* To avoid confusion, most options are only permitted by + themselves. This restriction might be removed later, but it is + imposed by most implementations. We do permit any combination of + -c, -s, -u, -o and -y, and any combination of -k and -r. */ + ccmds = 0; + if (fall) + ++ccmds; + if (ckills > 0 || crejuvs > 0) + ++ccmds; + if (fmachine) + ++ccmds; + if (fps) + ++ccmds; + if (fquery) + ++ccmds; + if (fexecute || csystems > 0 || cusers > 0 || ioldhours != -1 + || iyounghours != -1 || ccommands > 0) + ++ccmds; + + if (ccmds > 1) + { + ulog (LOG_ERROR, "Too many options"); + ususage (); + } + + iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + +#if DEBUG > 1 + { + const char *zdebug; + + iuuconf = uuconf_debuglevel (puuconf, &zdebug); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + if (zdebug != NULL) + iDebug |= idebug_parse (zdebug); + } +#endif + + usysdep_initialize (puuconf, INIT_SUID); + + /* If no commands were specified, we list all commands for the given + user. */ + if (ccmds == 0) + { + cusers = 1; + azoneuser[0] = zsysdep_login_name (); + pazusers = (char **) azoneuser; + } + + /* Canonicalize the system names. */ + if (csystems > 0) + { + int i; + + for (i = 0; i < csystems; i++) + { + struct uuconf_system ssys; + + iuuconf = uuconf_system_info (puuconf, pazsystems[i], &ssys); + if (iuuconf != UUCONF_SUCCESS) + { + if (iuuconf == UUCONF_NOT_FOUND) + ulog (LOG_FATAL, "%s: System not found", pazsystems[i]); + else + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + } + if (strcmp (pazsystems[i], ssys.uuconf_zname) != 0) + pazsystems[i] = zbufcpy (ssys.uuconf_zname); + (void) uuconf_system_free (puuconf, &ssys); + } + } + + if (ioldhours == -1) + iold = (long) -1; + else + { + iold = (ixsysdep_time ((long *) NULL) + - (long) ioldhours * (long) 60 * (long) 60); + if (iold < 0L) + iold = 0L; + } + if (iyounghours == -1) + iyoung = (long) -1; + else + { + iyoung = (ixsysdep_time ((long *) NULL) + - (long) iyounghours * (long) 60 * (long) 60); + if (iyoung < 0L) + iyoung = 0L; + } + + if (! fexecute + && (fall + || csystems > 0 + || cusers > 0 + || ioldhours != -1 + || iyounghours != -1 + || ccommands > 0)) + fret = fsworkfiles (puuconf, icmd, csystems, pazsystems, fnotsystems, + cusers, pazusers, fnotusers, iold, iyoung, + ccommands, pazcommands, fnotcommands, zcomment, + cstdin); + else if (fexecute) + fret = fsexecutions (puuconf, icmd, csystems, pazsystems, fnotsystems, + cusers, pazusers, fnotusers, iold, iyoung, + ccommands, pazcommands, fnotcommands, zcomment, + cstdin); + else if (icmd != JOB_SHOW) + { + ulog (LOG_ERROR, + "-i, -K, -M, -N, -Q not supported with -k, -m, -p, -q, -r"); + ususage (); + fret = FALSE; + } + else if (fquery) + fret = fsquery (puuconf); + else if (fmachine) + fret = fsmachines (); + else if (ckills > 0 || crejuvs > 0) + { + int i; + + fret = TRUE; + for (i = 0; i < ckills; i++) + if (! fsysdep_kill_job (puuconf, pazkills[i])) + fret = FALSE; + + for (i = 0; i < crejuvs; i++) + if (! fsysdep_rejuvenate_job (puuconf, pazrejuvs[i])) + fret = FALSE; + } + else if (fps) + fret = fsysdep_lock_status (); + else + { +#if DEBUG > 0 + ulog (LOG_FATAL, "Can't happen"); +#endif + fret = FALSE; + } + + ulog_close (); + + usysdep_exit (fret); + + /* Avoid errors about not returning a value. */ + return 0; +} + +/* Print a usage message and die. */ + +static void +ususage () +{ + fprintf (stderr, + "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n", + VERSION); + fprintf (stderr, + "Usage: uustat [options]\n"); + fprintf (stderr, + " -a: list all UUCP jobs\n"); + fprintf (stderr, + " -B num: number of lines to return in -M or -N mail message\n"); + fprintf (stderr, + " -c command: list requests for named command\n"); + fprintf (stderr, + " -C command: list requests for other than named command\n"); + fprintf (stderr, + " -e: list queued executions rather than job requests\n"); + fprintf (stderr, + " -i: prompt for whether to kill each listed job\n"); + fprintf (stderr, + " -k job: kill specified UUCP job\n"); + fprintf (stderr, + " -K: kill each listed job\n"); + fprintf (stderr, + " -m: report status for all remote machines\n"); + fprintf (stderr, + " -M: mail report on each listed job to UUCP administrator\n"); + fprintf (stderr, + " -N: mail report on each listed job to requestor\n"); + fprintf (stderr, + " -o hours: list all jobs older than given number of hours\n"); + fprintf (stderr, + " -p: show status of all processes holding UUCP locks\n"); + fprintf (stderr, + " -q: list number of jobs for each system\n"); + fprintf (stderr, + " -Q: don't list jobs, just take actions (-i, -K, -M, -N)\n"); + fprintf (stderr, + " -r job: rejuvenate specified UUCP job\n"); + fprintf (stderr, + " -s system: list all jobs for specified system\n"); + fprintf (stderr, + " -S system: list all jobs for other than specified system\n"); + fprintf (stderr, + " -u user: list all jobs for specified user\n"); + fprintf (stderr, + " -U user: list all jobs for other than specified user\n"); + fprintf (stderr, + " -W comment: comment to include in mail messages\n"); + fprintf (stderr, + " -y hours: list all jobs younger than given number of hours\n"); + fprintf (stderr, + " -x debug: Set debugging level (0 for none, 9 is max)\n"); +#if HAVE_TAYLOR_CONFIG + fprintf (stderr, + " -I file: Set configuration file to use\n"); +#endif /* HAVE_TAYLOR_CONFIG */ + exit (EXIT_FAILURE); +} + +/* We need to be able to read information from an execution file. */ + +/* The user name extracted from an execution file. */ +static char *zSxqt_user; + +/* The system name from an execution file. */ +static char *zSxqt_system; + +/* Address of requesting user (who to send mail to). */ +static const char *zSxqt_requestor; + +/* The command (no arguments) from an execution file. */ +static char *zSxqt_prog; + +/* The full command line from an execution file. */ +static char *zSxqt_cmd; + +/* Number of files associated with an execution file. */ +static int cSxqt_files; + +/* Names of files associated with execution file. */ +static char **pazSxqt_files; + +/* Standard input file name. */ +static const char *zSxqt_stdin; + +/* A command table used to dispatch an execution file. */ +static const struct uuconf_cmdtab asSxqt_cmds[] = +{ + { "C", UUCONF_CMDTABTYPE_FN | 0, NULL, isxqt_cmd }, + { "I", UUCONF_CMDTABTYPE_STRING, (pointer) &zSxqt_stdin, NULL }, + { "F", UUCONF_CMDTABTYPE_FN | 0, NULL, isxqt_file }, + { "R", UUCONF_CMDTABTYPE_STRING, (pointer) &zSxqt_requestor, NULL }, + { "U", UUCONF_CMDTABTYPE_FN | 3, NULL, isxqt_user }, + { NULL, 0, NULL, NULL } +}; + +/* Read an execution file, setting the above variables. */ + +static boolean +fsxqt_file_read (puuconf, zfile) + pointer puuconf; + const char *zfile; +{ + FILE *e; + int iuuconf; + boolean fret; + + e = fopen (zfile, "r"); + if (e == NULL) + { + ulog (LOG_ERROR, "fopen (%s): %s", zfile, strerror (errno)); + return FALSE; + } + + zSxqt_user = NULL; + zSxqt_system = NULL; + zSxqt_stdin = NULL; + zSxqt_requestor = NULL; + zSxqt_prog = NULL; + zSxqt_cmd = NULL; + cSxqt_files = 0; + pazSxqt_files = NULL; + + iuuconf = uuconf_cmd_file (puuconf, e, asSxqt_cmds, (pointer) NULL, + (uuconf_cmdtabfn) NULL, + UUCONF_CMDTABFLAG_CASE, (pointer) NULL); + (void) fclose (e); + if (iuuconf == UUCONF_SUCCESS) + fret = TRUE; + else + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + fret = FALSE; + } + + if (zSxqt_user == NULL) + zSxqt_user = zbufcpy ("*unknown*"); + if (zSxqt_system == NULL) + zSxqt_system = zbufcpy ("*unknown*"); + if (zSxqt_prog == NULL) + { + zSxqt_prog = zbufcpy ("*none*"); + zSxqt_cmd = zbufcpy ("*none*"); + } + + return fret; +} + +/* Free up the information read from an execution file. */ + +static void +usxqt_file_free () +{ + int i; + + ubuffree (zSxqt_user); + zSxqt_user = NULL; + ubuffree (zSxqt_system); + zSxqt_system = NULL; + ubuffree (zSxqt_prog); + zSxqt_prog = NULL; + ubuffree (zSxqt_cmd); + zSxqt_cmd = NULL; + for (i = 0; i < cSxqt_files; i++) + ubuffree (pazSxqt_files[i]); + cSxqt_files = 0; + xfree ((pointer) pazSxqt_files); + pazSxqt_files = NULL; + zSxqt_stdin = NULL; + zSxqt_requestor = NULL; +} + +/* Get the command from an execution file. */ + +/*ARGSUSED*/ +static int +isxqt_cmd (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + size_t clen; + int i; + + if (argc <= 1) + return UUCONF_CMDTABRET_CONTINUE; + + zSxqt_prog = zbufcpy (argv[1]); + + clen = 0; + for (i = 1; i < argc; i++) + clen += strlen (argv[i]) + 1; + + zSxqt_cmd = zbufalc (clen); + zSxqt_cmd[0] = '\0'; + for (i = 1; i < argc - 1; i++) + { + strcat (zSxqt_cmd, argv[i]); + strcat (zSxqt_cmd, " "); + } + strcat (zSxqt_cmd, argv[i]); + + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Get the associated files from an execution file. */ + +/*ARGSUSED*/ +static int +isxqt_file (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + if (argc != 2 && argc != 3) + return UUCONF_CMDTABRET_CONTINUE; + + /* If this file is not in the spool directory, just ignore it. */ + if (! fspool_file (argv[1])) + return UUCONF_CMDTABRET_CONTINUE; + + ++cSxqt_files; + pazSxqt_files = (char **) xrealloc ((pointer) pazSxqt_files, + cSxqt_files * sizeof (char *)); + + pazSxqt_files[cSxqt_files - 1] = zbufcpy (argv[1]); + + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Get the requesting user and system from an execution file. */ + +/*ARGSUSED*/ +static int +isxqt_user (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + zSxqt_user = zbufcpy (argv[1]); + zSxqt_system = zbufcpy (argv[2]); + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Handle various possible requests to look at work files. */ + +static boolean +fsworkfiles (puuconf, icmd, csystems, pazsystems, fnotsystems, cusers, + pazusers, fnotusers, iold, iyoung, ccommands, pazcommands, + fnotcommands, zcomment, cstdin) + pointer puuconf; + int icmd; + int csystems; + char **pazsystems; + boolean fnotsystems; + int cusers; + char **pazusers; + boolean fnotusers; + long iold; + long iyoung; + int ccommands; + char **pazcommands; + boolean fnotcommands; + const char *zcomment; + int cstdin; +{ + boolean fret; + int i; + int iuuconf; + struct uuconf_system ssys; + + fret = TRUE; + + if (csystems > 0 && ! fnotsystems) + { + for (i = 0; i < csystems; i++) + { + iuuconf = uuconf_system_info (puuconf, pazsystems[i], &ssys); + if (iuuconf != UUCONF_SUCCESS) + { + if (iuuconf == UUCONF_NOT_FOUND) + ulog (LOG_ERROR, "%s: System not found", pazsystems[i]); + else + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + fret = FALSE; + continue; + } + + if (! fsworkfiles_system (puuconf, icmd, &ssys, cusers, pazusers, + fnotusers, iold, iyoung, ccommands, + pazcommands, fnotcommands, zcomment, + cstdin)) + fret = FALSE; + + (void) uuconf_system_free (puuconf, &ssys); + } + } + else + { + char **pznames, **pz; + + iuuconf = uuconf_system_names (puuconf, &pznames, 0); + if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + + for (pz = pznames; *pz != NULL; pz++) + { + if (csystems > 0) + { + for (i = 0; i < csystems; i++) + if (strcmp (*pz, pazsystems[i]) == 0) + break; + if (i < csystems) + continue; + } + + iuuconf = uuconf_system_info (puuconf, *pz, &ssys); + if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + fret = FALSE; + continue; + } + + if (! fsworkfiles_system (puuconf, icmd, &ssys, cusers, pazusers, + fnotusers, iold, iyoung, ccommands, + pazcommands, fnotcommands, zcomment, + cstdin)) + fret = FALSE; + + (void) uuconf_system_free (puuconf, &ssys); + xfree ((pointer) *pz); + } + xfree ((pointer) pznames); + } + + return fret; +} + +/* Look at the work files for a particular system. */ + +static boolean +fsworkfiles_system (puuconf, icmd, qsys, cusers, pazusers, fnotusers, iold, + iyoung, ccommands, pazcommands, fnotcommands, zcomment, + cstdin) + pointer puuconf; + int icmd; + const struct uuconf_system *qsys; + int cusers; + char **pazusers; + boolean fnotusers; + long iold; + long iyoung; + int ccommands; + char **pazcommands; + boolean fnotcommands; + const char *zcomment; + int cstdin; +{ + boolean fret; + + if (! fsysdep_get_work_init (qsys, UUCONF_GRADE_LOW)) + return FALSE; + + while (TRUE) + { + struct scmd s; + long itime; + + if (! fsysdep_get_work (qsys, UUCONF_GRADE_LOW, &s)) + { + usysdep_get_work_free (qsys); + return FALSE; + } + if (s.bcmd == 'H') + break; + + if (cusers > 0) + { + boolean fmatch; + int i; + + fmatch = fnotusers; + for (i = 0; i < cusers; i++) + { + if (s.zuser != NULL + && strcmp (pazusers[i], s.zuser) == 0) + { + fmatch = ! fmatch; + break; + } + } + if (! fmatch) + continue; + } + + itime = ixsysdep_work_time (qsys, s.pseq); + + if (iold != (long) -1 && itime > iold) + continue; + + if (iyoung != (long) -1 && itime < iyoung) + continue; + + if (! fsworkfile_show (puuconf, icmd, qsys, &s, itime, ccommands, + pazcommands, fnotcommands, zcomment, cstdin)) + { + usysdep_get_work_free (qsys); + return FALSE; + } + } + + fret = fsworkfile_show (puuconf, icmd, qsys, (const struct scmd *) NULL, + 0L, ccommands, pazcommands, fnotcommands, zcomment, + cstdin); + + usysdep_get_work_free (qsys); + + return fret; +} + +/* Show a single workfile. This is actually called once for each line + in the workfile, so we accumulate the lines and show them all at + once. This lets us show an execution in a useful fashion. */ + +static boolean +fsworkfile_show (puuconf, icmd, qsys, qcmd, itime, ccommands, pazcommands, + fnotcommands, zcomment, cstdin) + pointer puuconf; + int icmd; + const struct uuconf_system *qsys; + const struct scmd *qcmd; + long itime; + int ccommands; + char **pazcommands; + boolean fnotcommands; + const char *zcomment; + int cstdin; +{ + static struct scmdlist *qlist; + static char *zlistid; + char *zid; + + if (qcmd == NULL) + zid = NULL; + else + { + zid = zsysdep_jobid (qsys, qcmd->pseq); + if (zid == NULL) + return FALSE; + } + + /* If this is the same jobid as the list, put it on the end. */ + + if (qcmd != NULL + && qlist != NULL + && strcmp (zlistid, zid) == 0) + { + struct scmdlist *qnew, **pq; + + ubuffree (zid); + qnew = (struct scmdlist *) xmalloc (sizeof (struct scmdlist)); + qnew->qnext = NULL; + qnew->s = *qcmd; + qnew->itime = itime; + for (pq = &qlist; *pq != NULL; pq = &(*pq)->qnext) + ; + *pq = qnew; + return TRUE; + } + + /* Here we have found a different job ID, so we print the scmd + structures that we have accumulated. We look for the special + case of an execution (an E command, or one of the destination + files begins with X.). We could be more clever about other + situations as well. */ + if (qlist != NULL) + { + boolean fmatch; + const char *zprog, *zcmd, *zrequestor, *zstdin; + char *zfree; + struct scmdlist *qxqt; + struct scmdlist *qfree; + + fmatch = FALSE; + zprog = zcmd = zrequestor = zstdin = NULL; + zfree = NULL; + + for (qxqt = qlist; qxqt != NULL; qxqt = qxqt->qnext) + if (qxqt->s.bcmd == 'E' + || (qxqt->s.bcmd == 'S' + && qxqt->s.zto[0] == 'X' + && qxqt->s.zto[1] == '.' + && fspool_file (qxqt->s.zfrom))) + break; + + if (qxqt == NULL) + { + if (ccommands == 0 + || (fnotcommands + && strcmp (pazcommands[0], "ALL") == 0)) + { + /* Show all the lines in a regular work file. */ + fmatch = TRUE; + + if ((icmd & JOB_SHOW) != 0) + { + struct scmdlist *qshow; + + for (qshow = qlist; qshow != NULL; qshow = qshow->qnext) + { + char *zfile; + long cbytes; + + usworkfile_header (qsys, &qshow->s, zlistid, + qshow->itime, qshow == qlist); + + switch (qshow->s.bcmd) + { + case 'S': + if (strchr (qshow->s.zoptions, 'C') != NULL + || fspool_file (qshow->s.zfrom)) + zfile = zsysdep_spool_file_name (qsys, + qshow->s.ztemp, + qshow->s.pseq); + else + zfile = zbufcpy (qshow->s.zfrom); + if (zfile == NULL) + cbytes = 0; + else + { + cbytes = csysdep_size (zfile); + if (cbytes < 0) + cbytes = 0; + } + printf ("Sending %s (%ld bytes) to %s", + qshow->s.zfrom, cbytes, qshow->s.zto); + ubuffree (zfile); + break; + case 'R': + printf ("Requesting %s to %s", qshow->s.zfrom, + qshow->s.zto); + break; + case 'X': + printf ("Requesting %s to %s", qshow->s.zfrom, + qshow->s.zto); + break; + case 'P': + printf ("(poll file)"); + break; +#if DEBUG > 0 + default: + printf ("Bad line %d", qshow->s.bcmd); + break; +#endif + } + + printf ("\n"); + } + } + } + } + else + { + long csize; + struct scmdlist *qsize; + + /* Show the command for an execution file. */ + if (qxqt->s.bcmd == 'E') + { + zfree = zbufcpy (qxqt->s.zcmd); + zfree[strcspn (zfree, " \t")] = '\0'; + zprog = zfree; + zcmd = qxqt->s.zcmd; + if (strchr (qxqt->s.zoptions, 'R') != NULL) + zrequestor = qxqt->s.znotify; + } + else + { + char *zxqt; + + zxqt = zsysdep_spool_file_name (qsys, qxqt->s.zfrom, + qxqt->s.pseq); + if (zxqt == NULL) + return FALSE; + + if (! fsxqt_file_read (puuconf, zxqt)) + { + ubuffree (zxqt); + return FALSE; + } + + ubuffree (zxqt); + + zprog = zSxqt_prog; + zcmd = zSxqt_cmd; + zrequestor = zSxqt_requestor; + } + + csize = 0L; + for (qsize = qlist; qsize != NULL; qsize = qsize->qnext) + { + if (qsize->s.bcmd == 'S' || qsize->s.bcmd == 'E') + { + char *zfile; + + if (strchr (qsize->s.zoptions, 'C') != NULL + || fspool_file (qsize->s.zfrom)) + zfile = zsysdep_spool_file_name (qsys, qsize->s.ztemp, + qsize->s.pseq); + else + zfile = zbufcpy (qsize->s.zfrom); + if (zfile != NULL) + { + long cbytes; + + cbytes = csysdep_size (zfile); + if (cbytes > 0) + csize += cbytes; + ubuffree (zfile); + } + } + } + + if (ccommands == 0) + fmatch = TRUE; + else + { + int i; + + fmatch = fnotcommands; + for (i = 0; i < ccommands; i++) + { + if (strcmp (pazcommands[i], "ALL") == 0 + || strcmp (pazcommands[i], zprog) == 0) + { + fmatch = ! fmatch; + break; + } + } + } + + /* To get the name of the standard input file on this system + we have to look through the list of file transfers to + find the right one on the remote system. */ + if (fmatch) + { + struct scmdlist *qstdin; + + if (qxqt->s.bcmd == 'E') + qstdin = qxqt; + else if (zSxqt_stdin != NULL) + { + for (qstdin = qlist; + qstdin != NULL; + qstdin = qstdin->qnext) + if (qstdin->s.bcmd == 'S' + && strcmp (qstdin->s.zto, zSxqt_stdin) == 0) + break; + } + else + qstdin = NULL; + + if (qstdin != NULL) + { + if (strchr (qstdin->s.zoptions, 'C') != NULL + || fspool_file (qstdin->s.zfrom)) + zstdin = qstdin->s.ztemp; + else + zstdin = qstdin->s.zfrom; + } + } + + if (fmatch && (icmd & JOB_SHOW) != 0) + { + usworkfile_header (qsys, &qxqt->s, zlistid, qxqt->itime, + TRUE); + printf ("Executing %s (sending %ld bytes)\n", zcmd, csize); + } + } + + if (fmatch) + { + boolean fkill; + + fkill = FALSE; + if ((icmd & JOB_INQUIRE) != 0) + { + int b; + + /* Ask stdin whether this job should be killed. */ + fprintf (stderr, "%s: Kill %s? ", abProgram, zlistid); + (void) fflush (stderr); + b = getchar (); + fkill = b == 'y' || b == 'Y'; + while (b != EOF && b != '\n') + b = getchar (); + } + else if ((icmd & JOB_KILL) != 0) + fkill = TRUE; + + if (fkill + && (qlist->s.zuser == NULL + || strcmp (zsysdep_login_name (), qlist->s.zuser) != 0) + && ! fsysdep_privileged ()) + ulog (LOG_ERROR, "%s: Not submitted by you", zlistid); + else + { + if ((icmd & (JOB_MAIL | JOB_NOTIFY)) != 0) + { + if (! fsnotify (puuconf, icmd, zcomment, cstdin, fkill, + zcmd, qlist, zlistid, qlist->s.zuser, + qsys, zstdin, qlist->s.pseq, zrequestor)) + return FALSE; + } + + if (fkill) + { + if (! fsysdep_kill_job (puuconf, zlistid)) + return FALSE; + } + } + } + + if (qxqt != NULL) + { + if (qxqt->s.bcmd == 'E') + ubuffree (zfree); + else + usxqt_file_free (); + } + + /* Free up the list of entries. */ + qfree = qlist; + while (qfree != NULL) + { + struct scmdlist *qnext; + + qnext = qfree->qnext; + xfree ((pointer) qfree); + qfree = qnext; + } + + ubuffree (zlistid); + + qlist = NULL; + zlistid = NULL; + } + + /* Start a new list with the entry we just got. */ + if (qcmd != NULL) + { + qlist = (struct scmdlist *) xmalloc (sizeof (struct scmdlist)); + qlist->qnext = NULL; + qlist->s = *qcmd; + qlist->itime = itime; + zlistid = zid; + } + + return TRUE; +} + +/* Show the header of the line describing a workfile. */ + +static void +usworkfile_header (qsys, qcmd, zjobid, itime, ffirst) + const struct uuconf_system *qsys; + const struct scmd *qcmd; + const char *zjobid; + long itime; + boolean ffirst; +{ + const char *zshowid; + struct tm stime; + + if (ffirst) + zshowid = zjobid; + else + zshowid = "-"; + + printf ("%s %s %s ", zshowid, qsys->uuconf_zname, + qcmd->zuser != NULL ? qcmd->zuser : OWNER); + + usysdep_localtime (itime, &stime); + printf ("%02d-%02d %02d:%02d ", + stime.tm_mon + 1, stime.tm_mday, stime.tm_hour, stime.tm_min); +} + +/* List queued executions that have not been processed by uuxqt for + one reason or another. */ + +static boolean +fsexecutions (puuconf, icmd, csystems, pazsystems, fnotsystems, cusers, + pazusers, fnotusers, iold, iyoung, ccommands, pazcommands, + fnotcommands, zcomment, cstdin) + pointer puuconf; + int icmd; + int csystems; + char **pazsystems; + boolean fnotsystems; + int cusers; + char **pazusers; + boolean fnotusers; + long iold; + long iyoung; + int ccommands; + char **pazcommands; + boolean fnotcommands; + const char *zcomment; + int cstdin; +{ + const char *zlocalname; + int iuuconf; + char *zfile; + char *zsystem; + boolean ferr; + + iuuconf = uuconf_localname (puuconf, &zlocalname); + if (iuuconf == UUCONF_NOT_FOUND) + { + zlocalname = zsysdep_localname (); + if (zlocalname == NULL) + return FALSE; + } + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + + if (! fsysdep_get_xqt_init ()) + return FALSE; + + while ((zfile = zsysdep_get_xqt (&zsystem, &ferr)) != NULL) + { + boolean fmatch; + int i; + long itime; + + if (csystems > 0) + { + fmatch = fnotsystems; + for (i = 0; i < csystems; i++) + { + if (strcmp (pazsystems[i], zsystem) == 0) + { + fmatch = ! fmatch; + break; + } + } + if (! fmatch) + { + ubuffree (zfile); + ubuffree (zsystem); + continue; + } + } + + itime = ixsysdep_file_time (zfile); + + if ((iold != (long) -1 && itime > iold) + || (iyoung != (long) -1 && itime < iyoung)) + { + ubuffree (zfile); + ubuffree (zsystem); + continue; + } + + /* We need to read the execution file before we can check the + user name. */ + if (! fsxqt_file_read (puuconf, zfile)) + { + ubuffree (zfile); + ubuffree (zsystem); + continue; + } + + if (cusers == 0) + fmatch = TRUE; + else + { + fmatch = fnotusers; + for (i = 0; i < cusers; i++) + { + if (strcmp (zSxqt_user, pazusers[i]) == 0 + || (zSxqt_requestor != NULL + && strcmp (zSxqt_requestor, pazusers[i]) == 0)) + { + fmatch = ! fmatch; + break; + } + } + } + + if (fmatch && ccommands > 0) + { + fmatch = fnotcommands; + for (i = 0; i < ccommands; i++) + { + if (strcmp (pazcommands[i], "ALL") == 0 + || strcmp (pazcommands[i], zSxqt_prog) == 0) + { + fmatch = ! fmatch; + break; + } + } + } + + if (fmatch) + { + boolean fbad, fkill; + struct uuconf_system ssys; + + fbad = FALSE; + + if ((icmd & JOB_SHOW) != 0) + { + struct tm stime; + + printf ("%s %s!", zsystem, zSxqt_system); + if (zSxqt_requestor != NULL) + printf ("%s", zSxqt_requestor); + else + printf ("%s", zSxqt_user); + + usysdep_localtime (itime, &stime); + printf (" %02d-%02d %02d:%02d ", + stime.tm_mon + 1, stime.tm_mday, stime.tm_hour, + stime.tm_min); + + printf ("%s\n", zSxqt_cmd); + } + + fkill = FALSE; + if ((icmd & JOB_INQUIRE) != 0) + { + int b; + + /* Ask stdin whether this job should be killed. */ + fprintf (stderr, "%s: Kill %s? ", abProgram, zSxqt_cmd); + (void) fflush (stderr); + b = getchar (); + fkill = b == 'y' || b == 'Y'; + while (b != EOF && b != '\n') + b = getchar (); + } + else if ((icmd & JOB_KILL) != 0) + fkill = TRUE; + + if (fkill) + { + if ((strcmp (zSxqt_user, zsysdep_login_name ()) != 0 + || strcmp (zsystem, zlocalname) != 0) + && ! fsysdep_privileged ()) + { + ulog (LOG_ERROR, "Job not submitted by you\n"); + fbad = TRUE; + } + } + + if (! fbad) + { + iuuconf = uuconf_system_info (puuconf, zsystem, &ssys); + if (iuuconf != UUCONF_SUCCESS) + { + if (iuuconf != UUCONF_NOT_FOUND) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + fbad = TRUE; + } + else if (strcmp (zsystem, zlocalname) == 0) + { + iuuconf = uuconf_system_local (puuconf, &ssys); + if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + fbad = TRUE; + } + } + else if (! funknown_system (puuconf, zsystem, &ssys)) + { + ulog (LOG_ERROR, "Job for unknown system %s", + zsystem); + fbad = TRUE; + } + } + } + + if (! fbad && (icmd & (JOB_MAIL | JOB_NOTIFY)) != 0) + { + if (! fsnotify (puuconf, icmd, zcomment, cstdin, fkill, + zSxqt_cmd, (struct scmdlist *) NULL, + (const char *) NULL, zSxqt_user, &ssys, + zSxqt_stdin, (pointer) NULL, zSxqt_requestor)) + { + ferr = TRUE; + usxqt_file_free (); + ubuffree (zfile); + ubuffree (zsystem); + break; + } + } + + if (! fbad && fkill) + { + for (i = 0; i < cSxqt_files; i++) + { + char *z; + + z = zsysdep_spool_file_name (&ssys, pazSxqt_files[i], + (pointer) NULL); + if (z != NULL) + { + (void) remove (z); + ubuffree (z); + } + } + if (remove (zfile) != 0) + ulog (LOG_ERROR, "remove (%s): %s", zfile, + strerror (errno)); + } + + if (! fbad) + (void) uuconf_system_free (puuconf, &ssys); + } + + usxqt_file_free (); + ubuffree (zfile); + ubuffree (zsystem); + } + + usysdep_get_xqt_free (); + + return ferr; +} + +/* When a job is killed, send mail to the appropriate people. */ + +static boolean +fsnotify (puuconf, icmd, zcomment, cstdin, fkilled, zcmd, qcmd, zid, zuser, + qsys, zstdin, pstdinseq, zrequestor) + pointer puuconf; + int icmd; + const char *zcomment; + int cstdin; + boolean fkilled; + const char *zcmd; + struct scmdlist *qcmd; + const char *zid; + const char *zuser; + const struct uuconf_system *qsys; + const char *zstdin; + pointer pstdinseq; + const char *zrequestor; +{ + const char **pz; + int cgot; + int i, istdin; + const char *zsubject; + boolean fret; + + pz = (const char **) xmalloc (20 * sizeof (const char *)); + cgot = 20; + + i = 0; + if (zid == NULL) + pz[i++] = "A UUCP execution request"; + else + { + pz[i++] = "UUCP job\n\t"; + pz[i++] = zid; + pz[i++] = "\nfor system\n\t"; + pz[i++] = qsys->uuconf_zname; + } + pz[i++] = "\nrequested by\n\t"; + pz[i++] = zuser != NULL ? zuser : OWNER; + if (zid == NULL) + { + pz[i++] = "\non system\n\t"; + pz[i++] = qsys->uuconf_zname; + } + pz[i++] = "\n"; + + if (fkilled) + pz[i++] = "has been killed.\n"; + + if (zcomment != NULL) + { + pz[i++] = zcomment; + pz[i++] = "\n"; + } + + pz[i++] = "The job "; + if (fkilled) + pz[i++] = "was\n"; + else + pz[i++] = "is\n"; + + if (zcmd != NULL) + { + pz[i++] = "\t"; + pz[i++] = zcmd; + } + else + { + struct scmdlist *qshow; + + for (qshow = qcmd; qshow != NULL; qshow = qshow->qnext) + { + if (i + 10 > cgot) + { + cgot += 20; + pz = (const char **) xrealloc ((pointer) pz, + cgot * sizeof (const char *)); + } + + switch (qshow->s.bcmd) + { + case 'S': + pz[i++] = "\tsend "; + break; + default: + case 'R': + case 'X': + pz[i++] = "\trequest "; + break; + case 'P': + pz[i++] = "\tpoll "; +#if DEBUG > 0 + case 'E': + ulog (LOG_FATAL, "fsnotify: Can't happen"); + break; +#endif + } + if (qshow->s.zfrom != NULL && qshow->s.zto != NULL) + { + pz[i++] = qshow->s.zfrom; + pz[i++] = " to "; + pz[i++] = qshow->s.zto; + } + } + } + + istdin = i; + if (cstdin > 0 && zstdin != NULL) + { + boolean fspool; + char *zfile; + FILE *e; + + fspool = fspool_file (zstdin); + if (fspool) + zfile = zsysdep_spool_file_name (qsys, zstdin, pstdinseq); + else + zfile = zsysdep_local_file (zstdin, qsys->uuconf_zpubdir); + + if (zfile != NULL + && (fspool + || fin_directory_list (zfile, qsys->uuconf_pzremote_send, + qsys->uuconf_zpubdir, TRUE, TRUE, + (const char *) NULL))) + { + e = fopen (zfile, "r"); + if (e != NULL) + { + int clines, clen; + char *zline; + size_t cline; + + pz[i++] = "\n"; + istdin = i; + + clines = 0; + + zline = NULL; + cline = 0; + while ((clen = getline (&zline, &cline, e)) > 0) + { + if (memchr (zline, '\0', (size_t) clen) != NULL) + { + int ifree; + + /* A null character means this is probably a + binary file. */ + for (ifree = istdin; ifree < i; ifree++) + ubuffree ((char *) pz[ifree]); + i = istdin - 1; + break; + } + ++clines; + if (clines > cstdin) + break; + if (i >= cgot) + { + cgot += 20; + pz = (const char **) xrealloc ((pointer) pz, + (cgot + * sizeof (char *))); + } + pz[i++] = zbufcpy (zline); + } + xfree ((pointer) zline); + (void) fclose (e); + } + } + + ubuffree (zfile); + } + + if (fkilled) + zsubject = "UUCP job killed"; + else + zsubject = "UUCP notification"; + + fret = TRUE; + + if ((icmd & JOB_MAIL) != 0) + { + if (! fsysdep_mail (OWNER, zsubject, i, pz)) + fret = FALSE; + } + + if ((icmd & JOB_NOTIFY) != 0 + && (zrequestor != NULL || zuser != NULL)) + { + const char *zmail; + char *zfree; + + if (zrequestor != NULL) + zmail = zrequestor; + else + zmail = zuser; + + zfree = NULL; + + if (zid == NULL) + { + int iuuconf; + const char *zloc; + + /* This is an execution request, which may be from another + system. If it is, we must prepend that system name to + the user name extracted from the X. file. */ + iuuconf = uuconf_localname (puuconf, &zloc); + if (iuuconf == UUCONF_NOT_FOUND) + { + zloc = zsysdep_localname (); + if (zloc == NULL) + return FALSE; + } + else if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + if (strcmp (qsys->uuconf_zname, zloc) != 0 +#if HAVE_INTERNET_MAIL + && strchr (zmail, '@') == NULL +#endif + ) + { + zfree = zbufalc (strlen (qsys->uuconf_zname) + + strlen (zmail) + + sizeof "!"); + sprintf (zfree, "%s!%s", qsys->uuconf_zname, zmail); + zmail = zfree; + } + } + + if (! fsysdep_mail (zmail, zsubject, i, pz)) + fret = FALSE; + + ubuffree (zfree); + } + + while (istdin < i) + { + ubuffree ((char *) pz[istdin]); + istdin++; + } + + xfree ((pointer) pz); + + return fret; +} + +/* Handle the -q option. For each remote system this lists the number + of jobs queued, the number of executions queued, and the current + call status. We get the executions all at once, because they are + not accessed by system. They could be, but it is possible to have + executions pending for an unknown system, so special handling would + still be required. */ + +struct sxqtlist +{ + struct sxqtlist *qnext; + char *zsystem; + int cxqts; + long ifirst; +}; + +/* These local functions need the definition of sxqtlist for the + prototype. */ + +static boolean fsquery_system P((const struct uuconf_system *qsys, + struct sxqtlist **pq, + long inow, const char *zlocalname)); +static boolean fsquery_show P((const struct uuconf_system *qsys, int cwork, + long ifirstwork, + struct sxqtlist *qxqt, + long inow, const char *zlocalname)); + +static boolean +fsquery (puuconf) + pointer puuconf; +{ + int iuuconf; + const char *zlocalname; + struct sxqtlist *qlist; + char *zfile, *zsystem; + boolean ferr; + long inow; + char **pznames, **pz; + boolean fret; + + iuuconf = uuconf_localname (puuconf, &zlocalname); + if (iuuconf == UUCONF_NOT_FOUND) + { + zlocalname = zsysdep_localname (); + if (zlocalname == NULL) + return FALSE; + } + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + + /* Get a count of all the execution files. */ + if (! fsysdep_get_xqt_init ()) + return FALSE; + + qlist = NULL; + while ((zfile = zsysdep_get_xqt (&zsystem, &ferr)) != NULL) + { + struct sxqtlist *qlook; + + for (qlook = qlist; qlook != NULL; qlook = qlook->qnext) + if (strcmp (zsystem, qlook->zsystem) == 0) + break; + + if (qlook != NULL) + { + long itime; + + ubuffree (zsystem); + ++qlook->cxqts; + itime = ixsysdep_file_time (zfile); + if (itime < qlook->ifirst) + qlook->ifirst = itime; + } + else + { + struct sxqtlist *qnew; + + qnew = (struct sxqtlist *) xmalloc (sizeof (struct sxqtlist)); + qnew->qnext = qlist; + qnew->zsystem = zsystem; + qnew->cxqts = 1; + qnew->ifirst = ixsysdep_file_time (zfile); + qlist = qnew; + } + + ubuffree (zfile); + } + + usysdep_get_xqt_free (); + + if (ferr) + return FALSE; + + inow = ixsysdep_time ((long *) NULL); + + /* Show the information for each system. */ + iuuconf = uuconf_system_names (puuconf, &pznames, 0); + if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + + fret = TRUE; + + for (pz = pznames; *pz != NULL; pz++) + { + struct uuconf_system ssys; + + iuuconf = uuconf_system_info (puuconf, *pz, &ssys); + if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + fret = FALSE; + continue; + } + + if (! fsquery_system (&ssys, &qlist, inow, zlocalname)) + fret = FALSE; + + (void) uuconf_system_free (puuconf, &ssys); + xfree ((pointer) *pz); + } + + /* Check for the local system in the list of execution files. */ + if (qlist != NULL) + { + struct sxqtlist **pq; + + for (pq = &qlist; *pq != NULL; pq = &(*pq)->qnext) + { + if (strcmp ((*pq)->zsystem, zlocalname) == 0) + { + struct uuconf_system ssys; + struct sxqtlist *qfree; + + iuuconf = uuconf_system_info (puuconf, zlocalname, &ssys); + if (iuuconf != UUCONF_SUCCESS) + { + if (iuuconf != UUCONF_NOT_FOUND) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + fret = FALSE; + break; + } + + iuuconf = uuconf_system_local (puuconf, &ssys); + if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + fret = FALSE; + break; + } + ssys.uuconf_zname = (char *) zlocalname; + } + + if (! fsquery_show (&ssys, 0, 0L, *pq, inow, zlocalname)) + fret = FALSE; + (void) uuconf_system_free (puuconf, &ssys); + qfree = *pq; + *pq = qfree->qnext; + ubuffree (qfree->zsystem); + xfree ((pointer) qfree); + break; + } + } + } + + /* Print out information for any unknown systems for which we have + execution files. */ + while (qlist != NULL) + { + struct uuconf_system ssys; + struct sxqtlist *qnext; + + if (! funknown_system (puuconf, qlist->zsystem, &ssys)) + { + ulog (LOG_ERROR, "Executions queued up for unknown systems"); + fret = FALSE; + break; + } + + if (! fsquery_show (&ssys, 0, 0L, qlist, inow, zlocalname)) + fret = FALSE; + (void) uuconf_system_free (puuconf, &ssys); + qnext = qlist->qnext; + ubuffree (qlist->zsystem); + xfree ((pointer) qlist); + qlist = qnext; + } + + return fret; +} + +/* Query a single known system. */ + +static boolean +fsquery_system (qsys, pq, inow, zlocalname) + const struct uuconf_system *qsys; + struct sxqtlist **pq; + long inow; + const char *zlocalname; +{ + int cwork; + long ifirstwork; + char *zid; + boolean fret; + + if (! fsysdep_get_work_init (qsys, UUCONF_GRADE_LOW)) + return FALSE; + + cwork = 0; + ifirstwork = 0L; + zid = NULL; + while (TRUE) + { + struct scmd s; + long itime; + char *zthisid; + + if (! fsysdep_get_work (qsys, UUCONF_GRADE_LOW, &s)) + return FALSE; + if (s.bcmd == 'H') + break; + + zthisid = zsysdep_jobid (qsys, s.pseq); + if (zid != NULL && strcmp (zid, zthisid) == 0) + ubuffree (zthisid); + else + { + ++cwork; + ubuffree (zid); + zid = zthisid; + } + + itime = ixsysdep_work_time (qsys, s.pseq); + if (ifirstwork == 0L || ifirstwork > itime) + ifirstwork = itime; + } + + usysdep_get_work_free (qsys); + ubuffree (zid); + + /* Find the execution information, if any. */ + while (*pq != NULL) + { + if (strcmp ((*pq)->zsystem, qsys->uuconf_zname) == 0) + break; + pq = &(*pq)->qnext; + } + + /* If there are no commands and no executions, don't print any + information for this system. */ + if (cwork == 0 && *pq == NULL) + return TRUE; + + fret = fsquery_show (qsys, cwork, ifirstwork, *pq, inow, zlocalname); + + if (*pq != NULL) + { + struct sxqtlist *qfree; + + qfree = *pq; + *pq = qfree->qnext; + ubuffree (qfree->zsystem); + xfree ((pointer) qfree); + } + + return fret; +} + +/* Print out the query information for a single system. We handle the + local system specially. */ + +static boolean +fsquery_show (qsys, cwork, ifirstwork, qxqt, inow, zlocalname) + const struct uuconf_system *qsys; + int cwork; + long ifirstwork; + struct sxqtlist *qxqt; + long inow; + const char *zlocalname; +{ + boolean flocal; + struct sstatus sstat; + boolean fnostatus; + struct tm stime; + int cpad; + + flocal = strcmp (qsys->uuconf_zname, zlocalname) == 0; + + if (! flocal) + { + if (! fsysdep_get_status (qsys, &sstat, &fnostatus)) + return FALSE; + } + + printf ("%-10s %3dC (", qsys->uuconf_zname, cwork); + + if (cwork == 0) + { + printf ("0 secs"); + cpad = 3; + } + else + cpad = csunits_show (inow - ifirstwork); + + printf (") "); + while (cpad-- != 0) + printf (" "); + + if (qxqt == NULL) + printf (" 0X (0 secs) "); + else + { + printf ("%3dX (", qxqt->cxqts); + cpad = csunits_show (inow - qxqt->ifirst); + printf (")"); + while (cpad-- != 0) + printf (" "); + } + + if (flocal || fnostatus) + { + printf ("\n"); + return TRUE; + } + + usysdep_localtime (sstat.ilast, &stime); + + printf (" %02d-%02d %02d:%02d ", + stime.tm_mon + 1,stime.tm_mday, stime.tm_hour, stime.tm_min); + + printf ("%s\n", azStatus[(int) sstat.ttype]); + + return TRUE; +} + +/* Print a time difference in the largest applicable units. */ + +static int +csunits_show (idiff) + long idiff; +{ + const char *zunit; + long iunits; + int cpad; + + if (idiff > (long) 24 * (long) 60 * (long) 60) + { + iunits = idiff / ((long) 24 * (long) 60 * (long) 60); + zunit = "day"; + cpad = 4; + } + else if (idiff > (long) 60 * 60) + { + iunits = idiff / (long) (60 * 60); + zunit = "hour"; + cpad = 3; + } + else if (idiff > (long) 60) + { + iunits = idiff / (long) 60; + zunit = "min"; + cpad = 4; + } + else + { + iunits = idiff; + zunit = "sec"; + cpad = 4; + } + + printf ("%ld %s%s", iunits, zunit, iunits == 1 ? "" : "s"); + + if (iunits != 1) + --cpad; + if (iunits > 99) + --cpad; + if (iunits > 9) + --cpad; + return cpad; +} + +/* Give a list of all status entries for all machines that we have + status entries for. We need to get a list of status entries in a + system dependent fashion, since we may have status for unknown + systems. */ + +static boolean +fsmachines () +{ + pointer phold; + char *zsystem; + boolean ferr; + struct sstatus sstat; + + if (! fsysdep_all_status_init (&phold)) + return FALSE; + + while ((zsystem = zsysdep_all_status (phold, &ferr, &sstat)) != NULL) + { + struct tm stime; + + usysdep_localtime (sstat.ilast, &stime); + printf ("%-14s %02d-%02d %02d:%02d %s", zsystem, + stime.tm_mon + 1, stime.tm_mday, stime.tm_hour, + stime.tm_min, azStatus[(int) sstat.ttype]); + ubuffree (zsystem); + if (sstat.ttype != STATUS_TALKING + && sstat.cwait > 0) + { + printf (" (%d %s", sstat.cretries, + sstat.cretries == 1 ? "try" : "tries"); + if (sstat.ilast + sstat.cwait > ixsysdep_time ((long *) NULL)) + { + usysdep_localtime (sstat.ilast + sstat.cwait, &stime); + printf (", next after %02d-%02d %02d:%02d", + stime.tm_mon + 1, stime.tm_mday, stime.tm_hour, + stime.tm_min); + } + printf (")"); + } + printf ("\n"); + } + + usysdep_all_status_free (phold); + + return ! ferr; +} diff --git a/gnu/libexec/uucp/uuto/Makefile b/gnu/libexec/uucp/uuto/Makefile new file mode 100644 index 0000000000..8c3437ec04 --- /dev/null +++ b/gnu/libexec/uucp/uuto/Makefile @@ -0,0 +1,17 @@ +# Makefile for uuto +# $Id: Makefile,v 1.1 1993/08/05 18:28:09 conklin Exp $ + +BINDIR= $(bindir) + +PROG= uuto +SRCS= +NOMAN= +STRIP= + +uuto: uuto.in + sed -e "s|@BINDIR@|$(bindir)|g" -e "s|@SBINDIR@|$(sbindir)|g" \ + $(.CURDIR)/uuto.in > $(.TARGET) + +depend: + +.include diff --git a/gnu/libexec/uucp/uuto/uuto.in b/gnu/libexec/uucp/uuto/uuto.in new file mode 100644 index 0000000000..2d7d96a958 --- /dev/null +++ b/gnu/libexec/uucp/uuto/uuto.in @@ -0,0 +1,16 @@ +: +# uuto +# Send files to a user on another system. +# +# Copyright (C) 1992 Ian Lance Taylor +# +# Please feel free do whatever you like with this exciting shell +# script. +# +# This is pretty trivial, since all the functionality was moved into +# uucp itself. The -t means to interpret the final argument as +# system!user, the -R means to copy directories recursively, and the +# -c means to not copy the files to the spool directory (may be +# overriden by -C or -p). +# +@BINDIR@/uucp -t -R -c $* diff --git a/gnu/libexec/uucp/uux/Makefile b/gnu/libexec/uucp/uux/Makefile new file mode 100644 index 0000000000..8f1cb4ef63 --- /dev/null +++ b/gnu/libexec/uucp/uux/Makefile @@ -0,0 +1,16 @@ +# Makefile for uux +# $Id: Makefile,v 1.2 1993/08/05 16:15:25 jtc Exp $ + +BINDIR= $(bindir) +BINOWN= $(owner) +BINMODE= 4555 + +PROG= uux +SRCS= uux.c util.c log.c copy.c +LDADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +DPADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +CFLAGS+= -I$(.CURDIR)/../common_sources\ + -DVERSION=\"$(VERSION)\" + +.include +.PATH: $(.CURDIR)/../common_sources diff --git a/gnu/libexec/uucp/uux/uux.1 b/gnu/libexec/uucp/uux/uux.1 new file mode 100644 index 0000000000..69c85abb51 --- /dev/null +++ b/gnu/libexec/uucp/uux/uux.1 @@ -0,0 +1,234 @@ +''' $Id: uux.1,v 1.1 1993/08/04 19:37:13 jtc Exp $ +.TH uux 1 "Taylor UUCP 1.04" +.SH NAME +uux \- Remote command execution over UUCP +.SH SYNOPSIS +.B uux +[ options ] command +.SH DESCRIPTION +The +.I uux +command is used to execute a command on a remote system, or to execute +a command on the local system using files from remote systems. +The command +is not executed immediately; the request is queued until the +.I uucico +(8) daemon calls the system and executes it. The daemon is +started automatically unless the +.B \-r +switch is given. + +The actual command execution is done by the +.I uuxqt +(8) daemon. + +File arguments can be gathered from remote systems to the execution +system, as can standard input. Standard output may be directed to a +file on a remote system. + +The command name may be preceded by a system name followed by an +exclamation point if it is to be executed on a remote system. An +empty system name is taken as the local system. + +Each argument that contains an exclamation point is treated as naming +a file. The system which the file is on is before the exclamation +point, and the pathname on that system follows it. An empty system +name is taken as the local system; this must be used to transfer a +file to a command being executed on a remote system. If the path is +not absolute, it will be appended to the current working directory on +the local system; the result may not be meaningful on the remote +system. A pathname may begin with ~/, in which case it is relative to +the UUCP public directory (usually /usr/spool/uucppublic) on the +appropriate system. A pathname may begin with ~name/, in which case +it is relative to the home directory of the named user on the +appropriate system. + +Standard input and output may be redirected as usual; the pathnames +used may contain exclamation points to indicate that they are on +remote systems. Note that the redirection characters must be quoted +so that they are passed to +.I uux +rather than interpreted by the shell. Append redirection (>>) does +not work. + +All specified files are gathered together into a single directory +before execution of the command begins. This means that each file +must have a distinct base name. For example, +.EX +uux 'sys1!diff sys2!~user1/foo sys3!~user2/foo >!foo.diff' +.EE +will fail because both files will be copied to sys1 and stored under +the name foo. + +Arguments may be quoted by parentheses to avoid interpretation of +exclamation points. This is useful when executing the +.I uucp +command on a remote system. +.SH OPTIONS +The following options may be given to +.I uux. +.TP 5 +.B \-,\-p +Read standard input and use it as the standard input for the command +to be executed. +.TP 5 +.B \-c +Do not copy local files to the spool directory. This is the default. +If they are +removed before being processed by the +.I uucico +(8) daemon, the copy will fail. The files must be readable by the +.I uucico +(8) daemon, +as well as the by the invoker of +.I uux. +.TP 5 +.B \-C +Copy local files to the spool directory. +.TP 5 +.B \-l +Link local files into the spool directory. If a file can not be +linked because it is on a different device, it will be copied unless +the +.B \-c +option also appears (in other words, use of +.B \-l +switches the default from +.B \-c +to +.B \-C). +If the files are changed before being processed by the +.I uucico +(8) daemon, the changed versions will be used. The files must be +readable by the +.I uucico +(8) daemon, as well as by the invoker of +.I uux. +.TP 5 +.B \-g grade +Set the grade of the file transfer command. Jobs of a higher grade +are executed first. Grades run 0 ... 9 A ... Z a ... z from high to +low. +.TP 5 +.B \-n +Do not send mail about the status of the job, even if it fails. +.TP 5 +.B \-z +Send mail about the status of the job if an error occurs. For many +.I uuxqt +daemons, including the Taylor UUCP +.I uuxqt, +this is the default action; for those, +.B \-z +will have no effect. However, some +.I uuxqt +daemons will send mail if the job succeeds unless the +.B \-z +option is used, and some other +.I uuxqt +daemons will not send mail if the job fails unless the +.B \-z +option is used. +.TP 5 +.B \-r +Do not start the +.I uucico +(8) daemon immediately; merely queue up the execution request for later +processing. +.TP 5 +.B \-j +Print jobids on standard output. A jobid will be generated for each +file copy operation required to perform the operation. These file +copies may be cancelled by +passing the jobid to the +.B \-k +switch of +.I uustat +(1), which will make the execution impossible to complete. +.TP 5 +.B \-a address +Report job status to the specified e-mail address. +.TP 5 +.B \-x type +Turn on particular debugging types. The following types are +recognized: abnormal, chat, handshake, uucp-proto, proto, port, +config, spooldir, execute, incoming, outgoing. Only abnormal, config, +spooldir and execute are meaningful for +.I uux. + +Multiple types may be given, separated by commas, and the +.B \-x +option may appear multiple times. A number may also be given, which +will turn on that many types from the foregoing list; for example, +.B \-x 2 +is equivalent to +.B \-x abnormal,chat. +.TP 5 +.B \-I file +Set configuration file to use. This option may not be available, +depending upon how +.I uux +was compiled. +.SH EXAMPLES +.EX +uux -z - sys1!rmail user1 +.EE +Execute the command ``rmail user1'' on the system sys1, giving it as +standard input whatever is given to +.I uux +as standard input. If a failure occurs, send a message using +.I mail +(1). +.EX +uux 'diff -c sys1!~user1/file1 sys2!~user2/file2 >!file.diff' +.EE +Fetch the two named files from system sys1 and system sys2 and execute +.I diff +putting the result in file.diff in the current directory. The current +directory must be writable by the +.I uuxqt +(8) daemon for this to work. +.EX +uux 'sys1!uucp ~user1/file1 (sys2!~user2/file2)' +.EE +Execute +.I uucp +on the system sys1 copying file1 (on system sys1) to sys2. This +illustrates the use of parentheses for quoting. +.SH RESTRICTIONS +The remote system may not permit you to execute certain commands. +Many remote systems only permit the execution of +.I rmail +and +.I rnews. + +Some of the options are dependent on the capabilities of the +.I uuxqt +(8) daemon on the remote system. +.SH FILES +The file names may be changed at compilation time or by the +configuration file, so these are only approximations. + +.br +/usr/lib/uucp/config - Configuration file. +.br +/usr/spool/uucp - +UUCP spool directory. +.br +/usr/spool/uucp/Log - +UUCP log file. +.br +/usr/spool/uucppublic - +Default UUCP public directory. +.SH SEE ALSO +mail(1), uustat(1), uucp(1), uucico(8), uuxqt(8) +.SH BUGS +Files can not be referenced across multiple systems. + +Too many jobids are output by +.B \-j, +and there is no good way to cancel a local execution requiring remote +files. +.SH AUTHOR +Ian Lance Taylor +(ian@airs.com or uunet!airs!ian) diff --git a/gnu/libexec/uucp/uux/uux.c b/gnu/libexec/uucp/uux/uux.c new file mode 100644 index 0000000000..5dc9866148 --- /dev/null +++ b/gnu/libexec/uucp/uux/uux.c @@ -0,0 +1,1502 @@ +/* uux.c + Prepare to execute a command on a remote system. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char uux_rcsid[] = "$Id: uux.c,v 1.1 1993/08/04 19:37:14 jtc Exp $"; +#endif + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" +#include "sysdep.h" +#include "getopt.h" + +#include +#include + +/* These character lists should, perhaps, be in sysdep.h. */ + +/* This is the list of shell metacharacters that we check for. If one + of these is present, we request uuxqt to execute the command with + /bin/sh. Otherwise we let it execute using execve. */ + +#define ZSHELLCHARS "\"'`*?[;&()|<>\\$" + +/* This is the list of word separators. We break filename arguments + at these characters. */ +#define ZSHELLSEPS ";&*|<> \t" + +/* This is the list of word separators without the redirection + operators. */ +#define ZSHELLNONREDIRSEPS ";&*| \t" + +/* The program name. */ +char abProgram[] = "uux"; + +/* The name of the execute file. */ +const char *zXxqt_name; + +/* The execute file we are creating. */ +static FILE *eXxqt_file; + +/* A list of commands to be spooled. */ +static struct scmd *pasXcmds; +static int cXcmds; + +/* A file to close if we're forced to exit. */ +static FILE *eXclose; + +/* Local functions. */ +static void uxusage P((void)); +static void uxadd_xqt_line P((int bchar, const char *z1, const char *z2)); +static void uxadd_send_file P((const char *zfrom, const char *zto, + const char *zoptions, const char *ztemp, + const char *zforward, + const struct uuconf_system *qxqtsys, + const char *zxqtloc, + int bgrade)); +static void uxcopy_stdin P((FILE *e)); +static void uxrecord_file P((const char *zfile)); +static void uxabort P((void)); + +/* Long getopt options. */ +static const struct option asXlongopts[] = { { NULL, 0, NULL, 0 } }; + +/* The main routine. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + /* -a: requestor address for status reports. */ + const char *zrequestor = NULL; + /* -b: if true, return standard input on error. */ + boolean fretstdin = FALSE; + /* -c,-C: if true, copy to spool directory. */ + boolean fcopy = FALSE; + /* -c: set if -c appears explicitly; if it and -l appear, then if the + link fails we don't copy the file. */ + boolean fdontcopy = FALSE; + /* -I: configuration file name. */ + const char *zconfig = NULL; + /* -j: output job id. */ + boolean fjobid = FALSE; + /* -g: job grade. */ + char bgrade = BDEFAULT_UUX_GRADE; + /* -l: link file to spool directory. */ + boolean flink = FALSE; + /* -n: do not notify upon command completion. */ + boolean fno_ack = FALSE; + /* -p: read standard input for command standard input. */ + boolean fread_stdin = FALSE; + /* -r: do not start uucico when finished. */ + boolean fuucico = TRUE; + /* -s: report status to named file. */ + const char *zstatus_file = NULL; + /* -W: only expand local file names. */ + boolean fexpand = TRUE; + /* -z: report status only on error. */ + boolean ferror_ack = FALSE; + int iopt; + pointer puuconf; + int iuuconf; + const char *zlocalname; + const char *zxqtloc; + int i; + size_t clen; + char *zargs; + char *zarg; + char *zcmd; + const char *zsys; + char *zexclam; + boolean fgetcwd; + const char *zuser; + struct uuconf_system sxqtsys; + boolean fxqtlocal; + char *zforward; + char **pzargs; + int calloc_args; + int cargs; + char abxqt_tname[CFILE_NAME_LEN]; + char abxqt_xname[CFILE_NAME_LEN]; + const char *zinput_from; + const char *zinput_to; + const char *zinput_temp; + boolean finputcopied; + char *zcall_system; + boolean fcall_any; + struct uuconf_system slocalsys; + boolean fneedshell; + char *zfullcmd; + boolean fexit; + + /* We need to be able to read a single - as an option, which getopt + won't do. So that we can still use getopt, we run through the + options looking for an option "-"; if we find one we change it to + "-p", which is equivalent to "-". */ + for (i = 1; i < argc; i++) + { + if (argv[i][0] != '-') + break; + if (argv[i][1] == '\0') + argv[i] = zbufcpy ("-p"); + else + { + const char *z; + + for (z = argv[i] + 1; *z != '\0'; z++) + { + /* If the option takes an argument, and the argument is + not appended, then skip the next argument. */ + if (*z == 'a' || *z == 'g' || *z == 'I' + || *z == 's' || *z == 'x') + { + if (z[1] == '\0') + i++; + break; + } + } + } + } + + /* The leading + in the getopt string means to stop processing + options as soon as a non-option argument is seen. */ + while ((iopt = getopt_long (argc, argv, "+a:bcCg:I:jlnprs:Wx:z", + asXlongopts, (int *) NULL)) != EOF) + { + switch (iopt) + { + case 'a': + /* Set requestor name: mail address to which status reports + should be sent. */ + zrequestor = optarg; + break; + + case 'b': + /* Return standard input on error. */ + fretstdin = TRUE; + break; + + case 'c': + /* Do not copy local files to spool directory. */ + fcopy = FALSE; + fdontcopy = TRUE; + break; + + case 'C': + /* Copy local files to spool directory. */ + fcopy = TRUE; + break; + + case 'I': + /* Configuration file name. */ + if (fsysdep_other_config (optarg)) + zconfig = optarg; + break; + + case 'j': + /* Output jobid. */ + fjobid = TRUE; + break; + + case 'g': + /* Set job grade. */ + bgrade = optarg[0]; + break; + + case 'l': + /* Link file to spool directory. */ + flink = TRUE; + break; + + case 'n': + /* Do not notify upon command completion. */ + fno_ack = TRUE; + break; + + case 'p': + /* Read standard input for command standard input. */ + fread_stdin = TRUE; + break; + + case 'r': + /* Do not start uucico when finished. */ + fuucico = FALSE; + break; + + case 's': + /* Report status to named file. */ + zstatus_file = optarg; + break; + + case 'W': + /* Only expand local file names. */ + fexpand = FALSE; + break; + + case 'x': +#if DEBUG > 1 + /* Set debugging level. */ + iDebug |= idebug_parse (optarg); +#endif + break; + + case 'z': + /* Report status only on error. */ + ferror_ack = TRUE; + break; + + case 0: + /* Long option found and flag set. */ + break; + + default: + uxusage (); + break; + } + } + + if (! UUCONF_GRADE_LEGAL (bgrade)) + { + ulog (LOG_ERROR, "Ignoring illegal grade"); + bgrade = BDEFAULT_UUX_GRADE; + } + + if (optind == argc) + uxusage (); + + iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + +#if DEBUG > 1 + { + const char *zdebug; + + iuuconf = uuconf_debuglevel (puuconf, &zdebug); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + if (zdebug != NULL) + iDebug |= idebug_parse (zdebug); + } +#endif + + /* The command and files arguments could be quoted in any number of + ways, so we split them apart ourselves. We do this before + calling usysdep_initialize because we want to set fgetcwd + correctly. */ + clen = 1; + for (i = optind; i < argc; i++) + clen += strlen (argv[i]) + 1; + + zargs = zbufalc (clen); + *zargs = '\0'; + for (i = optind; i < argc; i++) + { + strcat (zargs, argv[i]); + strcat (zargs, " "); + } + + /* The first argument is the command to execute. */ + clen = strcspn (zargs, ZSHELLSEPS); + zcmd = zbufalc (clen + 1); + strncpy (zcmd, zargs, clen); + zcmd[clen] = '\0'; + zargs += clen; + + /* Split the arguments out into an array. We break the arguments + into alternating sequences of characters not in ZSHELLSEPS + and characters in ZSHELLSEPS. We remove whitespace. We + separate the redirection characters '>' and '<' into their + own arguments to make them easier to process below. */ + calloc_args = 10; + pzargs = (char **) xmalloc (calloc_args * sizeof (char *)); + cargs = 0; + + for (zarg = strtok (zargs, " \t"); + zarg != NULL; + zarg = strtok ((char *) NULL, " \t")) + { + while (*zarg != '\0') + { + if (cargs + 1 >= calloc_args) + { + calloc_args += 10; + pzargs = (char **) xrealloc ((pointer) pzargs, + calloc_args * sizeof (char *)); + } + + clen = strcspn (zarg, ZSHELLSEPS); + if (clen > 0) + { + pzargs[cargs] = zbufalc (clen + 1); + memcpy (pzargs[cargs], zarg, clen); + pzargs[cargs][clen] = '\0'; + ++cargs; + zarg += clen; + } + + /* We deliberately separate '>' and '<' out. */ + if (*zarg != '\0') + { + clen = strspn (zarg, ZSHELLNONREDIRSEPS); + if (clen == 0) + clen = 1; + pzargs[cargs] = zbufalc (clen + 1); + memcpy (pzargs[cargs], zarg, clen); + pzargs[cargs][clen] = '\0'; + ++cargs; + zarg += clen; + } + } + } + + /* Now look through the arguments to see if we are going to need the + current working directory. We don't try to make a precise + determination, just a conservative one. The basic idea is that + we don't want to get the cwd for 'foo!rmail - user' (note that we + don't examine the command itself). */ + fgetcwd = FALSE; + for (i = 0; i < cargs; i++) + { + if (pzargs[i][0] == '(') + continue; + zexclam = strrchr (pzargs[i], '!'); + if (zexclam != NULL && fsysdep_needs_cwd (zexclam + 1)) + { + fgetcwd = TRUE; + break; + } + if ((pzargs[i][0] == '<' || pzargs[i][0] == '>') + && i + 1 < cargs + && strchr (pzargs[i + 1], '!') == NULL + && fsysdep_needs_cwd (pzargs[i + 1])) + { + fgetcwd = TRUE; + break; + } + } + +#ifdef SIGINT + usysdep_signal (SIGINT); +#endif +#ifdef SIGHUP + usysdep_signal (SIGHUP); +#endif +#ifdef SIGQUIT + usysdep_signal (SIGQUIT); +#endif +#ifdef SIGTERM + usysdep_signal (SIGTERM); +#endif +#ifdef SIGPIPE + usysdep_signal (SIGPIPE); +#endif + + usysdep_initialize (puuconf, INIT_SUID | (fgetcwd ? INIT_GETCWD : 0)); + + ulog_fatal_fn (uxabort); + + zuser = zsysdep_login_name (); + + /* Get the local system name. */ + iuuconf = uuconf_localname (puuconf, &zlocalname); + if (iuuconf == UUCONF_NOT_FOUND) + { + zlocalname = zsysdep_localname (); + if (zlocalname == NULL) + exit (EXIT_FAILURE); + } + else if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + /* Get the local system information. */ + iuuconf = uuconf_system_info (puuconf, zlocalname, &slocalsys); + if (iuuconf != UUCONF_SUCCESS) + { + if (iuuconf != UUCONF_NOT_FOUND) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + iuuconf = uuconf_system_local (puuconf, &slocalsys); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + } + + /* Figure out which system the command is to be executed on. Some + mailers apparently pass local!rmail, so we must explicitly check + for that. */ + zexclam = strchr (zcmd, '!'); + while (zexclam != NULL) + { + *zexclam = '\0'; + if (strcmp (zcmd, zlocalname) == 0) + ; + else if (slocalsys.uuconf_pzalias == NULL) + break; + else + { + char **pzal; + + for (pzal = slocalsys.uuconf_pzalias; *pzal != NULL; pzal++) + if (strcmp (zcmd, *pzal) == 0) + break; + if (*pzal == NULL) + break; + } + zcmd = zexclam + 1; + zexclam = strchr (zcmd, '!'); + } + if (zexclam == NULL) + { + zsys = zlocalname; + fxqtlocal = TRUE; + zforward = NULL; + } + else + { + zsys = zcmd; + zcmd = zexclam + 1; + fxqtlocal = FALSE; + + /* See if we must forward this command through other systems + (e.g. uux a!b!cmd). */ + zexclam = strrchr (zcmd, '!'); + if (zexclam == NULL) + zforward = NULL; + else + { + clen = zexclam - zcmd; + zforward = zbufalc (clen); + memcpy (zforward, zcmd, clen); + zforward[clen] = '\0'; + zcmd = zexclam + 1; + } + } + + if (fxqtlocal) + sxqtsys = slocalsys; + else + { + iuuconf = uuconf_system_info (puuconf, zsys, &sxqtsys); + if (iuuconf != UUCONF_SUCCESS) + { + if (iuuconf != UUCONF_NOT_FOUND) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + if (! funknown_system (puuconf, zsys, &sxqtsys)) + ulog (LOG_FATAL, "%s: System not found", zsys); + } + } + + /* Get the local name the remote system know us as. */ + zxqtloc = sxqtsys.uuconf_zlocalname; + if (zxqtloc == NULL) + zxqtloc = zlocalname; + + /* We can send this as an E command if the execution is on a + different, directly connected, system and the only file used is + the standard input and comes from this system. This is true of + the common cases of rmail and rnews. We get an execute file name + here in case we need it. */ + if (fxqtlocal) + zXxqt_name = zsysdep_xqt_file_name (); + else + zXxqt_name = zsysdep_data_file_name (&sxqtsys, zxqtloc, bgrade, TRUE, + abxqt_tname, (char *) NULL, + abxqt_xname); + if (zXxqt_name == NULL) + uxabort (); + + uxrecord_file (zXxqt_name); + + /* Look through the arguments. Any argument containing an + exclamation point character is interpreted as a file name, and is + sent to the appropriate system. */ + zinput_from = NULL; + zinput_to = NULL; + zinput_temp = NULL; + finputcopied = FALSE; + zcall_system = NULL; + fcall_any = FALSE; + + for (i = 0; i < cargs; i++) + { + const char *zsystem; + char *zfile; + char *zforw; + boolean finput, foutput; + boolean flocal, fonxqt; + + /* Check for a parenthesized argument; remove the parentheses + and otherwise ignore it (this is how an exclamation point is + quoted). */ + if (pzargs[i][0] == '(') + { + clen = strlen (pzargs[i]); + if (pzargs[i][clen - 1] != ')') + ulog (LOG_ERROR, "Mismatched parentheses"); + else + pzargs[i][clen - 1] = '\0'; + ++pzargs[i]; + continue; + } + + /* Check whether we are doing a redirection. */ + finput = FALSE; + foutput = FALSE; + if (i + 1 < cargs) + { + if (pzargs[i][0] == '<') + finput = TRUE; + else if (pzargs[i][0] == '>') + foutput = TRUE; + if (finput || foutput) + { + pzargs[i] = NULL; + i++; + } + } + + zexclam = strchr (pzargs[i], '!'); + + /* If there is no exclamation point and no redirection, this + argument is left untouched. */ + if (zexclam == NULL && ! finput && ! foutput) + continue; + + /* Get the system name and file name for this file. */ + if (zexclam == NULL) + { + zsystem = zlocalname; + zfile = pzargs[i]; + flocal = TRUE; + zforw = NULL; + } + else + { + *zexclam = '\0'; + zsystem = pzargs[i]; + if (*zsystem != '\0') + flocal = FALSE; + else + { + zsystem = zlocalname; + flocal = TRUE; + } + zfile = zexclam + 1; + zexclam = strrchr (zfile, '!'); + if (zexclam == NULL) + zforw = NULL; + else + { + if (flocal) + ulog (LOG_FATAL, "!%s: Can't figure out where to get file", + zfile); + *zexclam = '\0'; + zforw = zfile; + zfile = zexclam + 1; + } + } + + /* Check if the file is already on the execution system. */ + if (flocal) + fonxqt = fxqtlocal; + else if (fxqtlocal) + fonxqt = FALSE; + else if (zforward == NULL ? zforw != NULL : zforw == NULL) + fonxqt = FALSE; + else if (zforward != NULL + && zforw != NULL + && strcmp (zforward, zforw) != 0) + fonxqt = FALSE; + else if (strcmp (zsystem, sxqtsys.uuconf_zname) == 0) + fonxqt = TRUE; + else if (sxqtsys.uuconf_pzalias == NULL) + fonxqt = FALSE; + else + { + char **pzal; + + fonxqt = FALSE; + for (pzal = sxqtsys.uuconf_pzalias; *pzal != NULL; pzal++) + { + if (strcmp (zsystem, *pzal) == 0) + { + fonxqt = TRUE; + break; + } + } + } + + /* Turn the file into an absolute path. */ + if (flocal) + zfile = zsysdep_local_file_cwd (zfile, sxqtsys.uuconf_zpubdir); + else if (fexpand) + zfile = zsysdep_add_cwd (zfile); + if (zfile == NULL) + uxabort (); + + /* Check for output redirection. */ + if (foutput) + { + if (flocal) + { + if (! fin_directory_list (zfile, + sxqtsys.uuconf_pzremote_receive, + sxqtsys.uuconf_zpubdir, TRUE, + FALSE, (const char *) NULL)) + ulog (LOG_FATAL, "Not permitted to create %s", zfile); + } + + /* There are various cases of output redirection. + + uux cmd >out: The command is executed on the local + system, and the output file is placed on the local + system (fonxqt is TRUE). + + uux cmd >a!out: The command is executed on the local + system, and the output file is sent to a. + + uux a!cmd >out: The command is executed on a, and the + output file is returned to the local system (flocal + is TRUE). + + uux a!cmd >a!out: The command is executed on a, and the + output file is left on a (fonxqt is TRUE). + + uux a!cmd >b!out: The command is executed on a, and the + output file is sent to b; traditionally, I believe + that b is relative to a, rather than to the local + system. However, this essentially contradicts the + previous two cases, in which the output file is + relative to the local system. + + Now, the cases that we don't handle. + + uux cmd >a!b!out: The command is executed on the local + system, and the output file is sent to b via a. This + requires the local uuxqt to support forwarding of the + output file. + + uux a!b!cmd >out: The command is executed on b, which is + reached via a. Probably the output file is intended + for the local system, in which case the uuxqt on b + must support forwarding of the output file. + + uux a!b!cmd >c!out: Is c relative to b or to the local + system? If it's relative to b this is easy to + handle. Otherwise, we must arrange for the file to + be sent back to the local system and for the local + system to send it on to c. + + There are many variations of the last case. It's not at + all clear to me how they should be handled. */ + if (zforward != NULL || zforw != NULL) + ulog (LOG_FATAL, "May not forward standard output"); + + if (fonxqt) + uxadd_xqt_line ('O', zfile, (const char *) NULL); + else if (flocal) + uxadd_xqt_line ('O', zfile, zxqtloc); + else + uxadd_xqt_line ('O', zfile, zsystem); + pzargs[i] = NULL; + continue; + } + + if (finput) + { + if (fread_stdin) + ulog (LOG_FATAL, "Standard input specified twice"); + pzargs[i] = NULL; + } + + if (flocal) + { + char *zuse; + char *zdata; + char abtname[CFILE_NAME_LEN]; + char abdname[CFILE_NAME_LEN]; + + /* It's a local file. If requested by -C, copy the file to + the spool directory. If requested by -l, link the file + to the spool directory; if the link fails, we copy the + file, unless -c was explictly used. If the execution is + occurring on the local system, we force the copy as well, + because otherwise we would have to have some way to tell + uuxqt not to move the file. If the file is being shipped + to another system, we must set up a transfer request. + First make sure the user has legitimate access, since we + are running setuid. */ + if (! fsysdep_access (zfile)) + uxabort (); + + zdata = zsysdep_data_file_name (&sxqtsys, zxqtloc, bgrade, FALSE, + abtname, abdname, (char *) NULL); + if (zdata == NULL) + uxabort (); + + if (fcopy || flink || fxqtlocal) + { + boolean fdid; + + uxrecord_file (zdata); + + fdid = FALSE; + if (flink) + { + boolean fworked; + + if (! fsysdep_link (zfile, zdata, &fworked)) + uxabort (); + + if (fworked) + fdid = TRUE; + else if (fdontcopy) + ulog (LOG_FATAL, "%s: Can't link to spool directory", + zfile); + } + + if (! fdid) + { + openfile_t efile; + + efile = esysdep_user_fopen (zfile, TRUE, TRUE); + if (! ffileisopen (efile)) + uxabort (); + if (! fcopy_open_file (efile, zdata, FALSE, TRUE)) + uxabort (); + (void) ffileclose (efile); + } + + zuse = abtname; + } + else + { + /* We don't actually use the spool file name, but we + need a name to use as the destination. */ + ubuffree (zdata); + /* Make sure the daemon can access the file. */ + if (! fsysdep_daemon_access (zfile)) + uxabort (); + if (! fin_directory_list (zfile, sxqtsys.uuconf_pzlocal_send, + sxqtsys.uuconf_zpubdir, TRUE, + TRUE, zuser)) + ulog (LOG_FATAL, "Not permitted to send from %s", + zfile); + + zuse = zfile; + } + + if (fxqtlocal) + { + if (finput) + uxadd_xqt_line ('I', zuse, (char *) NULL); + else + pzargs[i] = zuse; + } + else + { + finputcopied = fcopy || flink; + + if (finput) + { + zinput_from = zuse; + zinput_to = zbufcpy (abdname); + zinput_temp = zbufcpy (abtname); + } + else + { + char *zbase; + + uxadd_send_file (zuse, abdname, + finputcopied ? "C" : "c", + abtname, zforward, &sxqtsys, + zxqtloc, bgrade); + zbase = zsysdep_base_name (zfile); + if (zbase == NULL) + uxabort (); + uxadd_xqt_line ('F', abdname, zbase); + pzargs[i] = zbase; + } + } + } + else if (fonxqt) + { + /* The file is already on the system where the command is to + be executed. */ + if (finput) + uxadd_xqt_line ('I', zfile, (const char *) NULL); + else + pzargs[i] = zfile; + } + else + { + struct uuconf_system sfromsys; + char abtname[CFILE_NAME_LEN]; + struct scmd s; + char *zjobid; + + /* We need to request a remote file. */ + iuuconf = uuconf_system_info (puuconf, zsystem, &sfromsys); + if (iuuconf != UUCONF_SUCCESS) + { + if (iuuconf != UUCONF_NOT_FOUND) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + if (! funknown_system (puuconf, zsystem, &sfromsys)) + ulog (LOG_FATAL, "%s: System not found", zsystem); + } + + if (fonxqt) + { + /* The file is already on the system where the command is to + be executed. */ + if (finput) + uxadd_xqt_line ('I', zfile, (const char *) NULL); + else + pzargs[i] = zfile; + } + else + { + char *zdata; + + if (! sfromsys.uuconf_fcall_transfer + && ! sfromsys.uuconf_fcalled_transfer) + ulog (LOG_FATAL, + "Not permitted to transfer files to or from %s", + sfromsys.uuconf_zname); + + if (zforw != NULL) + { + /* This is ``uux cmd a!b!file''. To make this work, + we would have to be able to set up a request to a + to fetch file from b and send it to us. But it + turns out that that will not work, because when a + sends us the file we will put it in a's spool + directory, not the local system spool directory. + So we won't have any way to find it. This is not + a conceptual problem, and it could doubtless be + solved. Please feel free to solve it and send me + the solution. */ + ulog (LOG_FATAL, "File forwarding not supported"); + } + + /* We must request the file from the remote system to + this one. */ + zdata = zsysdep_data_file_name (&slocalsys, zxqtloc, bgrade, + FALSE, abtname, (char *) NULL, + (char *) NULL); + if (zdata == NULL) + uxabort (); + ubuffree (zdata); + + /* Request the file. The special option '9' is a signal + to uucico that it's OK to receive a file into the + spool directory; normally such requests are rejected. + This privilege is easy to abuse. */ + s.bcmd = 'R'; + s.pseq = NULL; + s.zfrom = zfile; + s.zto = zbufcpy (abtname); + s.zuser = zuser; + s.zoptions = "9"; + s.ztemp = ""; + s.imode = 0600; + s.znotify = ""; + s.cbytes = -1; + s.zcmd = NULL; + s.ipos = 0; + + zjobid = zsysdep_spool_commands (&sfromsys, bgrade, 1, &s); + if (zjobid == NULL) + uxabort (); + + if (fjobid) + printf ("%s\n", zjobid); + + ubuffree (zjobid); + + if (fcall_any) + { + ubuffree (zcall_system); + zcall_system = NULL; + } + else + { + fcall_any = TRUE; + zcall_system = zbufcpy (sfromsys.uuconf_zname); + } + + if (fxqtlocal) + { + /* Tell the command execution to wait until the file + has been received, and tell it the real file + name. */ + if (finput) + { + uxadd_xqt_line ('F', abtname, (char *) NULL); + uxadd_xqt_line ('I', abtname, (char *) NULL); + } + else + { + char *zbase; + + zbase = zsysdep_base_name (zfile); + if (zbase == NULL) + uxabort (); + uxadd_xqt_line ('F', abtname, zbase); + pzargs[i] = zbase; + } + } + else + { + char abxtname[CFILE_NAME_LEN]; + char *zbase; + char *zxqt; + FILE *e; + + /* Now we must arrange to forward the file on to the + execution system. We need to get a name to give + the file on the execution system (abxtname). */ + zdata = zsysdep_data_file_name (&sxqtsys, zxqtloc, + bgrade, TRUE, abxtname, + (char *) NULL, + (char *) NULL); + if (zdata == NULL) + uxabort (); + ubuffree (zdata); + + zbase = zsysdep_base_name (zfile); + if (zbase == NULL) + uxabort (); + + zxqt = zsysdep_xqt_file_name (); + if (zxqt == NULL) + uxabort (); + e = esysdep_fopen (zxqt, FALSE, FALSE, TRUE); + if (e == NULL) + uxabort (); + uxrecord_file (zxqt); + + fprintf (e, "U %s %s\n", zsysdep_login_name (), + zlocalname); + fprintf (e, "F %s %s\n", abtname, zbase); + fprintf (e, "C uucp -C -W -d -g %c %s %s!", bgrade, + zbase, sxqtsys.uuconf_zname); + if (zforward != NULL) + fprintf (e, "%s!", zforward); + fprintf (e, "%s\n", abxtname); + + if (fclose (e) != 0) + ulog (LOG_FATAL, "fclose: %s", strerror (errno)); + + if (finput) + { + uxadd_xqt_line ('F', abxtname, (char *) NULL); + uxadd_xqt_line ('I', abxtname, (char *) NULL); + ubuffree (zbase); + } + else + { + uxadd_xqt_line ('F', abxtname, zbase); + pzargs[i] = zbase; + } + } + } + + (void) uuconf_system_free (puuconf, &sfromsys); + } + } + + /* If standard input is to be read from the stdin of uux, we read it + here into a temporary file and send it to the execute system. */ + if (fread_stdin) + { + char *zdata; + char abtname[CFILE_NAME_LEN]; + char abdname[CFILE_NAME_LEN]; + FILE *e; + + zdata = zsysdep_data_file_name (&sxqtsys, zxqtloc, bgrade, FALSE, + abtname, abdname, (char *) NULL); + if (zdata == NULL) + uxabort (); + + e = esysdep_fopen (zdata, FALSE, FALSE, TRUE); + if (e == NULL) + uxabort (); + + eXclose = e; + uxrecord_file (zdata); + + uxcopy_stdin (e); + + eXclose = NULL; + if (fclose (e) != 0) + ulog (LOG_FATAL, "fclose: %s", strerror (errno)); + + if (fxqtlocal) + uxadd_xqt_line ('I', abtname, (const char *) NULL); + else + { + zinput_from = zbufcpy (abtname); + zinput_to = zbufcpy (abdname); + zinput_temp = zinput_from; + finputcopied = TRUE; + } + } + + /* If we are returning standard input, or we're putting the status + in a file, we can't use an E command. */ + if (fretstdin) + uxadd_xqt_line ('B', (const char *) NULL, (const char *) NULL); + + if (zstatus_file != NULL) + uxadd_xqt_line ('M', zstatus_file, (const char *) NULL); + + /* Get the complete command line, and decide whether the command + needs to be executed by the shell. */ + fneedshell = FALSE; + + if (zcmd[strcspn (zcmd, ZSHELLCHARS)] != '\0') + fneedshell = TRUE; + + clen = strlen (zcmd) + 1; + for (i = 0; i < cargs; i++) + { + if (pzargs[i] != NULL) + { + clen += strlen (pzargs[i]) + 1; + if (pzargs[i][strcspn (pzargs[i], ZSHELLCHARS)] != '\0') + fneedshell = TRUE; + } + } + + zfullcmd = zbufalc (clen); + + strcpy (zfullcmd, zcmd); + for (i = 0; i < cargs; i++) + { + if (pzargs[i] != NULL) + { + strcat (zfullcmd, " "); + strcat (zfullcmd, pzargs[i]); + } + } + + /* If we haven't written anything to the execution file yet, and we + have a standard input file, and we're not forwarding, then every + other option can be handled in an E command. */ + if (eXxqt_file == NULL && zinput_from != NULL && zforward == NULL) + { + struct scmd s; + char aboptions[10]; + char *zoptions; + + /* Set up an E command. */ + s.bcmd = 'E'; + s.pseq = NULL; + s.zuser = zuser; + s.zfrom = zinput_from; + s.zto = zinput_to; + s.zoptions = aboptions; + zoptions = aboptions; + *zoptions++ = finputcopied ? 'C' : 'c'; + if (fno_ack) + *zoptions++ = 'N'; + if (ferror_ack) + *zoptions++ = 'Z'; + if (zrequestor != NULL) + *zoptions++ = 'R'; + if (fneedshell) + *zoptions++ = 'e'; + *zoptions = '\0'; + s.ztemp = zinput_temp; + s.imode = 0666; + if (zrequestor == NULL) + zrequestor = "\"\""; + s.znotify = zrequestor; + s.cbytes = -1; + s.zcmd = zfullcmd; + s.ipos = 0; + + ++cXcmds; + pasXcmds = (struct scmd *) xrealloc ((pointer) pasXcmds, + cXcmds * sizeof (struct scmd)); + pasXcmds[cXcmds - 1] = s; + } + else + { + /* Finish up the execute file. */ + uxadd_xqt_line ('U', zuser, zxqtloc); + if (zinput_from != NULL) + { + uxadd_xqt_line ('F', zinput_to, (char *) NULL); + uxadd_xqt_line ('I', zinput_to, (char *) NULL); + uxadd_send_file (zinput_from, zinput_to, + finputcopied ? "C" : "c", + zinput_temp, zforward, &sxqtsys, zxqtloc, + bgrade); + } + if (fno_ack) + uxadd_xqt_line ('N', (const char *) NULL, (const char *) NULL); + if (ferror_ack) + uxadd_xqt_line ('Z', (const char *) NULL, (const char *) NULL); + if (zrequestor != NULL) + uxadd_xqt_line ('R', zrequestor, (const char *) NULL); + if (fneedshell) + uxadd_xqt_line ('e', (const char *) NULL, (const char *) NULL); + uxadd_xqt_line ('C', zfullcmd, (const char *) NULL); + if (fclose (eXxqt_file) != 0) + ulog (LOG_FATAL, "fclose: %s", strerror (errno)); + eXxqt_file = NULL; + + /* If the execution is to occur on another system, we must now + arrange to copy the execute file to this system. */ + if (! fxqtlocal) + uxadd_send_file (abxqt_tname, abxqt_xname, "C", abxqt_tname, + zforward, &sxqtsys, zxqtloc, bgrade); + } + + /* If we got a signal, get out before spooling anything. */ + if (FGOT_SIGNAL ()) + uxabort (); + + /* From here on in, it's too late. We don't call uxabort. */ + if (cXcmds > 0) + { + char *zjobid; + + if (! sxqtsys.uuconf_fcall_transfer + && ! sxqtsys.uuconf_fcalled_transfer) + ulog (LOG_FATAL, "Not permitted to transfer files to or from %s", + sxqtsys.uuconf_zname); + + zjobid = zsysdep_spool_commands (&sxqtsys, bgrade, cXcmds, pasXcmds); + if (zjobid == NULL) + { + ulog_close (); + usysdep_exit (FALSE); + } + + if (fjobid) + printf ("%s\n", zjobid); + + ubuffree (zjobid); + + if (fcall_any) + { + ubuffree (zcall_system); + zcall_system = NULL; + } + else + { + fcall_any = TRUE; + zcall_system = zbufcpy (sxqtsys.uuconf_zname); + } + } + + /* If all that worked, make a log file entry. All log file reports + up to this point went to stderr. */ + ulog_to_file (puuconf, TRUE); + ulog_system (sxqtsys.uuconf_zname); + ulog_user (zuser); + + ulog (LOG_NORMAL, "Queuing %s", zfullcmd); + + ulog_close (); + + if (! fuucico) + fexit = TRUE; + else + { + if (zcall_system != NULL) + fexit = fsysdep_run ("uucico", "-s", zcall_system); + else if (fcall_any) + fexit = fsysdep_run ("uucico", "-r1", (const char *) NULL); + else + fexit = TRUE; + } + + usysdep_exit (fexit); + + /* Avoid error about not returning a value. */ + return 0; +} + +/* Report command usage. */ + +static void +uxusage () +{ + fprintf (stderr, + "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n", + VERSION); + fprintf (stderr, + "Usage: uux [options] [-] command\n"); + fprintf (stderr, + " -,-p: Read standard input for standard input of command\n"); + fprintf (stderr, + " -c: Do not copy local files to spool directory (default)\n"); + fprintf (stderr, + " -C: Copy local files to spool directory\n"); + fprintf (stderr, + " -l: link local files to spool directory\n"); + fprintf (stderr, + " -g grade: Set job grade (must be alphabetic)\n"); + fprintf (stderr, + " -n: Do not report completion status\n"); + fprintf (stderr, + " -z: Report completion status only on error\n"); + fprintf (stderr, + " -r: Do not start uucico daemon\n"); + fprintf (stderr, + " -a address: Address to mail status report to\n"); + fprintf (stderr, + " -b: Return standard input with status report\n"); + fprintf (stderr, + " -s file: Report completion status to file\n"); + fprintf (stderr, + " -j: Report job id\n"); + fprintf (stderr, + " -x debug: Set debugging level\n"); +#if HAVE_TAYLOR_CONFIG + fprintf (stderr, + " -I file: Set configuration file to use\n"); +#endif /* HAVE_TAYLOR_CONFIG */ + exit (EXIT_FAILURE); +} + +/* Add a line to the execute file. */ + +static void +uxadd_xqt_line (bchar, z1, z2) + int bchar; + const char *z1; + const char *z2; +{ + if (eXxqt_file == NULL) + { + eXxqt_file = esysdep_fopen (zXxqt_name, FALSE, FALSE, TRUE); + if (eXxqt_file == NULL) + uxabort (); + } + + if (z1 == NULL) + fprintf (eXxqt_file, "%c\n", bchar); + else if (z2 == NULL) + fprintf (eXxqt_file, "%c %s\n", bchar, z1); + else + fprintf (eXxqt_file, "%c %s %s\n", bchar, z1, z2); +} + +/* Add a file to be sent to the execute system. */ + +static void +uxadd_send_file (zfrom, zto, zoptions, ztemp, zforward, qxqtsys, zxqtloc, + bgrade) + const char *zfrom; + const char *zto; + const char *zoptions; + const char *ztemp; + const char *zforward; + const struct uuconf_system *qxqtsys; + const char *zxqtloc; + int bgrade; +{ + struct scmd s; + + if (zforward != NULL) + { + char *zbase; + char *zxqt; + char abtname[CFILE_NAME_LEN]; + char abdname[CFILE_NAME_LEN]; + char abxname[CFILE_NAME_LEN]; + FILE *e; + + /* We want to forward this file through the first execution + system to other systems. We set up a remote execution of + uucp to forward the file. */ + zbase = zsysdep_base_name (zfrom); + if (zbase == NULL) + uxabort (); + + zxqt = zsysdep_data_file_name (qxqtsys, zxqtloc, bgrade, TRUE, abtname, + abdname, abxname); + if (zxqt == NULL) + uxabort (); + e = esysdep_fopen (zxqt, FALSE, FALSE, TRUE); + if (e == NULL) + uxabort (); + uxrecord_file (zxqt); + + fprintf (e, "U %s %s\n", zsysdep_login_name (), zxqtloc); + fprintf (e, "F %s %s\n", abdname, zbase); + fprintf (e, "C uucp -C -W -d -g %c %s %s!%s\n", + bgrade, zbase, zforward, zto); + + ubuffree (zbase); + + if (fclose (e) != 0) + ulog (LOG_FATAL, "fclose: %s", strerror (errno)); + + /* Send the execution file. */ + s.bcmd = 'S'; + s.pseq = NULL; + s.zfrom = zbufcpy (abtname); + s.zto = zbufcpy (abxname); + s.zuser = zsysdep_login_name (); + s.zoptions = "C"; + s.ztemp = s.zfrom; + s.imode = 0666; + s.znotify = NULL; + s.cbytes = -1; + s.zcmd = NULL; + s.ipos = 0; + + ++cXcmds; + pasXcmds = (struct scmd *) xrealloc ((pointer) pasXcmds, + cXcmds * sizeof (struct scmd)); + pasXcmds[cXcmds - 1] = s; + + /* Send the data file to abdname where the execution file will + expect it. */ + zto = abdname; + } + + s.bcmd = 'S'; + s.pseq = NULL; + s.zfrom = zbufcpy (zfrom); + s.zto = zbufcpy (zto); + s.zuser = zsysdep_login_name (); + s.zoptions = zbufcpy (zoptions); + s.ztemp = zbufcpy (ztemp); + s.imode = 0666; + s.znotify = ""; + s.cbytes = -1; + s.zcmd = NULL; + s.ipos = 0; + + ++cXcmds; + pasXcmds = (struct scmd *) xrealloc ((pointer) pasXcmds, + cXcmds * sizeof (struct scmd)); + pasXcmds[cXcmds - 1] = s; +} + +/* Copy stdin to a file. This is a separate function because it may + call setjmp. */ + +static void +uxcopy_stdin (e) + FILE *e; +{ + CATCH_PROTECT size_t cread; + char ab[1024]; + + do + { + size_t cwrite; + + /* I want to use fread here, but there is a bug in some versions + of SVR4 which causes fread to return less than a complete + buffer even if EOF has not been reached. This is not online + time, so speed is not critical, but it's still quite annoying + to have to use an inefficient algorithm. */ + cread = 0; + if (fsysdep_catch ()) + { + usysdep_start_catch (); + + while (cread < sizeof (ab)) + { + int b; + + if (FGOT_SIGNAL ()) + uxabort (); + + /* There's an unimportant race here. If the user hits + ^C between the FGOT_SIGNAL we just did and the time + we enter getchar, we won't know about the signal + (unless we're doing a longjmp, but we normally + aren't). It's not a big problem, because the user + can just hit ^C again. */ + b = getchar (); + if (b == EOF) + break; + ab[cread] = b; + ++cread; + } + } + + usysdep_end_catch (); + + if (FGOT_SIGNAL ()) + uxabort (); + + if (cread > 0) + { + cwrite = fwrite (ab, sizeof (char), cread, e); + if (cwrite != cread) + ulog (LOG_FATAL, "fwrite: Wrote %d when attempted %d", + (int) cwrite, (int) cread); + } + } + while (cread == sizeof ab); +} + +/* Keep track of all files we have created so that we can delete them + if we get a signal. The argument will be on the heap. */ + +static int cXfiles; +static const char **pXaz; + +static void +uxrecord_file (zfile) + const char *zfile; +{ + pXaz = (const char **) xrealloc ((pointer) pXaz, + (cXfiles + 1) * sizeof (const char *)); + pXaz[cXfiles] = zfile; + ++cXfiles; +} + +/* Delete all the files we have recorded and exit. */ + +static void +uxabort () +{ + int i; + + if (eXxqt_file != NULL) + (void) fclose (eXxqt_file); + if (eXclose != NULL) + (void) fclose (eXclose); + for (i = 0; i < cXfiles; i++) + (void) remove (pXaz[i]); + ulog_close (); + usysdep_exit (FALSE); +} diff --git a/gnu/libexec/uucp/uuxqt/Makefile b/gnu/libexec/uucp/uuxqt/Makefile new file mode 100644 index 0000000000..b34c639fbc --- /dev/null +++ b/gnu/libexec/uucp/uuxqt/Makefile @@ -0,0 +1,18 @@ +# Makefile for uuxqt +# $Id: Makefile,v 1.1 1993/08/05 18:28:24 conklin Exp $ + +BINDIR= $(sbindir) +BINOWN= $(owner) +BINMODE= 4555 + +PROG= uuxqt +SRCS= uuxqt.c util.c log.c copy.c +LDADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +DPADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +CFLAGS+= -I$(.CURDIR)/../common_sources\ + -DVERSION=\"$(VERSION)\" + +MAN8= uuxqt.8 + +.include +.PATH: $(.CURDIR)/../common_sources diff --git a/gnu/libexec/uucp/uuxqt/uuxqt.8 b/gnu/libexec/uucp/uuxqt/uuxqt.8 new file mode 100644 index 0000000000..faee1cf7a8 --- /dev/null +++ b/gnu/libexec/uucp/uuxqt/uuxqt.8 @@ -0,0 +1,92 @@ +''' $Id: uuxqt.8,v 1.1 1993/08/04 19:37:18 jtc Exp $ +.TH uuxqt 8 "Taylor UUCP 1.04" +.SH NAME +uuxqt \- UUCP execution daemon +.SH SYNOPSIS +.B uuxqt +[ options ] +.SH DESCRIPTION +The +.I uuxqt +daemon executes commands requested by +.I uux +(1) from either the local system or from remote systems. +It is started automatically by the +.I uucico +(8) daemon (unless +.I uucico +(8) is given the +.B \-q +option). + +There is normally no need to run this command, since it will be +invoked by +.I uucico +(8). However, it can be used to provide greater control over the +processing of the work queue. + +Multiple invocations of +.I uuxqt +may be run at once, as controlled by the +.I max-uuxqts +configuration command. +.SH OPTIONS +The following options may be given to +.I uuxqt. +.TP 5 +.B \-c command +Only execute requests for the specified command. For example: +.EX +uuxqt -c rmail +.EE +.TP 5 +.B \-s system +Only execute requests originating from the specified system. +.TP 5 +.B \-x type +Turn on particular debugging types. The following types are +recognized: abnormal, chat, handshake, uucp-proto, proto, port, +config, spooldir, execute, incoming, outgoing. Only abnormal, config, +spooldir and execute are meaningful for +.I uuxqt. + +Multiple types may be given, separated by commas, and the +.B \-x +option may appear multiple times. A number may also be given, which +will turn on that many types from the foregoing list; for example, +.B \-x 2 +is equivalent to +.B \-x abnormal,chat. + +The debugging output is sent to the debugging file, usually one of +/usr/spool/uucp/Debug, /usr/spool/uucp/DEBUG, or +/usr/spool/uucp/.Admin/audit.local. +.TP 5 +.B \-I file +Set configuration file to use. This option may not be available, +depending upon how +.I uuxqt +was compiled. +.SH FILES +The file names may be changed at compilation time or by the +configuration file, so these are only approximations. + +.br +/usr/lib/uucp/config - Configuration file. +.br +/usr/spool/uucp - +UUCP spool directory. +.br +/usr/spool/uucp/Log - +UUCP log file. +.br +/usr/spool/uucppublic - +Default UUCP public directory. +.br +/usr/spool/uucp/Debug - +Debugging file. +.SH SEE ALSO +uucp(1), uux(1), uucico(8) +.SH AUTHOR +Ian Lance Taylor +(ian@airs.com or uunet!airs!ian) diff --git a/gnu/libexec/uucp/uuxqt/uuxqt.c b/gnu/libexec/uucp/uuxqt/uuxqt.c new file mode 100644 index 0000000000..919ea7ecd4 --- /dev/null +++ b/gnu/libexec/uucp/uuxqt/uuxqt.c @@ -0,0 +1,1549 @@ +/* uuxqt.c + Run uux commands. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char uuxqt_rcsid[] = "$Id: uuxqt.c,v 1.1 1993/08/04 19:37:19 jtc Exp $"; +#endif + +#include +#include + +#include "getopt.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" + +/* The program name. */ +char abProgram[] = "uuxqt"; + +/* Static variables used to unlock things if we get a fatal error. */ +static int iQlock_seq = -1; +static const char *zQunlock_cmd; +static const char *zQunlock_file; +static boolean fQunlock_directory; +int cQmaxuuxqts; + +/* Static variables to free in uqcleanup. */ +static char *zQoutput; +static char *zQmail; + +/* Local functions. */ +static void uqusage P((void)); +static void uqabort P((void)); +static void uqdo_xqt_file P((pointer puuconf, const char *zfile, + const char *zbase, + const struct uuconf_system *qsys, + const char *zlocalname, + const char *zcmd, boolean *pfprocessed)); +static void uqcleanup P((const char *zfile, int iflags)); +static boolean fqforward P((const char *zfile, char **pzallowed, + const char *zlog, const char *zmail)); + +/* Long getopt options. */ +static const struct option asQlongopts[] = { { NULL, 0, NULL, 0 } }; + +int +main (argc, argv) + int argc; + char **argv; +{ + /* The type of command to execute (NULL for any type). */ + const char *zcmd = NULL; + /* The configuration file name. */ + const char *zconfig = NULL; + /* The system to execute commands for. */ + const char *zdosys = NULL; + int iopt; + pointer puuconf; + int iuuconf; + const char *zlocalname; + boolean fany; + char *z, *zgetsys; + boolean ferr; + boolean fsys; + struct uuconf_system ssys; + + while ((iopt = getopt_long (argc, argv, "c:I:s:x:", asQlongopts, + (int *) NULL)) != EOF) + { + switch (iopt) + { + case 'c': + /* Set the type of command to execute. */ + zcmd = optarg; + break; + + case 'I': + /* Set the configuration file name. */ + if (fsysdep_other_config (optarg)) + zconfig = optarg; + break; + + case 's': + zdosys = optarg; + break; + + case 'x': +#if DEBUG > 1 + /* Set the debugging level. */ + iDebug |= idebug_parse (optarg); +#endif + break; + + case 0: + /* Long option found and flag set. */ + break; + + default: + uqusage (); + break; + } + } + + if (optind != argc) + uqusage (); + + iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + +#if DEBUG > 1 + { + const char *zdebug; + + iuuconf = uuconf_debuglevel (puuconf, &zdebug); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + if (zdebug != NULL) + iDebug |= idebug_parse (zdebug); + } +#endif + + iuuconf = uuconf_maxuuxqts (puuconf, &cQmaxuuxqts); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + +#ifdef SIGINT + usysdep_signal (SIGINT); +#endif +#ifdef SIGHUP + usysdep_signal (SIGHUP); +#endif +#ifdef SIGQUIT + usysdep_signal (SIGQUIT); +#endif +#ifdef SIGTERM + usysdep_signal (SIGTERM); +#endif +#ifdef SIGPIPE + usysdep_signal (SIGPIPE); +#endif + + usysdep_initialize (puuconf, INIT_SUID); + + ulog_to_file (puuconf, TRUE); + ulog_fatal_fn (uqabort); + + iuuconf = uuconf_localname (puuconf, &zlocalname); + if (iuuconf == UUCONF_NOT_FOUND) + { + zlocalname = zsysdep_localname (); + if (zlocalname == NULL) + exit (EXIT_FAILURE); + } + else if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + fsys = FALSE; + + /* If we were given a system name, canonicalize it, since the system + dependent layer will not be returning aliases. */ + if (zdosys != NULL) + { + iuuconf = uuconf_system_info (puuconf, zdosys, &ssys); + if (iuuconf == UUCONF_NOT_FOUND) + ulog (LOG_FATAL, "%s: System not found", zdosys); + else if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + zdosys = zbufcpy (ssys.uuconf_zname); + fsys = TRUE; + } + + /* Limit the number of uuxqt processes, and make sure we're the only + uuxqt daemon running for this command. */ + iQlock_seq = ixsysdep_lock_uuxqt (zcmd, cQmaxuuxqts); + if (iQlock_seq < 0) + { + ulog_close (); + usysdep_exit (TRUE); + } + zQunlock_cmd = zcmd; + + /* Keep scanning the execute files until we don't process any of + them. */ + do + { + fany = FALSE; + + /* Look for each execute file, and run it. */ + + if (! fsysdep_get_xqt_init ()) + { + ulog_close (); + usysdep_exit (FALSE); + } + + while ((z = zsysdep_get_xqt (&zgetsys, &ferr)) != NULL) + { + const char *zloc; + boolean fprocessed; + char *zbase; + + /* It would be more efficient to pass zdosys down to the + routines which retrieve execute files. */ + if (zdosys != NULL && strcmp (zdosys, zgetsys) != 0) + { + ubuffree (z); + ubuffree (zgetsys); + continue; + } + + if (! fsys || strcmp (ssys.uuconf_zname, zgetsys) != 0) + { + if (fsys) + (void) uuconf_system_free (puuconf, &ssys); + + iuuconf = uuconf_system_info (puuconf, zgetsys, + &ssys); + if (iuuconf != UUCONF_SUCCESS) + { + if (iuuconf != UUCONF_NOT_FOUND) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + ubuffree (z); + ubuffree (zgetsys); + continue; + } + else if (strcmp (zgetsys, zlocalname) == 0) + { + iuuconf = uuconf_system_local (puuconf, &ssys); + if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + ubuffree (z); + ubuffree (zgetsys); + continue; + } + } + else + { + if (! funknown_system (puuconf, zgetsys, &ssys)) + { + ulog (LOG_ERROR, + "%s: Execute file for unknown system %s", + z, zgetsys); + (void) remove (z); + ubuffree (z); + ubuffree (zgetsys); + continue; + } + } + } + + fsys = TRUE; + } + + /* If we've received a signal, get out of the loop. */ + if (FGOT_SIGNAL ()) + { + ubuffree (z); + ubuffree (zgetsys); + break; + } + + zloc = ssys.uuconf_zlocalname; + if (zloc == NULL) + zloc = zlocalname; + + ulog_system (ssys.uuconf_zname); + zbase = zsysdep_base_name (z); + uqdo_xqt_file (puuconf, z, zbase, &ssys, zloc, zcmd, &fprocessed); + ubuffree (zbase); + ulog_system ((const char *) NULL); + ulog_user ((const char *) NULL); + + if (fprocessed) + fany = TRUE; + ubuffree (z); + ubuffree (zgetsys); + } + + usysdep_get_xqt_free (); + } + while (fany && ! FGOT_SIGNAL ()); + + (void) fsysdep_unlock_uuxqt (iQlock_seq, zcmd, cQmaxuuxqts); + iQlock_seq = -1; + + ulog_close (); + + if (FGOT_SIGNAL ()) + ferr = TRUE; + + usysdep_exit (! ferr); + + /* Avoid errors about not returning a value. */ + return 0; +} + +static void +uqusage () +{ + fprintf (stderr, + "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n", + VERSION); + fprintf (stderr, + "Usage: uuxqt [-c cmd] [-I file] [-s system] [-x debug]\n"); + fprintf (stderr, + " -c cmd: Set type of command to execute\n"); + fprintf (stderr, + " -s system: Execute commands only for named system\n"); + fprintf (stderr, + " -x debug: Set debugging level (0 for none, 9 is max)\n"); +#if HAVE_TAYLOR_CONFIG + fprintf (stderr, + " -I file: Set configuration file to use\n"); +#endif /* HAVE_TAYLOR_CONFIG */ + exit (EXIT_FAILURE); +} + +/* This is the abort function called when we get a fatal error. */ + +static void +uqabort () +{ +#if ! HAVE_HDB_LOGGING + /* When using HDB logging, it's a pain to have no system name. */ + ulog_system ((const char *) NULL); +#endif + + ulog_user ((const char *) NULL); + + if (fQunlock_directory) + (void) fsysdep_unlock_uuxqt_dir (iQlock_seq); + + if (zQunlock_file != NULL) + (void) fsysdep_unlock_uuxqt_file (zQunlock_file); + + if (iQlock_seq >= 0) + (void) fsysdep_unlock_uuxqt (iQlock_seq, zQunlock_cmd, cQmaxuuxqts); + + ulog_close (); + + usysdep_exit (FALSE); +} + +/* An execute file is a series of lines. The first character of each + line is a command. The following commands are defined: + + C command-line + I standard-input + O standard-output [ system ] + F required-file filename-to-use + R requestor-address + U user system + Z (acknowledge if command failed; default) + N (no acknowledgement on failure) + n (acknowledge if command succeeded) + B (return command input on error) + e (process with sh) + E (process with exec) + M status-file + # comment + + Unrecognized commands are ignored. We actually do not recognize + the Z command, since it requests default behaviour. We always send + mail on failure, unless the N command appears. We never send mail + on success, unless the n command appears. + + This code does not currently support the B or M commands. */ + +/* Command arguments. */ +static char **azQargs; +/* Command as a complete string. */ +static char *zQcmd; +/* Standard input file name. */ +static char *zQinput; +/* Standard output file name. */ +static char *zQoutfile; +/* Standard output system. */ +static char *zQoutsys; +/* Number of required files. */ +static int cQfiles; +/* Names of required files. */ +static char **azQfiles; +/* Names required files should be renamed to (NULL if original is OK). */ +static char **azQfiles_to; +/* Requestor address (this is where mail should be sent). */ +static char *zQrequestor; +/* User name. */ +static const char *zQuser; +/* System name. */ +static const char *zQsystem; +/* This is set by the N flag, meaning that no acknowledgement should + be mailed on failure. */ +static boolean fQno_ack; +/* This is set by the n flag, meaning that acknowledgement should be + mailed if the command succeeded. */ +static boolean fQsuccess_ack; +/* This is set by the B flag, meaning that command input should be + mailed to the requestor if an error occurred. */ +static boolean fQsend_input; +/* This is set by the E flag, meaning that exec should be used to + execute the command. */ +static boolean fQuse_exec; +/* The status should be copied to this file on the requesting host. */ +static const char *zQstatus_file; +#if ALLOW_SH_EXECUTION +/* This is set by the e flag, meaning that sh should be used to + execute the command. */ +static boolean fQuse_sh; +#endif /* ALLOW_SH_EXECUTION */ + +static int iqcmd P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int iqout P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int iqfile P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int iqrequestor P((pointer puuconf, int argc, char **argv, + pointer pvar, pointer pinfo)); +static int iquser P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int iqset P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); + +static const struct uuconf_cmdtab asQcmds[] = +{ + { "C", UUCONF_CMDTABTYPE_FN | 0, NULL, iqcmd }, + { "I", UUCONF_CMDTABTYPE_STRING, (pointer) &zQinput, NULL }, + { "O", UUCONF_CMDTABTYPE_FN | 0, NULL, iqout }, + { "F", UUCONF_CMDTABTYPE_FN | 0, NULL, iqfile }, + { "R", UUCONF_CMDTABTYPE_FN, NULL, iqrequestor }, + { "U", UUCONF_CMDTABTYPE_FN | 3, NULL, iquser }, + { "N", UUCONF_CMDTABTYPE_FN | 1, (pointer) &fQno_ack, iqset }, + { "n", UUCONF_CMDTABTYPE_FN | 1, (pointer) &fQsuccess_ack, iqset }, + /* Some systems create execution files in which B takes an argument; + I don't know what it means, so I just ignore it. */ + { "B", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQsend_input, iqset }, +#if ALLOW_SH_EXECUTION + { "e", UUCONF_CMDTABTYPE_FN | 1, (pointer) &fQuse_sh, iqset }, +#endif + { "E", UUCONF_CMDTABTYPE_FN | 1, (pointer) &fQuse_exec, iqset }, + { "M", UUCONF_CMDTABTYPE_STRING, (pointer) &zQstatus_file, NULL }, + { NULL, 0, NULL, NULL } +}; + +/* Handle the C command: store off the arguments. */ + +/*ARGSUSED*/ +static int +iqcmd (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + int i; + size_t clen; + + if (argc <= 1) + return UUCONF_CMDTABRET_CONTINUE; + + azQargs = (char **) xmalloc (argc * sizeof (char *)); + clen = 0; + for (i = 1; i < argc; i++) + { + azQargs[i - 1] = zbufcpy (argv[i]); + clen += strlen (argv[i]) + 1; + } + azQargs[i - 1] = NULL; + + zQcmd = (char *) xmalloc (clen); + zQcmd[0] = '\0'; + for (i = 1; i < argc - 1; i++) + { + strcat (zQcmd, argv[i]); + strcat (zQcmd, " "); + } + strcat (zQcmd, argv[i]); + + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Handle the O command, which may have one or two arguments. */ + +/*ARGSUSED*/ +static int +iqout (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + const char *zbase = (const char *) pinfo; + + if (argc != 2 && argc != 3) + { + ulog (LOG_ERROR, "%s: %s: Wrong number of arguments", + zbase, argv[0]); + return UUCONF_CMDTABRET_CONTINUE; + } + + zQoutfile = zbufcpy (argv[1]); + if (argc == 3) + zQoutsys = zbufcpy (argv[2]); + + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Handle the F command, which may have one or two arguments. */ + +/*ARGSUSED*/ +static int +iqfile (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + const char *zbase = (const char *) pinfo; + + if (argc != 2 && argc != 3) + { + ulog (LOG_ERROR, "%s: %s: Wrong number of arguments", + zbase, argv[0]); + return UUCONF_CMDTABRET_CONTINUE; + } + + /* If this file is not in the spool directory, just ignore it. */ + if (! fspool_file (argv[1])) + return UUCONF_CMDTABRET_CONTINUE; + + ++cQfiles; + azQfiles = (char **) xrealloc ((pointer) azQfiles, + cQfiles * sizeof (char *)); + azQfiles_to = (char **) xrealloc ((pointer) azQfiles_to, + cQfiles * sizeof (char *)); + + azQfiles[cQfiles - 1] = zbufcpy (argv[1]); + if (argc == 3) + azQfiles_to[cQfiles - 1] = zbufcpy (argv[2]); + else + azQfiles_to[cQfiles - 1] = NULL; + + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Handle the R command, which may have one or two arguments. */ + +/*ARGSUSED*/ +static int +iqrequestor (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + const char *zbase = (const char *) pinfo; + + if (argc != 2 && argc != 3) + { + ulog (LOG_ERROR, "%s: %s: Wrong number of arguments", + zbase, argv[0]); + return UUCONF_CMDTABRET_CONTINUE; + } + + /* We normally have a single argument, which is the ``requestor'' + address, to which we should send any success or error messages. + Apparently the DOS program UUPC sends two arguments, which are + the username and the host. */ + if (argc == 2) + zQrequestor = zbufcpy (argv[1]); + else + { + zQrequestor = zbufalc (strlen (argv[1]) + strlen (argv[2]) + + sizeof "!"); + sprintf (zQrequestor, "%s!%s", argv[2], argv[1]); + } + + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Handle the U command, which takes two arguments. */ + +/*ARGSUSED*/ +static int +iquser (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + zQuser = argv[1]; + zQsystem = argv[2]; + return UUCONF_CMDTABRET_KEEP; +} + +/* Handle various commands which just set boolean variables. */ + +/*ARGSUSED*/ +static int +iqset (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + boolean *pf = (boolean *) pvar; + + *pf = TRUE; + return UUCONF_CMDTABRET_CONTINUE; +} + +/* The execution processing does a lot of things that have to be + cleaned up. Rather than try to add the appropriate statements + to each return point, we keep a set of flags indicating what + has to be cleaned up. The actual clean up is done by the + function uqcleanup. */ +#define REMOVE_FILE (01) +#define REMOVE_NEEDED (02) +#define FREE_QINPUT (04) +#define FREE_OUTPUT (010) +#define FREE_MAIL (020) + +/* Process an execute file. The zfile argument is the name of the + execute file. The zbase argument is the base name of zfile. The + qsys argument describes the system it came from. The zcmd argument + is the name of the command we are executing (from the -c option) or + NULL if any command is OK. This sets *pfprocessed to TRUE if the + file is ready to be executed. */ + +static void +uqdo_xqt_file (puuconf, zfile, zbase, qsys, zlocalname, zcmd, pfprocessed) + pointer puuconf; + const char *zfile; + const char *zbase; + const struct uuconf_system *qsys; + const char *zlocalname; + const char *zcmd; + boolean *pfprocessed; +{ + char *zabsolute; + boolean ferr; + FILE *e; + int iuuconf; + int i; + int iclean; + const char *zmail; + char *zoutput; + char *zinput; + char abtemp[CFILE_NAME_LEN]; + char abdata[CFILE_NAME_LEN]; + char *zerror; + struct uuconf_system soutsys; + const struct uuconf_system *qoutsys; + boolean fshell; + size_t clen; + char *zfullcmd; + boolean ftemp; + + *pfprocessed = FALSE; + + e = fopen (zfile, "r"); + if (e == NULL) + return; + + azQargs = NULL; + zQcmd = NULL; + zQinput = NULL; + zQoutfile = NULL; + zQoutsys = NULL; + cQfiles = 0; + azQfiles = NULL; + azQfiles_to = NULL; + zQrequestor = NULL; + zQuser = NULL; + zQsystem = NULL; + fQno_ack = FALSE; + fQsuccess_ack = FALSE; + fQsend_input = FALSE; + fQuse_exec = FALSE; + zQstatus_file = NULL; +#if ALLOW_SH_EXECUTION + fQuse_sh = FALSE; +#endif + + iuuconf = uuconf_cmd_file (puuconf, e, asQcmds, (pointer) zbase, + (uuconf_cmdtabfn) NULL, + UUCONF_CMDTABFLAG_CASE, (pointer) NULL); + (void) fclose (e); + + if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return; + } + + iclean = 0; + + if (azQargs == NULL) + { + ulog (LOG_ERROR, "%s: No command given", zbase); + uqcleanup (zfile, iclean | REMOVE_FILE); + return; + } + + if (zcmd != NULL) + { + if (strcmp (zcmd, azQargs[0]) != 0) + { + uqcleanup (zfile, iclean); + return; + } + } + else + { + /* If there is a lock file for this particular command already, + it means that some other uuxqt is supposed to handle it. */ + if (fsysdep_uuxqt_locked (azQargs[0])) + { + uqcleanup (zfile, iclean); + return; + } + } + + /* Lock this particular file. */ + if (! fsysdep_lock_uuxqt_file (zfile)) + { + uqcleanup (zfile, iclean); + return; + } + + zQunlock_file = zfile; + + /* Now that we have the file locked, make sure it still exists. + Otherwise another uuxqt could have just finished processing it + and removed the lock file. */ + if (! fsysdep_file_exists (zfile)) + { + uqcleanup (zfile, iclean); + return; + } + + if (zQuser != NULL) + ulog_user (zQuser); + else if (zQrequestor != NULL) + ulog_user (zQrequestor); + else + ulog_user ("unknown"); + + /* Make sure that all the required files exist, and get their + full names in the spool directory. */ + for (i = 0; i < cQfiles; i++) + { + char *zreal; + + zreal = zsysdep_spool_file_name (qsys, azQfiles[i], (pointer) NULL); + if (zreal == NULL) + { + uqcleanup (zfile, iclean); + return; + } + if (! fsysdep_file_exists (zreal)) + { + uqcleanup (zfile, iclean); + return; + } + ubuffree (azQfiles[i]); + azQfiles[i] = zbufcpy (zreal); + ubuffree (zreal); + } + + /* Lock the execution directory. */ + if (! fsysdep_lock_uuxqt_dir (iQlock_seq)) + { + ulog (LOG_ERROR, "Could not lock execute directory"); + uqcleanup (zfile, iclean); + return; + } + fQunlock_directory = TRUE; + + iclean |= REMOVE_FILE | REMOVE_NEEDED; + *pfprocessed = TRUE; + + /* Get the address to mail results to. Prepend the system from + which the execute file originated, since mail addresses are + relative to it. */ + zmail = NULL; + if (zQrequestor != NULL) + zmail = zQrequestor; + else if (zQuser != NULL) + zmail = zQuser; + if (zmail != NULL + && zQsystem != NULL +#if HAVE_INTERNET_MAIL + && strchr (zmail, '@') == NULL +#endif + && strcmp (zQsystem, zlocalname) != 0) + { + char *zset; + + zset = zbufalc (strlen (zQsystem) + strlen (zmail) + 2); + sprintf (zset, "%s!%s", zQsystem, zmail); + zmail = zset; + zQmail = zset; + iclean |= FREE_MAIL; + } + + /* The command "uucp" is handled specially. We make sure that the + appropriate forwarding is permitted, and we add a -u argument to + specify the user. */ + if (strcmp (azQargs[0], "uucp") == 0) + { + char *zfrom, *zto; + boolean fmany; + char **azargs; + const char *zuser, *zsystem; + + zfrom = NULL; + zto = NULL; + fmany = FALSE; + + /* Skip all the options, and get the from and to specs. We + don't permit multiple arguments. */ + for (i = 1; azQargs[i] != NULL; i++) + { + if (azQargs[i][0] == '-') + { + char *zopts; + + for (zopts = azQargs[i] + 1; *zopts != '\0'; zopts++) + { + /* The -g, -n, and -s options take an argument. */ + if (*zopts == 'g' || *zopts == 'n' || *zopts == 's') + { + if (zopts[1] == '\0') + ++i; + break; + } + /* The -I, -u and -x options are not permitted. */ + if (*zopts == 'I' || *zopts == 'u' || *zopts == 'x') + { + *zopts = 'r'; + if (zopts[1] != '\0') + zopts[1] = '\0'; + else + { + ++i; + azQargs[i] = zbufcpy ("-r"); + } + break; + } + } + } + else if (zfrom == NULL) + zfrom = azQargs[i]; + else if (zto == NULL) + zto = azQargs[i]; + else + { + fmany = TRUE; + break; + } + } + + /* Add the -u argument. This is required to let uucp do the + correct permissions checking on the file transfer. */ + for (i = 0; azQargs[i] != NULL; i++) + ; + azargs = (char **) xmalloc ((i + 2) * sizeof (char *)); + azargs[0] = azQargs[0]; + zuser = zQuser; + if (zuser == NULL) + zuser = "uucp"; + zsystem = zQsystem; + if (zsystem == NULL) + zsystem = qsys->uuconf_zname; + azargs[1] = zbufalc (strlen (zsystem) + strlen (zuser) + + sizeof "-u!"); + sprintf (azargs[1], "-u%s!%s", zsystem, zuser); + memcpy (azargs + 2, azQargs + 1, i * sizeof (char *)); + xfree ((pointer) azQargs); + azQargs = azargs; + + /* Find the uucp binary. */ + zabsolute = zsysdep_find_command ("uucp", qsys->uuconf_pzcmds, + qsys->uuconf_pzpath, &ferr); + if (zabsolute == NULL && ! ferr) + { + const char *azcmds[2]; + + /* If "uucp" is not a permitted command, then the forwarding + entries must be set. */ + if (! fqforward (zfrom, qsys->uuconf_pzforward_from, "from", zmail) + || ! fqforward (zto, qsys->uuconf_pzforward_to, "to", zmail)) + { + uqcleanup (zfile, iclean); + return; + } + + /* If "uucp" is not a permitted command, then only uucp + requests with a single source are permitted, since that + is all that will be generated by uucp or uux. */ + if (fmany) + { + ulog (LOG_ERROR, "Bad uucp request %s", zQcmd); + + if (zmail != NULL && ! fQno_ack) + { + const char *az[20]; + + i = 0; + az[i++] = "Your execution request failed because it was an"; + az[i++] = " unsupported uucp request.\n"; + az[i++] = "Execution requested was:\n\t"; + az[i++] = zQcmd; + az[i++] = "\n"; + + (void) fsysdep_mail (zmail, "Execution failed", i, az); + } + + uqcleanup (zfile, iclean); + return; + } + + azcmds[0] = "uucp"; + azcmds[1] = NULL; + zabsolute = zsysdep_find_command ("uucp", (char **) azcmds, + qsys->uuconf_pzpath, &ferr); + } + if (zabsolute == NULL) + { + if (! ferr) + ulog (LOG_ERROR, "Can't find uucp executable"); + + uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); + *pfprocessed = FALSE; + return; + } + } + else + { + /* Get the pathname to execute. */ + zabsolute = zsysdep_find_command (azQargs[0], qsys->uuconf_pzcmds, + qsys->uuconf_pzpath, + &ferr); + if (zabsolute == NULL) + { + if (ferr) + { + /* If we get an error, try again later. */ + uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); + *pfprocessed = FALSE; + return; + } + + /* Not permitted. Send mail to requestor. */ + ulog (LOG_ERROR, "Not permitted to execute %s", + azQargs[0]); + + if (zmail != NULL && ! fQno_ack) + { + const char *az[20]; + + i = 0; + az[i++] = "Your execution request failed because you are not"; + az[i++] = " permitted to execute\n\t"; + az[i++] = azQargs[0]; + az[i++] = "\non this system.\n"; + az[i++] = "Execution requested was:\n\t"; + az[i++] = zQcmd; + az[i++] = "\n"; + + (void) fsysdep_mail (zmail, "Execution failed", i, az); + } + + uqcleanup (zfile, iclean); + return; + } + } + + ubuffree (azQargs[0]); + azQargs[0] = zabsolute; + + for (i = 1; azQargs[i] != NULL; i++) + { + char *zlocal; + + zlocal = zsysdep_xqt_local_file (qsys, azQargs[i]); + if (zlocal != NULL) + { + ubuffree (azQargs[i]); + azQargs[i] = zlocal; + } + } + +#if ! ALLOW_FILENAME_ARGUMENTS + + /* Check all the arguments to make sure they don't try to specify + files they are not permitted to access. */ + for (i = 1; azQargs[i] != NULL; i++) + { + if (! fsysdep_xqt_check_file (qsys, azQargs[i])) + { + if (zmail != NULL && ! fQno_ack) + { + const char *az[20]; + const char *zfailed; + + zfailed = azQargs[i]; + i = 0; + az[i++] = "Your execution request failed because you are not"; + az[i++] = " permitted to refer to file\n\t"; + az[i++] = zfailed; + az[i++] = "\non this system.\n"; + az[i++] = "Execution requested was:\n\t"; + az[i++] = zQcmd; + az[i++] = "\n"; + + (void) fsysdep_mail (zmail, "Execution failed", i, az); + } + + uqcleanup (zfile, iclean); + return; + } + } + +#endif /* ! ALLOW_FILENAME_ARGUMENTS */ + + ulog (LOG_NORMAL, "Executing %s (%s)", zbase, zQcmd); + + if (zQinput != NULL) + { + boolean fspool; + char *zreal; + + fspool = fspool_file (zQinput); + if (fspool) + zreal = zsysdep_spool_file_name (qsys, zQinput, (pointer) NULL); + else + zreal = zsysdep_local_file (zQinput, qsys->uuconf_zpubdir); + if (zreal == NULL) + { + /* If we get an error, try again later. */ + uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); + *pfprocessed = FALSE; + return; + } + + zQinput = zreal; + iclean |= FREE_QINPUT; + + if (! fspool + && ! fin_directory_list (zQinput, qsys->uuconf_pzremote_send, + qsys->uuconf_zpubdir, TRUE, TRUE, + (const char *) NULL)) + { + ulog (LOG_ERROR, "Not permitted to read %s", zQinput); + + if (zmail != NULL && ! fQno_ack) + { + const char *az[20]; + + i = 0; + az[i++] = "Your execution request failed because you are"; + az[i++] = " not permitted to read\n\t"; + az[i++] = zQinput; + az[i++] = "\non this system.\n"; + az[i++] = "Execution requested was:\n\t"; + az[i++] = zQcmd; + az[i++] = "\n"; + + (void) fsysdep_mail (zmail, "Execution failed", i, az); + } + + uqcleanup (zfile, iclean); + return; + } + } + + zoutput = NULL; + if (zQoutfile == NULL) + qoutsys = NULL; + else if (zQoutsys != NULL + && strcmp (zQoutsys, zlocalname) != 0) + { + char *zdata; + + /* The output file is destined for some other system, so we must + use a temporary file to catch standard output. */ + if (strcmp (zQoutsys, qsys->uuconf_zname) == 0) + qoutsys = qsys; + else + { + iuuconf = uuconf_system_info (puuconf, zQoutsys, &soutsys); + if (iuuconf != UUCONF_SUCCESS) + { + if (iuuconf != UUCONF_NOT_FOUND) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); + *pfprocessed = FALSE; + return; + } + + if (! funknown_system (puuconf, zQoutsys, &soutsys)) + { + ulog (LOG_ERROR, + "Can't send standard output to unknown system %s", + zQoutsys); + /* We don't send mail to unknown systems, either. + Maybe we should. */ + uqcleanup (zfile, iclean); + return; + } + } + + qoutsys = &soutsys; + } + + zdata = zsysdep_data_file_name (qoutsys, zlocalname, + BDEFAULT_UUX_GRADE, FALSE, abtemp, + abdata, (char *) NULL); + if (zdata == NULL) + { + /* If we get an error, try again later. */ + uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); + *pfprocessed = FALSE; + return; + } + zoutput = zdata; + zQoutput = zoutput; + iclean |= FREE_OUTPUT; + } + else + { + boolean fok; + + qoutsys = NULL; + + /* If we permitted the standard output to be redirected into + the spool directory, people could set up phony commands. */ + if (fspool_file (zQoutfile)) + fok = FALSE; + else + { + zoutput = zsysdep_local_file (zQoutfile, qsys->uuconf_zpubdir); + if (zoutput == NULL) + { + /* If we get an error, try again later. */ + uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); + *pfprocessed = FALSE; + return; + } + ubuffree (zQoutfile); + zQoutfile = zoutput; + + /* Make sure it's OK to receive this file. */ + fok = fin_directory_list (zQoutfile, + qsys->uuconf_pzremote_receive, + qsys->uuconf_zpubdir, TRUE, FALSE, + (const char *) NULL); + } + + if (! fok) + { + ulog (LOG_ERROR, "Not permitted to write to %s", zQoutfile); + + if (zmail != NULL && ! fQno_ack) + { + const char *az[20]; + + i = 0; + az[i++] = "Your execution request failed because you are"; + az[i++] = " not permitted to write to\n\t"; + az[i++] = zQoutfile; + az[i++] = "\non this system.\n"; + az[i++] = "Execution requested was:\n\t"; + az[i++] = zQcmd; + az[i++] = "\n"; + + (void) fsysdep_mail (zmail, "Execution failed", i, az); + } + + uqcleanup (zfile, iclean); + return; + } + } + + /* Move the required files to the execution directory if necessary. */ + zinput = zQinput; + if (! fsysdep_move_uuxqt_files (cQfiles, (const char **) azQfiles, + (const char **) azQfiles_to, + TRUE, iQlock_seq, &zinput)) + { + /* If we get an error, try again later. */ + uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); + *pfprocessed = FALSE; + return; + } + if (zQinput != NULL && strcmp (zQinput, zinput) != 0) + { + if ((iclean & FREE_QINPUT) != 0) + ubuffree (zQinput); + zQinput = zinput; + iclean |= FREE_QINPUT; + } + +#if ALLOW_SH_EXECUTION + fshell = fQuse_sh; +#else + fshell = FALSE; +#endif + + /* Get a shell command which uses the full path of the command to + execute. */ + clen = 0; + for (i = 0; azQargs[i] != NULL; i++) + clen += strlen (azQargs[i]) + 1; + zfullcmd = zbufalc (clen); + strcpy (zfullcmd, azQargs[0]); + for (i = 1; azQargs[i] != NULL; i++) + { + strcat (zfullcmd, " "); + strcat (zfullcmd, azQargs[i]); + } + + if (! fsysdep_execute (qsys, + zQuser == NULL ? (const char *) "uucp" : zQuser, + (const char **) azQargs, zfullcmd, zQinput, + zoutput, fshell, iQlock_seq, &zerror, &ftemp)) + { + ubuffree (zfullcmd); + + if (ftemp) + { + ulog (LOG_NORMAL, "Will retry later (%s)", zbase); + if (zoutput != NULL) + (void) remove (zoutput); + if (zerror != NULL) + { + (void) remove (zerror); + ubuffree (zerror); + } + (void) fsysdep_move_uuxqt_files (cQfiles, (const char **) azQfiles, + (const char **) azQfiles_to, + FALSE, iQlock_seq, + (char **) NULL); + uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); + *pfprocessed = FALSE; + return; + } + + ulog (LOG_NORMAL, "Execution failed (%s)", zbase); + + if (zmail != NULL && ! fQno_ack) + { + const char **pz; + int cgot; + FILE *eerr; + int istart; + + cgot = 20; + pz = (const char **) xmalloc (cgot * sizeof (const char *)); + i = 0; + pz[i++] = "Execution request failed:\n\t"; + pz[i++] = zQcmd; + pz[i++] = "\n"; + + if (zerror == NULL) + eerr = NULL; + else + eerr = fopen (zerror, "r"); + if (eerr == NULL) + { + pz[i++] = "There was no output on standard error\n"; + istart = i; + } + else + { + char *zline; + size_t cline; + + pz[i++] = "Standard error output was:\n"; + istart = i; + + zline = NULL; + cline = 0; + while (getline (&zline, &cline, eerr) > 0) + { + if (i >= cgot) + { + cgot += 20; + pz = ((const char **) + xrealloc ((pointer) pz, + cgot * sizeof (const char *))); + } + pz[i++] = zbufcpy (zline); + } + + (void) fclose (eerr); + xfree ((pointer) zline); + } + + (void) fsysdep_mail (zmail, "Execution failed", i, pz); + + for (; istart < i; istart++) + ubuffree ((char *) pz[istart]); + xfree ((pointer) pz); + } + + if (qoutsys != NULL) + (void) remove (zoutput); + } + else + { + ubuffree (zfullcmd); + + if (zmail != NULL && fQsuccess_ack) + { + const char *az[20]; + + i = 0; + az[i++] = "\nExecution request succeeded:\n\t"; + az[i++] = zQcmd; + az[i++] = "\n"; + + (void) fsysdep_mail (zmail, "Execution succeded", i, az); + } + + /* Now we may have to uucp the output to some other machine. */ + + if (qoutsys != NULL) + { + struct scmd s; + + /* Fill in the command structure. */ + + s.bcmd = 'S'; + s.pseq = NULL; + s.zfrom = abtemp; + s.zto = zQoutfile; + if (zQuser != NULL) + s.zuser = zQuser; + else + s.zuser = "uucp"; + if (zmail != NULL && fQsuccess_ack) + s.zoptions = "Cn"; + else + s.zoptions = "C"; + s.ztemp = abtemp; + s.imode = 0666; + if (zmail != NULL && fQsuccess_ack) + s.znotify = zmail; + else + s.znotify = ""; + s.cbytes = -1; + s.zcmd = NULL; + s.ipos = 0; + + ubuffree (zsysdep_spool_commands (qoutsys, BDEFAULT_UUX_GRADE, + 1, &s)); + } + } + + if (zerror != NULL) + { + (void) remove (zerror); + ubuffree (zerror); + } + + uqcleanup (zfile, iclean); +} + +/* Clean up the results of uqdo_xqt_file. */ + +static void +uqcleanup (zfile, iflags) + const char *zfile; + int iflags; +{ + int i; + + DEBUG_MESSAGE2 (DEBUG_SPOOLDIR, + "uqcleanup: %s, %d", zfile, iflags); + + if (zQunlock_file != NULL) + { + (void) fsysdep_unlock_uuxqt_file (zQunlock_file); + zQunlock_file = NULL; + } + + if ((iflags & REMOVE_FILE) != 0) + (void) remove (zfile); + + if ((iflags & REMOVE_NEEDED) != 0) + { + for (i = 0; i < cQfiles; i++) + { + if (azQfiles[i] != NULL) + (void) remove (azQfiles[i]); + } + } + + if ((iflags & FREE_QINPUT) != 0) + ubuffree (zQinput); + + if ((iflags & FREE_OUTPUT) != 0) + ubuffree (zQoutput); + if ((iflags & FREE_MAIL) != 0) + ubuffree (zQmail); + + if (fQunlock_directory) + { + (void) fsysdep_unlock_uuxqt_dir (iQlock_seq); + fQunlock_directory = FALSE; + } + + for (i = 0; i < cQfiles; i++) + { + ubuffree (azQfiles[i]); + ubuffree (azQfiles_to[i]); + } + + ubuffree (zQoutfile); + ubuffree (zQoutsys); + ubuffree (zQrequestor); + + if (azQargs != NULL) + { + for (i = 0; azQargs[i] != NULL; i++) + ubuffree (azQargs[i]); + xfree ((pointer) azQargs); + azQargs = NULL; + } + + xfree ((pointer) zQcmd); + zQcmd = NULL; + + xfree ((pointer) azQfiles); + azQfiles = NULL; + + xfree ((pointer) azQfiles_to); + azQfiles_to = NULL; +} + +/* Check whether forwarding is permitted. */ + +static boolean +fqforward (zfile, pzallowed, zlog, zmail) + const char *zfile; + char **pzallowed; + const char *zlog; + const char *zmail; +{ + const char *zexclam; + + zexclam = strchr (zfile, '!'); + if (zexclam != NULL) + { + size_t clen; + char *zsys; + boolean fret; + + clen = zexclam - zfile; + zsys = zbufalc (clen + 1); + memcpy (zsys, zfile, clen); + zsys[clen] = '\0'; + + fret = FALSE; + if (pzallowed != NULL) + { + char **pz; + + for (pz = pzallowed; *pz != NULL; pz++) + { + if (strcmp (*pz, "ANY") == 0 + || strcmp (*pz, zsys) == 0) + { + fret = TRUE; + break; + } + } + } + + if (! fret) + { + ulog (LOG_ERROR, "Not permitted to forward %s %s (%s)", + zlog, zsys, zQcmd); + + if (zmail != NULL && ! fQno_ack) + { + int i; + const char *az[20]; + + i = 0; + az[i++] = "Your execution request failed because you are"; + az[i++] = " not permitted to forward files\n"; + az[i++] = zlog; + az[i++] = " the system\n\t"; + az[i++] = zsys; + az[i++] = "\n"; + az[i++] = "Execution requested was:\n\t"; + az[i++] = zQcmd; + az[i++] = "\n"; + + (void) fsysdep_mail (zmail, "Execution failed", i, az); + } + } + + ubuffree (zsys); + + return fret; + } + + return TRUE; +} diff --git a/bin/test/COPYING b/gnu/usr.bin/as/COPYING similarity index 100% rename from bin/test/COPYING rename to gnu/usr.bin/as/COPYING diff --git a/gnu/usr.bin/as/ChangeLog b/gnu/usr.bin/as/ChangeLog new file mode 100644 index 0000000000..3e0e64f37a --- /dev/null +++ b/gnu/usr.bin/as/ChangeLog @@ -0,0 +1,917 @@ +Fri Jan 4 12:48:22 EST 1991 Jay Fenlason (hack@ai.mit.edu) + + * messages.c Moved as_perror from input-scrub.c Modified the + error messages to look better. + + * output-file.c Don't call as_fatal, just call exit() + + expr.c Slightly improve checking for foo-foo case in + clean_up_expression(). Detect foo: bar: ... foo-bar... + +Tue Dec 4 16:29:20 EST 1990 Jay Fenlason (hack@ai.mit.edu) + + * m68k.c Fixed an obscure bug involving AOFF mode with a + large constant displacement (Was forgetting to output the + extension word.) + + make-gas.com Added a three line patch from Eric Youngdale that + makes it possible to submit make-gas.com to a batch queue. + +Wed Nov 21 15:07:51 EST 1990 Jay Fenlason (hack@ai.mit.edu) + + * vms.c (VMS_TBT_Routine_END) Add a four line patch from + Eric Youngdale. + +Tue Nov 13 14:02:15 EST 1990 Jay Fenlason (hack@ai.mti.edu) + + * vms-dbg.c (VMS_DBG_record()) Another one character patch from + Eric Youngdale. + +Mon Oct 29 15:49:21 EST 1990 Jay Fenlason (hack@ai.mit.edu) + + * read.c Replace some as_warn calls with as_bad. + +Fri Oct 26 15:21:15 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * i386.c, i860.c, ns32k.c Add const changes. + +Mon Oct 22 14:04:26 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * sparc.c Add const changes. + + * make-gas.com define const= for VMS, since many older versions of + GCC don't work correctly with const under VMS. + +Thu Oct 18 12:44:11 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * i860.c i860-opcode.h Added patches from rgb@mcc.com + + * read.c, symbols.c, vms.c, + new_file vms-dbg-module.c + Added Eric Youngdale's VMS debugging + patches, so debugging GCC output now works. + + * hash.c (hash_grow) Remember to blank out the wall entry in the new + hash table. This is important on systems where malloc() returns + non-zero storage. . . + +Tue Oct 16 10:11:35 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * output-file.c (output_file_create) if output filename is given as + '-', write to stdout. + + * m68k.c Finally get the PCREL code to work right. Add relaxation of + PCREL stuff This small fix from Ken Woodland + (kenny%datacube.uucp@uunet.uu.net). + + * m68k.c Added some const declarations to constants. (md_relax_table, + md_pseudo_table, etc. . .) + +Thu Oct 11 11:15:10 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * Makefile, read.c, write.c Include the i860 port. + (New files i860.c i860-opcode.h i860.h) + + * m68k.c Fix some addressing modes, (AOFF, AINDEX, etc) to work in + PC relative mode. + + * (all over) Raeburn's const hacking. This reduces the data-space size by + declaring many tables, etc, as 'const'. + +Thu Sep 27 13:43:49 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * m68k.c (get_num) Fix so that 1:w is treated properly. + + * Makefile Replace references to a.out.h with a.out.gnu.h + +Tue Sep 25 15:50:36 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * sparc.c (md_number_to_imm) Fix so that RELOC_32 locations contain + zero, so that it will work with some sparc loaders which don't assume + that the locations in question do. A xix line patch from Michael Bloom + (usc!srhqla!quad1!psivax!ttidca!mb@zaphod.mps.ohio-state.edu) + +Mon Sep 24 11:43:15 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * as.c #include if _POSIX_SOURCE defined. This because + uses pid_t that's defined in it. + + * m68k.c reverse the sense of -l option, and allow :w and :l to + override the default size of AOFF indexes. + + Also, allow -l to shorten branches to unknown labels from LONG to WORD. + +Thu Sep 13 17:05:09 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * vax.c (md_parse_option) Don't print a warning msg if given -J + +Wed Sep 5 14:26:14 EDT 1990 Jay Fenlason + + * expr.c (operand) Don't get garbaged high-order bits when given a + lot of leading zeroes. + +Tue Sep 4 11:40:21 EDT 1990 Jay Fenlason + + * read.c (pseudo_set) Compain if we're setting the symbol to the + difference of two symbols which are in different frags. (We can't + find out how far apart they are.) + +Wed Aug 15 12:18:58 EDT 1990 Jay Fenlason + + * m68k.c (m68k_ip_op) Dyke out the code that deals with parsing + :[wl] at the end of expressions since it is handled in get_num() + and this was putting the result in the wrong place anyway. + Corrected a couple of other references to ->isiz instead of con?->e_siz + +Mon Aug 13 15:53:46 EDT 1990 Jay Fenlason + + * read.c Handle .align specially on the sparc, or any other machine + where OTHER_ALIGN is defined. Added option and comments about it + to Makefile. + +Fri Aug 10 12:24:33 EDT 1990 Jay Fenlason + + * m68k.c (get_num) Handle SEG_PASS1 expressions. + +Mon Aug 6 16:32:29 EDT 1990 Jay Fenlason + + * write.c (fixup_segment) Added two patches for the NS32k + and SEQUENT A six line patch from Ian Dall + (asgard!aegir!hugin!augean!sibyl!ian@munnari.oz.au) + +Wed Aug 1 13:30:48 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * m68k.c Include REGISTER_PREFIX ifdefs. + + * write.c Include LOCAL_LABEL() and DOT_LABEL_PREFIX feature. + + * vax.c (md_parse_option) Accept -H option. + + * vms.c New version of case hasher, etc. These from Eric Youngdale + + +Fri Jul 20 13:39:02 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * README Added README.APOLLO and README.COFF stuff + +Wed Jul 18 16:29:22 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * Makefile Added option for SEQUENT_COMPATABILITY + + * ns32k.c Add configurable syntax feature from + ian@sibyl.eleceng.ua.oz@augean.ua.oz.au + and SEQUENT_COMPATABILITY + + * ns32k-opcode.h Add configurable syntax feature. + +Mon Jul 16 11:44:14 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * write.c (relax_segment) On ns32k, add fragP->fr_pcrel_adjust to + aim. + (fixup_segment) On ns32k, don't subtract size from + add_number on pcrel external symbols. + + * ns32k.c (md_relax_table) Use correct max displacements. + This is a six-line patch from ian@sibyl.eleceng.ua.oz.au + + * ns32k.c (md_atof, convert_iif) Emit floating point numbers in + the correct byte order. A seven line patch from + ian@sibyl.eleceng.ua.oz.au + + * ns32k.c (all over) Some lint fixes. + +Mon Jul 9 13:17:00 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * app.c (do_scrub_next_char) If a comment is #{whitespace} + don't treat the next line as comment also. + + * m68k.c (...) Accept apc@(num:[wl]), etc. + + * i386.c (md_assemble) Get bitfields correct when doing cross + assembly to 386. A two line patch from Michael Bloom. + (usc!srhqla!quad1!ttidca!mb@zaphod.mps.ohio-state.edu). + + * README.APOLLO a new file with vasta@apollo's name, address + and phone # in it. + + * make-gas.com Deleted references to gdb source files. + +Fri Jul 6 14:34:27 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * i386.c Ignore the .optim directive + + * input-file.c Change from _IOLBF to _IOFBF in setbuffer emulation + for SYSV. + +Mon Jun 18 15:36:49 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * sparc.c #ifdef DONTDEF s_sparc_align, since it isn't called from + anywhere. + +Fri Jun 15 15:53:30 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * vax.c (md_parse_option) make the code agree with the documentation + on the behaviour of the -d option. + +Thu Jun 7 14:23:54 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * atof-ieee.c (gen_to_words) Assemble 0r-0 correctly. + + * Makefile Remove last references to gdb*.c files. + + * version.c New version 1.36 + +Tue May 22 13:22:26 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * Makefile Mention a work-around for a possible problem with HPUX7.0 + +Mon May 21 14:06:04 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * sparc.c (sparc_ip): Change error message from "not in hash table" + to "unknown opcode". + +Wed May 16 15:33:14 EDT 1990 hack@wookumz + + * i386.c (i386_operand) Print error msg if given an operand like + 4(mumble) which we can't parse. + + * m68k.c (md_assemble) Add '3' to the list of operand-places that + can be found in 'symbol-dependent info'. Also change + 'confusing width' diagnostic to something more meaningful. + +Fri May 11 12:09:21 EDT 1990 hack@wookumz + + app.c (do_scrub_next_char) Don't flush the line after a line + consisting of a single '/' A one-line patch from Mike Kupfer + (kupfer@orc.olivetti.com) + + * i386.c (md_assemble) Call frag_wane() before calling frag_new() + A one line patch from Steve Bleazard (steve@robobar.co.uk + +Tue May 8 12:56:25 EDT 1990 hack@wookumz + + * atof-generic.c (atof-generic) Modified the Infinity detection code + to accept 0rinfinity and 0r-infinity as well as 0rinf and 0r-inf + +Thu Apr 26 15:17:31 EDT 1990 hack@wookumz + + * atof-ieee.c Change value of NaNs to 0x7fff ffff (float) and + 0x7fff ffff ffff ffff (double) If you want some other value for + NaN, use .long and spell it out yourself. + + atof-generic.c (atof_generic) Cleaned up code that detects NaN + and Inf. + + vax.c (md_assemble) print a useful error message if expression() + returns SEG_PASS1 or SEG_DIFFERENCE and we can't deal with those. + +Thu Apr 19 10:30:47 EDT 1990 hack@wookumz + + * input-scrub.c (AFTER_STRING) Make AFTER_STRING contain a null + so that the strstr() call when looking for "#NO_APP" after a "#APP" + will work. A two character patch from Bruce Robertson + (bruce@heather.pooh.com) + + * Makefile, i386.c Use atof-ieee.c instead of atof-i386.c + +Mon Apr 16 16:20:55 EDT 1990 hack@wookumz + + * m68k.c (md_relax_table) Many of the offsets were off by two. + Fixed some generic spacing problems thoughout the file. + +Thu Apr 12 12:22:35 EDT 1990 hack@wookumz + + * sparc.c (md_ri_to_chars) Handle little-endian cross assembly. + + * write.c (relax_segment) Compare addresses correctly to avoid + accidentally relaxing a branch that we don't have to. + These small changes from John Gilmore (gnu@toad.com) + +Fri Apr 6 12:52:15 EDT 1990 hack@wookumz + + * Makefile, expr.c symbols.c Correctly document the SUN_ASM_SYNTAX + option, and make it work. + +Tue Mar 20 12:46:59 EST 1990 + + * as.c (main) Only trap SIGHUP, SIGINT, SIGPIPE, and SIGTERM, + and only if they aren't being ignored. A three line patch + from Paul Eggert (eggert@twinsun.com) + + * write.c (relax_segment) Correct typo 'growth - ' should have been + growth = + + * atof-vax.c (next_bits, flonum_gen2vax) Clean up by sharing some + variables. While we're at it, fix next_bits so that it + doesn't use littlenums that don't exist. . . + +Tue Mar 13 16:23:21 EST 1990 hack@wookumz + + * Rename atof-m68k.c atof-ieee.c + + * Delete atof-ns32k.c + + * m68k.c sparc.c ns32k.c atof-ieee.c Call atof-ieee instead of + atof-m68k or atof-ns32k + + * Makefile Compile with atof-ieee.c instead of atof-ns32k.c or + atof-m68k.c + +Mon Mar 12 14:06:55 EST 1990 hack@wookumz + + * as.c If the signal handler gets called twice, exit immediatly. + + * ns32k.c Call gen_to_words with a pointer of the proper type, and + call md_number_to_chars to put the results in the proper byte-order. + Whoever wrote this code was *sloppy*! + + * Makefile ns32k.o depends on ns32k.c + + * vax.c (md_parse_option) If VMS, accept -+ and -h options. + + * vms.c (VMS_Case_Hack_Symbol) Replace #if NO_CASE_HACKING + with references to the -h option. These small VMS patches + from Angel Li (angel@flipper.miami.edu). + +Thu Mar 8 19:18:59 EST 1990 hack@wookumz + * vms.c Some trivial patches from Eric Youngdale + (YOUNGDALE@v6550c.nrl.navy.mil) + +Wed Mar 7 17:12:09 EST 1990 hack@wookumz + * make-gas.com (Define error as as_fatal when compiling vax.c and vms.c + A two line patch from Eric Youngdale + (YOUNGDALE@v6550c.nrl.navy.mil) + +Tue Mar 6 16:01:09 EST 1990 hack@wookumz + + * Makefile Include ns32k options in makefile. A small patch from + David Taylor (taylor@think.com). + + * as.c read.c write.c Makefile #ifdef DONTDEF out all the gdb + symbol stuff, since it isn't used anymore and it doesn't work. + +Mon Mar 5 14:51:04 EST 1990 hack@wookumz + + * i386.c (md_assemble) Replace memchr() with index(). + + * as.c Trap signals 1 through NSIG, print an error msg, and don't + produce an object file. + + * m68k.c Added a hack so that fsincosx fpx,fpy:fpz works. + + * messages.c New function: as_bad This is like as_warn, except + -W doesn't disable it, and calling it inhibits production of an + object file and causes a non-zero exit code. + +Tue Feb 13 14:25:53 EST 1990 hack@wookumz + * Makefile Include G0 and LOADLIBES for Sequent Symmetry. + Based on a small patch from Johan Widen (jw@sics.se) + +Thu Feb 1 14:08:58 EST 1990 hack@wookumz + * m68k.c Replace 'abort' with 'abort()' which will work. + +Wed Jan 24 17:15:08 EST 1990 hack@ai.mit.edu + + * read.c (ignore_rest_of_line) Have it print the first junk char + in both decimal and %c form. + + (read_a_source_file) On bad pseudo-op, print out the unknown + pseudo-op's name. + +Tue Jan 23 13:12:48 EST 1990 hack@ai.mit.edu + + * read.c (pseudo_set) If the symbol is external, have it remain + external. + + * i386-opcode.h Allow jc as a synonym for jb and jnc as a syn for jnb. + + +Wed Jan 3 09:35:31 EST 1990 hack@ai.mit.edu + + * ns32k.c [cpureg_032] Change register id of psr from 0x0b to 0x0d + * ns32k-opcode.h Change shift-counts for lsh and lshd + to one byte instead of 2 and 4. + A Trivial patch from John F. Peters (think!ames!practic.com!jfp@eddie) + +Tue Dec 5 16:37:44 EST 1989 hack@ai.mit.edu + + * ns32k.c (md_create_{long,short}_jump) Six line patch from + John F Peters (think!ames!vine!practice.com!jfp) to use the + correct addressing mode and byte-order for broken-word stuff. + + * write.c (write_object_file) One line patch to call fix_new_ns32k + with the correct # of args. + +Fri Dec 1 16:44:21 EST 1989 hack@ai.mit.edu + + * atof-generic.c, flonum-mult.c A real fix for the trailing-zeroes + problem from Georg Feil (ghfeil@white.toronto.edu) (two line change) + +Mon Nov 27 15:30:46 EST 1989 hack@ai.mit.edu + + * i386-opcode.h Fixed opcode-table entry for ljmp. A one char + patch from eliot@mgm.mit.edu + +Mon Nov 20 12:41:28 EST 1989 hack@ai.mit.edu + + * expr.c Replace the generic_buffer hack with a more portable one */ + + * atof-generic.c (atof_generic) Ignore trailing zeroes after a decimal + point. For some reason trailing zeroes (but not trailing nonzeroes) were + causing loss of precision. I don't know why. . . + + * vms.c Change copyright notice. Install changes from Kenneth Adelman + (adelman@tgv.com) for c++? (A dozen lines or so) + +Mon Nov 13 11:48:44 EST 1989 hack@ai.mit.edu + + * Makefile Add BINDIR and use it to control where the executable is + installed. + + * i386.c Use __builtin_alloca if possible (trivial patch from + Marco S. Hyman pacbell!dumbcat!marc) + +Mon Nov 6 18:24:47 EST 1989 hack@ai.mit.edu + + * version.c New version: 1.35 will be distributed with the + 1.36 gcc release. + +Mon Oct 30 10:38:11 EST 1989 hack@ai.mit.edu + + * atof-m68k.c (atof_m68k) Don't put the bits[] array on the stack, + since it may be pointed to after atof-m68k exits. + +Tue Oct 24 11:15:57 EDT 1989 hack@ai.mit.edu + + * atof-m68k.c Added #define for bcopy on USG systems. + #ifdef TEST the print_gen() function. + + * a.out.h if USE_HP_INC_HDR then use ../binutils/hp-include/a.out.h + +Fri Oct 13 14:36:48 EDT 1989 hack@ai.mit.edu + + * vax.c (all) Ran vax through indent -gnu to make it readable. + + vax.c (vip_op) Correctly assemble code like jmp $*0x11223344 + by setting vip_nbytes to 4 when using an immediate address. + I hope this works! + + m68k.c (s_proc (new)) Added s_proc no-op pseudo-op. + + Makefile Added instructions for compiling on Sequent Symmetry + and HP 9000/300. + + a.out.h Modified to compile on Sequent and HP above. (HP port + based on a msg from asjl@comp.vuw.ac.nz (real name unknown)). + +Tue Oct 10 14:39:44 EDT 1989 hack@ai.mit.edu + * vax.c (vip_op) Fixed a typo in an error msg and cleaned + up some spacing stuff. + +Wed Sep 27 19:07:12 EDT 1989 hack@ai.mit.edu + + * app.c (do_scrub_next_char) Fixed parsing of + # "file" garbage + text so that it'll work again? (8 line patch from Mike Hibler + (mike@cs.utah.edu)) + +Mon Sep 18 16:26:01 EDT 1989 hack@ai.mit.edu + + * app.c (do_scrub_next_char): Modify parsing of /* ... */ to work + on the text /* ****/ + + * sparc.c (sparc_ip): Don't abort on insns that use the Alternate + Spaces. Try to assemble them correctly. + +Thu Sep 14 11:42:44 EDT 1989 hack@ai.mit.edu + + * sparc.c (md_number_to_imm) Dozen line patch from jkp@sauna.hut.fi + (Jyrki Kuoppala) so that gas output will work with shared libraries. + + * ns32k.c Include instead of if USG defined. + + (md_end) free(freeptr_static) instead of free(freeptr) . + + * atof-ns32k.c Include as.h so that sysV stuff (bzero) will be + defined if needed. These ns32k changes from + nixbur!mollers.pad@seismo.css.gov (Josef Moellers) + +Fri Sep 1 11:39:52 EDT 1989 hack@ai.mit.edu + + * atof-m68k.c (gen_to_words) Get the sign right on negative + floating-point numbers. + +Wed Aug 30 13:59:57 EDT 1989 hack@ai.mit.edu + + * Makefile Remove the rest of the $< entries that kill sun make + +Fri Aug 25 15:00:30 EDT 1989 Nobody You Know (hack@ai.mit.edu) + + * atof-m68k.c (gen_to_words) deal with denormalized floating-point + numbers. + +Tue Aug 22 02:03:05 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * Makefile (gas-dist.tar): Put ChangeLog in the tar file. + + * version.c: Added comment telling Jay Fenl--I mean people--not to put + changes in version.c, but to use ChangeLog instead. + + * version.c (version_string): Put "GNU" in all-caps. + + * version.c: Moved all comments about changes to ChangeLog (this file). + Many anonymous entries have been attributed to Jay Fenlason (hack). + +Thu Aug 17 15:53:57 1989 Jay Fenlason (hack at apple-gunkies.ai.mit.edu) + + * Makefile: Removed $< references that seem + to choke some versions of make. + + * frags.c (frag_grow): Fixed to deal with requests for very + large frags (larger than frags.chunk_size). + + * app.c (do_scrub_next_char): Have it ignore any characters + after the filename in a # line "filename". + + * sparc.c (s_common): On an error, don't print out + input_line_pointer past the end of the line where the error is. + + * atof-generic.c (atof_generic): Accept any case for + inf and nan. + + * m68k.c (m68_ip): Don't use PC-relative mode for alterable + addressing modes. + +Tue Aug 15 04:58:36 1989 Roland McGrath (roland at apple-gunkies.ai.mit.edu) + + * sparc.c (md_begin): Rewrote this function to perform consistency + checks with the new opcode table. + +Fri Aug 11 16:01:16 1989 Roland McGrath (roland at apple-gunkies.ai.mit.edu) + + * sparc-opcode.h (struct sparc_opcode): Replaced `mask' field with + `lose'; removed `last' field. Updated all opcodes accordingly. + Fixed several opcodes that generated the wrong instructions. + sparc.c (md_begin, sparc_ip): Changed to use new struct sparc_opcode. + +Thu Aug 3 14:44:24 1989 Jay Fenlason (hack at apple-gunkies.ai.mit.edu) + + * Makefile (a32k): Use read- and write-ns32k.o + * ns32k.c (encode_operand): Make sure pcrel_adjust starts out zeroed. + * read.c (cons): Call fix_new_ns32k() if NS32K is defined. + * write.c (write_object_file): Ditto. + These so that .word sym-sym (etc) will produce values with + the proper byte-order. + +Wed Aug 2 12:55:?? 1989 Jay Fenlason (hack at apple-gunkies.ai.mit.edu) + + * sparc.c (comment_chars[]): Removed '|' because it was causing + problems. Probably not the best fix, since I suspect other + assemblers (68020) may get | in .stabs also, and the 68020 needs + the '|' comment character. + +Mon Jul 31 09:22:28 1989 Roland McGrath (roland at apple-gunkies.ai.mit.edu) + + * sparc.c (sparc_ip): Allow the characters [0123] in opcodes. + +Tue Jul 25 16:32:12 1989 Jay Fenlason (hack) + + * atof-generic.c (atof_generic): Tried to keep + size_of_digits_in_littlenum from going negative. + + * sparc-opcode.h: Added duplicate [i+1] entries to go with + the [1+i] entries already there. A kludgy fix, but it works. + +Mon Jul 24 17:20:03 1989 Jay Fenlason (hack) + + * write.c (relax_segment): Modified rs_org code so it won't + occasionally dump core. + + * write.c (pseudo_set): Modified SEG_DIFFERENCE to (perhaps) + allow one to set a symbol to the difference of two other symbols. + + * ns32k.c (convert_iif): Moved size_so_far+=size and size=0 inside + the check for a valid type. + + * sparc-opcode.h: Modified the entries for std "q,[1+i]", "D,[1+i]", + and "Q,[1+i]". + +(In version 1.34) Jay Fenlason (hack) + + * Makefile: Reorganized, added stuff to make asparc. + + * sparc.c, sparc-opcode.h, sparc.h: Sparc port. + + * write.c: Set the size of text and bss segments to a multiple of eight + bytes. + + * m68k.c: Moved .single pseudo-op to machine independent part. + + * atof-generic.c: Fixed type in #ifdef __GNUC__. + + * sparc-opcode.h: Handle "mov REG, %y". + + * make-gas.com: Know that error.c no longer exists. + + * sparc.c: Handle [expr+reg]. + Don't call getExpression when looking for an immediate and getting + something that starts with % and isn't %hi or %lo. + + * Teach the 68k about long conditional branches. + +(In version 1.33) Jay Fenlason (hack) + + * Use __builtin_alloca if available. + + * README: Added more instructions for reporting bugs. + + * ns32k-opcode.h: Changed the acbb, acbw, and acbd insns. + + * vax.c: Replaced instances of LENGTH[STRING] with STRING[LENGTH]. + + * ns32k.c (encode_operand): Increased max size of bit field for exts + and inss instructions from 31 to 32 bits. + + * flonum-mult.c (flonum_multip): Fixed typo. + + * m68kc.: Allow #32 to be the same as #0 for bit-field ops. + + * make-gas.com, version.c, hex-value.c, flonum-const.c: VMS fixes. + + * ns32k.c, ns32k-opcode.h: More fixes from taylor@think.com. + Mostly typos in comments, etc. + + * ns32k-opcode.h: Fixed size of immediate operands to andw and andd + instructions. + +(In version 1.32) Jay Fenlason (hack) + + * read.c (s_set): Fixed misnamed variable. + + * as.c: Don't hang if given an invalid option. + + * m68k.c: Fixed bug in creating absolute long addresses for branches. + + * ns3k*: Some small ns32k patches. + + * m68k.c: Recognize 0rnan, 0rinf, 0r-inf. + + * app.c: Don't dump core on unterminated strings. + + * symbols.c: Give reasonable error messages. + + * ns32k*: Allow -m32032 and -m32532 options. + + * atof-*.c: Added support for NaN, Inf, and -Inf in atof_generic and + the various descriptions. + + * m68k.c (add_fix): Replace occurrences of "width==" with + "(width)==". This correct a precedence problem. + + * write.c, struc-symbol.h, m68k-opcode.h, m-hpux.h, Makefile: Changes + for HP-UX from Chris Hanson (cph@kleph.ai.mit.edu). + + * m68k-opcode.h: Reorder movem insns so gdb will see the ones using the + register list syntax first. + + * symbols.c (colon): Give more useful error messages when something was + defined as a .comm and is now trying to be defined locally. + Also, redefining a symbol is a fatal, not a warning. + + * m68k.c: Fixed a bug in using bignums as literal bit patterns for + floating-point numbers. + +(In version 1.31) Jay Fenlason (hack) + + * i386*: More patches. + + * Moved machine-dependent option parsing into the machine-dependent + source files. + +(In version 1.30) Jay Fenlason (hack) + + * i386*: New new version. + + * atof-m68k.c: Changed to be smaller, with somewhat better modularity. + Also fixed an obscure bug wherein next_bits would return random bits. + + * m68k.c: Be more careful about creating PC-relative addressing modes + on the 68000 and 68010. + + * frags.c (frag_new): Zero out the new frag. + + * Don't choke on "foo= bar" or on formfeeds. + + * read.c: Allow Sun-syntax local labels #ifdef SUN_ASM_SYNTAX. + * m-sun3.h: Defined SUN_ASM_SYNTAX. + +(In version 1.29) Jay Fenlason (hack) + + * i386.c: Newer version that fixes a bug wherein a jump instruction + would be split between two frags. + + * i386*: New version. + + * m68k.c: #ifdef M_SUN and -m68010, produce Sun-2 executables. + +(In version 1.28) Jay Fenlason (hack) + + * m68k.c: Added .single pseudo-op. + + * Made ". = X" and ".set .,X" equivalent to ".org X". + The pseudo-symbol "." has the value of the location the assembler is + currently assembling to. + +(In version 1.27) Jay Fenlason (hack) + + * Merged ns32k and i386 support. + +(In version 1.26) Jay Fenlason (hack) + + * Added partial ns32k support. + + * Added RMS's evil .word misfeature. Invented the -k (kludge) option + to warn that this misfeature was used. + + * Modified some files to get rid of warnings from GCC. + + * Added fix so that / can also be a comment character by itself. + +(In version 1.25) Jay Fenlason (hack) + + * Installed patches for VMS. + + * as.h (SIZEOF_STRUCT_FRAG): Added space before backslash-newline. + + * messages.c: Fixed typo. + + * app.c: Handle : correctly. + + * error.c: Removed; no longer used. + + * m68k-opcode.h: Added fnop. + Fixed to correctly handle fmovem with a register list and + non-predecriment addressing mode. + + * m68k-opcode.h: Fixed to know about long form of FBcc insns. + + * write.c: Warn if a fixup ended up being wider than its field width. + +(In version 1.24) Jay Fenlason (hack) + + * Accept and ignore -mc68010 and -m68010 switches. + + * Correctly assemble long subroutine calls on the 68000 without using a + 68020-specific instruction. + + * When calling with no filenames, read stdin. + +(In version 1.23) Jay Fenlason (hack) + + * app.c: Rewritten. + + * xmalloc.c, xrealloc.c: Replaced to work with GCC. + +(In version 1.22) Jay Fenlason (hack) + + * write.c: Fixed a VMS bug. + + * m68k.c: Fixed a bug having to do with turning absolute into + PC-relative. + + * atof-m68k.c (atof_m68k, gen_to_words): Try to avoid a problem with + running off the end of the LITTLENUMS. + + * vax.c: Fixed so parenthesized expressions work. + + * atof-generic.c: Added a cast that fixes problems with some C + compilers. + +(In version 1.21) + + * Changes for VMS support and correct bitfield order for + cross-assembly. + +(In version 1.20) + + * m68k*: Fixed "fmovel #N, fpcr". Added fpcr and fpsr to the list of + registers. + +(In version 1.19) + + * m68k.c? (md_convert_frag): Don't put the fixups for absolute long to + PC-relative in the data segment. + + * atof-generic.c: #include #ifdef sparc. + +(In version 1.18) + + * Re-fixed _vfprintf stuff (?). + + * Made "movem REG, ADDR" work. + + * Improved preprocessing, without temporary files. + +(In version 1.17) + + * Don't produce an undefined empty symbol for ".globl foo," (a line + ending with a comma). + + * Fixed a bug wherein ".long X" became ".long 0" on the Sparc. + + * Fixed a bug which caused many "#APP" "#NO_APP" pairs to dump core. + + * Fixed calls to _doprnt to call _vfprintf #ifndef NO_VARARGS. + +(In version 1.16) + + * Merged HP-UX changes from Chris Hanson (cph@zurich.ai.mit.edu). + + * flonum-multip.c: Renamed to flonum-mult.c. + + * m-hpux.h: Created. + + * m68k.c (bcopy): Fixed. + +(In version 1.15) + + * struct-symbol.h: Renamed to struc-symbol.h. + +(In version 1.14) + + * vax.c: Added a quick fix for the offset of fixed-width branches not + fitting in the field given. + + * gdb-lines.c, read.c: Added support for .gdline and .gdbline + pseudo-ops. + +(In version 1.13) + + * read.c, atof-generic.c: Fixed bugs in reading in floating-point + numbers. + + * m68k-opcode.h: Made "fmovep a0@, fp0" work. + +(In version 1.12) + + * write.c: Fixed an obscure bug in relaction that would occasionally + cause the assembler to stop relaxing when it really had at least one + more pass to do. + +(In version 1.11) + + * m68k*: Allow register lists in fmovem. + + * Added more floating-point exponents. + + * Print an error message on exponent overflow. + +(In version 1.10) + + * Fixed floating point bugs that made it generate incorrect numbers for + values over 10^16 or so. + +(In version 1.09) + + * Fixed bug wherein you couldn't forward reference local label 0. + +(In version 1.08) + + * m68k.c, m68k-opcode.h: Added support for fmovem with register lists. + + * Fixed an obscure bug having to do with generating PC-relative + addressing mode for things in the middle of the instruction instead of + at the end. + +Wed Mar 1 15:29:24 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * *.*: Modified copyright notices to reflect new General Public + License. + + * Makefile: Added copyright notice. + +Fri Feb 17 09:42:01 1989 Jay Fenlason (hack at spiff) + + * Patched frags.c so that new frags start out bzero()ed. + +Thu Jan 26 14:23:44 1989 Jay Fenlason (hack at apple-gunkies.ai.mit.edu) + + * Added patches from pace to files as.h i386.c i386-opcode.h + imull foo,%eax no longer gets assembled into the 32-64 bit + multiply, which clobbers %edx behind gcc's back + + jcxz/jecxz were backwards + + There was a bug when using %ebp as a base register with no + displacement + + Instructions like andb $0xffffff, %al used to put out too many + immediate bytes + + The splitting jump instructions across frags could happen when + obstack_room()==6 too. + +Local Variables: +mode: indented-text +left-margin: 8 +version-control: never +End: diff --git a/gnu/usr.bin/as/Makefile b/gnu/usr.bin/as/Makefile new file mode 100644 index 0000000000..e75351b642 --- /dev/null +++ b/gnu/usr.bin/as/Makefile @@ -0,0 +1,15 @@ +# @(#)Makefile 6.1 (Berkeley) 3/3/91 + +PROG= as +SRCS= app.c append.c as.c atof-generic.c bignum-copy.c \ + expr.c flonum-const.c flonum-copy.c flonum-mult.c \ + frags.c hash.c hex-value.c input-file.c input-scrub.c \ + messages.c obstack.c output-file.c read.c subsegs.c \ + symbols.c version.c write.c xmalloc.c xrealloc.c +CFLAGS+= -I$(.CURDIR) -I$(.CURDIR)/config \ + -DSIGTY=void -Derror=as_fatal +.PATH: $(.CURDIR)/config + +.include "config/Makefile.$(MACHINE)" + +.include diff --git a/gnu/usr.bin/as/Makefile.gnu b/gnu/usr.bin/as/Makefile.gnu new file mode 100644 index 0000000000..4b81b0c1ad --- /dev/null +++ b/gnu/usr.bin/as/Makefile.gnu @@ -0,0 +1,356 @@ +# Makefile for GAS. +# Copyright (C) 1989, Free Software Foundation +# +# This file is part of GAS, the GNU Assembler. +# +# GAS is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 1, or (at your option) +# any later version. +# +# GAS is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GAS; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +# This makefile may be used to make the VAX, 68020, 80386, +# SPARC, ns32k, or i860 assembler(s). + +BINDIR = /usr/local/bin +#CC=gcc + +# If you are on a BSD system, un-comment the next two lines, and comment out +# the lines for SystemV and HPUX below +G0 = -g -I. #-O -Wall +LDFLAGS = $(CFLAGS) +# +# To compile gas on a System Five machine, comment out the two lines above +# and un-comment out the next three lines +# Comment out the -lPW on the LOADLIBES line if you are using GCC. +# G0 = -g -I. -DUSG +# LDFLAGS = $(CFLAGS) +# LOADLIBES = -lmalloc -lPW +# +# To compile gas for HPUX, link m-hpux.h to m68k.h , and un-comment the +# next two lines. (If you are using GCC, comment out the alloca.o part) +# (Get alloca from the emacs distribution, or use GCC.) +# HPUX 7.0 may have a bug in setvbuf. gas gives an error message like +# 1:"Unknown operator" -- Statement 'NO_APP' ignored +# if setvbuf is broken. Re-compile input-file.c (and only input-file.c +# with -DVMS and the problem should go away. +# +# G0 = -g -I. -DUSG +# LOADLIBES = alloca.o +# +# To compile gas for a Sequent Symmetry, comment out all the above lines, +# and un-comment the next two lines. +# G0 = -g -I. -DUSE_SYSTEM_HDR -DEXEC_VERSION=1 +# LOADLIBES = -lc /usr/att/lib/libc.a + +# If you just want to compile the vax assembler, type 'make avax' + +# If you just want to compile the i386 assembler, type 'make a386' + +# If you just want to compile the ns32k assembler, type 'make a32k' + +# If you just want to compile the sparc assembler, type 'make asparc' + +# If you just want to compile the mc68020 assembler, make sure m68k.h +# is correctly set up, and type type 'make a68' (Except on HPUX machines, +# where you will have to make the changes marked below before typing +# 'make a68' +# m68k.h should be a symbolic or hard-link to one of +# m-sun3.h , m-hpux.h or m-generic.h +# depending on which machine you want to compile the 68020 assembler for. +# +# If you want the 68k assembler to be completely compatable with the the +# SUN one, un-comment the -DSUN_ASM_SYNTAX line below. +# +# If you machine does not have vfprintf, but does have _doprnt(), +# remove the # from the -DNO_VARARGS line below. +# +# If the return-type of a signal-hander is void (instead of int), +# remove the # from the -DSIGTY line below. +# +# To include the mc68851 mmu coprocessor instructions in the 68020 assembler, +# remove the # from the -Dm68851 line below. +# +# If you want the 68020 assembler use a register prefix character, un-comment +# the REGISTER_PREFIX line, and (maybe) change the '%' to the appropriate +# character. +# +# If you want the assembler to treat .L* or ..* symbols as local, instead of +# the usual L* symbols, un-comment the DOT_LABEL_PREFIX line. +# +# If you want the 80386 assembler to correctly handle fsub/fsubr and fdiv/fdivr +# opcodes (unlike most 80386 assemblers), remove the # from +# the -DNON_BROKEN_WORDS line below. +# +# To compile 80386 Gas for the Sequent Symmetry, un-comment the -DEXEC_VERSION +# and the -DUSE_SYSTEM_HDR lines below. +# +# To compile gas for the HP 9000/300 un-comment the -DUSE_HP_HDR line below. +# +# For the ns32k, the options are 32532 or 32032 CPU and 32381 or 32081 FPU. +# To select the NS32532, remove the # from the -DNS32532 line below. +# To compile in tne NS32381 opcodes in addition to the NS32081 opcodes +# (the 32381 is a superset of the 32081), remove the # from the -DNS32381 +# line below. +# +# For the ns32k on a Sequent, uncomment the SEQUENT_COMPATABILITY line below. +# +# If you want the .align N directive to align to the next N byte boundry, +# instead of the next 1< .fname + mkdir `cat .fname` + + ln COPYING README ChangeLog $A $H $Z $Y $X $W $V $U Makefile `cat .fname` + tar cvhZf `cat .fname`.tar.Z `cat .fname` + -rm -r `cat .fname` + -rm .fname + +clean: + rm -f a avax a68 a386 a32k asparc $a $v $w $x $y $z a core gmon.out bugs a.out + +install: a + cp a $(BINDIR)/gas + + +# General .o-->.h dependencies + +app.o: as.h +as.o: a.out.gnu.h as.h read.h struc-symbol.h write.h +atof-generic.o: flonum.h +bignum-copy.o: bignum.h +expr.o: a.out.gnu.h as.h expr.h flonum.h obstack.h read.h struc-symbol.h +expr.o: symbols.h +flonum-const.o: flonum.h +flonum-copy.o: flonum.h +flonum-mult.o: flonum.h +flonum-normal.o:flonum.h +flonum-print.o: flonum.h +frags.o: a.out.gnu.h as.h frags.h obstack.h struc-symbol.h subsegs.h +#gdb.o: as.h +#gdb-blocks.o: as.h +#gdb-lines.o: as.h frags.h obstack.h +#gdb-symbols.o: a.out.gnu.h as.h struc-symbol.h +hash.o: hash.h +input-file.o: input-file.h +input-scrub.o: as.h input-file.h read.h +messages.o: as.h +obstack.o: obstack.h +read.o: a.out.gnu.h as.h expr.h flonum.h frags.h hash.h md.h obstack.h +read.o: read.h struc-symbol.h symbols.h +subsegs.o: a.out.gnu.h as.h frags.h obstack.h struc-symbol.h subsegs.h write.h +symbols.o: a.out.gnu.h as.h frags.h hash.h obstack.h struc-symbol.h symbols.h +write.o: a.out.gnu.h as.h md.h obstack.h struc-symbol.h subsegs.h +write.o: symbols.h write.h + +flonum.h: bignum.h + diff --git a/gnu/usr.bin/as/NOTES b/gnu/usr.bin/as/NOTES new file mode 100644 index 0000000000..b3f3f92d2e --- /dev/null +++ b/gnu/usr.bin/as/NOTES @@ -0,0 +1,35 @@ +gdb debugging of assembly sources: + write a function linestab() that generates a .stabd symbol + independently of the input + write a function filestab() to generate a .stabs symbol + we need to take especial care with #line directives + since we want to handle locore, and locore is passed thru cpp + this could be tough + outline of a solution: + cpp sends us lines of the form + # logical-line "logical-file" trash + these lines are interpreted ahead of the gas preprocess pass + in the starting state, the logical filename is the same + as the real filename (in case there're no #lines) + the initial logical line number is 1 + every time we're ready to process a new instruction line, + if the source file has changed, + emit a .stabs for the logical file + emit a .stabd for the logical line + bump the logical line number + can gas eat multiple actual lines in one insn? + +i386 nits: + jmp *$foo produces a short relative branch + string quotes in comments + Bill says gas eats text across newlines to find matches + works fine for me + I think it's most likely due to cpp + make / no longer be a comment char + it's now like the VAX: # is the only comment char + incorrectly assembles lcall, int3, into, bsr/f instructions + constant expressions fail if more than a few terms + gives (low+2)*3+4*5 as an example + works fine for me + cpp seems to think $ is a valid literal + use -$ in /usr/bin/cpp diff --git a/gnu/usr.bin/as/README.gnu b/gnu/usr.bin/as/README.gnu new file mode 100644 index 0000000000..46f135fcfd --- /dev/null +++ b/gnu/usr.bin/as/README.gnu @@ -0,0 +1,133 @@ +This is the beta-test version of the GNU assembler. (Probably +around Version 1.35, but check version.c which gets updated more +often than this readme.) + +The assembler has been modified to support a feature that is +potentially useful when assembling compiler output, but which may +confuse assembly language programmers. If assembler encounters a +.word pseudo-op of the form symbol1-symbol2 (the difference of two +symbols), and the difference of those two symbols will not fit in 16 +bits, the assembler will create a branch around a long jump to +symbol1, and insert this into the output directly before the next +label: The .word will (instead of containing garbage, or giving an +error message) contain (the address of the long jump)-symbol2. This +allows the assembler to assemble jump tables that jump to locations +very far away into code that works properly. If the next label is +more than 32K away from the .word, you lose (silently) RMS claims +this will never happen. If the -k option is given, you will get a +warning message when this happens. + +These files are currently set up to allow you to compile all of the +versions of the assembler (68020, VAX, ns32k, and i386) on the same +machine. To compile the 68020 version, type 'make a68'. To compile +the VAX version, type 'make avax'. To compile the ns32k version, +type 'make a32k'. To compile the Intel 80386 version, type 'make +a386'. The Makefile contains instructions on how to make one of the +assemblers compile as the default. + +Before you can compile the 68020 version of the assembler, you must +make m68k.h be a link to m-sun3.h , m-hpux.h or m-generic.h . If +you are on a SUN-3 (or other machine that uses a magic number of +(2 << 16) | OMAGIC type 'ln -s m-sun3.h m68k.h' else if you are on a +machine running HP-UX, type 'ln m-hpux.h m689k.h' else type +'ln -s m-generic.h m68k.h' If your machine does not support symbolic +links, omit the '-s'. + +See the instructions in the Makefile for compiling gas for the Sequent +Symmetry (dynix 3.0.12 + others?) or for the HP 9000/300 + +If your machine does not have both varargs.h and vfprintf(), but does have +_doprnt() add -DNO_VARARGS to the CFLAGS line in the makefile. If your +machine has neither vfprintf() or _doprnt(), you will have to change +messages.c in order to get readable error messages from the assembler. + + + REPORTING BUGS IN GAS + +Bugs in gas should be reported to bug-gnu-utils@prep.ai.mit.edu If you can't +get through to prep, try hack@gnu.ai.mit.edu or hack@media-lab.media.mit.edu + +If you report a bug in GAS, please remember to include: + +A description of exactly what went wrong. + +The type of machine GAS was running on (VAX, 68020, etc), + +The Operating System GAS was running under. + +The options given to GAS. + +The actual input file that caused the problem. + +It is silly to report a bug in GAS without including an input file for +GAS. Don't ask us to generate the file just because you made it from +files you think we have access to. + +1. You might be mistaken. +2. It might take us a lot of time to install things to regenerate that file. +3. We might get a different file from the one you got, and might not see any +bug. + +To save us these delays and uncertainties, always send the input file +for the program that failed. + +If the input file is very large, and you are on the internet, you may +want to make it avaliable for anonymous FTP instead of mailing it. If you +do, include instructions for FTP'ing it in your bug report. + +------------------------------ README.APOLLO --------------------------------- + +The changes required to get the GNU C compiler running on +Apollo 68K platforms are available via anonymous ftp from +labrea.stanford.edu (36.8.0.47) in the form of a compressed +tar file named "/pub/gnu/apollo-gcc-1.37.tar.Z". +The size of the file is 84145 bytes. + +To build GCC for the Apollo you'll need the virgin FSF +distributions of bison-1.03, gas-1.34, and gcc-1.37. They +are also on labrea.stanford.edu as well as prep.ai.mit.edu. +My changes are to enable gas to produce Apollo COFF object +files and allow gcc to parse some of the syntax extensions +which appear in Apollo C header files. Note that the +COFF encapsulation technique cannot be used on the Apollo. + +The tar file should be unpacked in the directory containing +the gas-1.34 and gcc-1.37 directories; a few files will be overlaid, +and an APOLLO-GCC-README file will appear in the top directory. +This file contains detailed instructions on how to proceed. + +These changes will only work for SR10.1 or later systems, using +the 6.6 or later version of the Apollo C compiler. + +If you do not have ftp access, I can mail you the changes in the +form of diffs; they are approximately 40K in length. If you request +them, be sure to give me a voice phone number so I can contact you +in case I can't send you mail; I've had several requests in the +past from people I can't contact. + +By the way, I'm working on getting the GNU C++ compiler running; +there are a couple problems to solve. I hope to be able to announce +the Apollo version shortly after the 1.37 version is released. + +John Vasta Hewlett-Packard Apollo Systems Division +vasta@apollo.hp.com M.S. CHA-01-LT +(508) 256-6600 x6362 300 Apollo Drive, Chelmsford, MA 01824 +UUCP: {decwrl!decvax, mit-eddie, attunix}!apollo!vasta + +------------------------------------ + +You might refer others who are interested in a similar thing. + +Kevin Buchs buchs@mayo.edu + + +------------------------------ README.COFF ----------------------------------- + +If you have a COFF system, you may wish to aquire + + UUCP: osu-cis!~/gnu/coff/gnu-coff.tar.Z + or + FTP: tut.cis.ohio-state.edu:/pub/gnu/coff/gnu-coff.tar.Z + +These contain patches for gas that will make it produce COFF output. +I have never seen these patches, so I don't know how well they work. diff --git a/gnu/usr.bin/as/app.c b/gnu/usr.bin/as/app.c new file mode 100644 index 0000000000..a0ec8a28e8 --- /dev/null +++ b/gnu/usr.bin/as/app.c @@ -0,0 +1,392 @@ +/* This is the Assembler Pre-Processor + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* App, the assembler pre-processor. This pre-processor strips out excess + spaces, turns single-quoted characters into a decimal constant, and turns + # into a .line ;.file pair. + This needs better error-handling. + */ +#include +#ifdef USG +#define bzero(s,n) memset(s,0,n) +#endif +#if !defined(__STDC__) && !defined(const) +#define const /* Nothing */ +#endif + +static char lex [256]; +static const char symbol_chars[] = + "$._ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + +extern const char comment_chars[]; +extern const char line_comment_chars[]; + +#define LEX_IS_SYMBOL_COMPONENT (1) +#define LEX_IS_WHITESPACE (2) +#define LEX_IS_LINE_SEPERATOR (4) +#define LEX_IS_COMMENT_START (8) /* JF added these two */ +#define LEX_IS_LINE_COMMENT_START (16) +#define IS_SYMBOL_COMPONENT(c) (lex [c] & LEX_IS_SYMBOL_COMPONENT) +#define IS_WHITESPACE(c) (lex [c] & LEX_IS_WHITESPACE) +#define IS_LINE_SEPERATOR(c) (lex [c] & LEX_IS_LINE_SEPERATOR) +#define IS_COMMENT(c) (lex [c] & LEX_IS_COMMENT_START) +#define IS_LINE_COMMENT(c) (lex [c] & LEX_IS_LINE_COMMENT_START) + +void +do_scrub_begin() +{ + const char *p; + + bzero (lex, sizeof(lex)); /* Trust NOBODY! */ + lex [' '] |= LEX_IS_WHITESPACE; + lex ['\t'] |= LEX_IS_WHITESPACE; + for (p =symbol_chars;*p;++p) + lex [*p] |= LEX_IS_SYMBOL_COMPONENT; + lex ['\n'] |= LEX_IS_LINE_SEPERATOR; +#ifdef DONTDEF + lex [':'] |= LEX_IS_LINE_SEPERATOR; +#endif + lex [';'] |= LEX_IS_LINE_SEPERATOR; + for (p=comment_chars;*p;p++) + lex[*p] |= LEX_IS_COMMENT_START; + for (p=line_comment_chars;*p;p++) + lex[*p] |= LEX_IS_LINE_COMMENT_START; +} + +FILE *scrub_file; + +int +scrub_from_file() +{ + return getc(scrub_file); +} + +void +scrub_to_file(ch) +int ch; +{ + ungetc(ch,scrub_file); +} + +char *scrub_string; +char *scrub_last_string; + +int +scrub_from_string() +{ + return scrub_string == scrub_last_string ? EOF : *scrub_string++; +} + +void +scrub_to_string(ch) +int ch; +{ + *--scrub_string=ch; +} + +int +do_scrub_next_char(get,unget) +int (*get)(); +void (*unget)(); +/* FILE *fp; */ +{ + /* State 0: beginning of normal line + 1: After first whitespace on normal line (flush more white) + 2: After first non-white on normal line (keep 1white) + 3: after second white on normal line (flush white) + 4: after putting out a .line, put out digits + 5: parsing a string, then go to old-state + 6: putting out \ escape in a "d string. + 7: After putting out a .file, put out string. + 8: After putting out a .file string, flush until newline. + -1: output string in out_string and go to the state in old_state + -2: flush text until a '*' '/' is seen, then go to state old_state + */ + + static state; + static old_state; + static char *out_string; + static char out_buf[20]; + static add_newlines; + int ch; + + if(state==-1) { + ch= *out_string++; + if(*out_string==0) { + state=old_state; + old_state=3; + } + return ch; + } + if(state==-2) { + for(;;) { + do ch=(*get)(); + while(ch!=EOF && ch!='\n' && ch!='*'); + if(ch=='\n' || ch==EOF) + return ch; + ch=(*get)(); + if(ch==EOF || ch=='/') + break; + (*unget)(ch); + } + state=old_state; + return ' '; + } + if(state==4) { + ch=(*get)(); + if(ch==EOF || (ch>='0' && ch<='9')) + return ch; + else { + while(ch!=EOF && IS_WHITESPACE(ch)) + ch=(*get)(); + if(ch=='"') { + (*unget)(ch); + out_string="; .file "; + old_state=7; + state= -1; + return *out_string++; + } else { + while(ch!=EOF && ch!='\n') + ch=(*get)(); + return ch; + } + } + } + if(state==5) { + ch=(*get)(); + if(ch=='"') { + state=old_state; + return '"'; + } else if(ch=='\\') { + state=6; + return ch; + } else if(ch==EOF) { + as_warn("End of file in string: inserted '\"'"); + state=old_state; + (*unget)('\n'); + return '"'; + } else { + return ch; + } + } + if(state==6) { + state=5; + ch=(*get)(); + switch(ch) { + /* This is neet. Turn "string + more string" into "string\n more string" + */ + case '\n': + (*unget)('n'); + add_newlines++; + return '\\'; + + case '"': + case '\\': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + break; + default: + as_warn("Unknown escape '\\%c' in string: Ignored",ch); + break; + + case EOF: + as_warn("End of file in string: '\"' inserted"); + return '"'; + } + return ch; + } + + if(state==7) { + ch=(*get)(); + state=5; + old_state=8; + return ch; + } + + if(state==8) { + do ch= (*get)(); + while(ch!='\n'); + state=0; + return ch; + } + + flushchar: + ch=(*get)(); + switch(ch) { + case ' ': + case '\t': + do ch=(*get)(); + while(ch!=EOF && IS_WHITESPACE(ch)); + if(ch==EOF) + return ch; + if(IS_COMMENT(ch) || (state==0 && IS_LINE_COMMENT(ch)) || ch=='/' || IS_LINE_SEPERATOR(ch)) { + (*unget)(ch); + goto flushchar; + } + (*unget)(ch); + if(state==0 || state==2) { + state++; + return ' '; + } else goto flushchar; + + case '/': + ch=(*get)(); + if(ch=='*') { + for(;;) { + do { + ch=(*get)(); + if(ch=='\n') + add_newlines++; + } while(ch!=EOF && ch!='*'); + ch=(*get)(); + if(ch==EOF || ch=='/') + break; + (*unget)(ch); + } + if(ch==EOF) + as_warn("End of file in '/' '*' string: */ inserted"); + + (*unget)(' '); + goto flushchar; + } else { + if(IS_COMMENT('/') || (state==0 && IS_LINE_COMMENT('/'))) { + (*unget)(ch); + ch='/'; + goto deal_misc; + } + if(ch!=EOF) + (*unget)(ch); + return '/'; + } + break; + + case '"': + old_state=state; + state=5; + return '"'; + break; + + case '\'': + ch=(*get)(); + if(ch==EOF) { + as_warn("End-of-file after a ': \000 inserted"); + ch=0; + } + sprintf(out_buf,"(%d)",ch&0xff); + old_state=state; + state= -1; + out_string=out_buf; + return *out_string++; + + case ':': + if(state!=3) + state=0; + return ch; + + case '\n': + if(add_newlines) { + --add_newlines; + (*unget)(ch); + } + case ';': + state=0; + return ch; + + default: + deal_misc: + if(state==0 && IS_LINE_COMMENT(ch)) { + do ch=(*get)(); + while(ch!=EOF && IS_WHITESPACE(ch)); + if(ch==EOF) { + as_warn("EOF in comment: Newline inserted"); + return '\n'; + } + if(ch<'0' || ch>'9') { + while(ch!=EOF && ch!='\n') + ch=(*get)(); + if(ch==EOF) + as_warn("EOF in Comment: Newline inserted"); + state=0; + return '\n'; + } + (*unget)(ch); + old_state=4; + state= -1; + out_string=".line "; + return *out_string++; + + } else if(IS_COMMENT(ch)) { + do ch=(*get)(); + while(ch!=EOF && ch!='\n'); + if(ch==EOF) + as_warn("EOF in comment: Newline inserted"); + state=0; + return '\n'; + + } else if(state==0) { + state=2; + return ch; + } else if(state==1) { + state=2; + return ch; + } else { + return ch; + + } + case EOF: + if(state==0) + return ch; + as_warn("End-of-File not at end of a line"); + } + return -1; +} + +#ifdef TEST + +char comment_chars[] = "|"; +char line_comment_chars[] = "#"; + +main() +{ + int ch; + + app_begin(); + while((ch=do_scrub_next_char(stdin))!=EOF) + putc(ch,stdout); +} + +as_warn(str) +char *str; +{ + fputs(str,stderr); + putc('\n',stderr); +} +#endif diff --git a/gnu/usr.bin/as/append.c b/gnu/usr.bin/as/append.c new file mode 100644 index 0000000000..d51a27fc65 --- /dev/null +++ b/gnu/usr.bin/as/append.c @@ -0,0 +1,37 @@ +/* Append a string ontp another string + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* JF: This is silly. Why not stuff this in some other file? */ +#ifdef USG +#define bcopy(from,to,n) memcpy(to,from,n) +#endif + +void +append (charPP, fromP, length) +char **charPP; +char *fromP; +unsigned long length; +{ + if (length) { /* Don't trust bcopy() of 0 chars. */ + bcopy (fromP, * charPP,(int) length); + *charPP += length; + } +} + +/* end: append.c */ diff --git a/gnu/usr.bin/as/as.1 b/gnu/usr.bin/as/as.1 new file mode 100644 index 0000000000..291027923a --- /dev/null +++ b/gnu/usr.bin/as/as.1 @@ -0,0 +1,271 @@ +.\" Copyright (c) 1991, 1992 Free Software Foundation +.\" See section COPYING for conditions for redistribution +.TH as 1 "21 January 1992" "cygnus support" "GNU Development Tools" + +.SH NAME +GNU as\-\-the portable GNU assembler. + +.SH SYNOPSIS +.na +.B as +.RB "[\|" \-a "\||\|" \-al "\||\|" -as\c +\&\|] +.RB "[\|" \-D "\|]" +.RB "[\|" \-f "\|]" +.RB "[\|" \-I +.I path\c +\&\|] +.RB "[\|" \-K "\|]" +.RB "[\|" \-L "\|]" +.RB "[\|" \-o +.I objfile\c +\&\|] +.RB "[\|" \-R "\|]" +.RB "[\|" \-v "\|]" +.RB "[\|" \-w "\|]" +.RB "[\|" \-\^\- "\ |\ " \c +.I files\c +\&\|.\|.\|.\|] + +.I i960-only options: +.br +.RB "[\|" \-ACA "\||\|" \-ACA_A "\||\|" \-ACB\c +.RB "\||\|" \-ACC "\||\|" \-AKA "\||\|" \-AKB\c +.RB "\||\|" \-AKC "\||\|" \-AMC "\|]" +.RB "[\|" \-b "\|]" +.RB "[\|" \-norelax "\|]" + +.I m680x0-only options: +.br +.RB "[\|" \-l "\|]" +.RB "[\|" \-mc68000 "\||\|" \-mc68010 "\||\|" \-mc68020 "\|]" +.ad b + +.SH DESCRIPTION +GNU \c +.B as\c +\& is really a family of assemblers. +If you use (or have used) the GNU assembler on one architecture, you +should find a fairly similar environment when you use it on another +architecture. Each version has much in common with the others, +including object file formats, most assembler directives (often called +\c +.I pseudo-ops)\c +\& and assembler syntax. + +For information on the syntax and pseudo-ops used by GNU \c +.B as\c +\&, see `\|\c +.B as\c +\|' entry in \c +.B info \c +(or the manual \c +.I +.I +Using as: The GNU Assembler\c +\&). + +\c +.B as\c +\& is primarily intended to assemble the output of the GNU C +compiler \c +.B gcc\c +\& for use by the linker \c +.B ld\c +\&. Nevertheless, +we've tried to make \c +.B as\c +\& assemble correctly everything that the native +assembler would. +This doesn't mean \c +.B as\c +\& always uses the same syntax as another +assembler for the same architecture; for example, we know of several +incompatible versions of 680x0 assembly language syntax. + +Each time you run \c +.B as\c +\& it assembles exactly one source +program. The source program is made up of one or more files. +(The standard input is also a file.) + +If \c +.B as\c +\& is given no file names it attempts to read one input file +from the \c +.B as\c +\& standard input, which is normally your terminal. You +may have to type \c +.B ctl-D\c +\& to tell \c +.B as\c +\& there is no more program +to assemble. Use `\|\c +.B \-\^\-\c +\|' if you need to explicitly name the standard input file +in your command line. + +.B as\c +\& may write warnings and error messages to the standard error +file (usually your terminal). This should not happen when \c +.B as\c +\& is +run automatically by a compiler. Warnings report an assumption made so +that \c +.B as\c +\& could keep assembling a flawed program; errors report a +grave problem that stops the assembly. + +.SH OPTIONS +.TP +.BR \-a \||\| \-al \||\| \-as +Turn on assembly listings; `\|\c +.B \-al\c +\&\|', listing only, `\|\c +.B \-as\c +\&\|', symbols +only, `\|\c +.B \-a\c +\&\|', everything. +.TP +.B \-D +This option is accepted only for script compatibility with calls to +other assemblers; it has no effect on \c +.B as\c +\&. +.TP +.B \-f +``fast''--skip preprocessing (assume source is compiler output). +.TP +.BI "\-I\ " path +Add +.I path +to the search list for +.B .include +directives. +.TP +.B \-K +Issue warnings when difference tables altered for long displacements. +.TP +.B \-L +Keep (in symbol table) local symbols, starting with `\|\c +.B L\c +\|' +.TP +.BI "\-o\ " objfile +Name the object-file output from \c +.B as +.TP +.B \-R +Fold data section into text section +.TP +.B \-v +Announce \c +.B as\c +\& version +.TP +.B \-W +Suppress warning messages +.TP +.IR "\-\^\-" "\ |\ " "files\|.\|.\|." +Source files to assemble, or standard input (\c +.BR "\-\^\-" ")" +.TP +.BI \-A var +.I +(When configured for Intel 960.) +Specify which variant of the 960 architecture is the target. +.TP +.B \-b +.I +(When configured for Intel 960.) +Add code to collect statistics about branches taken. +.TP +.B \-norelax +.I +(When configured for Intel 960.) +Do not alter compare-and-branch instructions for long displacements; +error if necessary. +.TP +.B \-l +.I +(When configured for Motorola 68000). +.br +Shorten references to undefined symbols, to one word instead of two. +.TP +.BR "\-mc68000" "\||\|" "\-mc68010" "\||\|" "\-mc68020" +.I +(When configured for Motorola 68000). +.br +Specify what processor in the 68000 family is the target (default 68020) + +.PP +Options may be in any order, and may be +before, after, or between file names. The order of file names is +significant. + +`\|\c +.B \-\^\-\c +\|' (two hyphens) by itself names the standard input file +explicitly, as one of the files for \c +.B as\c +\& to assemble. + +Except for `\|\c +.B \-\^\-\c +\|' any command line argument that begins with a +hyphen (`\|\c +.B \-\c +\|') is an option. Each option changes the behavior of +\c +.B as\c +\&. No option changes the way another option works. An +option is a `\|\c +.B \-\c +\|' followed by one or more letters; the case of +the letter is important. All options are optional. + +The `\|\c +.B \-o\c +\|' option expects exactly one file name to follow. The file +name may either immediately follow the option's letter (compatible +with older assemblers) or it may be the next command argument (GNU +standard). + +These two command lines are equivalent: +.br +.B +as\ \ \-o\ \ my\-object\-file.o\ \ mumble.s +.br +.B +as\ \ \-omy\-object\-file.o\ \ mumble.s + +.SH "SEE ALSO" +.RB "`\|" as "\|'" +entry in +.B +info\c +\&; +.I +Using as: The GNU Assembler\c +\&; +.BR gcc "(" 1 ")," +.BR ld "(" 1 ")." + +.SH COPYING +Copyright (c) 1991, 1992 Free Software Foundation, Inc. +.PP +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. +.PP +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. +.PP +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the Free Software Foundation instead of in +the original English. diff --git a/gnu/usr.bin/as/as.c b/gnu/usr.bin/as/as.c new file mode 100644 index 0000000000..db85525d5c --- /dev/null +++ b/gnu/usr.bin/as/as.c @@ -0,0 +1,324 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + */ + +#ifndef lint +static char sccsid[] = "@(#)as.c 6.3 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* as.c - GAS main program. + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * Main program for AS; a 32-bit assembler of GNU. + * Understands command arguments. + * Has a few routines that don't fit in other modules because they + * are shared. + * + * + * bugs + * + * : initialisers + * Since no-one else says they will support them in future: I + * don't support them now. + * + */ + +#ifdef _POSIX_SOURCE +#include /* For pid_t in signal.h */ +#endif +#include + +#define COMMON +#include "as.h" +#include "struc-symbol.h" +#include "write.h" + /* Warning! This may have some slightly strange side effects + if you try to compile two or more assemblers in the same + directory! + */ + +#ifndef SIGTY +#define SIGTY int +#endif + +SIGTY got_sig(); + +#ifdef DONTDEF +static char * gdb_symbol_file_name; +long int gdb_begin(); +#endif + +char *myname; /* argv[0] */ +extern char version_string[]; + +main(argc,argv) +int argc; +char **argv; +{ + int work_argc; /* variable copy of argc */ + char **work_argv; /* variable copy of argv */ + char *arg; /* an arg to program */ + char a; /* an arg flag (after -) */ + static const int sig[] = { SIGHUP, SIGINT, SIGPIPE, SIGTERM, 0}; + + extern int bad_error; /* Did we hit a bad error ? */ + + char *stralloc(); /* Make a (safe) copy of a string. */ + void symbol_begin(); + void read_begin(); + void write_object_file(); + + for(a=0;sig[a]!=0;a++) + if(signal(sig[a], SIG_IGN) != SIG_IGN) + signal(sig[a], got_sig); + + myname=argv[0]; + bzero (flagseen, sizeof(flagseen)); /* aint seen nothing yet */ + out_file_name = "a.out"; /* default .o file */ + symbol_begin(); /* symbols.c */ + subsegs_begin(); /* subsegs.c */ + read_begin(); /* read.c */ + md_begin(); /* MACHINE.c */ + input_scrub_begin(); /* input_scrub.c */ +#ifdef DONTDEF + gdb_symbol_file_name = 0; +#endif + /* + * Parse arguments, but we are only interested in flags. + * When we find a flag, we process it then make it's argv[] NULL. + * This helps any future argv[] scanners avoid what we processed. + * Since it is easy to do here we interpret the special arg "-" + * to mean "use stdin" and we set that argv[] pointing to "". + * After we have munged argv[], the only things left are source file + * name(s) and ""(s) denoting stdin. These file names are used + * (perhaps more than once) later. + */ + work_argc = argc-1; /* don't count argv[0] */ + work_argv = argv+1; /* skip argv[0] */ + for (;work_argc--;work_argv++) { + arg = * work_argv; /* work_argv points to this argument */ + + if (*arg!='-') /* Filename. We need it later. */ + continue; /* Keep scanning args looking for flags. */ + if (arg[1] == '-' && arg[2] == 0) { + /* "--" as an argument means read STDIN */ + /* on this scan, we don't want to think about filenames */ + * work_argv = ""; /* Code that means 'use stdin'. */ + continue; + } + /* This better be a switch. */ + arg ++; /* -> letter. */ + + while (a = * arg) {/* scan all the 1-char flags */ + arg ++; /* arg -> after letter. */ + a &= 0x7F; /* ascii only please */ + if (flagseen[a]) + as_warn("%s: Flag option -%c has already been seen!",myname,a); + flagseen[a] = TRUE; + switch (a) { + case 'f': + break; /* -f means fast - no need for "app" preprocessor. */ + + case 'D': + /* DEBUG is implemented: it debugs different */ + /* things to other people's assemblers. */ + break; + +#ifdef DONTDEF + case 'G': /* GNU AS switch: include gdbsyms. */ + if (*arg) /* Rest of argument is file-name. */ + gdb_symbol_file_name = stralloc (arg); + else if (work_argc) { /* Next argument is file-name. */ + work_argc --; + * work_argv = NULL; /* Not a source file-name. */ + gdb_symbol_file_name = * ++ work_argv; + } else + as_warn( "%s: I expected a filename after -G",myname); + arg = ""; /* Finished with this arg. */ + break; +#endif + +#ifndef WORKING_DOT_WORD + case 'k': + break; +#endif + + case 'L': /* -L means keep L* symbols */ + break; + + case 'o': + if (*arg) /* Rest of argument is object file-name. */ + out_file_name = stralloc (arg); + else if (work_argc) { /* Want next arg for a file-name. */ + * work_argv = NULL; /* This is not a file-name. */ + work_argc--; + out_file_name = * ++ work_argv; + } else + as_warn("%s: I expected a filename after -o. \"%s\" assumed.",myname,out_file_name); + arg = ""; /* Finished with this arg. */ + break; + + case 'R': + /* -R means put data into text segment */ + break; + + case 'v': +#ifdef VMS + { + extern char *compiler_version_string; + compiler_version_string = arg; + } +#else /* not VMS */ + fprintf(stderr,version_string); + if(*arg && strcmp(arg,"ersion")) + as_warn("Unknown -v option ignored"); +#endif + while(*arg) arg++; /* Skip the rest */ + break; + + case 'W': + /* -W means don't warn about things */ + break; + + case 'g': + /* + * -g asks gas to produce gdb/dbx line number + * and file name stabs so that an assembly + * file can be handled by a source debugger. + */ + break; + + default: + --arg; + if(md_parse_option(&arg,&work_argc,&work_argv)==0) + as_warn("%s: I don't understand '%c' flag!",myname,a); + if(arg && *arg) + arg++; + break; + } + } + /* + * We have just processed a "-..." arg, which was not a + * file-name. Smash it so the + * things that look for filenames won't ever see it. + * + * Whatever work_argv points to, it has already been used + * as part of a flag, so DON'T re-use it as a filename. + */ + *work_argv = NULL; /* NULL means 'not a file-name' */ + } +#ifdef DONTDEF + if (gdb_begin(gdb_symbol_file_name) == 0) + flagseen ['G'] = 0; /* Don't do any gdbsym stuff. */ +#endif + /* Here with flags set up in flagseen[]. */ + perform_an_assembly_pass(argc,argv); /* Assemble it. */ + if (seen_at_least_1_file() && !bad_error) + write_object_file();/* relax() addresses then emit object file */ + input_scrub_end(); + md_end(); /* MACHINE.c */ +#ifndef VMS + exit(bad_error); /* WIN */ +#else /* VMS */ + exit(!bad_error); /* WIN */ +#endif /* VMS */ +} + + +/* perform_an_assembly_pass() + * + * Here to attempt 1 pass over each input file. + * We scan argv[*] looking for filenames or exactly "" which is + * shorthand for stdin. Any argv that is NULL is not a file-name. + * We set need_pass_2 TRUE if, after this, we still have unresolved + * expressions of the form (unknown value)+-(unknown value). + * + * Note the un*x semantics: there is only 1 logical input file, but it + * may be a catenation of many 'physical' input files. + */ +perform_an_assembly_pass (argc, argv) +int argc; +char ** argv; +{ + char * buffer; /* Where each bufferful of lines will start. */ + void read_a_source_file(); + int saw_a_file = 0; + + text_fix_root = NULL; + data_fix_root = NULL; + need_pass_2 = FALSE; + + argv++; /* skip argv[0] */ + argc--; /* skip argv[0] */ + while (argc--) { + if (*argv) { /* Is it a file-name argument? */ + /* argv -> "" if stdin desired, else -> filename */ + if (buffer = input_scrub_new_file (*argv) ) { + saw_a_file++; + read_a_source_file(buffer); + } + } + argv++; /* completed that argv */ + } + if(!saw_a_file) + if(buffer = input_scrub_new_file("") ) + read_a_source_file(buffer); +} + +/* + * stralloc() + * + * Allocate memory for a new copy of a string. Copy the string. + * Return the address of the new string. Die if there is any error. + */ + +char * +stralloc (str) +char * str; +{ + register char * retval; + register long int len; + + len = strlen (str) + 1; + retval = xmalloc (len); + (void)strcpy (retval, str); + return (retval); +} + +lose() +{ + as_fatal( "%s: 2nd pass not implemented - get your code from random(3)",myname ); +} + +SIGTY +got_sig(sig) +int sig; +{ + static here_before = 0; + + as_bad("Interrupted by signal %d",sig); + if(here_before++) + exit(1); +} + +/* end: as.c */ diff --git a/gnu/usr.bin/as/as.h b/gnu/usr.bin/as/as.h new file mode 100644 index 0000000000..8de405257b --- /dev/null +++ b/gnu/usr.bin/as/as.h @@ -0,0 +1,292 @@ +/* as.h - global header file + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef asH +#define asH /* Don't declare things twice. */ + +#if !defined(__STDC__) && !defined(const) +#define const /* ignore */ +#endif + +#ifdef USG +#define index strchr +#define bzero(s,n) memset((s),0,(n)) +#define bcopy(from,to,n) memcpy((to),(from),(n)) +#define setbuffer(a,b,c) +#endif + +/* + * CAPITALISED names are #defined. + * "lowercaseH" is #defined if "lowercase.h" has been #include-d. + * "lowercaseT" is a typedef of "lowercase" objects. + * "lowercaseP" is type "pointer to object of type 'lowercase'". + * "lowercaseS" is typedef struct ... lowercaseS. + * + * #define SUSPECT when debugging. + * #define DUMP to include data-structure dumpers. + * #define COMMON as "extern" for all modules except one, where you #define + * COMMON as "". + * If TEST is #defined, then we are testing a module: #define COMMON as "". + */ + + + +/* These #defines are for parameters of entire assembler. */ + +/* #define SUSPECT JF remove for speed testing */ +/* #define DUMP */ +#define NDEBUG /* JF disable asserts */ +/* These #includes are for type definitions etc. */ + +/* #include "style.h" */ +#include +#include +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free xfree + +/* These defines are potentially useful */ +#define FALSE (0) +#define TRUE (!FALSE) +#define ASSERT assert +#define BAD_CASE(value) \ +{ \ + as_fatal ("Case value %d unexpected at line %d of file \"%s\"\n", \ + value, __LINE__, __FILE__); \ +} + + + + +/* These are assembler-wide concepts */ + + +#ifndef COMMON +#ifdef TEST +#define COMMON /* declare our COMMONs storage here. */ +#else +#define COMMON extern /* our commons live elswhere */ +#endif +#endif + /* COMMON now defined */ + +#ifdef SUSPECT +#define register /* no registers: helps debugging */ +#define know(p) ASSERT(p) /* know() is less ugly than #ifdef SUSPECT/ */ + /* assert()/#endif. */ +#else +#define know(p) /* know() checks are no-op.ed */ +#endif /* #ifdef SUSPECT */ + + +char *xmalloc(); /* keep C compilers happy */ +char *xrealloc(); /* " */ +void free(); /* " */ +#define xfree free + +/* input_scrub.c */ + +/* + * Supplies sanitised buffers to read.c. + * Also understands printing line-number part of error messages. + */ + + /* Line number things. */ +int seen_at_least_1_file(); +void bump_line_counters(); +void new_logical_line(); +void as_where(); +void as_perror(); +void as_howmuch(); + /* Sanitising things. */ +void input_scrub_begin(); +void input_scrub_end(); +char *input_scrub_new_file(); +char *input_scrub_next_buffer(); + +/* subsegs.c Sub-segments. Also, segment(=expression type)s.*/ + +/* + * This table describes the use of segments as EXPRESSION types. + * + * X_seg X_add_symbol X_subtract_symbol X_add_number + * SEG_NONE no (legal) expression + * SEG_PASS1 no (defined) " + * SEG_BIG * > 32 bits const. + * SEG_ABSOLUTE 0 + * SEG_DATA * 0 + * SEG_TEXT * 0 + * SEG_BSS * 0 + * SEG_UNKNOWN * 0 + * SEG_DIFFERENCE 0 * 0 + * + * The blank fields MUST be 0, and are nugatory. + * The '0' fields MAY be 0. The '*' fields MAY NOT be 0. + * + * SEG_BIG: X_add_number is < 0 if the result is in + * generic_floating_point_number. The value is -'c' where c is the + * character that introduced the constant. e.g. "0f6.9" will have -'f' + * as a X_add_number value. + * X_add_number > 0 is a count of how many littlenums it took to + * represent a bignum. + * SEG_DIFFERENCE: + * If segments of both symbols are known, they are the same segment. + * X_add_symbol != X_sub_symbol (then we just cancel them, => SEG_ABSOLUTE). + */ + +typedef enum +{ + SEG_ABSOLUTE, + SEG_TEXT, + SEG_DATA, + SEG_BSS, + SEG_UNKNOWN, + SEG_NONE, /* Mythical Segment: NO expression seen. */ + SEG_PASS1, /* Mythical Segment: Need another pass. */ + SEG_GOOF, /* Only happens if AS has a logic error. */ + /* Invented so we don't crash printing */ + /* error message involving weird segment. */ + SEG_BIG, /* Bigger than 32 bits constant. */ + SEG_DIFFERENCE /* Mythical Segment: absolute difference. */ +} segT; +#define SEG_MAXIMUM_ORDINAL (SEG_DIFFERENCE) + +typedef unsigned char subsegT; + +COMMON subsegT now_subseg; + /* What subseg we are accreting now? */ + + +COMMON segT now_seg; + /* Segment our instructions emit to. */ + /* Only OK values are SEG_TEXT or SEG_DATA. */ + + +extern char *const seg_name[]; +extern const int seg_N_TYPE[]; +extern const segT N_TYPE_seg[]; +void subsegs_begin(); +void subseg_change(); +void subseg_new(); + +/* relax() */ + +typedef enum +{ + rs_fill, /* Variable chars to be repeated fr_offset */ + /* times. Fr_symbol unused. */ + /* Used with fr_offset == 0 for a constant */ + /* length frag. */ + + rs_align, /* Align: Fr_offset: power of 2. */ + /* 1 variable char: fill character. */ + rs_org, /* Org: Fr_offset, fr_symbol: address. */ + /* 1 variable char: fill character. */ + + rs_machine_dependent, +#ifndef WORKING_DOT_WORD + rs_broken_word, /* JF: gunpoint */ +#endif +} +relax_stateT; + +/* typedef unsigned char relax_substateT; */ +/* JF this is more likely to leave the end of a struct frag on an align + boundry. Be very careful with this. */ +typedef unsigned long int relax_substateT; + +typedef unsigned long int relax_addressT;/* Enough bits for address. */ + /* Still an integer type. */ + + +/* frags.c */ + +/* + * A code fragment (frag) is some known number of chars, followed by some + * unknown number of chars. Typically the unknown number of chars is an + * instruction address whose size is yet unknown. We always know the greatest + * possible size the unknown number of chars may become, and reserve that + * much room at the end of the frag. + * Once created, frags do not change address during assembly. + * We chain the frags in (a) forward-linked list(s). The object-file address + * of the 1st char of a frag is generally not known until after relax(). + * Many things at assembly time describe an address by {object-file-address + * of a particular frag}+offset. + + BUG: it may be smarter to have a single pointer off to various different +notes for different frag kinds. See how code pans out. + + + */ +struct frag /* a code fragment */ +{ + long unsigned int fr_address; /* Object file address. */ + struct frag *fr_next; /* Chain forward; ascending address order. */ + /* Rooted in frch_root. */ + + long int fr_fix; /* (Fixed) number of chars we know we have. */ + /* May be 0. */ + long int fr_var; /* (Variable) number of chars after above. */ + /* May be 0. */ + struct symbol *fr_symbol; /* For variable-length tail. */ + long int fr_offset; /* For variable-length tail. */ + char *fr_opcode; /*->opcode low addr byte,for relax()ation*/ + relax_stateT fr_type; /* What state is my tail in? */ + relax_substateT fr_subtype; + /* These are needed only on the NS32K machines */ + char fr_pcrel_adjust; + char fr_bsr; + char fr_literal [1]; /* Chars begin here. */ + /* One day we will compile fr_literal[0]. */ +}; +#define SIZEOF_STRUCT_FRAG \ + ((int)zero_address_frag.fr_literal-(int)&zero_address_frag) + /* We want to say fr_literal[0] above. */ + +typedef struct frag fragS; + +COMMON fragS * frag_now; /* -> current frag we are building. */ + /* This frag is incomplete. */ + /* It is, however, included in frchain_now. */ + /* Frag_now->fr_fix is bogus. Use: */ +/* Virtual frag_now->fr_fix==obstack_next_free(&frags)-frag_now->fr_literal.*/ + +COMMON fragS zero_address_frag; /* For foreign-segment symbol fixups. */ +COMMON fragS bss_address_frag; /* For local common (N_BSS segment) fixups. */ + +void frag_new(); +char * frag_more(); +char * frag_var(); +void frag_wane(); +void frag_align(); + + +/* main program "as.c" (command arguments etc) */ + +COMMON char +flagseen[128]; /* ['x'] TRUE if "-x" seen. */ + +COMMON char * +out_file_name; /* name of emitted object file */ + +COMMON int need_pass_2; /* TRUE if we need a second pass. */ + + +#endif /* #ifdef asH */ + +/* end: as.h */ diff --git a/gnu/usr.bin/as/atof-generic.c b/gnu/usr.bin/as/atof-generic.c new file mode 100644 index 0000000000..497541079a --- /dev/null +++ b/gnu/usr.bin/as/atof-generic.c @@ -0,0 +1,526 @@ +/* atof_generic.c - turn a string of digits into a Flonum + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include "flonum.h" +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +#ifdef sparc +#include +#endif +#endif + +#ifdef USG +#define bzero(s,n) memset(s,0,n) +#define index strchr +#endif + +#define FALSE (0) +#define TRUE (1) + +char *index(); + +/***********************************************************************\ +* * +* Given a string of decimal digits , with optional decimal * +* mark and optional decimal exponent (place value) of the * +* lowest_order decimal digit: produce a floating point * +* number. The number is 'generic' floating point: our * +* caller will encode it for a specific machine architecture. * +* * +* Assumptions * +* uses base (radix) 2 * +* this machine uses 2's complement binary integers * +* target flonums use " " " " * +* target flonums exponents fit in a long int * +* * +\***********************************************************************/ + +/* + + Syntax: + + ::= + ::= '+' | '-' | {empty} + ::= + | + | + | + ::= {empty} | + ::= | + ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' + ::= {one character from "string_of_decimal_exponent_marks"} + ::= {one character from "string_of_decimal_marks"} + +*/ + +int /* 0 if OK */ + +atof_generic ( + address_of_string_pointer, /* return pointer to just AFTER number we read. */ + string_of_decimal_marks, /* At most one per number. */ + string_of_decimal_exponent_marks, + address_of_generic_floating_point_number) + + char * * address_of_string_pointer; + const char * string_of_decimal_marks; + const char * string_of_decimal_exponent_marks; + FLONUM_TYPE * address_of_generic_floating_point_number; + +{ + + int return_value; /* 0 means OK. */ + char * first_digit; + /* char * last_digit; JF unused */ + int number_of_digits_before_decimal; + int number_of_digits_after_decimal; + long int decimal_exponent; + int number_of_digits_available; + char digits_sign_char; + + { + /* + * Scan the input string, abstracting (1)digits (2)decimal mark (3) exponent. + * It would be simpler to modify the string, but we don't; just to be nice + * to caller. + * We need to know how many digits we have, so we can allocate space for + * the digits' value. + */ + + char * p; + char c; + int seen_significant_digit; + + first_digit = * address_of_string_pointer; + c= *first_digit; + if (c=='-' || c=='+') + { + digits_sign_char = c; + first_digit ++; + } + else + digits_sign_char = '+'; + + if( (first_digit[0]=='n' || first_digit[0]=='N') + && (first_digit[1]=='a' || first_digit[1]=='A') + && (first_digit[2]=='n' || first_digit[2]=='N')) { + address_of_generic_floating_point_number->sign=0; + address_of_generic_floating_point_number->exponent=0; + address_of_generic_floating_point_number->leader=address_of_generic_floating_point_number->low; + (*address_of_string_pointer)=first_digit+3; + return 0; + } + if( (first_digit[0]=='i' || first_digit[0]=='I') + && (first_digit[1]=='n' || first_digit[1]=='N') + && (first_digit[2]=='f' || first_digit[2]=='F')) { + address_of_generic_floating_point_number->sign= digits_sign_char=='+' ? 'P' : 'N'; + address_of_generic_floating_point_number->exponent=0; + address_of_generic_floating_point_number->leader=address_of_generic_floating_point_number->low; + if( (first_digit[3]=='i' || first_digit[3]=='I') + && (first_digit[4]=='n' || first_digit[4]=='N') + && (first_digit[5]=='i' || first_digit[5]=='I') + && (first_digit[6]=='t' || first_digit[6]=='T') + && (first_digit[7]=='y' || first_digit[7]=='Y')) + (*address_of_string_pointer)=first_digit+8; + else + (*address_of_string_pointer)=first_digit+3; + return 0; + } + + number_of_digits_before_decimal = 0; + number_of_digits_after_decimal = 0; + decimal_exponent = 0; + seen_significant_digit = FALSE; + for (p = first_digit; + (c = * p) + && (!c || ! index (string_of_decimal_marks, c) ) + && (!c || ! index (string_of_decimal_exponent_marks, c) ); + p ++) + { + if (isdigit(c)) + { + if (seen_significant_digit || c > '0') + { + number_of_digits_before_decimal ++; + seen_significant_digit = TRUE; + } + else + { + first_digit++; + } + } + else + { + break; /* p -> char after pre-decimal digits. */ + } + } /* For each digit before decimal mark. */ + if (c && index (string_of_decimal_marks, c)) + { + for (p ++; + (c = * p) + && (!c || ! index (string_of_decimal_exponent_marks, c) ); + p ++) + { + if (isdigit(c)) + { + number_of_digits_after_decimal ++; /* This may be retracted below. */ + if (/* seen_significant_digit || */ c > '0') + { + seen_significant_digit = TRUE; + } + } + else + { + if ( ! seen_significant_digit) + { + number_of_digits_after_decimal = 0; + } + break; + } + } /* For each digit after decimal mark. */ + } + while(number_of_digits_after_decimal && first_digit[number_of_digits_before_decimal+number_of_digits_after_decimal]=='0') + --number_of_digits_after_decimal; +/* last_digit = p; JF unused */ + + if (c && index (string_of_decimal_exponent_marks, c) ) + { + char digits_exponent_sign_char; + + c = * ++ p; + if (c && index ("+-",c)) + { + digits_exponent_sign_char = c; + c = * ++ p; + } + else + { + digits_exponent_sign_char = '+'; + } + for (; + (c); + c = * ++ p) + { + if (isdigit(c)) + { + decimal_exponent = decimal_exponent * 10 + c - '0'; + /* + * BUG! If we overflow here, we lose! + */ + } + else + { + break; + } + } + if (digits_exponent_sign_char == '-') + { + decimal_exponent = - decimal_exponent; + } + } + * address_of_string_pointer = p; + } + + number_of_digits_available = + number_of_digits_before_decimal + + number_of_digits_after_decimal; + return_value = 0; + if (number_of_digits_available == 0) + { + address_of_generic_floating_point_number -> exponent = 0; /* Not strictly necessary */ + address_of_generic_floating_point_number -> leader + = -1 + address_of_generic_floating_point_number -> low; + address_of_generic_floating_point_number -> sign = digits_sign_char; + /* We have just concocted (+/-)0.0E0 */ + } + else + { + LITTLENUM_TYPE * digits_binary_low; + int precision; + int maximum_useful_digits; + int number_of_digits_to_use; + int more_than_enough_bits_for_digits; + int more_than_enough_littlenums_for_digits; + int size_of_digits_in_littlenums; + int size_of_digits_in_chars; + FLONUM_TYPE power_of_10_flonum; + FLONUM_TYPE digits_flonum; + + + precision = (address_of_generic_floating_point_number -> high + - address_of_generic_floating_point_number -> low + + 1 + ); /* Number of destination littlenums. */ + /* Includes guard bits (two littlenums worth) */ + maximum_useful_digits = ( ((double) (precision - 2)) + * ((double) (LITTLENUM_NUMBER_OF_BITS)) + / (LOG_TO_BASE_2_OF_10) + ) + + 2; /* 2 :: guard digits. */ + if (number_of_digits_available > maximum_useful_digits) + { + number_of_digits_to_use = maximum_useful_digits; + } + else + { + number_of_digits_to_use = number_of_digits_available; + } + decimal_exponent += number_of_digits_before_decimal - number_of_digits_to_use; + + more_than_enough_bits_for_digits + = ((((double)number_of_digits_to_use) * LOG_TO_BASE_2_OF_10) + 1); + more_than_enough_littlenums_for_digits + = ( more_than_enough_bits_for_digits + / LITTLENUM_NUMBER_OF_BITS + ) + + 2; + + /* + * Compute (digits) part. In "12.34E56" this is the "1234" part. + * Arithmetic is exact here. If no digits are supplied then + * this part is a 0 valued binary integer. + * Allocate room to build up the binary number as littlenums. + * We want this memory to disappear when we leave this function. + * Assume no alignment problems => (room for n objects) == + * n * (room for 1 object). + */ + + size_of_digits_in_littlenums = more_than_enough_littlenums_for_digits; + size_of_digits_in_chars = size_of_digits_in_littlenums + * sizeof( LITTLENUM_TYPE ); + digits_binary_low = (LITTLENUM_TYPE *) + alloca (size_of_digits_in_chars); + bzero ((char *)digits_binary_low, size_of_digits_in_chars); + + /* Digits_binary_low[] is allocated and zeroed. */ + + { + /* + * Parse the decimal digits as if * digits_low was in the units position. + * Emit a binary number into digits_binary_low[]. + * + * Use a large-precision version of: + * (((1st-digit) * 10 + 2nd-digit) * 10 + 3rd-digit ...) * 10 + last-digit + */ + + char * p; + char c; + int count; /* Number of useful digits left to scan. */ + + for (p = first_digit, count = number_of_digits_to_use; + count; + p ++, -- count) + { + c = * p; + if (isdigit(c)) + { + /* + * Multiply by 10. Assume can never overflow. + * Add this digit to digits_binary_low[]. + */ + + long int carry; + LITTLENUM_TYPE * littlenum_pointer; + LITTLENUM_TYPE * littlenum_limit; + + littlenum_limit + = digits_binary_low + + more_than_enough_littlenums_for_digits + - 1; + carry = c - '0'; /* char -> binary */ + for (littlenum_pointer = digits_binary_low; + littlenum_pointer <= littlenum_limit; + littlenum_pointer ++) + { + long int work; + + work = carry + 10 * (long)(*littlenum_pointer); + * littlenum_pointer = work & LITTLENUM_MASK; + carry = work >> LITTLENUM_NUMBER_OF_BITS; + } + if (carry != 0) + { + /* + * We have a GROSS internal error. + * This should never happen. + */ + abort(); /* RMS prefers abort() to any message. */ + } + } + else + { + ++ count; /* '.' doesn't alter digits used count. */ + } /* if valid digit */ + } /* for each digit */ + } + + /* + * Digits_binary_low[] properly encodes the value of the digits. + * Forget about any high-order littlenums that are 0. + */ + while (digits_binary_low [size_of_digits_in_littlenums - 1] == 0 + && size_of_digits_in_littlenums >= 2) + size_of_digits_in_littlenums --; + + digits_flonum . low = digits_binary_low; + digits_flonum . high = digits_binary_low + size_of_digits_in_littlenums - 1; + digits_flonum . leader = digits_flonum . high; + digits_flonum . exponent = 0; + /* + * The value of digits_flonum . sign should not be important. + * We have already decided the output's sign. + * We trust that the sign won't influence the other parts of the number! + * So we give it a value for these reasons: + * (1) courtesy to humans reading/debugging + * these numbers so they don't get excited about strange values + * (2) in future there may be more meaning attached to sign, + * and what was + * harmless noise may become disruptive, ill-conditioned (or worse) + * input. + */ + digits_flonum . sign = '+'; + + { + /* + * Compute the mantssa (& exponent) of the power of 10. + * If sucessful, then multiply the power of 10 by the digits + * giving return_binary_mantissa and return_binary_exponent. + */ + + LITTLENUM_TYPE *power_binary_low; + int decimal_exponent_is_negative; + /* This refers to the "-56" in "12.34E-56". */ + /* FALSE: decimal_exponent is positive (or 0) */ + /* TRUE: decimal_exponent is negative */ + FLONUM_TYPE temporary_flonum; + LITTLENUM_TYPE *temporary_binary_low; + int size_of_power_in_littlenums; + int size_of_power_in_chars; + + size_of_power_in_littlenums = precision; +/* Precision has a built-in fudge factor so we get a few guard bits. */ + + + decimal_exponent_is_negative = decimal_exponent < 0; + if (decimal_exponent_is_negative) + { + decimal_exponent = - decimal_exponent; + } + /* From now on: the decimal exponent is > 0. Its sign is seperate. */ + + size_of_power_in_chars + = size_of_power_in_littlenums + * sizeof( LITTLENUM_TYPE ) + 2; + power_binary_low = (LITTLENUM_TYPE *) alloca ( size_of_power_in_chars ); + temporary_binary_low = (LITTLENUM_TYPE *) alloca ( size_of_power_in_chars ); + bzero ((char *)power_binary_low, size_of_power_in_chars); + * power_binary_low = 1; + power_of_10_flonum . exponent = 0; + power_of_10_flonum . low = power_binary_low; + power_of_10_flonum . leader = power_binary_low; + power_of_10_flonum . high = power_binary_low + size_of_power_in_littlenums - 1; + power_of_10_flonum . sign = '+'; + temporary_flonum . low = temporary_binary_low; + temporary_flonum . high = temporary_binary_low + size_of_power_in_littlenums - 1; + /* + * (power) == 1. + * Space for temporary_flonum allocated. + */ + + /* + * ... + * + * WHILE more bits + * DO find next bit (with place value) + * multiply into power mantissa + * OD + */ + { + int place_number_limit; + /* Any 10^(2^n) whose "n" exceeds this */ + /* value will fall off the end of */ + /* flonum_XXXX_powers_of_ten[]. */ + int place_number; + const FLONUM_TYPE * multiplicand; /* -> 10^(2^n) */ + + place_number_limit = table_size_of_flonum_powers_of_ten; + multiplicand + = ( decimal_exponent_is_negative + ? flonum_negative_powers_of_ten + : flonum_positive_powers_of_ten); + for (place_number = 1; /* Place value of this bit of exponent. */ + decimal_exponent; /* Quit when no more 1 bits in exponent. */ + decimal_exponent >>= 1 + , place_number ++) + { + if (decimal_exponent & 1) + { + if (place_number > place_number_limit) + { + /* + * The decimal exponent has a magnitude so great that + * our tables can't help us fragment it. Although this + * routine is in error because it can't imagine a + * number that big, signal an error as if it is the + * user's fault for presenting such a big number. + */ + return_value = ERROR_EXPONENT_OVERFLOW; + /* + * quit out of loop gracefully + */ + decimal_exponent = 0; + } + else + { +#ifdef TRACE +printf("before multiply, place_number = %d., power_of_10_flonum:\n", place_number); +flonum_print( & power_of_10_flonum ); +(void)putchar('\n'); +#endif + flonum_multip (multiplicand + place_number, & power_of_10_flonum, & temporary_flonum); + flonum_copy (& temporary_flonum, & power_of_10_flonum); + } /* If this bit of decimal_exponent was computable.*/ + } /* If this bit of decimal_exponent was set. */ + } /* For each bit of binary representation of exponent */ +#ifdef TRACE +printf( " after computing power_of_10_flonum: " ); +flonum_print( & power_of_10_flonum ); +(void)putchar('\n'); +#endif + } + + } + + /* + * power_of_10_flonum is power of ten in binary (mantissa) , (exponent). + * It may be the number 1, in which case we don't NEED to multiply. + * + * Multiply (decimal digits) by power_of_10_flonum. + */ + + flonum_multip (& power_of_10_flonum, & digits_flonum, address_of_generic_floating_point_number); + /* Assert sign of the number we made is '+'. */ + address_of_generic_floating_point_number -> sign = digits_sign_char; + + } /* If we had any significant digits. */ + return (return_value); +} /* atof_generic () */ + +/* end: atof_generic.c */ diff --git a/gnu/usr.bin/as/bignum-copy.c b/gnu/usr.bin/as/bignum-copy.c new file mode 100644 index 0000000000..26401213b9 --- /dev/null +++ b/gnu/usr.bin/as/bignum-copy.c @@ -0,0 +1,75 @@ +/* bignum_copy.c - copy a bignum + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "bignum.h" + +#ifdef USG +#define bzero(s,n) memset(s,0,n) +#define bcopy(from,to,n) memcpy(to,from,n) +#endif + +/* + * bignum_copy () + * + * Copy a bignum from in to out. + * If the output is shorter than the input, copy lower-order littlenums. + * Return 0 or the number of significant littlenums dropped. + * Assumes littlenum arrays are densely packed: no unused chars between + * the littlenums. Uses bcopy() to move littlenums, and wants to + * know length (in chars) of the input bignum. + */ + +/* void */ +int +bignum_copy (in, in_length, out, out_length) + register LITTLENUM_TYPE * in; + register int in_length; /* in sizeof(littlenum)s */ + register LITTLENUM_TYPE * out; + register int out_length; /* in sizeof(littlenum)s */ +{ + register int significant_littlenums_dropped; + + if (out_length < in_length) + { + register LITTLENUM_TYPE * p; /* -> most significant (non-zero) input littlenum. */ + + bcopy ((char *)in, (char *)out, out_length << LITTLENUM_SHIFT); + for (p = in + in_length - 1; p >= in; -- p) + { + if (* p) break; + } + significant_littlenums_dropped = p - in - in_length + 1; + if (significant_littlenums_dropped < 0) + { + significant_littlenums_dropped = 0; + } + } + else + { + bcopy ((char *)in, (char *)out, in_length << LITTLENUM_SHIFT); + if (out_length > in_length) + { + bzero ((char *)(out + out_length), (out_length - in_length) << LITTLENUM_SHIFT); + } + significant_littlenums_dropped = 0; + } + return (significant_littlenums_dropped); +} + +/* end: bignum_copy.c */ diff --git a/gnu/usr.bin/as/bignum.h b/gnu/usr.bin/as/bignum.h new file mode 100644 index 0000000000..dbc95a3b64 --- /dev/null +++ b/gnu/usr.bin/as/bignum.h @@ -0,0 +1,48 @@ +/* bignum.h-arbitrary precision integers + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/***********************************************************************\ +* * +* Arbitrary-precision integer arithmetic. * +* For speed, we work in groups of bits, even though this * +* complicates algorithms. * +* Each group of bits is called a 'littlenum'. * +* A bunch of littlenums representing a (possibly large) * +* integer is called a 'bignum'. * +* Bignums are >= 0. * +* * +\***********************************************************************/ + +#define LITTLENUM_NUMBER_OF_BITS (16) +#define LITTLENUM_RADIX (1 << LITTLENUM_NUMBER_OF_BITS) +#define LITTLENUM_MASK (0xFFFF) +#define LITTLENUM_SHIFT (1) +#define CHARS_PER_LITTLENUM (1 << LITTLENUM_SHIFT) +#ifndef BITS_PER_CHAR +#define BITS_PER_CHAR (8) +#endif + +typedef unsigned short int LITTLENUM_TYPE; + + +/* JF truncated this to get around a problem with GCC */ +#define LOG_TO_BASE_2_OF_10 (3.3219280948873623478703194294893901758651 ) + /* WARNING: I haven't checked that the trailing digits are correct! */ + +/* end: bignum.h */ diff --git a/gnu/usr.bin/as/config/Makefile.i386 b/gnu/usr.bin/as/config/Makefile.i386 new file mode 100644 index 0000000000..945246b3d8 --- /dev/null +++ b/gnu/usr.bin/as/config/Makefile.i386 @@ -0,0 +1,4 @@ +# @(#)Makefile.i386 6.1 (Berkeley) 3/3/91 + +CFLAGS+= -DNON_BROKEN_WORDS +SRCS+= i386.c atof-ieee.c diff --git a/gnu/usr.bin/as/config/a.out.gnu.h b/gnu/usr.bin/as/config/a.out.gnu.h new file mode 100644 index 0000000000..71b09a058f --- /dev/null +++ b/gnu/usr.bin/as/config/a.out.gnu.h @@ -0,0 +1,261 @@ +#ifndef __A_OUT_GNU_H__ +#define __A_OUT_GNU_H__ + +#define __GNU_EXEC_MACROS__ + +#ifndef __STRUCT_EXEC_OVERRIDE__ + +struct exec +{ + unsigned long a_info; /* Use macros N_MAGIC, etc for access */ + unsigned a_text; /* length of text, in bytes */ + unsigned a_data; /* length of data, in bytes */ + unsigned a_bss; /* length of uninitialized data area for file, in bytes */ + unsigned a_syms; /* length of symbol table data in file, in bytes */ + unsigned a_entry; /* start address */ + unsigned a_trsize; /* length of relocation info for text, in bytes */ + unsigned a_drsize; /* length of relocation info for data, in bytes */ +}; + +#endif /* __STRUCT_EXEC_OVERRIDE__ */ + +/* these go in the N_MACHTYPE field */ +enum machine_type { +#if defined (M_OLDSUN2) + M__OLDSUN2 = M_OLDSUN2, +#else + M_OLDSUN2 = 0, +#endif +#if defined (M_68010) + M__68010 = M_68010, +#else + M_68010 = 1, +#endif +#if defined (M_68020) + M__68020 = M_68020, +#else + M_68020 = 2, +#endif +#if defined (M_SPARC) + M__SPARC = M_SPARC, +#else + M_SPARC = 3, +#endif + /* skip a bunch so we don't run into any of sun's numbers */ + M_386 = 100, +}; + +#if !defined (N_MAGIC) +#define N_MAGIC(exec) ((exec).a_info & 0xffff) +#endif +#define N_MACHTYPE(exec) ((enum machine_type)(((exec).a_info >> 16) & 0xff)) +#define N_FLAGS(exec) (((exec).a_info >> 24) & 0xff) +#define N_SET_INFO(exec, magic, type, flags) \ + ((exec).a_info = ((magic) & 0xffff) \ + | (((int)(type) & 0xff) << 16) \ + | (((flags) & 0xff) << 24)) +#define N_SET_MAGIC(exec, magic) \ + ((exec).a_info = (((exec).a_info & 0xffff0000) | ((magic) & 0xffff))) + +#define N_SET_MACHTYPE(exec, machtype) \ + ((exec).a_info = \ + ((exec).a_info&0xff00ffff) | ((((int)(machtype))&0xff) << 16)) + +#define N_SET_FLAGS(exec, flags) \ + ((exec).a_info = \ + ((exec).a_info&0x00ffffff) | (((flags) & 0xff) << 24)) + +/* Code indicating object file or impure executable. */ +#define OMAGIC 0407 +/* Code indicating pure executable. */ +#define NMAGIC 0410 +/* Code indicating demand-paged executable. */ +#define ZMAGIC 0413 + +#if !defined (N_BADMAG) +#define N_BADMAG(x) \ + (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC \ + && N_MAGIC(x) != ZMAGIC) +#endif + +#define _N_BADMAG(x) \ + (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC \ + && N_MAGIC(x) != ZMAGIC) + +#define _N_HDROFF(x) (1024 - sizeof (struct exec)) + +#if !defined (N_TXTOFF) +#define N_TXTOFF(x) \ + (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : sizeof (struct exec)) +#endif + +#if !defined (N_DATOFF) +#define N_DATOFF(x) (N_TXTOFF(x) + (x).a_text) +#endif + +#if !defined (N_TRELOFF) +#define N_TRELOFF(x) (N_DATOFF(x) + (x).a_data) +#endif + +#if !defined (N_DRELOFF) +#define N_DRELOFF(x) (N_TRELOFF(x) + (x).a_trsize) +#endif + +#if !defined (N_SYMOFF) +#define N_SYMOFF(x) (N_DRELOFF(x) + (x).a_drsize) +#endif + +#if !defined (N_STROFF) +#define N_STROFF(x) (N_SYMOFF(x) + (x).a_syms) +#endif + +/* Address of text segment in memory after it is loaded. */ +#if !defined (N_TXTADDR) +#define N_TXTADDR(x) 0 +#endif + +/* Address of data segment in memory after it is loaded. + Note that it is up to you to define SEGMENT_SIZE + on machines not listed here. */ +#if defined(vax) || defined(hp300) || defined(pyr) +#define SEGMENT_SIZE page_size +#endif +#ifdef sony +#define SEGMENT_SIZE 0x2000 +#endif /* Sony. */ +#ifdef is68k +#define SEGMENT_SIZE 0x20000 +#endif +#if defined(m68k) && defined(PORTAR) +#define PAGE_SIZE 0x400 +#define SEGMENT_SIZE PAGE_SIZE +#endif + +#define _N_SEGMENT_ROUND(x) (((x) + SEGMENT_SIZE - 1) & ~(SEGMENT_SIZE - 1)) + +#define _N_TXTENDADDR(x) (N_TXTADDR(x)+(x).a_text) + +#ifndef N_DATADDR +#define N_DATADDR(x) \ + (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x)) \ + : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x)))) +#endif + +/* Address of bss segment in memory after it is loaded. */ +#if !defined (N_BSSADDR) +#define N_BSSADDR(x) (N_DATADDR(x) + (x).a_data) +#endif + +#if !defined (N_NLIST_DECLARED) +struct nlist { + union { + char *n_name; + struct nlist *n_next; + long n_strx; + } n_un; + unsigned char n_type; + char n_other; + short n_desc; + unsigned long n_value; +}; +#endif /* no N_NLIST_DECLARED. */ + +#if !defined (N_UNDF) +#define N_UNDF 0 +#endif +#if !defined (N_ABS) +#define N_ABS 2 +#endif +#if !defined (N_TEXT) +#define N_TEXT 4 +#endif +#if !defined (N_DATA) +#define N_DATA 6 +#endif +#if !defined (N_BSS) +#define N_BSS 8 +#endif +#if !defined (N_FN) +#define N_FN 15 +#endif + +#if !defined (N_EXT) +#define N_EXT 1 +#endif +#if !defined (N_TYPE) +#define N_TYPE 036 +#endif +#if !defined (N_STAB) +#define N_STAB 0340 +#endif + +/* The following type indicates the definition of a symbol as being + an indirect reference to another symbol. The other symbol + appears as an undefined reference, immediately following this symbol. + + Indirection is asymmetrical. The other symbol's value will be used + to satisfy requests for the indirect symbol, but not vice versa. + If the other symbol does not have a definition, libraries will + be searched to find a definition. */ +#define N_INDR 0xa + +/* The following symbols refer to set elements. + All the N_SET[ATDB] symbols with the same name form one set. + Space is allocated for the set in the text section, and each set + element's value is stored into one word of the space. + The first word of the space is the length of the set (number of elements). + + The address of the set is made into an N_SETV symbol + whose name is the same as the name of the set. + This symbol acts like a N_DATA global symbol + in that it can satisfy undefined external references. */ + +/* These appear as input to LD, in a .o file. */ +#define N_SETA 0x14 /* Absolute set element symbol */ +#define N_SETT 0x16 /* Text set element symbol */ +#define N_SETD 0x18 /* Data set element symbol */ +#define N_SETB 0x1A /* Bss set element symbol */ + +/* This is output from LD. */ +#define N_SETV 0x1C /* Pointer to set vector in data area. */ + +#if !defined (N_RELOCATION_INFO_DECLARED) +/* This structure describes a single relocation to be performed. + The text-relocation section of the file is a vector of these structures, + all of which apply to the text section. + Likewise, the data-relocation section applies to the data section. */ + +struct relocation_info +{ + /* Address (within segment) to be relocated. */ + int r_address; + /* The meaning of r_symbolnum depends on r_extern. */ + unsigned int r_symbolnum:24; + /* Nonzero means value is a pc-relative offset + and it should be relocated for changes in its own address + as well as for changes in the symbol or section specified. */ + unsigned int r_pcrel:1; + /* Length (as exponent of 2) of the field to be relocated. + Thus, a value of 2 indicates 1<<2 bytes. */ + unsigned int r_length:2; + /* 1 => relocate with value of symbol. + r_symbolnum is the index of the symbol + in file's the symbol table. + 0 => relocate with the address of a segment. + r_symbolnum is N_TEXT, N_DATA, N_BSS or N_ABS + (the N_EXT bit may be set also, but signifies nothing). */ + unsigned int r_extern:1; + /* Four bits that aren't used, but when writing an object file + it is desirable to clear them. */ +#ifdef NS32K + unsigned r_bsr:1; + unsigned r_disp:1; + unsigned r_pad:2; +#else + unsigned int r_pad:4; +#endif +}; +#endif /* no N_RELOCATION_INFO_DECLARED. */ + + +#endif /* __A_OUT_GNU_H__ */ diff --git a/gnu/usr.bin/as/config/atof-ieee.c b/gnu/usr.bin/as/config/atof-ieee.c new file mode 100644 index 0000000000..6ff45c8404 --- /dev/null +++ b/gnu/usr.bin/as/config/atof-ieee.c @@ -0,0 +1,505 @@ +/* atof_ieee.c - turn a Flonum into an IEEE floating point number + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "flonum.h" +#ifdef USG +#define bzero(s,n) memset(s,0,n) +#define bcopy(from,to,n) memcpy((to),(from),(n)) +#endif + +extern FLONUM_TYPE generic_floating_point_number; /* Flonums returned here. */ +#define NULL (0) + +extern char EXP_CHARS[]; + /* Precision in LittleNums. */ +#define MAX_PRECISION (6) +#define F_PRECISION (2) +#define D_PRECISION (4) +#define X_PRECISION (6) +#define P_PRECISION (6) + + /* Length in LittleNums of guard bits. */ +#define GUARD (2) + +static unsigned long int mask [] = { + 0x00000000, + 0x00000001, + 0x00000003, + 0x00000007, + 0x0000000f, + 0x0000001f, + 0x0000003f, + 0x0000007f, + 0x000000ff, + 0x000001ff, + 0x000003ff, + 0x000007ff, + 0x00000fff, + 0x00001fff, + 0x00003fff, + 0x00007fff, + 0x0000ffff, + 0x0001ffff, + 0x0003ffff, + 0x0007ffff, + 0x000fffff, + 0x001fffff, + 0x003fffff, + 0x007fffff, + 0x00ffffff, + 0x01ffffff, + 0x03ffffff, + 0x07ffffff, + 0x0fffffff, + 0x1fffffff, + 0x3fffffff, + 0x7fffffff, + 0xffffffff + }; + +static int bits_left_in_littlenum; +static int littlenums_left; +static LITTLENUM_TYPE * littlenum_pointer; + +static int +next_bits (number_of_bits) + int number_of_bits; +{ + int return_value; + + if(!littlenums_left) + return 0; + if (number_of_bits >= bits_left_in_littlenum) + { + return_value = mask [bits_left_in_littlenum] & *littlenum_pointer; + number_of_bits -= bits_left_in_littlenum; + return_value <<= number_of_bits; + if(--littlenums_left) { + bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS - number_of_bits; + littlenum_pointer --; + return_value |= (*littlenum_pointer>>bits_left_in_littlenum) & mask[number_of_bits]; + } + } + else + { + bits_left_in_littlenum -= number_of_bits; + return_value = mask [number_of_bits] & (*littlenum_pointer>>bits_left_in_littlenum); + } + return (return_value); +} + +/* Num had better be less than LITTLENUM_NUMBER_OF_BITS */ +static int +unget_bits(num) +{ + if(!littlenums_left) { + ++littlenum_pointer; + ++littlenums_left; + bits_left_in_littlenum=num; + } else if(bits_left_in_littlenum+num>LITTLENUM_NUMBER_OF_BITS) { + bits_left_in_littlenum= num-(LITTLENUM_NUMBER_OF_BITS-bits_left_in_littlenum); + ++littlenum_pointer; + ++littlenums_left; + } else + bits_left_in_littlenum+=num; +} + +static void +make_invalid_floating_point_number (words) + LITTLENUM_TYPE * words; +{ + as_warn("cannot create floating-point number"); + words[0]= ((unsigned)-1)>>1; /* Zero the leftmost bit */ + words[1]= -1; + words[2]= -1; + words[3]= -1; + words[4]= -1; + words[5]= -1; +} + +/***********************************************************************\ +* Warning: this returns 16-bit LITTLENUMs. It is up to the caller * +* to figure out any alignment problems and to conspire for the * +* bytes/word to be emitted in the right order. Bigendians beware! * +* * +\***********************************************************************/ + +/* Note that atof-ieee always has X and P precisions enabled. it is up + to md_atof to filter them out if the target machine does not support + them. */ + +char * /* Return pointer past text consumed. */ +atof_ieee (str, what_kind, words) + char * str; /* Text to convert to binary. */ + char what_kind; /* 'd', 'f', 'g', 'h' */ + LITTLENUM_TYPE * words; /* Build the binary here. */ +{ + static LITTLENUM_TYPE bits [MAX_PRECISION + MAX_PRECISION + GUARD]; + /* Extra bits for zeroed low-order bits. */ + /* The 1st MAX_PRECISION are zeroed, */ + /* the last contain flonum bits. */ + char * return_value; + int precision; /* Number of 16-bit words in the format. */ + long int exponent_bits; + + return_value = str; + generic_floating_point_number.low = bits + MAX_PRECISION; + generic_floating_point_number.high = NULL; + generic_floating_point_number.leader = NULL; + generic_floating_point_number.exponent = NULL; + generic_floating_point_number.sign = '\0'; + + /* Use more LittleNums than seems */ + /* necessary: the highest flonum may have */ + /* 15 leading 0 bits, so could be useless. */ + + bzero (bits, sizeof(LITTLENUM_TYPE) * MAX_PRECISION); + + switch(what_kind) { + case 'f': + case 'F': + case 's': + case 'S': + precision = F_PRECISION; + exponent_bits = 8; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + precision = D_PRECISION; + exponent_bits = 11; + break; + + case 'x': + case 'X': + case 'e': + case 'E': + precision = X_PRECISION; + exponent_bits = 15; + break; + + case 'p': + case 'P': + + precision = P_PRECISION; + exponent_bits= -1; + break; + + default: + make_invalid_floating_point_number (words); + return NULL; + } + + generic_floating_point_number.high = generic_floating_point_number.low + precision - 1 + GUARD; + + if (atof_generic (& return_value, ".", EXP_CHARS, & generic_floating_point_number)) { + /* as_warn("Error converting floating point number (Exponent overflow?)"); */ + make_invalid_floating_point_number (words); + return NULL; + } + gen_to_words(words, precision, exponent_bits); + return return_value; +} + +/* Turn generic_floating_point_number into a real float/double/extended */ +gen_to_words(words,precision,exponent_bits) +LITTLENUM_TYPE *words; +long int exponent_bits; +int precision; +{ + int return_value=0; + + long int exponent_1; + long int exponent_2; + long int exponent_3; + long int exponent_4; + int exponent_skippage; + LITTLENUM_TYPE word1; + LITTLENUM_TYPE * lp; + + if (generic_floating_point_number.low > generic_floating_point_number.leader) { + /* 0.0e0 seen. */ + if(generic_floating_point_number.sign=='+') + words[0]=0x0000; + else + words[0]=0x8000; + bzero (&words[1], sizeof(LITTLENUM_TYPE) * (precision-1)); + return return_value; + } + + /* NaN: Do the right thing */ + if(generic_floating_point_number.sign==0) { + if(precision==F_PRECISION) { + words[0]=0x7fff; + words[1]=0xffff; + } else { + words[0]=0x7fff; + words[1]=0xffff; + words[2]=0xffff; + words[3]=0xffff; + } + return return_value; + } else if(generic_floating_point_number.sign=='P') { + /* +INF: Do the right thing */ + if(precision==F_PRECISION) { + words[0]=0x7f80; + words[1]=0; + } else { + words[0]=0x7ff0; + words[1]=0; + words[2]=0; + words[3]=0; + } + return return_value; + } else if(generic_floating_point_number.sign=='N') { + /* Negative INF */ + if(precision==F_PRECISION) { + words[0]=0xff80; + words[1]=0x0; + } else { + words[0]=0xfff0; + words[1]=0x0; + words[2]=0x0; + words[3]=0x0; + } + return return_value; + } + /* + * The floating point formats we support have: + * Bit 15 is sign bit. + * Bits 14:n are excess-whatever exponent. + * Bits n-1:0 (if any) are most significant bits of fraction. + * Bits 15:0 of the next word(s) are the next most significant bits. + * + * So we need: number of bits of exponent, number of bits of + * mantissa. + */ + bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS; + littlenum_pointer = generic_floating_point_number.leader; + littlenums_left = 1+generic_floating_point_number.leader - generic_floating_point_number.low; + /* Seek (and forget) 1st significant bit */ + for (exponent_skippage = 0;! next_bits(1); exponent_skippage ++) + ; + exponent_1 = generic_floating_point_number.exponent + generic_floating_point_number.leader + 1 - + generic_floating_point_number.low; + /* Radix LITTLENUM_RADIX, point just higher than generic_floating_point_number.leader. */ + exponent_2 = exponent_1 * LITTLENUM_NUMBER_OF_BITS; + /* Radix 2. */ + exponent_3 = exponent_2 - exponent_skippage; + /* Forget leading zeros, forget 1st bit. */ + exponent_4 = exponent_3 + ((1 << (exponent_bits - 1)) - 2); + /* Offset exponent. */ + + lp = words; + + /* Word 1. Sign, exponent and perhaps high bits. */ + word1 = (generic_floating_point_number.sign == '+') ? 0 : (1<<(LITTLENUM_NUMBER_OF_BITS-1)); + + /* Assume 2's complement integers. */ + if(exponent_4<1 && exponent_4>=-62) { + int prec_bits; + int num_bits; + + unget_bits(1); + num_bits= -exponent_4; + prec_bits=LITTLENUM_NUMBER_OF_BITS*precision-(exponent_bits+1+num_bits); + if(precision==X_PRECISION && exponent_bits==15) + prec_bits-=LITTLENUM_NUMBER_OF_BITS+1; + + if(num_bits>=LITTLENUM_NUMBER_OF_BITS-exponent_bits) { + /* Bigger than one littlenum */ + num_bits-=(LITTLENUM_NUMBER_OF_BITS-1)-exponent_bits; + *lp++=word1; + if(num_bits+exponent_bits+1>=precision*LITTLENUM_NUMBER_OF_BITS) { + /* Exponent overflow */ + make_invalid_floating_point_number(words); + return return_value; + } + if(precision==X_PRECISION && exponent_bits==15) { + *lp++=0; + *lp++=0; + num_bits-=LITTLENUM_NUMBER_OF_BITS-1; + } + while(num_bits>=LITTLENUM_NUMBER_OF_BITS) { + num_bits-=LITTLENUM_NUMBER_OF_BITS; + *lp++=0; + } + if(num_bits) + *lp++=next_bits(LITTLENUM_NUMBER_OF_BITS-(num_bits)); + } else { + if(precision==X_PRECISION && exponent_bits==15) { + *lp++=word1; + *lp++=0; + if(num_bits==LITTLENUM_NUMBER_OF_BITS) { + *lp++=0; + *lp++=next_bits(LITTLENUM_NUMBER_OF_BITS-1); + } else if(num_bits==LITTLENUM_NUMBER_OF_BITS-1) + *lp++=0; + else + *lp++=next_bits(LITTLENUM_NUMBER_OF_BITS-1-num_bits); + num_bits=0; + } else { + word1|= next_bits ((LITTLENUM_NUMBER_OF_BITS-1) - (exponent_bits+num_bits)); + *lp++=word1; + } + } + while(lpLITTLENUM_NUMBER_OF_BITS) { + int n = 0; + int tmp_bits; + + n=0; + tmp_bits=prec_bits; + while(tmp_bits>LITTLENUM_NUMBER_OF_BITS) { + if(lp[n]!=(LITTLENUM_TYPE)-1) + break; + --n; + tmp_bits-=LITTLENUM_NUMBER_OF_BITS; + } + if(tmp_bits>LITTLENUM_NUMBER_OF_BITS || (lp[n]&mask[tmp_bits])!=mask[tmp_bits]) { + unsigned long int carry; + + for (carry = 1; carry && (lp >= words); lp --) { + carry = * lp + carry; + * lp = carry; + carry >>= LITTLENUM_NUMBER_OF_BITS; + } + } + } else if((*lp&mask[prec_bits])!=mask[prec_bits]) + lp++; + } + + return return_value; + } else if (exponent_4 & ~ mask [exponent_bits]) { + /* + * Exponent overflow. Lose immediately. + */ + + /* + * We leave return_value alone: admit we read the + * number, but return a floating exception + * because we can't encode the number. + */ + make_invalid_floating_point_number (words); + return return_value; + } else { + word1 |= (exponent_4 << ((LITTLENUM_NUMBER_OF_BITS-1) - exponent_bits)) + | next_bits ((LITTLENUM_NUMBER_OF_BITS-1) - exponent_bits); + } + + * lp ++ = word1; + + /* X_PRECISION is special: it has 16 bits of zero in the middle, + followed by a 1 bit. */ + if(exponent_bits==15 && precision==X_PRECISION) { + *lp++=0; + *lp++= 1<<(LITTLENUM_NUMBER_OF_BITS)|next_bits(LITTLENUM_NUMBER_OF_BITS-1); + } + + /* The rest of the words are just mantissa bits. */ + while(lp < words + precision) + *lp++ = next_bits (LITTLENUM_NUMBER_OF_BITS); + + if (next_bits (1)) { + unsigned long int carry; + /* + * Since the NEXT bit is a 1, round UP the mantissa. + * The cunning design of these hidden-1 floats permits + * us to let the mantissa overflow into the exponent, and + * it 'does the right thing'. However, we lose if the + * highest-order bit of the lowest-order word flips. + * Is that clear? + */ + + +/* #if (sizeof(carry)) < ((sizeof(bits[0]) * BITS_PER_CHAR) + 2) + Please allow at least 1 more bit in carry than is in a LITTLENUM. + We need that extra bit to hold a carry during a LITTLENUM carry + propagation. Another extra bit (kept 0) will assure us that we + don't get a sticky sign bit after shifting right, and that + permits us to propagate the carry without any masking of bits. +#endif */ + for (carry = 1, lp --; carry && (lp >= words); lp --) { + carry = * lp + carry; + * lp = carry; + carry >>= LITTLENUM_NUMBER_OF_BITS; + } + if ( (word1 ^ *words) & (1 << (LITTLENUM_NUMBER_OF_BITS - 1)) ) { + /* We leave return_value alone: admit we read the + * number, but return a floating exception + * because we can't encode the number. + */ + *words&= ~ (1 << (LITTLENUM_NUMBER_OF_BITS - 1)); + /* make_invalid_floating_point_number (words); */ + /* return return_value; */ + } + } + return (return_value); +} + +/* This routine is a real kludge. Someone really should do it better, but + I'm too lazy, and I don't understand this stuff all too well anyway + (JF) + */ +void +int_to_gen(x) +long x; +{ + char buf[20]; + char *bufp; + + sprintf(buf,"%ld",x); + bufp= &buf[0]; + if(atof_generic(&bufp,".", EXP_CHARS, &generic_floating_point_number)) + as_warn("Error converting number to floating point (Exponent overflow?)"); +} + +#ifdef TEST +char * +print_gen(gen) +FLONUM_TYPE *gen; +{ + FLONUM_TYPE f; + LITTLENUM_TYPE arr[10]; + double dv; + float fv; + static char sbuf[40]; + + if(gen) { + f=generic_floating_point_number; + generic_floating_point_number= *gen; + } + gen_to_words(&arr[0],4,11); + bcopy(&arr[0],&dv,sizeof(double)); + sprintf(sbuf,"%x %x %x %x %.14G ",arr[0],arr[1],arr[2],arr[3],dv); + gen_to_words(&arr[0],2,8); + bcopy(&arr[0],&fv,sizeof(float)); + sprintf(sbuf+strlen(sbuf),"%x %x %.12g\n",arr[0],arr[1],fv); + if(gen) + generic_floating_point_number=f; + return sbuf; +} +#endif diff --git a/gnu/usr.bin/as/config/i386-opcode.h b/gnu/usr.bin/as/config/i386-opcode.h new file mode 100644 index 0000000000..cace0c356d --- /dev/null +++ b/gnu/usr.bin/as/config/i386-opcode.h @@ -0,0 +1,806 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * + * @(#)i386-opcode.h 6.3 (Berkeley) 5/8/91 + */ + +/* i386-opcode.h -- Intel 80386 opcode table + Copyright (C) 1989, Free Software Foundation. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +template i386_optab[] = { + +#define _ None +/* move instructions */ +{ "mov", 2, 0xa0, _, DW|NoModrm, Disp32, Acc, 0 }, +{ "mov", 2, 0x88, _, DW|Modrm, Reg, Reg|Mem, 0 }, +{ "mov", 2, 0xb0, _, ShortFormW, Imm, Reg, 0 }, +{ "mov", 2, 0xc6, _, W|Modrm, Imm, Reg|Mem, 0 }, +{ "mov", 2, 0x8c, _, D|Modrm, SReg3|SReg2, Reg16|Mem16, 0 }, +/* move to/from control debug registers */ +{ "mov", 2, 0x0f20, _, D|Modrm, Control, Reg32, 0}, +{ "mov", 2, 0x0f21, _, D|Modrm, Debug, Reg32, 0}, +{ "mov", 2, 0x0f24, _, D|Modrm, Test, Reg32, 0}, + +/* move with sign extend */ +/* "movsbl" & "movsbw" must not be unified into "movsb" to avoid + conflict with the "movs" string move instruction. Thus, + {"movsb", 2, 0x0fbe, _, ReverseRegRegmem|Modrm, Reg8|Mem, Reg16|Reg32, 0}, + is not kosher; we must seperate the two instructions. */ +{"movsbl", 2, 0x0fbe, _, ReverseRegRegmem|Modrm, Reg8|Mem, Reg32, 0}, +{"movsbw", 2, 0x660fbe, _, ReverseRegRegmem|Modrm, Reg8|Mem, Reg16, 0}, +{"movswl", 2, 0x0fbf, _, ReverseRegRegmem|Modrm, Reg16|Mem, Reg32, 0}, + +/* move with zero extend */ +{"movzb", 2, 0x0fb6, _, ReverseRegRegmem|Modrm, Reg8|Mem, Reg16|Reg32, 0}, +{"movzwl", 2, 0x0fb7, _, ReverseRegRegmem|Modrm, Reg16|Mem, Reg32, 0}, + +/* push instructions */ +{"push", 1, 0x50, _, ShortForm, WordReg,0,0 }, +{"push", 1, 0xff, 0x6, Modrm, WordReg|WordMem, 0, 0 }, +{"push", 1, 0x6a, _, NoModrm, Imm8S, 0, 0}, +{"push", 1, 0x68, _, NoModrm, Imm16|Imm32, 0, 0}, +{"push", 1, 0x06, _, Seg2ShortForm, SReg2,0,0 }, +{"push", 1, 0x0fa0, _, Seg3ShortForm, SReg3,0,0 }, +/* push all */ +{"pusha", 0, 0x60, _, NoModrm, 0, 0, 0 }, + +/* pop instructions */ +{"pop", 1, 0x58, _, ShortForm, WordReg,0,0 }, +{"pop", 1, 0x8f, 0x0, Modrm, WordReg|WordMem, 0, 0 }, +#define POP_SEG_SHORT 0x7 +{"pop", 1, 0x07, _, Seg2ShortForm, SReg2,0,0 }, +{"pop", 1, 0x0fa1, _, Seg3ShortForm, SReg3,0,0 }, +/* pop all */ +{"popa", 0, 0x61, _, NoModrm, 0, 0, 0 }, + +/* xchg exchange instructions + xchg commutes: we allow both operand orders */ +{"xchg", 2, 0x90, _, ShortForm, WordReg, Acc, 0 }, +{"xchg", 2, 0x90, _, ShortForm, Acc, WordReg, 0 }, +{"xchg", 2, 0x86, _, W|Modrm, Reg, Reg|Mem, 0 }, +{"xchg", 2, 0x86, _, W|Modrm, Reg|Mem, Reg, 0 }, + +/* in/out from ports */ +{"in", 2, 0xe4, _, W|NoModrm, Imm8, Acc, 0 }, +{"in", 2, 0xec, _, W|NoModrm, InOutPortReg, Acc, 0 }, +{"out", 2, 0xe6, _, W|NoModrm, Acc, Imm8, 0 }, +{"out", 2, 0xee, _, W|NoModrm, Acc, InOutPortReg, 0 }, + +/* load effective address */ +{"lea", 2, 0x8d, _, Modrm, WordMem, WordReg, 0 }, + +/* load segment registers from memory */ +{"lds", 2, 0xc5, _, Modrm, Mem, Reg32, 0}, +{"les", 2, 0xc4, _, Modrm, Mem, Reg32, 0}, +{"lfs", 2, 0x0fb4, _, Modrm, Mem, Reg32, 0}, +{"lgs", 2, 0x0fb5, _, Modrm, Mem, Reg32, 0}, +{"lss", 2, 0x0fb2, _, Modrm, Mem, Reg32, 0}, + +/* flags register instructions */ +{"clc", 0, 0xf8, _, NoModrm, 0, 0, 0}, +{"cld", 0, 0xfc, _, NoModrm, 0, 0, 0}, +{"cli", 0, 0xfa, _, NoModrm, 0, 0, 0}, +{"clts", 0, 0x0f06, _, NoModrm, 0, 0, 0}, +{"cmc", 0, 0xf5, _, NoModrm, 0, 0, 0}, +{"lahf", 0, 0x9f, _, NoModrm, 0, 0, 0}, +{"sahf", 0, 0x9e, _, NoModrm, 0, 0, 0}, +{"pushf", 0, 0x9c, _, NoModrm, 0, 0, 0}, +{"popf", 0, 0x9d, _, NoModrm, 0, 0, 0}, +{"stc", 0, 0xf9, _, NoModrm, 0, 0, 0}, +{"std", 0, 0xfd, _, NoModrm, 0, 0, 0}, +{"sti", 0, 0xfb, _, NoModrm, 0, 0, 0}, + +{"add", 2, 0x0, _, DW|Modrm, Reg, Reg|Mem, 0}, +{"add", 2, 0x83, 0, Modrm, Imm8S, WordReg|WordMem, 0}, +{"add", 2, 0x4, _, W|NoModrm, Imm, Acc, 0}, +{"add", 2, 0x80, 0, W|Modrm, Imm, Reg|Mem, 0}, + +{"inc", 1, 0x40, _, ShortForm, WordReg, 0, 0}, +{"inc", 1, 0xfe, 0, W|Modrm, Reg|Mem, 0, 0}, + +{"sub", 2, 0x28, _, DW|Modrm, Reg, Reg|Mem, 0}, +{"sub", 2, 0x83, 5, Modrm, Imm8S, WordReg|WordMem, 0}, +{"sub", 2, 0x2c, _, W|NoModrm, Imm, Acc, 0}, +{"sub", 2, 0x80, 5, W|Modrm, Imm, Reg|Mem, 0}, + +{"dec", 1, 0x48, _, ShortForm, WordReg, 0, 0}, +{"dec", 1, 0xfe, 1, W|Modrm, Reg|Mem, 0, 0}, + +{"sbb", 2, 0x18, _, DW|Modrm, Reg, Reg|Mem, 0}, +{"sbb", 2, 0x83, 3, Modrm, Imm8S, WordReg|WordMem, 0}, +{"sbb", 2, 0x1c, _, W|NoModrm, Imm, Acc, 0}, +{"sbb", 2, 0x80, 3, W|Modrm, Imm, Reg|Mem, 0}, + +{"cmp", 2, 0x38, _, DW|Modrm, Reg, Reg|Mem, 0}, +{"cmp", 2, 0x83, 7, Modrm, Imm8S, WordReg|WordMem, 0}, +{"cmp", 2, 0x3c, _, W|NoModrm, Imm, Acc, 0}, +{"cmp", 2, 0x80, 7, W|Modrm, Imm, Reg|Mem, 0}, + +{"test", 2, 0x84, _, W|Modrm, Reg|Mem, Reg, 0}, +{"test", 2, 0x84, _, W|Modrm, Reg, Reg|Mem, 0}, +{"test", 2, 0xa8, _, W|NoModrm, Imm, Acc, 0}, +{"test", 2, 0xf6, 0, W|Modrm, Imm, Reg|Mem, 0}, + +{"and", 2, 0x20, _, DW|Modrm, Reg, Reg|Mem, 0}, +{"and", 2, 0x83, 4, Modrm, Imm8S, WordReg|WordMem, 0}, +{"and", 2, 0x24, _, W|NoModrm, Imm, Acc, 0}, +{"and", 2, 0x80, 4, W|Modrm, Imm, Reg|Mem, 0}, + +{"or", 2, 0x08, _, DW|Modrm, Reg, Reg|Mem, 0}, +{"or", 2, 0x83, 1, Modrm, Imm8S, WordReg|WordMem, 0}, +{"or", 2, 0x0c, _, W|NoModrm, Imm, Acc, 0}, +{"or", 2, 0x80, 1, W|Modrm, Imm, Reg|Mem, 0}, + +{"xor", 2, 0x30, _, DW|Modrm, Reg, Reg|Mem, 0}, +{"xor", 2, 0x83, 6, Modrm, Imm8S, WordReg|WordMem, 0}, +{"xor", 2, 0x34, _, W|NoModrm, Imm, Acc, 0}, +{"xor", 2, 0x80, 6, W|Modrm, Imm, Reg|Mem, 0}, + +{"adc", 2, 0x10, _, DW|Modrm, Reg, Reg|Mem, 0}, +{"adc", 2, 0x83, 2, Modrm, Imm8S, WordReg|WordMem, 0}, +{"adc", 2, 0x14, _, W|NoModrm, Imm, Acc, 0}, +{"adc", 2, 0x80, 2, W|Modrm, Imm, Reg|Mem, 0}, + +{"neg", 1, 0xf6, 3, W|Modrm, Reg|Mem, 0, 0}, +{"not", 1, 0xf6, 2, W|Modrm, Reg|Mem, 0, 0}, + +{"aaa", 0, 0x37, _, NoModrm, 0, 0, 0}, +{"aas", 0, 0x3f, _, NoModrm, 0, 0, 0}, +{"daa", 0, 0x27, _, NoModrm, 0, 0, 0}, +{"das", 0, 0x2f, _, NoModrm, 0, 0, 0}, +{"aad", 0, 0xd50a, _, NoModrm, 0, 0, 0}, +{"aam", 0, 0xd40a, _, NoModrm, 0, 0, 0}, + +/* conversion insns */ +/* conversion: intel naming */ +{"cbw", 0, 0x6698, _, NoModrm, 0, 0, 0}, +{"cwd", 0, 0x6699, _, NoModrm, 0, 0, 0}, +{"cwde", 0, 0x98, _, NoModrm, 0, 0, 0}, +{"cdq", 0, 0x99, _, NoModrm, 0, 0, 0}, +/* att naming */ +{"cbtw", 0, 0x6698, _, NoModrm, 0, 0, 0}, +{"cwtl", 0, 0x98, _, NoModrm, 0, 0, 0}, +{"cwtd", 0, 0x6699, _, NoModrm, 0, 0, 0}, +{"cltd", 0, 0x99, _, NoModrm, 0, 0, 0}, + +/* Warning! the mul/imul (opcode 0xf6) must only have 1 operand! They are + expanding 64-bit multiplies, and *cannot* be selected to accomplish + 'imul %ebx, %eax' (opcode 0x0faf must be used in this case) + These multiplies can only be selected with single opearnd forms. */ +{"mul", 1, 0xf6, 4, W|Modrm, Reg|Mem, 0, 0}, +{"imul", 1, 0xf6, 5, W|Modrm, Reg|Mem, 0, 0}, + + + + +/* imulKludge here is needed to reverse the i.rm.reg & i.rm.regmem fields. + These instructions are exceptions: 'imul $2, %eax, %ecx' would put + '%eax' in the reg field and '%ecx' in the regmem field if we did not + switch them. */ +{"imul", 2, 0x0faf, _, Modrm|ReverseRegRegmem, WordReg|Mem, WordReg, 0}, +{"imul", 3, 0x6b, _, Modrm|ReverseRegRegmem, Imm8S, WordReg|Mem, WordReg}, +{"imul", 3, 0x69, _, Modrm|ReverseRegRegmem, Imm16|Imm32, WordReg|Mem, WordReg}, +/* + imul with 2 operands mimicks imul with 3 by puting register both + in i.rm.reg & i.rm.regmem fields +*/ +{"imul", 2, 0x6b, _, Modrm|imulKludge, Imm8S, WordReg, 0}, +{"imul", 2, 0x69, _, Modrm|imulKludge, Imm16|Imm32, WordReg, 0}, +{"div", 1, 0xf6, 6, W|Modrm, Reg|Mem, 0, 0}, +{"div", 2, 0xf6, 6, W|Modrm, Reg|Mem, Acc, 0}, +{"idiv", 1, 0xf6, 7, W|Modrm, Reg|Mem, 0, 0}, +{"idiv", 2, 0xf6, 7, W|Modrm, Reg|Mem, Acc, 0}, + +{"rol", 2, 0xd0, 0, W|Modrm, Imm1, Reg|Mem, 0}, +{"rol", 2, 0xc0, 0, W|Modrm, Imm8, Reg|Mem, 0}, +{"rol", 2, 0xd2, 0, W|Modrm, ShiftCount, Reg|Mem, 0}, +{"rol", 1, 0xd0, 0, W|Modrm, Reg|Mem, 0, 0}, + +{"ror", 2, 0xd0, 1, W|Modrm, Imm1, Reg|Mem, 0}, +{"ror", 2, 0xc0, 1, W|Modrm, Imm8, Reg|Mem, 0}, +{"ror", 2, 0xd2, 1, W|Modrm, ShiftCount, Reg|Mem, 0}, +{"ror", 1, 0xd0, 1, W|Modrm, Reg|Mem, 0, 0}, + +{"rcl", 2, 0xd0, 2, W|Modrm, Imm1, Reg|Mem, 0}, +{"rcl", 2, 0xc0, 2, W|Modrm, Imm8, Reg|Mem, 0}, +{"rcl", 2, 0xd2, 2, W|Modrm, ShiftCount, Reg|Mem, 0}, +{"rcl", 1, 0xd0, 2, W|Modrm, Reg|Mem, 0, 0}, + +{"rcr", 2, 0xd0, 3, W|Modrm, Imm1, Reg|Mem, 0}, +{"rcr", 2, 0xc0, 3, W|Modrm, Imm8, Reg|Mem, 0}, +{"rcr", 2, 0xd2, 3, W|Modrm, ShiftCount, Reg|Mem, 0}, +{"rcr", 1, 0xd0, 3, W|Modrm, Reg|Mem, 0, 0}, + +{"sal", 2, 0xd0, 4, W|Modrm, Imm1, Reg|Mem, 0}, +{"sal", 2, 0xc0, 4, W|Modrm, Imm8, Reg|Mem, 0}, +{"sal", 2, 0xd2, 4, W|Modrm, ShiftCount, Reg|Mem, 0}, +{"sal", 1, 0xd0, 4, W|Modrm, Reg|Mem, 0, 0}, +{"shl", 2, 0xd0, 4, W|Modrm, Imm1, Reg|Mem, 0}, +{"shl", 2, 0xc0, 4, W|Modrm, Imm8, Reg|Mem, 0}, +{"shl", 2, 0xd2, 4, W|Modrm, ShiftCount, Reg|Mem, 0}, +{"shl", 1, 0xd0, 4, W|Modrm, Reg|Mem, 0, 0}, + +{"shld", 3, 0x0fa4, _, Modrm, Imm8, WordReg, WordReg|Mem}, +{"shld", 3, 0x0fa5, _, Modrm, ShiftCount, WordReg, WordReg|Mem}, + +{"shr", 2, 0xd0, 5, W|Modrm, Imm1, Reg|Mem, 0}, +{"shr", 2, 0xc0, 5, W|Modrm, Imm8, Reg|Mem, 0}, +{"shr", 2, 0xd2, 5, W|Modrm, ShiftCount, Reg|Mem, 0}, +{"shr", 1, 0xd0, 5, W|Modrm, Reg|Mem, 0, 0}, + +{"shrd", 3, 0x0fac, _, Modrm, Imm8, WordReg, WordReg|Mem}, +{"shrd", 3, 0x0fad, _, Modrm, ShiftCount, WordReg, WordReg|Mem}, + +{"sar", 2, 0xd0, 7, W|Modrm, Imm1, Reg|Mem, 0}, +{"sar", 2, 0xc0, 7, W|Modrm, Imm8, Reg|Mem, 0}, +{"sar", 2, 0xd2, 7, W|Modrm, ShiftCount, Reg|Mem, 0}, +{"sar", 1, 0xd0, 7, W|Modrm, Reg|Mem, 0, 0}, + +/* control transfer instructions */ +#define CALL_PC_RELATIVE 0xe8 +{"call", 1, 0xe8, _, JumpDword, Disp32, 0, 0}, +{"call", 1, 0xff, 2, Modrm, Reg|Mem|JumpAbsolute, 0, 0}, +#define CALL_FAR_IMMEDIATE 0x9a +{"lcall", 2, 0x9a, _, JumpInterSegment, Imm16, Imm32, 0}, +{"lcall", 1, 0xff, 3, Modrm, Mem, 0, 0}, + +#define JUMP_PC_RELATIVE 0xeb +{"jmp", 1, 0xeb, _, Jump, Disp, 0, 0}, +{"jmp", 1, 0xff, 4, Modrm, Reg32|Mem|JumpAbsolute, 0, 0}, +#define JUMP_FAR_IMMEDIATE 0xea +{"ljmp", 2, 0xea, _, JumpInterSegment, Imm16, Imm32, 0}, +{"ljmp", 1, 0xff, 5, Modrm, Mem, 0, 0}, + +{"ret", 0, 0xc3, _, NoModrm, 0, 0, 0}, +{"ret", 1, 0xc2, _, NoModrm, Imm16, 0, 0}, +{"lret", 0, 0xcb, _, NoModrm, 0, 0, 0}, +{"lret", 1, 0xca, _, NoModrm, Imm16, 0, 0}, +{"enter", 2, 0xc8, _, NoModrm, Imm16, Imm8, 0}, +{"leave", 0, 0xc9, _, NoModrm, 0, 0, 0}, + +/* conditional jumps */ +{"jo", 1, 0x70, _, Jump, Disp, 0, 0}, + +{"jno", 1, 0x71, _, Jump, Disp, 0, 0}, + +{"jb", 1, 0x72, _, Jump, Disp, 0, 0}, +{"jc", 1, 0x72, _, Jump, Disp, 0, 0}, +{"jnae", 1, 0x72, _, Jump, Disp, 0, 0}, + +{"jnb", 1, 0x73, _, Jump, Disp, 0, 0}, +{"jnc", 1, 0x73, _, Jump, Disp, 0, 0}, +{"jae", 1, 0x73, _, Jump, Disp, 0, 0}, + +{"je", 1, 0x74, _, Jump, Disp, 0, 0}, +{"jz", 1, 0x74, _, Jump, Disp, 0, 0}, + +{"jne", 1, 0x75, _, Jump, Disp, 0, 0}, +{"jnz", 1, 0x75, _, Jump, Disp, 0, 0}, + +{"jbe", 1, 0x76, _, Jump, Disp, 0, 0}, +{"jna", 1, 0x76, _, Jump, Disp, 0, 0}, + +{"jnbe", 1, 0x77, _, Jump, Disp, 0, 0}, +{"ja", 1, 0x77, _, Jump, Disp, 0, 0}, + +{"js", 1, 0x78, _, Jump, Disp, 0, 0}, + +{"jns", 1, 0x79, _, Jump, Disp, 0, 0}, + +{"jp", 1, 0x7a, _, Jump, Disp, 0, 0}, +{"jpe", 1, 0x7a, _, Jump, Disp, 0, 0}, + +{"jnp", 1, 0x7b, _, Jump, Disp, 0, 0}, +{"jpo", 1, 0x7b, _, Jump, Disp, 0, 0}, + +{"jl", 1, 0x7c, _, Jump, Disp, 0, 0}, +{"jnge", 1, 0x7c, _, Jump, Disp, 0, 0}, + +{"jnl", 1, 0x7d, _, Jump, Disp, 0, 0}, +{"jge", 1, 0x7d, _, Jump, Disp, 0, 0}, + +{"jle", 1, 0x7e, _, Jump, Disp, 0, 0}, +{"jng", 1, 0x7e, _, Jump, Disp, 0, 0}, + +{"jnle", 1, 0x7f, _, Jump, Disp, 0, 0}, +{"jg", 1, 0x7f, _, Jump, Disp, 0, 0}, + +/* these turn into pseudo operations when disp is larger than 8 bits */ +#define IS_JUMP_ON_CX_ZERO(o) \ + (o == 0x67e3) +#define IS_JUMP_ON_ECX_ZERO(o) \ + (o == 0xe3) + +{"jcxz", 1, 0x67e3, _, JumpByte, Disp, 0, 0}, +{"jecxz", 1, 0xe3, _, JumpByte, Disp, 0, 0}, + +#define IS_LOOP_ECX_TIMES(o) \ + (o == 0xe2 || o == 0xe1 || o == 0xe0) + +{"loop", 1, 0xe2, _, JumpByte, Disp, 0, 0}, + +{"loopz", 1, 0xe1, _, JumpByte, Disp, 0, 0}, +{"loope", 1, 0xe1, _, JumpByte, Disp, 0, 0}, + +{"loopnz", 1, 0xe0, _, JumpByte, Disp, 0, 0}, +{"loopne", 1, 0xe0, _, JumpByte, Disp, 0, 0}, + +/* set byte on flag instructions */ +{"seto", 1, 0x0f90, 0, Modrm, Reg8|Mem, 0, 0}, + +{"setno", 1, 0x0f91, 0, Modrm, Reg8|Mem, 0, 0}, + +{"setb", 1, 0x0f92, 0, Modrm, Reg8|Mem, 0, 0}, +{"setnae", 1, 0x0f92, 0, Modrm, Reg8|Mem, 0, 0}, + +{"setnb", 1, 0x0f93, 0, Modrm, Reg8|Mem, 0, 0}, +{"setae", 1, 0x0f93, 0, Modrm, Reg8|Mem, 0, 0}, + +{"sete", 1, 0x0f94, 0, Modrm, Reg8|Mem, 0, 0}, +{"setz", 1, 0x0f94, 0, Modrm, Reg8|Mem, 0, 0}, + +{"setne", 1, 0x0f95, 0, Modrm, Reg8|Mem, 0, 0}, +{"setnz", 1, 0x0f95, 0, Modrm, Reg8|Mem, 0, 0}, + +{"setbe", 1, 0x0f96, 0, Modrm, Reg8|Mem, 0, 0}, +{"setna", 1, 0x0f96, 0, Modrm, Reg8|Mem, 0, 0}, + +{"setnbe", 1, 0x0f97, 0, Modrm, Reg8|Mem, 0, 0}, +{"seta", 1, 0x0f97, 0, Modrm, Reg8|Mem, 0, 0}, + +{"sets", 1, 0x0f98, 0, Modrm, Reg8|Mem, 0, 0}, + +{"setns", 1, 0x0f99, 0, Modrm, Reg8|Mem, 0, 0}, + +{"setp", 1, 0x0f9a, 0, Modrm, Reg8|Mem, 0, 0}, +{"setpe", 1, 0x0f9a, 0, Modrm, Reg8|Mem, 0, 0}, + +{"setnp", 1, 0x0f9b, 0, Modrm, Reg8|Mem, 0, 0}, +{"setpo", 1, 0x0f9b, 0, Modrm, Reg8|Mem, 0, 0}, + +{"setl", 1, 0x0f9c, 0, Modrm, Reg8|Mem, 0, 0}, +{"setnge", 1, 0x0f9c, 0, Modrm, Reg8|Mem, 0, 0}, + +{"setnl", 1, 0x0f9d, 0, Modrm, Reg8|Mem, 0, 0}, +{"setge", 1, 0x0f9d, 0, Modrm, Reg8|Mem, 0, 0}, + +{"setle", 1, 0x0f9e, 0, Modrm, Reg8|Mem, 0, 0}, +{"setng", 1, 0x0f9e, 0, Modrm, Reg8|Mem, 0, 0}, + +{"setnle", 1, 0x0f9f, 0, Modrm, Reg8|Mem, 0, 0}, +{"setg", 1, 0x0f9f, 0, Modrm, Reg8|Mem, 0, 0}, + +#define IS_STRING_INSTRUCTION(o) \ + ((o) == 0xa6 || (o) == 0x6c || (o) == 0x6e || (o) == 0x6e || \ + (o) == 0xac || (o) == 0xa4 || (o) == 0xae || (o) == 0xaa || \ + (o) == 0xd7) + +/* string manipulation */ +{"cmps", 0, 0xa6, _, W|NoModrm, 0, 0, 0}, +{"ins", 0, 0x6c, _, W|NoModrm, 0, 0, 0}, +{"outs", 0, 0x6e, _, W|NoModrm, 0, 0, 0}, +{"lods", 0, 0xac, _, W|NoModrm, 0, 0, 0}, +{"movs", 0, 0xa4, _, W|NoModrm, 0, 0, 0}, +{"scas", 0, 0xae, _, W|NoModrm, 0, 0, 0}, +{"stos", 0, 0xaa, _, W|NoModrm, 0, 0, 0}, +{"xlat", 0, 0xd7, _, NoModrm, 0, 0, 0}, + +/* bit manipulation */ +{"bsf", 2, 0x0fbc, _, Modrm|ReverseRegRegmem, Reg|Mem, Reg, 0}, +{"bsr", 2, 0x0fbd, _, Modrm|ReverseRegRegmem, Reg|Mem, Reg, 0}, +{"bt", 2, 0x0fa3, _, Modrm, Reg, Reg|Mem, 0}, +{"bt", 2, 0x0fba, 4, Modrm, Imm8, Reg|Mem, 0}, +{"btc", 2, 0x0fbb, _, Modrm, Reg, Reg|Mem, 0}, +{"btc", 2, 0x0fba, 7, Modrm, Imm8, Reg|Mem, 0}, +{"btr", 2, 0x0fb3, _, Modrm, Reg, Reg|Mem, 0}, +{"btr", 2, 0x0fba, 6, Modrm, Imm8, Reg|Mem, 0}, +{"bts", 2, 0x0fab, _, Modrm, Reg, Reg|Mem, 0}, +{"bts", 2, 0x0fba, 5, Modrm, Imm8, Reg|Mem, 0}, + +/* interrupts & op. sys insns */ +/* See i386.c for conversion of 'int $3' into the special int 3 insn. */ +#define INT_OPCODE 0xcd +#define INT3_OPCODE 0xcc +{"int", 1, 0xcd, _, NoModrm, Imm8, 0, 0}, +{"int3", 0, 0xcc, _, NoModrm, 0, 0, 0}, +{"into", 0, 0xce, _, NoModrm, 0, 0, 0}, +{"iret", 0, 0xcf, _, NoModrm, 0, 0, 0}, + +{"boundl", 2, 0x62, _, Modrm, Reg32, Mem, 0}, +{"boundw", 2, 0x6662, _, Modrm, Reg16, Mem, 0}, + +{"hlt", 0, 0xf4, _, NoModrm, 0, 0, 0}, +{"wait", 0, 0x9b, _, NoModrm, 0, 0, 0}, +/* nop is actually 'xchgl %eax, %eax' */ +{"nop", 0, 0x90, _, NoModrm, 0, 0, 0}, + +/* protection control */ +{"arpl", 2, 0x63, _, Modrm, Reg16, Reg16|Mem, 0}, +{"lar", 2, 0x0f02, _, Modrm|ReverseRegRegmem, WordReg|Mem, WordReg, 0}, +{"lgdt", 1, 0x0f01, 2, Modrm, Mem, 0, 0}, +{"lidt", 1, 0x0f01, 3, Modrm, Mem, 0, 0}, +{"lldt", 1, 0x0f00, 2, Modrm, WordReg|Mem, 0, 0}, +{"lmsw", 1, 0x0f01, 6, Modrm, WordReg|Mem, 0, 0}, +{"lsl", 2, 0x0f03, _, Modrm|ReverseRegRegmem, WordReg|Mem, WordReg, 0}, +{"ltr", 1, 0x0f00, 3, Modrm, WordReg|Mem, 0, 0}, + +{"sgdt", 1, 0x0f01, 0, Modrm, Mem, 0, 0}, +{"sidt", 1, 0x0f01, 1, Modrm, Mem, 0, 0}, +{"sldt", 1, 0x0f00, 0, Modrm, WordReg|Mem, 0, 0}, +{"smsw", 1, 0x0f01, 4, Modrm, WordReg|Mem, 0, 0}, +{"str", 1, 0x0f00, 1, Modrm, Reg16|Mem, 0, 0}, + +{"verr", 1, 0x0f00, 4, Modrm, WordReg|Mem, 0, 0}, +{"verw", 1, 0x0f00, 5, Modrm, WordReg|Mem, 0, 0}, + +/* floating point instructions */ + +/* load */ +{"fld", 1, 0xd9c0, _, ShortForm, FloatReg, 0, 0}, /* register */ +{"flds", 1, 0xd9, 0, Modrm, Mem, 0, 0}, /* %st0 <-- mem float */ +{"fildl", 1, 0xdb, 0, Modrm, Mem, 0, 0}, /* %st0 <-- mem word */ +{"fldl", 1, 0xdd, 0, Modrm, Mem, 0, 0}, /* %st0 <-- mem double */ +{"fldl", 1, 0xd9c0, _, ShortForm, FloatReg, 0, 0}, /* register */ +{"filds", 1, 0xdf, 0, Modrm, Mem, 0, 0}, /* %st0 <-- mem dword */ +{"fildq", 1, 0xdf, 5, Modrm, Mem, 0, 0}, /* %st0 <-- mem qword */ +{"fldt", 1, 0xdb, 5, Modrm, Mem, 0, 0}, /* %st0 <-- mem efloat */ +{"fbld", 1, 0xdf, 4, Modrm, Mem, 0, 0}, /* %st0 <-- mem bcd */ + +/* store (no pop) */ +{"fst", 1, 0xddd0, _, ShortForm, FloatReg, 0, 0}, /* register */ +{"fsts", 1, 0xd9, 2, Modrm, Mem, 0, 0}, /* %st0 --> mem float */ +{"fistl", 1, 0xdb, 2, Modrm, Mem, 0, 0}, /* %st0 --> mem dword */ +{"fstl", 1, 0xdd, 2, Modrm, Mem, 0, 0}, /* %st0 --> mem double */ +{"fstl", 1, 0xddd0, _, ShortForm, FloatReg, 0, 0}, /* register */ +{"fists", 1, 0xdf, 2, Modrm, Mem, 0, 0}, /* %st0 --> mem word */ + +/* store (with pop) */ +{"fstp", 1, 0xddd8, _, ShortForm, FloatReg, 0, 0}, /* register */ +{"fstps", 1, 0xd9, 3, Modrm, Mem, 0, 0}, /* %st0 --> mem float */ +{"fistpl", 1, 0xdb, 3, Modrm, Mem, 0, 0}, /* %st0 --> mem word */ +{"fstpl", 1, 0xdd, 3, Modrm, Mem, 0, 0}, /* %st0 --> mem double */ +{"fstpl", 1, 0xddd8, _, ShortForm, FloatReg, 0, 0}, /* register */ +{"fistps", 1, 0xdf, 3, Modrm, Mem, 0, 0}, /* %st0 --> mem dword */ +{"fistpq", 1, 0xdf, 7, Modrm, Mem, 0, 0}, /* %st0 --> mem qword */ +{"fstpt", 1, 0xdb, 7, Modrm, Mem, 0, 0}, /* %st0 --> mem efloat */ +{"fbstp", 1, 0xdf, 6, Modrm, Mem, 0, 0}, /* %st0 --> mem bcd */ + +/* exchange %st with %st0 */ +{"fxch", 1, 0xd9c8, _, ShortForm, FloatReg, 0, 0}, + +/* comparison (without pop) */ +{"fcom", 1, 0xd8d0, _, ShortForm, FloatReg, 0, 0}, +{"fcoms", 1, 0xd8, 2, Modrm, Mem, 0, 0}, /* compare %st0, mem float */ +{"ficoml", 1, 0xda, 2, Modrm, Mem, 0, 0}, /* compare %st0, mem word */ +{"fcoml", 1, 0xdc, 2, Modrm, Mem, 0, 0}, /* compare %st0, mem double */ +{"fcoml", 1, 0xd8d0, _, ShortForm, FloatReg, 0, 0}, +{"ficoms", 1, 0xde, 2, Modrm, Mem, 0, 0}, /* compare %st0, mem dword */ + +/* comparison (with pop) */ +{"fcomp", 1, 0xd8d8, _, ShortForm, FloatReg, 0, 0}, +{"fcomps", 1, 0xd8, 3, Modrm, Mem, 0, 0}, /* compare %st0, mem float */ +{"ficompl", 1, 0xda, 3, Modrm, Mem, 0, 0}, /* compare %st0, mem word */ +{"fcompl", 1, 0xdc, 3, Modrm, Mem, 0, 0}, /* compare %st0, mem double */ +{"fcompl", 1, 0xd8d8, _, ShortForm, FloatReg, 0, 0}, +{"ficomps", 1, 0xde, 3, Modrm, Mem, 0, 0}, /* compare %st0, mem dword */ +{"fcompp", 0, 0xded9, _, NoModrm, 0, 0, 0}, /* compare %st0, %st1 & pop twice */ + +/* unordered comparison (with pop) */ +{"fucom", 1, 0xdde0, _, ShortForm, FloatReg, 0, 0}, +{"fucomp", 1, 0xdde8, _, ShortForm, FloatReg, 0, 0}, +{"fucompp", 0, 0xdae9, _, NoModrm, 0, 0, 0}, /* ucompare %st0, %st1 & pop twice */ + +{"ftst", 0, 0xd9e4, _, NoModrm, 0, 0, 0}, /* test %st0 */ +{"fxam", 0, 0xd9e5, _, NoModrm, 0, 0, 0}, /* examine %st0 */ + +/* load constants into %st0 */ +{"fld1", 0, 0xd9e8, _, NoModrm, 0, 0, 0}, /* %st0 <-- 1.0 */ +{"fldl2t", 0, 0xd9e9, _, NoModrm, 0, 0, 0}, /* %st0 <-- log2(10) */ +{"fldl2e", 0, 0xd9ea, _, NoModrm, 0, 0, 0}, /* %st0 <-- log2(e) */ +{"fldpi", 0, 0xd9eb, _, NoModrm, 0, 0, 0}, /* %st0 <-- pi */ +{"fldlg2", 0, 0xd9ec, _, NoModrm, 0, 0, 0}, /* %st0 <-- log10(2) */ +{"fldln2", 0, 0xd9ed, _, NoModrm, 0, 0, 0}, /* %st0 <-- ln(2) */ +{"fldz", 0, 0xd9ee, _, NoModrm, 0, 0, 0}, /* %st0 <-- 0.0 */ + +/* arithmetic */ + +/* add */ +{"fadd", 1, 0xd8c0, _, ShortForm, FloatReg, 0, 0}, +{"fadd", 2, 0xd8c0, _, ShortForm|FloatD, FloatReg, FloatAcc, 0}, +{"fadd", 0, 0xdcc1, _, NoModrm, 0, 0, 0}, /* alias for fadd %st, %st(1) */ +{"faddp", 1, 0xdac0, _, ShortForm, FloatReg, 0, 0}, +{"faddp", 2, 0xdac0, _, ShortForm|FloatD, FloatReg, FloatAcc, 0}, +{"faddp", 0, 0xdec1, _, NoModrm, 0, 0, 0}, /* alias for faddp %st, %st(1) */ +{"fadds", 1, 0xd8, 0, Modrm, Mem, 0, 0}, +{"fiaddl", 1, 0xda, 0, Modrm, Mem, 0, 0}, +{"faddl", 1, 0xdc, 0, Modrm, Mem, 0, 0}, +{"fiadds", 1, 0xde, 0, Modrm, Mem, 0, 0}, + +/* sub */ +/* Note: intel has decided that certain of these operations are reversed + in assembler syntax. */ +{"fsub", 1, 0xd8e0, _, ShortForm, FloatReg, 0, 0}, +{"fsub", 2, 0xd8e0, _, ShortForm, FloatReg, FloatAcc, 0}, +#ifdef NON_BROKEN_OPCODES +{"fsub", 2, 0xdce8, _, ShortForm, FloatAcc, FloatReg, 0}, +#else +{"fsub", 2, 0xdce0, _, ShortForm, FloatAcc, FloatReg, 0}, +#endif +{"fsub", 0, 0xdce1, _, NoModrm, 0, 0, 0}, +{"fsubp", 1, 0xdae0, _, ShortForm, FloatReg, 0, 0}, +{"fsubp", 2, 0xdae0, _, ShortForm, FloatReg, FloatAcc, 0}, +#ifdef NON_BROKEN_OPCODES +{"fsubp", 2, 0xdee8, _, ShortForm, FloatAcc, FloatReg, 0}, +#else +{"fsubp", 2, 0xdee0, _, ShortForm, FloatAcc, FloatReg, 0}, +#endif +{"fsubp", 0, 0xdee1, _, NoModrm, 0, 0, 0}, +{"fsubs", 1, 0xd8, 4, Modrm, Mem, 0, 0}, +{"fisubl", 1, 0xda, 4, Modrm, Mem, 0, 0}, +{"fsubl", 1, 0xdc, 4, Modrm, Mem, 0, 0}, +{"fisubs", 1, 0xde, 4, Modrm, Mem, 0, 0}, + +/* sub reverse */ +{"fsubr", 1, 0xd8e8, _, ShortForm, FloatReg, 0, 0}, +{"fsubr", 2, 0xd8e8, _, ShortForm, FloatReg, FloatAcc, 0}, +#ifdef NON_BROKEN_OPCODES +{"fsubr", 2, 0xdce0, _, ShortForm, FloatAcc, FloatReg, 0}, +#else +{"fsubr", 2, 0xdce8, _, ShortForm, FloatAcc, FloatReg, 0}, +#endif +{"fsubr", 0, 0xdce9, _, NoModrm, 0, 0, 0}, +{"fsubrp", 1, 0xdae8, _, ShortForm, FloatReg, 0, 0}, +{"fsubrp", 2, 0xdae8, _, ShortForm, FloatReg, FloatAcc, 0}, +#ifdef NON_BROKEN_OPCODES +{"fsubrp", 2, 0xdee0, _, ShortForm, FloatAcc, FloatReg, 0}, +#else +{"fsubrp", 2, 0xdee8, _, ShortForm, FloatAcc, FloatReg, 0}, +#endif +{"fsubrp", 0, 0xdee9, _, NoModrm, 0, 0, 0}, +{"fsubrs", 1, 0xd8, 5, Modrm, Mem, 0, 0}, +{"fisubrl", 1, 0xda, 5, Modrm, Mem, 0, 0}, +{"fsubrl", 1, 0xdc, 5, Modrm, Mem, 0, 0}, +{"fisubrs", 1, 0xde, 5, Modrm, Mem, 0, 0}, + +/* mul */ +{"fmul", 1, 0xd8c8, _, ShortForm, FloatReg, 0, 0}, +{"fmul", 2, 0xd8c8, _, ShortForm|FloatD, FloatReg, FloatAcc, 0}, +{"fmul", 0, 0xdcc9, _, NoModrm, 0, 0, 0}, +{"fmulp", 1, 0xdac8, _, ShortForm, FloatReg, 0, 0}, +{"fmulp", 2, 0xdac8, _, ShortForm|FloatD, FloatReg, FloatAcc, 0}, +{"fmulp", 0, 0xdec9, _, NoModrm, 0, 0, 0}, +{"fmuls", 1, 0xd8, 1, Modrm, Mem, 0, 0}, +{"fimull", 1, 0xda, 1, Modrm, Mem, 0, 0}, +{"fmull", 1, 0xdc, 1, Modrm, Mem, 0, 0}, +{"fimuls", 1, 0xde, 1, Modrm, Mem, 0, 0}, + +/* div */ +/* Note: intel has decided that certain of these operations are reversed + in assembler syntax. */ +{"fdiv", 1, 0xd8f0, _, ShortForm, FloatReg, 0, 0}, +{"fdiv", 2, 0xd8f0, _, ShortForm, FloatReg, FloatAcc, 0}, +#ifdef NON_BROKEN_OPCODES +{"fdiv", 2, 0xdcf8, _, ShortForm, FloatAcc, FloatReg, 0}, +#else +{"fdiv", 2, 0xdcf0, _, ShortForm, FloatAcc, FloatReg, 0}, +#endif +{"fdiv", 0, 0xdcf1, _, NoModrm, 0, 0, 0}, +{"fdivp", 1, 0xdaf0, _, ShortForm, FloatReg, 0, 0}, +{"fdivp", 2, 0xdaf0, _, ShortForm, FloatReg, FloatAcc, 0}, +#ifdef NON_BROKEN_OPCODES +{"fdivp", 2, 0xdef8, _, ShortForm, FloatAcc, FloatReg, 0}, +#else +{"fdivp", 2, 0xdef0, _, ShortForm, FloatAcc, FloatReg, 0}, +#endif +{"fdivp", 0, 0xdef1, _, NoModrm, 0, 0, 0}, +{"fdivs", 1, 0xd8, 6, Modrm, Mem, 0, 0}, +{"fidivl", 1, 0xda, 6, Modrm, Mem, 0, 0}, +{"fdivl", 1, 0xdc, 6, Modrm, Mem, 0, 0}, +{"fidivs", 1, 0xde, 6, Modrm, Mem, 0, 0}, + +/* div reverse */ +{"fdivr", 1, 0xd8f8, _, ShortForm, FloatReg, 0, 0}, +{"fdivr", 2, 0xd8f8, _, ShortForm, FloatReg, FloatAcc, 0}, +#ifdef NON_BROKEN_OPCODES +{"fdivr", 2, 0xdcf0, _, ShortForm, FloatAcc, FloatReg, 0}, +#else +{"fdivr", 2, 0xdcf8, _, ShortForm, FloatAcc, FloatReg, 0}, +#endif +{"fdivr", 0, 0xdcf9, _, NoModrm, 0, 0, 0}, +{"fdivrp", 1, 0xdaf8, _, ShortForm, FloatReg, 0, 0}, +{"fdivrp", 2, 0xdaf8, _, ShortForm, FloatReg, FloatAcc, 0}, +#ifdef NON_BROKEN_OPCODES +{"fdivrp", 2, 0xdef0, _, ShortForm, FloatAcc, FloatReg, 0}, +#else +{"fdivrp", 2, 0xdef8, _, ShortForm, FloatAcc, FloatReg, 0}, +#endif +{"fdivrp", 0, 0xdef9, _, NoModrm, 0, 0, 0}, +{"fdivrs", 1, 0xd8, 7, Modrm, Mem, 0, 0}, +{"fidivrl", 1, 0xda, 7, Modrm, Mem, 0, 0}, +{"fdivrl", 1, 0xdc, 7, Modrm, Mem, 0, 0}, +{"fidivrs", 1, 0xde, 7, Modrm, Mem, 0, 0}, + +{"f2xm1", 0, 0xd9f0, _, NoModrm, 0, 0, 0}, +{"fyl2x", 0, 0xd9f1, _, NoModrm, 0, 0, 0}, +{"fptan", 0, 0xd9f2, _, NoModrm, 0, 0, 0}, +{"fpatan", 0, 0xd9f3, _, NoModrm, 0, 0, 0}, +{"fxtract", 0, 0xd9f4, _, NoModrm, 0, 0, 0}, +{"fprem1", 0, 0xd9f5, _, NoModrm, 0, 0, 0}, +{"fdecstp", 0, 0xd9f6, _, NoModrm, 0, 0, 0}, +{"fincstp", 0, 0xd9f7, _, NoModrm, 0, 0, 0}, +{"fprem", 0, 0xd9f8, _, NoModrm, 0, 0, 0}, +{"fyl2xp1", 0, 0xd9f9, _, NoModrm, 0, 0, 0}, +{"fsqrt", 0, 0xd9fa, _, NoModrm, 0, 0, 0}, +{"fsincos", 0, 0xd9fb, _, NoModrm, 0, 0, 0}, +{"frndint", 0, 0xd9fc, _, NoModrm, 0, 0, 0}, +{"fscale", 0, 0xd9fd, _, NoModrm, 0, 0, 0}, +{"fsin", 0, 0xd9fe, _, NoModrm, 0, 0, 0}, +{"fcos", 0, 0xd9ff, _, NoModrm, 0, 0, 0}, + +{"fchs", 0, 0xd9e0, _, NoModrm, 0, 0, 0}, +{"fabs", 0, 0xd9e1, _, NoModrm, 0, 0, 0}, + +/* processor control */ +{"fninit", 0, 0xdbe3, _, NoModrm, 0, 0, 0}, +{"finit", 0, 0xdbe3, _, NoModrm, 0, 0, 0}, +{"fldcw", 1, 0xd9, 5, Modrm, Mem, 0, 0}, +{"fnstcw", 1, 0xd9, 7, Modrm, Mem, 0, 0}, +{"fstcw", 1, 0xd9, 7, Modrm, Mem, 0, 0}, +{"fnstsw", 1, 0xdfe0, _, NoModrm, Acc, 0, 0}, +{"fnstsw", 1, 0xdd, 7, Modrm, Mem, 0, 0}, +{"fnstsw", 0, 0xdfe0, _, NoModrm, 0, 0, 0}, +{"fstsw", 1, 0xdfe0, _, NoModrm, Acc, 0, 0}, +{"fstsw", 1, 0xdd, 7, Modrm, Mem, 0, 0}, +{"fstsw", 0, 0xdfe0, _, NoModrm, 0, 0, 0}, +{"fnclex", 0, 0xdbe2, _, NoModrm, 0, 0, 0}, +{"fclex", 0, 0xdbe2, _, NoModrm, 0, 0, 0}, +/* + We ignore the short format (287) versions of fstenv/fldenv & fsave/frstor + instructions; i'm not sure how to add them or how they are different. + My 386/387 book offers no details about this. +*/ +{"fnstenv", 1, 0xd9, 6, Modrm, Mem, 0, 0}, +{"fstenv", 1, 0xd9, 6, Modrm, Mem, 0, 0}, +{"fldenv", 1, 0xd9, 4, Modrm, Mem, 0, 0}, +{"fnsave", 1, 0xdd, 6, Modrm, Mem, 0, 0}, +{"fsave", 1, 0xdd, 6, Modrm, Mem, 0, 0}, +{"frstor", 1, 0xdd, 4, Modrm, Mem, 0, 0}, + +{"ffree", 1, 0xddc0, _, ShortForm, FloatReg, 0, 0}, +{"fnop", 0, 0xd9d0, _, NoModrm, 0, 0, 0}, +{"fwait", 0, 0x9b, _, NoModrm, 0, 0, 0}, + +/* + opcode prefixes; we allow them as seperate insns too + (see prefix table below) +*/ +{"aword", 0, 0x67, _, NoModrm, 0, 0, 0}, +{"word", 0, 0x66, _, NoModrm, 0, 0, 0}, +{"lock", 0, 0xf0, _, NoModrm, 0, 0, 0}, +{"cs", 0, 0x2e, _, NoModrm, 0, 0, 0}, +{"ds", 0, 0x3e, _, NoModrm, 0, 0, 0}, +{"es", 0, 0x26, _, NoModrm, 0, 0, 0}, +{"fs", 0, 0x64, _, NoModrm, 0, 0, 0}, +{"gs", 0, 0x65, _, NoModrm, 0, 0, 0}, +{"ss", 0, 0x36, _, NoModrm, 0, 0, 0}, +{"rep", 0, 0xf3, _, NoModrm, 0, 0, 0}, +{"repe", 0, 0xf3, _, NoModrm, 0, 0, 0}, +{ "repne", 0, 0xf2, _, NoModrm, 0, 0, 0}, + +{"", 0, 0, 0, 0, 0, 0, 0} /* sentinal */ +}; +#undef _ + +template *i386_optab_end + = i386_optab + sizeof (i386_optab)/sizeof(i386_optab[0]); + +/* 386 register table */ + +reg_entry i386_regtab[] = { + /* 8 bit regs */ + {"al", Reg8|Acc, 0}, {"cl", Reg8|ShiftCount, 1}, {"dl", Reg8, 2}, + {"bl", Reg8, 3}, + {"ah", Reg8, 4}, {"ch", Reg8, 5}, {"dh", Reg8, 6}, {"bh", Reg8, 7}, + /* 16 bit regs */ + {"ax", Reg16|Acc, 0}, {"cx", Reg16, 1}, {"dx", Reg16|InOutPortReg, 2}, {"bx", Reg16, 3}, + {"sp", Reg16, 4}, {"bp", Reg16, 5}, {"si", Reg16, 6}, {"di", Reg16, 7}, + /* 32 bit regs */ + {"eax", Reg32|Acc, 0}, {"ecx", Reg32, 1}, {"edx", Reg32, 2}, {"ebx", Reg32, 3}, + {"esp", Reg32, 4}, {"ebp", Reg32, 5}, {"esi", Reg32, 6}, {"edi", Reg32, 7}, + /* segment registers */ + {"es", SReg2, 0}, {"cs", SReg2, 1}, {"ss", SReg2, 2}, + {"ds", SReg2, 3}, {"fs", SReg3, 4}, {"gs", SReg3, 5}, + /* control registers */ + {"cr0", Control, 0}, {"cr2", Control, 2}, {"cr3", Control, 3}, + /* debug registers */ + {"db0", Debug, 0}, {"db1", Debug, 1}, {"db2", Debug, 2}, + {"db3", Debug, 3}, {"db6", Debug, 6}, {"db7", Debug, 7}, + /* test registers */ + {"tr6", Test, 6}, {"tr7", Test, 7}, + /* float registers */ + {"st(0)", FloatReg|FloatAcc, 0}, + {"st", FloatReg|FloatAcc, 0}, + {"st(1)", FloatReg, 1}, {"st(2)", FloatReg, 2}, + {"st(3)", FloatReg, 3}, {"st(4)", FloatReg, 4}, {"st(5)", FloatReg, 5}, + {"st(6)", FloatReg, 6}, {"st(7)", FloatReg, 7} +}; + +#define MAX_REG_NAME_SIZE 8 /* for parsing register names from input */ + +reg_entry *i386_regtab_end + = i386_regtab + sizeof(i386_regtab)/sizeof(i386_regtab[0]); + +/* segment stuff */ +seg_entry cs = { "cs", 0x2e }; +seg_entry ds = { "ds", 0x3e }; +seg_entry ss = { "ss", 0x36 }; +seg_entry es = { "es", 0x26 }; +seg_entry fs = { "fs", 0x64 }; +seg_entry gs = { "gs", 0x65 }; +seg_entry null = { "", 0x0 }; + +/* + This table is used to store the default segment register implied by all + possible memory addressing modes. + It is indexed by the mode & modrm entries of the modrm byte as follows: + index = (mode<<3) | modrm; +*/ +seg_entry *one_byte_segment_defaults[] = { + /* mode 0 */ + &ds, &ds, &ds, &ds, &null, &ds, &ds, &ds, + /* mode 1 */ + &ds, &ds, &ds, &ds, &null, &ss, &ds, &ds, + /* mode 2 */ + &ds, &ds, &ds, &ds, &null, &ss, &ds, &ds, + /* mode 3 --- not a memory reference; never referenced */ +}; + +seg_entry *two_byte_segment_defaults[] = { + /* mode 0 */ + &ds, &ds, &ds, &ds, &ss, &ds, &ds, &ds, + /* mode 1 */ + &ds, &ds, &ds, &ds, &ss, &ds, &ds, &ds, + /* mode 2 */ + &ds, &ds, &ds, &ds, &ss, &ds, &ds, &ds, + /* mode 3 --- not a memory reference; never referenced */ +}; + +prefix_entry i386_prefixtab[] = { + { "addr16", 0x67 }, /* address size prefix ==> 16bit addressing + * (How is this useful?) */ +#define WORD_PREFIX_OPCODE 0x66 + { "data16", 0x66 }, /* operand size prefix */ + { "lock", 0xf0 }, /* bus lock prefix */ + { "wait", 0x9b }, /* wait for coprocessor */ + { "cs", 0x2e }, { "ds", 0x3e }, /* segment overrides ... */ + { "es", 0x26 }, { "fs", 0x64 }, + { "gs", 0x65 }, { "ss", 0x36 }, +/* REPE & REPNE used to detect rep/repne with a non-string instruction */ +#define REPNE 0xf2 +#define REPE 0xf3 + { "rep", 0xf3 }, { "repe", 0xf3 }, /* repeat string instructions */ + { "repne", 0xf2 } +}; + +prefix_entry *i386_prefixtab_end + = i386_prefixtab + sizeof(i386_prefixtab)/sizeof(i386_prefixtab[0]); + diff --git a/gnu/usr.bin/as/config/i386.c b/gnu/usr.bin/as/config/i386.c new file mode 100644 index 0000000000..2281acd06f --- /dev/null +++ b/gnu/usr.bin/as/config/i386.c @@ -0,0 +1,1946 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + */ + +#ifndef lint +static char sccsid[] = "@(#)i386.c 6.4 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* i386.c -- Assemble code for the Intel 80386 + Copyright (C) 1989, Free Software Foundation. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + Intel 80386 machine specific gas. + Written by Eliot Dresselhaus (eliot@mgm.mit.edu). + Bugs & suggestions are completely welcome. This is free software. + Please help us make it better. +*/ + +#include +#include +#include + +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +extern char *alloca(); +#endif +#ifdef USG +#define index strchr +#endif + +#include "as.h" +#include "read.h" +#include "flonum.h" +#include "obstack.h" +#include "frags.h" +#include "struc-symbol.h" +#include "expr.h" +#include "symbols.h" +#include "hash.h" +#include "md.h" +#include "i386.h" +#include "i386-opcode.h" + +long omagic = OMAGIC; +char FLT_CHARS[] = "fFdDxX"; +char EXP_CHARS[] = "eE"; +char line_comment_chars[] = "#"; +char comment_chars[] = "#"; + +/* tables for lexical analysis */ +static char opcode_chars[256]; +static char register_chars[256]; +static char operand_chars[256]; +static char space_chars[256]; +static char identifier_chars[256]; +static char digit_chars[256]; + +/* lexical macros */ +#define is_opcode_char(x) (opcode_chars[(unsigned char) x]) +#define is_operand_char(x) (operand_chars[(unsigned char) x]) +#define is_register_char(x) (register_chars[(unsigned char) x]) +#define is_space_char(x) (space_chars[(unsigned char) x]) +#define is_identifier_char(x) (identifier_chars[(unsigned char) x]) +#define is_digit_char(x) (digit_chars[(unsigned char) x]) + +/* put here all non-digit non-letter charcters that may occur in an operand */ +static char operand_special_chars[] = "%$-+(,)*._~/<>|&^!:"; + +static char *ordinal_names[] = { "first", "second", "third" }; /* for printfs */ + +/* md_assemble() always leaves the strings it's passed unaltered. To + effect this we maintain a stack of saved characters that we've smashed + with '\0's (indicating end of strings for various sub-fields of the + assembler instruction). */ +static char save_stack[32]; +static char *save_stack_p; /* stack pointer */ +#define END_STRING_AND_SAVE(s) *save_stack_p++ = *s; *s = '\0' +#define RESTORE_END_STRING(s) *s = *--save_stack_p + +/* The instruction we're assembling. */ +static i386_insn i; + +/* Per instruction expressionS buffers: 2 displacements & 2 immediate max. */ +static expressionS disp_expressions[2], im_expressions[2]; + +/* pointers to ebp & esp entries in reg_hash hash table */ +static reg_entry *ebp, *esp; + +static int this_operand; /* current operand we are working on */ + +/* +Interface to relax_segment. +There are 2 relax states for 386 jump insns: one for conditional & one +for unconditional jumps. This is because the these two types of jumps +add different sizes to frags when we're figuring out what sort of jump +to choose to reach a given label. */ + +/* types */ +#define COND_JUMP 1 /* conditional jump */ +#define UNCOND_JUMP 2 /* unconditional jump */ +/* sizes */ +#define BYTE 0 +#define WORD 1 +#define DWORD 2 +#define UNKNOWN_SIZE 3 + +#define ENCODE_RELAX_STATE(type,size) ((type<<2) | (size)) +#define SIZE_FROM_RELAX_STATE(s) \ + ( (((s) & 0x3) == BYTE ? 1 : (((s) & 0x3) == WORD ? 2 : 4)) ) + +const relax_typeS md_relax_table[] = { +/* + The fields are: + 1) most positive reach of this state, + 2) most negative reach of this state, + 3) how many bytes this mode will add to the size of the current frag + 4) which index into the table to try if we can't fit into this one. +*/ + {1, 1, 0, 0}, + {1, 1, 0, 0}, + {1, 1, 0, 0}, + {1, 1, 0, 0}, + + /* For now we don't use word displacement jumps: they may be + untrustworthy. */ + {127+1, -128+1, 0, ENCODE_RELAX_STATE(COND_JUMP,DWORD) }, + /* word conditionals add 3 bytes to frag: + 2 opcode prefix; 1 displacement bytes */ + {32767+2, -32768+2, 3, ENCODE_RELAX_STATE(COND_JUMP,DWORD) }, + /* dword conditionals adds 4 bytes to frag: + 1 opcode prefix; 3 displacement bytes */ + {0, 0, 4, 0}, + {1, 1, 0, 0}, + + {127+1, -128+1, 0, ENCODE_RELAX_STATE(UNCOND_JUMP,DWORD) }, + /* word jmp adds 2 bytes to frag: + 1 opcode prefix; 1 displacement bytes */ + {32767+2, -32768+2, 2, ENCODE_RELAX_STATE(UNCOND_JUMP,DWORD) }, + /* dword jmp adds 3 bytes to frag: + 0 opcode prefix; 3 displacement bytes */ + {0, 0, 3, 0}, + {1, 1, 0, 0}, + +}; + +void float_cons (), cons (); + +/* Ignore certain directives generated by gcc. This probably should + not be here. */ +void dummy () +{ + while (*input_line_pointer && *input_line_pointer != '\n') + input_line_pointer++; +} + +const pseudo_typeS md_pseudo_table[] = { + { "ffloat", float_cons, 'f' }, + { "dfloat", float_cons, 'd' }, + { "tfloat", float_cons, 'x' }, + { "value", cons, 2 }, + { "ident", dummy, 0 }, /* ignore these directives */ + { "def", dummy, 0 }, + { "optim", dummy, 0 }, /* For sun386i cc */ + { "version", dummy, 0 }, + { "ln", dummy, 0 }, + { 0, 0, 0 } +}; + +/* for interface with expression () */ +extern char * input_line_pointer; +char * index (); + +char * output_invalid (); +reg_entry * parse_register (); + +/* obstack for constructing various things in md_begin */ +struct obstack o; + +/* hash table for opcode lookup */ +static struct hash_control *op_hash = (struct hash_control *) 0; +/* hash table for register lookup */ +static struct hash_control *reg_hash = (struct hash_control *) 0; +/* hash table for prefix lookup */ +static struct hash_control *prefix_hash = (struct hash_control *) 0; + + +void md_begin () +{ + char * hash_err; + + obstack_begin (&o,4096); + + /* initialize op_hash hash table */ + op_hash = hash_new(); /* xmalloc handles error */ + + { + register template *optab; + register templates *core_optab; + char *prev_name; + + optab = i386_optab; /* setup for loop */ + prev_name = optab->name; + obstack_grow (&o, optab, sizeof(template)); + core_optab = (templates *) xmalloc (sizeof (templates)); + + for (optab++; optab < i386_optab_end; optab++) { + if (! strcmp (optab->name, prev_name)) { + /* same name as before --> append to current template list */ + obstack_grow (&o, optab, sizeof(template)); + } else { + /* different name --> ship out current template list; + add to hash table; & begin anew */ + /* Note: end must be set before start! since obstack_next_free changes + upon opstack_finish */ + core_optab->end = (template *) obstack_next_free(&o); + core_optab->start = (template *) obstack_finish(&o); + hash_err = hash_insert (op_hash, prev_name, (char *) core_optab); + if (hash_err && *hash_err) { + hash_error: + as_fatal("Internal Error: Can't hash %s: %s",prev_name, hash_err); + } + prev_name = optab->name; + core_optab = (templates *) xmalloc (sizeof(templates)); + obstack_grow (&o, optab, sizeof(template)); + } + } + } + + /* initialize reg_hash hash table */ + reg_hash = hash_new(); + { + register reg_entry *regtab; + + for (regtab = i386_regtab; regtab < i386_regtab_end; regtab++) { + hash_err = hash_insert (reg_hash, regtab->reg_name, regtab); + if (hash_err && *hash_err) goto hash_error; + } + } + + esp = (reg_entry *) hash_find (reg_hash, "esp"); + ebp = (reg_entry *) hash_find (reg_hash, "ebp"); + + /* initialize reg_hash hash table */ + prefix_hash = hash_new(); + { + register prefix_entry *prefixtab; + + for (prefixtab = i386_prefixtab; + prefixtab < i386_prefixtab_end; prefixtab++) { + hash_err = hash_insert (prefix_hash, prefixtab->prefix_name, prefixtab); + if (hash_err && *hash_err) goto hash_error; + } + } + + /* fill in lexical tables: opcode_chars, operand_chars, space_chars */ + { + register unsigned int c; + + bzero (opcode_chars, sizeof(opcode_chars)); + bzero (operand_chars, sizeof(operand_chars)); + bzero (space_chars, sizeof(space_chars)); + bzero (identifier_chars, sizeof(identifier_chars)); + bzero (digit_chars, sizeof(digit_chars)); + + for (c = 0; c < 256; c++) { + if (islower(c) || isdigit(c)) { + opcode_chars[c] = c; + register_chars[c] = c; + } else if (isupper(c)) { + opcode_chars[c] = tolower(c); + register_chars[c] = opcode_chars[c]; + } else if (c == PREFIX_SEPERATOR) { + opcode_chars[c] = c; + } else if (c == ')' || c == '(') { + register_chars[c] = c; + } + + if (isupper(c) || islower(c) || isdigit(c)) + operand_chars[c] = c; + else if (c && index(operand_special_chars, c)) + operand_chars[c] = c; + + if (isdigit(c) || c == '-') digit_chars[c] = c; + + if (isalpha(c) || c == '_' || c == '.' || isdigit(c)) + identifier_chars[c] = c; + + if (c == ' ' || c == '\t') space_chars[c] = c; + } + } +} + +void md_end() {} /* not much to do here. */ + + +#ifdef DEBUG386 + +/* debugging routines for md_assemble */ +static void pi (), pte (), pt (), pe (), ps (); + +static void pi (line, x) + char * line; + i386_insn *x; +{ + register template *p; + int i; + + fprintf (stdout, "%s: template ", line); + pte (&x->tm); + fprintf (stdout, " modrm: mode %x reg %x reg/mem %x", + x->rm.mode, x->rm.reg, x->rm.regmem); + fprintf (stdout, " base %x index %x scale %x\n", + x->bi.base, x->bi.index, x->bi.scale); + for (i = 0; i < x->operands; i++) { + fprintf (stdout, " #%d: ", i+1); + pt (x->types[i]); + fprintf (stdout, "\n"); + if (x->types[i] & Reg) fprintf (stdout, "%s\n", x->regs[i]->reg_name); + if (x->types[i] & Imm) pe (x->imms[i]); + if (x->types[i] & (Disp|Abs)) pe (x->disps[i]); + } +} + +static void pte (t) + template *t; +{ + int i; + fprintf (stdout, " %d operands ", t->operands); + fprintf (stdout, "opcode %x ", + t->base_opcode); + if (t->extension_opcode != None) + fprintf (stdout, "ext %x ", t->extension_opcode); + if (t->opcode_modifier&D) + fprintf (stdout, "D"); + if (t->opcode_modifier&W) + fprintf (stdout, "W"); + fprintf (stdout, "\n"); + for (i = 0; i < t->operands; i++) { + fprintf (stdout, " #%d type ", i+1); + pt (t->operand_types[i]); + fprintf (stdout, "\n"); + } +} + +char *seg_names[] = { +"SEG_ABSOLUTE", "SEG_TEXT", "SEG_DATA", "SEG_BSS", "SEG_UNKNOWN", +"SEG_NONE", "SEG_PASS1", "SEG_GOOF", "SEG_BIG", "SEG_DIFFERENCE" }; + +static void pe (e) + expressionS *e; +{ + fprintf (stdout, " segment %s\n", seg_names[(int) e->X_seg]); + fprintf (stdout, " add_number %d (%x)\n", + e->X_add_number, e->X_add_number); + if (e->X_add_symbol) { + fprintf (stdout, " add_symbol "); + ps (e->X_add_symbol); + fprintf (stdout, "\n"); + } + if (e->X_subtract_symbol) { + fprintf (stdout, " sub_symbol "); + ps (e->X_subtract_symbol); + fprintf (stdout, "\n"); + } +} + +#define SYMBOL_TYPE(t) \ + (((t&N_TYPE) == N_UNDF) ? "UNDEFINED" : \ + (((t&N_TYPE) == N_ABS) ? "ABSOLUTE" : \ + (((t&N_TYPE) == N_TEXT) ? "TEXT" : \ + (((t&N_TYPE) == N_DATA) ? "DATA" : \ + (((t&N_TYPE) == N_BSS) ? "BSS" : "Bad n_type!"))))) + +static void ps (s) + symbolS *s; +{ + fprintf (stdout, "%s type %s%s", + s->sy_nlist.n_un.n_name, + (s->sy_nlist.n_type&N_EXT) ? "EXTERNAL " : "", + SYMBOL_TYPE (s->sy_nlist.n_type)); +} + +struct type_name { + uint mask; + char *tname; +} type_names[] = { + { Reg8, "r8" }, { Reg16, "r16" }, { Reg32, "r32" }, { Imm8, "i8" }, + { Imm8S, "i8s" }, + { Imm16, "i16" }, { Imm32, "i32" }, { Mem8, "Mem8"}, { Mem16, "Mem16"}, + { Mem32, "Mem32"}, { BaseIndex, "BaseIndex" }, + { Abs8, "Abs8" }, { Abs16, "Abs16" }, { Abs32, "Abs32" }, + { Disp8, "d8" }, { Disp16, "d16" }, + { Disp32, "d32" }, { SReg2, "SReg2" }, { SReg3, "SReg3" }, { Acc, "Acc" }, + { InOutPortReg, "InOutPortReg" }, { ShiftCount, "ShiftCount" }, + { Imm1, "i1" }, { Control, "control reg" }, {Test, "test reg"}, + { FloatReg, "FReg"}, {FloatAcc, "FAcc"}, + { JumpAbsolute, "Jump Absolute"}, + { 0, "" } +}; + +static void pt (t) + uint t; +{ + register struct type_name *ty; + + if (t == Unknown) { + fprintf (stdout, "Unknown"); + } else { + for (ty = type_names; ty->mask; ty++) + if (t & ty->mask) fprintf (stdout, "%s, ", ty->tname); + } + fflush (stdout); +} + +#endif /* DEBUG386 */ + +/* + This is the guts of the machine-dependent assembler. LINE points to a + machine dependent instruction. This funciton is supposed to emit + the frags/bytes it assembles to. + */ +void md_assemble (line) + char *line; +{ + /* Holds temlate once we've found it. */ + register template * t; + + /* Possible templates for current insn */ + templates *current_templates = (templates *) 0; + + /* Initialize globals. */ + bzero (&i, sizeof(i)); + bzero (disp_expressions, sizeof(disp_expressions)); + bzero (im_expressions, sizeof(im_expressions)); + save_stack_p = save_stack; /* reset stack pointer */ + + /* Fist parse an opcode & call i386_operand for the operands. + We assume that the scrubber has arranged it so that line[0] is the valid + start of a (possibly prefixed) opcode. */ + { + register char *l = line; /* Fast place to put LINE. */ + + /* TRUE if operand is pending after ','. */ + uint expecting_operand = 0; + /* TRUE if we found a prefix only acceptable with string insns. */ + uint expecting_string_instruction = 0; + /* Non-zero if operand parens not balenced. */ + uint paren_not_balenced; + char * token_start = l; + + while (! is_space_char(*l) && *l != END_OF_INSN) { + if (! is_opcode_char(*l)) { + as_bad ("invalid character %s in opcode", output_invalid(*l)); + return; + } else if (*l != PREFIX_SEPERATOR) { + *l = opcode_chars[(unsigned char) *l]; /* fold case of opcodes */ + l++; + } else { /* this opcode's got a prefix */ + register int q; + register prefix_entry * prefix; + + if (l == token_start) { + as_bad ("expecting prefix; got nothing"); + return; + } + END_STRING_AND_SAVE (l); + prefix = (prefix_entry *) hash_find (prefix_hash, token_start); + if (! prefix) { + as_bad ("no such opcode prefix ('%s')", token_start); + return; + } + RESTORE_END_STRING (l); + /* check for repeated prefix */ + for (q = 0; q < i.prefixes; q++) + if (i.prefix[q] == prefix->prefix_code) { + as_bad ("same prefix used twice; you don't really want this!"); + return; + } + if (i.prefixes == MAX_PREFIXES) { + as_bad ("too many opcode prefixes"); + return; + } + i.prefix[i.prefixes++] = prefix->prefix_code; + if (prefix->prefix_code == REPE || prefix->prefix_code == REPNE) + expecting_string_instruction = TRUE; + /* skip past PREFIX_SEPERATOR and reset token_start */ + token_start = ++l; + } + } + END_STRING_AND_SAVE (l); + if (token_start == l) { + as_bad ("expecting opcode; got nothing"); + return; + } + + /* Lookup insn in hash; try intel & att naming conventions if appropriate; + that is: we only use the opcode suffix 'b' 'w' or 'l' if we need to. */ + current_templates = (templates *) hash_find (op_hash, token_start); + if (! current_templates) { + int last_index = strlen(token_start) - 1; + char last_char = token_start[last_index]; + switch (last_char) { + case DWORD_OPCODE_SUFFIX: + case WORD_OPCODE_SUFFIX: + case BYTE_OPCODE_SUFFIX: + token_start[last_index] = '\0'; + current_templates = (templates *) hash_find (op_hash, token_start); + token_start[last_index] = last_char; + i.suffix = last_char; + } + if (!current_templates) { + as_bad ("no such 386 instruction: `%s'", token_start); return; + } + } + RESTORE_END_STRING (l); + + /* check for rep/repne without a string instruction */ + if (expecting_string_instruction && + ! IS_STRING_INSTRUCTION (current_templates-> + start->base_opcode)) { + as_bad ("expecting string instruction after rep/repne"); + return; + } + + /* There may be operands to parse. */ + if (*l != END_OF_INSN && + /* For string instructions, we ignore any operands if given. This + kludges, for example, 'rep/movsb %ds:(%esi), %es:(%edi)' where + the operands are always going to be the same, and are not really + encoded in machine code. */ + ! IS_STRING_INSTRUCTION (current_templates-> + start->base_opcode)) { + /* parse operands */ + do { + /* skip optional white space before operand */ + while (! is_operand_char(*l) && *l != END_OF_INSN) { + if (! is_space_char(*l)) { + as_bad ("invalid character %s before %s operand", + output_invalid(*l), + ordinal_names[i.operands]); + return; + } + l++; + } + token_start = l; /* after white space */ + paren_not_balenced = 0; + while (paren_not_balenced || *l != ',') { + if (*l == END_OF_INSN) { + if (paren_not_balenced) { + as_bad ("unbalenced parenthesis in %s operand.", + ordinal_names[i.operands]); + return; + } else break; /* we are done */ + } else if (! is_operand_char(*l)) { + as_bad ("invalid character %s in %s operand", + output_invalid(*l), + ordinal_names[i.operands]); + return; + } + if (*l == '(') ++paren_not_balenced; + if (*l == ')') --paren_not_balenced; + l++; + } + if (l != token_start) { /* yes, we've read in another operand */ + uint operand_ok; + this_operand = i.operands++; + if (i.operands > MAX_OPERANDS) { + as_bad ("spurious operands; (%d operands/instruction max)", + MAX_OPERANDS); + return; + } + /* now parse operand adding info to 'i' as we go along */ + END_STRING_AND_SAVE (l); + operand_ok = i386_operand (token_start); + RESTORE_END_STRING (l); /* restore old contents */ + if (!operand_ok) return; + } else { + if (expecting_operand) { + expecting_operand_after_comma: + as_bad ("expecting operand after ','; got nothing"); + return; + } + if (*l == ',') { + as_bad ("expecting operand before ','; got nothing"); + return; + } + } + + /* now *l must be either ',' or END_OF_INSN */ + if (*l == ',') { + if (*++l == END_OF_INSN) { /* just skip it, if it's \n complain */ + goto expecting_operand_after_comma; + } + expecting_operand = TRUE; + } + } while (*l != END_OF_INSN); /* until we get end of insn */ + } + } + + /* Now we've parsed the opcode into a set of templates, and have the + operands at hand. + Next, we find a template that matches the given insn, + making sure the overlap of the given operands types is consistent + with the template operand types. */ + +#define MATCH(overlap,given_type) \ + (overlap && \ + (overlap & (JumpAbsolute|BaseIndex|Mem8)) \ + == (given_type & (JumpAbsolute|BaseIndex|Mem8))) + + /* If m0 and m1 are register matches they must be consistent + with the expected operand types t0 and t1. + That is, if both m0 & m1 are register matches + i.e. ( ((m0 & (Reg)) && (m1 & (Reg)) ) ? + then, either 1. or 2. must be true: + 1. the expected operand type register overlap is null: + (t0 & t1 & Reg) == 0 + AND + the given register overlap is null: + (m0 & m1 & Reg) == 0 + 2. the expected operand type register overlap == the given + operand type overlap: (t0 & t1 & m0 & m1 & Reg). + */ +#define CONSISTENT_REGISTER_MATCH(m0, m1, t0, t1) \ + ( ((m0 & (Reg)) && (m1 & (Reg))) ? \ + ( ((t0 & t1 & (Reg)) == 0 && (m0 & m1 & (Reg)) == 0) || \ + ((t0 & t1) & (m0 & m1) & (Reg)) \ + ) : 1) + { + register uint overlap0, overlap1; + expressionS * exp; + uint overlap2; + uint found_reverse_match; + + overlap0 = overlap1 = overlap2 = found_reverse_match = 0; + for (t = current_templates->start; + t < current_templates->end; + t++) { + + /* must have right number of operands */ + if (i.operands != t->operands) continue; + else if (!t->operands) break; /* 0 operands always matches */ + + overlap0 = i.types[0] & t->operand_types[0]; + switch (t->operands) { + case 1: + if (! MATCH (overlap0,i.types[0])) continue; + break; + case 2: case 3: + overlap1 = i.types[1] & t->operand_types[1]; + if (! MATCH (overlap0,i.types[0]) || + ! MATCH (overlap1,i.types[1]) || + ! CONSISTENT_REGISTER_MATCH(overlap0, overlap1, + t->operand_types[0], + t->operand_types[1])) { + + /* check if other direction is valid ... */ + if (! (t->opcode_modifier & COMES_IN_BOTH_DIRECTIONS)) + continue; + + /* try reversing direction of operands */ + overlap0 = i.types[0] & t->operand_types[1]; + overlap1 = i.types[1] & t->operand_types[0]; + if (! MATCH (overlap0,i.types[0]) || + ! MATCH (overlap1,i.types[1]) || + ! CONSISTENT_REGISTER_MATCH (overlap0, overlap1, + t->operand_types[0], + t->operand_types[1])) { + /* does not match either direction */ + continue; + } + /* found a reverse match here -- slip through */ + /* found_reverse_match holds which of D or FloatD we've found */ + found_reverse_match = t->opcode_modifier & COMES_IN_BOTH_DIRECTIONS; + } /* endif: not forward match */ + /* found either forward/reverse 2 operand match here */ + if (t->operands == 3) { + overlap2 = i.types[2] & t->operand_types[2]; + if (! MATCH (overlap2,i.types[2]) || + ! CONSISTENT_REGISTER_MATCH (overlap0, overlap2, + t->operand_types[0], + t->operand_types[2]) || + ! CONSISTENT_REGISTER_MATCH (overlap1, overlap2, + t->operand_types[1], + t->operand_types[2])) + continue; + } + /* found either forward/reverse 2 or 3 operand match here: + slip through to break */ + } + break; /* we've found a match; break out of loop */ + } /* for (t = ... */ + if (t == current_templates->end) { /* we found no match */ + as_bad ("operands given don't match any known 386 instruction"); + return; + } + + /* Copy the template we found (we may change it!). */ + bcopy (t, &i.tm, sizeof (template)); + t = &i.tm; /* alter new copy of template */ + + /* If there's no opcode suffix we try to invent one based on register + operands. */ + if (! i.suffix && i.reg_operands) { + /* We take i.suffix from the LAST register operand specified. This + assumes that the last register operands is the destination register + operand. */ + int o; + for (o = 0; o < MAX_OPERANDS; o++) + if (i.types[o] & Reg) { + i.suffix = (i.types[o] == Reg8) ? BYTE_OPCODE_SUFFIX : + (i.types[o] == Reg16) ? WORD_OPCODE_SUFFIX : + DWORD_OPCODE_SUFFIX; + } + } + + /* Make still unresolved immediate matches conform to size of immediate + given in i.suffix. Note: overlap2 cannot be an immediate! + We assume this. */ + if ((overlap0 & (Imm8|Imm8S|Imm16|Imm32)) + && overlap0 != Imm8 && overlap0 != Imm8S + && overlap0 != Imm16 && overlap0 != Imm32) { + if (! i.suffix) { + as_bad ("no opcode suffix given; can't determine immediate size"); + return; + } + overlap0 &= (i.suffix == BYTE_OPCODE_SUFFIX ? (Imm8|Imm8S) : + (i.suffix == WORD_OPCODE_SUFFIX ? Imm16 : Imm32)); + } + if ((overlap1 & (Imm8|Imm8S|Imm16|Imm32)) + && overlap1 != Imm8 && overlap1 != Imm8S + && overlap1 != Imm16 && overlap1 != Imm32) { + if (! i.suffix) { + as_bad ("no opcode suffix given; can't determine immediate size"); + return; + } + overlap1 &= (i.suffix == BYTE_OPCODE_SUFFIX ? (Imm8|Imm8S) : + (i.suffix == WORD_OPCODE_SUFFIX ? Imm16 : Imm32)); + } + + i.types[0] = overlap0; + i.types[1] = overlap1; + i.types[2] = overlap2; + + if (overlap0 & ImplicitRegister) i.reg_operands--; + if (overlap1 & ImplicitRegister) i.reg_operands--; + if (overlap2 & ImplicitRegister) i.reg_operands--; + if (overlap0 & Imm1) i.imm_operands = 0; /* kludge for shift insns */ + + if (found_reverse_match) { + uint save; + save = t->operand_types[0]; + t->operand_types[0] = t->operand_types[1]; + t->operand_types[1] = save; + } + + /* Finalize opcode. First, we change the opcode based on the operand + size given by i.suffix: we never have to change things for byte insns, + or when no opcode suffix is need to size the operands. */ + + if (! i.suffix && (t->opcode_modifier & W)) { + as_bad ("no opcode suffix given and no register operands; can't size instruction"); + return; + } + + if (i.suffix && i.suffix != BYTE_OPCODE_SUFFIX) { + /* Select between byte and word/dword operations. */ + if (t->opcode_modifier & W) + t->base_opcode |= W; + /* Now select between word & dword operations via the + operand size prefix. */ + if (i.suffix == WORD_OPCODE_SUFFIX) { + if (i.prefixes == MAX_PREFIXES) { + as_bad ("%d prefixes given and 'w' opcode suffix gives too many prefixes", + MAX_PREFIXES); + return; + } + i.prefix[i.prefixes++] = WORD_PREFIX_OPCODE; + } + } + + /* For insns with operands there are more diddles to do to the opcode. */ + if (i.operands) { + /* If we found a reverse match we must alter the opcode direction bit + found_reverse_match holds bit to set (different for int & + float insns). */ + + if (found_reverse_match) { + t->base_opcode |= found_reverse_match; + } + + /* + The imul $imm, %reg instruction is converted into + imul $imm, %reg, %reg. */ + if (t->opcode_modifier & imulKludge) { + i.regs[2] = i.regs[1]; /* Pretend we saw the 3 operand case. */ + i.reg_operands = 2; + } + + /* Certain instructions expect the destination to be in the i.rm.reg + field. This is by far the exceptional case. For these instructions, + if the source operand is a register, we must reverse the i.rm.reg + and i.rm.regmem fields. We accomplish this by faking that the + two register operands were given in the reverse order. */ + if ((t->opcode_modifier & ReverseRegRegmem) && i.reg_operands == 2) { + uint first_reg_operand = (i.types[0] & Reg) ? 0 : 1; + uint second_reg_operand = first_reg_operand + 1; + reg_entry *tmp = i.regs[first_reg_operand]; + i.regs[first_reg_operand] = i.regs[second_reg_operand]; + i.regs[second_reg_operand] = tmp; + } + + if (t->opcode_modifier & ShortForm) { + /* The register or float register operand is in operand 0 or 1. */ + uint o = (i.types[0] & (Reg|FloatReg)) ? 0 : 1; + /* Register goes in low 3 bits of opcode. */ + t->base_opcode |= i.regs[o]->reg_num; + } else if (t->opcode_modifier & ShortFormW) { + /* Short form with 0x8 width bit. Register is always dest. operand */ + t->base_opcode |= i.regs[1]->reg_num; + if (i.suffix == WORD_OPCODE_SUFFIX || + i.suffix == DWORD_OPCODE_SUFFIX) + t->base_opcode |= 0x8; + } else if (t->opcode_modifier & Seg2ShortForm) { + if (t->base_opcode == POP_SEG_SHORT && i.regs[0]->reg_num == 1) { + as_bad ("you can't 'pop cs' on the 386."); + return; + } + t->base_opcode |= (i.regs[0]->reg_num << 3); + } else if (t->opcode_modifier & Seg3ShortForm) { + /* 'push %fs' is 0x0fa0; 'pop %fs' is 0x0fa1. + 'push %gs' is 0x0fa8; 'pop %fs' is 0x0fa9. + So, only if i.regs[0]->reg_num == 5 (%gs) do we need + to change the opcode. */ + if (i.regs[0]->reg_num == 5) + t->base_opcode |= 0x08; + } else if (t->opcode_modifier & Modrm) { + /* The opcode is completed (modulo t->extension_opcode which must + be put into the modrm byte. + Now, we make the modrm & index base bytes based on all the info + we've collected. */ + + /* i.reg_operands MUST be the number of real register operands; + implicit registers do not count. */ + if (i.reg_operands == 2) { + uint source, dest; + source = (i.types[0] & (Reg|SReg2|SReg3|Control|Debug|Test)) ? 0 : 1; + dest = source + 1; + i.rm.mode = 3; + /* We must be careful to make sure that all segment/control/test/ + debug registers go into the i.rm.reg field (despite the whether + they are source or destination operands). */ + if (i.regs[dest]->reg_type & (SReg2|SReg3|Control|Debug|Test)) { + i.rm.reg = i.regs[dest]->reg_num; + i.rm.regmem = i.regs[source]->reg_num; + } else { + i.rm.reg = i.regs[source]->reg_num; + i.rm.regmem = i.regs[dest]->reg_num; + } + } else { /* if it's not 2 reg operands... */ + if (i.mem_operands) { + uint fake_zero_displacement = FALSE; + uint o = (i.types[0] & Mem) ? 0 : ((i.types[1] & Mem) ? 1 : 2); + + /* Encode memory operand into modrm byte and base index byte. */ + + if (i.base_reg == esp && ! i.index_reg) { + /* (%esp) becomes two byte modrm with no index register. */ + i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING; + i.rm.mode = MODE_FROM_DISP_SIZE (i.types[o]); + i.bi.base = ESP_REG_NUM; + i.bi.index = NO_INDEX_REGISTER; + i.bi.scale = 0; /* Must be zero! */ + } else if (i.base_reg == ebp && !i.index_reg) { + if (! (i.types[o] & Disp)) { + /* Must fake a zero byte displacement. + There is no direct way to code '(%ebp)' directly. */ + fake_zero_displacement = TRUE; + /* fake_zero_displacement code does not set this. */ + i.types[o] |= Disp8; + } + i.rm.mode = MODE_FROM_DISP_SIZE (i.types[o]); + i.rm.regmem = EBP_REG_NUM; + } else if (! i.base_reg && (i.types[o] & BaseIndex)) { + /* There are three cases here. + Case 1: '<32bit disp>(,1)' -- indirect absolute. + (Same as cases 2 & 3 with NO index register) + Case 2: <32bit disp> (,) -- no base register with disp + Case 3: (, ) --- no base register; + no disp (must add 32bit 0 disp). */ + i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING; + i.rm.mode = 0; /* 32bit mode */ + i.bi.base = NO_BASE_REGISTER; + i.types[o] &= ~Disp; + i.types[o] |= Disp32; /* Must be 32bit! */ + if (i.index_reg) { /* case 2 or case 3 */ + i.bi.index = i.index_reg->reg_num; + i.bi.scale = i.log2_scale_factor; + if (i.disp_operands == 0) + fake_zero_displacement = TRUE; /* case 3 */ + } else { + i.bi.index = NO_INDEX_REGISTER; + i.bi.scale = 0; + } + } else if (i.disp_operands && !i.base_reg && !i.index_reg) { + /* Operand is just <32bit disp> */ + i.rm.regmem = EBP_REG_NUM; + i.rm.mode = 0; + i.types[o] &= ~Disp; + i.types[o] |= Disp32; + } else { + /* It's not a special case; rev'em up. */ + i.rm.regmem = i.base_reg->reg_num; + i.rm.mode = MODE_FROM_DISP_SIZE (i.types[o]); + if (i.index_reg) { + i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING; + i.bi.base = i.base_reg->reg_num; + i.bi.index = i.index_reg->reg_num; + i.bi.scale = i.log2_scale_factor; + if (i.base_reg == ebp && i.disp_operands == 0) { /* pace */ + fake_zero_displacement = TRUE; + i.types[o] |= Disp8; + i.rm.mode = MODE_FROM_DISP_SIZE (i.types[o]); + } + } + } + if (fake_zero_displacement) { + /* Fakes a zero displacement assuming that i.types[o] holds + the correct displacement size. */ + exp = &disp_expressions[i.disp_operands++]; + i.disps[o] = exp; + exp->X_seg = SEG_ABSOLUTE; + exp->X_add_number = 0; + exp->X_add_symbol = (symbolS *) 0; + exp->X_subtract_symbol = (symbolS *) 0; + } + + /* Select the correct segment for the memory operand. */ + if (i.seg) { + uint seg_index; + seg_entry * default_seg; + + if (i.rm.regmem == ESCAPE_TO_TWO_BYTE_ADDRESSING) { + seg_index = (i.rm.mode<<3) | i.bi.base; + default_seg = two_byte_segment_defaults [seg_index]; + } else { + seg_index = (i.rm.mode<<3) | i.rm.regmem; + default_seg = one_byte_segment_defaults [seg_index]; + } + /* If the specified segment is not the default, use an + opcode prefix to select it */ + if (i.seg != default_seg) { + if (i.prefixes == MAX_PREFIXES) { + as_bad ("%d prefixes given and %s segment override gives too many prefixes", + MAX_PREFIXES, i.seg->seg_name); + return; + } + i.prefix[i.prefixes++] = i.seg->seg_prefix; + } + } + } + + /* Fill in i.rm.reg or i.rm.regmem field with register operand + (if any) based on t->extension_opcode. Again, we must be careful + to make sure that segment/control/debug/test registers are coded + into the i.rm.reg field. */ + if (i.reg_operands) { + uint o = + (i.types[0] & (Reg|SReg2|SReg3|Control|Debug|Test)) ? 0 : + (i.types[1] & (Reg|SReg2|SReg3|Control|Debug|Test)) ? 1 : 2; + /* If there is an extension opcode to put here, the register number + must be put into the regmem field. */ + if (t->extension_opcode != None) + i.rm.regmem = i.regs[o]->reg_num; + else i.rm.reg = i.regs[o]->reg_num; + + /* Now, if no memory operand has set i.rm.mode = 0, 1, 2 + we must set it to 3 to indicate this is a register operand + int the regmem field */ + if (! i.mem_operands) i.rm.mode = 3; + } + + /* Fill in i.rm.reg field with extension opcode (if any). */ + if (t->extension_opcode != None) + i.rm.reg = t->extension_opcode; + } + } + } + } + + /* Handle conversion of 'int $3' --> special int3 insn. */ + if (t->base_opcode == INT_OPCODE && i.imms[0]->X_add_number == 3) { + t->base_opcode = INT3_OPCODE; + i.imm_operands = 0; + } + + /* We are ready to output the insn. */ + { + register char * p; + + /* Output jumps. */ + if (t->opcode_modifier & Jump) { + int n = i.disps[0]->X_add_number; + + switch (i.disps[0]->X_seg) { + case SEG_ABSOLUTE: + if (FITS_IN_SIGNED_BYTE (n)) { + p = frag_more (2); + p[0] = t->base_opcode; + p[1] = n; +#if 0 /* leave out 16 bit jumps - pace */ + } else if (FITS_IN_SIGNED_WORD (n)) { + p = frag_more (4); + p[0] = WORD_PREFIX_OPCODE; + p[1] = t->base_opcode; + md_number_to_chars (&p[2], n, 2); +#endif + } else { /* It's an absolute dword displacement. */ + if (t->base_opcode == JUMP_PC_RELATIVE) { /* pace */ + /* unconditional jump */ + p = frag_more (5); + p[0] = 0xe9; + md_number_to_chars (&p[1], n, 4); + } else { + /* conditional jump */ + p = frag_more (6); + p[0] = TWO_BYTE_OPCODE_ESCAPE; + p[1] = t->base_opcode + 0x10; + md_number_to_chars (&p[2], n, 4); + } + } + break; + default: + /* It's a symbol; end frag & setup for relax. + Make sure there are 6 chars left in the current frag; if not + we'll have to start a new one. */ + /* I caught it failing with obstack_room == 6, + so I changed to <= pace */ + if (obstack_room (&frags) <= 6) { + frag_wane(frag_now); + frag_new (0); + } + p = frag_more (1); + p[0] = t->base_opcode; + frag_var (rs_machine_dependent, + 6, /* 2 opcode/prefix + 4 displacement */ + 1, + ((uchar) *p == JUMP_PC_RELATIVE + ? ENCODE_RELAX_STATE (UNCOND_JUMP, BYTE) + : ENCODE_RELAX_STATE (COND_JUMP, BYTE)), + i.disps[0]->X_add_symbol, + n, p); + break; + } + } else if (t->opcode_modifier & (JumpByte|JumpDword)) { + int size = (t->opcode_modifier & JumpByte) ? 1 : 4; + int n = i.disps[0]->X_add_number; + + if (FITS_IN_UNSIGNED_BYTE(t->base_opcode)) { + FRAG_APPEND_1_CHAR (t->base_opcode); + } else { + p = frag_more (2); /* opcode can be at most two bytes */ + /* put out high byte first: can't use md_number_to_chars! */ + *p++ = (t->base_opcode >> 8) & 0xff; + *p = t->base_opcode & 0xff; + } + + p = frag_more (size); + switch (i.disps[0]->X_seg) { + case SEG_ABSOLUTE: + md_number_to_chars (p, n, size); + if (size == 1 && ! FITS_IN_SIGNED_BYTE (n)) { + as_bad ("loop/jecx only takes byte displacement; %d shortened to %d", + n, *p); + } + break; + default: + fix_new (frag_now, p - frag_now->fr_literal, size, + i.disps[0]->X_add_symbol, i.disps[0]->X_subtract_symbol, + i.disps[0]->X_add_number, 1); + break; + } + } else if (t->opcode_modifier & JumpInterSegment) { + p = frag_more (1 + 2 + 4); /* 1 opcode; 2 segment; 4 offset */ + p[0] = t->base_opcode; + if (i.imms[1]->X_seg == SEG_ABSOLUTE) + md_number_to_chars (p + 1, i.imms[1]->X_add_number, 4); + else + fix_new (frag_now, p + 1 - frag_now->fr_literal, 4, + i.imms[1]->X_add_symbol, + i.imms[1]->X_subtract_symbol, + i.imms[1]->X_add_number, 0); + if (i.imms[0]->X_seg != SEG_ABSOLUTE) + as_bad ("can't handle non absolute segment in long call/jmp"); + md_number_to_chars (p + 5, i.imms[0]->X_add_number, 2); + } else { + /* Output normal instructions here. */ + register char *q; + + /* First the prefix bytes. */ + for (q = i.prefix; q < i.prefix + i.prefixes; q++) { + p = frag_more (1); + md_number_to_chars (p, (uint) *q, 1); + } + + /* Now the opcode; be careful about word order here! */ + if (FITS_IN_UNSIGNED_BYTE(t->base_opcode)) { + FRAG_APPEND_1_CHAR (t->base_opcode); + } else if (FITS_IN_UNSIGNED_WORD(t->base_opcode)) { + p = frag_more (2); + /* put out high byte first: can't use md_number_to_chars! */ + *p++ = (t->base_opcode >> 8) & 0xff; + *p = t->base_opcode & 0xff; + } else { /* opcode is either 3 or 4 bytes */ + if (t->base_opcode & 0xff000000) { + p = frag_more (4); + *p++ = (t->base_opcode >> 24) & 0xff; + } else p = frag_more (3); + *p++ = (t->base_opcode >> 16) & 0xff; + *p++ = (t->base_opcode >> 8) & 0xff; + *p = (t->base_opcode ) & 0xff; + } + + /* Now the modrm byte and base index byte (if present). */ + if (t->opcode_modifier & Modrm) { + p = frag_more (1); + /* md_number_to_chars (p, i.rm, 1); */ + md_number_to_chars (p, (i.rm.regmem<<0 | i.rm.reg<<3 | i.rm.mode<<6), 1); + /* If i.rm.regmem == ESP (4) && i.rm.mode != Mode 3 (Register mode) + ==> need second modrm byte. */ + if (i.rm.regmem == ESCAPE_TO_TWO_BYTE_ADDRESSING && i.rm.mode != 3) { + p = frag_more (1); + /* md_number_to_chars (p, i.bi, 1); */ + md_number_to_chars (p,(i.bi.base<<0 | i.bi.index<<3 | i.bi.scale<<6), 1); + } + } + + if (i.disp_operands) { + register int n; + + for (n = 0; n < i.operands; n++) { + if (i.disps[n]) { + if (i.disps[n]->X_seg == SEG_ABSOLUTE) { + if (i.types[n] & (Disp8|Abs8)) { + p = frag_more (1); + md_number_to_chars (p, i.disps[n]->X_add_number, 1); + } else if (i.types[n] & (Disp16|Abs16)) { + p = frag_more (2); + md_number_to_chars (p, i.disps[n]->X_add_number, 2); + } else { /* Disp32|Abs32 */ + p = frag_more (4); + md_number_to_chars (p, i.disps[n]->X_add_number, 4); + } + } else { /* not SEG_ABSOLUTE */ + /* need a 32-bit fixup (don't support 8bit non-absolute disps) */ + p = frag_more (4); + fix_new (frag_now, p - frag_now->fr_literal, 4, + i.disps[n]->X_add_symbol, i.disps[n]->X_subtract_symbol, + i.disps[n]->X_add_number, 0); + } + } + } + } /* end displacement output */ + + /* output immediate */ + if (i.imm_operands) { + register int n; + + for (n = 0; n < i.operands; n++) { + if (i.imms[n]) { + if (i.imms[n]->X_seg == SEG_ABSOLUTE) { + if (i.types[n] & (Imm8|Imm8S)) { + p = frag_more (1); + md_number_to_chars (p, i.imms[n]->X_add_number, 1); + } else if (i.types[n] & Imm16) { + p = frag_more (2); + md_number_to_chars (p, i.imms[n]->X_add_number, 2); + } else { + p = frag_more (4); + md_number_to_chars (p, i.imms[n]->X_add_number, 4); + } + } else { /* not SEG_ABSOLUTE */ + /* need a 32-bit fixup (don't support 8bit non-absolute ims) */ + /* try to support other sizes ... */ + int size; + if (i.types[n] & (Imm8|Imm8S)) + size = 1; + else if (i.types[n] & Imm16) + size = 2; + else + size = 4; + p = frag_more (size); + fix_new (frag_now, p - frag_now->fr_literal, size, + i.imms[n]->X_add_symbol, i.imms[n]->X_subtract_symbol, + i.imms[n]->X_add_number, 0); + } + } + } + } /* end immediate output */ + } + +#ifdef DEBUG386 + if (flagseen ['D']) { + pi (line, &i); + } +#endif /* DEBUG386 */ + + } + return; +} + +/* Parse OPERAND_STRING into the i386_insn structure I. Returns non-zero + on error. */ + +int i386_operand (operand_string) + char *operand_string; +{ + register char *op_string = operand_string; + + /* Address of '\0' at end of operand_string. */ + char * end_of_operand_string = operand_string + strlen(operand_string); + + /* Start and end of displacement string expression (if found). */ + char * displacement_string_start = 0; + char * displacement_string_end; + + /* We check for an absolute prefix (differentiating, + for example, 'jmp pc_relative_label' from 'jmp *absolute_label'. */ + if (*op_string == ABSOLUTE_PREFIX) { + op_string++; + i.types[this_operand] |= JumpAbsolute; + } + + /* Check if operand is a register. */ + if (*op_string == REGISTER_PREFIX) { + register reg_entry * r; + if (! (r = parse_register (op_string))) { + as_bad ("bad register name ('%s')", op_string); + return 0; + } + /* Check for segment override, rather than segment register by + searching for ':' after %s where = s, c, d, e, f, g. */ + if ((r->reg_type & (SReg2|SReg3)) && op_string[3] == ':') { + switch (r->reg_num) { + case 0: + i.seg = &es; break; + case 1: + i.seg = &cs; break; + case 2: + i.seg = &ss; break; + case 3: + i.seg = &ds; break; + case 4: + i.seg = &fs; break; + case 5: + i.seg = &gs; break; + } + op_string += 4; /* skip % s : */ + operand_string = op_string; /* Pretend given string starts here. */ + if (!is_digit_char(*op_string) && !is_identifier_char(*op_string) + && *op_string != '(' && *op_string != ABSOLUTE_PREFIX) { + as_bad ("bad memory operand after segment override"); + return 0; + } + /* Handle case of %es:*foo. */ + if (*op_string == ABSOLUTE_PREFIX) { + op_string++; + i.types[this_operand] |= JumpAbsolute; + } + goto do_memory_reference; + } + i.types[this_operand] |= r->reg_type; + i.regs[this_operand] = r; + i.reg_operands++; + } else if (*op_string == IMMEDIATE_PREFIX) { /* ... or an immediate */ + char * save_input_line_pointer; + register expressionS *exp; + segT exp_seg; + if (i.imm_operands == MAX_IMMEDIATE_OPERANDS) { + as_bad ("only 1 or 2 immediate operands are allowed"); + return 0; + } + exp = &im_expressions[i.imm_operands++]; + i.imms [this_operand] = exp; + save_input_line_pointer = input_line_pointer; + input_line_pointer = ++op_string; /* must advance op_string! */ + exp_seg = expression (exp); + input_line_pointer = save_input_line_pointer; + switch (exp_seg) { + case SEG_NONE: /* missing or bad expr becomes absolute 0 */ + as_bad ("missing or invalid immediate expression '%s' taken as 0", + operand_string); + exp->X_seg = SEG_ABSOLUTE; + exp->X_add_number = 0; + exp->X_add_symbol = (symbolS *) 0; + exp->X_subtract_symbol = (symbolS *) 0; + i.types[this_operand] |= Imm; + break; + case SEG_ABSOLUTE: + i.types[this_operand] |= SMALLEST_IMM_TYPE (exp->X_add_number); + break; + case SEG_TEXT: case SEG_DATA: case SEG_BSS: case SEG_UNKNOWN: + i.types[this_operand] |= Imm32; /* this is an address ==> 32bit */ + break; + default: +seg_unimplemented: + as_bad ("Unimplemented segment type %d in parse_operand", exp_seg); + return 0; + } + /* shorten this type of this operand if the instruction wants + * fewer bits than are present in the immediate. The bit field + * code can put out 'andb $0xffffff, %al', for example. pace + * also 'movw $foo,(%eax)' + */ + switch (i.suffix) { + case WORD_OPCODE_SUFFIX: + i.types[this_operand] |= Imm16; + break; + case BYTE_OPCODE_SUFFIX: + i.types[this_operand] |= Imm16 | Imm8 | Imm8S; + break; + } + } else if (is_digit_char(*op_string) || is_identifier_char(*op_string) + || *op_string == '(') { + /* This is a memory reference of some sort. */ + register char * base_string; + uint found_base_index_form; + + do_memory_reference: + if (i.mem_operands == MAX_MEMORY_OPERANDS) { + as_bad ("more than 1 memory reference in instruction"); + return 0; + } + i.mem_operands++; + + /* Determine type of memory operand from opcode_suffix; + no opcode suffix implies general memory references. */ + switch (i.suffix) { + case BYTE_OPCODE_SUFFIX: + i.types[this_operand] |= Mem8; + break; + case WORD_OPCODE_SUFFIX: + i.types[this_operand] |= Mem16; + break; + case DWORD_OPCODE_SUFFIX: + default: + i.types[this_operand] |= Mem32; + } + + /* Check for base index form. We detect the base index form by + looking for an ')' at the end of the operand, searching + for the '(' matching it, and finding a REGISTER_PREFIX or ',' + after it. */ + base_string = end_of_operand_string - 1; + found_base_index_form = FALSE; + if (*base_string == ')') { + uint parens_balenced = 1; + /* We've already checked that the number of left & right ()'s are equal, + so this loop will not be infinite. */ + do { + base_string--; + if (*base_string == ')') parens_balenced++; + if (*base_string == '(') parens_balenced--; + } while (parens_balenced); + base_string++; /* Skip past '('. */ + if (*base_string == REGISTER_PREFIX || *base_string == ',') + found_base_index_form = TRUE; + } + + /* If we can't parse a base index register expression, we've found + a pure displacement expression. We set up displacement_string_start + and displacement_string_end for the code below. */ + if (! found_base_index_form) { + displacement_string_start = op_string; + displacement_string_end = end_of_operand_string; + } else { + char *base_reg_name, *index_reg_name, *num_string; + int num; + + i.types[this_operand] |= BaseIndex; + + /* If there is a displacement set-up for it to be parsed later. */ + if (base_string != op_string + 1) { + displacement_string_start = op_string; + displacement_string_end = base_string - 1; + } + + /* Find base register (if any). */ + if (*base_string != ',') { + base_reg_name = base_string++; + /* skip past register name & parse it */ + while (isalpha(*base_string)) base_string++; + if (base_string == base_reg_name+1) { + as_bad ("can't find base register name after '(%c'", + REGISTER_PREFIX); + return 0; + } + END_STRING_AND_SAVE (base_string); + if (! (i.base_reg = parse_register (base_reg_name))) { + as_bad ("bad base register name ('%s')", base_reg_name); + return 0; + } + RESTORE_END_STRING (base_string); + } + + /* Now check seperator; must be ',' ==> index reg + OR num ==> no index reg. just scale factor + OR ')' ==> end. (scale factor = 1) */ + if (*base_string != ',' && *base_string != ')') { + as_bad ("expecting ',' or ')' after base register in `%s'", + operand_string); + return 0; + } + + /* There may index reg here; and there may be a scale factor. */ + if (*base_string == ',' && *(base_string+1) == REGISTER_PREFIX) { + index_reg_name = ++base_string; + while (isalpha(*++base_string)); + END_STRING_AND_SAVE (base_string); + if (! (i.index_reg = parse_register(index_reg_name))) { + as_bad ("bad index register name ('%s')", index_reg_name); + return 0; + } + RESTORE_END_STRING (base_string); + } + + /* Check for scale factor. */ + if (*base_string == ',' && isdigit(*(base_string+1))) { + num_string = ++base_string; + while (is_digit_char(*base_string)) base_string++; + if (base_string == num_string) { + as_bad ("can't find a scale factor after ','"); + return 0; + } + END_STRING_AND_SAVE (base_string); + /* We've got a scale factor. */ + if (! sscanf (num_string, "%d", &num)) { + as_bad ("can't parse scale factor from '%s'", num_string); + return 0; + } + RESTORE_END_STRING (base_string); + switch (num) { /* must be 1 digit scale */ + case 1: i.log2_scale_factor = 0; break; + case 2: i.log2_scale_factor = 1; break; + case 4: i.log2_scale_factor = 2; break; + case 8: i.log2_scale_factor = 3; break; + default: + as_bad ("expecting scale factor of 1, 2, 4, 8; got %d", num); + return 0; + } + } else { + if (! i.index_reg && *base_string == ',') { + as_bad ("expecting index register or scale factor after ','; got '%c'", + *(base_string+1)); + return 0; + } + } + } + + /* If there's an expression begining the operand, parse it, + assuming displacement_string_start and displacement_string_end + are meaningful. */ + if (displacement_string_start) { + register expressionS * exp; + segT exp_seg; + char * save_input_line_pointer; + exp = &disp_expressions[i.disp_operands]; + i.disps [this_operand] = exp; + i.disp_operands++; + save_input_line_pointer = input_line_pointer; + input_line_pointer = displacement_string_start; + END_STRING_AND_SAVE (displacement_string_end); + exp_seg = expression (exp); + if(*input_line_pointer) + as_bad("Ignoring junk '%s' after expression",input_line_pointer); + RESTORE_END_STRING (displacement_string_end); + input_line_pointer = save_input_line_pointer; + switch (exp_seg) { + case SEG_NONE: + /* missing expr becomes absolute 0 */ + as_bad ("missing or invalid displacement '%s' taken as 0", + operand_string); + i.types[this_operand] |= (Disp|Abs); + exp->X_seg = SEG_ABSOLUTE; + exp->X_add_number = 0; + exp->X_add_symbol = (symbolS *) 0; + exp->X_subtract_symbol = (symbolS *) 0; + break; + case SEG_ABSOLUTE: + i.types[this_operand] |= SMALLEST_DISP_TYPE (exp->X_add_number); + break; + case SEG_TEXT: case SEG_DATA: case SEG_BSS: + case SEG_UNKNOWN: /* must be 32 bit displacement (i.e. address) */ + i.types[this_operand] |= Disp32; + break; + default: + goto seg_unimplemented; + } + } + + /* Make sure the memory operand we've been dealt is valid. */ + if (i.base_reg && i.index_reg && + ! (i.base_reg->reg_type & i.index_reg->reg_type & Reg)) { + as_bad ("register size mismatch in (base,index,scale) expression"); + return 0; + } + if ((i.base_reg && (i.base_reg->reg_type & Reg32) == 0) || + (i.index_reg && (i.index_reg->reg_type & Reg32) == 0)) { + as_bad ("base/index register must be 32 bit register"); + return 0; + } + if (i.index_reg && i.index_reg == esp) { + as_bad ("%s may not be used as an index register", esp->reg_name); + return 0; + } + } else { /* it's not a memory operand; argh! */ + as_bad ("invalid char %s begining %s operand '%s'", + output_invalid(*op_string), ordinal_names[this_operand], + op_string); + return 0; + } + return 1; /* normal return */ +} + +/* + * md_estimate_size_before_relax() + * + * Called just before relax(). + * Any symbol that is now undefined will not become defined. + * Return the correct fr_subtype in the frag. + * Return the initial "guess for fr_var" to caller. + * The guess for fr_var is ACTUALLY the growth beyond fr_fix. + * Whatever we do to grow fr_fix or fr_var contributes to our returned value. + * Although it may not be explicit in the frag, pretend fr_var starts with a + * 0 value. + */ +int +md_estimate_size_before_relax (fragP, segment_type) + register fragS * fragP; + register int segment_type; /* N_DATA or N_TEXT. */ +{ + register uchar * opcode; + register int old_fr_fix; + + old_fr_fix = fragP -> fr_fix; + opcode = (uchar *) fragP -> fr_opcode; + /* We've already got fragP->fr_subtype right; all we have to do is check + for un-relaxable symbols. */ + if ((fragP -> fr_symbol -> sy_type & N_TYPE) != segment_type) { + /* symbol is undefined in this segment */ + switch (opcode[0]) { + case JUMP_PC_RELATIVE: /* make jmp (0xeb) a dword displacement jump */ + opcode[0] = 0xe9; /* dword disp jmp */ + fragP -> fr_fix += 4; + fix_new (fragP, old_fr_fix, 4, + fragP -> fr_symbol, + (symbolS *) 0, + fragP -> fr_offset, 1); + break; + + default: + /* This changes the byte-displacement jump 0x7N --> + the dword-displacement jump 0x0f8N */ + opcode[1] = opcode[0] + 0x10; + opcode[0] = TWO_BYTE_OPCODE_ESCAPE; /* two-byte escape */ + fragP -> fr_fix += 1 + 4; /* we've added an opcode byte */ + fix_new (fragP, old_fr_fix + 1, 4, + fragP -> fr_symbol, + (symbolS *) 0, + fragP -> fr_offset, 1); + break; + } + frag_wane (fragP); + } + return (fragP -> fr_var + fragP -> fr_fix - old_fr_fix); +} /* md_estimate_size_before_relax() */ + +/* + * md_convert_frag(); + * + * Called after relax() is finished. + * In: Address of frag. + * fr_type == rs_machine_dependent. + * fr_subtype is what the address relaxed to. + * + * Out: Any fixSs and constants are set up. + * Caller will turn frag into a ".space 0". + */ +void +md_convert_frag (fragP) + register fragS * fragP; +{ + register uchar * opcode; + uchar * where_to_put_displacement; + uint target_address, opcode_address; + uint extension; + int displacement_from_opcode_start; + + opcode = (uchar *) fragP -> fr_opcode; + + /* Address we want to reach in file space. */ + target_address = fragP->fr_symbol->sy_value + fragP->fr_offset; + + /* Address opcode resides at in file space. */ + opcode_address = fragP->fr_address + fragP->fr_fix; + + /* Displacement from opcode start to fill into instruction. */ + displacement_from_opcode_start = target_address - opcode_address; + + switch (fragP->fr_subtype) { + case ENCODE_RELAX_STATE (COND_JUMP, BYTE): + case ENCODE_RELAX_STATE (UNCOND_JUMP, BYTE): + /* don't have to change opcode */ + extension = 1; /* 1 opcode + 1 displacement */ + where_to_put_displacement = &opcode[1]; + break; + + case ENCODE_RELAX_STATE (COND_JUMP, WORD): + opcode[1] = TWO_BYTE_OPCODE_ESCAPE; + opcode[2] = opcode[0] + 0x10; + opcode[0] = WORD_PREFIX_OPCODE; + extension = 4; /* 3 opcode + 2 displacement */ + where_to_put_displacement = &opcode[3]; + break; + + case ENCODE_RELAX_STATE (UNCOND_JUMP, WORD): + opcode[1] = 0xe9; + opcode[0] = WORD_PREFIX_OPCODE; + extension = 3; /* 2 opcode + 2 displacement */ + where_to_put_displacement = &opcode[2]; + break; + + case ENCODE_RELAX_STATE (COND_JUMP, DWORD): + opcode[1] = opcode[0] + 0x10; + opcode[0] = TWO_BYTE_OPCODE_ESCAPE; + extension = 5; /* 2 opcode + 4 displacement */ + where_to_put_displacement = &opcode[2]; + break; + + case ENCODE_RELAX_STATE (UNCOND_JUMP, DWORD): + opcode[0] = 0xe9; + extension = 4; /* 1 opcode + 4 displacement */ + where_to_put_displacement = &opcode[1]; + break; + + default: + BAD_CASE(fragP -> fr_subtype); + break; + } + /* now put displacement after opcode */ + md_number_to_chars (where_to_put_displacement, + displacement_from_opcode_start - extension, + SIZE_FROM_RELAX_STATE (fragP->fr_subtype)); + fragP -> fr_fix += extension; +} + + +int md_short_jump_size = 2; /* size of byte displacement jmp */ +int md_long_jump_size = 5; /* size of dword displacement jmp */ + +void md_create_short_jump(ptr, from_addr, to_addr) + char *ptr; + long from_addr, to_addr; +{ + long offset; + + offset = to_addr - (from_addr + 2); + md_number_to_chars (ptr, (long) 0xeb, 1); /* opcode for byte-disp jump */ + md_number_to_chars (ptr + 1, offset, 1); +} + +void md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol) + char *ptr; + long from_addr, to_addr; + fragS *frag; + symbolS *to_symbol; +{ + long offset; + + if (flagseen['m']) { + offset = to_addr - to_symbol->sy_value; + md_number_to_chars (ptr, 0xe9, 1); /* opcode for long jmp */ + md_number_to_chars (ptr + 1, offset, 4); + fix_new (frag, (ptr+1) - frag->fr_literal, 4, + to_symbol, (symbolS *) 0, (long int) 0, 0); + } else { + offset = to_addr - (from_addr + 5); + md_number_to_chars(ptr, (long) 0xe9, 1); + md_number_to_chars(ptr + 1, offset, 4); + } +} + +int +md_parse_option(argP,cntP,vecP) +char **argP; +int *cntP; +char ***vecP; +{ + return 1; +} + +void /* Knows about order of bytes in address. */ +md_number_to_chars (con, value, nbytes) + char con []; /* Return 'nbytes' of chars here. */ + long int value; /* The value of the bits. */ + int nbytes; /* Number of bytes in the output. */ +{ + register char * p = con; + + switch (nbytes) { + case 1: + p[0] = value & 0xff; + break; + case 2: + p[0] = value & 0xff; + p[1] = (value >> 8) & 0xff; + break; + case 4: + p[0] = value & 0xff; + p[1] = (value>>8) & 0xff; + p[2] = (value>>16) & 0xff; + p[3] = (value>>24) & 0xff; + break; + default: + BAD_CASE (nbytes); + } +} + +void /* Knows about order of bytes in address. */ +md_number_to_disp (con, value, nbytes) + char con []; /* Return 'nbytes' of chars here. */ + long int value; /* The value of the bits. */ + int nbytes; /* Number of bytes in the output. */ +{ + char * answer = alloca (nbytes); + register char * p = answer; + + switch (nbytes) { + case 1: + *p = value; + break; + case 2: + *p++ = value; + *p = (value>>8); + break; + case 4: + *p++ = value; + *p++ = (value>>8); + *p++ = (value>>16); + *p = (value>>24); + break; + default: + BAD_CASE (nbytes); + } + bcopy (answer, con, nbytes); +} + +void /* Knows about order of bytes in address. */ +md_number_to_imm (con, value, nbytes) + char con []; /* Return 'nbytes' of chars here. */ + long int value; /* The value of the bits. */ + int nbytes; /* Number of bytes in the output. */ +{ + char * answer = alloca (nbytes); + register char * p = answer; + + switch (nbytes) { + case 1: + *p = value; + break; + case 2: + *p++ = value; + *p = (value>>8); + break; + case 4: + *p++ = value; + *p++ = (value>>8); + *p++ = (value>>16); + *p = (value>>24); + break; + default: + BAD_CASE (nbytes); + } + bcopy (answer, con, nbytes); +} + +void /* Knows about order of bytes in address. */ +md_number_to_field (con, value, nbytes) + char con []; /* Return 'nbytes' of chars here. */ + long int value; /* The value of the bits. */ + int nbytes; /* Number of bytes in the output. */ +{ + char * answer = alloca (nbytes); + register char * p = answer; + + switch (nbytes) { + case 1: + *p = value; + break; + case 2: + *p++ = value; + *p = (value>>8); + break; + case 4: + *p++ = value; + *p++ = (value>>8); + *p++ = (value>>16); + *p = (value>>24); + break; + default: + BAD_CASE (nbytes); + } + bcopy (answer, con, nbytes); +} + +long int /* Knows about the byte order in a word. */ +md_chars_to_number (con, nbytes) +unsigned char con[]; /* Low order byte 1st. */ + int nbytes; /* Number of bytes in the input. */ +{ + long int retval; + for (retval=0, con+=nbytes-1; nbytes--; con--) + { + retval <<= BITS_PER_CHAR; + retval |= *con; + } + return retval; +} + +void md_ri_to_chars(ri_p, ri) + struct relocation_info *ri_p, ri; +{ + unsigned char the_bytes[8]; + + /* this is easy */ + md_number_to_chars(the_bytes, ri.r_address, sizeof(ri.r_address)); + /* now the fun stuff */ + the_bytes[6] = (ri.r_symbolnum >> 16) & 0x0ff; + the_bytes[5] = (ri.r_symbolnum >> 8) & 0x0ff; + the_bytes[4] = ri.r_symbolnum & 0x0ff; + the_bytes[7] = (((ri.r_extern << 3) & 0x08) | ((ri.r_length << 1) & 0x06) | + ((ri.r_pcrel << 0) & 0x01)) & 0x0F; + /* now put it back where you found it */ + bcopy (the_bytes, (char *)ri_p, sizeof(struct relocation_info)); +} + + +#define MAX_LITTLENUMS 6 + +/* Turn the string pointed to by litP into a floating point constant of type + type, and emit the appropriate bytes. The number of LITTLENUMS emitted + is stored in *sizeP . An error message is returned, or NULL on OK. + */ +char * +md_atof(type,litP,sizeP) + char type; + char *litP; + int *sizeP; +{ + int prec; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + LITTLENUM_TYPE *wordP; + char *t; + char *atof_ieee(); + + switch(type) { + case 'f': + case 'F': + prec = 2; + break; + + case 'd': + case 'D': + prec = 4; + break; + + case 'x': + case 'X': + prec = 5; + break; + + default: + *sizeP=0; + return "Bad call to md_atof ()"; + } + t = atof_ieee (input_line_pointer,type,words); + if(t) + input_line_pointer=t; + + *sizeP = prec * sizeof(LITTLENUM_TYPE); + /* this loops outputs the LITTLENUMs in REVERSE order; in accord with + the bigendian 386 */ + for(wordP = words + prec - 1;prec--;) { + md_number_to_chars (litP, (long) (*wordP--), sizeof(LITTLENUM_TYPE)); + litP += sizeof(LITTLENUM_TYPE); + } + return ""; /* Someone should teach Dean about null pointers */ +} + +char output_invalid_buf[8]; + +char * output_invalid (c) + char c; +{ + if (isprint(c)) sprintf (output_invalid_buf, "'%c'", c); + else sprintf (output_invalid_buf, "(0x%x)", c); + return output_invalid_buf; +} + +reg_entry *parse_register (reg_string) + char *reg_string; /* reg_string starts *before* REGISTER_PREFIX */ +{ + register char *s = reg_string; + register char *p; + char reg_name_given[MAX_REG_NAME_SIZE]; + + s++; /* skip REGISTER_PREFIX */ + for (p = reg_name_given; is_register_char (*s); p++, s++) { + *p = register_chars [*s]; + if (p >= reg_name_given + MAX_REG_NAME_SIZE) + return (reg_entry *) 0; + } + *p = '\0'; + return (reg_entry *) hash_find (reg_hash, reg_name_given); +} + diff --git a/gnu/usr.bin/as/config/i386.h b/gnu/usr.bin/as/config/i386.h new file mode 100644 index 0000000000..c569c1cb8c --- /dev/null +++ b/gnu/usr.bin/as/config/i386.h @@ -0,0 +1,296 @@ +/* i386.h -- Header file for i386.c + Copyright (C) 1989, Free Software Foundation. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#define MAX_OPERANDS 3 /* max operands per insn */ +#define MAX_PREFIXES 4 /* max prefixes per opcode */ +#define MAX_IMMEDIATE_OPERANDS 2 /* max immediates per insn */ +#define MAX_MEMORY_OPERANDS 2 /* max memory ref per insn + * lcall uses 2 + */ +/* we define the syntax here (modulo base,index,scale syntax) */ +#define REGISTER_PREFIX '%' +#define IMMEDIATE_PREFIX '$' +#define ABSOLUTE_PREFIX '*' +#define PREFIX_SEPERATOR '/' + +#define TWO_BYTE_OPCODE_ESCAPE 0x0f + +/* register numbers */ +#define EBP_REG_NUM 5 +#define ESP_REG_NUM 4 + +/* modrm_byte.regmem for twobyte escape */ +#define ESCAPE_TO_TWO_BYTE_ADDRESSING ESP_REG_NUM +/* index_base_byte.index for no index register addressing */ +#define NO_INDEX_REGISTER ESP_REG_NUM +/* index_base_byte.base for no base register addressing */ +#define NO_BASE_REGISTER EBP_REG_NUM + +/* these are the att as opcode suffixes, making movl --> mov, for example */ +#define DWORD_OPCODE_SUFFIX 'l' +#define WORD_OPCODE_SUFFIX 'w' +#define BYTE_OPCODE_SUFFIX 'b' + +/* modrm.mode = REGMEM_FIELD_HAS_REG when a register is in there */ +#define REGMEM_FIELD_HAS_REG 0x3 /* always = 0x3 */ +#define REGMEM_FIELD_HAS_MEM (~REGMEM_FIELD_HAS_REG) + +#define END_OF_INSN '\0' + +/* +When an operand is read in it is classified by its type. This type includes +all the possible ways an operand can be used. Thus, '%eax' is both 'register +# 0' and 'The Accumulator'. In our language this is expressed by OR'ing +'Reg32' (any 32 bit register) and 'Acc' (the accumulator). +Operands are classified so that we can match given operand types with +the opcode table in i386-opcode.h. + */ +#define Unknown 0x0 +/* register */ +#define Reg8 0x1 /* 8 bit reg */ +#define Reg16 0x2 /* 16 bit reg */ +#define Reg32 0x4 /* 32 bit reg */ +#define Reg (Reg8|Reg16|Reg32) /* gen'l register */ +#define WordReg (Reg16|Reg32) /* for push/pop operands */ +/* immediate */ +#define Imm8 0x8 /* 8 bit immediate */ +#define Imm8S 0x10 /* 8 bit immediate sign extended */ +#define Imm16 0x20 /* 16 bit immediate */ +#define Imm32 0x40 /* 32 bit immediate */ +#define Imm1 0x80 /* 1 bit immediate */ +#define ImmUnknown Imm32 /* for unknown expressions */ +#define Imm (Imm8|Imm8S|Imm16|Imm32) /* gen'l immediate */ +/* memory */ +#define Disp8 0x200 /* 8 bit displacement (for jumps) */ +#define Disp16 0x400 /* 16 bit displacement */ +#define Disp32 0x800 /* 32 bit displacement */ +#define Disp (Disp8|Disp16|Disp32) /* General displacement */ +#define DispUnknown Disp32 /* for unknown size displacements */ +#define Mem8 0x1000 +#define Mem16 0x2000 +#define Mem32 0x4000 +#define BaseIndex 0x8000 +#define Mem (Disp|Mem8|Mem16|Mem32|BaseIndex) /* General memory */ +#define WordMem (Mem16|Mem32|Disp|BaseIndex) +#define ByteMem (Mem8|Disp|BaseIndex) +/* specials */ +#define InOutPortReg 0x10000 /* register to hold in/out port addr = dx */ +#define ShiftCount 0x20000 /* register to hold shift cound = cl */ +#define Control 0x40000 /* Control register */ +#define Debug 0x80000 /* Debug register */ +#define Test 0x100000 /* Test register */ +#define FloatReg 0x200000 /* Float register */ +#define FloatAcc 0x400000 /* Float stack top %st(0) */ +#define SReg2 0x800000 /* 2 bit segment register */ +#define SReg3 0x1000000 /* 3 bit segment register */ +#define Acc 0x2000000 /* Accumulator %al or %ax or %eax */ +#define ImplicitRegister (InOutPortReg|ShiftCount|Acc|FloatAcc) +#define JumpAbsolute 0x4000000 +#define Abs8 0x08000000 +#define Abs16 0x10000000 +#define Abs32 0x20000000 +#define Abs (Abs8|Abs16|Abs32) + +#define MODE_FROM_DISP_SIZE(t) \ + ((t&(Disp8)) ? 1 : \ + ((t&(Disp32)) ? 2 : 0)) + +#define Byte (Reg8|Imm8|Imm8S) +#define Word (Reg16|Imm16) +#define DWord (Reg32|Imm32) + +/* convert opcode suffix ('b' 'w' 'l' typically) into type specifyer */ +#define OPCODE_SUFFIX_TO_TYPE(s) \ + (s == BYTE_OPCODE_SUFFIX ? Byte : \ + (s == WORD_OPCODE_SUFFIX ? Word : DWord)) + +#define FITS_IN_SIGNED_BYTE(num) ((num) >= -128 && (num) <= 127) +#define FITS_IN_UNSIGNED_BYTE(num) ((num) >= 0 && (num) <= 255) +#define FITS_IN_UNSIGNED_WORD(num) ((num) >= 0 && (num) <= 65535) +#define FITS_IN_SIGNED_WORD(num) ((num) >= -32768 && (num) <= 32767) + +#define SMALLEST_DISP_TYPE(num) \ + FITS_IN_SIGNED_BYTE(num) ? (Disp8|Disp32|Abs8|Abs32) : (Disp32|Abs32) + +#define SMALLEST_IMM_TYPE(num) \ + (num == 1) ? (Imm1|Imm8|Imm8S|Imm16|Imm32): \ + FITS_IN_SIGNED_BYTE(num) ? (Imm8S|Imm8|Imm16|Imm32) : \ + FITS_IN_UNSIGNED_BYTE(num) ? (Imm8|Imm16|Imm32): \ + (FITS_IN_SIGNED_WORD(num)||FITS_IN_UNSIGNED_WORD(num)) ? (Imm16|Imm32) : \ + (Imm32) + +typedef unsigned char uchar; +typedef unsigned int uint; + +typedef struct { + /* instruction name sans width suffix ("mov" for movl insns) */ + char *name; + + /* how many operands */ + uint operands; + + /* base_opcode is the fundamental opcode byte with a optional prefix(es). */ + uint base_opcode; + + /* extension_opcode is the 3 bit extension for group insns. + If this template has no extension opcode (the usual case) use None */ + uchar extension_opcode; +#define None 0xff /* If no extension_opcode is possible. */ + + /* the bits in opcode_modifier are used to generate the final opcode from + the base_opcode. These bits also are used to detect alternate forms of + the same instruction */ + uint opcode_modifier; + +/* opcode_modifier bits: */ +#define W 0x1 /* set if operands are words or dwords */ +#define D 0x2 /* D = 0 if Reg --> Regmem; D = 1 if Regmem --> Reg */ +/* direction flag for floating insns: MUST BE 0x400 */ +#define FloatD 0x400 +/* shorthand */ +#define DW (D|W) +#define ShortForm 0x10 /* register is in low 3 bits of opcode */ +#define ShortFormW 0x20 /* ShortForm and W bit is 0x8 */ +#define Seg2ShortForm 0x40 /* encoding of load segment reg insns */ +#define Seg3ShortForm 0x80 /* fs/gs segment register insns. */ +#define Jump 0x100 /* special case for jump insns. */ +#define JumpInterSegment 0x200 /* special case for intersegment leaps/calls */ +/* 0x400 CANNOT BE USED since it's already used by FloatD above */ +#define DONT_USE 0x400 +#define NoModrm 0x800 +#define Modrm 0x1000 +#define imulKludge 0x2000 +#define JumpByte 0x4000 +#define JumpDword 0x8000 +#define ReverseRegRegmem 0x10000 + + /* (opcode_modifier & COMES_IN_ALL_SIZES) is true if the + instuction comes in byte, word, and dword sizes and is encoded into + machine code in the canonical way. */ +#define COMES_IN_ALL_SIZES (W) + + /* (opcode_modifier & COMES_IN_BOTH_DIRECTIONS) indicates that the + source and destination operands can be reversed by setting either + the D (for integer insns) or the FloatD (for floating insns) bit + in base_opcode. */ +#define COMES_IN_BOTH_DIRECTIONS (D|FloatD) + + /* operand_types[i] describes the type of operand i. This is made + by OR'ing together all of the possible type masks. (e.g. + 'operand_types[i] = Reg|Imm' specifies that operand i can be + either a register or an immediate operand */ + uint operand_types[3]; +} template; + +/* + 'templates' is for grouping together 'template' structures for opcodes + of the same name. This is only used for storing the insns in the grand + ole hash table of insns. + The templates themselves start at START and range up to (but not including) + END. +*/ +typedef struct { + template *start; + template *end; +} templates; + +/* these are for register name --> number & type hash lookup */ +typedef struct { + char * reg_name; + uint reg_type; + uint reg_num; +} reg_entry; + +typedef struct { + char * seg_name; + uint seg_prefix; +} seg_entry; + +/* these are for prefix name --> prefix code hash lookup */ +typedef struct { + char * prefix_name; + uchar prefix_code; +} prefix_entry; + +/* 386 operand encoding bytes: see 386 book for details of this. */ +typedef struct { + unsigned regmem:3; /* codes register or memory operand */ + unsigned reg:3; /* codes register operand (or extended opcode) */ + unsigned mode:2; /* how to interpret regmem & reg */ +} modrm_byte; + +/* 386 opcode byte to code indirect addressing. */ +typedef struct { + unsigned base:3; + unsigned index:3; + unsigned scale:2; +} base_index_byte; + +/* 'md_assemble ()' gathers together information and puts it into a + i386_insn. */ + +typedef struct { + /* TM holds the template for the insn were currently assembling. */ + template tm; + /* SUFFIX holds the opcode suffix (e.g. 'l' for 'movl') if given. */ + char suffix; + /* Operands are coded with OPERANDS, TYPES, DISPS, IMMS, and REGS. */ + + /* OPERANDS gives the number of given operands. */ + uint operands; + + /* REG_OPERANDS, DISP_OPERANDS, MEM_OPERANDS, IMM_OPERANDS give the number of + given register, displacement, memory operands and immediate operands. */ + uint reg_operands, disp_operands, mem_operands, imm_operands; + + /* TYPES [i] is the type (see above #defines) which tells us how to + search through DISPS [i] & IMMS [i] & REGS [i] for the required + operand. */ + uint types [MAX_OPERANDS]; + + /* Displacements (if given) for each operand. */ + expressionS * disps [MAX_OPERANDS]; + + /* Immediate operands (if given) for each operand. */ + expressionS * imms [MAX_OPERANDS]; + + /* Register operands (if given) for each operand. */ + reg_entry * regs [MAX_OPERANDS]; + + /* BASE_REG, INDEX_REG, and LOG2_SCALE_FACTOR are used to encode + the base index byte below. */ + reg_entry * base_reg; + reg_entry * index_reg; + uint log2_scale_factor; + + /* SEG gives the seg_entry of this insn. It is equal to zero unless + an explicit segment override is given. */ + seg_entry * seg; /* segment for memory operands (if given) */ + + /* PREFIX holds all the given prefix opcodes (usually null). + PREFIXES is the size of PREFIX. */ + char prefix [MAX_PREFIXES]; + uint prefixes; + + /* RM and IB are the modrm byte and the base index byte where the addressing + modes of this insn are encoded. */ + + modrm_byte rm; + base_index_byte bi; +} i386_insn; diff --git a/gnu/usr.bin/as/expr.c b/gnu/usr.bin/as/expr.c new file mode 100644 index 0000000000..f3a377d350 --- /dev/null +++ b/gnu/usr.bin/as/expr.c @@ -0,0 +1,980 @@ +/* expr.c -operands, expressions- + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * This is really a branch office of as-read.c. I split it out to clearly + * distinguish the world of expressions from the world of statements. + * (It also gives smaller files to re-compile.) + * Here, "operand"s are of expressions, not instructions. + */ + +#include +#include "as.h" +#include "flonum.h" +#include "read.h" +#include "struc-symbol.h" +#include "expr.h" +#include "obstack.h" +#include "symbols.h" + +static void clean_up_expression(); /* Internal. */ +extern const char EXP_CHARS[]; /* JF hide MD floating pt stuff all the same place */ +extern const char FLT_CHARS[]; + +#ifdef SUN_ASM_SYNTAX +extern int local_label_defined[]; +#endif + +/* + * Build any floating-point literal here. + * Also build any bignum literal here. + */ + +/* LITTLENUM_TYPE generic_buffer [6]; /* JF this is a hack */ +/* Seems atof_machine can backscan through generic_bignum and hit whatever + happens to be loaded before it in memory. And its way too complicated + for me to fix right. Thus a hack. JF: Just make generic_bignum bigger, + and never write into the early words, thus they'll always be zero. + I hate Dean's floating-point code. Bleh. + */ +LITTLENUM_TYPE generic_bignum [SIZE_OF_LARGE_NUMBER+6]; +FLONUM_TYPE generic_floating_point_number = +{ + & generic_bignum [6], /* low (JF: Was 0) */ + & generic_bignum [SIZE_OF_LARGE_NUMBER+6 - 1], /* high JF: (added +6) */ + 0, /* leader */ + 0, /* exponent */ + 0 /* sign */ +}; +/* If nonzero, we've been asked to assemble nan, +inf or -inf */ +int generic_floating_point_magic; + +/* + * Summary of operand(). + * + * in: Input_line_pointer points to 1st char of operand, which may + * be a space. + * + * out: A expressionS. X_seg determines how to understand the rest of the + * expressionS. + * The operand may have been empty: in this case X_seg == SEG_NONE. + * Input_line_pointer -> (next non-blank) char after operand. + * + */ + +static segT +operand (expressionP) + register expressionS * expressionP; +{ + register char c; + register char *name; /* points to name of symbol */ + register struct symbol * symbolP; /* Points to symbol */ + + extern char hex_value[]; /* In hex_value.c */ + char *local_label_name(); + + SKIP_WHITESPACE(); /* Leading whitespace is part of operand. */ + c = * input_line_pointer ++; /* Input_line_pointer -> past char in c. */ + if (isdigit(c)) + { + register valueT number; /* offset or (absolute) value */ + register short int digit; /* value of next digit in current radix */ + /* invented for humans only, hope */ + /* optimising compiler flushes it! */ + register short int radix; /* 8, 10 or 16 */ + /* 0 means we saw start of a floating- */ + /* point constant. */ + register short int maxdig;/* Highest permitted digit value. */ + register int too_many_digits; /* If we see >= this number of */ + /* digits, assume it is a bignum. */ + register char * digit_2; /* -> 2nd digit of number. */ + int small; /* TRUE if fits in 32 bits. */ + + if (c=='0') + { /* non-decimal radix */ + if ((c = * input_line_pointer ++)=='x' || c=='X') + { + c = * input_line_pointer ++; /* read past "0x" or "0X" */ + maxdig = radix = 16; + too_many_digits = 9; + } + else + { + /* If it says '0f' and the line ends or it DOESN'T look like + a floating point #, its a local label ref. DTRT */ + if(c=='f' && (! *input_line_pointer || + (!index("+-.0123456789",*input_line_pointer) && + !index(EXP_CHARS,*input_line_pointer)))) + { + maxdig = radix = 10; + too_many_digits = 11; + c='0'; + input_line_pointer-=2; + } + else if (c && index (FLT_CHARS,c)) + { + radix = 0; /* Start of floating-point constant. */ + /* input_line_pointer -> 1st char of number. */ + expressionP -> X_add_number = - (isupper(c) ? tolower(c) : c); + } + else + { /* By elimination, assume octal radix. */ + radix = 8; + maxdig = 10; /* Un*x sux. Compatibility. */ + too_many_digits = 11; + } + } + /* c == char after "0" or "0x" or "0X" or "0e" etc.*/ + } + else + { + maxdig = radix = 10; + too_many_digits = 11; + } + if (radix) + { /* Fixed-point integer constant. */ + /* May be bignum, or may fit in 32 bits. */ +/* + * Most numbers fit into 32 bits, and we want this case to be fast. + * So we pretend it will fit into 32 bits. If, after making up a 32 + * bit number, we realise that we have scanned more digits than + * comfortably fit into 32 bits, we re-scan the digits coding + * them into a bignum. For decimal and octal numbers we are conservative: some + * numbers may be assumed bignums when in fact they do fit into 32 bits. + * Numbers of any radix can have excess leading zeros: we strive + * to recognise this and cast them back into 32 bits. + * We must check that the bignum really is more than 32 + * bits, and change it back to a 32-bit number if it fits. + * The number we are looking for is expected to be positive, but + * if it fits into 32 bits as an unsigned number, we let it be a 32-bit + * number. The cavalier approach is for speed in ordinary cases. + */ + digit_2 = input_line_pointer; + for (number=0; (digit=hex_value[c]) char after C. */ + small = input_line_pointer - digit_2 < too_many_digits; + if ( ! small) + { + /* + * We saw a lot of digits. Manufacture a bignum the hard way. + */ + LITTLENUM_TYPE * leader; /* -> high order littlenum of the bignum. */ + LITTLENUM_TYPE * pointer; /* -> littlenum we are frobbing now. */ + long int carry; + + leader = generic_bignum; + generic_bignum [0] = 0; + generic_bignum [1] = 0; + /* We could just use digit_2, but lets be mnemonic. */ + input_line_pointer = -- digit_2; /* -> 1st digit. */ + c = *input_line_pointer ++; + for (; (carry = hex_value [c]) < maxdig; c = * input_line_pointer ++) + { + for (pointer = generic_bignum; + pointer <= leader; + pointer ++) + { + long int work; + + work = carry + radix * * pointer; + * pointer = work & LITTLENUM_MASK; + carry = work >> LITTLENUM_NUMBER_OF_BITS; + } + if (carry) + { + if (leader < generic_bignum + SIZE_OF_LARGE_NUMBER - 1) + { /* Room to grow a longer bignum. */ + * ++ leader = carry; + } + } + } + /* Again, C is char after number, */ + /* input_line_pointer -> after C. */ + know( BITS_PER_INT == 32 ); + know( LITTLENUM_NUMBER_OF_BITS == 16 ); + /* Hence the constant "2" in the next line. */ + if (leader < generic_bignum + 2) + { /* Will fit into 32 bits. */ + number = + ( (generic_bignum [1] & LITTLENUM_MASK) << LITTLENUM_NUMBER_OF_BITS ) + | (generic_bignum [0] & LITTLENUM_MASK); + small = TRUE; + } + else + { + number = leader - generic_bignum + 1; /* Number of littlenums in the bignum. */ + } + } + if (small) + { + /* + * Here with number, in correct radix. c is the next char. + * Note that unlike Un*x, we allow "011f" "0x9f" to + * both mean the same as the (conventional) "9f". This is simply easier + * than checking for strict canonical form. Syntax sux! + */ + if (number<10) + { +#ifdef SUN_ASM_SYNTAX + if (c=='b' || (c=='$' && local_label_defined[number])) +#else + if (c=='b') +#endif + { + /* + * Backward ref to local label. + * Because it is backward, expect it to be DEFINED. + */ + /* + * Construct a local label. + */ + name = local_label_name ((int)number, 0); + if ( (symbolP = symbol_table_lookup(name)) /* seen before */ + && (symbolP -> sy_type & N_TYPE) != N_UNDF /* symbol is defined: OK */ + ) + { /* Expected path: symbol defined. */ + /* Local labels are never absolute. Don't waste time checking absoluteness. */ + know( (symbolP -> sy_type & N_TYPE) == N_DATA + || (symbolP -> sy_type & N_TYPE) == N_TEXT ); + expressionP -> X_add_symbol = symbolP; + expressionP -> X_add_number = 0; + expressionP -> X_seg = N_TYPE_seg [symbolP -> sy_type]; + } + else + { /* Either not seen or not defined. */ + as_warn( "Backw. ref to unknown label \"%d:\", 0 assumed.", + number + ); + expressionP -> X_add_number = 0; + expressionP -> X_seg = SEG_ABSOLUTE; + } + } + else + { +#ifdef SUN_ASM_SYNTAX + if (c=='f' || (c=='$' && !local_label_defined[number])) +#else + if (c=='f') +#endif + { + /* + * Forward reference. Expect symbol to be undefined or + * unknown. Undefined: seen it before. Unknown: never seen + * it in this pass. + * Construct a local label name, then an undefined symbol. + * Don't create a XSEG frag for it: caller may do that. + * Just return it as never seen before. + */ + name = local_label_name ((int)number, 1); + if ( symbolP = symbol_table_lookup( name )) + { + /* We have no need to check symbol properties. */ + know( (symbolP -> sy_type & N_TYPE) == N_UNDF + || (symbolP -> sy_type & N_TYPE) == N_DATA + || (symbolP -> sy_type & N_TYPE) == N_TEXT); + } + else + { + symbolP = symbol_new (name, N_UNDF, 0,0,0, & zero_address_frag); + symbol_table_insert (symbolP); + } + expressionP -> X_add_symbol = symbolP; + expressionP -> X_seg = SEG_UNKNOWN; + expressionP -> X_subtract_symbol = NULL; + expressionP -> X_add_number = 0; + } + else + { /* Really a number, not a local label. */ + expressionP -> X_add_number = number; + expressionP -> X_seg = SEG_ABSOLUTE; + input_line_pointer --; /* Restore following character. */ + } /* if (c=='f') */ + } /* if (c=='b') */ + } + else + { /* Really a number. */ + expressionP -> X_add_number = number; + expressionP -> X_seg = SEG_ABSOLUTE; + input_line_pointer --; /* Restore following character. */ + } /* if (number<10) */ + } + else + { + expressionP -> X_add_number = number; + expressionP -> X_seg = SEG_BIG; + input_line_pointer --; /* -> char following number. */ + } /* if (small) */ + } /* (If integer constant) */ + else + { /* input_line_pointer -> */ + /* floating-point constant. */ + int error_code; + + error_code = atof_generic + (& input_line_pointer, ".", EXP_CHARS, + & generic_floating_point_number); + + if (error_code) + { + if (error_code == ERROR_EXPONENT_OVERFLOW) + { + as_warn( "Bad floating-point constant: exponent overflow, probably assembling junk" ); + } + else + { + as_warn( "Bad floating-point constant: unknown error code=%d.", error_code); + } + } + expressionP -> X_seg = SEG_BIG; + /* input_line_pointer -> just after constant, */ + /* which may point to whitespace. */ + know( expressionP -> X_add_number < 0 ); /* < 0 means "floating point". */ + } /* if (not floating-point constant) */ + } + else if(c=='.' && !is_part_of_name(*input_line_pointer)) { + extern struct obstack frags; + + /* + JF: '.' is pseudo symbol with value of current location in current + segment. . . + */ + symbolP = symbol_new("L0\001", + (unsigned char)(seg_N_TYPE[(int)now_seg]), + 0, + 0, + (valueT)(obstack_next_free(&frags)-frag_now->fr_literal), + frag_now); + expressionP->X_add_number=0; + expressionP->X_add_symbol=symbolP; + expressionP->X_seg = now_seg; + + } else if ( is_name_beginner(c) ) /* here if did not begin with a digit */ + { + /* + * Identifier begins here. + * This is kludged for speed, so code is repeated. + */ + name = -- input_line_pointer; + c = get_symbol_end(); + symbolP = symbol_table_lookup(name); + if (symbolP) + { + /* + * If we have an absolute symbol, then we know it's value now. + */ + register segT seg; + + seg = N_TYPE_seg [(int) symbolP -> sy_type & N_TYPE]; + if ((expressionP -> X_seg = seg) == SEG_ABSOLUTE ) + { + expressionP -> X_add_number = symbolP -> sy_value; + } + else + { + expressionP -> X_add_number = 0; + expressionP -> X_add_symbol = symbolP; + } + } + else + { + expressionP -> X_add_symbol + = symbolP + = symbol_new (name, N_UNDF, 0,0,0, & zero_address_frag); + + expressionP -> X_add_number = 0; + expressionP -> X_seg = SEG_UNKNOWN; + symbol_table_insert (symbolP); + } + * input_line_pointer = c; + expressionP -> X_subtract_symbol = NULL; + } + else if (c=='(')/* didn't begin with digit & not a name */ + { + (void)expression( expressionP ); + /* Expression() will pass trailing whitespace */ + if ( * input_line_pointer ++ != ')' ) + { + as_warn( "Missing ')' assumed"); + input_line_pointer --; + } + /* here with input_line_pointer -> char after "(...)" */ + } + else if ( c=='~' || c=='-' ) + { /* unary operator: hope for SEG_ABSOLUTE */ + switch(operand (expressionP)) { + case SEG_ABSOLUTE: + /* input_line_pointer -> char after operand */ + if ( c=='-' ) + { + expressionP -> X_add_number = - expressionP -> X_add_number; +/* + * Notice: '-' may overflow: no warning is given. This is compatible + * with other people's assemblers. Sigh. + */ + } + else + { + expressionP -> X_add_number = ~ expressionP -> X_add_number; + } + break; + + case SEG_TEXT: + case SEG_DATA: + case SEG_BSS: + case SEG_PASS1: + case SEG_UNKNOWN: + if(c=='-') { /* JF I hope this hack works */ + expressionP->X_subtract_symbol=expressionP->X_add_symbol; + expressionP->X_add_symbol=0; + expressionP->X_seg=SEG_DIFFERENCE; + break; + } + default: /* unary on non-absolute is unsuported */ + as_warn("Unary operator %c ignored because bad operand follows", c); + break; + /* Expression undisturbed from operand(). */ + } + } + else if (c=='\'') + { +/* + * Warning: to conform to other people's assemblers NO ESCAPEMENT is permitted + * for a single quote. The next character, parity errors and all, is taken + * as the value of the operand. VERY KINKY. + */ + expressionP -> X_add_number = * input_line_pointer ++; + expressionP -> X_seg = SEG_ABSOLUTE; + } + else + { + /* can't imagine any other kind of operand */ + expressionP -> X_seg = SEG_NONE; + input_line_pointer --; + } +/* + * It is more 'efficient' to clean up the expressions when they are created. + * Doing it here saves lines of code. + */ + clean_up_expression (expressionP); + SKIP_WHITESPACE(); /* -> 1st char after operand. */ + know( * input_line_pointer != ' ' ); + return (expressionP -> X_seg); +} /* operand */ + +/* Internal. Simplify a struct expression for use by expr() */ + +/* + * In: address of a expressionS. + * The X_seg field of the expressionS may only take certain values. + * Now, we permit SEG_PASS1 to make code smaller & faster. + * Elsewise we waste time special-case testing. Sigh. Ditto SEG_NONE. + * Out: expressionS may have been modified: + * 'foo-foo' symbol references cancelled to 0, + * which changes X_seg from SEG_DIFFERENCE to SEG_ABSOLUTE; + * Unused fields zeroed to help expr(). + */ + +static void +clean_up_expression (expressionP) + register expressionS * expressionP; +{ + switch (expressionP -> X_seg) + { + case SEG_NONE: + case SEG_PASS1: + expressionP -> X_add_symbol = NULL; + expressionP -> X_subtract_symbol = NULL; + expressionP -> X_add_number = 0; + break; + + case SEG_BIG: + case SEG_ABSOLUTE: + expressionP -> X_subtract_symbol = NULL; + expressionP -> X_add_symbol = NULL; + break; + + case SEG_TEXT: + case SEG_DATA: + case SEG_BSS: + case SEG_UNKNOWN: + expressionP -> X_subtract_symbol = NULL; + break; + + case SEG_DIFFERENCE: + /* + * It does not hurt to 'cancel' NULL==NULL + * when comparing symbols for 'eq'ness. + * It is faster to re-cancel them to NULL + * than to check for this special case. + */ + if (expressionP -> X_subtract_symbol == expressionP -> X_add_symbol + || ( expressionP->X_subtract_symbol + && expressionP->X_add_symbol + && expressionP->X_subtract_symbol->sy_frag==expressionP->X_add_symbol->sy_frag + && expressionP->X_subtract_symbol->sy_value==expressionP->X_add_symbol->sy_value)) + { + expressionP -> X_subtract_symbol = NULL; + expressionP -> X_add_symbol = NULL; + expressionP -> X_seg = SEG_ABSOLUTE; + } + break; + + default: + BAD_CASE( expressionP -> X_seg); + break; + } +} + +/* + * expr_part () + * + * Internal. Made a function because this code is used in 2 places. + * Generate error or correct X_?????_symbol of expressionS. + */ + +/* + * symbol_1 += symbol_2 ... well ... sort of. + */ + +static segT +expr_part (symbol_1_PP, symbol_2_P) + struct symbol ** symbol_1_PP; + struct symbol * symbol_2_P; +{ + segT return_value; + + know( (* symbol_1_PP) == NULL + || ((* symbol_1_PP) -> sy_type & N_TYPE) == N_TEXT + || ((* symbol_1_PP) -> sy_type & N_TYPE) == N_DATA + || ((* symbol_1_PP) -> sy_type & N_TYPE) == N_BSS + || ((* symbol_1_PP) -> sy_type & N_TYPE) == N_UNDF + ); + know( symbol_2_P == NULL + || (symbol_2_P -> sy_type & N_TYPE) == N_TEXT + || (symbol_2_P -> sy_type & N_TYPE) == N_DATA + || (symbol_2_P -> sy_type & N_TYPE) == N_BSS + || (symbol_2_P -> sy_type & N_TYPE) == N_UNDF + ); + if (* symbol_1_PP) + { + if (((* symbol_1_PP) -> sy_type & N_TYPE) == N_UNDF) + { + if (symbol_2_P) + { + return_value = SEG_PASS1; + * symbol_1_PP = NULL; + } + else + { + know( ((* symbol_1_PP) -> sy_type & N_TYPE) == N_UNDF) + return_value = SEG_UNKNOWN; + } + } + else + { + if (symbol_2_P) + { + if ((symbol_2_P -> sy_type & N_TYPE) == N_UNDF) + { + * symbol_1_PP = NULL; + return_value = SEG_PASS1; + } + else + { + /* {seg1} - {seg2} */ + as_warn( "Expression too complex, 2 symbols forgotten: \"%s\" \"%s\"", + (* symbol_1_PP) -> sy_name, symbol_2_P -> sy_name ); + * symbol_1_PP = NULL; + return_value = SEG_ABSOLUTE; + } + } + else + { + return_value = N_TYPE_seg [(* symbol_1_PP) -> sy_type & N_TYPE]; + } + } + } + else + { /* (* symbol_1_PP) == NULL */ + if (symbol_2_P) + { + * symbol_1_PP = symbol_2_P; + return_value = N_TYPE_seg [(symbol_2_P) -> sy_type & N_TYPE]; + } + else + { + * symbol_1_PP = NULL; + return_value = SEG_ABSOLUTE; + } + } + know( return_value == SEG_ABSOLUTE + || return_value == SEG_TEXT + || return_value == SEG_DATA + || return_value == SEG_BSS + || return_value == SEG_UNKNOWN + || return_value == SEG_PASS1 + ); + know( (* symbol_1_PP) == NULL + || ((* symbol_1_PP) -> sy_type & N_TYPE) == seg_N_TYPE [(int) return_value] ); + return (return_value); +} /* expr_part() */ + +/* Expression parser. */ + +/* + * We allow an empty expression, and just assume (absolute,0) silently. + * Unary operators and parenthetical expressions are treated as operands. + * As usual, Q==quantity==operand, O==operator, X==expression mnemonics. + * + * We used to do a aho/ullman shift-reduce parser, but the logic got so + * warped that I flushed it and wrote a recursive-descent parser instead. + * Now things are stable, would anybody like to write a fast parser? + * Most expressions are either register (which does not even reach here) + * or 1 symbol. Then "symbol+constant" and "symbol-symbol" are common. + * So I guess it doesn't really matter how inefficient more complex expressions + * are parsed. + * + * After expr(RANK,resultP) input_line_pointer -> operator of rank <= RANK. + * Also, we have consumed any leading or trailing spaces (operand does that) + * and done all intervening operators. + */ + +typedef enum +{ +O_illegal, /* (0) what we get for illegal op */ + +O_multiply, /* (1) * */ +O_divide, /* (2) / */ +O_modulus, /* (3) % */ +O_left_shift, /* (4) < */ +O_right_shift, /* (5) > */ +O_bit_inclusive_or, /* (6) | */ +O_bit_or_not, /* (7) ! */ +O_bit_exclusive_or, /* (8) ^ */ +O_bit_and, /* (9) & */ +O_add, /* (10) + */ +O_subtract /* (11) - */ +} +operatorT; + +#define __ O_illegal + +static const operatorT op_encoding [256] = { /* maps ASCII -> operators */ + +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + +__, O_bit_or_not, __, __, __, O_modulus, O_bit_and, __, +__, __, O_multiply, O_add, __, O_subtract, __, O_divide, +__, __, __, __, __, __, __, __, +__, __, __, __, O_left_shift, __, O_right_shift, __, +__, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, +__, __, __, __, __, __, O_bit_exclusive_or, __, +__, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, +__, __, __, __, O_bit_inclusive_or, __, __, __, + +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __ +}; + + +/* + * Rank Examples + * 0 operand, (expression) + * 1 + - + * 2 & ^ ! | + * 3 * / % < > + */ +typedef char operator_rankT; +static const operator_rankT +op_rank [] = { 0, 3, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1 }; + +segT /* Return resultP -> X_seg. */ +expr (rank, resultP) + register operator_rankT rank; /* Larger # is higher rank. */ + register expressionS * resultP; /* Deliver result here. */ +{ + expressionS right; + register operatorT op_left; + register char c_left; /* 1st operator character. */ + register operatorT op_right; + register char c_right; + + know( rank >= 0 ); + (void)operand (resultP); + know( * input_line_pointer != ' ' ); /* Operand() gobbles spaces. */ + c_left = * input_line_pointer; /* Potential operator character. */ + op_left = op_encoding [c_left]; + while (op_left != O_illegal && op_rank [(int) op_left] > rank) + { + input_line_pointer ++; /* -> after 1st character of operator. */ + /* Operators "<<" and ">>" have 2 characters. */ + if (* input_line_pointer == c_left && (c_left == '<' || c_left == '>') ) + { + input_line_pointer ++; + } /* -> after operator. */ + if (SEG_NONE == expr (op_rank[(int) op_left], &right)) + { + as_warn("Missing operand value assumed absolute 0."); + resultP -> X_add_number = 0; + resultP -> X_subtract_symbol = NULL; + resultP -> X_add_symbol = NULL; + resultP -> X_seg = SEG_ABSOLUTE; + } + know( * input_line_pointer != ' ' ); + c_right = * input_line_pointer; + op_right = op_encoding [c_right]; + if (* input_line_pointer == c_right && (c_right == '<' || c_right == '>') ) + { + input_line_pointer ++; + } /* -> after operator. */ + know( (int) op_right == 0 + || op_rank [(int) op_right] <= op_rank[(int) op_left] ); + /* input_line_pointer -> after right-hand quantity. */ + /* left-hand quantity in resultP */ + /* right-hand quantity in right. */ + /* operator in op_left. */ + if ( resultP -> X_seg == SEG_PASS1 || right . X_seg == SEG_PASS1 ) + { + resultP -> X_seg = SEG_PASS1; + } + else + { + if ( resultP -> X_seg == SEG_BIG ) + { + as_warn( "Left operand of %c is a %s. Integer 0 assumed.", + c_left, resultP -> X_add_number > 0 ? "bignum" : "float"); + resultP -> X_seg = SEG_ABSOLUTE; + resultP -> X_add_symbol = 0; + resultP -> X_subtract_symbol = 0; + resultP -> X_add_number = 0; + } + if ( right . X_seg == SEG_BIG ) + { + as_warn( "Right operand of %c is a %s. Integer 0 assumed.", + c_left, right . X_add_number > 0 ? "bignum" : "float"); + right . X_seg = SEG_ABSOLUTE; + right . X_add_symbol = 0; + right . X_subtract_symbol = 0; + right . X_add_number = 0; + } + if ( op_left == O_subtract ) + { + /* + * Convert - into + by exchanging symbols and negating number. + * I know -infinity can't be negated in 2's complement: + * but then it can't be subtracted either. This trick + * does not cause any further inaccuracy. + */ + + register struct symbol * symbolP; + + right . X_add_number = - right . X_add_number; + symbolP = right . X_add_symbol; + right . X_add_symbol = right . X_subtract_symbol; + right . X_subtract_symbol = symbolP; + if (symbolP) + { + right . X_seg = SEG_DIFFERENCE; + } + op_left = O_add; + } + + if ( op_left == O_add ) + { + segT seg1; + segT seg2; + + know( resultP -> X_seg == SEG_DATA + || resultP -> X_seg == SEG_TEXT + || resultP -> X_seg == SEG_BSS + || resultP -> X_seg == SEG_UNKNOWN + || resultP -> X_seg == SEG_DIFFERENCE + || resultP -> X_seg == SEG_ABSOLUTE + || resultP -> X_seg == SEG_PASS1 + ); + know( right . X_seg == SEG_DATA + || right . X_seg == SEG_TEXT + || right . X_seg == SEG_BSS + || right . X_seg == SEG_UNKNOWN + || right . X_seg == SEG_DIFFERENCE + || right . X_seg == SEG_ABSOLUTE + || right . X_seg == SEG_PASS1 + ); + + clean_up_expression (& right); + clean_up_expression (resultP); + + seg1 = expr_part (& resultP -> X_add_symbol, right . X_add_symbol); + seg2 = expr_part (& resultP -> X_subtract_symbol, right . X_subtract_symbol); + if (seg1 == SEG_PASS1 || seg2 == SEG_PASS1) { + need_pass_2 = TRUE; + resultP -> X_seg = SEG_PASS1; + } else if (seg2 == SEG_ABSOLUTE) + resultP -> X_seg = seg1; + else if ( seg1 != SEG_UNKNOWN + && seg1 != SEG_ABSOLUTE + && seg2 != SEG_UNKNOWN + && seg1 != seg2) { + know( seg2 != SEG_ABSOLUTE ); + know( resultP -> X_subtract_symbol ); + + know( seg1 == SEG_TEXT || seg1 == SEG_DATA || seg1== SEG_BSS ); + know( seg2 == SEG_TEXT || seg2 == SEG_DATA || seg2== SEG_BSS ); + know( resultP -> X_add_symbol ); + know( resultP -> X_subtract_symbol ); + as_warn("Expression too complex: forgetting %s - %s", + resultP -> X_add_symbol -> sy_name, + resultP -> X_subtract_symbol -> sy_name); + resultP -> X_seg = SEG_ABSOLUTE; + /* Clean_up_expression() will do the rest. */ + } else + resultP -> X_seg = SEG_DIFFERENCE; + + resultP -> X_add_number += right . X_add_number; + clean_up_expression (resultP); + } + else + { /* Not +. */ + if ( resultP -> X_seg == SEG_UNKNOWN || right . X_seg == SEG_UNKNOWN ) + { + resultP -> X_seg = SEG_PASS1; + need_pass_2 = TRUE; + } + else + { + resultP -> X_subtract_symbol = NULL; + resultP -> X_add_symbol = NULL; + /* Will be SEG_ABSOLUTE. */ + if ( resultP -> X_seg != SEG_ABSOLUTE || right . X_seg != SEG_ABSOLUTE ) + { + as_warn( "Relocation error. Absolute 0 assumed."); + resultP -> X_seg = SEG_ABSOLUTE; + resultP -> X_add_number = 0; + } + else + { + switch ( op_left ) + { + case O_bit_inclusive_or: + resultP -> X_add_number |= right . X_add_number; + break; + + case O_modulus: + if (right . X_add_number) + { + resultP -> X_add_number %= right . X_add_number; + } + else + { + as_warn( "Division by 0. 0 assumed." ); + resultP -> X_add_number = 0; + } + break; + + case O_bit_and: + resultP -> X_add_number &= right . X_add_number; + break; + + case O_multiply: + resultP -> X_add_number *= right . X_add_number; + break; + + case O_divide: + if (right . X_add_number) + { + resultP -> X_add_number /= right . X_add_number; + } + else + { + as_warn( "Division by 0. 0 assumed." ); + resultP -> X_add_number = 0; + } + break; + + case O_left_shift: + resultP -> X_add_number <<= right . X_add_number; + break; + + case O_right_shift: + resultP -> X_add_number >>= right . X_add_number; + break; + + case O_bit_exclusive_or: + resultP -> X_add_number ^= right . X_add_number; + break; + + case O_bit_or_not: + resultP -> X_add_number |= ~ right . X_add_number; + break; + + default: + BAD_CASE( op_left ); + break; + } /* switch(operator) */ + } + } /* If we have to force need_pass_2. */ + } /* If operator was +. */ + } /* If we didn't set need_pass_2. */ + op_left = op_right; + } /* While next operator is >= this rank. */ + return (resultP -> X_seg); +} + +/* + * get_symbol_end() + * + * This lives here because it belongs equally in expr.c & read.c. + * Expr.c is just a branch office read.c anyway, and putting it + * here lessens the crowd at read.c. + * + * Assume input_line_pointer is at start of symbol name. + * Advance input_line_pointer past symbol name. + * Turn that character into a '\0', returning its former value. + * This allows a string compare (RMS wants symbol names to be strings) + * of the symbol name. + * There will always be a char following symbol name, because all good + * lines end in end-of-line. + */ +char +get_symbol_end() +{ + register char c; + + while ( is_part_of_name( c = * input_line_pointer ++ ) ) + ; + * -- input_line_pointer = 0; + return (c); +} + +/* end: expr.c */ diff --git a/gnu/usr.bin/as/expr.h b/gnu/usr.bin/as/expr.h new file mode 100644 index 0000000000..964d3b9a84 --- /dev/null +++ b/gnu/usr.bin/as/expr.h @@ -0,0 +1,69 @@ +/* expr.h -> header file for expr.c + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * Abbreviations (mnemonics). + * + * O operator + * Q quantity, operand + * X eXpression + */ + +/* + * By popular demand, we define a struct to represent an expression. + * This will no doubt mutate as expressions become baroque. + * + * Currently, we support expressions like "foo-bar+42". + * In other words we permit a (possibly undefined) minuend, a + * (possibly undefined) subtrahend and an (absolute) augend. + * RMS says this is so we can have 1-pass assembly for any compiler + * emmissions, and a 'case' statement might emit 'undefined1 - undefined2'. + * + * To simplify table-driven dispatch, we also have a "segment" for the + * entire expression. That way we don't require complex reasoning about + * whether particular components are defined; and we can change component + * semantics without re-working all the dispatch tables in the assembler. + * In other words the "type" of an expression is its segment. + */ + +typedef struct +{ + symbolS *X_add_symbol; /* foo */ + symbolS *X_subtract_symbol; /* bar */ + long int X_add_number; /* 42. Must be signed. */ + segT X_seg; /* What segment (expr type)? */ +} +expressionS; + + /* result should be type (expressionS *). */ +#define expression(result) expr(0,result) + + /* If an expression is SEG_BIG, look here */ + /* for its value. These common data may */ + /* be clobbered whenever expr() is called. */ +extern FLONUM_TYPE generic_floating_point_number; /* Flonums returned here. */ + /* Enough to hold most precise flonum. */ +extern LITTLENUM_TYPE generic_bignum []; /* Bignums returned here. */ +#define SIZE_OF_LARGE_NUMBER (20) /* Number of littlenums in above. */ + + +segT expr(); +char get_symbol_end(); + +/* end: expr.h */ diff --git a/gnu/usr.bin/as/flonum-const.c b/gnu/usr.bin/as/flonum-const.c new file mode 100644 index 0000000000..617e585291 --- /dev/null +++ b/gnu/usr.bin/as/flonum-const.c @@ -0,0 +1,157 @@ +/* flonum_const.c - Useful Flonum constants + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "flonum.h" +/* JF: I added the last entry to this table, and I'm not + sure if its right or not. Could go either way. I wish + I really understood this stuff. */ + + +const int table_size_of_flonum_powers_of_ten = 11; + +static const LITTLENUM_TYPE zero[] = { 1 }; + +/***********************************************************************\ +* * +* Warning: the low order bits may be WRONG here. * +* I took this from a suspect bc(1) script. * +* "minus_X"[] is supposed to be 10^(2^-X) expressed in base 2^16. * +* The radix point is just AFTER the highest element of the [] * +* * +* Because bc rounds DOWN for printing (I think), the lowest * +* significance littlenums should probably have 1 added to them. * +* * +\***********************************************************************/ + +/* JF: If this equals 6553/(2^16)+39321/(2^32)+... it approaches .1 */ +static const LITTLENUM_TYPE minus_1 [] = { + 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321, + 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321, 6553 }; +static const LITTLENUM_TYPE plus_1 [] = { 10 }; + +/* JF: If this equals 655/(2^16) + 23592/(2^32) + ... it approaches .01 */ +static const LITTLENUM_TYPE minus_2 [] = { + 10485, 36700, 62914, 23592, 49807, 10485, 36700, 62914, 23592, 49807, + 10485, 36700, 62914, 23592, 49807, 10485, 36700, 62914, 23592, 655 }; +static const LITTLENUM_TYPE plus_2 [] = { 100 }; + +/* This approaches .0001 */ +static const LITTLENUM_TYPE minus_3 [] = { + 52533, 20027, 37329, 65116, 64067, 60397, 14784, 18979, 33659, 19503, + 2726, 9542, 629, 2202, 40475, 10590, 4299, 47815, 36280, 6 }; +static const LITTLENUM_TYPE plus_3 [] = { 10000 }; + +/* JF: this approaches 1e-8 */ +static const LITTLENUM_TYPE minus_4 [] = { + 22516, 49501, 54293, 19424, 60699, 6716, 24348, 22618, 23904, 21327, + 3919, 44703, 19149, 28803, 48959, 6259, 50273, 62237, 42 }; +/* This equals 1525 * 2^16 + 57600 */ +static const LITTLENUM_TYPE plus_4 [] = { 57600, 1525 }; + +/* This approaches 1e-16 */ +static const LITTLENUM_TYPE minus_5 [] = { + 22199, 45957, 17005, 26266, 10526, 16260, 55017, 35680, 40443, 19789, + 17356, 30195, 55905, 28426, 63010, 44197, 1844 }; +static const LITTLENUM_TYPE plus_5 [] = { 28609, 34546, 35 }; + +static const LITTLENUM_TYPE minus_6 [] = { + 30926, 26518, 13110, 43018, 54982, 48258, 24658, 15209, 63366, 11929, + 20069, 43857, 60487, 51 }; +static const LITTLENUM_TYPE plus_6 [] = { 61313, 34220, 16731, 11629, 1262 }; + +static const LITTLENUM_TYPE minus_7 [] = { + 29819, 14733, 21490, 40602, 31315, 65186, 2695 }; +static const LITTLENUM_TYPE plus_7 [] = { + 7937, 49002, 60772, 28216, 38893, 55975, 63988, 59711, 20227, 24 }; + +static const LITTLENUM_TYPE minus_8 [] = { + 45849, 19069, 18068, 36324, 37948, 48745, 10873, 64360, 15961, 20566, + 24178, 15922, 59427, 110 }; +static const LITTLENUM_TYPE plus_8 [] = { + 15873, 11925, 39177, 991, 14589, 19735, 25347, 65086, 53853, 938, + 37209, 47086, 33626, 23253, 32586, 42547, 9731, 59679, 590 }; + +static const LITTLENUM_TYPE minus_9 [] = { + 63601, 55221, 43562, 33661, 29067, 28203, 65417, 64352, 22462, 41110, + 12570, 28635, 23199, 50572, 28471, 27074, 46375, 64028, 13106, 63700, + 32698, 17493, 32420, 34382, 22750, 20681, 12300 }; +static const LITTLENUM_TYPE plus_9 [] = { + 63564, 61556, 29377, 54467, 18621, 28141, 36415, 61241, 47119, 30026, + 19740, 46002, 13541, 61413, 30480, 38664, 32205, 50593, 51112, 48904, + 48263, 43814, 286, 30826, 52813, 62575, 61390, 24540, 21495, 5 }; + +static const LITTLENUM_TYPE minus_10 [] = { + 50313, 34681, 1464, 25889, 19575, 41125, 17635, 4598, 49708, 13427, + 17287, 56115, 53783, 38255, 32415, 17778, 31596, 7557, 20951, 18477, + 40353, 1178, 44405, 11837, 11571, 50963, 15649, 11698, 40675, 2308, }; +static const LITTLENUM_TYPE plus_10[] = { +18520, 53764, 54535, 61910, 61962, 59843, 46270, 58053, 12473, 63785, + 2449, 43230, 50044, 47595, 10403, 35766, 32607, 1124, 24966, 35044, +25524, 23631, 18826, 14518, 58448, 14562, 49618, 5588, 25396, 28 }; + +static const LITTLENUM_TYPE minus_11 [] = { + 6223, 59909, 62437, 59960, 14652, 45336, 48800, 7647, 51962, 37982, + 60436, 58176, 26767, 8440, 9831, 48556, 20994, 14148, 6757, 17221, + 60624, 46129, 53210, 44085, 54016, 24259, 11232, 21229, 21313, 81, }; +static const LITTLENUM_TYPE plus_11 [] = { + 36159, 2055, 33615, 61362, 23581, 62454, 9748, 15275, 39284, 58636, + 16269, 42793, 47240, 45774, 50861, 48400, 9413, 40281, 4030, 9572, + 7984, 33038, 59522, 19450, 40593, 24486, 54320, 6661, 55766, 805, }; + +/* Shut up complaints about differing pointer types. They only differ + in the const attribute, but there isn't any easy way to do this + */ +#define X (LITTLENUM_TYPE *) + +const FLONUM_TYPE flonum_negative_powers_of_ten [] = { + {X zero, X zero, X zero, 0, '+'}, + {X minus_1, X minus_1 +19, X minus_1 + 19, -20, '+'}, + {X minus_2, X minus_2 +19, X minus_2 + 19, -20, '+'}, + {X minus_3, X minus_3 +19, X minus_3 + 19, -20, '+'}, + {X minus_4, X minus_4 +18, X minus_4 + 18, -20, '+'}, + {X minus_5, X minus_5 +16, X minus_5 + 16, -20, '+'}, + {X minus_6, X minus_6 +13, X minus_6 + 13, -20, '+'}, + {X minus_7, X minus_7 + 6, X minus_7 + 6, -20, '+'}, + {X minus_8, X minus_8 +13, X minus_8 + 13, -40, '+'}, + {X minus_9, X minus_9 +26, X minus_9 + 26, -80, '+'}, + {X minus_10, X minus_10+29, X minus_10 + 29,-136, '+'}, + {X minus_11, X minus_11+29, X minus_11 + 29,-242, '+'}, +}; + +const FLONUM_TYPE flonum_positive_powers_of_ten [] = { + {X zero, X zero, X zero, 0, '+'}, + {X plus_1, X plus_1 + 0, X plus_1 + 0, 0, '+'}, + {X plus_2, X plus_2 + 0, X plus_2 + 0, 0, '+'}, + {X plus_3, X plus_3 + 0, X plus_3 + 0, 0, '+'}, + {X plus_4, X plus_4 + 1, X plus_4 + 1, 0, '+'}, + {X plus_5, X plus_5 + 2, X plus_5 + 2, 1, '+'}, + {X plus_6, X plus_6 + 4, X plus_6 + 4, 2, '+'}, + {X plus_7, X plus_7 + 9, X plus_7 + 9, 4, '+'}, + {X plus_8, X plus_8 + 18, X plus_8 + 18, 8, '+'}, + {X plus_9, X plus_9 + 29, X plus_9 + 29, 24, '+'}, + {X plus_10, X plus_10 + 29, X plus_10 + 29, 77, '+'}, + {X plus_11, X plus_11 + 29, X plus_11 + 29, 183, '+'}, +}; + +#ifdef VMS +dummy1() +{ +} +#endif +/* end: flonum_const.c */ diff --git a/gnu/usr.bin/as/flonum-copy.c b/gnu/usr.bin/as/flonum-copy.c new file mode 100644 index 0000000000..3a51f06d7e --- /dev/null +++ b/gnu/usr.bin/as/flonum-copy.c @@ -0,0 +1,76 @@ +/* flonum_copy.c - copy a flonum + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "flonum.h" +#ifdef USG +#define bzero(s,n) memset(s,0,n) +#define bcopy(from,to,n) memcpy(to,from,n) +#endif + +void +flonum_copy (in, out) + FLONUM_TYPE * in; + FLONUM_TYPE * out; +{ + int in_length; /* 0 origin */ + int out_length; /* 0 origin */ + + out -> sign = in -> sign; + in_length = in -> leader - in -> low; + if (in_length < 0) + { + out -> leader = out -> low - 1; /* 0.0 case */ + } + else + { + out_length = out -> high - out -> low; + /* + * Assume no GAPS in packing of littlenums. + * I.e. sizeof(array) == sizeof(element) * number_of_elements. + */ + if (in_length <= out_length) + { + { + /* + * For defensive programming, zero any high-order littlenums we don't need. + * This is destroying evidence and wasting time, so why bother??? + */ + if (in_length < out_length) + { + bzero ((char *)(out->low + in_length + 1), out_length - in_length); + } + } + bcopy ((char *)(in->low), (char *)(out->low), (int)((in_length + 1) * sizeof(LITTLENUM_TYPE))); + out -> exponent = in -> exponent; + out -> leader = in -> leader - in -> low + out -> low; + } + else + { + int shorten; /* 1-origin. Number of littlenums we drop. */ + + shorten = in_length - out_length; + /* Assume out_length >= 0 ! */ + bcopy ((char *)(in->low + shorten),(char *)( out->low), (int)((out_length + 1) * sizeof(LITTLENUM_TYPE))); + out -> leader = out -> high; + out -> exponent = in -> exponent + shorten; + } + } /* if any significant bits */ +} + +/* end: flonum_copy.c */ diff --git a/gnu/usr.bin/as/flonum-mult.c b/gnu/usr.bin/as/flonum-mult.c new file mode 100644 index 0000000000..1b7b5eadd3 --- /dev/null +++ b/gnu/usr.bin/as/flonum-mult.c @@ -0,0 +1,200 @@ +/* flonum_multip.c - multiply two flonums + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of Gas, the GNU Assembler. + +The GNU assembler is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY. No author or distributor +accepts responsibility to anyone for the consequences of using it +or for whether it serves any particular purpose or works at all, +unless he says so in writing. Refer to the GNU Assembler General +Public License for full details. + +Everyone is granted permission to copy, modify and redistribute +the GNU Assembler, but only under the conditions described in the +GNU Assembler General Public License. A copy of this license is +supposed to have been given to you along with the GNU Assembler +so you can know your rights and responsibilities. It should be +in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. */ + +#include "flonum.h" + +/* plan for a . b => p(roduct) + + + +-------+-------+-/ /-+-------+-------+ + | a | a | ... | a | a | + | A | A-1 | | 1 | 0 | + +-------+-------+-/ /-+-------+-------+ + + + +-------+-------+-/ /-+-------+-------+ + | b | b | ... | b | b | + | B | B-1 | | 1 | 0 | + +-------+-------+-/ /-+-------+-------+ + + + +-------+-------+-/ /-+-------+-/ /-+-------+-------+ + | p | p | ... | p | ... | p | p | + | A+B+1| A+B | | N | | 1 | 0 | + +-------+-------+-/ /-+-------+-/ /-+-------+-------+ + + /^\ + (carry) a .b ... | ... a .b a .b + A B | 0 1 0 0 + | + ... | ... a .b + | 1 0 + | + | ... + | + | + | + | ___ + | \ + +----- P = > a .b + N /__ i j + + N = 0 ... A+B + + for all i,j where i+j=N + [i,j integers > 0] + +a[], b[], p[] may not intersect. +Zero length factors signify 0 significant bits: treat as 0.0. +0.0 factors do the right thing. +Zero length product OK. + +I chose the ForTran accent "foo[bar]" instead of the C accent "*garply" +because I felt the ForTran way was more intuitive. The C way would +probably yield better code on most C compilers. Dean Elsner. +(C style also gives deeper insight [to me] ... oh well ...) +*/ + +void +flonum_multip (a, b, product) + FLONUM_TYPE * a, + * b, + * product; +{ + int size_of_a; /* 0 origin */ + int size_of_b; /* 0 origin */ + int size_of_product; /* 0 origin */ + int size_of_sum; /* 0 origin */ + int extra_product_positions;/* 1 origin */ + unsigned long int work; + unsigned long int carry; + long int exponent; + LITTLENUM_TYPE * q; + long int significant; /* TRUE when we emit a non-0 littlenum */ + /* ForTran accent follows. */ + int P; /* Scan product low-order -> high. */ + int N; /* As in sum above. */ + int A; /* Which [] of a? */ + int B; /* Which [] of b? */ + + if((a->sign!='-' && a->sign!='+') || (b->sign!='-' && b->sign!='+')) { + /* ... + Got to fail somehow. Any suggestions? */ + product->sign=0; + return; + } + product -> sign = (a->sign == b->sign) ? '+' : '-'; + size_of_a = a -> leader - a -> low; + size_of_b = b -> leader - b -> low; + exponent = a -> exponent + b -> exponent; + size_of_product = product -> high - product -> low; + size_of_sum = size_of_a + size_of_b; + extra_product_positions = size_of_product - size_of_sum; + if (extra_product_positions < 0) + { + P = extra_product_positions; /* P < 0 */ + exponent -= extra_product_positions; /* Increases exponent. */ + } + else + { + P = 0; + } + carry = 0; + significant = 0; + for (N = 0; + N <= size_of_sum; + N++) + { + work = carry; + carry = 0; + for (A = 0; + A <= N; + A ++) + { + B = N - A; + if (A <= size_of_a && B <= size_of_b && B >= 0) + { +#ifdef TRACE +printf("a:low[%d.]=%04x b:low[%d.]=%04x work_before=%08x\n", A, a->low[A], B, b->low[B], work); +#endif + work += a -> low [A] * b -> low [B]; + carry += work >> LITTLENUM_NUMBER_OF_BITS; + work &= LITTLENUM_MASK; +#ifdef TRACE +printf("work=%08x carry=%04x\n", work, carry); +#endif + } + } + significant |= work; + if (significant || P<0) + { + if (P >= 0) + { + product -> low [P] = work; +#ifdef TRACE +printf("P=%d. work[p]:=%04x\n", P, work); +#endif + } + P ++; + } + else + { + extra_product_positions ++; + exponent ++; + } + } + /* + * [P]-> position # size_of_sum + 1. + * This is where 'carry' should go. + */ +#ifdef TRACE +printf("final carry =%04x\n", carry); +#endif + if (carry) + { + if (extra_product_positions > 0) + { + product -> low [P] = carry; + } + else + { + /* No room at high order for carry littlenum. */ + /* Shift right 1 to make room for most significant littlenum. */ + exponent ++; + P --; + for (q = product -> low + P; + q >= product -> low; + q --) + { + work = * q; + * q = carry; + carry = work; + } + } + } + else + { + P --; + } + product -> leader = product -> low + P; + product -> exponent = exponent; +} + +/* end: flonum_multip.c */ diff --git a/gnu/usr.bin/as/flonum.h b/gnu/usr.bin/as/flonum.h new file mode 100644 index 0000000000..b2e841abce --- /dev/null +++ b/gnu/usr.bin/as/flonum.h @@ -0,0 +1,111 @@ +/* flonum.h - Floating point package + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/***********************************************************************\ +* * +* Arbitrary-precision floating point arithmetic. * +* * +* * +* Notation: a floating point number is expressed as * +* MANTISSA * (2 ** EXPONENT). * +* * +* If this offends more traditional mathematicians, then * +* please tell me your nomenclature for flonums! * +* * +\***********************************************************************/ +#if !defined(__STDC__) && !defined(const) +#define const /* empty */ +#endif + +#include "bignum.h" + +/***********************************************************************\ +* * +* Variable precision floating point numbers. * +* * +* Exponent is the place value of the low littlenum. E.g.: * +* If 0: low points to the units littlenum. * +* If 1: low points to the LITTLENUM_RADIX littlenum. * +* If -1: low points to the 1/LITTLENUM_RADIX littlenum. * +* * +\***********************************************************************/ + +/* JF: A sign value of 0 means we have been asked to assemble NaN + A sign value of 'P' means we've been asked to assemble +Inf + A sign value of 'N' means we've been asked to assemble -Inf + */ +struct FLONUM_STRUCT +{ + LITTLENUM_TYPE * low; /* low order littlenum of a bignum */ + LITTLENUM_TYPE * high; /* high order littlenum of a bignum */ + LITTLENUM_TYPE * leader; /* -> 1st non-zero littlenum */ + /* If flonum is 0.0, leader==low-1 */ + long int exponent; /* base LITTLENUM_RADIX */ + char sign; /* '+' or '-' */ +}; + +typedef struct FLONUM_STRUCT FLONUM_TYPE; + + +/***********************************************************************\ +* * +* Since we can (& do) meet with exponents like 10^5000, it * +* is silly to make a table of ~ 10,000 entries, one for each * +* power of 10. We keep a table where item [n] is a struct * +* FLONUM_FLOATING_POINT representing 10^(2^n). We then * +* multiply appropriate entries from this table to get any * +* particular power of 10. For the example of 10^5000, a table * +* of just 25 entries suffices: 10^(2^-12)...10^(2^+12). * +* * +\***********************************************************************/ + + +extern const FLONUM_TYPE flonum_positive_powers_of_ten[]; +extern const FLONUM_TYPE flonum_negative_powers_of_ten[]; +extern const int table_size_of_flonum_powers_of_ten; + /* Flonum_XXX_powers_of_ten[] table has */ + /* legal indices from 0 to */ + /* + this number inclusive. */ + + + +/***********************************************************************\ +* * +* Declare worker functions. * +* * +\***********************************************************************/ + +void flonum_multip(); +void flonum_copy(); +void flonum_print(); +char * flonum_get(); /* Returns "" or error string. */ +void flonum_normal(); + +int atof_generic(); + + +/***********************************************************************\ +* * +* Declare error codes. * +* * +\***********************************************************************/ + +#define ERROR_EXPONENT_OVERFLOW (2) + +/* end: flonum.h */ diff --git a/gnu/usr.bin/as/frags.c b/gnu/usr.bin/as/frags.c new file mode 100644 index 0000000000..ee10321ad1 --- /dev/null +++ b/gnu/usr.bin/as/frags.c @@ -0,0 +1,292 @@ +/* frags.c - manage frags - + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "as.h" +#include "subsegs.h" +#include "obstack.h" +#include "frags.h" +#include "struc-symbol.h" + +struct obstack frags; /* All, and only, frags live here. */ + +fragS zero_address_frag = { + 0, /* fr_address */ + NULL, /* fr_next */ + 0, /* fr_fix */ + 0, /* fr_var */ + 0, /* fr_symbol */ + 0, /* fr_offset */ + NULL, /* fr_opcode */ + rs_fill, /* fr_type */ + 0, /* fr_subtype */ + 0, /* fr_pcrel_adjust */ + 0, /* fr_bsr */ + 0 /* fr_literal [0] */ +}; + +fragS bss_address_frag = { + 0, /* fr_address. Gets filled in to make up + sy_value-s. */ + NULL, /* fr_next */ + 0, /* fr_fix */ + 0, /* fr_var */ + 0, /* fr_symbol */ + 0, /* fr_offset */ + NULL, /* fr_opcode */ + rs_fill, /* fr_type */ + 0, /* fr_subtype */ + 0, /* fr_pcrel_adjust */ + 0, /* fr_bsr */ + 0 /* fr_literal [0] */ +}; + +/* + * frag_grow() + * + * Internal. + * Try to augment current frag by nchars chars. + * If there is no room, close of the current frag with a ".fill 0" + * and begin a new frag. Unless the new frag has nchars chars available + * do not return. Do not set up any fields of *now_frag. + */ +static void +frag_grow (nchars) +int nchars; +{ + if (obstack_room (&frags) < nchars) { + unsigned int n,oldn; + long oldc; + + frag_wane (frag_now); + frag_new (0); + oldn=(unsigned)-1; + oldc=frags.chunk_size; + frags.chunk_size=2*nchars; + while((n=obstack_room(&frags))fr_fix = (char *) (obstack_next_free (&frags)) - + (frag_now->fr_literal) - old_frags_var_max_size; + /* Fix up old frag's fr_fix. */ + + obstack_finish (&frags); + /* This will align the obstack so the */ + /* next struct we allocate on it will */ + /* begin at a correct boundary. */ + frchP = frchain_now; + know (frchP); + former_last_fragP = frchP->frch_last; + know (former_last_fragP); + know (former_last_fragP == frag_now); + obstack_blank (&frags, SIZEOF_STRUCT_FRAG); + /* We expect this will begin at a correct */ + /* boundary for a struct. */ + tmp=obstack_alignment_mask(&frags); + obstack_alignment_mask(&frags)=0; /* Turn off alignment */ + /* If we ever hit a machine + where strings must be + aligned, we Lose Big */ + frag_now=(fragS *)obstack_finish(&frags); + obstack_alignment_mask(&frags)=tmp; /* Restore alignment */ + + /* Just in case we don't get zero'd bytes */ + bzero(frag_now, SIZEOF_STRUCT_FRAG); + +/* obstack_unaligned_done (&frags, &frag_now); */ +/* know (frags.obstack_c_next_free == frag_now->fr_literal); */ + /* Generally, frag_now->points to an */ + /* address rounded up to next alignment. */ + /* However, characters will add to obstack */ + /* frags IMMEDIATELY after the struct frag, */ + /* even if they are not starting at an */ + /* alignment address. */ + former_last_fragP->fr_next = frag_now; + frchP->frch_last = frag_now; + frag_now->fr_next = NULL; +} /* frag_new() */ + +/* + * frag_more() + * + * Start a new frag unless we have n more chars of room in the current frag. + * Close off the old frag with a .fill 0. + * + * Return the address of the 1st char to write into. Advance + * frag_now_growth past the new chars. + */ + +char * +frag_more (nchars) +int nchars; +{ + register char *retval; + + frag_grow (nchars); + retval = obstack_next_free (&frags); + obstack_blank_fast (&frags, nchars); + return (retval); +} /* frag_more() */ + +/* + * frag_var() + * + * Start a new frag unless we have max_chars more chars of room in the current frag. + * Close off the old frag with a .fill 0. + * + * Set up a machine_dependent relaxable frag, then start a new frag. + * Return the address of the 1st char of the var part of the old frag + * to write into. + */ + +char * +frag_var (type, max_chars, var, subtype, symbol, offset, opcode) +relax_stateT type; +int max_chars; +int var; +relax_substateT subtype; +symbolS * symbol; +long int offset; +char * opcode; +{ + register char *retval; + + frag_grow (max_chars); + retval = obstack_next_free (&frags); + obstack_blank_fast (&frags, max_chars); + frag_now->fr_var = var; + frag_now->fr_type = type; + frag_now->fr_subtype = subtype; + frag_now->fr_symbol = symbol; + frag_now->fr_offset = offset; + frag_now->fr_opcode = opcode; + /* default these to zero. */ + frag_now->fr_pcrel_adjust = 0; + frag_now->fr_bsr = 0; + frag_new (max_chars); + return (retval); +} /* frag_var() */ + +/* + * frag_variant() + * + * OVE: This variant of frag_var assumes that space for the tail has been + * allocated by caller. + * No call to frag_grow is done. + * Two new arguments have been added. + */ + +char * +frag_variant (type, max_chars, var, subtype, symbol, offset, opcode, pcrel_adjust,bsr) + relax_stateT type; + int max_chars; + int var; + relax_substateT subtype; + symbolS *symbol; + long int offset; + char *opcode; + char pcrel_adjust; + char bsr; +{ + register char *retval; + +/* frag_grow (max_chars); */ + retval = obstack_next_free (&frags); +/* obstack_blank_fast (&frags, max_chars); */ /* OVE: so far the only diff */ + frag_now->fr_var = var; + frag_now->fr_type = type; + frag_now->fr_subtype = subtype; + frag_now->fr_symbol = symbol; + frag_now->fr_offset = offset; + frag_now->fr_opcode = opcode; + frag_now->fr_pcrel_adjust = pcrel_adjust; + frag_now->fr_bsr = bsr; + frag_new (max_chars); + return (retval); +} /* frag_variant() */ + +/* + * frag_wane() + * + * Reduce the variable end of a frag to a harmless state. + */ +void +frag_wane (fragP) +register fragS * fragP; +{ + fragP->fr_type = rs_fill; + fragP->fr_offset = 0; + fragP->fr_var = 0; +} + +/* + * frag_align() + * + * Make a frag for ".align foo,bar". Call is "frag_align (foo,bar);". + * Foo & bar are absolute integers. + * + * Call to close off the current frag with a ".align", then start a new + * (so far empty) frag, in the same subsegment as the last frag. + */ + +void +frag_align (alignment, fill_character) +int alignment; +int fill_character; +{ + *(frag_var (rs_align, 1, 1, (relax_substateT)0, (symbolS *)0, + (long)alignment, (char *)0)) = fill_character; +} + +/* end: frags.c */ diff --git a/gnu/usr.bin/as/frags.h b/gnu/usr.bin/as/frags.h new file mode 100644 index 0000000000..6d7310b35b --- /dev/null +++ b/gnu/usr.bin/as/frags.h @@ -0,0 +1,41 @@ +/* frags.h - Header file for the frag concept. + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +extern struct obstack frags; + /* Frags ONLY live in this obstack. */ + /* We use obstack_next_free() macro */ + /* so please don't put any other objects */ + /* on this stack! */ + +/* + * A macro to speed up appending exactly 1 char + * to current frag. + */ +/* JF changed < 1 to <= 1 to avoid a race conditon */ +#define FRAG_APPEND_1_CHAR(datum) \ +{ \ + if (obstack_room( &frags ) <= 1) {\ + frag_wane (frag_now); \ + frag_new (0); \ + } \ + obstack_1grow( &frags, datum ); \ +} + + +/* end: frags.h */ diff --git a/gnu/usr.bin/as/hash.c b/gnu/usr.bin/as/hash.c new file mode 100644 index 0000000000..21f0cc028a --- /dev/null +++ b/gnu/usr.bin/as/hash.c @@ -0,0 +1,981 @@ +/* hash.c - hash table lookup strings - + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * BUGS, GRIPES, APOLOGIA etc. + * + * A typical user doesn't need ALL this: I intend to make a library out + * of it one day - Dean Elsner. + * Also, I want to change the definition of a symbol to (address,length) + * so I can put arbitrary binary in the names stored. [see hsh.c for that] + * + * This slime is common coupled inside the module. Com-coupling (and other + * vandalism) was done to speed running time. The interfaces at the + * module's edges are adequately clean. + * + * There is no way to (a) run a test script through this heap and (b) + * compare results with previous scripts, to see if we have broken any + * code. Use GNU (f)utilities to do this. A few commands assist test. + * The testing is awkward: it tries to be both batch & interactive. + * For now, interactive rules! + */ + +/* + * The idea is to implement a symbol table. A test jig is here. + * Symbols are arbitrary strings; they can't contain '\0'. + * [See hsh.c for a more general symbol flavour.] + * Each symbol is associated with a char*, which can point to anything + * you want, allowing an arbitrary property list for each symbol. + * + * The basic operations are: + * + * new creates symbol table, returns handle + * find (symbol) returns char* + * insert (symbol,char*) error if symbol already in table + * delete (symbol) returns char* if symbol was in table + * apply so you can delete all symbols before die() + * die destroy symbol table (free up memory) + * + * Supplementary functions include: + * + * say how big? what % full? + * replace (symbol,newval) report previous value + * jam (symbol,value) assert symbol:=value + * + * You, the caller, have control over errors: this just reports them. + * + * This package requires malloc(), free(). + * Malloc(size) returns NULL or address of char[size]. + * Free(address) frees same. + */ + +/* + * The code and its structures are re-enterent. + * Before you do anything else, you must call hash_new() which will + * return the address of a hash-table-control-block (or NULL if there + * is not enough memory). You then use this address as a handle of the + * symbol table by passing it to all the other hash_...() functions. + * The only approved way to recover the memory used by the symbol table + * is to call hash_die() with the handle of the symbol table. + * + * Before you call hash_die() you normally delete anything pointed to + * by individual symbols. After hash_die() you can't use that symbol + * table again. + * + * The char* you associate with a symbol may not be NULL (0) because + * NULL is returned whenever a symbol is not in the table. Any other + * value is OK, except DELETED, #defined below. + * + * When you supply a symbol string for insertion, YOU MUST PRESERVE THE + * STRING until that symbol is deleted from the table. The reason is that + * only the address you supply, NOT the symbol string itself, is stored + * in the symbol table. + * + * You may delete and add symbols arbitrarily. + * Any or all symbols may have the same 'value' (char *). In fact, these + * routines don't do anything with your symbol values. + * + * You have no right to know where the symbol:char* mapping is stored, + * because it moves around in memory; also because we may change how it + * works and we don't want to break your code do we? However the handle + * (address of struct hash_control) is never changed in + * the life of the symbol table. + * + * What you CAN find out about a symbol table is: + * how many slots are in the hash table? + * how many slots are filled with symbols? + * (total hashes,collisions) for (reads,writes) (*) + * All of the above values vary in time. + * (*) some of these numbers will not be meaningful if we change the + * internals. + */ + +/* + * I N T E R N A L + * + * Hash table is an array of hash_entries; each entry is a pointer to a + * a string and a user-supplied value 1 char* wide. + * + * The array always has 2 ** n elements, n>0, n integer. + * There is also a 'wall' entry after the array, which is always empty + * and acts as a sentinel to stop running off the end of the array. + * When the array gets too full, we create a new array twice as large + * and re-hash the symbols into the new array, then forget the old array. + * (Of course, we copy the values into the new array before we junk the + * old array!) + * + */ + +#include +#define TRUE (1) +#define FALSE (0) +#include +#define min(a, b) ((a) < (b) ? (a) : (b)) + +#include "hash.h" +char *xmalloc(); + +#define DELETED ((char *)1) /* guarenteed invalid address */ +#define START_POWER (11) /* power of two: size of new hash table *//* JF was 6 */ +/* JF These next two aren't used any more. */ +/* #define START_SIZE (64) / * 2 ** START_POWER */ +/* #define START_FULL (32) / * number of entries before table expands */ +#define islive(ptr) (ptr->hash_string && ptr->hash_string!=DELETED) + /* above TRUE if a symbol is in entry @ ptr */ + +#define STAT_SIZE (0) /* number of slots in hash table */ + /* the wall does not count here */ + /* we expect this is always a power of 2 */ +#define STAT_ACCESS (1) /* number of hash_ask()s */ +#define STAT__READ (0) /* reading */ +#define STAT__WRITE (1) /* writing */ +#define STAT_COLLIDE (3) /* number of collisions (total) */ + /* this may exceed STAT_ACCESS if we have */ + /* lots of collisions/access */ +#define STAT_USED (5) /* slots used right now */ +#define STATLENGTH (6) /* size of statistics block */ +#if STATLENGTH != HASH_STATLENGTH +Panic! Please make #include "stat.h" agree with previous definitions! +#endif + +/* #define SUSPECT to do runtime checks */ +/* #define TEST to be a test jig for hash...() */ + +#ifdef TEST /* TEST: use smaller hash table */ +#undef START_POWER +#define START_POWER (3) +#undef START_SIZE +#define START_SIZE (8) +#undef START_FULL +#define START_FULL (4) +#endif + +/*------------------ plan ---------------------------------- i = internal + +struct hash_control * c; +struct hash_entry * e; i +int b[z]; buffer for statistics + z size of b +char * s; symbol string (address) [ key ] +char * v; value string (address) [datum] +boolean f; TRUE if we found s in hash table i +char * t; error string; "" means OK +int a; access type [0...n) i + +c=hash_new () create new hash_control + +hash_die (c) destroy hash_control (and hash table) + table should be empty. + doesn't check if table is empty. + c has no meaning after this. + +hash_say (c,b,z) report statistics of hash_control. + also report number of available statistics. + +v=hash_delete (c,s) delete symbol, return old value if any. + ask() NULL means no old value. + f + +v=hash_replace (c,s,v) replace old value of s with v. + ask() NULL means no old value: no table change. + f + +t=hash_insert (c,s,v) insert (s,v) in c. + ask() return error string. + f it is an error to insert if s is already + in table. + if any error, c is unchanged. + +t=hash_jam (c,s,v) assert that new value of s will be v. i + ask() it may decide to GROW the table. i + f i + grow() i +t=hash_grow (c) grow the hash table. i + jam() will invoke JAM. i + +?=hash_apply (c,y) apply y() to every symbol in c. + y evtries visited in 'unspecified' order. + +v=hash_find (c,s) return value of s, or NULL if s not in c. + ask() + f + +f,e=hash_ask() (c,s,a) return slot where s SHOULD live. i + code() maintain collision stats in c. i + +.=hash_code (c,s) compute hash-code for s, i + from parameters of c. i + +*/ + +static char hash_found; /* returned by hash_ask() to stop extra */ + /* testing. hash_ask() wants to return both */ + /* a slot and a status. This is the status. */ + /* TRUE: found symbol */ + /* FALSE: absent: empty or deleted slot */ + /* Also returned by hash_jam(). */ + /* TRUE: we replaced a value */ + /* FALSE: we inserted a value */ + +static struct hash_entry * hash_ask(); +static int hash_code (); +static char * hash_grow(); + +/* + * h a s h _ n e w ( ) + * + */ +struct hash_control * +hash_new() /* create a new hash table */ + /* return NULL if failed */ + /* return handle (address of struct hash) */ +{ + register struct hash_control * retval; + register struct hash_entry * room; /* points to hash table */ + register struct hash_entry * wall; + register struct hash_entry * entry; + char * malloc(); + register int * ip; /* scan stats block of struct hash_control */ + register int * nd; /* limit of stats block */ + + if ( room = (struct hash_entry *) malloc( sizeof(struct hash_entry)*((1<hash_stat + STATLENGTH; + for (ip=retval->hash_stat; ip hash_stat[STAT_SIZE] = 1< hash_mask = (1< hash_sizelog = START_POWER; + /* works for 1's compl ok */ + retval -> hash_where = room; + retval -> hash_wall = + wall = room + (1< hash_full = (1<hash_string = NULL; + } + } + } + else + { + retval = NULL; /* no room for table: fake a failure */ + } + return(retval); /* return NULL or set-up structs */ +} + +/* + * h a s h _ d i e ( ) + * + * Table should be empty, but this is not checked. + * To empty the table, try hash_apply()ing a symbol deleter. + * Return to free memory both the hash table and it's control + * block. + * 'handle' has no meaning after this function. + * No errors are recoverable. + */ +void +hash_die(handle) + struct hash_control * handle; +{ + free((char *)handle->hash_where); + free((char *)handle); +} + +/* + * h a s h _ s a y ( ) + * + * Return the size of the statistics table, and as many statistics as + * we can until either (a) we have run out of statistics or (b) caller + * has run out of buffer. + * NOTE: hash_say treats all statistics alike. + * These numbers may change with time, due to insertions, deletions + * and expansions of the table. + * The first "statistic" returned is the length of hash_stat[]. + * Then contents of hash_stat[] are read out (in ascending order) + * until your buffer or hash_stat[] is exausted. + */ +void +hash_say(handle,buffer,bufsiz) + register struct hash_control * handle; + register int buffer[/*bufsiz*/]; + register int bufsiz; +{ + register int * nd; /* limit of statistics block */ + register int * ip; /* scan statistics */ + + ip = handle -> hash_stat; + nd = ip + min(bufsiz-1,STATLENGTH); + if (bufsiz>0) /* trust nothing! bufsiz<=0 is dangerous */ + { + *buffer++ = STATLENGTH; + for (; ip hash_value; + entry -> hash_string = DELETED; /* mark as deleted */ + handle -> hash_stat[STAT_USED] -= 1; /* slots-in-use count */ +#ifdef SUSPECT + if (handle->hash_stat[STAT_USED]<0) + { + error("hash_delete"); + } +#endif /* def SUSPECT */ + } + else + { + retval = NULL; + } + return(retval); +} + +/* + * h a s h _ r e p l a c e ( ) + * + * Try to replace the old value of a symbol with a new value. + * Normally return the old value. + * Return NULL and don't change the table if the symbol is not already + * in the table. + */ +char * +hash_replace(handle,string,value) + register struct hash_control * handle; + register char * string; + register char * value; +{ + register struct hash_entry * entry; + register char * retval; + + entry = hash_ask(handle,string,STAT__WRITE); + if (hash_found) + { + retval = entry -> hash_value; + entry -> hash_value = value; + } + else + { + retval = NULL; + } + ; + return (retval); +} + +/* + * h a s h _ i n s e r t ( ) + * + * Insert a (symbol-string, value) into the hash table. + * Return an error string, "" means OK. + * It is an 'error' to insert an existing symbol. + */ + +char * /* return error string */ +hash_insert(handle,string,value) + register struct hash_control * handle; + register char * string; + register char * value; +{ + register struct hash_entry * entry; + register char * retval; + + retval = ""; + if (handle->hash_stat[STAT_USED] > handle->hash_full) + { + retval = hash_grow(handle); + } + if ( ! * retval) + { + entry = hash_ask(handle,string,STAT__WRITE); + if (hash_found) + { + retval = "exists"; + } + else + { + entry -> hash_value = value; + entry -> hash_string = string; + handle-> hash_stat[STAT_USED] += 1; + } + } + return(retval); +} + +/* + * h a s h _ j a m ( ) + * + * Regardless of what was in the symbol table before, after hash_jam() + * the named symbol has the given value. The symbol is either inserted or + * (its value is) relpaced. + * An error message string is returned, "" means OK. + * + * WARNING: this may decide to grow the hashed symbol table. + * To do this, we call hash_grow(), WHICH WILL recursively CALL US. + * + * We report status internally: hash_found is TRUE if we replaced, but + * false if we inserted. + */ +char * +hash_jam(handle,string,value) + register struct hash_control * handle; + register char * string; + register char * value; +{ + register char * retval; + register struct hash_entry * entry; + + retval = ""; + if (handle->hash_stat[STAT_USED] > handle->hash_full) + { + retval = hash_grow(handle); + } + if (! * retval) + { + entry = hash_ask(handle,string,STAT__WRITE); + if ( ! hash_found) + { + entry -> hash_string = string; + handle->hash_stat[STAT_USED] += 1; + } + entry -> hash_value = value; + } + return(retval); +} + +/* + * h a s h _ g r o w ( ) + * + * Grow a new (bigger) hash table from the old one. + * We choose to double the hash table's size. + * Return a human-scrutible error string: "" if OK. + * Warning! This uses hash_jam(), which had better not recurse + * back here! Hash_jam() conditionally calls us, but we ALWAYS + * call hash_jam()! + * Internal. + */ +static char * +hash_grow(handle) /* make a hash table grow */ + struct hash_control * handle; +{ + register struct hash_entry * newwall; + register struct hash_entry * newwhere; + struct hash_entry * newtrack; + register struct hash_entry * oldtrack; + register struct hash_entry * oldwhere; + register struct hash_entry * oldwall; + register int temp; + int newsize; + char * string; + char * retval; +#ifdef SUSPECT + int oldused; +#endif + + /* + * capture info about old hash table + */ + oldwhere = handle -> hash_where; + oldwall = handle -> hash_wall; +#ifdef SUSPECT + oldused = handle -> hash_stat[STAT_USED]; +#endif + /* + * attempt to get enough room for a hash table twice as big + */ + temp = handle->hash_stat[STAT_SIZE]; + if ( newwhere = (struct hash_entry *) xmalloc((long)((temp+temp+1)*sizeof(struct hash_entry)))) + /* +1 for wall slot */ + { + retval = ""; /* assume success until proven otherwise */ + /* + * have enough room: now we do all the work. + * double the size of everything in handle, + * note: hash_mask frob works for 1's & for 2's complement machines + */ + handle->hash_mask = handle->hash_mask + handle->hash_mask + 1; + handle->hash_stat[STAT_SIZE] <<= 1; + newsize = handle->hash_stat[STAT_SIZE]; + handle->hash_where = newwhere; + handle->hash_full <<= 1; + handle->hash_sizelog += 1; + handle->hash_stat[STAT_USED] = 0; + handle->hash_wall = + newwall = newwhere + newsize; + /* + * set all those pesky new slots to vacant. + */ + for (newtrack=newwhere; newtrack <= newwall; newtrack++) + { + newtrack -> hash_string = NULL; + } + /* + * we will do a scan of the old table, the hard way, using the + * new control block to re-insert the data into new hash table. + */ + handle -> hash_stat[STAT_USED] = 0; /* inserts will bump it up to correct */ + for (oldtrack=oldwhere; oldtrack < oldwall; oldtrack++) + { + if ( (string=oldtrack->hash_string) && string!=DELETED ) + { + if ( * (retval = hash_jam(handle,string,oldtrack->hash_value) ) ) + { + break; + } + } + } +#ifdef SUSPECT + if ( !*retval && handle->hash_stat[STAT_USED] != oldused) + { + retval = "hash_used"; + } +#endif + if (!*retval) + { + /* + * we have a completely faked up control block. + * return the old hash table. + */ + free((char *)oldwhere); + /* + * Here with success. retval is already "". + */ + } + } + else + { + retval = "no room"; + } + return(retval); +} + +/* + * h a s h _ a p p l y ( ) + * + * Use this to scan each entry in symbol table. + * For each symbol, this calls (applys) a nominated function supplying the + * symbol's value (and the symbol's name). + * The idea is you use this to destroy whatever is associted with + * any values in the table BEFORE you destroy the table with hash_die. + * Of course, you can use it for other jobs; whenever you need to + * visit all extant symbols in the table. + * + * We choose to have a call-you-back idea for two reasons: + * asthetic: it is a neater idea to use apply than an explicit loop + * sensible: if we ever had to grow the symbol table (due to insertions) + * then we would lose our place in the table when we re-hashed + * symbols into the new table in a different order. + * + * The order symbols are visited depends entirely on the hashing function. + * Whenever you insert a (symbol, value) you risk expanding the table. If + * you do expand the table, then the hashing function WILL change, so you + * MIGHT get a different order of symbols visited. In other words, if you + * want the same order of visiting symbols as the last time you used + * hash_apply() then you better not have done any hash_insert()s or + * hash_jam()s since the last time you used hash_apply(). + * + * In future we may use the value returned by your nominated function. + * One idea is to abort the scan if, after applying the function to a + * certain node, the function returns a certain code. + * To be safe, please make your functions of type char *. If you always + * return NULL, then the scan will complete, visiting every symbol in + * the table exactly once. ALL OTHER RETURNED VALUES have no meaning yet! + * Caveat Actor! + * + * The function you supply should be of the form: + * char * myfunct(string,value) + * char * string; |* the symbol's name *| + * char * value; |* the symbol's value *| + * { + * |* ... *| + * return(NULL); + * } + * + * The returned value of hash_apply() is (char*)NULL. In future it may return + * other values. NULL means "completed scan OK". Other values have no meaning + * yet. (The function has no graceful failures.) + */ +char * +hash_apply(handle,function) + struct hash_control * handle; + char* (*function)(); +{ + register struct hash_entry * entry; + register struct hash_entry * wall; + + wall = handle->hash_wall; + for (entry = handle->hash_where; entry < wall; entry++) + { + if (islive(entry)) /* silly code: tests entry->string twice! */ + { + (*function)(entry->hash_string,entry->hash_value); + } + } + return (NULL); +} + +/* + * h a s h _ f i n d ( ) + * + * Given symbol string, find value (if any). + * Return found value or NULL. + */ +char * +hash_find(handle,string) /* return char* or NULL */ + struct hash_control * handle; + char * string; +{ + register struct hash_entry * entry; + register char * retval; + + entry = hash_ask(handle,string,STAT__READ); + if (hash_found) + { + retval = entry->hash_value; + } + else + { + retval = NULL; + } + return(retval); +} + +/* + * h a s h _ a s k ( ) + * + * Searches for given symbol string. + * Return the slot where it OUGHT to live. It may be there. + * Return hash_found: TRUE only if symbol is in that slot. + * Access argument is to help keep statistics in control block. + * Internal. + */ +static struct hash_entry * /* string slot, may be empty or deleted */ +hash_ask(handle,string,access) + struct hash_control * handle; + char * string; + int access; /* access type */ +{ + register char *string1; /* JF avoid strcmp calls */ + register char * s; + register int c; + register struct hash_entry * slot; + register int collision; /* count collisions */ + + slot = handle->hash_where + hash_code(handle,string); /* start looking here */ + handle->hash_stat[STAT_ACCESS+access] += 1; + collision = 0; + hash_found = FALSE; + while ( (s = slot->hash_string) && s!=DELETED ) + { + for(string1=string;;) { + if(!(c= *s++)) { + if(!*string1) + hash_found = TRUE; + break; + } + if(*string1++!=c) + break; + } + if(hash_found) + break; + collision++; + slot++; + } + /* + * slot: return: + * in use: we found string slot + * at empty: + * at wall: we fell off: wrap round ???? + * in table: dig here slot + * at DELETED: dig here slot + */ + if (slot==handle->hash_wall) + { + slot = handle->hash_where; /* now look again */ + while( (s = slot->hash_string) && s!=DELETED ) + { + for(string1=string;*s;string1++,s++) { + if(*string1!=*s) + break; + } + if(*s==*string1) { + hash_found = TRUE; + break; + } + collision++; + slot++; + } + /* + * slot: return: + * in use: we found it slot + * empty: wall: ERROR IMPOSSIBLE !!!! + * in table: dig here slot + * DELETED:dig here slot + */ + } +/* fprintf(stderr,"hash_ask(%s)->%d(%d)\n",string,hash_code(handle,string),collision); */ + handle -> hash_stat[STAT_COLLIDE+access] += collision; + return(slot); /* also return hash_found */ +} + +/* + * h a s h _ c o d e + * + * Does hashing of symbol string to hash number. + * Internal. + */ +static int +hash_code(handle,string) + struct hash_control * handle; + register char * string; +{ + register long int h; /* hash code built here */ + register long int c; /* each character lands here */ + register int n; /* Amount to shift h by */ + + n = (handle->hash_sizelog - 3); + h = 0; + while (c = *string++) + { + h += c; + h = (h<<3) + (h>>n) + c; + } + return (h & handle->hash_mask); +} + +/* + * Here is a test program to exercise above. + */ +#ifdef TEST + +#define TABLES (6) /* number of hash tables to maintain */ + /* (at once) in any testing */ +#define STATBUFSIZE (12) /* we can have 12 statistics */ + +int statbuf[STATBUFSIZE]; /* display statistics here */ +char answer[100]; /* human farts here */ +char * hashtable[TABLES]; /* we test many hash tables at once */ +char * h; /* points to curent hash_control */ +char ** pp; +char * p; +char * name; +char * value; +int size; +int used; +char command; +int number; /* number 0:TABLES-1 of current hashed */ + /* symbol table */ + +main() +{ + char (*applicatee()); + char * hash_find(); + char * destroy(); + char * what(); + struct hash_control * hash_new(); + char * hash_replace(); + int * ip; + + number = 0; + h = 0; + printf("type h for help\n"); + for(;;) + { + printf("hash_test command: "); + gets(answer); + command = answer[0]; + if (isupper(command)) command = tolower(command); /* ecch! */ + switch (command) + { + case '#': + printf("old hash table #=%d.\n",number); + whattable(); + break; + case '?': + for (pp=hashtable; pp=0 && numberradix-value - + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * Export: Hex_value[]. Converts digits to their radix-values. + * As distributed assumes 8 bits per char (256 entries) and ASCII. + */ + +#define __ (42) /* blatently illegal digit value */ + /* exceeds any normal radix */ +#if !defined(__STDC__) && !defined(const) +#define const /* empty */ +#endif +const char +hex_value [256] = { /* for fast ASCII -> binary */ + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, __, __, __, __, __, __, + __, 10, 11, 12, 13, 14, 15, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, 10, 11, 12, 13, 14, 15, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __ + }; + +#ifdef VMS +dummy2() +{ +} +#endif +/* end:hex_value.c */ diff --git a/gnu/usr.bin/as/input-file.c b/gnu/usr.bin/as/input-file.c new file mode 100644 index 0000000000..8de1dcf153 --- /dev/null +++ b/gnu/usr.bin/as/input-file.c @@ -0,0 +1,306 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + */ + +#ifndef lint +static char sccsid[] = "@(#)input-file.c 6.2 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* input_file.c - Deal with Input Files - + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * Confines all details of reading source bytes to this module. + * All O/S specific crocks should live here. + * What we lose in "efficiency" we gain in modularity. + * Note we don't need to #include the "as.h" file. No common coupling! + */ + +#define NDEBUG /* JF remove asserts */ + +#ifdef USG +#define index strchr +/* JF: What's the difference between _IOLBF and _IOFBF ? */ +#define setbuffer(stream, buf, size) setvbuf((stream), (buf), _IOFBF, (size)) +#endif + +#include +#include +/* #include +#include +#include +#include */ + +/* #include "style.h" */ +#include "input-file.h" + +/* This variable is non-zero if the file currently being read should be + preprocessed by app. It is zero if the file can be read straight in. + */ +int preprocess = 0; + +void as_perror(); + +/* + * This code opens a file, then delivers BUFFER_SIZE character + * chunks of the file on demand. + * BUFFER_SIZE is supposed to be a number chosen for speed. + * The caller only asks once what BUFFER_SIZE is, and asks before + * the nature of the input files (if any) is known. + */ + +#define BUFFER_SIZE (32 * 1024) + +static char in_buf[BUFFER_SIZE]; + +/* + * We use static data: the data area is not sharable. + */ + +FILE *f_in; /* JF do things the RIGHT way */ +/* static JF remove static so app.c can use file_name */ +char * file_name; + +/* These hooks accomodate most operating systems. */ + +void +input_file_begin () +{ + /* file_handle = -1; */ + f_in = (FILE *)0; +} + +void +input_file_end () +{ +} + +int /* Return BUFFER_SIZE. */ +input_file_buffer_size () +{ + return (BUFFER_SIZE); +} + +int +input_file_is_open () +{ + /* return (file_handle >= 0); */ + return f_in!=(FILE *)0; +} + +#ifdef DONTDEF /* JF save old version in case we need it */ +void +input_file_open (filename, preprocess, debugging) + char * filename; /* "" means use stdin. Must not be 0. */ + int preprocess; /* TRUE if needs app. */ + int debugging; /* TRUE if we are debugging assembler. */ +{ + assert( filename != 0 ); /* Filename may not be NULL. */ + if (filename [0]) + { /* We have a file name. Suck it and see. */ + file_handle = open (filename, O_RDONLY, 0); + file_name = filename; + } + else + { /* use stdin for the input file. */ + file_handle = fileno (stdin); + file_name = "{standard input}"; /* For error messages. */ + } + if (file_handle < 0) + as_perror ("Can't open %s for reading", file_name); + if ( preprocess ) + { +/* + * This code was written in haste for a frobbed BSD 4.2. + * I have a flight to catch: will someone please do proper + * error checks? - Dean. + */ + int pid; + char temporary_file_name [12]; + int fd; + union wait status; + char *mktemp(); + + (void)strcpy (temporary_file_name, "#appXXXXXX"); + (void)mktemp (temporary_file_name); + pid = vfork (); + if (pid == -1) + { + as_perror ("Vfork failed", file_name); + _exit (144); + } + if (pid == 0) + { + (void)dup2 (file_handle, fileno(stdin)); + fd = open (temporary_file_name, O_WRONLY + O_TRUNC + O_CREAT, 0666); + if (fd == -1) + { + (void)write(2,"Can't open temporary\n",21); + _exit (99); + } + (void)dup2 (fd, fileno(stdout)); +/* JF for testing #define PREPROCESSOR "/lib/app" */ +#define PREPROCESSOR "./app" + execl (PREPROCESSOR, PREPROCESSOR, 0); + execl ("app","app",0); + (void)write(2,"Exec of app failed. Get help.\n",31); + (void)unlink(temporary_file_name); + _exit (11); + } + (void)wait (& status); + if (status.w_status & 0xFF00) /* JF was 0xF000, was wrong */ + { + file_handle = -1; + as_warn( "Can't preprocess file \"%s\", status = %xx", file_name, status.w_status ); + } + else + { + file_handle = open (temporary_file_name, O_RDONLY, 0); + if ( ! debugging && unlink(temporary_file_name)) + as_perror ("Can't delete temp file %s", temporary_file_name); + } + if (file_handle == -1) + as_perror ("Can't retrieve temp file %s", temporary_file_name); + } +} +#else + +void +input_file_open (filename,pre) + char * filename; /* "" means use stdin. Must not be 0. */ + int pre; +{ + int c; + char buf[80]; + + preprocess = pre; + + assert( filename != 0 ); /* Filename may not be NULL. */ + if (filename [0]) { /* We have a file name. Suck it and see. */ + f_in=fopen(filename,"r"); + file_name=filename; + } else { /* use stdin for the input file. */ + f_in = stdin; + file_name = "{standard input}"; /* For error messages. */ + } + if (f_in==(FILE *)0) { + as_perror ("Can't open %s for reading", file_name); + return; + } +#ifndef VMS + setbuffer(f_in,in_buf,BUFFER_SIZE); +#endif /* VMS */ + c=getc(f_in); + if(c=='#') { /* Begins with comment, may not want to preprocess */ + c=getc(f_in); + if(c=='N') { + fgets(buf,80,f_in); + if(!strcmp(buf,"O_APP\n")) + preprocess=0; + if(!index(buf,'\n')) + ungetc('#',f_in); /* It was longer */ + else + ungetc('\n',f_in); + } else if(c=='\n') + ungetc('\n',f_in); + else + ungetc('#',f_in); + } else + ungetc(c,f_in); + +#ifdef DONTDEF + if ( preprocess ) { + char temporary_file_name [17]; + char *mktemp(); + FILE *f_out; + + (void)strcpy (temporary_file_name, "/tmp/#appXXXXXX"); + (void)mktemp (temporary_file_name); + f_out=fopen(temporary_file_name,"w+"); + if(f_out==(FILE *)0) + as_perror("Can't open temp file %s",temporary_file_name); + + /* JF this will have to be moved on any system that + does not support removal of open files. */ + (void)unlink(temporary_file_name);/* JF do it NOW */ + do_scrub(f_in,f_out); + (void)fclose(f_in); /* All done with it */ + (void)rewind(f_out); + f_in=f_out; + } +#endif +} +#endif + +char * +input_file_give_next_buffer (where) + char * where; /* Where to place 1st character of new buffer. */ +{ + char * return_value; /* -> Last char of what we read, + 1. */ + register int size; + + if (f_in == (FILE *)0) + return 0; + /* + * fflush (stdin); could be done here if you want to synchronise + * stdin and stdout, for the case where our input file is stdin. + * Since the assembler shouldn't do any output to stdout, we + * don't bother to synch output and input. + */ + /* size = read (file_handle, where, BUFFER_SIZE); */ + if(preprocess) { + char *p; + int n; + int ch; + extern FILE *scrub_file; + int scrub_from_file(); + void scrub_to_file(); + int do_scrub_next_char(); + + scrub_file=f_in; + for(p=where,n=BUFFER_SIZE;n;--n) { + ch=do_scrub_next_char(scrub_from_file,scrub_to_file); + if(ch==EOF) + break; + *p++=ch; + } + size=BUFFER_SIZE-n; + } else + size= fread(where,sizeof(char),BUFFER_SIZE,f_in); + if (size < 0) + { + as_perror ("Can't read from %s", file_name); + size = 0; + } + if (size) + return_value = where + size; + else + { + if (fclose (f_in)) + as_perror ("Can't close %s", file_name); + f_in = (FILE *)0; + return_value = 0; + } + return (return_value); +} + +/* end: input_file.c */ diff --git a/gnu/usr.bin/as/input-file.h b/gnu/usr.bin/as/input-file.h new file mode 100644 index 0000000000..42f490aba4 --- /dev/null +++ b/gnu/usr.bin/as/input-file.h @@ -0,0 +1,57 @@ +/* input_file.h header for input-file.c + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/*"input_file.c":Operating-system dependant functions to read source files.*/ + + +/* + * No matter what the operating system, this module must provide the + * following services to its callers. + * + * input_file_begin() Call once before anything else. + * + * input_file_end() Call once after everything else. + * + * input_file_buffer_size() Call anytime. Returns largest possible + * delivery from + * input_file_give_next_buffer(). + * + * input_file_open(name) Call once for each input file. + * + * input_file_give_next_buffer(where) Call once to get each new buffer. + * Return 0: no more chars left in file, + * the file has already been closed. + * Otherwise: return a pointer to just + * after the last character we read + * into the buffer. + * If we can only read 0 characters, then + * end-of-file is faked. + * + * All errors are reported (using as_perror) so caller doesn't have to think + * about I/O errors. No I/O errors are fatal: an end-of-file may be faked. + */ + +void input_file_begin(); +void input_file_end(); +int input_file_buffer_size(); +int input_file_is_open(); +void input_file_open(); +char * input_file_give_next_buffer(); + +/* end: input_file.h */ diff --git a/gnu/usr.bin/as/input-scrub.c b/gnu/usr.bin/as/input-scrub.c new file mode 100644 index 0000000000..71af8a0bf8 --- /dev/null +++ b/gnu/usr.bin/as/input-scrub.c @@ -0,0 +1,427 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + */ + +#ifndef lint +static char sccsid[] = "@(#)input-scrub.c 6.4 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* input_scrub.c - layer between app and the rest of the world + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "as.h" +#include "read.h" +#include "input-file.h" + +/* + * O/S independent module to supply buffers of sanitised source code + * to rest of assembler. We get raw input data of some length. + * Also looks after line numbers, for e.g. error messages. + * This module used to do the sanitising, but now a pre-processor program + * (app) does that job so this module is degenerate. + * Now input is pre-sanitised, so we only worry about finding the + * last partial line. A buffer of full lines is returned to caller. + * The last partial line begins the next buffer we build and return to caller. + * The buffer returned to caller is preceeded by BEFORE_STRING and followed + * by AFTER_STRING. The last character before AFTER_STRING is a newline. + */ + +/* + * We expect the following sanitation has already been done. + * + * No comments, reduce a comment to a space. + * Reduce a tab to a space unless it is 1st char of line. + * All multiple tabs and spaces collapsed into 1 char. Tab only + * legal if 1st char of line. + * # line file statements converted to .line x;.file y; statements. + * Escaped newlines at end of line: remove them but add as many newlines + * to end of statement as you removed in the middle, to synch line numbers. + */ + +#define BEFORE_STRING ("\n") +#define AFTER_STRING ("\0") /* bcopy of 0 chars might choke. */ +#define BEFORE_SIZE (1) +#define AFTER_SIZE (1) + +static char * buffer_start; /* -> 1st char of full buffer area. */ +static char * partial_where; /* -> after last full line in buffer. */ +static int partial_size; /* >=0. Number of chars in partial line in buffer. */ +static char save_source [AFTER_SIZE]; + /* Because we need AFTER_STRING just after last */ + /* full line, it clobbers 1st part of partial */ + /* line. So we preserve 1st part of partial */ + /* line here. */ +static int buffer_length; /* What is the largest size buffer that */ + /* input_file_give_next_buffer() could */ + /* return to us? */ + +static void as_1_char (); + +/* +We never have more than one source file open at once. +We may, however, read more than 1 source file in an assembly. +NULL means we have no file open right now. +*/ + + +/* +We must track the physical file and line number for error messages. +We also track a "logical" file and line number corresponding to (C?) +compiler source line numbers. +Whenever we open a file we must fill in physical_input_file. So if it is NULL +we have not opened any files yet. +*/ + +static +char * physical_input_file, + * logical_input_file; + + + +typedef unsigned int line_numberT; /* 1-origin line number in a source file. */ + /* A line ends in '\n' or eof. */ + +static +line_numberT physical_input_line, + logical_input_line; + +void +input_scrub_begin () +{ + know( strlen(BEFORE_STRING) == BEFORE_SIZE ); + know( strlen( AFTER_STRING) == AFTER_SIZE ); + + input_file_begin (); + + buffer_length = input_file_buffer_size (); + + buffer_start = xmalloc ((long)(BEFORE_SIZE + buffer_length + buffer_length + AFTER_SIZE)); + bcopy (BEFORE_STRING, buffer_start, (int)BEFORE_SIZE); + + /* Line number things. */ + logical_input_line = 0; + logical_input_file = (char *)NULL; + physical_input_file = NULL; /* No file read yet. */ + do_scrub_begin(); +} + +void +input_scrub_end () +{ + input_file_end (); +} + +char * /* Return start of caller's part of buffer. */ +input_scrub_new_file (filename) + char * filename; +{ + input_file_open (filename, !flagseen['f']); + physical_input_file = filename[0] ? filename : "{standard input}"; + physical_input_line = 0; + + partial_size = 0; + return (buffer_start + BEFORE_SIZE); +} + +char * +input_scrub_next_buffer (bufp) +char **bufp; +{ + register char * limit; /* -> just after last char of buffer. */ + +#ifdef DONTDEF + if(preprocess) { + if(save_buffer) { + *bufp = save_buffer; + save_buffer = 0; + } + limit = input_file_give_next_buffer(buffer_start+BEFORE_SIZE); + if (!limit) { + partial_where = 0; + if(partial_size) + as_warn("Partial line at end of file ignored"); + return partial_where; + } + + if(partial_size) + bcopy(save_source, partial_where,(int)AFTER_SIZE); + do_scrub(partial_where,partial_size,buffer_start+BEFORE_SIZE,limit-(buffer_start+BEFORE_SIZE),&out_string,&out_length); + limit=out_string + out_length; + for(p=limit;*--p!='\n';) + ; + p++; + if(p<=buffer_start+BEFORE_SIZE) + as_fatal("Source line too long. Please change file '%s' and re-make the assembler.",__FILE__); + + partial_where = p; + partial_size = limit-p; + bcopy(partial_where, save_source,(int)AFTER_SIZE); + bcopy(AFTER_STRING, partial_where, (int)AFTER_SIZE); + + save_buffer = *bufp; + *bufp = out_string; + + return partial_where; + } + + /* We're not preprocessing. Do the right thing */ +#endif + if (partial_size) + { + bcopy (partial_where, buffer_start + BEFORE_SIZE, (int)partial_size); + bcopy (save_source, buffer_start + BEFORE_SIZE, (int)AFTER_SIZE); + } + limit = input_file_give_next_buffer (buffer_start + BEFORE_SIZE + partial_size); + if (limit) + { + register char * p; /* Find last newline. */ + + for (p = limit; * -- p != '\n'; ) + { + } + ++ p; + if (p <= buffer_start + BEFORE_SIZE) + { + as_fatal ("Source line too long. Please change file %s then rebuild assembler.", __FILE__); + } + partial_where = p; + partial_size = limit - p; + bcopy (partial_where, save_source, (int)AFTER_SIZE); + bcopy (AFTER_STRING, partial_where, (int)AFTER_SIZE); + } + else + { + partial_where = 0; + if (partial_size > 0) + { + as_warn( "Partial line at end of file ignored" ); + } + } + return (partial_where); +} + +/* + * The remaining part of this file deals with line numbers, error + * messages and so on. + */ + + +int +seen_at_least_1_file () /* TRUE if we opened any file. */ +{ + return (physical_input_file != NULL); +} + +void +bump_line_counters () +{ + ++ physical_input_line; + ++ logical_input_line; +} + +/* + * new_logical_line() + * + * Tells us what the new logical line number and file are. + * If the line_number is <0, we don't change the current logical line number. + * If the fname is NULL, we don't change the current logical file name. + */ +void +new_logical_line (fname, line_number) + char * fname; /* DON'T destroy it! We point to it! */ + int line_number; +{ + if ( fname ) + { + logical_input_file = fname; + } + if ( line_number >= 0 ) + { + logical_input_line = line_number; + } +} + +/* + * a s _ w h e r e ( ) + * + * Write a line to stderr locating where we are in reading + * input source files. + * As a sop to the debugger of AS, pretty-print the offending line. + */ +void +as_where() +{ + char *p; + line_numberT line; + + if (physical_input_file) + { /* we tried to read SOME source */ + if (input_file_is_open()) + { /* we can still read lines from source */ +#ifdef DONTDEF + fprintf (stderr," @ physical line %ld., file \"%s\"", + (long) physical_input_line, physical_input_file); + fprintf (stderr," @ logical line %ld., file \"%s\"\n", + (long) logical_input_line, logical_input_file); + (void)putc(' ', stderr); + as_howmuch (stderr); + (void)putc('\n', stderr); +#else + p = logical_input_file ? logical_input_file : physical_input_file; + line = logical_input_line ? logical_input_line : physical_input_line; + fprintf(stderr,"%s:%u:", p, line); +#endif + } + else + { +#ifdef DONTDEF + fprintf (stderr," After reading source.\n"); +#else + p = logical_input_file ? logical_input_file : physical_input_file; + line = logical_input_line ? logical_input_line : physical_input_line; + fprintf (stderr,"%s:unknown:", p); +#endif + } + } + else + { +#ifdef DONTDEF + fprintf (stderr," Before reading source.\n"); +#else +#endif + } +} + +/* + * Support for source file debugging. These functions handle + * logical lines and logical files. + */ +static char *saved_file; +static int saved_len; +static line_numberT saved_line; + +void +filestab() +{ + char *file; + int len; + + if (!physical_input_file || + !input_file_is_open()) + return; + + file = logical_input_file ? logical_input_file : physical_input_file; + + if (saved_file == 0 || strcmp(file, saved_file) != 0) + { + stabs(file); + len = strlen(file) + 1; + if (len > saved_len) + { + if (saved_file == 0) + saved_file = xmalloc(len); + else + saved_file = xrealloc(saved_file, len); + memcpy(saved_file, file, len); + saved_len = len; + } + else + strcpy(saved_file, file); + saved_line = 0; + } +} + +void +funcstab(func) + char *func; +{ + if (now_seg != SEG_TEXT) + return; + + filestab(); + stabf(func); +} + +void +linestab() +{ + line_numberT line; + + if (now_seg != SEG_TEXT) + return; + + filestab(); + + line = logical_input_line ? logical_input_line : physical_input_line; + + if (saved_line == 0 || line != saved_line) + { + stabd(line); + saved_line = line; + } +} + +/* + * a s _ h o w m u c h ( ) + * + * Output to given stream how much of line we have scanned so far. + * Assumes we have scanned up to and including input_line_pointer. + * No free '\n' at end of line. + */ +void +as_howmuch (stream) + FILE * stream; /* Opened for write please. */ +{ + register char * p; /* Scan input line. */ + /* register char c; JF unused */ + + for (p = input_line_pointer - 1; * p != '\n'; --p) + { + } + ++ p; /* p -> 1st char of line. */ + for (; p <= input_line_pointer; p++) + { + /* Assume ASCII. EBCDIC & other micro-computer char sets ignored. */ + /* c = *p & 0xFF; JF unused */ + as_1_char (*p, stream); + } +} + +static void +as_1_char (c,stream) + unsigned char c; + FILE * stream; +{ + if ( c > 127 ) + { + (void)putc( '%', stream); + c -= 128; + } + if ( c < 32 ) + { + (void)putc( '^', stream); + c += '@'; + } + (void)putc( c, stream); +} + +/* end: input_scrub.c */ diff --git a/gnu/usr.bin/as/md.h b/gnu/usr.bin/as/md.h new file mode 100644 index 0000000000..681d027e52 --- /dev/null +++ b/gnu/usr.bin/as/md.h @@ -0,0 +1,57 @@ +/* md.h -machine dependent- */ + +/* Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of Gas, the GNU Assembler. + +The GNU assembler is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY. No author or distributor +accepts responsibility to anyone for the consequences of using it +or for whether it serves any particular purpose or works at all, +unless he says so in writing. Refer to the GNU Assembler General +Public License for full details. + +Everyone is granted permission to copy, modify and redistribute +the GNU Assembler, but only under the conditions described in the +GNU Assembler General Public License. A copy of this license is +supposed to have been given to you along with the GNU Assembler +so you can know your rights and responsibilities. It should be +in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. */ + +/* In theory (mine, at least!) the machine dependent part of the assembler + should only have to include one file. This one. -- JF */ + +/* JF added this here */ +typedef struct { + char * poc_name; /* assembler mnemonic, lower case, no '.' */ + void (*poc_handler)(); /* Do the work */ + int poc_val; /* Value to pass to handler */ +} +pseudo_typeS; +extern const pseudo_typeS md_pseudo_table[]; + +/* JF moved this here from as.h under the theory that nobody except MACHINE.c + and write.c care about it anyway. */ + +typedef struct +{ + long rlx_forward; /* Forward reach. Signed number. > 0. */ + long rlx_backward; /* Backward reach. Signed number. < 0. */ + unsigned char rlx_length; /* Bytes length of this address. */ + relax_substateT rlx_more; /* Next longer relax-state. */ + /* 0 means there is no 'next' relax-state. */ +} +relax_typeS; + +extern const relax_typeS md_relax_table[]; /* Define it in MACHINE.c */ + +char * md_atof(); +void md_assemble(); +void md_begin(); +void md_convert_frag(); +void md_end(); +int md_estimate_size_before_relax(); +void md_number_to_chars(); + +/* end: md.h */ diff --git a/gnu/usr.bin/as/messages.c b/gnu/usr.bin/as/messages.c new file mode 100644 index 0000000000..a7b1205287 --- /dev/null +++ b/gnu/usr.bin/as/messages.c @@ -0,0 +1,238 @@ +/* messages.c - error reporter - + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include /* define stderr */ +#ifdef VMS +#include /* Need this to make errno declaration right */ +#include /* Need this to make sys_errlist/sys_nerr right */ +#endif /* VMS */ + +#include "as.h" + +#ifndef NO_VARARGS +#include +#endif + +/* + ERRORS + + JF: this is now bogus. We now print more standard error messages + that try to look like everyone else's. + + We print the error message 1st, beginning in column 1. + All ancillary info starts in column 2 on lines after the + key error text. + We try to print a location in logical and physical file + just after the main error text. + Caller then prints any appendices after that, begining all + lines with at least 1 space. + + Optionally, we may die. + There is no need for a trailing '\n' in your error text format + because we supply one. + +as_warn(fmt,args) Like fprintf(stderr,fmt,args) but also call errwhere(). + +as_fatal(fmt,args) Like as_warn() but exit with a fatal status. + +*/ + + +/* Nonzero if we've hit a 'bad error', and should not write an obj file, + and exit with a nonzero error code */ + +int bad_error = 0; + + +/* + * a s _ p e r r o r + * + * Like perror(3), but with more info. + */ +/* JF moved from input-scrub.c to here. */ +void +as_perror(gripe, filename) + char * gripe; /* Unpunctuated error theme. */ + char * filename; +{ + extern int errno; /* See perror(3) for details. */ + extern int sys_nerr; + extern char * sys_errlist[]; + + as_where(); + fprintf (stderr,gripe,filename); + if (errno > sys_nerr) + fprintf (stderr, "Unknown error #%d.\n", errno); + else + fprintf (stderr, "%s.\n", sys_errlist [errno]); + errno = 0; /* After reporting, clear it. */ +} + +/* + * a s _ w a r n ( ) + * + * Send to stderr a string (with bell) (JF: Bell is obnoxious!) as a warning, and locate warning + * in input file(s). + * Please only use this for when we have some recovery action. + * Please explain in string (which may have '\n's) what recovery was done. + */ + +#ifdef NO_VARARGS +/*VARARGS1*/ +as_warn(Format,args) +char *Format; +{ + if ( ! flagseen ['W']) /* -W supresses warning messages. */ + { + as_where(); + _doprnt (Format, &args, stderr); + (void)putc ('\n', stderr); + /* as_where(); */ + } +} +#else +void +as_warn(Format,va_alist) +char *Format; +va_dcl +{ + va_list args; + + if( ! flagseen['W']) + { + as_where(); + va_start(args); + vfprintf(stderr, Format, args); + va_end(args); + (void) putc('\n', stderr); + } +} +#endif +#ifdef DONTDEF +void +as_warn(Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an) +char *format; +{ + if(!flagseen['W']) { + as_where(); + fprintf(stderr,Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an); + (void)putc('\n',stderr); + } +} +#endif +/* + * a s _ b a d ( ) + * + * Send to stderr a string (with bell) (JF: Bell is obnoxious!) as a warning, + * and locate warning in input file(s). + * Please us when there is no recovery, but we want to continue processing + * but not produce an object file. + * Please explain in string (which may have '\n's) what recovery was done. + */ + +#ifdef NO_VARARGS +/*VARARGS1*/ +as_bad(Format,args) +char *Format; +{ + bad_error=1; + as_where(); + _doprnt (Format, &args, stderr); + (void)putc ('\n', stderr); + /* as_where(); */ +} +#else +void +as_bad(Format,va_alist) +char *Format; +va_dcl +{ + va_list args; + + bad_error=1; + as_where(); + va_start(args); + vfprintf(stderr, Format, args); + va_end(args); + (void) putc('\n', stderr); +} +#endif +#ifdef DONTDEF +void +as_bad(Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an) +char *format; +{ + as_where(); + bad_error=1; + fprintf(stderr,Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an); + (void)putc('\n',stderr); +} +#endif +/* + * a s _ f a t a l ( ) + * + * Send to stderr a string (with bell) (JF: Bell is obnoxious!) as a fatal + * message, and locate stdsource in input file(s). + * Please only use this for when we DON'T have some recovery action. + * It exit()s with a warning status. + */ + +#ifdef NO_VARARGS +/*VARARGS1*/ +as_fatal (Format, args) +char *Format; +{ + as_where(); + fprintf(stderr,"FATAL:"); + _doprnt (Format, &args, stderr); + (void)putc ('\n', stderr); + /* as_where(); */ + exit(42); /* What is a good exit status? */ +} +#else +void +as_fatal(Format,va_alist) +char *Format; +va_dcl +{ + va_list args; + + as_where(); + va_start(args); + fprintf (stderr, "FATAL:"); + vfprintf(stderr, Format, args); + (void) putc('\n', stderr); + va_end(args); + exit(42); +} +#endif +#ifdef DONTDEF +void +as_fatal(Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an) +char *Format; +{ + as_where(); + fprintf (stderr, "FATAL:"); + fprintf(stderr, Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an); + (void) putc('\n', stderr); + exit(42); +} +#endif + +/* end: messages.c */ diff --git a/gnu/usr.bin/as/objrecdef.h b/gnu/usr.bin/as/objrecdef.h new file mode 100644 index 0000000000..fca8af40f5 --- /dev/null +++ b/gnu/usr.bin/as/objrecdef.h @@ -0,0 +1,255 @@ +/* + * + * $OBJRECDEF + * Generated automatically by "vms_struct Version 1.00" + * Created from VMS definition file "objrecdef.mar" + * Mon Oct 14 14:01:29 1985 + * + */ +struct OBJREC { + unsigned char obj$b_rectyp; + unsigned char obj$b_subtyp; + unsigned char obj$b_mhd_strlv; + unsigned char obj$b_mhd_recsz[2]; + unsigned char obj$t_mhd_name[1]; + }; + +#define OBJ$C_HDR 0 +#define OBJ$C_HDR_MHD 0 +#define OBJ$C_HDR_LNM 1 +#define OBJ$C_HDR_SRC 2 +#define OBJ$C_HDR_TTL 3 +#define OBJ$C_HDR_CPR 4 +#define OBJ$C_HDR_MTC 5 +#define OBJ$C_HDR_GTX 6 +#define OBJ$C_GSD 1 +#define OBJ$C_GSD_PSC 0 +#define OBJ$C_GSD_SYM 1 +#define OBJ$C_GSD_EPM 2 +#define OBJ$C_GSD_PRO 3 +#define OBJ$C_GSD_SYMW 4 +#define OBJ$C_GSD_EPMW 5 +#define OBJ$C_GSD_PROW 6 +#define OBJ$C_GSD_IDC 7 +#define OBJ$C_GSD_ENV 8 +#define OBJ$C_GSD_LSY 9 +#define OBJ$C_GSD_LEPM 10 +#define OBJ$C_GSD_LPRO 11 +#define OBJ$C_GSD_SPSC 12 +#define OBJ$C_TIR 2 +#define OBJ$C_EOM 3 +#define OBJ$C_DBG 4 +#define OBJ$C_TBT 5 +#define OBJ$C_LNK 6 +#define OBJ$C_EOMW 7 +#define OBJ$C_MAXRECTYP 7 +#define OBJ$K_SUBTYP 1 +#define OBJ$C_SUBTYP 1 +#define OBJ$C_MAXRECSIZ 2048 +#define OBJ$C_STRLVL 0 +#define OBJ$C_SYMSIZ 31 +#define OBJ$C_STOREPLIM -1 +#define OBJ$C_PSCALILIM 9 + +#define MHD$C_MHD 0 +#define MHD$C_LNM 1 +#define MHD$C_SRC 2 +#define MHD$C_TTL 3 +#define MHD$C_CPR 4 +#define MHD$C_MTC 5 +#define MHD$C_GTX 6 +#define MHD$C_MAXHDRTYP 6 + +#define GSD$K_ENTRIES 1 +#define GSD$C_ENTRIES 1 +#define GSD$C_PSC 0 +#define GSD$C_SYM 1 +#define GSD$C_EPM 2 +#define GSD$C_PRO 3 +#define GSD$C_SYMW 4 +#define GSD$C_EPMW 5 +#define GSD$C_PROW 6 +#define GSD$C_IDC 7 +#define GSD$C_ENV 8 +#define GSD$C_LSY 9 +#define GSD$C_LEPM 10 +#define GSD$C_LPRO 11 +#define GSD$C_SPSC 12 +#define GSD$C_SYMV 13 +#define GSD$C_EPMV 14 +#define GSD$C_PROV 15 +#define GSD$C_MAXRECTYP 15 + +#define GSY$M_WEAK 1 +#define GSY$M_DEF 2 +#define GSY$M_UNI 4 +#define GSY$M_REL 8 + +#define GPS$M_PIC 1 +#define GPS$M_LIB 2 +#define GPS$M_OVR 4 +#define GPS$M_REL 8 +#define GPS$M_GBL 16 +#define GPS$M_SHR 32 +#define GPS$M_EXE 64 +#define GPS$M_RD 128 +#define GPS$M_WRT 256 +#define GPS$M_VEC 512 +#define GPS$K_NAME 9 +#define GPS$C_NAME 9 + +#define TIR$C_STA_GBL 0 +#define TIR$C_STA_SB 1 +#define TIR$C_STA_SW 2 +#define TIR$C_STA_LW 3 +#define TIR$C_STA_PB 4 +#define TIR$C_STA_PW 5 +#define TIR$C_STA_PL 6 +#define TIR$C_STA_UB 7 +#define TIR$C_STA_UW 8 +#define TIR$C_STA_BFI 9 +#define TIR$C_STA_WFI 10 +#define TIR$C_STA_LFI 11 +#define TIR$C_STA_EPM 12 +#define TIR$C_STA_CKARG 13 +#define TIR$C_STA_WPB 14 +#define TIR$C_STA_WPW 15 +#define TIR$C_STA_WPL 16 +#define TIR$C_STA_LSY 17 +#define TIR$C_STA_LIT 18 +#define TIR$C_STA_LEPM 19 +#define TIR$C_MAXSTACOD 19 +#define TIR$C_MINSTOCOD 20 +#define TIR$C_STO_SB 20 +#define TIR$C_STO_SW 21 +#define TIR$C_STO_L 22 +#define TIR$C_STO_BD 23 +#define TIR$C_STO_WD 24 +#define TIR$C_STO_LD 25 +#define TIR$C_STO_LI 26 +#define TIR$C_STO_PIDR 27 +#define TIR$C_STO_PICR 28 +#define TIR$C_STO_RSB 29 +#define TIR$C_STO_RSW 30 +#define TIR$C_STO_RL 31 +#define TIR$C_STO_VPS 32 +#define TIR$C_STO_USB 33 +#define TIR$C_STO_USW 34 +#define TIR$C_STO_RUB 35 +#define TIR$C_STO_RUW 36 +#define TIR$C_STO_B 37 +#define TIR$C_STO_W 38 +#define TIR$C_STO_RB 39 +#define TIR$C_STO_RW 40 +#define TIR$C_STO_RIVB 41 +#define TIR$C_STO_PIRR 42 +#define TIR$C_MAXSTOCOD 42 +#define TIR$C_MINOPRCOD 50 +#define TIR$C_OPR_NOP 50 +#define TIR$C_OPR_ADD 51 +#define TIR$C_OPR_SUB 52 +#define TIR$C_OPR_MUL 53 +#define TIR$C_OPR_DIV 54 +#define TIR$C_OPR_AND 55 +#define TIR$C_OPR_IOR 56 +#define TIR$C_OPR_EOR 57 +#define TIR$C_OPR_NEG 58 +#define TIR$C_OPR_COM 59 +#define TIR$C_OPR_INSV 60 +#define TIR$C_OPR_ASH 61 +#define TIR$C_OPR_USH 62 +#define TIR$C_OPR_ROT 63 +#define TIR$C_OPR_SEL 64 +#define TIR$C_OPR_REDEF 65 +#define TIR$C_OPR_DFLIT 66 +#define TIR$C_MAXOPRCOD 66 +#define TIR$C_MINCTLCOD 80 +#define TIR$C_CTL_SETRB 80 +#define TIR$C_CTL_AUGRB 81 +#define TIR$C_CTL_DFLOC 82 +#define TIR$C_CTL_STLOC 83 +#define TIR$C_CTL_STKDL 84 +#define TIR$C_MAXCTLCOD 84 + +/* + * Debugger symbol definitions: These are done by hand, as no + * machine-readable version seems + * to be available. + */ +#define DST$C_C 7 /* Language == "C" */ +#define DST$C_VERSION 153 +#define DST$C_SOURCE 155 /* Source file */ +#define DST$C_PROLOG 162 +#define DST$C_BLKBEG 176 /* Beginning of block */ +#define DST$C_BLKEND 177 /* End of block */ +#define DST$C_ENTRY 181 +#define DST$C_PSECT 184 +#define DST$C_LINE_NUM 185 /* Line Number */ +#define DST$C_LBLORLIT 186 +#define DST$C_LABEL 187 +#define DST$C_MODBEG 188 /* Beginning of module */ +#define DST$C_MODEND 189 /* End of module */ +#define DST$C_RTNBEG 190 /* Beginning of routine */ +#define DST$C_RTNEND 191 /* End of routine */ +#define DST$C_DELTA_PC_W 1 /* Incr PC */ +#define DST$C_INCR_LINUM 2 /* Incr Line # */ +#define DST$C_INCR_LINUM_W 3 /* Incr Line # */ +#define DST$C_SET_LINUM_INCR 4 +#define DST$C_SET_LINUM_INCR_W 5 +#define DST$C_RESET_LINUM_INCR 6 +#define DST$C_BEG_STMT_MODE 7 +#define DST$C_END_STMT_MODE 8 +#define DST$C_SET_LINE_NUM 9 /* Set Line # */ +#define DST$C_SET_PC 10 +#define DST$C_SET_PC_W 11 +#define DST$C_SET_PC_L 12 +#define DST$C_SET_STMTNUM 13 +#define DST$C_TERM 14 /* End of lines */ +#define DST$C_TERM_W 15 /* End of lines */ +#define DST$C_SET_ABS_PC 16 /* Set PC */ +#define DST$C_DELTA_PC_L 17 /* Incr PC */ +#define DST$C_INCR_LINUM_L 18 /* Incr Line # */ +#define DST$C_SET_LINUM_B 19 /* Set Line # */ +#define DST$C_SET_LINUM_L 20 /* Set Line # */ +#define DST$C_TERM_L 21 /* End of lines */ +/* these are used with DST$C_SOURCE */ +#define DST$C_SRC_FORMFEED 16 /* ^L counts */ +#define DST$C_SRC_DECLFILE 1 /* Declare file */ +#define DST$C_SRC_SETFILE 2 /* Set file */ +#define DST$C_SRC_SETREC_L 3 /* Set record */ +#define DST$C_SRC_DEFLINES_W 10 /* # of line */ +/* the following are the codes for the various data types. Anything not on + * the list is included under 'advanced_type' + */ +#define DBG$C_UCHAR 0x02 +#define DBG$C_USINT 0x03 +#define DBG$C_ULINT 0x04 +#define DBG$C_SCHAR 0x06 +#define DBG$C_SSINT 0x07 +#define DBG$C_SLINT 0x08 +#define DBG$C_REAL4 0x0a +#define DBG$C_REAL8 0x0b +#define DBG$C_FUNCTION_ADDR 0x17 +#define DBG$C_ADVANCED_TYPE 0xa3 +/* These are the codes that are used to generate the definitions of struct + * union and enum records + */ +#define DBG$C_ENUM_ITEM 0xa4 +#define DBG$C_ENUM_START 0xa5 +#define DBG$C_ENUM_END 0xa6 +#define DBG$C_STRUCT_START 0xab +#define DBG$C_STRUCT_ITEM 0xff +#define DBG$C_STRUCT_END 0xac +/* These are the codes that are used in the suffix records to determine the + * actual data type + */ +#define DBG$C_BASIC 0x01 +#define DBG$C_BASIC_ARRAY 0x02 +#define DBG$C_STRUCT 0x03 +#define DBG$C_POINTER 0x04 +#define DBG$C_VOID 0x05 +#define DBG$C_COMPLEX_ARRAY 0x07 +/* These codes are used in the generation of the symbol definition records + */ +#define DBG$C_FUNCTION_PARAMETER 0xc9 +#define DBG$C_LOCAL_SYM 0xd9 diff --git a/gnu/usr.bin/as/obstack.c b/gnu/usr.bin/as/obstack.c new file mode 100644 index 0000000000..c9cf561f05 --- /dev/null +++ b/gnu/usr.bin/as/obstack.c @@ -0,0 +1,337 @@ +/* obstack.c - subroutines used implicitly by object stack macros + Copyright (C) 1988 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 1, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "obstack.h" + +#ifdef __STDC__ +#define POINTER void * +#else +#define POINTER char * +#endif + +/* Determine default alignment. */ +struct fooalign {char x; double d;}; +#define DEFAULT_ALIGNMENT ((char *)&((struct fooalign *) 0)->d - (char *)0) +/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT. + But in fact it might be less smart and round addresses to as much as + DEFAULT_ROUNDING. So we prepare for it to do that. */ +union fooround {long x; double d;}; +#define DEFAULT_ROUNDING (sizeof (union fooround)) + +/* When we copy a long block of data, this is the unit to do it with. + On some machines, copying successive ints does not work; + in such a case, redefine COPYING_UNIT to `long' (if that works) + or `char' as a last resort. */ +#ifndef COPYING_UNIT +#define COPYING_UNIT int +#endif + +/* The non-GNU-C macros copy the obstack into this global variable + to avoid multiple evaluation. */ + +struct obstack *_obstack; + +/* Initialize an obstack H for use. Specify chunk size SIZE (0 means default). + Objects start on multiples of ALIGNMENT (0 means use default). + CHUNKFUN is the function to use to allocate chunks, + and FREEFUN the function to free them. */ + +void +_obstack_begin (h, size, alignment, chunkfun, freefun) + struct obstack *h; + int size; + int alignment; + POINTER (*chunkfun) (); + void (*freefun) (); +{ + register struct _obstack_chunk* chunk; /* points to new chunk */ + + if (alignment == 0) + alignment = DEFAULT_ALIGNMENT; + if (size == 0) + /* Default size is what GNU malloc can fit in a 4096-byte block. */ + { + /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc. + Use the values for range checking, because if range checking is off, + the extra bytes won't be missed terribly, but if range checking is on + and we used a larger request, a whole extra 4096 bytes would be + allocated. + + These number are irrelevant to the new GNU malloc. I suspect it is + less sensitive to the size of the request. */ + int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1)) + + 4 + DEFAULT_ROUNDING - 1) + & ~(DEFAULT_ROUNDING - 1)); + size = 4096 - extra; + } + + h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun; + h->freefun = freefun; + h->chunk_size = size; + h->alignment_mask = alignment - 1; + + chunk = h->chunk = (*h->chunkfun) (h->chunk_size); + h->next_free = h->object_base = chunk->contents; + h->chunk_limit = chunk->limit + = (char *) chunk + h->chunk_size; + chunk->prev = 0; +} + +/* Allocate a new current chunk for the obstack *H + on the assumption that LENGTH bytes need to be added + to the current object, or a new object of length LENGTH allocated. + Copies any partial object from the end of the old chunk + to the beginning of the new one. */ + +void +_obstack_newchunk (h, length) + struct obstack *h; + int length; +{ + register struct _obstack_chunk* old_chunk = h->chunk; + register struct _obstack_chunk* new_chunk; + register long new_size; + register int obj_size = h->next_free - h->object_base; + register int i; + int already; + + /* Compute size for new chunk. */ + new_size = (obj_size + length) + (obj_size >> 3) + 100; + if (new_size < h->chunk_size) + new_size = h->chunk_size; + + /* Allocate and initialize the new chunk. */ + new_chunk = h->chunk = (*h->chunkfun) (new_size); + new_chunk->prev = old_chunk; + new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size; + + /* Move the existing object to the new chunk. + Word at a time is fast and is safe if the object + is sufficiently aligned. */ + if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT) + { + for (i = obj_size / sizeof (COPYING_UNIT) - 1; + i >= 0; i--) + ((COPYING_UNIT *)new_chunk->contents)[i] + = ((COPYING_UNIT *)h->object_base)[i]; + /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT, + but that can cross a page boundary on a machine + which does not do strict alignment for COPYING_UNITS. */ + already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT); + } + else + already = 0; + /* Copy remaining bytes one by one. */ + for (i = already; i < obj_size; i++) + new_chunk->contents[i] = h->object_base[i]; + + /* If the object just copied was the only data in OLD_CHUNK, + free that chunk and remove it from the chain. */ + if (h->object_base == old_chunk->contents) + { + new_chunk->prev = old_chunk->prev; + (*h->freefun) (old_chunk); + } + + h->object_base = new_chunk->contents; + h->next_free = h->object_base + obj_size; +} + +/* Return nonzero if object OBJ has been allocated from obstack H. + This is here for debugging. + If you use it in a program, you are probably losing. */ + +int +_obstack_allocated_p (h, obj) + struct obstack *h; + POINTER obj; +{ + register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */ + register struct _obstack_chunk* plp; /* point to previous chunk if any */ + + lp = (h)->chunk; + while (lp != 0 && ((POINTER)lp > obj || (POINTER)(lp)->limit < obj)) + { + plp = lp -> prev; + lp = plp; + } + return lp != 0; +} + +/* Free objects in obstack H, including OBJ and everything allocate + more recently than OBJ. If OBJ is zero, free everything in H. */ + +void +#ifdef __STDC__ +#undef obstack_free +obstack_free (struct obstack *h, POINTER obj) +#else +_obstack_free (h, obj) + struct obstack *h; + POINTER obj; +#endif +{ + register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */ + register struct _obstack_chunk* plp; /* point to previous chunk if any */ + + lp = (h)->chunk; + /* We use >= because there cannot be an object at the beginning of a chunk. + But there can be an empty object at that address + at the end of another chunk. */ + while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj)) + { + plp = lp -> prev; + (*h->freefun) (lp); + lp = plp; + } + if (lp) + { + (h)->object_base = (h)->next_free = (char *)(obj); + (h)->chunk_limit = lp->limit; + (h)->chunk = lp; + } + else if (obj != 0) + /* obj is not in any of the chunks! */ + abort (); +} + +/* Let same .o link with output of gcc and other compilers. */ + +#ifdef __STDC__ +void +_obstack_free (h, obj) + struct obstack *h; + POINTER obj; +{ + obstack_free (h, obj); +} +#endif + +#if 0 +/* These are now turned off because the applications do not use it + and it uses bcopy via obstack_grow, which causes trouble on sysV. */ + +/* Now define the functional versions of the obstack macros. + Define them to simply use the corresponding macros to do the job. */ + +#ifdef __STDC__ +/* These function definitions do not work with non-ANSI preprocessors; + they won't pass through the macro names in parentheses. */ + +/* The function names appear in parentheses in order to prevent + the macro-definitions of the names from being expanded there. */ + +POINTER (obstack_base) (obstack) + struct obstack *obstack; +{ + return obstack_base (obstack); +} + +POINTER (obstack_next_free) (obstack) + struct obstack *obstack; +{ + return obstack_next_free (obstack); +} + +int (obstack_object_size) (obstack) + struct obstack *obstack; +{ + return obstack_object_size (obstack); +} + +int (obstack_room) (obstack) + struct obstack *obstack; +{ + return obstack_room (obstack); +} + +void (obstack_grow) (obstack, pointer, length) + struct obstack *obstack; + POINTER pointer; + int length; +{ + obstack_grow (obstack, pointer, length); +} + +void (obstack_grow0) (obstack, pointer, length) + struct obstack *obstack; + POINTER pointer; + int length; +{ + obstack_grow0 (obstack, pointer, length); +} + +void (obstack_1grow) (obstack, character) + struct obstack *obstack; + int character; +{ + obstack_1grow (obstack, character); +} + +void (obstack_blank) (obstack, length) + struct obstack *obstack; + int length; +{ + obstack_blank (obstack, length); +} + +void (obstack_1grow_fast) (obstack, character) + struct obstack *obstack; + int character; +{ + obstack_1grow_fast (obstack, character); +} + +void (obstack_blank_fast) (obstack, length) + struct obstack *obstack; + int length; +{ + obstack_blank_fast (obstack, length); +} + +POINTER (obstack_finish) (obstack) + struct obstack *obstack; +{ + return obstack_finish (obstack); +} + +POINTER (obstack_alloc) (obstack, length) + struct obstack *obstack; + int length; +{ + return obstack_alloc (obstack, length); +} + +POINTER (obstack_copy) (obstack, pointer, length) + struct obstack *obstack; + POINTER pointer; + int length; +{ + return obstack_copy (obstack, pointer, length); +} + +POINTER (obstack_copy0) (obstack, pointer, length) + struct obstack *obstack; + POINTER pointer; + int length; +{ + return obstack_copy0 (obstack, pointer, length); +} + +#endif /* __STDC__ */ + +#endif /* 0 */ diff --git a/gnu/usr.bin/as/obstack.h b/gnu/usr.bin/as/obstack.h new file mode 100644 index 0000000000..fd779c4c9d --- /dev/null +++ b/gnu/usr.bin/as/obstack.h @@ -0,0 +1,418 @@ +/* obstack.h - object stack macros + Copyright (C) 1988 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 1, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Summary: + +All the apparent functions defined here are macros. The idea +is that you would use these pre-tested macros to solve a +very specific set of problems, and they would run fast. +Caution: no side-effects in arguments please!! They may be +evaluated MANY times!! + +These macros operate a stack of objects. Each object starts life +small, and may grow to maturity. (Consider building a word syllable +by syllable.) An object can move while it is growing. Once it has +been "finished" it never changes address again. So the "top of the +stack" is typically an immature growing object, while the rest of the +stack is of mature, fixed size and fixed address objects. + +These routines grab large chunks of memory, using a function you +supply, called `obstack_chunk_alloc'. On occasion, they free chunks, +by calling `obstack_chunk_free'. You must define them and declare +them before using any obstack macros. + +Each independent stack is represented by a `struct obstack'. +Each of the obstack macros expects a pointer to such a structure +as the first argument. + +One motivation for this package is the problem of growing char strings +in symbol tables. Unless you are "fascist pig with a read-only mind" +[Gosper's immortal quote from HAKMEM item 154, out of context] you +would not like to put any arbitrary upper limit on the length of your +symbols. + +In practice this often means you will build many short symbols and a +few long symbols. At the time you are reading a symbol you don't know +how long it is. One traditional method is to read a symbol into a +buffer, realloc()ating the buffer every time you try to read a symbol +that is longer than the buffer. This is beaut, but you still will +want to copy the symbol from the buffer to a more permanent +symbol-table entry say about half the time. + +With obstacks, you can work differently. Use one obstack for all symbol +names. As you read a symbol, grow the name in the obstack gradually. +When the name is complete, finalize it. Then, if the symbol exists already, +free the newly read name. + +The way we do this is to take a large chunk, allocating memory from +low addresses. When you want to build a symbol in the chunk you just +add chars above the current "high water mark" in the chunk. When you +have finished adding chars, because you got to the end of the symbol, +you know how long the chars are, and you can create a new object. +Mostly the chars will not burst over the highest address of the chunk, +because you would typically expect a chunk to be (say) 100 times as +long as an average object. + +In case that isn't clear, when we have enough chars to make up +the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed) +so we just point to it where it lies. No moving of chars is +needed and this is the second win: potentially long strings need +never be explicitly shuffled. Once an object is formed, it does not +change its address during its lifetime. + +When the chars burst over a chunk boundary, we allocate a larger +chunk, and then copy the partly formed object from the end of the old +chunk to the beginning of the new larger chunk. We then carry on +accreting characters to the end of the object as we normally would. + +A special macro is provided to add a single char at a time to a +growing object. This allows the use of register variables, which +break the ordinary 'growth' macro. + +Summary: + We allocate large chunks. + We carve out one object at a time from the current chunk. + Once carved, an object never moves. + We are free to append data of any size to the currently + growing object. + Exactly one object is growing in an obstack at any one time. + You can run one obstack per control block. + You may have as many control blocks as you dare. + Because of the way we do it, you can `unwind' a obstack + back to a previous state. (You may remove objects much + as you would with a stack.) +*/ + + +/* Don't do the contents of this file more than once. */ + +#ifndef __OBSTACKS__ +#define __OBSTACKS__ + +/* We use subtraction of (char *)0 instead of casting to int + because on word-addressable machines a simple cast to int + may ignore the byte-within-word field of the pointer. */ + +#ifndef __PTR_TO_INT +#define __PTR_TO_INT(P) ((P) - (char *)0) +#endif + +#ifndef __INT_TO_PTR +#define __INT_TO_PTR(P) ((P) + (char *)0) +#endif + +struct _obstack_chunk /* Lives at front of each chunk. */ +{ + char *limit; /* 1 past end of this chunk */ + struct _obstack_chunk *prev; /* address of prior chunk or NULL */ + char contents[4]; /* objects begin here */ +}; + +struct obstack /* control current object in current chunk */ +{ + long chunk_size; /* preferred size to allocate chunks in */ + struct _obstack_chunk* chunk; /* address of current struct obstack_chunk */ + char *object_base; /* address of object we are building */ + char *next_free; /* where to add next char to current object */ + char *chunk_limit; /* address of char after current chunk */ + int temp; /* Temporary for some macros. */ + int alignment_mask; /* Mask of alignment for each object. */ + struct _obstack_chunk *(*chunkfun) (); /* User's fcn to allocate a chunk. */ + void (*freefun) (); /* User's function to free a chunk. */ +}; + +/* Declare the external functions we use; they are in obstack.c. */ + +#ifdef __STDC__ + extern void _obstack_newchunk (struct obstack *, int); + extern void _obstack_free (struct obstack *, void *); + extern void _obstack_begin (struct obstack *, int, int, + void *(*) (), void (*) ()); +#else + extern void _obstack_newchunk (); + extern void _obstack_free (); + extern void _obstack_begin (); +#endif + +#ifdef __STDC__ + +/* Do the function-declarations after the structs + but before defining the macros. */ + +void obstack_init (struct obstack *obstack); + +void * obstack_alloc (struct obstack *obstack, int size); + +void * obstack_copy (struct obstack *obstack, void *address, int size); +void * obstack_copy0 (struct obstack *obstack, void *address, int size); + +void obstack_free (struct obstack *obstack, void *block); + +void obstack_blank (struct obstack *obstack, int size); + +void obstack_grow (struct obstack *obstack, void *data, int size); +void obstack_grow0 (struct obstack *obstack, void *data, int size); + +void obstack_1grow (struct obstack *obstack, int data_char); +void obstack_ptr_grow (struct obstack *obstack, void *data); +void obstack_int_grow (struct obstack *obstack, int data); + +void * obstack_finish (struct obstack *obstack); + +int obstack_object_size (struct obstack *obstack); + +int obstack_room (struct obstack *obstack); +void obstack_1grow_fast (struct obstack *obstack, int data_char); +void obstack_ptr_grow_fast (struct obstack *obstack, void *data); +void obstack_int_grow_fast (struct obstack *obstack, int data); +void obstack_blank_fast (struct obstack *obstack, int size); + +void * obstack_base (struct obstack *obstack); +void * obstack_next_free (struct obstack *obstack); +int obstack_alignment_mask (struct obstack *obstack); +int obstack_chunk_size (struct obstack *obstack); + +#endif /* __STDC__ */ + +/* Non-ANSI C cannot really support alternative functions for these macros, + so we do not declare them. */ + +/* Pointer to beginning of object being allocated or to be allocated next. + Note that this might not be the final address of the object + because a new chunk might be needed to hold the final size. */ + +#define obstack_base(h) ((h)->object_base) + +/* Size for allocating ordinary chunks. */ + +#define obstack_chunk_size(h) ((h)->chunk_size) + +/* Pointer to next byte not yet allocated in current chunk. */ + +#define obstack_next_free(h) ((h)->next_free) + +/* Mask specifying low bits that should be clear in address of an object. */ + +#define obstack_alignment_mask(h) ((h)->alignment_mask) + +#define obstack_init(h) \ + _obstack_begin ((h), 0, 0, \ + (void *(*) ()) obstack_chunk_alloc, obstack_chunk_free) + +#define obstack_begin(h, size) \ + _obstack_begin ((h), (size), 0, \ + (void *(*) ()) obstack_chunk_alloc, obstack_chunk_free) + +#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = achar) + +#define obstack_blank_fast(h,n) ((h)->next_free += (n)) + +#if defined (__GNUC__) && defined (__STDC__) + +/* For GNU C, if not -traditional, + we can define these macros to compute all args only once + without using a global variable. + Also, we can avoid using the `temp' slot, to make faster code. */ + +#define obstack_object_size(OBSTACK) \ + ({ struct obstack *__o = (OBSTACK); \ + (unsigned) (__o->next_free - __o->object_base); }) + +#define obstack_room(OBSTACK) \ + ({ struct obstack *__o = (OBSTACK); \ + (unsigned) (__o->chunk_limit - __o->next_free); }) + +/* Note that the call to _obstack_newchunk is enclosed in (..., 0) + so that we can avoid having void expressions + in the arms of the conditional expression. + Casting the third operand to void was tried before, + but some compilers won't accept it. */ +#define obstack_grow(OBSTACK,where,length) \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + ((__o->next_free + __len > __o->chunk_limit) \ + ? (_obstack_newchunk (__o, __len), 0) : 0); \ + bcopy (where, __o->next_free, __len); \ + __o->next_free += __len; \ + (void) 0; }) + +#define obstack_grow0(OBSTACK,where,length) \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + ((__o->next_free + __len + 1 > __o->chunk_limit) \ + ? (_obstack_newchunk (__o, __len + 1), 0) : 0), \ + bcopy (where, __o->next_free, __len), \ + __o->next_free += __len, \ + *(__o->next_free)++ = 0; \ + (void) 0; }) + +#define obstack_1grow(OBSTACK,datum) \ +({ struct obstack *__o = (OBSTACK); \ + ((__o->next_free + 1 > __o->chunk_limit) \ + ? (_obstack_newchunk (__o, 1), 0) : 0), \ + *(__o->next_free)++ = (datum); \ + (void) 0; }) + +/* These assume that the obstack alignment is good enough for pointers or ints, + and that the data added so far to the current object + shares that much alignment. */ + +#define obstack_ptr_grow(OBSTACK,datum) \ +({ struct obstack *__o = (OBSTACK); \ + ((__o->next_free + sizeof (void *) > __o->chunk_limit) \ + ? (_obstack_newchunk (__o, sizeof (void *)), 0) : 0), \ + *((void **)__o->next_free)++ = ((void *)datum); \ + (void) 0; }) + +#define obstack_int_grow(OBSTACK,datum) \ +({ struct obstack *__o = (OBSTACK); \ + ((__o->next_free + sizeof (int) > __o->chunk_limit) \ + ? (_obstack_newchunk (__o, sizeof (int)), 0) : 0), \ + *((int *)__o->next_free)++ = ((int)datum); \ + (void) 0; }) + +#define obstack_ptr_grow_fast(h,aptr) (*((void **)(h)->next_free)++ = (void *)aptr) +#define obstack_int_grow_fast(h,aint) (*((int *)(h)->next_free)++ = (int)aint) + +#define obstack_blank(OBSTACK,length) \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + ((__o->chunk_limit - __o->next_free < __len) \ + ? (_obstack_newchunk (__o, __len), 0) : 0); \ + __o->next_free += __len; \ + (void) 0; }) + +#define obstack_alloc(OBSTACK,length) \ +({ struct obstack *__h = (OBSTACK); \ + obstack_blank (__h, (length)); \ + obstack_finish (__h); }) + +#define obstack_copy(OBSTACK,where,length) \ +({ struct obstack *__h = (OBSTACK); \ + obstack_grow (__h, (where), (length)); \ + obstack_finish (__h); }) + +#define obstack_copy0(OBSTACK,where,length) \ +({ struct obstack *__h = (OBSTACK); \ + obstack_grow0 (__h, (where), (length)); \ + obstack_finish (__h); }) + +#define obstack_finish(OBSTACK) \ +({ struct obstack *__o = (OBSTACK); \ + void *value = (void *) __o->object_base; \ + __o->next_free \ + = __INT_TO_PTR ((__PTR_TO_INT (__o->next_free)+__o->alignment_mask)\ + & ~ (__o->alignment_mask)); \ + ((__o->next_free - (char *)__o->chunk \ + > __o->chunk_limit - (char *)__o->chunk) \ + ? (__o->next_free = __o->chunk_limit) : 0); \ + __o->object_base = __o->next_free; \ + value; }) + +#define obstack_free(OBSTACK, OBJ) \ +({ struct obstack *__o = (OBSTACK); \ + void *__obj = (OBJ); \ + if (__obj > (void *)__o->chunk && __obj < (void *)__o->chunk_limit) \ + __o->next_free = __o->object_base = __obj; \ + else (obstack_free) (__o, __obj); }) + +#else /* not __GNUC__ or not __STDC__ */ + +#define obstack_object_size(h) \ + (unsigned) ((h)->next_free - (h)->object_base) + +#define obstack_room(h) \ + (unsigned) ((h)->chunk_limit - (h)->next_free) + +#define obstack_grow(h,where,length) \ +( (h)->temp = (length), \ + (((h)->next_free + (h)->temp > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp), 0) : 0), \ + bcopy (where, (h)->next_free, (h)->temp), \ + (h)->next_free += (h)->temp) + +#define obstack_grow0(h,where,length) \ +( (h)->temp = (length), \ + (((h)->next_free + (h)->temp + 1 > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp + 1), 0) : 0), \ + bcopy (where, (h)->next_free, (h)->temp), \ + (h)->next_free += (h)->temp, \ + *((h)->next_free)++ = 0) + +#define obstack_1grow(h,datum) \ +( (((h)->next_free + 1 > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), 1), 0) : 0), \ + *((h)->next_free)++ = (datum)) + +#define obstack_ptr_grow(h,datum) \ +( (((h)->next_free + sizeof (char *) > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), sizeof (char *)), 0) : 0), \ + *((char **)(((h)->next_free+=sizeof(char *))-sizeof(char *))) = ((char *)datum)) + +#define obstack_int_grow(h,datum) \ +( (((h)->next_free + sizeof (int) > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), sizeof (int)), 0) : 0), \ + *((int *)(((h)->next_free+=sizeof(int))-sizeof(int))) = ((int)datum)) + +#define obstack_ptr_grow_fast(h,aptr) (*((char **)(h)->next_free)++ = (char *)aptr) +#define obstack_int_grow_fast(h,aint) (*((int *)(h)->next_free)++ = (int)aint) + +#define obstack_blank(h,length) \ +( (h)->temp = (length), \ + (((h)->chunk_limit - (h)->next_free < (h)->temp) \ + ? (_obstack_newchunk ((h), (h)->temp), 0) : 0), \ + (h)->next_free += (h)->temp) + +#define obstack_alloc(h,length) \ + (obstack_blank ((h), (length)), obstack_finish ((h))) + +#define obstack_copy(h,where,length) \ + (obstack_grow ((h), (where), (length)), obstack_finish ((h))) + +#define obstack_copy0(h,where,length) \ + (obstack_grow0 ((h), (where), (length)), obstack_finish ((h))) + +#define obstack_finish(h) \ +( (h)->temp = __PTR_TO_INT ((h)->object_base), \ + (h)->next_free \ + = __INT_TO_PTR ((__PTR_TO_INT ((h)->next_free)+(h)->alignment_mask) \ + & ~ ((h)->alignment_mask)), \ + (((h)->next_free - (char *)(h)->chunk \ + > (h)->chunk_limit - (char *)(h)->chunk) \ + ? ((h)->next_free = (h)->chunk_limit) : 0), \ + (h)->object_base = (h)->next_free, \ + __INT_TO_PTR ((h)->temp)) + +#ifdef __STDC__ +#define obstack_free(h,obj) \ +( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \ + (((h)->temp >= 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\ + ? (int) ((h)->next_free = (h)->object_base \ + = (h)->temp + (char *) (h)->chunk) \ + : (((obstack_free) ((h), (h)->temp + (char *) (h)->chunk), 0), 0))) +#else +#define obstack_free(h,obj) \ +( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \ + (((h)->temp >= 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\ + ? (int) ((h)->next_free = (h)->object_base \ + = (h)->temp + (char *) (h)->chunk) \ + : (_obstack_free ((h), (h)->temp + (char *) (h)->chunk), 0))) +#endif + +#endif /* not __GNUC__ or not __STDC__ */ + +#endif /* not __OBSTACKS__ */ diff --git a/gnu/usr.bin/as/output-file.c b/gnu/usr.bin/as/output-file.c new file mode 100644 index 0000000000..ff71f40f9a --- /dev/null +++ b/gnu/usr.bin/as/output-file.c @@ -0,0 +1,81 @@ +/* output-file.c - Deal with the output file + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * Confines all details of emitting object bytes to this module. + * All O/S specific crocks should live here. + * What we lose in "efficiency" we gain in modularity. + * Note we don't need to #include the "as.h" file. No common coupling! + */ + +/* #include "style.h" */ +#include + +void as_perror(); + +static FILE * +stdoutput; + +void +output_file_create (name) + char * name; +{ + if(name[0]=='-' && name[1]=='\0') + stdoutput=stdout; + else if ( ! (stdoutput = fopen( name, "w" )) ) + { + as_perror ("FATAL: Can't create %s", name); + exit(42); + } +} + + + +void +output_file_close (filename) + char * filename; +{ + if ( EOF == fclose( stdoutput ) ) + { + as_perror ("FATAL: Can't close %s", filename); + exit(42); + } + stdoutput = NULL; /* Trust nobody! */ +} + +void +output_file_append (where, length, filename) + char * where; + long int length; + char * filename; +{ + + for (; length; length--,where++) + { + (void)putc(*where,stdoutput); + if(ferror(stdoutput)) + /* if ( EOF == (putc( *where, stdoutput )) ) */ + { + as_perror("Failed to emit an object byte", filename); + as_fatal("Can't continue"); + } + } +} + +/* end: output-file.c */ diff --git a/gnu/usr.bin/as/read.c b/gnu/usr.bin/as/read.c new file mode 100644 index 0000000000..8357107709 --- /dev/null +++ b/gnu/usr.bin/as/read.c @@ -0,0 +1,2188 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + */ + +#ifndef lint +static char sccsid[] = "@(#)read.c 6.4 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* read.c - read a source file - + Copyright (C) 1986,1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#define MASK_CHAR (0xFF) /* If your chars aren't 8 bits, you will + change this a bit. But then, GNU isn't + spozed to run on your machine anyway. + (RMS is so shortsighted sometimes.) + */ + +#define MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT (16) + /* This is the largest known floating point */ + /* format (for now). It will grow when we */ + /* do 4361 style flonums. */ + + +/* Routines that read assembler source text to build spagetti in memory. */ +/* Another group of these functions is in the as-expr.c module */ + +#include +#include +#include +#include "as.h" +#include "read.h" +#include "md.h" +#include "hash.h" +#include "obstack.h" +#include "frags.h" +#include "flonum.h" +#include "struc-symbol.h" +#include "expr.h" +#include "symbols.h" + +#ifdef SPARC +#include "sparc.h" +#define OTHER_ALIGN +#endif +#ifdef I860 +#include "i860.h" +#endif + +char * input_line_pointer; /* -> next char of source file to parse. */ + + +#if BITS_PER_CHAR != 8 +The following table is indexed by [ (char) ] and will break if +a char does not have exactly 256 states (hopefully 0:255!) ! +#endif + +const char /* used by is_... macros. our ctype[] */ +lex_type [256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ABCDEFGHIJKLMNO */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* PQRSTUVWXYZ[\]^_ */ + 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, /* _!"#$%&'()*+,-./ */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0123456789:;<=>? */ + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* @ABCDEFGHIJKLMNO */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, /* PQRSTUVWXYZ[\]^_ */ + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* `abcdefghijklmno */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, /* pqrstuvwxyz{|}~. */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + + +/* + * In: a character. + * Out: TRUE if this character ends a line. + */ +#define _ (0) +const char is_end_of_line [256] = { + _, _, _, _, _, _, _, _, _, _,99, _, _, _, _, _, /* @abcdefghijklmno */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _,99, _, _, _, _, /* 0123456789:;<=>? */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _ /* */ +}; +#undef _ + + /* Functions private to this file. */ +void equals(); +void big_cons(); +void cons(); +static char* demand_copy_C_string(); +static char* demand_copy_string(); +void demand_empty_rest_of_line(); +void float_cons(); +long int get_absolute_expression(); +static char get_absolute_expression_and_terminator(); +static segT get_known_segmented_expression(); +void ignore_rest_of_line(); +static int is_it_end_of_statement(); +static void pobegin(); +static void pseudo_set(); +static void stab(); +static void stringer(); + +extern char line_comment_chars[]; + +static char * buffer_limit; /* -> 1 + last char in buffer. */ + +static char * bignum_low; /* Lowest char of bignum. */ +static char * bignum_limit; /* 1st illegal address of bignum. */ +static char * bignum_high; /* Highest char of bignum. */ + /* May point to (bignum_start-1). */ + /* Never >= bignum_limit. */ +static char *old_buffer = 0; /* JF a hack */ +static char *old_input; +static char *old_limit; + +#ifndef WORKING_DOT_WORD +struct broken_word *broken_words; +int new_broken_words = 0; +#endif + +static void grow_bignum (); +static int next_char_of_string (); + +void +read_begin() +{ + pobegin(); + obstack_begin( ¬es, 5000 ); +#define BIGNUM_BEGIN_SIZE (16) + bignum_low = xmalloc((long)BIGNUM_BEGIN_SIZE); + bignum_limit = bignum_low + BIGNUM_BEGIN_SIZE; +} + +/* set up pseudo-op tables */ + +static struct hash_control * +po_hash = NULL; /* use before set up: NULL-> address error */ + + +void s_abort(), s_align(), s_comm(), s_data(); +void s_desc(), s_even(), s_file(), s_fill(); +void s_globl(), s_lcomm(), s_line(), s_lsym(); +void s_org(), s_set(), s_space(), s_text(); +#ifdef VMS +char const_flag = 0; +void s_const(); +#endif + +#ifdef DONTDEF +void s_gdbline(), s_gdblinetab(); +void s_gdbbeg(), s_gdbblock(), s_gdbend(), s_gdbsym(); +#endif + +void stringer(); +void cons(); +void float_cons(); +void big_cons(); +void stab(); + +static const pseudo_typeS +potable[] = +{ + { "abort", s_abort, 0 }, + { "align", s_align, 0 }, + { "ascii", stringer, 0 }, + { "asciz", stringer, 1 }, + { "byte", cons, 1 }, + { "comm", s_comm, 0 }, +#ifdef VMS + { "const", s_const, 0 }, +#endif + { "data", s_data, 0 }, + { "desc", s_desc, 0 }, + { "double", float_cons, 'd' }, + { "file", s_file, 0 }, + { "fill", s_fill, 0 }, + { "float", float_cons, 'f' }, +#ifdef DONTDEF + { "gdbbeg", s_gdbbeg, 0 }, + { "gdbblock", s_gdbblock, 0 }, + { "gdbend", s_gdbend, 0 }, + { "gdbsym", s_gdbsym, 0 }, + { "gdbline", s_gdbline, 0 }, + { "gdblinetab",s_gdblinetab, 0 }, +#endif + { "globl", s_globl, 0 }, + { "int", cons, 4 }, + { "lcomm", s_lcomm, 0 }, + { "line", s_line, 0 }, + { "long", cons, 4 }, + { "lsym", s_lsym, 0 }, + { "octa", big_cons, 16 }, + { "org", s_org, 0 }, + { "quad", big_cons, 8 }, + { "set", s_set, 0 }, + { "short", cons, 2 }, + { "single", float_cons, 'f' }, + { "space", s_space, 0 }, + { "stabd", stab, 'd' }, + { "stabn", stab, 'n' }, + { "stabs", stab, 's' }, + { "text", s_text, 0 }, +#ifndef SPARC + { "word", cons, 2 }, +#endif + { NULL} /* end sentinel */ +}; + +static void +pobegin() +{ + char * errtxt; /* error text */ + const pseudo_typeS * pop; + + po_hash = hash_new(); + errtxt = ""; /* OK so far */ + for (pop=potable; pop->poc_name && !*errtxt; pop++) + { + errtxt = hash_insert (po_hash, pop->poc_name, (char *)pop); + } + + for(pop=md_pseudo_table; pop->poc_name && !*errtxt; pop++) + errtxt = hash_insert (po_hash, pop->poc_name, (char *)pop); + + if (*errtxt) + { + as_fatal ("error constructing pseudo-op table"); + } +} /* pobegin() */ + +/* read_a_source_file() + * + * File has already been opened, and will be closed by our caller. + * + * We read the file, putting things into a web that + * represents what we have been reading. + */ +void +read_a_source_file (buffer) + char * buffer; /* 1st character of each buffer of lines is here. */ +{ + register char c; + register char * s; /* string of symbol, '\0' appended */ + register int temp; + /* register struct frag * fragP; JF unused */ /* a frag we just made */ + pseudo_typeS *pop; +#ifdef DONTDEF + void gdb_block_beg(); + void gdb_block_position(); + void gdb_block_end(); + void gdb_symbols_fixup(); +#endif + + subseg_new (SEG_TEXT, 0); + while ( buffer_limit = input_scrub_next_buffer (&buffer) ) + { /* We have another line to parse. */ + know( buffer_limit [-1] == '\n' ); /* Must have a sentinel. */ + input_line_pointer = buffer; + contin: /* JF this goto is my fault I admit it. Someone brave please re-write + the whole input section here? Pleeze??? */ + while ( input_line_pointer < buffer_limit ) + { /* We have more of this buffer to parse. */ + /* + * We now have input_line_pointer -> 1st char of next line. + * If input_line_pointer [-1] == '\n' then we just + * scanned another line: so bump line counters. + */ + if (input_line_pointer [-1] == '\n') + { + bump_line_counters (); + } + /* + * We are at the begining of a line, or similar place. + * We expect a well-formed assembler statement. + * A "symbol-name:" is a statement. + * + * Depending on what compiler is used, the order of these tests + * may vary to catch most common case 1st. + * Each test is independent of all other tests at the (top) level. + * PLEASE make a compiler that doesn't use this assembler. + * It is crufty to waste a compiler's time encoding things for this + * assembler, which then wastes more time decoding it. + * (And communicating via (linear) files is silly! + * If you must pass stuff, please pass a tree!) + */ + if ( (c= * input_line_pointer ++) == '\t' || c == ' ' || c=='\f') + { + c = * input_line_pointer ++; + } + know( c != ' ' ); /* No further leading whitespace. */ + /* + * C is the 1st significant character. + * Input_line_pointer points after that character. + */ + if ( is_name_beginner(c) ) + { /* want user-defined label or pseudo/opcode */ + s = -- input_line_pointer; + c = get_symbol_end(); /* name's delimiter */ + /* + * C is character after symbol. + * That character's place in the input line is now '\0'. + * S points to the beginning of the symbol. + * [In case of pseudo-op, s -> '.'.] + * Input_line_pointer -> '\0' where c was. + */ + if ( c == ':' ) + { + if (flagseen['g']) + /* set line number for function definition */ + funcstab(s); + colon(s); /* user-defined label */ + * input_line_pointer ++ = ':'; /* Put ':' back for error messages' sake. */ + /* Input_line_pointer -> after ':'. */ + SKIP_WHITESPACE(); + } + else if(c=='=' || input_line_pointer[1]=='=') /* JF deal with FOO=BAR */ + { + equals(s); + demand_empty_rest_of_line(); + } + else + { /* expect pseudo-op or machine instruction */ + if ( *s=='.' ) + { + /* + * PSEUDO - OP. + * + * WARNING: c has next char, which may be end-of-line. + * We lookup the pseudo-op table with s+1 because we + * already know that the pseudo-op begins with a '.'. + */ + + pop= (pseudo_typeS *) hash_find (po_hash, s+1); + + /* Print the error msg now, while we still can */ + if(!pop) + as_bad("Unknown pseudo-op: '%s'",s); + + /* Put it back for error messages etc. */ + * input_line_pointer = c; + /* The following skip of whitespace is compulsory. */ + /* A well shaped space is sometimes all that seperates keyword from operands. */ + if ( c == ' ' || c == '\t' ) + { /* Skip seperator after keyword. */ + input_line_pointer ++; + } + /* + * Input_line is restored. + * Input_line_pointer -> 1st non-blank char + * after pseudo-operation. + */ + if(!pop) { + ignore_rest_of_line(); + break; + } + else + (*pop->poc_handler)(pop->poc_val); + } + else + { /* machine instruction */ + /* If source file debugging, emit a stab. */ + if (flagseen['g']) + linestab(); + + /* WARNING: c has char, which may be end-of-line. */ + /* Also: input_line_pointer -> `\0` where c was. */ + * input_line_pointer = c; + while ( ! is_end_of_line [* input_line_pointer] ) + { + input_line_pointer ++; + } + c = * input_line_pointer; + * input_line_pointer = '\0'; + md_assemble (s); /* Assemble 1 instruction. */ + * input_line_pointer ++ = c; + /* We resume loop AFTER the end-of-line from this instruction */ + } /* if (*s=='.') */ + } /* if c==':' */ + continue; + } /* if (is_name_beginner(c) */ + + + if ( is_end_of_line [c] ) + { /* empty statement */ + continue; + } + + if ( isdigit(c) ) + { /* local label ("4:") */ + temp = c - '0'; +#ifdef SUN_ASM_SYNTAX + if( *input_line_pointer=='$') + input_line_pointer++; +#endif + if ( * input_line_pointer ++ == ':' ) + { + local_colon (temp); + } + else + { + as_bad( "Spurious digit %d.", temp); + input_line_pointer -- ; + ignore_rest_of_line(); + } + continue; + } + if(c && index(line_comment_chars,c)) { /* Its a comment. Better say APP or NO_APP */ + char *ends; + char *strstr(); + char *new_buf; + char *new_tmp; + int new_length; + char *tmp_buf = 0; + extern char *scrub_string,*scrub_last_string; + int scrub_from_string(); + void scrub_to_string(); + + bump_line_counters(); + s=input_line_pointer; + if(strncmp(s,"APP\n",4)) + continue; /* We ignore it */ + s+=4; + + ends=strstr(s,"#NO_APP\n"); + + if(!ends) { + int tmp_len; + int num; + + /* The end of the #APP wasn't in this buffer. We + keep reading in buffers until we find the #NO_APP + that goes with this #APP There is one. The specs + guarentee it. . .*/ + tmp_len=buffer_limit-s; + tmp_buf=xmalloc(tmp_len); + bcopy(s,tmp_buf,tmp_len); + do { + new_tmp = input_scrub_next_buffer(&buffer); + if(!new_tmp) + break; + else + buffer_limit = new_tmp; + input_line_pointer = buffer; + ends = strstr(buffer,"#NO_APP\n"); + if(ends) + num=ends-buffer; + else + num=buffer_limit-buffer; + + tmp_buf=xrealloc(tmp_buf,tmp_len+num); + bcopy(buffer,tmp_buf+tmp_len,num); + tmp_len+=num; + } while(!ends); + + input_line_pointer= ends ? ends+8 : NULL; + + s=tmp_buf; + ends=s+tmp_len; + + } else { + input_line_pointer=ends+8; + } + new_buf=xmalloc(100); + new_length=100; + new_tmp=new_buf; + + scrub_string=s; + scrub_last_string = ends; + for(;;) { + int ch; + + ch=do_scrub_next_char(scrub_from_string,scrub_to_string); + if(ch==EOF) break; + *new_tmp++=ch; + if(new_tmp==new_buf+new_length) { + new_buf=xrealloc(new_buf,new_length+100); + new_tmp=new_buf+new_length; + new_length+=100; + } + } + + if(tmp_buf) + free(tmp_buf); + old_buffer=buffer; + old_input=input_line_pointer; + old_limit=buffer_limit; + buffer=new_buf; + input_line_pointer=new_buf; + buffer_limit=new_tmp; + continue; + } + + as_bad("Junk character %d.",c); + ignore_rest_of_line(); + } /* while (input_line_pointer MAX_ALIGNMENT ) { + as_bad("Alignment too large: %d. assumed.", temp = MAX_ALIGNMENT); + } + + /* + * For the sparc, `.align (1<>= 1, ++i) + ; + } + if (temp != 1) + as_bad("Alignment not a power of 2"); + + temp = i; + if (*input_line_pointer == ',') { + input_line_pointer ++; + temp_fill = get_absolute_expression (); + } else { + temp_fill = 0; + } + /* Only make a frag if we HAVE to. . . */ + if (temp && ! need_pass_2) + frag_align (temp, (int)temp_fill); + + demand_empty_rest_of_line(); +} +#else + +void +s_align() +{ + register int temp; + register long int temp_fill; + + temp = get_absolute_expression (); +#define MAX_ALIGNMENT (15) + if ( temp > MAX_ALIGNMENT ) + as_bad("Alignment too large: %d. assumed.", temp = MAX_ALIGNMENT); + else if ( temp < 0 ) { + as_bad("Alignment negative. 0 assumed."); + temp = 0; + } + if ( *input_line_pointer == ',' ) { + input_line_pointer ++; + temp_fill = get_absolute_expression (); + } else + temp_fill = 0; + /* Only make a frag if we HAVE to. . . */ + if ( temp && ! need_pass_2 ) + frag_align (temp, (int)temp_fill); + demand_empty_rest_of_line(); +} +#endif + +void +s_comm() +{ + register char *name; + register char c; + register char *p; + register int temp; + register symbolS * symbolP; + + name = input_line_pointer; + c = get_symbol_end(); + /* just after name is now '\0' */ + p = input_line_pointer; + *p = c; + SKIP_WHITESPACE(); + if ( * input_line_pointer != ',' ) { + as_bad("Expected comma after symbol-name"); + ignore_rest_of_line(); + return; + } + input_line_pointer ++; /* skip ',' */ + if ( (temp = get_absolute_expression ()) < 0 ) { + as_warn(".COMMon length (%d.) <0! Ignored.", temp); + ignore_rest_of_line(); + return; + } + *p = 0; + symbolP = symbol_find_or_make (name); + *p = c; + if ( (symbolP -> sy_type & N_TYPE) != N_UNDF || + symbolP -> sy_other != 0 || symbolP -> sy_desc != 0) { + as_warn( "Ignoring attempt to re-define symbol"); + ignore_rest_of_line(); + return; + } + if (symbolP -> sy_value) { + if (symbolP -> sy_value != temp) + as_warn( "Length of .comm \"%s\" is already %d. Not changed to %d.", + symbolP -> sy_name, symbolP -> sy_value, temp); + } else { + symbolP -> sy_value = temp; + symbolP -> sy_type |= N_EXT; + } +#ifdef VMS + if(!temp) + symbolP->sy_other = const_flag; +#endif + know( symbolP -> sy_frag == &zero_address_frag ); + demand_empty_rest_of_line(); +} + +#ifdef VMS +void +s_const() +{ + register int temp; + + temp = get_absolute_expression (); + subseg_new (SEG_DATA, (subsegT)temp); + const_flag = 1; + demand_empty_rest_of_line(); +} +#endif + +void +s_data() +{ + register int temp; + + temp = get_absolute_expression (); + subseg_new (SEG_DATA, (subsegT)temp); +#ifdef VMS + const_flag = 0; +#endif + demand_empty_rest_of_line(); +} + +void +s_desc() +{ + register char *name; + register char c; + register char *p; + register symbolS * symbolP; + register int temp; + + /* + * Frob invented at RMS' request. Set the n_desc of a symbol. + */ + name = input_line_pointer; + c = get_symbol_end(); + p = input_line_pointer; + symbolP = symbol_table_lookup (name); + * p = c; + SKIP_WHITESPACE(); + if ( * input_line_pointer != ',' ) { + *p = 0; + as_bad("Expected comma after name \"%s\"", name); + *p = c; + ignore_rest_of_line(); + } else { + input_line_pointer ++; + temp = get_absolute_expression (); + *p = 0; + symbolP = symbol_find_or_make (name); + *p = c; + symbolP -> sy_desc = temp; + } + demand_empty_rest_of_line(); +} + +void +s_file() +{ + register char *s; + int length; + + /* Some assemblers tolerate immediately following '"' */ + if ( s = demand_copy_string( & length ) ) { + new_logical_line (s, -1); + demand_empty_rest_of_line(); + } +} + +void +s_fill() +{ + long int temp_repeat; + long int temp_size; + register long int temp_fill; + char *p; + + if ( get_absolute_expression_and_terminator(& temp_repeat) != ',' ) { + input_line_pointer --; /* Backup over what was not a ','. */ + as_warn("Expect comma after rep-size in .fill"); + ignore_rest_of_line(); + return; + } + if ( get_absolute_expression_and_terminator( & temp_size) != ',' ) { + input_line_pointer --; /* Backup over what was not a ','. */ + as_warn("Expected comma after size in .fill"); + ignore_rest_of_line(); + return; + } + /* + * This is to be compatible with BSD 4.2 AS, not for any rational reason. + */ +#define BSD_FILL_SIZE_CROCK_8 (8) + if ( temp_size > BSD_FILL_SIZE_CROCK_8 ) { + as_bad(".fill size clamped to %d.", BSD_FILL_SIZE_CROCK_8); + temp_size = BSD_FILL_SIZE_CROCK_8 ; + } if ( temp_size < 0 ) { + as_warn("Size negative: .fill ignored."); + temp_size = 0; + } else if ( temp_repeat <= 0 ) { + as_warn("Repeat < 0, .fill ignored"); + temp_size = 0; + } + temp_fill = get_absolute_expression (); + if ( temp_size && !need_pass_2 ) { + p = frag_var (rs_fill, (int)temp_size, (int)temp_size, (relax_substateT)0, (symbolS *)0, temp_repeat, (char *)0); + bzero (p, (int)temp_size); +/* + * The magic number BSD_FILL_SIZE_CROCK_4 is from BSD 4.2 VAX flavoured AS. + * The following bizzare behaviour is to be compatible with above. + * I guess they tried to take up to 8 bytes from a 4-byte expression + * and they forgot to sign extend. Un*x Sux. + */ +#define BSD_FILL_SIZE_CROCK_4 (4) + md_number_to_chars (p, temp_fill, temp_size > BSD_FILL_SIZE_CROCK_4 ? BSD_FILL_SIZE_CROCK_4 : (int)temp_size); +/* + * Note: .fill (),0 emits no frag (since we are asked to .fill 0 bytes) + * but emits no error message because it seems a legal thing to do. + * It is a degenerate case of .fill but could be emitted by a compiler. + */ + } + demand_empty_rest_of_line(); +} + +#ifdef DONTDEF +void +s_gdbbeg() +{ + register int temp; + + temp = get_absolute_expression (); + if (temp < 0) + as_warn( "Block number <0. Ignored." ); + else if (flagseen ['G']) + gdb_block_beg ( (long int) temp, frag_now, (long int)(obstack_next_free(& frags) - frag_now -> fr_literal)); + demand_empty_rest_of_line (); +} + +void +s_gdbblock() +{ + register int position; + int temp; + + if (get_absolute_expression_and_terminator (&temp) != ',') { + as_warn( "expected comma before position in .gdbblock"); + --input_line_pointer; + ignore_rest_of_line (); + return; + } + position = get_absolute_expression (); + if (flagseen ['G']) + gdb_block_position ((long int) temp, (long int) position); + demand_empty_rest_of_line (); +} + +void +s_gdbend() +{ + register int temp; + + temp = get_absolute_expression (); + if (temp < 0) + as_warn( "Block number <0. Ignored." ); + else if (flagseen ['G']) + gdb_block_end ( (long int) temp, frag_now, (long int)(obstack_next_free(& frags) - frag_now -> fr_literal)); + demand_empty_rest_of_line (); +} + +void +s_gdbsym() +{ + register char *name, + *p; + register char c; + register symbolS * symbolP; + register int temp; + + name = input_line_pointer; + c = get_symbol_end(); + p = input_line_pointer; + symbolP = symbol_find_or_make (name); + *p = c; + SKIP_WHITESPACE(); + if ( * input_line_pointer != ',' ) { + as_warn("Expected comma after name"); + ignore_rest_of_line(); + return; + } + input_line_pointer ++; + if ( (temp = get_absolute_expression ()) < 0 ) { + as_warn("Bad GDB symbol file offset (%d.) <0! Ignored.", temp); + ignore_rest_of_line(); + return; + } + if (flagseen ['G']) + gdb_symbols_fixup (symbolP, (long int)temp); + demand_empty_rest_of_line (); +} + +void +s_gdbline() +{ + int file_number, + lineno; + + if(get_absolute_expression_and_terminator(&file_number) != ',') { + as_warn("expected comman after filenum in .gdbline"); + ignore_rest_of_line(); + return; + } + lineno=get_absolute_expression(); + if(flagseen['G']) + gdb_line(file_number,lineno); + demand_empty_rest_of_line(); +} + + +void +s_gdblinetab() +{ + int file_number, + offset; + + if(get_absolute_expression_and_terminator(&file_number) != ',') { + as_warn("expected comman after filenum in .gdblinetab"); + ignore_rest_of_line(); + return; + } + offset=get_absolute_expression(); + if(flagseen['G']) + gdb_line_tab(file_number,offset); + demand_empty_rest_of_line(); +} +#endif + +void +s_globl() +{ + register char *name; + register int c; + register symbolS * symbolP; + + do { + name = input_line_pointer; + c = get_symbol_end(); + symbolP = symbol_find_or_make (name); + * input_line_pointer = c; + SKIP_WHITESPACE(); + symbolP -> sy_type |= N_EXT; + if(c==',') { + input_line_pointer++; + SKIP_WHITESPACE(); + if(*input_line_pointer=='\n') + c='\n'; + } + } while(c==','); + demand_empty_rest_of_line(); +} + +void +s_lcomm() +{ + register char *name; + register char c; + register char *p; + register int temp; + register symbolS * symbolP; + + name = input_line_pointer; + c = get_symbol_end(); + p = input_line_pointer; + *p = c; + SKIP_WHITESPACE(); + if ( * input_line_pointer != ',' ) { + as_warn("Expected comma after name"); + ignore_rest_of_line(); + return; + } + input_line_pointer ++; + if ( (temp = get_absolute_expression ()) < 0 ) { + as_warn("BSS length (%d.) <0! Ignored.", temp); + ignore_rest_of_line(); + return; + } + *p = 0; + symbolP = symbol_find_or_make (name); + *p = c; + if ( symbolP -> sy_other == 0 + && symbolP -> sy_desc == 0 + && ( ( symbolP -> sy_type == N_BSS + && symbolP -> sy_value == local_bss_counter) + || ( (symbolP -> sy_type & N_TYPE) == N_UNDF + && symbolP -> sy_value == 0))) { + symbolP -> sy_value = local_bss_counter; + symbolP -> sy_type = N_BSS; + symbolP -> sy_frag = & bss_address_frag; + local_bss_counter += temp; + } else + as_warn( "Ignoring attempt to re-define symbol from %d. to %d.", + symbolP -> sy_value, local_bss_counter ); + demand_empty_rest_of_line(); +} + +void +s_line() +{ + /* Assume delimiter is part of expression. */ + /* BSD4.2 as fails with delightful bug, so we */ + /* are not being incompatible here. */ + new_logical_line ((char *)NULL, (int)(get_absolute_expression ())); + demand_empty_rest_of_line(); +} + +void +s_long() +{ + cons(4); +} + +void +s_int() +{ + cons(4); +} + +void +s_lsym() +{ + register char *name; + register char c; + register char *p; + register segT segment; + expressionS exp; + register symbolS *symbolP; + + /* we permit ANY expression: BSD4.2 demands constants */ + name = input_line_pointer; + c = get_symbol_end(); + p = input_line_pointer; + *p = c; + SKIP_WHITESPACE(); + if ( * input_line_pointer != ',' ) { + *p = 0; + as_warn("Expected comma after name \"%s\"", name); + *p = c; + ignore_rest_of_line(); + return; + } + input_line_pointer ++; + segment = expression (& exp); + if ( segment != SEG_ABSOLUTE && segment != SEG_DATA && + segment != SEG_TEXT && segment != SEG_BSS) { + as_bad("Bad expression: %s", seg_name [(int)segment]); + ignore_rest_of_line(); + return; + } + know( segment == SEG_ABSOLUTE || segment == SEG_DATA || segment == SEG_TEXT || segment == SEG_BSS ); + *p = 0; + symbolP = symbol_new (name,(unsigned char)(seg_N_TYPE [(int) segment]), + 0, 0, (valueT)(exp . X_add_number), & zero_address_frag); + *p = c; + demand_empty_rest_of_line(); +} + +void +s_org() +{ + register segT segment; + expressionS exp; + register long int temp_fill; + register char *p; +/* + * Don't believe the documentation of BSD 4.2 AS. + * There is no such thing as a sub-segment-relative origin. + * Any absolute origin is given a warning, then assumed to be segment-relative. + * Any segmented origin expression ("foo+42") had better be in the right + * segment or the .org is ignored. + * + * BSD 4.2 AS warns if you try to .org backwards. We cannot because we + * never know sub-segment sizes when we are reading code. + * BSD will crash trying to emit -ve numbers of filler bytes in certain + * .orgs. We don't crash, but see as-write for that code. + */ +/* + * Don't make frag if need_pass_2==TRUE. + */ + segment = get_known_segmented_expression(& exp); + if ( *input_line_pointer == ',' ) { + input_line_pointer ++; + temp_fill = get_absolute_expression (); + } else + temp_fill = 0; + if ( ! need_pass_2 ) { + if (segment != now_seg && segment != SEG_ABSOLUTE) + as_warn("Illegal segment \"%s\". Segment \"%s\" assumed.", + seg_name [(int) segment], seg_name [(int) now_seg]); + p = frag_var (rs_org, 1, 1, (relax_substateT)0, exp . X_add_symbol, + exp . X_add_number, (char *)0); + * p = temp_fill; + } /* if (ok to make frag) */ + demand_empty_rest_of_line(); +} + +void +s_set() +{ + register char *name; + register char delim; + register char *end_name; + register symbolS *symbolP; + + /* + * Especial apologies for the random logic: + * this just grew, and could be parsed much more simply! + * Dean in haste. + */ + name = input_line_pointer; + delim = get_symbol_end(); + end_name = input_line_pointer; + *end_name = delim; + SKIP_WHITESPACE(); + if ( * input_line_pointer != ',' ) { + *end_name = 0; + as_warn("Expected comma after name \"%s\"", name); + *end_name = delim; + ignore_rest_of_line(); + return; + } + input_line_pointer ++; + *end_name = 0; + if(name[0]=='.' && name[1]=='\0') { + /* Turn '. = mumble' into a .org mumble */ + register segT segment; + expressionS exp; + register char *ptr; + + segment = get_known_segmented_expression(& exp); + if ( ! need_pass_2 ) { + if (segment != now_seg && segment != SEG_ABSOLUTE) + as_warn("Illegal segment \"%s\". Segment \"%s\" assumed.", + seg_name [(int) segment], seg_name [(int) now_seg]); + ptr = frag_var (rs_org, 1, 1, (relax_substateT)0, exp.X_add_symbol, + exp.X_add_number, (char *)0); + *ptr= 0; + } /* if (ok to make frag) */ + *end_name = delim; + return; + } + symbolP = symbol_find_or_make (name); + *end_name = delim; + pseudo_set (symbolP); + demand_empty_rest_of_line (); +} + +void +s_space() +{ + long int temp_repeat; + register long int temp_fill; + register char *p; + + /* Just like .fill, but temp_size = 1 */ + if ( get_absolute_expression_and_terminator( & temp_repeat) == ',' ) { + temp_fill = get_absolute_expression (); + } else { + input_line_pointer --; /* Backup over what was not a ','. */ + temp_fill = 0; + } + if ( temp_repeat <= 0 ) { + as_warn("Repeat < 0, .space ignored"); + ignore_rest_of_line(); + return; + } + if ( ! need_pass_2 ) { + p = frag_var (rs_fill, 1, 1, (relax_substateT)0, (symbolS *)0, + temp_repeat, (char *)0); + * p = temp_fill; + } + demand_empty_rest_of_line(); +} + +void +s_text() +{ + register int temp; + + temp = get_absolute_expression (); + subseg_new (SEG_TEXT, (subsegT)temp); + demand_empty_rest_of_line(); +} + + +/*( JF was static, but can't be if machine dependent pseudo-ops are to use it */ + +void +demand_empty_rest_of_line() +{ + SKIP_WHITESPACE(); + if ( is_end_of_line [* input_line_pointer] ) + { + input_line_pointer ++; + } + else + { + ignore_rest_of_line(); + } + /* Return having already swallowed end-of-line. */ +} /* Return pointing just after end-of-line. */ + + +void +ignore_rest_of_line() /* For suspect lines: gives warning. */ +{ + if ( ! is_end_of_line [* input_line_pointer]) + { + as_warn("Rest of line ignored. 1st junk character valued %d (%c)." + , * input_line_pointer, *input_line_pointer); + while ( input_line_pointer < buffer_limit + && ! is_end_of_line [* input_line_pointer] ) + { + input_line_pointer ++; + } + } + input_line_pointer ++; /* Return pointing just after end-of-line. */ + know( is_end_of_line [input_line_pointer [-1]] ); +} + +/* + * stab() + * + * Handle .stabX directives, which used to be open-coded. + * So much creeping featurism overloaded the semantics that we decided + * to put all .stabX thinking in one place. Here. + * + * We try to make any .stabX directive legal. Other people's AS will often + * do assembly-time consistency checks: eg assigning meaning to n_type bits + * and "protecting" you from setting them to certain values. (They also zero + * certain bits before emitting symbols. Tut tut.) + * + * If an expression is not absolute we either gripe or use the relocation + * information. Other people's assemblers silently forget information they + * don't need and invent information they need that you didn't supply. + * + * .stabX directives always make a symbol table entry. It may be junk if + * the rest of your .stabX directive is malformed. + */ +static void +stab (what) +int what; +{ + register symbolS * symbolP; + register char * string; + int saved_type; + int length; + int goof; /* TRUE if we have aborted. */ + long int longint; + +/* + * Enter with input_line_pointer pointing past .stabX and any following + * whitespace. + */ + goof = FALSE; /* JF who forgot this?? */ + if (what == 's') { + string = demand_copy_C_string (& length); + SKIP_WHITESPACE(); + if (* input_line_pointer == ',') + input_line_pointer ++; + else { + as_warn( "I need a comma after symbol's name" ); + goof = TRUE; + } + } else + string = ""; + +/* + * Input_line_pointer->after ','. String -> symbol name. + */ + if (! goof) { + symbolP = symbol_new (string, 0,0,0,0,(struct frag *)0); + switch (what) { + case 'd': + symbolP->sy_name = NULL; /* .stabd feature. */ + symbolP->sy_value = obstack_next_free(& frags) - frag_now->fr_literal; + symbolP->sy_frag = frag_now; + break; + + case 'n': + symbolP->sy_frag = &zero_address_frag; + break; + + case 's': + symbolP->sy_frag = & zero_address_frag; + break; + + default: + BAD_CASE( what ); + break; + } + if (get_absolute_expression_and_terminator (& longint) == ',') + symbolP->sy_type = saved_type = longint; + else { + as_warn( "I want a comma after the n_type expression" ); + goof = TRUE; + input_line_pointer --; /* Backup over a non-',' char. */ + } + } + if (! goof) { + if (get_absolute_expression_and_terminator (& longint) == ',') + symbolP->sy_other = longint; + else { + as_warn( "I want a comma after the n_other expression" ); + goof = TRUE; + input_line_pointer --; /* Backup over a non-',' char. */ + } + } + if (! goof) { + symbolP->sy_desc = get_absolute_expression (); + if (what == 's' || what == 'n') { + if (* input_line_pointer != ',') { + as_warn( "I want a comma after the n_desc expression" ); + goof = TRUE; + } else { + input_line_pointer ++; + } + } + } + if ((! goof) && (what=='s' || what=='n')) { + pseudo_set (symbolP); + symbolP->sy_type = saved_type; + } + if (goof) + ignore_rest_of_line (); + else + demand_empty_rest_of_line (); +} + +/* + * pseudo_set() + * + * In: Pointer to a symbol. + * Input_line_pointer -> expression. + * + * Out: Input_line_pointer -> just after any whitespace after expression. + * Tried to set symbol to value of expression. + * Will change sy_type, sy_value, sy_frag; + * May set need_pass_2 == TRUE. + */ +static void +pseudo_set (symbolP) + symbolS * symbolP; +{ + expressionS exp; + register segT segment; + int ext; + + know( symbolP ); /* NULL pointer is logic error. */ + ext=(symbolP->sy_type&N_EXT); + if ((segment = expression( & exp )) == SEG_NONE) + { + as_warn( "Missing expression: absolute 0 assumed" ); + exp . X_seg = SEG_ABSOLUTE; + exp . X_add_number = 0; + } + switch (segment) + { + case SEG_BIG: + as_warn( "%s number illegal. Absolute 0 assumed.", + exp . X_add_number > 0 ? "Bignum" : "Floating-Point" ); + symbolP -> sy_type = N_ABS | ext; + symbolP -> sy_value = 0; + symbolP -> sy_frag = & zero_address_frag; + break; + + case SEG_NONE: + as_warn("No expression: Using absolute 0"); + symbolP -> sy_type = N_ABS | ext; + symbolP -> sy_value = 0; + symbolP -> sy_frag = & zero_address_frag; + break; + + case SEG_DIFFERENCE: + if (exp.X_add_symbol && exp.X_subtract_symbol + && (exp.X_add_symbol->sy_type & N_TYPE) + == (exp.X_subtract_symbol->sy_type & N_TYPE)) { + if(exp.X_add_symbol->sy_frag != exp.X_subtract_symbol->sy_frag) { + as_bad("Unknown expression: symbols %s and %s are in different frags.",exp.X_add_symbol->sy_name, exp.X_subtract_symbol->sy_name); + need_pass_2++; + } + exp.X_add_number+=exp.X_add_symbol->sy_value - exp.X_subtract_symbol->sy_value; + } else + as_warn( "Complex expression. Absolute segment assumed." ); + case SEG_ABSOLUTE: + symbolP -> sy_type = N_ABS | ext; + symbolP -> sy_value = exp . X_add_number; + symbolP -> sy_frag = & zero_address_frag; + break; + + case SEG_DATA: + case SEG_TEXT: + case SEG_BSS: + symbolP -> sy_type = seg_N_TYPE [(int) segment] | ext; + symbolP -> sy_value= exp . X_add_number + exp . X_add_symbol -> sy_value; + symbolP -> sy_frag = exp . X_add_symbol -> sy_frag; + break; + + case SEG_PASS1: /* Not an error. Just try another pass. */ + symbolP->sy_forward=exp.X_add_symbol; + as_warn("Unknown expression"); + know( need_pass_2 == TRUE ); + break; + + case SEG_UNKNOWN: + symbolP->sy_forward=exp.X_add_symbol; + /* as_warn("unknown symbol"); */ + /* need_pass_2 = TRUE; */ + break; + + default: + BAD_CASE( segment ); + break; + } +} + +/* + * stabs(file), stabf(func) and stabd(line) -- for the purpose of + * source file debugging of assembly files, generate file, + * function and line number stabs, respectively. + * These functions have corresponding functions named + * filestab(), funcstab() and linestab() in input-scrub.c, + * where logical files and logical line numbers are handled. + */ + +#include + +stabs(file) + char *file; +{ + /* .stabs "file",100,0,0,. */ + (void) symbol_new(file, + N_SO, + 0, + 0, + obstack_next_free(& frags) - frag_now->fr_literal, + frag_now); +} + +stabf(func) + char *func; +{ + symbolS *symbolP; + static int void_undefined = 1; + + /* crudely filter uninteresting labels: require an initial '_' */ + if (*func++ != '_') + return; + + /* assembly functions are assumed to have void type */ + if (void_undefined) + { + /* .stabs "void:t15=15",128,0,0,0 */ + (void) symbol_new("void:t1=1", + N_LSYM, + 0, + 0, + 0, + &zero_address_frag); + void_undefined = 0; + } + + /* .stabs "func:F1",36,0,0,. */ + symbolP = symbol_new((char *) 0, + N_FUN, + 0, + 0, + obstack_next_free(& frags) - frag_now->fr_literal, + frag_now); + obstack_grow(¬es, func, strlen(func)); + obstack_1grow(¬es, ':'); + obstack_1grow(¬es, 'F'); + obstack_1grow(¬es, '1'); + obstack_1grow(¬es, '\0'); + symbolP->sy_name = obstack_finish(¬es); +} + +stabd(line) + unsigned line; +{ + /* .stabd 68,0,line */ + (void) symbol_new((char *)0, + N_SLINE, + 0, + line, + obstack_next_free(& frags) - frag_now->fr_literal, + frag_now); +} + +/* + * cons() + * + * CONStruct more frag of .bytes, or .words etc. + * Should need_pass_2 be TRUE then emit no frag(s). + * This understands EXPRESSIONS, as opposed to big_cons(). + * + * Bug (?) + * + * This has a split personality. We use expression() to read the + * value. We can detect if the value won't fit in a byte or word. + * But we can't detect if expression() discarded significant digits + * in the case of a long. Not worth the crocks required to fix it. + */ +void +cons(nbytes) /* worker to do .byte etc statements */ + /* clobbers input_line_pointer, checks */ + /* end-of-line. */ + register int nbytes; /* 1=.byte, 2=.word, 4=.long */ +{ + register char c; + register long int mask; /* High-order bits we will left-truncate, */ + /* but includes sign bit also. */ + register long int get; /* what we get */ + register long int use; /* get after truncation. */ + register long int unmask; /* what bits we will store */ + register char * p; + register segT segment; + expressionS exp; +#ifdef NS32K + void fix_new_ns32k(); +#else + void fix_new(); +#endif + + /* + * Input_line_pointer -> 1st char after pseudo-op-code and could legally + * be a end-of-line. (Or, less legally an eof - which we cope with.) + */ + /* JF << of >= number of bits in the object is undefined. In particular + SPARC (Sun 4) has problems */ + if(nbytes>=sizeof(long int)) + mask = 0; + else + mask = ~0 << (BITS_PER_CHAR * nbytes); /* Don't store these bits. */ + unmask = ~ mask; /* Do store these bits. */ +#ifdef NEVER + "Do this mod if you want every overflow check to assume SIGNED 2's complement data."; + mask = ~ (unmask >> 1); /* Includes sign bit now. */ +#endif + /* + * The following awkward logic is to parse ZERO or more expressions, + * comma seperated. Recall an expression includes its leading & + * trailing blanks. We fake a leading ',' if there is (supposed to + * be) a 1st expression, and keep demanding 1 expression for each ','. + */ + if (is_it_end_of_statement()) + { + c = 0; /* Skip loop. */ + input_line_pointer ++; /* Matches end-of-loop 'correction'. */ + } + else + c = ','; /* Do loop. */ + while ( c == ',' ) + { + segment = expression( &exp ); /* At least scan over the expression. */ + if ( ! need_pass_2 ) + { /* Still worthwhile making frags. */ + + /* Don't call this if we are going to junk this pass anyway! */ + know( segment != SEG_PASS1 ); + + if ( segment == SEG_DIFFERENCE && exp . X_add_symbol == NULL ) + { + as_warn( "Subtracting symbol \"%s\"(segment\"%s\") is too hard. Absolute segment assumed.", + exp . X_subtract_symbol -> sy_name, + seg_name [(int) N_TYPE_seg [exp . X_subtract_symbol -> sy_type & N_TYPE]]); + segment = SEG_ABSOLUTE; + /* Leave exp . X_add_number alone. */ + } + p = frag_more (nbytes); + switch (segment) + { + case SEG_BIG: + as_warn( "%s number illegal. Absolute 0 assumed.", + exp . X_add_number > 0 ? "Bignum" : "Floating-Point"); + md_number_to_chars (p, (long)0, nbytes); + break; + + case SEG_NONE: + as_warn( "0 assumed for missing expression" ); + exp . X_add_number = 0; + know( exp . X_add_symbol == NULL ); + /* fall into SEG_ABSOLUTE */ + case SEG_ABSOLUTE: + get = exp . X_add_number; + use = get & unmask; + if ( (get & mask) && (get & mask) != mask ) + { /* Leading bits contain both 0s & 1s. */ + as_warn("Value x%x truncated to x%x.", get, use); + } + md_number_to_chars (p, use, nbytes); /* put bytes in right order. */ + break; + + case SEG_DIFFERENCE: +#ifndef WORKING_DOT_WORD + if(nbytes==2) { + struct broken_word *x; + + x=(struct broken_word *)xmalloc(sizeof(struct broken_word)); + x->next_broken_word=broken_words; + broken_words=x; + x->frag=frag_now; + x->word_goes_here=p; + x->dispfrag=0; + x->add=exp.X_add_symbol; + x->sub=exp.X_subtract_symbol; + x->addnum=exp.X_add_number; + x->added=0; + new_broken_words++; + break; + } + /* Else Fall through into. . . */ +#endif + case SEG_BSS: + case SEG_UNKNOWN: + case SEG_TEXT: + case SEG_DATA: +#if defined(SPARC) || defined(I860) + fix_new (frag_now, p - frag_now -> fr_literal, nbytes, + exp . X_add_symbol, exp . X_subtract_symbol, + exp . X_add_number, 0, RELOC_32); +#endif +#ifdef NS32K + fix_new_ns32k (frag_now, p - frag_now -> fr_literal, nbytes, + exp . X_add_symbol, exp . X_subtract_symbol, + exp . X_add_number, 0, 0, 2, 0, 0); +#endif +#if !defined(SPARC) && !defined(NS32K) && !defined(I860) + fix_new (frag_now, p - frag_now -> fr_literal, nbytes, + exp . X_add_symbol, exp . X_subtract_symbol, + exp . X_add_number, 0); +#endif + break; + + default: + BAD_CASE( segment ); + break; + } /* switch(segment) */ + } /* if(!need_pass_2) */ + c = * input_line_pointer ++; + } /* while(c==',') */ + input_line_pointer --; /* Put terminator back into stream. */ + demand_empty_rest_of_line(); +} /* cons() */ + +/* + * big_cons() + * + * CONStruct more frag(s) of .quads, or .octa etc. + * Makes 0 or more new frags. + * If need_pass_2 == TRUE, generate no frag. + * This understands only bignums, not expressions. Cons() understands + * expressions. + * + * Constants recognised are '0...'(octal) '0x...'(hex) '...'(decimal). + * + * This creates objects with struct obstack_control objs, destroying + * any context objs held about a partially completed object. Beware! + * + * + * I think it sucks to have 2 different types of integers, with 2 + * routines to read them, store them etc. + * It would be nicer to permit bignums in expressions and only + * complain if the result overflowed. However, due to "efficiency"... + */ +void +big_cons(nbytes) /* worker to do .quad etc statements */ + /* clobbers input_line_pointer, checks */ + /* end-of-line. */ + register int nbytes; /* 8=.quad 16=.octa ... */ +{ + register char c; /* input_line_pointer -> c. */ + register int radix; + register long int length; /* Number of chars in an object. */ + register int digit; /* Value of 1 digit. */ + register int carry; /* For multi-precision arithmetic. */ + register int work; /* For multi-precision arithmetic. */ + register char * p; /* For multi-precision arithmetic. */ + + extern char hex_value[]; /* In hex_value.c. */ + + /* + * The following awkward logic is to parse ZERO or more strings, + * comma seperated. Recall an expression includes its leading & + * trailing blanks. We fake a leading ',' if there is (supposed to + * be) a 1st expression, and keep demanding 1 expression for each ','. + */ + if (is_it_end_of_statement()) + { + c = 0; /* Skip loop. */ + } + else + { + c = ','; /* Do loop. */ + -- input_line_pointer; + } + while (c == ',') + { + ++ input_line_pointer; + SKIP_WHITESPACE(); + c = * input_line_pointer; + /* C contains 1st non-blank character of what we hope is a number. */ + if (c == '0') + { + c = * ++ input_line_pointer; + if (c == 'x' || c=='X') + { + c = * ++ input_line_pointer; + radix = 16; + } + else + { + radix = 8; + } + } + else + { + radix = 10; + } + /* + * This feature (?) is here to stop people worrying about + * mysterious zero constants: which is what they get when + * they completely omit digits. + */ + if (hex_value[c] >= radix) + { + as_warn( "Missing digits. 0 assumed." ); + } + bignum_high = bignum_low - 1; /* Start constant with 0 chars. */ + for( ; (digit = hex_value [c]) < radix; c = * ++ input_line_pointer) + { + /* Multiply existing number by radix, then add digit. */ + carry = digit; + for (p=bignum_low; p <= bignum_high; p++) + { + work = (*p & MASK_CHAR) * radix + carry; + *p = work & MASK_CHAR; + carry = work >> BITS_PER_CHAR; + } + if (carry) + { + grow_bignum(); + * bignum_high = carry & MASK_CHAR; + know( (carry & ~ MASK_CHAR) == 0); + } + } + length = bignum_high - bignum_low + 1; + if (length > nbytes) + { + as_warn( "Most significant bits truncated in integer constant." ); + } + else + { + register long int leading_zeroes; + + for(leading_zeroes = nbytes - length; + leading_zeroes; + leading_zeroes --) + { + grow_bignum(); + * bignum_high = 0; + } + } + if (! need_pass_2) + { + p = frag_more (nbytes); + bcopy (bignum_low, p, (int)nbytes); + } + /* C contains character after number. */ + SKIP_WHITESPACE(); + c = * input_line_pointer; + /* C contains 1st non-blank character after number. */ + } + demand_empty_rest_of_line(); +} /* big_cons() */ + +static void +grow_bignum() /* Extend bignum by 1 char. */ +{ + register long int length; + + bignum_high ++; + if (bignum_high >= bignum_limit) + { + length = bignum_limit - bignum_low; + bignum_low = xrealloc (bignum_low, length + length); + bignum_high = bignum_low + length; + bignum_limit = bignum_low + length + length; + } +} /* grow_bignum(); */ + +/* + * float_cons() + * + * CONStruct some more frag chars of .floats .ffloats etc. + * Makes 0 or more new frags. + * If need_pass_2 == TRUE, no frags are emitted. + * This understands only floating literals, not expressions. Sorry. + * + * A floating constant is defined by atof_generic(), except it is preceded + * by 0d 0f 0g or 0h. After observing the STRANGE way my BSD AS does its + * reading, I decided to be incompatible. This always tries to give you + * rounded bits to the precision of the pseudo-op. Former AS did premature + * truncatation, restored noisy bits instead of trailing 0s AND gave you + * a choice of 2 flavours of noise according to which of 2 floating-point + * scanners you directed AS to use. + * + * In: input_line_pointer -> whitespace before, or '0' of flonum. + * + */ + +void /* JF was static, but can't be if VAX.C is goning to use it */ +float_cons(float_type) /* Worker to do .float etc statements. */ + /* Clobbers input_line-pointer, checks end-of-line. */ + register float_type; /* 'f':.ffloat ... 'F':.float ... */ +{ + register char * p; + register char c; + int length; /* Number of chars in an object. */ + register char * err; /* Error from scanning floating literal. */ + char temp [MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT]; + + /* + * The following awkward logic is to parse ZERO or more strings, + * comma seperated. Recall an expression includes its leading & + * trailing blanks. We fake a leading ',' if there is (supposed to + * be) a 1st expression, and keep demanding 1 expression for each ','. + */ + if (is_it_end_of_statement()) + { + c = 0; /* Skip loop. */ + ++ input_line_pointer; /* -> past termintor. */ + } + else + { + c = ','; /* Do loop. */ + } + while (c == ',') + { + /* input_line_pointer -> 1st char of a flonum (we hope!). */ + SKIP_WHITESPACE(); + /* Skip any 0{letter} that may be present. Don't even check if the + * letter is legal. Someone may invent a "z" format and this routine + * has no use for such information. Lusers beware: you get + * diagnostics if your input is ill-conditioned. + */ + + if(input_line_pointer[0]=='0' && isalpha(input_line_pointer[1])) + input_line_pointer+=2; + + err = md_atof (float_type, temp, &length); + know( length <= MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT); + know( length > 0 ); + if (* err) + { + as_warn( "Bad floating literal: %s", err); + ignore_rest_of_line(); + /* Input_line_pointer -> just after end-of-line. */ + c = 0; /* Break out of loop. */ + } + else + { + if ( ! need_pass_2) + { + p = frag_more (length); + bcopy (temp, p, length); + } + SKIP_WHITESPACE(); + c = * input_line_pointer ++; + /* C contains 1st non-white character after number. */ + /* input_line_pointer -> just after terminator (c). */ + } + } + -- input_line_pointer; /* -> terminator (is not ','). */ + demand_empty_rest_of_line(); +} /* float_cons() */ + +/* + * stringer() + * + * We read 0 or more ',' seperated, double-quoted strings. + * + * Caller should have checked need_pass_2 is FALSE because we don't check it. + */ +static void +stringer(append_zero) /* Worker to do .ascii etc statements. */ + /* Checks end-of-line. */ + register int append_zero; /* 0: don't append '\0', else 1 */ +{ + /* register char * p; JF unused */ + /* register int length; JF unused */ /* Length of string we read, excluding */ + /* trailing '\0' implied by closing quote. */ + /* register char * where; JF unused */ + /* register fragS * fragP; JF unused */ + register int c; + + /* + * The following awkward logic is to parse ZERO or more strings, + * comma seperated. Recall a string expression includes spaces + * before the opening '\"' and spaces after the closing '\"'. + * We fake a leading ',' if there is (supposed to be) + * a 1st, expression. We keep demanding expressions for each + * ','. + */ + if (is_it_end_of_statement()) + { + c = 0; /* Skip loop. */ + ++ input_line_pointer; /* Compensate for end of loop. */ + } + else + { + c = ','; /* Do loop. */ + } + for ( ; c == ','; c = *input_line_pointer ++) + { + SKIP_WHITESPACE(); + if (* input_line_pointer == '\"') + { + ++ input_line_pointer; /* -> 1st char of string. */ + while ( (c = next_char_of_string()) >= 0) + { + FRAG_APPEND_1_CHAR( c ); + } + if (append_zero) + { + FRAG_APPEND_1_CHAR( 0 ); + } + know( input_line_pointer [-1] == '\"' ); + } + else + { + as_warn( "Expected \"-ed string" ); + } + SKIP_WHITESPACE(); + } + -- input_line_pointer; + demand_empty_rest_of_line(); +} /* stringer() */ + +static int +next_char_of_string () +{ + register int c; + + c = * input_line_pointer ++; + switch (c) + { + case '\"': + c = -1; + break; + + case '\\': + switch (c = * input_line_pointer ++) + { + case 'b': + c = '\b'; + break; + + case 'f': + c = '\f'; + break; + + case 'n': + c = '\n'; + break; + + case 'r': + c = '\r'; + break; + + case 't': + c = '\t'; + break; + + case '\\': + case '"': + break; /* As itself. */ + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + long int number; + + for (number = 0; isdigit(c); c = * input_line_pointer ++) + { + number = number * 8 + c - '0'; + } + c = number; + } + -- input_line_pointer; + break; + + case '\n': +/* as_fatal( "Unterminated string - use app!" ); */ +/* To be compatible with BSD 4.2 as: give the luser a linefeed!! */ + c = '\n'; + break; + + default: + as_warn( "Bad escaped character in string, '?' assumed" ); + c = '?'; + break; + } + break; + + default: + break; + } + return (c); +} + +static segT +get_segmented_expression ( expP ) + register expressionS * expP; +{ + register segT retval; + + if ( (retval = expression( expP )) == SEG_PASS1 || retval == SEG_NONE || retval == SEG_BIG ) + { + as_warn("Expected address expression: absolute 0 assumed"); + retval = expP -> X_seg = SEG_ABSOLUTE; + expP -> X_add_number = 0; + expP -> X_add_symbol = expP -> X_subtract_symbol = 0; + } + return (retval); /* SEG_ ABSOLUTE,UNKNOWN,DATA,TEXT,BSS */ +} + +static segT +get_known_segmented_expression ( expP ) + register expressionS * expP; +{ + register segT retval; + register char * name1; + register char * name2; + + if ( (retval = get_segmented_expression (expP)) == SEG_UNKNOWN + ) + { + name1 = expP -> X_add_symbol ? expP -> X_add_symbol -> sy_name : ""; + name2 = expP -> X_subtract_symbol ? expP -> X_subtract_symbol -> sy_name : ""; + if ( name1 && name2 ) + { + as_warn("Symbols \"%s\" \"%s\" are undefined: absolute 0 assumed.", + name1, name2); + } + else + { + as_warn("Symbol \"%s\" undefined: absolute 0 assumed.", + name1 ? name1 : name2); + } + retval = expP -> X_seg = SEG_ABSOLUTE; + expP -> X_add_number = 0; + expP -> X_add_symbol = expP -> X_subtract_symbol = NULL; + } + know( retval == SEG_ABSOLUTE || retval == SEG_DATA || retval == SEG_TEXT || retval == SEG_BSS || retval == SEG_DIFFERENCE ); + return (retval); +} /* get_known_segmented_expression() */ + + + +/* static */ long int /* JF was static, but can't be if the MD pseudos are to use it */ +get_absolute_expression () +{ + expressionS exp; + register segT s; + + if ( (s = expression(& exp)) != SEG_ABSOLUTE ) + { + if ( s != SEG_NONE ) + { + as_warn( "Bad Absolute Expression, absolute 0 assumed."); + } + exp . X_add_number = 0; + } + return (exp . X_add_number); +} + +static char /* return terminator */ +get_absolute_expression_and_terminator( val_pointer) + long int * val_pointer; /* return value of expression */ +{ + * val_pointer = get_absolute_expression (); + return ( * input_line_pointer ++ ); +} + +/* + * demand_copy_C_string() + * + * Like demand_copy_string, but return NULL if the string contains any '\0's. + * Give a warning if that happens. + */ +static char * +demand_copy_C_string (len_pointer) + int * len_pointer; +{ + register char * s; + + if (s = demand_copy_string (len_pointer)) + { + register int len; + + for (len = * len_pointer; + len > 0; + len--) + { + if (* s == 0) + { + s = 0; + len = 1; + * len_pointer = 0; + as_warn( "This string may not contain \'\\0\'" ); + } + } + } + return (s); +} + +/* + * demand_copy_string() + * + * Demand string, but return a safe (=private) copy of the string. + * Return NULL if we can't read a string here. + */ +static char * +demand_copy_string (lenP) + int * lenP; +{ + register int c; + register int len; + char * retval; + + len = 0; + SKIP_WHITESPACE(); + if (* input_line_pointer == '\"') + { + input_line_pointer ++; /* Skip opening quote. */ + while ( (c = next_char_of_string()) >= 0 ) { + obstack_1grow ( ¬es, c ); + len ++; + } + /* JF this next line is so demand_copy_C_string will return a null + termanated string. */ + obstack_1grow(¬es,'\0'); + retval=obstack_finish( ¬es); + } else { + as_warn( "Missing string" ); + retval = NULL; + ignore_rest_of_line (); + } + * lenP = len; + return (retval); +} + +/* + * is_it_end_of_statement() + * + * In: Input_line_pointer -> next character. + * + * Do: Skip input_line_pointer over all whitespace. + * + * Out: TRUE if input_line_pointer -> end-of-line. + */ +static int +is_it_end_of_statement() +{ + SKIP_WHITESPACE(); + return (is_end_of_line [* input_line_pointer]); +} + +void +equals(sym_name) +char *sym_name; +{ + register struct symbol * symbolP; /* symbol we are working with */ + + input_line_pointer++; + if(*input_line_pointer=='=') + input_line_pointer++; + + while(*input_line_pointer==' ' || *input_line_pointer=='\t') + input_line_pointer++; + + if(sym_name[0]=='.' && sym_name[1]=='\0') { + /* Turn '. = mumble' into a .org mumble */ + register segT segment; + expressionS exp; + register char *p; + + segment = get_known_segmented_expression(& exp); + if ( ! need_pass_2 ) { + if (segment != now_seg && segment != SEG_ABSOLUTE) + as_warn("Illegal segment \"%s\". Segment \"%s\" assumed.", + seg_name [(int) segment], seg_name [(int) now_seg]); + p = frag_var (rs_org, 1, 1, (relax_substateT)0, exp.X_add_symbol, + exp.X_add_number, (char *)0); + * p = 0; + } /* if (ok to make frag) */ + } else { + symbolP=symbol_find_or_make(sym_name); + pseudo_set(symbolP); + } +} + +/* end: read.c */ diff --git a/gnu/usr.bin/as/read.h b/gnu/usr.bin/as/read.h new file mode 100644 index 0000000000..6b46e8fcac --- /dev/null +++ b/gnu/usr.bin/as/read.h @@ -0,0 +1,47 @@ +/* read.h - of read.c + Copyright (C) 1986 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +extern char * input_line_pointer; /* -> char we are parsing now. */ + +#define PERMIT_WHITESPACE /* Define to make whitespace be allowed in */ + /* many syntactically unnecessary places. */ + /* Normally undefined. For compatibility */ + /* with ancient GNU cc. */ +#undef PERMIT_WHITESPACE + +#ifdef PERMIT_WHITESPACE +#define SKIP_WHITESPACE() {if (* input_line_pointer == ' ') ++ input_line_pointer;} +#else +#define SKIP_WHITESPACE() ASSERT( * input_line_pointer != ' ' ) +#endif + + +#define LEX_NAME (1) /* may continue a name */ +#define LEX_BEGIN_NAME (2) /* may begin a name */ + +#define is_name_beginner(c) ( lex_type[c] & LEX_BEGIN_NAME ) +#define is_part_of_name(c) ( lex_type[c] & LEX_NAME ) + +extern const char lex_type[]; + +void read_begin(); +void read_end(); +void read_a_source_file(); + +/* end: read.h */ diff --git a/gnu/usr.bin/as/struc-symbol.h b/gnu/usr.bin/as/struc-symbol.h new file mode 100644 index 0000000000..11eab6b72b --- /dev/null +++ b/gnu/usr.bin/as/struc-symbol.h @@ -0,0 +1,72 @@ +/* struct_symbol.h - Internal symbol structure + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef VMS +#include "a.out.gnu.h" /* Needed to define struct nlist. Sigh. */ +#else +#include "a_out.h" +#endif + +struct symbol /* our version of an nlist node */ +{ + struct nlist sy_nlist; /* what we write in .o file (if permitted) */ + long unsigned sy_name_offset; /* 4-origin position of sy_name in symbols */ + /* part of object file. */ + /* 0 for (nameless) .stabd symbols. */ + /* Not used until write_object_file() time. */ + long int sy_number; /* 24 bit symbol number. */ + /* Symbol numbers start at 0 and are */ + /* unsigned. */ + struct symbol * sy_next; /* forward chain, or NULL */ + struct frag * sy_frag; /* NULL or -> frag this symbol attaches to. */ + struct symbol *sy_forward; /* value is really that of this other symbol */ +}; + +typedef struct symbol symbolS; + +#define sy_name sy_nlist .n_un. n_name + /* Name field always points to a string. */ + /* 0 means .stabd-like anonymous symbol. */ +#define sy_type sy_nlist. n_type +#define sy_other sy_nlist. n_other +#define sy_desc sy_nlist. n_desc +#define sy_value sy_nlist. n_value + /* Value of symbol is this value + object */ + /* file address of sy_frag. */ + +typedef unsigned valueT; /* The type of n_value. Helps casting. */ + +/* end: struct_symbol.h */ +#ifndef WORKING_DOT_WORD +struct broken_word { + struct broken_word *next_broken_word;/* One of these strucs per .word x-y */ + fragS *frag; /* Which frag its in */ + char *word_goes_here;/* Where in the frag it is */ + fragS *dispfrag; /* where to add the break */ + symbolS *add; /* symbol_x */ + symbolS *sub; /* - symbol_y */ + long addnum; /* + addnum */ + int added; /* nasty thing happend yet? */ + /* 1: added and has a long-jump */ + /* 2: added but uses someone elses long-jump */ + struct broken_word *use_jump; /* points to broken_word with a similar + long-jump */ +}; +extern struct broken_word *broken_words; +#endif diff --git a/gnu/usr.bin/as/subsegs.c b/gnu/usr.bin/as/subsegs.c new file mode 100644 index 0000000000..c9eea3da0b --- /dev/null +++ b/gnu/usr.bin/as/subsegs.c @@ -0,0 +1,292 @@ +/* subsegs.c - subsegments - + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * Segments & sub-segments. + */ + +#include "as.h" +#include "subsegs.h" +#include "obstack.h" +#include "frags.h" +#include "struc-symbol.h" +#include "write.h" + +frchainS* frchain_root, + * frchain_now, /* Commented in "subsegs.h". */ + * data0_frchainP; + + +const int /* in: segT out: N_TYPE bits */ +seg_N_TYPE[] = { + N_ABS, + N_TEXT, + N_DATA, + N_BSS, + N_UNDF, + N_UNDF, + N_UNDF, + N_UNDF, + N_UNDF, + N_UNDF +}; + + +char * const /* in: segT out: char* */ +seg_name[] = { + "absolute", + "text", + "data", + "bss", + "unknown", + "absent", + "pass1", + "ASSEMBLER-INTERNAL-LOGIC-ERROR!", + "bignum/flonum", + "difference", + "" + }; /* Used by error reporters, dumpers etc. */ + +const segT N_TYPE_seg [N_TYPE+2] = /* N_TYPE == 0x1E = 32-2 */ +{ + SEG_UNKNOWN, /* N_UNDF == 0 */ + SEG_GOOF, + SEG_ABSOLUTE, /* N_ABS == 2 */ + SEG_GOOF, + SEG_TEXT, /* N_TEXT == 4 */ + SEG_GOOF, + SEG_DATA, /* N_DATA == 6 */ + SEG_GOOF, + SEG_BSS, /* N_BSS == 8 */ + SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, + SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, + SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF +}; + +void +subsegs_begin() +{ + /* Check table(s) seg_name[], seg_N_TYPE[] is in correct order */ + know( SEG_ABSOLUTE ==0 ); + know( SEG_TEXT ==1 ); + know( SEG_DATA ==2 ); + know( SEG_BSS ==3 ); + know( SEG_UNKNOWN ==4 ); + know( SEG_NONE ==5 ); + know( SEG_PASS1 ==6 ); + know( SEG_GOOF ==7 ); + know( SEG_BIG ==8 ); + know( SEG_DIFFERENCE ==9 ); + know( SEG_MAXIMUM_ORDINAL == SEG_DIFFERENCE ); + know( seg_name [(int) SEG_MAXIMUM_ORDINAL + 1] [0] == 0 ); + + obstack_begin( &frags, 5000); + frchain_root = NULL; + frchain_now = NULL; /* Warn new_subseg() that we are booting. */ + /* Fake up 1st frag. */ + /* It won't be used=> is ok if obstack... */ + /* pads the end of it for alignment. */ + frag_now=(fragS *)obstack_alloc(&frags,SIZEOF_STRUCT_FRAG); + /* obstack_1blank( &frags, SIZEOF_STRUCT_FRAG, & frag_now ); */ + /* This 1st frag will not be in any frchain. */ + /* We simply give subseg_new somewhere to scribble. */ + now_subseg = 42; /* Lie for 1st call to subseg_new. */ + subseg_new (SEG_DATA, 0); /* .data 0 */ + data0_frchainP = frchain_now; +} + +/* + * subseg_change() + * + * Change the subsegment we are in, BUT DO NOT MAKE A NEW FRAG for the + * subsegment. If we are already in the correct subsegment, change nothing. + * This is used eg as a worker for subseg_new [which does make a new frag_now] + * and for changing segments after we have read the source. We construct eg + * fixSs even after the source file is read, so we do have to keep the + * segment context correct. + */ +void +subseg_change (seg, subseg) + register segT seg; + register int subseg; +{ + now_seg = seg; + now_subseg = subseg; + if (seg == SEG_DATA) + { + seg_fix_rootP = & data_fix_root; + } + else + { + know (seg == SEG_TEXT); + seg_fix_rootP = & text_fix_root; + } +} + +/* + * subseg_new() + * + * If you attempt to change to the current subsegment, nothing happens. + * + * In: segT, subsegT code for new subsegment. + * frag_now -> incomplete frag for current subsegment. + * If frag_now==NULL, then there is no old, incomplete frag, so + * the old frag is not closed off. + * + * Out: now_subseg, now_seg updated. + * Frchain_now points to the (possibly new) struct frchain for this + * sub-segment. + * Frchain_root updated if needed. + */ + +void +subseg_new (seg, subseg) /* begin assembly for a new sub-segment */ + register segT seg; /* SEG_DATA or SEG_TEXT */ + register subsegT subseg; +{ + long tmp; /* JF for obstack alignment hacking */ + + know( seg == SEG_DATA || seg == SEG_TEXT ); + + if (seg != now_seg || subseg != now_subseg) + { /* we just changed sub-segments */ + register frchainS * frcP; /* crawl frchain chain */ + register frchainS** lastPP; /* address of last pointer */ + frchainS * newP; /* address of new frchain */ + register fragS * former_last_fragP; + register fragS * new_fragP; + + if (frag_now) /* If not bootstrapping. */ + { + frag_now -> fr_fix = obstack_next_free(& frags) - frag_now -> fr_literal; + frag_wane(frag_now); /* Close off any frag in old subseg. */ + } +/* + * It would be nice to keep an obstack for each subsegment, if we swap + * subsegments a lot. Hence we would have much fewer frag_wanes(). + */ + { + + obstack_finish( &frags); + /* + * If we don't do the above, the next object we put on obstack frags + * will appear to start at the fr_literal of the current frag. + * Also, above ensures that the next object will begin on a + * address that is aligned correctly for the engine that runs + * this program. + */ + } + subseg_change (seg, (int)subseg); + /* + * Attempt to find or make a frchain for that sub seg. + * Crawl along chain of frchainSs, begins @ frchain_root. + * If we need to make a frchainS, link it into correct + * position of chain rooted in frchain_root. + */ + for (frcP = * (lastPP = & frchain_root); + frcP + && (int)(frcP -> frch_seg) <= (int)seg; + frcP = * ( lastPP = & frcP -> frch_next) + ) + { + if ( (int)(frcP -> frch_seg) == (int)seg + && frcP -> frch_subseg >= subseg) + { + break; + } + } + /* + * frcP: Address of the 1st frchainS in correct segment with + * frch_subseg >= subseg. + * We want to either use this frchainS, or we want + * to insert a new frchainS just before it. + * + * If frcP==NULL, then we are at the end of the chain + * of frchainS-s. A NULL frcP means we fell off the end + * of the chain looking for a + * frch_subseg >= subseg, so we + * must make a new frchainS. + * + * If we ever maintain a pointer to + * the last frchainS in the chain, we change that pointer + * ONLY when frcP==NULL. + * + * lastPP: Address of the pointer with value frcP; + * Never NULL. + * May point to frchain_root. + * + */ + if ( ! frcP + || ( (int)(frcP -> frch_seg) > (int)seg + || frcP->frch_subseg > subseg)) /* Kinky logic only works with 2 segments. */ + { + /* + * This should be the only code that creates a frchainS. + */ + newP=(frchainS *)obstack_alloc(&frags,sizeof(frchainS)); + /* obstack_1blank( &frags, sizeof(frchainS), &newP); */ + /* This begines on a good boundary */ + /* because a obstack_done() preceeded it. */ + /* It implies an obstack_done(), so we */ + /* expect the next object allocated to */ + /* begin on a correct boundary. */ + *lastPP = newP; + newP -> frch_next = frcP; /* perhaps NULL */ + (frcP = newP) -> frch_subseg = subseg; + newP -> frch_seg = seg; + newP -> frch_last = NULL; + } + /* + * Here with frcP ->ing to the frchainS for subseg. + */ + frchain_now = frcP; + /* + * Make a fresh frag for the subsegment. + */ + /* We expect this to happen on a correct */ + /* boundary since it was proceeded by a */ + /* obstack_done(). */ + tmp=obstack_alignment_mask(&frags); /* JF disable alignment */ + obstack_alignment_mask(&frags)=0; + frag_now=(fragS *)obstack_alloc(&frags,SIZEOF_STRUCT_FRAG); + obstack_alignment_mask(&frags)=tmp; + /* know( frags . obstack_c_next_free == frag_now -> fr_literal ); */ + /* But we want any more chars to come */ + /* immediately after the structure we just made. */ + new_fragP = frag_now; + new_fragP -> fr_next = NULL; + /* + * Append new frag to current frchain. + */ + former_last_fragP = frcP -> frch_last; + if (former_last_fragP) + { + know( former_last_fragP -> fr_next == NULL ); + know( frchain_now -> frch_root ); + former_last_fragP -> fr_next = new_fragP; + } + else + { + frcP -> frch_root = new_fragP; + } + frcP -> frch_last = new_fragP; + } /* if (changing subsegments) */ +} /* subseg_new() */ + +/* end: subsegs.c */ diff --git a/gnu/usr.bin/as/subsegs.h b/gnu/usr.bin/as/subsegs.h new file mode 100644 index 0000000000..b8dbaf76e5 --- /dev/null +++ b/gnu/usr.bin/as/subsegs.h @@ -0,0 +1,65 @@ +/* subsegs.h -> subsegs.c + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * For every sub-segment the user mentions in the ASsembler program, + * we make one struct frchain. Each sub-segment has exactly one struct frchain + * and vice versa. + * + * Struct frchain's are forward chained (in ascending order of sub-segment + * code number). The chain runs through frch_next of each subsegment. + * This makes it hard to find a subsegment's frags + * if programmer uses a lot of them. Most programs only use text0 and + * data0, so they don't suffer. At least this way: + * (1) There are no "arbitrary" restrictions on how many subsegments + * can be programmed; + * (2) Subsegments' frchain-s are (later) chained together in the order in + * which they are emitted for object file viz text then data. + * + * From each struct frchain dangles a chain of struct frags. The frags + * represent code fragments, for that sub-segment, forward chained. + */ + +struct frchain /* control building of a frag chain */ +{ /* FRCH = FRagment CHain control */ + struct frag * frch_root; /* 1st struct frag in chain, or NULL */ + struct frag * frch_last; /* last struct frag in chain, or NULL */ + struct frchain * frch_next; /* next in chain of struct frchain-s */ + segT frch_seg; /* SEG_TEXT or SEG_DATA. */ + subsegT frch_subseg; /* subsegment number of this chain */ +}; + +typedef struct frchain frchainS; + +extern frchainS * frchain_root; /* NULL means no frchains yet. */ + /* all subsegments' chains hang off here */ + +extern frchainS * frchain_now; + /* Frchain we are assembling into now */ + /* That is, the current segment's frag */ + /* chain, even if it contains no (complete) */ + /* frags. */ + +extern frchainS * data0_frchainP; + /* Sentinel for frchain crawling. */ + /* Points to the 1st data-segment frchain. */ + /* (Which is pointed to by the last text- */ + /* segment frchain.) */ + +/* end: subsegs.h */ diff --git a/gnu/usr.bin/as/symbols.c b/gnu/usr.bin/as/symbols.c new file mode 100644 index 0000000000..ce7197a8e0 --- /dev/null +++ b/gnu/usr.bin/as/symbols.c @@ -0,0 +1,438 @@ +/* symbols.c -symbol table- + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include "as.h" +#include "hash.h" +#include "obstack.h" /* For "symbols.h" */ +#include "struc-symbol.h" +#include "symbols.h" +#include "frags.h" + +#ifndef WORKING_DOT_WORD +extern int new_broken_words; +#endif +#ifdef VMS +extern char const_flag; +#endif + +static +struct hash_control * +sy_hash; /* symbol-name => struct symbol pointer */ + + /* Below are commented in "symbols.h". */ +unsigned int local_bss_counter; +symbolS * symbol_rootP; +symbolS * symbol_lastP; +symbolS abs_symbol; +struct obstack notes; + + + +symbolS * symbol_find(); /* Keep C compiler happy. */ + +/* + * Un*x idea of local labels. They are made by "n:" where n + * is any decimal digit. Refer to them with + * "nb" for previous (backward) n: + * or "nf" for next (forward) n:. + * + * Like Un*x AS, we have one set of local label counters for entire assembly, + * not one set per (sub)segment like in most assemblers. This implies that + * one can refer to a label in another segment, and indeed some crufty + * compilers have done just that. + * + * I document the symbol names here to save duplicating words elsewhere. + * The mth occurence of label n: is turned into the symbol "Ln^Am" where + * n is a digit and m is a decimal number. "L" makes it a label discarded + * unless debugging and "^A"('\1') ensures no ordinary symbol SHOULD get the + * same name as a local label symbol. The first "4:" is "L4^A1" - the m + * numbers begin at 1. + */ + +typedef short unsigned int +local_label_countT; + +static local_label_countT +local_label_counter[10]; + +static /* Returned to caller, then copied. */ + char symbol_name_build[12]; /* used for created names ("4f") */ + +#ifdef SUN_ASM_SYNTAX +int local_label_defined[10]; +#endif + + +void +symbol_begin() +{ + symbol_lastP = NULL; + symbol_rootP = NULL; /* In case we have 0 symbols (!!) */ + sy_hash = hash_new(); + bzero ((char *)(& abs_symbol), sizeof(abs_symbol)); + abs_symbol . sy_type = N_ABS; /* Can't initialise a union. Sigh. */ + bzero ((char *)(local_label_counter), sizeof(local_label_counter) ); + local_bss_counter = 0; +} + +/* + * local_label_name() + * + * Caller must copy returned name: we re-use the area for the next name. + */ + +char * /* Return local label name. */ +local_label_name(n, augend) + register int n; /* we just saw "n:", "nf" or "nb" : n a digit */ + register int augend; /* 0 for nb, 1 for n:, nf */ +{ + register char * p; + register char * q; + char symbol_name_temporary[10]; /* build up a number, BACKWARDS */ + + know( n >= 0 ); + know( augend == 0 || augend == 1 ); + p = symbol_name_build; + * p ++ = 'L'; + * p ++ = n + '0'; /* Make into ASCII */ + * p ++ = 1; /* ^A */ + n = local_label_counter [ n ] + augend; + /* version number of this local label */ + /* + * Next code just does sprintf( {}, "%d", n); + * It is more elegant to do the next part recursively, but a procedure + * call for each digit emitted is considered too costly. + */ + q = symbol_name_temporary; + for (*q++=0; n; q++) /* emits NOTHING if n starts as 0 */ + { + know(n>0); /* We expect n > 0 always */ + *q = n % 10 + '0'; + n /= 10; + } + while ( * p ++ = * -- q ) + { + } + /* The label, as a '\0' ended string, starts at symbol_name_build. */ + return (symbol_name_build); +} + + +void +local_colon (n) + int n; /* just saw "n:" */ +{ + local_label_counter [n] ++; +#ifdef SUN_ASM_SYNTAX + local_label_defined[n]=1; +#endif + colon (local_label_name (n, 0)); +} + +/* + * symbol_new() + * + * Return a pointer to a new symbol. + * Die if we can't make a new symbol. + * Fill in the symbol's values. + * Add symbol to end of symbol chain. + * + * + * Please always call this to create a new symbol. + * + * Changes since 1985: Symbol names may not contain '\0'. Sigh. + */ + +symbolS * +symbol_new (name, type, other, desc, value, frag) + char * name; /* We copy this: OK to alter your copy. */ + unsigned char type; /* As in . */ + char other; /* As in . */ + short int desc; /* As in . */ + valueT value; /* As in , often an address. */ + /* Often used as offset from frag address. */ + struct frag * frag; /* For sy_frag. */ +{ + register symbolS * symbolP; + register char * preserved_copy_of_name; + register unsigned int name_length; + char * p; + + name_length = strlen(name) + 1; + obstack_grow(¬es,name,name_length); + p=obstack_finish(¬es); + /* obstack_1done( ¬es, name, name_length, &p ); */ + preserved_copy_of_name = p; + p=obstack_alloc(¬es,sizeof(struct symbol)); + /* obstack_1blank( ¬es, sizeof(struct symbol), &p ); */ + symbolP = (symbolS *) p; + symbolP -> sy_name = preserved_copy_of_name; + symbolP -> sy_type = type; + symbolP -> sy_other = other; + symbolP -> sy_desc = desc; + symbolP -> sy_value = value; + symbolP -> sy_frag = frag; + symbolP -> sy_next = NULL; /* End of chain. */ + symbolP -> sy_forward = NULL; /* JF */ +#ifdef SUSPECT + symbolP -> sy_name_offset = ~ 0; /* Impossible offset catches errors. */ + symbolP -> sy_number = ~ 0; /* Ditto. */ +#endif + /* + * Link to end of symbol chain. + */ + if (symbol_lastP) + { + symbol_lastP -> sy_next = symbolP; + } + else + { + symbol_rootP = symbolP; + } + symbol_lastP = symbolP; + + return (symbolP); +} + +/* + * colon() + * + * We have just seen ":". + * Creates a struct symbol unless it already exists. + * + * Gripes if we are redefining a symbol incompatibly (and ignores it). + * + */ +void +colon (sym_name) /* just seen "x:" - rattle symbols & frags */ + register char * sym_name; /* symbol name, as a cannonical string */ + /* We copy this string: OK to alter later. */ +{ + register struct symbol * symbolP; /* symbol we are working with */ + +#ifdef SUN_ASM_SYNTAX + /* Sun local labes go out of scope whenever a non-local symbol is + defined. */ + + if(*sym_name !='L') + bzero((void *)local_label_defined,sizeof(local_label_defined)); +#endif + +#ifndef WORKING_DOT_WORD + if(new_broken_words) { + struct broken_word *a; + int possible_bytes; + fragS *frag_tmp; + char *frag_opcode; + extern md_short_jump_size; + extern md_long_jump_size; + + possible_bytes=md_short_jump_size+new_broken_words*md_long_jump_size; + frag_tmp=frag_now; + frag_opcode=frag_var(rs_broken_word,possible_bytes,possible_bytes,(relax_substateT)0,(symbolS *)broken_words,(long int)0,(char *)0); + + /* We want to store the pointer to where to insert the jump table in the + fr_opcode of the rs_broken_word frag. This requires a little hackery */ + while(frag_tmp && (frag_tmp->fr_type!=rs_broken_word || frag_tmp->fr_opcode)) + frag_tmp=frag_tmp->fr_next; + know(frag_tmp); + frag_tmp->fr_opcode=frag_opcode; + new_broken_words = 0; + + for(a=broken_words;a && a->dispfrag==0;a=a->next_broken_word) + a->dispfrag=frag_tmp; + } +#endif + if (symbolP = symbol_table_lookup( sym_name )) + { +#ifdef VMS + /* + * If the new symbol is .comm AND it has a size of zero, + * we ignore it (i.e. the old symbol overrides it) + */ + if ((seg_N_TYPE [(int) now_seg] == (N_UNDF | N_EXT)) && + ((obstack_next_free(& frags) - frag_now -> fr_literal) == 0)) + return; + /* + * If the old symbol is .comm and it has a size of zero, + * we override it with the new symbol value. + */ + if ((symbolP -> sy_type == (N_UNDF | N_EXT)) && + (symbolP->sy_value == 0)) { + symbolP -> sy_frag = frag_now; + symbolP -> sy_other = const_flag; + symbolP -> sy_value = obstack_next_free(& frags) - frag_now -> fr_literal; + symbolP -> sy_type |= seg_N_TYPE [(int) now_seg]; /* keep N_EXT bit */ + return; + } +#endif /* VMS */ + /* + * Now check for undefined symbols + */ + if ((symbolP -> sy_type & N_TYPE) == N_UNDF) + { + if( symbolP -> sy_other == 0 + && symbolP -> sy_desc == 0 + && symbolP -> sy_value == 0) + { + symbolP -> sy_frag = frag_now; +#ifdef VMS + symbolP -> sy_other = const_flag; +#endif + symbolP -> sy_value = obstack_next_free(& frags) - frag_now -> fr_literal; + know( N_UNDF == 0 ); + symbolP -> sy_type |= seg_N_TYPE [(int) now_seg]; /* keep N_EXT bit */ + } + else + { +#ifdef VMS + /* + * There are still several cases to check: + * A .comm/.lcomm symbol being redefined as + * initialized data is OK + * A .comm/.lcomm symbol being redefined with + * a larger size is also OK + */ + char New_Type = seg_N_TYPE [(int) now_seg]; + if (((symbolP->sy_type == (N_UNDF | N_EXT)) || + (symbolP->sy_type == N_BSS)) && + (((New_Type & ~N_EXT) == N_DATA) || + (New_Type == symbolP->sy_type))) { + /* + * Select which of the 2 cases this is + */ + if (New_Type == symbolP->sy_type) { + /* + * If the new size is larger we just + * change its value. If the new size + * is smaller, we ignore this symbol + */ + if (symbolP->sy_value < + (obstack_next_free(& frags) - + frag_now -> fr_literal)) { + symbolP -> sy_value = + obstack_next_free(& frags) - + frag_now -> fr_literal; + } + } else { + /* + * It is a .comm/.lcomm being converted + * to initialized data. + */ + symbolP -> sy_frag = frag_now; + symbolP -> sy_other = const_flag; + symbolP -> sy_value = obstack_next_free(& frags) - frag_now -> fr_literal; + symbolP -> sy_type |= seg_N_TYPE [(int) now_seg]; /* keep N_EXT bit */ + } + } else { +#endif /* VMS */ + as_fatal( "Symbol \"%s\" is already defined as \"%s\"/%d.%d.%d.", + sym_name, + seg_name [(int) N_TYPE_seg [symbolP -> sy_type & N_TYPE]], + symbolP -> sy_other, symbolP -> sy_desc, + symbolP -> sy_value); +#ifdef VMS + } +#endif /* VMS */ + } + } + else + { + as_fatal("Symbol %s already defined.",sym_name); + } + } + else + { + symbolP = symbol_new (sym_name, + (unsigned char)(seg_N_TYPE [(int) now_seg]), +#ifdef VMS + const_flag, +#else + 0, +#endif + 0, + (valueT)(obstack_next_free(&frags)-frag_now->fr_literal), + frag_now); + symbol_table_insert (symbolP); + } +} + + +/* + * symbol_table_insert() + * + * Die if we can't insert the symbol. + * + */ + +void +symbol_table_insert (symbolP) + struct symbol * symbolP; +{ + register char * error_string; + + know( symbolP ); + know( symbolP -> sy_name ); + if ( * (error_string = hash_jam (sy_hash, symbolP -> sy_name, (char *)symbolP))) + { + as_fatal( "Inserting \"%s\" into symbol table failed: %s", + symbolP -> sy_name, error_string); + } +} + +/* + * symbol_find_or_make() + * + * If a symbol name does not exist, create it as undefined, and insert + * it into the symbol table. Return a pointer to it. + */ +symbolS * +symbol_find_or_make (name) + char * name; +{ + register symbolS * symbolP; + + symbolP = symbol_table_lookup (name); + if (symbolP == NULL) + { + symbolP = symbol_new (name, N_UNDF, 0, 0, 0, & zero_address_frag); + symbol_table_insert (symbolP); + } + return (symbolP); +} + +/* + * symbol_find() + * + * Implement symbol table lookup. + * In: A symbol's name as a string: '\0' can't be part of a symbol name. + * Out: NULL if the name was not in the symbol table, else the address + * of a struct symbol associated with that name. + */ + +symbolS * +symbol_find (name) + char * name; +{ + return ( (symbolS *) hash_find( sy_hash, name )); +} + + +/* end: symbols.c */ diff --git a/gnu/usr.bin/as/symbols.h b/gnu/usr.bin/as/symbols.h new file mode 100644 index 0000000000..5a52790645 --- /dev/null +++ b/gnu/usr.bin/as/symbols.h @@ -0,0 +1,42 @@ +/* symbols.h - + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +extern struct obstack notes; /* eg FixS live here. */ + +#define symbol_table_lookup(name) ((symbolS *)(symbol_find (name))) + +extern unsigned int local_bss_counter; /* Zeroed before a pass. */ + /* Only used by .lcomm directive. */ + + +extern symbolS * symbol_rootP; /* all the symbol nodes */ +extern symbolS * symbol_lastP; /* last struct symbol we made, or NULL */ + +extern symbolS abs_symbol; + +symbolS * symbol_find(); +void symbol_begin(); +char * local_label_name(); +void local_colon(); +symbolS * symbol_new(); +void colon(); +void symbol_table_insert(); +symbolS * symbol_find_or_make(); + +/* end: symbols.h */ diff --git a/gnu/usr.bin/as/version.c b/gnu/usr.bin/as/version.c new file mode 100644 index 0000000000..f94d3f4721 --- /dev/null +++ b/gnu/usr.bin/as/version.c @@ -0,0 +1,23 @@ +#if defined(__STDC__) || defined(const) +const +#endif +char version_string[] = "GNU assembler version 1.38\n"; + +/* DO NOT PUT COMMENTS ABOUT CHANGES IN THIS FILE. + + This file exists only to define `version_string'. + + Log changes in ChangeLog. The easiest way to do this is with + the Emacs command `add-change-log-entry'. If you don't use Emacs, + add entries of the form: + +Thu Jan 1 00:00:00 1970 Dennis Ritchie (dmr at alice) + + * universe.c (temporal_reality): Began Time. +*/ + +#ifdef VMS +dummy3() +{ +} +#endif diff --git a/gnu/usr.bin/as/write.c b/gnu/usr.bin/as/write.c new file mode 100644 index 0000000000..a850555d81 --- /dev/null +++ b/gnu/usr.bin/as/write.c @@ -0,0 +1,1259 @@ +/* write.c - emit .o file - Copyright(C)1986 Free Software Foundation, Inc. + Copyright (C) 1986,1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + + Umm, with real good luck, this thing should be set up to do byteordering + correctly, but I may have managed to miss a place or two. Treat a.out + very carefully until you're SURE that it works. . . + + In order to cross-assemble the target machine must have an a.out header + similar to the one in a.out.h on THIS machine. Byteorder doesn't matter; + we take special care of it, but the numbers must be the same SIZE (# of + bytes) and in the same PLACE. If this is not true, you will have some + trouble. + */ + +#include "as.h" +#include "md.h" +#include "subsegs.h" +#include "obstack.h" +#include "struc-symbol.h" +#include "write.h" +#include "symbols.h" + +#ifdef SPARC +#include "sparc.h" +#endif +#ifdef I860 +#include "i860.h" +#endif + +void append(); + +#ifdef hpux +#define EXEC_MACHINE_TYPE HP9000S200_ID +#endif + +#ifdef DOT_LABEL_PREFIX +#define LOCAL_LABEL(name) (name[0] =='.' \ + && ( name [1] == 'L' || name [1] == '.' )) +#else /* not defined DOT_LABEL_PREFIX */ +#define LOCAL_LABEL(name) (name [0] == 'L' ) +#endif /* not defined DOT_LABEL_PREFIX */ + +/* + * In: length of relocation (or of address) in chars: 1, 2 or 4. + * Out: GNU LD relocation length code: 0, 1, or 2. + */ + +static unsigned char + +nbytes_r_length [] = { + 42, 0, 1, 42, 2 + }; + + +static struct frag * text_frag_root; +static struct frag * data_frag_root; + +static struct frag * text_last_frag; /* Last frag in segment. */ +static struct frag * data_last_frag; /* Last frag in segment. */ + +static struct exec the_exec; + +static long int string_byte_count; + +static char * the_object_file; + +#if !defined(SPARC) && !defined(I860) +static +#endif +char * next_object_file_charP; /* Tracks object file bytes. */ + +static long int size_of_the_object_file; /* # bytes in object file. */ + +/* static long int length; JF unused */ /* String length, including trailing '\0'. */ + +static void relax_segment(); +void emit_segment(); +static relax_addressT relax_align(); +static long int fixup_segment(); +#if !defined(SPARC) && !defined(I860) +static void emit_relocations(); +#endif + /* + * fix_new() + * + * Create a fixS in obstack 'notes'. + */ +void +#if defined(SPARC) || defined(I860) +fix_new (frag, where, size, add_symbol, sub_symbol, offset, pcrel, r_type) +#else +fix_new (frag, where, size, add_symbol, sub_symbol, offset, pcrel) +#endif + fragS * frag; /* Which frag? */ + int where; /* Where in that frag? */ + short int size; /* 1, 2 or 4 usually. */ + symbolS * add_symbol; /* X_add_symbol. */ + symbolS * sub_symbol; /* X_subtract_symbol. */ + long int offset; /* X_add_number. */ + int pcrel; /* TRUE if PC-relative relocation. */ +#if defined(SPARC) || defined(I860) + int r_type; +#endif +{ + register fixS * fixP; + + fixP = (fixS *)obstack_alloc(¬es,sizeof(fixS)); + + fixP -> fx_frag = frag; + fixP -> fx_where = where; + fixP -> fx_size = size; + fixP -> fx_addsy = add_symbol; + fixP -> fx_subsy = sub_symbol; + fixP -> fx_offset = offset; + fixP -> fx_pcrel = pcrel; + fixP -> fx_next = * seg_fix_rootP; + + /* JF these 'cuz of the NS32K stuff */ + fixP -> fx_im_disp = 0; + fixP -> fx_pcrel_adjust = 0; + fixP -> fx_bsr = 0; + fixP ->fx_bit_fixP = 0; + +#if defined(SPARC) || defined(I860) + fixP->fx_r_type = r_type; +#endif + + * seg_fix_rootP = fixP; +} + +void +write_object_file() +{ + register struct frchain * frchainP; /* Track along all frchains. */ + register fragS * fragP; /* Track along all frags. */ + register struct frchain * next_frchainP; + register fragS * * prev_fragPP; + register char * name; + register symbolS * symbolP; + register symbolS ** symbolPP; + /* register fixS * fixP; JF unused */ + unsigned + text_siz, + data_siz, + syms_siz, + tr_siz, + dr_siz; + void output_file_create(); + void output_file_append(); + void output_file_close(); +#ifdef DONTDEF + void gdb_emit(); + void gdb_end(); +#endif + extern long omagic; /* JF magic # to write out. Is different for + Suns and Vaxen and other boxes */ + +#ifdef VMS + /* + * Under VMS we try to be compatible with VAX-11 "C". Thus, we + * call a routine to check for the definition of the procedure + * "_main", and if so -- fix it up so that it can be program + * entry point. + */ + VMS_Check_For_Main(); +#endif /* VMS */ + /* + * After every sub-segment, we fake an ".align ...". This conforms to BSD4.2 + * brane-damage. We then fake ".fill 0" because that is the kind of frag + * that requires least thought. ".align" frags like to have a following + * frag since that makes calculating their intended length trivial. + */ +#define SUB_SEGMENT_ALIGN (2) + for ( frchainP=frchain_root; frchainP; frchainP=frchainP->frch_next ) + { +#ifdef VMS + /* + * Under VAX/VMS, the linker (and PSECT specifications) + * take care of correctly aligning the segments. + * Doing the alignment here (on initialized data) can + * mess up the calculation of global data PSECT sizes. + */ +#undef SUB_SEGMENT_ALIGN +#define SUB_SEGMENT_ALIGN ((frchainP->frch_seg != SEG_DATA) ? 2 : 0) +#endif /* VMS */ + subseg_new (frchainP -> frch_seg, frchainP -> frch_subseg); + frag_align (SUB_SEGMENT_ALIGN, 0); + /* frag_align will have left a new frag. */ + /* Use this last frag for an empty ".fill". */ + /* + * For this segment ... + * Create a last frag. Do not leave a "being filled in frag". + */ + frag_wane (frag_now); + frag_now -> fr_fix = 0; + know( frag_now -> fr_next == NULL ); + /* know( frags . obstack_c_base == frags . obstack_c_next_free ); */ + /* Above shows we haven't left a half-completed object on obstack. */ + } + + /* + * From now on, we don't care about sub-segments. + * Build one frag chain for each segment. Linked thru fr_next. + * We know that there is at least 1 text frchain & at least 1 data frchain. + */ + prev_fragPP = &text_frag_root; + for ( frchainP=frchain_root; frchainP; frchainP=next_frchainP ) + { + know( frchainP -> frch_root ); + * prev_fragPP = frchainP -> frch_root; + prev_fragPP = & frchainP -> frch_last -> fr_next; + if ( ((next_frchainP = frchainP->frch_next) == NULL) + || next_frchainP == data0_frchainP) + { + prev_fragPP = & data_frag_root; + if ( next_frchainP ) + { + text_last_frag = frchainP -> frch_last; + } + else + { + data_last_frag = frchainP -> frch_last; + } + } + } /* for(each struct frchain) */ + + /* + * We have two segments. If user gave -R flag, then we must put the + * data frags into the text segment. Do this before relaxing so + * we know to take advantage of -R and make shorter addresses. + */ + if ( flagseen [ 'R' ] ) + { + fixS *tmp; + + text_last_frag -> fr_next = data_frag_root; + text_last_frag = data_last_frag; + data_last_frag = NULL; + data_frag_root = NULL; + if(text_fix_root) { + for(tmp=text_fix_root;tmp->fx_next;tmp=tmp->fx_next) + ; + tmp->fx_next=data_fix_root; + } else + text_fix_root=data_fix_root; + data_fix_root=NULL; + } + + relax_segment (text_frag_root, SEG_TEXT); + relax_segment (data_frag_root, SEG_DATA); + /* + * Now the addresses of frags are correct within the segment. + */ + + know( text_last_frag -> fr_type == rs_fill && text_last_frag -> fr_offset == 0 ); + text_siz=text_last_frag->fr_address; +#ifdef SPARC + text_siz= (text_siz+7)&(~7); + text_last_frag->fr_address=text_siz; +#endif + md_number_to_chars((char *)&the_exec.a_text,text_siz, sizeof(the_exec.a_text)); + /* the_exec . a_text = text_last_frag -> fr_address; */ + + /* + * Join the 2 segments into 1 huge segment. + * To do this, re-compute every rn_address in the SEG_DATA frags. + * Then join the data frags after the text frags. + * + * Determine a_data [length of data segment]. + */ + if (data_frag_root) + { + register relax_addressT slide; + + know( text_last_frag -> fr_type == rs_fill && text_last_frag -> fr_offset == 0 ); + data_siz=data_last_frag->fr_address; +#ifdef SPARC + data_siz += (8 - (data_siz % 8)) % 8; + data_last_frag->fr_address = data_siz; +#endif + md_number_to_chars((char *)&the_exec.a_data,data_siz,sizeof(the_exec.a_data)); + /* the_exec . a_data = data_last_frag -> fr_address; */ + slide = text_siz; /* Address in file of the data segment. */ + for (fragP = data_frag_root; + fragP; + fragP = fragP -> fr_next) + { + fragP -> fr_address += slide; + } + know( text_last_frag ); + text_last_frag -> fr_next = data_frag_root; + } + else { + md_number_to_chars((char *)&the_exec.a_data,0,sizeof(the_exec.a_data)); + data_siz = 0; + } + + bss_address_frag . fr_address = text_siz + data_siz; +#ifdef SPARC + local_bss_counter=(local_bss_counter+7)&(~7); +#endif + md_number_to_chars((char *)&the_exec.a_bss,local_bss_counter,sizeof(the_exec.a_bss)); + + + /* + * + * Crawl the symbol chain. + * + * For each symbol whose value depends on a frag, take the address of + * that frag and subsume it into the value of the symbol. + * After this, there is just one way to lookup a symbol value. + * Values are left in their final state for object file emission. + * We adjust the values of 'L' local symbols, even if we do + * not intend to emit them to the object file, because their values + * are needed for fix-ups. + * + * Unless we saw a -L flag, remove all symbols that begin with 'L' + * from the symbol chain. + * + * Count the (length of the nlists of the) (remaining) symbols. + * Assign a symbol number to each symbol. + * Count the number of string-table chars we will emit. + * + */ + know( zero_address_frag . fr_address == 0 ); + string_byte_count = sizeof( string_byte_count ); + + /* JF deal with forward references first. . . */ + for(symbolP=symbol_rootP;symbolP;symbolP=symbolP->sy_next) { + if(symbolP->sy_forward) { + symbolP->sy_value+=symbolP->sy_forward->sy_value+symbolP->sy_forward->sy_frag->fr_address; + symbolP->sy_forward=0; + } + } + symbolPP = & symbol_rootP; /* -> last symbol chain link. */ + { + register long int symbol_number; + + symbol_number = 0; + while (symbolP = * symbolPP) + { + name = symbolP -> sy_name; + if(flagseen['R'] && (symbolP->sy_nlist.n_type&N_DATA)) { + symbolP->sy_nlist.n_type&= ~N_DATA; + symbolP->sy_nlist.n_type|= N_TEXT; + } + /* if(symbolP->sy_forward) { + symbolP->sy_value += symbolP->sy_forward->sy_value + symbolP->sy_forward->sy_frag->fr_address; + } */ + + symbolP -> sy_value += symbolP -> sy_frag -> fr_address; + /* JF the 128 bit is a hack so stabs like + "LET_STMT:23. . ." don't go away */ + /* CPH: 128 bit hack is moby loser. N_SO for file "Lower.c" + fell through the cracks. I think that N_STAB should be + used instead of 128. */ + /* JF the \001 bit is to make sure that local labels + ( 1: - 9: don't make it into the symtable either */ +#ifndef VMS /* Under VMS we need to keep local symbols */ + if ( !name || (symbolP->sy_nlist.n_type&N_STAB) + || (name[2]!='\001' && (flagseen ['L'] || ! LOCAL_LABEL(name) ))) +#endif /* not VMS */ + { + symbolP -> sy_number = symbol_number ++; +#ifndef VMS + if (name) + { /* Ordinary case. */ + symbolP -> sy_name_offset = string_byte_count; + string_byte_count += strlen (symbolP -> sy_name) + 1; + } + else /* .Stabd case. */ +#endif /* not VMS */ + symbolP -> sy_name_offset = 0; + symbolPP = & (symbolP -> sy_next); + } +#ifndef VMS + else + * symbolPP = symbolP -> sy_next; +#endif /* not VMS */ + } /* for each symbol */ + + syms_siz = sizeof( struct nlist) * symbol_number; + md_number_to_chars((char *)&the_exec.a_syms,syms_siz,sizeof(the_exec.a_syms)); + /* the_exec . a_syms = sizeof( struct nlist) * symbol_number; */ + } + + /* + * Addresses of frags now reflect addresses we use in the object file. + * Symbol values are correct. + * Scan the frags, converting any ".org"s and ".align"s to ".fill"s. + * Also converting any machine-dependent frags using md_convert_frag(); + */ + subseg_change( SEG_TEXT, 0); + + for (fragP = text_frag_root; fragP; fragP = fragP -> fr_next) + { + switch (fragP -> fr_type) + { + case rs_align: + case rs_org: + fragP -> fr_type = rs_fill; + know( fragP -> fr_var == 1 ); + know( fragP -> fr_next ); + fragP -> fr_offset + = fragP -> fr_next -> fr_address + - fragP -> fr_address + - fragP -> fr_fix; + break; + + case rs_fill: + break; + + case rs_machine_dependent: + md_convert_frag (fragP); + /* + * After md_convert_frag, we make the frag into a ".space 0". + * Md_convert_frag() should set up any fixSs and constants + * required. + */ + frag_wane (fragP); + break; + +#ifndef WORKING_DOT_WORD + case rs_broken_word: + { + struct broken_word *lie; + extern md_short_jump_size; + extern md_long_jump_size; + + if(fragP->fr_subtype) { + fragP->fr_fix+=md_short_jump_size; + for(lie=(struct broken_word *)(fragP->fr_symbol);lie && lie->dispfrag==fragP;lie=lie->next_broken_word) + if(lie->added==1) + fragP->fr_fix+=md_long_jump_size; + } + frag_wane(fragP); + } + break; +#endif + + default: + BAD_CASE( fragP -> fr_type ); + break; + } /* switch (fr_type) */ + } /* for each frag. */ + +#ifndef WORKING_DOT_WORD + { + struct broken_word *lie; + struct broken_word **prevP; + + prevP= &broken_words; + for(lie=broken_words; lie; lie=lie->next_broken_word) + if(!lie->added) { +#if defined(SPARC) || defined(I860) + fix_new( lie->frag, lie->word_goes_here - lie->frag->fr_literal, + 2, lie->add, + lie->sub, lie->addnum, + 0, NO_RELOC); +#endif +#ifdef NS32K + fix_new_ns32k(lie->frag, + lie->word_goes_here - lie->frag->fr_literal, + 2, + lie->add, + lie->sub, + lie->addnum, + 0, 0, 2, 0, 0); +#endif +#if !defined(SPARC) && !defined(NS32K) && !defined(I860) + fix_new( lie->frag, lie->word_goes_here - lie->frag->fr_literal, + 2, lie->add, + lie->sub, lie->addnum, + 0); +#endif + /* md_number_to_chars(lie->word_goes_here, + lie->add->sy_value + + lie->addnum + - (lie->sub->sy_value), + 2); */ + *prevP=lie->next_broken_word; + } else + prevP= &(lie->next_broken_word); + + for(lie=broken_words;lie;) { + struct broken_word *untruth; + char *table_ptr; + long table_addr; + long from_addr, + to_addr; + int n, + m; + + extern md_short_jump_size; + extern md_long_jump_size; + void md_create_short_jump(); + void md_create_long_jump(); + + + + fragP=lie->dispfrag; + + /* Find out how many broken_words go here */ + n=0; + for(untruth=lie;untruth && untruth->dispfrag==fragP;untruth=untruth->next_broken_word) + if(untruth->added==1) + n++; + + table_ptr=lie->dispfrag->fr_opcode; + table_addr=lie->dispfrag->fr_address+(table_ptr - lie->dispfrag->fr_literal); + /* Create the jump around the long jumps */ + /* This is a short jump from table_ptr+0 to table_ptr+n*long_jump_size */ + from_addr=table_addr; + to_addr = table_addr + md_short_jump_size + n * md_long_jump_size; + md_create_short_jump(table_ptr,from_addr,to_addr,lie->dispfrag,lie->add); + table_ptr+=md_short_jump_size; + table_addr+=md_short_jump_size; + + for(m=0;lie && lie->dispfrag==fragP;m++,lie=lie->next_broken_word) { + if(lie->added==2) + continue; + /* Patch the jump table */ + /* This is the offset from ??? to table_ptr+0 */ + to_addr = table_addr + - (lie->sub->sy_value); + md_number_to_chars(lie->word_goes_here,to_addr,2); + for(untruth=lie->next_broken_word;untruth && untruth->dispfrag==fragP;untruth=untruth->next_broken_word) { + if(untruth->use_jump==lie) + md_number_to_chars(untruth->word_goes_here,to_addr,2); + } + + /* Install the long jump */ + /* this is a long jump from table_ptr+0 to the final target */ + from_addr=table_addr; + to_addr=lie->add->sy_value+lie->addnum; + md_create_long_jump(table_ptr,from_addr,to_addr,lie->dispfrag,lie->add); + table_ptr+=md_long_jump_size; + table_addr+=md_long_jump_size; + } + } + } +#endif +#ifndef VMS + /* + * Scan every FixS performing fixups. We had to wait until now to do + * this because md_convert_frag() may have made some fixSs. + */ + /* the_exec . a_trsize + = sizeof(struct relocation_info) * fixup_segment (text_fix_root, N_TEXT); + the_exec . a_drsize + = sizeof(struct relocation_info) * fixup_segment (data_fix_root, N_DATA); */ + + tr_siz=sizeof(struct relocation_info) * fixup_segment (text_fix_root, N_TEXT); + md_number_to_chars((char *)&the_exec.a_trsize, tr_siz ,sizeof(the_exec.a_trsize)); + dr_siz=sizeof(struct relocation_info) * fixup_segment (data_fix_root, N_DATA); + md_number_to_chars((char *)&the_exec.a_drsize, dr_siz, sizeof(the_exec.a_drsize)); + md_number_to_chars((char *)&the_exec.a_info,omagic,sizeof(the_exec.a_info)); + md_number_to_chars((char *)&the_exec.a_entry,0,sizeof(the_exec.a_entry)); + +#ifdef EXEC_MACHINE_TYPE + md_number_to_chars((char *)&the_exec.a_machtype, EXEC_MACHINE_TYPE, sizeof(the_exec.a_machtype)); +#endif +#ifdef EXEC_VERSION + md_number_to_chars((char *)&the_exec.a_version,EXEC_VERSION,sizeof(the_exec.a_version)); +#endif + + /* the_exec . a_entry = 0; */ + + size_of_the_object_file = + sizeof( the_exec ) + + text_siz + + data_siz + + syms_siz + + tr_siz + + dr_siz + + string_byte_count; + + next_object_file_charP + = the_object_file + = xmalloc ( size_of_the_object_file ); + + output_file_create (out_file_name); + + append (& next_object_file_charP, (char *)(&the_exec), (unsigned long)sizeof(the_exec)); + + /* + * Emit code. + */ + for (fragP = text_frag_root; fragP; fragP = fragP -> fr_next) + { + register long int count; + register char * fill_literal; + register long int fill_size; + + know( fragP -> fr_type == rs_fill ); + append (& next_object_file_charP, fragP -> fr_literal, (unsigned long)fragP -> fr_fix); + fill_literal= fragP -> fr_literal + fragP -> fr_fix; + fill_size = fragP -> fr_var; + know( fragP -> fr_offset >= 0 ); + for (count = fragP -> fr_offset; count; count --) + append (& next_object_file_charP, fill_literal, (unsigned long)fill_size); + } /* for each code frag. */ + + /* + * Emit relocations. + */ + emit_relocations (text_fix_root, (relax_addressT)0); + emit_relocations (data_fix_root, text_last_frag -> fr_address); + /* + * Emit all symbols left in the symbol chain. + * Any symbol still undefined is made N_EXT. + */ + for ( symbolP = symbol_rootP; symbolP; symbolP = symbolP -> sy_next ) + { + register char * temp; + + temp = symbolP -> sy_nlist . n_un . n_name; + /* JF fix the numbers up. Call by value RULES! */ + md_number_to_chars((char *)&(symbolP -> sy_nlist . n_un . n_strx ),symbolP -> sy_name_offset,sizeof(symbolP -> sy_nlist . n_un . n_strx )); + md_number_to_chars((char *)&(symbolP->sy_nlist.n_desc),symbolP->sy_nlist.n_desc,sizeof(symbolP -> sy_nlist . n_desc)); + md_number_to_chars((char *)&(symbolP->sy_nlist.n_value),symbolP->sy_nlist.n_value,sizeof(symbolP->sy_nlist.n_value)); + /* symbolP -> sy_nlist . n_un . n_strx = symbolP -> sy_name_offset; JF replaced by md above */ + if (symbolP -> sy_type == N_UNDF) + symbolP -> sy_type |= N_EXT; /* Any undefined symbols become N_EXT. */ + append (& next_object_file_charP, (char *)(& symbolP -> sy_nlist), + (unsigned long)sizeof(struct nlist)); + symbolP -> sy_nlist . n_un . n_name = temp; + } /* for each symbol */ + + /* + * next_object_file_charP -> slot for next object byte. + * Emit strings. + * Find strings by crawling along symbol table chain. + */ +/* Gotta do md_ byte-ordering stuff for string_byte_count first - KWK */ + md_number_to_chars((char *)&string_byte_count, string_byte_count, sizeof(string_byte_count)); + + append (& next_object_file_charP, (char *)&string_byte_count, (unsigned long)sizeof(string_byte_count)); + for ( symbolP = symbol_rootP; symbolP; symbolP = symbolP -> sy_next ) + { + if (symbolP -> sy_name) + { /* Ordinary case: not .stabd. */ + append (& next_object_file_charP, symbolP -> sy_name, + (unsigned long)(strlen (symbolP -> sy_name) + 1)); + } + } /* for each symbol */ + + know( next_object_file_charP == the_object_file + size_of_the_object_file ); + + output_file_append (the_object_file, size_of_the_object_file, out_file_name); + +#ifdef DONTDEF + if (flagseen['G']) /* GDB symbol file to be appended? */ + { + gdb_emit (out_file_name); + gdb_end (); + } +#endif + + output_file_close (out_file_name); +#else /* VMS */ + /* + * Now do the VMS-dependent part of writing the object file + */ + VMS_write_object_file(text_siz, data_siz, text_frag_root, data_frag_root); +#endif /* VMS */ +} /* write_object_file() */ + +/* + * relax_segment() + * + * Now we have a segment, not a crowd of sub-segments, we can make fr_address + * values. + * + * Relax the frags. + * + * After this, all frags in this segment have addresses that are correct + * within the segment. Since segments live in different file addresses, + * these frag addresses may not be the same as final object-file addresses. + */ +#ifndef VMS +static +#endif /* not VMS */ +void +relax_segment (segment_frag_root, segment_type) + struct frag * segment_frag_root; + segT segment_type; /* N_DATA or N_TEXT */ +{ + register struct frag * fragP; + register relax_addressT address; + /* register relax_addressT old_address; JF unused */ + /* register relax_addressT new_address; JF unused */ + + know( segment_type == SEG_DATA || segment_type == SEG_TEXT ); + + /* In case md_estimate_size_before_relax() wants to make fixSs. */ + subseg_change (segment_type, 0); + + /* + * For each frag in segment: count and store (a 1st guess of) fr_address. + */ + address = 0; + for ( fragP = segment_frag_root; fragP; fragP = fragP -> fr_next ) + { + fragP -> fr_address = address; + address += fragP -> fr_fix; + switch (fragP -> fr_type) + { + case rs_fill: + address += fragP -> fr_offset * fragP -> fr_var; + break; + + case rs_align: + address += relax_align (address, fragP -> fr_offset); + break; + + case rs_org: + /* + * Assume .org is nugatory. It will grow with 1st relax. + */ + break; + + case rs_machine_dependent: + address += md_estimate_size_before_relax + (fragP, seg_N_TYPE [(int) segment_type]); + break; + +#ifndef WORKING_DOT_WORD + /* Broken words don't concern us yet */ + case rs_broken_word: + break; +#endif + + default: + BAD_CASE( fragP -> fr_type ); + break; + } /* switch(fr_type) */ + } /* for each frag in the segment */ + + /* + * Do relax(). + */ + { + register long int stretch; /* May be any size, 0 or negative. */ + /* Cumulative number of addresses we have */ + /* relaxed this pass. */ + /* We may have relaxed more than one address. */ + register long int stretched; /* Have we stretched on this pass? */ + /* This is 'cuz stretch may be zero, when, + in fact some piece of code grew, and + another shrank. If a branch instruction + doesn't fit anymore, we could be scrod */ + + do + { + stretch = stretched = 0; + for (fragP = segment_frag_root; fragP; fragP = fragP -> fr_next) + { + register long int growth; + register long int was_address; + /* register long int var; */ + register long int offset; + register symbolS * symbolP; + register long int target; + register long int after; + register long int aim; + + was_address = fragP -> fr_address; + address = fragP -> fr_address += stretch; + symbolP = fragP -> fr_symbol; + offset = fragP -> fr_offset; + /* var = fragP -> fr_var; */ + switch (fragP -> fr_type) + { + case rs_fill: /* .fill never relaxes. */ + growth = 0; + break; + +#ifndef WORKING_DOT_WORD + /* JF: This is RMS's idea. I do *NOT* want to be blamed + for it I do not want to write it. I do not want to have + anything to do with it. This is not the proper way to + implement this misfeature. */ + case rs_broken_word: + { + struct broken_word *lie; + struct broken_word *untruth; + extern int md_short_jump_size; + extern int md_long_jump_size; + + /* Yes this is ugly (storing the broken_word pointer + in the symbol slot). Still, this whole chunk of + code is ugly, and I don't feel like doing anything + about it. Think of it as stubbornness in action */ + growth=0; + for(lie=(struct broken_word *)(fragP->fr_symbol); + lie && lie->dispfrag==fragP; + lie=lie->next_broken_word) { + + if(lie->added) + continue; + offset= lie->add->sy_frag->fr_address+lie->add->sy_value + lie->addnum - + (lie->sub->sy_frag->fr_address+lie->sub->sy_value); + if(offset<=-32768 || offset>=32767) { + if(flagseen['k']) + as_warn(".word %s-%s+%ld didn't fit",lie->add->sy_name,lie->sub->sy_name,lie->addnum); + lie->added=1; + if(fragP->fr_subtype==0) { + fragP->fr_subtype++; + growth+=md_short_jump_size; + } + for(untruth=lie->next_broken_word;untruth && untruth->dispfrag==lie->dispfrag;untruth=untruth->next_broken_word) + if(untruth->add->sy_frag==lie->add->sy_frag && untruth->add->sy_value==lie->add->sy_value) { + untruth->added=2; + untruth->use_jump=lie; + } + growth+=md_long_jump_size; + } + } + } + break; +#endif + case rs_align: + growth = relax_align ((relax_addressT)(address + fragP -> fr_fix), offset) + - relax_align ((relax_addressT)(was_address + fragP -> fr_fix), offset); + break; + + case rs_org: + target = offset; + if (symbolP) + { + know( ((symbolP -> sy_type & N_TYPE) == N_ABS) || ((symbolP -> sy_type & N_TYPE) == N_DATA) || ((symbolP -> sy_type & N_TYPE) == N_TEXT)); + know( symbolP -> sy_frag ); + know( (symbolP->sy_type&N_TYPE)!=N_ABS || symbolP->sy_frag==&zero_address_frag ); + target += + symbolP -> sy_value + + symbolP -> sy_frag -> fr_address; + } + know( fragP -> fr_next ); + after = fragP -> fr_next -> fr_address; + growth = ((target - after ) > 0) ? (target - after) : 0; + /* Growth may be -ve, but variable part */ + /* of frag cannot have < 0 chars. */ + /* That is, we can't .org backwards. */ + + growth -= stretch; /* This is an absolute growth factor */ + break; + + case rs_machine_dependent: + { + register const relax_typeS * this_type; + register const relax_typeS * start_type; + register relax_substateT next_state; + register relax_substateT this_state; + + start_type = this_type + = md_relax_table + (this_state = fragP -> fr_subtype); + target = offset; + if (symbolP) + { + know( ((symbolP -> sy_type & N_TYPE) == N_ABS) || ((symbolP -> sy_type & + N_TYPE) == N_DATA) || ((symbolP -> sy_type & N_TYPE) == N_TEXT)); + know( symbolP -> sy_frag ); + know( (symbolP->sy_type&N_TYPE)!=N_ABS || symbolP->sy_frag==&zero_address_frag ); + target += + symbolP -> sy_value + + symbolP -> sy_frag -> fr_address; + + /* If frag has yet to be reached on this pass, + assume it will move by STRETCH just as we did. + If this is not so, it will be because some frag + between grows, and that will force another pass. */ + + /* JF was just address */ + /* JF also added is_dnrange hack */ + /* There's gotta be a better/faster/etc way + to do this. . . */ + /* gnu@cygnus.com: I changed this from > to >= + because I ran into a zero-length frag (fr_fix=0) + which was created when the obstack needed a new + chunk JUST AFTER the opcode of a branch. Since + fr_fix is zero, fr_address of this frag is the same + as fr_address of the next frag. This + zero-length frag was variable and jumped to .+2 + (in the next frag), but since the > comparison + below failed (the two were =, not >), "stretch" + was not added to the target. Stretch was 178, so + the offset appeared to be .-176 instead, which did + not fit into a byte branch, so the assembler + relaxed the branch to a word. This didn't compare + with what happened when the same source file was + assembled on other machines, which is how I found it. + You might want to think about what other places have + trouble with zero length frags... */ + + if (symbolP->sy_frag->fr_address >= was_address && is_dnrange(fragP,symbolP->sy_frag)) + target += stretch; + + } + aim = target - address - fragP -> fr_fix; + /* The displacement is affected by the instruction size + * for the 32k architecture. I think we ought to be able + * to add fragP->fr_pcrel_adjust in all cases (it should be + * zero if not used), but just in case it breaks something + * else we'll put this inside #ifdef NS32K ... #endif + */ +#ifdef NS32K + aim += fragP->fr_pcrel_adjust; +#endif + + if (aim < 0) + { + /* Look backwards. */ + for (next_state = this_type -> rlx_more; next_state; ) + { + if (aim >= this_type -> rlx_backward) + next_state = 0; + else + { /* Grow to next state. */ + this_type = md_relax_table + (this_state = next_state); + next_state = this_type -> rlx_more; + } + } + } + else + { +#ifdef DONTDEF +/* JF these next few lines of code are for the mc68020 which can't handle short + offsets of zero in branch instructions. What a kludge! */ + if(aim==0 && this_state==(1<<2+0)) { /* FOO hard encoded from m.c */ + aim=this_type->rlx_forward+1; /* Force relaxation into word mode */ + } +#endif +/* JF end of 68020 code */ + /* Look forwards. */ + for (next_state = this_type -> rlx_more; next_state; ) + { + if (aim <= this_type -> rlx_forward) + next_state = 0; + else + { /* Grow to next state. */ + this_type = md_relax_table + (this_state = next_state); + next_state = this_type -> rlx_more; + } + } + } + if (growth = this_type -> rlx_length - start_type -> rlx_length) + fragP -> fr_subtype = this_state; + } + break; + + default: + BAD_CASE( fragP -> fr_type ); + break; + } + if(growth) { + stretch += growth; + stretched++; + } + } /* For each frag in the segment. */ + } while (stretched); /* Until nothing further to relax. */ + } + + /* + * We now have valid fr_address'es for each frag. + */ + + /* + * All fr_address's are correct, relative to their own segment. + * We have made all the fixS we will ever make. + */ +} /* relax_segment() */ + +/* + * Relax_align. Advance location counter to next address that has 'alignment' + * lowest order bits all 0s. + */ + +static relax_addressT /* How many addresses does the .align take? */ +relax_align (address, alignment) + register relax_addressT address; /* Address now. */ + register long int alignment; /* Alignment (binary). */ +{ + relax_addressT mask; + relax_addressT new_address; + + mask = ~ ( (~0) << alignment ); + new_address = (address + mask) & (~ mask); + return (new_address - address); +} + +/* + * fixup_segment() + */ +static long int +fixup_segment (fixP, this_segment_type) + register fixS * fixP; + int this_segment_type; /* N_TYPE bits for segment. */ +{ + register long int seg_reloc_count; + /* JF these all used to be local to the for loop, but GDB doesn't seem to be able to deal with them there, so I moved them here for a bit. */ + register symbolS * add_symbolP; + register symbolS * sub_symbolP; + register long int add_number; + register int size; + register char * place; + register long int where; + register char pcrel; + register fragS * fragP; + register int add_symbol_N_TYPE; + + + seg_reloc_count = 0; + for ( ; fixP; fixP = fixP -> fx_next) + { + fragP = fixP -> fx_frag; + know( fragP ); + where = fixP -> fx_where; + place = fragP -> fr_literal + where; + size = fixP -> fx_size; + add_symbolP = fixP -> fx_addsy; + sub_symbolP = fixP -> fx_subsy; + add_number = fixP -> fx_offset; + pcrel = fixP -> fx_pcrel; + if(add_symbolP) + add_symbol_N_TYPE = add_symbolP -> sy_type & N_TYPE; + if (sub_symbolP) + { + if(!add_symbolP) /* Its just -sym */ + { + if(sub_symbolP->sy_type!=N_ABS) + as_warn("Negative of non-absolute symbol %s", sub_symbolP->sy_name); + add_number-=sub_symbolP->sy_value; + } + else if ( ((sub_symbolP -> sy_type ^ add_symbol_N_TYPE) & N_TYPE) == 0 + && ( add_symbol_N_TYPE == N_DATA + || add_symbol_N_TYPE == N_TEXT + || add_symbol_N_TYPE == N_BSS + || add_symbol_N_TYPE == N_ABS)) + { + /* Difference of 2 symbols from same segment. */ + /* Can't make difference of 2 undefineds: 'value' means */ + /* something different for N_UNDF. */ + add_number += add_symbolP -> sy_value - sub_symbolP -> sy_value; + add_symbolP = NULL; + fixP -> fx_addsy = NULL; + } + else + { + /* Different segments in subtraction. */ + know( sub_symbolP -> sy_type != (N_ABS | N_EXT)) + if (sub_symbolP -> sy_type == N_ABS) + add_number -= sub_symbolP -> sy_value; + else + { + as_warn("Can't emit reloc {- %s-seg symbol \"%s\"} @ file address %d.", + seg_name[(int)N_TYPE_seg[sub_symbolP->sy_type&N_TYPE]], + sub_symbolP -> sy_name, fragP -> fr_address + where); + } + } + } + if (add_symbolP) + { + if (add_symbol_N_TYPE == this_segment_type && pcrel) + { + /* + * This fixup was made when the symbol's segment was + * SEG_UNKNOWN, but it is now in the local segment. + * So we know how to do the address without relocation. + */ + add_number += add_symbolP -> sy_value; + add_number -= +#ifndef NS32K + size + +#endif + where + fragP -> fr_address; +#if defined(NS32K) && defined(SEQUENT_COMPATABILITY) + if (fragP->fr_bsr) + add_number -= 0x12; /* FOO Kludge alert! */ +#endif + /* Kenny thinks this needs * + /* add_number +=size-2; */ + pcrel = 0; /* Lie. Don't want further pcrel processing. */ + fixP -> fx_addsy = NULL; /* No relocations please. */ + /* + * It would be nice to check that the address does not overflow. + * I didn't do this check because: + * + It is machine dependent in the general case (eg 32032) + * + Compiler output will never need this checking, so why + * slow down the usual case? + */ + } + else + { + switch (add_symbol_N_TYPE) + { + case N_ABS: + add_number += add_symbolP -> sy_value; + fixP -> fx_addsy = NULL; + add_symbolP = NULL; + break; + + case N_BSS: + case N_DATA: + case N_TEXT: + seg_reloc_count ++; + add_number += add_symbolP -> sy_value; + break; + + case N_UNDF: + seg_reloc_count ++; + break; + + default: + BAD_CASE( add_symbol_N_TYPE ); + break; + } /* switch on symbol seg */ + } /* if not in local seg */ + } /* if there was a + symbol */ + if (pcrel) + { + add_number -= +#ifndef NS32K + size + +#endif + where + fragP -> fr_address; + if (add_symbolP == 0) + { + fixP -> fx_addsy = & abs_symbol; + seg_reloc_count ++; + } + } + /* OVE added fx_im_disp for ns32k and others */ + if (!fixP->fx_bit_fixP) { + /* JF I hope this works . . . */ + if((size==1 && (add_number& ~0xFF) && (add_number&~0xFF!=(-1&~0xFF))) || + (size==2 && (add_number& ~0xFFFF) && (add_number&~0xFFFF!=(-1&~0xFFFF)))) + as_warn("Fixup of %d too large for field width of %d",add_number, size); + + switch (fixP->fx_im_disp) { + case 0: +#if defined(SPARC) || defined(I860) + fixP->fx_addnumber = add_number; + md_number_to_imm(place, add_number, size, fixP, this_segment_type); +#else + md_number_to_imm (place, add_number, size); + /* OVE: the immediates, like disps, have lsb at lowest address */ +#endif + break; + case 1: + md_number_to_disp (place, + fixP->fx_pcrel ? add_number+fixP->fx_pcrel_adjust:add_number, + size); + break; + case 2: /* fix requested for .long .word etc */ + md_number_to_chars (place, add_number, size); + break; + default: + as_fatal("Internal error in write.c in fixup_segment"); + } /* OVE: maybe one ought to put _imm _disp _chars in one md-func */ + } else { + md_number_to_field (place, add_number, fixP->fx_bit_fixP); + } + } /* For each fixS in this segment. */ + return (seg_reloc_count); +} /* fixup_segment() */ + + +/* The sparc needs its own emit_relocations() */ +#if !defined(SPARC) && !defined(I860) +/* + * emit_relocations() + * + * Crawl along a fixS chain. Emit the segment's relocations. + */ +static void +emit_relocations (fixP, segment_address_in_file) + register fixS * fixP; /* Fixup chain for this segment. */ + relax_addressT segment_address_in_file; +{ + struct relocation_info ri; + register symbolS * symbolP; + + /* JF this is for paranoia */ + bzero((char *)&ri,sizeof(ri)); + for ( ; fixP; fixP = fixP -> fx_next) + { + if (symbolP = fixP -> fx_addsy) + { + +#ifdef NS32K + /* These two 'cuz of NS32K */ + ri . r_bsr = fixP -> fx_bsr; + ri . r_disp = fixP -> fx_im_disp; +#endif + + ri . r_length = nbytes_r_length [fixP -> fx_size]; + ri . r_pcrel = fixP -> fx_pcrel; + ri . r_address = fixP -> fx_frag -> fr_address + + fixP -> fx_where + - segment_address_in_file; + if ((symbolP -> sy_type & N_TYPE) == N_UNDF) + { + ri . r_extern = 1; + ri . r_symbolnum = symbolP -> sy_number; + } + else + { + ri . r_extern = 0; + ri . r_symbolnum = symbolP -> sy_type & N_TYPE; + } + + /* + The 68k machines assign bit-fields from higher bits to + lower bits ("left-to-right") within the int. VAXen assign + bit-fields from lower bits to higher bits ("right-to-left"). + Both handle multi-byte numbers in their usual fashion + (Big-endian and little-endian stuff). + Thus we need a machine dependent routine to make + sure the structure is written out correctly. FUN! + */ + md_ri_to_chars((char *) &ri, ri); + append (&next_object_file_charP, (char *)& ri, (unsigned long)sizeof(ri)); + } + } + +} +#endif + +int +is_dnrange(f1,f2) +struct frag *f1,*f2; +{ + while(f1) { + if(f1->fr_next==f2) + return 1; + f1=f1->fr_next; + } + return 0; +} +/* End: as-write.c */ diff --git a/gnu/usr.bin/as/write.h b/gnu/usr.bin/as/write.h new file mode 100644 index 0000000000..73276908af --- /dev/null +++ b/gnu/usr.bin/as/write.h @@ -0,0 +1,77 @@ +/* write.h -> write.c + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* The bit_fix was implemented to support machines that need variables + to be inserted in bitfields other than 1, 2 and 4 bytes. + Furthermore it gives us a possibillity to mask in bits in the symbol + when it's fixed in the objectcode and check the symbols limits. + + The or-mask is used to set the huffman bits in displacements for the + ns32k port. + The acbi, addqi, movqi, cmpqi instruction requires an assembler that + can handle bitfields. Ie handle an expression, evaluate it and insert + the result in an some bitfield. ( ex: 5 bits in a short field of a opcode) + */ + + +struct bit_fix { + int fx_bit_size; /* Length of bitfield */ + int fx_bit_offset; /* Bit offset to bitfield */ + long fx_bit_base; /* Where do we apply the bitfix. + If this is zero, default is assumed. */ + long fx_bit_base_adj;/* Adjustment of base */ + long fx_bit_max; /* Signextended max for bitfield */ + long fx_bit_min; /* Signextended min for bitfield */ + long fx_bit_add; /* Or mask, used for huffman prefix */ +}; +typedef struct bit_fix bit_fixS; +/* + * FixSs may be built up in any order. + */ + +struct fix +{ + fragS * fx_frag; /* Which frag? */ + long int fx_where; /* Where is the 1st byte to fix up? */ + symbolS * fx_addsy; /* NULL or Symbol whose value we add in. */ + symbolS * fx_subsy; /* NULL or Symbol whose value we subtract. */ + long int fx_offset; /* Absolute number we add in. */ + struct fix * fx_next; /* NULL or -> next fixS. */ + short int fx_size; /* How many bytes are involved? */ + char fx_pcrel; /* TRUE: pc-relative. */ + char fx_pcrel_adjust;/* pc-relative offset adjust */ + char fx_im_disp; /* TRUE: value is a displacement */ + bit_fixS * fx_bit_fixP; /* IF NULL no bitfix's to do */ + char fx_bsr; /* sequent-hack */ +#if defined(SPARC) || defined(I860) + char fx_r_type; /* Sparc hacks */ + long fx_addnumber; +#endif +}; + +typedef struct fix fixS; + + +COMMON fixS * text_fix_root; /* Chains fixSs. */ +COMMON fixS * data_fix_root; /* Chains fixSs. */ +COMMON fixS ** seg_fix_rootP; /* -> one of above. */ + +bit_fixS *bit_fix_new(); +/* end: write.h */ + diff --git a/gnu/usr.bin/as/xmalloc.c b/gnu/usr.bin/as/xmalloc.c new file mode 100644 index 0000000000..78c8c7f8c4 --- /dev/null +++ b/gnu/usr.bin/as/xmalloc.c @@ -0,0 +1,60 @@ +/* xmalloc.c - get memory or bust + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* +NAME + xmalloc() - get memory or bust +INDEX + xmalloc() uses malloc() + +SYNOPSIS + char * my_memory; + + my_memory = xmalloc(42); / * my_memory gets address of 42 chars * / + +DESCRIPTION + + Use xmalloc() as an "error-free" malloc(). It does almost the same job. + When it cannot honour your request for memory it BOMBS your program + with a "virtual memory exceeded" message. Malloc() returns NULL and + does not bomb your program. + +SEE ALSO + malloc() + +*/ +#ifdef USG +#include +#endif + +char * xmalloc(n) + long n; +{ + char * retval; + char * malloc(); + void error(); + + if ( ! (retval = malloc ((unsigned)n)) ) + { + error("virtual memory exceeded"); + } + return (retval); +} + +/* end: xmalloc.c */ diff --git a/gnu/usr.bin/as/xrealloc.c b/gnu/usr.bin/as/xrealloc.c new file mode 100644 index 0000000000..a5010bc9b0 --- /dev/null +++ b/gnu/usr.bin/as/xrealloc.c @@ -0,0 +1,61 @@ +/* xrealloc.c -new memory or bust- + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* + +NAME + xrealloc () - get more memory or bust +INDEX + xrealloc () uses realloc () +SYNOPSIS + char *my_memory; + + my_memory = xrealloc (my_memory, 42); + / * my_memory gets (perhaps new) address of 42 chars * / + +DESCRIPTION + + Use xrealloc () as an "error-free" realloc ().It does almost the same + job. When it cannot honour your request for memory it BOMBS your + program with a "virtual memory exceeded" message. Realloc() returns + NULL and does not bomb your program. + +SEE ALSO + realloc () +*/ + +#ifdef USG +#include +#endif + +char * +xrealloc (ptr, n) +register char *ptr; +long n; +{ + char *realloc (); + void error(); + + if ((ptr = realloc (ptr, (unsigned)n)) == 0) + error ("virtual memory exceeded"); + return (ptr); +} + +/* end: xrealloc.c */ diff --git a/gnu/usr.bin/awk/Makefile b/gnu/usr.bin/awk/Makefile index fdca82c448..3ef9894d17 100644 --- a/gnu/usr.bin/awk/Makefile +++ b/gnu/usr.bin/awk/Makefile @@ -7,7 +7,7 @@ LDADD= -lm DPADD= ${LIBM} CLEANFILES+= awk.c y.tab.h -MAN1= awk.0 +MAN1= awk.1 .include .include "../../usr.bin/Makefile.inc" diff --git a/gnu/usr.bin/bc/Makefile b/gnu/usr.bin/bc/Makefile index 2db220aa6e..fac7437c0b 100644 --- a/gnu/usr.bin/bc/Makefile +++ b/gnu/usr.bin/bc/Makefile @@ -1,7 +1,6 @@ PROG= bc SRCS= bc.c global.c scan.c util.c main.c number.c storage.c load.c execute.c -MAN1= bc.0 -BINDIR= +MAN1= bc.1 CFLAGS+= -D_POSIX_SOURCE -I$(.CURDIR) .include diff --git a/gnu/usr.bin/bc/bc.1 b/gnu/usr.bin/bc/bc.1 index 0dfff0c0f7..b387f92d70 100644 --- a/gnu/usr.bin/bc/bc.1 +++ b/gnu/usr.bin/bc/bc.1 @@ -428,7 +428,7 @@ parameter numbers and types are checked when a function is called. Any mismatch in number or types of parameters will cause a runtime error. A runtime error will also occur for the call to an undefined function. .PP -The \fIauto_list\f is an optional list of variables that are for +The \fIauto_list\fR is an optional list of variables that are for "local" use. The syntax of the auto list (if present) is "\fBauto \fIname\fR, ... ;". (The semicolon is optional.) Each \fIname\fR is the name of an auto variable. Arrays may be specified by using the diff --git a/gnu/usr.bin/cc/Makefile b/gnu/usr.bin/cc/Makefile new file mode 100644 index 0000000000..c93ffa66fb --- /dev/null +++ b/gnu/usr.bin/cc/Makefile @@ -0,0 +1,2 @@ +SUBDIR= cc cpp lib cc1 libgcc cc1plus cc1obj #libobjc +.include diff --git a/gnu/usr.bin/cc/Makefile.inc b/gnu/usr.bin/cc/Makefile.inc deleted file mode 100644 index b5875c2f2e..0000000000 --- a/gnu/usr.bin/cc/Makefile.inc +++ /dev/null @@ -1,12 +0,0 @@ -COMMON_SRC= toplev.c version.c tree.c print-tree.c \ - stor-layout.c fold-const.c function.c stmt.c expr.c \ - calls.c expmed.c explow.c optabs.c varasm.c \ - rtl.c print-rtl.c rtlanal.c emit-rtl.c real.c \ - dbxout.c sdbout.c dwarfout.c xcoffout.c\ - integrate.c jump.c cse.c loop.c unroll.c flow.c \ - stupid.c combine.c regclass.c local-alloc.c \ - global.c reload.c reload1.c caller-save.c \ - insn-peep.c reorg.c sched.c final.c recog.c reg-stack.c \ - insn-opinit.c insn-recog.c insn-extract.c insn-output.c insn-emit.c \ - insn-attrtab.c aux-output.c getpwd.c convert.c \ - c-common.c obstack.c diff --git a/gnu/usr.bin/cc/TODO b/gnu/usr.bin/cc/TODO new file mode 100644 index 0000000000..66783533a8 --- /dev/null +++ b/gnu/usr.bin/cc/TODO @@ -0,0 +1,3 @@ +Fix obj library -- doesn't compile with b-make +Move i386 sources to arch dependant directory +Fix up make to build insn files from config files. diff --git a/gnu/usr.bin/cc/cc/Makefile b/gnu/usr.bin/cc/cc/Makefile index 66edadfb86..d5c31592a1 100644 --- a/gnu/usr.bin/cc/cc/Makefile +++ b/gnu/usr.bin/cc/cc/Makefile @@ -2,14 +2,15 @@ PROG= gcc SRCS= gcc.c version.c obstack.c -MAN1= gcc.0 g++.0 +MAN1= gcc.1 g++.1 BINDIR= /usr/bin -CFLAGS+= -I$(.CURDIR) -I$(.CURDIR)/../common -.PATH: $(.CURDIR)/../common - +CFLAGS+= -I$(.CURDIR) -I$(.CURDIR)/../lib +.PATH: $(.CURDIR)/../lib afterinstall: - -rm -f $(DESTDIR)$(BINDIR)/gcc - cd $(DESTDIR)$(BINDIR); ln -s cc gcc + install -c -o $(BINOWN) -g $(BINGRP) -m $(BINMODE) \ + $(.CURDIR)/g++.script $(DESTDIR)/usr/bin/g++ + rm -f $(DESTDIR)$(BINDIR)/cc + cd $(DESTDIR)$(BINDIR); ln -s gcc cc .include diff --git a/gnu/usr.bin/cc/cc/g++.script b/gnu/usr.bin/cc/cc/g++.script new file mode 100644 index 0000000000..d633e1c459 --- /dev/null +++ b/gnu/usr.bin/cc/cc/g++.script @@ -0,0 +1,111 @@ +#!/bin/sh +# Compile programs, treating .c files as C++. +: || exec /bin/sh -f $0 $argv:q + +# The compiler name might be different when doing cross-compilation +# (this should be configured) +gcc_name=gcc +speclang=-xnone + +# replace the command name by the name of the new command +progname=`basename $0` +case "$0" in + */*) + gcc=`echo $0 | sed -e "s;/[^/]*$;;"`/$gcc_name + ;; + *) + gcc=$gcc_name + ;; +esac + +# $first is yes for first arg, no afterwards. +first=yes +# If next arg is the argument of an option, $quote is non-empty. +# More precisely, it is the option that wants an argument. +quote= +# $library is made empty to disable use of libg++. +library=-lg++ +numargs=$# + +# ash requires the newline before `do'. +for arg +do + if [ $first = yes ] + then + # Need some 1st arg to `set' which does not begin with `-'. + # We get rid of it after the loop ends. + set gcc + first=no + fi + # If you have to ask what this does, you should not edit this file. :-) + # The ``S'' at the start is so that echo -nostdinc does not eat the + # -nostdinc. + arg=`echo "S$arg" | sed "s/^S//; s/'/'\\\\\\\\''/g"` + if [ x$quote != x ] + then + quote= + else + quote= + case $arg in + -nostdlib) + # Inhibit linking with -lg++. + library= + ;; + -lm | -lmath) + # Because libg++ uses things from the math library, make sure it + # always comes before the math library. We recognize both -lm + # and -lmath, since on some systems (e.g. m88k SVR3), it + # doesn't call it libm.a for some reason. + set "$@" $library + library="" + ;; + -[bBVDUoeTuIYmLiA] | -Tdata) + # these switches take following word as argument, + # so don't treat it as a file name. + quote=$arg + ;; + -[cSEM] | -MM) + # Don't specify libraries if we won't link, + # since that would cause a warning. + library= + ;; + -x*) + speclang=$arg + ;; + -v) + # catch `g++ -v' + if [ $numargs = 1 ] ; then library="" ; fi + ;; + -*) + # Pass other options through; they don't need -x and aren't inputs. + ;; + *) + # If file ends in .c or .i, put options around it. + # But not if a specified -x option is currently active. + case "$speclang $arg" in -xnone\ *.[ci]) + set "$@" -xc++ "'$arg'" -xnone + continue + esac + ;; + esac + fi + set "$@" "'$arg'" +done + +# Get rid of that initial 1st arg +if [ $first = no ]; then + shift +else + echo "$0: No input files specified." + exit 1 +fi + +if [ x$quote != x ] +then + echo "$0: argument to \`$quote' missing" + exit 1 +fi + +eval $gcc "$@" $library + + diff --git a/gnu/usr.bin/cc/cc/gcc.c b/gnu/usr.bin/cc/cc/gcc.c index 27aa2a2fce..5470d9e7f7 100644 --- a/gnu/usr.bin/cc/cc/gcc.c +++ b/gnu/usr.bin/cc/cc/gcc.c @@ -437,7 +437,7 @@ static struct compiler default_compilers[] = { {".c", "@c"}, {"@c", - "cpp -lang-c %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\ + "gcpp -lang-c %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\ %{C:%{!E:%eGNU C does not support -C without using -E}}\ %{M} %{MM} %{MD:-MD %b.d} %{MMD:-MMD %b.d}\ -undef -D__GNUC__=2 %{ansi:-trigraphs -$ -D__STRICT_ANSI__}\ @@ -446,7 +446,7 @@ static struct compiler default_compilers[] = %{traditional-cpp:-traditional}\ %{g*} %{W*} %{w} %{pedantic*} %{H} %{d*} %C %{D*} %{U*} %{i*}\ %i %{!M:%{!MM:%{!E:%{!pipe:%g.i}}}}%{E:%W{o*}}%{M:%W{o*}}%{MM:%W{o*}} |\n", - "%{!M:%{!MM:%{!E:cc1 %{!pipe:%g.i} %1 \ + "%{!M:%{!MM:%{!E:gcc1 %{!pipe:%g.i} %1 \ %{!Q:-quiet} -dumpbase %b.c %{d*} %{m*} %{a}\ %{g*} %{O*} %{W*} %{w} %{pedantic*} %{ansi} \ %{traditional} %{v:-version} %{pg:-p} %{p} %{f*}\ @@ -457,7 +457,7 @@ static struct compiler default_compilers[] = %{c:%W{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%u.o}\ %{!pipe:%g.s} %A\n }}}}"}, {"-", - "%{E:cpp -lang-c %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\ + "%{E:gcpp -lang-c %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\ %{C:%{!E:%eGNU C does not support -C without using -E}}\ %{M} %{MM} %{MD:-MD %b.d} %{MMD:-MMD %b.d}\ -undef -D__GNUC__=2 %{ansi:-trigraphs -$ -D__STRICT_ANSI__}\ @@ -469,7 +469,7 @@ static struct compiler default_compilers[] = %{!E:%e-E required when input is from standard input}"}, {".m", "@objective-c"}, {"@objective-c", - "cpp -lang-objc %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\ + "gcpp -lang-objc %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\ %{C:%{!E:%eGNU C does not support -C without using -E}}\ %{M} %{MM} %{MD:-MD %b.d} %{MMD:-MMD %b.d}\ -undef -D__OBJC__ -D__GNUC__=2 %{ansi:-trigraphs -$ -D__STRICT_ANSI__}\ @@ -478,7 +478,7 @@ static struct compiler default_compilers[] = %{traditional-cpp:-traditional}\ %{g*} %{W*} %{w} %{pedantic*} %{H} %{d*} %C %{D*} %{U*} %{i*}\ %i %{!M:%{!MM:%{!E:%{!pipe:%g.i}}}}%{E:%W{o*}}%{M:%W{o*}}%{MM:%W{o*}} |\n", - "%{!M:%{!MM:%{!E:cc1obj %{!pipe:%g.i} %1 \ + "%{!M:%{!MM:%{!E:gcc1obj %{!pipe:%g.i} %1 \ %{!Q:-quiet} -dumpbase %b.m %{d*} %{m*} %{a}\ %{g*} %{O*} %{W*} %{w} %{pedantic*} %{ansi} \ %{traditional} %{v:-version} %{pg:-p} %{p} %{f*} \ @@ -492,7 +492,7 @@ static struct compiler default_compilers[] = {".h", "@c-header"}, {"@c-header", "%{!E:%eCompilation of header file requested} \ - cpp %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\ + gcpp %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\ %{C:%{!E:%eGNU C does not support -C without using -E}}\ %{M} %{MM} %{MD:-MD %b.d} %{MMD:-MMD %b.d} \ -undef -D__GNUC__=2 %{ansi:-trigraphs -$ -D__STRICT_ANSI__}\ @@ -505,7 +505,7 @@ static struct compiler default_compilers[] = {".cxx", "@c++"}, {".C", "@c++"}, {"@c++", - "cpp -lang-c++ %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\ + "gcpp -lang-c++ %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\ %{C:%{!E:%eGNU C++ does not support -C without using -E}}\ %{M} %{MM} %{MD:-MD %b.d} %{MMD:-MMD %b.d} \ -undef -D__GNUC__=2 -D__GNUG__=2 -D__cplusplus \ @@ -514,7 +514,7 @@ static struct compiler default_compilers[] = %{traditional-cpp:-traditional} %{trigraphs}\ %{g*} %{W*} %{w} %{pedantic*} %{H} %{d*} %C %{D*} %{U*} %{i*}\ %i %{!M:%{!MM:%{!E:%{!pipe:%g.i}}}}%{E:%W{o*}}%{M:%W{o*}}%{MM:%W{o*}} |\n", - "%{!M:%{!MM:%{!E:cc1plus %{!pipe:%g.i} %1 %2\ + "%{!M:%{!MM:%{!E:gcc1plus %{!pipe:%g.i} %1 %2\ %{!Q:-quiet} -dumpbase %b.cc %{d*} %{m*} %{a}\ %{g*} %{O*} %{W*} %{w} %{pedantic*} %{ansi} %{traditional}\ %{v:-version} %{pg:-p} %{p} %{f*} %{+e*}\ @@ -526,7 +526,7 @@ static struct compiler default_compilers[] = %{!pipe:%g.s} %A\n }}}}"}, {".i", "@cpp-output"}, {"@cpp-output", - "cc1 %i %1 %{!Q:-quiet} %{d*} %{m*} %{a}\ + "gcc1 %i %1 %{!Q:-quiet} %{d*} %{m*} %{a}\ %{g*} %{O*} %{W*} %{w} %{pedantic*} %{ansi} %{traditional}\ %{v:-version} %{pg:-p} %{p} %{f*}\ %{aux-info*}\ @@ -536,7 +536,7 @@ static struct compiler default_compilers[] = %{c:%W{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%u.o} %{!pipe:%g.s} %A\n }"}, {".ii", "@c++-cpp-output"}, {"@c++-cpp-output", - "cc1plus %i %1 %2 %{!Q:-quiet} %{d*} %{m*} %{a}\ + "gcc1plus %i %1 %2 %{!Q:-quiet} %{d*} %{m*} %{a}\ %{g*} %{O*} %{W*} %{w} %{pedantic*} %{ansi} %{traditional}\ %{v:-version} %{pg:-p} %{p} %{f*} %{+e*}\ %{aux-info*}\ @@ -551,7 +551,7 @@ static struct compiler default_compilers[] = %{c:%W{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%u.o} %i %A\n }"}, {".S", "@assembler-with-cpp"}, {"@assembler-with-cpp", - "cpp -lang-asm %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\ + "gcpp -lang-asm %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\ %{C:%{!E:%eGNU C does not support -C without using -E}}\ %{M} %{MM} %{MD:-MD %b.d} %{MMD:-MMD %b.d} %{trigraphs} \ -undef -$ %{!undef:%p %P} -D__ASSEMBLER__ \ @@ -731,6 +731,10 @@ translate_options (argcp, argvp) { int optlen = strlen (option_map[j].name); int complen = strlen (argv[i]); + char *arginfo = option_map[j].arg_info; + + if (arginfo == 0) + arginfo = ""; if (complen > optlen) complen = optlen; if (!strncmp (argv[i], option_map[j].name, complen)) @@ -745,18 +749,18 @@ translate_options (argcp, argvp) arg = argv[i] + optlen + 1; /* If this mapping allows extra text at end of name, accept that as "argument". */ - else if (index (option_map[j].arg_info, '*') != 0) + else if (index (arginfo, '*') != 0) arg = argv[i] + optlen; /* Otherwise, extra text at end means mismatch. Try other mappings. */ else continue; } - else if (index (option_map[j].arg_info, '*') != 0) + else if (index (arginfo, '*') != 0) error ("Incomplete `%s' option", option_map[j].name); /* Handle arguments. */ - if (index (option_map[j].arg_info, 'o') != 0) + if (index (arginfo, 'o') != 0) { if (arg == 0) { @@ -766,7 +770,7 @@ translate_options (argcp, argvp) arg = argv[++i]; } } - else if (index (option_map[j].arg_info, 'a') == 0) + else if (index (arginfo, 'a') == 0) { if (arg != 0) error ("Extraneous argument to `%s' option", @@ -775,7 +779,7 @@ translate_options (argcp, argvp) } /* Store the translation as one argv elt or as two. */ - if (arg != 0 && index (option_map[j].arg_info, 'j') != 0) + if (arg != 0 && index (arginfo, 'j') != 0) newv[newindex++] = concat (option_map[j].equivalent, arg, ""); else if (arg != 0) @@ -1125,17 +1129,17 @@ static char *gcc_exec_prefix; #endif #ifndef STANDARD_EXEC_PREFIX -#define STANDARD_EXEC_PREFIX "/usr/local/lib/gcc-lib/" +#define STANDARD_EXEC_PREFIX "/usr/libexec/" #endif /* !defined STANDARD_EXEC_PREFIX */ static char *standard_exec_prefix = STANDARD_EXEC_PREFIX; -static char *standard_exec_prefix_1 = "/usr/lib/gcc/"; +static char *standard_exec_prefix_1 = ""; #ifdef MD_EXEC_PREFIX static char *md_exec_prefix = MD_EXEC_PREFIX; #endif #ifndef STANDARD_STARTFILE_PREFIX -#define STANDARD_STARTFILE_PREFIX "/usr/local/lib/" +#define STANDARD_STARTFILE_PREFIX "/usr/lib/" #endif /* !defined STANDARD_STARTFILE_PREFIX */ #ifdef MD_STARTFILE_PREFIX diff --git a/gnu/usr.bin/cc/cc1/Makefile b/gnu/usr.bin/cc/cc1/Makefile index 82c023f852..6e26b51406 100644 --- a/gnu/usr.bin/cc/cc1/Makefile +++ b/gnu/usr.bin/cc/cc1/Makefile @@ -1,13 +1,23 @@ # @(#)Makefile 6.2 (Berkeley) 2/2/91 -PROG= cc1 +PROG= gcc1 BINDIR= /usr/libexec SRCS= c-parse.c c-lang.c c-lex.c c-pragma.c c-decl.c \ - c-typeck.c c-convert.c c-aux-info.c c-iterate.c \ - $(COMMON_SRC) + c-typeck.c c-convert.c c-aux-info.c c-iterate.c -CFLAGS+= -I. -I$(.CURDIR) -I$(.CURDIR)/../common +CFLAGS+= -I. -I$(.CURDIR) -I$(.CURDIR)/../lib YFLAGS= NOMAN= noman -.PATH: $(.CURDIR)/../common + +.if exists(${.CURDIR}/../lib/obj) +LDADD= -L${.CURDIR}/../lib/obj -lgcc2 +DPADD= ${.CURDIR}/../lib/obj/libgcc2.a +.else +LDADD= -L${.CURDIR}/../lib/ -lgcc2 +DPADD= ${.CURDIR}/../lib/libgcc2.a +.endif + +LDADD+= -lgnumalloc +DPADD+= ${LIBGNUMALLOC} + .include diff --git a/gnu/usr.bin/cc/cc1/c-decl.c b/gnu/usr.bin/cc/cc1/c-decl.c index f661dc40f8..08f003e1f0 100644 --- a/gnu/usr.bin/cc/cc1/c-decl.c +++ b/gnu/usr.bin/cc/cc1/c-decl.c @@ -2028,6 +2028,7 @@ pushdecl (x) DECL_INITIAL (x) = (current_function_decl == oldglobal ? 0 : DECL_INITIAL (oldglobal)); DECL_SAVED_INSNS (x) = DECL_SAVED_INSNS (oldglobal); + DECL_FRAME_SIZE (x) = DECL_FRAME_SIZE (oldglobal); DECL_ARGUMENTS (x) = DECL_ARGUMENTS (oldglobal); DECL_RESULT (x) = DECL_RESULT (oldglobal); TREE_ASM_WRITTEN (x) = TREE_ASM_WRITTEN (oldglobal); @@ -3518,7 +3519,10 @@ finish_decl (decl, init, asmspec_tree) /* ??? After 2.3, test (init != 0) instead of TREE_CODE. */ if (!(TREE_CODE (decl) == FUNCTION_DECL && DECL_INLINE (decl)) - && temporary && TREE_PERMANENT (decl)) + && temporary && TREE_PERMANENT (decl) + /* DECL_INITIAL is not defined in PARM_DECLs, since it shares + space with DECL_ARG_TYPE. */ + && TREE_CODE (decl) != PARM_DECL) { /* We need to remember that this array HAD an initialization, but discard the actual temporary nodes, @@ -4453,13 +4457,16 @@ grokdeclarator (declarator, declspecs, decl_context, initialized) When there is a prototype, this is overridden later. */ DECL_ARG_TYPE (decl) = type; - main_type = TYPE_MAIN_VARIANT (type); + main_type = (type == error_mark_node + ? error_mark_node + : TYPE_MAIN_VARIANT (type)); if (main_type == float_type_node) DECL_ARG_TYPE (decl) = double_type_node; /* Don't use TYPE_PRECISION to decide whether to promote, because we should convert short if it's the same size as int, but we should not convert long if it's the same size as int. */ - else if (C_PROMOTING_INTEGER_TYPE_P (main_type)) + else if (TREE_CODE (main_type) != ERROR_MARK + && C_PROMOTING_INTEGER_TYPE_P (main_type)) { if (TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node) && TREE_UNSIGNED (type)) diff --git a/gnu/usr.bin/cc/cc1/c-iterate.c b/gnu/usr.bin/cc/cc1/c-iterate.c index 0325026fdc..09df3e4663 100644 --- a/gnu/usr.bin/cc/cc1/c-iterate.c +++ b/gnu/usr.bin/cc/cc1/c-iterate.c @@ -31,7 +31,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "rtl.h" static void expand_stmt_with_iterators_1 (); -static tree collect_iterators(); +static tree collect_iterators (); static void iterator_loop_prologue (); static void iterator_loop_epilogue (); static void add_ixpansion (); @@ -97,6 +97,9 @@ struct iter_stack_node struct iter_stack_node *iter_stack; struct iter_stack_node sublevel_ixpansions; + +/* During collect_iterators, a list of SAVE_EXPRs already scanned. */ +static tree save_exprs; /* Initialize our obstack once per compilation. */ @@ -144,7 +147,9 @@ void iterator_expand (stmt) tree stmt; { - tree iter_list = collect_iterators (stmt, NULL_TREE); + tree iter_list; + save_exprs = NULL_TREE; + iter_list = collect_iterators (stmt, NULL_TREE); expand_stmt_with_iterators_1 (stmt, iter_list); istack_sublevel_to_current (); } @@ -202,6 +207,17 @@ collect_iterators (exp, list) return list; } + case SAVE_EXPR: + /* In each scan, scan a given save_expr only once. */ + { + tree tail; + for (tail = save_exprs; tail; tail = TREE_CHAIN (tail)) + if (TREE_VALUE (tail) == exp) + return list; + } + save_exprs = tree_cons (NULL_TREE, exp, save_exprs); + return collect_iterators (TREE_OPERAND (exp, 0), list); + /* we do not automatically iterate blocks -- one must */ /* use the FOR construct to do that */ @@ -229,7 +245,6 @@ collect_iterators (exp, list) /* Some tree codes have RTL, not trees, as operands. */ switch (TREE_CODE (exp)) { - case SAVE_EXPR: case CALL_EXPR: num_args = 2; break; diff --git a/gnu/usr.bin/cc/cc1/c-typeck.c b/gnu/usr.bin/cc/cc1/c-typeck.c index 89a838fef8..a10fb0abcc 100644 --- a/gnu/usr.bin/cc/cc1/c-typeck.c +++ b/gnu/usr.bin/cc/cc1/c-typeck.c @@ -1794,12 +1794,18 @@ check_format (info, params) this = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (wanted_type))); that = 0; - if (TYPE_NAME (cur_type) != 0 + if (TREE_CODE (cur_type) != ERROR_MARK + && TYPE_NAME (cur_type) != 0 && TREE_CODE (cur_type) != INTEGER_TYPE && !(TREE_CODE (cur_type) == POINTER_TYPE - && TREE_CODE (TREE_TYPE (cur_type)) == INTEGER_TYPE) - && DECL_NAME (TYPE_NAME (cur_type)) != 0) - that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (cur_type))); + && TREE_CODE (TREE_TYPE (cur_type)) == INTEGER_TYPE)) + { + if (TREE_CODE (TYPE_NAME (cur_type)) == TYPE_DECL + && DECL_NAME (TYPE_NAME (cur_type)) != 0) + that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (cur_type))); + else + that = IDENTIFIER_POINTER (TYPE_NAME (cur_type)); + } /* A nameless type can't possibly match what the format wants. So there will be a warning for it. @@ -4173,6 +4179,51 @@ convert_for_assignment (type, rhs, errtype, fundecl, funname, parmnum) pedwarn ("ANSI C prohibits argument conversion to union type"); return build1 (NOP_EXPR, type, rhs); } + else if (coder == POINTER_TYPE + && TREE_CODE (TREE_TYPE (memb_types)) == POINTER_TYPE) + { + tree memb_type = TREE_TYPE (memb_types); + register tree ttl = TREE_TYPE (memb_type); + register tree ttr = TREE_TYPE (rhstype); + + /* Any non-function converts to a [const][volatile] void * + and vice versa; otherwise, targets must be the same. + Meanwhile, the lhs target must have all the qualifiers of the rhs. */ + if (TYPE_MAIN_VARIANT (ttl) == void_type_node + || TYPE_MAIN_VARIANT (ttr) == void_type_node + || comp_target_types (memb_type, rhstype)) + { + /* Const and volatile mean something different for function types, + so the usual warnings are not appropriate. */ + if (TREE_CODE (ttr) != FUNCTION_TYPE + || TREE_CODE (ttl) != FUNCTION_TYPE) + { + if (! TYPE_READONLY (ttl) && TYPE_READONLY (ttr)) + warn_for_assignment ("%s discards `const' from pointer target type", + get_spelling (errtype), funname, parmnum); + if (! TYPE_VOLATILE (ttl) && TYPE_VOLATILE (ttr)) + warn_for_assignment ("%s discards `volatile' from pointer target type", + get_spelling (errtype), funname, parmnum); + } + else + { + /* Because const and volatile on functions are restrictions + that say the function will not do certain things, + it is okay to use a const or volatile function + where an ordinary one is wanted, but not vice-versa. */ + if (TYPE_READONLY (ttl) && ! TYPE_READONLY (ttr)) + warn_for_assignment ("%s makes `const *' function pointer from non-const", + get_spelling (errtype), funname, parmnum); + if (TYPE_VOLATILE (ttl) && ! TYPE_VOLATILE (ttr)) + warn_for_assignment ("%s makes `volatile *' function pointer from non-volatile", + get_spelling (errtype), funname, parmnum); + } + if (pedantic + && !(fundecl != 0 && DECL_IN_SYSTEM_HEADER (fundecl))) + pedwarn ("ANSI C prohibits argument conversion to union type"); + return build1 (NOP_EXPR, type, rhs); + } + } } } /* Conversions among pointers */ diff --git a/gnu/usr.bin/cc/cc1obj/Makefile b/gnu/usr.bin/cc/cc1obj/Makefile index a7c398dca3..c505419ef6 100644 --- a/gnu/usr.bin/cc/cc1obj/Makefile +++ b/gnu/usr.bin/cc/cc1obj/Makefile @@ -1,12 +1,22 @@ -PROG= cc1obj -BINDIR= /usr/libexec -SRCS= objc-parse.c objc-act.c \ - c-lex.c c-pragma.c c-decl.c c-typeck.c c-convert.c\ - c-aux-info.c c-iterate.c \ - $(COMMON_SRC) -CFLAGS+= -I. -I$(.CURDIR) -I$(.CURDIR)/../common +PROG= gcc1obj +BINDIR= /usr/libexec +SRCS= objc-parse.c objc-act.c \ + c-lex.c c-pragma.c c-decl.c c-typeck.c c-convert.c \ + c-aux-info.c c-iterate.c +CFLAGS+= -I. -I$(.CURDIR) -I$(.CURDIR)/../lib YFLAGS= -NOMAN= noman -.PATH: $(.CURDIR)/../common $(.CURDIR)/../cc1 +NOMAN= noman +.PATH: $(.CURDIR)/../cc1 + +.if exists(${.CURDIR}/../lib/obj) +LDADD= -L${.CURDIR}/../lib/obj -lgcc2 +DPADD= ${.CURDIR}/../lib/obj/libgcc2.a +.else +LDADD= -L${.CURDIR}/../lib/ -lgcc2 +DPADD= ${.CURDIR}/../lib/libgcc2.a +.endif + +LDADD+= -lgnumalloc +DPADD+= ${LIBGNUMALLOC} .include diff --git a/gnu/usr.bin/cc/cc1obj/objc-act.c b/gnu/usr.bin/cc/cc1obj/objc-act.c index fbec4d313c..a307b336be 100644 --- a/gnu/usr.bin/cc/cc1obj/objc-act.c +++ b/gnu/usr.bin/cc/cc1obj/objc-act.c @@ -6073,7 +6073,8 @@ encode_type (type, curtype, format) if (code == INTEGER_TYPE) { - if (TREE_INT_CST_LOW (TYPE_MIN_VALUE (type)) == 0) + if (TREE_INT_CST_LOW (TYPE_MIN_VALUE (type)) == 0 + && TREE_INT_CST_HIGH (TYPE_MIN_VALUE (type)) == 0) { /* unsigned integer types */ @@ -6088,6 +6089,8 @@ encode_type (type, curtype, format) else obstack_1grow (&util_obstack, 'I'); /* 'I' */ } + else if (TYPE_MODE (type) == DImode) /* 'Q' */ + obstack_1grow (&util_obstack, 'Q'); } else /* signed integer types */ { @@ -6102,6 +6105,8 @@ encode_type (type, curtype, format) else obstack_1grow (&util_obstack, 'i'); /* 'i' */ } + else if (TYPE_MODE (type) == DImode) /* 'q' */ + obstack_1grow (&util_obstack, 'q'); } } else if (code == REAL_TYPE) diff --git a/gnu/usr.bin/cc/cc1plus/Makefile b/gnu/usr.bin/cc/cc1plus/Makefile index def6f2bf50..2d7210fbb8 100644 --- a/gnu/usr.bin/cc/cc1plus/Makefile +++ b/gnu/usr.bin/cc/cc1plus/Makefile @@ -1,14 +1,24 @@ -PROG= cc1plus +PROG= gcc1plus BINDIR= /usr/libexec SRCS= cp-parse.c cp-decl.c cp-decl2.c cp-typeck.c cp-type2.c \ cp-tree.c cp-ptree.c cp-cvt.c cp-search.c cp-lex.c \ cp-gc.c cp-call.c cp-class.c cp-init.c cp-method.c \ cp-except.c cp-expr.c cp-pt.c cp-edsel.c cp-xref.c \ - cp-spew.c $(COMMON_SRC) + cp-spew.c -CFLAGS+= -I. -I$(.CURDIR) -I$(.CURDIR)/../common +CFLAGS+= -I. -I$(.CURDIR) -I$(.CURDIR)/../lib NOMAN= noman YFLAGS= -.PATH: $(.CURDIR)/../common + +.if exists(${.CURDIR}/../lib/obj) +LDADD= -L${.CURDIR}/../lib/obj -lgcc2 +DPADD= ${.CURDIR}../lib/obj/libgcc2.a +.else +LDADD= -L${.CURDIR}/../lib/ -lgcc2 +DPADD= ${.CURDIR}../lib/libgcc2.a +.endif + +LDADD+= -lgnumalloc +DPADD+= ${LIBGNUMALLOC} .include diff --git a/gnu/usr.bin/cc/cc1plus/cp-decl.c b/gnu/usr.bin/cc/cc1plus/cp-decl.c index 7cc3283922..b888baa16b 100644 --- a/gnu/usr.bin/cc/cc1plus/cp-decl.c +++ b/gnu/usr.bin/cc/cc1plus/cp-decl.c @@ -9794,7 +9794,11 @@ start_function (declspecs, declarator, raises, pre_parsed_p) if (!ctype && DECL_FRIEND_P (decl1)) { ctype = TREE_TYPE (TREE_CHAIN (decl1)); - if (TREE_CODE (ctype) != RECORD_TYPE) + + /* CTYPE could be null here if we're dealing with a template; + for example, `inline friend float foo()' inside a template + will have no CTYPE set. */ + if (ctype && TREE_CODE (ctype) != RECORD_TYPE) ctype = NULL_TREE; else doing_friend = 1; @@ -10854,13 +10858,6 @@ finish_function (lineno, call_poplevel) poplevel (0, 0, 0); } - /* Must mark the RESULT_DECL as being in this function. */ - DECL_CONTEXT (DECL_RESULT (fndecl)) = DECL_INITIAL (fndecl); - - /* Obey `register' declarations if `setjmp' is called in this fn. */ - if (flag_traditional && current_function_calls_setjmp) - setjmp_protect (DECL_INITIAL (fndecl)); - if (cleanup_label) /* Emit label at beginning of cleanup code for parameters. */ emit_label (cleanup_label); @@ -10927,6 +10924,13 @@ finish_function (lineno, call_poplevel) my_friendly_abort (122); poplevel (1, 0, 1); + /* Must mark the RESULT_DECL as being in this function. */ + DECL_CONTEXT (DECL_RESULT (fndecl)) = DECL_INITIAL (fndecl); + + /* Obey `register' declarations if `setjmp' is called in this fn. */ + if (flag_traditional && current_function_calls_setjmp) + setjmp_protect (DECL_INITIAL (fndecl)); + /* Set the BLOCK_SUPERCONTEXT of the outermost function scope to point to the FUNCTION_DECL node itself. */ BLOCK_SUPERCONTEXT (DECL_INITIAL (fndecl)) = fndecl; diff --git a/gnu/usr.bin/cc/cc1plus/cp-search.c b/gnu/usr.bin/cc/cc1plus/cp-search.c index 46d7be6dc2..9f3e0a5a15 100644 --- a/gnu/usr.bin/cc/cc1plus/cp-search.c +++ b/gnu/usr.bin/cc/cc1plus/cp-search.c @@ -1642,6 +1642,8 @@ is_subobject_of_p (parent, binfo) for (i = 0; i < n_baselinks; i++) { tree base_binfo = TREE_VEC_ELT (binfos, i); + if (TREE_VIA_VIRTUAL (base_binfo)) + base_binfo = TYPE_BINFO (BINFO_TYPE (base_binfo)); if (is_subobject_of_p (parent, base_binfo)) return 1; } @@ -1681,7 +1683,8 @@ lookup_fnfields_here (type, name) fndecls = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type), index); while (fndecls) { - if (DECL_CLASS_CONTEXT (fndecls) == type) + if (TYPE_MAIN_VARIANT (DECL_CLASS_CONTEXT (fndecls)) + == TYPE_MAIN_VARIANT (type)) return index; fndecls = TREE_CHAIN (fndecls); } @@ -1702,10 +1705,10 @@ lookup_field (xbasetype, name, protect, want_type) int protect, want_type; { int head = 0, tail = 0; - tree rval, rval_binfo = NULL_TREE; + tree rval, rval_binfo = NULL_TREE, rval_binfo_h; tree type, basetype_chain, basetype_path; enum visibility_type this_v = visibility_default; - tree entry, binfo; + tree entry, binfo, binfo_h; enum visibility_type own_visibility = visibility_default; int vbase_name_p = VBASE_NAME_P (name); @@ -1716,6 +1719,13 @@ lookup_field (xbasetype, name, protect, want_type) checks. Whereas rval is only set if a proper (not hidden) non-function member is found. */ + /* rval_binfo_h and binfo_h are binfo values used when we perform the + hiding checks, as virtual base classes may not be shared. The strategy + is we always go into the the binfo hierarchy owned by TYPE_BINFO of + virtual base classes, as we cross virtual base class lines. This way + we know that binfo of a virtual base class will always == itself when + found along any line. (mrs) */ + /* Things for memoization. */ char *errstr = 0; @@ -1765,7 +1775,10 @@ lookup_field (xbasetype, name, protect, want_type) rval = lookup_field_1 (type, name); if (rval || lookup_fnfields_here (type, name)>=0) - rval_binfo = basetype_path; + { + rval_binfo = basetype_path; + rval_binfo_h = rval_binfo; + } if (rval && TREE_CODE (rval) != TYPE_DECL && want_type) rval = NULL_TREE; @@ -1825,6 +1838,7 @@ lookup_field (xbasetype, name, protect, want_type) BINFO_VIA_PUBLIC (basetype_path) = 1; BINFO_INHERITANCE_CHAIN (basetype_path) = NULL_TREE; binfo = basetype_path; + binfo_h = binfo; while (1) { @@ -1845,6 +1859,14 @@ lookup_field (xbasetype, name, protect, want_type) TREE_VIA_PUBLIC (btypes) = TREE_VIA_PUBLIC (base_binfo); TREE_VIA_PROTECTED (btypes) = TREE_VIA_PROTECTED (base_binfo); TREE_VIA_VIRTUAL (btypes) = TREE_VIA_VIRTUAL (base_binfo); + if (TREE_VIA_VIRTUAL (base_binfo)) + btypes = tree_cons (NULL_TREE, + TYPE_BINFO (BINFO_TYPE (TREE_VEC_ELT (BINFO_BASETYPES (binfo_h), i))), + btypes); + else + btypes = tree_cons (NULL_TREE, + TREE_VEC_ELT (BINFO_BASETYPES (binfo_h), i), + btypes); obstack_ptr_grow (&search_obstack, btypes); tail += 1; if (tail >= search_stack->limit) @@ -1857,6 +1879,8 @@ lookup_field (xbasetype, name, protect, want_type) break; basetype_chain = search_stack->first[head++]; + binfo_h = TREE_VALUE (basetype_chain); + basetype_chain = TREE_CHAIN (basetype_chain); basetype_path = TREE_VALUE (basetype_chain); if (TREE_CHAIN (basetype_chain)) BINFO_INHERITANCE_CHAIN (basetype_path) = TREE_VALUE (TREE_CHAIN (basetype_chain)); @@ -1874,12 +1898,12 @@ lookup_field (xbasetype, name, protect, want_type) if (nval || lookup_fnfields_here (type, name)>=0) { - if (rval_binfo && hides (rval_binfo, binfo)) + if (rval_binfo && hides (rval_binfo_h, binfo_h)) { /* This is ok, the member found is in rval_binfo, not here (binfo). */ } - else if (rval_binfo==NULL_TREE || hides (binfo, rval_binfo)) + else if (rval_binfo==NULL_TREE || hides (binfo_h, rval_binfo_h)) { /* This is ok, the member found is here (binfo), not in rval_binfo. */ @@ -1898,6 +1922,7 @@ lookup_field (xbasetype, name, protect, want_type) rval = NULL_TREE; } rval_binfo = binfo; + rval_binfo_h = binfo_h; } else { @@ -1936,14 +1961,14 @@ lookup_field (xbasetype, name, protect, want_type) enum visibility_type new_v; if (this_v != visibility_default) - new_v = compute_visibility (TREE_VALUE (*tp), rval); + new_v = compute_visibility (TREE_VALUE (TREE_CHAIN (*tp)), rval); if (this_v != visibility_default && new_v != this_v) { errstr = "conflicting visibilities to member `%s'"; this_v = visibility_default; } own_visibility = new_v; - CLEAR_BINFO_FIELDS_MARKED (TREE_VALUE (*tp)); + CLEAR_BINFO_FIELDS_MARKED (TREE_VALUE (TREE_CHAIN (*tp))); tp += 1; } } @@ -1951,7 +1976,7 @@ lookup_field (xbasetype, name, protect, want_type) { while (tp < search_tail) { - CLEAR_BINFO_FIELDS_MARKED (TREE_VALUE (*tp)); + CLEAR_BINFO_FIELDS_MARKED (TREE_VALUE (TREE_CHAIN (*tp))); tp += 1; } } @@ -2120,8 +2145,8 @@ lookup_fnfields (basetype_path, name, complain) int complain; { int head = 0, tail = 0; - tree type, rval, rval_binfo = NULL_TREE, rvals = NULL_TREE; - tree entry, binfo, basetype_chain; + tree type, rval, rval_binfo = NULL_TREE, rvals = NULL_TREE, rval_binfo_h; + tree entry, binfo, basetype_chain, binfo_h; int find_all = 0; /* rval_binfo is the binfo associated with the found member, note, @@ -2131,6 +2156,13 @@ lookup_fnfields (basetype_path, name, complain) checks. Whereas rval is only set if a proper (not hidden) function member is found. */ + /* rval_binfo_h and binfo_h are binfo values used when we perform the + hiding checks, as virtual base classes may not be shared. The strategy + is we always go into the the binfo hierarchy owned by TYPE_BINFO of + virtual base classes, as we cross virtual base class lines. This way + we know that binfo of a virtual base class will always == itself when + found along any line. (mrs) */ + /* For now, don't try this. */ int protect = complain; @@ -2148,6 +2180,7 @@ lookup_fnfields (basetype_path, name, complain) } binfo = basetype_path; + binfo_h = binfo; type = BINFO_TYPE (basetype_path); /* The memoization code is in need of maintenance. */ @@ -2212,7 +2245,10 @@ lookup_fnfields (basetype_path, name, complain) index = lookup_fnfields_here (type, name); if (index >= 0 || lookup_field_1 (type, name)) - rval_binfo = basetype_path; + { + rval_binfo = basetype_path; + rval_binfo_h = rval_binfo; + } if (index >= 0) { @@ -2245,6 +2281,7 @@ lookup_fnfields (basetype_path, name, complain) BINFO_VIA_PUBLIC (basetype_path) = 1; BINFO_INHERITANCE_CHAIN (basetype_path) = NULL_TREE; binfo = basetype_path; + binfo_h = binfo; while (1) { @@ -2265,6 +2302,14 @@ lookup_fnfields (basetype_path, name, complain) TREE_VIA_PUBLIC (btypes) = TREE_VIA_PUBLIC (base_binfo); TREE_VIA_PROTECTED (btypes) = TREE_VIA_PROTECTED (base_binfo); TREE_VIA_VIRTUAL (btypes) = TREE_VIA_VIRTUAL (base_binfo); + if (TREE_VIA_VIRTUAL (base_binfo)) + btypes = tree_cons (NULL_TREE, + TYPE_BINFO (BINFO_TYPE (TREE_VEC_ELT (BINFO_BASETYPES (binfo_h), i))), + btypes); + else + btypes = tree_cons (NULL_TREE, + TREE_VEC_ELT (BINFO_BASETYPES (binfo_h), i), + btypes); obstack_ptr_grow (&search_obstack, btypes); tail += 1; if (tail >= search_stack->limit) @@ -2277,6 +2322,8 @@ lookup_fnfields (basetype_path, name, complain) break; basetype_chain = search_stack->first[head++]; + binfo_h = TREE_VALUE (basetype_chain); + basetype_chain = TREE_CHAIN (basetype_chain); basetype_path = TREE_VALUE (basetype_chain); if (TREE_CHAIN (basetype_chain)) BINFO_INHERITANCE_CHAIN (basetype_path) = TREE_VALUE (TREE_CHAIN (basetype_chain)); @@ -2294,12 +2341,12 @@ lookup_fnfields (basetype_path, name, complain) if (index >= 0 || (lookup_field_1 (type, name)!=NULL_TREE && !find_all)) { - if (rval_binfo && !find_all && hides (rval_binfo, binfo)) + if (rval_binfo && !find_all && hides (rval_binfo_h, binfo_h)) { /* This is ok, the member found is in rval_binfo, not here (binfo). */ } - else if (rval_binfo==NULL_TREE || find_all || hides (binfo, rval_binfo)) + else if (rval_binfo==NULL_TREE || find_all || hides (binfo_h, rval_binfo_h)) { /* This is ok, the member found is here (binfo), not in rval_binfo. */ @@ -2320,6 +2367,7 @@ lookup_fnfields (basetype_path, name, complain) rvals = NULL_TREE; } rval_binfo = binfo; + rval_binfo_h = binfo_h; } else { @@ -2336,7 +2384,7 @@ lookup_fnfields (basetype_path, name, complain) while (tp < search_tail) { - CLEAR_BINFO_FIELDS_MARKED (TREE_VALUE (*tp)); + CLEAR_BINFO_FIELDS_MARKED (TREE_VALUE (TREE_CHAIN (*tp))); tp += 1; } } diff --git a/gnu/usr.bin/cc/cc1plus/cp-typeck.c b/gnu/usr.bin/cc/cc1plus/cp-typeck.c index eb1847160c..362e170809 100644 --- a/gnu/usr.bin/cc/cc1plus/cp-typeck.c +++ b/gnu/usr.bin/cc/cc1plus/cp-typeck.c @@ -3554,6 +3554,7 @@ build_unary_op (code, xarg, noconvert) } else if (TREE_CODE (arg) == FUNCTION_DECL && DECL_NAME (arg) + && DECL_CONTEXT (arg) == NULL_TREE && IDENTIFIER_LENGTH (DECL_NAME (arg)) == 4 && IDENTIFIER_POINTER (DECL_NAME (arg))[0] == 'm' && ! strcmp (IDENTIFIER_POINTER (DECL_NAME (arg)), "main")) diff --git a/gnu/usr.bin/cc/common/aux-output.c b/gnu/usr.bin/cc/common/aux-output.c deleted file mode 100644 index 7aecf5c9ec..0000000000 --- a/gnu/usr.bin/cc/common/aux-output.c +++ /dev/null @@ -1,1901 +0,0 @@ -/* Subroutines for insn-output.c for Intel 80386. - Copyright (C) 1988, 1992 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include "config.h" -#include "rtl.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "real.h" -#include "insn-config.h" -#include "conditions.h" -#include "insn-flags.h" -#include "output.h" -#include "insn-attr.h" -#include "tree.h" -#include "flags.h" - -#ifdef EXTRA_CONSTRAINT -/* If EXTRA_CONSTRAINT is defined, then the 'S' - constraint in REG_CLASS_FROM_LETTER will no longer work, and various - asm statements that need 'S' for class SIREG will break. */ - error EXTRA_CONSTRAINT conflicts with S constraint letter -/* The previous line used to be #error, but some compilers barf - even if the conditional was untrue. */ -#endif - -#define AT_BP(mode) (gen_rtx (MEM, (mode), frame_pointer_rtx)) - -extern FILE *asm_out_file; -extern char *strcat (); - -char *singlemove_string (); -char *output_move_const_single (); -char *output_fp_cc0_set (); - -char *hi_reg_name[] = HI_REGISTER_NAMES; -char *qi_reg_name[] = QI_REGISTER_NAMES; -char *qi_high_reg_name[] = QI_HIGH_REGISTER_NAMES; - -/* Array of the smallest class containing reg number REGNO, indexed by - REGNO. Used by REGNO_REG_CLASS in i386.h. */ - -enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] = -{ - /* ax, dx, cx, bx */ - AREG, DREG, CREG, BREG, - /* si, di, bp, sp */ - SIREG, DIREG, INDEX_REGS, GENERAL_REGS, - /* FP registers */ - FP_TOP_REG, FP_SECOND_REG, FLOAT_REGS, FLOAT_REGS, - FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, - /* arg pointer */ - INDEX_REGS -}; - -/* Test and compare insns in i386.md store the information needed to - generate branch and scc insns here. */ - -struct rtx_def *i386_compare_op0, *i386_compare_op1; -struct rtx_def *(*i386_compare_gen)(), *(*i386_compare_gen_eq)(); - -/* Output an insn whose source is a 386 integer register. SRC is the - rtx for the register, and TEMPLATE is the op-code template. SRC may - be either SImode or DImode. - - The template will be output with operands[0] as SRC, and operands[1] - as a pointer to the top of the 386 stack. So a call from floatsidf2 - would look like this: - - output_op_from_reg (operands[1], AS1 (fild%z0,%1)); - - where %z0 corresponds to the caller's operands[1], and is used to - emit the proper size suffix. - - ??? Extend this to handle HImode - a 387 can load and store HImode - values directly. */ - -void -output_op_from_reg (src, template) - rtx src; - char *template; -{ - rtx xops[4]; - - xops[0] = src; - xops[1] = AT_SP (Pmode); - xops[2] = GEN_INT (GET_MODE_SIZE (GET_MODE (src))); - xops[3] = stack_pointer_rtx; - - if (GET_MODE_SIZE (GET_MODE (src)) > UNITS_PER_WORD) - { - rtx high = gen_rtx (REG, SImode, REGNO (src) + 1); - output_asm_insn (AS1 (push%L0,%0), &high); - } - output_asm_insn (AS1 (push%L0,%0), &src); - - output_asm_insn (template, xops); - - output_asm_insn (AS2 (add%L3,%2,%3), xops); -} - -/* Output an insn to pop an value from the 387 top-of-stack to 386 - register DEST. The 387 register stack is popped if DIES is true. If - the mode of DEST is an integer mode, a `fist' integer store is done, - otherwise a `fst' float store is done. */ - -void -output_to_reg (dest, dies) - rtx dest; - int dies; -{ - rtx xops[4]; - - xops[0] = AT_SP (Pmode); - xops[1] = stack_pointer_rtx; - xops[2] = GEN_INT (GET_MODE_SIZE (GET_MODE (dest))); - xops[3] = dest; - - output_asm_insn (AS2 (sub%L1,%2,%1), xops); - - if (GET_MODE_CLASS (GET_MODE (dest)) == MODE_INT) - { - if (dies) - output_asm_insn (AS1 (fistp%z3,%y0), xops); - else - output_asm_insn (AS1 (fist%z3,%y0), xops); - } - else if (GET_MODE_CLASS (GET_MODE (dest)) == MODE_FLOAT) - { - if (dies) - output_asm_insn (AS1 (fstp%z3,%y0), xops); - else - output_asm_insn (AS1 (fst%z3,%y0), xops); - } - else - abort (); - - output_asm_insn (AS1 (pop%L0,%0), &dest); - - if (GET_MODE_SIZE (GET_MODE (dest)) > UNITS_PER_WORD) - { - dest = gen_rtx (REG, SImode, REGNO (dest) + 1); - output_asm_insn (AS1 (pop%L0,%0), &dest); - } -} - -char * -singlemove_string (operands) - rtx *operands; -{ - rtx x; - if (GET_CODE (operands[0]) == MEM - && GET_CODE (x = XEXP (operands[0], 0)) == PRE_DEC) - { - if (XEXP (x, 0) != stack_pointer_rtx) - abort (); - return "push%L1 %1"; - } - else if (GET_CODE (operands[1]) == CONST_DOUBLE) - { - return output_move_const_single (operands); - } - else if (GET_CODE (operands[0]) == REG || GET_CODE (operands[1]) == REG) - return AS2 (mov%L0,%1,%0); - else if (CONSTANT_P (operands[1])) - return AS2 (mov%L0,%1,%0); - else - { - output_asm_insn ("push%L1 %1", operands); - return "pop%L0 %0"; - } -} - -/* Return a REG that occurs in ADDR with coefficient 1. - ADDR can be effectively incremented by incrementing REG. */ - -static rtx -find_addr_reg (addr) - rtx addr; -{ - while (GET_CODE (addr) == PLUS) - { - if (GET_CODE (XEXP (addr, 0)) == REG) - addr = XEXP (addr, 0); - else if (GET_CODE (XEXP (addr, 1)) == REG) - addr = XEXP (addr, 1); - else if (CONSTANT_P (XEXP (addr, 0))) - addr = XEXP (addr, 1); - else if (CONSTANT_P (XEXP (addr, 1))) - addr = XEXP (addr, 0); - else - abort (); - } - if (GET_CODE (addr) == REG) - return addr; - abort (); -} - -/* Output an insn to add the constant N to the register X. */ - -static void -asm_add (n, x) - int n; - rtx x; -{ - rtx xops[2]; - xops[1] = x; - if (n < 0) - { - xops[0] = GEN_INT (-n); - output_asm_insn (AS2 (sub%L0,%0,%1), xops); - } - else if (n > 0) - { - xops[0] = GEN_INT (n); - output_asm_insn (AS2 (add%L0,%0,%1), xops); - } -} - -/* Output assembler code to perform a doubleword move insn - with operands OPERANDS. */ - -char * -output_move_double (operands) - rtx *operands; -{ - enum {REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1; - rtx latehalf[2]; - rtx addreg0 = 0, addreg1 = 0; - int dest_overlapped_low = 0; - - /* First classify both operands. */ - - if (REG_P (operands[0])) - optype0 = REGOP; - else if (offsettable_memref_p (operands[0])) - optype0 = OFFSOP; - else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC) - optype0 = POPOP; - else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) - optype0 = PUSHOP; - else if (GET_CODE (operands[0]) == MEM) - optype0 = MEMOP; - else - optype0 = RNDOP; - - if (REG_P (operands[1])) - optype1 = REGOP; - else if (CONSTANT_P (operands[1])) - optype1 = CNSTOP; - else if (offsettable_memref_p (operands[1])) - optype1 = OFFSOP; - else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC) - optype1 = POPOP; - else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) - optype1 = PUSHOP; - else if (GET_CODE (operands[1]) == MEM) - optype1 = MEMOP; - else - optype1 = RNDOP; - - /* Check for the cases that the operand constraints are not - supposed to allow to happen. Abort if we get one, - because generating code for these cases is painful. */ - - if (optype0 == RNDOP || optype1 == RNDOP) - abort (); - - /* If one operand is decrementing and one is incrementing - decrement the former register explicitly - and change that operand into ordinary indexing. */ - - if (optype0 == PUSHOP && optype1 == POPOP) - { - operands[0] = XEXP (XEXP (operands[0], 0), 0); - asm_add (-8, operands[0]); - operands[0] = gen_rtx (MEM, DImode, operands[0]); - optype0 = OFFSOP; - } - if (optype0 == POPOP && optype1 == PUSHOP) - { - operands[1] = XEXP (XEXP (operands[1], 0), 0); - asm_add (-8, operands[1]); - operands[1] = gen_rtx (MEM, DImode, operands[1]); - optype1 = OFFSOP; - } - - /* If an operand is an unoffsettable memory ref, find a register - we can increment temporarily to make it refer to the second word. */ - - if (optype0 == MEMOP) - addreg0 = find_addr_reg (XEXP (operands[0], 0)); - - if (optype1 == MEMOP) - addreg1 = find_addr_reg (XEXP (operands[1], 0)); - - /* Ok, we can do one word at a time. - Normally we do the low-numbered word first, - but if either operand is autodecrementing then we - do the high-numbered word first. - - In either case, set up in LATEHALF the operands to use - for the high-numbered word and in some cases alter the - operands in OPERANDS to be suitable for the low-numbered word. */ - - if (optype0 == REGOP) - latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); - else if (optype0 == OFFSOP) - latehalf[0] = adj_offsettable_operand (operands[0], 4); - else - latehalf[0] = operands[0]; - - if (optype1 == REGOP) - latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); - else if (optype1 == OFFSOP) - latehalf[1] = adj_offsettable_operand (operands[1], 4); - else if (optype1 == CNSTOP) - { - if (GET_CODE (operands[1]) == CONST_DOUBLE) - split_double (operands[1], &operands[1], &latehalf[1]); - else if (CONSTANT_P (operands[1])) - { - if (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) < 0) - latehalf[1] = constm1_rtx; - else - latehalf[1] = const0_rtx; - } - } - else - latehalf[1] = operands[1]; - - /* If insn is effectively movd N (sp),-(sp) then we will do the - high word first. We should use the adjusted operand 1 (which is N+4 (sp)) - for the low word as well, to compensate for the first decrement of sp. */ - if (optype0 == PUSHOP - && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM - && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) - operands[1] = latehalf[1]; - - /* For (set (reg:DI N) (mem:DI ... (reg:SI N) ...)), - if the upper part of reg N does not appear in the MEM, arrange to - emit the move late-half first. Otherwise, compute the MEM address - into the upper part of N and use that as a pointer to the memory - operand. */ - if (optype0 == REGOP - && (optype1 == OFFSOP || optype1 == MEMOP)) - { - if (reg_mentioned_p (operands[0], XEXP (operands[1], 0)) - && reg_mentioned_p (latehalf[0], XEXP (operands[1], 0))) - { - /* If both halves of dest are used in the src memory address, - compute the address into latehalf of dest. */ - rtx xops[2]; - xops[0] = latehalf[0]; - xops[1] = XEXP (operands[1], 0); - output_asm_insn (AS2 (lea%L0,%a1,%0), xops); - operands[1] = gen_rtx (MEM, DImode, latehalf[0]); - latehalf[1] = adj_offsettable_operand (operands[1], 4); - } - else if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))) - /* If the low half of dest is mentioned in the source memory - address, the arrange to emit the move late half first. */ - dest_overlapped_low = 1; - } - - /* If one or both operands autodecrementing, - do the two words, high-numbered first. */ - - /* Likewise, the first move would clobber the source of the second one, - do them in the other order. This happens only for registers; - such overlap can't happen in memory unless the user explicitly - sets it up, and that is an undefined circumstance. */ - - if (optype0 == PUSHOP || optype1 == PUSHOP - || (optype0 == REGOP && optype1 == REGOP - && REGNO (operands[0]) == REGNO (latehalf[1])) - || dest_overlapped_low) - { - /* Make any unoffsettable addresses point at high-numbered word. */ - if (addreg0) - asm_add (4, addreg0); - if (addreg1) - asm_add (4, addreg1); - - /* Do that word. */ - output_asm_insn (singlemove_string (latehalf), latehalf); - - /* Undo the adds we just did. */ - if (addreg0) - asm_add (-4, addreg0); - if (addreg1) - asm_add (-4, addreg1); - - /* Do low-numbered word. */ - return singlemove_string (operands); - } - - /* Normal case: do the two words, low-numbered first. */ - - output_asm_insn (singlemove_string (operands), operands); - - /* Make any unoffsettable addresses point at high-numbered word. */ - if (addreg0) - asm_add (4, addreg0); - if (addreg1) - asm_add (4, addreg1); - - /* Do that word. */ - output_asm_insn (singlemove_string (latehalf), latehalf); - - /* Undo the adds we just did. */ - if (addreg0) - asm_add (-4, addreg0); - if (addreg1) - asm_add (-4, addreg1); - - return ""; -} - -int -standard_80387_constant_p (x) - rtx x; -{ -#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) - REAL_VALUE_TYPE d; - jmp_buf handler; - int is0, is1; - - if (setjmp (handler)) - return 0; - - set_float_handler (handler); - REAL_VALUE_FROM_CONST_DOUBLE (d, x); - is0 = REAL_VALUES_EQUAL (d, dconst0); - is1 = REAL_VALUES_EQUAL (d, dconst1); - set_float_handler (NULL_PTR); - - if (is0) - return 1; - - if (is1) - return 2; - - /* Note that on the 80387, other constants, such as pi, - are much slower to load as standard constants - than to load from doubles in memory! */ -#endif - - return 0; -} - -char * -output_move_const_single (operands) - rtx *operands; -{ - if (FP_REG_P (operands[0])) - { - int conval = standard_80387_constant_p (operands[1]); - - if (conval == 1) - return "fldz"; - - if (conval == 2) - return "fld1"; - } - if (GET_CODE (operands[1]) == CONST_DOUBLE) - { - union { int i[2]; double d;} u1; - union { int i; float f;} u2; - u1.i[0] = CONST_DOUBLE_LOW (operands[1]); - u1.i[1] = CONST_DOUBLE_HIGH (operands[1]); - u2.f = u1.d; - operands[1] = GEN_INT (u2.i); - } - return singlemove_string (operands); -} - -/* Returns 1 if OP is either a symbol reference or a sum of a symbol - reference and a constant. */ - -int -symbolic_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - switch (GET_CODE (op)) - { - case SYMBOL_REF: - case LABEL_REF: - return 1; - case CONST: - op = XEXP (op, 0); - return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF - || GET_CODE (XEXP (op, 0)) == LABEL_REF) - && GET_CODE (XEXP (op, 1)) == CONST_INT); - default: - return 0; - } -} - -/* Test for a valid operand for a call instruction. - Don't allow the arg pointer register or virtual regs - since they may change into reg + const, which the patterns - can't handle yet. */ - -int -call_insn_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) == MEM - && (CONSTANT_ADDRESS_P (XEXP (op, 0)) - || (GET_CODE (XEXP (op, 0)) == REG - && XEXP (op, 0) != arg_pointer_rtx - && !(REGNO (XEXP (op, 0)) >= FIRST_PSEUDO_REGISTER - && REGNO (XEXP (op, 0)) <= LAST_VIRTUAL_REGISTER)))) - return 1; - return 0; -} - -/* Returns 1 if OP contains a symbol reference */ - -int -symbolic_reference_mentioned_p (op) - rtx op; -{ - register char *fmt; - register int i; - - if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF) - return 1; - - fmt = GET_RTX_FORMAT (GET_CODE (op)); - for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--) - { - if (fmt[i] == 'E') - { - register int j; - - for (j = XVECLEN (op, i) - 1; j >= 0; j--) - if (symbolic_reference_mentioned_p (XVECEXP (op, i, j))) - return 1; - } - else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i))) - return 1; - } - - return 0; -} - -/* Return a legitimate reference for ORIG (an address) using the - register REG. If REG is 0, a new pseudo is generated. - - There are three types of references that must be handled: - - 1. Global data references must load the address from the GOT, via - the PIC reg. An insn is emitted to do this load, and the reg is - returned. - - 2. Static data references must compute the address as an offset - from the GOT, whose base is in the PIC reg. An insn is emitted to - compute the address into a reg, and the reg is returned. Static - data objects have SYMBOL_REF_FLAG set to differentiate them from - global data objects. - - 3. Constant pool addresses must be handled special. They are - considered legitimate addresses, but only if not used with regs. - When printed, the output routines know to print the reference with the - PIC reg, even though the PIC reg doesn't appear in the RTL. - - GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC - reg also appears in the address (except for constant pool references, - noted above). - - "switch" statements also require special handling when generating - PIC code. See comments by the `casesi' insn in i386.md for details. */ - -rtx -legitimize_pic_address (orig, reg) - rtx orig; - rtx reg; -{ - rtx addr = orig; - rtx new = orig; - - if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF) - { - if (GET_CODE (addr) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (addr)) - reg = new = orig; - else - { - if (reg == 0) - reg = gen_reg_rtx (Pmode); - - if (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_FLAG (addr)) - new = gen_rtx (PLUS, Pmode, pic_offset_table_rtx, orig); - else - new = gen_rtx (MEM, Pmode, - gen_rtx (PLUS, Pmode, - pic_offset_table_rtx, orig)); - - emit_move_insn (reg, new); - } - current_function_uses_pic_offset_table = 1; - return reg; - } - else if (GET_CODE (addr) == CONST || GET_CODE (addr) == PLUS) - { - rtx base; - - if (GET_CODE (addr) == CONST) - { - addr = XEXP (addr, 0); - if (GET_CODE (addr) != PLUS) - abort (); - } - - if (XEXP (addr, 0) == pic_offset_table_rtx) - return orig; - - if (reg == 0) - reg = gen_reg_rtx (Pmode); - - base = legitimize_pic_address (XEXP (addr, 0), reg); - addr = legitimize_pic_address (XEXP (addr, 1), - base == reg ? NULL_RTX : reg); - - if (GET_CODE (addr) == CONST_INT) - return plus_constant (base, INTVAL (addr)); - - if (GET_CODE (addr) == PLUS && CONSTANT_P (XEXP (addr, 1))) - { - base = gen_rtx (PLUS, Pmode, base, XEXP (addr, 0)); - addr = XEXP (addr, 1); - } - return gen_rtx (PLUS, Pmode, base, addr); - } - return new; -} - -/* Emit insns to move operands[1] into operands[0]. */ - -void -emit_pic_move (operands, mode) - rtx *operands; - enum machine_mode mode; -{ - rtx temp = reload_in_progress ? operands[0] : gen_reg_rtx (Pmode); - - if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1])) - operands[1] = (rtx) force_reg (SImode, operands[1]); - else - operands[1] = legitimize_pic_address (operands[1], temp); -} - -/* This function generates the assembly code for function entry. - FILE is an stdio stream to output the code to. - SIZE is an int: how many units of temporary storage to allocate. */ - -void -function_prologue (file, size) - FILE *file; - int size; -{ - register int regno; - int limit; - rtx xops[4]; - int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table - || current_function_uses_const_pool); - - xops[0] = stack_pointer_rtx; - xops[1] = frame_pointer_rtx; - xops[2] = GEN_INT (size); - if (frame_pointer_needed) - { - output_asm_insn ("push%L1 %1", xops); - output_asm_insn (AS2 (mov%L0,%0,%1), xops); - } - - if (size) - output_asm_insn (AS2 (sub%L0,%2,%0), xops); - - /* Note If use enter it is NOT reversed args. - This one is not reversed from intel!! - I think enter is slower. Also sdb doesn't like it. - But if you want it the code is: - { - xops[3] = const0_rtx; - output_asm_insn ("enter %2,%3", xops); - } - */ - limit = (frame_pointer_needed ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM); - for (regno = limit - 1; regno >= 0; regno--) - if ((regs_ever_live[regno] && ! call_used_regs[regno]) - || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used)) - { - xops[0] = gen_rtx (REG, SImode, regno); - output_asm_insn ("push%L0 %0", xops); - } - - if (pic_reg_used) - { - xops[0] = pic_offset_table_rtx; - xops[1] = (rtx) gen_label_rtx (); - - output_asm_insn (AS1 (call,%P1), xops); - ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (xops[1])); - output_asm_insn (AS1 (pop%L0,%0), xops); - output_asm_insn ("addl $_GLOBAL_OFFSET_TABLE_+[.-%P1],%0", xops); - } -} - -/* Return 1 if it is appropriate to emit `ret' instructions in the - body of a function. Do this only if the epilogue is simple, needing a - couple of insns. Prior to reloading, we can't tell how many registers - must be saved, so return 0 then. - - If NON_SAVING_SETJMP is defined and true, then it is not possible - for the epilogue to be simple, so return 0. This is a special case - since NON_SAVING_SETJMP will not cause regs_ever_live to change until - final, but jump_optimize may need to know sooner if a `return' is OK. */ - -int -simple_386_epilogue () -{ - int regno; - int nregs = 0; - int reglimit = (frame_pointer_needed - ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM); - int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table - || current_function_uses_const_pool); - -#ifdef NON_SAVING_SETJMP - if (NON_SAVING_SETJMP && current_function_calls_setjmp) - return 0; -#endif - - if (! reload_completed) - return 0; - - for (regno = reglimit - 1; regno >= 0; regno--) - if ((regs_ever_live[regno] && ! call_used_regs[regno]) - || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used)) - nregs++; - - return nregs == 0 || ! frame_pointer_needed; -} - -/* This function generates the assembly code for function exit. - FILE is an stdio stream to output the code to. - SIZE is an int: how many units of temporary storage to deallocate. */ - -void -function_epilogue (file, size) - FILE *file; - int size; -{ - register int regno; - register int nregs, limit; - int offset; - rtx xops[3]; - int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table - || current_function_uses_const_pool); - - /* Compute the number of registers to pop */ - - limit = (frame_pointer_needed - ? FRAME_POINTER_REGNUM - : STACK_POINTER_REGNUM); - - nregs = 0; - - for (regno = limit - 1; regno >= 0; regno--) - if ((regs_ever_live[regno] && ! call_used_regs[regno]) - || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used)) - nregs++; - - /* sp is often unreliable so we must go off the frame pointer, - */ - - /* In reality, we may not care if sp is unreliable, because we can - restore the register relative to the frame pointer. In theory, - since each move is the same speed as a pop, and we don't need the - leal, this is faster. For now restore multiple registers the old - way. */ - - offset = -size - (nregs * UNITS_PER_WORD); - - xops[2] = stack_pointer_rtx; - - if (nregs > 1 || ! frame_pointer_needed) - { - if (frame_pointer_needed) - { - xops[0] = adj_offsettable_operand (AT_BP (Pmode), offset); - output_asm_insn (AS2 (lea%L2,%0,%2), xops); - } - - for (regno = 0; regno < limit; regno++) - if ((regs_ever_live[regno] && ! call_used_regs[regno]) - || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used)) - { - xops[0] = gen_rtx (REG, SImode, regno); - output_asm_insn ("pop%L0 %0", xops); - } - } - else - for (regno = 0; regno < limit; regno++) - if ((regs_ever_live[regno] && ! call_used_regs[regno]) - || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used)) - { - xops[0] = gen_rtx (REG, SImode, regno); - xops[1] = adj_offsettable_operand (AT_BP (Pmode), offset); - output_asm_insn (AS2 (mov%L0,%1,%0), xops); - offset += 4; - } - - if (frame_pointer_needed) - { - /* On i486, mov & pop is faster than "leave". */ - - if (TARGET_486) - { - xops[0] = frame_pointer_rtx; - output_asm_insn (AS2 (mov%L2,%0,%2), xops); - output_asm_insn ("pop%L0 %0", xops); - } - else - output_asm_insn ("leave", xops); - } - else if (size) - { - /* If there is no frame pointer, we must still release the frame. */ - - xops[0] = GEN_INT (size); - output_asm_insn (AS2 (add%L2,%0,%2), xops); - } - - if (current_function_pops_args && current_function_args_size) - { - xops[1] = GEN_INT (current_function_pops_args); - - /* i386 can only pop 32K bytes (maybe 64K? Is it signed?). If - asked to pop more, pop return address, do explicit add, and jump - indirectly to the caller. */ - - if (current_function_pops_args >= 32768) - { - /* ??? Which register to use here? */ - xops[0] = gen_rtx (REG, SImode, 2); - output_asm_insn ("pop%L0 %0", xops); - output_asm_insn (AS2 (add%L2,%1,%2), xops); - output_asm_insn ("jmp %*%0", xops); - } - else - output_asm_insn ("ret %1", xops); - } - else - output_asm_insn ("ret", xops); -} - -/* Print an integer constant expression in assembler syntax. Addition - and subtraction are the only arithmetic that may appear in these - expressions. FILE is the stdio stream to write to, X is the rtx, and - CODE is the operand print code from the output string. */ - -static void -output_pic_addr_const (file, x, code) - FILE *file; - rtx x; - int code; -{ - char buf[256]; - - switch (GET_CODE (x)) - { - case PC: - if (flag_pic) - putc ('.', file); - else - abort (); - break; - - case SYMBOL_REF: - case LABEL_REF: - if (GET_CODE (x) == SYMBOL_REF) - assemble_name (file, XSTR (x, 0)); - else - { - ASM_GENERATE_INTERNAL_LABEL (buf, "L", - CODE_LABEL_NUMBER (XEXP (x, 0))); - assemble_name (asm_out_file, buf); - } - - if (GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x)) - fprintf (file, "@GOTOFF(%%ebx)"); - else if (code == 'P') - fprintf (file, "@PLT"); - else if (GET_CODE (x) == LABEL_REF || ! SYMBOL_REF_FLAG (x)) - fprintf (file, "@GOT"); - else - fprintf (file, "@GOTOFF"); - - break; - - case CODE_LABEL: - ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x)); - assemble_name (asm_out_file, buf); - break; - - case CONST_INT: - fprintf (file, "%d", INTVAL (x)); - break; - - case CONST: - /* This used to output parentheses around the expression, - but that does not work on the 386 (either ATT or BSD assembler). */ - output_pic_addr_const (file, XEXP (x, 0), code); - break; - - case CONST_DOUBLE: - if (GET_MODE (x) == VOIDmode) - { - /* We can use %d if the number is <32 bits and positive. */ - if (CONST_DOUBLE_HIGH (x) || CONST_DOUBLE_LOW (x) < 0) - fprintf (file, "0x%x%08x", - CONST_DOUBLE_HIGH (x), CONST_DOUBLE_LOW (x)); - else - fprintf (file, "%d", CONST_DOUBLE_LOW (x)); - } - else - /* We can't handle floating point constants; - PRINT_OPERAND must handle them. */ - output_operand_lossage ("floating constant misused"); - break; - - case PLUS: - /* Some assemblers need integer constants to appear last (eg masm). */ - if (GET_CODE (XEXP (x, 0)) == CONST_INT) - { - output_pic_addr_const (file, XEXP (x, 1), code); - if (INTVAL (XEXP (x, 0)) >= 0) - fprintf (file, "+"); - output_pic_addr_const (file, XEXP (x, 0), code); - } - else - { - output_pic_addr_const (file, XEXP (x, 0), code); - if (INTVAL (XEXP (x, 1)) >= 0) - fprintf (file, "+"); - output_pic_addr_const (file, XEXP (x, 1), code); - } - break; - - case MINUS: - output_pic_addr_const (file, XEXP (x, 0), code); - fprintf (file, "-"); - output_pic_addr_const (file, XEXP (x, 1), code); - break; - - default: - output_operand_lossage ("invalid expression as operand"); - } -} - -/* Meaning of CODE: - f -- float insn (print a CONST_DOUBLE as a float rather than in hex). - D,L,W,B,Q,S -- print the opcode suffix for specified size of operand. - R -- print the prefix for register names. - z -- print the opcode suffix for the size of the current operand. - * -- print a star (in certain assembler syntax) - w -- print the operand as if it's a "word" (HImode) even if it isn't. - c -- don't print special prefixes before constant operands. -*/ - -void -print_operand (file, x, code) - FILE *file; - rtx x; - int code; -{ - if (code) - { - switch (code) - { - case '*': - if (USE_STAR) - putc ('*', file); - return; - - case 'L': - PUT_OP_SIZE (code, 'l', file); - return; - - case 'W': - PUT_OP_SIZE (code, 'w', file); - return; - - case 'B': - PUT_OP_SIZE (code, 'b', file); - return; - - case 'Q': - PUT_OP_SIZE (code, 'l', file); - return; - - case 'S': - PUT_OP_SIZE (code, 's', file); - return; - - case 'z': - /* 387 opcodes don't get size suffixes if the operands are - registers. */ - - if (STACK_REG_P (x)) - return; - - /* this is the size of op from size of operand */ - switch (GET_MODE_SIZE (GET_MODE (x))) - { - case 1: - PUT_OP_SIZE ('B', 'b', file); - return; - - case 2: - PUT_OP_SIZE ('W', 'w', file); - return; - - case 4: - if (GET_MODE (x) == SFmode) - { - PUT_OP_SIZE ('S', 's', file); - return; - } - else - PUT_OP_SIZE ('L', 'l', file); - return; - - case 8: - if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT) - { -#ifdef GAS_MNEMONICS - PUT_OP_SIZE ('Q', 'q', file); - return; -#else - PUT_OP_SIZE ('Q', 'l', file); /* Fall through */ -#endif - } - - PUT_OP_SIZE ('Q', 'l', file); - return; - } - - case 'b': - case 'w': - case 'k': - case 'h': - case 'y': - case 'P': - break; - - default: - { - char str[50]; - - sprintf (str, "invalid operand code `%c'", code); - output_operand_lossage (str); - } - } - } - if (GET_CODE (x) == REG) - { - PRINT_REG (x, code, file); - } - else if (GET_CODE (x) == MEM) - { - PRINT_PTR (x, file); - if (CONSTANT_ADDRESS_P (XEXP (x, 0))) - { - if (flag_pic) - output_pic_addr_const (file, XEXP (x, 0), code); - else - output_addr_const (file, XEXP (x, 0)); - } - else - output_address (XEXP (x, 0)); - } - else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == SFmode) - { - union { double d; int i[2]; } u; - union { float f; int i; } u1; - u.i[0] = CONST_DOUBLE_LOW (x); - u.i[1] = CONST_DOUBLE_HIGH (x); - u1.f = u.d; - PRINT_IMMED_PREFIX (file); - fprintf (file, "0x%x", u1.i); - } - else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == DFmode) - { - union { double d; int i[2]; } u; - u.i[0] = CONST_DOUBLE_LOW (x); - u.i[1] = CONST_DOUBLE_HIGH (x); - fprintf (file, "%.22e", u.d); - } - else - { - if (code != 'P') - { - if (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE) - PRINT_IMMED_PREFIX (file); - else if (GET_CODE (x) == CONST || GET_CODE (x) == SYMBOL_REF - || GET_CODE (x) == LABEL_REF) - PRINT_OFFSET_PREFIX (file); - } - if (flag_pic) - output_pic_addr_const (file, x, code); - else - output_addr_const (file, x); - } -} - -/* Print a memory operand whose address is ADDR. */ - -void -print_operand_address (file, addr) - FILE *file; - register rtx addr; -{ - register rtx reg1, reg2, breg, ireg; - rtx offset; - - switch (GET_CODE (addr)) - { - case REG: - ADDR_BEG (file); - fprintf (file, "%se", RP); - fputs (hi_reg_name[REGNO (addr)], file); - ADDR_END (file); - break; - - case PLUS: - reg1 = 0; - reg2 = 0; - ireg = 0; - breg = 0; - offset = 0; - if (CONSTANT_ADDRESS_P (XEXP (addr, 0))) - { - offset = XEXP (addr, 0); - addr = XEXP (addr, 1); - } - else if (CONSTANT_ADDRESS_P (XEXP (addr, 1))) - { - offset = XEXP (addr, 1); - addr = XEXP (addr, 0); - } - if (GET_CODE (addr) != PLUS) ; - else if (GET_CODE (XEXP (addr, 0)) == MULT) - { - reg1 = XEXP (addr, 0); - addr = XEXP (addr, 1); - } - else if (GET_CODE (XEXP (addr, 1)) == MULT) - { - reg1 = XEXP (addr, 1); - addr = XEXP (addr, 0); - } - else if (GET_CODE (XEXP (addr, 0)) == REG) - { - reg1 = XEXP (addr, 0); - addr = XEXP (addr, 1); - } - else if (GET_CODE (XEXP (addr, 1)) == REG) - { - reg1 = XEXP (addr, 1); - addr = XEXP (addr, 0); - } - if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT) - { - if (reg1 == 0) reg1 = addr; - else reg2 = addr; - addr = 0; - } - if (offset != 0) - { - if (addr != 0) abort (); - addr = offset; - } - if ((reg1 && GET_CODE (reg1) == MULT) - || (reg2 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg2)))) - { - breg = reg2; - ireg = reg1; - } - else if (reg1 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg1))) - { - breg = reg1; - ireg = reg2; - } - - if (ireg != 0 || breg != 0) - { - int scale = 1; - - if (addr != 0) - { - if (GET_CODE (addr) == LABEL_REF) - output_asm_label (addr); - else - { - if (flag_pic) - output_pic_addr_const (file, addr, 0); - else - output_addr_const (file, addr); - } - } - - if (ireg != 0 && GET_CODE (ireg) == MULT) - { - scale = INTVAL (XEXP (ireg, 1)); - ireg = XEXP (ireg, 0); - } - - /* The stack pointer can only appear as a base register, - never an index register, so exchange the regs if it is wrong. */ - - if (scale == 1 && ireg && REGNO (ireg) == STACK_POINTER_REGNUM) - { - rtx tmp; - - tmp = breg; - breg = ireg; - ireg = tmp; - } - - /* output breg+ireg*scale */ - PRINT_B_I_S (breg, ireg, scale, file); - break; - } - - case MULT: - { - int scale; - if (GET_CODE (XEXP (addr, 0)) == CONST_INT) - { - scale = INTVAL (XEXP (addr, 0)); - ireg = XEXP (addr, 1); - } - else - { - scale = INTVAL (XEXP (addr, 1)); - ireg = XEXP (addr, 0); - } - output_addr_const (file, const0_rtx); - PRINT_B_I_S ((rtx) 0, ireg, scale, file); - } - break; - - default: - if (GET_CODE (addr) == CONST_INT - && INTVAL (addr) < 0x8000 - && INTVAL (addr) >= -0x8000) - fprintf (file, "%d", INTVAL (addr)); - else - { - if (flag_pic) - output_pic_addr_const (file, addr, 0); - else - output_addr_const (file, addr); - } - } -} - -/* Set the cc_status for the results of an insn whose pattern is EXP. - On the 80386, we assume that only test and compare insns, as well - as SI, HI, & DI mode ADD, SUB, NEG, AND, IOR, XOR, ASHIFT, LSHIFT, - ASHIFTRT, and LSHIFTRT instructions set the condition codes usefully. - Also, we assume that jumps, moves and sCOND don't affect the condition - codes. All else clobbers the condition codes, by assumption. - - We assume that ALL integer add, minus, etc. instructions effect the - condition codes. This MUST be consistent with i386.md. - - We don't record any float test or compare - the redundant test & - compare check in final.c does not handle stack-like regs correctly. */ - -void -notice_update_cc (exp) - rtx exp; -{ - if (GET_CODE (exp) == SET) - { - /* Jumps do not alter the cc's. */ - if (SET_DEST (exp) == pc_rtx) - return; - /* Moving register or memory into a register: - it doesn't alter the cc's, but it might invalidate - the RTX's which we remember the cc's came from. - (Note that moving a constant 0 or 1 MAY set the cc's). */ - if (REG_P (SET_DEST (exp)) - && (REG_P (SET_SRC (exp)) || GET_CODE (SET_SRC (exp)) == MEM - || GET_RTX_CLASS (GET_CODE (SET_SRC (exp))) == '<')) - { - if (cc_status.value1 - && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1)) - cc_status.value1 = 0; - if (cc_status.value2 - && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2)) - cc_status.value2 = 0; - return; - } - /* Moving register into memory doesn't alter the cc's. - It may invalidate the RTX's which we remember the cc's came from. */ - if (GET_CODE (SET_DEST (exp)) == MEM - && (REG_P (SET_SRC (exp)) - || GET_RTX_CLASS (GET_CODE (SET_SRC (exp))) == '<')) - { - if (cc_status.value1 && GET_CODE (cc_status.value1) == MEM) - cc_status.value1 = 0; - if (cc_status.value2 && GET_CODE (cc_status.value2) == MEM) - cc_status.value2 = 0; - return; - } - /* Function calls clobber the cc's. */ - else if (GET_CODE (SET_SRC (exp)) == CALL) - { - CC_STATUS_INIT; - return; - } - /* Tests and compares set the cc's in predictable ways. */ - else if (SET_DEST (exp) == cc0_rtx) - { - CC_STATUS_INIT; - cc_status.value1 = SET_SRC (exp); - return; - } - /* Certain instructions effect the condition codes. */ - else if (GET_MODE (SET_SRC (exp)) == SImode - || GET_MODE (SET_SRC (exp)) == HImode - || GET_MODE (SET_SRC (exp)) == QImode) - switch (GET_CODE (SET_SRC (exp))) - { - case ASHIFTRT: case LSHIFTRT: - case ASHIFT: case LSHIFT: - /* Shifts on the 386 don't set the condition codes if the - shift count is zero. */ - if (GET_CODE (XEXP (SET_SRC (exp), 1)) != CONST_INT) - { - CC_STATUS_INIT; - break; - } - /* We assume that the CONST_INT is non-zero (this rtx would - have been deleted if it were zero. */ - - case PLUS: case MINUS: case NEG: - case AND: case IOR: case XOR: - cc_status.flags = CC_NO_OVERFLOW; - cc_status.value1 = SET_SRC (exp); - cc_status.value2 = SET_DEST (exp); - break; - - default: - CC_STATUS_INIT; - } - else - { - CC_STATUS_INIT; - } - } - else if (GET_CODE (exp) == PARALLEL - && GET_CODE (XVECEXP (exp, 0, 0)) == SET) - { - if (SET_DEST (XVECEXP (exp, 0, 0)) == pc_rtx) - return; - if (SET_DEST (XVECEXP (exp, 0, 0)) == cc0_rtx) - { - CC_STATUS_INIT; - if (stack_regs_mentioned_p (SET_SRC (XVECEXP (exp, 0, 0)))) - cc_status.flags |= CC_IN_80387; - else - cc_status.value1 = SET_SRC (XVECEXP (exp, 0, 0)); - return; - } - CC_STATUS_INIT; - } - else - { - CC_STATUS_INIT; - } -} - -/* Split one or more DImode RTL references into pairs of SImode - references. The RTL can be REG, offsettable MEM, integer constant, or - CONST_DOUBLE. "operands" is a pointer to an array of DImode RTL to - split and "num" is its length. lo_half and hi_half are output arrays - that parallel "operands". */ - -void -split_di (operands, num, lo_half, hi_half) - rtx operands[]; - int num; - rtx lo_half[], hi_half[]; -{ - while (num--) - { - if (GET_CODE (operands[num]) == REG) - { - lo_half[num] = gen_rtx (REG, SImode, REGNO (operands[num])); - hi_half[num] = gen_rtx (REG, SImode, REGNO (operands[num]) + 1); - } - else if (CONSTANT_P (operands[num])) - { - split_double (operands[num], &lo_half[num], &hi_half[num]); - } - else if (offsettable_memref_p (operands[num])) - { - lo_half[num] = operands[num]; - hi_half[num] = adj_offsettable_operand (operands[num], 4); - } - else - abort(); - } -} - -/* Return 1 if this is a valid binary operation on a 387. - OP is the expression matched, and MODE is its mode. */ - -int -binary_387_op (op, mode) - register rtx op; - enum machine_mode mode; -{ - if (mode != VOIDmode && mode != GET_MODE (op)) - return 0; - - switch (GET_CODE (op)) - { - case PLUS: - case MINUS: - case MULT: - case DIV: - return GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT; - - default: - return 0; - } -} - -/* Return 1 if this is a valid conversion operation on a 387. - OP is the expression matched, and MODE is its mode. */ - -int -convert_387_op (op, mode) - register rtx op; - enum machine_mode mode; -{ - if (mode != VOIDmode && mode != GET_MODE (op)) - return 0; - - switch (GET_CODE (op)) - { - case FLOAT: - return GET_MODE (XEXP (op, 0)) == SImode; - - case FLOAT_EXTEND: - return mode == DFmode && GET_MODE (XEXP (op, 0)) == SFmode; - - default: - return 0; - } -} - -/* Return 1 if this is a valid shift or rotate operation on a 386. - OP is the expression matched, and MODE is its mode. */ - -int -shift_op (op, mode) - register rtx op; - enum machine_mode mode; -{ - rtx operand = XEXP (op, 0); - - if (mode != VOIDmode && mode != GET_MODE (op)) - return 0; - - if (GET_MODE (operand) != GET_MODE (op) - || GET_MODE_CLASS (GET_MODE (op)) != MODE_INT) - return 0; - - return (GET_CODE (op) == ASHIFT - || GET_CODE (op) == ASHIFTRT - || GET_CODE (op) == LSHIFTRT - || GET_CODE (op) == ROTATE - || GET_CODE (op) == ROTATERT); -} - -/* Return 1 if OP is COMPARE rtx with mode VOIDmode. - MODE is not used. */ - -int -VOIDmode_compare_op (op, mode) - register rtx op; - enum machine_mode mode; -{ - return GET_CODE (op) == COMPARE && GET_MODE (op) == VOIDmode; -} - -/* Output code to perform a 387 binary operation in INSN, one of PLUS, - MINUS, MULT or DIV. OPERANDS are the insn operands, where operands[3] - is the expression of the binary operation. The output may either be - emitted here, or returned to the caller, like all output_* functions. - - There is no guarantee that the operands are the same mode, as they - might be within FLOAT or FLOAT_EXTEND expressions. */ - -char * -output_387_binary_op (insn, operands) - rtx insn; - rtx *operands; -{ - rtx temp; - char *base_op; - static char buf[100]; - - switch (GET_CODE (operands[3])) - { - case PLUS: - if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT - || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT) - base_op = "fiadd"; - else - base_op = "fadd"; - break; - - case MINUS: - if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT - || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT) - base_op = "fisub"; - else - base_op = "fsub"; - break; - - case MULT: - if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT - || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT) - base_op = "fimul"; - else - base_op = "fmul"; - break; - - case DIV: - if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT - || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT) - base_op = "fidiv"; - else - base_op = "fdiv"; - break; - - default: - abort (); - } - - strcpy (buf, base_op); - - switch (GET_CODE (operands[3])) - { - case MULT: - case PLUS: - if (REG_P (operands[2]) && REGNO (operands[0]) == REGNO (operands[2])) - { - temp = operands[2]; - operands[2] = operands[1]; - operands[1] = temp; - } - - if (GET_CODE (operands[2]) == MEM) - return strcat (buf, AS1 (%z2,%2)); - - if (NON_STACK_REG_P (operands[1])) - { - output_op_from_reg (operands[1], strcat (buf, AS1 (%z0,%1))); - RET; - } - else if (NON_STACK_REG_P (operands[2])) - { - output_op_from_reg (operands[2], strcat (buf, AS1 (%z0,%1))); - RET; - } - - if (find_regno_note (insn, REG_DEAD, REGNO (operands[2]))) - return strcat (buf, AS2 (p,%2,%0)); - - if (STACK_TOP_P (operands[0])) - return strcat (buf, AS2 (,%y2,%0)); - else - return strcat (buf, AS2 (,%2,%0)); - - case MINUS: - case DIV: - if (GET_CODE (operands[1]) == MEM) - return strcat (buf, AS1 (r%z1,%1)); - - if (GET_CODE (operands[2]) == MEM) - return strcat (buf, AS1 (%z2,%2)); - - if (NON_STACK_REG_P (operands[1])) - { - output_op_from_reg (operands[1], strcat (buf, AS1 (r%z0,%1))); - RET; - } - else if (NON_STACK_REG_P (operands[2])) - { - output_op_from_reg (operands[2], strcat (buf, AS1 (%z0,%1))); - RET; - } - - if (! STACK_REG_P (operands[1]) || ! STACK_REG_P (operands[2])) - abort (); - - if (find_regno_note (insn, REG_DEAD, REGNO (operands[2]))) - return strcat (buf, AS2 (rp,%2,%0)); - - if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) - return strcat (buf, AS2 (p,%1,%0)); - - if (STACK_TOP_P (operands[0])) - { - if (STACK_TOP_P (operands[1])) - return strcat (buf, AS2 (,%y2,%0)); - else - return strcat (buf, AS2 (r,%y1,%0)); - } - else if (STACK_TOP_P (operands[1])) - return strcat (buf, AS2 (,%1,%0)); - else - return strcat (buf, AS2 (r,%2,%0)); - - default: - abort (); - } -} - -/* Output code for INSN to convert a float to a signed int. OPERANDS - are the insn operands. The output may be SFmode or DFmode and the - input operand may be SImode or DImode. As a special case, make sure - that the 387 stack top dies if the output mode is DImode, because the - hardware requires this. */ - -char * -output_fix_trunc (insn, operands) - rtx insn; - rtx *operands; -{ - int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0; - rtx xops[2]; - - if (! STACK_TOP_P (operands[1]) || - (GET_MODE (operands[0]) == DImode && ! stack_top_dies)) - abort (); - - xops[0] = GEN_INT (12); - xops[1] = operands[4]; - - output_asm_insn (AS1 (fnstc%W2,%2), operands); - output_asm_insn (AS2 (mov%L2,%2,%4), operands); - output_asm_insn (AS2 (mov%B1,%0,%h1), xops); - output_asm_insn (AS2 (mov%L4,%4,%3), operands); - output_asm_insn (AS1 (fldc%W3,%3), operands); - - if (NON_STACK_REG_P (operands[0])) - output_to_reg (operands[0], stack_top_dies); - else if (GET_CODE (operands[0]) == MEM) - { - if (stack_top_dies) - output_asm_insn (AS1 (fistp%z0,%0), operands); - else - output_asm_insn (AS1 (fist%z0,%0), operands); - } - else - abort (); - - return AS1 (fldc%W2,%2); -} - -/* Output code for INSN to compare OPERANDS. The two operands might - not have the same mode: one might be within a FLOAT or FLOAT_EXTEND - expression. If the compare is in mode CCFPEQmode, use an opcode that - will not fault if a qNaN is present. */ - -char * -output_float_compare (insn, operands) - rtx insn; - rtx *operands; -{ - int stack_top_dies; - rtx body = XVECEXP (PATTERN (insn), 0, 0); - int unordered_compare = GET_MODE (SET_SRC (body)) == CCFPEQmode; - - if (! STACK_TOP_P (operands[0])) - abort (); - - stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0; - - if (STACK_REG_P (operands[1]) - && stack_top_dies - && find_regno_note (insn, REG_DEAD, REGNO (operands[1])) - && REGNO (operands[1]) != FIRST_STACK_REG) - { - /* If both the top of the 387 stack dies, and the other operand - is also a stack register that dies, then this must be a - `fcompp' float compare */ - - if (unordered_compare) - output_asm_insn ("fucompp", operands); - else - output_asm_insn ("fcompp", operands); - } - else - { - static char buf[100]; - - /* Decide if this is the integer or float compare opcode, or the - unordered float compare. */ - - if (unordered_compare) - strcpy (buf, "fucom"); - else if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_FLOAT) - strcpy (buf, "fcom"); - else - strcpy (buf, "ficom"); - - /* Modify the opcode if the 387 stack is to be popped. */ - - if (stack_top_dies) - strcat (buf, "p"); - - if (NON_STACK_REG_P (operands[1])) - output_op_from_reg (operands[1], strcat (buf, AS1 (%z0,%1))); - else - output_asm_insn (strcat (buf, AS1 (%z1,%y1)), operands); - } - - /* Now retrieve the condition code. */ - - return output_fp_cc0_set (insn); -} - -/* Output opcodes to transfer the results of FP compare or test INSN - from the FPU to the CPU flags. If TARGET_IEEE_FP, ensure that if the - result of the compare or test is unordered, no comparison operator - succeeds except NE. Return an output template, if any. */ - -char * -output_fp_cc0_set (insn) - rtx insn; -{ - rtx xops[3]; - rtx unordered_label; - rtx next; - enum rtx_code code; - - xops[0] = gen_rtx (REG, HImode, 0); - output_asm_insn (AS1 (fnsts%W0,%0), xops); - - if (! TARGET_IEEE_FP) - return "sahf"; - - next = next_cc0_user (insn); - if (next == NULL_RTX) - abort (); - - if (GET_CODE (next) == JUMP_INSN - && GET_CODE (PATTERN (next)) == SET - && SET_DEST (PATTERN (next)) == pc_rtx - && GET_CODE (SET_SRC (PATTERN (next))) == IF_THEN_ELSE) - { - code = GET_CODE (XEXP (SET_SRC (PATTERN (next)), 0)); - } - else if (GET_CODE (PATTERN (next)) == SET) - { - code = GET_CODE (SET_SRC (PATTERN (next))); - } - else - abort (); - - xops[0] = gen_rtx (REG, QImode, 0); - - switch (code) - { - case GT: - xops[1] = GEN_INT (0x45); - output_asm_insn (AS2 (and%B0,%1,%h0), xops); - /* je label */ - break; - - case LT: - xops[1] = GEN_INT (0x45); - xops[2] = GEN_INT (0x01); - output_asm_insn (AS2 (and%B0,%1,%h0), xops); - output_asm_insn (AS2 (cmp%B0,%2,%h0), xops); - /* je label */ - break; - - case GE: - xops[1] = GEN_INT (0x05); - output_asm_insn (AS2 (and%B0,%1,%h0), xops); - /* je label */ - break; - - case LE: - xops[1] = GEN_INT (0x45); - xops[2] = GEN_INT (0x40); - output_asm_insn (AS2 (and%B0,%1,%h0), xops); - output_asm_insn (AS1 (dec%B0,%h0), xops); - output_asm_insn (AS2 (cmp%B0,%2,%h0), xops); - /* jb label */ - break; - - case EQ: - xops[1] = GEN_INT (0x45); - xops[2] = GEN_INT (0x40); - output_asm_insn (AS2 (and%B0,%1,%h0), xops); - output_asm_insn (AS2 (cmp%B0,%2,%h0), xops); - /* je label */ - break; - - case NE: - xops[1] = GEN_INT (0x44); - xops[2] = GEN_INT (0x40); - output_asm_insn (AS2 (and%B0,%1,%h0), xops); - output_asm_insn (AS2 (xor%B0,%2,%h0), xops); - /* jne label */ - break; - - case GTU: - case LTU: - case GEU: - case LEU: - default: - abort (); - } - RET; -} - -#define MAX_386_STACK_LOCALS 2 - -static rtx i386_stack_locals[(int) MAX_MACHINE_MODE][MAX_386_STACK_LOCALS]; - -/* Clear stack slot assignments remembered from previous functions. - This is called from INIT_EXPANDERS once before RTL is emitted for each - function. */ - -void -clear_386_stack_locals () -{ - enum machine_mode mode; - int n; - - for (mode = VOIDmode; (int) mode < (int) MAX_MACHINE_MODE; - mode = (enum machine_mode) ((int) mode + 1)) - for (n = 0; n < MAX_386_STACK_LOCALS; n++) - i386_stack_locals[(int) mode][n] = NULL_RTX; -} - -/* Return a MEM corresponding to a stack slot with mode MODE. - Allocate a new slot if necessary. - - The RTL for a function can have several slots available: N is - which slot to use. */ - -rtx -assign_386_stack_local (mode, n) - enum machine_mode mode; - int n; -{ - if (n < 0 || n >= MAX_386_STACK_LOCALS) - abort (); - - if (i386_stack_locals[(int) mode][n] == NULL_RTX) - i386_stack_locals[(int) mode][n] - = assign_stack_local (mode, GET_MODE_SIZE (mode), 0); - - return i386_stack_locals[(int) mode][n]; -} diff --git a/gnu/usr.bin/cc/common/c-common.c b/gnu/usr.bin/cc/common/c-common.c deleted file mode 100644 index b36a6aca51..0000000000 --- a/gnu/usr.bin/cc/common/c-common.c +++ /dev/null @@ -1,1219 +0,0 @@ -/* Subroutines shared by all languages that are variants of C. - Copyright (C) 1992 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include "config.h" -#include "tree.h" -#include "c-lex.h" -#include "c-tree.h" -#include "flags.h" -#include "obstack.h" -#include - -extern struct obstack permanent_obstack; - -/* Make bindings for __FUNCTION__ and __PRETTY_FUNCTION__. */ - -void -declare_function_name () -{ - tree decl, type, init; - char *name, *printable_name; - int len; - - if (current_function_decl == NULL) - { - name = ""; - printable_name = "top level"; - } - else - { - char *kind = "function"; - if (TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE) - kind = "method"; - /* Allow functions to be nameless (such as artificial ones). */ - if (DECL_NAME (current_function_decl)) - name = IDENTIFIER_POINTER (DECL_NAME (current_function_decl)); - else - name = ""; - printable_name = (*decl_printable_name) (current_function_decl, &kind); - } - - /* If the default size of char arrays isn't big enough for the name, - make a bigger one. */ - len = strlen (name) + 1; - type = char_array_type_node; - if (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TREE_TYPE (char_array_type_node))) - < len) - type = build_array_type (char_type_node, - build_index_type (build_int_2 (len, 0))); - - push_obstacks_nochange (); - decl = build_decl (VAR_DECL, get_identifier ("__FUNCTION__"), type); - TREE_STATIC (decl) = 1; - TREE_READONLY (decl) = 1; - DECL_SOURCE_LINE (decl) = 0; - DECL_IN_SYSTEM_HEADER (decl) = 1; - DECL_IGNORED_P (decl) = 1; - init = build_string (len, name); - TREE_TYPE (init) = type; - DECL_INITIAL (decl) = init; - finish_decl (pushdecl (decl), init, NULL_TREE); - - len = strlen (printable_name) + 1; - type = char_array_type_node; - if (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TREE_TYPE (char_array_type_node))) - < len) - type = build_array_type (char_type_node, - build_index_type (build_int_2 (len, 0))); - - push_obstacks_nochange (); - decl = build_decl (VAR_DECL, get_identifier ("__PRETTY_FUNCTION__"), type); - TREE_STATIC (decl) = 1; - TREE_READONLY (decl) = 1; - DECL_SOURCE_LINE (decl) = 0; - DECL_IN_SYSTEM_HEADER (decl) = 1; - DECL_IGNORED_P (decl) = 1; - init = build_string (len, printable_name); - TREE_TYPE (init) = type; - DECL_INITIAL (decl) = init; - finish_decl (pushdecl (decl), init, NULL_TREE); -} - -/* Given a chain of STRING_CST nodes, - concatenate them into one STRING_CST - and give it a suitable array-of-chars data type. */ - -tree -combine_strings (strings) - tree strings; -{ - register tree value, t; - register int length = 1; - int wide_length = 0; - int wide_flag = 0; - int wchar_bytes = TYPE_PRECISION (wchar_type_node) / BITS_PER_UNIT; - int nchars; - - if (TREE_CHAIN (strings)) - { - /* More than one in the chain, so concatenate. */ - register char *p, *q; - - /* Don't include the \0 at the end of each substring, - except for the last one. - Count wide strings and ordinary strings separately. */ - for (t = strings; t; t = TREE_CHAIN (t)) - { - if (TREE_TYPE (t) == wchar_array_type_node) - { - wide_length += (TREE_STRING_LENGTH (t) - wchar_bytes); - wide_flag = 1; - } - else - length += (TREE_STRING_LENGTH (t) - 1); - } - - /* If anything is wide, the non-wides will be converted, - which makes them take more space. */ - if (wide_flag) - length = length * wchar_bytes + wide_length; - - p = savealloc (length); - - /* Copy the individual strings into the new combined string. - If the combined string is wide, convert the chars to ints - for any individual strings that are not wide. */ - - q = p; - for (t = strings; t; t = TREE_CHAIN (t)) - { - int len = (TREE_STRING_LENGTH (t) - - ((TREE_TYPE (t) == wchar_array_type_node) - ? wchar_bytes : 1)); - if ((TREE_TYPE (t) == wchar_array_type_node) == wide_flag) - { - bcopy (TREE_STRING_POINTER (t), q, len); - q += len; - } - else - { - int i; - for (i = 0; i < len; i++) - ((int *) q)[i] = TREE_STRING_POINTER (t)[i]; - q += len * wchar_bytes; - } - } - if (wide_flag) - { - int i; - for (i = 0; i < wchar_bytes; i++) - *q++ = 0; - } - else - *q = 0; - - value = make_node (STRING_CST); - TREE_STRING_POINTER (value) = p; - TREE_STRING_LENGTH (value) = length; - TREE_CONSTANT (value) = 1; - } - else - { - value = strings; - length = TREE_STRING_LENGTH (value); - if (TREE_TYPE (value) == wchar_array_type_node) - wide_flag = 1; - } - - /* Compute the number of elements, for the array type. */ - nchars = wide_flag ? length / wchar_bytes : length; - - /* Create the array type for the string constant. - -Wwrite-strings says make the string constant an array of const char - so that copying it to a non-const pointer will get a warning. */ - if (warn_write_strings - && (! flag_traditional && ! flag_writable_strings)) - { - tree elements - = build_type_variant (wide_flag ? wchar_type_node : char_type_node, - 1, 0); - TREE_TYPE (value) - = build_array_type (elements, - build_index_type (build_int_2 (nchars - 1, 0))); - } - else - TREE_TYPE (value) - = build_array_type (wide_flag ? wchar_type_node : char_type_node, - build_index_type (build_int_2 (nchars - 1, 0))); - TREE_CONSTANT (value) = 1; - TREE_STATIC (value) = 1; - return value; -} - -/* Process the attributes listed in ATTRIBUTES - and install them in DECL. */ - -void -decl_attributes (decl, attributes) - tree decl, attributes; -{ - tree a; - for (a = attributes; a; a = TREE_CHAIN (a)) - if (TREE_VALUE (a) == get_identifier ("packed")) - { - if (TREE_CODE (decl) == FIELD_DECL) - DECL_PACKED (decl) = 1; - /* We can't set DECL_PACKED for a VAR_DECL, because the bit is - used for DECL_REGISTER. It wouldn't mean anything anyway. */ - } - else if (TREE_VALUE (a) != 0 - && TREE_CODE (TREE_VALUE (a)) == TREE_LIST - && TREE_PURPOSE (TREE_VALUE (a)) == get_identifier ("mode")) - { - int i; - char *specified_name - = IDENTIFIER_POINTER (TREE_VALUE (TREE_VALUE (a))); - - /* Give this decl a type with the specified mode. */ - for (i = 0; i < NUM_MACHINE_MODES; i++) - if (!strcmp (specified_name, GET_MODE_NAME (i))) - { - tree type - = type_for_mode (i, TREE_UNSIGNED (TREE_TYPE (decl))); - if (type != 0) - { - TREE_TYPE (decl) = type; - DECL_SIZE (decl) = 0; - layout_decl (decl, 0); - } - else - error ("no data type for mode `%s'", specified_name); - break; - } - if (i == NUM_MACHINE_MODES) - error ("unknown machine mode `%s'", specified_name); - } - else if (TREE_VALUE (a) != 0 - && TREE_CODE (TREE_VALUE (a)) == TREE_LIST - && TREE_PURPOSE (TREE_VALUE (a)) == get_identifier ("aligned")) - { - int align = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (a))) - * BITS_PER_UNIT; - - if (exact_log2 (align) == -1) - error_with_decl (decl, - "requested alignment of `%s' is not a power of 2"); - else if (TREE_CODE (decl) != VAR_DECL - && TREE_CODE (decl) != FIELD_DECL) - error_with_decl (decl, - "alignment specified for `%s'"); - else - DECL_ALIGN (decl) = align; - } - else if (TREE_VALUE (a) != 0 - && TREE_CODE (TREE_VALUE (a)) == TREE_LIST - && TREE_PURPOSE (TREE_VALUE (a)) == get_identifier ("format")) - { - tree list = TREE_VALUE (TREE_VALUE (a)); - tree format_type = TREE_PURPOSE (list); - int format_num = TREE_INT_CST_LOW (TREE_PURPOSE (TREE_VALUE (list))); - int first_arg_num = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (list))); - int is_scan; - tree argument; - int arg_num; - - if (TREE_CODE (decl) != FUNCTION_DECL) - { - error_with_decl (decl, - "argument format specified for non-function `%s'"); - return; - } - - if (format_type == get_identifier ("printf")) - is_scan = 0; - else if (format_type == get_identifier ("scanf")) - is_scan = 1; - else - { - error_with_decl (decl, "unrecognized format specifier for `%s'"); - return; - } - - if (first_arg_num != 0 && first_arg_num <= format_num) - { - error_with_decl (decl, - "format string arg follows the args to be formatted, for `%s'"); - return; - } - - /* Verify that the format_num argument is actually a string, in case - the format attribute is in error. */ - argument = TYPE_ARG_TYPES (TREE_TYPE (decl)); - for (arg_num = 1; ; ++arg_num) - { - if (argument == 0 || arg_num == format_num) - break; - argument = TREE_CHAIN (argument); - } - if (! argument - || TREE_CODE (TREE_VALUE (argument)) != POINTER_TYPE - || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (argument))) - != char_type_node)) - { - error_with_decl (decl, - "format string arg not a string type, for `%s'"); - return; - } - /* Verify that first_arg_num points to the last argument, the ... */ - while (argument) - arg_num++, argument = TREE_CHAIN (argument); - if (arg_num != first_arg_num) - { - error_with_decl (decl, - "args to be formatted is not ..., for `%s'"); - return; - } - - record_format_info (DECL_NAME (decl), is_scan, format_num, - first_arg_num); - } -} - -/* Print a warning if a constant expression had overflow in folding. - Invoke this function on every expression that the language - requires to be a constant expression. - Note the ANSI C standard says it is erroneous for a - constant expression to overflow. */ - -void -constant_expression_warning (value) - tree value; -{ - if (TREE_CODE (value) == INTEGER_CST && TREE_CONSTANT_OVERFLOW (value)) - { - /* ??? This is a warning, not a pedwarn, in 2.4, - because it happens in contexts that are not - "constant expressions" in ANSI C. - Fix the problem differently in 2.5. */ - warning ("overflow in constant expression"); - /* Suppress duplicate warnings. */ - TREE_CONSTANT_OVERFLOW (value) = 0; - } -} - -/* Print a warning if an expression had overflow in folding. - Invoke this function on every expression that - (1) appears in the source code, and - (2) might be a constant expression that overflowed, and - (3) is not already checked by convert_and_check; - however, do not invoke this function on operands of explicit casts. */ - -void -overflow_warning (value) - tree value; -{ - if (TREE_CODE (value) == INTEGER_CST && TREE_CONSTANT_OVERFLOW (value)) - { - /* ??? This is a warning, not a pedwarn, in 2.4, - because it happens in contexts that are not - "constant expressions" in ANSI C. - Fix the problem differently in 2.5. */ - warning ("integer overflow in expression"); - TREE_CONSTANT_OVERFLOW (value) = 0; - } -} - -/* Print a warning if a large constant is truncated to unsigned, - or if -Wconversion is used and a constant < 0 is converted to unsigned. - Invoke this function on every expression that might be implicitly - converted to an unsigned type. */ - -void -unsigned_conversion_warning (result, operand) - tree result, operand; -{ - if (TREE_CODE (operand) == INTEGER_CST - && TREE_CODE (TREE_TYPE (result)) == INTEGER_TYPE - && TREE_UNSIGNED (TREE_TYPE (result)) - && !int_fits_type_p (operand, TREE_TYPE (result))) - { - if (!int_fits_type_p (operand, signed_type (TREE_TYPE (result)))) - /* This detects cases like converting -129 or 256 to unsigned char. */ - pedwarn ("large integer implicitly truncated to unsigned type"); - else if (warn_conversion) - pedwarn ("negative integer implicitly converted to unsigned type"); - } -} - -/* Convert EXPR to TYPE, warning about conversion problems with constants. - Invoke this function on every expression that is converted implicitly, - i.e. because of language rules and not because of an explicit cast. */ - -tree -convert_and_check (type, expr) - tree type, expr; -{ - tree t = convert (type, expr); - if (TREE_CODE (t) == INTEGER_CST) - { - if (TREE_UNSIGNED (TREE_TYPE (expr)) - && !TREE_UNSIGNED (type) - && TREE_CODE (TREE_TYPE (expr)) == INTEGER_TYPE - && TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (expr))) - /* No warning for converting 0x80000000 to int. */ - TREE_CONSTANT_OVERFLOW (t) = 0; - else if (TREE_CONSTANT_OVERFLOW (t)) - { - /* ??? This is a warning, not a pedwarn, in 2.4, - because it happens in contexts that are not - "constant expressions" in ANSI C. - Fix the problem differently in 2.5. */ - warning ("overflow in implicit constant conversion"); - TREE_CONSTANT_OVERFLOW (t) = 0; - } - else - unsigned_conversion_warning (t, expr); - } - return t; -} - -void -c_expand_expr_stmt (expr) - tree expr; -{ - /* Do default conversion if safe and possibly important, - in case within ({...}). */ - if ((TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE && lvalue_p (expr)) - || TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE) - expr = default_conversion (expr); - - if (TREE_TYPE (expr) != error_mark_node - && TYPE_SIZE (TREE_TYPE (expr)) == 0 - && TREE_CODE (TREE_TYPE (expr)) != ARRAY_TYPE) - error ("expression statement has incomplete type"); - - expand_expr_stmt (expr); -} - -/* Validate the expression after `case' and apply default promotions. */ - -tree -check_case_value (value) - tree value; -{ - if (value == NULL_TREE) - return value; - - /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */ - STRIP_TYPE_NOPS (value); - - if (TREE_CODE (value) != INTEGER_CST - && value != error_mark_node) - { - error ("case label does not reduce to an integer constant"); - value = error_mark_node; - } - else - /* Promote char or short to int. */ - value = default_conversion (value); - - constant_expression_warning (value); - - return value; -} - -/* Return an integer type with BITS bits of precision, - that is unsigned if UNSIGNEDP is nonzero, otherwise signed. */ - -tree -type_for_size (bits, unsignedp) - unsigned bits; - int unsignedp; -{ - if (bits == TYPE_PRECISION (signed_char_type_node)) - return unsignedp ? unsigned_char_type_node : signed_char_type_node; - - if (bits == TYPE_PRECISION (short_integer_type_node)) - return unsignedp ? short_unsigned_type_node : short_integer_type_node; - - if (bits == TYPE_PRECISION (integer_type_node)) - return unsignedp ? unsigned_type_node : integer_type_node; - - if (bits == TYPE_PRECISION (long_integer_type_node)) - return unsignedp ? long_unsigned_type_node : long_integer_type_node; - - if (bits == TYPE_PRECISION (long_long_integer_type_node)) - return (unsignedp ? long_long_unsigned_type_node - : long_long_integer_type_node); - - if (bits <= TYPE_PRECISION (intQI_type_node)) - return unsignedp ? unsigned_intQI_type_node : intQI_type_node; - - if (bits <= TYPE_PRECISION (intHI_type_node)) - return unsignedp ? unsigned_intHI_type_node : intHI_type_node; - - if (bits <= TYPE_PRECISION (intSI_type_node)) - return unsignedp ? unsigned_intSI_type_node : intSI_type_node; - - if (bits <= TYPE_PRECISION (intDI_type_node)) - return unsignedp ? unsigned_intDI_type_node : intDI_type_node; - - return 0; -} - -/* Return a data type that has machine mode MODE. - If the mode is an integer, - then UNSIGNEDP selects between signed and unsigned types. */ - -tree -type_for_mode (mode, unsignedp) - enum machine_mode mode; - int unsignedp; -{ - if (mode == TYPE_MODE (signed_char_type_node)) - return unsignedp ? unsigned_char_type_node : signed_char_type_node; - - if (mode == TYPE_MODE (short_integer_type_node)) - return unsignedp ? short_unsigned_type_node : short_integer_type_node; - - if (mode == TYPE_MODE (integer_type_node)) - return unsignedp ? unsigned_type_node : integer_type_node; - - if (mode == TYPE_MODE (long_integer_type_node)) - return unsignedp ? long_unsigned_type_node : long_integer_type_node; - - if (mode == TYPE_MODE (long_long_integer_type_node)) - return unsignedp ? long_long_unsigned_type_node : long_long_integer_type_node; - - if (mode == TYPE_MODE (intQI_type_node)) - return unsignedp ? unsigned_intQI_type_node : intQI_type_node; - - if (mode == TYPE_MODE (intHI_type_node)) - return unsignedp ? unsigned_intHI_type_node : intHI_type_node; - - if (mode == TYPE_MODE (intSI_type_node)) - return unsignedp ? unsigned_intSI_type_node : intSI_type_node; - - if (mode == TYPE_MODE (intDI_type_node)) - return unsignedp ? unsigned_intDI_type_node : intDI_type_node; - - if (mode == TYPE_MODE (float_type_node)) - return float_type_node; - - if (mode == TYPE_MODE (double_type_node)) - return double_type_node; - - if (mode == TYPE_MODE (long_double_type_node)) - return long_double_type_node; - - if (mode == TYPE_MODE (build_pointer_type (char_type_node))) - return build_pointer_type (char_type_node); - - if (mode == TYPE_MODE (build_pointer_type (integer_type_node))) - return build_pointer_type (integer_type_node); - - return 0; -} - -/* Print an error message for invalid operands to arith operation CODE. - NOP_EXPR is used as a special case (see truthvalue_conversion). */ - -void -binary_op_error (code) - enum tree_code code; -{ - register char *opname; - switch (code) - { - case NOP_EXPR: - error ("invalid truth-value expression"); - return; - - case PLUS_EXPR: - opname = "+"; break; - case MINUS_EXPR: - opname = "-"; break; - case MULT_EXPR: - opname = "*"; break; - case MAX_EXPR: - opname = "max"; break; - case MIN_EXPR: - opname = "min"; break; - case EQ_EXPR: - opname = "=="; break; - case NE_EXPR: - opname = "!="; break; - case LE_EXPR: - opname = "<="; break; - case GE_EXPR: - opname = ">="; break; - case LT_EXPR: - opname = "<"; break; - case GT_EXPR: - opname = ">"; break; - case LSHIFT_EXPR: - opname = "<<"; break; - case RSHIFT_EXPR: - opname = ">>"; break; - case TRUNC_MOD_EXPR: - case FLOOR_MOD_EXPR: - opname = "%"; break; - case TRUNC_DIV_EXPR: - case FLOOR_DIV_EXPR: - opname = "/"; break; - case BIT_AND_EXPR: - opname = "&"; break; - case BIT_IOR_EXPR: - opname = "|"; break; - case TRUTH_ANDIF_EXPR: - opname = "&&"; break; - case TRUTH_ORIF_EXPR: - opname = "||"; break; - case BIT_XOR_EXPR: - opname = "^"; break; - case LROTATE_EXPR: - case RROTATE_EXPR: - opname = "rotate"; break; - } - error ("invalid operands to binary %s", opname); -} - -/* Subroutine of build_binary_op, used for comparison operations. - See if the operands have both been converted from subword integer types - and, if so, perhaps change them both back to their original type. - - The arguments of this function are all pointers to local variables - of build_binary_op: OP0_PTR is &OP0, OP1_PTR is &OP1, - RESTYPE_PTR is &RESULT_TYPE and RESCODE_PTR is &RESULTCODE. - - If this function returns nonzero, it means that the comparison has - a constant value. What this function returns is an expression for - that value. */ - -tree -shorten_compare (op0_ptr, op1_ptr, restype_ptr, rescode_ptr) - tree *op0_ptr, *op1_ptr; - tree *restype_ptr; - enum tree_code *rescode_ptr; -{ - register tree type; - tree op0 = *op0_ptr; - tree op1 = *op1_ptr; - int unsignedp0, unsignedp1; - int real1, real2; - tree primop0, primop1; - enum tree_code code = *rescode_ptr; - - /* Throw away any conversions to wider types - already present in the operands. */ - - primop0 = get_narrower (op0, &unsignedp0); - primop1 = get_narrower (op1, &unsignedp1); - - /* Handle the case that OP0 does not *contain* a conversion - but it *requires* conversion to FINAL_TYPE. */ - - if (op0 == primop0 && TREE_TYPE (op0) != *restype_ptr) - unsignedp0 = TREE_UNSIGNED (TREE_TYPE (op0)); - if (op1 == primop1 && TREE_TYPE (op1) != *restype_ptr) - unsignedp1 = TREE_UNSIGNED (TREE_TYPE (op1)); - - /* If one of the operands must be floated, we cannot optimize. */ - real1 = TREE_CODE (TREE_TYPE (primop0)) == REAL_TYPE; - real2 = TREE_CODE (TREE_TYPE (primop1)) == REAL_TYPE; - - /* If first arg is constant, swap the args (changing operation - so value is preserved), for canonicalization. */ - - if (TREE_CONSTANT (primop0)) - { - register tree tem = primop0; - register int temi = unsignedp0; - primop0 = primop1; - primop1 = tem; - tem = op0; - op0 = op1; - op1 = tem; - *op0_ptr = op0; - *op1_ptr = op1; - unsignedp0 = unsignedp1; - unsignedp1 = temi; - temi = real1; - real1 = real2; - real2 = temi; - - switch (code) - { - case LT_EXPR: - code = GT_EXPR; - break; - case GT_EXPR: - code = LT_EXPR; - break; - case LE_EXPR: - code = GE_EXPR; - break; - case GE_EXPR: - code = LE_EXPR; - break; - } - *rescode_ptr = code; - } - - /* If comparing an integer against a constant more bits wide, - maybe we can deduce a value of 1 or 0 independent of the data. - Or else truncate the constant now - rather than extend the variable at run time. - - This is only interesting if the constant is the wider arg. - Also, it is not safe if the constant is unsigned and the - variable arg is signed, since in this case the variable - would be sign-extended and then regarded as unsigned. - Our technique fails in this case because the lowest/highest - possible unsigned results don't follow naturally from the - lowest/highest possible values of the variable operand. - For just EQ_EXPR and NE_EXPR there is another technique that - could be used: see if the constant can be faithfully represented - in the other operand's type, by truncating it and reextending it - and see if that preserves the constant's value. */ - - if (!real1 && !real2 - && TREE_CODE (primop1) == INTEGER_CST - && TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr)) - { - int min_gt, max_gt, min_lt, max_lt; - tree maxval, minval; - /* 1 if comparison is nominally unsigned. */ - int unsignedp = TREE_UNSIGNED (*restype_ptr); - tree val; - - type = signed_or_unsigned_type (unsignedp0, TREE_TYPE (primop0)); - - maxval = TYPE_MAX_VALUE (type); - minval = TYPE_MIN_VALUE (type); - - if (unsignedp && !unsignedp0) - *restype_ptr = signed_type (*restype_ptr); - - if (TREE_TYPE (primop1) != *restype_ptr) - primop1 = convert (*restype_ptr, primop1); - if (type != *restype_ptr) - { - minval = convert (*restype_ptr, minval); - maxval = convert (*restype_ptr, maxval); - } - - if (unsignedp && unsignedp0) - { - min_gt = INT_CST_LT_UNSIGNED (primop1, minval); - max_gt = INT_CST_LT_UNSIGNED (primop1, maxval); - min_lt = INT_CST_LT_UNSIGNED (minval, primop1); - max_lt = INT_CST_LT_UNSIGNED (maxval, primop1); - } - else - { - min_gt = INT_CST_LT (primop1, minval); - max_gt = INT_CST_LT (primop1, maxval); - min_lt = INT_CST_LT (minval, primop1); - max_lt = INT_CST_LT (maxval, primop1); - } - - val = 0; - /* This used to be a switch, but Genix compiler can't handle that. */ - if (code == NE_EXPR) - { - if (max_lt || min_gt) - val = integer_one_node; - } - else if (code == EQ_EXPR) - { - if (max_lt || min_gt) - val = integer_zero_node; - } - else if (code == LT_EXPR) - { - if (max_lt) - val = integer_one_node; - if (!min_lt) - val = integer_zero_node; - } - else if (code == GT_EXPR) - { - if (min_gt) - val = integer_one_node; - if (!max_gt) - val = integer_zero_node; - } - else if (code == LE_EXPR) - { - if (!max_gt) - val = integer_one_node; - if (min_gt) - val = integer_zero_node; - } - else if (code == GE_EXPR) - { - if (!min_lt) - val = integer_one_node; - if (max_lt) - val = integer_zero_node; - } - - /* If primop0 was sign-extended and unsigned comparison specd, - we did a signed comparison above using the signed type bounds. - But the comparison we output must be unsigned. - - Also, for inequalities, VAL is no good; but if the signed - comparison had *any* fixed result, it follows that the - unsigned comparison just tests the sign in reverse - (positive values are LE, negative ones GE). - So we can generate an unsigned comparison - against an extreme value of the signed type. */ - - if (unsignedp && !unsignedp0) - { - if (val != 0) - switch (code) - { - case LT_EXPR: - case GE_EXPR: - primop1 = TYPE_MIN_VALUE (type); - val = 0; - break; - - case LE_EXPR: - case GT_EXPR: - primop1 = TYPE_MAX_VALUE (type); - val = 0; - break; - } - type = unsigned_type (type); - } - - if (!max_gt && !unsignedp0) - { - /* This is the case of (char)x >?< 0x80, which people used to use - expecting old C compilers to change the 0x80 into -0x80. */ - if (val == integer_zero_node) - warning ("comparison is always 0 due to limited range of data type"); - if (val == integer_one_node) - warning ("comparison is always 1 due to limited range of data type"); - } - - if (!min_lt && unsignedp0) - { - /* This is the case of (unsigned char)x >?< -1 or < 0. */ - if (val == integer_zero_node) - warning ("comparison is always 0 due to limited range of data type"); - if (val == integer_one_node) - warning ("comparison is always 1 due to limited range of data type"); - } - - if (val != 0) - { - /* Don't forget to evaluate PRIMOP0 if it has side effects. */ - if (TREE_SIDE_EFFECTS (primop0)) - return build (COMPOUND_EXPR, TREE_TYPE (val), primop0, val); - return val; - } - - /* Value is not predetermined, but do the comparison - in the type of the operand that is not constant. - TYPE is already properly set. */ - } - else if (real1 && real2 - && (TYPE_PRECISION (TREE_TYPE (primop0)) - == TYPE_PRECISION (TREE_TYPE (primop1)))) - type = TREE_TYPE (primop0); - - /* If args' natural types are both narrower than nominal type - and both extend in the same manner, compare them - in the type of the wider arg. - Otherwise must actually extend both to the nominal - common type lest different ways of extending - alter the result. - (eg, (short)-1 == (unsigned short)-1 should be 0.) */ - - else if (unsignedp0 == unsignedp1 && real1 == real2 - && TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr) - && TYPE_PRECISION (TREE_TYPE (primop1)) < TYPE_PRECISION (*restype_ptr)) - { - type = common_type (TREE_TYPE (primop0), TREE_TYPE (primop1)); - type = signed_or_unsigned_type (unsignedp0 - || TREE_UNSIGNED (*restype_ptr), - type); - /* Make sure shorter operand is extended the right way - to match the longer operand. */ - primop0 = convert (signed_or_unsigned_type (unsignedp0, TREE_TYPE (primop0)), - primop0); - primop1 = convert (signed_or_unsigned_type (unsignedp1, TREE_TYPE (primop1)), - primop1); - } - else - { - /* Here we must do the comparison on the nominal type - using the args exactly as we received them. */ - type = *restype_ptr; - primop0 = op0; - primop1 = op1; - - if (!real1 && !real2 && integer_zerop (primop1) - && TREE_UNSIGNED (TREE_TYPE (primop0))) - { - tree value = 0; - switch (code) - { - case GE_EXPR: - if (extra_warnings) - warning ("unsigned value >= 0 is always 1"); - value = integer_one_node; - break; - - case LT_EXPR: - if (extra_warnings) - warning ("unsigned value < 0 is always 0"); - value = integer_zero_node; - } - - if (value != 0) - { - /* Don't forget to evaluate PRIMOP0 if it has side effects. */ - if (TREE_SIDE_EFFECTS (primop0)) - return build (COMPOUND_EXPR, TREE_TYPE (value), - primop0, value); - return value; - } - } - } - - *op0_ptr = convert (type, primop0); - *op1_ptr = convert (type, primop1); - - *restype_ptr = integer_type_node; - - return 0; -} - -/* Prepare expr to be an argument of a TRUTH_NOT_EXPR, - or validate its data type for an `if' or `while' statement or ?..: exp. - - This preparation consists of taking the ordinary - representation of an expression expr and producing a valid tree - boolean expression describing whether expr is nonzero. We could - simply always do build_binary_op (NE_EXPR, expr, integer_zero_node, 1), - but we optimize comparisons, &&, ||, and !. - - The resulting type should always be `integer_type_node'. */ - -tree -truthvalue_conversion (expr) - tree expr; -{ - register enum tree_code code; - - if (TREE_CODE (expr) == ERROR_MARK) - return expr; - -#if 0 /* This appears to be wrong for C++. */ - /* These really should return error_mark_node after 2.4 is stable. - But not all callers handle ERROR_MARK properly. */ - switch (TREE_CODE (TREE_TYPE (expr))) - { - case RECORD_TYPE: - error ("struct type value used where scalar is required"); - return integer_zero_node; - - case UNION_TYPE: - error ("union type value used where scalar is required"); - return integer_zero_node; - - case ARRAY_TYPE: - error ("array type value used where scalar is required"); - return integer_zero_node; - - default: - break; - } -#endif /* 0 */ - - switch (TREE_CODE (expr)) - { - /* It is simpler and generates better code to have only TRUTH_*_EXPR - or comparison expressions as truth values at this level. */ -#if 0 - case COMPONENT_REF: - /* A one-bit unsigned bit-field is already acceptable. */ - if (1 == TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (expr, 1))) - && TREE_UNSIGNED (TREE_OPERAND (expr, 1))) - return expr; - break; -#endif - - case EQ_EXPR: - /* It is simpler and generates better code to have only TRUTH_*_EXPR - or comparison expressions as truth values at this level. */ -#if 0 - if (integer_zerop (TREE_OPERAND (expr, 1))) - return build_unary_op (TRUTH_NOT_EXPR, TREE_OPERAND (expr, 0), 0); -#endif - case NE_EXPR: case LE_EXPR: case GE_EXPR: case LT_EXPR: case GT_EXPR: - case TRUTH_ANDIF_EXPR: - case TRUTH_ORIF_EXPR: - case TRUTH_AND_EXPR: - case TRUTH_OR_EXPR: - case TRUTH_XOR_EXPR: - case ERROR_MARK: - return expr; - - case INTEGER_CST: - return integer_zerop (expr) ? integer_zero_node : integer_one_node; - - case REAL_CST: - return real_zerop (expr) ? integer_zero_node : integer_one_node; - - case ADDR_EXPR: - if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 0))) - return build (COMPOUND_EXPR, integer_type_node, - TREE_OPERAND (expr, 0), integer_one_node); - else - return integer_one_node; - - case COMPLEX_EXPR: - return build_binary_op ((TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1)) - ? TRUTH_AND_EXPR : TRUTH_ANDIF_EXPR), - truthvalue_conversion (TREE_OPERAND (expr, 0)), - truthvalue_conversion (TREE_OPERAND (expr, 1)), - 0); - - case NEGATE_EXPR: - case ABS_EXPR: - case FLOAT_EXPR: - case FFS_EXPR: - /* These don't change whether an object is non-zero or zero. */ - return truthvalue_conversion (TREE_OPERAND (expr, 0)); - - case LROTATE_EXPR: - case RROTATE_EXPR: - /* These don't change whether an object is zero or non-zero, but - we can't ignore them if their second arg has side-effects. */ - if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1))) - return build (COMPOUND_EXPR, integer_type_node, TREE_OPERAND (expr, 1), - truthvalue_conversion (TREE_OPERAND (expr, 0))); - else - return truthvalue_conversion (TREE_OPERAND (expr, 0)); - - case COND_EXPR: - /* Distribute the conversion into the arms of a COND_EXPR. */ - return fold (build (COND_EXPR, integer_type_node, TREE_OPERAND (expr, 0), - truthvalue_conversion (TREE_OPERAND (expr, 1)), - truthvalue_conversion (TREE_OPERAND (expr, 2)))); - - case CONVERT_EXPR: - /* Don't cancel the effect of a CONVERT_EXPR from a REFERENCE_TYPE, - since that affects how `default_conversion' will behave. */ - if (TREE_CODE (TREE_TYPE (expr)) == REFERENCE_TYPE - || TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == REFERENCE_TYPE) - break; - /* fall through... */ - case NOP_EXPR: - /* If this is widening the argument, we can ignore it. */ - if (TYPE_PRECISION (TREE_TYPE (expr)) - >= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (expr, 0)))) - return truthvalue_conversion (TREE_OPERAND (expr, 0)); - break; - - case MINUS_EXPR: - /* With IEEE arithmetic, x - x may not equal 0, so we can't optimize - this case. */ - if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT - && TREE_CODE (TREE_TYPE (expr)) == REAL_TYPE) - break; - /* fall through... */ - case BIT_XOR_EXPR: - /* This and MINUS_EXPR can be changed into a comparison of the - two objects. */ - if (TREE_TYPE (TREE_OPERAND (expr, 0)) - == TREE_TYPE (TREE_OPERAND (expr, 1))) - return build_binary_op (NE_EXPR, TREE_OPERAND (expr, 0), - TREE_OPERAND (expr, 1), 1); - return build_binary_op (NE_EXPR, TREE_OPERAND (expr, 0), - fold (build1 (NOP_EXPR, - TREE_TYPE (TREE_OPERAND (expr, 0)), - TREE_OPERAND (expr, 1))), 1); - - case MODIFY_EXPR: - if (warn_parentheses && C_EXP_ORIGINAL_CODE (expr) == MODIFY_EXPR) - warning ("suggest parentheses around assignment used as truth value"); - break; - } - - if (TREE_CODE (TREE_TYPE (expr)) == COMPLEX_TYPE) - return (build_binary_op - ((TREE_SIDE_EFFECTS (expr) - ? TRUTH_AND_EXPR : TRUTH_ANDIF_EXPR), - truthvalue_conversion (build_unary_op (REALPART_EXPR, expr, 0)), - truthvalue_conversion (build_unary_op (IMAGPART_EXPR, expr, 0)), - 0)); - - return build_binary_op (NE_EXPR, expr, integer_zero_node, 1); -} - -/* Read the rest of a #-directive from input stream FINPUT. - In normal use, the directive name and the white space after it - have already been read, so they won't be included in the result. - We allow for the fact that the directive line may contain - a newline embedded within a character or string literal which forms - a part of the directive. - - The value is a string in a reusable buffer. It remains valid - only until the next time this function is called. */ - -char * -get_directive_line (finput) - register FILE *finput; -{ - static char *directive_buffer = NULL; - static unsigned buffer_length = 0; - register char *p; - register char *buffer_limit; - register int looking_for = 0; - register int char_escaped = 0; - - if (buffer_length == 0) - { - directive_buffer = (char *)xmalloc (128); - buffer_length = 128; - } - - buffer_limit = &directive_buffer[buffer_length]; - - for (p = directive_buffer; ; ) - { - int c; - - /* Make buffer bigger if it is full. */ - if (p >= buffer_limit) - { - register unsigned bytes_used = (p - directive_buffer); - - buffer_length *= 2; - directive_buffer - = (char *)xrealloc (directive_buffer, buffer_length); - p = &directive_buffer[bytes_used]; - buffer_limit = &directive_buffer[buffer_length]; - } - - c = getc (finput); - - /* Discard initial whitespace. */ - if ((c == ' ' || c == '\t') && p == directive_buffer) - continue; - - /* Detect the end of the directive. */ - if (c == '\n' && looking_for == 0) - { - ungetc (c, finput); - c = '\0'; - } - - *p++ = c; - - if (c == 0) - return directive_buffer; - - /* Handle string and character constant syntax. */ - if (looking_for) - { - if (looking_for == c && !char_escaped) - looking_for = 0; /* Found terminator... stop looking. */ - } - else - if (c == '\'' || c == '"') - looking_for = c; /* Don't stop buffering until we see another - another one of these (or an EOF). */ - - /* Handle backslash. */ - char_escaped = (c == '\\' && ! char_escaped); - } -} - -/* Make a variant type in the proper way for C/C++, propagating qualifiers - down to the element type of an array. */ - -tree -c_build_type_variant (type, constp, volatilep) - tree type; - int constp, volatilep; -{ - if (TREE_CODE (type) == ARRAY_TYPE) - { - tree real_main_variant = TYPE_MAIN_VARIANT (type); - int permanent = TREE_PERMANENT (type); - - if (permanent) - push_obstacks (&permanent_obstack, &permanent_obstack); - type = build_array_type (c_build_type_variant (TREE_TYPE (type), - constp, volatilep), - TYPE_DOMAIN (type)); - TYPE_MAIN_VARIANT (type) = real_main_variant; - if (permanent) - pop_obstacks (); - } - return build_type_variant (type, constp, volatilep); -} diff --git a/gnu/usr.bin/cc/common/calls.c b/gnu/usr.bin/cc/common/calls.c deleted file mode 100644 index 4e0e4e2b9d..0000000000 --- a/gnu/usr.bin/cc/common/calls.c +++ /dev/null @@ -1,2880 +0,0 @@ -/* Convert function calls to rtl insns, for GNU C compiler. - Copyright (C) 1989, 1992 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include "config.h" -#include "rtl.h" -#include "tree.h" -#include "flags.h" -#include "expr.h" -#include "gvarargs.h" -#include "insn-flags.h" - -/* Decide whether a function's arguments should be processed - from first to last or from last to first. - - They should if the stack and args grow in opposite directions, but - only if we have push insns. */ - -#ifdef PUSH_ROUNDING - -#if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNARD) -#define PUSH_ARGS_REVERSED /* If it's last to first */ -#endif - -#endif - -/* Like STACK_BOUNDARY but in units of bytes, not bits. */ -#define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT) - -/* Data structure and subroutines used within expand_call. */ - -struct arg_data -{ - /* Tree node for this argument. */ - tree tree_value; - /* Mode for value; TYPE_MODE unless promoted. */ - enum machine_mode mode; - /* Current RTL value for argument, or 0 if it isn't precomputed. */ - rtx value; - /* Initially-compute RTL value for argument; only for const functions. */ - rtx initial_value; - /* Register to pass this argument in, 0 if passed on stack, or an - EXPR_LIST if the arg is to be copied into multiple different - registers. */ - rtx reg; - /* If REG was promoted from the actual mode of the argument expression, - indicates whether the promotion is sign- or zero-extended. */ - int unsignedp; - /* Number of registers to use. 0 means put the whole arg in registers. - Also 0 if not passed in registers. */ - int partial; - /* Non-zero if argument must be passed on stack. - Note that some arguments may be passed on the stack - even though pass_on_stack is zero, just because FUNCTION_ARG says so. - pass_on_stack identifies arguments that *cannot* go in registers. */ - int pass_on_stack; - /* Offset of this argument from beginning of stack-args. */ - struct args_size offset; - /* Similar, but offset to the start of the stack slot. Different from - OFFSET if this arg pads downward. */ - struct args_size slot_offset; - /* Size of this argument on the stack, rounded up for any padding it gets, - parts of the argument passed in registers do not count. - If REG_PARM_STACK_SPACE is defined, then register parms - are counted here as well. */ - struct args_size size; - /* Location on the stack at which parameter should be stored. The store - has already been done if STACK == VALUE. */ - rtx stack; - /* Location on the stack of the start of this argument slot. This can - differ from STACK if this arg pads downward. This location is known - to be aligned to FUNCTION_ARG_BOUNDARY. */ - rtx stack_slot; -#ifdef ACCUMULATE_OUTGOING_ARGS - /* Place that this stack area has been saved, if needed. */ - rtx save_area; -#endif -#ifdef STRICT_ALIGNMENT - /* If an argument's alignment does not permit direct copying into registers, - copy in smaller-sized pieces into pseudos. These are stored in a - block pointed to by this field. The next field says how many - word-sized pseudos we made. */ - rtx *aligned_regs; - int n_aligned_regs; -#endif -}; - -#ifdef ACCUMULATE_OUTGOING_ARGS -/* A vector of one char per byte of stack space. A byte if non-zero if - the corresponding stack location has been used. - This vector is used to prevent a function call within an argument from - clobbering any stack already set up. */ -static char *stack_usage_map; - -/* Size of STACK_USAGE_MAP. */ -static int highest_outgoing_arg_in_use; - -/* stack_arg_under_construction is nonzero when an argument may be - initialized with a constructor call (including a C function that - returns a BLKmode struct) and expand_call must take special action - to make sure the object being constructed does not overlap the - argument list for the constructor call. */ -int stack_arg_under_construction; -#endif - -static int calls_function PROTO((tree, int)); -static void emit_call_1 PROTO((rtx, tree, int, int, rtx, rtx, int, - rtx, int)); -static void store_one_arg PROTO ((struct arg_data *, rtx, int, int, - tree, int)); - -/* If WHICH is 1, return 1 if EXP contains a call to the built-in function - `alloca'. - - If WHICH is 0, return 1 if EXP contains a call to any function. - Actually, we only need return 1 if evaluating EXP would require pushing - arguments on the stack, but that is too difficult to compute, so we just - assume any function call might require the stack. */ - -static int -calls_function (exp, which) - tree exp; - int which; -{ - register int i; - int type = TREE_CODE_CLASS (TREE_CODE (exp)); - int length = tree_code_length[(int) TREE_CODE (exp)]; - - /* Only expressions and references can contain calls. */ - - if (type != 'e' && type != '<' && type != '1' && type != '2' && type != 'r' - && type != 'b') - return 0; - - switch (TREE_CODE (exp)) - { - case CALL_EXPR: - if (which == 0) - return 1; - else if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR - && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) - == FUNCTION_DECL) - && DECL_BUILT_IN (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) - && (DECL_FUNCTION_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) - == BUILT_IN_ALLOCA)) - return 1; - - /* Third operand is RTL. */ - length = 2; - break; - - case SAVE_EXPR: - if (SAVE_EXPR_RTL (exp) != 0) - return 0; - break; - - case BLOCK: - { - register tree local; - - for (local = BLOCK_VARS (exp); local; local = TREE_CHAIN (local)) - if (DECL_INITIAL (local) != 0 - && calls_function (DECL_INITIAL (local), which)) - return 1; - } - { - register tree subblock; - - for (subblock = BLOCK_SUBBLOCKS (exp); - subblock; - subblock = TREE_CHAIN (subblock)) - if (calls_function (subblock, which)) - return 1; - } - return 0; - - case METHOD_CALL_EXPR: - length = 3; - break; - - case WITH_CLEANUP_EXPR: - length = 1; - break; - - case RTL_EXPR: - return 0; - } - - for (i = 0; i < length; i++) - if (TREE_OPERAND (exp, i) != 0 - && calls_function (TREE_OPERAND (exp, i), which)) - return 1; - - return 0; -} - -/* Force FUNEXP into a form suitable for the address of a CALL, - and return that as an rtx. Also load the static chain register - if FNDECL is a nested function. - - USE_INSNS points to a variable holding a chain of USE insns - to which a USE of the static chain - register should be added, if required. */ - -rtx -prepare_call_address (funexp, fndecl, use_insns) - rtx funexp; - tree fndecl; - rtx *use_insns; -{ - rtx static_chain_value = 0; - - funexp = protect_from_queue (funexp, 0); - - if (fndecl != 0) - /* Get possible static chain value for nested function in C. */ - static_chain_value = lookup_static_chain (fndecl); - - /* Make a valid memory address and copy constants thru pseudo-regs, - but not for a constant address if -fno-function-cse. */ - if (GET_CODE (funexp) != SYMBOL_REF) - funexp = memory_address (FUNCTION_MODE, funexp); - else - { -#ifndef NO_FUNCTION_CSE - if (optimize && ! flag_no_function_cse) -#ifdef NO_RECURSIVE_FUNCTION_CSE - if (fndecl != current_function_decl) -#endif - funexp = force_reg (Pmode, funexp); -#endif - } - - if (static_chain_value != 0) - { - emit_move_insn (static_chain_rtx, static_chain_value); - - /* Put the USE insn in the chain we were passed. It will later be - output immediately in front of the CALL insn. */ - push_to_sequence (*use_insns); - emit_insn (gen_rtx (USE, VOIDmode, static_chain_rtx)); - *use_insns = get_insns (); - end_sequence (); - } - - return funexp; -} - -/* Generate instructions to call function FUNEXP, - and optionally pop the results. - The CALL_INSN is the first insn generated. - - FUNTYPE is the data type of the function, or, for a library call, - the identifier for the name of the call. This is given to the - macro RETURN_POPS_ARGS to determine whether this function pops its own args. - - STACK_SIZE is the number of bytes of arguments on the stack, - rounded up to STACK_BOUNDARY; zero if the size is variable. - This is both to put into the call insn and - to generate explicit popping code if necessary. - - STRUCT_VALUE_SIZE is the number of bytes wanted in a structure value. - It is zero if this call doesn't want a structure value. - - NEXT_ARG_REG is the rtx that results from executing - FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1) - just after all the args have had their registers assigned. - This could be whatever you like, but normally it is the first - arg-register beyond those used for args in this call, - or 0 if all the arg-registers are used in this call. - It is passed on to `gen_call' so you can put this info in the call insn. - - VALREG is a hard register in which a value is returned, - or 0 if the call does not return a value. - - OLD_INHIBIT_DEFER_POP is the value that `inhibit_defer_pop' had before - the args to this call were processed. - We restore `inhibit_defer_pop' to that value. - - USE_INSNS is a chain of USE insns to be emitted immediately before - the actual CALL insn. - - IS_CONST is true if this is a `const' call. */ - -static void -emit_call_1 (funexp, funtype, stack_size, struct_value_size, next_arg_reg, - valreg, old_inhibit_defer_pop, use_insns, is_const) - rtx funexp; - tree funtype; - int stack_size; - int struct_value_size; - rtx next_arg_reg; - rtx valreg; - int old_inhibit_defer_pop; - rtx use_insns; - int is_const; -{ - rtx stack_size_rtx = GEN_INT (stack_size); - rtx struct_value_size_rtx = GEN_INT (struct_value_size); - rtx call_insn; - int already_popped = 0; - - /* Ensure address is valid. SYMBOL_REF is already valid, so no need, - and we don't want to load it into a register as an optimization, - because prepare_call_address already did it if it should be done. */ - if (GET_CODE (funexp) != SYMBOL_REF) - funexp = memory_address (FUNCTION_MODE, funexp); - -#ifndef ACCUMULATE_OUTGOING_ARGS -#if defined (HAVE_call_pop) && defined (HAVE_call_value_pop) - if (HAVE_call_pop && HAVE_call_value_pop - && (RETURN_POPS_ARGS (funtype, stack_size) > 0 || stack_size == 0)) - { - rtx n_pop = GEN_INT (RETURN_POPS_ARGS (funtype, stack_size)); - rtx pat; - - /* If this subroutine pops its own args, record that in the call insn - if possible, for the sake of frame pointer elimination. */ - if (valreg) - pat = gen_call_value_pop (valreg, - gen_rtx (MEM, FUNCTION_MODE, funexp), - stack_size_rtx, next_arg_reg, n_pop); - else - pat = gen_call_pop (gen_rtx (MEM, FUNCTION_MODE, funexp), - stack_size_rtx, next_arg_reg, n_pop); - - emit_call_insn (pat); - already_popped = 1; - } - else -#endif -#endif - -#if defined (HAVE_call) && defined (HAVE_call_value) - if (HAVE_call && HAVE_call_value) - { - if (valreg) - emit_call_insn (gen_call_value (valreg, - gen_rtx (MEM, FUNCTION_MODE, funexp), - stack_size_rtx, next_arg_reg, - NULL_RTX)); - else - emit_call_insn (gen_call (gen_rtx (MEM, FUNCTION_MODE, funexp), - stack_size_rtx, next_arg_reg, - struct_value_size_rtx)); - } - else -#endif - abort (); - - /* Find the CALL insn we just emitted and write the USE insns before it. */ - for (call_insn = get_last_insn (); - call_insn && GET_CODE (call_insn) != CALL_INSN; - call_insn = PREV_INSN (call_insn)) - ; - - if (! call_insn) - abort (); - - /* Put the USE insns before the CALL. */ - emit_insns_before (use_insns, call_insn); - - /* If this is a const call, then set the insn's unchanging bit. */ - if (is_const) - CONST_CALL_P (call_insn) = 1; - - /* Restore this now, so that we do defer pops for this call's args - if the context of the call as a whole permits. */ - inhibit_defer_pop = old_inhibit_defer_pop; - -#ifndef ACCUMULATE_OUTGOING_ARGS - /* If returning from the subroutine does not automatically pop the args, - we need an instruction to pop them sooner or later. - Perhaps do it now; perhaps just record how much space to pop later. - - If returning from the subroutine does pop the args, indicate that the - stack pointer will be changed. */ - - if (stack_size != 0 && RETURN_POPS_ARGS (funtype, stack_size) > 0) - { - if (!already_popped) - emit_insn (gen_rtx (CLOBBER, VOIDmode, stack_pointer_rtx)); - stack_size -= RETURN_POPS_ARGS (funtype, stack_size); - stack_size_rtx = GEN_INT (stack_size); - } - - if (stack_size != 0) - { - if (flag_defer_pop && inhibit_defer_pop == 0 && !is_const) - pending_stack_adjust += stack_size; - else - adjust_stack (stack_size_rtx); - } -#endif -} - -/* Generate all the code for a function call - and return an rtx for its value. - Store the value in TARGET (specified as an rtx) if convenient. - If the value is stored in TARGET then TARGET is returned. - If IGNORE is nonzero, then we ignore the value of the function call. */ - -rtx -expand_call (exp, target, ignore) - tree exp; - rtx target; - int ignore; -{ - /* List of actual parameters. */ - tree actparms = TREE_OPERAND (exp, 1); - /* RTX for the function to be called. */ - rtx funexp; - /* Tree node for the function to be called (not the address!). */ - tree funtree; - /* Data type of the function. */ - tree funtype; - /* Declaration of the function being called, - or 0 if the function is computed (not known by name). */ - tree fndecl = 0; - char *name = 0; - - /* Register in which non-BLKmode value will be returned, - or 0 if no value or if value is BLKmode. */ - rtx valreg; - /* Address where we should return a BLKmode value; - 0 if value not BLKmode. */ - rtx structure_value_addr = 0; - /* Nonzero if that address is being passed by treating it as - an extra, implicit first parameter. Otherwise, - it is passed by being copied directly into struct_value_rtx. */ - int structure_value_addr_parm = 0; - /* Size of aggregate value wanted, or zero if none wanted - or if we are using the non-reentrant PCC calling convention - or expecting the value in registers. */ - int struct_value_size = 0; - /* Nonzero if called function returns an aggregate in memory PCC style, - by returning the address of where to find it. */ - int pcc_struct_value = 0; - - /* Number of actual parameters in this call, including struct value addr. */ - int num_actuals; - /* Number of named args. Args after this are anonymous ones - and they must all go on the stack. */ - int n_named_args; - /* Count arg position in order args appear. */ - int argpos; - - /* Vector of information about each argument. - Arguments are numbered in the order they will be pushed, - not the order they are written. */ - struct arg_data *args; - - /* Total size in bytes of all the stack-parms scanned so far. */ - struct args_size args_size; - /* Size of arguments before any adjustments (such as rounding). */ - struct args_size original_args_size; - /* Data on reg parms scanned so far. */ - CUMULATIVE_ARGS args_so_far; - /* Nonzero if a reg parm has been scanned. */ - int reg_parm_seen; - /* Nonzero if this is an indirect function call. */ - int current_call_is_indirect = 0; - - /* Nonzero if we must avoid push-insns in the args for this call. - If stack space is allocated for register parameters, but not by the - caller, then it is preallocated in the fixed part of the stack frame. - So the entire argument block must then be preallocated (i.e., we - ignore PUSH_ROUNDING in that case). */ - -#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE) - int must_preallocate = 1; -#else -#ifdef PUSH_ROUNDING - int must_preallocate = 0; -#else - int must_preallocate = 1; -#endif -#endif - - /* Size of the stack reserved for parameter registers. */ - int reg_parm_stack_space = 0; - - /* 1 if scanning parms front to back, -1 if scanning back to front. */ - int inc; - /* Address of space preallocated for stack parms - (on machines that lack push insns), or 0 if space not preallocated. */ - rtx argblock = 0; - - /* Nonzero if it is plausible that this is a call to alloca. */ - int may_be_alloca; - /* Nonzero if this is a call to setjmp or a related function. */ - int returns_twice; - /* Nonzero if this is a call to `longjmp'. */ - int is_longjmp; - /* Nonzero if this is a call to an inline function. */ - int is_integrable = 0; - /* Nonzero if this is a call to a `const' function. - Note that only explicitly named functions are handled as `const' here. */ - int is_const = 0; - /* Nonzero if this is a call to a `volatile' function. */ - int is_volatile = 0; -#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE) - /* Define the boundary of the register parm stack space that needs to be - save, if any. */ - int low_to_save = -1, high_to_save; - rtx save_area = 0; /* Place that it is saved */ -#endif - -#ifdef ACCUMULATE_OUTGOING_ARGS - int initial_highest_arg_in_use = highest_outgoing_arg_in_use; - char *initial_stack_usage_map = stack_usage_map; -#endif - - rtx old_stack_level = 0; - int old_pending_adj; - int old_stack_arg_under_construction; - int old_inhibit_defer_pop = inhibit_defer_pop; - tree old_cleanups = cleanups_this_call; - - rtx use_insns = 0; - - register tree p; - register int i, j; - - /* See if we can find a DECL-node for the actual function. - As a result, decide whether this is a call to an integrable function. */ - - p = TREE_OPERAND (exp, 0); - if (TREE_CODE (p) == ADDR_EXPR) - { - fndecl = TREE_OPERAND (p, 0); - if (TREE_CODE (fndecl) != FUNCTION_DECL) - { - /* May still be a `const' function if it is - a call through a pointer-to-const. - But we don't handle that. */ - fndecl = 0; - } - else - { - if (!flag_no_inline - && fndecl != current_function_decl - && DECL_SAVED_INSNS (fndecl)) - is_integrable = 1; - else if (! TREE_ADDRESSABLE (fndecl)) - { - /* In case this function later becomes inlinable, - record that there was already a non-inline call to it. - - Use abstraction instead of setting TREE_ADDRESSABLE - directly. */ - if (DECL_INLINE (fndecl) && extra_warnings && warn_inline - && !flag_no_inline) - warning_with_decl (fndecl, "can't inline call to `%s' which was declared inline"); - mark_addressable (fndecl); - } - - if (TREE_READONLY (fndecl) && ! TREE_THIS_VOLATILE (fndecl) - && TYPE_MODE (TREE_TYPE (exp)) != VOIDmode) - is_const = 1; - } - } - - is_volatile = TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (p))); - -#ifdef REG_PARM_STACK_SPACE -#ifdef MAYBE_REG_PARM_STACK_SPACE - reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE; -#else - reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl); -#endif -#endif - - /* Warn if this value is an aggregate type, - regardless of which calling convention we are using for it. */ - if (warn_aggregate_return - && (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE - || TREE_CODE (TREE_TYPE (exp)) == QUAL_UNION_TYPE - || TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE)) - warning ("function call has aggregate value"); - - /* Set up a place to return a structure. */ - - /* Cater to broken compilers. */ - if (aggregate_value_p (exp)) - { - /* This call returns a big structure. */ - is_const = 0; - -#ifdef PCC_STATIC_STRUCT_RETURN - { - pcc_struct_value = 1; - is_integrable = 0; /* Easier than making that case work right. */ - } -#else /* not PCC_STATIC_STRUCT_RETURN */ - { - struct_value_size = int_size_in_bytes (TREE_TYPE (exp)); - - if (struct_value_size < 0) - abort (); - - if (target && GET_CODE (target) == MEM) - structure_value_addr = XEXP (target, 0); - else - { - /* Assign a temporary on the stack to hold the value. */ - - /* For variable-sized objects, we must be called with a target - specified. If we were to allocate space on the stack here, - we would have no way of knowing when to free it. */ - - structure_value_addr - = XEXP (assign_stack_temp (BLKmode, struct_value_size, 1), 0); - target = 0; - } - } -#endif /* not PCC_STATIC_STRUCT_RETURN */ - } - - /* If called function is inline, try to integrate it. */ - - if (is_integrable) - { - rtx temp; - rtx before_call = get_last_insn (); - - temp = expand_inline_function (fndecl, actparms, target, - ignore, TREE_TYPE (exp), - structure_value_addr); - - /* If inlining succeeded, return. */ - if ((HOST_WIDE_INT) temp != -1) - { - /* Perform all cleanups needed for the arguments of this call - (i.e. destructors in C++). It is ok if these destructors - clobber RETURN_VALUE_REG, because the only time we care about - this is when TARGET is that register. But in C++, we take - care to never return that register directly. */ - expand_cleanups_to (old_cleanups); - -#ifdef ACCUMULATE_OUTGOING_ARGS - /* If the outgoing argument list must be preserved, push - the stack before executing the inlined function if it - makes any calls. */ - - for (i = reg_parm_stack_space - 1; i >= 0; i--) - if (i < highest_outgoing_arg_in_use && stack_usage_map[i] != 0) - break; - - if (stack_arg_under_construction || i >= 0) - { - rtx insn = NEXT_INSN (before_call), seq; - - /* Look for a call in the inline function code. - If OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)) is - nonzero then there is a call and it is not necessary - to scan the insns. */ - - if (OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)) == 0) - for (; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == CALL_INSN) - break; - - if (insn) - { - /* Reserve enough stack space so that the largest - argument list of any function call in the inline - function does not overlap the argument list being - evaluated. This is usually an overestimate because - allocate_dynamic_stack_space reserves space for an - outgoing argument list in addition to the requested - space, but there is no way to ask for stack space such - that an argument list of a certain length can be - safely constructed. */ - - int adjust = OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)); -#ifdef REG_PARM_STACK_SPACE - /* Add the stack space reserved for register arguments - in the inline function. What is really needed is the - largest value of reg_parm_stack_space in the inline - function, but that is not available. Using the current - value of reg_parm_stack_space is wrong, but gives - correct results on all supported machines. */ - adjust += reg_parm_stack_space; -#endif - start_sequence (); - emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); - allocate_dynamic_stack_space (GEN_INT (adjust), - NULL_RTX, BITS_PER_UNIT); - seq = get_insns (); - end_sequence (); - emit_insns_before (seq, NEXT_INSN (before_call)); - emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX); - } - } -#endif - - /* If the result is equivalent to TARGET, return TARGET to simplify - checks in store_expr. They can be equivalent but not equal in the - case of a function that returns BLKmode. */ - if (temp != target && rtx_equal_p (temp, target)) - return target; - return temp; - } - - /* If inlining failed, mark FNDECL as needing to be compiled - separately after all. */ - mark_addressable (fndecl); - } - - /* When calling a const function, we must pop the stack args right away, - so that the pop is deleted or moved with the call. */ - if (is_const) - NO_DEFER_POP; - - function_call_count++; - - if (fndecl && DECL_NAME (fndecl)) - name = IDENTIFIER_POINTER (DECL_NAME (fndecl)); - - /* On some machines (such as the PA) indirect calls have a different - calling convention than normal calls. FUNCTION_ARG in the target - description can look at current_call_is_indirect to determine which - calling convention to use. */ - current_call_is_indirect = (fndecl == 0); -#if 0 - = TREE_CODE (TREE_OPERAND (exp, 0)) == NON_LVALUE_EXPR ? 1 : 0; -#endif - -#if 0 - /* Unless it's a call to a specific function that isn't alloca, - if it has one argument, we must assume it might be alloca. */ - - may_be_alloca = - (!(fndecl != 0 && strcmp (name, "alloca")) - && actparms != 0 - && TREE_CHAIN (actparms) == 0); -#else - /* We assume that alloca will always be called by name. It - makes no sense to pass it as a pointer-to-function to - anything that does not understand its behavior. */ - may_be_alloca = - (name && ((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6 - && name[0] == 'a' - && ! strcmp (name, "alloca")) - || (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16 - && name[0] == '_' - && ! strcmp (name, "__builtin_alloca")))); -#endif - - /* See if this is a call to a function that can return more than once - or a call to longjmp. */ - - returns_twice = 0; - is_longjmp = 0; - - if (name != 0 && IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 15) - { - char *tname = name; - - if (name[0] == '_') - tname += ((name[1] == '_' && name[2] == 'x') ? 3 : 1); - - if (tname[0] == 's') - { - returns_twice - = ((tname[1] == 'e' - && (! strcmp (tname, "setjmp") - || ! strcmp (tname, "setjmp_syscall"))) - || (tname[1] == 'i' - && ! strcmp (tname, "sigsetjmp")) - || (tname[1] == 'a' - && ! strcmp (tname, "savectx"))); - if (tname[1] == 'i' - && ! strcmp (tname, "siglongjmp")) - is_longjmp = 1; - } - else if ((tname[0] == 'q' && tname[1] == 's' - && ! strcmp (tname, "qsetjmp")) - || (tname[0] == 'v' && tname[1] == 'f' - && ! strcmp (tname, "vfork"))) - returns_twice = 1; - - else if (tname[0] == 'l' && tname[1] == 'o' - && ! strcmp (tname, "longjmp")) - is_longjmp = 1; - } - - if (may_be_alloca) - current_function_calls_alloca = 1; - - /* Don't let pending stack adjusts add up to too much. - Also, do all pending adjustments now - if there is any chance this might be a call to alloca. */ - - if (pending_stack_adjust >= 32 - || (pending_stack_adjust > 0 && may_be_alloca)) - do_pending_stack_adjust (); - - /* Operand 0 is a pointer-to-function; get the type of the function. */ - funtype = TREE_TYPE (TREE_OPERAND (exp, 0)); - if (TREE_CODE (funtype) != POINTER_TYPE) - abort (); - funtype = TREE_TYPE (funtype); - - /* Push the temporary stack slot level so that we can free temporaries used - by each of the arguments separately. */ - push_temp_slots (); - - /* Start updating where the next arg would go. */ - INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX); - - /* If struct_value_rtx is 0, it means pass the address - as if it were an extra parameter. */ - if (structure_value_addr && struct_value_rtx == 0) - { -#ifdef ACCUMULATE_OUTGOING_ARGS - /* If the stack will be adjusted, make sure the structure address - does not refer to virtual_outgoing_args_rtx. */ - rtx temp = (stack_arg_under_construction - ? copy_addr_to_reg (structure_value_addr) - : force_reg (Pmode, structure_value_addr)); -#else - rtx temp = force_reg (Pmode, structure_value_addr); -#endif - - actparms - = tree_cons (error_mark_node, - make_tree (build_pointer_type (TREE_TYPE (funtype)), - temp), - actparms); - structure_value_addr_parm = 1; - } - - /* Count the arguments and set NUM_ACTUALS. */ - for (p = actparms, i = 0; p; p = TREE_CHAIN (p)) i++; - num_actuals = i; - - /* Compute number of named args. - Normally, don't include the last named arg if anonymous args follow. - (If no anonymous args follow, the result of list_length - is actually one too large.) - - If SETUP_INCOMING_VARARGS is defined, this machine will be able to - place unnamed args that were passed in registers into the stack. So - treat all args as named. This allows the insns emitting for a specific - argument list to be independent of the function declaration. - - If SETUP_INCOMING_VARARGS is not defined, we do not have any reliable - way to pass unnamed args in registers, so we must force them into - memory. */ -#ifndef SETUP_INCOMING_VARARGS - if (TYPE_ARG_TYPES (funtype) != 0) - n_named_args - = list_length (TYPE_ARG_TYPES (funtype)) - 1 - /* Count the struct value address, if it is passed as a parm. */ - + structure_value_addr_parm; - else -#endif - /* If we know nothing, treat all args as named. */ - n_named_args = num_actuals; - - /* Make a vector to hold all the information about each arg. */ - args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data)); - bzero (args, num_actuals * sizeof (struct arg_data)); - - args_size.constant = 0; - args_size.var = 0; - - /* In this loop, we consider args in the order they are written. - We fill up ARGS from the front of from the back if necessary - so that in any case the first arg to be pushed ends up at the front. */ - -#ifdef PUSH_ARGS_REVERSED - i = num_actuals - 1, inc = -1; - /* In this case, must reverse order of args - so that we compute and push the last arg first. */ -#else - i = 0, inc = 1; -#endif - - /* I counts args in order (to be) pushed; ARGPOS counts in order written. */ - for (p = actparms, argpos = 0; p; p = TREE_CHAIN (p), i += inc, argpos++) - { - tree type = TREE_TYPE (TREE_VALUE (p)); - enum machine_mode mode; - - args[i].tree_value = TREE_VALUE (p); - - /* Replace erroneous argument with constant zero. */ - if (type == error_mark_node || TYPE_SIZE (type) == 0) - args[i].tree_value = integer_zero_node, type = integer_type_node; - - /* Decide where to pass this arg. - - args[i].reg is nonzero if all or part is passed in registers. - - args[i].partial is nonzero if part but not all is passed in registers, - and the exact value says how many words are passed in registers. - - args[i].pass_on_stack is nonzero if the argument must at least be - computed on the stack. It may then be loaded back into registers - if args[i].reg is nonzero. - - These decisions are driven by the FUNCTION_... macros and must agree - with those made by function.c. */ - -#ifdef FUNCTION_ARG_PASS_BY_REFERENCE - /* See if this argument should be passed by invisible reference. */ - if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, TYPE_MODE (type), type, - argpos < n_named_args)) - { -#ifdef FUNCTION_ARG_CALLEE_COPIES - if (FUNCTION_ARG_CALLEE_COPIES (args_so_far, TYPE_MODE (type), type, - argpos < n_named_args) - /* If it's in a register, we must make a copy of it too. */ - /* ??? Is this a sufficient test? Is there a better one? */ - && !(TREE_CODE (args[i].tree_value) == VAR_DECL - && REG_P (DECL_RTL (args[i].tree_value)))) - { - args[i].tree_value = build1 (ADDR_EXPR, - build_pointer_type (type), - args[i].tree_value); - type = build_pointer_type (type); - } - else -#endif - { - /* We make a copy of the object and pass the address to the - function being called. */ - rtx copy; - - if (TYPE_SIZE (type) == 0 - || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) - { - /* This is a variable-sized object. Make space on the stack - for it. */ - rtx size_rtx = expr_size (TREE_VALUE (p)); - - if (old_stack_level == 0) - { - emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); - old_pending_adj = pending_stack_adjust; - pending_stack_adjust = 0; - } - - copy = gen_rtx (MEM, BLKmode, - allocate_dynamic_stack_space (size_rtx, - NULL_RTX, - TYPE_ALIGN (type))); - } - else - { - int size = int_size_in_bytes (type); - copy = assign_stack_temp (TYPE_MODE (type), size, 1); - } - - store_expr (args[i].tree_value, copy, 0); - - args[i].tree_value = build1 (ADDR_EXPR, - build_pointer_type (type), - make_tree (type, copy)); - type = build_pointer_type (type); - } - } -#endif /* FUNCTION_ARG_PASS_BY_REFERENCE */ - - mode = TYPE_MODE (type); - -#ifdef PROMOTE_FUNCTION_ARGS - /* Compute the mode in which the arg is actually to be extended to. */ - if (TREE_CODE (type) == INTEGER_TYPE || TREE_CODE (type) == ENUMERAL_TYPE - || TREE_CODE (type) == BOOLEAN_TYPE || TREE_CODE (type) == CHAR_TYPE - || TREE_CODE (type) == REAL_TYPE || TREE_CODE (type) == POINTER_TYPE - || TREE_CODE (type) == OFFSET_TYPE) - { - int unsignedp = TREE_UNSIGNED (type); - PROMOTE_MODE (mode, unsignedp, type); - args[i].unsignedp = unsignedp; - } -#endif - - args[i].mode = mode; - args[i].reg = FUNCTION_ARG (args_so_far, mode, type, - argpos < n_named_args); -#ifdef FUNCTION_ARG_PARTIAL_NREGS - if (args[i].reg) - args[i].partial - = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, type, - argpos < n_named_args); -#endif - - args[i].pass_on_stack = MUST_PASS_IN_STACK (mode, type); - - /* If FUNCTION_ARG returned an (expr_list (nil) FOO), it means that - we are to pass this arg in the register(s) designated by FOO, but - also to pass it in the stack. */ - if (args[i].reg && GET_CODE (args[i].reg) == EXPR_LIST - && XEXP (args[i].reg, 0) == 0) - args[i].pass_on_stack = 1, args[i].reg = XEXP (args[i].reg, 1); - - /* If this is an addressable type, we must preallocate the stack - since we must evaluate the object into its final location. - - If this is to be passed in both registers and the stack, it is simpler - to preallocate. */ - if (TREE_ADDRESSABLE (type) - || (args[i].pass_on_stack && args[i].reg != 0)) - must_preallocate = 1; - - /* If this is an addressable type, we cannot pre-evaluate it. Thus, - we cannot consider this function call constant. */ - if (TREE_ADDRESSABLE (type)) - is_const = 0; - - /* Compute the stack-size of this argument. */ - if (args[i].reg == 0 || args[i].partial != 0 -#ifdef REG_PARM_STACK_SPACE - || reg_parm_stack_space > 0 -#endif - || args[i].pass_on_stack) - locate_and_pad_parm (mode, type, -#ifdef STACK_PARMS_IN_REG_PARM_AREA - 1, -#else - args[i].reg != 0, -#endif - fndecl, &args_size, &args[i].offset, - &args[i].size); - -#ifndef ARGS_GROW_DOWNWARD - args[i].slot_offset = args_size; -#endif - -#ifndef REG_PARM_STACK_SPACE - /* If a part of the arg was put into registers, - don't include that part in the amount pushed. */ - if (! args[i].pass_on_stack) - args[i].size.constant -= ((args[i].partial * UNITS_PER_WORD) - / (PARM_BOUNDARY / BITS_PER_UNIT) - * (PARM_BOUNDARY / BITS_PER_UNIT)); -#endif - - /* Update ARGS_SIZE, the total stack space for args so far. */ - - args_size.constant += args[i].size.constant; - if (args[i].size.var) - { - ADD_PARM_SIZE (args_size, args[i].size.var); - } - - /* Since the slot offset points to the bottom of the slot, - we must record it after incrementing if the args grow down. */ -#ifdef ARGS_GROW_DOWNWARD - args[i].slot_offset = args_size; - - args[i].slot_offset.constant = -args_size.constant; - if (args_size.var) - { - SUB_PARM_SIZE (args[i].slot_offset, args_size.var); - } -#endif - - /* Increment ARGS_SO_FAR, which has info about which arg-registers - have been used, etc. */ - - FUNCTION_ARG_ADVANCE (args_so_far, TYPE_MODE (type), type, - argpos < n_named_args); - } - -#ifdef FINAL_REG_PARM_STACK_SPACE - reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant, - args_size.var); -#endif - - /* Compute the actual size of the argument block required. The variable - and constant sizes must be combined, the size may have to be rounded, - and there may be a minimum required size. */ - - original_args_size = args_size; - if (args_size.var) - { - /* If this function requires a variable-sized argument list, don't try to - make a cse'able block for this call. We may be able to do this - eventually, but it is too complicated to keep track of what insns go - in the cse'able block and which don't. */ - - is_const = 0; - must_preallocate = 1; - - args_size.var = ARGS_SIZE_TREE (args_size); - args_size.constant = 0; - -#ifdef STACK_BOUNDARY - if (STACK_BOUNDARY != BITS_PER_UNIT) - args_size.var = round_up (args_size.var, STACK_BYTES); -#endif - -#ifdef REG_PARM_STACK_SPACE - if (reg_parm_stack_space > 0) - { - args_size.var - = size_binop (MAX_EXPR, args_size.var, - size_int (REG_PARM_STACK_SPACE (fndecl))); - -#ifndef OUTGOING_REG_PARM_STACK_SPACE - /* The area corresponding to register parameters is not to count in - the size of the block we need. So make the adjustment. */ - args_size.var - = size_binop (MINUS_EXPR, args_size.var, - size_int (reg_parm_stack_space)); -#endif - } -#endif - } - else - { -#ifdef STACK_BOUNDARY - args_size.constant = (((args_size.constant + (STACK_BYTES - 1)) - / STACK_BYTES) * STACK_BYTES); -#endif - -#ifdef REG_PARM_STACK_SPACE - args_size.constant = MAX (args_size.constant, - reg_parm_stack_space); -#ifdef MAYBE_REG_PARM_STACK_SPACE - if (reg_parm_stack_space == 0) - args_size.constant = 0; -#endif -#ifndef OUTGOING_REG_PARM_STACK_SPACE - args_size.constant -= reg_parm_stack_space; -#endif -#endif - } - - /* See if we have or want to preallocate stack space. - - If we would have to push a partially-in-regs parm - before other stack parms, preallocate stack space instead. - - If the size of some parm is not a multiple of the required stack - alignment, we must preallocate. - - If the total size of arguments that would otherwise create a copy in - a temporary (such as a CALL) is more than half the total argument list - size, preallocation is faster. - - Another reason to preallocate is if we have a machine (like the m88k) - where stack alignment is required to be maintained between every - pair of insns, not just when the call is made. However, we assume here - that such machines either do not have push insns (and hence preallocation - would occur anyway) or the problem is taken care of with - PUSH_ROUNDING. */ - - if (! must_preallocate) - { - int partial_seen = 0; - int copy_to_evaluate_size = 0; - - for (i = 0; i < num_actuals && ! must_preallocate; i++) - { - if (args[i].partial > 0 && ! args[i].pass_on_stack) - partial_seen = 1; - else if (partial_seen && args[i].reg == 0) - must_preallocate = 1; - - if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode - && (TREE_CODE (args[i].tree_value) == CALL_EXPR - || TREE_CODE (args[i].tree_value) == TARGET_EXPR - || TREE_CODE (args[i].tree_value) == COND_EXPR - || TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)))) - copy_to_evaluate_size - += int_size_in_bytes (TREE_TYPE (args[i].tree_value)); - } - - if (copy_to_evaluate_size * 2 >= args_size.constant - && args_size.constant > 0) - must_preallocate = 1; - } - - /* If the structure value address will reference the stack pointer, we must - stabilize it. We don't need to do this if we know that we are not going - to adjust the stack pointer in processing this call. */ - - if (structure_value_addr - && (reg_mentioned_p (virtual_stack_dynamic_rtx, structure_value_addr) - || reg_mentioned_p (virtual_outgoing_args_rtx, structure_value_addr)) - && (args_size.var -#ifndef ACCUMULATE_OUTGOING_ARGS - || args_size.constant -#endif - )) - structure_value_addr = copy_to_reg (structure_value_addr); - - /* If this function call is cse'able, precompute all the parameters. - Note that if the parameter is constructed into a temporary, this will - cause an additional copy because the parameter will be constructed - into a temporary location and then copied into the outgoing arguments. - If a parameter contains a call to alloca and this function uses the - stack, precompute the parameter. */ - - /* If we preallocated the stack space, and some arguments must be passed - on the stack, then we must precompute any parameter which contains a - function call which will store arguments on the stack. - Otherwise, evaluating the parameter may clobber previous parameters - which have already been stored into the stack. */ - - for (i = 0; i < num_actuals; i++) - if (is_const - || ((args_size.var != 0 || args_size.constant != 0) - && calls_function (args[i].tree_value, 1)) - || (must_preallocate && (args_size.var != 0 || args_size.constant != 0) - && calls_function (args[i].tree_value, 0))) - { - args[i].initial_value = args[i].value - = expand_expr (args[i].tree_value, NULL_RTX, VOIDmode, 0); - - if (GET_MODE (args[i].value ) != VOIDmode - && GET_MODE (args[i].value) != args[i].mode) - args[i].value = convert_to_mode (args[i].mode, args[i].value, - args[i].unsignedp); - preserve_temp_slots (args[i].value); - - free_temp_slots (); - - /* ANSI doesn't require a sequence point here, - but PCC has one, so this will avoid some problems. */ - emit_queue (); - } - - /* Now we are about to start emitting insns that can be deleted - if a libcall is deleted. */ - if (is_const) - start_sequence (); - - /* If we have no actual push instructions, or shouldn't use them, - make space for all args right now. */ - - if (args_size.var != 0) - { - if (old_stack_level == 0) - { - emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); - old_pending_adj = pending_stack_adjust; - pending_stack_adjust = 0; -#ifdef ACCUMULATE_OUTGOING_ARGS - /* stack_arg_under_construction says whether a stack arg is - being constructed at the old stack level. Pushing the stack - gets a clean outgoing argument block. */ - old_stack_arg_under_construction = stack_arg_under_construction; - stack_arg_under_construction = 0; -#endif - } - argblock = push_block (ARGS_SIZE_RTX (args_size), 0, 0); - } - else if (must_preallocate) - { - /* Note that we must go through the motions of allocating an argument - block even if the size is zero because we may be storing args - in the area reserved for register arguments, which may be part of - the stack frame. */ - int needed = args_size.constant; - -#ifdef ACCUMULATE_OUTGOING_ARGS - /* Store the maximum argument space used. It will be pushed by the - prologue. - - Since the stack pointer will never be pushed, it is possible for - the evaluation of a parm to clobber something we have already - written to the stack. Since most function calls on RISC machines - do not use the stack, this is uncommon, but must work correctly. - - Therefore, we save any area of the stack that was already written - and that we are using. Here we set up to do this by making a new - stack usage map from the old one. The actual save will be done - by store_one_arg. - - Another approach might be to try to reorder the argument - evaluations to avoid this conflicting stack usage. */ - - if (needed > current_function_outgoing_args_size) - current_function_outgoing_args_size = needed; - -#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE) - /* Since we will be writing into the entire argument area, the - map must be allocated for its entire size, not just the part that - is the responsibility of the caller. */ - needed += reg_parm_stack_space; -#endif - -#ifdef ARGS_GROW_DOWNWARD - highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use, - needed + 1); -#else - highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use, needed); -#endif - stack_usage_map = (char *) alloca (highest_outgoing_arg_in_use); - - if (initial_highest_arg_in_use) - bcopy (initial_stack_usage_map, stack_usage_map, - initial_highest_arg_in_use); - - if (initial_highest_arg_in_use != highest_outgoing_arg_in_use) - bzero (&stack_usage_map[initial_highest_arg_in_use], - highest_outgoing_arg_in_use - initial_highest_arg_in_use); - needed = 0; - - /* The address of the outgoing argument list must not be copied to a - register here, because argblock would be left pointing to the - wrong place after the call to allocate_dynamic_stack_space below. */ - - argblock = virtual_outgoing_args_rtx; - -#else /* not ACCUMULATE_OUTGOING_ARGS */ - if (inhibit_defer_pop == 0) - { - /* Try to reuse some or all of the pending_stack_adjust - to get this space. Maybe we can avoid any pushing. */ - if (needed > pending_stack_adjust) - { - needed -= pending_stack_adjust; - pending_stack_adjust = 0; - } - else - { - pending_stack_adjust -= needed; - needed = 0; - } - } - /* Special case this because overhead of `push_block' in this - case is non-trivial. */ - if (needed == 0) - argblock = virtual_outgoing_args_rtx; - else - argblock = push_block (GEN_INT (needed), 0, 0); - - /* We only really need to call `copy_to_reg' in the case where push - insns are going to be used to pass ARGBLOCK to a function - call in ARGS. In that case, the stack pointer changes value - from the allocation point to the call point, and hence - the value of VIRTUAL_OUTGOING_ARGS_RTX changes as well. - But might as well always do it. */ - argblock = copy_to_reg (argblock); -#endif /* not ACCUMULATE_OUTGOING_ARGS */ - } - - -#ifdef ACCUMULATE_OUTGOING_ARGS - /* The save/restore code in store_one_arg handles all cases except one: - a constructor call (including a C function returning a BLKmode struct) - to initialize an argument. */ - if (stack_arg_under_construction) - { -#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE) - rtx push_size = GEN_INT (reg_parm_stack_space + args_size.constant); -#else - rtx push_size = GEN_INT (args_size.constant); -#endif - if (old_stack_level == 0) - { - emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); - old_pending_adj = pending_stack_adjust; - pending_stack_adjust = 0; - /* stack_arg_under_construction says whether a stack arg is - being constructed at the old stack level. Pushing the stack - gets a clean outgoing argument block. */ - old_stack_arg_under_construction = stack_arg_under_construction; - stack_arg_under_construction = 0; - /* Make a new map for the new argument list. */ - stack_usage_map = (char *)alloca (highest_outgoing_arg_in_use); - bzero (stack_usage_map, highest_outgoing_arg_in_use); - highest_outgoing_arg_in_use = 0; - } - allocate_dynamic_stack_space (push_size, NULL_RTX, BITS_PER_UNIT); - } - /* If argument evaluation might modify the stack pointer, copy the - address of the argument list to a register. */ - for (i = 0; i < num_actuals; i++) - if (args[i].pass_on_stack) - { - argblock = copy_addr_to_reg (argblock); - break; - } -#endif - - - /* If we preallocated stack space, compute the address of each argument. - We need not ensure it is a valid memory address here; it will be - validized when it is used. */ - if (argblock) - { - rtx arg_reg = argblock; - int arg_offset = 0; - - if (GET_CODE (argblock) == PLUS) - arg_reg = XEXP (argblock, 0), arg_offset = INTVAL (XEXP (argblock, 1)); - - for (i = 0; i < num_actuals; i++) - { - rtx offset = ARGS_SIZE_RTX (args[i].offset); - rtx slot_offset = ARGS_SIZE_RTX (args[i].slot_offset); - rtx addr; - - /* Skip this parm if it will not be passed on the stack. */ - if (! args[i].pass_on_stack && args[i].reg != 0) - continue; - - if (GET_CODE (offset) == CONST_INT) - addr = plus_constant (arg_reg, INTVAL (offset)); - else - addr = gen_rtx (PLUS, Pmode, arg_reg, offset); - - addr = plus_constant (addr, arg_offset); - args[i].stack = gen_rtx (MEM, args[i].mode, addr); - - if (GET_CODE (slot_offset) == CONST_INT) - addr = plus_constant (arg_reg, INTVAL (slot_offset)); - else - addr = gen_rtx (PLUS, Pmode, arg_reg, slot_offset); - - addr = plus_constant (addr, arg_offset); - args[i].stack_slot = gen_rtx (MEM, args[i].mode, addr); - } - } - -#ifdef PUSH_ARGS_REVERSED -#ifdef STACK_BOUNDARY - /* If we push args individually in reverse order, perform stack alignment - before the first push (the last arg). */ - if (argblock == 0) - anti_adjust_stack (GEN_INT (args_size.constant - - original_args_size.constant)); -#endif -#endif - - /* Don't try to defer pops if preallocating, not even from the first arg, - since ARGBLOCK probably refers to the SP. */ - if (argblock) - NO_DEFER_POP; - - /* Get the function to call, in the form of RTL. */ - if (fndecl) - /* Get a SYMBOL_REF rtx for the function address. */ - funexp = XEXP (DECL_RTL (fndecl), 0); - else - /* Generate an rtx (probably a pseudo-register) for the address. */ - { - funexp = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0); - free_temp_slots (); /* FUNEXP can't be BLKmode */ - emit_queue (); - } - - /* Figure out the register where the value, if any, will come back. */ - valreg = 0; - if (TYPE_MODE (TREE_TYPE (exp)) != VOIDmode - && ! structure_value_addr) - { - if (pcc_struct_value) - valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)), - fndecl); - else - valreg = hard_function_value (TREE_TYPE (exp), fndecl); - } - - /* Precompute all register parameters. It isn't safe to compute anything - once we have started filling any specific hard regs. */ - reg_parm_seen = 0; - for (i = 0; i < num_actuals; i++) - if (args[i].reg != 0 && ! args[i].pass_on_stack) - { - reg_parm_seen = 1; - - if (args[i].value == 0) - { - args[i].value = expand_expr (args[i].tree_value, NULL_RTX, - VOIDmode, 0); - preserve_temp_slots (args[i].value); - free_temp_slots (); - - /* ANSI doesn't require a sequence point here, - but PCC has one, so this will avoid some problems. */ - emit_queue (); - } - - /* If we are to promote the function arg to a wider mode, - do it now. */ - - if (GET_MODE (args[i].value) != VOIDmode - && GET_MODE (args[i].value) != args[i].mode) - args[i].value = convert_to_mode (args[i].mode, args[i].value, - args[i].unsignedp); - } - -#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE) - /* The argument list is the property of the called routine and it - may clobber it. If the fixed area has been used for previous - parameters, we must save and restore it. - - Here we compute the boundary of the that needs to be saved, if any. */ - -#ifdef ARGS_GROW_DOWNWARD - for (i = 0; i < reg_parm_stack_space + 1; i++) -#else - for (i = 0; i < reg_parm_stack_space; i++) -#endif - { - if (i >= highest_outgoing_arg_in_use - || stack_usage_map[i] == 0) - continue; - - if (low_to_save == -1) - low_to_save = i; - - high_to_save = i; - } - - if (low_to_save >= 0) - { - int num_to_save = high_to_save - low_to_save + 1; - enum machine_mode save_mode - = mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1); - rtx stack_area; - - /* If we don't have the required alignment, must do this in BLKmode. */ - if ((low_to_save & (MIN (GET_MODE_SIZE (save_mode), - BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1))) - save_mode = BLKmode; - - stack_area = gen_rtx (MEM, save_mode, - memory_address (save_mode, - -#ifdef ARGS_GROW_DOWNWARD - plus_constant (argblock, - - high_to_save) -#else - plus_constant (argblock, - low_to_save) -#endif - )); - if (save_mode == BLKmode) - { - save_area = assign_stack_temp (BLKmode, num_to_save, 1); - emit_block_move (validize_mem (save_area), stack_area, - GEN_INT (num_to_save), - PARM_BOUNDARY / BITS_PER_UNIT); - } - else - { - save_area = gen_reg_rtx (save_mode); - emit_move_insn (save_area, stack_area); - } - } -#endif - - - /* Now store (and compute if necessary) all non-register parms. - These come before register parms, since they can require block-moves, - which could clobber the registers used for register parms. - Parms which have partial registers are not stored here, - but we do preallocate space here if they want that. */ - - for (i = 0; i < num_actuals; i++) - if (args[i].reg == 0 || args[i].pass_on_stack) - store_one_arg (&args[i], argblock, may_be_alloca, - args_size.var != 0, fndecl, reg_parm_stack_space); - -#ifdef STRICT_ALIGNMENT - /* If we have a parm that is passed in registers but not in memory - and whose alignment does not permit a direct copy into registers, - make a group of pseudos that correspond to each register that we - will later fill. */ - - for (i = 0; i < num_actuals; i++) - if (args[i].reg != 0 && ! args[i].pass_on_stack - && args[i].mode == BLKmode - && (TYPE_ALIGN (TREE_TYPE (args[i].tree_value)) - < MIN (BIGGEST_ALIGNMENT, BITS_PER_WORD))) - { - int bytes = int_size_in_bytes (TREE_TYPE (args[i].tree_value)); - - args[i].n_aligned_regs - = args[i].partial ? args[i].partial - : (bytes + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD; - - args[i].aligned_regs = (rtx *) alloca (sizeof (rtx) - * args[i].n_aligned_regs); - - for (j = 0; j < args[i].n_aligned_regs; j++) - { - rtx reg = gen_reg_rtx (word_mode); - rtx word = operand_subword_force (args[i].value, j, BLKmode); - int bitsize = TYPE_ALIGN (TREE_TYPE (args[i].tree_value)); - int bitpos; - - args[i].aligned_regs[j] = reg; - - /* Clobber REG and move each partword into it. Ensure we don't - go past the end of the structure. Note that the loop below - works because we've already verified that padding - and endianness are compatible. */ - - emit_insn (gen_rtx (CLOBBER, VOIDmode, reg)); - - for (bitpos = 0; - bitpos < BITS_PER_WORD && bytes > 0; - bitpos += bitsize, bytes -= bitsize / BITS_PER_UNIT) - { - int xbitpos = (BYTES_BIG_ENDIAN - ? BITS_PER_WORD - bitpos - bitsize - : bitpos); - - store_bit_field (reg, bitsize, xbitpos, word_mode, - extract_bit_field (word, bitsize, xbitpos, 1, - NULL_RTX, word_mode, - word_mode, - bitsize / BITS_PER_UNIT, - BITS_PER_WORD), - bitsize / BITS_PER_UNIT, BITS_PER_WORD); - } - } - } -#endif - - /* Now store any partially-in-registers parm. - This is the last place a block-move can happen. */ - if (reg_parm_seen) - for (i = 0; i < num_actuals; i++) - if (args[i].partial != 0 && ! args[i].pass_on_stack) - store_one_arg (&args[i], argblock, may_be_alloca, - args_size.var != 0, fndecl, reg_parm_stack_space); - -#ifndef PUSH_ARGS_REVERSED -#ifdef STACK_BOUNDARY - /* If we pushed args in forward order, perform stack alignment - after pushing the last arg. */ - if (argblock == 0) - anti_adjust_stack (GEN_INT (args_size.constant - - original_args_size.constant)); -#endif -#endif - - /* If register arguments require space on the stack and stack space - was not preallocated, allocate stack space here for arguments - passed in registers. */ -#if ! defined(ALLOCATE_OUTGOING_ARGS) && defined(OUTGOING_REG_PARM_STACK_SPACE) - if (must_preallocate == 0 && reg_parm_stack_space > 0) - anti_adjust_stack (GEN_INT (reg_parm_stack_space)); -#endif - - /* Pass the function the address in which to return a structure value. */ - if (structure_value_addr && ! structure_value_addr_parm) - { - emit_move_insn (struct_value_rtx, - force_reg (Pmode, - force_operand (structure_value_addr, - NULL_RTX))); - if (GET_CODE (struct_value_rtx) == REG) - { - push_to_sequence (use_insns); - emit_insn (gen_rtx (USE, VOIDmode, struct_value_rtx)); - use_insns = get_insns (); - end_sequence (); - } - } - - /* Now do the register loads required for any wholly-register parms or any - parms which are passed both on the stack and in a register. Their - expressions were already evaluated. - - Mark all register-parms as living through the call, putting these USE - insns in a list headed by USE_INSNS. */ - - for (i = 0; i < num_actuals; i++) - { - rtx list = args[i].reg; - int partial = args[i].partial; - - while (list) - { - rtx reg; - int nregs; - - /* Process each register that needs to get this arg. */ - if (GET_CODE (list) == EXPR_LIST) - reg = XEXP (list, 0), list = XEXP (list, 1); - else - reg = list, list = 0; - - /* Set to non-zero if must move a word at a time, even if just one - word (e.g, partial == 1 && mode == DFmode). Set to zero if - we just use a normal move insn. */ - nregs = (partial ? partial - : (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode - ? ((int_size_in_bytes (TREE_TYPE (args[i].tree_value)) - + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD) - : 0)); - - /* If simple case, just do move. If normal partial, store_one_arg - has already loaded the register for us. In all other cases, - load the register(s) from memory. */ - - if (nregs == 0) - emit_move_insn (reg, args[i].value); - -#ifdef STRICT_ALIGNMENT - /* If we have pre-computed the values to put in the registers in - the case of non-aligned structures, copy them in now. */ - - else if (args[i].n_aligned_regs != 0) - for (j = 0; j < args[i].n_aligned_regs; j++) - emit_move_insn (gen_rtx (REG, word_mode, REGNO (reg) + j), - args[i].aligned_regs[j]); -#endif - - else if (args[i].partial == 0 || args[i].pass_on_stack) - move_block_to_reg (REGNO (reg), - validize_mem (args[i].value), nregs, - args[i].mode); - - push_to_sequence (use_insns); - if (nregs == 0) - emit_insn (gen_rtx (USE, VOIDmode, reg)); - else - use_regs (REGNO (reg), nregs); - use_insns = get_insns (); - end_sequence (); - - /* PARTIAL referred only to the first register, so clear it for the - next time. */ - partial = 0; - } - } - - /* Perform postincrements before actually calling the function. */ - emit_queue (); - - /* All arguments and registers used for the call must be set up by now! */ - - funexp = prepare_call_address (funexp, fndecl, &use_insns); - - /* Generate the actual call instruction. */ - emit_call_1 (funexp, funtype, args_size.constant, struct_value_size, - FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1), - valreg, old_inhibit_defer_pop, use_insns, is_const); - - /* If call is cse'able, make appropriate pair of reg-notes around it. - Test valreg so we don't crash; may safely ignore `const' - if return type is void. */ - if (is_const && valreg != 0) - { - rtx note = 0; - rtx temp = gen_reg_rtx (GET_MODE (valreg)); - rtx insns; - - /* Construct an "equal form" for the value which mentions all the - arguments in order as well as the function name. */ -#ifdef PUSH_ARGS_REVERSED - for (i = 0; i < num_actuals; i++) - note = gen_rtx (EXPR_LIST, VOIDmode, args[i].initial_value, note); -#else - for (i = num_actuals - 1; i >= 0; i--) - note = gen_rtx (EXPR_LIST, VOIDmode, args[i].initial_value, note); -#endif - note = gen_rtx (EXPR_LIST, VOIDmode, funexp, note); - - insns = get_insns (); - end_sequence (); - - emit_libcall_block (insns, temp, valreg, note); - - valreg = temp; - } - - /* For calls to `setjmp', etc., inform flow.c it should complain - if nonvolatile values are live. */ - - if (returns_twice) - { - emit_note (name, NOTE_INSN_SETJMP); - current_function_calls_setjmp = 1; - } - - if (is_longjmp) - current_function_calls_longjmp = 1; - - /* Notice functions that cannot return. - If optimizing, insns emitted below will be dead. - If not optimizing, they will exist, which is useful - if the user uses the `return' command in the debugger. */ - - if (is_volatile || is_longjmp) - emit_barrier (); - - /* If value type not void, return an rtx for the value. */ - - /* If there are cleanups to be called, don't use a hard reg as target. */ - if (cleanups_this_call != old_cleanups - && target && REG_P (target) - && REGNO (target) < FIRST_PSEUDO_REGISTER) - target = 0; - - if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode - || ignore) - { - target = const0_rtx; - } - else if (structure_value_addr) - { - if (target == 0 || GET_CODE (target) != MEM) - { - target = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), - memory_address (TYPE_MODE (TREE_TYPE (exp)), - structure_value_addr)); - MEM_IN_STRUCT_P (target) - = (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE - || TREE_CODE (TREE_TYPE (exp)) == QUAL_UNION_TYPE); - } - } - else if (pcc_struct_value) - { - if (target == 0) - { - target = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), - copy_to_reg (valreg)); - MEM_IN_STRUCT_P (target) - = (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE - || TREE_CODE (TREE_TYPE (exp)) == QUAL_UNION_TYPE); - } - else if (TYPE_MODE (TREE_TYPE (exp)) != BLKmode) - emit_move_insn (target, gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), - copy_to_reg (valreg))); - else - emit_block_move (target, gen_rtx (MEM, BLKmode, copy_to_reg (valreg)), - expr_size (exp), - TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); - } - else if (target && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp)) - && GET_MODE (target) == GET_MODE (valreg)) - /* TARGET and VALREG cannot be equal at this point because the latter - would not have REG_FUNCTION_VALUE_P true, while the former would if - it were referring to the same register. - - If they refer to the same register, this move will be a no-op, except - when function inlining is being done. */ - emit_move_insn (target, valreg); - else - target = copy_to_reg (valreg); - -#ifdef PROMOTE_FUNCTION_RETURN - /* If we promoted this return value, make the proper SUBREG. TARGET - might be const0_rtx here, so be careful. */ - if (GET_CODE (target) == REG - && GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp))) - { - enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp)); - int unsignedp = TREE_UNSIGNED (TREE_TYPE (exp)); - - if (TREE_CODE (TREE_TYPE (exp)) == INTEGER_TYPE - || TREE_CODE (TREE_TYPE (exp)) == ENUMERAL_TYPE - || TREE_CODE (TREE_TYPE (exp)) == BOOLEAN_TYPE - || TREE_CODE (TREE_TYPE (exp)) == CHAR_TYPE - || TREE_CODE (TREE_TYPE (exp)) == REAL_TYPE - || TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE - || TREE_CODE (TREE_TYPE (exp)) == OFFSET_TYPE) - { - PROMOTE_MODE (mode, unsignedp, TREE_TYPE (exp)); - } - - /* If we didn't promote as expected, something is wrong. */ - if (mode != GET_MODE (target)) - abort (); - - target = gen_rtx (SUBREG, TYPE_MODE (TREE_TYPE (exp)), target, 0); - SUBREG_PROMOTED_VAR_P (target) = 1; - SUBREG_PROMOTED_UNSIGNED_P (target) = unsignedp; - } -#endif - - /* Perform all cleanups needed for the arguments of this call - (i.e. destructors in C++). */ - expand_cleanups_to (old_cleanups); - - /* If size of args is variable or this was a constructor call for a stack - argument, restore saved stack-pointer value. */ - - if (old_stack_level) - { - emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX); - pending_stack_adjust = old_pending_adj; -#ifdef ACCUMULATE_OUTGOING_ARGS - stack_arg_under_construction = old_stack_arg_under_construction; - highest_outgoing_arg_in_use = initial_highest_arg_in_use; - stack_usage_map = initial_stack_usage_map; -#endif - } -#ifdef ACCUMULATE_OUTGOING_ARGS - else - { -#ifdef REG_PARM_STACK_SPACE - if (save_area) - { - enum machine_mode save_mode = GET_MODE (save_area); - rtx stack_area - = gen_rtx (MEM, save_mode, - memory_address (save_mode, -#ifdef ARGS_GROW_DOWNWARD - plus_constant (argblock, - high_to_save) -#else - plus_constant (argblock, low_to_save) -#endif - )); - - if (save_mode != BLKmode) - emit_move_insn (stack_area, save_area); - else - emit_block_move (stack_area, validize_mem (save_area), - GEN_INT (high_to_save - low_to_save + 1), - PARM_BOUNDARY / BITS_PER_UNIT); - } -#endif - - /* If we saved any argument areas, restore them. */ - for (i = 0; i < num_actuals; i++) - if (args[i].save_area) - { - enum machine_mode save_mode = GET_MODE (args[i].save_area); - rtx stack_area - = gen_rtx (MEM, save_mode, - memory_address (save_mode, - XEXP (args[i].stack_slot, 0))); - - if (save_mode != BLKmode) - emit_move_insn (stack_area, args[i].save_area); - else - emit_block_move (stack_area, validize_mem (args[i].save_area), - GEN_INT (args[i].size.constant), - PARM_BOUNDARY / BITS_PER_UNIT); - } - - highest_outgoing_arg_in_use = initial_highest_arg_in_use; - stack_usage_map = initial_stack_usage_map; - } -#endif - - /* If this was alloca, record the new stack level for nonlocal gotos. - Check for the handler slots since we might not have a save area - for non-local gotos. */ - - if (may_be_alloca && nonlocal_goto_handler_slot != 0) - emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX); - - pop_temp_slots (); - - return target; -} - -/* Output a library call to function FUN (a SYMBOL_REF rtx) - (emitting the queue unless NO_QUEUE is nonzero), - for a value of mode OUTMODE, - with NARGS different arguments, passed as alternating rtx values - and machine_modes to convert them to. - The rtx values should have been passed through protect_from_queue already. - - NO_QUEUE will be true if and only if the library call is a `const' call - which will be enclosed in REG_LIBCALL/REG_RETVAL notes; it is equivalent - to the variable is_const in expand_call. - - NO_QUEUE must be true for const calls, because if it isn't, then - any pending increment will be emitted between REG_LIBCALL/REG_RETVAL notes, - and will be lost if the libcall sequence is optimized away. - - NO_QUEUE must be false for non-const calls, because if it isn't, the - call insn will have its CONST_CALL_P bit set, and it will be incorrectly - optimized. For instance, the instruction scheduler may incorrectly - move memory references across the non-const call. */ - -void -emit_library_call (va_alist) - va_dcl -{ - va_list p; - /* Total size in bytes of all the stack-parms scanned so far. */ - struct args_size args_size; - /* Size of arguments before any adjustments (such as rounding). */ - struct args_size original_args_size; - register int argnum; - enum machine_mode outmode; - int nargs; - rtx fun; - rtx orgfun; - int inc; - int count; - rtx argblock = 0; - CUMULATIVE_ARGS args_so_far; - struct arg { rtx value; enum machine_mode mode; rtx reg; int partial; - struct args_size offset; struct args_size size; }; - struct arg *argvec; - int old_inhibit_defer_pop = inhibit_defer_pop; - int no_queue = 0; - rtx use_insns; - /* library calls are never indirect calls. */ - int current_call_is_indirect = 0; - - va_start (p); - orgfun = fun = va_arg (p, rtx); - no_queue = va_arg (p, int); - outmode = va_arg (p, enum machine_mode); - nargs = va_arg (p, int); - - /* Copy all the libcall-arguments out of the varargs data - and into a vector ARGVEC. - - Compute how to pass each argument. We only support a very small subset - of the full argument passing conventions to limit complexity here since - library functions shouldn't have many args. */ - - argvec = (struct arg *) alloca (nargs * sizeof (struct arg)); - - INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun); - - args_size.constant = 0; - args_size.var = 0; - - for (count = 0; count < nargs; count++) - { - rtx val = va_arg (p, rtx); - enum machine_mode mode = va_arg (p, enum machine_mode); - - /* We cannot convert the arg value to the mode the library wants here; - must do it earlier where we know the signedness of the arg. */ - if (mode == BLKmode - || (GET_MODE (val) != mode && GET_MODE (val) != VOIDmode)) - abort (); - - /* On some machines, there's no way to pass a float to a library fcn. - Pass it as a double instead. */ -#ifdef LIBGCC_NEEDS_DOUBLE - if (LIBGCC_NEEDS_DOUBLE && mode == SFmode) - val = convert_to_mode (DFmode, val, 0), mode = DFmode; -#endif - - /* There's no need to call protect_from_queue, because - either emit_move_insn or emit_push_insn will do that. */ - - /* Make sure it is a reasonable operand for a move or push insn. */ - if (GET_CODE (val) != REG && GET_CODE (val) != MEM - && ! (CONSTANT_P (val) && LEGITIMATE_CONSTANT_P (val))) - val = force_operand (val, NULL_RTX); - - argvec[count].value = val; - argvec[count].mode = mode; - -#ifdef FUNCTION_ARG_PASS_BY_REFERENCE - if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, mode, NULL_TREE, 1)) - abort (); -#endif - - argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1); - if (argvec[count].reg && GET_CODE (argvec[count].reg) == EXPR_LIST) - abort (); -#ifdef FUNCTION_ARG_PARTIAL_NREGS - argvec[count].partial - = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, NULL_TREE, 1); -#else - argvec[count].partial = 0; -#endif - - locate_and_pad_parm (mode, NULL_TREE, - argvec[count].reg && argvec[count].partial == 0, - NULL_TREE, &args_size, &argvec[count].offset, - &argvec[count].size); - - if (argvec[count].size.var) - abort (); - -#ifndef REG_PARM_STACK_SPACE - if (argvec[count].partial) - argvec[count].size.constant -= argvec[count].partial * UNITS_PER_WORD; -#endif - - if (argvec[count].reg == 0 || argvec[count].partial != 0 -#ifdef REG_PARM_STACK_SPACE - || 1 -#endif - ) - args_size.constant += argvec[count].size.constant; - -#ifdef ACCUMULATE_OUTGOING_ARGS - /* If this arg is actually passed on the stack, it might be - clobbering something we already put there (this library call might - be inside the evaluation of an argument to a function whose call - requires the stack). This will only occur when the library call - has sufficient args to run out of argument registers. Abort in - this case; if this ever occurs, code must be added to save and - restore the arg slot. */ - - if (argvec[count].reg == 0 || argvec[count].partial != 0) - abort (); -#endif - - FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree)0, 1); - } - va_end (p); - - /* If this machine requires an external definition for library - functions, write one out. */ - assemble_external_libcall (fun); - - original_args_size = args_size; -#ifdef STACK_BOUNDARY - args_size.constant = (((args_size.constant + (STACK_BYTES - 1)) - / STACK_BYTES) * STACK_BYTES); -#endif - -#ifdef REG_PARM_STACK_SPACE - args_size.constant = MAX (args_size.constant, - REG_PARM_STACK_SPACE (NULL_TREE)); -#ifndef OUTGOING_REG_PARM_STACK_SPACE - args_size.constant -= REG_PARM_STACK_SPACE (NULL_TREE); -#endif -#endif - -#ifdef ACCUMULATE_OUTGOING_ARGS - if (args_size.constant > current_function_outgoing_args_size) - current_function_outgoing_args_size = args_size.constant; - args_size.constant = 0; -#endif - -#ifndef PUSH_ROUNDING - argblock = push_block (GEN_INT (args_size.constant), 0, 0); -#endif - -#ifdef PUSH_ARGS_REVERSED -#ifdef STACK_BOUNDARY - /* If we push args individually in reverse order, perform stack alignment - before the first push (the last arg). */ - if (argblock == 0) - anti_adjust_stack (GEN_INT (args_size.constant - - original_args_size.constant)); -#endif -#endif - -#ifdef PUSH_ARGS_REVERSED - inc = -1; - argnum = nargs - 1; -#else - inc = 1; - argnum = 0; -#endif - - /* Push the args that need to be pushed. */ - - for (count = 0; count < nargs; count++, argnum += inc) - { - register enum machine_mode mode = argvec[argnum].mode; - register rtx val = argvec[argnum].value; - rtx reg = argvec[argnum].reg; - int partial = argvec[argnum].partial; - - if (! (reg != 0 && partial == 0)) - emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0, - argblock, GEN_INT (argvec[count].offset.constant)); - NO_DEFER_POP; - } - -#ifndef PUSH_ARGS_REVERSED -#ifdef STACK_BOUNDARY - /* If we pushed args in forward order, perform stack alignment - after pushing the last arg. */ - if (argblock == 0) - anti_adjust_stack (GEN_INT (args_size.constant - - original_args_size.constant)); -#endif -#endif - -#ifdef PUSH_ARGS_REVERSED - argnum = nargs - 1; -#else - argnum = 0; -#endif - - /* Now load any reg parms into their regs. */ - - for (count = 0; count < nargs; count++, argnum += inc) - { - register enum machine_mode mode = argvec[argnum].mode; - register rtx val = argvec[argnum].value; - rtx reg = argvec[argnum].reg; - int partial = argvec[argnum].partial; - - if (reg != 0 && partial == 0) - emit_move_insn (reg, val); - NO_DEFER_POP; - } - - /* For version 1.37, try deleting this entirely. */ - if (! no_queue) - emit_queue (); - - /* Any regs containing parms remain in use through the call. */ - start_sequence (); - for (count = 0; count < nargs; count++) - if (argvec[count].reg != 0) - emit_insn (gen_rtx (USE, VOIDmode, argvec[count].reg)); - - use_insns = get_insns (); - end_sequence (); - - fun = prepare_call_address (fun, NULL_TREE, &use_insns); - - /* Don't allow popping to be deferred, since then - cse'ing of library calls could delete a call and leave the pop. */ - NO_DEFER_POP; - - /* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which - will set inhibit_defer_pop to that value. */ - - emit_call_1 (fun, get_identifier (XSTR (orgfun, 0)), args_size.constant, 0, - FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1), - outmode != VOIDmode ? hard_libcall_value (outmode) : NULL_RTX, - old_inhibit_defer_pop + 1, use_insns, no_queue); - - /* Now restore inhibit_defer_pop to its actual original value. */ - OK_DEFER_POP; -} - -/* Like emit_library_call except that an extra argument, VALUE, - comes second and says where to store the result. - (If VALUE is zero, the result comes in the function value register.) */ - -void -emit_library_call_value (va_alist) - va_dcl -{ - va_list p; - /* Total size in bytes of all the stack-parms scanned so far. */ - struct args_size args_size; - /* Size of arguments before any adjustments (such as rounding). */ - struct args_size original_args_size; - register int argnum; - enum machine_mode outmode; - int nargs; - rtx fun; - rtx orgfun; - int inc; - int count; - rtx argblock = 0; - CUMULATIVE_ARGS args_so_far; - struct arg { rtx value; enum machine_mode mode; rtx reg; int partial; - struct args_size offset; struct args_size size; }; - struct arg *argvec; - int old_inhibit_defer_pop = inhibit_defer_pop; - int no_queue = 0; - rtx use_insns; - rtx value; - rtx mem_value = 0; - /* library calls are never indirect calls. */ - int current_call_is_indirect = 0; - - va_start (p); - orgfun = fun = va_arg (p, rtx); - value = va_arg (p, rtx); - no_queue = va_arg (p, int); - outmode = va_arg (p, enum machine_mode); - nargs = va_arg (p, int); - - /* If this kind of value comes back in memory, - decide where in memory it should come back. */ - if (RETURN_IN_MEMORY (type_for_mode (outmode, 0))) - { - if (GET_CODE (value) == MEM) - mem_value = value; - else - mem_value = assign_stack_temp (outmode, GET_MODE_SIZE (outmode), 0); - } - - /* ??? Unfinished: must pass the memory address as an argument. */ - - /* Copy all the libcall-arguments out of the varargs data - and into a vector ARGVEC. - - Compute how to pass each argument. We only support a very small subset - of the full argument passing conventions to limit complexity here since - library functions shouldn't have many args. */ - - argvec = (struct arg *) alloca ((nargs + 1) * sizeof (struct arg)); - - INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun); - - args_size.constant = 0; - args_size.var = 0; - - count = 0; - - /* If there's a structure value address to be passed, - either pass it in the special place, or pass it as an extra argument. */ - if (mem_value) - { - rtx addr = XEXP (mem_value, 0); - - if (! struct_value_rtx) - { - nargs++; - - /* Make sure it is a reasonable operand for a move or push insn. */ - if (GET_CODE (addr) != REG && GET_CODE (addr) != MEM - && ! (CONSTANT_P (addr) && LEGITIMATE_CONSTANT_P (addr))) - addr = force_operand (addr, NULL_RTX); - - argvec[count].value = addr; - argvec[count].mode = outmode; - argvec[count].partial = 0; - - argvec[count].reg = FUNCTION_ARG (args_so_far, outmode, NULL_TREE, 1); -#ifdef FUNCTION_ARG_PARTIAL_NREGS - if (FUNCTION_ARG_PARTIAL_NREGS (args_so_far, outmode, NULL_TREE, 1)) - abort (); -#endif - - locate_and_pad_parm (outmode, NULL_TREE, - argvec[count].reg && argvec[count].partial == 0, - NULL_TREE, &args_size, &argvec[count].offset, - &argvec[count].size); - - - if (argvec[count].reg == 0 || argvec[count].partial != 0 -#ifdef REG_PARM_STACK_SPACE - || 1 -#endif - ) - args_size.constant += argvec[count].size.constant; - - FUNCTION_ARG_ADVANCE (args_so_far, outmode, (tree)0, 1); - } - } - - for (; count < nargs; count++) - { - rtx val = va_arg (p, rtx); - enum machine_mode mode = va_arg (p, enum machine_mode); - - /* We cannot convert the arg value to the mode the library wants here; - must do it earlier where we know the signedness of the arg. */ - if (mode == BLKmode - || (GET_MODE (val) != mode && GET_MODE (val) != VOIDmode)) - abort (); - - /* On some machines, there's no way to pass a float to a library fcn. - Pass it as a double instead. */ -#ifdef LIBGCC_NEEDS_DOUBLE - if (LIBGCC_NEEDS_DOUBLE && mode == SFmode) - val = convert_to_mode (DFmode, val, 0), mode = DFmode; -#endif - - /* There's no need to call protect_from_queue, because - either emit_move_insn or emit_push_insn will do that. */ - - /* Make sure it is a reasonable operand for a move or push insn. */ - if (GET_CODE (val) != REG && GET_CODE (val) != MEM - && ! (CONSTANT_P (val) && LEGITIMATE_CONSTANT_P (val))) - val = force_operand (val, NULL_RTX); - - argvec[count].value = val; - argvec[count].mode = mode; - -#ifdef FUNCTION_ARG_PASS_BY_REFERENCE - if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, mode, NULL_TREE, 1)) - abort (); -#endif - - argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1); - if (argvec[count].reg && GET_CODE (argvec[count].reg) == EXPR_LIST) - abort (); -#ifdef FUNCTION_ARG_PARTIAL_NREGS - argvec[count].partial - = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, NULL_TREE, 1); -#else - argvec[count].partial = 0; -#endif - - locate_and_pad_parm (mode, NULL_TREE, - argvec[count].reg && argvec[count].partial == 0, - NULL_TREE, &args_size, &argvec[count].offset, - &argvec[count].size); - - if (argvec[count].size.var) - abort (); - -#ifndef REG_PARM_STACK_SPACE - if (argvec[count].partial) - argvec[count].size.constant -= argvec[count].partial * UNITS_PER_WORD; -#endif - - if (argvec[count].reg == 0 || argvec[count].partial != 0 -#ifdef REG_PARM_STACK_SPACE - || 1 -#endif - ) - args_size.constant += argvec[count].size.constant; - -#ifdef ACCUMULATE_OUTGOING_ARGS - /* If this arg is actually passed on the stack, it might be - clobbering something we already put there (this library call might - be inside the evaluation of an argument to a function whose call - requires the stack). This will only occur when the library call - has sufficient args to run out of argument registers. Abort in - this case; if this ever occurs, code must be added to save and - restore the arg slot. */ - - if (argvec[count].reg == 0 || argvec[count].partial != 0) - abort (); -#endif - - FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree)0, 1); - } - va_end (p); - - /* If this machine requires an external definition for library - functions, write one out. */ - assemble_external_libcall (fun); - - original_args_size = args_size; -#ifdef STACK_BOUNDARY - args_size.constant = (((args_size.constant + (STACK_BYTES - 1)) - / STACK_BYTES) * STACK_BYTES); -#endif - -#ifdef REG_PARM_STACK_SPACE - args_size.constant = MAX (args_size.constant, - REG_PARM_STACK_SPACE (NULL_TREE)); -#ifndef OUTGOING_REG_PARM_STACK_SPACE - args_size.constant -= REG_PARM_STACK_SPACE (NULL_TREE); -#endif -#endif - -#ifdef ACCUMULATE_OUTGOING_ARGS - if (args_size.constant > current_function_outgoing_args_size) - current_function_outgoing_args_size = args_size.constant; - args_size.constant = 0; -#endif - -#ifndef PUSH_ROUNDING - argblock = push_block (GEN_INT (args_size.constant), 0, 0); -#endif - -#ifdef PUSH_ARGS_REVERSED -#ifdef STACK_BOUNDARY - /* If we push args individually in reverse order, perform stack alignment - before the first push (the last arg). */ - if (argblock == 0) - anti_adjust_stack (GEN_INT (args_size.constant - - original_args_size.constant)); -#endif -#endif - -#ifdef PUSH_ARGS_REVERSED - inc = -1; - argnum = nargs - 1; -#else - inc = 1; - argnum = 0; -#endif - - /* Push the args that need to be pushed. */ - - for (count = 0; count < nargs; count++, argnum += inc) - { - register enum machine_mode mode = argvec[argnum].mode; - register rtx val = argvec[argnum].value; - rtx reg = argvec[argnum].reg; - int partial = argvec[argnum].partial; - - if (! (reg != 0 && partial == 0)) - emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0, - argblock, GEN_INT (argvec[count].offset.constant)); - NO_DEFER_POP; - } - -#ifndef PUSH_ARGS_REVERSED -#ifdef STACK_BOUNDARY - /* If we pushed args in forward order, perform stack alignment - after pushing the last arg. */ - if (argblock == 0) - anti_adjust_stack (GEN_INT (args_size.constant - - original_args_size.constant)); -#endif -#endif - -#ifdef PUSH_ARGS_REVERSED - argnum = nargs - 1; -#else - argnum = 0; -#endif - - /* Now load any reg parms into their regs. */ - - if (mem_value != 0 && struct_value_rtx != 0) - emit_move_insn (struct_value_rtx, XEXP (mem_value, 0)); - - for (count = 0; count < nargs; count++, argnum += inc) - { - register enum machine_mode mode = argvec[argnum].mode; - register rtx val = argvec[argnum].value; - rtx reg = argvec[argnum].reg; - int partial = argvec[argnum].partial; - - if (reg != 0 && partial == 0) - emit_move_insn (reg, val); - NO_DEFER_POP; - } - -#if 0 - /* For version 1.37, try deleting this entirely. */ - if (! no_queue) - emit_queue (); -#endif - - /* Any regs containing parms remain in use through the call. */ - start_sequence (); - for (count = 0; count < nargs; count++) - if (argvec[count].reg != 0) - emit_insn (gen_rtx (USE, VOIDmode, argvec[count].reg)); - - use_insns = get_insns (); - end_sequence (); - - fun = prepare_call_address (fun, NULL_TREE, &use_insns); - - /* Don't allow popping to be deferred, since then - cse'ing of library calls could delete a call and leave the pop. */ - NO_DEFER_POP; - - /* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which - will set inhibit_defer_pop to that value. */ - - emit_call_1 (fun, get_identifier (XSTR (orgfun, 0)), args_size.constant, 0, - FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1), - outmode != VOIDmode ? hard_libcall_value (outmode) : NULL_RTX, - old_inhibit_defer_pop + 1, use_insns, no_queue); - - /* Now restore inhibit_defer_pop to its actual original value. */ - OK_DEFER_POP; - - /* Copy the value to the right place. */ - if (outmode != VOIDmode) - { - if (mem_value) - { - if (value == 0) - value = hard_libcall_value (outmode); - if (value != mem_value) - emit_move_insn (value, mem_value); - } - else if (value != 0) - emit_move_insn (value, hard_libcall_value (outmode)); - } -} - -#if 0 -/* Return an rtx which represents a suitable home on the stack - given TYPE, the type of the argument looking for a home. - This is called only for BLKmode arguments. - - SIZE is the size needed for this target. - ARGS_ADDR is the address of the bottom of the argument block for this call. - OFFSET describes this parameter's offset into ARGS_ADDR. It is meaningless - if this machine uses push insns. */ - -static rtx -target_for_arg (type, size, args_addr, offset) - tree type; - rtx size; - rtx args_addr; - struct args_size offset; -{ - rtx target; - rtx offset_rtx = ARGS_SIZE_RTX (offset); - - /* We do not call memory_address if possible, - because we want to address as close to the stack - as possible. For non-variable sized arguments, - this will be stack-pointer relative addressing. */ - if (GET_CODE (offset_rtx) == CONST_INT) - target = plus_constant (args_addr, INTVAL (offset_rtx)); - else - { - /* I have no idea how to guarantee that this - will work in the presence of register parameters. */ - target = gen_rtx (PLUS, Pmode, args_addr, offset_rtx); - target = memory_address (QImode, target); - } - - return gen_rtx (MEM, BLKmode, target); -} -#endif - -/* Store a single argument for a function call - into the register or memory area where it must be passed. - *ARG describes the argument value and where to pass it. - - ARGBLOCK is the address of the stack-block for all the arguments, - or 0 on a machine where arguments are pushed individually. - - MAY_BE_ALLOCA nonzero says this could be a call to `alloca' - so must be careful about how the stack is used. - - VARIABLE_SIZE nonzero says that this was a variable-sized outgoing - argument stack. This is used if ACCUMULATE_OUTGOING_ARGS to indicate - that we need not worry about saving and restoring the stack. - - FNDECL is the declaration of the function we are calling. */ - -static void -store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl, - reg_parm_stack_space) - struct arg_data *arg; - rtx argblock; - int may_be_alloca; - int variable_size; - tree fndecl; - int reg_parm_stack_space; -{ - register tree pval = arg->tree_value; - rtx reg = 0; - int partial = 0; - int used = 0; - int i, lower_bound, upper_bound; - - if (TREE_CODE (pval) == ERROR_MARK) - return; - -#ifdef ACCUMULATE_OUTGOING_ARGS - /* If this is being stored into a pre-allocated, fixed-size, stack area, - save any previous data at that location. */ - if (argblock && ! variable_size && arg->stack) - { -#ifdef ARGS_GROW_DOWNWARD - /* stack_slot is negative, but we want to index stack_usage_map */ - /* with positive values. */ - if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS) - upper_bound = -INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1)) + 1; - else - abort (); - - lower_bound = upper_bound - arg->size.constant; -#else - if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS) - lower_bound = INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1)); - else - lower_bound = 0; - - upper_bound = lower_bound + arg->size.constant; -#endif - - for (i = lower_bound; i < upper_bound; i++) - if (stack_usage_map[i] -#ifdef REG_PARM_STACK_SPACE - /* Don't store things in the fixed argument area at this point; - it has already been saved. */ - && i > reg_parm_stack_space -#endif - ) - break; - - if (i != upper_bound) - { - /* We need to make a save area. See what mode we can make it. */ - enum machine_mode save_mode - = mode_for_size (arg->size.constant * BITS_PER_UNIT, MODE_INT, 1); - rtx stack_area - = gen_rtx (MEM, save_mode, - memory_address (save_mode, XEXP (arg->stack_slot, 0))); - - if (save_mode == BLKmode) - { - arg->save_area = assign_stack_temp (BLKmode, - arg->size.constant, 1); - emit_block_move (validize_mem (arg->save_area), stack_area, - GEN_INT (arg->size.constant), - PARM_BOUNDARY / BITS_PER_UNIT); - } - else - { - arg->save_area = gen_reg_rtx (save_mode); - emit_move_insn (arg->save_area, stack_area); - } - } - } -#endif - - /* If this isn't going to be placed on both the stack and in registers, - set up the register and number of words. */ - if (! arg->pass_on_stack) - reg = arg->reg, partial = arg->partial; - - if (reg != 0 && partial == 0) - /* Being passed entirely in a register. We shouldn't be called in - this case. */ - abort (); - -#ifdef STRICT_ALIGNMENT - /* If this arg needs special alignment, don't load the registers - here. */ - if (arg->n_aligned_regs != 0) - reg = 0; -#endif - - /* If this is being partially passed in a register, but multiple locations - are specified, we assume that the one partially used is the one that is - listed first. */ - if (reg && GET_CODE (reg) == EXPR_LIST) - reg = XEXP (reg, 0); - - /* If this is being passed partially in a register, we can't evaluate - it directly into its stack slot. Otherwise, we can. */ - if (arg->value == 0) - { -#ifdef ACCUMULATE_OUTGOING_ARGS - /* stack_arg_under_construction is nonzero if a function argument is - being evaluated directly into the outgoing argument list and - expand_call must take special action to preserve the argument list - if it is called recursively. - - For scalar function arguments stack_usage_map is sufficient to - determine which stack slots must be saved and restored. Scalar - arguments in general have pass_on_stack == 0. - - If this argument is initialized by a function which takes the - address of the argument (a C++ constructor or a C function - returning a BLKmode structure), then stack_usage_map is - insufficient and expand_call must push the stack around the - function call. Such arguments have pass_on_stack == 1. - - Note that it is always safe to set stack_arg_under_construction, - but this generates suboptimal code if set when not needed. */ - - if (arg->pass_on_stack) - stack_arg_under_construction++; -#endif - arg->value = expand_expr (pval, partial ? NULL_RTX : arg->stack, - VOIDmode, 0); - - /* If we are promoting object (or for any other reason) the mode - doesn't agree, convert the mode. */ - - if (GET_MODE (arg->value) != VOIDmode - && GET_MODE (arg->value) != arg->mode) - arg->value = convert_to_mode (arg->mode, arg->value, arg->unsignedp); - -#ifdef ACCUMULATE_OUTGOING_ARGS - if (arg->pass_on_stack) - stack_arg_under_construction--; -#endif - } - - /* Don't allow anything left on stack from computation - of argument to alloca. */ - if (may_be_alloca) - do_pending_stack_adjust (); - - if (arg->value == arg->stack) - /* If the value is already in the stack slot, we are done. */ - ; - else if (arg->mode != BLKmode) - { - register int size; - - /* Argument is a scalar, not entirely passed in registers. - (If part is passed in registers, arg->partial says how much - and emit_push_insn will take care of putting it there.) - - Push it, and if its size is less than the - amount of space allocated to it, - also bump stack pointer by the additional space. - Note that in C the default argument promotions - will prevent such mismatches. */ - - size = GET_MODE_SIZE (arg->mode); - /* Compute how much space the push instruction will push. - On many machines, pushing a byte will advance the stack - pointer by a halfword. */ -#ifdef PUSH_ROUNDING - size = PUSH_ROUNDING (size); -#endif - used = size; - - /* Compute how much space the argument should get: - round up to a multiple of the alignment for arguments. */ - if (none != FUNCTION_ARG_PADDING (arg->mode, TREE_TYPE (pval))) - used = (((size + PARM_BOUNDARY / BITS_PER_UNIT - 1) - / (PARM_BOUNDARY / BITS_PER_UNIT)) - * (PARM_BOUNDARY / BITS_PER_UNIT)); - - /* This isn't already where we want it on the stack, so put it there. - This can either be done with push or copy insns. */ - emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX, - 0, partial, reg, used - size, - argblock, ARGS_SIZE_RTX (arg->offset)); - } - else - { - /* BLKmode, at least partly to be pushed. */ - - register int excess; - rtx size_rtx; - - /* Pushing a nonscalar. - If part is passed in registers, PARTIAL says how much - and emit_push_insn will take care of putting it there. */ - - /* Round its size up to a multiple - of the allocation unit for arguments. */ - - if (arg->size.var != 0) - { - excess = 0; - size_rtx = ARGS_SIZE_RTX (arg->size); - } - else - { - /* PUSH_ROUNDING has no effect on us, because - emit_push_insn for BLKmode is careful to avoid it. */ - excess = (arg->size.constant - int_size_in_bytes (TREE_TYPE (pval)) - + partial * UNITS_PER_WORD); - size_rtx = expr_size (pval); - } - - emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx, - TYPE_ALIGN (TREE_TYPE (pval)) / BITS_PER_UNIT, partial, - reg, excess, argblock, ARGS_SIZE_RTX (arg->offset)); - } - - - /* Unless this is a partially-in-register argument, the argument is now - in the stack. - - ??? Note that this can change arg->value from arg->stack to - arg->stack_slot and it matters when they are not the same. - It isn't totally clear that this is correct in all cases. */ - if (partial == 0) - arg->value = arg->stack_slot; - - /* Once we have pushed something, pops can't safely - be deferred during the rest of the arguments. */ - NO_DEFER_POP; - - /* ANSI doesn't require a sequence point here, - but PCC has one, so this will avoid some problems. */ - emit_queue (); - - /* Free any temporary slots made in processing this argument. */ - free_temp_slots (); - -#ifdef ACCUMULATE_OUTGOING_ARGS - /* Now mark the segment we just used. */ - if (argblock && ! variable_size && arg->stack) - for (i = lower_bound; i < upper_bound; i++) - stack_usage_map[i] = 1; -#endif -} diff --git a/gnu/usr.bin/cc/common/combine.c b/gnu/usr.bin/cc/common/combine.c deleted file mode 100644 index 5ac961f184..0000000000 --- a/gnu/usr.bin/cc/common/combine.c +++ /dev/null @@ -1,10025 +0,0 @@ -/* Optimize by combining instructions for GNU compiler. - Copyright (C) 1987, 1988, 1992, 1993 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This module is essentially the "combiner" phase of the U. of Arizona - Portable Optimizer, but redone to work on our list-structured - representation for RTL instead of their string representation. - - The LOG_LINKS of each insn identify the most recent assignment - to each REG used in the insn. It is a list of previous insns, - each of which contains a SET for a REG that is used in this insn - and not used or set in between. LOG_LINKs never cross basic blocks. - They were set up by the preceding pass (lifetime analysis). - - We try to combine each pair of insns joined by a logical link. - We also try to combine triples of insns A, B and C when - C has a link back to B and B has a link back to A. - - LOG_LINKS does not have links for use of the CC0. They don't - need to, because the insn that sets the CC0 is always immediately - before the insn that tests it. So we always regard a branch - insn as having a logical link to the preceding insn. The same is true - for an insn explicitly using CC0. - - We check (with use_crosses_set_p) to avoid combining in such a way - as to move a computation to a place where its value would be different. - - Combination is done by mathematically substituting the previous - insn(s) values for the regs they set into the expressions in - the later insns that refer to these regs. If the result is a valid insn - for our target machine, according to the machine description, - we install it, delete the earlier insns, and update the data flow - information (LOG_LINKS and REG_NOTES) for what we did. - - There are a few exceptions where the dataflow information created by - flow.c aren't completely updated: - - - reg_live_length is not updated - - reg_n_refs is not adjusted in the rare case when a register is - no longer required in a computation - - there are extremely rare cases (see distribute_regnotes) when a - REG_DEAD note is lost - - a LOG_LINKS entry that refers to an insn with multiple SETs may be - removed because there is no way to know which register it was - linking - - To simplify substitution, we combine only when the earlier insn(s) - consist of only a single assignment. To simplify updating afterward, - we never combine when a subroutine call appears in the middle. - - Since we do not represent assignments to CC0 explicitly except when that - is all an insn does, there is no LOG_LINKS entry in an insn that uses - the condition code for the insn that set the condition code. - Fortunately, these two insns must be consecutive. - Therefore, every JUMP_INSN is taken to have an implicit logical link - to the preceding insn. This is not quite right, since non-jumps can - also use the condition code; but in practice such insns would not - combine anyway. */ - -#include "config.h" -#include "gvarargs.h" -#include "rtl.h" -#include "flags.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "expr.h" -#include "basic-block.h" -#include "insn-config.h" -#include "insn-flags.h" -#include "insn-codes.h" -#include "insn-attr.h" -#include "recog.h" -#include "real.h" -#include - -/* It is not safe to use ordinary gen_lowpart in combine. - Use gen_lowpart_for_combine instead. See comments there. */ -#define gen_lowpart dont_use_gen_lowpart_you_dummy - -/* If byte loads either zero- or sign- extend, define BYTE_LOADS_EXTEND - for cases when we don't care which is true. Define LOAD_EXTEND to - be ZERO_EXTEND or SIGN_EXTEND, depending on which was defined. */ - -#ifdef BYTE_LOADS_ZERO_EXTEND -#define BYTE_LOADS_EXTEND -#define LOAD_EXTEND ZERO_EXTEND -#endif - -#ifdef BYTE_LOADS_SIGN_EXTEND -#define BYTE_LOADS_EXTEND -#define LOAD_EXTEND SIGN_EXTEND -#endif - -/* Number of attempts to combine instructions in this function. */ - -static int combine_attempts; - -/* Number of attempts that got as far as substitution in this function. */ - -static int combine_merges; - -/* Number of instructions combined with added SETs in this function. */ - -static int combine_extras; - -/* Number of instructions combined in this function. */ - -static int combine_successes; - -/* Totals over entire compilation. */ - -static int total_attempts, total_merges, total_extras, total_successes; - -/* Vector mapping INSN_UIDs to cuids. - The cuids are like uids but increase monotonically always. - Combine always uses cuids so that it can compare them. - But actually renumbering the uids, which we used to do, - proves to be a bad idea because it makes it hard to compare - the dumps produced by earlier passes with those from later passes. */ - -static int *uid_cuid; - -/* Get the cuid of an insn. */ - -#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)]) - -/* Maximum register number, which is the size of the tables below. */ - -static int combine_max_regno; - -/* Record last point of death of (hard or pseudo) register n. */ - -static rtx *reg_last_death; - -/* Record last point of modification of (hard or pseudo) register n. */ - -static rtx *reg_last_set; - -/* Record the cuid of the last insn that invalidated memory - (anything that writes memory, and subroutine calls, but not pushes). */ - -static int mem_last_set; - -/* Record the cuid of the last CALL_INSN - so we can tell whether a potential combination crosses any calls. */ - -static int last_call_cuid; - -/* When `subst' is called, this is the insn that is being modified - (by combining in a previous insn). The PATTERN of this insn - is still the old pattern partially modified and it should not be - looked at, but this may be used to examine the successors of the insn - to judge whether a simplification is valid. */ - -static rtx subst_insn; - -/* This is the lowest CUID that `subst' is currently dealing with. - get_last_value will not return a value if the register was set at or - after this CUID. If not for this mechanism, we could get confused if - I2 or I1 in try_combine were an insn that used the old value of a register - to obtain a new value. In that case, we might erroneously get the - new value of the register when we wanted the old one. */ - -static int subst_low_cuid; - -/* This is the value of undobuf.num_undo when we started processing this - substitution. This will prevent gen_rtx_combine from re-used a piece - from the previous expression. Doing so can produce circular rtl - structures. */ - -static int previous_num_undos; - -/* The next group of arrays allows the recording of the last value assigned - to (hard or pseudo) register n. We use this information to see if a - operation being processed is redundant given a prior operation performed - on the register. For example, an `and' with a constant is redundant if - all the zero bits are already known to be turned off. - - We use an approach similar to that used by cse, but change it in the - following ways: - - (1) We do not want to reinitialize at each label. - (2) It is useful, but not critical, to know the actual value assigned - to a register. Often just its form is helpful. - - Therefore, we maintain the following arrays: - - reg_last_set_value the last value assigned - reg_last_set_label records the value of label_tick when the - register was assigned - reg_last_set_table_tick records the value of label_tick when a - value using the register is assigned - reg_last_set_invalid set to non-zero when it is not valid - to use the value of this register in some - register's value - - To understand the usage of these tables, it is important to understand - the distinction between the value in reg_last_set_value being valid - and the register being validly contained in some other expression in the - table. - - Entry I in reg_last_set_value is valid if it is non-zero, and either - reg_n_sets[i] is 1 or reg_last_set_label[i] == label_tick. - - Register I may validly appear in any expression returned for the value - of another register if reg_n_sets[i] is 1. It may also appear in the - value for register J if reg_last_set_label[i] < reg_last_set_label[j] or - reg_last_set_invalid[j] is zero. - - If an expression is found in the table containing a register which may - not validly appear in an expression, the register is replaced by - something that won't match, (clobber (const_int 0)). - - reg_last_set_invalid[i] is set non-zero when register I is being assigned - to and reg_last_set_table_tick[i] == label_tick. */ - -/* Record last value assigned to (hard or pseudo) register n. */ - -static rtx *reg_last_set_value; - -/* Record the value of label_tick when the value for register n is placed in - reg_last_set_value[n]. */ - -static int *reg_last_set_label; - -/* Record the value of label_tick when an expression involving register n - is placed in reg_last_set_value. */ - -static int *reg_last_set_table_tick; - -/* Set non-zero if references to register n in expressions should not be - used. */ - -static char *reg_last_set_invalid; - -/* Incremented for each label. */ - -static int label_tick; - -/* Some registers that are set more than once and used in more than one - basic block are nevertheless always set in similar ways. For example, - a QImode register may be loaded from memory in two places on a machine - where byte loads zero extend. - - We record in the following array what we know about the nonzero - bits of a register, specifically which bits are known to be zero. - - If an entry is zero, it means that we don't know anything special. */ - -static unsigned HOST_WIDE_INT *reg_nonzero_bits; - -/* Mode used to compute significance in reg_nonzero_bits. It is the largest - integer mode that can fit in HOST_BITS_PER_WIDE_INT. */ - -static enum machine_mode nonzero_bits_mode; - -/* Nonzero if we know that a register has some leading bits that are always - equal to the sign bit. */ - -static char *reg_sign_bit_copies; - -/* Nonzero when reg_nonzero_bits and reg_sign_bit_copies can be safely used. - It is zero while computing them and after combine has completed. This - former test prevents propagating values based on previously set values, - which can be incorrect if a variable is modified in a loop. */ - -static int nonzero_sign_valid; - -/* These arrays are maintained in parallel with reg_last_set_value - and are used to store the mode in which the register was last set, - the bits that were known to be zero when it was last set, and the - number of sign bits copies it was known to have when it was last set. */ - -static enum machine_mode *reg_last_set_mode; -static unsigned HOST_WIDE_INT *reg_last_set_nonzero_bits; -static char *reg_last_set_sign_bit_copies; - -/* Record one modification to rtl structure - to be undone by storing old_contents into *where. - is_int is 1 if the contents are an int. */ - -struct undo -{ - int is_int; - union {rtx rtx; int i;} old_contents; - union {rtx *rtx; int *i;} where; -}; - -/* Record a bunch of changes to be undone, up to MAX_UNDO of them. - num_undo says how many are currently recorded. - - storage is nonzero if we must undo the allocation of new storage. - The value of storage is what to pass to obfree. - - other_insn is nonzero if we have modified some other insn in the process - of working on subst_insn. It must be verified too. */ - -#define MAX_UNDO 50 - -struct undobuf -{ - int num_undo; - char *storage; - struct undo undo[MAX_UNDO]; - rtx other_insn; -}; - -static struct undobuf undobuf; - -/* Substitute NEWVAL, an rtx expression, into INTO, a place in some - insn. The substitution can be undone by undo_all. If INTO is already - set to NEWVAL, do not record this change. Because computing NEWVAL might - also call SUBST, we have to compute it before we put anything into - the undo table. */ - -#define SUBST(INTO, NEWVAL) \ - do { rtx _new = (NEWVAL); \ - if (undobuf.num_undo < MAX_UNDO) \ - { \ - undobuf.undo[undobuf.num_undo].is_int = 0; \ - undobuf.undo[undobuf.num_undo].where.rtx = &INTO; \ - undobuf.undo[undobuf.num_undo].old_contents.rtx = INTO; \ - INTO = _new; \ - if (undobuf.undo[undobuf.num_undo].old_contents.rtx != INTO) \ - undobuf.num_undo++; \ - } \ - } while (0) - -/* Similar to SUBST, but NEWVAL is an int. INTO will normally be an XINT - expression. - Note that substitution for the value of a CONST_INT is not safe. */ - -#define SUBST_INT(INTO, NEWVAL) \ - do { if (undobuf.num_undo < MAX_UNDO) \ -{ \ - undobuf.undo[undobuf.num_undo].is_int = 1; \ - undobuf.undo[undobuf.num_undo].where.i = (int *) &INTO; \ - undobuf.undo[undobuf.num_undo].old_contents.i = INTO; \ - INTO = NEWVAL; \ - if (undobuf.undo[undobuf.num_undo].old_contents.i != INTO) \ - undobuf.num_undo++; \ - } \ - } while (0) - -/* Number of times the pseudo being substituted for - was found and replaced. */ - -static int n_occurrences; - -static void set_nonzero_bits_and_sign_copies (); -static void setup_incoming_promotions (); -static void move_deaths (); -rtx remove_death (); -static void record_value_for_reg (); -static void record_dead_and_set_regs (); -static int use_crosses_set_p (); -static rtx try_combine (); -static rtx *find_split_point (); -static rtx subst (); -static void undo_all (); -static int reg_dead_at_p (); -static rtx expand_compound_operation (); -static rtx expand_field_assignment (); -static rtx make_extraction (); -static int get_pos_from_mask (); -static rtx force_to_mode (); -static rtx known_cond (); -static rtx make_field_assignment (); -static rtx make_compound_operation (); -static rtx apply_distributive_law (); -static rtx simplify_and_const_int (); -static unsigned HOST_WIDE_INT nonzero_bits (); -static int num_sign_bit_copies (); -static int merge_outer_ops (); -static rtx simplify_shift_const (); -static int recog_for_combine (); -static rtx gen_lowpart_for_combine (); -static rtx gen_rtx_combine (); -static rtx gen_binary (); -static rtx gen_unary (); -static enum rtx_code simplify_comparison (); -static int reversible_comparison_p (); -static int get_last_value_validate (); -static rtx get_last_value (); -static void distribute_notes (); -static void distribute_links (); - -/* Main entry point for combiner. F is the first insn of the function. - NREGS is the first unused pseudo-reg number. */ - -void -combine_instructions (f, nregs) - rtx f; - int nregs; -{ - register rtx insn, next, prev; - register int i; - register rtx links, nextlinks; - - combine_attempts = 0; - combine_merges = 0; - combine_extras = 0; - combine_successes = 0; - undobuf.num_undo = previous_num_undos = 0; - - combine_max_regno = nregs; - - reg_last_death = (rtx *) alloca (nregs * sizeof (rtx)); - reg_last_set = (rtx *) alloca (nregs * sizeof (rtx)); - reg_last_set_value = (rtx *) alloca (nregs * sizeof (rtx)); - reg_last_set_table_tick = (int *) alloca (nregs * sizeof (int)); - reg_last_set_label = (int *) alloca (nregs * sizeof (int)); - reg_last_set_invalid = (char *) alloca (nregs * sizeof (char)); - reg_last_set_mode - = (enum machine_mode *) alloca (nregs * sizeof (enum machine_mode)); - reg_last_set_nonzero_bits - = (unsigned HOST_WIDE_INT *) alloca (nregs * sizeof (HOST_WIDE_INT)); - reg_last_set_sign_bit_copies - = (char *) alloca (nregs * sizeof (char)); - - reg_nonzero_bits - = (unsigned HOST_WIDE_INT *) alloca (nregs * sizeof (HOST_WIDE_INT)); - reg_sign_bit_copies = (char *) alloca (nregs * sizeof (char)); - - bzero (reg_last_death, nregs * sizeof (rtx)); - bzero (reg_last_set, nregs * sizeof (rtx)); - bzero (reg_last_set_value, nregs * sizeof (rtx)); - bzero (reg_last_set_table_tick, nregs * sizeof (int)); - bzero (reg_last_set_label, nregs * sizeof (int)); - bzero (reg_last_set_invalid, nregs * sizeof (char)); - bzero (reg_last_set_mode, nregs * sizeof (enum machine_mode)); - bzero (reg_last_set_nonzero_bits, nregs * sizeof (HOST_WIDE_INT)); - bzero (reg_last_set_sign_bit_copies, nregs * sizeof (char)); - bzero (reg_nonzero_bits, nregs * sizeof (HOST_WIDE_INT)); - bzero (reg_sign_bit_copies, nregs * sizeof (char)); - - init_recog_no_volatile (); - - /* Compute maximum uid value so uid_cuid can be allocated. */ - - for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) - if (INSN_UID (insn) > i) - i = INSN_UID (insn); - - uid_cuid = (int *) alloca ((i + 1) * sizeof (int)); - - nonzero_bits_mode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0); - - /* Don't use reg_nonzero_bits when computing it. This can cause problems - when, for example, we have j <<= 1 in a loop. */ - - nonzero_sign_valid = 0; - - /* Compute the mapping from uids to cuids. - Cuids are numbers assigned to insns, like uids, - except that cuids increase monotonically through the code. - - Scan all SETs and see if we can deduce anything about what - bits are known to be zero for some registers and how many copies - of the sign bit are known to exist for those registers. - - Also set any known values so that we can use it while searching - for what bits are known to be set. */ - - label_tick = 1; - - setup_incoming_promotions (); - - for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) - { - INSN_CUID (insn) = ++i; - subst_low_cuid = i; - subst_insn = insn; - - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') - { - note_stores (PATTERN (insn), set_nonzero_bits_and_sign_copies); - record_dead_and_set_regs (insn); - } - - if (GET_CODE (insn) == CODE_LABEL) - label_tick++; - } - - nonzero_sign_valid = 1; - - /* Now scan all the insns in forward order. */ - - label_tick = 1; - last_call_cuid = 0; - mem_last_set = 0; - bzero (reg_last_death, nregs * sizeof (rtx)); - bzero (reg_last_set, nregs * sizeof (rtx)); - bzero (reg_last_set_value, nregs * sizeof (rtx)); - bzero (reg_last_set_table_tick, nregs * sizeof (int)); - bzero (reg_last_set_label, nregs * sizeof (int)); - bzero (reg_last_set_invalid, nregs * sizeof (char)); - - setup_incoming_promotions (); - - for (insn = f; insn; insn = next ? next : NEXT_INSN (insn)) - { - next = 0; - - if (GET_CODE (insn) == CODE_LABEL) - label_tick++; - - else if (GET_CODE (insn) == INSN - || GET_CODE (insn) == CALL_INSN - || GET_CODE (insn) == JUMP_INSN) - { - /* Try this insn with each insn it links back to. */ - - for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) - if ((next = try_combine (insn, XEXP (links, 0), NULL_RTX)) != 0) - goto retry; - - /* Try each sequence of three linked insns ending with this one. */ - - for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) - for (nextlinks = LOG_LINKS (XEXP (links, 0)); nextlinks; - nextlinks = XEXP (nextlinks, 1)) - if ((next = try_combine (insn, XEXP (links, 0), - XEXP (nextlinks, 0))) != 0) - goto retry; - -#ifdef HAVE_cc0 - /* Try to combine a jump insn that uses CC0 - with a preceding insn that sets CC0, and maybe with its - logical predecessor as well. - This is how we make decrement-and-branch insns. - We need this special code because data flow connections - via CC0 do not get entered in LOG_LINKS. */ - - if (GET_CODE (insn) == JUMP_INSN - && (prev = prev_nonnote_insn (insn)) != 0 - && GET_CODE (prev) == INSN - && sets_cc0_p (PATTERN (prev))) - { - if ((next = try_combine (insn, prev, NULL_RTX)) != 0) - goto retry; - - for (nextlinks = LOG_LINKS (prev); nextlinks; - nextlinks = XEXP (nextlinks, 1)) - if ((next = try_combine (insn, prev, - XEXP (nextlinks, 0))) != 0) - goto retry; - } - - /* Do the same for an insn that explicitly references CC0. */ - if (GET_CODE (insn) == INSN - && (prev = prev_nonnote_insn (insn)) != 0 - && GET_CODE (prev) == INSN - && sets_cc0_p (PATTERN (prev)) - && GET_CODE (PATTERN (insn)) == SET - && reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (insn)))) - { - if ((next = try_combine (insn, prev, NULL_RTX)) != 0) - goto retry; - - for (nextlinks = LOG_LINKS (prev); nextlinks; - nextlinks = XEXP (nextlinks, 1)) - if ((next = try_combine (insn, prev, - XEXP (nextlinks, 0))) != 0) - goto retry; - } - - /* Finally, see if any of the insns that this insn links to - explicitly references CC0. If so, try this insn, that insn, - and its predecessor if it sets CC0. */ - for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) - if (GET_CODE (XEXP (links, 0)) == INSN - && GET_CODE (PATTERN (XEXP (links, 0))) == SET - && reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (XEXP (links, 0)))) - && (prev = prev_nonnote_insn (XEXP (links, 0))) != 0 - && GET_CODE (prev) == INSN - && sets_cc0_p (PATTERN (prev)) - && (next = try_combine (insn, XEXP (links, 0), prev)) != 0) - goto retry; -#endif - - /* Try combining an insn with two different insns whose results it - uses. */ - for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) - for (nextlinks = XEXP (links, 1); nextlinks; - nextlinks = XEXP (nextlinks, 1)) - if ((next = try_combine (insn, XEXP (links, 0), - XEXP (nextlinks, 0))) != 0) - goto retry; - - if (GET_CODE (insn) != NOTE) - record_dead_and_set_regs (insn); - - retry: - ; - } - } - - total_attempts += combine_attempts; - total_merges += combine_merges; - total_extras += combine_extras; - total_successes += combine_successes; - - nonzero_sign_valid = 0; -} - -/* Set up any promoted values for incoming argument registers. */ - -static void -setup_incoming_promotions () -{ -#ifdef PROMOTE_FUNCTION_ARGS - int regno; - rtx reg; - enum machine_mode mode; - int unsignedp; - rtx first = get_insns (); - - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if (FUNCTION_ARG_REGNO_P (regno) - && (reg = promoted_input_arg (regno, &mode, &unsignedp)) != 0) - record_value_for_reg (reg, first, - gen_rtx (unsignedp ? ZERO_EXTEND : SIGN_EXTEND, - GET_MODE (reg), - gen_rtx (CLOBBER, mode, const0_rtx))); -#endif -} - -/* Called via note_stores. If X is a pseudo that is used in more than - one basic block, is narrower that HOST_BITS_PER_WIDE_INT, and is being - set, record what bits are known zero. If we are clobbering X, - ignore this "set" because the clobbered value won't be used. - - If we are setting only a portion of X and we can't figure out what - portion, assume all bits will be used since we don't know what will - be happening. - - Similarly, set how many bits of X are known to be copies of the sign bit - at all locations in the function. This is the smallest number implied - by any set of X. */ - -static void -set_nonzero_bits_and_sign_copies (x, set) - rtx x; - rtx set; -{ - int num; - - if (GET_CODE (x) == REG - && REGNO (x) >= FIRST_PSEUDO_REGISTER - && reg_n_sets[REGNO (x)] > 1 - && reg_basic_block[REGNO (x)] < 0 - /* If this register is undefined at the start of the file, we can't - say what its contents were. */ - && ! (basic_block_live_at_start[0][REGNO (x) / REGSET_ELT_BITS] - & ((REGSET_ELT_TYPE) 1 << (REGNO (x) % REGSET_ELT_BITS))) - && GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT) - { - if (GET_CODE (set) == CLOBBER) - { - reg_nonzero_bits[REGNO (x)] = GET_MODE_MASK (GET_MODE (x)); - reg_sign_bit_copies[REGNO (x)] = 0; - return; - } - - /* If this is a complex assignment, see if we can convert it into a - simple assignment. */ - set = expand_field_assignment (set); - - /* If this is a simple assignment, or we have a paradoxical SUBREG, - set what we know about X. */ - - if (SET_DEST (set) == x - || (GET_CODE (SET_DEST (set)) == SUBREG - && (GET_MODE_SIZE (GET_MODE (SET_DEST (set))) - > GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (set))))) - && SUBREG_REG (SET_DEST (set)) == x)) - { - rtx src = SET_SRC (set); - -#ifdef SHORT_IMMEDIATES_SIGN_EXTEND - /* If X is narrower than a word and SRC is a non-negative - constant that would appear negative in the mode of X, - sign-extend it for use in reg_nonzero_bits because some - machines (maybe most) will actually do the sign-extension - and this is the conservative approach. - - ??? For 2.5, try to tighten up the MD files in this regard - instead of this kludge. */ - - if (GET_MODE_BITSIZE (GET_MODE (x)) < BITS_PER_WORD - && GET_CODE (src) == CONST_INT - && INTVAL (src) > 0 - && 0 != (INTVAL (src) - & ((HOST_WIDE_INT) 1 - << GET_MODE_BITSIZE (GET_MODE (x))))) - src = GEN_INT (INTVAL (src) - | ((HOST_WIDE_INT) (-1) - << GET_MODE_BITSIZE (GET_MODE (x)))); -#endif - - reg_nonzero_bits[REGNO (x)] - |= nonzero_bits (src, nonzero_bits_mode); - num = num_sign_bit_copies (SET_SRC (set), GET_MODE (x)); - if (reg_sign_bit_copies[REGNO (x)] == 0 - || reg_sign_bit_copies[REGNO (x)] > num) - reg_sign_bit_copies[REGNO (x)] = num; - } - else - { - reg_nonzero_bits[REGNO (x)] = GET_MODE_MASK (GET_MODE (x)); - reg_sign_bit_copies[REGNO (x)] = 0; - } - } -} - -/* See if INSN can be combined into I3. PRED and SUCC are optionally - insns that were previously combined into I3 or that will be combined - into the merger of INSN and I3. - - Return 0 if the combination is not allowed for any reason. - - If the combination is allowed, *PDEST will be set to the single - destination of INSN and *PSRC to the single source, and this function - will return 1. */ - -static int -can_combine_p (insn, i3, pred, succ, pdest, psrc) - rtx insn; - rtx i3; - rtx pred, succ; - rtx *pdest, *psrc; -{ - int i; - rtx set = 0, src, dest; - rtx p, link; - int all_adjacent = (succ ? (next_active_insn (insn) == succ - && next_active_insn (succ) == i3) - : next_active_insn (insn) == i3); - - /* Can combine only if previous insn is a SET of a REG, a SUBREG or CC0. - or a PARALLEL consisting of such a SET and CLOBBERs. - - If INSN has CLOBBER parallel parts, ignore them for our processing. - By definition, these happen during the execution of the insn. When it - is merged with another insn, all bets are off. If they are, in fact, - needed and aren't also supplied in I3, they may be added by - recog_for_combine. Otherwise, it won't match. - - We can also ignore a SET whose SET_DEST is mentioned in a REG_UNUSED - note. - - Get the source and destination of INSN. If more than one, can't - combine. */ - - if (GET_CODE (PATTERN (insn)) == SET) - set = PATTERN (insn); - else if (GET_CODE (PATTERN (insn)) == PARALLEL - && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) - { - for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) - { - rtx elt = XVECEXP (PATTERN (insn), 0, i); - - switch (GET_CODE (elt)) - { - /* We can ignore CLOBBERs. */ - case CLOBBER: - break; - - case SET: - /* Ignore SETs whose result isn't used but not those that - have side-effects. */ - if (find_reg_note (insn, REG_UNUSED, SET_DEST (elt)) - && ! side_effects_p (elt)) - break; - - /* If we have already found a SET, this is a second one and - so we cannot combine with this insn. */ - if (set) - return 0; - - set = elt; - break; - - default: - /* Anything else means we can't combine. */ - return 0; - } - } - - if (set == 0 - /* If SET_SRC is an ASM_OPERANDS we can't throw away these CLOBBERs, - so don't do anything with it. */ - || GET_CODE (SET_SRC (set)) == ASM_OPERANDS) - return 0; - } - else - return 0; - - if (set == 0) - return 0; - - set = expand_field_assignment (set); - src = SET_SRC (set), dest = SET_DEST (set); - - /* Don't eliminate a store in the stack pointer. */ - if (dest == stack_pointer_rtx - /* Don't install a subreg involving two modes not tieable. - It can worsen register allocation, and can even make invalid reload - insns, since the reg inside may need to be copied from in the - outside mode, and that may be invalid if it is an fp reg copied in - integer mode. As a special exception, we can allow this if - I3 is simply copying DEST, a REG, to CC0. */ - || (GET_CODE (src) == SUBREG - && ! MODES_TIEABLE_P (GET_MODE (src), GET_MODE (SUBREG_REG (src))) -#ifdef HAVE_cc0 - && ! (GET_CODE (i3) == INSN && GET_CODE (PATTERN (i3)) == SET - && SET_DEST (PATTERN (i3)) == cc0_rtx - && GET_CODE (dest) == REG && dest == SET_SRC (PATTERN (i3))) -#endif - ) - /* If we couldn't eliminate a field assignment, we can't combine. */ - || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == STRICT_LOW_PART - /* Don't combine with an insn that sets a register to itself if it has - a REG_EQUAL note. This may be part of a REG_NO_CONFLICT sequence. */ - || (rtx_equal_p (src, dest) && find_reg_note (insn, REG_EQUAL, NULL_RTX)) - /* Can't merge a function call. */ - || GET_CODE (src) == CALL - /* Don't substitute into an incremented register. */ - || FIND_REG_INC_NOTE (i3, dest) - || (succ && FIND_REG_INC_NOTE (succ, dest)) - /* Don't combine the end of a libcall into anything. */ - || find_reg_note (insn, REG_RETVAL, NULL_RTX) - /* Make sure that DEST is not used after SUCC but before I3. */ - || (succ && ! all_adjacent - && reg_used_between_p (dest, succ, i3)) - /* Make sure that the value that is to be substituted for the register - does not use any registers whose values alter in between. However, - If the insns are adjacent, a use can't cross a set even though we - think it might (this can happen for a sequence of insns each setting - the same destination; reg_last_set of that register might point to - a NOTE). Also, don't move a volatile asm or UNSPEC_VOLATILE across - any other insns. */ - || (! all_adjacent - && (use_crosses_set_p (src, INSN_CUID (insn)) - || (GET_CODE (src) == ASM_OPERANDS && MEM_VOLATILE_P (src)) - || GET_CODE (src) == UNSPEC_VOLATILE)) - /* If there is a REG_NO_CONFLICT note for DEST in I3 or SUCC, we get - better register allocation by not doing the combine. */ - || find_reg_note (i3, REG_NO_CONFLICT, dest) - || (succ && find_reg_note (succ, REG_NO_CONFLICT, dest)) - /* Don't combine across a CALL_INSN, because that would possibly - change whether the life span of some REGs crosses calls or not, - and it is a pain to update that information. - Exception: if source is a constant, moving it later can't hurt. - Accept that special case, because it helps -fforce-addr a lot. */ - || (INSN_CUID (insn) < last_call_cuid && ! CONSTANT_P (src))) - return 0; - - /* DEST must either be a REG or CC0. */ - if (GET_CODE (dest) == REG) - { - /* If register alignment is being enforced for multi-word items in all - cases except for parameters, it is possible to have a register copy - insn referencing a hard register that is not allowed to contain the - mode being copied and which would not be valid as an operand of most - insns. Eliminate this problem by not combining with such an insn. - - Also, on some machines we don't want to extend the life of a hard - register. */ - - if (GET_CODE (src) == REG - && ((REGNO (dest) < FIRST_PSEUDO_REGISTER - && ! HARD_REGNO_MODE_OK (REGNO (dest), GET_MODE (dest))) -#ifdef SMALL_REGISTER_CLASSES - /* Don't extend the life of a hard register. */ - || REGNO (src) < FIRST_PSEUDO_REGISTER -#else - || (REGNO (src) < FIRST_PSEUDO_REGISTER - && ! HARD_REGNO_MODE_OK (REGNO (src), GET_MODE (src))) -#endif - )) - return 0; - } - else if (GET_CODE (dest) != CC0) - return 0; - - /* Don't substitute for a register intended as a clobberable operand. - Similarly, don't substitute an expression containing a register that - will be clobbered in I3. */ - if (GET_CODE (PATTERN (i3)) == PARALLEL) - for (i = XVECLEN (PATTERN (i3), 0) - 1; i >= 0; i--) - if (GET_CODE (XVECEXP (PATTERN (i3), 0, i)) == CLOBBER - && (reg_overlap_mentioned_p (XEXP (XVECEXP (PATTERN (i3), 0, i), 0), - src) - || rtx_equal_p (XEXP (XVECEXP (PATTERN (i3), 0, i), 0), dest))) - return 0; - - /* If INSN contains anything volatile, or is an `asm' (whether volatile - or not), reject, unless nothing volatile comes between it and I3, - with the exception of SUCC. */ - - if (GET_CODE (src) == ASM_OPERANDS || volatile_refs_p (src)) - for (p = NEXT_INSN (insn); p != i3; p = NEXT_INSN (p)) - if (GET_RTX_CLASS (GET_CODE (p)) == 'i' - && p != succ && volatile_refs_p (PATTERN (p))) - return 0; - - /* If INSN or I2 contains an autoincrement or autodecrement, - make sure that register is not used between there and I3, - and not already used in I3 either. - Also insist that I3 not be a jump; if it were one - and the incremented register were spilled, we would lose. */ - -#ifdef AUTO_INC_DEC - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_INC - && (GET_CODE (i3) == JUMP_INSN - || reg_used_between_p (XEXP (link, 0), insn, i3) - || reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i3)))) - return 0; -#endif - -#ifdef HAVE_cc0 - /* Don't combine an insn that follows a CC0-setting insn. - An insn that uses CC0 must not be separated from the one that sets it. - We do, however, allow I2 to follow a CC0-setting insn if that insn - is passed as I1; in that case it will be deleted also. - We also allow combining in this case if all the insns are adjacent - because that would leave the two CC0 insns adjacent as well. - It would be more logical to test whether CC0 occurs inside I1 or I2, - but that would be much slower, and this ought to be equivalent. */ - - p = prev_nonnote_insn (insn); - if (p && p != pred && GET_CODE (p) == INSN && sets_cc0_p (PATTERN (p)) - && ! all_adjacent) - return 0; -#endif - - /* If we get here, we have passed all the tests and the combination is - to be allowed. */ - - *pdest = dest; - *psrc = src; - - return 1; -} - -/* LOC is the location within I3 that contains its pattern or the component - of a PARALLEL of the pattern. We validate that it is valid for combining. - - One problem is if I3 modifies its output, as opposed to replacing it - entirely, we can't allow the output to contain I2DEST or I1DEST as doing - so would produce an insn that is not equivalent to the original insns. - - Consider: - - (set (reg:DI 101) (reg:DI 100)) - (set (subreg:SI (reg:DI 101) 0) ) - - This is NOT equivalent to: - - (parallel [(set (subreg:SI (reg:DI 100) 0) ) - (set (reg:DI 101) (reg:DI 100))]) - - Not only does this modify 100 (in which case it might still be valid - if 100 were dead in I2), it sets 101 to the ORIGINAL value of 100. - - We can also run into a problem if I2 sets a register that I1 - uses and I1 gets directly substituted into I3 (not via I2). In that - case, we would be getting the wrong value of I2DEST into I3, so we - must reject the combination. This case occurs when I2 and I1 both - feed into I3, rather than when I1 feeds into I2, which feeds into I3. - If I1_NOT_IN_SRC is non-zero, it means that finding I1 in the source - of a SET must prevent combination from occurring. - - On machines where SMALL_REGISTER_CLASSES is defined, we don't combine - if the destination of a SET is a hard register. - - Before doing the above check, we first try to expand a field assignment - into a set of logical operations. - - If PI3_DEST_KILLED is non-zero, it is a pointer to a location in which - we place a register that is both set and used within I3. If more than one - such register is detected, we fail. - - Return 1 if the combination is valid, zero otherwise. */ - -static int -combinable_i3pat (i3, loc, i2dest, i1dest, i1_not_in_src, pi3dest_killed) - rtx i3; - rtx *loc; - rtx i2dest; - rtx i1dest; - int i1_not_in_src; - rtx *pi3dest_killed; -{ - rtx x = *loc; - - if (GET_CODE (x) == SET) - { - rtx set = expand_field_assignment (x); - rtx dest = SET_DEST (set); - rtx src = SET_SRC (set); - rtx inner_dest = dest, inner_src = src; - - SUBST (*loc, set); - - while (GET_CODE (inner_dest) == STRICT_LOW_PART - || GET_CODE (inner_dest) == SUBREG - || GET_CODE (inner_dest) == ZERO_EXTRACT) - inner_dest = XEXP (inner_dest, 0); - - /* We probably don't need this any more now that LIMIT_RELOAD_CLASS - was added. */ -#if 0 - while (GET_CODE (inner_src) == STRICT_LOW_PART - || GET_CODE (inner_src) == SUBREG - || GET_CODE (inner_src) == ZERO_EXTRACT) - inner_src = XEXP (inner_src, 0); - - /* If it is better that two different modes keep two different pseudos, - avoid combining them. This avoids producing the following pattern - on a 386: - (set (subreg:SI (reg/v:QI 21) 0) - (lshiftrt:SI (reg/v:SI 20) - (const_int 24))) - If that were made, reload could not handle the pair of - reg 20/21, since it would try to get any GENERAL_REGS - but some of them don't handle QImode. */ - - if (rtx_equal_p (inner_src, i2dest) - && GET_CODE (inner_dest) == REG - && ! MODES_TIEABLE_P (GET_MODE (i2dest), GET_MODE (inner_dest))) - return 0; -#endif - - /* Check for the case where I3 modifies its output, as - discussed above. */ - if ((inner_dest != dest - && (reg_overlap_mentioned_p (i2dest, inner_dest) - || (i1dest && reg_overlap_mentioned_p (i1dest, inner_dest)))) - /* This is the same test done in can_combine_p except that we - allow a hard register with SMALL_REGISTER_CLASSES if SRC is a - CALL operation. */ - || (GET_CODE (inner_dest) == REG - && REGNO (inner_dest) < FIRST_PSEUDO_REGISTER -#ifdef SMALL_REGISTER_CLASSES - && GET_CODE (src) != CALL -#else - && ! HARD_REGNO_MODE_OK (REGNO (inner_dest), - GET_MODE (inner_dest)) -#endif - ) - - || (i1_not_in_src && reg_overlap_mentioned_p (i1dest, src))) - return 0; - - /* If DEST is used in I3, it is being killed in this insn, - so record that for later. - Never add REG_DEAD notes for the FRAME_POINTER_REGNUM or the - STACK_POINTER_REGNUM, since these are always considered to be - live. Similarly for ARG_POINTER_REGNUM if it is fixed. */ - if (pi3dest_killed && GET_CODE (dest) == REG - && reg_referenced_p (dest, PATTERN (i3)) - && REGNO (dest) != FRAME_POINTER_REGNUM -#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM - && (REGNO (dest) != ARG_POINTER_REGNUM - || ! fixed_regs [REGNO (dest)]) -#endif - && REGNO (dest) != STACK_POINTER_REGNUM) - { - if (*pi3dest_killed) - return 0; - - *pi3dest_killed = dest; - } - } - - else if (GET_CODE (x) == PARALLEL) - { - int i; - - for (i = 0; i < XVECLEN (x, 0); i++) - if (! combinable_i3pat (i3, &XVECEXP (x, 0, i), i2dest, i1dest, - i1_not_in_src, pi3dest_killed)) - return 0; - } - - return 1; -} - -/* Try to combine the insns I1 and I2 into I3. - Here I1 and I2 appear earlier than I3. - I1 can be zero; then we combine just I2 into I3. - - It we are combining three insns and the resulting insn is not recognized, - try splitting it into two insns. If that happens, I2 and I3 are retained - and I1 is pseudo-deleted by turning it into a NOTE. Otherwise, I1 and I2 - are pseudo-deleted. - - If we created two insns, return I2; otherwise return I3. - Return 0 if the combination does not work. Then nothing is changed. */ - -static rtx -try_combine (i3, i2, i1) - register rtx i3, i2, i1; -{ - /* New patterns for I3 and I3, respectively. */ - rtx newpat, newi2pat = 0; - /* Indicates need to preserve SET in I1 or I2 in I3 if it is not dead. */ - int added_sets_1, added_sets_2; - /* Total number of SETs to put into I3. */ - int total_sets; - /* Nonzero is I2's body now appears in I3. */ - int i2_is_used; - /* INSN_CODEs for new I3, new I2, and user of condition code. */ - int insn_code_number, i2_code_number, other_code_number; - /* Contains I3 if the destination of I3 is used in its source, which means - that the old life of I3 is being killed. If that usage is placed into - I2 and not in I3, a REG_DEAD note must be made. */ - rtx i3dest_killed = 0; - /* SET_DEST and SET_SRC of I2 and I1. */ - rtx i2dest, i2src, i1dest = 0, i1src = 0; - /* PATTERN (I2), or a copy of it in certain cases. */ - rtx i2pat; - /* Indicates if I2DEST or I1DEST is in I2SRC or I1_SRC. */ - int i2dest_in_i2src, i1dest_in_i1src = 0, i2dest_in_i1src = 0; - int i1_feeds_i3 = 0; - /* Notes that must be added to REG_NOTES in I3 and I2. */ - rtx new_i3_notes, new_i2_notes; - - int maxreg; - rtx temp; - register rtx link; - int i; - - /* If any of I1, I2, and I3 isn't really an insn, we can't do anything. - This can occur when flow deletes an insn that it has merged into an - auto-increment address. We also can't do anything if I3 has a - REG_LIBCALL note since we don't want to disrupt the contiguity of a - libcall. */ - - if (GET_RTX_CLASS (GET_CODE (i3)) != 'i' - || GET_RTX_CLASS (GET_CODE (i2)) != 'i' - || (i1 && GET_RTX_CLASS (GET_CODE (i1)) != 'i') - || find_reg_note (i3, REG_LIBCALL, NULL_RTX)) - return 0; - - combine_attempts++; - - undobuf.num_undo = previous_num_undos = 0; - undobuf.other_insn = 0; - - /* Save the current high-water-mark so we can free storage if we didn't - accept this combination. */ - undobuf.storage = (char *) oballoc (0); - - /* If I1 and I2 both feed I3, they can be in any order. To simplify the - code below, set I1 to be the earlier of the two insns. */ - if (i1 && INSN_CUID (i1) > INSN_CUID (i2)) - temp = i1, i1 = i2, i2 = temp; - - /* First check for one important special-case that the code below will - not handle. Namely, the case where I1 is zero, I2 has multiple sets, - and I3 is a SET whose SET_SRC is a SET_DEST in I2. In that case, - we may be able to replace that destination with the destination of I3. - This occurs in the common code where we compute both a quotient and - remainder into a structure, in which case we want to do the computation - directly into the structure to avoid register-register copies. - - We make very conservative checks below and only try to handle the - most common cases of this. For example, we only handle the case - where I2 and I3 are adjacent to avoid making difficult register - usage tests. */ - - if (i1 == 0 && GET_CODE (i3) == INSN && GET_CODE (PATTERN (i3)) == SET - && GET_CODE (SET_SRC (PATTERN (i3))) == REG - && REGNO (SET_SRC (PATTERN (i3))) >= FIRST_PSEUDO_REGISTER -#ifdef SMALL_REGISTER_CLASSES - && (GET_CODE (SET_DEST (PATTERN (i3))) != REG - || REGNO (SET_DEST (PATTERN (i3))) >= FIRST_PSEUDO_REGISTER) -#endif - && find_reg_note (i3, REG_DEAD, SET_SRC (PATTERN (i3))) - && GET_CODE (PATTERN (i2)) == PARALLEL - && ! side_effects_p (SET_DEST (PATTERN (i3))) - /* If the dest of I3 is a ZERO_EXTRACT or STRICT_LOW_PART, the code - below would need to check what is inside (and reg_overlap_mentioned_p - doesn't support those codes anyway). Don't allow those destinations; - the resulting insn isn't likely to be recognized anyway. */ - && GET_CODE (SET_DEST (PATTERN (i3))) != ZERO_EXTRACT - && GET_CODE (SET_DEST (PATTERN (i3))) != STRICT_LOW_PART - && ! reg_overlap_mentioned_p (SET_SRC (PATTERN (i3)), - SET_DEST (PATTERN (i3))) - && next_real_insn (i2) == i3) - { - rtx p2 = PATTERN (i2); - - /* Make sure that the destination of I3, - which we are going to substitute into one output of I2, - is not used within another output of I2. We must avoid making this: - (parallel [(set (mem (reg 69)) ...) - (set (reg 69) ...)]) - which is not well-defined as to order of actions. - (Besides, reload can't handle output reloads for this.) - - The problem can also happen if the dest of I3 is a memory ref, - if another dest in I2 is an indirect memory ref. */ - for (i = 0; i < XVECLEN (p2, 0); i++) - if (GET_CODE (XVECEXP (p2, 0, i)) == SET - && reg_overlap_mentioned_p (SET_DEST (PATTERN (i3)), - SET_DEST (XVECEXP (p2, 0, i)))) - break; - - if (i == XVECLEN (p2, 0)) - for (i = 0; i < XVECLEN (p2, 0); i++) - if (SET_DEST (XVECEXP (p2, 0, i)) == SET_SRC (PATTERN (i3))) - { - combine_merges++; - - subst_insn = i3; - subst_low_cuid = INSN_CUID (i2); - - added_sets_2 = 0; - i2dest = SET_SRC (PATTERN (i3)); - - /* Replace the dest in I2 with our dest and make the resulting - insn the new pattern for I3. Then skip to where we - validate the pattern. Everything was set up above. */ - SUBST (SET_DEST (XVECEXP (p2, 0, i)), - SET_DEST (PATTERN (i3))); - - newpat = p2; - goto validate_replacement; - } - } - -#ifndef HAVE_cc0 - /* If we have no I1 and I2 looks like: - (parallel [(set (reg:CC X) (compare:CC OP (const_int 0))) - (set Y OP)]) - make up a dummy I1 that is - (set Y OP) - and change I2 to be - (set (reg:CC X) (compare:CC Y (const_int 0))) - - (We can ignore any trailing CLOBBERs.) - - This undoes a previous combination and allows us to match a branch-and- - decrement insn. */ - - if (i1 == 0 && GET_CODE (PATTERN (i2)) == PARALLEL - && XVECLEN (PATTERN (i2), 0) >= 2 - && GET_CODE (XVECEXP (PATTERN (i2), 0, 0)) == SET - && (GET_MODE_CLASS (GET_MODE (SET_DEST (XVECEXP (PATTERN (i2), 0, 0)))) - == MODE_CC) - && GET_CODE (SET_SRC (XVECEXP (PATTERN (i2), 0, 0))) == COMPARE - && XEXP (SET_SRC (XVECEXP (PATTERN (i2), 0, 0)), 1) == const0_rtx - && GET_CODE (XVECEXP (PATTERN (i2), 0, 1)) == SET - && GET_CODE (SET_DEST (XVECEXP (PATTERN (i2), 0, 1))) == REG - && rtx_equal_p (XEXP (SET_SRC (XVECEXP (PATTERN (i2), 0, 0)), 0), - SET_SRC (XVECEXP (PATTERN (i2), 0, 1)))) - { - for (i = XVECLEN (PATTERN (i2), 0) - 1; i >= 2; i--) - if (GET_CODE (XVECEXP (PATTERN (i2), 0, i)) != CLOBBER) - break; - - if (i == 1) - { - /* We make I1 with the same INSN_UID as I2. This gives it - the same INSN_CUID for value tracking. Our fake I1 will - never appear in the insn stream so giving it the same INSN_UID - as I2 will not cause a problem. */ - - i1 = gen_rtx (INSN, VOIDmode, INSN_UID (i2), 0, i2, - XVECEXP (PATTERN (i2), 0, 1), -1, 0, 0); - - SUBST (PATTERN (i2), XVECEXP (PATTERN (i2), 0, 0)); - SUBST (XEXP (SET_SRC (PATTERN (i2)), 0), - SET_DEST (PATTERN (i1))); - } - } -#endif - - /* Verify that I2 and I1 are valid for combining. */ - if (! can_combine_p (i2, i3, i1, NULL_RTX, &i2dest, &i2src) - || (i1 && ! can_combine_p (i1, i3, NULL_RTX, i2, &i1dest, &i1src))) - { - undo_all (); - return 0; - } - - /* Record whether I2DEST is used in I2SRC and similarly for the other - cases. Knowing this will help in register status updating below. */ - i2dest_in_i2src = reg_overlap_mentioned_p (i2dest, i2src); - i1dest_in_i1src = i1 && reg_overlap_mentioned_p (i1dest, i1src); - i2dest_in_i1src = i1 && reg_overlap_mentioned_p (i2dest, i1src); - - /* See if I1 directly feeds into I3. It does if I1DEST is not used - in I2SRC. */ - i1_feeds_i3 = i1 && ! reg_overlap_mentioned_p (i1dest, i2src); - - /* Ensure that I3's pattern can be the destination of combines. */ - if (! combinable_i3pat (i3, &PATTERN (i3), i2dest, i1dest, - i1 && i2dest_in_i1src && i1_feeds_i3, - &i3dest_killed)) - { - undo_all (); - return 0; - } - - /* If I3 has an inc, then give up if I1 or I2 uses the reg that is inc'd. - We used to do this EXCEPT in one case: I3 has a post-inc in an - output operand. However, that exception can give rise to insns like - mov r3,(r3)+ - which is a famous insn on the PDP-11 where the value of r3 used as the - source was model-dependent. Avoid this sort of thing. */ - -#if 0 - if (!(GET_CODE (PATTERN (i3)) == SET - && GET_CODE (SET_SRC (PATTERN (i3))) == REG - && GET_CODE (SET_DEST (PATTERN (i3))) == MEM - && (GET_CODE (XEXP (SET_DEST (PATTERN (i3)), 0)) == POST_INC - || GET_CODE (XEXP (SET_DEST (PATTERN (i3)), 0)) == POST_DEC))) - /* It's not the exception. */ -#endif -#ifdef AUTO_INC_DEC - for (link = REG_NOTES (i3); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_INC - && (reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i2)) - || (i1 != 0 - && reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i1))))) - { - undo_all (); - return 0; - } -#endif - - /* See if the SETs in I1 or I2 need to be kept around in the merged - instruction: whenever the value set there is still needed past I3. - For the SETs in I2, this is easy: we see if I2DEST dies or is set in I3. - - For the SET in I1, we have two cases: If I1 and I2 independently - feed into I3, the set in I1 needs to be kept around if I1DEST dies - or is set in I3. Otherwise (if I1 feeds I2 which feeds I3), the set - in I1 needs to be kept around unless I1DEST dies or is set in either - I2 or I3. We can distinguish these cases by seeing if I2SRC mentions - I1DEST. If so, we know I1 feeds into I2. */ - - added_sets_2 = ! dead_or_set_p (i3, i2dest); - - added_sets_1 - = i1 && ! (i1_feeds_i3 ? dead_or_set_p (i3, i1dest) - : (dead_or_set_p (i3, i1dest) || dead_or_set_p (i2, i1dest))); - - /* If the set in I2 needs to be kept around, we must make a copy of - PATTERN (I2), so that when we substitute I1SRC for I1DEST in - PATTERN (I2), we are only substituting for the original I1DEST, not into - an already-substituted copy. This also prevents making self-referential - rtx. If I2 is a PARALLEL, we just need the piece that assigns I2SRC to - I2DEST. */ - - i2pat = (GET_CODE (PATTERN (i2)) == PARALLEL - ? gen_rtx (SET, VOIDmode, i2dest, i2src) - : PATTERN (i2)); - - if (added_sets_2) - i2pat = copy_rtx (i2pat); - - combine_merges++; - - /* Substitute in the latest insn for the regs set by the earlier ones. */ - - maxreg = max_reg_num (); - - subst_insn = i3; - - /* It is possible that the source of I2 or I1 may be performing an - unneeded operation, such as a ZERO_EXTEND of something that is known - to have the high part zero. Handle that case by letting subst look at - the innermost one of them. - - Another way to do this would be to have a function that tries to - simplify a single insn instead of merging two or more insns. We don't - do this because of the potential of infinite loops and because - of the potential extra memory required. However, doing it the way - we are is a bit of a kludge and doesn't catch all cases. - - But only do this if -fexpensive-optimizations since it slows things down - and doesn't usually win. */ - - if (flag_expensive_optimizations) - { - /* Pass pc_rtx so no substitutions are done, just simplifications. - The cases that we are interested in here do not involve the few - cases were is_replaced is checked. */ - if (i1) - { - subst_low_cuid = INSN_CUID (i1); - i1src = subst (i1src, pc_rtx, pc_rtx, 0, 0); - } - else - { - subst_low_cuid = INSN_CUID (i2); - i2src = subst (i2src, pc_rtx, pc_rtx, 0, 0); - } - - previous_num_undos = undobuf.num_undo; - } - -#ifndef HAVE_cc0 - /* Many machines that don't use CC0 have insns that can both perform an - arithmetic operation and set the condition code. These operations will - be represented as a PARALLEL with the first element of the vector - being a COMPARE of an arithmetic operation with the constant zero. - The second element of the vector will set some pseudo to the result - of the same arithmetic operation. If we simplify the COMPARE, we won't - match such a pattern and so will generate an extra insn. Here we test - for this case, where both the comparison and the operation result are - needed, and make the PARALLEL by just replacing I2DEST in I3SRC with - I2SRC. Later we will make the PARALLEL that contains I2. */ - - if (i1 == 0 && added_sets_2 && GET_CODE (PATTERN (i3)) == SET - && GET_CODE (SET_SRC (PATTERN (i3))) == COMPARE - && XEXP (SET_SRC (PATTERN (i3)), 1) == const0_rtx - && rtx_equal_p (XEXP (SET_SRC (PATTERN (i3)), 0), i2dest)) - { - rtx *cc_use; - enum machine_mode compare_mode; - - newpat = PATTERN (i3); - SUBST (XEXP (SET_SRC (newpat), 0), i2src); - - i2_is_used = 1; - -#ifdef EXTRA_CC_MODES - /* See if a COMPARE with the operand we substituted in should be done - with the mode that is currently being used. If not, do the same - processing we do in `subst' for a SET; namely, if the destination - is used only once, try to replace it with a register of the proper - mode and also replace the COMPARE. */ - if (undobuf.other_insn == 0 - && (cc_use = find_single_use (SET_DEST (newpat), i3, - &undobuf.other_insn)) - && ((compare_mode = SELECT_CC_MODE (GET_CODE (*cc_use), - i2src, const0_rtx)) - != GET_MODE (SET_DEST (newpat)))) - { - int regno = REGNO (SET_DEST (newpat)); - rtx new_dest = gen_rtx (REG, compare_mode, regno); - - if (regno < FIRST_PSEUDO_REGISTER - || (reg_n_sets[regno] == 1 && ! added_sets_2 - && ! REG_USERVAR_P (SET_DEST (newpat)))) - { - if (regno >= FIRST_PSEUDO_REGISTER) - SUBST (regno_reg_rtx[regno], new_dest); - - SUBST (SET_DEST (newpat), new_dest); - SUBST (XEXP (*cc_use, 0), new_dest); - SUBST (SET_SRC (newpat), - gen_rtx_combine (COMPARE, compare_mode, - i2src, const0_rtx)); - } - else - undobuf.other_insn = 0; - } -#endif - } - else -#endif - { - n_occurrences = 0; /* `subst' counts here */ - - /* If I1 feeds into I2 (not into I3) and I1DEST is in I1SRC, we - need to make a unique copy of I2SRC each time we substitute it - to avoid self-referential rtl. */ - - subst_low_cuid = INSN_CUID (i2); - newpat = subst (PATTERN (i3), i2dest, i2src, 0, - ! i1_feeds_i3 && i1dest_in_i1src); - previous_num_undos = undobuf.num_undo; - - /* Record whether i2's body now appears within i3's body. */ - i2_is_used = n_occurrences; - } - - /* If we already got a failure, don't try to do more. Otherwise, - try to substitute in I1 if we have it. */ - - if (i1 && GET_CODE (newpat) != CLOBBER) - { - /* Before we can do this substitution, we must redo the test done - above (see detailed comments there) that ensures that I1DEST - isn't mentioned in any SETs in NEWPAT that are field assignments. */ - - if (! combinable_i3pat (NULL_RTX, &newpat, i1dest, NULL_RTX, - 0, NULL_PTR)) - { - undo_all (); - return 0; - } - - n_occurrences = 0; - subst_low_cuid = INSN_CUID (i1); - newpat = subst (newpat, i1dest, i1src, 0, 0); - previous_num_undos = undobuf.num_undo; - } - - /* Fail if an autoincrement side-effect has been duplicated. Be careful - to count all the ways that I2SRC and I1SRC can be used. */ - if ((FIND_REG_INC_NOTE (i2, NULL_RTX) != 0 - && i2_is_used + added_sets_2 > 1) - || (i1 != 0 && FIND_REG_INC_NOTE (i1, NULL_RTX) != 0 - && (n_occurrences + added_sets_1 + (added_sets_2 && ! i1_feeds_i3) - > 1)) - /* Fail if we tried to make a new register (we used to abort, but there's - really no reason to). */ - || max_reg_num () != maxreg - /* Fail if we couldn't do something and have a CLOBBER. */ - || GET_CODE (newpat) == CLOBBER) - { - undo_all (); - return 0; - } - - /* If the actions of the earlier insns must be kept - in addition to substituting them into the latest one, - we must make a new PARALLEL for the latest insn - to hold additional the SETs. */ - - if (added_sets_1 || added_sets_2) - { - combine_extras++; - - if (GET_CODE (newpat) == PARALLEL) - { - rtvec old = XVEC (newpat, 0); - total_sets = XVECLEN (newpat, 0) + added_sets_1 + added_sets_2; - newpat = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (total_sets)); - bcopy (&old->elem[0], &XVECEXP (newpat, 0, 0), - sizeof (old->elem[0]) * old->num_elem); - } - else - { - rtx old = newpat; - total_sets = 1 + added_sets_1 + added_sets_2; - newpat = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (total_sets)); - XVECEXP (newpat, 0, 0) = old; - } - - if (added_sets_1) - XVECEXP (newpat, 0, --total_sets) - = (GET_CODE (PATTERN (i1)) == PARALLEL - ? gen_rtx (SET, VOIDmode, i1dest, i1src) : PATTERN (i1)); - - if (added_sets_2) - { - /* If there is no I1, use I2's body as is. We used to also not do - the subst call below if I2 was substituted into I3, - but that could lose a simplification. */ - if (i1 == 0) - XVECEXP (newpat, 0, --total_sets) = i2pat; - else - /* See comment where i2pat is assigned. */ - XVECEXP (newpat, 0, --total_sets) - = subst (i2pat, i1dest, i1src, 0, 0); - } - } - - /* We come here when we are replacing a destination in I2 with the - destination of I3. */ - validate_replacement: - - /* Is the result of combination a valid instruction? */ - insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); - - /* If the result isn't valid, see if it is a PARALLEL of two SETs where - the second SET's destination is a register that is unused. In that case, - we just need the first SET. This can occur when simplifying a divmod - insn. We *must* test for this case here because the code below that - splits two independent SETs doesn't handle this case correctly when it - updates the register status. Also check the case where the first - SET's destination is unused. That would not cause incorrect code, but - does cause an unneeded insn to remain. */ - - if (insn_code_number < 0 && GET_CODE (newpat) == PARALLEL - && XVECLEN (newpat, 0) == 2 - && GET_CODE (XVECEXP (newpat, 0, 0)) == SET - && GET_CODE (XVECEXP (newpat, 0, 1)) == SET - && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) == REG - && find_reg_note (i3, REG_UNUSED, SET_DEST (XVECEXP (newpat, 0, 1))) - && ! side_effects_p (SET_SRC (XVECEXP (newpat, 0, 1))) - && asm_noperands (newpat) < 0) - { - newpat = XVECEXP (newpat, 0, 0); - insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); - } - - else if (insn_code_number < 0 && GET_CODE (newpat) == PARALLEL - && XVECLEN (newpat, 0) == 2 - && GET_CODE (XVECEXP (newpat, 0, 0)) == SET - && GET_CODE (XVECEXP (newpat, 0, 1)) == SET - && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) == REG - && find_reg_note (i3, REG_UNUSED, SET_DEST (XVECEXP (newpat, 0, 0))) - && ! side_effects_p (SET_SRC (XVECEXP (newpat, 0, 0))) - && asm_noperands (newpat) < 0) - { - newpat = XVECEXP (newpat, 0, 1); - insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); - } - - /* See if this is an XOR. If so, perhaps the problem is that the - constant is out of range. Replace it with a complemented XOR with - a complemented constant; it might be in range. */ - - else if (insn_code_number < 0 && GET_CODE (newpat) == SET - && GET_CODE (SET_SRC (newpat)) == XOR - && GET_CODE (XEXP (SET_SRC (newpat), 1)) == CONST_INT - && ((temp = simplify_unary_operation (NOT, - GET_MODE (SET_SRC (newpat)), - XEXP (SET_SRC (newpat), 1), - GET_MODE (SET_SRC (newpat)))) - != 0)) - { - enum machine_mode i_mode = GET_MODE (SET_SRC (newpat)); - rtx pat - = gen_rtx_combine (SET, VOIDmode, SET_DEST (newpat), - gen_unary (NOT, i_mode, - gen_binary (XOR, i_mode, - XEXP (SET_SRC (newpat), 0), - temp))); - - insn_code_number = recog_for_combine (&pat, i3, &new_i3_notes); - if (insn_code_number >= 0) - newpat = pat; - } - - /* If we were combining three insns and the result is a simple SET - with no ASM_OPERANDS that wasn't recognized, try to split it into two - insns. There are two ways to do this. It can be split using a - machine-specific method (like when you have an addition of a large - constant) or by combine in the function find_split_point. */ - - if (i1 && insn_code_number < 0 && GET_CODE (newpat) == SET - && asm_noperands (newpat) < 0) - { - rtx m_split, *split; - rtx ni2dest = i2dest; - - /* See if the MD file can split NEWPAT. If it can't, see if letting it - use I2DEST as a scratch register will help. In the latter case, - convert I2DEST to the mode of the source of NEWPAT if we can. */ - - m_split = split_insns (newpat, i3); - - /* We can only use I2DEST as a scratch reg if it doesn't overlap any - inputs of NEWPAT. */ - - /* ??? If I2DEST is not safe, and I1DEST exists, then it would be - possible to try that as a scratch reg. This would require adding - more code to make it work though. */ - - if (m_split == 0 && ! reg_overlap_mentioned_p (ni2dest, newpat)) - { - /* If I2DEST is a hard register or the only use of a pseudo, - we can change its mode. */ - if (GET_MODE (SET_DEST (newpat)) != GET_MODE (i2dest) - && GET_MODE (SET_DEST (newpat)) != VOIDmode - && GET_CODE (i2dest) == REG - && (REGNO (i2dest) < FIRST_PSEUDO_REGISTER - || (reg_n_sets[REGNO (i2dest)] == 1 && ! added_sets_2 - && ! REG_USERVAR_P (i2dest)))) - ni2dest = gen_rtx (REG, GET_MODE (SET_DEST (newpat)), - REGNO (i2dest)); - - m_split = split_insns (gen_rtx (PARALLEL, VOIDmode, - gen_rtvec (2, newpat, - gen_rtx (CLOBBER, - VOIDmode, - ni2dest))), - i3); - } - - if (m_split && GET_CODE (m_split) == SEQUENCE - && XVECLEN (m_split, 0) == 2 - && (next_real_insn (i2) == i3 - || ! use_crosses_set_p (PATTERN (XVECEXP (m_split, 0, 0)), - INSN_CUID (i2)))) - { - rtx i2set, i3set; - rtx newi3pat = PATTERN (XVECEXP (m_split, 0, 1)); - newi2pat = PATTERN (XVECEXP (m_split, 0, 0)); - - i3set = single_set (XVECEXP (m_split, 0, 1)); - i2set = single_set (XVECEXP (m_split, 0, 0)); - - /* In case we changed the mode of I2DEST, replace it in the - pseudo-register table here. We can't do it above in case this - code doesn't get executed and we do a split the other way. */ - - if (REGNO (i2dest) >= FIRST_PSEUDO_REGISTER) - SUBST (regno_reg_rtx[REGNO (i2dest)], ni2dest); - - i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes); - - /* If I2 or I3 has multiple SETs, we won't know how to track - register status, so don't use these insns. */ - - if (i2_code_number >= 0 && i2set && i3set) - insn_code_number = recog_for_combine (&newi3pat, i3, - &new_i3_notes); - - if (insn_code_number >= 0) - newpat = newi3pat; - - /* It is possible that both insns now set the destination of I3. - If so, we must show an extra use of it. */ - - if (insn_code_number >= 0 && GET_CODE (SET_DEST (i3set)) == REG - && GET_CODE (SET_DEST (i2set)) == REG - && REGNO (SET_DEST (i3set)) == REGNO (SET_DEST (i2set))) - reg_n_sets[REGNO (SET_DEST (i2set))]++; - } - - /* If we can split it and use I2DEST, go ahead and see if that - helps things be recognized. Verify that none of the registers - are set between I2 and I3. */ - if (insn_code_number < 0 && (split = find_split_point (&newpat, i3)) != 0 -#ifdef HAVE_cc0 - && GET_CODE (i2dest) == REG -#endif - /* We need I2DEST in the proper mode. If it is a hard register - or the only use of a pseudo, we can change its mode. */ - && (GET_MODE (*split) == GET_MODE (i2dest) - || GET_MODE (*split) == VOIDmode - || REGNO (i2dest) < FIRST_PSEUDO_REGISTER - || (reg_n_sets[REGNO (i2dest)] == 1 && ! added_sets_2 - && ! REG_USERVAR_P (i2dest))) - && (next_real_insn (i2) == i3 - || ! use_crosses_set_p (*split, INSN_CUID (i2))) - /* We can't overwrite I2DEST if its value is still used by - NEWPAT. */ - && ! reg_referenced_p (i2dest, newpat)) - { - rtx newdest = i2dest; - - /* Get NEWDEST as a register in the proper mode. We have already - validated that we can do this. */ - if (GET_MODE (i2dest) != GET_MODE (*split) - && GET_MODE (*split) != VOIDmode) - { - newdest = gen_rtx (REG, GET_MODE (*split), REGNO (i2dest)); - - if (REGNO (i2dest) >= FIRST_PSEUDO_REGISTER) - SUBST (regno_reg_rtx[REGNO (i2dest)], newdest); - } - - /* If *SPLIT is a (mult FOO (const_int pow2)), convert it to - an ASHIFT. This can occur if it was inside a PLUS and hence - appeared to be a memory address. This is a kludge. */ - if (GET_CODE (*split) == MULT - && GET_CODE (XEXP (*split, 1)) == CONST_INT - && (i = exact_log2 (INTVAL (XEXP (*split, 1)))) >= 0) - SUBST (*split, gen_rtx_combine (ASHIFT, GET_MODE (*split), - XEXP (*split, 0), GEN_INT (i))); - -#ifdef INSN_SCHEDULING - /* If *SPLIT is a paradoxical SUBREG, when we split it, it should - be written as a ZERO_EXTEND. */ - if (GET_CODE (*split) == SUBREG - && GET_CODE (SUBREG_REG (*split)) == MEM) - SUBST (*split, gen_rtx_combine (ZERO_EXTEND, GET_MODE (*split), - XEXP (*split, 0))); -#endif - - newi2pat = gen_rtx_combine (SET, VOIDmode, newdest, *split); - SUBST (*split, newdest); - i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes); - if (i2_code_number >= 0) - insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); - } - } - - /* Check for a case where we loaded from memory in a narrow mode and - then sign extended it, but we need both registers. In that case, - we have a PARALLEL with both loads from the same memory location. - We can split this into a load from memory followed by a register-register - copy. This saves at least one insn, more if register allocation can - eliminate the copy. */ - - else if (i1 && insn_code_number < 0 && asm_noperands (newpat) < 0 - && GET_CODE (newpat) == PARALLEL - && XVECLEN (newpat, 0) == 2 - && GET_CODE (XVECEXP (newpat, 0, 0)) == SET - && GET_CODE (SET_SRC (XVECEXP (newpat, 0, 0))) == SIGN_EXTEND - && GET_CODE (XVECEXP (newpat, 0, 1)) == SET - && rtx_equal_p (SET_SRC (XVECEXP (newpat, 0, 1)), - XEXP (SET_SRC (XVECEXP (newpat, 0, 0)), 0)) - && ! use_crosses_set_p (SET_SRC (XVECEXP (newpat, 0, 1)), - INSN_CUID (i2)) - && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != ZERO_EXTRACT - && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != STRICT_LOW_PART - && ! reg_overlap_mentioned_p (SET_DEST (XVECEXP (newpat, 0, 1)), - SET_SRC (XVECEXP (newpat, 0, 1))) - && ! find_reg_note (i3, REG_UNUSED, - SET_DEST (XVECEXP (newpat, 0, 0)))) - { - rtx ni2dest; - - newi2pat = XVECEXP (newpat, 0, 0); - ni2dest = SET_DEST (XVECEXP (newpat, 0, 0)); - newpat = XVECEXP (newpat, 0, 1); - SUBST (SET_SRC (newpat), - gen_lowpart_for_combine (GET_MODE (SET_SRC (newpat)), ni2dest)); - i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes); - if (i2_code_number >= 0) - insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); - - if (insn_code_number >= 0) - { - rtx insn; - rtx link; - - /* If we will be able to accept this, we have made a change to the - destination of I3. This can invalidate a LOG_LINKS pointing - to I3. No other part of combine.c makes such a transformation. - - The new I3 will have a destination that was previously the - destination of I1 or I2 and which was used in i2 or I3. Call - distribute_links to make a LOG_LINK from the next use of - that destination. */ - - PATTERN (i3) = newpat; - distribute_links (gen_rtx (INSN_LIST, VOIDmode, i3, NULL_RTX)); - - /* I3 now uses what used to be its destination and which is - now I2's destination. That means we need a LOG_LINK from - I3 to I2. But we used to have one, so we still will. - - However, some later insn might be using I2's dest and have - a LOG_LINK pointing at I3. We must remove this link. - The simplest way to remove the link is to point it at I1, - which we know will be a NOTE. */ - - for (insn = NEXT_INSN (i3); - insn && GET_CODE (insn) != CODE_LABEL - && GET_CODE (PREV_INSN (insn)) != JUMP_INSN; - insn = NEXT_INSN (insn)) - { - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && reg_referenced_p (ni2dest, PATTERN (insn))) - { - for (link = LOG_LINKS (insn); link; - link = XEXP (link, 1)) - if (XEXP (link, 0) == i3) - XEXP (link, 0) = i1; - - break; - } - } - } - } - - /* Similarly, check for a case where we have a PARALLEL of two independent - SETs but we started with three insns. In this case, we can do the sets - as two separate insns. This case occurs when some SET allows two - other insns to combine, but the destination of that SET is still live. */ - - else if (i1 && insn_code_number < 0 && asm_noperands (newpat) < 0 - && GET_CODE (newpat) == PARALLEL - && XVECLEN (newpat, 0) == 2 - && GET_CODE (XVECEXP (newpat, 0, 0)) == SET - && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != ZERO_EXTRACT - && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != STRICT_LOW_PART - && GET_CODE (XVECEXP (newpat, 0, 1)) == SET - && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != ZERO_EXTRACT - && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != STRICT_LOW_PART - && ! use_crosses_set_p (SET_SRC (XVECEXP (newpat, 0, 1)), - INSN_CUID (i2)) - /* Don't pass sets with (USE (MEM ...)) dests to the following. */ - && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != USE - && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != USE - && ! reg_referenced_p (SET_DEST (XVECEXP (newpat, 0, 1)), - XVECEXP (newpat, 0, 0)) - && ! reg_referenced_p (SET_DEST (XVECEXP (newpat, 0, 0)), - XVECEXP (newpat, 0, 1))) - { - newi2pat = XVECEXP (newpat, 0, 1); - newpat = XVECEXP (newpat, 0, 0); - - i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes); - if (i2_code_number >= 0) - insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); - } - - /* If it still isn't recognized, fail and change things back the way they - were. */ - if ((insn_code_number < 0 - /* Is the result a reasonable ASM_OPERANDS? */ - && (! check_asm_operands (newpat) || added_sets_1 || added_sets_2))) - { - undo_all (); - return 0; - } - - /* If we had to change another insn, make sure it is valid also. */ - if (undobuf.other_insn) - { - rtx other_notes = REG_NOTES (undobuf.other_insn); - rtx other_pat = PATTERN (undobuf.other_insn); - rtx new_other_notes; - rtx note, next; - - other_code_number = recog_for_combine (&other_pat, undobuf.other_insn, - &new_other_notes); - - if (other_code_number < 0 && ! check_asm_operands (other_pat)) - { - undo_all (); - return 0; - } - - PATTERN (undobuf.other_insn) = other_pat; - - /* If any of the notes in OTHER_INSN were REG_UNUSED, ensure that they - are still valid. Then add any non-duplicate notes added by - recog_for_combine. */ - for (note = REG_NOTES (undobuf.other_insn); note; note = next) - { - next = XEXP (note, 1); - - if (REG_NOTE_KIND (note) == REG_UNUSED - && ! reg_set_p (XEXP (note, 0), PATTERN (undobuf.other_insn))) - { - if (GET_CODE (XEXP (note, 0)) == REG) - reg_n_deaths[REGNO (XEXP (note, 0))]--; - - remove_note (undobuf.other_insn, note); - } - } - - for (note = new_other_notes; note; note = XEXP (note, 1)) - if (GET_CODE (XEXP (note, 0)) == REG) - reg_n_deaths[REGNO (XEXP (note, 0))]++; - - distribute_notes (new_other_notes, undobuf.other_insn, - undobuf.other_insn, NULL_RTX, NULL_RTX, NULL_RTX); - } - - /* We now know that we can do this combination. Merge the insns and - update the status of registers and LOG_LINKS. */ - - { - rtx i3notes, i2notes, i1notes = 0; - rtx i3links, i2links, i1links = 0; - rtx midnotes = 0; - int all_adjacent = (next_real_insn (i2) == i3 - && (i1 == 0 || next_real_insn (i1) == i2)); - register int regno; - /* Compute which registers we expect to eliminate. */ - rtx elim_i2 = (newi2pat || i2dest_in_i2src || i2dest_in_i1src - ? 0 : i2dest); - rtx elim_i1 = i1 == 0 || i1dest_in_i1src ? 0 : i1dest; - - /* Get the old REG_NOTES and LOG_LINKS from all our insns and - clear them. */ - i3notes = REG_NOTES (i3), i3links = LOG_LINKS (i3); - i2notes = REG_NOTES (i2), i2links = LOG_LINKS (i2); - if (i1) - i1notes = REG_NOTES (i1), i1links = LOG_LINKS (i1); - - /* Ensure that we do not have something that should not be shared but - occurs multiple times in the new insns. Check this by first - resetting all the `used' flags and then copying anything is shared. */ - - reset_used_flags (i3notes); - reset_used_flags (i2notes); - reset_used_flags (i1notes); - reset_used_flags (newpat); - reset_used_flags (newi2pat); - if (undobuf.other_insn) - reset_used_flags (PATTERN (undobuf.other_insn)); - - i3notes = copy_rtx_if_shared (i3notes); - i2notes = copy_rtx_if_shared (i2notes); - i1notes = copy_rtx_if_shared (i1notes); - newpat = copy_rtx_if_shared (newpat); - newi2pat = copy_rtx_if_shared (newi2pat); - if (undobuf.other_insn) - reset_used_flags (PATTERN (undobuf.other_insn)); - - INSN_CODE (i3) = insn_code_number; - PATTERN (i3) = newpat; - if (undobuf.other_insn) - INSN_CODE (undobuf.other_insn) = other_code_number; - - /* We had one special case above where I2 had more than one set and - we replaced a destination of one of those sets with the destination - of I3. In that case, we have to update LOG_LINKS of insns later - in this basic block. Note that this (expensive) case is rare. */ - - if (GET_CODE (PATTERN (i2)) == PARALLEL) - for (i = 0; i < XVECLEN (PATTERN (i2), 0); i++) - if (GET_CODE (SET_DEST (XVECEXP (PATTERN (i2), 0, i))) == REG - && SET_DEST (XVECEXP (PATTERN (i2), 0, i)) != i2dest - && ! find_reg_note (i2, REG_UNUSED, - SET_DEST (XVECEXP (PATTERN (i2), 0, i)))) - { - register rtx insn; - - for (insn = NEXT_INSN (i2); insn; insn = NEXT_INSN (insn)) - { - if (insn != i3 && GET_RTX_CLASS (GET_CODE (insn)) == 'i') - for (link = LOG_LINKS (insn); link; link = XEXP (link, 1)) - if (XEXP (link, 0) == i2) - XEXP (link, 0) = i3; - - if (GET_CODE (insn) == CODE_LABEL - || GET_CODE (insn) == JUMP_INSN) - break; - } - } - - LOG_LINKS (i3) = 0; - REG_NOTES (i3) = 0; - LOG_LINKS (i2) = 0; - REG_NOTES (i2) = 0; - - if (newi2pat) - { - INSN_CODE (i2) = i2_code_number; - PATTERN (i2) = newi2pat; - } - else - { - PUT_CODE (i2, NOTE); - NOTE_LINE_NUMBER (i2) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (i2) = 0; - } - - if (i1) - { - LOG_LINKS (i1) = 0; - REG_NOTES (i1) = 0; - PUT_CODE (i1, NOTE); - NOTE_LINE_NUMBER (i1) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (i1) = 0; - } - - /* Get death notes for everything that is now used in either I3 or - I2 and used to die in a previous insn. */ - - move_deaths (newpat, i1 ? INSN_CUID (i1) : INSN_CUID (i2), i3, &midnotes); - if (newi2pat) - move_deaths (newi2pat, INSN_CUID (i1), i2, &midnotes); - - /* Distribute all the LOG_LINKS and REG_NOTES from I1, I2, and I3. */ - if (i3notes) - distribute_notes (i3notes, i3, i3, newi2pat ? i2 : NULL_RTX, - elim_i2, elim_i1); - if (i2notes) - distribute_notes (i2notes, i2, i3, newi2pat ? i2 : NULL_RTX, - elim_i2, elim_i1); - if (i1notes) - distribute_notes (i1notes, i1, i3, newi2pat ? i2 : NULL_RTX, - elim_i2, elim_i1); - if (midnotes) - distribute_notes (midnotes, NULL_RTX, i3, newi2pat ? i2 : NULL_RTX, - elim_i2, elim_i1); - - /* Distribute any notes added to I2 or I3 by recog_for_combine. We - know these are REG_UNUSED and want them to go to the desired insn, - so we always pass it as i3. We have not counted the notes in - reg_n_deaths yet, so we need to do so now. */ - - if (newi2pat && new_i2_notes) - { - for (temp = new_i2_notes; temp; temp = XEXP (temp, 1)) - if (GET_CODE (XEXP (temp, 0)) == REG) - reg_n_deaths[REGNO (XEXP (temp, 0))]++; - - distribute_notes (new_i2_notes, i2, i2, NULL_RTX, NULL_RTX, NULL_RTX); - } - - if (new_i3_notes) - { - for (temp = new_i3_notes; temp; temp = XEXP (temp, 1)) - if (GET_CODE (XEXP (temp, 0)) == REG) - reg_n_deaths[REGNO (XEXP (temp, 0))]++; - - distribute_notes (new_i3_notes, i3, i3, NULL_RTX, NULL_RTX, NULL_RTX); - } - - /* If I3DEST was used in I3SRC, it really died in I3. We may need to - put a REG_DEAD note for it somewhere. Similarly for I2 and I1. - Show an additional death due to the REG_DEAD note we make here. If - we discard it in distribute_notes, we will decrement it again. */ - - if (i3dest_killed) - { - if (GET_CODE (i3dest_killed) == REG) - reg_n_deaths[REGNO (i3dest_killed)]++; - - distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i3dest_killed, - NULL_RTX), - NULL_RTX, i3, newi2pat ? i2 : NULL_RTX, - NULL_RTX, NULL_RTX); - } - - /* For I2 and I1, we have to be careful. If NEWI2PAT exists and sets - I2DEST or I1DEST, the death must be somewhere before I2, not I3. If - we passed I3 in that case, it might delete I2. */ - - if (i2dest_in_i2src) - { - if (GET_CODE (i2dest) == REG) - reg_n_deaths[REGNO (i2dest)]++; - - if (newi2pat && reg_set_p (i2dest, newi2pat)) - distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i2dest, NULL_RTX), - NULL_RTX, i2, NULL_RTX, NULL_RTX, NULL_RTX); - else - distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i2dest, NULL_RTX), - NULL_RTX, i3, newi2pat ? i2 : NULL_RTX, - NULL_RTX, NULL_RTX); - } - - if (i1dest_in_i1src) - { - if (GET_CODE (i1dest) == REG) - reg_n_deaths[REGNO (i1dest)]++; - - if (newi2pat && reg_set_p (i1dest, newi2pat)) - distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i1dest, NULL_RTX), - NULL_RTX, i2, NULL_RTX, NULL_RTX, NULL_RTX); - else - distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i1dest, NULL_RTX), - NULL_RTX, i3, newi2pat ? i2 : NULL_RTX, - NULL_RTX, NULL_RTX); - } - - distribute_links (i3links); - distribute_links (i2links); - distribute_links (i1links); - - if (GET_CODE (i2dest) == REG) - { - rtx link; - rtx i2_insn = 0, i2_val = 0, set; - - /* The insn that used to set this register doesn't exist, and - this life of the register may not exist either. See if one of - I3's links points to an insn that sets I2DEST. If it does, - that is now the last known value for I2DEST. If we don't update - this and I2 set the register to a value that depended on its old - contents, we will get confused. If this insn is used, thing - will be set correctly in combine_instructions. */ - - for (link = LOG_LINKS (i3); link; link = XEXP (link, 1)) - if ((set = single_set (XEXP (link, 0))) != 0 - && rtx_equal_p (i2dest, SET_DEST (set))) - i2_insn = XEXP (link, 0), i2_val = SET_SRC (set); - - record_value_for_reg (i2dest, i2_insn, i2_val); - - /* If the reg formerly set in I2 died only once and that was in I3, - zero its use count so it won't make `reload' do any work. */ - if (! added_sets_2 && newi2pat == 0) - { - regno = REGNO (i2dest); - reg_n_sets[regno]--; - if (reg_n_sets[regno] == 0 - && ! (basic_block_live_at_start[0][regno / REGSET_ELT_BITS] - & ((REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS)))) - reg_n_refs[regno] = 0; - } - } - - if (i1 && GET_CODE (i1dest) == REG) - { - rtx link; - rtx i1_insn = 0, i1_val = 0, set; - - for (link = LOG_LINKS (i3); link; link = XEXP (link, 1)) - if ((set = single_set (XEXP (link, 0))) != 0 - && rtx_equal_p (i1dest, SET_DEST (set))) - i1_insn = XEXP (link, 0), i1_val = SET_SRC (set); - - record_value_for_reg (i1dest, i1_insn, i1_val); - - regno = REGNO (i1dest); - if (! added_sets_1) - { - reg_n_sets[regno]--; - if (reg_n_sets[regno] == 0 - && ! (basic_block_live_at_start[0][regno / REGSET_ELT_BITS] - & ((REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS)))) - reg_n_refs[regno] = 0; - } - } - - /* Update reg_nonzero_bits et al for any changes that may have been made - to this insn. */ - - note_stores (newpat, set_nonzero_bits_and_sign_copies); - if (newi2pat) - note_stores (newi2pat, set_nonzero_bits_and_sign_copies); - - /* If I3 is now an unconditional jump, ensure that it has a - BARRIER following it since it may have initially been a - conditional jump. It may also be the last nonnote insn. */ - - if ((GET_CODE (newpat) == RETURN || simplejump_p (i3)) - && ((temp = next_nonnote_insn (i3)) == NULL_RTX - || GET_CODE (temp) != BARRIER)) - emit_barrier_after (i3); - } - - combine_successes++; - - return newi2pat ? i2 : i3; -} - -/* Undo all the modifications recorded in undobuf. */ - -static void -undo_all () -{ - register int i; - if (undobuf.num_undo > MAX_UNDO) - undobuf.num_undo = MAX_UNDO; - for (i = undobuf.num_undo - 1; i >= 0; i--) - { - if (undobuf.undo[i].is_int) - *undobuf.undo[i].where.i = undobuf.undo[i].old_contents.i; - else - *undobuf.undo[i].where.rtx = undobuf.undo[i].old_contents.rtx; - - } - - obfree (undobuf.storage); - undobuf.num_undo = 0; -} - -/* Find the innermost point within the rtx at LOC, possibly LOC itself, - where we have an arithmetic expression and return that point. LOC will - be inside INSN. - - try_combine will call this function to see if an insn can be split into - two insns. */ - -static rtx * -find_split_point (loc, insn) - rtx *loc; - rtx insn; -{ - rtx x = *loc; - enum rtx_code code = GET_CODE (x); - rtx *split; - int len = 0, pos, unsignedp; - rtx inner; - - /* First special-case some codes. */ - switch (code) - { - case SUBREG: -#ifdef INSN_SCHEDULING - /* If we are making a paradoxical SUBREG invalid, it becomes a split - point. */ - if (GET_CODE (SUBREG_REG (x)) == MEM) - return loc; -#endif - return find_split_point (&SUBREG_REG (x), insn); - - case MEM: -#ifdef HAVE_lo_sum - /* If we have (mem (const ..)) or (mem (symbol_ref ...)), split it - using LO_SUM and HIGH. */ - if (GET_CODE (XEXP (x, 0)) == CONST - || GET_CODE (XEXP (x, 0)) == SYMBOL_REF) - { - SUBST (XEXP (x, 0), - gen_rtx_combine (LO_SUM, Pmode, - gen_rtx_combine (HIGH, Pmode, XEXP (x, 0)), - XEXP (x, 0))); - return &XEXP (XEXP (x, 0), 0); - } -#endif - - /* If we have a PLUS whose second operand is a constant and the - address is not valid, perhaps will can split it up using - the machine-specific way to split large constants. We use - the first psuedo-reg (one of the virtual regs) as a placeholder; - it will not remain in the result. */ - if (GET_CODE (XEXP (x, 0)) == PLUS - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && ! memory_address_p (GET_MODE (x), XEXP (x, 0))) - { - rtx reg = regno_reg_rtx[FIRST_PSEUDO_REGISTER]; - rtx seq = split_insns (gen_rtx (SET, VOIDmode, reg, XEXP (x, 0)), - subst_insn); - - /* This should have produced two insns, each of which sets our - placeholder. If the source of the second is a valid address, - we can make put both sources together and make a split point - in the middle. */ - - if (seq && XVECLEN (seq, 0) == 2 - && GET_CODE (XVECEXP (seq, 0, 0)) == INSN - && GET_CODE (PATTERN (XVECEXP (seq, 0, 0))) == SET - && SET_DEST (PATTERN (XVECEXP (seq, 0, 0))) == reg - && ! reg_mentioned_p (reg, - SET_SRC (PATTERN (XVECEXP (seq, 0, 0)))) - && GET_CODE (XVECEXP (seq, 0, 1)) == INSN - && GET_CODE (PATTERN (XVECEXP (seq, 0, 1))) == SET - && SET_DEST (PATTERN (XVECEXP (seq, 0, 1))) == reg - && memory_address_p (GET_MODE (x), - SET_SRC (PATTERN (XVECEXP (seq, 0, 1))))) - { - rtx src1 = SET_SRC (PATTERN (XVECEXP (seq, 0, 0))); - rtx src2 = SET_SRC (PATTERN (XVECEXP (seq, 0, 1))); - - /* Replace the placeholder in SRC2 with SRC1. If we can - find where in SRC2 it was placed, that can become our - split point and we can replace this address with SRC2. - Just try two obvious places. */ - - src2 = replace_rtx (src2, reg, src1); - split = 0; - if (XEXP (src2, 0) == src1) - split = &XEXP (src2, 0); - else if (GET_RTX_FORMAT (GET_CODE (XEXP (src2, 0)))[0] == 'e' - && XEXP (XEXP (src2, 0), 0) == src1) - split = &XEXP (XEXP (src2, 0), 0); - - if (split) - { - SUBST (XEXP (x, 0), src2); - return split; - } - } - - /* If that didn't work, perhaps the first operand is complex and - needs to be computed separately, so make a split point there. - This will occur on machines that just support REG + CONST - and have a constant moved through some previous computation. */ - - else if (GET_RTX_CLASS (GET_CODE (XEXP (XEXP (x, 0), 0))) != 'o' - && ! (GET_CODE (XEXP (XEXP (x, 0), 0)) == SUBREG - && (GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (XEXP (x, 0), 0)))) - == 'o'))) - return &XEXP (XEXP (x, 0), 0); - } - break; - - case SET: -#ifdef HAVE_cc0 - /* If SET_DEST is CC0 and SET_SRC is not an operand, a COMPARE, or a - ZERO_EXTRACT, the most likely reason why this doesn't match is that - we need to put the operand into a register. So split at that - point. */ - - if (SET_DEST (x) == cc0_rtx - && GET_CODE (SET_SRC (x)) != COMPARE - && GET_CODE (SET_SRC (x)) != ZERO_EXTRACT - && GET_RTX_CLASS (GET_CODE (SET_SRC (x))) != 'o' - && ! (GET_CODE (SET_SRC (x)) == SUBREG - && GET_RTX_CLASS (GET_CODE (SUBREG_REG (SET_SRC (x)))) == 'o')) - return &SET_SRC (x); -#endif - - /* See if we can split SET_SRC as it stands. */ - split = find_split_point (&SET_SRC (x), insn); - if (split && split != &SET_SRC (x)) - return split; - - /* See if this is a bitfield assignment with everything constant. If - so, this is an IOR of an AND, so split it into that. */ - if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT - && (GET_MODE_BITSIZE (GET_MODE (XEXP (SET_DEST (x), 0))) - <= HOST_BITS_PER_WIDE_INT) - && GET_CODE (XEXP (SET_DEST (x), 1)) == CONST_INT - && GET_CODE (XEXP (SET_DEST (x), 2)) == CONST_INT - && GET_CODE (SET_SRC (x)) == CONST_INT - && ((INTVAL (XEXP (SET_DEST (x), 1)) - + INTVAL (XEXP (SET_DEST (x), 2))) - <= GET_MODE_BITSIZE (GET_MODE (XEXP (SET_DEST (x), 0)))) - && ! side_effects_p (XEXP (SET_DEST (x), 0))) - { - int pos = INTVAL (XEXP (SET_DEST (x), 2)); - int len = INTVAL (XEXP (SET_DEST (x), 1)); - int src = INTVAL (SET_SRC (x)); - rtx dest = XEXP (SET_DEST (x), 0); - enum machine_mode mode = GET_MODE (dest); - unsigned HOST_WIDE_INT mask = ((HOST_WIDE_INT) 1 << len) - 1; - -#if BITS_BIG_ENDIAN - pos = GET_MODE_BITSIZE (mode) - len - pos; -#endif - - if (src == mask) - SUBST (SET_SRC (x), - gen_binary (IOR, mode, dest, GEN_INT (src << pos))); - else - SUBST (SET_SRC (x), - gen_binary (IOR, mode, - gen_binary (AND, mode, dest, - GEN_INT (~ (mask << pos) - & GET_MODE_MASK (mode))), - GEN_INT (src << pos))); - - SUBST (SET_DEST (x), dest); - - split = find_split_point (&SET_SRC (x), insn); - if (split && split != &SET_SRC (x)) - return split; - } - - /* Otherwise, see if this is an operation that we can split into two. - If so, try to split that. */ - code = GET_CODE (SET_SRC (x)); - - switch (code) - { - case AND: - /* If we are AND'ing with a large constant that is only a single - bit and the result is only being used in a context where we - need to know if it is zero or non-zero, replace it with a bit - extraction. This will avoid the large constant, which might - have taken more than one insn to make. If the constant were - not a valid argument to the AND but took only one insn to make, - this is no worse, but if it took more than one insn, it will - be better. */ - - if (GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT - && GET_CODE (XEXP (SET_SRC (x), 0)) == REG - && (pos = exact_log2 (INTVAL (XEXP (SET_SRC (x), 1)))) >= 7 - && GET_CODE (SET_DEST (x)) == REG - && (split = find_single_use (SET_DEST (x), insn, NULL_PTR)) != 0 - && (GET_CODE (*split) == EQ || GET_CODE (*split) == NE) - && XEXP (*split, 0) == SET_DEST (x) - && XEXP (*split, 1) == const0_rtx) - { - SUBST (SET_SRC (x), - make_extraction (GET_MODE (SET_DEST (x)), - XEXP (SET_SRC (x), 0), - pos, NULL_RTX, 1, 1, 0, 0)); - return find_split_point (loc, insn); - } - break; - - case SIGN_EXTEND: - inner = XEXP (SET_SRC (x), 0); - pos = 0; - len = GET_MODE_BITSIZE (GET_MODE (inner)); - unsignedp = 0; - break; - - case SIGN_EXTRACT: - case ZERO_EXTRACT: - if (GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT - && GET_CODE (XEXP (SET_SRC (x), 2)) == CONST_INT) - { - inner = XEXP (SET_SRC (x), 0); - len = INTVAL (XEXP (SET_SRC (x), 1)); - pos = INTVAL (XEXP (SET_SRC (x), 2)); - -#if BITS_BIG_ENDIAN - pos = GET_MODE_BITSIZE (GET_MODE (inner)) - len - pos; -#endif - unsignedp = (code == ZERO_EXTRACT); - } - break; - } - - if (len && pos >= 0 && pos + len <= GET_MODE_BITSIZE (GET_MODE (inner))) - { - enum machine_mode mode = GET_MODE (SET_SRC (x)); - - /* For unsigned, we have a choice of a shift followed by an - AND or two shifts. Use two shifts for field sizes where the - constant might be too large. We assume here that we can - always at least get 8-bit constants in an AND insn, which is - true for every current RISC. */ - - if (unsignedp && len <= 8) - { - SUBST (SET_SRC (x), - gen_rtx_combine - (AND, mode, - gen_rtx_combine (LSHIFTRT, mode, - gen_lowpart_for_combine (mode, inner), - GEN_INT (pos)), - GEN_INT (((HOST_WIDE_INT) 1 << len) - 1))); - - split = find_split_point (&SET_SRC (x), insn); - if (split && split != &SET_SRC (x)) - return split; - } - else - { - SUBST (SET_SRC (x), - gen_rtx_combine - (unsignedp ? LSHIFTRT : ASHIFTRT, mode, - gen_rtx_combine (ASHIFT, mode, - gen_lowpart_for_combine (mode, inner), - GEN_INT (GET_MODE_BITSIZE (mode) - - len - pos)), - GEN_INT (GET_MODE_BITSIZE (mode) - len))); - - split = find_split_point (&SET_SRC (x), insn); - if (split && split != &SET_SRC (x)) - return split; - } - } - - /* See if this is a simple operation with a constant as the second - operand. It might be that this constant is out of range and hence - could be used as a split point. */ - if ((GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '2' - || GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == 'c' - || GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '<') - && CONSTANT_P (XEXP (SET_SRC (x), 1)) - && (GET_RTX_CLASS (GET_CODE (XEXP (SET_SRC (x), 0))) == 'o' - || (GET_CODE (XEXP (SET_SRC (x), 0)) == SUBREG - && (GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (SET_SRC (x), 0)))) - == 'o')))) - return &XEXP (SET_SRC (x), 1); - - /* Finally, see if this is a simple operation with its first operand - not in a register. The operation might require this operand in a - register, so return it as a split point. We can always do this - because if the first operand were another operation, we would have - already found it as a split point. */ - if ((GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '2' - || GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == 'c' - || GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '<' - || GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '1') - && ! register_operand (XEXP (SET_SRC (x), 0), VOIDmode)) - return &XEXP (SET_SRC (x), 0); - - return 0; - - case AND: - case IOR: - /* We write NOR as (and (not A) (not B)), but if we don't have a NOR, - it is better to write this as (not (ior A B)) so we can split it. - Similarly for IOR. */ - if (GET_CODE (XEXP (x, 0)) == NOT && GET_CODE (XEXP (x, 1)) == NOT) - { - SUBST (*loc, - gen_rtx_combine (NOT, GET_MODE (x), - gen_rtx_combine (code == IOR ? AND : IOR, - GET_MODE (x), - XEXP (XEXP (x, 0), 0), - XEXP (XEXP (x, 1), 0)))); - return find_split_point (loc, insn); - } - - /* Many RISC machines have a large set of logical insns. If the - second operand is a NOT, put it first so we will try to split the - other operand first. */ - if (GET_CODE (XEXP (x, 1)) == NOT) - { - rtx tem = XEXP (x, 0); - SUBST (XEXP (x, 0), XEXP (x, 1)); - SUBST (XEXP (x, 1), tem); - } - break; - } - - /* Otherwise, select our actions depending on our rtx class. */ - switch (GET_RTX_CLASS (code)) - { - case 'b': /* This is ZERO_EXTRACT and SIGN_EXTRACT. */ - case '3': - split = find_split_point (&XEXP (x, 2), insn); - if (split) - return split; - /* ... fall through ... */ - case '2': - case 'c': - case '<': - split = find_split_point (&XEXP (x, 1), insn); - if (split) - return split; - /* ... fall through ... */ - case '1': - /* Some machines have (and (shift ...) ...) insns. If X is not - an AND, but XEXP (X, 0) is, use it as our split point. */ - if (GET_CODE (x) != AND && GET_CODE (XEXP (x, 0)) == AND) - return &XEXP (x, 0); - - split = find_split_point (&XEXP (x, 0), insn); - if (split) - return split; - return loc; - } - - /* Otherwise, we don't have a split point. */ - return 0; -} - -/* Throughout X, replace FROM with TO, and return the result. - The result is TO if X is FROM; - otherwise the result is X, but its contents may have been modified. - If they were modified, a record was made in undobuf so that - undo_all will (among other things) return X to its original state. - - If the number of changes necessary is too much to record to undo, - the excess changes are not made, so the result is invalid. - The changes already made can still be undone. - undobuf.num_undo is incremented for such changes, so by testing that - the caller can tell whether the result is valid. - - `n_occurrences' is incremented each time FROM is replaced. - - IN_DEST is non-zero if we are processing the SET_DEST of a SET. - - UNIQUE_COPY is non-zero if each substitution must be unique. We do this - by copying if `n_occurrences' is non-zero. */ - -static rtx -subst (x, from, to, in_dest, unique_copy) - register rtx x, from, to; - int in_dest; - int unique_copy; -{ - register char *fmt; - register int len, i; - register enum rtx_code code = GET_CODE (x), orig_code = code; - rtx temp; - enum machine_mode mode = GET_MODE (x); - enum machine_mode op0_mode = VOIDmode; - rtx other_insn; - rtx *cc_use; - int n_restarts = 0; - -/* FAKE_EXTEND_SAFE_P (MODE, FROM) is 1 if (subreg:MODE FROM 0) is a safe - replacement for (zero_extend:MODE FROM) or (sign_extend:MODE FROM). - If it is 0, that cannot be done. We can now do this for any MEM - because (SUBREG (MEM...)) is guaranteed to cause the MEM to be reloaded. - If not for that, MEM's would very rarely be safe. */ - -/* Reject MODEs bigger than a word, because we might not be able - to reference a two-register group starting with an arbitrary register - (and currently gen_lowpart might crash for a SUBREG). */ - -#define FAKE_EXTEND_SAFE_P(MODE, FROM) \ - (GET_MODE_SIZE (MODE) <= UNITS_PER_WORD) - -/* Two expressions are equal if they are identical copies of a shared - RTX or if they are both registers with the same register number - and mode. */ - -#define COMBINE_RTX_EQUAL_P(X,Y) \ - ((X) == (Y) \ - || (GET_CODE (X) == REG && GET_CODE (Y) == REG \ - && REGNO (X) == REGNO (Y) && GET_MODE (X) == GET_MODE (Y))) - - if (! in_dest && COMBINE_RTX_EQUAL_P (x, from)) - { - n_occurrences++; - return (unique_copy && n_occurrences > 1 ? copy_rtx (to) : to); - } - - /* If X and FROM are the same register but different modes, they will - not have been seen as equal above. However, flow.c will make a - LOG_LINKS entry for that case. If we do nothing, we will try to - rerecognize our original insn and, when it succeeds, we will - delete the feeding insn, which is incorrect. - - So force this insn not to match in this (rare) case. */ - if (! in_dest && code == REG && GET_CODE (from) == REG - && REGNO (x) == REGNO (from)) - return gen_rtx (CLOBBER, GET_MODE (x), const0_rtx); - - /* If this is an object, we are done unless it is a MEM or LO_SUM, both - of which may contain things that can be combined. */ - if (code != MEM && code != LO_SUM && GET_RTX_CLASS (code) == 'o') - return x; - - /* It is possible to have a subexpression appear twice in the insn. - Suppose that FROM is a register that appears within TO. - Then, after that subexpression has been scanned once by `subst', - the second time it is scanned, TO may be found. If we were - to scan TO here, we would find FROM within it and create a - self-referent rtl structure which is completely wrong. */ - if (COMBINE_RTX_EQUAL_P (x, to)) - return to; - - len = GET_RTX_LENGTH (code); - fmt = GET_RTX_FORMAT (code); - - /* We don't need to process a SET_DEST that is a register, CC0, or PC, so - set up to skip this common case. All other cases where we want to - suppress replacing something inside a SET_SRC are handled via the - IN_DEST operand. */ - if (code == SET - && (GET_CODE (SET_DEST (x)) == REG - || GET_CODE (SET_DEST (x)) == CC0 - || GET_CODE (SET_DEST (x)) == PC)) - fmt = "ie"; - - /* Get the mode of operand 0 in case X is now a SIGN_EXTEND of a constant. */ - if (fmt[0] == 'e') - op0_mode = GET_MODE (XEXP (x, 0)); - - for (i = 0; i < len; i++) - { - if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - { - register rtx new; - if (COMBINE_RTX_EQUAL_P (XVECEXP (x, i, j), from)) - { - new = (unique_copy && n_occurrences ? copy_rtx (to) : to); - n_occurrences++; - } - else - { - new = subst (XVECEXP (x, i, j), from, to, 0, unique_copy); - - /* If this substitution failed, this whole thing fails. */ - if (GET_CODE (new) == CLOBBER && XEXP (new, 0) == const0_rtx) - return new; - } - - SUBST (XVECEXP (x, i, j), new); - } - } - else if (fmt[i] == 'e') - { - register rtx new; - - if (COMBINE_RTX_EQUAL_P (XEXP (x, i), from)) - { - new = (unique_copy && n_occurrences ? copy_rtx (to) : to); - n_occurrences++; - } - else - /* If we are in a SET_DEST, suppress most cases unless we - have gone inside a MEM, in which case we want to - simplify the address. We assume here that things that - are actually part of the destination have their inner - parts in the first expression. This is true for SUBREG, - STRICT_LOW_PART, and ZERO_EXTRACT, which are the only - things aside from REG and MEM that should appear in a - SET_DEST. */ - new = subst (XEXP (x, i), from, to, - (((in_dest - && (code == SUBREG || code == STRICT_LOW_PART - || code == ZERO_EXTRACT)) - || code == SET) - && i == 0), unique_copy); - - /* If we found that we will have to reject this combination, - indicate that by returning the CLOBBER ourselves, rather than - an expression containing it. This will speed things up as - well as prevent accidents where two CLOBBERs are considered - to be equal, thus producing an incorrect simplification. */ - - if (GET_CODE (new) == CLOBBER && XEXP (new, 0) == const0_rtx) - return new; - - SUBST (XEXP (x, i), new); - } - } - - /* We come back to here if we have replaced the expression with one of - a different code and it is likely that further simplification will be - possible. */ - - restart: - - /* If we have restarted more than 4 times, we are probably looping, so - give up. */ - if (++n_restarts > 4) - return x; - - /* If we are restarting at all, it means that we no longer know the - original mode of operand 0 (since we have probably changed the - form of X). */ - - if (n_restarts > 1) - op0_mode = VOIDmode; - - code = GET_CODE (x); - - /* If this is a commutative operation, put a constant last and a complex - expression first. We don't need to do this for comparisons here. */ - if (GET_RTX_CLASS (code) == 'c' - && ((CONSTANT_P (XEXP (x, 0)) && GET_CODE (XEXP (x, 1)) != CONST_INT) - || (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == 'o' - && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) != 'o') - || (GET_CODE (XEXP (x, 0)) == SUBREG - && GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (x, 0)))) == 'o' - && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) != 'o'))) - { - temp = XEXP (x, 0); - SUBST (XEXP (x, 0), XEXP (x, 1)); - SUBST (XEXP (x, 1), temp); - } - - /* If this is a PLUS, MINUS, or MULT, and the first operand is the - sign extension of a PLUS with a constant, reverse the order of the sign - extension and the addition. Note that this not the same as the original - code, but overflow is undefined for signed values. Also note that the - PLUS will have been partially moved "inside" the sign-extension, so that - the first operand of X will really look like: - (ashiftrt (plus (ashift A C4) C5) C4). - We convert this to - (plus (ashiftrt (ashift A C4) C2) C4) - and replace the first operand of X with that expression. Later parts - of this function may simplify the expression further. - - For example, if we start with (mult (sign_extend (plus A C1)) C2), - we swap the SIGN_EXTEND and PLUS. Later code will apply the - distributive law to produce (plus (mult (sign_extend X) C1) C3). - - We do this to simplify address expressions. */ - - if ((code == PLUS || code == MINUS || code == MULT) - && GET_CODE (XEXP (x, 0)) == ASHIFTRT - && GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS - && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == ASHIFT - && GET_CODE (XEXP (XEXP (XEXP (XEXP (x, 0), 0), 0), 1)) == CONST_INT - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && XEXP (XEXP (XEXP (XEXP (x, 0), 0), 0), 1) == XEXP (XEXP (x, 0), 1) - && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT - && (temp = simplify_binary_operation (ASHIFTRT, mode, - XEXP (XEXP (XEXP (x, 0), 0), 1), - XEXP (XEXP (x, 0), 1))) != 0) - { - rtx new - = simplify_shift_const (NULL_RTX, ASHIFT, mode, - XEXP (XEXP (XEXP (XEXP (x, 0), 0), 0), 0), - INTVAL (XEXP (XEXP (x, 0), 1))); - - new = simplify_shift_const (NULL_RTX, ASHIFTRT, mode, new, - INTVAL (XEXP (XEXP (x, 0), 1))); - - SUBST (XEXP (x, 0), gen_binary (PLUS, mode, new, temp)); - } - - /* If this is a simple operation applied to an IF_THEN_ELSE, try - applying it to the arms of the IF_THEN_ELSE. This often simplifies - things. Don't deal with operations that change modes here. */ - - if ((GET_RTX_CLASS (code) == '2' || GET_RTX_CLASS (code) == 'c') - && GET_CODE (XEXP (x, 0)) == IF_THEN_ELSE) - { - /* Don't do this by using SUBST inside X since we might be messing - up a shared expression. */ - rtx cond = XEXP (XEXP (x, 0), 0); - rtx t_arm = subst (gen_binary (code, mode, XEXP (XEXP (x, 0), 1), - XEXP (x, 1)), - pc_rtx, pc_rtx, 0, 0); - rtx f_arm = subst (gen_binary (code, mode, XEXP (XEXP (x, 0), 2), - XEXP (x, 1)), - pc_rtx, pc_rtx, 0, 0); - - - x = gen_rtx (IF_THEN_ELSE, mode, cond, t_arm, f_arm); - goto restart; - } - - else if (GET_RTX_CLASS (code) == '1' - && GET_CODE (XEXP (x, 0)) == IF_THEN_ELSE - && GET_MODE (XEXP (x, 0)) == mode) - { - rtx cond = XEXP (XEXP (x, 0), 0); - rtx t_arm = subst (gen_unary (code, mode, XEXP (XEXP (x, 0), 1)), - pc_rtx, pc_rtx, 0, 0); - rtx f_arm = subst (gen_unary (code, mode, XEXP (XEXP (x, 0), 2)), - pc_rtx, pc_rtx, 0, 0); - - x = gen_rtx_combine (IF_THEN_ELSE, mode, cond, t_arm, f_arm); - goto restart; - } - - /* Try to fold this expression in case we have constants that weren't - present before. */ - temp = 0; - switch (GET_RTX_CLASS (code)) - { - case '1': - temp = simplify_unary_operation (code, mode, XEXP (x, 0), op0_mode); - break; - case '<': - temp = simplify_relational_operation (code, op0_mode, - XEXP (x, 0), XEXP (x, 1)); -#ifdef FLOAT_STORE_FLAG_VALUE - if (temp != 0 && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) - temp = ((temp == const0_rtx) ? CONST0_RTX (GET_MODE (x)) - : immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, GET_MODE (x))); -#endif - break; - case 'c': - case '2': - temp = simplify_binary_operation (code, mode, XEXP (x, 0), XEXP (x, 1)); - break; - case 'b': - case '3': - temp = simplify_ternary_operation (code, mode, op0_mode, XEXP (x, 0), - XEXP (x, 1), XEXP (x, 2)); - break; - } - - if (temp) - x = temp, code = GET_CODE (temp); - - /* First see if we can apply the inverse distributive law. */ - if (code == PLUS || code == MINUS || code == IOR || code == XOR) - { - x = apply_distributive_law (x); - code = GET_CODE (x); - } - - /* If CODE is an associative operation not otherwise handled, see if we - can associate some operands. This can win if they are constants or - if they are logically related (i.e. (a & b) & a. */ - if ((code == PLUS || code == MINUS - || code == MULT || code == AND || code == IOR || code == XOR - || code == DIV || code == UDIV - || code == SMAX || code == SMIN || code == UMAX || code == UMIN) - && GET_MODE_CLASS (mode) == MODE_INT) - { - if (GET_CODE (XEXP (x, 0)) == code) - { - rtx other = XEXP (XEXP (x, 0), 0); - rtx inner_op0 = XEXP (XEXP (x, 0), 1); - rtx inner_op1 = XEXP (x, 1); - rtx inner; - - /* Make sure we pass the constant operand if any as the second - one if this is a commutative operation. */ - if (CONSTANT_P (inner_op0) && GET_RTX_CLASS (code) == 'c') - { - rtx tem = inner_op0; - inner_op0 = inner_op1; - inner_op1 = tem; - } - inner = simplify_binary_operation (code == MINUS ? PLUS - : code == DIV ? MULT - : code == UDIV ? MULT - : code, - mode, inner_op0, inner_op1); - - /* For commutative operations, try the other pair if that one - didn't simplify. */ - if (inner == 0 && GET_RTX_CLASS (code) == 'c') - { - other = XEXP (XEXP (x, 0), 1); - inner = simplify_binary_operation (code, mode, - XEXP (XEXP (x, 0), 0), - XEXP (x, 1)); - } - - if (inner) - { - x = gen_binary (code, mode, other, inner); - goto restart; - - } - } - } - - /* A little bit of algebraic simplification here. */ - switch (code) - { - case MEM: - /* Ensure that our address has any ASHIFTs converted to MULT in case - address-recognizing predicates are called later. */ - temp = make_compound_operation (XEXP (x, 0), MEM); - SUBST (XEXP (x, 0), temp); - break; - - case SUBREG: - /* (subreg:A (mem:B X) N) becomes a modified MEM unless the SUBREG - is paradoxical. If we can't do that safely, then it becomes - something nonsensical so that this combination won't take place. */ - - if (GET_CODE (SUBREG_REG (x)) == MEM - && (GET_MODE_SIZE (mode) - <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))) - { - rtx inner = SUBREG_REG (x); - int endian_offset = 0; - /* Don't change the mode of the MEM - if that would change the meaning of the address. */ - if (MEM_VOLATILE_P (SUBREG_REG (x)) - || mode_dependent_address_p (XEXP (inner, 0))) - return gen_rtx (CLOBBER, mode, const0_rtx); - -#if BYTES_BIG_ENDIAN - if (GET_MODE_SIZE (mode) < UNITS_PER_WORD) - endian_offset += UNITS_PER_WORD - GET_MODE_SIZE (mode); - if (GET_MODE_SIZE (GET_MODE (inner)) < UNITS_PER_WORD) - endian_offset -= UNITS_PER_WORD - GET_MODE_SIZE (GET_MODE (inner)); -#endif - /* Note if the plus_constant doesn't make a valid address - then this combination won't be accepted. */ - x = gen_rtx (MEM, mode, - plus_constant (XEXP (inner, 0), - (SUBREG_WORD (x) * UNITS_PER_WORD - + endian_offset))); - MEM_VOLATILE_P (x) = MEM_VOLATILE_P (inner); - RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (inner); - MEM_IN_STRUCT_P (x) = MEM_IN_STRUCT_P (inner); - return x; - } - - /* If we are in a SET_DEST, these other cases can't apply. */ - if (in_dest) - return x; - - /* Changing mode twice with SUBREG => just change it once, - or not at all if changing back to starting mode. */ - if (GET_CODE (SUBREG_REG (x)) == SUBREG) - { - if (mode == GET_MODE (SUBREG_REG (SUBREG_REG (x))) - && SUBREG_WORD (x) == 0 && SUBREG_WORD (SUBREG_REG (x)) == 0) - return SUBREG_REG (SUBREG_REG (x)); - - SUBST_INT (SUBREG_WORD (x), - SUBREG_WORD (x) + SUBREG_WORD (SUBREG_REG (x))); - SUBST (SUBREG_REG (x), SUBREG_REG (SUBREG_REG (x))); - } - - /* SUBREG of a hard register => just change the register number - and/or mode. If the hard register is not valid in that mode, - suppress this combination. If the hard register is the stack, - frame, or argument pointer, leave this as a SUBREG. */ - - if (GET_CODE (SUBREG_REG (x)) == REG - && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER - && REGNO (SUBREG_REG (x)) != FRAME_POINTER_REGNUM -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - && REGNO (SUBREG_REG (x)) != ARG_POINTER_REGNUM -#endif - && REGNO (SUBREG_REG (x)) != STACK_POINTER_REGNUM) - { - if (HARD_REGNO_MODE_OK (REGNO (SUBREG_REG (x)) + SUBREG_WORD (x), - mode)) - return gen_rtx (REG, mode, - REGNO (SUBREG_REG (x)) + SUBREG_WORD (x)); - else - return gen_rtx (CLOBBER, mode, const0_rtx); - } - - /* For a constant, try to pick up the part we want. Handle a full - word and low-order part. Only do this if we are narrowing - the constant; if it is being widened, we have no idea what - the extra bits will have been set to. */ - - if (CONSTANT_P (SUBREG_REG (x)) && op0_mode != VOIDmode - && GET_MODE_SIZE (mode) == UNITS_PER_WORD - && GET_MODE_SIZE (op0_mode) < UNITS_PER_WORD - && GET_MODE_CLASS (mode) == MODE_INT) - { - temp = operand_subword (SUBREG_REG (x), SUBREG_WORD (x), - 0, op0_mode); - if (temp) - return temp; - } - - /* If we want a subreg of a constant, at offset 0, - take the low bits. On a little-endian machine, that's - always valid. On a big-endian machine, it's valid - only if the constant's mode fits in one word. */ - if (CONSTANT_P (SUBREG_REG (x)) && subreg_lowpart_p (x) - && GET_MODE_SIZE (mode) < GET_MODE_SIZE (op0_mode) -#if WORDS_BIG_ENDIAN - && GET_MODE_BITSIZE (op0_mode) <= BITS_PER_WORD -#endif - ) - return gen_lowpart_for_combine (mode, SUBREG_REG (x)); - - /* If we are narrowing the object, we need to see if we can simplify - the expression for the object knowing that we only need the - low-order bits. */ - - if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) - && subreg_lowpart_p (x)) - return force_to_mode (SUBREG_REG (x), mode, GET_MODE_BITSIZE (mode), - NULL_RTX); - break; - - case NOT: - /* (not (plus X -1)) can become (neg X). */ - if (GET_CODE (XEXP (x, 0)) == PLUS - && XEXP (XEXP (x, 0), 1) == constm1_rtx) - { - x = gen_rtx_combine (NEG, mode, XEXP (XEXP (x, 0), 0)); - goto restart; - } - - /* Similarly, (not (neg X)) is (plus X -1). */ - if (GET_CODE (XEXP (x, 0)) == NEG) - { - x = gen_rtx_combine (PLUS, mode, XEXP (XEXP (x, 0), 0), constm1_rtx); - goto restart; - } - - /* (not (xor X C)) for C constant is (xor X D) with D = ~ C. */ - if (GET_CODE (XEXP (x, 0)) == XOR - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && (temp = simplify_unary_operation (NOT, mode, - XEXP (XEXP (x, 0), 1), - mode)) != 0) - { - SUBST (XEXP (XEXP (x, 0), 1), temp); - return XEXP (x, 0); - } - - /* (not (ashift 1 X)) is (rotate ~1 X). We used to do this for operands - other than 1, but that is not valid. We could do a similar - simplification for (not (lshiftrt C X)) where C is just the sign bit, - but this doesn't seem common enough to bother with. */ - if (GET_CODE (XEXP (x, 0)) == ASHIFT - && XEXP (XEXP (x, 0), 0) == const1_rtx) - { - x = gen_rtx (ROTATE, mode, gen_unary (NOT, mode, const1_rtx), - XEXP (XEXP (x, 0), 1)); - goto restart; - } - - if (GET_CODE (XEXP (x, 0)) == SUBREG - && subreg_lowpart_p (XEXP (x, 0)) - && (GET_MODE_SIZE (GET_MODE (XEXP (x, 0))) - < GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (x, 0))))) - && GET_CODE (SUBREG_REG (XEXP (x, 0))) == ASHIFT - && XEXP (SUBREG_REG (XEXP (x, 0)), 0) == const1_rtx) - { - enum machine_mode inner_mode = GET_MODE (SUBREG_REG (XEXP (x, 0))); - - x = gen_rtx (ROTATE, inner_mode, - gen_unary (NOT, inner_mode, const1_rtx), - XEXP (SUBREG_REG (XEXP (x, 0)), 1)); - x = gen_lowpart_for_combine (mode, x); - goto restart; - } - -#if STORE_FLAG_VALUE == -1 - /* (not (comparison foo bar)) can be done by reversing the comparison - code if valid. */ - if (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' - && reversible_comparison_p (XEXP (x, 0))) - return gen_rtx_combine (reverse_condition (GET_CODE (XEXP (x, 0))), - mode, XEXP (XEXP (x, 0), 0), - XEXP (XEXP (x, 0), 1)); - - /* (ashiftrt foo C) where C is the number of bits in FOO minus 1 - is (lt foo (const_int 0)), so we can perform the above - simplification. */ - - if (XEXP (x, 1) == const1_rtx - && GET_CODE (XEXP (x, 0)) == ASHIFTRT - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && INTVAL (XEXP (XEXP (x, 0), 1)) == GET_MODE_BITSIZE (mode) - 1) - return gen_rtx_combine (GE, mode, XEXP (XEXP (x, 0), 0), const0_rtx); -#endif - - /* Apply De Morgan's laws to reduce number of patterns for machines - with negating logical insns (and-not, nand, etc.). If result has - only one NOT, put it first, since that is how the patterns are - coded. */ - - if (GET_CODE (XEXP (x, 0)) == IOR || GET_CODE (XEXP (x, 0)) == AND) - { - rtx in1 = XEXP (XEXP (x, 0), 0), in2 = XEXP (XEXP (x, 0), 1); - - if (GET_CODE (in1) == NOT) - in1 = XEXP (in1, 0); - else - in1 = gen_rtx_combine (NOT, GET_MODE (in1), in1); - - if (GET_CODE (in2) == NOT) - in2 = XEXP (in2, 0); - else if (GET_CODE (in2) == CONST_INT - && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT) - in2 = GEN_INT (GET_MODE_MASK (mode) & ~ INTVAL (in2)); - else - in2 = gen_rtx_combine (NOT, GET_MODE (in2), in2); - - if (GET_CODE (in2) == NOT) - { - rtx tem = in2; - in2 = in1; in1 = tem; - } - - x = gen_rtx_combine (GET_CODE (XEXP (x, 0)) == IOR ? AND : IOR, - mode, in1, in2); - goto restart; - } - break; - - case NEG: - /* (neg (plus X 1)) can become (not X). */ - if (GET_CODE (XEXP (x, 0)) == PLUS - && XEXP (XEXP (x, 0), 1) == const1_rtx) - { - x = gen_rtx_combine (NOT, mode, XEXP (XEXP (x, 0), 0)); - goto restart; - } - - /* Similarly, (neg (not X)) is (plus X 1). */ - if (GET_CODE (XEXP (x, 0)) == NOT) - { - x = gen_rtx_combine (PLUS, mode, XEXP (XEXP (x, 0), 0), const1_rtx); - goto restart; - } - - /* (neg (minus X Y)) can become (minus Y X). */ - if (GET_CODE (XEXP (x, 0)) == MINUS - && (GET_MODE_CLASS (mode) != MODE_FLOAT - /* x-y != -(y-x) with IEEE floating point. */ - || TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT)) - { - x = gen_binary (MINUS, mode, XEXP (XEXP (x, 0), 1), - XEXP (XEXP (x, 0), 0)); - goto restart; - } - - /* (neg (xor A 1)) is (plus A -1) if A is known to be either 0 or 1. */ - if (GET_CODE (XEXP (x, 0)) == XOR && XEXP (XEXP (x, 0), 1) == const1_rtx - && nonzero_bits (XEXP (XEXP (x, 0), 0), mode) == 1) - { - x = gen_binary (PLUS, mode, XEXP (XEXP (x, 0), 0), constm1_rtx); - goto restart; - } - - /* NEG commutes with ASHIFT since it is multiplication. Only do this - if we can then eliminate the NEG (e.g., - if the operand is a constant). */ - - if (GET_CODE (XEXP (x, 0)) == ASHIFT) - { - temp = simplify_unary_operation (NEG, mode, - XEXP (XEXP (x, 0), 0), mode); - if (temp) - { - SUBST (XEXP (XEXP (x, 0), 0), temp); - return XEXP (x, 0); - } - } - - temp = expand_compound_operation (XEXP (x, 0)); - - /* For C equal to the width of MODE minus 1, (neg (ashiftrt X C)) can be - replaced by (lshiftrt X C). This will convert - (neg (sign_extract X 1 Y)) to (zero_extract X 1 Y). */ - - if (GET_CODE (temp) == ASHIFTRT - && GET_CODE (XEXP (temp, 1)) == CONST_INT - && INTVAL (XEXP (temp, 1)) == GET_MODE_BITSIZE (mode) - 1) - { - x = simplify_shift_const (temp, LSHIFTRT, mode, XEXP (temp, 0), - INTVAL (XEXP (temp, 1))); - goto restart; - } - - /* If X has only a single bit that might be nonzero, say, bit I, convert - (neg X) to (ashiftrt (ashift X C-I) C-I) where C is the bitsize of - MODE minus 1. This will convert (neg (zero_extract X 1 Y)) to - (sign_extract X 1 Y). But only do this if TEMP isn't a register - or a SUBREG of one since we'd be making the expression more - complex if it was just a register. */ - - if (GET_CODE (temp) != REG - && ! (GET_CODE (temp) == SUBREG - && GET_CODE (SUBREG_REG (temp)) == REG) - && (i = exact_log2 (nonzero_bits (temp, mode))) >= 0) - { - rtx temp1 = simplify_shift_const - (NULL_RTX, ASHIFTRT, mode, - simplify_shift_const (NULL_RTX, ASHIFT, mode, temp, - GET_MODE_BITSIZE (mode) - 1 - i), - GET_MODE_BITSIZE (mode) - 1 - i); - - /* If all we did was surround TEMP with the two shifts, we - haven't improved anything, so don't use it. Otherwise, - we are better off with TEMP1. */ - if (GET_CODE (temp1) != ASHIFTRT - || GET_CODE (XEXP (temp1, 0)) != ASHIFT - || XEXP (XEXP (temp1, 0), 0) != temp) - { - x = temp1; - goto restart; - } - } - break; - - case FLOAT_TRUNCATE: - /* (float_truncate:SF (float_extend:DF foo:SF)) = foo:SF. */ - if (GET_CODE (XEXP (x, 0)) == FLOAT_EXTEND - && GET_MODE (XEXP (XEXP (x, 0), 0)) == mode) - return XEXP (XEXP (x, 0), 0); - break; - -#ifdef HAVE_cc0 - case COMPARE: - /* Convert (compare FOO (const_int 0)) to FOO unless we aren't - using cc0, in which case we want to leave it as a COMPARE - so we can distinguish it from a register-register-copy. */ - if (XEXP (x, 1) == const0_rtx) - return XEXP (x, 0); - - /* In IEEE floating point, x-0 is not the same as x. */ - if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT - || GET_MODE_CLASS (GET_MODE (XEXP (x, 0))) == MODE_INT) - && XEXP (x, 1) == CONST0_RTX (GET_MODE (XEXP (x, 0)))) - return XEXP (x, 0); - break; -#endif - - case CONST: - /* (const (const X)) can become (const X). Do it this way rather than - returning the inner CONST since CONST can be shared with a - REG_EQUAL note. */ - if (GET_CODE (XEXP (x, 0)) == CONST) - SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0)); - break; - -#ifdef HAVE_lo_sum - case LO_SUM: - /* Convert (lo_sum (high FOO) FOO) to FOO. This is necessary so we - can add in an offset. find_split_point will split this address up - again if it doesn't match. */ - if (GET_CODE (XEXP (x, 0)) == HIGH - && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1))) - return XEXP (x, 1); - break; -#endif - - case PLUS: - /* If we have (plus (plus (A const) B)), associate it so that CONST is - outermost. That's because that's the way indexed addresses are - supposed to appear. This code used to check many more cases, but - they are now checked elsewhere. */ - if (GET_CODE (XEXP (x, 0)) == PLUS - && CONSTANT_ADDRESS_P (XEXP (XEXP (x, 0), 1))) - return gen_binary (PLUS, mode, - gen_binary (PLUS, mode, XEXP (XEXP (x, 0), 0), - XEXP (x, 1)), - XEXP (XEXP (x, 0), 1)); - - /* (plus (xor (and (const_int pow2 - 1)) ) <-c>) - when c is (const_int (pow2 + 1) / 2) is a sign extension of a - bit-field and can be replaced by either a sign_extend or a - sign_extract. The `and' may be a zero_extend. */ - if (GET_CODE (XEXP (x, 0)) == XOR - && GET_CODE (XEXP (x, 1)) == CONST_INT - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) == - INTVAL (XEXP (XEXP (x, 0), 1)) - && (i = exact_log2 (INTVAL (XEXP (XEXP (x, 0), 1)))) >= 0 - && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT - && ((GET_CODE (XEXP (XEXP (x, 0), 0)) == AND - && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT - && (INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1)) - == ((HOST_WIDE_INT) 1 << (i + 1)) - 1)) - || (GET_CODE (XEXP (XEXP (x, 0), 0)) == ZERO_EXTEND - && (GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (XEXP (x, 0), 0), 0))) - == i + 1)))) - { - x = simplify_shift_const - (NULL_RTX, ASHIFTRT, mode, - simplify_shift_const (NULL_RTX, ASHIFT, mode, - XEXP (XEXP (XEXP (x, 0), 0), 0), - GET_MODE_BITSIZE (mode) - (i + 1)), - GET_MODE_BITSIZE (mode) - (i + 1)); - goto restart; - } - - /* If only the low-order bit of X is possible nonzero, (plus x -1) - can become (ashiftrt (ashift (xor x 1) C) C) where C is - the bitsize of the mode - 1. This allows simplification of - "a = (b & 8) == 0;" */ - if (XEXP (x, 1) == constm1_rtx - && GET_CODE (XEXP (x, 0)) != REG - && ! (GET_CODE (XEXP (x,0)) == SUBREG - && GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG) - && nonzero_bits (XEXP (x, 0), mode) == 1) - { - x = simplify_shift_const - (NULL_RTX, ASHIFTRT, mode, - simplify_shift_const (NULL_RTX, ASHIFT, mode, - gen_rtx_combine (XOR, mode, - XEXP (x, 0), const1_rtx), - GET_MODE_BITSIZE (mode) - 1), - GET_MODE_BITSIZE (mode) - 1); - goto restart; - } - - /* If we are adding two things that have no bits in common, convert - the addition into an IOR. This will often be further simplified, - for example in cases like ((a & 1) + (a & 2)), which can - become a & 3. */ - - if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT - && (nonzero_bits (XEXP (x, 0), mode) - & nonzero_bits (XEXP (x, 1), mode)) == 0) - { - x = gen_binary (IOR, mode, XEXP (x, 0), XEXP (x, 1)); - goto restart; - } - break; - - case MINUS: - /* (minus (and (const_int -pow2))) becomes - (and (const_int pow2-1)) */ - if (GET_CODE (XEXP (x, 1)) == AND - && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT - && exact_log2 (- INTVAL (XEXP (XEXP (x, 1), 1))) >= 0 - && rtx_equal_p (XEXP (XEXP (x, 1), 0), XEXP (x, 0))) - { - x = simplify_and_const_int (NULL_RTX, mode, XEXP (x, 0), - - INTVAL (XEXP (XEXP (x, 1), 1)) - 1); - goto restart; - } - break; - - case MULT: - /* If we have (mult (plus A B) C), apply the distributive law and then - the inverse distributive law to see if things simplify. This - occurs mostly in addresses, often when unrolling loops. */ - - if (GET_CODE (XEXP (x, 0)) == PLUS) - { - x = apply_distributive_law - (gen_binary (PLUS, mode, - gen_binary (MULT, mode, - XEXP (XEXP (x, 0), 0), XEXP (x, 1)), - gen_binary (MULT, mode, - XEXP (XEXP (x, 0), 1), XEXP (x, 1)))); - - if (GET_CODE (x) != MULT) - goto restart; - } - - /* If this is multiplication by a power of two and its first operand is - a shift, treat the multiply as a shift to allow the shifts to - possibly combine. */ - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && (i = exact_log2 (INTVAL (XEXP (x, 1)))) >= 0 - && (GET_CODE (XEXP (x, 0)) == ASHIFT - || GET_CODE (XEXP (x, 0)) == LSHIFTRT - || GET_CODE (XEXP (x, 0)) == ASHIFTRT - || GET_CODE (XEXP (x, 0)) == ROTATE - || GET_CODE (XEXP (x, 0)) == ROTATERT)) - { - x = simplify_shift_const (NULL_RTX, ASHIFT, mode, XEXP (x, 0), i); - goto restart; - } - - /* Convert (mult (ashift (const_int 1) A) B) to (ashift B A). */ - if (GET_CODE (XEXP (x, 0)) == ASHIFT - && XEXP (XEXP (x, 0), 0) == const1_rtx) - return gen_rtx_combine (ASHIFT, mode, XEXP (x, 1), - XEXP (XEXP (x, 0), 1)); - break; - - case UDIV: - /* If this is a divide by a power of two, treat it as a shift if - its first operand is a shift. */ - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && (i = exact_log2 (INTVAL (XEXP (x, 1)))) >= 0 - && (GET_CODE (XEXP (x, 0)) == ASHIFT - || GET_CODE (XEXP (x, 0)) == LSHIFTRT - || GET_CODE (XEXP (x, 0)) == ASHIFTRT - || GET_CODE (XEXP (x, 0)) == ROTATE - || GET_CODE (XEXP (x, 0)) == ROTATERT)) - { - x = simplify_shift_const (NULL_RTX, LSHIFTRT, mode, XEXP (x, 0), i); - goto restart; - } - break; - - case EQ: case NE: - case GT: case GTU: case GE: case GEU: - case LT: case LTU: case LE: case LEU: - /* If the first operand is a condition code, we can't do anything - with it. */ - if (GET_CODE (XEXP (x, 0)) == COMPARE - || (GET_MODE_CLASS (GET_MODE (XEXP (x, 0))) != MODE_CC -#ifdef HAVE_cc0 - && XEXP (x, 0) != cc0_rtx -#endif - )) - { - rtx op0 = XEXP (x, 0); - rtx op1 = XEXP (x, 1); - enum rtx_code new_code; - - if (GET_CODE (op0) == COMPARE) - op1 = XEXP (op0, 1), op0 = XEXP (op0, 0); - - /* Simplify our comparison, if possible. */ - new_code = simplify_comparison (code, &op0, &op1); - -#if STORE_FLAG_VALUE == 1 - /* If STORE_FLAG_VALUE is 1, we can convert (ne x 0) to simply X - if only the low-order bit is possibly nonzero in X (such as when - X is a ZERO_EXTRACT of one bit. Similarly, we can convert - EQ to (xor X 1). Remove any ZERO_EXTRACT we made when thinking - this was a comparison. It may now be simpler to use, e.g., an - AND. If a ZERO_EXTRACT is indeed appropriate, it will - be placed back by the call to make_compound_operation in the - SET case. */ - if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT - && op1 == const0_rtx - && nonzero_bits (op0, GET_MODE (op0)) == 1) - return gen_lowpart_for_combine (mode, - expand_compound_operation (op0)); - else if (new_code == EQ && GET_MODE_CLASS (mode) == MODE_INT - && op1 == const0_rtx - && nonzero_bits (op0, GET_MODE (op0)) == 1) - { - op0 = expand_compound_operation (op0); - - x = gen_rtx_combine (XOR, mode, - gen_lowpart_for_combine (mode, op0), - const1_rtx); - goto restart; - } -#endif - -#if STORE_FLAG_VALUE == -1 - /* If STORE_FLAG_VALUE is -1, we can convert (ne x 0) - to (neg x) if only the low-order bit of X can be nonzero. - This converts (ne (zero_extract X 1 Y) 0) to - (sign_extract X 1 Y). */ - if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT - && op1 == const0_rtx - && nonzero_bits (op0, GET_MODE (op0)) == 1) - { - op0 = expand_compound_operation (op0); - x = gen_rtx_combine (NEG, mode, - gen_lowpart_for_combine (mode, op0)); - goto restart; - } -#endif - - /* If STORE_FLAG_VALUE says to just test the sign bit and X has just - one bit that might be nonzero, we can convert (ne x 0) to - (ashift x c) where C puts the bit in the sign bit. Remove any - AND with STORE_FLAG_VALUE when we are done, since we are only - going to test the sign bit. */ - if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT - && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT - && (STORE_FLAG_VALUE - == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)) - && op1 == const0_rtx - && mode == GET_MODE (op0) - && (i = exact_log2 (nonzero_bits (op0, GET_MODE (op0)))) >= 0) - { - x = simplify_shift_const (NULL_RTX, ASHIFT, mode, - expand_compound_operation (op0), - GET_MODE_BITSIZE (mode) - 1 - i); - if (GET_CODE (x) == AND && XEXP (x, 1) == const_true_rtx) - return XEXP (x, 0); - else - return x; - } - - /* If the code changed, return a whole new comparison. */ - if (new_code != code) - return gen_rtx_combine (new_code, mode, op0, op1); - - /* Otherwise, keep this operation, but maybe change its operands. - This also converts (ne (compare FOO BAR) 0) to (ne FOO BAR). */ - SUBST (XEXP (x, 0), op0); - SUBST (XEXP (x, 1), op1); - } - break; - - case IF_THEN_ELSE: - /* Sometimes we can simplify the arm of an IF_THEN_ELSE if a register - used in it is being compared against certain values. Get the - true and false comparisons and see if that says anything about the - value of each arm. */ - - if (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' - && reversible_comparison_p (XEXP (x, 0)) - && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG) - { - HOST_WIDE_INT nzb; - rtx from = XEXP (XEXP (x, 0), 0); - enum rtx_code true_code = GET_CODE (XEXP (x, 0)); - enum rtx_code false_code = reverse_condition (true_code); - rtx true_val = XEXP (XEXP (x, 0), 1); - rtx false_val = true_val; - rtx true_arm = XEXP (x, 1); - rtx false_arm = XEXP (x, 2); - int swapped = 0; - - /* If FALSE_CODE is EQ, swap the codes and arms. */ - - if (false_code == EQ) - { - swapped = 1, true_code = EQ, false_code = NE; - true_arm = XEXP (x, 2), false_arm = XEXP (x, 1); - } - - /* If we are comparing against zero and the expression being tested - has only a single bit that might be nonzero, that is its value - when it is not equal to zero. Similarly if it is known to be - -1 or 0. */ - - if (true_code == EQ && true_val == const0_rtx - && exact_log2 (nzb = nonzero_bits (from, GET_MODE (from))) >= 0) - false_code = EQ, false_val = GEN_INT (nzb); - else if (true_code == EQ && true_val == const0_rtx - && (num_sign_bit_copies (from, GET_MODE (from)) - == GET_MODE_BITSIZE (GET_MODE (from)))) - false_code = EQ, false_val = constm1_rtx; - - /* Now simplify an arm if we know the value of the register - in the branch and it is used in the arm. Be carefull due to - the potential of locally-shared RTL. */ - - if (reg_mentioned_p (from, true_arm)) - true_arm = subst (known_cond (copy_rtx (true_arm), true_code, - from, true_val), - pc_rtx, pc_rtx, 0, 0); - if (reg_mentioned_p (from, false_arm)) - false_arm = subst (known_cond (copy_rtx (false_arm), false_code, - from, false_val), - pc_rtx, pc_rtx, 0, 0); - - SUBST (XEXP (x, 1), swapped ? false_arm : true_arm); - SUBST (XEXP (x, 2), swapped ? true_arm : false_arm); - } - - /* If we have (if_then_else FOO (pc) (label_ref BAR)) and FOO can be - reversed, do so to avoid needing two sets of patterns for - subtract-and-branch insns. Similarly if we have a constant in that - position or if the third operand is the same as the first operand - of the comparison. */ - - if (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' - && reversible_comparison_p (XEXP (x, 0)) - && (XEXP (x, 1) == pc_rtx || GET_CODE (XEXP (x, 1)) == CONST_INT - || rtx_equal_p (XEXP (x, 2), XEXP (XEXP (x, 0), 0)))) - { - SUBST (XEXP (x, 0), - gen_binary (reverse_condition (GET_CODE (XEXP (x, 0))), - GET_MODE (XEXP (x, 0)), - XEXP (XEXP (x, 0), 0), XEXP (XEXP (x, 0), 1))); - - temp = XEXP (x, 1); - SUBST (XEXP (x, 1), XEXP (x, 2)); - SUBST (XEXP (x, 2), temp); - } - - /* If the two arms are identical, we don't need the comparison. */ - - if (rtx_equal_p (XEXP (x, 1), XEXP (x, 2)) - && ! side_effects_p (XEXP (x, 0))) - return XEXP (x, 1); - - /* Look for cases where we have (abs x) or (neg (abs X)). */ - - if (GET_MODE_CLASS (mode) == MODE_INT - && GET_CODE (XEXP (x, 2)) == NEG - && rtx_equal_p (XEXP (x, 1), XEXP (XEXP (x, 2), 0)) - && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' - && rtx_equal_p (XEXP (x, 1), XEXP (XEXP (x, 0), 0)) - && ! side_effects_p (XEXP (x, 1))) - switch (GET_CODE (XEXP (x, 0))) - { - case GT: - case GE: - x = gen_unary (ABS, mode, XEXP (x, 1)); - goto restart; - case LT: - case LE: - x = gen_unary (NEG, mode, gen_unary (ABS, mode, XEXP (x, 1))); - goto restart; - } - - /* Look for MIN or MAX. */ - - if (GET_MODE_CLASS (mode) == MODE_INT - && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' - && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1)) - && rtx_equal_p (XEXP (XEXP (x, 0), 1), XEXP (x, 2)) - && ! side_effects_p (XEXP (x, 0))) - switch (GET_CODE (XEXP (x, 0))) - { - case GE: - case GT: - x = gen_binary (SMAX, mode, XEXP (x, 1), XEXP (x, 2)); - goto restart; - case LE: - case LT: - x = gen_binary (SMIN, mode, XEXP (x, 1), XEXP (x, 2)); - goto restart; - case GEU: - case GTU: - x = gen_binary (UMAX, mode, XEXP (x, 1), XEXP (x, 2)); - goto restart; - case LEU: - case LTU: - x = gen_binary (UMIN, mode, XEXP (x, 1), XEXP (x, 2)); - goto restart; - } - - /* If we have something like (if_then_else (ne A 0) (OP X C) X), - A is known to be either 0 or 1, and OP is an identity when its - second operand is zero, this can be done as (OP X (mult A C)). - Similarly if A is known to be 0 or -1 and also similarly if we have - a ZERO_EXTEND or SIGN_EXTEND as long as X is already extended (so - we don't destroy it). */ - - if (mode != VOIDmode - && (GET_CODE (XEXP (x, 0)) == EQ || GET_CODE (XEXP (x, 0)) == NE) - && XEXP (XEXP (x, 0), 1) == const0_rtx - && (nonzero_bits (XEXP (XEXP (x, 0), 0), mode) == 1 - || (num_sign_bit_copies (XEXP (XEXP (x, 0), 0), mode) - == GET_MODE_BITSIZE (mode)))) - { - rtx nz = make_compound_operation (GET_CODE (XEXP (x, 0)) == NE - ? XEXP (x, 1) : XEXP (x, 2)); - rtx z = GET_CODE (XEXP (x, 0)) == NE ? XEXP (x, 2) : XEXP (x, 1); - rtx dir = (nonzero_bits (XEXP (XEXP (x, 0), 0), mode) == 1 - ? const1_rtx : constm1_rtx); - rtx c = 0; - enum machine_mode m = mode; - enum rtx_code op, extend_op = 0; - - if ((GET_CODE (nz) == PLUS || GET_CODE (nz) == MINUS - || GET_CODE (nz) == IOR || GET_CODE (nz) == XOR - || GET_CODE (nz) == ASHIFT - || GET_CODE (nz) == LSHIFTRT || GET_CODE (nz) == ASHIFTRT) - && rtx_equal_p (XEXP (nz, 0), z)) - c = XEXP (nz, 1), op = GET_CODE (nz); - else if (GET_CODE (nz) == SIGN_EXTEND - && (GET_CODE (XEXP (nz, 0)) == PLUS - || GET_CODE (XEXP (nz, 0)) == MINUS - || GET_CODE (XEXP (nz, 0)) == IOR - || GET_CODE (XEXP (nz, 0)) == XOR - || GET_CODE (XEXP (nz, 0)) == ASHIFT - || GET_CODE (XEXP (nz, 0)) == LSHIFTRT - || GET_CODE (XEXP (nz, 0)) == ASHIFTRT) - && GET_CODE (XEXP (XEXP (nz, 0), 0)) == SUBREG - && subreg_lowpart_p (XEXP (XEXP (nz, 0), 0)) - && rtx_equal_p (SUBREG_REG (XEXP (XEXP (nz, 0), 0)), z) - && (num_sign_bit_copies (z, GET_MODE (z)) - >= (GET_MODE_BITSIZE (mode) - - GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (nz, 0), 0)))))) - { - c = XEXP (XEXP (nz, 0), 1); - op = GET_CODE (XEXP (nz, 0)); - extend_op = SIGN_EXTEND; - m = GET_MODE (XEXP (nz, 0)); - } - else if (GET_CODE (nz) == ZERO_EXTEND - && (GET_CODE (XEXP (nz, 0)) == PLUS - || GET_CODE (XEXP (nz, 0)) == MINUS - || GET_CODE (XEXP (nz, 0)) == IOR - || GET_CODE (XEXP (nz, 0)) == XOR - || GET_CODE (XEXP (nz, 0)) == ASHIFT - || GET_CODE (XEXP (nz, 0)) == LSHIFTRT - || GET_CODE (XEXP (nz, 0)) == ASHIFTRT) - && GET_CODE (XEXP (XEXP (nz, 0), 0)) == SUBREG - && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT - && subreg_lowpart_p (XEXP (XEXP (nz, 0), 0)) - && rtx_equal_p (SUBREG_REG (XEXP (XEXP (nz, 0), 0)), z) - && ((nonzero_bits (z, GET_MODE (z)) - & ~ GET_MODE_MASK (GET_MODE (XEXP (XEXP (nz, 0), 0)))) - == 0)) - { - c = XEXP (XEXP (nz, 0), 1); - op = GET_CODE (XEXP (nz, 0)); - extend_op = ZERO_EXTEND; - m = GET_MODE (XEXP (nz, 0)); - } - - if (c && ! side_effects_p (c) && ! side_effects_p (z)) - { - temp - = gen_binary (MULT, m, - gen_lowpart_for_combine (m, - XEXP (XEXP (x, 0), 0)), - gen_binary (MULT, m, c, dir)); - - temp = gen_binary (op, m, gen_lowpart_for_combine (m, z), temp); - - if (extend_op != 0) - temp = gen_unary (extend_op, mode, temp); - - return temp; - } - } - break; - - case ZERO_EXTRACT: - case SIGN_EXTRACT: - case ZERO_EXTEND: - case SIGN_EXTEND: - /* If we are processing SET_DEST, we are done. */ - if (in_dest) - return x; - - x = expand_compound_operation (x); - if (GET_CODE (x) != code) - goto restart; - break; - - case SET: - /* (set (pc) (return)) gets written as (return). */ - if (GET_CODE (SET_DEST (x)) == PC && GET_CODE (SET_SRC (x)) == RETURN) - return SET_SRC (x); - - /* Convert this into a field assignment operation, if possible. */ - x = make_field_assignment (x); - - /* If we are setting CC0 or if the source is a COMPARE, look for the - use of the comparison result and try to simplify it unless we already - have used undobuf.other_insn. */ - if ((GET_CODE (SET_SRC (x)) == COMPARE -#ifdef HAVE_cc0 - || SET_DEST (x) == cc0_rtx -#endif - ) - && (cc_use = find_single_use (SET_DEST (x), subst_insn, - &other_insn)) != 0 - && (undobuf.other_insn == 0 || other_insn == undobuf.other_insn) - && GET_RTX_CLASS (GET_CODE (*cc_use)) == '<' - && XEXP (*cc_use, 0) == SET_DEST (x)) - { - enum rtx_code old_code = GET_CODE (*cc_use); - enum rtx_code new_code; - rtx op0, op1; - int other_changed = 0; - enum machine_mode compare_mode = GET_MODE (SET_DEST (x)); - - if (GET_CODE (SET_SRC (x)) == COMPARE) - op0 = XEXP (SET_SRC (x), 0), op1 = XEXP (SET_SRC (x), 1); - else - op0 = SET_SRC (x), op1 = const0_rtx; - - /* Simplify our comparison, if possible. */ - new_code = simplify_comparison (old_code, &op0, &op1); - -#ifdef EXTRA_CC_MODES - /* If this machine has CC modes other than CCmode, check to see - if we need to use a different CC mode here. */ - compare_mode = SELECT_CC_MODE (new_code, op0, op1); -#endif /* EXTRA_CC_MODES */ - -#if !defined (HAVE_cc0) && defined (EXTRA_CC_MODES) - /* If the mode changed, we have to change SET_DEST, the mode - in the compare, and the mode in the place SET_DEST is used. - If SET_DEST is a hard register, just build new versions with - the proper mode. If it is a pseudo, we lose unless it is only - time we set the pseudo, in which case we can safely change - its mode. */ - if (compare_mode != GET_MODE (SET_DEST (x))) - { - int regno = REGNO (SET_DEST (x)); - rtx new_dest = gen_rtx (REG, compare_mode, regno); - - if (regno < FIRST_PSEUDO_REGISTER - || (reg_n_sets[regno] == 1 - && ! REG_USERVAR_P (SET_DEST (x)))) - { - if (regno >= FIRST_PSEUDO_REGISTER) - SUBST (regno_reg_rtx[regno], new_dest); - - SUBST (SET_DEST (x), new_dest); - SUBST (XEXP (*cc_use, 0), new_dest); - other_changed = 1; - } - } -#endif - - /* If the code changed, we have to build a new comparison - in undobuf.other_insn. */ - if (new_code != old_code) - { - unsigned HOST_WIDE_INT mask; - - SUBST (*cc_use, gen_rtx_combine (new_code, GET_MODE (*cc_use), - SET_DEST (x), const0_rtx)); - - /* If the only change we made was to change an EQ into an - NE or vice versa, OP0 has only one bit that might be nonzero, - and OP1 is zero, check if changing the user of the condition - code will produce a valid insn. If it won't, we can keep - the original code in that insn by surrounding our operation - with an XOR. */ - - if (((old_code == NE && new_code == EQ) - || (old_code == EQ && new_code == NE)) - && ! other_changed && op1 == const0_rtx - && (GET_MODE_BITSIZE (GET_MODE (op0)) - <= HOST_BITS_PER_WIDE_INT) - && (exact_log2 (mask = nonzero_bits (op0, GET_MODE (op0))) - >= 0)) - { - rtx pat = PATTERN (other_insn), note = 0; - - if ((recog_for_combine (&pat, other_insn, ¬e) < 0 - && ! check_asm_operands (pat))) - { - PUT_CODE (*cc_use, old_code); - other_insn = 0; - - op0 = gen_binary (XOR, GET_MODE (op0), op0, - GEN_INT (mask)); - } - } - - other_changed = 1; - } - - if (other_changed) - undobuf.other_insn = other_insn; - -#ifdef HAVE_cc0 - /* If we are now comparing against zero, change our source if - needed. If we do not use cc0, we always have a COMPARE. */ - if (op1 == const0_rtx && SET_DEST (x) == cc0_rtx) - SUBST (SET_SRC (x), op0); - else -#endif - - /* Otherwise, if we didn't previously have a COMPARE in the - correct mode, we need one. */ - if (GET_CODE (SET_SRC (x)) != COMPARE - || GET_MODE (SET_SRC (x)) != compare_mode) - SUBST (SET_SRC (x), gen_rtx_combine (COMPARE, compare_mode, - op0, op1)); - else - { - /* Otherwise, update the COMPARE if needed. */ - SUBST (XEXP (SET_SRC (x), 0), op0); - SUBST (XEXP (SET_SRC (x), 1), op1); - } - } - else - { - /* Get SET_SRC in a form where we have placed back any - compound expressions. Then do the checks below. */ - temp = make_compound_operation (SET_SRC (x), SET); - SUBST (SET_SRC (x), temp); - } - - /* If we have (set x (subreg:m1 (op:m2 ...) 0)) with OP being some - operation, and X being a REG or (subreg (reg)), we may be able to - convert this to (set (subreg:m2 x) (op)). - - We can always do this if M1 is narrower than M2 because that - means that we only care about the low bits of the result. - - However, on most machines (those with neither BYTE_LOADS_ZERO_EXTEND - nor BYTES_LOADS_SIGN_EXTEND defined), we cannot perform a - narrower operation that requested since the high-order bits will - be undefined. On machine where BYTE_LOADS_*_EXTEND is defined, - however, this transformation is safe as long as M1 and M2 have - the same number of words. */ - - if (GET_CODE (SET_SRC (x)) == SUBREG - && subreg_lowpart_p (SET_SRC (x)) - && GET_RTX_CLASS (GET_CODE (SUBREG_REG (SET_SRC (x)))) != 'o' - && (((GET_MODE_SIZE (GET_MODE (SET_SRC (x))) + (UNITS_PER_WORD - 1)) - / UNITS_PER_WORD) - == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_SRC (x)))) - + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)) -#ifndef BYTE_LOADS_EXTEND - && (GET_MODE_SIZE (GET_MODE (SET_SRC (x))) - < GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_SRC (x))))) -#endif - && (GET_CODE (SET_DEST (x)) == REG - || (GET_CODE (SET_DEST (x)) == SUBREG - && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG))) - { - SUBST (SET_DEST (x), - gen_lowpart_for_combine (GET_MODE (SUBREG_REG (SET_SRC (x))), - SET_DEST (x))); - SUBST (SET_SRC (x), SUBREG_REG (SET_SRC (x))); - } - -#ifdef BYTE_LOADS_EXTEND - /* If we have (set FOO (subreg:M (mem:N BAR) 0)) with - M wider than N, this would require a paradoxical subreg. - Replace the subreg with a zero_extend to avoid the reload that - would otherwise be required. */ - - if (GET_CODE (SET_SRC (x)) == SUBREG - && subreg_lowpart_p (SET_SRC (x)) - && SUBREG_WORD (SET_SRC (x)) == 0 - && (GET_MODE_SIZE (GET_MODE (SET_SRC (x))) - > GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_SRC (x))))) - && GET_CODE (SUBREG_REG (SET_SRC (x))) == MEM) - SUBST (SET_SRC (x), gen_rtx_combine (LOAD_EXTEND, - GET_MODE (SET_SRC (x)), - XEXP (SET_SRC (x), 0))); -#endif - -#ifndef HAVE_conditional_move - - /* If we don't have a conditional move, SET_SRC is an IF_THEN_ELSE, - and we are comparing an item known to be 0 or -1 against 0, use a - logical operation instead. Check for one of the arms being an IOR - of the other arm with some value. We compute three terms to be - IOR'ed together. In practice, at most two will be nonzero. Then - we do the IOR's. */ - - if (GET_CODE (SET_DEST (x)) != PC - && GET_CODE (SET_SRC (x)) == IF_THEN_ELSE - && (GET_CODE (XEXP (SET_SRC (x), 0)) == EQ - || GET_CODE (XEXP (SET_SRC (x), 0)) == NE) - && XEXP (XEXP (SET_SRC (x), 0), 1) == const0_rtx - && (num_sign_bit_copies (XEXP (XEXP (SET_SRC (x), 0), 0), - GET_MODE (XEXP (XEXP (SET_SRC (x), 0), 0))) - == GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (SET_SRC (x), 0), 0)))) - && ! side_effects_p (SET_SRC (x))) - { - rtx true = (GET_CODE (XEXP (SET_SRC (x), 0)) == NE - ? XEXP (SET_SRC (x), 1) : XEXP (SET_SRC (x), 2)); - rtx false = (GET_CODE (XEXP (SET_SRC (x), 0)) == NE - ? XEXP (SET_SRC (x), 2) : XEXP (SET_SRC (x), 1)); - rtx term1 = const0_rtx, term2, term3; - - if (GET_CODE (true) == IOR && rtx_equal_p (XEXP (true, 0), false)) - term1 = false, true = XEXP (true, 1), false = const0_rtx; - else if (GET_CODE (true) == IOR - && rtx_equal_p (XEXP (true, 1), false)) - term1 = false, true = XEXP (true, 0), false = const0_rtx; - else if (GET_CODE (false) == IOR - && rtx_equal_p (XEXP (false, 0), true)) - term1 = true, false = XEXP (false, 1), true = const0_rtx; - else if (GET_CODE (false) == IOR - && rtx_equal_p (XEXP (false, 1), true)) - term1 = true, false = XEXP (false, 0), true = const0_rtx; - - term2 = gen_binary (AND, GET_MODE (SET_SRC (x)), - XEXP (XEXP (SET_SRC (x), 0), 0), true); - term3 = gen_binary (AND, GET_MODE (SET_SRC (x)), - gen_unary (NOT, GET_MODE (SET_SRC (x)), - XEXP (XEXP (SET_SRC (x), 0), 0)), - false); - - SUBST (SET_SRC (x), - gen_binary (IOR, GET_MODE (SET_SRC (x)), - gen_binary (IOR, GET_MODE (SET_SRC (x)), - term1, term2), - term3)); - } -#endif - break; - - case AND: - if (GET_CODE (XEXP (x, 1)) == CONST_INT) - { - x = simplify_and_const_int (x, mode, XEXP (x, 0), - INTVAL (XEXP (x, 1))); - - /* If we have (ior (and (X C1) C2)) and the next restart would be - the last, simplify this by making C1 as small as possible - and then exit. */ - if (n_restarts >= 3 && GET_CODE (x) == IOR - && GET_CODE (XEXP (x, 0)) == AND - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && GET_CODE (XEXP (x, 1)) == CONST_INT) - { - temp = gen_binary (AND, mode, XEXP (XEXP (x, 0), 0), - GEN_INT (INTVAL (XEXP (XEXP (x, 0), 1)) - & ~ INTVAL (XEXP (x, 1)))); - return gen_binary (IOR, mode, temp, XEXP (x, 1)); - } - - if (GET_CODE (x) != AND) - goto restart; - } - - /* Convert (A | B) & A to A. */ - if (GET_CODE (XEXP (x, 0)) == IOR - && (rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1)) - || rtx_equal_p (XEXP (XEXP (x, 0), 1), XEXP (x, 1))) - && ! side_effects_p (XEXP (XEXP (x, 0), 0)) - && ! side_effects_p (XEXP (XEXP (x, 0), 1))) - return XEXP (x, 1); - - /* Convert (A ^ B) & A to A & (~ B) since the latter is often a single - insn (and may simplify more). */ - else if (GET_CODE (XEXP (x, 0)) == XOR - && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1)) - && ! side_effects_p (XEXP (x, 1))) - { - x = gen_binary (AND, mode, - gen_unary (NOT, mode, XEXP (XEXP (x, 0), 1)), - XEXP (x, 1)); - goto restart; - } - else if (GET_CODE (XEXP (x, 0)) == XOR - && rtx_equal_p (XEXP (XEXP (x, 0), 1), XEXP (x, 1)) - && ! side_effects_p (XEXP (x, 1))) - { - x = gen_binary (AND, mode, - gen_unary (NOT, mode, XEXP (XEXP (x, 0), 0)), - XEXP (x, 1)); - goto restart; - } - - /* Similarly for (~ (A ^ B)) & A. */ - else if (GET_CODE (XEXP (x, 0)) == NOT - && GET_CODE (XEXP (XEXP (x, 0), 0)) == XOR - && rtx_equal_p (XEXP (XEXP (XEXP (x, 0), 0), 0), XEXP (x, 1)) - && ! side_effects_p (XEXP (x, 1))) - { - x = gen_binary (AND, mode, XEXP (XEXP (XEXP (x, 0), 0), 1), - XEXP (x, 1)); - goto restart; - } - else if (GET_CODE (XEXP (x, 0)) == NOT - && GET_CODE (XEXP (XEXP (x, 0), 0)) == XOR - && rtx_equal_p (XEXP (XEXP (XEXP (x, 0), 0), 1), XEXP (x, 1)) - && ! side_effects_p (XEXP (x, 1))) - { - x = gen_binary (AND, mode, XEXP (XEXP (XEXP (x, 0), 0), 0), - XEXP (x, 1)); - goto restart; - } - - /* If we have (and A B) with A not an object but that is known to - be -1 or 0, this is equivalent to the expression - (if_then_else (ne A (const_int 0)) B (const_int 0)) - We make this conversion because it may allow further - simplifications and then allow use of conditional move insns. - If the machine doesn't have condition moves, code in case SET - will convert the IF_THEN_ELSE back to the logical operation. - We build the IF_THEN_ELSE here in case further simplification - is possible (e.g., we can convert it to ABS). */ - - if (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) != 'o' - && ! (GET_CODE (XEXP (x, 0)) == SUBREG - && GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (x, 0)))) == 'o') - && (num_sign_bit_copies (XEXP (x, 0), GET_MODE (XEXP (x, 0))) - == GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))))) - { - rtx op0 = XEXP (x, 0); - rtx op1 = const0_rtx; - enum rtx_code comp_code - = simplify_comparison (NE, &op0, &op1); - - x = gen_rtx_combine (IF_THEN_ELSE, mode, - gen_binary (comp_code, VOIDmode, op0, op1), - XEXP (x, 1), const0_rtx); - goto restart; - } - - /* In the following group of tests (and those in case IOR below), - we start with some combination of logical operations and apply - the distributive law followed by the inverse distributive law. - Most of the time, this results in no change. However, if some of - the operands are the same or inverses of each other, simplifications - will result. - - For example, (and (ior A B) (not B)) can occur as the result of - expanding a bit field assignment. When we apply the distributive - law to this, we get (ior (and (A (not B))) (and (B (not B)))), - which then simplifies to (and (A (not B))). */ - - /* If we have (and (ior A B) C), apply the distributive law and then - the inverse distributive law to see if things simplify. */ - - if (GET_CODE (XEXP (x, 0)) == IOR || GET_CODE (XEXP (x, 0)) == XOR) - { - x = apply_distributive_law - (gen_binary (GET_CODE (XEXP (x, 0)), mode, - gen_binary (AND, mode, - XEXP (XEXP (x, 0), 0), XEXP (x, 1)), - gen_binary (AND, mode, - XEXP (XEXP (x, 0), 1), XEXP (x, 1)))); - if (GET_CODE (x) != AND) - goto restart; - } - - if (GET_CODE (XEXP (x, 1)) == IOR || GET_CODE (XEXP (x, 1)) == XOR) - { - x = apply_distributive_law - (gen_binary (GET_CODE (XEXP (x, 1)), mode, - gen_binary (AND, mode, - XEXP (XEXP (x, 1), 0), XEXP (x, 0)), - gen_binary (AND, mode, - XEXP (XEXP (x, 1), 1), XEXP (x, 0)))); - if (GET_CODE (x) != AND) - goto restart; - } - - /* Similarly, taking advantage of the fact that - (and (not A) (xor B C)) == (xor (ior A B) (ior A C)) */ - - if (GET_CODE (XEXP (x, 0)) == NOT && GET_CODE (XEXP (x, 1)) == XOR) - { - x = apply_distributive_law - (gen_binary (XOR, mode, - gen_binary (IOR, mode, XEXP (XEXP (x, 0), 0), - XEXP (XEXP (x, 1), 0)), - gen_binary (IOR, mode, XEXP (XEXP (x, 0), 0), - XEXP (XEXP (x, 1), 1)))); - if (GET_CODE (x) != AND) - goto restart; - } - - else if (GET_CODE (XEXP (x, 1)) == NOT && GET_CODE (XEXP (x, 0)) == XOR) - { - x = apply_distributive_law - (gen_binary (XOR, mode, - gen_binary (IOR, mode, XEXP (XEXP (x, 1), 0), - XEXP (XEXP (x, 0), 0)), - gen_binary (IOR, mode, XEXP (XEXP (x, 1), 0), - XEXP (XEXP (x, 0), 1)))); - if (GET_CODE (x) != AND) - goto restart; - } - break; - - case IOR: - /* (ior A C) is C if all bits of A that might be nonzero are on in C. */ - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT - && (nonzero_bits (XEXP (x, 0), mode) & ~ INTVAL (XEXP (x, 1))) == 0) - return XEXP (x, 1); - - /* Convert (A & B) | A to A. */ - if (GET_CODE (XEXP (x, 0)) == AND - && (rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1)) - || rtx_equal_p (XEXP (XEXP (x, 0), 1), XEXP (x, 1))) - && ! side_effects_p (XEXP (XEXP (x, 0), 0)) - && ! side_effects_p (XEXP (XEXP (x, 0), 1))) - return XEXP (x, 1); - - /* If we have (ior (and A B) C), apply the distributive law and then - the inverse distributive law to see if things simplify. */ - - if (GET_CODE (XEXP (x, 0)) == AND) - { - x = apply_distributive_law - (gen_binary (AND, mode, - gen_binary (IOR, mode, - XEXP (XEXP (x, 0), 0), XEXP (x, 1)), - gen_binary (IOR, mode, - XEXP (XEXP (x, 0), 1), XEXP (x, 1)))); - - if (GET_CODE (x) != IOR) - goto restart; - } - - if (GET_CODE (XEXP (x, 1)) == AND) - { - x = apply_distributive_law - (gen_binary (AND, mode, - gen_binary (IOR, mode, - XEXP (XEXP (x, 1), 0), XEXP (x, 0)), - gen_binary (IOR, mode, - XEXP (XEXP (x, 1), 1), XEXP (x, 0)))); - - if (GET_CODE (x) != IOR) - goto restart; - } - - /* Convert (ior (ashift A CX) (lshiftrt A CY)) where CX+CY equals the - mode size to (rotate A CX). */ - - if (((GET_CODE (XEXP (x, 0)) == ASHIFT - && GET_CODE (XEXP (x, 1)) == LSHIFTRT) - || (GET_CODE (XEXP (x, 1)) == ASHIFT - && GET_CODE (XEXP (x, 0)) == LSHIFTRT)) - && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (XEXP (x, 1), 0)) - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT - && (INTVAL (XEXP (XEXP (x, 0), 1)) + INTVAL (XEXP (XEXP (x, 1), 1)) - == GET_MODE_BITSIZE (mode))) - { - rtx shift_count; - - if (GET_CODE (XEXP (x, 0)) == ASHIFT) - shift_count = XEXP (XEXP (x, 0), 1); - else - shift_count = XEXP (XEXP (x, 1), 1); - x = gen_rtx (ROTATE, mode, XEXP (XEXP (x, 0), 0), shift_count); - goto restart; - } - break; - - case XOR: - /* Convert (XOR (NOT x) (NOT y)) to (XOR x y). - Also convert (XOR (NOT x) y) to (NOT (XOR x y)), similarly for - (NOT y). */ - { - int num_negated = 0; - rtx in1 = XEXP (x, 0), in2 = XEXP (x, 1); - - if (GET_CODE (in1) == NOT) - num_negated++, in1 = XEXP (in1, 0); - if (GET_CODE (in2) == NOT) - num_negated++, in2 = XEXP (in2, 0); - - if (num_negated == 2) - { - SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0)); - SUBST (XEXP (x, 1), XEXP (XEXP (x, 1), 0)); - } - else if (num_negated == 1) - { - x = gen_unary (NOT, mode, - gen_binary (XOR, mode, in1, in2)); - goto restart; - } - } - - /* Convert (xor (and A B) B) to (and (not A) B). The latter may - correspond to a machine insn or result in further simplifications - if B is a constant. */ - - if (GET_CODE (XEXP (x, 0)) == AND - && rtx_equal_p (XEXP (XEXP (x, 0), 1), XEXP (x, 1)) - && ! side_effects_p (XEXP (x, 1))) - { - x = gen_binary (AND, mode, - gen_unary (NOT, mode, XEXP (XEXP (x, 0), 0)), - XEXP (x, 1)); - goto restart; - } - else if (GET_CODE (XEXP (x, 0)) == AND - && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1)) - && ! side_effects_p (XEXP (x, 1))) - { - x = gen_binary (AND, mode, - gen_unary (NOT, mode, XEXP (XEXP (x, 0), 1)), - XEXP (x, 1)); - goto restart; - } - - -#if STORE_FLAG_VALUE == 1 - /* (xor (comparison foo bar) (const_int 1)) can become the reversed - comparison. */ - if (XEXP (x, 1) == const1_rtx - && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' - && reversible_comparison_p (XEXP (x, 0))) - return gen_rtx_combine (reverse_condition (GET_CODE (XEXP (x, 0))), - mode, XEXP (XEXP (x, 0), 0), - XEXP (XEXP (x, 0), 1)); - - /* (lshiftrt foo C) where C is the number of bits in FOO minus 1 - is (lt foo (const_int 0)), so we can perform the above - simplification. */ - - if (XEXP (x, 1) == const1_rtx - && GET_CODE (XEXP (x, 0)) == LSHIFTRT - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && INTVAL (XEXP (XEXP (x, 0), 1)) == GET_MODE_BITSIZE (mode) - 1) - return gen_rtx_combine (GE, mode, XEXP (XEXP (x, 0), 0), const0_rtx); -#endif - - /* (xor (comparison foo bar) (const_int sign-bit)) - when STORE_FLAG_VALUE is the sign bit. */ - if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT - && (STORE_FLAG_VALUE - == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)) - && XEXP (x, 1) == const_true_rtx - && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' - && reversible_comparison_p (XEXP (x, 0))) - return gen_rtx_combine (reverse_condition (GET_CODE (XEXP (x, 0))), - mode, XEXP (XEXP (x, 0), 0), - XEXP (XEXP (x, 0), 1)); - break; - - case ABS: - /* (abs (neg )) -> (abs ) */ - if (GET_CODE (XEXP (x, 0)) == NEG) - SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0)); - - /* If operand is something known to be positive, ignore the ABS. */ - if (GET_CODE (XEXP (x, 0)) == FFS || GET_CODE (XEXP (x, 0)) == ABS - || ((GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - <= HOST_BITS_PER_WIDE_INT) - && ((nonzero_bits (XEXP (x, 0), GET_MODE (XEXP (x, 0))) - & ((HOST_WIDE_INT) 1 - << (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - 1))) - == 0))) - return XEXP (x, 0); - - - /* If operand is known to be only -1 or 0, convert ABS to NEG. */ - if (num_sign_bit_copies (XEXP (x, 0), mode) == GET_MODE_BITSIZE (mode)) - { - x = gen_rtx_combine (NEG, mode, XEXP (x, 0)); - goto restart; - } - break; - - case FFS: - /* (ffs (*_extend )) = (ffs ) */ - if (GET_CODE (XEXP (x, 0)) == SIGN_EXTEND - || GET_CODE (XEXP (x, 0)) == ZERO_EXTEND) - SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0)); - break; - - case FLOAT: - /* (float (sign_extend )) = (float ). */ - if (GET_CODE (XEXP (x, 0)) == SIGN_EXTEND) - SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0)); - break; - - case LSHIFT: - case ASHIFT: - case LSHIFTRT: - case ASHIFTRT: - case ROTATE: - case ROTATERT: - /* If this is a shift by a constant amount, simplify it. */ - if (GET_CODE (XEXP (x, 1)) == CONST_INT) - { - x = simplify_shift_const (x, code, mode, XEXP (x, 0), - INTVAL (XEXP (x, 1))); - if (GET_CODE (x) != code) - goto restart; - } - -#ifdef SHIFT_COUNT_TRUNCATED - else if (GET_CODE (XEXP (x, 1)) != REG) - SUBST (XEXP (x, 1), - force_to_mode (XEXP (x, 1), GET_MODE (x), - exact_log2 (GET_MODE_BITSIZE (GET_MODE (x))), - NULL_RTX)); -#endif - - break; - } - - return x; -} - -/* We consider ZERO_EXTRACT, SIGN_EXTRACT, and SIGN_EXTEND as "compound - operations" because they can be replaced with two more basic operations. - ZERO_EXTEND is also considered "compound" because it can be replaced with - an AND operation, which is simpler, though only one operation. - - The function expand_compound_operation is called with an rtx expression - and will convert it to the appropriate shifts and AND operations, - simplifying at each stage. - - The function make_compound_operation is called to convert an expression - consisting of shifts and ANDs into the equivalent compound expression. - It is the inverse of this function, loosely speaking. */ - -static rtx -expand_compound_operation (x) - rtx x; -{ - int pos = 0, len; - int unsignedp = 0; - int modewidth; - rtx tem; - - switch (GET_CODE (x)) - { - case ZERO_EXTEND: - unsignedp = 1; - case SIGN_EXTEND: - /* We can't necessarily use a const_int for a multiword mode; - it depends on implicitly extending the value. - Since we don't know the right way to extend it, - we can't tell whether the implicit way is right. - - Even for a mode that is no wider than a const_int, - we can't win, because we need to sign extend one of its bits through - the rest of it, and we don't know which bit. */ - if (GET_CODE (XEXP (x, 0)) == CONST_INT) - return x; - - if (! FAKE_EXTEND_SAFE_P (GET_MODE (XEXP (x, 0)), XEXP (x, 0))) - return x; - - len = GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))); - /* If the inner object has VOIDmode (the only way this can happen - is if it is a ASM_OPERANDS), we can't do anything since we don't - know how much masking to do. */ - if (len == 0) - return x; - - break; - - case ZERO_EXTRACT: - unsignedp = 1; - case SIGN_EXTRACT: - /* If the operand is a CLOBBER, just return it. */ - if (GET_CODE (XEXP (x, 0)) == CLOBBER) - return XEXP (x, 0); - - if (GET_CODE (XEXP (x, 1)) != CONST_INT - || GET_CODE (XEXP (x, 2)) != CONST_INT - || GET_MODE (XEXP (x, 0)) == VOIDmode) - return x; - - len = INTVAL (XEXP (x, 1)); - pos = INTVAL (XEXP (x, 2)); - - /* If this goes outside the object being extracted, replace the object - with a (use (mem ...)) construct that only combine understands - and is used only for this purpose. */ - if (len + pos > GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))) - SUBST (XEXP (x, 0), gen_rtx (USE, GET_MODE (x), XEXP (x, 0))); - -#if BITS_BIG_ENDIAN - pos = GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - len - pos; -#endif - break; - - default: - return x; - } - - /* If we reach here, we want to return a pair of shifts. The inner - shift is a left shift of BITSIZE - POS - LEN bits. The outer - shift is a right shift of BITSIZE - LEN bits. It is arithmetic or - logical depending on the value of UNSIGNEDP. - - If this was a ZERO_EXTEND or ZERO_EXTRACT, this pair of shifts will be - converted into an AND of a shift. - - We must check for the case where the left shift would have a negative - count. This can happen in a case like (x >> 31) & 255 on machines - that can't shift by a constant. On those machines, we would first - combine the shift with the AND to produce a variable-position - extraction. Then the constant of 31 would be substituted in to produce - a such a position. */ - - modewidth = GET_MODE_BITSIZE (GET_MODE (x)); - if (modewidth >= pos - len) - tem = simplify_shift_const (NULL_RTX, unsignedp ? LSHIFTRT : ASHIFTRT, - GET_MODE (x), - simplify_shift_const (NULL_RTX, ASHIFT, - GET_MODE (x), - XEXP (x, 0), - modewidth - pos - len), - modewidth - len); - - else if (unsignedp && len < HOST_BITS_PER_WIDE_INT) - tem = simplify_and_const_int (NULL_RTX, GET_MODE (x), - simplify_shift_const (NULL_RTX, LSHIFTRT, - GET_MODE (x), - XEXP (x, 0), pos), - ((HOST_WIDE_INT) 1 << len) - 1); - else - /* Any other cases we can't handle. */ - return x; - - - /* If we couldn't do this for some reason, return the original - expression. */ - if (GET_CODE (tem) == CLOBBER) - return x; - - return tem; -} - -/* X is a SET which contains an assignment of one object into - a part of another (such as a bit-field assignment, STRICT_LOW_PART, - or certain SUBREGS). If possible, convert it into a series of - logical operations. - - We half-heartedly support variable positions, but do not at all - support variable lengths. */ - -static rtx -expand_field_assignment (x) - rtx x; -{ - rtx inner; - rtx pos; /* Always counts from low bit. */ - int len; - rtx mask; - enum machine_mode compute_mode; - - /* Loop until we find something we can't simplify. */ - while (1) - { - if (GET_CODE (SET_DEST (x)) == STRICT_LOW_PART - && GET_CODE (XEXP (SET_DEST (x), 0)) == SUBREG) - { - inner = SUBREG_REG (XEXP (SET_DEST (x), 0)); - len = GET_MODE_BITSIZE (GET_MODE (XEXP (SET_DEST (x), 0))); - pos = const0_rtx; - } - else if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT - && GET_CODE (XEXP (SET_DEST (x), 1)) == CONST_INT) - { - inner = XEXP (SET_DEST (x), 0); - len = INTVAL (XEXP (SET_DEST (x), 1)); - pos = XEXP (SET_DEST (x), 2); - - /* If the position is constant and spans the width of INNER, - surround INNER with a USE to indicate this. */ - if (GET_CODE (pos) == CONST_INT - && INTVAL (pos) + len > GET_MODE_BITSIZE (GET_MODE (inner))) - inner = gen_rtx (USE, GET_MODE (SET_DEST (x)), inner); - -#if BITS_BIG_ENDIAN - if (GET_CODE (pos) == CONST_INT) - pos = GEN_INT (GET_MODE_BITSIZE (GET_MODE (inner)) - len - - INTVAL (pos)); - else if (GET_CODE (pos) == MINUS - && GET_CODE (XEXP (pos, 1)) == CONST_INT - && (INTVAL (XEXP (pos, 1)) - == GET_MODE_BITSIZE (GET_MODE (inner)) - len)) - /* If position is ADJUST - X, new position is X. */ - pos = XEXP (pos, 0); - else - pos = gen_binary (MINUS, GET_MODE (pos), - GEN_INT (GET_MODE_BITSIZE (GET_MODE (inner)) - - len), - pos); -#endif - } - - /* A SUBREG between two modes that occupy the same numbers of words - can be done by moving the SUBREG to the source. */ - else if (GET_CODE (SET_DEST (x)) == SUBREG - && (((GET_MODE_SIZE (GET_MODE (SET_DEST (x))) - + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD) - == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (x)))) - + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD))) - { - x = gen_rtx (SET, VOIDmode, SUBREG_REG (SET_DEST (x)), - gen_lowpart_for_combine (GET_MODE (SUBREG_REG (SET_DEST (x))), - SET_SRC (x))); - continue; - } - else - break; - - while (GET_CODE (inner) == SUBREG && subreg_lowpart_p (inner)) - inner = SUBREG_REG (inner); - - compute_mode = GET_MODE (inner); - - /* Compute a mask of LEN bits, if we can do this on the host machine. */ - if (len < HOST_BITS_PER_WIDE_INT) - mask = GEN_INT (((HOST_WIDE_INT) 1 << len) - 1); - else - break; - - /* Now compute the equivalent expression. Make a copy of INNER - for the SET_DEST in case it is a MEM into which we will substitute; - we don't want shared RTL in that case. */ - x = gen_rtx (SET, VOIDmode, copy_rtx (inner), - gen_binary (IOR, compute_mode, - gen_binary (AND, compute_mode, - gen_unary (NOT, compute_mode, - gen_binary (ASHIFT, - compute_mode, - mask, pos)), - inner), - gen_binary (ASHIFT, compute_mode, - gen_binary (AND, compute_mode, - gen_lowpart_for_combine - (compute_mode, - SET_SRC (x)), - mask), - pos))); - } - - return x; -} - -/* Return an RTX for a reference to LEN bits of INNER. If POS_RTX is nonzero, - it is an RTX that represents a variable starting position; otherwise, - POS is the (constant) starting bit position (counted from the LSB). - - INNER may be a USE. This will occur when we started with a bitfield - that went outside the boundary of the object in memory, which is - allowed on most machines. To isolate this case, we produce a USE - whose mode is wide enough and surround the MEM with it. The only - code that understands the USE is this routine. If it is not removed, - it will cause the resulting insn not to match. - - UNSIGNEDP is non-zero for an unsigned reference and zero for a - signed reference. - - IN_DEST is non-zero if this is a reference in the destination of a - SET. This is used when a ZERO_ or SIGN_EXTRACT isn't needed. If non-zero, - a STRICT_LOW_PART will be used, if zero, ZERO_EXTEND or SIGN_EXTEND will - be used. - - IN_COMPARE is non-zero if we are in a COMPARE. This means that a - ZERO_EXTRACT should be built even for bits starting at bit 0. - - MODE is the desired mode of the result (if IN_DEST == 0). */ - -static rtx -make_extraction (mode, inner, pos, pos_rtx, len, - unsignedp, in_dest, in_compare) - enum machine_mode mode; - rtx inner; - int pos; - rtx pos_rtx; - int len; - int unsignedp; - int in_dest, in_compare; -{ - /* This mode describes the size of the storage area - to fetch the overall value from. Within that, we - ignore the POS lowest bits, etc. */ - enum machine_mode is_mode = GET_MODE (inner); - enum machine_mode inner_mode; - enum machine_mode wanted_mem_mode = byte_mode; - enum machine_mode pos_mode = word_mode; - enum machine_mode extraction_mode = word_mode; - enum machine_mode tmode = mode_for_size (len, MODE_INT, 1); - int spans_byte = 0; - rtx new = 0; - rtx orig_pos_rtx = pos_rtx; - - /* Get some information about INNER and get the innermost object. */ - if (GET_CODE (inner) == USE) - /* (use:SI (mem:QI foo)) stands for (mem:SI foo). */ - /* We don't need to adjust the position because we set up the USE - to pretend that it was a full-word object. */ - spans_byte = 1, inner = XEXP (inner, 0); - else if (GET_CODE (inner) == SUBREG && subreg_lowpart_p (inner)) - { - /* If going from (subreg:SI (mem:QI ...)) to (mem:QI ...), - consider just the QI as the memory to extract from. - The subreg adds or removes high bits; its mode is - irrelevant to the meaning of this extraction, - since POS and LEN count from the lsb. */ - if (GET_CODE (SUBREG_REG (inner)) == MEM) - is_mode = GET_MODE (SUBREG_REG (inner)); - inner = SUBREG_REG (inner); - } - - inner_mode = GET_MODE (inner); - - if (pos_rtx && GET_CODE (pos_rtx) == CONST_INT) - pos = INTVAL (pos_rtx), pos_rtx = 0; - - /* See if this can be done without an extraction. We never can if the - width of the field is not the same as that of some integer mode. For - registers, we can only avoid the extraction if the position is at the - low-order bit and this is either not in the destination or we have the - appropriate STRICT_LOW_PART operation available. - - For MEM, we can avoid an extract if the field starts on an appropriate - boundary and we can change the mode of the memory reference. However, - we cannot directly access the MEM if we have a USE and the underlying - MEM is not TMODE. This combination means that MEM was being used in a - context where bits outside its mode were being referenced; that is only - valid in bit-field insns. */ - - if (tmode != BLKmode - && ! (spans_byte && inner_mode != tmode) - && ((pos_rtx == 0 && pos == 0 && GET_CODE (inner) != MEM - && (! in_dest - || (GET_CODE (inner) == REG - && (movstrict_optab->handlers[(int) tmode].insn_code - != CODE_FOR_nothing)))) - || (GET_CODE (inner) == MEM && pos_rtx == 0 - && (pos - % (STRICT_ALIGNMENT ? GET_MODE_ALIGNMENT (tmode) - : BITS_PER_UNIT)) == 0 - /* We can't do this if we are widening INNER_MODE (it - may not be aligned, for one thing). */ - && GET_MODE_BITSIZE (inner_mode) >= GET_MODE_BITSIZE (tmode) - && (inner_mode == tmode - || (! mode_dependent_address_p (XEXP (inner, 0)) - && ! MEM_VOLATILE_P (inner)))))) - { - /* If INNER is a MEM, make a new MEM that encompasses just the desired - field. If the original and current mode are the same, we need not - adjust the offset. Otherwise, we do if bytes big endian. - - If INNER is not a MEM, get a piece consisting of the just the field - of interest (in this case POS must be 0). */ - - if (GET_CODE (inner) == MEM) - { - int offset; - /* POS counts from lsb, but make OFFSET count in memory order. */ - if (BYTES_BIG_ENDIAN) - offset = (GET_MODE_BITSIZE (is_mode) - len - pos) / BITS_PER_UNIT; - else - offset = pos / BITS_PER_UNIT; - - new = gen_rtx (MEM, tmode, plus_constant (XEXP (inner, 0), offset)); - RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (inner); - MEM_VOLATILE_P (new) = MEM_VOLATILE_P (inner); - MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (inner); - } - else if (GET_CODE (inner) == REG) - /* We can't call gen_lowpart_for_combine here since we always want - a SUBREG and it would sometimes return a new hard register. */ - new = gen_rtx (SUBREG, tmode, inner, - (WORDS_BIG_ENDIAN - && GET_MODE_SIZE (inner_mode) > UNITS_PER_WORD - ? ((GET_MODE_SIZE (inner_mode) - GET_MODE_SIZE (tmode)) - / UNITS_PER_WORD) - : 0)); - else - new = force_to_mode (inner, tmode, len, NULL_RTX); - - /* If this extraction is going into the destination of a SET, - make a STRICT_LOW_PART unless we made a MEM. */ - - if (in_dest) - return (GET_CODE (new) == MEM ? new - : (GET_CODE (new) != SUBREG - ? gen_rtx (CLOBBER, tmode, const0_rtx) - : gen_rtx_combine (STRICT_LOW_PART, VOIDmode, new))); - - /* Otherwise, sign- or zero-extend unless we already are in the - proper mode. */ - - return (mode == tmode ? new - : gen_rtx_combine (unsignedp ? ZERO_EXTEND : SIGN_EXTEND, - mode, new)); - } - - /* Unless this is a COMPARE or we have a funny memory reference, - don't do anything with zero-extending field extracts starting at - the low-order bit since they are simple AND operations. */ - if (pos_rtx == 0 && pos == 0 && ! in_dest - && ! in_compare && ! spans_byte && unsignedp) - return 0; - - /* Get the mode to use should INNER be a MEM, the mode for the position, - and the mode for the result. */ -#ifdef HAVE_insv - if (in_dest) - { - wanted_mem_mode = insn_operand_mode[(int) CODE_FOR_insv][0]; - pos_mode = insn_operand_mode[(int) CODE_FOR_insv][2]; - extraction_mode = insn_operand_mode[(int) CODE_FOR_insv][3]; - } -#endif - -#ifdef HAVE_extzv - if (! in_dest && unsignedp) - { - wanted_mem_mode = insn_operand_mode[(int) CODE_FOR_extzv][1]; - pos_mode = insn_operand_mode[(int) CODE_FOR_extzv][3]; - extraction_mode = insn_operand_mode[(int) CODE_FOR_extzv][0]; - } -#endif - -#ifdef HAVE_extv - if (! in_dest && ! unsignedp) - { - wanted_mem_mode = insn_operand_mode[(int) CODE_FOR_extv][1]; - pos_mode = insn_operand_mode[(int) CODE_FOR_extv][3]; - extraction_mode = insn_operand_mode[(int) CODE_FOR_extv][0]; - } -#endif - - /* Never narrow an object, since that might not be safe. */ - - if (mode != VOIDmode - && GET_MODE_SIZE (extraction_mode) < GET_MODE_SIZE (mode)) - extraction_mode = mode; - - if (pos_rtx && GET_MODE (pos_rtx) != VOIDmode - && GET_MODE_SIZE (pos_mode) < GET_MODE_SIZE (GET_MODE (pos_rtx))) - pos_mode = GET_MODE (pos_rtx); - - /* If this is not from memory or we have to change the mode of memory and - cannot, the desired mode is EXTRACTION_MODE. */ - if (GET_CODE (inner) != MEM - || (inner_mode != wanted_mem_mode - && (mode_dependent_address_p (XEXP (inner, 0)) - || MEM_VOLATILE_P (inner)))) - wanted_mem_mode = extraction_mode; - -#if BITS_BIG_ENDIAN - /* If position is constant, compute new position. Otherwise, build - subtraction. */ - if (pos_rtx == 0) - pos = (MAX (GET_MODE_BITSIZE (is_mode), GET_MODE_BITSIZE (wanted_mem_mode)) - - len - pos); - else - pos_rtx - = gen_rtx_combine (MINUS, GET_MODE (pos_rtx), - GEN_INT (MAX (GET_MODE_BITSIZE (is_mode), - GET_MODE_BITSIZE (wanted_mem_mode)) - - len), - pos_rtx); -#endif - - /* If INNER has a wider mode, make it smaller. If this is a constant - extract, try to adjust the byte to point to the byte containing - the value. */ - if (wanted_mem_mode != VOIDmode - && GET_MODE_SIZE (wanted_mem_mode) < GET_MODE_SIZE (is_mode) - && ((GET_CODE (inner) == MEM - && (inner_mode == wanted_mem_mode - || (! mode_dependent_address_p (XEXP (inner, 0)) - && ! MEM_VOLATILE_P (inner)))))) - { - int offset = 0; - - /* The computations below will be correct if the machine is big - endian in both bits and bytes or little endian in bits and bytes. - If it is mixed, we must adjust. */ - - /* If bytes are big endian and we had a paradoxical SUBREG, we must - adjust OFFSET to compensate. */ -#if BYTES_BIG_ENDIAN - if (! spans_byte - && GET_MODE_SIZE (inner_mode) < GET_MODE_SIZE (is_mode)) - offset -= GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (inner_mode); -#endif - - /* If this is a constant position, we can move to the desired byte. */ - if (pos_rtx == 0) - { - offset += pos / BITS_PER_UNIT; - pos %= GET_MODE_BITSIZE (wanted_mem_mode); - } - -#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN - if (! spans_byte && is_mode != wanted_mem_mode) - offset = (GET_MODE_SIZE (is_mode) - - GET_MODE_SIZE (wanted_mem_mode) - offset); -#endif - - if (offset != 0 || inner_mode != wanted_mem_mode) - { - rtx newmem = gen_rtx (MEM, wanted_mem_mode, - plus_constant (XEXP (inner, 0), offset)); - RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (inner); - MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (inner); - MEM_IN_STRUCT_P (newmem) = MEM_IN_STRUCT_P (inner); - inner = newmem; - } - } - - /* If INNER is not memory, we can always get it into the proper mode. */ - else if (GET_CODE (inner) != MEM) - inner = force_to_mode (inner, extraction_mode, - (pos < 0 ? GET_MODE_BITSIZE (extraction_mode) - : len + pos), - NULL_RTX); - - /* Adjust mode of POS_RTX, if needed. If we want a wider mode, we - have to zero extend. Otherwise, we can just use a SUBREG. */ - if (pos_rtx != 0 - && GET_MODE_SIZE (pos_mode) > GET_MODE_SIZE (GET_MODE (pos_rtx))) - pos_rtx = gen_rtx_combine (ZERO_EXTEND, pos_mode, pos_rtx); - else if (pos_rtx != 0 - && GET_MODE_SIZE (pos_mode) < GET_MODE_SIZE (GET_MODE (pos_rtx))) - pos_rtx = gen_lowpart_for_combine (pos_mode, pos_rtx); - - /* Make POS_RTX unless we already have it and it is correct. If we don't - have a POS_RTX but we do have an ORIG_POS_RTX, the latter must - be a CONST_INT. */ - if (pos_rtx == 0 && orig_pos_rtx != 0 && INTVAL (orig_pos_rtx) == pos) - pos_rtx = orig_pos_rtx; - - else if (pos_rtx == 0) - pos_rtx = GEN_INT (pos); - - /* Make the required operation. See if we can use existing rtx. */ - new = gen_rtx_combine (unsignedp ? ZERO_EXTRACT : SIGN_EXTRACT, - extraction_mode, inner, GEN_INT (len), pos_rtx); - if (! in_dest) - new = gen_lowpart_for_combine (mode, new); - - return new; -} - -/* Look at the expression rooted at X. Look for expressions - equivalent to ZERO_EXTRACT, SIGN_EXTRACT, ZERO_EXTEND, SIGN_EXTEND. - Form these expressions. - - Return the new rtx, usually just X. - - Also, for machines like the Vax that don't have logical shift insns, - try to convert logical to arithmetic shift operations in cases where - they are equivalent. This undoes the canonicalizations to logical - shifts done elsewhere. - - We try, as much as possible, to re-use rtl expressions to save memory. - - IN_CODE says what kind of expression we are processing. Normally, it is - SET. In a memory address (inside a MEM, PLUS or minus, the latter two - being kludges), it is MEM. When processing the arguments of a comparison - or a COMPARE against zero, it is COMPARE. */ - -static rtx -make_compound_operation (x, in_code) - rtx x; - enum rtx_code in_code; -{ - enum rtx_code code = GET_CODE (x); - enum machine_mode mode = GET_MODE (x); - int mode_width = GET_MODE_BITSIZE (mode); - enum rtx_code next_code; - int i, count; - rtx new = 0; - rtx tem; - char *fmt; - - /* Select the code to be used in recursive calls. Once we are inside an - address, we stay there. If we have a comparison, set to COMPARE, - but once inside, go back to our default of SET. */ - - next_code = (code == MEM || code == PLUS || code == MINUS ? MEM - : ((code == COMPARE || GET_RTX_CLASS (code) == '<') - && XEXP (x, 1) == const0_rtx) ? COMPARE - : in_code == COMPARE ? SET : in_code); - - /* Process depending on the code of this operation. If NEW is set - non-zero, it will be returned. */ - - switch (code) - { - case ASHIFT: - case LSHIFT: - /* Convert shifts by constants into multiplications if inside - an address. */ - if (in_code == MEM && GET_CODE (XEXP (x, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT - && INTVAL (XEXP (x, 1)) >= 0) - { - new = make_compound_operation (XEXP (x, 0), next_code); - new = gen_rtx_combine (MULT, mode, new, - GEN_INT ((HOST_WIDE_INT) 1 - << INTVAL (XEXP (x, 1)))); - } - break; - - case AND: - /* If the second operand is not a constant, we can't do anything - with it. */ - if (GET_CODE (XEXP (x, 1)) != CONST_INT) - break; - - /* If the constant is a power of two minus one and the first operand - is a logical right shift, make an extraction. */ - if (GET_CODE (XEXP (x, 0)) == LSHIFTRT - && (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0) - { - new = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code); - new = make_extraction (mode, new, 0, XEXP (XEXP (x, 0), 1), i, 1, - 0, in_code == COMPARE); - } - - /* Same as previous, but for (subreg (lshiftrt ...)) in first op. */ - else if (GET_CODE (XEXP (x, 0)) == SUBREG - && subreg_lowpart_p (XEXP (x, 0)) - && GET_CODE (SUBREG_REG (XEXP (x, 0))) == LSHIFTRT - && (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0) - { - new = make_compound_operation (XEXP (SUBREG_REG (XEXP (x, 0)), 0), - next_code); - new = make_extraction (GET_MODE (SUBREG_REG (XEXP (x, 0))), new, 0, - XEXP (SUBREG_REG (XEXP (x, 0)), 1), i, 1, - 0, in_code == COMPARE); - } - - /* If we are have (and (rotate X C) M) and C is larger than the number - of bits in M, this is an extraction. */ - - else if (GET_CODE (XEXP (x, 0)) == ROTATE - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0 - && i <= INTVAL (XEXP (XEXP (x, 0), 1))) - { - new = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code); - new = make_extraction (mode, new, - (GET_MODE_BITSIZE (mode) - - INTVAL (XEXP (XEXP (x, 0), 1))), - NULL_RTX, i, 1, 0, in_code == COMPARE); - } - - /* On machines without logical shifts, if the operand of the AND is - a logical shift and our mask turns off all the propagated sign - bits, we can replace the logical shift with an arithmetic shift. */ - else if (ashr_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing - && (lshr_optab->handlers[(int) mode].insn_code - == CODE_FOR_nothing) - && GET_CODE (XEXP (x, 0)) == LSHIFTRT - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && INTVAL (XEXP (XEXP (x, 0), 1)) >= 0 - && INTVAL (XEXP (XEXP (x, 0), 1)) < HOST_BITS_PER_WIDE_INT - && mode_width <= HOST_BITS_PER_WIDE_INT) - { - unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode); - - mask >>= INTVAL (XEXP (XEXP (x, 0), 1)); - if ((INTVAL (XEXP (x, 1)) & ~mask) == 0) - SUBST (XEXP (x, 0), - gen_rtx_combine (ASHIFTRT, mode, - make_compound_operation (XEXP (XEXP (x, 0), 0), - next_code), - XEXP (XEXP (x, 0), 1))); - } - - /* If the constant is one less than a power of two, this might be - representable by an extraction even if no shift is present. - If it doesn't end up being a ZERO_EXTEND, we will ignore it unless - we are in a COMPARE. */ - else if ((i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0) - new = make_extraction (mode, - make_compound_operation (XEXP (x, 0), - next_code), - 0, NULL_RTX, i, 1, 0, in_code == COMPARE); - - /* If we are in a comparison and this is an AND with a power of two, - convert this into the appropriate bit extract. */ - else if (in_code == COMPARE - && (i = exact_log2 (INTVAL (XEXP (x, 1)))) >= 0) - new = make_extraction (mode, - make_compound_operation (XEXP (x, 0), - next_code), - i, NULL_RTX, 1, 1, 0, 1); - - break; - - case LSHIFTRT: - /* If the sign bit is known to be zero, replace this with an - arithmetic shift. */ - if (ashr_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing - && lshr_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing - && mode_width <= HOST_BITS_PER_WIDE_INT - && (nonzero_bits (XEXP (x, 0), mode) & (1 << (mode_width - 1))) == 0) - { - new = gen_rtx_combine (ASHIFTRT, mode, - make_compound_operation (XEXP (x, 0), - next_code), - XEXP (x, 1)); - break; - } - - /* ... fall through ... */ - - case ASHIFTRT: - /* If we have (ashiftrt (ashift foo C1) C2) with C2 >= C1, - this is a SIGN_EXTRACT. */ - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && GET_CODE (XEXP (x, 0)) == ASHIFT - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) >= INTVAL (XEXP (XEXP (x, 0), 1))) - { - new = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code); - new = make_extraction (mode, new, - (INTVAL (XEXP (x, 1)) - - INTVAL (XEXP (XEXP (x, 0), 1))), - NULL_RTX, mode_width - INTVAL (XEXP (x, 1)), - code == LSHIFTRT, 0, in_code == COMPARE); - } - - /* Similarly if we have (ashifrt (OP (ashift foo C1) C3) C2). In these - cases, we are better off returning a SIGN_EXTEND of the operation. */ - - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && (GET_CODE (XEXP (x, 0)) == IOR || GET_CODE (XEXP (x, 0)) == AND - || GET_CODE (XEXP (x, 0)) == XOR - || GET_CODE (XEXP (x, 0)) == PLUS) - && GET_CODE (XEXP (XEXP (x, 0), 0)) == ASHIFT - && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT - && INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1)) < HOST_BITS_PER_WIDE_INT - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && 0 == (INTVAL (XEXP (XEXP (x, 0), 1)) - & (((HOST_WIDE_INT) 1 - << (MIN (INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1)), - INTVAL (XEXP (x, 1))) - - 1))))) - { - rtx c1 = XEXP (XEXP (XEXP (x, 0), 0), 1); - rtx c2 = XEXP (x, 1); - rtx c3 = XEXP (XEXP (x, 0), 1); - HOST_WIDE_INT newop1; - rtx inner = XEXP (XEXP (XEXP (x, 0), 0), 0); - - /* If C1 > C2, INNER needs to have the shift performed on it - for C1-C2 bits. */ - if (INTVAL (c1) > INTVAL (c2)) - { - inner = gen_binary (ASHIFT, mode, inner, - GEN_INT (INTVAL (c1) - INTVAL (c2))); - c1 = c2; - } - - newop1 = INTVAL (c3) >> INTVAL (c1); - new = make_compound_operation (inner, - GET_CODE (XEXP (x, 0)) == PLUS - ? MEM : GET_CODE (XEXP (x, 0))); - new = make_extraction (mode, - gen_binary (GET_CODE (XEXP (x, 0)), mode, new, - GEN_INT (newop1)), - INTVAL (c2) - INTVAL (c1), - NULL_RTX, mode_width - INTVAL (c2), - code == LSHIFTRT, 0, in_code == COMPARE); - } - - /* Similarly for (ashiftrt (neg (ashift FOO C1)) C2). */ - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && GET_CODE (XEXP (x, 0)) == NEG - && GET_CODE (XEXP (XEXP (x, 0), 0)) == ASHIFT - && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) >= INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1))) - { - new = make_compound_operation (XEXP (XEXP (XEXP (x, 0), 0), 0), - next_code); - new = make_extraction (mode, - gen_unary (GET_CODE (XEXP (x, 0)), mode, - new, 0), - (INTVAL (XEXP (x, 1)) - - INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1))), - NULL_RTX, mode_width - INTVAL (XEXP (x, 1)), - code == LSHIFTRT, 0, in_code == COMPARE); - } - break; - - case SUBREG: - /* Call ourselves recursively on the inner expression. If we are - narrowing the object and it has a different RTL code from - what it originally did, do this SUBREG as a force_to_mode. */ - - tem = make_compound_operation (SUBREG_REG (x), in_code); - if (GET_CODE (tem) != GET_CODE (SUBREG_REG (x)) - && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (tem)) - && subreg_lowpart_p (x)) - { - rtx newer = force_to_mode (tem, mode, - GET_MODE_BITSIZE (mode), NULL_RTX); - - /* If we have something other than a SUBREG, we might have - done an expansion, so rerun outselves. */ - if (GET_CODE (newer) != SUBREG) - newer = make_compound_operation (newer, in_code); - - return newer; - } - } - - if (new) - { - x = gen_lowpart_for_combine (mode, new); - code = GET_CODE (x); - } - - /* Now recursively process each operand of this operation. */ - fmt = GET_RTX_FORMAT (code); - for (i = 0; i < GET_RTX_LENGTH (code); i++) - if (fmt[i] == 'e') - { - new = make_compound_operation (XEXP (x, i), next_code); - SUBST (XEXP (x, i), new); - } - - return x; -} - -/* Given M see if it is a value that would select a field of bits - within an item, but not the entire word. Return -1 if not. - Otherwise, return the starting position of the field, where 0 is the - low-order bit. - - *PLEN is set to the length of the field. */ - -static int -get_pos_from_mask (m, plen) - unsigned HOST_WIDE_INT m; - int *plen; -{ - /* Get the bit number of the first 1 bit from the right, -1 if none. */ - int pos = exact_log2 (m & - m); - - if (pos < 0) - return -1; - - /* Now shift off the low-order zero bits and see if we have a power of - two minus 1. */ - *plen = exact_log2 ((m >> pos) + 1); - - if (*plen <= 0) - return -1; - - return pos; -} - -/* Rewrite X so that it is an expression in MODE. We only care about the - low-order BITS bits so we can ignore AND operations that just clear - higher-order bits. - - Also, if REG is non-zero and X is a register equal in value to REG, - replace X with REG. */ - -static rtx -force_to_mode (x, mode, bits, reg) - rtx x; - enum machine_mode mode; - int bits; - rtx reg; -{ - enum rtx_code code = GET_CODE (x); - enum machine_mode op_mode = mode; - - /* If X is narrower than MODE or if BITS is larger than the size of MODE, - just get X in the proper mode. */ - - if (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (mode) - || bits > GET_MODE_BITSIZE (mode)) - return gen_lowpart_for_combine (mode, x); - - switch (code) - { - case SIGN_EXTEND: - case ZERO_EXTEND: - case ZERO_EXTRACT: - case SIGN_EXTRACT: - x = expand_compound_operation (x); - if (GET_CODE (x) != code) - return force_to_mode (x, mode, bits, reg); - break; - - case REG: - if (reg != 0 && (rtx_equal_p (get_last_value (reg), x) - || rtx_equal_p (reg, get_last_value (x)))) - x = reg; - break; - - case CONST_INT: - if (bits < HOST_BITS_PER_WIDE_INT) - x = GEN_INT (INTVAL (x) & (((HOST_WIDE_INT) 1 << bits) - 1)); - return x; - - case SUBREG: - /* Ignore low-order SUBREGs. */ - if (subreg_lowpart_p (x)) - return force_to_mode (SUBREG_REG (x), mode, bits, reg); - break; - - case AND: - /* If this is an AND with a constant. Otherwise, we fall through to - do the general binary case. */ - - if (GET_CODE (XEXP (x, 1)) == CONST_INT) - { - HOST_WIDE_INT mask = INTVAL (XEXP (x, 1)); - int len = exact_log2 (mask + 1); - rtx op = XEXP (x, 0); - - /* If this is masking some low-order bits, we may be able to - impose a stricter constraint on what bits of the operand are - required. */ - - op = force_to_mode (op, mode, len > 0 ? MIN (len, bits) : bits, - reg); - - if (bits < HOST_BITS_PER_WIDE_INT) - mask &= ((HOST_WIDE_INT) 1 << bits) - 1; - - /* If we have no AND in MODE, use the original mode for the - operation. */ - - if (and_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) - op_mode = GET_MODE (x); - - x = simplify_and_const_int (x, op_mode, op, mask); - - /* If X is still an AND, see if it is an AND with a mask that - is just some low-order bits. If so, and it is BITS wide (it - can't be wider), we don't need it. */ - - if (GET_CODE (x) == AND && GET_CODE (XEXP (x, 1)) == CONST_INT - && bits < HOST_BITS_PER_WIDE_INT - && INTVAL (XEXP (x, 1)) == ((HOST_WIDE_INT) 1 << bits) - 1) - x = XEXP (x, 0); - - break; - } - - /* ... fall through ... */ - - case PLUS: - case MINUS: - case MULT: - case IOR: - case XOR: - /* For most binary operations, just propagate into the operation and - change the mode if we have an operation of that mode. */ - - if ((code == PLUS - && add_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) - || (code == MINUS - && sub_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) - || (code == MULT && (smul_optab->handlers[(int) mode].insn_code - == CODE_FOR_nothing)) - || (code == AND - && and_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) - || (code == IOR - && ior_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) - || (code == XOR && (xor_optab->handlers[(int) mode].insn_code - == CODE_FOR_nothing))) - op_mode = GET_MODE (x); - - x = gen_binary (code, op_mode, - gen_lowpart_for_combine (op_mode, - force_to_mode (XEXP (x, 0), - mode, bits, - reg)), - gen_lowpart_for_combine (op_mode, - force_to_mode (XEXP (x, 1), - mode, bits, - reg))); - break; - - case ASHIFT: - case LSHIFT: - /* For left shifts, do the same, but just for the first operand. - However, we cannot do anything with shifts where we cannot - guarantee that the counts are smaller than the size of the mode - because such a count will have a different meaning in a - wider mode. - - If we can narrow the shift and know the count, we need even fewer - bits of the first operand. */ - - if (! (GET_CODE (XEXP (x, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) < GET_MODE_BITSIZE (mode)) - && ! (GET_MODE (XEXP (x, 1)) != VOIDmode - && (nonzero_bits (XEXP (x, 1), GET_MODE (XEXP (x, 1))) - < (unsigned HOST_WIDE_INT) GET_MODE_BITSIZE (mode)))) - break; - - if (GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) < bits) - bits -= INTVAL (XEXP (x, 1)); - - if ((code == ASHIFT - && ashl_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) - || (code == LSHIFT && (lshl_optab->handlers[(int) mode].insn_code - == CODE_FOR_nothing))) - op_mode = GET_MODE (x); - - x = gen_binary (code, op_mode, - gen_lowpart_for_combine (op_mode, - force_to_mode (XEXP (x, 0), - mode, bits, - reg)), - XEXP (x, 1)); - break; - - case LSHIFTRT: - /* Here we can only do something if the shift count is a constant and - the count plus BITS is no larger than the width of MODE. In that - case, we can do the shift in MODE. */ - - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) + bits <= GET_MODE_BITSIZE (mode)) - { - rtx inner = force_to_mode (XEXP (x, 0), mode, - bits + INTVAL (XEXP (x, 1)), reg); - - if (lshr_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) - op_mode = GET_MODE (x); - - x = gen_binary (LSHIFTRT, op_mode, - gen_lowpart_for_combine (op_mode, inner), - XEXP (x, 1)); - } - break; - - case ASHIFTRT: - /* If this is a sign-extension operation that just affects bits - we don't care about, remove it. */ - - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) >= 0 - && INTVAL (XEXP (x, 1)) <= GET_MODE_BITSIZE (GET_MODE (x)) - bits - && GET_CODE (XEXP (x, 0)) == ASHIFT - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && INTVAL (XEXP (XEXP (x, 0), 1)) == INTVAL (XEXP (x, 1))) - return force_to_mode (XEXP (XEXP (x, 0), 0), mode, bits, reg); - break; - - case NEG: - case NOT: - if ((code == NEG - && neg_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) - || (code == NOT && (one_cmpl_optab->handlers[(int) mode].insn_code - == CODE_FOR_nothing))) - op_mode = GET_MODE (x); - - /* Handle these similarly to the way we handle most binary operations. */ - x = gen_unary (code, op_mode, - gen_lowpart_for_combine (op_mode, - force_to_mode (XEXP (x, 0), mode, - bits, reg))); - break; - - case IF_THEN_ELSE: - /* We have no way of knowing if the IF_THEN_ELSE can itself be - written in a narrower mode. We play it safe and do not do so. */ - - SUBST (XEXP (x, 1), - gen_lowpart_for_combine (GET_MODE (x), - force_to_mode (XEXP (x, 1), mode, - bits, reg))); - SUBST (XEXP (x, 2), - gen_lowpart_for_combine (GET_MODE (x), - force_to_mode (XEXP (x, 2), mode, - bits, reg))); - break; - } - - /* Ensure we return a value of the proper mode. */ - return gen_lowpart_for_combine (mode, x); -} - -/* Return the value of expression X given the fact that condition COND - is known to be true when applied to REG as its first operand and VAL - as its second. X is known to not be shared and so can be modified in - place. - - We only handle the simplest cases, and specifically those cases that - arise with IF_THEN_ELSE expressions. */ - -static rtx -known_cond (x, cond, reg, val) - rtx x; - enum rtx_code cond; - rtx reg, val; -{ - enum rtx_code code = GET_CODE (x); - rtx new, temp; - char *fmt; - int i, j; - - if (side_effects_p (x)) - return x; - - if (cond == EQ && rtx_equal_p (x, reg)) - return val; - - /* If X is (abs REG) and we know something about REG's relationship - with zero, we may be able to simplify this. */ - - if (code == ABS && rtx_equal_p (XEXP (x, 0), reg) && val == const0_rtx) - switch (cond) - { - case GE: case GT: case EQ: - return XEXP (x, 0); - case LT: case LE: - return gen_unary (NEG, GET_MODE (XEXP (x, 0)), XEXP (x, 0)); - } - - /* The only other cases we handle are MIN, MAX, and comparisons if the - operands are the same as REG and VAL. */ - - else if (GET_RTX_CLASS (code) == '<' || GET_RTX_CLASS (code) == 'c') - { - if (rtx_equal_p (XEXP (x, 0), val)) - cond = swap_condition (cond), temp = val, val = reg, reg = temp; - - if (rtx_equal_p (XEXP (x, 0), reg) && rtx_equal_p (XEXP (x, 1), val)) - { - if (GET_RTX_CLASS (code) == '<') - return (comparison_dominates_p (cond, code) ? const_true_rtx - : (comparison_dominates_p (cond, - reverse_condition (code)) - ? const0_rtx : x)); - - else if (code == SMAX || code == SMIN - || code == UMIN || code == UMAX) - { - int unsignedp = (code == UMIN || code == UMAX); - - if (code == SMAX || code == UMAX) - cond = reverse_condition (cond); - - switch (cond) - { - case GE: case GT: - return unsignedp ? x : XEXP (x, 1); - case LE: case LT: - return unsignedp ? x : XEXP (x, 0); - case GEU: case GTU: - return unsignedp ? XEXP (x, 1) : x; - case LEU: case LTU: - return unsignedp ? XEXP (x, 0) : x; - } - } - } - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - SUBST (XEXP (x, i), known_cond (XEXP (x, i), cond, reg, val)); - else if (fmt[i] == 'E') - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - SUBST (XVECEXP (x, i, j), known_cond (XVECEXP (x, i, j), - cond, reg, val)); - } - - return x; -} - -/* See if X, a SET operation, can be rewritten as a bit-field assignment. - Return that assignment if so. - - We only handle the most common cases. */ - -static rtx -make_field_assignment (x) - rtx x; -{ - rtx dest = SET_DEST (x); - rtx src = SET_SRC (x); - rtx ourdest; - rtx assign; - HOST_WIDE_INT c1; - int pos, len; - rtx other; - enum machine_mode mode; - - /* If SRC was (and (not (ashift (const_int 1) POS)) DEST), this is - a clear of a one-bit field. We will have changed it to - (and (rotate (const_int -2) POS) DEST), so check for that. Also check - for a SUBREG. */ - - if (GET_CODE (src) == AND && GET_CODE (XEXP (src, 0)) == ROTATE - && GET_CODE (XEXP (XEXP (src, 0), 0)) == CONST_INT - && INTVAL (XEXP (XEXP (src, 0), 0)) == -2 - && (rtx_equal_p (dest, XEXP (src, 1)) - || rtx_equal_p (dest, get_last_value (XEXP (src, 1))) - || rtx_equal_p (get_last_value (dest), XEXP (src, 1)))) - { - assign = make_extraction (VOIDmode, dest, 0, XEXP (XEXP (src, 0), 1), - 1, 1, 1, 0); - return gen_rtx (SET, VOIDmode, assign, const0_rtx); - } - - else if (GET_CODE (src) == AND && GET_CODE (XEXP (src, 0)) == SUBREG - && subreg_lowpart_p (XEXP (src, 0)) - && (GET_MODE_SIZE (GET_MODE (XEXP (src, 0))) - < GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (src, 0))))) - && GET_CODE (SUBREG_REG (XEXP (src, 0))) == ROTATE - && INTVAL (XEXP (SUBREG_REG (XEXP (src, 0)), 0)) == -2 - && (rtx_equal_p (dest, XEXP (src, 1)) - || rtx_equal_p (dest, get_last_value (XEXP (src, 1))) - || rtx_equal_p (get_last_value (dest), XEXP (src, 1)))) - { - assign = make_extraction (VOIDmode, dest, 0, - XEXP (SUBREG_REG (XEXP (src, 0)), 1), - 1, 1, 1, 0); - return gen_rtx (SET, VOIDmode, assign, const0_rtx); - } - - /* If SRC is (ior (ashift (const_int 1) POS DEST)), this is a set of a - one-bit field. */ - else if (GET_CODE (src) == IOR && GET_CODE (XEXP (src, 0)) == ASHIFT - && XEXP (XEXP (src, 0), 0) == const1_rtx - && (rtx_equal_p (dest, XEXP (src, 1)) - || rtx_equal_p (dest, get_last_value (XEXP (src, 1))) - || rtx_equal_p (get_last_value (dest), XEXP (src, 1)))) - { - assign = make_extraction (VOIDmode, dest, 0, XEXP (XEXP (src, 0), 1), - 1, 1, 1, 0); - return gen_rtx (SET, VOIDmode, assign, const1_rtx); - } - - /* The other case we handle is assignments into a constant-position - field. They look like (ior (and DEST C1) OTHER). If C1 represents - a mask that has all one bits except for a group of zero bits and - OTHER is known to have zeros where C1 has ones, this is such an - assignment. Compute the position and length from C1. Shift OTHER - to the appropriate position, force it to the required mode, and - make the extraction. Check for the AND in both operands. */ - - if (GET_CODE (src) == IOR && GET_CODE (XEXP (src, 0)) == AND - && GET_CODE (XEXP (XEXP (src, 0), 1)) == CONST_INT - && (rtx_equal_p (XEXP (XEXP (src, 0), 0), dest) - || rtx_equal_p (XEXP (XEXP (src, 0), 0), get_last_value (dest)) - || rtx_equal_p (get_last_value (XEXP (XEXP (src, 0), 1)), dest))) - c1 = INTVAL (XEXP (XEXP (src, 0), 1)), other = XEXP (src, 1); - else if (GET_CODE (src) == IOR && GET_CODE (XEXP (src, 1)) == AND - && GET_CODE (XEXP (XEXP (src, 1), 1)) == CONST_INT - && (rtx_equal_p (XEXP (XEXP (src, 1), 0), dest) - || rtx_equal_p (XEXP (XEXP (src, 1), 0), get_last_value (dest)) - || rtx_equal_p (get_last_value (XEXP (XEXP (src, 1), 0)), - dest))) - c1 = INTVAL (XEXP (XEXP (src, 1), 1)), other = XEXP (src, 0); - else - return x; - - pos = get_pos_from_mask (~c1, &len); - if (pos < 0 || pos + len > GET_MODE_BITSIZE (GET_MODE (dest)) - || (GET_MODE_BITSIZE (GET_MODE (other)) <= HOST_BITS_PER_WIDE_INT - && (c1 & nonzero_bits (other, GET_MODE (other))) != 0)) - return x; - - assign = make_extraction (VOIDmode, dest, pos, NULL_RTX, len, 1, 1, 0); - - /* The mode to use for the source is the mode of the assignment, or of - what is inside a possible STRICT_LOW_PART. */ - mode = (GET_CODE (assign) == STRICT_LOW_PART - ? GET_MODE (XEXP (assign, 0)) : GET_MODE (assign)); - - /* Shift OTHER right POS places and make it the source, restricting it - to the proper length and mode. */ - - src = force_to_mode (simplify_shift_const (NULL_RTX, LSHIFTRT, - GET_MODE (src), other, pos), - mode, len, dest); - - return gen_rtx_combine (SET, VOIDmode, assign, src); -} - -/* See if X is of the form (+ (* a c) (* b c)) and convert to (* (+ a b) c) - if so. */ - -static rtx -apply_distributive_law (x) - rtx x; -{ - enum rtx_code code = GET_CODE (x); - rtx lhs, rhs, other; - rtx tem; - enum rtx_code inner_code; - - /* Distributivity is not true for floating point. - It can change the value. So don't do it. - -- rms and moshier@world.std.com. */ - if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) - return x; - - /* The outer operation can only be one of the following: */ - if (code != IOR && code != AND && code != XOR - && code != PLUS && code != MINUS) - return x; - - lhs = XEXP (x, 0), rhs = XEXP (x, 1); - - /* If either operand is a primitive we can't do anything, so get out fast. */ - if (GET_RTX_CLASS (GET_CODE (lhs)) == 'o' - || GET_RTX_CLASS (GET_CODE (rhs)) == 'o') - return x; - - lhs = expand_compound_operation (lhs); - rhs = expand_compound_operation (rhs); - inner_code = GET_CODE (lhs); - if (inner_code != GET_CODE (rhs)) - return x; - - /* See if the inner and outer operations distribute. */ - switch (inner_code) - { - case LSHIFTRT: - case ASHIFTRT: - case AND: - case IOR: - /* These all distribute except over PLUS. */ - if (code == PLUS || code == MINUS) - return x; - break; - - case MULT: - if (code != PLUS && code != MINUS) - return x; - break; - - case ASHIFT: - case LSHIFT: - /* These are also multiplies, so they distribute over everything. */ - break; - - case SUBREG: - /* Non-paradoxical SUBREGs distributes over all operations, provided - the inner modes and word numbers are the same, this is an extraction - of a low-order part, we don't convert an fp operation to int or - vice versa, and we would not be converting a single-word - operation into a multi-word operation. The latter test is not - required, but it prevents generating unneeded multi-word operations. - Some of the previous tests are redundant given the latter test, but - are retained because they are required for correctness. - - We produce the result slightly differently in this case. */ - - if (GET_MODE (SUBREG_REG (lhs)) != GET_MODE (SUBREG_REG (rhs)) - || SUBREG_WORD (lhs) != SUBREG_WORD (rhs) - || ! subreg_lowpart_p (lhs) - || (GET_MODE_CLASS (GET_MODE (lhs)) - != GET_MODE_CLASS (GET_MODE (SUBREG_REG (lhs)))) - || (GET_MODE_SIZE (GET_MODE (lhs)) - < GET_MODE_SIZE (GET_MODE (SUBREG_REG (lhs)))) - || GET_MODE_SIZE (GET_MODE (SUBREG_REG (lhs))) > UNITS_PER_WORD) - return x; - - tem = gen_binary (code, GET_MODE (SUBREG_REG (lhs)), - SUBREG_REG (lhs), SUBREG_REG (rhs)); - return gen_lowpart_for_combine (GET_MODE (x), tem); - - default: - return x; - } - - /* Set LHS and RHS to the inner operands (A and B in the example - above) and set OTHER to the common operand (C in the example). - These is only one way to do this unless the inner operation is - commutative. */ - if (GET_RTX_CLASS (inner_code) == 'c' - && rtx_equal_p (XEXP (lhs, 0), XEXP (rhs, 0))) - other = XEXP (lhs, 0), lhs = XEXP (lhs, 1), rhs = XEXP (rhs, 1); - else if (GET_RTX_CLASS (inner_code) == 'c' - && rtx_equal_p (XEXP (lhs, 0), XEXP (rhs, 1))) - other = XEXP (lhs, 0), lhs = XEXP (lhs, 1), rhs = XEXP (rhs, 0); - else if (GET_RTX_CLASS (inner_code) == 'c' - && rtx_equal_p (XEXP (lhs, 1), XEXP (rhs, 0))) - other = XEXP (lhs, 1), lhs = XEXP (lhs, 0), rhs = XEXP (rhs, 1); - else if (rtx_equal_p (XEXP (lhs, 1), XEXP (rhs, 1))) - other = XEXP (lhs, 1), lhs = XEXP (lhs, 0), rhs = XEXP (rhs, 0); - else - return x; - - /* Form the new inner operation, seeing if it simplifies first. */ - tem = gen_binary (code, GET_MODE (x), lhs, rhs); - - /* There is one exception to the general way of distributing: - (a ^ b) | (a ^ c) -> (~a) & (b ^ c) */ - if (code == XOR && inner_code == IOR) - { - inner_code = AND; - other = gen_unary (NOT, GET_MODE (x), other); - } - - /* We may be able to continuing distributing the result, so call - ourselves recursively on the inner operation before forming the - outer operation, which we return. */ - return gen_binary (inner_code, GET_MODE (x), - apply_distributive_law (tem), other); -} - -/* We have X, a logical `and' of VAROP with the constant CONSTOP, to be done - in MODE. - - Return an equivalent form, if different from X. Otherwise, return X. If - X is zero, we are to always construct the equivalent form. */ - -static rtx -simplify_and_const_int (x, mode, varop, constop) - rtx x; - enum machine_mode mode; - rtx varop; - unsigned HOST_WIDE_INT constop; -{ - register enum machine_mode tmode; - register rtx temp; - unsigned HOST_WIDE_INT nonzero; - - /* There is a large class of optimizations based on the principle that - some operations produce results where certain bits are known to be zero, - and hence are not significant to the AND. For example, if we have just - done a left shift of one bit, the low-order bit is known to be zero and - hence an AND with a mask of ~1 would not do anything. - - At the end of the following loop, we set: - - VAROP to be the item to be AND'ed with; - CONSTOP to the constant value to AND it with. */ - - while (1) - { - /* If we ever encounter a mode wider than the host machine's widest - integer size, we can't compute the masks accurately, so give up. */ - if (GET_MODE_BITSIZE (GET_MODE (varop)) > HOST_BITS_PER_WIDE_INT) - break; - - /* Unless one of the cases below does a `continue', - a `break' will be executed to exit the loop. */ - - switch (GET_CODE (varop)) - { - case CLOBBER: - /* If VAROP is a (clobber (const_int)), return it since we know - we are generating something that won't match. */ - return varop; - -#if ! BITS_BIG_ENDIAN - case USE: - /* VAROP is a (use (mem ..)) that was made from a bit-field - extraction that spanned the boundary of the MEM. If we are - now masking so it is within that boundary, we don't need the - USE any more. */ - if ((constop & ~ GET_MODE_MASK (GET_MODE (XEXP (varop, 0)))) == 0) - { - varop = XEXP (varop, 0); - continue; - } - break; -#endif - - case SUBREG: - if (subreg_lowpart_p (varop) - /* We can ignore the effect this SUBREG if it narrows the mode - or, on machines where byte operations extend, if the - constant masks to zero all the bits the mode doesn't have. */ - && ((GET_MODE_SIZE (GET_MODE (varop)) - < GET_MODE_SIZE (GET_MODE (SUBREG_REG (varop)))) -#ifdef BYTE_LOADS_EXTEND - || (0 == (constop - & GET_MODE_MASK (GET_MODE (varop)) - & ~ GET_MODE_MASK (GET_MODE (SUBREG_REG (varop))))) -#endif - )) - { - varop = SUBREG_REG (varop); - continue; - } - break; - - case ZERO_EXTRACT: - case SIGN_EXTRACT: - case ZERO_EXTEND: - case SIGN_EXTEND: - /* Try to expand these into a series of shifts and then work - with that result. If we can't, for example, if the extract - isn't at a fixed position, give up. */ - temp = expand_compound_operation (varop); - if (temp != varop) - { - varop = temp; - continue; - } - break; - - case AND: - if (GET_CODE (XEXP (varop, 1)) == CONST_INT) - { - constop &= INTVAL (XEXP (varop, 1)); - varop = XEXP (varop, 0); - continue; - } - break; - - case IOR: - case XOR: - /* If VAROP is (ior (lshiftrt FOO C1) C2), try to commute the IOR and - LSHIFT so we end up with an (and (lshiftrt (ior ...) ...) ...) - operation which may be a bitfield extraction. Ensure - that the constant we form is not wider than the mode of - VAROP. */ - - if (GET_CODE (XEXP (varop, 0)) == LSHIFTRT - && GET_CODE (XEXP (XEXP (varop, 0), 1)) == CONST_INT - && INTVAL (XEXP (XEXP (varop, 0), 1)) >= 0 - && INTVAL (XEXP (XEXP (varop, 0), 1)) < HOST_BITS_PER_WIDE_INT - && GET_CODE (XEXP (varop, 1)) == CONST_INT - && ((INTVAL (XEXP (XEXP (varop, 0), 1)) - + floor_log2 (INTVAL (XEXP (varop, 1)))) - < GET_MODE_BITSIZE (GET_MODE (varop))) - && (INTVAL (XEXP (varop, 1)) - & ~ nonzero_bits (XEXP (varop, 0), GET_MODE (varop)) == 0)) - { - temp = GEN_INT ((INTVAL (XEXP (varop, 1)) & constop) - << INTVAL (XEXP (XEXP (varop, 0), 1))); - temp = gen_binary (GET_CODE (varop), GET_MODE (varop), - XEXP (XEXP (varop, 0), 0), temp); - varop = gen_rtx_combine (LSHIFTRT, GET_MODE (varop), - temp, XEXP (varop, 1)); - continue; - } - - /* Apply the AND to both branches of the IOR or XOR, then try to - apply the distributive law. This may eliminate operations - if either branch can be simplified because of the AND. - It may also make some cases more complex, but those cases - probably won't match a pattern either with or without this. */ - return - gen_lowpart_for_combine - (mode, apply_distributive_law - (gen_rtx_combine - (GET_CODE (varop), GET_MODE (varop), - simplify_and_const_int (NULL_RTX, GET_MODE (varop), - XEXP (varop, 0), constop), - simplify_and_const_int (NULL_RTX, GET_MODE (varop), - XEXP (varop, 1), constop)))); - - case NOT: - /* (and (not FOO)) is (and (xor FOO CONST)), so if FOO is an - LSHIFTRT, we can do the same as above. Ensure that the constant - we form is not wider than the mode of VAROP. */ - - if (GET_CODE (XEXP (varop, 0)) == LSHIFTRT - && GET_CODE (XEXP (XEXP (varop, 0), 1)) == CONST_INT - && INTVAL (XEXP (XEXP (varop, 0), 1)) >= 0 - && (INTVAL (XEXP (XEXP (varop, 0), 1)) + floor_log2 (constop) - < GET_MODE_BITSIZE (GET_MODE (varop))) - && INTVAL (XEXP (XEXP (varop, 0), 1)) < HOST_BITS_PER_WIDE_INT) - { - temp = GEN_INT (constop << INTVAL (XEXP (XEXP (varop, 0), 1))); - temp = gen_binary (XOR, GET_MODE (varop), - XEXP (XEXP (varop, 0), 0), temp); - varop = gen_rtx_combine (LSHIFTRT, GET_MODE (varop), - temp, XEXP (XEXP (varop, 0), 1)); - continue; - } - break; - - case ASHIFTRT: - /* If we are just looking for the sign bit, we don't need this - shift at all, even if it has a variable count. */ - if (constop == ((HOST_WIDE_INT) 1 - << (GET_MODE_BITSIZE (GET_MODE (varop)) - 1))) - { - varop = XEXP (varop, 0); - continue; - } - - /* If this is a shift by a constant, get a mask that contains - those bits that are not copies of the sign bit. We then have - two cases: If CONSTOP only includes those bits, this can be - a logical shift, which may allow simplifications. If CONSTOP - is a single-bit field not within those bits, we are requesting - a copy of the sign bit and hence can shift the sign bit to - the appropriate location. */ - if (GET_CODE (XEXP (varop, 1)) == CONST_INT - && INTVAL (XEXP (varop, 1)) >= 0 - && INTVAL (XEXP (varop, 1)) < HOST_BITS_PER_WIDE_INT) - { - int i = -1; - - nonzero = GET_MODE_MASK (GET_MODE (varop)); - nonzero >>= INTVAL (XEXP (varop, 1)); - - if ((constop & ~ nonzero) == 0 - || (i = exact_log2 (constop)) >= 0) - { - varop = simplify_shift_const - (varop, LSHIFTRT, GET_MODE (varop), XEXP (varop, 0), - i < 0 ? INTVAL (XEXP (varop, 1)) - : GET_MODE_BITSIZE (GET_MODE (varop)) - 1 - i); - if (GET_CODE (varop) != ASHIFTRT) - continue; - } - } - - /* If our mask is 1, convert this to a LSHIFTRT. This can be done - even if the shift count isn't a constant. */ - if (constop == 1) - varop = gen_rtx_combine (LSHIFTRT, GET_MODE (varop), - XEXP (varop, 0), XEXP (varop, 1)); - break; - - case LSHIFTRT: - /* If we have (and (lshiftrt FOO C1) C2) where the combination of the - shift and AND produces only copies of the sign bit (C2 is one less - than a power of two), we can do this with just a shift. */ - - if (GET_CODE (XEXP (varop, 1)) == CONST_INT - && ((INTVAL (XEXP (varop, 1)) - + num_sign_bit_copies (XEXP (varop, 0), - GET_MODE (XEXP (varop, 0)))) - >= GET_MODE_BITSIZE (GET_MODE (varop))) - && exact_log2 (constop + 1) >= 0) - varop - = gen_rtx_combine (LSHIFTRT, GET_MODE (varop), XEXP (varop, 0), - GEN_INT (GET_MODE_BITSIZE (GET_MODE (varop)) - - exact_log2 (constop + 1))); - break; - - case NE: - /* (and (ne FOO 0) CONST) can be (and FOO CONST) if CONST is - included in STORE_FLAG_VALUE and FOO has no bits that might be - nonzero not in CONST. */ - if ((constop & ~ STORE_FLAG_VALUE) == 0 - && XEXP (varop, 0) == const0_rtx - && (nonzero_bits (XEXP (varop, 0), mode) & ~ constop) == 0) - { - varop = XEXP (varop, 0); - continue; - } - break; - - case PLUS: - /* In (and (plus FOO C1) M), if M is a mask that just turns off - low-order bits (as in an alignment operation) and FOO is already - aligned to that boundary, we can convert remove this AND - and possibly the PLUS if it is now adding zero. */ - if (GET_CODE (XEXP (varop, 1)) == CONST_INT - && exact_log2 (-constop) >= 0 - && (nonzero_bits (XEXP (varop, 0), mode) & ~ constop) == 0) - { - varop = plus_constant (XEXP (varop, 0), - INTVAL (XEXP (varop, 1)) & constop); - constop = ~0; - break; - } - - /* ... fall through ... */ - - case MINUS: - /* In (and (plus (and FOO M1) BAR) M2), if M1 and M2 are one - less than powers of two and M2 is narrower than M1, we can - eliminate the inner AND. This occurs when incrementing - bit fields. */ - - if (GET_CODE (XEXP (varop, 0)) == ZERO_EXTRACT - || GET_CODE (XEXP (varop, 0)) == ZERO_EXTEND) - SUBST (XEXP (varop, 0), - expand_compound_operation (XEXP (varop, 0))); - - if (GET_CODE (XEXP (varop, 0)) == AND - && GET_CODE (XEXP (XEXP (varop, 0), 1)) == CONST_INT - && exact_log2 (constop + 1) >= 0 - && exact_log2 (INTVAL (XEXP (XEXP (varop, 0), 1)) + 1) >= 0 - && (~ INTVAL (XEXP (XEXP (varop, 0), 1)) & constop) == 0) - SUBST (XEXP (varop, 0), XEXP (XEXP (varop, 0), 0)); - break; - } - - break; - } - - /* If we have reached a constant, this whole thing is constant. */ - if (GET_CODE (varop) == CONST_INT) - return GEN_INT (constop & INTVAL (varop)); - - /* See what bits may be nonzero in VAROP. Unlike the general case of - a call to nonzero_bits, here we don't care about bits outside - MODE. */ - - nonzero = nonzero_bits (varop, mode) & GET_MODE_MASK (mode); - - /* Turn off all bits in the constant that are known to already be zero. - Thus, if the AND isn't needed at all, we will have CONSTOP == NONZERO_BITS - which is tested below. */ - - constop &= nonzero; - - /* If we don't have any bits left, return zero. */ - if (constop == 0) - return const0_rtx; - - /* Get VAROP in MODE. Try to get a SUBREG if not. Don't make a new SUBREG - if we already had one (just check for the simplest cases). */ - if (x && GET_CODE (XEXP (x, 0)) == SUBREG - && GET_MODE (XEXP (x, 0)) == mode - && SUBREG_REG (XEXP (x, 0)) == varop) - varop = XEXP (x, 0); - else - varop = gen_lowpart_for_combine (mode, varop); - - /* If we can't make the SUBREG, try to return what we were given. */ - if (GET_CODE (varop) == CLOBBER) - return x ? x : varop; - - /* If we are only masking insignificant bits, return VAROP. */ - if (constop == nonzero) - x = varop; - - /* Otherwise, return an AND. See how much, if any, of X we can use. */ - else if (x == 0 || GET_CODE (x) != AND || GET_MODE (x) != mode) - x = gen_rtx_combine (AND, mode, varop, GEN_INT (constop)); - - else - { - if (GET_CODE (XEXP (x, 1)) != CONST_INT - || INTVAL (XEXP (x, 1)) != constop) - SUBST (XEXP (x, 1), GEN_INT (constop)); - - SUBST (XEXP (x, 0), varop); - } - - return x; -} - -/* Given an expression, X, compute which bits in X can be non-zero. - We don't care about bits outside of those defined in MODE. - - For most X this is simply GET_MODE_MASK (GET_MODE (MODE)), but if X is - a shift, AND, or zero_extract, we can do better. */ - -static unsigned HOST_WIDE_INT -nonzero_bits (x, mode) - rtx x; - enum machine_mode mode; -{ - unsigned HOST_WIDE_INT nonzero = GET_MODE_MASK (mode); - unsigned HOST_WIDE_INT inner_nz; - enum rtx_code code; - int mode_width = GET_MODE_BITSIZE (mode); - rtx tem; - - /* If X is wider than MODE, use its mode instead. */ - if (GET_MODE_BITSIZE (GET_MODE (x)) > mode_width) - { - mode = GET_MODE (x); - nonzero = GET_MODE_MASK (mode); - mode_width = GET_MODE_BITSIZE (mode); - } - - if (mode_width > HOST_BITS_PER_WIDE_INT) - /* Our only callers in this case look for single bit values. So - just return the mode mask. Those tests will then be false. */ - return nonzero; - - code = GET_CODE (x); - switch (code) - { - case REG: -#ifdef STACK_BOUNDARY - /* If this is the stack pointer, we may know something about its - alignment. If PUSH_ROUNDING is defined, it is possible for the - stack to be momentarily aligned only to that amount, so we pick - the least alignment. */ - - if (x == stack_pointer_rtx) - { - int sp_alignment = STACK_BOUNDARY / BITS_PER_UNIT; - -#ifdef PUSH_ROUNDING - sp_alignment = MIN (PUSH_ROUNDING (1), sp_alignment); -#endif - - return nonzero & ~ (sp_alignment - 1); - } -#endif - - /* If X is a register whose nonzero bits value is current, use it. - Otherwise, if X is a register whose value we can find, use that - value. Otherwise, use the previously-computed global nonzero bits - for this register. */ - - if (reg_last_set_value[REGNO (x)] != 0 - && reg_last_set_mode[REGNO (x)] == mode - && (reg_n_sets[REGNO (x)] == 1 - || reg_last_set_label[REGNO (x)] == label_tick) - && INSN_CUID (reg_last_set[REGNO (x)]) < subst_low_cuid) - return reg_last_set_nonzero_bits[REGNO (x)]; - - tem = get_last_value (x); - - if (tem) - { -#ifdef SHORT_IMMEDIATES_SIGN_EXTEND - /* If X is narrower than MODE and TEM is a non-negative - constant that would appear negative in the mode of X, - sign-extend it for use in reg_nonzero_bits because some - machines (maybe most) will actually do the sign-extension - and this is the conservative approach. - - ??? For 2.5, try to tighten up the MD files in this regard - instead of this kludge. */ - - if (GET_MODE_BITSIZE (GET_MODE (x)) < mode_width - && GET_CODE (tem) == CONST_INT - && INTVAL (tem) > 0 - && 0 != (INTVAL (tem) - & ((HOST_WIDE_INT) 1 - << GET_MODE_BITSIZE (GET_MODE (x))))) - tem = GEN_INT (INTVAL (tem) - | ((HOST_WIDE_INT) (-1) - << GET_MODE_BITSIZE (GET_MODE (x)))); -#endif - return nonzero_bits (tem, mode); - } - else if (nonzero_sign_valid && reg_nonzero_bits[REGNO (x)]) - return reg_nonzero_bits[REGNO (x)] & nonzero; - else - return nonzero; - - case CONST_INT: -#ifdef SHORT_IMMEDIATES_SIGN_EXTEND - /* If X is negative in MODE, sign-extend the value. */ - if (INTVAL (x) > 0 - && 0 != (INTVAL (x) - & ((HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (GET_MODE (x))))) - return (INTVAL (x) - | ((HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (GET_MODE (x)))); -#endif - - return INTVAL (x); - -#ifdef BYTE_LOADS_ZERO_EXTEND - case MEM: - /* In many, if not most, RISC machines, reading a byte from memory - zeros the rest of the register. Noticing that fact saves a lot - of extra zero-extends. */ - nonzero &= GET_MODE_MASK (GET_MODE (x)); - break; -#endif - -#if STORE_FLAG_VALUE == 1 - case EQ: case NE: - case GT: case GTU: - case LT: case LTU: - case GE: case GEU: - case LE: case LEU: - - if (GET_MODE_CLASS (mode) == MODE_INT) - nonzero = 1; - - /* A comparison operation only sets the bits given by its mode. The - rest are set undefined. */ - if (GET_MODE_SIZE (GET_MODE (x)) < mode_width) - nonzero |= (GET_MODE_MASK (mode) & ~ GET_MODE_MASK (GET_MODE (x))); - break; -#endif - - case NEG: - if (num_sign_bit_copies (XEXP (x, 0), GET_MODE (x)) - == GET_MODE_BITSIZE (GET_MODE (x))) - nonzero = 1; - - if (GET_MODE_SIZE (GET_MODE (x)) < mode_width) - nonzero |= (GET_MODE_MASK (mode) & ~ GET_MODE_MASK (GET_MODE (x))); - break; - - case ABS: - if (num_sign_bit_copies (XEXP (x, 0), GET_MODE (x)) - == GET_MODE_BITSIZE (GET_MODE (x))) - nonzero = 1; - break; - - case TRUNCATE: - nonzero &= (nonzero_bits (XEXP (x, 0), mode) & GET_MODE_MASK (mode)); - break; - - case ZERO_EXTEND: - nonzero &= nonzero_bits (XEXP (x, 0), mode); - if (GET_MODE (XEXP (x, 0)) != VOIDmode) - nonzero &= GET_MODE_MASK (GET_MODE (XEXP (x, 0))); - break; - - case SIGN_EXTEND: - /* If the sign bit is known clear, this is the same as ZERO_EXTEND. - Otherwise, show all the bits in the outer mode but not the inner - may be non-zero. */ - inner_nz = nonzero_bits (XEXP (x, 0), mode); - if (GET_MODE (XEXP (x, 0)) != VOIDmode) - { - inner_nz &= GET_MODE_MASK (GET_MODE (XEXP (x, 0))); - if (inner_nz & - (((HOST_WIDE_INT) 1 - << (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - 1)))) - inner_nz |= (GET_MODE_MASK (mode) - & ~ GET_MODE_MASK (GET_MODE (XEXP (x, 0)))); - } - - nonzero &= inner_nz; - break; - - case AND: - nonzero &= (nonzero_bits (XEXP (x, 0), mode) - & nonzero_bits (XEXP (x, 1), mode)); - break; - - case XOR: case IOR: - case UMIN: case UMAX: case SMIN: case SMAX: - nonzero &= (nonzero_bits (XEXP (x, 0), mode) - | nonzero_bits (XEXP (x, 1), mode)); - break; - - case PLUS: case MINUS: - case MULT: - case DIV: case UDIV: - case MOD: case UMOD: - /* We can apply the rules of arithmetic to compute the number of - high- and low-order zero bits of these operations. We start by - computing the width (position of the highest-order non-zero bit) - and the number of low-order zero bits for each value. */ - { - unsigned HOST_WIDE_INT nz0 = nonzero_bits (XEXP (x, 0), mode); - unsigned HOST_WIDE_INT nz1 = nonzero_bits (XEXP (x, 1), mode); - int width0 = floor_log2 (nz0) + 1; - int width1 = floor_log2 (nz1) + 1; - int low0 = floor_log2 (nz0 & -nz0); - int low1 = floor_log2 (nz1 & -nz1); - int op0_maybe_minusp = (nz0 & ((HOST_WIDE_INT) 1 << (mode_width - 1))); - int op1_maybe_minusp = (nz1 & ((HOST_WIDE_INT) 1 << (mode_width - 1))); - int result_width = mode_width; - int result_low = 0; - - switch (code) - { - case PLUS: - result_width = MAX (width0, width1) + 1; - result_low = MIN (low0, low1); - break; - case MINUS: - result_low = MIN (low0, low1); - break; - case MULT: - result_width = width0 + width1; - result_low = low0 + low1; - break; - case DIV: - if (! op0_maybe_minusp && ! op1_maybe_minusp) - result_width = width0; - break; - case UDIV: - result_width = width0; - break; - case MOD: - if (! op0_maybe_minusp && ! op1_maybe_minusp) - result_width = MIN (width0, width1); - result_low = MIN (low0, low1); - break; - case UMOD: - result_width = MIN (width0, width1); - result_low = MIN (low0, low1); - break; - } - - if (result_width < mode_width) - nonzero &= ((HOST_WIDE_INT) 1 << result_width) - 1; - - if (result_low > 0) - nonzero &= ~ (((HOST_WIDE_INT) 1 << result_low) - 1); - } - break; - - case ZERO_EXTRACT: - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT) - nonzero &= ((HOST_WIDE_INT) 1 << INTVAL (XEXP (x, 1))) - 1; - break; - - case SUBREG: - /* If this is a SUBREG formed for a promoted variable that has - been zero-extended, we know that at least the high-order bits - are zero, though others might be too. */ - - if (SUBREG_PROMOTED_VAR_P (x) && SUBREG_PROMOTED_UNSIGNED_P (x)) - nonzero = (GET_MODE_MASK (GET_MODE (x)) - & nonzero_bits (SUBREG_REG (x), GET_MODE (x))); - - /* If the inner mode is a single word for both the host and target - machines, we can compute this from which bits of the inner - object might be nonzero. */ - if (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) <= BITS_PER_WORD - && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) - <= HOST_BITS_PER_WIDE_INT)) - { - nonzero &= nonzero_bits (SUBREG_REG (x), mode); -#ifndef BYTE_LOADS_EXTEND - /* On many CISC machines, accessing an object in a wider mode - causes the high-order bits to become undefined. So they are - not known to be zero. */ - if (GET_MODE_SIZE (GET_MODE (x)) - > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) - nonzero |= (GET_MODE_MASK (GET_MODE (x)) - & ~ GET_MODE_MASK (GET_MODE (SUBREG_REG (x)))); -#endif - } - break; - - case ASHIFTRT: - case LSHIFTRT: - case ASHIFT: - case LSHIFT: - case ROTATE: - /* The nonzero bits are in two classes: any bits within MODE - that aren't in GET_MODE (x) are always significant. The rest of the - nonzero bits are those that are significant in the operand of - the shift when shifted the appropriate number of bits. This - shows that high-order bits are cleared by the right shift and - low-order bits by left shifts. */ - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) >= 0 - && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT) - { - enum machine_mode inner_mode = GET_MODE (x); - int width = GET_MODE_BITSIZE (inner_mode); - int count = INTVAL (XEXP (x, 1)); - unsigned HOST_WIDE_INT mode_mask = GET_MODE_MASK (inner_mode); - unsigned HOST_WIDE_INT op_nonzero = nonzero_bits (XEXP (x, 0), mode); - unsigned HOST_WIDE_INT inner = op_nonzero & mode_mask; - unsigned HOST_WIDE_INT outer = 0; - - if (mode_width > width) - outer = (op_nonzero & nonzero & ~ mode_mask); - - if (code == LSHIFTRT) - inner >>= count; - else if (code == ASHIFTRT) - { - inner >>= count; - - /* If the sign bit may have been nonzero before the shift, we - need to mark all the places it could have been copied to - by the shift as possibly nonzero. */ - if (inner & ((HOST_WIDE_INT) 1 << (width - 1 - count))) - inner |= (((HOST_WIDE_INT) 1 << count) - 1) << (width - count); - } - else if (code == LSHIFT || code == ASHIFT) - inner <<= count; - else - inner = ((inner << (count % width) - | (inner >> (width - (count % width)))) & mode_mask); - - nonzero &= (outer | inner); - } - break; - - case FFS: - /* This is at most the number of bits in the mode. */ - nonzero = ((HOST_WIDE_INT) 1 << (floor_log2 (mode_width) + 1)) - 1; - break; - - case IF_THEN_ELSE: - nonzero &= (nonzero_bits (XEXP (x, 1), mode) - | nonzero_bits (XEXP (x, 2), mode)); - break; - } - - return nonzero; -} - -/* Return the number of bits at the high-order end of X that are known to - be equal to the sign bit. This number will always be between 1 and - the number of bits in the mode of X. MODE is the mode to be used - if X is VOIDmode. */ - -static int -num_sign_bit_copies (x, mode) - rtx x; - enum machine_mode mode; -{ - enum rtx_code code = GET_CODE (x); - int bitwidth; - int num0, num1, result; - unsigned HOST_WIDE_INT nonzero; - rtx tem; - - /* If we weren't given a mode, use the mode of X. If the mode is still - VOIDmode, we don't know anything. */ - - if (mode == VOIDmode) - mode = GET_MODE (x); - - if (mode == VOIDmode) - return 1; - - bitwidth = GET_MODE_BITSIZE (mode); - - switch (code) - { - case REG: - - if (reg_last_set_value[REGNO (x)] != 0 - && reg_last_set_mode[REGNO (x)] == mode - && (reg_n_sets[REGNO (x)] == 1 - || reg_last_set_label[REGNO (x)] == label_tick) - && INSN_CUID (reg_last_set[REGNO (x)]) < subst_low_cuid) - return reg_last_set_sign_bit_copies[REGNO (x)]; - - tem = get_last_value (x); - if (tem != 0) - return num_sign_bit_copies (tem, mode); - - if (nonzero_sign_valid && reg_sign_bit_copies[REGNO (x)] != 0) - return reg_sign_bit_copies[REGNO (x)]; - break; - -#ifdef BYTE_LOADS_SIGN_EXTEND - case MEM: - /* Some RISC machines sign-extend all loads of smaller than a word. */ - return MAX (1, bitwidth - GET_MODE_BITSIZE (GET_MODE (x)) + 1); -#endif - - case CONST_INT: - /* If the constant is negative, take its 1's complement and remask. - Then see how many zero bits we have. */ - nonzero = INTVAL (x) & GET_MODE_MASK (mode); - if (bitwidth <= HOST_BITS_PER_WIDE_INT - && (nonzero & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0) - nonzero = (~ nonzero) & GET_MODE_MASK (mode); - - return (nonzero == 0 ? bitwidth : bitwidth - floor_log2 (nonzero) - 1); - - case SUBREG: - /* If this is a SUBREG for a promoted object that is sign-extended - and we are looking at it in a wider mode, we know that at least the - high-order bits are known to be sign bit copies. */ - - if (SUBREG_PROMOTED_VAR_P (x) && ! SUBREG_PROMOTED_UNSIGNED_P (x)) - return MAX (bitwidth - GET_MODE_BITSIZE (GET_MODE (x)) + 1, - num_sign_bit_copies (SUBREG_REG (x), mode)); - - /* For a smaller object, just ignore the high bits. */ - if (bitwidth <= GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x)))) - { - num0 = num_sign_bit_copies (SUBREG_REG (x), VOIDmode); - return MAX (1, (num0 - - (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) - - bitwidth))); - } - -#ifdef BYTE_LOADS_EXTEND - /* For paradoxical SUBREGs, just look inside since, on machines with - one of these defined, we assume that operations are actually - performed on the full register. Note that we are passing MODE - to the recursive call, so the number of sign bit copies will - remain relative to that mode, not the inner mode. */ - - if (GET_MODE_SIZE (GET_MODE (x)) - > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) - return num_sign_bit_copies (SUBREG_REG (x), mode); -#endif - - break; - - case SIGN_EXTRACT: - if (GET_CODE (XEXP (x, 1)) == CONST_INT) - return MAX (1, bitwidth - INTVAL (XEXP (x, 1))); - break; - - case SIGN_EXTEND: - return (bitwidth - GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - + num_sign_bit_copies (XEXP (x, 0), VOIDmode)); - - case TRUNCATE: - /* For a smaller object, just ignore the high bits. */ - num0 = num_sign_bit_copies (XEXP (x, 0), VOIDmode); - return MAX (1, (num0 - (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - - bitwidth))); - - case NOT: - return num_sign_bit_copies (XEXP (x, 0), mode); - - case ROTATE: case ROTATERT: - /* If we are rotating left by a number of bits less than the number - of sign bit copies, we can just subtract that amount from the - number. */ - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) >= 0 && INTVAL (XEXP (x, 1)) < bitwidth) - { - num0 = num_sign_bit_copies (XEXP (x, 0), mode); - return MAX (1, num0 - (code == ROTATE ? INTVAL (XEXP (x, 1)) - : bitwidth - INTVAL (XEXP (x, 1)))); - } - break; - - case NEG: - /* In general, this subtracts one sign bit copy. But if the value - is known to be positive, the number of sign bit copies is the - same as that of the input. Finally, if the input has just one bit - that might be nonzero, all the bits are copies of the sign bit. */ - nonzero = nonzero_bits (XEXP (x, 0), mode); - if (nonzero == 1) - return bitwidth; - - num0 = num_sign_bit_copies (XEXP (x, 0), mode); - if (num0 > 1 - && bitwidth <= HOST_BITS_PER_WIDE_INT - && (((HOST_WIDE_INT) 1 << (bitwidth - 1)) & nonzero)) - num0--; - - return num0; - - case IOR: case AND: case XOR: - case SMIN: case SMAX: case UMIN: case UMAX: - /* Logical operations will preserve the number of sign-bit copies. - MIN and MAX operations always return one of the operands. */ - num0 = num_sign_bit_copies (XEXP (x, 0), mode); - num1 = num_sign_bit_copies (XEXP (x, 1), mode); - return MIN (num0, num1); - - case PLUS: case MINUS: - /* For addition and subtraction, we can have a 1-bit carry. However, - if we are subtracting 1 from a positive number, there will not - be such a carry. Furthermore, if the positive number is known to - be 0 or 1, we know the result is either -1 or 0. */ - - if (code == PLUS && XEXP (x, 1) == constm1_rtx - && bitwidth <= HOST_BITS_PER_WIDE_INT) - { - nonzero = nonzero_bits (XEXP (x, 0), mode); - if ((((HOST_WIDE_INT) 1 << (bitwidth - 1)) & nonzero) == 0) - return (nonzero == 1 || nonzero == 0 ? bitwidth - : bitwidth - floor_log2 (nonzero) - 1); - } - - num0 = num_sign_bit_copies (XEXP (x, 0), mode); - num1 = num_sign_bit_copies (XEXP (x, 1), mode); - return MAX (1, MIN (num0, num1) - 1); - - case MULT: - /* The number of bits of the product is the sum of the number of - bits of both terms. However, unless one of the terms if known - to be positive, we must allow for an additional bit since negating - a negative number can remove one sign bit copy. */ - - num0 = num_sign_bit_copies (XEXP (x, 0), mode); - num1 = num_sign_bit_copies (XEXP (x, 1), mode); - - result = bitwidth - (bitwidth - num0) - (bitwidth - num1); - if (result > 0 - && bitwidth <= HOST_BITS_PER_WIDE_INT - && ((nonzero_bits (XEXP (x, 0), mode) - & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0) - && (nonzero_bits (XEXP (x, 1), mode) - & ((HOST_WIDE_INT) 1 << (bitwidth - 1)) != 0)) - result--; - - return MAX (1, result); - - case UDIV: - /* The result must be <= the first operand. */ - return num_sign_bit_copies (XEXP (x, 0), mode); - - case UMOD: - /* The result must be <= the scond operand. */ - return num_sign_bit_copies (XEXP (x, 1), mode); - - case DIV: - /* Similar to unsigned division, except that we have to worry about - the case where the divisor is negative, in which case we have - to add 1. */ - result = num_sign_bit_copies (XEXP (x, 0), mode); - if (result > 1 - && bitwidth <= HOST_BITS_PER_WIDE_INT - && (nonzero_bits (XEXP (x, 1), mode) - & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0) - result --; - - return result; - - case MOD: - result = num_sign_bit_copies (XEXP (x, 1), mode); - if (result > 1 - && bitwidth <= HOST_BITS_PER_WIDE_INT - && (nonzero_bits (XEXP (x, 1), mode) - & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0) - result --; - - return result; - - case ASHIFTRT: - /* Shifts by a constant add to the number of bits equal to the - sign bit. */ - num0 = num_sign_bit_copies (XEXP (x, 0), mode); - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) > 0) - num0 = MIN (bitwidth, num0 + INTVAL (XEXP (x, 1))); - - return num0; - - case ASHIFT: - case LSHIFT: - /* Left shifts destroy copies. */ - if (GET_CODE (XEXP (x, 1)) != CONST_INT - || INTVAL (XEXP (x, 1)) < 0 - || INTVAL (XEXP (x, 1)) >= bitwidth) - return 1; - - num0 = num_sign_bit_copies (XEXP (x, 0), mode); - return MAX (1, num0 - INTVAL (XEXP (x, 1))); - - case IF_THEN_ELSE: - num0 = num_sign_bit_copies (XEXP (x, 1), mode); - num1 = num_sign_bit_copies (XEXP (x, 2), mode); - return MIN (num0, num1); - -#if STORE_FLAG_VALUE == -1 - case EQ: case NE: case GE: case GT: case LE: case LT: - case GEU: case GTU: case LEU: case LTU: - return bitwidth; -#endif - } - - /* If we haven't been able to figure it out by one of the above rules, - see if some of the high-order bits are known to be zero. If so, - count those bits and return one less than that amount. If we can't - safely compute the mask for this mode, always return BITWIDTH. */ - - if (bitwidth > HOST_BITS_PER_WIDE_INT) - return 1; - - nonzero = nonzero_bits (x, mode); - return (nonzero == GET_MODE_MASK (mode) - ? 1 : bitwidth - floor_log2 (nonzero) - 1); -} - -/* Return the number of "extended" bits there are in X, when interpreted - as a quantity in MODE whose signedness is indicated by UNSIGNEDP. For - unsigned quantities, this is the number of high-order zero bits. - For signed quantities, this is the number of copies of the sign bit - minus 1. In both case, this function returns the number of "spare" - bits. For example, if two quantities for which this function returns - at least 1 are added, the addition is known not to overflow. - - This function will always return 0 unless called during combine, which - implies that it must be called from a define_split. */ - -int -extended_count (x, mode, unsignedp) - rtx x; - enum machine_mode mode; - int unsignedp; -{ - if (nonzero_sign_valid == 0) - return 0; - - return (unsignedp - ? (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT - && (GET_MODE_BITSIZE (mode) - 1 - - floor_log2 (nonzero_bits (x, mode)))) - : num_sign_bit_copies (x, mode) - 1); -} - -/* This function is called from `simplify_shift_const' to merge two - outer operations. Specifically, we have already found that we need - to perform operation *POP0 with constant *PCONST0 at the outermost - position. We would now like to also perform OP1 with constant CONST1 - (with *POP0 being done last). - - Return 1 if we can do the operation and update *POP0 and *PCONST0 with - the resulting operation. *PCOMP_P is set to 1 if we would need to - complement the innermost operand, otherwise it is unchanged. - - MODE is the mode in which the operation will be done. No bits outside - the width of this mode matter. It is assumed that the width of this mode - is smaller than or equal to HOST_BITS_PER_WIDE_INT. - - If *POP0 or OP1 are NIL, it means no operation is required. Only NEG, PLUS, - IOR, XOR, and AND are supported. We may set *POP0 to SET if the proper - result is simply *PCONST0. - - If the resulting operation cannot be expressed as one operation, we - return 0 and do not change *POP0, *PCONST0, and *PCOMP_P. */ - -static int -merge_outer_ops (pop0, pconst0, op1, const1, mode, pcomp_p) - enum rtx_code *pop0; - HOST_WIDE_INT *pconst0; - enum rtx_code op1; - HOST_WIDE_INT const1; - enum machine_mode mode; - int *pcomp_p; -{ - enum rtx_code op0 = *pop0; - HOST_WIDE_INT const0 = *pconst0; - - const0 &= GET_MODE_MASK (mode); - const1 &= GET_MODE_MASK (mode); - - /* If OP0 is an AND, clear unimportant bits in CONST1. */ - if (op0 == AND) - const1 &= const0; - - /* If OP0 or OP1 is NIL, this is easy. Similarly if they are the same or - if OP0 is SET. */ - - if (op1 == NIL || op0 == SET) - return 1; - - else if (op0 == NIL) - op0 = op1, const0 = const1; - - else if (op0 == op1) - { - switch (op0) - { - case AND: - const0 &= const1; - break; - case IOR: - const0 |= const1; - break; - case XOR: - const0 ^= const1; - break; - case PLUS: - const0 += const1; - break; - case NEG: - op0 = NIL; - break; - } - } - - /* Otherwise, if either is a PLUS or NEG, we can't do anything. */ - else if (op0 == PLUS || op1 == PLUS || op0 == NEG || op1 == NEG) - return 0; - - /* If the two constants aren't the same, we can't do anything. The - remaining six cases can all be done. */ - else if (const0 != const1) - return 0; - - else - switch (op0) - { - case IOR: - if (op1 == AND) - /* (a & b) | b == b */ - op0 = SET; - else /* op1 == XOR */ - /* (a ^ b) | b == a | b */ - ; - break; - - case XOR: - if (op1 == AND) - /* (a & b) ^ b == (~a) & b */ - op0 = AND, *pcomp_p = 1; - else /* op1 == IOR */ - /* (a | b) ^ b == a & ~b */ - op0 = AND, *pconst0 = ~ const0; - break; - - case AND: - if (op1 == IOR) - /* (a | b) & b == b */ - op0 = SET; - else /* op1 == XOR */ - /* (a ^ b) & b) == (~a) & b */ - *pcomp_p = 1; - break; - } - - /* Check for NO-OP cases. */ - const0 &= GET_MODE_MASK (mode); - if (const0 == 0 - && (op0 == IOR || op0 == XOR || op0 == PLUS)) - op0 = NIL; - else if (const0 == 0 && op0 == AND) - op0 = SET; - else if (const0 == GET_MODE_MASK (mode) && op0 == AND) - op0 = NIL; - - *pop0 = op0; - *pconst0 = const0; - - return 1; -} - -/* Simplify a shift of VAROP by COUNT bits. CODE says what kind of shift. - The result of the shift is RESULT_MODE. X, if non-zero, is an expression - that we started with. - - The shift is normally computed in the widest mode we find in VAROP, as - long as it isn't a different number of words than RESULT_MODE. Exceptions - are ASHIFTRT and ROTATE, which are always done in their original mode, */ - -static rtx -simplify_shift_const (x, code, result_mode, varop, count) - rtx x; - enum rtx_code code; - enum machine_mode result_mode; - rtx varop; - int count; -{ - enum rtx_code orig_code = code; - int orig_count = count; - enum machine_mode mode = result_mode; - enum machine_mode shift_mode, tmode; - int mode_words - = (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD; - /* We form (outer_op (code varop count) (outer_const)). */ - enum rtx_code outer_op = NIL; - HOST_WIDE_INT outer_const; - rtx const_rtx; - int complement_p = 0; - rtx new; - - /* If we were given an invalid count, don't do anything except exactly - what was requested. */ - - if (count < 0 || count > GET_MODE_BITSIZE (mode)) - { - if (x) - return x; - - return gen_rtx (code, mode, varop, GEN_INT (count)); - } - - /* Unless one of the branches of the `if' in this loop does a `continue', - we will `break' the loop after the `if'. */ - - while (count != 0) - { - /* If we have an operand of (clobber (const_int 0)), just return that - value. */ - if (GET_CODE (varop) == CLOBBER) - return varop; - - /* If we discovered we had to complement VAROP, leave. Making a NOT - here would cause an infinite loop. */ - if (complement_p) - break; - - /* Convert ROTATETRT to ROTATE. */ - if (code == ROTATERT) - code = ROTATE, count = GET_MODE_BITSIZE (result_mode) - count; - - /* Canonicalize LSHIFT to ASHIFT. */ - if (code == LSHIFT) - code = ASHIFT; - - /* We need to determine what mode we will do the shift in. If the - shift is a ASHIFTRT or ROTATE, we must always do it in the mode it - was originally done in. Otherwise, we can do it in MODE, the widest - mode encountered. */ - shift_mode = (code == ASHIFTRT || code == ROTATE ? result_mode : mode); - - /* Handle cases where the count is greater than the size of the mode - minus 1. For ASHIFT, use the size minus one as the count (this can - occur when simplifying (lshiftrt (ashiftrt ..))). For rotates, - take the count modulo the size. For other shifts, the result is - zero. - - Since these shifts are being produced by the compiler by combining - multiple operations, each of which are defined, we know what the - result is supposed to be. */ - - if (count > GET_MODE_BITSIZE (shift_mode) - 1) - { - if (code == ASHIFTRT) - count = GET_MODE_BITSIZE (shift_mode) - 1; - else if (code == ROTATE || code == ROTATERT) - count %= GET_MODE_BITSIZE (shift_mode); - else - { - /* We can't simply return zero because there may be an - outer op. */ - varop = const0_rtx; - count = 0; - break; - } - } - - /* Negative counts are invalid and should not have been made (a - programmer-specified negative count should have been handled - above). */ - else if (count < 0) - abort (); - - /* An arithmetic right shift of a quantity known to be -1 or 0 - is a no-op. */ - if (code == ASHIFTRT - && (num_sign_bit_copies (varop, shift_mode) - == GET_MODE_BITSIZE (shift_mode))) - { - count = 0; - break; - } - - /* If we are doing an arithmetic right shift and discarding all but - the sign bit copies, this is equivalent to doing a shift by the - bitsize minus one. Convert it into that shift because it will often - allow other simplifications. */ - - if (code == ASHIFTRT - && (count + num_sign_bit_copies (varop, shift_mode) - >= GET_MODE_BITSIZE (shift_mode))) - count = GET_MODE_BITSIZE (shift_mode) - 1; - - /* We simplify the tests below and elsewhere by converting - ASHIFTRT to LSHIFTRT if we know the sign bit is clear. - `make_compound_operation' will convert it to a ASHIFTRT for - those machines (such as Vax) that don't have a LSHIFTRT. */ - if (GET_MODE_BITSIZE (shift_mode) <= HOST_BITS_PER_WIDE_INT - && code == ASHIFTRT - && ((nonzero_bits (varop, shift_mode) - & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (shift_mode) - 1))) - == 0)) - code = LSHIFTRT; - - switch (GET_CODE (varop)) - { - case SIGN_EXTEND: - case ZERO_EXTEND: - case SIGN_EXTRACT: - case ZERO_EXTRACT: - new = expand_compound_operation (varop); - if (new != varop) - { - varop = new; - continue; - } - break; - - case MEM: - /* If we have (xshiftrt (mem ...) C) and C is MODE_WIDTH - minus the width of a smaller mode, we can do this with a - SIGN_EXTEND or ZERO_EXTEND from the narrower memory location. */ - if ((code == ASHIFTRT || code == LSHIFTRT) - && ! mode_dependent_address_p (XEXP (varop, 0)) - && ! MEM_VOLATILE_P (varop) - && (tmode = mode_for_size (GET_MODE_BITSIZE (mode) - count, - MODE_INT, 1)) != BLKmode) - { -#if BYTES_BIG_ENDIAN - new = gen_rtx (MEM, tmode, XEXP (varop, 0)); -#else - new = gen_rtx (MEM, tmode, - plus_constant (XEXP (varop, 0), - count / BITS_PER_UNIT)); - RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (varop); - MEM_VOLATILE_P (new) = MEM_VOLATILE_P (varop); - MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (varop); -#endif - varop = gen_rtx_combine (code == ASHIFTRT ? SIGN_EXTEND - : ZERO_EXTEND, mode, new); - count = 0; - continue; - } - break; - - case USE: - /* Similar to the case above, except that we can only do this if - the resulting mode is the same as that of the underlying - MEM and adjust the address depending on the *bits* endianness - because of the way that bit-field extract insns are defined. */ - if ((code == ASHIFTRT || code == LSHIFTRT) - && (tmode = mode_for_size (GET_MODE_BITSIZE (mode) - count, - MODE_INT, 1)) != BLKmode - && tmode == GET_MODE (XEXP (varop, 0))) - { -#if BITS_BIG_ENDIAN - new = XEXP (varop, 0); -#else - new = copy_rtx (XEXP (varop, 0)); - SUBST (XEXP (new, 0), - plus_constant (XEXP (new, 0), - count / BITS_PER_UNIT)); -#endif - - varop = gen_rtx_combine (code == ASHIFTRT ? SIGN_EXTEND - : ZERO_EXTEND, mode, new); - count = 0; - continue; - } - break; - - case SUBREG: - /* If VAROP is a SUBREG, strip it as long as the inner operand has - the same number of words as what we've seen so far. Then store - the widest mode in MODE. */ - if (subreg_lowpart_p (varop) - && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (varop))) - > GET_MODE_SIZE (GET_MODE (varop))) - && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (varop))) - + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD) - == mode_words)) - { - varop = SUBREG_REG (varop); - if (GET_MODE_SIZE (GET_MODE (varop)) > GET_MODE_SIZE (mode)) - mode = GET_MODE (varop); - continue; - } - break; - - case MULT: - /* Some machines use MULT instead of ASHIFT because MULT - is cheaper. But it is still better on those machines to - merge two shifts into one. */ - if (GET_CODE (XEXP (varop, 1)) == CONST_INT - && exact_log2 (INTVAL (XEXP (varop, 1))) >= 0) - { - varop = gen_binary (ASHIFT, GET_MODE (varop), XEXP (varop, 0), - GEN_INT (exact_log2 (INTVAL (XEXP (varop, 1)))));; - continue; - } - break; - - case UDIV: - /* Similar, for when divides are cheaper. */ - if (GET_CODE (XEXP (varop, 1)) == CONST_INT - && exact_log2 (INTVAL (XEXP (varop, 1))) >= 0) - { - varop = gen_binary (LSHIFTRT, GET_MODE (varop), XEXP (varop, 0), - GEN_INT (exact_log2 (INTVAL (XEXP (varop, 1))))); - continue; - } - break; - - case ASHIFTRT: - /* If we are extracting just the sign bit of an arithmetic right - shift, that shift is not needed. */ - if (code == LSHIFTRT && count == GET_MODE_BITSIZE (result_mode) - 1) - { - varop = XEXP (varop, 0); - continue; - } - - /* ... fall through ... */ - - case LSHIFTRT: - case ASHIFT: - case LSHIFT: - case ROTATE: - /* Here we have two nested shifts. The result is usually the - AND of a new shift with a mask. We compute the result below. */ - if (GET_CODE (XEXP (varop, 1)) == CONST_INT - && INTVAL (XEXP (varop, 1)) >= 0 - && INTVAL (XEXP (varop, 1)) < GET_MODE_BITSIZE (GET_MODE (varop)) - && GET_MODE_BITSIZE (result_mode) <= HOST_BITS_PER_WIDE_INT - && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT) - { - enum rtx_code first_code = GET_CODE (varop); - int first_count = INTVAL (XEXP (varop, 1)); - unsigned HOST_WIDE_INT mask; - rtx mask_rtx; - rtx inner; - - if (first_code == LSHIFT) - first_code = ASHIFT; - - /* We have one common special case. We can't do any merging if - the inner code is an ASHIFTRT of a smaller mode. However, if - we have (ashift:M1 (subreg:M1 (ashiftrt:M2 FOO C1) 0) C2) - with C2 == GET_MODE_BITSIZE (M1) - GET_MODE_BITSIZE (M2), - we can convert it to - (ashiftrt:M1 (ashift:M1 (and:M1 (subreg:M1 FOO 0 C2) C3) C1). - This simplifies certain SIGN_EXTEND operations. */ - if (code == ASHIFT && first_code == ASHIFTRT - && (GET_MODE_BITSIZE (result_mode) - - GET_MODE_BITSIZE (GET_MODE (varop))) == count) - { - /* C3 has the low-order C1 bits zero. */ - - mask = (GET_MODE_MASK (mode) - & ~ (((HOST_WIDE_INT) 1 << first_count) - 1)); - - varop = simplify_and_const_int (NULL_RTX, result_mode, - XEXP (varop, 0), mask); - varop = simplify_shift_const (NULL_RTX, ASHIFT, result_mode, - varop, count); - count = first_count; - code = ASHIFTRT; - continue; - } - - /* If this was (ashiftrt (ashift foo C1) C2) and FOO has more - than C1 high-order bits equal to the sign bit, we can convert - this to either an ASHIFT or a ASHIFTRT depending on the - two counts. - - We cannot do this if VAROP's mode is not SHIFT_MODE. */ - - if (code == ASHIFTRT && first_code == ASHIFT - && GET_MODE (varop) == shift_mode - && (num_sign_bit_copies (XEXP (varop, 0), shift_mode) - > first_count)) - { - count -= first_count; - if (count < 0) - count = - count, code = ASHIFT; - varop = XEXP (varop, 0); - continue; - } - - /* There are some cases we can't do. If CODE is ASHIFTRT, - we can only do this if FIRST_CODE is also ASHIFTRT. - - We can't do the case when CODE is ROTATE and FIRST_CODE is - ASHIFTRT. - - If the mode of this shift is not the mode of the outer shift, - we can't do this if either shift is ASHIFTRT or ROTATE. - - Finally, we can't do any of these if the mode is too wide - unless the codes are the same. - - Handle the case where the shift codes are the same - first. */ - - if (code == first_code) - { - if (GET_MODE (varop) != result_mode - && (code == ASHIFTRT || code == ROTATE)) - break; - - count += first_count; - varop = XEXP (varop, 0); - continue; - } - - if (code == ASHIFTRT - || (code == ROTATE && first_code == ASHIFTRT) - || GET_MODE_BITSIZE (mode) > HOST_BITS_PER_WIDE_INT - || (GET_MODE (varop) != result_mode - && (first_code == ASHIFTRT || first_code == ROTATE - || code == ROTATE))) - break; - - /* To compute the mask to apply after the shift, shift the - nonzero bits of the inner shift the same way the - outer shift will. */ - - mask_rtx = GEN_INT (nonzero_bits (varop, GET_MODE (varop))); - - mask_rtx - = simplify_binary_operation (code, result_mode, mask_rtx, - GEN_INT (count)); - - /* Give up if we can't compute an outer operation to use. */ - if (mask_rtx == 0 - || GET_CODE (mask_rtx) != CONST_INT - || ! merge_outer_ops (&outer_op, &outer_const, AND, - INTVAL (mask_rtx), - result_mode, &complement_p)) - break; - - /* If the shifts are in the same direction, we add the - counts. Otherwise, we subtract them. */ - if ((code == ASHIFTRT || code == LSHIFTRT) - == (first_code == ASHIFTRT || first_code == LSHIFTRT)) - count += first_count; - else - count -= first_count; - - /* If COUNT is positive, the new shift is usually CODE, - except for the two exceptions below, in which case it is - FIRST_CODE. If the count is negative, FIRST_CODE should - always be used */ - if (count > 0 - && ((first_code == ROTATE && code == ASHIFT) - || (first_code == ASHIFTRT && code == LSHIFTRT))) - code = first_code; - else if (count < 0) - code = first_code, count = - count; - - varop = XEXP (varop, 0); - continue; - } - - /* If we have (A << B << C) for any shift, we can convert this to - (A << C << B). This wins if A is a constant. Only try this if - B is not a constant. */ - - else if (GET_CODE (varop) == code - && GET_CODE (XEXP (varop, 1)) != CONST_INT - && 0 != (new - = simplify_binary_operation (code, mode, - XEXP (varop, 0), - GEN_INT (count)))) - { - varop = gen_rtx_combine (code, mode, new, XEXP (varop, 1)); - count = 0; - continue; - } - break; - - case NOT: - /* Make this fit the case below. */ - varop = gen_rtx_combine (XOR, mode, XEXP (varop, 0), - GEN_INT (GET_MODE_MASK (mode))); - continue; - - case IOR: - case AND: - case XOR: - /* If we have (xshiftrt (ior (plus X (const_int -1)) X) C) - with C the size of VAROP - 1 and the shift is logical if - STORE_FLAG_VALUE is 1 and arithmetic if STORE_FLAG_VALUE is -1, - we have an (le X 0) operation. If we have an arithmetic shift - and STORE_FLAG_VALUE is 1 or we have a logical shift with - STORE_FLAG_VALUE of -1, we have a (neg (le X 0)) operation. */ - - if (GET_CODE (varop) == IOR && GET_CODE (XEXP (varop, 0)) == PLUS - && XEXP (XEXP (varop, 0), 1) == constm1_rtx - && (STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1) - && (code == LSHIFTRT || code == ASHIFTRT) - && count == GET_MODE_BITSIZE (GET_MODE (varop)) - 1 - && rtx_equal_p (XEXP (XEXP (varop, 0), 0), XEXP (varop, 1))) - { - count = 0; - varop = gen_rtx_combine (LE, GET_MODE (varop), XEXP (varop, 1), - const0_rtx); - - if (STORE_FLAG_VALUE == 1 ? code == ASHIFTRT : code == LSHIFTRT) - varop = gen_rtx_combine (NEG, GET_MODE (varop), varop); - - continue; - } - - /* If we have (shift (logical)), move the logical to the outside - to allow it to possibly combine with another logical and the - shift to combine with another shift. This also canonicalizes to - what a ZERO_EXTRACT looks like. Also, some machines have - (and (shift)) insns. */ - - if (GET_CODE (XEXP (varop, 1)) == CONST_INT - && (new = simplify_binary_operation (code, result_mode, - XEXP (varop, 1), - GEN_INT (count))) != 0 - && merge_outer_ops (&outer_op, &outer_const, GET_CODE (varop), - INTVAL (new), result_mode, &complement_p)) - { - varop = XEXP (varop, 0); - continue; - } - - /* If we can't do that, try to simplify the shift in each arm of the - logical expression, make a new logical expression, and apply - the inverse distributive law. */ - { - rtx lhs = simplify_shift_const (NULL_RTX, code, result_mode, - XEXP (varop, 0), count); - rtx rhs = simplify_shift_const (NULL_RTX, code, result_mode, - XEXP (varop, 1), count); - - varop = gen_binary (GET_CODE (varop), result_mode, lhs, rhs); - varop = apply_distributive_law (varop); - - count = 0; - } - break; - - case EQ: - /* convert (lshift (eq FOO 0) C) to (xor FOO 1) if STORE_FLAG_VALUE - says that the sign bit can be tested, FOO has mode MODE, C is - GET_MODE_BITSIZE (MODE) - 1, and FOO has only the low-order bit - may be nonzero. */ - if (code == LSHIFT - && XEXP (varop, 1) == const0_rtx - && GET_MODE (XEXP (varop, 0)) == result_mode - && count == GET_MODE_BITSIZE (result_mode) - 1 - && GET_MODE_BITSIZE (result_mode) <= HOST_BITS_PER_WIDE_INT - && ((STORE_FLAG_VALUE - & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (result_mode) - 1)))) - && nonzero_bits (XEXP (varop, 0), result_mode) == 1 - && merge_outer_ops (&outer_op, &outer_const, XOR, - (HOST_WIDE_INT) 1, result_mode, - &complement_p)) - { - varop = XEXP (varop, 0); - count = 0; - continue; - } - break; - - case NEG: - /* (lshiftrt (neg A) C) where A is either 0 or 1 and C is one less - than the number of bits in the mode is equivalent to A. */ - if (code == LSHIFTRT && count == GET_MODE_BITSIZE (result_mode) - 1 - && nonzero_bits (XEXP (varop, 0), result_mode) == 1) - { - varop = XEXP (varop, 0); - count = 0; - continue; - } - - /* NEG commutes with ASHIFT since it is multiplication. Move the - NEG outside to allow shifts to combine. */ - if (code == ASHIFT - && merge_outer_ops (&outer_op, &outer_const, NEG, - (HOST_WIDE_INT) 0, result_mode, - &complement_p)) - { - varop = XEXP (varop, 0); - continue; - } - break; - - case PLUS: - /* (lshiftrt (plus A -1) C) where A is either 0 or 1 and C - is one less than the number of bits in the mode is - equivalent to (xor A 1). */ - if (code == LSHIFTRT && count == GET_MODE_BITSIZE (result_mode) - 1 - && XEXP (varop, 1) == constm1_rtx - && nonzero_bits (XEXP (varop, 0), result_mode) == 1 - && merge_outer_ops (&outer_op, &outer_const, XOR, - (HOST_WIDE_INT) 1, result_mode, - &complement_p)) - { - count = 0; - varop = XEXP (varop, 0); - continue; - } - - /* If we have (xshiftrt (plus FOO BAR) C), and the only bits - that might be nonzero in BAR are those being shifted out and those - bits are known zero in FOO, we can replace the PLUS with FOO. - Similarly in the other operand order. This code occurs when - we are computing the size of a variable-size array. */ - - if ((code == ASHIFTRT || code == LSHIFTRT) - && count < HOST_BITS_PER_WIDE_INT - && nonzero_bits (XEXP (varop, 1), result_mode) >> count == 0 - && (nonzero_bits (XEXP (varop, 1), result_mode) - & nonzero_bits (XEXP (varop, 0), result_mode)) == 0) - { - varop = XEXP (varop, 0); - continue; - } - else if ((code == ASHIFTRT || code == LSHIFTRT) - && count < HOST_BITS_PER_WIDE_INT - && GET_MODE_BITSIZE (result_mode) <= HOST_BITS_PER_WIDE_INT - && 0 == (nonzero_bits (XEXP (varop, 0), result_mode) - >> count) - && 0 == (nonzero_bits (XEXP (varop, 0), result_mode) - & nonzero_bits (XEXP (varop, 1), - result_mode))) - { - varop = XEXP (varop, 1); - continue; - } - - /* (ashift (plus foo C) N) is (plus (ashift foo N) C'). */ - if (code == ASHIFT - && GET_CODE (XEXP (varop, 1)) == CONST_INT - && (new = simplify_binary_operation (ASHIFT, result_mode, - XEXP (varop, 1), - GEN_INT (count))) != 0 - && merge_outer_ops (&outer_op, &outer_const, PLUS, - INTVAL (new), result_mode, &complement_p)) - { - varop = XEXP (varop, 0); - continue; - } - break; - - case MINUS: - /* If we have (xshiftrt (minus (ashiftrt X C)) X) C) - with C the size of VAROP - 1 and the shift is logical if - STORE_FLAG_VALUE is 1 and arithmetic if STORE_FLAG_VALUE is -1, - we have a (gt X 0) operation. If the shift is arithmetic with - STORE_FLAG_VALUE of 1 or logical with STORE_FLAG_VALUE == -1, - we have a (neg (gt X 0)) operation. */ - - if (GET_CODE (XEXP (varop, 0)) == ASHIFTRT - && count == GET_MODE_BITSIZE (GET_MODE (varop)) - 1 - && (STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1) - && (code == LSHIFTRT || code == ASHIFTRT) - && GET_CODE (XEXP (XEXP (varop, 0), 1)) == CONST_INT - && INTVAL (XEXP (XEXP (varop, 0), 1)) == count - && rtx_equal_p (XEXP (XEXP (varop, 0), 0), XEXP (varop, 1))) - { - count = 0; - varop = gen_rtx_combine (GT, GET_MODE (varop), XEXP (varop, 1), - const0_rtx); - - if (STORE_FLAG_VALUE == 1 ? code == ASHIFTRT : code == LSHIFTRT) - varop = gen_rtx_combine (NEG, GET_MODE (varop), varop); - - continue; - } - break; - } - - break; - } - - /* We need to determine what mode to do the shift in. If the shift is - a ASHIFTRT or ROTATE, we must always do it in the mode it was originally - done in. Otherwise, we can do it in MODE, the widest mode encountered. - The code we care about is that of the shift that will actually be done, - not the shift that was originally requested. */ - shift_mode = (code == ASHIFTRT || code == ROTATE ? result_mode : mode); - - /* We have now finished analyzing the shift. The result should be - a shift of type CODE with SHIFT_MODE shifting VAROP COUNT places. If - OUTER_OP is non-NIL, it is an operation that needs to be applied - to the result of the shift. OUTER_CONST is the relevant constant, - but we must turn off all bits turned off in the shift. - - If we were passed a value for X, see if we can use any pieces of - it. If not, make new rtx. */ - - if (x && GET_RTX_CLASS (GET_CODE (x)) == '2' - && GET_CODE (XEXP (x, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) == count) - const_rtx = XEXP (x, 1); - else - const_rtx = GEN_INT (count); - - if (x && GET_CODE (XEXP (x, 0)) == SUBREG - && GET_MODE (XEXP (x, 0)) == shift_mode - && SUBREG_REG (XEXP (x, 0)) == varop) - varop = XEXP (x, 0); - else if (GET_MODE (varop) != shift_mode) - varop = gen_lowpart_for_combine (shift_mode, varop); - - /* If we can't make the SUBREG, try to return what we were given. */ - if (GET_CODE (varop) == CLOBBER) - return x ? x : varop; - - new = simplify_binary_operation (code, shift_mode, varop, const_rtx); - if (new != 0) - x = new; - else - { - if (x == 0 || GET_CODE (x) != code || GET_MODE (x) != shift_mode) - x = gen_rtx_combine (code, shift_mode, varop, const_rtx); - - SUBST (XEXP (x, 0), varop); - SUBST (XEXP (x, 1), const_rtx); - } - - /* If we were doing a LSHIFTRT in a wider mode than it was originally, - turn off all the bits that the shift would have turned off. */ - if (orig_code == LSHIFTRT && result_mode != shift_mode) - x = simplify_and_const_int (NULL_RTX, shift_mode, x, - GET_MODE_MASK (result_mode) >> orig_count); - - /* Do the remainder of the processing in RESULT_MODE. */ - x = gen_lowpart_for_combine (result_mode, x); - - /* If COMPLEMENT_P is set, we have to complement X before doing the outer - operation. */ - if (complement_p) - x = gen_unary (NOT, result_mode, x); - - if (outer_op != NIL) - { - if (GET_MODE_BITSIZE (result_mode) < HOST_BITS_PER_WIDE_INT) - outer_const &= GET_MODE_MASK (result_mode); - - if (outer_op == AND) - x = simplify_and_const_int (NULL_RTX, result_mode, x, outer_const); - else if (outer_op == SET) - /* This means that we have determined that the result is - equivalent to a constant. This should be rare. */ - x = GEN_INT (outer_const); - else if (GET_RTX_CLASS (outer_op) == '1') - x = gen_unary (outer_op, result_mode, x); - else - x = gen_binary (outer_op, result_mode, x, GEN_INT (outer_const)); - } - - return x; -} - -/* Like recog, but we receive the address of a pointer to a new pattern. - We try to match the rtx that the pointer points to. - If that fails, we may try to modify or replace the pattern, - storing the replacement into the same pointer object. - - Modifications include deletion or addition of CLOBBERs. - - PNOTES is a pointer to a location where any REG_UNUSED notes added for - the CLOBBERs are placed. - - The value is the final insn code from the pattern ultimately matched, - or -1. */ - -static int -recog_for_combine (pnewpat, insn, pnotes) - rtx *pnewpat; - rtx insn; - rtx *pnotes; -{ - register rtx pat = *pnewpat; - int insn_code_number; - int num_clobbers_to_add = 0; - int i; - rtx notes = 0; - - /* Is the result of combination a valid instruction? */ - insn_code_number = recog (pat, insn, &num_clobbers_to_add); - - /* If it isn't, there is the possibility that we previously had an insn - that clobbered some register as a side effect, but the combined - insn doesn't need to do that. So try once more without the clobbers - unless this represents an ASM insn. */ - - if (insn_code_number < 0 && ! check_asm_operands (pat) - && GET_CODE (pat) == PARALLEL) - { - int pos; - - for (pos = 0, i = 0; i < XVECLEN (pat, 0); i++) - if (GET_CODE (XVECEXP (pat, 0, i)) != CLOBBER) - { - if (i != pos) - SUBST (XVECEXP (pat, 0, pos), XVECEXP (pat, 0, i)); - pos++; - } - - SUBST_INT (XVECLEN (pat, 0), pos); - - if (pos == 1) - pat = XVECEXP (pat, 0, 0); - - insn_code_number = recog (pat, insn, &num_clobbers_to_add); - } - - /* If we had any clobbers to add, make a new pattern than contains - them. Then check to make sure that all of them are dead. */ - if (num_clobbers_to_add) - { - rtx newpat = gen_rtx (PARALLEL, VOIDmode, - gen_rtvec (GET_CODE (pat) == PARALLEL - ? XVECLEN (pat, 0) + num_clobbers_to_add - : num_clobbers_to_add + 1)); - - if (GET_CODE (pat) == PARALLEL) - for (i = 0; i < XVECLEN (pat, 0); i++) - XVECEXP (newpat, 0, i) = XVECEXP (pat, 0, i); - else - XVECEXP (newpat, 0, 0) = pat; - - add_clobbers (newpat, insn_code_number); - - for (i = XVECLEN (newpat, 0) - num_clobbers_to_add; - i < XVECLEN (newpat, 0); i++) - { - if (GET_CODE (XEXP (XVECEXP (newpat, 0, i), 0)) == REG - && ! reg_dead_at_p (XEXP (XVECEXP (newpat, 0, i), 0), insn)) - return -1; - notes = gen_rtx (EXPR_LIST, REG_UNUSED, - XEXP (XVECEXP (newpat, 0, i), 0), notes); - } - pat = newpat; - } - - *pnewpat = pat; - *pnotes = notes; - - return insn_code_number; -} - -/* Like gen_lowpart but for use by combine. In combine it is not possible - to create any new pseudoregs. However, it is safe to create - invalid memory addresses, because combine will try to recognize - them and all they will do is make the combine attempt fail. - - If for some reason this cannot do its job, an rtx - (clobber (const_int 0)) is returned. - An insn containing that will not be recognized. */ - -#undef gen_lowpart - -static rtx -gen_lowpart_for_combine (mode, x) - enum machine_mode mode; - register rtx x; -{ - rtx result; - - if (GET_MODE (x) == mode) - return x; - - /* We can only support MODE being wider than a word if X is a - constant integer or has a mode the same size. */ - - if (GET_MODE_SIZE (mode) > UNITS_PER_WORD - && ! ((GET_MODE (x) == VOIDmode - && (GET_CODE (x) == CONST_INT - || GET_CODE (x) == CONST_DOUBLE)) - || GET_MODE_SIZE (GET_MODE (x)) == GET_MODE_SIZE (mode))) - return gen_rtx (CLOBBER, GET_MODE (x), const0_rtx); - - /* X might be a paradoxical (subreg (mem)). In that case, gen_lowpart - won't know what to do. So we will strip off the SUBREG here and - process normally. */ - if (GET_CODE (x) == SUBREG && GET_CODE (SUBREG_REG (x)) == MEM) - { - x = SUBREG_REG (x); - if (GET_MODE (x) == mode) - return x; - } - - result = gen_lowpart_common (mode, x); - if (result) - return result; - - if (GET_CODE (x) == MEM) - { - register int offset = 0; - rtx new; - - /* Refuse to work on a volatile memory ref or one with a mode-dependent - address. */ - if (MEM_VOLATILE_P (x) || mode_dependent_address_p (XEXP (x, 0))) - return gen_rtx (CLOBBER, GET_MODE (x), const0_rtx); - - /* If we want to refer to something bigger than the original memref, - generate a perverse subreg instead. That will force a reload - of the original memref X. */ - if (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (mode)) - return gen_rtx (SUBREG, mode, x, 0); - -#if WORDS_BIG_ENDIAN - offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD) - - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD)); -#endif -#if BYTES_BIG_ENDIAN - /* Adjust the address so that the address-after-the-data - is unchanged. */ - offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)) - - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x)))); -#endif - new = gen_rtx (MEM, mode, plus_constant (XEXP (x, 0), offset)); - RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (x); - MEM_VOLATILE_P (new) = MEM_VOLATILE_P (x); - MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (x); - return new; - } - - /* If X is a comparison operator, rewrite it in a new mode. This - probably won't match, but may allow further simplifications. */ - else if (GET_RTX_CLASS (GET_CODE (x)) == '<') - return gen_rtx_combine (GET_CODE (x), mode, XEXP (x, 0), XEXP (x, 1)); - - /* If we couldn't simplify X any other way, just enclose it in a - SUBREG. Normally, this SUBREG won't match, but some patterns may - include an explicit SUBREG or we may simplify it further in combine. */ - else - { - int word = 0; - - if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD) - word = ((GET_MODE_SIZE (GET_MODE (x)) - - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD)) - / UNITS_PER_WORD); - return gen_rtx (SUBREG, mode, x, word); - } -} - -/* Make an rtx expression. This is a subset of gen_rtx and only supports - expressions of 1, 2, or 3 operands, each of which are rtx expressions. - - If the identical expression was previously in the insn (in the undobuf), - it will be returned. Only if it is not found will a new expression - be made. */ - -/*VARARGS2*/ -static rtx -gen_rtx_combine (va_alist) - va_dcl -{ - va_list p; - enum rtx_code code; - enum machine_mode mode; - int n_args; - rtx args[3]; - int i, j; - char *fmt; - rtx rt; - - va_start (p); - code = va_arg (p, enum rtx_code); - mode = va_arg (p, enum machine_mode); - n_args = GET_RTX_LENGTH (code); - fmt = GET_RTX_FORMAT (code); - - if (n_args == 0 || n_args > 3) - abort (); - - /* Get each arg and verify that it is supposed to be an expression. */ - for (j = 0; j < n_args; j++) - { - if (*fmt++ != 'e') - abort (); - - args[j] = va_arg (p, rtx); - } - - /* See if this is in undobuf. Be sure we don't use objects that came - from another insn; this could produce circular rtl structures. */ - - for (i = previous_num_undos; i < undobuf.num_undo; i++) - if (!undobuf.undo[i].is_int - && GET_CODE (undobuf.undo[i].old_contents.rtx) == code - && GET_MODE (undobuf.undo[i].old_contents.rtx) == mode) - { - for (j = 0; j < n_args; j++) - if (XEXP (undobuf.undo[i].old_contents.rtx, j) != args[j]) - break; - - if (j == n_args) - return undobuf.undo[i].old_contents.rtx; - } - - /* Otherwise make a new rtx. We know we have 1, 2, or 3 args. - Use rtx_alloc instead of gen_rtx because it's faster on RISC. */ - rt = rtx_alloc (code); - PUT_MODE (rt, mode); - XEXP (rt, 0) = args[0]; - if (n_args > 1) - { - XEXP (rt, 1) = args[1]; - if (n_args > 2) - XEXP (rt, 2) = args[2]; - } - return rt; -} - -/* These routines make binary and unary operations by first seeing if they - fold; if not, a new expression is allocated. */ - -static rtx -gen_binary (code, mode, op0, op1) - enum rtx_code code; - enum machine_mode mode; - rtx op0, op1; -{ - rtx result; - rtx tem; - - if (GET_RTX_CLASS (code) == 'c' - && (GET_CODE (op0) == CONST_INT - || (CONSTANT_P (op0) && GET_CODE (op1) != CONST_INT))) - tem = op0, op0 = op1, op1 = tem; - - if (GET_RTX_CLASS (code) == '<') - { - enum machine_mode op_mode = GET_MODE (op0); - if (op_mode == VOIDmode) - op_mode = GET_MODE (op1); - result = simplify_relational_operation (code, op_mode, op0, op1); - } - else - result = simplify_binary_operation (code, mode, op0, op1); - - if (result) - return result; - - /* Put complex operands first and constants second. */ - if (GET_RTX_CLASS (code) == 'c' - && ((CONSTANT_P (op0) && GET_CODE (op1) != CONST_INT) - || (GET_RTX_CLASS (GET_CODE (op0)) == 'o' - && GET_RTX_CLASS (GET_CODE (op1)) != 'o') - || (GET_CODE (op0) == SUBREG - && GET_RTX_CLASS (GET_CODE (SUBREG_REG (op0))) == 'o' - && GET_RTX_CLASS (GET_CODE (op1)) != 'o'))) - return gen_rtx_combine (code, mode, op1, op0); - - return gen_rtx_combine (code, mode, op0, op1); -} - -static rtx -gen_unary (code, mode, op0) - enum rtx_code code; - enum machine_mode mode; - rtx op0; -{ - rtx result = simplify_unary_operation (code, mode, op0, mode); - - if (result) - return result; - - return gen_rtx_combine (code, mode, op0); -} - -/* Simplify a comparison between *POP0 and *POP1 where CODE is the - comparison code that will be tested. - - The result is a possibly different comparison code to use. *POP0 and - *POP1 may be updated. - - It is possible that we might detect that a comparison is either always - true or always false. However, we do not perform general constant - folding in combine, so this knowledge isn't useful. Such tautologies - should have been detected earlier. Hence we ignore all such cases. */ - -static enum rtx_code -simplify_comparison (code, pop0, pop1) - enum rtx_code code; - rtx *pop0; - rtx *pop1; -{ - rtx op0 = *pop0; - rtx op1 = *pop1; - rtx tem, tem1; - int i; - enum machine_mode mode, tmode; - - /* Try a few ways of applying the same transformation to both operands. */ - while (1) - { - /* If both operands are the same constant shift, see if we can ignore the - shift. We can if the shift is a rotate or if the bits shifted out of - this shift are known to be zero for both inputs and if the type of - comparison is compatible with the shift. */ - if (GET_CODE (op0) == GET_CODE (op1) - && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT - && ((GET_CODE (op0) == ROTATE && (code == NE || code == EQ)) - || ((GET_CODE (op0) == LSHIFTRT - || GET_CODE (op0) == ASHIFT || GET_CODE (op0) == LSHIFT) - && (code != GT && code != LT && code != GE && code != LE)) - || (GET_CODE (op0) == ASHIFTRT - && (code != GTU && code != LTU - && code != GEU && code != GEU))) - && GET_CODE (XEXP (op0, 1)) == CONST_INT - && INTVAL (XEXP (op0, 1)) >= 0 - && INTVAL (XEXP (op0, 1)) < HOST_BITS_PER_WIDE_INT - && XEXP (op0, 1) == XEXP (op1, 1)) - { - enum machine_mode mode = GET_MODE (op0); - unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode); - int shift_count = INTVAL (XEXP (op0, 1)); - - if (GET_CODE (op0) == LSHIFTRT || GET_CODE (op0) == ASHIFTRT) - mask &= (mask >> shift_count) << shift_count; - else if (GET_CODE (op0) == ASHIFT || GET_CODE (op0) == LSHIFT) - mask = (mask & (mask << shift_count)) >> shift_count; - - if ((nonzero_bits (XEXP (op0, 0), mode) & ~ mask) == 0 - && (nonzero_bits (XEXP (op1, 0), mode) & ~ mask) == 0) - op0 = XEXP (op0, 0), op1 = XEXP (op1, 0); - else - break; - } - - /* If both operands are AND's of a paradoxical SUBREG by constant, the - SUBREGs are of the same mode, and, in both cases, the AND would - be redundant if the comparison was done in the narrower mode, - do the comparison in the narrower mode (e.g., we are AND'ing with 1 - and the operand's possibly nonzero bits are 0xffffff01; in that case - if we only care about QImode, we don't need the AND). This case - occurs if the output mode of an scc insn is not SImode and - STORE_FLAG_VALUE == 1 (e.g., the 386). */ - - else if (GET_CODE (op0) == AND && GET_CODE (op1) == AND - && GET_CODE (XEXP (op0, 1)) == CONST_INT - && GET_CODE (XEXP (op1, 1)) == CONST_INT - && GET_CODE (XEXP (op0, 0)) == SUBREG - && GET_CODE (XEXP (op1, 0)) == SUBREG - && (GET_MODE_SIZE (GET_MODE (XEXP (op0, 0))) - > GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (op0, 0))))) - && (GET_MODE (SUBREG_REG (XEXP (op0, 0))) - == GET_MODE (SUBREG_REG (XEXP (op1, 0)))) - && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (XEXP (op0, 0)))) - <= HOST_BITS_PER_WIDE_INT) - && (nonzero_bits (SUBREG_REG (XEXP (op0, 0)), - GET_MODE (SUBREG_REG (XEXP (op0, 0)))) - & ~ INTVAL (XEXP (op0, 1))) == 0 - && (nonzero_bits (SUBREG_REG (XEXP (op1, 0)), - GET_MODE (SUBREG_REG (XEXP (op1, 0)))) - & ~ INTVAL (XEXP (op1, 1))) == 0) - { - op0 = SUBREG_REG (XEXP (op0, 0)); - op1 = SUBREG_REG (XEXP (op1, 0)); - - /* the resulting comparison is always unsigned since we masked off - the original sign bit. */ - code = unsigned_condition (code); - } - else - break; - } - - /* If the first operand is a constant, swap the operands and adjust the - comparison code appropriately. */ - if (CONSTANT_P (op0)) - { - tem = op0, op0 = op1, op1 = tem; - code = swap_condition (code); - } - - /* We now enter a loop during which we will try to simplify the comparison. - For the most part, we only are concerned with comparisons with zero, - but some things may really be comparisons with zero but not start - out looking that way. */ - - while (GET_CODE (op1) == CONST_INT) - { - enum machine_mode mode = GET_MODE (op0); - int mode_width = GET_MODE_BITSIZE (mode); - unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode); - int equality_comparison_p; - int sign_bit_comparison_p; - int unsigned_comparison_p; - HOST_WIDE_INT const_op; - - /* We only want to handle integral modes. This catches VOIDmode, - CCmode, and the floating-point modes. An exception is that we - can handle VOIDmode if OP0 is a COMPARE or a comparison - operation. */ - - if (GET_MODE_CLASS (mode) != MODE_INT - && ! (mode == VOIDmode - && (GET_CODE (op0) == COMPARE - || GET_RTX_CLASS (GET_CODE (op0)) == '<'))) - break; - - /* Get the constant we are comparing against and turn off all bits - not on in our mode. */ - const_op = INTVAL (op1); - if (mode_width <= HOST_BITS_PER_WIDE_INT) - const_op &= mask; - - /* If we are comparing against a constant power of two and the value - being compared can only have that single bit nonzero (e.g., it was - `and'ed with that bit), we can replace this with a comparison - with zero. */ - if (const_op - && (code == EQ || code == NE || code == GE || code == GEU - || code == LT || code == LTU) - && mode_width <= HOST_BITS_PER_WIDE_INT - && exact_log2 (const_op) >= 0 - && nonzero_bits (op0, mode) == const_op) - { - code = (code == EQ || code == GE || code == GEU ? NE : EQ); - op1 = const0_rtx, const_op = 0; - } - - /* Similarly, if we are comparing a value known to be either -1 or - 0 with -1, change it to the opposite comparison against zero. */ - - if (const_op == -1 - && (code == EQ || code == NE || code == GT || code == LE - || code == GEU || code == LTU) - && num_sign_bit_copies (op0, mode) == mode_width) - { - code = (code == EQ || code == LE || code == GEU ? NE : EQ); - op1 = const0_rtx, const_op = 0; - } - - /* Do some canonicalizations based on the comparison code. We prefer - comparisons against zero and then prefer equality comparisons. - If we can reduce the size of a constant, we will do that too. */ - - switch (code) - { - case LT: - /* < C is equivalent to <= (C - 1) */ - if (const_op > 0) - { - const_op -= 1; - op1 = GEN_INT (const_op); - code = LE; - /* ... fall through to LE case below. */ - } - else - break; - - case LE: - /* <= C is equivalent to < (C + 1); we do this for C < 0 */ - if (const_op < 0) - { - const_op += 1; - op1 = GEN_INT (const_op); - code = LT; - } - - /* If we are doing a <= 0 comparison on a value known to have - a zero sign bit, we can replace this with == 0. */ - else if (const_op == 0 - && mode_width <= HOST_BITS_PER_WIDE_INT - && (nonzero_bits (op0, mode) - & ((HOST_WIDE_INT) 1 << (mode_width - 1))) == 0) - code = EQ; - break; - - case GE: - /* >= C is equivalent to > (C - 1). */ - if (const_op > 0) - { - const_op -= 1; - op1 = GEN_INT (const_op); - code = GT; - /* ... fall through to GT below. */ - } - else - break; - - case GT: - /* > C is equivalent to >= (C + 1); we do this for C < 0*/ - if (const_op < 0) - { - const_op += 1; - op1 = GEN_INT (const_op); - code = GE; - } - - /* If we are doing a > 0 comparison on a value known to have - a zero sign bit, we can replace this with != 0. */ - else if (const_op == 0 - && mode_width <= HOST_BITS_PER_WIDE_INT - && (nonzero_bits (op0, mode) - & ((HOST_WIDE_INT) 1 << (mode_width - 1))) == 0) - code = NE; - break; - - case LTU: - /* < C is equivalent to <= (C - 1). */ - if (const_op > 0) - { - const_op -= 1; - op1 = GEN_INT (const_op); - code = LEU; - /* ... fall through ... */ - } - - /* (unsigned) < 0x80000000 is equivalent to >= 0. */ - else if (const_op == (HOST_WIDE_INT) 1 << (mode_width - 1)) - { - const_op = 0, op1 = const0_rtx; - code = GE; - break; - } - else - break; - - case LEU: - /* unsigned <= 0 is equivalent to == 0 */ - if (const_op == 0) - code = EQ; - - /* (unsigned) <= 0x7fffffff is equivalent to >= 0. */ - else if (const_op == ((HOST_WIDE_INT) 1 << (mode_width - 1)) - 1) - { - const_op = 0, op1 = const0_rtx; - code = GE; - } - break; - - case GEU: - /* >= C is equivalent to < (C - 1). */ - if (const_op > 1) - { - const_op -= 1; - op1 = GEN_INT (const_op); - code = GTU; - /* ... fall through ... */ - } - - /* (unsigned) >= 0x80000000 is equivalent to < 0. */ - else if (const_op == (HOST_WIDE_INT) 1 << (mode_width - 1)) - { - const_op = 0, op1 = const0_rtx; - code = LT; - } - else - break; - - case GTU: - /* unsigned > 0 is equivalent to != 0 */ - if (const_op == 0) - code = NE; - - /* (unsigned) > 0x7fffffff is equivalent to < 0. */ - else if (const_op == ((HOST_WIDE_INT) 1 << (mode_width - 1)) - 1) - { - const_op = 0, op1 = const0_rtx; - code = LT; - } - break; - } - - /* Compute some predicates to simplify code below. */ - - equality_comparison_p = (code == EQ || code == NE); - sign_bit_comparison_p = ((code == LT || code == GE) && const_op == 0); - unsigned_comparison_p = (code == LTU || code == LEU || code == GTU - || code == LEU); - - /* Now try cases based on the opcode of OP0. If none of the cases - does a "continue", we exit this loop immediately after the - switch. */ - - switch (GET_CODE (op0)) - { - case ZERO_EXTRACT: - /* If we are extracting a single bit from a variable position in - a constant that has only a single bit set and are comparing it - with zero, we can convert this into an equality comparison - between the position and the location of the single bit. We can't - do this if bit endian and we don't have an extzv since we then - can't know what mode to use for the endianness adjustment. */ - -#if ! BITS_BIG_ENDIAN || defined (HAVE_extzv) - if (GET_CODE (XEXP (op0, 0)) == CONST_INT - && XEXP (op0, 1) == const1_rtx - && equality_comparison_p && const_op == 0 - && (i = exact_log2 (INTVAL (XEXP (op0, 0)))) >= 0) - { -#if BITS_BIG_ENDIAN - i = (GET_MODE_BITSIZE - (insn_operand_mode[(int) CODE_FOR_extzv][1]) - 1 - i); -#endif - - op0 = XEXP (op0, 2); - op1 = GEN_INT (i); - const_op = i; - - /* Result is nonzero iff shift count is equal to I. */ - code = reverse_condition (code); - continue; - } -#endif - - /* ... fall through ... */ - - case SIGN_EXTRACT: - tem = expand_compound_operation (op0); - if (tem != op0) - { - op0 = tem; - continue; - } - break; - - case NOT: - /* If testing for equality, we can take the NOT of the constant. */ - if (equality_comparison_p - && (tem = simplify_unary_operation (NOT, mode, op1, mode)) != 0) - { - op0 = XEXP (op0, 0); - op1 = tem; - continue; - } - - /* If just looking at the sign bit, reverse the sense of the - comparison. */ - if (sign_bit_comparison_p) - { - op0 = XEXP (op0, 0); - code = (code == GE ? LT : GE); - continue; - } - break; - - case NEG: - /* If testing for equality, we can take the NEG of the constant. */ - if (equality_comparison_p - && (tem = simplify_unary_operation (NEG, mode, op1, mode)) != 0) - { - op0 = XEXP (op0, 0); - op1 = tem; - continue; - } - - /* The remaining cases only apply to comparisons with zero. */ - if (const_op != 0) - break; - - /* When X is ABS or is known positive, - (neg X) is < 0 if and only if X != 0. */ - - if (sign_bit_comparison_p - && (GET_CODE (XEXP (op0, 0)) == ABS - || (mode_width <= HOST_BITS_PER_WIDE_INT - && (nonzero_bits (XEXP (op0, 0), mode) - & ((HOST_WIDE_INT) 1 << (mode_width - 1))) == 0))) - { - op0 = XEXP (op0, 0); - code = (code == LT ? NE : EQ); - continue; - } - - /* If we have NEG of something whose two high-order bits are the - same, we know that "(-a) < 0" is equivalent to "a > 0". */ - if (num_sign_bit_copies (op0, mode) >= 2) - { - op0 = XEXP (op0, 0); - code = swap_condition (code); - continue; - } - break; - - case ROTATE: - /* If we are testing equality and our count is a constant, we - can perform the inverse operation on our RHS. */ - if (equality_comparison_p && GET_CODE (XEXP (op0, 1)) == CONST_INT - && (tem = simplify_binary_operation (ROTATERT, mode, - op1, XEXP (op0, 1))) != 0) - { - op0 = XEXP (op0, 0); - op1 = tem; - continue; - } - - /* If we are doing a < 0 or >= 0 comparison, it means we are testing - a particular bit. Convert it to an AND of a constant of that - bit. This will be converted into a ZERO_EXTRACT. */ - if (const_op == 0 && sign_bit_comparison_p - && GET_CODE (XEXP (op0, 1)) == CONST_INT - && mode_width <= HOST_BITS_PER_WIDE_INT) - { - op0 = simplify_and_const_int (NULL_RTX, mode, XEXP (op0, 0), - ((HOST_WIDE_INT) 1 - << (mode_width - 1 - - INTVAL (XEXP (op0, 1))))); - code = (code == LT ? NE : EQ); - continue; - } - - /* ... fall through ... */ - - case ABS: - /* ABS is ignorable inside an equality comparison with zero. */ - if (const_op == 0 && equality_comparison_p) - { - op0 = XEXP (op0, 0); - continue; - } - break; - - - case SIGN_EXTEND: - /* Can simplify (compare (zero/sign_extend FOO) CONST) - to (compare FOO CONST) if CONST fits in FOO's mode and we - are either testing inequality or have an unsigned comparison - with ZERO_EXTEND or a signed comparison with SIGN_EXTEND. */ - if (! unsigned_comparison_p - && (GET_MODE_BITSIZE (GET_MODE (XEXP (op0, 0))) - <= HOST_BITS_PER_WIDE_INT) - && ((unsigned HOST_WIDE_INT) const_op - < (((HOST_WIDE_INT) 1 - << (GET_MODE_BITSIZE (GET_MODE (XEXP (op0, 0))) - 1))))) - { - op0 = XEXP (op0, 0); - continue; - } - break; - - case SUBREG: - /* Check for the case where we are comparing A - C1 with C2, - both constants are smaller than 1/2 the maxium positive - value in MODE, and the comparison is equality or unsigned. - In that case, if A is either zero-extended to MODE or has - sufficient sign bits so that the high-order bit in MODE - is a copy of the sign in the inner mode, we can prove that it is - safe to do the operation in the wider mode. This simplifies - many range checks. */ - - if (mode_width <= HOST_BITS_PER_WIDE_INT - && subreg_lowpart_p (op0) - && GET_CODE (SUBREG_REG (op0)) == PLUS - && GET_CODE (XEXP (SUBREG_REG (op0), 1)) == CONST_INT - && INTVAL (XEXP (SUBREG_REG (op0), 1)) < 0 - && (- INTVAL (XEXP (SUBREG_REG (op0), 1)) - < GET_MODE_MASK (mode) / 2) - && (unsigned HOST_WIDE_INT) const_op < GET_MODE_MASK (mode) / 2 - && (0 == (nonzero_bits (XEXP (SUBREG_REG (op0), 0), - GET_MODE (SUBREG_REG (op0))) - & ~ GET_MODE_MASK (mode)) - || (num_sign_bit_copies (XEXP (SUBREG_REG (op0), 0), - GET_MODE (SUBREG_REG (op0))) - > (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0))) - - GET_MODE_BITSIZE (mode))))) - { - op0 = SUBREG_REG (op0); - continue; - } - - /* If the inner mode is narrower and we are extracting the low part, - we can treat the SUBREG as if it were a ZERO_EXTEND. */ - if (subreg_lowpart_p (op0) - && GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0))) < mode_width) - /* Fall through */ ; - else - break; - - /* ... fall through ... */ - - case ZERO_EXTEND: - if ((unsigned_comparison_p || equality_comparison_p) - && (GET_MODE_BITSIZE (GET_MODE (XEXP (op0, 0))) - <= HOST_BITS_PER_WIDE_INT) - && ((unsigned HOST_WIDE_INT) const_op - < GET_MODE_MASK (GET_MODE (XEXP (op0, 0))))) - { - op0 = XEXP (op0, 0); - continue; - } - break; - - case PLUS: - /* (eq (plus X A) B) -> (eq X (minus B A)). We can only do - this for equality comparisons due to pathological cases involving - overflows. */ - if (equality_comparison_p - && 0 != (tem = simplify_binary_operation (MINUS, mode, - op1, XEXP (op0, 1)))) - { - op0 = XEXP (op0, 0); - op1 = tem; - continue; - } - - /* (plus (abs X) (const_int -1)) is < 0 if and only if X == 0. */ - if (const_op == 0 && XEXP (op0, 1) == constm1_rtx - && GET_CODE (XEXP (op0, 0)) == ABS && sign_bit_comparison_p) - { - op0 = XEXP (XEXP (op0, 0), 0); - code = (code == LT ? EQ : NE); - continue; - } - break; - - case MINUS: - /* (eq (minus A B) C) -> (eq A (plus B C)) or - (eq B (minus A C)), whichever simplifies. We can only do - this for equality comparisons due to pathological cases involving - overflows. */ - if (equality_comparison_p - && 0 != (tem = simplify_binary_operation (PLUS, mode, - XEXP (op0, 1), op1))) - { - op0 = XEXP (op0, 0); - op1 = tem; - continue; - } - - if (equality_comparison_p - && 0 != (tem = simplify_binary_operation (MINUS, mode, - XEXP (op0, 0), op1))) - { - op0 = XEXP (op0, 1); - op1 = tem; - continue; - } - - /* The sign bit of (minus (ashiftrt X C) X), where C is the number - of bits in X minus 1, is one iff X > 0. */ - if (sign_bit_comparison_p && GET_CODE (XEXP (op0, 0)) == ASHIFTRT - && GET_CODE (XEXP (XEXP (op0, 0), 1)) == CONST_INT - && INTVAL (XEXP (XEXP (op0, 0), 1)) == mode_width - 1 - && rtx_equal_p (XEXP (XEXP (op0, 0), 0), XEXP (op0, 1))) - { - op0 = XEXP (op0, 1); - code = (code == GE ? LE : GT); - continue; - } - break; - - case XOR: - /* (eq (xor A B) C) -> (eq A (xor B C)). This is a simplification - if C is zero or B is a constant. */ - if (equality_comparison_p - && 0 != (tem = simplify_binary_operation (XOR, mode, - XEXP (op0, 1), op1))) - { - op0 = XEXP (op0, 0); - op1 = tem; - continue; - } - break; - - case EQ: case NE: - case LT: case LTU: case LE: case LEU: - case GT: case GTU: case GE: case GEU: - /* We can't do anything if OP0 is a condition code value, rather - than an actual data value. */ - if (const_op != 0 -#ifdef HAVE_cc0 - || XEXP (op0, 0) == cc0_rtx -#endif - || GET_MODE_CLASS (GET_MODE (XEXP (op0, 0))) == MODE_CC) - break; - - /* Get the two operands being compared. */ - if (GET_CODE (XEXP (op0, 0)) == COMPARE) - tem = XEXP (XEXP (op0, 0), 0), tem1 = XEXP (XEXP (op0, 0), 1); - else - tem = XEXP (op0, 0), tem1 = XEXP (op0, 1); - - /* Check for the cases where we simply want the result of the - earlier test or the opposite of that result. */ - if (code == NE - || (code == EQ && reversible_comparison_p (op0)) - || (GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT - && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT - && (STORE_FLAG_VALUE - & (((HOST_WIDE_INT) 1 - << (GET_MODE_BITSIZE (GET_MODE (op0)) - 1)))) - && (code == LT - || (code == GE && reversible_comparison_p (op0))))) - { - code = (code == LT || code == NE - ? GET_CODE (op0) : reverse_condition (GET_CODE (op0))); - op0 = tem, op1 = tem1; - continue; - } - break; - - case IOR: - /* The sign bit of (ior (plus X (const_int -1)) X) is non-zero - iff X <= 0. */ - if (sign_bit_comparison_p && GET_CODE (XEXP (op0, 0)) == PLUS - && XEXP (XEXP (op0, 0), 1) == constm1_rtx - && rtx_equal_p (XEXP (XEXP (op0, 0), 0), XEXP (op0, 1))) - { - op0 = XEXP (op0, 1); - code = (code == GE ? GT : LE); - continue; - } - break; - - case AND: - /* Convert (and (xshift 1 X) Y) to (and (lshiftrt Y X) 1). This - will be converted to a ZERO_EXTRACT later. */ - if (const_op == 0 && equality_comparison_p - && (GET_CODE (XEXP (op0, 0)) == ASHIFT - || GET_CODE (XEXP (op0, 0)) == LSHIFT) - && XEXP (XEXP (op0, 0), 0) == const1_rtx) - { - op0 = simplify_and_const_int - (op0, mode, gen_rtx_combine (LSHIFTRT, mode, - XEXP (op0, 1), - XEXP (XEXP (op0, 0), 1)), - (HOST_WIDE_INT) 1); - continue; - } - - /* If we are comparing (and (lshiftrt X C1) C2) for equality with - zero and X is a comparison and C1 and C2 describe only bits set - in STORE_FLAG_VALUE, we can compare with X. */ - if (const_op == 0 && equality_comparison_p - && mode_width <= HOST_BITS_PER_WIDE_INT - && GET_CODE (XEXP (op0, 1)) == CONST_INT - && GET_CODE (XEXP (op0, 0)) == LSHIFTRT - && GET_CODE (XEXP (XEXP (op0, 0), 1)) == CONST_INT - && INTVAL (XEXP (XEXP (op0, 0), 1)) >= 0 - && INTVAL (XEXP (XEXP (op0, 0), 1)) < HOST_BITS_PER_WIDE_INT) - { - mask = ((INTVAL (XEXP (op0, 1)) & GET_MODE_MASK (mode)) - << INTVAL (XEXP (XEXP (op0, 0), 1))); - if ((~ STORE_FLAG_VALUE & mask) == 0 - && (GET_RTX_CLASS (GET_CODE (XEXP (XEXP (op0, 0), 0))) == '<' - || ((tem = get_last_value (XEXP (XEXP (op0, 0), 0))) != 0 - && GET_RTX_CLASS (GET_CODE (tem)) == '<'))) - { - op0 = XEXP (XEXP (op0, 0), 0); - continue; - } - } - - /* If we are doing an equality comparison of an AND of a bit equal - to the sign bit, replace this with a LT or GE comparison of - the underlying value. */ - if (equality_comparison_p - && const_op == 0 - && GET_CODE (XEXP (op0, 1)) == CONST_INT - && mode_width <= HOST_BITS_PER_WIDE_INT - && ((INTVAL (XEXP (op0, 1)) & GET_MODE_MASK (mode)) - == (HOST_WIDE_INT) 1 << (mode_width - 1))) - { - op0 = XEXP (op0, 0); - code = (code == EQ ? GE : LT); - continue; - } - - /* If this AND operation is really a ZERO_EXTEND from a narrower - mode, the constant fits within that mode, and this is either an - equality or unsigned comparison, try to do this comparison in - the narrower mode. */ - if ((equality_comparison_p || unsigned_comparison_p) - && GET_CODE (XEXP (op0, 1)) == CONST_INT - && (i = exact_log2 ((INTVAL (XEXP (op0, 1)) - & GET_MODE_MASK (mode)) - + 1)) >= 0 - && const_op >> i == 0 - && (tmode = mode_for_size (i, MODE_INT, 1)) != BLKmode) - { - op0 = gen_lowpart_for_combine (tmode, XEXP (op0, 0)); - continue; - } - break; - - case ASHIFT: - case LSHIFT: - /* If we have (compare (xshift FOO N) (const_int C)) and - the high order N bits of FOO (N+1 if an inequality comparison) - are known to be zero, we can do this by comparing FOO with C - shifted right N bits so long as the low-order N bits of C are - zero. */ - if (GET_CODE (XEXP (op0, 1)) == CONST_INT - && INTVAL (XEXP (op0, 1)) >= 0 - && ((INTVAL (XEXP (op0, 1)) + ! equality_comparison_p) - < HOST_BITS_PER_WIDE_INT) - && ((const_op - & ((HOST_WIDE_INT) 1 << INTVAL (XEXP (op0, 1))) - 1) == 0) - && mode_width <= HOST_BITS_PER_WIDE_INT - && (nonzero_bits (XEXP (op0, 0), mode) - & ~ (mask >> (INTVAL (XEXP (op0, 1)) - + ! equality_comparison_p))) == 0) - { - const_op >>= INTVAL (XEXP (op0, 1)); - op1 = GEN_INT (const_op); - op0 = XEXP (op0, 0); - continue; - } - - /* If we are doing a sign bit comparison, it means we are testing - a particular bit. Convert it to the appropriate AND. */ - if (sign_bit_comparison_p && GET_CODE (XEXP (op0, 1)) == CONST_INT - && mode_width <= HOST_BITS_PER_WIDE_INT) - { - op0 = simplify_and_const_int (NULL_RTX, mode, XEXP (op0, 0), - ((HOST_WIDE_INT) 1 - << (mode_width - 1 - - INTVAL (XEXP (op0, 1))))); - code = (code == LT ? NE : EQ); - continue; - } - - /* If this an equality comparison with zero and we are shifting - the low bit to the sign bit, we can convert this to an AND of the - low-order bit. */ - if (const_op == 0 && equality_comparison_p - && GET_CODE (XEXP (op0, 1)) == CONST_INT - && INTVAL (XEXP (op0, 1)) == mode_width - 1) - { - op0 = simplify_and_const_int (NULL_RTX, mode, XEXP (op0, 0), - (HOST_WIDE_INT) 1); - continue; - } - break; - - case ASHIFTRT: - /* If this is an equality comparison with zero, we can do this - as a logical shift, which might be much simpler. */ - if (equality_comparison_p && const_op == 0 - && GET_CODE (XEXP (op0, 1)) == CONST_INT) - { - op0 = simplify_shift_const (NULL_RTX, LSHIFTRT, mode, - XEXP (op0, 0), - INTVAL (XEXP (op0, 1))); - continue; - } - - /* If OP0 is a sign extension and CODE is not an unsigned comparison, - do the comparison in a narrower mode. */ - if (! unsigned_comparison_p - && GET_CODE (XEXP (op0, 1)) == CONST_INT - && GET_CODE (XEXP (op0, 0)) == ASHIFT - && XEXP (op0, 1) == XEXP (XEXP (op0, 0), 1) - && (tmode = mode_for_size (mode_width - INTVAL (XEXP (op0, 1)), - MODE_INT, 1)) != BLKmode - && ((unsigned HOST_WIDE_INT) const_op <= GET_MODE_MASK (tmode) - || ((unsigned HOST_WIDE_INT) - const_op - <= GET_MODE_MASK (tmode)))) - { - op0 = gen_lowpart_for_combine (tmode, XEXP (XEXP (op0, 0), 0)); - continue; - } - - /* ... fall through ... */ - case LSHIFTRT: - /* If we have (compare (xshiftrt FOO N) (const_int C)) and - the low order N bits of FOO are known to be zero, we can do this - by comparing FOO with C shifted left N bits so long as no - overflow occurs. */ - if (GET_CODE (XEXP (op0, 1)) == CONST_INT - && INTVAL (XEXP (op0, 1)) >= 0 - && INTVAL (XEXP (op0, 1)) < HOST_BITS_PER_WIDE_INT - && mode_width <= HOST_BITS_PER_WIDE_INT - && (nonzero_bits (XEXP (op0, 0), mode) - & (((HOST_WIDE_INT) 1 << INTVAL (XEXP (op0, 1))) - 1)) == 0 - && (const_op == 0 - || (floor_log2 (const_op) + INTVAL (XEXP (op0, 1)) - < mode_width))) - { - const_op <<= INTVAL (XEXP (op0, 1)); - op1 = GEN_INT (const_op); - op0 = XEXP (op0, 0); - continue; - } - - /* If we are using this shift to extract just the sign bit, we - can replace this with an LT or GE comparison. */ - if (const_op == 0 - && (equality_comparison_p || sign_bit_comparison_p) - && GET_CODE (XEXP (op0, 1)) == CONST_INT - && INTVAL (XEXP (op0, 1)) == mode_width - 1) - { - op0 = XEXP (op0, 0); - code = (code == NE || code == GT ? LT : GE); - continue; - } - break; - } - - break; - } - - /* Now make any compound operations involved in this comparison. Then, - check for an outmost SUBREG on OP0 that isn't doing anything or is - paradoxical. The latter case can only occur when it is known that the - "extra" bits will be zero. Therefore, it is safe to remove the SUBREG. - We can never remove a SUBREG for a non-equality comparison because the - sign bit is in a different place in the underlying object. */ - - op0 = make_compound_operation (op0, op1 == const0_rtx ? COMPARE : SET); - op1 = make_compound_operation (op1, SET); - - if (GET_CODE (op0) == SUBREG && subreg_lowpart_p (op0) - && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT - && (code == NE || code == EQ) - && ((GET_MODE_SIZE (GET_MODE (op0)) - > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)))))) - { - op0 = SUBREG_REG (op0); - op1 = gen_lowpart_for_combine (GET_MODE (op0), op1); - } - - else if (GET_CODE (op0) == SUBREG && subreg_lowpart_p (op0) - && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT - && (code == NE || code == EQ) - && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0))) - <= HOST_BITS_PER_WIDE_INT) - && (nonzero_bits (SUBREG_REG (op0), GET_MODE (SUBREG_REG (op0))) - & ~ GET_MODE_MASK (GET_MODE (op0))) == 0 - && (tem = gen_lowpart_for_combine (GET_MODE (SUBREG_REG (op0)), - op1), - (nonzero_bits (tem, GET_MODE (SUBREG_REG (op0))) - & ~ GET_MODE_MASK (GET_MODE (op0))) == 0)) - op0 = SUBREG_REG (op0), op1 = tem; - - /* We now do the opposite procedure: Some machines don't have compare - insns in all modes. If OP0's mode is an integer mode smaller than a - word and we can't do a compare in that mode, see if there is a larger - mode for which we can do the compare. There are a number of cases in - which we can use the wider mode. */ - - mode = GET_MODE (op0); - if (mode != VOIDmode && GET_MODE_CLASS (mode) == MODE_INT - && GET_MODE_SIZE (mode) < UNITS_PER_WORD - && cmp_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) - for (tmode = GET_MODE_WIDER_MODE (mode); - (tmode != VOIDmode - && GET_MODE_BITSIZE (tmode) <= HOST_BITS_PER_WIDE_INT); - tmode = GET_MODE_WIDER_MODE (tmode)) - if (cmp_optab->handlers[(int) tmode].insn_code != CODE_FOR_nothing) - { - /* If the only nonzero bits in OP0 and OP1 are those in the - narrower mode and this is an equality or unsigned comparison, - we can use the wider mode. Similarly for sign-extended - values and equality or signed comparisons. */ - if (((code == EQ || code == NE - || code == GEU || code == GTU || code == LEU || code == LTU) - && (nonzero_bits (op0, tmode) & ~ GET_MODE_MASK (mode)) == 0 - && (nonzero_bits (op1, tmode) & ~ GET_MODE_MASK (mode)) == 0) - || ((code == EQ || code == NE - || code == GE || code == GT || code == LE || code == LT) - && (num_sign_bit_copies (op0, tmode) - > GET_MODE_BITSIZE (tmode) - GET_MODE_BITSIZE (mode)) - && (num_sign_bit_copies (op1, tmode) - > GET_MODE_BITSIZE (tmode) - GET_MODE_BITSIZE (mode)))) - { - op0 = gen_lowpart_for_combine (tmode, op0); - op1 = gen_lowpart_for_combine (tmode, op1); - break; - } - - /* If this is a test for negative, we can make an explicit - test of the sign bit. */ - - if (op1 == const0_rtx && (code == LT || code == GE) - && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT) - { - op0 = gen_binary (AND, tmode, - gen_lowpart_for_combine (tmode, op0), - GEN_INT ((HOST_WIDE_INT) 1 - << (GET_MODE_BITSIZE (mode) - 1))); - code = (code == LT) ? NE : EQ; - break; - } - } - - *pop0 = op0; - *pop1 = op1; - - return code; -} - -/* Return 1 if we know that X, a comparison operation, is not operating - on a floating-point value or is EQ or NE, meaning that we can safely - reverse it. */ - -static int -reversible_comparison_p (x) - rtx x; -{ - if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT - || GET_CODE (x) == NE || GET_CODE (x) == EQ) - return 1; - - switch (GET_MODE_CLASS (GET_MODE (XEXP (x, 0)))) - { - case MODE_INT: - return 1; - - case MODE_CC: - x = get_last_value (XEXP (x, 0)); - return (x && GET_CODE (x) == COMPARE - && GET_MODE_CLASS (GET_MODE (XEXP (x, 0))) == MODE_INT); - } - - return 0; -} - -/* Utility function for following routine. Called when X is part of a value - being stored into reg_last_set_value. Sets reg_last_set_table_tick - for each register mentioned. Similar to mention_regs in cse.c */ - -static void -update_table_tick (x) - rtx x; -{ - register enum rtx_code code = GET_CODE (x); - register char *fmt = GET_RTX_FORMAT (code); - register int i; - - if (code == REG) - { - int regno = REGNO (x); - int endregno = regno + (regno < FIRST_PSEUDO_REGISTER - ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1); - - for (i = regno; i < endregno; i++) - reg_last_set_table_tick[i] = label_tick; - - return; - } - - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - /* Note that we can't have an "E" in values stored; see - get_last_value_validate. */ - if (fmt[i] == 'e') - update_table_tick (XEXP (x, i)); -} - -/* Record that REG is set to VALUE in insn INSN. If VALUE is zero, we - are saying that the register is clobbered and we no longer know its - value. If INSN is zero, don't update reg_last_set; this is only permitted - with VALUE also zero and is used to invalidate the register. */ - -static void -record_value_for_reg (reg, insn, value) - rtx reg; - rtx insn; - rtx value; -{ - int regno = REGNO (reg); - int endregno = regno + (regno < FIRST_PSEUDO_REGISTER - ? HARD_REGNO_NREGS (regno, GET_MODE (reg)) : 1); - int i; - - /* If VALUE contains REG and we have a previous value for REG, substitute - the previous value. */ - if (value && insn && reg_overlap_mentioned_p (reg, value)) - { - rtx tem; - - /* Set things up so get_last_value is allowed to see anything set up to - our insn. */ - subst_low_cuid = INSN_CUID (insn); - tem = get_last_value (reg); - - if (tem) - value = replace_rtx (copy_rtx (value), reg, tem); - } - - /* For each register modified, show we don't know its value, that - its value has been updated, and that we don't know the location of - the death of the register. */ - for (i = regno; i < endregno; i ++) - { - if (insn) - reg_last_set[i] = insn; - reg_last_set_value[i] = 0; - reg_last_death[i] = 0; - } - - /* Mark registers that are being referenced in this value. */ - if (value) - update_table_tick (value); - - /* Now update the status of each register being set. - If someone is using this register in this block, set this register - to invalid since we will get confused between the two lives in this - basic block. This makes using this register always invalid. In cse, we - scan the table to invalidate all entries using this register, but this - is too much work for us. */ - - for (i = regno; i < endregno; i++) - { - reg_last_set_label[i] = label_tick; - if (value && reg_last_set_table_tick[i] == label_tick) - reg_last_set_invalid[i] = 1; - else - reg_last_set_invalid[i] = 0; - } - - /* The value being assigned might refer to X (like in "x++;"). In that - case, we must replace it with (clobber (const_int 0)) to prevent - infinite loops. */ - if (value && ! get_last_value_validate (&value, - reg_last_set_label[regno], 0)) - { - value = copy_rtx (value); - if (! get_last_value_validate (&value, reg_last_set_label[regno], 1)) - value = 0; - } - - /* For the main register being modified, update the value, the mode, the - nonzero bits, and the number of sign bit copies. */ - - reg_last_set_value[regno] = value; - - if (value) - { - subst_low_cuid = INSN_CUID (insn); - reg_last_set_mode[regno] = GET_MODE (reg); - reg_last_set_nonzero_bits[regno] = nonzero_bits (value, GET_MODE (reg)); - reg_last_set_sign_bit_copies[regno] - = num_sign_bit_copies (value, GET_MODE (reg)); - } -} - -/* Used for communication between the following two routines. */ -static rtx record_dead_insn; - -/* Called via note_stores from record_dead_and_set_regs to handle one - SET or CLOBBER in an insn. */ - -static void -record_dead_and_set_regs_1 (dest, setter) - rtx dest, setter; -{ - if (GET_CODE (dest) == REG) - { - /* If we are setting the whole register, we know its value. Otherwise - show that we don't know the value. We can handle SUBREG in - some cases. */ - if (GET_CODE (setter) == SET && dest == SET_DEST (setter)) - record_value_for_reg (dest, record_dead_insn, SET_SRC (setter)); - else if (GET_CODE (setter) == SET - && GET_CODE (SET_DEST (setter)) == SUBREG - && SUBREG_REG (SET_DEST (setter)) == dest - && subreg_lowpart_p (SET_DEST (setter))) - record_value_for_reg (dest, record_dead_insn, - gen_lowpart_for_combine (GET_MODE (dest), - SET_SRC (setter))); - else - record_value_for_reg (dest, record_dead_insn, NULL_RTX); - } - else if (GET_CODE (dest) == MEM - /* Ignore pushes, they clobber nothing. */ - && ! push_operand (dest, GET_MODE (dest))) - mem_last_set = INSN_CUID (record_dead_insn); -} - -/* Update the records of when each REG was most recently set or killed - for the things done by INSN. This is the last thing done in processing - INSN in the combiner loop. - - We update reg_last_set, reg_last_set_value, reg_last_death, and also the - similar information mem_last_set (which insn most recently modified memory) - and last_call_cuid (which insn was the most recent subroutine call). */ - -static void -record_dead_and_set_regs (insn) - rtx insn; -{ - register rtx link; - int i; - - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - { - if (REG_NOTE_KIND (link) == REG_DEAD - && GET_CODE (XEXP (link, 0)) == REG) - { - int regno = REGNO (XEXP (link, 0)); - int endregno - = regno + (regno < FIRST_PSEUDO_REGISTER - ? HARD_REGNO_NREGS (regno, GET_MODE (XEXP (link, 0))) - : 1); - - for (i = regno; i < endregno; i++) - reg_last_death[i] = insn; - } - else if (REG_NOTE_KIND (link) == REG_INC) - record_value_for_reg (XEXP (link, 0), insn, NULL_RTX); - } - - if (GET_CODE (insn) == CALL_INSN) - { - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (call_used_regs[i]) - { - reg_last_set_value[i] = 0; - reg_last_death[i] = 0; - } - - last_call_cuid = mem_last_set = INSN_CUID (insn); - } - - record_dead_insn = insn; - note_stores (PATTERN (insn), record_dead_and_set_regs_1); -} - -/* Utility routine for the following function. Verify that all the registers - mentioned in *LOC are valid when *LOC was part of a value set when - label_tick == TICK. Return 0 if some are not. - - If REPLACE is non-zero, replace the invalid reference with - (clobber (const_int 0)) and return 1. This replacement is useful because - we often can get useful information about the form of a value (e.g., if - it was produced by a shift that always produces -1 or 0) even though - we don't know exactly what registers it was produced from. */ - -static int -get_last_value_validate (loc, tick, replace) - rtx *loc; - int tick; - int replace; -{ - rtx x = *loc; - char *fmt = GET_RTX_FORMAT (GET_CODE (x)); - int len = GET_RTX_LENGTH (GET_CODE (x)); - int i; - - if (GET_CODE (x) == REG) - { - int regno = REGNO (x); - int endregno = regno + (regno < FIRST_PSEUDO_REGISTER - ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1); - int j; - - for (j = regno; j < endregno; j++) - if (reg_last_set_invalid[j] - /* If this is a pseudo-register that was only set once, it is - always valid. */ - || (! (regno >= FIRST_PSEUDO_REGISTER && reg_n_sets[regno] == 1) - && reg_last_set_label[j] > tick)) - { - if (replace) - *loc = gen_rtx (CLOBBER, GET_MODE (x), const0_rtx); - return replace; - } - - return 1; - } - - for (i = 0; i < len; i++) - if ((fmt[i] == 'e' - && get_last_value_validate (&XEXP (x, i), tick, replace) == 0) - /* Don't bother with these. They shouldn't occur anyway. */ - || fmt[i] == 'E') - return 0; - - /* If we haven't found a reason for it to be invalid, it is valid. */ - return 1; -} - -/* Get the last value assigned to X, if known. Some registers - in the value may be replaced with (clobber (const_int 0)) if their value - is known longer known reliably. */ - -static rtx -get_last_value (x) - rtx x; -{ - int regno; - rtx value; - - /* If this is a non-paradoxical SUBREG, get the value of its operand and - then convert it to the desired mode. If this is a paradoxical SUBREG, - we cannot predict what values the "extra" bits might have. */ - if (GET_CODE (x) == SUBREG - && subreg_lowpart_p (x) - && (GET_MODE_SIZE (GET_MODE (x)) - <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) - && (value = get_last_value (SUBREG_REG (x))) != 0) - return gen_lowpart_for_combine (GET_MODE (x), value); - - if (GET_CODE (x) != REG) - return 0; - - regno = REGNO (x); - value = reg_last_set_value[regno]; - - /* If we don't have a value or if it isn't for this basic block, return 0. */ - - if (value == 0 - || (reg_n_sets[regno] != 1 - && reg_last_set_label[regno] != label_tick)) - return 0; - - /* If the value was set in a later insn that the ones we are processing, - we can't use it even if the register was only set once, but make a quick - check to see if the previous insn set it to something. This is commonly - the case when the same pseudo is used by repeated insns. */ - - if (INSN_CUID (reg_last_set[regno]) >= subst_low_cuid) - { - rtx insn, set; - - for (insn = prev_nonnote_insn (subst_insn); - insn && INSN_CUID (insn) >= subst_low_cuid; - insn = prev_nonnote_insn (insn)) - ; - - if (insn - && (set = single_set (insn)) != 0 - && rtx_equal_p (SET_DEST (set), x)) - { - value = SET_SRC (set); - - /* Make sure that VALUE doesn't reference X. Replace any - expliit references with a CLOBBER. If there are any remaining - references (rare), don't use the value. */ - - if (reg_mentioned_p (x, value)) - value = replace_rtx (copy_rtx (value), x, - gen_rtx (CLOBBER, GET_MODE (x), const0_rtx)); - - if (reg_overlap_mentioned_p (x, value)) - return 0; - } - else - return 0; - } - - /* If the value has all its registers valid, return it. */ - if (get_last_value_validate (&value, reg_last_set_label[regno], 0)) - return value; - - /* Otherwise, make a copy and replace any invalid register with - (clobber (const_int 0)). If that fails for some reason, return 0. */ - - value = copy_rtx (value); - if (get_last_value_validate (&value, reg_last_set_label[regno], 1)) - return value; - - return 0; -} - -/* Return nonzero if expression X refers to a REG or to memory - that is set in an instruction more recent than FROM_CUID. */ - -static int -use_crosses_set_p (x, from_cuid) - register rtx x; - int from_cuid; -{ - register char *fmt; - register int i; - register enum rtx_code code = GET_CODE (x); - - if (code == REG) - { - register int regno = REGNO (x); -#ifdef PUSH_ROUNDING - /* Don't allow uses of the stack pointer to be moved, - because we don't know whether the move crosses a push insn. */ - if (regno == STACK_POINTER_REGNUM) - return 1; -#endif - return (reg_last_set[regno] - && INSN_CUID (reg_last_set[regno]) > from_cuid); - } - - if (code == MEM && mem_last_set > from_cuid) - return 1; - - fmt = GET_RTX_FORMAT (code); - - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - if (use_crosses_set_p (XVECEXP (x, i, j), from_cuid)) - return 1; - } - else if (fmt[i] == 'e' - && use_crosses_set_p (XEXP (x, i), from_cuid)) - return 1; - } - return 0; -} - -/* Define three variables used for communication between the following - routines. */ - -static int reg_dead_regno, reg_dead_endregno; -static int reg_dead_flag; - -/* Function called via note_stores from reg_dead_at_p. - - If DEST is within [reg_dead_rengno, reg_dead_endregno), set - reg_dead_flag to 1 if X is a CLOBBER and to -1 it is a SET. */ - -static void -reg_dead_at_p_1 (dest, x) - rtx dest; - rtx x; -{ - int regno, endregno; - - if (GET_CODE (dest) != REG) - return; - - regno = REGNO (dest); - endregno = regno + (regno < FIRST_PSEUDO_REGISTER - ? HARD_REGNO_NREGS (regno, GET_MODE (dest)) : 1); - - if (reg_dead_endregno > regno && reg_dead_regno < endregno) - reg_dead_flag = (GET_CODE (x) == CLOBBER) ? 1 : -1; -} - -/* Return non-zero if REG is known to be dead at INSN. - - We scan backwards from INSN. If we hit a REG_DEAD note or a CLOBBER - referencing REG, it is dead. If we hit a SET referencing REG, it is - live. Otherwise, see if it is live or dead at the start of the basic - block we are in. */ - -static int -reg_dead_at_p (reg, insn) - rtx reg; - rtx insn; -{ - int block, i; - - /* Set variables for reg_dead_at_p_1. */ - reg_dead_regno = REGNO (reg); - reg_dead_endregno = reg_dead_regno + (reg_dead_regno < FIRST_PSEUDO_REGISTER - ? HARD_REGNO_NREGS (reg_dead_regno, - GET_MODE (reg)) - : 1); - - reg_dead_flag = 0; - - /* Scan backwards until we find a REG_DEAD note, SET, CLOBBER, label, or - beginning of function. */ - for (; insn && GET_CODE (insn) != CODE_LABEL; - insn = prev_nonnote_insn (insn)) - { - note_stores (PATTERN (insn), reg_dead_at_p_1); - if (reg_dead_flag) - return reg_dead_flag == 1 ? 1 : 0; - - if (find_regno_note (insn, REG_DEAD, reg_dead_regno)) - return 1; - } - - /* Get the basic block number that we were in. */ - if (insn == 0) - block = 0; - else - { - for (block = 0; block < n_basic_blocks; block++) - if (insn == basic_block_head[block]) - break; - - if (block == n_basic_blocks) - return 0; - } - - for (i = reg_dead_regno; i < reg_dead_endregno; i++) - if (basic_block_live_at_start[block][i / REGSET_ELT_BITS] - & ((REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS))) - return 0; - - return 1; -} - -/* Remove register number REGNO from the dead registers list of INSN. - - Return the note used to record the death, if there was one. */ - -rtx -remove_death (regno, insn) - int regno; - rtx insn; -{ - register rtx note = find_regno_note (insn, REG_DEAD, regno); - - if (note) - { - reg_n_deaths[regno]--; - remove_note (insn, note); - } - - return note; -} - -/* For each register (hardware or pseudo) used within expression X, if its - death is in an instruction with cuid between FROM_CUID (inclusive) and - TO_INSN (exclusive), put a REG_DEAD note for that register in the - list headed by PNOTES. - - This is done when X is being merged by combination into TO_INSN. These - notes will then be distributed as needed. */ - -static void -move_deaths (x, from_cuid, to_insn, pnotes) - rtx x; - int from_cuid; - rtx to_insn; - rtx *pnotes; -{ - register char *fmt; - register int len, i; - register enum rtx_code code = GET_CODE (x); - - if (code == REG) - { - register int regno = REGNO (x); - register rtx where_dead = reg_last_death[regno]; - - if (where_dead && INSN_CUID (where_dead) >= from_cuid - && INSN_CUID (where_dead) < INSN_CUID (to_insn)) - { - rtx note = remove_death (regno, where_dead); - - /* It is possible for the call above to return 0. This can occur - when reg_last_death points to I2 or I1 that we combined with. - In that case make a new note. - - We must also check for the case where X is a hard register - and NOTE is a death note for a range of hard registers - including X. In that case, we must put REG_DEAD notes for - the remaining registers in place of NOTE. */ - - if (note != 0 && regno < FIRST_PSEUDO_REGISTER - && (GET_MODE_SIZE (GET_MODE (XEXP (note, 0))) - != GET_MODE_SIZE (GET_MODE (x)))) - { - int deadregno = REGNO (XEXP (note, 0)); - int deadend - = (deadregno + HARD_REGNO_NREGS (deadregno, - GET_MODE (XEXP (note, 0)))); - int ourend = regno + HARD_REGNO_NREGS (regno, GET_MODE (x)); - int i; - - for (i = deadregno; i < deadend; i++) - if (i < regno || i >= ourend) - REG_NOTES (where_dead) - = gen_rtx (EXPR_LIST, REG_DEAD, - gen_rtx (REG, word_mode, i), - REG_NOTES (where_dead)); - } - - if (note != 0 && GET_MODE (XEXP (note, 0)) == GET_MODE (x)) - { - XEXP (note, 1) = *pnotes; - *pnotes = note; - } - else - *pnotes = gen_rtx (EXPR_LIST, REG_DEAD, x, *pnotes); - - reg_n_deaths[regno]++; - } - - return; - } - - else if (GET_CODE (x) == SET) - { - rtx dest = SET_DEST (x); - - move_deaths (SET_SRC (x), from_cuid, to_insn, pnotes); - - /* In the case of a ZERO_EXTRACT, a STRICT_LOW_PART, or a SUBREG - that accesses one word of a multi-word item, some - piece of everything register in the expression is used by - this insn, so remove any old death. */ - - if (GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == STRICT_LOW_PART - || (GET_CODE (dest) == SUBREG - && (((GET_MODE_SIZE (GET_MODE (dest)) - + UNITS_PER_WORD - 1) / UNITS_PER_WORD) - == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) - + UNITS_PER_WORD - 1) / UNITS_PER_WORD)))) - { - move_deaths (dest, from_cuid, to_insn, pnotes); - return; - } - - /* If this is some other SUBREG, we know it replaces the entire - value, so use that as the destination. */ - if (GET_CODE (dest) == SUBREG) - dest = SUBREG_REG (dest); - - /* If this is a MEM, adjust deaths of anything used in the address. - For a REG (the only other possibility), the entire value is - being replaced so the old value is not used in this insn. */ - - if (GET_CODE (dest) == MEM) - move_deaths (XEXP (dest, 0), from_cuid, to_insn, pnotes); - return; - } - - else if (GET_CODE (x) == CLOBBER) - return; - - len = GET_RTX_LENGTH (code); - fmt = GET_RTX_FORMAT (code); - - for (i = 0; i < len; i++) - { - if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - move_deaths (XVECEXP (x, i, j), from_cuid, to_insn, pnotes); - } - else if (fmt[i] == 'e') - move_deaths (XEXP (x, i), from_cuid, to_insn, pnotes); - } -} - -/* Return 1 if X is the target of a bit-field assignment in BODY, the - pattern of an insn. X must be a REG. */ - -static int -reg_bitfield_target_p (x, body) - rtx x; - rtx body; -{ - int i; - - if (GET_CODE (body) == SET) - { - rtx dest = SET_DEST (body); - rtx target; - int regno, tregno, endregno, endtregno; - - if (GET_CODE (dest) == ZERO_EXTRACT) - target = XEXP (dest, 0); - else if (GET_CODE (dest) == STRICT_LOW_PART) - target = SUBREG_REG (XEXP (dest, 0)); - else - return 0; - - if (GET_CODE (target) == SUBREG) - target = SUBREG_REG (target); - - if (GET_CODE (target) != REG) - return 0; - - tregno = REGNO (target), regno = REGNO (x); - if (tregno >= FIRST_PSEUDO_REGISTER || regno >= FIRST_PSEUDO_REGISTER) - return target == x; - - endtregno = tregno + HARD_REGNO_NREGS (tregno, GET_MODE (target)); - endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x)); - - return endregno > tregno && regno < endtregno; - } - - else if (GET_CODE (body) == PARALLEL) - for (i = XVECLEN (body, 0) - 1; i >= 0; i--) - if (reg_bitfield_target_p (x, XVECEXP (body, 0, i))) - return 1; - - return 0; -} - -/* Given a chain of REG_NOTES originally from FROM_INSN, try to place them - as appropriate. I3 and I2 are the insns resulting from the combination - insns including FROM (I2 may be zero). - - ELIM_I2 and ELIM_I1 are either zero or registers that we know will - not need REG_DEAD notes because they are being substituted for. This - saves searching in the most common cases. - - Each note in the list is either ignored or placed on some insns, depending - on the type of note. */ - -static void -distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1) - rtx notes; - rtx from_insn; - rtx i3, i2; - rtx elim_i2, elim_i1; -{ - rtx note, next_note; - rtx tem; - - for (note = notes; note; note = next_note) - { - rtx place = 0, place2 = 0; - - /* If this NOTE references a pseudo register, ensure it references - the latest copy of that register. */ - if (XEXP (note, 0) && GET_CODE (XEXP (note, 0)) == REG - && REGNO (XEXP (note, 0)) >= FIRST_PSEUDO_REGISTER) - XEXP (note, 0) = regno_reg_rtx[REGNO (XEXP (note, 0))]; - - next_note = XEXP (note, 1); - switch (REG_NOTE_KIND (note)) - { - case REG_UNUSED: - /* If this register is set or clobbered in I3, put the note there - unless there is one already. */ - if (reg_set_p (XEXP (note, 0), PATTERN (i3))) - { - if (! (GET_CODE (XEXP (note, 0)) == REG - ? find_regno_note (i3, REG_UNUSED, REGNO (XEXP (note, 0))) - : find_reg_note (i3, REG_UNUSED, XEXP (note, 0)))) - place = i3; - } - /* Otherwise, if this register is used by I3, then this register - now dies here, so we must put a REG_DEAD note here unless there - is one already. */ - else if (reg_referenced_p (XEXP (note, 0), PATTERN (i3)) - && ! (GET_CODE (XEXP (note, 0)) == REG - ? find_regno_note (i3, REG_DEAD, REGNO (XEXP (note, 0))) - : find_reg_note (i3, REG_DEAD, XEXP (note, 0)))) - { - PUT_REG_NOTE_KIND (note, REG_DEAD); - place = i3; - } - break; - - case REG_EQUAL: - case REG_EQUIV: - case REG_NONNEG: - /* These notes say something about results of an insn. We can - only support them if they used to be on I3 in which case they - remain on I3. Otherwise they are ignored. - - If the note refers to an expression that is not a constant, we - must also ignore the note since we cannot tell whether the - equivalence is still true. It might be possible to do - slightly better than this (we only have a problem if I2DEST - or I1DEST is present in the expression), but it doesn't - seem worth the trouble. */ - - if (from_insn == i3 - && (XEXP (note, 0) == 0 || CONSTANT_P (XEXP (note, 0)))) - place = i3; - break; - - case REG_INC: - case REG_NO_CONFLICT: - case REG_LABEL: - /* These notes say something about how a register is used. They must - be present on any use of the register in I2 or I3. */ - if (reg_mentioned_p (XEXP (note, 0), PATTERN (i3))) - place = i3; - - if (i2 && reg_mentioned_p (XEXP (note, 0), PATTERN (i2))) - { - if (place) - place2 = i2; - else - place = i2; - } - break; - - case REG_WAS_0: - /* It is too much trouble to try to see if this note is still - correct in all situations. It is better to simply delete it. */ - break; - - case REG_RETVAL: - /* If the insn previously containing this note still exists, - put it back where it was. Otherwise move it to the previous - insn. Adjust the corresponding REG_LIBCALL note. */ - if (GET_CODE (from_insn) != NOTE) - place = from_insn; - else - { - tem = find_reg_note (XEXP (note, 0), REG_LIBCALL, NULL_RTX); - place = prev_real_insn (from_insn); - if (tem && place) - XEXP (tem, 0) = place; - } - break; - - case REG_LIBCALL: - /* This is handled similarly to REG_RETVAL. */ - if (GET_CODE (from_insn) != NOTE) - place = from_insn; - else - { - tem = find_reg_note (XEXP (note, 0), REG_RETVAL, NULL_RTX); - place = next_real_insn (from_insn); - if (tem && place) - XEXP (tem, 0) = place; - } - break; - - case REG_DEAD: - /* If the register is used as an input in I3, it dies there. - Similarly for I2, if it is non-zero and adjacent to I3. - - If the register is not used as an input in either I3 or I2 - and it is not one of the registers we were supposed to eliminate, - there are two possibilities. We might have a non-adjacent I2 - or we might have somehow eliminated an additional register - from a computation. For example, we might have had A & B where - we discover that B will always be zero. In this case we will - eliminate the reference to A. - - In both cases, we must search to see if we can find a previous - use of A and put the death note there. */ - - if (reg_referenced_p (XEXP (note, 0), PATTERN (i3))) - place = i3; - else if (i2 != 0 && next_nonnote_insn (i2) == i3 - && reg_referenced_p (XEXP (note, 0), PATTERN (i2))) - place = i2; - - if (XEXP (note, 0) == elim_i2 || XEXP (note, 0) == elim_i1) - break; - - /* If the register is used in both I2 and I3 and it dies in I3, - we might have added another reference to it. If reg_n_refs - was 2, bump it to 3. This has to be correct since the - register must have been set somewhere. The reason this is - done is because local-alloc.c treats 2 references as a - special case. */ - - if (place == i3 && i2 != 0 && GET_CODE (XEXP (note, 0)) == REG - && reg_n_refs[REGNO (XEXP (note, 0))]== 2 - && reg_referenced_p (XEXP (note, 0), PATTERN (i2))) - reg_n_refs[REGNO (XEXP (note, 0))] = 3; - - if (place == 0) - for (tem = prev_nonnote_insn (i3); - tem && (GET_CODE (tem) == INSN - || GET_CODE (tem) == CALL_INSN); - tem = prev_nonnote_insn (tem)) - { - /* If the register is being set at TEM, see if that is all - TEM is doing. If so, delete TEM. Otherwise, make this - into a REG_UNUSED note instead. */ - if (reg_set_p (XEXP (note, 0), PATTERN (tem))) - { - rtx set = single_set (tem); - - /* Verify that it was the set, and not a clobber that - modified the register. */ - - if (set != 0 && ! side_effects_p (SET_SRC (set)) - && rtx_equal_p (XEXP (note, 0), SET_DEST (set))) - { - /* Move the notes and links of TEM elsewhere. - This might delete other dead insns recursively. - First set the pattern to something that won't use - any register. */ - - PATTERN (tem) = pc_rtx; - - distribute_notes (REG_NOTES (tem), tem, tem, - NULL_RTX, NULL_RTX, NULL_RTX); - distribute_links (LOG_LINKS (tem)); - - PUT_CODE (tem, NOTE); - NOTE_LINE_NUMBER (tem) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (tem) = 0; - } - else - { - PUT_REG_NOTE_KIND (note, REG_UNUSED); - - /* If there isn't already a REG_UNUSED note, put one - here. */ - if (! find_regno_note (tem, REG_UNUSED, - REGNO (XEXP (note, 0)))) - place = tem; - break; - } - } - else if (reg_referenced_p (XEXP (note, 0), PATTERN (tem))) - { - place = tem; - break; - } - } - - /* If the register is set or already dead at PLACE, we needn't do - anything with this note if it is still a REG_DEAD note. - - Note that we cannot use just `dead_or_set_p' here since we can - convert an assignment to a register into a bit-field assignment. - Therefore, we must also omit the note if the register is the - target of a bitfield assignment. */ - - if (place && REG_NOTE_KIND (note) == REG_DEAD) - { - int regno = REGNO (XEXP (note, 0)); - - if (dead_or_set_p (place, XEXP (note, 0)) - || reg_bitfield_target_p (XEXP (note, 0), PATTERN (place))) - { - /* Unless the register previously died in PLACE, clear - reg_last_death. [I no longer understand why this is - being done.] */ - if (reg_last_death[regno] != place) - reg_last_death[regno] = 0; - place = 0; - } - else - reg_last_death[regno] = place; - - /* If this is a death note for a hard reg that is occupying - multiple registers, ensure that we are still using all - parts of the object. If we find a piece of the object - that is unused, we must add a USE for that piece before - PLACE and put the appropriate REG_DEAD note on it. - - An alternative would be to put a REG_UNUSED for the pieces - on the insn that set the register, but that can't be done if - it is not in the same block. It is simpler, though less - efficient, to add the USE insns. */ - - if (place && regno < FIRST_PSEUDO_REGISTER - && HARD_REGNO_NREGS (regno, GET_MODE (XEXP (note, 0))) > 1) - { - int endregno - = regno + HARD_REGNO_NREGS (regno, - GET_MODE (XEXP (note, 0))); - int all_used = 1; - int i; - - for (i = regno; i < endregno; i++) - if (! refers_to_regno_p (i, i + 1, PATTERN (place), 0)) - { - rtx piece = gen_rtx (REG, word_mode, i); - rtx p; - - /* See if we already placed a USE note for this - register in front of PLACE. */ - for (p = place; - GET_CODE (PREV_INSN (p)) == INSN - && GET_CODE (PATTERN (PREV_INSN (p))) == USE; - p = PREV_INSN (p)) - if (rtx_equal_p (piece, - XEXP (PATTERN (PREV_INSN (p)), 0))) - { - p = 0; - break; - } - - if (p) - { - rtx use_insn - = emit_insn_before (gen_rtx (USE, VOIDmode, - piece), - p); - REG_NOTES (use_insn) - = gen_rtx (EXPR_LIST, REG_DEAD, piece, - REG_NOTES (use_insn)); - } - - all_used = 0; - } - - /* Check for the case where the register dying partially - overlaps the register set by this insn. */ - if (all_used) - for (i = regno; i < endregno; i++) - if (dead_or_set_regno_p (place, i)) - { - all_used = 0; - break; - } - - if (! all_used) - { - /* Put only REG_DEAD notes for pieces that are - still used and that are not already dead or set. */ - - for (i = regno; i < endregno; i++) - { - rtx piece = gen_rtx (REG, word_mode, i); - - if (reg_referenced_p (piece, PATTERN (place)) - && ! dead_or_set_p (place, piece) - && ! reg_bitfield_target_p (piece, - PATTERN (place))) - REG_NOTES (place) = gen_rtx (EXPR_LIST, REG_DEAD, - piece, - REG_NOTES (place)); - } - - place = 0; - } - } - } - break; - - default: - /* Any other notes should not be present at this point in the - compilation. */ - abort (); - } - - if (place) - { - XEXP (note, 1) = REG_NOTES (place); - REG_NOTES (place) = note; - } - else if ((REG_NOTE_KIND (note) == REG_DEAD - || REG_NOTE_KIND (note) == REG_UNUSED) - && GET_CODE (XEXP (note, 0)) == REG) - reg_n_deaths[REGNO (XEXP (note, 0))]--; - - if (place2) - { - if ((REG_NOTE_KIND (note) == REG_DEAD - || REG_NOTE_KIND (note) == REG_UNUSED) - && GET_CODE (XEXP (note, 0)) == REG) - reg_n_deaths[REGNO (XEXP (note, 0))]++; - - REG_NOTES (place2) = gen_rtx (GET_CODE (note), REG_NOTE_KIND (note), - XEXP (note, 0), REG_NOTES (place2)); - } - } -} - -/* Similarly to above, distribute the LOG_LINKS that used to be present on - I3, I2, and I1 to new locations. This is also called in one case to - add a link pointing at I3 when I3's destination is changed. */ - -static void -distribute_links (links) - rtx links; -{ - rtx link, next_link; - - for (link = links; link; link = next_link) - { - rtx place = 0; - rtx insn; - rtx set, reg; - - next_link = XEXP (link, 1); - - /* If the insn that this link points to is a NOTE or isn't a single - set, ignore it. In the latter case, it isn't clear what we - can do other than ignore the link, since we can't tell which - register it was for. Such links wouldn't be used by combine - anyway. - - It is not possible for the destination of the target of the link to - have been changed by combine. The only potential of this is if we - replace I3, I2, and I1 by I3 and I2. But in that case the - destination of I2 also remains unchanged. */ - - if (GET_CODE (XEXP (link, 0)) == NOTE - || (set = single_set (XEXP (link, 0))) == 0) - continue; - - reg = SET_DEST (set); - while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == ZERO_EXTRACT - || GET_CODE (reg) == SIGN_EXTRACT - || GET_CODE (reg) == STRICT_LOW_PART) - reg = XEXP (reg, 0); - - /* A LOG_LINK is defined as being placed on the first insn that uses - a register and points to the insn that sets the register. Start - searching at the next insn after the target of the link and stop - when we reach a set of the register or the end of the basic block. - - Note that this correctly handles the link that used to point from - I3 to I2. Also note that not much searching is typically done here - since most links don't point very far away. */ - - for (insn = NEXT_INSN (XEXP (link, 0)); - (insn && GET_CODE (insn) != CODE_LABEL - && GET_CODE (PREV_INSN (insn)) != JUMP_INSN); - insn = NEXT_INSN (insn)) - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && reg_overlap_mentioned_p (reg, PATTERN (insn))) - { - if (reg_referenced_p (reg, PATTERN (insn))) - place = insn; - break; - } - - /* If we found a place to put the link, place it there unless there - is already a link to the same insn as LINK at that point. */ - - if (place) - { - rtx link2; - - for (link2 = LOG_LINKS (place); link2; link2 = XEXP (link2, 1)) - if (XEXP (link2, 0) == XEXP (link, 0)) - break; - - if (link2 == 0) - { - XEXP (link, 1) = LOG_LINKS (place); - LOG_LINKS (place) = link; - } - } - } -} - -void -dump_combine_stats (file) - FILE *file; -{ - fprintf - (file, - ";; Combiner statistics: %d attempts, %d substitutions (%d requiring new space),\n;; %d successes.\n\n", - combine_attempts, combine_merges, combine_extras, combine_successes); -} - -void -dump_combine_total_stats (file) - FILE *file; -{ - fprintf - (file, - "\n;; Combiner totals: %d attempts, %d substitutions (%d requiring new space),\n;; %d successes.\n", - total_attempts, total_merges, total_extras, total_successes); -} diff --git a/gnu/usr.bin/cc/common/cse.c b/gnu/usr.bin/cc/common/cse.c deleted file mode 100644 index 8ae9714276..0000000000 --- a/gnu/usr.bin/cc/common/cse.c +++ /dev/null @@ -1,8234 +0,0 @@ -/* Common subexpression elimination for GNU compiler. - Copyright (C) 1987, 1988, 1989, 1992. 1993 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "config.h" -#include "rtl.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "flags.h" -#include "real.h" -#include "insn-config.h" -#include "recog.h" - -#include -#include - -/* The basic idea of common subexpression elimination is to go - through the code, keeping a record of expressions that would - have the same value at the current scan point, and replacing - expressions encountered with the cheapest equivalent expression. - - It is too complicated to keep track of the different possibilities - when control paths merge; so, at each label, we forget all that is - known and start fresh. This can be described as processing each - basic block separately. Note, however, that these are not quite - the same as the basic blocks found by a later pass and used for - data flow analysis and register packing. We do not need to start fresh - after a conditional jump instruction if there is no label there. - - We use two data structures to record the equivalent expressions: - a hash table for most expressions, and several vectors together - with "quantity numbers" to record equivalent (pseudo) registers. - - The use of the special data structure for registers is desirable - because it is faster. It is possible because registers references - contain a fairly small number, the register number, taken from - a contiguously allocated series, and two register references are - identical if they have the same number. General expressions - do not have any such thing, so the only way to retrieve the - information recorded on an expression other than a register - is to keep it in a hash table. - -Registers and "quantity numbers": - - At the start of each basic block, all of the (hardware and pseudo) - registers used in the function are given distinct quantity - numbers to indicate their contents. During scan, when the code - copies one register into another, we copy the quantity number. - When a register is loaded in any other way, we allocate a new - quantity number to describe the value generated by this operation. - `reg_qty' records what quantity a register is currently thought - of as containing. - - All real quantity numbers are greater than or equal to `max_reg'. - If register N has not been assigned a quantity, reg_qty[N] will equal N. - - Quantity numbers below `max_reg' do not exist and none of the `qty_...' - variables should be referenced with an index below `max_reg'. - - We also maintain a bidirectional chain of registers for each - quantity number. `qty_first_reg', `qty_last_reg', - `reg_next_eqv' and `reg_prev_eqv' hold these chains. - - The first register in a chain is the one whose lifespan is least local. - Among equals, it is the one that was seen first. - We replace any equivalent register with that one. - - If two registers have the same quantity number, it must be true that - REG expressions with `qty_mode' must be in the hash table for both - registers and must be in the same class. - - The converse is not true. Since hard registers may be referenced in - any mode, two REG expressions might be equivalent in the hash table - but not have the same quantity number if the quantity number of one - of the registers is not the same mode as those expressions. - -Constants and quantity numbers - - When a quantity has a known constant value, that value is stored - in the appropriate element of qty_const. This is in addition to - putting the constant in the hash table as is usual for non-regs. - - Whether a reg or a constant is preferred is determined by the configuration - macro CONST_COSTS and will often depend on the constant value. In any - event, expressions containing constants can be simplified, by fold_rtx. - - When a quantity has a known nearly constant value (such as an address - of a stack slot), that value is stored in the appropriate element - of qty_const. - - Integer constants don't have a machine mode. However, cse - determines the intended machine mode from the destination - of the instruction that moves the constant. The machine mode - is recorded in the hash table along with the actual RTL - constant expression so that different modes are kept separate. - -Other expressions: - - To record known equivalences among expressions in general - we use a hash table called `table'. It has a fixed number of buckets - that contain chains of `struct table_elt' elements for expressions. - These chains connect the elements whose expressions have the same - hash codes. - - Other chains through the same elements connect the elements which - currently have equivalent values. - - Register references in an expression are canonicalized before hashing - the expression. This is done using `reg_qty' and `qty_first_reg'. - The hash code of a register reference is computed using the quantity - number, not the register number. - - When the value of an expression changes, it is necessary to remove from the - hash table not just that expression but all expressions whose values - could be different as a result. - - 1. If the value changing is in memory, except in special cases - ANYTHING referring to memory could be changed. That is because - nobody knows where a pointer does not point. - The function `invalidate_memory' removes what is necessary. - - The special cases are when the address is constant or is - a constant plus a fixed register such as the frame pointer - or a static chain pointer. When such addresses are stored in, - we can tell exactly which other such addresses must be invalidated - due to overlap. `invalidate' does this. - All expressions that refer to non-constant - memory addresses are also invalidated. `invalidate_memory' does this. - - 2. If the value changing is a register, all expressions - containing references to that register, and only those, - must be removed. - - Because searching the entire hash table for expressions that contain - a register is very slow, we try to figure out when it isn't necessary. - Precisely, this is necessary only when expressions have been - entered in the hash table using this register, and then the value has - changed, and then another expression wants to be added to refer to - the register's new value. This sequence of circumstances is rare - within any one basic block. - - The vectors `reg_tick' and `reg_in_table' are used to detect this case. - reg_tick[i] is incremented whenever a value is stored in register i. - reg_in_table[i] holds -1 if no references to register i have been - entered in the table; otherwise, it contains the value reg_tick[i] had - when the references were entered. If we want to enter a reference - and reg_in_table[i] != reg_tick[i], we must scan and remove old references. - Until we want to enter a new entry, the mere fact that the two vectors - don't match makes the entries be ignored if anyone tries to match them. - - Registers themselves are entered in the hash table as well as in - the equivalent-register chains. However, the vectors `reg_tick' - and `reg_in_table' do not apply to expressions which are simple - register references. These expressions are removed from the table - immediately when they become invalid, and this can be done even if - we do not immediately search for all the expressions that refer to - the register. - - A CLOBBER rtx in an instruction invalidates its operand for further - reuse. A CLOBBER or SET rtx whose operand is a MEM:BLK - invalidates everything that resides in memory. - -Related expressions: - - Constant expressions that differ only by an additive integer - are called related. When a constant expression is put in - the table, the related expression with no constant term - is also entered. These are made to point at each other - so that it is possible to find out if there exists any - register equivalent to an expression related to a given expression. */ - -/* One plus largest register number used in this function. */ - -static int max_reg; - -/* Length of vectors indexed by quantity number. - We know in advance we will not need a quantity number this big. */ - -static int max_qty; - -/* Next quantity number to be allocated. - This is 1 + the largest number needed so far. */ - -static int next_qty; - -/* Indexed by quantity number, gives the first (or last) (pseudo) register - in the chain of registers that currently contain this quantity. */ - -static int *qty_first_reg; -static int *qty_last_reg; - -/* Index by quantity number, gives the mode of the quantity. */ - -static enum machine_mode *qty_mode; - -/* Indexed by quantity number, gives the rtx of the constant value of the - quantity, or zero if it does not have a known value. - A sum of the frame pointer (or arg pointer) plus a constant - can also be entered here. */ - -static rtx *qty_const; - -/* Indexed by qty number, gives the insn that stored the constant value - recorded in `qty_const'. */ - -static rtx *qty_const_insn; - -/* The next three variables are used to track when a comparison between a - quantity and some constant or register has been passed. In that case, we - know the results of the comparison in case we see it again. These variables - record a comparison that is known to be true. */ - -/* Indexed by qty number, gives the rtx code of a comparison with a known - result involving this quantity. If none, it is UNKNOWN. */ -static enum rtx_code *qty_comparison_code; - -/* Indexed by qty number, gives the constant being compared against in a - comparison of known result. If no such comparison, it is undefined. - If the comparison is not with a constant, it is zero. */ - -static rtx *qty_comparison_const; - -/* Indexed by qty number, gives the quantity being compared against in a - comparison of known result. If no such comparison, if it undefined. - If the comparison is not with a register, it is -1. */ - -static int *qty_comparison_qty; - -#ifdef HAVE_cc0 -/* For machines that have a CC0, we do not record its value in the hash - table since its use is guaranteed to be the insn immediately following - its definition and any other insn is presumed to invalidate it. - - Instead, we store below the value last assigned to CC0. If it should - happen to be a constant, it is stored in preference to the actual - assigned value. In case it is a constant, we store the mode in which - the constant should be interpreted. */ - -static rtx prev_insn_cc0; -static enum machine_mode prev_insn_cc0_mode; -#endif - -/* Previous actual insn. 0 if at first insn of basic block. */ - -static rtx prev_insn; - -/* Insn being scanned. */ - -static rtx this_insn; - -/* Index by (pseudo) register number, gives the quantity number - of the register's current contents. */ - -static int *reg_qty; - -/* Index by (pseudo) register number, gives the number of the next (or - previous) (pseudo) register in the chain of registers sharing the same - value. - - Or -1 if this register is at the end of the chain. - - If reg_qty[N] == N, reg_next_eqv[N] is undefined. */ - -static int *reg_next_eqv; -static int *reg_prev_eqv; - -/* Index by (pseudo) register number, gives the number of times - that register has been altered in the current basic block. */ - -static int *reg_tick; - -/* Index by (pseudo) register number, gives the reg_tick value at which - rtx's containing this register are valid in the hash table. - If this does not equal the current reg_tick value, such expressions - existing in the hash table are invalid. - If this is -1, no expressions containing this register have been - entered in the table. */ - -static int *reg_in_table; - -/* A HARD_REG_SET containing all the hard registers for which there is - currently a REG expression in the hash table. Note the difference - from the above variables, which indicate if the REG is mentioned in some - expression in the table. */ - -static HARD_REG_SET hard_regs_in_table; - -/* A HARD_REG_SET containing all the hard registers that are invalidated - by a CALL_INSN. */ - -static HARD_REG_SET regs_invalidated_by_call; - -/* Two vectors of ints: - one containing max_reg -1's; the other max_reg + 500 (an approximation - for max_qty) elements where element i contains i. - These are used to initialize various other vectors fast. */ - -static int *all_minus_one; -static int *consec_ints; - -/* CUID of insn that starts the basic block currently being cse-processed. */ - -static int cse_basic_block_start; - -/* CUID of insn that ends the basic block currently being cse-processed. */ - -static int cse_basic_block_end; - -/* Vector mapping INSN_UIDs to cuids. - The cuids are like uids but increase monotonically always. - We use them to see whether a reg is used outside a given basic block. */ - -static int *uid_cuid; - -/* Highest UID in UID_CUID. */ -static int max_uid; - -/* Get the cuid of an insn. */ - -#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)]) - -/* Nonzero if cse has altered conditional jump insns - in such a way that jump optimization should be redone. */ - -static int cse_jumps_altered; - -/* canon_hash stores 1 in do_not_record - if it notices a reference to CC0, PC, or some other volatile - subexpression. */ - -static int do_not_record; - -/* canon_hash stores 1 in hash_arg_in_memory - if it notices a reference to memory within the expression being hashed. */ - -static int hash_arg_in_memory; - -/* canon_hash stores 1 in hash_arg_in_struct - if it notices a reference to memory that's part of a structure. */ - -static int hash_arg_in_struct; - -/* The hash table contains buckets which are chains of `struct table_elt's, - each recording one expression's information. - That expression is in the `exp' field. - - Those elements with the same hash code are chained in both directions - through the `next_same_hash' and `prev_same_hash' fields. - - Each set of expressions with equivalent values - are on a two-way chain through the `next_same_value' - and `prev_same_value' fields, and all point with - the `first_same_value' field at the first element in - that chain. The chain is in order of increasing cost. - Each element's cost value is in its `cost' field. - - The `in_memory' field is nonzero for elements that - involve any reference to memory. These elements are removed - whenever a write is done to an unidentified location in memory. - To be safe, we assume that a memory address is unidentified unless - the address is either a symbol constant or a constant plus - the frame pointer or argument pointer. - - The `in_struct' field is nonzero for elements that - involve any reference to memory inside a structure or array. - - The `related_value' field is used to connect related expressions - (that differ by adding an integer). - The related expressions are chained in a circular fashion. - `related_value' is zero for expressions for which this - chain is not useful. - - The `cost' field stores the cost of this element's expression. - - The `is_const' flag is set if the element is a constant (including - a fixed address). - - The `flag' field is used as a temporary during some search routines. - - The `mode' field is usually the same as GET_MODE (`exp'), but - if `exp' is a CONST_INT and has no machine mode then the `mode' - field is the mode it was being used as. Each constant is - recorded separately for each mode it is used with. */ - - -struct table_elt -{ - rtx exp; - struct table_elt *next_same_hash; - struct table_elt *prev_same_hash; - struct table_elt *next_same_value; - struct table_elt *prev_same_value; - struct table_elt *first_same_value; - struct table_elt *related_value; - int cost; - enum machine_mode mode; - char in_memory; - char in_struct; - char is_const; - char flag; -}; - -#define HASHBITS 16 - -/* We don't want a lot of buckets, because we rarely have very many - things stored in the hash table, and a lot of buckets slows - down a lot of loops that happen frequently. */ -#define NBUCKETS 31 - -/* Compute hash code of X in mode M. Special-case case where X is a pseudo - register (hard registers may require `do_not_record' to be set). */ - -#define HASH(X, M) \ - (GET_CODE (X) == REG && REGNO (X) >= FIRST_PSEUDO_REGISTER \ - ? ((((int) REG << 7) + reg_qty[REGNO (X)]) % NBUCKETS) \ - : canon_hash (X, M) % NBUCKETS) - -/* Determine whether register number N is considered a fixed register for CSE. - It is desirable to replace other regs with fixed regs, to reduce need for - non-fixed hard regs. - A reg wins if it is either the frame pointer or designated as fixed, - but not if it is an overlapping register. */ -#ifdef OVERLAPPING_REGNO_P -#define FIXED_REGNO_P(N) \ - (((N) == FRAME_POINTER_REGNUM || fixed_regs[N]) \ - && ! OVERLAPPING_REGNO_P ((N))) -#else -#define FIXED_REGNO_P(N) \ - ((N) == FRAME_POINTER_REGNUM || fixed_regs[N]) -#endif - -/* Compute cost of X, as stored in the `cost' field of a table_elt. Fixed - hard registers and pointers into the frame are the cheapest with a cost - of 0. Next come pseudos with a cost of one and other hard registers with - a cost of 2. Aside from these special cases, call `rtx_cost'. */ - -#define CHEAP_REG(N) \ - ((N) == FRAME_POINTER_REGNUM || (N) == STACK_POINTER_REGNUM \ - || (N) == ARG_POINTER_REGNUM \ - || ((N) >= FIRST_VIRTUAL_REGISTER && (N) <= LAST_VIRTUAL_REGISTER) \ - || ((N) < FIRST_PSEUDO_REGISTER \ - && FIXED_REGNO_P (N) && REGNO_REG_CLASS (N) != NO_REGS)) - -#define COST(X) \ - (GET_CODE (X) == REG \ - ? (CHEAP_REG (REGNO (X)) ? 0 \ - : REGNO (X) >= FIRST_PSEUDO_REGISTER ? 1 \ - : 2) \ - : rtx_cost (X, SET) * 2) - -/* Determine if the quantity number for register X represents a valid index - into the `qty_...' variables. */ - -#define REGNO_QTY_VALID_P(N) (reg_qty[N] != (N)) - -static struct table_elt *table[NBUCKETS]; - -/* Chain of `struct table_elt's made so far for this function - but currently removed from the table. */ - -static struct table_elt *free_element_chain; - -/* Number of `struct table_elt' structures made so far for this function. */ - -static int n_elements_made; - -/* Maximum value `n_elements_made' has had so far in this compilation - for functions previously processed. */ - -static int max_elements_made; - -/* Surviving equivalence class when two equivalence classes are merged - by recording the effects of a jump in the last insn. Zero if the - last insn was not a conditional jump. */ - -static struct table_elt *last_jump_equiv_class; - -/* Set to the cost of a constant pool reference if one was found for a - symbolic constant. If this was found, it means we should try to - convert constants into constant pool entries if they don't fit in - the insn. */ - -static int constant_pool_entries_cost; - -/* Bits describing what kind of values in memory must be invalidated - for a particular instruction. If all three bits are zero, - no memory refs need to be invalidated. Each bit is more powerful - than the preceding ones, and if a bit is set then the preceding - bits are also set. - - Here is how the bits are set: - Pushing onto the stack invalidates only the stack pointer, - writing at a fixed address invalidates only variable addresses, - writing in a structure element at variable address - invalidates all but scalar variables, - and writing in anything else at variable address invalidates everything. */ - -struct write_data -{ - int sp : 1; /* Invalidate stack pointer. */ - int var : 1; /* Invalidate variable addresses. */ - int nonscalar : 1; /* Invalidate all but scalar variables. */ - int all : 1; /* Invalidate all memory refs. */ -}; - -/* Define maximum length of a branch path. */ - -#define PATHLENGTH 10 - -/* This data describes a block that will be processed by cse_basic_block. */ - -struct cse_basic_block_data { - /* Lowest CUID value of insns in block. */ - int low_cuid; - /* Highest CUID value of insns in block. */ - int high_cuid; - /* Total number of SETs in block. */ - int nsets; - /* Last insn in the block. */ - rtx last; - /* Size of current branch path, if any. */ - int path_size; - /* Current branch path, indicating which branches will be taken. */ - struct branch_path { - /* The branch insn. */ - rtx branch; - /* Whether it should be taken or not. AROUND is the same as taken - except that it is used when the destination label is not preceded - by a BARRIER. */ - enum taken {TAKEN, NOT_TAKEN, AROUND} status; - } path[PATHLENGTH]; -}; - -/* Nonzero if X has the form (PLUS frame-pointer integer). We check for - virtual regs here because the simplify_*_operation routines are called - by integrate.c, which is called before virtual register instantiation. */ - -#define FIXED_BASE_PLUS_P(X) \ - ((X) == frame_pointer_rtx || (X) == arg_pointer_rtx \ - || (X) == virtual_stack_vars_rtx \ - || (X) == virtual_incoming_args_rtx \ - || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \ - && (XEXP (X, 0) == frame_pointer_rtx \ - || XEXP (X, 0) == arg_pointer_rtx \ - || XEXP (X, 0) == virtual_stack_vars_rtx \ - || XEXP (X, 0) == virtual_incoming_args_rtx))) - -/* Similar, but also allows reference to the stack pointer. - - This used to include FIXED_BASE_PLUS_P, however, we can't assume that - arg_pointer_rtx by itself is nonzero, because on at least one machine, - the i960, the arg pointer is zero when it is unused. */ - -#define NONZERO_BASE_PLUS_P(X) \ - ((X) == frame_pointer_rtx \ - || (X) == virtual_stack_vars_rtx \ - || (X) == virtual_incoming_args_rtx \ - || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \ - && (XEXP (X, 0) == frame_pointer_rtx \ - || XEXP (X, 0) == arg_pointer_rtx \ - || XEXP (X, 0) == virtual_stack_vars_rtx \ - || XEXP (X, 0) == virtual_incoming_args_rtx)) \ - || (X) == stack_pointer_rtx \ - || (X) == virtual_stack_dynamic_rtx \ - || (X) == virtual_outgoing_args_rtx \ - || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \ - && (XEXP (X, 0) == stack_pointer_rtx \ - || XEXP (X, 0) == virtual_stack_dynamic_rtx \ - || XEXP (X, 0) == virtual_outgoing_args_rtx))) - -static void new_basic_block PROTO((void)); -static void make_new_qty PROTO((int)); -static void make_regs_eqv PROTO((int, int)); -static void delete_reg_equiv PROTO((int)); -static int mention_regs PROTO((rtx)); -static int insert_regs PROTO((rtx, struct table_elt *, int)); -static void free_element PROTO((struct table_elt *)); -static void remove_from_table PROTO((struct table_elt *, int)); -static struct table_elt *get_element PROTO((void)); -static struct table_elt *lookup PROTO((rtx, int, enum machine_mode)), - *lookup_for_remove PROTO((rtx, int, enum machine_mode)); -static rtx lookup_as_function PROTO((rtx, enum rtx_code)); -static struct table_elt *insert PROTO((rtx, struct table_elt *, int, - enum machine_mode)); -static void merge_equiv_classes PROTO((struct table_elt *, - struct table_elt *)); -static void invalidate PROTO((rtx)); -static void remove_invalid_refs PROTO((int)); -static void rehash_using_reg PROTO((rtx)); -static void invalidate_memory PROTO((struct write_data *)); -static void invalidate_for_call PROTO((void)); -static rtx use_related_value PROTO((rtx, struct table_elt *)); -static int canon_hash PROTO((rtx, enum machine_mode)); -static int safe_hash PROTO((rtx, enum machine_mode)); -static int exp_equiv_p PROTO((rtx, rtx, int, int)); -static void set_nonvarying_address_components PROTO((rtx, int, rtx *, - HOST_WIDE_INT *, - HOST_WIDE_INT *)); -static int refers_to_p PROTO((rtx, rtx)); -static int refers_to_mem_p PROTO((rtx, rtx, HOST_WIDE_INT, - HOST_WIDE_INT)); -static int cse_rtx_addr_varies_p PROTO((rtx)); -static rtx canon_reg PROTO((rtx, rtx)); -static void find_best_addr PROTO((rtx, rtx *)); -static enum rtx_code find_comparison_args PROTO((enum rtx_code, rtx *, rtx *, - enum machine_mode *, - enum machine_mode *)); -static rtx cse_gen_binary PROTO((enum rtx_code, enum machine_mode, - rtx, rtx)); -static rtx simplify_plus_minus PROTO((enum rtx_code, enum machine_mode, - rtx, rtx)); -static rtx fold_rtx PROTO((rtx, rtx)); -static rtx equiv_constant PROTO((rtx)); -static void record_jump_equiv PROTO((rtx, int)); -static void record_jump_cond PROTO((enum rtx_code, enum machine_mode, - rtx, rtx, int)); -static void cse_insn PROTO((rtx, int)); -static void note_mem_written PROTO((rtx, struct write_data *)); -static void invalidate_from_clobbers PROTO((struct write_data *, rtx)); -static rtx cse_process_notes PROTO((rtx, rtx)); -static void cse_around_loop PROTO((rtx)); -static void invalidate_skipped_set PROTO((rtx, rtx)); -static void invalidate_skipped_block PROTO((rtx)); -static void cse_check_loop_start PROTO((rtx, rtx)); -static void cse_set_around_loop PROTO((rtx, rtx, rtx)); -static rtx cse_basic_block PROTO((rtx, rtx, struct branch_path *, int)); -static void count_reg_usage PROTO((rtx, int *, int)); - -/* Return an estimate of the cost of computing rtx X. - One use is in cse, to decide which expression to keep in the hash table. - Another is in rtl generation, to pick the cheapest way to multiply. - Other uses like the latter are expected in the future. */ - -/* Return the right cost to give to an operation - to make the cost of the corresponding register-to-register instruction - N times that of a fast register-to-register instruction. */ - -#define COSTS_N_INSNS(N) ((N) * 4 - 2) - -int -rtx_cost (x, outer_code) - rtx x; - enum rtx_code outer_code; -{ - register int i, j; - register enum rtx_code code; - register char *fmt; - register int total; - - if (x == 0) - return 0; - - /* Compute the default costs of certain things. - Note that RTX_COSTS can override the defaults. */ - - code = GET_CODE (x); - switch (code) - { - case MULT: - /* Count multiplication by 2**n as a shift, - because if we are considering it, we would output it as a shift. */ - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && exact_log2 (INTVAL (XEXP (x, 1))) >= 0) - total = 2; - else - total = COSTS_N_INSNS (5); - break; - case DIV: - case UDIV: - case MOD: - case UMOD: - total = COSTS_N_INSNS (7); - break; - case USE: - /* Used in loop.c and combine.c as a marker. */ - total = 0; - break; - case ASM_OPERANDS: - /* We don't want these to be used in substitutions because - we have no way of validating the resulting insn. So assign - anything containing an ASM_OPERANDS a very high cost. */ - total = 1000; - break; - default: - total = 2; - } - - switch (code) - { - case REG: - return ! CHEAP_REG (REGNO (x)); - - case SUBREG: - /* If we can't tie these modes, make this expensive. The larger - the mode, the more expensive it is. */ - if (! MODES_TIEABLE_P (GET_MODE (x), GET_MODE (SUBREG_REG (x)))) - return COSTS_N_INSNS (2 - + GET_MODE_SIZE (GET_MODE (x)) / UNITS_PER_WORD); - return 2; -#ifdef RTX_COSTS - RTX_COSTS (x, code, outer_code); -#endif - CONST_COSTS (x, code, outer_code); - } - - /* Sum the costs of the sub-rtx's, plus cost of this operation, - which is already in total. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - total += rtx_cost (XEXP (x, i), code); - else if (fmt[i] == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - total += rtx_cost (XVECEXP (x, i, j), code); - - return total; -} - -/* Clear the hash table and initialize each register with its own quantity, - for a new basic block. */ - -static void -new_basic_block () -{ - register int i; - - next_qty = max_reg; - - bzero (reg_tick, max_reg * sizeof (int)); - - bcopy (all_minus_one, reg_in_table, max_reg * sizeof (int)); - bcopy (consec_ints, reg_qty, max_reg * sizeof (int)); - CLEAR_HARD_REG_SET (hard_regs_in_table); - - /* The per-quantity values used to be initialized here, but it is - much faster to initialize each as it is made in `make_new_qty'. */ - - for (i = 0; i < NBUCKETS; i++) - { - register struct table_elt *this, *next; - for (this = table[i]; this; this = next) - { - next = this->next_same_hash; - free_element (this); - } - } - - bzero (table, sizeof table); - - prev_insn = 0; - -#ifdef HAVE_cc0 - prev_insn_cc0 = 0; -#endif -} - -/* Say that register REG contains a quantity not in any register before - and initialize that quantity. */ - -static void -make_new_qty (reg) - register int reg; -{ - register int q; - - if (next_qty >= max_qty) - abort (); - - q = reg_qty[reg] = next_qty++; - qty_first_reg[q] = reg; - qty_last_reg[q] = reg; - qty_const[q] = qty_const_insn[q] = 0; - qty_comparison_code[q] = UNKNOWN; - - reg_next_eqv[reg] = reg_prev_eqv[reg] = -1; -} - -/* Make reg NEW equivalent to reg OLD. - OLD is not changing; NEW is. */ - -static void -make_regs_eqv (new, old) - register int new, old; -{ - register int lastr, firstr; - register int q = reg_qty[old]; - - /* Nothing should become eqv until it has a "non-invalid" qty number. */ - if (! REGNO_QTY_VALID_P (old)) - abort (); - - reg_qty[new] = q; - firstr = qty_first_reg[q]; - lastr = qty_last_reg[q]; - - /* Prefer fixed hard registers to anything. Prefer pseudo regs to other - hard regs. Among pseudos, if NEW will live longer than any other reg - of the same qty, and that is beyond the current basic block, - make it the new canonical replacement for this qty. */ - if (! (firstr < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (firstr)) - /* Certain fixed registers might be of the class NO_REGS. This means - that not only can they not be allocated by the compiler, but - they cannot be used in substitutions or canonicalizations - either. */ - && (new >= FIRST_PSEUDO_REGISTER || REGNO_REG_CLASS (new) != NO_REGS) - && ((new < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (new)) - || (new >= FIRST_PSEUDO_REGISTER - && (firstr < FIRST_PSEUDO_REGISTER - || ((uid_cuid[regno_last_uid[new]] > cse_basic_block_end - || (uid_cuid[regno_first_uid[new]] - < cse_basic_block_start)) - && (uid_cuid[regno_last_uid[new]] - > uid_cuid[regno_last_uid[firstr]])))))) - { - reg_prev_eqv[firstr] = new; - reg_next_eqv[new] = firstr; - reg_prev_eqv[new] = -1; - qty_first_reg[q] = new; - } - else - { - /* If NEW is a hard reg (known to be non-fixed), insert at end. - Otherwise, insert before any non-fixed hard regs that are at the - end. Registers of class NO_REGS cannot be used as an - equivalent for anything. */ - while (lastr < FIRST_PSEUDO_REGISTER && reg_prev_eqv[lastr] >= 0 - && (REGNO_REG_CLASS (lastr) == NO_REGS || ! FIXED_REGNO_P (lastr)) - && new >= FIRST_PSEUDO_REGISTER) - lastr = reg_prev_eqv[lastr]; - reg_next_eqv[new] = reg_next_eqv[lastr]; - if (reg_next_eqv[lastr] >= 0) - reg_prev_eqv[reg_next_eqv[lastr]] = new; - else - qty_last_reg[q] = new; - reg_next_eqv[lastr] = new; - reg_prev_eqv[new] = lastr; - } -} - -/* Remove REG from its equivalence class. */ - -static void -delete_reg_equiv (reg) - register int reg; -{ - register int n = reg_next_eqv[reg]; - register int p = reg_prev_eqv[reg]; - register int q = reg_qty[reg]; - - /* If invalid, do nothing. N and P above are undefined in that case. */ - if (q == reg) - return; - - if (n != -1) - reg_prev_eqv[n] = p; - else - qty_last_reg[q] = p; - if (p != -1) - reg_next_eqv[p] = n; - else - qty_first_reg[q] = n; - - reg_qty[reg] = reg; -} - -/* Remove any invalid expressions from the hash table - that refer to any of the registers contained in expression X. - - Make sure that newly inserted references to those registers - as subexpressions will be considered valid. - - mention_regs is not called when a register itself - is being stored in the table. - - Return 1 if we have done something that may have changed the hash code - of X. */ - -static int -mention_regs (x) - rtx x; -{ - register enum rtx_code code; - register int i, j; - register char *fmt; - register int changed = 0; - - if (x == 0) - return 0; - - code = GET_CODE (x); - if (code == REG) - { - register int regno = REGNO (x); - register int endregno - = regno + (regno >= FIRST_PSEUDO_REGISTER ? 1 - : HARD_REGNO_NREGS (regno, GET_MODE (x))); - int i; - - for (i = regno; i < endregno; i++) - { - if (reg_in_table[i] >= 0 && reg_in_table[i] != reg_tick[i]) - remove_invalid_refs (i); - - reg_in_table[i] = reg_tick[i]; - } - - return 0; - } - - /* If X is a comparison or a COMPARE and either operand is a register - that does not have a quantity, give it one. This is so that a later - call to record_jump_equiv won't cause X to be assigned a different - hash code and not found in the table after that call. - - It is not necessary to do this here, since rehash_using_reg can - fix up the table later, but doing this here eliminates the need to - call that expensive function in the most common case where the only - use of the register is in the comparison. */ - - if (code == COMPARE || GET_RTX_CLASS (code) == '<') - { - if (GET_CODE (XEXP (x, 0)) == REG - && ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 0)))) - if (insert_regs (XEXP (x, 0), NULL_PTR, 0)) - { - rehash_using_reg (XEXP (x, 0)); - changed = 1; - } - - if (GET_CODE (XEXP (x, 1)) == REG - && ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 1)))) - if (insert_regs (XEXP (x, 1), NULL_PTR, 0)) - { - rehash_using_reg (XEXP (x, 1)); - changed = 1; - } - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - changed |= mention_regs (XEXP (x, i)); - else if (fmt[i] == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - changed |= mention_regs (XVECEXP (x, i, j)); - - return changed; -} - -/* Update the register quantities for inserting X into the hash table - with a value equivalent to CLASSP. - (If the class does not contain a REG, it is irrelevant.) - If MODIFIED is nonzero, X is a destination; it is being modified. - Note that delete_reg_equiv should be called on a register - before insert_regs is done on that register with MODIFIED != 0. - - Nonzero value means that elements of reg_qty have changed - so X's hash code may be different. */ - -static int -insert_regs (x, classp, modified) - rtx x; - struct table_elt *classp; - int modified; -{ - if (GET_CODE (x) == REG) - { - register int regno = REGNO (x); - - /* If REGNO is in the equivalence table already but is of the - wrong mode for that equivalence, don't do anything here. */ - - if (REGNO_QTY_VALID_P (regno) - && qty_mode[reg_qty[regno]] != GET_MODE (x)) - return 0; - - if (modified || ! REGNO_QTY_VALID_P (regno)) - { - if (classp) - for (classp = classp->first_same_value; - classp != 0; - classp = classp->next_same_value) - if (GET_CODE (classp->exp) == REG - && GET_MODE (classp->exp) == GET_MODE (x)) - { - make_regs_eqv (regno, REGNO (classp->exp)); - return 1; - } - - make_new_qty (regno); - qty_mode[reg_qty[regno]] = GET_MODE (x); - return 1; - } - } - - /* If X is a SUBREG, we will likely be inserting the inner register in the - table. If that register doesn't have an assigned quantity number at - this point but does later, the insertion that we will be doing now will - not be accessible because its hash code will have changed. So assign - a quantity number now. */ - - else if (GET_CODE (x) == SUBREG && GET_CODE (SUBREG_REG (x)) == REG - && ! REGNO_QTY_VALID_P (REGNO (SUBREG_REG (x)))) - { - insert_regs (SUBREG_REG (x), NULL_PTR, 0); - mention_regs (SUBREG_REG (x)); - return 1; - } - else - return mention_regs (x); -} - -/* Look in or update the hash table. */ - -/* Put the element ELT on the list of free elements. */ - -static void -free_element (elt) - struct table_elt *elt; -{ - elt->next_same_hash = free_element_chain; - free_element_chain = elt; -} - -/* Return an element that is free for use. */ - -static struct table_elt * -get_element () -{ - struct table_elt *elt = free_element_chain; - if (elt) - { - free_element_chain = elt->next_same_hash; - return elt; - } - n_elements_made++; - return (struct table_elt *) oballoc (sizeof (struct table_elt)); -} - -/* Remove table element ELT from use in the table. - HASH is its hash code, made using the HASH macro. - It's an argument because often that is known in advance - and we save much time not recomputing it. */ - -static void -remove_from_table (elt, hash) - register struct table_elt *elt; - int hash; -{ - if (elt == 0) - return; - - /* Mark this element as removed. See cse_insn. */ - elt->first_same_value = 0; - - /* Remove the table element from its equivalence class. */ - - { - register struct table_elt *prev = elt->prev_same_value; - register struct table_elt *next = elt->next_same_value; - - if (next) next->prev_same_value = prev; - - if (prev) - prev->next_same_value = next; - else - { - register struct table_elt *newfirst = next; - while (next) - { - next->first_same_value = newfirst; - next = next->next_same_value; - } - } - } - - /* Remove the table element from its hash bucket. */ - - { - register struct table_elt *prev = elt->prev_same_hash; - register struct table_elt *next = elt->next_same_hash; - - if (next) next->prev_same_hash = prev; - - if (prev) - prev->next_same_hash = next; - else if (table[hash] == elt) - table[hash] = next; - else - { - /* This entry is not in the proper hash bucket. This can happen - when two classes were merged by `merge_equiv_classes'. Search - for the hash bucket that it heads. This happens only very - rarely, so the cost is acceptable. */ - for (hash = 0; hash < NBUCKETS; hash++) - if (table[hash] == elt) - table[hash] = next; - } - } - - /* Remove the table element from its related-value circular chain. */ - - if (elt->related_value != 0 && elt->related_value != elt) - { - register struct table_elt *p = elt->related_value; - while (p->related_value != elt) - p = p->related_value; - p->related_value = elt->related_value; - if (p->related_value == p) - p->related_value = 0; - } - - free_element (elt); -} - -/* Look up X in the hash table and return its table element, - or 0 if X is not in the table. - - MODE is the machine-mode of X, or if X is an integer constant - with VOIDmode then MODE is the mode with which X will be used. - - Here we are satisfied to find an expression whose tree structure - looks like X. */ - -static struct table_elt * -lookup (x, hash, mode) - rtx x; - int hash; - enum machine_mode mode; -{ - register struct table_elt *p; - - for (p = table[hash]; p; p = p->next_same_hash) - if (mode == p->mode && ((x == p->exp && GET_CODE (x) == REG) - || exp_equiv_p (x, p->exp, GET_CODE (x) != REG, 0))) - return p; - - return 0; -} - -/* Like `lookup' but don't care whether the table element uses invalid regs. - Also ignore discrepancies in the machine mode of a register. */ - -static struct table_elt * -lookup_for_remove (x, hash, mode) - rtx x; - int hash; - enum machine_mode mode; -{ - register struct table_elt *p; - - if (GET_CODE (x) == REG) - { - int regno = REGNO (x); - /* Don't check the machine mode when comparing registers; - invalidating (REG:SI 0) also invalidates (REG:DF 0). */ - for (p = table[hash]; p; p = p->next_same_hash) - if (GET_CODE (p->exp) == REG - && REGNO (p->exp) == regno) - return p; - } - else - { - for (p = table[hash]; p; p = p->next_same_hash) - if (mode == p->mode && (x == p->exp || exp_equiv_p (x, p->exp, 0, 0))) - return p; - } - - return 0; -} - -/* Look for an expression equivalent to X and with code CODE. - If one is found, return that expression. */ - -static rtx -lookup_as_function (x, code) - rtx x; - enum rtx_code code; -{ - register struct table_elt *p = lookup (x, safe_hash (x, VOIDmode) % NBUCKETS, - GET_MODE (x)); - if (p == 0) - return 0; - - for (p = p->first_same_value; p; p = p->next_same_value) - { - if (GET_CODE (p->exp) == code - /* Make sure this is a valid entry in the table. */ - && exp_equiv_p (p->exp, p->exp, 1, 0)) - return p->exp; - } - - return 0; -} - -/* Insert X in the hash table, assuming HASH is its hash code - and CLASSP is an element of the class it should go in - (or 0 if a new class should be made). - It is inserted at the proper position to keep the class in - the order cheapest first. - - MODE is the machine-mode of X, or if X is an integer constant - with VOIDmode then MODE is the mode with which X will be used. - - For elements of equal cheapness, the most recent one - goes in front, except that the first element in the list - remains first unless a cheaper element is added. The order of - pseudo-registers does not matter, as canon_reg will be called to - find the cheapest when a register is retrieved from the table. - - The in_memory field in the hash table element is set to 0. - The caller must set it nonzero if appropriate. - - You should call insert_regs (X, CLASSP, MODIFY) before calling here, - and if insert_regs returns a nonzero value - you must then recompute its hash code before calling here. - - If necessary, update table showing constant values of quantities. */ - -#define CHEAPER(X,Y) ((X)->cost < (Y)->cost) - -static struct table_elt * -insert (x, classp, hash, mode) - register rtx x; - register struct table_elt *classp; - int hash; - enum machine_mode mode; -{ - register struct table_elt *elt; - - /* If X is a register and we haven't made a quantity for it, - something is wrong. */ - if (GET_CODE (x) == REG && ! REGNO_QTY_VALID_P (REGNO (x))) - abort (); - - /* If X is a hard register, show it is being put in the table. */ - if (GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER) - { - int regno = REGNO (x); - int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x)); - int i; - - for (i = regno; i < endregno; i++) - SET_HARD_REG_BIT (hard_regs_in_table, i); - } - - - /* Put an element for X into the right hash bucket. */ - - elt = get_element (); - elt->exp = x; - elt->cost = COST (x); - elt->next_same_value = 0; - elt->prev_same_value = 0; - elt->next_same_hash = table[hash]; - elt->prev_same_hash = 0; - elt->related_value = 0; - elt->in_memory = 0; - elt->mode = mode; - elt->is_const = (CONSTANT_P (x) - /* GNU C++ takes advantage of this for `this' - (and other const values). */ - || (RTX_UNCHANGING_P (x) - && GET_CODE (x) == REG - && REGNO (x) >= FIRST_PSEUDO_REGISTER) - || FIXED_BASE_PLUS_P (x)); - - if (table[hash]) - table[hash]->prev_same_hash = elt; - table[hash] = elt; - - /* Put it into the proper value-class. */ - if (classp) - { - classp = classp->first_same_value; - if (CHEAPER (elt, classp)) - /* Insert at the head of the class */ - { - register struct table_elt *p; - elt->next_same_value = classp; - classp->prev_same_value = elt; - elt->first_same_value = elt; - - for (p = classp; p; p = p->next_same_value) - p->first_same_value = elt; - } - else - { - /* Insert not at head of the class. */ - /* Put it after the last element cheaper than X. */ - register struct table_elt *p, *next; - for (p = classp; (next = p->next_same_value) && CHEAPER (next, elt); - p = next); - /* Put it after P and before NEXT. */ - elt->next_same_value = next; - if (next) - next->prev_same_value = elt; - elt->prev_same_value = p; - p->next_same_value = elt; - elt->first_same_value = classp; - } - } - else - elt->first_same_value = elt; - - /* If this is a constant being set equivalent to a register or a register - being set equivalent to a constant, note the constant equivalence. - - If this is a constant, it cannot be equivalent to a different constant, - and a constant is the only thing that can be cheaper than a register. So - we know the register is the head of the class (before the constant was - inserted). - - If this is a register that is not already known equivalent to a - constant, we must check the entire class. - - If this is a register that is already known equivalent to an insn, - update `qty_const_insn' to show that `this_insn' is the latest - insn making that quantity equivalent to the constant. */ - - if (elt->is_const && classp && GET_CODE (classp->exp) == REG) - { - qty_const[reg_qty[REGNO (classp->exp)]] - = gen_lowpart_if_possible (qty_mode[reg_qty[REGNO (classp->exp)]], x); - qty_const_insn[reg_qty[REGNO (classp->exp)]] = this_insn; - } - - else if (GET_CODE (x) == REG && classp && ! qty_const[reg_qty[REGNO (x)]]) - { - register struct table_elt *p; - - for (p = classp; p != 0; p = p->next_same_value) - { - if (p->is_const) - { - qty_const[reg_qty[REGNO (x)]] - = gen_lowpart_if_possible (GET_MODE (x), p->exp); - qty_const_insn[reg_qty[REGNO (x)]] = this_insn; - break; - } - } - } - - else if (GET_CODE (x) == REG && qty_const[reg_qty[REGNO (x)]] - && GET_MODE (x) == qty_mode[reg_qty[REGNO (x)]]) - qty_const_insn[reg_qty[REGNO (x)]] = this_insn; - - /* If this is a constant with symbolic value, - and it has a term with an explicit integer value, - link it up with related expressions. */ - if (GET_CODE (x) == CONST) - { - rtx subexp = get_related_value (x); - int subhash; - struct table_elt *subelt, *subelt_prev; - - if (subexp != 0) - { - /* Get the integer-free subexpression in the hash table. */ - subhash = safe_hash (subexp, mode) % NBUCKETS; - subelt = lookup (subexp, subhash, mode); - if (subelt == 0) - subelt = insert (subexp, NULL_PTR, subhash, mode); - /* Initialize SUBELT's circular chain if it has none. */ - if (subelt->related_value == 0) - subelt->related_value = subelt; - /* Find the element in the circular chain that precedes SUBELT. */ - subelt_prev = subelt; - while (subelt_prev->related_value != subelt) - subelt_prev = subelt_prev->related_value; - /* Put new ELT into SUBELT's circular chain just before SUBELT. - This way the element that follows SUBELT is the oldest one. */ - elt->related_value = subelt_prev->related_value; - subelt_prev->related_value = elt; - } - } - - return elt; -} - -/* Given two equivalence classes, CLASS1 and CLASS2, put all the entries from - CLASS2 into CLASS1. This is done when we have reached an insn which makes - the two classes equivalent. - - CLASS1 will be the surviving class; CLASS2 should not be used after this - call. - - Any invalid entries in CLASS2 will not be copied. */ - -static void -merge_equiv_classes (class1, class2) - struct table_elt *class1, *class2; -{ - struct table_elt *elt, *next, *new; - - /* Ensure we start with the head of the classes. */ - class1 = class1->first_same_value; - class2 = class2->first_same_value; - - /* If they were already equal, forget it. */ - if (class1 == class2) - return; - - for (elt = class2; elt; elt = next) - { - int hash; - rtx exp = elt->exp; - enum machine_mode mode = elt->mode; - - next = elt->next_same_value; - - /* Remove old entry, make a new one in CLASS1's class. - Don't do this for invalid entries as we cannot find their - hash code (it also isn't necessary). */ - if (GET_CODE (exp) == REG || exp_equiv_p (exp, exp, 1, 0)) - { - hash_arg_in_memory = 0; - hash_arg_in_struct = 0; - hash = HASH (exp, mode); - - if (GET_CODE (exp) == REG) - delete_reg_equiv (REGNO (exp)); - - remove_from_table (elt, hash); - - if (insert_regs (exp, class1, 0)) - hash = HASH (exp, mode); - new = insert (exp, class1, hash, mode); - new->in_memory = hash_arg_in_memory; - new->in_struct = hash_arg_in_struct; - } - } -} - -/* Remove from the hash table, or mark as invalid, - all expressions whose values could be altered by storing in X. - X is a register, a subreg, or a memory reference with nonvarying address - (because, when a memory reference with a varying address is stored in, - all memory references are removed by invalidate_memory - so specific invalidation is superfluous). - - A nonvarying address may be just a register or just - a symbol reference, or it may be either of those plus - a numeric offset. */ - -static void -invalidate (x) - rtx x; -{ - register int i; - register struct table_elt *p; - rtx base; - HOST_WIDE_INT start, end; - - /* If X is a register, dependencies on its contents - are recorded through the qty number mechanism. - Just change the qty number of the register, - mark it as invalid for expressions that refer to it, - and remove it itself. */ - - if (GET_CODE (x) == REG) - { - register int regno = REGNO (x); - register int hash = HASH (x, GET_MODE (x)); - - /* Remove REGNO from any quantity list it might be on and indicate - that it's value might have changed. If it is a pseudo, remove its - entry from the hash table. - - For a hard register, we do the first two actions above for any - additional hard registers corresponding to X. Then, if any of these - registers are in the table, we must remove any REG entries that - overlap these registers. */ - - delete_reg_equiv (regno); - reg_tick[regno]++; - - if (regno >= FIRST_PSEUDO_REGISTER) - remove_from_table (lookup_for_remove (x, hash, GET_MODE (x)), hash); - else - { - HOST_WIDE_INT in_table - = TEST_HARD_REG_BIT (hard_regs_in_table, regno); - int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x)); - int tregno, tendregno; - register struct table_elt *p, *next; - - CLEAR_HARD_REG_BIT (hard_regs_in_table, regno); - - for (i = regno + 1; i < endregno; i++) - { - in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, i); - CLEAR_HARD_REG_BIT (hard_regs_in_table, i); - delete_reg_equiv (i); - reg_tick[i]++; - } - - if (in_table) - for (hash = 0; hash < NBUCKETS; hash++) - for (p = table[hash]; p; p = next) - { - next = p->next_same_hash; - - if (GET_CODE (p->exp) != REG - || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER) - continue; - - tregno = REGNO (p->exp); - tendregno - = tregno + HARD_REGNO_NREGS (tregno, GET_MODE (p->exp)); - if (tendregno > regno && tregno < endregno) - remove_from_table (p, hash); - } - } - - return; - } - - if (GET_CODE (x) == SUBREG) - { - if (GET_CODE (SUBREG_REG (x)) != REG) - abort (); - invalidate (SUBREG_REG (x)); - return; - } - - /* X is not a register; it must be a memory reference with - a nonvarying address. Remove all hash table elements - that refer to overlapping pieces of memory. */ - - if (GET_CODE (x) != MEM) - abort (); - - set_nonvarying_address_components (XEXP (x, 0), GET_MODE_SIZE (GET_MODE (x)), - &base, &start, &end); - - for (i = 0; i < NBUCKETS; i++) - { - register struct table_elt *next; - for (p = table[i]; p; p = next) - { - next = p->next_same_hash; - if (refers_to_mem_p (p->exp, base, start, end)) - remove_from_table (p, i); - } - } -} - -/* Remove all expressions that refer to register REGNO, - since they are already invalid, and we are about to - mark that register valid again and don't want the old - expressions to reappear as valid. */ - -static void -remove_invalid_refs (regno) - int regno; -{ - register int i; - register struct table_elt *p, *next; - - for (i = 0; i < NBUCKETS; i++) - for (p = table[i]; p; p = next) - { - next = p->next_same_hash; - if (GET_CODE (p->exp) != REG - && refers_to_regno_p (regno, regno + 1, p->exp, NULL_PTR)) - remove_from_table (p, i); - } -} - -/* Recompute the hash codes of any valid entries in the hash table that - reference X, if X is a register, or SUBREG_REG (X) if X is a SUBREG. - - This is called when we make a jump equivalence. */ - -static void -rehash_using_reg (x) - rtx x; -{ - int i; - struct table_elt *p, *next; - int hash; - - if (GET_CODE (x) == SUBREG) - x = SUBREG_REG (x); - - /* If X is not a register or if the register is known not to be in any - valid entries in the table, we have no work to do. */ - - if (GET_CODE (x) != REG - || reg_in_table[REGNO (x)] < 0 - || reg_in_table[REGNO (x)] != reg_tick[REGNO (x)]) - return; - - /* Scan all hash chains looking for valid entries that mention X. - If we find one and it is in the wrong hash chain, move it. We can skip - objects that are registers, since they are handled specially. */ - - for (i = 0; i < NBUCKETS; i++) - for (p = table[i]; p; p = next) - { - next = p->next_same_hash; - if (GET_CODE (p->exp) != REG && reg_mentioned_p (x, p->exp) - && exp_equiv_p (p->exp, p->exp, 1, 0) - && i != (hash = safe_hash (p->exp, p->mode) % NBUCKETS)) - { - if (p->next_same_hash) - p->next_same_hash->prev_same_hash = p->prev_same_hash; - - if (p->prev_same_hash) - p->prev_same_hash->next_same_hash = p->next_same_hash; - else - table[i] = p->next_same_hash; - - p->next_same_hash = table[hash]; - p->prev_same_hash = 0; - if (table[hash]) - table[hash]->prev_same_hash = p; - table[hash] = p; - } - } -} - -/* Remove from the hash table all expressions that reference memory, - or some of them as specified by *WRITES. */ - -static void -invalidate_memory (writes) - struct write_data *writes; -{ - register int i; - register struct table_elt *p, *next; - int all = writes->all; - int nonscalar = writes->nonscalar; - - for (i = 0; i < NBUCKETS; i++) - for (p = table[i]; p; p = next) - { - next = p->next_same_hash; - if (p->in_memory - && (all - || (nonscalar && p->in_struct) - || cse_rtx_addr_varies_p (p->exp))) - remove_from_table (p, i); - } -} - -/* Remove from the hash table any expression that is a call-clobbered - register. Also update their TICK values. */ - -static void -invalidate_for_call () -{ - int regno, endregno; - int i; - int hash; - struct table_elt *p, *next; - int in_table = 0; - - /* Go through all the hard registers. For each that is clobbered in - a CALL_INSN, remove the register from quantity chains and update - reg_tick if defined. Also see if any of these registers is currently - in the table. */ - - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno)) - { - delete_reg_equiv (regno); - if (reg_tick[regno] >= 0) - reg_tick[regno]++; - - in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, regno); - } - - /* In the case where we have no call-clobbered hard registers in the - table, we are done. Otherwise, scan the table and remove any - entry that overlaps a call-clobbered register. */ - - if (in_table) - for (hash = 0; hash < NBUCKETS; hash++) - for (p = table[hash]; p; p = next) - { - next = p->next_same_hash; - - if (GET_CODE (p->exp) != REG - || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER) - continue; - - regno = REGNO (p->exp); - endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (p->exp)); - - for (i = regno; i < endregno; i++) - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i)) - { - remove_from_table (p, hash); - break; - } - } -} - -/* Given an expression X of type CONST, - and ELT which is its table entry (or 0 if it - is not in the hash table), - return an alternate expression for X as a register plus integer. - If none can be found, return 0. */ - -static rtx -use_related_value (x, elt) - rtx x; - struct table_elt *elt; -{ - register struct table_elt *relt = 0; - register struct table_elt *p, *q; - HOST_WIDE_INT offset; - - /* First, is there anything related known? - If we have a table element, we can tell from that. - Otherwise, must look it up. */ - - if (elt != 0 && elt->related_value != 0) - relt = elt; - else if (elt == 0 && GET_CODE (x) == CONST) - { - rtx subexp = get_related_value (x); - if (subexp != 0) - relt = lookup (subexp, - safe_hash (subexp, GET_MODE (subexp)) % NBUCKETS, - GET_MODE (subexp)); - } - - if (relt == 0) - return 0; - - /* Search all related table entries for one that has an - equivalent register. */ - - p = relt; - while (1) - { - /* This loop is strange in that it is executed in two different cases. - The first is when X is already in the table. Then it is searching - the RELATED_VALUE list of X's class (RELT). The second case is when - X is not in the table. Then RELT points to a class for the related - value. - - Ensure that, whatever case we are in, that we ignore classes that have - the same value as X. */ - - if (rtx_equal_p (x, p->exp)) - q = 0; - else - for (q = p->first_same_value; q; q = q->next_same_value) - if (GET_CODE (q->exp) == REG) - break; - - if (q) - break; - - p = p->related_value; - - /* We went all the way around, so there is nothing to be found. - Alternatively, perhaps RELT was in the table for some other reason - and it has no related values recorded. */ - if (p == relt || p == 0) - break; - } - - if (q == 0) - return 0; - - offset = (get_integer_term (x) - get_integer_term (p->exp)); - /* Note: OFFSET may be 0 if P->xexp and X are related by commutativity. */ - return plus_constant (q->exp, offset); -} - -/* Hash an rtx. We are careful to make sure the value is never negative. - Equivalent registers hash identically. - MODE is used in hashing for CONST_INTs only; - otherwise the mode of X is used. - - Store 1 in do_not_record if any subexpression is volatile. - - Store 1 in hash_arg_in_memory if X contains a MEM rtx - which does not have the RTX_UNCHANGING_P bit set. - In this case, also store 1 in hash_arg_in_struct - if there is a MEM rtx which has the MEM_IN_STRUCT_P bit set. - - Note that cse_insn knows that the hash code of a MEM expression - is just (int) MEM plus the hash code of the address. */ - -static int -canon_hash (x, mode) - rtx x; - enum machine_mode mode; -{ - register int i, j; - register int hash = 0; - register enum rtx_code code; - register char *fmt; - - /* repeat is used to turn tail-recursion into iteration. */ - repeat: - if (x == 0) - return hash; - - code = GET_CODE (x); - switch (code) - { - case REG: - { - register int regno = REGNO (x); - - /* On some machines, we can't record any non-fixed hard register, - because extending its life will cause reload problems. We - consider ap, fp, and sp to be fixed for this purpose. - On all machines, we can't record any global registers. */ - - if (regno < FIRST_PSEUDO_REGISTER - && (global_regs[regno] -#ifdef SMALL_REGISTER_CLASSES - || (! fixed_regs[regno] - && regno != FRAME_POINTER_REGNUM - && regno != ARG_POINTER_REGNUM - && regno != STACK_POINTER_REGNUM) -#endif - )) - { - do_not_record = 1; - return 0; - } - return hash + ((int) REG << 7) + reg_qty[regno]; - } - - case CONST_INT: - hash += ((int) mode + ((int) CONST_INT << 7) - + INTVAL (x) + (INTVAL (x) >> HASHBITS)); - return ((1 << HASHBITS) - 1) & hash; - - case CONST_DOUBLE: - /* This is like the general case, except that it only counts - the integers representing the constant. */ - hash += (int) code + (int) GET_MODE (x); - { - int i; - for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++) - { - int tem = XINT (x, i); - hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS)); - } - } - return hash; - - /* Assume there is only one rtx object for any given label. */ - case LABEL_REF: - /* Use `and' to ensure a positive number. */ - return (hash + ((HOST_WIDE_INT) LABEL_REF << 7) - + ((HOST_WIDE_INT) XEXP (x, 0) & ((1 << HASHBITS) - 1))); - - case SYMBOL_REF: - return (hash + ((HOST_WIDE_INT) SYMBOL_REF << 7) - + ((HOST_WIDE_INT) XEXP (x, 0) & ((1 << HASHBITS) - 1))); - - case MEM: - if (MEM_VOLATILE_P (x)) - { - do_not_record = 1; - return 0; - } - if (! RTX_UNCHANGING_P (x)) - { - hash_arg_in_memory = 1; - if (MEM_IN_STRUCT_P (x)) hash_arg_in_struct = 1; - } - /* Now that we have already found this special case, - might as well speed it up as much as possible. */ - hash += (int) MEM; - x = XEXP (x, 0); - goto repeat; - - case PRE_DEC: - case PRE_INC: - case POST_DEC: - case POST_INC: - case PC: - case CC0: - case CALL: - case UNSPEC_VOLATILE: - do_not_record = 1; - return 0; - - case ASM_OPERANDS: - if (MEM_VOLATILE_P (x)) - { - do_not_record = 1; - return 0; - } - } - - i = GET_RTX_LENGTH (code) - 1; - hash += (int) code + (int) GET_MODE (x); - fmt = GET_RTX_FORMAT (code); - for (; i >= 0; i--) - { - if (fmt[i] == 'e') - { - rtx tem = XEXP (x, i); - rtx tem1; - - /* If the operand is a REG that is equivalent to a constant, hash - as if we were hashing the constant, since we will be comparing - that way. */ - if (tem != 0 && GET_CODE (tem) == REG - && REGNO_QTY_VALID_P (REGNO (tem)) - && qty_mode[reg_qty[REGNO (tem)]] == GET_MODE (tem) - && (tem1 = qty_const[reg_qty[REGNO (tem)]]) != 0 - && CONSTANT_P (tem1)) - tem = tem1; - - /* If we are about to do the last recursive call - needed at this level, change it into iteration. - This function is called enough to be worth it. */ - if (i == 0) - { - x = tem; - goto repeat; - } - hash += canon_hash (tem, 0); - } - else if (fmt[i] == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - hash += canon_hash (XVECEXP (x, i, j), 0); - else if (fmt[i] == 's') - { - register char *p = XSTR (x, i); - if (p) - while (*p) - { - register int tem = *p++; - hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS)); - } - } - else if (fmt[i] == 'i') - { - register int tem = XINT (x, i); - hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS)); - } - else - abort (); - } - return hash; -} - -/* Like canon_hash but with no side effects. */ - -static int -safe_hash (x, mode) - rtx x; - enum machine_mode mode; -{ - int save_do_not_record = do_not_record; - int save_hash_arg_in_memory = hash_arg_in_memory; - int save_hash_arg_in_struct = hash_arg_in_struct; - int hash = canon_hash (x, mode); - hash_arg_in_memory = save_hash_arg_in_memory; - hash_arg_in_struct = save_hash_arg_in_struct; - do_not_record = save_do_not_record; - return hash; -} - -/* Return 1 iff X and Y would canonicalize into the same thing, - without actually constructing the canonicalization of either one. - If VALIDATE is nonzero, - we assume X is an expression being processed from the rtl - and Y was found in the hash table. We check register refs - in Y for being marked as valid. - - If EQUAL_VALUES is nonzero, we allow a register to match a constant value - that is known to be in the register. Ordinarily, we don't allow them - to match, because letting them match would cause unpredictable results - in all the places that search a hash table chain for an equivalent - for a given value. A possible equivalent that has different structure - has its hash code computed from different data. Whether the hash code - is the same as that of the the given value is pure luck. */ - -static int -exp_equiv_p (x, y, validate, equal_values) - rtx x, y; - int validate; - int equal_values; -{ - register int i, j; - register enum rtx_code code; - register char *fmt; - - /* Note: it is incorrect to assume an expression is equivalent to itself - if VALIDATE is nonzero. */ - if (x == y && !validate) - return 1; - if (x == 0 || y == 0) - return x == y; - - code = GET_CODE (x); - if (code != GET_CODE (y)) - { - if (!equal_values) - return 0; - - /* If X is a constant and Y is a register or vice versa, they may be - equivalent. We only have to validate if Y is a register. */ - if (CONSTANT_P (x) && GET_CODE (y) == REG - && REGNO_QTY_VALID_P (REGNO (y)) - && GET_MODE (y) == qty_mode[reg_qty[REGNO (y)]] - && rtx_equal_p (x, qty_const[reg_qty[REGNO (y)]]) - && (! validate || reg_in_table[REGNO (y)] == reg_tick[REGNO (y)])) - return 1; - - if (CONSTANT_P (y) && code == REG - && REGNO_QTY_VALID_P (REGNO (x)) - && GET_MODE (x) == qty_mode[reg_qty[REGNO (x)]] - && rtx_equal_p (y, qty_const[reg_qty[REGNO (x)]])) - return 1; - - return 0; - } - - /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */ - if (GET_MODE (x) != GET_MODE (y)) - return 0; - - switch (code) - { - case PC: - case CC0: - return x == y; - - case CONST_INT: - return INTVAL (x) == INTVAL (y); - - case LABEL_REF: - case SYMBOL_REF: - return XEXP (x, 0) == XEXP (y, 0); - - case REG: - { - int regno = REGNO (y); - int endregno - = regno + (regno >= FIRST_PSEUDO_REGISTER ? 1 - : HARD_REGNO_NREGS (regno, GET_MODE (y))); - int i; - - /* If the quantities are not the same, the expressions are not - equivalent. If there are and we are not to validate, they - are equivalent. Otherwise, ensure all regs are up-to-date. */ - - if (reg_qty[REGNO (x)] != reg_qty[regno]) - return 0; - - if (! validate) - return 1; - - for (i = regno; i < endregno; i++) - if (reg_in_table[i] != reg_tick[i]) - return 0; - - return 1; - } - - /* For commutative operations, check both orders. */ - case PLUS: - case MULT: - case AND: - case IOR: - case XOR: - case NE: - case EQ: - return ((exp_equiv_p (XEXP (x, 0), XEXP (y, 0), validate, equal_values) - && exp_equiv_p (XEXP (x, 1), XEXP (y, 1), - validate, equal_values)) - || (exp_equiv_p (XEXP (x, 0), XEXP (y, 1), - validate, equal_values) - && exp_equiv_p (XEXP (x, 1), XEXP (y, 0), - validate, equal_values))); - } - - /* Compare the elements. If any pair of corresponding elements - fail to match, return 0 for the whole things. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - switch (fmt[i]) - { - case 'e': - if (! exp_equiv_p (XEXP (x, i), XEXP (y, i), validate, equal_values)) - return 0; - break; - - case 'E': - if (XVECLEN (x, i) != XVECLEN (y, i)) - return 0; - for (j = 0; j < XVECLEN (x, i); j++) - if (! exp_equiv_p (XVECEXP (x, i, j), XVECEXP (y, i, j), - validate, equal_values)) - return 0; - break; - - case 's': - if (strcmp (XSTR (x, i), XSTR (y, i))) - return 0; - break; - - case 'i': - if (XINT (x, i) != XINT (y, i)) - return 0; - break; - - case 'w': - if (XWINT (x, i) != XWINT (y, i)) - return 0; - break; - - case '0': - break; - - default: - abort (); - } - } - - return 1; -} - -/* Return 1 iff any subexpression of X matches Y. - Here we do not require that X or Y be valid (for registers referred to) - for being in the hash table. */ - -static int -refers_to_p (x, y) - rtx x, y; -{ - register int i; - register enum rtx_code code; - register char *fmt; - - repeat: - if (x == y) - return 1; - if (x == 0 || y == 0) - return 0; - - code = GET_CODE (x); - /* If X as a whole has the same code as Y, they may match. - If so, return 1. */ - if (code == GET_CODE (y)) - { - if (exp_equiv_p (x, y, 0, 1)) - return 1; - } - - /* X does not match, so try its subexpressions. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - { - if (i == 0) - { - x = XEXP (x, 0); - goto repeat; - } - else - if (refers_to_p (XEXP (x, i), y)) - return 1; - } - else if (fmt[i] == 'E') - { - int j; - for (j = 0; j < XVECLEN (x, i); j++) - if (refers_to_p (XVECEXP (x, i, j), y)) - return 1; - } - - return 0; -} - -/* Given ADDR and SIZE (a memory address, and the size of the memory reference), - set PBASE, PSTART, and PEND which correspond to the base of the address, - the starting offset, and ending offset respectively. - - ADDR is known to be a nonvarying address. - - cse_address_varies_p returns zero for nonvarying addresses. */ - -static void -set_nonvarying_address_components (addr, size, pbase, pstart, pend) - rtx addr; - int size; - rtx *pbase; - HOST_WIDE_INT *pstart, *pend; -{ - rtx base; - int start, end; - - base = addr; - start = 0; - end = 0; - - /* Registers with nonvarying addresses usually have constant equivalents; - but the frame pointer register is also possible. */ - if (GET_CODE (base) == REG - && qty_const != 0 - && REGNO_QTY_VALID_P (REGNO (base)) - && qty_mode[reg_qty[REGNO (base)]] == GET_MODE (base) - && qty_const[reg_qty[REGNO (base)]] != 0) - base = qty_const[reg_qty[REGNO (base)]]; - else if (GET_CODE (base) == PLUS - && GET_CODE (XEXP (base, 1)) == CONST_INT - && GET_CODE (XEXP (base, 0)) == REG - && qty_const != 0 - && REGNO_QTY_VALID_P (REGNO (XEXP (base, 0))) - && (qty_mode[reg_qty[REGNO (XEXP (base, 0))]] - == GET_MODE (XEXP (base, 0))) - && qty_const[reg_qty[REGNO (XEXP (base, 0))]]) - { - start = INTVAL (XEXP (base, 1)); - base = qty_const[reg_qty[REGNO (XEXP (base, 0))]]; - } - - /* By definition, operand1 of a LO_SUM is the associated constant - address. Use the associated constant address as the base instead. */ - if (GET_CODE (base) == LO_SUM) - base = XEXP (base, 1); - - /* Strip off CONST. */ - if (GET_CODE (base) == CONST) - base = XEXP (base, 0); - - if (GET_CODE (base) == PLUS - && GET_CODE (XEXP (base, 1)) == CONST_INT) - { - start += INTVAL (XEXP (base, 1)); - base = XEXP (base, 0); - } - - end = start + size; - - /* Set the return values. */ - *pbase = base; - *pstart = start; - *pend = end; -} - -/* Return 1 iff any subexpression of X refers to memory - at an address of BASE plus some offset - such that any of the bytes' offsets fall between START (inclusive) - and END (exclusive). - - The value is undefined if X is a varying address (as determined by - cse_rtx_addr_varies_p). This function is not used in such cases. - - When used in the cse pass, `qty_const' is nonzero, and it is used - to treat an address that is a register with a known constant value - as if it were that constant value. - In the loop pass, `qty_const' is zero, so this is not done. */ - -static int -refers_to_mem_p (x, base, start, end) - rtx x, base; - HOST_WIDE_INT start, end; -{ - register HOST_WIDE_INT i; - register enum rtx_code code; - register char *fmt; - - if (GET_CODE (base) == CONST_INT) - { - start += INTVAL (base); - end += INTVAL (base); - base = const0_rtx; - } - - repeat: - if (x == 0) - return 0; - - code = GET_CODE (x); - if (code == MEM) - { - register rtx addr = XEXP (x, 0); /* Get the address. */ - rtx mybase; - HOST_WIDE_INT mystart, myend; - - set_nonvarying_address_components (addr, GET_MODE_SIZE (GET_MODE (x)), - &mybase, &mystart, &myend); - - - /* refers_to_mem_p is never called with varying addresses. - If the base addresses are not equal, there is no chance - of the memory addresses conflicting. */ - if (! rtx_equal_p (mybase, base)) - return 0; - - return myend > start && mystart < end; - } - - /* X does not match, so try its subexpressions. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - { - if (i == 0) - { - x = XEXP (x, 0); - goto repeat; - } - else - if (refers_to_mem_p (XEXP (x, i), base, start, end)) - return 1; - } - else if (fmt[i] == 'E') - { - int j; - for (j = 0; j < XVECLEN (x, i); j++) - if (refers_to_mem_p (XVECEXP (x, i, j), base, start, end)) - return 1; - } - - return 0; -} - -/* Nonzero if X refers to memory at a varying address; - except that a register which has at the moment a known constant value - isn't considered variable. */ - -static int -cse_rtx_addr_varies_p (x) - rtx x; -{ - /* We need not check for X and the equivalence class being of the same - mode because if X is equivalent to a constant in some mode, it - doesn't vary in any mode. */ - - if (GET_CODE (x) == MEM - && GET_CODE (XEXP (x, 0)) == REG - && REGNO_QTY_VALID_P (REGNO (XEXP (x, 0))) - && GET_MODE (XEXP (x, 0)) == qty_mode[reg_qty[REGNO (XEXP (x, 0))]] - && qty_const[reg_qty[REGNO (XEXP (x, 0))]] != 0) - return 0; - - if (GET_CODE (x) == MEM - && GET_CODE (XEXP (x, 0)) == PLUS - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG - && REGNO_QTY_VALID_P (REGNO (XEXP (XEXP (x, 0), 0))) - && (GET_MODE (XEXP (XEXP (x, 0), 0)) - == qty_mode[reg_qty[REGNO (XEXP (XEXP (x, 0), 0))]]) - && qty_const[reg_qty[REGNO (XEXP (XEXP (x, 0), 0))]]) - return 0; - - return rtx_addr_varies_p (x); -} - -/* Canonicalize an expression: - replace each register reference inside it - with the "oldest" equivalent register. - - If INSN is non-zero and we are replacing a pseudo with a hard register - or vice versa, validate_change is used to ensure that INSN remains valid - after we make our substitution. The calls are made with IN_GROUP non-zero - so apply_change_group must be called upon the outermost return from this - function (unless INSN is zero). The result of apply_change_group can - generally be discarded since the changes we are making are optional. */ - -static rtx -canon_reg (x, insn) - rtx x; - rtx insn; -{ - register int i; - register enum rtx_code code; - register char *fmt; - - if (x == 0) - return x; - - code = GET_CODE (x); - switch (code) - { - case PC: - case CC0: - case CONST: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case LABEL_REF: - case ADDR_VEC: - case ADDR_DIFF_VEC: - return x; - - case REG: - { - register int first; - - /* Never replace a hard reg, because hard regs can appear - in more than one machine mode, and we must preserve the mode - of each occurrence. Also, some hard regs appear in - MEMs that are shared and mustn't be altered. Don't try to - replace any reg that maps to a reg of class NO_REGS. */ - if (REGNO (x) < FIRST_PSEUDO_REGISTER - || ! REGNO_QTY_VALID_P (REGNO (x))) - return x; - - first = qty_first_reg[reg_qty[REGNO (x)]]; - return (first >= FIRST_PSEUDO_REGISTER ? regno_reg_rtx[first] - : REGNO_REG_CLASS (first) == NO_REGS ? x - : gen_rtx (REG, qty_mode[reg_qty[REGNO (x)]], first)); - } - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - register int j; - - if (fmt[i] == 'e') - { - rtx new = canon_reg (XEXP (x, i), insn); - - /* If replacing pseudo with hard reg or vice versa, ensure the - insn remains valid. Likewise if the insn has MATCH_DUPs. */ - if (insn != 0 && new != 0 - && GET_CODE (new) == REG && GET_CODE (XEXP (x, i)) == REG - && (((REGNO (new) < FIRST_PSEUDO_REGISTER) - != (REGNO (XEXP (x, i)) < FIRST_PSEUDO_REGISTER)) - || insn_n_dups[recog_memoized (insn)] > 0)) - validate_change (insn, &XEXP (x, i), new, 1); - else - XEXP (x, i) = new; - } - else if (fmt[i] == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - XVECEXP (x, i, j) = canon_reg (XVECEXP (x, i, j), insn); - } - - return x; -} - -/* LOC is a location with INSN that is an operand address (the contents of - a MEM). Find the best equivalent address to use that is valid for this - insn. - - On most CISC machines, complicated address modes are costly, and rtx_cost - is a good approximation for that cost. However, most RISC machines have - only a few (usually only one) memory reference formats. If an address is - valid at all, it is often just as cheap as any other address. Hence, for - RISC machines, we use the configuration macro `ADDRESS_COST' to compare the - costs of various addresses. For two addresses of equal cost, choose the one - with the highest `rtx_cost' value as that has the potential of eliminating - the most insns. For equal costs, we choose the first in the equivalence - class. Note that we ignore the fact that pseudo registers are cheaper - than hard registers here because we would also prefer the pseudo registers. - */ - -static void -find_best_addr (insn, loc) - rtx insn; - rtx *loc; -{ - struct table_elt *elt, *p; - rtx addr = *loc; - int our_cost; - int found_better = 1; - int save_do_not_record = do_not_record; - int save_hash_arg_in_memory = hash_arg_in_memory; - int save_hash_arg_in_struct = hash_arg_in_struct; - int hash_code; - int addr_volatile; - int regno; - - /* Do not try to replace constant addresses or addresses of local and - argument slots. These MEM expressions are made only once and inserted - in many instructions, as well as being used to control symbol table - output. It is not safe to clobber them. - - There are some uncommon cases where the address is already in a register - for some reason, but we cannot take advantage of that because we have - no easy way to unshare the MEM. In addition, looking up all stack - addresses is costly. */ - if ((GET_CODE (addr) == PLUS - && GET_CODE (XEXP (addr, 0)) == REG - && GET_CODE (XEXP (addr, 1)) == CONST_INT - && (regno = REGNO (XEXP (addr, 0)), - regno == FRAME_POINTER_REGNUM || regno == ARG_POINTER_REGNUM)) - || (GET_CODE (addr) == REG - && (regno = REGNO (addr), - regno == FRAME_POINTER_REGNUM || regno == ARG_POINTER_REGNUM)) - || CONSTANT_ADDRESS_P (addr)) - return; - - /* If this address is not simply a register, try to fold it. This will - sometimes simplify the expression. Many simplifications - will not be valid, but some, usually applying the associative rule, will - be valid and produce better code. */ - if (GET_CODE (addr) != REG - && validate_change (insn, loc, fold_rtx (addr, insn), 0)) - addr = *loc; - - /* If this address is not in the hash table, we can't look for equivalences - of the whole address. Also, ignore if volatile. */ - - do_not_record = 0; - hash_code = HASH (addr, Pmode); - addr_volatile = do_not_record; - do_not_record = save_do_not_record; - hash_arg_in_memory = save_hash_arg_in_memory; - hash_arg_in_struct = save_hash_arg_in_struct; - - if (addr_volatile) - return; - - elt = lookup (addr, hash_code, Pmode); - -#ifndef ADDRESS_COST - if (elt) - { - our_cost = elt->cost; - - /* Find the lowest cost below ours that works. */ - for (elt = elt->first_same_value; elt; elt = elt->next_same_value) - if (elt->cost < our_cost - && (GET_CODE (elt->exp) == REG - || exp_equiv_p (elt->exp, elt->exp, 1, 0)) - && validate_change (insn, loc, - canon_reg (copy_rtx (elt->exp), NULL_RTX), 0)) - return; - } -#else - - if (elt) - { - /* We need to find the best (under the criteria documented above) entry - in the class that is valid. We use the `flag' field to indicate - choices that were invalid and iterate until we can't find a better - one that hasn't already been tried. */ - - for (p = elt->first_same_value; p; p = p->next_same_value) - p->flag = 0; - - while (found_better) - { - int best_addr_cost = ADDRESS_COST (*loc); - int best_rtx_cost = (elt->cost + 1) >> 1; - struct table_elt *best_elt = elt; - - found_better = 0; - for (p = elt->first_same_value; p; p = p->next_same_value) - if (! p->flag - && (GET_CODE (p->exp) == REG - || exp_equiv_p (p->exp, p->exp, 1, 0)) - && (ADDRESS_COST (p->exp) < best_addr_cost - || (ADDRESS_COST (p->exp) == best_addr_cost - && (p->cost + 1) >> 1 > best_rtx_cost))) - { - found_better = 1; - best_addr_cost = ADDRESS_COST (p->exp); - best_rtx_cost = (p->cost + 1) >> 1; - best_elt = p; - } - - if (found_better) - { - if (validate_change (insn, loc, - canon_reg (copy_rtx (best_elt->exp), - NULL_RTX), 0)) - return; - else - best_elt->flag = 1; - } - } - } - - /* If the address is a binary operation with the first operand a register - and the second a constant, do the same as above, but looking for - equivalences of the register. Then try to simplify before checking for - the best address to use. This catches a few cases: First is when we - have REG+const and the register is another REG+const. We can often merge - the constants and eliminate one insn and one register. It may also be - that a machine has a cheap REG+REG+const. Finally, this improves the - code on the Alpha for unaligned byte stores. */ - - if (flag_expensive_optimizations - && (GET_RTX_CLASS (GET_CODE (*loc)) == '2' - || GET_RTX_CLASS (GET_CODE (*loc)) == 'c') - && GET_CODE (XEXP (*loc, 0)) == REG - && GET_CODE (XEXP (*loc, 1)) == CONST_INT) - { - rtx c = XEXP (*loc, 1); - - do_not_record = 0; - hash_code = HASH (XEXP (*loc, 0), Pmode); - do_not_record = save_do_not_record; - hash_arg_in_memory = save_hash_arg_in_memory; - hash_arg_in_struct = save_hash_arg_in_struct; - - elt = lookup (XEXP (*loc, 0), hash_code, Pmode); - if (elt == 0) - return; - - /* We need to find the best (under the criteria documented above) entry - in the class that is valid. We use the `flag' field to indicate - choices that were invalid and iterate until we can't find a better - one that hasn't already been tried. */ - - for (p = elt->first_same_value; p; p = p->next_same_value) - p->flag = 0; - - while (found_better) - { - int best_addr_cost = ADDRESS_COST (*loc); - int best_rtx_cost = (COST (*loc) + 1) >> 1; - struct table_elt *best_elt = elt; - rtx best_rtx = *loc; - - found_better = 0; - for (p = elt->first_same_value; p; p = p->next_same_value) - if (! p->flag - && (GET_CODE (p->exp) == REG - || exp_equiv_p (p->exp, p->exp, 1, 0))) - { - rtx new = cse_gen_binary (GET_CODE (*loc), Pmode, p->exp, c); - - if ((ADDRESS_COST (new) < best_addr_cost - || (ADDRESS_COST (new) == best_addr_cost - && (COST (new) + 1) >> 1 > best_rtx_cost))) - { - found_better = 1; - best_addr_cost = ADDRESS_COST (new); - best_rtx_cost = (COST (new) + 1) >> 1; - best_elt = p; - best_rtx = new; - } - } - - if (found_better) - { - if (validate_change (insn, loc, - canon_reg (copy_rtx (best_rtx), - NULL_RTX), 0)) - return; - else - best_elt->flag = 1; - } - } - } -#endif -} - -/* Given an operation (CODE, *PARG1, *PARG2), where code is a comparison - operation (EQ, NE, GT, etc.), follow it back through the hash table and - what values are being compared. - - *PARG1 and *PARG2 are updated to contain the rtx representing the values - actually being compared. For example, if *PARG1 was (cc0) and *PARG2 - was (const_int 0), *PARG1 and *PARG2 will be set to the objects that were - compared to produce cc0. - - The return value is the comparison operator and is either the code of - A or the code corresponding to the inverse of the comparison. */ - -static enum rtx_code -find_comparison_args (code, parg1, parg2, pmode1, pmode2) - enum rtx_code code; - rtx *parg1, *parg2; - enum machine_mode *pmode1, *pmode2; -{ - rtx arg1, arg2; - - arg1 = *parg1, arg2 = *parg2; - - /* If ARG2 is const0_rtx, see what ARG1 is equivalent to. */ - - while (arg2 == CONST0_RTX (GET_MODE (arg1))) - { - /* Set non-zero when we find something of interest. */ - rtx x = 0; - int reverse_code = 0; - struct table_elt *p = 0; - - /* If arg1 is a COMPARE, extract the comparison arguments from it. - On machines with CC0, this is the only case that can occur, since - fold_rtx will return the COMPARE or item being compared with zero - when given CC0. */ - - if (GET_CODE (arg1) == COMPARE && arg2 == const0_rtx) - x = arg1; - - /* If ARG1 is a comparison operator and CODE is testing for - STORE_FLAG_VALUE, get the inner arguments. */ - - else if (GET_RTX_CLASS (GET_CODE (arg1)) == '<') - { - if (code == NE - || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT - && code == LT && STORE_FLAG_VALUE == -1) -#ifdef FLOAT_STORE_FLAG_VALUE - || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_FLOAT - && FLOAT_STORE_FLAG_VALUE < 0) -#endif - ) - x = arg1; - else if (code == EQ - || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT - && code == GE && STORE_FLAG_VALUE == -1) -#ifdef FLOAT_STORE_FLAG_VALUE - || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_FLOAT - && FLOAT_STORE_FLAG_VALUE < 0) -#endif - ) - x = arg1, reverse_code = 1; - } - - /* ??? We could also check for - - (ne (and (eq (...) (const_int 1))) (const_int 0)) - - and related forms, but let's wait until we see them occurring. */ - - if (x == 0) - /* Look up ARG1 in the hash table and see if it has an equivalence - that lets us see what is being compared. */ - p = lookup (arg1, safe_hash (arg1, GET_MODE (arg1)) % NBUCKETS, - GET_MODE (arg1)); - if (p) p = p->first_same_value; - - for (; p; p = p->next_same_value) - { - enum machine_mode inner_mode = GET_MODE (p->exp); - - /* If the entry isn't valid, skip it. */ - if (! exp_equiv_p (p->exp, p->exp, 1, 0)) - continue; - - if (GET_CODE (p->exp) == COMPARE - /* Another possibility is that this machine has a compare insn - that includes the comparison code. In that case, ARG1 would - be equivalent to a comparison operation that would set ARG1 to - either STORE_FLAG_VALUE or zero. If this is an NE operation, - ORIG_CODE is the actual comparison being done; if it is an EQ, - we must reverse ORIG_CODE. On machine with a negative value - for STORE_FLAG_VALUE, also look at LT and GE operations. */ - || ((code == NE - || (code == LT - && GET_MODE_CLASS (inner_mode) == MODE_INT - && (GET_MODE_BITSIZE (inner_mode) - <= HOST_BITS_PER_WIDE_INT) - && (STORE_FLAG_VALUE - & ((HOST_WIDE_INT) 1 - << (GET_MODE_BITSIZE (inner_mode) - 1)))) -#ifdef FLOAT_STORE_FLAG_VALUE - || (code == LT - && GET_MODE_CLASS (inner_mode) == MODE_FLOAT - && FLOAT_STORE_FLAG_VALUE < 0) -#endif - ) - && GET_RTX_CLASS (GET_CODE (p->exp)) == '<')) - { - x = p->exp; - break; - } - else if ((code == EQ - || (code == GE - && GET_MODE_CLASS (inner_mode) == MODE_INT - && (GET_MODE_BITSIZE (inner_mode) - <= HOST_BITS_PER_WIDE_INT) - && (STORE_FLAG_VALUE - & ((HOST_WIDE_INT) 1 - << (GET_MODE_BITSIZE (inner_mode) - 1)))) -#ifdef FLOAT_STORE_FLAG_VALUE - || (code == GE - && GET_MODE_CLASS (inner_mode) == MODE_FLOAT - && FLOAT_STORE_FLAG_VALUE < 0) -#endif - ) - && GET_RTX_CLASS (GET_CODE (p->exp)) == '<') - { - reverse_code = 1; - x = p->exp; - break; - } - - /* If this is fp + constant, the equivalent is a better operand since - it may let us predict the value of the comparison. */ - else if (NONZERO_BASE_PLUS_P (p->exp)) - { - arg1 = p->exp; - continue; - } - } - - /* If we didn't find a useful equivalence for ARG1, we are done. - Otherwise, set up for the next iteration. */ - if (x == 0) - break; - - arg1 = XEXP (x, 0), arg2 = XEXP (x, 1); - if (GET_RTX_CLASS (GET_CODE (x)) == '<') - code = GET_CODE (x); - - if (reverse_code) - code = reverse_condition (code); - } - - /* Return our results. Return the modes from before fold_rtx - because fold_rtx might produce const_int, and then it's too late. */ - *pmode1 = GET_MODE (arg1), *pmode2 = GET_MODE (arg2); - *parg1 = fold_rtx (arg1, 0), *parg2 = fold_rtx (arg2, 0); - - return code; -} - -/* Try to simplify a unary operation CODE whose output mode is to be - MODE with input operand OP whose mode was originally OP_MODE. - Return zero if no simplification can be made. */ - -rtx -simplify_unary_operation (code, mode, op, op_mode) - enum rtx_code code; - enum machine_mode mode; - rtx op; - enum machine_mode op_mode; -{ - register int width = GET_MODE_BITSIZE (mode); - - /* The order of these tests is critical so that, for example, we don't - check the wrong mode (input vs. output) for a conversion operation, - such as FIX. At some point, this should be simplified. */ - -#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) - if (code == FLOAT && GET_CODE (op) == CONST_INT) - { - REAL_VALUE_TYPE d; - -#ifdef REAL_ARITHMETIC - REAL_VALUE_FROM_INT (d, INTVAL (op), INTVAL (op) < 0 ? ~0 : 0); -#else - d = (double) INTVAL (op); -#endif - return CONST_DOUBLE_FROM_REAL_VALUE (d, mode); - } - else if (code == UNSIGNED_FLOAT && GET_CODE (op) == CONST_INT) - { - REAL_VALUE_TYPE d; - -#ifdef REAL_ARITHMETIC - REAL_VALUE_FROM_INT (d, INTVAL (op), 0); -#else - d = (double) (unsigned int) INTVAL (op); -#endif - return CONST_DOUBLE_FROM_REAL_VALUE (d, mode); - } - - else if (code == FLOAT && GET_CODE (op) == CONST_DOUBLE - && GET_MODE (op) == VOIDmode) - { - REAL_VALUE_TYPE d; - -#ifdef REAL_ARITHMETIC - REAL_VALUE_FROM_INT (d, CONST_DOUBLE_LOW (op), CONST_DOUBLE_HIGH (op)); -#else - if (CONST_DOUBLE_HIGH (op) < 0) - { - d = (double) (~ CONST_DOUBLE_HIGH (op)); - d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)) - * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))); - d += (double) (unsigned HOST_WIDE_INT) (~ CONST_DOUBLE_LOW (op)); - d = (- d - 1.0); - } - else - { - d = (double) CONST_DOUBLE_HIGH (op); - d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)) - * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))); - d += (double) (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op); - } -#endif /* REAL_ARITHMETIC */ - return CONST_DOUBLE_FROM_REAL_VALUE (d, mode); - } - else if (code == UNSIGNED_FLOAT && GET_CODE (op) == CONST_DOUBLE - && GET_MODE (op) == VOIDmode) - { - REAL_VALUE_TYPE d; - -#ifdef REAL_ARITHMETIC - REAL_VALUE_FROM_UNSIGNED_INT (d, CONST_DOUBLE_LOW (op), - CONST_DOUBLE_HIGH (op)); -#else - d = (double) CONST_DOUBLE_HIGH (op); - d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)) - * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))); - d += (double) (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op); -#endif /* REAL_ARITHMETIC */ - return CONST_DOUBLE_FROM_REAL_VALUE (d, mode); - } -#endif - - if (GET_CODE (op) == CONST_INT - && width <= HOST_BITS_PER_WIDE_INT && width > 0) - { - register HOST_WIDE_INT arg0 = INTVAL (op); - register HOST_WIDE_INT val; - - switch (code) - { - case NOT: - val = ~ arg0; - break; - - case NEG: - val = - arg0; - break; - - case ABS: - val = (arg0 >= 0 ? arg0 : - arg0); - break; - - case FFS: - /* Don't use ffs here. Instead, get low order bit and then its - number. If arg0 is zero, this will return 0, as desired. */ - arg0 &= GET_MODE_MASK (mode); - val = exact_log2 (arg0 & (- arg0)) + 1; - break; - - case TRUNCATE: - val = arg0; - break; - - case ZERO_EXTEND: - if (op_mode == VOIDmode) - op_mode = mode; - if (GET_MODE_BITSIZE (op_mode) == HOST_BITS_PER_WIDE_INT) - { - /* If we were really extending the mode, - we would have to distinguish between zero-extension - and sign-extension. */ - if (width != GET_MODE_BITSIZE (op_mode)) - abort (); - val = arg0; - } - else if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT) - val = arg0 & ~((HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (op_mode)); - else - return 0; - break; - - case SIGN_EXTEND: - if (op_mode == VOIDmode) - op_mode = mode; - if (GET_MODE_BITSIZE (op_mode) == HOST_BITS_PER_WIDE_INT) - { - /* If we were really extending the mode, - we would have to distinguish between zero-extension - and sign-extension. */ - if (width != GET_MODE_BITSIZE (op_mode)) - abort (); - val = arg0; - } - else if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT) - { - val - = arg0 & ~((HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (op_mode)); - if (val - & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (op_mode) - 1))) - val -= (HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (op_mode); - } - else - return 0; - break; - - case SQRT: - return 0; - - default: - abort (); - } - - /* Clear the bits that don't belong in our mode, - unless they and our sign bit are all one. - So we get either a reasonable negative value or a reasonable - unsigned value for this mode. */ - if (width < HOST_BITS_PER_WIDE_INT - && ((val & ((HOST_WIDE_INT) (-1) << (width - 1))) - != ((HOST_WIDE_INT) (-1) << (width - 1)))) - val &= (1 << width) - 1; - - return GEN_INT (val); - } - - /* We can do some operations on integer CONST_DOUBLEs. Also allow - for a DImode operation on a CONST_INT. */ - else if (GET_MODE (op) == VOIDmode - && (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT)) - { - HOST_WIDE_INT l1, h1, lv, hv; - - if (GET_CODE (op) == CONST_DOUBLE) - l1 = CONST_DOUBLE_LOW (op), h1 = CONST_DOUBLE_HIGH (op); - else - l1 = INTVAL (op), h1 = l1 < 0 ? -1 : 0; - - switch (code) - { - case NOT: - lv = ~ l1; - hv = ~ h1; - break; - - case NEG: - neg_double (l1, h1, &lv, &hv); - break; - - case ABS: - if (h1 < 0) - neg_double (l1, h1, &lv, &hv); - else - lv = l1, hv = h1; - break; - - case FFS: - hv = 0; - if (l1 == 0) - lv = HOST_BITS_PER_WIDE_INT + exact_log2 (h1 & (-h1)) + 1; - else - lv = exact_log2 (l1 & (-l1)) + 1; - break; - - case TRUNCATE: - if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT) - return GEN_INT (l1 & GET_MODE_MASK (mode)); - else - return 0; - break; - - case ZERO_EXTEND: - if (op_mode == VOIDmode - || GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT) - return 0; - - hv = 0; - lv = l1 & GET_MODE_MASK (op_mode); - break; - - case SIGN_EXTEND: - if (op_mode == VOIDmode - || GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT) - return 0; - else - { - lv = l1 & GET_MODE_MASK (op_mode); - if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT - && (lv & ((HOST_WIDE_INT) 1 - << (GET_MODE_BITSIZE (op_mode) - 1))) != 0) - lv -= (HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (op_mode); - - hv = (lv < 0) ? ~ (HOST_WIDE_INT) 0 : 0; - } - break; - - case SQRT: - return 0; - - default: - return 0; - } - - return immed_double_const (lv, hv, mode); - } - -#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) - else if (GET_CODE (op) == CONST_DOUBLE - && GET_MODE_CLASS (mode) == MODE_FLOAT) - { - REAL_VALUE_TYPE d; - jmp_buf handler; - rtx x; - - if (setjmp (handler)) - /* There used to be a warning here, but that is inadvisable. - People may want to cause traps, and the natural way - to do it should not get a warning. */ - return 0; - - set_float_handler (handler); - - REAL_VALUE_FROM_CONST_DOUBLE (d, op); - - switch (code) - { - case NEG: - d = REAL_VALUE_NEGATE (d); - break; - - case ABS: - if (REAL_VALUE_NEGATIVE (d)) - d = REAL_VALUE_NEGATE (d); - break; - - case FLOAT_TRUNCATE: - d = real_value_truncate (mode, d); - break; - - case FLOAT_EXTEND: - /* All this does is change the mode. */ - break; - - case FIX: - d = REAL_VALUE_RNDZINT (d); - break; - - case UNSIGNED_FIX: - d = REAL_VALUE_UNSIGNED_RNDZINT (d); - break; - - case SQRT: - return 0; - - default: - abort (); - } - - x = immed_real_const_1 (d, mode); - set_float_handler (NULL_PTR); - return x; - } - else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE_CLASS (mode) == MODE_INT - && width <= HOST_BITS_PER_WIDE_INT && width > 0) - { - REAL_VALUE_TYPE d; - jmp_buf handler; - rtx x; - HOST_WIDE_INT val; - - if (setjmp (handler)) - return 0; - - set_float_handler (handler); - - REAL_VALUE_FROM_CONST_DOUBLE (d, op); - - switch (code) - { - case FIX: - val = REAL_VALUE_FIX (d); - break; - - case UNSIGNED_FIX: - val = REAL_VALUE_UNSIGNED_FIX (d); - break; - - default: - abort (); - } - - set_float_handler (NULL_PTR); - - /* Clear the bits that don't belong in our mode, - unless they and our sign bit are all one. - So we get either a reasonable negative value or a reasonable - unsigned value for this mode. */ - if (width < HOST_BITS_PER_WIDE_INT - && ((val & ((HOST_WIDE_INT) (-1) << (width - 1))) - != ((HOST_WIDE_INT) (-1) << (width - 1)))) - val &= ((HOST_WIDE_INT) 1 << width) - 1; - - return GEN_INT (val); - } -#endif - /* This was formerly used only for non-IEEE float. - eggert@twinsun.com says it is safe for IEEE also. */ - else - { - /* There are some simplifications we can do even if the operands - aren't constant. */ - switch (code) - { - case NEG: - case NOT: - /* (not (not X)) == X, similarly for NEG. */ - if (GET_CODE (op) == code) - return XEXP (op, 0); - break; - - case SIGN_EXTEND: - /* (sign_extend (truncate (minus (label_ref L1) (label_ref L2)))) - becomes just the MINUS if its mode is MODE. This allows - folding switch statements on machines using casesi (such as - the Vax). */ - if (GET_CODE (op) == TRUNCATE - && GET_MODE (XEXP (op, 0)) == mode - && GET_CODE (XEXP (op, 0)) == MINUS - && GET_CODE (XEXP (XEXP (op, 0), 0)) == LABEL_REF - && GET_CODE (XEXP (XEXP (op, 0), 1)) == LABEL_REF) - return XEXP (op, 0); - break; - } - - return 0; - } -} - -/* Simplify a binary operation CODE with result mode MODE, operating on OP0 - and OP1. Return 0 if no simplification is possible. - - Don't use this for relational operations such as EQ or LT. - Use simplify_relational_operation instead. */ - -rtx -simplify_binary_operation (code, mode, op0, op1) - enum rtx_code code; - enum machine_mode mode; - rtx op0, op1; -{ - register HOST_WIDE_INT arg0, arg1, arg0s, arg1s; - HOST_WIDE_INT val; - int width = GET_MODE_BITSIZE (mode); - rtx tem; - - /* Relational operations don't work here. We must know the mode - of the operands in order to do the comparison correctly. - Assuming a full word can give incorrect results. - Consider comparing 128 with -128 in QImode. */ - - if (GET_RTX_CLASS (code) == '<') - abort (); - -#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) - if (GET_MODE_CLASS (mode) == MODE_FLOAT - && GET_CODE (op0) == CONST_DOUBLE && GET_CODE (op1) == CONST_DOUBLE - && mode == GET_MODE (op0) && mode == GET_MODE (op1)) - { - REAL_VALUE_TYPE f0, f1, value; - jmp_buf handler; - - if (setjmp (handler)) - return 0; - - set_float_handler (handler); - - REAL_VALUE_FROM_CONST_DOUBLE (f0, op0); - REAL_VALUE_FROM_CONST_DOUBLE (f1, op1); - f0 = real_value_truncate (mode, f0); - f1 = real_value_truncate (mode, f1); - -#ifdef REAL_ARITHMETIC - REAL_ARITHMETIC (value, rtx_to_tree_code (code), f0, f1); -#else - switch (code) - { - case PLUS: - value = f0 + f1; - break; - case MINUS: - value = f0 - f1; - break; - case MULT: - value = f0 * f1; - break; - case DIV: -#ifndef REAL_INFINITY - if (f1 == 0) - return 0; -#endif - value = f0 / f1; - break; - case SMIN: - value = MIN (f0, f1); - break; - case SMAX: - value = MAX (f0, f1); - break; - default: - abort (); - } -#endif - - set_float_handler (NULL_PTR); - value = real_value_truncate (mode, value); - return immed_real_const_1 (value, mode); - } -#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */ - - /* We can fold some multi-word operations. */ - if (GET_MODE_CLASS (mode) == MODE_INT - && GET_CODE (op0) == CONST_DOUBLE - && (GET_CODE (op1) == CONST_DOUBLE || GET_CODE (op1) == CONST_INT)) - { - HOST_WIDE_INT l1, l2, h1, h2, lv, hv; - - l1 = CONST_DOUBLE_LOW (op0), h1 = CONST_DOUBLE_HIGH (op0); - - if (GET_CODE (op1) == CONST_DOUBLE) - l2 = CONST_DOUBLE_LOW (op1), h2 = CONST_DOUBLE_HIGH (op1); - else - l2 = INTVAL (op1), h2 = l2 < 0 ? -1 : 0; - - switch (code) - { - case MINUS: - /* A - B == A + (-B). */ - neg_double (l2, h2, &lv, &hv); - l2 = lv, h2 = hv; - - /* .. fall through ... */ - - case PLUS: - add_double (l1, h1, l2, h2, &lv, &hv); - break; - - case MULT: - mul_double (l1, h1, l2, h2, &lv, &hv); - break; - - case DIV: case MOD: case UDIV: case UMOD: - /* We'd need to include tree.h to do this and it doesn't seem worth - it. */ - return 0; - - case AND: - lv = l1 & l2, hv = h1 & h2; - break; - - case IOR: - lv = l1 | l2, hv = h1 | h2; - break; - - case XOR: - lv = l1 ^ l2, hv = h1 ^ h2; - break; - - case SMIN: - if (h1 < h2 - || (h1 == h2 - && ((unsigned HOST_WIDE_INT) l1 - < (unsigned HOST_WIDE_INT) l2))) - lv = l1, hv = h1; - else - lv = l2, hv = h2; - break; - - case SMAX: - if (h1 > h2 - || (h1 == h2 - && ((unsigned HOST_WIDE_INT) l1 - > (unsigned HOST_WIDE_INT) l2))) - lv = l1, hv = h1; - else - lv = l2, hv = h2; - break; - - case UMIN: - if ((unsigned HOST_WIDE_INT) h1 < (unsigned HOST_WIDE_INT) h2 - || (h1 == h2 - && ((unsigned HOST_WIDE_INT) l1 - < (unsigned HOST_WIDE_INT) l2))) - lv = l1, hv = h1; - else - lv = l2, hv = h2; - break; - - case UMAX: - if ((unsigned HOST_WIDE_INT) h1 > (unsigned HOST_WIDE_INT) h2 - || (h1 == h2 - && ((unsigned HOST_WIDE_INT) l1 - > (unsigned HOST_WIDE_INT) l2))) - lv = l1, hv = h1; - else - lv = l2, hv = h2; - break; - - case LSHIFTRT: case ASHIFTRT: - case ASHIFT: case LSHIFT: - case ROTATE: case ROTATERT: -#ifdef SHIFT_COUNT_TRUNCATED - l2 &= (GET_MODE_BITSIZE (mode) - 1), h2 = 0; -#endif - - if (h2 != 0 || l2 < 0 || l2 >= GET_MODE_BITSIZE (mode)) - return 0; - - if (code == LSHIFTRT || code == ASHIFTRT) - rshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv, - code == ASHIFTRT); - else if (code == ASHIFT || code == LSHIFT) - lshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv, - code == ASHIFT); - else if (code == ROTATE) - lrotate_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv); - else /* code == ROTATERT */ - rrotate_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv); - break; - - default: - return 0; - } - - return immed_double_const (lv, hv, mode); - } - - if (GET_CODE (op0) != CONST_INT || GET_CODE (op1) != CONST_INT - || width > HOST_BITS_PER_WIDE_INT || width == 0) - { - /* Even if we can't compute a constant result, - there are some cases worth simplifying. */ - - switch (code) - { - case PLUS: - /* In IEEE floating point, x+0 is not the same as x. Similarly - for the other optimizations below. */ - if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT - && GET_MODE_CLASS (mode) != MODE_INT) - break; - - if (op1 == CONST0_RTX (mode)) - return op0; - - /* ((-a) + b) -> (b - a) and similarly for (a + (-b)) */ - if (GET_CODE (op0) == NEG) - return cse_gen_binary (MINUS, mode, op1, XEXP (op0, 0)); - else if (GET_CODE (op1) == NEG) - return cse_gen_binary (MINUS, mode, op0, XEXP (op1, 0)); - - /* Handle both-operands-constant cases. We can only add - CONST_INTs to constants since the sum of relocatable symbols - can't be handled by most assemblers. */ - - if (CONSTANT_P (op0) && GET_CODE (op1) == CONST_INT) - return plus_constant (op0, INTVAL (op1)); - else if (CONSTANT_P (op1) && GET_CODE (op0) == CONST_INT) - return plus_constant (op1, INTVAL (op0)); - - /* If one of the operands is a PLUS or a MINUS, see if we can - simplify this by the associative law. - Don't use the associative law for floating point. - The inaccuracy makes it nonassociative, - and subtle programs can break if operations are associated. */ - - if ((GET_MODE_CLASS (mode) == MODE_INT - || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT) - && (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS - || GET_CODE (op1) == PLUS || GET_CODE (op1) == MINUS) - && (tem = simplify_plus_minus (code, mode, op0, op1)) != 0) - return tem; - break; - - case COMPARE: -#ifdef HAVE_cc0 - /* Convert (compare FOO (const_int 0)) to FOO unless we aren't - using cc0, in which case we want to leave it as a COMPARE - so we can distinguish it from a register-register-copy. - - In IEEE floating point, x-0 is not the same as x. */ - - if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT - || GET_MODE_CLASS (mode) == MODE_INT) - && op1 == CONST0_RTX (mode)) - return op0; -#else - /* Do nothing here. */ -#endif - break; - - case MINUS: - /* None of these optimizations can be done for IEEE - floating point. */ - if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT - && GET_MODE_CLASS (mode) != MODE_INT - && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT) - break; - - /* We can't assume x-x is 0 even with non-IEEE floating point. */ - if (rtx_equal_p (op0, op1) - && ! side_effects_p (op0) - && GET_MODE_CLASS (mode) != MODE_FLOAT - && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT) - return const0_rtx; - - /* Change subtraction from zero into negation. */ - if (op0 == CONST0_RTX (mode)) - return gen_rtx (NEG, mode, op1); - - /* (-1 - a) is ~a. */ - if (op0 == constm1_rtx) - return gen_rtx (NOT, mode, op1); - - /* Subtracting 0 has no effect. */ - if (op1 == CONST0_RTX (mode)) - return op0; - - /* (a - (-b)) -> (a + b). */ - if (GET_CODE (op1) == NEG) - return cse_gen_binary (PLUS, mode, op0, XEXP (op1, 0)); - - /* If one of the operands is a PLUS or a MINUS, see if we can - simplify this by the associative law. - Don't use the associative law for floating point. - The inaccuracy makes it nonassociative, - and subtle programs can break if operations are associated. */ - - if ((GET_MODE_CLASS (mode) == MODE_INT - || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT) - && (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS - || GET_CODE (op1) == PLUS || GET_CODE (op1) == MINUS) - && (tem = simplify_plus_minus (code, mode, op0, op1)) != 0) - return tem; - - /* Don't let a relocatable value get a negative coeff. */ - if (GET_CODE (op1) == CONST_INT) - return plus_constant (op0, - INTVAL (op1)); - break; - - case MULT: - if (op1 == constm1_rtx) - { - tem = simplify_unary_operation (NEG, mode, op0, mode); - - return tem ? tem : gen_rtx (NEG, mode, op0); - } - - /* In IEEE floating point, x*0 is not always 0. */ - if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT - || GET_MODE_CLASS (mode) == MODE_INT) - && op1 == CONST0_RTX (mode) - && ! side_effects_p (op0)) - return op1; - - /* In IEEE floating point, x*1 is not equivalent to x for nans. - However, ANSI says we can drop signals, - so we can do this anyway. */ - if (op1 == CONST1_RTX (mode)) - return op0; - - /* Convert multiply by constant power of two into shift. */ - if (GET_CODE (op1) == CONST_INT - && (val = exact_log2 (INTVAL (op1))) >= 0) - return gen_rtx (ASHIFT, mode, op0, GEN_INT (val)); - - if (GET_CODE (op1) == CONST_DOUBLE - && GET_MODE_CLASS (GET_MODE (op1)) == MODE_FLOAT) - { - REAL_VALUE_TYPE d; - jmp_buf handler; - int op1is2, op1ism1; - - if (setjmp (handler)) - return 0; - - set_float_handler (handler); - REAL_VALUE_FROM_CONST_DOUBLE (d, op1); - op1is2 = REAL_VALUES_EQUAL (d, dconst2); - op1ism1 = REAL_VALUES_EQUAL (d, dconstm1); - set_float_handler (NULL_PTR); - - /* x*2 is x+x and x*(-1) is -x */ - if (op1is2 && GET_MODE (op0) == mode) - return gen_rtx (PLUS, mode, op0, copy_rtx (op0)); - - else if (op1ism1 && GET_MODE (op0) == mode) - return gen_rtx (NEG, mode, op0); - } - break; - - case IOR: - if (op1 == const0_rtx) - return op0; - if (GET_CODE (op1) == CONST_INT - && (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode)) - return op1; - if (rtx_equal_p (op0, op1) && ! side_effects_p (op0)) - return op0; - /* A | (~A) -> -1 */ - if (((GET_CODE (op0) == NOT && rtx_equal_p (XEXP (op0, 0), op1)) - || (GET_CODE (op1) == NOT && rtx_equal_p (XEXP (op1, 0), op0))) - && ! side_effects_p (op0) - && GET_MODE_CLASS (mode) != MODE_CC) - return constm1_rtx; - break; - - case XOR: - if (op1 == const0_rtx) - return op0; - if (GET_CODE (op1) == CONST_INT - && (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode)) - return gen_rtx (NOT, mode, op0); - if (op0 == op1 && ! side_effects_p (op0) - && GET_MODE_CLASS (mode) != MODE_CC) - return const0_rtx; - break; - - case AND: - if (op1 == const0_rtx && ! side_effects_p (op0)) - return const0_rtx; - if (GET_CODE (op1) == CONST_INT - && (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode)) - return op0; - if (op0 == op1 && ! side_effects_p (op0) - && GET_MODE_CLASS (mode) != MODE_CC) - return op0; - /* A & (~A) -> 0 */ - if (((GET_CODE (op0) == NOT && rtx_equal_p (XEXP (op0, 0), op1)) - || (GET_CODE (op1) == NOT && rtx_equal_p (XEXP (op1, 0), op0))) - && ! side_effects_p (op0) - && GET_MODE_CLASS (mode) != MODE_CC) - return const0_rtx; - break; - - case UDIV: - /* Convert divide by power of two into shift (divide by 1 handled - below). */ - if (GET_CODE (op1) == CONST_INT - && (arg1 = exact_log2 (INTVAL (op1))) > 0) - return gen_rtx (LSHIFTRT, mode, op0, GEN_INT (arg1)); - - /* ... fall through ... */ - - case DIV: - if (op1 == CONST1_RTX (mode)) - return op0; - - /* In IEEE floating point, 0/x is not always 0. */ - if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT - || GET_MODE_CLASS (mode) == MODE_INT) - && op0 == CONST0_RTX (mode) - && ! side_effects_p (op1)) - return op0; - -#if 0 /* Turned off till an expert says this is a safe thing to do. */ -#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) - /* Change division by a constant into multiplication. */ - else if (GET_CODE (op1) == CONST_DOUBLE - && GET_MODE_CLASS (GET_MODE (op1)) == MODE_FLOAT - && op1 != CONST0_RTX (mode)) - { - REAL_VALUE_TYPE d; - REAL_VALUE_FROM_CONST_DOUBLE (d, op1); - if (REAL_VALUES_EQUAL (d, dconst0)) - abort(); -#if defined (REAL_ARITHMETIC) - REAL_ARITHMETIC (d, (int) RDIV_EXPR, dconst1, d); - return gen_rtx (MULT, mode, op0, - CONST_DOUBLE_FROM_REAL_VALUE (d, mode)); -#else - return gen_rtx (MULT, mode, op0, - CONST_DOUBLE_FROM_REAL_VALUE (1./d, mode)); - } -#endif -#endif -#endif - break; - - case UMOD: - /* Handle modulus by power of two (mod with 1 handled below). */ - if (GET_CODE (op1) == CONST_INT - && exact_log2 (INTVAL (op1)) > 0) - return gen_rtx (AND, mode, op0, GEN_INT (INTVAL (op1) - 1)); - - /* ... fall through ... */ - - case MOD: - if ((op0 == const0_rtx || op1 == const1_rtx) - && ! side_effects_p (op0) && ! side_effects_p (op1)) - return const0_rtx; - break; - - case ROTATERT: - case ROTATE: - /* Rotating ~0 always results in ~0. */ - if (GET_CODE (op0) == CONST_INT && width <= HOST_BITS_PER_WIDE_INT - && INTVAL (op0) == GET_MODE_MASK (mode) - && ! side_effects_p (op1)) - return op0; - - /* ... fall through ... */ - - case LSHIFT: - case ASHIFT: - case ASHIFTRT: - case LSHIFTRT: - if (op1 == const0_rtx) - return op0; - if (op0 == const0_rtx && ! side_effects_p (op1)) - return op0; - break; - - case SMIN: - if (width <= HOST_BITS_PER_WIDE_INT && GET_CODE (op1) == CONST_INT - && INTVAL (op1) == (HOST_WIDE_INT) 1 << (width -1) - && ! side_effects_p (op0)) - return op1; - else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0)) - return op0; - break; - - case SMAX: - if (width <= HOST_BITS_PER_WIDE_INT && GET_CODE (op1) == CONST_INT - && (INTVAL (op1) - == (unsigned HOST_WIDE_INT) GET_MODE_MASK (mode) >> 1) - && ! side_effects_p (op0)) - return op1; - else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0)) - return op0; - break; - - case UMIN: - if (op1 == const0_rtx && ! side_effects_p (op0)) - return op1; - else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0)) - return op0; - break; - - case UMAX: - if (op1 == constm1_rtx && ! side_effects_p (op0)) - return op1; - else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0)) - return op0; - break; - - default: - abort (); - } - - return 0; - } - - /* Get the integer argument values in two forms: - zero-extended in ARG0, ARG1 and sign-extended in ARG0S, ARG1S. */ - - arg0 = INTVAL (op0); - arg1 = INTVAL (op1); - - if (width < HOST_BITS_PER_WIDE_INT) - { - arg0 &= ((HOST_WIDE_INT) 1 << width) - 1; - arg1 &= ((HOST_WIDE_INT) 1 << width) - 1; - - arg0s = arg0; - if (arg0s & ((HOST_WIDE_INT) 1 << (width - 1))) - arg0s |= ((HOST_WIDE_INT) (-1) << width); - - arg1s = arg1; - if (arg1s & ((HOST_WIDE_INT) 1 << (width - 1))) - arg1s |= ((HOST_WIDE_INT) (-1) << width); - } - else - { - arg0s = arg0; - arg1s = arg1; - } - - /* Compute the value of the arithmetic. */ - - switch (code) - { - case PLUS: - val = arg0s + arg1s; - break; - - case MINUS: - val = arg0s - arg1s; - break; - - case MULT: - val = arg0s * arg1s; - break; - - case DIV: - if (arg1s == 0) - return 0; - val = arg0s / arg1s; - break; - - case MOD: - if (arg1s == 0) - return 0; - val = arg0s % arg1s; - break; - - case UDIV: - if (arg1 == 0) - return 0; - val = (unsigned HOST_WIDE_INT) arg0 / arg1; - break; - - case UMOD: - if (arg1 == 0) - return 0; - val = (unsigned HOST_WIDE_INT) arg0 % arg1; - break; - - case AND: - val = arg0 & arg1; - break; - - case IOR: - val = arg0 | arg1; - break; - - case XOR: - val = arg0 ^ arg1; - break; - - case LSHIFTRT: - /* If shift count is undefined, don't fold it; let the machine do - what it wants. But truncate it if the machine will do that. */ - if (arg1 < 0) - return 0; - -#ifdef SHIFT_COUNT_TRUNCATED - arg1 &= (BITS_PER_WORD - 1); -#endif - - if (arg1 >= width) - return 0; - - val = ((unsigned HOST_WIDE_INT) arg0) >> arg1; - break; - - case ASHIFT: - case LSHIFT: - if (arg1 < 0) - return 0; - -#ifdef SHIFT_COUNT_TRUNCATED - arg1 &= (BITS_PER_WORD - 1); -#endif - - if (arg1 >= width) - return 0; - - val = ((unsigned HOST_WIDE_INT) arg0) << arg1; - break; - - case ASHIFTRT: - if (arg1 < 0) - return 0; - -#ifdef SHIFT_COUNT_TRUNCATED - arg1 &= (BITS_PER_WORD - 1); -#endif - - if (arg1 >= width) - return 0; - - val = arg0s >> arg1; - - /* Bootstrap compiler may not have sign extended the right shift. - Manually extend the sign to insure bootstrap cc matches gcc. */ - if (arg0s < 0 && arg1 > 0) - val |= ((HOST_WIDE_INT) -1) << (HOST_BITS_PER_WIDE_INT - arg1); - - break; - - case ROTATERT: - if (arg1 < 0) - return 0; - - arg1 %= width; - val = ((((unsigned HOST_WIDE_INT) arg0) << (width - arg1)) - | (((unsigned HOST_WIDE_INT) arg0) >> arg1)); - break; - - case ROTATE: - if (arg1 < 0) - return 0; - - arg1 %= width; - val = ((((unsigned HOST_WIDE_INT) arg0) << arg1) - | (((unsigned HOST_WIDE_INT) arg0) >> (width - arg1))); - break; - - case COMPARE: - /* Do nothing here. */ - return 0; - - case SMIN: - val = arg0s <= arg1s ? arg0s : arg1s; - break; - - case UMIN: - val = ((unsigned HOST_WIDE_INT) arg0 - <= (unsigned HOST_WIDE_INT) arg1 ? arg0 : arg1); - break; - - case SMAX: - val = arg0s > arg1s ? arg0s : arg1s; - break; - - case UMAX: - val = ((unsigned HOST_WIDE_INT) arg0 - > (unsigned HOST_WIDE_INT) arg1 ? arg0 : arg1); - break; - - default: - abort (); - } - - /* Clear the bits that don't belong in our mode, unless they and our sign - bit are all one. So we get either a reasonable negative value or a - reasonable unsigned value for this mode. */ - if (width < HOST_BITS_PER_WIDE_INT - && ((val & ((HOST_WIDE_INT) (-1) << (width - 1))) - != ((HOST_WIDE_INT) (-1) << (width - 1)))) - val &= ((HOST_WIDE_INT) 1 << width) - 1; - - return GEN_INT (val); -} - -/* Simplify a PLUS or MINUS, at least one of whose operands may be another - PLUS or MINUS. - - Rather than test for specific case, we do this by a brute-force method - and do all possible simplifications until no more changes occur. Then - we rebuild the operation. */ - -static rtx -simplify_plus_minus (code, mode, op0, op1) - enum rtx_code code; - enum machine_mode mode; - rtx op0, op1; -{ - rtx ops[8]; - int negs[8]; - rtx result, tem; - int n_ops = 2, input_ops = 2, input_consts = 0, n_consts = 0; - int first = 1, negate = 0, changed; - int i, j; - - bzero (ops, sizeof ops); - - /* Set up the two operands and then expand them until nothing has been - changed. If we run out of room in our array, give up; this should - almost never happen. */ - - ops[0] = op0, ops[1] = op1, negs[0] = 0, negs[1] = (code == MINUS); - - changed = 1; - while (changed) - { - changed = 0; - - for (i = 0; i < n_ops; i++) - switch (GET_CODE (ops[i])) - { - case PLUS: - case MINUS: - if (n_ops == 7) - return 0; - - ops[n_ops] = XEXP (ops[i], 1); - negs[n_ops++] = GET_CODE (ops[i]) == MINUS ? !negs[i] : negs[i]; - ops[i] = XEXP (ops[i], 0); - input_ops++; - changed = 1; - break; - - case NEG: - ops[i] = XEXP (ops[i], 0); - negs[i] = ! negs[i]; - changed = 1; - break; - - case CONST: - ops[i] = XEXP (ops[i], 0); - input_consts++; - changed = 1; - break; - - case NOT: - /* ~a -> (-a - 1) */ - if (n_ops != 7) - { - ops[n_ops] = constm1_rtx; - negs[n_ops++] = negs[i]; - ops[i] = XEXP (ops[i], 0); - negs[i] = ! negs[i]; - changed = 1; - } - break; - - case CONST_INT: - if (negs[i]) - ops[i] = GEN_INT (- INTVAL (ops[i])), negs[i] = 0, changed = 1; - break; - } - } - - /* If we only have two operands, we can't do anything. */ - if (n_ops <= 2) - return 0; - - /* Now simplify each pair of operands until nothing changes. The first - time through just simplify constants against each other. */ - - changed = 1; - while (changed) - { - changed = first; - - for (i = 0; i < n_ops - 1; i++) - for (j = i + 1; j < n_ops; j++) - if (ops[i] != 0 && ops[j] != 0 - && (! first || (CONSTANT_P (ops[i]) && CONSTANT_P (ops[j])))) - { - rtx lhs = ops[i], rhs = ops[j]; - enum rtx_code ncode = PLUS; - - if (negs[i] && ! negs[j]) - lhs = ops[j], rhs = ops[i], ncode = MINUS; - else if (! negs[i] && negs[j]) - ncode = MINUS; - - tem = simplify_binary_operation (ncode, mode, lhs, rhs); - if (tem) - { - ops[i] = tem, ops[j] = 0; - negs[i] = negs[i] && negs[j]; - if (GET_CODE (tem) == NEG) - ops[i] = XEXP (tem, 0), negs[i] = ! negs[i]; - - if (GET_CODE (ops[i]) == CONST_INT && negs[i]) - ops[i] = GEN_INT (- INTVAL (ops[i])), negs[i] = 0; - changed = 1; - } - } - - first = 0; - } - - /* Pack all the operands to the lower-numbered entries and give up if - we didn't reduce the number of operands we had. Make sure we - count a CONST as two operands. If we have the same number of - operands, but have made more CONSTs than we had, this is also - an improvement, so accept it. */ - - for (i = 0, j = 0; j < n_ops; j++) - if (ops[j] != 0) - { - ops[i] = ops[j], negs[i++] = negs[j]; - if (GET_CODE (ops[j]) == CONST) - n_consts++; - } - - if (i + n_consts > input_ops - || (i + n_consts == input_ops && n_consts <= input_consts)) - return 0; - - n_ops = i; - - /* If we have a CONST_INT, put it last. */ - for (i = 0; i < n_ops - 1; i++) - if (GET_CODE (ops[i]) == CONST_INT) - { - tem = ops[n_ops - 1], ops[n_ops - 1] = ops[i] , ops[i] = tem; - j = negs[n_ops - 1], negs[n_ops - 1] = negs[i], negs[i] = j; - } - - /* Put a non-negated operand first. If there aren't any, make all - operands positive and negate the whole thing later. */ - for (i = 0; i < n_ops && negs[i]; i++) - ; - - if (i == n_ops) - { - for (i = 0; i < n_ops; i++) - negs[i] = 0; - negate = 1; - } - else if (i != 0) - { - tem = ops[0], ops[0] = ops[i], ops[i] = tem; - j = negs[0], negs[0] = negs[i], negs[i] = j; - } - - /* Now make the result by performing the requested operations. */ - result = ops[0]; - for (i = 1; i < n_ops; i++) - result = cse_gen_binary (negs[i] ? MINUS : PLUS, mode, result, ops[i]); - - return negate ? gen_rtx (NEG, mode, result) : result; -} - -/* Make a binary operation by properly ordering the operands and - seeing if the expression folds. */ - -static rtx -cse_gen_binary (code, mode, op0, op1) - enum rtx_code code; - enum machine_mode mode; - rtx op0, op1; -{ - rtx tem; - - /* Put complex operands first and constants second if commutative. */ - if (GET_RTX_CLASS (code) == 'c' - && ((CONSTANT_P (op0) && GET_CODE (op1) != CONST_INT) - || (GET_RTX_CLASS (GET_CODE (op0)) == 'o' - && GET_RTX_CLASS (GET_CODE (op1)) != 'o') - || (GET_CODE (op0) == SUBREG - && GET_RTX_CLASS (GET_CODE (SUBREG_REG (op0))) == 'o' - && GET_RTX_CLASS (GET_CODE (op1)) != 'o'))) - tem = op0, op0 = op1, op1 = tem; - - /* If this simplifies, do it. */ - tem = simplify_binary_operation (code, mode, op0, op1); - - if (tem) - return tem; - - /* Handle addition and subtraction of CONST_INT specially. Otherwise, - just form the operation. */ - - if (code == PLUS && GET_CODE (op1) == CONST_INT - && GET_MODE (op0) != VOIDmode) - return plus_constant (op0, INTVAL (op1)); - else if (code == MINUS && GET_CODE (op1) == CONST_INT - && GET_MODE (op0) != VOIDmode) - return plus_constant (op0, - INTVAL (op1)); - else - return gen_rtx (code, mode, op0, op1); -} - -/* Like simplify_binary_operation except used for relational operators. - MODE is the mode of the operands, not that of the result. */ - -rtx -simplify_relational_operation (code, mode, op0, op1) - enum rtx_code code; - enum machine_mode mode; - rtx op0, op1; -{ - register HOST_WIDE_INT arg0, arg1, arg0s, arg1s; - HOST_WIDE_INT val; - int width = GET_MODE_BITSIZE (mode); - - /* If op0 is a compare, extract the comparison arguments from it. */ - if (GET_CODE (op0) == COMPARE && op1 == const0_rtx) - op1 = XEXP (op0, 1), op0 = XEXP (op0, 0); - - /* What to do with MODE_CC isn't clear yet. - Let's make sure nothing erroneous is done. */ - if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC) - return 0; - - /* Unlike the arithmetic operations, we can do the comparison whether - or not WIDTH is larger than HOST_BITS_PER_WIDE_INT because the - CONST_INTs are to be understood as being infinite precision as - is the comparison. So there is no question of overflow. */ - - if (GET_CODE (op0) != CONST_INT || GET_CODE (op1) != CONST_INT || width == 0) - { - /* Even if we can't compute a constant result, - there are some cases worth simplifying. */ - - /* For non-IEEE floating-point, if the two operands are equal, we know - the result. */ - if (rtx_equal_p (op0, op1) - && (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT - || GET_MODE_CLASS (GET_MODE (op0)) != MODE_FLOAT)) - return (code == EQ || code == GE || code == LE || code == LEU - || code == GEU) ? const_true_rtx : const0_rtx; - -#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) - else if (GET_CODE (op0) == CONST_DOUBLE - && GET_CODE (op1) == CONST_DOUBLE - && GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT) - { - REAL_VALUE_TYPE d0, d1; - jmp_buf handler; - int op0lt, op1lt, equal; - - if (setjmp (handler)) - return 0; - - set_float_handler (handler); - REAL_VALUE_FROM_CONST_DOUBLE (d0, op0); - REAL_VALUE_FROM_CONST_DOUBLE (d1, op1); - equal = REAL_VALUES_EQUAL (d0, d1); - op0lt = REAL_VALUES_LESS (d0, d1); - op1lt = REAL_VALUES_LESS (d1, d0); - set_float_handler (NULL_PTR); - - switch (code) - { - case EQ: - return equal ? const_true_rtx : const0_rtx; - case NE: - return !equal ? const_true_rtx : const0_rtx; - case LE: - return equal || op0lt ? const_true_rtx : const0_rtx; - case LT: - return op0lt ? const_true_rtx : const0_rtx; - case GE: - return equal || op1lt ? const_true_rtx : const0_rtx; - case GT: - return op1lt ? const_true_rtx : const0_rtx; - } - } -#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */ - - else if (GET_MODE_CLASS (mode) == MODE_INT - && width > HOST_BITS_PER_WIDE_INT - && (GET_CODE (op0) == CONST_DOUBLE - || GET_CODE (op0) == CONST_INT) - && (GET_CODE (op1) == CONST_DOUBLE - || GET_CODE (op1) == CONST_INT)) - { - HOST_WIDE_INT h0, l0, h1, l1; - unsigned HOST_WIDE_INT uh0, ul0, uh1, ul1; - int op0lt, op0ltu, equal; - - if (GET_CODE (op0) == CONST_DOUBLE) - l0 = CONST_DOUBLE_LOW (op0), h0 = CONST_DOUBLE_HIGH (op0); - else - l0 = INTVAL (op0), h0 = l0 < 0 ? -1 : 0; - - if (GET_CODE (op1) == CONST_DOUBLE) - l1 = CONST_DOUBLE_LOW (op1), h1 = CONST_DOUBLE_HIGH (op1); - else - l1 = INTVAL (op1), h1 = l1 < 0 ? -1 : 0; - - uh0 = h0, ul0 = l0, uh1 = h1, ul1 = l1; - - equal = (h0 == h1 && l0 == l1); - op0lt = (h0 < h1 || (h0 == h1 && l0 < l1)); - op0ltu = (uh0 < uh1 || (uh0 == uh1 && ul0 < ul1)); - - switch (code) - { - case EQ: - return equal ? const_true_rtx : const0_rtx; - case NE: - return !equal ? const_true_rtx : const0_rtx; - case LE: - return equal || op0lt ? const_true_rtx : const0_rtx; - case LT: - return op0lt ? const_true_rtx : const0_rtx; - case GE: - return !op0lt ? const_true_rtx : const0_rtx; - case GT: - return !equal && !op0lt ? const_true_rtx : const0_rtx; - case LEU: - return equal || op0ltu ? const_true_rtx : const0_rtx; - case LTU: - return op0ltu ? const_true_rtx : const0_rtx; - case GEU: - return !op0ltu ? const_true_rtx : const0_rtx; - case GTU: - return !equal && !op0ltu ? const_true_rtx : const0_rtx; - } - } - - switch (code) - { - case EQ: - { -#if 0 - /* We can't make this assumption due to #pragma weak */ - if (CONSTANT_P (op0) && op1 == const0_rtx) - return const0_rtx; -#endif - if (NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx - /* On some machines, the ap reg can be 0 sometimes. */ - && op0 != arg_pointer_rtx) - return const0_rtx; - break; - } - - case NE: -#if 0 - /* We can't make this assumption due to #pragma weak */ - if (CONSTANT_P (op0) && op1 == const0_rtx) - return const_true_rtx; -#endif - if (NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx - /* On some machines, the ap reg can be 0 sometimes. */ - && op0 != arg_pointer_rtx) - return const_true_rtx; - break; - - case GEU: - /* Unsigned values are never negative, but we must be sure we are - actually comparing a value, not a CC operand. */ - if (op1 == const0_rtx - && GET_MODE_CLASS (mode) == MODE_INT) - return const_true_rtx; - break; - - case LTU: - if (op1 == const0_rtx - && GET_MODE_CLASS (mode) == MODE_INT) - return const0_rtx; - break; - - case LEU: - /* Unsigned values are never greater than the largest - unsigned value. */ - if (GET_CODE (op1) == CONST_INT - && INTVAL (op1) == GET_MODE_MASK (mode) - && GET_MODE_CLASS (mode) == MODE_INT) - return const_true_rtx; - break; - - case GTU: - if (GET_CODE (op1) == CONST_INT - && INTVAL (op1) == GET_MODE_MASK (mode) - && GET_MODE_CLASS (mode) == MODE_INT) - return const0_rtx; - break; - } - - return 0; - } - - /* Get the integer argument values in two forms: - zero-extended in ARG0, ARG1 and sign-extended in ARG0S, ARG1S. */ - - arg0 = INTVAL (op0); - arg1 = INTVAL (op1); - - if (width < HOST_BITS_PER_WIDE_INT) - { - arg0 &= ((HOST_WIDE_INT) 1 << width) - 1; - arg1 &= ((HOST_WIDE_INT) 1 << width) - 1; - - arg0s = arg0; - if (arg0s & ((HOST_WIDE_INT) 1 << (width - 1))) - arg0s |= ((HOST_WIDE_INT) (-1) << width); - - arg1s = arg1; - if (arg1s & ((HOST_WIDE_INT) 1 << (width - 1))) - arg1s |= ((HOST_WIDE_INT) (-1) << width); - } - else - { - arg0s = arg0; - arg1s = arg1; - } - - /* Compute the value of the arithmetic. */ - - switch (code) - { - case NE: - val = arg0 != arg1 ? STORE_FLAG_VALUE : 0; - break; - - case EQ: - val = arg0 == arg1 ? STORE_FLAG_VALUE : 0; - break; - - case LE: - val = arg0s <= arg1s ? STORE_FLAG_VALUE : 0; - break; - - case LT: - val = arg0s < arg1s ? STORE_FLAG_VALUE : 0; - break; - - case GE: - val = arg0s >= arg1s ? STORE_FLAG_VALUE : 0; - break; - - case GT: - val = arg0s > arg1s ? STORE_FLAG_VALUE : 0; - break; - - case LEU: - val = (((unsigned HOST_WIDE_INT) arg0) - <= ((unsigned HOST_WIDE_INT) arg1) ? STORE_FLAG_VALUE : 0); - break; - - case LTU: - val = (((unsigned HOST_WIDE_INT) arg0) - < ((unsigned HOST_WIDE_INT) arg1) ? STORE_FLAG_VALUE : 0); - break; - - case GEU: - val = (((unsigned HOST_WIDE_INT) arg0) - >= ((unsigned HOST_WIDE_INT) arg1) ? STORE_FLAG_VALUE : 0); - break; - - case GTU: - val = (((unsigned HOST_WIDE_INT) arg0) - > ((unsigned HOST_WIDE_INT) arg1) ? STORE_FLAG_VALUE : 0); - break; - - default: - abort (); - } - - /* Clear the bits that don't belong in our mode, unless they and our sign - bit are all one. So we get either a reasonable negative value or a - reasonable unsigned value for this mode. */ - if (width < HOST_BITS_PER_WIDE_INT - && ((val & ((HOST_WIDE_INT) (-1) << (width - 1))) - != ((HOST_WIDE_INT) (-1) << (width - 1)))) - val &= ((HOST_WIDE_INT) 1 << width) - 1; - - return GEN_INT (val); -} - -/* Simplify CODE, an operation with result mode MODE and three operands, - OP0, OP1, and OP2. OP0_MODE was the mode of OP0 before it became - a constant. Return 0 if no simplifications is possible. */ - -rtx -simplify_ternary_operation (code, mode, op0_mode, op0, op1, op2) - enum rtx_code code; - enum machine_mode mode, op0_mode; - rtx op0, op1, op2; -{ - int width = GET_MODE_BITSIZE (mode); - - /* VOIDmode means "infinite" precision. */ - if (width == 0) - width = HOST_BITS_PER_WIDE_INT; - - switch (code) - { - case SIGN_EXTRACT: - case ZERO_EXTRACT: - if (GET_CODE (op0) == CONST_INT - && GET_CODE (op1) == CONST_INT - && GET_CODE (op2) == CONST_INT - && INTVAL (op1) + INTVAL (op2) <= GET_MODE_BITSIZE (op0_mode) - && width <= HOST_BITS_PER_WIDE_INT) - { - /* Extracting a bit-field from a constant */ - HOST_WIDE_INT val = INTVAL (op0); - -#if BITS_BIG_ENDIAN - val >>= (GET_MODE_BITSIZE (op0_mode) - INTVAL (op2) - INTVAL (op1)); -#else - val >>= INTVAL (op2); -#endif - if (HOST_BITS_PER_WIDE_INT != INTVAL (op1)) - { - /* First zero-extend. */ - val &= ((HOST_WIDE_INT) 1 << INTVAL (op1)) - 1; - /* If desired, propagate sign bit. */ - if (code == SIGN_EXTRACT - && (val & ((HOST_WIDE_INT) 1 << (INTVAL (op1) - 1)))) - val |= ~ (((HOST_WIDE_INT) 1 << INTVAL (op1)) - 1); - } - - /* Clear the bits that don't belong in our mode, - unless they and our sign bit are all one. - So we get either a reasonable negative value or a reasonable - unsigned value for this mode. */ - if (width < HOST_BITS_PER_WIDE_INT - && ((val & ((HOST_WIDE_INT) (-1) << (width - 1))) - != ((HOST_WIDE_INT) (-1) << (width - 1)))) - val &= ((HOST_WIDE_INT) 1 << width) - 1; - - return GEN_INT (val); - } - break; - - case IF_THEN_ELSE: - if (GET_CODE (op0) == CONST_INT) - return op0 != const0_rtx ? op1 : op2; - break; - - default: - abort (); - } - - return 0; -} - -/* If X is a nontrivial arithmetic operation on an argument - for which a constant value can be determined, return - the result of operating on that value, as a constant. - Otherwise, return X, possibly with one or more operands - modified by recursive calls to this function. - - If X is a register whose contents are known, we do NOT - return those contents here. equiv_constant is called to - perform that task. - - INSN is the insn that we may be modifying. If it is 0, make a copy - of X before modifying it. */ - -static rtx -fold_rtx (x, insn) - rtx x; - rtx insn; -{ - register enum rtx_code code; - register enum machine_mode mode; - register char *fmt; - register int i; - rtx new = 0; - int copied = 0; - int must_swap = 0; - - /* Folded equivalents of first two operands of X. */ - rtx folded_arg0; - rtx folded_arg1; - - /* Constant equivalents of first three operands of X; - 0 when no such equivalent is known. */ - rtx const_arg0; - rtx const_arg1; - rtx const_arg2; - - /* The mode of the first operand of X. We need this for sign and zero - extends. */ - enum machine_mode mode_arg0; - - if (x == 0) - return x; - - mode = GET_MODE (x); - code = GET_CODE (x); - switch (code) - { - case CONST: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case LABEL_REF: - case REG: - /* No use simplifying an EXPR_LIST - since they are used only for lists of args - in a function call's REG_EQUAL note. */ - case EXPR_LIST: - return x; - -#ifdef HAVE_cc0 - case CC0: - return prev_insn_cc0; -#endif - - case PC: - /* If the next insn is a CODE_LABEL followed by a jump table, - PC's value is a LABEL_REF pointing to that label. That - lets us fold switch statements on the Vax. */ - if (insn && GET_CODE (insn) == JUMP_INSN) - { - rtx next = next_nonnote_insn (insn); - - if (next && GET_CODE (next) == CODE_LABEL - && NEXT_INSN (next) != 0 - && GET_CODE (NEXT_INSN (next)) == JUMP_INSN - && (GET_CODE (PATTERN (NEXT_INSN (next))) == ADDR_VEC - || GET_CODE (PATTERN (NEXT_INSN (next))) == ADDR_DIFF_VEC)) - return gen_rtx (LABEL_REF, Pmode, next); - } - break; - - case SUBREG: - /* See if we previously assigned a constant value to this SUBREG. */ - if ((new = lookup_as_function (x, CONST_INT)) != 0 - || (new = lookup_as_function (x, CONST_DOUBLE)) != 0) - return new; - - /* If this is a paradoxical SUBREG, we have no idea what value the - extra bits would have. However, if the operand is equivalent - to a SUBREG whose operand is the same as our mode, and all the - modes are within a word, we can just use the inner operand - because these SUBREGs just say how to treat the register. */ - - if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) - { - enum machine_mode imode = GET_MODE (SUBREG_REG (x)); - struct table_elt *elt; - - if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD - && GET_MODE_SIZE (imode) <= UNITS_PER_WORD - && (elt = lookup (SUBREG_REG (x), HASH (SUBREG_REG (x), imode), - imode)) != 0) - { - for (elt = elt->first_same_value; - elt; elt = elt->next_same_value) - if (GET_CODE (elt->exp) == SUBREG - && GET_MODE (SUBREG_REG (elt->exp)) == mode - && exp_equiv_p (elt->exp, elt->exp, 1, 0)) - return copy_rtx (SUBREG_REG (elt->exp)); - } - - return x; - } - - /* Fold SUBREG_REG. If it changed, see if we can simplify the SUBREG. - We might be able to if the SUBREG is extracting a single word in an - integral mode or extracting the low part. */ - - folded_arg0 = fold_rtx (SUBREG_REG (x), insn); - const_arg0 = equiv_constant (folded_arg0); - if (const_arg0) - folded_arg0 = const_arg0; - - if (folded_arg0 != SUBREG_REG (x)) - { - new = 0; - - if (GET_MODE_CLASS (mode) == MODE_INT - && GET_MODE_SIZE (mode) == UNITS_PER_WORD - && GET_MODE (SUBREG_REG (x)) != VOIDmode) - new = operand_subword (folded_arg0, SUBREG_WORD (x), 0, - GET_MODE (SUBREG_REG (x))); - if (new == 0 && subreg_lowpart_p (x)) - new = gen_lowpart_if_possible (mode, folded_arg0); - if (new) - return new; - } - - /* If this is a narrowing SUBREG and our operand is a REG, see if - we can find an equivalence for REG that is an arithmetic operation - in a wider mode where both operands are paradoxical SUBREGs - from objects of our result mode. In that case, we couldn't report - an equivalent value for that operation, since we don't know what the - extra bits will be. But we can find an equivalence for this SUBREG - by folding that operation is the narrow mode. This allows us to - fold arithmetic in narrow modes when the machine only supports - word-sized arithmetic. - - Also look for a case where we have a SUBREG whose operand is the - same as our result. If both modes are smaller than a word, we - are simply interpreting a register in different modes and we - can use the inner value. */ - - if (GET_CODE (folded_arg0) == REG - && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (folded_arg0)) - && subreg_lowpart_p (x)) - { - struct table_elt *elt; - - /* We can use HASH here since we know that canon_hash won't be - called. */ - elt = lookup (folded_arg0, - HASH (folded_arg0, GET_MODE (folded_arg0)), - GET_MODE (folded_arg0)); - - if (elt) - elt = elt->first_same_value; - - for (; elt; elt = elt->next_same_value) - { - enum rtx_code eltcode = GET_CODE (elt->exp); - - /* Just check for unary and binary operations. */ - if (GET_RTX_CLASS (GET_CODE (elt->exp)) == '1' - && GET_CODE (elt->exp) != SIGN_EXTEND - && GET_CODE (elt->exp) != ZERO_EXTEND - && GET_CODE (XEXP (elt->exp, 0)) == SUBREG - && GET_MODE (SUBREG_REG (XEXP (elt->exp, 0))) == mode) - { - rtx op0 = SUBREG_REG (XEXP (elt->exp, 0)); - - if (GET_CODE (op0) != REG && ! CONSTANT_P (op0)) - op0 = fold_rtx (op0, NULL_RTX); - - op0 = equiv_constant (op0); - if (op0) - new = simplify_unary_operation (GET_CODE (elt->exp), mode, - op0, mode); - } - else if ((GET_RTX_CLASS (GET_CODE (elt->exp)) == '2' - || GET_RTX_CLASS (GET_CODE (elt->exp)) == 'c') - && eltcode != DIV && eltcode != MOD - && eltcode != UDIV && eltcode != UMOD - && eltcode != ASHIFTRT && eltcode != LSHIFTRT - && eltcode != ROTATE && eltcode != ROTATERT - && ((GET_CODE (XEXP (elt->exp, 0)) == SUBREG - && (GET_MODE (SUBREG_REG (XEXP (elt->exp, 0))) - == mode)) - || CONSTANT_P (XEXP (elt->exp, 0))) - && ((GET_CODE (XEXP (elt->exp, 1)) == SUBREG - && (GET_MODE (SUBREG_REG (XEXP (elt->exp, 1))) - == mode)) - || CONSTANT_P (XEXP (elt->exp, 1)))) - { - rtx op0 = gen_lowpart_common (mode, XEXP (elt->exp, 0)); - rtx op1 = gen_lowpart_common (mode, XEXP (elt->exp, 1)); - - if (op0 && GET_CODE (op0) != REG && ! CONSTANT_P (op0)) - op0 = fold_rtx (op0, NULL_RTX); - - if (op0) - op0 = equiv_constant (op0); - - if (op1 && GET_CODE (op1) != REG && ! CONSTANT_P (op1)) - op1 = fold_rtx (op1, NULL_RTX); - - if (op1) - op1 = equiv_constant (op1); - - if (op0 && op1) - new = simplify_binary_operation (GET_CODE (elt->exp), mode, - op0, op1); - } - - else if (GET_CODE (elt->exp) == SUBREG - && GET_MODE (SUBREG_REG (elt->exp)) == mode - && (GET_MODE_SIZE (GET_MODE (folded_arg0)) - <= UNITS_PER_WORD) - && exp_equiv_p (elt->exp, elt->exp, 1, 0)) - new = copy_rtx (SUBREG_REG (elt->exp)); - - if (new) - return new; - } - } - - return x; - - case NOT: - case NEG: - /* If we have (NOT Y), see if Y is known to be (NOT Z). - If so, (NOT Y) simplifies to Z. Similarly for NEG. */ - new = lookup_as_function (XEXP (x, 0), code); - if (new) - return fold_rtx (copy_rtx (XEXP (new, 0)), insn); - break; - - case MEM: - /* If we are not actually processing an insn, don't try to find the - best address. Not only don't we care, but we could modify the - MEM in an invalid way since we have no insn to validate against. */ - if (insn != 0) - find_best_addr (insn, &XEXP (x, 0)); - - { - /* Even if we don't fold in the insn itself, - we can safely do so here, in hopes of getting a constant. */ - rtx addr = fold_rtx (XEXP (x, 0), NULL_RTX); - rtx base = 0; - HOST_WIDE_INT offset = 0; - - if (GET_CODE (addr) == REG - && REGNO_QTY_VALID_P (REGNO (addr)) - && GET_MODE (addr) == qty_mode[reg_qty[REGNO (addr)]] - && qty_const[reg_qty[REGNO (addr)]] != 0) - addr = qty_const[reg_qty[REGNO (addr)]]; - - /* If address is constant, split it into a base and integer offset. */ - if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF) - base = addr; - else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == PLUS - && GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT) - { - base = XEXP (XEXP (addr, 0), 0); - offset = INTVAL (XEXP (XEXP (addr, 0), 1)); - } - else if (GET_CODE (addr) == LO_SUM - && GET_CODE (XEXP (addr, 1)) == SYMBOL_REF) - base = XEXP (addr, 1); - - /* If this is a constant pool reference, we can fold it into its - constant to allow better value tracking. */ - if (base && GET_CODE (base) == SYMBOL_REF - && CONSTANT_POOL_ADDRESS_P (base)) - { - rtx constant = get_pool_constant (base); - enum machine_mode const_mode = get_pool_mode (base); - rtx new; - - if (CONSTANT_P (constant) && GET_CODE (constant) != CONST_INT) - constant_pool_entries_cost = COST (constant); - - /* If we are loading the full constant, we have an equivalence. */ - if (offset == 0 && mode == const_mode) - return constant; - - /* If this actually isn't a constant (wierd!), we can't do - anything. Otherwise, handle the two most common cases: - extracting a word from a multi-word constant, and extracting - the low-order bits. Other cases don't seem common enough to - worry about. */ - if (! CONSTANT_P (constant)) - return x; - - if (GET_MODE_CLASS (mode) == MODE_INT - && GET_MODE_SIZE (mode) == UNITS_PER_WORD - && offset % UNITS_PER_WORD == 0 - && (new = operand_subword (constant, - offset / UNITS_PER_WORD, - 0, const_mode)) != 0) - return new; - - if (((BYTES_BIG_ENDIAN - && offset == GET_MODE_SIZE (GET_MODE (constant)) - 1) - || (! BYTES_BIG_ENDIAN && offset == 0)) - && (new = gen_lowpart_if_possible (mode, constant)) != 0) - return new; - } - - /* If this is a reference to a label at a known position in a jump - table, we also know its value. */ - if (base && GET_CODE (base) == LABEL_REF) - { - rtx label = XEXP (base, 0); - rtx table_insn = NEXT_INSN (label); - - if (table_insn && GET_CODE (table_insn) == JUMP_INSN - && GET_CODE (PATTERN (table_insn)) == ADDR_VEC) - { - rtx table = PATTERN (table_insn); - - if (offset >= 0 - && (offset / GET_MODE_SIZE (GET_MODE (table)) - < XVECLEN (table, 0))) - return XVECEXP (table, 0, - offset / GET_MODE_SIZE (GET_MODE (table))); - } - if (table_insn && GET_CODE (table_insn) == JUMP_INSN - && GET_CODE (PATTERN (table_insn)) == ADDR_DIFF_VEC) - { - rtx table = PATTERN (table_insn); - - if (offset >= 0 - && (offset / GET_MODE_SIZE (GET_MODE (table)) - < XVECLEN (table, 1))) - { - offset /= GET_MODE_SIZE (GET_MODE (table)); - new = gen_rtx (MINUS, Pmode, XVECEXP (table, 1, offset), - XEXP (table, 0)); - - if (GET_MODE (table) != Pmode) - new = gen_rtx (TRUNCATE, GET_MODE (table), new); - - return new; - } - } - } - - return x; - } - } - - const_arg0 = 0; - const_arg1 = 0; - const_arg2 = 0; - mode_arg0 = VOIDmode; - - /* Try folding our operands. - Then see which ones have constant values known. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - { - rtx arg = XEXP (x, i); - rtx folded_arg = arg, const_arg = 0; - enum machine_mode mode_arg = GET_MODE (arg); - rtx cheap_arg, expensive_arg; - rtx replacements[2]; - int j; - - /* Most arguments are cheap, so handle them specially. */ - switch (GET_CODE (arg)) - { - case REG: - /* This is the same as calling equiv_constant; it is duplicated - here for speed. */ - if (REGNO_QTY_VALID_P (REGNO (arg)) - && qty_const[reg_qty[REGNO (arg)]] != 0 - && GET_CODE (qty_const[reg_qty[REGNO (arg)]]) != REG - && GET_CODE (qty_const[reg_qty[REGNO (arg)]]) != PLUS) - const_arg - = gen_lowpart_if_possible (GET_MODE (arg), - qty_const[reg_qty[REGNO (arg)]]); - break; - - case CONST: - case CONST_INT: - case SYMBOL_REF: - case LABEL_REF: - case CONST_DOUBLE: - const_arg = arg; - break; - -#ifdef HAVE_cc0 - case CC0: - folded_arg = prev_insn_cc0; - mode_arg = prev_insn_cc0_mode; - const_arg = equiv_constant (folded_arg); - break; -#endif - - default: - folded_arg = fold_rtx (arg, insn); - const_arg = equiv_constant (folded_arg); - } - - /* For the first three operands, see if the operand - is constant or equivalent to a constant. */ - switch (i) - { - case 0: - folded_arg0 = folded_arg; - const_arg0 = const_arg; - mode_arg0 = mode_arg; - break; - case 1: - folded_arg1 = folded_arg; - const_arg1 = const_arg; - break; - case 2: - const_arg2 = const_arg; - break; - } - - /* Pick the least expensive of the folded argument and an - equivalent constant argument. */ - if (const_arg == 0 || const_arg == folded_arg - || COST (const_arg) > COST (folded_arg)) - cheap_arg = folded_arg, expensive_arg = const_arg; - else - cheap_arg = const_arg, expensive_arg = folded_arg; - - /* Try to replace the operand with the cheapest of the two - possibilities. If it doesn't work and this is either of the first - two operands of a commutative operation, try swapping them. - If THAT fails, try the more expensive, provided it is cheaper - than what is already there. */ - - if (cheap_arg == XEXP (x, i)) - continue; - - if (insn == 0 && ! copied) - { - x = copy_rtx (x); - copied = 1; - } - - replacements[0] = cheap_arg, replacements[1] = expensive_arg; - for (j = 0; - j < 2 && replacements[j] - && COST (replacements[j]) < COST (XEXP (x, i)); - j++) - { - if (validate_change (insn, &XEXP (x, i), replacements[j], 0)) - break; - - if (code == NE || code == EQ || GET_RTX_CLASS (code) == 'c') - { - validate_change (insn, &XEXP (x, i), XEXP (x, 1 - i), 1); - validate_change (insn, &XEXP (x, 1 - i), replacements[j], 1); - - if (apply_change_group ()) - { - /* Swap them back to be invalid so that this loop can - continue and flag them to be swapped back later. */ - rtx tem; - - tem = XEXP (x, 0); XEXP (x, 0) = XEXP (x, 1); - XEXP (x, 1) = tem; - must_swap = 1; - break; - } - } - } - } - - else if (fmt[i] == 'E') - /* Don't try to fold inside of a vector of expressions. - Doing nothing is harmless. */ - ; - - /* If a commutative operation, place a constant integer as the second - operand unless the first operand is also a constant integer. Otherwise, - place any constant second unless the first operand is also a constant. */ - - if (code == EQ || code == NE || GET_RTX_CLASS (code) == 'c') - { - if (must_swap || (const_arg0 - && (const_arg1 == 0 - || (GET_CODE (const_arg0) == CONST_INT - && GET_CODE (const_arg1) != CONST_INT)))) - { - register rtx tem = XEXP (x, 0); - - if (insn == 0 && ! copied) - { - x = copy_rtx (x); - copied = 1; - } - - validate_change (insn, &XEXP (x, 0), XEXP (x, 1), 1); - validate_change (insn, &XEXP (x, 1), tem, 1); - if (apply_change_group ()) - { - tem = const_arg0, const_arg0 = const_arg1, const_arg1 = tem; - tem = folded_arg0, folded_arg0 = folded_arg1, folded_arg1 = tem; - } - } - } - - /* If X is an arithmetic operation, see if we can simplify it. */ - - switch (GET_RTX_CLASS (code)) - { - case '1': - /* We can't simplify extension ops unless we know the original mode. */ - if ((code == ZERO_EXTEND || code == SIGN_EXTEND) - && mode_arg0 == VOIDmode) - break; - new = simplify_unary_operation (code, mode, - const_arg0 ? const_arg0 : folded_arg0, - mode_arg0); - break; - - case '<': - /* See what items are actually being compared and set FOLDED_ARG[01] - to those values and CODE to the actual comparison code. If any are - constant, set CONST_ARG0 and CONST_ARG1 appropriately. We needn't - do anything if both operands are already known to be constant. */ - - if (const_arg0 == 0 || const_arg1 == 0) - { - struct table_elt *p0, *p1; - rtx true = const_true_rtx, false = const0_rtx; - enum machine_mode mode_arg1; - -#ifdef FLOAT_STORE_FLAG_VALUE - if (GET_MODE_CLASS (mode) == MODE_FLOAT) - { - true = immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, mode); - false = CONST0_RTX (mode); - } -#endif - - code = find_comparison_args (code, &folded_arg0, &folded_arg1, - &mode_arg0, &mode_arg1); - const_arg0 = equiv_constant (folded_arg0); - const_arg1 = equiv_constant (folded_arg1); - - /* If the mode is VOIDmode or a MODE_CC mode, we don't know - what kinds of things are being compared, so we can't do - anything with this comparison. */ - - if (mode_arg0 == VOIDmode || GET_MODE_CLASS (mode_arg0) == MODE_CC) - break; - - /* If we do not now have two constants being compared, see if we - can nevertheless deduce some things about the comparison. */ - if (const_arg0 == 0 || const_arg1 == 0) - { - /* Is FOLDED_ARG0 frame-pointer plus a constant? Or non-explicit - constant? These aren't zero, but we don't know their sign. */ - if (const_arg1 == const0_rtx - && (NONZERO_BASE_PLUS_P (folded_arg0) -#if 0 /* Sad to say, on sysvr4, #pragma weak can make a symbol address - come out as 0. */ - || GET_CODE (folded_arg0) == SYMBOL_REF -#endif - || GET_CODE (folded_arg0) == LABEL_REF - || GET_CODE (folded_arg0) == CONST)) - { - if (code == EQ) - return false; - else if (code == NE) - return true; - } - - /* See if the two operands are the same. We don't do this - for IEEE floating-point since we can't assume x == x - since x might be a NaN. */ - - if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT - || GET_MODE_CLASS (mode_arg0) != MODE_FLOAT) - && (folded_arg0 == folded_arg1 - || (GET_CODE (folded_arg0) == REG - && GET_CODE (folded_arg1) == REG - && (reg_qty[REGNO (folded_arg0)] - == reg_qty[REGNO (folded_arg1)])) - || ((p0 = lookup (folded_arg0, - (safe_hash (folded_arg0, mode_arg0) - % NBUCKETS), mode_arg0)) - && (p1 = lookup (folded_arg1, - (safe_hash (folded_arg1, mode_arg0) - % NBUCKETS), mode_arg0)) - && p0->first_same_value == p1->first_same_value))) - return ((code == EQ || code == LE || code == GE - || code == LEU || code == GEU) - ? true : false); - - /* If FOLDED_ARG0 is a register, see if the comparison we are - doing now is either the same as we did before or the reverse - (we only check the reverse if not floating-point). */ - else if (GET_CODE (folded_arg0) == REG) - { - int qty = reg_qty[REGNO (folded_arg0)]; - - if (REGNO_QTY_VALID_P (REGNO (folded_arg0)) - && (comparison_dominates_p (qty_comparison_code[qty], code) - || (comparison_dominates_p (qty_comparison_code[qty], - reverse_condition (code)) - && GET_MODE_CLASS (mode_arg0) == MODE_INT)) - && (rtx_equal_p (qty_comparison_const[qty], folded_arg1) - || (const_arg1 - && rtx_equal_p (qty_comparison_const[qty], - const_arg1)) - || (GET_CODE (folded_arg1) == REG - && (reg_qty[REGNO (folded_arg1)] - == qty_comparison_qty[qty])))) - return (comparison_dominates_p (qty_comparison_code[qty], - code) - ? true : false); - } - } - } - - /* If we are comparing against zero, see if the first operand is - equivalent to an IOR with a constant. If so, we may be able to - determine the result of this comparison. */ - - if (const_arg1 == const0_rtx) - { - rtx y = lookup_as_function (folded_arg0, IOR); - rtx inner_const; - - if (y != 0 - && (inner_const = equiv_constant (XEXP (y, 1))) != 0 - && GET_CODE (inner_const) == CONST_INT - && INTVAL (inner_const) != 0) - { - int sign_bitnum = GET_MODE_BITSIZE (mode_arg0) - 1; - int has_sign = (HOST_BITS_PER_WIDE_INT >= sign_bitnum - && (INTVAL (inner_const) - & ((HOST_WIDE_INT) 1 << sign_bitnum))); - rtx true = const_true_rtx, false = const0_rtx; - -#ifdef FLOAT_STORE_FLAG_VALUE - if (GET_MODE_CLASS (mode) == MODE_FLOAT) - { - true = immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, mode); - false = CONST0_RTX (mode); - } -#endif - - switch (code) - { - case EQ: - return false; - case NE: - return true; - case LT: case LE: - if (has_sign) - return true; - break; - case GT: case GE: - if (has_sign) - return false; - break; - } - } - } - - new = simplify_relational_operation (code, mode_arg0, - const_arg0 ? const_arg0 : folded_arg0, - const_arg1 ? const_arg1 : folded_arg1); -#ifdef FLOAT_STORE_FLAG_VALUE - if (new != 0 && GET_MODE_CLASS (mode) == MODE_FLOAT) - new = ((new == const0_rtx) ? CONST0_RTX (mode) - : immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, mode)); -#endif - break; - - case '2': - case 'c': - switch (code) - { - case PLUS: - /* If the second operand is a LABEL_REF, see if the first is a MINUS - with that LABEL_REF as its second operand. If so, the result is - the first operand of that MINUS. This handles switches with an - ADDR_DIFF_VEC table. */ - if (const_arg1 && GET_CODE (const_arg1) == LABEL_REF) - { - rtx y = lookup_as_function (folded_arg0, MINUS); - - if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF - && XEXP (XEXP (y, 1), 0) == XEXP (const_arg1, 0)) - return XEXP (y, 0); - } - goto from_plus; - - case MINUS: - /* If we have (MINUS Y C), see if Y is known to be (PLUS Z C2). - If so, produce (PLUS Z C2-C). */ - if (const_arg1 != 0 && GET_CODE (const_arg1) == CONST_INT) - { - rtx y = lookup_as_function (XEXP (x, 0), PLUS); - if (y && GET_CODE (XEXP (y, 1)) == CONST_INT) - return fold_rtx (plus_constant (y, -INTVAL (const_arg1)), - NULL_RTX); - } - - /* ... fall through ... */ - - from_plus: - case SMIN: case SMAX: case UMIN: case UMAX: - case IOR: case AND: case XOR: - case MULT: case DIV: case UDIV: - case ASHIFT: case LSHIFTRT: case ASHIFTRT: - /* If we have ( ) for an associative OP and REG - is known to be of similar form, we may be able to replace the - operation with a combined operation. This may eliminate the - intermediate operation if every use is simplified in this way. - Note that the similar optimization done by combine.c only works - if the intermediate operation's result has only one reference. */ - - if (GET_CODE (folded_arg0) == REG - && const_arg1 && GET_CODE (const_arg1) == CONST_INT) - { - int is_shift - = (code == ASHIFT || code == ASHIFTRT || code == LSHIFTRT); - rtx y = lookup_as_function (folded_arg0, code); - rtx inner_const; - enum rtx_code associate_code; - rtx new_const; - - if (y == 0 - || 0 == (inner_const - = equiv_constant (fold_rtx (XEXP (y, 1), 0))) - || GET_CODE (inner_const) != CONST_INT - /* If we have compiled a statement like - "if (x == (x & mask1))", and now are looking at - "x & mask2", we will have a case where the first operand - of Y is the same as our first operand. Unless we detect - this case, an infinite loop will result. */ - || XEXP (y, 0) == folded_arg0) - break; - - /* Don't associate these operations if they are a PLUS with the - same constant and it is a power of two. These might be doable - with a pre- or post-increment. Similarly for two subtracts of - identical powers of two with post decrement. */ - - if (code == PLUS && INTVAL (const_arg1) == INTVAL (inner_const) - && (0 -#if defined(HAVE_PRE_INCREMENT) || defined(HAVE_POST_INCREMENT) - || exact_log2 (INTVAL (const_arg1)) >= 0 -#endif -#if defined(HAVE_PRE_DECREMENT) || defined(HAVE_POST_DECREMENT) - || exact_log2 (- INTVAL (const_arg1)) >= 0 -#endif - )) - break; - - /* Compute the code used to compose the constants. For example, - A/C1/C2 is A/(C1 * C2), so if CODE == DIV, we want MULT. */ - - associate_code - = (code == MULT || code == DIV || code == UDIV ? MULT - : is_shift || code == PLUS || code == MINUS ? PLUS : code); - - new_const = simplify_binary_operation (associate_code, mode, - const_arg1, inner_const); - - if (new_const == 0) - break; - - /* If we are associating shift operations, don't let this - produce a shift of the size of the object or larger. - This could occur when we follow a sign-extend by a right - shift on a machine that does a sign-extend as a pair - of shifts. */ - - if (is_shift && GET_CODE (new_const) == CONST_INT - && INTVAL (new_const) >= GET_MODE_BITSIZE (mode)) - { - /* As an exception, we can turn an ASHIFTRT of this - form into a shift of the number of bits - 1. */ - if (code == ASHIFTRT) - new_const = GEN_INT (GET_MODE_BITSIZE (mode) - 1); - else - break; - } - - y = copy_rtx (XEXP (y, 0)); - - /* If Y contains our first operand (the most common way this - can happen is if Y is a MEM), we would do into an infinite - loop if we tried to fold it. So don't in that case. */ - - if (! reg_mentioned_p (folded_arg0, y)) - y = fold_rtx (y, insn); - - return cse_gen_binary (code, mode, y, new_const); - } - } - - new = simplify_binary_operation (code, mode, - const_arg0 ? const_arg0 : folded_arg0, - const_arg1 ? const_arg1 : folded_arg1); - break; - - case 'o': - /* (lo_sum (high X) X) is simply X. */ - if (code == LO_SUM && const_arg0 != 0 - && GET_CODE (const_arg0) == HIGH - && rtx_equal_p (XEXP (const_arg0, 0), const_arg1)) - return const_arg1; - break; - - case '3': - case 'b': - new = simplify_ternary_operation (code, mode, mode_arg0, - const_arg0 ? const_arg0 : folded_arg0, - const_arg1 ? const_arg1 : folded_arg1, - const_arg2 ? const_arg2 : XEXP (x, 2)); - break; - } - - return new ? new : x; -} - -/* Return a constant value currently equivalent to X. - Return 0 if we don't know one. */ - -static rtx -equiv_constant (x) - rtx x; -{ - if (GET_CODE (x) == REG - && REGNO_QTY_VALID_P (REGNO (x)) - && qty_const[reg_qty[REGNO (x)]]) - x = gen_lowpart_if_possible (GET_MODE (x), qty_const[reg_qty[REGNO (x)]]); - - if (x != 0 && CONSTANT_P (x)) - return x; - - /* If X is a MEM, try to fold it outside the context of any insn to see if - it might be equivalent to a constant. That handles the case where it - is a constant-pool reference. Then try to look it up in the hash table - in case it is something whose value we have seen before. */ - - if (GET_CODE (x) == MEM) - { - struct table_elt *elt; - - x = fold_rtx (x, NULL_RTX); - if (CONSTANT_P (x)) - return x; - - elt = lookup (x, safe_hash (x, GET_MODE (x)) % NBUCKETS, GET_MODE (x)); - if (elt == 0) - return 0; - - for (elt = elt->first_same_value; elt; elt = elt->next_same_value) - if (elt->is_const && CONSTANT_P (elt->exp)) - return elt->exp; - } - - return 0; -} - -/* Assuming that X is an rtx (e.g., MEM, REG or SUBREG) for a fixed-point - number, return an rtx (MEM, SUBREG, or CONST_INT) that refers to the - least-significant part of X. - MODE specifies how big a part of X to return. - - If the requested operation cannot be done, 0 is returned. - - This is similar to gen_lowpart in emit-rtl.c. */ - -rtx -gen_lowpart_if_possible (mode, x) - enum machine_mode mode; - register rtx x; -{ - rtx result = gen_lowpart_common (mode, x); - - if (result) - return result; - else if (GET_CODE (x) == MEM) - { - /* This is the only other case we handle. */ - register int offset = 0; - rtx new; - -#if WORDS_BIG_ENDIAN - offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD) - - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD)); -#endif -#if BYTES_BIG_ENDIAN - /* Adjust the address so that the address-after-the-data - is unchanged. */ - offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)) - - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x)))); -#endif - new = gen_rtx (MEM, mode, plus_constant (XEXP (x, 0), offset)); - if (! memory_address_p (mode, XEXP (new, 0))) - return 0; - MEM_VOLATILE_P (new) = MEM_VOLATILE_P (x); - RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (x); - MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (x); - return new; - } - else - return 0; -} - -/* Given INSN, a jump insn, TAKEN indicates if we are following the "taken" - branch. It will be zero if not. - - In certain cases, this can cause us to add an equivalence. For example, - if we are following the taken case of - if (i == 2) - we can add the fact that `i' and '2' are now equivalent. - - In any case, we can record that this comparison was passed. If the same - comparison is seen later, we will know its value. */ - -static void -record_jump_equiv (insn, taken) - rtx insn; - int taken; -{ - int cond_known_true; - rtx op0, op1; - enum machine_mode mode, mode0, mode1; - int reversed_nonequality = 0; - enum rtx_code code; - - /* Ensure this is the right kind of insn. */ - if (! condjump_p (insn) || simplejump_p (insn)) - return; - - /* See if this jump condition is known true or false. */ - if (taken) - cond_known_true = (XEXP (SET_SRC (PATTERN (insn)), 2) == pc_rtx); - else - cond_known_true = (XEXP (SET_SRC (PATTERN (insn)), 1) == pc_rtx); - - /* Get the type of comparison being done and the operands being compared. - If we had to reverse a non-equality condition, record that fact so we - know that it isn't valid for floating-point. */ - code = GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 0)); - op0 = fold_rtx (XEXP (XEXP (SET_SRC (PATTERN (insn)), 0), 0), insn); - op1 = fold_rtx (XEXP (XEXP (SET_SRC (PATTERN (insn)), 0), 1), insn); - - code = find_comparison_args (code, &op0, &op1, &mode0, &mode1); - if (! cond_known_true) - { - reversed_nonequality = (code != EQ && code != NE); - code = reverse_condition (code); - } - - /* The mode is the mode of the non-constant. */ - mode = mode0; - if (mode1 != VOIDmode) - mode = mode1; - - record_jump_cond (code, mode, op0, op1, reversed_nonequality); -} - -/* We know that comparison CODE applied to OP0 and OP1 in MODE is true. - REVERSED_NONEQUALITY is nonzero if CODE had to be swapped. - Make any useful entries we can with that information. Called from - above function and called recursively. */ - -static void -record_jump_cond (code, mode, op0, op1, reversed_nonequality) - enum rtx_code code; - enum machine_mode mode; - rtx op0, op1; - int reversed_nonequality; -{ - int op0_hash_code, op1_hash_code; - int op0_in_memory, op0_in_struct, op1_in_memory, op1_in_struct; - struct table_elt *op0_elt, *op1_elt; - - /* If OP0 and OP1 are known equal, and either is a paradoxical SUBREG, - we know that they are also equal in the smaller mode (this is also - true for all smaller modes whether or not there is a SUBREG, but - is not worth testing for with no SUBREG. */ - - /* Note that GET_MODE (op0) may not equal MODE. */ - if (code == EQ && GET_CODE (op0) == SUBREG - && (GET_MODE_SIZE (GET_MODE (op0)) - > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0))))) - { - enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op0)); - rtx tem = gen_lowpart_if_possible (inner_mode, op1); - - record_jump_cond (code, mode, SUBREG_REG (op0), - tem ? tem : gen_rtx (SUBREG, inner_mode, op1, 0), - reversed_nonequality); - } - - if (code == EQ && GET_CODE (op1) == SUBREG - && (GET_MODE_SIZE (GET_MODE (op1)) - > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1))))) - { - enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op1)); - rtx tem = gen_lowpart_if_possible (inner_mode, op0); - - record_jump_cond (code, mode, SUBREG_REG (op1), - tem ? tem : gen_rtx (SUBREG, inner_mode, op0, 0), - reversed_nonequality); - } - - /* Similarly, if this is an NE comparison, and either is a SUBREG - making a smaller mode, we know the whole thing is also NE. */ - - /* Note that GET_MODE (op0) may not equal MODE; - if we test MODE instead, we can get an infinite recursion - alternating between two modes each wider than MODE. */ - - if (code == NE && GET_CODE (op0) == SUBREG - && subreg_lowpart_p (op0) - && (GET_MODE_SIZE (GET_MODE (op0)) - < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0))))) - { - enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op0)); - rtx tem = gen_lowpart_if_possible (inner_mode, op1); - - record_jump_cond (code, mode, SUBREG_REG (op0), - tem ? tem : gen_rtx (SUBREG, inner_mode, op1, 0), - reversed_nonequality); - } - - if (code == NE && GET_CODE (op1) == SUBREG - && subreg_lowpart_p (op1) - && (GET_MODE_SIZE (GET_MODE (op1)) - < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1))))) - { - enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op1)); - rtx tem = gen_lowpart_if_possible (inner_mode, op0); - - record_jump_cond (code, mode, SUBREG_REG (op1), - tem ? tem : gen_rtx (SUBREG, inner_mode, op0, 0), - reversed_nonequality); - } - - /* Hash both operands. */ - - do_not_record = 0; - hash_arg_in_memory = 0; - hash_arg_in_struct = 0; - op0_hash_code = HASH (op0, mode); - op0_in_memory = hash_arg_in_memory; - op0_in_struct = hash_arg_in_struct; - - if (do_not_record) - return; - - do_not_record = 0; - hash_arg_in_memory = 0; - hash_arg_in_struct = 0; - op1_hash_code = HASH (op1, mode); - op1_in_memory = hash_arg_in_memory; - op1_in_struct = hash_arg_in_struct; - - if (do_not_record) - return; - - /* Look up both operands. */ - op0_elt = lookup (op0, op0_hash_code, mode); - op1_elt = lookup (op1, op1_hash_code, mode); - - /* If we aren't setting two things equal all we can do is save this - comparison. Similarly if this is floating-point. In the latter - case, OP1 might be zero and both -0.0 and 0.0 are equal to it. - If we record the equality, we might inadvertently delete code - whose intent was to change -0 to +0. */ - - if (code != EQ || GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT) - { - /* If we reversed a floating-point comparison, if OP0 is not a - register, or if OP1 is neither a register or constant, we can't - do anything. */ - - if (GET_CODE (op1) != REG) - op1 = equiv_constant (op1); - - if ((reversed_nonequality && GET_MODE_CLASS (mode) != MODE_INT) - || GET_CODE (op0) != REG || op1 == 0) - return; - - /* Put OP0 in the hash table if it isn't already. This gives it a - new quantity number. */ - if (op0_elt == 0) - { - if (insert_regs (op0, NULL_PTR, 0)) - { - rehash_using_reg (op0); - op0_hash_code = HASH (op0, mode); - } - - op0_elt = insert (op0, NULL_PTR, op0_hash_code, mode); - op0_elt->in_memory = op0_in_memory; - op0_elt->in_struct = op0_in_struct; - } - - qty_comparison_code[reg_qty[REGNO (op0)]] = code; - if (GET_CODE (op1) == REG) - { - /* Put OP1 in the hash table so it gets a new quantity number. */ - if (op1_elt == 0) - { - if (insert_regs (op1, NULL_PTR, 0)) - { - rehash_using_reg (op1); - op1_hash_code = HASH (op1, mode); - } - - op1_elt = insert (op1, NULL_PTR, op1_hash_code, mode); - op1_elt->in_memory = op1_in_memory; - op1_elt->in_struct = op1_in_struct; - } - - qty_comparison_qty[reg_qty[REGNO (op0)]] = reg_qty[REGNO (op1)]; - qty_comparison_const[reg_qty[REGNO (op0)]] = 0; - } - else - { - qty_comparison_qty[reg_qty[REGNO (op0)]] = -1; - qty_comparison_const[reg_qty[REGNO (op0)]] = op1; - } - - return; - } - - /* If both are equivalent, merge the two classes. Save this class for - `cse_set_around_loop'. */ - if (op0_elt && op1_elt) - { - merge_equiv_classes (op0_elt, op1_elt); - last_jump_equiv_class = op0_elt; - } - - /* For whichever side doesn't have an equivalence, make one. */ - if (op0_elt == 0) - { - if (insert_regs (op0, op1_elt, 0)) - { - rehash_using_reg (op0); - op0_hash_code = HASH (op0, mode); - } - - op0_elt = insert (op0, op1_elt, op0_hash_code, mode); - op0_elt->in_memory = op0_in_memory; - op0_elt->in_struct = op0_in_struct; - last_jump_equiv_class = op0_elt; - } - - if (op1_elt == 0) - { - if (insert_regs (op1, op0_elt, 0)) - { - rehash_using_reg (op1); - op1_hash_code = HASH (op1, mode); - } - - op1_elt = insert (op1, op0_elt, op1_hash_code, mode); - op1_elt->in_memory = op1_in_memory; - op1_elt->in_struct = op1_in_struct; - last_jump_equiv_class = op1_elt; - } -} - -/* CSE processing for one instruction. - First simplify sources and addresses of all assignments - in the instruction, using previously-computed equivalents values. - Then install the new sources and destinations in the table - of available values. - - If IN_LIBCALL_BLOCK is nonzero, don't record any equivalence made in - the insn. */ - -/* Data on one SET contained in the instruction. */ - -struct set -{ - /* The SET rtx itself. */ - rtx rtl; - /* The SET_SRC of the rtx (the original value, if it is changing). */ - rtx src; - /* The hash-table element for the SET_SRC of the SET. */ - struct table_elt *src_elt; - /* Hash code for the SET_SRC. */ - int src_hash_code; - /* Hash code for the SET_DEST. */ - int dest_hash_code; - /* The SET_DEST, with SUBREG, etc., stripped. */ - rtx inner_dest; - /* Place where the pointer to the INNER_DEST was found. */ - rtx *inner_dest_loc; - /* Nonzero if the SET_SRC is in memory. */ - char src_in_memory; - /* Nonzero if the SET_SRC is in a structure. */ - char src_in_struct; - /* Nonzero if the SET_SRC contains something - whose value cannot be predicted and understood. */ - char src_volatile; - /* Original machine mode, in case it becomes a CONST_INT. */ - enum machine_mode mode; - /* A constant equivalent for SET_SRC, if any. */ - rtx src_const; - /* Hash code of constant equivalent for SET_SRC. */ - int src_const_hash_code; - /* Table entry for constant equivalent for SET_SRC, if any. */ - struct table_elt *src_const_elt; -}; - -static void -cse_insn (insn, in_libcall_block) - rtx insn; - int in_libcall_block; -{ - register rtx x = PATTERN (insn); - rtx tem; - register int i; - register int n_sets = 0; - - /* Records what this insn does to set CC0. */ - rtx this_insn_cc0 = 0; - enum machine_mode this_insn_cc0_mode; - struct write_data writes_memory; - static struct write_data init = {0, 0, 0, 0}; - - rtx src_eqv = 0; - struct table_elt *src_eqv_elt = 0; - int src_eqv_volatile; - int src_eqv_in_memory; - int src_eqv_in_struct; - int src_eqv_hash_code; - - struct set *sets; - - this_insn = insn; - writes_memory = init; - - /* Find all the SETs and CLOBBERs in this instruction. - Record all the SETs in the array `set' and count them. - Also determine whether there is a CLOBBER that invalidates - all memory references, or all references at varying addresses. */ - - if (GET_CODE (x) == SET) - { - sets = (struct set *) alloca (sizeof (struct set)); - sets[0].rtl = x; - - /* Ignore SETs that are unconditional jumps. - They never need cse processing, so this does not hurt. - The reason is not efficiency but rather - so that we can test at the end for instructions - that have been simplified to unconditional jumps - and not be misled by unchanged instructions - that were unconditional jumps to begin with. */ - if (SET_DEST (x) == pc_rtx - && GET_CODE (SET_SRC (x)) == LABEL_REF) - ; - - /* Don't count call-insns, (set (reg 0) (call ...)), as a set. - The hard function value register is used only once, to copy to - someplace else, so it isn't worth cse'ing (and on 80386 is unsafe)! - Ensure we invalidate the destination register. On the 80386 no - other code would invalidate it since it is a fixed_reg. - We need not check the return of apply_change_group; see canon_reg. */ - - else if (GET_CODE (SET_SRC (x)) == CALL) - { - canon_reg (SET_SRC (x), insn); - apply_change_group (); - fold_rtx (SET_SRC (x), insn); - invalidate (SET_DEST (x)); - } - else - n_sets = 1; - } - else if (GET_CODE (x) == PARALLEL) - { - register int lim = XVECLEN (x, 0); - - sets = (struct set *) alloca (lim * sizeof (struct set)); - - /* Find all regs explicitly clobbered in this insn, - and ensure they are not replaced with any other regs - elsewhere in this insn. - When a reg that is clobbered is also used for input, - we should presume that that is for a reason, - and we should not substitute some other register - which is not supposed to be clobbered. - Therefore, this loop cannot be merged into the one below - because a CALL may precede a CLOBBER and refer to the - value clobbered. We must not let a canonicalization do - anything in that case. */ - for (i = 0; i < lim; i++) - { - register rtx y = XVECEXP (x, 0, i); - if (GET_CODE (y) == CLOBBER - && (GET_CODE (XEXP (y, 0)) == REG - || GET_CODE (XEXP (y, 0)) == SUBREG)) - invalidate (XEXP (y, 0)); - } - - for (i = 0; i < lim; i++) - { - register rtx y = XVECEXP (x, 0, i); - if (GET_CODE (y) == SET) - { - /* As above, we ignore unconditional jumps and call-insns and - ignore the result of apply_change_group. */ - if (GET_CODE (SET_SRC (y)) == CALL) - { - canon_reg (SET_SRC (y), insn); - apply_change_group (); - fold_rtx (SET_SRC (y), insn); - invalidate (SET_DEST (y)); - } - else if (SET_DEST (y) == pc_rtx - && GET_CODE (SET_SRC (y)) == LABEL_REF) - ; - else - sets[n_sets++].rtl = y; - } - else if (GET_CODE (y) == CLOBBER) - { - /* If we clobber memory, take note of that, - and canon the address. - This does nothing when a register is clobbered - because we have already invalidated the reg. */ - if (GET_CODE (XEXP (y, 0)) == MEM) - { - canon_reg (XEXP (y, 0), NULL_RTX); - note_mem_written (XEXP (y, 0), &writes_memory); - } - } - else if (GET_CODE (y) == USE - && ! (GET_CODE (XEXP (y, 0)) == REG - && REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER)) - canon_reg (y, NULL_RTX); - else if (GET_CODE (y) == CALL) - { - /* The result of apply_change_group can be ignored; see - canon_reg. */ - canon_reg (y, insn); - apply_change_group (); - fold_rtx (y, insn); - } - } - } - else if (GET_CODE (x) == CLOBBER) - { - if (GET_CODE (XEXP (x, 0)) == MEM) - { - canon_reg (XEXP (x, 0), NULL_RTX); - note_mem_written (XEXP (x, 0), &writes_memory); - } - } - - /* Canonicalize a USE of a pseudo register or memory location. */ - else if (GET_CODE (x) == USE - && ! (GET_CODE (XEXP (x, 0)) == REG - && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER)) - canon_reg (XEXP (x, 0), NULL_RTX); - else if (GET_CODE (x) == CALL) - { - /* The result of apply_change_group can be ignored; see canon_reg. */ - canon_reg (x, insn); - apply_change_group (); - fold_rtx (x, insn); - } - - if (n_sets == 1 && REG_NOTES (insn) != 0) - { - /* Store the equivalent value in SRC_EQV, if different. */ - rtx tem = find_reg_note (insn, REG_EQUAL, NULL_RTX); - - if (tem && ! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))) - src_eqv = canon_reg (XEXP (tem, 0), NULL_RTX); - } - - /* Canonicalize sources and addresses of destinations. - We do this in a separate pass to avoid problems when a MATCH_DUP is - present in the insn pattern. In that case, we want to ensure that - we don't break the duplicate nature of the pattern. So we will replace - both operands at the same time. Otherwise, we would fail to find an - equivalent substitution in the loop calling validate_change below. - - We used to suppress canonicalization of DEST if it appears in SRC, - but we don't do this any more. */ - - for (i = 0; i < n_sets; i++) - { - rtx dest = SET_DEST (sets[i].rtl); - rtx src = SET_SRC (sets[i].rtl); - rtx new = canon_reg (src, insn); - - if ((GET_CODE (new) == REG && GET_CODE (src) == REG - && ((REGNO (new) < FIRST_PSEUDO_REGISTER) - != (REGNO (src) < FIRST_PSEUDO_REGISTER))) - || insn_n_dups[recog_memoized (insn)] > 0) - validate_change (insn, &SET_SRC (sets[i].rtl), new, 1); - else - SET_SRC (sets[i].rtl) = new; - - if (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT) - { - validate_change (insn, &XEXP (dest, 1), - canon_reg (XEXP (dest, 1), insn), 1); - validate_change (insn, &XEXP (dest, 2), - canon_reg (XEXP (dest, 2), insn), 1); - } - - while (GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART - || GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SIGN_EXTRACT) - dest = XEXP (dest, 0); - - if (GET_CODE (dest) == MEM) - canon_reg (dest, insn); - } - - /* Now that we have done all the replacements, we can apply the change - group and see if they all work. Note that this will cause some - canonicalizations that would have worked individually not to be applied - because some other canonicalization didn't work, but this should not - occur often. - - The result of apply_change_group can be ignored; see canon_reg. */ - - apply_change_group (); - - /* Set sets[i].src_elt to the class each source belongs to. - Detect assignments from or to volatile things - and set set[i] to zero so they will be ignored - in the rest of this function. - - Nothing in this loop changes the hash table or the register chains. */ - - for (i = 0; i < n_sets; i++) - { - register rtx src, dest; - register rtx src_folded; - register struct table_elt *elt = 0, *p; - enum machine_mode mode; - rtx src_eqv_here; - rtx src_const = 0; - rtx src_related = 0; - struct table_elt *src_const_elt = 0; - int src_cost = 10000, src_eqv_cost = 10000, src_folded_cost = 10000; - int src_related_cost = 10000, src_elt_cost = 10000; - /* Set non-zero if we need to call force_const_mem on with the - contents of src_folded before using it. */ - int src_folded_force_flag = 0; - - dest = SET_DEST (sets[i].rtl); - src = SET_SRC (sets[i].rtl); - - /* If SRC is a constant that has no machine mode, - hash it with the destination's machine mode. - This way we can keep different modes separate. */ - - mode = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src); - sets[i].mode = mode; - - if (src_eqv) - { - enum machine_mode eqvmode = mode; - if (GET_CODE (dest) == STRICT_LOW_PART) - eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0))); - do_not_record = 0; - hash_arg_in_memory = 0; - hash_arg_in_struct = 0; - src_eqv = fold_rtx (src_eqv, insn); - src_eqv_hash_code = HASH (src_eqv, eqvmode); - - /* Find the equivalence class for the equivalent expression. */ - - if (!do_not_record) - src_eqv_elt = lookup (src_eqv, src_eqv_hash_code, eqvmode); - - src_eqv_volatile = do_not_record; - src_eqv_in_memory = hash_arg_in_memory; - src_eqv_in_struct = hash_arg_in_struct; - } - - /* If this is a STRICT_LOW_PART assignment, src_eqv corresponds to the - value of the INNER register, not the destination. So it is not - a legal substitution for the source. But save it for later. */ - if (GET_CODE (dest) == STRICT_LOW_PART) - src_eqv_here = 0; - else - src_eqv_here = src_eqv; - - /* Simplify and foldable subexpressions in SRC. Then get the fully- - simplified result, which may not necessarily be valid. */ - src_folded = fold_rtx (src, insn); - - /* If storing a constant in a bitfield, pre-truncate the constant - so we will be able to record it later. */ - if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT - || GET_CODE (SET_DEST (sets[i].rtl)) == SIGN_EXTRACT) - { - rtx width = XEXP (SET_DEST (sets[i].rtl), 1); - - if (GET_CODE (src) == CONST_INT - && GET_CODE (width) == CONST_INT - && INTVAL (width) < HOST_BITS_PER_WIDE_INT - && (INTVAL (src) & ((HOST_WIDE_INT) (-1) << INTVAL (width)))) - src_folded - = GEN_INT (INTVAL (src) & (((HOST_WIDE_INT) 1 - << INTVAL (width)) - 1)); - } - - /* Compute SRC's hash code, and also notice if it - should not be recorded at all. In that case, - prevent any further processing of this assignment. */ - do_not_record = 0; - hash_arg_in_memory = 0; - hash_arg_in_struct = 0; - - sets[i].src = src; - sets[i].src_hash_code = HASH (src, mode); - sets[i].src_volatile = do_not_record; - sets[i].src_in_memory = hash_arg_in_memory; - sets[i].src_in_struct = hash_arg_in_struct; - -#if 0 - /* It is no longer clear why we used to do this, but it doesn't - appear to still be needed. So let's try without it since this - code hurts cse'ing widened ops. */ - /* If source is a perverse subreg (such as QI treated as an SI), - treat it as volatile. It may do the work of an SI in one context - where the extra bits are not being used, but cannot replace an SI - in general. */ - if (GET_CODE (src) == SUBREG - && (GET_MODE_SIZE (GET_MODE (src)) - > GET_MODE_SIZE (GET_MODE (SUBREG_REG (src))))) - sets[i].src_volatile = 1; -#endif - - /* Locate all possible equivalent forms for SRC. Try to replace - SRC in the insn with each cheaper equivalent. - - We have the following types of equivalents: SRC itself, a folded - version, a value given in a REG_EQUAL note, or a value related - to a constant. - - Each of these equivalents may be part of an additional class - of equivalents (if more than one is in the table, they must be in - the same class; we check for this). - - If the source is volatile, we don't do any table lookups. - - We note any constant equivalent for possible later use in a - REG_NOTE. */ - - if (!sets[i].src_volatile) - elt = lookup (src, sets[i].src_hash_code, mode); - - sets[i].src_elt = elt; - - if (elt && src_eqv_here && src_eqv_elt) - { - if (elt->first_same_value != src_eqv_elt->first_same_value) - { - /* The REG_EQUAL is indicating that two formerly distinct - classes are now equivalent. So merge them. */ - merge_equiv_classes (elt, src_eqv_elt); - src_eqv_hash_code = HASH (src_eqv, elt->mode); - src_eqv_elt = lookup (src_eqv, src_eqv_hash_code, elt->mode); - } - - src_eqv_here = 0; - } - - else if (src_eqv_elt) - elt = src_eqv_elt; - - /* Try to find a constant somewhere and record it in `src_const'. - Record its table element, if any, in `src_const_elt'. Look in - any known equivalences first. (If the constant is not in the - table, also set `sets[i].src_const_hash_code'). */ - if (elt) - for (p = elt->first_same_value; p; p = p->next_same_value) - if (p->is_const) - { - src_const = p->exp; - src_const_elt = elt; - break; - } - - if (src_const == 0 - && (CONSTANT_P (src_folded) - /* Consider (minus (label_ref L1) (label_ref L2)) as - "constant" here so we will record it. This allows us - to fold switch statements when an ADDR_DIFF_VEC is used. */ - || (GET_CODE (src_folded) == MINUS - && GET_CODE (XEXP (src_folded, 0)) == LABEL_REF - && GET_CODE (XEXP (src_folded, 1)) == LABEL_REF))) - src_const = src_folded, src_const_elt = elt; - else if (src_const == 0 && src_eqv_here && CONSTANT_P (src_eqv_here)) - src_const = src_eqv_here, src_const_elt = src_eqv_elt; - - /* If we don't know if the constant is in the table, get its - hash code and look it up. */ - if (src_const && src_const_elt == 0) - { - sets[i].src_const_hash_code = HASH (src_const, mode); - src_const_elt = lookup (src_const, sets[i].src_const_hash_code, - mode); - } - - sets[i].src_const = src_const; - sets[i].src_const_elt = src_const_elt; - - /* If the constant and our source are both in the table, mark them as - equivalent. Otherwise, if a constant is in the table but the source - isn't, set ELT to it. */ - if (src_const_elt && elt - && src_const_elt->first_same_value != elt->first_same_value) - merge_equiv_classes (elt, src_const_elt); - else if (src_const_elt && elt == 0) - elt = src_const_elt; - - /* See if there is a register linearly related to a constant - equivalent of SRC. */ - if (src_const - && (GET_CODE (src_const) == CONST - || (src_const_elt && src_const_elt->related_value != 0))) - { - src_related = use_related_value (src_const, src_const_elt); - if (src_related) - { - struct table_elt *src_related_elt - = lookup (src_related, HASH (src_related, mode), mode); - if (src_related_elt && elt) - { - if (elt->first_same_value - != src_related_elt->first_same_value) - /* This can occur when we previously saw a CONST - involving a SYMBOL_REF and then see the SYMBOL_REF - twice. Merge the involved classes. */ - merge_equiv_classes (elt, src_related_elt); - - src_related = 0; - src_related_elt = 0; - } - else if (src_related_elt && elt == 0) - elt = src_related_elt; - } - } - - /* See if we have a CONST_INT that is already in a register in a - wider mode. */ - - if (src_const && src_related == 0 && GET_CODE (src_const) == CONST_INT - && GET_MODE_CLASS (mode) == MODE_INT - && GET_MODE_BITSIZE (mode) < BITS_PER_WORD) - { - enum machine_mode wider_mode; - - for (wider_mode = GET_MODE_WIDER_MODE (mode); - GET_MODE_BITSIZE (wider_mode) <= BITS_PER_WORD - && src_related == 0; - wider_mode = GET_MODE_WIDER_MODE (wider_mode)) - { - struct table_elt *const_elt - = lookup (src_const, HASH (src_const, wider_mode), wider_mode); - - if (const_elt == 0) - continue; - - for (const_elt = const_elt->first_same_value; - const_elt; const_elt = const_elt->next_same_value) - if (GET_CODE (const_elt->exp) == REG) - { - src_related = gen_lowpart_if_possible (mode, - const_elt->exp); - break; - } - } - } - - /* Another possibility is that we have an AND with a constant in - a mode narrower than a word. If so, it might have been generated - as part of an "if" which would narrow the AND. If we already - have done the AND in a wider mode, we can use a SUBREG of that - value. */ - - if (flag_expensive_optimizations && ! src_related - && GET_CODE (src) == AND && GET_CODE (XEXP (src, 1)) == CONST_INT - && GET_MODE_SIZE (mode) < UNITS_PER_WORD) - { - enum machine_mode tmode; - rtx new_and = gen_rtx (AND, VOIDmode, NULL_RTX, XEXP (src, 1)); - - for (tmode = GET_MODE_WIDER_MODE (mode); - GET_MODE_SIZE (tmode) <= UNITS_PER_WORD; - tmode = GET_MODE_WIDER_MODE (tmode)) - { - rtx inner = gen_lowpart_if_possible (tmode, XEXP (src, 0)); - struct table_elt *larger_elt; - - if (inner) - { - PUT_MODE (new_and, tmode); - XEXP (new_and, 0) = inner; - larger_elt = lookup (new_and, HASH (new_and, tmode), tmode); - if (larger_elt == 0) - continue; - - for (larger_elt = larger_elt->first_same_value; - larger_elt; larger_elt = larger_elt->next_same_value) - if (GET_CODE (larger_elt->exp) == REG) - { - src_related - = gen_lowpart_if_possible (mode, larger_elt->exp); - break; - } - - if (src_related) - break; - } - } - } - - if (src == src_folded) - src_folded = 0; - - /* At this point, ELT, if non-zero, points to a class of expressions - equivalent to the source of this SET and SRC, SRC_EQV, SRC_FOLDED, - and SRC_RELATED, if non-zero, each contain additional equivalent - expressions. Prune these latter expressions by deleting expressions - already in the equivalence class. - - Check for an equivalent identical to the destination. If found, - this is the preferred equivalent since it will likely lead to - elimination of the insn. Indicate this by placing it in - `src_related'. */ - - if (elt) elt = elt->first_same_value; - for (p = elt; p; p = p->next_same_value) - { - enum rtx_code code = GET_CODE (p->exp); - - /* If the expression is not valid, ignore it. Then we do not - have to check for validity below. In most cases, we can use - `rtx_equal_p', since canonicalization has already been done. */ - if (code != REG && ! exp_equiv_p (p->exp, p->exp, 1, 0)) - continue; - - if (src && GET_CODE (src) == code && rtx_equal_p (src, p->exp)) - src = 0; - else if (src_folded && GET_CODE (src_folded) == code - && rtx_equal_p (src_folded, p->exp)) - src_folded = 0; - else if (src_eqv_here && GET_CODE (src_eqv_here) == code - && rtx_equal_p (src_eqv_here, p->exp)) - src_eqv_here = 0; - else if (src_related && GET_CODE (src_related) == code - && rtx_equal_p (src_related, p->exp)) - src_related = 0; - - /* This is the same as the destination of the insns, we want - to prefer it. Copy it to src_related. The code below will - then give it a negative cost. */ - if (GET_CODE (dest) == code && rtx_equal_p (p->exp, dest)) - src_related = dest; - - } - - /* Find the cheapest valid equivalent, trying all the available - possibilities. Prefer items not in the hash table to ones - that are when they are equal cost. Note that we can never - worsen an insn as the current contents will also succeed. - If we find an equivalent identical to the destination, use it as best, - since this insn will probably be eliminated in that case. */ - if (src) - { - if (rtx_equal_p (src, dest)) - src_cost = -1; - else - src_cost = COST (src); - } - - if (src_eqv_here) - { - if (rtx_equal_p (src_eqv_here, dest)) - src_eqv_cost = -1; - else - src_eqv_cost = COST (src_eqv_here); - } - - if (src_folded) - { - if (rtx_equal_p (src_folded, dest)) - src_folded_cost = -1; - else - src_folded_cost = COST (src_folded); - } - - if (src_related) - { - if (rtx_equal_p (src_related, dest)) - src_related_cost = -1; - else - src_related_cost = COST (src_related); - } - - /* If this was an indirect jump insn, a known label will really be - cheaper even though it looks more expensive. */ - if (dest == pc_rtx && src_const && GET_CODE (src_const) == LABEL_REF) - src_folded = src_const, src_folded_cost = -1; - - /* Terminate loop when replacement made. This must terminate since - the current contents will be tested and will always be valid. */ - while (1) - { - rtx trial; - - /* Skip invalid entries. */ - while (elt && GET_CODE (elt->exp) != REG - && ! exp_equiv_p (elt->exp, elt->exp, 1, 0)) - elt = elt->next_same_value; - - if (elt) src_elt_cost = elt->cost; - - /* Find cheapest and skip it for the next time. For items - of equal cost, use this order: - src_folded, src, src_eqv, src_related and hash table entry. */ - if (src_folded_cost <= src_cost - && src_folded_cost <= src_eqv_cost - && src_folded_cost <= src_related_cost - && src_folded_cost <= src_elt_cost) - { - trial = src_folded, src_folded_cost = 10000; - if (src_folded_force_flag) - trial = force_const_mem (mode, trial); - } - else if (src_cost <= src_eqv_cost - && src_cost <= src_related_cost - && src_cost <= src_elt_cost) - trial = src, src_cost = 10000; - else if (src_eqv_cost <= src_related_cost - && src_eqv_cost <= src_elt_cost) - trial = src_eqv_here, src_eqv_cost = 10000; - else if (src_related_cost <= src_elt_cost) - trial = src_related, src_related_cost = 10000; - else - { - trial = copy_rtx (elt->exp); - elt = elt->next_same_value; - src_elt_cost = 10000; - } - - /* We don't normally have an insn matching (set (pc) (pc)), so - check for this separately here. We will delete such an - insn below. - - Tablejump insns contain a USE of the table, so simply replacing - the operand with the constant won't match. This is simply an - unconditional branch, however, and is therefore valid. Just - insert the substitution here and we will delete and re-emit - the insn later. */ - - if (n_sets == 1 && dest == pc_rtx - && (trial == pc_rtx - || (GET_CODE (trial) == LABEL_REF - && ! condjump_p (insn)))) - { - /* If TRIAL is a label in front of a jump table, we are - really falling through the switch (this is how casesi - insns work), so we must branch around the table. */ - if (GET_CODE (trial) == CODE_LABEL - && NEXT_INSN (trial) != 0 - && GET_CODE (NEXT_INSN (trial)) == JUMP_INSN - && (GET_CODE (PATTERN (NEXT_INSN (trial))) == ADDR_DIFF_VEC - || GET_CODE (PATTERN (NEXT_INSN (trial))) == ADDR_VEC)) - - trial = gen_rtx (LABEL_REF, Pmode, get_label_after (trial)); - - SET_SRC (sets[i].rtl) = trial; - break; - } - - /* Look for a substitution that makes a valid insn. */ - else if (validate_change (insn, &SET_SRC (sets[i].rtl), trial, 0)) - { - /* The result of apply_change_group can be ignored; see - canon_reg. */ - - validate_change (insn, &SET_SRC (sets[i].rtl), - canon_reg (SET_SRC (sets[i].rtl), insn), - 1); - apply_change_group (); - break; - } - - /* If we previously found constant pool entries for - constants and this is a constant, try making a - pool entry. Put it in src_folded unless we already have done - this since that is where it likely came from. */ - - else if (constant_pool_entries_cost - && CONSTANT_P (trial) - && (src_folded == 0 || GET_CODE (src_folded) != MEM) - && GET_MODE_CLASS (mode) != MODE_CC) - { - src_folded_force_flag = 1; - src_folded = trial; - src_folded_cost = constant_pool_entries_cost; - } - } - - src = SET_SRC (sets[i].rtl); - - /* In general, it is good to have a SET with SET_SRC == SET_DEST. - However, there is an important exception: If both are registers - that are not the head of their equivalence class, replace SET_SRC - with the head of the class. If we do not do this, we will have - both registers live over a portion of the basic block. This way, - their lifetimes will likely abut instead of overlapping. */ - if (GET_CODE (dest) == REG - && REGNO_QTY_VALID_P (REGNO (dest)) - && qty_mode[reg_qty[REGNO (dest)]] == GET_MODE (dest) - && qty_first_reg[reg_qty[REGNO (dest)]] != REGNO (dest) - && GET_CODE (src) == REG && REGNO (src) == REGNO (dest) - /* Don't do this if the original insn had a hard reg as - SET_SRC. */ - && (GET_CODE (sets[i].src) != REG - || REGNO (sets[i].src) >= FIRST_PSEUDO_REGISTER)) - /* We can't call canon_reg here because it won't do anything if - SRC is a hard register. */ - { - int first = qty_first_reg[reg_qty[REGNO (src)]]; - - src = SET_SRC (sets[i].rtl) - = first >= FIRST_PSEUDO_REGISTER ? regno_reg_rtx[first] - : gen_rtx (REG, GET_MODE (src), first); - - /* If we had a constant that is cheaper than what we are now - setting SRC to, use that constant. We ignored it when we - thought we could make this into a no-op. */ - if (src_const && COST (src_const) < COST (src) - && validate_change (insn, &SET_SRC (sets[i].rtl), src_const, 0)) - src = src_const; - } - - /* If we made a change, recompute SRC values. */ - if (src != sets[i].src) - { - do_not_record = 0; - hash_arg_in_memory = 0; - hash_arg_in_struct = 0; - sets[i].src = src; - sets[i].src_hash_code = HASH (src, mode); - sets[i].src_volatile = do_not_record; - sets[i].src_in_memory = hash_arg_in_memory; - sets[i].src_in_struct = hash_arg_in_struct; - sets[i].src_elt = lookup (src, sets[i].src_hash_code, mode); - } - - /* If this is a single SET, we are setting a register, and we have an - equivalent constant, we want to add a REG_NOTE. We don't want - to write a REG_EQUAL note for a constant pseudo since verifying that - that pseudo hasn't been eliminated is a pain. Such a note also - won't help anything. */ - if (n_sets == 1 && src_const && GET_CODE (dest) == REG - && GET_CODE (src_const) != REG) - { - rtx tem = find_reg_note (insn, REG_EQUAL, NULL_RTX); - - /* Record the actual constant value in a REG_EQUAL note, making - a new one if one does not already exist. */ - if (tem) - XEXP (tem, 0) = src_const; - else - REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL, - src_const, REG_NOTES (insn)); - - /* If storing a constant value in a register that - previously held the constant value 0, - record this fact with a REG_WAS_0 note on this insn. - - Note that the *register* is required to have previously held 0, - not just any register in the quantity and we must point to the - insn that set that register to zero. - - Rather than track each register individually, we just see if - the last set for this quantity was for this register. */ - - if (REGNO_QTY_VALID_P (REGNO (dest)) - && qty_const[reg_qty[REGNO (dest)]] == const0_rtx) - { - /* See if we previously had a REG_WAS_0 note. */ - rtx note = find_reg_note (insn, REG_WAS_0, NULL_RTX); - rtx const_insn = qty_const_insn[reg_qty[REGNO (dest)]]; - - if ((tem = single_set (const_insn)) != 0 - && rtx_equal_p (SET_DEST (tem), dest)) - { - if (note) - XEXP (note, 0) = const_insn; - else - REG_NOTES (insn) = gen_rtx (INSN_LIST, REG_WAS_0, - const_insn, REG_NOTES (insn)); - } - } - } - - /* Now deal with the destination. */ - do_not_record = 0; - sets[i].inner_dest_loc = &SET_DEST (sets[0].rtl); - - /* Look within any SIGN_EXTRACT or ZERO_EXTRACT - to the MEM or REG within it. */ - while (GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SUBREG - || GET_CODE (dest) == STRICT_LOW_PART) - { - sets[i].inner_dest_loc = &XEXP (dest, 0); - dest = XEXP (dest, 0); - } - - sets[i].inner_dest = dest; - - if (GET_CODE (dest) == MEM) - { - dest = fold_rtx (dest, insn); - - /* Decide whether we invalidate everything in memory, - or just things at non-fixed places. - Writing a large aggregate must invalidate everything - because we don't know how long it is. */ - note_mem_written (dest, &writes_memory); - } - - /* Compute the hash code of the destination now, - before the effects of this instruction are recorded, - since the register values used in the address computation - are those before this instruction. */ - sets[i].dest_hash_code = HASH (dest, mode); - - /* Don't enter a bit-field in the hash table - because the value in it after the store - may not equal what was stored, due to truncation. */ - - if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT - || GET_CODE (SET_DEST (sets[i].rtl)) == SIGN_EXTRACT) - { - rtx width = XEXP (SET_DEST (sets[i].rtl), 1); - - if (src_const != 0 && GET_CODE (src_const) == CONST_INT - && GET_CODE (width) == CONST_INT - && INTVAL (width) < HOST_BITS_PER_WIDE_INT - && ! (INTVAL (src_const) - & ((HOST_WIDE_INT) (-1) << INTVAL (width)))) - /* Exception: if the value is constant, - and it won't be truncated, record it. */ - ; - else - { - /* This is chosen so that the destination will be invalidated - but no new value will be recorded. - We must invalidate because sometimes constant - values can be recorded for bitfields. */ - sets[i].src_elt = 0; - sets[i].src_volatile = 1; - src_eqv = 0; - src_eqv_elt = 0; - } - } - - /* If only one set in a JUMP_INSN and it is now a no-op, we can delete - the insn. */ - else if (n_sets == 1 && dest == pc_rtx && src == pc_rtx) - { - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - cse_jumps_altered = 1; - /* One less use of the label this insn used to jump to. */ - --LABEL_NUSES (JUMP_LABEL (insn)); - /* No more processing for this set. */ - sets[i].rtl = 0; - } - - /* If this SET is now setting PC to a label, we know it used to - be a conditional or computed branch. So we see if we can follow - it. If it was a computed branch, delete it and re-emit. */ - else if (dest == pc_rtx && GET_CODE (src) == LABEL_REF) - { - rtx p; - - /* If this is not in the format for a simple branch and - we are the only SET in it, re-emit it. */ - if (! simplejump_p (insn) && n_sets == 1) - { - rtx new = emit_jump_insn_before (gen_jump (XEXP (src, 0)), insn); - JUMP_LABEL (new) = XEXP (src, 0); - LABEL_NUSES (XEXP (src, 0))++; - delete_insn (insn); - insn = new; - } - else - /* Otherwise, force rerecognition, since it probably had - a different pattern before. - This shouldn't really be necessary, since whatever - changed the source value above should have done this. - Until the right place is found, might as well do this here. */ - INSN_CODE (insn) = -1; - - /* Now that we've converted this jump to an unconditional jump, - there is dead code after it. Delete the dead code until we - reach a BARRIER, the end of the function, or a label. Do - not delete NOTEs except for NOTE_INSN_DELETED since later - phases assume these notes are retained. */ - - p = insn; - - while (NEXT_INSN (p) != 0 - && GET_CODE (NEXT_INSN (p)) != BARRIER - && GET_CODE (NEXT_INSN (p)) != CODE_LABEL) - { - if (GET_CODE (NEXT_INSN (p)) != NOTE - || NOTE_LINE_NUMBER (NEXT_INSN (p)) == NOTE_INSN_DELETED) - delete_insn (NEXT_INSN (p)); - else - p = NEXT_INSN (p); - } - - /* If we don't have a BARRIER immediately after INSN, put one there. - Much code assumes that there are no NOTEs between a JUMP_INSN and - BARRIER. */ - - if (NEXT_INSN (insn) == 0 - || GET_CODE (NEXT_INSN (insn)) != BARRIER) - emit_barrier_after (insn); - - /* We might have two BARRIERs separated by notes. Delete the second - one if so. */ - - if (p != insn && NEXT_INSN (p) != 0 - && GET_CODE (NEXT_INSN (p)) == BARRIER) - delete_insn (NEXT_INSN (p)); - - cse_jumps_altered = 1; - sets[i].rtl = 0; - } - - /* If destination is volatile, invalidate it and then do no further - processing for this assignment. */ - - else if (do_not_record) - { - if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG - || GET_CODE (dest) == MEM) - invalidate (dest); - sets[i].rtl = 0; - } - - if (sets[i].rtl != 0 && dest != SET_DEST (sets[i].rtl)) - sets[i].dest_hash_code = HASH (SET_DEST (sets[i].rtl), mode); - -#ifdef HAVE_cc0 - /* If setting CC0, record what it was set to, or a constant, if it - is equivalent to a constant. If it is being set to a floating-point - value, make a COMPARE with the appropriate constant of 0. If we - don't do this, later code can interpret this as a test against - const0_rtx, which can cause problems if we try to put it into an - insn as a floating-point operand. */ - if (dest == cc0_rtx) - { - this_insn_cc0 = src_const && mode != VOIDmode ? src_const : src; - this_insn_cc0_mode = mode; - if (GET_MODE_CLASS (mode) == MODE_FLOAT) - this_insn_cc0 = gen_rtx (COMPARE, VOIDmode, this_insn_cc0, - CONST0_RTX (mode)); - } -#endif - } - - /* Now enter all non-volatile source expressions in the hash table - if they are not already present. - Record their equivalence classes in src_elt. - This way we can insert the corresponding destinations into - the same classes even if the actual sources are no longer in them - (having been invalidated). */ - - if (src_eqv && src_eqv_elt == 0 && sets[0].rtl != 0 && ! src_eqv_volatile - && ! rtx_equal_p (src_eqv, SET_DEST (sets[0].rtl))) - { - register struct table_elt *elt; - register struct table_elt *classp = sets[0].src_elt; - rtx dest = SET_DEST (sets[0].rtl); - enum machine_mode eqvmode = GET_MODE (dest); - - if (GET_CODE (dest) == STRICT_LOW_PART) - { - eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0))); - classp = 0; - } - if (insert_regs (src_eqv, classp, 0)) - src_eqv_hash_code = HASH (src_eqv, eqvmode); - elt = insert (src_eqv, classp, src_eqv_hash_code, eqvmode); - elt->in_memory = src_eqv_in_memory; - elt->in_struct = src_eqv_in_struct; - src_eqv_elt = elt; - } - - for (i = 0; i < n_sets; i++) - if (sets[i].rtl && ! sets[i].src_volatile - && ! rtx_equal_p (SET_SRC (sets[i].rtl), SET_DEST (sets[i].rtl))) - { - if (GET_CODE (SET_DEST (sets[i].rtl)) == STRICT_LOW_PART) - { - /* REG_EQUAL in setting a STRICT_LOW_PART - gives an equivalent for the entire destination register, - not just for the subreg being stored in now. - This is a more interesting equivalence, so we arrange later - to treat the entire reg as the destination. */ - sets[i].src_elt = src_eqv_elt; - sets[i].src_hash_code = src_eqv_hash_code; - } - else - { - /* Insert source and constant equivalent into hash table, if not - already present. */ - register struct table_elt *classp = src_eqv_elt; - register rtx src = sets[i].src; - register rtx dest = SET_DEST (sets[i].rtl); - enum machine_mode mode - = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src); - - if (sets[i].src_elt == 0) - { - register struct table_elt *elt; - - /* Note that these insert_regs calls cannot remove - any of the src_elt's, because they would have failed to - match if not still valid. */ - if (insert_regs (src, classp, 0)) - sets[i].src_hash_code = HASH (src, mode); - elt = insert (src, classp, sets[i].src_hash_code, mode); - elt->in_memory = sets[i].src_in_memory; - elt->in_struct = sets[i].src_in_struct; - sets[i].src_elt = classp = elt; - } - - if (sets[i].src_const && sets[i].src_const_elt == 0 - && src != sets[i].src_const - && ! rtx_equal_p (sets[i].src_const, src)) - sets[i].src_elt = insert (sets[i].src_const, classp, - sets[i].src_const_hash_code, mode); - } - } - else if (sets[i].src_elt == 0) - /* If we did not insert the source into the hash table (e.g., it was - volatile), note the equivalence class for the REG_EQUAL value, if any, - so that the destination goes into that class. */ - sets[i].src_elt = src_eqv_elt; - - invalidate_from_clobbers (&writes_memory, x); - - /* Some registers are invalidated by subroutine calls. Memory is - invalidated by non-constant calls. */ - - if (GET_CODE (insn) == CALL_INSN) - { - static struct write_data everything = {0, 1, 1, 1}; - - if (! CONST_CALL_P (insn)) - invalidate_memory (&everything); - invalidate_for_call (); - } - - /* Now invalidate everything set by this instruction. - If a SUBREG or other funny destination is being set, - sets[i].rtl is still nonzero, so here we invalidate the reg - a part of which is being set. */ - - for (i = 0; i < n_sets; i++) - if (sets[i].rtl) - { - register rtx dest = sets[i].inner_dest; - - /* Needed for registers to remove the register from its - previous quantity's chain. - Needed for memory if this is a nonvarying address, unless - we have just done an invalidate_memory that covers even those. */ - if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG - || (! writes_memory.all && ! cse_rtx_addr_varies_p (dest))) - invalidate (dest); - } - - /* Make sure registers mentioned in destinations - are safe for use in an expression to be inserted. - This removes from the hash table - any invalid entry that refers to one of these registers. - - We don't care about the return value from mention_regs because - we are going to hash the SET_DEST values unconditionally. */ - - for (i = 0; i < n_sets; i++) - if (sets[i].rtl && GET_CODE (SET_DEST (sets[i].rtl)) != REG) - mention_regs (SET_DEST (sets[i].rtl)); - - /* We may have just removed some of the src_elt's from the hash table. - So replace each one with the current head of the same class. */ - - for (i = 0; i < n_sets; i++) - if (sets[i].rtl) - { - if (sets[i].src_elt && sets[i].src_elt->first_same_value == 0) - /* If elt was removed, find current head of same class, - or 0 if nothing remains of that class. */ - { - register struct table_elt *elt = sets[i].src_elt; - - while (elt && elt->prev_same_value) - elt = elt->prev_same_value; - - while (elt && elt->first_same_value == 0) - elt = elt->next_same_value; - sets[i].src_elt = elt ? elt->first_same_value : 0; - } - } - - /* Now insert the destinations into their equivalence classes. */ - - for (i = 0; i < n_sets; i++) - if (sets[i].rtl) - { - register rtx dest = SET_DEST (sets[i].rtl); - register struct table_elt *elt; - - /* Don't record value if we are not supposed to risk allocating - floating-point values in registers that might be wider than - memory. */ - if ((flag_float_store - && GET_CODE (dest) == MEM - && GET_MODE_CLASS (GET_MODE (dest)) == MODE_FLOAT) - /* Don't record values of destinations set inside a libcall block - since we might delete the libcall. Things should have been set - up so we won't want to reuse such a value, but we play it safe - here. */ - || in_libcall_block - /* If we didn't put a REG_EQUAL value or a source into the hash - table, there is no point is recording DEST. */ - || sets[i].src_elt == 0) - continue; - - /* STRICT_LOW_PART isn't part of the value BEING set, - and neither is the SUBREG inside it. - Note that in this case SETS[I].SRC_ELT is really SRC_EQV_ELT. */ - if (GET_CODE (dest) == STRICT_LOW_PART) - dest = SUBREG_REG (XEXP (dest, 0)); - - if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG) - /* Registers must also be inserted into chains for quantities. */ - if (insert_regs (dest, sets[i].src_elt, 1)) - /* If `insert_regs' changes something, the hash code must be - recalculated. */ - sets[i].dest_hash_code = HASH (dest, GET_MODE (dest)); - - elt = insert (dest, sets[i].src_elt, - sets[i].dest_hash_code, GET_MODE (dest)); - elt->in_memory = GET_CODE (sets[i].inner_dest) == MEM; - if (elt->in_memory) - { - /* This implicitly assumes a whole struct - need not have MEM_IN_STRUCT_P. - But a whole struct is *supposed* to have MEM_IN_STRUCT_P. */ - elt->in_struct = (MEM_IN_STRUCT_P (sets[i].inner_dest) - || sets[i].inner_dest != SET_DEST (sets[i].rtl)); - } - - /* If we have (set (subreg:m1 (reg:m2 foo) 0) (bar:m1)), M1 is no - narrower than M2, and both M1 and M2 are the same number of words, - we are also doing (set (reg:m2 foo) (subreg:m2 (bar:m1) 0)) so - make that equivalence as well. - - However, BAR may have equivalences for which gen_lowpart_if_possible - will produce a simpler value than gen_lowpart_if_possible applied to - BAR (e.g., if BAR was ZERO_EXTENDed from M2), so we will scan all - BAR's equivalences. If we don't get a simplified form, make - the SUBREG. It will not be used in an equivalence, but will - cause two similar assignments to be detected. - - Note the loop below will find SUBREG_REG (DEST) since we have - already entered SRC and DEST of the SET in the table. */ - - if (GET_CODE (dest) == SUBREG - && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) / UNITS_PER_WORD - == GET_MODE_SIZE (GET_MODE (dest)) / UNITS_PER_WORD) - && (GET_MODE_SIZE (GET_MODE (dest)) - >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest)))) - && sets[i].src_elt != 0) - { - enum machine_mode new_mode = GET_MODE (SUBREG_REG (dest)); - struct table_elt *elt, *classp = 0; - - for (elt = sets[i].src_elt->first_same_value; elt; - elt = elt->next_same_value) - { - rtx new_src = 0; - int src_hash; - struct table_elt *src_elt; - - /* Ignore invalid entries. */ - if (GET_CODE (elt->exp) != REG - && ! exp_equiv_p (elt->exp, elt->exp, 1, 0)) - continue; - - new_src = gen_lowpart_if_possible (new_mode, elt->exp); - if (new_src == 0) - new_src = gen_rtx (SUBREG, new_mode, elt->exp, 0); - - src_hash = HASH (new_src, new_mode); - src_elt = lookup (new_src, src_hash, new_mode); - - /* Put the new source in the hash table is if isn't - already. */ - if (src_elt == 0) - { - if (insert_regs (new_src, classp, 0)) - src_hash = HASH (new_src, new_mode); - src_elt = insert (new_src, classp, src_hash, new_mode); - src_elt->in_memory = elt->in_memory; - src_elt->in_struct = elt->in_struct; - } - else if (classp && classp != src_elt->first_same_value) - /* Show that two things that we've seen before are - actually the same. */ - merge_equiv_classes (src_elt, classp); - - classp = src_elt->first_same_value; - } - } - } - - /* Special handling for (set REG0 REG1) - where REG0 is the "cheapest", cheaper than REG1. - After cse, REG1 will probably not be used in the sequel, - so (if easily done) change this insn to (set REG1 REG0) and - replace REG1 with REG0 in the previous insn that computed their value. - Then REG1 will become a dead store and won't cloud the situation - for later optimizations. - - Do not make this change if REG1 is a hard register, because it will - then be used in the sequel and we may be changing a two-operand insn - into a three-operand insn. - - Also do not do this if we are operating on a copy of INSN. */ - - if (n_sets == 1 && sets[0].rtl && GET_CODE (SET_DEST (sets[0].rtl)) == REG - && NEXT_INSN (PREV_INSN (insn)) == insn - && GET_CODE (SET_SRC (sets[0].rtl)) == REG - && REGNO (SET_SRC (sets[0].rtl)) >= FIRST_PSEUDO_REGISTER - && REGNO_QTY_VALID_P (REGNO (SET_SRC (sets[0].rtl))) - && (qty_first_reg[reg_qty[REGNO (SET_SRC (sets[0].rtl))]] - == REGNO (SET_DEST (sets[0].rtl)))) - { - rtx prev = PREV_INSN (insn); - while (prev && GET_CODE (prev) == NOTE) - prev = PREV_INSN (prev); - - if (prev && GET_CODE (prev) == INSN && GET_CODE (PATTERN (prev)) == SET - && SET_DEST (PATTERN (prev)) == SET_SRC (sets[0].rtl)) - { - rtx dest = SET_DEST (sets[0].rtl); - rtx note = find_reg_note (prev, REG_EQUIV, NULL_RTX); - - validate_change (prev, & SET_DEST (PATTERN (prev)), dest, 1); - validate_change (insn, & SET_DEST (sets[0].rtl), - SET_SRC (sets[0].rtl), 1); - validate_change (insn, & SET_SRC (sets[0].rtl), dest, 1); - apply_change_group (); - - /* If REG1 was equivalent to a constant, REG0 is not. */ - if (note) - PUT_REG_NOTE_KIND (note, REG_EQUAL); - - /* If there was a REG_WAS_0 note on PREV, remove it. Move - any REG_WAS_0 note on INSN to PREV. */ - note = find_reg_note (prev, REG_WAS_0, NULL_RTX); - if (note) - remove_note (prev, note); - - note = find_reg_note (insn, REG_WAS_0, NULL_RTX); - if (note) - { - remove_note (insn, note); - XEXP (note, 1) = REG_NOTES (prev); - REG_NOTES (prev) = note; - } - } - } - - /* If this is a conditional jump insn, record any known equivalences due to - the condition being tested. */ - - last_jump_equiv_class = 0; - if (GET_CODE (insn) == JUMP_INSN - && n_sets == 1 && GET_CODE (x) == SET - && GET_CODE (SET_SRC (x)) == IF_THEN_ELSE) - record_jump_equiv (insn, 0); - -#ifdef HAVE_cc0 - /* If the previous insn set CC0 and this insn no longer references CC0, - delete the previous insn. Here we use the fact that nothing expects CC0 - to be valid over an insn, which is true until the final pass. */ - if (prev_insn && GET_CODE (prev_insn) == INSN - && (tem = single_set (prev_insn)) != 0 - && SET_DEST (tem) == cc0_rtx - && ! reg_mentioned_p (cc0_rtx, x)) - { - PUT_CODE (prev_insn, NOTE); - NOTE_LINE_NUMBER (prev_insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (prev_insn) = 0; - } - - prev_insn_cc0 = this_insn_cc0; - prev_insn_cc0_mode = this_insn_cc0_mode; -#endif - - prev_insn = insn; -} - -/* Store 1 in *WRITES_PTR for those categories of memory ref - that must be invalidated when the expression WRITTEN is stored in. - If WRITTEN is null, say everything must be invalidated. */ - -static void -note_mem_written (written, writes_ptr) - rtx written; - struct write_data *writes_ptr; -{ - static struct write_data everything = {0, 1, 1, 1}; - - if (written == 0) - *writes_ptr = everything; - else if (GET_CODE (written) == MEM) - { - /* Pushing or popping the stack invalidates just the stack pointer. */ - rtx addr = XEXP (written, 0); - if ((GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC - || GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC) - && GET_CODE (XEXP (addr, 0)) == REG - && REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM) - { - writes_ptr->sp = 1; - return; - } - else if (GET_MODE (written) == BLKmode) - *writes_ptr = everything; - else if (cse_rtx_addr_varies_p (written)) - { - /* A varying address that is a sum indicates an array element, - and that's just as good as a structure element - in implying that we need not invalidate scalar variables. - However, we must allow QImode aliasing of scalars, because the - ANSI C standard allows character pointers to alias anything. */ - if (! ((MEM_IN_STRUCT_P (written) - || GET_CODE (XEXP (written, 0)) == PLUS) - && GET_MODE (written) != QImode)) - writes_ptr->all = 1; - writes_ptr->nonscalar = 1; - } - writes_ptr->var = 1; - } -} - -/* Perform invalidation on the basis of everything about an insn - except for invalidating the actual places that are SET in it. - This includes the places CLOBBERed, and anything that might - alias with something that is SET or CLOBBERed. - - W points to the writes_memory for this insn, a struct write_data - saying which kinds of memory references must be invalidated. - X is the pattern of the insn. */ - -static void -invalidate_from_clobbers (w, x) - struct write_data *w; - rtx x; -{ - /* If W->var is not set, W specifies no action. - If W->all is set, this step gets all memory refs - so they can be ignored in the rest of this function. */ - if (w->var) - invalidate_memory (w); - - if (w->sp) - { - if (reg_tick[STACK_POINTER_REGNUM] >= 0) - reg_tick[STACK_POINTER_REGNUM]++; - - /* This should be *very* rare. */ - if (TEST_HARD_REG_BIT (hard_regs_in_table, STACK_POINTER_REGNUM)) - invalidate (stack_pointer_rtx); - } - - if (GET_CODE (x) == CLOBBER) - { - rtx ref = XEXP (x, 0); - if (ref - && (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG - || (GET_CODE (ref) == MEM && ! w->all))) - invalidate (ref); - } - else if (GET_CODE (x) == PARALLEL) - { - register int i; - for (i = XVECLEN (x, 0) - 1; i >= 0; i--) - { - register rtx y = XVECEXP (x, 0, i); - if (GET_CODE (y) == CLOBBER) - { - rtx ref = XEXP (y, 0); - if (ref - &&(GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG - || (GET_CODE (ref) == MEM && !w->all))) - invalidate (ref); - } - } - } -} - -/* Process X, part of the REG_NOTES of an insn. Look at any REG_EQUAL notes - and replace any registers in them with either an equivalent constant - or the canonical form of the register. If we are inside an address, - only do this if the address remains valid. - - OBJECT is 0 except when within a MEM in which case it is the MEM. - - Return the replacement for X. */ - -static rtx -cse_process_notes (x, object) - rtx x; - rtx object; -{ - enum rtx_code code = GET_CODE (x); - char *fmt = GET_RTX_FORMAT (code); - int qty; - int i; - - switch (code) - { - case CONST_INT: - case CONST: - case SYMBOL_REF: - case LABEL_REF: - case CONST_DOUBLE: - case PC: - case CC0: - case LO_SUM: - return x; - - case MEM: - XEXP (x, 0) = cse_process_notes (XEXP (x, 0), x); - return x; - - case EXPR_LIST: - case INSN_LIST: - if (REG_NOTE_KIND (x) == REG_EQUAL) - XEXP (x, 0) = cse_process_notes (XEXP (x, 0), NULL_RTX); - if (XEXP (x, 1)) - XEXP (x, 1) = cse_process_notes (XEXP (x, 1), NULL_RTX); - return x; - - case SIGN_EXTEND: - case ZERO_EXTEND: - { - rtx new = cse_process_notes (XEXP (x, 0), object); - /* We don't substitute VOIDmode constants into these rtx, - since they would impede folding. */ - if (GET_MODE (new) != VOIDmode) - validate_change (object, &XEXP (x, 0), new, 0); - return x; - } - - case REG: - i = reg_qty[REGNO (x)]; - - /* Return a constant or a constant register. */ - if (REGNO_QTY_VALID_P (REGNO (x)) - && qty_const[i] != 0 - && (CONSTANT_P (qty_const[i]) - || GET_CODE (qty_const[i]) == REG)) - { - rtx new = gen_lowpart_if_possible (GET_MODE (x), qty_const[i]); - if (new) - return new; - } - - /* Otherwise, canonicalize this register. */ - return canon_reg (x, NULL_RTX); - } - - for (i = 0; i < GET_RTX_LENGTH (code); i++) - if (fmt[i] == 'e') - validate_change (object, &XEXP (x, i), - cse_process_notes (XEXP (x, i), object), 0); - - return x; -} - -/* Find common subexpressions between the end test of a loop and the beginning - of the loop. LOOP_START is the CODE_LABEL at the start of a loop. - - Often we have a loop where an expression in the exit test is used - in the body of the loop. For example "while (*p) *q++ = *p++;". - Because of the way we duplicate the loop exit test in front of the loop, - however, we don't detect that common subexpression. This will be caught - when global cse is implemented, but this is a quite common case. - - This function handles the most common cases of these common expressions. - It is called after we have processed the basic block ending with the - NOTE_INSN_LOOP_END note that ends a loop and the previous JUMP_INSN - jumps to a label used only once. */ - -static void -cse_around_loop (loop_start) - rtx loop_start; -{ - rtx insn; - int i; - struct table_elt *p; - - /* If the jump at the end of the loop doesn't go to the start, we don't - do anything. */ - for (insn = PREV_INSN (loop_start); - insn && (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) >= 0); - insn = PREV_INSN (insn)) - ; - - if (insn == 0 - || GET_CODE (insn) != NOTE - || NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG) - return; - - /* If the last insn of the loop (the end test) was an NE comparison, - we will interpret it as an EQ comparison, since we fell through - the loop. Any equivalences resulting from that comparison are - therefore not valid and must be invalidated. */ - if (last_jump_equiv_class) - for (p = last_jump_equiv_class->first_same_value; p; - p = p->next_same_value) - if (GET_CODE (p->exp) == MEM || GET_CODE (p->exp) == REG - || GET_CODE (p->exp) == SUBREG) - invalidate (p->exp); - - /* Process insns starting after LOOP_START until we hit a CALL_INSN or - a CODE_LABEL (we could handle a CALL_INSN, but it isn't worth it). - - The only thing we do with SET_DEST is invalidate entries, so we - can safely process each SET in order. It is slightly less efficient - to do so, but we only want to handle the most common cases. */ - - for (insn = NEXT_INSN (loop_start); - GET_CODE (insn) != CALL_INSN && GET_CODE (insn) != CODE_LABEL - && ! (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END); - insn = NEXT_INSN (insn)) - { - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && (GET_CODE (PATTERN (insn)) == SET - || GET_CODE (PATTERN (insn)) == CLOBBER)) - cse_set_around_loop (PATTERN (insn), insn, loop_start); - else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && GET_CODE (PATTERN (insn)) == PARALLEL) - for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) - if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET - || GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == CLOBBER) - cse_set_around_loop (XVECEXP (PATTERN (insn), 0, i), insn, - loop_start); - } -} - -/* Variable used for communications between the next two routines. */ - -static struct write_data skipped_writes_memory; - -/* Process one SET of an insn that was skipped. We ignore CLOBBERs - since they are done elsewhere. This function is called via note_stores. */ - -static void -invalidate_skipped_set (dest, set) - rtx set; - rtx dest; -{ - if (GET_CODE (set) == CLOBBER -#ifdef HAVE_cc0 - || dest == cc0_rtx -#endif - || dest == pc_rtx) - return; - - if (GET_CODE (dest) == MEM) - note_mem_written (dest, &skipped_writes_memory); - - if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG - || (! skipped_writes_memory.all && ! cse_rtx_addr_varies_p (dest))) - invalidate (dest); -} - -/* Invalidate all insns from START up to the end of the function or the - next label. This called when we wish to CSE around a block that is - conditionally executed. */ - -static void -invalidate_skipped_block (start) - rtx start; -{ - rtx insn; - int i; - static struct write_data init = {0, 0, 0, 0}; - static struct write_data everything = {0, 1, 1, 1}; - - for (insn = start; insn && GET_CODE (insn) != CODE_LABEL; - insn = NEXT_INSN (insn)) - { - if (GET_RTX_CLASS (GET_CODE (insn)) != 'i') - continue; - - skipped_writes_memory = init; - - if (GET_CODE (insn) == CALL_INSN) - { - invalidate_for_call (); - skipped_writes_memory = everything; - } - - note_stores (PATTERN (insn), invalidate_skipped_set); - invalidate_from_clobbers (&skipped_writes_memory, PATTERN (insn)); - } -} - -/* Used for communication between the following two routines; contains a - value to be checked for modification. */ - -static rtx cse_check_loop_start_value; - -/* If modifying X will modify the value in CSE_CHECK_LOOP_START_VALUE, - indicate that fact by setting CSE_CHECK_LOOP_START_VALUE to 0. */ - -static void -cse_check_loop_start (x, set) - rtx x; - rtx set; -{ - if (cse_check_loop_start_value == 0 - || GET_CODE (x) == CC0 || GET_CODE (x) == PC) - return; - - if ((GET_CODE (x) == MEM && GET_CODE (cse_check_loop_start_value) == MEM) - || reg_overlap_mentioned_p (x, cse_check_loop_start_value)) - cse_check_loop_start_value = 0; -} - -/* X is a SET or CLOBBER contained in INSN that was found near the start of - a loop that starts with the label at LOOP_START. - - If X is a SET, we see if its SET_SRC is currently in our hash table. - If so, we see if it has a value equal to some register used only in the - loop exit code (as marked by jump.c). - - If those two conditions are true, we search backwards from the start of - the loop to see if that same value was loaded into a register that still - retains its value at the start of the loop. - - If so, we insert an insn after the load to copy the destination of that - load into the equivalent register and (try to) replace our SET_SRC with that - register. - - In any event, we invalidate whatever this SET or CLOBBER modifies. */ - -static void -cse_set_around_loop (x, insn, loop_start) - rtx x; - rtx insn; - rtx loop_start; -{ - rtx p; - struct table_elt *src_elt; - static struct write_data init = {0, 0, 0, 0}; - struct write_data writes_memory; - - writes_memory = init; - - /* If this is a SET, see if we can replace SET_SRC, but ignore SETs that - are setting PC or CC0 or whose SET_SRC is already a register. */ - if (GET_CODE (x) == SET - && GET_CODE (SET_DEST (x)) != PC && GET_CODE (SET_DEST (x)) != CC0 - && GET_CODE (SET_SRC (x)) != REG) - { - src_elt = lookup (SET_SRC (x), - HASH (SET_SRC (x), GET_MODE (SET_DEST (x))), - GET_MODE (SET_DEST (x))); - - if (src_elt) - for (src_elt = src_elt->first_same_value; src_elt; - src_elt = src_elt->next_same_value) - if (GET_CODE (src_elt->exp) == REG && REG_LOOP_TEST_P (src_elt->exp) - && COST (src_elt->exp) < COST (SET_SRC (x))) - { - rtx p, set; - - /* Look for an insn in front of LOOP_START that sets - something in the desired mode to SET_SRC (x) before we hit - a label or CALL_INSN. */ - - for (p = prev_nonnote_insn (loop_start); - p && GET_CODE (p) != CALL_INSN - && GET_CODE (p) != CODE_LABEL; - p = prev_nonnote_insn (p)) - if ((set = single_set (p)) != 0 - && GET_CODE (SET_DEST (set)) == REG - && GET_MODE (SET_DEST (set)) == src_elt->mode - && rtx_equal_p (SET_SRC (set), SET_SRC (x))) - { - /* We now have to ensure that nothing between P - and LOOP_START modified anything referenced in - SET_SRC (x). We know that nothing within the loop - can modify it, or we would have invalidated it in - the hash table. */ - rtx q; - - cse_check_loop_start_value = SET_SRC (x); - for (q = p; q != loop_start; q = NEXT_INSN (q)) - if (GET_RTX_CLASS (GET_CODE (q)) == 'i') - note_stores (PATTERN (q), cse_check_loop_start); - - /* If nothing was changed and we can replace our - SET_SRC, add an insn after P to copy its destination - to what we will be replacing SET_SRC with. */ - if (cse_check_loop_start_value - && validate_change (insn, &SET_SRC (x), - src_elt->exp, 0)) - emit_insn_after (gen_move_insn (src_elt->exp, - SET_DEST (set)), - p); - break; - } - } - } - - /* Now invalidate anything modified by X. */ - note_mem_written (SET_DEST (x), &writes_memory); - - if (writes_memory.var) - invalidate_memory (&writes_memory); - - /* See comment on similar code in cse_insn for explanation of these tests. */ - if (GET_CODE (SET_DEST (x)) == REG || GET_CODE (SET_DEST (x)) == SUBREG - || (GET_CODE (SET_DEST (x)) == MEM && ! writes_memory.all - && ! cse_rtx_addr_varies_p (SET_DEST (x)))) - invalidate (SET_DEST (x)); -} - -/* Find the end of INSN's basic block and return its range, - the total number of SETs in all the insns of the block, the last insn of the - block, and the branch path. - - The branch path indicates which branches should be followed. If a non-zero - path size is specified, the block should be rescanned and a different set - of branches will be taken. The branch path is only used if - FLAG_CSE_FOLLOW_JUMPS or FLAG_CSE_SKIP_BLOCKS is non-zero. - - DATA is a pointer to a struct cse_basic_block_data, defined below, that is - used to describe the block. It is filled in with the information about - the current block. The incoming structure's branch path, if any, is used - to construct the output branch path. */ - -void -cse_end_of_basic_block (insn, data, follow_jumps, after_loop, skip_blocks) - rtx insn; - struct cse_basic_block_data *data; - int follow_jumps; - int after_loop; - int skip_blocks; -{ - rtx p = insn, q; - int nsets = 0; - int low_cuid = INSN_CUID (insn), high_cuid = INSN_CUID (insn); - rtx next = GET_RTX_CLASS (GET_CODE (insn)) == 'i' ? insn : next_real_insn (insn); - int path_size = data->path_size; - int path_entry = 0; - int i; - - /* Update the previous branch path, if any. If the last branch was - previously TAKEN, mark it NOT_TAKEN. If it was previously NOT_TAKEN, - shorten the path by one and look at the previous branch. We know that - at least one branch must have been taken if PATH_SIZE is non-zero. */ - while (path_size > 0) - { - if (data->path[path_size - 1].status != NOT_TAKEN) - { - data->path[path_size - 1].status = NOT_TAKEN; - break; - } - else - path_size--; - } - - /* Scan to end of this basic block. */ - while (p && GET_CODE (p) != CODE_LABEL) - { - /* Don't cse out the end of a loop. This makes a difference - only for the unusual loops that always execute at least once; - all other loops have labels there so we will stop in any case. - Cse'ing out the end of the loop is dangerous because it - might cause an invariant expression inside the loop - to be reused after the end of the loop. This would make it - hard to move the expression out of the loop in loop.c, - especially if it is one of several equivalent expressions - and loop.c would like to eliminate it. - - If we are running after loop.c has finished, we can ignore - the NOTE_INSN_LOOP_END. */ - - if (! after_loop && GET_CODE (p) == NOTE - && NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END) - break; - - /* Don't cse over a call to setjmp; on some machines (eg vax) - the regs restored by the longjmp come from - a later time than the setjmp. */ - if (GET_CODE (p) == NOTE - && NOTE_LINE_NUMBER (p) == NOTE_INSN_SETJMP) - break; - - /* A PARALLEL can have lots of SETs in it, - especially if it is really an ASM_OPERANDS. */ - if (GET_RTX_CLASS (GET_CODE (p)) == 'i' - && GET_CODE (PATTERN (p)) == PARALLEL) - nsets += XVECLEN (PATTERN (p), 0); - else if (GET_CODE (p) != NOTE) - nsets += 1; - - /* Ignore insns made by CSE; they cannot affect the boundaries of - the basic block. */ - - if (INSN_UID (p) <= max_uid && INSN_CUID (p) > high_cuid) - high_cuid = INSN_CUID (p); - if (INSN_UID (p) <= max_uid && INSN_CUID (p) < low_cuid) - low_cuid = INSN_CUID (p); - - /* See if this insn is in our branch path. If it is and we are to - take it, do so. */ - if (path_entry < path_size && data->path[path_entry].branch == p) - { - if (data->path[path_entry].status != NOT_TAKEN) - p = JUMP_LABEL (p); - - /* Point to next entry in path, if any. */ - path_entry++; - } - - /* If this is a conditional jump, we can follow it if -fcse-follow-jumps - was specified, we haven't reached our maximum path length, there are - insns following the target of the jump, this is the only use of the - jump label, and the target label is preceded by a BARRIER. - - Alternatively, we can follow the jump if it branches around a - block of code and there are no other branches into the block. - In this case invalidate_skipped_block will be called to invalidate any - registers set in the block when following the jump. */ - - else if ((follow_jumps || skip_blocks) && path_size < PATHLENGTH - 1 - && GET_CODE (p) == JUMP_INSN - && GET_CODE (PATTERN (p)) == SET - && GET_CODE (SET_SRC (PATTERN (p))) == IF_THEN_ELSE - && LABEL_NUSES (JUMP_LABEL (p)) == 1 - && NEXT_INSN (JUMP_LABEL (p)) != 0) - { - for (q = PREV_INSN (JUMP_LABEL (p)); q; q = PREV_INSN (q)) - if ((GET_CODE (q) != NOTE - || NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_END - || NOTE_LINE_NUMBER (q) == NOTE_INSN_SETJMP) - && (GET_CODE (q) != CODE_LABEL || LABEL_NUSES (q) != 0)) - break; - - /* If we ran into a BARRIER, this code is an extension of the - basic block when the branch is taken. */ - if (follow_jumps && q != 0 && GET_CODE (q) == BARRIER) - { - /* Don't allow ourself to keep walking around an - always-executed loop. */ - if (next_real_insn (q) == next) - { - p = NEXT_INSN (p); - continue; - } - - /* Similarly, don't put a branch in our path more than once. */ - for (i = 0; i < path_entry; i++) - if (data->path[i].branch == p) - break; - - if (i != path_entry) - break; - - data->path[path_entry].branch = p; - data->path[path_entry++].status = TAKEN; - - /* This branch now ends our path. It was possible that we - didn't see this branch the last time around (when the - insn in front of the target was a JUMP_INSN that was - turned into a no-op). */ - path_size = path_entry; - - p = JUMP_LABEL (p); - /* Mark block so we won't scan it again later. */ - PUT_MODE (NEXT_INSN (p), QImode); - } - /* Detect a branch around a block of code. */ - else if (skip_blocks && q != 0 && GET_CODE (q) != CODE_LABEL) - { - register rtx tmp; - - if (next_real_insn (q) == next) - { - p = NEXT_INSN (p); - continue; - } - - for (i = 0; i < path_entry; i++) - if (data->path[i].branch == p) - break; - - if (i != path_entry) - break; - - /* This is no_labels_between_p (p, q) with an added check for - reaching the end of a function (in case Q precedes P). */ - for (tmp = NEXT_INSN (p); tmp && tmp != q; tmp = NEXT_INSN (tmp)) - if (GET_CODE (tmp) == CODE_LABEL) - break; - - if (tmp == q) - { - data->path[path_entry].branch = p; - data->path[path_entry++].status = AROUND; - - path_size = path_entry; - - p = JUMP_LABEL (p); - /* Mark block so we won't scan it again later. */ - PUT_MODE (NEXT_INSN (p), QImode); - } - } - } - p = NEXT_INSN (p); - } - - data->low_cuid = low_cuid; - data->high_cuid = high_cuid; - data->nsets = nsets; - data->last = p; - - /* If all jumps in the path are not taken, set our path length to zero - so a rescan won't be done. */ - for (i = path_size - 1; i >= 0; i--) - if (data->path[i].status != NOT_TAKEN) - break; - - if (i == -1) - data->path_size = 0; - else - data->path_size = path_size; - - /* End the current branch path. */ - data->path[path_size].branch = 0; -} - -/* Perform cse on the instructions of a function. - F is the first instruction. - NREGS is one plus the highest pseudo-reg number used in the instruction. - - AFTER_LOOP is 1 if this is the cse call done after loop optimization - (only if -frerun-cse-after-loop). - - Returns 1 if jump_optimize should be redone due to simplifications - in conditional jump instructions. */ - -int -cse_main (f, nregs, after_loop, file) - rtx f; - int nregs; - int after_loop; - FILE *file; -{ - struct cse_basic_block_data val; - register rtx insn = f; - register int i; - - cse_jumps_altered = 0; - constant_pool_entries_cost = 0; - val.path_size = 0; - - init_recog (); - - max_reg = nregs; - - all_minus_one = (int *) alloca (nregs * sizeof (int)); - consec_ints = (int *) alloca (nregs * sizeof (int)); - - for (i = 0; i < nregs; i++) - { - all_minus_one[i] = -1; - consec_ints[i] = i; - } - - reg_next_eqv = (int *) alloca (nregs * sizeof (int)); - reg_prev_eqv = (int *) alloca (nregs * sizeof (int)); - reg_qty = (int *) alloca (nregs * sizeof (int)); - reg_in_table = (int *) alloca (nregs * sizeof (int)); - reg_tick = (int *) alloca (nregs * sizeof (int)); - - /* Discard all the free elements of the previous function - since they are allocated in the temporarily obstack. */ - bzero (table, sizeof table); - free_element_chain = 0; - n_elements_made = 0; - - /* Find the largest uid. */ - - max_uid = get_max_uid (); - uid_cuid = (int *) alloca ((max_uid + 1) * sizeof (int)); - bzero (uid_cuid, (max_uid + 1) * sizeof (int)); - - /* Compute the mapping from uids to cuids. - CUIDs are numbers assigned to insns, like uids, - except that cuids increase monotonically through the code. - Don't assign cuids to line-number NOTEs, so that the distance in cuids - between two insns is not affected by -g. */ - - for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) != NOTE - || NOTE_LINE_NUMBER (insn) < 0) - INSN_CUID (insn) = ++i; - else - /* Give a line number note the same cuid as preceding insn. */ - INSN_CUID (insn) = i; - } - - /* Initialize which registers are clobbered by calls. */ - - CLEAR_HARD_REG_SET (regs_invalidated_by_call); - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if ((call_used_regs[i] - /* Used to check !fixed_regs[i] here, but that isn't safe; - fixed regs are still call-clobbered, and sched can get - confused if they can "live across calls". - - The frame pointer is always preserved across calls. The arg - pointer is if it is fixed. The stack pointer usually is, unless - RETURN_POPS_ARGS, in which case an explicit CLOBBER - will be present. If we are generating PIC code, the PIC offset - table register is preserved across calls. */ - - && i != STACK_POINTER_REGNUM - && i != FRAME_POINTER_REGNUM -#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM - && ! (i == ARG_POINTER_REGNUM && fixed_regs[i]) -#endif -#ifdef PIC_OFFSET_TABLE_REGNUM - && ! (i == PIC_OFFSET_TABLE_REGNUM && flag_pic) -#endif - ) - || global_regs[i]) - SET_HARD_REG_BIT (regs_invalidated_by_call, i); - - /* Loop over basic blocks. - Compute the maximum number of qty's needed for each basic block - (which is 2 for each SET). */ - insn = f; - while (insn) - { - cse_end_of_basic_block (insn, &val, flag_cse_follow_jumps, after_loop, - flag_cse_skip_blocks); - - /* If this basic block was already processed or has no sets, skip it. */ - if (val.nsets == 0 || GET_MODE (insn) == QImode) - { - PUT_MODE (insn, VOIDmode); - insn = (val.last ? NEXT_INSN (val.last) : 0); - val.path_size = 0; - continue; - } - - cse_basic_block_start = val.low_cuid; - cse_basic_block_end = val.high_cuid; - max_qty = val.nsets * 2; - - if (file) - fprintf (file, ";; Processing block from %d to %d, %d sets.\n", - INSN_UID (insn), val.last ? INSN_UID (val.last) : 0, - val.nsets); - - /* Make MAX_QTY bigger to give us room to optimize - past the end of this basic block, if that should prove useful. */ - if (max_qty < 500) - max_qty = 500; - - max_qty += max_reg; - - /* If this basic block is being extended by following certain jumps, - (see `cse_end_of_basic_block'), we reprocess the code from the start. - Otherwise, we start after this basic block. */ - if (val.path_size > 0) - cse_basic_block (insn, val.last, val.path, 0); - else - { - int old_cse_jumps_altered = cse_jumps_altered; - rtx temp; - - /* When cse changes a conditional jump to an unconditional - jump, we want to reprocess the block, since it will give - us a new branch path to investigate. */ - cse_jumps_altered = 0; - temp = cse_basic_block (insn, val.last, val.path, ! after_loop); - if (cse_jumps_altered == 0 - || (flag_cse_follow_jumps == 0 && flag_cse_skip_blocks == 0)) - insn = temp; - - cse_jumps_altered |= old_cse_jumps_altered; - } - -#ifdef USE_C_ALLOCA - alloca (0); -#endif - } - - /* Tell refers_to_mem_p that qty_const info is not available. */ - qty_const = 0; - - if (max_elements_made < n_elements_made) - max_elements_made = n_elements_made; - - return cse_jumps_altered; -} - -/* Process a single basic block. FROM and TO and the limits of the basic - block. NEXT_BRANCH points to the branch path when following jumps or - a null path when not following jumps. - - AROUND_LOOP is non-zero if we are to try to cse around to the start of a - loop. This is true when we are being called for the last time on a - block and this CSE pass is before loop.c. */ - -static rtx -cse_basic_block (from, to, next_branch, around_loop) - register rtx from, to; - struct branch_path *next_branch; - int around_loop; -{ - register rtx insn; - int to_usage = 0; - int in_libcall_block = 0; - - /* Each of these arrays is undefined before max_reg, so only allocate - the space actually needed and adjust the start below. */ - - qty_first_reg = (int *) alloca ((max_qty - max_reg) * sizeof (int)); - qty_last_reg = (int *) alloca ((max_qty - max_reg) * sizeof (int)); - qty_mode= (enum machine_mode *) alloca ((max_qty - max_reg) * sizeof (enum machine_mode)); - qty_const = (rtx *) alloca ((max_qty - max_reg) * sizeof (rtx)); - qty_const_insn = (rtx *) alloca ((max_qty - max_reg) * sizeof (rtx)); - qty_comparison_code - = (enum rtx_code *) alloca ((max_qty - max_reg) * sizeof (enum rtx_code)); - qty_comparison_qty = (int *) alloca ((max_qty - max_reg) * sizeof (int)); - qty_comparison_const = (rtx *) alloca ((max_qty - max_reg) * sizeof (rtx)); - - qty_first_reg -= max_reg; - qty_last_reg -= max_reg; - qty_mode -= max_reg; - qty_const -= max_reg; - qty_const_insn -= max_reg; - qty_comparison_code -= max_reg; - qty_comparison_qty -= max_reg; - qty_comparison_const -= max_reg; - - new_basic_block (); - - /* TO might be a label. If so, protect it from being deleted. */ - if (to != 0 && GET_CODE (to) == CODE_LABEL) - ++LABEL_NUSES (to); - - for (insn = from; insn != to; insn = NEXT_INSN (insn)) - { - register enum rtx_code code; - - /* See if this is a branch that is part of the path. If so, and it is - to be taken, do so. */ - if (next_branch->branch == insn) - { - enum taken status = next_branch++->status; - if (status != NOT_TAKEN) - { - if (status == TAKEN) - record_jump_equiv (insn, 1); - else - invalidate_skipped_block (NEXT_INSN (insn)); - - /* Set the last insn as the jump insn; it doesn't affect cc0. - Then follow this branch. */ -#ifdef HAVE_cc0 - prev_insn_cc0 = 0; -#endif - prev_insn = insn; - insn = JUMP_LABEL (insn); - continue; - } - } - - code = GET_CODE (insn); - if (GET_MODE (insn) == QImode) - PUT_MODE (insn, VOIDmode); - - if (GET_RTX_CLASS (code) == 'i') - { - /* Process notes first so we have all notes in canonical forms when - looking for duplicate operations. */ - - if (REG_NOTES (insn)) - REG_NOTES (insn) = cse_process_notes (REG_NOTES (insn), NULL_RTX); - - /* Track when we are inside in LIBCALL block. Inside such a block, - we do not want to record destinations. The last insn of a - LIBCALL block is not considered to be part of the block, since - its destination is the result of the block and hence should be - recorded. */ - - if (find_reg_note (insn, REG_LIBCALL, NULL_RTX)) - in_libcall_block = 1; - else if (find_reg_note (insn, REG_RETVAL, NULL_RTX)) - in_libcall_block = 0; - - cse_insn (insn, in_libcall_block); - } - - /* If INSN is now an unconditional jump, skip to the end of our - basic block by pretending that we just did the last insn in the - basic block. If we are jumping to the end of our block, show - that we can have one usage of TO. */ - - if (simplejump_p (insn)) - { - if (to == 0) - return 0; - - if (JUMP_LABEL (insn) == to) - to_usage = 1; - - /* Maybe TO was deleted because the jump is unconditional. - If so, there is nothing left in this basic block. */ - /* ??? Perhaps it would be smarter to set TO - to whatever follows this insn, - and pretend the basic block had always ended here. */ - if (INSN_DELETED_P (to)) - break; - - insn = PREV_INSN (to); - } - - /* See if it is ok to keep on going past the label - which used to end our basic block. Remember that we incremented - the count of that label, so we decrement it here. If we made - a jump unconditional, TO_USAGE will be one; in that case, we don't - want to count the use in that jump. */ - - if (to != 0 && NEXT_INSN (insn) == to - && GET_CODE (to) == CODE_LABEL && --LABEL_NUSES (to) == to_usage) - { - struct cse_basic_block_data val; - - insn = NEXT_INSN (to); - - if (LABEL_NUSES (to) == 0) - delete_insn (to); - - /* Find the end of the following block. Note that we won't be - following branches in this case. If TO was the last insn - in the function, we are done. Similarly, if we deleted the - insn after TO, it must have been because it was preceded by - a BARRIER. In that case, we are done with this block because it - has no continuation. */ - - if (insn == 0 || INSN_DELETED_P (insn)) - return 0; - - to_usage = 0; - val.path_size = 0; - cse_end_of_basic_block (insn, &val, 0, 0, 0); - - /* If the tables we allocated have enough space left - to handle all the SETs in the next basic block, - continue through it. Otherwise, return, - and that block will be scanned individually. */ - if (val.nsets * 2 + next_qty > max_qty) - break; - - cse_basic_block_start = val.low_cuid; - cse_basic_block_end = val.high_cuid; - to = val.last; - - /* Prevent TO from being deleted if it is a label. */ - if (to != 0 && GET_CODE (to) == CODE_LABEL) - ++LABEL_NUSES (to); - - /* Back up so we process the first insn in the extension. */ - insn = PREV_INSN (insn); - } - } - - if (next_qty > max_qty) - abort (); - - /* If we are running before loop.c, we stopped on a NOTE_INSN_LOOP_END, and - the previous insn is the only insn that branches to the head of a loop, - we can cse into the loop. Don't do this if we changed the jump - structure of a loop unless we aren't going to be following jumps. */ - - if ((cse_jumps_altered == 0 - || (flag_cse_follow_jumps == 0 && flag_cse_skip_blocks == 0)) - && around_loop && to != 0 - && GET_CODE (to) == NOTE && NOTE_LINE_NUMBER (to) == NOTE_INSN_LOOP_END - && GET_CODE (PREV_INSN (to)) == JUMP_INSN - && JUMP_LABEL (PREV_INSN (to)) != 0 - && LABEL_NUSES (JUMP_LABEL (PREV_INSN (to))) == 1) - cse_around_loop (JUMP_LABEL (PREV_INSN (to))); - - return to ? NEXT_INSN (to) : 0; -} - -/* Count the number of times registers are used (not set) in X. - COUNTS is an array in which we accumulate the count, INCR is how much - we count each register usage. */ - -static void -count_reg_usage (x, counts, incr) - rtx x; - int *counts; - int incr; -{ - enum rtx_code code = GET_CODE (x); - char *fmt; - int i, j; - - switch (code) - { - case REG: - counts[REGNO (x)] += incr; - return; - - case PC: - case CC0: - case CONST: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case LABEL_REF: - case CLOBBER: - return; - - case SET: - /* Unless we are setting a REG, count everything in SET_DEST. */ - if (GET_CODE (SET_DEST (x)) != REG) - count_reg_usage (SET_DEST (x), counts, incr); - count_reg_usage (SET_SRC (x), counts, incr); - return; - - case INSN: - case JUMP_INSN: - case CALL_INSN: - count_reg_usage (PATTERN (x), counts, incr); - - /* Things used in a REG_EQUAL note aren't dead since loop may try to - use them. */ - - if (REG_NOTES (x)) - count_reg_usage (REG_NOTES (x), counts, incr); - return; - - case EXPR_LIST: - case INSN_LIST: - if (REG_NOTE_KIND (x) == REG_EQUAL) - count_reg_usage (XEXP (x, 0), counts, incr); - if (XEXP (x, 1)) - count_reg_usage (XEXP (x, 1), counts, incr); - return; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - count_reg_usage (XEXP (x, i), counts, incr); - else if (fmt[i] == 'E') - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - count_reg_usage (XVECEXP (x, i, j), counts, incr); - } -} - -/* Scan all the insns and delete any that are dead; i.e., they store a register - that is never used or they copy a register to itself. - - This is used to remove insns made obviously dead by cse. It improves the - heuristics in loop since it won't try to move dead invariants out of loops - or make givs for dead quantities. The remaining passes of the compilation - are also sped up. */ - -void -delete_dead_from_cse (insns, nreg) - rtx insns; - int nreg; -{ - int *counts = (int *) alloca (nreg * sizeof (int)); - rtx insn, prev; - rtx tem; - int i; - int in_libcall = 0; - - /* First count the number of times each register is used. */ - bzero (counts, sizeof (int) * nreg); - for (insn = next_real_insn (insns); insn; insn = next_real_insn (insn)) - count_reg_usage (insn, counts, 1); - - /* Go from the last insn to the first and delete insns that only set unused - registers or copy a register to itself. As we delete an insn, remove - usage counts for registers it uses. */ - for (insn = prev_real_insn (get_last_insn ()); insn; insn = prev) - { - int live_insn = 0; - - prev = prev_real_insn (insn); - - /* Don't delete any insns that are part of a libcall block. - Flow or loop might get confused if we did that. Remember - that we are scanning backwards. */ - if (find_reg_note (insn, REG_RETVAL, NULL_RTX)) - in_libcall = 1; - - if (in_libcall) - live_insn = 1; - else if (GET_CODE (PATTERN (insn)) == SET) - { - if (GET_CODE (SET_DEST (PATTERN (insn))) == REG - && SET_DEST (PATTERN (insn)) == SET_SRC (PATTERN (insn))) - ; - -#ifdef HAVE_cc0 - else if (GET_CODE (SET_DEST (PATTERN (insn))) == CC0 - && ! side_effects_p (SET_SRC (PATTERN (insn))) - && ((tem = next_nonnote_insn (insn)) == 0 - || GET_RTX_CLASS (GET_CODE (tem)) != 'i' - || ! reg_referenced_p (cc0_rtx, PATTERN (tem)))) - ; -#endif - else if (GET_CODE (SET_DEST (PATTERN (insn))) != REG - || REGNO (SET_DEST (PATTERN (insn))) < FIRST_PSEUDO_REGISTER - || counts[REGNO (SET_DEST (PATTERN (insn)))] != 0 - || side_effects_p (SET_SRC (PATTERN (insn)))) - live_insn = 1; - } - else if (GET_CODE (PATTERN (insn)) == PARALLEL) - for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) - { - rtx elt = XVECEXP (PATTERN (insn), 0, i); - - if (GET_CODE (elt) == SET) - { - if (GET_CODE (SET_DEST (elt)) == REG - && SET_DEST (elt) == SET_SRC (elt)) - ; - -#ifdef HAVE_cc0 - else if (GET_CODE (SET_DEST (elt)) == CC0 - && ! side_effects_p (SET_SRC (elt)) - && ((tem = next_nonnote_insn (insn)) == 0 - || GET_RTX_CLASS (GET_CODE (tem)) != 'i' - || ! reg_referenced_p (cc0_rtx, PATTERN (tem)))) - ; -#endif - else if (GET_CODE (SET_DEST (elt)) != REG - || REGNO (SET_DEST (elt)) < FIRST_PSEUDO_REGISTER - || counts[REGNO (SET_DEST (elt))] != 0 - || side_effects_p (SET_SRC (elt))) - live_insn = 1; - } - else if (GET_CODE (elt) != CLOBBER && GET_CODE (elt) != USE) - live_insn = 1; - } - else - live_insn = 1; - - /* If this is a dead insn, delete it and show registers in it aren't - being used. */ - - if (! live_insn) - { - count_reg_usage (insn, counts, -1); - delete_insn (insn); - } - - if (find_reg_note (insn, REG_LIBCALL, NULL_RTX)) - in_libcall = 0; - } -} diff --git a/gnu/usr.bin/cc/common/dbxout.c b/gnu/usr.bin/cc/common/dbxout.c deleted file mode 100644 index 4b9f2e8881..0000000000 --- a/gnu/usr.bin/cc/common/dbxout.c +++ /dev/null @@ -1,2437 +0,0 @@ -/* Output dbx-format symbol table information from GNU compiler. - Copyright (C) 1987, 1988, 1992 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* Output dbx-format symbol table data. - This consists of many symbol table entries, each of them - a .stabs assembler pseudo-op with four operands: - a "name" which is really a description of one symbol and its type, - a "code", which is a symbol defined in stab.h whose name starts with N_, - an unused operand always 0, - and a "value" which is an address or an offset. - The name is enclosed in doublequote characters. - - Each function, variable, typedef, and structure tag - has a symbol table entry to define it. - The beginning and end of each level of name scoping within - a function are also marked by special symbol table entries. - - The "name" consists of the symbol name, a colon, a kind-of-symbol letter, - and a data type number. The data type number may be followed by - "=" and a type definition; normally this will happen the first time - the type number is mentioned. The type definition may refer to - other types by number, and those type numbers may be followed - by "=" and nested definitions. - - This can make the "name" quite long. - When a name is more than 80 characters, we split the .stabs pseudo-op - into two .stabs pseudo-ops, both sharing the same "code" and "value". - The first one is marked as continued with a double-backslash at the - end of its "name". - - The kind-of-symbol letter distinguished function names from global - variables from file-scope variables from parameters from auto - variables in memory from typedef names from register variables. - See `dbxout_symbol'. - - The "code" is mostly redundant with the kind-of-symbol letter - that goes in the "name", but not entirely: for symbols located - in static storage, the "code" says which segment the address is in, - which controls how it is relocated. - - The "value" for a symbol in static storage - is the core address of the symbol (actually, the assembler - label for the symbol). For a symbol located in a stack slot - it is the stack offset; for one in a register, the register number. - For a typedef symbol, it is zero. - - If DEBUG_SYMS_TEXT is defined, all debugging symbols must be - output while in the text section. - - For more on data type definitions, see `dbxout_type'. */ - -/* Include these first, because they may define MIN and MAX. */ -#include -#include - -#include "config.h" -#include "tree.h" -#include "rtl.h" -#include "flags.h" -#include "regs.h" -#include "insn-config.h" -#include "reload.h" -#include "defaults.h" -#include "output.h" /* ASM_OUTPUT_SOURCE_LINE may refer to sdb functions. */ - -#ifndef errno -extern int errno; -#endif - -#ifdef XCOFF_DEBUGGING_INFO -#include "xcoffout.h" -#endif - -#ifndef ASM_STABS_OP -#define ASM_STABS_OP ".stabs" -#endif - -#ifndef ASM_STABN_OP -#define ASM_STABN_OP ".stabn" -#endif - -#ifndef DBX_TYPE_DECL_STABS_CODE -#define DBX_TYPE_DECL_STABS_CODE N_LSYM -#endif - -#ifndef DBX_STATIC_CONST_VAR_CODE -#define DBX_STATIC_CONST_VAR_CODE N_FUN -#endif - -#ifndef DBX_REGPARM_STABS_CODE -#define DBX_REGPARM_STABS_CODE N_RSYM -#endif - -#ifndef DBX_REGPARM_STABS_LETTER -#define DBX_REGPARM_STABS_LETTER 'P' -#endif - -#ifndef DBX_MEMPARM_STABS_LETTER -#define DBX_MEMPARM_STABS_LETTER 'p' -#endif - -/* Nonzero means if the type has methods, only output debugging - information if methods are actually written to the asm file. */ - -static int flag_minimal_debug = 1; - -/* Nonzero if we have actually used any of the GDB extensions - to the debugging format. The idea is that we use them for the - first time only if there's a strong reason, but once we have done that, - we use them whenever convenient. */ - -static int have_used_extensions = 0; - -char *getpwd (); - -/* Typical USG systems don't have stab.h, and they also have - no use for DBX-format debugging info. */ - -#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) - -#ifdef DEBUG_SYMS_TEXT -#define FORCE_TEXT text_section (); -#else -#define FORCE_TEXT -#endif - -#if defined (USG) || defined (NO_STAB_H) -#include "gstab.h" /* If doing DBX on sysV, use our own stab.h. */ -#else -#include /* On BSD, use the system's stab.h. */ - -/* This is a GNU extension we need to reference in this file. */ -#ifndef N_CATCH -#define N_CATCH 0x54 -#endif -#endif /* not USG */ - -#ifdef __GNU_STAB__ -#define STAB_CODE_TYPE enum __stab_debug_code -#else -#define STAB_CODE_TYPE int -#endif - -/* 1 if PARM is passed to this function in memory. */ - -#define PARM_PASSED_IN_MEMORY(PARM) \ - (GET_CODE (DECL_INCOMING_RTL (PARM)) == MEM) - -/* A C expression for the integer offset value of an automatic variable - (N_LSYM) having address X (an RTX). */ -#ifndef DEBUGGER_AUTO_OFFSET -#define DEBUGGER_AUTO_OFFSET(X) \ - (GET_CODE (X) == PLUS ? INTVAL (XEXP (X, 1)) : 0) -#endif - -/* A C expression for the integer offset value of an argument (N_PSYM) - having address X (an RTX). The nominal offset is OFFSET. */ -#ifndef DEBUGGER_ARG_OFFSET -#define DEBUGGER_ARG_OFFSET(OFFSET, X) (OFFSET) -#endif - -/* Stream for writing to assembler file. */ - -static FILE *asmfile; - -/* Last source file name mentioned in a NOTE insn. */ - -static char *lastfile; - -/* Current working directory. */ - -static char *cwd; - -enum typestatus {TYPE_UNSEEN, TYPE_XREF, TYPE_DEFINED}; - -/* Vector recording the status of describing C data types. - When we first notice a data type (a tree node), - we assign it a number using next_type_number. - That is its index in this vector. - The vector element says whether we have yet output - the definition of the type. TYPE_XREF says we have - output it as a cross-reference only. */ - -enum typestatus *typevec; - -/* Number of elements of space allocated in `typevec'. */ - -static int typevec_len; - -/* In dbx output, each type gets a unique number. - This is the number for the next type output. - The number, once assigned, is in the TYPE_SYMTAB_ADDRESS field. */ - -static int next_type_number; - -/* In dbx output, we must assign symbol-blocks id numbers - in the order in which their beginnings are encountered. - We output debugging info that refers to the beginning and - end of the ranges of code in each block - with assembler labels LBBn and LBEn, where n is the block number. - The labels are generated in final, which assigns numbers to the - blocks in the same way. */ - -static int next_block_number; - -/* These variables are for dbxout_symbol to communicate to - dbxout_finish_symbol. - current_sym_code is the symbol-type-code, a symbol N_... define in stab.h. - current_sym_value and current_sym_addr are two ways to address the - value to store in the symtab entry. - current_sym_addr if nonzero represents the value as an rtx. - If that is zero, current_sym_value is used. This is used - when the value is an offset (such as for auto variables, - register variables and parms). */ - -static STAB_CODE_TYPE current_sym_code; -static int current_sym_value; -static rtx current_sym_addr; - -/* Number of chars of symbol-description generated so far for the - current symbol. Used by CHARS and CONTIN. */ - -static int current_sym_nchars; - -/* Report having output N chars of the current symbol-description. */ - -#define CHARS(N) (current_sym_nchars += (N)) - -/* Break the current symbol-description, generating a continuation, - if it has become long. */ - -#ifndef DBX_CONTIN_LENGTH -#define DBX_CONTIN_LENGTH 80 -#endif - -#if DBX_CONTIN_LENGTH > 0 -#define CONTIN \ - do {if (current_sym_nchars > DBX_CONTIN_LENGTH) dbxout_continue ();} while (0) -#else -#define CONTIN -#endif - -void dbxout_types (); -void dbxout_args (); -void dbxout_symbol (); -static void dbxout_type_name (); -static void dbxout_type (); -static void dbxout_typedefs (); -static void dbxout_prepare_symbol (); -static void dbxout_finish_symbol (); -static void dbxout_continue (); -static void print_int_cst_octal (); -static void print_octal (); - -#if 0 /* Not clear we will actually need this. */ - -/* Return the absolutized filename for the given relative - filename. Note that if that filename is already absolute, it may - still be returned in a modified form because this routine also - eliminates redundant slashes and single dots and eliminates double - dots to get a shortest possible filename from the given input - filename. The absolutization of relative filenames is made by - assuming that the given filename is to be taken as relative to - the first argument (cwd) or to the current directory if cwd is - NULL. */ - -static char * -abspath (rel_filename) - char *rel_filename; -{ - /* Setup the current working directory as needed. */ - char *abs_buffer - = (char *) alloca (strlen (cwd) + strlen (rel_filename) + 1); - char *endp = abs_buffer; - char *outp, *inp; - char *value; - - /* Copy the filename (possibly preceded by the current working - directory name) into the absolutization buffer. */ - - { - char *src_p; - - if (rel_filename[0] != '/') - { - src_p = cwd; - while (*endp++ = *src_p++) - continue; - *(endp-1) = '/'; /* overwrite null */ - } - src_p = rel_filename; - while (*endp++ = *src_p++) - continue; - if (endp[-1] == '/') - *endp = '\0'; - - /* Now make a copy of abs_buffer into abs_buffer, shortening the - filename (by taking out slashes and dots) as we go. */ - - outp = inp = abs_buffer; - *outp++ = *inp++; /* copy first slash */ - for (;;) - { - if (!inp[0]) - break; - else if (inp[0] == '/' && outp[-1] == '/') - { - inp++; - continue; - } - else if (inp[0] == '.' && outp[-1] == '/') - { - if (!inp[1]) - break; - else if (inp[1] == '/') - { - inp += 2; - continue; - } - else if ((inp[1] == '.') && (inp[2] == 0 || inp[2] == '/')) - { - inp += (inp[2] == '/') ? 3 : 2; - outp -= 2; - while (outp >= abs_buffer && *outp != '/') - outp--; - if (outp < abs_buffer) - { - /* Catch cases like /.. where we try to backup to a - point above the absolute root of the logical file - system. */ - - fprintf (stderr, "%s: invalid file name: %s\n", - pname, rel_filename); - exit (1); - } - *++outp = '\0'; - continue; - } - } - *outp++ = *inp++; - } - - /* On exit, make sure that there is a trailing null, and make sure that - the last character of the returned string is *not* a slash. */ - - *outp = '\0'; - if (outp[-1] == '/') - *--outp = '\0'; - - /* Make a copy (in the heap) of the stuff left in the absolutization - buffer and return a pointer to the copy. */ - - value = (char *) oballoc (strlen (abs_buffer) + 1); - strcpy (value, abs_buffer); - return value; -} -#endif /* 0 */ - -/* At the beginning of compilation, start writing the symbol table. - Initialize `typevec' and output the standard data types of C. */ - -void -dbxout_init (asm_file, input_file_name, syms) - FILE *asm_file; - char *input_file_name; - tree syms; -{ - char ltext_label_name[100]; - - asmfile = asm_file; - - typevec_len = 100; - typevec = (enum typestatus *) xmalloc (typevec_len * sizeof typevec[0]); - bzero (typevec, typevec_len * sizeof typevec[0]); - - /* Convert Ltext into the appropriate format for local labels in case - the system doesn't insert underscores in front of user generated - labels. */ - ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0); - - /* Put the current working directory in an N_SO symbol. */ -#ifndef DBX_WORKING_DIRECTORY /* Only some versions of DBX want this, - but GDB always does. */ - if (use_gnu_debug_info_extensions) -#endif - { - if (cwd || (cwd = getpwd ())) - { -#ifdef DBX_OUTPUT_MAIN_SOURCE_DIRECTORY - DBX_OUTPUT_MAIN_SOURCE_DIRECTORY (asmfile, cwd); -#else /* no DBX_OUTPUT_MAIN_SOURCE_DIRECTORY */ - fprintf (asmfile, "%s \"%s/\",%d,0,0,%s\n", ASM_STABS_OP, - cwd, N_SO, <ext_label_name[1]); -#endif /* no DBX_OUTPUT_MAIN_SOURCE_DIRECTORY */ - } - } - -#ifdef DBX_OUTPUT_MAIN_SOURCE_FILENAME - /* This should NOT be DBX_OUTPUT_SOURCE_FILENAME. That - would give us an N_SOL, and we want an N_SO. */ - DBX_OUTPUT_MAIN_SOURCE_FILENAME (asmfile, input_file_name); -#else /* no DBX_OUTPUT_MAIN_SOURCE_FILENAME */ - /* We include outputting `Ltext:' here, - because that gives you a way to override it. */ - /* Used to put `Ltext:' before the reference, but that loses on sun 4. */ - fprintf (asmfile, "%s \"%s\",%d,0,0,%s\n", ASM_STABS_OP, input_file_name, - N_SO, <ext_label_name[1]); - text_section (); - ASM_OUTPUT_INTERNAL_LABEL (asmfile, "Ltext", 0); -#endif /* no DBX_OUTPUT_MAIN_SOURCE_FILENAME */ - - /* Possibly output something to inform GDB that this compilation was by - GCC. It's easier for GDB to parse it when after the N_SO's. This - is used in Solaris 2. */ -#ifdef ASM_IDENTIFY_GCC_AFTER_SOURCE - ASM_IDENTIFY_GCC_AFTER_SOURCE (asmfile); -#endif - - lastfile = input_file_name; - - next_type_number = 1; - next_block_number = 2; - - /* Make sure that types `int' and `char' have numbers 1 and 2. - Definitions of other integer types will refer to those numbers. - (Actually it should no longer matter what their numbers are. - Also, if any types with tags have been defined, dbxout_symbol - will output them first, so the numbers won't be 1 and 2. That - happens in C++. So it's a good thing it should no longer matter). */ - -#ifdef DBX_OUTPUT_STANDARD_TYPES - DBX_OUTPUT_STANDARD_TYPES (syms); -#else - dbxout_symbol (TYPE_NAME (integer_type_node), 0); - dbxout_symbol (TYPE_NAME (char_type_node), 0); -#endif - - /* Get all permanent types that have typedef names, - and output them all, except for those already output. */ - - dbxout_typedefs (syms); -} - -/* Output any typedef names for types described by TYPE_DECLs in SYMS, - in the reverse order from that which is found in SYMS. */ - -static void -dbxout_typedefs (syms) - tree syms; -{ - if (syms) - { - dbxout_typedefs (TREE_CHAIN (syms)); - if (TREE_CODE (syms) == TYPE_DECL) - { - tree type = TREE_TYPE (syms); - if (TYPE_NAME (type) - && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL - && ! TREE_ASM_WRITTEN (TYPE_NAME (type))) - dbxout_symbol (TYPE_NAME (type), 0); - } - } -} - -/* Output debugging info to FILE to switch to sourcefile FILENAME. */ - -void -dbxout_source_file (file, filename) - FILE *file; - char *filename; -{ - char ltext_label_name[100]; - - if (filename && (lastfile == 0 || strcmp (filename, lastfile))) - { -#ifdef DBX_OUTPUT_SOURCE_FILENAME - DBX_OUTPUT_SOURCE_FILENAME (file, filename); -#else - ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0); - fprintf (file, "%s \"%s\",%d,0,0,%s\n", ASM_STABS_OP, - filename, N_SOL, <ext_label_name[1]); -#endif - lastfile = filename; - } -} - -/* Output a line number symbol entry into output stream FILE, - for source file FILENAME and line number LINENO. */ - -void -dbxout_source_line (file, filename, lineno) - FILE *file; - char *filename; - int lineno; -{ - dbxout_source_file (file, filename); - -#ifdef ASM_OUTPUT_SOURCE_LINE - ASM_OUTPUT_SOURCE_LINE (file, lineno); -#else - fprintf (file, "\t%s %d,0,%d\n", ASM_STABD_OP, N_SLINE, lineno); -#endif -} - -/* At the end of compilation, finish writing the symbol table. - Unless you define DBX_OUTPUT_MAIN_SOURCE_FILE_END, the default is - to do nothing. */ - -void -dbxout_finish (file, filename) - FILE *file; - char *filename; -{ -#ifdef DBX_OUTPUT_MAIN_SOURCE_FILE_END - DBX_OUTPUT_MAIN_SOURCE_FILE_END (file, filename); -#endif /* DBX_OUTPUT_MAIN_SOURCE_FILE_END */ -} - -/* Continue a symbol-description that gets too big. - End one symbol table entry with a double-backslash - and start a new one, eventually producing something like - .stabs "start......\\",code,0,value - .stabs "...rest",code,0,value */ - -static void -dbxout_continue () -{ -#ifdef DBX_CONTIN_CHAR - fprintf (asmfile, "%c", DBX_CONTIN_CHAR); -#else - fprintf (asmfile, "\\\\"); -#endif - dbxout_finish_symbol (NULL_TREE); - fprintf (asmfile, "%s \"", ASM_STABS_OP); - current_sym_nchars = 0; -} - -/* Subroutine of `dbxout_type'. Output the type fields of TYPE. - This must be a separate function because anonymous unions require - recursive calls. */ - -static void -dbxout_type_fields (type) - tree type; -{ - tree tem; - /* Output the name, type, position (in bits), size (in bits) of each - field. */ - for (tem = TYPE_FIELDS (type); tem; tem = TREE_CHAIN (tem)) - { - /* For nameless subunions and subrecords, treat their fields as ours. */ - if (DECL_NAME (tem) == NULL_TREE - && (TREE_CODE (TREE_TYPE (tem)) == UNION_TYPE - || TREE_CODE (TREE_TYPE (tem)) == QUAL_UNION_TYPE - || TREE_CODE (TREE_TYPE (tem)) == RECORD_TYPE)) - dbxout_type_fields (TREE_TYPE (tem)); - /* Omit here local type decls until we know how to support them. */ - else if (TREE_CODE (tem) == TYPE_DECL) - continue; - /* Omit here the nameless fields that are used to skip bits. */ - else if (DECL_NAME (tem) != 0 && TREE_CODE (tem) != CONST_DECL) - { - /* Continue the line if necessary, - but not before the first field. */ - if (tem != TYPE_FIELDS (type)) - CONTIN; - - if (use_gnu_debug_info_extensions - && flag_minimal_debug - && TREE_CODE (tem) == FIELD_DECL - && DECL_VIRTUAL_P (tem) - && DECL_ASSEMBLER_NAME (tem)) - { - have_used_extensions = 1; - CHARS (3 + IDENTIFIER_LENGTH (DECL_NAME (TYPE_NAME (DECL_FCONTEXT (tem))))); - fputs (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (tem)), asmfile); - dbxout_type (DECL_FCONTEXT (tem), 0, 0); - fprintf (asmfile, ":"); - dbxout_type (TREE_TYPE (tem), 0, 0); - fprintf (asmfile, ",%d;", - TREE_INT_CST_LOW (DECL_FIELD_BITPOS (tem))); - continue; - } - - fprintf (asmfile, "%s:", IDENTIFIER_POINTER (DECL_NAME (tem))); - CHARS (2 + IDENTIFIER_LENGTH (DECL_NAME (tem))); - - if (use_gnu_debug_info_extensions - && (TREE_PRIVATE (tem) || TREE_PROTECTED (tem) - || TREE_CODE (tem) != FIELD_DECL)) - { - have_used_extensions = 1; - putc ('/', asmfile); - putc ((TREE_PRIVATE (tem) ? '0' - : TREE_PROTECTED (tem) ? '1' : '2'), - asmfile); - CHARS (2); - } - - dbxout_type ((TREE_CODE (tem) == FIELD_DECL - && DECL_BIT_FIELD_TYPE (tem)) - ? DECL_BIT_FIELD_TYPE (tem) - : TREE_TYPE (tem), 0, 0); - - if (TREE_CODE (tem) == VAR_DECL) - { - if (TREE_STATIC (tem) && use_gnu_debug_info_extensions) - { - char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (tem)); - have_used_extensions = 1; - fprintf (asmfile, ":%s;", name); - CHARS (strlen (name)); - } - else - { - /* If TEM is non-static, GDB won't understand it. */ - fprintf (asmfile, ",0,0;"); - } - } - else if (TREE_CODE (DECL_FIELD_BITPOS (tem)) == INTEGER_CST) - { - fprintf (asmfile, ",%d,%d;", - TREE_INT_CST_LOW (DECL_FIELD_BITPOS (tem)), - TREE_INT_CST_LOW (DECL_SIZE (tem))); - } - else - /* This has yet to be implemented. */ - abort (); - CHARS (23); - } - } -} - -/* Subroutine of `dbxout_type_methods'. Output debug info about the - method described DECL. DEBUG_NAME is an encoding of the method's - type signature. ??? We may be able to do without DEBUG_NAME altogether - now. */ - -static void -dbxout_type_method_1 (decl, debug_name) - tree decl; - char *debug_name; -{ - tree firstarg = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (decl))); - char c1 = 'A', c2; - - if (TREE_CODE (TREE_TYPE (decl)) == FUNCTION_TYPE) - c2 = '?'; - else /* it's a METHOD_TYPE. */ - { - /* A for normal functions. - B for `const' member functions. - C for `volatile' member functions. - D for `const volatile' member functions. */ - if (TYPE_READONLY (TREE_TYPE (firstarg))) - c1 += 1; - if (TYPE_VOLATILE (TREE_TYPE (firstarg))) - c1 += 2; - - if (DECL_VINDEX (decl)) - c2 = '*'; - else - c2 = '.'; - } - - fprintf (asmfile, ":%s;%c%c%c", debug_name, - TREE_PRIVATE (decl) ? '0' : TREE_PROTECTED (decl) ? '1' : '2', c1, c2); - CHARS (IDENTIFIER_LENGTH (DECL_ASSEMBLER_NAME (decl)) + 6 - - (debug_name - IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)))); - if (DECL_VINDEX (decl)) - { - fprintf (asmfile, "%d;", - TREE_INT_CST_LOW (DECL_VINDEX (decl))); - dbxout_type (DECL_CONTEXT (decl), 0, 0); - fprintf (asmfile, ";"); - CHARS (8); - } -} - -/* Subroutine of `dbxout_type'. Output debug info about the methods defined - in TYPE. */ - -static void -dbxout_type_methods (type) - register tree type; -{ - /* C++: put out the method names and their parameter lists */ - tree methods = TYPE_METHODS (type); - tree type_encoding; - register tree fndecl; - register tree last; - char formatted_type_identifier_length[16]; - register int type_identifier_length; - - if (methods == NULL_TREE) - return; - - type_encoding = DECL_NAME (TYPE_NAME (type)); - - /* C++: Template classes break some assumptions made by this code about - the class names, constructor names, and encodings for assembler - label names. For now, disable output of dbx info for them. */ - { - char *ptr = IDENTIFIER_POINTER (type_encoding); - /* This should use index. (mrs) */ - while (*ptr && *ptr != '<') ptr++; - if (*ptr != 0) - { - static int warned; - if (!warned) - { - warned = 1; -#ifdef HAVE_TEMPLATES - if (warn_template_debugging) - warning ("dbx info for template class methods not yet supported"); -#endif - } - return; - } - } - - type_identifier_length = IDENTIFIER_LENGTH (type_encoding); - - sprintf(formatted_type_identifier_length, "%d", type_identifier_length); - - if (TREE_CODE (methods) == FUNCTION_DECL) - fndecl = methods; - else if (TREE_VEC_ELT (methods, 0) != NULL_TREE) - fndecl = TREE_VEC_ELT (methods, 0); - else - fndecl = TREE_VEC_ELT (methods, 1); - - while (fndecl) - { - tree name = DECL_NAME (fndecl); - int need_prefix = 1; - - /* Group together all the methods for the same operation. - These differ in the types of the arguments. */ - for (last = NULL_TREE; - fndecl && (last == NULL_TREE || DECL_NAME (fndecl) == DECL_NAME (last)); - fndecl = TREE_CHAIN (fndecl)) - /* Output the name of the field (after overloading), as - well as the name of the field before overloading, along - with its parameter list */ - { - /* This is the "mangled" name of the method. - It encodes the argument types. */ - char *debug_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (fndecl)); - int destructor = 0; - - CONTIN; - - last = fndecl; - - if (DECL_IGNORED_P (fndecl)) - continue; - - if (flag_minimal_debug) - { - /* Detect ordinary methods because their mangled names - start with the operation name. */ - if (!strncmp (IDENTIFIER_POINTER (name), debug_name, - IDENTIFIER_LENGTH (name))) - { - debug_name += IDENTIFIER_LENGTH (name); - if (debug_name[0] == '_' && debug_name[1] == '_') - { - char *method_name = debug_name + 2; - char *length_ptr = formatted_type_identifier_length; - /* Get past const and volatile qualifiers. */ - while (*method_name == 'C' || *method_name == 'V') - method_name++; - /* Skip digits for length of type_encoding. */ - while (*method_name == *length_ptr && *length_ptr) - length_ptr++, method_name++; - if (! strncmp (method_name, - IDENTIFIER_POINTER (type_encoding), - type_identifier_length)) - method_name += type_identifier_length; - debug_name = method_name; - } - } - /* Detect constructors by their style of name mangling. */ - else if (debug_name[0] == '_' && debug_name[1] == '_') - { - char *ctor_name = debug_name + 2; - char *length_ptr = formatted_type_identifier_length; - while (*ctor_name == 'C' || *ctor_name == 'V') - ctor_name++; - /* Skip digits for length of type_encoding. */ - while (*ctor_name == *length_ptr && *length_ptr) - length_ptr++, ctor_name++; - if (!strncmp (IDENTIFIER_POINTER (type_encoding), ctor_name, - type_identifier_length)) - debug_name = ctor_name + type_identifier_length; - } - /* The other alternative is a destructor. */ - else - destructor = 1; - - /* Output the operation name just once, for the first method - that we output. */ - if (need_prefix) - { - fprintf (asmfile, "%s::", IDENTIFIER_POINTER (name)); - CHARS (IDENTIFIER_LENGTH (name) + 2); - need_prefix = 0; - } - } - - dbxout_type (TREE_TYPE (fndecl), 0, destructor); - - dbxout_type_method_1 (fndecl, debug_name); - } - if (!need_prefix) - { - putc (';', asmfile); - CHARS (1); - } - } -} - -/* Emit a "range" type specification, which has the form: - "r;;;". - TYPE is an INTEGER_TYPE. */ - -static void -dbxout_range_type (type) - tree type; -{ - fprintf (asmfile, "r"); - if (TREE_TYPE (type) && TREE_CODE (TREE_TYPE(type)) != INTEGER_TYPE) - dbxout_type (TREE_TYPE (type), 0, 0); - else - { - /* This used to say `r1' and we used to take care - to make sure that `int' was type number 1. */ - fprintf (asmfile, "%d", TYPE_SYMTAB_ADDRESS (integer_type_node)); - } - if (TREE_CODE (TYPE_MIN_VALUE (type)) == INTEGER_CST) - fprintf (asmfile, ";%d", - TREE_INT_CST_LOW (TYPE_MIN_VALUE (type))); - else - fprintf (asmfile, ";0"); - if (TREE_CODE (TYPE_MAX_VALUE (type)) == INTEGER_CST) - fprintf (asmfile, ";%d;", - TREE_INT_CST_LOW (TYPE_MAX_VALUE (type))); - else - fprintf (asmfile, ";-1;"); -} - -/* Output a reference to a type. If the type has not yet been - described in the dbx output, output its definition now. - For a type already defined, just refer to its definition - using the type number. - - If FULL is nonzero, and the type has been described only with - a forward-reference, output the definition now. - If FULL is zero in this case, just refer to the forward-reference - using the number previously allocated. - - If SHOW_ARG_TYPES is nonzero, we output a description of the argument - types for a METHOD_TYPE. */ - -static void -dbxout_type (type, full, show_arg_types) - tree type; - int full; - int show_arg_types; -{ - register tree tem; - static int anonymous_type_number = 0; - - /* If there was an input error and we don't really have a type, - avoid crashing and write something that is at least valid - by assuming `int'. */ - if (type == error_mark_node) - type = integer_type_node; - else - { - type = TYPE_MAIN_VARIANT (type); - if (TYPE_NAME (type) - && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL - && DECL_IGNORED_P (TYPE_NAME (type))) - full = 0; - } - - if (TYPE_SYMTAB_ADDRESS (type) == 0) - { - /* Type has no dbx number assigned. Assign next available number. */ - TYPE_SYMTAB_ADDRESS (type) = next_type_number++; - - /* Make sure type vector is long enough to record about this type. */ - - if (next_type_number == typevec_len) - { - typevec = (enum typestatus *) xrealloc (typevec, typevec_len * 2 * sizeof typevec[0]); - bzero (typevec + typevec_len, typevec_len * sizeof typevec[0]); - typevec_len *= 2; - } - } - - /* Output the number of this type, to refer to it. */ - fprintf (asmfile, "%d", TYPE_SYMTAB_ADDRESS (type)); - CHARS (3); - -#ifdef DBX_TYPE_DEFINED - if (DBX_TYPE_DEFINED (type)) - return; -#endif - - /* If this type's definition has been output or is now being output, - that is all. */ - - switch (typevec[TYPE_SYMTAB_ADDRESS (type)]) - { - case TYPE_UNSEEN: - break; - case TYPE_XREF: - /* If we have already had a cross reference, - and either that's all we want or that's the best we could do, - don't repeat the cross reference. - Sun dbx crashes if we do. */ - if (! full || TYPE_SIZE (type) == 0 - /* No way in DBX fmt to describe a variable size. */ - || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) - return; - break; - case TYPE_DEFINED: - return; - } - -#ifdef DBX_NO_XREFS - /* For systems where dbx output does not allow the `=xsNAME:' syntax, - leave the type-number completely undefined rather than output - a cross-reference. */ - if (TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE - || TREE_CODE (type) == QUAL_UNION_TYPE - || TREE_CODE (type) == ENUMERAL_TYPE) - - if ((TYPE_NAME (type) != 0 && !full) - || TYPE_SIZE (type) == 0) - { - typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF; - return; - } -#endif - - /* Output a definition now. */ - - fprintf (asmfile, "="); - CHARS (1); - - /* Mark it as defined, so that if it is self-referent - we will not get into an infinite recursion of definitions. */ - - typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_DEFINED; - - switch (TREE_CODE (type)) - { - case VOID_TYPE: - case LANG_TYPE: - /* For a void type, just define it as itself; ie, "5=5". - This makes us consider it defined - without saying what it is. The debugger will make it - a void type when the reference is seen, and nothing will - ever override that default. */ - fprintf (asmfile, "%d", TYPE_SYMTAB_ADDRESS (type)); - CHARS (3); - break; - - case INTEGER_TYPE: - if (type == char_type_node && ! TREE_UNSIGNED (type)) - /* Output the type `char' as a subrange of itself! - I don't understand this definition, just copied it - from the output of pcc. - This used to use `r2' explicitly and we used to - take care to make sure that `char' was type number 2. */ - fprintf (asmfile, "r%d;0;127;", TYPE_SYMTAB_ADDRESS (type)); - else if (use_gnu_debug_info_extensions && TYPE_PRECISION (type) > BITS_PER_WORD) - { - /* This used to say `r1' and we used to take care - to make sure that `int' was type number 1. */ - fprintf (asmfile, "r%d;", TYPE_SYMTAB_ADDRESS (integer_type_node)); - print_int_cst_octal (TYPE_MIN_VALUE (type)); - fprintf (asmfile, ";"); - print_int_cst_octal (TYPE_MAX_VALUE (type)); - fprintf (asmfile, ";"); - } - else /* Output other integer types as subranges of `int'. */ - dbxout_range_type (type); - CHARS (25); - break; - - case REAL_TYPE: - /* This used to say `r1' and we used to take care - to make sure that `int' was type number 1. */ - fprintf (asmfile, "r%d;%d;0;", TYPE_SYMTAB_ADDRESS (integer_type_node), - int_size_in_bytes (type)); - CHARS (16); - break; - - case CHAR_TYPE: - /* Output the type `char' as a subrange of itself. - That is what pcc seems to do. */ - fprintf (asmfile, "r%d;0;%d;", TYPE_SYMTAB_ADDRESS (char_type_node), - TREE_UNSIGNED (type) ? 255 : 127); - CHARS (9); - break; - - case BOOLEAN_TYPE: /* Define as enumeral type (False, True) */ - fprintf (asmfile, "eFalse:0,True:1,;"); - CHARS (17); - break; - - case FILE_TYPE: - putc ('d', asmfile); - CHARS (1); - dbxout_type (TREE_TYPE (type), 0, 0); - break; - - case COMPLEX_TYPE: - /* Differs from the REAL_TYPE by its new data type number */ - - if (TREE_CODE (TREE_TYPE (type)) == REAL_TYPE) - { - fprintf (asmfile, "r%d;%d;0;", - TYPE_SYMTAB_ADDRESS (type), - int_size_in_bytes (TREE_TYPE (type))); - CHARS (15); /* The number is probably incorrect here. */ - } - else - { - /* Output a complex integer type as a structure, - pending some other way to do it. */ - fprintf (asmfile, "s%d", int_size_in_bytes (type)); - - fprintf (asmfile, "real:"); - CHARS (10); - dbxout_type (TREE_TYPE (type), 0, 0); - fprintf (asmfile, ",%d,%d;", - 0, TYPE_PRECISION (TREE_TYPE (type))); - CHARS (8); - fprintf (asmfile, "imag:"); - CHARS (5); - dbxout_type (TREE_TYPE (type), 0, 0); - fprintf (asmfile, ",%d,%d;;", - TYPE_PRECISION (TREE_TYPE (type)), - TYPE_PRECISION (TREE_TYPE (type))); - CHARS (9); - } - break; - - case SET_TYPE: - putc ('S', asmfile); - CHARS (1); - dbxout_type (TREE_TYPE (type), 0, 0); - break; - - case ARRAY_TYPE: - /* Output "a" followed by a range type definition - for the index type of the array - followed by a reference to the target-type. - ar1;0;N;M for a C array of type M and size N+1. */ - tem = TYPE_DOMAIN (type); - if (tem == NULL) - fprintf (asmfile, "ar%d;0;-1;", - TYPE_SYMTAB_ADDRESS (integer_type_node)); - else - { - fprintf (asmfile, "a"); - dbxout_range_type (tem); - } - CHARS (17); - dbxout_type (TREE_TYPE (type), 0, 0); - break; - - case RECORD_TYPE: - case UNION_TYPE: - case QUAL_UNION_TYPE: - { - int i, n_baseclasses = 0; - - if (TYPE_BINFO (type) != 0 && TYPE_BINFO_BASETYPES (type) != 0) - n_baseclasses = TREE_VEC_LENGTH (TYPE_BINFO_BASETYPES (type)); - - /* Output a structure type. */ - if ((TYPE_NAME (type) != 0 - /* Long ago, Tiemann said this creates output that "confuses GDB". - In April 93, mrs@cygnus.com said there is no such problem. - The type decls made automatically by struct specifiers - are marked with DECL_IGNORED_P in C++. */ - && ! (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL - && DECL_IGNORED_P (TYPE_NAME (type))) - && !full) - || TYPE_SIZE (type) == 0 - /* No way in DBX fmt to describe a variable size. */ - || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) - { - /* If the type is just a cross reference, output one - and mark the type as partially described. - If it later becomes defined, we will output - its real definition. - If the type has a name, don't nest its definition within - another type's definition; instead, output an xref - and let the definition come when the name is defined. */ - fprintf (asmfile, (TREE_CODE (type) == RECORD_TYPE) ? "xs" : "xu"); - CHARS (3); -#if 0 /* This assertion is legitimately false in C++. */ - /* We shouldn't be outputting a reference to a type before its - definition unless the type has a tag name. - A typedef name without a tag name should be impossible. */ - if (TREE_CODE (TYPE_NAME (type)) != IDENTIFIER_NODE) - abort (); -#endif - if (TYPE_NAME (type) != 0) - dbxout_type_name (type); - else - fprintf (asmfile, "$$%d", anonymous_type_number++); - fprintf (asmfile, ":"); - typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF; - break; - } - - /* Identify record or union, and print its size. */ - fprintf (asmfile, (TREE_CODE (type) == RECORD_TYPE) ? "s%d" : "u%d", - int_size_in_bytes (type)); - - if (use_gnu_debug_info_extensions) - { - if (n_baseclasses) - { - have_used_extensions = 1; - fprintf (asmfile, "!%d,", n_baseclasses); - CHARS (8); - } - } - for (i = 0; i < n_baseclasses; i++) - { - tree child = TREE_VEC_ELT (BINFO_BASETYPES (TYPE_BINFO (type)), i); - if (use_gnu_debug_info_extensions) - { - have_used_extensions = 1; - putc (TREE_VIA_VIRTUAL (child) ? '1' - : '0', - asmfile); - putc (TREE_VIA_PUBLIC (child) ? '2' - : '0', - asmfile); - fprintf (asmfile, "%d,", - TREE_INT_CST_LOW (BINFO_OFFSET (child)) * BITS_PER_UNIT); - CHARS (15); - dbxout_type (BINFO_TYPE (child), 0, 0); - putc (';', asmfile); - } - else - { - /* Print out the base class information with fields - which have the same names at the types they hold. */ - dbxout_type_name (BINFO_TYPE (child)); - putc (':', asmfile); - dbxout_type (BINFO_TYPE (child), full, 0); - fprintf (asmfile, ",%d,%d;", - TREE_INT_CST_LOW (BINFO_OFFSET (child)) * BITS_PER_UNIT, - TREE_INT_CST_LOW (DECL_SIZE (TYPE_NAME (BINFO_TYPE (child)))) * BITS_PER_UNIT); - CHARS (20); - } - } - } - - CHARS (11); - - /* Write out the field declarations. */ - dbxout_type_fields (type); - if (use_gnu_debug_info_extensions && TYPE_METHODS (type) != NULL_TREE) - { - have_used_extensions = 1; - dbxout_type_methods (type); - } - putc (';', asmfile); - - if (use_gnu_debug_info_extensions && TREE_CODE (type) == RECORD_TYPE - /* Avoid the ~ if we don't really need it--it confuses dbx. */ - && TYPE_VFIELD (type)) - { - have_used_extensions = 1; - - /* Tell GDB+ that it may keep reading. */ - putc ('~', asmfile); - - /* We need to write out info about what field this class - uses as its "main" vtable pointer field, because if this - field is inherited from a base class, GDB cannot necessarily - figure out which field it's using in time. */ - if (TYPE_VFIELD (type)) - { - putc ('%', asmfile); - dbxout_type (DECL_FCONTEXT (TYPE_VFIELD (type)), 0, 0); - } - putc (';', asmfile); - CHARS (3); - } - break; - - case ENUMERAL_TYPE: - if ((TYPE_NAME (type) != 0 && !full - && (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL - && ! DECL_IGNORED_P (TYPE_NAME (type)))) - || TYPE_SIZE (type) == 0) - { - fprintf (asmfile, "xe"); - CHARS (3); - dbxout_type_name (type); - typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF; - fprintf (asmfile, ":"); - return; - } -#ifdef DBX_OUTPUT_ENUM - DBX_OUTPUT_ENUM (asmfile, type); -#else - putc ('e', asmfile); - CHARS (1); - for (tem = TYPE_VALUES (type); tem; tem = TREE_CHAIN (tem)) - { - fprintf (asmfile, "%s:%d,", IDENTIFIER_POINTER (TREE_PURPOSE (tem)), - TREE_INT_CST_LOW (TREE_VALUE (tem))); - CHARS (11 + IDENTIFIER_LENGTH (TREE_PURPOSE (tem))); - if (TREE_CHAIN (tem) != 0) - CONTIN; - } - putc (';', asmfile); - CHARS (1); -#endif - break; - - case POINTER_TYPE: - putc ('*', asmfile); - CHARS (1); - dbxout_type (TREE_TYPE (type), 0, 0); - break; - - case METHOD_TYPE: - if (use_gnu_debug_info_extensions) - { - have_used_extensions = 1; - putc ('#', asmfile); - CHARS (1); - if (flag_minimal_debug && !show_arg_types) - { - /* Normally, just output the return type. - The argument types are encoded in the method name. */ - putc ('#', asmfile); - dbxout_type (TREE_TYPE (type), 0, 0); - putc (';', asmfile); - CHARS (1); - } - else - { - /* When outputting destructors, we need to write - the argument types out longhand. */ - dbxout_type (TYPE_METHOD_BASETYPE (type), 0, 0); - putc (',', asmfile); - CHARS (1); - dbxout_type (TREE_TYPE (type), 0, 0); - dbxout_args (TYPE_ARG_TYPES (type)); - putc (';', asmfile); - CHARS (1); - } - } - else - { - /* Treat it as a function type. */ - dbxout_type (TREE_TYPE (type), 0, 0); - } - break; - - case OFFSET_TYPE: - if (use_gnu_debug_info_extensions) - { - have_used_extensions = 1; - putc ('@', asmfile); - CHARS (1); - dbxout_type (TYPE_OFFSET_BASETYPE (type), 0, 0); - putc (',', asmfile); - CHARS (1); - dbxout_type (TREE_TYPE (type), 0, 0); - } - else - { - /* Should print as an int, because it is really - just an offset. */ - dbxout_type (integer_type_node, 0, 0); - } - break; - - case REFERENCE_TYPE: - if (use_gnu_debug_info_extensions) - have_used_extensions = 1; - putc (use_gnu_debug_info_extensions ? '&' : '*', asmfile); - CHARS (1); - dbxout_type (TREE_TYPE (type), 0, 0); - break; - - case FUNCTION_TYPE: - putc ('f', asmfile); - CHARS (1); - dbxout_type (TREE_TYPE (type), 0, 0); - break; - - default: - abort (); - } -} - -/* Print the value of integer constant C, in octal, - handling double precision. */ - -static void -print_int_cst_octal (c) - tree c; -{ - unsigned HOST_WIDE_INT high = TREE_INT_CST_HIGH (c); - unsigned HOST_WIDE_INT low = TREE_INT_CST_LOW (c); - int excess = (3 - (HOST_BITS_PER_WIDE_INT % 3)); - - fprintf (asmfile, "0"); - - if (excess == 3) - { - print_octal (high, HOST_BITS_PER_WIDE_INT / 3); - print_octal (low, HOST_BITS_PER_WIDE_INT / 3); - } - else - { - unsigned HOST_WIDE_INT beg = high >> excess; - unsigned HOST_WIDE_INT middle - = ((high & (((HOST_WIDE_INT) 1 << excess) - 1)) << (3 - excess) - | (low >> (HOST_BITS_PER_WIDE_INT / 3 * 3))); - unsigned HOST_WIDE_INT end - = low & (((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 3 * 3)) - 1); - fprintf (asmfile, "%o%01o", beg, middle); - print_octal (end, HOST_BITS_PER_WIDE_INT / 3); - } -} - -static void -print_octal (value, digits) - unsigned HOST_WIDE_INT value; - int digits; -{ - int i; - - for (i = digits - 1; i >= 0; i--) - fprintf (asmfile, "%01o", ((value >> (3 * i)) & 7)); -} - -/* Output the name of type TYPE, with no punctuation. - Such names can be set up either by typedef declarations - or by struct, enum and union tags. */ - -static void -dbxout_type_name (type) - register tree type; -{ - tree t; - if (TYPE_NAME (type) == 0) - abort (); - if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) - { - t = TYPE_NAME (type); - } - else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL) - { - t = DECL_NAME (TYPE_NAME (type)); - } - else - abort (); - - fprintf (asmfile, "%s", IDENTIFIER_POINTER (t)); - CHARS (IDENTIFIER_LENGTH (t)); -} - -/* Output a .stabs for the symbol defined by DECL, - which must be a ..._DECL node in the normal namespace. - It may be a CONST_DECL, a FUNCTION_DECL, a PARM_DECL or a VAR_DECL. - LOCAL is nonzero if the scope is less than the entire file. */ - -void -dbxout_symbol (decl, local) - tree decl; - int local; -{ - int letter = 0; - tree type = TREE_TYPE (decl); - tree context = NULL_TREE; - int regno = -1; - - /* Cast avoids warning in old compilers. */ - current_sym_code = (STAB_CODE_TYPE) 0; - current_sym_value = 0; - current_sym_addr = 0; - - /* Ignore nameless syms, but don't ignore type tags. */ - - if ((DECL_NAME (decl) == 0 && TREE_CODE (decl) != TYPE_DECL) - || DECL_IGNORED_P (decl)) - return; - - dbxout_prepare_symbol (decl); - - /* The output will always start with the symbol name, - so always count that in the length-output-so-far. */ - - if (DECL_NAME (decl) != 0) - current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (decl)); - - switch (TREE_CODE (decl)) - { - case CONST_DECL: - /* Enum values are defined by defining the enum type. */ - break; - - case FUNCTION_DECL: - if (DECL_RTL (decl) == 0) - return; - if (DECL_EXTERNAL (decl)) - break; - /* Don't mention a nested function under its parent. */ - context = decl_function_context (decl); - if (context == current_function_decl) - break; - if (GET_CODE (DECL_RTL (decl)) != MEM - || GET_CODE (XEXP (DECL_RTL (decl), 0)) != SYMBOL_REF) - break; - FORCE_TEXT; - - fprintf (asmfile, "%s \"%s:%c", ASM_STABS_OP, - IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), - TREE_PUBLIC (decl) ? 'F' : 'f'); - - current_sym_code = N_FUN; - current_sym_addr = XEXP (DECL_RTL (decl), 0); - - if (TREE_TYPE (type)) - dbxout_type (TREE_TYPE (type), 0, 0); - else - dbxout_type (void_type_node, 0, 0); - - /* For a nested function, when that function is compiled, - mention the containing function name - as well as (since dbx wants it) our own assembler-name. */ - if (context != 0) - fprintf (asmfile, ",%s,%s", - IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), - IDENTIFIER_POINTER (DECL_NAME (context))); - - dbxout_finish_symbol (decl); - break; - - case TYPE_DECL: -#if 0 - /* This seems all wrong. Outputting most kinds of types gives no name - at all. A true definition gives no name; a cross-ref for a - structure can give the tag name, but not a type name. - It seems that no typedef name is defined by outputting a type. */ - - /* If this typedef name was defined by outputting the type, - don't duplicate it. */ - if (typevec[TYPE_SYMTAB_ADDRESS (type)] == TYPE_DEFINED - && TYPE_NAME (TREE_TYPE (decl)) == decl) - return; -#endif - /* Don't output the same typedef twice. - And don't output what language-specific stuff doesn't want output. */ - if (TREE_ASM_WRITTEN (decl) || DECL_IGNORED_P (decl)) - return; - - FORCE_TEXT; - - { - int tag_needed = 1; - int did_output = 0; - - if (DECL_NAME (decl)) - { - /* Nonzero means we must output a tag as well as a typedef. */ - tag_needed = 0; - - /* Handle the case of a C++ structure or union - where the TYPE_NAME is a TYPE_DECL - which gives both a typedef name and a tag. */ - /* dbx requires the tag first and the typedef second. */ - if ((TREE_CODE (type) == RECORD_TYPE - || TREE_CODE (type) == UNION_TYPE - || TREE_CODE (type) == QUAL_UNION_TYPE) - && TYPE_NAME (type) == decl - && !(use_gnu_debug_info_extensions && have_used_extensions) - && !TREE_ASM_WRITTEN (TYPE_NAME (type)) - /* Distinguish the implicit typedefs of C++ - from explicit ones that might be found in C. */ - && DECL_SOURCE_LINE (decl) == 0) - { - tree name = TYPE_NAME (type); - if (TREE_CODE (name) == TYPE_DECL) - name = DECL_NAME (name); - - current_sym_code = DBX_TYPE_DECL_STABS_CODE; - current_sym_value = 0; - current_sym_addr = 0; - current_sym_nchars = 2 + IDENTIFIER_LENGTH (name); - - fprintf (asmfile, "%s \"%s:T", ASM_STABS_OP, - IDENTIFIER_POINTER (name)); - dbxout_type (type, 1, 0); - dbxout_finish_symbol (NULL_TREE); - } - - /* Output typedef name. */ - fprintf (asmfile, "%s \"%s:", ASM_STABS_OP, - IDENTIFIER_POINTER (DECL_NAME (decl))); - - /* Short cut way to output a tag also. */ - if ((TREE_CODE (type) == RECORD_TYPE - || TREE_CODE (type) == UNION_TYPE - || TREE_CODE (type) == QUAL_UNION_TYPE) - && TYPE_NAME (type) == decl) - { - if (use_gnu_debug_info_extensions && have_used_extensions) - { - putc ('T', asmfile); - TREE_ASM_WRITTEN (TYPE_NAME (type)) = 1; - } -#if 0 /* Now we generate the tag for this case up above. */ - else - tag_needed = 1; -#endif - } - - putc ('t', asmfile); - current_sym_code = DBX_TYPE_DECL_STABS_CODE; - - dbxout_type (type, 1, 0); - dbxout_finish_symbol (decl); - did_output = 1; - } - - /* Don't output a tag if this is an incomplete type (TYPE_SIZE is - zero). This prevents the sun4 Sun OS 4.x dbx from crashing. */ - - if (tag_needed && TYPE_NAME (type) != 0 && TYPE_SIZE (type) != 0 - && !TREE_ASM_WRITTEN (TYPE_NAME (type))) - { - /* For a TYPE_DECL with no name, but the type has a name, - output a tag. - This is what represents `struct foo' with no typedef. */ - /* In C++, the name of a type is the corresponding typedef. - In C, it is an IDENTIFIER_NODE. */ - tree name = TYPE_NAME (type); - if (TREE_CODE (name) == TYPE_DECL) - name = DECL_NAME (name); - - current_sym_code = DBX_TYPE_DECL_STABS_CODE; - current_sym_value = 0; - current_sym_addr = 0; - current_sym_nchars = 2 + IDENTIFIER_LENGTH (name); - - fprintf (asmfile, "%s \"%s:T", ASM_STABS_OP, - IDENTIFIER_POINTER (name)); - dbxout_type (type, 1, 0); - dbxout_finish_symbol (NULL_TREE); - did_output = 1; - } - - /* If an enum type has no name, it cannot be referred to, - but we must output it anyway, since the enumeration constants - can be referred to. */ - if (!did_output && TREE_CODE (type) == ENUMERAL_TYPE) - { - current_sym_code = DBX_TYPE_DECL_STABS_CODE; - current_sym_value = 0; - current_sym_addr = 0; - current_sym_nchars = 2; - - /* Some debuggers fail when given NULL names, so give this a - harmless name of ` '. */ - fprintf (asmfile, "%s \" :T", ASM_STABS_OP); - dbxout_type (type, 1, 0); - dbxout_finish_symbol (NULL_TREE); - } - - /* Prevent duplicate output of a typedef. */ - TREE_ASM_WRITTEN (decl) = 1; - break; - } - - case PARM_DECL: - /* Parm decls go in their own separate chains - and are output by dbxout_reg_parms and dbxout_parms. */ - abort (); - - case RESULT_DECL: - /* Named return value, treat like a VAR_DECL. */ - case VAR_DECL: - if (DECL_RTL (decl) == 0) - return; - /* Don't mention a variable that is external. - Let the file that defines it describe it. */ - if (DECL_EXTERNAL (decl)) - break; - - /* If the variable is really a constant - and not written in memory, inform the debugger. */ - if (TREE_STATIC (decl) && TREE_READONLY (decl) - && DECL_INITIAL (decl) != 0 - && ! TREE_ASM_WRITTEN (decl) - && (DECL_FIELD_CONTEXT (decl) == NULL_TREE - || TREE_CODE (DECL_FIELD_CONTEXT (decl)) == BLOCK)) - { - if (TREE_PUBLIC (decl) == 0) - { - /* The sun4 assembler does not grok this. */ - char *name = IDENTIFIER_POINTER (DECL_NAME (decl)); - if (TREE_CODE (TREE_TYPE (decl)) == INTEGER_TYPE - || TREE_CODE (TREE_TYPE (decl)) == ENUMERAL_TYPE) - { - HOST_WIDE_INT ival = TREE_INT_CST_LOW (DECL_INITIAL (decl)); -#ifdef DBX_OUTPUT_CONSTANT_SYMBOL - DBX_OUTPUT_CONSTANT_SYMBOL (asmfile, name, ival); -#else - fprintf (asmfile, "%s \"%s:c=i%d\",0x%x,0,0,0\n", - ASM_STABS_OP, name, ival, N_LSYM); -#endif - return; - } - else if (TREE_CODE (TREE_TYPE (decl)) == REAL_TYPE) - { - /* don't know how to do this yet. */ - } - break; - } - /* else it is something we handle like a normal variable. */ - } - - DECL_RTL (decl) = eliminate_regs (DECL_RTL (decl), 0, NULL_RTX); -#ifdef LEAF_REG_REMAP - if (leaf_function) - leaf_renumber_regs_insn (DECL_RTL (decl)); -#endif - - /* Don't mention a variable at all - if it was completely optimized into nothingness. - - If DECL was from an inline function, then it's rtl - is not identically the rtl that was used in this - particular compilation. */ - if (GET_CODE (DECL_RTL (decl)) == REG) - { - regno = REGNO (DECL_RTL (decl)); - if (regno >= FIRST_PSEUDO_REGISTER) - return; - } - else if (GET_CODE (DECL_RTL (decl)) == SUBREG) - { - rtx value = DECL_RTL (decl); - int offset = 0; - while (GET_CODE (value) == SUBREG) - { - offset += SUBREG_WORD (value); - value = SUBREG_REG (value); - } - if (GET_CODE (value) == REG) - { - regno = REGNO (value); - if (regno >= FIRST_PSEUDO_REGISTER) - return; - regno += offset; - } - alter_subreg (DECL_RTL (decl)); - } - - /* The kind-of-variable letter depends on where - the variable is and on the scope of its name: - G and N_GSYM for static storage and global scope, - S for static storage and file scope, - V for static storage and local scope, - for those two, use N_LCSYM if data is in bss segment, - N_STSYM if in data segment, N_FUN otherwise. - (We used N_FUN originally, then changed to N_STSYM - to please GDB. However, it seems that confused ld. - Now GDB has been fixed to like N_FUN, says Kingdon.) - no letter at all, and N_LSYM, for auto variable, - r and N_RSYM for register variable. */ - - if (GET_CODE (DECL_RTL (decl)) == MEM - && GET_CODE (XEXP (DECL_RTL (decl), 0)) == SYMBOL_REF) - { - if (TREE_PUBLIC (decl)) - { - letter = 'G'; - current_sym_code = N_GSYM; - } - else - { - current_sym_addr = XEXP (DECL_RTL (decl), 0); - - letter = decl_function_context (decl) ? 'V' : 'S'; - - if (!DECL_INITIAL (decl)) - current_sym_code = N_LCSYM; - else if (TREE_READONLY (decl) && ! TREE_THIS_VOLATILE (decl)) - /* This is not quite right, but it's the closest - of all the codes that Unix defines. */ - current_sym_code = DBX_STATIC_CONST_VAR_CODE; - else - { -/* Ultrix `as' seems to need this. */ -#ifdef DBX_STATIC_STAB_DATA_SECTION - data_section (); -#endif - current_sym_code = N_STSYM; - } - } - } - else if (regno >= 0) - { - letter = 'r'; - current_sym_code = N_RSYM; - current_sym_value = DBX_REGISTER_NUMBER (regno); - } - else if (GET_CODE (DECL_RTL (decl)) == MEM - && (GET_CODE (XEXP (DECL_RTL (decl), 0)) == MEM - || (GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG - && REGNO (XEXP (DECL_RTL (decl), 0)) != FRAME_POINTER_REGNUM))) - /* If the value is indirect by memory or by a register - that isn't the frame pointer - then it means the object is variable-sized and address through - that register or stack slot. DBX has no way to represent this - so all we can do is output the variable as a pointer. - If it's not a parameter, ignore it. - (VAR_DECLs like this can be made by integrate.c.) */ - { - if (GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG) - { - letter = 'r'; - current_sym_code = N_RSYM; - current_sym_value = DBX_REGISTER_NUMBER (REGNO (XEXP (DECL_RTL (decl), 0))); - } - else - { - current_sym_code = N_LSYM; - /* DECL_RTL looks like (MEM (MEM (PLUS (REG...) (CONST_INT...)))). - We want the value of that CONST_INT. */ - current_sym_value - = DEBUGGER_AUTO_OFFSET (XEXP (XEXP (DECL_RTL (decl), 0), 0)); - } - - /* Effectively do build_pointer_type, but don't cache this type, - since it might be temporary whereas the type it points to - might have been saved for inlining. */ - /* Don't use REFERENCE_TYPE because dbx can't handle that. */ - type = make_node (POINTER_TYPE); - TREE_TYPE (type) = TREE_TYPE (decl); - } - else if (GET_CODE (DECL_RTL (decl)) == MEM - && GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG) - { - current_sym_code = N_LSYM; - current_sym_value = DEBUGGER_AUTO_OFFSET (XEXP (DECL_RTL (decl), 0)); - } - else if (GET_CODE (DECL_RTL (decl)) == MEM - && GET_CODE (XEXP (DECL_RTL (decl), 0)) == PLUS - && GET_CODE (XEXP (XEXP (DECL_RTL (decl), 0), 1)) == CONST_INT) - { - current_sym_code = N_LSYM; - /* DECL_RTL looks like (MEM (PLUS (REG...) (CONST_INT...))) - We want the value of that CONST_INT. */ - current_sym_value = DEBUGGER_AUTO_OFFSET (XEXP (DECL_RTL (decl), 0)); - } - else if (GET_CODE (DECL_RTL (decl)) == MEM - && GET_CODE (XEXP (DECL_RTL (decl), 0)) == CONST) - { - /* Handle an obscure case which can arise when optimizing and - when there are few available registers. (This is *always* - the case for i386/i486 targets). The DECL_RTL looks like - (MEM (CONST ...)) even though this variable is a local `auto' - or a local `register' variable. In effect, what has happened - is that the reload pass has seen that all assignments and - references for one such a local variable can be replaced by - equivalent assignments and references to some static storage - variable, thereby avoiding the need for a register. In such - cases we're forced to lie to debuggers and tell them that - this variable was itself `static'. */ - current_sym_code = N_LCSYM; - letter = 'V'; - current_sym_addr = XEXP (XEXP (DECL_RTL (decl), 0), 0); - } - else - /* Address might be a MEM, when DECL is a variable-sized object. - Or it might be const0_rtx, meaning previous passes - want us to ignore this variable. */ - break; - - /* Ok, start a symtab entry and output the variable name. */ - FORCE_TEXT; - -#ifdef DBX_STATIC_BLOCK_START - DBX_STATIC_BLOCK_START (asmfile, current_sym_code); -#endif - - /* One slight hitch: if this is a VAR_DECL which is a static - class member, we must put out the mangled name instead of the - DECL_NAME. */ - { - char *name; - /* Note also that static member (variable) names DO NOT begin - with underscores in .stabs directives. */ - if (DECL_LANG_SPECIFIC (decl)) - name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); - else - name = IDENTIFIER_POINTER (DECL_NAME (decl)); - fprintf (asmfile, "%s \"%s:", ASM_STABS_OP, name); - } - if (letter) putc (letter, asmfile); - dbxout_type (type, 0, 0); - dbxout_finish_symbol (decl); - -#ifdef DBX_STATIC_BLOCK_END - DBX_STATIC_BLOCK_END (asmfile, current_sym_code); -#endif - break; - } -} - -static void -dbxout_prepare_symbol (decl) - tree decl; -{ -#ifdef WINNING_GDB - char *filename = DECL_SOURCE_FILE (decl); - - dbxout_source_file (asmfile, filename); -#endif -} - -static void -dbxout_finish_symbol (sym) - tree sym; -{ -#ifdef DBX_FINISH_SYMBOL - DBX_FINISH_SYMBOL (sym); -#else - int line = 0; - if (use_gnu_debug_info_extensions && sym != 0) - line = DECL_SOURCE_LINE (sym); - - fprintf (asmfile, "\",%d,0,%d,", current_sym_code, line); - if (current_sym_addr) - output_addr_const (asmfile, current_sym_addr); - else - fprintf (asmfile, "%d", current_sym_value); - putc ('\n', asmfile); -#endif -} - -/* Output definitions of all the decls in a chain. */ - -void -dbxout_syms (syms) - tree syms; -{ - while (syms) - { - dbxout_symbol (syms, 1); - syms = TREE_CHAIN (syms); - } -} - -/* The following two functions output definitions of function parameters. - Each parameter gets a definition locating it in the parameter list. - Each parameter that is a register variable gets a second definition - locating it in the register. - - Printing or argument lists in gdb uses the definitions that - locate in the parameter list. But reference to the variable in - expressions uses preferentially the definition as a register. */ - -/* Output definitions, referring to storage in the parmlist, - of all the parms in PARMS, which is a chain of PARM_DECL nodes. */ - -void -dbxout_parms (parms) - tree parms; -{ - for (; parms; parms = TREE_CHAIN (parms)) - if (DECL_NAME (parms) && TREE_TYPE (parms) != error_mark_node) - { - dbxout_prepare_symbol (parms); - - /* Perform any necessary register eliminations on the parameter's rtl, - so that the debugging output will be accurate. */ - DECL_INCOMING_RTL (parms) - = eliminate_regs (DECL_INCOMING_RTL (parms), 0, NULL_RTX); - DECL_RTL (parms) = eliminate_regs (DECL_RTL (parms), 0, NULL_RTX); -#ifdef LEAF_REG_REMAP - if (leaf_function) - { - leaf_renumber_regs_insn (DECL_INCOMING_RTL (parms)); - leaf_renumber_regs_insn (DECL_RTL (parms)); - } -#endif - - if (PARM_PASSED_IN_MEMORY (parms)) - { - rtx addr = XEXP (DECL_INCOMING_RTL (parms), 0); - - /* ??? Here we assume that the parm address is indexed - off the frame pointer or arg pointer. - If that is not true, we produce meaningless results, - but do not crash. */ - if (GET_CODE (addr) == PLUS - && GET_CODE (XEXP (addr, 1)) == CONST_INT) - current_sym_value = INTVAL (XEXP (addr, 1)); - else - current_sym_value = 0; - - current_sym_code = N_PSYM; - current_sym_addr = 0; - - FORCE_TEXT; - if (DECL_NAME (parms)) - { - current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms)); - - fprintf (asmfile, "%s \"%s:%c", ASM_STABS_OP, - IDENTIFIER_POINTER (DECL_NAME (parms)), - DBX_MEMPARM_STABS_LETTER); - } - else - { - current_sym_nchars = 8; - fprintf (asmfile, "%s \"(anon):%c", ASM_STABS_OP, - DBX_MEMPARM_STABS_LETTER); - } - - if (GET_CODE (DECL_RTL (parms)) == REG - && REGNO (DECL_RTL (parms)) >= 0 - && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER) - dbxout_type (DECL_ARG_TYPE (parms), 0, 0); - else - { - int original_value = current_sym_value; - - /* This is the case where the parm is passed as an int or double - and it is converted to a char, short or float and stored back - in the parmlist. In this case, describe the parm - with the variable's declared type, and adjust the address - if the least significant bytes (which we are using) are not - the first ones. */ -#if BYTES_BIG_ENDIAN - if (TREE_TYPE (parms) != DECL_ARG_TYPE (parms)) - current_sym_value += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms))) - - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms)))); -#endif - - if (GET_CODE (DECL_RTL (parms)) == MEM - && GET_CODE (XEXP (DECL_RTL (parms), 0)) == PLUS - && GET_CODE (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == CONST_INT - && INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == current_sym_value) - dbxout_type (TREE_TYPE (parms), 0, 0); - else - { - current_sym_value = original_value; - dbxout_type (DECL_ARG_TYPE (parms), 0, 0); - } - } - current_sym_value = DEBUGGER_ARG_OFFSET (current_sym_value, addr); - dbxout_finish_symbol (parms); - } - else if (GET_CODE (DECL_RTL (parms)) == REG) - { - rtx best_rtl; - char regparm_letter; - /* Parm passed in registers and lives in registers or nowhere. */ - - current_sym_code = DBX_REGPARM_STABS_CODE; - regparm_letter = DBX_REGPARM_STABS_LETTER; - current_sym_addr = 0; - - /* If parm lives in a register, use that register; - pretend the parm was passed there. It would be more consistent - to describe the register where the parm was passed, - but in practice that register usually holds something else. */ - if (REGNO (DECL_RTL (parms)) >= 0 - && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER) - best_rtl = DECL_RTL (parms); - /* If the parm lives nowhere, - use the register where it was passed. */ - else - best_rtl = DECL_INCOMING_RTL (parms); - current_sym_value = DBX_REGISTER_NUMBER (REGNO (best_rtl)); - - FORCE_TEXT; - if (DECL_NAME (parms)) - { - current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms)); - fprintf (asmfile, "%s \"%s:%c", ASM_STABS_OP, - IDENTIFIER_POINTER (DECL_NAME (parms)), - regparm_letter); - } - else - { - current_sym_nchars = 8; - fprintf (asmfile, "%s \"(anon):%c", ASM_STABS_OP, - regparm_letter); - } - - dbxout_type (DECL_ARG_TYPE (parms), 0, 0); - dbxout_finish_symbol (parms); - } - else if (GET_CODE (DECL_RTL (parms)) == MEM - && GET_CODE (XEXP (DECL_RTL (parms), 0)) == REG) -/* && rtx_equal_p (XEXP (DECL_RTL (parms), 0), - DECL_INCOMING_RTL (parms))) */ - { - /* Parm was passed via invisible reference. - That is, its address was passed in a register. - Output it as if it lived in that register. - The debugger will know from the type - that it was actually passed by invisible reference. */ - - char regparm_letter; - /* Parm passed in registers and lives in registers or nowhere. */ - - current_sym_code = DBX_REGPARM_STABS_CODE; - regparm_letter = DBX_REGPARM_STABS_LETTER; - - /* DECL_RTL looks like (MEM (REG...). Get the register number. */ - current_sym_value = REGNO (XEXP (DECL_RTL (parms), 0)); - current_sym_addr = 0; - - FORCE_TEXT; - if (DECL_NAME (parms)) - { - current_sym_nchars = 2 + strlen (IDENTIFIER_POINTER (DECL_NAME (parms))); - - fprintf (asmfile, "%s \"%s:%c", ASM_STABS_OP, - IDENTIFIER_POINTER (DECL_NAME (parms)), - DBX_REGPARM_STABS_LETTER); - } - else - { - current_sym_nchars = 8; - fprintf (asmfile, "%s \"(anon):%c", ASM_STABS_OP, - DBX_REGPARM_STABS_LETTER); - } - - dbxout_type (TREE_TYPE (parms), 0, 0); - dbxout_finish_symbol (parms); - } - else if (GET_CODE (DECL_RTL (parms)) == MEM - && XEXP (DECL_RTL (parms), 0) != const0_rtx - /* ??? A constant address for a parm can happen - when the reg it lives in is equiv to a constant in memory. - Should make this not happen, after 2.4. */ - && ! CONSTANT_P (XEXP (DECL_RTL (parms), 0))) - { - /* Parm was passed in registers but lives on the stack. */ - - current_sym_code = N_PSYM; - /* DECL_RTL looks like (MEM (PLUS (REG...) (CONST_INT...))), - in which case we want the value of that CONST_INT, - or (MEM (REG ...)) or (MEM (MEM ...)), - in which case we use a value of zero. */ - if (GET_CODE (XEXP (DECL_RTL (parms), 0)) == REG - || GET_CODE (XEXP (DECL_RTL (parms), 0)) == MEM) - current_sym_value = 0; - else - current_sym_value = INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)); - current_sym_addr = 0; - - FORCE_TEXT; - if (DECL_NAME (parms)) - { - current_sym_nchars = 2 + strlen (IDENTIFIER_POINTER (DECL_NAME (parms))); - - fprintf (asmfile, "%s \"%s:%c", ASM_STABS_OP, - IDENTIFIER_POINTER (DECL_NAME (parms)), - DBX_MEMPARM_STABS_LETTER); - } - else - { - current_sym_nchars = 8; - fprintf (asmfile, "%s \"(anon):%c", ASM_STABS_OP, - DBX_MEMPARM_STABS_LETTER); - } - - current_sym_value - = DEBUGGER_ARG_OFFSET (current_sym_value, - XEXP (DECL_RTL (parms), 0)); - dbxout_type (TREE_TYPE (parms), 0, 0); - dbxout_finish_symbol (parms); - } - } -} - -/* Output definitions for the places where parms live during the function, - when different from where they were passed, when the parms were passed - in memory. - - It is not useful to do this for parms passed in registers - that live during the function in different registers, because it is - impossible to look in the passed register for the passed value, - so we use the within-the-function register to begin with. - - PARMS is a chain of PARM_DECL nodes. */ - -void -dbxout_reg_parms (parms) - tree parms; -{ - for (; parms; parms = TREE_CHAIN (parms)) - if (DECL_NAME (parms)) - { - dbxout_prepare_symbol (parms); - - /* Report parms that live in registers during the function - but were passed in memory. */ - if (GET_CODE (DECL_RTL (parms)) == REG - && REGNO (DECL_RTL (parms)) >= 0 - && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER - && PARM_PASSED_IN_MEMORY (parms)) - { - current_sym_code = N_RSYM; - current_sym_value = DBX_REGISTER_NUMBER (REGNO (DECL_RTL (parms))); - current_sym_addr = 0; - - FORCE_TEXT; - if (DECL_NAME (parms)) - { - current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms)); - fprintf (asmfile, "%s \"%s:r", ASM_STABS_OP, - IDENTIFIER_POINTER (DECL_NAME (parms))); - } - else - { - current_sym_nchars = 8; - fprintf (asmfile, "%s \"(anon):r", ASM_STABS_OP); - } - dbxout_type (TREE_TYPE (parms), 0, 0); - dbxout_finish_symbol (parms); - } - /* Report parms that live in memory but not where they were passed. */ - else if (GET_CODE (DECL_RTL (parms)) == MEM - && GET_CODE (XEXP (DECL_RTL (parms), 0)) == PLUS - && GET_CODE (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == CONST_INT - && PARM_PASSED_IN_MEMORY (parms) - && ! rtx_equal_p (DECL_RTL (parms), DECL_INCOMING_RTL (parms))) - { -#if 0 /* ??? It is not clear yet what should replace this. */ - int offset = DECL_OFFSET (parms) / BITS_PER_UNIT; - /* A parm declared char is really passed as an int, - so it occupies the least significant bytes. - On a big-endian machine those are not the low-numbered ones. */ -#if BYTES_BIG_ENDIAN - if (offset != -1 && TREE_TYPE (parms) != DECL_ARG_TYPE (parms)) - offset += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms))) - - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms)))); -#endif - if (INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)) != offset) {...} -#endif - current_sym_code = N_LSYM; - current_sym_value = DEBUGGER_AUTO_OFFSET (XEXP (DECL_RTL (parms), 0)); - current_sym_addr = 0; - FORCE_TEXT; - if (DECL_NAME (parms)) - { - current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms)); - fprintf (asmfile, "%s \"%s:", ASM_STABS_OP, - IDENTIFIER_POINTER (DECL_NAME (parms))); - } - else - { - current_sym_nchars = 8; - fprintf (asmfile, "%s \"(anon):", ASM_STABS_OP); - } - dbxout_type (TREE_TYPE (parms), 0, 0); - dbxout_finish_symbol (parms); - } -#if 0 - else if (GET_CODE (DECL_RTL (parms)) == MEM - && GET_CODE (XEXP (DECL_RTL (parms), 0)) == REG) - { - /* Parm was passed via invisible reference. - That is, its address was passed in a register. - Output it as if it lived in that register. - The debugger will know from the type - that it was actually passed by invisible reference. */ - - current_sym_code = N_RSYM; - - /* DECL_RTL looks like (MEM (REG...). Get the register number. */ - current_sym_value = REGNO (XEXP (DECL_RTL (parms), 0)); - current_sym_addr = 0; - - FORCE_TEXT; - if (DECL_NAME (parms)) - { - current_sym_nchars = 2 + strlen (IDENTIFIER_POINTER (DECL_NAME (parms))); - - fprintf (asmfile, "%s \"%s:r", ASM_STABS_OP, - IDENTIFIER_POINTER (DECL_NAME (parms))); - } - else - { - current_sym_nchars = 8; - fprintf (asmfile, "%s \"(anon):r", ASM_STABS_OP); - } - - dbxout_type (TREE_TYPE (parms), 0, 0); - dbxout_finish_symbol (parms); - } -#endif - } -} - -/* Given a chain of ..._TYPE nodes (as come in a parameter list), - output definitions of those names, in raw form */ - -void -dbxout_args (args) - tree args; -{ - while (args) - { - putc (',', asmfile); - dbxout_type (TREE_VALUE (args), 0, 0); - CHARS (1); - args = TREE_CHAIN (args); - } -} - -/* Given a chain of ..._TYPE nodes, - find those which have typedef names and output those names. - This is to ensure those types get output. */ - -void -dbxout_types (types) - register tree types; -{ - while (types) - { - if (TYPE_NAME (types) - && TREE_CODE (TYPE_NAME (types)) == TYPE_DECL - && ! TREE_ASM_WRITTEN (TYPE_NAME (types))) - dbxout_symbol (TYPE_NAME (types), 1); - types = TREE_CHAIN (types); - } -} - -/* Output everything about a symbol block (a BLOCK node - that represents a scope level), - including recursive output of contained blocks. - - BLOCK is the BLOCK node. - DEPTH is its depth within containing symbol blocks. - ARGS is usually zero; but for the outermost block of the - body of a function, it is a chain of PARM_DECLs for the function parameters. - We output definitions of all the register parms - as if they were local variables of that block. - - If -g1 was used, we count blocks just the same, but output nothing - except for the outermost block. - - Actually, BLOCK may be several blocks chained together. - We handle them all in sequence. */ - -static void -dbxout_block (block, depth, args) - register tree block; - int depth; - tree args; -{ - int blocknum; - - while (block) - { - /* Ignore blocks never expanded or otherwise marked as real. */ - if (TREE_USED (block)) - { -#ifndef DBX_LBRAC_FIRST - /* In dbx format, the syms of a block come before the N_LBRAC. */ - if (debug_info_level != DINFO_LEVEL_TERSE || depth == 0) - dbxout_syms (BLOCK_VARS (block)); - if (args) - dbxout_reg_parms (args); -#endif - - /* Now output an N_LBRAC symbol to represent the beginning of - the block. Use the block's tree-walk order to generate - the assembler symbols LBBn and LBEn - that final will define around the code in this block. */ - if (depth > 0 && debug_info_level != DINFO_LEVEL_TERSE) - { - char buf[20]; - blocknum = next_block_number++; - ASM_GENERATE_INTERNAL_LABEL (buf, "LBB", blocknum); - - if (BLOCK_HANDLER_BLOCK (block)) - { - /* A catch block. Must precede N_LBRAC. */ - tree decl = BLOCK_VARS (block); - while (decl) - { -#ifdef DBX_OUTPUT_CATCH - DBX_OUTPUT_CATCH (asmfile, decl, buf); -#else - fprintf (asmfile, "%s \"%s:C1\",%d,0,0,", ASM_STABS_OP, - IDENTIFIER_POINTER (DECL_NAME (decl)), N_CATCH); - assemble_name (asmfile, buf); - fprintf (asmfile, "\n"); -#endif - decl = TREE_CHAIN (decl); - } - } - -#ifdef DBX_OUTPUT_LBRAC - DBX_OUTPUT_LBRAC (asmfile, buf); -#else - fprintf (asmfile, "%s %d,0,0,", ASM_STABN_OP, N_LBRAC); - assemble_name (asmfile, buf); -#if DBX_BLOCKS_FUNCTION_RELATIVE - fputc ('-', asmfile); - assemble_name (asmfile, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); -#endif - fprintf (asmfile, "\n"); -#endif - } - else if (depth > 0) - /* Count blocks the same way regardless of debug_info_level. */ - next_block_number++; - -#ifdef DBX_LBRAC_FIRST - /* On some weird machines, the syms of a block - come after the N_LBRAC. */ - if (debug_info_level != DINFO_LEVEL_TERSE || depth == 0) - dbxout_syms (BLOCK_VARS (block)); - if (args) - dbxout_reg_parms (args); -#endif - - /* Output the subblocks. */ - dbxout_block (BLOCK_SUBBLOCKS (block), depth + 1, NULL_TREE); - - /* Refer to the marker for the end of the block. */ - if (depth > 0 && debug_info_level != DINFO_LEVEL_TERSE) - { - char buf[20]; - ASM_GENERATE_INTERNAL_LABEL (buf, "LBE", blocknum); -#ifdef DBX_OUTPUT_RBRAC - DBX_OUTPUT_RBRAC (asmfile, buf); -#else - fprintf (asmfile, "%s %d,0,0,", ASM_STABN_OP, N_RBRAC); - assemble_name (asmfile, buf); -#if DBX_BLOCKS_FUNCTION_RELATIVE - fputc ('-', asmfile); - assemble_name (asmfile, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); -#endif - fprintf (asmfile, "\n"); -#endif - } - } - block = BLOCK_CHAIN (block); - } -} - -/* Output the information about a function and its arguments and result. - Usually this follows the function's code, - but on some systems, it comes before. */ - -static void -dbxout_really_begin_function (decl) - tree decl; -{ - dbxout_symbol (decl, 0); - dbxout_parms (DECL_ARGUMENTS (decl)); - if (DECL_NAME (DECL_RESULT (decl)) != 0) - dbxout_symbol (DECL_RESULT (decl), 1); -} - -/* Called at beginning of output of function definition. */ - -void -dbxout_begin_function (decl) - tree decl; -{ -#ifdef DBX_FUNCTION_FIRST - dbxout_really_begin_function (decl); -#endif -} - -/* Output dbx data for a function definition. - This includes a definition of the function name itself (a symbol), - definitions of the parameters (locating them in the parameter list) - and then output the block that makes up the function's body - (including all the auto variables of the function). */ - -void -dbxout_function (decl) - tree decl; -{ -#ifndef DBX_FUNCTION_FIRST - dbxout_really_begin_function (decl); -#endif - dbxout_block (DECL_INITIAL (decl), 0, DECL_ARGUMENTS (decl)); -#ifdef DBX_OUTPUT_FUNCTION_END - DBX_OUTPUT_FUNCTION_END (asmfile, decl); -#endif -} -#endif /* DBX_DEBUGGING_INFO */ diff --git a/gnu/usr.bin/cc/common/dwarfout.c b/gnu/usr.bin/cc/common/dwarfout.c deleted file mode 100644 index d81d4620e8..0000000000 --- a/gnu/usr.bin/cc/common/dwarfout.c +++ /dev/null @@ -1,5627 +0,0 @@ -/* This file contains code written by Ron Guilmette (rfg@ncd.com) for - Network Computing Devices, August, September, October, November 1990. - - Output Dwarf format symbol table information from the GNU C compiler. - Copyright (C) 1992 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include "config.h" - -#ifdef DWARF_DEBUGGING_INFO -#include -#include "dwarf.h" -#include "tree.h" -#include "flags.h" -#include "rtl.h" -#include "hard-reg-set.h" -#include "insn-config.h" -#include "reload.h" -#include "output.h" -#include "defaults.h" - -#ifndef DWARF_VERSION -#define DWARF_VERSION 1 -#endif - -/* #define NDEBUG 1 */ -#include "assert.h" - -#if defined(DWARF_TIMESTAMPS) -#if defined(POSIX) -#include -#else /* !defined(POSIX) */ -#include -#if defined(__STDC__) -extern time_t time (time_t *); -#else /* !defined(__STDC__) */ -extern time_t time (); -#endif /* !defined(__STDC__) */ -#endif /* !defined(POSIX) */ -#endif /* defined(DWARF_TIMESTAMPS) */ - -extern char *getpwd (); - -extern char *index (); -extern char *rindex (); - -/* IMPORTANT NOTE: Please see the file README.DWARF for important details - regarding the GNU implementation of Dwarf. */ - -/* NOTE: In the comments in this file, many references are made to - so called "Debugging Information Entries". For the sake of brevity, - this term is abbreviated to `DIE' throughout the remainder of this - file. */ - -/* Note that the implementation of C++ support herein is (as yet) unfinished. - If you want to try to complete it, more power to you. */ - -#if defined(__GNUC__) && (NDEBUG == 1) -#define inline static inline -#else -#define inline static -#endif - -/* How to start an assembler comment. */ -#ifndef ASM_COMMENT_START -#define ASM_COMMENT_START ";#" -#endif - -/* How to print out a register name. */ -#ifndef PRINT_REG -#define PRINT_REG(RTX, CODE, FILE) \ - fprintf ((FILE), "%s", reg_names[REGNO (RTX)]) -#endif - -/* Define a macro which returns non-zero for any tagged type which is - used (directly or indirectly) in the specification of either some - function's return type or some formal parameter of some function. - We use this macro when we are operating in "terse" mode to help us - know what tagged types have to be represented in Dwarf (even in - terse mode) and which ones don't. - - A flag bit with this meaning really should be a part of the normal - GCC ..._TYPE nodes, but at the moment, there is no such bit defined - for these nodes. For now, we have to just fake it. It it safe for - us to simply return zero for all complete tagged types (which will - get forced out anyway if they were used in the specification of some - formal or return type) and non-zero for all incomplete tagged types. -*/ - -#define TYPE_USED_FOR_FUNCTION(tagged_type) (TYPE_SIZE (tagged_type) == 0) - -extern int flag_traditional; -extern char *version_string; -extern char *language_string; - -/* Maximum size (in bytes) of an artificially generated label. */ - -#define MAX_ARTIFICIAL_LABEL_BYTES 30 - -/* Make sure we know the sizes of the various types dwarf can describe. - These are only defaults. If the sizes are different for your target, - you should override these values by defining the appropriate symbols - in your tm.h file. */ - -#ifndef CHAR_TYPE_SIZE -#define CHAR_TYPE_SIZE BITS_PER_UNIT -#endif - -#ifndef SHORT_TYPE_SIZE -#define SHORT_TYPE_SIZE (BITS_PER_UNIT * 2) -#endif - -#ifndef INT_TYPE_SIZE -#define INT_TYPE_SIZE BITS_PER_WORD -#endif - -#ifndef LONG_TYPE_SIZE -#define LONG_TYPE_SIZE BITS_PER_WORD -#endif - -#ifndef LONG_LONG_TYPE_SIZE -#define LONG_LONG_TYPE_SIZE (BITS_PER_WORD * 2) -#endif - -#ifndef WCHAR_TYPE_SIZE -#define WCHAR_TYPE_SIZE INT_TYPE_SIZE -#endif - -#ifndef WCHAR_UNSIGNED -#define WCHAR_UNSIGNED 0 -#endif - -#ifndef FLOAT_TYPE_SIZE -#define FLOAT_TYPE_SIZE BITS_PER_WORD -#endif - -#ifndef DOUBLE_TYPE_SIZE -#define DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2) -#endif - -#ifndef LONG_DOUBLE_TYPE_SIZE -#define LONG_DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2) -#endif - -/* Structure to keep track of source filenames. */ - -struct filename_entry { - unsigned number; - char * name; -}; - -typedef struct filename_entry filename_entry; - -/* Pointer to an array of elements, each one having the structure above. */ - -static filename_entry *filename_table; - -/* Total number of entries in the table (i.e. array) pointed to by - `filename_table'. This is the *total* and includes both used and - unused slots. */ - -static unsigned ft_entries_allocated; - -/* Number of entries in the filename_table which are actually in use. */ - -static unsigned ft_entries; - -/* Size (in elements) of increments by which we may expand the filename - table. Actually, a single hunk of space of this size should be enough - for most typical programs. */ - -#define FT_ENTRIES_INCREMENT 64 - -/* Local pointer to the name of the main input file. Initialized in - dwarfout_init. */ - -static char *primary_filename; - -/* Pointer to the most recent filename for which we produced some line info. */ - -static char *last_filename; - -/* For Dwarf output, we must assign lexical-blocks id numbers - in the order in which their beginnings are encountered. - We output Dwarf debugging info that refers to the beginnings - and ends of the ranges of code for each lexical block with - assembler labels ..Bn and ..Bn.e, where n is the block number. - The labels themselves are generated in final.c, which assigns - numbers to the blocks in the same way. */ - -static unsigned next_block_number = 2; - -/* Counter to generate unique names for DIEs. */ - -static unsigned next_unused_dienum = 1; - -/* Number of the DIE which is currently being generated. */ - -static unsigned current_dienum; - -/* Number to use for the special "pubname" label on the next DIE which - represents a function or data object defined in this compilation - unit which has "extern" linkage. */ - -static next_pubname_number = 0; - -#define NEXT_DIE_NUM pending_sibling_stack[pending_siblings-1] - -/* Pointer to a dynamically allocated list of pre-reserved and still - pending sibling DIE numbers. Note that this list will grow as needed. */ - -static unsigned *pending_sibling_stack; - -/* Counter to keep track of the number of pre-reserved and still pending - sibling DIE numbers. */ - -static unsigned pending_siblings; - -/* The currently allocated size of the above list (expressed in number of - list elements). */ - -static unsigned pending_siblings_allocated; - -/* Size (in elements) of increments by which we may expand the pending - sibling stack. Actually, a single hunk of space of this size should - be enough for most typical programs. */ - -#define PENDING_SIBLINGS_INCREMENT 64 - -/* Non-zero if we are performing our file-scope finalization pass and if - we should force out Dwarf descriptions of any and all file-scope - tagged types which are still incomplete types. */ - -static int finalizing = 0; - -/* A pointer to the base of a list of pending types which we haven't - generated DIEs for yet, but which we will have to come back to - later on. */ - -static tree *pending_types_list; - -/* Number of elements currently allocated for the pending_types_list. */ - -static unsigned pending_types_allocated; - -/* Number of elements of pending_types_list currently in use. */ - -static unsigned pending_types; - -/* Size (in elements) of increments by which we may expand the pending - types list. Actually, a single hunk of space of this size should - be enough for most typical programs. */ - -#define PENDING_TYPES_INCREMENT 64 - -/* Pointer to an artificial RECORD_TYPE which we create in dwarfout_init. - This is used in a hack to help us get the DIEs describing types of - formal parameters to come *after* all of the DIEs describing the formal - parameters themselves. That's necessary in order to be compatible - with what the brain-damaged svr4 SDB debugger requires. */ - -static tree fake_containing_scope; - -/* The number of the current function definition that we are generating - debugging information for. These numbers range from 1 up to the maximum - number of function definitions contained within the current compilation - unit. These numbers are used to create unique labels for various things - contained within various function definitions. */ - -static unsigned current_funcdef_number = 1; - -/* A pointer to the ..._DECL node which we have most recently been working - on. We keep this around just in case something about it looks screwy - and we want to tell the user what the source coordinates for the actual - declaration are. */ - -static tree dwarf_last_decl; - -/* Forward declarations for functions defined in this file. */ - -static void output_type (); -static void type_attribute (); -static void output_decls_for_scope (); -static void output_decl (); -static unsigned lookup_filename (); - -/* Definitions of defaults for assembler-dependent names of various - pseudo-ops and section names. - - Theses may be overridden in your tm.h file (if necessary) for your - particular assembler. The default values provided here correspond to - what is expected by "standard" AT&T System V.4 assemblers. */ - -#ifndef FILE_ASM_OP -#define FILE_ASM_OP ".file" -#endif -#ifndef VERSION_ASM_OP -#define VERSION_ASM_OP ".version" -#endif -#ifndef UNALIGNED_SHORT_ASM_OP -#define UNALIGNED_SHORT_ASM_OP ".2byte" -#endif -#ifndef UNALIGNED_INT_ASM_OP -#define UNALIGNED_INT_ASM_OP ".4byte" -#endif -#ifndef ASM_BYTE_OP -#define ASM_BYTE_OP ".byte" -#endif -#ifndef SET_ASM_OP -#define SET_ASM_OP ".set" -#endif - -/* Pseudo-ops for pushing the current section onto the section stack (and - simultaneously changing to a new section) and for poping back to the - section we were in immediately before this one. Note that most svr4 - assemblers only maintain a one level stack... you can push all the - sections you want, but you can only pop out one level. (The sparc - svr4 assembler is an exception to this general rule.) That's - OK because we only use at most one level of the section stack herein. */ - -#ifndef PUSHSECTION_ASM_OP -#define PUSHSECTION_ASM_OP ".section" -#endif -#ifndef POPSECTION_ASM_OP -#define POPSECTION_ASM_OP ".previous" -#endif - -/* The default format used by the ASM_OUTPUT_PUSH_SECTION macro (see below) - to print the PUSHSECTION_ASM_OP and the section name. The default here - works for almost all svr4 assemblers, except for the sparc, where the - section name must be enclosed in double quotes. (See sparcv4.h.) */ - -#ifndef PUSHSECTION_FORMAT -#define PUSHSECTION_FORMAT "%s\t%s\n" -#endif - -#ifndef DEBUG_SECTION -#define DEBUG_SECTION ".debug" -#endif -#ifndef LINE_SECTION -#define LINE_SECTION ".line" -#endif -#ifndef SFNAMES_SECTION -#define SFNAMES_SECTION ".debug_sfnames" -#endif -#ifndef SRCINFO_SECTION -#define SRCINFO_SECTION ".debug_srcinfo" -#endif -#ifndef MACINFO_SECTION -#define MACINFO_SECTION ".debug_macinfo" -#endif -#ifndef PUBNAMES_SECTION -#define PUBNAMES_SECTION ".debug_pubnames" -#endif -#ifndef ARANGES_SECTION -#define ARANGES_SECTION ".debug_aranges" -#endif -#ifndef TEXT_SECTION -#define TEXT_SECTION ".text" -#endif -#ifndef DATA_SECTION -#define DATA_SECTION ".data" -#endif -#ifndef DATA1_SECTION -#define DATA1_SECTION ".data1" -#endif -#ifndef RODATA_SECTION -#define RODATA_SECTION ".rodata" -#endif -#ifndef RODATA1_SECTION -#define RODATA1_SECTION ".rodata1" -#endif -#ifndef BSS_SECTION -#define BSS_SECTION ".bss" -#endif - -/* Definitions of defaults for formats and names of various special - (artificial) labels which may be generated within this file (when - the -g options is used and DWARF_DEBUGGING_INFO is in effect. - - If necessary, these may be overridden from within your tm.h file, - but typically, you should never need to override these. - - These labels have been hacked (temporarily) so that they all begin with - a `.L' sequence so as to appease the stock sparc/svr4 assembler and the - stock m88k/svr4 assembler, both of which need to see .L at the start of - a label in order to prevent that label from going into the linker symbol - table). When I get time, I'll have to fix this the right way so that we - will use ASM_GENERATE_INTERNAL_LABEL and ASM_OUTPUT_INTERNAL_LABEL herein, - but that will require a rather massive set of changes. For the moment, - the following definitions out to produce the right results for all svr4 - and svr3 assemblers. -- rfg -*/ - -#ifndef TEXT_BEGIN_LABEL -#define TEXT_BEGIN_LABEL ".L_text_b" -#endif -#ifndef TEXT_END_LABEL -#define TEXT_END_LABEL ".L_text_e" -#endif - -#ifndef DATA_BEGIN_LABEL -#define DATA_BEGIN_LABEL ".L_data_b" -#endif -#ifndef DATA_END_LABEL -#define DATA_END_LABEL ".L_data_e" -#endif - -#ifndef DATA1_BEGIN_LABEL -#define DATA1_BEGIN_LABEL ".L_data1_b" -#endif -#ifndef DATA1_END_LABEL -#define DATA1_END_LABEL ".L_data1_e" -#endif - -#ifndef RODATA_BEGIN_LABEL -#define RODATA_BEGIN_LABEL ".L_rodata_b" -#endif -#ifndef RODATA_END_LABEL -#define RODATA_END_LABEL ".L_rodata_e" -#endif - -#ifndef RODATA1_BEGIN_LABEL -#define RODATA1_BEGIN_LABEL ".L_rodata1_b" -#endif -#ifndef RODATA1_END_LABEL -#define RODATA1_END_LABEL ".L_rodata1_e" -#endif - -#ifndef BSS_BEGIN_LABEL -#define BSS_BEGIN_LABEL ".L_bss_b" -#endif -#ifndef BSS_END_LABEL -#define BSS_END_LABEL ".L_bss_e" -#endif - -#ifndef LINE_BEGIN_LABEL -#define LINE_BEGIN_LABEL ".L_line_b" -#endif -#ifndef LINE_LAST_ENTRY_LABEL -#define LINE_LAST_ENTRY_LABEL ".L_line_last" -#endif -#ifndef LINE_END_LABEL -#define LINE_END_LABEL ".L_line_e" -#endif - -#ifndef DEBUG_BEGIN_LABEL -#define DEBUG_BEGIN_LABEL ".L_debug_b" -#endif -#ifndef SFNAMES_BEGIN_LABEL -#define SFNAMES_BEGIN_LABEL ".L_sfnames_b" -#endif -#ifndef SRCINFO_BEGIN_LABEL -#define SRCINFO_BEGIN_LABEL ".L_srcinfo_b" -#endif -#ifndef MACINFO_BEGIN_LABEL -#define MACINFO_BEGIN_LABEL ".L_macinfo_b" -#endif - -#ifndef DIE_BEGIN_LABEL_FMT -#define DIE_BEGIN_LABEL_FMT ".L_D%u" -#endif -#ifndef DIE_END_LABEL_FMT -#define DIE_END_LABEL_FMT ".L_D%u_e" -#endif -#ifndef PUB_DIE_LABEL_FMT -#define PUB_DIE_LABEL_FMT ".L_P%u" -#endif -#ifndef INSN_LABEL_FMT -#define INSN_LABEL_FMT ".L_I%u_%u" -#endif -#ifndef BLOCK_BEGIN_LABEL_FMT -#define BLOCK_BEGIN_LABEL_FMT ".L_B%u" -#endif -#ifndef BLOCK_END_LABEL_FMT -#define BLOCK_END_LABEL_FMT ".L_B%u_e" -#endif -#ifndef SS_BEGIN_LABEL_FMT -#define SS_BEGIN_LABEL_FMT ".L_s%u" -#endif -#ifndef SS_END_LABEL_FMT -#define SS_END_LABEL_FMT ".L_s%u_e" -#endif -#ifndef EE_BEGIN_LABEL_FMT -#define EE_BEGIN_LABEL_FMT ".L_e%u" -#endif -#ifndef EE_END_LABEL_FMT -#define EE_END_LABEL_FMT ".L_e%u_e" -#endif -#ifndef MT_BEGIN_LABEL_FMT -#define MT_BEGIN_LABEL_FMT ".L_t%u" -#endif -#ifndef MT_END_LABEL_FMT -#define MT_END_LABEL_FMT ".L_t%u_e" -#endif -#ifndef LOC_BEGIN_LABEL_FMT -#define LOC_BEGIN_LABEL_FMT ".L_l%u" -#endif -#ifndef LOC_END_LABEL_FMT -#define LOC_END_LABEL_FMT ".L_l%u_e" -#endif -#ifndef BOUND_BEGIN_LABEL_FMT -#define BOUND_BEGIN_LABEL_FMT ".L_b%u_%u_%c" -#endif -#ifndef BOUND_END_LABEL_FMT -#define BOUND_END_LABEL_FMT ".L_b%u_%u_%c_e" -#endif -#ifndef DERIV_BEGIN_LABEL_FMT -#define DERIV_BEGIN_LABEL_FMT ".L_d%u" -#endif -#ifndef DERIV_END_LABEL_FMT -#define DERIV_END_LABEL_FMT ".L_d%u_e" -#endif -#ifndef SL_BEGIN_LABEL_FMT -#define SL_BEGIN_LABEL_FMT ".L_sl%u" -#endif -#ifndef SL_END_LABEL_FMT -#define SL_END_LABEL_FMT ".L_sl%u_e" -#endif -#ifndef BODY_BEGIN_LABEL_FMT -#define BODY_BEGIN_LABEL_FMT ".L_b%u" -#endif -#ifndef BODY_END_LABEL_FMT -#define BODY_END_LABEL_FMT ".L_b%u_e" -#endif -#ifndef FUNC_END_LABEL_FMT -#define FUNC_END_LABEL_FMT ".L_f%u_e" -#endif -#ifndef TYPE_NAME_FMT -#define TYPE_NAME_FMT ".L_T%u" -#endif -#ifndef DECL_NAME_FMT -#define DECL_NAME_FMT ".L_E%u" -#endif -#ifndef LINE_CODE_LABEL_FMT -#define LINE_CODE_LABEL_FMT ".L_LC%u" -#endif -#ifndef SFNAMES_ENTRY_LABEL_FMT -#define SFNAMES_ENTRY_LABEL_FMT ".L_F%u" -#endif -#ifndef LINE_ENTRY_LABEL_FMT -#define LINE_ENTRY_LABEL_FMT ".L_LE%u" -#endif - -/* Definitions of defaults for various types of primitive assembly language - output operations. - - If necessary, these may be overridden from within your tm.h file, - but typically, you shouldn't need to override these. One known - exception is ASM_OUTPUT_DEF which has to be different for stock - sparc/svr4 assemblers. -*/ - -#ifndef ASM_OUTPUT_PUSH_SECTION -#define ASM_OUTPUT_PUSH_SECTION(FILE, SECTION) \ - fprintf ((FILE), PUSHSECTION_FORMAT, PUSHSECTION_ASM_OP, SECTION) -#endif - -#ifndef ASM_OUTPUT_POP_SECTION -#define ASM_OUTPUT_POP_SECTION(FILE) \ - fprintf ((FILE), "\t%s\n", POPSECTION_ASM_OP) -#endif - -#ifndef ASM_OUTPUT_SOURCE_FILENAME -#define ASM_OUTPUT_SOURCE_FILENAME(FILE,NAME) \ - fprintf ((FILE), "\t%s\t\"%s\"\n", FILE_ASM_OP, NAME) -#endif - -#ifndef ASM_OUTPUT_DEF -#define ASM_OUTPUT_DEF(FILE,LABEL1,LABEL2) \ - do { fprintf ((FILE), "\t%s\t", SET_ASM_OP); \ - assemble_name (FILE, LABEL1); \ - fprintf (FILE, ","); \ - assemble_name (FILE, LABEL2); \ - fprintf (FILE, "\n"); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_DELTA2 -#define ASM_OUTPUT_DWARF_DELTA2(FILE,LABEL1,LABEL2) \ - do { fprintf ((FILE), "\t%s\t", UNALIGNED_SHORT_ASM_OP); \ - assemble_name (FILE, LABEL1); \ - fprintf (FILE, "-"); \ - assemble_name (FILE, LABEL2); \ - fprintf (FILE, "\n"); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_DELTA4 -#define ASM_OUTPUT_DWARF_DELTA4(FILE,LABEL1,LABEL2) \ - do { fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \ - assemble_name (FILE, LABEL1); \ - fprintf (FILE, "-"); \ - assemble_name (FILE, LABEL2); \ - fprintf (FILE, "\n"); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_TAG -#define ASM_OUTPUT_DWARF_TAG(FILE,TAG) \ - do { \ - fprintf ((FILE), "\t%s\t0x%x", \ - UNALIGNED_SHORT_ASM_OP, (unsigned) TAG); \ - if (flag_verbose_asm) \ - fprintf ((FILE), "\t%s %s", \ - ASM_COMMENT_START, dwarf_tag_name (TAG)); \ - fputc ('\n', (FILE)); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_ATTRIBUTE -#define ASM_OUTPUT_DWARF_ATTRIBUTE(FILE,ATTR) \ - do { \ - fprintf ((FILE), "\t%s\t0x%x", \ - UNALIGNED_SHORT_ASM_OP, (unsigned) ATTR); \ - if (flag_verbose_asm) \ - fprintf ((FILE), "\t%s %s", \ - ASM_COMMENT_START, dwarf_attr_name (ATTR)); \ - fputc ('\n', (FILE)); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_STACK_OP -#define ASM_OUTPUT_DWARF_STACK_OP(FILE,OP) \ - do { \ - fprintf ((FILE), "\t%s\t0x%x", ASM_BYTE_OP, (unsigned) OP); \ - if (flag_verbose_asm) \ - fprintf ((FILE), "\t%s %s", \ - ASM_COMMENT_START, dwarf_stack_op_name (OP)); \ - fputc ('\n', (FILE)); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_FUND_TYPE -#define ASM_OUTPUT_DWARF_FUND_TYPE(FILE,FT) \ - do { \ - fprintf ((FILE), "\t%s\t0x%x", \ - UNALIGNED_SHORT_ASM_OP, (unsigned) FT); \ - if (flag_verbose_asm) \ - fprintf ((FILE), "\t%s %s", \ - ASM_COMMENT_START, dwarf_fund_type_name (FT)); \ - fputc ('\n', (FILE)); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_FMT_BYTE -#define ASM_OUTPUT_DWARF_FMT_BYTE(FILE,FMT) \ - do { \ - fprintf ((FILE), "\t%s\t0x%x", ASM_BYTE_OP, (unsigned) FMT); \ - if (flag_verbose_asm) \ - fprintf ((FILE), "\t%s %s", \ - ASM_COMMENT_START, dwarf_fmt_byte_name (FMT)); \ - fputc ('\n', (FILE)); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_TYPE_MODIFIER -#define ASM_OUTPUT_DWARF_TYPE_MODIFIER(FILE,MOD) \ - do { \ - fprintf ((FILE), "\t%s\t0x%x", ASM_BYTE_OP, (unsigned) MOD); \ - if (flag_verbose_asm) \ - fprintf ((FILE), "\t%s %s", \ - ASM_COMMENT_START, dwarf_typemod_name (MOD)); \ - fputc ('\n', (FILE)); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_ADDR -#define ASM_OUTPUT_DWARF_ADDR(FILE,LABEL) \ - do { fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \ - assemble_name (FILE, LABEL); \ - fprintf (FILE, "\n"); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_ADDR_CONST -#define ASM_OUTPUT_DWARF_ADDR_CONST(FILE,RTX) \ - do { \ - fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \ - output_addr_const ((FILE), (RTX)); \ - fputc ('\n', (FILE)); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_REF -#define ASM_OUTPUT_DWARF_REF(FILE,LABEL) \ - do { fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \ - assemble_name (FILE, LABEL); \ - fprintf (FILE, "\n"); \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_DATA1 -#define ASM_OUTPUT_DWARF_DATA1(FILE,VALUE) \ - fprintf ((FILE), "\t%s\t0x%x\n", ASM_BYTE_OP, VALUE) -#endif - -#ifndef ASM_OUTPUT_DWARF_DATA2 -#define ASM_OUTPUT_DWARF_DATA2(FILE,VALUE) \ - fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_SHORT_ASM_OP, (unsigned) VALUE) -#endif - -#ifndef ASM_OUTPUT_DWARF_DATA4 -#define ASM_OUTPUT_DWARF_DATA4(FILE,VALUE) \ - fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_INT_ASM_OP, (unsigned) VALUE) -#endif - -#ifndef ASM_OUTPUT_DWARF_DATA8 -#define ASM_OUTPUT_DWARF_DATA8(FILE,HIGH_VALUE,LOW_VALUE) \ - do { \ - if (WORDS_BIG_ENDIAN) \ - { \ - fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_INT_ASM_OP, HIGH_VALUE); \ - fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_INT_ASM_OP, LOW_VALUE);\ - } \ - else \ - { \ - fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_INT_ASM_OP, LOW_VALUE);\ - fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_INT_ASM_OP, HIGH_VALUE); \ - } \ - } while (0) -#endif - -#ifndef ASM_OUTPUT_DWARF_STRING -#define ASM_OUTPUT_DWARF_STRING(FILE,P) \ - ASM_OUTPUT_ASCII ((FILE), P, strlen (P)+1) -#endif - -/************************ general utility functions **************************/ - -inline char * -xstrdup (s) - register char *s; -{ - register char *p = (char *) xmalloc (strlen (s) + 1); - - strcpy (p, s); - return p; -} - -inline int -is_pseudo_reg (rtl) - register rtx rtl; -{ - return (((GET_CODE (rtl) == REG) && (REGNO (rtl) >= FIRST_PSEUDO_REGISTER)) - || ((GET_CODE (rtl) == SUBREG) - && (REGNO (XEXP (rtl, 0)) >= FIRST_PSEUDO_REGISTER))); -} - -/* Return non-zero if the given type node represents a tagged type. */ - -inline int -is_tagged_type (type) - register tree type; -{ - register enum tree_code code = TREE_CODE (type); - - return (code == RECORD_TYPE || code == UNION_TYPE - || code == QUAL_UNION_TYPE || code == ENUMERAL_TYPE); -} - -static char * -dwarf_tag_name (tag) - register unsigned tag; -{ - switch (tag) - { - case TAG_padding: return "TAG_padding"; - case TAG_array_type: return "TAG_array_type"; - case TAG_class_type: return "TAG_class_type"; - case TAG_entry_point: return "TAG_entry_point"; - case TAG_enumeration_type: return "TAG_enumeration_type"; - case TAG_formal_parameter: return "TAG_formal_parameter"; - case TAG_global_subroutine: return "TAG_global_subroutine"; - case TAG_global_variable: return "TAG_global_variable"; - case TAG_label: return "TAG_label"; - case TAG_lexical_block: return "TAG_lexical_block"; - case TAG_local_variable: return "TAG_local_variable"; - case TAG_member: return "TAG_member"; - case TAG_pointer_type: return "TAG_pointer_type"; - case TAG_reference_type: return "TAG_reference_type"; - case TAG_compile_unit: return "TAG_compile_unit"; - case TAG_string_type: return "TAG_string_type"; - case TAG_structure_type: return "TAG_structure_type"; - case TAG_subroutine: return "TAG_subroutine"; - case TAG_subroutine_type: return "TAG_subroutine_type"; - case TAG_typedef: return "TAG_typedef"; - case TAG_union_type: return "TAG_union_type"; - case TAG_unspecified_parameters: return "TAG_unspecified_parameters"; - case TAG_variant: return "TAG_variant"; - case TAG_common_block: return "TAG_common_block"; - case TAG_common_inclusion: return "TAG_common_inclusion"; - case TAG_inheritance: return "TAG_inheritance"; - case TAG_inlined_subroutine: return "TAG_inlined_subroutine"; - case TAG_module: return "TAG_module"; - case TAG_ptr_to_member_type: return "TAG_ptr_to_member_type"; - case TAG_set_type: return "TAG_set_type"; - case TAG_subrange_type: return "TAG_subrange_type"; - case TAG_with_stmt: return "TAG_with_stmt"; - - /* GNU extensions. */ - - case TAG_format_label: return "TAG_format_label"; - case TAG_namelist: return "TAG_namelist"; - case TAG_function_template: return "TAG_function_template"; - case TAG_class_template: return "TAG_class_template"; - - default: return "TAG_"; - } -} - -static char * -dwarf_attr_name (attr) - register unsigned attr; -{ - switch (attr) - { - case AT_sibling: return "AT_sibling"; - case AT_location: return "AT_location"; - case AT_name: return "AT_name"; - case AT_fund_type: return "AT_fund_type"; - case AT_mod_fund_type: return "AT_mod_fund_type"; - case AT_user_def_type: return "AT_user_def_type"; - case AT_mod_u_d_type: return "AT_mod_u_d_type"; - case AT_ordering: return "AT_ordering"; - case AT_subscr_data: return "AT_subscr_data"; - case AT_byte_size: return "AT_byte_size"; - case AT_bit_offset: return "AT_bit_offset"; - case AT_bit_size: return "AT_bit_size"; - case AT_element_list: return "AT_element_list"; - case AT_stmt_list: return "AT_stmt_list"; - case AT_low_pc: return "AT_low_pc"; - case AT_high_pc: return "AT_high_pc"; - case AT_language: return "AT_language"; - case AT_member: return "AT_member"; - case AT_discr: return "AT_discr"; - case AT_discr_value: return "AT_discr_value"; - case AT_string_length: return "AT_string_length"; - case AT_common_reference: return "AT_common_reference"; - case AT_comp_dir: return "AT_comp_dir"; - case AT_const_value_string: return "AT_const_value_string"; - case AT_const_value_data2: return "AT_const_value_data2"; - case AT_const_value_data4: return "AT_const_value_data4"; - case AT_const_value_data8: return "AT_const_value_data8"; - case AT_const_value_block2: return "AT_const_value_block2"; - case AT_const_value_block4: return "AT_const_value_block4"; - case AT_containing_type: return "AT_containing_type"; - case AT_default_value_addr: return "AT_default_value_addr"; - case AT_default_value_data2: return "AT_default_value_data2"; - case AT_default_value_data4: return "AT_default_value_data4"; - case AT_default_value_data8: return "AT_default_value_data8"; - case AT_default_value_string: return "AT_default_value_string"; - case AT_friends: return "AT_friends"; - case AT_inline: return "AT_inline"; - case AT_is_optional: return "AT_is_optional"; - case AT_lower_bound_ref: return "AT_lower_bound_ref"; - case AT_lower_bound_data2: return "AT_lower_bound_data2"; - case AT_lower_bound_data4: return "AT_lower_bound_data4"; - case AT_lower_bound_data8: return "AT_lower_bound_data8"; - case AT_private: return "AT_private"; - case AT_producer: return "AT_producer"; - case AT_program: return "AT_program"; - case AT_protected: return "AT_protected"; - case AT_prototyped: return "AT_prototyped"; - case AT_public: return "AT_public"; - case AT_pure_virtual: return "AT_pure_virtual"; - case AT_return_addr: return "AT_return_addr"; - case AT_abstract_origin: return "AT_abstract_origin"; - case AT_start_scope: return "AT_start_scope"; - case AT_stride_size: return "AT_stride_size"; - case AT_upper_bound_ref: return "AT_upper_bound_ref"; - case AT_upper_bound_data2: return "AT_upper_bound_data2"; - case AT_upper_bound_data4: return "AT_upper_bound_data4"; - case AT_upper_bound_data8: return "AT_upper_bound_data8"; - case AT_virtual: return "AT_virtual"; - - /* GNU extensions */ - - case AT_sf_names: return "AT_sf_names"; - case AT_src_info: return "AT_src_info"; - case AT_mac_info: return "AT_mac_info"; - case AT_src_coords: return "AT_src_coords"; - case AT_body_begin: return "AT_body_begin"; - case AT_body_end: return "AT_body_end"; - - default: return "AT_"; - } -} - -static char * -dwarf_stack_op_name (op) - register unsigned op; -{ - switch (op) - { - case OP_REG: return "OP_REG"; - case OP_BASEREG: return "OP_BASEREG"; - case OP_ADDR: return "OP_ADDR"; - case OP_CONST: return "OP_CONST"; - case OP_DEREF2: return "OP_DEREF2"; - case OP_DEREF4: return "OP_DEREF4"; - case OP_ADD: return "OP_ADD"; - default: return "OP_"; - } -} - -static char * -dwarf_typemod_name (mod) - register unsigned mod; -{ - switch (mod) - { - case MOD_pointer_to: return "MOD_pointer_to"; - case MOD_reference_to: return "MOD_reference_to"; - case MOD_const: return "MOD_const"; - case MOD_volatile: return "MOD_volatile"; - default: return "MOD_"; - } -} - -static char * -dwarf_fmt_byte_name (fmt) - register unsigned fmt; -{ - switch (fmt) - { - case FMT_FT_C_C: return "FMT_FT_C_C"; - case FMT_FT_C_X: return "FMT_FT_C_X"; - case FMT_FT_X_C: return "FMT_FT_X_C"; - case FMT_FT_X_X: return "FMT_FT_X_X"; - case FMT_UT_C_C: return "FMT_UT_C_C"; - case FMT_UT_C_X: return "FMT_UT_C_X"; - case FMT_UT_X_C: return "FMT_UT_X_C"; - case FMT_UT_X_X: return "FMT_UT_X_X"; - case FMT_ET: return "FMT_ET"; - default: return "FMT_"; - } -} -static char * -dwarf_fund_type_name (ft) - register unsigned ft; -{ - switch (ft) - { - case FT_char: return "FT_char"; - case FT_signed_char: return "FT_signed_char"; - case FT_unsigned_char: return "FT_unsigned_char"; - case FT_short: return "FT_short"; - case FT_signed_short: return "FT_signed_short"; - case FT_unsigned_short: return "FT_unsigned_short"; - case FT_integer: return "FT_integer"; - case FT_signed_integer: return "FT_signed_integer"; - case FT_unsigned_integer: return "FT_unsigned_integer"; - case FT_long: return "FT_long"; - case FT_signed_long: return "FT_signed_long"; - case FT_unsigned_long: return "FT_unsigned_long"; - case FT_pointer: return "FT_pointer"; - case FT_float: return "FT_float"; - case FT_dbl_prec_float: return "FT_dbl_prec_float"; - case FT_ext_prec_float: return "FT_ext_prec_float"; - case FT_complex: return "FT_complex"; - case FT_dbl_prec_complex: return "FT_dbl_prec_complex"; - case FT_void: return "FT_void"; - case FT_boolean: return "FT_boolean"; - case FT_ext_prec_complex: return "FT_ext_prec_complex"; - case FT_label: return "FT_label"; - - /* GNU extensions. */ - - case FT_long_long: return "FT_long_long"; - case FT_signed_long_long: return "FT_signed_long_long"; - case FT_unsigned_long_long: return "FT_unsigned_long_long"; - - case FT_int8: return "FT_int8"; - case FT_signed_int8: return "FT_signed_int8"; - case FT_unsigned_int8: return "FT_unsigned_int8"; - case FT_int16: return "FT_int16"; - case FT_signed_int16: return "FT_signed_int16"; - case FT_unsigned_int16: return "FT_unsigned_int16"; - case FT_int32: return "FT_int32"; - case FT_signed_int32: return "FT_signed_int32"; - case FT_unsigned_int32: return "FT_unsigned_int32"; - case FT_int64: return "FT_int64"; - case FT_signed_int64: return "FT_signed_int64"; - case FT_unsigned_int64: return "FT_signed_int64"; - - case FT_real32: return "FT_real32"; - case FT_real64: return "FT_real64"; - case FT_real96: return "FT_real96"; - case FT_real128: return "FT_real128"; - - default: return "FT_"; - } -} - -/* Determine the "ultimate origin" of a decl. The decl may be an - inlined instance of an inlined instance of a decl which is local - to an inline function, so we have to trace all of the way back - through the origin chain to find out what sort of node actually - served as the original seed for the given block. */ - -static tree -decl_ultimate_origin (decl) - register tree decl; -{ - register tree immediate_origin = DECL_ABSTRACT_ORIGIN (decl); - - if (immediate_origin == NULL) - return NULL; - else - { - register tree ret_val; - register tree lookahead = immediate_origin; - - do - { - ret_val = lookahead; - lookahead = DECL_ABSTRACT_ORIGIN (ret_val); - } - while (lookahead != NULL && lookahead != ret_val); - return ret_val; - } -} - -/* Determine the "ultimate origin" of a block. The block may be an - inlined instance of an inlined instance of a block which is local - to an inline function, so we have to trace all of the way back - through the origin chain to find out what sort of node actually - served as the original seed for the given block. */ - -static tree -block_ultimate_origin (block) - register tree block; -{ - register tree immediate_origin = BLOCK_ABSTRACT_ORIGIN (block); - - if (immediate_origin == NULL) - return NULL; - else - { - register tree ret_val; - register tree lookahead = immediate_origin; - - do - { - ret_val = lookahead; - lookahead = (TREE_CODE (ret_val) == BLOCK) - ? BLOCK_ABSTRACT_ORIGIN (ret_val) - : NULL; - } - while (lookahead != NULL && lookahead != ret_val); - return ret_val; - } -} - -static void -output_unsigned_leb128 (value) - register unsigned long value; -{ - register unsigned long orig_value = value; - - do - { - register unsigned byte = (value & 0x7f); - - value >>= 7; - if (value != 0) /* more bytes to follow */ - byte |= 0x80; - fprintf (asm_out_file, "\t%s\t0x%x", ASM_BYTE_OP, (unsigned) byte); - if (flag_verbose_asm && value == 0) - fprintf (asm_out_file, "\t%s ULEB128 number - value = %u", - ASM_COMMENT_START, orig_value); - fputc ('\n', asm_out_file); - } - while (value != 0); -} - -static void -output_signed_leb128 (value) - register long value; -{ - register long orig_value = value; - register int negative = (value < 0); - register int more; - - do - { - register unsigned byte = (value & 0x7f); - - value >>= 7; - if (negative) - value |= 0xfe000000; /* manually sign extend */ - if (((value == 0) && ((byte & 0x40) == 0)) - || ((value == -1) && ((byte & 0x40) == 1))) - more = 0; - else - { - byte |= 0x80; - more = 1; - } - fprintf (asm_out_file, "\t%s\t0x%x", ASM_BYTE_OP, (unsigned) byte); - if (flag_verbose_asm && more == 0) - fprintf (asm_out_file, "\t%s SLEB128 number - value = %d", - ASM_COMMENT_START, orig_value); - fputc ('\n', asm_out_file); - } - while (more); -} - -/**************** utility functions for attribute functions ******************/ - -/* Given a pointer to a BLOCK node return non-zero if (and only if) the - node in question represents the outermost pair of curly braces (i.e. - the "body block") of a function or method. - - For any BLOCK node representing a "body block" of a function or method, - the BLOCK_SUPERCONTEXT of the node will point to another BLOCK node - which represents the outermost (function) scope for the function or - method (i.e. the one which includes the formal parameters). The - BLOCK_SUPERCONTEXT of *that* node in turn will point to the relevant - FUNCTION_DECL node. -*/ - -inline int -is_body_block (stmt) - register tree stmt; -{ - if (TREE_CODE (stmt) == BLOCK) - { - register tree parent = BLOCK_SUPERCONTEXT (stmt); - - if (TREE_CODE (parent) == BLOCK) - { - register tree grandparent = BLOCK_SUPERCONTEXT (parent); - - if (TREE_CODE (grandparent) == FUNCTION_DECL) - return 1; - } - } - return 0; -} - -/* Given a pointer to a tree node for some type, return a Dwarf fundamental - type code for the given type. - - This routine must only be called for GCC type nodes that correspond to - Dwarf fundamental types. - - The current Dwarf draft specification calls for Dwarf fundamental types - to accurately reflect the fact that a given type was either a "plain" - integral type or an explicitly "signed" integral type. Unfortunately, - we can't always do this, because GCC may already have thrown away the - information about the precise way in which the type was originally - specified, as in: - - typedef signed int my_type; - - struct s { my_type f; }; - - Since we may be stuck here without enought information to do exactly - what is called for in the Dwarf draft specification, we do the best - that we can under the circumstances and always use the "plain" integral - fundamental type codes for int, short, and long types. That's probably - good enough. The additional accuracy called for in the current DWARF - draft specification is probably never even useful in practice. */ - -static int -fundamental_type_code (type) - register tree type; -{ - if (TREE_CODE (type) == ERROR_MARK) - return 0; - - switch (TREE_CODE (type)) - { - case ERROR_MARK: - return FT_void; - - case VOID_TYPE: - return FT_void; - - case INTEGER_TYPE: - /* Carefully distinguish all the standard types of C, - without messing up if the language is not C. - Note that we check only for the names that contain spaces; - other names might occur by coincidence in other languages. */ - if (TYPE_NAME (type) != 0 - && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL - && DECL_NAME (TYPE_NAME (type)) != 0 - && TREE_CODE (DECL_NAME (TYPE_NAME (type))) == IDENTIFIER_NODE) - { - char *name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); - - if (!strcmp (name, "unsigned char")) - return FT_unsigned_char; - if (!strcmp (name, "signed char")) - return FT_signed_char; - if (!strcmp (name, "unsigned int")) - return FT_unsigned_integer; - if (!strcmp (name, "short int")) - return FT_short; - if (!strcmp (name, "short unsigned int")) - return FT_unsigned_short; - if (!strcmp (name, "long int")) - return FT_long; - if (!strcmp (name, "long unsigned int")) - return FT_unsigned_long; - if (!strcmp (name, "long long int")) - return FT_long_long; /* Not grok'ed by svr4 SDB */ - if (!strcmp (name, "long long unsigned int")) - return FT_unsigned_long_long; /* Not grok'ed by svr4 SDB */ - } - - /* Most integer types will be sorted out above, however, for the - sake of special `array index' integer types, the following code - is also provided. */ - - if (TYPE_PRECISION (type) == INT_TYPE_SIZE) - return (TREE_UNSIGNED (type) ? FT_unsigned_integer : FT_integer); - - if (TYPE_PRECISION (type) == LONG_TYPE_SIZE) - return (TREE_UNSIGNED (type) ? FT_unsigned_long : FT_long); - - if (TYPE_PRECISION (type) == LONG_LONG_TYPE_SIZE) - return (TREE_UNSIGNED (type) ? FT_unsigned_long_long : FT_long_long); - - if (TYPE_PRECISION (type) == SHORT_TYPE_SIZE) - return (TREE_UNSIGNED (type) ? FT_unsigned_short : FT_short); - - if (TYPE_PRECISION (type) == CHAR_TYPE_SIZE) - return (TREE_UNSIGNED (type) ? FT_unsigned_char : FT_char); - - abort (); - - case REAL_TYPE: - /* Carefully distinguish all the standard types of C, - without messing up if the language is not C. */ - if (TYPE_NAME (type) != 0 - && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL - && DECL_NAME (TYPE_NAME (type)) != 0 - && TREE_CODE (DECL_NAME (TYPE_NAME (type))) == IDENTIFIER_NODE) - { - char *name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); - - /* Note that here we can run afowl of a serious bug in "classic" - svr4 SDB debuggers. They don't seem to understand the - FT_ext_prec_float type (even though they should). */ - - if (!strcmp (name, "long double")) - return FT_ext_prec_float; - } - - if (TYPE_PRECISION (type) == DOUBLE_TYPE_SIZE) - return FT_dbl_prec_float; - if (TYPE_PRECISION (type) == FLOAT_TYPE_SIZE) - return FT_float; - - /* Note that here we can run afowl of a serious bug in "classic" - svr4 SDB debuggers. They don't seem to understand the - FT_ext_prec_float type (even though they should). */ - - if (TYPE_PRECISION (type) == LONG_DOUBLE_TYPE_SIZE) - return FT_ext_prec_float; - abort (); - - case COMPLEX_TYPE: - return FT_complex; /* GNU FORTRAN COMPLEX type. */ - - case CHAR_TYPE: - return FT_char; /* GNU Pascal CHAR type. Not used in C. */ - - case BOOLEAN_TYPE: - return FT_boolean; /* GNU FORTRAN BOOLEAN type. */ - - default: - abort (); /* No other TREE_CODEs are Dwarf fundamental types. */ - } - return 0; -} - -/* Given a pointer to an arbitrary ..._TYPE tree node, return a pointer to - the Dwarf "root" type for the given input type. The Dwarf "root" type - of a given type is generally the same as the given type, except that if - the given type is a pointer or reference type, then the root type of - the given type is the root type of the "basis" type for the pointer or - reference type. (This definition of the "root" type is recursive.) - Also, the root type of a `const' qualified type or a `volatile' - qualified type is the root type of the given type without the - qualifiers. */ - -static tree -root_type (type) - register tree type; -{ - if (TREE_CODE (type) == ERROR_MARK) - return error_mark_node; - - switch (TREE_CODE (type)) - { - case ERROR_MARK: - return error_mark_node; - - case POINTER_TYPE: - case REFERENCE_TYPE: - return TYPE_MAIN_VARIANT (root_type (TREE_TYPE (type))); - - default: - return TYPE_MAIN_VARIANT (type); - } -} - -/* Given a pointer to an arbitrary ..._TYPE tree node, write out a sequence - of zero or more Dwarf "type-modifier" bytes applicable to the type. */ - -static void -write_modifier_bytes (type, decl_const, decl_volatile) - register tree type; - register int decl_const; - register int decl_volatile; -{ - if (TREE_CODE (type) == ERROR_MARK) - return; - - if (TYPE_READONLY (type) || decl_const) - ASM_OUTPUT_DWARF_TYPE_MODIFIER (asm_out_file, MOD_const); - if (TYPE_VOLATILE (type) || decl_volatile) - ASM_OUTPUT_DWARF_TYPE_MODIFIER (asm_out_file, MOD_volatile); - switch (TREE_CODE (type)) - { - case POINTER_TYPE: - ASM_OUTPUT_DWARF_TYPE_MODIFIER (asm_out_file, MOD_pointer_to); - write_modifier_bytes (TREE_TYPE (type), 0, 0); - return; - - case REFERENCE_TYPE: - ASM_OUTPUT_DWARF_TYPE_MODIFIER (asm_out_file, MOD_reference_to); - write_modifier_bytes (TREE_TYPE (type), 0, 0); - return; - - case ERROR_MARK: - default: - return; - } -} - -/* Given a pointer to an arbitrary ..._TYPE tree node, return non-zero if the - given input type is a Dwarf "fundamental" type. Otherwise return zero. */ - -inline int -type_is_fundamental (type) - register tree type; -{ - switch (TREE_CODE (type)) - { - case ERROR_MARK: - case VOID_TYPE: - case INTEGER_TYPE: - case REAL_TYPE: - case COMPLEX_TYPE: - case BOOLEAN_TYPE: - case CHAR_TYPE: - return 1; - - case SET_TYPE: - case ARRAY_TYPE: - case RECORD_TYPE: - case UNION_TYPE: - case QUAL_UNION_TYPE: - case ENUMERAL_TYPE: - case FUNCTION_TYPE: - case METHOD_TYPE: - case POINTER_TYPE: - case REFERENCE_TYPE: - case STRING_TYPE: - case FILE_TYPE: - case OFFSET_TYPE: - case LANG_TYPE: - return 0; - - default: - abort (); - } - return 0; -} - -/* Given a pointer to some ..._DECL tree node, generate an assembly language - equate directive which will associate a symbolic name with the current DIE. - - The name used is an artificial label generated from the DECL_UID number - associated with the given decl node. The name it gets equated to is the - symbolic label that we (previously) output at the start of the DIE that - we are currently generating. - - Calling this function while generating some "decl related" form of DIE - makes it possible to later refer to the DIE which represents the given - decl simply by re-generating the symbolic name from the ..._DECL node's - UID number. */ - -static void -equate_decl_number_to_die_number (decl) - register tree decl; -{ - /* In the case where we are generating a DIE for some ..._DECL node - which represents either some inline function declaration or some - entity declared within an inline function declaration/definition, - setup a symbolic name for the current DIE so that we have a name - for this DIE that we can easily refer to later on within - AT_abstract_origin attributes. */ - - char decl_label[MAX_ARTIFICIAL_LABEL_BYTES]; - char die_label[MAX_ARTIFICIAL_LABEL_BYTES]; - - sprintf (decl_label, DECL_NAME_FMT, DECL_UID (decl)); - sprintf (die_label, DIE_BEGIN_LABEL_FMT, current_dienum); - ASM_OUTPUT_DEF (asm_out_file, decl_label, die_label); -} - -/* Given a pointer to some ..._TYPE tree node, generate an assembly language - equate directive which will associate a symbolic name with the current DIE. - - The name used is an artificial label generated from the TYPE_UID number - associated with the given type node. The name it gets equated to is the - symbolic label that we (previously) output at the start of the DIE that - we are currently generating. - - Calling this function while generating some "type related" form of DIE - makes it easy to later refer to the DIE which represents the given type - simply by re-generating the alternative name from the ..._TYPE node's - UID number. */ - -inline void -equate_type_number_to_die_number (type) - register tree type; -{ - char type_label[MAX_ARTIFICIAL_LABEL_BYTES]; - char die_label[MAX_ARTIFICIAL_LABEL_BYTES]; - - /* We are generating a DIE to represent the main variant of this type - (i.e the type without any const or volatile qualifiers) so in order - to get the equate to come out right, we need to get the main variant - itself here. */ - - type = TYPE_MAIN_VARIANT (type); - - sprintf (type_label, TYPE_NAME_FMT, TYPE_UID (type)); - sprintf (die_label, DIE_BEGIN_LABEL_FMT, current_dienum); - ASM_OUTPUT_DEF (asm_out_file, type_label, die_label); -} - -static void -output_reg_number (rtl) - register rtx rtl; -{ - register unsigned regno = REGNO (rtl); - - if (regno >= FIRST_PSEUDO_REGISTER) - { - warning_with_decl (dwarf_last_decl, "internal regno botch: regno = %d\n", - regno); - regno = 0; - } - fprintf (asm_out_file, "\t%s\t0x%x", - UNALIGNED_INT_ASM_OP, DBX_REGISTER_NUMBER (regno)); - if (flag_verbose_asm) - { - fprintf (asm_out_file, "\t%s ", ASM_COMMENT_START); - PRINT_REG (rtl, 0, asm_out_file); - } - fputc ('\n', asm_out_file); -} - -/* The following routine is a nice and simple transducer. It converts the - RTL for a variable or parameter (resident in memory) into an equivalent - Dwarf representation of a mechanism for getting the address of that same - variable onto the top of a hypothetical "address evaluation" stack. - - When creating memory location descriptors, we are effectively trans- - forming the RTL for a memory-resident object into its Dwarf postfix - expression equivalent. This routine just recursively descends an - RTL tree, turning it into Dwarf postfix code as it goes. */ - -static void -output_mem_loc_descriptor (rtl) - register rtx rtl; -{ - /* Note that for a dynamically sized array, the location we will - generate a description of here will be the lowest numbered location - which is actually within the array. That's *not* necessarily the - same as the zeroth element of the array. */ - - switch (GET_CODE (rtl)) - { - case SUBREG: - - /* The case of a subreg may arise when we have a local (register) - variable or a formal (register) parameter which doesn't quite - fill up an entire register. For now, just assume that it is - legitimate to make the Dwarf info refer to the whole register - which contains the given subreg. */ - - rtl = XEXP (rtl, 0); - /* Drop thru. */ - - case REG: - - /* Whenever a register number forms a part of the description of - the method for calculating the (dynamic) address of a memory - resident object, DWARF rules require the register number to - be referred to as a "base register". This distinction is not - based in any way upon what category of register the hardware - believes the given register belongs to. This is strictly - DWARF terminology we're dealing with here. - - Note that in cases where the location of a memory-resident data - object could be expressed as: - - OP_ADD (OP_BASEREG (basereg), OP_CONST (0)) - - the actual DWARF location descriptor that we generate may just - be OP_BASEREG (basereg). This may look deceptively like the - object in question was allocated to a register (rather than - in memory) so DWARF consumers need to be aware of the subtle - distinction between OP_REG and OP_BASEREG. */ - - ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_BASEREG); - output_reg_number (rtl); - break; - - case MEM: - output_mem_loc_descriptor (XEXP (rtl, 0)); - ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_DEREF4); - break; - - case CONST: - case SYMBOL_REF: - ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_ADDR); - ASM_OUTPUT_DWARF_ADDR_CONST (asm_out_file, rtl); - break; - - case PLUS: - output_mem_loc_descriptor (XEXP (rtl, 0)); - output_mem_loc_descriptor (XEXP (rtl, 1)); - ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_ADD); - break; - - case CONST_INT: - ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_CONST); - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, INTVAL (rtl)); - break; - - default: - abort (); - } -} - -/* Output a proper Dwarf location descriptor for a variable or parameter - which is either allocated in a register or in a memory location. For - a register, we just generate an OP_REG and the register number. For a - memory location we provide a Dwarf postfix expression describing how to - generate the (dynamic) address of the object onto the address stack. */ - -static void -output_loc_descriptor (rtl) - register rtx rtl; -{ - switch (GET_CODE (rtl)) - { - case SUBREG: - - /* The case of a subreg may arise when we have a local (register) - variable or a formal (register) parameter which doesn't quite - fill up an entire register. For now, just assume that it is - legitimate to make the Dwarf info refer to the whole register - which contains the given subreg. */ - - rtl = XEXP (rtl, 0); - /* Drop thru. */ - - case REG: - ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_REG); - output_reg_number (rtl); - break; - - case MEM: - output_mem_loc_descriptor (XEXP (rtl, 0)); - break; - - default: - abort (); /* Should never happen */ - } -} - -/* Given a tree node describing an array bound (either lower or upper) - output a representation for that bound. */ - -static void -output_bound_representation (bound, dim_num, u_or_l) - register tree bound; - register unsigned dim_num; /* For multi-dimensional arrays. */ - register char u_or_l; /* Designates upper or lower bound. */ -{ - switch (TREE_CODE (bound)) - { - - case ERROR_MARK: - return; - - /* All fixed-bounds are represented by INTEGER_CST nodes. */ - - case INTEGER_CST: - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, - (unsigned) TREE_INT_CST_LOW (bound)); - break; - - /* Dynamic bounds may be represented by NOP_EXPR nodes containing - SAVE_EXPR nodes. */ - - case NOP_EXPR: - bound = TREE_OPERAND (bound, 0); - /* ... fall thru... */ - - case SAVE_EXPR: - { - char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; - char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; - - sprintf (begin_label, BOUND_BEGIN_LABEL_FMT, - current_dienum, dim_num, u_or_l); - - sprintf (end_label, BOUND_END_LABEL_FMT, - current_dienum, dim_num, u_or_l); - - ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label); - ASM_OUTPUT_LABEL (asm_out_file, begin_label); - - /* If we are working on a bound for a dynamic dimension in C, - the dynamic dimension in question had better have a static - (zero) lower bound and a dynamic *upper* bound. */ - - if (u_or_l != 'u') - abort (); - - /* If optimization is turned on, the SAVE_EXPRs that describe - how to access the upper bound values are essentially bogus. - They only describe (at best) how to get at these values at - the points in the generated code right after they have just - been computed. Worse yet, in the typical case, the upper - bound values will not even *be* computed in the optimized - code, so these SAVE_EXPRs are entirely bogus. - - In order to compensate for this fact, we check here to see - if optimization is enabled, and if so, we effectively create - an empty location description for the (unknown and unknowable) - upper bound. - - This should not cause too much trouble for existing (stupid?) - debuggers because they have to deal with empty upper bounds - location descriptions anyway in order to be able to deal with - incomplete array types. - - Of course an intelligent debugger (GDB?) should be able to - comprehend that a missing upper bound specification in a - array type used for a storage class `auto' local array variable - indicates that the upper bound is both unknown (at compile- - time) and unknowable (at run-time) due to optimization. - */ - - if (! optimize) - output_loc_descriptor - (eliminate_regs (SAVE_EXPR_RTL (bound), 0, NULL_RTX)); - - ASM_OUTPUT_LABEL (asm_out_file, end_label); - } - break; - - default: - abort (); - } -} - -/* Recursive function to output a sequence of value/name pairs for - enumeration constants in reversed order. This is called from - enumeration_type_die. */ - -static void -output_enumeral_list (link) - register tree link; -{ - if (link) - { - output_enumeral_list (TREE_CHAIN (link)); - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, - (unsigned) TREE_INT_CST_LOW (TREE_VALUE (link))); - ASM_OUTPUT_DWARF_STRING (asm_out_file, - IDENTIFIER_POINTER (TREE_PURPOSE (link))); - } -} - -/* Given an unsigned value, round it up to the lowest multiple of `boundary' - which is not less than the value itself. */ - -inline unsigned -ceiling (value, boundary) - register unsigned value; - register unsigned boundary; -{ - return (((value + boundary - 1) / boundary) * boundary); -} - -/* Given a pointer to what is assumed to be a FIELD_DECL node, return a - pointer to the declared type for the relevant field variable, or return - `integer_type_node' if the given node turns out to be an ERROR_MARK node. */ - -inline tree -field_type (decl) - register tree decl; -{ - register tree type; - - if (TREE_CODE (decl) == ERROR_MARK) - return integer_type_node; - - type = DECL_BIT_FIELD_TYPE (decl); - if (type == NULL) - type = TREE_TYPE (decl); - return type; -} - -/* Given a pointer to a tree node, assumed to be some kind of a ..._TYPE - node, return the alignment in bits for the type, or else return - BITS_PER_WORD if the node actually turns out to be an ERROR_MARK node. */ - -inline unsigned -simple_type_align_in_bits (type) - register tree type; -{ - return (TREE_CODE (type) != ERROR_MARK) ? TYPE_ALIGN (type) : BITS_PER_WORD; -} - -/* Given a pointer to a tree node, assumed to be some kind of a ..._TYPE - node, return the size in bits for the type if it is a constant, or - else return the alignment for the type if the type's size is not - constant, or else return BITS_PER_WORD if the type actually turns out - to be an ERROR_MARK node. */ - -inline unsigned -simple_type_size_in_bits (type) - register tree type; -{ - if (TREE_CODE (type) == ERROR_MARK) - return BITS_PER_WORD; - else - { - register tree type_size_tree = TYPE_SIZE (type); - - if (TREE_CODE (type_size_tree) != INTEGER_CST) - return TYPE_ALIGN (type); - - return (unsigned) TREE_INT_CST_LOW (type_size_tree); - } -} - -/* Given a pointer to what is assumed to be a FIELD_DECL node, compute and - return the byte offset of the lowest addressed byte of the "containing - object" for the given FIELD_DECL, or return 0 if we are unable to deter- - mine what that offset is, either because the argument turns out to be a - pointer to an ERROR_MARK node, or because the offset is actually variable. - (We can't handle the latter case just yet.) */ - -static unsigned -field_byte_offset (decl) - register tree decl; -{ - register unsigned type_align_in_bytes; - register unsigned type_align_in_bits; - register unsigned type_size_in_bits; - register unsigned object_offset_in_align_units; - register unsigned object_offset_in_bits; - register unsigned object_offset_in_bytes; - register tree type; - register tree bitpos_tree; - register tree field_size_tree; - register unsigned bitpos_int; - register unsigned deepest_bitpos; - register unsigned field_size_in_bits; - - if (TREE_CODE (decl) == ERROR_MARK) - return 0; - - if (TREE_CODE (decl) != FIELD_DECL) - abort (); - - type = field_type (decl); - - bitpos_tree = DECL_FIELD_BITPOS (decl); - field_size_tree = DECL_SIZE (decl); - - /* We cannot yet cope with fields whose positions or sizes are variable, - so for now, when we see such things, we simply return 0. Someday, - we may be able to handle such cases, but it will be damn difficult. */ - - if (TREE_CODE (bitpos_tree) != INTEGER_CST) - return 0; - bitpos_int = (unsigned) TREE_INT_CST_LOW (bitpos_tree); - - if (TREE_CODE (field_size_tree) != INTEGER_CST) - return 0; - field_size_in_bits = (unsigned) TREE_INT_CST_LOW (field_size_tree); - - type_size_in_bits = simple_type_size_in_bits (type); - - type_align_in_bits = simple_type_align_in_bits (type); - type_align_in_bytes = type_align_in_bits / BITS_PER_UNIT; - - /* Note that the GCC front-end doesn't make any attempt to keep track - of the starting bit offset (relative to the start of the containing - structure type) of the hypothetical "containing object" for a bit- - field. Thus, when computing the byte offset value for the start of - the "containing object" of a bit-field, we must deduce this infor- - mation on our own. - - This can be rather tricky to do in some cases. For example, handling - the following structure type definition when compiling for an i386/i486 - target (which only aligns long long's to 32-bit boundaries) can be very - tricky: - - struct S { - int field1; - long long field2:31; - }; - - Fortunately, there is a simple rule-of-thumb which can be used in such - cases. When compiling for an i386/i486, GCC will allocate 8 bytes for - the structure shown above. It decides to do this based upon one simple - rule for bit-field allocation. Quite simply, GCC allocates each "con- - taining object" for each bit-field at the first (i.e. lowest addressed) - legitimate alignment boundary (based upon the required minimum alignment - for the declared type of the field) which it can possibly use, subject - to the condition that there is still enough available space remaining - in the containing object (when allocated at the selected point) to - fully accommodate all of the bits of the bit-field itself. - - This simple rule makes it obvious why GCC allocates 8 bytes for each - object of the structure type shown above. When looking for a place to - allocate the "containing object" for `field2', the compiler simply tries - to allocate a 64-bit "containing object" at each successive 32-bit - boundary (starting at zero) until it finds a place to allocate that 64- - bit field such that at least 31 contiguous (and previously unallocated) - bits remain within that selected 64 bit field. (As it turns out, for - the example above, the compiler finds that it is OK to allocate the - "containing object" 64-bit field at bit-offset zero within the - structure type.) - - Here we attempt to work backwards from the limited set of facts we're - given, and we try to deduce from those facts, where GCC must have - believed that the containing object started (within the structure type). - - The value we deduce is then used (by the callers of this routine) to - generate AT_location and AT_bit_offset attributes for fields (both - bit-fields and, in the case of AT_location, regular fields as well). - */ - - /* Figure out the bit-distance from the start of the structure to the - "deepest" bit of the bit-field. */ - deepest_bitpos = bitpos_int + field_size_in_bits; - - /* This is the tricky part. Use some fancy footwork to deduce where the - lowest addressed bit of the containing object must be. */ - object_offset_in_bits - = ceiling (deepest_bitpos, type_align_in_bits) - type_size_in_bits; - - /* Compute the offset of the containing object in "alignment units". */ - object_offset_in_align_units = object_offset_in_bits / type_align_in_bits; - - /* Compute the offset of the containing object in bytes. */ - object_offset_in_bytes = object_offset_in_align_units * type_align_in_bytes; - - return object_offset_in_bytes; -} - -/****************************** attributes *********************************/ - -/* The following routines are responsible for writing out the various types - of Dwarf attributes (and any following data bytes associated with them). - These routines are listed in order based on the numerical codes of their - associated attributes. */ - -/* Generate an AT_sibling attribute. */ - -inline void -sibling_attribute () -{ - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_sibling); - sprintf (label, DIE_BEGIN_LABEL_FMT, NEXT_DIE_NUM); - ASM_OUTPUT_DWARF_REF (asm_out_file, label); -} - -/* Output the form of location attributes suitable for whole variables and - whole parameters. Note that the location attributes for struct fields - are generated by the routine `data_member_location_attribute' below. */ - -static void -location_attribute (rtl) - register rtx rtl; -{ - char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; - char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; - - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_location); - sprintf (begin_label, LOC_BEGIN_LABEL_FMT, current_dienum); - sprintf (end_label, LOC_END_LABEL_FMT, current_dienum); - ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label); - ASM_OUTPUT_LABEL (asm_out_file, begin_label); - - /* Handle a special case. If we are about to output a location descriptor - for a variable or parameter which has been optimized out of existence, - don't do that. Instead we output a zero-length location descriptor - value as part of the location attribute. - - A variable which has been optimized out of existence will have a - DECL_RTL value which denotes a pseudo-reg. - - Currently, in some rare cases, variables can have DECL_RTL values - which look like (MEM (REG pseudo-reg#)). These cases are due to - bugs elsewhere in the compiler. We treat such cases - as if the variable(s) in question had been optimized out of existence. - - Note that in all cases where we wish to express the fact that a - variable has been optimized out of existence, we do not simply - suppress the generation of the entire location attribute because - the absence of a location attribute in certain kinds of DIEs is - used to indicate something else entirely... i.e. that the DIE - represents an object declaration, but not a definition. So sayeth - the PLSIG. - */ - - if (! is_pseudo_reg (rtl) - && (GET_CODE (rtl) != MEM || ! is_pseudo_reg (XEXP (rtl, 0)))) - output_loc_descriptor (eliminate_regs (rtl, 0, NULL_RTX)); - - ASM_OUTPUT_LABEL (asm_out_file, end_label); -} - -/* Output the specialized form of location attribute used for data members - of struct and union types. - - In the special case of a FIELD_DECL node which represents a bit-field, - the "offset" part of this special location descriptor must indicate the - distance in bytes from the lowest-addressed byte of the containing - struct or union type to the lowest-addressed byte of the "containing - object" for the bit-field. (See the `field_byte_offset' function above.) - - For any given bit-field, the "containing object" is a hypothetical - object (of some integral or enum type) within which the given bit-field - lives. The type of this hypothetical "containing object" is always the - same as the declared type of the individual bit-field itself (for GCC - anyway... the DWARF spec doesn't actually mandate this). - - Note that it is the size (in bytes) of the hypothetical "containing - object" which will be given in the AT_byte_size attribute for this - bit-field. (See the `byte_size_attribute' function below.) It is - also used when calculating the value of the AT_bit_offset attribute. - (See the `bit_offset_attribute' function below.) -*/ - -static void -data_member_location_attribute (decl) - register tree decl; -{ - register unsigned object_offset_in_bytes = field_byte_offset (decl); - char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; - char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; - - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_location); - sprintf (begin_label, LOC_BEGIN_LABEL_FMT, current_dienum); - sprintf (end_label, LOC_END_LABEL_FMT, current_dienum); - ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label); - ASM_OUTPUT_LABEL (asm_out_file, begin_label); - ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_CONST); - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, object_offset_in_bytes); - ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_ADD); - ASM_OUTPUT_LABEL (asm_out_file, end_label); -} - -/* Output an AT_const_value attribute for a variable or a parameter which - does not have a "location" either in memory or in a register. These - things can arise in GNU C when a constant is passed as an actual - parameter to an inlined function. They can also arise in C++ where - declared constants do not necessarily get memory "homes". */ - -static void -const_value_attribute (rtl) - register rtx rtl; -{ - char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; - char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; - - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_const_value_block4); - sprintf (begin_label, LOC_BEGIN_LABEL_FMT, current_dienum); - sprintf (end_label, LOC_END_LABEL_FMT, current_dienum); - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, end_label, begin_label); - ASM_OUTPUT_LABEL (asm_out_file, begin_label); - - switch (GET_CODE (rtl)) - { - case CONST_INT: - /* Note that a CONST_INT rtx could represent either an integer or - a floating-point constant. A CONST_INT is used whenever the - constant will fit into a single word. In all such cases, the - original mode of the constant value is wiped out, and the - CONST_INT rtx is assigned VOIDmode. Since we no longer have - precise mode information for these constants, we always just - output them using 4 bytes. */ - - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, (unsigned) INTVAL (rtl)); - break; - - case CONST_DOUBLE: - /* Note that a CONST_DOUBLE rtx could represent either an integer - or a floating-point constant. A CONST_DOUBLE is used whenever - the constant requires more than one word in order to be adequately - represented. In all such cases, the original mode of the constant - value is preserved as the mode of the CONST_DOUBLE rtx, but for - simplicity we always just output CONST_DOUBLEs using 8 bytes. */ - - ASM_OUTPUT_DWARF_DATA8 (asm_out_file, - (unsigned HOST_WIDE_INT) CONST_DOUBLE_HIGH (rtl), - (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (rtl)); - break; - - case CONST_STRING: - ASM_OUTPUT_DWARF_STRING (asm_out_file, XSTR (rtl, 0)); - break; - - case SYMBOL_REF: - case LABEL_REF: - case CONST: - ASM_OUTPUT_DWARF_ADDR_CONST (asm_out_file, rtl); - break; - - case PLUS: - /* In cases where an inlined instance of an inline function is passed - the address of an `auto' variable (which is local to the caller) - we can get a situation where the DECL_RTL of the artificial - local variable (for the inlining) which acts as a stand-in for - the corresponding formal parameter (of the inline function) - will look like (plus:SI (reg:SI FRAME_PTR) (const_int ...)). - This is not exactly a compile-time constant expression, but it - isn't the address of the (artificial) local variable either. - Rather, it represents the *value* which the artificial local - variable always has during its lifetime. We currently have no - way to represent such quasi-constant values in Dwarf, so for now - we just punt and generate an AT_const_value attribute with form - FORM_BLOCK4 and a length of zero. */ - break; - - default: - abort (); /* No other kinds of rtx should be possible here. */ - } - - ASM_OUTPUT_LABEL (asm_out_file, end_label); -} - -/* Generate *either* an AT_location attribute or else an AT_const_value - data attribute for a variable or a parameter. We generate the - AT_const_value attribute only in those cases where the given - variable or parameter does not have a true "location" either in - memory or in a register. This can happen (for example) when a - constant is passed as an actual argument in a call to an inline - function. (It's possible that these things can crop up in other - ways also.) Note that one type of constant value which can be - passed into an inlined function is a constant pointer. This can - happen for example if an actual argument in an inlined function - call evaluates to a compile-time constant address. */ - -static void -location_or_const_value_attribute (decl) - register tree decl; -{ - register rtx rtl; - - if (TREE_CODE (decl) == ERROR_MARK) - return; - - if ((TREE_CODE (decl) != VAR_DECL) && (TREE_CODE (decl) != PARM_DECL)) - { - /* Should never happen. */ - abort (); - return; - } - - /* Here we have to decide where we are going to say the parameter "lives" - (as far as the debugger is concerned). We only have a couple of choices. - GCC provides us with DECL_RTL and with DECL_INCOMING_RTL. DECL_RTL - normally indicates where the parameter lives during most of the activa- - tion of the function. If optimization is enabled however, this could - be either NULL or else a pseudo-reg. Both of those cases indicate that - the parameter doesn't really live anywhere (as far as the code generation - parts of GCC are concerned) during most of the function's activation. - That will happen (for example) if the parameter is never referenced - within the function. - - We could just generate a location descriptor here for all non-NULL - non-pseudo values of DECL_RTL and ignore all of the rest, but we can - be a little nicer than that if we also consider DECL_INCOMING_RTL in - cases where DECL_RTL is NULL or is a pseudo-reg. - - Note however that we can only get away with using DECL_INCOMING_RTL as - a backup substitute for DECL_RTL in certain limited cases. In cases - where DECL_ARG_TYPE(decl) indicates the same type as TREE_TYPE(decl) - we can be sure that the parameter was passed using the same type as it - is declared to have within the function, and that its DECL_INCOMING_RTL - points us to a place where a value of that type is passed. In cases - where DECL_ARG_TYPE(decl) and TREE_TYPE(decl) are different types - however, we cannot (in general) use DECL_INCOMING_RTL as a backup - substitute for DECL_RTL because in these cases, DECL_INCOMING_RTL - points us to a value of some type which is *different* from the type - of the parameter itself. Thus, if we tried to use DECL_INCOMING_RTL - to generate a location attribute in such cases, the debugger would - end up (for example) trying to fetch a `float' from a place which - actually contains the first part of a `double'. That would lead to - really incorrect and confusing output at debug-time, and we don't - want that now do we? - - So in general, we DO NOT use DECL_INCOMING_RTL as a backup for DECL_RTL - in cases where DECL_ARG_TYPE(decl) != TREE_TYPE(decl). There are a - couple of cute exceptions however. On little-endian machines we can - get away with using DECL_INCOMING_RTL even when DECL_ARG_TYPE(decl) is - not the same as TREE_TYPE(decl) but only when DECL_ARG_TYPE(decl) is - an integral type which is smaller than TREE_TYPE(decl). These cases - arise when (on a little-endian machine) a non-prototyped function has - a parameter declared to be of type `short' or `char'. In such cases, - TREE_TYPE(decl) will be `short' or `char', DECL_ARG_TYPE(decl) will be - `int', and DECL_INCOMING_RTL will point to the lowest-order byte of the - passed `int' value. If the debugger then uses that address to fetch a - `short' or a `char' (on a little-endian machine) the result will be the - correct data, so we allow for such exceptional cases below. - - Note that our goal here is to describe the place where the given formal - parameter lives during most of the function's activation (i.e. between - the end of the prologue and the start of the epilogue). We'll do that - as best as we can. Note however that if the given formal parameter is - modified sometime during the execution of the function, then a stack - backtrace (at debug-time) will show the function as having been called - with the *new* value rather than the value which was originally passed - in. This happens rarely enough that it is not a major problem, but it - *is* a problem, and I'd like to fix it. A future version of dwarfout.c - may generate two additional attributes for any given TAG_formal_parameter - DIE which will describe the "passed type" and the "passed location" for - the given formal parameter in addition to the attributes we now generate - to indicate the "declared type" and the "active location" for each - parameter. This additional set of attributes could be used by debuggers - for stack backtraces. - - Separately, note that sometimes DECL_RTL can be NULL and DECL_INCOMING_RTL - can be NULL also. This happens (for example) for inlined-instances of - inline function formal parameters which are never referenced. This really - shouldn't be happening. All PARM_DECL nodes should get valid non-NULL - DECL_INCOMING_RTL values, but integrate.c doesn't currently generate - these values for inlined instances of inline function parameters, so - when we see such cases, we are just SOL (shit-out-of-luck) for the time - being (until integrate.c gets fixed). - */ - - /* Use DECL_RTL as the "location" unless we find something better. */ - rtl = DECL_RTL (decl); - - if (TREE_CODE (decl) == PARM_DECL) - if (rtl == NULL_RTX || is_pseudo_reg (rtl)) - { - /* This decl represents a formal parameter which was optimized out. */ - register tree declared_type = TYPE_MAIN_VARIANT (TREE_TYPE (decl)); - register tree passed_type = TYPE_MAIN_VARIANT (DECL_ARG_TYPE (decl)); - - /* Note that DECL_INCOMING_RTL may be NULL in here, but we handle - *all* cases where (rtl == NULL_RTX) just below. */ - - if (declared_type == passed_type) - rtl = DECL_INCOMING_RTL (decl); -#if (BYTES_BIG_ENDIAN == 0) - else - if (TREE_CODE (declared_type) == INTEGER_TYPE) - if (TYPE_SIZE (declared_type) <= TYPE_SIZE (passed_type)) - rtl = DECL_INCOMING_RTL (decl); -#endif /* (BYTES_BIG_ENDIAN == 0) */ - } - - if (rtl == NULL_RTX) - return; - - switch (GET_CODE (rtl)) - { - case CONST_INT: - case CONST_DOUBLE: - case CONST_STRING: - case SYMBOL_REF: - case LABEL_REF: - case CONST: - case PLUS: /* DECL_RTL could be (plus (reg ...) (const_int ...)) */ - const_value_attribute (rtl); - break; - - case MEM: - case REG: - case SUBREG: - location_attribute (rtl); - break; - - default: - abort (); /* Should never happen. */ - } -} - -/* Generate an AT_name attribute given some string value to be included as - the value of the attribute. */ - -inline void -name_attribute (name_string) - register char *name_string; -{ - if (name_string && *name_string) - { - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_name); - ASM_OUTPUT_DWARF_STRING (asm_out_file, name_string); - } -} - -inline void -fund_type_attribute (ft_code) - register unsigned ft_code; -{ - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_fund_type); - ASM_OUTPUT_DWARF_FUND_TYPE (asm_out_file, ft_code); -} - -static void -mod_fund_type_attribute (type, decl_const, decl_volatile) - register tree type; - register int decl_const; - register int decl_volatile; -{ - char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; - char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; - - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_mod_fund_type); - sprintf (begin_label, MT_BEGIN_LABEL_FMT, current_dienum); - sprintf (end_label, MT_END_LABEL_FMT, current_dienum); - ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label); - ASM_OUTPUT_LABEL (asm_out_file, begin_label); - write_modifier_bytes (type, decl_const, decl_volatile); - ASM_OUTPUT_DWARF_FUND_TYPE (asm_out_file, - fundamental_type_code (root_type (type))); - ASM_OUTPUT_LABEL (asm_out_file, end_label); -} - -inline void -user_def_type_attribute (type) - register tree type; -{ - char ud_type_name[MAX_ARTIFICIAL_LABEL_BYTES]; - - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_user_def_type); - sprintf (ud_type_name, TYPE_NAME_FMT, TYPE_UID (type)); - ASM_OUTPUT_DWARF_REF (asm_out_file, ud_type_name); -} - -static void -mod_u_d_type_attribute (type, decl_const, decl_volatile) - register tree type; - register int decl_const; - register int decl_volatile; -{ - char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; - char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; - char ud_type_name[MAX_ARTIFICIAL_LABEL_BYTES]; - - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_mod_u_d_type); - sprintf (begin_label, MT_BEGIN_LABEL_FMT, current_dienum); - sprintf (end_label, MT_END_LABEL_FMT, current_dienum); - ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label); - ASM_OUTPUT_LABEL (asm_out_file, begin_label); - write_modifier_bytes (type, decl_const, decl_volatile); - sprintf (ud_type_name, TYPE_NAME_FMT, TYPE_UID (root_type (type))); - ASM_OUTPUT_DWARF_REF (asm_out_file, ud_type_name); - ASM_OUTPUT_LABEL (asm_out_file, end_label); -} - -#ifdef USE_ORDERING_ATTRIBUTE -inline void -ordering_attribute (ordering) - register unsigned ordering; -{ - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_ordering); - ASM_OUTPUT_DWARF_DATA2 (asm_out_file, ordering); -} -#endif /* defined(USE_ORDERING_ATTRIBUTE) */ - -/* Note that the block of subscript information for an array type also - includes information about the element type of type given array type. */ - -static void -subscript_data_attribute (type) - register tree type; -{ - register unsigned dimension_number; - char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; - char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; - - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_subscr_data); - sprintf (begin_label, SS_BEGIN_LABEL_FMT, current_dienum); - sprintf (end_label, SS_END_LABEL_FMT, current_dienum); - ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label); - ASM_OUTPUT_LABEL (asm_out_file, begin_label); - - /* The GNU compilers represent multidimensional array types as sequences - of one dimensional array types whose element types are themselves array - types. Here we squish that down, so that each multidimensional array - type gets only one array_type DIE in the Dwarf debugging info. The - draft Dwarf specification say that we are allowed to do this kind - of compression in C (because there is no difference between an - array or arrays and a multidimensional array in C) but for other - source languages (e.g. Ada) we probably shouldn't do this. */ - - for (dimension_number = 0; - TREE_CODE (type) == ARRAY_TYPE; - type = TREE_TYPE (type), dimension_number++) - { - register tree domain = TYPE_DOMAIN (type); - - /* Arrays come in three flavors. Unspecified bounds, fixed - bounds, and (in GNU C only) variable bounds. Handle all - three forms here. */ - - if (domain) - { - /* We have an array type with specified bounds. */ - - register tree lower = TYPE_MIN_VALUE (domain); - register tree upper = TYPE_MAX_VALUE (domain); - - /* Handle only fundamental types as index types for now. */ - - if (! type_is_fundamental (domain)) - abort (); - - /* Output the representation format byte for this dimension. */ - - ASM_OUTPUT_DWARF_FMT_BYTE (asm_out_file, - FMT_CODE (1, - TREE_CODE (lower) == INTEGER_CST, - TREE_CODE (upper) == INTEGER_CST)); - - /* Output the index type for this dimension. */ - - ASM_OUTPUT_DWARF_FUND_TYPE (asm_out_file, - fundamental_type_code (domain)); - - /* Output the representation for the lower bound. */ - - output_bound_representation (lower, dimension_number, 'l'); - - /* Output the representation for the upper bound. */ - - output_bound_representation (upper, dimension_number, 'u'); - } - else - { - /* We have an array type with an unspecified length. For C and - C++ we can assume that this really means that (a) the index - type is an integral type, and (b) the lower bound is zero. - Note that Dwarf defines the representation of an unspecified - (upper) bound as being a zero-length location description. */ - - /* Output the array-bounds format byte. */ - - ASM_OUTPUT_DWARF_FMT_BYTE (asm_out_file, FMT_FT_C_X); - - /* Output the (assumed) index type. */ - - ASM_OUTPUT_DWARF_FUND_TYPE (asm_out_file, FT_integer); - - /* Output the (assumed) lower bound (constant) value. */ - - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0); - - /* Output the (empty) location description for the upper bound. */ - - ASM_OUTPUT_DWARF_DATA2 (asm_out_file, 0); - } - } - - /* Output the prefix byte that says that the element type is comming up. */ - - ASM_OUTPUT_DWARF_FMT_BYTE (asm_out_file, FMT_ET); - - /* Output a representation of the type of the elements of this array type. */ - - type_attribute (type, 0, 0); - - ASM_OUTPUT_LABEL (asm_out_file, end_label); -} - -static void -byte_size_attribute (tree_node) - register tree tree_node; -{ - register unsigned size; - - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_byte_size); - switch (TREE_CODE (tree_node)) - { - case ERROR_MARK: - size = 0; - break; - - case ENUMERAL_TYPE: - case RECORD_TYPE: - case UNION_TYPE: - case QUAL_UNION_TYPE: - size = int_size_in_bytes (tree_node); - break; - - case FIELD_DECL: - /* For a data member of a struct or union, the AT_byte_size is - generally given as the number of bytes normally allocated for - an object of the *declared* type of the member itself. This - is true even for bit-fields. */ - size = simple_type_size_in_bits (field_type (tree_node)) - / BITS_PER_UNIT; - break; - - default: - abort (); - } - - /* Note that `size' might be -1 when we get to this point. If it - is, that indicates that the byte size of the entity in question - is variable. We have no good way of expressing this fact in Dwarf - at the present time, so just let the -1 pass on through. */ - - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, size); -} - -/* For a FIELD_DECL node which represents a bit-field, output an attribute - which specifies the distance in bits from the highest order bit of the - "containing object" for the bit-field to the highest order bit of the - bit-field itself. - - For any given bit-field, the "containing object" is a hypothetical - object (of some integral or enum type) within which the given bit-field - lives. The type of this hypothetical "containing object" is always the - same as the declared type of the individual bit-field itself. - - The determination of the exact location of the "containing object" for - a bit-field is rather complicated. It's handled by the `field_byte_offset' - function (above). - - Note that it is the size (in bytes) of the hypothetical "containing - object" which will be given in the AT_byte_size attribute for this - bit-field. (See `byte_size_attribute' above.) -*/ - -inline void -bit_offset_attribute (decl) - register tree decl; -{ - register unsigned object_offset_in_bytes = field_byte_offset (decl); - register tree type = DECL_BIT_FIELD_TYPE (decl); - register tree bitpos_tree = DECL_FIELD_BITPOS (decl); - register unsigned bitpos_int; - register unsigned highest_order_object_bit_offset; - register unsigned highest_order_field_bit_offset; - register unsigned bit_offset; - - assert (TREE_CODE (decl) == FIELD_DECL); /* Must be a field. */ - assert (type); /* Must be a bit field. */ - - /* We can't yet handle bit-fields whose offsets are variable, so if we - encounter such things, just return without generating any attribute - whatsoever. */ - - if (TREE_CODE (bitpos_tree) != INTEGER_CST) - return; - bitpos_int = (unsigned) TREE_INT_CST_LOW (bitpos_tree); - - /* Note that the bit offset is always the distance (in bits) from the - highest-order bit of the "containing object" to the highest-order - bit of the bit-field itself. Since the "high-order end" of any - object or field is different on big-endian and little-endian machines, - the computation below must take account of these differences. */ - - highest_order_object_bit_offset = object_offset_in_bytes * BITS_PER_UNIT; - highest_order_field_bit_offset = bitpos_int; - -#if (BYTES_BIG_ENDIAN == 0) - highest_order_field_bit_offset - += (unsigned) TREE_INT_CST_LOW (DECL_SIZE (decl)); - - highest_order_object_bit_offset += simple_type_size_in_bits (type); -#endif /* (BYTES_BIG_ENDIAN == 0) */ - - bit_offset = -#if (BYTES_BIG_ENDIAN == 0) - highest_order_object_bit_offset - highest_order_field_bit_offset; -#else /* (BYTES_BIG_ENDIAN != 0) */ - highest_order_field_bit_offset - highest_order_object_bit_offset; -#endif /* (BYTES_BIG_ENDIAN != 0) */ - - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_bit_offset); - ASM_OUTPUT_DWARF_DATA2 (asm_out_file, bit_offset); -} - -/* For a FIELD_DECL node which represents a bit field, output an attribute - which specifies the length in bits of the given field. */ - -inline void -bit_size_attribute (decl) - register tree decl; -{ - assert (TREE_CODE (decl) == FIELD_DECL); /* Must be a field. */ - assert (DECL_BIT_FIELD_TYPE (decl)); /* Must be a bit field. */ - - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_bit_size); - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, - (unsigned) TREE_INT_CST_LOW (DECL_SIZE (decl))); -} - -/* The following routine outputs the `element_list' attribute for enumeration - type DIEs. The element_lits attribute includes the names and values of - all of the enumeration constants associated with the given enumeration - type. */ - -inline void -element_list_attribute (element) - register tree element; -{ - char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; - char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; - - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_element_list); - sprintf (begin_label, EE_BEGIN_LABEL_FMT, current_dienum); - sprintf (end_label, EE_END_LABEL_FMT, current_dienum); - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, end_label, begin_label); - ASM_OUTPUT_LABEL (asm_out_file, begin_label); - - /* Here we output a list of value/name pairs for each enumeration constant - defined for this enumeration type (as required), but we do it in REVERSE - order. The order is the one required by the draft #5 Dwarf specification - published by the UI/PLSIG. */ - - output_enumeral_list (element); /* Recursively output the whole list. */ - - ASM_OUTPUT_LABEL (asm_out_file, end_label); -} - -/* Generate an AT_stmt_list attribute. These are normally present only in - DIEs with a TAG_compile_unit tag. */ - -inline void -stmt_list_attribute (label) - register char *label; -{ - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_stmt_list); - /* Don't use ASM_OUTPUT_DWARF_DATA4 here. */ - ASM_OUTPUT_DWARF_ADDR (asm_out_file, label); -} - -/* Generate an AT_low_pc attribute for a label DIE, a lexical_block DIE or - for a subroutine DIE. */ - -inline void -low_pc_attribute (asm_low_label) - register char *asm_low_label; -{ - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_low_pc); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, asm_low_label); -} - -/* Generate an AT_high_pc attribute for a lexical_block DIE or for a - subroutine DIE. */ - -inline void -high_pc_attribute (asm_high_label) - register char *asm_high_label; -{ - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_high_pc); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, asm_high_label); -} - -/* Generate an AT_body_begin attribute for a subroutine DIE. */ - -inline void -body_begin_attribute (asm_begin_label) - register char *asm_begin_label; -{ - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_body_begin); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, asm_begin_label); -} - -/* Generate an AT_body_end attribute for a subroutine DIE. */ - -inline void -body_end_attribute (asm_end_label) - register char *asm_end_label; -{ - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_body_end); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, asm_end_label); -} - -/* Generate an AT_language attribute given a LANG value. These attributes - are used only within TAG_compile_unit DIEs. */ - -inline void -language_attribute (language_code) - register unsigned language_code; -{ - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_language); - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, language_code); -} - -inline void -member_attribute (context) - register tree context; -{ - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - /* Generate this attribute only for members in C++. */ - - if (context != NULL && is_tagged_type (context)) - { - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_member); - sprintf (label, TYPE_NAME_FMT, TYPE_UID (context)); - ASM_OUTPUT_DWARF_REF (asm_out_file, label); - } -} - -inline void -string_length_attribute (upper_bound) - register tree upper_bound; -{ - char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; - char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; - - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_string_length); - sprintf (begin_label, SL_BEGIN_LABEL_FMT, current_dienum); - sprintf (end_label, SL_END_LABEL_FMT, current_dienum); - ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label); - ASM_OUTPUT_LABEL (asm_out_file, begin_label); - output_bound_representation (upper_bound, 0, 'u'); - ASM_OUTPUT_LABEL (asm_out_file, end_label); -} - -inline void -comp_dir_attribute (dirname) - register char *dirname; -{ - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_comp_dir); - ASM_OUTPUT_DWARF_STRING (asm_out_file, dirname); -} - -inline void -sf_names_attribute (sf_names_start_label) - register char *sf_names_start_label; -{ - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_sf_names); - /* Don't use ASM_OUTPUT_DWARF_DATA4 here. */ - ASM_OUTPUT_DWARF_ADDR (asm_out_file, sf_names_start_label); -} - -inline void -src_info_attribute (src_info_start_label) - register char *src_info_start_label; -{ - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_src_info); - /* Don't use ASM_OUTPUT_DWARF_DATA4 here. */ - ASM_OUTPUT_DWARF_ADDR (asm_out_file, src_info_start_label); -} - -inline void -mac_info_attribute (mac_info_start_label) - register char *mac_info_start_label; -{ - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_mac_info); - /* Don't use ASM_OUTPUT_DWARF_DATA4 here. */ - ASM_OUTPUT_DWARF_ADDR (asm_out_file, mac_info_start_label); -} - -inline void -prototyped_attribute (func_type) - register tree func_type; -{ - if ((strcmp (language_string, "GNU C") == 0) - && (TYPE_ARG_TYPES (func_type) != NULL)) - { - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_prototyped); - ASM_OUTPUT_DWARF_STRING (asm_out_file, ""); - } -} - -inline void -producer_attribute (producer) - register char *producer; -{ - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_producer); - ASM_OUTPUT_DWARF_STRING (asm_out_file, producer); -} - -inline void -inline_attribute (decl) - register tree decl; -{ - if (DECL_INLINE (decl)) - { - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_inline); - ASM_OUTPUT_DWARF_STRING (asm_out_file, ""); - } -} - -inline void -containing_type_attribute (containing_type) - register tree containing_type; -{ - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_containing_type); - sprintf (label, TYPE_NAME_FMT, TYPE_UID (containing_type)); - ASM_OUTPUT_DWARF_REF (asm_out_file, label); -} - -inline void -abstract_origin_attribute (origin) - register tree origin; -{ - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_abstract_origin); - switch (TREE_CODE_CLASS (TREE_CODE (origin))) - { - case 'd': - sprintf (label, DECL_NAME_FMT, DECL_UID (origin)); - break; - - case 't': - sprintf (label, TYPE_NAME_FMT, TYPE_UID (origin)); - break; - - default: - abort (); /* Should never happen. */ - - } - ASM_OUTPUT_DWARF_REF (asm_out_file, label); -} - -#ifdef DWARF_DECL_COORDINATES -inline void -src_coords_attribute (src_fileno, src_lineno) - register unsigned src_fileno; - register unsigned src_lineno; -{ - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_src_coords); - ASM_OUTPUT_DWARF_DATA2 (asm_out_file, src_fileno); - ASM_OUTPUT_DWARF_DATA2 (asm_out_file, src_lineno); -} -#endif /* defined(DWARF_DECL_COORDINATES) */ - -inline void -pure_or_virtual_attribute (func_decl) - register tree func_decl; -{ - if (DECL_VIRTUAL_P (func_decl)) - { -#if 0 /* DECL_ABSTRACT_VIRTUAL_P is C++-specific. */ - if (DECL_ABSTRACT_VIRTUAL_P (func_decl)) - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_pure_virtual); - else -#endif - ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_virtual); - ASM_OUTPUT_DWARF_STRING (asm_out_file, ""); - } -} - -/************************* end of attributes *****************************/ - -/********************* utility routines for DIEs *************************/ - -/* Output an AT_name attribute and an AT_src_coords attribute for the - given decl, but only if it actually has a name. */ - -static void -name_and_src_coords_attributes (decl) - register tree decl; -{ - register tree decl_name = DECL_NAME (decl); - - if (decl_name && IDENTIFIER_POINTER (decl_name)) - { - name_attribute (IDENTIFIER_POINTER (decl_name)); -#ifdef DWARF_DECL_COORDINATES - { - register unsigned file_index; - - /* This is annoying, but we have to pop out of the .debug section - for a moment while we call `lookup_filename' because calling it - may cause a temporary switch into the .debug_sfnames section and - most svr4 assemblers are not smart enough be be able to nest - section switches to any depth greater than one. Note that we - also can't skirt this issue by delaying all output to the - .debug_sfnames section unit the end of compilation because that - would cause us to have inter-section forward references and - Fred Fish sez that m68k/svr4 assemblers botch those. */ - - ASM_OUTPUT_POP_SECTION (asm_out_file); - file_index = lookup_filename (DECL_SOURCE_FILE (decl)); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, DEBUG_SECTION); - - src_coords_attribute (file_index, DECL_SOURCE_LINE (decl)); - } -#endif /* defined(DWARF_DECL_COORDINATES) */ - } -} - -/* Many forms of DIEs contain a "type description" part. The following - routine writes out these "type descriptor" parts. */ - -static void -type_attribute (type, decl_const, decl_volatile) - register tree type; - register int decl_const; - register int decl_volatile; -{ - register enum tree_code code = TREE_CODE (type); - register int root_type_modified; - - if (TREE_CODE (type) == ERROR_MARK) - return; - - /* Handle a special case. For functions whose return type is void, - we generate *no* type attribute. (Note that no object may have - type `void', so this only applies to function return types. */ - - if (TREE_CODE (type) == VOID_TYPE) - return; - - root_type_modified = (code == POINTER_TYPE || code == REFERENCE_TYPE - || decl_const || decl_volatile - || TYPE_READONLY (type) || TYPE_VOLATILE (type)); - - if (type_is_fundamental (root_type (type))) - if (root_type_modified) - mod_fund_type_attribute (type, decl_const, decl_volatile); - else - fund_type_attribute (fundamental_type_code (type)); - else - if (root_type_modified) - mod_u_d_type_attribute (type, decl_const, decl_volatile); - else - /* We have to get the TYPE_MAIN_VARIANT here (and pass that to the - `user_def_type_attribute' routine) because the ..._TYPE node we - have might simply be a *copy* of some original type node (where - the copy was created to help us keep track of typedef names) - and that copy might have a different TYPE_UID from the original - ..._TYPE node. (Note that when `equate_type_number_to_die_number' - is labeling a given type DIE for future reference, it always and - only creates labels for DIEs representing *main variants*, and it - never even knows about non-main-variants.) */ - user_def_type_attribute (TYPE_MAIN_VARIANT (type)); -} - -/* Given a tree pointer to a struct, class, union, or enum type node, return - a pointer to the (string) tag name for the given type, or zero if the - type was declared without a tag. */ - -static char * -type_tag (type) - register tree type; -{ - register char *name = 0; - - if (TYPE_NAME (type) != 0) - { - register tree t = 0; - - /* Find the IDENTIFIER_NODE for the type name. */ - if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) - t = TYPE_NAME (type); -#if 0 - /* The g++ front end makes the TYPE_NAME of *each* tagged type point - to a TYPE_DECL node, regardless of whether or not a `typedef' was - involved. This is distinctly different from what the gcc front-end - does. It always makes the TYPE_NAME for each tagged type be either - NULL (signifying an anonymous tagged type) or else a pointer to an - IDENTIFIER_NODE. Obviously, we would like to generate correct Dwarf - for both C and C++, but given this inconsistency in the TREE - representation of tagged types for C and C++ in the GNU front-ends, - we cannot support both languages correctly unless we introduce some - front-end specific code here, and rms objects to that, so we can - only generate correct Dwarf for one of these two languages. C is - more important, so for now we'll do the right thing for C and let - g++ go fish. */ - - else - if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL) - t = DECL_NAME (TYPE_NAME (type)); -#endif - /* Now get the name as a string, or invent one. */ - if (t != 0) - name = IDENTIFIER_POINTER (t); - } - - return (name == 0 || *name == '\0') ? 0 : name; -} - -inline void -dienum_push () -{ - /* Start by checking if the pending_sibling_stack needs to be expanded. - If necessary, expand it. */ - - if (pending_siblings == pending_siblings_allocated) - { - pending_siblings_allocated += PENDING_SIBLINGS_INCREMENT; - pending_sibling_stack - = (unsigned *) xrealloc (pending_sibling_stack, - pending_siblings_allocated * sizeof(unsigned)); - } - - pending_siblings++; - NEXT_DIE_NUM = next_unused_dienum++; -} - -/* Pop the sibling stack so that the most recently pushed DIEnum becomes the - NEXT_DIE_NUM. */ - -inline void -dienum_pop () -{ - pending_siblings--; -} - -inline tree -member_declared_type (member) - register tree member; -{ - return (DECL_BIT_FIELD_TYPE (member)) - ? DECL_BIT_FIELD_TYPE (member) - : TREE_TYPE (member); -} - -/******************************* DIEs ************************************/ - -/* Output routines for individual types of DIEs. */ - -/* Note that every type of DIE (except a null DIE) gets a sibling. */ - -static void -output_array_type_die (arg) - register void *arg; -{ - register tree type = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_array_type); - sibling_attribute (); - equate_type_number_to_die_number (type); - member_attribute (TYPE_CONTEXT (type)); - - /* I believe that we can default the array ordering. SDB will probably - do the right things even if AT_ordering is not present. It's not - even an issue until we start to get into multidimensional arrays - anyway. If SDB is ever caught doing the Wrong Thing for multi- - dimensional arrays, then we'll have to put the AT_ordering attribute - back in. (But if and when we find out that we need to put these in, - we will only do so for multidimensional arrays. After all, we don't - want to waste space in the .debug section now do we?) */ - -#ifdef USE_ORDERING_ATTRIBUTE - ordering_attribute (ORD_row_major); -#endif /* defined(USE_ORDERING_ATTRIBUTE) */ - - subscript_data_attribute (type); -} - -static void -output_set_type_die (arg) - register void *arg; -{ - register tree type = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_set_type); - sibling_attribute (); - equate_type_number_to_die_number (type); - member_attribute (TYPE_CONTEXT (type)); - type_attribute (TREE_TYPE (type), 0, 0); -} - -#if 0 -/* Implement this when there is a GNU FORTRAN or GNU Ada front end. */ -static void -output_entry_point_die (arg) - register void *arg; -{ - register tree decl = arg; - register tree origin = decl_ultimate_origin (decl); - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_entry_point); - sibling_attribute (); - dienum_push (); - if (origin != NULL) - abstract_origin_attribute (origin); - else - { - name_and_src_coords_attributes (decl); - member_attribute (DECL_CONTEXT (decl)); - type_attribute (TREE_TYPE (TREE_TYPE (decl)), 0, 0); - } - if (DECL_ABSTRACT (decl)) - equate_decl_number_to_die_number (decl); - else - low_pc_attribute (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); -} -#endif - -/* Output a DIE to represent an inlined instance of an enumeration type. */ - -static void -output_inlined_enumeration_type_die (arg) - register void *arg; -{ - register tree type = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_enumeration_type); - sibling_attribute (); - assert (TREE_ASM_WRITTEN (type)); - abstract_origin_attribute (type); -} - -/* Output a DIE to represent an inlined instance of a structure type. */ - -static void -output_inlined_structure_type_die (arg) - register void *arg; -{ - register tree type = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_structure_type); - sibling_attribute (); - assert (TREE_ASM_WRITTEN (type)); - abstract_origin_attribute (type); -} - -/* Output a DIE to represent an inlined instance of a union type. */ - -static void -output_inlined_union_type_die (arg) - register void *arg; -{ - register tree type = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_union_type); - sibling_attribute (); - assert (TREE_ASM_WRITTEN (type)); - abstract_origin_attribute (type); -} - -/* Output a DIE to represent an enumeration type. Note that these DIEs - include all of the information about the enumeration values also. - This information is encoded into the element_list attribute. */ - -static void -output_enumeration_type_die (arg) - register void *arg; -{ - register tree type = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_enumeration_type); - sibling_attribute (); - equate_type_number_to_die_number (type); - name_attribute (type_tag (type)); - member_attribute (TYPE_CONTEXT (type)); - - /* Handle a GNU C/C++ extension, i.e. incomplete enum types. If the - given enum type is incomplete, do not generate the AT_byte_size - attribute or the AT_element_list attribute. */ - - if (TYPE_SIZE (type)) - { - byte_size_attribute (type); - element_list_attribute (TYPE_FIELDS (type)); - } -} - -/* Output a DIE to represent either a real live formal parameter decl or - to represent just the type of some formal parameter position in some - function type. - - Note that this routine is a bit unusual because its argument may be - a ..._DECL node (i.e. either a PARM_DECL or perhaps a VAR_DECL which - represents an inlining of some PARM_DECL) or else some sort of a - ..._TYPE node. If it's the former then this function is being called - to output a DIE to represent a formal parameter object (or some inlining - thereof). If it's the latter, then this function is only being called - to output a TAG_formal_parameter DIE to stand as a placeholder for some - formal argument type of some subprogram type. */ - -static void -output_formal_parameter_die (arg) - register void *arg; -{ - register tree node = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_formal_parameter); - sibling_attribute (); - - switch (TREE_CODE_CLASS (TREE_CODE (node))) - { - case 'd': /* We were called with some kind of a ..._DECL node. */ - { - register tree origin = decl_ultimate_origin (node); - - if (origin != NULL) - abstract_origin_attribute (origin); - else - { - name_and_src_coords_attributes (node); - type_attribute (TREE_TYPE (node), - TREE_READONLY (node), TREE_THIS_VOLATILE (node)); - } - if (DECL_ABSTRACT (node)) - equate_decl_number_to_die_number (node); - else - location_or_const_value_attribute (node); - } - break; - - case 't': /* We were called with some kind of a ..._TYPE node. */ - type_attribute (node, 0, 0); - break; - - default: - abort (); /* Should never happen. */ - } -} - -/* Output a DIE to represent a declared function (either file-scope - or block-local) which has "external linkage" (according to ANSI-C). */ - -static void -output_global_subroutine_die (arg) - register void *arg; -{ - register tree decl = arg; - register tree origin = decl_ultimate_origin (decl); - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_global_subroutine); - sibling_attribute (); - dienum_push (); - if (origin != NULL) - abstract_origin_attribute (origin); - else - { - register tree type = TREE_TYPE (decl); - - name_and_src_coords_attributes (decl); - inline_attribute (decl); - prototyped_attribute (type); - member_attribute (DECL_CONTEXT (decl)); - type_attribute (TREE_TYPE (type), 0, 0); - pure_or_virtual_attribute (decl); - } - if (DECL_ABSTRACT (decl)) - equate_decl_number_to_die_number (decl); - else - { - if (! DECL_EXTERNAL (decl)) - { - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - low_pc_attribute (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); - sprintf (label, FUNC_END_LABEL_FMT, current_funcdef_number); - high_pc_attribute (label); - sprintf (label, BODY_BEGIN_LABEL_FMT, current_funcdef_number); - body_begin_attribute (label); - sprintf (label, BODY_END_LABEL_FMT, current_funcdef_number); - body_end_attribute (label); - } - } -} - -/* Output a DIE to represent a declared data object (either file-scope - or block-local) which has "external linkage" (according to ANSI-C). */ - -static void -output_global_variable_die (arg) - register void *arg; -{ - register tree decl = arg; - register tree origin = decl_ultimate_origin (decl); - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_global_variable); - sibling_attribute (); - if (origin != NULL) - abstract_origin_attribute (origin); - else - { - name_and_src_coords_attributes (decl); - member_attribute (DECL_CONTEXT (decl)); - type_attribute (TREE_TYPE (decl), - TREE_READONLY (decl), TREE_THIS_VOLATILE (decl)); - } - if (DECL_ABSTRACT (decl)) - equate_decl_number_to_die_number (decl); - else - { - if (!DECL_EXTERNAL (decl)) - location_or_const_value_attribute (decl); - } -} - -static void -output_label_die (arg) - register void *arg; -{ - register tree decl = arg; - register tree origin = decl_ultimate_origin (decl); - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_label); - sibling_attribute (); - if (origin != NULL) - abstract_origin_attribute (origin); - else - name_and_src_coords_attributes (decl); - if (DECL_ABSTRACT (decl)) - equate_decl_number_to_die_number (decl); - else - { - register rtx insn = DECL_RTL (decl); - - if (GET_CODE (insn) == CODE_LABEL) - { - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - /* When optimization is enabled (via -O) some parts of the compiler - (e.g. jump.c and cse.c) may try to delete CODE_LABEL insns which - represent source-level labels which were explicitly declared by - the user. This really shouldn't be happening though, so catch - it if it ever does happen. */ - - if (INSN_DELETED_P (insn)) - abort (); /* Should never happen. */ - - sprintf (label, INSN_LABEL_FMT, current_funcdef_number, - (unsigned) INSN_UID (insn)); - low_pc_attribute (label); - } - } -} - -static void -output_lexical_block_die (arg) - register void *arg; -{ - register tree stmt = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_lexical_block); - sibling_attribute (); - dienum_push (); - if (! BLOCK_ABSTRACT (stmt)) - { - char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; - char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; - - sprintf (begin_label, BLOCK_BEGIN_LABEL_FMT, next_block_number); - low_pc_attribute (begin_label); - sprintf (end_label, BLOCK_END_LABEL_FMT, next_block_number); - high_pc_attribute (end_label); - } -} - -static void -output_inlined_subroutine_die (arg) - register void *arg; -{ - register tree stmt = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_inlined_subroutine); - sibling_attribute (); - dienum_push (); - abstract_origin_attribute (block_ultimate_origin (stmt)); - if (! BLOCK_ABSTRACT (stmt)) - { - char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; - char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; - - sprintf (begin_label, BLOCK_BEGIN_LABEL_FMT, next_block_number); - low_pc_attribute (begin_label); - sprintf (end_label, BLOCK_END_LABEL_FMT, next_block_number); - high_pc_attribute (end_label); - } -} - -/* Output a DIE to represent a declared data object (either file-scope - or block-local) which has "internal linkage" (according to ANSI-C). */ - -static void -output_local_variable_die (arg) - register void *arg; -{ - register tree decl = arg; - register tree origin = decl_ultimate_origin (decl); - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_local_variable); - sibling_attribute (); - if (origin != NULL) - abstract_origin_attribute (origin); - else - { - name_and_src_coords_attributes (decl); - member_attribute (DECL_CONTEXT (decl)); - type_attribute (TREE_TYPE (decl), - TREE_READONLY (decl), TREE_THIS_VOLATILE (decl)); - } - if (DECL_ABSTRACT (decl)) - equate_decl_number_to_die_number (decl); - else - location_or_const_value_attribute (decl); -} - -static void -output_member_die (arg) - register void *arg; -{ - register tree decl = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_member); - sibling_attribute (); - name_and_src_coords_attributes (decl); - member_attribute (DECL_CONTEXT (decl)); - type_attribute (member_declared_type (decl), - TREE_READONLY (decl), TREE_THIS_VOLATILE (decl)); - if (DECL_BIT_FIELD_TYPE (decl)) /* If this is a bit field... */ - { - byte_size_attribute (decl); - bit_size_attribute (decl); - bit_offset_attribute (decl); - } - data_member_location_attribute (decl); -} - -#if 0 -/* Don't generate either pointer_type DIEs or reference_type DIEs. Use - modified types instead. - - We keep this code here just in case these types of DIEs may be needed - to represent certain things in other languages (e.g. Pascal) someday. -*/ - -static void -output_pointer_type_die (arg) - register void *arg; -{ - register tree type = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_pointer_type); - sibling_attribute (); - equate_type_number_to_die_number (type); - member_attribute (TYPE_CONTEXT (type)); - type_attribute (TREE_TYPE (type), 0, 0); -} - -static void -output_reference_type_die (arg) - register void *arg; -{ - register tree type = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_reference_type); - sibling_attribute (); - equate_type_number_to_die_number (type); - member_attribute (TYPE_CONTEXT (type)); - type_attribute (TREE_TYPE (type), 0, 0); -} -#endif - -static void -output_ptr_to_mbr_type_die (arg) - register void *arg; -{ - register tree type = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_ptr_to_member_type); - sibling_attribute (); - equate_type_number_to_die_number (type); - member_attribute (TYPE_CONTEXT (type)); - containing_type_attribute (TYPE_OFFSET_BASETYPE (type)); - type_attribute (TREE_TYPE (type), 0, 0); -} - -static void -output_compile_unit_die (arg) - register void *arg; -{ - register char *main_input_filename = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_compile_unit); - sibling_attribute (); - dienum_push (); - name_attribute (main_input_filename); - - { - char producer[250]; - - sprintf (producer, "%s %s", language_string, version_string); - producer_attribute (producer); - } - - if (strcmp (language_string, "GNU C++") == 0) - language_attribute (LANG_C_PLUS_PLUS); - else if (flag_traditional) - language_attribute (LANG_C); - else - language_attribute (LANG_C89); - low_pc_attribute (TEXT_BEGIN_LABEL); - high_pc_attribute (TEXT_END_LABEL); - if (debug_info_level >= DINFO_LEVEL_NORMAL) - stmt_list_attribute (LINE_BEGIN_LABEL); - last_filename = xstrdup (main_input_filename); - - { - char *wd = getpwd (); - if (wd) - comp_dir_attribute (wd); - } - - if (debug_info_level >= DINFO_LEVEL_NORMAL) - { - sf_names_attribute (SFNAMES_BEGIN_LABEL); - src_info_attribute (SRCINFO_BEGIN_LABEL); - if (debug_info_level >= DINFO_LEVEL_VERBOSE) - mac_info_attribute (MACINFO_BEGIN_LABEL); - } -} - -static void -output_string_type_die (arg) - register void *arg; -{ - register tree type = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_string_type); - sibling_attribute (); - member_attribute (TYPE_CONTEXT (type)); - - /* Fudge the string length attribute for now. */ - - string_length_attribute (TYPE_MAX_VALUE (TYPE_DOMAIN (type))); -} - -static void -output_structure_type_die (arg) - register void *arg; -{ - register tree type = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_structure_type); - sibling_attribute (); - equate_type_number_to_die_number (type); - name_attribute (type_tag (type)); - member_attribute (TYPE_CONTEXT (type)); - - /* If this type has been completed, then give it a byte_size attribute - and prepare to give a list of members. Otherwise, don't do either of - these things. In the latter case, we will not be generating a list - of members (since we don't have any idea what they might be for an - incomplete type). */ - - if (TYPE_SIZE (type)) - { - dienum_push (); - byte_size_attribute (type); - } -} - -/* Output a DIE to represent a declared function (either file-scope - or block-local) which has "internal linkage" (according to ANSI-C). */ - -static void -output_local_subroutine_die (arg) - register void *arg; -{ - register tree decl = arg; - register tree origin = decl_ultimate_origin (decl); - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_subroutine); - sibling_attribute (); - dienum_push (); - if (origin != NULL) - abstract_origin_attribute (origin); - else - { - register tree type = TREE_TYPE (decl); - - name_and_src_coords_attributes (decl); - inline_attribute (decl); - prototyped_attribute (type); - member_attribute (DECL_CONTEXT (decl)); - type_attribute (TREE_TYPE (type), 0, 0); - pure_or_virtual_attribute (decl); - } - if (DECL_ABSTRACT (decl)) - equate_decl_number_to_die_number (decl); - else - { - /* Avoid getting screwed up in cases where a function was declared - static but where no definition was ever given for it. */ - - if (TREE_ASM_WRITTEN (decl)) - { - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - low_pc_attribute (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); - sprintf (label, FUNC_END_LABEL_FMT, current_funcdef_number); - high_pc_attribute (label); - sprintf (label, BODY_BEGIN_LABEL_FMT, current_funcdef_number); - body_begin_attribute (label); - sprintf (label, BODY_END_LABEL_FMT, current_funcdef_number); - body_end_attribute (label); - } - } -} - -static void -output_subroutine_type_die (arg) - register void *arg; -{ - register tree type = arg; - register tree return_type = TREE_TYPE (type); - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_subroutine_type); - sibling_attribute (); - dienum_push (); - equate_type_number_to_die_number (type); - prototyped_attribute (type); - member_attribute (TYPE_CONTEXT (type)); - type_attribute (return_type, 0, 0); -} - -static void -output_typedef_die (arg) - register void *arg; -{ - register tree decl = arg; - register tree origin = decl_ultimate_origin (decl); - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_typedef); - sibling_attribute (); - if (origin != NULL) - abstract_origin_attribute (origin); - else - { - name_and_src_coords_attributes (decl); - member_attribute (DECL_CONTEXT (decl)); - type_attribute (TREE_TYPE (decl), - TREE_READONLY (decl), TREE_THIS_VOLATILE (decl)); - } - if (DECL_ABSTRACT (decl)) - equate_decl_number_to_die_number (decl); -} - -static void -output_union_type_die (arg) - register void *arg; -{ - register tree type = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_union_type); - sibling_attribute (); - equate_type_number_to_die_number (type); - name_attribute (type_tag (type)); - member_attribute (TYPE_CONTEXT (type)); - - /* If this type has been completed, then give it a byte_size attribute - and prepare to give a list of members. Otherwise, don't do either of - these things. In the latter case, we will not be generating a list - of members (since we don't have any idea what they might be for an - incomplete type). */ - - if (TYPE_SIZE (type)) - { - dienum_push (); - byte_size_attribute (type); - } -} - -/* Generate a special type of DIE used as a stand-in for a trailing ellipsis - at the end of an (ANSI prototyped) formal parameters list. */ - -static void -output_unspecified_parameters_die (arg) - register void *arg; -{ - register tree decl_or_type = arg; - - ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_unspecified_parameters); - sibling_attribute (); - - /* This kludge is here only for the sake of being compatible with what - the USL CI5 C compiler does. The specification of Dwarf Version 1 - doesn't say that TAG_unspecified_parameters DIEs should contain any - attributes other than the AT_sibling attribute, but they are certainly - allowed to contain additional attributes, and the CI5 compiler - generates AT_name, AT_fund_type, and AT_location attributes within - TAG_unspecified_parameters DIEs which appear in the child lists for - DIEs representing function definitions, so we do likewise here. */ - - if (TREE_CODE (decl_or_type) == FUNCTION_DECL && DECL_INITIAL (decl_or_type)) - { - name_attribute ("..."); - fund_type_attribute (FT_pointer); - /* location_attribute (?); */ - } -} - -static void -output_padded_null_die (arg) - register void *arg; -{ - ASM_OUTPUT_ALIGN (asm_out_file, 2); /* 2**2 == 4 */ -} - -/*************************** end of DIEs *********************************/ - -/* Generate some type of DIE. This routine generates the generic outer - wrapper stuff which goes around all types of DIE's (regardless of their - TAGs. All forms of DIEs start with a DIE-specific label, followed by a - DIE-length word, followed by the guts of the DIE itself. After the guts - of the DIE, there must always be a terminator label for the DIE. */ - -static void -output_die (die_specific_output_function, param) - register void (*die_specific_output_function)(); - register void *param; -{ - char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; - char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; - - current_dienum = NEXT_DIE_NUM; - NEXT_DIE_NUM = next_unused_dienum; - - sprintf (begin_label, DIE_BEGIN_LABEL_FMT, current_dienum); - sprintf (end_label, DIE_END_LABEL_FMT, current_dienum); - - /* Write a label which will act as the name for the start of this DIE. */ - - ASM_OUTPUT_LABEL (asm_out_file, begin_label); - - /* Write the DIE-length word. */ - - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, end_label, begin_label); - - /* Fill in the guts of the DIE. */ - - next_unused_dienum++; - die_specific_output_function (param); - - /* Write a label which will act as the name for the end of this DIE. */ - - ASM_OUTPUT_LABEL (asm_out_file, end_label); -} - -static void -end_sibling_chain () -{ - char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; - - current_dienum = NEXT_DIE_NUM; - NEXT_DIE_NUM = next_unused_dienum; - - sprintf (begin_label, DIE_BEGIN_LABEL_FMT, current_dienum); - - /* Write a label which will act as the name for the start of this DIE. */ - - ASM_OUTPUT_LABEL (asm_out_file, begin_label); - - /* Write the DIE-length word. */ - - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 4); - - dienum_pop (); -} - -/* Generate a list of nameless TAG_formal_parameter DIEs (and perhaps a - TAG_unspecified_parameters DIE) to represent the types of the formal - parameters as specified in some function type specification (except - for those which appear as part of a function *definition*). - - Note that we must be careful here to output all of the parameter DIEs - *before* we output any DIEs needed to represent the types of the formal - parameters. This keeps svr4 SDB happy because it (incorrectly) thinks - that the first non-parameter DIE it sees ends the formal parameter list. -*/ - -static void -output_formal_types (function_or_method_type) - register tree function_or_method_type; -{ - register tree link; - register tree formal_type = NULL; - register tree first_parm_type = TYPE_ARG_TYPES (function_or_method_type); - - /* In the case where we are generating a formal types list for a C++ - non-static member function type, skip over the first thing on the - TYPE_ARG_TYPES list because it only represents the type of the - hidden `this pointer'. The debugger should be able to figure - out (without being explicitly told) that this non-static member - function type takes a `this pointer' and should be able to figure - what the type of that hidden parameter is from the AT_member - attribute of the parent TAG_subroutine_type DIE. */ - - if (TREE_CODE (function_or_method_type) == METHOD_TYPE) - first_parm_type = TREE_CHAIN (first_parm_type); - - /* Make our first pass over the list of formal parameter types and output - a TAG_formal_parameter DIE for each one. */ - - for (link = first_parm_type; link; link = TREE_CHAIN (link)) - { - formal_type = TREE_VALUE (link); - if (formal_type == void_type_node) - break; - - /* Output a (nameless) DIE to represent the formal parameter itself. */ - - output_die (output_formal_parameter_die, formal_type); - } - - /* If this function type has an ellipsis, add a TAG_unspecified_parameters - DIE to the end of the parameter list. */ - - if (formal_type != void_type_node) - output_die (output_unspecified_parameters_die, function_or_method_type); - - /* Make our second (and final) pass over the list of formal parameter types - and output DIEs to represent those types (as necessary). */ - - for (link = TYPE_ARG_TYPES (function_or_method_type); - link; - link = TREE_CHAIN (link)) - { - formal_type = TREE_VALUE (link); - if (formal_type == void_type_node) - break; - - output_type (formal_type, function_or_method_type); - } -} - -/* Remember a type in the pending_types_list. */ - -static void -pend_type (type) - register tree type; -{ - if (pending_types == pending_types_allocated) - { - pending_types_allocated += PENDING_TYPES_INCREMENT; - pending_types_list - = (tree *) xrealloc (pending_types_list, - sizeof (tree) * pending_types_allocated); - } - pending_types_list[pending_types++] = type; - - /* Mark the pending type as having been output already (even though - it hasn't been). This prevents the type from being added to the - pending_types_list more than once. */ - - TREE_ASM_WRITTEN (type) = 1; -} - -/* Return non-zero if it is legitimate to output DIEs to represent a - given type while we are generating the list of child DIEs for some - DIE (e.g. a function or lexical block DIE) associated with a given scope. - - See the comments within the function for a description of when it is - considered legitimate to output DIEs for various kinds of types. - - Note that TYPE_CONTEXT(type) may be NULL (to indicate global scope) - or it may point to a BLOCK node (for types local to a block), or to a - FUNCTION_DECL node (for types local to the heading of some function - definition), or to a FUNCTION_TYPE node (for types local to the - prototyped parameter list of a function type specification), or to a - RECORD_TYPE, UNION_TYPE, or QUAL_UNION_TYPE node - (in the case of C++ nested types). - - The `scope' parameter should likewise be NULL or should point to a - BLOCK node, a FUNCTION_DECL node, a FUNCTION_TYPE node, a RECORD_TYPE - node, a UNION_TYPE node, or a QUAL_UNION_TYPE node. - - This function is used only for deciding when to "pend" and when to - "un-pend" types to/from the pending_types_list. - - Note that we sometimes make use of this "type pending" feature in a - rather twisted way to temporarily delay the production of DIEs for the - types of formal parameters. (We do this just to make svr4 SDB happy.) - It order to delay the production of DIEs representing types of formal - parameters, callers of this function supply `fake_containing_scope' as - the `scope' parameter to this function. Given that fake_containing_scope - is a tagged type which is *not* the containing scope for *any* other type, - the desired effect is achieved, i.e. output of DIEs representing types - is temporarily suspended, and any type DIEs which would have otherwise - been output are instead placed onto the pending_types_list. Later on, - we force these (temporarily pended) types to be output simply by calling - `output_pending_types_for_scope' with an actual argument equal to the - true scope of the types we temporarily pended. -*/ - -inline int -type_ok_for_scope (type, scope) - register tree type; - register tree scope; -{ - /* Tagged types (i.e. struct, union, and enum types) must always be - output only in the scopes where they actually belong (or else the - scoping of their own tag names and the scoping of their member - names will be incorrect). Non-tagged-types on the other hand can - generally be output anywhere, except that svr4 SDB really doesn't - want to see them nested within struct or union types, so here we - say it is always OK to immediately output any such a (non-tagged) - type, so long as we are not within such a context. Note that the - only kinds of non-tagged types which we will be dealing with here - (for C and C++ anyway) will be array types and function types. */ - - return is_tagged_type (type) - ? (TYPE_CONTEXT (type) == scope) - : (scope == NULL_TREE || ! is_tagged_type (scope)); -} - -/* Output any pending types (from the pending_types list) which we can output - now (taking into account the scope that we are working on now). - - For each type output, remove the given type from the pending_types_list - *before* we try to output it. - - Note that we have to process the list in beginning-to-end order, - because the call made here to output_type may cause yet more types - to be added to the end of the list, and we may have to output some - of them too. -*/ - -static void -output_pending_types_for_scope (containing_scope) - register tree containing_scope; -{ - register unsigned i; - - for (i = 0; i < pending_types; ) - { - register tree type = pending_types_list[i]; - - if (type_ok_for_scope (type, containing_scope)) - { - register tree *mover; - register tree *limit; - - pending_types--; - limit = &pending_types_list[pending_types]; - for (mover = &pending_types_list[i]; mover < limit; mover++) - *mover = *(mover+1); - - /* Un-mark the type as having been output already (because it - hasn't been, really). Then call output_type to generate a - Dwarf representation of it. */ - - TREE_ASM_WRITTEN (type) = 0; - output_type (type, containing_scope); - - /* Don't increment the loop counter in this case because we - have shifted all of the subsequent pending types down one - element in the pending_types_list array. */ - } - else - i++; - } -} - -static void -output_type (type, containing_scope) - register tree type; - register tree containing_scope; -{ - if (type == 0 || type == error_mark_node) - return; - - /* We are going to output a DIE to represent the unqualified version of - of this type (i.e. without any const or volatile qualifiers) so get - the main variant (i.e. the unqualified version) of this type now. */ - - type = TYPE_MAIN_VARIANT (type); - - if (TREE_ASM_WRITTEN (type)) - return; - - /* Don't generate any DIEs for this type now unless it is OK to do so - (based upon what `type_ok_for_scope' tells us). */ - - if (! type_ok_for_scope (type, containing_scope)) - { - pend_type (type); - return; - } - - switch (TREE_CODE (type)) - { - case ERROR_MARK: - break; - - case POINTER_TYPE: - case REFERENCE_TYPE: - /* For these types, all that is required is that we output a DIE - (or a set of DIEs) to represent the "basis" type. */ - output_type (TREE_TYPE (type), containing_scope); - break; - - case OFFSET_TYPE: - /* This code is used for C++ pointer-to-data-member types. */ - /* Output a description of the relevant class type. */ - output_type (TYPE_OFFSET_BASETYPE (type), containing_scope); - /* Output a description of the type of the object pointed to. */ - output_type (TREE_TYPE (type), containing_scope); - /* Now output a DIE to represent this pointer-to-data-member type - itself. */ - output_die (output_ptr_to_mbr_type_die, type); - break; - - case SET_TYPE: - output_type (TREE_TYPE (type), containing_scope); - output_die (output_set_type_die, type); - break; - - case FILE_TYPE: - output_type (TREE_TYPE (type), containing_scope); - abort (); /* No way to represent these in Dwarf yet! */ - break; - - case STRING_TYPE: - output_type (TREE_TYPE (type), containing_scope); - output_die (output_string_type_die, type); - break; - - case FUNCTION_TYPE: - /* Force out return type (in case it wasn't forced out already). */ - output_type (TREE_TYPE (type), containing_scope); - output_die (output_subroutine_type_die, type); - output_formal_types (type); - end_sibling_chain (); - break; - - case METHOD_TYPE: - /* Force out return type (in case it wasn't forced out already). */ - output_type (TREE_TYPE (type), containing_scope); - output_die (output_subroutine_type_die, type); - output_formal_types (type); - end_sibling_chain (); - break; - - case ARRAY_TYPE: - { - register tree element_type; - - element_type = TREE_TYPE (type); - while (TREE_CODE (element_type) == ARRAY_TYPE) - element_type = TREE_TYPE (element_type); - - output_type (element_type, containing_scope); - output_die (output_array_type_die, type); - } - break; - - case ENUMERAL_TYPE: - case RECORD_TYPE: - case UNION_TYPE: - case QUAL_UNION_TYPE: - - /* For a non-file-scope tagged type, we can always go ahead and - output a Dwarf description of this type right now, even if - the type in question is still incomplete, because if this - local type *was* ever completed anywhere within its scope, - that complete definition would already have been attached to - this RECORD_TYPE, UNION_TYPE, QUAL_UNION_TYPE or ENUMERAL_TYPE - node by the time we reach this point. That's true because of the - way the front-end does its processing of file-scope declarations (of - functions and class types) within which other types might be - nested. The C and C++ front-ends always gobble up such "local - scope" things en-mass before they try to output *any* debugging - information for any of the stuff contained inside them and thus, - we get the benefit here of what is (in effect) a pre-resolution - of forward references to tagged types in local scopes. - - Note however that for file-scope tagged types we cannot assume - that such pre-resolution of forward references has taken place. - A given file-scope tagged type may appear to be incomplete when - we reach this point, but it may yet be given a full definition - (at file-scope) later on during compilation. In order to avoid - generating a premature (and possibly incorrect) set of Dwarf - DIEs for such (as yet incomplete) file-scope tagged types, we - generate nothing at all for as-yet incomplete file-scope tagged - types here unless we are making our special "finalization" pass - for file-scope things at the very end of compilation. At that - time, we will certainly know as much about each file-scope tagged - type as we are ever going to know, so at that point in time, we - can safely generate correct Dwarf descriptions for these file- - scope tagged types. - */ - - if (TYPE_SIZE (type) == 0 && TYPE_CONTEXT (type) == NULL && !finalizing) - return; /* EARLY EXIT! Avoid setting TREE_ASM_WRITTEN. */ - - /* Prevent infinite recursion in cases where the type of some - member of this type is expressed in terms of this type itself. */ - - TREE_ASM_WRITTEN (type) = 1; - - /* Output a DIE to represent the tagged type itself. */ - - switch (TREE_CODE (type)) - { - case ENUMERAL_TYPE: - output_die (output_enumeration_type_die, type); - return; /* a special case -- nothing left to do so just return */ - - case RECORD_TYPE: - output_die (output_structure_type_die, type); - break; - - case UNION_TYPE: - case QUAL_UNION_TYPE: - output_die (output_union_type_die, type); - break; - - default: - abort (); /* Should never happen. */ - } - - /* If this is not an incomplete type, output descriptions of - each of its members. - - Note that as we output the DIEs necessary to represent the - members of this record or union type, we will also be trying - to output DIEs to represent the *types* of those members. - However the `output_type' function (above) will specifically - avoid generating type DIEs for member types *within* the list - of member DIEs for this (containing) type execpt for those - types (of members) which are explicitly marked as also being - members of this (containing) type themselves. The g++ front- - end can force any given type to be treated as a member of some - other (containing) type by setting the TYPE_CONTEXT of the - given (member) type to point to the TREE node representing the - appropriate (containing) type. - */ - - if (TYPE_SIZE (type)) - { - { - register tree normal_member; - - /* First output info about the data members and type members. */ - - for (normal_member = TYPE_FIELDS (type); - normal_member; - normal_member = TREE_CHAIN (normal_member)) - output_decl (normal_member, type); - } - - { - register tree vec_base; - - /* Now output info about the function members (if any). */ - - vec_base = TYPE_METHODS (type); - if (vec_base) - { - register tree first_func_member = TREE_VEC_ELT (vec_base, 0); - register tree func_member; - - /* This isn't documented, but the first element of the - vector of member functions can be NULL in cases where - the class type in question didn't have either a - constructor or a destructor declared for it. We have - to make allowances for that here. */ - - if (first_func_member == NULL) - first_func_member = TREE_VEC_ELT (vec_base, 1); - - for (func_member = first_func_member; - func_member; - func_member = TREE_CHAIN (func_member)) - output_decl (func_member, type); - } - } - - /* RECORD_TYPEs, UNION_TYPEs, and QUAL_UNION_TYPEs are themselves - scopes (at least in C++) so we must now output any nested - pending types which are local just to this type. */ - - output_pending_types_for_scope (type); - - end_sibling_chain (); /* Terminate member chain. */ - } - - break; - - case VOID_TYPE: - case INTEGER_TYPE: - case REAL_TYPE: - case COMPLEX_TYPE: - case BOOLEAN_TYPE: - case CHAR_TYPE: - break; /* No DIEs needed for fundamental types. */ - - case LANG_TYPE: /* No Dwarf representation currently defined. */ - break; - - default: - abort (); - } - - TREE_ASM_WRITTEN (type) = 1; -} - -static void -output_tagged_type_instantiation (type) - register tree type; -{ - if (type == 0 || type == error_mark_node) - return; - - /* We are going to output a DIE to represent the unqualified version of - of this type (i.e. without any const or volatile qualifiers) so make - sure that we have the main variant (i.e. the unqualified version) of - this type now. */ - - assert (type == TYPE_MAIN_VARIANT (type)); - - assert (TREE_ASM_WRITTEN (type)); - - switch (TREE_CODE (type)) - { - case ERROR_MARK: - break; - - case ENUMERAL_TYPE: - output_die (output_inlined_enumeration_type_die, type); - break; - - case RECORD_TYPE: - output_die (output_inlined_structure_type_die, type); - break; - - case UNION_TYPE: - case QUAL_UNION_TYPE: - output_die (output_inlined_union_type_die, type); - break; - - default: - abort (); /* Should never happen. */ - } -} - -/* Output a TAG_lexical_block DIE followed by DIEs to represent all of - the things which are local to the given block. */ - -static void -output_block (stmt) - register tree stmt; -{ - register int must_output_die = 0; - register tree origin; - register enum tree_code origin_code; - - /* Ignore blocks never really used to make RTL. */ - - if (! stmt || ! TREE_USED (stmt)) - return; - - /* Determine the "ultimate origin" of this block. This block may be an - inlined instance of an inlined instance of inline function, so we - have to trace all of the way back through the origin chain to find - out what sort of node actually served as the original seed for the - creation of the current block. */ - - origin = block_ultimate_origin (stmt); - origin_code = (origin != NULL) ? TREE_CODE (origin) : ERROR_MARK; - - /* Determine if we need to output any Dwarf DIEs at all to represent this - block. */ - - if (origin_code == FUNCTION_DECL) - /* The outer scopes for inlinings *must* always be represented. We - generate TAG_inlined_subroutine DIEs for them. (See below.) */ - must_output_die = 1; - else - { - /* In the case where the current block represents an inlining of the - "body block" of an inline function, we must *NOT* output any DIE - for this block because we have already output a DIE to represent - the whole inlined function scope and the "body block" of any - function doesn't really represent a different scope according to - ANSI C rules. So we check here to make sure that this block does - not represent a "body block inlining" before trying to set the - `must_output_die' flag. */ - - if (origin == NULL || ! is_body_block (origin)) - { - /* Determine if this block directly contains any "significant" - local declarations which we will need to output DIEs for. */ - - if (debug_info_level > DINFO_LEVEL_TERSE) - /* We are not in terse mode so *any* local declaration counts - as being a "significant" one. */ - must_output_die = (BLOCK_VARS (stmt) != NULL); - else - { - register tree decl; - - /* We are in terse mode, so only local (nested) function - definitions count as "significant" local declarations. */ - - for (decl = BLOCK_VARS (stmt); decl; decl = TREE_CHAIN (decl)) - if (TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl)) - { - must_output_die = 1; - break; - } - } - } - } - - /* It would be a waste of space to generate a Dwarf TAG_lexical_block - DIE for any block which contains no significant local declarations - at all. Rather, in such cases we just call `output_decls_for_scope' - so that any needed Dwarf info for any sub-blocks will get properly - generated. Note that in terse mode, our definition of what constitutes - a "significant" local declaration gets restricted to include only - inlined function instances and local (nested) function definitions. */ - - if (must_output_die) - { - output_die ((origin_code == FUNCTION_DECL) - ? output_inlined_subroutine_die - : output_lexical_block_die, - stmt); - output_decls_for_scope (stmt); - end_sibling_chain (); - } - else - output_decls_for_scope (stmt); -} - -/* Output all of the decls declared within a given scope (also called - a `binding contour') and (recursively) all of it's sub-blocks. */ - -static void -output_decls_for_scope (stmt) - register tree stmt; -{ - /* Ignore blocks never really used to make RTL. */ - - if (! stmt || ! TREE_USED (stmt)) - return; - - if (! BLOCK_ABSTRACT (stmt)) - next_block_number++; - - /* Output the DIEs to represent all of the data objects, functions, - typedefs, and tagged types declared directly within this block - but not within any nested sub-blocks. */ - - { - register tree decl; - - for (decl = BLOCK_VARS (stmt); decl; decl = TREE_CHAIN (decl)) - output_decl (decl, stmt); - } - - output_pending_types_for_scope (stmt); - - /* Output the DIEs to represent all sub-blocks (and the items declared - therein) of this block. */ - - { - register tree subblocks; - - for (subblocks = BLOCK_SUBBLOCKS (stmt); - subblocks; - subblocks = BLOCK_CHAIN (subblocks)) - output_block (subblocks); - } -} - -/* Output Dwarf .debug information for a decl described by DECL. */ - -static void -output_decl (decl, containing_scope) - register tree decl; - register tree containing_scope; -{ - /* Make a note of the decl node we are going to be working on. We may - need to give the user the source coordinates of where it appeared in - case we notice (later on) that something about it looks screwy. */ - - dwarf_last_decl = decl; - - if (TREE_CODE (decl) == ERROR_MARK) - return; - - /* If this ..._DECL node is marked to be ignored, then ignore it. - But don't ignore a function definition, since that would screw - up our count of blocks, and that it turn will completely screw up the - the labels we will reference in subsequent AT_low_pc and AT_high_pc - attributes (for subsequent blocks). */ - - if (DECL_IGNORED_P (decl) && TREE_CODE (decl) != FUNCTION_DECL) - return; - - switch (TREE_CODE (decl)) - { - case CONST_DECL: - /* The individual enumerators of an enum type get output when we - output the Dwarf representation of the relevant enum type itself. */ - break; - - case FUNCTION_DECL: - /* If we are in terse mode, don't output any DIEs to represent - mere function declarations. Also, if we are conforming - to the DWARF version 1 specification, don't output DIEs for - mere function declarations. */ - - if (DECL_INITIAL (decl) == NULL_TREE) -#if (DWARF_VERSION > 1) - if (debug_info_level <= DINFO_LEVEL_TERSE) -#endif - break; - - /* Before we describe the FUNCTION_DECL itself, make sure that we - have described its return type. */ - - output_type (TREE_TYPE (TREE_TYPE (decl)), containing_scope); - - /* If the following DIE will represent a function definition for a - function with "extern" linkage, output a special "pubnames" DIE - label just ahead of the actual DIE. A reference to this label - was already generated in the .debug_pubnames section sub-entry - for this function definition. */ - - if (TREE_PUBLIC (decl)) - { - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - sprintf (label, PUB_DIE_LABEL_FMT, next_pubname_number++); - ASM_OUTPUT_LABEL (asm_out_file, label); - } - - /* Now output a DIE to represent the function itself. */ - - output_die (TREE_PUBLIC (decl) || DECL_EXTERNAL (decl) - ? output_global_subroutine_die - : output_local_subroutine_die, - decl); - - /* Now output descriptions of the arguments for this function. - This gets (unnecessarily?) complex because of the fact that - the DECL_ARGUMENT list for a FUNCTION_DECL doesn't indicate - cases where there was a trailing `...' at the end of the formal - parameter list. In order to find out if there was a trailing - ellipsis or not, we must instead look at the type associated - with the FUNCTION_DECL. This will be a node of type FUNCTION_TYPE. - If the chain of type nodes hanging off of this FUNCTION_TYPE node - ends with a void_type_node then there should *not* be an ellipsis - at the end. */ - - /* In the case where we are describing a mere function declaration, all - we need to do here (and all we *can* do here) is to describe - the *types* of its formal parameters. */ - - if (DECL_INITIAL (decl) == NULL_TREE) - output_formal_types (TREE_TYPE (decl)); - else - { - register tree arg_decls = DECL_ARGUMENTS (decl); - - { - register tree last_arg; - - last_arg = (arg_decls && TREE_CODE (arg_decls) != ERROR_MARK) - ? tree_last (arg_decls) - : NULL; - - /* Generate DIEs to represent all known formal parameters, but - don't do it if this looks like a varargs function. A given - function is considered to be a varargs function if (and only - if) its last named argument is named `__builtin_va_alist'. */ - - if (! last_arg - || ! DECL_NAME (last_arg) - || strcmp (IDENTIFIER_POINTER (DECL_NAME (last_arg)), - "__builtin_va_alist")) - { - register tree parm; - - /* WARNING! Kludge zone ahead! Here we have a special - hack for svr4 SDB compatibility. Instead of passing the - current FUNCTION_DECL node as the second parameter (i.e. - the `containing_scope' parameter) to `output_decl' (as - we ought to) we instead pass a pointer to our own private - fake_containing_scope node. That node is a RECORD_TYPE - node which NO OTHER TYPE may ever actually be a member of. - - This pointer will ultimately get passed into `output_type' - as its `containing_scope' parameter. `Output_type' will - then perform its part in the hack... i.e. it will pend - the type of the formal parameter onto the pending_types - list. Later on, when we are done generating the whole - sequence of formal parameter DIEs for this function - definition, we will un-pend all previously pended types - of formal parameters for this function definition. - - This whole kludge prevents any type DIEs from being - mixed in with the formal parameter DIEs. That's good - because svr4 SDB believes that the list of formal - parameter DIEs for a function ends wherever the first - non-formal-parameter DIE appears. Thus, we have to - keep the formal parameter DIEs segregated. They must - all appear (consecutively) at the start of the list of - children for the DIE representing the function definition. - Then (and only then) may we output any additional DIEs - needed to represent the types of these formal parameters. - */ - - for (parm = arg_decls; parm; parm = TREE_CHAIN (parm)) - if (TREE_CODE (parm) == PARM_DECL) - output_decl (parm, fake_containing_scope); - - /* Now that we have finished generating all of the DIEs to - represent the formal parameters themselves, force out - any DIEs needed to represent their types. We do this - simply by un-pending all previously pended types which - can legitimately go into the chain of children DIEs for - the current FUNCTION_DECL. */ - - output_pending_types_for_scope (decl); - } - } - - /* Now try to decide if we should put an ellipsis at the end. */ - - { - register int has_ellipsis = TRUE; /* default assumption */ - register tree fn_arg_types = TYPE_ARG_TYPES (TREE_TYPE (decl)); - - if (fn_arg_types) - { - /* This function declaration/definition was prototyped. */ - - /* If the list of formal argument types ends with a - void_type_node, then the formals list did *not* end - with an ellipsis. */ - - if (TREE_VALUE (tree_last (fn_arg_types)) == void_type_node) - has_ellipsis = FALSE; - } - else - { - /* This function declaration/definition was not prototyped. */ - - /* Note that all non-prototyped function *declarations* are - assumed to represent varargs functions (until proven - otherwise). */ - - if (DECL_INITIAL (decl)) /* if this is a func definition */ - { - if (!arg_decls) - has_ellipsis = FALSE; /* no args == (void) */ - else - { - /* For a non-prototyped function definition which - declares one or more formal parameters, if the name - of the first formal parameter is *not* - __builtin_va_alist then we must assume that this - is *not* a varargs function. */ - - if (DECL_NAME (arg_decls) - && strcmp (IDENTIFIER_POINTER (DECL_NAME (arg_decls)), - "__builtin_va_alist")) - has_ellipsis = FALSE; - } - } - } - - if (has_ellipsis) - output_die (output_unspecified_parameters_die, decl); - } - } - - /* Output Dwarf info for all of the stuff within the body of the - function (if it has one - it may be just a declaration). */ - - { - register tree outer_scope = DECL_INITIAL (decl); - - if (outer_scope && TREE_CODE (outer_scope) != ERROR_MARK) - { - /* Note that here, `outer_scope' is a pointer to the outermost - BLOCK node created to represent a function. - This outermost BLOCK actually represents the outermost - binding contour for the function, i.e. the contour in which - the function's formal parameters and labels get declared. - - Curiously, it appears that the front end doesn't actually - put the PARM_DECL nodes for the current function onto the - BLOCK_VARS list for this outer scope. (They are strung - off of the DECL_ARGUMENTS list for the function instead.) - The BLOCK_VARS list for the `outer_scope' does provide us - with a list of the LABEL_DECL nodes for the function however, - and we output DWARF info for those here. - - Just within the `outer_scope' there will be another BLOCK - node representing the function's outermost pair of curly - braces. We musn't generate a lexical_block DIE for this - outermost pair of curly braces because that is not really an - independent scope according to ANSI C rules. Rather, it is - the same scope in which the parameters were declared. */ - - { - register tree label; - - for (label = BLOCK_VARS (outer_scope); - label; - label = TREE_CHAIN (label)) - output_decl (label, outer_scope); - } - - /* Note here that `BLOCK_SUBBLOCKS (outer_scope)' points to a - list of BLOCK nodes which is always only one element long. - That one element represents the outermost pair of curley - braces for the function body. */ - - output_decls_for_scope (BLOCK_SUBBLOCKS (outer_scope)); - - /* Finally, force out any pending types which are local to the - outermost block of this function definition. These will - all have a TYPE_CONTEXT which points to the FUNCTION_DECL - node itself. */ - - output_pending_types_for_scope (decl); - } - } - - /* Generate a terminator for the list of stuff `owned' by this - function. */ - - end_sibling_chain (); - - break; - - case TYPE_DECL: - /* If we are in terse mode, don't generate any DIEs to represent - any actual typedefs. Note that even when we are in terse mode, - we must still output DIEs to represent those tagged types which - are used (directly or indirectly) in the specification of either - a return type or a formal parameter type of some function. */ - - if (debug_info_level <= DINFO_LEVEL_TERSE) - if (DECL_NAME (decl) != NULL - || ! TYPE_USED_FOR_FUNCTION (TREE_TYPE (decl))) - return; - - /* In the special case of a null-named TYPE_DECL node (representing - the declaration of some type tag), if the given TYPE_DECL is - marked as having been instantiated from some other (original) - TYPE_DECL node (e.g. one which was generated within the original - definition of an inline function) we have to generate a special - (abbreviated) TAG_structure_type, TAG_union_type, or - TAG_enumeration-type DIE here. */ - - if (! DECL_NAME (decl) && DECL_ABSTRACT_ORIGIN (decl)) - { - output_tagged_type_instantiation (TREE_TYPE (decl)); - return; - } - - output_type (TREE_TYPE (decl), containing_scope); - - /* Note that unlike the gcc front end (which generates a NULL named - TYPE_DECL node for each complete tagged type, each array type, - and each function type node created) the g++ front end generates - a *named* TYPE_DECL node for each tagged type node created. - Unfortunately, these g++ TYPE_DECL nodes cause us to output many - superfluous and unnecessary TAG_typedef DIEs here. When g++ is - fixed to stop generating these superfluous named TYPE_DECL nodes, - the superfluous TAG_typedef DIEs will likewise cease. */ - - if (DECL_NAME (decl)) - /* Output a DIE to represent the typedef itself. */ - output_die (output_typedef_die, decl); - break; - - case LABEL_DECL: - if (debug_info_level >= DINFO_LEVEL_NORMAL) - output_die (output_label_die, decl); - break; - - case VAR_DECL: - /* If we are conforming to the DWARF version 1 specification, don't - generated any DIEs to represent mere external object declarations. */ - -#if (DWARF_VERSION <= 1) - if (DECL_EXTERNAL (decl) && ! TREE_PUBLIC (decl)) - break; -#endif - - /* If we are in terse mode, don't generate any DIEs to represent - any variable declarations or definitions. */ - - if (debug_info_level <= DINFO_LEVEL_TERSE) - break; - - /* Output any DIEs that are needed to specify the type of this data - object. */ - - output_type (TREE_TYPE (decl), containing_scope); - - /* If the following DIE will represent a data object definition for a - data object with "extern" linkage, output a special "pubnames" DIE - label just ahead of the actual DIE. A reference to this label - was already generated in the .debug_pubnames section sub-entry - for this data object definition. */ - - if (TREE_PUBLIC (decl) && ! DECL_ABSTRACT (decl)) - { - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - sprintf (label, PUB_DIE_LABEL_FMT, next_pubname_number++); - ASM_OUTPUT_LABEL (asm_out_file, label); - } - - /* Now output the DIE to represent the data object itself. This gets - complicated because of the possibility that the VAR_DECL really - represents an inlined instance of a formal parameter for an inline - function. */ - - { - register void (*func) (); - register tree origin = decl_ultimate_origin (decl); - - if (origin != NULL && TREE_CODE (origin) == PARM_DECL) - func = output_formal_parameter_die; - else - { - if (TREE_PUBLIC (decl) || DECL_EXTERNAL (decl)) - func = output_global_variable_die; - else - func = output_local_variable_die; - } - output_die (func, decl); - } - break; - - case FIELD_DECL: - /* Ignore the nameless fields that are used to skip bits. */ - if (DECL_NAME (decl) != 0) - { - output_type (member_declared_type (decl), containing_scope); - output_die (output_member_die, decl); - } - break; - - case PARM_DECL: - /* Force out the type of this formal, if it was not forced out yet. - Note that here we can run afowl of a bug in "classic" svr4 SDB. - It should be able to grok the presence of type DIEs within a list - of TAG_formal_parameter DIEs, but it doesn't. */ - - output_type (TREE_TYPE (decl), containing_scope); - output_die (output_formal_parameter_die, decl); - break; - - default: - abort (); - } -} - -void -dwarfout_file_scope_decl (decl, set_finalizing) - register tree decl; - register int set_finalizing; -{ - if (TREE_CODE (decl) == ERROR_MARK) - return; - - /* If this ..._DECL node is marked to be ignored, then ignore it. We - gotta hope that the node in question doesn't represent a function - definition. If it does, then totally ignoring it is bound to screw - up our count of blocks, and that it turn will completely screw up the - the labels we will reference in subsequent AT_low_pc and AT_high_pc - attributes (for subsequent blocks). (It's too bad that BLOCK nodes - don't carry their own sequence numbers with them!) */ - - if (DECL_IGNORED_P (decl)) - { - if (TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl) != NULL) - abort (); - return; - } - - switch (TREE_CODE (decl)) - { - case FUNCTION_DECL: - - /* Ignore this FUNCTION_DECL if it refers to a builtin declaration of - a builtin function. Explicit programmer-supplied declarations of - these same functions should NOT be ignored however. */ - - if (DECL_EXTERNAL (decl) && DECL_FUNCTION_CODE (decl)) - return; - - /* What we would really like to do here is to filter out all mere - file-scope declarations of file-scope functions which are never - referenced later within this translation unit (and keep all of - ones that *are* referenced later on) but we aren't clarvoiant, - so we have no idea which functions will be referenced in the - future (i.e. later on within the current translation unit). - So here we just ignore all file-scope function declarations - which are not also definitions. If and when the debugger needs - to know something about these funcstion, it wil have to hunt - around and find the DWARF information associated with the - *definition* of the function. - - Note that we can't just check `DECL_EXTERNAL' to find out which - FUNCTION_DECL nodes represent definitions and which ones represent - mere declarations. We have to check `DECL_INITIAL' instead. That's - because the C front-end supports some weird semantics for "extern - inline" function definitions. These can get inlined within the - current translation unit (an thus, we need to generate DWARF info - for their abstract instances so that the DWARF info for the - concrete inlined instances can have something to refer to) but - the compiler never generates any out-of-lines instances of such - things (despite the fact that they *are* definitions). The - important point is that the C front-end marks these "extern inline" - functions as DECL_EXTERNAL, but we need to generate DWARf for them - anyway. - - Note that the C++ front-end also plays some similar games for inline - function definitions appearing within include files which also - contain `#pragma interface' pragmas. */ - - if (DECL_INITIAL (decl) == NULL_TREE) - return; - - if (TREE_PUBLIC (decl) - && ! DECL_EXTERNAL (decl) - && ! DECL_ABSTRACT (decl)) - { - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - /* Output a .debug_pubnames entry for a public function - defined in this compilation unit. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, PUBNAMES_SECTION); - sprintf (label, PUB_DIE_LABEL_FMT, next_pubname_number); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, label); - ASM_OUTPUT_DWARF_STRING (asm_out_file, - IDENTIFIER_POINTER (DECL_NAME (decl))); - ASM_OUTPUT_POP_SECTION (asm_out_file); - } - - break; - - case VAR_DECL: - - /* Ignore this VAR_DECL if it refers to a file-scope extern data - object declaration and if the declaration was never even - referenced from within this entire compilation unit. We - suppress these DIEs in order to save space in the .debug section - (by eliminating entries which are probably useless). Note that - we must not suppress block-local extern declarations (whether - used or not) because that would screw-up the debugger's name - lookup mechanism and cause it to miss things which really ought - to be in scope at a given point. */ - - if (DECL_EXTERNAL (decl) && !TREE_USED (decl)) - return; - - if (TREE_PUBLIC (decl) - && ! DECL_EXTERNAL (decl) - && GET_CODE (DECL_RTL (decl)) == MEM - && ! DECL_ABSTRACT (decl)) - { - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - if (debug_info_level >= DINFO_LEVEL_NORMAL) - { - /* Output a .debug_pubnames entry for a public variable - defined in this compilation unit. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, PUBNAMES_SECTION); - sprintf (label, PUB_DIE_LABEL_FMT, next_pubname_number); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, label); - ASM_OUTPUT_DWARF_STRING (asm_out_file, - IDENTIFIER_POINTER (DECL_NAME (decl))); - ASM_OUTPUT_POP_SECTION (asm_out_file); - } - - if (DECL_INITIAL (decl) == NULL) - { - /* Output a .debug_aranges entry for a public variable - which is tentatively defined in this compilation unit. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, ARANGES_SECTION); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, - IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, - (unsigned) int_size_in_bytes (TREE_TYPE (decl))); - ASM_OUTPUT_POP_SECTION (asm_out_file); - } - } - - /* If we are in terse mode, don't generate any DIEs to represent - any variable declarations or definitions. */ - - if (debug_info_level <= DINFO_LEVEL_TERSE) - return; - - break; - - case TYPE_DECL: - /* Don't bother trying to generate any DIEs to represent any of the - normal built-in types for the language we are compiling, except - in cases where the types in question are *not* DWARF fundamental - types. We make an exception in the case of non-fundamental types - for the sake of objective C (and perhaps C++) because the GNU - front-ends for these languages may in fact create certain "built-in" - types which are (for example) RECORD_TYPEs. In such cases, we - really need to output these (non-fundamental) types because other - DIEs may contain references to them. */ - - if (DECL_SOURCE_LINE (decl) == 0 - && type_is_fundamental (TREE_TYPE (decl))) - return; - - /* If we are in terse mode, don't generate any DIEs to represent - any actual typedefs. Note that even when we are in terse mode, - we must still output DIEs to represent those tagged types which - are used (directly or indirectly) in the specification of either - a return type or a formal parameter type of some function. */ - - if (debug_info_level <= DINFO_LEVEL_TERSE) - if (DECL_NAME (decl) != NULL - || ! TYPE_USED_FOR_FUNCTION (TREE_TYPE (decl))) - return; - - break; - - default: - return; - } - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, DEBUG_SECTION); - finalizing = set_finalizing; - output_decl (decl, NULL_TREE); - - /* NOTE: The call above to `output_decl' may have caused one or more - file-scope named types (i.e. tagged types) to be placed onto the - pending_types_list. We have to get those types off of that list - at some point, and this is the perfect time to do it. If we didn't - take them off now, they might still be on the list when cc1 finally - exits. That might be OK if it weren't for the fact that when we put - types onto the pending_types_list, we set the TREE_ASM_WRITTEN flag - for these types, and that causes them never to be output unless - `output_pending_types_for_scope' takes them off of the list and un-sets - their TREE_ASM_WRITTEN flags. */ - - output_pending_types_for_scope (NULL_TREE); - - /* The above call should have totally emptied the pending_types_list. */ - - assert (pending_types == 0); - - ASM_OUTPUT_POP_SECTION (asm_out_file); - - if (TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl) != NULL) - current_funcdef_number++; -} - -/* Output a marker (i.e. a label) for the beginning of the generated code - for a lexical block. */ - -void -dwarfout_begin_block (blocknum) - register unsigned blocknum; -{ - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - text_section (); - sprintf (label, BLOCK_BEGIN_LABEL_FMT, blocknum); - ASM_OUTPUT_LABEL (asm_out_file, label); -} - -/* Output a marker (i.e. a label) for the end of the generated code - for a lexical block. */ - -void -dwarfout_end_block (blocknum) - register unsigned blocknum; -{ - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - text_section (); - sprintf (label, BLOCK_END_LABEL_FMT, blocknum); - ASM_OUTPUT_LABEL (asm_out_file, label); -} - -/* Output a marker (i.e. a label) at a point in the assembly code which - corresponds to a given source level label. */ - -void -dwarfout_label (insn) - register rtx insn; -{ - if (debug_info_level >= DINFO_LEVEL_NORMAL) - { - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - text_section (); - sprintf (label, INSN_LABEL_FMT, current_funcdef_number, - (unsigned) INSN_UID (insn)); - ASM_OUTPUT_LABEL (asm_out_file, label); - } -} - -/* Output a marker (i.e. a label) for the point in the generated code where - the real body of the function begins (after parameters have been moved - to their home locations). */ - -void -dwarfout_begin_function () -{ - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - text_section (); - sprintf (label, BODY_BEGIN_LABEL_FMT, current_funcdef_number); - ASM_OUTPUT_LABEL (asm_out_file, label); -} - -/* Output a marker (i.e. a label) for the point in the generated code where - the real body of the function ends (just before the epilogue code). */ - -void -dwarfout_end_function () -{ - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - text_section (); - sprintf (label, BODY_END_LABEL_FMT, current_funcdef_number); - ASM_OUTPUT_LABEL (asm_out_file, label); -} - -/* Output a marker (i.e. a label) for the absolute end of the generated code - for a function definition. This gets called *after* the epilogue code - has been generated. */ - -void -dwarfout_end_epilogue () -{ - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - /* Output a label to mark the endpoint of the code generated for this - function. */ - - sprintf (label, FUNC_END_LABEL_FMT, current_funcdef_number); - ASM_OUTPUT_LABEL (asm_out_file, label); -} - -static void -shuffle_filename_entry (new_zeroth) - register filename_entry *new_zeroth; -{ - filename_entry temp_entry; - register filename_entry *limit_p; - register filename_entry *move_p; - - if (new_zeroth == &filename_table[0]) - return; - - temp_entry = *new_zeroth; - - /* Shift entries up in the table to make room at [0]. */ - - limit_p = &filename_table[0]; - for (move_p = new_zeroth; move_p > limit_p; move_p--) - *move_p = *(move_p-1); - - /* Install the found entry at [0]. */ - - filename_table[0] = temp_entry; -} - -/* Create a new (string) entry for the .debug_sfnames section. */ - -static void -generate_new_sfname_entry () -{ - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, SFNAMES_SECTION); - sprintf (label, SFNAMES_ENTRY_LABEL_FMT, filename_table[0].number); - ASM_OUTPUT_LABEL (asm_out_file, label); - ASM_OUTPUT_DWARF_STRING (asm_out_file, - filename_table[0].name - ? filename_table[0].name - : ""); - ASM_OUTPUT_POP_SECTION (asm_out_file); -} - -/* Lookup a filename (in the list of filenames that we know about here in - dwarfout.c) and return its "index". The index of each (known) filename - is just a unique number which is associated with only that one filename. - We need such numbers for the sake of generating labels (in the - .debug_sfnames section) and references to those unique labels (in the - .debug_srcinfo and .debug_macinfo sections). - - If the filename given as an argument is not found in our current list, - add it to the list and assign it the next available unique index number. - - Whatever we do (i.e. whether we find a pre-existing filename or add a new - one), we shuffle the filename found (or added) up to the zeroth entry of - our list of filenames (which is always searched linearly). We do this so - as to optimize the most common case for these filename lookups within - dwarfout.c. The most common case by far is the case where we call - lookup_filename to lookup the very same filename that we did a lookup - on the last time we called lookup_filename. We make sure that this - common case is fast because such cases will constitute 99.9% of the - lookups we ever do (in practice). - - If we add a new filename entry to our table, we go ahead and generate - the corresponding entry in the .debug_sfnames section right away. - Doing so allows us to avoid tickling an assembler bug (present in some - m68k assemblers) which yields assembly-time errors in cases where the - difference of two label addresses is taken and where the two labels - are in a section *other* than the one where the difference is being - calculated, and where at least one of the two symbol references is a - forward reference. (This bug could be tickled by our .debug_srcinfo - entries if we don't output their corresponding .debug_sfnames entries - before them.) -*/ - -static unsigned -lookup_filename (file_name) - char *file_name; -{ - register filename_entry *search_p; - register filename_entry *limit_p = &filename_table[ft_entries]; - - for (search_p = filename_table; search_p < limit_p; search_p++) - if (!strcmp (file_name, search_p->name)) - { - /* When we get here, we have found the filename that we were - looking for in the filename_table. Now we want to make sure - that it gets moved to the zero'th entry in the table (if it - is not already there) so that subsequent attempts to find the - same filename will find it as quickly as possible. */ - - shuffle_filename_entry (search_p); - return filename_table[0].number; - } - - /* We come here whenever we have a new filename which is not registered - in the current table. Here we add it to the table. */ - - /* Prepare to add a new table entry by making sure there is enough space - in the table to do so. If not, expand the current table. */ - - if (ft_entries == ft_entries_allocated) - { - ft_entries_allocated += FT_ENTRIES_INCREMENT; - filename_table - = (filename_entry *) - xrealloc (filename_table, - ft_entries_allocated * sizeof (filename_entry)); - } - - /* Initially, add the new entry at the end of the filename table. */ - - filename_table[ft_entries].number = ft_entries; - filename_table[ft_entries].name = xstrdup (file_name); - - /* Shuffle the new entry into filename_table[0]. */ - - shuffle_filename_entry (&filename_table[ft_entries]); - - if (debug_info_level >= DINFO_LEVEL_NORMAL) - generate_new_sfname_entry (); - - ft_entries++; - return filename_table[0].number; -} - -static void -generate_srcinfo_entry (line_entry_num, files_entry_num) - unsigned line_entry_num; - unsigned files_entry_num; -{ - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, SRCINFO_SECTION); - sprintf (label, LINE_ENTRY_LABEL_FMT, line_entry_num); - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, label, LINE_BEGIN_LABEL); - sprintf (label, SFNAMES_ENTRY_LABEL_FMT, files_entry_num); - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, label, SFNAMES_BEGIN_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); -} - -void -dwarfout_line (filename, line) - register char *filename; - register unsigned line; -{ - if (debug_info_level >= DINFO_LEVEL_NORMAL) - { - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - static unsigned last_line_entry_num = 0; - static unsigned prev_file_entry_num = (unsigned) -1; - register unsigned this_file_entry_num = lookup_filename (filename); - - text_section (); - sprintf (label, LINE_CODE_LABEL_FMT, ++last_line_entry_num); - ASM_OUTPUT_LABEL (asm_out_file, label); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, LINE_SECTION); - - if (this_file_entry_num != prev_file_entry_num) - { - char line_entry_label[MAX_ARTIFICIAL_LABEL_BYTES]; - - sprintf (line_entry_label, LINE_ENTRY_LABEL_FMT, last_line_entry_num); - ASM_OUTPUT_LABEL (asm_out_file, line_entry_label); - } - - { - register char *tail = rindex (filename, '/'); - - if (tail != NULL) - filename = tail; - } - - fprintf (asm_out_file, "\t%s\t%u\t%s %s:%u\n", - UNALIGNED_INT_ASM_OP, line, ASM_COMMENT_START, - filename, line); - ASM_OUTPUT_DWARF_DATA2 (asm_out_file, 0xffff); - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, label, TEXT_BEGIN_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); - - if (this_file_entry_num != prev_file_entry_num) - generate_srcinfo_entry (last_line_entry_num, this_file_entry_num); - prev_file_entry_num = this_file_entry_num; - } -} - -/* Generate an entry in the .debug_macinfo section. */ - -static void -generate_macinfo_entry (type_and_offset, string) - register char *type_and_offset; - register char *string; -{ - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, MACINFO_SECTION); - fprintf (asm_out_file, "\t%s\t%s\n", UNALIGNED_INT_ASM_OP, type_and_offset); - ASM_OUTPUT_DWARF_STRING (asm_out_file, string); - ASM_OUTPUT_POP_SECTION (asm_out_file); -} - -void -dwarfout_start_new_source_file (filename) - register char *filename; -{ - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - char type_and_offset[MAX_ARTIFICIAL_LABEL_BYTES*3]; - - sprintf (label, SFNAMES_ENTRY_LABEL_FMT, lookup_filename (filename)); - sprintf (type_and_offset, "0x%08x+%s-%s", - ((unsigned) MACINFO_start << 24), label, SFNAMES_BEGIN_LABEL); - generate_macinfo_entry (type_and_offset, ""); -} - -void -dwarfout_resume_previous_source_file (lineno) - register unsigned lineno; -{ - char type_and_offset[MAX_ARTIFICIAL_LABEL_BYTES*2]; - - sprintf (type_and_offset, "0x%08x+%u", - ((unsigned) MACINFO_resume << 24), lineno); - generate_macinfo_entry (type_and_offset, ""); -} - -/* Called from check_newline in c-parse.y. The `buffer' parameter - contains the tail part of the directive line, i.e. the part which - is past the initial whitespace, #, whitespace, directive-name, - whitespace part. */ - -void -dwarfout_define (lineno, buffer) - register unsigned lineno; - register char *buffer; -{ - static int initialized = 0; - char type_and_offset[MAX_ARTIFICIAL_LABEL_BYTES*2]; - - if (!initialized) - { - dwarfout_start_new_source_file (primary_filename); - initialized = 1; - } - sprintf (type_and_offset, "0x%08x+%u", - ((unsigned) MACINFO_define << 24), lineno); - generate_macinfo_entry (type_and_offset, buffer); -} - -/* Called from check_newline in c-parse.y. The `buffer' parameter - contains the tail part of the directive line, i.e. the part which - is past the initial whitespace, #, whitespace, directive-name, - whitespace part. */ - -void -dwarfout_undef (lineno, buffer) - register unsigned lineno; - register char *buffer; -{ - char type_and_offset[MAX_ARTIFICIAL_LABEL_BYTES*2]; - - sprintf (type_and_offset, "0x%08x+%u", - ((unsigned) MACINFO_undef << 24), lineno); - generate_macinfo_entry (type_and_offset, buffer); -} - -/* Set up for Dwarf output at the start of compilation. */ - -void -dwarfout_init (asm_out_file, main_input_filename) - register FILE *asm_out_file; - register char *main_input_filename; -{ - /* Remember the name of the primary input file. */ - - primary_filename = main_input_filename; - - /* Allocate the initial hunk of the pending_sibling_stack. */ - - pending_sibling_stack - = (unsigned *) - xmalloc (PENDING_SIBLINGS_INCREMENT * sizeof (unsigned)); - pending_siblings_allocated = PENDING_SIBLINGS_INCREMENT; - pending_siblings = 1; - - /* Allocate the initial hunk of the filename_table. */ - - filename_table - = (filename_entry *) - xmalloc (FT_ENTRIES_INCREMENT * sizeof (filename_entry)); - ft_entries_allocated = FT_ENTRIES_INCREMENT; - ft_entries = 0; - - /* Allocate the initial hunk of the pending_types_list. */ - - pending_types_list - = (tree *) xmalloc (PENDING_TYPES_INCREMENT * sizeof (tree)); - pending_types_allocated = PENDING_TYPES_INCREMENT; - pending_types = 0; - - /* Create an artificial RECORD_TYPE node which we can use in our hack - to get the DIEs representing types of formal parameters to come out - only *after* the DIEs for the formal parameters themselves. */ - - fake_containing_scope = make_node (RECORD_TYPE); - - /* Output a starting label for the .text section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, TEXT_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, TEXT_BEGIN_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); - - /* Output a starting label for the .data section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, DATA_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, DATA_BEGIN_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); - -#if 0 /* GNU C doesn't currently use .data1. */ - /* Output a starting label for the .data1 section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, DATA1_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, DATA1_BEGIN_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); -#endif - - /* Output a starting label for the .rodata section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, RODATA_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, RODATA_BEGIN_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); - -#if 0 /* GNU C doesn't currently use .rodata1. */ - /* Output a starting label for the .rodata1 section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, RODATA1_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, RODATA1_BEGIN_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); -#endif - - /* Output a starting label for the .bss section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, BSS_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, BSS_BEGIN_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); - - if (debug_info_level >= DINFO_LEVEL_NORMAL) - { - /* Output a starting label and an initial (compilation directory) - entry for the .debug_sfnames section. The starting label will be - referenced by the initial entry in the .debug_srcinfo section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, SFNAMES_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, SFNAMES_BEGIN_LABEL); - { - register char *pwd = getpwd (); - register unsigned len = strlen (pwd); - register char *dirname = (char *) xmalloc (len + 2); - - strcpy (dirname, pwd); - strcpy (dirname + len, "/"); - ASM_OUTPUT_DWARF_STRING (asm_out_file, dirname); - free (dirname); - } - ASM_OUTPUT_POP_SECTION (asm_out_file); - - if (debug_info_level >= DINFO_LEVEL_VERBOSE) - { - /* Output a starting label for the .debug_macinfo section. This - label will be referenced by the AT_mac_info attribute in the - TAG_compile_unit DIE. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, MACINFO_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, MACINFO_BEGIN_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); - } - - /* Generate the initial entry for the .line section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, LINE_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, LINE_BEGIN_LABEL); - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, LINE_END_LABEL, LINE_BEGIN_LABEL); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, TEXT_BEGIN_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); - - /* Generate the initial entry for the .debug_srcinfo section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, SRCINFO_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, SRCINFO_BEGIN_LABEL); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, LINE_BEGIN_LABEL); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, SFNAMES_BEGIN_LABEL); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, TEXT_BEGIN_LABEL); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, TEXT_END_LABEL); -#ifdef DWARF_TIMESTAMPS - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, time (NULL)); -#else - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, -1); -#endif - ASM_OUTPUT_POP_SECTION (asm_out_file); - - /* Generate the initial entry for the .debug_pubnames section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, PUBNAMES_SECTION); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, DEBUG_BEGIN_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); - - /* Generate the initial entry for the .debug_aranges section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, ARANGES_SECTION); - ASM_OUTPUT_DWARF_ADDR (asm_out_file, DEBUG_BEGIN_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); - } - - /* Setup first DIE number == 1. */ - NEXT_DIE_NUM = next_unused_dienum++; - - /* Generate the initial DIE for the .debug section. Note that the - (string) value given in the AT_name attribute of the TAG_compile_unit - DIE will (typically) be a relative pathname and that this pathname - should be taken as being relative to the directory from which the - compiler was invoked when the given (base) source file was compiled. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, DEBUG_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, DEBUG_BEGIN_LABEL); - output_die (output_compile_unit_die, main_input_filename); - ASM_OUTPUT_POP_SECTION (asm_out_file); - - fputc ('\n', asm_out_file); -} - -/* Output stuff that dwarf requires at the end of every file. */ - -void -dwarfout_finish () -{ - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, DEBUG_SECTION); - - /* Mark the end of the chain of siblings which represent all file-scope - declarations in this compilation unit. */ - - /* The (null) DIE which represents the terminator for the (sibling linked) - list of file-scope items is *special*. Normally, we would just call - end_sibling_chain at this point in order to output a word with the - value `4' and that word would act as the terminator for the list of - DIEs describing file-scope items. Unfortunately, if we were to simply - do that, the label that would follow this DIE in the .debug section - (i.e. `..D2') would *not* be properly aligned (as it must be on some - machines) to a 4 byte boundary. - - In order to force the label `..D2' to get aligned to a 4 byte boundary, - the trick used is to insert extra (otherwise useless) padding bytes - into the (null) DIE that we know must precede the ..D2 label in the - .debug section. The amount of padding required can be anywhere between - 0 and 3 bytes. The length word at the start of this DIE (i.e. the one - with the padding) would normally contain the value 4, but now it will - also have to include the padding bytes, so it will instead have some - value in the range 4..7. - - Fortunately, the rules of Dwarf say that any DIE whose length word - contains *any* value less than 8 should be treated as a null DIE, so - this trick works out nicely. Clever, eh? Don't give me any credit - (or blame). I didn't think of this scheme. I just conformed to it. - */ - - output_die (output_padded_null_die, (void *)0); - dienum_pop (); - - sprintf (label, DIE_BEGIN_LABEL_FMT, NEXT_DIE_NUM); - ASM_OUTPUT_LABEL (asm_out_file, label); /* should be ..D2 */ - ASM_OUTPUT_POP_SECTION (asm_out_file); - - /* Output a terminator label for the .text section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, TEXT_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, TEXT_END_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); - - /* Output a terminator label for the .data section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, DATA_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, DATA_END_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); - -#if 0 /* GNU C doesn't currently use .data1. */ - /* Output a terminator label for the .data1 section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, DATA1_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, DATA1_END_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); -#endif - - /* Output a terminator label for the .rodata section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, RODATA_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, RODATA_END_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); - -#if 0 /* GNU C doesn't currently use .rodata1. */ - /* Output a terminator label for the .rodata1 section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, RODATA1_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, RODATA1_END_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); -#endif - - /* Output a terminator label for the .bss section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, BSS_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, BSS_END_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); - - if (debug_info_level >= DINFO_LEVEL_NORMAL) - { - /* Output a terminating entry for the .line section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, LINE_SECTION); - ASM_OUTPUT_LABEL (asm_out_file, LINE_LAST_ENTRY_LABEL); - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0); - ASM_OUTPUT_DWARF_DATA2 (asm_out_file, 0xffff); - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, TEXT_END_LABEL, TEXT_BEGIN_LABEL); - ASM_OUTPUT_LABEL (asm_out_file, LINE_END_LABEL); - ASM_OUTPUT_POP_SECTION (asm_out_file); - - /* Output a terminating entry for the .debug_srcinfo section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, SRCINFO_SECTION); - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, - LINE_LAST_ENTRY_LABEL, LINE_BEGIN_LABEL); - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, -1); - ASM_OUTPUT_POP_SECTION (asm_out_file); - - if (debug_info_level >= DINFO_LEVEL_VERBOSE) - { - /* Output terminating entries for the .debug_macinfo section. */ - - dwarfout_resume_previous_source_file (0); - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, MACINFO_SECTION); - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0); - ASM_OUTPUT_DWARF_STRING (asm_out_file, ""); - ASM_OUTPUT_POP_SECTION (asm_out_file); - } - - /* Generate the terminating entry for the .debug_pubnames section. */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, PUBNAMES_SECTION); - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0); - ASM_OUTPUT_DWARF_STRING (asm_out_file, ""); - ASM_OUTPUT_POP_SECTION (asm_out_file); - - /* Generate the terminating entries for the .debug_aranges section. - - Note that we want to do this only *after* we have output the end - labels (for the various program sections) which we are going to - refer to here. This allows us to work around a bug in the m68k - svr4 assembler. That assembler gives bogus assembly-time errors - if (within any given section) you try to take the difference of - two relocatable symbols, both of which are located within some - other section, and if one (or both?) of the symbols involved is - being forward-referenced. By generating the .debug_aranges - entries at this late point in the assembly output, we skirt the - issue simply by avoiding forward-references. - */ - - fputc ('\n', asm_out_file); - ASM_OUTPUT_PUSH_SECTION (asm_out_file, ARANGES_SECTION); - - ASM_OUTPUT_DWARF_ADDR (asm_out_file, TEXT_BEGIN_LABEL); - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, TEXT_END_LABEL, TEXT_BEGIN_LABEL); - - ASM_OUTPUT_DWARF_ADDR (asm_out_file, DATA_BEGIN_LABEL); - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, DATA_END_LABEL, DATA_BEGIN_LABEL); - -#if 0 /* GNU C doesn't currently use .data1. */ - ASM_OUTPUT_DWARF_ADDR (asm_out_file, DATA1_BEGIN_LABEL); - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, DATA1_END_LABEL, - DATA1_BEGIN_LABEL); -#endif - - ASM_OUTPUT_DWARF_ADDR (asm_out_file, RODATA_BEGIN_LABEL); - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, RODATA_END_LABEL, - RODATA_BEGIN_LABEL); - -#if 0 /* GNU C doesn't currently use .rodata1. */ - ASM_OUTPUT_DWARF_ADDR (asm_out_file, RODATA1_BEGIN_LABEL); - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, RODATA1_END_LABEL, - RODATA1_BEGIN_LABEL); -#endif - - ASM_OUTPUT_DWARF_ADDR (asm_out_file, BSS_BEGIN_LABEL); - ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, BSS_END_LABEL, BSS_BEGIN_LABEL); - - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0); - ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0); - - ASM_OUTPUT_POP_SECTION (asm_out_file); - } -} - -#endif /* DWARF_DEBUGGING_INFO */ diff --git a/gnu/usr.bin/cc/common/emit-rtl.c b/gnu/usr.bin/cc/common/emit-rtl.c deleted file mode 100644 index 2daddca31e..0000000000 --- a/gnu/usr.bin/cc/common/emit-rtl.c +++ /dev/null @@ -1,3105 +0,0 @@ -/* Emit RTL for the GNU C-Compiler expander. - Copyright (C) 1987, 1988, 1992, 1993 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* Middle-to-low level generation of rtx code and insns. - - This file contains the functions `gen_rtx', `gen_reg_rtx' - and `gen_label_rtx' that are the usual ways of creating rtl - expressions for most purposes. - - It also has the functions for creating insns and linking - them in the doubly-linked chain. - - The patterns of the insns are created by machine-dependent - routines in insn-emit.c, which is generated automatically from - the machine description. These routines use `gen_rtx' to make - the individual rtx's of the pattern; what is machine dependent - is the kind of rtx's they make and what arguments they use. */ - -#include "config.h" -#include "gvarargs.h" -#include "rtl.h" -#include "flags.h" -#include "function.h" -#include "expr.h" -#include "regs.h" -#include "insn-config.h" -#include "real.h" -#include - -/* This is reset to LAST_VIRTUAL_REGISTER + 1 at the start of each function. - After rtl generation, it is 1 plus the largest register number used. */ - -int reg_rtx_no = LAST_VIRTUAL_REGISTER + 1; - -/* This is *not* reset after each function. It gives each CODE_LABEL - in the entire compilation a unique label number. */ - -static int label_num = 1; - -/* Lowest label number in current function. */ - -static int first_label_num; - -/* Highest label number in current function. - Zero means use the value of label_num instead. - This is nonzero only when belatedly compiling an inline function. */ - -static int last_label_num; - -/* Value label_num had when set_new_first_and_last_label_number was called. - If label_num has not changed since then, last_label_num is valid. */ - -static int base_label_num; - -/* Nonzero means do not generate NOTEs for source line numbers. */ - -static int no_line_numbers; - -/* Commonly used rtx's, so that we only need space for one copy. - These are initialized once for the entire compilation. - All of these except perhaps the floating-point CONST_DOUBLEs - are unique; no other rtx-object will be equal to any of these. */ - -rtx pc_rtx; /* (PC) */ -rtx cc0_rtx; /* (CC0) */ -rtx cc1_rtx; /* (CC1) (not actually used nowadays) */ -rtx const0_rtx; /* (CONST_INT 0) */ -rtx const1_rtx; /* (CONST_INT 1) */ -rtx const2_rtx; /* (CONST_INT 2) */ -rtx constm1_rtx; /* (CONST_INT -1) */ -rtx const_true_rtx; /* (CONST_INT STORE_FLAG_VALUE) */ - -/* We record floating-point CONST_DOUBLEs in each floating-point mode for - the values of 0, 1, and 2. For the integer entries and VOIDmode, we - record a copy of const[012]_rtx. */ - -rtx const_tiny_rtx[3][(int) MAX_MACHINE_MODE]; - -REAL_VALUE_TYPE dconst0; -REAL_VALUE_TYPE dconst1; -REAL_VALUE_TYPE dconst2; -REAL_VALUE_TYPE dconstm1; - -/* All references to the following fixed hard registers go through - these unique rtl objects. On machines where the frame-pointer and - arg-pointer are the same register, they use the same unique object. - - After register allocation, other rtl objects which used to be pseudo-regs - may be clobbered to refer to the frame-pointer register. - But references that were originally to the frame-pointer can be - distinguished from the others because they contain frame_pointer_rtx. - - In an inline procedure, the stack and frame pointer rtxs may not be - used for anything else. */ -rtx stack_pointer_rtx; /* (REG:Pmode STACK_POINTER_REGNUM) */ -rtx frame_pointer_rtx; /* (REG:Pmode FRAME_POINTER_REGNUM) */ -rtx arg_pointer_rtx; /* (REG:Pmode ARG_POINTER_REGNUM) */ -rtx struct_value_rtx; /* (REG:Pmode STRUCT_VALUE_REGNUM) */ -rtx struct_value_incoming_rtx; /* (REG:Pmode STRUCT_VALUE_INCOMING_REGNUM) */ -rtx static_chain_rtx; /* (REG:Pmode STATIC_CHAIN_REGNUM) */ -rtx static_chain_incoming_rtx; /* (REG:Pmode STATIC_CHAIN_INCOMING_REGNUM) */ -rtx pic_offset_table_rtx; /* (REG:Pmode PIC_OFFSET_TABLE_REGNUM) */ - -rtx virtual_incoming_args_rtx; /* (REG:Pmode VIRTUAL_INCOMING_ARGS_REGNUM) */ -rtx virtual_stack_vars_rtx; /* (REG:Pmode VIRTUAL_STACK_VARS_REGNUM) */ -rtx virtual_stack_dynamic_rtx; /* (REG:Pmode VIRTUAL_STACK_DYNAMIC_REGNUM) */ -rtx virtual_outgoing_args_rtx; /* (REG:Pmode VIRTUAL_OUTGOING_ARGS_REGNUM) */ - -/* We make one copy of (const_int C) where C is in - [- MAX_SAVED_CONST_INT, MAX_SAVED_CONST_INT] - to save space during the compilation and simplify comparisons of - integers. */ - -#define MAX_SAVED_CONST_INT 64 - -static rtx const_int_rtx[MAX_SAVED_CONST_INT * 2 + 1]; - -/* The ends of the doubly-linked chain of rtl for the current function. - Both are reset to null at the start of rtl generation for the function. - - start_sequence saves both of these on `sequence_stack' and then - starts a new, nested sequence of insns. */ - -static rtx first_insn = NULL; -static rtx last_insn = NULL; - -/* INSN_UID for next insn emitted. - Reset to 1 for each function compiled. */ - -static int cur_insn_uid = 1; - -/* Line number and source file of the last line-number NOTE emitted. - This is used to avoid generating duplicates. */ - -static int last_linenum = 0; -static char *last_filename = 0; - -/* A vector indexed by pseudo reg number. The allocated length - of this vector is regno_pointer_flag_length. Since this - vector is needed during the expansion phase when the total - number of registers in the function is not yet known, - it is copied and made bigger when necessary. */ - -char *regno_pointer_flag; -int regno_pointer_flag_length; - -/* Indexed by pseudo register number, gives the rtx for that pseudo. - Allocated in parallel with regno_pointer_flag. */ - -rtx *regno_reg_rtx; - -/* Stack of pending (incomplete) sequences saved by `start_sequence'. - Each element describes one pending sequence. - The main insn-chain is saved in the last element of the chain, - unless the chain is empty. */ - -struct sequence_stack *sequence_stack; - -/* start_sequence and gen_sequence can make a lot of rtx expressions which are - shortly thrown away. We use two mechanisms to prevent this waste: - - First, we keep a list of the expressions used to represent the sequence - stack in sequence_element_free_list. - - Second, for sizes up to 5 elements, we keep a SEQUENCE and its associated - rtvec for use by gen_sequence. One entry for each size is sufficient - because most cases are calls to gen_sequence followed by immediately - emitting the SEQUENCE. Reuse is safe since emitting a sequence is - destructive on the insn in it anyway and hence can't be redone. - - We do not bother to save this cached data over nested function calls. - Instead, we just reinitialize them. */ - -#define SEQUENCE_RESULT_SIZE 5 - -static struct sequence_stack *sequence_element_free_list; -static rtx sequence_result[SEQUENCE_RESULT_SIZE]; - -extern int rtx_equal_function_value_matters; - -/* Filename and line number of last line-number note, - whether we actually emitted it or not. */ -extern char *emit_filename; -extern int emit_lineno; - -rtx change_address (); -void init_emit (); - -/* rtx gen_rtx (code, mode, [element1, ..., elementn]) -** -** This routine generates an RTX of the size specified by -** , which is an RTX code. The RTX structure is initialized -** from the arguments through , which are -** interpreted according to the specific RTX type's format. The -** special machine mode associated with the rtx (if any) is specified -** in . -** -** gen_rtx can be invoked in a way which resembles the lisp-like -** rtx it will generate. For example, the following rtx structure: -** -** (plus:QI (mem:QI (reg:SI 1)) -** (mem:QI (plusw:SI (reg:SI 2) (reg:SI 3)))) -** -** ...would be generated by the following C code: -** -** gen_rtx (PLUS, QImode, -** gen_rtx (MEM, QImode, -** gen_rtx (REG, SImode, 1)), -** gen_rtx (MEM, QImode, -** gen_rtx (PLUS, SImode, -** gen_rtx (REG, SImode, 2), -** gen_rtx (REG, SImode, 3)))), -*/ - -/*VARARGS2*/ -rtx -gen_rtx (va_alist) - va_dcl -{ - va_list p; - enum rtx_code code; - enum machine_mode mode; - register int i; /* Array indices... */ - register char *fmt; /* Current rtx's format... */ - register rtx rt_val; /* RTX to return to caller... */ - - va_start (p); - code = va_arg (p, enum rtx_code); - mode = va_arg (p, enum machine_mode); - - if (code == CONST_INT) - { - HOST_WIDE_INT arg = va_arg (p, HOST_WIDE_INT); - - if (arg >= - MAX_SAVED_CONST_INT && arg <= MAX_SAVED_CONST_INT) - return const_int_rtx[arg + MAX_SAVED_CONST_INT]; - - if (const_true_rtx && arg == STORE_FLAG_VALUE) - return const_true_rtx; - - rt_val = rtx_alloc (code); - INTVAL (rt_val) = arg; - } - else if (code == REG) - { - int regno = va_arg (p, int); - - /* In case the MD file explicitly references the frame pointer, have - all such references point to the same frame pointer. This is used - during frame pointer elimination to distinguish the explicit - references to these registers from pseudos that happened to be - assigned to them. - - If we have eliminated the frame pointer or arg pointer, we will - be using it as a normal register, for example as a spill register. - In such cases, we might be accessing it in a mode that is not - Pmode and therefore cannot use the pre-allocated rtx. - - Also don't do this when we are making new REGs in reload, - since we don't want to get confused with the real pointers. */ - - if (frame_pointer_rtx && regno == FRAME_POINTER_REGNUM && mode == Pmode - && ! reload_in_progress) - return frame_pointer_rtx; -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - if (arg_pointer_rtx && regno == ARG_POINTER_REGNUM && mode == Pmode - && ! reload_in_progress) - return arg_pointer_rtx; -#endif - if (stack_pointer_rtx && regno == STACK_POINTER_REGNUM && mode == Pmode - && ! reload_in_progress) - return stack_pointer_rtx; - else - { - rt_val = rtx_alloc (code); - rt_val->mode = mode; - REGNO (rt_val) = regno; - return rt_val; - } - } - else - { - rt_val = rtx_alloc (code); /* Allocate the storage space. */ - rt_val->mode = mode; /* Store the machine mode... */ - - fmt = GET_RTX_FORMAT (code); /* Find the right format... */ - for (i = 0; i < GET_RTX_LENGTH (code); i++) - { - switch (*fmt++) - { - case '0': /* Unused field. */ - break; - - case 'i': /* An integer? */ - XINT (rt_val, i) = va_arg (p, int); - break; - - case 'w': /* A wide integer? */ - XWINT (rt_val, i) = va_arg (p, HOST_WIDE_INT); - break; - - case 's': /* A string? */ - XSTR (rt_val, i) = va_arg (p, char *); - break; - - case 'e': /* An expression? */ - case 'u': /* An insn? Same except when printing. */ - XEXP (rt_val, i) = va_arg (p, rtx); - break; - - case 'E': /* An RTX vector? */ - XVEC (rt_val, i) = va_arg (p, rtvec); - break; - - default: - abort (); - } - } - } - va_end (p); - return rt_val; /* Return the new RTX... */ -} - -/* gen_rtvec (n, [rt1, ..., rtn]) -** -** This routine creates an rtvec and stores within it the -** pointers to rtx's which are its arguments. -*/ - -/*VARARGS1*/ -rtvec -gen_rtvec (va_alist) - va_dcl -{ - int n, i; - va_list p; - rtx *vector; - - va_start (p); - n = va_arg (p, int); - - if (n == 0) - return NULL_RTVEC; /* Don't allocate an empty rtvec... */ - - vector = (rtx *) alloca (n * sizeof (rtx)); - for (i = 0; i < n; i++) - vector[i] = va_arg (p, rtx); - va_end (p); - - return gen_rtvec_v (n, vector); -} - -rtvec -gen_rtvec_v (n, argp) - int n; - rtx *argp; -{ - register int i; - register rtvec rt_val; - - if (n == 0) - return NULL_RTVEC; /* Don't allocate an empty rtvec... */ - - rt_val = rtvec_alloc (n); /* Allocate an rtvec... */ - - for (i = 0; i < n; i++) - rt_val->elem[i].rtx = *argp++; - - return rt_val; -} - -/* Generate a REG rtx for a new pseudo register of mode MODE. - This pseudo is assigned the next sequential register number. */ - -rtx -gen_reg_rtx (mode) - enum machine_mode mode; -{ - register rtx val; - - /* Don't let anything called by or after reload create new registers - (actually, registers can't be created after flow, but this is a good - approximation). */ - - if (reload_in_progress || reload_completed) - abort (); - - /* Make sure regno_pointer_flag and regno_reg_rtx are large - enough to have an element for this pseudo reg number. */ - - if (reg_rtx_no == regno_pointer_flag_length) - { - rtx *new1; - char *new = - (char *) oballoc (regno_pointer_flag_length * 2); - bzero (new, regno_pointer_flag_length * 2); - bcopy (regno_pointer_flag, new, regno_pointer_flag_length); - regno_pointer_flag = new; - - new1 = (rtx *) oballoc (regno_pointer_flag_length * 2 * sizeof (rtx)); - bzero (new1, regno_pointer_flag_length * 2 * sizeof (rtx)); - bcopy (regno_reg_rtx, new1, regno_pointer_flag_length * sizeof (rtx)); - regno_reg_rtx = new1; - - regno_pointer_flag_length *= 2; - } - - val = gen_rtx (REG, mode, reg_rtx_no); - regno_reg_rtx[reg_rtx_no++] = val; - return val; -} - -/* Identify REG as a probable pointer register. */ - -void -mark_reg_pointer (reg) - rtx reg; -{ - REGNO_POINTER_FLAG (REGNO (reg)) = 1; -} - -/* Return 1 plus largest pseudo reg number used in the current function. */ - -int -max_reg_num () -{ - return reg_rtx_no; -} - -/* Return 1 + the largest label number used so far in the current function. */ - -int -max_label_num () -{ - if (last_label_num && label_num == base_label_num) - return last_label_num; - return label_num; -} - -/* Return first label number used in this function (if any were used). */ - -int -get_first_label_num () -{ - return first_label_num; -} - -/* Return a value representing some low-order bits of X, where the number - of low-order bits is given by MODE. Note that no conversion is done - between floating-point and fixed-point values, rather, the bit - representation is returned. - - This function handles the cases in common between gen_lowpart, below, - and two variants in cse.c and combine.c. These are the cases that can - be safely handled at all points in the compilation. - - If this is not a case we can handle, return 0. */ - -rtx -gen_lowpart_common (mode, x) - enum machine_mode mode; - register rtx x; -{ - int word = 0; - - if (GET_MODE (x) == mode) - return x; - - /* MODE must occupy no more words than the mode of X. */ - if (GET_MODE (x) != VOIDmode - && ((GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD - > ((GET_MODE_SIZE (GET_MODE (x)) + (UNITS_PER_WORD - 1)) - / UNITS_PER_WORD))) - return 0; - - if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD) - word = ((GET_MODE_SIZE (GET_MODE (x)) - - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD)) - / UNITS_PER_WORD); - - if ((GET_CODE (x) == ZERO_EXTEND || GET_CODE (x) == SIGN_EXTEND) - && (GET_MODE_CLASS (mode) == MODE_INT - || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT)) - { - /* If we are getting the low-order part of something that has been - sign- or zero-extended, we can either just use the object being - extended or make a narrower extension. If we want an even smaller - piece than the size of the object being extended, call ourselves - recursively. - - This case is used mostly by combine and cse. */ - - if (GET_MODE (XEXP (x, 0)) == mode) - return XEXP (x, 0); - else if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (XEXP (x, 0)))) - return gen_lowpart_common (mode, XEXP (x, 0)); - else if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (x))) - return gen_rtx (GET_CODE (x), mode, XEXP (x, 0)); - } - else if (GET_CODE (x) == SUBREG - && (GET_MODE_SIZE (mode) <= UNITS_PER_WORD - || GET_MODE_SIZE (mode) == GET_MODE_UNIT_SIZE (GET_MODE (x)))) - return (GET_MODE (SUBREG_REG (x)) == mode && SUBREG_WORD (x) == 0 - ? SUBREG_REG (x) - : gen_rtx (SUBREG, mode, SUBREG_REG (x), SUBREG_WORD (x))); - else if (GET_CODE (x) == REG) - { - /* If the register is not valid for MODE, return 0. If we don't - do this, there is no way to fix up the resulting REG later. */ - if (REGNO (x) < FIRST_PSEUDO_REGISTER - && ! HARD_REGNO_MODE_OK (REGNO (x) + word, mode)) - return 0; - else if (REGNO (x) < FIRST_PSEUDO_REGISTER - /* integrate.c can't handle parts of a return value register. */ - && (! REG_FUNCTION_VALUE_P (x) - || ! rtx_equal_function_value_matters) - /* We want to keep the stack, frame, and arg pointers - special. */ - && REGNO (x) != FRAME_POINTER_REGNUM -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - && REGNO (x) != ARG_POINTER_REGNUM -#endif - && REGNO (x) != STACK_POINTER_REGNUM) - return gen_rtx (REG, mode, REGNO (x) + word); - else - return gen_rtx (SUBREG, mode, x, word); - } - - /* If X is a CONST_INT or a CONST_DOUBLE, extract the appropriate bits - from the low-order part of the constant. */ - else if ((GET_MODE_CLASS (mode) == MODE_INT - || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT) - && GET_MODE (x) == VOIDmode - && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE)) - { - /* If MODE is twice the host word size, X is already the desired - representation. Otherwise, if MODE is wider than a word, we can't - do this. If MODE is exactly a word, return just one CONST_INT. - If MODE is smaller than a word, clear the bits that don't belong - in our mode, unless they and our sign bit are all one. So we get - either a reasonable negative value or a reasonable unsigned value - for this mode. */ - - if (GET_MODE_BITSIZE (mode) == 2 * HOST_BITS_PER_WIDE_INT) - return x; - else if (GET_MODE_BITSIZE (mode) > HOST_BITS_PER_WIDE_INT) - return 0; - else if (GET_MODE_BITSIZE (mode) == HOST_BITS_PER_WIDE_INT) - return (GET_CODE (x) == CONST_INT ? x - : GEN_INT (CONST_DOUBLE_LOW (x))); - else - { - /* MODE must be narrower than HOST_BITS_PER_INT. */ - int width = GET_MODE_BITSIZE (mode); - HOST_WIDE_INT val = (GET_CODE (x) == CONST_INT ? INTVAL (x) - : CONST_DOUBLE_LOW (x)); - - if (((val & ((HOST_WIDE_INT) (-1) << (width - 1))) - != ((HOST_WIDE_INT) (-1) << (width - 1)))) - val &= ((HOST_WIDE_INT) 1 << width) - 1; - - return (GET_CODE (x) == CONST_INT && INTVAL (x) == val ? x - : GEN_INT (val)); - } - } - - /* If X is an integral constant but we want it in floating-point, it - must be the case that we have a union of an integer and a floating-point - value. If the machine-parameters allow it, simulate that union here - and return the result. The two-word and single-word cases are - different. */ - - else if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT - && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD) - || flag_pretend_float) - && GET_MODE_CLASS (mode) == MODE_FLOAT - && GET_MODE_SIZE (mode) == UNITS_PER_WORD - && GET_CODE (x) == CONST_INT - && sizeof (float) * HOST_BITS_PER_CHAR == HOST_BITS_PER_WIDE_INT) - { - union {HOST_WIDE_INT i; float d; } u; - - u.i = INTVAL (x); - return immed_real_const_1 (u.d, mode); - } - - else if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT - && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD) - || flag_pretend_float) - && GET_MODE_CLASS (mode) == MODE_FLOAT - && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD - && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE) - && GET_MODE (x) == VOIDmode - && (sizeof (double) * HOST_BITS_PER_CHAR - == 2 * HOST_BITS_PER_WIDE_INT)) - { - union {HOST_WIDE_INT i[2]; double d; } u; - HOST_WIDE_INT low, high; - - if (GET_CODE (x) == CONST_INT) - low = INTVAL (x), high = low >> (HOST_BITS_PER_WIDE_INT -1); - else - low = CONST_DOUBLE_LOW (x), high = CONST_DOUBLE_HIGH (x); - -#ifdef HOST_WORDS_BIG_ENDIAN - u.i[0] = high, u.i[1] = low; -#else - u.i[0] = low, u.i[1] = high; -#endif - - return immed_real_const_1 (u.d, mode); - } - - /* Similarly, if this is converting a floating-point value into a - single-word integer. Only do this is the host and target parameters are - compatible. */ - - else if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT - && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD) - || flag_pretend_float) - && (GET_MODE_CLASS (mode) == MODE_INT - || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT) - && GET_CODE (x) == CONST_DOUBLE - && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT - && GET_MODE_BITSIZE (mode) == BITS_PER_WORD) - return operand_subword (x, 0, 0, GET_MODE (x)); - - /* Similarly, if this is converting a floating-point value into a - two-word integer, we can do this one word at a time and make an - integer. Only do this is the host and target parameters are - compatible. */ - - else if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT - && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD) - || flag_pretend_float) - && (GET_MODE_CLASS (mode) == MODE_INT - || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT) - && GET_CODE (x) == CONST_DOUBLE - && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT - && GET_MODE_BITSIZE (mode) == 2 * BITS_PER_WORD) - { - rtx lowpart = operand_subword (x, WORDS_BIG_ENDIAN, 0, GET_MODE (x)); - rtx highpart = operand_subword (x, ! WORDS_BIG_ENDIAN, 0, GET_MODE (x)); - - if (lowpart && GET_CODE (lowpart) == CONST_INT - && highpart && GET_CODE (highpart) == CONST_INT) - return immed_double_const (INTVAL (lowpart), INTVAL (highpart), mode); - } - - /* Otherwise, we can't do this. */ - return 0; -} - -/* Return the real part (which has mode MODE) of a complex value X. - This always comes at the low address in memory. */ - -rtx -gen_realpart (mode, x) - enum machine_mode mode; - register rtx x; -{ - if (WORDS_BIG_ENDIAN) - return gen_highpart (mode, x); - else - return gen_lowpart (mode, x); -} - -/* Return the imaginary part (which has mode MODE) of a complex value X. - This always comes at the high address in memory. */ - -rtx -gen_imagpart (mode, x) - enum machine_mode mode; - register rtx x; -{ - if (WORDS_BIG_ENDIAN) - return gen_lowpart (mode, x); - else - return gen_highpart (mode, x); -} - -/* Assuming that X is an rtx (e.g., MEM, REG or SUBREG) for a value, - return an rtx (MEM, SUBREG, or CONST_INT) that refers to the - least-significant part of X. - MODE specifies how big a part of X to return; - it usually should not be larger than a word. - If X is a MEM whose address is a QUEUED, the value may be so also. */ - -rtx -gen_lowpart (mode, x) - enum machine_mode mode; - register rtx x; -{ - rtx result = gen_lowpart_common (mode, x); - - if (result) - return result; - else if (GET_CODE (x) == MEM) - { - /* The only additional case we can do is MEM. */ - register int offset = 0; - if (WORDS_BIG_ENDIAN) - offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD) - - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD)); - - if (BYTES_BIG_ENDIAN) - /* Adjust the address so that the address-after-the-data - is unchanged. */ - offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)) - - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x)))); - - return change_address (x, mode, plus_constant (XEXP (x, 0), offset)); - } - else - abort (); -} - -/* Like `gen_lowpart', but refer to the most significant part. - This is used to access the imaginary part of a complex number. */ - -rtx -gen_highpart (mode, x) - enum machine_mode mode; - register rtx x; -{ - /* This case loses if X is a subreg. To catch bugs early, - complain if an invalid MODE is used even in other cases. */ - if (GET_MODE_SIZE (mode) > UNITS_PER_WORD - && GET_MODE_SIZE (mode) != GET_MODE_UNIT_SIZE (GET_MODE (x))) - abort (); - if (GET_CODE (x) == CONST_DOUBLE -#if !(TARGET_FLOAT_FORMAT != HOST_FLOAT_FORMAT || defined (REAL_IS_NOT_DOUBLE)) - && GET_MODE_CLASS (GET_MODE (x)) != MODE_FLOAT -#endif - ) - return gen_rtx (CONST_INT, VOIDmode, - CONST_DOUBLE_HIGH (x) & GET_MODE_MASK (mode)); - else if (GET_CODE (x) == CONST_INT) - return const0_rtx; - else if (GET_CODE (x) == MEM) - { - register int offset = 0; -#if !WORDS_BIG_ENDIAN - offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD) - - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD)); -#endif -#if !BYTES_BIG_ENDIAN - if (GET_MODE_SIZE (mode) < UNITS_PER_WORD) - offset -= (GET_MODE_SIZE (mode) - - MIN (UNITS_PER_WORD, - GET_MODE_SIZE (GET_MODE (x)))); -#endif - return change_address (x, mode, plus_constant (XEXP (x, 0), offset)); - } - else if (GET_CODE (x) == SUBREG) - { - /* The only time this should occur is when we are looking at a - multi-word item with a SUBREG whose mode is the same as that of the - item. It isn't clear what we would do if it wasn't. */ - if (SUBREG_WORD (x) != 0) - abort (); - return gen_highpart (mode, SUBREG_REG (x)); - } - else if (GET_CODE (x) == REG) - { - int word = 0; - -#if !WORDS_BIG_ENDIAN - if (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD) - word = ((GET_MODE_SIZE (GET_MODE (x)) - - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD)) - / UNITS_PER_WORD); -#endif - if (REGNO (x) < FIRST_PSEUDO_REGISTER - /* We want to keep the stack, frame, and arg pointers special. */ - && REGNO (x) != FRAME_POINTER_REGNUM -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - && REGNO (x) != ARG_POINTER_REGNUM -#endif - && REGNO (x) != STACK_POINTER_REGNUM) - return gen_rtx (REG, mode, REGNO (x) + word); - else - return gen_rtx (SUBREG, mode, x, word); - } - else - abort (); -} - -/* Return 1 iff X, assumed to be a SUBREG, - refers to the least significant part of its containing reg. - If X is not a SUBREG, always return 1 (it is its own low part!). */ - -int -subreg_lowpart_p (x) - rtx x; -{ - if (GET_CODE (x) != SUBREG) - return 1; - - if (WORDS_BIG_ENDIAN - && GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) > UNITS_PER_WORD) - return (SUBREG_WORD (x) - == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) - - MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD)) - / UNITS_PER_WORD)); - - return SUBREG_WORD (x) == 0; -} - -/* Return subword I of operand OP. - The word number, I, is interpreted as the word number starting at the - low-order address. Word 0 is the low-order word if not WORDS_BIG_ENDIAN, - otherwise it is the high-order word. - - If we cannot extract the required word, we return zero. Otherwise, an - rtx corresponding to the requested word will be returned. - - VALIDATE_ADDRESS is nonzero if the address should be validated. Before - reload has completed, a valid address will always be returned. After - reload, if a valid address cannot be returned, we return zero. - - If VALIDATE_ADDRESS is zero, we simply form the required address; validating - it is the responsibility of the caller. - - MODE is the mode of OP in case it is a CONST_INT. */ - -rtx -operand_subword (op, i, validate_address, mode) - rtx op; - int i; - int validate_address; - enum machine_mode mode; -{ - HOST_WIDE_INT val; - int size_ratio = HOST_BITS_PER_WIDE_INT / BITS_PER_WORD; - - if (mode == VOIDmode) - mode = GET_MODE (op); - - if (mode == VOIDmode) - abort (); - - /* If OP is narrower than a word or if we want a word outside OP, fail. */ - if (mode != BLKmode - && (GET_MODE_SIZE (mode) < UNITS_PER_WORD - || (i + 1) * UNITS_PER_WORD > GET_MODE_SIZE (mode))) - return 0; - - /* If OP is already an integer word, return it. */ - if (GET_MODE_CLASS (mode) == MODE_INT - && GET_MODE_SIZE (mode) == UNITS_PER_WORD) - return op; - - /* If OP is a REG or SUBREG, we can handle it very simply. */ - if (GET_CODE (op) == REG) - { - /* If the register is not valid for MODE, return 0. If we don't - do this, there is no way to fix up the resulting REG later. */ - if (REGNO (op) < FIRST_PSEUDO_REGISTER - && ! HARD_REGNO_MODE_OK (REGNO (op) + i, word_mode)) - return 0; - else if (REGNO (op) >= FIRST_PSEUDO_REGISTER - || (REG_FUNCTION_VALUE_P (op) - && rtx_equal_function_value_matters) - /* We want to keep the stack, frame, and arg pointers - special. */ - || REGNO (op) == FRAME_POINTER_REGNUM -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - || REGNO (op) == ARG_POINTER_REGNUM -#endif - || REGNO (op) == STACK_POINTER_REGNUM) - return gen_rtx (SUBREG, word_mode, op, i); - else - return gen_rtx (REG, word_mode, REGNO (op) + i); - } - else if (GET_CODE (op) == SUBREG) - return gen_rtx (SUBREG, word_mode, SUBREG_REG (op), i + SUBREG_WORD (op)); - - /* Form a new MEM at the requested address. */ - if (GET_CODE (op) == MEM) - { - rtx addr = plus_constant (XEXP (op, 0), i * UNITS_PER_WORD); - rtx new; - - if (validate_address) - { - if (reload_completed) - { - if (! strict_memory_address_p (word_mode, addr)) - return 0; - } - else - addr = memory_address (word_mode, addr); - } - - new = gen_rtx (MEM, word_mode, addr); - - MEM_VOLATILE_P (new) = MEM_VOLATILE_P (op); - MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (op); - RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (op); - - return new; - } - - /* The only remaining cases are when OP is a constant. If the host and - target floating formats are the same, handling two-word floating - constants are easy. Note that REAL_VALUE_TO_TARGET_{SINGLE,DOUBLE} - are defined as returning 32 bit and 64-bit values, respectively, - and not values of BITS_PER_WORD and 2 * BITS_PER_WORD bits. */ -#ifdef REAL_ARITHMETIC - if ((HOST_BITS_PER_WIDE_INT == BITS_PER_WORD) - && GET_MODE_CLASS (mode) == MODE_FLOAT - && GET_MODE_BITSIZE (mode) == 64 - && GET_CODE (op) == CONST_DOUBLE) - { - HOST_WIDE_INT k[2]; - REAL_VALUE_TYPE rv; - - REAL_VALUE_FROM_CONST_DOUBLE (rv, op); - REAL_VALUE_TO_TARGET_DOUBLE (rv, k); - - /* We handle 32-bit and 64-bit host words here. Note that the order in - which the words are written depends on the word endianness. - - ??? This is a potential portability problem and should - be fixed at some point. */ - if (HOST_BITS_PER_WIDE_INT == 32) - return GEN_INT (k[i]); - else if (HOST_BITS_PER_WIDE_INT == 64 && i == 0) - return GEN_INT ((k[! WORDS_BIG_ENDIAN] << (HOST_BITS_PER_WIDE_INT / 2)) - | k[WORDS_BIG_ENDIAN]); - else - abort (); - } -#else /* no REAL_ARITHMETIC */ - if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT - && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD) - || flag_pretend_float) - && GET_MODE_CLASS (mode) == MODE_FLOAT - && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD - && GET_CODE (op) == CONST_DOUBLE) - { - /* The constant is stored in the host's word-ordering, - but we want to access it in the target's word-ordering. Some - compilers don't like a conditional inside macro args, so we have two - copies of the return. */ -#ifdef HOST_WORDS_BIG_ENDIAN - return GEN_INT (i == WORDS_BIG_ENDIAN - ? CONST_DOUBLE_HIGH (op) : CONST_DOUBLE_LOW (op)); -#else - return GEN_INT (i != WORDS_BIG_ENDIAN - ? CONST_DOUBLE_HIGH (op) : CONST_DOUBLE_LOW (op)); -#endif - } -#endif /* no REAL_ARITHMETIC */ - - /* Single word float is a little harder, since single- and double-word - values often do not have the same high-order bits. We have already - verified that we want the only defined word of the single-word value. */ -#ifdef REAL_ARITHMETIC - if ((HOST_BITS_PER_WIDE_INT == BITS_PER_WORD) - && GET_MODE_CLASS (mode) == MODE_FLOAT - && GET_MODE_BITSIZE (mode) == 32 - && GET_CODE (op) == CONST_DOUBLE) - { - HOST_WIDE_INT l; - REAL_VALUE_TYPE rv; - - REAL_VALUE_FROM_CONST_DOUBLE (rv, op); - REAL_VALUE_TO_TARGET_SINGLE (rv, l); - return GEN_INT (l); - } -#else - if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT - && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD) - || flag_pretend_float) - && GET_MODE_CLASS (mode) == MODE_FLOAT - && GET_MODE_SIZE (mode) == UNITS_PER_WORD - && GET_CODE (op) == CONST_DOUBLE) - { - double d; - union {float f; HOST_WIDE_INT i; } u; - - REAL_VALUE_FROM_CONST_DOUBLE (d, op); - - u.f = d; - return GEN_INT (u.i); - } -#endif /* no REAL_ARITHMETIC */ - - /* The only remaining cases that we can handle are integers. - Convert to proper endianness now since these cases need it. - At this point, i == 0 means the low-order word. - - We do not want to handle the case when BITS_PER_WORD <= HOST_BITS_PER_INT - in general. However, if OP is (const_int 0), we can just return - it for any word. */ - - if (op == const0_rtx) - return op; - - if (GET_MODE_CLASS (mode) != MODE_INT - || (GET_CODE (op) != CONST_INT && GET_CODE (op) != CONST_DOUBLE) - || BITS_PER_WORD > HOST_BITS_PER_INT) - return 0; - - if (WORDS_BIG_ENDIAN) - i = GET_MODE_SIZE (mode) / UNITS_PER_WORD - 1 - i; - - /* Find out which word on the host machine this value is in and get - it from the constant. */ - val = (i / size_ratio == 0 - ? (GET_CODE (op) == CONST_INT ? INTVAL (op) : CONST_DOUBLE_LOW (op)) - : (GET_CODE (op) == CONST_INT - ? (INTVAL (op) < 0 ? ~0 : 0) : CONST_DOUBLE_HIGH (op))); - - /* If BITS_PER_WORD is smaller than an int, get the appropriate bits. */ - if (BITS_PER_WORD < HOST_BITS_PER_WIDE_INT) - val = ((val >> ((i % size_ratio) * BITS_PER_WORD)) - & (((HOST_WIDE_INT) 1 - << (BITS_PER_WORD % HOST_BITS_PER_WIDE_INT)) - 1)); - - return GEN_INT (val); -} - -/* Similar to `operand_subword', but never return 0. If we can't extract - the required subword, put OP into a register and try again. If that fails, - abort. We always validate the address in this case. It is not valid - to call this function after reload; it is mostly meant for RTL - generation. - - MODE is the mode of OP, in case it is CONST_INT. */ - -rtx -operand_subword_force (op, i, mode) - rtx op; - int i; - enum machine_mode mode; -{ - rtx result = operand_subword (op, i, 1, mode); - - if (result) - return result; - - if (mode != BLKmode && mode != VOIDmode) - op = force_reg (mode, op); - - result = operand_subword (op, i, 1, mode); - if (result == 0) - abort (); - - return result; -} - -/* Given a compare instruction, swap the operands. - A test instruction is changed into a compare of 0 against the operand. */ - -void -reverse_comparison (insn) - rtx insn; -{ - rtx body = PATTERN (insn); - rtx comp; - - if (GET_CODE (body) == SET) - comp = SET_SRC (body); - else - comp = SET_SRC (XVECEXP (body, 0, 0)); - - if (GET_CODE (comp) == COMPARE) - { - rtx op0 = XEXP (comp, 0); - rtx op1 = XEXP (comp, 1); - XEXP (comp, 0) = op1; - XEXP (comp, 1) = op0; - } - else - { - rtx new = gen_rtx (COMPARE, VOIDmode, - CONST0_RTX (GET_MODE (comp)), comp); - if (GET_CODE (body) == SET) - SET_SRC (body) = new; - else - SET_SRC (XVECEXP (body, 0, 0)) = new; - } -} - -/* Return a memory reference like MEMREF, but with its mode changed - to MODE and its address changed to ADDR. - (VOIDmode means don't change the mode. - NULL for ADDR means don't change the address.) */ - -rtx -change_address (memref, mode, addr) - rtx memref; - enum machine_mode mode; - rtx addr; -{ - rtx new; - - if (GET_CODE (memref) != MEM) - abort (); - if (mode == VOIDmode) - mode = GET_MODE (memref); - if (addr == 0) - addr = XEXP (memref, 0); - - /* If reload is in progress or has completed, ADDR must be valid. - Otherwise, we can call memory_address to make it valid. */ - if (reload_completed || reload_in_progress) - { - if (! memory_address_p (mode, addr)) - abort (); - } - else - addr = memory_address (mode, addr); - - new = gen_rtx (MEM, mode, addr); - MEM_VOLATILE_P (new) = MEM_VOLATILE_P (memref); - RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (memref); - MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (memref); - return new; -} - -/* Return a newly created CODE_LABEL rtx with a unique label number. */ - -rtx -gen_label_rtx () -{ - register rtx label = gen_rtx (CODE_LABEL, VOIDmode, 0, 0, 0, - label_num++, NULL_PTR); - LABEL_NUSES (label) = 0; - return label; -} - -/* For procedure integration. */ - -/* Return a newly created INLINE_HEADER rtx. Should allocate this - from a permanent obstack when the opportunity arises. */ - -rtx -gen_inline_header_rtx (first_insn, first_parm_insn, first_labelno, - last_labelno, max_parm_regnum, max_regnum, args_size, - pops_args, stack_slots, function_flags, - outgoing_args_size, original_arg_vector, - original_decl_initial) - rtx first_insn, first_parm_insn; - int first_labelno, last_labelno, max_parm_regnum, max_regnum, args_size; - int pops_args; - rtx stack_slots; - int function_flags; - int outgoing_args_size; - rtvec original_arg_vector; - rtx original_decl_initial; -{ - rtx header = gen_rtx (INLINE_HEADER, VOIDmode, - cur_insn_uid++, NULL_RTX, - first_insn, first_parm_insn, - first_labelno, last_labelno, - max_parm_regnum, max_regnum, args_size, pops_args, - stack_slots, function_flags, outgoing_args_size, - original_arg_vector, original_decl_initial); - return header; -} - -/* Install new pointers to the first and last insns in the chain. - Used for an inline-procedure after copying the insn chain. */ - -void -set_new_first_and_last_insn (first, last) - rtx first, last; -{ - first_insn = first; - last_insn = last; -} - -/* Set the range of label numbers found in the current function. - This is used when belatedly compiling an inline function. */ - -void -set_new_first_and_last_label_num (first, last) - int first, last; -{ - base_label_num = label_num; - first_label_num = first; - last_label_num = last; -} - -/* Save all variables describing the current status into the structure *P. - This is used before starting a nested function. */ - -void -save_emit_status (p) - struct function *p; -{ - p->reg_rtx_no = reg_rtx_no; - p->first_label_num = first_label_num; - p->first_insn = first_insn; - p->last_insn = last_insn; - p->sequence_stack = sequence_stack; - p->cur_insn_uid = cur_insn_uid; - p->last_linenum = last_linenum; - p->last_filename = last_filename; - p->regno_pointer_flag = regno_pointer_flag; - p->regno_pointer_flag_length = regno_pointer_flag_length; - p->regno_reg_rtx = regno_reg_rtx; -} - -/* Restore all variables describing the current status from the structure *P. - This is used after a nested function. */ - -void -restore_emit_status (p) - struct function *p; -{ - int i; - - reg_rtx_no = p->reg_rtx_no; - first_label_num = p->first_label_num; - first_insn = p->first_insn; - last_insn = p->last_insn; - sequence_stack = p->sequence_stack; - cur_insn_uid = p->cur_insn_uid; - last_linenum = p->last_linenum; - last_filename = p->last_filename; - regno_pointer_flag = p->regno_pointer_flag; - regno_pointer_flag_length = p->regno_pointer_flag_length; - regno_reg_rtx = p->regno_reg_rtx; - - /* Clear our cache of rtx expressions for start_sequence and gen_sequence. */ - sequence_element_free_list = 0; - for (i = 0; i < SEQUENCE_RESULT_SIZE; i++) - sequence_result[i] = 0; -} - -/* Go through all the RTL insn bodies and copy any invalid shared structure. - It does not work to do this twice, because the mark bits set here - are not cleared afterwards. */ - -void -unshare_all_rtl (insn) - register rtx insn; -{ - for (; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN - || GET_CODE (insn) == CALL_INSN) - { - PATTERN (insn) = copy_rtx_if_shared (PATTERN (insn)); - REG_NOTES (insn) = copy_rtx_if_shared (REG_NOTES (insn)); - LOG_LINKS (insn) = copy_rtx_if_shared (LOG_LINKS (insn)); - } - - /* Make sure the addresses of stack slots found outside the insn chain - (such as, in DECL_RTL of a variable) are not shared - with the insn chain. - - This special care is necessary when the stack slot MEM does not - actually appear in the insn chain. If it does appear, its address - is unshared from all else at that point. */ - - copy_rtx_if_shared (stack_slot_list); -} - -/* Mark ORIG as in use, and return a copy of it if it was already in use. - Recursively does the same for subexpressions. */ - -rtx -copy_rtx_if_shared (orig) - rtx orig; -{ - register rtx x = orig; - register int i; - register enum rtx_code code; - register char *format_ptr; - int copied = 0; - - if (x == 0) - return 0; - - code = GET_CODE (x); - - /* These types may be freely shared. */ - - switch (code) - { - case REG: - case QUEUED: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case CODE_LABEL: - case PC: - case CC0: - case SCRATCH: - /* SCRATCH must be shared because they represent distinct values. */ - return x; - - case CONST: - /* CONST can be shared if it contains a SYMBOL_REF. If it contains - a LABEL_REF, it isn't sharable. */ - if (GET_CODE (XEXP (x, 0)) == PLUS - && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) - return x; - break; - - case INSN: - case JUMP_INSN: - case CALL_INSN: - case NOTE: - case LABEL_REF: - case BARRIER: - /* The chain of insns is not being copied. */ - return x; - - case MEM: - /* A MEM is allowed to be shared if its address is constant - or is a constant plus one of the special registers. */ - if (CONSTANT_ADDRESS_P (XEXP (x, 0)) - || XEXP (x, 0) == virtual_stack_vars_rtx - || XEXP (x, 0) == virtual_incoming_args_rtx) - return x; - - if (GET_CODE (XEXP (x, 0)) == PLUS - && (XEXP (XEXP (x, 0), 0) == virtual_stack_vars_rtx - || XEXP (XEXP (x, 0), 0) == virtual_incoming_args_rtx) - && CONSTANT_ADDRESS_P (XEXP (XEXP (x, 0), 1))) - { - /* This MEM can appear in more than one place, - but its address better not be shared with anything else. */ - if (! x->used) - XEXP (x, 0) = copy_rtx_if_shared (XEXP (x, 0)); - x->used = 1; - return x; - } - } - - /* This rtx may not be shared. If it has already been seen, - replace it with a copy of itself. */ - - if (x->used) - { - register rtx copy; - - copy = rtx_alloc (code); - bcopy (x, copy, (sizeof (*copy) - sizeof (copy->fld) - + sizeof (copy->fld[0]) * GET_RTX_LENGTH (code))); - x = copy; - copied = 1; - } - x->used = 1; - - /* Now scan the subexpressions recursively. - We can store any replaced subexpressions directly into X - since we know X is not shared! Any vectors in X - must be copied if X was copied. */ - - format_ptr = GET_RTX_FORMAT (code); - - for (i = 0; i < GET_RTX_LENGTH (code); i++) - { - switch (*format_ptr++) - { - case 'e': - XEXP (x, i) = copy_rtx_if_shared (XEXP (x, i)); - break; - - case 'E': - if (XVEC (x, i) != NULL) - { - register int j; - - if (copied) - XVEC (x, i) = gen_rtvec_v (XVECLEN (x, i), &XVECEXP (x, i, 0)); - for (j = 0; j < XVECLEN (x, i); j++) - XVECEXP (x, i, j) - = copy_rtx_if_shared (XVECEXP (x, i, j)); - } - break; - } - } - return x; -} - -/* Clear all the USED bits in X to allow copy_rtx_if_shared to be used - to look for shared sub-parts. */ - -void -reset_used_flags (x) - rtx x; -{ - register int i, j; - register enum rtx_code code; - register char *format_ptr; - int copied = 0; - - if (x == 0) - return; - - code = GET_CODE (x); - - /* These types may be freely shared so we needn't do any reseting - for them. */ - - switch (code) - { - case REG: - case QUEUED: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case CODE_LABEL: - case PC: - case CC0: - return; - - case INSN: - case JUMP_INSN: - case CALL_INSN: - case NOTE: - case LABEL_REF: - case BARRIER: - /* The chain of insns is not being copied. */ - return; - } - - x->used = 0; - - format_ptr = GET_RTX_FORMAT (code); - for (i = 0; i < GET_RTX_LENGTH (code); i++) - { - switch (*format_ptr++) - { - case 'e': - reset_used_flags (XEXP (x, i)); - break; - - case 'E': - for (j = 0; j < XVECLEN (x, i); j++) - reset_used_flags (XVECEXP (x, i, j)); - break; - } - } -} - -/* Copy X if necessary so that it won't be altered by changes in OTHER. - Return X or the rtx for the pseudo reg the value of X was copied into. - OTHER must be valid as a SET_DEST. */ - -rtx -make_safe_from (x, other) - rtx x, other; -{ - while (1) - switch (GET_CODE (other)) - { - case SUBREG: - other = SUBREG_REG (other); - break; - case STRICT_LOW_PART: - case SIGN_EXTEND: - case ZERO_EXTEND: - other = XEXP (other, 0); - break; - default: - goto done; - } - done: - if ((GET_CODE (other) == MEM - && ! CONSTANT_P (x) - && GET_CODE (x) != REG - && GET_CODE (x) != SUBREG) - || (GET_CODE (other) == REG - && (REGNO (other) < FIRST_PSEUDO_REGISTER - || reg_mentioned_p (other, x)))) - { - rtx temp = gen_reg_rtx (GET_MODE (x)); - emit_move_insn (temp, x); - return temp; - } - return x; -} - -/* Emission of insns (adding them to the doubly-linked list). */ - -/* Return the first insn of the current sequence or current function. */ - -rtx -get_insns () -{ - return first_insn; -} - -/* Return the last insn emitted in current sequence or current function. */ - -rtx -get_last_insn () -{ - return last_insn; -} - -/* Specify a new insn as the last in the chain. */ - -void -set_last_insn (insn) - rtx insn; -{ - if (NEXT_INSN (insn) != 0) - abort (); - last_insn = insn; -} - -/* Return the last insn emitted, even if it is in a sequence now pushed. */ - -rtx -get_last_insn_anywhere () -{ - struct sequence_stack *stack; - if (last_insn) - return last_insn; - for (stack = sequence_stack; stack; stack = stack->next) - if (stack->last != 0) - return stack->last; - return 0; -} - -/* Return a number larger than any instruction's uid in this function. */ - -int -get_max_uid () -{ - return cur_insn_uid; -} - -/* Return the next insn. If it is a SEQUENCE, return the first insn - of the sequence. */ - -rtx -next_insn (insn) - rtx insn; -{ - if (insn) - { - insn = NEXT_INSN (insn); - if (insn && GET_CODE (insn) == INSN - && GET_CODE (PATTERN (insn)) == SEQUENCE) - insn = XVECEXP (PATTERN (insn), 0, 0); - } - - return insn; -} - -/* Return the previous insn. If it is a SEQUENCE, return the last insn - of the sequence. */ - -rtx -previous_insn (insn) - rtx insn; -{ - if (insn) - { - insn = PREV_INSN (insn); - if (insn && GET_CODE (insn) == INSN - && GET_CODE (PATTERN (insn)) == SEQUENCE) - insn = XVECEXP (PATTERN (insn), 0, XVECLEN (PATTERN (insn), 0) - 1); - } - - return insn; -} - -/* Return the next insn after INSN that is not a NOTE. This routine does not - look inside SEQUENCEs. */ - -rtx -next_nonnote_insn (insn) - rtx insn; -{ - while (insn) - { - insn = NEXT_INSN (insn); - if (insn == 0 || GET_CODE (insn) != NOTE) - break; - } - - return insn; -} - -/* Return the previous insn before INSN that is not a NOTE. This routine does - not look inside SEQUENCEs. */ - -rtx -prev_nonnote_insn (insn) - rtx insn; -{ - while (insn) - { - insn = PREV_INSN (insn); - if (insn == 0 || GET_CODE (insn) != NOTE) - break; - } - - return insn; -} - -/* Return the next INSN, CALL_INSN or JUMP_INSN after INSN; - or 0, if there is none. This routine does not look inside - SEQUENCEs. */ - -rtx -next_real_insn (insn) - rtx insn; -{ - while (insn) - { - insn = NEXT_INSN (insn); - if (insn == 0 || GET_CODE (insn) == INSN - || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN) - break; - } - - return insn; -} - -/* Return the last INSN, CALL_INSN or JUMP_INSN before INSN; - or 0, if there is none. This routine does not look inside - SEQUENCEs. */ - -rtx -prev_real_insn (insn) - rtx insn; -{ - while (insn) - { - insn = PREV_INSN (insn); - if (insn == 0 || GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN - || GET_CODE (insn) == JUMP_INSN) - break; - } - - return insn; -} - -/* Find the next insn after INSN that really does something. This routine - does not look inside SEQUENCEs. Until reload has completed, this is the - same as next_real_insn. */ - -rtx -next_active_insn (insn) - rtx insn; -{ - while (insn) - { - insn = NEXT_INSN (insn); - if (insn == 0 - || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN - || (GET_CODE (insn) == INSN - && (! reload_completed - || (GET_CODE (PATTERN (insn)) != USE - && GET_CODE (PATTERN (insn)) != CLOBBER)))) - break; - } - - return insn; -} - -/* Find the last insn before INSN that really does something. This routine - does not look inside SEQUENCEs. Until reload has completed, this is the - same as prev_real_insn. */ - -rtx -prev_active_insn (insn) - rtx insn; -{ - while (insn) - { - insn = PREV_INSN (insn); - if (insn == 0 - || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN - || (GET_CODE (insn) == INSN - && (! reload_completed - || (GET_CODE (PATTERN (insn)) != USE - && GET_CODE (PATTERN (insn)) != CLOBBER)))) - break; - } - - return insn; -} - -/* Return the next CODE_LABEL after the insn INSN, or 0 if there is none. */ - -rtx -next_label (insn) - rtx insn; -{ - while (insn) - { - insn = NEXT_INSN (insn); - if (insn == 0 || GET_CODE (insn) == CODE_LABEL) - break; - } - - return insn; -} - -/* Return the last CODE_LABEL before the insn INSN, or 0 if there is none. */ - -rtx -prev_label (insn) - rtx insn; -{ - while (insn) - { - insn = PREV_INSN (insn); - if (insn == 0 || GET_CODE (insn) == CODE_LABEL) - break; - } - - return insn; -} - -#ifdef HAVE_cc0 -/* INSN uses CC0 and is being moved into a delay slot. Set up REG_CC_SETTER - and REG_CC_USER notes so we can find it. */ - -void -link_cc0_insns (insn) - rtx insn; -{ - rtx user = next_nonnote_insn (insn); - - if (GET_CODE (user) == INSN && GET_CODE (PATTERN (user)) == SEQUENCE) - user = XVECEXP (PATTERN (user), 0, 0); - - REG_NOTES (user) = gen_rtx (INSN_LIST, REG_CC_SETTER, insn, - REG_NOTES (user)); - REG_NOTES (insn) = gen_rtx (INSN_LIST, REG_CC_USER, user, REG_NOTES (insn)); -} - -/* Return the next insn that uses CC0 after INSN, which is assumed to - set it. This is the inverse of prev_cc0_setter (i.e., prev_cc0_setter - applied to the result of this function should yield INSN). - - Normally, this is simply the next insn. However, if a REG_CC_USER note - is present, it contains the insn that uses CC0. - - Return 0 if we can't find the insn. */ - -rtx -next_cc0_user (insn) - rtx insn; -{ - rtx note = find_reg_note (insn, REG_CC_USER, NULL_RTX); - - if (note) - return XEXP (note, 0); - - insn = next_nonnote_insn (insn); - if (insn && GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE) - insn = XVECEXP (PATTERN (insn), 0, 0); - - if (insn && GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && reg_mentioned_p (cc0_rtx, PATTERN (insn))) - return insn; - - return 0; -} - -/* Find the insn that set CC0 for INSN. Unless INSN has a REG_CC_SETTER - note, it is the previous insn. */ - -rtx -prev_cc0_setter (insn) - rtx insn; -{ - rtx note = find_reg_note (insn, REG_CC_SETTER, NULL_RTX); - rtx link; - - if (note) - return XEXP (note, 0); - - insn = prev_nonnote_insn (insn); - if (! sets_cc0_p (PATTERN (insn))) - abort (); - - return insn; -} -#endif - -/* Try splitting insns that can be split for better scheduling. - PAT is the pattern which might split. - TRIAL is the insn providing PAT. - BACKWARDS is non-zero if we are scanning insns from last to first. - - If this routine succeeds in splitting, it returns the first or last - replacement insn depending on the value of BACKWARDS. Otherwise, it - returns TRIAL. If the insn to be returned can be split, it will be. */ - -rtx -try_split (pat, trial, backwards) - rtx pat, trial; - int backwards; -{ - rtx before = PREV_INSN (trial); - rtx after = NEXT_INSN (trial); - rtx seq = split_insns (pat, trial); - int has_barrier = 0; - rtx tem; - - /* If we are splitting a JUMP_INSN, it might be followed by a BARRIER. - We may need to handle this specially. */ - if (after && GET_CODE (after) == BARRIER) - { - has_barrier = 1; - after = NEXT_INSN (after); - } - - if (seq) - { - /* SEQ can either be a SEQUENCE or the pattern of a single insn. - The latter case will normally arise only when being done so that - it, in turn, will be split (SFmode on the 29k is an example). */ - if (GET_CODE (seq) == SEQUENCE) - { - /* If we are splitting a JUMP_INSN, look for the JUMP_INSN in - SEQ and copy our JUMP_LABEL to it. If JUMP_LABEL is non-zero, - increment the usage count so we don't delete the label. */ - int i; - - if (GET_CODE (trial) == JUMP_INSN) - for (i = XVECLEN (seq, 0) - 1; i >= 0; i--) - if (GET_CODE (XVECEXP (seq, 0, i)) == JUMP_INSN) - { - JUMP_LABEL (XVECEXP (seq, 0, i)) = JUMP_LABEL (trial); - - if (JUMP_LABEL (trial)) - LABEL_NUSES (JUMP_LABEL (trial))++; - } - - tem = emit_insn_after (seq, before); - - delete_insn (trial); - if (has_barrier) - emit_barrier_after (tem); - } - /* Avoid infinite loop if the result matches the original pattern. */ - else if (rtx_equal_p (seq, pat)) - return trial; - else - { - PATTERN (trial) = seq; - INSN_CODE (trial) = -1; - } - - /* Set TEM to the insn we should return. */ - tem = backwards ? prev_active_insn (after) : next_active_insn (before); - return try_split (PATTERN (tem), tem, backwards); - } - - return trial; -} - -/* Make and return an INSN rtx, initializing all its slots. - Store PATTERN in the pattern slots. */ - -rtx -make_insn_raw (pattern) - rtx pattern; -{ - register rtx insn; - - insn = rtx_alloc (INSN); - INSN_UID (insn) = cur_insn_uid++; - - PATTERN (insn) = pattern; - INSN_CODE (insn) = -1; - LOG_LINKS (insn) = NULL; - REG_NOTES (insn) = NULL; - - return insn; -} - -/* Like `make_insn' but make a JUMP_INSN instead of an insn. */ - -static rtx -make_jump_insn_raw (pattern) - rtx pattern; -{ - register rtx insn; - - insn = rtx_alloc (JUMP_INSN); - INSN_UID (insn) = cur_insn_uid++; - - PATTERN (insn) = pattern; - INSN_CODE (insn) = -1; - LOG_LINKS (insn) = NULL; - REG_NOTES (insn) = NULL; - JUMP_LABEL (insn) = NULL; - - return insn; -} - -/* Add INSN to the end of the doubly-linked list. - INSN may be an INSN, JUMP_INSN, CALL_INSN, CODE_LABEL, BARRIER or NOTE. */ - -void -add_insn (insn) - register rtx insn; -{ - PREV_INSN (insn) = last_insn; - NEXT_INSN (insn) = 0; - - if (NULL != last_insn) - NEXT_INSN (last_insn) = insn; - - if (NULL == first_insn) - first_insn = insn; - - last_insn = insn; -} - -/* Add INSN into the doubly-linked list after insn AFTER. This should be the - only function called to insert an insn once delay slots have been filled - since only it knows how to update a SEQUENCE. */ - -void -add_insn_after (insn, after) - rtx insn, after; -{ - rtx next = NEXT_INSN (after); - - NEXT_INSN (insn) = next; - PREV_INSN (insn) = after; - - if (next) - { - PREV_INSN (next) = insn; - if (GET_CODE (next) == INSN && GET_CODE (PATTERN (next)) == SEQUENCE) - PREV_INSN (XVECEXP (PATTERN (next), 0, 0)) = insn; - } - else if (last_insn == after) - last_insn = insn; - else - { - struct sequence_stack *stack = sequence_stack; - /* Scan all pending sequences too. */ - for (; stack; stack = stack->next) - if (after == stack->last) - stack->last = insn; - } - - NEXT_INSN (after) = insn; - if (GET_CODE (after) == INSN && GET_CODE (PATTERN (after)) == SEQUENCE) - { - rtx sequence = PATTERN (after); - NEXT_INSN (XVECEXP (sequence, 0, XVECLEN (sequence, 0) - 1)) = insn; - } -} - -/* Delete all insns made since FROM. - FROM becomes the new last instruction. */ - -void -delete_insns_since (from) - rtx from; -{ - if (from == 0) - first_insn = 0; - else - NEXT_INSN (from) = 0; - last_insn = from; -} - -/* Move a consecutive bunch of insns to a different place in the chain. - The insns to be moved are those between FROM and TO. - They are moved to a new position after the insn AFTER. - AFTER must not be FROM or TO or any insn in between. - - This function does not know about SEQUENCEs and hence should not be - called after delay-slot filling has been done. */ - -void -reorder_insns (from, to, after) - rtx from, to, after; -{ - /* Splice this bunch out of where it is now. */ - if (PREV_INSN (from)) - NEXT_INSN (PREV_INSN (from)) = NEXT_INSN (to); - if (NEXT_INSN (to)) - PREV_INSN (NEXT_INSN (to)) = PREV_INSN (from); - if (last_insn == to) - last_insn = PREV_INSN (from); - if (first_insn == from) - first_insn = NEXT_INSN (to); - - /* Make the new neighbors point to it and it to them. */ - if (NEXT_INSN (after)) - PREV_INSN (NEXT_INSN (after)) = to; - - NEXT_INSN (to) = NEXT_INSN (after); - PREV_INSN (from) = after; - NEXT_INSN (after) = from; - if (after == last_insn) - last_insn = to; -} - -/* Return the line note insn preceding INSN. */ - -static rtx -find_line_note (insn) - rtx insn; -{ - if (no_line_numbers) - return 0; - - for (; insn; insn = PREV_INSN (insn)) - if (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) >= 0) - break; - - return insn; -} - -/* Like reorder_insns, but inserts line notes to preserve the line numbers - of the moved insns when debugging. This may insert a note between AFTER - and FROM, and another one after TO. */ - -void -reorder_insns_with_line_notes (from, to, after) - rtx from, to, after; -{ - rtx from_line = find_line_note (from); - rtx after_line = find_line_note (after); - - reorder_insns (from, to, after); - - if (from_line == after_line) - return; - - if (from_line) - emit_line_note_after (NOTE_SOURCE_FILE (from_line), - NOTE_LINE_NUMBER (from_line), - after); - if (after_line) - emit_line_note_after (NOTE_SOURCE_FILE (after_line), - NOTE_LINE_NUMBER (after_line), - to); -} - -/* Emit an insn of given code and pattern - at a specified place within the doubly-linked list. */ - -/* Make an instruction with body PATTERN - and output it before the instruction BEFORE. */ - -rtx -emit_insn_before (pattern, before) - register rtx pattern, before; -{ - register rtx insn = before; - - if (GET_CODE (pattern) == SEQUENCE) - { - register int i; - - for (i = 0; i < XVECLEN (pattern, 0); i++) - { - insn = XVECEXP (pattern, 0, i); - add_insn_after (insn, PREV_INSN (before)); - } - if (XVECLEN (pattern, 0) < SEQUENCE_RESULT_SIZE) - sequence_result[XVECLEN (pattern, 0)] = pattern; - } - else - { - insn = make_insn_raw (pattern); - add_insn_after (insn, PREV_INSN (before)); - } - - return insn; -} - -/* Make an instruction with body PATTERN and code JUMP_INSN - and output it before the instruction BEFORE. */ - -rtx -emit_jump_insn_before (pattern, before) - register rtx pattern, before; -{ - register rtx insn; - - if (GET_CODE (pattern) == SEQUENCE) - insn = emit_insn_before (pattern, before); - else - { - insn = make_jump_insn_raw (pattern); - add_insn_after (insn, PREV_INSN (before)); - } - - return insn; -} - -/* Make an instruction with body PATTERN and code CALL_INSN - and output it before the instruction BEFORE. */ - -rtx -emit_call_insn_before (pattern, before) - register rtx pattern, before; -{ - rtx insn = emit_insn_before (pattern, before); - PUT_CODE (insn, CALL_INSN); - return insn; -} - -/* Make an insn of code BARRIER - and output it before the insn AFTER. */ - -rtx -emit_barrier_before (before) - register rtx before; -{ - register rtx insn = rtx_alloc (BARRIER); - - INSN_UID (insn) = cur_insn_uid++; - - add_insn_after (insn, PREV_INSN (before)); - return insn; -} - -/* Emit a note of subtype SUBTYPE before the insn BEFORE. */ - -rtx -emit_note_before (subtype, before) - int subtype; - rtx before; -{ - register rtx note = rtx_alloc (NOTE); - INSN_UID (note) = cur_insn_uid++; - NOTE_SOURCE_FILE (note) = 0; - NOTE_LINE_NUMBER (note) = subtype; - - add_insn_after (note, PREV_INSN (before)); - return note; -} - -/* Make an insn of code INSN with body PATTERN - and output it after the insn AFTER. */ - -rtx -emit_insn_after (pattern, after) - register rtx pattern, after; -{ - register rtx insn = after; - - if (GET_CODE (pattern) == SEQUENCE) - { - register int i; - - for (i = 0; i < XVECLEN (pattern, 0); i++) - { - insn = XVECEXP (pattern, 0, i); - add_insn_after (insn, after); - after = insn; - } - if (XVECLEN (pattern, 0) < SEQUENCE_RESULT_SIZE) - sequence_result[XVECLEN (pattern, 0)] = pattern; - } - else - { - insn = make_insn_raw (pattern); - add_insn_after (insn, after); - } - - return insn; -} - -/* Similar to emit_insn_after, except that line notes are to be inserted so - as to act as if this insn were at FROM. */ - -void -emit_insn_after_with_line_notes (pattern, after, from) - rtx pattern, after, from; -{ - rtx from_line = find_line_note (from); - rtx after_line = find_line_note (after); - rtx insn = emit_insn_after (pattern, after); - - if (from_line) - emit_line_note_after (NOTE_SOURCE_FILE (from_line), - NOTE_LINE_NUMBER (from_line), - after); - - if (after_line) - emit_line_note_after (NOTE_SOURCE_FILE (after_line), - NOTE_LINE_NUMBER (after_line), - insn); -} - -/* Make an insn of code JUMP_INSN with body PATTERN - and output it after the insn AFTER. */ - -rtx -emit_jump_insn_after (pattern, after) - register rtx pattern, after; -{ - register rtx insn; - - if (GET_CODE (pattern) == SEQUENCE) - insn = emit_insn_after (pattern, after); - else - { - insn = make_jump_insn_raw (pattern); - add_insn_after (insn, after); - } - - return insn; -} - -/* Make an insn of code BARRIER - and output it after the insn AFTER. */ - -rtx -emit_barrier_after (after) - register rtx after; -{ - register rtx insn = rtx_alloc (BARRIER); - - INSN_UID (insn) = cur_insn_uid++; - - add_insn_after (insn, after); - return insn; -} - -/* Emit the label LABEL after the insn AFTER. */ - -rtx -emit_label_after (label, after) - rtx label, after; -{ - /* This can be called twice for the same label - as a result of the confusion that follows a syntax error! - So make it harmless. */ - if (INSN_UID (label) == 0) - { - INSN_UID (label) = cur_insn_uid++; - add_insn_after (label, after); - } - - return label; -} - -/* Emit a note of subtype SUBTYPE after the insn AFTER. */ - -rtx -emit_note_after (subtype, after) - int subtype; - rtx after; -{ - register rtx note = rtx_alloc (NOTE); - INSN_UID (note) = cur_insn_uid++; - NOTE_SOURCE_FILE (note) = 0; - NOTE_LINE_NUMBER (note) = subtype; - add_insn_after (note, after); - return note; -} - -/* Emit a line note for FILE and LINE after the insn AFTER. */ - -rtx -emit_line_note_after (file, line, after) - char *file; - int line; - rtx after; -{ - register rtx note; - - if (no_line_numbers && line > 0) - { - cur_insn_uid++; - return 0; - } - - note = rtx_alloc (NOTE); - INSN_UID (note) = cur_insn_uid++; - NOTE_SOURCE_FILE (note) = file; - NOTE_LINE_NUMBER (note) = line; - add_insn_after (note, after); - return note; -} - -/* Make an insn of code INSN with pattern PATTERN - and add it to the end of the doubly-linked list. - If PATTERN is a SEQUENCE, take the elements of it - and emit an insn for each element. - - Returns the last insn emitted. */ - -rtx -emit_insn (pattern) - rtx pattern; -{ - rtx insn = last_insn; - - if (GET_CODE (pattern) == SEQUENCE) - { - register int i; - - for (i = 0; i < XVECLEN (pattern, 0); i++) - { - insn = XVECEXP (pattern, 0, i); - add_insn (insn); - } - if (XVECLEN (pattern, 0) < SEQUENCE_RESULT_SIZE) - sequence_result[XVECLEN (pattern, 0)] = pattern; - } - else - { - insn = make_insn_raw (pattern); - add_insn (insn); - } - - return insn; -} - -/* Emit the insns in a chain starting with INSN. - Return the last insn emitted. */ - -rtx -emit_insns (insn) - rtx insn; -{ - rtx last = 0; - - while (insn) - { - rtx next = NEXT_INSN (insn); - add_insn (insn); - last = insn; - insn = next; - } - - return last; -} - -/* Emit the insns in a chain starting with INSN and place them in front of - the insn BEFORE. Return the last insn emitted. */ - -rtx -emit_insns_before (insn, before) - rtx insn; - rtx before; -{ - rtx last = 0; - - while (insn) - { - rtx next = NEXT_INSN (insn); - add_insn_after (insn, PREV_INSN (before)); - last = insn; - insn = next; - } - - return last; -} - -/* Emit the insns in a chain starting with FIRST and place them in back of - the insn AFTER. Return the last insn emitted. */ - -rtx -emit_insns_after (first, after) - register rtx first; - register rtx after; -{ - register rtx last; - register rtx after_after; - - if (!after) - abort (); - - if (!first) - return first; - - for (last = first; NEXT_INSN (last); last = NEXT_INSN (last)) - continue; - - after_after = NEXT_INSN (after); - - NEXT_INSN (after) = first; - PREV_INSN (first) = after; - NEXT_INSN (last) = after_after; - if (after_after) - PREV_INSN (after_after) = last; - - if (after == last_insn) - last_insn = last; - return last; -} - -/* Make an insn of code JUMP_INSN with pattern PATTERN - and add it to the end of the doubly-linked list. */ - -rtx -emit_jump_insn (pattern) - rtx pattern; -{ - if (GET_CODE (pattern) == SEQUENCE) - return emit_insn (pattern); - else - { - register rtx insn = make_jump_insn_raw (pattern); - add_insn (insn); - return insn; - } -} - -/* Make an insn of code CALL_INSN with pattern PATTERN - and add it to the end of the doubly-linked list. */ - -rtx -emit_call_insn (pattern) - rtx pattern; -{ - if (GET_CODE (pattern) == SEQUENCE) - return emit_insn (pattern); - else - { - register rtx insn = make_insn_raw (pattern); - add_insn (insn); - PUT_CODE (insn, CALL_INSN); - return insn; - } -} - -/* Add the label LABEL to the end of the doubly-linked list. */ - -rtx -emit_label (label) - rtx label; -{ - /* This can be called twice for the same label - as a result of the confusion that follows a syntax error! - So make it harmless. */ - if (INSN_UID (label) == 0) - { - INSN_UID (label) = cur_insn_uid++; - add_insn (label); - } - return label; -} - -/* Make an insn of code BARRIER - and add it to the end of the doubly-linked list. */ - -rtx -emit_barrier () -{ - register rtx barrier = rtx_alloc (BARRIER); - INSN_UID (barrier) = cur_insn_uid++; - add_insn (barrier); - return barrier; -} - -/* Make an insn of code NOTE - with data-fields specified by FILE and LINE - and add it to the end of the doubly-linked list, - but only if line-numbers are desired for debugging info. */ - -rtx -emit_line_note (file, line) - char *file; - int line; -{ - emit_filename = file; - emit_lineno = line; - -#if 0 - if (no_line_numbers) - return 0; -#endif - - return emit_note (file, line); -} - -/* Make an insn of code NOTE - with data-fields specified by FILE and LINE - and add it to the end of the doubly-linked list. - If it is a line-number NOTE, omit it if it matches the previous one. */ - -rtx -emit_note (file, line) - char *file; - int line; -{ - register rtx note; - - if (line > 0) - { - if (file && last_filename && !strcmp (file, last_filename) - && line == last_linenum) - return 0; - last_filename = file; - last_linenum = line; - } - - if (no_line_numbers && line > 0) - { - cur_insn_uid++; - return 0; - } - - note = rtx_alloc (NOTE); - INSN_UID (note) = cur_insn_uid++; - NOTE_SOURCE_FILE (note) = file; - NOTE_LINE_NUMBER (note) = line; - add_insn (note); - return note; -} - -/* Emit a NOTE, and don't omit it even if LINE it the previous note. */ - -rtx -emit_line_note_force (file, line) - char *file; - int line; -{ - last_linenum = -1; - return emit_line_note (file, line); -} - -/* Cause next statement to emit a line note even if the line number - has not changed. This is used at the beginning of a function. */ - -void -force_next_line_note () -{ - last_linenum = -1; -} - -/* Return an indication of which type of insn should have X as a body. - The value is CODE_LABEL, INSN, CALL_INSN or JUMP_INSN. */ - -enum rtx_code -classify_insn (x) - rtx x; -{ - if (GET_CODE (x) == CODE_LABEL) - return CODE_LABEL; - if (GET_CODE (x) == CALL) - return CALL_INSN; - if (GET_CODE (x) == RETURN) - return JUMP_INSN; - if (GET_CODE (x) == SET) - { - if (SET_DEST (x) == pc_rtx) - return JUMP_INSN; - else if (GET_CODE (SET_SRC (x)) == CALL) - return CALL_INSN; - else - return INSN; - } - if (GET_CODE (x) == PARALLEL) - { - register int j; - for (j = XVECLEN (x, 0) - 1; j >= 0; j--) - if (GET_CODE (XVECEXP (x, 0, j)) == CALL) - return CALL_INSN; - else if (GET_CODE (XVECEXP (x, 0, j)) == SET - && SET_DEST (XVECEXP (x, 0, j)) == pc_rtx) - return JUMP_INSN; - else if (GET_CODE (XVECEXP (x, 0, j)) == SET - && GET_CODE (SET_SRC (XVECEXP (x, 0, j))) == CALL) - return CALL_INSN; - } - return INSN; -} - -/* Emit the rtl pattern X as an appropriate kind of insn. - If X is a label, it is simply added into the insn chain. */ - -rtx -emit (x) - rtx x; -{ - enum rtx_code code = classify_insn (x); - - if (code == CODE_LABEL) - return emit_label (x); - else if (code == INSN) - return emit_insn (x); - else if (code == JUMP_INSN) - { - register rtx insn = emit_jump_insn (x); - if (simplejump_p (insn) || GET_CODE (x) == RETURN) - return emit_barrier (); - return insn; - } - else if (code == CALL_INSN) - return emit_call_insn (x); - else - abort (); -} - -/* Begin emitting insns to a sequence which can be packaged in an RTL_EXPR. */ - -void -start_sequence () -{ - struct sequence_stack *tem; - - if (sequence_element_free_list) - { - /* Reuse a previously-saved struct sequence_stack. */ - tem = sequence_element_free_list; - sequence_element_free_list = tem->next; - } - else - tem = (struct sequence_stack *) permalloc (sizeof (struct sequence_stack)); - - tem->next = sequence_stack; - tem->first = first_insn; - tem->last = last_insn; - - sequence_stack = tem; - - first_insn = 0; - last_insn = 0; -} - -/* Set up the insn chain starting with FIRST - as the current sequence, saving the previously current one. */ - -void -push_to_sequence (first) - rtx first; -{ - rtx last; - - start_sequence (); - - for (last = first; last && NEXT_INSN (last); last = NEXT_INSN (last)); - - first_insn = first; - last_insn = last; -} - -/* Set up the outer-level insn chain - as the current sequence, saving the previously current one. */ - -void -push_topmost_sequence () -{ - struct sequence_stack *stack, *top; - - start_sequence (); - - for (stack = sequence_stack; stack; stack = stack->next) - top = stack; - - first_insn = top->first; - last_insn = top->last; -} - -/* After emitting to the outer-level insn chain, update the outer-level - insn chain, and restore the previous saved state. */ - -void -pop_topmost_sequence () -{ - struct sequence_stack *stack, *top; - - for (stack = sequence_stack; stack; stack = stack->next) - top = stack; - - top->first = first_insn; - top->last = last_insn; - - end_sequence (); -} - -/* After emitting to a sequence, restore previous saved state. - - To get the contents of the sequence just made, - you must call `gen_sequence' *before* calling here. */ - -void -end_sequence () -{ - struct sequence_stack *tem = sequence_stack; - - first_insn = tem->first; - last_insn = tem->last; - sequence_stack = tem->next; - - tem->next = sequence_element_free_list; - sequence_element_free_list = tem; -} - -/* Return 1 if currently emitting into a sequence. */ - -int -in_sequence_p () -{ - return sequence_stack != 0; -} - -/* Generate a SEQUENCE rtx containing the insns already emitted - to the current sequence. - - This is how the gen_... function from a DEFINE_EXPAND - constructs the SEQUENCE that it returns. */ - -rtx -gen_sequence () -{ - rtx result; - rtx tem; - rtvec newvec; - int i; - int len; - - /* Count the insns in the chain. */ - len = 0; - for (tem = first_insn; tem; tem = NEXT_INSN (tem)) - len++; - - /* If only one insn, return its pattern rather than a SEQUENCE. - (Now that we cache SEQUENCE expressions, it isn't worth special-casing - the case of an empty list.) */ - if (len == 1 - && (GET_CODE (first_insn) == INSN - || GET_CODE (first_insn) == JUMP_INSN - || GET_CODE (first_insn) == CALL_INSN)) - return PATTERN (first_insn); - - /* Put them in a vector. See if we already have a SEQUENCE of the - appropriate length around. */ - if (len < SEQUENCE_RESULT_SIZE && (result = sequence_result[len]) != 0) - sequence_result[len] = 0; - else - { - /* Ensure that this rtl goes in saveable_obstack, since we may be - caching it. */ - push_obstacks_nochange (); - rtl_in_saveable_obstack (); - result = gen_rtx (SEQUENCE, VOIDmode, rtvec_alloc (len)); - pop_obstacks (); - } - - for (i = 0, tem = first_insn; tem; tem = NEXT_INSN (tem), i++) - XVECEXP (result, 0, i) = tem; - - return result; -} - -/* Set up regno_reg_rtx, reg_rtx_no and regno_pointer_flag - according to the chain of insns starting with FIRST. - - Also set cur_insn_uid to exceed the largest uid in that chain. - - This is used when an inline function's rtl is saved - and passed to rest_of_compilation later. */ - -static void restore_reg_data_1 (); - -void -restore_reg_data (first) - rtx first; -{ - register rtx insn; - int i; - register int max_uid = 0; - - for (insn = first; insn; insn = NEXT_INSN (insn)) - { - if (INSN_UID (insn) >= max_uid) - max_uid = INSN_UID (insn); - - switch (GET_CODE (insn)) - { - case NOTE: - case CODE_LABEL: - case BARRIER: - break; - - case JUMP_INSN: - case CALL_INSN: - case INSN: - restore_reg_data_1 (PATTERN (insn)); - break; - } - } - - /* Don't duplicate the uids already in use. */ - cur_insn_uid = max_uid + 1; - - /* If any regs are missing, make them up. - - ??? word_mode is not necessarily the right mode. Most likely these REGs - are never used. At some point this should be checked. */ - - for (i = FIRST_PSEUDO_REGISTER; i < reg_rtx_no; i++) - if (regno_reg_rtx[i] == 0) - regno_reg_rtx[i] = gen_rtx (REG, word_mode, i); -} - -static void -restore_reg_data_1 (orig) - rtx orig; -{ - register rtx x = orig; - register int i; - register enum rtx_code code; - register char *format_ptr; - - code = GET_CODE (x); - - switch (code) - { - case QUEUED: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case CODE_LABEL: - case PC: - case CC0: - case LABEL_REF: - return; - - case REG: - if (REGNO (x) >= FIRST_PSEUDO_REGISTER) - { - /* Make sure regno_pointer_flag and regno_reg_rtx are large - enough to have an element for this pseudo reg number. */ - if (REGNO (x) >= reg_rtx_no) - { - reg_rtx_no = REGNO (x); - - if (reg_rtx_no >= regno_pointer_flag_length) - { - int newlen = MAX (regno_pointer_flag_length * 2, - reg_rtx_no + 30); - rtx *new1; - char *new = (char *) oballoc (newlen); - bzero (new, newlen); - bcopy (regno_pointer_flag, new, regno_pointer_flag_length); - - new1 = (rtx *) oballoc (newlen * sizeof (rtx)); - bzero (new1, newlen * sizeof (rtx)); - bcopy (regno_reg_rtx, new1, regno_pointer_flag_length * sizeof (rtx)); - - regno_pointer_flag = new; - regno_reg_rtx = new1; - regno_pointer_flag_length = newlen; - } - reg_rtx_no ++; - } - regno_reg_rtx[REGNO (x)] = x; - } - return; - - case MEM: - if (GET_CODE (XEXP (x, 0)) == REG) - mark_reg_pointer (XEXP (x, 0)); - restore_reg_data_1 (XEXP (x, 0)); - return; - } - - /* Now scan the subexpressions recursively. */ - - format_ptr = GET_RTX_FORMAT (code); - - for (i = 0; i < GET_RTX_LENGTH (code); i++) - { - switch (*format_ptr++) - { - case 'e': - restore_reg_data_1 (XEXP (x, i)); - break; - - case 'E': - if (XVEC (x, i) != NULL) - { - register int j; - - for (j = 0; j < XVECLEN (x, i); j++) - restore_reg_data_1 (XVECEXP (x, i, j)); - } - break; - } - } -} - -/* Initialize data structures and variables in this file - before generating rtl for each function. */ - -void -init_emit () -{ - int i; - - first_insn = NULL; - last_insn = NULL; - cur_insn_uid = 1; - reg_rtx_no = LAST_VIRTUAL_REGISTER + 1; - last_linenum = 0; - last_filename = 0; - first_label_num = label_num; - last_label_num = 0; - sequence_stack = NULL; - - /* Clear the start_sequence/gen_sequence cache. */ - sequence_element_free_list = 0; - for (i = 0; i < SEQUENCE_RESULT_SIZE; i++) - sequence_result[i] = 0; - - /* Init the tables that describe all the pseudo regs. */ - - regno_pointer_flag_length = LAST_VIRTUAL_REGISTER + 101; - - regno_pointer_flag - = (char *) oballoc (regno_pointer_flag_length); - bzero (regno_pointer_flag, regno_pointer_flag_length); - - regno_reg_rtx - = (rtx *) oballoc (regno_pointer_flag_length * sizeof (rtx)); - bzero (regno_reg_rtx, regno_pointer_flag_length * sizeof (rtx)); - - /* Put copies of all the virtual register rtx into regno_reg_rtx. */ - regno_reg_rtx[VIRTUAL_INCOMING_ARGS_REGNUM] = virtual_incoming_args_rtx; - regno_reg_rtx[VIRTUAL_STACK_VARS_REGNUM] = virtual_stack_vars_rtx; - regno_reg_rtx[VIRTUAL_STACK_DYNAMIC_REGNUM] = virtual_stack_dynamic_rtx; - regno_reg_rtx[VIRTUAL_OUTGOING_ARGS_REGNUM] = virtual_outgoing_args_rtx; - - /* Indicate that the virtual registers and stack locations are - all pointers. */ - REGNO_POINTER_FLAG (STACK_POINTER_REGNUM) = 1; - REGNO_POINTER_FLAG (FRAME_POINTER_REGNUM) = 1; - REGNO_POINTER_FLAG (ARG_POINTER_REGNUM) = 1; - - REGNO_POINTER_FLAG (VIRTUAL_INCOMING_ARGS_REGNUM) = 1; - REGNO_POINTER_FLAG (VIRTUAL_STACK_VARS_REGNUM) = 1; - REGNO_POINTER_FLAG (VIRTUAL_STACK_DYNAMIC_REGNUM) = 1; - REGNO_POINTER_FLAG (VIRTUAL_OUTGOING_ARGS_REGNUM) = 1; - -#ifdef INIT_EXPANDERS - INIT_EXPANDERS; -#endif -} - -/* Create some permanent unique rtl objects shared between all functions. - LINE_NUMBERS is nonzero if line numbers are to be generated. */ - -void -init_emit_once (line_numbers) - int line_numbers; -{ - int i; - enum machine_mode mode; - - no_line_numbers = ! line_numbers; - - sequence_stack = NULL; - - /* Create the unique rtx's for certain rtx codes and operand values. */ - - pc_rtx = gen_rtx (PC, VOIDmode); - cc0_rtx = gen_rtx (CC0, VOIDmode); - - /* Don't use gen_rtx here since gen_rtx in this case - tries to use these variables. */ - for (i = - MAX_SAVED_CONST_INT; i <= MAX_SAVED_CONST_INT; i++) - { - const_int_rtx[i + MAX_SAVED_CONST_INT] = rtx_alloc (CONST_INT); - PUT_MODE (const_int_rtx[i + MAX_SAVED_CONST_INT], VOIDmode); - INTVAL (const_int_rtx[i + MAX_SAVED_CONST_INT]) = i; - } - - /* These four calls obtain some of the rtx expressions made above. */ - const0_rtx = GEN_INT (0); - const1_rtx = GEN_INT (1); - const2_rtx = GEN_INT (2); - constm1_rtx = GEN_INT (-1); - - /* This will usually be one of the above constants, but may be a new rtx. */ - const_true_rtx = GEN_INT (STORE_FLAG_VALUE); - - dconst0 = REAL_VALUE_ATOF ("0", DFmode); - dconst1 = REAL_VALUE_ATOF ("1", DFmode); - dconst2 = REAL_VALUE_ATOF ("2", DFmode); - dconstm1 = REAL_VALUE_ATOF ("-1", DFmode); - - for (i = 0; i <= 2; i++) - { - for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); mode != VOIDmode; - mode = GET_MODE_WIDER_MODE (mode)) - { - rtx tem = rtx_alloc (CONST_DOUBLE); - union real_extract u; - - bzero (&u, sizeof u); /* Zero any holes in a structure. */ - u.d = i == 0 ? dconst0 : i == 1 ? dconst1 : dconst2; - - bcopy (&u, &CONST_DOUBLE_LOW (tem), sizeof u); - CONST_DOUBLE_MEM (tem) = cc0_rtx; - PUT_MODE (tem, mode); - - const_tiny_rtx[i][(int) mode] = tem; - } - - const_tiny_rtx[i][(int) VOIDmode] = GEN_INT (i); - - for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode; - mode = GET_MODE_WIDER_MODE (mode)) - const_tiny_rtx[i][(int) mode] = GEN_INT (i); - - for (mode = GET_CLASS_NARROWEST_MODE (MODE_PARTIAL_INT); - mode != VOIDmode; - mode = GET_MODE_WIDER_MODE (mode)) - const_tiny_rtx[i][(int) mode] = GEN_INT (i); - } - - for (mode = GET_CLASS_NARROWEST_MODE (MODE_CC); mode != VOIDmode; - mode = GET_MODE_WIDER_MODE (mode)) - const_tiny_rtx[0][(int) mode] = const0_rtx; - - stack_pointer_rtx = gen_rtx (REG, Pmode, STACK_POINTER_REGNUM); - frame_pointer_rtx = gen_rtx (REG, Pmode, FRAME_POINTER_REGNUM); - - if (FRAME_POINTER_REGNUM == ARG_POINTER_REGNUM) - arg_pointer_rtx = frame_pointer_rtx; - else if (STACK_POINTER_REGNUM == ARG_POINTER_REGNUM) - arg_pointer_rtx = stack_pointer_rtx; - else - arg_pointer_rtx = gen_rtx (REG, Pmode, ARG_POINTER_REGNUM); - - /* Create the virtual registers. Do so here since the following objects - might reference them. */ - - virtual_incoming_args_rtx = gen_rtx (REG, Pmode, - VIRTUAL_INCOMING_ARGS_REGNUM); - virtual_stack_vars_rtx = gen_rtx (REG, Pmode, - VIRTUAL_STACK_VARS_REGNUM); - virtual_stack_dynamic_rtx = gen_rtx (REG, Pmode, - VIRTUAL_STACK_DYNAMIC_REGNUM); - virtual_outgoing_args_rtx = gen_rtx (REG, Pmode, - VIRTUAL_OUTGOING_ARGS_REGNUM); - -#ifdef STRUCT_VALUE - struct_value_rtx = STRUCT_VALUE; -#else - struct_value_rtx = gen_rtx (REG, Pmode, STRUCT_VALUE_REGNUM); -#endif - -#ifdef STRUCT_VALUE_INCOMING - struct_value_incoming_rtx = STRUCT_VALUE_INCOMING; -#else -#ifdef STRUCT_VALUE_INCOMING_REGNUM - struct_value_incoming_rtx - = gen_rtx (REG, Pmode, STRUCT_VALUE_INCOMING_REGNUM); -#else - struct_value_incoming_rtx = struct_value_rtx; -#endif -#endif - -#ifdef STATIC_CHAIN_REGNUM - static_chain_rtx = gen_rtx (REG, Pmode, STATIC_CHAIN_REGNUM); - -#ifdef STATIC_CHAIN_INCOMING_REGNUM - if (STATIC_CHAIN_INCOMING_REGNUM != STATIC_CHAIN_REGNUM) - static_chain_incoming_rtx = gen_rtx (REG, Pmode, STATIC_CHAIN_INCOMING_REGNUM); - else -#endif - static_chain_incoming_rtx = static_chain_rtx; -#endif - -#ifdef STATIC_CHAIN - static_chain_rtx = STATIC_CHAIN; - -#ifdef STATIC_CHAIN_INCOMING - static_chain_incoming_rtx = STATIC_CHAIN_INCOMING; -#else - static_chain_incoming_rtx = static_chain_rtx; -#endif -#endif - -#ifdef PIC_OFFSET_TABLE_REGNUM - pic_offset_table_rtx = gen_rtx (REG, Pmode, PIC_OFFSET_TABLE_REGNUM); -#endif -} diff --git a/gnu/usr.bin/cc/common/expmed.c b/gnu/usr.bin/cc/common/expmed.c deleted file mode 100644 index 802ba45c87..0000000000 --- a/gnu/usr.bin/cc/common/expmed.c +++ /dev/null @@ -1,3159 +0,0 @@ -/* Medium-level subroutines: convert bit-field store and extract - and shifts, multiplies and divides to rtl instructions. - Copyright (C) 1987, 1988, 1989, 1992, 1993 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "config.h" -#include "rtl.h" -#include "tree.h" -#include "flags.h" -#include "insn-flags.h" -#include "insn-codes.h" -#include "insn-config.h" -#include "expr.h" -#include "real.h" -#include "recog.h" - -static rtx extract_split_bit_field (); -static rtx extract_fixed_bit_field (); -static void store_split_bit_field (); -static void store_fixed_bit_field (); -static rtx mask_rtx (); -static rtx lshift_value (); - -#define CEIL(x,y) (((x) + (y) - 1) / (y)) - -/* Non-zero means multiply instructions are cheaper than shifts. */ -int mult_is_very_cheap; - -/* Non-zero means divides or modulus operations are relatively cheap for - powers of two, so don't use branches; emit the operation instead. - Usually, this will mean that the MD file will emit non-branch - sequences. */ - -static int sdiv_pow2_cheap, smod_pow2_cheap; - -/* Cost of various pieces of RTL. */ -static int add_cost, mult_cost, negate_cost, zero_cost; -static int shift_cost[BITS_PER_WORD]; -static int shiftadd_cost[BITS_PER_WORD]; -static int shiftsub_cost[BITS_PER_WORD]; - -void -init_expmed () -{ - char *free_point; - /* This is "some random pseudo register" for purposes of calling recog - to see what insns exist. */ - rtx reg = gen_rtx (REG, word_mode, FIRST_PSEUDO_REGISTER); - rtx shift_insn, shiftadd_insn, shiftsub_insn; - int dummy; - int m; - - start_sequence (); - - /* Since we are on the permanent obstack, we must be sure we save this - spot AFTER we call start_sequence, since it will reuse the rtl it - makes. */ - - free_point = (char *) oballoc (0); - - zero_cost = rtx_cost (const0_rtx, 0); - add_cost = rtx_cost (gen_rtx (PLUS, word_mode, reg, reg), SET); - - shift_insn = emit_insn (gen_rtx (SET, VOIDmode, reg, - gen_rtx (ASHIFT, word_mode, reg, - const0_rtx))); - - shiftadd_insn = emit_insn (gen_rtx (SET, VOIDmode, reg, - gen_rtx (PLUS, word_mode, - gen_rtx (MULT, word_mode, - reg, const0_rtx), - reg))); - - shiftsub_insn = emit_insn (gen_rtx (SET, VOIDmode, reg, - gen_rtx (MINUS, word_mode, - gen_rtx (MULT, word_mode, - reg, const0_rtx), - reg))); - - init_recog (); - - shift_cost[0] = 0; - shiftadd_cost[0] = shiftsub_cost[0] = add_cost; - - for (m = 1; m < BITS_PER_WORD; m++) - { - shift_cost[m] = shiftadd_cost[m] = shiftsub_cost[m] = 32000; - - XEXP (SET_SRC (PATTERN (shift_insn)), 1) = GEN_INT (m); - if (recog (PATTERN (shift_insn), shift_insn, &dummy) >= 0) - shift_cost[m] = rtx_cost (SET_SRC (PATTERN (shift_insn)), SET); - - XEXP (XEXP (SET_SRC (PATTERN (shiftadd_insn)), 0), 1) - = GEN_INT ((HOST_WIDE_INT) 1 << m); - if (recog (PATTERN (shiftadd_insn), shiftadd_insn, &dummy) >= 0) - shiftadd_cost[m] = rtx_cost (SET_SRC (PATTERN (shiftadd_insn)), SET); - - XEXP (XEXP (SET_SRC (PATTERN (shiftsub_insn)), 0), 1) - = GEN_INT ((HOST_WIDE_INT) 1 << m); - if (recog (PATTERN (shiftsub_insn), shiftsub_insn, &dummy) >= 0) - shiftsub_cost[m] = rtx_cost (SET_SRC (PATTERN (shiftsub_insn)), SET); - } - - mult_cost = rtx_cost (gen_rtx (MULT, word_mode, reg, reg), SET); - negate_cost = rtx_cost (gen_rtx (NEG, word_mode, reg), SET); - - /* 999999 is chosen to avoid any plausible faster special case. */ - mult_is_very_cheap - = (rtx_cost (gen_rtx (MULT, word_mode, reg, GEN_INT (999999)), SET) - < rtx_cost (gen_rtx (ASHIFT, word_mode, reg, GEN_INT (7)), SET)); - - sdiv_pow2_cheap - = (rtx_cost (gen_rtx (DIV, word_mode, reg, GEN_INT (32)), SET) - <= 2 * add_cost); - smod_pow2_cheap - = (rtx_cost (gen_rtx (MOD, word_mode, reg, GEN_INT (32)), SET) - <= 2 * add_cost); - - /* Free the objects we just allocated. */ - end_sequence (); - obfree (free_point); -} - -/* Return an rtx representing minus the value of X. - MODE is the intended mode of the result, - useful if X is a CONST_INT. */ - -rtx -negate_rtx (mode, x) - enum machine_mode mode; - rtx x; -{ - if (GET_CODE (x) == CONST_INT) - { - HOST_WIDE_INT val = - INTVAL (x); - if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT) - { - /* Sign extend the value from the bits that are significant. */ - if (val & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))) - val |= (HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (mode); - else - val &= ((HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (mode)) - 1; - } - return GEN_INT (val); - } - else - return expand_unop (GET_MODE (x), neg_optab, x, NULL_RTX, 0); -} - -/* Generate code to store value from rtx VALUE - into a bit-field within structure STR_RTX - containing BITSIZE bits starting at bit BITNUM. - FIELDMODE is the machine-mode of the FIELD_DECL node for this field. - ALIGN is the alignment that STR_RTX is known to have, measured in bytes. - TOTAL_SIZE is the size of the structure in bytes, or -1 if varying. */ - -/* ??? Note that there are two different ideas here for how - to determine the size to count bits within, for a register. - One is BITS_PER_WORD, and the other is the size of operand 3 - of the insv pattern. (The latter assumes that an n-bit machine - will be able to insert bit fields up to n bits wide.) - It isn't certain that either of these is right. - extract_bit_field has the same quandary. */ - -rtx -store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size) - rtx str_rtx; - register int bitsize; - int bitnum; - enum machine_mode fieldmode; - rtx value; - int align; - int total_size; -{ - int unit = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD; - register int offset = bitnum / unit; - register int bitpos = bitnum % unit; - register rtx op0 = str_rtx; - - if (GET_CODE (str_rtx) == MEM && ! MEM_IN_STRUCT_P (str_rtx)) - abort (); - - /* Discount the part of the structure before the desired byte. - We need to know how many bytes are safe to reference after it. */ - if (total_size >= 0) - total_size -= (bitpos / BIGGEST_ALIGNMENT - * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); - - while (GET_CODE (op0) == SUBREG) - { - /* The following line once was done only if WORDS_BIG_ENDIAN, - but I think that is a mistake. WORDS_BIG_ENDIAN is - meaningful at a much higher level; when structures are copied - between memory and regs, the higher-numbered regs - always get higher addresses. */ - offset += SUBREG_WORD (op0); - /* We used to adjust BITPOS here, but now we do the whole adjustment - right after the loop. */ - op0 = SUBREG_REG (op0); - } - -#if BYTES_BIG_ENDIAN - /* If OP0 is a register, BITPOS must count within a word. - But as we have it, it counts within whatever size OP0 now has. - On a bigendian machine, these are not the same, so convert. */ - if (GET_CODE (op0) != MEM && unit > GET_MODE_BITSIZE (GET_MODE (op0))) - bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0)); -#endif - - value = protect_from_queue (value, 0); - - if (flag_force_mem) - value = force_not_mem (value); - - /* Note that the adjustment of BITPOS above has no effect on whether - BITPOS is 0 in a REG bigger than a word. */ - if (GET_MODE_SIZE (fieldmode) >= UNITS_PER_WORD - && (! STRICT_ALIGNMENT || GET_CODE (op0) != MEM) - && bitpos == 0 && bitsize == GET_MODE_BITSIZE (fieldmode)) - { - /* Storing in a full-word or multi-word field in a register - can be done with just SUBREG. */ - if (GET_MODE (op0) != fieldmode) - if (GET_CODE (op0) == REG) - op0 = gen_rtx (SUBREG, fieldmode, op0, offset); - else - op0 = change_address (op0, fieldmode, - plus_constant (XEXP (op0, 0), offset)); - emit_move_insn (op0, value); - return value; - } - - /* Storing an lsb-aligned field in a register - can be done with a movestrict instruction. */ - - if (GET_CODE (op0) != MEM -#if BYTES_BIG_ENDIAN - && bitpos + bitsize == unit -#else - && bitpos == 0 -#endif - && bitsize == GET_MODE_BITSIZE (fieldmode) - && (GET_MODE (op0) == fieldmode - || (movstrict_optab->handlers[(int) fieldmode].insn_code - != CODE_FOR_nothing))) - { - /* Get appropriate low part of the value being stored. */ - if (GET_CODE (value) == CONST_INT || GET_CODE (value) == REG) - value = gen_lowpart (fieldmode, value); - else if (!(GET_CODE (value) == SYMBOL_REF - || GET_CODE (value) == LABEL_REF - || GET_CODE (value) == CONST)) - value = convert_to_mode (fieldmode, value, 0); - - if (GET_MODE (op0) == fieldmode) - emit_move_insn (op0, value); - else - { - int icode = movstrict_optab->handlers[(int) fieldmode].insn_code; - if(! (*insn_operand_predicate[icode][1]) (value, fieldmode)) - value = copy_to_mode_reg (fieldmode, value); - emit_insn (GEN_FCN (icode) - (gen_rtx (SUBREG, fieldmode, op0, offset), value)); - } - return value; - } - - /* Handle fields bigger than a word. */ - - if (bitsize > BITS_PER_WORD) - { - /* Here we transfer the words of the field - in the order least significant first. - This is because the most significant word is the one which may - be less than full. */ - - int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD; - int i; - - /* This is the mode we must force value to, so that there will be enough - subwords to extract. Note that fieldmode will often (always?) be - VOIDmode, because that is what store_field uses to indicate that this - is a bit field, but passing VOIDmode to operand_subword_force will - result in an abort. */ - fieldmode = mode_for_size (nwords * BITS_PER_WORD, MODE_INT, 0); - - for (i = 0; i < nwords; i++) - { - /* If I is 0, use the low-order word in both field and target; - if I is 1, use the next to lowest word; and so on. */ - int wordnum = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i); - int bit_offset = (WORDS_BIG_ENDIAN - ? MAX (bitsize - (i + 1) * BITS_PER_WORD, 0) - : i * BITS_PER_WORD); - store_bit_field (op0, MIN (BITS_PER_WORD, - bitsize - i * BITS_PER_WORD), - bitnum + bit_offset, word_mode, - operand_subword_force (value, wordnum, fieldmode), - align, total_size); - } - return value; - } - - /* From here on we can assume that the field to be stored in is - a full-word (whatever type that is), since it is shorter than a word. */ - - /* OFFSET is the number of words or bytes (UNIT says which) - from STR_RTX to the first word or byte containing part of the field. */ - - if (GET_CODE (op0) == REG) - { - if (offset != 0 - || GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD) - op0 = gen_rtx (SUBREG, TYPE_MODE (type_for_size (BITS_PER_WORD, 0)), - op0, offset); - offset = 0; - } - else - { - op0 = protect_from_queue (op0, 1); - } - - /* Now OFFSET is nonzero only if OP0 is memory - and is therefore always measured in bytes. */ - -#ifdef HAVE_insv - if (HAVE_insv - && !(bitsize == 1 && GET_CODE (value) == CONST_INT) - /* Ensure insv's size is wide enough for this field. */ - && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_insv][3]) - >= bitsize)) - { - int xbitpos = bitpos; - rtx value1; - rtx xop0 = op0; - rtx last = get_last_insn (); - rtx pat; - enum machine_mode maxmode - = insn_operand_mode[(int) CODE_FOR_insv][3]; - - int save_volatile_ok = volatile_ok; - volatile_ok = 1; - - /* If this machine's insv can only insert into a register, or if we - are to force MEMs into a register, copy OP0 into a register and - save it back later. */ - if (GET_CODE (op0) == MEM - && (flag_force_mem - || ! ((*insn_operand_predicate[(int) CODE_FOR_insv][0]) - (op0, VOIDmode)))) - { - rtx tempreg; - enum machine_mode bestmode; - - /* Get the mode to use for inserting into this field. If OP0 is - BLKmode, get the smallest mode consistent with the alignment. If - OP0 is a non-BLKmode object that is no wider than MAXMODE, use its - mode. Otherwise, use the smallest mode containing the field. */ - - if (GET_MODE (op0) == BLKmode - || GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (maxmode)) - bestmode - = get_best_mode (bitsize, bitnum, align * BITS_PER_UNIT, maxmode, - MEM_VOLATILE_P (op0)); - else - bestmode = GET_MODE (op0); - - if (bestmode == VOIDmode) - goto insv_loses; - - /* Adjust address to point to the containing unit of that mode. */ - unit = GET_MODE_BITSIZE (bestmode); - /* Compute offset as multiple of this unit, counting in bytes. */ - offset = (bitnum / unit) * GET_MODE_SIZE (bestmode); - bitpos = bitnum % unit; - op0 = change_address (op0, bestmode, - plus_constant (XEXP (op0, 0), offset)); - - /* Fetch that unit, store the bitfield in it, then store the unit. */ - tempreg = copy_to_reg (op0); - store_bit_field (tempreg, bitsize, bitpos, fieldmode, value, - align, total_size); - emit_move_insn (op0, tempreg); - return value; - } - volatile_ok = save_volatile_ok; - - /* Add OFFSET into OP0's address. */ - if (GET_CODE (xop0) == MEM) - xop0 = change_address (xop0, byte_mode, - plus_constant (XEXP (xop0, 0), offset)); - - /* If xop0 is a register, we need it in MAXMODE - to make it acceptable to the format of insv. */ - if (GET_CODE (xop0) == SUBREG) - PUT_MODE (xop0, maxmode); - if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode) - xop0 = gen_rtx (SUBREG, maxmode, xop0, 0); - - /* On big-endian machines, we count bits from the most significant. - If the bit field insn does not, we must invert. */ - -#if BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN - xbitpos = unit - bitsize - xbitpos; -#endif - /* We have been counting XBITPOS within UNIT. - Count instead within the size of the register. */ -#if BITS_BIG_ENDIAN - if (GET_CODE (xop0) != MEM) - xbitpos += GET_MODE_BITSIZE (maxmode) - unit; -#endif - unit = GET_MODE_BITSIZE (maxmode); - - /* Convert VALUE to maxmode (which insv insn wants) in VALUE1. */ - value1 = value; - if (GET_MODE (value) != maxmode) - { - if (GET_MODE_BITSIZE (GET_MODE (value)) >= bitsize) - { - /* Optimization: Don't bother really extending VALUE - if it has all the bits we will actually use. However, - if we must narrow it, be sure we do it correctly. */ - - if (GET_MODE_SIZE (GET_MODE (value)) < GET_MODE_SIZE (maxmode)) - { - /* Avoid making subreg of a subreg, or of a mem. */ - if (GET_CODE (value1) != REG) - value1 = copy_to_reg (value1); - value1 = gen_rtx (SUBREG, maxmode, value1, 0); - } - else - value1 = gen_lowpart (maxmode, value1); - } - else if (!CONSTANT_P (value)) - /* Parse phase is supposed to make VALUE's data type - match that of the component reference, which is a type - at least as wide as the field; so VALUE should have - a mode that corresponds to that type. */ - abort (); - } - - /* If this machine's insv insists on a register, - get VALUE1 into a register. */ - if (! ((*insn_operand_predicate[(int) CODE_FOR_insv][3]) - (value1, maxmode))) - value1 = force_reg (maxmode, value1); - - pat = gen_insv (xop0, GEN_INT (bitsize), GEN_INT (xbitpos), value1); - if (pat) - emit_insn (pat); - else - { - delete_insns_since (last); - store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align); - } - } - else - insv_loses: -#endif - /* Insv is not available; store using shifts and boolean ops. */ - store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align); - return value; -} - -/* Use shifts and boolean operations to store VALUE - into a bit field of width BITSIZE - in a memory location specified by OP0 except offset by OFFSET bytes. - (OFFSET must be 0 if OP0 is a register.) - The field starts at position BITPOS within the byte. - (If OP0 is a register, it may be a full word or a narrower mode, - but BITPOS still counts within a full word, - which is significant on bigendian machines.) - STRUCT_ALIGN is the alignment the structure is known to have (in bytes). - - Note that protect_from_queue has already been done on OP0 and VALUE. */ - -static void -store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align) - register rtx op0; - register int offset, bitsize, bitpos; - register rtx value; - int struct_align; -{ - register enum machine_mode mode; - int total_bits = BITS_PER_WORD; - rtx subtarget, temp; - int all_zero = 0; - int all_one = 0; - - /* Add OFFSET to OP0's address (if it is in memory) - and if a single byte contains the whole bit field - change OP0 to a byte. */ - - /* There is a case not handled here: - a structure with a known alignment of just a halfword - and a field split across two aligned halfwords within the structure. - Or likewise a structure with a known alignment of just a byte - and a field split across two bytes. - Such cases are not supposed to be able to occur. */ - - if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) - { - if (offset != 0) - abort (); - /* Special treatment for a bit field split across two registers. */ - if (bitsize + bitpos > BITS_PER_WORD) - { - store_split_bit_field (op0, bitsize, bitpos, value, BITS_PER_WORD); - return; - } - } - else - { - /* Get the proper mode to use for this field. We want a mode that - includes the entire field. If such a mode would be larger than - a word, we won't be doing the extraction the normal way. */ - - mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT, - struct_align * BITS_PER_UNIT, word_mode, - GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0)); - - if (mode == VOIDmode) - { - /* The only way this should occur is if the field spans word - boundaries. */ - store_split_bit_field (op0, bitsize, bitpos + offset * BITS_PER_UNIT, - value, struct_align); - return; - } - - total_bits = GET_MODE_BITSIZE (mode); - - /* Get ref to an aligned byte, halfword, or word containing the field. - Adjust BITPOS to be position within a word, - and OFFSET to be the offset of that word. - Then alter OP0 to refer to that word. */ - bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT; - offset -= (offset % (total_bits / BITS_PER_UNIT)); - op0 = change_address (op0, mode, - plus_constant (XEXP (op0, 0), offset)); - } - - mode = GET_MODE (op0); - - /* Now MODE is either some integral mode for a MEM as OP0, - or is a full-word for a REG as OP0. TOTAL_BITS corresponds. - The bit field is contained entirely within OP0. - BITPOS is the starting bit number within OP0. - (OP0's mode may actually be narrower than MODE.) */ - -#if BYTES_BIG_ENDIAN - /* BITPOS is the distance between our msb - and that of the containing datum. - Convert it to the distance from the lsb. */ - - bitpos = total_bits - bitsize - bitpos; -#endif - /* Now BITPOS is always the distance between our lsb - and that of OP0. */ - - /* Shift VALUE left by BITPOS bits. If VALUE is not constant, - we must first convert its mode to MODE. */ - - if (GET_CODE (value) == CONST_INT) - { - register HOST_WIDE_INT v = INTVAL (value); - - if (bitsize < HOST_BITS_PER_WIDE_INT) - v &= ((HOST_WIDE_INT) 1 << bitsize) - 1; - - if (v == 0) - all_zero = 1; - else if ((bitsize < HOST_BITS_PER_WIDE_INT - && v == ((HOST_WIDE_INT) 1 << bitsize) - 1) - || (bitsize == HOST_BITS_PER_WIDE_INT && v == -1)) - all_one = 1; - - value = lshift_value (mode, value, bitpos, bitsize); - } - else - { - int must_and = (GET_MODE_BITSIZE (GET_MODE (value)) != bitsize - && bitpos + bitsize != GET_MODE_BITSIZE (mode)); - - if (GET_MODE (value) != mode) - { - /* If VALUE is a floating-point mode, access it as an integer - of the corresponding size, then convert it. This can occur on - a machine with 64 bit registers that uses SFmode for float. */ - if (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT) - { - if (GET_CODE (value) != REG) - value = copy_to_reg (value); - value - = gen_rtx (SUBREG, word_mode, value, 0); - } - - if ((GET_CODE (value) == REG || GET_CODE (value) == SUBREG) - && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (value))) - value = gen_lowpart (mode, value); - else - value = convert_to_mode (mode, value, 1); - } - - if (must_and) - value = expand_binop (mode, and_optab, value, - mask_rtx (mode, 0, bitsize, 0), - NULL_RTX, 1, OPTAB_LIB_WIDEN); - if (bitpos > 0) - value = expand_shift (LSHIFT_EXPR, mode, value, - build_int_2 (bitpos, 0), NULL_RTX, 1); - } - - /* Now clear the chosen bits in OP0, - except that if VALUE is -1 we need not bother. */ - - subtarget = (GET_CODE (op0) == REG || ! flag_force_mem) ? op0 : 0; - - if (! all_one) - { - temp = expand_binop (mode, and_optab, op0, - mask_rtx (mode, bitpos, bitsize, 1), - subtarget, 1, OPTAB_LIB_WIDEN); - subtarget = temp; - } - else - temp = op0; - - /* Now logical-or VALUE into OP0, unless it is zero. */ - - if (! all_zero) - temp = expand_binop (mode, ior_optab, temp, value, - subtarget, 1, OPTAB_LIB_WIDEN); - if (op0 != temp) - emit_move_insn (op0, temp); -} - -/* Store a bit field that is split across two words. - - OP0 is the REG, SUBREG or MEM rtx for the first of the two words. - BITSIZE is the field width; BITPOS the position of its first bit - (within the word). - VALUE is the value to store. */ - -static void -store_split_bit_field (op0, bitsize, bitpos, value, align) - rtx op0; - int bitsize, bitpos; - rtx value; - int align; -{ - /* BITSIZE_1 is size of the part in the first word. */ - int bitsize_1 = BITS_PER_WORD - bitpos % BITS_PER_WORD; - /* BITSIZE_2 is size of the rest (in the following word). */ - int bitsize_2 = bitsize - bitsize_1; - rtx part1, part2; - int unit = GET_CODE (op0) == MEM ? BITS_PER_UNIT : BITS_PER_WORD; - int offset = bitpos / unit; - rtx word; - - /* The field must span exactly one word boundary. */ - if (bitpos / BITS_PER_WORD != (bitpos + bitsize - 1) / BITS_PER_WORD - 1) - abort (); - - if (GET_MODE (value) != VOIDmode) - value = convert_to_mode (word_mode, value, 1); - - if (GET_CODE (value) == CONST_DOUBLE - && (part1 = gen_lowpart_common (word_mode, value)) != 0) - value = part1; - - if (CONSTANT_P (value) && GET_CODE (value) != CONST_INT) - value = copy_to_mode_reg (word_mode, value); - - /* Split the value into two parts: - PART1 gets that which goes in the first word; PART2 the other. */ -#if BYTES_BIG_ENDIAN - /* PART1 gets the more significant part. */ - if (GET_CODE (value) == CONST_INT) - { - part1 = GEN_INT ((unsigned HOST_WIDE_INT) (INTVAL (value)) >> bitsize_2); - part2 = GEN_INT ((unsigned HOST_WIDE_INT) (INTVAL (value)) - & (((HOST_WIDE_INT) 1 << bitsize_2) - 1)); - } - else - { - part1 = extract_fixed_bit_field (word_mode, value, 0, bitsize_1, - BITS_PER_WORD - bitsize, NULL_RTX, 1, - BITS_PER_WORD); - part2 = extract_fixed_bit_field (word_mode, value, 0, bitsize_2, - BITS_PER_WORD - bitsize_2, NULL_RTX, 1, - BITS_PER_WORD); - } -#else - /* PART1 gets the less significant part. */ - if (GET_CODE (value) == CONST_INT) - { - part1 = GEN_INT ((unsigned HOST_WIDE_INT) (INTVAL (value)) - & (((HOST_WIDE_INT) 1 << bitsize_1) - 1)); - part2 = GEN_INT ((unsigned HOST_WIDE_INT) (INTVAL (value)) >> bitsize_1); - } - else - { - part1 = extract_fixed_bit_field (word_mode, value, 0, bitsize_1, 0, - NULL_RTX, 1, BITS_PER_WORD); - part2 = extract_fixed_bit_field (word_mode, value, 0, bitsize_2, - bitsize_1, NULL_RTX, 1, BITS_PER_WORD); - } -#endif - - /* Store PART1 into the first word. If OP0 is a MEM, pass OP0 and the - offset computed above. Otherwise, get the proper word and pass an - offset of zero. */ - word = (GET_CODE (op0) == MEM ? op0 - : operand_subword (op0, offset, 1, GET_MODE (op0))); - if (word == 0) - abort (); - - store_fixed_bit_field (word, GET_CODE (op0) == MEM ? offset : 0, - bitsize_1, bitpos % unit, part1, align); - - /* Offset op0 by 1 word to get to the following one. */ - if (GET_CODE (op0) == SUBREG) - word = operand_subword (SUBREG_REG (op0), SUBREG_WORD (op0) + offset + 1, - 1, VOIDmode); - else if (GET_CODE (op0) == MEM) - word = op0; - else - word = operand_subword (op0, offset + 1, 1, GET_MODE (op0)); - - if (word == 0) - abort (); - - /* Store PART2 into the second word. */ - store_fixed_bit_field (word, - (GET_CODE (op0) == MEM - ? CEIL (offset + 1, UNITS_PER_WORD) * UNITS_PER_WORD - : 0), - bitsize_2, 0, part2, align); -} - -/* Generate code to extract a byte-field from STR_RTX - containing BITSIZE bits, starting at BITNUM, - and put it in TARGET if possible (if TARGET is nonzero). - Regardless of TARGET, we return the rtx for where the value is placed. - It may be a QUEUED. - - STR_RTX is the structure containing the byte (a REG or MEM). - UNSIGNEDP is nonzero if this is an unsigned bit field. - MODE is the natural mode of the field value once extracted. - TMODE is the mode the caller would like the value to have; - but the value may be returned with type MODE instead. - - ALIGN is the alignment that STR_RTX is known to have, measured in bytes. - TOTAL_SIZE is the size in bytes of the containing structure, - or -1 if varying. - - If a TARGET is specified and we can store in it at no extra cost, - we do so, and return TARGET. - Otherwise, we return a REG of mode TMODE or MODE, with TMODE preferred - if they are equally easy. */ - -rtx -extract_bit_field (str_rtx, bitsize, bitnum, unsignedp, - target, mode, tmode, align, total_size) - rtx str_rtx; - register int bitsize; - int bitnum; - int unsignedp; - rtx target; - enum machine_mode mode, tmode; - int align; - int total_size; -{ - int unit = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD; - register int offset = bitnum / unit; - register int bitpos = bitnum % unit; - register rtx op0 = str_rtx; - rtx spec_target = target; - rtx spec_target_subreg = 0; - - if (GET_CODE (str_rtx) == MEM && ! MEM_IN_STRUCT_P (str_rtx)) - abort (); - - /* Discount the part of the structure before the desired byte. - We need to know how many bytes are safe to reference after it. */ - if (total_size >= 0) - total_size -= (bitpos / BIGGEST_ALIGNMENT - * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); - - if (tmode == VOIDmode) - tmode = mode; - while (GET_CODE (op0) == SUBREG) - { - offset += SUBREG_WORD (op0); - op0 = SUBREG_REG (op0); - } - -#if BYTES_BIG_ENDIAN - /* If OP0 is a register, BITPOS must count within a word. - But as we have it, it counts within whatever size OP0 now has. - On a bigendian machine, these are not the same, so convert. */ - if (GET_CODE (op0) != MEM && unit > GET_MODE_BITSIZE (GET_MODE (op0))) - bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0)); -#endif - - /* Extracting a full-word or multi-word value - from a structure in a register. - This can be done with just SUBREG. - So too extracting a subword value in - the least significant part of the register. */ - - if (GET_CODE (op0) == REG - && ((bitsize >= BITS_PER_WORD && bitsize == GET_MODE_BITSIZE (mode) - && bitpos % BITS_PER_WORD == 0) - || (mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0) != BLKmode -#if BYTES_BIG_ENDIAN - && bitpos + bitsize == BITS_PER_WORD -#else - && bitpos == 0 -#endif - ))) - { - enum machine_mode mode1 - = mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0); - - if (mode1 != GET_MODE (op0)) - op0 = gen_rtx (SUBREG, mode1, op0, offset); - - if (mode1 != mode) - return convert_to_mode (tmode, op0, unsignedp); - return op0; - } - - /* Handle fields bigger than a word. */ - - if (bitsize > BITS_PER_WORD) - { - /* Here we transfer the words of the field - in the order least significant first. - This is because the most significant word is the one which may - be less than full. */ - - int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD; - int i; - - if (target == 0 || GET_CODE (target) != REG) - target = gen_reg_rtx (mode); - - for (i = 0; i < nwords; i++) - { - /* If I is 0, use the low-order word in both field and target; - if I is 1, use the next to lowest word; and so on. */ - int wordnum = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i); - int bit_offset = (WORDS_BIG_ENDIAN - ? MAX (0, bitsize - (i + 1) * BITS_PER_WORD) - : i * BITS_PER_WORD); - rtx target_part = operand_subword (target, wordnum, 1, VOIDmode); - rtx result_part - = extract_bit_field (op0, MIN (BITS_PER_WORD, - bitsize - i * BITS_PER_WORD), - bitnum + bit_offset, - 1, target_part, mode, word_mode, - align, total_size); - - if (target_part == 0) - abort (); - - if (result_part != target_part) - emit_move_insn (target_part, result_part); - } - - return target; - } - - /* From here on we know the desired field is smaller than a word - so we can assume it is an integer. So we can safely extract it as one - size of integer, if necessary, and then truncate or extend - to the size that is wanted. */ - - /* OFFSET is the number of words or bytes (UNIT says which) - from STR_RTX to the first word or byte containing part of the field. */ - - if (GET_CODE (op0) == REG) - { - if (offset != 0 - || GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD) - op0 = gen_rtx (SUBREG, TYPE_MODE (type_for_size (BITS_PER_WORD, 0)), - op0, offset); - offset = 0; - } - else - { - op0 = protect_from_queue (str_rtx, 1); - } - - /* Now OFFSET is nonzero only for memory operands. */ - - if (unsignedp) - { -#ifdef HAVE_extzv - if (HAVE_extzv - && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extzv][0]) - >= bitsize)) - { - int xbitpos = bitpos, xoffset = offset; - rtx bitsize_rtx, bitpos_rtx; - rtx last = get_last_insn(); - rtx xop0 = op0; - rtx xtarget = target; - rtx xspec_target = spec_target; - rtx xspec_target_subreg = spec_target_subreg; - rtx pat; - enum machine_mode maxmode - = insn_operand_mode[(int) CODE_FOR_extzv][0]; - - if (GET_CODE (xop0) == MEM) - { - int save_volatile_ok = volatile_ok; - volatile_ok = 1; - - /* Is the memory operand acceptable? */ - if (flag_force_mem - || ! ((*insn_operand_predicate[(int) CODE_FOR_extzv][1]) - (xop0, GET_MODE (xop0)))) - { - /* No, load into a reg and extract from there. */ - enum machine_mode bestmode; - - /* Get the mode to use for inserting into this field. If - OP0 is BLKmode, get the smallest mode consistent with the - alignment. If OP0 is a non-BLKmode object that is no - wider than MAXMODE, use its mode. Otherwise, use the - smallest mode containing the field. */ - - if (GET_MODE (xop0) == BLKmode - || (GET_MODE_SIZE (GET_MODE (op0)) - > GET_MODE_SIZE (maxmode))) - bestmode = get_best_mode (bitsize, bitnum, - align * BITS_PER_UNIT, maxmode, - MEM_VOLATILE_P (xop0)); - else - bestmode = GET_MODE (xop0); - - if (bestmode == VOIDmode) - goto extzv_loses; - - /* Compute offset as multiple of this unit, - counting in bytes. */ - unit = GET_MODE_BITSIZE (bestmode); - xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode); - xbitpos = bitnum % unit; - xop0 = change_address (xop0, bestmode, - plus_constant (XEXP (xop0, 0), - xoffset)); - /* Fetch it to a register in that size. */ - xop0 = force_reg (bestmode, xop0); - - /* XBITPOS counts within UNIT, which is what is expected. */ - } - else - /* Get ref to first byte containing part of the field. */ - xop0 = change_address (xop0, byte_mode, - plus_constant (XEXP (xop0, 0), xoffset)); - - volatile_ok = save_volatile_ok; - } - - /* If op0 is a register, we need it in MAXMODE (which is usually - SImode). to make it acceptable to the format of extzv. */ - if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != maxmode) - abort (); - if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode) - xop0 = gen_rtx (SUBREG, maxmode, xop0, 0); - - /* On big-endian machines, we count bits from the most significant. - If the bit field insn does not, we must invert. */ -#if BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN - xbitpos = unit - bitsize - xbitpos; -#endif - /* Now convert from counting within UNIT to counting in MAXMODE. */ -#if BITS_BIG_ENDIAN - if (GET_CODE (xop0) != MEM) - xbitpos += GET_MODE_BITSIZE (maxmode) - unit; -#endif - unit = GET_MODE_BITSIZE (maxmode); - - if (xtarget == 0 - || (flag_force_mem && GET_CODE (xtarget) == MEM)) - xtarget = xspec_target = gen_reg_rtx (tmode); - - if (GET_MODE (xtarget) != maxmode) - { - if (GET_CODE (xtarget) == REG) - { - int wider = (GET_MODE_SIZE (maxmode) - > GET_MODE_SIZE (GET_MODE (xtarget))); - xtarget = gen_lowpart (maxmode, xtarget); - if (wider) - xspec_target_subreg = xtarget; - } - else - xtarget = gen_reg_rtx (maxmode); - } - - /* If this machine's extzv insists on a register target, - make sure we have one. */ - if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][0]) - (xtarget, maxmode))) - xtarget = gen_reg_rtx (maxmode); - - bitsize_rtx = GEN_INT (bitsize); - bitpos_rtx = GEN_INT (xbitpos); - - pat = gen_extzv (protect_from_queue (xtarget, 1), - xop0, bitsize_rtx, bitpos_rtx); - if (pat) - { - emit_insn (pat); - target = xtarget; - spec_target = xspec_target; - spec_target_subreg = xspec_target_subreg; - } - else - { - delete_insns_since (last); - target = extract_fixed_bit_field (tmode, op0, offset, bitsize, - bitpos, target, 1, align); - } - } - else - extzv_loses: -#endif - target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos, - target, 1, align); - } - else - { -#ifdef HAVE_extv - if (HAVE_extv - && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extv][0]) - >= bitsize)) - { - int xbitpos = bitpos, xoffset = offset; - rtx bitsize_rtx, bitpos_rtx; - rtx last = get_last_insn(); - rtx xop0 = op0, xtarget = target; - rtx xspec_target = spec_target; - rtx xspec_target_subreg = spec_target_subreg; - rtx pat; - enum machine_mode maxmode - = insn_operand_mode[(int) CODE_FOR_extv][0]; - - if (GET_CODE (xop0) == MEM) - { - /* Is the memory operand acceptable? */ - if (! ((*insn_operand_predicate[(int) CODE_FOR_extv][1]) - (xop0, GET_MODE (xop0)))) - { - /* No, load into a reg and extract from there. */ - enum machine_mode bestmode; - - /* Get the mode to use for inserting into this field. If - OP0 is BLKmode, get the smallest mode consistent with the - alignment. If OP0 is a non-BLKmode object that is no - wider than MAXMODE, use its mode. Otherwise, use the - smallest mode containing the field. */ - - if (GET_MODE (xop0) == BLKmode - || (GET_MODE_SIZE (GET_MODE (op0)) - > GET_MODE_SIZE (maxmode))) - bestmode = get_best_mode (bitsize, bitnum, - align * BITS_PER_UNIT, maxmode, - MEM_VOLATILE_P (xop0)); - else - bestmode = GET_MODE (xop0); - - if (bestmode == VOIDmode) - goto extv_loses; - - /* Compute offset as multiple of this unit, - counting in bytes. */ - unit = GET_MODE_BITSIZE (bestmode); - xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode); - xbitpos = bitnum % unit; - xop0 = change_address (xop0, bestmode, - plus_constant (XEXP (xop0, 0), - xoffset)); - /* Fetch it to a register in that size. */ - xop0 = force_reg (bestmode, xop0); - - /* XBITPOS counts within UNIT, which is what is expected. */ - } - else - /* Get ref to first byte containing part of the field. */ - xop0 = change_address (xop0, byte_mode, - plus_constant (XEXP (xop0, 0), xoffset)); - } - - /* If op0 is a register, we need it in MAXMODE (which is usually - SImode) to make it acceptable to the format of extv. */ - if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != maxmode) - abort (); - if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode) - xop0 = gen_rtx (SUBREG, maxmode, xop0, 0); - - /* On big-endian machines, we count bits from the most significant. - If the bit field insn does not, we must invert. */ -#if BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN - xbitpos = unit - bitsize - xbitpos; -#endif - /* XBITPOS counts within a size of UNIT. - Adjust to count within a size of MAXMODE. */ -#if BITS_BIG_ENDIAN - if (GET_CODE (xop0) != MEM) - xbitpos += (GET_MODE_BITSIZE (maxmode) - unit); -#endif - unit = GET_MODE_BITSIZE (maxmode); - - if (xtarget == 0 - || (flag_force_mem && GET_CODE (xtarget) == MEM)) - xtarget = xspec_target = gen_reg_rtx (tmode); - - if (GET_MODE (xtarget) != maxmode) - { - if (GET_CODE (xtarget) == REG) - { - int wider = (GET_MODE_SIZE (maxmode) - > GET_MODE_SIZE (GET_MODE (xtarget))); - xtarget = gen_lowpart (maxmode, xtarget); - if (wider) - xspec_target_subreg = xtarget; - } - else - xtarget = gen_reg_rtx (maxmode); - } - - /* If this machine's extv insists on a register target, - make sure we have one. */ - if (! ((*insn_operand_predicate[(int) CODE_FOR_extv][0]) - (xtarget, maxmode))) - xtarget = gen_reg_rtx (maxmode); - - bitsize_rtx = GEN_INT (bitsize); - bitpos_rtx = GEN_INT (xbitpos); - - pat = gen_extv (protect_from_queue (xtarget, 1), - xop0, bitsize_rtx, bitpos_rtx); - if (pat) - { - emit_insn (pat); - target = xtarget; - spec_target = xspec_target; - spec_target_subreg = xspec_target_subreg; - } - else - { - delete_insns_since (last); - target = extract_fixed_bit_field (tmode, op0, offset, bitsize, - bitpos, target, 0, align); - } - } - else - extv_loses: -#endif - target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos, - target, 0, align); - } - if (target == spec_target) - return target; - if (target == spec_target_subreg) - return spec_target; - if (GET_MODE (target) != tmode && GET_MODE (target) != mode) - { - /* If the target mode is floating-point, first convert to the - integer mode of that size and then access it as a floating-point - value via a SUBREG. */ - if (GET_MODE_CLASS (tmode) == MODE_FLOAT) - { - target = convert_to_mode (mode_for_size (GET_MODE_BITSIZE (tmode), - MODE_INT, 0), - target, unsignedp); - if (GET_CODE (target) != REG) - target = copy_to_reg (target); - return gen_rtx (SUBREG, tmode, target, 0); - } - else - return convert_to_mode (tmode, target, unsignedp); - } - return target; -} - -/* Extract a bit field using shifts and boolean operations - Returns an rtx to represent the value. - OP0 addresses a register (word) or memory (byte). - BITPOS says which bit within the word or byte the bit field starts in. - OFFSET says how many bytes farther the bit field starts; - it is 0 if OP0 is a register. - BITSIZE says how many bits long the bit field is. - (If OP0 is a register, it may be narrower than a full word, - but BITPOS still counts within a full word, - which is significant on bigendian machines.) - - UNSIGNEDP is nonzero for an unsigned bit field (don't sign-extend value). - If TARGET is nonzero, attempts to store the value there - and return TARGET, but this is not guaranteed. - If TARGET is not used, create a pseudo-reg of mode TMODE for the value. - - ALIGN is the alignment that STR_RTX is known to have, measured in bytes. */ - -static rtx -extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos, - target, unsignedp, align) - enum machine_mode tmode; - register rtx op0, target; - register int offset, bitsize, bitpos; - int unsignedp; - int align; -{ - int total_bits = BITS_PER_WORD; - enum machine_mode mode; - - if (GET_CODE (op0) == SUBREG || GET_CODE (op0) == REG) - { - /* Special treatment for a bit field split across two registers. */ - if (bitsize + bitpos > BITS_PER_WORD) - return extract_split_bit_field (op0, bitsize, bitpos, - unsignedp, align); - } - else - { - /* Get the proper mode to use for this field. We want a mode that - includes the entire field. If such a mode would be larger than - a word, we won't be doing the extraction the normal way. */ - - mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT, - align * BITS_PER_UNIT, word_mode, - GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0)); - - if (mode == VOIDmode) - /* The only way this should occur is if the field spans word - boundaries. */ - return extract_split_bit_field (op0, bitsize, - bitpos + offset * BITS_PER_UNIT, - unsignedp, align); - - total_bits = GET_MODE_BITSIZE (mode); - - /* Make sure bitpos is valid for the chosen mode. Adjust BITPOS to - be be in the range 0 to total_bits-1, and put any excess bytes in - OFFSET. */ - if (bitpos >= total_bits) - { - offset += (bitpos / total_bits) * (total_bits / BITS_PER_UNIT); - bitpos -= ((bitpos / total_bits) * (total_bits / BITS_PER_UNIT) - * BITS_PER_UNIT); - } - - /* Get ref to an aligned byte, halfword, or word containing the field. - Adjust BITPOS to be position within a word, - and OFFSET to be the offset of that word. - Then alter OP0 to refer to that word. */ - bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT; - offset -= (offset % (total_bits / BITS_PER_UNIT)); - op0 = change_address (op0, mode, - plus_constant (XEXP (op0, 0), offset)); - } - - mode = GET_MODE (op0); - -#if BYTES_BIG_ENDIAN - /* BITPOS is the distance between our msb and that of OP0. - Convert it to the distance from the lsb. */ - - bitpos = total_bits - bitsize - bitpos; -#endif - /* Now BITPOS is always the distance between the field's lsb and that of OP0. - We have reduced the big-endian case to the little-endian case. */ - - if (unsignedp) - { - if (bitpos) - { - /* If the field does not already start at the lsb, - shift it so it does. */ - tree amount = build_int_2 (bitpos, 0); - /* Maybe propagate the target for the shift. */ - /* But not if we will return it--could confuse integrate.c. */ - rtx subtarget = (target != 0 && GET_CODE (target) == REG - && !REG_FUNCTION_VALUE_P (target) - ? target : 0); - if (tmode != mode) subtarget = 0; - op0 = expand_shift (RSHIFT_EXPR, mode, op0, amount, subtarget, 1); - } - /* Convert the value to the desired mode. */ - if (mode != tmode) - op0 = convert_to_mode (tmode, op0, 1); - - /* Unless the msb of the field used to be the msb when we shifted, - mask out the upper bits. */ - - if (GET_MODE_BITSIZE (mode) != bitpos + bitsize -#if 0 -#ifdef SLOW_ZERO_EXTEND - /* Always generate an `and' if - we just zero-extended op0 and SLOW_ZERO_EXTEND, since it - will combine fruitfully with the zero-extend. */ - || tmode != mode -#endif -#endif - ) - return expand_binop (GET_MODE (op0), and_optab, op0, - mask_rtx (GET_MODE (op0), 0, bitsize, 0), - target, 1, OPTAB_LIB_WIDEN); - return op0; - } - - /* To extract a signed bit-field, first shift its msb to the msb of the word, - then arithmetic-shift its lsb to the lsb of the word. */ - op0 = force_reg (mode, op0); - if (mode != tmode) - target = 0; - - /* Find the narrowest integer mode that contains the field. */ - - for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode; - mode = GET_MODE_WIDER_MODE (mode)) - if (GET_MODE_BITSIZE (mode) >= bitsize + bitpos) - { - op0 = convert_to_mode (mode, op0, 0); - break; - } - - if (GET_MODE_BITSIZE (mode) != (bitsize + bitpos)) - { - tree amount = build_int_2 (GET_MODE_BITSIZE (mode) - (bitsize + bitpos), 0); - /* Maybe propagate the target for the shift. */ - /* But not if we will return the result--could confuse integrate.c. */ - rtx subtarget = (target != 0 && GET_CODE (target) == REG - && ! REG_FUNCTION_VALUE_P (target) - ? target : 0); - op0 = expand_shift (LSHIFT_EXPR, mode, op0, amount, subtarget, 1); - } - - return expand_shift (RSHIFT_EXPR, mode, op0, - build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0), - target, 0); -} - -/* Return a constant integer (CONST_INT or CONST_DOUBLE) mask value - of mode MODE with BITSIZE ones followed by BITPOS zeros, or the - complement of that if COMPLEMENT. The mask is truncated if - necessary to the width of mode MODE. */ - -static rtx -mask_rtx (mode, bitpos, bitsize, complement) - enum machine_mode mode; - int bitpos, bitsize, complement; -{ - HOST_WIDE_INT masklow, maskhigh; - - if (bitpos < HOST_BITS_PER_WIDE_INT) - masklow = (HOST_WIDE_INT) -1 << bitpos; - else - masklow = 0; - - if (bitpos + bitsize < HOST_BITS_PER_WIDE_INT) - masklow &= ((unsigned HOST_WIDE_INT) -1 - >> (HOST_BITS_PER_WIDE_INT - bitpos - bitsize)); - - if (bitpos <= HOST_BITS_PER_WIDE_INT) - maskhigh = -1; - else - maskhigh = (HOST_WIDE_INT) -1 << (bitpos - HOST_BITS_PER_WIDE_INT); - - if (bitpos + bitsize > HOST_BITS_PER_WIDE_INT) - maskhigh &= ((unsigned HOST_WIDE_INT) -1 - >> (2 * HOST_BITS_PER_WIDE_INT - bitpos - bitsize)); - else - maskhigh = 0; - - if (complement) - { - maskhigh = ~maskhigh; - masklow = ~masklow; - } - - return immed_double_const (masklow, maskhigh, mode); -} - -/* Return a constant integer (CONST_INT or CONST_DOUBLE) rtx with the value - VALUE truncated to BITSIZE bits and then shifted left BITPOS bits. */ - -static rtx -lshift_value (mode, value, bitpos, bitsize) - enum machine_mode mode; - rtx value; - int bitpos, bitsize; -{ - unsigned HOST_WIDE_INT v = INTVAL (value); - HOST_WIDE_INT low, high; - - if (bitsize < HOST_BITS_PER_WIDE_INT) - v &= ~((HOST_WIDE_INT) -1 << bitsize); - - if (bitpos < HOST_BITS_PER_WIDE_INT) - { - low = v << bitpos; - high = (bitpos > 0 ? (v >> (HOST_BITS_PER_WIDE_INT - bitpos)) : 0); - } - else - { - low = 0; - high = v << (bitpos - HOST_BITS_PER_WIDE_INT); - } - - return immed_double_const (low, high, mode); -} - -/* Extract a bit field that is split across two words - and return an RTX for the result. - - OP0 is the REG, SUBREG or MEM rtx for the first of the two words. - BITSIZE is the field width; BITPOS, position of its first bit, in the word. - UNSIGNEDP is 1 if should zero-extend the contents; else sign-extend. */ - -static rtx -extract_split_bit_field (op0, bitsize, bitpos, unsignedp, align) - rtx op0; - int bitsize, bitpos, unsignedp, align; -{ - /* BITSIZE_1 is size of the part in the first word. */ - int bitsize_1 = BITS_PER_WORD - bitpos % BITS_PER_WORD; - /* BITSIZE_2 is size of the rest (in the following word). */ - int bitsize_2 = bitsize - bitsize_1; - rtx part1, part2, result; - int unit = GET_CODE (op0) == MEM ? BITS_PER_UNIT : BITS_PER_WORD; - int offset = bitpos / unit; - rtx word; - - /* The field must span exactly one word boundary. */ - if (bitpos / BITS_PER_WORD != (bitpos + bitsize - 1) / BITS_PER_WORD - 1) - abort (); - - /* Get the part of the bit field from the first word. If OP0 is a MEM, - pass OP0 and the offset computed above. Otherwise, get the proper - word and pass an offset of zero. */ - word = (GET_CODE (op0) == MEM ? op0 - : operand_subword_force (op0, offset, GET_MODE (op0))); - part1 = extract_fixed_bit_field (word_mode, word, - GET_CODE (op0) == MEM ? offset : 0, - bitsize_1, bitpos % unit, NULL_RTX, - 1, align); - - /* Offset op0 by 1 word to get to the following one. */ - if (GET_CODE (op0) == SUBREG) - word = operand_subword_force (SUBREG_REG (op0), - SUBREG_WORD (op0) + offset + 1, VOIDmode); - else if (GET_CODE (op0) == MEM) - word = op0; - else - word = operand_subword_force (op0, offset + 1, GET_MODE (op0)); - - /* Get the part of the bit field from the second word. */ - part2 = extract_fixed_bit_field (word_mode, word, - (GET_CODE (op0) == MEM - ? CEIL (offset + 1, UNITS_PER_WORD) * UNITS_PER_WORD - : 0), - bitsize_2, 0, NULL_RTX, 1, align); - - /* Shift the more significant part up to fit above the other part. */ -#if BYTES_BIG_ENDIAN - part1 = expand_shift (LSHIFT_EXPR, word_mode, part1, - build_int_2 (bitsize_2, 0), 0, 1); -#else - part2 = expand_shift (LSHIFT_EXPR, word_mode, part2, - build_int_2 (bitsize_1, 0), 0, 1); -#endif - - /* Combine the two parts with bitwise or. This works - because we extracted both parts as unsigned bit fields. */ - result = expand_binop (word_mode, ior_optab, part1, part2, NULL_RTX, 1, - OPTAB_LIB_WIDEN); - - /* Unsigned bit field: we are done. */ - if (unsignedp) - return result; - /* Signed bit field: sign-extend with two arithmetic shifts. */ - result = expand_shift (LSHIFT_EXPR, word_mode, result, - build_int_2 (BITS_PER_WORD - bitsize, 0), - NULL_RTX, 0); - return expand_shift (RSHIFT_EXPR, word_mode, result, - build_int_2 (BITS_PER_WORD - bitsize, 0), NULL_RTX, 0); -} - -/* Add INC into TARGET. */ - -void -expand_inc (target, inc) - rtx target, inc; -{ - rtx value = expand_binop (GET_MODE (target), add_optab, - target, inc, - target, 0, OPTAB_LIB_WIDEN); - if (value != target) - emit_move_insn (target, value); -} - -/* Subtract DEC from TARGET. */ - -void -expand_dec (target, dec) - rtx target, dec; -{ - rtx value = expand_binop (GET_MODE (target), sub_optab, - target, dec, - target, 0, OPTAB_LIB_WIDEN); - if (value != target) - emit_move_insn (target, value); -} - -/* Output a shift instruction for expression code CODE, - with SHIFTED being the rtx for the value to shift, - and AMOUNT the tree for the amount to shift by. - Store the result in the rtx TARGET, if that is convenient. - If UNSIGNEDP is nonzero, do a logical shift; otherwise, arithmetic. - Return the rtx for where the value is. */ - -rtx -expand_shift (code, mode, shifted, amount, target, unsignedp) - enum tree_code code; - register enum machine_mode mode; - rtx shifted; - tree amount; - register rtx target; - int unsignedp; -{ - register rtx op1, temp = 0; - register int left = (code == LSHIFT_EXPR || code == LROTATE_EXPR); - register int rotate = (code == LROTATE_EXPR || code == RROTATE_EXPR); - int try; - - /* Previously detected shift-counts computed by NEGATE_EXPR - and shifted in the other direction; but that does not work - on all machines. */ - - op1 = expand_expr (amount, NULL_RTX, VOIDmode, 0); - - if (op1 == const0_rtx) - return shifted; - - for (try = 0; temp == 0 && try < 3; try++) - { - enum optab_methods methods; - - if (try == 0) - methods = OPTAB_DIRECT; - else if (try == 1) - methods = OPTAB_WIDEN; - else - methods = OPTAB_LIB_WIDEN; - - if (rotate) - { - /* Widening does not work for rotation. */ - if (methods == OPTAB_WIDEN) - continue; - else if (methods == OPTAB_LIB_WIDEN) - { - /* If we are rotating by a constant that is valid and - we have been unable to open-code this by a rotation, - do it as the IOR of two shifts. I.e., to rotate A - by N bits, compute (A << N) | ((unsigned) A >> (C - N)) - where C is the bitsize of A. - - It is theoretically possible that the target machine might - not be able to perform either shift and hence we would - be making two libcalls rather than just the one for the - shift (similarly if IOR could not be done). We will allow - this extremely unlikely lossage to avoid complicating the - code below. */ - - if (GET_CODE (op1) == CONST_INT && INTVAL (op1) > 0 - && INTVAL (op1) < GET_MODE_BITSIZE (mode)) - { - rtx subtarget = target == shifted ? 0 : target; - rtx temp1; - tree other_amount - = build_int_2 (GET_MODE_BITSIZE (mode) - INTVAL (op1), 0); - - shifted = force_reg (mode, shifted); - - temp = expand_shift (left ? LSHIFT_EXPR : RSHIFT_EXPR, - mode, shifted, amount, subtarget, 1); - temp1 = expand_shift (left ? RSHIFT_EXPR : LSHIFT_EXPR, - mode, shifted, other_amount, 0, 1); - return expand_binop (mode, ior_optab, temp, temp1, target, - unsignedp, methods); - } - else - methods = OPTAB_LIB; - } - - temp = expand_binop (mode, - left ? rotl_optab : rotr_optab, - shifted, op1, target, unsignedp, methods); - - /* If we don't have the rotate, but we are rotating by a constant - that is in range, try a rotate in the opposite direction. */ - - if (temp == 0 && GET_CODE (op1) == CONST_INT - && INTVAL (op1) > 0 && INTVAL (op1) < GET_MODE_BITSIZE (mode)) - temp = expand_binop (mode, - left ? rotr_optab : rotl_optab, - shifted, - GEN_INT (GET_MODE_BITSIZE (mode) - - INTVAL (op1)), - target, unsignedp, methods); - } - else if (unsignedp) - { - temp = expand_binop (mode, - left ? lshl_optab : lshr_optab, - shifted, op1, target, unsignedp, methods); - if (temp == 0 && left) - temp = expand_binop (mode, ashl_optab, - shifted, op1, target, unsignedp, methods); - } - - /* Do arithmetic shifts. - Also, if we are going to widen the operand, we can just as well - use an arithmetic right-shift instead of a logical one. */ - if (temp == 0 && ! rotate - && (! unsignedp || (! left && methods == OPTAB_WIDEN))) - { - enum optab_methods methods1 = methods; - - /* If trying to widen a log shift to an arithmetic shift, - don't accept an arithmetic shift of the same size. */ - if (unsignedp) - methods1 = OPTAB_MUST_WIDEN; - - /* Arithmetic shift */ - - temp = expand_binop (mode, - left ? ashl_optab : ashr_optab, - shifted, op1, target, unsignedp, methods1); - } - -#ifdef HAVE_extzv - /* We can do a logical (unsigned) right shift with a bit-field - extract insn. But first check if one of the above methods worked. */ - if (temp != 0) - return temp; - - if (unsignedp && code == RSHIFT_EXPR && ! BITS_BIG_ENDIAN && HAVE_extzv) - { - enum machine_mode output_mode - = insn_operand_mode[(int) CODE_FOR_extzv][0]; - - if ((methods == OPTAB_DIRECT && mode == output_mode) - || (methods == OPTAB_WIDEN - && GET_MODE_SIZE (mode) < GET_MODE_SIZE (output_mode))) - { - rtx shifted1 = convert_to_mode (output_mode, - protect_from_queue (shifted, 0), - 1); - enum machine_mode length_mode - = insn_operand_mode[(int) CODE_FOR_extzv][2]; - enum machine_mode pos_mode - = insn_operand_mode[(int) CODE_FOR_extzv][3]; - rtx target1 = 0; - rtx last = get_last_insn (); - rtx width; - rtx xop1 = op1; - rtx pat; - - if (target != 0) - target1 = protect_from_queue (target, 1); - - /* We define extract insns as having OUTPUT_MODE in a register - and the mode of operand 1 in memory. Since we want - OUTPUT_MODE, we will always force the operand into a - register. At some point we might want to support MEM - directly. */ - shifted1 = force_reg (output_mode, shifted1); - - /* If we don't have or cannot use a suggested target, - make a place for the result, in the proper mode. */ - if (methods == OPTAB_WIDEN || target1 == 0 - || ! ((*insn_operand_predicate[(int) CODE_FOR_extzv][0]) - (target1, output_mode))) - target1 = gen_reg_rtx (output_mode); - - xop1 = protect_from_queue (xop1, 0); - xop1 = convert_to_mode (pos_mode, xop1, - TREE_UNSIGNED (TREE_TYPE (amount))); - - /* If this machine's extzv insists on a register for - operand 3 (position), arrange for that. */ - if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][3]) - (xop1, pos_mode))) - xop1 = force_reg (pos_mode, xop1); - - /* WIDTH gets the width of the bit field to extract: - wordsize minus # bits to shift by. */ - if (GET_CODE (xop1) == CONST_INT) - width = GEN_INT (GET_MODE_BITSIZE (mode) - INTVAL (op1)); - else - { - /* Now get the width in the proper mode. */ - op1 = protect_from_queue (op1, 0); - width = convert_to_mode (length_mode, op1, - TREE_UNSIGNED (TREE_TYPE (amount))); - - width = expand_binop (length_mode, sub_optab, - GEN_INT (GET_MODE_BITSIZE (mode)), - width, NULL_RTX, 0, OPTAB_LIB_WIDEN); - } - - /* If this machine's extzv insists on a register for - operand 2 (length), arrange for that. */ - if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][2]) - (width, length_mode))) - width = force_reg (length_mode, width); - - /* Now extract with WIDTH, omitting OP1 least sig bits. */ - pat = gen_extzv (target1, shifted1, width, xop1); - if (pat) - { - emit_insn (pat); - temp = convert_to_mode (mode, target1, 1); - } - else - delete_insns_since (last); - } - - /* Can also do logical shift with signed bit-field extract - followed by inserting the bit-field at a different position. - That strategy is not yet implemented. */ - } -#endif /* HAVE_extzv */ - } - - if (temp == 0) - abort (); - return temp; -} - -enum alg_code { alg_zero, alg_m, alg_shift, - alg_add_t_m2, alg_sub_t_m2, - alg_add_factor, alg_sub_factor, - alg_add_t2_m, alg_sub_t2_m, - alg_add, alg_subtract, alg_factor, alg_shiftop }; - -/* This structure records a sequence of operations. - `ops' is the number of operations recorded. - `cost' is their total cost. - The operations are stored in `op' and the corresponding - logarithms of the integer coefficients in `log'. - - These are the operations: - alg_zero total := 0; - alg_m total := multiplicand; - alg_shift total := total * coeff - alg_add_t_m2 total := total + multiplicand * coeff; - alg_sub_t_m2 total := total - multiplicand * coeff; - alg_add_factor total := total * coeff + total; - alg_sub_factor total := total * coeff - total; - alg_add_t2_m total := total * coeff + multiplicand; - alg_sub_t2_m total := total * coeff - multiplicand; - - The first operand must be either alg_zero or alg_m. */ - -#ifndef MAX_BITS_PER_WORD -#define MAX_BITS_PER_WORD BITS_PER_WORD -#endif - -struct algorithm -{ - short cost; - short ops; - /* The size of the OP and LOG fields are not directly related to the - word size, but the worst-case algorithms will be if we have few - consecutive ones or zeros, i.e., a multiplicand like 10101010101... - In that case we will generate shift-by-2, add, shift-by-2, add,..., - in total wordsize operations. */ - enum alg_code op[MAX_BITS_PER_WORD]; - char log[MAX_BITS_PER_WORD]; -}; - -/* Compute and return the best algorithm for multiplying by T. - The algorithm must cost less than cost_limit - If retval.cost >= COST_LIMIT, no algorithm was found and all - other field of the returned struct are undefined. */ - -static struct algorithm -synth_mult (t, cost_limit) - unsigned HOST_WIDE_INT t; - int cost_limit; -{ - int m; - struct algorithm *best_alg - = (struct algorithm *)alloca (sizeof (struct algorithm)); - struct algorithm *alg_in - = (struct algorithm *)alloca (sizeof (struct algorithm)); - unsigned int cost; - unsigned HOST_WIDE_INT q; - - /* Indicate that no algorithm is yet found. If no algorithm - is found, this value will be returned and indicate failure. */ - best_alg->cost = cost_limit; - - if (cost_limit <= 0) - return *best_alg; - - /* t == 1 can be done in zero cost. */ - if (t == 1) - { - best_alg->ops = 1; - best_alg->cost = 0; - best_alg->op[0] = alg_m; - return *best_alg; - } - - /* t == 0 sometimes has a cost. If it does and it exceeds our limit, - fail now. */ - - else if (t == 0) - { - if (zero_cost >= cost_limit) - return *best_alg; - else - { - best_alg->ops = 1; - best_alg->cost = zero_cost; - best_alg->op[0] = alg_zero; - return *best_alg; - } - } - - /* If we have a group of zero bits at the low-order part of T, try - multiplying by the remaining bits and then doing a shift. */ - - if ((t & 1) == 0) - { - m = floor_log2 (t & -t); /* m = number of low zero bits */ - q = t >> m; - cost = shift_cost[m]; - if (cost < cost_limit) - { - *alg_in = synth_mult (q, cost_limit - cost); - - cost += alg_in->cost; - if (cost < best_alg->cost) - { - struct algorithm *x; - x = alg_in, alg_in = best_alg, best_alg = x; - best_alg->log[best_alg->ops] = m; - best_alg->op[best_alg->ops++] = alg_shift; - best_alg->cost = cost_limit = cost; - } - } - } - - /* Look for factors of t of the form - t = q(2**m +- 1), 2 <= m <= floor(log2(t - 1)). - If we find such a factor, we can multiply by t using an algorithm that - multiplies by q, shift the result by m and add/subtract it to itself. - - We search for large factors first and loop down, even if large factors - are less probable than small; if we find a large factor we will find a - good sequence quickly, and therefore be able to prune (by decreasing - COST_LIMIT) the search. */ - - for (m = floor_log2 (t - 1); m >= 2; m--) - { - unsigned HOST_WIDE_INT d; - - d = ((unsigned HOST_WIDE_INT) 1 << m) + 1; - if (t % d == 0 && t > d) - { - cost = MIN (shiftadd_cost[m], add_cost + shift_cost[m]); - *alg_in = synth_mult (t / d, cost_limit - cost); - - cost += alg_in->cost; - if (cost < best_alg->cost) - { - struct algorithm *x; - x = alg_in, alg_in = best_alg, best_alg = x; - best_alg->log[best_alg->ops] = m; - best_alg->op[best_alg->ops++] = alg_add_factor; - best_alg->cost = cost_limit = cost; - } - } - - d = ((unsigned HOST_WIDE_INT) 1 << m) - 1; - if (t % d == 0 && t > d) - { - cost = MIN (shiftsub_cost[m], add_cost + shift_cost[m]); - *alg_in = synth_mult (t / d, cost_limit - cost); - - cost += alg_in->cost; - if (cost < best_alg->cost) - { - struct algorithm *x; - x = alg_in, alg_in = best_alg, best_alg = x; - best_alg->log[best_alg->ops] = m; - best_alg->op[best_alg->ops++] = alg_sub_factor; - best_alg->cost = cost_limit = cost; - } - } - } - - /* Try shift-and-add (load effective address) instructions, - i.e. do a*3, a*5, a*9. */ - if ((t & 1) != 0) - { - q = t - 1; - q = q & -q; - m = exact_log2 (q); - if (m >= 0) - { - cost = shiftadd_cost[m]; - *alg_in = synth_mult ((t - 1) >> m, cost_limit - cost); - - cost += alg_in->cost; - if (cost < best_alg->cost) - { - struct algorithm *x; - x = alg_in, alg_in = best_alg, best_alg = x; - best_alg->log[best_alg->ops] = m; - best_alg->op[best_alg->ops++] = alg_add_t2_m; - best_alg->cost = cost_limit = cost; - } - } - - q = t + 1; - q = q & -q; - m = exact_log2 (q); - if (m >= 0) - { - cost = shiftsub_cost[m]; - *alg_in = synth_mult ((t + 1) >> m, cost_limit - cost); - - cost += alg_in->cost; - if (cost < best_alg->cost) - { - struct algorithm *x; - x = alg_in, alg_in = best_alg, best_alg = x; - best_alg->log[best_alg->ops] = m; - best_alg->op[best_alg->ops++] = alg_sub_t2_m; - best_alg->cost = cost_limit = cost; - } - } - } - - /* Now, use the simple method of adding or subtracting at the leftmost - 1-bit. */ - { - unsigned HOST_WIDE_INT w; - - q = t & -t; /* get out lsb */ - for (w = q; (w & t) != 0; w <<= 1) - ; - if ((w > q << 1) - /* Reject the case where t has only two bits. - Thus we prefer addition in that case. */ - && !(t < w && w == q << 2)) - { - /* There are many bits in a row. Make 'em by subtraction. */ - - m = exact_log2 (q); - - /* Don't use shiftsub_cost here, this operation - scales wrong operand. */ - cost = add_cost + shift_cost[m]; - *alg_in = synth_mult (t + q, cost_limit - cost); - - cost += alg_in->cost; - if (cost < best_alg->cost) - { - struct algorithm *x; - x = alg_in, alg_in = best_alg, best_alg = x; - best_alg->log[best_alg->ops] = m; - best_alg->op[best_alg->ops++] = alg_sub_t_m2; - best_alg->cost = cost_limit = cost; - } - } - else - { - /* There's only one or two bit at the left. Make it by addition. */ - - m = exact_log2 (q); - cost = MIN (shiftadd_cost[m], add_cost + shift_cost[m]); - *alg_in = synth_mult (t - q, cost_limit - cost); - - cost += alg_in->cost; - if (cost < best_alg->cost) - { - struct algorithm *x; - x = alg_in, alg_in = best_alg, best_alg = x; - best_alg->log[best_alg->ops] = m; - best_alg->op[best_alg->ops++] = alg_add_t_m2; - best_alg->cost = cost_limit = cost; - } - } - } - - /* If we are getting a too long sequence for `struct algorithm' - to record, store a fake cost to make this search fail. */ - if (best_alg->ops == MAX_BITS_PER_WORD) - best_alg->cost = cost_limit; - - return *best_alg; -} - -/* Perform a multiplication and return an rtx for the result. - MODE is mode of value; OP0 and OP1 are what to multiply (rtx's); - TARGET is a suggestion for where to store the result (an rtx). - - We check specially for a constant integer as OP1. - If you want this check for OP0 as well, then before calling - you should swap the two operands if OP0 would be constant. */ - -rtx -expand_mult (mode, op0, op1, target, unsignedp) - enum machine_mode mode; - register rtx op0, op1, target; - int unsignedp; -{ - rtx const_op1 = op1; - - /* If we are multiplying in DImode, it may still be a win - to try to work with shifts and adds. */ - if (GET_CODE (op1) == CONST_DOUBLE - && GET_MODE_CLASS (GET_MODE (op1)) == MODE_INT - && HOST_BITS_PER_INT <= BITS_PER_WORD) - { - if ((CONST_DOUBLE_HIGH (op1) == 0 && CONST_DOUBLE_LOW (op1) >= 0) - || (CONST_DOUBLE_HIGH (op1) == -1 && CONST_DOUBLE_LOW (op1) < 0)) - const_op1 = GEN_INT (CONST_DOUBLE_LOW (op1)); - } - - /* We used to test optimize here, on the grounds that it's better to - produce a smaller program when -O is not used. - But this causes such a terrible slowdown sometimes - that it seems better to use synth_mult always. */ - - if (GET_CODE (const_op1) == CONST_INT && ! mult_is_very_cheap) - { - struct algorithm alg; - struct algorithm neg_alg; - int negate = 0; - HOST_WIDE_INT val = INTVAL (op1); - HOST_WIDE_INT val_so_far; - rtx insn; - - /* Try to do the computation two ways: multiply by the negative of OP1 - and then negate, or do the multiplication directly. The latter is - usually faster for positive numbers and the former for negative - numbers, but the opposite can be faster if the original value - has a factor of 2**m +/- 1, while the negated value does not or - vice versa. */ - - alg = synth_mult (val, mult_cost); - neg_alg = synth_mult (- val, - (alg.cost < mult_cost ? alg.cost : mult_cost) - - negate_cost); - - if (neg_alg.cost + negate_cost < alg.cost) - alg = neg_alg, negate = 1; - - if (alg.cost < mult_cost) - { - /* We found something cheaper than a multiply insn. */ - int opno; - rtx accum, tem; - - op0 = protect_from_queue (op0, 0); - - /* Avoid referencing memory over and over. - For speed, but also for correctness when mem is volatile. */ - if (GET_CODE (op0) == MEM) - op0 = force_reg (mode, op0); - - /* ACCUM starts out either as OP0 or as a zero, depending on - the first operation. */ - - if (alg.op[0] == alg_zero) - { - accum = copy_to_mode_reg (mode, const0_rtx); - val_so_far = 0; - } - else if (alg.op[0] == alg_m) - { - accum = copy_to_mode_reg (mode, op0); - val_so_far = 1; - } - else - abort (); - - for (opno = 1; opno < alg.ops; opno++) - { - int log = alg.log[opno]; - rtx shift_subtarget = preserve_subexpressions_p () ? 0 : accum; - rtx add_target = opno == alg.ops - 1 && target != 0 ? target : 0; - - switch (alg.op[opno]) - { - case alg_shift: - accum = expand_shift (LSHIFT_EXPR, mode, accum, - build_int_2 (log, 0), NULL_RTX, 0); - val_so_far <<= log; - break; - - case alg_add_t_m2: - tem = expand_shift (LSHIFT_EXPR, mode, op0, - build_int_2 (log, 0), NULL_RTX, 0); - accum = force_operand (gen_rtx (PLUS, mode, accum, tem), - add_target ? add_target : accum); - val_so_far += (HOST_WIDE_INT) 1 << log; - break; - - case alg_sub_t_m2: - tem = expand_shift (LSHIFT_EXPR, mode, op0, - build_int_2 (log, 0), NULL_RTX, 0); - accum = force_operand (gen_rtx (MINUS, mode, accum, tem), - add_target ? add_target : accum); - val_so_far -= (HOST_WIDE_INT) 1 << log; - break; - - case alg_add_t2_m: - accum = expand_shift (LSHIFT_EXPR, mode, accum, - build_int_2 (log, 0), accum, 0); - accum = force_operand (gen_rtx (PLUS, mode, accum, op0), - add_target ? add_target : accum); - val_so_far = (val_so_far << log) + 1; - break; - - case alg_sub_t2_m: - accum = expand_shift (LSHIFT_EXPR, mode, accum, - build_int_2 (log, 0), accum, 0); - accum = force_operand (gen_rtx (MINUS, mode, accum, op0), - add_target ? add_target : accum); - val_so_far = (val_so_far << log) - 1; - break; - - case alg_add_factor: - tem = expand_shift (LSHIFT_EXPR, mode, accum, - build_int_2 (log, 0), NULL_RTX, 0); - accum = force_operand (gen_rtx (PLUS, mode, accum, tem), - add_target ? add_target : accum); - val_so_far += val_so_far << log; - break; - - case alg_sub_factor: - tem = expand_shift (LSHIFT_EXPR, mode, accum, - build_int_2 (log, 0), NULL_RTX, 0); - accum = force_operand (gen_rtx (MINUS, mode, tem, accum), - add_target ? add_target : tem); - val_so_far = (val_so_far << log) - val_so_far; - break; - - default: - abort ();; - } - - /* Write a REG_EQUAL note on the last insn so that we can cse - multiplication sequences. */ - - insn = get_last_insn (); - REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_EQUAL, - gen_rtx (MULT, mode, op0, GEN_INT (val_so_far)), - REG_NOTES (insn)); - } - - if (negate) - { - val_so_far = - val_so_far; - accum = expand_unop (mode, neg_optab, accum, target, 0); - } - - if (val != val_so_far) - abort (); - - return accum; - } - } - - /* This used to use umul_optab if unsigned, - but for non-widening multiply there is no difference - between signed and unsigned. */ - op0 = expand_binop (mode, smul_optab, - op0, op1, target, unsignedp, OPTAB_LIB_WIDEN); - if (op0 == 0) - abort (); - return op0; -} - -/* Emit the code to divide OP0 by OP1, putting the result in TARGET - if that is convenient, and returning where the result is. - You may request either the quotient or the remainder as the result; - specify REM_FLAG nonzero to get the remainder. - - CODE is the expression code for which kind of division this is; - it controls how rounding is done. MODE is the machine mode to use. - UNSIGNEDP nonzero means do unsigned division. */ - -/* ??? For CEIL_MOD_EXPR, can compute incorrect remainder with ANDI - and then correct it by or'ing in missing high bits - if result of ANDI is nonzero. - For ROUND_MOD_EXPR, can use ANDI and then sign-extend the result. - This could optimize to a bfexts instruction. - But C doesn't use these operations, so their optimizations are - left for later. */ - -rtx -expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp) - int rem_flag; - enum tree_code code; - enum machine_mode mode; - register rtx op0, op1, target; - int unsignedp; -{ - register rtx result = 0; - enum machine_mode compute_mode; - int log = -1; - int size; - int can_clobber_op0; - int mod_insn_no_good = 0; - rtx adjusted_op0 = op0; - optab optab1, optab2; - - /* We shouldn't be called with op1 == const1_rtx, but some of the - code below will malfunction if we are, so check here and handle - the special case if so. */ - if (op1 == const1_rtx) - return rem_flag ? const0_rtx : op0; - - /* Don't use the function value register as a target - since we have to read it as well as write it, - and function-inlining gets confused by this. */ - if (target && REG_P (target) && REG_FUNCTION_VALUE_P (target)) - target = 0; - - /* Don't clobber an operand while doing a multi-step calculation. */ - if (target) - if ((rem_flag && (reg_mentioned_p (target, op0) - || (GET_CODE (op0) == MEM && GET_CODE (target) == MEM))) - || reg_mentioned_p (target, op1) - || (GET_CODE (op1) == MEM && GET_CODE (target) == MEM)) - target = 0; - - can_clobber_op0 = (GET_CODE (op0) == REG && op0 == target); - - if (GET_CODE (op1) == CONST_INT) - log = exact_log2 (INTVAL (op1)); - - /* If log is >= 0, we are dividing by 2**log, and will do it by shifting, - which is really floor-division. Otherwise we will really do a divide, - and we assume that is trunc-division. - - We must correct the dividend by adding or subtracting something - based on the divisor, in order to do the kind of rounding specified - by CODE. The correction depends on what kind of rounding is actually - available, and that depends on whether we will shift or divide. - - In many of these cases it is possible to perform the operation by a - clever series of logical operations (shifts and/or exclusive-ors). - Although avoiding the jump has the advantage that it extends the basic - block and allows further optimization, the branch-free code is normally - at least one instruction longer in the (most common) case where the - dividend is non-negative. Performance measurements of the two - alternatives show that the branch-free code is slightly faster on the - IBM ROMP but slower on CISC processors (significantly slower on the - VAX). Accordingly, the jump code has been retained. - - On machines where the jump code is slower, the cost of a DIV or MOD - operation can be set small (less than twice that of an addition); in - that case, we pretend that we don't have a power of two and perform - a normal division or modulus operation. */ - - if ((code == TRUNC_MOD_EXPR || code == TRUNC_DIV_EXPR) - && ! unsignedp - && (rem_flag ? smod_pow2_cheap : sdiv_pow2_cheap)) - log = -1; - - /* Get the mode in which to perform this computation. Normally it will - be MODE, but sometimes we can't do the desired operation in MODE. - If so, pick a wider mode in which we can do the operation. Convert - to that mode at the start to avoid repeated conversions. - - First see what operations we need. These depend on the expression - we are evaluating. (We assume that divxx3 insns exist under the - same conditions that modxx3 insns and that these insns don't normally - fail. If these assumptions are not correct, we may generate less - efficient code in some cases.) - - Then see if we find a mode in which we can open-code that operation - (either a division, modulus, or shift). Finally, check for the smallest - mode for which we can do the operation with a library call. */ - - optab1 = (log >= 0 ? (unsignedp ? lshr_optab : ashr_optab) - : (unsignedp ? udiv_optab : sdiv_optab)); - optab2 = (log >= 0 ? optab1 : (unsignedp ? udivmod_optab : sdivmod_optab)); - - for (compute_mode = mode; compute_mode != VOIDmode; - compute_mode = GET_MODE_WIDER_MODE (compute_mode)) - if (optab1->handlers[(int) compute_mode].insn_code != CODE_FOR_nothing - || optab2->handlers[(int) compute_mode].insn_code != CODE_FOR_nothing) - break; - - if (compute_mode == VOIDmode) - for (compute_mode = mode; compute_mode != VOIDmode; - compute_mode = GET_MODE_WIDER_MODE (compute_mode)) - if (optab1->handlers[(int) compute_mode].libfunc - || optab2->handlers[(int) compute_mode].libfunc) - break; - - /* If we still couldn't find a mode, use MODE; we'll probably abort in - expand_binop. */ - if (compute_mode == VOIDmode) - compute_mode = mode; - - size = GET_MODE_BITSIZE (compute_mode); - - /* Now convert to the best mode to use. Show we made a copy of OP0 - and hence we can clobber it (we cannot use a SUBREG to widen - something. */ - if (compute_mode != mode) - { - adjusted_op0 = op0 = convert_to_mode (compute_mode, op0, unsignedp); - can_clobber_op0 = 1; - op1 = convert_to_mode (compute_mode, op1, unsignedp); - } - - /* If we are computing the remainder and one of the operands is a volatile - MEM, copy it into a register. */ - - if (rem_flag && GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0)) - adjusted_op0 = op0 = force_reg (compute_mode, op0), can_clobber_op0 = 1; - if (rem_flag && GET_CODE (op1) == MEM && MEM_VOLATILE_P (op1)) - op1 = force_reg (compute_mode, op1); - - /* If we are computing the remainder, op0 will be needed later to calculate - X - Y * (X / Y), therefore cannot be clobbered. */ - if (rem_flag) - can_clobber_op0 = 0; - - if (target == 0 || GET_MODE (target) != compute_mode) - target = gen_reg_rtx (compute_mode); - - switch (code) - { - case TRUNC_MOD_EXPR: - case TRUNC_DIV_EXPR: - if (log >= 0 && ! unsignedp) - { - /* Here we need to add OP1-1 if OP0 is negative, 0 otherwise. - This can be computed without jumps by arithmetically shifting - OP0 right LOG-1 places and then shifting right logically - SIZE-LOG bits. The resulting value is unconditionally added - to OP0. */ - if (log == 1 || BRANCH_COST >= 3) - { - rtx temp = gen_reg_rtx (compute_mode); - if (! can_clobber_op0) - /* Copy op0 to a reg, to play safe, - since this is done in the other path. */ - op0 = force_reg (compute_mode, op0); - temp = copy_to_suggested_reg (adjusted_op0, temp, compute_mode); - temp = expand_shift (RSHIFT_EXPR, compute_mode, temp, - build_int_2 (log - 1, 0), NULL_RTX, 0); - temp = expand_shift (RSHIFT_EXPR, compute_mode, temp, - build_int_2 (size - log, 0), - temp, 1); - /* We supply 0 as the target to make a new pseudo - for the value; that helps loop.c optimize the result. */ - adjusted_op0 = expand_binop (compute_mode, add_optab, - adjusted_op0, temp, - 0, 0, OPTAB_LIB_WIDEN); - } - else - { - rtx label = gen_label_rtx (); - if (! can_clobber_op0) - { - adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target, - compute_mode); - /* Copy op0 to a reg, since emit_cmp_insn will call emit_queue - which will screw up mem refs for autoincrements. */ - op0 = force_reg (compute_mode, op0); - } - emit_cmp_insn (adjusted_op0, const0_rtx, GE, - NULL_RTX, compute_mode, 0, 0); - emit_jump_insn (gen_bge (label)); - expand_inc (adjusted_op0, plus_constant (op1, -1)); - emit_label (label); - } - mod_insn_no_good = 1; - } - break; - - case FLOOR_DIV_EXPR: - case FLOOR_MOD_EXPR: - if (log < 0 && ! unsignedp) - { - rtx label = gen_label_rtx (); - if (! can_clobber_op0) - { - adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target, - compute_mode); - /* Copy op0 to a reg, since emit_cmp_insn will call emit_queue - which will screw up mem refs for autoincrements. */ - op0 = force_reg (compute_mode, op0); - } - emit_cmp_insn (adjusted_op0, const0_rtx, GE, - NULL_RTX, compute_mode, 0, 0); - emit_jump_insn (gen_bge (label)); - expand_dec (adjusted_op0, op1); - expand_inc (adjusted_op0, const1_rtx); - emit_label (label); - mod_insn_no_good = 1; - } - break; - - case CEIL_DIV_EXPR: - case CEIL_MOD_EXPR: - if (! can_clobber_op0) - { - adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target, - compute_mode); - /* Copy op0 to a reg, since emit_cmp_insn will call emit_queue - which will screw up mem refs for autoincrements. */ - op0 = force_reg (compute_mode, op0); - } - if (log < 0) - { - rtx label = 0; - if (! unsignedp) - { - label = gen_label_rtx (); - emit_cmp_insn (adjusted_op0, const0_rtx, LE, - NULL_RTX, compute_mode, 0, 0); - emit_jump_insn (gen_ble (label)); - } - expand_inc (adjusted_op0, op1); - expand_dec (adjusted_op0, const1_rtx); - if (! unsignedp) - emit_label (label); - } - else - { - adjusted_op0 = expand_binop (compute_mode, add_optab, - adjusted_op0, plus_constant (op1, -1), - NULL_RTX, 0, OPTAB_LIB_WIDEN); - } - mod_insn_no_good = 1; - break; - - case ROUND_DIV_EXPR: - case ROUND_MOD_EXPR: - if (! can_clobber_op0) - { - adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target, - compute_mode); - /* Copy op0 to a reg, since emit_cmp_insn will call emit_queue - which will screw up mem refs for autoincrements. */ - op0 = force_reg (compute_mode, op0); - } - if (log < 0) - { - op1 = expand_shift (RSHIFT_EXPR, compute_mode, op1, - integer_one_node, NULL_RTX, 0); - if (! unsignedp) - { - if (BRANCH_COST >= 2) - { - /* Negate OP1 if OP0 < 0. Do this by computing a temporary - that has all bits equal to the sign bit and exclusive - or-ing it with OP1. */ - rtx temp = gen_reg_rtx (compute_mode); - temp = copy_to_suggested_reg (adjusted_op0, temp, compute_mode); - temp = expand_shift (RSHIFT_EXPR, compute_mode, temp, - build_int_2 (size - 1, 0), - NULL_RTX, 0); - op1 = expand_binop (compute_mode, xor_optab, op1, temp, op1, - unsignedp, OPTAB_LIB_WIDEN); - } - else - { - rtx label = gen_label_rtx (); - emit_cmp_insn (adjusted_op0, const0_rtx, GE, NULL_RTX, - compute_mode, 0, 0); - emit_jump_insn (gen_bge (label)); - expand_unop (compute_mode, neg_optab, op1, op1, 0); - emit_label (label); - } - } - expand_inc (adjusted_op0, op1); - } - else - { - op1 = GEN_INT (((HOST_WIDE_INT) 1 << log) / 2); - expand_inc (adjusted_op0, op1); - } - mod_insn_no_good = 1; - break; - } - - if (rem_flag && !mod_insn_no_good) - { - /* Try to produce the remainder directly */ - if (log >= 0) - result = expand_binop (compute_mode, and_optab, adjusted_op0, - GEN_INT (((HOST_WIDE_INT) 1 << log) - 1), - target, 1, OPTAB_LIB_WIDEN); - else - { - /* See if we can do remainder without a library call. */ - result = sign_expand_binop (mode, umod_optab, smod_optab, - adjusted_op0, op1, target, - unsignedp, OPTAB_WIDEN); - if (result == 0) - { - /* No luck there. Can we do remainder and divide at once - without a library call? */ - result = gen_reg_rtx (compute_mode); - if (! expand_twoval_binop (unsignedp - ? udivmod_optab : sdivmod_optab, - adjusted_op0, op1, - NULL_RTX, result, unsignedp)) - result = 0; - } - } - } - - if (result) - return gen_lowpart (mode, result); - - /* Produce the quotient. */ - if (log >= 0) - result = expand_shift (RSHIFT_EXPR, compute_mode, adjusted_op0, - build_int_2 (log, 0), target, unsignedp); - else if (rem_flag && !mod_insn_no_good) - /* If producing quotient in order to subtract for remainder, - and a remainder subroutine would be ok, - don't use a divide subroutine. */ - result = sign_expand_binop (compute_mode, udiv_optab, sdiv_optab, - adjusted_op0, op1, NULL_RTX, unsignedp, - OPTAB_WIDEN); - else - { - /* Try a quotient insn, but not a library call. */ - result = sign_expand_binop (compute_mode, udiv_optab, sdiv_optab, - adjusted_op0, op1, - rem_flag ? NULL_RTX : target, - unsignedp, OPTAB_WIDEN); - if (result == 0) - { - /* No luck there. Try a quotient-and-remainder insn, - keeping the quotient alone. */ - result = gen_reg_rtx (mode); - if (! expand_twoval_binop (unsignedp ? udivmod_optab : sdivmod_optab, - adjusted_op0, op1, - result, NULL_RTX, unsignedp)) - result = 0; - } - - /* If still no luck, use a library call. */ - if (result == 0) - result = sign_expand_binop (compute_mode, udiv_optab, sdiv_optab, - adjusted_op0, op1, - rem_flag ? NULL_RTX : target, - unsignedp, OPTAB_LIB_WIDEN); - } - - /* If we really want the remainder, get it by subtraction. */ - if (rem_flag) - { - if (result == 0) - /* No divide instruction either. Use library for remainder. */ - result = sign_expand_binop (compute_mode, umod_optab, smod_optab, - op0, op1, target, - unsignedp, OPTAB_LIB_WIDEN); - else - { - /* We divided. Now finish doing X - Y * (X / Y). */ - result = expand_mult (compute_mode, result, op1, target, unsignedp); - if (! result) abort (); - result = expand_binop (compute_mode, sub_optab, op0, - result, target, unsignedp, OPTAB_LIB_WIDEN); - } - } - - if (result == 0) - abort (); - - return gen_lowpart (mode, result); -} - -/* Return a tree node with data type TYPE, describing the value of X. - Usually this is an RTL_EXPR, if there is no obvious better choice. - X may be an expression, however we only support those expressions - generated by loop.c. */ - -tree -make_tree (type, x) - tree type; - rtx x; -{ - tree t; - - switch (GET_CODE (x)) - { - case CONST_INT: - t = build_int_2 (INTVAL (x), - ! TREE_UNSIGNED (type) && INTVAL (x) >= 0 ? 0 : -1); - TREE_TYPE (t) = type; - return t; - - case CONST_DOUBLE: - if (GET_MODE (x) == VOIDmode) - { - t = build_int_2 (CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x)); - TREE_TYPE (t) = type; - } - else - { - REAL_VALUE_TYPE d; - - REAL_VALUE_FROM_CONST_DOUBLE (d, x); - t = build_real (type, d); - } - - return t; - - case PLUS: - return fold (build (PLUS_EXPR, type, make_tree (type, XEXP (x, 0)), - make_tree (type, XEXP (x, 1)))); - - case MINUS: - return fold (build (MINUS_EXPR, type, make_tree (type, XEXP (x, 0)), - make_tree (type, XEXP (x, 1)))); - - case NEG: - return fold (build1 (NEGATE_EXPR, type, make_tree (type, XEXP (x, 0)))); - - case MULT: - return fold (build (MULT_EXPR, type, make_tree (type, XEXP (x, 0)), - make_tree (type, XEXP (x, 1)))); - - case ASHIFT: - return fold (build (LSHIFT_EXPR, type, make_tree (type, XEXP (x, 0)), - make_tree (type, XEXP (x, 1)))); - - case LSHIFTRT: - return fold (convert (type, - build (RSHIFT_EXPR, unsigned_type (type), - make_tree (unsigned_type (type), - XEXP (x, 0)), - make_tree (type, XEXP (x, 1))))); - - case ASHIFTRT: - return fold (convert (type, - build (RSHIFT_EXPR, signed_type (type), - make_tree (signed_type (type), XEXP (x, 0)), - make_tree (type, XEXP (x, 1))))); - - case DIV: - if (TREE_CODE (type) != REAL_TYPE) - t = signed_type (type); - else - t = type; - - return fold (convert (type, - build (TRUNC_DIV_EXPR, t, - make_tree (t, XEXP (x, 0)), - make_tree (t, XEXP (x, 1))))); - case UDIV: - t = unsigned_type (type); - return fold (convert (type, - build (TRUNC_DIV_EXPR, t, - make_tree (t, XEXP (x, 0)), - make_tree (t, XEXP (x, 1))))); - default: - t = make_node (RTL_EXPR); - TREE_TYPE (t) = type; - RTL_EXPR_RTL (t) = x; - /* There are no insns to be output - when this rtl_expr is used. */ - RTL_EXPR_SEQUENCE (t) = 0; - return t; - } -} - -/* Return an rtx representing the value of X * MULT + ADD. - TARGET is a suggestion for where to store the result (an rtx). - MODE is the machine mode for the computation. - X and MULT must have mode MODE. ADD may have a different mode. - So can X (defaults to same as MODE). - UNSIGNEDP is non-zero to do unsigned multiplication. - This may emit insns. */ - -rtx -expand_mult_add (x, target, mult, add, mode, unsignedp) - rtx x, target, mult, add; - enum machine_mode mode; - int unsignedp; -{ - tree type = type_for_mode (mode, unsignedp); - tree add_type = (GET_MODE (add) == VOIDmode - ? type : type_for_mode (GET_MODE (add), unsignedp)); - tree result = fold (build (PLUS_EXPR, type, - fold (build (MULT_EXPR, type, - make_tree (type, x), - make_tree (type, mult))), - make_tree (add_type, add))); - - return expand_expr (result, target, VOIDmode, 0); -} - -/* Compute the logical-and of OP0 and OP1, storing it in TARGET - and returning TARGET. - - If TARGET is 0, a pseudo-register or constant is returned. */ - -rtx -expand_and (op0, op1, target) - rtx op0, op1, target; -{ - enum machine_mode mode = VOIDmode; - rtx tem; - - if (GET_MODE (op0) != VOIDmode) - mode = GET_MODE (op0); - else if (GET_MODE (op1) != VOIDmode) - mode = GET_MODE (op1); - - if (mode != VOIDmode) - tem = expand_binop (mode, and_optab, op0, op1, target, 0, OPTAB_LIB_WIDEN); - else if (GET_CODE (op0) == CONST_INT && GET_CODE (op1) == CONST_INT) - tem = GEN_INT (INTVAL (op0) & INTVAL (op1)); - else - abort (); - - if (target == 0) - target = tem; - else if (tem != target) - emit_move_insn (target, tem); - return target; -} - -/* Emit a store-flags instruction for comparison CODE on OP0 and OP1 - and storing in TARGET. Normally return TARGET. - Return 0 if that cannot be done. - - MODE is the mode to use for OP0 and OP1 should they be CONST_INTs. If - it is VOIDmode, they cannot both be CONST_INT. - - UNSIGNEDP is for the case where we have to widen the operands - to perform the operation. It says to use zero-extension. - - NORMALIZEP is 1 if we should convert the result to be either zero - or one one. Normalize is -1 if we should convert the result to be - either zero or -1. If NORMALIZEP is zero, the result will be left - "raw" out of the scc insn. */ - -rtx -emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep) - rtx target; - enum rtx_code code; - rtx op0, op1; - enum machine_mode mode; - int unsignedp; - int normalizep; -{ - rtx subtarget; - enum insn_code icode; - enum machine_mode compare_mode; - enum machine_mode target_mode = GET_MODE (target); - rtx tem; - rtx last = 0; - rtx pattern, comparison; - - if (mode == VOIDmode) - mode = GET_MODE (op0); - - /* If one operand is constant, make it the second one. Only do this - if the other operand is not constant as well. */ - - if ((CONSTANT_P (op0) && ! CONSTANT_P (op1)) - || (GET_CODE (op0) == CONST_INT && GET_CODE (op1) != CONST_INT)) - { - tem = op0; - op0 = op1; - op1 = tem; - code = swap_condition (code); - } - - /* For some comparisons with 1 and -1, we can convert this to - comparisons with zero. This will often produce more opportunities for - store-flag insns. */ - - switch (code) - { - case LT: - if (op1 == const1_rtx) - op1 = const0_rtx, code = LE; - break; - case LE: - if (op1 == constm1_rtx) - op1 = const0_rtx, code = LT; - break; - case GE: - if (op1 == const1_rtx) - op1 = const0_rtx, code = GT; - break; - case GT: - if (op1 == constm1_rtx) - op1 = const0_rtx, code = GE; - break; - case GEU: - if (op1 == const1_rtx) - op1 = const0_rtx, code = NE; - break; - case LTU: - if (op1 == const1_rtx) - op1 = const0_rtx, code = EQ; - break; - } - - /* From now on, we won't change CODE, so set ICODE now. */ - icode = setcc_gen_code[(int) code]; - - /* If this is A < 0 or A >= 0, we can do this by taking the ones - complement of A (for GE) and shifting the sign bit to the low bit. */ - if (op1 == const0_rtx && (code == LT || code == GE) - && GET_MODE_CLASS (mode) == MODE_INT - && (normalizep || STORE_FLAG_VALUE == 1 - || (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT - && (STORE_FLAG_VALUE - == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))))) - { - subtarget = target; - - /* If the result is to be wider than OP0, it is best to convert it - first. If it is to be narrower, it is *incorrect* to convert it - first. */ - if (GET_MODE_SIZE (target_mode) > GET_MODE_SIZE (mode)) - { - op0 = protect_from_queue (op0, 0); - op0 = convert_to_mode (target_mode, op0, 0); - mode = target_mode; - } - - if (target_mode != mode) - subtarget = 0; - - if (code == GE) - op0 = expand_unop (mode, one_cmpl_optab, op0, subtarget, 0); - - if (normalizep || STORE_FLAG_VALUE == 1) - /* If we are supposed to produce a 0/1 value, we want to do - a logical shift from the sign bit to the low-order bit; for - a -1/0 value, we do an arithmetic shift. */ - op0 = expand_shift (RSHIFT_EXPR, mode, op0, - size_int (GET_MODE_BITSIZE (mode) - 1), - subtarget, normalizep != -1); - - if (mode != target_mode) - op0 = convert_to_mode (target_mode, op0, 0); - - return op0; - } - - if (icode != CODE_FOR_nothing) - { - /* We think we may be able to do this with a scc insn. Emit the - comparison and then the scc insn. - - compare_from_rtx may call emit_queue, which would be deleted below - if the scc insn fails. So call it ourselves before setting LAST. */ - - emit_queue (); - last = get_last_insn (); - - comparison - = compare_from_rtx (op0, op1, code, unsignedp, mode, NULL_RTX, 0); - if (GET_CODE (comparison) == CONST_INT) - return (comparison == const0_rtx ? const0_rtx - : normalizep == 1 ? const1_rtx - : normalizep == -1 ? constm1_rtx - : const_true_rtx); - - /* If the code of COMPARISON doesn't match CODE, something is - wrong; we can no longer be sure that we have the operation. - We could handle this case, but it should not happen. */ - - if (GET_CODE (comparison) != code) - abort (); - - /* Get a reference to the target in the proper mode for this insn. */ - compare_mode = insn_operand_mode[(int) icode][0]; - subtarget = target; - if (preserve_subexpressions_p () - || ! (*insn_operand_predicate[(int) icode][0]) (subtarget, compare_mode)) - subtarget = gen_reg_rtx (compare_mode); - - pattern = GEN_FCN (icode) (subtarget); - if (pattern) - { - emit_insn (pattern); - - /* If we are converting to a wider mode, first convert to - TARGET_MODE, then normalize. This produces better combining - opportunities on machines that have a SIGN_EXTRACT when we are - testing a single bit. This mostly benefits the 68k. - - If STORE_FLAG_VALUE does not have the sign bit set when - interpreted in COMPARE_MODE, we can do this conversion as - unsigned, which is usually more efficient. */ - if (GET_MODE_SIZE (target_mode) > GET_MODE_SIZE (compare_mode)) - { - convert_move (target, subtarget, - (GET_MODE_BITSIZE (compare_mode) - <= HOST_BITS_PER_WIDE_INT) - && 0 == (STORE_FLAG_VALUE - & ((HOST_WIDE_INT) 1 - << (GET_MODE_BITSIZE (compare_mode) -1)))); - op0 = target; - compare_mode = target_mode; - } - else - op0 = subtarget; - - /* If we want to keep subexpressions around, don't reuse our - last target. */ - - if (preserve_subexpressions_p ()) - subtarget = 0; - - /* Now normalize to the proper value in COMPARE_MODE. Sometimes - we don't have to do anything. */ - if (normalizep == 0 || normalizep == STORE_FLAG_VALUE) - ; - else if (normalizep == - STORE_FLAG_VALUE) - op0 = expand_unop (compare_mode, neg_optab, op0, subtarget, 0); - - /* We don't want to use STORE_FLAG_VALUE < 0 below since this - makes it hard to use a value of just the sign bit due to - ANSI integer constant typing rules. */ - else if (GET_MODE_BITSIZE (compare_mode) <= HOST_BITS_PER_WIDE_INT - && (STORE_FLAG_VALUE - & ((HOST_WIDE_INT) 1 - << (GET_MODE_BITSIZE (compare_mode) - 1)))) - op0 = expand_shift (RSHIFT_EXPR, compare_mode, op0, - size_int (GET_MODE_BITSIZE (compare_mode) - 1), - subtarget, normalizep == 1); - else if (STORE_FLAG_VALUE & 1) - { - op0 = expand_and (op0, const1_rtx, subtarget); - if (normalizep == -1) - op0 = expand_unop (compare_mode, neg_optab, op0, op0, 0); - } - else - abort (); - - /* If we were converting to a smaller mode, do the - conversion now. */ - if (target_mode != compare_mode) - { - convert_move (target, op0, 0); - return target; - } - else - return op0; - } - } - - if (last) - delete_insns_since (last); - - subtarget = target_mode == mode ? target : 0; - - /* If we reached here, we can't do this with a scc insn. However, there - are some comparisons that can be done directly. For example, if - this is an equality comparison of integers, we can try to exclusive-or - (or subtract) the two operands and use a recursive call to try the - comparison with zero. Don't do any of these cases if branches are - very cheap. */ - - if (BRANCH_COST > 0 - && GET_MODE_CLASS (mode) == MODE_INT && (code == EQ || code == NE) - && op1 != const0_rtx) - { - tem = expand_binop (mode, xor_optab, op0, op1, subtarget, 1, - OPTAB_WIDEN); - - if (tem == 0) - tem = expand_binop (mode, sub_optab, op0, op1, subtarget, 1, - OPTAB_WIDEN); - if (tem != 0) - tem = emit_store_flag (target, code, tem, const0_rtx, - mode, unsignedp, normalizep); - if (tem == 0) - delete_insns_since (last); - return tem; - } - - /* Some other cases we can do are EQ, NE, LE, and GT comparisons with - the constant zero. Reject all other comparisons at this point. Only - do LE and GT if branches are expensive since they are expensive on - 2-operand machines. */ - - if (BRANCH_COST == 0 - || GET_MODE_CLASS (mode) != MODE_INT || op1 != const0_rtx - || (code != EQ && code != NE - && (BRANCH_COST <= 1 || (code != LE && code != GT)))) - return 0; - - /* See what we need to return. We can only return a 1, -1, or the - sign bit. */ - - if (normalizep == 0) - { - if (STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1) - normalizep = STORE_FLAG_VALUE; - - else if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT - && (STORE_FLAG_VALUE - == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))) - ; - else - return 0; - } - - /* Try to put the result of the comparison in the sign bit. Assume we can't - do the necessary operation below. */ - - tem = 0; - - /* To see if A <= 0, compute (A | (A - 1)). A <= 0 iff that result has - the sign bit set. */ - - if (code == LE) - { - /* This is destructive, so SUBTARGET can't be OP0. */ - if (rtx_equal_p (subtarget, op0)) - subtarget = 0; - - tem = expand_binop (mode, sub_optab, op0, const1_rtx, subtarget, 0, - OPTAB_WIDEN); - if (tem) - tem = expand_binop (mode, ior_optab, op0, tem, subtarget, 0, - OPTAB_WIDEN); - } - - /* To see if A > 0, compute (((signed) A) << BITS) - A, where BITS is the - number of bits in the mode of OP0, minus one. */ - - if (code == GT) - { - if (rtx_equal_p (subtarget, op0)) - subtarget = 0; - - tem = expand_shift (RSHIFT_EXPR, mode, op0, - size_int (GET_MODE_BITSIZE (mode) - 1), - subtarget, 0); - tem = expand_binop (mode, sub_optab, tem, op0, subtarget, 0, - OPTAB_WIDEN); - } - - if (code == EQ || code == NE) - { - /* For EQ or NE, one way to do the comparison is to apply an operation - that converts the operand into a positive number if it is non-zero - or zero if it was originally zero. Then, for EQ, we subtract 1 and - for NE we negate. This puts the result in the sign bit. Then we - normalize with a shift, if needed. - - Two operations that can do the above actions are ABS and FFS, so try - them. If that doesn't work, and MODE is smaller than a full word, - we can use zero-extension to the wider mode (an unsigned conversion) - as the operation. */ - - if (abs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) - tem = expand_unop (mode, abs_optab, op0, subtarget, 1); - else if (ffs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) - tem = expand_unop (mode, ffs_optab, op0, subtarget, 1); - else if (GET_MODE_SIZE (mode) < UNITS_PER_WORD) - { - mode = word_mode; - op0 = protect_from_queue (op0, 0); - tem = convert_to_mode (mode, op0, 1); - } - - if (tem != 0) - { - if (code == EQ) - tem = expand_binop (mode, sub_optab, tem, const1_rtx, subtarget, - 0, OPTAB_WIDEN); - else - tem = expand_unop (mode, neg_optab, tem, subtarget, 0); - } - - /* If we couldn't do it that way, for NE we can "or" the two's complement - of the value with itself. For EQ, we take the one's complement of - that "or", which is an extra insn, so we only handle EQ if branches - are expensive. */ - - if (tem == 0 && (code == NE || BRANCH_COST > 1)) - { - if (rtx_equal_p (subtarget, op0)) - subtarget = 0; - - tem = expand_unop (mode, neg_optab, op0, subtarget, 0); - tem = expand_binop (mode, ior_optab, tem, op0, subtarget, 0, - OPTAB_WIDEN); - - if (tem && code == EQ) - tem = expand_unop (mode, one_cmpl_optab, tem, subtarget, 0); - } - } - - if (tem && normalizep) - tem = expand_shift (RSHIFT_EXPR, mode, tem, - size_int (GET_MODE_BITSIZE (mode) - 1), - tem, normalizep == 1); - - if (tem && GET_MODE (tem) != target_mode) - { - convert_move (target, tem, 0); - tem = target; - } - - if (tem == 0) - delete_insns_since (last); - - return tem; -} diff --git a/gnu/usr.bin/cc/common/expr.c b/gnu/usr.bin/cc/common/expr.c deleted file mode 100644 index 08b2078cb5..0000000000 --- a/gnu/usr.bin/cc/common/expr.c +++ /dev/null @@ -1,7994 +0,0 @@ -/* Convert tree expression to rtl instructions, for GNU compiler. - Copyright (C) 1988, 1992, 1993 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "config.h" -#include "rtl.h" -#include "tree.h" -#include "flags.h" -#include "function.h" -#include "insn-flags.h" -#include "insn-codes.h" -#include "expr.h" -#include "insn-config.h" -#include "recog.h" -#include "output.h" -#include "typeclass.h" - -#define CEIL(x,y) (((x) + (y) - 1) / (y)) - -/* Decide whether a function's arguments should be processed - from first to last or from last to first. - - They should if the stack and args grow in opposite directions, but - only if we have push insns. */ - -#ifdef PUSH_ROUNDING - -#if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNARD) -#define PUSH_ARGS_REVERSED /* If it's last to first */ -#endif - -#endif - -#ifndef STACK_PUSH_CODE -#ifdef STACK_GROWS_DOWNWARD -#define STACK_PUSH_CODE PRE_DEC -#else -#define STACK_PUSH_CODE PRE_INC -#endif -#endif - -/* Like STACK_BOUNDARY but in units of bytes, not bits. */ -#define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT) - -/* If this is nonzero, we do not bother generating VOLATILE - around volatile memory references, and we are willing to - output indirect addresses. If cse is to follow, we reject - indirect addresses so a useful potential cse is generated; - if it is used only once, instruction combination will produce - the same indirect address eventually. */ -int cse_not_expected; - -/* Nonzero to generate code for all the subroutines within an - expression before generating the upper levels of the expression. - Nowadays this is never zero. */ -int do_preexpand_calls = 1; - -/* Number of units that we should eventually pop off the stack. - These are the arguments to function calls that have already returned. */ -int pending_stack_adjust; - -/* Nonzero means stack pops must not be deferred, and deferred stack - pops must not be output. It is nonzero inside a function call, - inside a conditional expression, inside a statement expression, - and in other cases as well. */ -int inhibit_defer_pop; - -/* A list of all cleanups which belong to the arguments of - function calls being expanded by expand_call. */ -tree cleanups_this_call; - -/* Nonzero means __builtin_saveregs has already been done in this function. - The value is the pseudoreg containing the value __builtin_saveregs - returned. */ -static rtx saveregs_value; - -/* Similarly for __builtin_apply_args. */ -static rtx apply_args_value; - -/* This structure is used by move_by_pieces to describe the move to - be performed. */ - -struct move_by_pieces -{ - rtx to; - rtx to_addr; - int autinc_to; - int explicit_inc_to; - rtx from; - rtx from_addr; - int autinc_from; - int explicit_inc_from; - int len; - int offset; - int reverse; -}; - -static rtx enqueue_insn PROTO((rtx, rtx)); -static int queued_subexp_p PROTO((rtx)); -static void init_queue PROTO((void)); -static void move_by_pieces PROTO((rtx, rtx, int, int)); -static int move_by_pieces_ninsns PROTO((unsigned int, int)); -static void move_by_pieces_1 PROTO((rtx (*) (), enum machine_mode, - struct move_by_pieces *)); -static void group_insns PROTO((rtx)); -static void store_constructor PROTO((tree, rtx)); -static rtx store_field PROTO((rtx, int, int, enum machine_mode, tree, - enum machine_mode, int, int, int)); -static tree save_noncopied_parts PROTO((tree, tree)); -static tree init_noncopied_parts PROTO((tree, tree)); -static int safe_from_p PROTO((rtx, tree)); -static int fixed_type_p PROTO((tree)); -static int get_pointer_alignment PROTO((tree, unsigned)); -static tree string_constant PROTO((tree, tree *)); -static tree c_strlen PROTO((tree)); -static rtx expand_builtin PROTO((tree, rtx, rtx, enum machine_mode, int)); -static int apply_args_size PROTO((void)); -static int apply_result_size PROTO((void)); -static rtx result_vector PROTO((int, rtx)); -static rtx expand_builtin_apply_args PROTO((void)); -static rtx expand_builtin_apply PROTO((rtx, rtx, rtx)); -static void expand_builtin_return PROTO((rtx)); -static rtx expand_increment PROTO((tree, int)); -static void preexpand_calls PROTO((tree)); -static void do_jump_by_parts_greater PROTO((tree, int, rtx, rtx)); -static void do_jump_by_parts_greater_rtx PROTO((enum machine_mode, int, rtx, rtx, rtx, rtx)); -static void do_jump_by_parts_equality PROTO((tree, rtx, rtx)); -static void do_jump_by_parts_equality_rtx PROTO((rtx, rtx, rtx)); -static void do_jump_for_compare PROTO((rtx, rtx, rtx)); -static rtx compare PROTO((tree, enum rtx_code, enum rtx_code)); -static rtx do_store_flag PROTO((tree, rtx, enum machine_mode, int)); - -/* Record for each mode whether we can move a register directly to or - from an object of that mode in memory. If we can't, we won't try - to use that mode directly when accessing a field of that mode. */ - -static char direct_load[NUM_MACHINE_MODES]; -static char direct_store[NUM_MACHINE_MODES]; - -/* MOVE_RATIO is the number of move instructions that is better than - a block move. */ - -#ifndef MOVE_RATIO -#if defined (HAVE_movstrqi) || defined (HAVE_movstrhi) || defined (HAVE_movstrsi) || defined (HAVE_movstrdi) || defined (HAVE_movstrti) -#define MOVE_RATIO 2 -#else -/* A value of around 6 would minimize code size; infinity would minimize - execution time. */ -#define MOVE_RATIO 15 -#endif -#endif - -/* This array records the insn_code of insns to perform block moves. */ -enum insn_code movstr_optab[NUM_MACHINE_MODES]; - -/* SLOW_UNALIGNED_ACCESS is non-zero if unaligned accesses are very slow. */ - -#ifndef SLOW_UNALIGNED_ACCESS -#define SLOW_UNALIGNED_ACCESS 0 -#endif - -/* Register mappings for target machines without register windows. */ -#ifndef INCOMING_REGNO -#define INCOMING_REGNO(OUT) (OUT) -#endif -#ifndef OUTGOING_REGNO -#define OUTGOING_REGNO(IN) (IN) -#endif - -/* This is run once per compilation to set up which modes can be used - directly in memory and to initialize the block move optab. */ - -void -init_expr_once () -{ - rtx insn, pat; - enum machine_mode mode; - /* Try indexing by frame ptr and try by stack ptr. - It is known that on the Convex the stack ptr isn't a valid index. - With luck, one or the other is valid on any machine. */ - rtx mem = gen_rtx (MEM, VOIDmode, stack_pointer_rtx); - rtx mem1 = gen_rtx (MEM, VOIDmode, frame_pointer_rtx); - - start_sequence (); - insn = emit_insn (gen_rtx (SET, 0, 0)); - pat = PATTERN (insn); - - for (mode = VOIDmode; (int) mode < NUM_MACHINE_MODES; - mode = (enum machine_mode) ((int) mode + 1)) - { - int regno; - rtx reg; - int num_clobbers; - - direct_load[(int) mode] = direct_store[(int) mode] = 0; - PUT_MODE (mem, mode); - PUT_MODE (mem1, mode); - - /* See if there is some register that can be used in this mode and - directly loaded or stored from memory. */ - - if (mode != VOIDmode && mode != BLKmode) - for (regno = 0; regno < FIRST_PSEUDO_REGISTER - && (direct_load[(int) mode] == 0 || direct_store[(int) mode] == 0); - regno++) - { - if (! HARD_REGNO_MODE_OK (regno, mode)) - continue; - - reg = gen_rtx (REG, mode, regno); - - SET_SRC (pat) = mem; - SET_DEST (pat) = reg; - if (recog (pat, insn, &num_clobbers) >= 0) - direct_load[(int) mode] = 1; - - SET_SRC (pat) = mem1; - SET_DEST (pat) = reg; - if (recog (pat, insn, &num_clobbers) >= 0) - direct_load[(int) mode] = 1; - - SET_SRC (pat) = reg; - SET_DEST (pat) = mem; - if (recog (pat, insn, &num_clobbers) >= 0) - direct_store[(int) mode] = 1; - - SET_SRC (pat) = reg; - SET_DEST (pat) = mem1; - if (recog (pat, insn, &num_clobbers) >= 0) - direct_store[(int) mode] = 1; - } - } - - end_sequence (); -} - -/* This is run at the start of compiling a function. */ - -void -init_expr () -{ - init_queue (); - - pending_stack_adjust = 0; - inhibit_defer_pop = 0; - cleanups_this_call = 0; - saveregs_value = 0; - apply_args_value = 0; - forced_labels = 0; -} - -/* Save all variables describing the current status into the structure *P. - This is used before starting a nested function. */ - -void -save_expr_status (p) - struct function *p; -{ - /* Instead of saving the postincrement queue, empty it. */ - emit_queue (); - - p->pending_stack_adjust = pending_stack_adjust; - p->inhibit_defer_pop = inhibit_defer_pop; - p->cleanups_this_call = cleanups_this_call; - p->saveregs_value = saveregs_value; - p->apply_args_value = apply_args_value; - p->forced_labels = forced_labels; - - pending_stack_adjust = 0; - inhibit_defer_pop = 0; - cleanups_this_call = 0; - saveregs_value = 0; - apply_args_value = 0; - forced_labels = 0; -} - -/* Restore all variables describing the current status from the structure *P. - This is used after a nested function. */ - -void -restore_expr_status (p) - struct function *p; -{ - pending_stack_adjust = p->pending_stack_adjust; - inhibit_defer_pop = p->inhibit_defer_pop; - cleanups_this_call = p->cleanups_this_call; - saveregs_value = p->saveregs_value; - apply_args_value = p->apply_args_value; - forced_labels = p->forced_labels; -} - -/* Manage the queue of increment instructions to be output - for POSTINCREMENT_EXPR expressions, etc. */ - -static rtx pending_chain; - -/* Queue up to increment (or change) VAR later. BODY says how: - BODY should be the same thing you would pass to emit_insn - to increment right away. It will go to emit_insn later on. - - The value is a QUEUED expression to be used in place of VAR - where you want to guarantee the pre-incrementation value of VAR. */ - -static rtx -enqueue_insn (var, body) - rtx var, body; -{ - pending_chain = gen_rtx (QUEUED, GET_MODE (var), - var, NULL_RTX, NULL_RTX, body, pending_chain); - return pending_chain; -} - -/* Use protect_from_queue to convert a QUEUED expression - into something that you can put immediately into an instruction. - If the queued incrementation has not happened yet, - protect_from_queue returns the variable itself. - If the incrementation has happened, protect_from_queue returns a temp - that contains a copy of the old value of the variable. - - Any time an rtx which might possibly be a QUEUED is to be put - into an instruction, it must be passed through protect_from_queue first. - QUEUED expressions are not meaningful in instructions. - - Do not pass a value through protect_from_queue and then hold - on to it for a while before putting it in an instruction! - If the queue is flushed in between, incorrect code will result. */ - -rtx -protect_from_queue (x, modify) - register rtx x; - int modify; -{ - register RTX_CODE code = GET_CODE (x); - -#if 0 /* A QUEUED can hang around after the queue is forced out. */ - /* Shortcut for most common case. */ - if (pending_chain == 0) - return x; -#endif - - if (code != QUEUED) - { - /* A special hack for read access to (MEM (QUEUED ...)) - to facilitate use of autoincrement. - Make a copy of the contents of the memory location - rather than a copy of the address, but not - if the value is of mode BLKmode. */ - if (code == MEM && GET_MODE (x) != BLKmode - && GET_CODE (XEXP (x, 0)) == QUEUED && !modify) - { - register rtx y = XEXP (x, 0); - XEXP (x, 0) = QUEUED_VAR (y); - if (QUEUED_INSN (y)) - { - register rtx temp = gen_reg_rtx (GET_MODE (x)); - emit_insn_before (gen_move_insn (temp, x), - QUEUED_INSN (y)); - return temp; - } - return x; - } - /* Otherwise, recursively protect the subexpressions of all - the kinds of rtx's that can contain a QUEUED. */ - if (code == MEM) - XEXP (x, 0) = protect_from_queue (XEXP (x, 0), 0); - else if (code == PLUS || code == MULT) - { - XEXP (x, 0) = protect_from_queue (XEXP (x, 0), 0); - XEXP (x, 1) = protect_from_queue (XEXP (x, 1), 0); - } - return x; - } - /* If the increment has not happened, use the variable itself. */ - if (QUEUED_INSN (x) == 0) - return QUEUED_VAR (x); - /* If the increment has happened and a pre-increment copy exists, - use that copy. */ - if (QUEUED_COPY (x) != 0) - return QUEUED_COPY (x); - /* The increment has happened but we haven't set up a pre-increment copy. - Set one up now, and use it. */ - QUEUED_COPY (x) = gen_reg_rtx (GET_MODE (QUEUED_VAR (x))); - emit_insn_before (gen_move_insn (QUEUED_COPY (x), QUEUED_VAR (x)), - QUEUED_INSN (x)); - return QUEUED_COPY (x); -} - -/* Return nonzero if X contains a QUEUED expression: - if it contains anything that will be altered by a queued increment. - We handle only combinations of MEM, PLUS, MINUS and MULT operators - since memory addresses generally contain only those. */ - -static int -queued_subexp_p (x) - rtx x; -{ - register enum rtx_code code = GET_CODE (x); - switch (code) - { - case QUEUED: - return 1; - case MEM: - return queued_subexp_p (XEXP (x, 0)); - case MULT: - case PLUS: - case MINUS: - return queued_subexp_p (XEXP (x, 0)) - || queued_subexp_p (XEXP (x, 1)); - } - return 0; -} - -/* Perform all the pending incrementations. */ - -void -emit_queue () -{ - register rtx p; - while (p = pending_chain) - { - QUEUED_INSN (p) = emit_insn (QUEUED_BODY (p)); - pending_chain = QUEUED_NEXT (p); - } -} - -static void -init_queue () -{ - if (pending_chain) - abort (); -} - -/* Copy data from FROM to TO, where the machine modes are not the same. - Both modes may be integer, or both may be floating. - UNSIGNEDP should be nonzero if FROM is an unsigned type. - This causes zero-extension instead of sign-extension. */ - -void -convert_move (to, from, unsignedp) - register rtx to, from; - int unsignedp; -{ - enum machine_mode to_mode = GET_MODE (to); - enum machine_mode from_mode = GET_MODE (from); - int to_real = GET_MODE_CLASS (to_mode) == MODE_FLOAT; - int from_real = GET_MODE_CLASS (from_mode) == MODE_FLOAT; - enum insn_code code; - rtx libcall; - - /* rtx code for making an equivalent value. */ - enum rtx_code equiv_code = (unsignedp ? ZERO_EXTEND : SIGN_EXTEND); - - to = protect_from_queue (to, 1); - from = protect_from_queue (from, 0); - - if (to_real != from_real) - abort (); - - /* If FROM is a SUBREG that indicates that we have already done at least - the required extension, strip it. We don't handle such SUBREGs as - TO here. */ - - if (GET_CODE (from) == SUBREG && SUBREG_PROMOTED_VAR_P (from) - && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (from))) - >= GET_MODE_SIZE (to_mode)) - && SUBREG_PROMOTED_UNSIGNED_P (from) == unsignedp) - from = gen_lowpart (to_mode, from), from_mode = to_mode; - - if (GET_CODE (to) == SUBREG && SUBREG_PROMOTED_VAR_P (to)) - abort (); - - if (to_mode == from_mode - || (from_mode == VOIDmode && CONSTANT_P (from))) - { - emit_move_insn (to, from); - return; - } - - if (to_real) - { -#ifdef HAVE_extendqfhf2 - if (HAVE_extendqfsf2 && from_mode == QFmode && to_mode == HFmode) - { - emit_unop_insn (CODE_FOR_extendqfsf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_extendqfsf2 - if (HAVE_extendqfsf2 && from_mode == QFmode && to_mode == SFmode) - { - emit_unop_insn (CODE_FOR_extendqfsf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_extendqfdf2 - if (HAVE_extendqfdf2 && from_mode == QFmode && to_mode == DFmode) - { - emit_unop_insn (CODE_FOR_extendqfdf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_extendqfxf2 - if (HAVE_extendqfxf2 && from_mode == QFmode && to_mode == XFmode) - { - emit_unop_insn (CODE_FOR_extendqfxf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_extendqftf2 - if (HAVE_extendqftf2 && from_mode == QFmode && to_mode == TFmode) - { - emit_unop_insn (CODE_FOR_extendqftf2, to, from, UNKNOWN); - return; - } -#endif - -#ifdef HAVE_extendhfsf2 - if (HAVE_extendhfsf2 && from_mode == HFmode && to_mode == SFmode) - { - emit_unop_insn (CODE_FOR_extendhfsf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_extendhfdf2 - if (HAVE_extendhfdf2 && from_mode == HFmode && to_mode == DFmode) - { - emit_unop_insn (CODE_FOR_extendhfdf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_extendhfxf2 - if (HAVE_extendhfxf2 && from_mode == HFmode && to_mode == XFmode) - { - emit_unop_insn (CODE_FOR_extendhfxf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_extendhftf2 - if (HAVE_extendhftf2 && from_mode == HFmode && to_mode == TFmode) - { - emit_unop_insn (CODE_FOR_extendhftf2, to, from, UNKNOWN); - return; - } -#endif - -#ifdef HAVE_extendsfdf2 - if (HAVE_extendsfdf2 && from_mode == SFmode && to_mode == DFmode) - { - emit_unop_insn (CODE_FOR_extendsfdf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_extendsfxf2 - if (HAVE_extendsfxf2 && from_mode == SFmode && to_mode == XFmode) - { - emit_unop_insn (CODE_FOR_extendsfxf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_extendsftf2 - if (HAVE_extendsftf2 && from_mode == SFmode && to_mode == TFmode) - { - emit_unop_insn (CODE_FOR_extendsftf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_extenddfxf2 - if (HAVE_extenddfxf2 && from_mode == DFmode && to_mode == XFmode) - { - emit_unop_insn (CODE_FOR_extenddfxf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_extenddftf2 - if (HAVE_extenddftf2 && from_mode == DFmode && to_mode == TFmode) - { - emit_unop_insn (CODE_FOR_extenddftf2, to, from, UNKNOWN); - return; - } -#endif - -#ifdef HAVE_trunchfqf2 - if (HAVE_trunchfqf2 && from_mode == HFmode && to_mode == QFmode) - { - emit_unop_insn (CODE_FOR_trunchfqf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_truncsfqf2 - if (HAVE_truncsfqf2 && from_mode == SFmode && to_mode == QFmode) - { - emit_unop_insn (CODE_FOR_truncsfqf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_truncdfqf2 - if (HAVE_truncdfqf2 && from_mode == DFmode && to_mode == QFmode) - { - emit_unop_insn (CODE_FOR_truncdfqf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_truncxfqf2 - if (HAVE_truncxfqf2 && from_mode == XFmode && to_mode == QFmode) - { - emit_unop_insn (CODE_FOR_truncxfqf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_trunctfqf2 - if (HAVE_trunctfqf2 && from_mode == TFmode && to_mode == QFmode) - { - emit_unop_insn (CODE_FOR_trunctfqf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_truncsfhf2 - if (HAVE_truncsfhf2 && from_mode == SFmode && to_mode == HFmode) - { - emit_unop_insn (CODE_FOR_truncsfhf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_truncdfhf2 - if (HAVE_truncdfhf2 && from_mode == DFmode && to_mode == HFmode) - { - emit_unop_insn (CODE_FOR_truncdfhf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_truncxfhf2 - if (HAVE_truncxfhf2 && from_mode == XFmode && to_mode == HFmode) - { - emit_unop_insn (CODE_FOR_truncxfhf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_trunctfhf2 - if (HAVE_trunctfhf2 && from_mode == TFmode && to_mode == HFmode) - { - emit_unop_insn (CODE_FOR_trunctfhf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_truncdfsf2 - if (HAVE_truncdfsf2 && from_mode == DFmode && to_mode == SFmode) - { - emit_unop_insn (CODE_FOR_truncdfsf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_truncxfsf2 - if (HAVE_truncxfsf2 && from_mode == XFmode && to_mode == SFmode) - { - emit_unop_insn (CODE_FOR_truncxfsf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_trunctfsf2 - if (HAVE_trunctfsf2 && from_mode == TFmode && to_mode == SFmode) - { - emit_unop_insn (CODE_FOR_trunctfsf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_truncxfdf2 - if (HAVE_truncxfdf2 && from_mode == XFmode && to_mode == DFmode) - { - emit_unop_insn (CODE_FOR_truncxfdf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_trunctfdf2 - if (HAVE_trunctfdf2 && from_mode == TFmode && to_mode == DFmode) - { - emit_unop_insn (CODE_FOR_trunctfdf2, to, from, UNKNOWN); - return; - } -#endif - - libcall = (rtx) 0; - switch (from_mode) - { - case SFmode: - switch (to_mode) - { - case DFmode: - libcall = extendsfdf2_libfunc; - break; - - case XFmode: - libcall = extendsfxf2_libfunc; - break; - - case TFmode: - libcall = extendsftf2_libfunc; - break; - } - break; - - case DFmode: - switch (to_mode) - { - case SFmode: - libcall = truncdfsf2_libfunc; - break; - - case XFmode: - libcall = extenddfxf2_libfunc; - break; - - case TFmode: - libcall = extenddftf2_libfunc; - break; - } - break; - - case XFmode: - switch (to_mode) - { - case SFmode: - libcall = truncxfsf2_libfunc; - break; - - case DFmode: - libcall = truncxfdf2_libfunc; - break; - } - break; - - case TFmode: - switch (to_mode) - { - case SFmode: - libcall = trunctfsf2_libfunc; - break; - - case DFmode: - libcall = trunctfdf2_libfunc; - break; - } - break; - } - - if (libcall == (rtx) 0) - /* This conversion is not implemented yet. */ - abort (); - - emit_library_call (libcall, 1, to_mode, 1, from, from_mode); - emit_move_insn (to, hard_libcall_value (to_mode)); - return; - } - - /* Now both modes are integers. */ - - /* Handle expanding beyond a word. */ - if (GET_MODE_BITSIZE (from_mode) < GET_MODE_BITSIZE (to_mode) - && GET_MODE_BITSIZE (to_mode) > BITS_PER_WORD) - { - rtx insns; - rtx lowpart; - rtx fill_value; - rtx lowfrom; - int i; - enum machine_mode lowpart_mode; - int nwords = CEIL (GET_MODE_SIZE (to_mode), UNITS_PER_WORD); - - /* Try converting directly if the insn is supported. */ - if ((code = can_extend_p (to_mode, from_mode, unsignedp)) - != CODE_FOR_nothing) - { - /* If FROM is a SUBREG, put it into a register. Do this - so that we always generate the same set of insns for - better cse'ing; if an intermediate assignment occurred, - we won't be doing the operation directly on the SUBREG. */ - if (optimize > 0 && GET_CODE (from) == SUBREG) - from = force_reg (from_mode, from); - emit_unop_insn (code, to, from, equiv_code); - return; - } - /* Next, try converting via full word. */ - else if (GET_MODE_BITSIZE (from_mode) < BITS_PER_WORD - && ((code = can_extend_p (to_mode, word_mode, unsignedp)) - != CODE_FOR_nothing)) - { - convert_move (gen_lowpart (word_mode, to), from, unsignedp); - emit_unop_insn (code, to, - gen_lowpart (word_mode, to), equiv_code); - return; - } - - /* No special multiword conversion insn; do it by hand. */ - start_sequence (); - - /* Get a copy of FROM widened to a word, if necessary. */ - if (GET_MODE_BITSIZE (from_mode) < BITS_PER_WORD) - lowpart_mode = word_mode; - else - lowpart_mode = from_mode; - - lowfrom = convert_to_mode (lowpart_mode, from, unsignedp); - - lowpart = gen_lowpart (lowpart_mode, to); - emit_move_insn (lowpart, lowfrom); - - /* Compute the value to put in each remaining word. */ - if (unsignedp) - fill_value = const0_rtx; - else - { -#ifdef HAVE_slt - if (HAVE_slt - && insn_operand_mode[(int) CODE_FOR_slt][0] == word_mode - && STORE_FLAG_VALUE == -1) - { - emit_cmp_insn (lowfrom, const0_rtx, NE, NULL_RTX, - lowpart_mode, 0, 0); - fill_value = gen_reg_rtx (word_mode); - emit_insn (gen_slt (fill_value)); - } - else -#endif - { - fill_value - = expand_shift (RSHIFT_EXPR, lowpart_mode, lowfrom, - size_int (GET_MODE_BITSIZE (lowpart_mode) - 1), - NULL_RTX, 0); - fill_value = convert_to_mode (word_mode, fill_value, 1); - } - } - - /* Fill the remaining words. */ - for (i = GET_MODE_SIZE (lowpart_mode) / UNITS_PER_WORD; i < nwords; i++) - { - int index = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i); - rtx subword = operand_subword (to, index, 1, to_mode); - - if (subword == 0) - abort (); - - if (fill_value != subword) - emit_move_insn (subword, fill_value); - } - - insns = get_insns (); - end_sequence (); - - emit_no_conflict_block (insns, to, from, NULL_RTX, - gen_rtx (equiv_code, to_mode, copy_rtx (from))); - return; - } - - /* Truncating multi-word to a word or less. */ - if (GET_MODE_BITSIZE (from_mode) > BITS_PER_WORD - && GET_MODE_BITSIZE (to_mode) <= BITS_PER_WORD) - { - convert_move (to, gen_lowpart (word_mode, from), 0); - return; - } - - /* Handle pointer conversion */ /* SPEE 900220 */ - if (to_mode == PSImode) - { - if (from_mode != SImode) - from = convert_to_mode (SImode, from, unsignedp); - -#ifdef HAVE_truncsipsi - if (HAVE_truncsipsi) - { - emit_unop_insn (CODE_FOR_truncsipsi, to, from, UNKNOWN); - return; - } -#endif /* HAVE_truncsipsi */ - abort (); - } - - if (from_mode == PSImode) - { - if (to_mode != SImode) - { - from = convert_to_mode (SImode, from, unsignedp); - from_mode = SImode; - } - else - { -#ifdef HAVE_extendpsisi - if (HAVE_extendpsisi) - { - emit_unop_insn (CODE_FOR_extendpsisi, to, from, UNKNOWN); - return; - } -#endif /* HAVE_extendpsisi */ - abort (); - } - } - - /* Now follow all the conversions between integers - no more than a word long. */ - - /* For truncation, usually we can just refer to FROM in a narrower mode. */ - if (GET_MODE_BITSIZE (to_mode) < GET_MODE_BITSIZE (from_mode) - && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (to_mode), - GET_MODE_BITSIZE (from_mode))) - { - if (!((GET_CODE (from) == MEM - && ! MEM_VOLATILE_P (from) - && direct_load[(int) to_mode] - && ! mode_dependent_address_p (XEXP (from, 0))) - || GET_CODE (from) == REG - || GET_CODE (from) == SUBREG)) - from = force_reg (from_mode, from); - emit_move_insn (to, gen_lowpart (to_mode, from)); - return; - } - - /* Handle extension. */ - if (GET_MODE_BITSIZE (to_mode) > GET_MODE_BITSIZE (from_mode)) - { - /* Convert directly if that works. */ - if ((code = can_extend_p (to_mode, from_mode, unsignedp)) - != CODE_FOR_nothing) - { - /* If FROM is a SUBREG, put it into a register. Do this - so that we always generate the same set of insns for - better cse'ing; if an intermediate assignment occurred, - we won't be doing the operation directly on the SUBREG. */ - if (optimize > 0 && GET_CODE (from) == SUBREG) - from = force_reg (from_mode, from); - emit_unop_insn (code, to, from, equiv_code); - return; - } - else - { - enum machine_mode intermediate; - - /* Search for a mode to convert via. */ - for (intermediate = from_mode; intermediate != VOIDmode; - intermediate = GET_MODE_WIDER_MODE (intermediate)) - if ((can_extend_p (to_mode, intermediate, unsignedp) - != CODE_FOR_nothing) - && (can_extend_p (intermediate, from_mode, unsignedp) - != CODE_FOR_nothing)) - { - convert_move (to, convert_to_mode (intermediate, from, - unsignedp), unsignedp); - return; - } - - /* No suitable intermediate mode. */ - abort (); - } - } - - /* Support special truncate insns for certain modes. */ - - if (from_mode == DImode && to_mode == SImode) - { -#ifdef HAVE_truncdisi2 - if (HAVE_truncdisi2) - { - emit_unop_insn (CODE_FOR_truncdisi2, to, from, UNKNOWN); - return; - } -#endif - convert_move (to, force_reg (from_mode, from), unsignedp); - return; - } - - if (from_mode == DImode && to_mode == HImode) - { -#ifdef HAVE_truncdihi2 - if (HAVE_truncdihi2) - { - emit_unop_insn (CODE_FOR_truncdihi2, to, from, UNKNOWN); - return; - } -#endif - convert_move (to, force_reg (from_mode, from), unsignedp); - return; - } - - if (from_mode == DImode && to_mode == QImode) - { -#ifdef HAVE_truncdiqi2 - if (HAVE_truncdiqi2) - { - emit_unop_insn (CODE_FOR_truncdiqi2, to, from, UNKNOWN); - return; - } -#endif - convert_move (to, force_reg (from_mode, from), unsignedp); - return; - } - - if (from_mode == SImode && to_mode == HImode) - { -#ifdef HAVE_truncsihi2 - if (HAVE_truncsihi2) - { - emit_unop_insn (CODE_FOR_truncsihi2, to, from, UNKNOWN); - return; - } -#endif - convert_move (to, force_reg (from_mode, from), unsignedp); - return; - } - - if (from_mode == SImode && to_mode == QImode) - { -#ifdef HAVE_truncsiqi2 - if (HAVE_truncsiqi2) - { - emit_unop_insn (CODE_FOR_truncsiqi2, to, from, UNKNOWN); - return; - } -#endif - convert_move (to, force_reg (from_mode, from), unsignedp); - return; - } - - if (from_mode == HImode && to_mode == QImode) - { -#ifdef HAVE_trunchiqi2 - if (HAVE_trunchiqi2) - { - emit_unop_insn (CODE_FOR_trunchiqi2, to, from, UNKNOWN); - return; - } -#endif - convert_move (to, force_reg (from_mode, from), unsignedp); - return; - } - - /* Handle truncation of volatile memrefs, and so on; - the things that couldn't be truncated directly, - and for which there was no special instruction. */ - if (GET_MODE_BITSIZE (to_mode) < GET_MODE_BITSIZE (from_mode)) - { - rtx temp = force_reg (to_mode, gen_lowpart (to_mode, from)); - emit_move_insn (to, temp); - return; - } - - /* Mode combination is not recognized. */ - abort (); -} - -/* Return an rtx for a value that would result - from converting X to mode MODE. - Both X and MODE may be floating, or both integer. - UNSIGNEDP is nonzero if X is an unsigned value. - This can be done by referring to a part of X in place - or by copying to a new temporary with conversion. - - This function *must not* call protect_from_queue - except when putting X into an insn (in which case convert_move does it). */ - -rtx -convert_to_mode (mode, x, unsignedp) - enum machine_mode mode; - rtx x; - int unsignedp; -{ - register rtx temp; - - /* If FROM is a SUBREG that indicates that we have already done at least - the required extension, strip it. */ - - if (GET_CODE (x) == SUBREG && SUBREG_PROMOTED_VAR_P (x) - && GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) >= GET_MODE_SIZE (mode) - && SUBREG_PROMOTED_UNSIGNED_P (x) == unsignedp) - x = gen_lowpart (mode, x); - - if (mode == GET_MODE (x)) - return x; - - /* There is one case that we must handle specially: If we are converting - a CONST_INT into a mode whose size is twice HOST_BITS_PER_WIDE_INT and - we are to interpret the constant as unsigned, gen_lowpart will do - the wrong if the constant appears negative. What we want to do is - make the high-order word of the constant zero, not all ones. */ - - if (unsignedp && GET_MODE_CLASS (mode) == MODE_INT - && GET_MODE_BITSIZE (mode) == 2 * HOST_BITS_PER_WIDE_INT - && GET_CODE (x) == CONST_INT && INTVAL (x) < 0) - return immed_double_const (INTVAL (x), (HOST_WIDE_INT) 0, mode); - - /* We can do this with a gen_lowpart if both desired and current modes - are integer, and this is either a constant integer, a register, or a - non-volatile MEM. Except for the constant case, we must be narrowing - the operand. */ - - if (GET_CODE (x) == CONST_INT - || (GET_MODE_CLASS (mode) == MODE_INT - && GET_MODE_CLASS (GET_MODE (x)) == MODE_INT - && (GET_CODE (x) == CONST_DOUBLE - || (GET_MODE_SIZE (mode) <= GET_MODE_SIZE (GET_MODE (x)) - && ((GET_CODE (x) == MEM && ! MEM_VOLATILE_P (x)) - && direct_load[(int) mode] - || GET_CODE (x) == REG))))) - return gen_lowpart (mode, x); - - temp = gen_reg_rtx (mode); - convert_move (temp, x, unsignedp); - return temp; -} - -/* Generate several move instructions to copy LEN bytes - from block FROM to block TO. (These are MEM rtx's with BLKmode). - The caller must pass FROM and TO - through protect_from_queue before calling. - ALIGN (in bytes) is maximum alignment we can assume. */ - -static void -move_by_pieces (to, from, len, align) - rtx to, from; - int len, align; -{ - struct move_by_pieces data; - rtx to_addr = XEXP (to, 0), from_addr = XEXP (from, 0); - int max_size = MOVE_MAX + 1; - - data.offset = 0; - data.to_addr = to_addr; - data.from_addr = from_addr; - data.to = to; - data.from = from; - data.autinc_to - = (GET_CODE (to_addr) == PRE_INC || GET_CODE (to_addr) == PRE_DEC - || GET_CODE (to_addr) == POST_INC || GET_CODE (to_addr) == POST_DEC); - data.autinc_from - = (GET_CODE (from_addr) == PRE_INC || GET_CODE (from_addr) == PRE_DEC - || GET_CODE (from_addr) == POST_INC - || GET_CODE (from_addr) == POST_DEC); - - data.explicit_inc_from = 0; - data.explicit_inc_to = 0; - data.reverse - = (GET_CODE (to_addr) == PRE_DEC || GET_CODE (to_addr) == POST_DEC); - if (data.reverse) data.offset = len; - data.len = len; - - /* If copying requires more than two move insns, - copy addresses to registers (to make displacements shorter) - and use post-increment if available. */ - if (!(data.autinc_from && data.autinc_to) - && move_by_pieces_ninsns (len, align) > 2) - { -#ifdef HAVE_PRE_DECREMENT - if (data.reverse && ! data.autinc_from) - { - data.from_addr = copy_addr_to_reg (plus_constant (from_addr, len)); - data.autinc_from = 1; - data.explicit_inc_from = -1; - } -#endif -#ifdef HAVE_POST_INCREMENT - if (! data.autinc_from) - { - data.from_addr = copy_addr_to_reg (from_addr); - data.autinc_from = 1; - data.explicit_inc_from = 1; - } -#endif - if (!data.autinc_from && CONSTANT_P (from_addr)) - data.from_addr = copy_addr_to_reg (from_addr); -#ifdef HAVE_PRE_DECREMENT - if (data.reverse && ! data.autinc_to) - { - data.to_addr = copy_addr_to_reg (plus_constant (to_addr, len)); - data.autinc_to = 1; - data.explicit_inc_to = -1; - } -#endif -#ifdef HAVE_POST_INCREMENT - if (! data.reverse && ! data.autinc_to) - { - data.to_addr = copy_addr_to_reg (to_addr); - data.autinc_to = 1; - data.explicit_inc_to = 1; - } -#endif - if (!data.autinc_to && CONSTANT_P (to_addr)) - data.to_addr = copy_addr_to_reg (to_addr); - } - - if (! (STRICT_ALIGNMENT || SLOW_UNALIGNED_ACCESS) - || align > MOVE_MAX || align >= BIGGEST_ALIGNMENT / BITS_PER_UNIT) - align = MOVE_MAX; - - /* First move what we can in the largest integer mode, then go to - successively smaller modes. */ - - while (max_size > 1) - { - enum machine_mode mode = VOIDmode, tmode; - enum insn_code icode; - - for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT); - tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode)) - if (GET_MODE_SIZE (tmode) < max_size) - mode = tmode; - - if (mode == VOIDmode) - break; - - icode = mov_optab->handlers[(int) mode].insn_code; - if (icode != CODE_FOR_nothing - && align >= MIN (BIGGEST_ALIGNMENT / BITS_PER_UNIT, - GET_MODE_SIZE (mode))) - move_by_pieces_1 (GEN_FCN (icode), mode, &data); - - max_size = GET_MODE_SIZE (mode); - } - - /* The code above should have handled everything. */ - if (data.len != 0) - abort (); -} - -/* Return number of insns required to move L bytes by pieces. - ALIGN (in bytes) is maximum alignment we can assume. */ - -static int -move_by_pieces_ninsns (l, align) - unsigned int l; - int align; -{ - register int n_insns = 0; - int max_size = MOVE_MAX + 1; - - if (! (STRICT_ALIGNMENT || SLOW_UNALIGNED_ACCESS) - || align > MOVE_MAX || align >= BIGGEST_ALIGNMENT / BITS_PER_UNIT) - align = MOVE_MAX; - - while (max_size > 1) - { - enum machine_mode mode = VOIDmode, tmode; - enum insn_code icode; - - for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT); - tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode)) - if (GET_MODE_SIZE (tmode) < max_size) - mode = tmode; - - if (mode == VOIDmode) - break; - - icode = mov_optab->handlers[(int) mode].insn_code; - if (icode != CODE_FOR_nothing - && align >= MIN (BIGGEST_ALIGNMENT / BITS_PER_UNIT, - GET_MODE_SIZE (mode))) - n_insns += l / GET_MODE_SIZE (mode), l %= GET_MODE_SIZE (mode); - - max_size = GET_MODE_SIZE (mode); - } - - return n_insns; -} - -/* Subroutine of move_by_pieces. Move as many bytes as appropriate - with move instructions for mode MODE. GENFUN is the gen_... function - to make a move insn for that mode. DATA has all the other info. */ - -static void -move_by_pieces_1 (genfun, mode, data) - rtx (*genfun) (); - enum machine_mode mode; - struct move_by_pieces *data; -{ - register int size = GET_MODE_SIZE (mode); - register rtx to1, from1; - - while (data->len >= size) - { - if (data->reverse) data->offset -= size; - - to1 = (data->autinc_to - ? gen_rtx (MEM, mode, data->to_addr) - : change_address (data->to, mode, - plus_constant (data->to_addr, data->offset))); - from1 = - (data->autinc_from - ? gen_rtx (MEM, mode, data->from_addr) - : change_address (data->from, mode, - plus_constant (data->from_addr, data->offset))); - -#ifdef HAVE_PRE_DECREMENT - if (data->explicit_inc_to < 0) - emit_insn (gen_add2_insn (data->to_addr, GEN_INT (-size))); - if (data->explicit_inc_from < 0) - emit_insn (gen_add2_insn (data->from_addr, GEN_INT (-size))); -#endif - - emit_insn ((*genfun) (to1, from1)); -#ifdef HAVE_POST_INCREMENT - if (data->explicit_inc_to > 0) - emit_insn (gen_add2_insn (data->to_addr, GEN_INT (size))); - if (data->explicit_inc_from > 0) - emit_insn (gen_add2_insn (data->from_addr, GEN_INT (size))); -#endif - - if (! data->reverse) data->offset += size; - - data->len -= size; - } -} - -/* Emit code to move a block Y to a block X. - This may be done with string-move instructions, - with multiple scalar move instructions, or with a library call. - - Both X and Y must be MEM rtx's (perhaps inside VOLATILE) - with mode BLKmode. - SIZE is an rtx that says how long they are. - ALIGN is the maximum alignment we can assume they have, - measured in bytes. */ - -void -emit_block_move (x, y, size, align) - rtx x, y; - rtx size; - int align; -{ - if (GET_MODE (x) != BLKmode) - abort (); - - if (GET_MODE (y) != BLKmode) - abort (); - - x = protect_from_queue (x, 1); - y = protect_from_queue (y, 0); - size = protect_from_queue (size, 0); - - if (GET_CODE (x) != MEM) - abort (); - if (GET_CODE (y) != MEM) - abort (); - if (size == 0) - abort (); - - if (GET_CODE (size) == CONST_INT - && (move_by_pieces_ninsns (INTVAL (size), align) < MOVE_RATIO)) - move_by_pieces (x, y, INTVAL (size), align); - else - { - /* Try the most limited insn first, because there's no point - including more than one in the machine description unless - the more limited one has some advantage. */ - - rtx opalign = GEN_INT (align); - enum machine_mode mode; - - for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode; - mode = GET_MODE_WIDER_MODE (mode)) - { - enum insn_code code = movstr_optab[(int) mode]; - - if (code != CODE_FOR_nothing - /* We don't need MODE to be narrower than BITS_PER_HOST_WIDE_INT - here because if SIZE is less than the mode mask, as it is - returned by the macro, it will definitely be less than the - actual mode mask. */ - && (unsigned HOST_WIDE_INT) INTVAL (size) <= GET_MODE_MASK (mode) - && (insn_operand_predicate[(int) code][0] == 0 - || (*insn_operand_predicate[(int) code][0]) (x, BLKmode)) - && (insn_operand_predicate[(int) code][1] == 0 - || (*insn_operand_predicate[(int) code][1]) (y, BLKmode)) - && (insn_operand_predicate[(int) code][3] == 0 - || (*insn_operand_predicate[(int) code][3]) (opalign, - VOIDmode))) - { - rtx op2; - rtx last = get_last_insn (); - rtx pat; - - op2 = convert_to_mode (mode, size, 1); - if (insn_operand_predicate[(int) code][2] != 0 - && ! (*insn_operand_predicate[(int) code][2]) (op2, mode)) - op2 = copy_to_mode_reg (mode, op2); - - pat = GEN_FCN ((int) code) (x, y, op2, opalign); - if (pat) - { - emit_insn (pat); - return; - } - else - delete_insns_since (last); - } - } - -#ifdef TARGET_MEM_FUNCTIONS - emit_library_call (memcpy_libfunc, 0, - VOIDmode, 3, XEXP (x, 0), Pmode, - XEXP (y, 0), Pmode, - convert_to_mode (TYPE_MODE (sizetype), size, - TREE_UNSIGNED (sizetype)), - TYPE_MODE (sizetype)); -#else - emit_library_call (bcopy_libfunc, 0, - VOIDmode, 3, XEXP (y, 0), Pmode, - XEXP (x, 0), Pmode, - convert_to_mode (TYPE_MODE (sizetype), size, - TREE_UNSIGNED (sizetype)), - TYPE_MODE (sizetype)); -#endif - } -} - -/* Copy all or part of a value X into registers starting at REGNO. - The number of registers to be filled is NREGS. */ - -void -move_block_to_reg (regno, x, nregs, mode) - int regno; - rtx x; - int nregs; - enum machine_mode mode; -{ - int i; - rtx pat, last; - - if (CONSTANT_P (x) && ! LEGITIMATE_CONSTANT_P (x)) - x = validize_mem (force_const_mem (mode, x)); - - /* See if the machine can do this with a load multiple insn. */ -#ifdef HAVE_load_multiple - last = get_last_insn (); - pat = gen_load_multiple (gen_rtx (REG, word_mode, regno), x, - GEN_INT (nregs)); - if (pat) - { - emit_insn (pat); - return; - } - else - delete_insns_since (last); -#endif - - for (i = 0; i < nregs; i++) - emit_move_insn (gen_rtx (REG, word_mode, regno + i), - operand_subword_force (x, i, mode)); -} - -/* Copy all or part of a BLKmode value X out of registers starting at REGNO. - The number of registers to be filled is NREGS. */ - -void -move_block_from_reg (regno, x, nregs) - int regno; - rtx x; - int nregs; -{ - int i; - rtx pat, last; - - /* See if the machine can do this with a store multiple insn. */ -#ifdef HAVE_store_multiple - last = get_last_insn (); - pat = gen_store_multiple (x, gen_rtx (REG, word_mode, regno), - GEN_INT (nregs)); - if (pat) - { - emit_insn (pat); - return; - } - else - delete_insns_since (last); -#endif - - for (i = 0; i < nregs; i++) - { - rtx tem = operand_subword (x, i, 1, BLKmode); - - if (tem == 0) - abort (); - - emit_move_insn (tem, gen_rtx (REG, word_mode, regno + i)); - } -} - -/* Mark NREGS consecutive regs, starting at REGNO, as being live now. */ - -void -use_regs (regno, nregs) - int regno; - int nregs; -{ - int i; - - for (i = 0; i < nregs; i++) - emit_insn (gen_rtx (USE, VOIDmode, gen_rtx (REG, word_mode, regno + i))); -} - -/* Mark the instructions since PREV as a libcall block. - Add REG_LIBCALL to PREV and add a REG_RETVAL to the most recent insn. */ - -static void -group_insns (prev) - rtx prev; -{ - rtx insn_first; - rtx insn_last; - - /* Find the instructions to mark */ - if (prev) - insn_first = NEXT_INSN (prev); - else - insn_first = get_insns (); - - insn_last = get_last_insn (); - - REG_NOTES (insn_last) = gen_rtx (INSN_LIST, REG_RETVAL, insn_first, - REG_NOTES (insn_last)); - - REG_NOTES (insn_first) = gen_rtx (INSN_LIST, REG_LIBCALL, insn_last, - REG_NOTES (insn_first)); -} - -/* Write zeros through the storage of OBJECT. - If OBJECT has BLKmode, SIZE is its length in bytes. */ - -void -clear_storage (object, size) - rtx object; - int size; -{ - if (GET_MODE (object) == BLKmode) - { -#ifdef TARGET_MEM_FUNCTIONS - emit_library_call (memset_libfunc, 0, - VOIDmode, 3, - XEXP (object, 0), Pmode, const0_rtx, Pmode, - GEN_INT (size), Pmode); -#else - emit_library_call (bzero_libfunc, 0, - VOIDmode, 2, - XEXP (object, 0), Pmode, - GEN_INT (size), Pmode); -#endif - } - else - emit_move_insn (object, const0_rtx); -} - -/* Generate code to copy Y into X. - Both Y and X must have the same mode, except that - Y can be a constant with VOIDmode. - This mode cannot be BLKmode; use emit_block_move for that. - - Return the last instruction emitted. */ - -rtx -emit_move_insn (x, y) - rtx x, y; -{ - enum machine_mode mode = GET_MODE (x); - enum machine_mode submode; - enum mode_class class = GET_MODE_CLASS (mode); - int i; - - x = protect_from_queue (x, 1); - y = protect_from_queue (y, 0); - - if (mode == BLKmode || (GET_MODE (y) != mode && GET_MODE (y) != VOIDmode)) - abort (); - - if (CONSTANT_P (y) && ! LEGITIMATE_CONSTANT_P (y)) - y = force_const_mem (mode, y); - - /* If X or Y are memory references, verify that their addresses are valid - for the machine. */ - if (GET_CODE (x) == MEM - && ((! memory_address_p (GET_MODE (x), XEXP (x, 0)) - && ! push_operand (x, GET_MODE (x))) - || (flag_force_addr - && CONSTANT_ADDRESS_P (XEXP (x, 0))))) - x = change_address (x, VOIDmode, XEXP (x, 0)); - - if (GET_CODE (y) == MEM - && (! memory_address_p (GET_MODE (y), XEXP (y, 0)) - || (flag_force_addr - && CONSTANT_ADDRESS_P (XEXP (y, 0))))) - y = change_address (y, VOIDmode, XEXP (y, 0)); - - if (mode == BLKmode) - abort (); - - return emit_move_insn_1 (x, y); -} - -/* Low level part of emit_move_insn. - Called just like emit_move_insn, but assumes X and Y - are basically valid. */ - -rtx -emit_move_insn_1 (x, y) - rtx x, y; -{ - enum machine_mode mode = GET_MODE (x); - enum machine_mode submode; - enum mode_class class = GET_MODE_CLASS (mode); - int i; - - if (class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT) - submode = mode_for_size (GET_MODE_UNIT_SIZE (mode) * BITS_PER_UNIT, - (class == MODE_COMPLEX_INT - ? MODE_INT : MODE_FLOAT), - 0); - - if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) - return - emit_insn (GEN_FCN (mov_optab->handlers[(int) mode].insn_code) (x, y)); - - /* Expand complex moves by moving real part and imag part, if possible. */ - else if ((class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT) - && submode != BLKmode - && (mov_optab->handlers[(int) submode].insn_code - != CODE_FOR_nothing)) - { - /* Don't split destination if it is a stack push. */ - int stack = push_operand (x, GET_MODE (x)); - rtx prev = get_last_insn (); - - /* Tell flow that the whole of the destination is being set. */ - if (GET_CODE (x) == REG) - emit_insn (gen_rtx (CLOBBER, VOIDmode, x)); - - /* If this is a stack, push the highpart first, so it - will be in the argument order. - - In that case, change_address is used only to convert - the mode, not to change the address. */ - emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code) - ((stack ? change_address (x, submode, (rtx) 0) - : gen_highpart (submode, x)), - gen_highpart (submode, y))); - emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code) - ((stack ? change_address (x, submode, (rtx) 0) - : gen_lowpart (submode, x)), - gen_lowpart (submode, y))); - - group_insns (prev); - - return get_last_insn (); - } - - /* This will handle any multi-word mode that lacks a move_insn pattern. - However, you will get better code if you define such patterns, - even if they must turn into multiple assembler instructions. */ - else if (GET_MODE_SIZE (mode) > UNITS_PER_WORD) - { - rtx last_insn = 0; - rtx prev_insn = get_last_insn (); - - for (i = 0; - i < (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD; - i++) - { - rtx xpart = operand_subword (x, i, 1, mode); - rtx ypart = operand_subword (y, i, 1, mode); - - /* If we can't get a part of Y, put Y into memory if it is a - constant. Otherwise, force it into a register. If we still - can't get a part of Y, abort. */ - if (ypart == 0 && CONSTANT_P (y)) - { - y = force_const_mem (mode, y); - ypart = operand_subword (y, i, 1, mode); - } - else if (ypart == 0) - ypart = operand_subword_force (y, i, mode); - - if (xpart == 0 || ypart == 0) - abort (); - - last_insn = emit_move_insn (xpart, ypart); - } - /* Mark these insns as a libcall block. */ - group_insns (prev_insn); - - return last_insn; - } - else - abort (); -} - -/* Pushing data onto the stack. */ - -/* Push a block of length SIZE (perhaps variable) - and return an rtx to address the beginning of the block. - Note that it is not possible for the value returned to be a QUEUED. - The value may be virtual_outgoing_args_rtx. - - EXTRA is the number of bytes of padding to push in addition to SIZE. - BELOW nonzero means this padding comes at low addresses; - otherwise, the padding comes at high addresses. */ - -rtx -push_block (size, extra, below) - rtx size; - int extra, below; -{ - register rtx temp; - if (CONSTANT_P (size)) - anti_adjust_stack (plus_constant (size, extra)); - else if (GET_CODE (size) == REG && extra == 0) - anti_adjust_stack (size); - else - { - rtx temp = copy_to_mode_reg (Pmode, size); - if (extra != 0) - temp = expand_binop (Pmode, add_optab, temp, GEN_INT (extra), - temp, 0, OPTAB_LIB_WIDEN); - anti_adjust_stack (temp); - } - -#ifdef STACK_GROWS_DOWNWARD - temp = virtual_outgoing_args_rtx; - if (extra != 0 && below) - temp = plus_constant (temp, extra); -#else - if (GET_CODE (size) == CONST_INT) - temp = plus_constant (virtual_outgoing_args_rtx, - - INTVAL (size) - (below ? 0 : extra)); - else if (extra != 0 && !below) - temp = gen_rtx (PLUS, Pmode, virtual_outgoing_args_rtx, - negate_rtx (Pmode, plus_constant (size, extra))); - else - temp = gen_rtx (PLUS, Pmode, virtual_outgoing_args_rtx, - negate_rtx (Pmode, size)); -#endif - - return memory_address (GET_CLASS_NARROWEST_MODE (MODE_INT), temp); -} - -rtx -gen_push_operand () -{ - return gen_rtx (STACK_PUSH_CODE, Pmode, stack_pointer_rtx); -} - -/* Generate code to push X onto the stack, assuming it has mode MODE and - type TYPE. - MODE is redundant except when X is a CONST_INT (since they don't - carry mode info). - SIZE is an rtx for the size of data to be copied (in bytes), - needed only if X is BLKmode. - - ALIGN (in bytes) is maximum alignment we can assume. - - If PARTIAL and REG are both nonzero, then copy that many of the first - words of X into registers starting with REG, and push the rest of X. - The amount of space pushed is decreased by PARTIAL words, - rounded *down* to a multiple of PARM_BOUNDARY. - REG must be a hard register in this case. - If REG is zero but PARTIAL is not, take any all others actions for an - argument partially in registers, but do not actually load any - registers. - - EXTRA is the amount in bytes of extra space to leave next to this arg. - This is ignored if an argument block has already been allocated. - - On a machine that lacks real push insns, ARGS_ADDR is the address of - the bottom of the argument block for this call. We use indexing off there - to store the arg. On machines with push insns, ARGS_ADDR is 0 when a - argument block has not been preallocated. - - ARGS_SO_FAR is the size of args previously pushed for this call. */ - -void -emit_push_insn (x, mode, type, size, align, partial, reg, extra, - args_addr, args_so_far) - register rtx x; - enum machine_mode mode; - tree type; - rtx size; - int align; - int partial; - rtx reg; - int extra; - rtx args_addr; - rtx args_so_far; -{ - rtx xinner; - enum direction stack_direction -#ifdef STACK_GROWS_DOWNWARD - = downward; -#else - = upward; -#endif - - /* Decide where to pad the argument: `downward' for below, - `upward' for above, or `none' for don't pad it. - Default is below for small data on big-endian machines; else above. */ - enum direction where_pad = FUNCTION_ARG_PADDING (mode, type); - - /* Invert direction if stack is post-update. */ - if (STACK_PUSH_CODE == POST_INC || STACK_PUSH_CODE == POST_DEC) - if (where_pad != none) - where_pad = (where_pad == downward ? upward : downward); - - xinner = x = protect_from_queue (x, 0); - - if (mode == BLKmode) - { - /* Copy a block into the stack, entirely or partially. */ - - register rtx temp; - int used = partial * UNITS_PER_WORD; - int offset = used % (PARM_BOUNDARY / BITS_PER_UNIT); - int skip; - - if (size == 0) - abort (); - - used -= offset; - - /* USED is now the # of bytes we need not copy to the stack - because registers will take care of them. */ - - if (partial != 0) - xinner = change_address (xinner, BLKmode, - plus_constant (XEXP (xinner, 0), used)); - - /* If the partial register-part of the arg counts in its stack size, - skip the part of stack space corresponding to the registers. - Otherwise, start copying to the beginning of the stack space, - by setting SKIP to 0. */ -#ifndef REG_PARM_STACK_SPACE - skip = 0; -#else - skip = used; -#endif - -#ifdef PUSH_ROUNDING - /* Do it with several push insns if that doesn't take lots of insns - and if there is no difficulty with push insns that skip bytes - on the stack for alignment purposes. */ - if (args_addr == 0 - && GET_CODE (size) == CONST_INT - && skip == 0 - && (move_by_pieces_ninsns ((unsigned) INTVAL (size) - used, align) - < MOVE_RATIO) - /* Here we avoid the case of a structure whose weak alignment - forces many pushes of a small amount of data, - and such small pushes do rounding that causes trouble. */ - && ((! STRICT_ALIGNMENT && ! SLOW_UNALIGNED_ACCESS) - || align >= BIGGEST_ALIGNMENT / BITS_PER_UNIT - || PUSH_ROUNDING (align) == align) - && PUSH_ROUNDING (INTVAL (size)) == INTVAL (size)) - { - /* Push padding now if padding above and stack grows down, - or if padding below and stack grows up. - But if space already allocated, this has already been done. */ - if (extra && args_addr == 0 - && where_pad != none && where_pad != stack_direction) - anti_adjust_stack (GEN_INT (extra)); - - move_by_pieces (gen_rtx (MEM, BLKmode, gen_push_operand ()), xinner, - INTVAL (size) - used, align); - } - else -#endif /* PUSH_ROUNDING */ - { - /* Otherwise make space on the stack and copy the data - to the address of that space. */ - - /* Deduct words put into registers from the size we must copy. */ - if (partial != 0) - { - if (GET_CODE (size) == CONST_INT) - size = GEN_INT (INTVAL (size) - used); - else - size = expand_binop (GET_MODE (size), sub_optab, size, - GEN_INT (used), NULL_RTX, 0, - OPTAB_LIB_WIDEN); - } - - /* Get the address of the stack space. - In this case, we do not deal with EXTRA separately. - A single stack adjust will do. */ - if (! args_addr) - { - temp = push_block (size, extra, where_pad == downward); - extra = 0; - } - else if (GET_CODE (args_so_far) == CONST_INT) - temp = memory_address (BLKmode, - plus_constant (args_addr, - skip + INTVAL (args_so_far))); - else - temp = memory_address (BLKmode, - plus_constant (gen_rtx (PLUS, Pmode, - args_addr, args_so_far), - skip)); - - /* TEMP is the address of the block. Copy the data there. */ - if (GET_CODE (size) == CONST_INT - && (move_by_pieces_ninsns ((unsigned) INTVAL (size), align) - < MOVE_RATIO)) - { - move_by_pieces (gen_rtx (MEM, BLKmode, temp), xinner, - INTVAL (size), align); - goto ret; - } - /* Try the most limited insn first, because there's no point - including more than one in the machine description unless - the more limited one has some advantage. */ -#ifdef HAVE_movstrqi - if (HAVE_movstrqi - && GET_CODE (size) == CONST_INT - && ((unsigned) INTVAL (size) - < (1 << (GET_MODE_BITSIZE (QImode) - 1)))) - { - rtx pat = gen_movstrqi (gen_rtx (MEM, BLKmode, temp), - xinner, size, GEN_INT (align)); - if (pat != 0) - { - emit_insn (pat); - goto ret; - } - } -#endif -#ifdef HAVE_movstrhi - if (HAVE_movstrhi - && GET_CODE (size) == CONST_INT - && ((unsigned) INTVAL (size) - < (1 << (GET_MODE_BITSIZE (HImode) - 1)))) - { - rtx pat = gen_movstrhi (gen_rtx (MEM, BLKmode, temp), - xinner, size, GEN_INT (align)); - if (pat != 0) - { - emit_insn (pat); - goto ret; - } - } -#endif -#ifdef HAVE_movstrsi - if (HAVE_movstrsi) - { - rtx pat = gen_movstrsi (gen_rtx (MEM, BLKmode, temp), - xinner, size, GEN_INT (align)); - if (pat != 0) - { - emit_insn (pat); - goto ret; - } - } -#endif -#ifdef HAVE_movstrdi - if (HAVE_movstrdi) - { - rtx pat = gen_movstrdi (gen_rtx (MEM, BLKmode, temp), - xinner, size, GEN_INT (align)); - if (pat != 0) - { - emit_insn (pat); - goto ret; - } - } -#endif - -#ifndef ACCUMULATE_OUTGOING_ARGS - /* If the source is referenced relative to the stack pointer, - copy it to another register to stabilize it. We do not need - to do this if we know that we won't be changing sp. */ - - if (reg_mentioned_p (virtual_stack_dynamic_rtx, temp) - || reg_mentioned_p (virtual_outgoing_args_rtx, temp)) - temp = copy_to_reg (temp); -#endif - - /* Make inhibit_defer_pop nonzero around the library call - to force it to pop the bcopy-arguments right away. */ - NO_DEFER_POP; -#ifdef TARGET_MEM_FUNCTIONS - emit_library_call (memcpy_libfunc, 0, - VOIDmode, 3, temp, Pmode, XEXP (xinner, 0), Pmode, - convert_to_mode (TYPE_MODE (sizetype), - size, TREE_UNSIGNED (sizetype)), - TYPE_MODE (sizetype)); -#else - emit_library_call (bcopy_libfunc, 0, - VOIDmode, 3, XEXP (xinner, 0), Pmode, temp, Pmode, - convert_to_mode (TYPE_MODE (sizetype), - size, TREE_UNSIGNED (sizetype)), - TYPE_MODE (sizetype)); -#endif - OK_DEFER_POP; - } - } - else if (partial > 0) - { - /* Scalar partly in registers. */ - - int size = GET_MODE_SIZE (mode) / UNITS_PER_WORD; - int i; - int not_stack; - /* # words of start of argument - that we must make space for but need not store. */ - int offset = partial % (PARM_BOUNDARY / BITS_PER_WORD); - int args_offset = INTVAL (args_so_far); - int skip; - - /* Push padding now if padding above and stack grows down, - or if padding below and stack grows up. - But if space already allocated, this has already been done. */ - if (extra && args_addr == 0 - && where_pad != none && where_pad != stack_direction) - anti_adjust_stack (GEN_INT (extra)); - - /* If we make space by pushing it, we might as well push - the real data. Otherwise, we can leave OFFSET nonzero - and leave the space uninitialized. */ - if (args_addr == 0) - offset = 0; - - /* Now NOT_STACK gets the number of words that we don't need to - allocate on the stack. */ - not_stack = partial - offset; - - /* If the partial register-part of the arg counts in its stack size, - skip the part of stack space corresponding to the registers. - Otherwise, start copying to the beginning of the stack space, - by setting SKIP to 0. */ -#ifndef REG_PARM_STACK_SPACE - skip = 0; -#else - skip = not_stack; -#endif - - if (CONSTANT_P (x) && ! LEGITIMATE_CONSTANT_P (x)) - x = validize_mem (force_const_mem (mode, x)); - - /* If X is a hard register in a non-integer mode, copy it into a pseudo; - SUBREGs of such registers are not allowed. */ - if ((GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER - && GET_MODE_CLASS (GET_MODE (x)) != MODE_INT)) - x = copy_to_reg (x); - - /* Loop over all the words allocated on the stack for this arg. */ - /* We can do it by words, because any scalar bigger than a word - has a size a multiple of a word. */ -#ifndef PUSH_ARGS_REVERSED - for (i = not_stack; i < size; i++) -#else - for (i = size - 1; i >= not_stack; i--) -#endif - if (i >= not_stack + offset) - emit_push_insn (operand_subword_force (x, i, mode), - word_mode, NULL_TREE, NULL_RTX, align, 0, NULL_RTX, - 0, args_addr, - GEN_INT (args_offset + ((i - not_stack + skip) - * UNITS_PER_WORD))); - } - else - { - rtx addr; - - /* Push padding now if padding above and stack grows down, - or if padding below and stack grows up. - But if space already allocated, this has already been done. */ - if (extra && args_addr == 0 - && where_pad != none && where_pad != stack_direction) - anti_adjust_stack (GEN_INT (extra)); - -#ifdef PUSH_ROUNDING - if (args_addr == 0) - addr = gen_push_operand (); - else -#endif - if (GET_CODE (args_so_far) == CONST_INT) - addr - = memory_address (mode, - plus_constant (args_addr, INTVAL (args_so_far))); - else - addr = memory_address (mode, gen_rtx (PLUS, Pmode, args_addr, - args_so_far)); - - emit_move_insn (gen_rtx (MEM, mode, addr), x); - } - - ret: - /* If part should go in registers, copy that part - into the appropriate registers. Do this now, at the end, - since mem-to-mem copies above may do function calls. */ - if (partial > 0 && reg != 0) - move_block_to_reg (REGNO (reg), x, partial, mode); - - if (extra && args_addr == 0 && where_pad == stack_direction) - anti_adjust_stack (GEN_INT (extra)); -} - -/* Expand an assignment that stores the value of FROM into TO. - If WANT_VALUE is nonzero, return an rtx for the value of TO. - (This may contain a QUEUED rtx.) - Otherwise, the returned value is not meaningful. - - SUGGEST_REG is no longer actually used. - It used to mean, copy the value through a register - and return that register, if that is possible. - But now we do this if WANT_VALUE. - - If the value stored is a constant, we return the constant. */ - -rtx -expand_assignment (to, from, want_value, suggest_reg) - tree to, from; - int want_value; - int suggest_reg; -{ - register rtx to_rtx = 0; - rtx result; - - /* Don't crash if the lhs of the assignment was erroneous. */ - - if (TREE_CODE (to) == ERROR_MARK) - return expand_expr (from, NULL_RTX, VOIDmode, 0); - - /* Assignment of a structure component needs special treatment - if the structure component's rtx is not simply a MEM. - Assignment of an array element at a constant index - has the same problem. */ - - if (TREE_CODE (to) == COMPONENT_REF - || TREE_CODE (to) == BIT_FIELD_REF - || (TREE_CODE (to) == ARRAY_REF - && TREE_CODE (TREE_OPERAND (to, 1)) == INTEGER_CST - && TREE_CODE (TYPE_SIZE (TREE_TYPE (to))) == INTEGER_CST)) - { - enum machine_mode mode1; - int bitsize; - int bitpos; - tree offset; - int unsignedp; - int volatilep = 0; - tree tem = get_inner_reference (to, &bitsize, &bitpos, &offset, - &mode1, &unsignedp, &volatilep); - - /* If we are going to use store_bit_field and extract_bit_field, - make sure to_rtx will be safe for multiple use. */ - - if (mode1 == VOIDmode && want_value) - tem = stabilize_reference (tem); - - to_rtx = expand_expr (tem, NULL_RTX, VOIDmode, 0); - if (offset != 0) - { - rtx offset_rtx = expand_expr (offset, NULL_RTX, VOIDmode, 0); - - if (GET_CODE (to_rtx) != MEM) - abort (); - to_rtx = change_address (to_rtx, VOIDmode, - gen_rtx (PLUS, Pmode, XEXP (to_rtx, 0), - force_reg (Pmode, offset_rtx))); - } - if (volatilep) - { - if (GET_CODE (to_rtx) == MEM) - MEM_VOLATILE_P (to_rtx) = 1; -#if 0 /* This was turned off because, when a field is volatile - in an object which is not volatile, the object may be in a register, - and then we would abort over here. */ - else - abort (); -#endif - } - - result = store_field (to_rtx, bitsize, bitpos, mode1, from, - (want_value - /* Spurious cast makes HPUX compiler happy. */ - ? (enum machine_mode) TYPE_MODE (TREE_TYPE (to)) - : VOIDmode), - unsignedp, - /* Required alignment of containing datum. */ - TYPE_ALIGN (TREE_TYPE (tem)) / BITS_PER_UNIT, - int_size_in_bytes (TREE_TYPE (tem))); - preserve_temp_slots (result); - free_temp_slots (); - - /* If we aren't returning a result, just pass on what expand_expr - returned; it was probably const0_rtx. Otherwise, convert RESULT - to the proper mode. */ - return (want_value ? convert_to_mode (TYPE_MODE (TREE_TYPE (to)), result, - TREE_UNSIGNED (TREE_TYPE (to))) - : result); - } - - /* Ordinary treatment. Expand TO to get a REG or MEM rtx. - Don't re-expand if it was expanded already (in COMPONENT_REF case). */ - - if (to_rtx == 0) - to_rtx = expand_expr (to, NULL_RTX, VOIDmode, 0); - - /* Don't move directly into a return register. */ - if (TREE_CODE (to) == RESULT_DECL && GET_CODE (to_rtx) == REG) - { - rtx temp = expand_expr (from, 0, GET_MODE (to_rtx), 0); - emit_move_insn (to_rtx, temp); - preserve_temp_slots (to_rtx); - free_temp_slots (); - return to_rtx; - } - - /* In case we are returning the contents of an object which overlaps - the place the value is being stored, use a safe function when copying - a value through a pointer into a structure value return block. */ - if (TREE_CODE (to) == RESULT_DECL && TREE_CODE (from) == INDIRECT_REF - && current_function_returns_struct - && !current_function_returns_pcc_struct) - { - rtx from_rtx = expand_expr (from, NULL_RTX, VOIDmode, 0); - rtx size = expr_size (from); - -#ifdef TARGET_MEM_FUNCTIONS - emit_library_call (memcpy_libfunc, 0, - VOIDmode, 3, XEXP (to_rtx, 0), Pmode, - XEXP (from_rtx, 0), Pmode, - convert_to_mode (TYPE_MODE (sizetype), - size, TREE_UNSIGNED (sizetype)), - TYPE_MODE (sizetype)); -#else - emit_library_call (bcopy_libfunc, 0, - VOIDmode, 3, XEXP (from_rtx, 0), Pmode, - XEXP (to_rtx, 0), Pmode, - convert_to_mode (TYPE_MODE (sizetype), - size, TREE_UNSIGNED (sizetype)), - TYPE_MODE (sizetype)); -#endif - - preserve_temp_slots (to_rtx); - free_temp_slots (); - return to_rtx; - } - - /* Compute FROM and store the value in the rtx we got. */ - - result = store_expr (from, to_rtx, want_value); - preserve_temp_slots (result); - free_temp_slots (); - return result; -} - -/* Generate code for computing expression EXP, - and storing the value into TARGET. - Returns TARGET or an equivalent value. - TARGET may contain a QUEUED rtx. - - If SUGGEST_REG is nonzero, copy the value through a register - and return that register, if that is possible. - - If the value stored is a constant, we return the constant. */ - -rtx -store_expr (exp, target, suggest_reg) - register tree exp; - register rtx target; - int suggest_reg; -{ - register rtx temp; - int dont_return_target = 0; - - if (TREE_CODE (exp) == COMPOUND_EXPR) - { - /* Perform first part of compound expression, then assign from second - part. */ - expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, 0); - emit_queue (); - return store_expr (TREE_OPERAND (exp, 1), target, suggest_reg); - } - else if (TREE_CODE (exp) == COND_EXPR && GET_MODE (target) == BLKmode) - { - /* For conditional expression, get safe form of the target. Then - test the condition, doing the appropriate assignment on either - side. This avoids the creation of unnecessary temporaries. - For non-BLKmode, it is more efficient not to do this. */ - - rtx lab1 = gen_label_rtx (), lab2 = gen_label_rtx (); - - emit_queue (); - target = protect_from_queue (target, 1); - - NO_DEFER_POP; - jumpifnot (TREE_OPERAND (exp, 0), lab1); - store_expr (TREE_OPERAND (exp, 1), target, suggest_reg); - emit_queue (); - emit_jump_insn (gen_jump (lab2)); - emit_barrier (); - emit_label (lab1); - store_expr (TREE_OPERAND (exp, 2), target, suggest_reg); - emit_queue (); - emit_label (lab2); - OK_DEFER_POP; - return target; - } - else if (suggest_reg && GET_CODE (target) == MEM - && GET_MODE (target) != BLKmode) - /* If target is in memory and caller wants value in a register instead, - arrange that. Pass TARGET as target for expand_expr so that, - if EXP is another assignment, SUGGEST_REG will be nonzero for it. - We know expand_expr will not use the target in that case. */ - { - temp = expand_expr (exp, cse_not_expected ? NULL_RTX : target, - GET_MODE (target), 0); - if (GET_MODE (temp) != BLKmode && GET_MODE (temp) != VOIDmode) - temp = copy_to_reg (temp); - dont_return_target = 1; - } - else if (queued_subexp_p (target)) - /* If target contains a postincrement, it is not safe - to use as the returned value. It would access the wrong - place by the time the queued increment gets output. - So copy the value through a temporary and use that temp - as the result. */ - { - if (GET_MODE (target) != BLKmode && GET_MODE (target) != VOIDmode) - { - /* Expand EXP into a new pseudo. */ - temp = gen_reg_rtx (GET_MODE (target)); - temp = expand_expr (exp, temp, GET_MODE (target), 0); - } - else - temp = expand_expr (exp, NULL_RTX, GET_MODE (target), 0); - dont_return_target = 1; - } - else if (GET_CODE (target) == SUBREG && SUBREG_PROMOTED_VAR_P (target)) - /* If this is an scalar in a register that is stored in a wider mode - than the declared mode, compute the result into its declared mode - and then convert to the wider mode. Our value is the computed - expression. */ - { - temp = expand_expr (exp, NULL_RTX, VOIDmode, 0); - convert_move (SUBREG_REG (target), temp, - SUBREG_PROMOTED_UNSIGNED_P (target)); - return temp; - } - else - { - temp = expand_expr (exp, target, GET_MODE (target), 0); - /* DO return TARGET if it's a specified hardware register. - expand_return relies on this. */ - if (!(target && GET_CODE (target) == REG - && REGNO (target) < FIRST_PSEUDO_REGISTER) - && CONSTANT_P (temp)) - dont_return_target = 1; - } - - /* If value was not generated in the target, store it there. - Convert the value to TARGET's type first if nec. */ - - if (temp != target && TREE_CODE (exp) != ERROR_MARK) - { - target = protect_from_queue (target, 1); - if (GET_MODE (temp) != GET_MODE (target) - && GET_MODE (temp) != VOIDmode) - { - int unsignedp = TREE_UNSIGNED (TREE_TYPE (exp)); - if (dont_return_target) - { - /* In this case, we will return TEMP, - so make sure it has the proper mode. - But don't forget to store the value into TARGET. */ - temp = convert_to_mode (GET_MODE (target), temp, unsignedp); - emit_move_insn (target, temp); - } - else - convert_move (target, temp, unsignedp); - } - - else if (GET_MODE (temp) == BLKmode && TREE_CODE (exp) == STRING_CST) - { - /* Handle copying a string constant into an array. - The string constant may be shorter than the array. - So copy just the string's actual length, and clear the rest. */ - rtx size; - - /* Get the size of the data type of the string, - which is actually the size of the target. */ - size = expr_size (exp); - if (GET_CODE (size) == CONST_INT - && INTVAL (size) < TREE_STRING_LENGTH (exp)) - emit_block_move (target, temp, size, - TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); - else - { - /* Compute the size of the data to copy from the string. */ - tree copy_size - = size_binop (MIN_EXPR, - size_binop (CEIL_DIV_EXPR, - TYPE_SIZE (TREE_TYPE (exp)), - size_int (BITS_PER_UNIT)), - convert (sizetype, - build_int_2 (TREE_STRING_LENGTH (exp), 0))); - rtx copy_size_rtx = expand_expr (copy_size, NULL_RTX, - VOIDmode, 0); - rtx label = 0; - - /* Copy that much. */ - emit_block_move (target, temp, copy_size_rtx, - TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); - - /* Figure out how much is left in TARGET - that we have to clear. */ - if (GET_CODE (copy_size_rtx) == CONST_INT) - { - temp = plus_constant (XEXP (target, 0), - TREE_STRING_LENGTH (exp)); - size = plus_constant (size, - - TREE_STRING_LENGTH (exp)); - } - else - { - enum machine_mode size_mode = Pmode; - - temp = force_reg (Pmode, XEXP (target, 0)); - temp = expand_binop (size_mode, add_optab, temp, - copy_size_rtx, NULL_RTX, 0, - OPTAB_LIB_WIDEN); - - size = expand_binop (size_mode, sub_optab, size, - copy_size_rtx, NULL_RTX, 0, - OPTAB_LIB_WIDEN); - - emit_cmp_insn (size, const0_rtx, LT, NULL_RTX, - GET_MODE (size), 0, 0); - label = gen_label_rtx (); - emit_jump_insn (gen_blt (label)); - } - - if (size != const0_rtx) - { -#ifdef TARGET_MEM_FUNCTIONS - emit_library_call (memset_libfunc, 0, VOIDmode, 3, - temp, Pmode, const0_rtx, Pmode, size, Pmode); -#else - emit_library_call (bzero_libfunc, 0, VOIDmode, 2, - temp, Pmode, size, Pmode); -#endif - } - if (label) - emit_label (label); - } - } - else if (GET_MODE (temp) == BLKmode) - emit_block_move (target, temp, expr_size (exp), - TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); - else - emit_move_insn (target, temp); - } - if (dont_return_target) - return temp; - return target; -} - -/* Store the value of constructor EXP into the rtx TARGET. - TARGET is either a REG or a MEM. */ - -static void -store_constructor (exp, target) - tree exp; - rtx target; -{ - tree type = TREE_TYPE (exp); - - /* We know our target cannot conflict, since safe_from_p has been called. */ -#if 0 - /* Don't try copying piece by piece into a hard register - since that is vulnerable to being clobbered by EXP. - Instead, construct in a pseudo register and then copy it all. */ - if (GET_CODE (target) == REG && REGNO (target) < FIRST_PSEUDO_REGISTER) - { - rtx temp = gen_reg_rtx (GET_MODE (target)); - store_constructor (exp, temp); - emit_move_insn (target, temp); - return; - } -#endif - - if (TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE) - { - register tree elt; - - /* Inform later passes that the whole union value is dead. */ - if (TREE_CODE (type) == UNION_TYPE) - emit_insn (gen_rtx (CLOBBER, VOIDmode, target)); - - /* If we are building a static constructor into a register, - set the initial value as zero so we can fold the value into - a constant. */ - else if (GET_CODE (target) == REG && TREE_STATIC (exp)) - emit_move_insn (target, const0_rtx); - - /* If the constructor has fewer fields than the structure, - clear the whole structure first. */ - else if (list_length (CONSTRUCTOR_ELTS (exp)) - != list_length (TYPE_FIELDS (type))) - clear_storage (target, int_size_in_bytes (type)); - else - /* Inform later passes that the old value is dead. */ - emit_insn (gen_rtx (CLOBBER, VOIDmode, target)); - - /* Store each element of the constructor into - the corresponding field of TARGET. */ - - for (elt = CONSTRUCTOR_ELTS (exp); elt; elt = TREE_CHAIN (elt)) - { - register tree field = TREE_PURPOSE (elt); - register enum machine_mode mode; - int bitsize; - int bitpos; - int unsignedp; - - /* Just ignore missing fields. - We cleared the whole structure, above, - if any fields are missing. */ - if (field == 0) - continue; - - bitsize = TREE_INT_CST_LOW (DECL_SIZE (field)); - unsignedp = TREE_UNSIGNED (field); - mode = DECL_MODE (field); - if (DECL_BIT_FIELD (field)) - mode = VOIDmode; - - if (TREE_CODE (DECL_FIELD_BITPOS (field)) != INTEGER_CST) - /* ??? This case remains to be written. */ - abort (); - - bitpos = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)); - - store_field (target, bitsize, bitpos, mode, TREE_VALUE (elt), - /* The alignment of TARGET is - at least what its type requires. */ - VOIDmode, 0, - TYPE_ALIGN (type) / BITS_PER_UNIT, - int_size_in_bytes (type)); - } - } - else if (TREE_CODE (type) == ARRAY_TYPE) - { - register tree elt; - register int i; - tree domain = TYPE_DOMAIN (type); - HOST_WIDE_INT minelt = TREE_INT_CST_LOW (TYPE_MIN_VALUE (domain)); - HOST_WIDE_INT maxelt = TREE_INT_CST_LOW (TYPE_MAX_VALUE (domain)); - tree elttype = TREE_TYPE (type); - - /* If the constructor has fewer fields than the structure, - clear the whole structure first. Similarly if this this is - static constructor of a non-BLKmode object. */ - - if (list_length (CONSTRUCTOR_ELTS (exp)) < maxelt - minelt + 1 - || (GET_CODE (target) == REG && TREE_STATIC (exp))) - clear_storage (target, int_size_in_bytes (type)); - else - /* Inform later passes that the old value is dead. */ - emit_insn (gen_rtx (CLOBBER, VOIDmode, target)); - - /* Store each element of the constructor into - the corresponding element of TARGET, determined - by counting the elements. */ - for (elt = CONSTRUCTOR_ELTS (exp), i = 0; - elt; - elt = TREE_CHAIN (elt), i++) - { - register enum machine_mode mode; - int bitsize; - int bitpos; - int unsignedp; - - mode = TYPE_MODE (elttype); - bitsize = GET_MODE_BITSIZE (mode); - unsignedp = TREE_UNSIGNED (elttype); - - bitpos = (i * TREE_INT_CST_LOW (TYPE_SIZE (elttype))); - - store_field (target, bitsize, bitpos, mode, TREE_VALUE (elt), - /* The alignment of TARGET is - at least what its type requires. */ - VOIDmode, 0, - TYPE_ALIGN (type) / BITS_PER_UNIT, - int_size_in_bytes (type)); - } - } - - else - abort (); -} - -/* Store the value of EXP (an expression tree) - into a subfield of TARGET which has mode MODE and occupies - BITSIZE bits, starting BITPOS bits from the start of TARGET. - If MODE is VOIDmode, it means that we are storing into a bit-field. - - If VALUE_MODE is VOIDmode, return nothing in particular. - UNSIGNEDP is not used in this case. - - Otherwise, return an rtx for the value stored. This rtx - has mode VALUE_MODE if that is convenient to do. - In this case, UNSIGNEDP must be nonzero if the value is an unsigned type. - - ALIGN is the alignment that TARGET is known to have, measured in bytes. - TOTAL_SIZE is the size in bytes of the structure, or -1 if varying. */ - -static rtx -store_field (target, bitsize, bitpos, mode, exp, value_mode, - unsignedp, align, total_size) - rtx target; - int bitsize, bitpos; - enum machine_mode mode; - tree exp; - enum machine_mode value_mode; - int unsignedp; - int align; - int total_size; -{ - HOST_WIDE_INT width_mask = 0; - - if (bitsize < HOST_BITS_PER_WIDE_INT) - width_mask = ((HOST_WIDE_INT) 1 << bitsize) - 1; - - /* If we are storing into an unaligned field of an aligned union that is - in a register, we may have the mode of TARGET being an integer mode but - MODE == BLKmode. In that case, get an aligned object whose size and - alignment are the same as TARGET and store TARGET into it (we can avoid - the store if the field being stored is the entire width of TARGET). Then - call ourselves recursively to store the field into a BLKmode version of - that object. Finally, load from the object into TARGET. This is not - very efficient in general, but should only be slightly more expensive - than the otherwise-required unaligned accesses. Perhaps this can be - cleaned up later. */ - - if (mode == BLKmode - && (GET_CODE (target) == REG || GET_CODE (target) == SUBREG)) - { - rtx object = assign_stack_temp (GET_MODE (target), - GET_MODE_SIZE (GET_MODE (target)), 0); - rtx blk_object = copy_rtx (object); - - PUT_MODE (blk_object, BLKmode); - - if (bitsize != GET_MODE_BITSIZE (GET_MODE (target))) - emit_move_insn (object, target); - - store_field (blk_object, bitsize, bitpos, mode, exp, VOIDmode, 0, - align, total_size); - - emit_move_insn (target, object); - - return target; - } - - /* If the structure is in a register or if the component - is a bit field, we cannot use addressing to access it. - Use bit-field techniques or SUBREG to store in it. */ - - if (mode == VOIDmode - || (mode != BLKmode && ! direct_store[(int) mode]) - || GET_CODE (target) == REG - || GET_CODE (target) == SUBREG) - { - rtx temp = expand_expr (exp, NULL_RTX, VOIDmode, 0); - /* Store the value in the bitfield. */ - store_bit_field (target, bitsize, bitpos, mode, temp, align, total_size); - if (value_mode != VOIDmode) - { - /* The caller wants an rtx for the value. */ - /* If possible, avoid refetching from the bitfield itself. */ - if (width_mask != 0 - && ! (GET_CODE (target) == MEM && MEM_VOLATILE_P (target))) - { - tree count; - enum machine_mode tmode; - - if (unsignedp) - return expand_and (temp, GEN_INT (width_mask), NULL_RTX); - tmode = GET_MODE (temp); - if (tmode == VOIDmode) - tmode = value_mode; - count = build_int_2 (GET_MODE_BITSIZE (tmode) - bitsize, 0); - temp = expand_shift (LSHIFT_EXPR, tmode, temp, count, 0, 0); - return expand_shift (RSHIFT_EXPR, tmode, temp, count, 0, 0); - } - return extract_bit_field (target, bitsize, bitpos, unsignedp, - NULL_RTX, value_mode, 0, align, - total_size); - } - return const0_rtx; - } - else - { - rtx addr = XEXP (target, 0); - rtx to_rtx; - - /* If a value is wanted, it must be the lhs; - so make the address stable for multiple use. */ - - if (value_mode != VOIDmode && GET_CODE (addr) != REG - && ! CONSTANT_ADDRESS_P (addr) - /* A frame-pointer reference is already stable. */ - && ! (GET_CODE (addr) == PLUS - && GET_CODE (XEXP (addr, 1)) == CONST_INT - && (XEXP (addr, 0) == virtual_incoming_args_rtx - || XEXP (addr, 0) == virtual_stack_vars_rtx))) - addr = copy_to_reg (addr); - - /* Now build a reference to just the desired component. */ - - to_rtx = change_address (target, mode, - plus_constant (addr, (bitpos / BITS_PER_UNIT))); - MEM_IN_STRUCT_P (to_rtx) = 1; - - return store_expr (exp, to_rtx, value_mode != VOIDmode); - } -} - -/* Given an expression EXP that may be a COMPONENT_REF, a BIT_FIELD_REF, - or an ARRAY_REF, look for nested COMPONENT_REFs, BIT_FIELD_REFs, or - ARRAY_REFs and find the ultimate containing object, which we return. - - We set *PBITSIZE to the size in bits that we want, *PBITPOS to the - bit position, and *PUNSIGNEDP to the signedness of the field. - If the position of the field is variable, we store a tree - giving the variable offset (in units) in *POFFSET. - This offset is in addition to the bit position. - If the position is not variable, we store 0 in *POFFSET. - - If any of the extraction expressions is volatile, - we store 1 in *PVOLATILEP. Otherwise we don't change that. - - If the field is a bit-field, *PMODE is set to VOIDmode. Otherwise, it - is a mode that can be used to access the field. In that case, *PBITSIZE - is redundant. - - If the field describes a variable-sized object, *PMODE is set to - VOIDmode and *PBITSIZE is set to -1. An access cannot be made in - this case, but the address of the object can be found. */ - -tree -get_inner_reference (exp, pbitsize, pbitpos, poffset, pmode, - punsignedp, pvolatilep) - tree exp; - int *pbitsize; - int *pbitpos; - tree *poffset; - enum machine_mode *pmode; - int *punsignedp; - int *pvolatilep; -{ - tree size_tree = 0; - enum machine_mode mode = VOIDmode; - tree offset = integer_zero_node; - - if (TREE_CODE (exp) == COMPONENT_REF) - { - size_tree = DECL_SIZE (TREE_OPERAND (exp, 1)); - if (! DECL_BIT_FIELD (TREE_OPERAND (exp, 1))) - mode = DECL_MODE (TREE_OPERAND (exp, 1)); - *punsignedp = TREE_UNSIGNED (TREE_OPERAND (exp, 1)); - } - else if (TREE_CODE (exp) == BIT_FIELD_REF) - { - size_tree = TREE_OPERAND (exp, 1); - *punsignedp = TREE_UNSIGNED (exp); - } - else - { - mode = TYPE_MODE (TREE_TYPE (exp)); - *pbitsize = GET_MODE_BITSIZE (mode); - *punsignedp = TREE_UNSIGNED (TREE_TYPE (exp)); - } - - if (size_tree) - { - if (TREE_CODE (size_tree) != INTEGER_CST) - mode = BLKmode, *pbitsize = -1; - else - *pbitsize = TREE_INT_CST_LOW (size_tree); - } - - /* Compute cumulative bit-offset for nested component-refs and array-refs, - and find the ultimate containing object. */ - - *pbitpos = 0; - - while (1) - { - if (TREE_CODE (exp) == COMPONENT_REF || TREE_CODE (exp) == BIT_FIELD_REF) - { - tree pos = (TREE_CODE (exp) == COMPONENT_REF - ? DECL_FIELD_BITPOS (TREE_OPERAND (exp, 1)) - : TREE_OPERAND (exp, 2)); - - /* If this field hasn't been filled in yet, don't go - past it. This should only happen when folding expressions - made during type construction. */ - if (pos == 0) - break; - - if (TREE_CODE (pos) == PLUS_EXPR) - { - tree constant, var; - if (TREE_CODE (TREE_OPERAND (pos, 0)) == INTEGER_CST) - { - constant = TREE_OPERAND (pos, 0); - var = TREE_OPERAND (pos, 1); - } - else if (TREE_CODE (TREE_OPERAND (pos, 1)) == INTEGER_CST) - { - constant = TREE_OPERAND (pos, 1); - var = TREE_OPERAND (pos, 0); - } - else - abort (); - - *pbitpos += TREE_INT_CST_LOW (constant); - offset = size_binop (PLUS_EXPR, offset, - size_binop (FLOOR_DIV_EXPR, var, - size_int (BITS_PER_UNIT))); - } - else if (TREE_CODE (pos) == INTEGER_CST) - *pbitpos += TREE_INT_CST_LOW (pos); - else - { - /* Assume here that the offset is a multiple of a unit. - If not, there should be an explicitly added constant. */ - offset = size_binop (PLUS_EXPR, offset, - size_binop (FLOOR_DIV_EXPR, pos, - size_int (BITS_PER_UNIT))); - } - } - - else if (TREE_CODE (exp) == ARRAY_REF) - { - /* This code is based on the code in case ARRAY_REF in expand_expr - below. We assume here that the size of an array element is - always an integral multiple of BITS_PER_UNIT. */ - - tree index = TREE_OPERAND (exp, 1); - tree domain = TYPE_DOMAIN (TREE_TYPE (TREE_OPERAND (exp, 0))); - tree low_bound - = domain ? TYPE_MIN_VALUE (domain) : integer_zero_node; - tree index_type = TREE_TYPE (index); - - if (! integer_zerop (low_bound)) - index = fold (build (MINUS_EXPR, index_type, index, low_bound)); - - if (TYPE_PRECISION (index_type) != POINTER_SIZE) - { - index = convert (type_for_size (POINTER_SIZE, 0), index); - index_type = TREE_TYPE (index); - } - - index = fold (build (MULT_EXPR, index_type, index, - TYPE_SIZE (TREE_TYPE (exp)))); - - if (TREE_CODE (index) == INTEGER_CST - && TREE_INT_CST_HIGH (index) == 0) - *pbitpos += TREE_INT_CST_LOW (index); - else - offset = size_binop (PLUS_EXPR, offset, - size_binop (FLOOR_DIV_EXPR, index, - size_int (BITS_PER_UNIT))); - } - else if (TREE_CODE (exp) != NON_LVALUE_EXPR - && ! ((TREE_CODE (exp) == NOP_EXPR - || TREE_CODE (exp) == CONVERT_EXPR) - && (TYPE_MODE (TREE_TYPE (exp)) - == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))))) - break; - - /* If any reference in the chain is volatile, the effect is volatile. */ - if (TREE_THIS_VOLATILE (exp)) - *pvolatilep = 1; - exp = TREE_OPERAND (exp, 0); - } - - /* If this was a bit-field, see if there is a mode that allows direct - access in case EXP is in memory. */ - if (mode == VOIDmode && *pbitsize != 0 && *pbitpos % *pbitsize == 0) - { - mode = mode_for_size (*pbitsize, MODE_INT, 0); - if (mode == BLKmode) - mode = VOIDmode; - } - - if (integer_zerop (offset)) - offset = 0; - - *pmode = mode; - *poffset = offset; -#if 0 - /* We aren't finished fixing the callers to really handle nonzero offset. */ - if (offset != 0) - abort (); -#endif - - return exp; -} - -/* Given an rtx VALUE that may contain additions and multiplications, - return an equivalent value that just refers to a register or memory. - This is done by generating instructions to perform the arithmetic - and returning a pseudo-register containing the value. - - The returned value may be a REG, SUBREG, MEM or constant. */ - -rtx -force_operand (value, target) - rtx value, target; -{ - register optab binoptab = 0; - /* Use a temporary to force order of execution of calls to - `force_operand'. */ - rtx tmp; - register rtx op2; - /* Use subtarget as the target for operand 0 of a binary operation. */ - register rtx subtarget = (target != 0 && GET_CODE (target) == REG ? target : 0); - - if (GET_CODE (value) == PLUS) - binoptab = add_optab; - else if (GET_CODE (value) == MINUS) - binoptab = sub_optab; - else if (GET_CODE (value) == MULT) - { - op2 = XEXP (value, 1); - if (!CONSTANT_P (op2) - && !(GET_CODE (op2) == REG && op2 != subtarget)) - subtarget = 0; - tmp = force_operand (XEXP (value, 0), subtarget); - return expand_mult (GET_MODE (value), tmp, - force_operand (op2, NULL_RTX), - target, 0); - } - - if (binoptab) - { - op2 = XEXP (value, 1); - if (!CONSTANT_P (op2) - && !(GET_CODE (op2) == REG && op2 != subtarget)) - subtarget = 0; - if (binoptab == sub_optab && GET_CODE (op2) == CONST_INT) - { - binoptab = add_optab; - op2 = negate_rtx (GET_MODE (value), op2); - } - - /* Check for an addition with OP2 a constant integer and our first - operand a PLUS of a virtual register and something else. In that - case, we want to emit the sum of the virtual register and the - constant first and then add the other value. This allows virtual - register instantiation to simply modify the constant rather than - creating another one around this addition. */ - if (binoptab == add_optab && GET_CODE (op2) == CONST_INT - && GET_CODE (XEXP (value, 0)) == PLUS - && GET_CODE (XEXP (XEXP (value, 0), 0)) == REG - && REGNO (XEXP (XEXP (value, 0), 0)) >= FIRST_VIRTUAL_REGISTER - && REGNO (XEXP (XEXP (value, 0), 0)) <= LAST_VIRTUAL_REGISTER) - { - rtx temp = expand_binop (GET_MODE (value), binoptab, - XEXP (XEXP (value, 0), 0), op2, - subtarget, 0, OPTAB_LIB_WIDEN); - return expand_binop (GET_MODE (value), binoptab, temp, - force_operand (XEXP (XEXP (value, 0), 1), 0), - target, 0, OPTAB_LIB_WIDEN); - } - - tmp = force_operand (XEXP (value, 0), subtarget); - return expand_binop (GET_MODE (value), binoptab, tmp, - force_operand (op2, NULL_RTX), - target, 0, OPTAB_LIB_WIDEN); - /* We give UNSIGNEDP = 0 to expand_binop - because the only operations we are expanding here are signed ones. */ - } - return value; -} - -/* Subroutine of expand_expr: - save the non-copied parts (LIST) of an expr (LHS), and return a list - which can restore these values to their previous values, - should something modify their storage. */ - -static tree -save_noncopied_parts (lhs, list) - tree lhs; - tree list; -{ - tree tail; - tree parts = 0; - - for (tail = list; tail; tail = TREE_CHAIN (tail)) - if (TREE_CODE (TREE_VALUE (tail)) == TREE_LIST) - parts = chainon (parts, save_noncopied_parts (lhs, TREE_VALUE (tail))); - else - { - tree part = TREE_VALUE (tail); - tree part_type = TREE_TYPE (part); - tree to_be_saved = build (COMPONENT_REF, part_type, lhs, part); - rtx target = assign_stack_temp (TYPE_MODE (part_type), - int_size_in_bytes (part_type), 0); - if (! memory_address_p (TYPE_MODE (part_type), XEXP (target, 0))) - target = change_address (target, TYPE_MODE (part_type), NULL_RTX); - parts = tree_cons (to_be_saved, - build (RTL_EXPR, part_type, NULL_TREE, - (tree) target), - parts); - store_expr (TREE_PURPOSE (parts), RTL_EXPR_RTL (TREE_VALUE (parts)), 0); - } - return parts; -} - -/* Subroutine of expand_expr: - record the non-copied parts (LIST) of an expr (LHS), and return a list - which specifies the initial values of these parts. */ - -static tree -init_noncopied_parts (lhs, list) - tree lhs; - tree list; -{ - tree tail; - tree parts = 0; - - for (tail = list; tail; tail = TREE_CHAIN (tail)) - if (TREE_CODE (TREE_VALUE (tail)) == TREE_LIST) - parts = chainon (parts, init_noncopied_parts (lhs, TREE_VALUE (tail))); - else - { - tree part = TREE_VALUE (tail); - tree part_type = TREE_TYPE (part); - tree to_be_initialized = build (COMPONENT_REF, part_type, lhs, part); - parts = tree_cons (TREE_PURPOSE (tail), to_be_initialized, parts); - } - return parts; -} - -/* Subroutine of expand_expr: return nonzero iff there is no way that - EXP can reference X, which is being modified. */ - -static int -safe_from_p (x, exp) - rtx x; - tree exp; -{ - rtx exp_rtl = 0; - int i, nops; - - if (x == 0) - return 1; - - /* If this is a subreg of a hard register, declare it unsafe, otherwise, - find the underlying pseudo. */ - if (GET_CODE (x) == SUBREG) - { - x = SUBREG_REG (x); - if (GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER) - return 0; - } - - /* If X is a location in the outgoing argument area, it is always safe. */ - if (GET_CODE (x) == MEM - && (XEXP (x, 0) == virtual_outgoing_args_rtx - || (GET_CODE (XEXP (x, 0)) == PLUS - && XEXP (XEXP (x, 0), 0) == virtual_outgoing_args_rtx))) - return 1; - - switch (TREE_CODE_CLASS (TREE_CODE (exp))) - { - case 'd': - exp_rtl = DECL_RTL (exp); - break; - - case 'c': - return 1; - - case 'x': - if (TREE_CODE (exp) == TREE_LIST) - return ((TREE_VALUE (exp) == 0 - || safe_from_p (x, TREE_VALUE (exp))) - && (TREE_CHAIN (exp) == 0 - || safe_from_p (x, TREE_CHAIN (exp)))); - else - return 0; - - case '1': - return safe_from_p (x, TREE_OPERAND (exp, 0)); - - case '2': - case '<': - return (safe_from_p (x, TREE_OPERAND (exp, 0)) - && safe_from_p (x, TREE_OPERAND (exp, 1))); - - case 'e': - case 'r': - /* Now do code-specific tests. EXP_RTL is set to any rtx we find in - the expression. If it is set, we conflict iff we are that rtx or - both are in memory. Otherwise, we check all operands of the - expression recursively. */ - - switch (TREE_CODE (exp)) - { - case ADDR_EXPR: - return staticp (TREE_OPERAND (exp, 0)); - - case INDIRECT_REF: - if (GET_CODE (x) == MEM) - return 0; - break; - - case CALL_EXPR: - exp_rtl = CALL_EXPR_RTL (exp); - if (exp_rtl == 0) - { - /* Assume that the call will clobber all hard registers and - all of memory. */ - if ((GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER) - || GET_CODE (x) == MEM) - return 0; - } - - break; - - case RTL_EXPR: - exp_rtl = RTL_EXPR_RTL (exp); - if (exp_rtl == 0) - /* We don't know what this can modify. */ - return 0; - - break; - - case WITH_CLEANUP_EXPR: - exp_rtl = RTL_EXPR_RTL (exp); - break; - - case SAVE_EXPR: - exp_rtl = SAVE_EXPR_RTL (exp); - break; - - case BIND_EXPR: - /* The only operand we look at is operand 1. The rest aren't - part of the expression. */ - return safe_from_p (x, TREE_OPERAND (exp, 1)); - - case METHOD_CALL_EXPR: - /* This takes a rtx argument, but shouldn't appear here. */ - abort (); - } - - /* If we have an rtx, we do not need to scan our operands. */ - if (exp_rtl) - break; - - nops = tree_code_length[(int) TREE_CODE (exp)]; - for (i = 0; i < nops; i++) - if (TREE_OPERAND (exp, i) != 0 - && ! safe_from_p (x, TREE_OPERAND (exp, i))) - return 0; - } - - /* If we have an rtl, find any enclosed object. Then see if we conflict - with it. */ - if (exp_rtl) - { - if (GET_CODE (exp_rtl) == SUBREG) - { - exp_rtl = SUBREG_REG (exp_rtl); - if (GET_CODE (exp_rtl) == REG - && REGNO (exp_rtl) < FIRST_PSEUDO_REGISTER) - return 0; - } - - /* If the rtl is X, then it is not safe. Otherwise, it is unless both - are memory and EXP is not readonly. */ - return ! (rtx_equal_p (x, exp_rtl) - || (GET_CODE (x) == MEM && GET_CODE (exp_rtl) == MEM - && ! TREE_READONLY (exp))); - } - - /* If we reach here, it is safe. */ - return 1; -} - -/* Subroutine of expand_expr: return nonzero iff EXP is an - expression whose type is statically determinable. */ - -static int -fixed_type_p (exp) - tree exp; -{ - if (TREE_CODE (exp) == PARM_DECL - || TREE_CODE (exp) == VAR_DECL - || TREE_CODE (exp) == CALL_EXPR || TREE_CODE (exp) == TARGET_EXPR - || TREE_CODE (exp) == COMPONENT_REF - || TREE_CODE (exp) == ARRAY_REF) - return 1; - return 0; -} - -/* expand_expr: generate code for computing expression EXP. - An rtx for the computed value is returned. The value is never null. - In the case of a void EXP, const0_rtx is returned. - - The value may be stored in TARGET if TARGET is nonzero. - TARGET is just a suggestion; callers must assume that - the rtx returned may not be the same as TARGET. - - If TARGET is CONST0_RTX, it means that the value will be ignored. - - If TMODE is not VOIDmode, it suggests generating the - result in mode TMODE. But this is done only when convenient. - Otherwise, TMODE is ignored and the value generated in its natural mode. - TMODE is just a suggestion; callers must assume that - the rtx returned may not have mode TMODE. - - EXPAND_CONST_ADDRESS says that it is okay to return a MEM - with a constant address even if that address is not normally legitimate. - EXPAND_INITIALIZER and EXPAND_SUM also have this effect. - - If MODIFIER is EXPAND_SUM then when EXP is an addition - we can return an rtx of the form (MULT (REG ...) (CONST_INT ...)) - or a nest of (PLUS ...) and (MINUS ...) where the terms are - products as above, or REG or MEM, or constant. - Ordinarily in such cases we would output mul or add instructions - and then return a pseudo reg containing the sum. - - EXPAND_INITIALIZER is much like EXPAND_SUM except that - it also marks a label as absolutely required (it can't be dead). - It also makes a ZERO_EXTEND or SIGN_EXTEND instead of emitting extend insns. - This is used for outputting expressions used in initializers. */ - -rtx -expand_expr (exp, target, tmode, modifier) - register tree exp; - rtx target; - enum machine_mode tmode; - enum expand_modifier modifier; -{ - register rtx op0, op1, temp; - tree type = TREE_TYPE (exp); - int unsignedp = TREE_UNSIGNED (type); - register enum machine_mode mode = TYPE_MODE (type); - register enum tree_code code = TREE_CODE (exp); - optab this_optab; - /* Use subtarget as the target for operand 0 of a binary operation. */ - rtx subtarget = (target != 0 && GET_CODE (target) == REG ? target : 0); - rtx original_target = target; - int ignore = target == const0_rtx; - tree context; - - /* Don't use hard regs as subtargets, because the combiner - can only handle pseudo regs. */ - if (subtarget && REGNO (subtarget) < FIRST_PSEUDO_REGISTER) - subtarget = 0; - /* Avoid subtargets inside loops, - since they hide some invariant expressions. */ - if (preserve_subexpressions_p ()) - subtarget = 0; - - if (ignore) target = 0, original_target = 0; - - /* If will do cse, generate all results into pseudo registers - since 1) that allows cse to find more things - and 2) otherwise cse could produce an insn the machine - cannot support. */ - - if (! cse_not_expected && mode != BLKmode && target - && (GET_CODE (target) != REG || REGNO (target) < FIRST_PSEUDO_REGISTER)) - target = subtarget; - - /* Ensure we reference a volatile object even if value is ignored. */ - if (ignore && TREE_THIS_VOLATILE (exp) - && TREE_CODE (exp) != FUNCTION_DECL - && mode != VOIDmode && mode != BLKmode) - { - target = gen_reg_rtx (mode); - temp = expand_expr (exp, target, VOIDmode, modifier); - if (temp != target) - emit_move_insn (target, temp); - return target; - } - - switch (code) - { - case LABEL_DECL: - { - tree function = decl_function_context (exp); - /* Handle using a label in a containing function. */ - if (function != current_function_decl && function != 0) - { - struct function *p = find_function_data (function); - /* Allocate in the memory associated with the function - that the label is in. */ - push_obstacks (p->function_obstack, - p->function_maybepermanent_obstack); - - p->forced_labels = gen_rtx (EXPR_LIST, VOIDmode, - label_rtx (exp), p->forced_labels); - pop_obstacks (); - } - else if (modifier == EXPAND_INITIALIZER) - forced_labels = gen_rtx (EXPR_LIST, VOIDmode, - label_rtx (exp), forced_labels); - temp = gen_rtx (MEM, FUNCTION_MODE, - gen_rtx (LABEL_REF, Pmode, label_rtx (exp))); - if (function != current_function_decl && function != 0) - LABEL_REF_NONLOCAL_P (XEXP (temp, 0)) = 1; - return temp; - } - - case PARM_DECL: - if (DECL_RTL (exp) == 0) - { - error_with_decl (exp, "prior parameter's size depends on `%s'"); - return CONST0_RTX (mode); - } - - case FUNCTION_DECL: - case VAR_DECL: - case RESULT_DECL: - if (DECL_RTL (exp) == 0) - abort (); - /* Ensure variable marked as used - even if it doesn't go through a parser. */ - TREE_USED (exp) = 1; - /* Handle variables inherited from containing functions. */ - context = decl_function_context (exp); - - /* We treat inline_function_decl as an alias for the current function - because that is the inline function whose vars, types, etc. - are being merged into the current function. - See expand_inline_function. */ - if (context != 0 && context != current_function_decl - && context != inline_function_decl - /* If var is static, we don't need a static chain to access it. */ - && ! (GET_CODE (DECL_RTL (exp)) == MEM - && CONSTANT_P (XEXP (DECL_RTL (exp), 0)))) - { - rtx addr; - - /* Mark as non-local and addressable. */ - DECL_NONLOCAL (exp) = 1; - mark_addressable (exp); - if (GET_CODE (DECL_RTL (exp)) != MEM) - abort (); - addr = XEXP (DECL_RTL (exp), 0); - if (GET_CODE (addr) == MEM) - addr = gen_rtx (MEM, Pmode, fix_lexical_addr (XEXP (addr, 0), exp)); - else - addr = fix_lexical_addr (addr, exp); - return change_address (DECL_RTL (exp), mode, addr); - } - - /* This is the case of an array whose size is to be determined - from its initializer, while the initializer is still being parsed. - See expand_decl. */ - if (GET_CODE (DECL_RTL (exp)) == MEM - && GET_CODE (XEXP (DECL_RTL (exp), 0)) == REG) - return change_address (DECL_RTL (exp), GET_MODE (DECL_RTL (exp)), - XEXP (DECL_RTL (exp), 0)); - if (GET_CODE (DECL_RTL (exp)) == MEM - && modifier != EXPAND_CONST_ADDRESS - && modifier != EXPAND_SUM - && modifier != EXPAND_INITIALIZER) - { - /* DECL_RTL probably contains a constant address. - On RISC machines where a constant address isn't valid, - make some insns to get that address into a register. */ - if (!memory_address_p (DECL_MODE (exp), XEXP (DECL_RTL (exp), 0)) - || (flag_force_addr - && CONSTANT_ADDRESS_P (XEXP (DECL_RTL (exp), 0)))) - return change_address (DECL_RTL (exp), VOIDmode, - copy_rtx (XEXP (DECL_RTL (exp), 0))); - } - - /* If the mode of DECL_RTL does not match that of the decl, it - must be a promoted value. We return a SUBREG of the wanted mode, - but mark it so that we know that it was already extended. */ - - if (GET_CODE (DECL_RTL (exp)) == REG - && GET_MODE (DECL_RTL (exp)) != mode) - { - enum machine_mode decl_mode = DECL_MODE (exp); - - /* Get the signedness used for this variable. Ensure we get the - same mode we got when the variable was declared. */ - - PROMOTE_MODE (decl_mode, unsignedp, type); - - if (decl_mode != GET_MODE (DECL_RTL (exp))) - abort (); - - temp = gen_rtx (SUBREG, mode, DECL_RTL (exp), 0); - SUBREG_PROMOTED_VAR_P (temp) = 1; - SUBREG_PROMOTED_UNSIGNED_P (temp) = unsignedp; - return temp; - } - - return DECL_RTL (exp); - - case INTEGER_CST: - return immed_double_const (TREE_INT_CST_LOW (exp), - TREE_INT_CST_HIGH (exp), - mode); - - case CONST_DECL: - return expand_expr (DECL_INITIAL (exp), target, VOIDmode, 0); - - case REAL_CST: - /* If optimized, generate immediate CONST_DOUBLE - which will be turned into memory by reload if necessary. - - We used to force a register so that loop.c could see it. But - this does not allow gen_* patterns to perform optimizations with - the constants. It also produces two insns in cases like "x = 1.0;". - On most machines, floating-point constants are not permitted in - many insns, so we'd end up copying it to a register in any case. - - Now, we do the copying in expand_binop, if appropriate. */ - return immed_real_const (exp); - - case COMPLEX_CST: - case STRING_CST: - if (! TREE_CST_RTL (exp)) - output_constant_def (exp); - - /* TREE_CST_RTL probably contains a constant address. - On RISC machines where a constant address isn't valid, - make some insns to get that address into a register. */ - if (GET_CODE (TREE_CST_RTL (exp)) == MEM - && modifier != EXPAND_CONST_ADDRESS - && modifier != EXPAND_INITIALIZER - && modifier != EXPAND_SUM - && !memory_address_p (mode, XEXP (TREE_CST_RTL (exp), 0))) - return change_address (TREE_CST_RTL (exp), VOIDmode, - copy_rtx (XEXP (TREE_CST_RTL (exp), 0))); - return TREE_CST_RTL (exp); - - case SAVE_EXPR: - context = decl_function_context (exp); - /* We treat inline_function_decl as an alias for the current function - because that is the inline function whose vars, types, etc. - are being merged into the current function. - See expand_inline_function. */ - if (context == current_function_decl || context == inline_function_decl) - context = 0; - - /* If this is non-local, handle it. */ - if (context) - { - temp = SAVE_EXPR_RTL (exp); - if (temp && GET_CODE (temp) == REG) - { - put_var_into_stack (exp); - temp = SAVE_EXPR_RTL (exp); - } - if (temp == 0 || GET_CODE (temp) != MEM) - abort (); - return change_address (temp, mode, - fix_lexical_addr (XEXP (temp, 0), exp)); - } - if (SAVE_EXPR_RTL (exp) == 0) - { - if (mode == BLKmode) - temp - = assign_stack_temp (mode, - int_size_in_bytes (TREE_TYPE (exp)), 0); - else - { - enum machine_mode var_mode = mode; - - if (TREE_CODE (type) == INTEGER_TYPE - || TREE_CODE (type) == ENUMERAL_TYPE - || TREE_CODE (type) == BOOLEAN_TYPE - || TREE_CODE (type) == CHAR_TYPE - || TREE_CODE (type) == REAL_TYPE - || TREE_CODE (type) == POINTER_TYPE - || TREE_CODE (type) == OFFSET_TYPE) - { - PROMOTE_MODE (var_mode, unsignedp, type); - } - - temp = gen_reg_rtx (var_mode); - } - - SAVE_EXPR_RTL (exp) = temp; - if (!optimize && GET_CODE (temp) == REG) - save_expr_regs = gen_rtx (EXPR_LIST, VOIDmode, temp, - save_expr_regs); - - /* If the mode of TEMP does not match that of the expression, it - must be a promoted value. We pass store_expr a SUBREG of the - wanted mode but mark it so that we know that it was already - extended. Note that `unsignedp' was modified above in - this case. */ - - if (GET_CODE (temp) == REG && GET_MODE (temp) != mode) - { - temp = gen_rtx (SUBREG, mode, SAVE_EXPR_RTL (exp), 0); - SUBREG_PROMOTED_VAR_P (temp) = 1; - SUBREG_PROMOTED_UNSIGNED_P (temp) = unsignedp; - } - - store_expr (TREE_OPERAND (exp, 0), temp, 0); - } - - /* If the mode of SAVE_EXPR_RTL does not match that of the expression, it - must be a promoted value. We return a SUBREG of the wanted mode, - but mark it so that we know that it was already extended. Note - that `unsignedp' was modified above in this case. */ - - if (GET_CODE (SAVE_EXPR_RTL (exp)) == REG - && GET_MODE (SAVE_EXPR_RTL (exp)) != mode) - { - temp = gen_rtx (SUBREG, mode, SAVE_EXPR_RTL (exp), 0); - SUBREG_PROMOTED_VAR_P (temp) = 1; - SUBREG_PROMOTED_UNSIGNED_P (temp) = unsignedp; - return temp; - } - - return SAVE_EXPR_RTL (exp); - - case EXIT_EXPR: - /* Exit the current loop if the body-expression is true. */ - { - rtx label = gen_label_rtx (); - do_jump (TREE_OPERAND (exp, 0), label, NULL_RTX); - expand_exit_loop (NULL_PTR); - emit_label (label); - } - return const0_rtx; - - case LOOP_EXPR: - expand_start_loop (1); - expand_expr_stmt (TREE_OPERAND (exp, 0)); - expand_end_loop (); - - return const0_rtx; - - case BIND_EXPR: - { - tree vars = TREE_OPERAND (exp, 0); - int vars_need_expansion = 0; - - /* Need to open a binding contour here because - if there are any cleanups they most be contained here. */ - expand_start_bindings (0); - - /* Mark the corresponding BLOCK for output in its proper place. */ - if (TREE_OPERAND (exp, 2) != 0 - && ! TREE_USED (TREE_OPERAND (exp, 2))) - insert_block (TREE_OPERAND (exp, 2)); - - /* If VARS have not yet been expanded, expand them now. */ - while (vars) - { - if (DECL_RTL (vars) == 0) - { - vars_need_expansion = 1; - expand_decl (vars); - } - expand_decl_init (vars); - vars = TREE_CHAIN (vars); - } - - temp = expand_expr (TREE_OPERAND (exp, 1), target, tmode, modifier); - - expand_end_bindings (TREE_OPERAND (exp, 0), 0, 0); - - return temp; - } - - case RTL_EXPR: - if (RTL_EXPR_SEQUENCE (exp) == const0_rtx) - abort (); - emit_insns (RTL_EXPR_SEQUENCE (exp)); - RTL_EXPR_SEQUENCE (exp) = const0_rtx; - return RTL_EXPR_RTL (exp); - - case CONSTRUCTOR: - /* All elts simple constants => refer to a constant in memory. But - if this is a non-BLKmode mode, let it store a field at a time - since that should make a CONST_INT or CONST_DOUBLE when we - fold. */ - if (TREE_STATIC (exp) && (mode == BLKmode || TREE_ADDRESSABLE (exp))) - { - rtx constructor = output_constant_def (exp); - if (modifier != EXPAND_CONST_ADDRESS - && modifier != EXPAND_INITIALIZER - && modifier != EXPAND_SUM - && !memory_address_p (GET_MODE (constructor), - XEXP (constructor, 0))) - constructor = change_address (constructor, VOIDmode, - XEXP (constructor, 0)); - return constructor; - } - - if (ignore) - { - tree elt; - for (elt = CONSTRUCTOR_ELTS (exp); elt; elt = TREE_CHAIN (elt)) - expand_expr (TREE_VALUE (elt), const0_rtx, VOIDmode, 0); - return const0_rtx; - } - else - { - if (target == 0 || ! safe_from_p (target, exp)) - { - if (mode != BLKmode && ! TREE_ADDRESSABLE (exp)) - target = gen_reg_rtx (mode); - else - { - enum tree_code c = TREE_CODE (type); - target - = assign_stack_temp (mode, int_size_in_bytes (type), 0); - if (c == RECORD_TYPE || c == UNION_TYPE - || c == QUAL_UNION_TYPE || c == ARRAY_TYPE) - MEM_IN_STRUCT_P (target) = 1; - } - } - store_constructor (exp, target); - return target; - } - - case INDIRECT_REF: - { - tree exp1 = TREE_OPERAND (exp, 0); - tree exp2; - - /* A SAVE_EXPR as the address in an INDIRECT_EXPR is generated - for *PTR += ANYTHING where PTR is put inside the SAVE_EXPR. - This code has the same general effect as simply doing - expand_expr on the save expr, except that the expression PTR - is computed for use as a memory address. This means different - code, suitable for indexing, may be generated. */ - if (TREE_CODE (exp1) == SAVE_EXPR - && SAVE_EXPR_RTL (exp1) == 0 - && TREE_CODE (exp2 = TREE_OPERAND (exp1, 0)) != ERROR_MARK - && TYPE_MODE (TREE_TYPE (exp1)) == Pmode - && TYPE_MODE (TREE_TYPE (exp2)) == Pmode) - { - temp = expand_expr (TREE_OPERAND (exp1, 0), NULL_RTX, - VOIDmode, EXPAND_SUM); - op0 = memory_address (mode, temp); - op0 = copy_all_regs (op0); - SAVE_EXPR_RTL (exp1) = op0; - } - else - { - op0 = expand_expr (exp1, NULL_RTX, VOIDmode, EXPAND_SUM); - op0 = memory_address (mode, op0); - } - - temp = gen_rtx (MEM, mode, op0); - /* If address was computed by addition, - mark this as an element of an aggregate. */ - if (TREE_CODE (TREE_OPERAND (exp, 0)) == PLUS_EXPR - || (TREE_CODE (TREE_OPERAND (exp, 0)) == SAVE_EXPR - && TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) == PLUS_EXPR) - || TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE - || TREE_CODE (TREE_TYPE (exp)) == QUAL_UNION_TYPE - || (TREE_CODE (exp1) == ADDR_EXPR - && (exp2 = TREE_OPERAND (exp1, 0)) - && (TREE_CODE (TREE_TYPE (exp2)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE (exp2)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (exp2)) == UNION_TYPE - || TREE_CODE (TREE_TYPE (exp2)) == QUAL_UNION_TYPE))) - MEM_IN_STRUCT_P (temp) = 1; - MEM_VOLATILE_P (temp) = TREE_THIS_VOLATILE (exp); -#if 0 /* It is incorrect to set RTX_UNCHANGING_P here, because the fact that - a location is accessed through a pointer to const does not mean - that the value there can never change. */ - RTX_UNCHANGING_P (temp) = TREE_READONLY (exp); -#endif - return temp; - } - - case ARRAY_REF: - if (TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 0))) != ARRAY_TYPE) - abort (); - - { - tree array = TREE_OPERAND (exp, 0); - tree domain = TYPE_DOMAIN (TREE_TYPE (array)); - tree low_bound = domain ? TYPE_MIN_VALUE (domain) : integer_zero_node; - tree index = TREE_OPERAND (exp, 1); - tree index_type = TREE_TYPE (index); - int i; - - /* Optimize the special-case of a zero lower bound. */ - if (! integer_zerop (low_bound)) - index = fold (build (MINUS_EXPR, index_type, index, low_bound)); - - if (TREE_CODE (index) != INTEGER_CST - || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) - { - /* Nonconstant array index or nonconstant element size. - Generate the tree for *(&array+index) and expand that, - except do it in a language-independent way - and don't complain about non-lvalue arrays. - `mark_addressable' should already have been called - for any array for which this case will be reached. */ - - /* Don't forget the const or volatile flag from the array - element. */ - tree variant_type = build_type_variant (type, - TREE_READONLY (exp), - TREE_THIS_VOLATILE (exp)); - tree array_adr = build1 (ADDR_EXPR, - build_pointer_type (variant_type), array); - tree elt; - - /* Convert the integer argument to a type the same size as a - pointer so the multiply won't overflow spuriously. */ - if (TYPE_PRECISION (index_type) != POINTER_SIZE) - index = convert (type_for_size (POINTER_SIZE, 0), index); - - /* Don't think the address has side effects - just because the array does. - (In some cases the address might have side effects, - and we fail to record that fact here. However, it should not - matter, since expand_expr should not care.) */ - TREE_SIDE_EFFECTS (array_adr) = 0; - - elt = build1 (INDIRECT_REF, type, - fold (build (PLUS_EXPR, - TYPE_POINTER_TO (variant_type), - array_adr, - fold (build (MULT_EXPR, - TYPE_POINTER_TO (variant_type), - index, - size_in_bytes (type)))))); - - /* Volatility, etc., of new expression is same as old - expression. */ - TREE_SIDE_EFFECTS (elt) = TREE_SIDE_EFFECTS (exp); - TREE_THIS_VOLATILE (elt) = TREE_THIS_VOLATILE (exp); - TREE_READONLY (elt) = TREE_READONLY (exp); - - return expand_expr (elt, target, tmode, modifier); - } - - /* Fold an expression like: "foo"[2]. - This is not done in fold so it won't happen inside &. */ - - if (TREE_CODE (array) == STRING_CST - && TREE_CODE (index) == INTEGER_CST - && !TREE_INT_CST_HIGH (index) - && (i = TREE_INT_CST_LOW (index)) < TREE_STRING_LENGTH (array)) - { - if (TREE_TYPE (TREE_TYPE (array)) == integer_type_node) - { - exp = build_int_2 (((int *)TREE_STRING_POINTER (array))[i], 0); - TREE_TYPE (exp) = integer_type_node; - return expand_expr (exp, target, tmode, modifier); - } - if (TREE_TYPE (TREE_TYPE (array)) == char_type_node) - { - exp = build_int_2 (TREE_STRING_POINTER (array)[i], 0); - TREE_TYPE (exp) = integer_type_node; - return expand_expr (convert (TREE_TYPE (TREE_TYPE (array)), - exp), - target, tmode, modifier); - } - } - - /* If this is a constant index into a constant array, - just get the value from the array. Handle both the cases when - we have an explicit constructor and when our operand is a variable - that was declared const. */ - - if (TREE_CODE (array) == CONSTRUCTOR && ! TREE_SIDE_EFFECTS (array)) - { - if (TREE_CODE (index) == INTEGER_CST - && TREE_INT_CST_HIGH (index) == 0) - { - tree elem = CONSTRUCTOR_ELTS (TREE_OPERAND (exp, 0)); - - i = TREE_INT_CST_LOW (index); - while (elem && i--) - elem = TREE_CHAIN (elem); - if (elem) - return expand_expr (fold (TREE_VALUE (elem)), target, - tmode, modifier); - } - } - - else if (optimize >= 1 - && TREE_READONLY (array) && ! TREE_SIDE_EFFECTS (array) - && TREE_CODE (array) == VAR_DECL && DECL_INITIAL (array) - && TREE_CODE (DECL_INITIAL (array)) != ERROR_MARK) - { - if (TREE_CODE (index) == INTEGER_CST - && TREE_INT_CST_HIGH (index) == 0) - { - tree init = DECL_INITIAL (array); - - i = TREE_INT_CST_LOW (index); - if (TREE_CODE (init) == CONSTRUCTOR) - { - tree elem = CONSTRUCTOR_ELTS (init); - - while (elem && i--) - elem = TREE_CHAIN (elem); - if (elem) - return expand_expr (fold (TREE_VALUE (elem)), target, - tmode, modifier); - } - else if (TREE_CODE (init) == STRING_CST - && i < TREE_STRING_LENGTH (init)) - { - temp = GEN_INT (TREE_STRING_POINTER (init)[i]); - return convert_to_mode (mode, temp, 0); - } - } - } - } - - /* Treat array-ref with constant index as a component-ref. */ - - case COMPONENT_REF: - case BIT_FIELD_REF: - /* If the operand is a CONSTRUCTOR, we can just extract the - appropriate field if it is present. */ - if (code != ARRAY_REF - && TREE_CODE (TREE_OPERAND (exp, 0)) == CONSTRUCTOR) - { - tree elt; - - for (elt = CONSTRUCTOR_ELTS (TREE_OPERAND (exp, 0)); elt; - elt = TREE_CHAIN (elt)) - if (TREE_PURPOSE (elt) == TREE_OPERAND (exp, 1)) - return expand_expr (TREE_VALUE (elt), target, tmode, modifier); - } - - { - enum machine_mode mode1; - int bitsize; - int bitpos; - tree offset; - int volatilep = 0; - tree tem = get_inner_reference (exp, &bitsize, &bitpos, &offset, - &mode1, &unsignedp, &volatilep); - - /* If we got back the original object, something is wrong. Perhaps - we are evaluating an expression too early. In any event, don't - infinitely recurse. */ - if (tem == exp) - abort (); - - /* In some cases, we will be offsetting OP0's address by a constant. - So get it as a sum, if possible. If we will be using it - directly in an insn, we validate it. */ - op0 = expand_expr (tem, NULL_RTX, VOIDmode, EXPAND_SUM); - - /* If this is a constant, put it into a register if it is a - legitimate constant and memory if it isn't. */ - if (CONSTANT_P (op0)) - { - enum machine_mode mode = TYPE_MODE (TREE_TYPE (tem)); - if (mode != BLKmode && LEGITIMATE_CONSTANT_P (op0)) - op0 = force_reg (mode, op0); - else - op0 = validize_mem (force_const_mem (mode, op0)); - } - - if (offset != 0) - { - rtx offset_rtx = expand_expr (offset, NULL_RTX, VOIDmode, 0); - - if (GET_CODE (op0) != MEM) - abort (); - op0 = change_address (op0, VOIDmode, - gen_rtx (PLUS, Pmode, XEXP (op0, 0), - force_reg (Pmode, offset_rtx))); - } - - /* Don't forget about volatility even if this is a bitfield. */ - if (GET_CODE (op0) == MEM && volatilep && ! MEM_VOLATILE_P (op0)) - { - op0 = copy_rtx (op0); - MEM_VOLATILE_P (op0) = 1; - } - - if (mode1 == VOIDmode - || (mode1 != BLKmode && ! direct_load[(int) mode1] - && modifier != EXPAND_CONST_ADDRESS - && modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER) - || GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) - { - /* In cases where an aligned union has an unaligned object - as a field, we might be extracting a BLKmode value from - an integer-mode (e.g., SImode) object. Handle this case - by doing the extract into an object as wide as the field - (which we know to be the width of a basic mode), then - storing into memory, and changing the mode to BLKmode. */ - enum machine_mode ext_mode = mode; - - if (ext_mode == BLKmode) - ext_mode = mode_for_size (bitsize, MODE_INT, 1); - - if (ext_mode == BLKmode) - abort (); - - op0 = extract_bit_field (validize_mem (op0), bitsize, bitpos, - unsignedp, target, ext_mode, ext_mode, - TYPE_ALIGN (TREE_TYPE (tem)) / BITS_PER_UNIT, - int_size_in_bytes (TREE_TYPE (tem))); - if (mode == BLKmode) - { - rtx new = assign_stack_temp (ext_mode, - bitsize / BITS_PER_UNIT, 0); - - emit_move_insn (new, op0); - op0 = copy_rtx (new); - PUT_MODE (op0, BLKmode); - } - - return op0; - } - - /* Get a reference to just this component. */ - if (modifier == EXPAND_CONST_ADDRESS - || modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER) - op0 = gen_rtx (MEM, mode1, plus_constant (XEXP (op0, 0), - (bitpos / BITS_PER_UNIT))); - else - op0 = change_address (op0, mode1, - plus_constant (XEXP (op0, 0), - (bitpos / BITS_PER_UNIT))); - MEM_IN_STRUCT_P (op0) = 1; - MEM_VOLATILE_P (op0) |= volatilep; - if (mode == mode1 || mode1 == BLKmode || mode1 == tmode) - return op0; - if (target == 0) - target = gen_reg_rtx (tmode != VOIDmode ? tmode : mode); - convert_move (target, op0, unsignedp); - return target; - } - - case OFFSET_REF: - { - tree base = build1 (ADDR_EXPR, type, TREE_OPERAND (exp, 0)); - tree addr = build (PLUS_EXPR, type, base, TREE_OPERAND (exp, 1)); - op0 = expand_expr (addr, NULL_RTX, VOIDmode, EXPAND_SUM); - temp = gen_rtx (MEM, mode, memory_address (mode, op0)); - MEM_IN_STRUCT_P (temp) = 1; - MEM_VOLATILE_P (temp) = TREE_THIS_VOLATILE (exp); -#if 0 /* It is incorrect to set RTX_UNCHANGING_P here, because the fact that - a location is accessed through a pointer to const does not mean - that the value there can never change. */ - RTX_UNCHANGING_P (temp) = TREE_READONLY (exp); -#endif - return temp; - } - - /* Intended for a reference to a buffer of a file-object in Pascal. - But it's not certain that a special tree code will really be - necessary for these. INDIRECT_REF might work for them. */ - case BUFFER_REF: - abort (); - - /* IN_EXPR: Inlined pascal set IN expression. - - Algorithm: - rlo = set_low - (set_low%bits_per_word); - the_word = set [ (index - rlo)/bits_per_word ]; - bit_index = index % bits_per_word; - bitmask = 1 << bit_index; - return !!(the_word & bitmask); */ - case IN_EXPR: - preexpand_calls (exp); - { - tree set = TREE_OPERAND (exp, 0); - tree index = TREE_OPERAND (exp, 1); - tree set_type = TREE_TYPE (set); - - tree set_low_bound = TYPE_MIN_VALUE (TYPE_DOMAIN (set_type)); - tree set_high_bound = TYPE_MAX_VALUE (TYPE_DOMAIN (set_type)); - - rtx index_val; - rtx lo_r; - rtx hi_r; - rtx rlow; - rtx diff, quo, rem, addr, bit, result; - rtx setval, setaddr; - enum machine_mode index_mode = TYPE_MODE (TREE_TYPE (index)); - - if (target == 0) - target = gen_reg_rtx (mode); - - /* If domain is empty, answer is no. */ - if (tree_int_cst_lt (set_high_bound, set_low_bound)) - return const0_rtx; - - index_val = expand_expr (index, 0, VOIDmode, 0); - lo_r = expand_expr (set_low_bound, 0, VOIDmode, 0); - hi_r = expand_expr (set_high_bound, 0, VOIDmode, 0); - setval = expand_expr (set, 0, VOIDmode, 0); - setaddr = XEXP (setval, 0); - - /* Compare index against bounds, if they are constant. */ - if (GET_CODE (index_val) == CONST_INT - && GET_CODE (lo_r) == CONST_INT - && INTVAL (index_val) < INTVAL (lo_r)) - return const0_rtx; - - if (GET_CODE (index_val) == CONST_INT - && GET_CODE (hi_r) == CONST_INT - && INTVAL (hi_r) < INTVAL (index_val)) - return const0_rtx; - - /* If we get here, we have to generate the code for both cases - (in range and out of range). */ - - op0 = gen_label_rtx (); - op1 = gen_label_rtx (); - - if (! (GET_CODE (index_val) == CONST_INT - && GET_CODE (lo_r) == CONST_INT)) - { - emit_cmp_insn (index_val, lo_r, LT, NULL_RTX, - GET_MODE (index_val), 0, 0); - emit_jump_insn (gen_blt (op1)); - } - - if (! (GET_CODE (index_val) == CONST_INT - && GET_CODE (hi_r) == CONST_INT)) - { - emit_cmp_insn (index_val, hi_r, GT, NULL_RTX, - GET_MODE (index_val), 0, 0); - emit_jump_insn (gen_bgt (op1)); - } - - /* Calculate the element number of bit zero in the first word - of the set. */ - if (GET_CODE (lo_r) == CONST_INT) - rlow = GEN_INT (INTVAL (lo_r) - & ~ ((HOST_WIDE_INT) 1 << BITS_PER_UNIT)); - else - rlow = expand_binop (index_mode, and_optab, lo_r, - GEN_INT (~((HOST_WIDE_INT) 1 << BITS_PER_UNIT)), - NULL_RTX, 0, OPTAB_LIB_WIDEN); - - diff = expand_binop (index_mode, sub_optab, - index_val, rlow, NULL_RTX, 0, OPTAB_LIB_WIDEN); - - quo = expand_divmod (0, TRUNC_DIV_EXPR, index_mode, diff, - GEN_INT (BITS_PER_UNIT), NULL_RTX, 0); - rem = expand_divmod (1, TRUNC_MOD_EXPR, index_mode, index_val, - GEN_INT (BITS_PER_UNIT), NULL_RTX, 0); - addr = memory_address (byte_mode, - expand_binop (index_mode, add_optab, - diff, setaddr, NULL_RTX, 0, - OPTAB_LIB_WIDEN)); - /* Extract the bit we want to examine */ - bit = expand_shift (RSHIFT_EXPR, byte_mode, - gen_rtx (MEM, byte_mode, addr), - make_tree (TREE_TYPE (index), rem), - NULL_RTX, 1); - result = expand_binop (byte_mode, and_optab, bit, const1_rtx, - GET_MODE (target) == byte_mode ? target : 0, - 1, OPTAB_LIB_WIDEN); - - if (result != target) - convert_move (target, result, 1); - - /* Output the code to handle the out-of-range case. */ - emit_jump (op0); - emit_label (op1); - emit_move_insn (target, const0_rtx); - emit_label (op0); - return target; - } - - case WITH_CLEANUP_EXPR: - if (RTL_EXPR_RTL (exp) == 0) - { - RTL_EXPR_RTL (exp) - = expand_expr (TREE_OPERAND (exp, 0), target, tmode, modifier); - cleanups_this_call - = tree_cons (NULL_TREE, TREE_OPERAND (exp, 2), cleanups_this_call); - /* That's it for this cleanup. */ - TREE_OPERAND (exp, 2) = 0; - } - return RTL_EXPR_RTL (exp); - - case CALL_EXPR: - /* Check for a built-in function. */ - if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) == FUNCTION_DECL - && DECL_BUILT_IN (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))) - return expand_builtin (exp, target, subtarget, tmode, ignore); - /* If this call was expanded already by preexpand_calls, - just return the result we got. */ - if (CALL_EXPR_RTL (exp) != 0) - return CALL_EXPR_RTL (exp); - return expand_call (exp, target, ignore); - - case NON_LVALUE_EXPR: - case NOP_EXPR: - case CONVERT_EXPR: - case REFERENCE_EXPR: - if (TREE_CODE (type) == VOID_TYPE || ignore) - { - expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, modifier); - return const0_rtx; - } - if (mode == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) - return expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, modifier); - if (TREE_CODE (type) == UNION_TYPE) - { - tree valtype = TREE_TYPE (TREE_OPERAND (exp, 0)); - if (target == 0) - { - if (mode == BLKmode) - { - if (TYPE_SIZE (type) == 0 - || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) - abort (); - target = assign_stack_temp (BLKmode, - (TREE_INT_CST_LOW (TYPE_SIZE (type)) - + BITS_PER_UNIT - 1) - / BITS_PER_UNIT, 0); - } - else - target = gen_reg_rtx (mode); - } - if (GET_CODE (target) == MEM) - /* Store data into beginning of memory target. */ - store_expr (TREE_OPERAND (exp, 0), - change_address (target, TYPE_MODE (valtype), 0), 0); - - else if (GET_CODE (target) == REG) - /* Store this field into a union of the proper type. */ - store_field (target, GET_MODE_BITSIZE (TYPE_MODE (valtype)), 0, - TYPE_MODE (valtype), TREE_OPERAND (exp, 0), - VOIDmode, 0, 1, - int_size_in_bytes (TREE_TYPE (TREE_OPERAND (exp, 0)))); - else - abort (); - - /* Return the entire union. */ - return target; - } - op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, mode, 0); - if (GET_MODE (op0) == mode) - return op0; - /* If arg is a constant integer being extended from a narrower mode, - we must really truncate to get the extended bits right. Otherwise - (unsigned long) (unsigned char) ("\377"[0]) - would come out as ffffffff. */ - if (GET_MODE (op0) == VOIDmode - && (GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) - < GET_MODE_BITSIZE (mode))) - { - /* MODE must be narrower than HOST_BITS_PER_INT. */ - int width = GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))); - - if (width < HOST_BITS_PER_WIDE_INT) - { - HOST_WIDE_INT val = (GET_CODE (op0) == CONST_INT ? INTVAL (op0) - : CONST_DOUBLE_LOW (op0)); - if (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))) - || !(val & ((HOST_WIDE_INT) 1 << (width - 1)))) - val &= ((HOST_WIDE_INT) 1 << width) - 1; - else - val |= ~(((HOST_WIDE_INT) 1 << width) - 1); - - op0 = GEN_INT (val); - } - else - { - op0 = (simplify_unary_operation - ((TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))) - ? ZERO_EXTEND : SIGN_EXTEND), - mode, op0, - TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))); - if (op0 == 0) - abort (); - } - } - if (GET_MODE (op0) == VOIDmode) - return op0; - if (modifier == EXPAND_INITIALIZER) - return gen_rtx (unsignedp ? ZERO_EXTEND : SIGN_EXTEND, mode, op0); - if (flag_force_mem && GET_CODE (op0) == MEM) - op0 = copy_to_reg (op0); - - if (target == 0) - return convert_to_mode (mode, op0, TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0)))); - else - convert_move (target, op0, TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0)))); - return target; - - case PLUS_EXPR: - /* We come here from MINUS_EXPR when the second operand is a constant. */ - plus_expr: - this_optab = add_optab; - - /* If we are adding a constant, an RTL_EXPR that is sp, fp, or ap, and - something else, make sure we add the register to the constant and - then to the other thing. This case can occur during strength - reduction and doing it this way will produce better code if the - frame pointer or argument pointer is eliminated. - - fold-const.c will ensure that the constant is always in the inner - PLUS_EXPR, so the only case we need to do anything about is if - sp, ap, or fp is our second argument, in which case we must swap - the innermost first argument and our second argument. */ - - if (TREE_CODE (TREE_OPERAND (exp, 0)) == PLUS_EXPR - && TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 1)) == INTEGER_CST - && TREE_CODE (TREE_OPERAND (exp, 1)) == RTL_EXPR - && (RTL_EXPR_RTL (TREE_OPERAND (exp, 1)) == frame_pointer_rtx - || RTL_EXPR_RTL (TREE_OPERAND (exp, 1)) == stack_pointer_rtx - || RTL_EXPR_RTL (TREE_OPERAND (exp, 1)) == arg_pointer_rtx)) - { - tree t = TREE_OPERAND (exp, 1); - - TREE_OPERAND (exp, 1) = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); - TREE_OPERAND (TREE_OPERAND (exp, 0), 0) = t; - } - - /* If the result is to be Pmode and we are adding an integer to - something, we might be forming a constant. So try to use - plus_constant. If it produces a sum and we can't accept it, - use force_operand. This allows P = &ARR[const] to generate - efficient code on machines where a SYMBOL_REF is not a valid - address. - - If this is an EXPAND_SUM call, always return the sum. */ - if (TREE_CODE (TREE_OPERAND (exp, 0)) == INTEGER_CST - && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT - && (modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER - || mode == Pmode)) - { - op1 = expand_expr (TREE_OPERAND (exp, 1), subtarget, VOIDmode, - EXPAND_SUM); - op1 = plus_constant (op1, TREE_INT_CST_LOW (TREE_OPERAND (exp, 0))); - if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER) - op1 = force_operand (op1, target); - return op1; - } - - else if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST - && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_INT - && (modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER - || mode == Pmode)) - { - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, - EXPAND_SUM); - op0 = plus_constant (op0, TREE_INT_CST_LOW (TREE_OPERAND (exp, 1))); - if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER) - op0 = force_operand (op0, target); - return op0; - } - - /* No sense saving up arithmetic to be done - if it's all in the wrong mode to form part of an address. - And force_operand won't know whether to sign-extend or - zero-extend. */ - if ((modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER) - || mode != Pmode) goto binop; - - preexpand_calls (exp); - if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1))) - subtarget = 0; - - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, modifier); - op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, modifier); - - /* Make sure any term that's a sum with a constant comes last. */ - if (GET_CODE (op0) == PLUS - && CONSTANT_P (XEXP (op0, 1))) - { - temp = op0; - op0 = op1; - op1 = temp; - } - /* If adding to a sum including a constant, - associate it to put the constant outside. */ - if (GET_CODE (op1) == PLUS - && CONSTANT_P (XEXP (op1, 1))) - { - rtx constant_term = const0_rtx; - - temp = simplify_binary_operation (PLUS, mode, XEXP (op1, 0), op0); - if (temp != 0) - op0 = temp; - /* Ensure that MULT comes first if there is one. */ - else if (GET_CODE (op0) == MULT) - op0 = gen_rtx (PLUS, mode, op0, XEXP (op1, 0)); - else - op0 = gen_rtx (PLUS, mode, XEXP (op1, 0), op0); - - /* Let's also eliminate constants from op0 if possible. */ - op0 = eliminate_constant_term (op0, &constant_term); - - /* CONSTANT_TERM and XEXP (op1, 1) are known to be constant, so - their sum should be a constant. Form it into OP1, since the - result we want will then be OP0 + OP1. */ - - temp = simplify_binary_operation (PLUS, mode, constant_term, - XEXP (op1, 1)); - if (temp != 0) - op1 = temp; - else - op1 = gen_rtx (PLUS, mode, constant_term, XEXP (op1, 1)); - } - - /* Put a constant term last and put a multiplication first. */ - if (CONSTANT_P (op0) || GET_CODE (op1) == MULT) - temp = op1, op1 = op0, op0 = temp; - - temp = simplify_binary_operation (PLUS, mode, op0, op1); - return temp ? temp : gen_rtx (PLUS, mode, op0, op1); - - case MINUS_EXPR: - /* Handle difference of two symbolic constants, - for the sake of an initializer. */ - if ((modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER) - && really_constant_p (TREE_OPERAND (exp, 0)) - && really_constant_p (TREE_OPERAND (exp, 1))) - { - rtx op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, - VOIDmode, modifier); - rtx op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, - VOIDmode, modifier); - return gen_rtx (MINUS, mode, op0, op1); - } - /* Convert A - const to A + (-const). */ - if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST) - { - exp = build (PLUS_EXPR, type, TREE_OPERAND (exp, 0), - fold (build1 (NEGATE_EXPR, type, - TREE_OPERAND (exp, 1)))); - goto plus_expr; - } - this_optab = sub_optab; - goto binop; - - case MULT_EXPR: - preexpand_calls (exp); - /* If first operand is constant, swap them. - Thus the following special case checks need only - check the second operand. */ - if (TREE_CODE (TREE_OPERAND (exp, 0)) == INTEGER_CST) - { - register tree t1 = TREE_OPERAND (exp, 0); - TREE_OPERAND (exp, 0) = TREE_OPERAND (exp, 1); - TREE_OPERAND (exp, 1) = t1; - } - - /* Attempt to return something suitable for generating an - indexed address, for machines that support that. */ - - if (modifier == EXPAND_SUM && mode == Pmode - && TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST - && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT) - { - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, EXPAND_SUM); - - /* Apply distributive law if OP0 is x+c. */ - if (GET_CODE (op0) == PLUS - && GET_CODE (XEXP (op0, 1)) == CONST_INT) - return gen_rtx (PLUS, mode, - gen_rtx (MULT, mode, XEXP (op0, 0), - GEN_INT (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)))), - GEN_INT (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)) - * INTVAL (XEXP (op0, 1)))); - - if (GET_CODE (op0) != REG) - op0 = force_operand (op0, NULL_RTX); - if (GET_CODE (op0) != REG) - op0 = copy_to_mode_reg (mode, op0); - - return gen_rtx (MULT, mode, op0, - GEN_INT (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)))); - } - - if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1))) - subtarget = 0; - - /* Check for multiplying things that have been extended - from a narrower type. If this machine supports multiplying - in that narrower type with a result in the desired type, - do it that way, and avoid the explicit type-conversion. */ - if (TREE_CODE (TREE_OPERAND (exp, 0)) == NOP_EXPR - && TREE_CODE (type) == INTEGER_TYPE - && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))) - < TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (exp, 0)))) - && ((TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST - && int_fits_type_p (TREE_OPERAND (exp, 1), - TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))) - /* Don't use a widening multiply if a shift will do. */ - && ((GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 1)))) - > HOST_BITS_PER_WIDE_INT) - || exact_log2 (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1))) < 0)) - || - (TREE_CODE (TREE_OPERAND (exp, 1)) == NOP_EXPR - && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 1), 0))) - == - TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))) - /* If both operands are extended, they must either both - be zero-extended or both be sign-extended. */ - && (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 1), 0))) - == - TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))))))) - { - enum machine_mode innermode - = TYPE_MODE (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))); - this_optab = (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))) - ? umul_widen_optab : smul_widen_optab); - if (mode == GET_MODE_WIDER_MODE (innermode) - && this_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) - { - op0 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 0), 0), - NULL_RTX, VOIDmode, 0); - if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST) - op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, - VOIDmode, 0); - else - op1 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 1), 0), - NULL_RTX, VOIDmode, 0); - goto binop2; - } - } - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0); - return expand_mult (mode, op0, op1, target, unsignedp); - - case TRUNC_DIV_EXPR: - case FLOOR_DIV_EXPR: - case CEIL_DIV_EXPR: - case ROUND_DIV_EXPR: - case EXACT_DIV_EXPR: - preexpand_calls (exp); - if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1))) - subtarget = 0; - /* Possible optimization: compute the dividend with EXPAND_SUM - then if the divisor is constant can optimize the case - where some terms of the dividend have coeffs divisible by it. */ - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0); - return expand_divmod (0, code, mode, op0, op1, target, unsignedp); - - case RDIV_EXPR: - this_optab = flodiv_optab; - goto binop; - - case TRUNC_MOD_EXPR: - case FLOOR_MOD_EXPR: - case CEIL_MOD_EXPR: - case ROUND_MOD_EXPR: - preexpand_calls (exp); - if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1))) - subtarget = 0; - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0); - return expand_divmod (1, code, mode, op0, op1, target, unsignedp); - - case FIX_ROUND_EXPR: - case FIX_FLOOR_EXPR: - case FIX_CEIL_EXPR: - abort (); /* Not used for C. */ - - case FIX_TRUNC_EXPR: - op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0); - if (target == 0) - target = gen_reg_rtx (mode); - expand_fix (target, op0, unsignedp); - return target; - - case FLOAT_EXPR: - op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0); - if (target == 0) - target = gen_reg_rtx (mode); - /* expand_float can't figure out what to do if FROM has VOIDmode. - So give it the correct mode. With -O, cse will optimize this. */ - if (GET_MODE (op0) == VOIDmode) - op0 = copy_to_mode_reg (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))), - op0); - expand_float (target, op0, - TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0)))); - return target; - - case NEGATE_EXPR: - op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, 0); - temp = expand_unop (mode, neg_optab, op0, target, 0); - if (temp == 0) - abort (); - return temp; - - case ABS_EXPR: - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - - /* Handle complex values specially. */ - { - enum machine_mode opmode - = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))); - - if (GET_MODE_CLASS (opmode) == MODE_COMPLEX_INT - || GET_MODE_CLASS (opmode) == MODE_COMPLEX_FLOAT) - return expand_complex_abs (opmode, op0, target, unsignedp); - } - - /* Unsigned abs is simply the operand. Testing here means we don't - risk generating incorrect code below. */ - if (TREE_UNSIGNED (type)) - return op0; - - /* First try to do it with a special abs instruction. */ - temp = expand_unop (mode, abs_optab, op0, target, 0); - if (temp != 0) - return temp; - - /* If this machine has expensive jumps, we can do integer absolute - value of X as (((signed) x >> (W-1)) ^ x) - ((signed) x >> (W-1)), - where W is the width of MODE. */ - - if (GET_MODE_CLASS (mode) == MODE_INT && BRANCH_COST >= 2) - { - rtx extended = expand_shift (RSHIFT_EXPR, mode, op0, - size_int (GET_MODE_BITSIZE (mode) - 1), - NULL_RTX, 0); - - temp = expand_binop (mode, xor_optab, extended, op0, target, 0, - OPTAB_LIB_WIDEN); - if (temp != 0) - temp = expand_binop (mode, sub_optab, temp, extended, target, 0, - OPTAB_LIB_WIDEN); - - if (temp != 0) - return temp; - } - - /* If that does not win, use conditional jump and negate. */ - target = original_target; - temp = gen_label_rtx (); - if (target == 0 || ! safe_from_p (target, TREE_OPERAND (exp, 0)) - || (GET_CODE (target) == REG - && REGNO (target) < FIRST_PSEUDO_REGISTER)) - target = gen_reg_rtx (mode); - emit_move_insn (target, op0); - emit_cmp_insn (target, - expand_expr (convert (type, integer_zero_node), - NULL_RTX, VOIDmode, 0), - GE, NULL_RTX, mode, 0, 0); - NO_DEFER_POP; - emit_jump_insn (gen_bge (temp)); - op0 = expand_unop (mode, neg_optab, target, target, 0); - if (op0 != target) - emit_move_insn (target, op0); - emit_label (temp); - OK_DEFER_POP; - return target; - - case MAX_EXPR: - case MIN_EXPR: - target = original_target; - if (target == 0 || ! safe_from_p (target, TREE_OPERAND (exp, 1)) - || (GET_CODE (target) == REG - && REGNO (target) < FIRST_PSEUDO_REGISTER)) - target = gen_reg_rtx (mode); - op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0); - op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, 0); - - /* First try to do it with a special MIN or MAX instruction. - If that does not win, use a conditional jump to select the proper - value. */ - this_optab = (TREE_UNSIGNED (type) - ? (code == MIN_EXPR ? umin_optab : umax_optab) - : (code == MIN_EXPR ? smin_optab : smax_optab)); - - temp = expand_binop (mode, this_optab, op0, op1, target, unsignedp, - OPTAB_WIDEN); - if (temp != 0) - return temp; - - if (target != op0) - emit_move_insn (target, op0); - op0 = gen_label_rtx (); - /* If this mode is an integer too wide to compare properly, - compare word by word. Rely on cse to optimize constant cases. */ - if (GET_MODE_CLASS (mode) == MODE_INT - && !can_compare_p (mode)) - { - if (code == MAX_EXPR) - do_jump_by_parts_greater_rtx (mode, TREE_UNSIGNED (type), target, op1, NULL, op0); - else - do_jump_by_parts_greater_rtx (mode, TREE_UNSIGNED (type), op1, target, NULL, op0); - emit_move_insn (target, op1); - } - else - { - if (code == MAX_EXPR) - temp = (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 1))) - ? compare_from_rtx (target, op1, GEU, 1, mode, NULL_RTX, 0) - : compare_from_rtx (target, op1, GE, 0, mode, NULL_RTX, 0)); - else - temp = (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 1))) - ? compare_from_rtx (target, op1, LEU, 1, mode, NULL_RTX, 0) - : compare_from_rtx (target, op1, LE, 0, mode, NULL_RTX, 0)); - if (temp == const0_rtx) - emit_move_insn (target, op1); - else if (temp != const_true_rtx) - { - if (bcc_gen_fctn[(int) GET_CODE (temp)] != 0) - emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (temp)]) (op0)); - else - abort (); - emit_move_insn (target, op1); - } - } - emit_label (op0); - return target; - -/* ??? Can optimize when the operand of this is a bitwise operation, - by using a different bitwise operation. */ - case BIT_NOT_EXPR: - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - temp = expand_unop (mode, one_cmpl_optab, op0, target, 1); - if (temp == 0) - abort (); - return temp; - - case FFS_EXPR: - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - temp = expand_unop (mode, ffs_optab, op0, target, 1); - if (temp == 0) - abort (); - return temp; - -/* ??? Can optimize bitwise operations with one arg constant. - Can optimize (a bitwise1 n) bitwise2 (a bitwise3 b) - and (a bitwise1 b) bitwise2 b (etc) - but that is probably not worth while. */ - -/* BIT_AND_EXPR is for bitwise anding. - TRUTH_AND_EXPR is for anding two boolean values - when we want in all cases to compute both of them. - In general it is fastest to do TRUTH_AND_EXPR by - computing both operands as actual zero-or-1 values - and then bitwise anding. In cases where there cannot - be any side effects, better code would be made by - treating TRUTH_AND_EXPR like TRUTH_ANDIF_EXPR; - but the question is how to recognize those cases. */ - - case TRUTH_AND_EXPR: - case BIT_AND_EXPR: - this_optab = and_optab; - goto binop; - -/* See comment above about TRUTH_AND_EXPR; it applies here too. */ - case TRUTH_OR_EXPR: - case BIT_IOR_EXPR: - this_optab = ior_optab; - goto binop; - - case TRUTH_XOR_EXPR: - case BIT_XOR_EXPR: - this_optab = xor_optab; - goto binop; - - case LSHIFT_EXPR: - case RSHIFT_EXPR: - case LROTATE_EXPR: - case RROTATE_EXPR: - preexpand_calls (exp); - if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1))) - subtarget = 0; - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - return expand_shift (code, mode, op0, TREE_OPERAND (exp, 1), target, - unsignedp); - -/* Could determine the answer when only additive constants differ. - Also, the addition of one can be handled by changing the condition. */ - case LT_EXPR: - case LE_EXPR: - case GT_EXPR: - case GE_EXPR: - case EQ_EXPR: - case NE_EXPR: - preexpand_calls (exp); - temp = do_store_flag (exp, target, tmode != VOIDmode ? tmode : mode, 0); - if (temp != 0) - return temp; - /* For foo != 0, load foo, and if it is nonzero load 1 instead. */ - if (code == NE_EXPR && integer_zerop (TREE_OPERAND (exp, 1)) - && original_target - && GET_CODE (original_target) == REG - && (GET_MODE (original_target) - == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))) - { - temp = expand_expr (TREE_OPERAND (exp, 0), original_target, VOIDmode, 0); - if (temp != original_target) - temp = copy_to_reg (temp); - op1 = gen_label_rtx (); - emit_cmp_insn (temp, const0_rtx, EQ, NULL_RTX, - GET_MODE (temp), unsignedp, 0); - emit_jump_insn (gen_beq (op1)); - emit_move_insn (temp, const1_rtx); - emit_label (op1); - return temp; - } - /* If no set-flag instruction, must generate a conditional - store into a temporary variable. Drop through - and handle this like && and ||. */ - - case TRUTH_ANDIF_EXPR: - case TRUTH_ORIF_EXPR: - if (target == 0 || ! safe_from_p (target, exp) - /* Make sure we don't have a hard reg (such as function's return - value) live across basic blocks, if not optimizing. */ - || (!optimize && GET_CODE (target) == REG - && REGNO (target) < FIRST_PSEUDO_REGISTER)) - target = gen_reg_rtx (tmode != VOIDmode ? tmode : mode); - emit_clr_insn (target); - op1 = gen_label_rtx (); - jumpifnot (exp, op1); - emit_0_to_1_insn (target); - emit_label (op1); - return target; - - case TRUTH_NOT_EXPR: - op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, 0); - /* The parser is careful to generate TRUTH_NOT_EXPR - only with operands that are always zero or one. */ - temp = expand_binop (mode, xor_optab, op0, const1_rtx, - target, 1, OPTAB_LIB_WIDEN); - if (temp == 0) - abort (); - return temp; - - case COMPOUND_EXPR: - expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, 0); - emit_queue (); - return expand_expr (TREE_OPERAND (exp, 1), - (ignore ? const0_rtx : target), - VOIDmode, 0); - - case COND_EXPR: - { - /* Note that COND_EXPRs whose type is a structure or union - are required to be constructed to contain assignments of - a temporary variable, so that we can evaluate them here - for side effect only. If type is void, we must do likewise. */ - - /* If an arm of the branch requires a cleanup, - only that cleanup is performed. */ - - tree singleton = 0; - tree binary_op = 0, unary_op = 0; - tree old_cleanups = cleanups_this_call; - cleanups_this_call = 0; - - /* If this is (A ? 1 : 0) and A is a condition, just evaluate it and - convert it to our mode, if necessary. */ - if (integer_onep (TREE_OPERAND (exp, 1)) - && integer_zerop (TREE_OPERAND (exp, 2)) - && TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == '<') - { - op0 = expand_expr (TREE_OPERAND (exp, 0), target, mode, modifier); - if (GET_MODE (op0) == mode) - return op0; - if (target == 0) - target = gen_reg_rtx (mode); - convert_move (target, op0, unsignedp); - return target; - } - - /* If we are not to produce a result, we have no target. Otherwise, - if a target was specified use it; it will not be used as an - intermediate target unless it is safe. If no target, use a - temporary. */ - - if (mode == VOIDmode || ignore) - temp = 0; - else if (original_target - && safe_from_p (original_target, TREE_OPERAND (exp, 0))) - temp = original_target; - else if (mode == BLKmode) - { - if (TYPE_SIZE (type) == 0 - || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) - abort (); - temp = assign_stack_temp (BLKmode, - (TREE_INT_CST_LOW (TYPE_SIZE (type)) - + BITS_PER_UNIT - 1) - / BITS_PER_UNIT, 0); - } - else - temp = gen_reg_rtx (mode); - - /* Check for X ? A + B : A. If we have this, we can copy - A to the output and conditionally add B. Similarly for unary - operations. Don't do this if X has side-effects because - those side effects might affect A or B and the "?" operation is - a sequence point in ANSI. (We test for side effects later.) */ - - if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 1))) == '2' - && operand_equal_p (TREE_OPERAND (exp, 2), - TREE_OPERAND (TREE_OPERAND (exp, 1), 0), 0)) - singleton = TREE_OPERAND (exp, 2), binary_op = TREE_OPERAND (exp, 1); - else if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 2))) == '2' - && operand_equal_p (TREE_OPERAND (exp, 1), - TREE_OPERAND (TREE_OPERAND (exp, 2), 0), 0)) - singleton = TREE_OPERAND (exp, 1), binary_op = TREE_OPERAND (exp, 2); - else if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 1))) == '1' - && operand_equal_p (TREE_OPERAND (exp, 2), - TREE_OPERAND (TREE_OPERAND (exp, 1), 0), 0)) - singleton = TREE_OPERAND (exp, 2), unary_op = TREE_OPERAND (exp, 1); - else if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 2))) == '1' - && operand_equal_p (TREE_OPERAND (exp, 1), - TREE_OPERAND (TREE_OPERAND (exp, 2), 0), 0)) - singleton = TREE_OPERAND (exp, 1), unary_op = TREE_OPERAND (exp, 2); - - /* If we had X ? A + 1 : A and we can do the test of X as a store-flag - operation, do this as A + (X != 0). Similarly for other simple - binary operators. */ - if (singleton && binary_op - && ! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 0)) - && (TREE_CODE (binary_op) == PLUS_EXPR - || TREE_CODE (binary_op) == MINUS_EXPR - || TREE_CODE (binary_op) == BIT_IOR_EXPR - || TREE_CODE (binary_op) == BIT_XOR_EXPR - || TREE_CODE (binary_op) == BIT_AND_EXPR) - && integer_onep (TREE_OPERAND (binary_op, 1)) - && TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == '<') - { - rtx result; - optab boptab = (TREE_CODE (binary_op) == PLUS_EXPR ? add_optab - : TREE_CODE (binary_op) == MINUS_EXPR ? sub_optab - : TREE_CODE (binary_op) == BIT_IOR_EXPR ? ior_optab - : TREE_CODE (binary_op) == BIT_XOR_EXPR ? xor_optab - : and_optab); - - /* If we had X ? A : A + 1, do this as A + (X == 0). - - We have to invert the truth value here and then put it - back later if do_store_flag fails. We cannot simply copy - TREE_OPERAND (exp, 0) to another variable and modify that - because invert_truthvalue can modify the tree pointed to - by its argument. */ - if (singleton == TREE_OPERAND (exp, 1)) - TREE_OPERAND (exp, 0) - = invert_truthvalue (TREE_OPERAND (exp, 0)); - - result = do_store_flag (TREE_OPERAND (exp, 0), - (safe_from_p (temp, singleton) - ? temp : NULL_RTX), - mode, BRANCH_COST <= 1); - - if (result) - { - op1 = expand_expr (singleton, NULL_RTX, VOIDmode, 0); - return expand_binop (mode, boptab, op1, result, temp, - unsignedp, OPTAB_LIB_WIDEN); - } - else if (singleton == TREE_OPERAND (exp, 1)) - TREE_OPERAND (exp, 0) - = invert_truthvalue (TREE_OPERAND (exp, 0)); - } - - NO_DEFER_POP; - op0 = gen_label_rtx (); - - if (singleton && ! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 0))) - { - if (temp != 0) - { - /* If the target conflicts with the other operand of the - binary op, we can't use it. Also, we can't use the target - if it is a hard register, because evaluating the condition - might clobber it. */ - if ((binary_op - && ! safe_from_p (temp, TREE_OPERAND (binary_op, 1))) - || (GET_CODE (temp) == REG - && REGNO (temp) < FIRST_PSEUDO_REGISTER)) - temp = gen_reg_rtx (mode); - store_expr (singleton, temp, 0); - } - else - expand_expr (singleton, - ignore ? const1_rtx : NULL_RTX, VOIDmode, 0); - if (cleanups_this_call) - { - sorry ("aggregate value in COND_EXPR"); - cleanups_this_call = 0; - } - if (singleton == TREE_OPERAND (exp, 1)) - jumpif (TREE_OPERAND (exp, 0), op0); - else - jumpifnot (TREE_OPERAND (exp, 0), op0); - - if (binary_op && temp == 0) - /* Just touch the other operand. */ - expand_expr (TREE_OPERAND (binary_op, 1), - ignore ? const0_rtx : NULL_RTX, VOIDmode, 0); - else if (binary_op) - store_expr (build (TREE_CODE (binary_op), type, - make_tree (type, temp), - TREE_OPERAND (binary_op, 1)), - temp, 0); - else - store_expr (build1 (TREE_CODE (unary_op), type, - make_tree (type, temp)), - temp, 0); - op1 = op0; - } -#if 0 - /* This is now done in jump.c and is better done there because it - produces shorter register lifetimes. */ - - /* Check for both possibilities either constants or variables - in registers (but not the same as the target!). If so, can - save branches by assigning one, branching, and assigning the - other. */ - else if (temp && GET_MODE (temp) != BLKmode - && (TREE_CONSTANT (TREE_OPERAND (exp, 1)) - || ((TREE_CODE (TREE_OPERAND (exp, 1)) == PARM_DECL - || TREE_CODE (TREE_OPERAND (exp, 1)) == VAR_DECL) - && DECL_RTL (TREE_OPERAND (exp, 1)) - && GET_CODE (DECL_RTL (TREE_OPERAND (exp, 1))) == REG - && DECL_RTL (TREE_OPERAND (exp, 1)) != temp)) - && (TREE_CONSTANT (TREE_OPERAND (exp, 2)) - || ((TREE_CODE (TREE_OPERAND (exp, 2)) == PARM_DECL - || TREE_CODE (TREE_OPERAND (exp, 2)) == VAR_DECL) - && DECL_RTL (TREE_OPERAND (exp, 2)) - && GET_CODE (DECL_RTL (TREE_OPERAND (exp, 2))) == REG - && DECL_RTL (TREE_OPERAND (exp, 2)) != temp))) - { - if (GET_CODE (temp) == REG && REGNO (temp) < FIRST_PSEUDO_REGISTER) - temp = gen_reg_rtx (mode); - store_expr (TREE_OPERAND (exp, 2), temp, 0); - jumpifnot (TREE_OPERAND (exp, 0), op0); - store_expr (TREE_OPERAND (exp, 1), temp, 0); - op1 = op0; - } -#endif - /* Check for A op 0 ? A : FOO and A op 0 ? FOO : A where OP is any - comparison operator. If we have one of these cases, set the - output to A, branch on A (cse will merge these two references), - then set the output to FOO. */ - else if (temp - && TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == '<' - && integer_zerop (TREE_OPERAND (TREE_OPERAND (exp, 0), 1)) - && operand_equal_p (TREE_OPERAND (TREE_OPERAND (exp, 0), 0), - TREE_OPERAND (exp, 1), 0) - && ! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 0)) - && safe_from_p (temp, TREE_OPERAND (exp, 2))) - { - if (GET_CODE (temp) == REG && REGNO (temp) < FIRST_PSEUDO_REGISTER) - temp = gen_reg_rtx (mode); - store_expr (TREE_OPERAND (exp, 1), temp, 0); - jumpif (TREE_OPERAND (exp, 0), op0); - store_expr (TREE_OPERAND (exp, 2), temp, 0); - op1 = op0; - } - else if (temp - && TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == '<' - && integer_zerop (TREE_OPERAND (TREE_OPERAND (exp, 0), 1)) - && operand_equal_p (TREE_OPERAND (TREE_OPERAND (exp, 0), 0), - TREE_OPERAND (exp, 2), 0) - && ! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 0)) - && safe_from_p (temp, TREE_OPERAND (exp, 1))) - { - if (GET_CODE (temp) == REG && REGNO (temp) < FIRST_PSEUDO_REGISTER) - temp = gen_reg_rtx (mode); - store_expr (TREE_OPERAND (exp, 2), temp, 0); - jumpifnot (TREE_OPERAND (exp, 0), op0); - store_expr (TREE_OPERAND (exp, 1), temp, 0); - op1 = op0; - } - else - { - op1 = gen_label_rtx (); - jumpifnot (TREE_OPERAND (exp, 0), op0); - if (temp != 0) - store_expr (TREE_OPERAND (exp, 1), temp, 0); - else - expand_expr (TREE_OPERAND (exp, 1), - ignore ? const0_rtx : NULL_RTX, VOIDmode, 0); - if (cleanups_this_call) - { - sorry ("aggregate value in COND_EXPR"); - cleanups_this_call = 0; - } - - emit_queue (); - emit_jump_insn (gen_jump (op1)); - emit_barrier (); - emit_label (op0); - if (temp != 0) - store_expr (TREE_OPERAND (exp, 2), temp, 0); - else - expand_expr (TREE_OPERAND (exp, 2), - ignore ? const0_rtx : NULL_RTX, VOIDmode, 0); - } - - if (cleanups_this_call) - { - sorry ("aggregate value in COND_EXPR"); - cleanups_this_call = 0; - } - - emit_queue (); - emit_label (op1); - OK_DEFER_POP; - cleanups_this_call = old_cleanups; - return temp; - } - - case TARGET_EXPR: - { - /* Something needs to be initialized, but we didn't know - where that thing was when building the tree. For example, - it could be the return value of a function, or a parameter - to a function which lays down in the stack, or a temporary - variable which must be passed by reference. - - We guarantee that the expression will either be constructed - or copied into our original target. */ - - tree slot = TREE_OPERAND (exp, 0); - tree exp1; - - if (TREE_CODE (slot) != VAR_DECL) - abort (); - - if (target == 0) - { - if (DECL_RTL (slot) != 0) - { - target = DECL_RTL (slot); - /* If we have already expanded the slot, so don't do - it again. (mrs) */ - if (TREE_OPERAND (exp, 1) == NULL_TREE) - return target; - } - else - { - target = assign_stack_temp (mode, int_size_in_bytes (type), 0); - /* All temp slots at this level must not conflict. */ - preserve_temp_slots (target); - DECL_RTL (slot) = target; - } - -#if 0 - /* I bet this needs to be done, and I bet that it needs to - be above, inside the else clause. The reason is - simple, how else is it going to get cleaned up? (mrs) - - The reason is probably did not work before, and was - commented out is because this was re-expanding already - expanded target_exprs (target == 0 and DECL_RTL (slot) - != 0) also cleaning them up many times as well. :-( */ - - /* Since SLOT is not known to the called function - to belong to its stack frame, we must build an explicit - cleanup. This case occurs when we must build up a reference - to pass the reference as an argument. In this case, - it is very likely that such a reference need not be - built here. */ - - if (TREE_OPERAND (exp, 2) == 0) - TREE_OPERAND (exp, 2) = maybe_build_cleanup (slot); - if (TREE_OPERAND (exp, 2)) - cleanups_this_call = tree_cons (NULL_TREE, TREE_OPERAND (exp, 2), - cleanups_this_call); -#endif - } - else - { - /* This case does occur, when expanding a parameter which - needs to be constructed on the stack. The target - is the actual stack address that we want to initialize. - The function we call will perform the cleanup in this case. */ - - /* If we have already assigned it space, use that space, - not target that we were passed in, as our target - parameter is only a hint. */ - if (DECL_RTL (slot) != 0) - { - target = DECL_RTL (slot); - /* If we have already expanded the slot, so don't do - it again. (mrs) */ - if (TREE_OPERAND (exp, 1) == NULL_TREE) - return target; - } - - DECL_RTL (slot) = target; - } - - exp1 = TREE_OPERAND (exp, 1); - /* Mark it as expanded. */ - TREE_OPERAND (exp, 1) = NULL_TREE; - - return expand_expr (exp1, target, tmode, modifier); - } - - case INIT_EXPR: - { - tree lhs = TREE_OPERAND (exp, 0); - tree rhs = TREE_OPERAND (exp, 1); - tree noncopied_parts = 0; - tree lhs_type = TREE_TYPE (lhs); - - temp = expand_assignment (lhs, rhs, ! ignore, original_target != 0); - if (TYPE_NONCOPIED_PARTS (lhs_type) != 0 && !fixed_type_p (rhs)) - noncopied_parts = init_noncopied_parts (stabilize_reference (lhs), - TYPE_NONCOPIED_PARTS (lhs_type)); - while (noncopied_parts != 0) - { - expand_assignment (TREE_VALUE (noncopied_parts), - TREE_PURPOSE (noncopied_parts), 0, 0); - noncopied_parts = TREE_CHAIN (noncopied_parts); - } - return temp; - } - - case MODIFY_EXPR: - { - /* If lhs is complex, expand calls in rhs before computing it. - That's so we don't compute a pointer and save it over a call. - If lhs is simple, compute it first so we can give it as a - target if the rhs is just a call. This avoids an extra temp and copy - and that prevents a partial-subsumption which makes bad code. - Actually we could treat component_ref's of vars like vars. */ - - tree lhs = TREE_OPERAND (exp, 0); - tree rhs = TREE_OPERAND (exp, 1); - tree noncopied_parts = 0; - tree lhs_type = TREE_TYPE (lhs); - - temp = 0; - - if (TREE_CODE (lhs) != VAR_DECL - && TREE_CODE (lhs) != RESULT_DECL - && TREE_CODE (lhs) != PARM_DECL) - preexpand_calls (exp); - - /* Check for |= or &= of a bitfield of size one into another bitfield - of size 1. In this case, (unless we need the result of the - assignment) we can do this more efficiently with a - test followed by an assignment, if necessary. - - ??? At this point, we can't get a BIT_FIELD_REF here. But if - things change so we do, this code should be enhanced to - support it. */ - if (ignore - && TREE_CODE (lhs) == COMPONENT_REF - && (TREE_CODE (rhs) == BIT_IOR_EXPR - || TREE_CODE (rhs) == BIT_AND_EXPR) - && TREE_OPERAND (rhs, 0) == lhs - && TREE_CODE (TREE_OPERAND (rhs, 1)) == COMPONENT_REF - && TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (lhs, 1))) == 1 - && TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (TREE_OPERAND (rhs, 1), 1))) == 1) - { - rtx label = gen_label_rtx (); - - do_jump (TREE_OPERAND (rhs, 1), - TREE_CODE (rhs) == BIT_IOR_EXPR ? label : 0, - TREE_CODE (rhs) == BIT_AND_EXPR ? label : 0); - expand_assignment (lhs, convert (TREE_TYPE (rhs), - (TREE_CODE (rhs) == BIT_IOR_EXPR - ? integer_one_node - : integer_zero_node)), - 0, 0); - do_pending_stack_adjust (); - emit_label (label); - return const0_rtx; - } - - if (TYPE_NONCOPIED_PARTS (lhs_type) != 0 - && ! (fixed_type_p (lhs) && fixed_type_p (rhs))) - noncopied_parts = save_noncopied_parts (stabilize_reference (lhs), - TYPE_NONCOPIED_PARTS (lhs_type)); - - temp = expand_assignment (lhs, rhs, ! ignore, original_target != 0); - while (noncopied_parts != 0) - { - expand_assignment (TREE_PURPOSE (noncopied_parts), - TREE_VALUE (noncopied_parts), 0, 0); - noncopied_parts = TREE_CHAIN (noncopied_parts); - } - return temp; - } - - case PREINCREMENT_EXPR: - case PREDECREMENT_EXPR: - return expand_increment (exp, 0); - - case POSTINCREMENT_EXPR: - case POSTDECREMENT_EXPR: - /* Faster to treat as pre-increment if result is not used. */ - return expand_increment (exp, ! ignore); - - case ADDR_EXPR: - /* Are we taking the address of a nested function? */ - if (TREE_CODE (TREE_OPERAND (exp, 0)) == FUNCTION_DECL - && decl_function_context (TREE_OPERAND (exp, 0)) != 0) - { - op0 = trampoline_address (TREE_OPERAND (exp, 0)); - op0 = force_operand (op0, target); - } - else - { - op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, - (modifier == EXPAND_INITIALIZER - ? modifier : EXPAND_CONST_ADDRESS)); - - /* We would like the object in memory. If it is a constant, - we can have it be statically allocated into memory. For - a non-constant (REG or SUBREG), we need to allocate some - memory and store the value into it. */ - - if (CONSTANT_P (op0)) - op0 = force_const_mem (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))), - op0); - - if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) - { - /* If this object is in a register, it must be not - be BLKmode. */ - tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0)); - enum machine_mode inner_mode = TYPE_MODE (inner_type); - rtx memloc - = assign_stack_temp (inner_mode, - int_size_in_bytes (inner_type), 1); - - emit_move_insn (memloc, op0); - op0 = memloc; - } - - if (GET_CODE (op0) != MEM) - abort (); - - if (modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER) - return XEXP (op0, 0); - op0 = force_operand (XEXP (op0, 0), target); - } - if (flag_force_addr && GET_CODE (op0) != REG) - return force_reg (Pmode, op0); - return op0; - - case ENTRY_VALUE_EXPR: - abort (); - - /* COMPLEX type for Extended Pascal & Fortran */ - case COMPLEX_EXPR: - { - enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp))); - - rtx prev; - - /* Get the rtx code of the operands. */ - op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0); - op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0); - - if (! target) - target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp))); - - prev = get_last_insn (); - - /* Tell flow that the whole of the destination is being set. */ - if (GET_CODE (target) == REG) - emit_insn (gen_rtx (CLOBBER, VOIDmode, target)); - - /* Move the real (op0) and imaginary (op1) parts to their location. */ - emit_move_insn (gen_realpart (mode, target), op0); - emit_move_insn (gen_imagpart (mode, target), op1); - - /* Complex construction should appear as a single unit. */ - group_insns (prev); - - return target; - } - - case REALPART_EXPR: - op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0); - return gen_realpart (mode, op0); - - case IMAGPART_EXPR: - op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0); - return gen_imagpart (mode, op0); - - case CONJ_EXPR: - { - enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp))); - rtx imag_t; - rtx prev; - - op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0); - - if (! target) - target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp))); - - prev = get_last_insn (); - - /* Tell flow that the whole of the destination is being set. */ - if (GET_CODE (target) == REG) - emit_insn (gen_rtx (CLOBBER, VOIDmode, target)); - - /* Store the realpart and the negated imagpart to target. */ - emit_move_insn (gen_realpart (mode, target), gen_realpart (mode, op0)); - - imag_t = gen_imagpart (mode, target); - temp = expand_unop (mode, neg_optab, - gen_imagpart (mode, op0), imag_t, 0); - if (temp != imag_t) - emit_move_insn (imag_t, temp); - - /* Conjugate should appear as a single unit */ - group_insns (prev); - - return target; - } - - case ERROR_MARK: - op0 = CONST0_RTX (tmode); - if (op0 != 0) - return op0; - return const0_rtx; - - default: - return (*lang_expand_expr) (exp, target, tmode, modifier); - } - - /* Here to do an ordinary binary operator, generating an instruction - from the optab already placed in `this_optab'. */ - binop: - preexpand_calls (exp); - if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1))) - subtarget = 0; - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0); - binop2: - temp = expand_binop (mode, this_optab, op0, op1, target, - unsignedp, OPTAB_LIB_WIDEN); - if (temp == 0) - abort (); - return temp; -} - -/* Return the alignment in bits of EXP, a pointer valued expression. - But don't return more than MAX_ALIGN no matter what. - The alignment returned is, by default, the alignment of the thing that - EXP points to (if it is not a POINTER_TYPE, 0 is returned). - - Otherwise, look at the expression to see if we can do better, i.e., if the - expression is actually pointing at an object whose alignment is tighter. */ - -static int -get_pointer_alignment (exp, max_align) - tree exp; - unsigned max_align; -{ - unsigned align, inner; - - if (TREE_CODE (TREE_TYPE (exp)) != POINTER_TYPE) - return 0; - - align = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))); - align = MIN (align, max_align); - - while (1) - { - switch (TREE_CODE (exp)) - { - case NOP_EXPR: - case CONVERT_EXPR: - case NON_LVALUE_EXPR: - exp = TREE_OPERAND (exp, 0); - if (TREE_CODE (TREE_TYPE (exp)) != POINTER_TYPE) - return align; - inner = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))); - inner = MIN (inner, max_align); - align = MAX (align, inner); - break; - - case PLUS_EXPR: - /* If sum of pointer + int, restrict our maximum alignment to that - imposed by the integer. If not, we can't do any better than - ALIGN. */ - if (TREE_CODE (TREE_OPERAND (exp, 1)) != INTEGER_CST) - return align; - - while (((TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)) * BITS_PER_UNIT) - & (max_align - 1)) - != 0) - max_align >>= 1; - - exp = TREE_OPERAND (exp, 0); - break; - - case ADDR_EXPR: - /* See what we are pointing at and look at its alignment. */ - exp = TREE_OPERAND (exp, 0); - if (TREE_CODE (exp) == FUNCTION_DECL) - align = MAX (align, FUNCTION_BOUNDARY); - else if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'd') - align = MAX (align, DECL_ALIGN (exp)); -#ifdef CONSTANT_ALIGNMENT - else if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'c') - align = CONSTANT_ALIGNMENT (exp, align); -#endif - return MIN (align, max_align); - - default: - return align; - } - } -} - -/* Return the tree node and offset if a given argument corresponds to - a string constant. */ - -static tree -string_constant (arg, ptr_offset) - tree arg; - tree *ptr_offset; -{ - STRIP_NOPS (arg); - - if (TREE_CODE (arg) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (arg, 0)) == STRING_CST) - { - *ptr_offset = integer_zero_node; - return TREE_OPERAND (arg, 0); - } - else if (TREE_CODE (arg) == PLUS_EXPR) - { - tree arg0 = TREE_OPERAND (arg, 0); - tree arg1 = TREE_OPERAND (arg, 1); - - STRIP_NOPS (arg0); - STRIP_NOPS (arg1); - - if (TREE_CODE (arg0) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (arg0, 0)) == STRING_CST) - { - *ptr_offset = arg1; - return TREE_OPERAND (arg0, 0); - } - else if (TREE_CODE (arg1) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (arg1, 0)) == STRING_CST) - { - *ptr_offset = arg0; - return TREE_OPERAND (arg1, 0); - } - } - - return 0; -} - -/* Compute the length of a C string. TREE_STRING_LENGTH is not the right - way, because it could contain a zero byte in the middle. - TREE_STRING_LENGTH is the size of the character array, not the string. - - Unfortunately, string_constant can't access the values of const char - arrays with initializers, so neither can we do so here. */ - -static tree -c_strlen (src) - tree src; -{ - tree offset_node; - int offset, max; - char *ptr; - - src = string_constant (src, &offset_node); - if (src == 0) - return 0; - max = TREE_STRING_LENGTH (src); - ptr = TREE_STRING_POINTER (src); - if (offset_node && TREE_CODE (offset_node) != INTEGER_CST) - { - /* If the string has an internal zero byte (e.g., "foo\0bar"), we can't - compute the offset to the following null if we don't know where to - start searching for it. */ - int i; - for (i = 0; i < max; i++) - if (ptr[i] == 0) - return 0; - /* We don't know the starting offset, but we do know that the string - has no internal zero bytes. We can assume that the offset falls - within the bounds of the string; otherwise, the programmer deserves - what he gets. Subtract the offset from the length of the string, - and return that. */ - /* This would perhaps not be valid if we were dealing with named - arrays in addition to literal string constants. */ - return size_binop (MINUS_EXPR, size_int (max), offset_node); - } - - /* We have a known offset into the string. Start searching there for - a null character. */ - if (offset_node == 0) - offset = 0; - else - { - /* Did we get a long long offset? If so, punt. */ - if (TREE_INT_CST_HIGH (offset_node) != 0) - return 0; - offset = TREE_INT_CST_LOW (offset_node); - } - /* If the offset is known to be out of bounds, warn, and call strlen at - runtime. */ - if (offset < 0 || offset > max) - { - warning ("offset outside bounds of constant string"); - return 0; - } - /* Use strlen to search for the first zero byte. Since any strings - constructed with build_string will have nulls appended, we win even - if we get handed something like (char[4])"abcd". - - Since OFFSET is our starting index into the string, no further - calculation is needed. */ - return size_int (strlen (ptr + offset)); -} - -/* Expand an expression EXP that calls a built-in function, - with result going to TARGET if that's convenient - (and in mode MODE if that's convenient). - SUBTARGET may be used as the target for computing one of EXP's operands. - IGNORE is nonzero if the value is to be ignored. */ - -static rtx -expand_builtin (exp, target, subtarget, mode, ignore) - tree exp; - rtx target; - rtx subtarget; - enum machine_mode mode; - int ignore; -{ - tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); - tree arglist = TREE_OPERAND (exp, 1); - rtx op0; - rtx lab1, insns; - enum machine_mode value_mode = TYPE_MODE (TREE_TYPE (exp)); - optab builtin_optab; - - switch (DECL_FUNCTION_CODE (fndecl)) - { - case BUILT_IN_ABS: - case BUILT_IN_LABS: - case BUILT_IN_FABS: - /* build_function_call changes these into ABS_EXPR. */ - abort (); - - case BUILT_IN_SIN: - case BUILT_IN_COS: - case BUILT_IN_FSQRT: - /* If not optimizing, call the library function. */ - if (! optimize) - break; - - if (arglist == 0 - /* Arg could be wrong type if user redeclared this fcn wrong. */ - || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != REAL_TYPE) - return CONST0_RTX (TYPE_MODE (TREE_TYPE (exp))); - - /* Stabilize and compute the argument. */ - if (TREE_CODE (TREE_VALUE (arglist)) != VAR_DECL - && TREE_CODE (TREE_VALUE (arglist)) != PARM_DECL) - { - exp = copy_node (exp); - arglist = copy_node (arglist); - TREE_OPERAND (exp, 1) = arglist; - TREE_VALUE (arglist) = save_expr (TREE_VALUE (arglist)); - } - op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0); - - /* Make a suitable register to place result in. */ - target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp))); - - emit_queue (); - start_sequence (); - - switch (DECL_FUNCTION_CODE (fndecl)) - { - case BUILT_IN_SIN: - builtin_optab = sin_optab; break; - case BUILT_IN_COS: - builtin_optab = cos_optab; break; - case BUILT_IN_FSQRT: - builtin_optab = sqrt_optab; break; - default: - abort (); - } - - /* Compute into TARGET. - Set TARGET to wherever the result comes back. */ - target = expand_unop (TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))), - builtin_optab, op0, target, 0); - - /* If we were unable to expand via the builtin, stop the - sequence (without outputting the insns) and break, causing - a call the the library function. */ - if (target == 0) - { - end_sequence (); - break; - } - - /* Check the results by default. But if flag_fast_math is turned on, - then assume sqrt will always be called with valid arguments. */ - - if (! flag_fast_math) - { - /* Don't define the builtin FP instructions - if your machine is not IEEE. */ - if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT) - abort (); - - lab1 = gen_label_rtx (); - - /* Test the result; if it is NaN, set errno=EDOM because - the argument was not in the domain. */ - emit_cmp_insn (target, target, EQ, 0, GET_MODE (target), 0, 0); - emit_jump_insn (gen_beq (lab1)); - -#if TARGET_EDOM - { -#ifdef GEN_ERRNO_RTX - rtx errno_rtx = GEN_ERRNO_RTX; -#else - rtx errno_rtx - = gen_rtx (MEM, word_mode, gen_rtx (SYMBOL_REF, Pmode, "*errno")); -#endif - - emit_move_insn (errno_rtx, GEN_INT (TARGET_EDOM)); - } -#else - /* We can't set errno=EDOM directly; let the library call do it. - Pop the arguments right away in case the call gets deleted. */ - NO_DEFER_POP; - expand_call (exp, target, 0); - OK_DEFER_POP; -#endif - - emit_label (lab1); - } - - /* Output the entire sequence. */ - insns = get_insns (); - end_sequence (); - emit_insns (insns); - - return target; - - /* __builtin_apply_args returns block of memory allocated on - the stack into which is stored the arg pointer, structure - value address, static chain, and all the registers that might - possibly be used in performing a function call. The code is - moved to the start of the function so the incoming values are - saved. */ - case BUILT_IN_APPLY_ARGS: - /* Don't do __builtin_apply_args more than once in a function. - Save the result of the first call and reuse it. */ - if (apply_args_value != 0) - return apply_args_value; - { - /* When this function is called, it means that registers must be - saved on entry to this function. So we migrate the - call to the first insn of this function. */ - rtx temp; - rtx seq; - - start_sequence (); - temp = expand_builtin_apply_args (); - seq = get_insns (); - end_sequence (); - - apply_args_value = temp; - - /* Put the sequence after the NOTE that starts the function. - If this is inside a SEQUENCE, make the outer-level insn - chain current, so the code is placed at the start of the - function. */ - push_topmost_sequence (); - emit_insns_before (seq, NEXT_INSN (get_insns ())); - pop_topmost_sequence (); - return temp; - } - - /* __builtin_apply (FUNCTION, ARGUMENTS, ARGSIZE) invokes - FUNCTION with a copy of the parameters described by - ARGUMENTS, and ARGSIZE. It returns a block of memory - allocated on the stack into which is stored all the registers - that might possibly be used for returning the result of a - function. ARGUMENTS is the value returned by - __builtin_apply_args. ARGSIZE is the number of bytes of - arguments that must be copied. ??? How should this value be - computed? We'll also need a safe worst case value for varargs - functions. */ - case BUILT_IN_APPLY: - if (arglist == 0 - /* Arg could be non-pointer if user redeclared this fcn wrong. */ - || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE - || TREE_CHAIN (arglist) == 0 - || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE - || TREE_CHAIN (TREE_CHAIN (arglist)) == 0 - || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))))) != INTEGER_TYPE) - return const0_rtx; - else - { - int i; - tree t; - rtx ops[3]; - - for (t = arglist, i = 0; t; t = TREE_CHAIN (t), i++) - ops[i] = expand_expr (TREE_VALUE (t), NULL_RTX, VOIDmode, 0); - - return expand_builtin_apply (ops[0], ops[1], ops[2]); - } - - /* __builtin_return (RESULT) causes the function to return the - value described by RESULT. RESULT is address of the block of - memory returned by __builtin_apply. */ - case BUILT_IN_RETURN: - if (arglist - /* Arg could be non-pointer if user redeclared this fcn wrong. */ - && TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) == POINTER_TYPE) - expand_builtin_return (expand_expr (TREE_VALUE (arglist), - NULL_RTX, VOIDmode, 0)); - return const0_rtx; - - case BUILT_IN_SAVEREGS: - /* Don't do __builtin_saveregs more than once in a function. - Save the result of the first call and reuse it. */ - if (saveregs_value != 0) - return saveregs_value; - { - /* When this function is called, it means that registers must be - saved on entry to this function. So we migrate the - call to the first insn of this function. */ - rtx temp; - rtx seq; - rtx valreg, saved_valreg; - - /* Now really call the function. `expand_call' does not call - expand_builtin, so there is no danger of infinite recursion here. */ - start_sequence (); - -#ifdef EXPAND_BUILTIN_SAVEREGS - /* Do whatever the machine needs done in this case. */ - temp = EXPAND_BUILTIN_SAVEREGS (arglist); -#else - /* The register where the function returns its value - is likely to have something else in it, such as an argument. - So preserve that register around the call. */ - if (value_mode != VOIDmode) - { - valreg = hard_libcall_value (value_mode); - saved_valreg = gen_reg_rtx (value_mode); - emit_move_insn (saved_valreg, valreg); - } - - /* Generate the call, putting the value in a pseudo. */ - temp = expand_call (exp, target, ignore); - - if (value_mode != VOIDmode) - emit_move_insn (valreg, saved_valreg); -#endif - - seq = get_insns (); - end_sequence (); - - saveregs_value = temp; - - /* Put the sequence after the NOTE that starts the function. - If this is inside a SEQUENCE, make the outer-level insn - chain current, so the code is placed at the start of the - function. */ - push_topmost_sequence (); - emit_insns_before (seq, NEXT_INSN (get_insns ())); - pop_topmost_sequence (); - return temp; - } - - /* __builtin_args_info (N) returns word N of the arg space info - for the current function. The number and meanings of words - is controlled by the definition of CUMULATIVE_ARGS. */ - case BUILT_IN_ARGS_INFO: - { - int nwords = sizeof (CUMULATIVE_ARGS) / sizeof (int); - int i; - int *word_ptr = (int *) ¤t_function_args_info; - tree type, elts, result; - - if (sizeof (CUMULATIVE_ARGS) % sizeof (int) != 0) - fatal ("CUMULATIVE_ARGS type defined badly; see %s, line %d", - __FILE__, __LINE__); - - if (arglist != 0) - { - tree arg = TREE_VALUE (arglist); - if (TREE_CODE (arg) != INTEGER_CST) - error ("argument of `__builtin_args_info' must be constant"); - else - { - int wordnum = TREE_INT_CST_LOW (arg); - - if (wordnum < 0 || wordnum >= nwords || TREE_INT_CST_HIGH (arg)) - error ("argument of `__builtin_args_info' out of range"); - else - return GEN_INT (word_ptr[wordnum]); - } - } - else - error ("missing argument in `__builtin_args_info'"); - - return const0_rtx; - -#if 0 - for (i = 0; i < nwords; i++) - elts = tree_cons (NULL_TREE, build_int_2 (word_ptr[i], 0)); - - type = build_array_type (integer_type_node, - build_index_type (build_int_2 (nwords, 0))); - result = build (CONSTRUCTOR, type, NULL_TREE, nreverse (elts)); - TREE_CONSTANT (result) = 1; - TREE_STATIC (result) = 1; - result = build (INDIRECT_REF, build_pointer_type (type), result); - TREE_CONSTANT (result) = 1; - return expand_expr (result, NULL_RTX, VOIDmode, 0); -#endif - } - - /* Return the address of the first anonymous stack arg. */ - case BUILT_IN_NEXT_ARG: - { - tree fntype = TREE_TYPE (current_function_decl); - if (!(TYPE_ARG_TYPES (fntype) != 0 - && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) - != void_type_node))) - { - error ("`va_start' used in function with fixed args"); - return const0_rtx; - } - } - - return expand_binop (Pmode, add_optab, - current_function_internal_arg_pointer, - current_function_arg_offset_rtx, - NULL_RTX, 0, OPTAB_LIB_WIDEN); - - case BUILT_IN_CLASSIFY_TYPE: - if (arglist != 0) - { - tree type = TREE_TYPE (TREE_VALUE (arglist)); - enum tree_code code = TREE_CODE (type); - if (code == VOID_TYPE) - return GEN_INT (void_type_class); - if (code == INTEGER_TYPE) - return GEN_INT (integer_type_class); - if (code == CHAR_TYPE) - return GEN_INT (char_type_class); - if (code == ENUMERAL_TYPE) - return GEN_INT (enumeral_type_class); - if (code == BOOLEAN_TYPE) - return GEN_INT (boolean_type_class); - if (code == POINTER_TYPE) - return GEN_INT (pointer_type_class); - if (code == REFERENCE_TYPE) - return GEN_INT (reference_type_class); - if (code == OFFSET_TYPE) - return GEN_INT (offset_type_class); - if (code == REAL_TYPE) - return GEN_INT (real_type_class); - if (code == COMPLEX_TYPE) - return GEN_INT (complex_type_class); - if (code == FUNCTION_TYPE) - return GEN_INT (function_type_class); - if (code == METHOD_TYPE) - return GEN_INT (method_type_class); - if (code == RECORD_TYPE) - return GEN_INT (record_type_class); - if (code == UNION_TYPE || code == QUAL_UNION_TYPE) - return GEN_INT (union_type_class); - if (code == ARRAY_TYPE) - return GEN_INT (array_type_class); - if (code == STRING_TYPE) - return GEN_INT (string_type_class); - if (code == SET_TYPE) - return GEN_INT (set_type_class); - if (code == FILE_TYPE) - return GEN_INT (file_type_class); - if (code == LANG_TYPE) - return GEN_INT (lang_type_class); - } - return GEN_INT (no_type_class); - - case BUILT_IN_CONSTANT_P: - if (arglist == 0) - return const0_rtx; - else - return (TREE_CODE_CLASS (TREE_CODE (TREE_VALUE (arglist))) == 'c' - ? const1_rtx : const0_rtx); - - case BUILT_IN_FRAME_ADDRESS: - /* The argument must be a nonnegative integer constant. - It counts the number of frames to scan up the stack. - The value is the address of that frame. */ - case BUILT_IN_RETURN_ADDRESS: - /* The argument must be a nonnegative integer constant. - It counts the number of frames to scan up the stack. - The value is the return address saved in that frame. */ - if (arglist == 0) - /* Warning about missing arg was already issued. */ - return const0_rtx; - else if (TREE_CODE (TREE_VALUE (arglist)) != INTEGER_CST) - { - error ("invalid arg to `__builtin_return_address'"); - return const0_rtx; - } - else if (tree_int_cst_lt (TREE_VALUE (arglist), integer_zero_node)) - { - error ("invalid arg to `__builtin_return_address'"); - return const0_rtx; - } - else - { - int count = TREE_INT_CST_LOW (TREE_VALUE (arglist)); - rtx tem = frame_pointer_rtx; - int i; - - /* Some machines need special handling before we can access arbitrary - frames. For example, on the sparc, we must first flush all - register windows to the stack. */ -#ifdef SETUP_FRAME_ADDRESSES - SETUP_FRAME_ADDRESSES (); -#endif - - /* On the sparc, the return address is not in the frame, it is - in a register. There is no way to access it off of the current - frame pointer, but it can be accessed off the previous frame - pointer by reading the value from the register window save - area. */ -#ifdef RETURN_ADDR_IN_PREVIOUS_FRAME - if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_RETURN_ADDRESS) - count--; -#endif - - /* Scan back COUNT frames to the specified frame. */ - for (i = 0; i < count; i++) - { - /* Assume the dynamic chain pointer is in the word that - the frame address points to, unless otherwise specified. */ -#ifdef DYNAMIC_CHAIN_ADDRESS - tem = DYNAMIC_CHAIN_ADDRESS (tem); -#endif - tem = memory_address (Pmode, tem); - tem = copy_to_reg (gen_rtx (MEM, Pmode, tem)); - } - - /* For __builtin_frame_address, return what we've got. */ - if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS) - return tem; - - /* For __builtin_return_address, - Get the return address from that frame. */ -#ifdef RETURN_ADDR_RTX - return RETURN_ADDR_RTX (count, tem); -#else - tem = memory_address (Pmode, - plus_constant (tem, GET_MODE_SIZE (Pmode))); - return copy_to_reg (gen_rtx (MEM, Pmode, tem)); -#endif - } - - case BUILT_IN_ALLOCA: - if (arglist == 0 - /* Arg could be non-integer if user redeclared this fcn wrong. */ - || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE) - return const0_rtx; - current_function_calls_alloca = 1; - /* Compute the argument. */ - op0 = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0); - - /* Allocate the desired space. */ - target = allocate_dynamic_stack_space (op0, target, BITS_PER_UNIT); - - /* Record the new stack level for nonlocal gotos. */ - if (nonlocal_goto_handler_slot != 0) - emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX); - return target; - - case BUILT_IN_FFS: - /* If not optimizing, call the library function. */ - if (!optimize) - break; - - if (arglist == 0 - /* Arg could be non-integer if user redeclared this fcn wrong. */ - || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE) - return const0_rtx; - - /* Compute the argument. */ - op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0); - /* Compute ffs, into TARGET if possible. - Set TARGET to wherever the result comes back. */ - target = expand_unop (TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))), - ffs_optab, op0, target, 1); - if (target == 0) - abort (); - return target; - - case BUILT_IN_STRLEN: - /* If not optimizing, call the library function. */ - if (!optimize) - break; - - if (arglist == 0 - /* Arg could be non-pointer if user redeclared this fcn wrong. */ - || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE) - return const0_rtx; - else - { - tree src = TREE_VALUE (arglist); - tree len = c_strlen (src); - - int align - = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; - - rtx result, src_rtx, char_rtx; - enum machine_mode insn_mode = value_mode, char_mode; - enum insn_code icode; - - /* If the length is known, just return it. */ - if (len != 0) - return expand_expr (len, target, mode, 0); - - /* If SRC is not a pointer type, don't do this operation inline. */ - if (align == 0) - break; - - /* Call a function if we can't compute strlen in the right mode. */ - - while (insn_mode != VOIDmode) - { - icode = strlen_optab->handlers[(int) insn_mode].insn_code; - if (icode != CODE_FOR_nothing) - break; - - insn_mode = GET_MODE_WIDER_MODE (insn_mode); - } - if (insn_mode == VOIDmode) - break; - - /* Make a place to write the result of the instruction. */ - result = target; - if (! (result != 0 - && GET_CODE (result) == REG - && GET_MODE (result) == insn_mode - && REGNO (result) >= FIRST_PSEUDO_REGISTER)) - result = gen_reg_rtx (insn_mode); - - /* Make sure the operands are acceptable to the predicates. */ - - if (! (*insn_operand_predicate[(int)icode][0]) (result, insn_mode)) - result = gen_reg_rtx (insn_mode); - - src_rtx = memory_address (BLKmode, - expand_expr (src, NULL_RTX, Pmode, - EXPAND_NORMAL)); - if (! (*insn_operand_predicate[(int)icode][1]) (src_rtx, Pmode)) - src_rtx = copy_to_mode_reg (Pmode, src_rtx); - - char_rtx = const0_rtx; - char_mode = insn_operand_mode[(int)icode][2]; - if (! (*insn_operand_predicate[(int)icode][2]) (char_rtx, char_mode)) - char_rtx = copy_to_mode_reg (char_mode, char_rtx); - - emit_insn (GEN_FCN (icode) (result, - gen_rtx (MEM, BLKmode, src_rtx), - char_rtx, GEN_INT (align))); - - /* Return the value in the proper mode for this function. */ - if (GET_MODE (result) == value_mode) - return result; - else if (target != 0) - { - convert_move (target, result, 0); - return target; - } - else - return convert_to_mode (value_mode, result, 0); - } - - case BUILT_IN_STRCPY: - /* If not optimizing, call the library function. */ - if (!optimize) - break; - - if (arglist == 0 - /* Arg could be non-pointer if user redeclared this fcn wrong. */ - || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE - || TREE_CHAIN (arglist) == 0 - || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE) - return const0_rtx; - else - { - tree len = c_strlen (TREE_VALUE (TREE_CHAIN (arglist))); - - if (len == 0) - break; - - len = size_binop (PLUS_EXPR, len, integer_one_node); - - chainon (arglist, build_tree_list (NULL_TREE, len)); - } - - /* Drops in. */ - case BUILT_IN_MEMCPY: - /* If not optimizing, call the library function. */ - if (!optimize) - break; - - if (arglist == 0 - /* Arg could be non-pointer if user redeclared this fcn wrong. */ - || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE - || TREE_CHAIN (arglist) == 0 - || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE - || TREE_CHAIN (TREE_CHAIN (arglist)) == 0 - || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))))) != INTEGER_TYPE) - return const0_rtx; - else - { - tree dest = TREE_VALUE (arglist); - tree src = TREE_VALUE (TREE_CHAIN (arglist)); - tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); - - int src_align - = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; - int dest_align - = get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; - rtx dest_rtx, dest_mem, src_mem; - - /* If either SRC or DEST is not a pointer type, don't do - this operation in-line. */ - if (src_align == 0 || dest_align == 0) - { - if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_STRCPY) - TREE_CHAIN (TREE_CHAIN (arglist)) = 0; - break; - } - - dest_rtx = expand_expr (dest, NULL_RTX, Pmode, EXPAND_NORMAL); - dest_mem = gen_rtx (MEM, BLKmode, - memory_address (BLKmode, dest_rtx)); - src_mem = gen_rtx (MEM, BLKmode, - memory_address (BLKmode, - expand_expr (src, NULL_RTX, - Pmode, - EXPAND_NORMAL))); - - /* Copy word part most expediently. */ - emit_block_move (dest_mem, src_mem, - expand_expr (len, NULL_RTX, VOIDmode, 0), - MIN (src_align, dest_align)); - return dest_rtx; - } - -/* These comparison functions need an instruction that returns an actual - index. An ordinary compare that just sets the condition codes - is not enough. */ -#ifdef HAVE_cmpstrsi - case BUILT_IN_STRCMP: - /* If not optimizing, call the library function. */ - if (!optimize) - break; - - if (arglist == 0 - /* Arg could be non-pointer if user redeclared this fcn wrong. */ - || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE - || TREE_CHAIN (arglist) == 0 - || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE) - return const0_rtx; - else if (!HAVE_cmpstrsi) - break; - { - tree arg1 = TREE_VALUE (arglist); - tree arg2 = TREE_VALUE (TREE_CHAIN (arglist)); - tree offset; - tree len, len2; - - len = c_strlen (arg1); - if (len) - len = size_binop (PLUS_EXPR, integer_one_node, len); - len2 = c_strlen (arg2); - if (len2) - len2 = size_binop (PLUS_EXPR, integer_one_node, len2); - - /* If we don't have a constant length for the first, use the length - of the second, if we know it. We don't require a constant for - this case; some cost analysis could be done if both are available - but neither is constant. For now, assume they're equally cheap. - - If both strings have constant lengths, use the smaller. This - could arise if optimization results in strcpy being called with - two fixed strings, or if the code was machine-generated. We should - add some code to the `memcmp' handler below to deal with such - situations, someday. */ - if (!len || TREE_CODE (len) != INTEGER_CST) - { - if (len2) - len = len2; - else if (len == 0) - break; - } - else if (len2 && TREE_CODE (len2) == INTEGER_CST) - { - if (tree_int_cst_lt (len2, len)) - len = len2; - } - - chainon (arglist, build_tree_list (NULL_TREE, len)); - } - - /* Drops in. */ - case BUILT_IN_MEMCMP: - /* If not optimizing, call the library function. */ - if (!optimize) - break; - - if (arglist == 0 - /* Arg could be non-pointer if user redeclared this fcn wrong. */ - || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE - || TREE_CHAIN (arglist) == 0 - || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE - || TREE_CHAIN (TREE_CHAIN (arglist)) == 0 - || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))))) != INTEGER_TYPE) - return const0_rtx; - else if (!HAVE_cmpstrsi) - break; - { - tree arg1 = TREE_VALUE (arglist); - tree arg2 = TREE_VALUE (TREE_CHAIN (arglist)); - tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); - rtx result; - - int arg1_align - = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; - int arg2_align - = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; - enum machine_mode insn_mode - = insn_operand_mode[(int) CODE_FOR_cmpstrsi][0]; - - /* If we don't have POINTER_TYPE, call the function. */ - if (arg1_align == 0 || arg2_align == 0) - { - if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_STRCMP) - TREE_CHAIN (TREE_CHAIN (arglist)) = 0; - break; - } - - /* Make a place to write the result of the instruction. */ - result = target; - if (! (result != 0 - && GET_CODE (result) == REG && GET_MODE (result) == insn_mode - && REGNO (result) >= FIRST_PSEUDO_REGISTER)) - result = gen_reg_rtx (insn_mode); - - emit_insn (gen_cmpstrsi (result, - gen_rtx (MEM, BLKmode, - expand_expr (arg1, NULL_RTX, Pmode, - EXPAND_NORMAL)), - gen_rtx (MEM, BLKmode, - expand_expr (arg2, NULL_RTX, Pmode, - EXPAND_NORMAL)), - expand_expr (len, NULL_RTX, VOIDmode, 0), - GEN_INT (MIN (arg1_align, arg2_align)))); - - /* Return the value in the proper mode for this function. */ - mode = TYPE_MODE (TREE_TYPE (exp)); - if (GET_MODE (result) == mode) - return result; - else if (target != 0) - { - convert_move (target, result, 0); - return target; - } - else - return convert_to_mode (mode, result, 0); - } -#else - case BUILT_IN_STRCMP: - case BUILT_IN_MEMCMP: - break; -#endif - - default: /* just do library call, if unknown builtin */ - error ("built-in function `%s' not currently supported", - IDENTIFIER_POINTER (DECL_NAME (fndecl))); - } - - /* The switch statement above can drop through to cause the function - to be called normally. */ - - return expand_call (exp, target, ignore); -} - -/* Built-in functions to perform an untyped call and return. */ - -/* For each register that may be used for calling a function, this - gives a mode used to copy the register's value. VOIDmode indicates - the register is not used for calling a function. If the machine - has register windows, this gives only the outbound registers. - INCOMING_REGNO gives the corresponding inbound register. */ -static enum machine_mode apply_args_mode[FIRST_PSEUDO_REGISTER]; - -/* For each register that may be used for returning values, this gives - a mode used to copy the register's value. VOIDmode indicates the - register is not used for returning values. If the machine has - register windows, this gives only the outbound registers. - INCOMING_REGNO gives the corresponding inbound register. */ -static enum machine_mode apply_result_mode[FIRST_PSEUDO_REGISTER]; - -/* Return the size required for the block returned by __builtin_apply_args, - and initialize apply_args_mode. */ -static int -apply_args_size () -{ - static int size = -1; - int align, regno; - enum machine_mode mode; - - /* The values computed by this function never change. */ - if (size < 0) - { - /* The first value is the incoming arg-pointer. */ - size = GET_MODE_SIZE (Pmode); - - /* The second value is the structure value address unless this is - passed as an "invisible" first argument. */ - if (struct_value_rtx) - size += GET_MODE_SIZE (Pmode); - - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if (FUNCTION_ARG_REGNO_P (regno)) - { - /* Search for the proper mode for copying this register's - value. I'm not sure this is right, but it works so far. */ - enum machine_mode best_mode = VOIDmode; - - for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); - mode != VOIDmode; - mode = GET_MODE_WIDER_MODE (mode)) - if (HARD_REGNO_MODE_OK (regno, mode) - && HARD_REGNO_NREGS (regno, mode) == 1) - best_mode = mode; - - if (best_mode == VOIDmode) - for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); - mode != VOIDmode; - mode = GET_MODE_WIDER_MODE (mode)) - if (HARD_REGNO_MODE_OK (regno, mode) - && (mov_optab->handlers[(int) mode].insn_code - != CODE_FOR_nothing)) - best_mode = mode; - - mode = best_mode; - if (mode == VOIDmode) - abort (); - - align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; - if (size % align != 0) - size = CEIL (size, align) * align; - size += GET_MODE_SIZE (mode); - apply_args_mode[regno] = mode; - } - else - apply_args_mode[regno] = VOIDmode; - } - return size; -} - -/* Return the size required for the block returned by __builtin_apply, - and initialize apply_result_mode. */ -static int -apply_result_size () -{ - static int size = -1; - int align, regno; - enum machine_mode mode; - - /* The values computed by this function never change. */ - if (size < 0) - { - size = 0; - - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if (FUNCTION_VALUE_REGNO_P (regno)) - { - /* Search for the proper mode for copying this register's - value. I'm not sure this is right, but it works so far. */ - enum machine_mode best_mode = VOIDmode; - - for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); - mode != TImode; - mode = GET_MODE_WIDER_MODE (mode)) - if (HARD_REGNO_MODE_OK (regno, mode)) - best_mode = mode; - - if (best_mode == VOIDmode) - for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); - mode != VOIDmode; - mode = GET_MODE_WIDER_MODE (mode)) - if (HARD_REGNO_MODE_OK (regno, mode) - && (mov_optab->handlers[(int) mode].insn_code - != CODE_FOR_nothing)) - best_mode = mode; - - mode = best_mode; - if (mode == VOIDmode) - abort (); - - align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; - if (size % align != 0) - size = CEIL (size, align) * align; - size += GET_MODE_SIZE (mode); - apply_result_mode[regno] = mode; - } - else - apply_result_mode[regno] = VOIDmode; - - /* Allow targets that use untyped_call and untyped_return to override - the size so that machine-specific information can be stored here. */ -#ifdef APPLY_RESULT_SIZE - size = APPLY_RESULT_SIZE; -#endif - } - return size; -} - -#if defined (HAVE_untyped_call) || defined (HAVE_untyped_return) -/* Create a vector describing the result block RESULT. If SAVEP is true, - the result block is used to save the values; otherwise it is used to - restore the values. */ -static rtx -result_vector (savep, result) - int savep; - rtx result; -{ - int regno, size, align, nelts; - enum machine_mode mode; - rtx reg, mem; - rtx *savevec = (rtx *) alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx)); - - size = nelts = 0; - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if ((mode = apply_result_mode[regno]) != VOIDmode) - { - align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; - if (size % align != 0) - size = CEIL (size, align) * align; - reg = gen_rtx (REG, mode, savep ? INCOMING_REGNO (regno) : regno); - mem = change_address (result, mode, - plus_constant (XEXP (result, 0), size)); - savevec[nelts++] = (savep - ? gen_rtx (SET, VOIDmode, mem, reg) - : gen_rtx (SET, VOIDmode, reg, mem)); - size += GET_MODE_SIZE (mode); - } - return gen_rtx (PARALLEL, VOIDmode, gen_rtvec_v (nelts, savevec)); -} -#endif /* HAVE_untyped_call or HAVE_untyped_return */ - - -/* Save the state required to perform an untyped call with the same - arguments as were passed to the current function. */ -static rtx -expand_builtin_apply_args () -{ - rtx registers; - int size, align, regno; - enum machine_mode mode; - - /* Create a block where the arg-pointer, structure value address, - and argument registers can be saved. */ - registers = assign_stack_local (BLKmode, apply_args_size (), -1); - - /* Walk past the arg-pointer and structure value address. */ - size = GET_MODE_SIZE (Pmode); - if (struct_value_rtx) - size += GET_MODE_SIZE (Pmode); - - /* Save each register used in calling a function to the block. */ - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if ((mode = apply_args_mode[regno]) != VOIDmode) - { - align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; - if (size % align != 0) - size = CEIL (size, align) * align; - emit_move_insn (change_address (registers, mode, - plus_constant (XEXP (registers, 0), - size)), - gen_rtx (REG, mode, INCOMING_REGNO (regno))); - size += GET_MODE_SIZE (mode); - } - - /* Save the arg pointer to the block. */ - emit_move_insn (change_address (registers, Pmode, XEXP (registers, 0)), - copy_to_reg (virtual_incoming_args_rtx)); - size = GET_MODE_SIZE (Pmode); - - /* Save the structure value address unless this is passed as an - "invisible" first argument. */ - if (struct_value_incoming_rtx) - { - emit_move_insn (change_address (registers, Pmode, - plus_constant (XEXP (registers, 0), - size)), - copy_to_reg (struct_value_incoming_rtx)); - size += GET_MODE_SIZE (Pmode); - } - - /* Return the address of the block. */ - return copy_addr_to_reg (XEXP (registers, 0)); -} - -/* Perform an untyped call and save the state required to perform an - untyped return of whatever value was returned by the given function. */ -static rtx -expand_builtin_apply (function, arguments, argsize) - rtx function, arguments, argsize; -{ - int size, align, regno; - enum machine_mode mode; - rtx incoming_args, result, reg, dest, call_insn; - rtx old_stack_level = 0; - rtx use_insns = 0; - - /* Create a block where the return registers can be saved. */ - result = assign_stack_local (BLKmode, apply_result_size (), -1); - - /* ??? The argsize value should be adjusted here. */ - - /* Fetch the arg pointer from the ARGUMENTS block. */ - incoming_args = gen_reg_rtx (Pmode); - emit_move_insn (incoming_args, - gen_rtx (MEM, Pmode, arguments)); -#ifndef STACK_GROWS_DOWNWARD - incoming_args = expand_binop (Pmode, add_optab, incoming_args, argsize, - incoming_args, 0, OPTAB_LIB_WIDEN); -#endif - - /* Perform postincrements before actually calling the function. */ - emit_queue (); - - /* Push a new argument block and copy the arguments. */ - do_pending_stack_adjust (); - emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); - - /* Push a block of memory onto the stack to store the memory arguments. - Save the address in a register, and copy the memory arguments. ??? I - haven't figured out how the calling convention macros effect this, - but it's likely that the source and/or destination addresses in - the block copy will need updating in machine specific ways. */ - dest = copy_addr_to_reg (push_block (argsize, 0, 0)); - emit_block_move (gen_rtx (MEM, BLKmode, dest), - gen_rtx (MEM, BLKmode, incoming_args), - argsize, - PARM_BOUNDARY / BITS_PER_UNIT); - - /* Refer to the argument block. */ - apply_args_size (); - arguments = gen_rtx (MEM, BLKmode, arguments); - - /* Walk past the arg-pointer and structure value address. */ - size = GET_MODE_SIZE (Pmode); - if (struct_value_rtx) - size += GET_MODE_SIZE (Pmode); - - /* Restore each of the registers previously saved. Make USE insns - for each of these registers for use in making the call. */ - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if ((mode = apply_args_mode[regno]) != VOIDmode) - { - align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; - if (size % align != 0) - size = CEIL (size, align) * align; - reg = gen_rtx (REG, mode, regno); - emit_move_insn (reg, - change_address (arguments, mode, - plus_constant (XEXP (arguments, 0), - size))); - - push_to_sequence (use_insns); - emit_insn (gen_rtx (USE, VOIDmode, reg)); - use_insns = get_insns (); - end_sequence (); - size += GET_MODE_SIZE (mode); - } - - /* Restore the structure value address unless this is passed as an - "invisible" first argument. */ - size = GET_MODE_SIZE (Pmode); - if (struct_value_rtx) - { - rtx value = gen_reg_rtx (Pmode); - emit_move_insn (value, - change_address (arguments, Pmode, - plus_constant (XEXP (arguments, 0), - size))); - emit_move_insn (struct_value_rtx, value); - if (GET_CODE (struct_value_rtx) == REG) - { - push_to_sequence (use_insns); - emit_insn (gen_rtx (USE, VOIDmode, struct_value_rtx)); - use_insns = get_insns (); - end_sequence (); - } - size += GET_MODE_SIZE (Pmode); - } - - /* All arguments and registers used for the call are set up by now! */ - function = prepare_call_address (function, NULL_TREE, &use_insns); - - /* Ensure address is valid. SYMBOL_REF is already valid, so no need, - and we don't want to load it into a register as an optimization, - because prepare_call_address already did it if it should be done. */ - if (GET_CODE (function) != SYMBOL_REF) - function = memory_address (FUNCTION_MODE, function); - - /* Generate the actual call instruction and save the return value. */ -#ifdef HAVE_untyped_call - if (HAVE_untyped_call) - emit_call_insn (gen_untyped_call (gen_rtx (MEM, FUNCTION_MODE, function), - result, result_vector (1, result))); - else -#endif -#ifdef HAVE_call_value - if (HAVE_call_value) - { - rtx valreg = 0; - - /* Locate the unique return register. It is not possible to - express a call that sets more than one return register using - call_value; use untyped_call for that. In fact, untyped_call - only needs to save the return registers in the given block. */ - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if ((mode = apply_result_mode[regno]) != VOIDmode) - { - if (valreg) - abort (); /* HAVE_untyped_call required. */ - valreg = gen_rtx (REG, mode, regno); - } - - emit_call_insn (gen_call_value (valreg, - gen_rtx (MEM, FUNCTION_MODE, function), - const0_rtx, NULL_RTX, const0_rtx)); - - emit_move_insn (change_address (result, GET_MODE (valreg), - XEXP (result, 0)), - valreg); - } - else -#endif - abort (); - - /* Find the CALL insn we just emitted and write the USE insns before it. */ - for (call_insn = get_last_insn (); - call_insn && GET_CODE (call_insn) != CALL_INSN; - call_insn = PREV_INSN (call_insn)) - ; - - if (! call_insn) - abort (); - - /* Put the USE insns before the CALL. */ - emit_insns_before (use_insns, call_insn); - - /* Restore the stack. */ - emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX); - - /* Return the address of the result block. */ - return copy_addr_to_reg (XEXP (result, 0)); -} - -/* Perform an untyped return. */ -static void -expand_builtin_return (result) - rtx result; -{ - int size, align, regno; - enum machine_mode mode; - rtx reg; - rtx use_insns = 0; - - apply_result_size (); - result = gen_rtx (MEM, BLKmode, result); - -#ifdef HAVE_untyped_return - if (HAVE_untyped_return) - { - emit_jump_insn (gen_untyped_return (result, result_vector (0, result))); - emit_barrier (); - return; - } -#endif - - /* Restore the return value and note that each value is used. */ - size = 0; - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if ((mode = apply_result_mode[regno]) != VOIDmode) - { - align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; - if (size % align != 0) - size = CEIL (size, align) * align; - reg = gen_rtx (REG, mode, INCOMING_REGNO (regno)); - emit_move_insn (reg, - change_address (result, mode, - plus_constant (XEXP (result, 0), - size))); - - push_to_sequence (use_insns); - emit_insn (gen_rtx (USE, VOIDmode, reg)); - use_insns = get_insns (); - end_sequence (); - size += GET_MODE_SIZE (mode); - } - - /* Put the USE insns before the return. */ - emit_insns (use_insns); - - /* Return whatever values was restored by jumping directly to the end - of the function. */ - expand_null_return (); -} - -/* Expand code for a post- or pre- increment or decrement - and return the RTX for the result. - POST is 1 for postinc/decrements and 0 for preinc/decrements. */ - -static rtx -expand_increment (exp, post) - register tree exp; - int post; -{ - register rtx op0, op1; - register rtx temp, value; - register tree incremented = TREE_OPERAND (exp, 0); - optab this_optab = add_optab; - int icode; - enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp)); - int op0_is_copy = 0; - - /* Stabilize any component ref that might need to be - evaluated more than once below. */ - if (!post - || TREE_CODE (incremented) == BIT_FIELD_REF - || (TREE_CODE (incremented) == COMPONENT_REF - && (TREE_CODE (TREE_OPERAND (incremented, 0)) != INDIRECT_REF - || DECL_BIT_FIELD (TREE_OPERAND (incremented, 1))))) - incremented = stabilize_reference (incremented); - - /* Compute the operands as RTX. - Note whether OP0 is the actual lvalue or a copy of it: - I believe it is a copy iff it is a register or subreg - and insns were generated in computing it. */ - - temp = get_last_insn (); - op0 = expand_expr (incremented, NULL_RTX, VOIDmode, 0); - - /* If OP0 is a SUBREG made for a promoted variable, we cannot increment - in place but intead must do sign- or zero-extension during assignment, - so we copy it into a new register and let the code below use it as - a copy. - - Note that we can safely modify this SUBREG since it is know not to be - shared (it was made by the expand_expr call above). */ - - if (GET_CODE (op0) == SUBREG && SUBREG_PROMOTED_VAR_P (op0)) - SUBREG_REG (op0) = copy_to_reg (SUBREG_REG (op0)); - - op0_is_copy = ((GET_CODE (op0) == SUBREG || GET_CODE (op0) == REG) - && temp != get_last_insn ()); - op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0); - - /* Decide whether incrementing or decrementing. */ - if (TREE_CODE (exp) == POSTDECREMENT_EXPR - || TREE_CODE (exp) == PREDECREMENT_EXPR) - this_optab = sub_optab; - - /* If OP0 is not the actual lvalue, but rather a copy in a register, - then we cannot just increment OP0. We must therefore contrive to - increment the original value. Then, for postincrement, we can return - OP0 since it is a copy of the old value. For preincrement, we want - to always expand here, since this generates better or equivalent code. */ - if (!post || op0_is_copy) - { - /* This is the easiest way to increment the value wherever it is. - Problems with multiple evaluation of INCREMENTED are prevented - because either (1) it is a component_ref or preincrement, - in which case it was stabilized above, or (2) it is an array_ref - with constant index in an array in a register, which is - safe to reevaluate. */ - tree newexp = build ((this_optab == add_optab - ? PLUS_EXPR : MINUS_EXPR), - TREE_TYPE (exp), - incremented, - TREE_OPERAND (exp, 1)); - temp = expand_assignment (incremented, newexp, ! post, 0); - return post ? op0 : temp; - } - - /* Convert decrement by a constant into a negative increment. */ - if (this_optab == sub_optab - && GET_CODE (op1) == CONST_INT) - { - op1 = GEN_INT (- INTVAL (op1)); - this_optab = add_optab; - } - - if (post) - { - /* We have a true reference to the value in OP0. - If there is an insn to add or subtract in this mode, queue it. */ - -#if 0 /* Turned off to avoid making extra insn for indexed memref. */ - op0 = stabilize (op0); -#endif - - icode = (int) this_optab->handlers[(int) mode].insn_code; - if (icode != (int) CODE_FOR_nothing - /* Make sure that OP0 is valid for operands 0 and 1 - of the insn we want to queue. */ - && (*insn_operand_predicate[icode][0]) (op0, mode) - && (*insn_operand_predicate[icode][1]) (op0, mode)) - { - if (! (*insn_operand_predicate[icode][2]) (op1, mode)) - op1 = force_reg (mode, op1); - - return enqueue_insn (op0, GEN_FCN (icode) (op0, op0, op1)); - } - } - - /* Preincrement, or we can't increment with one simple insn. */ - if (post) - /* Save a copy of the value before inc or dec, to return it later. */ - temp = value = copy_to_reg (op0); - else - /* Arrange to return the incremented value. */ - /* Copy the rtx because expand_binop will protect from the queue, - and the results of that would be invalid for us to return - if our caller does emit_queue before using our result. */ - temp = copy_rtx (value = op0); - - /* Increment however we can. */ - op1 = expand_binop (mode, this_optab, value, op1, op0, - TREE_UNSIGNED (TREE_TYPE (exp)), OPTAB_LIB_WIDEN); - /* Make sure the value is stored into OP0. */ - if (op1 != op0) - emit_move_insn (op0, op1); - - return temp; -} - -/* Expand all function calls contained within EXP, innermost ones first. - But don't look within expressions that have sequence points. - For each CALL_EXPR, record the rtx for its value - in the CALL_EXPR_RTL field. */ - -static void -preexpand_calls (exp) - tree exp; -{ - register int nops, i; - int type = TREE_CODE_CLASS (TREE_CODE (exp)); - - if (! do_preexpand_calls) - return; - - /* Only expressions and references can contain calls. */ - - if (type != 'e' && type != '<' && type != '1' && type != '2' && type != 'r') - return; - - switch (TREE_CODE (exp)) - { - case CALL_EXPR: - /* Do nothing if already expanded. */ - if (CALL_EXPR_RTL (exp) != 0) - return; - - /* Do nothing to built-in functions. */ - if (TREE_CODE (TREE_OPERAND (exp, 0)) != ADDR_EXPR - || TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) != FUNCTION_DECL - || ! DECL_BUILT_IN (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))) - CALL_EXPR_RTL (exp) = expand_call (exp, NULL_RTX, 0); - return; - - case COMPOUND_EXPR: - case COND_EXPR: - case TRUTH_ANDIF_EXPR: - case TRUTH_ORIF_EXPR: - /* If we find one of these, then we can be sure - the adjust will be done for it (since it makes jumps). - Do it now, so that if this is inside an argument - of a function, we don't get the stack adjustment - after some other args have already been pushed. */ - do_pending_stack_adjust (); - return; - - case BLOCK: - case RTL_EXPR: - case WITH_CLEANUP_EXPR: - return; - - case SAVE_EXPR: - if (SAVE_EXPR_RTL (exp) != 0) - return; - } - - nops = tree_code_length[(int) TREE_CODE (exp)]; - for (i = 0; i < nops; i++) - if (TREE_OPERAND (exp, i) != 0) - { - type = TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, i))); - if (type == 'e' || type == '<' || type == '1' || type == '2' - || type == 'r') - preexpand_calls (TREE_OPERAND (exp, i)); - } -} - -/* At the start of a function, record that we have no previously-pushed - arguments waiting to be popped. */ - -void -init_pending_stack_adjust () -{ - pending_stack_adjust = 0; -} - -/* When exiting from function, if safe, clear out any pending stack adjust - so the adjustment won't get done. */ - -void -clear_pending_stack_adjust () -{ -#ifdef EXIT_IGNORE_STACK - if (! flag_omit_frame_pointer && EXIT_IGNORE_STACK - && ! (DECL_INLINE (current_function_decl) && ! flag_no_inline) - && ! flag_inline_functions) - pending_stack_adjust = 0; -#endif -} - -/* Pop any previously-pushed arguments that have not been popped yet. */ - -void -do_pending_stack_adjust () -{ - if (inhibit_defer_pop == 0) - { - if (pending_stack_adjust != 0) - adjust_stack (GEN_INT (pending_stack_adjust)); - pending_stack_adjust = 0; - } -} - -/* Expand all cleanups up to OLD_CLEANUPS. - Needed here, and also for language-dependent calls. */ - -void -expand_cleanups_to (old_cleanups) - tree old_cleanups; -{ - while (cleanups_this_call != old_cleanups) - { - expand_expr (TREE_VALUE (cleanups_this_call), NULL_RTX, VOIDmode, 0); - cleanups_this_call = TREE_CHAIN (cleanups_this_call); - } -} - -/* Expand conditional expressions. */ - -/* Generate code to evaluate EXP and jump to LABEL if the value is zero. - LABEL is an rtx of code CODE_LABEL, in this function and all the - functions here. */ - -void -jumpifnot (exp, label) - tree exp; - rtx label; -{ - do_jump (exp, label, NULL_RTX); -} - -/* Generate code to evaluate EXP and jump to LABEL if the value is nonzero. */ - -void -jumpif (exp, label) - tree exp; - rtx label; -{ - do_jump (exp, NULL_RTX, label); -} - -/* Generate code to evaluate EXP and jump to IF_FALSE_LABEL if - the result is zero, or IF_TRUE_LABEL if the result is one. - Either of IF_FALSE_LABEL and IF_TRUE_LABEL may be zero, - meaning fall through in that case. - - do_jump always does any pending stack adjust except when it does not - actually perform a jump. An example where there is no jump - is when EXP is `(foo (), 0)' and IF_FALSE_LABEL is null. - - This function is responsible for optimizing cases such as - &&, || and comparison operators in EXP. */ - -void -do_jump (exp, if_false_label, if_true_label) - tree exp; - rtx if_false_label, if_true_label; -{ - register enum tree_code code = TREE_CODE (exp); - /* Some cases need to create a label to jump to - in order to properly fall through. - These cases set DROP_THROUGH_LABEL nonzero. */ - rtx drop_through_label = 0; - rtx temp; - rtx comparison = 0; - int i; - tree type; - - emit_queue (); - - switch (code) - { - case ERROR_MARK: - break; - - case INTEGER_CST: - temp = integer_zerop (exp) ? if_false_label : if_true_label; - if (temp) - emit_jump (temp); - break; - -#if 0 - /* This is not true with #pragma weak */ - case ADDR_EXPR: - /* The address of something can never be zero. */ - if (if_true_label) - emit_jump (if_true_label); - break; -#endif - - case NOP_EXPR: - if (TREE_CODE (TREE_OPERAND (exp, 0)) == COMPONENT_REF - || TREE_CODE (TREE_OPERAND (exp, 0)) == BIT_FIELD_REF - || TREE_CODE (TREE_OPERAND (exp, 0)) == ARRAY_REF) - goto normal; - case CONVERT_EXPR: - /* If we are narrowing the operand, we have to do the compare in the - narrower mode. */ - if ((TYPE_PRECISION (TREE_TYPE (exp)) - < TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (exp, 0))))) - goto normal; - case NON_LVALUE_EXPR: - case REFERENCE_EXPR: - case ABS_EXPR: - case NEGATE_EXPR: - case LROTATE_EXPR: - case RROTATE_EXPR: - /* These cannot change zero->non-zero or vice versa. */ - do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label); - break; - -#if 0 - /* This is never less insns than evaluating the PLUS_EXPR followed by - a test and can be longer if the test is eliminated. */ - case PLUS_EXPR: - /* Reduce to minus. */ - exp = build (MINUS_EXPR, TREE_TYPE (exp), - TREE_OPERAND (exp, 0), - fold (build1 (NEGATE_EXPR, TREE_TYPE (TREE_OPERAND (exp, 1)), - TREE_OPERAND (exp, 1)))); - /* Process as MINUS. */ -#endif - - case MINUS_EXPR: - /* Non-zero iff operands of minus differ. */ - comparison = compare (build (NE_EXPR, TREE_TYPE (exp), - TREE_OPERAND (exp, 0), - TREE_OPERAND (exp, 1)), - NE, NE); - break; - - case BIT_AND_EXPR: - /* If we are AND'ing with a small constant, do this comparison in the - smallest type that fits. If the machine doesn't have comparisons - that small, it will be converted back to the wider comparison. - This helps if we are testing the sign bit of a narrower object. - combine can't do this for us because it can't know whether a - ZERO_EXTRACT or a compare in a smaller mode exists, but we do. */ - - if (! SLOW_BYTE_ACCESS - && TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST - && TYPE_PRECISION (TREE_TYPE (exp)) <= HOST_BITS_PER_WIDE_INT - && (i = floor_log2 (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)))) >= 0 - && (type = type_for_size (i + 1, 1)) != 0 - && TYPE_PRECISION (type) < TYPE_PRECISION (TREE_TYPE (exp)) - && (cmp_optab->handlers[(int) TYPE_MODE (type)].insn_code - != CODE_FOR_nothing)) - { - do_jump (convert (type, exp), if_false_label, if_true_label); - break; - } - goto normal; - - case TRUTH_NOT_EXPR: - do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label); - break; - - case TRUTH_ANDIF_EXPR: - if (if_false_label == 0) - if_false_label = drop_through_label = gen_label_rtx (); - do_jump (TREE_OPERAND (exp, 0), if_false_label, NULL_RTX); - do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label); - break; - - case TRUTH_ORIF_EXPR: - if (if_true_label == 0) - if_true_label = drop_through_label = gen_label_rtx (); - do_jump (TREE_OPERAND (exp, 0), NULL_RTX, if_true_label); - do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label); - break; - - case COMPOUND_EXPR: - expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, 0); - free_temp_slots (); - emit_queue (); - do_pending_stack_adjust (); - do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label); - break; - - case COMPONENT_REF: - case BIT_FIELD_REF: - case ARRAY_REF: - { - int bitsize, bitpos, unsignedp; - enum machine_mode mode; - tree type; - tree offset; - int volatilep = 0; - - /* Get description of this reference. We don't actually care - about the underlying object here. */ - get_inner_reference (exp, &bitsize, &bitpos, &offset, - &mode, &unsignedp, &volatilep); - - type = type_for_size (bitsize, unsignedp); - if (! SLOW_BYTE_ACCESS - && type != 0 && bitsize >= 0 - && TYPE_PRECISION (type) < TYPE_PRECISION (TREE_TYPE (exp)) - && (cmp_optab->handlers[(int) TYPE_MODE (type)].insn_code - != CODE_FOR_nothing)) - { - do_jump (convert (type, exp), if_false_label, if_true_label); - break; - } - goto normal; - } - - case COND_EXPR: - /* Do (a ? 1 : 0) and (a ? 0 : 1) as special cases. */ - if (integer_onep (TREE_OPERAND (exp, 1)) - && integer_zerop (TREE_OPERAND (exp, 2))) - do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label); - - else if (integer_zerop (TREE_OPERAND (exp, 1)) - && integer_onep (TREE_OPERAND (exp, 2))) - do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label); - - else - { - register rtx label1 = gen_label_rtx (); - drop_through_label = gen_label_rtx (); - do_jump (TREE_OPERAND (exp, 0), label1, NULL_RTX); - /* Now the THEN-expression. */ - do_jump (TREE_OPERAND (exp, 1), - if_false_label ? if_false_label : drop_through_label, - if_true_label ? if_true_label : drop_through_label); - /* In case the do_jump just above never jumps. */ - do_pending_stack_adjust (); - emit_label (label1); - /* Now the ELSE-expression. */ - do_jump (TREE_OPERAND (exp, 2), - if_false_label ? if_false_label : drop_through_label, - if_true_label ? if_true_label : drop_through_label); - } - break; - - case EQ_EXPR: - if (integer_zerop (TREE_OPERAND (exp, 1))) - do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label); - else if ((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) - == MODE_INT) - && - !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))) - do_jump_by_parts_equality (exp, if_false_label, if_true_label); - else - comparison = compare (exp, EQ, EQ); - break; - - case NE_EXPR: - if (integer_zerop (TREE_OPERAND (exp, 1))) - do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label); - else if ((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) - == MODE_INT) - && - !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))) - do_jump_by_parts_equality (exp, if_true_label, if_false_label); - else - comparison = compare (exp, NE, NE); - break; - - case LT_EXPR: - if ((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) - == MODE_INT) - && !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))) - do_jump_by_parts_greater (exp, 1, if_false_label, if_true_label); - else - comparison = compare (exp, LT, LTU); - break; - - case LE_EXPR: - if ((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) - == MODE_INT) - && !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))) - do_jump_by_parts_greater (exp, 0, if_true_label, if_false_label); - else - comparison = compare (exp, LE, LEU); - break; - - case GT_EXPR: - if ((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) - == MODE_INT) - && !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))) - do_jump_by_parts_greater (exp, 0, if_false_label, if_true_label); - else - comparison = compare (exp, GT, GTU); - break; - - case GE_EXPR: - if ((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) - == MODE_INT) - && !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))) - do_jump_by_parts_greater (exp, 1, if_true_label, if_false_label); - else - comparison = compare (exp, GE, GEU); - break; - - default: - normal: - temp = expand_expr (exp, NULL_RTX, VOIDmode, 0); -#if 0 - /* This is not needed any more and causes poor code since it causes - comparisons and tests from non-SI objects to have different code - sequences. */ - /* Copy to register to avoid generating bad insns by cse - from (set (mem ...) (arithop)) (set (cc0) (mem ...)). */ - if (!cse_not_expected && GET_CODE (temp) == MEM) - temp = copy_to_reg (temp); -#endif - do_pending_stack_adjust (); - if (GET_CODE (temp) == CONST_INT) - comparison = (temp == const0_rtx ? const0_rtx : const_true_rtx); - else if (GET_CODE (temp) == LABEL_REF) - comparison = const_true_rtx; - else if (GET_MODE_CLASS (GET_MODE (temp)) == MODE_INT - && !can_compare_p (GET_MODE (temp))) - /* Note swapping the labels gives us not-equal. */ - do_jump_by_parts_equality_rtx (temp, if_true_label, if_false_label); - else if (GET_MODE (temp) != VOIDmode) - comparison = compare_from_rtx (temp, CONST0_RTX (GET_MODE (temp)), - NE, TREE_UNSIGNED (TREE_TYPE (exp)), - GET_MODE (temp), NULL_RTX, 0); - else - abort (); - } - - /* Do any postincrements in the expression that was tested. */ - emit_queue (); - - /* If COMPARISON is nonzero here, it is an rtx that can be substituted - straight into a conditional jump instruction as the jump condition. - Otherwise, all the work has been done already. */ - - if (comparison == const_true_rtx) - { - if (if_true_label) - emit_jump (if_true_label); - } - else if (comparison == const0_rtx) - { - if (if_false_label) - emit_jump (if_false_label); - } - else if (comparison) - do_jump_for_compare (comparison, if_false_label, if_true_label); - - free_temp_slots (); - - if (drop_through_label) - { - /* If do_jump produces code that might be jumped around, - do any stack adjusts from that code, before the place - where control merges in. */ - do_pending_stack_adjust (); - emit_label (drop_through_label); - } -} - -/* Given a comparison expression EXP for values too wide to be compared - with one insn, test the comparison and jump to the appropriate label. - The code of EXP is ignored; we always test GT if SWAP is 0, - and LT if SWAP is 1. */ - -static void -do_jump_by_parts_greater (exp, swap, if_false_label, if_true_label) - tree exp; - int swap; - rtx if_false_label, if_true_label; -{ - rtx op0 = expand_expr (TREE_OPERAND (exp, swap), NULL_RTX, VOIDmode, 0); - rtx op1 = expand_expr (TREE_OPERAND (exp, !swap), NULL_RTX, VOIDmode, 0); - enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))); - int nwords = (GET_MODE_SIZE (mode) / UNITS_PER_WORD); - rtx drop_through_label = 0; - int unsignedp = TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))); - int i; - - if (! if_true_label || ! if_false_label) - drop_through_label = gen_label_rtx (); - if (! if_true_label) - if_true_label = drop_through_label; - if (! if_false_label) - if_false_label = drop_through_label; - - /* Compare a word at a time, high order first. */ - for (i = 0; i < nwords; i++) - { - rtx comp; - rtx op0_word, op1_word; - - if (WORDS_BIG_ENDIAN) - { - op0_word = operand_subword_force (op0, i, mode); - op1_word = operand_subword_force (op1, i, mode); - } - else - { - op0_word = operand_subword_force (op0, nwords - 1 - i, mode); - op1_word = operand_subword_force (op1, nwords - 1 - i, mode); - } - - /* All but high-order word must be compared as unsigned. */ - comp = compare_from_rtx (op0_word, op1_word, - (unsignedp || i > 0) ? GTU : GT, - unsignedp, word_mode, NULL_RTX, 0); - if (comp == const_true_rtx) - emit_jump (if_true_label); - else if (comp != const0_rtx) - do_jump_for_compare (comp, NULL_RTX, if_true_label); - - /* Consider lower words only if these are equal. */ - comp = compare_from_rtx (op0_word, op1_word, NE, unsignedp, word_mode, - NULL_RTX, 0); - if (comp == const_true_rtx) - emit_jump (if_false_label); - else if (comp != const0_rtx) - do_jump_for_compare (comp, NULL_RTX, if_false_label); - } - - if (if_false_label) - emit_jump (if_false_label); - if (drop_through_label) - emit_label (drop_through_label); -} - -/* Compare OP0 with OP1, word at a time, in mode MODE. - UNSIGNEDP says to do unsigned comparison. - Jump to IF_TRUE_LABEL if OP0 is greater, IF_FALSE_LABEL otherwise. */ - -static void -do_jump_by_parts_greater_rtx (mode, unsignedp, op0, op1, if_false_label, if_true_label) - enum machine_mode mode; - int unsignedp; - rtx op0, op1; - rtx if_false_label, if_true_label; -{ - int nwords = (GET_MODE_SIZE (mode) / UNITS_PER_WORD); - rtx drop_through_label = 0; - int i; - - if (! if_true_label || ! if_false_label) - drop_through_label = gen_label_rtx (); - if (! if_true_label) - if_true_label = drop_through_label; - if (! if_false_label) - if_false_label = drop_through_label; - - /* Compare a word at a time, high order first. */ - for (i = 0; i < nwords; i++) - { - rtx comp; - rtx op0_word, op1_word; - - if (WORDS_BIG_ENDIAN) - { - op0_word = operand_subword_force (op0, i, mode); - op1_word = operand_subword_force (op1, i, mode); - } - else - { - op0_word = operand_subword_force (op0, nwords - 1 - i, mode); - op1_word = operand_subword_force (op1, nwords - 1 - i, mode); - } - - /* All but high-order word must be compared as unsigned. */ - comp = compare_from_rtx (op0_word, op1_word, - (unsignedp || i > 0) ? GTU : GT, - unsignedp, word_mode, NULL_RTX, 0); - if (comp == const_true_rtx) - emit_jump (if_true_label); - else if (comp != const0_rtx) - do_jump_for_compare (comp, NULL_RTX, if_true_label); - - /* Consider lower words only if these are equal. */ - comp = compare_from_rtx (op0_word, op1_word, NE, unsignedp, word_mode, - NULL_RTX, 0); - if (comp == const_true_rtx) - emit_jump (if_false_label); - else if (comp != const0_rtx) - do_jump_for_compare (comp, NULL_RTX, if_false_label); - } - - if (if_false_label) - emit_jump (if_false_label); - if (drop_through_label) - emit_label (drop_through_label); -} - -/* Given an EQ_EXPR expression EXP for values too wide to be compared - with one insn, test the comparison and jump to the appropriate label. */ - -static void -do_jump_by_parts_equality (exp, if_false_label, if_true_label) - tree exp; - rtx if_false_label, if_true_label; -{ - rtx op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0); - rtx op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0); - enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))); - int nwords = (GET_MODE_SIZE (mode) / UNITS_PER_WORD); - int i; - rtx drop_through_label = 0; - - if (! if_false_label) - drop_through_label = if_false_label = gen_label_rtx (); - - for (i = 0; i < nwords; i++) - { - rtx comp = compare_from_rtx (operand_subword_force (op0, i, mode), - operand_subword_force (op1, i, mode), - EQ, TREE_UNSIGNED (TREE_TYPE (exp)), - word_mode, NULL_RTX, 0); - if (comp == const_true_rtx) - emit_jump (if_false_label); - else if (comp != const0_rtx) - do_jump_for_compare (comp, if_false_label, NULL_RTX); - } - - if (if_true_label) - emit_jump (if_true_label); - if (drop_through_label) - emit_label (drop_through_label); -} - -/* Jump according to whether OP0 is 0. - We assume that OP0 has an integer mode that is too wide - for the available compare insns. */ - -static void -do_jump_by_parts_equality_rtx (op0, if_false_label, if_true_label) - rtx op0; - rtx if_false_label, if_true_label; -{ - int nwords = GET_MODE_SIZE (GET_MODE (op0)) / UNITS_PER_WORD; - int i; - rtx drop_through_label = 0; - - if (! if_false_label) - drop_through_label = if_false_label = gen_label_rtx (); - - for (i = 0; i < nwords; i++) - { - rtx comp = compare_from_rtx (operand_subword_force (op0, i, - GET_MODE (op0)), - const0_rtx, EQ, 1, word_mode, NULL_RTX, 0); - if (comp == const_true_rtx) - emit_jump (if_false_label); - else if (comp != const0_rtx) - do_jump_for_compare (comp, if_false_label, NULL_RTX); - } - - if (if_true_label) - emit_jump (if_true_label); - if (drop_through_label) - emit_label (drop_through_label); -} - -/* Given a comparison expression in rtl form, output conditional branches to - IF_TRUE_LABEL, IF_FALSE_LABEL, or both. */ - -static void -do_jump_for_compare (comparison, if_false_label, if_true_label) - rtx comparison, if_false_label, if_true_label; -{ - if (if_true_label) - { - if (bcc_gen_fctn[(int) GET_CODE (comparison)] != 0) - emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (comparison)]) (if_true_label)); - else - abort (); - - if (if_false_label) - emit_jump (if_false_label); - } - else if (if_false_label) - { - rtx insn; - rtx prev = PREV_INSN (get_last_insn ()); - rtx branch = 0; - - /* Output the branch with the opposite condition. Then try to invert - what is generated. If more than one insn is a branch, or if the - branch is not the last insn written, abort. If we can't invert - the branch, emit make a true label, redirect this jump to that, - emit a jump to the false label and define the true label. */ - - if (bcc_gen_fctn[(int) GET_CODE (comparison)] != 0) - emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (comparison)]) (if_false_label)); - else - abort (); - - /* Here we get the insn before what was just emitted. - On some machines, emitting the branch can discard - the previous compare insn and emit a replacement. */ - if (prev == 0) - /* If there's only one preceding insn... */ - insn = get_insns (); - else - insn = NEXT_INSN (prev); - - for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == JUMP_INSN) - { - if (branch) - abort (); - branch = insn; - } - - if (branch != get_last_insn ()) - abort (); - - if (! invert_jump (branch, if_false_label)) - { - if_true_label = gen_label_rtx (); - redirect_jump (branch, if_true_label); - emit_jump (if_false_label); - emit_label (if_true_label); - } - } -} - -/* Generate code for a comparison expression EXP - (including code to compute the values to be compared) - and set (CC0) according to the result. - SIGNED_CODE should be the rtx operation for this comparison for - signed data; UNSIGNED_CODE, likewise for use if data is unsigned. - - We force a stack adjustment unless there are currently - things pushed on the stack that aren't yet used. */ - -static rtx -compare (exp, signed_code, unsigned_code) - register tree exp; - enum rtx_code signed_code, unsigned_code; -{ - register rtx op0 - = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0); - register rtx op1 - = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0); - register tree type = TREE_TYPE (TREE_OPERAND (exp, 0)); - register enum machine_mode mode = TYPE_MODE (type); - int unsignedp = TREE_UNSIGNED (type); - enum rtx_code code = unsignedp ? unsigned_code : signed_code; - - return compare_from_rtx (op0, op1, code, unsignedp, mode, - ((mode == BLKmode) - ? expr_size (TREE_OPERAND (exp, 0)) : NULL_RTX), - TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); -} - -/* Like compare but expects the values to compare as two rtx's. - The decision as to signed or unsigned comparison must be made by the caller. - - If MODE is BLKmode, SIZE is an RTX giving the size of the objects being - compared. - - If ALIGN is non-zero, it is the alignment of this type; if zero, the - size of MODE should be used. */ - -rtx -compare_from_rtx (op0, op1, code, unsignedp, mode, size, align) - register rtx op0, op1; - enum rtx_code code; - int unsignedp; - enum machine_mode mode; - rtx size; - int align; -{ - rtx tem; - - /* If one operand is constant, make it the second one. Only do this - if the other operand is not constant as well. */ - - if ((CONSTANT_P (op0) && ! CONSTANT_P (op1)) - || (GET_CODE (op0) == CONST_INT && GET_CODE (op1) != CONST_INT)) - { - tem = op0; - op0 = op1; - op1 = tem; - code = swap_condition (code); - } - - if (flag_force_mem) - { - op0 = force_not_mem (op0); - op1 = force_not_mem (op1); - } - - do_pending_stack_adjust (); - - if (GET_CODE (op0) == CONST_INT && GET_CODE (op1) == CONST_INT - && (tem = simplify_relational_operation (code, mode, op0, op1)) != 0) - return tem; - -#if 0 - /* There's no need to do this now that combine.c can eliminate lots of - sign extensions. This can be less efficient in certain cases on other - machines. */ - - /* If this is a signed equality comparison, we can do it as an - unsigned comparison since zero-extension is cheaper than sign - extension and comparisons with zero are done as unsigned. This is - the case even on machines that can do fast sign extension, since - zero-extension is easier to combine with other operations than - sign-extension is. If we are comparing against a constant, we must - convert it to what it would look like unsigned. */ - if ((code == EQ || code == NE) && ! unsignedp - && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT) - { - if (GET_CODE (op1) == CONST_INT - && (INTVAL (op1) & GET_MODE_MASK (GET_MODE (op0))) != INTVAL (op1)) - op1 = GEN_INT (INTVAL (op1) & GET_MODE_MASK (GET_MODE (op0))); - unsignedp = 1; - } -#endif - - emit_cmp_insn (op0, op1, code, size, mode, unsignedp, align); - - return gen_rtx (code, VOIDmode, cc0_rtx, const0_rtx); -} - -/* Generate code to calculate EXP using a store-flag instruction - and return an rtx for the result. EXP is either a comparison - or a TRUTH_NOT_EXPR whose operand is a comparison. - - If TARGET is nonzero, store the result there if convenient. - - If ONLY_CHEAP is non-zero, only do this if it is likely to be very - cheap. - - Return zero if there is no suitable set-flag instruction - available on this machine. - - Once expand_expr has been called on the arguments of the comparison, - we are committed to doing the store flag, since it is not safe to - re-evaluate the expression. We emit the store-flag insn by calling - emit_store_flag, but only expand the arguments if we have a reason - to believe that emit_store_flag will be successful. If we think that - it will, but it isn't, we have to simulate the store-flag with a - set/jump/set sequence. */ - -static rtx -do_store_flag (exp, target, mode, only_cheap) - tree exp; - rtx target; - enum machine_mode mode; - int only_cheap; -{ - enum rtx_code code; - tree arg0, arg1, type; - tree tem; - enum machine_mode operand_mode; - int invert = 0; - int unsignedp; - rtx op0, op1; - enum insn_code icode; - rtx subtarget = target; - rtx result, label, pattern, jump_pat; - - /* If this is a TRUTH_NOT_EXPR, set a flag indicating we must invert the - result at the end. We can't simply invert the test since it would - have already been inverted if it were valid. This case occurs for - some floating-point comparisons. */ - - if (TREE_CODE (exp) == TRUTH_NOT_EXPR) - invert = 1, exp = TREE_OPERAND (exp, 0); - - arg0 = TREE_OPERAND (exp, 0); - arg1 = TREE_OPERAND (exp, 1); - type = TREE_TYPE (arg0); - operand_mode = TYPE_MODE (type); - unsignedp = TREE_UNSIGNED (type); - - /* We won't bother with BLKmode store-flag operations because it would mean - passing a lot of information to emit_store_flag. */ - if (operand_mode == BLKmode) - return 0; - - STRIP_NOPS (arg0); - STRIP_NOPS (arg1); - - /* Get the rtx comparison code to use. We know that EXP is a comparison - operation of some type. Some comparisons against 1 and -1 can be - converted to comparisons with zero. Do so here so that the tests - below will be aware that we have a comparison with zero. These - tests will not catch constants in the first operand, but constants - are rarely passed as the first operand. */ - - switch (TREE_CODE (exp)) - { - case EQ_EXPR: - code = EQ; - break; - case NE_EXPR: - code = NE; - break; - case LT_EXPR: - if (integer_onep (arg1)) - arg1 = integer_zero_node, code = unsignedp ? LEU : LE; - else - code = unsignedp ? LTU : LT; - break; - case LE_EXPR: - if (! unsignedp && integer_all_onesp (arg1)) - arg1 = integer_zero_node, code = LT; - else - code = unsignedp ? LEU : LE; - break; - case GT_EXPR: - if (! unsignedp && integer_all_onesp (arg1)) - arg1 = integer_zero_node, code = GE; - else - code = unsignedp ? GTU : GT; - break; - case GE_EXPR: - if (integer_onep (arg1)) - arg1 = integer_zero_node, code = unsignedp ? GTU : GT; - else - code = unsignedp ? GEU : GE; - break; - default: - abort (); - } - - /* Put a constant second. */ - if (TREE_CODE (arg0) == REAL_CST || TREE_CODE (arg0) == INTEGER_CST) - { - tem = arg0; arg0 = arg1; arg1 = tem; - code = swap_condition (code); - } - - /* If this is an equality or inequality test of a single bit, we can - do this by shifting the bit being tested to the low-order bit and - masking the result with the constant 1. If the condition was EQ, - we xor it with 1. This does not require an scc insn and is faster - than an scc insn even if we have it. */ - - if ((code == NE || code == EQ) - && TREE_CODE (arg0) == BIT_AND_EXPR && integer_zerop (arg1) - && integer_pow2p (TREE_OPERAND (arg0, 1)) - && TYPE_PRECISION (type) <= HOST_BITS_PER_WIDE_INT) - { - int bitnum = exact_log2 (INTVAL (expand_expr (TREE_OPERAND (arg0, 1), - NULL_RTX, VOIDmode, 0))); - - if (subtarget == 0 || GET_CODE (subtarget) != REG - || GET_MODE (subtarget) != operand_mode - || ! safe_from_p (subtarget, TREE_OPERAND (arg0, 0))) - subtarget = 0; - - op0 = expand_expr (TREE_OPERAND (arg0, 0), subtarget, VOIDmode, 0); - - if (bitnum != 0) - op0 = expand_shift (RSHIFT_EXPR, GET_MODE (op0), op0, - size_int (bitnum), target, 1); - - if (GET_MODE (op0) != mode) - op0 = convert_to_mode (mode, op0, 1); - - if (bitnum != TYPE_PRECISION (type) - 1) - op0 = expand_and (op0, const1_rtx, target); - - if ((code == EQ && ! invert) || (code == NE && invert)) - op0 = expand_binop (mode, xor_optab, op0, const1_rtx, target, 0, - OPTAB_LIB_WIDEN); - - return op0; - } - - /* Now see if we are likely to be able to do this. Return if not. */ - if (! can_compare_p (operand_mode)) - return 0; - icode = setcc_gen_code[(int) code]; - if (icode == CODE_FOR_nothing - || (only_cheap && insn_operand_mode[(int) icode][0] != mode)) - { - /* We can only do this if it is one of the special cases that - can be handled without an scc insn. */ - if ((code == LT && integer_zerop (arg1)) - || (! only_cheap && code == GE && integer_zerop (arg1))) - ; - else if (BRANCH_COST >= 0 - && ! only_cheap && (code == NE || code == EQ) - && TREE_CODE (type) != REAL_TYPE - && ((abs_optab->handlers[(int) operand_mode].insn_code - != CODE_FOR_nothing) - || (ffs_optab->handlers[(int) operand_mode].insn_code - != CODE_FOR_nothing))) - ; - else - return 0; - } - - preexpand_calls (exp); - if (subtarget == 0 || GET_CODE (subtarget) != REG - || GET_MODE (subtarget) != operand_mode - || ! safe_from_p (subtarget, arg1)) - subtarget = 0; - - op0 = expand_expr (arg0, subtarget, VOIDmode, 0); - op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); - - if (target == 0) - target = gen_reg_rtx (mode); - - /* Pass copies of OP0 and OP1 in case they contain a QUEUED. This is safe - because, if the emit_store_flag does anything it will succeed and - OP0 and OP1 will not be used subsequently. */ - - result = emit_store_flag (target, code, - queued_subexp_p (op0) ? copy_rtx (op0) : op0, - queued_subexp_p (op1) ? copy_rtx (op1) : op1, - operand_mode, unsignedp, 1); - - if (result) - { - if (invert) - result = expand_binop (mode, xor_optab, result, const1_rtx, - result, 0, OPTAB_LIB_WIDEN); - return result; - } - - /* If this failed, we have to do this with set/compare/jump/set code. */ - if (target == 0 || GET_CODE (target) != REG - || reg_mentioned_p (target, op0) || reg_mentioned_p (target, op1)) - target = gen_reg_rtx (GET_MODE (target)); - - emit_move_insn (target, invert ? const0_rtx : const1_rtx); - result = compare_from_rtx (op0, op1, code, unsignedp, - operand_mode, NULL_RTX, 0); - if (GET_CODE (result) == CONST_INT) - return (((result == const0_rtx && ! invert) - || (result != const0_rtx && invert)) - ? const0_rtx : const1_rtx); - - label = gen_label_rtx (); - if (bcc_gen_fctn[(int) code] == 0) - abort (); - - emit_jump_insn ((*bcc_gen_fctn[(int) code]) (label)); - emit_move_insn (target, invert ? const1_rtx : const0_rtx); - emit_label (label); - - return target; -} - -/* Generate a tablejump instruction (used for switch statements). */ - -#ifdef HAVE_tablejump - -/* INDEX is the value being switched on, with the lowest value - in the table already subtracted. - MODE is its expected mode (needed if INDEX is constant). - RANGE is the length of the jump table. - TABLE_LABEL is a CODE_LABEL rtx for the table itself. - - DEFAULT_LABEL is a CODE_LABEL rtx to jump to if the - index value is out of range. */ - -void -do_tablejump (index, mode, range, table_label, default_label) - rtx index, range, table_label, default_label; - enum machine_mode mode; -{ - register rtx temp, vector; - - /* Do an unsigned comparison (in the proper mode) between the index - expression and the value which represents the length of the range. - Since we just finished subtracting the lower bound of the range - from the index expression, this comparison allows us to simultaneously - check that the original index expression value is both greater than - or equal to the minimum value of the range and less than or equal to - the maximum value of the range. */ - - emit_cmp_insn (range, index, LTU, NULL_RTX, mode, 1, 0); - emit_jump_insn (gen_bltu (default_label)); - - /* If index is in range, it must fit in Pmode. - Convert to Pmode so we can index with it. */ - if (mode != Pmode) - index = convert_to_mode (Pmode, index, 1); - - /* If flag_force_addr were to affect this address - it could interfere with the tricky assumptions made - about addresses that contain label-refs, - which may be valid only very near the tablejump itself. */ - /* ??? The only correct use of CASE_VECTOR_MODE is the one inside the - GET_MODE_SIZE, because this indicates how large insns are. The other - uses should all be Pmode, because they are addresses. This code - could fail if addresses and insns are not the same size. */ - index = memory_address_noforce - (CASE_VECTOR_MODE, - gen_rtx (PLUS, Pmode, - gen_rtx (MULT, Pmode, index, - GEN_INT (GET_MODE_SIZE (CASE_VECTOR_MODE))), - gen_rtx (LABEL_REF, Pmode, table_label))); - temp = gen_reg_rtx (CASE_VECTOR_MODE); - vector = gen_rtx (MEM, CASE_VECTOR_MODE, index); - RTX_UNCHANGING_P (vector) = 1; - convert_move (temp, vector, 0); - - emit_jump_insn (gen_tablejump (temp, table_label)); - -#ifndef CASE_VECTOR_PC_RELATIVE - /* If we are generating PIC code or if the table is PC-relative, the - table and JUMP_INSN must be adjacent, so don't output a BARRIER. */ - if (! flag_pic) - emit_barrier (); -#endif -} - -#endif /* HAVE_tablejump */ diff --git a/gnu/usr.bin/cc/common/final.c b/gnu/usr.bin/cc/common/final.c deleted file mode 100644 index 67a30fa353..0000000000 --- a/gnu/usr.bin/cc/common/final.c +++ /dev/null @@ -1,2740 +0,0 @@ -/* Convert RTL to assembler code and output it, for GNU compiler. - Copyright (C) 1987, 1988, 1989, 1992 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This is the final pass of the compiler. - It looks at the rtl code for a function and outputs assembler code. - - Call `final_start_function' to output the assembler code for function entry, - `final' to output assembler code for some RTL code, - `final_end_function' to output assembler code for function exit. - If a function is compiled in several pieces, each piece is - output separately with `final'. - - Some optimizations are also done at this level. - Move instructions that were made unnecessary by good register allocation - are detected and omitted from the output. (Though most of these - are removed by the last jump pass.) - - Instructions to set the condition codes are omitted when it can be - seen that the condition codes already had the desired values. - - In some cases it is sufficient if the inherited condition codes - have related values, but this may require the following insn - (the one that tests the condition codes) to be modified. - - The code for the function prologue and epilogue are generated - directly as assembler code by the macros FUNCTION_PROLOGUE and - FUNCTION_EPILOGUE. Those instructions never exist as rtl. */ - -#include "config.h" -#include "gvarargs.h" -#include "rtl.h" -#include "regs.h" -#include "insn-config.h" -#include "insn-flags.h" -#include "insn-attr.h" -#include "insn-codes.h" -#include "recog.h" -#include "conditions.h" -#include "flags.h" -#include "real.h" -#include "hard-reg-set.h" -#include "defaults.h" - -#include - -#include "output.h" - -/* Get N_SLINE and N_SOL from stab.h if we can expect the file to exist. */ -#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) -#if defined (USG) || defined (NO_STAB_H) -#include "gstab.h" /* If doing DBX on sysV, use our own stab.h. */ -#else -#include /* On BSD, use the system's stab.h. */ -#endif /* not USG */ -#endif /* DBX_DEBUGGING_INFO || XCOFF_DEBUGGING_INFO */ - -#ifdef XCOFF_DEBUGGING_INFO -#include "xcoffout.h" -#endif - -/* .stabd code for line number. */ -#ifndef N_SLINE -#define N_SLINE 0x44 -#endif - -/* .stabs code for included file name. */ -#ifndef N_SOL -#define N_SOL 0x84 -#endif - -#ifndef INT_TYPE_SIZE -#define INT_TYPE_SIZE BITS_PER_WORD -#endif - -/* If we aren't using cc0, CC_STATUS_INIT shouldn't exist. So define a - null default for it to save conditionalization later. */ -#ifndef CC_STATUS_INIT -#define CC_STATUS_INIT -#endif - -/* How to start an assembler comment. */ -#ifndef ASM_COMMENT_START -#define ASM_COMMENT_START ";#" -#endif - -rtx peephole (); -void output_asm_insn (); -rtx alter_subreg (); -static int alter_cond (); -void output_asm_label (); -static void output_operand (); -void output_address (); -void output_addr_const (); -static void output_source_line (); -rtx final_scan_insn (); -void profile_function (); -static void profile_after_prologue (); - -#ifdef HAVE_ATTR_length -static int asm_insn_count (); -#endif - -/* Nonzero means this function is a leaf function, with no function calls. - This variable exists to be examined in FUNCTION_PROLOGUE - and FUNCTION_EPILOGUE. Always zero, unless set by some action. */ -int leaf_function; - -int leaf_function_p (); - -#ifdef LEAF_REGISTERS -int only_leaf_regs_used (); -static void leaf_renumber_regs (); -void leaf_renumber_regs_insn (); -#endif - -/* Last insn processed by final_scan_insn. */ -static rtx debug_insn = 0; - -/* Line number of last NOTE. */ -static int last_linenum; - -/* Number of basic blocks seen so far; - used if profile_block_flag is set. */ -static int count_basic_blocks; - -/* Nonzero while outputting an `asm' with operands. - This means that inconsistencies are the user's fault, so don't abort. - The precise value is the insn being output, to pass to error_for_asm. */ -static rtx this_is_asm_operands; - -/* Number of operands of this insn, for an `asm' with operands. */ -static int insn_noperands; - -/* Compare optimization flag. */ - -static rtx last_ignored_compare = 0; - -/* Flag indicating this insn is the start of a new basic block. */ - -static int new_block = 1; - -/* All the symbol-blocks (levels of scoping) in the compilation - are assigned sequence numbers in order of appearance of the - beginnings of the symbol-blocks. Both final and dbxout do this, - and assume that they will both give the same number to each block. - Final uses these sequence numbers to generate assembler label names - LBBnnn and LBEnnn for the beginning and end of the symbol-block. - Dbxout uses the sequence numbers to generate references to the same labels - from the dbx debugging information. - - Sdb records this level at the beginning of each function, - in order to find the current level when recursing down declarations. - It outputs the block beginning and endings - at the point in the asm file where the blocks would begin and end. */ - -int next_block_index; - -/* Assign a unique number to each insn that is output. - This can be used to generate unique local labels. */ - -static int insn_counter = 0; - -#ifdef HAVE_cc0 -/* This variable contains machine-dependent flags (defined in tm.h) - set and examined by output routines - that describe how to interpret the condition codes properly. */ - -CC_STATUS cc_status; - -/* During output of an insn, this contains a copy of cc_status - from before the insn. */ - -CC_STATUS cc_prev_status; -#endif - -/* Indexed by hardware reg number, is 1 if that register is ever - used in the current function. - - In life_analysis, or in stupid_life_analysis, this is set - up to record the hard regs used explicitly. Reload adds - in the hard regs used for holding pseudo regs. Final uses - it to generate the code in the function prologue and epilogue - to save and restore registers as needed. */ - -char regs_ever_live[FIRST_PSEUDO_REGISTER]; - -/* Nonzero means current function must be given a frame pointer. - Set in stmt.c if anything is allocated on the stack there. - Set in reload1.c if anything is allocated on the stack there. */ - -int frame_pointer_needed; - -/* Assign unique numbers to labels generated for profiling. */ - -int profile_label_no; - -/* Length so far allocated in PENDING_BLOCKS. */ - -static int max_block_depth; - -/* Stack of sequence numbers of symbol-blocks of which we have seen the - beginning but not yet the end. Sequence numbers are assigned at - the beginning; this stack allows us to find the sequence number - of a block that is ending. */ - -static int *pending_blocks; - -/* Number of elements currently in use in PENDING_BLOCKS. */ - -static int block_depth; - -/* Nonzero if have enabled APP processing of our assembler output. */ - -static int app_on; - -/* If we are outputting an insn sequence, this contains the sequence rtx. - Zero otherwise. */ - -rtx final_sequence; - -/* Indexed by line number, nonzero if there is a note for that line. */ - -static char *line_note_exists; - -/* Initialize data in final at the beginning of a compilation. */ - -void -init_final (filename) - char *filename; -{ - next_block_index = 2; - app_on = 0; - max_block_depth = 20; - pending_blocks = (int *) xmalloc (20 * sizeof *pending_blocks); - final_sequence = 0; -} - -/* Called at end of source file, - to output the block-profiling table for this entire compilation. */ - -void -end_final (filename) - char *filename; -{ - int i; - - if (profile_block_flag) - { - char name[12]; - - data_section (); - - /* Output the main header, of 6 words: - 0: 1 if this file's initialized, else 0. - 1: address of file name. - 2: address of table of counts. - 4: number of counts in the table. - 5: always 0, for compatibility with Sun. - 6: extra word added by GNU: address of address table - which contains addresses of basic blocks, - in parallel with the table of counts. */ - ASM_OUTPUT_ALIGN (asm_out_file, - exact_log2 (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); - - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 0); - assemble_integer (const0_rtx, UNITS_PER_WORD, 1); - ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 1); - assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1); - ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 2); - assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1); - assemble_integer (GEN_INT (count_basic_blocks), UNITS_PER_WORD, 1); - assemble_integer (const0_rtx, UNITS_PER_WORD, 1); - ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 3); - assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1); - - /* Output the file name. */ - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 1); - { - int len = strlen (filename); - char *data_file = (char *) alloca (len + 3); - strcpy (data_file, filename); - strip_off_ending (data_file, len); - strcat (data_file, ".d"); - assemble_string (data_file, strlen (data_file) + 1); - } - - /* Realign data section. */ - ASM_OUTPUT_ALIGN (asm_out_file, - exact_log2 (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); - - /* Make space for the table of counts. */ - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 2); - if (count_basic_blocks != 0) - assemble_zeros (INT_TYPE_SIZE / BITS_PER_UNIT * count_basic_blocks); - - /* Output the table of addresses. */ - readonly_data_section (); - /* Realign in new section */ - ASM_OUTPUT_ALIGN (asm_out_file, - floor_log2 (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 3); - for (i = 0; i < count_basic_blocks; i++) - { - char name[12]; - ASM_GENERATE_INTERNAL_LABEL (name, "LPB", i); - assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), - UNITS_PER_WORD, 1); - } - - /* End with the address of the table of addresses, - so we can find it easily, as the last word in the file's text. */ - ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 3); - assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1); - } -} - -/* Enable APP processing of subsequent output. - Used before the output from an `asm' statement. */ - -void -app_enable () -{ - if (! app_on) - { - fprintf (asm_out_file, ASM_APP_ON); - app_on = 1; - } -} - -/* Enable APP processing of subsequent output. - Called from varasm.c before most kinds of output. */ - -void -app_disable () -{ - if (app_on) - { - fprintf (asm_out_file, ASM_APP_OFF); - app_on = 0; - } -} - -/* Return the number of slots filled in the current - delayed branch sequence (we don't count the insn needing the - delay slot). Zero if not in a delayed branch sequence. */ - -#ifdef DELAY_SLOTS -int -dbr_sequence_length () -{ - if (final_sequence != 0) - return XVECLEN (final_sequence, 0) - 1; - else - return 0; -} -#endif - -/* The next two pages contain routines used to compute the length of an insn - and to shorten branches. */ - -/* Arrays for insn lengths, and addresses. The latter is referenced by - `insn_current_length'. */ - -static short *insn_lengths; -int *insn_addresses; - -/* Address of insn being processed. Used by `insn_current_length'. */ -int insn_current_address; - -/* Indicate the branch shortening hasn't yet been done. */ - -void -init_insn_lengths () -{ - insn_lengths = 0; -} - -/* Obtain the current length of an insn. If branch shortening has been done, - get its actual length. Otherwise, get its maximum length. */ - -int -get_attr_length (insn) - rtx insn; -{ -#ifdef HAVE_ATTR_length - rtx body; - int i; - int length = 0; - - if (insn_lengths) - return insn_lengths[INSN_UID (insn)]; - else - switch (GET_CODE (insn)) - { - case NOTE: - case BARRIER: - case CODE_LABEL: - return 0; - - case CALL_INSN: - length = insn_default_length (insn); - break; - - case JUMP_INSN: - body = PATTERN (insn); - if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC) - { - /* This only takes room if jump tables go into the text section. */ -#if !defined(READONLY_DATA_SECTION) || defined(JUMP_TABLES_IN_TEXT_SECTION) - length = (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC) - * GET_MODE_SIZE (GET_MODE (body))); - - /* Be pessimistic and assume worst-case alignment. */ - length += (GET_MODE_SIZE (GET_MODE (body)) - 1); -#else - return 0; -#endif - } - else - length = insn_default_length (insn); - break; - - case INSN: - body = PATTERN (insn); - if (GET_CODE (body) == USE || GET_CODE (body) == CLOBBER) - return 0; - - else if (GET_CODE (body) == ASM_INPUT || asm_noperands (body) >= 0) - length = asm_insn_count (insn) * insn_default_length (insn); - else if (GET_CODE (body) == SEQUENCE) - for (i = 0; i < XVECLEN (body, 0); i++) - length += get_attr_length (XVECEXP (body, 0, i)); - else - length = insn_default_length (insn); - } - -#ifdef ADJUST_INSN_LENGTH - ADJUST_INSN_LENGTH (insn, length); -#endif - return length; -#else /* not HAVE_ATTR_length */ - return 0; -#endif /* not HAVE_ATTR_length */ -} - -/* Make a pass over all insns and compute their actual lengths by shortening - any branches of variable length if possible. */ - -/* Give a default value for the lowest address in a function. */ - -#ifndef FIRST_INSN_ADDRESS -#define FIRST_INSN_ADDRESS 0 -#endif - -void -shorten_branches (first) - rtx first; -{ -#ifdef HAVE_ATTR_length - rtx insn; - int something_changed = 1; - int max_uid = 0; - char *varying_length; - rtx body; - int uid; - - /* Compute maximum UID and allocate arrays. */ - for (insn = first; insn; insn = NEXT_INSN (insn)) - if (INSN_UID (insn) > max_uid) - max_uid = INSN_UID (insn); - - max_uid++; - insn_lengths = (short *) oballoc (max_uid * sizeof (short)); - insn_addresses = (int *) oballoc (max_uid * sizeof (int)); - varying_length = (char *) oballoc (max_uid * sizeof (char)); - - /* Compute initial lengths, addresses, and varying flags for each insn. */ - for (insn_current_address = FIRST_INSN_ADDRESS, insn = first; - insn != 0; - insn_current_address += insn_lengths[uid], insn = NEXT_INSN (insn)) - { - uid = INSN_UID (insn); - insn_addresses[uid] = insn_current_address; - insn_lengths[uid] = 0; - varying_length[uid] = 0; - - if (GET_CODE (insn) == NOTE || GET_CODE (insn) == BARRIER - || GET_CODE (insn) == CODE_LABEL) - continue; - - body = PATTERN (insn); - if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC) - { - /* This only takes room if read-only data goes into the text - section. */ -#if !defined(READONLY_DATA_SECTION) || defined(JUMP_TABLES_IN_TEXT_SECTION) - int unitsize = GET_MODE_SIZE (GET_MODE (body)); - - insn_lengths[uid] = (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC) - * GET_MODE_SIZE (GET_MODE (body))); - - /* Account for possible alignment. */ - insn_lengths[uid] - += unitsize - (insn_current_address & (unitsize - 1)); -#else - ; -#endif - } - else if (asm_noperands (body) >= 0) - insn_lengths[uid] = asm_insn_count (body) * insn_default_length (insn); - else if (GET_CODE (body) == SEQUENCE) - { - int i; - int const_delay_slots; -#ifdef DELAY_SLOTS - const_delay_slots = const_num_delay_slots (XVECEXP (body, 0, 0)); -#else - const_delay_slots = 0; -#endif - /* Inside a delay slot sequence, we do not do any branch shortening - if the shortening could change the number of delay slots - of the branch. */ - for (i = 0; i < XVECLEN (body, 0); i++) - { - rtx inner_insn = XVECEXP (body, 0, i); - int inner_uid = INSN_UID (inner_insn); - int inner_length; - - if (asm_noperands (PATTERN (XVECEXP (body, 0, i))) >= 0) - inner_length = (asm_insn_count (PATTERN (inner_insn)) - * insn_default_length (inner_insn)); - else - inner_length = insn_default_length (inner_insn); - - insn_lengths[inner_uid] = inner_length; - if (const_delay_slots) - { - if ((varying_length[inner_uid] - = insn_variable_length_p (inner_insn)) != 0) - varying_length[uid] = 1; - insn_addresses[inner_uid] = (insn_current_address + - insn_lengths[uid]); - } - else - varying_length[inner_uid] = 0; - insn_lengths[uid] += inner_length; - } - } - else if (GET_CODE (body) != USE && GET_CODE (body) != CLOBBER) - { - insn_lengths[uid] = insn_default_length (insn); - varying_length[uid] = insn_variable_length_p (insn); - } - - /* If needed, do any adjustment. */ -#ifdef ADJUST_INSN_LENGTH - ADJUST_INSN_LENGTH (insn, insn_lengths[uid]); -#endif - } - - /* Now loop over all the insns finding varying length insns. For each, - get the current insn length. If it has changed, reflect the change. - When nothing changes for a full pass, we are done. */ - - while (something_changed) - { - something_changed = 0; - for (insn_current_address = FIRST_INSN_ADDRESS, insn = first; - insn != 0; - insn = NEXT_INSN (insn)) - { - int new_length; - int tmp_length; - - uid = INSN_UID (insn); - insn_addresses[uid] = insn_current_address; - if (! varying_length[uid]) - { - insn_current_address += insn_lengths[uid]; - continue; - } - if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE) - { - int i; - - body = PATTERN (insn); - new_length = 0; - for (i = 0; i < XVECLEN (body, 0); i++) - { - rtx inner_insn = XVECEXP (body, 0, i); - int inner_uid = INSN_UID (inner_insn); - int inner_length; - - insn_addresses[inner_uid] = insn_current_address; - - /* insn_current_length returns 0 for insns with a - non-varying length. */ - if (! varying_length[inner_uid]) - inner_length = insn_lengths[inner_uid]; - else - inner_length = insn_current_length (inner_insn); - - if (inner_length != insn_lengths[inner_uid]) - { - insn_lengths[inner_uid] = inner_length; - something_changed = 1; - } - insn_current_address += insn_lengths[inner_uid]; - new_length += inner_length; - } - } - else - { - new_length = insn_current_length (insn); - insn_current_address += new_length; - } - -#ifdef SHORTEN_WITH_ADJUST_INSN_LENGTH -#ifdef ADJUST_INSN_LENGTH - /* If needed, do any adjustment. */ - tmp_length = new_length; - ADJUST_INSN_LENGTH (insn, new_length); - insn_current_address += (new_length - tmp_length); -#endif -#endif - - if (new_length != insn_lengths[uid]) - { - insn_lengths[uid] = new_length; - something_changed = 1; - } - } - } -#endif /* HAVE_ATTR_length */ -} - -#ifdef HAVE_ATTR_length -/* Given the body of an INSN known to be generated by an ASM statement, return - the number of machine instructions likely to be generated for this insn. - This is used to compute its length. */ - -static int -asm_insn_count (body) - rtx body; -{ - char *template; - int count = 1; - - for (template = decode_asm_operands (body, NULL_PTR, NULL_PTR, - NULL_PTR, NULL_PTR); - *template; template++) - if (*template == ';' || *template == '\n') - count++; - - return count; -} -#endif - -/* Output assembler code for the start of a function, - and initialize some of the variables in this file - for the new function. The label for the function and associated - assembler pseudo-ops have already been output in `assemble_start_function'. - - FIRST is the first insn of the rtl for the function being compiled. - FILE is the file to write assembler code to. - OPTIMIZE is nonzero if we should eliminate redundant - test and compare insns. */ - -void -final_start_function (first, file, optimize) - rtx first; - FILE *file; - int optimize; -{ - block_depth = 0; - - this_is_asm_operands = 0; - -#ifdef NON_SAVING_SETJMP - /* A function that calls setjmp should save and restore all the - call-saved registers on a system where longjmp clobbers them. */ - if (NON_SAVING_SETJMP && current_function_calls_setjmp) - { - int i; - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (!call_used_regs[i] && !call_fixed_regs[i]) - regs_ever_live[i] = 1; - } -#endif - - /* Initial line number is supposed to be output - before the function's prologue and label - so that the function's address will not appear to be - in the last statement of the preceding function. */ - if (NOTE_LINE_NUMBER (first) != NOTE_INSN_DELETED) - { - if (write_symbols == SDB_DEBUG) - /* For sdb, let's not, but say we did. - We need to set last_linenum for sdbout_function_begin, - but we can't have an actual line number before the .bf symbol. - (sdb_begin_function_line is not set, - and other compilers don't do it.) */ - last_linenum = NOTE_LINE_NUMBER (first); -#ifdef XCOFF_DEBUGGING_INFO - else if (write_symbols == XCOFF_DEBUG) - { - last_linenum = NOTE_LINE_NUMBER (first); - xcoffout_output_first_source_line (file, last_linenum); - } -#endif - else - output_source_line (file, first); - } - -#ifdef LEAF_REG_REMAP - if (leaf_function) - leaf_renumber_regs (first); -#endif - - /* The Sun386i and perhaps other machines don't work right - if the profiling code comes after the prologue. */ -#ifdef PROFILE_BEFORE_PROLOGUE - if (profile_flag) - profile_function (file); -#endif /* PROFILE_BEFORE_PROLOGUE */ - -#ifdef FUNCTION_PROLOGUE - /* First output the function prologue: code to set up the stack frame. */ - FUNCTION_PROLOGUE (file, get_frame_size ()); -#endif - -#if defined (SDB_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) - if (write_symbols == SDB_DEBUG || write_symbols == XCOFF_DEBUG) - next_block_index = 1; -#endif - - /* If the machine represents the prologue as RTL, the profiling code must - be emitted when NOTE_INSN_PROLOGUE_END is scanned. */ -#ifdef HAVE_prologue - if (! HAVE_prologue) -#endif - profile_after_prologue (file); - - profile_label_no++; -} - -static void -profile_after_prologue (file) - FILE *file; -{ -#ifdef FUNCTION_BLOCK_PROFILER - if (profile_block_flag) - { - FUNCTION_BLOCK_PROFILER (file, profile_label_no); - } -#endif /* FUNCTION_BLOCK_PROFILER */ - -#ifndef PROFILE_BEFORE_PROLOGUE - if (profile_flag) - profile_function (file); -#endif /* not PROFILE_BEFORE_PROLOGUE */ -} - -void -profile_function (file) - FILE *file; -{ - int align = MIN (BIGGEST_ALIGNMENT, INT_TYPE_SIZE); - int sval = current_function_returns_struct; - int cxt = current_function_needs_context; - - data_section (); - ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT)); - ASM_OUTPUT_INTERNAL_LABEL (file, "LP", profile_label_no); - assemble_integer (const0_rtx, UNITS_PER_WORD, 1); - - text_section (); - -#ifdef STRUCT_VALUE_INCOMING_REGNUM - if (sval) - ASM_OUTPUT_REG_PUSH (file, STRUCT_VALUE_INCOMING_REGNUM); -#else -#ifdef STRUCT_VALUE_REGNUM - if (sval) - ASM_OUTPUT_REG_PUSH (file, STRUCT_VALUE_REGNUM); -#endif -#endif - -#if 0 -#ifdef STATIC_CHAIN_INCOMING_REGNUM - if (cxt) - ASM_OUTPUT_REG_PUSH (file, STATIC_CHAIN_INCOMING_REGNUM); -#else -#ifdef STATIC_CHAIN_REGNUM - if (cxt) - ASM_OUTPUT_REG_PUSH (file, STATIC_CHAIN_REGNUM); -#endif -#endif -#endif /* 0 */ - - FUNCTION_PROFILER (file, profile_label_no); - -#if 0 -#ifdef STATIC_CHAIN_INCOMING_REGNUM - if (cxt) - ASM_OUTPUT_REG_POP (file, STATIC_CHAIN_INCOMING_REGNUM); -#else -#ifdef STATIC_CHAIN_REGNUM - if (cxt) - ASM_OUTPUT_REG_POP (file, STATIC_CHAIN_REGNUM); -#endif -#endif -#endif /* 0 */ - -#ifdef STRUCT_VALUE_INCOMING_REGNUM - if (sval) - ASM_OUTPUT_REG_POP (file, STRUCT_VALUE_INCOMING_REGNUM); -#else -#ifdef STRUCT_VALUE_REGNUM - if (sval) - ASM_OUTPUT_REG_POP (file, STRUCT_VALUE_REGNUM); -#endif -#endif -} - -/* Output assembler code for the end of a function. - For clarity, args are same as those of `final_start_function' - even though not all of them are needed. */ - -void -final_end_function (first, file, optimize) - rtx first; - FILE *file; - int optimize; -{ - if (app_on) - { - fprintf (file, ASM_APP_OFF); - app_on = 0; - } - -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG) - sdbout_end_function (last_linenum); -#endif - -#ifdef DWARF_DEBUGGING_INFO - if (write_symbols == DWARF_DEBUG) - dwarfout_end_function (); -#endif - -#ifdef XCOFF_DEBUGGING_INFO - if (write_symbols == XCOFF_DEBUG) - xcoffout_end_function (file, last_linenum); -#endif - -#ifdef FUNCTION_EPILOGUE - /* Finally, output the function epilogue: - code to restore the stack frame and return to the caller. */ - FUNCTION_EPILOGUE (file, get_frame_size ()); -#endif - -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG) - sdbout_end_epilogue (); -#endif - -#ifdef DWARF_DEBUGGING_INFO - if (write_symbols == DWARF_DEBUG) - dwarfout_end_epilogue (); -#endif - -#ifdef XCOFF_DEBUGGING_INFO - if (write_symbols == XCOFF_DEBUG) - xcoffout_end_epilogue (file); -#endif - - /* If FUNCTION_EPILOGUE is not defined, then the function body - itself contains return instructions wherever needed. */ -} - -/* Output assembler code for some insns: all or part of a function. - For description of args, see `final_start_function', above. - - PRESCAN is 1 if we are not really outputting, - just scanning as if we were outputting. - Prescanning deletes and rearranges insns just like ordinary output. - PRESCAN is -2 if we are outputting after having prescanned. - In this case, don't try to delete or rearrange insns - because that has already been done. - Prescanning is done only on certain machines. */ - -void -final (first, file, optimize, prescan) - rtx first; - FILE *file; - int optimize; - int prescan; -{ - register rtx insn; - int max_line = 0; - - last_ignored_compare = 0; - new_block = 1; - - /* Make a map indicating which line numbers appear in this function. - When producing SDB debugging info, delete troublesome line number - notes from inlined functions in other files as well as duplicate - line number notes. */ -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG) - { - rtx last = 0; - for (insn = first; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0) - { - if ((RTX_INTEGRATED_P (insn) - && strcmp (NOTE_SOURCE_FILE (insn), main_input_filename) != 0) - || (last != 0 - && NOTE_LINE_NUMBER (insn) == NOTE_LINE_NUMBER (last) - && NOTE_SOURCE_FILE (insn) == NOTE_SOURCE_FILE (last))) - { - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - continue; - } - last = insn; - if (NOTE_LINE_NUMBER (insn) > max_line) - max_line = NOTE_LINE_NUMBER (insn); - } - } - else -#endif - { - for (insn = first; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > max_line) - max_line = NOTE_LINE_NUMBER (insn); - } - - line_note_exists = (char *) oballoc (max_line + 1); - bzero (line_note_exists, max_line + 1); - - for (insn = first; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0) - line_note_exists[NOTE_LINE_NUMBER (insn)] = 1; - - init_recog (); - - CC_STATUS_INIT; - - /* Output the insns. */ - for (insn = NEXT_INSN (first); insn;) - insn = final_scan_insn (insn, file, optimize, prescan, 0); - - /* Do basic-block profiling here - if the last insn was a conditional branch. */ - if (profile_block_flag && new_block) - { - new_block = 0; - /* Enable the table of basic-block use counts - to point at the code it applies to. */ - ASM_OUTPUT_INTERNAL_LABEL (file, "LPB", count_basic_blocks); - /* Before first insn of this basic block, increment the - count of times it was entered. */ -#ifdef BLOCK_PROFILER - BLOCK_PROFILER (file, count_basic_blocks); - CC_STATUS_INIT; -#endif - count_basic_blocks++; - } -} - -/* The final scan for one insn, INSN. - Args are same as in `final', except that INSN - is the insn being scanned. - Value returned is the next insn to be scanned. - - NOPEEPHOLES is the flag to disallow peephole processing (currently - used for within delayed branch sequence output). */ - -rtx -final_scan_insn (insn, file, optimize, prescan, nopeepholes) - rtx insn; - FILE *file; - int optimize; - int prescan; - int nopeepholes; -{ - register int i; - insn_counter++; - - /* Ignore deleted insns. These can occur when we split insns (due to a - template of "#") while not optimizing. */ - if (INSN_DELETED_P (insn)) - return NEXT_INSN (insn); - - switch (GET_CODE (insn)) - { - case NOTE: - if (prescan > 0) - break; - - /* Align the beginning of a loop, for higher speed - on certain machines. */ - - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG && optimize > 0) - { -#ifdef ASM_OUTPUT_LOOP_ALIGN - rtx next = next_nonnote_insn (insn); - if (next && GET_CODE (next) == CODE_LABEL) - { - ASM_OUTPUT_LOOP_ALIGN (asm_out_file); - } -#endif - break; - } - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END) - break; - - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_PROLOGUE_END) - { -#ifdef FUNCTION_END_PROLOGUE - FUNCTION_END_PROLOGUE (file); -#endif - profile_after_prologue (file); - break; - } - -#ifdef FUNCTION_BEGIN_EPILOGUE - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EPILOGUE_BEG) - { - FUNCTION_BEGIN_EPILOGUE (file); - break; - } -#endif - - if (write_symbols == NO_DEBUG) - break; - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG) - { -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG) - sdbout_begin_function (last_linenum); -#endif -#ifdef XCOFF_DEBUGGING_INFO - if (write_symbols == XCOFF_DEBUG) - xcoffout_begin_function (file, last_linenum); -#endif -#ifdef DWARF_DEBUGGING_INFO - if (write_symbols == DWARF_DEBUG) - dwarfout_begin_function (); -#endif - break; - } - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED) - break; /* An insn that was "deleted" */ - if (app_on) - { - fprintf (file, ASM_APP_OFF); - app_on = 0; - } - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG - && (debug_info_level == DINFO_LEVEL_NORMAL - || debug_info_level == DINFO_LEVEL_VERBOSE -#ifdef DWARF_DEBUGGING_INFO - || write_symbols == DWARF_DEBUG -#endif - ) - ) - { - /* Beginning of a symbol-block. Assign it a sequence number - and push the number onto the stack PENDING_BLOCKS. */ - - if (block_depth == max_block_depth) - { - /* PENDING_BLOCKS is full; make it longer. */ - max_block_depth *= 2; - pending_blocks - = (int *) xrealloc (pending_blocks, - max_block_depth * sizeof (int)); - } - pending_blocks[block_depth++] = next_block_index; - - /* Output debugging info about the symbol-block beginning. */ - -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG) - sdbout_begin_block (file, last_linenum, next_block_index); -#endif -#ifdef XCOFF_DEBUGGING_INFO - if (write_symbols == XCOFF_DEBUG) - xcoffout_begin_block (file, last_linenum, next_block_index); -#endif -#ifdef DBX_DEBUGGING_INFO - if (write_symbols == DBX_DEBUG) - ASM_OUTPUT_INTERNAL_LABEL (file, "LBB", next_block_index); -#endif -#ifdef DWARF_DEBUGGING_INFO - if (write_symbols == DWARF_DEBUG && block_depth > 1) - dwarfout_begin_block (next_block_index); -#endif - - next_block_index++; - } - else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END - && (debug_info_level == DINFO_LEVEL_NORMAL - || debug_info_level == DINFO_LEVEL_VERBOSE -#ifdef DWARF_DEBUGGING_INFO - || write_symbols == DWARF_DEBUG -#endif - ) - ) - { - /* End of a symbol-block. Pop its sequence number off - PENDING_BLOCKS and output debugging info based on that. */ - - --block_depth; - -#ifdef XCOFF_DEBUGGING_INFO - if (write_symbols == XCOFF_DEBUG && block_depth >= 0) - xcoffout_end_block (file, last_linenum, pending_blocks[block_depth]); -#endif -#ifdef DBX_DEBUGGING_INFO - if (write_symbols == DBX_DEBUG && block_depth >= 0) - ASM_OUTPUT_INTERNAL_LABEL (file, "LBE", - pending_blocks[block_depth]); -#endif -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG && block_depth >= 0) - sdbout_end_block (file, last_linenum); -#endif -#ifdef DWARF_DEBUGGING_INFO - if (write_symbols == DWARF_DEBUG && block_depth >= 1) - dwarfout_end_block (pending_blocks[block_depth]); -#endif - } - else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED_LABEL - && (debug_info_level == DINFO_LEVEL_NORMAL - || debug_info_level == DINFO_LEVEL_VERBOSE)) - { -#ifdef DWARF_DEBUGGING_INFO - if (write_symbols == DWARF_DEBUG) - dwarfout_label (insn); -#endif - } - else if (NOTE_LINE_NUMBER (insn) > 0) - /* This note is a line-number. */ - { - register rtx note; - -#if 0 /* This is what we used to do. */ - output_source_line (file, insn); -#endif - int note_after = 0; - - /* If there is anything real after this note, - output it. If another line note follows, omit this one. */ - for (note = NEXT_INSN (insn); note; note = NEXT_INSN (note)) - { - if (GET_CODE (note) != NOTE && GET_CODE (note) != CODE_LABEL) - break; - /* These types of notes can be significant - so make sure the preceding line number stays. */ - else if (GET_CODE (note) == NOTE - && (NOTE_LINE_NUMBER (note) == NOTE_INSN_BLOCK_BEG - || NOTE_LINE_NUMBER (note) == NOTE_INSN_BLOCK_END - || NOTE_LINE_NUMBER (note) == NOTE_INSN_FUNCTION_BEG)) - break; - else if (GET_CODE (note) == NOTE && NOTE_LINE_NUMBER (note) > 0) - { - /* Another line note follows; we can delete this note - if no intervening line numbers have notes elsewhere. */ - int num; - for (num = NOTE_LINE_NUMBER (insn) + 1; - num < NOTE_LINE_NUMBER (note); - num++) - if (line_note_exists[num]) - break; - - if (num >= NOTE_LINE_NUMBER (note)) - note_after = 1; - break; - } - } - - /* Output this line note - if it is the first or the last line note in a row. */ - if (!note_after) - output_source_line (file, insn); - } - break; - - case BARRIER: -#ifdef ASM_OUTPUT_ALIGN_CODE - /* Don't litter the assembler output with needless alignments. A - BARRIER will be placed at the end of every function if HAVE_epilogue - is true. */ - if (NEXT_INSN (insn)) - ASM_OUTPUT_ALIGN_CODE (file); -#endif - break; - - case CODE_LABEL: - CC_STATUS_INIT; - if (prescan > 0) - break; - new_block = 1; -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG && LABEL_NAME (insn)) - sdbout_label (insn); -#endif -#ifdef DWARF_DEBUGGING_INFO - if (write_symbols == DWARF_DEBUG && LABEL_NAME (insn)) - dwarfout_label (insn); -#endif - if (app_on) - { - fprintf (file, ASM_APP_OFF); - app_on = 0; - } - if (NEXT_INSN (insn) != 0 - && GET_CODE (NEXT_INSN (insn)) == JUMP_INSN) - { - rtx nextbody = PATTERN (NEXT_INSN (insn)); - - /* If this label is followed by a jump-table, - make sure we put the label in the read-only section. Also - possibly write the label and jump table together. */ - - if (GET_CODE (nextbody) == ADDR_VEC - || GET_CODE (nextbody) == ADDR_DIFF_VEC) - { -#ifndef JUMP_TABLES_IN_TEXT_SECTION - readonly_data_section (); -#ifdef READONLY_DATA_SECTION - ASM_OUTPUT_ALIGN (file, - exact_log2 (BIGGEST_ALIGNMENT - / BITS_PER_UNIT)); -#endif /* READONLY_DATA_SECTION */ -#else /* JUMP_TABLES_IN_TEXT_SECTION */ - text_section (); -#endif /* JUMP_TABLES_IN_TEXT_SECTION */ -#ifdef ASM_OUTPUT_CASE_LABEL - ASM_OUTPUT_CASE_LABEL (file, "L", CODE_LABEL_NUMBER (insn), - NEXT_INSN (insn)); -#else - ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (insn)); -#endif - break; - } - } - - ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (insn)); - break; - - default: - { - register rtx body = PATTERN (insn); - int insn_code_number; - char *template; - rtx note; - - /* An INSN, JUMP_INSN or CALL_INSN. - First check for special kinds that recog doesn't recognize. */ - - if (GET_CODE (body) == USE /* These are just declarations */ - || GET_CODE (body) == CLOBBER) - break; - -#ifdef HAVE_cc0 - /* If there is a REG_CC_SETTER note on this insn, it means that - the setting of the condition code was done in the delay slot - of the insn that branched here. So recover the cc status - from the insn that set it. */ - - note = find_reg_note (insn, REG_CC_SETTER, NULL_RTX); - if (note) - { - NOTICE_UPDATE_CC (PATTERN (XEXP (note, 0)), XEXP (note, 0)); - cc_prev_status = cc_status; - } -#endif - - /* Detect insns that are really jump-tables - and output them as such. */ - - if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC) - { - register int vlen, idx; - - if (prescan > 0) - break; - - if (app_on) - { - fprintf (file, ASM_APP_OFF); - app_on = 0; - } - - vlen = XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC); - for (idx = 0; idx < vlen; idx++) - { - if (GET_CODE (body) == ADDR_VEC) - { -#ifdef ASM_OUTPUT_ADDR_VEC_ELT - ASM_OUTPUT_ADDR_VEC_ELT - (file, CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 0, idx), 0))); -#else - abort (); -#endif - } - else - { -#ifdef ASM_OUTPUT_ADDR_DIFF_ELT - ASM_OUTPUT_ADDR_DIFF_ELT - (file, - CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 1, idx), 0)), - CODE_LABEL_NUMBER (XEXP (XEXP (body, 0), 0))); -#else - abort (); -#endif - } - } -#ifdef ASM_OUTPUT_CASE_END - ASM_OUTPUT_CASE_END (file, - CODE_LABEL_NUMBER (PREV_INSN (insn)), - insn); -#endif - - text_section (); - - break; - } - - /* Do basic-block profiling when we reach a new block. - Done here to avoid jump tables. */ - if (profile_block_flag && new_block) - { - new_block = 0; - /* Enable the table of basic-block use counts - to point at the code it applies to. */ - ASM_OUTPUT_INTERNAL_LABEL (file, "LPB", count_basic_blocks); - /* Before first insn of this basic block, increment the - count of times it was entered. */ -#ifdef BLOCK_PROFILER - BLOCK_PROFILER (file, count_basic_blocks); - CC_STATUS_INIT; -#endif - count_basic_blocks++; - } - - if (GET_CODE (body) == ASM_INPUT) - { - /* There's no telling what that did to the condition codes. */ - CC_STATUS_INIT; - if (prescan > 0) - break; - if (! app_on) - { - fprintf (file, ASM_APP_ON); - app_on = 1; - } - fprintf (asm_out_file, "\t%s\n", XSTR (body, 0)); - break; - } - - /* Detect `asm' construct with operands. */ - if (asm_noperands (body) >= 0) - { - int noperands = asm_noperands (body); - rtx *ops; - char *string; - - /* There's no telling what that did to the condition codes. */ - CC_STATUS_INIT; - if (prescan > 0) - break; - - /* alloca won't do here, since only return from `final' - would free it. */ - if (noperands > 0) - ops = (rtx *) xmalloc (noperands * sizeof (rtx)); - - if (! app_on) - { - fprintf (file, ASM_APP_ON); - app_on = 1; - } - - /* Get out the operand values. */ - string = decode_asm_operands (body, ops, NULL_PTR, - NULL_PTR, NULL_PTR); - /* Inhibit aborts on what would otherwise be compiler bugs. */ - insn_noperands = noperands; - this_is_asm_operands = insn; - /* Output the insn using them. */ - output_asm_insn (string, ops); - this_is_asm_operands = 0; - if (noperands > 0) - free (ops); - break; - } - - if (prescan <= 0 && app_on) - { - fprintf (file, ASM_APP_OFF); - app_on = 0; - } - - if (GET_CODE (body) == SEQUENCE) - { - /* A delayed-branch sequence */ - register int i; - rtx next; - - if (prescan > 0) - break; - final_sequence = body; - - /* The first insn in this SEQUENCE might be a JUMP_INSN that will - force the restoration of a comparison that was previously - thought unnecessary. If that happens, cancel this sequence - and cause that insn to be restored. */ - - next = final_scan_insn (XVECEXP (body, 0, 0), file, 0, prescan, 1); - if (next != XVECEXP (body, 0, 1)) - { - final_sequence = 0; - return next; - } - - for (i = 1; i < XVECLEN (body, 0); i++) - final_scan_insn (XVECEXP (body, 0, i), file, 0, prescan, 1); -#ifdef DBR_OUTPUT_SEQEND - DBR_OUTPUT_SEQEND (file); -#endif - final_sequence = 0; - - /* If the insn requiring the delay slot was a CALL_INSN, the - insns in the delay slot are actually executed before the - called function. Hence we don't preserve any CC-setting - actions in these insns and the CC must be marked as being - clobbered by the function. */ - if (GET_CODE (XVECEXP (body, 0, 0)) == CALL_INSN) - CC_STATUS_INIT; - - /* Following a conditional branch sequence, we have a new basic - block. */ - if (profile_block_flag) - { - rtx insn = XVECEXP (body, 0, 0); - rtx body = PATTERN (insn); - - if ((GET_CODE (insn) == JUMP_INSN && GET_CODE (body) == SET - && GET_CODE (SET_SRC (body)) != LABEL_REF) - || (GET_CODE (insn) == JUMP_INSN - && GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == SET - && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) != LABEL_REF)) - new_block = 1; - } - break; - } - - /* We have a real machine instruction as rtl. */ - - body = PATTERN (insn); - -#ifdef HAVE_cc0 - /* Check for redundant test and compare instructions - (when the condition codes are already set up as desired). - This is done only when optimizing; if not optimizing, - it should be possible for the user to alter a variable - with the debugger in between statements - and the next statement should reexamine the variable - to compute the condition codes. */ - - if (optimize - && GET_CODE (body) == SET - && GET_CODE (SET_DEST (body)) == CC0 - && insn != last_ignored_compare) - { - if (GET_CODE (SET_SRC (body)) == SUBREG) - SET_SRC (body) = alter_subreg (SET_SRC (body)); - else if (GET_CODE (SET_SRC (body)) == COMPARE) - { - if (GET_CODE (XEXP (SET_SRC (body), 0)) == SUBREG) - XEXP (SET_SRC (body), 0) - = alter_subreg (XEXP (SET_SRC (body), 0)); - if (GET_CODE (XEXP (SET_SRC (body), 1)) == SUBREG) - XEXP (SET_SRC (body), 1) - = alter_subreg (XEXP (SET_SRC (body), 1)); - } - if ((cc_status.value1 != 0 - && rtx_equal_p (SET_SRC (body), cc_status.value1)) - || (cc_status.value2 != 0 - && rtx_equal_p (SET_SRC (body), cc_status.value2))) - { - /* Don't delete insn if it has an addressing side-effect. */ - if (! FIND_REG_INC_NOTE (insn, 0) - /* or if anything in it is volatile. */ - && ! volatile_refs_p (PATTERN (insn))) - { - /* We don't really delete the insn; just ignore it. */ - last_ignored_compare = insn; - break; - } - } - } -#endif - - /* Following a conditional branch, we have a new basic block. - But if we are inside a sequence, the new block starts after the - last insn of the sequence. */ - if (profile_block_flag && final_sequence == 0 - && ((GET_CODE (insn) == JUMP_INSN && GET_CODE (body) == SET - && GET_CODE (SET_SRC (body)) != LABEL_REF) - || (GET_CODE (insn) == JUMP_INSN && GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == SET - && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) != LABEL_REF))) - new_block = 1; - -#ifndef STACK_REGS - /* Don't bother outputting obvious no-ops, even without -O. - This optimization is fast and doesn't interfere with debugging. - Don't do this if the insn is in a delay slot, since this - will cause an improper number of delay insns to be written. */ - if (final_sequence == 0 - && prescan >= 0 - && GET_CODE (insn) == INSN && GET_CODE (body) == SET - && GET_CODE (SET_SRC (body)) == REG - && GET_CODE (SET_DEST (body)) == REG - && REGNO (SET_SRC (body)) == REGNO (SET_DEST (body))) - break; -#endif - -#ifdef HAVE_cc0 - /* If this is a conditional branch, maybe modify it - if the cc's are in a nonstandard state - so that it accomplishes the same thing that it would - do straightforwardly if the cc's were set up normally. */ - - if (cc_status.flags != 0 - && GET_CODE (insn) == JUMP_INSN - && GET_CODE (body) == SET - && SET_DEST (body) == pc_rtx - && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE - /* This is done during prescan; it is not done again - in final scan when prescan has been done. */ - && prescan >= 0) - { - /* This function may alter the contents of its argument - and clear some of the cc_status.flags bits. - It may also return 1 meaning condition now always true - or -1 meaning condition now always false - or 2 meaning condition nontrivial but altered. */ - register int result = alter_cond (XEXP (SET_SRC (body), 0)); - /* If condition now has fixed value, replace the IF_THEN_ELSE - with its then-operand or its else-operand. */ - if (result == 1) - SET_SRC (body) = XEXP (SET_SRC (body), 1); - if (result == -1) - SET_SRC (body) = XEXP (SET_SRC (body), 2); - - /* The jump is now either unconditional or a no-op. - If it has become a no-op, don't try to output it. - (It would not be recognized.) */ - if (SET_SRC (body) == pc_rtx) - { - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - break; - } - else if (GET_CODE (SET_SRC (body)) == RETURN) - /* Replace (set (pc) (return)) with (return). */ - PATTERN (insn) = body = SET_SRC (body); - - /* Rerecognize the instruction if it has changed. */ - if (result != 0) - INSN_CODE (insn) = -1; - } - - /* Make same adjustments to instructions that examine the - condition codes without jumping (if this machine has them). */ - - if (cc_status.flags != 0 - && GET_CODE (body) == SET) - { - switch (GET_CODE (SET_SRC (body))) - { - case GTU: - case GT: - case LTU: - case LT: - case GEU: - case GE: - case LEU: - case LE: - case EQ: - case NE: - { - register int result; - if (XEXP (SET_SRC (body), 0) != cc0_rtx) - break; - result = alter_cond (SET_SRC (body)); - if (result == 1) - validate_change (insn, &SET_SRC (body), const_true_rtx, 0); - else if (result == -1) - validate_change (insn, &SET_SRC (body), const0_rtx, 0); - else if (result == 2) - INSN_CODE (insn) = -1; - } - } - } -#endif - - /* Do machine-specific peephole optimizations if desired. */ - - if (optimize && !flag_no_peephole && !nopeepholes) - { - rtx next = peephole (insn); - /* When peepholing, if there were notes within the peephole, - emit them before the peephole. */ - if (next != 0 && next != NEXT_INSN (insn)) - { - rtx prev = PREV_INSN (insn); - rtx note; - - for (note = NEXT_INSN (insn); note != next; - note = NEXT_INSN (note)) - final_scan_insn (note, file, optimize, prescan, nopeepholes); - - /* In case this is prescan, put the notes - in proper position for later rescan. */ - note = NEXT_INSN (insn); - PREV_INSN (note) = prev; - NEXT_INSN (prev) = note; - NEXT_INSN (PREV_INSN (next)) = insn; - PREV_INSN (insn) = PREV_INSN (next); - NEXT_INSN (insn) = next; - PREV_INSN (next) = insn; - } - - /* PEEPHOLE might have changed this. */ - body = PATTERN (insn); - } - - /* Try to recognize the instruction. - If successful, verify that the operands satisfy the - constraints for the instruction. Crash if they don't, - since `reload' should have changed them so that they do. */ - - insn_code_number = recog_memoized (insn); - insn_extract (insn); - for (i = 0; i < insn_n_operands[insn_code_number]; i++) - { - if (GET_CODE (recog_operand[i]) == SUBREG) - recog_operand[i] = alter_subreg (recog_operand[i]); - } - - for (i = 0; i < insn_n_dups[insn_code_number]; i++) - { - if (GET_CODE (*recog_dup_loc[i]) == SUBREG) - *recog_dup_loc[i] = alter_subreg (*recog_dup_loc[i]); - } - -#ifdef REGISTER_CONSTRAINTS - if (! constrain_operands (insn_code_number, 1)) - fatal_insn_not_found (insn); -#endif - - /* Some target machines need to prescan each insn before - it is output. */ - -#ifdef FINAL_PRESCAN_INSN - FINAL_PRESCAN_INSN (insn, recog_operand, - insn_n_operands[insn_code_number]); -#endif - -#ifdef HAVE_cc0 - cc_prev_status = cc_status; - - /* Update `cc_status' for this instruction. - The instruction's output routine may change it further. - If the output routine for a jump insn needs to depend - on the cc status, it should look at cc_prev_status. */ - - NOTICE_UPDATE_CC (body, insn); -#endif - - debug_insn = insn; - - /* If the proper template needs to be chosen by some C code, - run that code and get the real template. */ - - template = insn_template[insn_code_number]; - if (template == 0) - { - template = (*insn_outfun[insn_code_number]) (recog_operand, insn); - - /* If the C code returns 0, it means that it is a jump insn - which follows a deleted test insn, and that test insn - needs to be reinserted. */ - if (template == 0) - { - if (prev_nonnote_insn (insn) != last_ignored_compare) - abort (); - new_block = 0; - return prev_nonnote_insn (insn); - } - } - - /* If the template is the string "#", it means that this insn must - be split. */ - if (template[0] == '#' && template[1] == '\0') - { - rtx new = try_split (body, insn, 0); - - /* If we didn't split the insn, go away. */ - if (new == insn && PATTERN (new) == body) - abort (); - - new_block = 0; - return new; - } - - if (prescan > 0) - break; - - /* Output assembler code from the template. */ - - output_asm_insn (template, recog_operand); - -#if 0 - /* It's not at all clear why we did this and doing so interferes - with tests we'd like to do to use REG_WAS_0 notes, so let's try - with this out. */ - - /* Mark this insn as having been output. */ - INSN_DELETED_P (insn) = 1; -#endif - - debug_insn = 0; - } - } - return NEXT_INSN (insn); -} - -/* Output debugging info to the assembler file FILE - based on the NOTE-insn INSN, assumed to be a line number. */ - -static void -output_source_line (file, insn) - FILE *file; - rtx insn; -{ - char ltext_label_name[100]; - register char *filename = NOTE_SOURCE_FILE (insn); - - last_linenum = NOTE_LINE_NUMBER (insn); - - if (write_symbols != NO_DEBUG) - { -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG -#if 0 /* People like having line numbers even in wrong file! */ - /* COFF can't handle multiple source files--lose, lose. */ - && !strcmp (filename, main_input_filename) -#endif - /* COFF relative line numbers must be positive. */ - && last_linenum > sdb_begin_function_line) - { -#ifdef ASM_OUTPUT_SOURCE_LINE - ASM_OUTPUT_SOURCE_LINE (file, last_linenum); -#else - fprintf (file, "\t.ln\t%d\n", - ((sdb_begin_function_line > -1) - ? last_linenum - sdb_begin_function_line : 1)); -#endif - } -#endif - -#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) - if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) - dbxout_source_line (file, filename, NOTE_LINE_NUMBER (insn)); -#endif /* DBX_DEBUGGING_INFO || XCOFF_DEBUGGING_INFO */ - -#ifdef DWARF_DEBUGGING_INFO - if (write_symbols == DWARF_DEBUG) - dwarfout_line (filename, NOTE_LINE_NUMBER (insn)); -#endif - } -} - -/* If X is a SUBREG, replace it with a REG or a MEM, - based on the thing it is a subreg of. */ - -rtx -alter_subreg (x) - register rtx x; -{ - register rtx y = SUBREG_REG (x); - if (GET_CODE (y) == SUBREG) - y = alter_subreg (y); - - if (GET_CODE (y) == REG) - { - /* If the containing reg really gets a hard reg, so do we. */ - PUT_CODE (x, REG); - REGNO (x) = REGNO (y) + SUBREG_WORD (x); - } - else if (GET_CODE (y) == MEM) - { - register int offset = SUBREG_WORD (x) * UNITS_PER_WORD; -#if BYTES_BIG_ENDIAN - offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x))) - - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (y)))); -#endif - PUT_CODE (x, MEM); - MEM_VOLATILE_P (x) = MEM_VOLATILE_P (y); - XEXP (x, 0) = plus_constant (XEXP (y, 0), offset); - } - - return x; -} - -/* Do alter_subreg on all the SUBREGs contained in X. */ - -static rtx -walk_alter_subreg (x) - rtx x; -{ - switch (GET_CODE (x)) - { - case PLUS: - case MULT: - XEXP (x, 0) = walk_alter_subreg (XEXP (x, 0)); - XEXP (x, 1) = walk_alter_subreg (XEXP (x, 1)); - break; - - case MEM: - XEXP (x, 0) = walk_alter_subreg (XEXP (x, 0)); - break; - - case SUBREG: - return alter_subreg (x); - } - - return x; -} - -#ifdef HAVE_cc0 - -/* Given BODY, the body of a jump instruction, alter the jump condition - as required by the bits that are set in cc_status.flags. - Not all of the bits there can be handled at this level in all cases. - - The value is normally 0. - 1 means that the condition has become always true. - -1 means that the condition has become always false. - 2 means that COND has been altered. */ - -static int -alter_cond (cond) - register rtx cond; -{ - int value = 0; - - if (cc_status.flags & CC_REVERSED) - { - value = 2; - PUT_CODE (cond, swap_condition (GET_CODE (cond))); - } - - if (cc_status.flags & CC_INVERTED) - { - value = 2; - PUT_CODE (cond, reverse_condition (GET_CODE (cond))); - } - - if (cc_status.flags & CC_NOT_POSITIVE) - switch (GET_CODE (cond)) - { - case LE: - case LEU: - case GEU: - /* Jump becomes unconditional. */ - return 1; - - case GT: - case GTU: - case LTU: - /* Jump becomes no-op. */ - return -1; - - case GE: - PUT_CODE (cond, EQ); - value = 2; - break; - - case LT: - PUT_CODE (cond, NE); - value = 2; - break; - } - - if (cc_status.flags & CC_NOT_NEGATIVE) - switch (GET_CODE (cond)) - { - case GE: - case GEU: - /* Jump becomes unconditional. */ - return 1; - - case LT: - case LTU: - /* Jump becomes no-op. */ - return -1; - - case LE: - case LEU: - PUT_CODE (cond, EQ); - value = 2; - break; - - case GT: - case GTU: - PUT_CODE (cond, NE); - value = 2; - break; - } - - if (cc_status.flags & CC_NO_OVERFLOW) - switch (GET_CODE (cond)) - { - case GEU: - /* Jump becomes unconditional. */ - return 1; - - case LEU: - PUT_CODE (cond, EQ); - value = 2; - break; - - case GTU: - PUT_CODE (cond, NE); - value = 2; - break; - - case LTU: - /* Jump becomes no-op. */ - return -1; - } - - if (cc_status.flags & (CC_Z_IN_NOT_N | CC_Z_IN_N)) - switch (GET_CODE (cond)) - { - case LE: - case LEU: - case GE: - case GEU: - case LT: - case LTU: - case GT: - case GTU: - abort (); - - case NE: - PUT_CODE (cond, cc_status.flags & CC_Z_IN_N ? GE : LT); - value = 2; - break; - - case EQ: - PUT_CODE (cond, cc_status.flags & CC_Z_IN_N ? LT : GE); - value = 2; - break; - } - - if (cc_status.flags & CC_NOT_SIGNED) - /* The flags are valid if signed condition operators are converted - to unsigned. */ - switch (GET_CODE (cond)) - { - case LE: - PUT_CODE (cond, LEU); - value = 2; - break; - - case LT: - PUT_CODE (cond, LTU); - value = 2; - break; - - case GT: - PUT_CODE (cond, GTU); - value = 2; - break; - - case GE: - PUT_CODE (cond, GEU); - value = 2; - break; - } - - return value; -} -#endif - -/* Report inconsistency between the assembler template and the operands. - In an `asm', it's the user's fault; otherwise, the compiler's fault. */ - -void -output_operand_lossage (str) - char *str; -{ - if (this_is_asm_operands) - error_for_asm (this_is_asm_operands, "invalid `asm': %s", str); - else - abort (); -} - -/* Output of assembler code from a template, and its subroutines. */ - -/* Output text from TEMPLATE to the assembler output file, - obeying %-directions to substitute operands taken from - the vector OPERANDS. - - %N (for N a digit) means print operand N in usual manner. - %lN means require operand N to be a CODE_LABEL or LABEL_REF - and print the label name with no punctuation. - %cN means require operand N to be a constant - and print the constant expression with no punctuation. - %aN means expect operand N to be a memory address - (not a memory reference!) and print a reference - to that address. - %nN means expect operand N to be a constant - and print a constant expression for minus the value - of the operand, with no other punctuation. */ - -void -output_asm_insn (template, operands) - char *template; - rtx *operands; -{ - register char *p; - register int c; - - /* An insn may return a null string template - in a case where no assembler code is needed. */ - if (*template == 0) - return; - - p = template; - putc ('\t', asm_out_file); - -#ifdef ASM_OUTPUT_OPCODE - ASM_OUTPUT_OPCODE (asm_out_file, p); -#endif - - while (c = *p++) - { -#ifdef ASM_OUTPUT_OPCODE - if (c == '\n') - { - putc (c, asm_out_file); - while ((c = *p) == '\t') - { - putc (c, asm_out_file); - p++; - } - ASM_OUTPUT_OPCODE (asm_out_file, p); - } - else -#endif - if (c != '%') - putc (c, asm_out_file); - else - { - /* %% outputs a single %. */ - if (*p == '%') - { - p++; - putc (c, asm_out_file); - } - /* %= outputs a number which is unique to each insn in the entire - compilation. This is useful for making local labels that are - referred to more than once in a given insn. */ - else if (*p == '=') - { - p++; - fprintf (asm_out_file, "%d", insn_counter); - } - /* % followed by a letter and some digits - outputs an operand in a special way depending on the letter. - Letters `acln' are implemented directly. - Other letters are passed to `output_operand' so that - the PRINT_OPERAND macro can define them. */ - else if ((*p >= 'a' && *p <= 'z') - || (*p >= 'A' && *p <= 'Z')) - { - int letter = *p++; - c = atoi (p); - - if (! (*p >= '0' && *p <= '9')) - output_operand_lossage ("operand number missing after %-letter"); - else if (this_is_asm_operands && c >= (unsigned) insn_noperands) - output_operand_lossage ("operand number out of range"); - else if (letter == 'l') - output_asm_label (operands[c]); - else if (letter == 'a') - output_address (operands[c]); - else if (letter == 'c') - { - if (CONSTANT_ADDRESS_P (operands[c])) - output_addr_const (asm_out_file, operands[c]); - else - output_operand (operands[c], 'c'); - } - else if (letter == 'n') - { - if (GET_CODE (operands[c]) == CONST_INT) - fprintf (asm_out_file, -#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT - "%d", -#else - "%ld", -#endif - - INTVAL (operands[c])); - else - { - putc ('-', asm_out_file); - output_addr_const (asm_out_file, operands[c]); - } - } - else - output_operand (operands[c], letter); - - while ((c = *p) >= '0' && c <= '9') p++; - } - /* % followed by a digit outputs an operand the default way. */ - else if (*p >= '0' && *p <= '9') - { - c = atoi (p); - if (this_is_asm_operands && c >= (unsigned) insn_noperands) - output_operand_lossage ("operand number out of range"); - else - output_operand (operands[c], 0); - while ((c = *p) >= '0' && c <= '9') p++; - } - /* % followed by punctuation: output something for that - punctuation character alone, with no operand. - The PRINT_OPERAND macro decides what is actually done. */ -#ifdef PRINT_OPERAND_PUNCT_VALID_P - else if (PRINT_OPERAND_PUNCT_VALID_P (*p)) - output_operand (NULL_RTX, *p++); -#endif - else - output_operand_lossage ("invalid %%-code"); - } - } - - if (flag_print_asm_name) - { - /* Annotate the assembly with a comment describing the pattern and - alternative used. */ - if (debug_insn) - { - register int num = INSN_CODE (debug_insn); - fprintf (asm_out_file, " %s %d %s", - ASM_COMMENT_START, INSN_UID (debug_insn), insn_name[num]); - if (insn_n_alternatives[num] > 1) - fprintf (asm_out_file, "/%d", which_alternative + 1); - - /* Clear this so only the first assembler insn - of any rtl insn will get the special comment for -dp. */ - debug_insn = 0; - } - } - - putc ('\n', asm_out_file); -} - -/* Output a LABEL_REF, or a bare CODE_LABEL, as an assembler symbol. */ - -void -output_asm_label (x) - rtx x; -{ - char buf[256]; - - if (GET_CODE (x) == LABEL_REF) - ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (XEXP (x, 0))); - else if (GET_CODE (x) == CODE_LABEL) - ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x)); - else - output_operand_lossage ("`%l' operand isn't a label"); - - assemble_name (asm_out_file, buf); -} - -/* Print operand X using machine-dependent assembler syntax. - The macro PRINT_OPERAND is defined just to control this function. - CODE is a non-digit that preceded the operand-number in the % spec, - such as 'z' if the spec was `%z3'. CODE is 0 if there was no char - between the % and the digits. - When CODE is a non-letter, X is 0. - - The meanings of the letters are machine-dependent and controlled - by PRINT_OPERAND. */ - -static void -output_operand (x, code) - rtx x; - int code; -{ - if (x && GET_CODE (x) == SUBREG) - x = alter_subreg (x); - - /* If X is a pseudo-register, abort now rather than writing trash to the - assembler file. */ - - if (x && GET_CODE (x) == REG && REGNO (x) >= FIRST_PSEUDO_REGISTER) - abort (); - - PRINT_OPERAND (asm_out_file, x, code); -} - -/* Print a memory reference operand for address X - using machine-dependent assembler syntax. - The macro PRINT_OPERAND_ADDRESS exists just to control this function. */ - -void -output_address (x) - rtx x; -{ - walk_alter_subreg (x); - PRINT_OPERAND_ADDRESS (asm_out_file, x); -} - -/* Print an integer constant expression in assembler syntax. - Addition and subtraction are the only arithmetic - that may appear in these expressions. */ - -void -output_addr_const (file, x) - FILE *file; - rtx x; -{ - char buf[256]; - - restart: - switch (GET_CODE (x)) - { - case PC: - if (flag_pic) - putc ('.', file); - else - abort (); - break; - - case SYMBOL_REF: - assemble_name (file, XSTR (x, 0)); - break; - - case LABEL_REF: - ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (XEXP (x, 0))); - assemble_name (file, buf); - break; - - case CODE_LABEL: - ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x)); - assemble_name (file, buf); - break; - - case CONST_INT: - fprintf (file, -#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT - "%d", -#else - "%ld", -#endif - INTVAL (x)); - break; - - case CONST: - /* This used to output parentheses around the expression, - but that does not work on the 386 (either ATT or BSD assembler). */ - output_addr_const (file, XEXP (x, 0)); - break; - - case CONST_DOUBLE: - if (GET_MODE (x) == VOIDmode) - { - /* We can use %d if the number is one word and positive. */ - if (CONST_DOUBLE_HIGH (x)) - fprintf (file, -#if HOST_BITS_PER_WIDE_INT == 64 -#if HOST_BITS_PER_WIDE_INT != HOST_BITS_PER_INT - " 0x%lx%016lx", -#else - " 0x%x%016x", -#endif -#else -#if HOST_BITS_PER_WIDE_INT != HOST_BITS_PER_INT - " 0x%lx%08lx", -#else - " 0x%x%08x", -#endif -#endif - CONST_DOUBLE_HIGH (x), CONST_DOUBLE_LOW (x)); - else if (CONST_DOUBLE_LOW (x) < 0) - fprintf (file, -#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT - "0x%x", -#else - "0x%lx", -#endif - CONST_DOUBLE_LOW (x)); - else - fprintf (file, -#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT - "%d", -#else - "%ld", -#endif - CONST_DOUBLE_LOW (x)); - } - else - /* We can't handle floating point constants; - PRINT_OPERAND must handle them. */ - output_operand_lossage ("floating constant misused"); - break; - - case PLUS: - /* Some assemblers need integer constants to appear last (eg masm). */ - if (GET_CODE (XEXP (x, 0)) == CONST_INT) - { - output_addr_const (file, XEXP (x, 1)); - if (INTVAL (XEXP (x, 0)) >= 0) - fprintf (file, "+"); - output_addr_const (file, XEXP (x, 0)); - } - else - { - output_addr_const (file, XEXP (x, 0)); - if (INTVAL (XEXP (x, 1)) >= 0) - fprintf (file, "+"); - output_addr_const (file, XEXP (x, 1)); - } - break; - - case MINUS: - /* Avoid outputting things like x-x or x+5-x, - since some assemblers can't handle that. */ - x = simplify_subtraction (x); - if (GET_CODE (x) != MINUS) - goto restart; - - output_addr_const (file, XEXP (x, 0)); - fprintf (file, "-"); - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) < 0) - { - fprintf (file, ASM_OPEN_PAREN); - output_addr_const (file, XEXP (x, 1)); - fprintf (file, ASM_CLOSE_PAREN); - } - else - output_addr_const (file, XEXP (x, 1)); - break; - - case ZERO_EXTEND: - case SIGN_EXTEND: - output_addr_const (file, XEXP (x, 0)); - break; - - default: - output_operand_lossage ("invalid expression as operand"); - } -} - -/* A poor man's fprintf, with the added features of %I, %R, %L, and %U. - %R prints the value of REGISTER_PREFIX. - %L prints the value of LOCAL_LABEL_PREFIX. - %U prints the value of USER_LABEL_PREFIX. - %I prints the value of IMMEDIATE_PREFIX. - %O runs ASM_OUTPUT_OPCODE to transform what follows in the string. - Also supported are %d, %x, %s, %e, %f, %g and %%. */ - -void -asm_fprintf (va_alist) - va_dcl -{ - va_list argptr; - FILE *file; - char buf[10]; - char *p, *q, c; - - va_start (argptr); - - file = va_arg (argptr, FILE *); - p = va_arg (argptr, char *); - buf[0] = '%'; - - while (c = *p++) - switch (c) - { - case '%': - c = *p++; - q = &buf[1]; - while ((c >= '0' && c <= '9') || c == '.') - { - *q++ = c; - c = *p++; - } - switch (c) - { - case '%': - fprintf (file, "%%"); - break; - - case 'd': case 'i': case 'u': - case 'x': case 'p': case 'X': - case 'o': - *q++ = c; - *q = 0; - fprintf (file, buf, va_arg (argptr, int)); - break; - - case 'e': - case 'f': - case 'g': - *q++ = c; - *q = 0; - fprintf (file, buf, va_arg (argptr, double)); - break; - - case 's': - *q++ = c; - *q = 0; - fprintf (file, buf, va_arg (argptr, char *)); - break; - - case 'O': -#ifdef ASM_OUTPUT_OPCODE - ASM_OUTPUT_OPCODE (asm_out_file, p); -#endif - break; - - case 'R': -#ifdef REGISTER_PREFIX - fprintf (file, "%s", REGISTER_PREFIX); -#endif - break; - - case 'I': -#ifdef IMMEDIATE_PREFIX - fprintf (file, "%s", IMMEDIATE_PREFIX); -#endif - break; - - case 'L': -#ifdef LOCAL_LABEL_PREFIX - fprintf (file, "%s", LOCAL_LABEL_PREFIX); -#endif - break; - - case 'U': -#ifdef USER_LABEL_PREFIX - fprintf (file, "%s", USER_LABEL_PREFIX); -#endif - break; - - default: - abort (); - } - break; - - default: - fputc (c, file); - } -} - -/* Split up a CONST_DOUBLE or integer constant rtx - into two rtx's for single words, - storing in *FIRST the word that comes first in memory in the target - and in *SECOND the other. */ - -void -split_double (value, first, second) - rtx value; - rtx *first, *second; -{ - if (GET_CODE (value) == CONST_INT) - { - /* The rule for using CONST_INT for a wider mode - is that we regard the value as signed. - So sign-extend it. */ - rtx high = (INTVAL (value) < 0 ? constm1_rtx : const0_rtx); -#if WORDS_BIG_ENDIAN - *first = high; - *second = value; -#else - *first = value; - *second = high; -#endif - } - else if (GET_CODE (value) != CONST_DOUBLE) - { -#if WORDS_BIG_ENDIAN - *first = const0_rtx; - *second = value; -#else - *first = value; - *second = const0_rtx; -#endif - } - else if (GET_MODE (value) == VOIDmode - /* This is the old way we did CONST_DOUBLE integers. */ - || GET_MODE_CLASS (GET_MODE (value)) == MODE_INT) - { - /* In an integer, the words are defined as most and least significant. - So order them by the target's convention. */ -#if WORDS_BIG_ENDIAN - *first = GEN_INT (CONST_DOUBLE_HIGH (value)); - *second = GEN_INT (CONST_DOUBLE_LOW (value)); -#else - *first = GEN_INT (CONST_DOUBLE_LOW (value)); - *second = GEN_INT (CONST_DOUBLE_HIGH (value)); -#endif - } - else - { -#ifdef REAL_ARITHMETIC - REAL_VALUE_TYPE r; HOST_WIDE_INT l[2]; - REAL_VALUE_FROM_CONST_DOUBLE (r, value); - REAL_VALUE_TO_TARGET_DOUBLE (r, l); - *first = GEN_INT (l[0]); - *second = GEN_INT (l[1]); -#else - if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT - || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD) - && ! flag_pretend_float) - abort (); - -#if defined (HOST_WORDS_BIG_ENDIAN) == WORDS_BIG_ENDIAN - /* Host and target agree => no need to swap. */ - *first = GEN_INT (CONST_DOUBLE_LOW (value)); - *second = GEN_INT (CONST_DOUBLE_HIGH (value)); -#else - *second = GEN_INT (CONST_DOUBLE_LOW (value)); - *first = GEN_INT (CONST_DOUBLE_HIGH (value)); -#endif -#endif /* no REAL_ARITHMETIC */ - } -} - -/* Return nonzero if this function has no function calls. */ - -int -leaf_function_p () -{ - rtx insn; - - if (profile_flag || profile_block_flag) - return 0; - - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == CALL_INSN) - return 0; - if (GET_CODE (insn) == INSN - && GET_CODE (PATTERN (insn)) == SEQUENCE - && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == CALL_INSN) - return 0; - } - for (insn = current_function_epilogue_delay_list; insn; insn = XEXP (insn, 1)) - { - if (GET_CODE (XEXP (insn, 0)) == CALL_INSN) - return 0; - if (GET_CODE (XEXP (insn, 0)) == INSN - && GET_CODE (PATTERN (XEXP (insn, 0))) == SEQUENCE - && GET_CODE (XVECEXP (PATTERN (XEXP (insn, 0)), 0, 0)) == CALL_INSN) - return 0; - } - - return 1; -} - -/* On some machines, a function with no call insns - can run faster if it doesn't create its own register window. - When output, the leaf function should use only the "output" - registers. Ordinarily, the function would be compiled to use - the "input" registers to find its arguments; it is a candidate - for leaf treatment if it uses only the "input" registers. - Leaf function treatment means renumbering so the function - uses the "output" registers instead. */ - -#ifdef LEAF_REGISTERS - -static char permitted_reg_in_leaf_functions[] = LEAF_REGISTERS; - -/* Return 1 if this function uses only the registers that can be - safely renumbered. */ - -int -only_leaf_regs_used () -{ - int i; - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - if ((regs_ever_live[i] || global_regs[i]) - && ! permitted_reg_in_leaf_functions[i]) - return 0; - } - return 1; -} - -/* Scan all instructions and renumber all registers into those - available in leaf functions. */ - -static void -leaf_renumber_regs (first) - rtx first; -{ - rtx insn; - - /* Renumber only the actual patterns. - The reg-notes can contain frame pointer refs, - and renumbering them could crash, and should not be needed. */ - for (insn = first; insn; insn = NEXT_INSN (insn)) - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') - leaf_renumber_regs_insn (PATTERN (insn)); - for (insn = current_function_epilogue_delay_list; insn; insn = XEXP (insn, 1)) - if (GET_RTX_CLASS (GET_CODE (XEXP (insn, 0))) == 'i') - leaf_renumber_regs_insn (PATTERN (XEXP (insn, 0))); -} - -/* Scan IN_RTX and its subexpressions, and renumber all regs into those - available in leaf functions. */ - -void -leaf_renumber_regs_insn (in_rtx) - register rtx in_rtx; -{ - register int i, j; - register char *format_ptr; - - if (in_rtx == 0) - return; - - /* Renumber all input-registers into output-registers. - renumbered_regs would be 1 for an output-register; - they */ - - if (GET_CODE (in_rtx) == REG) - { - int newreg; - - /* Don't renumber the same reg twice. */ - if (in_rtx->used) - return; - - newreg = REGNO (in_rtx); - /* Don't try to renumber pseudo regs. It is possible for a pseudo reg - to reach here as part of a REG_NOTE. */ - if (newreg >= FIRST_PSEUDO_REGISTER) - { - in_rtx->used = 1; - return; - } - newreg = LEAF_REG_REMAP (newreg); - if (newreg < 0) - abort (); - regs_ever_live[REGNO (in_rtx)] = 0; - regs_ever_live[newreg] = 1; - REGNO (in_rtx) = newreg; - in_rtx->used = 1; - } - - if (GET_RTX_CLASS (GET_CODE (in_rtx)) == 'i') - { - /* Inside a SEQUENCE, we find insns. - Renumber just the patterns of these insns, - just as we do for the top-level insns. */ - leaf_renumber_regs_insn (PATTERN (in_rtx)); - return; - } - - format_ptr = GET_RTX_FORMAT (GET_CODE (in_rtx)); - - for (i = 0; i < GET_RTX_LENGTH (GET_CODE (in_rtx)); i++) - switch (*format_ptr++) - { - case 'e': - leaf_renumber_regs_insn (XEXP (in_rtx, i)); - break; - - case 'E': - if (NULL != XVEC (in_rtx, i)) - { - for (j = 0; j < XVECLEN (in_rtx, i); j++) - leaf_renumber_regs_insn (XVECEXP (in_rtx, i, j)); - } - break; - - case 'S': - case 's': - case '0': - case 'i': - case 'w': - case 'n': - case 'u': - break; - - default: - abort (); - } -} -#endif diff --git a/gnu/usr.bin/cc/common/function.c b/gnu/usr.bin/cc/common/function.c deleted file mode 100644 index d411abcbf9..0000000000 --- a/gnu/usr.bin/cc/common/function.c +++ /dev/null @@ -1,4913 +0,0 @@ -/* Expands front end tree to back end RTL for GNU C-Compiler - Copyright (C) 1987, 88, 89, 91, 92, 1993 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This file handles the generation of rtl code from tree structure - at the level of the function as a whole. - It creates the rtl expressions for parameters and auto variables - and has full responsibility for allocating stack slots. - - `expand_function_start' is called at the beginning of a function, - before the function body is parsed, and `expand_function_end' is - called after parsing the body. - - Call `assign_stack_local' to allocate a stack slot for a local variable. - This is usually done during the RTL generation for the function body, - but it can also be done in the reload pass when a pseudo-register does - not get a hard register. - - Call `put_var_into_stack' when you learn, belatedly, that a variable - previously given a pseudo-register must in fact go in the stack. - This function changes the DECL_RTL to be a stack slot instead of a reg - then scans all the RTL instructions so far generated to correct them. */ - -#include "config.h" - -#include - -#include "rtl.h" -#include "tree.h" -#include "flags.h" -#include "function.h" -#include "insn-flags.h" -#include "expr.h" -#include "insn-codes.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "insn-config.h" -#include "recog.h" -#include "output.h" -#include "basic-block.h" - -/* Round a value to the lowest integer less than it that is a multiple of - the required alignment. Avoid using division in case the value is - negative. Assume the alignment is a power of two. */ -#define FLOOR_ROUND(VALUE,ALIGN) ((VALUE) & ~((ALIGN) - 1)) - -/* Similar, but round to the next highest integer that meets the - alignment. */ -#define CEIL_ROUND(VALUE,ALIGN) (((VALUE) + (ALIGN) - 1) & ~((ALIGN)- 1)) - -/* NEED_SEPARATE_AP means that we cannot derive ap from the value of fp - during rtl generation. If they are different register numbers, this is - always true. It may also be true if - FIRST_PARM_OFFSET - STARTING_FRAME_OFFSET is not a constant during rtl - generation. See fix_lexical_addr for details. */ - -#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM -#define NEED_SEPARATE_AP -#endif - -/* Number of bytes of args popped by function being compiled on its return. - Zero if no bytes are to be popped. - May affect compilation of return insn or of function epilogue. */ - -int current_function_pops_args; - -/* Nonzero if function being compiled needs to be given an address - where the value should be stored. */ - -int current_function_returns_struct; - -/* Nonzero if function being compiled needs to - return the address of where it has put a structure value. */ - -int current_function_returns_pcc_struct; - -/* Nonzero if function being compiled needs to be passed a static chain. */ - -int current_function_needs_context; - -/* Nonzero if function being compiled can call setjmp. */ - -int current_function_calls_setjmp; - -/* Nonzero if function being compiled can call longjmp. */ - -int current_function_calls_longjmp; - -/* Nonzero if function being compiled receives nonlocal gotos - from nested functions. */ - -int current_function_has_nonlocal_label; - -/* Nonzero if function being compiled contains nested functions. */ - -int current_function_contains_functions; - -/* Nonzero if function being compiled can call alloca, - either as a subroutine or builtin. */ - -int current_function_calls_alloca; - -/* Nonzero if the current function returns a pointer type */ - -int current_function_returns_pointer; - -/* If some insns can be deferred to the delay slots of the epilogue, the - delay list for them is recorded here. */ - -rtx current_function_epilogue_delay_list; - -/* If function's args have a fixed size, this is that size, in bytes. - Otherwise, it is -1. - May affect compilation of return insn or of function epilogue. */ - -int current_function_args_size; - -/* # bytes the prologue should push and pretend that the caller pushed them. - The prologue must do this, but only if parms can be passed in registers. */ - -int current_function_pretend_args_size; - -/* # of bytes of outgoing arguments required to be pushed by the prologue. - If this is non-zero, it means that ACCUMULATE_OUTGOING_ARGS was defined - and no stack adjusts will be done on function calls. */ - -int current_function_outgoing_args_size; - -/* This is the offset from the arg pointer to the place where the first - anonymous arg can be found, if there is one. */ - -rtx current_function_arg_offset_rtx; - -/* Nonzero if current function uses varargs.h or equivalent. - Zero for functions that use stdarg.h. */ - -int current_function_varargs; - -/* Quantities of various kinds of registers - used for the current function's args. */ - -CUMULATIVE_ARGS current_function_args_info; - -/* Name of function now being compiled. */ - -char *current_function_name; - -/* If non-zero, an RTL expression for that location at which the current - function returns its result. Always equal to - DECL_RTL (DECL_RESULT (current_function_decl)), but provided - independently of the tree structures. */ - -rtx current_function_return_rtx; - -/* Nonzero if the current function uses the constant pool. */ - -int current_function_uses_const_pool; - -/* Nonzero if the current function uses pic_offset_table_rtx. */ -int current_function_uses_pic_offset_table; - -/* The arg pointer hard register, or the pseudo into which it was copied. */ -rtx current_function_internal_arg_pointer; - -/* The FUNCTION_DECL for an inline function currently being expanded. */ -tree inline_function_decl; - -/* Number of function calls seen so far in current function. */ - -int function_call_count; - -/* List (chain of TREE_LIST) of LABEL_DECLs for all nonlocal labels - (labels to which there can be nonlocal gotos from nested functions) - in this function. */ - -tree nonlocal_labels; - -/* RTX for stack slot that holds the current handler for nonlocal gotos. - Zero when function does not have nonlocal labels. */ - -rtx nonlocal_goto_handler_slot; - -/* RTX for stack slot that holds the stack pointer value to restore - for a nonlocal goto. - Zero when function does not have nonlocal labels. */ - -rtx nonlocal_goto_stack_level; - -/* Label that will go on parm cleanup code, if any. - Jumping to this label runs cleanup code for parameters, if - such code must be run. Following this code is the logical return label. */ - -rtx cleanup_label; - -/* Label that will go on function epilogue. - Jumping to this label serves as a "return" instruction - on machines which require execution of the epilogue on all returns. */ - -rtx return_label; - -/* List (chain of EXPR_LISTs) of pseudo-regs of SAVE_EXPRs. - So we can mark them all live at the end of the function, if nonopt. */ -rtx save_expr_regs; - -/* List (chain of EXPR_LISTs) of all stack slots in this function. - Made for the sake of unshare_all_rtl. */ -rtx stack_slot_list; - -/* Chain of all RTL_EXPRs that have insns in them. */ -tree rtl_expr_chain; - -/* Label to jump back to for tail recursion, or 0 if we have - not yet needed one for this function. */ -rtx tail_recursion_label; - -/* Place after which to insert the tail_recursion_label if we need one. */ -rtx tail_recursion_reentry; - -/* Location at which to save the argument pointer if it will need to be - referenced. There are two cases where this is done: if nonlocal gotos - exist, or if vars stored at an offset from the argument pointer will be - needed by inner routines. */ - -rtx arg_pointer_save_area; - -/* Offset to end of allocated area of stack frame. - If stack grows down, this is the address of the last stack slot allocated. - If stack grows up, this is the address for the next slot. */ -int frame_offset; - -/* List (chain of TREE_LISTs) of static chains for containing functions. - Each link has a FUNCTION_DECL in the TREE_PURPOSE and a reg rtx - in an RTL_EXPR in the TREE_VALUE. */ -static tree context_display; - -/* List (chain of TREE_LISTs) of trampolines for nested functions. - The trampoline sets up the static chain and jumps to the function. - We supply the trampoline's address when the function's address is requested. - - Each link has a FUNCTION_DECL in the TREE_PURPOSE and a reg rtx - in an RTL_EXPR in the TREE_VALUE. */ -static tree trampoline_list; - -/* Insn after which register parms and SAVE_EXPRs are born, if nonopt. */ -static rtx parm_birth_insn; - -#if 0 -/* Nonzero if a stack slot has been generated whose address is not - actually valid. It means that the generated rtl must all be scanned - to detect and correct the invalid addresses where they occur. */ -static int invalid_stack_slot; -#endif - -/* Last insn of those whose job was to put parms into their nominal homes. */ -static rtx last_parm_insn; - -/* 1 + last pseudo register number used for loading a copy - of a parameter of this function. */ -static int max_parm_reg; - -/* Vector indexed by REGNO, containing location on stack in which - to put the parm which is nominally in pseudo register REGNO, - if we discover that that parm must go in the stack. */ -static rtx *parm_reg_stack_loc; - -#if 0 /* Turned off because 0 seems to work just as well. */ -/* Cleanup lists are required for binding levels regardless of whether - that binding level has cleanups or not. This node serves as the - cleanup list whenever an empty list is required. */ -static tree empty_cleanup_list; -#endif - -/* Nonzero once virtual register instantiation has been done. - assign_stack_local uses frame_pointer_rtx when this is nonzero. */ -static int virtuals_instantiated; - -/* Nonzero if we need to distinguish between the return value of this function - and the return value of a function called by this function. This helps - integrate.c */ - -extern int rtx_equal_function_value_matters; - -void fixup_gotos (); - -static tree round_down (); -static rtx round_trampoline_addr (); -static rtx fixup_stack_1 (); -static void fixup_var_refs (); -static void fixup_var_refs_insns (); -static void fixup_var_refs_1 (); -static void optimize_bit_field (); -static void instantiate_decls (); -static void instantiate_decls_1 (); -static void instantiate_decl (); -static int instantiate_virtual_regs_1 (); -static rtx fixup_memory_subreg (); -static rtx walk_fixup_memory_subreg (); - -/* In order to evaluate some expressions, such as function calls returning - structures in memory, we need to temporarily allocate stack locations. - We record each allocated temporary in the following structure. - - Associated with each temporary slot is a nesting level. When we pop up - one level, all temporaries associated with the previous level are freed. - Normally, all temporaries are freed after the execution of the statement - in which they were created. However, if we are inside a ({...}) grouping, - the result may be in a temporary and hence must be preserved. If the - result could be in a temporary, we preserve it if we can determine which - one it is in. If we cannot determine which temporary may contain the - result, all temporaries are preserved. A temporary is preserved by - pretending it was allocated at the previous nesting level. - - Automatic variables are also assigned temporary slots, at the nesting - level where they are defined. They are marked a "kept" so that - free_temp_slots will not free them. */ - -struct temp_slot -{ - /* Points to next temporary slot. */ - struct temp_slot *next; - /* The rtx to used to reference the slot. */ - rtx slot; - /* The size, in units, of the slot. */ - int size; - /* Non-zero if this temporary is currently in use. */ - char in_use; - /* Nesting level at which this slot is being used. */ - int level; - /* Non-zero if this should survive a call to free_temp_slots. */ - int keep; -}; - -/* List of all temporaries allocated, both available and in use. */ - -struct temp_slot *temp_slots; - -/* Current nesting level for temporaries. */ - -int temp_slot_level; - -/* Pointer to chain of `struct function' for containing functions. */ -struct function *outer_function_chain; - -/* Given a function decl for a containing function, - return the `struct function' for it. */ - -struct function * -find_function_data (decl) - tree decl; -{ - struct function *p; - for (p = outer_function_chain; p; p = p->next) - if (p->decl == decl) - return p; - abort (); -} - -/* Save the current context for compilation of a nested function. - This is called from language-specific code. - The caller is responsible for saving any language-specific status, - since this function knows only about language-independent variables. */ - -void -push_function_context () -{ - struct function *p = (struct function *) xmalloc (sizeof (struct function)); - - p->next = outer_function_chain; - outer_function_chain = p; - - p->name = current_function_name; - p->decl = current_function_decl; - p->pops_args = current_function_pops_args; - p->returns_struct = current_function_returns_struct; - p->returns_pcc_struct = current_function_returns_pcc_struct; - p->needs_context = current_function_needs_context; - p->calls_setjmp = current_function_calls_setjmp; - p->calls_longjmp = current_function_calls_longjmp; - p->calls_alloca = current_function_calls_alloca; - p->has_nonlocal_label = current_function_has_nonlocal_label; - p->args_size = current_function_args_size; - p->pretend_args_size = current_function_pretend_args_size; - p->arg_offset_rtx = current_function_arg_offset_rtx; - p->uses_const_pool = current_function_uses_const_pool; - p->uses_pic_offset_table = current_function_uses_pic_offset_table; - p->internal_arg_pointer = current_function_internal_arg_pointer; - p->max_parm_reg = max_parm_reg; - p->parm_reg_stack_loc = parm_reg_stack_loc; - p->outgoing_args_size = current_function_outgoing_args_size; - p->return_rtx = current_function_return_rtx; - p->nonlocal_goto_handler_slot = nonlocal_goto_handler_slot; - p->nonlocal_goto_stack_level = nonlocal_goto_stack_level; - p->nonlocal_labels = nonlocal_labels; - p->cleanup_label = cleanup_label; - p->return_label = return_label; - p->save_expr_regs = save_expr_regs; - p->stack_slot_list = stack_slot_list; - p->parm_birth_insn = parm_birth_insn; - p->frame_offset = frame_offset; - p->tail_recursion_label = tail_recursion_label; - p->tail_recursion_reentry = tail_recursion_reentry; - p->arg_pointer_save_area = arg_pointer_save_area; - p->rtl_expr_chain = rtl_expr_chain; - p->last_parm_insn = last_parm_insn; - p->context_display = context_display; - p->trampoline_list = trampoline_list; - p->function_call_count = function_call_count; - p->temp_slots = temp_slots; - p->temp_slot_level = temp_slot_level; - p->fixup_var_refs_queue = 0; - p->epilogue_delay_list = current_function_epilogue_delay_list; - - save_tree_status (p); - save_storage_status (p); - save_emit_status (p); - init_emit (); - save_expr_status (p); - save_stmt_status (p); - save_varasm_status (p); -} - -/* Restore the last saved context, at the end of a nested function. - This function is called from language-specific code. */ - -void -pop_function_context () -{ - struct function *p = outer_function_chain; - - outer_function_chain = p->next; - - current_function_name = p->name; - current_function_decl = p->decl; - current_function_pops_args = p->pops_args; - current_function_returns_struct = p->returns_struct; - current_function_returns_pcc_struct = p->returns_pcc_struct; - current_function_needs_context = p->needs_context; - current_function_calls_setjmp = p->calls_setjmp; - current_function_calls_longjmp = p->calls_longjmp; - current_function_calls_alloca = p->calls_alloca; - current_function_has_nonlocal_label = p->has_nonlocal_label; - current_function_contains_functions = 1; - current_function_args_size = p->args_size; - current_function_pretend_args_size = p->pretend_args_size; - current_function_arg_offset_rtx = p->arg_offset_rtx; - current_function_uses_const_pool = p->uses_const_pool; - current_function_uses_pic_offset_table = p->uses_pic_offset_table; - current_function_internal_arg_pointer = p->internal_arg_pointer; - max_parm_reg = p->max_parm_reg; - parm_reg_stack_loc = p->parm_reg_stack_loc; - current_function_outgoing_args_size = p->outgoing_args_size; - current_function_return_rtx = p->return_rtx; - nonlocal_goto_handler_slot = p->nonlocal_goto_handler_slot; - nonlocal_goto_stack_level = p->nonlocal_goto_stack_level; - nonlocal_labels = p->nonlocal_labels; - cleanup_label = p->cleanup_label; - return_label = p->return_label; - save_expr_regs = p->save_expr_regs; - stack_slot_list = p->stack_slot_list; - parm_birth_insn = p->parm_birth_insn; - frame_offset = p->frame_offset; - tail_recursion_label = p->tail_recursion_label; - tail_recursion_reentry = p->tail_recursion_reentry; - arg_pointer_save_area = p->arg_pointer_save_area; - rtl_expr_chain = p->rtl_expr_chain; - last_parm_insn = p->last_parm_insn; - context_display = p->context_display; - trampoline_list = p->trampoline_list; - function_call_count = p->function_call_count; - temp_slots = p->temp_slots; - temp_slot_level = p->temp_slot_level; - current_function_epilogue_delay_list = p->epilogue_delay_list; - - restore_tree_status (p); - restore_storage_status (p); - restore_expr_status (p); - restore_emit_status (p); - restore_stmt_status (p); - restore_varasm_status (p); - - /* Finish doing put_var_into_stack for any of our variables - which became addressable during the nested function. */ - { - struct var_refs_queue *queue = p->fixup_var_refs_queue; - for (; queue; queue = queue->next) - fixup_var_refs (queue->modified, queue->promoted_mode, queue->unsignedp); - } - - free (p); - - /* Reset variables that have known state during rtx generation. */ - rtx_equal_function_value_matters = 1; - virtuals_instantiated = 0; -} - -/* Allocate fixed slots in the stack frame of the current function. */ - -/* Return size needed for stack frame based on slots so far allocated. - This size counts from zero. It is not rounded to STACK_BOUNDARY; - the caller may have to do that. */ - -int -get_frame_size () -{ -#ifdef FRAME_GROWS_DOWNWARD - return -frame_offset; -#else - return frame_offset; -#endif -} - -/* Allocate a stack slot of SIZE bytes and return a MEM rtx for it - with machine mode MODE. - - ALIGN controls the amount of alignment for the address of the slot: - 0 means according to MODE, - -1 means use BIGGEST_ALIGNMENT and round size to multiple of that, - positive specifies alignment boundary in bits. - - We do not round to stack_boundary here. */ - -rtx -assign_stack_local (mode, size, align) - enum machine_mode mode; - int size; - int align; -{ - register rtx x, addr; - int bigend_correction = 0; - int alignment; - - if (align == 0) - { - alignment = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; - if (mode == BLKmode) - alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT; - } - else if (align == -1) - { - alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT; - size = CEIL_ROUND (size, alignment); - } - else - alignment = align / BITS_PER_UNIT; - - /* Round frame offset to that alignment. - We must be careful here, since FRAME_OFFSET might be negative and - division with a negative dividend isn't as well defined as we might - like. So we instead assume that ALIGNMENT is a power of two and - use logical operations which are unambiguous. */ -#ifdef FRAME_GROWS_DOWNWARD - frame_offset = FLOOR_ROUND (frame_offset, alignment); -#else - frame_offset = CEIL_ROUND (frame_offset, alignment); -#endif - - /* On a big-endian machine, if we are allocating more space than we will use, - use the least significant bytes of those that are allocated. */ -#if BYTES_BIG_ENDIAN - if (mode != BLKmode) - bigend_correction = size - GET_MODE_SIZE (mode); -#endif - -#ifdef FRAME_GROWS_DOWNWARD - frame_offset -= size; -#endif - - /* If we have already instantiated virtual registers, return the actual - address relative to the frame pointer. */ - if (virtuals_instantiated) - addr = plus_constant (frame_pointer_rtx, - (frame_offset + bigend_correction - + STARTING_FRAME_OFFSET)); - else - addr = plus_constant (virtual_stack_vars_rtx, - frame_offset + bigend_correction); - -#ifndef FRAME_GROWS_DOWNWARD - frame_offset += size; -#endif - - x = gen_rtx (MEM, mode, addr); - - stack_slot_list = gen_rtx (EXPR_LIST, VOIDmode, x, stack_slot_list); - - return x; -} - -/* Assign a stack slot in a containing function. - First three arguments are same as in preceding function. - The last argument specifies the function to allocate in. */ - -rtx -assign_outer_stack_local (mode, size, align, function) - enum machine_mode mode; - int size; - int align; - struct function *function; -{ - register rtx x, addr; - int bigend_correction = 0; - int alignment; - - /* Allocate in the memory associated with the function in whose frame - we are assigning. */ - push_obstacks (function->function_obstack, - function->function_maybepermanent_obstack); - - if (align == 0) - { - alignment = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; - if (mode == BLKmode) - alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT; - } - else if (align == -1) - { - alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT; - size = CEIL_ROUND (size, alignment); - } - else - alignment = align / BITS_PER_UNIT; - - /* Round frame offset to that alignment. */ -#ifdef FRAME_GROWS_DOWNWARD - function->frame_offset = FLOOR_ROUND (function->frame_offset, alignment); -#else - function->frame_offset = CEIL_ROUND (function->frame_offset, alignment); -#endif - - /* On a big-endian machine, if we are allocating more space than we will use, - use the least significant bytes of those that are allocated. */ -#if BYTES_BIG_ENDIAN - if (mode != BLKmode) - bigend_correction = size - GET_MODE_SIZE (mode); -#endif - -#ifdef FRAME_GROWS_DOWNWARD - function->frame_offset -= size; -#endif - addr = plus_constant (virtual_stack_vars_rtx, - function->frame_offset + bigend_correction); -#ifndef FRAME_GROWS_DOWNWARD - function->frame_offset += size; -#endif - - x = gen_rtx (MEM, mode, addr); - - function->stack_slot_list - = gen_rtx (EXPR_LIST, VOIDmode, x, function->stack_slot_list); - - pop_obstacks (); - - return x; -} - -/* Allocate a temporary stack slot and record it for possible later - reuse. - - MODE is the machine mode to be given to the returned rtx. - - SIZE is the size in units of the space required. We do no rounding here - since assign_stack_local will do any required rounding. - - KEEP is non-zero if this slot is to be retained after a call to - free_temp_slots. Automatic variables for a block are allocated with this - flag. */ - -rtx -assign_stack_temp (mode, size, keep) - enum machine_mode mode; - int size; - int keep; -{ - struct temp_slot *p, *best_p = 0; - - /* First try to find an available, already-allocated temporary that is the - exact size we require. */ - for (p = temp_slots; p; p = p->next) - if (p->size == size && GET_MODE (p->slot) == mode && ! p->in_use) - break; - - /* If we didn't find, one, try one that is larger than what we want. We - find the smallest such. */ - if (p == 0) - for (p = temp_slots; p; p = p->next) - if (p->size > size && GET_MODE (p->slot) == mode && ! p->in_use - && (best_p == 0 || best_p->size > p->size)) - best_p = p; - - /* Make our best, if any, the one to use. */ - if (best_p) - p = best_p; - - /* If we still didn't find one, make a new temporary. */ - if (p == 0) - { - p = (struct temp_slot *) oballoc (sizeof (struct temp_slot)); - p->size = size; - /* If the temp slot mode doesn't indicate the alignment, - use the largest possible, so no one will be disappointed. */ - p->slot = assign_stack_local (mode, size, mode == BLKmode ? -1 : 0); - p->next = temp_slots; - temp_slots = p; - } - - p->in_use = 1; - p->level = temp_slot_level; - p->keep = keep; - return p->slot; -} - -/* If X could be a reference to a temporary slot, mark that slot as belonging - to the to one level higher. If X matched one of our slots, just mark that - one. Otherwise, we can't easily predict which it is, so upgrade all of - them. Kept slots need not be touched. - - This is called when an ({...}) construct occurs and a statement - returns a value in memory. */ - -void -preserve_temp_slots (x) - rtx x; -{ - struct temp_slot *p; - - /* If X is not in memory or is at a constant address, it cannot be in - a temporary slot. */ - if (x == 0 || GET_CODE (x) != MEM || CONSTANT_P (XEXP (x, 0))) - return; - - /* First see if we can find a match. */ - for (p = temp_slots; p; p = p->next) - if (p->in_use && x == p->slot) - { - p->level--; - return; - } - - /* Otherwise, preserve all non-kept slots at this level. */ - for (p = temp_slots; p; p = p->next) - if (p->in_use && p->level == temp_slot_level && ! p->keep) - p->level--; -} - -/* Free all temporaries used so far. This is normally called at the end - of generating code for a statement. */ - -void -free_temp_slots () -{ - struct temp_slot *p; - - for (p = temp_slots; p; p = p->next) - if (p->in_use && p->level == temp_slot_level && ! p->keep) - p->in_use = 0; -} - -/* Push deeper into the nesting level for stack temporaries. */ - -void -push_temp_slots () -{ - /* For GNU C++, we must allow a sequence to be emitted anywhere in - the level where the sequence was started. By not changing levels - when the compiler is inside a sequence, the temporaries for the - sequence and the temporaries will not unwittingly conflict with - the temporaries for other sequences and/or code at that level. */ - if (in_sequence_p ()) - return; - - temp_slot_level++; -} - -/* Pop a temporary nesting level. All slots in use in the current level - are freed. */ - -void -pop_temp_slots () -{ - struct temp_slot *p; - - /* See comment in push_temp_slots about why we don't change levels - in sequences. */ - if (in_sequence_p ()) - return; - - for (p = temp_slots; p; p = p->next) - if (p->in_use && p->level == temp_slot_level) - p->in_use = 0; - - temp_slot_level--; -} - -/* Retroactively move an auto variable from a register to a stack slot. - This is done when an address-reference to the variable is seen. */ - -void -put_var_into_stack (decl) - tree decl; -{ - register rtx reg; - register rtx new = 0; - enum machine_mode promoted_mode, decl_mode; - struct function *function = 0; - tree context = decl_function_context (decl); - - /* Get the current rtl used for this object and it's original mode. */ - reg = TREE_CODE (decl) == SAVE_EXPR ? SAVE_EXPR_RTL (decl) : DECL_RTL (decl); - - /* No need to do anything if decl has no rtx yet - since in that case caller is setting TREE_ADDRESSABLE - and a stack slot will be assigned when the rtl is made. */ - if (reg == 0) - return; - - /* Get the declared mode for this object. */ - decl_mode = (TREE_CODE (decl) == SAVE_EXPR ? TYPE_MODE (TREE_TYPE (decl)) - : DECL_MODE (decl)); - /* Get the mode it's actually stored in. */ - promoted_mode = GET_MODE (reg); - - /* If this variable comes from an outer function, - find that function's saved context. */ - if (context != current_function_decl) - for (function = outer_function_chain; function; function = function->next) - if (function->decl == context) - break; - - /* If this is a variable-size object with a pseudo to address it, - put that pseudo into the stack, if the var is nonlocal. */ - if (DECL_NONLOCAL (decl) - && GET_CODE (reg) == MEM - && GET_CODE (XEXP (reg, 0)) == REG - && REGNO (XEXP (reg, 0)) > LAST_VIRTUAL_REGISTER) - { - reg = XEXP (reg, 0); - decl_mode = promoted_mode = GET_MODE (reg); - } - - if (GET_CODE (reg) != REG) - return; - - if (function) - { - if (REGNO (reg) < function->max_parm_reg) - new = function->parm_reg_stack_loc[REGNO (reg)]; - if (new == 0) - new = assign_outer_stack_local (decl_mode, GET_MODE_SIZE (decl_mode), - 0, function); - } - else - { - if (REGNO (reg) < max_parm_reg) - new = parm_reg_stack_loc[REGNO (reg)]; - if (new == 0) - new = assign_stack_local (decl_mode, GET_MODE_SIZE (decl_mode), 0); - } - - XEXP (reg, 0) = XEXP (new, 0); - /* `volatil' bit means one thing for MEMs, another entirely for REGs. */ - REG_USERVAR_P (reg) = 0; - PUT_CODE (reg, MEM); - PUT_MODE (reg, decl_mode); - - /* If this is a memory ref that contains aggregate components, - mark it as such for cse and loop optimize. */ - MEM_IN_STRUCT_P (reg) - = (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE - || TREE_CODE (TREE_TYPE (decl)) == QUAL_UNION_TYPE); - - /* Now make sure that all refs to the variable, previously made - when it was a register, are fixed up to be valid again. */ - if (function) - { - struct var_refs_queue *temp; - - /* Variable is inherited; fix it up when we get back to its function. */ - push_obstacks (function->function_obstack, - function->function_maybepermanent_obstack); - temp - = (struct var_refs_queue *) oballoc (sizeof (struct var_refs_queue)); - temp->modified = reg; - temp->promoted_mode = promoted_mode; - temp->unsignedp = TREE_UNSIGNED (TREE_TYPE (decl)); - temp->next = function->fixup_var_refs_queue; - function->fixup_var_refs_queue = temp; - pop_obstacks (); - } - else - /* Variable is local; fix it up now. */ - fixup_var_refs (reg, promoted_mode, TREE_UNSIGNED (TREE_TYPE (decl))); -} - -static void -fixup_var_refs (var, promoted_mode, unsignedp) - rtx var; - enum machine_mode promoted_mode; - int unsignedp; -{ - tree pending; - rtx first_insn = get_insns (); - struct sequence_stack *stack = sequence_stack; - tree rtl_exps = rtl_expr_chain; - - /* Must scan all insns for stack-refs that exceed the limit. */ - fixup_var_refs_insns (var, promoted_mode, unsignedp, first_insn, stack == 0); - - /* Scan all pending sequences too. */ - for (; stack; stack = stack->next) - { - push_to_sequence (stack->first); - fixup_var_refs_insns (var, promoted_mode, unsignedp, - stack->first, stack->next != 0); - /* Update remembered end of sequence - in case we added an insn at the end. */ - stack->last = get_last_insn (); - end_sequence (); - } - - /* Scan all waiting RTL_EXPRs too. */ - for (pending = rtl_exps; pending; pending = TREE_CHAIN (pending)) - { - rtx seq = RTL_EXPR_SEQUENCE (TREE_VALUE (pending)); - if (seq != const0_rtx && seq != 0) - { - push_to_sequence (seq); - fixup_var_refs_insns (var, promoted_mode, unsignedp, seq, 0); - end_sequence (); - } - } -} - -/* This structure is used by the following two functions to record MEMs or - pseudos used to replace VAR, any SUBREGs of VAR, and any MEMs containing - VAR as an address. We need to maintain this list in case two operands of - an insn were required to match; in that case we must ensure we use the - same replacement. */ - -struct fixup_replacement -{ - rtx old; - rtx new; - struct fixup_replacement *next; -}; - -/* REPLACEMENTS is a pointer to a list of the above structures and X is - some part of an insn. Return a struct fixup_replacement whose OLD - value is equal to X. Allocate a new structure if no such entry exists. */ - -static struct fixup_replacement * -find_fixup_replacement (replacements, x) - struct fixup_replacement **replacements; - rtx x; -{ - struct fixup_replacement *p; - - /* See if we have already replaced this. */ - for (p = *replacements; p && p->old != x; p = p->next) - ; - - if (p == 0) - { - p = (struct fixup_replacement *) oballoc (sizeof (struct fixup_replacement)); - p->old = x; - p->new = 0; - p->next = *replacements; - *replacements = p; - } - - return p; -} - -/* Scan the insn-chain starting with INSN for refs to VAR - and fix them up. TOPLEVEL is nonzero if this chain is the - main chain of insns for the current function. */ - -static void -fixup_var_refs_insns (var, promoted_mode, unsignedp, insn, toplevel) - rtx var; - enum machine_mode promoted_mode; - int unsignedp; - rtx insn; - int toplevel; -{ - rtx call_dest = 0; - - while (insn) - { - rtx next = NEXT_INSN (insn); - rtx note; - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') - { - /* The insn to load VAR from a home in the arglist - is now a no-op. When we see it, just delete it. */ - if (toplevel - && GET_CODE (PATTERN (insn)) == SET - && SET_DEST (PATTERN (insn)) == var - /* If this represents the result of an insn group, - don't delete the insn. */ - && find_reg_note (insn, REG_RETVAL, NULL_RTX) == 0 - && rtx_equal_p (SET_SRC (PATTERN (insn)), var)) - { - /* In unoptimized compilation, we shouldn't call delete_insn - except in jump.c doing warnings. */ - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - if (insn == last_parm_insn) - last_parm_insn = PREV_INSN (next); - } - else - { - struct fixup_replacement *replacements = 0; - rtx next_insn = NEXT_INSN (insn); - -#ifdef SMALL_REGISTER_CLASSES - /* If the insn that copies the results of a CALL_INSN - into a pseudo now references VAR, we have to use an - intermediate pseudo since we want the life of the - return value register to be only a single insn. - - If we don't use an intermediate pseudo, such things as - address computations to make the address of VAR valid - if it is not can be placed beween the CALL_INSN and INSN. - - To make sure this doesn't happen, we record the destination - of the CALL_INSN and see if the next insn uses both that - and VAR. */ - - if (call_dest != 0 && GET_CODE (insn) == INSN - && reg_mentioned_p (var, PATTERN (insn)) - && reg_mentioned_p (call_dest, PATTERN (insn))) - { - rtx temp = gen_reg_rtx (GET_MODE (call_dest)); - - emit_insn_before (gen_move_insn (temp, call_dest), insn); - - PATTERN (insn) = replace_rtx (PATTERN (insn), - call_dest, temp); - } - - if (GET_CODE (insn) == CALL_INSN - && GET_CODE (PATTERN (insn)) == SET) - call_dest = SET_DEST (PATTERN (insn)); - else if (GET_CODE (insn) == CALL_INSN - && GET_CODE (PATTERN (insn)) == PARALLEL - && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) - call_dest = SET_DEST (XVECEXP (PATTERN (insn), 0, 0)); - else - call_dest = 0; -#endif - - /* See if we have to do anything to INSN now that VAR is in - memory. If it needs to be loaded into a pseudo, use a single - pseudo for the entire insn in case there is a MATCH_DUP - between two operands. We pass a pointer to the head of - a list of struct fixup_replacements. If fixup_var_refs_1 - needs to allocate pseudos or replacement MEMs (for SUBREGs), - it will record them in this list. - - If it allocated a pseudo for any replacement, we copy into - it here. */ - - fixup_var_refs_1 (var, promoted_mode, &PATTERN (insn), insn, - &replacements); - - /* If this is last_parm_insn, and any instructions were output - after it to fix it up, then we must set last_parm_insn to - the last such instruction emitted. */ - if (insn == last_parm_insn) - last_parm_insn = PREV_INSN (next_insn); - - while (replacements) - { - if (GET_CODE (replacements->new) == REG) - { - rtx insert_before; - rtx seq; - - /* OLD might be a (subreg (mem)). */ - if (GET_CODE (replacements->old) == SUBREG) - replacements->old - = fixup_memory_subreg (replacements->old, insn, 0); - else - replacements->old - = fixup_stack_1 (replacements->old, insn); - - /* We can not separate USE insns from the CALL_INSN - that they belong to. If this is a CALL_INSN, insert - the move insn before the USE insns preceding it - instead of immediately before the insn. */ - if (GET_CODE (insn) == CALL_INSN) - { - insert_before = insn; - while (GET_CODE (PREV_INSN (insert_before)) == INSN - && GET_CODE (PATTERN (PREV_INSN (insert_before))) == USE) - insert_before = PREV_INSN (insert_before); - } - else - insert_before = insn; - - /* If we are changing the mode, do a conversion. - This might be wasteful, but combine.c will - eliminate much of the waste. */ - - if (GET_MODE (replacements->new) - != GET_MODE (replacements->old)) - { - start_sequence (); - convert_move (replacements->new, - replacements->old, unsignedp); - seq = gen_sequence (); - end_sequence (); - } - else - seq = gen_move_insn (replacements->new, - replacements->old); - - emit_insn_before (seq, insert_before); - } - - replacements = replacements->next; - } - } - - /* Also fix up any invalid exprs in the REG_NOTES of this insn. - But don't touch other insns referred to by reg-notes; - we will get them elsewhere. */ - for (note = REG_NOTES (insn); note; note = XEXP (note, 1)) - if (GET_CODE (note) != INSN_LIST) - XEXP (note, 0) - = walk_fixup_memory_subreg (XEXP (note, 0), insn, 1); - } - insn = next; - } -} - -/* VAR is a MEM that used to be a pseudo register with mode PROMOTED_MODE. - See if the rtx expression at *LOC in INSN needs to be changed. - - REPLACEMENTS is a pointer to a list head that starts out zero, but may - contain a list of original rtx's and replacements. If we find that we need - to modify this insn by replacing a memory reference with a pseudo or by - making a new MEM to implement a SUBREG, we consult that list to see if - we have already chosen a replacement. If none has already been allocated, - we allocate it and update the list. fixup_var_refs_insns will copy VAR - or the SUBREG, as appropriate, to the pseudo. */ - -static void -fixup_var_refs_1 (var, promoted_mode, loc, insn, replacements) - register rtx var; - enum machine_mode promoted_mode; - register rtx *loc; - rtx insn; - struct fixup_replacement **replacements; -{ - register int i; - register rtx x = *loc; - RTX_CODE code = GET_CODE (x); - register char *fmt; - register rtx tem, tem1; - struct fixup_replacement *replacement; - - switch (code) - { - case MEM: - if (var == x) - { - /* If we already have a replacement, use it. Otherwise, - try to fix up this address in case it is invalid. */ - - replacement = find_fixup_replacement (replacements, var); - if (replacement->new) - { - *loc = replacement->new; - return; - } - - *loc = replacement->new = x = fixup_stack_1 (x, insn); - - /* Unless we are forcing memory to register or we changed the mode, - we can leave things the way they are if the insn is valid. */ - - INSN_CODE (insn) = -1; - if (! flag_force_mem && GET_MODE (x) == promoted_mode - && recog_memoized (insn) >= 0) - return; - - *loc = replacement->new = gen_reg_rtx (promoted_mode); - return; - } - - /* If X contains VAR, we need to unshare it here so that we update - each occurrence separately. But all identical MEMs in one insn - must be replaced with the same rtx because of the possibility of - MATCH_DUPs. */ - - if (reg_mentioned_p (var, x)) - { - replacement = find_fixup_replacement (replacements, x); - if (replacement->new == 0) - replacement->new = copy_most_rtx (x, var); - - *loc = x = replacement->new; - } - break; - - case REG: - case CC0: - case PC: - case CONST_INT: - case CONST: - case SYMBOL_REF: - case LABEL_REF: - case CONST_DOUBLE: - return; - - case SIGN_EXTRACT: - case ZERO_EXTRACT: - /* Note that in some cases those types of expressions are altered - by optimize_bit_field, and do not survive to get here. */ - if (XEXP (x, 0) == var - || (GET_CODE (XEXP (x, 0)) == SUBREG - && SUBREG_REG (XEXP (x, 0)) == var)) - { - /* Get TEM as a valid MEM in the mode presently in the insn. - - We don't worry about the possibility of MATCH_DUP here; it - is highly unlikely and would be tricky to handle. */ - - tem = XEXP (x, 0); - if (GET_CODE (tem) == SUBREG) - tem = fixup_memory_subreg (tem, insn, 1); - tem = fixup_stack_1 (tem, insn); - - /* Unless we want to load from memory, get TEM into the proper mode - for an extract from memory. This can only be done if the - extract is at a constant position and length. */ - - if (! flag_force_mem && GET_CODE (XEXP (x, 1)) == CONST_INT - && GET_CODE (XEXP (x, 2)) == CONST_INT - && ! mode_dependent_address_p (XEXP (tem, 0)) - && ! MEM_VOLATILE_P (tem)) - { - enum machine_mode wanted_mode = VOIDmode; - enum machine_mode is_mode = GET_MODE (tem); - int width = INTVAL (XEXP (x, 1)); - int pos = INTVAL (XEXP (x, 2)); - -#ifdef HAVE_extzv - if (GET_CODE (x) == ZERO_EXTRACT) - wanted_mode = insn_operand_mode[(int) CODE_FOR_extzv][1]; -#endif -#ifdef HAVE_extv - if (GET_CODE (x) == SIGN_EXTRACT) - wanted_mode = insn_operand_mode[(int) CODE_FOR_extv][1]; -#endif - /* If we have a narrower mode, we can do something. */ - if (wanted_mode != VOIDmode - && GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode)) - { - int offset = pos / BITS_PER_UNIT; - rtx old_pos = XEXP (x, 2); - rtx newmem; - - /* If the bytes and bits are counted differently, we - must adjust the offset. */ -#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN - offset = (GET_MODE_SIZE (is_mode) - - GET_MODE_SIZE (wanted_mode) - offset); -#endif - - pos %= GET_MODE_BITSIZE (wanted_mode); - - newmem = gen_rtx (MEM, wanted_mode, - plus_constant (XEXP (tem, 0), offset)); - RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (tem); - MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (tem); - MEM_IN_STRUCT_P (newmem) = MEM_IN_STRUCT_P (tem); - - /* Make the change and see if the insn remains valid. */ - INSN_CODE (insn) = -1; - XEXP (x, 0) = newmem; - XEXP (x, 2) = GEN_INT (pos); - - if (recog_memoized (insn) >= 0) - return; - - /* Otherwise, restore old position. XEXP (x, 0) will be - restored later. */ - XEXP (x, 2) = old_pos; - } - } - - /* If we get here, the bitfield extract insn can't accept a memory - reference. Copy the input into a register. */ - - tem1 = gen_reg_rtx (GET_MODE (tem)); - emit_insn_before (gen_move_insn (tem1, tem), insn); - XEXP (x, 0) = tem1; - return; - } - break; - - case SUBREG: - if (SUBREG_REG (x) == var) - { - /* If this is a special SUBREG made because VAR was promoted - from a wider mode, replace it with VAR and call ourself - recursively, this time saying that the object previously - had its current mode (by virtue of the SUBREG). */ - - if (SUBREG_PROMOTED_VAR_P (x)) - { - *loc = var; - fixup_var_refs_1 (var, GET_MODE (var), loc, insn, replacements); - return; - } - - /* If this SUBREG makes VAR wider, it has become a paradoxical - SUBREG with VAR in memory, but these aren't allowed at this - stage of the compilation. So load VAR into a pseudo and take - a SUBREG of that pseudo. */ - if (GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (var))) - { - replacement = find_fixup_replacement (replacements, var); - if (replacement->new == 0) - replacement->new = gen_reg_rtx (GET_MODE (var)); - SUBREG_REG (x) = replacement->new; - return; - } - - /* See if we have already found a replacement for this SUBREG. - If so, use it. Otherwise, make a MEM and see if the insn - is recognized. If not, or if we should force MEM into a register, - make a pseudo for this SUBREG. */ - replacement = find_fixup_replacement (replacements, x); - if (replacement->new) - { - *loc = replacement->new; - return; - } - - replacement->new = *loc = fixup_memory_subreg (x, insn, 0); - - INSN_CODE (insn) = -1; - if (! flag_force_mem && recog_memoized (insn) >= 0) - return; - - *loc = replacement->new = gen_reg_rtx (GET_MODE (x)); - return; - } - break; - - case SET: - /* First do special simplification of bit-field references. */ - if (GET_CODE (SET_DEST (x)) == SIGN_EXTRACT - || GET_CODE (SET_DEST (x)) == ZERO_EXTRACT) - optimize_bit_field (x, insn, 0); - if (GET_CODE (SET_SRC (x)) == SIGN_EXTRACT - || GET_CODE (SET_SRC (x)) == ZERO_EXTRACT) - optimize_bit_field (x, insn, NULL_PTR); - - /* If SET_DEST is now a paradoxical SUBREG, put the result of this - insn into a pseudo and store the low part of the pseudo into VAR. */ - if (GET_CODE (SET_DEST (x)) == SUBREG - && SUBREG_REG (SET_DEST (x)) == var - && (GET_MODE_SIZE (GET_MODE (SET_DEST (x))) - > GET_MODE_SIZE (GET_MODE (var)))) - { - SET_DEST (x) = tem = gen_reg_rtx (GET_MODE (SET_DEST (x))); - emit_insn_after (gen_move_insn (var, gen_lowpart (GET_MODE (var), - tem)), - insn); - break; - } - - { - rtx dest = SET_DEST (x); - rtx src = SET_SRC (x); - rtx outerdest = dest; - - while (GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == ZERO_EXTRACT) - dest = XEXP (dest, 0); - - if (GET_CODE (src) == SUBREG) - src = XEXP (src, 0); - - /* If VAR does not appear at the top level of the SET - just scan the lower levels of the tree. */ - - if (src != var && dest != var) - break; - - /* We will need to rerecognize this insn. */ - INSN_CODE (insn) = -1; - -#ifdef HAVE_insv - if (GET_CODE (outerdest) == ZERO_EXTRACT && dest == var) - { - /* Since this case will return, ensure we fixup all the - operands here. */ - fixup_var_refs_1 (var, promoted_mode, &XEXP (outerdest, 1), - insn, replacements); - fixup_var_refs_1 (var, promoted_mode, &XEXP (outerdest, 2), - insn, replacements); - fixup_var_refs_1 (var, promoted_mode, &SET_SRC (x), - insn, replacements); - - tem = XEXP (outerdest, 0); - - /* Clean up (SUBREG:SI (MEM:mode ...) 0) - that may appear inside a ZERO_EXTRACT. - This was legitimate when the MEM was a REG. */ - if (GET_CODE (tem) == SUBREG - && SUBREG_REG (tem) == var) - tem = fixup_memory_subreg (tem, insn, 1); - else - tem = fixup_stack_1 (tem, insn); - - if (GET_CODE (XEXP (outerdest, 1)) == CONST_INT - && GET_CODE (XEXP (outerdest, 2)) == CONST_INT - && ! mode_dependent_address_p (XEXP (tem, 0)) - && ! MEM_VOLATILE_P (tem)) - { - enum machine_mode wanted_mode - = insn_operand_mode[(int) CODE_FOR_insv][0]; - enum machine_mode is_mode = GET_MODE (tem); - int width = INTVAL (XEXP (outerdest, 1)); - int pos = INTVAL (XEXP (outerdest, 2)); - - /* If we have a narrower mode, we can do something. */ - if (GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode)) - { - int offset = pos / BITS_PER_UNIT; - rtx old_pos = XEXP (outerdest, 2); - rtx newmem; - -#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN - offset = (GET_MODE_SIZE (is_mode) - - GET_MODE_SIZE (wanted_mode) - offset); -#endif - - pos %= GET_MODE_BITSIZE (wanted_mode); - - newmem = gen_rtx (MEM, wanted_mode, - plus_constant (XEXP (tem, 0), offset)); - RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (tem); - MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (tem); - MEM_IN_STRUCT_P (newmem) = MEM_IN_STRUCT_P (tem); - - /* Make the change and see if the insn remains valid. */ - INSN_CODE (insn) = -1; - XEXP (outerdest, 0) = newmem; - XEXP (outerdest, 2) = GEN_INT (pos); - - if (recog_memoized (insn) >= 0) - return; - - /* Otherwise, restore old position. XEXP (x, 0) will be - restored later. */ - XEXP (outerdest, 2) = old_pos; - } - } - - /* If we get here, the bit-field store doesn't allow memory - or isn't located at a constant position. Load the value into - a register, do the store, and put it back into memory. */ - - tem1 = gen_reg_rtx (GET_MODE (tem)); - emit_insn_before (gen_move_insn (tem1, tem), insn); - emit_insn_after (gen_move_insn (tem, tem1), insn); - XEXP (outerdest, 0) = tem1; - return; - } -#endif - - /* STRICT_LOW_PART is a no-op on memory references - and it can cause combinations to be unrecognizable, - so eliminate it. */ - - if (dest == var && GET_CODE (SET_DEST (x)) == STRICT_LOW_PART) - SET_DEST (x) = XEXP (SET_DEST (x), 0); - - /* A valid insn to copy VAR into or out of a register - must be left alone, to avoid an infinite loop here. - If the reference to VAR is by a subreg, fix that up, - since SUBREG is not valid for a memref. - Also fix up the address of the stack slot. - - Note that we must not try to recognize the insn until - after we know that we have valid addresses and no - (subreg (mem ...) ...) constructs, since these interfere - with determining the validity of the insn. */ - - if ((SET_SRC (x) == var - || (GET_CODE (SET_SRC (x)) == SUBREG - && SUBREG_REG (SET_SRC (x)) == var)) - && (GET_CODE (SET_DEST (x)) == REG - || (GET_CODE (SET_DEST (x)) == SUBREG - && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG)) - && x == single_set (PATTERN (insn))) - { - rtx pat; - - replacement = find_fixup_replacement (replacements, SET_SRC (x)); - if (replacement->new) - SET_SRC (x) = replacement->new; - else if (GET_CODE (SET_SRC (x)) == SUBREG) - SET_SRC (x) = replacement->new - = fixup_memory_subreg (SET_SRC (x), insn, 0); - else - SET_SRC (x) = replacement->new - = fixup_stack_1 (SET_SRC (x), insn); - - if (recog_memoized (insn) >= 0) - return; - - /* INSN is not valid, but we know that we want to - copy SET_SRC (x) to SET_DEST (x) in some way. So - we generate the move and see whether it requires more - than one insn. If it does, we emit those insns and - delete INSN. Otherwise, we an just replace the pattern - of INSN; we have already verified above that INSN has - no other function that to do X. */ - - pat = gen_move_insn (SET_DEST (x), SET_SRC (x)); - if (GET_CODE (pat) == SEQUENCE) - { - emit_insn_after (pat, insn); - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - } - else - PATTERN (insn) = pat; - - return; - } - - if ((SET_DEST (x) == var - || (GET_CODE (SET_DEST (x)) == SUBREG - && SUBREG_REG (SET_DEST (x)) == var)) - && (GET_CODE (SET_SRC (x)) == REG - || (GET_CODE (SET_SRC (x)) == SUBREG - && GET_CODE (SUBREG_REG (SET_SRC (x))) == REG)) - && x == single_set (PATTERN (insn))) - { - rtx pat; - - if (GET_CODE (SET_DEST (x)) == SUBREG) - SET_DEST (x) = fixup_memory_subreg (SET_DEST (x), insn, 0); - else - SET_DEST (x) = fixup_stack_1 (SET_DEST (x), insn); - - if (recog_memoized (insn) >= 0) - return; - - pat = gen_move_insn (SET_DEST (x), SET_SRC (x)); - if (GET_CODE (pat) == SEQUENCE) - { - emit_insn_after (pat, insn); - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - } - else - PATTERN (insn) = pat; - - return; - } - - /* Otherwise, storing into VAR must be handled specially - by storing into a temporary and copying that into VAR - with a new insn after this one. Note that this case - will be used when storing into a promoted scalar since - the insn will now have different modes on the input - and output and hence will be invalid (except for the case - of setting it to a constant, which does not need any - change if it is valid). We generate extra code in that case, - but combine.c will eliminate it. */ - - if (dest == var) - { - rtx temp; - rtx fixeddest = SET_DEST (x); - - /* STRICT_LOW_PART can be discarded, around a MEM. */ - if (GET_CODE (fixeddest) == STRICT_LOW_PART) - fixeddest = XEXP (fixeddest, 0); - /* Convert (SUBREG (MEM)) to a MEM in a changed mode. */ - if (GET_CODE (fixeddest) == SUBREG) - fixeddest = fixup_memory_subreg (fixeddest, insn, 0); - else - fixeddest = fixup_stack_1 (fixeddest, insn); - - temp = gen_reg_rtx (GET_MODE (SET_SRC (x)) == VOIDmode - ? GET_MODE (fixeddest) - : GET_MODE (SET_SRC (x))); - - emit_insn_after (gen_move_insn (fixeddest, - gen_lowpart (GET_MODE (fixeddest), - temp)), - insn); - - SET_DEST (x) = temp; - } - } - } - - /* Nothing special about this RTX; fix its operands. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - fixup_var_refs_1 (var, promoted_mode, &XEXP (x, i), insn, replacements); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - fixup_var_refs_1 (var, promoted_mode, &XVECEXP (x, i, j), - insn, replacements); - } - } -} - -/* Given X, an rtx of the form (SUBREG:m1 (MEM:m2 addr)), - return an rtx (MEM:m1 newaddr) which is equivalent. - If any insns must be emitted to compute NEWADDR, put them before INSN. - - UNCRITICAL nonzero means accept paradoxical subregs. - This is used for subregs found inside of ZERO_EXTRACTs and in REG_NOTES. */ - -static rtx -fixup_memory_subreg (x, insn, uncritical) - rtx x; - rtx insn; - int uncritical; -{ - int offset = SUBREG_WORD (x) * UNITS_PER_WORD; - rtx addr = XEXP (SUBREG_REG (x), 0); - enum machine_mode mode = GET_MODE (x); - rtx saved, result; - - /* Paradoxical SUBREGs are usually invalid during RTL generation. */ - if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) - && ! uncritical) - abort (); - -#if BYTES_BIG_ENDIAN - offset += (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) - - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))); -#endif - addr = plus_constant (addr, offset); - if (!flag_force_addr && memory_address_p (mode, addr)) - /* Shortcut if no insns need be emitted. */ - return change_address (SUBREG_REG (x), mode, addr); - start_sequence (); - result = change_address (SUBREG_REG (x), mode, addr); - emit_insn_before (gen_sequence (), insn); - end_sequence (); - return result; -} - -/* Do fixup_memory_subreg on all (SUBREG (MEM ...) ...) contained in X. - Replace subexpressions of X in place. - If X itself is a (SUBREG (MEM ...) ...), return the replacement expression. - Otherwise return X, with its contents possibly altered. - - If any insns must be emitted to compute NEWADDR, put them before INSN. - - UNCRITICAL is as in fixup_memory_subreg. */ - -static rtx -walk_fixup_memory_subreg (x, insn, uncritical) - register rtx x; - rtx insn; - int uncritical; -{ - register enum rtx_code code; - register char *fmt; - register int i; - - if (x == 0) - return 0; - - code = GET_CODE (x); - - if (code == SUBREG && GET_CODE (SUBREG_REG (x)) == MEM) - return fixup_memory_subreg (x, insn, uncritical); - - /* Nothing special about this RTX; fix its operands. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - XEXP (x, i) = walk_fixup_memory_subreg (XEXP (x, i), insn, uncritical); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - XVECEXP (x, i, j) - = walk_fixup_memory_subreg (XVECEXP (x, i, j), insn, uncritical); - } - } - return x; -} - -#if 0 -/* Fix up any references to stack slots that are invalid memory addresses - because they exceed the maximum range of a displacement. */ - -void -fixup_stack_slots () -{ - register rtx insn; - - /* Did we generate a stack slot that is out of range - or otherwise has an invalid address? */ - if (invalid_stack_slot) - { - /* Yes. Must scan all insns for stack-refs that exceed the limit. */ - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN - || GET_CODE (insn) == JUMP_INSN) - fixup_stack_1 (PATTERN (insn), insn); - } -} -#endif - -/* For each memory ref within X, if it refers to a stack slot - with an out of range displacement, put the address in a temp register - (emitting new insns before INSN to load these registers) - and alter the memory ref to use that register. - Replace each such MEM rtx with a copy, to avoid clobberage. */ - -static rtx -fixup_stack_1 (x, insn) - rtx x; - rtx insn; -{ - register int i; - register RTX_CODE code = GET_CODE (x); - register char *fmt; - - if (code == MEM) - { - register rtx ad = XEXP (x, 0); - /* If we have address of a stack slot but it's not valid - (displacement is too large), compute the sum in a register. */ - if (GET_CODE (ad) == PLUS - && GET_CODE (XEXP (ad, 0)) == REG - && REGNO (XEXP (ad, 0)) >= FIRST_VIRTUAL_REGISTER - && REGNO (XEXP (ad, 0)) <= LAST_VIRTUAL_REGISTER - && GET_CODE (XEXP (ad, 1)) == CONST_INT) - { - rtx temp, seq; - if (memory_address_p (GET_MODE (x), ad)) - return x; - - start_sequence (); - temp = copy_to_reg (ad); - seq = gen_sequence (); - end_sequence (); - emit_insn_before (seq, insn); - return change_address (x, VOIDmode, temp); - } - return x; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - XEXP (x, i) = fixup_stack_1 (XEXP (x, i), insn); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - XVECEXP (x, i, j) = fixup_stack_1 (XVECEXP (x, i, j), insn); - } - } - return x; -} - -/* Optimization: a bit-field instruction whose field - happens to be a byte or halfword in memory - can be changed to a move instruction. - - We call here when INSN is an insn to examine or store into a bit-field. - BODY is the SET-rtx to be altered. - - EQUIV_MEM is the table `reg_equiv_mem' if that is available; else 0. - (Currently this is called only from function.c, and EQUIV_MEM - is always 0.) */ - -static void -optimize_bit_field (body, insn, equiv_mem) - rtx body; - rtx insn; - rtx *equiv_mem; -{ - register rtx bitfield; - int destflag; - rtx seq = 0; - enum machine_mode mode; - - if (GET_CODE (SET_DEST (body)) == SIGN_EXTRACT - || GET_CODE (SET_DEST (body)) == ZERO_EXTRACT) - bitfield = SET_DEST (body), destflag = 1; - else - bitfield = SET_SRC (body), destflag = 0; - - /* First check that the field being stored has constant size and position - and is in fact a byte or halfword suitably aligned. */ - - if (GET_CODE (XEXP (bitfield, 1)) == CONST_INT - && GET_CODE (XEXP (bitfield, 2)) == CONST_INT - && ((mode = mode_for_size (INTVAL (XEXP (bitfield, 1)), MODE_INT, 1)) - != BLKmode) - && INTVAL (XEXP (bitfield, 2)) % INTVAL (XEXP (bitfield, 1)) == 0) - { - register rtx memref = 0; - - /* Now check that the containing word is memory, not a register, - and that it is safe to change the machine mode. */ - - if (GET_CODE (XEXP (bitfield, 0)) == MEM) - memref = XEXP (bitfield, 0); - else if (GET_CODE (XEXP (bitfield, 0)) == REG - && equiv_mem != 0) - memref = equiv_mem[REGNO (XEXP (bitfield, 0))]; - else if (GET_CODE (XEXP (bitfield, 0)) == SUBREG - && GET_CODE (SUBREG_REG (XEXP (bitfield, 0))) == MEM) - memref = SUBREG_REG (XEXP (bitfield, 0)); - else if (GET_CODE (XEXP (bitfield, 0)) == SUBREG - && equiv_mem != 0 - && GET_CODE (SUBREG_REG (XEXP (bitfield, 0))) == REG) - memref = equiv_mem[REGNO (SUBREG_REG (XEXP (bitfield, 0)))]; - - if (memref - && ! mode_dependent_address_p (XEXP (memref, 0)) - && ! MEM_VOLATILE_P (memref)) - { - /* Now adjust the address, first for any subreg'ing - that we are now getting rid of, - and then for which byte of the word is wanted. */ - - register int offset = INTVAL (XEXP (bitfield, 2)); - /* Adjust OFFSET to count bits from low-address byte. */ -#if BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN - offset = (GET_MODE_BITSIZE (GET_MODE (XEXP (bitfield, 0))) - - offset - INTVAL (XEXP (bitfield, 1))); -#endif - /* Adjust OFFSET to count bytes from low-address byte. */ - offset /= BITS_PER_UNIT; - if (GET_CODE (XEXP (bitfield, 0)) == SUBREG) - { - offset += SUBREG_WORD (XEXP (bitfield, 0)) * UNITS_PER_WORD; -#if BYTES_BIG_ENDIAN - offset -= (MIN (UNITS_PER_WORD, - GET_MODE_SIZE (GET_MODE (XEXP (bitfield, 0)))) - - MIN (UNITS_PER_WORD, - GET_MODE_SIZE (GET_MODE (memref)))); -#endif - } - - memref = change_address (memref, mode, - plus_constant (XEXP (memref, 0), offset)); - - /* Store this memory reference where - we found the bit field reference. */ - - if (destflag) - { - validate_change (insn, &SET_DEST (body), memref, 1); - if (! CONSTANT_ADDRESS_P (SET_SRC (body))) - { - rtx src = SET_SRC (body); - while (GET_CODE (src) == SUBREG - && SUBREG_WORD (src) == 0) - src = SUBREG_REG (src); - if (GET_MODE (src) != GET_MODE (memref)) - src = gen_lowpart (GET_MODE (memref), SET_SRC (body)); - validate_change (insn, &SET_SRC (body), src, 1); - } - else if (GET_MODE (SET_SRC (body)) != VOIDmode - && GET_MODE (SET_SRC (body)) != GET_MODE (memref)) - /* This shouldn't happen because anything that didn't have - one of these modes should have got converted explicitly - and then referenced through a subreg. - This is so because the original bit-field was - handled by agg_mode and so its tree structure had - the same mode that memref now has. */ - abort (); - } - else - { - rtx dest = SET_DEST (body); - - while (GET_CODE (dest) == SUBREG - && SUBREG_WORD (dest) == 0) - dest = SUBREG_REG (dest); - - validate_change (insn, &SET_DEST (body), dest, 1); - - if (GET_MODE (dest) == GET_MODE (memref)) - validate_change (insn, &SET_SRC (body), memref, 1); - else - { - /* Convert the mem ref to the destination mode. */ - rtx newreg = gen_reg_rtx (GET_MODE (dest)); - - start_sequence (); - convert_move (newreg, memref, - GET_CODE (SET_SRC (body)) == ZERO_EXTRACT); - seq = get_insns (); - end_sequence (); - - validate_change (insn, &SET_SRC (body), newreg, 1); - } - } - - /* See if we can convert this extraction or insertion into - a simple move insn. We might not be able to do so if this - was, for example, part of a PARALLEL. - - If we succeed, write out any needed conversions. If we fail, - it is hard to guess why we failed, so don't do anything - special; just let the optimization be suppressed. */ - - if (apply_change_group () && seq) - emit_insns_before (seq, insn); - } - } -} - -/* These routines are responsible for converting virtual register references - to the actual hard register references once RTL generation is complete. - - The following four variables are used for communication between the - routines. They contain the offsets of the virtual registers from their - respective hard registers. */ - -static int in_arg_offset; -static int var_offset; -static int dynamic_offset; -static int out_arg_offset; - -/* In most machines, the stack pointer register is equivalent to the bottom - of the stack. */ - -#ifndef STACK_POINTER_OFFSET -#define STACK_POINTER_OFFSET 0 -#endif - -/* If not defined, pick an appropriate default for the offset of dynamically - allocated memory depending on the value of ACCUMULATE_OUTGOING_ARGS, - REG_PARM_STACK_SPACE, and OUTGOING_REG_PARM_STACK_SPACE. */ - -#ifndef STACK_DYNAMIC_OFFSET - -#ifdef ACCUMULATE_OUTGOING_ARGS -/* The bottom of the stack points to the actual arguments. If - REG_PARM_STACK_SPACE is defined, this includes the space for the register - parameters. However, if OUTGOING_REG_PARM_STACK space is not defined, - stack space for register parameters is not pushed by the caller, but - rather part of the fixed stack areas and hence not included in - `current_function_outgoing_args_size'. Nevertheless, we must allow - for it when allocating stack dynamic objects. */ - -#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE) -#define STACK_DYNAMIC_OFFSET(FNDECL) \ -(current_function_outgoing_args_size \ - + REG_PARM_STACK_SPACE (FNDECL) + (STACK_POINTER_OFFSET)) - -#else -#define STACK_DYNAMIC_OFFSET(FNDECL) \ -(current_function_outgoing_args_size + (STACK_POINTER_OFFSET)) -#endif - -#else -#define STACK_DYNAMIC_OFFSET(FNDECL) STACK_POINTER_OFFSET -#endif -#endif - -/* Pass through the INSNS of function FNDECL and convert virtual register - references to hard register references. */ - -void -instantiate_virtual_regs (fndecl, insns) - tree fndecl; - rtx insns; -{ - rtx insn; - - /* Compute the offsets to use for this function. */ - in_arg_offset = FIRST_PARM_OFFSET (fndecl); - var_offset = STARTING_FRAME_OFFSET; - dynamic_offset = STACK_DYNAMIC_OFFSET (fndecl); - out_arg_offset = STACK_POINTER_OFFSET; - - /* Scan all variables and parameters of this function. For each that is - in memory, instantiate all virtual registers if the result is a valid - address. If not, we do it later. That will handle most uses of virtual - regs on many machines. */ - instantiate_decls (fndecl, 1); - - /* Initialize recognition, indicating that volatile is OK. */ - init_recog (); - - /* Scan through all the insns, instantiating every virtual register still - present. */ - for (insn = insns; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN - || GET_CODE (insn) == CALL_INSN) - { - instantiate_virtual_regs_1 (&PATTERN (insn), insn, 1); - instantiate_virtual_regs_1 (®_NOTES (insn), NULL_RTX, 0); - } - - /* Now instantiate the remaining register equivalences for debugging info. - These will not be valid addresses. */ - instantiate_decls (fndecl, 0); - - /* Indicate that, from now on, assign_stack_local should use - frame_pointer_rtx. */ - virtuals_instantiated = 1; -} - -/* Scan all decls in FNDECL (both variables and parameters) and instantiate - all virtual registers in their DECL_RTL's. - - If VALID_ONLY, do this only if the resulting address is still valid. - Otherwise, always do it. */ - -static void -instantiate_decls (fndecl, valid_only) - tree fndecl; - int valid_only; -{ - tree decl; - - if (DECL_INLINE (fndecl)) - /* When compiling an inline function, the obstack used for - rtl allocation is the maybepermanent_obstack. Calling - `resume_temporary_allocation' switches us back to that - obstack while we process this function's parameters. */ - resume_temporary_allocation (); - - /* Process all parameters of the function. */ - for (decl = DECL_ARGUMENTS (fndecl); decl; decl = TREE_CHAIN (decl)) - { - instantiate_decl (DECL_RTL (decl), int_size_in_bytes (TREE_TYPE (decl)), - valid_only); - instantiate_decl (DECL_INCOMING_RTL (decl), - int_size_in_bytes (TREE_TYPE (decl)), valid_only); - } - - /* Now process all variables defined in the function or its subblocks. */ - instantiate_decls_1 (DECL_INITIAL (fndecl), valid_only); - - if (DECL_INLINE (fndecl)) - { - /* Save all rtl allocated for this function by raising the - high-water mark on the maybepermanent_obstack. */ - preserve_data (); - /* All further rtl allocation is now done in the current_obstack. */ - rtl_in_current_obstack (); - } -} - -/* Subroutine of instantiate_decls: Process all decls in the given - BLOCK node and all its subblocks. */ - -static void -instantiate_decls_1 (let, valid_only) - tree let; - int valid_only; -{ - tree t; - - for (t = BLOCK_VARS (let); t; t = TREE_CHAIN (t)) - instantiate_decl (DECL_RTL (t), int_size_in_bytes (TREE_TYPE (t)), - valid_only); - - /* Process all subblocks. */ - for (t = BLOCK_SUBBLOCKS (let); t; t = TREE_CHAIN (t)) - instantiate_decls_1 (t, valid_only); -} - -/* Subroutine of the preceding procedures: Given RTL representing a - decl and the size of the object, do any instantiation required. - - If VALID_ONLY is non-zero, it means that the RTL should only be - changed if the new address is valid. */ - -static void -instantiate_decl (x, size, valid_only) - rtx x; - int size; - int valid_only; -{ - enum machine_mode mode; - rtx addr; - - /* If this is not a MEM, no need to do anything. Similarly if the - address is a constant or a register that is not a virtual register. */ - - if (x == 0 || GET_CODE (x) != MEM) - return; - - addr = XEXP (x, 0); - if (CONSTANT_P (addr) - || (GET_CODE (addr) == REG - && (REGNO (addr) < FIRST_VIRTUAL_REGISTER - || REGNO (addr) > LAST_VIRTUAL_REGISTER))) - return; - - /* If we should only do this if the address is valid, copy the address. - We need to do this so we can undo any changes that might make the - address invalid. This copy is unfortunate, but probably can't be - avoided. */ - - if (valid_only) - addr = copy_rtx (addr); - - instantiate_virtual_regs_1 (&addr, NULL_RTX, 0); - - if (! valid_only) - return; - - /* Now verify that the resulting address is valid for every integer or - floating-point mode up to and including SIZE bytes long. We do this - since the object might be accessed in any mode and frame addresses - are shared. */ - - for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); - mode != VOIDmode && GET_MODE_SIZE (mode) <= size; - mode = GET_MODE_WIDER_MODE (mode)) - if (! memory_address_p (mode, addr)) - return; - - for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); - mode != VOIDmode && GET_MODE_SIZE (mode) <= size; - mode = GET_MODE_WIDER_MODE (mode)) - if (! memory_address_p (mode, addr)) - return; - - /* Otherwise, put back the address, now that we have updated it and we - know it is valid. */ - - XEXP (x, 0) = addr; -} - -/* Given a pointer to a piece of rtx and an optional pointer to the - containing object, instantiate any virtual registers present in it. - - If EXTRA_INSNS, we always do the replacement and generate - any extra insns before OBJECT. If it zero, we do nothing if replacement - is not valid. - - Return 1 if we either had nothing to do or if we were able to do the - needed replacement. Return 0 otherwise; we only return zero if - EXTRA_INSNS is zero. - - We first try some simple transformations to avoid the creation of extra - pseudos. */ - -static int -instantiate_virtual_regs_1 (loc, object, extra_insns) - rtx *loc; - rtx object; - int extra_insns; -{ - rtx x; - RTX_CODE code; - rtx new = 0; - int offset; - rtx temp; - rtx seq; - int i, j; - char *fmt; - - /* Re-start here to avoid recursion in common cases. */ - restart: - - x = *loc; - if (x == 0) - return 1; - - code = GET_CODE (x); - - /* Check for some special cases. */ - switch (code) - { - case CONST_INT: - case CONST_DOUBLE: - case CONST: - case SYMBOL_REF: - case CODE_LABEL: - case PC: - case CC0: - case ASM_INPUT: - case ADDR_VEC: - case ADDR_DIFF_VEC: - case RETURN: - return 1; - - case SET: - /* We are allowed to set the virtual registers. This means that - that the actual register should receive the source minus the - appropriate offset. This is used, for example, in the handling - of non-local gotos. */ - if (SET_DEST (x) == virtual_incoming_args_rtx) - new = arg_pointer_rtx, offset = - in_arg_offset; - else if (SET_DEST (x) == virtual_stack_vars_rtx) - new = frame_pointer_rtx, offset = - var_offset; - else if (SET_DEST (x) == virtual_stack_dynamic_rtx) - new = stack_pointer_rtx, offset = - dynamic_offset; - else if (SET_DEST (x) == virtual_outgoing_args_rtx) - new = stack_pointer_rtx, offset = - out_arg_offset; - - if (new) - { - /* The only valid sources here are PLUS or REG. Just do - the simplest possible thing to handle them. */ - if (GET_CODE (SET_SRC (x)) != REG - && GET_CODE (SET_SRC (x)) != PLUS) - abort (); - - start_sequence (); - if (GET_CODE (SET_SRC (x)) != REG) - temp = force_operand (SET_SRC (x), NULL_RTX); - else - temp = SET_SRC (x); - temp = force_operand (plus_constant (temp, offset), NULL_RTX); - seq = get_insns (); - end_sequence (); - - emit_insns_before (seq, object); - SET_DEST (x) = new; - - if (!validate_change (object, &SET_SRC (x), temp, 0) - || ! extra_insns) - abort (); - - return 1; - } - - instantiate_virtual_regs_1 (&SET_DEST (x), object, extra_insns); - loc = &SET_SRC (x); - goto restart; - - case PLUS: - /* Handle special case of virtual register plus constant. */ - if (CONSTANT_P (XEXP (x, 1))) - { - rtx old; - - /* Check for (plus (plus VIRT foo) (const_int)) first. */ - if (GET_CODE (XEXP (x, 0)) == PLUS) - { - rtx inner = XEXP (XEXP (x, 0), 0); - - if (inner == virtual_incoming_args_rtx) - new = arg_pointer_rtx, offset = in_arg_offset; - else if (inner == virtual_stack_vars_rtx) - new = frame_pointer_rtx, offset = var_offset; - else if (inner == virtual_stack_dynamic_rtx) - new = stack_pointer_rtx, offset = dynamic_offset; - else if (inner == virtual_outgoing_args_rtx) - new = stack_pointer_rtx, offset = out_arg_offset; - else - { - loc = &XEXP (x, 0); - goto restart; - } - - instantiate_virtual_regs_1 (&XEXP (XEXP (x, 0), 1), object, - extra_insns); - new = gen_rtx (PLUS, Pmode, new, XEXP (XEXP (x, 0), 1)); - } - - else if (XEXP (x, 0) == virtual_incoming_args_rtx) - new = arg_pointer_rtx, offset = in_arg_offset; - else if (XEXP (x, 0) == virtual_stack_vars_rtx) - new = frame_pointer_rtx, offset = var_offset; - else if (XEXP (x, 0) == virtual_stack_dynamic_rtx) - new = stack_pointer_rtx, offset = dynamic_offset; - else if (XEXP (x, 0) == virtual_outgoing_args_rtx) - new = stack_pointer_rtx, offset = out_arg_offset; - else - { - /* We know the second operand is a constant. Unless the - first operand is a REG (which has been already checked), - it needs to be checked. */ - if (GET_CODE (XEXP (x, 0)) != REG) - { - loc = &XEXP (x, 0); - goto restart; - } - return 1; - } - - old = XEXP (x, 0); - XEXP (x, 0) = new; - new = plus_constant (XEXP (x, 1), offset); - - /* If the new constant is zero, try to replace the sum with its - first operand. */ - if (new == const0_rtx - && validate_change (object, loc, XEXP (x, 0), 0)) - return 1; - - /* Next try to replace constant with new one. */ - if (!validate_change (object, &XEXP (x, 1), new, 0)) - { - if (! extra_insns) - { - XEXP (x, 0) = old; - return 0; - } - - /* Otherwise copy the new constant into a register and replace - constant with that register. */ - temp = gen_reg_rtx (Pmode); - if (validate_change (object, &XEXP (x, 1), temp, 0)) - emit_insn_before (gen_move_insn (temp, new), object); - else - { - /* If that didn't work, replace this expression with a - register containing the sum. */ - - new = gen_rtx (PLUS, Pmode, XEXP (x, 0), new); - XEXP (x, 0) = old; - - start_sequence (); - temp = force_operand (new, NULL_RTX); - seq = get_insns (); - end_sequence (); - - emit_insns_before (seq, object); - if (! validate_change (object, loc, temp, 0) - && ! validate_replace_rtx (x, temp, object)) - abort (); - } - } - - return 1; - } - - /* Fall through to generic two-operand expression case. */ - case EXPR_LIST: - case CALL: - case COMPARE: - case MINUS: - case MULT: - case DIV: case UDIV: - case MOD: case UMOD: - case AND: case IOR: case XOR: - case LSHIFT: case ASHIFT: case ROTATE: - case ASHIFTRT: case LSHIFTRT: case ROTATERT: - case NE: case EQ: - case GE: case GT: case GEU: case GTU: - case LE: case LT: case LEU: case LTU: - if (XEXP (x, 1) && ! CONSTANT_P (XEXP (x, 1))) - instantiate_virtual_regs_1 (&XEXP (x, 1), object, extra_insns); - loc = &XEXP (x, 0); - goto restart; - - case MEM: - /* Most cases of MEM that convert to valid addresses have already been - handled by our scan of regno_reg_rtx. The only special handling we - need here is to make a copy of the rtx to ensure it isn't being - shared if we have to change it to a pseudo. - - If the rtx is a simple reference to an address via a virtual register, - it can potentially be shared. In such cases, first try to make it - a valid address, which can also be shared. Otherwise, copy it and - proceed normally. - - First check for common cases that need no processing. These are - usually due to instantiation already being done on a previous instance - of a shared rtx. */ - - temp = XEXP (x, 0); - if (CONSTANT_ADDRESS_P (temp) -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - || temp == arg_pointer_rtx -#endif - || temp == frame_pointer_rtx) - return 1; - - if (GET_CODE (temp) == PLUS - && CONSTANT_ADDRESS_P (XEXP (temp, 1)) - && (XEXP (temp, 0) == frame_pointer_rtx -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - || XEXP (temp, 0) == arg_pointer_rtx -#endif - )) - return 1; - - if (temp == virtual_stack_vars_rtx - || temp == virtual_incoming_args_rtx - || (GET_CODE (temp) == PLUS - && CONSTANT_ADDRESS_P (XEXP (temp, 1)) - && (XEXP (temp, 0) == virtual_stack_vars_rtx - || XEXP (temp, 0) == virtual_incoming_args_rtx))) - { - /* This MEM may be shared. If the substitution can be done without - the need to generate new pseudos, we want to do it in place - so all copies of the shared rtx benefit. The call below will - only make substitutions if the resulting address is still - valid. - - Note that we cannot pass X as the object in the recursive call - since the insn being processed may not allow all valid - addresses. However, if we were not passed on object, we can - only modify X without copying it if X will have a valid - address. - - ??? Also note that this can still lose if OBJECT is an insn that - has less restrictions on an address that some other insn. - In that case, we will modify the shared address. This case - doesn't seem very likely, though. */ - - if (instantiate_virtual_regs_1 (&XEXP (x, 0), - object ? object : x, 0)) - return 1; - - /* Otherwise make a copy and process that copy. We copy the entire - RTL expression since it might be a PLUS which could also be - shared. */ - *loc = x = copy_rtx (x); - } - - /* Fall through to generic unary operation case. */ - case USE: - case CLOBBER: - case SUBREG: - case STRICT_LOW_PART: - case NEG: case NOT: - case PRE_DEC: case PRE_INC: case POST_DEC: case POST_INC: - case SIGN_EXTEND: case ZERO_EXTEND: - case TRUNCATE: case FLOAT_EXTEND: case FLOAT_TRUNCATE: - case FLOAT: case FIX: - case UNSIGNED_FIX: case UNSIGNED_FLOAT: - case ABS: - case SQRT: - case FFS: - /* These case either have just one operand or we know that we need not - check the rest of the operands. */ - loc = &XEXP (x, 0); - goto restart; - - case REG: - /* Try to replace with a PLUS. If that doesn't work, compute the sum - in front of this insn and substitute the temporary. */ - if (x == virtual_incoming_args_rtx) - new = arg_pointer_rtx, offset = in_arg_offset; - else if (x == virtual_stack_vars_rtx) - new = frame_pointer_rtx, offset = var_offset; - else if (x == virtual_stack_dynamic_rtx) - new = stack_pointer_rtx, offset = dynamic_offset; - else if (x == virtual_outgoing_args_rtx) - new = stack_pointer_rtx, offset = out_arg_offset; - - if (new) - { - temp = plus_constant (new, offset); - if (!validate_change (object, loc, temp, 0)) - { - if (! extra_insns) - return 0; - - start_sequence (); - temp = force_operand (temp, NULL_RTX); - seq = get_insns (); - end_sequence (); - - emit_insns_before (seq, object); - if (! validate_change (object, loc, temp, 0) - && ! validate_replace_rtx (x, temp, object)) - abort (); - } - } - - return 1; - } - - /* Scan all subexpressions. */ - fmt = GET_RTX_FORMAT (code); - for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++) - if (*fmt == 'e') - { - if (!instantiate_virtual_regs_1 (&XEXP (x, i), object, extra_insns)) - return 0; - } - else if (*fmt == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - if (! instantiate_virtual_regs_1 (&XVECEXP (x, i, j), object, - extra_insns)) - return 0; - - return 1; -} - -/* Optimization: assuming this function does not receive nonlocal gotos, - delete the handlers for such, as well as the insns to establish - and disestablish them. */ - -static void -delete_handlers () -{ - rtx insn; - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - { - /* Delete the handler by turning off the flag that would - prevent jump_optimize from deleting it. - Also permit deletion of the nonlocal labels themselves - if nothing local refers to them. */ - if (GET_CODE (insn) == CODE_LABEL) - LABEL_PRESERVE_P (insn) = 0; - if (GET_CODE (insn) == INSN - && ((nonlocal_goto_handler_slot != 0 - && reg_mentioned_p (nonlocal_goto_handler_slot, PATTERN (insn))) - || (nonlocal_goto_stack_level != 0 - && reg_mentioned_p (nonlocal_goto_stack_level, - PATTERN (insn))))) - delete_insn (insn); - } -} - -/* Return a list (chain of EXPR_LIST nodes) for the nonlocal labels - of the current function. */ - -rtx -nonlocal_label_rtx_list () -{ - tree t; - rtx x = 0; - - for (t = nonlocal_labels; t; t = TREE_CHAIN (t)) - x = gen_rtx (EXPR_LIST, VOIDmode, label_rtx (TREE_VALUE (t)), x); - - return x; -} - -/* Output a USE for any register use in RTL. - This is used with -noreg to mark the extent of lifespan - of any registers used in a user-visible variable's DECL_RTL. */ - -void -use_variable (rtl) - rtx rtl; -{ - if (GET_CODE (rtl) == REG) - /* This is a register variable. */ - emit_insn (gen_rtx (USE, VOIDmode, rtl)); - else if (GET_CODE (rtl) == MEM - && GET_CODE (XEXP (rtl, 0)) == REG - && (REGNO (XEXP (rtl, 0)) < FIRST_VIRTUAL_REGISTER - || REGNO (XEXP (rtl, 0)) > LAST_VIRTUAL_REGISTER) - && XEXP (rtl, 0) != current_function_internal_arg_pointer) - /* This is a variable-sized structure. */ - emit_insn (gen_rtx (USE, VOIDmode, XEXP (rtl, 0))); -} - -/* Like use_variable except that it outputs the USEs after INSN - instead of at the end of the insn-chain. */ - -void -use_variable_after (rtl, insn) - rtx rtl, insn; -{ - if (GET_CODE (rtl) == REG) - /* This is a register variable. */ - emit_insn_after (gen_rtx (USE, VOIDmode, rtl), insn); - else if (GET_CODE (rtl) == MEM - && GET_CODE (XEXP (rtl, 0)) == REG - && (REGNO (XEXP (rtl, 0)) < FIRST_VIRTUAL_REGISTER - || REGNO (XEXP (rtl, 0)) > LAST_VIRTUAL_REGISTER) - && XEXP (rtl, 0) != current_function_internal_arg_pointer) - /* This is a variable-sized structure. */ - emit_insn_after (gen_rtx (USE, VOIDmode, XEXP (rtl, 0)), insn); -} - -int -max_parm_reg_num () -{ - return max_parm_reg; -} - -/* Return the first insn following those generated by `assign_parms'. */ - -rtx -get_first_nonparm_insn () -{ - if (last_parm_insn) - return NEXT_INSN (last_parm_insn); - return get_insns (); -} - -/* Return the first NOTE_INSN_BLOCK_BEG note in the function. - Crash if there is none. */ - -rtx -get_first_block_beg () -{ - register rtx searcher; - register rtx insn = get_first_nonparm_insn (); - - for (searcher = insn; searcher; searcher = NEXT_INSN (searcher)) - if (GET_CODE (searcher) == NOTE - && NOTE_LINE_NUMBER (searcher) == NOTE_INSN_BLOCK_BEG) - return searcher; - - abort (); /* Invalid call to this function. (See comments above.) */ - return NULL_RTX; -} - -/* Return 1 if EXP returns an aggregate value, for which an address - must be passed to the function or returned by the function. */ - -int -aggregate_value_p (exp) - tree exp; -{ - int i, regno, nregs; - rtx reg; - if (RETURN_IN_MEMORY (TREE_TYPE (exp))) - return 1; - if (flag_pcc_struct_return - && (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE - || TREE_CODE (TREE_TYPE (exp)) == QUAL_UNION_TYPE)) - return 1; - /* Make sure we have suitable call-clobbered regs to return - the value in; if not, we must return it in memory. */ - reg = hard_function_value (TREE_TYPE (exp), 0); - regno = REGNO (reg); - nregs = HARD_REGNO_NREGS (regno, TYPE_MODE (TREE_TYPE (exp))); - for (i = 0; i < nregs; i++) - if (! call_used_regs[regno + i]) - return 1; - return 0; -} - -/* Assign RTL expressions to the function's parameters. - This may involve copying them into registers and using - those registers as the RTL for them. - - If SECOND_TIME is non-zero it means that this function is being - called a second time. This is done by integrate.c when a function's - compilation is deferred. We need to come back here in case the - FUNCTION_ARG macro computes items needed for the rest of the compilation - (such as changing which registers are fixed or caller-saved). But suppress - writing any insns or setting DECL_RTL of anything in this case. */ - -void -assign_parms (fndecl, second_time) - tree fndecl; - int second_time; -{ - register tree parm; - register rtx entry_parm = 0; - register rtx stack_parm = 0; - CUMULATIVE_ARGS args_so_far; - enum machine_mode promoted_mode, passed_mode, nominal_mode; - int unsignedp; - /* Total space needed so far for args on the stack, - given as a constant and a tree-expression. */ - struct args_size stack_args_size; - tree fntype = TREE_TYPE (fndecl); - tree fnargs = DECL_ARGUMENTS (fndecl); - /* This is used for the arg pointer when referring to stack args. */ - rtx internal_arg_pointer; - /* This is a dummy PARM_DECL that we used for the function result if - the function returns a structure. */ - tree function_result_decl = 0; - int nparmregs = list_length (fnargs) + LAST_VIRTUAL_REGISTER + 1; - int varargs_setup = 0; - rtx conversion_insns = 0; - /* FUNCTION_ARG may look at this variable. Since this is not - expanding a call it will always be zero in this function. */ - int current_call_is_indirect = 0; - - /* Nonzero if the last arg is named `__builtin_va_alist', - which is used on some machines for old-fashioned non-ANSI varargs.h; - this should be stuck onto the stack as if it had arrived there. */ - int vararg - = (fnargs - && (parm = tree_last (fnargs)) != 0 - && DECL_NAME (parm) - && (! strcmp (IDENTIFIER_POINTER (DECL_NAME (parm)), - "__builtin_va_alist"))); - - /* Nonzero if function takes extra anonymous args. - This means the last named arg must be on the stack - right before the anonymous ones. */ - int stdarg - = (TYPE_ARG_TYPES (fntype) != 0 - && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) - != void_type_node)); - - /* If the reg that the virtual arg pointer will be translated into is - not a fixed reg or is the stack pointer, make a copy of the virtual - arg pointer, and address parms via the copy. The frame pointer is - considered fixed even though it is not marked as such. - - The second time through, simply use ap to avoid generating rtx. */ - - if ((ARG_POINTER_REGNUM == STACK_POINTER_REGNUM - || ! (fixed_regs[ARG_POINTER_REGNUM] - || ARG_POINTER_REGNUM == FRAME_POINTER_REGNUM)) - && ! second_time) - internal_arg_pointer = copy_to_reg (virtual_incoming_args_rtx); - else - internal_arg_pointer = virtual_incoming_args_rtx; - current_function_internal_arg_pointer = internal_arg_pointer; - - stack_args_size.constant = 0; - stack_args_size.var = 0; - - /* If struct value address is treated as the first argument, make it so. */ - if (aggregate_value_p (DECL_RESULT (fndecl)) - && ! current_function_returns_pcc_struct - && struct_value_incoming_rtx == 0) - { - tree type = build_pointer_type (fntype); - - function_result_decl = build_decl (PARM_DECL, NULL_TREE, type); - - DECL_ARG_TYPE (function_result_decl) = type; - TREE_CHAIN (function_result_decl) = fnargs; - fnargs = function_result_decl; - } - - parm_reg_stack_loc = (rtx *) oballoc (nparmregs * sizeof (rtx)); - bzero (parm_reg_stack_loc, nparmregs * sizeof (rtx)); - -#ifdef INIT_CUMULATIVE_INCOMING_ARGS - INIT_CUMULATIVE_INCOMING_ARGS (args_so_far, fntype, NULL_RTX); -#else - INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX); -#endif - - /* We haven't yet found an argument that we must push and pretend the - caller did. */ - current_function_pretend_args_size = 0; - - for (parm = fnargs; parm; parm = TREE_CHAIN (parm)) - { - int aggregate - = (TREE_CODE (TREE_TYPE (parm)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE (parm)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (parm)) == UNION_TYPE - || TREE_CODE (TREE_TYPE (parm)) == QUAL_UNION_TYPE); - struct args_size stack_offset; - struct args_size arg_size; - int passed_pointer = 0; - tree passed_type = DECL_ARG_TYPE (parm); - - /* Set LAST_NAMED if this is last named arg before some - anonymous args. We treat it as if it were anonymous too. */ - int last_named = ((TREE_CHAIN (parm) == 0 - || DECL_NAME (TREE_CHAIN (parm)) == 0) - && (vararg || stdarg)); - - if (TREE_TYPE (parm) == error_mark_node - /* This can happen after weird syntax errors - or if an enum type is defined among the parms. */ - || TREE_CODE (parm) != PARM_DECL - || passed_type == NULL) - { - DECL_INCOMING_RTL (parm) = DECL_RTL (parm) = gen_rtx (MEM, BLKmode, - const0_rtx); - TREE_USED (parm) = 1; - continue; - } - - /* For varargs.h function, save info about regs and stack space - used by the individual args, not including the va_alist arg. */ - if (vararg && last_named) - current_function_args_info = args_so_far; - - /* Find mode of arg as it is passed, and mode of arg - as it should be during execution of this function. */ - passed_mode = TYPE_MODE (passed_type); - nominal_mode = TYPE_MODE (TREE_TYPE (parm)); - - /* If the parm's mode is VOID, its value doesn't matter, - and avoid the usual things like emit_move_insn that could crash. */ - if (nominal_mode == VOIDmode) - { - DECL_INCOMING_RTL (parm) = DECL_RTL (parm) = const0_rtx; - continue; - } - -#ifdef FUNCTION_ARG_PASS_BY_REFERENCE - /* See if this arg was passed by invisible reference. */ - if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, passed_mode, - passed_type, ! last_named)) - { - passed_type = build_pointer_type (passed_type); - passed_pointer = 1; - passed_mode = nominal_mode = Pmode; - } -#endif - - promoted_mode = passed_mode; - -#ifdef PROMOTE_FUNCTION_ARGS - /* Compute the mode in which the arg is actually extended to. */ - if (TREE_CODE (passed_type) == INTEGER_TYPE - || TREE_CODE (passed_type) == ENUMERAL_TYPE - || TREE_CODE (passed_type) == BOOLEAN_TYPE - || TREE_CODE (passed_type) == CHAR_TYPE - || TREE_CODE (passed_type) == REAL_TYPE - || TREE_CODE (passed_type) == POINTER_TYPE - || TREE_CODE (passed_type) == OFFSET_TYPE) - { - unsignedp = TREE_UNSIGNED (passed_type); - PROMOTE_MODE (promoted_mode, unsignedp, passed_type); - } -#endif - - /* Let machine desc say which reg (if any) the parm arrives in. - 0 means it arrives on the stack. */ -#ifdef FUNCTION_INCOMING_ARG - entry_parm = FUNCTION_INCOMING_ARG (args_so_far, promoted_mode, - passed_type, ! last_named); -#else - entry_parm = FUNCTION_ARG (args_so_far, promoted_mode, - passed_type, ! last_named); -#endif - - if (entry_parm) - passed_mode = promoted_mode; - -#ifdef SETUP_INCOMING_VARARGS - /* If this is the last named parameter, do any required setup for - varargs or stdargs. We need to know about the case of this being an - addressable type, in which case we skip the registers it - would have arrived in. - - For stdargs, LAST_NAMED will be set for two parameters, the one that - is actually the last named, and the dummy parameter. We only - want to do this action once. - - Also, indicate when RTL generation is to be suppressed. */ - if (last_named && !varargs_setup) - { - SETUP_INCOMING_VARARGS (args_so_far, passed_mode, passed_type, - current_function_pretend_args_size, - second_time); - varargs_setup = 1; - } -#endif - - /* Determine parm's home in the stack, - in case it arrives in the stack or we should pretend it did. - - Compute the stack position and rtx where the argument arrives - and its size. - - There is one complexity here: If this was a parameter that would - have been passed in registers, but wasn't only because it is - __builtin_va_alist, we want locate_and_pad_parm to treat it as if - it came in a register so that REG_PARM_STACK_SPACE isn't skipped. - In this case, we call FUNCTION_ARG with NAMED set to 1 instead of - 0 as it was the previous time. */ - - locate_and_pad_parm (passed_mode, passed_type, -#ifdef STACK_PARMS_IN_REG_PARM_AREA - 1, -#else -#ifdef FUNCTION_INCOMING_ARG - FUNCTION_INCOMING_ARG (args_so_far, passed_mode, - passed_type, - (! last_named - || varargs_setup)) != 0, -#else - FUNCTION_ARG (args_so_far, passed_mode, - passed_type, - ! last_named || varargs_setup) != 0, -#endif -#endif - fndecl, &stack_args_size, &stack_offset, &arg_size); - - if (! second_time) - { - rtx offset_rtx = ARGS_SIZE_RTX (stack_offset); - - if (offset_rtx == const0_rtx) - stack_parm = gen_rtx (MEM, passed_mode, internal_arg_pointer); - else - stack_parm = gen_rtx (MEM, passed_mode, - gen_rtx (PLUS, Pmode, - internal_arg_pointer, offset_rtx)); - - /* If this is a memory ref that contains aggregate components, - mark it as such for cse and loop optimize. */ - MEM_IN_STRUCT_P (stack_parm) = aggregate; - } - - /* If this parameter was passed both in registers and in the stack, - use the copy on the stack. */ - if (MUST_PASS_IN_STACK (passed_mode, passed_type)) - entry_parm = 0; - -#ifdef FUNCTION_ARG_PARTIAL_NREGS - /* If this parm was passed part in regs and part in memory, - pretend it arrived entirely in memory - by pushing the register-part onto the stack. - - In the special case of a DImode or DFmode that is split, - we could put it together in a pseudoreg directly, - but for now that's not worth bothering with. */ - - if (entry_parm) - { - int nregs = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, passed_mode, - passed_type, ! last_named); - - if (nregs > 0) - { - current_function_pretend_args_size - = (((nregs * UNITS_PER_WORD) + (PARM_BOUNDARY / BITS_PER_UNIT) - 1) - / (PARM_BOUNDARY / BITS_PER_UNIT) - * (PARM_BOUNDARY / BITS_PER_UNIT)); - - if (! second_time) - move_block_from_reg (REGNO (entry_parm), - validize_mem (stack_parm), nregs); - entry_parm = stack_parm; - } - } -#endif - - /* If we didn't decide this parm came in a register, - by default it came on the stack. */ - if (entry_parm == 0) - entry_parm = stack_parm; - - /* Record permanently how this parm was passed. */ - if (! second_time) - DECL_INCOMING_RTL (parm) = entry_parm; - - /* If there is actually space on the stack for this parm, - count it in stack_args_size; otherwise set stack_parm to 0 - to indicate there is no preallocated stack slot for the parm. */ - - if (entry_parm == stack_parm -#if defined (REG_PARM_STACK_SPACE) && ! defined (MAYBE_REG_PARM_STACK_SPACE) - /* On some machines, even if a parm value arrives in a register - there is still an (uninitialized) stack slot allocated for it. - - ??? When MAYBE_REG_PARM_STACK_SPACE is defined, we can't tell - whether this parameter already has a stack slot allocated, - because an arg block exists only if current_function_args_size - is larger than some threshhold, and we haven't calculated that - yet. So, for now, we just assume that stack slots never exist - in this case. */ - || REG_PARM_STACK_SPACE (fndecl) > 0 -#endif - ) - { - stack_args_size.constant += arg_size.constant; - if (arg_size.var) - ADD_PARM_SIZE (stack_args_size, arg_size.var); - } - else - /* No stack slot was pushed for this parm. */ - stack_parm = 0; - - /* Update info on where next arg arrives in registers. */ - - FUNCTION_ARG_ADVANCE (args_so_far, passed_mode, - passed_type, ! last_named); - - /* If this is our second time through, we are done with this parm. */ - if (second_time) - continue; - - /* If we can't trust the parm stack slot to be aligned enough - for its ultimate type, don't use that slot after entry. - We'll make another stack slot, if we need one. */ - { - int thisparm_boundary - = FUNCTION_ARG_BOUNDARY (passed_mode, passed_type); - - if (GET_MODE_ALIGNMENT (nominal_mode) > thisparm_boundary) - stack_parm = 0; - } - - /* Now adjust STACK_PARM to the mode and precise location - where this parameter should live during execution, - if we discover that it must live in the stack during execution. - To make debuggers happier on big-endian machines, we store - the value in the last bytes of the space available. */ - - if (nominal_mode != BLKmode && nominal_mode != passed_mode - && stack_parm != 0) - { - rtx offset_rtx; - -#if BYTES_BIG_ENDIAN - if (GET_MODE_SIZE (nominal_mode) < UNITS_PER_WORD) - stack_offset.constant += (GET_MODE_SIZE (passed_mode) - - GET_MODE_SIZE (nominal_mode)); -#endif - - offset_rtx = ARGS_SIZE_RTX (stack_offset); - if (offset_rtx == const0_rtx) - stack_parm = gen_rtx (MEM, nominal_mode, internal_arg_pointer); - else - stack_parm = gen_rtx (MEM, nominal_mode, - gen_rtx (PLUS, Pmode, - internal_arg_pointer, offset_rtx)); - - /* If this is a memory ref that contains aggregate components, - mark it as such for cse and loop optimize. */ - MEM_IN_STRUCT_P (stack_parm) = aggregate; - } - - /* ENTRY_PARM is an RTX for the parameter as it arrives, - in the mode in which it arrives. - STACK_PARM is an RTX for a stack slot where the parameter can live - during the function (in case we want to put it there). - STACK_PARM is 0 if no stack slot was pushed for it. - - Now output code if necessary to convert ENTRY_PARM to - the type in which this function declares it, - and store that result in an appropriate place, - which may be a pseudo reg, may be STACK_PARM, - or may be a local stack slot if STACK_PARM is 0. - - Set DECL_RTL to that place. */ - - if (nominal_mode == BLKmode) - { - /* If a BLKmode arrives in registers, copy it to a stack slot. */ - if (GET_CODE (entry_parm) == REG) - { - int size_stored = CEIL_ROUND (int_size_in_bytes (TREE_TYPE (parm)), - UNITS_PER_WORD); - - /* Note that we will be storing an integral number of words. - So we have to be careful to ensure that we allocate an - integral number of words. We do this below in the - assign_stack_local if space was not allocated in the argument - list. If it was, this will not work if PARM_BOUNDARY is not - a multiple of BITS_PER_WORD. It isn't clear how to fix this - if it becomes a problem. */ - - if (stack_parm == 0) - { - stack_parm - = assign_stack_local (GET_MODE (entry_parm), size_stored, 0); - /* If this is a memory ref that contains aggregate components, - mark it as such for cse and loop optimize. */ - MEM_IN_STRUCT_P (stack_parm) = aggregate; - } - - else if (PARM_BOUNDARY % BITS_PER_WORD != 0) - abort (); - - move_block_from_reg (REGNO (entry_parm), - validize_mem (stack_parm), - size_stored / UNITS_PER_WORD); - } - DECL_RTL (parm) = stack_parm; - } - else if (! ((obey_regdecls && ! DECL_REGISTER (parm) - && ! DECL_INLINE (fndecl)) - /* layout_decl may set this. */ - || TREE_ADDRESSABLE (parm) - || TREE_SIDE_EFFECTS (parm) - /* If -ffloat-store specified, don't put explicit - float variables into registers. */ - || (flag_float_store - && TREE_CODE (TREE_TYPE (parm)) == REAL_TYPE)) - /* Always assign pseudo to structure return or item passed - by invisible reference. */ - || passed_pointer || parm == function_result_decl) - { - /* Store the parm in a pseudoregister during the function, but we - may need to do it in a wider mode. */ - - register rtx parmreg; - - unsignedp = TREE_UNSIGNED (TREE_TYPE (parm)); - if (TREE_CODE (TREE_TYPE (parm)) == INTEGER_TYPE - || TREE_CODE (TREE_TYPE (parm)) == ENUMERAL_TYPE - || TREE_CODE (TREE_TYPE (parm)) == BOOLEAN_TYPE - || TREE_CODE (TREE_TYPE (parm)) == CHAR_TYPE - || TREE_CODE (TREE_TYPE (parm)) == REAL_TYPE - || TREE_CODE (TREE_TYPE (parm)) == POINTER_TYPE - || TREE_CODE (TREE_TYPE (parm)) == OFFSET_TYPE) - { - PROMOTE_MODE (nominal_mode, unsignedp, TREE_TYPE (parm)); - } - - parmreg = gen_reg_rtx (nominal_mode); - REG_USERVAR_P (parmreg) = 1; - - /* If this was an item that we received a pointer to, set DECL_RTL - appropriately. */ - if (passed_pointer) - { - DECL_RTL (parm) = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (passed_type)), parmreg); - MEM_IN_STRUCT_P (DECL_RTL (parm)) = aggregate; - } - else - DECL_RTL (parm) = parmreg; - - /* Copy the value into the register. */ - if (GET_MODE (parmreg) != GET_MODE (entry_parm)) - { - /* If ENTRY_PARM is a hard register, it might be in a register - not valid for operating in its mode (e.g., an odd-numbered - register for a DFmode). In that case, moves are the only - thing valid, so we can't do a convert from there. This - occurs when the calling sequence allow such misaligned - usages. - - In addition, the conversion may involve a call, which could - clobber parameters which haven't been copied to pseudo - registers yet. Therefore, we must first copy the parm to - a pseudo reg here, and save the conversion until after all - parameters have been moved. */ - - rtx tempreg = gen_reg_rtx (GET_MODE (entry_parm)); - - emit_move_insn (tempreg, validize_mem (entry_parm)); - - push_to_sequence (conversion_insns); - convert_move (parmreg, tempreg, unsignedp); - conversion_insns = get_insns (); - end_sequence (); - } - else - emit_move_insn (parmreg, validize_mem (entry_parm)); - - /* If we were passed a pointer but the actual value - can safely live in a register, put it in one. */ - if (passed_pointer && TYPE_MODE (TREE_TYPE (parm)) != BLKmode - && ! ((obey_regdecls && ! DECL_REGISTER (parm) - && ! DECL_INLINE (fndecl)) - /* layout_decl may set this. */ - || TREE_ADDRESSABLE (parm) - || TREE_SIDE_EFFECTS (parm) - /* If -ffloat-store specified, don't put explicit - float variables into registers. */ - || (flag_float_store - && TREE_CODE (TREE_TYPE (parm)) == REAL_TYPE))) - { - /* We can't use nominal_mode, because it will have been set to - Pmode above. We must use the actual mode of the parm. */ - parmreg = gen_reg_rtx (TYPE_MODE (TREE_TYPE (parm))); - emit_move_insn (parmreg, DECL_RTL (parm)); - DECL_RTL (parm) = parmreg; - } -#ifdef FUNCTION_ARG_CALLEE_COPIES - /* If we are passed an arg by reference and it is our responsibility - to make a copy, do it now. - PASSED_TYPE and PASSED mode now refer to the pointer, not the - original argument, so we must recreate them in the call to - FUNCTION_ARG_CALLEE_COPIES. */ - /* ??? Later add code to handle the case that if the argument isn't - modified, don't do the copy. */ - - else if (passed_pointer - && FUNCTION_ARG_CALLEE_COPIES (args_so_far, - TYPE_MODE (DECL_ARG_TYPE (parm)), - DECL_ARG_TYPE (parm), - ! last_named)) - { - rtx copy; - tree type = DECL_ARG_TYPE (parm); - - /* This sequence may involve a library call perhaps clobbering - registers that haven't been copied to pseudos yet. */ - - push_to_sequence (conversion_insns); - - if (TYPE_SIZE (type) == 0 - || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) - { - /* This is a variable sized object. */ - /* ??? Can we use expr_size here? */ - rtx size_rtx = expand_expr (size_in_bytes (type), NULL_RTX, - TYPE_MODE (sizetype), 0); - - copy = gen_rtx (MEM, BLKmode, - allocate_dynamic_stack_space (size_rtx, NULL_RTX, - TYPE_ALIGN (type))); - } - else - { - int size = int_size_in_bytes (type); - copy = assign_stack_temp (TYPE_MODE (type), size, 1); - } - - store_expr (parm, copy, 0); - emit_move_insn (parmreg, XEXP (copy, 0)); - conversion_insns = get_insns (); - end_sequence (); - } -#endif /* FUNCTION_ARG_CALLEE_COPIES */ - - /* In any case, record the parm's desired stack location - in case we later discover it must live in the stack. */ - if (REGNO (parmreg) >= nparmregs) - { - rtx *new; - nparmregs = REGNO (parmreg) + 5; - new = (rtx *) oballoc (nparmregs * sizeof (rtx)); - bcopy (parm_reg_stack_loc, new, nparmregs * sizeof (rtx)); - parm_reg_stack_loc = new; - } - parm_reg_stack_loc[REGNO (parmreg)] = stack_parm; - - /* Mark the register as eliminable if we did no conversion - and it was copied from memory at a fixed offset, - and the arg pointer was not copied to a pseudo-reg. - If the arg pointer is a pseudo reg or the offset formed - an invalid address, such memory-equivalences - as we make here would screw up life analysis for it. */ - if (nominal_mode == passed_mode - && GET_CODE (entry_parm) == MEM - && entry_parm == stack_parm - && stack_offset.var == 0 - && reg_mentioned_p (virtual_incoming_args_rtx, - XEXP (entry_parm, 0))) - REG_NOTES (get_last_insn ()) - = gen_rtx (EXPR_LIST, REG_EQUIV, - entry_parm, REG_NOTES (get_last_insn ())); - - /* For pointer data type, suggest pointer register. */ - if (TREE_CODE (TREE_TYPE (parm)) == POINTER_TYPE) - mark_reg_pointer (parmreg); - } - else - { - /* Value must be stored in the stack slot STACK_PARM - during function execution. */ - - if (passed_mode != nominal_mode) - { - /* Conversion is required. */ - rtx tempreg = gen_reg_rtx (GET_MODE (entry_parm)); - - emit_move_insn (tempreg, validize_mem (entry_parm)); - - push_to_sequence (conversion_insns); - entry_parm = convert_to_mode (nominal_mode, tempreg, - TREE_UNSIGNED (TREE_TYPE (parm))); - conversion_insns = get_insns (); - end_sequence (); - } - - if (entry_parm != stack_parm) - { - if (stack_parm == 0) - { - stack_parm - = assign_stack_local (GET_MODE (entry_parm), - GET_MODE_SIZE (GET_MODE (entry_parm)), 0); - /* If this is a memory ref that contains aggregate components, - mark it as such for cse and loop optimize. */ - MEM_IN_STRUCT_P (stack_parm) = aggregate; - } - - if (passed_mode != nominal_mode) - { - push_to_sequence (conversion_insns); - emit_move_insn (validize_mem (stack_parm), - validize_mem (entry_parm)); - conversion_insns = get_insns (); - end_sequence (); - } - else - emit_move_insn (validize_mem (stack_parm), - validize_mem (entry_parm)); - } - - DECL_RTL (parm) = stack_parm; - } - - /* If this "parameter" was the place where we are receiving the - function's incoming structure pointer, set up the result. */ - if (parm == function_result_decl) - DECL_RTL (DECL_RESULT (fndecl)) - = gen_rtx (MEM, DECL_MODE (DECL_RESULT (fndecl)), DECL_RTL (parm)); - - if (TREE_THIS_VOLATILE (parm)) - MEM_VOLATILE_P (DECL_RTL (parm)) = 1; - if (TREE_READONLY (parm)) - RTX_UNCHANGING_P (DECL_RTL (parm)) = 1; - } - - /* Output all parameter conversion instructions (possibly including calls) - now that all parameters have been copied out of hard registers. */ - emit_insns (conversion_insns); - - max_parm_reg = max_reg_num (); - last_parm_insn = get_last_insn (); - - current_function_args_size = stack_args_size.constant; - - /* Adjust function incoming argument size for alignment and - minimum length. */ - -#ifdef REG_PARM_STACK_SPACE -#ifndef MAYBE_REG_PARM_STACK_SPACE - current_function_args_size = MAX (current_function_args_size, - REG_PARM_STACK_SPACE (fndecl)); -#endif -#endif - -#ifdef STACK_BOUNDARY -#define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT) - - current_function_args_size - = ((current_function_args_size + STACK_BYTES - 1) - / STACK_BYTES) * STACK_BYTES; -#endif - -#ifdef ARGS_GROW_DOWNWARD - current_function_arg_offset_rtx - = (stack_args_size.var == 0 ? GEN_INT (-stack_args_size.constant) - : expand_expr (size_binop (MINUS_EXPR, stack_args_size.var, - size_int (-stack_args_size.constant)), - NULL_RTX, VOIDmode, 0)); -#else - current_function_arg_offset_rtx = ARGS_SIZE_RTX (stack_args_size); -#endif - - /* See how many bytes, if any, of its args a function should try to pop - on return. */ - - current_function_pops_args = RETURN_POPS_ARGS (TREE_TYPE (fndecl), - current_function_args_size); - - /* For stdarg.h function, save info about regs and stack space - used by the named args. */ - - if (stdarg) - current_function_args_info = args_so_far; - - /* Set the rtx used for the function return value. Put this in its - own variable so any optimizers that need this information don't have - to include tree.h. Do this here so it gets done when an inlined - function gets output. */ - - current_function_return_rtx = DECL_RTL (DECL_RESULT (fndecl)); -} - -/* Indicate whether REGNO is an incoming argument to the current function - that was promoted to a wider mode. If so, return the RTX for the - register (to get its mode). PMODE and PUNSIGNEDP are set to the mode - that REGNO is promoted from and whether the promotion was signed or - unsigned. */ - -#ifdef PROMOTE_FUNCTION_ARGS - -rtx -promoted_input_arg (regno, pmode, punsignedp) - int regno; - enum machine_mode *pmode; - int *punsignedp; -{ - tree arg; - - for (arg = DECL_ARGUMENTS (current_function_decl); arg; - arg = TREE_CHAIN (arg)) - if (GET_CODE (DECL_INCOMING_RTL (arg)) == REG - && REGNO (DECL_INCOMING_RTL (arg)) == regno - && (TREE_CODE (TREE_TYPE (arg)) == INTEGER_TYPE - || TREE_CODE (TREE_TYPE (arg)) == ENUMERAL_TYPE - || TREE_CODE (TREE_TYPE (arg)) == BOOLEAN_TYPE - || TREE_CODE (TREE_TYPE (arg)) == CHAR_TYPE - || TREE_CODE (TREE_TYPE (arg)) == REAL_TYPE - || TREE_CODE (TREE_TYPE (arg)) == POINTER_TYPE - || TREE_CODE (TREE_TYPE (arg)) == OFFSET_TYPE)) - { - enum machine_mode mode = TYPE_MODE (TREE_TYPE (arg)); - int unsignedp = TREE_UNSIGNED (TREE_TYPE (arg)); - - PROMOTE_MODE (mode, unsignedp, TREE_TYPE (arg)); - if (mode == GET_MODE (DECL_INCOMING_RTL (arg)) - && mode != DECL_MODE (arg)) - { - *pmode = DECL_MODE (arg); - *punsignedp = unsignedp; - return DECL_INCOMING_RTL (arg); - } - } - - return 0; -} - -#endif - -/* Compute the size and offset from the start of the stacked arguments for a - parm passed in mode PASSED_MODE and with type TYPE. - - INITIAL_OFFSET_PTR points to the current offset into the stacked - arguments. - - The starting offset and size for this parm are returned in *OFFSET_PTR - and *ARG_SIZE_PTR, respectively. - - IN_REGS is non-zero if the argument will be passed in registers. It will - never be set if REG_PARM_STACK_SPACE is not defined. - - FNDECL is the function in which the argument was defined. - - There are two types of rounding that are done. The first, controlled by - FUNCTION_ARG_BOUNDARY, forces the offset from the start of the argument - list to be aligned to the specific boundary (in bits). This rounding - affects the initial and starting offsets, but not the argument size. - - The second, controlled by FUNCTION_ARG_PADDING and PARM_BOUNDARY, - optionally rounds the size of the parm to PARM_BOUNDARY. The - initial offset is not affected by this rounding, while the size always - is and the starting offset may be. */ - -/* offset_ptr will be negative for ARGS_GROW_DOWNWARD case; - initial_offset_ptr is positive because locate_and_pad_parm's - callers pass in the total size of args so far as - initial_offset_ptr. arg_size_ptr is always positive.*/ - -static void pad_to_arg_alignment (), pad_below (); - -void -locate_and_pad_parm (passed_mode, type, in_regs, fndecl, - initial_offset_ptr, offset_ptr, arg_size_ptr) - enum machine_mode passed_mode; - tree type; - int in_regs; - tree fndecl; - struct args_size *initial_offset_ptr; - struct args_size *offset_ptr; - struct args_size *arg_size_ptr; -{ - tree sizetree - = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode)); - enum direction where_pad = FUNCTION_ARG_PADDING (passed_mode, type); - int boundary = FUNCTION_ARG_BOUNDARY (passed_mode, type); - int boundary_in_bytes = boundary / BITS_PER_UNIT; - int reg_parm_stack_space = 0; - -#ifdef REG_PARM_STACK_SPACE - /* If we have found a stack parm before we reach the end of the - area reserved for registers, skip that area. */ - if (! in_regs) - { -#ifdef MAYBE_REG_PARM_STACK_SPACE - reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE; -#else - reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl); -#endif - if (reg_parm_stack_space > 0) - { - if (initial_offset_ptr->var) - { - initial_offset_ptr->var - = size_binop (MAX_EXPR, ARGS_SIZE_TREE (*initial_offset_ptr), - size_int (reg_parm_stack_space)); - initial_offset_ptr->constant = 0; - } - else if (initial_offset_ptr->constant < reg_parm_stack_space) - initial_offset_ptr->constant = reg_parm_stack_space; - } - } -#endif /* REG_PARM_STACK_SPACE */ - - arg_size_ptr->var = 0; - arg_size_ptr->constant = 0; - -#ifdef ARGS_GROW_DOWNWARD - if (initial_offset_ptr->var) - { - offset_ptr->constant = 0; - offset_ptr->var = size_binop (MINUS_EXPR, integer_zero_node, - initial_offset_ptr->var); - } - else - { - offset_ptr->constant = - initial_offset_ptr->constant; - offset_ptr->var = 0; - } - if (where_pad == upward - && (TREE_CODE (sizetree) != INTEGER_CST - || ((TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY))) - sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT); - SUB_PARM_SIZE (*offset_ptr, sizetree); - if (where_pad != downward) - pad_to_arg_alignment (offset_ptr, boundary); - if (initial_offset_ptr->var) - { - arg_size_ptr->var = size_binop (MINUS_EXPR, - size_binop (MINUS_EXPR, - integer_zero_node, - initial_offset_ptr->var), - offset_ptr->var); - } - else - { - arg_size_ptr->constant = (- initial_offset_ptr->constant - - offset_ptr->constant); - } -/* ADD_PARM_SIZE (*arg_size_ptr, sizetree); */ - if (where_pad == downward) - pad_below (arg_size_ptr, passed_mode, sizetree); -#else /* !ARGS_GROW_DOWNWARD */ - pad_to_arg_alignment (initial_offset_ptr, boundary); - *offset_ptr = *initial_offset_ptr; - if (where_pad == downward) - pad_below (offset_ptr, passed_mode, sizetree); - -#ifdef PUSH_ROUNDING - if (passed_mode != BLKmode) - sizetree = size_int (PUSH_ROUNDING (TREE_INT_CST_LOW (sizetree))); -#endif - - if (where_pad != none - && (TREE_CODE (sizetree) != INTEGER_CST - || ((TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY))) - sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT); - - ADD_PARM_SIZE (*arg_size_ptr, sizetree); -#endif /* ARGS_GROW_DOWNWARD */ -} - -/* Round the stack offset in *OFFSET_PTR up to a multiple of BOUNDARY. - BOUNDARY is measured in bits, but must be a multiple of a storage unit. */ - -static void -pad_to_arg_alignment (offset_ptr, boundary) - struct args_size *offset_ptr; - int boundary; -{ - int boundary_in_bytes = boundary / BITS_PER_UNIT; - - if (boundary > BITS_PER_UNIT) - { - if (offset_ptr->var) - { - offset_ptr->var = -#ifdef ARGS_GROW_DOWNWARD - round_down -#else - round_up -#endif - (ARGS_SIZE_TREE (*offset_ptr), - boundary / BITS_PER_UNIT); - offset_ptr->constant = 0; /*?*/ - } - else - offset_ptr->constant = -#ifdef ARGS_GROW_DOWNWARD - FLOOR_ROUND (offset_ptr->constant, boundary_in_bytes); -#else - CEIL_ROUND (offset_ptr->constant, boundary_in_bytes); -#endif - } -} - -static void -pad_below (offset_ptr, passed_mode, sizetree) - struct args_size *offset_ptr; - enum machine_mode passed_mode; - tree sizetree; -{ - if (passed_mode != BLKmode) - { - if (GET_MODE_BITSIZE (passed_mode) % PARM_BOUNDARY) - offset_ptr->constant - += (((GET_MODE_BITSIZE (passed_mode) + PARM_BOUNDARY - 1) - / PARM_BOUNDARY * PARM_BOUNDARY / BITS_PER_UNIT) - - GET_MODE_SIZE (passed_mode)); - } - else - { - if (TREE_CODE (sizetree) != INTEGER_CST - || (TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY) - { - /* Round the size up to multiple of PARM_BOUNDARY bits. */ - tree s2 = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT); - /* Add it in. */ - ADD_PARM_SIZE (*offset_ptr, s2); - SUB_PARM_SIZE (*offset_ptr, sizetree); - } - } -} - -static tree -round_down (value, divisor) - tree value; - int divisor; -{ - return size_binop (MULT_EXPR, - size_binop (FLOOR_DIV_EXPR, value, size_int (divisor)), - size_int (divisor)); -} - -/* Walk the tree of blocks describing the binding levels within a function - and warn about uninitialized variables. - This is done after calling flow_analysis and before global_alloc - clobbers the pseudo-regs to hard regs. */ - -void -uninitialized_vars_warning (block) - tree block; -{ - register tree decl, sub; - for (decl = BLOCK_VARS (block); decl; decl = TREE_CHAIN (decl)) - { - if (TREE_CODE (decl) == VAR_DECL - /* These warnings are unreliable for and aggregates - because assigning the fields one by one can fail to convince - flow.c that the entire aggregate was initialized. - Unions are troublesome because members may be shorter. */ - && TREE_CODE (TREE_TYPE (decl)) != RECORD_TYPE - && TREE_CODE (TREE_TYPE (decl)) != UNION_TYPE - && TREE_CODE (TREE_TYPE (decl)) != QUAL_UNION_TYPE - && TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE - && DECL_RTL (decl) != 0 - && GET_CODE (DECL_RTL (decl)) == REG - && regno_uninitialized (REGNO (DECL_RTL (decl)))) - warning_with_decl (decl, - "`%s' may be used uninitialized in this function"); - if (TREE_CODE (decl) == VAR_DECL - && DECL_RTL (decl) != 0 - && GET_CODE (DECL_RTL (decl)) == REG - && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl)))) - warning_with_decl (decl, - "variable `%s' may be clobbered by `longjmp'"); - } - for (sub = BLOCK_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub)) - uninitialized_vars_warning (sub); -} - -/* Do the appropriate part of uninitialized_vars_warning - but for arguments instead of local variables. */ - -void -setjmp_args_warning (block) - tree block; -{ - register tree decl; - for (decl = DECL_ARGUMENTS (current_function_decl); - decl; decl = TREE_CHAIN (decl)) - if (DECL_RTL (decl) != 0 - && GET_CODE (DECL_RTL (decl)) == REG - && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl)))) - warning_with_decl (decl, "argument `%s' may be clobbered by `longjmp'"); -} - -/* If this function call setjmp, put all vars into the stack - unless they were declared `register'. */ - -void -setjmp_protect (block) - tree block; -{ - register tree decl, sub; - for (decl = BLOCK_VARS (block); decl; decl = TREE_CHAIN (decl)) - if ((TREE_CODE (decl) == VAR_DECL - || TREE_CODE (decl) == PARM_DECL) - && DECL_RTL (decl) != 0 - && GET_CODE (DECL_RTL (decl)) == REG - /* If this variable came from an inline function, it must be - that it's life doesn't overlap the setjmp. If there was a - setjmp in the function, it would already be in memory. We - must exclude such variable because their DECL_RTL might be - set to strange things such as virtual_stack_vars_rtx. */ - && ! DECL_FROM_INLINE (decl) - && ( -#ifdef NON_SAVING_SETJMP - /* If longjmp doesn't restore the registers, - don't put anything in them. */ - NON_SAVING_SETJMP - || -#endif - ! DECL_REGISTER (decl))) - put_var_into_stack (decl); - for (sub = BLOCK_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub)) - setjmp_protect (sub); -} - -/* Like the previous function, but for args instead of local variables. */ - -void -setjmp_protect_args () -{ - register tree decl, sub; - for (decl = DECL_ARGUMENTS (current_function_decl); - decl; decl = TREE_CHAIN (decl)) - if ((TREE_CODE (decl) == VAR_DECL - || TREE_CODE (decl) == PARM_DECL) - && DECL_RTL (decl) != 0 - && GET_CODE (DECL_RTL (decl)) == REG - && ( - /* If longjmp doesn't restore the registers, - don't put anything in them. */ -#ifdef NON_SAVING_SETJMP - NON_SAVING_SETJMP - || -#endif - ! DECL_REGISTER (decl))) - put_var_into_stack (decl); -} - -/* Return the context-pointer register corresponding to DECL, - or 0 if it does not need one. */ - -rtx -lookup_static_chain (decl) - tree decl; -{ - tree context = decl_function_context (decl); - tree link; - - if (context == 0) - return 0; - - /* We treat inline_function_decl as an alias for the current function - because that is the inline function whose vars, types, etc. - are being merged into the current function. - See expand_inline_function. */ - if (context == current_function_decl || context == inline_function_decl) - return virtual_stack_vars_rtx; - - for (link = context_display; link; link = TREE_CHAIN (link)) - if (TREE_PURPOSE (link) == context) - return RTL_EXPR_RTL (TREE_VALUE (link)); - - abort (); -} - -/* Convert a stack slot address ADDR for variable VAR - (from a containing function) - into an address valid in this function (using a static chain). */ - -rtx -fix_lexical_addr (addr, var) - rtx addr; - tree var; -{ - rtx basereg; - int displacement; - tree context = decl_function_context (var); - struct function *fp; - rtx base = 0; - - /* If this is the present function, we need not do anything. */ - if (context == current_function_decl || context == inline_function_decl) - return addr; - - for (fp = outer_function_chain; fp; fp = fp->next) - if (fp->decl == context) - break; - - if (fp == 0) - abort (); - - /* Decode given address as base reg plus displacement. */ - if (GET_CODE (addr) == REG) - basereg = addr, displacement = 0; - else if (GET_CODE (addr) == PLUS && GET_CODE (XEXP (addr, 1)) == CONST_INT) - basereg = XEXP (addr, 0), displacement = INTVAL (XEXP (addr, 1)); - else - abort (); - - /* We accept vars reached via the containing function's - incoming arg pointer and via its stack variables pointer. */ - if (basereg == fp->internal_arg_pointer) - { - /* If reached via arg pointer, get the arg pointer value - out of that function's stack frame. - - There are two cases: If a separate ap is needed, allocate a - slot in the outer function for it and dereference it that way. - This is correct even if the real ap is actually a pseudo. - Otherwise, just adjust the offset from the frame pointer to - compensate. */ - -#ifdef NEED_SEPARATE_AP - rtx addr; - - if (fp->arg_pointer_save_area == 0) - fp->arg_pointer_save_area - = assign_outer_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0, fp); - - addr = fix_lexical_addr (XEXP (fp->arg_pointer_save_area, 0), var); - addr = memory_address (Pmode, addr); - - base = copy_to_reg (gen_rtx (MEM, Pmode, addr)); -#else - displacement += (FIRST_PARM_OFFSET (context) - STARTING_FRAME_OFFSET); - base = lookup_static_chain (var); -#endif - } - - else if (basereg == virtual_stack_vars_rtx) - { - /* This is the same code as lookup_static_chain, duplicated here to - avoid an extra call to decl_function_context. */ - tree link; - - for (link = context_display; link; link = TREE_CHAIN (link)) - if (TREE_PURPOSE (link) == context) - { - base = RTL_EXPR_RTL (TREE_VALUE (link)); - break; - } - } - - if (base == 0) - abort (); - - /* Use same offset, relative to appropriate static chain or argument - pointer. */ - return plus_constant (base, displacement); -} - -/* Return the address of the trampoline for entering nested fn FUNCTION. - If necessary, allocate a trampoline (in the stack frame) - and emit rtl to initialize its contents (at entry to this function). */ - -rtx -trampoline_address (function) - tree function; -{ - tree link; - tree rtlexp; - rtx tramp; - struct function *fp; - tree fn_context; - - /* Find an existing trampoline and return it. */ - for (link = trampoline_list; link; link = TREE_CHAIN (link)) - if (TREE_PURPOSE (link) == function) - return XEXP (RTL_EXPR_RTL (TREE_VALUE (link)), 0); - for (fp = outer_function_chain; fp; fp = fp->next) - for (link = fp->trampoline_list; link; link = TREE_CHAIN (link)) - if (TREE_PURPOSE (link) == function) - { - tramp = fix_lexical_addr (XEXP (RTL_EXPR_RTL (TREE_VALUE (link)), 0), - function); - return round_trampoline_addr (tramp); - } - - /* None exists; we must make one. */ - - /* Find the `struct function' for the function containing FUNCTION. */ - fp = 0; - fn_context = decl_function_context (function); - if (fn_context != current_function_decl) - for (fp = outer_function_chain; fp; fp = fp->next) - if (fp->decl == fn_context) - break; - - /* Allocate run-time space for this trampoline - (usually in the defining function's stack frame). */ -#ifdef ALLOCATE_TRAMPOLINE - tramp = ALLOCATE_TRAMPOLINE (fp); -#else - /* If rounding needed, allocate extra space - to ensure we have TRAMPOLINE_SIZE bytes left after rounding up. */ -#ifdef TRAMPOLINE_ALIGNMENT -#define TRAMPOLINE_REAL_SIZE (TRAMPOLINE_SIZE + TRAMPOLINE_ALIGNMENT - 1) -#else -#define TRAMPOLINE_REAL_SIZE (TRAMPOLINE_SIZE) -#endif - if (fp != 0) - tramp = assign_outer_stack_local (BLKmode, TRAMPOLINE_REAL_SIZE, 0, fp); - else - tramp = assign_stack_local (BLKmode, TRAMPOLINE_REAL_SIZE, 0); -#endif - - /* Record the trampoline for reuse and note it for later initialization - by expand_function_end. */ - if (fp != 0) - { - push_obstacks (fp->current_obstack, fp->function_maybepermanent_obstack); - rtlexp = make_node (RTL_EXPR); - RTL_EXPR_RTL (rtlexp) = tramp; - fp->trampoline_list = tree_cons (function, rtlexp, fp->trampoline_list); - pop_obstacks (); - } - else - { - /* Make the RTL_EXPR node temporary, not momentary, so that the - trampoline_list doesn't become garbage. */ - int momentary = suspend_momentary (); - rtlexp = make_node (RTL_EXPR); - resume_momentary (momentary); - - RTL_EXPR_RTL (rtlexp) = tramp; - trampoline_list = tree_cons (function, rtlexp, trampoline_list); - } - - tramp = fix_lexical_addr (XEXP (tramp, 0), function); - return round_trampoline_addr (tramp); -} - -/* Given a trampoline address, - round it to multiple of TRAMPOLINE_ALIGNMENT. */ - -static rtx -round_trampoline_addr (tramp) - rtx tramp; -{ -#ifdef TRAMPOLINE_ALIGNMENT - /* Round address up to desired boundary. */ - rtx temp = gen_reg_rtx (Pmode); - temp = expand_binop (Pmode, add_optab, tramp, - GEN_INT (TRAMPOLINE_ALIGNMENT - 1), - temp, 0, OPTAB_LIB_WIDEN); - tramp = expand_binop (Pmode, and_optab, temp, - GEN_INT (- TRAMPOLINE_ALIGNMENT), - temp, 0, OPTAB_LIB_WIDEN); -#endif - return tramp; -} - -/* The functions identify_blocks and reorder_blocks provide a way to - reorder the tree of BLOCK nodes, for optimizers that reshuffle or - duplicate portions of the RTL code. Call identify_blocks before - changing the RTL, and call reorder_blocks after. */ - -static int all_blocks (); -static tree blocks_nreverse (); - -/* Put all this function's BLOCK nodes into a vector, and return it. - Also store in each NOTE for the beginning or end of a block - the index of that block in the vector. - The arguments are TOP_BLOCK, the top-level block of the function, - and INSNS, the insn chain of the function. */ - -tree * -identify_blocks (top_block, insns) - tree top_block; - rtx insns; -{ - int n_blocks; - tree *block_vector; - int *block_stack; - int depth = 0; - int next_block_number = 0; - int current_block_number = 0; - rtx insn; - - if (top_block == 0) - return 0; - - n_blocks = all_blocks (top_block, 0); - block_vector = (tree *) xmalloc (n_blocks * sizeof (tree)); - block_stack = (int *) alloca (n_blocks * sizeof (int)); - - all_blocks (top_block, block_vector); - - for (insn = insns; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == NOTE) - { - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG) - { - block_stack[depth++] = current_block_number; - current_block_number = next_block_number; - NOTE_BLOCK_NUMBER (insn) = next_block_number++; - } - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END) - { - current_block_number = block_stack[--depth]; - NOTE_BLOCK_NUMBER (insn) = current_block_number; - } - } - - return block_vector; -} - -/* Given BLOCK_VECTOR which was returned by identify_blocks, - and a revised instruction chain, rebuild the tree structure - of BLOCK nodes to correspond to the new order of RTL. - The new block tree is inserted below TOP_BLOCK. - Returns the current top-level block. */ - -tree -reorder_blocks (block_vector, top_block, insns) - tree *block_vector; - tree top_block; - rtx insns; -{ - tree current_block = top_block; - rtx insn; - - if (block_vector == 0) - return top_block; - - /* Prune the old tree away, so that it doesn't get in the way. */ - BLOCK_SUBBLOCKS (current_block) = 0; - - for (insn = insns; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == NOTE) - { - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG) - { - tree block = block_vector[NOTE_BLOCK_NUMBER (insn)]; - /* If we have seen this block before, copy it. */ - if (TREE_ASM_WRITTEN (block)) - block = copy_node (block); - BLOCK_SUBBLOCKS (block) = 0; - TREE_ASM_WRITTEN (block) = 1; - BLOCK_SUPERCONTEXT (block) = current_block; - BLOCK_CHAIN (block) = BLOCK_SUBBLOCKS (current_block); - BLOCK_SUBBLOCKS (current_block) = block; - current_block = block; - NOTE_SOURCE_FILE (insn) = 0; - } - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END) - { - BLOCK_SUBBLOCKS (current_block) - = blocks_nreverse (BLOCK_SUBBLOCKS (current_block)); - current_block = BLOCK_SUPERCONTEXT (current_block); - NOTE_SOURCE_FILE (insn) = 0; - } - } - - return current_block; -} - -/* Reverse the order of elements in the chain T of blocks, - and return the new head of the chain (old last element). */ - -static tree -blocks_nreverse (t) - tree t; -{ - register tree prev = 0, decl, next; - for (decl = t; decl; decl = next) - { - next = BLOCK_CHAIN (decl); - BLOCK_CHAIN (decl) = prev; - prev = decl; - } - return prev; -} - -/* Count the subblocks of BLOCK, and list them all into the vector VECTOR. - Also clear TREE_ASM_WRITTEN in all blocks. */ - -static int -all_blocks (block, vector) - tree block; - tree *vector; -{ - int n_blocks = 1; - tree subblocks; - - TREE_ASM_WRITTEN (block) = 0; - /* Record this block. */ - if (vector) - vector[0] = block; - - /* Record the subblocks, and their subblocks. */ - for (subblocks = BLOCK_SUBBLOCKS (block); - subblocks; subblocks = BLOCK_CHAIN (subblocks)) - n_blocks += all_blocks (subblocks, vector ? vector + n_blocks : 0); - - return n_blocks; -} - -/* Generate RTL for the start of the function SUBR (a FUNCTION_DECL tree node) - and initialize static variables for generating RTL for the statements - of the function. */ - -void -init_function_start (subr, filename, line) - tree subr; - char *filename; - int line; -{ - char *junk; - - init_stmt_for_function (); - - cse_not_expected = ! optimize; - - /* Caller save not needed yet. */ - caller_save_needed = 0; - - /* No stack slots have been made yet. */ - stack_slot_list = 0; - - /* There is no stack slot for handling nonlocal gotos. */ - nonlocal_goto_handler_slot = 0; - nonlocal_goto_stack_level = 0; - - /* No labels have been declared for nonlocal use. */ - nonlocal_labels = 0; - - /* No function calls so far in this function. */ - function_call_count = 0; - - /* No parm regs have been allocated. - (This is important for output_inline_function.) */ - max_parm_reg = LAST_VIRTUAL_REGISTER + 1; - - /* Initialize the RTL mechanism. */ - init_emit (); - - /* Initialize the queue of pending postincrement and postdecrements, - and some other info in expr.c. */ - init_expr (); - - /* We haven't done register allocation yet. */ - reg_renumber = 0; - - init_const_rtx_hash_table (); - - current_function_name = (*decl_printable_name) (subr, &junk); - - /* Nonzero if this is a nested function that uses a static chain. */ - - current_function_needs_context - = (decl_function_context (current_function_decl) != 0); - - /* Set if a call to setjmp is seen. */ - current_function_calls_setjmp = 0; - - /* Set if a call to longjmp is seen. */ - current_function_calls_longjmp = 0; - - current_function_calls_alloca = 0; - current_function_has_nonlocal_label = 0; - current_function_contains_functions = 0; - - current_function_returns_pcc_struct = 0; - current_function_returns_struct = 0; - current_function_epilogue_delay_list = 0; - current_function_uses_const_pool = 0; - current_function_uses_pic_offset_table = 0; - - /* We have not yet needed to make a label to jump to for tail-recursion. */ - tail_recursion_label = 0; - - /* We haven't had a need to make a save area for ap yet. */ - - arg_pointer_save_area = 0; - - /* No stack slots allocated yet. */ - frame_offset = 0; - - /* No SAVE_EXPRs in this function yet. */ - save_expr_regs = 0; - - /* No RTL_EXPRs in this function yet. */ - rtl_expr_chain = 0; - - /* We have not allocated any temporaries yet. */ - temp_slots = 0; - temp_slot_level = 0; - - /* Within function body, compute a type's size as soon it is laid out. */ - immediate_size_expand++; - - init_pending_stack_adjust (); - inhibit_defer_pop = 0; - - current_function_outgoing_args_size = 0; - - /* Initialize the insn lengths. */ - init_insn_lengths (); - - /* Prevent ever trying to delete the first instruction of a function. - Also tell final how to output a linenum before the function prologue. */ - emit_line_note (filename, line); - - /* Make sure first insn is a note even if we don't want linenums. - This makes sure the first insn will never be deleted. - Also, final expects a note to appear there. */ - emit_note (NULL_PTR, NOTE_INSN_DELETED); - - /* Set flags used by final.c. */ - if (aggregate_value_p (DECL_RESULT (subr))) - { -#ifdef PCC_STATIC_STRUCT_RETURN - current_function_returns_pcc_struct = 1; -#endif - current_function_returns_struct = 1; - } - - /* Warn if this value is an aggregate type, - regardless of which calling convention we are using for it. */ - if (warn_aggregate_return - && (TREE_CODE (TREE_TYPE (DECL_RESULT (subr))) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (DECL_RESULT (subr))) == UNION_TYPE - || TREE_CODE (TREE_TYPE (DECL_RESULT (subr))) == QUAL_UNION_TYPE - || TREE_CODE (TREE_TYPE (DECL_RESULT (subr))) == ARRAY_TYPE)) - warning ("function returns an aggregate"); - - current_function_returns_pointer - = (TREE_CODE (TREE_TYPE (DECL_RESULT (subr))) == POINTER_TYPE); - - /* Indicate that we need to distinguish between the return value of the - present function and the return value of a function being called. */ - rtx_equal_function_value_matters = 1; - - /* Indicate that we have not instantiated virtual registers yet. */ - virtuals_instantiated = 0; - - /* Indicate we have no need of a frame pointer yet. */ - frame_pointer_needed = 0; - - /* By default assume not varargs. */ - current_function_varargs = 0; -} - -/* Indicate that the current function uses extra args - not explicitly mentioned in the argument list in any fashion. */ - -void -mark_varargs () -{ - current_function_varargs = 1; -} - -/* Expand a call to __main at the beginning of a possible main function. */ - -void -expand_main_function () -{ -#if !defined (INIT_SECTION_ASM_OP) || defined (INVOKE__main) - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__main"), 0, - VOIDmode, 0); -#endif /* not INIT_SECTION_ASM_OP or INVOKE__main */ -} - -/* Start the RTL for a new function, and set variables used for - emitting RTL. - SUBR is the FUNCTION_DECL node. - PARMS_HAVE_CLEANUPS is nonzero if there are cleanups associated with - the function's parameters, which must be run at any return statement. */ - -void -expand_function_start (subr, parms_have_cleanups) - tree subr; - int parms_have_cleanups; -{ - register int i; - tree tem; - rtx last_ptr; - - /* Make sure volatile mem refs aren't considered - valid operands of arithmetic insns. */ - init_recog_no_volatile (); - - /* If function gets a static chain arg, store it in the stack frame. - Do this first, so it gets the first stack slot offset. */ - if (current_function_needs_context) - { - last_ptr = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0); - emit_move_insn (last_ptr, static_chain_incoming_rtx); - } - - /* If the parameters of this function need cleaning up, get a label - for the beginning of the code which executes those cleanups. This must - be done before doing anything with return_label. */ - if (parms_have_cleanups) - cleanup_label = gen_label_rtx (); - else - cleanup_label = 0; - - /* Make the label for return statements to jump to, if this machine - does not have a one-instruction return and uses an epilogue, - or if it returns a structure, or if it has parm cleanups. */ -#ifdef HAVE_return - if (cleanup_label == 0 && HAVE_return - && ! current_function_returns_pcc_struct - && ! (current_function_returns_struct && ! optimize)) - return_label = 0; - else - return_label = gen_label_rtx (); -#else - return_label = gen_label_rtx (); -#endif - - /* Initialize rtx used to return the value. */ - /* Do this before assign_parms so that we copy the struct value address - before any library calls that assign parms might generate. */ - - /* Decide whether to return the value in memory or in a register. */ - if (aggregate_value_p (DECL_RESULT (subr))) - { - /* Returning something that won't go in a register. */ - register rtx value_address; - -#ifdef PCC_STATIC_STRUCT_RETURN - if (current_function_returns_pcc_struct) - { - int size = int_size_in_bytes (TREE_TYPE (DECL_RESULT (subr))); - value_address = assemble_static_space (size); - } - else -#endif - { - /* Expect to be passed the address of a place to store the value. - If it is passed as an argument, assign_parms will take care of - it. */ - if (struct_value_incoming_rtx) - { - value_address = gen_reg_rtx (Pmode); - emit_move_insn (value_address, struct_value_incoming_rtx); - } - } - if (value_address) - DECL_RTL (DECL_RESULT (subr)) - = gen_rtx (MEM, DECL_MODE (DECL_RESULT (subr)), - value_address); - } - else if (DECL_MODE (DECL_RESULT (subr)) == VOIDmode) - /* If return mode is void, this decl rtl should not be used. */ - DECL_RTL (DECL_RESULT (subr)) = 0; - else if (parms_have_cleanups) - { - /* If function will end with cleanup code for parms, - compute the return values into a pseudo reg, - which we will copy into the true return register - after the cleanups are done. */ - - enum machine_mode mode = DECL_MODE (DECL_RESULT (subr)); -#ifdef PROMOTE_FUNCTION_RETURN - tree type = TREE_TYPE (DECL_RESULT (subr)); - int unsignedp = TREE_UNSIGNED (type); - - if (TREE_CODE (type) == INTEGER_TYPE || TREE_CODE (type) == ENUMERAL_TYPE - || TREE_CODE (type) == BOOLEAN_TYPE || TREE_CODE (type) == CHAR_TYPE - || TREE_CODE (type) == REAL_TYPE || TREE_CODE (type) == POINTER_TYPE - || TREE_CODE (type) == OFFSET_TYPE) - { - PROMOTE_MODE (mode, unsignedp, type); - } -#endif - - DECL_RTL (DECL_RESULT (subr)) = gen_reg_rtx (mode); - } - else - /* Scalar, returned in a register. */ - { -#ifdef FUNCTION_OUTGOING_VALUE - DECL_RTL (DECL_RESULT (subr)) - = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (subr)), subr); -#else - DECL_RTL (DECL_RESULT (subr)) - = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (subr)), subr); -#endif - - /* Mark this reg as the function's return value. */ - if (GET_CODE (DECL_RTL (DECL_RESULT (subr))) == REG) - { - REG_FUNCTION_VALUE_P (DECL_RTL (DECL_RESULT (subr))) = 1; - /* Needed because we may need to move this to memory - in case it's a named return value whose address is taken. */ - DECL_REGISTER (DECL_RESULT (subr)) = 1; - } - } - - /* Initialize rtx for parameters and local variables. - In some cases this requires emitting insns. */ - - assign_parms (subr, 0); - - /* The following was moved from init_function_start. - The move is supposed to make sdb output more accurate. */ - /* Indicate the beginning of the function body, - as opposed to parm setup. */ - emit_note (NULL_PTR, NOTE_INSN_FUNCTION_BEG); - - /* If doing stupid allocation, mark parms as born here. */ - - if (GET_CODE (get_last_insn ()) != NOTE) - emit_note (NULL_PTR, NOTE_INSN_DELETED); - parm_birth_insn = get_last_insn (); - - if (obey_regdecls) - { - for (i = LAST_VIRTUAL_REGISTER + 1; i < max_parm_reg; i++) - use_variable (regno_reg_rtx[i]); - - if (current_function_internal_arg_pointer != virtual_incoming_args_rtx) - use_variable (current_function_internal_arg_pointer); - } - - /* Fetch static chain values for containing functions. */ - tem = decl_function_context (current_function_decl); - /* If not doing stupid register allocation, then start off with the static - chain pointer in a pseudo register. Otherwise, we use the stack - address that was generated above. */ - if (tem && ! obey_regdecls) - last_ptr = copy_to_reg (static_chain_incoming_rtx); - context_display = 0; - while (tem) - { - tree rtlexp = make_node (RTL_EXPR); - - RTL_EXPR_RTL (rtlexp) = last_ptr; - context_display = tree_cons (tem, rtlexp, context_display); - tem = decl_function_context (tem); - if (tem == 0) - break; - /* Chain thru stack frames, assuming pointer to next lexical frame - is found at the place we always store it. */ -#ifdef FRAME_GROWS_DOWNWARD - last_ptr = plus_constant (last_ptr, - GET_MODE_SIZE (Pmode)); -#endif - last_ptr = copy_to_reg (gen_rtx (MEM, Pmode, - memory_address (Pmode, last_ptr))); - } - - /* After the display initializations is where the tail-recursion label - should go, if we end up needing one. Ensure we have a NOTE here - since some things (like trampolines) get placed before this. */ - tail_recursion_reentry = emit_note (NULL_PTR, NOTE_INSN_DELETED); - - /* Evaluate now the sizes of any types declared among the arguments. */ - for (tem = nreverse (get_pending_sizes ()); tem; tem = TREE_CHAIN (tem)) - expand_expr (TREE_VALUE (tem), const0_rtx, VOIDmode, 0); - - /* Make sure there is a line number after the function entry setup code. */ - force_next_line_note (); -} - -/* Generate RTL for the end of the current function. - FILENAME and LINE are the current position in the source file. */ - -/* It is up to language-specific callers to do cleanups for parameters. */ - -void -expand_function_end (filename, line) - char *filename; - int line; -{ - register int i; - tree link; - - static rtx initial_trampoline; - -#ifdef NON_SAVING_SETJMP - /* Don't put any variables in registers if we call setjmp - on a machine that fails to restore the registers. */ - if (NON_SAVING_SETJMP && current_function_calls_setjmp) - { - setjmp_protect (DECL_INITIAL (current_function_decl)); - setjmp_protect_args (); - } -#endif - - /* Save the argument pointer if a save area was made for it. */ - if (arg_pointer_save_area) - { - rtx x = gen_move_insn (arg_pointer_save_area, virtual_incoming_args_rtx); - emit_insn_before (x, tail_recursion_reentry); - } - - /* Initialize any trampolines required by this function. */ - for (link = trampoline_list; link; link = TREE_CHAIN (link)) - { - tree function = TREE_PURPOSE (link); - rtx context = lookup_static_chain (function); - rtx tramp = RTL_EXPR_RTL (TREE_VALUE (link)); - rtx seq; - - /* First make sure this compilation has a template for - initializing trampolines. */ - if (initial_trampoline == 0) - { - end_temporary_allocation (); - initial_trampoline - = gen_rtx (MEM, BLKmode, assemble_trampoline_template ()); - resume_temporary_allocation (); - } - - /* Generate insns to initialize the trampoline. */ - start_sequence (); - tramp = change_address (initial_trampoline, BLKmode, - round_trampoline_addr (XEXP (tramp, 0))); - emit_block_move (tramp, initial_trampoline, GEN_INT (TRAMPOLINE_SIZE), - FUNCTION_BOUNDARY / BITS_PER_UNIT); - INITIALIZE_TRAMPOLINE (XEXP (tramp, 0), - XEXP (DECL_RTL (function), 0), context); - seq = get_insns (); - end_sequence (); - - /* Put those insns at entry to the containing function (this one). */ - emit_insns_before (seq, tail_recursion_reentry); - } - /* Clear the trampoline_list for the next function. */ - trampoline_list = 0; - -#if 0 /* I think unused parms are legitimate enough. */ - /* Warn about unused parms. */ - if (warn_unused) - { - rtx decl; - - for (decl = DECL_ARGUMENTS (current_function_decl); - decl; decl = TREE_CHAIN (decl)) - if (! TREE_USED (decl) && TREE_CODE (decl) == VAR_DECL) - warning_with_decl (decl, "unused parameter `%s'"); - } -#endif - - /* Delete handlers for nonlocal gotos if nothing uses them. */ - if (nonlocal_goto_handler_slot != 0 && !current_function_has_nonlocal_label) - delete_handlers (); - - /* End any sequences that failed to be closed due to syntax errors. */ - while (in_sequence_p ()) - end_sequence (); - - /* Outside function body, can't compute type's actual size - until next function's body starts. */ - immediate_size_expand--; - - /* If doing stupid register allocation, - mark register parms as dying here. */ - - if (obey_regdecls) - { - rtx tem; - for (i = LAST_VIRTUAL_REGISTER + 1; i < max_parm_reg; i++) - use_variable (regno_reg_rtx[i]); - - /* Likewise for the regs of all the SAVE_EXPRs in the function. */ - - for (tem = save_expr_regs; tem; tem = XEXP (tem, 1)) - { - use_variable (XEXP (tem, 0)); - use_variable_after (XEXP (tem, 0), parm_birth_insn); - } - - if (current_function_internal_arg_pointer != virtual_incoming_args_rtx) - use_variable (current_function_internal_arg_pointer); - } - - clear_pending_stack_adjust (); - do_pending_stack_adjust (); - - /* Mark the end of the function body. - If control reaches this insn, the function can drop through - without returning a value. */ - emit_note (NULL_PTR, NOTE_INSN_FUNCTION_END); - - /* Output a linenumber for the end of the function. - SDB depends on this. */ - emit_line_note_force (filename, line); - - /* Output the label for the actual return from the function, - if one is expected. This happens either because a function epilogue - is used instead of a return instruction, or because a return was done - with a goto in order to run local cleanups, or because of pcc-style - structure returning. */ - - if (return_label) - emit_label (return_label); - - /* If we had calls to alloca, and this machine needs - an accurate stack pointer to exit the function, - insert some code to save and restore the stack pointer. */ -#ifdef EXIT_IGNORE_STACK - if (! EXIT_IGNORE_STACK) -#endif - if (current_function_calls_alloca) - { - rtx tem = 0; - - emit_stack_save (SAVE_FUNCTION, &tem, parm_birth_insn); - emit_stack_restore (SAVE_FUNCTION, tem, NULL_RTX); - } - - /* If scalar return value was computed in a pseudo-reg, - copy that to the hard return register. */ - if (DECL_RTL (DECL_RESULT (current_function_decl)) != 0 - && GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) == REG - && (REGNO (DECL_RTL (DECL_RESULT (current_function_decl))) - >= FIRST_PSEUDO_REGISTER)) - { - rtx real_decl_result; - -#ifdef FUNCTION_OUTGOING_VALUE - real_decl_result - = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (current_function_decl)), - current_function_decl); -#else - real_decl_result - = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (current_function_decl)), - current_function_decl); -#endif - REG_FUNCTION_VALUE_P (real_decl_result) = 1; - emit_move_insn (real_decl_result, - DECL_RTL (DECL_RESULT (current_function_decl))); - emit_insn (gen_rtx (USE, VOIDmode, real_decl_result)); - } - - /* If returning a structure, arrange to return the address of the value - in a place where debuggers expect to find it. - - If returning a structure PCC style, - the caller also depends on this value. - And current_function_returns_pcc_struct is not necessarily set. */ - if (current_function_returns_struct - || current_function_returns_pcc_struct) - { - rtx value_address = XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0); - tree type = TREE_TYPE (DECL_RESULT (current_function_decl)); -#ifdef FUNCTION_OUTGOING_VALUE - rtx outgoing - = FUNCTION_OUTGOING_VALUE (build_pointer_type (type), - current_function_decl); -#else - rtx outgoing - = FUNCTION_VALUE (build_pointer_type (type), - current_function_decl); -#endif - - /* Mark this as a function return value so integrate will delete the - assignment and USE below when inlining this function. */ - REG_FUNCTION_VALUE_P (outgoing) = 1; - - emit_move_insn (outgoing, value_address); - use_variable (outgoing); - } - - /* Output a return insn if we are using one. - Otherwise, let the rtl chain end here, to drop through - into the epilogue. */ - -#ifdef HAVE_return - if (HAVE_return) - { - emit_jump_insn (gen_return ()); - emit_barrier (); - } -#endif - - /* Fix up any gotos that jumped out to the outermost - binding level of the function. - Must follow emitting RETURN_LABEL. */ - - /* If you have any cleanups to do at this point, - and they need to create temporary variables, - then you will lose. */ - fixup_gotos (NULL_PTR, NULL_RTX, NULL_TREE, get_insns (), 0); -} - -/* These arrays record the INSN_UIDs of the prologue and epilogue insns. */ - -static int *prologue; -static int *epilogue; - -/* Create an array that records the INSN_UIDs of INSNS (either a sequence - or a single insn). */ - -static int * -record_insns (insns) - rtx insns; -{ - int *vec; - - if (GET_CODE (insns) == SEQUENCE) - { - int len = XVECLEN (insns, 0); - vec = (int *) oballoc ((len + 1) * sizeof (int)); - vec[len] = 0; - while (--len >= 0) - vec[len] = INSN_UID (XVECEXP (insns, 0, len)); - } - else - { - vec = (int *) oballoc (2 * sizeof (int)); - vec[0] = INSN_UID (insns); - vec[1] = 0; - } - return vec; -} - -/* Determine how many INSN_UIDs in VEC are part of INSN. */ - -static int -contains (insn, vec) - rtx insn; - int *vec; -{ - register int i, j; - - if (GET_CODE (insn) == INSN - && GET_CODE (PATTERN (insn)) == SEQUENCE) - { - int count = 0; - for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) - for (j = 0; vec[j]; j++) - if (INSN_UID (XVECEXP (PATTERN (insn), 0, i)) == vec[j]) - count++; - return count; - } - else - { - for (j = 0; vec[j]; j++) - if (INSN_UID (insn) == vec[j]) - return 1; - } - return 0; -} - -/* Generate the prologe and epilogue RTL if the machine supports it. Thread - this into place with notes indicating where the prologue ends and where - the epilogue begins. Update the basic block information when possible. */ - -void -thread_prologue_and_epilogue_insns (f) - rtx f; -{ -#ifdef HAVE_prologue - if (HAVE_prologue) - { - rtx head, seq, insn; - - /* The first insn (a NOTE_INSN_DELETED) is followed by zero or more - prologue insns and a NOTE_INSN_PROLOGUE_END. */ - emit_note_after (NOTE_INSN_PROLOGUE_END, f); - seq = gen_prologue (); - head = emit_insn_after (seq, f); - - /* Include the new prologue insns in the first block. Ignore them - if they form a basic block unto themselves. */ - if (basic_block_head && n_basic_blocks - && GET_CODE (basic_block_head[0]) != CODE_LABEL) - basic_block_head[0] = NEXT_INSN (f); - - /* Retain a map of the prologue insns. */ - prologue = record_insns (GET_CODE (seq) == SEQUENCE ? seq : head); - } - else -#endif - prologue = 0; - -#ifdef HAVE_epilogue - if (HAVE_epilogue) - { - rtx insn = get_last_insn (); - rtx prev = prev_nonnote_insn (insn); - - /* If we end with a BARRIER, we don't need an epilogue. */ - if (! (prev && GET_CODE (prev) == BARRIER)) - { - rtx tail, seq; - - /* The last basic block ends with a NOTE_INSN_EPILOGUE_BEG, - the epilogue insns (this must include the jump insn that - returns), USE insns ad the end of a function, and a BARRIER. */ - - emit_barrier_after (insn); - - /* Place the epilogue before the USE insns at the end of a - function. */ - while (prev - && GET_CODE (prev) == INSN - && GET_CODE (PATTERN (prev)) == USE) - { - insn = PREV_INSN (prev); - prev = prev_nonnote_insn (prev); - } - - seq = gen_epilogue (); - tail = emit_jump_insn_after (seq, insn); - emit_note_after (NOTE_INSN_EPILOGUE_BEG, insn); - - /* Include the new epilogue insns in the last block. Ignore - them if they form a basic block unto themselves. */ - if (basic_block_end && n_basic_blocks - && GET_CODE (basic_block_end[n_basic_blocks - 1]) != JUMP_INSN) - basic_block_end[n_basic_blocks - 1] = tail; - - /* Retain a map of the epilogue insns. */ - epilogue = record_insns (GET_CODE (seq) == SEQUENCE ? seq : tail); - return; - } - } -#endif - epilogue = 0; -} - -/* Reposition the prologue-end and epilogue-begin notes after instruction - scheduling and delayed branch scheduling. */ - -void -reposition_prologue_and_epilogue_notes (f) - rtx f; -{ -#if defined (HAVE_prologue) || defined (HAVE_epilogue) - /* Reposition the prologue and epilogue notes. */ - if (n_basic_blocks) - { - rtx next, prev; - int len; - - if (prologue) - { - register rtx insn, note = 0; - - /* Scan from the beginning until we reach the last prologue insn. - We apparently can't depend on basic_block_{head,end} after - reorg has run. */ - for (len = 0; prologue[len]; len++) - ; - for (insn = f; len && insn; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == NOTE) - { - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_PROLOGUE_END) - note = insn; - } - else if ((len -= contains (insn, prologue)) == 0) - { - /* Find the prologue-end note if we haven't already, and - move it to just after the last prologue insn. */ - if (note == 0) - { - for (note = insn; note = NEXT_INSN (note);) - if (GET_CODE (note) == NOTE - && NOTE_LINE_NUMBER (note) == NOTE_INSN_PROLOGUE_END) - break; - } - next = NEXT_INSN (note); - prev = PREV_INSN (note); - if (prev) - NEXT_INSN (prev) = next; - if (next) - PREV_INSN (next) = prev; - add_insn_after (note, insn); - } - } - } - - if (epilogue) - { - register rtx insn, note = 0; - - /* Scan from the end until we reach the first epilogue insn. - We apparently can't depend on basic_block_{head,end} after - reorg has run. */ - for (len = 0; epilogue[len]; len++) - ; - for (insn = get_last_insn (); len && insn; insn = PREV_INSN (insn)) - { - if (GET_CODE (insn) == NOTE) - { - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EPILOGUE_BEG) - note = insn; - } - else if ((len -= contains (insn, epilogue)) == 0) - { - /* Find the epilogue-begin note if we haven't already, and - move it to just before the first epilogue insn. */ - if (note == 0) - { - for (note = insn; note = PREV_INSN (note);) - if (GET_CODE (note) == NOTE - && NOTE_LINE_NUMBER (note) == NOTE_INSN_EPILOGUE_BEG) - break; - } - next = NEXT_INSN (note); - prev = PREV_INSN (note); - if (prev) - NEXT_INSN (prev) = next; - if (next) - PREV_INSN (next) = prev; - add_insn_after (note, PREV_INSN (insn)); - } - } - } - } -#endif /* HAVE_prologue or HAVE_epilogue */ -} diff --git a/gnu/usr.bin/cc/common/gstddef.h b/gnu/usr.bin/cc/common/gstddef.h deleted file mode 100644 index 5ad0ef2271..0000000000 --- a/gnu/usr.bin/cc/common/gstddef.h +++ /dev/null @@ -1,217 +0,0 @@ -#ifndef _STDDEF_H -#ifndef _STDDEF_H_ -#ifndef _ANSI_STDDEF_H - -/* Any one of these symbols __need_* means that GNU libc - wants us just to define one data type. So don't define - the symbols that indicate this file's entire job has been done. */ -#if (!defined(__need_wchar_t) && !defined(__need_size_t) \ - && !defined(__need_ptrdiff_t) && !defined(__need_NULL)) -#define _STDDEF_H -#define _STDDEF_H_ -/* snaroff@next.com says the NeXT needs this. */ -#define _ANSI_STDDEF_H -#endif - -#ifndef __sys_stdtypes_h -/* This avoids lossage on SunOS but only if stdtypes.h comes first. - There's no way to win with the other order! Sun lossage. */ - -/* On 4.3bsd-net2, make sure ansi.h is included, so we have - one less case to deal with in the following. */ -#if defined (__BSD_NET2__) || defined (____386BSD____) -#include -#endif - -/* In 4.3bsd-net2, machine/ansi.h defines these symbols, which are - defined if the corresponding type is *not* defined. */ -#ifdef _ANSI_H_ -#ifndef _SIZE_T_ -#define _SIZE_T -#endif -#ifndef _PTRDIFF_T_ -#define _PTRDIFF_T -#endif -#ifndef _WCHAR_T_ -#define _WCHAR_T -#endif -/* Undef _FOO_T_ if we are supposed to define foo_t. */ -#if defined (__need_ptrdiff_t) || defined (_STDDEF_H_) -#undef _PTRDIFF_T_ -#endif -#if defined (__need_size_t) || defined (_STDDEF_H_) -#undef _SIZE_T_ -#endif -#if defined (__need_wchar_t) || defined (_STDDEF_H_) -#undef _WCHAR_T_ -#endif -#endif /* _ANSI_H_ */ - -/* Sequent's header files use _PTRDIFF_T_ in some conflicting way. - Just ignore it. */ -#if defined (__sequent__) && defined (_PTRDIFF_T_) -#undef _PTRDIFF_T_ -#endif - -/* In case nobody has defined these types, but we aren't running under - GCC 2.00, make sure that __PTRDIFF_TYPE__, __SIZE__TYPE__, and - __WCHAR_TYPE__ have reasonable values. This can happen if the - parts of GCC is compiled by an older compiler, that actually - include gstddef.h, such as collect2. */ - -/* Signed type of difference of two pointers. */ - -/* Define this type if we are doing the whole job, - or if we want this type in particular. */ -#if defined (_STDDEF_H) || defined (__need_ptrdiff_t) -#ifndef _PTRDIFF_T /* in case has defined it. */ -#ifndef _T_PTRDIFF_ -#ifndef _T_PTRDIFF -#ifndef __PTRDIFF_T -#ifndef _PTRDIFF_T_ -#ifndef ___int_ptrdiff_t_h -#ifndef _GCC_PTRDIFF_T -#define _PTRDIFF_T -#define _T_PTRDIFF_ -#define _T_PTRDIFF -#define __PTRDIFF_T -#define _PTRDIFF_T_ -#define ___int_ptrdiff_t_h -#define _GCC_PTRDIFF_T -#ifndef __PTRDIFF_TYPE__ -#define __PTRDIFF_TYPE__ long int -#endif -typedef __PTRDIFF_TYPE__ ptrdiff_t; -#endif /* _GCC_PTRDIFF_T */ -#endif /* ___int_ptrdiff_t_h */ -#endif /* _PTRDIFF_T_ */ -#endif /* __PTRDIFF_T */ -#endif /* _T_PTRDIFF */ -#endif /* _T_PTRDIFF_ */ -#endif /* _PTRDIFF_T */ - -/* If this symbol has done its job, get rid of it. */ -#undef __need_ptrdiff_t - -#endif /* _STDDEF_H or __need_ptrdiff_t. */ - -/* Unsigned type of `sizeof' something. */ - -/* Define this type if we are doing the whole job, - or if we want this type in particular. */ -#if defined (_STDDEF_H) || defined (__need_size_t) -#ifndef _SIZE_T /* in case has defined it. */ -#ifndef _SYS_SIZE_T_H -#ifndef _T_SIZE_ -#ifndef _T_SIZE -#ifndef __SIZE_T -#ifndef _SIZE_T_ -#ifndef ___int_size_t_h -#ifndef _GCC_SIZE_T -#ifndef _SIZET_ -#define _SIZE_T -#define _SYS_SIZE_T_H -#define _T_SIZE_ -#define _T_SIZE -#define __SIZE_T -#define _SIZE_T_ -#define ___int_size_t_h -#define _GCC_SIZE_T -#define _SIZET_ -#ifndef __SIZE_TYPE__ -#define __SIZE_TYPE__ long unsigned int -#endif -#if !(defined (__GNUG__) && defined (size_t)) -typedef __SIZE_TYPE__ size_t; -#endif /* !(defined (__GNUG__) && defined (size_t)) */ -#endif /* _SIZET_ */ -#endif /* _GCC_SIZE_T */ -#endif /* ___int_size_t_h */ -#endif /* _SIZE_T_ */ -#endif /* __SIZE_T */ -#endif /* _T_SIZE */ -#endif /* _T_SIZE_ */ -#endif /* _SYS_SIZE_T_H */ -#endif /* _SIZE_T */ -#undef __need_size_t -#endif /* _STDDEF_H or __need_size_t. */ - - -/* Wide character type. - Locale-writers should change this as necessary to - be big enough to hold unique values not between 0 and 127, - and not (wchar_t) -1, for each defined multibyte character. */ - -/* Define this type if we are doing the whole job, - or if we want this type in particular. */ -#if defined (_STDDEF_H) || defined (__need_wchar_t) -#ifndef _WCHAR_T -#ifndef _T_WCHAR_ -#ifndef _T_WCHAR -#ifndef __WCHAR_T -#ifndef _WCHAR_T_ -#ifndef ___int_wchar_t_h -#ifndef _GCC_WCHAR_T -#define _WCHAR_T -#define _T_WCHAR_ -#define _T_WCHAR -#define __WCHAR_T -#define _WCHAR_T_ -#define ___int_wchar_t_h -#define _GCC_WCHAR_T -#ifndef __WCHAR_TYPE__ -#define __WCHAR_TYPE__ int -#endif -#ifdef __GNUG__ -/* In C++, wchar_t is a distinct basic type, - and we can expect __wchar_t to be defined by cc1plus. */ -typedef __wchar_t wchar_t; -#else -/* In C, cpp tells us which type to make an alias for. */ -typedef __WCHAR_TYPE__ wchar_t; -#endif -#endif -#endif -#endif -#endif -#endif -#endif -#endif -#undef __need_wchar_t -#endif /* _STDDEF_H or __need_wchar_t. */ - -/* In 4.3bsd-net2, leave these undefined to indicate that size_t, etc. - are already defined. */ -#ifdef _ANSI_H_ -#ifdef _GCC_PTRDIFF_T_ -#undef _PTRDIFF_T_ -#endif -#ifdef _GCC_SIZE_T_ -#undef _SIZE_T_ -#endif -#ifdef _GCC_WCHAR_T_ -#undef _WCHAR_T_ -#endif -#endif /* _ANSI_H_ */ - -#endif /* __sys_stdtypes_h */ - -/* A null pointer constant. */ - -#if defined (_STDDEF_H) || defined (__need_NULL) -#undef NULL /* in case has defined it. */ -#define NULL ((void *)0) -#endif /* NULL not defined and or need NULL. */ -#undef __need_NULL - -#ifdef _STDDEF_H - -/* Offset of member MEMBER in a struct of type TYPE. */ - -#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) - -#endif /* _STDDEF_H was defined this time */ - -#endif /* _ANSI_STDDEF_H was not defined before */ -#endif /* _STDDEF_H_ was not defined before */ -#endif /* _STDDEF_H was not defined before */ diff --git a/gnu/usr.bin/cc/common/insn-emit.c b/gnu/usr.bin/cc/common/insn-emit.c deleted file mode 100644 index 4101b905e1..0000000000 --- a/gnu/usr.bin/cc/common/insn-emit.c +++ /dev/null @@ -1,2698 +0,0 @@ -/* Generated automatically by the program `genemit' -from the machine description file `md'. */ - -#include "config.h" -#include "rtl.h" -#include "expr.h" -#include "real.h" -#include "output.h" -#include "insn-config.h" - -#include "insn-flags.h" - -#include "insn-codes.h" - -extern char *insn_operand_constraint[][MAX_RECOG_OPERANDS]; - -extern rtx recog_operand[]; -#define operands emit_operand - -#define FAIL goto _fail - -#define DONE goto _done - -rtx -gen_tstsi_1 (operand0) - rtx operand0; -{ - return gen_rtx (SET, VOIDmode, cc0_rtx, operand0); -} - -rtx -gen_tstsi (operand0) - rtx operand0; -{ - rtx operands[1]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - -{ - i386_compare_gen = gen_tstsi_1; - i386_compare_op0 = operands[0]; - DONE; -} - operand0 = operands[0]; - emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, operand0)); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_tsthi_1 (operand0) - rtx operand0; -{ - return gen_rtx (SET, VOIDmode, cc0_rtx, operand0); -} - -rtx -gen_tsthi (operand0) - rtx operand0; -{ - rtx operands[1]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - -{ - i386_compare_gen = gen_tsthi_1; - i386_compare_op0 = operands[0]; - DONE; -} - operand0 = operands[0]; - emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, operand0)); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_tstqi_1 (operand0) - rtx operand0; -{ - return gen_rtx (SET, VOIDmode, cc0_rtx, operand0); -} - -rtx -gen_tstqi (operand0) - rtx operand0; -{ - rtx operands[1]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - -{ - i386_compare_gen = gen_tstqi_1; - i386_compare_op0 = operands[0]; - DONE; -} - operand0 = operands[0]; - emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, operand0)); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_tstsf_cc (operand0) - rtx operand0; -{ - return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, cc0_rtx, operand0), - gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0)))); -} - -rtx -gen_tstsf (operand0) - rtx operand0; -{ - rtx operands[1]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - -{ - i386_compare_gen = gen_tstsf_cc; - i386_compare_op0 = operands[0]; - DONE; -} - operand0 = operands[0]; - emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, cc0_rtx, operand0), - gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0))))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_tstdf_cc (operand0) - rtx operand0; -{ - return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, cc0_rtx, operand0), - gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0)))); -} - -rtx -gen_tstdf (operand0) - rtx operand0; -{ - rtx operands[1]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - -{ - i386_compare_gen = gen_tstdf_cc; - i386_compare_op0 = operands[0]; - DONE; -} - operand0 = operands[0]; - emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, cc0_rtx, operand0), - gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0))))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_cmpsi_1 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1)); -} - -rtx -gen_cmpsi (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - -{ - if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) - operands[0] = force_reg (SImode, operands[0]); - - i386_compare_gen = gen_cmpsi_1; - i386_compare_op0 = operands[0]; - i386_compare_op1 = operands[1]; - DONE; -} - operand0 = operands[0]; - operand1 = operands[1]; - emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_cmphi_1 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1)); -} - -rtx -gen_cmphi (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - -{ - if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) - operands[0] = force_reg (HImode, operands[0]); - - i386_compare_gen = gen_cmphi_1; - i386_compare_op0 = operands[0]; - i386_compare_op1 = operands[1]; - DONE; -} - operand0 = operands[0]; - operand1 = operands[1]; - emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_cmpqi_1 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1)); -} - -rtx -gen_cmpqi (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - -{ - if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) - operands[0] = force_reg (QImode, operands[0]); - - i386_compare_gen = gen_cmpqi_1; - i386_compare_op0 = operands[0]; - i386_compare_op1 = operands[1]; - DONE; -} - operand0 = operands[0]; - operand1 = operands[1]; - emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_cmpsf_cc_1 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (GET_CODE (operand2), VOIDmode, - operand0, - operand1)), - gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0)))); -} - -rtx -gen_cmpdf (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - -{ - i386_compare_gen = gen_cmpdf_cc; - i386_compare_gen_eq = gen_cmpdf_ccfpeq; - i386_compare_op0 = operands[0]; - i386_compare_op1 = operands[1]; - DONE; -} - operand0 = operands[0]; - operand1 = operands[1]; - emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_cmpsf (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - -{ - i386_compare_gen = gen_cmpsf_cc; - i386_compare_gen_eq = gen_cmpsf_ccfpeq; - i386_compare_op0 = operands[0]; - i386_compare_op1 = operands[1]; - DONE; -} - operand0 = operands[0]; - operand1 = operands[1]; - emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_cmpdf_cc (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1)), - gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0)))); -} - -rtx -gen_cmpdf_ccfpeq (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - -{ - if (! register_operand (operands[1], DFmode)) - operands[1] = copy_to_mode_reg (DFmode, operands[1]); -} - operand0 = operands[0]; - operand1 = operands[1]; - emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, CCFPEQmode, operand0, operand1)), - gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0))))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_cmpsf_cc (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1)), - gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0)))); -} - -rtx -gen_cmpsf_ccfpeq (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - -{ - if (! register_operand (operands[1], SFmode)) - operands[1] = copy_to_mode_reg (SFmode, operands[1]); -} - operand0 = operands[0]; - operand1 = operands[1]; - emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, CCFPEQmode, operand0, operand1)), - gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0))))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_movsi (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - -{ - extern int flag_pic; - - if (flag_pic && SYMBOLIC_CONST (operands[1])) - emit_pic_move (operands, SImode); -} - operand0 = operands[0]; - operand1 = operands[1]; - emit_insn (gen_rtx (SET, VOIDmode, operand0, operand1)); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_movhi (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, operand1); -} - -rtx -gen_movstricthi (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, gen_rtx (STRICT_LOW_PART, VOIDmode, operand0), operand1); -} - -rtx -gen_movqi (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, operand1); -} - -rtx -gen_movstrictqi (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, gen_rtx (STRICT_LOW_PART, VOIDmode, operand0), operand1); -} - -rtx -gen_movsf (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, operand1); -} - -rtx -gen_swapdf (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, operand0, operand1), - gen_rtx (SET, VOIDmode, operand1, operand0))); -} - -rtx -gen_movdf (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, operand1); -} - -rtx -gen_movdi (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, operand1); -} - -rtx -gen_zero_extendhisi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ZERO_EXTEND, SImode, operand1)); -} - -rtx -gen_zero_extendqihi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ZERO_EXTEND, HImode, operand1)); -} - -rtx -gen_zero_extendqisi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ZERO_EXTEND, SImode, operand1)); -} - -rtx -gen_zero_extendsidi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ZERO_EXTEND, DImode, operand1)); -} - -rtx -gen_extendsidi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (SIGN_EXTEND, DImode, operand1)); -} - -rtx -gen_extendhisi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (SIGN_EXTEND, SImode, operand1)); -} - -rtx -gen_extendqihi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (SIGN_EXTEND, HImode, operand1)); -} - -rtx -gen_extendqisi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (SIGN_EXTEND, SImode, operand1)); -} - -rtx -gen_extendsfdf2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (FLOAT_EXTEND, DFmode, operand1)); -} - -rtx -gen_truncdfsf2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operand2; - rtx operands[3]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - -{ - operands[2] = (rtx) assign_386_stack_local (SFmode, 0); -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, operand0, gen_rtx (FLOAT_TRUNCATE, SFmode, operand1)), - gen_rtx (CLOBBER, VOIDmode, operand2)))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_fixuns_truncdfsi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operand2; - rtx operand3; - rtx operand4; - rtx operand5; - rtx operand6; - rtx operands[7]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - -{ - operands[2] = gen_reg_rtx (DImode); - operands[3] = gen_lowpart (SImode, operands[2]); - operands[4] = gen_reg_rtx (DFmode); - operands[5] = (rtx) assign_386_stack_local (SImode, 0); - operands[6] = (rtx) assign_386_stack_local (SImode, 1); -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - operand3 = operands[3]; - operand4 = operands[4]; - operand5 = operands[5]; - operand6 = operands[6]; - emit_insn (gen_rtx (SET, VOIDmode, operand4, operand1)); - emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (5, - gen_rtx (SET, VOIDmode, operand2, gen_rtx (FIX, DImode, gen_rtx (FIX, DFmode, operand4))), - gen_rtx (CLOBBER, VOIDmode, operand4), - gen_rtx (CLOBBER, VOIDmode, operand5), - gen_rtx (CLOBBER, VOIDmode, operand6), - gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0))))); - emit_insn (gen_rtx (SET, VOIDmode, operand0, operand3)); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_fixuns_truncsfsi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operand2; - rtx operand3; - rtx operand4; - rtx operand5; - rtx operand6; - rtx operands[7]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - -{ - operands[2] = gen_reg_rtx (DImode); - operands[3] = gen_lowpart (SImode, operands[2]); - operands[4] = gen_reg_rtx (SFmode); - operands[5] = (rtx) assign_386_stack_local (SImode, 0); - operands[6] = (rtx) assign_386_stack_local (SImode, 1); -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - operand3 = operands[3]; - operand4 = operands[4]; - operand5 = operands[5]; - operand6 = operands[6]; - emit_insn (gen_rtx (SET, VOIDmode, operand4, operand1)); - emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (5, - gen_rtx (SET, VOIDmode, operand2, gen_rtx (FIX, DImode, gen_rtx (FIX, SFmode, operand4))), - gen_rtx (CLOBBER, VOIDmode, operand4), - gen_rtx (CLOBBER, VOIDmode, operand5), - gen_rtx (CLOBBER, VOIDmode, operand6), - gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0))))); - emit_insn (gen_rtx (SET, VOIDmode, operand0, operand3)); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_fix_truncdfdi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operand2; - rtx operand3; - rtx operand4; - rtx operands[5]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - -{ - operands[1] = copy_to_mode_reg (DFmode, operands[1]); - operands[2] = gen_reg_rtx (DFmode); - operands[3] = (rtx) assign_386_stack_local (SImode, 0); - operands[4] = (rtx) assign_386_stack_local (SImode, 1); -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - operand3 = operands[3]; - operand4 = operands[4]; - emit_insn (gen_rtx (SET, VOIDmode, operand2, operand1)); - emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (5, - gen_rtx (SET, VOIDmode, operand0, gen_rtx (FIX, DImode, gen_rtx (FIX, DFmode, operand2))), - gen_rtx (CLOBBER, VOIDmode, operand2), - gen_rtx (CLOBBER, VOIDmode, operand3), - gen_rtx (CLOBBER, VOIDmode, operand4), - gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0))))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_fix_truncsfdi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operand2; - rtx operand3; - rtx operand4; - rtx operands[5]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - -{ - operands[1] = copy_to_mode_reg (SFmode, operands[1]); - operands[2] = gen_reg_rtx (SFmode); - operands[3] = (rtx) assign_386_stack_local (SImode, 0); - operands[4] = (rtx) assign_386_stack_local (SImode, 1); -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - operand3 = operands[3]; - operand4 = operands[4]; - emit_insn (gen_rtx (SET, VOIDmode, operand2, operand1)); - emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (5, - gen_rtx (SET, VOIDmode, operand0, gen_rtx (FIX, DImode, gen_rtx (FIX, SFmode, operand2))), - gen_rtx (CLOBBER, VOIDmode, operand2), - gen_rtx (CLOBBER, VOIDmode, operand3), - gen_rtx (CLOBBER, VOIDmode, operand4), - gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0))))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_fix_truncdfsi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operand2; - rtx operand3; - rtx operands[4]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - -{ - operands[2] = (rtx) assign_386_stack_local (SImode, 0); - operands[3] = (rtx) assign_386_stack_local (SImode, 1); -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - operand3 = operands[3]; - emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (4, - gen_rtx (SET, VOIDmode, operand0, gen_rtx (FIX, SImode, gen_rtx (FIX, DFmode, operand1))), - gen_rtx (CLOBBER, VOIDmode, operand2), - gen_rtx (CLOBBER, VOIDmode, operand3), - gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0))))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_fix_truncsfsi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operand2; - rtx operand3; - rtx operands[4]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - -{ - operands[2] = (rtx) assign_386_stack_local (SImode, 0); - operands[3] = (rtx) assign_386_stack_local (SImode, 1); -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - operand3 = operands[3]; - emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (4, - gen_rtx (SET, VOIDmode, operand0, gen_rtx (FIX, SImode, gen_rtx (FIX, SFmode, operand1))), - gen_rtx (CLOBBER, VOIDmode, operand2), - gen_rtx (CLOBBER, VOIDmode, operand3), - gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0))))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_floatsisf2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (FLOAT, SFmode, operand1)); -} - -rtx -gen_floatdisf2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (FLOAT, SFmode, operand1)); -} - -rtx -gen_floatsidf2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (FLOAT, DFmode, operand1)); -} - -rtx -gen_floatdidf2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (FLOAT, DFmode, operand1)); -} - -rtx -gen_adddi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (PLUS, DImode, operand1, operand2)); -} - -rtx -gen_addsi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (PLUS, SImode, operand1, operand2)); -} - -rtx -gen_addhi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (PLUS, HImode, operand1, operand2)); -} - -rtx -gen_addqi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (PLUS, QImode, operand1, operand2)); -} - -rtx -gen_adddf3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (PLUS, DFmode, operand1, operand2)); -} - -rtx -gen_addsf3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (PLUS, SFmode, operand1, operand2)); -} - -rtx -gen_subdi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MINUS, DImode, operand1, operand2)); -} - -rtx -gen_subsi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MINUS, SImode, operand1, operand2)); -} - -rtx -gen_subhi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MINUS, HImode, operand1, operand2)); -} - -rtx -gen_subqi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MINUS, QImode, operand1, operand2)); -} - -rtx -gen_subdf3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MINUS, DFmode, operand1, operand2)); -} - -rtx -gen_subsf3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MINUS, SFmode, operand1, operand2)); -} - -rtx -gen_mulhi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MULT, SImode, operand1, operand2)); -} - -rtx -gen_mulsi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MULT, SImode, operand1, operand2)); -} - -rtx -gen_muldf3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MULT, DFmode, operand1, operand2)); -} - -rtx -gen_mulsf3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MULT, SFmode, operand1, operand2)); -} - -rtx -gen_divqi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (DIV, QImode, operand1, operand2)); -} - -rtx -gen_udivqi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (UDIV, QImode, operand1, operand2)); -} - -rtx -gen_divdf3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (DIV, DFmode, operand1, operand2)); -} - -rtx -gen_divsf3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (DIV, SFmode, operand1, operand2)); -} - -rtx -gen_divmodsi4 (operand0, operand1, operand2, operand3) - rtx operand0; - rtx operand1; - rtx operand2; - rtx operand3; -{ - return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, operand0, gen_rtx (DIV, SImode, operand1, operand2)), - gen_rtx (SET, VOIDmode, operand3, gen_rtx (MOD, SImode, operand1, operand2)))); -} - -rtx -gen_divmodhi4 (operand0, operand1, operand2, operand3) - rtx operand0; - rtx operand1; - rtx operand2; - rtx operand3; -{ - return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, operand0, gen_rtx (DIV, HImode, operand1, operand2)), - gen_rtx (SET, VOIDmode, operand3, gen_rtx (MOD, HImode, operand1, operand2)))); -} - -rtx -gen_udivmodsi4 (operand0, operand1, operand2, operand3) - rtx operand0; - rtx operand1; - rtx operand2; - rtx operand3; -{ - return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, operand0, gen_rtx (UDIV, SImode, operand1, operand2)), - gen_rtx (SET, VOIDmode, operand3, gen_rtx (UMOD, SImode, operand1, operand2)))); -} - -rtx -gen_udivmodhi4 (operand0, operand1, operand2, operand3) - rtx operand0; - rtx operand1; - rtx operand2; - rtx operand3; -{ - return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, operand0, gen_rtx (UDIV, HImode, operand1, operand2)), - gen_rtx (SET, VOIDmode, operand3, gen_rtx (UMOD, HImode, operand1, operand2)))); -} - -rtx -gen_andsi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (AND, SImode, operand1, operand2)); -} - -rtx -gen_andhi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (AND, HImode, operand1, operand2)); -} - -rtx -gen_andqi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (AND, QImode, operand1, operand2)); -} - -rtx -gen_iorsi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (IOR, SImode, operand1, operand2)); -} - -rtx -gen_iorhi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (IOR, HImode, operand1, operand2)); -} - -rtx -gen_iorqi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (IOR, QImode, operand1, operand2)); -} - -rtx -gen_xorsi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (XOR, SImode, operand1, operand2)); -} - -rtx -gen_xorhi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (XOR, HImode, operand1, operand2)); -} - -rtx -gen_xorqi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (XOR, QImode, operand1, operand2)); -} - -rtx -gen_negdi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NEG, DImode, operand1)); -} - -rtx -gen_negsi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NEG, SImode, operand1)); -} - -rtx -gen_neghi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NEG, HImode, operand1)); -} - -rtx -gen_negqi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NEG, QImode, operand1)); -} - -rtx -gen_negsf2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NEG, SFmode, operand1)); -} - -rtx -gen_negdf2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NEG, DFmode, operand1)); -} - -rtx -gen_abssf2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ABS, SFmode, operand1)); -} - -rtx -gen_absdf2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ABS, DFmode, operand1)); -} - -rtx -gen_sqrtsf2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (SQRT, SFmode, operand1)); -} - -rtx -gen_sqrtdf2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (SQRT, DFmode, operand1)); -} - -rtx -gen_sindf2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (UNSPEC, DFmode, gen_rtvec (1, - operand1), 1)); -} - -rtx -gen_sinsf2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (UNSPEC, SFmode, gen_rtvec (1, - operand1), 1)); -} - -rtx -gen_cosdf2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (UNSPEC, DFmode, gen_rtvec (1, - operand1), 2)); -} - -rtx -gen_cossf2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (UNSPEC, SFmode, gen_rtvec (1, - operand1), 2)); -} - -rtx -gen_one_cmplsi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NOT, SImode, operand1)); -} - -rtx -gen_one_cmplhi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NOT, HImode, operand1)); -} - -rtx -gen_one_cmplqi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NOT, QImode, operand1)); -} - -rtx -gen_ashldi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - rtx operands[3]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - operands[2] = operand2; - -{ - if (GET_CODE (operands[2]) != CONST_INT - || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J')) - { - operands[2] = copy_to_mode_reg (QImode, operands[2]); - emit_insn (gen_ashldi3_non_const_int (operands[0], operands[1], - operands[2])); - } - else - emit_insn (gen_ashldi3_const_int (operands[0], operands[1], operands[2])); - - DONE; -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFT, DImode, operand1, operand2))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_ashldi3_const_int (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFT, DImode, operand1, operand2)); -} - -rtx -gen_ashldi3_non_const_int (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFT, DImode, operand1, operand2)), - gen_rtx (CLOBBER, VOIDmode, operand2))); -} - -rtx -gen_ashlsi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFT, SImode, operand1, operand2)); -} - -rtx -gen_ashlhi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFT, HImode, operand1, operand2)); -} - -rtx -gen_ashlqi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFT, QImode, operand1, operand2)); -} - -rtx -gen_ashrdi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - rtx operands[3]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - operands[2] = operand2; - -{ - if (GET_CODE (operands[2]) != CONST_INT - || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J')) - { - operands[2] = copy_to_mode_reg (QImode, operands[2]); - emit_insn (gen_ashrdi3_non_const_int (operands[0], operands[1], - operands[2])); - } - else - emit_insn (gen_ashrdi3_const_int (operands[0], operands[1], operands[2])); - - DONE; -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFTRT, DImode, operand1, operand2))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_ashrdi3_const_int (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFTRT, DImode, operand1, operand2)); -} - -rtx -gen_ashrdi3_non_const_int (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFTRT, DImode, operand1, operand2)), - gen_rtx (CLOBBER, VOIDmode, operand2))); -} - -rtx -gen_ashrsi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFTRT, SImode, operand1, operand2)); -} - -rtx -gen_ashrhi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFTRT, HImode, operand1, operand2)); -} - -rtx -gen_ashrqi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFTRT, QImode, operand1, operand2)); -} - -rtx -gen_lshrdi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - rtx operands[3]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - operands[2] = operand2; - -{ - if (GET_CODE (operands[2]) != CONST_INT - || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J')) - { - operands[2] = copy_to_mode_reg (QImode, operands[2]); - emit_insn (gen_lshrdi3_non_const_int (operands[0], operands[1], - operands[2])); - } - else - emit_insn (gen_lshrdi3_const_int (operands[0], operands[1], operands[2])); - - DONE; -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (LSHIFTRT, DImode, operand1, operand2))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_lshrdi3_const_int (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (LSHIFTRT, DImode, operand1, operand2)); -} - -rtx -gen_lshrdi3_non_const_int (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, operand0, gen_rtx (LSHIFTRT, DImode, operand1, operand2)), - gen_rtx (CLOBBER, VOIDmode, operand2))); -} - -rtx -gen_lshrsi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (LSHIFTRT, SImode, operand1, operand2)); -} - -rtx -gen_lshrhi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (LSHIFTRT, HImode, operand1, operand2)); -} - -rtx -gen_lshrqi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (LSHIFTRT, QImode, operand1, operand2)); -} - -rtx -gen_rotlsi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ROTATE, SImode, operand1, operand2)); -} - -rtx -gen_rotlhi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ROTATE, HImode, operand1, operand2)); -} - -rtx -gen_rotlqi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ROTATE, QImode, operand1, operand2)); -} - -rtx -gen_rotrsi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ROTATERT, SImode, operand1, operand2)); -} - -rtx -gen_rotrhi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ROTATERT, HImode, operand1, operand2)); -} - -rtx -gen_rotrqi3 (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ROTATERT, QImode, operand1, operand2)); -} - -rtx -gen_seq (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - -{ - if (TARGET_IEEE_FP - && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT) - operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1); - else - operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); -} - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (EQ, QImode, cc0_rtx, const0_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_sne (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - -{ - if (TARGET_IEEE_FP - && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT) - operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1); - else - operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); -} - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (NE, QImode, cc0_rtx, const0_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_sgt (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; -operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (GT, QImode, cc0_rtx, const0_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_sgtu (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; -operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (GTU, QImode, cc0_rtx, const0_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_slt (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; -operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (LT, QImode, cc0_rtx, const0_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_sltu (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; -operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (LTU, QImode, cc0_rtx, const0_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_sge (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; -operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (GE, QImode, cc0_rtx, const0_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_sgeu (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; -operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (GEU, QImode, cc0_rtx, const0_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_sle (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; -operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (LE, QImode, cc0_rtx, const0_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_sleu (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; -operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (LEU, QImode, cc0_rtx, const0_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_beq (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - -{ - if (TARGET_IEEE_FP - && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT) - operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1); - else - operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); -} - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (EQ, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_bne (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - -{ - if (TARGET_IEEE_FP - && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT) - operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1); - else - operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); -} - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (NE, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_bgt (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; -operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (GT, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_bgtu (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; -operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (GTU, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_blt (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; -operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (LT, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_bltu (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; -operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (LTU, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_bge (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; -operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (GE, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_bgeu (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; -operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (GEU, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_ble (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; -operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (LE, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_bleu (operand0) - rtx operand0; -{ - rtx operand1; - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; -operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand1); - emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (LEU, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_jump (operand0) - rtx operand0; -{ - return gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (LABEL_REF, VOIDmode, operand0)); -} - -rtx -gen_indirect_jump (operand0) - rtx operand0; -{ - return gen_rtx (SET, VOIDmode, pc_rtx, operand0); -} - -rtx -gen_casesi (operand0, operand1, operand2, operand3, operand4) - rtx operand0; - rtx operand1; - rtx operand2; - rtx operand3; - rtx operand4; -{ - rtx operand5; - rtx operands[6]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - operands[2] = operand2; - operands[3] = operand3; - operands[4] = operand4; - -{ - operands[5] = gen_reg_rtx (SImode); - current_function_uses_pic_offset_table = 1; -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - operand3 = operands[3]; - operand4 = operands[4]; - operand5 = operands[5]; - emit_insn (gen_rtx (SET, VOIDmode, operand5, gen_rtx (MINUS, SImode, operand0, operand1))); - emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, CCmode, operand5, operand2))); - emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (GTU, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand4), pc_rtx))); - emit_jump_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (MINUS, SImode, gen_rtx (REG, SImode, 3), gen_rtx (MEM, SImode, gen_rtx (PLUS, SImode, gen_rtx (MULT, SImode, operand5, GEN_INT (4)), gen_rtx (LABEL_REF, VOIDmode, operand3))))), - gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0))))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_tablejump (operand0, operand1) - rtx operand0; - rtx operand1; -{ - return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, pc_rtx, operand0), - gen_rtx (USE, VOIDmode, gen_rtx (LABEL_REF, VOIDmode, operand1)))); -} - -rtx -gen_call_pop (operand0, operand1, operand2, operand3) - rtx operand0; - rtx operand1; - rtx operand2; - rtx operand3; -{ - rtx operands[4]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - operands[2] = operand2; - operands[3] = operand3; - -{ - rtx addr; - - if (flag_pic) - current_function_uses_pic_offset_table = 1; - - /* With half-pic, force the address into a register. */ - addr = XEXP (operands[0], 0); - if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr)) - XEXP (operands[0], 0) = force_reg (Pmode, addr); - - if (! call_insn_operand (operands[0], QImode)) - operands[0] - = change_address (operands[0], VOIDmode, - copy_to_mode_reg (Pmode, XEXP (operands[0], 0))); -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - operand3 = operands[3]; - emit_call_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (CALL, VOIDmode, operand0, operand1), - gen_rtx (SET, VOIDmode, gen_rtx (REG, SImode, 7), gen_rtx (PLUS, SImode, gen_rtx (REG, SImode, 7), operand3))))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_call (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - -{ - rtx addr; - - if (flag_pic) - current_function_uses_pic_offset_table = 1; - - /* With half-pic, force the address into a register. */ - addr = XEXP (operands[0], 0); - if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr)) - XEXP (operands[0], 0) = force_reg (Pmode, addr); - - if (! call_insn_operand (operands[0], QImode)) - operands[0] - = change_address (operands[0], VOIDmode, - copy_to_mode_reg (Pmode, XEXP (operands[0], 0))); -} - operand0 = operands[0]; - operand1 = operands[1]; - emit_call_insn (gen_rtx (CALL, VOIDmode, operand0, operand1)); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_call_value_pop (operand0, operand1, operand2, operand3, operand4) - rtx operand0; - rtx operand1; - rtx operand2; - rtx operand3; - rtx operand4; -{ - rtx operands[5]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - operands[2] = operand2; - operands[3] = operand3; - operands[4] = operand4; - -{ - rtx addr; - - if (flag_pic) - current_function_uses_pic_offset_table = 1; - - /* With half-pic, force the address into a register. */ - addr = XEXP (operands[1], 0); - if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr)) - XEXP (operands[1], 0) = force_reg (Pmode, addr); - - if (! call_insn_operand (operands[1], QImode)) - operands[1] - = change_address (operands[1], VOIDmode, - copy_to_mode_reg (Pmode, XEXP (operands[1], 0))); -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - operand3 = operands[3]; - operand4 = operands[4]; - emit_call_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, operand0, gen_rtx (CALL, VOIDmode, operand1, operand2)), - gen_rtx (SET, VOIDmode, gen_rtx (REG, SImode, 7), gen_rtx (PLUS, SImode, gen_rtx (REG, SImode, 7), operand4))))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_call_value (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - rtx operands[3]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - operands[2] = operand2; - -{ - rtx addr; - - if (flag_pic) - current_function_uses_pic_offset_table = 1; - - /* With half-pic, force the address into a register. */ - addr = XEXP (operands[1], 0); - if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr)) - XEXP (operands[1], 0) = force_reg (Pmode, addr); - - if (! call_insn_operand (operands[1], QImode)) - operands[1] - = change_address (operands[1], VOIDmode, - copy_to_mode_reg (Pmode, XEXP (operands[1], 0))); -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - emit_call_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (CALL, VOIDmode, operand1, operand2))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_untyped_call (operand0, operand1, operand2) - rtx operand0; - rtx operand1; - rtx operand2; -{ - rtx operands[3]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - operands[2] = operand2; - -{ - rtx addr; - - if (flag_pic) - current_function_uses_pic_offset_table = 1; - - /* With half-pic, force the address into a register. */ - addr = XEXP (operands[0], 0); - if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr)) - XEXP (operands[0], 0) = force_reg (Pmode, addr); - - operands[1] = change_address (operands[1], DImode, XEXP (operands[1], 0)); - if (! call_insn_operand (operands[1], QImode)) - operands[1] - = change_address (operands[1], VOIDmode, - copy_to_mode_reg (Pmode, XEXP (operands[1], 0))); -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - emit_call_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (3, - gen_rtx (CALL, VOIDmode, operand0, const0_rtx), - operand1, - operand2))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_untyped_return (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operands[2]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - -{ - rtx valreg1 = gen_rtx (REG, SImode, 0); - rtx valreg2 = gen_rtx (REG, SImode, 1); - rtx result = operands[0]; - - /* Restore the FPU state. */ - emit_insn (gen_update_return (change_address (result, SImode, - plus_constant (XEXP (result, 0), - 8)))); - - /* Reload the function value registers. */ - emit_move_insn (valreg1, change_address (result, SImode, XEXP (result, 0))); - emit_move_insn (valreg2, - change_address (result, SImode, - plus_constant (XEXP (result, 0), 4))); - - /* Put USE insns before the return. */ - emit_insn (gen_rtx (USE, VOIDmode, valreg1)); - emit_insn (gen_rtx (USE, VOIDmode, valreg2)); - - /* Construct the return. */ - expand_null_return (); - - DONE; -} - operand0 = operands[0]; - operand1 = operands[1]; - emit (operand0); - emit (operand1); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_update_return (operand0) - rtx operand0; -{ - return gen_rtx (UNSPEC, SImode, gen_rtvec (1, - operand0), 0); -} - -rtx -gen_return () -{ - return gen_rtx (RETURN, VOIDmode); -} - -rtx -gen_nop () -{ - return const0_rtx; -} - -rtx -gen_movstrsi (operand0, operand1, operand2, operand3) - rtx operand0; - rtx operand1; - rtx operand2; - rtx operand3; -{ - rtx operand4; - rtx operand5; - rtx operand6; - rtx operands[7]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - operands[2] = operand2; - operands[3] = operand3; - -{ - rtx addr0, addr1; - - if (GET_CODE (operands[2]) != CONST_INT) - FAIL; - - addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0)); - addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0)); - - operands[5] = addr0; - operands[6] = addr1; - - operands[0] = gen_rtx (MEM, BLKmode, addr0); - operands[1] = gen_rtx (MEM, BLKmode, addr1); -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - operand3 = operands[3]; - operand4 = operands[4]; - operand5 = operands[5]; - operand6 = operands[6]; - emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (6, - gen_rtx (SET, VOIDmode, operand0, operand1), - gen_rtx (USE, VOIDmode, operand2), - gen_rtx (USE, VOIDmode, operand3), - gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0)), - gen_rtx (CLOBBER, VOIDmode, operand5), - gen_rtx (CLOBBER, VOIDmode, operand6)))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_cmpstrsi (operand0, operand1, operand2, operand3, operand4) - rtx operand0; - rtx operand1; - rtx operand2; - rtx operand3; - rtx operand4; -{ - rtx operand5; - rtx operand6; - rtx operands[7]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - operands[2] = operand2; - operands[3] = operand3; - operands[4] = operand4; - -{ - rtx addr1, addr2; - - addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0)); - addr2 = copy_to_mode_reg (Pmode, XEXP (operands[2], 0)); - operands[3] = copy_to_mode_reg (SImode, operands[3]); - - operands[5] = addr1; - operands[6] = addr2; - - operands[1] = gen_rtx (MEM, BLKmode, addr1); - operands[2] = gen_rtx (MEM, BLKmode, addr2); - -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - operand3 = operands[3]; - operand4 = operands[4]; - operand5 = operands[5]; - operand6 = operands[6]; - emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (6, - gen_rtx (SET, VOIDmode, operand0, gen_rtx (COMPARE, SImode, operand1, operand2)), - gen_rtx (USE, VOIDmode, operand3), - gen_rtx (USE, VOIDmode, operand4), - gen_rtx (CLOBBER, VOIDmode, operand5), - gen_rtx (CLOBBER, VOIDmode, operand6), - gen_rtx (CLOBBER, VOIDmode, operand3)))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_ffssi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operand2; - rtx operands[3]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; -operands[2] = gen_reg_rtx (SImode); - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - emit_insn (gen_rtx (SET, VOIDmode, operand2, gen_rtx (PLUS, SImode, gen_rtx (FFS, SImode, operand1), constm1_rtx))); - emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (PLUS, SImode, operand2, const1_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_ffshi2 (operand0, operand1) - rtx operand0; - rtx operand1; -{ - rtx operand2; - rtx operands[3]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; -operands[2] = gen_reg_rtx (HImode); - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - emit_insn (gen_rtx (SET, VOIDmode, operand2, gen_rtx (PLUS, HImode, gen_rtx (FFS, HImode, operand1), constm1_rtx))); - emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (PLUS, HImode, operand2, const1_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - -rtx -gen_strlensi (operand0, operand1, operand2, operand3) - rtx operand0; - rtx operand1; - rtx operand2; - rtx operand3; -{ - rtx operand4; - rtx operand5; - rtx operands[6]; - rtx _val = 0; - start_sequence (); - operands[0] = operand0; - operands[1] = operand1; - operands[2] = operand2; - operands[3] = operand3; - -{ - operands[1] = copy_to_mode_reg (SImode, XEXP (operands[1], 0)); - operands[4] = gen_reg_rtx (SImode); - operands[5] = gen_reg_rtx (SImode); -} - operand0 = operands[0]; - operand1 = operands[1]; - operand2 = operands[2]; - operand3 = operands[3]; - operand4 = operands[4]; - operand5 = operands[5]; - emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, operand4, gen_rtx (UNSPEC, SImode, gen_rtvec (3, - gen_rtx (MEM, BLKmode, operand1), - operand2, - operand3), 0)), - gen_rtx (CLOBBER, VOIDmode, operand1)))); - emit_insn (gen_rtx (SET, VOIDmode, operand5, gen_rtx (NOT, SImode, operand4))); - emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (MINUS, SImode, operand5, const1_rtx))); - _done: - _val = gen_sequence (); - _fail: - end_sequence (); - return _val; -} - - - -void -add_clobbers (pattern, insn_code_number) - rtx pattern; - int insn_code_number; -{ - int i; - - switch (insn_code_number) - { - case 223: - XVECEXP (pattern, 0, 1) = gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0)); - break; - - case 72: - case 71: - XVECEXP (pattern, 0, 3) = gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0)); - break; - - case 68: - case 67: - XVECEXP (pattern, 0, 4) = gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0)); - break; - - case 25: - case 24: - case 23: - case 22: - case 21: - case 20: - case 19: - case 18: - case 17: - case 16: - case 8: - case 6: - XVECEXP (pattern, 0, 1) = gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0)); - break; - - default: - abort (); - } -} - -void -init_mov_optab () -{ -#ifdef HAVE_movccfpeq - if (HAVE_movccfpeq) - mov_optab->handlers[(int) CCFPEQmode].insn_code = CODE_FOR_movccfpeq; -#endif -} diff --git a/gnu/usr.bin/cc/common/insn-extract.c b/gnu/usr.bin/cc/common/insn-extract.c deleted file mode 100644 index 9413925be7..0000000000 --- a/gnu/usr.bin/cc/common/insn-extract.c +++ /dev/null @@ -1,500 +0,0 @@ -/* Generated automatically by the program `genextract' -from the machine description file `md'. */ - -#include "config.h" -#include "rtl.h" - -static rtx junk; -extern rtx recog_operand[]; -extern rtx *recog_operand_loc[]; -extern rtx *recog_dup_loc[]; -extern char recog_dup_num[]; -extern -#ifdef __GNUC__ -volatile -#endif -void fatal_insn_not_found (); - -void -insn_extract (insn) - rtx insn; -{ - register rtx *ro = recog_operand; - register rtx **ro_loc = recog_operand_loc; - rtx pat = PATTERN (insn); - switch (INSN_CODE (insn)) - { - case -1: - fatal_insn_not_found (insn); - - case 262: - ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0, 0), 0)); - ro[2] = *(ro_loc[2] = &XVECEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0, 1)); - ro[3] = *(ro_loc[3] = &XVECEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0, 2)); - recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 1), 0); - recog_dup_num[0] = 1; - break; - - case 260: - case 257: - case 256: - ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XEXP (XEXP (pat, 1), 1), 0)); - ro[3] = *(ro_loc[3] = &XEXP (pat, 1)); - break; - - case 259: - case 255: - case 254: - ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (pat, 1), 0), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 1)); - ro[3] = *(ro_loc[3] = &XEXP (pat, 1)); - break; - - case 258: - case 253: - ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 1)); - ro[3] = *(ro_loc[3] = &XEXP (pat, 1)); - break; - - case 248: - ro[0] = *(ro_loc[0] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0)); - ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 2), 0)); - recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 5), 0); - recog_dup_num[0] = 2; - recog_dup_loc[1] = &XEXP (XVECEXP (pat, 0, 4), 0); - recog_dup_num[1] = 1; - recog_dup_loc[2] = &XEXP (XVECEXP (pat, 0, 3), 0); - recog_dup_num[2] = 0; - break; - - case 247: - ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1), 0)); - ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 1), 0)); - ro[4] = *(ro_loc[4] = &XEXP (XVECEXP (pat, 0, 2), 0)); - recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 5), 0); - recog_dup_num[0] = 3; - recog_dup_loc[1] = &XEXP (XVECEXP (pat, 0, 4), 0); - recog_dup_num[1] = 2; - recog_dup_loc[2] = &XEXP (XVECEXP (pat, 0, 3), 0); - recog_dup_num[2] = 1; - break; - - case 245: - ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 0), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0)); - ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 2), 0)); - ro[4] = *(ro_loc[4] = &XEXP (XVECEXP (pat, 0, 3), 0)); - recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 5), 0); - recog_dup_num[0] = 1; - recog_dup_loc[1] = &XEXP (XVECEXP (pat, 0, 4), 0); - recog_dup_num[1] = 0; - break; - - case 243: - case 242: - break; - - case 241: - ro[0] = *(ro_loc[0] = &XVECEXP (pat, 0, 0)); - break; - - case 239: - ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 0), 0)); - ro[1] = *(ro_loc[1] = &XVECEXP (pat, 0, 1)); - ro[2] = *(ro_loc[2] = &XVECEXP (pat, 0, 2)); - break; - - case 238: - ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); - ro[1] = *(ro_loc[1] = &XVECEXP (pat, 0, 1)); - ro[2] = *(ro_loc[2] = &XVECEXP (pat, 0, 2)); - break; - - case 236: - ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (pat, 1), 0), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 1)); - break; - - case 233: - ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1)); - ro[3] = const0_rtx; - ro_loc[3] = &junk; - ro[4] = *(ro_loc[4] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 1)); - break; - - case 232: - ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1)); - ro[3] = const0_rtx; - ro_loc[3] = &junk; - ro[4] = *(ro_loc[4] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 1)); - break; - - case 227: - ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 0), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (pat, 0, 0), 1)); - ro[2] = const0_rtx; - ro_loc[2] = &junk; - ro[3] = *(ro_loc[3] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 1)); - break; - - case 226: - ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (pat, 0, 0), 1)); - ro[2] = const0_rtx; - ro_loc[2] = &junk; - ro[3] = *(ro_loc[3] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 1)); - break; - - case 224: - ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 1)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 0), 0)); - break; - - case 223: - ro[0] = *(ro_loc[0] = &XEXP (XEXP (XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1), 0), 0), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1), 0), 1), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0)); - break; - - case 220: - ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 1), 0)); - break; - - case 219: - case 218: - case 217: - case 216: - case 215: - case 214: - case 213: - case 212: - case 211: - case 210: - ro[0] = *(ro_loc[0] = &XEXP (XEXP (XEXP (pat, 1), 2), 0)); - break; - - case 209: - case 207: - case 205: - case 203: - case 201: - case 199: - case 197: - case 195: - case 193: - case 191: - ro[0] = *(ro_loc[0] = &XEXP (XEXP (XEXP (pat, 1), 1), 0)); - break; - - case 189: - case 187: - case 185: - case 183: - case 181: - case 179: - case 177: - case 175: - case 173: - case 171: - ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); - break; - - case 169: - case 168: - ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 1), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 1)); - ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 2)); - break; - - case 167: - ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 1), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 2)); - break; - - case 166: - ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XEXP (XEXP (pat, 1), 1), 1)); - break; - - case 165: - ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (pat, 1), 0), 1)); - ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 1)); - break; - - case 164: - ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 0), 0)); - ro[1] = const0_rtx; - ro_loc[1] = &junk; - ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 0), 2)); - ro[3] = *(ro_loc[3] = &XEXP (pat, 1)); - break; - - case 154: - case 148: - case 142: - ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1)); - recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 1), 0); - recog_dup_num[0] = 2; - break; - - case 136: - case 133: - ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); - ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (XEXP (pat, 1), 0, 0), 0)); - break; - - case 135: - case 134: - case 132: - case 131: - ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); - ro[1] = *(ro_loc[1] = &XVECEXP (XEXP (pat, 1), 0, 0)); - break; - - case 252: - case 250: - case 130: - case 127: - case 124: - ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (pat, 1), 0), 0)); - break; - - case 108: - case 107: - case 106: - case 105: - ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1)); - ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 1), 0)); - recog_dup_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 0); - recog_dup_num[0] = 1; - recog_dup_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 1); - recog_dup_num[1] = 2; - break; - - case 98: - ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (pat, 1), 0), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XEXP (XEXP (pat, 1), 1), 0)); - break; - - case 235: - case 163: - case 162: - case 161: - case 160: - case 159: - case 158: - case 157: - case 156: - case 155: - case 153: - case 151: - case 150: - case 149: - case 147: - case 145: - case 144: - case 143: - case 141: - case 117: - case 116: - case 115: - case 114: - case 113: - case 112: - case 111: - case 110: - case 109: - case 102: - case 101: - case 97: - case 96: - case 95: - case 94: - case 91: - case 90: - case 89: - case 88: - case 84: - case 83: - case 82: - case 81: - ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 1)); - break; - - case 72: - case 71: - ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0)); - ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 2), 0)); - ro[4] = *(ro_loc[4] = &XEXP (XVECEXP (pat, 0, 3), 0)); - break; - - case 68: - case 67: - ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 2), 0)); - ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 3), 0)); - ro[4] = *(ro_loc[4] = &XEXP (XVECEXP (pat, 0, 4), 0)); - recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 1), 0); - recog_dup_num[0] = 1; - break; - - case 62: - ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0)); - break; - - case 139: - case 138: - case 137: - case 129: - case 128: - case 126: - case 125: - case 123: - case 122: - case 121: - case 120: - case 119: - case 118: - case 80: - case 79: - case 78: - case 77: - case 60: - case 59: - case 58: - case 57: - case 56: - case 55: - case 54: - case 53: - case 52: - ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 0)); - break; - - case 48: - ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (pat, 0, 0), 1)); - recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 1), 0); - recog_dup_num[0] = 1; - recog_dup_loc[1] = &XEXP (XVECEXP (pat, 0, 1), 1); - recog_dup_num[1] = 0; - break; - - case 230: - case 44: - case 41: - ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 0), 0)); - ro[1] = *(ro_loc[1] = &XEXP (pat, 1)); - break; - - case 229: - case 85: - case 51: - case 50: - case 49: - case 47: - case 46: - case 45: - case 43: - case 42: - case 40: - case 39: - case 38: - case 36: - case 35: - ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); - ro[1] = *(ro_loc[1] = &XEXP (pat, 1)); - break; - - case 25: - case 21: - ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1)); - ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0)); - break; - - case 24: - case 20: - case 18: - ro[0] = *(ro_loc[0] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1)); - ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 0), 1)); - ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 1), 0)); - break; - - case 23: - case 19: - case 17: - ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1), 0)); - ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 0), 1)); - ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 1), 0)); - break; - - case 22: - case 16: - ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1)); - ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 0), 1)); - ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 1), 0)); - break; - - case 34: - case 33: - case 32: - case 14: - case 12: - case 10: - ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 1), 0)); - ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 1)); - break; - - case 8: - case 6: - ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 1)); - ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (pat, 0, 1), 0)); - break; - - case 221: - case 4: - case 2: - case 0: - ro[0] = *(ro_loc[0] = &XEXP (pat, 1)); - break; - - default: - abort (); - } -} diff --git a/gnu/usr.bin/cc/common/insn-flags.h b/gnu/usr.bin/cc/common/insn-flags.h deleted file mode 100644 index 9070b93d11..0000000000 --- a/gnu/usr.bin/cc/common/insn-flags.h +++ /dev/null @@ -1,510 +0,0 @@ -/* Generated automatically by the program `genflags' -from the machine description file `md'. */ - -#define HAVE_tstsi_1 1 -#define HAVE_tstsi 1 -#define HAVE_tsthi_1 1 -#define HAVE_tsthi 1 -#define HAVE_tstqi_1 1 -#define HAVE_tstqi 1 -#define HAVE_tstsf_cc (TARGET_80387 && ! TARGET_IEEE_FP) -#define HAVE_tstsf (TARGET_80387 && ! TARGET_IEEE_FP) -#define HAVE_tstdf_cc (TARGET_80387 && ! TARGET_IEEE_FP) -#define HAVE_tstdf (TARGET_80387 && ! TARGET_IEEE_FP) -#define HAVE_cmpsi_1 (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) -#define HAVE_cmpsi 1 -#define HAVE_cmphi_1 (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) -#define HAVE_cmphi 1 -#define HAVE_cmpqi_1 (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) -#define HAVE_cmpqi 1 -#define HAVE_cmpsf_cc_1 (TARGET_80387 \ - && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)) -#define HAVE_cmpdf (TARGET_80387) -#define HAVE_cmpsf (TARGET_80387) -#define HAVE_cmpdf_cc (TARGET_80387) -#define HAVE_cmpdf_ccfpeq (TARGET_80387) -#define HAVE_cmpsf_cc (TARGET_80387) -#define HAVE_cmpsf_ccfpeq (TARGET_80387) -#define HAVE_movsi 1 -#define HAVE_movhi 1 -#define HAVE_movstricthi 1 -#define HAVE_movqi 1 -#define HAVE_movstrictqi 1 -#define HAVE_movsf 1 -#define HAVE_swapdf 1 -#define HAVE_movdf 1 -#define HAVE_movdi 1 -#define HAVE_zero_extendhisi2 1 -#define HAVE_zero_extendqihi2 1 -#define HAVE_zero_extendqisi2 1 -#define HAVE_zero_extendsidi2 1 -#define HAVE_extendsidi2 1 -#define HAVE_extendhisi2 1 -#define HAVE_extendqihi2 1 -#define HAVE_extendqisi2 1 -#define HAVE_extendsfdf2 (TARGET_80387) -#define HAVE_truncdfsf2 (TARGET_80387) -#define HAVE_fixuns_truncdfsi2 (TARGET_80387) -#define HAVE_fixuns_truncsfsi2 (TARGET_80387) -#define HAVE_fix_truncdfdi2 (TARGET_80387) -#define HAVE_fix_truncsfdi2 (TARGET_80387) -#define HAVE_fix_truncdfsi2 (TARGET_80387) -#define HAVE_fix_truncsfsi2 (TARGET_80387) -#define HAVE_floatsisf2 (TARGET_80387) -#define HAVE_floatdisf2 (TARGET_80387) -#define HAVE_floatsidf2 (TARGET_80387) -#define HAVE_floatdidf2 (TARGET_80387) -#define HAVE_adddi3 1 -#define HAVE_addsi3 1 -#define HAVE_addhi3 1 -#define HAVE_addqi3 1 -#define HAVE_adddf3 (TARGET_80387) -#define HAVE_addsf3 (TARGET_80387) -#define HAVE_subdi3 1 -#define HAVE_subsi3 1 -#define HAVE_subhi3 1 -#define HAVE_subqi3 1 -#define HAVE_subdf3 (TARGET_80387) -#define HAVE_subsf3 (TARGET_80387) -#define HAVE_mulhi3 1 -#define HAVE_mulsi3 1 -#define HAVE_muldf3 (TARGET_80387) -#define HAVE_mulsf3 (TARGET_80387) -#define HAVE_divqi3 1 -#define HAVE_udivqi3 1 -#define HAVE_divdf3 (TARGET_80387) -#define HAVE_divsf3 (TARGET_80387) -#define HAVE_divmodsi4 1 -#define HAVE_divmodhi4 1 -#define HAVE_udivmodsi4 1 -#define HAVE_udivmodhi4 1 -#define HAVE_andsi3 1 -#define HAVE_andhi3 1 -#define HAVE_andqi3 1 -#define HAVE_iorsi3 1 -#define HAVE_iorhi3 1 -#define HAVE_iorqi3 1 -#define HAVE_xorsi3 1 -#define HAVE_xorhi3 1 -#define HAVE_xorqi3 1 -#define HAVE_negdi2 1 -#define HAVE_negsi2 1 -#define HAVE_neghi2 1 -#define HAVE_negqi2 1 -#define HAVE_negsf2 (TARGET_80387) -#define HAVE_negdf2 (TARGET_80387) -#define HAVE_abssf2 (TARGET_80387) -#define HAVE_absdf2 (TARGET_80387) -#define HAVE_sqrtsf2 (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) -#define HAVE_sqrtdf2 (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) -#define HAVE_sindf2 (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) -#define HAVE_sinsf2 (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) -#define HAVE_cosdf2 (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) -#define HAVE_cossf2 (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) -#define HAVE_one_cmplsi2 1 -#define HAVE_one_cmplhi2 1 -#define HAVE_one_cmplqi2 1 -#define HAVE_ashldi3 1 -#define HAVE_ashldi3_const_int 1 -#define HAVE_ashldi3_non_const_int 1 -#define HAVE_ashlsi3 1 -#define HAVE_ashlhi3 1 -#define HAVE_ashlqi3 1 -#define HAVE_ashrdi3 1 -#define HAVE_ashrdi3_const_int 1 -#define HAVE_ashrdi3_non_const_int 1 -#define HAVE_ashrsi3 1 -#define HAVE_ashrhi3 1 -#define HAVE_ashrqi3 1 -#define HAVE_lshrdi3 1 -#define HAVE_lshrdi3_const_int 1 -#define HAVE_lshrdi3_non_const_int 1 -#define HAVE_lshrsi3 1 -#define HAVE_lshrhi3 1 -#define HAVE_lshrqi3 1 -#define HAVE_rotlsi3 1 -#define HAVE_rotlhi3 1 -#define HAVE_rotlqi3 1 -#define HAVE_rotrsi3 1 -#define HAVE_rotrhi3 1 -#define HAVE_rotrqi3 1 -#define HAVE_seq 1 -#define HAVE_sne 1 -#define HAVE_sgt 1 -#define HAVE_sgtu 1 -#define HAVE_slt 1 -#define HAVE_sltu 1 -#define HAVE_sge 1 -#define HAVE_sgeu 1 -#define HAVE_sle 1 -#define HAVE_sleu 1 -#define HAVE_beq 1 -#define HAVE_bne 1 -#define HAVE_bgt 1 -#define HAVE_bgtu 1 -#define HAVE_blt 1 -#define HAVE_bltu 1 -#define HAVE_bge 1 -#define HAVE_bgeu 1 -#define HAVE_ble 1 -#define HAVE_bleu 1 -#define HAVE_jump 1 -#define HAVE_indirect_jump 1 -#define HAVE_casesi (flag_pic) -#define HAVE_tablejump 1 -#define HAVE_call_pop 1 -#define HAVE_call 1 -#define HAVE_call_value_pop 1 -#define HAVE_call_value 1 -#define HAVE_untyped_call 1 -#define HAVE_untyped_return 1 -#define HAVE_update_return 1 -#define HAVE_return (simple_386_epilogue ()) -#define HAVE_nop 1 -#define HAVE_movstrsi 1 -#define HAVE_cmpstrsi 1 -#define HAVE_ffssi2 1 -#define HAVE_ffshi2 1 -#define HAVE_strlensi 1 - -#ifndef NO_MD_PROTOTYPES -extern rtx gen_tstsi_1 PROTO((rtx)); -extern rtx gen_tstsi PROTO((rtx)); -extern rtx gen_tsthi_1 PROTO((rtx)); -extern rtx gen_tsthi PROTO((rtx)); -extern rtx gen_tstqi_1 PROTO((rtx)); -extern rtx gen_tstqi PROTO((rtx)); -extern rtx gen_tstsf_cc PROTO((rtx)); -extern rtx gen_tstsf PROTO((rtx)); -extern rtx gen_tstdf_cc PROTO((rtx)); -extern rtx gen_tstdf PROTO((rtx)); -extern rtx gen_cmpsi_1 PROTO((rtx, rtx)); -extern rtx gen_cmpsi PROTO((rtx, rtx)); -extern rtx gen_cmphi_1 PROTO((rtx, rtx)); -extern rtx gen_cmphi PROTO((rtx, rtx)); -extern rtx gen_cmpqi_1 PROTO((rtx, rtx)); -extern rtx gen_cmpqi PROTO((rtx, rtx)); -extern rtx gen_cmpsf_cc_1 PROTO((rtx, rtx, rtx)); -extern rtx gen_cmpdf PROTO((rtx, rtx)); -extern rtx gen_cmpsf PROTO((rtx, rtx)); -extern rtx gen_cmpdf_cc PROTO((rtx, rtx)); -extern rtx gen_cmpdf_ccfpeq PROTO((rtx, rtx)); -extern rtx gen_cmpsf_cc PROTO((rtx, rtx)); -extern rtx gen_cmpsf_ccfpeq PROTO((rtx, rtx)); -extern rtx gen_movsi PROTO((rtx, rtx)); -extern rtx gen_movhi PROTO((rtx, rtx)); -extern rtx gen_movstricthi PROTO((rtx, rtx)); -extern rtx gen_movqi PROTO((rtx, rtx)); -extern rtx gen_movstrictqi PROTO((rtx, rtx)); -extern rtx gen_movsf PROTO((rtx, rtx)); -extern rtx gen_swapdf PROTO((rtx, rtx)); -extern rtx gen_movdf PROTO((rtx, rtx)); -extern rtx gen_movdi PROTO((rtx, rtx)); -extern rtx gen_zero_extendhisi2 PROTO((rtx, rtx)); -extern rtx gen_zero_extendqihi2 PROTO((rtx, rtx)); -extern rtx gen_zero_extendqisi2 PROTO((rtx, rtx)); -extern rtx gen_zero_extendsidi2 PROTO((rtx, rtx)); -extern rtx gen_extendsidi2 PROTO((rtx, rtx)); -extern rtx gen_extendhisi2 PROTO((rtx, rtx)); -extern rtx gen_extendqihi2 PROTO((rtx, rtx)); -extern rtx gen_extendqisi2 PROTO((rtx, rtx)); -extern rtx gen_extendsfdf2 PROTO((rtx, rtx)); -extern rtx gen_truncdfsf2 PROTO((rtx, rtx)); -extern rtx gen_fixuns_truncdfsi2 PROTO((rtx, rtx)); -extern rtx gen_fixuns_truncsfsi2 PROTO((rtx, rtx)); -extern rtx gen_fix_truncdfdi2 PROTO((rtx, rtx)); -extern rtx gen_fix_truncsfdi2 PROTO((rtx, rtx)); -extern rtx gen_fix_truncdfsi2 PROTO((rtx, rtx)); -extern rtx gen_fix_truncsfsi2 PROTO((rtx, rtx)); -extern rtx gen_floatsisf2 PROTO((rtx, rtx)); -extern rtx gen_floatdisf2 PROTO((rtx, rtx)); -extern rtx gen_floatsidf2 PROTO((rtx, rtx)); -extern rtx gen_floatdidf2 PROTO((rtx, rtx)); -extern rtx gen_adddi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_addsi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_addhi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_addqi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_adddf3 PROTO((rtx, rtx, rtx)); -extern rtx gen_addsf3 PROTO((rtx, rtx, rtx)); -extern rtx gen_subdi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_subsi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_subhi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_subqi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_subdf3 PROTO((rtx, rtx, rtx)); -extern rtx gen_subsf3 PROTO((rtx, rtx, rtx)); -extern rtx gen_mulhi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_mulsi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_muldf3 PROTO((rtx, rtx, rtx)); -extern rtx gen_mulsf3 PROTO((rtx, rtx, rtx)); -extern rtx gen_divqi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_udivqi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_divdf3 PROTO((rtx, rtx, rtx)); -extern rtx gen_divsf3 PROTO((rtx, rtx, rtx)); -extern rtx gen_divmodsi4 PROTO((rtx, rtx, rtx, rtx)); -extern rtx gen_divmodhi4 PROTO((rtx, rtx, rtx, rtx)); -extern rtx gen_udivmodsi4 PROTO((rtx, rtx, rtx, rtx)); -extern rtx gen_udivmodhi4 PROTO((rtx, rtx, rtx, rtx)); -extern rtx gen_andsi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_andhi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_andqi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_iorsi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_iorhi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_iorqi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_xorsi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_xorhi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_xorqi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_negdi2 PROTO((rtx, rtx)); -extern rtx gen_negsi2 PROTO((rtx, rtx)); -extern rtx gen_neghi2 PROTO((rtx, rtx)); -extern rtx gen_negqi2 PROTO((rtx, rtx)); -extern rtx gen_negsf2 PROTO((rtx, rtx)); -extern rtx gen_negdf2 PROTO((rtx, rtx)); -extern rtx gen_abssf2 PROTO((rtx, rtx)); -extern rtx gen_absdf2 PROTO((rtx, rtx)); -extern rtx gen_sqrtsf2 PROTO((rtx, rtx)); -extern rtx gen_sqrtdf2 PROTO((rtx, rtx)); -extern rtx gen_sindf2 PROTO((rtx, rtx)); -extern rtx gen_sinsf2 PROTO((rtx, rtx)); -extern rtx gen_cosdf2 PROTO((rtx, rtx)); -extern rtx gen_cossf2 PROTO((rtx, rtx)); -extern rtx gen_one_cmplsi2 PROTO((rtx, rtx)); -extern rtx gen_one_cmplhi2 PROTO((rtx, rtx)); -extern rtx gen_one_cmplqi2 PROTO((rtx, rtx)); -extern rtx gen_ashldi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_ashldi3_const_int PROTO((rtx, rtx, rtx)); -extern rtx gen_ashldi3_non_const_int PROTO((rtx, rtx, rtx)); -extern rtx gen_ashlsi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_ashlhi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_ashlqi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_ashrdi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_ashrdi3_const_int PROTO((rtx, rtx, rtx)); -extern rtx gen_ashrdi3_non_const_int PROTO((rtx, rtx, rtx)); -extern rtx gen_ashrsi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_ashrhi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_ashrqi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_lshrdi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_lshrdi3_const_int PROTO((rtx, rtx, rtx)); -extern rtx gen_lshrdi3_non_const_int PROTO((rtx, rtx, rtx)); -extern rtx gen_lshrsi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_lshrhi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_lshrqi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_rotlsi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_rotlhi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_rotlqi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_rotrsi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_rotrhi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_rotrqi3 PROTO((rtx, rtx, rtx)); -extern rtx gen_seq PROTO((rtx)); -extern rtx gen_sne PROTO((rtx)); -extern rtx gen_sgt PROTO((rtx)); -extern rtx gen_sgtu PROTO((rtx)); -extern rtx gen_slt PROTO((rtx)); -extern rtx gen_sltu PROTO((rtx)); -extern rtx gen_sge PROTO((rtx)); -extern rtx gen_sgeu PROTO((rtx)); -extern rtx gen_sle PROTO((rtx)); -extern rtx gen_sleu PROTO((rtx)); -extern rtx gen_beq PROTO((rtx)); -extern rtx gen_bne PROTO((rtx)); -extern rtx gen_bgt PROTO((rtx)); -extern rtx gen_bgtu PROTO((rtx)); -extern rtx gen_blt PROTO((rtx)); -extern rtx gen_bltu PROTO((rtx)); -extern rtx gen_bge PROTO((rtx)); -extern rtx gen_bgeu PROTO((rtx)); -extern rtx gen_ble PROTO((rtx)); -extern rtx gen_bleu PROTO((rtx)); -extern rtx gen_jump PROTO((rtx)); -extern rtx gen_indirect_jump PROTO((rtx)); -extern rtx gen_casesi PROTO((rtx, rtx, rtx, rtx, rtx)); -extern rtx gen_tablejump PROTO((rtx, rtx)); -extern rtx gen_untyped_call PROTO((rtx, rtx, rtx)); -extern rtx gen_untyped_return PROTO((rtx, rtx)); -extern rtx gen_update_return PROTO((rtx)); -extern rtx gen_return PROTO((void)); -extern rtx gen_nop PROTO((void)); -extern rtx gen_movstrsi PROTO((rtx, rtx, rtx, rtx)); -extern rtx gen_cmpstrsi PROTO((rtx, rtx, rtx, rtx, rtx)); -extern rtx gen_ffssi2 PROTO((rtx, rtx)); -extern rtx gen_ffshi2 PROTO((rtx, rtx)); -extern rtx gen_strlensi PROTO((rtx, rtx, rtx, rtx)); - -#ifdef MD_CALL_PROTOTYPES -extern rtx gen_call_pop PROTO((rtx, rtx, rtx)); -extern rtx gen_call PROTO((rtx, rtx)); -extern rtx gen_call_value_pop PROTO((rtx, rtx, rtx, rtx)); -extern rtx gen_call_value PROTO((rtx, rtx, rtx)); - -#else /* !MD_CALL_PROTOTYPES */ -extern rtx gen_call_pop (); -extern rtx gen_call (); -extern rtx gen_call_value_pop (); -extern rtx gen_call_value (); -#endif /* !MD_CALL_PROTOTYPES */ - -#else /* NO_MD_PROTOTYPES */ -extern rtx gen_tstsi_1 (); -extern rtx gen_tstsi (); -extern rtx gen_tsthi_1 (); -extern rtx gen_tsthi (); -extern rtx gen_tstqi_1 (); -extern rtx gen_tstqi (); -extern rtx gen_tstsf_cc (); -extern rtx gen_tstsf (); -extern rtx gen_tstdf_cc (); -extern rtx gen_tstdf (); -extern rtx gen_cmpsi_1 (); -extern rtx gen_cmpsi (); -extern rtx gen_cmphi_1 (); -extern rtx gen_cmphi (); -extern rtx gen_cmpqi_1 (); -extern rtx gen_cmpqi (); -extern rtx gen_cmpsf_cc_1 (); -extern rtx gen_cmpdf (); -extern rtx gen_cmpsf (); -extern rtx gen_cmpdf_cc (); -extern rtx gen_cmpdf_ccfpeq (); -extern rtx gen_cmpsf_cc (); -extern rtx gen_cmpsf_ccfpeq (); -extern rtx gen_movsi (); -extern rtx gen_movhi (); -extern rtx gen_movstricthi (); -extern rtx gen_movqi (); -extern rtx gen_movstrictqi (); -extern rtx gen_movsf (); -extern rtx gen_swapdf (); -extern rtx gen_movdf (); -extern rtx gen_movdi (); -extern rtx gen_zero_extendhisi2 (); -extern rtx gen_zero_extendqihi2 (); -extern rtx gen_zero_extendqisi2 (); -extern rtx gen_zero_extendsidi2 (); -extern rtx gen_extendsidi2 (); -extern rtx gen_extendhisi2 (); -extern rtx gen_extendqihi2 (); -extern rtx gen_extendqisi2 (); -extern rtx gen_extendsfdf2 (); -extern rtx gen_truncdfsf2 (); -extern rtx gen_fixuns_truncdfsi2 (); -extern rtx gen_fixuns_truncsfsi2 (); -extern rtx gen_fix_truncdfdi2 (); -extern rtx gen_fix_truncsfdi2 (); -extern rtx gen_fix_truncdfsi2 (); -extern rtx gen_fix_truncsfsi2 (); -extern rtx gen_floatsisf2 (); -extern rtx gen_floatdisf2 (); -extern rtx gen_floatsidf2 (); -extern rtx gen_floatdidf2 (); -extern rtx gen_adddi3 (); -extern rtx gen_addsi3 (); -extern rtx gen_addhi3 (); -extern rtx gen_addqi3 (); -extern rtx gen_adddf3 (); -extern rtx gen_addsf3 (); -extern rtx gen_subdi3 (); -extern rtx gen_subsi3 (); -extern rtx gen_subhi3 (); -extern rtx gen_subqi3 (); -extern rtx gen_subdf3 (); -extern rtx gen_subsf3 (); -extern rtx gen_mulhi3 (); -extern rtx gen_mulsi3 (); -extern rtx gen_muldf3 (); -extern rtx gen_mulsf3 (); -extern rtx gen_divqi3 (); -extern rtx gen_udivqi3 (); -extern rtx gen_divdf3 (); -extern rtx gen_divsf3 (); -extern rtx gen_divmodsi4 (); -extern rtx gen_divmodhi4 (); -extern rtx gen_udivmodsi4 (); -extern rtx gen_udivmodhi4 (); -extern rtx gen_andsi3 (); -extern rtx gen_andhi3 (); -extern rtx gen_andqi3 (); -extern rtx gen_iorsi3 (); -extern rtx gen_iorhi3 (); -extern rtx gen_iorqi3 (); -extern rtx gen_xorsi3 (); -extern rtx gen_xorhi3 (); -extern rtx gen_xorqi3 (); -extern rtx gen_negdi2 (); -extern rtx gen_negsi2 (); -extern rtx gen_neghi2 (); -extern rtx gen_negqi2 (); -extern rtx gen_negsf2 (); -extern rtx gen_negdf2 (); -extern rtx gen_abssf2 (); -extern rtx gen_absdf2 (); -extern rtx gen_sqrtsf2 (); -extern rtx gen_sqrtdf2 (); -extern rtx gen_sindf2 (); -extern rtx gen_sinsf2 (); -extern rtx gen_cosdf2 (); -extern rtx gen_cossf2 (); -extern rtx gen_one_cmplsi2 (); -extern rtx gen_one_cmplhi2 (); -extern rtx gen_one_cmplqi2 (); -extern rtx gen_ashldi3 (); -extern rtx gen_ashldi3_const_int (); -extern rtx gen_ashldi3_non_const_int (); -extern rtx gen_ashlsi3 (); -extern rtx gen_ashlhi3 (); -extern rtx gen_ashlqi3 (); -extern rtx gen_ashrdi3 (); -extern rtx gen_ashrdi3_const_int (); -extern rtx gen_ashrdi3_non_const_int (); -extern rtx gen_ashrsi3 (); -extern rtx gen_ashrhi3 (); -extern rtx gen_ashrqi3 (); -extern rtx gen_lshrdi3 (); -extern rtx gen_lshrdi3_const_int (); -extern rtx gen_lshrdi3_non_const_int (); -extern rtx gen_lshrsi3 (); -extern rtx gen_lshrhi3 (); -extern rtx gen_lshrqi3 (); -extern rtx gen_rotlsi3 (); -extern rtx gen_rotlhi3 (); -extern rtx gen_rotlqi3 (); -extern rtx gen_rotrsi3 (); -extern rtx gen_rotrhi3 (); -extern rtx gen_rotrqi3 (); -extern rtx gen_seq (); -extern rtx gen_sne (); -extern rtx gen_sgt (); -extern rtx gen_sgtu (); -extern rtx gen_slt (); -extern rtx gen_sltu (); -extern rtx gen_sge (); -extern rtx gen_sgeu (); -extern rtx gen_sle (); -extern rtx gen_sleu (); -extern rtx gen_beq (); -extern rtx gen_bne (); -extern rtx gen_bgt (); -extern rtx gen_bgtu (); -extern rtx gen_blt (); -extern rtx gen_bltu (); -extern rtx gen_bge (); -extern rtx gen_bgeu (); -extern rtx gen_ble (); -extern rtx gen_bleu (); -extern rtx gen_jump (); -extern rtx gen_indirect_jump (); -extern rtx gen_casesi (); -extern rtx gen_tablejump (); -extern rtx gen_untyped_call (); -extern rtx gen_untyped_return (); -extern rtx gen_update_return (); -extern rtx gen_return (); -extern rtx gen_nop (); -extern rtx gen_movstrsi (); -extern rtx gen_cmpstrsi (); -extern rtx gen_ffssi2 (); -extern rtx gen_ffshi2 (); -extern rtx gen_strlensi (); -extern rtx gen_call_pop (); -extern rtx gen_call (); -extern rtx gen_call_value_pop (); -extern rtx gen_call_value (); -#endif /* NO_MD_PROTOTYPES */ diff --git a/gnu/usr.bin/cc/common/insn-opinit.c b/gnu/usr.bin/cc/common/insn-opinit.c deleted file mode 100644 index 51d9d811e2..0000000000 --- a/gnu/usr.bin/cc/common/insn-opinit.c +++ /dev/null @@ -1,178 +0,0 @@ -/* Generated automatically by the program `genopinit' -from the machine description file `md'. */ - -#include "config.h" -#include "rtl.h" -#include "flags.h" -#include "insn-flags.h" -#include "insn-codes.h" -#include "insn-config.h" -#include "recog.h" -#include "expr.h" -#include "reload.h" - -void -init_all_optabs () -{ - tst_optab->handlers[(int) SImode].insn_code = CODE_FOR_tstsi; - tst_optab->handlers[(int) HImode].insn_code = CODE_FOR_tsthi; - tst_optab->handlers[(int) QImode].insn_code = CODE_FOR_tstqi; - if (HAVE_tstsf) - tst_optab->handlers[(int) SFmode].insn_code = CODE_FOR_tstsf; - if (HAVE_tstdf) - tst_optab->handlers[(int) DFmode].insn_code = CODE_FOR_tstdf; - cmp_optab->handlers[(int) SImode].insn_code = CODE_FOR_cmpsi; - cmp_optab->handlers[(int) HImode].insn_code = CODE_FOR_cmphi; - cmp_optab->handlers[(int) QImode].insn_code = CODE_FOR_cmpqi; - if (HAVE_cmpdf) - cmp_optab->handlers[(int) DFmode].insn_code = CODE_FOR_cmpdf; - if (HAVE_cmpsf) - cmp_optab->handlers[(int) SFmode].insn_code = CODE_FOR_cmpsf; - mov_optab->handlers[(int) SImode].insn_code = CODE_FOR_movsi; - mov_optab->handlers[(int) HImode].insn_code = CODE_FOR_movhi; - movstrict_optab->handlers[(int) HImode].insn_code = CODE_FOR_movstricthi; - mov_optab->handlers[(int) QImode].insn_code = CODE_FOR_movqi; - movstrict_optab->handlers[(int) QImode].insn_code = CODE_FOR_movstrictqi; - mov_optab->handlers[(int) SFmode].insn_code = CODE_FOR_movsf; - mov_optab->handlers[(int) DFmode].insn_code = CODE_FOR_movdf; - mov_optab->handlers[(int) DImode].insn_code = CODE_FOR_movdi; - extendtab[(int) SImode][(int) HImode][1] = CODE_FOR_zero_extendhisi2; - extendtab[(int) HImode][(int) QImode][1] = CODE_FOR_zero_extendqihi2; - extendtab[(int) SImode][(int) QImode][1] = CODE_FOR_zero_extendqisi2; - extendtab[(int) DImode][(int) SImode][1] = CODE_FOR_zero_extendsidi2; - extendtab[(int) DImode][(int) SImode][0] = CODE_FOR_extendsidi2; - extendtab[(int) SImode][(int) HImode][0] = CODE_FOR_extendhisi2; - extendtab[(int) HImode][(int) QImode][0] = CODE_FOR_extendqihi2; - extendtab[(int) SImode][(int) QImode][0] = CODE_FOR_extendqisi2; - if (HAVE_extendsfdf2) - extendtab[(int) DFmode][(int) SFmode][0] = CODE_FOR_extendsfdf2; - if (HAVE_fixuns_truncdfsi2) - fixtrunctab[(int) DFmode][(int) SImode][1] = CODE_FOR_fixuns_truncdfsi2; - if (HAVE_fixuns_truncsfsi2) - fixtrunctab[(int) SFmode][(int) SImode][1] = CODE_FOR_fixuns_truncsfsi2; - if (HAVE_fix_truncdfdi2) - fixtrunctab[(int) DFmode][(int) DImode][0] = CODE_FOR_fix_truncdfdi2; - if (HAVE_fix_truncsfdi2) - fixtrunctab[(int) SFmode][(int) DImode][0] = CODE_FOR_fix_truncsfdi2; - if (HAVE_fix_truncdfsi2) - fixtrunctab[(int) DFmode][(int) SImode][0] = CODE_FOR_fix_truncdfsi2; - if (HAVE_fix_truncsfsi2) - fixtrunctab[(int) SFmode][(int) SImode][0] = CODE_FOR_fix_truncsfsi2; - if (HAVE_floatsisf2) - floattab[(int) SFmode][(int) SImode][0] = CODE_FOR_floatsisf2; - if (HAVE_floatdisf2) - floattab[(int) SFmode][(int) DImode][0] = CODE_FOR_floatdisf2; - if (HAVE_floatsidf2) - floattab[(int) DFmode][(int) SImode][0] = CODE_FOR_floatsidf2; - if (HAVE_floatdidf2) - floattab[(int) DFmode][(int) DImode][0] = CODE_FOR_floatdidf2; - add_optab->handlers[(int) DImode].insn_code = CODE_FOR_adddi3; - add_optab->handlers[(int) SImode].insn_code = CODE_FOR_addsi3; - add_optab->handlers[(int) HImode].insn_code = CODE_FOR_addhi3; - add_optab->handlers[(int) QImode].insn_code = CODE_FOR_addqi3; - if (HAVE_adddf3) - add_optab->handlers[(int) DFmode].insn_code = CODE_FOR_adddf3; - if (HAVE_addsf3) - add_optab->handlers[(int) SFmode].insn_code = CODE_FOR_addsf3; - sub_optab->handlers[(int) DImode].insn_code = CODE_FOR_subdi3; - sub_optab->handlers[(int) SImode].insn_code = CODE_FOR_subsi3; - sub_optab->handlers[(int) HImode].insn_code = CODE_FOR_subhi3; - sub_optab->handlers[(int) QImode].insn_code = CODE_FOR_subqi3; - if (HAVE_subdf3) - sub_optab->handlers[(int) DFmode].insn_code = CODE_FOR_subdf3; - if (HAVE_subsf3) - sub_optab->handlers[(int) SFmode].insn_code = CODE_FOR_subsf3; - smul_optab->handlers[(int) HImode].insn_code = CODE_FOR_mulhi3; - smul_optab->handlers[(int) SImode].insn_code = CODE_FOR_mulsi3; - if (HAVE_muldf3) - smul_optab->handlers[(int) DFmode].insn_code = CODE_FOR_muldf3; - if (HAVE_mulsf3) - smul_optab->handlers[(int) SFmode].insn_code = CODE_FOR_mulsf3; - sdiv_optab->handlers[(int) QImode].insn_code = CODE_FOR_divqi3; - udiv_optab->handlers[(int) QImode].insn_code = CODE_FOR_udivqi3; - if (HAVE_divdf3) - flodiv_optab->handlers[(int) DFmode].insn_code = CODE_FOR_divdf3; - if (HAVE_divsf3) - flodiv_optab->handlers[(int) SFmode].insn_code = CODE_FOR_divsf3; - sdivmod_optab->handlers[(int) SImode].insn_code = CODE_FOR_divmodsi4; - sdivmod_optab->handlers[(int) HImode].insn_code = CODE_FOR_divmodhi4; - udivmod_optab->handlers[(int) SImode].insn_code = CODE_FOR_udivmodsi4; - udivmod_optab->handlers[(int) HImode].insn_code = CODE_FOR_udivmodhi4; - and_optab->handlers[(int) SImode].insn_code = CODE_FOR_andsi3; - and_optab->handlers[(int) HImode].insn_code = CODE_FOR_andhi3; - and_optab->handlers[(int) QImode].insn_code = CODE_FOR_andqi3; - ior_optab->handlers[(int) SImode].insn_code = CODE_FOR_iorsi3; - ior_optab->handlers[(int) HImode].insn_code = CODE_FOR_iorhi3; - ior_optab->handlers[(int) QImode].insn_code = CODE_FOR_iorqi3; - xor_optab->handlers[(int) SImode].insn_code = CODE_FOR_xorsi3; - xor_optab->handlers[(int) HImode].insn_code = CODE_FOR_xorhi3; - xor_optab->handlers[(int) QImode].insn_code = CODE_FOR_xorqi3; - neg_optab->handlers[(int) DImode].insn_code = CODE_FOR_negdi2; - neg_optab->handlers[(int) SImode].insn_code = CODE_FOR_negsi2; - neg_optab->handlers[(int) HImode].insn_code = CODE_FOR_neghi2; - neg_optab->handlers[(int) QImode].insn_code = CODE_FOR_negqi2; - if (HAVE_negsf2) - neg_optab->handlers[(int) SFmode].insn_code = CODE_FOR_negsf2; - if (HAVE_negdf2) - neg_optab->handlers[(int) DFmode].insn_code = CODE_FOR_negdf2; - if (HAVE_abssf2) - abs_optab->handlers[(int) SFmode].insn_code = CODE_FOR_abssf2; - if (HAVE_absdf2) - abs_optab->handlers[(int) DFmode].insn_code = CODE_FOR_absdf2; - if (HAVE_sqrtsf2) - sqrt_optab->handlers[(int) SFmode].insn_code = CODE_FOR_sqrtsf2; - if (HAVE_sqrtdf2) - sqrt_optab->handlers[(int) DFmode].insn_code = CODE_FOR_sqrtdf2; - if (HAVE_sindf2) - sin_optab->handlers[(int) DFmode].insn_code = CODE_FOR_sindf2; - if (HAVE_sinsf2) - sin_optab->handlers[(int) SFmode].insn_code = CODE_FOR_sinsf2; - if (HAVE_cosdf2) - cos_optab->handlers[(int) DFmode].insn_code = CODE_FOR_cosdf2; - if (HAVE_cossf2) - cos_optab->handlers[(int) SFmode].insn_code = CODE_FOR_cossf2; - one_cmpl_optab->handlers[(int) SImode].insn_code = CODE_FOR_one_cmplsi2; - one_cmpl_optab->handlers[(int) HImode].insn_code = CODE_FOR_one_cmplhi2; - one_cmpl_optab->handlers[(int) QImode].insn_code = CODE_FOR_one_cmplqi2; - ashl_optab->handlers[(int) DImode].insn_code = CODE_FOR_ashldi3; - ashl_optab->handlers[(int) SImode].insn_code = CODE_FOR_ashlsi3; - ashl_optab->handlers[(int) HImode].insn_code = CODE_FOR_ashlhi3; - ashl_optab->handlers[(int) QImode].insn_code = CODE_FOR_ashlqi3; - ashr_optab->handlers[(int) DImode].insn_code = CODE_FOR_ashrdi3; - ashr_optab->handlers[(int) SImode].insn_code = CODE_FOR_ashrsi3; - ashr_optab->handlers[(int) HImode].insn_code = CODE_FOR_ashrhi3; - ashr_optab->handlers[(int) QImode].insn_code = CODE_FOR_ashrqi3; - lshr_optab->handlers[(int) DImode].insn_code = CODE_FOR_lshrdi3; - lshr_optab->handlers[(int) SImode].insn_code = CODE_FOR_lshrsi3; - lshr_optab->handlers[(int) HImode].insn_code = CODE_FOR_lshrhi3; - lshr_optab->handlers[(int) QImode].insn_code = CODE_FOR_lshrqi3; - rotl_optab->handlers[(int) SImode].insn_code = CODE_FOR_rotlsi3; - rotl_optab->handlers[(int) HImode].insn_code = CODE_FOR_rotlhi3; - rotl_optab->handlers[(int) QImode].insn_code = CODE_FOR_rotlqi3; - rotr_optab->handlers[(int) SImode].insn_code = CODE_FOR_rotrsi3; - rotr_optab->handlers[(int) HImode].insn_code = CODE_FOR_rotrhi3; - rotr_optab->handlers[(int) QImode].insn_code = CODE_FOR_rotrqi3; - setcc_gen_code[(int) EQ] = CODE_FOR_seq; - setcc_gen_code[(int) NE] = CODE_FOR_sne; - setcc_gen_code[(int) GT] = CODE_FOR_sgt; - setcc_gen_code[(int) GTU] = CODE_FOR_sgtu; - setcc_gen_code[(int) LT] = CODE_FOR_slt; - setcc_gen_code[(int) LTU] = CODE_FOR_sltu; - setcc_gen_code[(int) GE] = CODE_FOR_sge; - setcc_gen_code[(int) GEU] = CODE_FOR_sgeu; - setcc_gen_code[(int) LE] = CODE_FOR_sle; - setcc_gen_code[(int) LEU] = CODE_FOR_sleu; - bcc_gen_fctn[(int) EQ] = gen_beq; - bcc_gen_fctn[(int) NE] = gen_bne; - bcc_gen_fctn[(int) GT] = gen_bgt; - bcc_gen_fctn[(int) GTU] = gen_bgtu; - bcc_gen_fctn[(int) LT] = gen_blt; - bcc_gen_fctn[(int) LTU] = gen_bltu; - bcc_gen_fctn[(int) GE] = gen_bge; - bcc_gen_fctn[(int) GEU] = gen_bgeu; - bcc_gen_fctn[(int) LE] = gen_ble; - bcc_gen_fctn[(int) LEU] = gen_bleu; - movstr_optab[(int) SImode] = CODE_FOR_movstrsi; - ffs_optab->handlers[(int) SImode].insn_code = CODE_FOR_ffssi2; - ffs_optab->handlers[(int) HImode].insn_code = CODE_FOR_ffshi2; -} diff --git a/gnu/usr.bin/cc/common/insn-output.c b/gnu/usr.bin/cc/common/insn-output.c deleted file mode 100644 index 207cd129b3..0000000000 --- a/gnu/usr.bin/cc/common/insn-output.c +++ /dev/null @@ -1,5893 +0,0 @@ -/* Generated automatically by the program `genoutput' -from the machine description file `md'. */ - -#include "config.h" -#include "rtl.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "real.h" -#include "insn-config.h" - -#include "conditions.h" -#include "insn-flags.h" -#include "insn-attr.h" - -#include "insn-codes.h" - -#include "recog.h" - -#include -#include "output.h" - -static char * -output_0 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[0])) - return AS2 (test%L0,%0,%0); - - operands[1] = const0_rtx; - return AS2 (cmp%L0,%1,%0); -} -} - -static char * -output_2 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[0])) - return AS2 (test%W0,%0,%0); - - operands[1] = const0_rtx; - return AS2 (cmp%W0,%1,%0); -} -} - -static char * -output_4 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[0])) - return AS2 (test%B0,%0,%0); - - operands[1] = const0_rtx; - return AS2 (cmp%B0,%1,%0); -} -} - -static char * -output_6 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (! STACK_TOP_P (operands[0])) - abort (); - - output_asm_insn ("ftst", operands); - - if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG)) - output_asm_insn (AS1 (fstp,%y0), operands); - - return (char *) output_fp_cc0_set (insn); -} -} - -static char * -output_8 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (! STACK_TOP_P (operands[0])) - abort (); - - output_asm_insn ("ftst", operands); - - if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG)) - output_asm_insn (AS1 (fstp,%y0), operands); - - return (char *) output_fp_cc0_set (insn); -} -} - -static char * -output_10 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM) - { - cc_status.flags |= CC_REVERSED; - return AS2 (cmp%L0,%0,%1); - } - return AS2 (cmp%L0,%1,%0); -} -} - -static char * -output_12 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM) - { - cc_status.flags |= CC_REVERSED; - return AS2 (cmp%W0,%0,%1); - } - return AS2 (cmp%W0,%1,%0); -} -} - -static char * -output_14 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM) - { - cc_status.flags |= CC_REVERSED; - return AS2 (cmp%B0,%0,%1); - } - return AS2 (cmp%B0,%1,%0); -} -} - -static char * -output_16 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_float_compare (insn, operands); -} - -static char * -output_17 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_float_compare (insn, operands); -} - -static char * -output_18 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_float_compare (insn, operands); -} - -static char * -output_19 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_float_compare (insn, operands); -} - -static char * -output_20 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_float_compare (insn, operands); -} - -static char * -output_21 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_float_compare (insn, operands); -} - -static char * -output_22 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_float_compare (insn, operands); -} - -static char * -output_23 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_float_compare (insn, operands); -} - -static char * -output_24 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_float_compare (insn, operands); -} - -static char * -output_25 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_float_compare (insn, operands); -} - -static char * -output_32 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - /* For small integers, we may actually use testb. */ - if (GET_CODE (operands[1]) == CONST_INT - && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])) - && (! REG_P (operands[0]) || QI_REG_P (operands[0]))) - { - /* We may set the sign bit spuriously. */ - - if ((INTVAL (operands[1]) & ~0xff) == 0) - { - cc_status.flags |= CC_NOT_NEGATIVE; - return AS2 (test%B0,%1,%b0); - } - - if ((INTVAL (operands[1]) & ~0xff00) == 0) - { - cc_status.flags |= CC_NOT_NEGATIVE; - operands[1] = GEN_INT (INTVAL (operands[1]) >> 8); - - if (QI_REG_P (operands[0])) - return AS2 (test%B0,%1,%h0); - else - { - operands[0] = adj_offsettable_operand (operands[0], 1); - return AS2 (test%B0,%1,%b0); - } - } - - if (GET_CODE (operands[0]) == MEM - && (INTVAL (operands[1]) & ~0xff0000) == 0) - { - cc_status.flags |= CC_NOT_NEGATIVE; - operands[1] = GEN_INT (INTVAL (operands[1]) >> 16); - operands[0] = adj_offsettable_operand (operands[0], 2); - return AS2 (test%B0,%1,%b0); - } - - if (GET_CODE (operands[0]) == MEM - && (INTVAL (operands[1]) & ~0xff000000) == 0) - { - operands[1] = GEN_INT ((INTVAL (operands[1]) >> 24) & 0xff); - operands[0] = adj_offsettable_operand (operands[0], 3); - return AS2 (test%B0,%1,%b0); - } - } - - if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM) - return AS2 (test%L0,%1,%0); - - return AS2 (test%L1,%0,%1); -} -} - -static char * -output_33 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (GET_CODE (operands[1]) == CONST_INT - && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])) - && (! REG_P (operands[0]) || QI_REG_P (operands[0]))) - { - if ((INTVAL (operands[1]) & 0xff00) == 0) - { - /* ??? This might not be necessary. */ - if (INTVAL (operands[1]) & 0xffff0000) - operands[1] = GEN_INT (INTVAL (operands[1]) & 0xff); - - /* We may set the sign bit spuriously. */ - cc_status.flags |= CC_NOT_NEGATIVE; - return AS2 (test%B0,%1,%b0); - } - - if ((INTVAL (operands[1]) & 0xff) == 0) - { - operands[1] = GEN_INT ((INTVAL (operands[1]) >> 8) & 0xff); - - if (QI_REG_P (operands[0])) - return AS2 (test%B0,%1,%h0); - else - { - operands[0] = adj_offsettable_operand (operands[0], 1); - return AS2 (test%B0,%1,%b0); - } - } - } - - if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM) - return AS2 (test%W0,%1,%0); - - return AS2 (test%W1,%0,%1); -} -} - -static char * -output_34 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM) - return AS2 (test%B0,%1,%0); - - return AS2 (test%B1,%0,%1); -} -} - -static char * -output_38 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx link; - if (operands[1] == const0_rtx && REG_P (operands[0])) - return AS2 (xor%L0,%0,%0); - - if (operands[1] == const1_rtx - && (link = find_reg_note (insn, REG_WAS_0, 0)) - /* Make sure the insn that stored the 0 is still present. */ - && ! INSN_DELETED_P (XEXP (link, 0)) - && GET_CODE (XEXP (link, 0)) != NOTE - /* Make sure cross jumping didn't happen here. */ - && no_labels_between_p (XEXP (link, 0), insn) - /* Make sure the reg hasn't been clobbered. */ - && ! reg_set_between_p (operands[0], XEXP (link, 0), insn)) - /* Fastest way to change a 0 to a 1. */ - return AS1 (inc%L0,%0); - - return AS2 (mov%L0,%1,%0); -} -} - -static char * -output_40 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx link; - if (REG_P (operands[0]) && operands[1] == const0_rtx) - return AS2 (xor%L0,%k0,%k0); - - if (REG_P (operands[0]) && operands[1] == const1_rtx - && (link = find_reg_note (insn, REG_WAS_0, 0)) - /* Make sure the insn that stored the 0 is still present. */ - && ! INSN_DELETED_P (XEXP (link, 0)) - && GET_CODE (XEXP (link, 0)) != NOTE - /* Make sure cross jumping didn't happen here. */ - && no_labels_between_p (XEXP (link, 0), insn) - /* Make sure the reg hasn't been clobbered. */ - && ! reg_set_between_p (operands[0], XEXP (link, 0), insn)) - /* Fastest way to change a 0 to a 1. */ - return AS1 (inc%L0,%k0); - - if (REG_P (operands[0])) - { - if (REG_P (operands[1])) - return AS2 (mov%L0,%k1,%k0); - else if (CONSTANT_P (operands[1])) - return AS2 (mov%L0,%1,%k0); - } - - return AS2 (mov%W0,%1,%0); -} -} - -static char * -output_41 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx link; - if (operands[1] == const0_rtx && REG_P (operands[0])) - return AS2 (xor%W0,%0,%0); - - if (operands[1] == const1_rtx - && (link = find_reg_note (insn, REG_WAS_0, 0)) - /* Make sure the insn that stored the 0 is still present. */ - && ! INSN_DELETED_P (XEXP (link, 0)) - && GET_CODE (XEXP (link, 0)) != NOTE - /* Make sure cross jumping didn't happen here. */ - && no_labels_between_p (XEXP (link, 0), insn) - /* Make sure the reg hasn't been clobbered. */ - && ! reg_set_between_p (operands[0], XEXP (link, 0), insn)) - /* Fastest way to change a 0 to a 1. */ - return AS1 (inc%W0,%0); - - return AS2 (mov%W0,%1,%0); -} -} - -static char * -output_42 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - operands[1] = gen_rtx (REG, HImode, REGNO (operands[1])); - return AS1 (push%W0,%1); -} -} - -static char * -output_43 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx link; - if (operands[1] == const0_rtx && REG_P (operands[0])) - return AS2 (xor%B0,%0,%0); - - if (operands[1] == const1_rtx - && (link = find_reg_note (insn, REG_WAS_0, 0)) - /* Make sure the insn that stored the 0 is still present. */ - && ! INSN_DELETED_P (XEXP (link, 0)) - && GET_CODE (XEXP (link, 0)) != NOTE - /* Make sure cross jumping didn't happen here. */ - && no_labels_between_p (XEXP (link, 0), insn) - /* Make sure the reg hasn't been clobbered. */ - && ! reg_set_between_p (operands[0], XEXP (link, 0), insn)) - /* Fastest way to change a 0 to a 1. */ - return AS1 (inc%B0,%0); - - /* If mov%B0 isn't allowed for one of these regs, use mov%L0. */ - if (NON_QI_REG_P (operands[0]) || NON_QI_REG_P (operands[1])) - return (AS2 (mov%L0,%k1,%k0)); - - return (AS2 (mov%B0,%1,%0)); -} -} - -static char * -output_44 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx link; - if (operands[1] == const0_rtx && REG_P (operands[0])) - return AS2 (xor%B0,%0,%0); - - if (operands[1] == const1_rtx - && (link = find_reg_note (insn, REG_WAS_0, 0)) - /* Make sure the insn that stored the 0 is still present. */ - && ! INSN_DELETED_P (XEXP (link, 0)) - && GET_CODE (XEXP (link, 0)) != NOTE - /* Make sure cross jumping didn't happen here. */ - && no_labels_between_p (XEXP (link, 0), insn) - /* Make sure the reg hasn't been clobbered. */ - && ! reg_set_between_p (operands[0], XEXP (link, 0), insn)) - /* Fastest way to change a 0 to a 1. */ - return AS1 (inc%B0,%0); - - /* If mov%B0 isn't allowed for one of these regs, use mov%W0. */ - if (NON_QI_REG_P (operands[0]) || NON_QI_REG_P (operands[1])) - { - abort (); - return (AS2 (mov%L0,%k1,%k0)); - } - - return AS2 (mov%B0,%1,%0); -} -} - -static char * -output_45 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (STACK_REG_P (operands[1])) - { - rtx xops[3]; - - if (! STACK_TOP_P (operands[1])) - abort (); - - xops[0] = AT_SP (SFmode); - xops[1] = GEN_INT (4); - xops[2] = stack_pointer_rtx; - - output_asm_insn (AS2 (sub%L2,%1,%2), xops); - - if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG)) - output_asm_insn (AS1 (fstp%S0,%0), xops); - else - output_asm_insn (AS1 (fst%S0,%0), xops); - RET; - } - return AS1 (push%L1,%1); -} -} - -static char * -output_46 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0; - - /* First handle a `pop' insn or a `fld %st(0)' */ - - if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1])) - { - if (stack_top_dies) - return AS1 (fstp,%y0); - else - return AS1 (fld,%y0); - } - - /* Handle a transfer between the 387 and a 386 register */ - - if (STACK_TOP_P (operands[0]) && NON_STACK_REG_P (operands[1])) - { - output_op_from_reg (operands[1], AS1 (fld%z0,%y1)); - RET; - } - - if (STACK_TOP_P (operands[1]) && NON_STACK_REG_P (operands[0])) - { - output_to_reg (operands[0], stack_top_dies); - RET; - } - - /* Handle other kinds of writes from the 387 */ - - if (STACK_TOP_P (operands[1])) - { - if (stack_top_dies) - return AS1 (fstp%z0,%y0); - else - return AS1 (fst%z0,%y0); - } - - /* Handle other kinds of reads to the 387 */ - - if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE) - return (char *) output_move_const_single (operands); - - if (STACK_TOP_P (operands[0])) - return AS1 (fld%z1,%y1); - - /* Handle all SFmode moves not involving the 387 */ - - return (char *) singlemove_string (operands); -} -} - -static char * -output_47 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (STACK_REG_P (operands[1])) - { - rtx xops[3]; - - xops[0] = AT_SP (SFmode); - xops[1] = GEN_INT (8); - xops[2] = stack_pointer_rtx; - - output_asm_insn (AS2 (sub%L2,%1,%2), xops); - - if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG)) - output_asm_insn (AS1 (fstp%Q0,%0), xops); - else - output_asm_insn (AS1 (fst%Q0,%0), xops); - - RET; - } - else - return (char *) output_move_double (operands); -} -} - -static char * -output_48 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (STACK_TOP_P (operands[0])) - return AS1 (fxch,%1); - else - return AS1 (fxch,%0); -} -} - -static char * -output_49 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0; - - /* First handle a `pop' insn or a `fld %st(0)' */ - - if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1])) - { - if (stack_top_dies) - return AS1 (fstp,%y0); - else - return AS1 (fld,%y0); - } - - /* Handle a transfer between the 387 and a 386 register */ - - if (STACK_TOP_P (operands[0]) && NON_STACK_REG_P (operands[1])) - { - output_op_from_reg (operands[1], AS1 (fld%z0,%y1)); - RET; - } - - if (STACK_TOP_P (operands[1]) && NON_STACK_REG_P (operands[0])) - { - output_to_reg (operands[0], stack_top_dies); - RET; - } - - /* Handle other kinds of writes from the 387 */ - - if (STACK_TOP_P (operands[1])) - { - if (stack_top_dies) - return AS1 (fstp%z0,%y0); - else - return AS1 (fst%z0,%y0); - } - - /* Handle other kinds of reads to the 387 */ - - if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE) - return (char *) output_move_const_single (operands); - - if (STACK_TOP_P (operands[0])) - return AS1 (fld%z1,%y1); - - /* Handle all DFmode moves not involving the 387 */ - - return (char *) output_move_double (operands); -} -} - -static char * -output_50 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - return (char *) output_move_double (operands); -} -} - -static char * -output_51 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - return (char *) output_move_double (operands); -} -} - -static char * -output_52 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if ((TARGET_486 || REGNO (operands[0]) == 0) - && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1])) - { - rtx xops[2]; - xops[0] = operands[0]; - xops[1] = GEN_INT (0xffff); - output_asm_insn (AS2 (and%L0,%1,%k0), xops); - RET; - } - -#ifdef INTEL_SYNTAX - return AS2 (movzx,%1,%0); -#else - return AS2 (movz%W0%L0,%1,%0); -#endif -} -} - -static char * -output_53 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if ((TARGET_486 || REGNO (operands[0]) == 0) - && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1])) - { - rtx xops[2]; - xops[0] = operands[0]; - xops[1] = GEN_INT (0xff); - output_asm_insn (AS2 (and%L0,%1,%k0), xops); - RET; - } - -#ifdef INTEL_SYNTAX - return AS2 (movzx,%1,%0); -#else - return AS2 (movz%B0%W0,%1,%0); -#endif -} -} - -static char * -output_54 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if ((TARGET_486 || REGNO (operands[0]) == 0) - && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1])) - { - rtx xops[2]; - xops[0] = operands[0]; - xops[1] = GEN_INT (0xff); - output_asm_insn (AS2 (and%L0,%1,%k0), xops); - RET; - } - -#ifdef INTEL_SYNTAX - return AS2 (movzx,%1,%0); -#else - return AS2 (movz%B0%L0,%1,%0); -#endif -} -} - -static char * -output_55 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); - return AS2 (xor%L0,%0,%0); -} -} - -static char * -output_56 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REGNO (operands[0]) == 0) - { - /* This used to be cwtl, but that extends HI to SI somehow. */ -#ifdef INTEL_SYNTAX - return "cdq"; -#else - return "cltd"; -#endif - } - - operands[1] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); - output_asm_insn (AS2 (mov%L0,%0,%1), operands); - - operands[0] = GEN_INT (31); - return AS2 (sar%L1,%0,%1); -} -} - -static char * -output_57 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REGNO (operands[0]) == 0 - && REG_P (operands[1]) && REGNO (operands[1]) == 0) -#ifdef INTEL_SYNTAX - return "cwde"; -#else - return "cwtl"; -#endif - -#ifdef INTEL_SYNTAX - return AS2 (movsx,%1,%0); -#else - return AS2 (movs%W0%L0,%1,%0); -#endif -} -} - -static char * -output_58 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REGNO (operands[0]) == 0 - && REG_P (operands[1]) && REGNO (operands[1]) == 0) - return "cbtw"; - -#ifdef INTEL_SYNTAX - return AS2 (movsx,%1,%0); -#else - return AS2 (movs%B0%W0,%1,%0); -#endif -} -} - -static char * -output_59 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ -#ifdef INTEL_SYNTAX - return AS2 (movsx,%1,%0); -#else - return AS2 (movs%B0%L0,%1,%0); -#endif -} -} - -static char * -output_60 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0; - - if (NON_STACK_REG_P (operands[1])) - { - output_op_from_reg (operands[1], AS1 (fld%z0,%y1)); - RET; - } - - if (NON_STACK_REG_P (operands[0])) - { - output_to_reg (operands[0], stack_top_dies); - RET; - } - - if (STACK_TOP_P (operands[0])) - return AS1 (fld%z1,%y1); - - if (GET_CODE (operands[0]) == MEM) - { - if (stack_top_dies) - return AS1 (fstp%z0,%y0); - else - return AS1 (fst%z0,%y0); - } - - abort (); -} -} - -static char * -output_62 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0; - - if (GET_CODE (operands[0]) == MEM) - { - if (stack_top_dies) - return AS1 (fstp%z0,%0); - else - return AS1 (fst%z0,%0); - } - else if (STACK_TOP_P (operands[0])) - { - output_asm_insn (AS1 (fstp%z2,%y2), operands); - return AS1 (fld%z2,%y2); - } - else - abort (); -} -} - -static char * -output_67 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_fix_trunc (insn, operands); -} - -static char * -output_68 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_fix_trunc (insn, operands); -} - -static char * -output_71 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_fix_trunc (insn, operands); -} - -static char * -output_72 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_fix_trunc (insn, operands); -} - -static char * -output_77 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (NON_STACK_REG_P (operands[1])) - { - output_op_from_reg (operands[1], AS1 (fild%z0,%1)); - RET; - } - else if (GET_CODE (operands[1]) == MEM) - return AS1 (fild%z1,%1); - else - abort (); -} -} - -static char * -output_78 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (NON_STACK_REG_P (operands[1])) - { - output_op_from_reg (operands[1], AS1 (fild%z0,%1)); - RET; - } - else if (GET_CODE (operands[1]) == MEM) - return AS1 (fild%z1,%1); - else - abort (); -} -} - -static char * -output_79 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (NON_STACK_REG_P (operands[1])) - { - output_op_from_reg (operands[1], AS1 (fild%z0,%1)); - RET; - } - else if (GET_CODE (operands[1]) == MEM) - return AS1 (fild%z1,%1); - else - abort (); -} -} - -static char * -output_80 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (NON_STACK_REG_P (operands[1])) - { - output_op_from_reg (operands[1], AS1 (fild%z0,%1)); - RET; - } - else if (GET_CODE (operands[1]) == MEM) - return AS1 (fild%z1,%1); - else - abort (); -} -} - -static char * -output_81 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx low[3], high[3]; - - CC_STATUS_INIT; - - split_di (operands, 3, low, high); - - if (GET_CODE (low[2]) != CONST_INT || INTVAL (low[2]) != 0) - { - output_asm_insn (AS2 (add%L0,%2,%0), low); - output_asm_insn (AS2 (adc%L0,%2,%0), high); - } - else - output_asm_insn (AS2 (add%L0,%2,%0), high); - RET; -} -} - -static char * -output_82 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[0]) && REGNO (operands[0]) != REGNO (operands[1])) - { - if (REG_P (operands[2]) && REGNO (operands[0]) == REGNO (operands[2])) - return AS2 (add%L0,%1,%0); - - if (! TARGET_486 || ! REG_P (operands[2])) - { - CC_STATUS_INIT; - - if (operands[2] == stack_pointer_rtx) - { - rtx temp; - - temp = operands[1]; - operands[1] = operands[2]; - operands[2] = temp; - } - if (operands[2] != stack_pointer_rtx) - { - operands[1] = SET_SRC (PATTERN (insn)); - return AS2 (lea%L0,%a1,%0); - } - } - - output_asm_insn (AS2 (mov%L0,%1,%0), operands); - } - - if (operands[2] == const1_rtx) - return AS1 (inc%L0,%0); - - if (operands[2] == constm1_rtx) - return AS1 (dec%L0,%0); - - return AS2 (add%L0,%2,%0); -} -} - -static char * -output_83 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (operands[2] == const1_rtx) - return AS1 (inc%W0,%0); - - if (operands[2] == constm1_rtx) - return AS1 (dec%W0,%0); - - return AS2 (add%W0,%2,%0); -} -} - -static char * -output_84 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (operands[2] == const1_rtx) - return AS1 (inc%B0,%0); - - if (operands[2] == constm1_rtx) - return AS1 (dec%B0,%0); - - return AS2 (add%B0,%2,%0); -} -} - -static char * -output_85 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - CC_STATUS_INIT; - /* Adding a constant to a register is faster with an add. */ - /* ??? can this ever happen? */ - if (GET_CODE (operands[1]) == PLUS - && GET_CODE (XEXP (operands[1], 1)) == CONST_INT - && rtx_equal_p (operands[0], XEXP (operands[1], 0))) - { - operands[1] = XEXP (operands[1], 1); - - if (operands[1] == const1_rtx) - return AS1 (inc%L0,%0); - - if (operands[1] == constm1_rtx) - return AS1 (dec%L0,%0); - - return AS2 (add%L0,%1,%0); - } - return AS2 (lea%L0,%a1,%0); -} -} - -static char * -output_88 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx low[3], high[3]; - - CC_STATUS_INIT; - - split_di (operands, 3, low, high); - - if (GET_CODE (low[2]) != CONST_INT || INTVAL (low[2]) != 0) - { - output_asm_insn (AS2 (sub%L0,%2,%0), low); - output_asm_insn (AS2 (sbb%L0,%2,%0), high); - } - else - output_asm_insn (AS2 (sub%L0,%2,%0), high); - - RET; -} -} - -static char * -output_89 (operands, insn) - rtx *operands; - rtx insn; -{ - return AS2 (sub%L0,%2,%0); -} - -static char * -output_90 (operands, insn) - rtx *operands; - rtx insn; -{ - return AS2 (sub%W0,%2,%0); -} - -static char * -output_91 (operands, insn) - rtx *operands; - rtx insn; -{ - return AS2 (sub%B0,%2,%0); -} - -static char * -output_94 (operands, insn) - rtx *operands; - rtx insn; -{ - return AS2 (imul%W0,%2,%0); -} - -static char * -output_95 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (GET_CODE (operands[1]) == REG - && REGNO (operands[1]) == REGNO (operands[0]) - && (GET_CODE (operands[2]) == MEM || GET_CODE (operands[2]) == REG)) - /* Assembler has weird restrictions. */ - return AS2 (imul%W0,%2,%0); - return AS3 (imul%W0,%2,%1,%0); -} -} - -static char * -output_96 (operands, insn) - rtx *operands; - rtx insn; -{ - return AS2 (imul%L0,%2,%0); -} - -static char * -output_97 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (GET_CODE (operands[1]) == REG - && REGNO (operands[1]) == REGNO (operands[0]) - && (GET_CODE (operands[2]) == MEM || GET_CODE (operands[2]) == REG)) - /* Assembler has weird restrictions. */ - return AS2 (imul%L0,%2,%0); - return AS3 (imul%L0,%2,%1,%0); -} -} - -static char * -output_105 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ -#ifdef INTEL_SYNTAX - output_asm_insn ("cdq", operands); -#else - output_asm_insn ("cltd", operands); -#endif - return AS1 (idiv%L0,%2); -} -} - -static char * -output_107 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - output_asm_insn (AS2 (xor%L3,%3,%3), operands); - return AS1 (div%L0,%2); -} -} - -static char * -output_108 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - output_asm_insn (AS2 (xor%W0,%3,%3), operands); - return AS1 (div%W0,%2); -} -} - -static char * -output_109 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (GET_CODE (operands[2]) == CONST_INT - && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) - { - if (INTVAL (operands[2]) == 0xffff && REG_P (operands[0]) - && (! REG_P (operands[1]) - || REGNO (operands[0]) != 0 || REGNO (operands[1]) != 0) - && (! TARGET_486 || ! rtx_equal_p (operands[0], operands[1]))) - { - /* ??? tege: Should forget CC_STATUS only if we clobber a - remembered operand. Fix that later. */ - CC_STATUS_INIT; -#ifdef INTEL_SYNTAX - return AS2 (movzx,%w1,%0); -#else - return AS2 (movz%W0%L0,%w1,%0); -#endif - } - - if (INTVAL (operands[2]) == 0xff && REG_P (operands[0]) - && !(REG_P (operands[1]) && NON_QI_REG_P (operands[1])) - && (! REG_P (operands[1]) - || REGNO (operands[0]) != 0 || REGNO (operands[1]) != 0) - && (! TARGET_486 || ! rtx_equal_p (operands[0], operands[1]))) - { - /* ??? tege: Should forget CC_STATUS only if we clobber a - remembered operand. Fix that later. */ - CC_STATUS_INIT; -#ifdef INTEL_SYNTAX - return AS2 (movzx,%b1,%0); -#else - return AS2 (movz%B0%L0,%b1,%0); -#endif - } - - if (QI_REG_P (operands[0]) && ~(INTVAL (operands[2]) | 0xff) == 0) - { - CC_STATUS_INIT; - - if (INTVAL (operands[2]) == 0xffffff00) - { - operands[2] = const0_rtx; - return AS2 (mov%B0,%2,%b0); - } - - operands[2] = GEN_INT (INTVAL (operands[2]) & 0xff); - return AS2 (and%B0,%2,%b0); - } - - if (QI_REG_P (operands[0]) && ~(INTVAL (operands[2]) | 0xff00) == 0) - { - CC_STATUS_INIT; - - if (INTVAL (operands[2]) == 0xffff00ff) - { - operands[2] = const0_rtx; - return AS2 (mov%B0,%2,%h0); - } - - operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff); - return AS2 (and%B0,%2,%h0); - } - - if (GET_CODE (operands[0]) == MEM && INTVAL (operands[2]) == 0xffff0000) - { - operands[2] = const0_rtx; - return AS2 (mov%W0,%2,%w0); - } - } - - return AS2 (and%L0,%2,%0); -} -} - -static char * -output_110 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (GET_CODE (operands[2]) == CONST_INT - && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) - { - /* Can we ignore the upper byte? */ - if ((! REG_P (operands[0]) || QI_REG_P (operands[0])) - && (INTVAL (operands[2]) & 0xff00) == 0xff00) - { - CC_STATUS_INIT; - - if ((INTVAL (operands[2]) & 0xff) == 0) - { - operands[2] = const0_rtx; - return AS2 (mov%B0,%2,%b0); - } - - operands[2] = GEN_INT (INTVAL (operands[2]) & 0xff); - return AS2 (and%B0,%2,%b0); - } - - /* Can we ignore the lower byte? */ - /* ??? what about offsettable memory references? */ - if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & 0xff) == 0xff) - { - CC_STATUS_INIT; - - if ((INTVAL (operands[2]) & 0xff00) == 0) - { - operands[2] = const0_rtx; - return AS2 (mov%B0,%2,%h0); - } - - operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff); - return AS2 (and%B0,%2,%h0); - } - } - - return AS2 (and%W0,%2,%0); -} -} - -static char * -output_111 (operands, insn) - rtx *operands; - rtx insn; -{ - return AS2 (and%B0,%2,%0); -} - -static char * -output_112 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (GET_CODE (operands[2]) == CONST_INT - && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) - { - if ((! REG_P (operands[0]) || QI_REG_P (operands[0])) - && (INTVAL (operands[2]) & ~0xff) == 0) - { - CC_STATUS_INIT; - - if (INTVAL (operands[2]) == 0xff) - return AS2 (mov%B0,%2,%b0); - - return AS2 (or%B0,%2,%b0); - } - - if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & ~0xff00) == 0) - { - CC_STATUS_INIT; - operands[2] = GEN_INT (INTVAL (operands[2]) >> 8); - - if (INTVAL (operands[2]) == 0xff) - return AS2 (mov%B0,%2,%h0); - - return AS2 (or%B0,%2,%h0); - } - } - - return AS2 (or%L0,%2,%0); -} -} - -static char * -output_113 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (GET_CODE (operands[2]) == CONST_INT - && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) - { - /* Can we ignore the upper byte? */ - if ((! REG_P (operands[0]) || QI_REG_P (operands[0])) - && (INTVAL (operands[2]) & 0xff00) == 0) - { - CC_STATUS_INIT; - if (INTVAL (operands[2]) & 0xffff0000) - operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff); - - if (INTVAL (operands[2]) == 0xff) - return AS2 (mov%B0,%2,%b0); - - return AS2 (or%B0,%2,%b0); - } - - /* Can we ignore the lower byte? */ - /* ??? what about offsettable memory references? */ - if (QI_REG_P (operands[0]) - && (INTVAL (operands[2]) & 0xff) == 0) - { - CC_STATUS_INIT; - operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff); - - if (INTVAL (operands[2]) == 0xff) - return AS2 (mov%B0,%2,%h0); - - return AS2 (or%B0,%2,%h0); - } - } - - return AS2 (or%W0,%2,%0); -} -} - -static char * -output_114 (operands, insn) - rtx *operands; - rtx insn; -{ - return AS2 (or%B0,%2,%0); -} - -static char * -output_115 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (GET_CODE (operands[2]) == CONST_INT - && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) - { - if ((! REG_P (operands[0]) || QI_REG_P (operands[0])) - && (INTVAL (operands[2]) & ~0xff) == 0) - { - CC_STATUS_INIT; - - if (INTVAL (operands[2]) == 0xff) - return AS1 (not%B0,%b0); - - return AS2 (xor%B0,%2,%b0); - } - - if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & ~0xff00) == 0) - { - CC_STATUS_INIT; - operands[2] = GEN_INT (INTVAL (operands[2]) >> 8); - - if (INTVAL (operands[2]) == 0xff) - return AS1 (not%B0,%h0); - - return AS2 (xor%B0,%2,%h0); - } - } - - return AS2 (xor%L0,%2,%0); -} -} - -static char * -output_116 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (GET_CODE (operands[2]) == CONST_INT - && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) - { - /* Can we ignore the upper byte? */ - if ((! REG_P (operands[0]) || QI_REG_P (operands[0])) - && (INTVAL (operands[2]) & 0xff00) == 0) - { - CC_STATUS_INIT; - if (INTVAL (operands[2]) & 0xffff0000) - operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff); - - if (INTVAL (operands[2]) == 0xff) - return AS1 (not%B0,%b0); - - return AS2 (xor%B0,%2,%b0); - } - - /* Can we ignore the lower byte? */ - /* ??? what about offsettable memory references? */ - if (QI_REG_P (operands[0]) - && (INTVAL (operands[2]) & 0xff) == 0) - { - CC_STATUS_INIT; - operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff); - - if (INTVAL (operands[2]) == 0xff) - return AS1 (not%B0,%h0); - - return AS2 (xor%B0,%2,%h0); - } - } - - return AS2 (xor%W0,%2,%0); -} -} - -static char * -output_117 (operands, insn) - rtx *operands; - rtx insn; -{ - return AS2 (xor%B0,%2,%0); -} - -static char * -output_118 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx xops[2], low[1], high[1]; - - CC_STATUS_INIT; - - split_di (operands, 1, low, high); - xops[0] = const0_rtx; - xops[1] = high[0]; - - output_asm_insn (AS1 (neg%L0,%0), low); - output_asm_insn (AS2 (adc%L1,%0,%1), xops); - output_asm_insn (AS1 (neg%L0,%0), high); - RET; -} -} - -static char * -output_141 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx xops[4], low[1], high[1]; - - CC_STATUS_INIT; - - split_di (operands, 1, low, high); - xops[0] = operands[2]; - xops[1] = const1_rtx; - xops[2] = low[0]; - xops[3] = high[0]; - - if (INTVAL (xops[0]) > 31) - { - output_asm_insn (AS2 (mov%L3,%2,%3), xops); /* Fast shift by 32 */ - output_asm_insn (AS2 (xor%L2,%2,%2), xops); - - if (INTVAL (xops[0]) > 32) - { - xops[0] = GEN_INT (INTVAL (xops[0]) - 32); - output_asm_insn (AS2 (sal%L3,%0,%3), xops); /* Remaining shift */ - } - } - else - { - output_asm_insn (AS3 (shld%L3,%0,%2,%3), xops); - output_asm_insn (AS2 (sal%L2,%0,%2), xops); - } - RET; -} -} - -static char * -output_142 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx xops[4], low[1], high[1]; - - CC_STATUS_INIT; - - split_di (operands, 1, low, high); - xops[0] = operands[2]; - xops[1] = const1_rtx; - xops[2] = low[0]; - xops[3] = high[0]; - - output_asm_insn (AS2 (ror%B0,%1,%0), xops); /* shift count / 2 */ - - output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops); - output_asm_insn (AS2 (sal%L2,%0,%2), xops); - output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops); - output_asm_insn (AS2 (sal%L2,%0,%2), xops); - - xops[1] = GEN_INT (7); /* shift count & 1 */ - - output_asm_insn (AS2 (shr%B0,%1,%0), xops); - - output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops); - output_asm_insn (AS2 (sal%L2,%0,%2), xops); - - RET; -} -} - -static char * -output_143 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[0]) && REGNO (operands[0]) != REGNO (operands[1])) - { - if (TARGET_486 && INTVAL (operands[2]) == 1) - { - output_asm_insn (AS2 (mov%L0,%1,%0), operands); - return AS2 (add%L0,%1,%0); - } - else - { - CC_STATUS_INIT; - - if (operands[1] == stack_pointer_rtx) - { - output_asm_insn (AS2 (mov%L0,%1,%0), operands); - operands[1] = operands[0]; - } - operands[1] = gen_rtx (MULT, SImode, operands[1], - GEN_INT (1 << INTVAL (operands[2]))); - return AS2 (lea%L0,%a1,%0); - } - } - - if (REG_P (operands[2])) - return AS2 (sal%L0,%b2,%0); - - if (REG_P (operands[0]) && operands[2] == const1_rtx) - return AS2 (add%L0,%0,%0); - - return AS2 (sal%L0,%2,%0); -} -} - -static char * -output_144 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[2])) - return AS2 (sal%W0,%b2,%0); - - if (REG_P (operands[0]) && operands[2] == const1_rtx) - return AS2 (add%W0,%0,%0); - - return AS2 (sal%W0,%2,%0); -} -} - -static char * -output_145 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[2])) - return AS2 (sal%B0,%b2,%0); - - if (REG_P (operands[0]) && operands[2] == const1_rtx) - return AS2 (add%B0,%0,%0); - - return AS2 (sal%B0,%2,%0); -} -} - -static char * -output_147 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx xops[4], low[1], high[1]; - - CC_STATUS_INIT; - - split_di (operands, 1, low, high); - xops[0] = operands[2]; - xops[1] = const1_rtx; - xops[2] = low[0]; - xops[3] = high[0]; - - if (INTVAL (xops[0]) > 31) - { - xops[1] = GEN_INT (31); - output_asm_insn (AS2 (mov%L2,%3,%2), xops); - output_asm_insn (AS2 (sar%L3,%1,%3), xops); /* shift by 32 */ - - if (INTVAL (xops[0]) > 32) - { - xops[0] = GEN_INT (INTVAL (xops[0]) - 32); - output_asm_insn (AS2 (sar%L2,%0,%2), xops); /* Remaining shift */ - } - } - else - { - output_asm_insn (AS3 (shrd%L2,%0,%3,%2), xops); - output_asm_insn (AS2 (sar%L3,%0,%3), xops); - } - - RET; -} -} - -static char * -output_148 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx xops[4], low[1], high[1]; - - CC_STATUS_INIT; - - split_di (operands, 1, low, high); - xops[0] = operands[2]; - xops[1] = const1_rtx; - xops[2] = low[0]; - xops[3] = high[0]; - - output_asm_insn (AS2 (ror%B0,%1,%0), xops); /* shift count / 2 */ - - output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); - output_asm_insn (AS2 (sar%L3,%0,%3), xops); - output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); - output_asm_insn (AS2 (sar%L3,%0,%3), xops); - - xops[1] = GEN_INT (7); /* shift count & 1 */ - - output_asm_insn (AS2 (shr%B0,%1,%0), xops); - - output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); - output_asm_insn (AS2 (sar%L3,%0,%3), xops); - - RET; -} -} - -static char * -output_149 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[2])) - return AS2 (sar%L0,%b2,%0); - else - return AS2 (sar%L0,%2,%0); -} -} - -static char * -output_150 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[2])) - return AS2 (sar%W0,%b2,%0); - else - return AS2 (sar%W0,%2,%0); -} -} - -static char * -output_151 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[2])) - return AS2 (sar%B0,%b2,%0); - else - return AS2 (sar%B0,%2,%0); -} -} - -static char * -output_153 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx xops[4], low[1], high[1]; - - CC_STATUS_INIT; - - split_di (operands, 1, low, high); - xops[0] = operands[2]; - xops[1] = const1_rtx; - xops[2] = low[0]; - xops[3] = high[0]; - - if (INTVAL (xops[0]) > 31) - { - output_asm_insn (AS2 (mov%L2,%3,%2), xops); /* Fast shift by 32 */ - output_asm_insn (AS2 (xor%L3,%3,%3), xops); - - if (INTVAL (xops[0]) > 32) - { - xops[0] = GEN_INT (INTVAL (xops[0]) - 32); - output_asm_insn (AS2 (shr%L2,%0,%2), xops); /* Remaining shift */ - } - } - else - { - output_asm_insn (AS3 (shrd%L2,%0,%3,%2), xops); - output_asm_insn (AS2 (shr%L3,%0,%3), xops); - } - - RET; -} -} - -static char * -output_154 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx xops[4], low[1], high[1]; - - CC_STATUS_INIT; - - split_di (operands, 1, low, high); - xops[0] = operands[2]; - xops[1] = const1_rtx; - xops[2] = low[0]; - xops[3] = high[0]; - - output_asm_insn (AS2 (ror%B0,%1,%0), xops); /* shift count / 2 */ - - output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); - output_asm_insn (AS2 (shr%L3,%0,%3), xops); - output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); - output_asm_insn (AS2 (shr%L3,%0,%3), xops); - - xops[1] = GEN_INT (7); /* shift count & 1 */ - - output_asm_insn (AS2 (shr%B0,%1,%0), xops); - - output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); - output_asm_insn (AS2 (shr%L3,%0,%3), xops); - - RET; -} -} - -static char * -output_155 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[2])) - return AS2 (shr%L0,%b2,%0); - else - return AS2 (shr%L0,%2,%1); -} -} - -static char * -output_156 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[2])) - return AS2 (shr%W0,%b2,%0); - else - return AS2 (shr%W0,%2,%0); -} -} - -static char * -output_157 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[2])) - return AS2 (shr%B0,%b2,%0); - else - return AS2 (shr%B0,%2,%0); -} -} - -static char * -output_158 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[2])) - return AS2 (rol%L0,%b2,%0); - else - return AS2 (rol%L0,%2,%0); -} -} - -static char * -output_159 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[2])) - return AS2 (rol%W0,%b2,%0); - else - return AS2 (rol%W0,%2,%0); -} -} - -static char * -output_160 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[2])) - return AS2 (rol%B0,%b2,%0); - else - return AS2 (rol%B0,%2,%0); -} -} - -static char * -output_161 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[2])) - return AS2 (ror%L0,%b2,%0); - else - return AS2 (ror%L0,%2,%0); -} -} - -static char * -output_162 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[2])) - return AS2 (ror%W0,%b2,%0); - else - return AS2 (ror%W0,%2,%0); -} -} - -static char * -output_163 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (REG_P (operands[2])) - return AS2 (ror%B0,%b2,%0); - else - return AS2 (ror%B0,%2,%0); -} -} - -static char * -output_164 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - CC_STATUS_INIT; - - if (INTVAL (operands[3]) == 1) - return AS2 (bts%L0,%2,%0); - else - return AS2 (btr%L0,%2,%0); -} -} - -static char * -output_165 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - CC_STATUS_INIT; - - return AS2 (btc%L0,%1,%0); -} -} - -static char * -output_166 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - CC_STATUS_INIT; - - return AS2 (btc%L0,%2,%0); -} -} - -static char * -output_167 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - cc_status.flags |= CC_Z_IN_NOT_C; - return AS2 (bt%L0,%1,%0); -} -} - -static char * -output_168 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - unsigned int mask; - - mask = ((1 << INTVAL (operands[1])) - 1) << INTVAL (operands[2]); - operands[1] = GEN_INT (mask); - - if (QI_REG_P (operands[0])) - { - if ((mask & ~0xff) == 0) - { - cc_status.flags |= CC_NOT_NEGATIVE; - return AS2 (test%B0,%1,%b0); - } - - if ((mask & ~0xff00) == 0) - { - cc_status.flags |= CC_NOT_NEGATIVE; - operands[1] = GEN_INT (mask >> 8); - return AS2 (test%B0,%1,%h0); - } - } - - return AS2 (test%L0,%1,%0); -} -} - -static char * -output_169 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - unsigned int mask; - - mask = ((1 << INTVAL (operands[1])) - 1) << INTVAL (operands[2]); - operands[1] = GEN_INT (mask); - - if (! REG_P (operands[0]) || QI_REG_P (operands[0])) - { - if ((mask & ~0xff) == 0) - { - cc_status.flags |= CC_NOT_NEGATIVE; - return AS2 (test%B0,%1,%b0); - } - - if ((mask & ~0xff00) == 0) - { - cc_status.flags |= CC_NOT_NEGATIVE; - operands[1] = GEN_INT (mask >> 8); - - if (QI_REG_P (operands[0])) - return AS2 (test%B0,%1,%h0); - else - { - operands[0] = adj_offsettable_operand (operands[0], 1); - return AS2 (test%B0,%1,%b0); - } - } - - if (GET_CODE (operands[0]) == MEM && (mask & ~0xff0000) == 0) - { - cc_status.flags |= CC_NOT_NEGATIVE; - operands[1] = GEN_INT (mask >> 16); - operands[0] = adj_offsettable_operand (operands[0], 2); - return AS2 (test%B0,%1,%b0); - } - - if (GET_CODE (operands[0]) == MEM && (mask & ~0xff000000) == 0) - { - cc_status.flags |= CC_NOT_NEGATIVE; - operands[1] = GEN_INT (mask >> 24); - operands[0] = adj_offsettable_operand (operands[0], 3); - return AS2 (test%B0,%1,%b0); - } - } - - if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM) - return AS2 (test%L0,%1,%0); - - return AS2 (test%L1,%0,%1); -} -} - -static char * -output_171 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (cc_prev_status.flags & CC_Z_IN_NOT_C) - return AS1 (setnb,%0); - else - return AS1 (sete,%0); -} -} - -static char * -output_173 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (cc_prev_status.flags & CC_Z_IN_NOT_C) - return AS1 (setb,%0); - else - return AS1 (setne,%0); -} - -} - -static char * -output_175 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) - return AS1 (sete,%0); - - OUTPUT_JUMP ("setg %0", "seta %0", NULL_PTR); -} -} - -static char * -output_177 (operands, insn) - rtx *operands; - rtx insn; -{ - return "seta %0"; -} - -static char * -output_179 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) - return AS1 (sete,%0); - - OUTPUT_JUMP ("setl %0", "setb %0", "sets %0"); -} -} - -static char * -output_181 (operands, insn) - rtx *operands; - rtx insn; -{ - return "setb %0"; -} - -static char * -output_183 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) - return AS1 (sete,%0); - - OUTPUT_JUMP ("setge %0", "setae %0", "setns %0"); -} -} - -static char * -output_185 (operands, insn) - rtx *operands; - rtx insn; -{ - return "setae %0"; -} - -static char * -output_187 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) - return AS1 (setb,%0); - - OUTPUT_JUMP ("setle %0", "setbe %0", NULL_PTR); -} -} - -static char * -output_189 (operands, insn) - rtx *operands; - rtx insn; -{ - return "setbe %0"; -} - -static char * -output_191 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (cc_prev_status.flags & CC_Z_IN_NOT_C) - return "jnc %l0"; - else - return "je %l0"; -} -} - -static char * -output_193 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (cc_prev_status.flags & CC_Z_IN_NOT_C) - return "jc %l0"; - else - return "jne %l0"; -} -} - -static char * -output_195 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) - return AS1 (je,%l0); - - OUTPUT_JUMP ("jg %l0", "ja %l0", NULL_PTR); -} -} - -static char * -output_199 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) - return AS1 (je,%l0); - - OUTPUT_JUMP ("jl %l0", "jb %l0", "js %l0"); -} -} - -static char * -output_203 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) - return AS1 (je,%l0); - - OUTPUT_JUMP ("jge %l0", "jae %l0", "jns %l0"); -} -} - -static char * -output_207 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) - return AS1 (jb,%l0); - - OUTPUT_JUMP ("jle %l0", "jbe %l0", NULL_PTR); -} -} - -static char * -output_210 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (cc_prev_status.flags & CC_Z_IN_NOT_C) - return "jc %l0"; - else - return "jne %l0"; -} -} - -static char * -output_211 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (cc_prev_status.flags & CC_Z_IN_NOT_C) - return "jnc %l0"; - else - return "je %l0"; -} -} - -static char * -output_212 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) - return AS1 (jne,%l0); - - OUTPUT_JUMP ("jle %l0", "jbe %l0", NULL_PTR); -} -} - -static char * -output_214 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) - return AS1 (jne,%l0); - - OUTPUT_JUMP ("jge %l0", "jae %l0", "jns %l0"); -} -} - -static char * -output_216 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) - return AS1 (jne,%l0); - - OUTPUT_JUMP ("jl %l0", "jb %l0", "js %l0"); -} -} - -static char * -output_218 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) - return AS1 (jae,%l0); - - OUTPUT_JUMP ("jg %l0", "ja %l0", NULL_PTR); -} -} - -static char * -output_221 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - CC_STATUS_INIT; - - return AS1 (jmp,%*%0); -} -} - -static char * -output_223 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx xops[4]; - - xops[0] = operands[0]; - xops[1] = operands[1]; - xops[2] = operands[2]; - xops[3] = pic_offset_table_rtx; - - output_asm_insn (AS2 (mov%L2,%3,%2), xops); - output_asm_insn ("sub%L2 %l1@GOTOFF(%3,%0,4),%2", xops); - output_asm_insn (AS1 (jmp,%*%2), xops); - ASM_OUTPUT_ALIGN_CODE (asm_out_file); - RET; -} -} - -static char * -output_224 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - CC_STATUS_INIT; - - return AS1 (jmp,%*%0); -} -} - -static char * -output_226 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (GET_CODE (operands[0]) == MEM - && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) - { - operands[0] = XEXP (operands[0], 0); - return AS1 (call,%*%0); - } - else - return AS1 (call,%P0); -} -} - -static char * -output_229 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (GET_CODE (operands[0]) == MEM - && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) - { - operands[0] = XEXP (operands[0], 0); - return AS1 (call,%*%0); - } - else - return AS1 (call,%P0); -} -} - -static char * -output_232 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (GET_CODE (operands[1]) == MEM - && ! CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) - { - operands[1] = XEXP (operands[1], 0); - output_asm_insn (AS1 (call,%*%1), operands); - } - else - output_asm_insn (AS1 (call,%P1), operands); - - RET; -} -} - -static char * -output_235 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - if (GET_CODE (operands[1]) == MEM - && ! CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) - { - operands[1] = XEXP (operands[1], 0); - output_asm_insn (AS1 (call,%*%1), operands); - } - else - output_asm_insn (AS1 (call,%P1), operands); - - RET; -} -} - -static char * -output_238 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx addr = operands[1]; - - if (GET_CODE (operands[0]) == MEM - && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) - { - operands[0] = XEXP (operands[0], 0); - output_asm_insn (AS1 (call,%*%0), operands); - } - else - output_asm_insn (AS1 (call,%P0), operands); - - operands[2] = gen_rtx (REG, SImode, 0); - output_asm_insn (AS2 (mov%L2,%2,%1), operands); - - operands[2] = gen_rtx (REG, SImode, 1); - operands[1] = adj_offsettable_operand (addr, 4); - output_asm_insn (AS2 (mov%L2,%2,%1), operands); - - operands[1] = adj_offsettable_operand (addr, 8); - return AS1 (fnsave,%1); -} -} - -static char * -output_239 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx addr = operands[1]; - - output_asm_insn (AS1 (call,%P0), operands); - - operands[2] = gen_rtx (REG, SImode, 0); - output_asm_insn (AS2 (mov%L2,%2,%1), operands); - - operands[2] = gen_rtx (REG, SImode, 1); - operands[1] = adj_offsettable_operand (addr, 4); - output_asm_insn (AS2 (mov%L2,%2,%1), operands); - - operands[1] = adj_offsettable_operand (addr, 8); - return AS1 (fnsave,%1); -} -} - -static char * -output_242 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - function_epilogue (asm_out_file, get_frame_size ()); - RET; -} -} - -static char * -output_245 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx xops[2]; - - output_asm_insn ("cld", operands); - if (GET_CODE (operands[2]) == CONST_INT) - { - if (INTVAL (operands[2]) & ~0x03) - { - xops[0] = GEN_INT ((INTVAL (operands[2]) >> 2) & 0x3fffffff); - xops[1] = operands[4]; - - output_asm_insn (AS2 (mov%L1,%0,%1), xops); -#ifdef INTEL_SYNTAX - output_asm_insn ("rep movsd", xops); -#else - output_asm_insn ("rep\n\tmovsl", xops); -#endif - } - if (INTVAL (operands[2]) & 0x02) - output_asm_insn ("movsw", operands); - if (INTVAL (operands[2]) & 0x01) - output_asm_insn ("movsb", operands); - } - else - abort (); - RET; -} -} - -static char * -output_247 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx xops[4], label; - - label = gen_label_rtx (); - - output_asm_insn ("cld", operands); - output_asm_insn (AS2 (xor%L0,%0,%0), operands); - output_asm_insn ("repz\n\tcmps%B2", operands); - output_asm_insn ("je %l0", &label); - - xops[0] = operands[0]; - xops[1] = gen_rtx (MEM, QImode, - gen_rtx (PLUS, SImode, operands[1], constm1_rtx)); - xops[2] = gen_rtx (MEM, QImode, - gen_rtx (PLUS, SImode, operands[2], constm1_rtx)); - xops[3] = operands[3]; - - output_asm_insn (AS2 (movz%B1%L0,%1,%0), xops); - output_asm_insn (AS2 (movz%B2%L3,%2,%3), xops); - - output_asm_insn (AS2 (sub%L0,%3,%0), xops); - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (label)); - RET; -} -} - -static char * -output_248 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx xops[2]; - - cc_status.flags |= CC_NOT_SIGNED; - - xops[0] = gen_rtx (REG, QImode, 0); - xops[1] = CONST0_RTX (QImode); - - output_asm_insn ("cld", operands); - output_asm_insn (AS2 (test%B0,%1,%0), xops); - return "repz\n\tcmps%B2"; -} -} - -static char * -output_250 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx xops[2]; - - xops[0] = operands[0]; - xops[1] = constm1_rtx; - output_asm_insn (AS2 (mov%L0,%1,%0), xops); - return AS2 (bsf%L0,%1,%0); -} -} - -static char * -output_252 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx xops[2]; - - xops[0] = operands[0]; - xops[1] = constm1_rtx; - output_asm_insn (AS2 (mov%W0,%1,%0), xops); - return AS2 (bsf%W0,%1,%0); -} -} - -static char * -output_253 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_387_binary_op (insn, operands); -} - -static char * -output_254 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_387_binary_op (insn, operands); -} - -static char * -output_255 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_387_binary_op (insn, operands); -} - -static char * -output_256 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_387_binary_op (insn, operands); -} - -static char * -output_257 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_387_binary_op (insn, operands); -} - -static char * -output_258 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_387_binary_op (insn, operands); -} - -static char * -output_259 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_387_binary_op (insn, operands); -} - -static char * -output_260 (operands, insn) - rtx *operands; - rtx insn; -{ - return (char *) output_387_binary_op (insn, operands); -} - -static char * -output_262 (operands, insn) - rtx *operands; - rtx insn; -{ - -{ - rtx xops[2]; - - xops[0] = operands[0]; - xops[1] = constm1_rtx; - output_asm_insn ("cld", operands); - output_asm_insn (AS2 (mov%L0,%1,%0), xops); - return "repnz\n\tscas%B2"; -} -} - -char * const insn_template[] = - { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - "push%L0 %1", - "push%L0 %1", - 0, - 0, - "push%W0 %1", - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - "mul%B0 %2", - 0, - 0, - "idiv%B0 %2", - "div%B0 %2", - 0, - 0, - 0, - "cwtd\n\tidiv%W0 %2", - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - "neg%L0 %0", - "neg%W0 %0", - "neg%B0 %0", - "fchs", - "fchs", - "fchs", - "fabs", - "fabs", - "fabs", - "fsqrt", - "fsqrt", - "fsqrt", - "fsin", - "fsin", - "fsin", - "fcos", - "fcos", - "fcos", - "not%L0 %0", - "not%W0 %0", - "not%B0 %0", - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - "ja %l0", - 0, - 0, - 0, - "jb %l0", - 0, - 0, - 0, - "jae %l0", - 0, - 0, - 0, - "jbe %l0", - 0, - 0, - 0, - "jbe %l0", - 0, - "jae %l0", - 0, - "jb %l0", - 0, - "ja %l0", - "jmp %l0", - 0, - 0, - 0, - 0, - 0, - 0, - "call %P0", - 0, - 0, - "call %P0", - 0, - 0, - "call %P1", - 0, - 0, - "call %P1", - 0, - 0, - 0, - 0, - "frstor %0", - 0, - "nop", - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - }; - -char *(*const insn_outfun[])() = - { - output_0, - 0, - output_2, - 0, - output_4, - 0, - output_6, - 0, - output_8, - 0, - output_10, - 0, - output_12, - 0, - output_14, - 0, - output_16, - output_17, - output_18, - output_19, - output_20, - output_21, - output_22, - output_23, - output_24, - output_25, - 0, - 0, - 0, - 0, - 0, - 0, - output_32, - output_33, - output_34, - 0, - 0, - 0, - output_38, - 0, - output_40, - output_41, - output_42, - output_43, - output_44, - output_45, - output_46, - output_47, - output_48, - output_49, - output_50, - output_51, - output_52, - output_53, - output_54, - output_55, - output_56, - output_57, - output_58, - output_59, - output_60, - 0, - output_62, - 0, - 0, - 0, - 0, - output_67, - output_68, - 0, - 0, - output_71, - output_72, - 0, - 0, - 0, - 0, - output_77, - output_78, - output_79, - output_80, - output_81, - output_82, - output_83, - output_84, - output_85, - 0, - 0, - output_88, - output_89, - output_90, - output_91, - 0, - 0, - output_94, - output_95, - output_96, - output_97, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - output_105, - 0, - output_107, - output_108, - output_109, - output_110, - output_111, - output_112, - output_113, - output_114, - output_115, - output_116, - output_117, - output_118, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - output_141, - output_142, - output_143, - output_144, - output_145, - 0, - output_147, - output_148, - output_149, - output_150, - output_151, - 0, - output_153, - output_154, - output_155, - output_156, - output_157, - output_158, - output_159, - output_160, - output_161, - output_162, - output_163, - output_164, - output_165, - output_166, - output_167, - output_168, - output_169, - 0, - output_171, - 0, - output_173, - 0, - output_175, - 0, - output_177, - 0, - output_179, - 0, - output_181, - 0, - output_183, - 0, - output_185, - 0, - output_187, - 0, - output_189, - 0, - output_191, - 0, - output_193, - 0, - output_195, - 0, - 0, - 0, - output_199, - 0, - 0, - 0, - output_203, - 0, - 0, - 0, - output_207, - 0, - 0, - output_210, - output_211, - output_212, - 0, - output_214, - 0, - output_216, - 0, - output_218, - 0, - 0, - output_221, - 0, - output_223, - output_224, - 0, - output_226, - 0, - 0, - output_229, - 0, - 0, - output_232, - 0, - 0, - output_235, - 0, - 0, - output_238, - output_239, - 0, - 0, - output_242, - 0, - 0, - output_245, - 0, - output_247, - output_248, - 0, - output_250, - 0, - output_252, - output_253, - output_254, - output_255, - output_256, - output_257, - output_258, - output_259, - output_260, - 0, - output_262, - }; - -rtx (*const insn_gen_function[]) () = - { - gen_tstsi_1, - gen_tstsi, - gen_tsthi_1, - gen_tsthi, - gen_tstqi_1, - gen_tstqi, - gen_tstsf_cc, - gen_tstsf, - gen_tstdf_cc, - gen_tstdf, - gen_cmpsi_1, - gen_cmpsi, - gen_cmphi_1, - gen_cmphi, - gen_cmpqi_1, - gen_cmpqi, - 0, - 0, - 0, - 0, - 0, - 0, - gen_cmpsf_cc_1, - 0, - 0, - 0, - gen_cmpdf, - gen_cmpsf, - gen_cmpdf_cc, - gen_cmpdf_ccfpeq, - gen_cmpsf_cc, - gen_cmpsf_ccfpeq, - 0, - 0, - 0, - 0, - 0, - gen_movsi, - 0, - 0, - gen_movhi, - gen_movstricthi, - 0, - gen_movqi, - gen_movstrictqi, - 0, - gen_movsf, - 0, - gen_swapdf, - gen_movdf, - 0, - gen_movdi, - gen_zero_extendhisi2, - gen_zero_extendqihi2, - gen_zero_extendqisi2, - gen_zero_extendsidi2, - gen_extendsidi2, - gen_extendhisi2, - gen_extendqihi2, - gen_extendqisi2, - gen_extendsfdf2, - gen_truncdfsf2, - 0, - gen_fixuns_truncdfsi2, - gen_fixuns_truncsfsi2, - gen_fix_truncdfdi2, - gen_fix_truncsfdi2, - 0, - 0, - gen_fix_truncdfsi2, - gen_fix_truncsfsi2, - 0, - 0, - gen_floatsisf2, - gen_floatdisf2, - gen_floatsidf2, - gen_floatdidf2, - 0, - 0, - 0, - 0, - gen_adddi3, - gen_addsi3, - gen_addhi3, - gen_addqi3, - 0, - gen_adddf3, - gen_addsf3, - gen_subdi3, - gen_subsi3, - gen_subhi3, - gen_subqi3, - gen_subdf3, - gen_subsf3, - 0, - gen_mulhi3, - 0, - gen_mulsi3, - 0, - gen_muldf3, - gen_mulsf3, - gen_divqi3, - gen_udivqi3, - gen_divdf3, - gen_divsf3, - gen_divmodsi4, - gen_divmodhi4, - gen_udivmodsi4, - gen_udivmodhi4, - gen_andsi3, - gen_andhi3, - gen_andqi3, - gen_iorsi3, - gen_iorhi3, - gen_iorqi3, - gen_xorsi3, - gen_xorhi3, - gen_xorqi3, - gen_negdi2, - gen_negsi2, - gen_neghi2, - gen_negqi2, - gen_negsf2, - gen_negdf2, - 0, - gen_abssf2, - gen_absdf2, - 0, - gen_sqrtsf2, - gen_sqrtdf2, - 0, - gen_sindf2, - gen_sinsf2, - 0, - gen_cosdf2, - gen_cossf2, - 0, - gen_one_cmplsi2, - gen_one_cmplhi2, - gen_one_cmplqi2, - gen_ashldi3, - gen_ashldi3_const_int, - gen_ashldi3_non_const_int, - gen_ashlsi3, - gen_ashlhi3, - gen_ashlqi3, - gen_ashrdi3, - gen_ashrdi3_const_int, - gen_ashrdi3_non_const_int, - gen_ashrsi3, - gen_ashrhi3, - gen_ashrqi3, - gen_lshrdi3, - gen_lshrdi3_const_int, - gen_lshrdi3_non_const_int, - gen_lshrsi3, - gen_lshrhi3, - gen_lshrqi3, - gen_rotlsi3, - gen_rotlhi3, - gen_rotlqi3, - gen_rotrsi3, - gen_rotrhi3, - gen_rotrqi3, - 0, - 0, - 0, - 0, - 0, - 0, - gen_seq, - 0, - gen_sne, - 0, - gen_sgt, - 0, - gen_sgtu, - 0, - gen_slt, - 0, - gen_sltu, - 0, - gen_sge, - 0, - gen_sgeu, - 0, - gen_sle, - 0, - gen_sleu, - 0, - gen_beq, - 0, - gen_bne, - 0, - gen_bgt, - 0, - gen_bgtu, - 0, - gen_blt, - 0, - gen_bltu, - 0, - gen_bge, - 0, - gen_bgeu, - 0, - gen_ble, - 0, - gen_bleu, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - gen_jump, - gen_indirect_jump, - gen_casesi, - 0, - gen_tablejump, - gen_call_pop, - 0, - 0, - gen_call, - 0, - 0, - gen_call_value_pop, - 0, - 0, - gen_call_value, - 0, - 0, - gen_untyped_call, - 0, - 0, - gen_untyped_return, - gen_update_return, - gen_return, - gen_nop, - gen_movstrsi, - 0, - gen_cmpstrsi, - 0, - 0, - gen_ffssi2, - 0, - gen_ffshi2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - gen_strlensi, - 0, - }; - -char *insn_name[] = - { - "tstsi_1", - "tstsi", - "tsthi_1", - "tsthi", - "tstqi_1", - "tstqi", - "tstsf_cc", - "tstsf", - "tstdf_cc", - "tstdf", - "cmpsi_1", - "cmpsi", - "cmphi_1", - "cmphi", - "cmpqi_1", - "cmpqi", - "cmpqi+1", - "cmpqi+2", - "cmpqi+3", - "cmpsf_cc_1-3", - "cmpsf_cc_1-2", - "cmpsf_cc_1-1", - "cmpsf_cc_1", - "cmpsf_cc_1+1", - "cmpsf_cc_1+2", - "cmpdf-1", - "cmpdf", - "cmpsf", - "cmpdf_cc", - "cmpdf_ccfpeq", - "cmpsf_cc", - "cmpsf_ccfpeq", - "cmpsf_ccfpeq+1", - "cmpsf_ccfpeq+2", - "cmpsf_ccfpeq+3", - "movsi-2", - "movsi-1", - "movsi", - "movsi+1", - "movhi-1", - "movhi", - "movstricthi", - "movstricthi+1", - "movqi", - "movstrictqi", - "movstrictqi+1", - "movsf", - "movsf+1", - "swapdf", - "movdf", - "movdf+1", - "movdi", - "zero_extendhisi2", - "zero_extendqihi2", - "zero_extendqisi2", - "zero_extendsidi2", - "extendsidi2", - "extendhisi2", - "extendqihi2", - "extendqisi2", - "extendsfdf2", - "truncdfsf2", - "truncdfsf2+1", - "fixuns_truncdfsi2", - "fixuns_truncsfsi2", - "fix_truncdfdi2", - "fix_truncsfdi2", - "fix_truncsfdi2+1", - "fix_truncdfsi2-1", - "fix_truncdfsi2", - "fix_truncsfsi2", - "fix_truncsfsi2+1", - "floatsisf2-1", - "floatsisf2", - "floatdisf2", - "floatsidf2", - "floatdidf2", - "floatdidf2+1", - "floatdidf2+2", - "adddi3-2", - "adddi3-1", - "adddi3", - "addsi3", - "addhi3", - "addqi3", - "addqi3+1", - "adddf3", - "addsf3", - "subdi3", - "subsi3", - "subhi3", - "subqi3", - "subdf3", - "subsf3", - "subsf3+1", - "mulhi3", - "mulhi3+1", - "mulsi3", - "mulsi3+1", - "muldf3", - "mulsf3", - "divqi3", - "udivqi3", - "divdf3", - "divsf3", - "divmodsi4", - "divmodhi4", - "udivmodsi4", - "udivmodhi4", - "andsi3", - "andhi3", - "andqi3", - "iorsi3", - "iorhi3", - "iorqi3", - "xorsi3", - "xorhi3", - "xorqi3", - "negdi2", - "negsi2", - "neghi2", - "negqi2", - "negsf2", - "negdf2", - "negdf2+1", - "abssf2", - "absdf2", - "absdf2+1", - "sqrtsf2", - "sqrtdf2", - "sqrtdf2+1", - "sindf2", - "sinsf2", - "sinsf2+1", - "cosdf2", - "cossf2", - "cossf2+1", - "one_cmplsi2", - "one_cmplhi2", - "one_cmplqi2", - "ashldi3", - "ashldi3_const_int", - "ashldi3_non_const_int", - "ashlsi3", - "ashlhi3", - "ashlqi3", - "ashrdi3", - "ashrdi3_const_int", - "ashrdi3_non_const_int", - "ashrsi3", - "ashrhi3", - "ashrqi3", - "lshrdi3", - "lshrdi3_const_int", - "lshrdi3_non_const_int", - "lshrsi3", - "lshrhi3", - "lshrqi3", - "rotlsi3", - "rotlhi3", - "rotlqi3", - "rotrsi3", - "rotrhi3", - "rotrqi3", - "rotrqi3+1", - "rotrqi3+2", - "rotrqi3+3", - "seq-3", - "seq-2", - "seq-1", - "seq", - "seq+1", - "sne", - "sne+1", - "sgt", - "sgt+1", - "sgtu", - "sgtu+1", - "slt", - "slt+1", - "sltu", - "sltu+1", - "sge", - "sge+1", - "sgeu", - "sgeu+1", - "sle", - "sle+1", - "sleu", - "sleu+1", - "beq", - "beq+1", - "bne", - "bne+1", - "bgt", - "bgt+1", - "bgtu", - "bgtu+1", - "blt", - "blt+1", - "bltu", - "bltu+1", - "bge", - "bge+1", - "bgeu", - "bgeu+1", - "ble", - "ble+1", - "bleu", - "bleu+1", - "bleu+2", - "bleu+3", - "bleu+4", - "bleu+5", - "bleu+6", - "jump-5", - "jump-4", - "jump-3", - "jump-2", - "jump-1", - "jump", - "indirect_jump", - "casesi", - "casesi+1", - "tablejump", - "call_pop", - "call_pop+1", - "call-1", - "call", - "call+1", - "call_value_pop-1", - "call_value_pop", - "call_value_pop+1", - "call_value-1", - "call_value", - "call_value+1", - "untyped_call-1", - "untyped_call", - "untyped_call+1", - "untyped_return-1", - "untyped_return", - "update_return", - "return", - "nop", - "movstrsi", - "movstrsi+1", - "cmpstrsi", - "cmpstrsi+1", - "ffssi2-1", - "ffssi2", - "ffssi2+1", - "ffshi2", - "ffshi2+1", - "ffshi2+2", - "ffshi2+3", - "ffshi2+4", - "ffshi2+5", - "strlensi-4", - "strlensi-3", - "strlensi-2", - "strlensi-1", - "strlensi", - "strlensi+1", - }; -char **insn_name_ptr = insn_name; - -const int insn_n_operands[] = - { - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 4, - 4, - 4, - 4, - 4, - 3, - 4, - 4, - 4, - 3, - 2, - 2, - 3, - 3, - 3, - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 3, - 8, - 8, - 6, - 6, - 5, - 5, - 5, - 5, - 5, - 5, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 3, - 3, - 3, - 3, - 2, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 4, - 4, - 4, - 4, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 4, - 3, - 3, - 2, - 3, - 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 7, - 3, - 1, - 4, - 4, - 4, - 2, - 2, - 2, - 5, - 5, - 5, - 3, - 3, - 3, - 3, - 3, - 3, - 2, - 1, - 0, - 0, - 5, - 5, - 5, - 5, - 4, - 2, - 2, - 2, - 2, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - }; - -const int insn_n_dups[] = - { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 7, - 7, - 5, - 5, - 1, - 1, - 2, - 2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 2, - 2, - 2, - 2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 3, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 2, - 2, - 3, - 3, - 3, - 2, - 0, - 2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 5, - 1, - }; - -char *const insn_operand_constraint[][MAX_RECOG_OPERANDS] = - { - { "rm", }, - { "", }, - { "rm", }, - { "", }, - { "qm", }, - { "", }, - { "f", "=a", }, - { "", "", }, - { "f", "=a", }, - { "", "", }, - { "mr,r", "ri,mr", }, - { "", "", }, - { "mr,r", "ri,mr", }, - { "", "", }, - { "q,mq", "qm,nq", }, - { "", "", }, - { "f,fm", "fm,f", "", "=a,a", }, - { "f", "rm", "", "=a", }, - { "rm", "f", "", "=a", }, - { "f", "fm", "", "=a", }, - { "fm", "f", "", "=a", }, - { "f", "f", "=a", }, - { "f,fm", "fm,f", "", "=a,a", }, - { "f", "rm", "", "=a", }, - { "rm", "f", "", "=a", }, - { "f", "f", "=a", }, - { "", "", }, - { "", "", }, - { "", "", "", }, - { "", "", "", }, - { "", "", "", }, - { "", "", "", }, - { "%ro", "ri", }, - { "%ro", "ri", }, - { "%qm", "qi", }, - { "=<", "g", }, - { "=<", "ri", }, - { "", "", }, - { "=g,r", "ri,m", }, - { "=<", "g", }, - { "=g,r", "ri,m", }, - { "+g,r", "ri,m", }, - { "=<", "q", }, - { "=q,*r,qm", "*g,q,qn", }, - { "+q,qm", "*g,qn", }, - { "=<,<", "gF,f", }, - { "=*rfm,*rf,f,!*rm", "*rf,*rfm,fG,fF", }, - { "=<,<", "gF,f", }, - { "f", "f", }, - { "=*rfm,*rf,f,!*rm", "*rf,*rfm,fG,fF", }, - { "=<", "roiF", }, - { "=r,rm", "m,riF", }, - { "=r", "rm", }, - { "=r", "qm", }, - { "=r", "qm", }, - { "=r", "0", }, - { "=r", "0", }, - { "=r", "rm", }, - { "=r", "qm", }, - { "=r", "qm", }, - { "=fm,f", "f,fm", }, - { "", "", }, - { "=f,m", "0,f", "m,m", }, - { "", "", "", "", "", "", "", "", }, - { "", "", "", "", "", "", "", "", }, - { "", "", "", "", "", "", }, - { "", "", "", "", "", "", }, - { "=rm", "f", "m", "m", "=&q", }, - { "=rm", "f", "m", "m", "=&q", }, - { "", "", "", "", "", }, - { "", "", "", "", "", }, - { "=rm", "f", "m", "m", "=&q", }, - { "=rm", "f", "m", "m", "=&q", }, - { "", "", }, - { "", "", }, - { "", "", }, - { "", "", }, - { "=f", "rm", }, - { "=f", "rm", }, - { "=f", "rm", }, - { "=f", "rm", }, - { "=&r,ro", "%0,0", "o,riF", }, - { "=?r,rm,r", "%r,0,0", "ri,ri,rm", }, - { "=rm,r", "%0,0", "ri,rm", }, - { "=qm,q", "%0,0", "qn,qmn", }, - { "=r", "p", }, - { "", "", "", }, - { "", "", "", }, - { "=&r,ro", "0,0", "o,riF", }, - { "=rm,r", "0,0", "ri,rm", }, - { "=rm,r", "0,0", "ri,rm", }, - { "=qm,q", "0,0", "qn,qmn", }, - { "", "", "", }, - { "", "", "", }, - { "=r", "%0", "r", }, - { "=r,r", "%0,rm", "g,i", }, - { "=r", "%0", "r", }, - { "=r,r", "%0,rm", "g,i", }, - { "=a", "%0", "qm", }, - { "", "", "", }, - { "", "", "", }, - { "=a", "0", "qm", }, - { "=a", "0", "qm", }, - { "", "", "", }, - { "", "", "", }, - { "=a", "0", "rm", "=&d", }, - { "=a", "0", "rm", "=&d", }, - { "=a", "0", "rm", "=&d", }, - { "=a", "0", "rm", "=&d", }, - { "=r,r,rm,r", "%rm,qm,0,0", "L,K,ri,rm", }, - { "=rm,r", "%0,0", "ri,rm", }, - { "=qm,q", "%0,0", "qn,qmn", }, - { "=rm,r", "%0,0", "ri,rm", }, - { "=rm,r", "%0,0", "ri,rm", }, - { "=qm,q", "%0,0", "qn,qmn", }, - { "=rm,r", "%0,0", "ri,rm", }, - { "=rm,r", "%0,0", "ri,rm", }, - { "=qm,q", "%0,0", "qn,qm", }, - { "=&ro", "0", }, - { "=rm", "0", }, - { "=rm", "0", }, - { "=qm", "0", }, - { "=f", "0", }, - { "=f", "0", }, - { "=f", "0", }, - { "=f", "0", }, - { "=f", "0", }, - { "=f", "0", }, - { "=f", "0", }, - { "=f", "0", }, - { "=f", "0", }, - { "=f", "0", }, - { "=f", "0", }, - { "=f", "0", }, - { "=f", "0", }, - { "=f", "0", }, - { "=f", "0", }, - { "=rm", "0", }, - { "=rm", "0", }, - { "=qm", "0", }, - { "", "", "", }, - { "=&r", "0", "J", }, - { "=&r", "0", "c", }, - { "=r,rm", "r,0", "M,cI", }, - { "=rm", "0", "cI", }, - { "=qm", "0", "cI", }, - { "", "", "", }, - { "=&r", "0", "J", }, - { "=&r", "0", "c", }, - { "=rm", "0", "cI", }, - { "=rm", "0", "cI", }, - { "=qm", "0", "cI", }, - { "", "", "", }, - { "=&r", "0", "J", }, - { "=&r", "0", "c", }, - { "=rm", "0", "cI", }, - { "=rm", "0", "cI", }, - { "=qm", "0", "cI", }, - { "=rm", "0", "cI", }, - { "=rm", "0", "cI", }, - { "=qm", "0", "cI", }, - { "=rm", "0", "cI", }, - { "=rm", "0", "cI", }, - { "=qm", "0", "cI", }, - { "+rm", "", "r", "n", }, - { "=rm", "r", "0", }, - { "=rm", "0", "r", }, - { "r", "r", }, - { "r", "n", "n", }, - { "rm", "n", "n", }, - { "", }, - { "=q", }, - { "", }, - { "=q", }, - { "", }, - { "=q", }, - { "", }, - { "=q", }, - { "", }, - { "=q", }, - { "", }, - { "=q", }, - { "", }, - { "=q", }, - { "", }, - { "=q", }, - { "", }, - { "=q", }, - { "", }, - { "=q", }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { "rm", }, - { "", "", "", "", "", "", "", }, - { "r", "", "=&r", }, - { "rm", }, - { "", "", "", "", }, - { "m", "g", "", "i", }, - { "", "g", "", "i", }, - { "", "", }, - { "m", "g", }, - { "", "g", }, - { "", "", "", "", "", }, - { "=rf", "m", "g", "", "i", }, - { "=rf", "", "g", "", "i", }, - { "", "", "", }, - { "=rf", "m", "g", }, - { "=rf", "", "g", }, - { "", "", "", }, - { "m", "o", "", }, - { "", "o", "", }, - { "", "", }, - { "m", }, - { 0 }, - { 0 }, - { "", "", "", "", "", }, - { "D", "S", "n", "i", "=&c", }, - { "", "", "", "", "", }, - { "=&r", "S", "D", "c", "i", }, - { "S", "D", "c", "i", }, - { "", "", }, - { "=&r", "rm", }, - { "", "", }, - { "=&r", "rm", }, - { "=f,f", "0,fm", "fm,0", "", }, - { "=f", "rm", "0", "", }, - { "=f,f", "fm,0", "0,f", "", }, - { "=f", "0", "rm", "", }, - { "=f,f", "0,f", "fm,0", "", }, - { "=f,f", "0,fm", "fm,0", "", }, - { "=f", "rm", "0", "", }, - { "=f", "0", "rm", "", }, - { "", "", "", "", }, - { "=&c", "D", "a", "i", }, - }; - -const enum machine_mode insn_operand_mode[][MAX_RECOG_OPERANDS] = - { - { SImode, }, - { SImode, }, - { HImode, }, - { HImode, }, - { QImode, }, - { QImode, }, - { SFmode, HImode, }, - { SFmode, HImode, }, - { DFmode, HImode, }, - { DFmode, HImode, }, - { SImode, SImode, }, - { SImode, SImode, }, - { HImode, HImode, }, - { HImode, HImode, }, - { QImode, QImode, }, - { QImode, QImode, }, - { DFmode, DFmode, VOIDmode, HImode, }, - { DFmode, SImode, VOIDmode, HImode, }, - { SImode, DFmode, VOIDmode, HImode, }, - { DFmode, SFmode, VOIDmode, HImode, }, - { SFmode, DFmode, VOIDmode, HImode, }, - { DFmode, DFmode, HImode, }, - { SFmode, SFmode, VOIDmode, HImode, }, - { SFmode, SImode, VOIDmode, HImode, }, - { SImode, SFmode, VOIDmode, HImode, }, - { SFmode, SFmode, HImode, }, - { DFmode, DFmode, }, - { SFmode, SFmode, }, - { DFmode, DFmode, HImode, }, - { DFmode, DFmode, HImode, }, - { SFmode, SFmode, HImode, }, - { SFmode, SFmode, HImode, }, - { SImode, SImode, }, - { HImode, HImode, }, - { QImode, QImode, }, - { SImode, SImode, }, - { SImode, SImode, }, - { SImode, SImode, }, - { SImode, SImode, }, - { HImode, HImode, }, - { HImode, HImode, }, - { HImode, HImode, }, - { QImode, QImode, }, - { QImode, QImode, }, - { QImode, QImode, }, - { SFmode, SFmode, }, - { SFmode, SFmode, }, - { DFmode, DFmode, }, - { DFmode, DFmode, }, - { DFmode, DFmode, }, - { DImode, DImode, }, - { DImode, DImode, }, - { SImode, HImode, }, - { HImode, QImode, }, - { SImode, QImode, }, - { DImode, SImode, }, - { DImode, SImode, }, - { SImode, HImode, }, - { HImode, QImode, }, - { SImode, QImode, }, - { DFmode, SFmode, }, - { SFmode, DFmode, }, - { SFmode, DFmode, SFmode, }, - { SImode, DFmode, VOIDmode, VOIDmode, VOIDmode, VOIDmode, VOIDmode, SImode, }, - { SImode, SFmode, VOIDmode, VOIDmode, VOIDmode, VOIDmode, VOIDmode, SImode, }, - { DImode, DFmode, VOIDmode, VOIDmode, VOIDmode, SImode, }, - { DImode, SFmode, VOIDmode, VOIDmode, VOIDmode, SImode, }, - { DImode, DFmode, SImode, SImode, SImode, }, - { DImode, SFmode, SImode, SImode, SImode, }, - { SImode, DFmode, VOIDmode, VOIDmode, SImode, }, - { SImode, SFmode, VOIDmode, VOIDmode, SImode, }, - { SImode, DFmode, SImode, SImode, SImode, }, - { SImode, SFmode, SImode, SImode, SImode, }, - { SFmode, SImode, }, - { SFmode, DImode, }, - { DFmode, SImode, }, - { DFmode, DImode, }, - { DFmode, DImode, }, - { SFmode, DImode, }, - { DFmode, SImode, }, - { SFmode, SImode, }, - { DImode, DImode, DImode, }, - { SImode, SImode, SImode, }, - { HImode, HImode, HImode, }, - { QImode, QImode, QImode, }, - { SImode, QImode, }, - { DFmode, DFmode, DFmode, }, - { SFmode, SFmode, SFmode, }, - { DImode, DImode, DImode, }, - { SImode, SImode, SImode, }, - { HImode, HImode, HImode, }, - { QImode, QImode, QImode, }, - { DFmode, DFmode, DFmode, }, - { SFmode, SFmode, SFmode, }, - { HImode, HImode, HImode, }, - { HImode, HImode, HImode, }, - { SImode, SImode, SImode, }, - { SImode, SImode, SImode, }, - { HImode, QImode, QImode, }, - { DFmode, DFmode, DFmode, }, - { SFmode, SFmode, SFmode, }, - { QImode, HImode, QImode, }, - { QImode, HImode, QImode, }, - { DFmode, DFmode, DFmode, }, - { SFmode, SFmode, SFmode, }, - { SImode, SImode, SImode, SImode, }, - { HImode, HImode, HImode, HImode, }, - { SImode, SImode, SImode, SImode, }, - { HImode, HImode, HImode, HImode, }, - { SImode, SImode, SImode, }, - { HImode, HImode, HImode, }, - { QImode, QImode, QImode, }, - { SImode, SImode, SImode, }, - { HImode, HImode, HImode, }, - { QImode, QImode, QImode, }, - { SImode, SImode, SImode, }, - { HImode, HImode, HImode, }, - { QImode, QImode, QImode, }, - { DImode, DImode, }, - { SImode, SImode, }, - { HImode, HImode, }, - { QImode, QImode, }, - { SFmode, SFmode, }, - { DFmode, DFmode, }, - { DFmode, SFmode, }, - { SFmode, SFmode, }, - { DFmode, DFmode, }, - { DFmode, SFmode, }, - { SFmode, SFmode, }, - { DFmode, DFmode, }, - { DFmode, SFmode, }, - { DFmode, DFmode, }, - { SFmode, SFmode, }, - { DFmode, SFmode, }, - { DFmode, DFmode, }, - { SFmode, SFmode, }, - { DFmode, SFmode, }, - { SImode, SImode, }, - { HImode, HImode, }, - { QImode, QImode, }, - { DImode, DImode, QImode, }, - { DImode, DImode, QImode, }, - { DImode, DImode, QImode, }, - { SImode, SImode, SImode, }, - { HImode, HImode, HImode, }, - { QImode, QImode, QImode, }, - { DImode, DImode, QImode, }, - { DImode, DImode, QImode, }, - { DImode, DImode, QImode, }, - { SImode, SImode, SImode, }, - { HImode, HImode, HImode, }, - { QImode, QImode, QImode, }, - { DImode, DImode, QImode, }, - { DImode, DImode, QImode, }, - { DImode, DImode, QImode, }, - { SImode, SImode, SImode, }, - { HImode, HImode, HImode, }, - { QImode, QImode, QImode, }, - { SImode, SImode, SImode, }, - { HImode, HImode, HImode, }, - { QImode, QImode, QImode, }, - { SImode, SImode, SImode, }, - { HImode, HImode, HImode, }, - { QImode, QImode, QImode, }, - { SImode, VOIDmode, SImode, SImode, }, - { SImode, SImode, SImode, }, - { SImode, SImode, SImode, }, - { SImode, SImode, }, - { SImode, SImode, SImode, }, - { QImode, SImode, SImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { QImode, }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { VOIDmode }, - { SImode, }, - { SImode, SImode, SImode, VOIDmode, VOIDmode, VOIDmode, SImode, }, - { SImode, VOIDmode, SImode, }, - { SImode, }, - { QImode, SImode, VOIDmode, SImode, }, - { QImode, SImode, VOIDmode, SImode, }, - { SImode, SImode, VOIDmode, SImode, }, - { QImode, SImode, }, - { QImode, SImode, }, - { SImode, SImode, }, - { VOIDmode, QImode, SImode, VOIDmode, SImode, }, - { VOIDmode, QImode, SImode, VOIDmode, SImode, }, - { VOIDmode, SImode, SImode, VOIDmode, SImode, }, - { VOIDmode, QImode, SImode, }, - { VOIDmode, QImode, SImode, }, - { VOIDmode, SImode, SImode, }, - { QImode, BLKmode, VOIDmode, }, - { QImode, DImode, VOIDmode, }, - { SImode, DImode, VOIDmode, }, - { BLKmode, VOIDmode, }, - { SImode, }, - { VOIDmode }, - { VOIDmode }, - { BLKmode, BLKmode, SImode, SImode, SImode, }, - { SImode, SImode, SImode, SImode, SImode, }, - { SImode, BLKmode, BLKmode, SImode, SImode, }, - { SImode, SImode, SImode, SImode, SImode, }, - { SImode, SImode, SImode, SImode, }, - { SImode, SImode, }, - { SImode, SImode, }, - { HImode, HImode, }, - { HImode, SImode, }, - { DFmode, DFmode, DFmode, DFmode, }, - { DFmode, SImode, DFmode, DFmode, }, - { DFmode, SFmode, DFmode, DFmode, }, - { DFmode, DFmode, SImode, DFmode, }, - { DFmode, DFmode, SFmode, DFmode, }, - { SFmode, SFmode, SFmode, SFmode, }, - { SFmode, SImode, SFmode, SFmode, }, - { SFmode, SFmode, SImode, SFmode, }, - { SImode, BLKmode, QImode, SImode, }, - { SImode, SImode, QImode, SImode, }, - }; - -const char insn_operand_strict_low[][MAX_RECOG_OPERANDS] = - { - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 1, 0, }, - { 0, 0, }, - { 0, 0, }, - { 1, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, 0, 0, 0, 0, 0, }, - { 0, 0, 0, 0, 0, 0, 0, 0, }, - { 0, 0, 0, 0, 0, 0, }, - { 0, 0, 0, 0, 0, 0, }, - { 0, 0, 0, 0, 0, }, - { 0, 0, 0, 0, 0, }, - { 0, 0, 0, 0, 0, }, - { 0, 0, 0, 0, 0, }, - { 0, 0, 0, 0, 0, }, - { 0, 0, 0, 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0, }, - { 0, 0, 0, 0, 0, 0, 0, }, - { 0, 0, 0, }, - { 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, 0, 0, 0, }, - { 0, 0, 0, 0, 0, }, - { 0, 0, 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, 0, }, - { 0, 0, }, - { 0, }, - { 0 }, - { 0 }, - { 0, 0, 0, 0, 0, }, - { 0, 0, 0, 0, 0, }, - { 0, 0, 0, 0, 0, }, - { 0, 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - { 0, 0, 0, 0, }, - }; - -extern int nonimmediate_operand (); -extern int register_operand (); -extern int scratch_operand (); -extern int general_operand (); -extern int VOIDmode_compare_op (); -extern int push_operand (); -extern int memory_operand (); -extern int address_operand (); -extern int nonmemory_operand (); -extern int const_int_operand (); -extern int indirect_operand (); -extern int immediate_operand (); -extern int call_insn_operand (); -extern int symbolic_operand (); -extern int binary_387_op (); - -int (*const insn_operand_predicate[][MAX_RECOG_OPERANDS])() = - { - { nonimmediate_operand, }, - { nonimmediate_operand, }, - { nonimmediate_operand, }, - { nonimmediate_operand, }, - { nonimmediate_operand, }, - { nonimmediate_operand, }, - { register_operand, scratch_operand, }, - { register_operand, scratch_operand, }, - { register_operand, scratch_operand, }, - { register_operand, scratch_operand, }, - { nonimmediate_operand, general_operand, }, - { nonimmediate_operand, general_operand, }, - { nonimmediate_operand, general_operand, }, - { nonimmediate_operand, general_operand, }, - { nonimmediate_operand, general_operand, }, - { nonimmediate_operand, general_operand, }, - { nonimmediate_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, }, - { register_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, }, - { nonimmediate_operand, register_operand, VOIDmode_compare_op, scratch_operand, }, - { register_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, }, - { nonimmediate_operand, register_operand, VOIDmode_compare_op, scratch_operand, }, - { register_operand, register_operand, scratch_operand, }, - { nonimmediate_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, }, - { register_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, }, - { nonimmediate_operand, register_operand, VOIDmode_compare_op, scratch_operand, }, - { register_operand, register_operand, scratch_operand, }, - { register_operand, nonimmediate_operand, }, - { register_operand, nonimmediate_operand, }, - { register_operand, register_operand, scratch_operand, }, - { register_operand, register_operand, scratch_operand, }, - { register_operand, register_operand, scratch_operand, }, - { register_operand, register_operand, scratch_operand, }, - { general_operand, general_operand, }, - { general_operand, general_operand, }, - { general_operand, general_operand, }, - { push_operand, general_operand, }, - { push_operand, general_operand, }, - { general_operand, general_operand, }, - { general_operand, general_operand, }, - { push_operand, general_operand, }, - { general_operand, general_operand, }, - { general_operand, general_operand, }, - { push_operand, general_operand, }, - { general_operand, general_operand, }, - { general_operand, general_operand, }, - { push_operand, general_operand, }, - { general_operand, general_operand, }, - { push_operand, general_operand, }, - { register_operand, register_operand, }, - { general_operand, general_operand, }, - { push_operand, general_operand, }, - { general_operand, general_operand, }, - { general_operand, nonimmediate_operand, }, - { general_operand, nonimmediate_operand, }, - { general_operand, nonimmediate_operand, }, - { register_operand, register_operand, }, - { register_operand, register_operand, }, - { general_operand, nonimmediate_operand, }, - { general_operand, nonimmediate_operand, }, - { general_operand, nonimmediate_operand, }, - { general_operand, general_operand, }, - { nonimmediate_operand, register_operand, }, - { nonimmediate_operand, register_operand, memory_operand, }, - { general_operand, register_operand, 0, 0, 0, 0, 0, scratch_operand, }, - { general_operand, register_operand, 0, 0, 0, 0, 0, scratch_operand, }, - { general_operand, register_operand, 0, 0, 0, scratch_operand, }, - { general_operand, register_operand, 0, 0, 0, scratch_operand, }, - { general_operand, register_operand, memory_operand, memory_operand, scratch_operand, }, - { general_operand, register_operand, memory_operand, memory_operand, scratch_operand, }, - { general_operand, register_operand, 0, 0, scratch_operand, }, - { general_operand, register_operand, 0, 0, scratch_operand, }, - { general_operand, register_operand, memory_operand, memory_operand, scratch_operand, }, - { general_operand, register_operand, memory_operand, memory_operand, scratch_operand, }, - { register_operand, nonimmediate_operand, }, - { register_operand, nonimmediate_operand, }, - { register_operand, nonimmediate_operand, }, - { register_operand, nonimmediate_operand, }, - { register_operand, nonimmediate_operand, }, - { register_operand, nonimmediate_operand, }, - { register_operand, nonimmediate_operand, }, - { register_operand, nonimmediate_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { register_operand, address_operand, }, - { register_operand, nonimmediate_operand, nonimmediate_operand, }, - { register_operand, nonimmediate_operand, nonimmediate_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { register_operand, nonimmediate_operand, nonimmediate_operand, }, - { register_operand, nonimmediate_operand, nonimmediate_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, nonimmediate_operand, nonimmediate_operand, }, - { register_operand, nonimmediate_operand, nonimmediate_operand, }, - { register_operand, nonimmediate_operand, nonimmediate_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { register_operand, nonimmediate_operand, nonimmediate_operand, }, - { register_operand, nonimmediate_operand, nonimmediate_operand, }, - { register_operand, register_operand, general_operand, register_operand, }, - { register_operand, register_operand, general_operand, register_operand, }, - { register_operand, register_operand, general_operand, register_operand, }, - { register_operand, register_operand, general_operand, register_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, }, - { general_operand, general_operand, }, - { general_operand, general_operand, }, - { general_operand, general_operand, }, - { register_operand, general_operand, }, - { register_operand, general_operand, }, - { register_operand, general_operand, }, - { register_operand, general_operand, }, - { register_operand, general_operand, }, - { register_operand, general_operand, }, - { register_operand, general_operand, }, - { register_operand, general_operand, }, - { register_operand, general_operand, }, - { register_operand, register_operand, }, - { register_operand, register_operand, }, - { register_operand, register_operand, }, - { register_operand, register_operand, }, - { register_operand, register_operand, }, - { register_operand, register_operand, }, - { general_operand, general_operand, }, - { general_operand, general_operand, }, - { general_operand, general_operand, }, - { register_operand, register_operand, nonmemory_operand, }, - { register_operand, register_operand, const_int_operand, }, - { register_operand, register_operand, register_operand, }, - { general_operand, general_operand, nonmemory_operand, }, - { general_operand, general_operand, nonmemory_operand, }, - { general_operand, general_operand, nonmemory_operand, }, - { register_operand, register_operand, nonmemory_operand, }, - { register_operand, register_operand, const_int_operand, }, - { register_operand, register_operand, register_operand, }, - { general_operand, general_operand, nonmemory_operand, }, - { general_operand, general_operand, nonmemory_operand, }, - { general_operand, general_operand, nonmemory_operand, }, - { register_operand, register_operand, nonmemory_operand, }, - { register_operand, register_operand, const_int_operand, }, - { register_operand, register_operand, register_operand, }, - { general_operand, general_operand, nonmemory_operand, }, - { general_operand, general_operand, nonmemory_operand, }, - { general_operand, general_operand, nonmemory_operand, }, - { general_operand, general_operand, nonmemory_operand, }, - { general_operand, general_operand, nonmemory_operand, }, - { general_operand, general_operand, nonmemory_operand, }, - { general_operand, general_operand, nonmemory_operand, }, - { general_operand, general_operand, nonmemory_operand, }, - { general_operand, general_operand, nonmemory_operand, }, - { general_operand, 0, general_operand, const_int_operand, }, - { general_operand, general_operand, general_operand, }, - { general_operand, general_operand, general_operand, }, - { register_operand, general_operand, }, - { register_operand, const_int_operand, const_int_operand, }, - { general_operand, const_int_operand, const_int_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { register_operand, }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { general_operand, }, - { general_operand, general_operand, general_operand, 0, 0, 0, scratch_operand, }, - { register_operand, 0, scratch_operand, }, - { general_operand, }, - { indirect_operand, general_operand, 0, immediate_operand, }, - { call_insn_operand, general_operand, 0, immediate_operand, }, - { symbolic_operand, general_operand, 0, immediate_operand, }, - { indirect_operand, general_operand, }, - { call_insn_operand, general_operand, }, - { symbolic_operand, general_operand, }, - { 0, indirect_operand, general_operand, 0, immediate_operand, }, - { 0, call_insn_operand, general_operand, 0, immediate_operand, }, - { 0, symbolic_operand, general_operand, 0, immediate_operand, }, - { 0, indirect_operand, general_operand, }, - { 0, call_insn_operand, general_operand, }, - { 0, symbolic_operand, general_operand, }, - { indirect_operand, memory_operand, 0, }, - { call_insn_operand, memory_operand, 0, }, - { symbolic_operand, memory_operand, 0, }, - { memory_operand, 0, }, - { memory_operand, }, - { 0 }, - { 0 }, - { memory_operand, memory_operand, const_int_operand, const_int_operand, scratch_operand, }, - { address_operand, address_operand, const_int_operand, immediate_operand, scratch_operand, }, - { general_operand, general_operand, general_operand, general_operand, immediate_operand, }, - { general_operand, address_operand, address_operand, register_operand, immediate_operand, }, - { address_operand, address_operand, register_operand, immediate_operand, }, - { general_operand, general_operand, }, - { general_operand, general_operand, }, - { general_operand, general_operand, }, - { general_operand, general_operand, }, - { register_operand, nonimmediate_operand, nonimmediate_operand, binary_387_op, }, - { register_operand, general_operand, general_operand, binary_387_op, }, - { register_operand, general_operand, general_operand, binary_387_op, }, - { register_operand, general_operand, general_operand, binary_387_op, }, - { register_operand, general_operand, general_operand, binary_387_op, }, - { register_operand, nonimmediate_operand, nonimmediate_operand, binary_387_op, }, - { register_operand, general_operand, general_operand, binary_387_op, }, - { register_operand, general_operand, general_operand, binary_387_op, }, - { register_operand, general_operand, register_operand, immediate_operand, }, - { register_operand, address_operand, register_operand, immediate_operand, }, - }; - -const int insn_n_alternatives[] = - { - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 2, - 0, - 2, - 0, - 2, - 0, - 2, - 1, - 1, - 1, - 1, - 1, - 2, - 1, - 1, - 1, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1, - 1, - 0, - 2, - 1, - 2, - 2, - 1, - 3, - 2, - 2, - 4, - 2, - 1, - 4, - 1, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 0, - 2, - 0, - 0, - 0, - 0, - 1, - 1, - 0, - 0, - 1, - 1, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1, - 2, - 3, - 2, - 2, - 1, - 0, - 0, - 2, - 2, - 2, - 2, - 0, - 0, - 1, - 2, - 1, - 2, - 1, - 0, - 0, - 1, - 1, - 0, - 0, - 1, - 1, - 1, - 1, - 4, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 0, - 1, - 1, - 2, - 1, - 1, - 0, - 1, - 1, - 1, - 1, - 1, - 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 1, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 1, - 1, - 0, - 1, - 1, - 0, - 1, - 1, - 0, - 1, - 1, - 0, - 1, - 1, - 0, - 1, - 1, - 0, - 1, - 0, - 0, - 0, - 1, - 0, - 1, - 1, - 0, - 1, - 0, - 1, - 2, - 1, - 2, - 1, - 2, - 2, - 1, - 1, - 0, - 1, - }; diff --git a/gnu/usr.bin/cc/common/insn-recog.c b/gnu/usr.bin/cc/common/insn-recog.c deleted file mode 100644 index 821be49a55..0000000000 --- a/gnu/usr.bin/cc/common/insn-recog.c +++ /dev/null @@ -1,6055 +0,0 @@ -/* Generated automatically by the program `genrecog' -from the machine description file `md'. */ - -#include "config.h" -#include "rtl.h" -#include "insn-config.h" -#include "recog.h" -#include "real.h" -#include "output.h" -#include "flags.h" - - -/* `recog' contains a decision tree - that recognizes whether the rtx X0 is a valid instruction. - - recog returns -1 if the rtx is not valid. - If the rtx is valid, recog returns a nonnegative number - which is the insn code number for the pattern that matched. - This is the same as the order in the machine description of - the entry that matched. This number can be used as an index into - entry that matched. This number can be used as an index into various - insn_* tables, such as insn_templates, insn_outfun, and insn_n_operands - (found in insn-output.c). - - The third argument to recog is an optional pointer to an int. - If present, recog will accept a pattern if it matches except for - missing CLOBBER expressions at the end. In that case, the value - pointed to by the optional pointer will be set to the number of - CLOBBERs that need to be added (it should be initialized to zero by - the caller). If it is set nonzero, the caller should allocate a - PARALLEL of the appropriate size, copy the initial entries, and call - add_clobbers (found in insn-emit.c) to fill in the CLOBBERs.*/ - -rtx recog_operand[MAX_RECOG_OPERANDS]; - -rtx *recog_operand_loc[MAX_RECOG_OPERANDS]; - -rtx *recog_dup_loc[MAX_DUP_OPERANDS]; - -char recog_dup_num[MAX_DUP_OPERANDS]; - -#define operands recog_operand - -int -recog_1 (x0, insn, pnum_clobbers) - register rtx x0; - rtx insn; - int *pnum_clobbers; -{ - register rtx *ro = &recog_operand[0]; - register rtx x1, x2, x3, x4, x5, x6; - int tem; - - x1 = XEXP (x0, 1); - switch (GET_MODE (x1)) - { - case SImode: - if (nonimmediate_operand (x1, SImode)) - { - ro[0] = x1; - return 0; - } - break; - case HImode: - if (nonimmediate_operand (x1, HImode)) - { - ro[0] = x1; - return 2; - } - break; - case QImode: - if (nonimmediate_operand (x1, QImode)) - { - ro[0] = x1; - return 4; - } - break; - case SFmode: - if (pnum_clobbers != 0 && register_operand (x1, SFmode)) - { - ro[0] = x1; - if (TARGET_80387 && ! TARGET_IEEE_FP) - { - *pnum_clobbers = 1; - return 6; - } - } - break; - case DFmode: - if (pnum_clobbers != 0 && register_operand (x1, DFmode)) - { - ro[0] = x1; - if (TARGET_80387 && ! TARGET_IEEE_FP) - { - *pnum_clobbers = 1; - return 8; - } - } - } - switch (GET_CODE (x1)) - { - case COMPARE: - goto L30; - case ZERO_EXTRACT: - goto L813; - } - L52: - if (VOIDmode_compare_op (x1, VOIDmode)) - { - ro[2] = x1; - goto L82; - } - L125: - switch (GET_MODE (x1)) - { - case CCFPEQmode: - switch (GET_CODE (x1)) - { - case COMPARE: - goto L126; - } - break; - case SImode: - switch (GET_CODE (x1)) - { - case AND: - goto L187; - } - break; - case HImode: - switch (GET_CODE (x1)) - { - case AND: - goto L192; - } - break; - case QImode: - if (GET_CODE (x1) == AND && 1) - goto L197; - } - goto ret0; - - L30: - x2 = XEXP (x1, 0); - switch (GET_MODE (x2)) - { - case SImode: - if (nonimmediate_operand (x2, SImode)) - { - ro[0] = x2; - goto L31; - } - break; - case HImode: - if (nonimmediate_operand (x2, HImode)) - { - ro[0] = x2; - goto L36; - } - break; - case QImode: - if (nonimmediate_operand (x2, QImode)) - { - ro[0] = x2; - goto L41; - } - } - goto L52; - - L31: - x2 = XEXP (x1, 1); - if (general_operand (x2, SImode)) - { - ro[1] = x2; - if (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) - return 10; - } - goto L52; - - L36: - x2 = XEXP (x1, 1); - if (general_operand (x2, HImode)) - { - ro[1] = x2; - if (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) - return 12; - } - goto L52; - - L41: - x2 = XEXP (x1, 1); - if (general_operand (x2, QImode)) - { - ro[1] = x2; - if (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) - return 14; - } - goto L52; - - L813: - x2 = XEXP (x1, 0); - switch (GET_MODE (x2)) - { - case SImode: - if (register_operand (x2, SImode)) - { - ro[0] = x2; - goto L814; - } - break; - case QImode: - if (general_operand (x2, QImode)) - { - ro[0] = x2; - goto L826; - } - } - goto L52; - - L814: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) != CONST_INT) - { - goto L52; - } - if (XWINT (x2, 0) == 1 && 1) - goto L815; - L820: - ro[1] = x2; - goto L821; - - L815: - x2 = XEXP (x1, 2); - if (general_operand (x2, SImode)) - { - ro[1] = x2; - if (GET_CODE (operands[1]) != CONST_INT) - return 167; - } - x2 = XEXP (x1, 1); - goto L820; - - L821: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == CONST_INT && 1) - { - ro[2] = x2; - return 168; - } - goto L52; - - L826: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && 1) - { - ro[1] = x2; - goto L827; - } - goto L52; - - L827: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == CONST_INT && 1) - { - ro[2] = x2; - if (GET_CODE (operands[0]) != MEM || ! MEM_VOLATILE_P (operands[0])) - return 169; - } - goto L52; - - L82: - x2 = XEXP (x1, 0); - switch (GET_MODE (x2)) - { - case DFmode: - switch (GET_CODE (x2)) - { - case FLOAT: - goto L83; - case FLOAT_EXTEND: - goto L113; - case SUBREG: - case REG: - case MEM: - if (nonimmediate_operand (x2, DFmode)) - { - ro[0] = x2; - goto L54; - } - } - L67: - if (register_operand (x2, DFmode)) - { - ro[0] = x2; - goto L68; - } - break; - case SFmode: - if (GET_CODE (x2) == FLOAT && 1) - goto L169; - if (nonimmediate_operand (x2, SFmode)) - { - ro[0] = x2; - goto L140; - } - L153: - if (register_operand (x2, SFmode)) - { - ro[0] = x2; - goto L154; - } - } - goto L125; - - L83: - x3 = XEXP (x2, 0); - if (nonimmediate_operand (x3, SImode)) - { - ro[0] = x3; - goto L84; - } - goto L125; - - L84: - x2 = XEXP (x1, 1); - if (pnum_clobbers != 0 && register_operand (x2, DFmode)) - { - ro[1] = x2; - if (TARGET_80387) - { - *pnum_clobbers = 1; - return 18; - } - } - goto L125; - - L113: - x3 = XEXP (x2, 0); - if (nonimmediate_operand (x3, SFmode)) - { - ro[0] = x3; - goto L114; - } - goto L125; - - L114: - x2 = XEXP (x1, 1); - if (pnum_clobbers != 0 && register_operand (x2, DFmode)) - { - ro[1] = x2; - if (TARGET_80387) - { - *pnum_clobbers = 1; - return 20; - } - } - goto L125; - - L54: - x2 = XEXP (x1, 1); - if (pnum_clobbers != 0 && nonimmediate_operand (x2, DFmode)) - { - ro[1] = x2; - if (TARGET_80387 - && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)) - { - *pnum_clobbers = 1; - return 16; - } - } - x2 = XEXP (x1, 0); - goto L67; - - L68: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) != DFmode) - { - goto L125; - } - switch (GET_CODE (x2)) - { - case FLOAT: - goto L69; - case FLOAT_EXTEND: - goto L99; - } - goto L125; - - L69: - x3 = XEXP (x2, 0); - if (pnum_clobbers != 0 && nonimmediate_operand (x3, SImode)) - { - ro[1] = x3; - if (TARGET_80387) - { - *pnum_clobbers = 1; - return 17; - } - } - goto L125; - - L99: - x3 = XEXP (x2, 0); - if (pnum_clobbers != 0 && nonimmediate_operand (x3, SFmode)) - { - ro[1] = x3; - if (TARGET_80387) - { - *pnum_clobbers = 1; - return 19; - } - } - goto L125; - - L169: - x3 = XEXP (x2, 0); - if (nonimmediate_operand (x3, SImode)) - { - ro[0] = x3; - goto L170; - } - goto L125; - - L170: - x2 = XEXP (x1, 1); - if (pnum_clobbers != 0 && register_operand (x2, SFmode)) - { - ro[1] = x2; - if (TARGET_80387) - { - *pnum_clobbers = 1; - return 24; - } - } - goto L125; - - L140: - x2 = XEXP (x1, 1); - if (pnum_clobbers != 0 && nonimmediate_operand (x2, SFmode)) - { - ro[1] = x2; - if (TARGET_80387 - && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)) - { - *pnum_clobbers = 1; - return 22; - } - } - x2 = XEXP (x1, 0); - goto L153; - - L154: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == SFmode && GET_CODE (x2) == FLOAT && 1) - goto L155; - goto L125; - - L155: - x3 = XEXP (x2, 0); - if (pnum_clobbers != 0 && nonimmediate_operand (x3, SImode)) - { - ro[1] = x3; - if (TARGET_80387) - { - *pnum_clobbers = 1; - return 23; - } - } - goto L125; - - L126: - x2 = XEXP (x1, 0); - switch (GET_MODE (x2)) - { - case DFmode: - if (register_operand (x2, DFmode)) - { - ro[0] = x2; - goto L127; - } - break; - case SFmode: - if (register_operand (x2, SFmode)) - { - ro[0] = x2; - goto L183; - } - } - goto ret0; - - L127: - x2 = XEXP (x1, 1); - if (pnum_clobbers != 0 && register_operand (x2, DFmode)) - { - ro[1] = x2; - if (TARGET_80387) - { - *pnum_clobbers = 1; - return 21; - } - } - goto ret0; - - L183: - x2 = XEXP (x1, 1); - if (pnum_clobbers != 0 && register_operand (x2, SFmode)) - { - ro[1] = x2; - if (TARGET_80387) - { - *pnum_clobbers = 1; - return 25; - } - } - goto ret0; - - L187: - x2 = XEXP (x1, 0); - if (general_operand (x2, SImode)) - { - ro[0] = x2; - goto L188; - } - goto ret0; - - L188: - x2 = XEXP (x1, 1); - if (general_operand (x2, SImode)) - { - ro[1] = x2; - return 32; - } - goto ret0; - - L192: - x2 = XEXP (x1, 0); - if (general_operand (x2, HImode)) - { - ro[0] = x2; - goto L193; - } - goto ret0; - - L193: - x2 = XEXP (x1, 1); - if (general_operand (x2, HImode)) - { - ro[1] = x2; - return 33; - } - goto ret0; - - L197: - x2 = XEXP (x1, 0); - if (general_operand (x2, QImode)) - { - ro[0] = x2; - goto L198; - } - goto ret0; - - L198: - x2 = XEXP (x1, 1); - if (general_operand (x2, QImode)) - { - ro[1] = x2; - return 34; - } - goto ret0; - ret0: return -1; -} - -int -recog_2 (x0, insn, pnum_clobbers) - register rtx x0; - rtx insn; - int *pnum_clobbers; -{ - register rtx *ro = &recog_operand[0]; - register rtx x1, x2, x3, x4, x5, x6; - int tem; - - x1 = XEXP (x0, 1); - x2 = XEXP (x1, 0); - switch (GET_CODE (x2)) - { - case EQ: - goto L882; - case NE: - goto L891; - case GT: - goto L900; - case GTU: - goto L909; - case LT: - goto L918; - case LTU: - goto L927; - case GE: - goto L936; - case GEU: - goto L945; - case LE: - goto L954; - case LEU: - goto L963; - } - goto ret0; - - L882: - x3 = XEXP (x2, 0); - if (GET_CODE (x3) == CC0 && 1) - goto L883; - goto ret0; - - L883: - x3 = XEXP (x2, 1); - if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) - goto L884; - goto ret0; - - L884: - x2 = XEXP (x1, 1); - switch (GET_CODE (x2)) - { - case LABEL_REF: - goto L885; - case PC: - goto L975; - } - goto ret0; - - L885: - x3 = XEXP (x2, 0); - ro[0] = x3; - goto L886; - - L886: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == PC && 1) - return 191; - goto ret0; - - L975: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == LABEL_REF && 1) - goto L976; - goto ret0; - - L976: - x3 = XEXP (x2, 0); - ro[0] = x3; - return 210; - - L891: - x3 = XEXP (x2, 0); - if (GET_CODE (x3) == CC0 && 1) - goto L892; - goto ret0; - - L892: - x3 = XEXP (x2, 1); - if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) - goto L893; - goto ret0; - - L893: - x2 = XEXP (x1, 1); - switch (GET_CODE (x2)) - { - case LABEL_REF: - goto L894; - case PC: - goto L984; - } - goto ret0; - - L894: - x3 = XEXP (x2, 0); - ro[0] = x3; - goto L895; - - L895: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == PC && 1) - return 193; - goto ret0; - - L984: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == LABEL_REF && 1) - goto L985; - goto ret0; - - L985: - x3 = XEXP (x2, 0); - ro[0] = x3; - return 211; - - L900: - x3 = XEXP (x2, 0); - if (GET_CODE (x3) == CC0 && 1) - goto L901; - goto ret0; - - L901: - x3 = XEXP (x2, 1); - if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) - goto L902; - goto ret0; - - L902: - x2 = XEXP (x1, 1); - switch (GET_CODE (x2)) - { - case LABEL_REF: - goto L903; - case PC: - goto L993; - } - goto ret0; - - L903: - x3 = XEXP (x2, 0); - ro[0] = x3; - goto L904; - - L904: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == PC && 1) - return 195; - goto ret0; - - L993: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == LABEL_REF && 1) - goto L994; - goto ret0; - - L994: - x3 = XEXP (x2, 0); - ro[0] = x3; - return 212; - - L909: - x3 = XEXP (x2, 0); - if (GET_CODE (x3) == CC0 && 1) - goto L910; - goto ret0; - - L910: - x3 = XEXP (x2, 1); - if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) - goto L911; - goto ret0; - - L911: - x2 = XEXP (x1, 1); - switch (GET_CODE (x2)) - { - case LABEL_REF: - goto L912; - case PC: - goto L1002; - } - goto ret0; - - L912: - x3 = XEXP (x2, 0); - ro[0] = x3; - goto L913; - - L913: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == PC && 1) - return 197; - goto ret0; - - L1002: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == LABEL_REF && 1) - goto L1003; - goto ret0; - - L1003: - x3 = XEXP (x2, 0); - ro[0] = x3; - return 213; - - L918: - x3 = XEXP (x2, 0); - if (GET_CODE (x3) == CC0 && 1) - goto L919; - goto ret0; - - L919: - x3 = XEXP (x2, 1); - if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) - goto L920; - goto ret0; - - L920: - x2 = XEXP (x1, 1); - switch (GET_CODE (x2)) - { - case LABEL_REF: - goto L921; - case PC: - goto L1011; - } - goto ret0; - - L921: - x3 = XEXP (x2, 0); - ro[0] = x3; - goto L922; - - L922: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == PC && 1) - return 199; - goto ret0; - - L1011: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == LABEL_REF && 1) - goto L1012; - goto ret0; - - L1012: - x3 = XEXP (x2, 0); - ro[0] = x3; - return 214; - - L927: - x3 = XEXP (x2, 0); - if (GET_CODE (x3) == CC0 && 1) - goto L928; - goto ret0; - - L928: - x3 = XEXP (x2, 1); - if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) - goto L929; - goto ret0; - - L929: - x2 = XEXP (x1, 1); - switch (GET_CODE (x2)) - { - case LABEL_REF: - goto L930; - case PC: - goto L1020; - } - goto ret0; - - L930: - x3 = XEXP (x2, 0); - ro[0] = x3; - goto L931; - - L931: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == PC && 1) - return 201; - goto ret0; - - L1020: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == LABEL_REF && 1) - goto L1021; - goto ret0; - - L1021: - x3 = XEXP (x2, 0); - ro[0] = x3; - return 215; - - L936: - x3 = XEXP (x2, 0); - if (GET_CODE (x3) == CC0 && 1) - goto L937; - goto ret0; - - L937: - x3 = XEXP (x2, 1); - if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) - goto L938; - goto ret0; - - L938: - x2 = XEXP (x1, 1); - switch (GET_CODE (x2)) - { - case LABEL_REF: - goto L939; - case PC: - goto L1029; - } - goto ret0; - - L939: - x3 = XEXP (x2, 0); - ro[0] = x3; - goto L940; - - L940: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == PC && 1) - return 203; - goto ret0; - - L1029: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == LABEL_REF && 1) - goto L1030; - goto ret0; - - L1030: - x3 = XEXP (x2, 0); - ro[0] = x3; - return 216; - - L945: - x3 = XEXP (x2, 0); - if (GET_CODE (x3) == CC0 && 1) - goto L946; - goto ret0; - - L946: - x3 = XEXP (x2, 1); - if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) - goto L947; - goto ret0; - - L947: - x2 = XEXP (x1, 1); - switch (GET_CODE (x2)) - { - case LABEL_REF: - goto L948; - case PC: - goto L1038; - } - goto ret0; - - L948: - x3 = XEXP (x2, 0); - ro[0] = x3; - goto L949; - - L949: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == PC && 1) - return 205; - goto ret0; - - L1038: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == LABEL_REF && 1) - goto L1039; - goto ret0; - - L1039: - x3 = XEXP (x2, 0); - ro[0] = x3; - return 217; - - L954: - x3 = XEXP (x2, 0); - if (GET_CODE (x3) == CC0 && 1) - goto L955; - goto ret0; - - L955: - x3 = XEXP (x2, 1); - if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) - goto L956; - goto ret0; - - L956: - x2 = XEXP (x1, 1); - switch (GET_CODE (x2)) - { - case LABEL_REF: - goto L957; - case PC: - goto L1047; - } - goto ret0; - - L957: - x3 = XEXP (x2, 0); - ro[0] = x3; - goto L958; - - L958: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == PC && 1) - return 207; - goto ret0; - - L1047: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == LABEL_REF && 1) - goto L1048; - goto ret0; - - L1048: - x3 = XEXP (x2, 0); - ro[0] = x3; - return 218; - - L963: - x3 = XEXP (x2, 0); - if (GET_CODE (x3) == CC0 && 1) - goto L964; - goto ret0; - - L964: - x3 = XEXP (x2, 1); - if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) - goto L965; - goto ret0; - - L965: - x2 = XEXP (x1, 1); - switch (GET_CODE (x2)) - { - case LABEL_REF: - goto L966; - case PC: - goto L1056; - } - goto ret0; - - L966: - x3 = XEXP (x2, 0); - ro[0] = x3; - goto L967; - - L967: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == PC && 1) - return 209; - goto ret0; - - L1056: - x2 = XEXP (x1, 2); - if (GET_CODE (x2) == LABEL_REF && 1) - goto L1057; - goto ret0; - - L1057: - x3 = XEXP (x2, 0); - ro[0] = x3; - return 219; - ret0: return -1; -} - -int -recog_3 (x0, insn, pnum_clobbers) - register rtx x0; - rtx insn; - int *pnum_clobbers; -{ - register rtx *ro = &recog_operand[0]; - register rtx x1, x2, x3, x4, x5, x6; - int tem; - - x1 = XEXP (x0, 0); - switch (GET_MODE (x1)) - { - case SImode: - switch (GET_CODE (x1)) - { - case MEM: - if (push_operand (x1, SImode)) - { - ro[0] = x1; - goto L201; - } - break; - case ZERO_EXTRACT: - goto L792; - } - L208: - if (general_operand (x1, SImode)) - { - ro[0] = x1; - goto L257; - } - L431: - if (register_operand (x1, SImode)) - { - ro[0] = x1; - goto L432; - } - L439: - if (general_operand (x1, SImode)) - { - ro[0] = x1; - goto L440; - } - break; - case HImode: - if (GET_CODE (x1) == MEM && push_operand (x1, HImode)) - { - ro[0] = x1; - goto L212; - } - L214: - if (general_operand (x1, HImode)) - { - ro[0] = x1; - goto L261; - } - break; - case QImode: - if (GET_CODE (x1) == MEM && push_operand (x1, QImode)) - { - ro[0] = x1; - goto L222; - } - L224: - if (general_operand (x1, QImode)) - { - ro[0] = x1; - goto L427; - } - L829: - if (register_operand (x1, QImode)) - { - ro[0] = x1; - goto L830; - } - break; - case SFmode: - if (GET_CODE (x1) == MEM && push_operand (x1, SFmode)) - { - ro[0] = x1; - goto L232; - } - L234: - if (general_operand (x1, SFmode)) - { - ro[0] = x1; - goto L235; - } - L399: - if (register_operand (x1, SFmode)) - { - ro[0] = x1; - goto L400; - } - break; - case DFmode: - if (GET_CODE (x1) == MEM && push_operand (x1, DFmode)) - { - ro[0] = x1; - goto L238; - } - L247: - if (general_operand (x1, DFmode)) - { - ro[0] = x1; - goto L289; - } - L395: - if (register_operand (x1, DFmode)) - { - ro[0] = x1; - goto L396; - } - break; - case DImode: - if (GET_CODE (x1) == MEM && push_operand (x1, DImode)) - { - ro[0] = x1; - goto L251; - } - L253: - if (general_operand (x1, DImode)) - { - ro[0] = x1; - goto L412; - } - L268: - if (register_operand (x1, DImode)) - { - ro[0] = x1; - goto L269; - } - } - switch (GET_CODE (x1)) - { - case CC0: - goto L2; - case STRICT_LOW_PART: - goto L218; - case PC: - goto L1081; - } - L1147: - ro[0] = x1; - goto L1148; - L1227: - switch (GET_MODE (x1)) - { - case SImode: - if (general_operand (x1, SImode)) - { - ro[0] = x1; - goto L1228; - } - break; - case HImode: - if (general_operand (x1, HImode)) - { - ro[0] = x1; - goto L1234; - } - break; - case DFmode: - if (register_operand (x1, DFmode)) - { - ro[0] = x1; - goto L1240; - } - break; - case SFmode: - if (register_operand (x1, SFmode)) - { - ro[0] = x1; - goto L1269; - } - } - goto ret0; - - L201: - x1 = XEXP (x0, 1); - if (general_operand (x1, SImode)) - goto L205; - x1 = XEXP (x0, 0); - goto L208; - - L205: - ro[1] = x1; - if (! TARGET_486) - return 35; - L206: - ro[1] = x1; - if (TARGET_486) - return 36; - x1 = XEXP (x0, 0); - goto L208; - - L792: - x2 = XEXP (x1, 0); - if (GET_MODE (x2) == SImode && general_operand (x2, SImode)) - { - ro[0] = x2; - goto L793; - } - goto L1147; - - L793: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 1 && 1) - goto L794; - goto L1147; - - L794: - x2 = XEXP (x1, 2); - if (general_operand (x2, SImode)) - { - ro[2] = x2; - goto L795; - } - goto L1147; - - L795: - x1 = XEXP (x0, 1); - if (GET_CODE (x1) == CONST_INT && 1) - { - ro[3] = x1; - if (! TARGET_486 && GET_CODE (operands[2]) != CONST_INT) - return 164; - } - x1 = XEXP (x0, 0); - goto L1147; - - L257: - x1 = XEXP (x0, 1); - switch (GET_MODE (x1)) - { - case SImode: - switch (GET_CODE (x1)) - { - case ZERO_EXTEND: - goto L258; - case SIGN_EXTEND: - goto L278; - case PLUS: - goto L418; - } - } - if (general_operand (x1, SImode)) - { - ro[1] = x1; - return 38; - } - x1 = XEXP (x0, 0); - goto L431; - - L258: - x2 = XEXP (x1, 0); - switch (GET_MODE (x2)) - { - case HImode: - if (nonimmediate_operand (x2, HImode)) - { - ro[1] = x2; - return 52; - } - break; - case QImode: - if (nonimmediate_operand (x2, QImode)) - { - ro[1] = x2; - return 54; - } - } - x1 = XEXP (x0, 0); - goto L431; - - L278: - x2 = XEXP (x1, 0); - switch (GET_MODE (x2)) - { - case HImode: - if (nonimmediate_operand (x2, HImode)) - { - ro[1] = x2; - return 57; - } - break; - case QImode: - if (nonimmediate_operand (x2, QImode)) - { - ro[1] = x2; - return 59; - } - } - x1 = XEXP (x0, 0); - goto L431; - - L418: - x2 = XEXP (x1, 0); - if (general_operand (x2, SImode)) - { - ro[1] = x2; - goto L419; - } - x1 = XEXP (x0, 0); - goto L431; - - L419: - x2 = XEXP (x1, 1); - if (general_operand (x2, SImode)) - { - ro[2] = x2; - return 82; - } - x1 = XEXP (x0, 0); - goto L431; - - L432: - x1 = XEXP (x0, 1); - if (address_operand (x1, QImode)) - { - ro[1] = x1; - return 85; - } - x1 = XEXP (x0, 0); - goto L439; - - L440: - x1 = XEXP (x0, 1); - if (GET_MODE (x1) != SImode) - { - x1 = XEXP (x0, 0); - goto L1147; - } - switch (GET_CODE (x1)) - { - case MINUS: - goto L441; - case MULT: - goto L468; - case AND: - goto L541; - case IOR: - goto L556; - case XOR: - goto L799; - case NEG: - goto L590; - case NOT: - goto L667; - case ASHIFT: - goto L692; - case ASHIFTRT: - goto L720; - case LSHIFTRT: - goto L748; - case ROTATE: - goto L763; - case ROTATERT: - goto L778; - } - x1 = XEXP (x0, 0); - goto L1147; - - L441: - x2 = XEXP (x1, 0); - if (general_operand (x2, SImode)) - { - ro[1] = x2; - goto L442; - } - x1 = XEXP (x0, 0); - goto L1147; - - L442: - x2 = XEXP (x1, 1); - if (general_operand (x2, SImode)) - { - ro[2] = x2; - return 89; - } - x1 = XEXP (x0, 0); - goto L1147; - - L468: - x2 = XEXP (x1, 0); - if (general_operand (x2, SImode)) - { - ro[1] = x2; - goto L469; - } - x1 = XEXP (x0, 0); - goto L1147; - - L469: - x2 = XEXP (x1, 1); - if (general_operand (x2, SImode)) - goto L475; - x1 = XEXP (x0, 0); - goto L1147; - - L475: - ro[2] = x2; - if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x80) - return 96; - L476: - ro[2] = x2; - return 97; - - L541: - x2 = XEXP (x1, 0); - if (general_operand (x2, SImode)) - { - ro[1] = x2; - goto L542; - } - x1 = XEXP (x0, 0); - goto L1147; - - L542: - x2 = XEXP (x1, 1); - if (general_operand (x2, SImode)) - { - ro[2] = x2; - return 109; - } - x1 = XEXP (x0, 0); - goto L1147; - - L556: - x2 = XEXP (x1, 0); - if (general_operand (x2, SImode)) - { - ro[1] = x2; - goto L557; - } - x1 = XEXP (x0, 0); - goto L1147; - - L557: - x2 = XEXP (x1, 1); - if (general_operand (x2, SImode)) - { - ro[2] = x2; - return 112; - } - x1 = XEXP (x0, 0); - goto L1147; - - L799: - x2 = XEXP (x1, 0); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == ASHIFT && 1) - goto L800; - if (general_operand (x2, SImode)) - { - ro[1] = x2; - goto L807; - } - x1 = XEXP (x0, 0); - goto L1147; - - L800: - x3 = XEXP (x2, 0); - if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 1 && 1) - goto L801; - x1 = XEXP (x0, 0); - goto L1147; - - L801: - x3 = XEXP (x2, 1); - if (general_operand (x3, SImode)) - { - ro[1] = x3; - goto L802; - } - x1 = XEXP (x0, 0); - goto L1147; - - L802: - x2 = XEXP (x1, 1); - if (general_operand (x2, SImode)) - { - ro[2] = x2; - if (! TARGET_486 && GET_CODE (operands[1]) != CONST_INT) - return 165; - } - x1 = XEXP (x0, 0); - goto L1147; - - L807: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == ASHIFT && 1) - goto L808; - if (general_operand (x2, SImode)) - { - ro[2] = x2; - return 115; - } - x1 = XEXP (x0, 0); - goto L1147; - - L808: - x3 = XEXP (x2, 0); - if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 1 && 1) - goto L809; - x1 = XEXP (x0, 0); - goto L1147; - - L809: - x3 = XEXP (x2, 1); - if (general_operand (x3, SImode)) - { - ro[2] = x3; - if (! TARGET_486 && GET_CODE (operands[2]) != CONST_INT) - return 166; - } - x1 = XEXP (x0, 0); - goto L1147; - - L590: - x2 = XEXP (x1, 0); - if (general_operand (x2, SImode)) - { - ro[1] = x2; - return 119; - } - x1 = XEXP (x0, 0); - goto L1147; - - L667: - x2 = XEXP (x1, 0); - if (general_operand (x2, SImode)) - { - ro[1] = x2; - return 137; - } - x1 = XEXP (x0, 0); - goto L1147; - - L692: - x2 = XEXP (x1, 0); - if (general_operand (x2, SImode)) - { - ro[1] = x2; - goto L693; - } - x1 = XEXP (x0, 0); - goto L1147; - - L693: - x2 = XEXP (x1, 1); - if (nonmemory_operand (x2, SImode)) - { - ro[2] = x2; - return 143; - } - x1 = XEXP (x0, 0); - goto L1147; - - L720: - x2 = XEXP (x1, 0); - if (general_operand (x2, SImode)) - { - ro[1] = x2; - goto L721; - } - x1 = XEXP (x0, 0); - goto L1147; - - L721: - x2 = XEXP (x1, 1); - if (nonmemory_operand (x2, SImode)) - { - ro[2] = x2; - return 149; - } - x1 = XEXP (x0, 0); - goto L1147; - - L748: - x2 = XEXP (x1, 0); - if (general_operand (x2, SImode)) - { - ro[1] = x2; - goto L749; - } - x1 = XEXP (x0, 0); - goto L1147; - - L749: - x2 = XEXP (x1, 1); - if (nonmemory_operand (x2, SImode)) - { - ro[2] = x2; - return 155; - } - x1 = XEXP (x0, 0); - goto L1147; - - L763: - x2 = XEXP (x1, 0); - if (general_operand (x2, SImode)) - { - ro[1] = x2; - goto L764; - } - x1 = XEXP (x0, 0); - goto L1147; - - L764: - x2 = XEXP (x1, 1); - if (nonmemory_operand (x2, SImode)) - { - ro[2] = x2; - return 158; - } - x1 = XEXP (x0, 0); - goto L1147; - - L778: - x2 = XEXP (x1, 0); - if (general_operand (x2, SImode)) - { - ro[1] = x2; - goto L779; - } - x1 = XEXP (x0, 0); - goto L1147; - - L779: - x2 = XEXP (x1, 1); - if (nonmemory_operand (x2, SImode)) - { - ro[2] = x2; - return 161; - } - x1 = XEXP (x0, 0); - goto L1147; - - L212: - x1 = XEXP (x0, 1); - if (general_operand (x1, HImode)) - { - ro[1] = x1; - return 39; - } - x1 = XEXP (x0, 0); - goto L214; - - L261: - x1 = XEXP (x0, 1); - switch (GET_MODE (x1)) - { - case HImode: - switch (GET_CODE (x1)) - { - case ZERO_EXTEND: - goto L262; - case SIGN_EXTEND: - goto L282; - case PLUS: - goto L423; - case MINUS: - goto L446; - case AND: - goto L546; - case IOR: - goto L561; - case XOR: - goto L576; - case NEG: - goto L594; - case NOT: - goto L671; - case ASHIFT: - goto L697; - case ASHIFTRT: - goto L725; - case LSHIFTRT: - goto L753; - case ROTATE: - goto L768; - case ROTATERT: - goto L783; - } - break; - case SImode: - if (GET_CODE (x1) == MULT && 1) - goto L480; - } - if (general_operand (x1, HImode)) - { - ro[1] = x1; - return 40; - } - x1 = XEXP (x0, 0); - goto L1147; - - L262: - x2 = XEXP (x1, 0); - if (nonimmediate_operand (x2, QImode)) - { - ro[1] = x2; - return 53; - } - x1 = XEXP (x0, 0); - goto L1147; - - L282: - x2 = XEXP (x1, 0); - if (nonimmediate_operand (x2, QImode)) - { - ro[1] = x2; - return 58; - } - x1 = XEXP (x0, 0); - goto L1147; - - L423: - x2 = XEXP (x1, 0); - if (general_operand (x2, HImode)) - { - ro[1] = x2; - goto L424; - } - x1 = XEXP (x0, 0); - goto L1147; - - L424: - x2 = XEXP (x1, 1); - if (general_operand (x2, HImode)) - { - ro[2] = x2; - return 83; - } - x1 = XEXP (x0, 0); - goto L1147; - - L446: - x2 = XEXP (x1, 0); - if (general_operand (x2, HImode)) - { - ro[1] = x2; - goto L447; - } - x1 = XEXP (x0, 0); - goto L1147; - - L447: - x2 = XEXP (x1, 1); - if (general_operand (x2, HImode)) - { - ro[2] = x2; - return 90; - } - x1 = XEXP (x0, 0); - goto L1147; - - L546: - x2 = XEXP (x1, 0); - if (general_operand (x2, HImode)) - { - ro[1] = x2; - goto L547; - } - x1 = XEXP (x0, 0); - goto L1147; - - L547: - x2 = XEXP (x1, 1); - if (general_operand (x2, HImode)) - { - ro[2] = x2; - return 110; - } - x1 = XEXP (x0, 0); - goto L1147; - - L561: - x2 = XEXP (x1, 0); - if (general_operand (x2, HImode)) - { - ro[1] = x2; - goto L562; - } - x1 = XEXP (x0, 0); - goto L1147; - - L562: - x2 = XEXP (x1, 1); - if (general_operand (x2, HImode)) - { - ro[2] = x2; - return 113; - } - x1 = XEXP (x0, 0); - goto L1147; - - L576: - x2 = XEXP (x1, 0); - if (general_operand (x2, HImode)) - { - ro[1] = x2; - goto L577; - } - x1 = XEXP (x0, 0); - goto L1147; - - L577: - x2 = XEXP (x1, 1); - if (general_operand (x2, HImode)) - { - ro[2] = x2; - return 116; - } - x1 = XEXP (x0, 0); - goto L1147; - - L594: - x2 = XEXP (x1, 0); - if (general_operand (x2, HImode)) - { - ro[1] = x2; - return 120; - } - x1 = XEXP (x0, 0); - goto L1147; - - L671: - x2 = XEXP (x1, 0); - if (general_operand (x2, HImode)) - { - ro[1] = x2; - return 138; - } - x1 = XEXP (x0, 0); - goto L1147; - - L697: - x2 = XEXP (x1, 0); - if (general_operand (x2, HImode)) - { - ro[1] = x2; - goto L698; - } - x1 = XEXP (x0, 0); - goto L1147; - - L698: - x2 = XEXP (x1, 1); - if (nonmemory_operand (x2, HImode)) - { - ro[2] = x2; - return 144; - } - x1 = XEXP (x0, 0); - goto L1147; - - L725: - x2 = XEXP (x1, 0); - if (general_operand (x2, HImode)) - { - ro[1] = x2; - goto L726; - } - x1 = XEXP (x0, 0); - goto L1147; - - L726: - x2 = XEXP (x1, 1); - if (nonmemory_operand (x2, HImode)) - { - ro[2] = x2; - return 150; - } - x1 = XEXP (x0, 0); - goto L1147; - - L753: - x2 = XEXP (x1, 0); - if (general_operand (x2, HImode)) - { - ro[1] = x2; - goto L754; - } - x1 = XEXP (x0, 0); - goto L1147; - - L754: - x2 = XEXP (x1, 1); - if (nonmemory_operand (x2, HImode)) - { - ro[2] = x2; - return 156; - } - x1 = XEXP (x0, 0); - goto L1147; - - L768: - x2 = XEXP (x1, 0); - if (general_operand (x2, HImode)) - { - ro[1] = x2; - goto L769; - } - x1 = XEXP (x0, 0); - goto L1147; - - L769: - x2 = XEXP (x1, 1); - if (nonmemory_operand (x2, HImode)) - { - ro[2] = x2; - return 159; - } - x1 = XEXP (x0, 0); - goto L1147; - - L783: - x2 = XEXP (x1, 0); - if (general_operand (x2, HImode)) - { - ro[1] = x2; - goto L784; - } - x1 = XEXP (x0, 0); - goto L1147; - - L784: - x2 = XEXP (x1, 1); - if (nonmemory_operand (x2, HImode)) - { - ro[2] = x2; - return 162; - } - x1 = XEXP (x0, 0); - goto L1147; - - L480: - x2 = XEXP (x1, 0); - if (GET_MODE (x2) == HImode && GET_CODE (x2) == ZERO_EXTEND && 1) - goto L481; - if (general_operand (x2, HImode)) - { - ro[1] = x2; - goto L457; - } - x1 = XEXP (x0, 0); - goto L1147; - - L481: - x3 = XEXP (x2, 0); - if (nonimmediate_operand (x3, QImode)) - { - ro[1] = x3; - goto L482; - } - x1 = XEXP (x0, 0); - goto L1147; - - L482: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == HImode && GET_CODE (x2) == ZERO_EXTEND && 1) - goto L483; - x1 = XEXP (x0, 0); - goto L1147; - - L483: - x3 = XEXP (x2, 0); - if (nonimmediate_operand (x3, QImode)) - { - ro[2] = x3; - return 98; - } - x1 = XEXP (x0, 0); - goto L1147; - - L457: - x2 = XEXP (x1, 1); - if (general_operand (x2, HImode)) - goto L463; - x1 = XEXP (x0, 0); - goto L1147; - - L463: - ro[2] = x2; - if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x80) - return 94; - L464: - ro[2] = x2; - return 95; - - L222: - x1 = XEXP (x0, 1); - if (general_operand (x1, QImode)) - { - ro[1] = x1; - return 42; - } - x1 = XEXP (x0, 0); - goto L224; - - L427: - x1 = XEXP (x0, 1); - switch (GET_MODE (x1)) - { - case QImode: - switch (GET_CODE (x1)) - { - case PLUS: - goto L428; - case MINUS: - goto L451; - case DIV: - goto L487; - case UDIV: - goto L492; - case AND: - goto L551; - case IOR: - goto L566; - case XOR: - goto L581; - case NEG: - goto L598; - case NOT: - goto L675; - case ASHIFT: - goto L702; - case ASHIFTRT: - goto L730; - case LSHIFTRT: - goto L758; - case ROTATE: - goto L773; - case ROTATERT: - goto L788; - } - } - if (general_operand (x1, QImode)) - { - ro[1] = x1; - return 43; - } - x1 = XEXP (x0, 0); - goto L829; - - L428: - x2 = XEXP (x1, 0); - if (general_operand (x2, QImode)) - { - ro[1] = x2; - goto L429; - } - x1 = XEXP (x0, 0); - goto L829; - - L429: - x2 = XEXP (x1, 1); - if (general_operand (x2, QImode)) - { - ro[2] = x2; - return 84; - } - x1 = XEXP (x0, 0); - goto L829; - - L451: - x2 = XEXP (x1, 0); - if (general_operand (x2, QImode)) - { - ro[1] = x2; - goto L452; - } - x1 = XEXP (x0, 0); - goto L829; - - L452: - x2 = XEXP (x1, 1); - if (general_operand (x2, QImode)) - { - ro[2] = x2; - return 91; - } - x1 = XEXP (x0, 0); - goto L829; - - L487: - x2 = XEXP (x1, 0); - if (general_operand (x2, HImode)) - { - ro[1] = x2; - goto L488; - } - x1 = XEXP (x0, 0); - goto L829; - - L488: - x2 = XEXP (x1, 1); - if (general_operand (x2, QImode)) - { - ro[2] = x2; - return 101; - } - x1 = XEXP (x0, 0); - goto L829; - - L492: - x2 = XEXP (x1, 0); - if (general_operand (x2, HImode)) - { - ro[1] = x2; - goto L493; - } - x1 = XEXP (x0, 0); - goto L829; - - L493: - x2 = XEXP (x1, 1); - if (general_operand (x2, QImode)) - { - ro[2] = x2; - return 102; - } - x1 = XEXP (x0, 0); - goto L829; - - L551: - x2 = XEXP (x1, 0); - if (general_operand (x2, QImode)) - { - ro[1] = x2; - goto L552; - } - x1 = XEXP (x0, 0); - goto L829; - - L552: - x2 = XEXP (x1, 1); - if (general_operand (x2, QImode)) - { - ro[2] = x2; - return 111; - } - x1 = XEXP (x0, 0); - goto L829; - - L566: - x2 = XEXP (x1, 0); - if (general_operand (x2, QImode)) - { - ro[1] = x2; - goto L567; - } - x1 = XEXP (x0, 0); - goto L829; - - L567: - x2 = XEXP (x1, 1); - if (general_operand (x2, QImode)) - { - ro[2] = x2; - return 114; - } - x1 = XEXP (x0, 0); - goto L829; - - L581: - x2 = XEXP (x1, 0); - if (general_operand (x2, QImode)) - { - ro[1] = x2; - goto L582; - } - x1 = XEXP (x0, 0); - goto L829; - - L582: - x2 = XEXP (x1, 1); - if (general_operand (x2, QImode)) - { - ro[2] = x2; - return 117; - } - x1 = XEXP (x0, 0); - goto L829; - - L598: - x2 = XEXP (x1, 0); - if (general_operand (x2, QImode)) - { - ro[1] = x2; - return 121; - } - x1 = XEXP (x0, 0); - goto L829; - - L675: - x2 = XEXP (x1, 0); - if (general_operand (x2, QImode)) - { - ro[1] = x2; - return 139; - } - x1 = XEXP (x0, 0); - goto L829; - - L702: - x2 = XEXP (x1, 0); - if (general_operand (x2, QImode)) - { - ro[1] = x2; - goto L703; - } - x1 = XEXP (x0, 0); - goto L829; - - L703: - x2 = XEXP (x1, 1); - if (nonmemory_operand (x2, QImode)) - { - ro[2] = x2; - return 145; - } - x1 = XEXP (x0, 0); - goto L829; - - L730: - x2 = XEXP (x1, 0); - if (general_operand (x2, QImode)) - { - ro[1] = x2; - goto L731; - } - x1 = XEXP (x0, 0); - goto L829; - - L731: - x2 = XEXP (x1, 1); - if (nonmemory_operand (x2, QImode)) - { - ro[2] = x2; - return 151; - } - x1 = XEXP (x0, 0); - goto L829; - - L758: - x2 = XEXP (x1, 0); - if (general_operand (x2, QImode)) - { - ro[1] = x2; - goto L759; - } - x1 = XEXP (x0, 0); - goto L829; - - L759: - x2 = XEXP (x1, 1); - if (nonmemory_operand (x2, QImode)) - { - ro[2] = x2; - return 157; - } - x1 = XEXP (x0, 0); - goto L829; - - L773: - x2 = XEXP (x1, 0); - if (general_operand (x2, QImode)) - { - ro[1] = x2; - goto L774; - } - x1 = XEXP (x0, 0); - goto L829; - - L774: - x2 = XEXP (x1, 1); - if (nonmemory_operand (x2, QImode)) - { - ro[2] = x2; - return 160; - } - x1 = XEXP (x0, 0); - goto L829; - - L788: - x2 = XEXP (x1, 0); - if (general_operand (x2, QImode)) - { - ro[1] = x2; - goto L789; - } - x1 = XEXP (x0, 0); - goto L829; - - L789: - x2 = XEXP (x1, 1); - if (nonmemory_operand (x2, QImode)) - { - ro[2] = x2; - return 163; - } - x1 = XEXP (x0, 0); - goto L829; - - L830: - x1 = XEXP (x0, 1); - if (GET_MODE (x1) != QImode) - { - x1 = XEXP (x0, 0); - goto L1147; - } - switch (GET_CODE (x1)) - { - case EQ: - goto L831; - case NE: - goto L836; - case GT: - goto L841; - case GTU: - goto L846; - case LT: - goto L851; - case LTU: - goto L856; - case GE: - goto L861; - case GEU: - goto L866; - case LE: - goto L871; - case LEU: - goto L876; - } - x1 = XEXP (x0, 0); - goto L1147; - - L831: - x2 = XEXP (x1, 0); - if (GET_CODE (x2) == CC0 && 1) - goto L832; - x1 = XEXP (x0, 0); - goto L1147; - - L832: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) - return 171; - x1 = XEXP (x0, 0); - goto L1147; - - L836: - x2 = XEXP (x1, 0); - if (GET_CODE (x2) == CC0 && 1) - goto L837; - x1 = XEXP (x0, 0); - goto L1147; - - L837: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) - return 173; - x1 = XEXP (x0, 0); - goto L1147; - - L841: - x2 = XEXP (x1, 0); - if (GET_CODE (x2) == CC0 && 1) - goto L842; - x1 = XEXP (x0, 0); - goto L1147; - - L842: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) - return 175; - x1 = XEXP (x0, 0); - goto L1147; - - L846: - x2 = XEXP (x1, 0); - if (GET_CODE (x2) == CC0 && 1) - goto L847; - x1 = XEXP (x0, 0); - goto L1147; - - L847: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) - return 177; - x1 = XEXP (x0, 0); - goto L1147; - - L851: - x2 = XEXP (x1, 0); - if (GET_CODE (x2) == CC0 && 1) - goto L852; - x1 = XEXP (x0, 0); - goto L1147; - - L852: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) - return 179; - x1 = XEXP (x0, 0); - goto L1147; - - L856: - x2 = XEXP (x1, 0); - if (GET_CODE (x2) == CC0 && 1) - goto L857; - x1 = XEXP (x0, 0); - goto L1147; - - L857: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) - return 181; - x1 = XEXP (x0, 0); - goto L1147; - - L861: - x2 = XEXP (x1, 0); - if (GET_CODE (x2) == CC0 && 1) - goto L862; - x1 = XEXP (x0, 0); - goto L1147; - - L862: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) - return 183; - x1 = XEXP (x0, 0); - goto L1147; - - L866: - x2 = XEXP (x1, 0); - if (GET_CODE (x2) == CC0 && 1) - goto L867; - x1 = XEXP (x0, 0); - goto L1147; - - L867: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) - return 185; - x1 = XEXP (x0, 0); - goto L1147; - - L871: - x2 = XEXP (x1, 0); - if (GET_CODE (x2) == CC0 && 1) - goto L872; - x1 = XEXP (x0, 0); - goto L1147; - - L872: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) - return 187; - x1 = XEXP (x0, 0); - goto L1147; - - L876: - x2 = XEXP (x1, 0); - if (GET_CODE (x2) == CC0 && 1) - goto L877; - x1 = XEXP (x0, 0); - goto L1147; - - L877: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) - return 189; - x1 = XEXP (x0, 0); - goto L1147; - - L232: - x1 = XEXP (x0, 1); - if (general_operand (x1, SFmode)) - { - ro[1] = x1; - return 45; - } - x1 = XEXP (x0, 0); - goto L234; - - L235: - x1 = XEXP (x0, 1); - if (general_operand (x1, SFmode)) - { - ro[1] = x1; - return 46; - } - x1 = XEXP (x0, 0); - goto L399; - - L400: - x1 = XEXP (x0, 1); - if (GET_MODE (x1) != SFmode) - { - x1 = XEXP (x0, 0); - goto L1147; - } - switch (GET_CODE (x1)) - { - case FLOAT: - goto L401; - case NEG: - goto L602; - case ABS: - goto L615; - case SQRT: - goto L628; - case UNSPEC: - if (XINT (x1, 1) == 1 && XVECLEN (x1, 0) == 1 && 1) - goto L645; - if (XINT (x1, 1) == 2 && XVECLEN (x1, 0) == 1 && 1) - goto L658; - } - x1 = XEXP (x0, 0); - goto L1147; - - L401: - x2 = XEXP (x1, 0); - switch (GET_MODE (x2)) - { - case DImode: - if (nonimmediate_operand (x2, DImode)) - { - ro[1] = x2; - if (TARGET_80387) - return 78; - } - break; - case SImode: - if (nonimmediate_operand (x2, SImode)) - { - ro[1] = x2; - if (TARGET_80387) - return 80; - } - } - x1 = XEXP (x0, 0); - goto L1147; - - L602: - x2 = XEXP (x1, 0); - if (general_operand (x2, SFmode)) - { - ro[1] = x2; - if (TARGET_80387) - return 122; - } - x1 = XEXP (x0, 0); - goto L1147; - - L615: - x2 = XEXP (x1, 0); - if (general_operand (x2, SFmode)) - { - ro[1] = x2; - if (TARGET_80387) - return 125; - } - x1 = XEXP (x0, 0); - goto L1147; - - L628: - x2 = XEXP (x1, 0); - if (general_operand (x2, SFmode)) - { - ro[1] = x2; - if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) - return 128; - } - x1 = XEXP (x0, 0); - goto L1147; - - L645: - x2 = XVECEXP (x1, 0, 0); - if (register_operand (x2, SFmode)) - { - ro[1] = x2; - if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) - return 132; - } - x1 = XEXP (x0, 0); - goto L1147; - - L658: - x2 = XVECEXP (x1, 0, 0); - if (register_operand (x2, SFmode)) - { - ro[1] = x2; - if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) - return 135; - } - x1 = XEXP (x0, 0); - goto L1147; - - L238: - x1 = XEXP (x0, 1); - if (general_operand (x1, DFmode)) - { - ro[1] = x1; - return 47; - } - x1 = XEXP (x0, 0); - goto L247; - - L289: - x1 = XEXP (x0, 1); - if (GET_MODE (x1) == DFmode && GET_CODE (x1) == FLOAT_EXTEND && 1) - goto L290; - if (general_operand (x1, DFmode)) - { - ro[1] = x1; - return 49; - } - x1 = XEXP (x0, 0); - goto L395; - - L290: - x2 = XEXP (x1, 0); - if (general_operand (x2, SFmode)) - { - ro[1] = x2; - if (TARGET_80387) - return 60; - } - x1 = XEXP (x0, 0); - goto L395; - - L396: - x1 = XEXP (x0, 1); - if (GET_MODE (x1) != DFmode) - { - x1 = XEXP (x0, 0); - goto L1147; - } - switch (GET_CODE (x1)) - { - case FLOAT: - goto L397; - case NEG: - goto L610; - case ABS: - goto L623; - case SQRT: - goto L636; - case UNSPEC: - if (XINT (x1, 1) == 1 && XVECLEN (x1, 0) == 1 && 1) - goto L649; - if (XINT (x1, 1) == 2 && XVECLEN (x1, 0) == 1 && 1) - goto L662; - } - x1 = XEXP (x0, 0); - goto L1147; - - L397: - x2 = XEXP (x1, 0); - switch (GET_MODE (x2)) - { - case DImode: - if (nonimmediate_operand (x2, DImode)) - { - ro[1] = x2; - if (TARGET_80387) - return 77; - } - break; - case SImode: - if (nonimmediate_operand (x2, SImode)) - { - ro[1] = x2; - if (TARGET_80387) - return 79; - } - } - x1 = XEXP (x0, 0); - goto L1147; - - L610: - x2 = XEXP (x1, 0); - if (GET_MODE (x2) == DFmode && GET_CODE (x2) == FLOAT_EXTEND && 1) - goto L611; - if (general_operand (x2, DFmode)) - { - ro[1] = x2; - if (TARGET_80387) - return 123; - } - x1 = XEXP (x0, 0); - goto L1147; - - L611: - x3 = XEXP (x2, 0); - if (general_operand (x3, SFmode)) - { - ro[1] = x3; - if (TARGET_80387) - return 124; - } - x1 = XEXP (x0, 0); - goto L1147; - - L623: - x2 = XEXP (x1, 0); - if (GET_MODE (x2) == DFmode && GET_CODE (x2) == FLOAT_EXTEND && 1) - goto L624; - if (general_operand (x2, DFmode)) - { - ro[1] = x2; - if (TARGET_80387) - return 126; - } - x1 = XEXP (x0, 0); - goto L1147; - - L624: - x3 = XEXP (x2, 0); - if (general_operand (x3, SFmode)) - { - ro[1] = x3; - if (TARGET_80387) - return 127; - } - x1 = XEXP (x0, 0); - goto L1147; - - L636: - x2 = XEXP (x1, 0); - if (GET_MODE (x2) == DFmode && GET_CODE (x2) == FLOAT_EXTEND && 1) - goto L637; - if (general_operand (x2, DFmode)) - { - ro[1] = x2; - if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) - return 129; - } - x1 = XEXP (x0, 0); - goto L1147; - - L637: - x3 = XEXP (x2, 0); - if (general_operand (x3, SFmode)) - { - ro[1] = x3; - if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) - return 130; - } - x1 = XEXP (x0, 0); - goto L1147; - - L649: - x2 = XVECEXP (x1, 0, 0); - if (GET_MODE (x2) != DFmode) - { - x1 = XEXP (x0, 0); - goto L1147; - } - if (GET_CODE (x2) == FLOAT_EXTEND && 1) - goto L650; - if (register_operand (x2, DFmode)) - { - ro[1] = x2; - if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) - return 131; - } - x1 = XEXP (x0, 0); - goto L1147; - - L650: - x3 = XEXP (x2, 0); - if (register_operand (x3, SFmode)) - { - ro[1] = x3; - if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) - return 133; - } - x1 = XEXP (x0, 0); - goto L1147; - - L662: - x2 = XVECEXP (x1, 0, 0); - if (GET_MODE (x2) != DFmode) - { - x1 = XEXP (x0, 0); - goto L1147; - } - if (GET_CODE (x2) == FLOAT_EXTEND && 1) - goto L663; - if (register_operand (x2, DFmode)) - { - ro[1] = x2; - if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) - return 134; - } - x1 = XEXP (x0, 0); - goto L1147; - - L663: - x3 = XEXP (x2, 0); - if (register_operand (x3, SFmode)) - { - ro[1] = x3; - if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) - return 136; - } - x1 = XEXP (x0, 0); - goto L1147; - - L251: - x1 = XEXP (x0, 1); - if (general_operand (x1, DImode)) - { - ro[1] = x1; - return 50; - } - x1 = XEXP (x0, 0); - goto L253; - - L412: - x1 = XEXP (x0, 1); - switch (GET_MODE (x1)) - { - case DImode: - switch (GET_CODE (x1)) - { - case PLUS: - goto L413; - case MINUS: - goto L436; - case NEG: - goto L586; - } - } - if (general_operand (x1, DImode)) - { - ro[1] = x1; - return 51; - } - x1 = XEXP (x0, 0); - goto L268; - - L413: - x2 = XEXP (x1, 0); - if (general_operand (x2, DImode)) - { - ro[1] = x2; - goto L414; - } - x1 = XEXP (x0, 0); - goto L268; - - L414: - x2 = XEXP (x1, 1); - if (general_operand (x2, DImode)) - { - ro[2] = x2; - return 81; - } - x1 = XEXP (x0, 0); - goto L268; - - L436: - x2 = XEXP (x1, 0); - if (general_operand (x2, DImode)) - { - ro[1] = x2; - goto L437; - } - x1 = XEXP (x0, 0); - goto L268; - - L437: - x2 = XEXP (x1, 1); - if (general_operand (x2, DImode)) - { - ro[2] = x2; - return 88; - } - x1 = XEXP (x0, 0); - goto L268; - - L586: - x2 = XEXP (x1, 0); - if (general_operand (x2, DImode)) - { - ro[1] = x2; - return 118; - } - x1 = XEXP (x0, 0); - goto L268; - - L269: - x1 = XEXP (x0, 1); - if (GET_MODE (x1) != DImode) - { - x1 = XEXP (x0, 0); - goto L1147; - } - switch (GET_CODE (x1)) - { - case ZERO_EXTEND: - goto L270; - case SIGN_EXTEND: - goto L274; - case ASHIFT: - goto L679; - case ASHIFTRT: - goto L707; - case LSHIFTRT: - goto L735; - } - x1 = XEXP (x0, 0); - goto L1147; - - L270: - x2 = XEXP (x1, 0); - if (register_operand (x2, SImode)) - { - ro[1] = x2; - return 55; - } - x1 = XEXP (x0, 0); - goto L1147; - - L274: - x2 = XEXP (x1, 0); - if (register_operand (x2, SImode)) - { - ro[1] = x2; - return 56; - } - x1 = XEXP (x0, 0); - goto L1147; - - L679: - x2 = XEXP (x1, 0); - if (register_operand (x2, DImode)) - { - ro[1] = x2; - goto L680; - } - x1 = XEXP (x0, 0); - goto L1147; - - L680: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && 1) - { - ro[2] = x2; - return 141; - } - x1 = XEXP (x0, 0); - goto L1147; - - L707: - x2 = XEXP (x1, 0); - if (register_operand (x2, DImode)) - { - ro[1] = x2; - goto L708; - } - x1 = XEXP (x0, 0); - goto L1147; - - L708: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && 1) - { - ro[2] = x2; - return 147; - } - x1 = XEXP (x0, 0); - goto L1147; - - L735: - x2 = XEXP (x1, 0); - if (register_operand (x2, DImode)) - { - ro[1] = x2; - goto L736; - } - x1 = XEXP (x0, 0); - goto L1147; - - L736: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && 1) - { - ro[2] = x2; - return 153; - } - x1 = XEXP (x0, 0); - goto L1147; - L2: - tem = recog_1 (x0, insn, pnum_clobbers); - if (tem >= 0) return tem; - x1 = XEXP (x0, 0); - goto L1147; - - L218: - x2 = XEXP (x1, 0); - switch (GET_MODE (x2)) - { - case HImode: - if (general_operand (x2, HImode)) - { - ro[0] = x2; - goto L219; - } - break; - case QImode: - if (general_operand (x2, QImode)) - { - ro[0] = x2; - goto L229; - } - } - goto L1147; - - L219: - x1 = XEXP (x0, 1); - if (general_operand (x1, HImode)) - { - ro[1] = x1; - return 41; - } - x1 = XEXP (x0, 0); - goto L1147; - - L229: - x1 = XEXP (x0, 1); - if (general_operand (x1, QImode)) - { - ro[1] = x1; - return 44; - } - x1 = XEXP (x0, 0); - goto L1147; - - L1081: - x1 = XEXP (x0, 1); - switch (GET_CODE (x1)) - { - case MINUS: - if (GET_MODE (x1) == SImode && 1) - goto L1082; - break; - case IF_THEN_ELSE: - goto L881; - case LABEL_REF: - goto L1061; - } - L1064: - if (general_operand (x1, SImode)) - { - ro[0] = x1; - return 221; - } - x1 = XEXP (x0, 0); - goto L1147; - - L1082: - x2 = XEXP (x1, 0); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == REG && XINT (x2, 0) == 3 && 1) - goto L1083; - x1 = XEXP (x0, 0); - goto L1147; - - L1083: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == MEM && 1) - goto L1084; - x1 = XEXP (x0, 0); - goto L1147; - - L1084: - x3 = XEXP (x2, 0); - if (GET_MODE (x3) == SImode && GET_CODE (x3) == PLUS && 1) - goto L1085; - x1 = XEXP (x0, 0); - goto L1147; - - L1085: - x4 = XEXP (x3, 0); - if (GET_MODE (x4) == SImode && GET_CODE (x4) == MULT && 1) - goto L1086; - x1 = XEXP (x0, 0); - goto L1147; - - L1086: - x5 = XEXP (x4, 0); - if (register_operand (x5, SImode)) - { - ro[0] = x5; - goto L1087; - } - x1 = XEXP (x0, 0); - goto L1147; - - L1087: - x5 = XEXP (x4, 1); - if (GET_CODE (x5) == CONST_INT && XWINT (x5, 0) == 4 && 1) - goto L1088; - x1 = XEXP (x0, 0); - goto L1147; - - L1088: - x4 = XEXP (x3, 1); - if (GET_CODE (x4) == LABEL_REF && 1) - goto L1089; - x1 = XEXP (x0, 0); - goto L1147; - - L1089: - x5 = XEXP (x4, 0); - if (pnum_clobbers != 0 && 1) - { - ro[1] = x5; - *pnum_clobbers = 1; - return 223; - } - x1 = XEXP (x0, 0); - goto L1147; - L881: - tem = recog_2 (x0, insn, pnum_clobbers); - if (tem >= 0) return tem; - x1 = XEXP (x0, 0); - goto L1147; - - L1061: - x2 = XEXP (x1, 0); - ro[0] = x2; - return 220; - - L1148: - x1 = XEXP (x0, 1); - if (GET_CODE (x1) == CALL && 1) - goto L1149; - x1 = XEXP (x0, 0); - goto L1227; - - L1149: - x2 = XEXP (x1, 0); - if (call_insn_operand (x2, QImode)) - { - ro[1] = x2; - goto L1150; - } - L1154: - if (GET_MODE (x2) == QImode && GET_CODE (x2) == MEM && 1) - goto L1155; - x1 = XEXP (x0, 0); - goto L1227; - - L1150: - x2 = XEXP (x1, 1); - if (general_operand (x2, SImode)) - { - ro[2] = x2; - return 235; - } - x2 = XEXP (x1, 0); - goto L1154; - - L1155: - x3 = XEXP (x2, 0); - if (symbolic_operand (x3, SImode)) - { - ro[1] = x3; - goto L1156; - } - x1 = XEXP (x0, 0); - goto L1227; - - L1156: - x2 = XEXP (x1, 1); - if (general_operand (x2, SImode)) - { - ro[2] = x2; - if (!HALF_PIC_P ()) - return 236; - } - x1 = XEXP (x0, 0); - goto L1227; - - L1228: - x1 = XEXP (x0, 1); - if (GET_MODE (x1) == SImode && GET_CODE (x1) == PLUS && 1) - goto L1229; - goto ret0; - - L1229: - x2 = XEXP (x1, 0); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == FFS && 1) - goto L1230; - goto ret0; - - L1230: - x3 = XEXP (x2, 0); - if (general_operand (x3, SImode)) - { - ro[1] = x3; - goto L1231; - } - goto ret0; - - L1231: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == -1 && 1) - return 250; - goto ret0; - - L1234: - x1 = XEXP (x0, 1); - if (GET_MODE (x1) == HImode && GET_CODE (x1) == PLUS && 1) - goto L1235; - goto ret0; - - L1235: - x2 = XEXP (x1, 0); - if (GET_MODE (x2) == HImode && GET_CODE (x2) == FFS && 1) - goto L1236; - goto ret0; - - L1236: - x3 = XEXP (x2, 0); - if (general_operand (x3, SImode)) - { - ro[1] = x3; - goto L1237; - } - goto ret0; - - L1237: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == -1 && 1) - return 252; - goto ret0; - - L1240: - x1 = XEXP (x0, 1); - if (binary_387_op (x1, DFmode)) - { - ro[3] = x1; - goto L1246; - } - goto ret0; - - L1246: - x2 = XEXP (x1, 0); - switch (GET_MODE (x2)) - { - case DFmode: - switch (GET_CODE (x2)) - { - case FLOAT: - goto L1247; - case FLOAT_EXTEND: - goto L1253; - case SUBREG: - case REG: - case MEM: - if (nonimmediate_operand (x2, DFmode)) - { - ro[1] = x2; - goto L1242; - } - } - } - L1258: - if (general_operand (x2, DFmode)) - { - ro[1] = x2; - goto L1259; - } - goto ret0; - - L1247: - x3 = XEXP (x2, 0); - if (general_operand (x3, SImode)) - { - ro[1] = x3; - goto L1248; - } - goto ret0; - - L1248: - x2 = XEXP (x1, 1); - if (general_operand (x2, DFmode)) - { - ro[2] = x2; - if (TARGET_80387) - return 254; - } - goto ret0; - - L1253: - x3 = XEXP (x2, 0); - if (general_operand (x3, SFmode)) - { - ro[1] = x3; - goto L1254; - } - goto ret0; - - L1254: - x2 = XEXP (x1, 1); - if (general_operand (x2, DFmode)) - { - ro[2] = x2; - if (TARGET_80387) - return 255; - } - goto ret0; - - L1242: - x2 = XEXP (x1, 1); - if (nonimmediate_operand (x2, DFmode)) - { - ro[2] = x2; - if (TARGET_80387) - return 253; - } - x2 = XEXP (x1, 0); - goto L1258; - - L1259: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) != DFmode) - goto ret0; - switch (GET_CODE (x2)) - { - case FLOAT: - goto L1260; - case FLOAT_EXTEND: - goto L1266; - } - goto ret0; - - L1260: - x3 = XEXP (x2, 0); - if (general_operand (x3, SImode)) - { - ro[2] = x3; - if (TARGET_80387) - return 256; - } - goto ret0; - - L1266: - x3 = XEXP (x2, 0); - if (general_operand (x3, SFmode)) - { - ro[2] = x3; - if (TARGET_80387) - return 257; - } - goto ret0; - - L1269: - x1 = XEXP (x0, 1); - if (binary_387_op (x1, SFmode)) - { - ro[3] = x1; - goto L1275; - } - goto ret0; - - L1275: - x2 = XEXP (x1, 0); - switch (GET_MODE (x2)) - { - case SFmode: - if (GET_CODE (x2) == FLOAT && 1) - goto L1276; - if (nonimmediate_operand (x2, SFmode)) - { - ro[1] = x2; - goto L1271; - } - } - L1281: - if (general_operand (x2, SFmode)) - { - ro[1] = x2; - goto L1282; - } - goto ret0; - - L1276: - x3 = XEXP (x2, 0); - if (general_operand (x3, SImode)) - { - ro[1] = x3; - goto L1277; - } - goto ret0; - - L1277: - x2 = XEXP (x1, 1); - if (general_operand (x2, SFmode)) - { - ro[2] = x2; - if (TARGET_80387) - return 259; - } - goto ret0; - - L1271: - x2 = XEXP (x1, 1); - if (nonimmediate_operand (x2, SFmode)) - { - ro[2] = x2; - if (TARGET_80387) - return 258; - } - x2 = XEXP (x1, 0); - goto L1281; - - L1282: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == SFmode && GET_CODE (x2) == FLOAT && 1) - goto L1283; - goto ret0; - - L1283: - x3 = XEXP (x2, 0); - if (general_operand (x3, SImode)) - { - ro[2] = x3; - if (TARGET_80387) - return 260; - } - goto ret0; - ret0: return -1; -} - -int -recog_4 (x0, insn, pnum_clobbers) - register rtx x0; - rtx insn; - int *pnum_clobbers; -{ - register rtx *ro = &recog_operand[0]; - register rtx x1, x2, x3, x4, x5, x6; - int tem; - - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - switch (GET_MODE (x2)) - { - case SFmode: - if (register_operand (x2, SFmode)) - { - ro[0] = x2; - goto L13; - } - break; - case DFmode: - if (register_operand (x2, DFmode)) - { - ro[0] = x2; - goto L22; - } - } - L45: - if (VOIDmode_compare_op (x2, VOIDmode)) - { - ro[2] = x2; - goto L74; - } - L118: - if (GET_MODE (x2) == CCFPEQmode && GET_CODE (x2) == COMPARE && 1) - goto L119; - goto ret0; - - L13: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L14; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - goto L45; - - L14: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, HImode)) - { - ro[1] = x2; - if (TARGET_80387 && ! TARGET_IEEE_FP) - return 6; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - goto L45; - - L22: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L23; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - goto L45; - - L23: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, HImode)) - { - ro[1] = x2; - if (TARGET_80387 && ! TARGET_IEEE_FP) - return 8; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - goto L45; - - L74: - x3 = XEXP (x2, 0); - switch (GET_MODE (x3)) - { - case DFmode: - switch (GET_CODE (x3)) - { - case FLOAT: - goto L75; - case FLOAT_EXTEND: - goto L105; - case SUBREG: - case REG: - case MEM: - if (nonimmediate_operand (x3, DFmode)) - { - ro[0] = x3; - goto L47; - } - } - L59: - if (register_operand (x3, DFmode)) - { - ro[0] = x3; - goto L60; - } - break; - case SFmode: - if (GET_CODE (x3) == FLOAT && 1) - goto L161; - if (nonimmediate_operand (x3, SFmode)) - { - ro[0] = x3; - goto L133; - } - L145: - if (register_operand (x3, SFmode)) - { - ro[0] = x3; - goto L146; - } - } - goto L118; - - L75: - x4 = XEXP (x3, 0); - if (nonimmediate_operand (x4, SImode)) - { - ro[0] = x4; - goto L76; - } - goto L118; - - L76: - x3 = XEXP (x2, 1); - if (register_operand (x3, DFmode)) - { - ro[1] = x3; - goto L77; - } - goto L118; - - L77: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L78; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - goto L118; - - L78: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, HImode)) - { - ro[3] = x2; - if (TARGET_80387) - return 18; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - goto L118; - - L105: - x4 = XEXP (x3, 0); - if (nonimmediate_operand (x4, SFmode)) - { - ro[0] = x4; - goto L106; - } - goto L118; - - L106: - x3 = XEXP (x2, 1); - if (register_operand (x3, DFmode)) - { - ro[1] = x3; - goto L107; - } - goto L118; - - L107: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L108; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - goto L118; - - L108: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, HImode)) - { - ro[3] = x2; - if (TARGET_80387) - return 20; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - goto L118; - - L47: - x3 = XEXP (x2, 1); - if (nonimmediate_operand (x3, DFmode)) - { - ro[1] = x3; - goto L48; - } - x3 = XEXP (x2, 0); - goto L59; - - L48: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L49; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - x3 = XEXP (x2, 0); - goto L59; - - L49: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, HImode)) - { - ro[3] = x2; - if (TARGET_80387 - && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)) - return 16; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - x3 = XEXP (x2, 0); - goto L59; - - L60: - x3 = XEXP (x2, 1); - if (GET_MODE (x3) != DFmode) - { - goto L118; - } - switch (GET_CODE (x3)) - { - case FLOAT: - goto L61; - case FLOAT_EXTEND: - goto L91; - } - goto L118; - - L61: - x4 = XEXP (x3, 0); - if (nonimmediate_operand (x4, SImode)) - { - ro[1] = x4; - goto L62; - } - goto L118; - - L62: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L63; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - goto L118; - - L63: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, HImode)) - { - ro[3] = x2; - if (TARGET_80387) - return 17; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - goto L118; - - L91: - x4 = XEXP (x3, 0); - if (nonimmediate_operand (x4, SFmode)) - { - ro[1] = x4; - goto L92; - } - goto L118; - - L92: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L93; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - goto L118; - - L93: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, HImode)) - { - ro[3] = x2; - if (TARGET_80387) - return 19; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - goto L118; - - L161: - x4 = XEXP (x3, 0); - if (nonimmediate_operand (x4, SImode)) - { - ro[0] = x4; - goto L162; - } - goto L118; - - L162: - x3 = XEXP (x2, 1); - if (register_operand (x3, SFmode)) - { - ro[1] = x3; - goto L163; - } - goto L118; - - L163: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L164; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - goto L118; - - L164: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, HImode)) - { - ro[3] = x2; - if (TARGET_80387) - return 24; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - goto L118; - - L133: - x3 = XEXP (x2, 1); - if (nonimmediate_operand (x3, SFmode)) - { - ro[1] = x3; - goto L134; - } - x3 = XEXP (x2, 0); - goto L145; - - L134: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L135; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - x3 = XEXP (x2, 0); - goto L145; - - L135: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, HImode)) - { - ro[3] = x2; - if (TARGET_80387 - && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)) - return 22; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - x3 = XEXP (x2, 0); - goto L145; - - L146: - x3 = XEXP (x2, 1); - if (GET_MODE (x3) == SFmode && GET_CODE (x3) == FLOAT && 1) - goto L147; - goto L118; - - L147: - x4 = XEXP (x3, 0); - if (nonimmediate_operand (x4, SImode)) - { - ro[1] = x4; - goto L148; - } - goto L118; - - L148: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L149; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - goto L118; - - L149: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, HImode)) - { - ro[3] = x2; - if (TARGET_80387) - return 23; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - goto L118; - - L119: - x3 = XEXP (x2, 0); - switch (GET_MODE (x3)) - { - case DFmode: - if (register_operand (x3, DFmode)) - { - ro[0] = x3; - goto L120; - } - break; - case SFmode: - if (register_operand (x3, SFmode)) - { - ro[0] = x3; - goto L176; - } - } - goto ret0; - - L120: - x3 = XEXP (x2, 1); - if (register_operand (x3, DFmode)) - { - ro[1] = x3; - goto L121; - } - goto ret0; - - L121: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L122; - goto ret0; - - L122: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, HImode)) - { - ro[2] = x2; - if (TARGET_80387) - return 21; - } - goto ret0; - - L176: - x3 = XEXP (x2, 1); - if (register_operand (x3, SFmode)) - { - ro[1] = x3; - goto L177; - } - goto ret0; - - L177: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L178; - goto ret0; - - L178: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, HImode)) - { - ro[2] = x2; - if (TARGET_80387) - return 25; - } - goto ret0; - ret0: return -1; -} - -int -recog_5 (x0, insn, pnum_clobbers) - register rtx x0; - rtx insn; - int *pnum_clobbers; -{ - register rtx *ro = &recog_operand[0]; - register rtx x1, x2, x3, x4, x5, x6; - int tem; - - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - switch (GET_MODE (x2)) - { - case DFmode: - if (register_operand (x2, DFmode)) - { - ro[0] = x2; - goto L242; - } - break; - case SFmode: - if (nonimmediate_operand (x2, SFmode)) - { - ro[0] = x2; - goto L294; - } - break; - case SImode: - if (register_operand (x2, SImode)) - { - ro[0] = x2; - goto L497; - } - break; - case HImode: - if (register_operand (x2, HImode)) - { - ro[0] = x2; - goto L508; - } - break; - case DImode: - if (register_operand (x2, DImode)) - { - ro[0] = x2; - goto L684; - } - } - switch (GET_CODE (x2)) - { - case CC0: - goto L12; - case PC: - goto L1068; - } - L1125: - ro[0] = x2; - goto L1126; - L1286: - if (register_operand (x2, SImode)) - { - ro[0] = x2; - goto L1287; - } - goto ret0; - - L242: - x2 = XEXP (x1, 1); - if (register_operand (x2, DFmode)) - { - ro[1] = x2; - goto L243; - } - x2 = XEXP (x1, 0); - goto L1125; - - L243: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == SET && 1) - goto L244; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L244: - x2 = XEXP (x1, 0); - if (rtx_equal_p (x2, ro[1]) && 1) - goto L245; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L245: - x2 = XEXP (x1, 1); - if (rtx_equal_p (x2, ro[0]) && 1) - return 48; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L294: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == SFmode && GET_CODE (x2) == FLOAT_TRUNCATE && 1) - goto L295; - x2 = XEXP (x1, 0); - goto L1125; - - L295: - x3 = XEXP (x2, 0); - if (register_operand (x3, DFmode)) - { - ro[1] = x3; - goto L296; - } - x2 = XEXP (x1, 0); - goto L1125; - - L296: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L297; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L297: - x2 = XEXP (x1, 0); - if (memory_operand (x2, SFmode)) - { - ro[2] = x2; - if (TARGET_80387) - return 62; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L497: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) != SImode) - { - x2 = XEXP (x1, 0); - goto L1125; - } - switch (GET_CODE (x2)) - { - case DIV: - goto L498; - case UDIV: - goto L520; - } - x2 = XEXP (x1, 0); - goto L1125; - - L498: - x3 = XEXP (x2, 0); - if (register_operand (x3, SImode)) - { - ro[1] = x3; - goto L499; - } - x2 = XEXP (x1, 0); - goto L1125; - - L499: - x3 = XEXP (x2, 1); - if (general_operand (x3, SImode)) - { - ro[2] = x3; - goto L500; - } - x2 = XEXP (x1, 0); - goto L1125; - - L500: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == SET && 1) - goto L501; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L501: - x2 = XEXP (x1, 0); - if (register_operand (x2, SImode)) - { - ro[3] = x2; - goto L502; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L502: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == MOD && 1) - goto L503; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L503: - x3 = XEXP (x2, 0); - if (rtx_equal_p (x3, ro[1]) && 1) - goto L504; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L504: - x3 = XEXP (x2, 1); - if (rtx_equal_p (x3, ro[2]) && 1) - return 105; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L520: - x3 = XEXP (x2, 0); - if (register_operand (x3, SImode)) - { - ro[1] = x3; - goto L521; - } - x2 = XEXP (x1, 0); - goto L1125; - - L521: - x3 = XEXP (x2, 1); - if (general_operand (x3, SImode)) - { - ro[2] = x3; - goto L522; - } - x2 = XEXP (x1, 0); - goto L1125; - - L522: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == SET && 1) - goto L523; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L523: - x2 = XEXP (x1, 0); - if (register_operand (x2, SImode)) - { - ro[3] = x2; - goto L524; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L524: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == UMOD && 1) - goto L525; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L525: - x3 = XEXP (x2, 0); - if (rtx_equal_p (x3, ro[1]) && 1) - goto L526; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L526: - x3 = XEXP (x2, 1); - if (rtx_equal_p (x3, ro[2]) && 1) - return 107; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L508: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) != HImode) - { - x2 = XEXP (x1, 0); - goto L1125; - } - switch (GET_CODE (x2)) - { - case DIV: - goto L509; - case UDIV: - goto L531; - } - x2 = XEXP (x1, 0); - goto L1125; - - L509: - x3 = XEXP (x2, 0); - if (register_operand (x3, HImode)) - { - ro[1] = x3; - goto L510; - } - x2 = XEXP (x1, 0); - goto L1125; - - L510: - x3 = XEXP (x2, 1); - if (general_operand (x3, HImode)) - { - ro[2] = x3; - goto L511; - } - x2 = XEXP (x1, 0); - goto L1125; - - L511: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == SET && 1) - goto L512; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L512: - x2 = XEXP (x1, 0); - if (register_operand (x2, HImode)) - { - ro[3] = x2; - goto L513; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L513: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == HImode && GET_CODE (x2) == MOD && 1) - goto L514; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L514: - x3 = XEXP (x2, 0); - if (rtx_equal_p (x3, ro[1]) && 1) - goto L515; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L515: - x3 = XEXP (x2, 1); - if (rtx_equal_p (x3, ro[2]) && 1) - return 106; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L531: - x3 = XEXP (x2, 0); - if (register_operand (x3, HImode)) - { - ro[1] = x3; - goto L532; - } - x2 = XEXP (x1, 0); - goto L1125; - - L532: - x3 = XEXP (x2, 1); - if (general_operand (x3, HImode)) - { - ro[2] = x3; - goto L533; - } - x2 = XEXP (x1, 0); - goto L1125; - - L533: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == SET && 1) - goto L534; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L534: - x2 = XEXP (x1, 0); - if (register_operand (x2, HImode)) - { - ro[3] = x2; - goto L535; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L535: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == HImode && GET_CODE (x2) == UMOD && 1) - goto L536; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L536: - x3 = XEXP (x2, 0); - if (rtx_equal_p (x3, ro[1]) && 1) - goto L537; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L537: - x3 = XEXP (x2, 1); - if (rtx_equal_p (x3, ro[2]) && 1) - return 108; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L684: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) != DImode) - { - x2 = XEXP (x1, 0); - goto L1125; - } - switch (GET_CODE (x2)) - { - case ASHIFT: - goto L685; - case ASHIFTRT: - goto L713; - case LSHIFTRT: - goto L741; - } - x2 = XEXP (x1, 0); - goto L1125; - - L685: - x3 = XEXP (x2, 0); - if (register_operand (x3, DImode)) - { - ro[1] = x3; - goto L686; - } - x2 = XEXP (x1, 0); - goto L1125; - - L686: - x3 = XEXP (x2, 1); - if (register_operand (x3, QImode)) - { - ro[2] = x3; - goto L687; - } - x2 = XEXP (x1, 0); - goto L1125; - - L687: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L688; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L688: - x2 = XEXP (x1, 0); - if (rtx_equal_p (x2, ro[2]) && 1) - return 142; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L713: - x3 = XEXP (x2, 0); - if (register_operand (x3, DImode)) - { - ro[1] = x3; - goto L714; - } - x2 = XEXP (x1, 0); - goto L1125; - - L714: - x3 = XEXP (x2, 1); - if (register_operand (x3, QImode)) - { - ro[2] = x3; - goto L715; - } - x2 = XEXP (x1, 0); - goto L1125; - - L715: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L716; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L716: - x2 = XEXP (x1, 0); - if (rtx_equal_p (x2, ro[2]) && 1) - return 148; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L741: - x3 = XEXP (x2, 0); - if (register_operand (x3, DImode)) - { - ro[1] = x3; - goto L742; - } - x2 = XEXP (x1, 0); - goto L1125; - - L742: - x3 = XEXP (x2, 1); - if (register_operand (x3, QImode)) - { - ro[2] = x3; - goto L743; - } - x2 = XEXP (x1, 0); - goto L1125; - - L743: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L744; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L744: - x2 = XEXP (x1, 0); - if (rtx_equal_p (x2, ro[2]) && 1) - return 154; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - L12: - tem = recog_4 (x0, insn, pnum_clobbers); - if (tem >= 0) return tem; - x2 = XEXP (x1, 0); - goto L1125; - - L1068: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == MINUS && 1) - goto L1069; - if (general_operand (x2, SImode)) - { - ro[0] = x2; - goto L1094; - } - x2 = XEXP (x1, 0); - goto L1125; - - L1069: - x3 = XEXP (x2, 0); - if (GET_MODE (x3) == SImode && GET_CODE (x3) == REG && XINT (x3, 0) == 3 && 1) - goto L1070; - x2 = XEXP (x1, 0); - goto L1125; - - L1070: - x3 = XEXP (x2, 1); - if (GET_MODE (x3) == SImode && GET_CODE (x3) == MEM && 1) - goto L1071; - x2 = XEXP (x1, 0); - goto L1125; - - L1071: - x4 = XEXP (x3, 0); - if (GET_MODE (x4) == SImode && GET_CODE (x4) == PLUS && 1) - goto L1072; - x2 = XEXP (x1, 0); - goto L1125; - - L1072: - x5 = XEXP (x4, 0); - if (GET_MODE (x5) == SImode && GET_CODE (x5) == MULT && 1) - goto L1073; - x2 = XEXP (x1, 0); - goto L1125; - - L1073: - x6 = XEXP (x5, 0); - if (register_operand (x6, SImode)) - { - ro[0] = x6; - goto L1074; - } - x2 = XEXP (x1, 0); - goto L1125; - - L1074: - x6 = XEXP (x5, 1); - if (GET_CODE (x6) == CONST_INT && XWINT (x6, 0) == 4 && 1) - goto L1075; - x2 = XEXP (x1, 0); - goto L1125; - - L1075: - x5 = XEXP (x4, 1); - if (GET_CODE (x5) == LABEL_REF && 1) - goto L1076; - x2 = XEXP (x1, 0); - goto L1125; - - L1076: - x6 = XEXP (x5, 0); - ro[1] = x6; - goto L1077; - - L1077: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L1078; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L1078: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, SImode)) - { - ro[2] = x2; - return 223; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L1094: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == USE && 1) - goto L1095; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L1095: - x2 = XEXP (x1, 0); - if (GET_CODE (x2) == LABEL_REF && 1) - goto L1096; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1125; - - L1096: - x3 = XEXP (x2, 0); - ro[1] = x3; - return 224; - - L1126: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CALL && 1) - goto L1138; - x2 = XEXP (x1, 0); - goto L1286; - - L1138: - x3 = XEXP (x2, 0); - if (GET_MODE (x3) == QImode && GET_CODE (x3) == MEM && 1) - goto L1139; - L1127: - if (call_insn_operand (x3, QImode)) - { - ro[1] = x3; - goto L1128; - } - x2 = XEXP (x1, 0); - goto L1286; - - L1139: - x4 = XEXP (x3, 0); - if (symbolic_operand (x4, SImode)) - { - ro[1] = x4; - goto L1140; - } - goto L1127; - - L1140: - x3 = XEXP (x2, 1); - if (general_operand (x3, SImode)) - { - ro[2] = x3; - goto L1141; - } - x3 = XEXP (x2, 0); - goto L1127; - - L1141: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == SET && 1) - goto L1142; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - x3 = XEXP (x2, 0); - goto L1127; - - L1142: - x2 = XEXP (x1, 0); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == REG && XINT (x2, 0) == 7 && 1) - goto L1143; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - x3 = XEXP (x2, 0); - goto L1127; - - L1143: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == PLUS && 1) - goto L1144; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - x3 = XEXP (x2, 0); - goto L1127; - - L1144: - x3 = XEXP (x2, 0); - if (GET_MODE (x3) == SImode && GET_CODE (x3) == REG && XINT (x3, 0) == 7 && 1) - goto L1145; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - x3 = XEXP (x2, 0); - goto L1127; - - L1145: - x3 = XEXP (x2, 1); - if (immediate_operand (x3, SImode)) - { - ro[4] = x3; - if (!HALF_PIC_P ()) - return 233; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 1); - x3 = XEXP (x2, 0); - goto L1127; - - L1128: - x3 = XEXP (x2, 1); - if (general_operand (x3, SImode)) - { - ro[2] = x3; - goto L1129; - } - x2 = XEXP (x1, 0); - goto L1286; - - L1129: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == SET && 1) - goto L1130; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1286; - - L1130: - x2 = XEXP (x1, 0); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == REG && XINT (x2, 0) == 7 && 1) - goto L1131; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1286; - - L1131: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == PLUS && 1) - goto L1132; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1286; - - L1132: - x3 = XEXP (x2, 0); - if (GET_MODE (x3) == SImode && GET_CODE (x3) == REG && XINT (x3, 0) == 7 && 1) - goto L1133; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1286; - - L1133: - x3 = XEXP (x2, 1); - if (immediate_operand (x3, SImode)) - { - ro[4] = x3; - return 232; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1286; - - L1287: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == UNSPEC && XINT (x2, 1) == 0 && XVECLEN (x2, 0) == 3 && 1) - goto L1288; - goto ret0; - - L1288: - x3 = XVECEXP (x2, 0, 0); - if (GET_MODE (x3) == BLKmode && GET_CODE (x3) == MEM && 1) - goto L1289; - goto ret0; - - L1289: - x4 = XEXP (x3, 0); - if (address_operand (x4, SImode)) - { - ro[1] = x4; - goto L1290; - } - goto ret0; - - L1290: - x3 = XVECEXP (x2, 0, 1); - if (register_operand (x3, QImode)) - { - ro[2] = x3; - goto L1291; - } - goto ret0; - - L1291: - x3 = XVECEXP (x2, 0, 2); - if (immediate_operand (x3, SImode)) - { - ro[3] = x3; - goto L1292; - } - goto ret0; - - L1292: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L1293; - goto ret0; - - L1293: - x2 = XEXP (x1, 0); - if (rtx_equal_p (x2, ro[1]) && 1) - return 262; - goto ret0; - ret0: return -1; -} - -int -recog (x0, insn, pnum_clobbers) - register rtx x0; - rtx insn; - int *pnum_clobbers; -{ - register rtx *ro = &recog_operand[0]; - register rtx x1, x2, x3, x4, x5, x6; - int tem; - - L1170: - switch (GET_CODE (x0)) - { - case UNSPEC: - if (GET_MODE (x0) == SImode && XINT (x0, 1) == 0 && XVECLEN (x0, 0) == 1 && 1) - goto L1171; - break; - case SET: - goto L200; - case PARALLEL: - if (XVECLEN (x0, 0) == 2 && 1) - goto L10; - if (XVECLEN (x0, 0) == 5 && 1) - goto L299; - if (XVECLEN (x0, 0) == 4 && 1) - goto L313; - if (XVECLEN (x0, 0) == 3 && 1) - goto L363; - if (XVECLEN (x0, 0) == 6 && 1) - goto L1175; - break; - case CALL: - goto L1117; - case RETURN: - if (simple_386_epilogue ()) - return 242; - break; - case CONST_INT: - if (XWINT (x0, 0) == 0 && 1) - return 243; - } - goto ret0; - - L1171: - x1 = XVECEXP (x0, 0, 0); - if (memory_operand (x1, SImode)) - { - ro[0] = x1; - return 241; - } - goto ret0; - L200: - return recog_3 (x0, insn, pnum_clobbers); - - L10: - x1 = XVECEXP (x0, 0, 0); - switch (GET_CODE (x1)) - { - case SET: - goto L241; - case CALL: - goto L1108; - } - goto ret0; - L241: - return recog_5 (x0, insn, pnum_clobbers); - - L1108: - x2 = XEXP (x1, 0); - if (GET_MODE (x2) == QImode && GET_CODE (x2) == MEM && 1) - goto L1109; - L1099: - if (call_insn_operand (x2, QImode)) - { - ro[0] = x2; - goto L1100; - } - goto ret0; - - L1109: - x3 = XEXP (x2, 0); - if (symbolic_operand (x3, SImode)) - { - ro[0] = x3; - goto L1110; - } - goto L1099; - - L1110: - x2 = XEXP (x1, 1); - if (general_operand (x2, SImode)) - { - ro[1] = x2; - goto L1111; - } - x2 = XEXP (x1, 0); - goto L1099; - - L1111: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == SET && 1) - goto L1112; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1099; - - L1112: - x2 = XEXP (x1, 0); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == REG && XINT (x2, 0) == 7 && 1) - goto L1113; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1099; - - L1113: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == PLUS && 1) - goto L1114; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1099; - - L1114: - x3 = XEXP (x2, 0); - if (GET_MODE (x3) == SImode && GET_CODE (x3) == REG && XINT (x3, 0) == 7 && 1) - goto L1115; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1099; - - L1115: - x3 = XEXP (x2, 1); - if (immediate_operand (x3, SImode)) - { - ro[3] = x3; - if (!HALF_PIC_P ()) - return 227; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1099; - - L1100: - x2 = XEXP (x1, 1); - if (general_operand (x2, SImode)) - { - ro[1] = x2; - goto L1101; - } - goto ret0; - - L1101: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == SET && 1) - goto L1102; - goto ret0; - - L1102: - x2 = XEXP (x1, 0); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == REG && XINT (x2, 0) == 7 && 1) - goto L1103; - goto ret0; - - L1103: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == PLUS && 1) - goto L1104; - goto ret0; - - L1104: - x3 = XEXP (x2, 0); - if (GET_MODE (x3) == SImode && GET_CODE (x3) == REG && XINT (x3, 0) == 7 && 1) - goto L1105; - goto ret0; - - L1105: - x3 = XEXP (x2, 1); - if (immediate_operand (x3, SImode)) - { - ro[3] = x3; - return 226; - } - goto ret0; - - L299: - x1 = XVECEXP (x0, 0, 0); - if (GET_CODE (x1) == SET && 1) - goto L300; - goto ret0; - - L300: - x2 = XEXP (x1, 0); - if (GET_MODE (x2) == DImode && general_operand (x2, DImode)) - { - ro[0] = x2; - goto L301; - } - goto ret0; - - L301: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == DImode && GET_CODE (x2) == FIX && 1) - goto L302; - goto ret0; - - L302: - x3 = XEXP (x2, 0); - if (GET_CODE (x3) != FIX) - goto ret0; - switch (GET_MODE (x3)) - { - case DFmode: - goto L303; - case SFmode: - goto L329; - } - goto ret0; - - L303: - x4 = XEXP (x3, 0); - if (register_operand (x4, DFmode)) - { - ro[1] = x4; - goto L304; - } - goto ret0; - - L304: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L305; - goto ret0; - - L305: - x2 = XEXP (x1, 0); - if (rtx_equal_p (x2, ro[1]) && 1) - goto L306; - goto ret0; - - L306: - x1 = XVECEXP (x0, 0, 2); - if (GET_CODE (x1) == CLOBBER && 1) - goto L307; - goto ret0; - - L307: - x2 = XEXP (x1, 0); - if (memory_operand (x2, SImode)) - { - ro[2] = x2; - goto L308; - } - goto ret0; - - L308: - x1 = XVECEXP (x0, 0, 3); - if (GET_CODE (x1) == CLOBBER && 1) - goto L309; - goto ret0; - - L309: - x2 = XEXP (x1, 0); - if (memory_operand (x2, SImode)) - { - ro[3] = x2; - goto L310; - } - goto ret0; - - L310: - x1 = XVECEXP (x0, 0, 4); - if (GET_CODE (x1) == CLOBBER && 1) - goto L311; - goto ret0; - - L311: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, SImode)) - { - ro[4] = x2; - if (TARGET_80387) - return 67; - } - goto ret0; - - L329: - x4 = XEXP (x3, 0); - if (register_operand (x4, SFmode)) - { - ro[1] = x4; - goto L330; - } - goto ret0; - - L330: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L331; - goto ret0; - - L331: - x2 = XEXP (x1, 0); - if (rtx_equal_p (x2, ro[1]) && 1) - goto L332; - goto ret0; - - L332: - x1 = XVECEXP (x0, 0, 2); - if (GET_CODE (x1) == CLOBBER && 1) - goto L333; - goto ret0; - - L333: - x2 = XEXP (x1, 0); - if (memory_operand (x2, SImode)) - { - ro[2] = x2; - goto L334; - } - goto ret0; - - L334: - x1 = XVECEXP (x0, 0, 3); - if (GET_CODE (x1) == CLOBBER && 1) - goto L335; - goto ret0; - - L335: - x2 = XEXP (x1, 0); - if (memory_operand (x2, SImode)) - { - ro[3] = x2; - goto L336; - } - goto ret0; - - L336: - x1 = XVECEXP (x0, 0, 4); - if (GET_CODE (x1) == CLOBBER && 1) - goto L337; - goto ret0; - - L337: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, SImode)) - { - ro[4] = x2; - if (TARGET_80387) - return 68; - } - goto ret0; - - L313: - x1 = XVECEXP (x0, 0, 0); - if (GET_CODE (x1) == SET && 1) - goto L314; - goto ret0; - - L314: - x2 = XEXP (x1, 0); - switch (GET_MODE (x2)) - { - case DImode: - if (general_operand (x2, DImode)) - { - ro[0] = x2; - goto L315; - } - break; - case SImode: - if (general_operand (x2, SImode)) - { - ro[0] = x2; - goto L353; - } - } - goto ret0; - - L315: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == DImode && GET_CODE (x2) == FIX && 1) - goto L316; - goto ret0; - - L316: - x3 = XEXP (x2, 0); - if (GET_CODE (x3) != FIX) - goto ret0; - switch (GET_MODE (x3)) - { - case DFmode: - goto L317; - case SFmode: - goto L343; - } - goto ret0; - - L317: - x4 = XEXP (x3, 0); - if (register_operand (x4, DFmode)) - { - ro[1] = x4; - goto L318; - } - goto ret0; - - L318: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L319; - goto ret0; - - L319: - x2 = XEXP (x1, 0); - if (rtx_equal_p (x2, ro[1]) && 1) - goto L320; - goto ret0; - - L320: - x1 = XVECEXP (x0, 0, 2); - if (GET_CODE (x1) == CLOBBER && 1) - goto L321; - goto ret0; - - L321: - x2 = XEXP (x1, 0); - if (memory_operand (x2, SImode)) - { - ro[2] = x2; - goto L322; - } - goto ret0; - - L322: - x1 = XVECEXP (x0, 0, 3); - if (GET_CODE (x1) == CLOBBER && 1) - goto L323; - goto ret0; - - L323: - x2 = XEXP (x1, 0); - if (pnum_clobbers != 0 && memory_operand (x2, SImode)) - { - ro[3] = x2; - if (TARGET_80387) - { - *pnum_clobbers = 1; - return 67; - } - } - goto ret0; - - L343: - x4 = XEXP (x3, 0); - if (register_operand (x4, SFmode)) - { - ro[1] = x4; - goto L344; - } - goto ret0; - - L344: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L345; - goto ret0; - - L345: - x2 = XEXP (x1, 0); - if (rtx_equal_p (x2, ro[1]) && 1) - goto L346; - goto ret0; - - L346: - x1 = XVECEXP (x0, 0, 2); - if (GET_CODE (x1) == CLOBBER && 1) - goto L347; - goto ret0; - - L347: - x2 = XEXP (x1, 0); - if (memory_operand (x2, SImode)) - { - ro[2] = x2; - goto L348; - } - goto ret0; - - L348: - x1 = XVECEXP (x0, 0, 3); - if (GET_CODE (x1) == CLOBBER && 1) - goto L349; - goto ret0; - - L349: - x2 = XEXP (x1, 0); - if (pnum_clobbers != 0 && memory_operand (x2, SImode)) - { - ro[3] = x2; - if (TARGET_80387) - { - *pnum_clobbers = 1; - return 68; - } - } - goto ret0; - - L353: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == FIX && 1) - goto L354; - goto ret0; - - L354: - x3 = XEXP (x2, 0); - if (GET_CODE (x3) != FIX) - goto ret0; - switch (GET_MODE (x3)) - { - case DFmode: - goto L355; - case SFmode: - goto L377; - } - goto ret0; - - L355: - x4 = XEXP (x3, 0); - if (register_operand (x4, DFmode)) - { - ro[1] = x4; - goto L356; - } - goto ret0; - - L356: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L357; - goto ret0; - - L357: - x2 = XEXP (x1, 0); - if (memory_operand (x2, SImode)) - { - ro[2] = x2; - goto L358; - } - goto ret0; - - L358: - x1 = XVECEXP (x0, 0, 2); - if (GET_CODE (x1) == CLOBBER && 1) - goto L359; - goto ret0; - - L359: - x2 = XEXP (x1, 0); - if (memory_operand (x2, SImode)) - { - ro[3] = x2; - goto L360; - } - goto ret0; - - L360: - x1 = XVECEXP (x0, 0, 3); - if (GET_CODE (x1) == CLOBBER && 1) - goto L361; - goto ret0; - - L361: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, SImode)) - { - ro[4] = x2; - if (TARGET_80387) - return 71; - } - goto ret0; - - L377: - x4 = XEXP (x3, 0); - if (register_operand (x4, SFmode)) - { - ro[1] = x4; - goto L378; - } - goto ret0; - - L378: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L379; - goto ret0; - - L379: - x2 = XEXP (x1, 0); - if (memory_operand (x2, SImode)) - { - ro[2] = x2; - goto L380; - } - goto ret0; - - L380: - x1 = XVECEXP (x0, 0, 2); - if (GET_CODE (x1) == CLOBBER && 1) - goto L381; - goto ret0; - - L381: - x2 = XEXP (x1, 0); - if (memory_operand (x2, SImode)) - { - ro[3] = x2; - goto L382; - } - goto ret0; - - L382: - x1 = XVECEXP (x0, 0, 3); - if (GET_CODE (x1) == CLOBBER && 1) - goto L383; - goto ret0; - - L383: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, SImode)) - { - ro[4] = x2; - if (TARGET_80387) - return 72; - } - goto ret0; - - L363: - x1 = XVECEXP (x0, 0, 0); - switch (GET_CODE (x1)) - { - case SET: - goto L364; - case CALL: - goto L1165; - } - goto ret0; - - L364: - x2 = XEXP (x1, 0); - if (GET_MODE (x2) == SImode && general_operand (x2, SImode)) - { - ro[0] = x2; - goto L365; - } - goto ret0; - - L365: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == FIX && 1) - goto L366; - goto ret0; - - L366: - x3 = XEXP (x2, 0); - if (GET_CODE (x3) != FIX) - goto ret0; - switch (GET_MODE (x3)) - { - case DFmode: - goto L367; - case SFmode: - goto L389; - } - goto ret0; - - L367: - x4 = XEXP (x3, 0); - if (register_operand (x4, DFmode)) - { - ro[1] = x4; - goto L368; - } - goto ret0; - - L368: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L369; - goto ret0; - - L369: - x2 = XEXP (x1, 0); - if (memory_operand (x2, SImode)) - { - ro[2] = x2; - goto L370; - } - goto ret0; - - L370: - x1 = XVECEXP (x0, 0, 2); - if (GET_CODE (x1) == CLOBBER && 1) - goto L371; - goto ret0; - - L371: - x2 = XEXP (x1, 0); - if (pnum_clobbers != 0 && memory_operand (x2, SImode)) - { - ro[3] = x2; - if (TARGET_80387) - { - *pnum_clobbers = 1; - return 71; - } - } - goto ret0; - - L389: - x4 = XEXP (x3, 0); - if (register_operand (x4, SFmode)) - { - ro[1] = x4; - goto L390; - } - goto ret0; - - L390: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == CLOBBER && 1) - goto L391; - goto ret0; - - L391: - x2 = XEXP (x1, 0); - if (memory_operand (x2, SImode)) - { - ro[2] = x2; - goto L392; - } - goto ret0; - - L392: - x1 = XVECEXP (x0, 0, 2); - if (GET_CODE (x1) == CLOBBER && 1) - goto L393; - goto ret0; - - L393: - x2 = XEXP (x1, 0); - if (pnum_clobbers != 0 && memory_operand (x2, SImode)) - { - ro[3] = x2; - if (TARGET_80387) - { - *pnum_clobbers = 1; - return 72; - } - } - goto ret0; - - L1165: - x2 = XEXP (x1, 0); - if (GET_MODE (x2) == QImode && GET_CODE (x2) == MEM && 1) - goto L1166; - L1159: - if (call_insn_operand (x2, QImode)) - { - ro[0] = x2; - goto L1160; - } - goto ret0; - - L1166: - x3 = XEXP (x2, 0); - if (symbolic_operand (x3, SImode)) - { - ro[0] = x3; - goto L1167; - } - goto L1159; - - L1167: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) - goto L1168; - x2 = XEXP (x1, 0); - goto L1159; - - L1168: - x1 = XVECEXP (x0, 0, 1); - if (memory_operand (x1, DImode)) - { - ro[1] = x1; - goto L1169; - } - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1159; - - L1169: - x1 = XVECEXP (x0, 0, 2); - ro[2] = x1; - if (!HALF_PIC_P ()) - return 239; - x1 = XVECEXP (x0, 0, 0); - x2 = XEXP (x1, 0); - goto L1159; - - L1160: - x2 = XEXP (x1, 1); - if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) - goto L1161; - goto ret0; - - L1161: - x1 = XVECEXP (x0, 0, 1); - if (memory_operand (x1, DImode)) - { - ro[1] = x1; - goto L1162; - } - goto ret0; - - L1162: - x1 = XVECEXP (x0, 0, 2); - ro[2] = x1; - return 238; - - L1175: - x1 = XVECEXP (x0, 0, 0); - if (GET_CODE (x1) == SET && 1) - goto L1176; - goto ret0; - - L1176: - x2 = XEXP (x1, 0); - switch (GET_MODE (x2)) - { - case BLKmode: - if (GET_CODE (x2) == MEM && 1) - goto L1177; - break; - case SImode: - if (general_operand (x2, SImode)) - { - ro[0] = x2; - goto L1193; - } - } - if (GET_CODE (x2) == CC0 && 1) - goto L1211; - goto ret0; - - L1177: - x3 = XEXP (x2, 0); - if (address_operand (x3, SImode)) - { - ro[0] = x3; - goto L1178; - } - goto ret0; - - L1178: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == BLKmode && GET_CODE (x2) == MEM && 1) - goto L1179; - goto ret0; - - L1179: - x3 = XEXP (x2, 0); - if (address_operand (x3, SImode)) - { - ro[1] = x3; - goto L1180; - } - goto ret0; - - L1180: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == USE && 1) - goto L1181; - goto ret0; - - L1181: - x2 = XEXP (x1, 0); - if (GET_CODE (x2) == CONST_INT && 1) - { - ro[2] = x2; - goto L1182; - } - goto ret0; - - L1182: - x1 = XVECEXP (x0, 0, 2); - if (GET_CODE (x1) == USE && 1) - goto L1183; - goto ret0; - - L1183: - x2 = XEXP (x1, 0); - if (immediate_operand (x2, SImode)) - { - ro[3] = x2; - goto L1184; - } - goto ret0; - - L1184: - x1 = XVECEXP (x0, 0, 3); - if (GET_CODE (x1) == CLOBBER && 1) - goto L1185; - goto ret0; - - L1185: - x2 = XEXP (x1, 0); - if (scratch_operand (x2, SImode)) - { - ro[4] = x2; - goto L1186; - } - goto ret0; - - L1186: - x1 = XVECEXP (x0, 0, 4); - if (GET_CODE (x1) == CLOBBER && 1) - goto L1187; - goto ret0; - - L1187: - x2 = XEXP (x1, 0); - if (rtx_equal_p (x2, ro[0]) && 1) - goto L1188; - goto ret0; - - L1188: - x1 = XVECEXP (x0, 0, 5); - if (GET_CODE (x1) == CLOBBER && 1) - goto L1189; - goto ret0; - - L1189: - x2 = XEXP (x1, 0); - if (rtx_equal_p (x2, ro[1]) && 1) - return 245; - goto ret0; - - L1193: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == COMPARE && 1) - goto L1194; - goto ret0; - - L1194: - x3 = XEXP (x2, 0); - if (GET_MODE (x3) == BLKmode && GET_CODE (x3) == MEM && 1) - goto L1195; - goto ret0; - - L1195: - x4 = XEXP (x3, 0); - if (address_operand (x4, SImode)) - { - ro[1] = x4; - goto L1196; - } - goto ret0; - - L1196: - x3 = XEXP (x2, 1); - if (GET_MODE (x3) == BLKmode && GET_CODE (x3) == MEM && 1) - goto L1197; - goto ret0; - - L1197: - x4 = XEXP (x3, 0); - if (address_operand (x4, SImode)) - { - ro[2] = x4; - goto L1198; - } - goto ret0; - - L1198: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == USE && 1) - goto L1199; - goto ret0; - - L1199: - x2 = XEXP (x1, 0); - if (register_operand (x2, SImode)) - { - ro[3] = x2; - goto L1200; - } - goto ret0; - - L1200: - x1 = XVECEXP (x0, 0, 2); - if (GET_CODE (x1) == USE && 1) - goto L1201; - goto ret0; - - L1201: - x2 = XEXP (x1, 0); - if (immediate_operand (x2, SImode)) - { - ro[4] = x2; - goto L1202; - } - goto ret0; - - L1202: - x1 = XVECEXP (x0, 0, 3); - if (GET_CODE (x1) == CLOBBER && 1) - goto L1203; - goto ret0; - - L1203: - x2 = XEXP (x1, 0); - if (rtx_equal_p (x2, ro[1]) && 1) - goto L1204; - goto ret0; - - L1204: - x1 = XVECEXP (x0, 0, 4); - if (GET_CODE (x1) == CLOBBER && 1) - goto L1205; - goto ret0; - - L1205: - x2 = XEXP (x1, 0); - if (rtx_equal_p (x2, ro[2]) && 1) - goto L1206; - goto ret0; - - L1206: - x1 = XVECEXP (x0, 0, 5); - if (GET_CODE (x1) == CLOBBER && 1) - goto L1207; - goto ret0; - - L1207: - x2 = XEXP (x1, 0); - if (rtx_equal_p (x2, ro[3]) && 1) - return 247; - goto ret0; - - L1211: - x2 = XEXP (x1, 1); - if (GET_MODE (x2) == SImode && GET_CODE (x2) == COMPARE && 1) - goto L1212; - goto ret0; - - L1212: - x3 = XEXP (x2, 0); - if (GET_MODE (x3) == BLKmode && GET_CODE (x3) == MEM && 1) - goto L1213; - goto ret0; - - L1213: - x4 = XEXP (x3, 0); - if (address_operand (x4, SImode)) - { - ro[0] = x4; - goto L1214; - } - goto ret0; - - L1214: - x3 = XEXP (x2, 1); - if (GET_MODE (x3) == BLKmode && GET_CODE (x3) == MEM && 1) - goto L1215; - goto ret0; - - L1215: - x4 = XEXP (x3, 0); - if (address_operand (x4, SImode)) - { - ro[1] = x4; - goto L1216; - } - goto ret0; - - L1216: - x1 = XVECEXP (x0, 0, 1); - if (GET_CODE (x1) == USE && 1) - goto L1217; - goto ret0; - - L1217: - x2 = XEXP (x1, 0); - if (register_operand (x2, SImode)) - { - ro[2] = x2; - goto L1218; - } - goto ret0; - - L1218: - x1 = XVECEXP (x0, 0, 2); - if (GET_CODE (x1) == USE && 1) - goto L1219; - goto ret0; - - L1219: - x2 = XEXP (x1, 0); - if (immediate_operand (x2, SImode)) - { - ro[3] = x2; - goto L1220; - } - goto ret0; - - L1220: - x1 = XVECEXP (x0, 0, 3); - if (GET_CODE (x1) == CLOBBER && 1) - goto L1221; - goto ret0; - - L1221: - x2 = XEXP (x1, 0); - if (rtx_equal_p (x2, ro[0]) && 1) - goto L1222; - goto ret0; - - L1222: - x1 = XVECEXP (x0, 0, 4); - if (GET_CODE (x1) == CLOBBER && 1) - goto L1223; - goto ret0; - - L1223: - x2 = XEXP (x1, 0); - if (rtx_equal_p (x2, ro[1]) && 1) - goto L1224; - goto ret0; - - L1224: - x1 = XVECEXP (x0, 0, 5); - if (GET_CODE (x1) == CLOBBER && 1) - goto L1225; - goto ret0; - - L1225: - x2 = XEXP (x1, 0); - if (rtx_equal_p (x2, ro[2]) && 1) - return 248; - goto ret0; - - L1117: - x1 = XEXP (x0, 0); - if (call_insn_operand (x1, QImode)) - { - ro[0] = x1; - goto L1118; - } - L1120: - if (GET_MODE (x1) == QImode && GET_CODE (x1) == MEM && 1) - goto L1121; - goto ret0; - - L1118: - x1 = XEXP (x0, 1); - if (general_operand (x1, SImode)) - { - ro[1] = x1; - return 229; - } - x1 = XEXP (x0, 0); - goto L1120; - - L1121: - x2 = XEXP (x1, 0); - if (symbolic_operand (x2, SImode)) - { - ro[0] = x2; - goto L1122; - } - goto ret0; - - L1122: - x1 = XEXP (x0, 1); - if (general_operand (x1, SImode)) - { - ro[1] = x1; - if (!HALF_PIC_P ()) - return 230; - } - goto ret0; - ret0: return -1; -} - -rtx -split_insns (x0, insn) - register rtx x0; - rtx insn; -{ - register rtx *ro = &recog_operand[0]; - register rtx x1, x2, x3, x4, x5, x6; - rtx tem; - - goto ret0; - ret0: return 0; -} - diff --git a/gnu/usr.bin/cc/common/integrate.c b/gnu/usr.bin/cc/common/integrate.c deleted file mode 100644 index e18f69041e..0000000000 --- a/gnu/usr.bin/cc/common/integrate.c +++ /dev/null @@ -1,2898 +0,0 @@ -/* Procedure integration for GNU CC. - Copyright (C) 1988, 1991, 1993 Free Software Foundation, Inc. - Contributed by Michael Tiemann (tiemann@cygnus.com) - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include - -#include "config.h" -#include "rtl.h" -#include "tree.h" -#include "flags.h" -#include "insn-config.h" -#include "insn-flags.h" -#include "expr.h" -#include "output.h" -#include "integrate.h" -#include "real.h" -#include "function.h" - -#include "obstack.h" -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free - -extern struct obstack *function_maybepermanent_obstack; - -extern tree pushdecl (); -extern tree poplevel (); - -/* Similar, but round to the next highest integer that meets the - alignment. */ -#define CEIL_ROUND(VALUE,ALIGN) (((VALUE) + (ALIGN) - 1) & ~((ALIGN)- 1)) - -/* Default max number of insns a function can have and still be inline. - This is overridden on RISC machines. */ -#ifndef INTEGRATE_THRESHOLD -#define INTEGRATE_THRESHOLD(DECL) \ - (8 * (8 + list_length (DECL_ARGUMENTS (DECL)))) -#endif - -/* Save any constant pool constants in an insn. */ -static void save_constants (); - -/* Note when parameter registers are the destination of a SET. */ -static void note_modified_parmregs (); - -/* Copy an rtx for save_for_inline_copying. */ -static rtx copy_for_inline (); - -/* Make copies of MEMs in DECL_RTLs. */ -static void copy_decl_rtls (); - -static tree copy_decl_tree (); -static tree copy_decl_list (); - -static void integrate_parm_decls (); -static void integrate_decl_tree (); - -static void subst_constants (); - -/* Zero if the current function (whose FUNCTION_DECL is FNDECL) - is safe and reasonable to integrate into other functions. - Nonzero means value is a warning message with a single %s - for the function's name. */ - -char * -function_cannot_inline_p (fndecl) - register tree fndecl; -{ - register rtx insn; - tree last = tree_last (TYPE_ARG_TYPES (TREE_TYPE (fndecl))); - int max_insns = INTEGRATE_THRESHOLD (fndecl); - register int ninsns = 0; - register tree parms; - - /* No inlines with varargs. `grokdeclarator' gives a warning - message about that if `inline' is specified. This code - it put in to catch the volunteers. */ - if ((last && TREE_VALUE (last) != void_type_node) - || (DECL_ARGUMENTS (fndecl) && DECL_NAME (DECL_ARGUMENTS (fndecl)) - && ! strcmp (IDENTIFIER_POINTER (DECL_NAME (DECL_ARGUMENTS (fndecl))), - "__builtin_va_alist"))) - return "varargs function cannot be inline"; - - if (current_function_calls_alloca) - return "function using alloca cannot be inline"; - - if (current_function_contains_functions) - return "function with nested functions cannot be inline"; - - /* This restriction may be eliminated sometime soon. But for now, don't - worry about remapping the static chain. */ - if (current_function_needs_context) - return "nested function cannot be inline"; - - /* If its not even close, don't even look. */ - if (!DECL_INLINE (fndecl) && get_max_uid () > 3 * max_insns) - return "function too large to be inline"; - -#if 0 - /* Large stacks are OK now that inlined functions can share them. */ - /* Don't inline functions with large stack usage, - since they can make other recursive functions burn up stack. */ - if (!DECL_INLINE (fndecl) && get_frame_size () > 100) - return "function stack frame for inlining"; -#endif - -#if 0 - /* Don't inline functions which do not specify a function prototype and - have BLKmode argument or take the address of a parameter. */ - for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms)) - { - if (TYPE_MODE (TREE_TYPE (parms)) == BLKmode) - TREE_ADDRESSABLE (parms) = 1; - if (last == NULL_TREE && TREE_ADDRESSABLE (parms)) - return "no prototype, and parameter address used; cannot be inline"; - } -#endif - - /* We can't inline functions that return structures - the old-fashioned PCC way, copying into a static block. */ - if (current_function_returns_pcc_struct) - return "inline functions not supported for this return value type"; - - /* We can't inline functions that return structures of varying size. */ - if (int_size_in_bytes (TREE_TYPE (TREE_TYPE (fndecl))) < 0) - return "function with varying-size return value cannot be inline"; - - /* Cannot inline a function with a varying size argument. */ - for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms)) - if (int_size_in_bytes (TREE_TYPE (parms)) < 0) - return "function with varying-size parameter cannot be inline"; - - if (!DECL_INLINE (fndecl) && get_max_uid () > max_insns) - { - for (ninsns = 0, insn = get_first_nonparm_insn (); insn && ninsns < max_insns; - insn = NEXT_INSN (insn)) - { - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') - ninsns++; - } - - if (ninsns >= max_insns) - return "function too large to be inline"; - } - - /* We cannot inline this function if forced_labels is non-zero. This - implies that a label in this function was used as an initializer. - Because labels can not be duplicated, all labels in the function - will be renamed when it is inlined. However, there is no way to find - and fix all variables initialized with addresses of labels in this - function, hence inlining is impossible. */ - - if (forced_labels) - return "function with label addresses used in initializers cannot inline"; - - return 0; -} - -/* Variables used within save_for_inline. */ - -/* Mapping from old pseudo-register to new pseudo-registers. - The first element of this map is reg_map[FIRST_PSEUDO_REGISTER]. - It is allocated in `save_for_inline' and `expand_inline_function', - and deallocated on exit from each of those routines. */ -static rtx *reg_map; - -/* Mapping from old code-labels to new code-labels. - The first element of this map is label_map[min_labelno]. - It is allocated in `save_for_inline' and `expand_inline_function', - and deallocated on exit from each of those routines. */ -static rtx *label_map; - -/* Mapping from old insn uid's to copied insns. - It is allocated in `save_for_inline' and `expand_inline_function', - and deallocated on exit from each of those routines. */ -static rtx *insn_map; - -/* Map pseudo reg number into the PARM_DECL for the parm living in the reg. - Zero for a reg that isn't a parm's home. - Only reg numbers less than max_parm_reg are mapped here. */ -static tree *parmdecl_map; - -/* Keep track of first pseudo-register beyond those that are parms. */ -static int max_parm_reg; - -/* When an insn is being copied by copy_for_inline, - this is nonzero if we have copied an ASM_OPERANDS. - In that case, it is the original input-operand vector. */ -static rtvec orig_asm_operands_vector; - -/* When an insn is being copied by copy_for_inline, - this is nonzero if we have copied an ASM_OPERANDS. - In that case, it is the copied input-operand vector. */ -static rtvec copy_asm_operands_vector; - -/* Likewise, this is the copied constraints vector. */ -static rtvec copy_asm_constraints_vector; - -/* In save_for_inline, nonzero if past the parm-initialization insns. */ -static int in_nonparm_insns; - -/* Subroutine for `save_for_inline{copying,nocopy}'. Performs initialization - needed to save FNDECL's insns and info for future inline expansion. */ - -static rtx -initialize_for_inline (fndecl, min_labelno, max_labelno, max_reg, copy) - tree fndecl; - int min_labelno; - int max_labelno; - int max_reg; - int copy; -{ - int function_flags, i; - rtvec arg_vector; - tree parms; - - /* Compute the values of any flags we must restore when inlining this. */ - - function_flags - = (current_function_calls_alloca * FUNCTION_FLAGS_CALLS_ALLOCA - + current_function_calls_setjmp * FUNCTION_FLAGS_CALLS_SETJMP - + current_function_calls_longjmp * FUNCTION_FLAGS_CALLS_LONGJMP - + current_function_returns_struct * FUNCTION_FLAGS_RETURNS_STRUCT - + current_function_returns_pcc_struct * FUNCTION_FLAGS_RETURNS_PCC_STRUCT - + current_function_needs_context * FUNCTION_FLAGS_NEEDS_CONTEXT - + current_function_has_nonlocal_label * FUNCTION_FLAGS_HAS_NONLOCAL_LABEL - + current_function_returns_pointer * FUNCTION_FLAGS_RETURNS_POINTER - + current_function_uses_const_pool * FUNCTION_FLAGS_USES_CONST_POOL - + current_function_uses_pic_offset_table * FUNCTION_FLAGS_USES_PIC_OFFSET_TABLE); - - /* Clear out PARMDECL_MAP. It was allocated in the caller's frame. */ - bzero (parmdecl_map, max_parm_reg * sizeof (tree)); - arg_vector = rtvec_alloc (list_length (DECL_ARGUMENTS (fndecl))); - - for (parms = DECL_ARGUMENTS (fndecl), i = 0; - parms; - parms = TREE_CHAIN (parms), i++) - { - rtx p = DECL_RTL (parms); - - if (GET_CODE (p) == MEM && copy) - { - /* Copy the rtl so that modifications of the addresses - later in compilation won't affect this arg_vector. - Virtual register instantiation can screw the address - of the rtl. */ - rtx new = copy_rtx (p); - - /* Don't leave the old copy anywhere in this decl. */ - if (DECL_RTL (parms) == DECL_INCOMING_RTL (parms) - || (GET_CODE (DECL_RTL (parms)) == MEM - && GET_CODE (DECL_INCOMING_RTL (parms)) == MEM - && (XEXP (DECL_RTL (parms), 0) - == XEXP (DECL_INCOMING_RTL (parms), 0)))) - DECL_INCOMING_RTL (parms) = new; - DECL_RTL (parms) = new; - } - - RTVEC_ELT (arg_vector, i) = p; - - if (GET_CODE (p) == REG) - parmdecl_map[REGNO (p)] = parms; - /* This flag is cleared later - if the function ever modifies the value of the parm. */ - TREE_READONLY (parms) = 1; - } - - /* Assume we start out in the insns that set up the parameters. */ - in_nonparm_insns = 0; - - /* The list of DECL_SAVED_INSNS, starts off with a header which - contains the following information: - - the first insn of the function (not including the insns that copy - parameters into registers). - the first parameter insn of the function, - the first label used by that function, - the last label used by that function, - the highest register number used for parameters, - the total number of registers used, - the size of the incoming stack area for parameters, - the number of bytes popped on return, - the stack slot list, - some flags that are used to restore compiler globals, - the value of current_function_outgoing_args_size, - the original argument vector, - and the original DECL_INITIAL. */ - - return gen_inline_header_rtx (NULL_RTX, NULL_RTX, min_labelno, max_labelno, - max_parm_reg, max_reg, - current_function_args_size, - current_function_pops_args, - stack_slot_list, function_flags, - current_function_outgoing_args_size, - arg_vector, (rtx) DECL_INITIAL (fndecl)); -} - -/* Subroutine for `save_for_inline{copying,nocopy}'. Finishes up the - things that must be done to make FNDECL expandable as an inline function. - HEAD contains the chain of insns to which FNDECL will expand. */ - -static void -finish_inline (fndecl, head) - tree fndecl; - rtx head; -{ - NEXT_INSN (head) = get_first_nonparm_insn (); - FIRST_PARM_INSN (head) = get_insns (); - DECL_SAVED_INSNS (fndecl) = head; - DECL_FRAME_SIZE (fndecl) = get_frame_size (); - DECL_INLINE (fndecl) = 1; -} - -/* Adjust the BLOCK_END_NOTE pointers in a given copied DECL tree so that - they all point to the new (copied) rtxs. */ - -static void -adjust_copied_decl_tree (block) - register tree block; -{ - register tree subblock; - register rtx original_end; - - original_end = BLOCK_END_NOTE (block); - if (original_end) - { - BLOCK_END_NOTE (block) = (rtx) NOTE_SOURCE_FILE (original_end); - NOTE_SOURCE_FILE (original_end) = 0; - } - - /* Process all subblocks. */ - for (subblock = BLOCK_SUBBLOCKS (block); - subblock; - subblock = TREE_CHAIN (subblock)) - adjust_copied_decl_tree (subblock); -} - -/* Make the insns and PARM_DECLs of the current function permanent - and record other information in DECL_SAVED_INSNS to allow inlining - of this function in subsequent calls. - - This function is called when we are going to immediately compile - the insns for FNDECL. The insns in maybepermanent_obstack cannot be - modified by the compilation process, so we copy all of them to - new storage and consider the new insns to be the insn chain to be - compiled. Our caller (rest_of_compilation) saves the original - DECL_INITIAL and DECL_ARGUMENTS; here we copy them. */ - -void -save_for_inline_copying (fndecl) - tree fndecl; -{ - rtx first_insn, last_insn, insn; - rtx head, copy; - int max_labelno, min_labelno, i, len; - int max_reg; - int max_uid; - rtx first_nonparm_insn; - - /* Make and emit a return-label if we have not already done so. - Do this before recording the bounds on label numbers. */ - - if (return_label == 0) - { - return_label = gen_label_rtx (); - emit_label (return_label); - } - - /* Get some bounds on the labels and registers used. */ - - max_labelno = max_label_num (); - min_labelno = get_first_label_num (); - max_reg = max_reg_num (); - - /* Set up PARMDECL_MAP which maps pseudo-reg number to its PARM_DECL. - Later we set TREE_READONLY to 0 if the parm is modified inside the fn. - Also set up ARG_VECTOR, which holds the unmodified DECL_RTX values - for the parms, prior to elimination of virtual registers. - These values are needed for substituting parms properly. */ - - max_parm_reg = max_parm_reg_num (); - parmdecl_map = (tree *) alloca (max_parm_reg * sizeof (tree)); - - head = initialize_for_inline (fndecl, min_labelno, max_labelno, max_reg, 1); - - if (current_function_uses_const_pool) - { - /* Replace any constant pool references with the actual constant. We - will put the constants back in the copy made below. */ - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') - { - save_constants (&PATTERN (insn)); - if (REG_NOTES (insn)) - save_constants (®_NOTES (insn)); - } - - /* Clear out the constant pool so that we can recreate it with the - copied constants below. */ - init_const_rtx_hash_table (); - clear_const_double_mem (); - } - - max_uid = INSN_UID (head); - - /* We have now allocated all that needs to be allocated permanently - on the rtx obstack. Set our high-water mark, so that we - can free the rest of this when the time comes. */ - - preserve_data (); - - /* Copy the chain insns of this function. - Install the copied chain as the insns of this function, - for continued compilation; - the original chain is recorded as the DECL_SAVED_INSNS - for inlining future calls. */ - - /* If there are insns that copy parms from the stack into pseudo registers, - those insns are not copied. `expand_inline_function' must - emit the correct code to handle such things. */ - - insn = get_insns (); - if (GET_CODE (insn) != NOTE) - abort (); - first_insn = rtx_alloc (NOTE); - NOTE_SOURCE_FILE (first_insn) = NOTE_SOURCE_FILE (insn); - NOTE_LINE_NUMBER (first_insn) = NOTE_LINE_NUMBER (insn); - INSN_UID (first_insn) = INSN_UID (insn); - PREV_INSN (first_insn) = NULL; - NEXT_INSN (first_insn) = NULL; - last_insn = first_insn; - - /* Each pseudo-reg in the old insn chain must have a unique rtx in the copy. - Make these new rtx's now, and install them in regno_reg_rtx, so they - will be the official pseudo-reg rtx's for the rest of compilation. */ - - reg_map = (rtx *) alloca ((max_reg + 1) * sizeof (rtx)); - - len = sizeof (struct rtx_def) + (GET_RTX_LENGTH (REG) - 1) * sizeof (rtunion); - for (i = max_reg - 1; i > LAST_VIRTUAL_REGISTER; i--) - reg_map[i] = (rtx)obstack_copy (function_maybepermanent_obstack, - regno_reg_rtx[i], len); - - bcopy (reg_map + LAST_VIRTUAL_REGISTER + 1, - regno_reg_rtx + LAST_VIRTUAL_REGISTER + 1, - (max_reg - (LAST_VIRTUAL_REGISTER + 1)) * sizeof (rtx)); - - /* Likewise each label rtx must have a unique rtx as its copy. */ - - label_map = (rtx *)alloca ((max_labelno - min_labelno) * sizeof (rtx)); - label_map -= min_labelno; - - for (i = min_labelno; i < max_labelno; i++) - label_map[i] = gen_label_rtx (); - - /* Record the mapping of old insns to copied insns. */ - - insn_map = (rtx *) alloca (max_uid * sizeof (rtx)); - bzero (insn_map, max_uid * sizeof (rtx)); - - /* Get the insn which signals the end of parameter setup code. */ - first_nonparm_insn = get_first_nonparm_insn (); - - /* Copy any entries in regno_reg_rtx or DECL_RTLs that reference MEM - (the former occurs when a variable has its address taken) - since these may be shared and can be changed by virtual - register instantiation. DECL_RTL values for our arguments - have already been copied by initialize_for_inline. */ - for (i = LAST_VIRTUAL_REGISTER + 1; i < max_reg; i++) - if (GET_CODE (regno_reg_rtx[i]) == MEM) - XEXP (regno_reg_rtx[i], 0) - = copy_for_inline (XEXP (regno_reg_rtx[i], 0)); - - /* Copy the tree of subblocks of the function, and the decls in them. - We will use the copy for compiling this function, then restore the original - subblocks and decls for use when inlining this function. - - Several parts of the compiler modify BLOCK trees. In particular, - instantiate_virtual_regs will instantiate any virtual regs - mentioned in the DECL_RTLs of the decls, and loop - unrolling will replicate any BLOCK trees inside an unrolled loop. - - The modified subblocks or DECL_RTLs would be incorrect for the original rtl - which we will use for inlining. The rtl might even contain pseudoregs - whose space has been freed. */ - - DECL_INITIAL (fndecl) = copy_decl_tree (DECL_INITIAL (fndecl)); - DECL_ARGUMENTS (fndecl) = copy_decl_list (DECL_ARGUMENTS (fndecl)); - - /* Now copy each DECL_RTL which is a MEM, - so it is safe to modify their addresses. */ - copy_decl_rtls (DECL_INITIAL (fndecl)); - - /* The fndecl node acts as its own progenitor, so mark it as such. */ - DECL_ABSTRACT_ORIGIN (fndecl) = fndecl; - - /* Now copy the chain of insns. Do this twice. The first copy the insn - itself and its body. The second time copy of REG_NOTES. This is because - a REG_NOTE may have a forward pointer to another insn. */ - - for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn)) - { - orig_asm_operands_vector = 0; - - if (insn == first_nonparm_insn) - in_nonparm_insns = 1; - - switch (GET_CODE (insn)) - { - case NOTE: - /* No need to keep these. */ - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED) - continue; - - copy = rtx_alloc (NOTE); - NOTE_LINE_NUMBER (copy) = NOTE_LINE_NUMBER (insn); - if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_BLOCK_END) - NOTE_SOURCE_FILE (copy) = NOTE_SOURCE_FILE (insn); - else - { - NOTE_SOURCE_FILE (insn) = (char *) copy; - NOTE_SOURCE_FILE (copy) = 0; - } - break; - - case INSN: - case CALL_INSN: - case JUMP_INSN: - copy = rtx_alloc (GET_CODE (insn)); - PATTERN (copy) = copy_for_inline (PATTERN (insn)); - INSN_CODE (copy) = -1; - LOG_LINKS (copy) = NULL; - RTX_INTEGRATED_P (copy) = RTX_INTEGRATED_P (insn); - break; - - case CODE_LABEL: - copy = label_map[CODE_LABEL_NUMBER (insn)]; - LABEL_NAME (copy) = LABEL_NAME (insn); - break; - - case BARRIER: - copy = rtx_alloc (BARRIER); - break; - - default: - abort (); - } - INSN_UID (copy) = INSN_UID (insn); - insn_map[INSN_UID (insn)] = copy; - NEXT_INSN (last_insn) = copy; - PREV_INSN (copy) = last_insn; - last_insn = copy; - } - - adjust_copied_decl_tree (DECL_INITIAL (fndecl)); - - /* Now copy the REG_NOTES. */ - for (insn = NEXT_INSN (get_insns ()); insn; insn = NEXT_INSN (insn)) - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && insn_map[INSN_UID(insn)]) - REG_NOTES (insn_map[INSN_UID (insn)]) - = copy_for_inline (REG_NOTES (insn)); - - NEXT_INSN (last_insn) = NULL; - - finish_inline (fndecl, head); - - set_new_first_and_last_insn (first_insn, last_insn); -} - -/* Return a copy of a chain of nodes, chained through the TREE_CHAIN field. - For example, this can copy a list made of TREE_LIST nodes. While copying, - for each node copied which doesn't already have is DECL_ABSTRACT_ORIGIN - set to some non-zero value, set the DECL_ABSTRACT_ORIGIN of the copy to - point to the corresponding (abstract) original node. */ - -static tree -copy_decl_list (list) - tree list; -{ - tree head; - register tree prev, next; - - if (list == 0) - return 0; - - head = prev = copy_node (list); - if (DECL_ABSTRACT_ORIGIN (head) == NULL_TREE) - DECL_ABSTRACT_ORIGIN (head) = list; - next = TREE_CHAIN (list); - while (next) - { - register tree copy; - - copy = copy_node (next); - if (DECL_ABSTRACT_ORIGIN (copy) == NULL_TREE) - DECL_ABSTRACT_ORIGIN (copy) = next; - TREE_CHAIN (prev) = copy; - prev = copy; - next = TREE_CHAIN (next); - } - return head; -} - -/* Make a copy of the entire tree of blocks BLOCK, and return it. */ - -static tree -copy_decl_tree (block) - tree block; -{ - tree t, vars, subblocks; - - vars = copy_decl_list (BLOCK_VARS (block)); - subblocks = 0; - - /* Process all subblocks. */ - for (t = BLOCK_SUBBLOCKS (block); t; t = TREE_CHAIN (t)) - { - tree copy = copy_decl_tree (t); - TREE_CHAIN (copy) = subblocks; - subblocks = copy; - } - - t = copy_node (block); - BLOCK_VARS (t) = vars; - BLOCK_SUBBLOCKS (t) = nreverse (subblocks); - /* If the BLOCK being cloned is already marked as having been instantiated - from something else, then leave that `origin' marking alone. Elsewise, - mark the clone as having originated from the BLOCK we are cloning. */ - if (BLOCK_ABSTRACT_ORIGIN (t) == NULL_TREE) - BLOCK_ABSTRACT_ORIGIN (t) = block; - return t; -} - -/* Copy DECL_RTLs in all decls in the given BLOCK node. */ - -static void -copy_decl_rtls (block) - tree block; -{ - tree t; - - for (t = BLOCK_VARS (block); t; t = TREE_CHAIN (t)) - if (DECL_RTL (t) && GET_CODE (DECL_RTL (t)) == MEM) - DECL_RTL (t) = copy_for_inline (DECL_RTL (t)); - - /* Process all subblocks. */ - for (t = BLOCK_SUBBLOCKS (block); t; t = TREE_CHAIN (t)) - copy_decl_rtls (t); -} - -/* Make the insns and PARM_DECLs of the current function permanent - and record other information in DECL_SAVED_INSNS to allow inlining - of this function in subsequent calls. - - This routine need not copy any insns because we are not going - to immediately compile the insns in the insn chain. There - are two cases when we would compile the insns for FNDECL: - (1) when FNDECL is expanded inline, and (2) when FNDECL needs to - be output at the end of other compilation, because somebody took - its address. In the first case, the insns of FNDECL are copied - as it is expanded inline, so FNDECL's saved insns are not - modified. In the second case, FNDECL is used for the last time, - so modifying the rtl is not a problem. - - ??? Actually, we do not verify that FNDECL is not inline expanded - by other functions which must also be written down at the end - of compilation. We could set flag_no_inline to nonzero when - the time comes to write down such functions. */ - -void -save_for_inline_nocopy (fndecl) - tree fndecl; -{ - rtx insn; - rtx head, copy; - tree parms; - int max_labelno, min_labelno, i, len; - int max_reg; - int max_uid; - rtx first_nonparm_insn; - int function_flags; - - /* Set up PARMDECL_MAP which maps pseudo-reg number to its PARM_DECL. - Later we set TREE_READONLY to 0 if the parm is modified inside the fn. - Also set up ARG_VECTOR, which holds the unmodified DECL_RTX values - for the parms, prior to elimination of virtual registers. - These values are needed for substituting parms properly. */ - - max_parm_reg = max_parm_reg_num (); - parmdecl_map = (tree *) alloca (max_parm_reg * sizeof (tree)); - - /* Make and emit a return-label if we have not already done so. */ - - if (return_label == 0) - { - return_label = gen_label_rtx (); - emit_label (return_label); - } - - head = initialize_for_inline (fndecl, get_first_label_num (), - max_label_num (), max_reg_num (), 0); - - /* If there are insns that copy parms from the stack into pseudo registers, - those insns are not copied. `expand_inline_function' must - emit the correct code to handle such things. */ - - insn = get_insns (); - if (GET_CODE (insn) != NOTE) - abort (); - - /* Get the insn which signals the end of parameter setup code. */ - first_nonparm_insn = get_first_nonparm_insn (); - - /* Now just scan the chain of insns to see what happens to our - PARM_DECLs. If a PARM_DECL is used but never modified, we - can substitute its rtl directly when expanding inline (and - perform constant folding when its incoming value is constant). - Otherwise, we have to copy its value into a new register and track - the new register's life. */ - - for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn)) - { - if (insn == first_nonparm_insn) - in_nonparm_insns = 1; - - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') - { - if (current_function_uses_const_pool) - { - /* Replace any constant pool references with the actual constant. - We will put the constant back if we need to write the - function out after all. */ - save_constants (&PATTERN (insn)); - if (REG_NOTES (insn)) - save_constants (®_NOTES (insn)); - } - - /* Record what interesting things happen to our parameters. */ - note_stores (PATTERN (insn), note_modified_parmregs); - } - } - - /* We have now allocated all that needs to be allocated permanently - on the rtx obstack. Set our high-water mark, so that we - can free the rest of this when the time comes. */ - - preserve_data (); - - finish_inline (fndecl, head); -} - -/* Given PX, a pointer into an insn, search for references to the constant - pool. Replace each with a CONST that has the mode of the original - constant, contains the constant, and has RTX_INTEGRATED_P set. - Similarly, constant pool addresses not enclosed in a MEM are replaced - with an ADDRESS rtx which also gives the constant, mode, and has - RTX_INTEGRATED_P set. */ - -static void -save_constants (px) - rtx *px; -{ - rtx x; - int i, j; - - again: - x = *px; - - /* If this is a CONST_DOUBLE, don't try to fix things up in - CONST_DOUBLE_MEM, because this is an infinite recursion. */ - if (GET_CODE (x) == CONST_DOUBLE) - return; - else if (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == SYMBOL_REF - && CONSTANT_POOL_ADDRESS_P (XEXP (x,0))) - { - enum machine_mode const_mode = get_pool_mode (XEXP (x, 0)); - rtx new = gen_rtx (CONST, const_mode, get_pool_constant (XEXP (x, 0))); - RTX_INTEGRATED_P (new) = 1; - - /* If the MEM was in a different mode than the constant (perhaps we - were only looking at the low-order part), surround it with a - SUBREG so we can save both modes. */ - - if (GET_MODE (x) != const_mode) - { - new = gen_rtx (SUBREG, GET_MODE (x), new, 0); - RTX_INTEGRATED_P (new) = 1; - } - - *px = new; - save_constants (&XEXP (*px, 0)); - } - else if (GET_CODE (x) == SYMBOL_REF - && CONSTANT_POOL_ADDRESS_P (x)) - { - *px = gen_rtx (ADDRESS, get_pool_mode (x), get_pool_constant (x)); - save_constants (&XEXP (*px, 0)); - RTX_INTEGRATED_P (*px) = 1; - } - - else - { - char *fmt = GET_RTX_FORMAT (GET_CODE (x)); - int len = GET_RTX_LENGTH (GET_CODE (x)); - - for (i = len-1; i >= 0; i--) - { - switch (fmt[i]) - { - case 'E': - for (j = 0; j < XVECLEN (x, i); j++) - save_constants (&XVECEXP (x, i, j)); - break; - - case 'e': - if (XEXP (x, i) == 0) - continue; - if (i == 0) - { - /* Hack tail-recursion here. */ - px = &XEXP (x, 0); - goto again; - } - save_constants (&XEXP (x, i)); - break; - } - } - } -} - -/* Note whether a parameter is modified or not. */ - -static void -note_modified_parmregs (reg, x) - rtx reg; - rtx x; -{ - if (GET_CODE (reg) == REG && in_nonparm_insns - && REGNO (reg) < max_parm_reg - && REGNO (reg) >= FIRST_PSEUDO_REGISTER - && parmdecl_map[REGNO (reg)] != 0) - TREE_READONLY (parmdecl_map[REGNO (reg)]) = 0; -} - -/* Copy the rtx ORIG recursively, replacing pseudo-regs and labels - according to `reg_map' and `label_map'. The original rtl insns - will be saved for inlining; this is used to make a copy - which is used to finish compiling the inline function itself. - - If we find a "saved" constant pool entry, one which was replaced with - the value of the constant, convert it back to a constant pool entry. - Since the pool wasn't touched, this should simply restore the old - address. - - All other kinds of rtx are copied except those that can never be - changed during compilation. */ - -static rtx -copy_for_inline (orig) - rtx orig; -{ - register rtx x = orig; - register int i; - register enum rtx_code code; - register char *format_ptr; - - if (x == 0) - return x; - - code = GET_CODE (x); - - /* These types may be freely shared. */ - - switch (code) - { - case QUEUED: - case CONST_INT: - case SYMBOL_REF: - case PC: - case CC0: - return x; - - case CONST_DOUBLE: - /* We have to make a new CONST_DOUBLE to ensure that we account for - it correctly. Using the old CONST_DOUBLE_MEM data is wrong. */ - if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) - { - REAL_VALUE_TYPE d; - - REAL_VALUE_FROM_CONST_DOUBLE (d, x); - return immed_real_const_1 (d, GET_MODE (x)); - } - else - return immed_double_const (CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x), - VOIDmode); - - case CONST: - /* Get constant pool entry for constant in the pool. */ - if (RTX_INTEGRATED_P (x)) - return validize_mem (force_const_mem (GET_MODE (x), - copy_for_inline (XEXP (x, 0)))); - break; - - case SUBREG: - /* Get constant pool entry, but access in different mode. */ - if (RTX_INTEGRATED_P (x)) - { - rtx new - = force_const_mem (GET_MODE (SUBREG_REG (x)), - copy_for_inline (XEXP (SUBREG_REG (x), 0))); - - PUT_MODE (new, GET_MODE (x)); - return validize_mem (new); - } - break; - - case ADDRESS: - /* If not special for constant pool error. Else get constant pool - address. */ - if (! RTX_INTEGRATED_P (x)) - abort (); - - return XEXP (force_const_mem (GET_MODE (x), - copy_for_inline (XEXP (x, 0))), 0); - - case ASM_OPERANDS: - /* If a single asm insn contains multiple output operands - then it contains multiple ASM_OPERANDS rtx's that share operand 3. - We must make sure that the copied insn continues to share it. */ - if (orig_asm_operands_vector == XVEC (orig, 3)) - { - x = rtx_alloc (ASM_OPERANDS); - XSTR (x, 0) = XSTR (orig, 0); - XSTR (x, 1) = XSTR (orig, 1); - XINT (x, 2) = XINT (orig, 2); - XVEC (x, 3) = copy_asm_operands_vector; - XVEC (x, 4) = copy_asm_constraints_vector; - XSTR (x, 5) = XSTR (orig, 5); - XINT (x, 6) = XINT (orig, 6); - return x; - } - break; - - case MEM: - /* A MEM is usually allowed to be shared if its address is constant - or is a constant plus one of the special registers. - - We do not allow sharing of addresses that are either a special - register or the sum of a constant and a special register because - it is possible for unshare_all_rtl to copy the address, into memory - that won't be saved. Although the MEM can safely be shared, and - won't be copied there, the address itself cannot be shared, and may - need to be copied. - - There are also two exceptions with constants: The first is if the - constant is a LABEL_REF or the sum of the LABEL_REF - and an integer. This case can happen if we have an inline - function that supplies a constant operand to the call of another - inline function that uses it in a switch statement. In this case, - we will be replacing the LABEL_REF, so we have to replace this MEM - as well. - - The second case is if we have a (const (plus (address ..) ...)). - In that case we need to put back the address of the constant pool - entry. */ - - if (CONSTANT_ADDRESS_P (XEXP (x, 0)) - && GET_CODE (XEXP (x, 0)) != LABEL_REF - && ! (GET_CODE (XEXP (x, 0)) == CONST - && (GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS - && ((GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) - == LABEL_REF) - || (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) - == ADDRESS))))) - return x; - break; - - case LABEL_REF: - { - /* Must point to the new insn. */ - return gen_rtx (LABEL_REF, GET_MODE (orig), - label_map[CODE_LABEL_NUMBER (XEXP (orig, 0))]); - } - - case REG: - if (REGNO (x) > LAST_VIRTUAL_REGISTER) - return reg_map [REGNO (x)]; - else - return x; - - case SET: - /* If a parm that gets modified lives in a pseudo-reg, - clear its TREE_READONLY to prevent certain optimizations. */ - { - rtx dest = SET_DEST (x); - - while (GET_CODE (dest) == STRICT_LOW_PART - || GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SUBREG) - dest = XEXP (dest, 0); - - if (GET_CODE (dest) == REG - && REGNO (dest) < max_parm_reg - && REGNO (dest) >= FIRST_PSEUDO_REGISTER - && parmdecl_map[REGNO (dest)] != 0 - /* The insn to load an arg pseudo from a stack slot - does not count as modifying it. */ - && in_nonparm_insns) - TREE_READONLY (parmdecl_map[REGNO (dest)]) = 0; - } - break; - -#if 0 /* This is a good idea, but here is the wrong place for it. */ - /* Arrange that CONST_INTs always appear as the second operand - if they appear, and that `frame_pointer_rtx' or `arg_pointer_rtx' - always appear as the first. */ - case PLUS: - if (GET_CODE (XEXP (x, 0)) == CONST_INT - || (XEXP (x, 1) == frame_pointer_rtx - || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM - && XEXP (x, 1) == arg_pointer_rtx))) - { - rtx t = XEXP (x, 0); - XEXP (x, 0) = XEXP (x, 1); - XEXP (x, 1) = t; - } - break; -#endif - } - - /* Replace this rtx with a copy of itself. */ - - x = rtx_alloc (code); - bcopy (orig, x, (sizeof (*x) - sizeof (x->fld) - + sizeof (x->fld[0]) * GET_RTX_LENGTH (code))); - - /* Now scan the subexpressions recursively. - We can store any replaced subexpressions directly into X - since we know X is not shared! Any vectors in X - must be copied if X was copied. */ - - format_ptr = GET_RTX_FORMAT (code); - - for (i = 0; i < GET_RTX_LENGTH (code); i++) - { - switch (*format_ptr++) - { - case 'e': - XEXP (x, i) = copy_for_inline (XEXP (x, i)); - break; - - case 'u': - /* Change any references to old-insns to point to the - corresponding copied insns. */ - XEXP (x, i) = insn_map[INSN_UID (XEXP (x, i))]; - break; - - case 'E': - if (XVEC (x, i) != NULL && XVECLEN (x, i) != 0) - { - register int j; - - XVEC (x, i) = gen_rtvec_v (XVECLEN (x, i), &XVECEXP (x, i, 0)); - for (j = 0; j < XVECLEN (x, i); j++) - XVECEXP (x, i, j) - = copy_for_inline (XVECEXP (x, i, j)); - } - break; - } - } - - if (code == ASM_OPERANDS && orig_asm_operands_vector == 0) - { - orig_asm_operands_vector = XVEC (orig, 3); - copy_asm_operands_vector = XVEC (x, 3); - copy_asm_constraints_vector = XVEC (x, 4); - } - - return x; -} - -/* Unfortunately, we need a global copy of const_equiv map for communication - with a function called from note_stores. Be *very* careful that this - is used properly in the presence of recursion. */ - -rtx *global_const_equiv_map; - -#define FIXED_BASE_PLUS_P(X) \ - (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \ - && GET_CODE (XEXP (X, 0)) == REG \ - && REGNO (XEXP (X, 0)) >= FIRST_VIRTUAL_REGISTER \ - && REGNO (XEXP (X, 0)) <= LAST_VIRTUAL_REGISTER) - -/* Integrate the procedure defined by FNDECL. Note that this function - may wind up calling itself. Since the static variables are not - reentrant, we do not assign them until after the possibility - of recursion is eliminated. - - If IGNORE is nonzero, do not produce a value. - Otherwise store the value in TARGET if it is nonzero and that is convenient. - - Value is: - (rtx)-1 if we could not substitute the function - 0 if we substituted it and it does not produce a value - else an rtx for where the value is stored. */ - -rtx -expand_inline_function (fndecl, parms, target, ignore, type, structure_value_addr) - tree fndecl, parms; - rtx target; - int ignore; - tree type; - rtx structure_value_addr; -{ - tree formal, actual, block; - rtx header = DECL_SAVED_INSNS (fndecl); - rtx insns = FIRST_FUNCTION_INSN (header); - rtx parm_insns = FIRST_PARM_INSN (header); - tree *arg_trees; - rtx *arg_vals; - rtx insn; - int max_regno; - register int i; - int min_labelno = FIRST_LABELNO (header); - int max_labelno = LAST_LABELNO (header); - int nargs; - rtx local_return_label = 0; - rtx loc; - rtx temp; - struct inline_remap *map; - rtx cc0_insn = 0; - rtvec arg_vector = ORIGINAL_ARG_VECTOR (header); - - /* Allow for equivalences of the pseudos we make for virtual fp and ap. */ - max_regno = MAX_REGNUM (header) + 3; - if (max_regno < FIRST_PSEUDO_REGISTER) - abort (); - - nargs = list_length (DECL_ARGUMENTS (fndecl)); - - /* We expect PARMS to have the right length; don't crash if not. */ - if (list_length (parms) != nargs) - return (rtx) (HOST_WIDE_INT) -1; - /* Also check that the parms type match. Since the appropriate - conversions or default promotions have already been applied, - the machine modes should match exactly. */ - for (formal = DECL_ARGUMENTS (fndecl), - actual = parms; - formal; - formal = TREE_CHAIN (formal), - actual = TREE_CHAIN (actual)) - { - tree arg = TREE_VALUE (actual); - enum machine_mode mode = TYPE_MODE (DECL_ARG_TYPE (formal)); - if (mode != TYPE_MODE (TREE_TYPE (arg))) - return (rtx) (HOST_WIDE_INT) -1; - /* If they are block mode, the types should match exactly. - They don't match exactly if TREE_TYPE (FORMAL) == ERROR_MARK_NODE, - which could happen if the parameter has incomplete type. */ - if (mode == BLKmode && TREE_TYPE (arg) != TREE_TYPE (formal)) - return (rtx) (HOST_WIDE_INT) -1; - } - - /* Make a binding contour to keep inline cleanups called at - outer function-scope level from looking like they are shadowing - parameter declarations. */ - pushlevel (0); - - /* Make a fresh binding contour that we can easily remove. */ - pushlevel (0); - expand_start_bindings (0); - if (GET_CODE (parm_insns) == NOTE - && NOTE_LINE_NUMBER (parm_insns) > 0) - { - rtx note = emit_note (NOTE_SOURCE_FILE (parm_insns), - NOTE_LINE_NUMBER (parm_insns)); - if (note) - RTX_INTEGRATED_P (note) = 1; - } - - /* Expand the function arguments. Do this first so that any - new registers get created before we allocate the maps. */ - - arg_vals = (rtx *) alloca (nargs * sizeof (rtx)); - arg_trees = (tree *) alloca (nargs * sizeof (tree)); - - for (formal = DECL_ARGUMENTS (fndecl), actual = parms, i = 0; - formal; - formal = TREE_CHAIN (formal), actual = TREE_CHAIN (actual), i++) - { - /* Actual parameter, converted to the type of the argument within the - function. */ - tree arg = convert (TREE_TYPE (formal), TREE_VALUE (actual)); - /* Mode of the variable used within the function. */ - enum machine_mode mode = TYPE_MODE (TREE_TYPE (formal)); - /* Where parameter is located in the function. */ - rtx copy; - - /* Make sure this formal has some correspondence in the users code - * before emitting any line notes for it. */ - if (DECL_SOURCE_LINE (formal)) - { - rtx note = emit_note (DECL_SOURCE_FILE (formal), - DECL_SOURCE_LINE (formal)); - if (note) - RTX_INTEGRATED_P (note) = 1; - } - - arg_trees[i] = arg; - loc = RTVEC_ELT (arg_vector, i); - - /* If this is an object passed by invisible reference, we copy the - object into a stack slot and save its address. If this will go - into memory, we do nothing now. Otherwise, we just expand the - argument. */ - if (GET_CODE (loc) == MEM && GET_CODE (XEXP (loc, 0)) == REG - && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER) - { - rtx stack_slot - = assign_stack_temp (TYPE_MODE (TREE_TYPE (arg)), - int_size_in_bytes (TREE_TYPE (arg)), 1); - - store_expr (arg, stack_slot, 0); - - arg_vals[i] = XEXP (stack_slot, 0); - } - else if (GET_CODE (loc) != MEM) - { - if (GET_MODE (loc) != TYPE_MODE (TREE_TYPE (arg))) - /* The mode if LOC and ARG can differ if LOC was a variable - that had its mode promoted via PROMOTED_MODE. */ - arg_vals[i] = convert_to_mode (GET_MODE (loc), - expand_expr (arg, NULL_RTX, mode, - EXPAND_SUM), - TREE_UNSIGNED (TREE_TYPE (formal))); - else - arg_vals[i] = expand_expr (arg, NULL_RTX, mode, EXPAND_SUM); - } - else - arg_vals[i] = 0; - - if (arg_vals[i] != 0 - && (! TREE_READONLY (formal) - /* If the parameter is not read-only, copy our argument through - a register. Also, we cannot use ARG_VALS[I] if it overlaps - TARGET in any way. In the inline function, they will likely - be two different pseudos, and `safe_from_p' will make all - sorts of smart assumptions about their not conflicting. - But if ARG_VALS[I] overlaps TARGET, these assumptions are - wrong, so put ARG_VALS[I] into a fresh register. */ - || (target != 0 - && (GET_CODE (arg_vals[i]) == REG - || GET_CODE (arg_vals[i]) == SUBREG - || GET_CODE (arg_vals[i]) == MEM) - && reg_overlap_mentioned_p (arg_vals[i], target)))) - arg_vals[i] = copy_to_mode_reg (GET_MODE (loc), arg_vals[i]); - } - - /* Allocate the structures we use to remap things. */ - - map = (struct inline_remap *) alloca (sizeof (struct inline_remap)); - map->fndecl = fndecl; - - map->reg_map = (rtx *) alloca (max_regno * sizeof (rtx)); - bzero (map->reg_map, max_regno * sizeof (rtx)); - - map->label_map = (rtx *)alloca ((max_labelno - min_labelno) * sizeof (rtx)); - map->label_map -= min_labelno; - - map->insn_map = (rtx *) alloca (INSN_UID (header) * sizeof (rtx)); - bzero (map->insn_map, INSN_UID (header) * sizeof (rtx)); - map->min_insnno = 0; - map->max_insnno = INSN_UID (header); - - map->integrating = 1; - - /* const_equiv_map maps pseudos in our routine to constants, so it needs to - be large enough for all our pseudos. This is the number we are currently - using plus the number in the called routine, plus 15 for each arg, - five to compute the virtual frame pointer, and five for the return value. - This should be enough for most cases. We do not reference entries - outside the range of the map. - - ??? These numbers are quite arbitrary and were obtained by - experimentation. At some point, we should try to allocate the - table after all the parameters are set up so we an more accurately - estimate the number of pseudos we will need. */ - - map->const_equiv_map_size - = max_reg_num () + (max_regno - FIRST_PSEUDO_REGISTER) + 15 * nargs + 10; - - map->const_equiv_map - = (rtx *)alloca (map->const_equiv_map_size * sizeof (rtx)); - bzero (map->const_equiv_map, map->const_equiv_map_size * sizeof (rtx)); - - map->const_age_map - = (unsigned *)alloca (map->const_equiv_map_size * sizeof (unsigned)); - bzero (map->const_age_map, map->const_equiv_map_size * sizeof (unsigned)); - map->const_age = 0; - - /* Record the current insn in case we have to set up pointers to frame - and argument memory blocks. */ - map->insns_at_start = get_last_insn (); - - /* Update the outgoing argument size to allow for those in the inlined - function. */ - if (OUTGOING_ARGS_SIZE (header) > current_function_outgoing_args_size) - current_function_outgoing_args_size = OUTGOING_ARGS_SIZE (header); - - /* If the inline function needs to make PIC references, that means - that this function's PIC offset table must be used. */ - if (FUNCTION_FLAGS (header) & FUNCTION_FLAGS_USES_PIC_OFFSET_TABLE) - current_function_uses_pic_offset_table = 1; - - /* Process each argument. For each, set up things so that the function's - reference to the argument will refer to the argument being passed. - We only replace REG with REG here. Any simplifications are done - via const_equiv_map. - - We make two passes: In the first, we deal with parameters that will - be placed into registers, since we need to ensure that the allocated - register number fits in const_equiv_map. Then we store all non-register - parameters into their memory location. */ - - for (i = 0; i < nargs; i++) - { - rtx copy = arg_vals[i]; - - loc = RTVEC_ELT (arg_vector, i); - - /* There are three cases, each handled separately. */ - if (GET_CODE (loc) == MEM && GET_CODE (XEXP (loc, 0)) == REG - && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER) - { - /* This must be an object passed by invisible reference (it could - also be a variable-sized object, but we forbid inlining functions - with variable-sized arguments). COPY is the address of the - actual value (this computation will cause it to be copied). We - map that address for the register, noting the actual address as - an equivalent in case it can be substituted into the insns. */ - - if (GET_CODE (copy) != REG) - { - temp = copy_addr_to_reg (copy); - if (CONSTANT_P (copy) || FIXED_BASE_PLUS_P (copy)) - { - map->const_equiv_map[REGNO (temp)] = copy; - map->const_age_map[REGNO (temp)] = CONST_AGE_PARM; - } - copy = temp; - } - map->reg_map[REGNO (XEXP (loc, 0))] = copy; - } - else if (GET_CODE (loc) == MEM) - { - /* This is the case of a parameter that lives in memory. - It will live in the block we allocate in the called routine's - frame that simulates the incoming argument area. Do nothing - now; we will call store_expr later. */ - ; - } - else if (GET_CODE (loc) == REG) - { - /* This is the good case where the parameter is in a register. - If it is read-only and our argument is a constant, set up the - constant equivalence. - - If LOC is REG_USERVAR_P, the usual case, COPY must also have - that flag set if it is a register. */ - - if ((GET_CODE (copy) != REG && GET_CODE (copy) != SUBREG) - || (GET_CODE (copy) == REG && REG_USERVAR_P (loc) - && ! REG_USERVAR_P (copy))) - { - temp = copy_to_mode_reg (GET_MODE (loc), copy); - REG_USERVAR_P (temp) = REG_USERVAR_P (loc); - if (CONSTANT_P (copy) || FIXED_BASE_PLUS_P (copy)) - { - map->const_equiv_map[REGNO (temp)] = copy; - map->const_age_map[REGNO (temp)] = CONST_AGE_PARM; - } - copy = temp; - } - map->reg_map[REGNO (loc)] = copy; - } - else - abort (); - - /* Free any temporaries we made setting up this parameter. */ - free_temp_slots (); - } - - /* Now do the parameters that will be placed in memory. */ - - for (formal = DECL_ARGUMENTS (fndecl), i = 0; - formal; formal = TREE_CHAIN (formal), i++) - { - rtx copy = arg_vals[i]; - - loc = RTVEC_ELT (arg_vector, i); - - if (GET_CODE (loc) == MEM - /* Exclude case handled above. */ - && ! (GET_CODE (XEXP (loc, 0)) == REG - && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER)) - { - rtx note = emit_note (DECL_SOURCE_FILE (formal), - DECL_SOURCE_LINE (formal)); - if (note) - RTX_INTEGRATED_P (note) = 1; - - /* Compute the address in the area we reserved and store the - value there. */ - temp = copy_rtx_and_substitute (loc, map); - subst_constants (&temp, NULL_RTX, map); - apply_change_group (); - if (! memory_address_p (GET_MODE (temp), XEXP (temp, 0))) - temp = change_address (temp, VOIDmode, XEXP (temp, 0)); - store_expr (arg_trees[i], temp, 0); - - /* Free any temporaries we made setting up this parameter. */ - free_temp_slots (); - } - } - - /* Deal with the places that the function puts its result. - We are driven by what is placed into DECL_RESULT. - - Initially, we assume that we don't have anything special handling for - REG_FUNCTION_RETURN_VALUE_P. */ - - map->inline_target = 0; - loc = DECL_RTL (DECL_RESULT (fndecl)); - if (TYPE_MODE (type) == VOIDmode) - /* There is no return value to worry about. */ - ; - else if (GET_CODE (loc) == MEM) - { - if (! structure_value_addr || ! aggregate_value_p (DECL_RESULT (fndecl))) - abort (); - - /* Pass the function the address in which to return a structure value. - Note that a constructor can cause someone to call us with - STRUCTURE_VALUE_ADDR, but the initialization takes place - via the first parameter, rather than the struct return address. - - We have two cases: If the address is a simple register indirect, - use the mapping mechanism to point that register to our structure - return address. Otherwise, store the structure return value into - the place that it will be referenced from. */ - - if (GET_CODE (XEXP (loc, 0)) == REG) - { - temp = force_reg (Pmode, structure_value_addr); - map->reg_map[REGNO (XEXP (loc, 0))] = temp; - if (CONSTANT_P (structure_value_addr) - || (GET_CODE (structure_value_addr) == PLUS - && XEXP (structure_value_addr, 0) == virtual_stack_vars_rtx - && GET_CODE (XEXP (structure_value_addr, 1)) == CONST_INT)) - { - map->const_equiv_map[REGNO (temp)] = structure_value_addr; - map->const_age_map[REGNO (temp)] = CONST_AGE_PARM; - } - } - else - { - temp = copy_rtx_and_substitute (loc, map); - subst_constants (&temp, NULL_RTX, map); - apply_change_group (); - emit_move_insn (temp, structure_value_addr); - } - } - else if (ignore) - /* We will ignore the result value, so don't look at its structure. - Note that preparations for an aggregate return value - do need to be made (above) even if it will be ignored. */ - ; - else if (GET_CODE (loc) == REG) - { - /* The function returns an object in a register and we use the return - value. Set up our target for remapping. */ - - /* Machine mode function was declared to return. */ - enum machine_mode departing_mode = TYPE_MODE (type); - /* (Possibly wider) machine mode it actually computes - (for the sake of callers that fail to declare it right). */ - enum machine_mode arriving_mode - = TYPE_MODE (TREE_TYPE (DECL_RESULT (fndecl))); - rtx reg_to_map; - - /* Don't use MEMs as direct targets because on some machines - substituting a MEM for a REG makes invalid insns. - Let the combiner substitute the MEM if that is valid. */ - if (target == 0 || GET_CODE (target) != REG - || GET_MODE (target) != departing_mode) - target = gen_reg_rtx (departing_mode); - - /* If function's value was promoted before return, - avoid machine mode mismatch when we substitute INLINE_TARGET. - But TARGET is what we will return to the caller. */ - if (arriving_mode != departing_mode) - reg_to_map = gen_rtx (SUBREG, arriving_mode, target, 0); - else - reg_to_map = target; - - /* Usually, the result value is the machine's return register. - Sometimes it may be a pseudo. Handle both cases. */ - if (REG_FUNCTION_VALUE_P (loc)) - map->inline_target = reg_to_map; - else - map->reg_map[REGNO (loc)] = reg_to_map; - } - - /* Make new label equivalences for the labels in the called function. */ - for (i = min_labelno; i < max_labelno; i++) - map->label_map[i] = gen_label_rtx (); - - /* Perform postincrements before actually calling the function. */ - emit_queue (); - - /* Clean up stack so that variables might have smaller offsets. */ - do_pending_stack_adjust (); - - /* Save a copy of the location of const_equiv_map for mark_stores, called - via note_stores. */ - global_const_equiv_map = map->const_equiv_map; - - /* Now copy the insns one by one. Do this in two passes, first the insns and - then their REG_NOTES, just like save_for_inline. */ - - /* This loop is very similar to the loop in copy_loop_body in unroll.c. */ - - for (insn = insns; insn; insn = NEXT_INSN (insn)) - { - rtx copy, pattern; - - map->orig_asm_operands_vector = 0; - - switch (GET_CODE (insn)) - { - case INSN: - pattern = PATTERN (insn); - copy = 0; - if (GET_CODE (pattern) == USE - && GET_CODE (XEXP (pattern, 0)) == REG - && REG_FUNCTION_VALUE_P (XEXP (pattern, 0))) - /* The (USE (REG n)) at return from the function should - be ignored since we are changing (REG n) into - inline_target. */ - break; - - /* Ignore setting a function value that we don't want to use. */ - if (map->inline_target == 0 - && GET_CODE (pattern) == SET - && GET_CODE (SET_DEST (pattern)) == REG - && REG_FUNCTION_VALUE_P (SET_DEST (pattern))) - { - if (volatile_refs_p (SET_SRC (pattern))) - { - /* If we must not delete the source, - load it into a new temporary. */ - copy = emit_insn (copy_rtx_and_substitute (pattern, map)); - SET_DEST (PATTERN (copy)) - = gen_reg_rtx (GET_MODE (SET_DEST (PATTERN (copy)))); - } - else - break; - } - else - copy = emit_insn (copy_rtx_and_substitute (pattern, map)); - /* REG_NOTES will be copied later. */ - -#ifdef HAVE_cc0 - /* If this insn is setting CC0, it may need to look at - the insn that uses CC0 to see what type of insn it is. - In that case, the call to recog via validate_change will - fail. So don't substitute constants here. Instead, - do it when we emit the following insn. - - For example, see the pyr.md file. That machine has signed and - unsigned compares. The compare patterns must check the - following branch insn to see which what kind of compare to - emit. - - If the previous insn set CC0, substitute constants on it as - well. */ - if (sets_cc0_p (PATTERN (copy)) != 0) - cc0_insn = copy; - else - { - if (cc0_insn) - try_constants (cc0_insn, map); - cc0_insn = 0; - try_constants (copy, map); - } -#else - try_constants (copy, map); -#endif - break; - - case JUMP_INSN: - if (GET_CODE (PATTERN (insn)) == RETURN) - { - if (local_return_label == 0) - local_return_label = gen_label_rtx (); - pattern = gen_jump (local_return_label); - } - else - pattern = copy_rtx_and_substitute (PATTERN (insn), map); - - copy = emit_jump_insn (pattern); - -#ifdef HAVE_cc0 - if (cc0_insn) - try_constants (cc0_insn, map); - cc0_insn = 0; -#endif - try_constants (copy, map); - - /* If this used to be a conditional jump insn but whose branch - direction is now know, we must do something special. */ - if (condjump_p (insn) && ! simplejump_p (insn) && map->last_pc_value) - { -#ifdef HAVE_cc0 - /* The previous insn set cc0 for us. So delete it. */ - delete_insn (PREV_INSN (copy)); -#endif - - /* If this is now a no-op, delete it. */ - if (map->last_pc_value == pc_rtx) - { - delete_insn (copy); - copy = 0; - } - else - /* Otherwise, this is unconditional jump so we must put a - BARRIER after it. We could do some dead code elimination - here, but jump.c will do it just as well. */ - emit_barrier (); - } - break; - - case CALL_INSN: - pattern = copy_rtx_and_substitute (PATTERN (insn), map); - copy = emit_call_insn (pattern); - -#ifdef HAVE_cc0 - if (cc0_insn) - try_constants (cc0_insn, map); - cc0_insn = 0; -#endif - try_constants (copy, map); - - /* Be lazy and assume CALL_INSNs clobber all hard registers. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - map->const_equiv_map[i] = 0; - break; - - case CODE_LABEL: - copy = emit_label (map->label_map[CODE_LABEL_NUMBER (insn)]); - LABEL_NAME (copy) = LABEL_NAME (insn); - map->const_age++; - break; - - case BARRIER: - copy = emit_barrier (); - break; - - case NOTE: - /* It is important to discard function-end and function-beg notes, - so we have only one of each in the current function. - Also, NOTE_INSN_DELETED notes aren't useful (save_for_inline - deleted these in the copy used for continuing compilation, - not the copy used for inlining). */ - if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END - && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_BEG - && NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED) - copy = emit_note (NOTE_SOURCE_FILE (insn), NOTE_LINE_NUMBER (insn)); - else - copy = 0; - break; - - default: - abort (); - break; - } - - if (copy) - RTX_INTEGRATED_P (copy) = 1; - - map->insn_map[INSN_UID (insn)] = copy; - } - - /* Now copy the REG_NOTES. Increment const_age, so that only constants - from parameters can be substituted in. These are the only ones that - are valid across the entire function. */ - map->const_age++; - for (insn = insns; insn; insn = NEXT_INSN (insn)) - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && map->insn_map[INSN_UID (insn)] - && REG_NOTES (insn)) - { - rtx tem = copy_rtx_and_substitute (REG_NOTES (insn), map); - /* We must also do subst_constants, in case one of our parameters - has const type and constant value. */ - subst_constants (&tem, NULL_RTX, map); - apply_change_group (); - REG_NOTES (map->insn_map[INSN_UID (insn)]) = tem; - } - - if (local_return_label) - emit_label (local_return_label); - - /* Make copies of the decls of the symbols in the inline function, so that - the copies of the variables get declared in the current function. Set - up things so that lookup_static_chain knows that to interpret registers - in SAVE_EXPRs for TYPE_SIZEs as local. */ - - inline_function_decl = fndecl; - integrate_parm_decls (DECL_ARGUMENTS (fndecl), map, arg_vector); - integrate_decl_tree ((tree) ORIGINAL_DECL_INITIAL (header), 0, map); - inline_function_decl = 0; - - /* End the scope containing the copied formal parameter variables - and copied LABEL_DECLs. */ - - expand_end_bindings (getdecls (), 1, 1); - block = poplevel (1, 1, 0); - BLOCK_ABSTRACT_ORIGIN (block) = (DECL_ABSTRACT_ORIGIN (fndecl) == NULL - ? fndecl : DECL_ABSTRACT_ORIGIN (fndecl)); - poplevel (0, 0, 0); - emit_line_note (input_filename, lineno); - - if (structure_value_addr) - { - target = gen_rtx (MEM, TYPE_MODE (type), - memory_address (TYPE_MODE (type), structure_value_addr)); - MEM_IN_STRUCT_P (target) = 1; - } - return target; -} - -/* Given a chain of PARM_DECLs, ARGS, copy each decl into a VAR_DECL, - push all of those decls and give each one the corresponding home. */ - -static void -integrate_parm_decls (args, map, arg_vector) - tree args; - struct inline_remap *map; - rtvec arg_vector; -{ - register tree tail; - register int i; - - for (tail = args, i = 0; tail; tail = TREE_CHAIN (tail), i++) - { - register tree decl = build_decl (VAR_DECL, DECL_NAME (tail), - TREE_TYPE (tail)); - rtx new_decl_rtl - = copy_rtx_and_substitute (RTVEC_ELT (arg_vector, i), map); - - DECL_ARG_TYPE (decl) = DECL_ARG_TYPE (tail); - /* We really should be setting DECL_INCOMING_RTL to something reasonable - here, but that's going to require some more work. */ - /* DECL_INCOMING_RTL (decl) = ?; */ - /* These args would always appear unused, if not for this. */ - TREE_USED (decl) = 1; - /* Prevent warning for shadowing with these. */ - DECL_ABSTRACT_ORIGIN (decl) = tail; - pushdecl (decl); - /* Fully instantiate the address with the equivalent form so that the - debugging information contains the actual register, instead of the - virtual register. Do this by not passing an insn to - subst_constants. */ - subst_constants (&new_decl_rtl, NULL_RTX, map); - apply_change_group (); - DECL_RTL (decl) = new_decl_rtl; - } -} - -/* Given a BLOCK node LET, push decls and levels so as to construct in the - current function a tree of contexts isomorphic to the one that is given. - - LEVEL indicates how far down into the BLOCK tree is the node we are - currently traversing. It is always zero except for recursive calls. - - MAP, if nonzero, is a pointer to an inline_remap map which indicates how - registers used in the DECL_RTL field should be remapped. If it is zero, - no mapping is necessary. */ - -static void -integrate_decl_tree (let, level, map) - tree let; - int level; - struct inline_remap *map; -{ - tree t, node; - - if (level > 0) - pushlevel (0); - - for (t = BLOCK_VARS (let); t; t = TREE_CHAIN (t)) - { - tree d = build_decl (TREE_CODE (t), DECL_NAME (t), TREE_TYPE (t)); - DECL_SOURCE_LINE (d) = DECL_SOURCE_LINE (t); - DECL_SOURCE_FILE (d) = DECL_SOURCE_FILE (t); - if (DECL_RTL (t) != 0) - { - DECL_RTL (d) = copy_rtx_and_substitute (DECL_RTL (t), map); - /* Fully instantiate the address with the equivalent form so that the - debugging information contains the actual register, instead of the - virtual register. Do this by not passing an insn to - subst_constants. */ - subst_constants (&DECL_RTL (d), NULL_RTX, map); - apply_change_group (); - } - else if (DECL_RTL (t)) - DECL_RTL (d) = copy_rtx (DECL_RTL (t)); - DECL_EXTERNAL (d) = DECL_EXTERNAL (t); - TREE_STATIC (d) = TREE_STATIC (t); - TREE_PUBLIC (d) = TREE_PUBLIC (t); - TREE_CONSTANT (d) = TREE_CONSTANT (t); - TREE_ADDRESSABLE (d) = TREE_ADDRESSABLE (t); - TREE_READONLY (d) = TREE_READONLY (t); - TREE_SIDE_EFFECTS (d) = TREE_SIDE_EFFECTS (t); - /* These args would always appear unused, if not for this. */ - TREE_USED (d) = 1; - /* Prevent warning for shadowing with these. */ - DECL_ABSTRACT_ORIGIN (d) = t; - pushdecl (d); - } - - for (t = BLOCK_SUBBLOCKS (let); t; t = TREE_CHAIN (t)) - integrate_decl_tree (t, level + 1, map); - - if (level > 0) - { - node = poplevel (1, 0, 0); - if (node) - { - TREE_USED (node) = TREE_USED (let); - BLOCK_ABSTRACT_ORIGIN (node) = let; - } - } -} - -/* Create a new copy of an rtx. - Recursively copies the operands of the rtx, - except for those few rtx codes that are sharable. - - We always return an rtx that is similar to that incoming rtx, with the - exception of possibly changing a REG to a SUBREG or vice versa. No - rtl is ever emitted. - - Handle constants that need to be placed in the constant pool by - calling `force_const_mem'. */ - -rtx -copy_rtx_and_substitute (orig, map) - register rtx orig; - struct inline_remap *map; -{ - register rtx copy, temp; - register int i, j; - register RTX_CODE code; - register enum machine_mode mode; - register char *format_ptr; - int regno; - - if (orig == 0) - return 0; - - code = GET_CODE (orig); - mode = GET_MODE (orig); - - switch (code) - { - case REG: - /* If the stack pointer register shows up, it must be part of - stack-adjustments (*not* because we eliminated the frame pointer!). - Small hard registers are returned as-is. Pseudo-registers - go through their `reg_map'. */ - regno = REGNO (orig); - if (regno <= LAST_VIRTUAL_REGISTER) - { - /* Some hard registers are also mapped, - but others are not translated. */ - if (map->reg_map[regno] != 0) - return map->reg_map[regno]; - - /* If this is the virtual frame pointer, make space in current - function's stack frame for the stack frame of the inline function. - - Copy the address of this area into a pseudo. Map - virtual_stack_vars_rtx to this pseudo and set up a constant - equivalence for it to be the address. This will substitute the - address into insns where it can be substituted and use the new - pseudo where it can't. */ - if (regno == VIRTUAL_STACK_VARS_REGNUM) - { - rtx loc, seq; - int size = DECL_FRAME_SIZE (map->fndecl); - int rounded; - - start_sequence (); - loc = assign_stack_temp (BLKmode, size, 1); - loc = XEXP (loc, 0); -#ifdef FRAME_GROWS_DOWNWARD - /* In this case, virtual_stack_vars_rtx points to one byte - higher than the top of the frame area. So compute the offset - to one byte higher than our substitute frame. - Keep the fake frame pointer aligned like a real one. */ - rounded = CEIL_ROUND (size, BIGGEST_ALIGNMENT / BITS_PER_UNIT); - loc = plus_constant (loc, rounded); -#endif - map->reg_map[regno] = temp - = force_reg (Pmode, force_operand (loc, NULL_RTX)); - map->const_equiv_map[REGNO (temp)] = loc; - map->const_age_map[REGNO (temp)] = CONST_AGE_PARM; - - seq = gen_sequence (); - end_sequence (); - emit_insn_after (seq, map->insns_at_start); - return temp; - } - else if (regno == VIRTUAL_INCOMING_ARGS_REGNUM) - { - /* Do the same for a block to contain any arguments referenced - in memory. */ - rtx loc, seq; - int size = FUNCTION_ARGS_SIZE (DECL_SAVED_INSNS (map->fndecl)); - - start_sequence (); - loc = assign_stack_temp (BLKmode, size, 1); - loc = XEXP (loc, 0); - /* When arguments grow downward, the virtual incoming - args pointer points to the top of the argument block, - so the remapped location better do the same. */ -#ifdef ARGS_GROW_DOWNWARD - loc = plus_constant (loc, size); -#endif - map->reg_map[regno] = temp - = force_reg (Pmode, force_operand (loc, NULL_RTX)); - map->const_equiv_map[REGNO (temp)] = loc; - map->const_age_map[REGNO (temp)] = CONST_AGE_PARM; - - seq = gen_sequence (); - end_sequence (); - emit_insn_after (seq, map->insns_at_start); - return temp; - } - else if (REG_FUNCTION_VALUE_P (orig)) - { - /* This is a reference to the function return value. If - the function doesn't have a return value, error. If the - mode doesn't agree, make a SUBREG. */ - if (map->inline_target == 0) - /* Must be unrolling loops or replicating code if we - reach here, so return the register unchanged. */ - return orig; - else if (mode != GET_MODE (map->inline_target)) - return gen_lowpart (mode, map->inline_target); - else - return map->inline_target; - } - return orig; - } - if (map->reg_map[regno] == NULL) - { - map->reg_map[regno] = gen_reg_rtx (mode); - REG_USERVAR_P (map->reg_map[regno]) = REG_USERVAR_P (orig); - REG_LOOP_TEST_P (map->reg_map[regno]) = REG_LOOP_TEST_P (orig); - RTX_UNCHANGING_P (map->reg_map[regno]) = RTX_UNCHANGING_P (orig); - /* A reg with REG_FUNCTION_VALUE_P true will never reach here. */ - } - return map->reg_map[regno]; - - case SUBREG: - copy = copy_rtx_and_substitute (SUBREG_REG (orig), map); - /* SUBREG is ordinary, but don't make nested SUBREGs. */ - if (GET_CODE (copy) == SUBREG) - return gen_rtx (SUBREG, GET_MODE (orig), SUBREG_REG (copy), - SUBREG_WORD (orig) + SUBREG_WORD (copy)); - else - return gen_rtx (SUBREG, GET_MODE (orig), copy, - SUBREG_WORD (orig)); - - case USE: - case CLOBBER: - /* USE and CLOBBER are ordinary, but we convert (use (subreg foo)) - to (use foo) if the original insn didn't have a subreg. - Removing the subreg distorts the VAX movstrhi pattern - by changing the mode of an operand. */ - copy = copy_rtx_and_substitute (XEXP (orig, 0), map); - if (GET_CODE (copy) == SUBREG && GET_CODE (XEXP (orig, 0)) != SUBREG) - copy = SUBREG_REG (copy); - return gen_rtx (code, VOIDmode, copy); - - case CODE_LABEL: - LABEL_PRESERVE_P (map->label_map[CODE_LABEL_NUMBER (orig)]) - = LABEL_PRESERVE_P (orig); - return map->label_map[CODE_LABEL_NUMBER (orig)]; - - case LABEL_REF: - copy = rtx_alloc (LABEL_REF); - PUT_MODE (copy, mode); - XEXP (copy, 0) = map->label_map[CODE_LABEL_NUMBER (XEXP (orig, 0))]; - LABEL_OUTSIDE_LOOP_P (copy) = LABEL_OUTSIDE_LOOP_P (orig); - return copy; - - case PC: - case CC0: - case CONST_INT: - return orig; - - case SYMBOL_REF: - /* Symbols which represent the address of a label stored in the constant - pool must be modified to point to a constant pool entry for the - remapped label. Otherwise, symbols are returned unchanged. */ - if (CONSTANT_POOL_ADDRESS_P (orig)) - { - rtx constant = get_pool_constant (orig); - if (GET_CODE (constant) == LABEL_REF) - { - copy = rtx_alloc (LABEL_REF); - PUT_MODE (copy, mode); - XEXP (copy, 0) - = map->label_map[CODE_LABEL_NUMBER (XEXP (constant, 0))]; - LABEL_OUTSIDE_LOOP_P (copy) = LABEL_OUTSIDE_LOOP_P (orig); - copy = force_const_mem (Pmode, copy); - return XEXP (copy, 0); - } - } - return orig; - - case CONST_DOUBLE: - /* We have to make a new copy of this CONST_DOUBLE because don't want - to use the old value of CONST_DOUBLE_MEM. Also, this may be a - duplicate of a CONST_DOUBLE we have already seen. */ - if (GET_MODE_CLASS (GET_MODE (orig)) == MODE_FLOAT) - { - REAL_VALUE_TYPE d; - - REAL_VALUE_FROM_CONST_DOUBLE (d, orig); - return immed_real_const_1 (d, GET_MODE (orig)); - } - else - return immed_double_const (CONST_DOUBLE_LOW (orig), - CONST_DOUBLE_HIGH (orig), VOIDmode); - - case CONST: - /* Make new constant pool entry for a constant - that was in the pool of the inline function. */ - if (RTX_INTEGRATED_P (orig)) - { - /* If this was an address of a constant pool entry that itself - had to be placed in the constant pool, it might not be a - valid address. So the recursive call below might turn it - into a register. In that case, it isn't a constant any - more, so return it. This has the potential of changing a - MEM into a REG, but we'll assume that it safe. */ - temp = copy_rtx_and_substitute (XEXP (orig, 0), map); - if (! CONSTANT_P (temp)) - return temp; - return validize_mem (force_const_mem (GET_MODE (orig), temp)); - } - break; - - case ADDRESS: - /* If from constant pool address, make new constant pool entry and - return its address. */ - if (! RTX_INTEGRATED_P (orig)) - abort (); - - temp = force_const_mem (GET_MODE (orig), - copy_rtx_and_substitute (XEXP (orig, 0), map)); - -#if 0 - /* Legitimizing the address here is incorrect. - - The only ADDRESS rtx's that can reach here are ones created by - save_constants. Hence the operand of the ADDRESS is always legal - in this position of the instruction, since the original rtx without - the ADDRESS was legal. - - The reason we don't legitimize the address here is that on the - Sparc, the caller may have a (high ...) surrounding this ADDRESS. - This code forces the operand of the address to a register, which - fails because we can not take the HIGH part of a register. - - Also, change_address may create new registers. These registers - will not have valid reg_map entries. This can cause try_constants() - to fail because assumes that all registers in the rtx have valid - reg_map entries, and it may end up replacing one of these new - registers with junk. */ - - if (! memory_address_p (GET_MODE (temp), XEXP (temp, 0))) - temp = change_address (temp, GET_MODE (temp), XEXP (temp, 0)); -#endif - - return XEXP (temp, 0); - - case ASM_OPERANDS: - /* If a single asm insn contains multiple output operands - then it contains multiple ASM_OPERANDS rtx's that share operand 3. - We must make sure that the copied insn continues to share it. */ - if (map->orig_asm_operands_vector == XVEC (orig, 3)) - { - copy = rtx_alloc (ASM_OPERANDS); - XSTR (copy, 0) = XSTR (orig, 0); - XSTR (copy, 1) = XSTR (orig, 1); - XINT (copy, 2) = XINT (orig, 2); - XVEC (copy, 3) = map->copy_asm_operands_vector; - XVEC (copy, 4) = map->copy_asm_constraints_vector; - XSTR (copy, 5) = XSTR (orig, 5); - XINT (copy, 6) = XINT (orig, 6); - return copy; - } - break; - - case CALL: - /* This is given special treatment because the first - operand of a CALL is a (MEM ...) which may get - forced into a register for cse. This is undesirable - if function-address cse isn't wanted or if we won't do cse. */ -#ifndef NO_FUNCTION_CSE - if (! (optimize && ! flag_no_function_cse)) -#endif - return gen_rtx (CALL, GET_MODE (orig), - gen_rtx (MEM, GET_MODE (XEXP (orig, 0)), - copy_rtx_and_substitute (XEXP (XEXP (orig, 0), 0), map)), - copy_rtx_and_substitute (XEXP (orig, 1), map)); - break; - -#if 0 - /* Must be ifdefed out for loop unrolling to work. */ - case RETURN: - abort (); -#endif - - case SET: - /* If this is setting fp or ap, it means that we have a nonlocal goto. - Don't alter that. - If the nonlocal goto is into the current function, - this will result in unnecessarily bad code, but should work. */ - if (SET_DEST (orig) == virtual_stack_vars_rtx - || SET_DEST (orig) == virtual_incoming_args_rtx) - return gen_rtx (SET, VOIDmode, SET_DEST (orig), - copy_rtx_and_substitute (SET_SRC (orig), map)); - break; - - case MEM: - copy = rtx_alloc (MEM); - PUT_MODE (copy, mode); - XEXP (copy, 0) = copy_rtx_and_substitute (XEXP (orig, 0), map); - MEM_IN_STRUCT_P (copy) = MEM_IN_STRUCT_P (orig); - MEM_VOLATILE_P (copy) = MEM_VOLATILE_P (orig); - - /* If doing function inlining, this MEM might not be const in the - function that it is being inlined into, and thus may not be - unchanging after function inlining. Constant pool references are - handled elsewhere, so this doesn't lose RTX_UNCHANGING_P bits - for them. */ - if (! map->integrating) - RTX_UNCHANGING_P (copy) = RTX_UNCHANGING_P (orig); - - return copy; - } - - copy = rtx_alloc (code); - PUT_MODE (copy, mode); - copy->in_struct = orig->in_struct; - copy->volatil = orig->volatil; - copy->unchanging = orig->unchanging; - - format_ptr = GET_RTX_FORMAT (GET_CODE (copy)); - - for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++) - { - switch (*format_ptr++) - { - case '0': - break; - - case 'e': - XEXP (copy, i) = copy_rtx_and_substitute (XEXP (orig, i), map); - break; - - case 'u': - /* Change any references to old-insns to point to the - corresponding copied insns. */ - XEXP (copy, i) = map->insn_map[INSN_UID (XEXP (orig, i))]; - break; - - case 'E': - XVEC (copy, i) = XVEC (orig, i); - if (XVEC (orig, i) != NULL && XVECLEN (orig, i) != 0) - { - XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i)); - for (j = 0; j < XVECLEN (copy, i); j++) - XVECEXP (copy, i, j) - = copy_rtx_and_substitute (XVECEXP (orig, i, j), map); - } - break; - - case 'w': - XWINT (copy, i) = XWINT (orig, i); - break; - - case 'i': - XINT (copy, i) = XINT (orig, i); - break; - - case 's': - XSTR (copy, i) = XSTR (orig, i); - break; - - default: - abort (); - } - } - - if (code == ASM_OPERANDS && map->orig_asm_operands_vector == 0) - { - map->orig_asm_operands_vector = XVEC (orig, 3); - map->copy_asm_operands_vector = XVEC (copy, 3); - map->copy_asm_constraints_vector = XVEC (copy, 4); - } - - return copy; -} - -/* Substitute known constant values into INSN, if that is valid. */ - -void -try_constants (insn, map) - rtx insn; - struct inline_remap *map; -{ - int i; - - map->num_sets = 0; - subst_constants (&PATTERN (insn), insn, map); - - /* Apply the changes if they are valid; otherwise discard them. */ - apply_change_group (); - - /* Show we don't know the value of anything stored or clobbered. */ - note_stores (PATTERN (insn), mark_stores); - map->last_pc_value = 0; -#ifdef HAVE_cc0 - map->last_cc0_value = 0; -#endif - - /* Set up any constant equivalences made in this insn. */ - for (i = 0; i < map->num_sets; i++) - { - if (GET_CODE (map->equiv_sets[i].dest) == REG) - { - int regno = REGNO (map->equiv_sets[i].dest); - - if (map->const_equiv_map[regno] == 0 - /* Following clause is a hack to make case work where GNU C++ - reassigns a variable to make cse work right. */ - || ! rtx_equal_p (map->const_equiv_map[regno], - map->equiv_sets[i].equiv)) - { - map->const_equiv_map[regno] = map->equiv_sets[i].equiv; - map->const_age_map[regno] = map->const_age; - } - } - else if (map->equiv_sets[i].dest == pc_rtx) - map->last_pc_value = map->equiv_sets[i].equiv; -#ifdef HAVE_cc0 - else if (map->equiv_sets[i].dest == cc0_rtx) - map->last_cc0_value = map->equiv_sets[i].equiv; -#endif - } -} - -/* Substitute known constants for pseudo regs in the contents of LOC, - which are part of INSN. - If INSN is zero, the substitution should always be done (this is used to - update DECL_RTL). - These changes are taken out by try_constants if the result is not valid. - - Note that we are more concerned with determining when the result of a SET - is a constant, for further propagation, than actually inserting constants - into insns; cse will do the latter task better. - - This function is also used to adjust address of items previously addressed - via the virtual stack variable or virtual incoming arguments registers. */ - -static void -subst_constants (loc, insn, map) - rtx *loc; - rtx insn; - struct inline_remap *map; -{ - rtx x = *loc; - register int i; - register enum rtx_code code; - register char *format_ptr; - int num_changes = num_validated_changes (); - rtx new = 0; - enum machine_mode op0_mode; - - code = GET_CODE (x); - - switch (code) - { - case PC: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case CONST: - case LABEL_REF: - case ADDRESS: - return; - -#ifdef HAVE_cc0 - case CC0: - validate_change (insn, loc, map->last_cc0_value, 1); - return; -#endif - - case USE: - case CLOBBER: - /* The only thing we can do with a USE or CLOBBER is possibly do - some substitutions in a MEM within it. */ - if (GET_CODE (XEXP (x, 0)) == MEM) - subst_constants (&XEXP (XEXP (x, 0), 0), insn, map); - return; - - case REG: - /* Substitute for parms and known constants. Don't replace - hard regs used as user variables with constants. */ - { - int regno = REGNO (x); - - if (! (regno < FIRST_PSEUDO_REGISTER && REG_USERVAR_P (x)) - && regno < map->const_equiv_map_size - && map->const_equiv_map[regno] != 0 - && map->const_age_map[regno] >= map->const_age) - validate_change (insn, loc, map->const_equiv_map[regno], 1); - return; - } - - case SUBREG: - /* SUBREG applied to something other than a reg - should be treated as ordinary, since that must - be a special hack and we don't know how to treat it specially. - Consider for example mulsidi3 in m68k.md. - Ordinary SUBREG of a REG needs this special treatment. */ - if (GET_CODE (SUBREG_REG (x)) == REG) - { - rtx inner = SUBREG_REG (x); - rtx new = 0; - - /* We can't call subst_constants on &SUBREG_REG (x) because any - constant or SUBREG wouldn't be valid inside our SUBEG. Instead, - see what is inside, try to form the new SUBREG and see if that is - valid. We handle two cases: extracting a full word in an - integral mode and extracting the low part. */ - subst_constants (&inner, NULL_RTX, map); - - if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT - && GET_MODE_SIZE (GET_MODE (x)) == UNITS_PER_WORD - && GET_MODE (SUBREG_REG (x)) != VOIDmode) - new = operand_subword (inner, SUBREG_WORD (x), 0, - GET_MODE (SUBREG_REG (x))); - - if (new == 0 && subreg_lowpart_p (x)) - new = gen_lowpart_common (GET_MODE (x), inner); - - if (new) - validate_change (insn, loc, new, 1); - - return; - } - break; - - case MEM: - subst_constants (&XEXP (x, 0), insn, map); - - /* If a memory address got spoiled, change it back. */ - if (insn != 0 && num_validated_changes () != num_changes - && !memory_address_p (GET_MODE (x), XEXP (x, 0))) - cancel_changes (num_changes); - return; - - case SET: - { - /* Substitute constants in our source, and in any arguments to a - complex (e..g, ZERO_EXTRACT) destination, but not in the destination - itself. */ - rtx *dest_loc = &SET_DEST (x); - rtx dest = *dest_loc; - rtx src, tem; - - subst_constants (&SET_SRC (x), insn, map); - src = SET_SRC (x); - - while (GET_CODE (*dest_loc) == ZERO_EXTRACT - /* By convention, we always use ZERO_EXTRACT in the dest. */ -/* || GET_CODE (*dest_loc) == SIGN_EXTRACT */ - || GET_CODE (*dest_loc) == SUBREG - || GET_CODE (*dest_loc) == STRICT_LOW_PART) - { - if (GET_CODE (*dest_loc) == ZERO_EXTRACT) - { - subst_constants (&XEXP (*dest_loc, 1), insn, map); - subst_constants (&XEXP (*dest_loc, 2), insn, map); - } - dest_loc = &XEXP (*dest_loc, 0); - } - - /* Do substitute in the address of a destination in memory. */ - if (GET_CODE (*dest_loc) == MEM) - subst_constants (&XEXP (*dest_loc, 0), insn, map); - - /* Check for the case of DEST a SUBREG, both it and the underlying - register are less than one word, and the SUBREG has the wider mode. - In the case, we are really setting the underlying register to the - source converted to the mode of DEST. So indicate that. */ - if (GET_CODE (dest) == SUBREG - && GET_MODE_SIZE (GET_MODE (dest)) <= UNITS_PER_WORD - && GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) <= UNITS_PER_WORD - && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) - <= GET_MODE_SIZE (GET_MODE (dest))) - && (tem = gen_lowpart_if_possible (GET_MODE (SUBREG_REG (dest)), - src))) - src = tem, dest = SUBREG_REG (dest); - - /* If storing a recognizable value save it for later recording. */ - if ((map->num_sets < MAX_RECOG_OPERANDS) - && (CONSTANT_P (src) - || (GET_CODE (src) == PLUS - && GET_CODE (XEXP (src, 0)) == REG - && REGNO (XEXP (src, 0)) >= FIRST_VIRTUAL_REGISTER - && REGNO (XEXP (src, 0)) <= LAST_VIRTUAL_REGISTER - && CONSTANT_P (XEXP (src, 1))) - || GET_CODE (src) == COMPARE -#ifdef HAVE_cc0 - || dest == cc0_rtx -#endif - || (dest == pc_rtx - && (src == pc_rtx || GET_CODE (src) == RETURN - || GET_CODE (src) == LABEL_REF)))) - { - /* Normally, this copy won't do anything. But, if SRC is a COMPARE - it will cause us to save the COMPARE with any constants - substituted, which is what we want for later. */ - map->equiv_sets[map->num_sets].equiv = copy_rtx (src); - map->equiv_sets[map->num_sets++].dest = dest; - } - - return; - } - } - - format_ptr = GET_RTX_FORMAT (code); - - /* If the first operand is an expression, save its mode for later. */ - if (*format_ptr == 'e') - op0_mode = GET_MODE (XEXP (x, 0)); - - for (i = 0; i < GET_RTX_LENGTH (code); i++) - { - switch (*format_ptr++) - { - case '0': - break; - - case 'e': - if (XEXP (x, i)) - subst_constants (&XEXP (x, i), insn, map); - break; - - case 'u': - case 'i': - case 's': - case 'w': - break; - - case 'E': - if (XVEC (x, i) != NULL && XVECLEN (x, i) != 0) - { - int j; - for (j = 0; j < XVECLEN (x, i); j++) - subst_constants (&XVECEXP (x, i, j), insn, map); - } - break; - - default: - abort (); - } - } - - /* If this is a commutative operation, move a constant to the second - operand unless the second operand is already a CONST_INT. */ - if ((GET_RTX_CLASS (code) == 'c' || code == NE || code == EQ) - && CONSTANT_P (XEXP (x, 0)) && GET_CODE (XEXP (x, 1)) != CONST_INT) - { - rtx tem = XEXP (x, 0); - validate_change (insn, &XEXP (x, 0), XEXP (x, 1), 1); - validate_change (insn, &XEXP (x, 1), tem, 1); - } - - /* Simplify the expression in case we put in some constants. */ - switch (GET_RTX_CLASS (code)) - { - case '1': - new = simplify_unary_operation (code, GET_MODE (x), - XEXP (x, 0), op0_mode); - break; - - case '<': - { - enum machine_mode op_mode = GET_MODE (XEXP (x, 0)); - if (op_mode == VOIDmode) - op_mode = GET_MODE (XEXP (x, 1)); - new = simplify_relational_operation (code, op_mode, - XEXP (x, 0), XEXP (x, 1)); -#ifdef FLOAT_STORE_FLAG_VALUE - if (new != 0 && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) - new = ((new == const0_rtx) ? CONST0_RTX (GET_MODE (x)) - : immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, GET_MODE (x))); -#endif - break; - } - - case '2': - case 'c': - new = simplify_binary_operation (code, GET_MODE (x), - XEXP (x, 0), XEXP (x, 1)); - break; - - case 'b': - case '3': - new = simplify_ternary_operation (code, GET_MODE (x), op0_mode, - XEXP (x, 0), XEXP (x, 1), XEXP (x, 2)); - break; - } - - if (new) - validate_change (insn, loc, new, 1); -} - -/* Show that register modified no longer contain known constants. We are - called from note_stores with parts of the new insn. */ - -void -mark_stores (dest, x) - rtx dest; - rtx x; -{ - int regno = -1; - enum machine_mode mode; - - /* DEST is always the innermost thing set, except in the case of - SUBREGs of hard registers. */ - - if (GET_CODE (dest) == REG) - regno = REGNO (dest), mode = GET_MODE (dest); - else if (GET_CODE (dest) == SUBREG && GET_CODE (SUBREG_REG (dest)) == REG) - { - regno = REGNO (SUBREG_REG (dest)) + SUBREG_WORD (dest); - mode = GET_MODE (SUBREG_REG (dest)); - } - - if (regno >= 0) - { - int last_reg = (regno >= FIRST_PSEUDO_REGISTER ? regno - : regno + HARD_REGNO_NREGS (regno, mode) - 1); - int i; - - for (i = regno; i <= last_reg; i++) - global_const_equiv_map[i] = 0; - } -} - -/* If any CONST expressions with RTX_INTEGRATED_P are present in the rtx - pointed to by PX, they represent constants in the constant pool. - Replace these with a new memory reference obtained from force_const_mem. - Similarly, ADDRESS expressions with RTX_INTEGRATED_P represent the - address of a constant pool entry. Replace them with the address of - a new constant pool entry obtained from force_const_mem. */ - -static void -restore_constants (px) - rtx *px; -{ - rtx x = *px; - int i, j; - char *fmt; - - if (x == 0) - return; - - if (GET_CODE (x) == CONST_DOUBLE) - { - /* We have to make a new CONST_DOUBLE to ensure that we account for - it correctly. Using the old CONST_DOUBLE_MEM data is wrong. */ - if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) - { - REAL_VALUE_TYPE d; - - REAL_VALUE_FROM_CONST_DOUBLE (d, x); - *px = immed_real_const_1 (d, GET_MODE (x)); - } - else - *px = immed_double_const (CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x), - VOIDmode); - } - - else if (RTX_INTEGRATED_P (x) && GET_CODE (x) == CONST) - { - restore_constants (&XEXP (x, 0)); - *px = validize_mem (force_const_mem (GET_MODE (x), XEXP (x, 0))); - } - else if (RTX_INTEGRATED_P (x) && GET_CODE (x) == SUBREG) - { - /* This must be (subreg/i:M1 (const/i:M2 ...) 0). */ - rtx new = XEXP (SUBREG_REG (x), 0); - - restore_constants (&new); - new = force_const_mem (GET_MODE (SUBREG_REG (x)), new); - PUT_MODE (new, GET_MODE (x)); - *px = validize_mem (new); - } - else if (RTX_INTEGRATED_P (x) && GET_CODE (x) == ADDRESS) - { - restore_constants (&XEXP (x, 0)); - *px = XEXP (force_const_mem (GET_MODE (x), XEXP (x, 0)), 0); - } - else - { - fmt = GET_RTX_FORMAT (GET_CODE (x)); - for (i = 0; i < GET_RTX_LENGTH (GET_CODE (x)); i++) - { - switch (*fmt++) - { - case 'E': - for (j = 0; j < XVECLEN (x, i); j++) - restore_constants (&XVECEXP (x, i, j)); - break; - - case 'e': - restore_constants (&XEXP (x, i)); - break; - } - } - } -} - -/* Given a pointer to some BLOCK node, if the BLOCK_ABSTRACT_ORIGIN for the - given BLOCK node is NULL, set the BLOCK_ABSTRACT_ORIGIN for the node so - that it points to the node itself, thus indicating that the node is its - own (abstract) origin. Additionally, if the BLOCK_ABSTRACT_ORIGIN for - the given node is NULL, recursively descend the decl/block tree which - it is the root of, and for each other ..._DECL or BLOCK node contained - therein whose DECL_ABSTRACT_ORIGINs or BLOCK_ABSTRACT_ORIGINs are also - still NULL, set *their* DECL_ABSTRACT_ORIGIN or BLOCK_ABSTRACT_ORIGIN - values to point to themselves. */ - -static void set_decl_origin_self (); - -static void -set_block_origin_self (stmt) - register tree stmt; -{ - if (BLOCK_ABSTRACT_ORIGIN (stmt) == NULL_TREE) - { - BLOCK_ABSTRACT_ORIGIN (stmt) = stmt; - - { - register tree local_decl; - - for (local_decl = BLOCK_VARS (stmt); - local_decl != NULL_TREE; - local_decl = TREE_CHAIN (local_decl)) - set_decl_origin_self (local_decl); /* Potential recursion. */ - } - - { - register tree subblock; - - for (subblock = BLOCK_SUBBLOCKS (stmt); - subblock != NULL_TREE; - subblock = BLOCK_CHAIN (subblock)) - set_block_origin_self (subblock); /* Recurse. */ - } - } -} - -/* Given a pointer to some ..._DECL node, if the DECL_ABSTRACT_ORIGIN for - the given ..._DECL node is NULL, set the DECL_ABSTRACT_ORIGIN for the - node to so that it points to the node itself, thus indicating that the - node represents its own (abstract) origin. Additionally, if the - DECL_ABSTRACT_ORIGIN for the given node is NULL, recursively descend - the decl/block tree of which the given node is the root of, and for - each other ..._DECL or BLOCK node contained therein whose - DECL_ABSTRACT_ORIGINs or BLOCK_ABSTRACT_ORIGINs are also still NULL, - set *their* DECL_ABSTRACT_ORIGIN or BLOCK_ABSTRACT_ORIGIN values to - point to themselves. */ - -static void -set_decl_origin_self (decl) - register tree decl; -{ - if (DECL_ABSTRACT_ORIGIN (decl) == NULL_TREE) - { - DECL_ABSTRACT_ORIGIN (decl) = decl; - if (TREE_CODE (decl) == FUNCTION_DECL) - { - register tree arg; - - for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg)) - DECL_ABSTRACT_ORIGIN (arg) = arg; - if (DECL_INITIAL (decl) != NULL_TREE) - set_block_origin_self (DECL_INITIAL (decl)); - } - } -} - -/* Given a pointer to some BLOCK node, and a boolean value to set the - "abstract" flags to, set that value into the BLOCK_ABSTRACT flag for - the given block, and for all local decls and all local sub-blocks - (recursively) which are contained therein. */ - -void set_decl_abstract_flags (); - -static void -set_block_abstract_flags (stmt, setting) - register tree stmt; - register int setting; -{ - BLOCK_ABSTRACT (stmt) = setting; - - { - register tree local_decl; - - for (local_decl = BLOCK_VARS (stmt); - local_decl != NULL_TREE; - local_decl = TREE_CHAIN (local_decl)) - set_decl_abstract_flags (local_decl, setting); - } - - { - register tree subblock; - - for (subblock = BLOCK_SUBBLOCKS (stmt); - subblock != NULL_TREE; - subblock = BLOCK_CHAIN (subblock)) - set_block_abstract_flags (subblock, setting); - } -} - -/* Given a pointer to some ..._DECL node, and a boolean value to set the - "abstract" flags to, set that value into the DECL_ABSTRACT flag for the - given decl, and (in the case where the decl is a FUNCTION_DECL) also - set the abstract flags for all of the parameters, local vars, local - blocks and sub-blocks (recursively) to the same setting. */ - -void -set_decl_abstract_flags (decl, setting) - register tree decl; - register int setting; -{ - DECL_ABSTRACT (decl) = setting; - if (TREE_CODE (decl) == FUNCTION_DECL) - { - register tree arg; - - for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg)) - DECL_ABSTRACT (arg) = setting; - if (DECL_INITIAL (decl) != NULL_TREE) - set_block_abstract_flags (DECL_INITIAL (decl), setting); - } -} - -/* Output the assembly language code for the function FNDECL - from its DECL_SAVED_INSNS. Used for inline functions that are output - at end of compilation instead of where they came in the source. */ - -void -output_inline_function (fndecl) - tree fndecl; -{ - rtx head = DECL_SAVED_INSNS (fndecl); - rtx last; - - temporary_allocation (); - - current_function_decl = fndecl; - - /* This call is only used to initialize global variables. */ - init_function_start (fndecl, "lossage", 1); - - /* Redo parameter determinations in case the FUNCTION_... - macros took machine-specific actions that need to be redone. */ - assign_parms (fndecl, 1); - - /* Set stack frame size. */ - assign_stack_local (BLKmode, DECL_FRAME_SIZE (fndecl), 0); - - restore_reg_data (FIRST_PARM_INSN (head)); - - stack_slot_list = STACK_SLOT_LIST (head); - - if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_CALLS_ALLOCA) - current_function_calls_alloca = 1; - - if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_CALLS_SETJMP) - current_function_calls_setjmp = 1; - - if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_CALLS_LONGJMP) - current_function_calls_longjmp = 1; - - if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_RETURNS_STRUCT) - current_function_returns_struct = 1; - - if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_RETURNS_PCC_STRUCT) - current_function_returns_pcc_struct = 1; - - if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_NEEDS_CONTEXT) - current_function_needs_context = 1; - - if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_HAS_NONLOCAL_LABEL) - current_function_has_nonlocal_label = 1; - - if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_RETURNS_POINTER) - current_function_returns_pointer = 1; - - if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_USES_CONST_POOL) - current_function_uses_const_pool = 1; - - if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_USES_PIC_OFFSET_TABLE) - current_function_uses_pic_offset_table = 1; - - current_function_outgoing_args_size = OUTGOING_ARGS_SIZE (head); - current_function_pops_args = POPS_ARGS (head); - - /* There is no need to output a return label again. */ - return_label = 0; - - expand_function_end (DECL_SOURCE_FILE (fndecl), DECL_SOURCE_LINE (fndecl)); - - /* Find last insn and rebuild the constant pool. */ - for (last = FIRST_PARM_INSN (head); - NEXT_INSN (last); last = NEXT_INSN (last)) - { - if (GET_RTX_CLASS (GET_CODE (last)) == 'i') - { - restore_constants (&PATTERN (last)); - restore_constants (®_NOTES (last)); - } - } - - set_new_first_and_last_insn (FIRST_PARM_INSN (head), last); - set_new_first_and_last_label_num (FIRST_LABELNO (head), LAST_LABELNO (head)); - - /* We must have already output DWARF debugging information for the - original (abstract) inline function declaration/definition, so - we want to make sure that the debugging information we generate - for this special instance of the inline function refers back to - the information we already generated. To make sure that happens, - we simply have to set the DECL_ABSTRACT_ORIGIN for the function - node (and for all of the local ..._DECL nodes which are its children) - so that they all point to themselves. */ - - set_decl_origin_self (fndecl); - - /* Compile this function all the way down to assembly code. */ - rest_of_compilation (fndecl); - - current_function_decl = 0; - - permanent_allocation (); -} diff --git a/gnu/usr.bin/cc/common/loop.c b/gnu/usr.bin/cc/common/loop.c deleted file mode 100644 index 357eee672d..0000000000 --- a/gnu/usr.bin/cc/common/loop.c +++ /dev/null @@ -1,6496 +0,0 @@ -/* Move constant computations out of loops. - Copyright (C) 1987, 1988, 1989, 1991, 1992 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This is the loop optimization pass of the compiler. - It finds invariant computations within loops and moves them - to the beginning of the loop. Then it identifies basic and - general induction variables. Strength reduction is applied to the general - induction variables, and induction variable elimination is applied to - the basic induction variables. - - It also finds cases where - a register is set within the loop by zero-extending a narrower value - and changes these to zero the entire register once before the loop - and merely copy the low part within the loop. - - Most of the complexity is in heuristics to decide when it is worth - while to do these things. */ - -#include -#include "config.h" -#include "rtl.h" -#include "obstack.h" -#include "expr.h" -#include "insn-config.h" -#include "insn-flags.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "recog.h" -#include "flags.h" -#include "real.h" -#include "loop.h" - -/* Vector mapping INSN_UIDs to luids. - The luids are like uids but increase monotonically always. - We use them to see whether a jump comes from outside a given loop. */ - -int *uid_luid; - -/* Indexed by INSN_UID, contains the ordinal giving the (innermost) loop - number the insn is contained in. */ - -int *uid_loop_num; - -/* 1 + largest uid of any insn. */ - -int max_uid_for_loop; - -/* 1 + luid of last insn. */ - -static int max_luid; - -/* Number of loops detected in current function. Used as index to the - next few tables. */ - -static int max_loop_num; - -/* Indexed by loop number, contains the first and last insn of each loop. */ - -static rtx *loop_number_loop_starts, *loop_number_loop_ends; - -/* For each loop, gives the containing loop number, -1 if none. */ - -int *loop_outer_loop; - -/* Indexed by loop number, contains a nonzero value if the "loop" isn't - really a loop (an insn outside the loop branches into it). */ - -static char *loop_invalid; - -/* Indexed by loop number, links together all LABEL_REFs which refer to - code labels outside the loop. Used by routines that need to know all - loop exits, such as final_biv_value and final_giv_value. - - This does not include loop exits due to return instructions. This is - because all bivs and givs are pseudos, and hence must be dead after a - return, so the presense of a return does not affect any of the - optimizations that use this info. It is simpler to just not include return - instructions on this list. */ - -rtx *loop_number_exit_labels; - -/* Holds the number of loop iterations. It is zero if the number could not be - calculated. Must be unsigned since the number of iterations can - be as high as 2^wordsize-1. For loops with a wider iterator, this number - will will be zero if the number of loop iterations is too large for an - unsigned integer to hold. */ - -unsigned HOST_WIDE_INT loop_n_iterations; - -/* Nonzero if there is a subroutine call in the current loop. - (unknown_address_altered is also nonzero in this case.) */ - -static int loop_has_call; - -/* Nonzero if there is a volatile memory reference in the current - loop. */ - -static int loop_has_volatile; - -/* Added loop_continue which is the NOTE_INSN_LOOP_CONT of the - current loop. A continue statement will generate a branch to - NEXT_INSN (loop_continue). */ - -static rtx loop_continue; - -/* Indexed by register number, contains the number of times the reg - is set during the loop being scanned. - During code motion, a negative value indicates a reg that has been - made a candidate; in particular -2 means that it is an candidate that - we know is equal to a constant and -1 means that it is an candidate - not known equal to a constant. - After code motion, regs moved have 0 (which is accurate now) - while the failed candidates have the original number of times set. - - Therefore, at all times, == 0 indicates an invariant register; - < 0 a conditionally invariant one. */ - -static short *n_times_set; - -/* Original value of n_times_set; same except that this value - is not set negative for a reg whose sets have been made candidates - and not set to 0 for a reg that is moved. */ - -static short *n_times_used; - -/* Index by register number, 1 indicates that the register - cannot be moved or strength reduced. */ - -static char *may_not_optimize; - -/* Nonzero means reg N has already been moved out of one loop. - This reduces the desire to move it out of another. */ - -static char *moved_once; - -/* Array of MEMs that are stored in this loop. If there are too many to fit - here, we just turn on unknown_address_altered. */ - -#define NUM_STORES 20 -static rtx loop_store_mems[NUM_STORES]; - -/* Index of first available slot in above array. */ -static int loop_store_mems_idx; - -/* Nonzero if we don't know what MEMs were changed in the current loop. - This happens if the loop contains a call (in which case `loop_has_call' - will also be set) or if we store into more than NUM_STORES MEMs. */ - -static int unknown_address_altered; - -/* Count of movable (i.e. invariant) instructions discovered in the loop. */ -static int num_movables; - -/* Count of memory write instructions discovered in the loop. */ -static int num_mem_sets; - -/* Number of loops contained within the current one, including itself. */ -static int loops_enclosed; - -/* Bound on pseudo register number before loop optimization. - A pseudo has valid regscan info if its number is < max_reg_before_loop. */ -int max_reg_before_loop; - -/* This obstack is used in product_cheap_p to allocate its rtl. It - may call gen_reg_rtx which, in turn, may reallocate regno_reg_rtx. - If we used the same obstack that it did, we would be deallocating - that array. */ - -static struct obstack temp_obstack; - -/* This is where the pointer to the obstack being used for RTL is stored. */ - -extern struct obstack *rtl_obstack; - -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free - -extern char *oballoc (); - -/* During the analysis of a loop, a chain of `struct movable's - is made to record all the movable insns found. - Then the entire chain can be scanned to decide which to move. */ - -struct movable -{ - rtx insn; /* A movable insn */ - rtx set_src; /* The expression this reg is set from. */ - rtx set_dest; /* The destination of this SET. */ - rtx dependencies; /* When INSN is libcall, this is an EXPR_LIST - of any registers used within the LIBCALL. */ - int consec; /* Number of consecutive following insns - that must be moved with this one. */ - int regno; /* The register it sets */ - short lifetime; /* lifetime of that register; - may be adjusted when matching movables - that load the same value are found. */ - short savings; /* Number of insns we can move for this reg, - including other movables that force this - or match this one. */ - unsigned int cond : 1; /* 1 if only conditionally movable */ - unsigned int force : 1; /* 1 means MUST move this insn */ - unsigned int global : 1; /* 1 means reg is live outside this loop */ - /* If PARTIAL is 1, GLOBAL means something different: - that the reg is live outside the range from where it is set - to the following label. */ - unsigned int done : 1; /* 1 inhibits further processing of this */ - - unsigned int partial : 1; /* 1 means this reg is used for zero-extending. - In particular, moving it does not make it - invariant. */ - unsigned int move_insn : 1; /* 1 means that we call emit_move_insn to - load SRC, rather than copying INSN. */ - unsigned int is_equiv : 1; /* 1 means a REG_EQUIV is present on INSN. */ - enum machine_mode savemode; /* Nonzero means it is a mode for a low part - that we should avoid changing when clearing - the rest of the reg. */ - struct movable *match; /* First entry for same value */ - struct movable *forces; /* An insn that must be moved if this is */ - struct movable *next; -}; - -FILE *loop_dump_stream; - -/* Forward declarations. */ - -static void find_and_verify_loops (); -static void mark_loop_jump (); -static void prescan_loop (); -static int reg_in_basic_block_p (); -static int consec_sets_invariant_p (); -static rtx libcall_other_reg (); -static int labels_in_range_p (); -static void count_loop_regs_set (); -static void note_addr_stored (); -static int loop_reg_used_before_p (); -static void scan_loop (); -static void replace_call_address (); -static rtx skip_consec_insns (); -static int libcall_benefit (); -static void ignore_some_movables (); -static void force_movables (); -static void combine_movables (); -static int rtx_equal_for_loop_p (); -static void move_movables (); -static void strength_reduce (); -static int valid_initial_value_p (); -static void find_mem_givs (); -static void record_biv (); -static void check_final_value (); -static void record_giv (); -static void update_giv_derive (); -static int basic_induction_var (); -static rtx simplify_giv_expr (); -static int general_induction_var (); -static int consec_sets_giv (); -static int check_dbra_loop (); -static rtx express_from (); -static int combine_givs_p (); -static void combine_givs (); -static int product_cheap_p (); -static int maybe_eliminate_biv (); -static int maybe_eliminate_biv_1 (); -static int last_use_this_basic_block (); -static void record_initial (); -static void update_reg_last_use (); - -/* Relative gain of eliminating various kinds of operations. */ -int add_cost; -#if 0 -int shift_cost; -int mult_cost; -#endif - -/* Benefit penalty, if a giv is not replaceable, i.e. must emit an insn to - copy the value of the strength reduced giv to its original register. */ -int copy_cost; - -void -init_loop () -{ - char *free_point = (char *) oballoc (1); - rtx reg = gen_rtx (REG, word_mode, 0); - rtx pow2 = GEN_INT (32); - rtx lea; - int i; - - add_cost = rtx_cost (gen_rtx (PLUS, word_mode, reg, reg), SET); - - /* We multiply by 2 to reconcile the difference in scale between - these two ways of computing costs. Otherwise the cost of a copy - will be far less than the cost of an add. */ - - copy_cost = 2 * 2; - - /* Free the objects we just allocated. */ - obfree (free_point); - - /* Initialize the obstack used for rtl in product_cheap_p. */ - gcc_obstack_init (&temp_obstack); -} - -/* Entry point of this file. Perform loop optimization - on the current function. F is the first insn of the function - and DUMPFILE is a stream for output of a trace of actions taken - (or 0 if none should be output). */ - -void -loop_optimize (f, dumpfile) - /* f is the first instruction of a chain of insns for one function */ - rtx f; - FILE *dumpfile; -{ - register rtx insn; - register int i; - rtx end; - rtx last_insn; - - loop_dump_stream = dumpfile; - - init_recog_no_volatile (); - init_alias_analysis (); - - max_reg_before_loop = max_reg_num (); - - moved_once = (char *) alloca (max_reg_before_loop); - bzero (moved_once, max_reg_before_loop); - - regs_may_share = 0; - - /* Count the number of loops. */ - - max_loop_num = 0; - for (insn = f; insn; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG) - max_loop_num++; - } - - /* Don't waste time if no loops. */ - if (max_loop_num == 0) - return; - - /* Get size to use for tables indexed by uids. - Leave some space for labels allocated by find_and_verify_loops. */ - max_uid_for_loop = get_max_uid () + 1 + max_loop_num * 32; - - uid_luid = (int *) alloca (max_uid_for_loop * sizeof (int)); - uid_loop_num = (int *) alloca (max_uid_for_loop * sizeof (int)); - - bzero (uid_luid, max_uid_for_loop * sizeof (int)); - bzero (uid_loop_num, max_uid_for_loop * sizeof (int)); - - /* Allocate tables for recording each loop. We set each entry, so they need - not be zeroed. */ - loop_number_loop_starts = (rtx *) alloca (max_loop_num * sizeof (rtx)); - loop_number_loop_ends = (rtx *) alloca (max_loop_num * sizeof (rtx)); - loop_outer_loop = (int *) alloca (max_loop_num * sizeof (int)); - loop_invalid = (char *) alloca (max_loop_num * sizeof (char)); - loop_number_exit_labels = (rtx *) alloca (max_loop_num * sizeof (rtx)); - - /* Find and process each loop. - First, find them, and record them in order of their beginnings. */ - find_and_verify_loops (f); - - /* Now find all register lifetimes. This must be done after - find_and_verify_loops, because it might reorder the insns in the - function. */ - reg_scan (f, max_reg_num (), 1); - - /* See if we went too far. */ - if (get_max_uid () > max_uid_for_loop) - abort (); - - /* Compute the mapping from uids to luids. - LUIDs are numbers assigned to insns, like uids, - except that luids increase monotonically through the code. - Don't assign luids to line-number NOTEs, so that the distance in luids - between two insns is not affected by -g. */ - - for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) - { - last_insn = insn; - if (GET_CODE (insn) != NOTE - || NOTE_LINE_NUMBER (insn) <= 0) - uid_luid[INSN_UID (insn)] = ++i; - else - /* Give a line number note the same luid as preceding insn. */ - uid_luid[INSN_UID (insn)] = i; - } - - max_luid = i + 1; - - /* Don't leave gaps in uid_luid for insns that have been - deleted. It is possible that the first or last insn - using some register has been deleted by cross-jumping. - Make sure that uid_luid for that former insn's uid - points to the general area where that insn used to be. */ - for (i = 0; i < max_uid_for_loop; i++) - { - uid_luid[0] = uid_luid[i]; - if (uid_luid[0] != 0) - break; - } - for (i = 0; i < max_uid_for_loop; i++) - if (uid_luid[i] == 0) - uid_luid[i] = uid_luid[i - 1]; - - /* Create a mapping from loops to BLOCK tree nodes. */ - if (flag_unroll_loops && write_symbols != NO_DEBUG) - find_loop_tree_blocks (); - - /* Now scan the loops, last ones first, since this means inner ones are done - before outer ones. */ - for (i = max_loop_num-1; i >= 0; i--) - if (! loop_invalid[i] && loop_number_loop_ends[i]) - scan_loop (loop_number_loop_starts[i], loop_number_loop_ends[i], - max_reg_num ()); - - /* If debugging and unrolling loops, we must replicate the tree nodes - corresponding to the blocks inside the loop, so that the original one - to one mapping will remain. */ - if (flag_unroll_loops && write_symbols != NO_DEBUG) - unroll_block_trees (); -} - -/* Optimize one loop whose start is LOOP_START and end is END. - LOOP_START is the NOTE_INSN_LOOP_BEG and END is the matching - NOTE_INSN_LOOP_END. */ - -/* ??? Could also move memory writes out of loops if the destination address - is invariant, the source is invariant, the memory write is not volatile, - and if we can prove that no read inside the loop can read this address - before the write occurs. If there is a read of this address after the - write, then we can also mark the memory read as invariant. */ - -static void -scan_loop (loop_start, end, nregs) - rtx loop_start, end; - int nregs; -{ - register int i; - register rtx p; - /* 1 if we are scanning insns that could be executed zero times. */ - int maybe_never = 0; - /* 1 if we are scanning insns that might never be executed - due to a subroutine call which might exit before they are reached. */ - int call_passed = 0; - /* For a rotated loop that is entered near the bottom, - this is the label at the top. Otherwise it is zero. */ - rtx loop_top = 0; - /* Jump insn that enters the loop, or 0 if control drops in. */ - rtx loop_entry_jump = 0; - /* Place in the loop where control enters. */ - rtx scan_start; - /* Number of insns in the loop. */ - int insn_count; - int in_libcall = 0; - int tem; - rtx temp; - /* The SET from an insn, if it is the only SET in the insn. */ - rtx set, set1; - /* Chain describing insns movable in current loop. */ - struct movable *movables = 0; - /* Last element in `movables' -- so we can add elements at the end. */ - struct movable *last_movable = 0; - /* Ratio of extra register life span we can justify - for saving an instruction. More if loop doesn't call subroutines - since in that case saving an insn makes more difference - and more registers are available. */ - int threshold; - /* If we have calls, contains the insn in which a register was used - if it was used exactly once; contains const0_rtx if it was used more - than once. */ - rtx *reg_single_usage = 0; - - n_times_set = (short *) alloca (nregs * sizeof (short)); - n_times_used = (short *) alloca (nregs * sizeof (short)); - may_not_optimize = (char *) alloca (nregs); - - /* Determine whether this loop starts with a jump down to a test at - the end. This will occur for a small number of loops with a test - that is too complex to duplicate in front of the loop. - - We search for the first insn or label in the loop, skipping NOTEs. - However, we must be careful not to skip past a NOTE_INSN_LOOP_BEG - (because we might have a loop executed only once that contains a - loop which starts with a jump to its exit test) or a NOTE_INSN_LOOP_END - (in case we have a degenerate loop). - - Note that if we mistakenly think that a loop is entered at the top - when, in fact, it is entered at the exit test, the only effect will be - slightly poorer optimization. Making the opposite error can generate - incorrect code. Since very few loops now start with a jump to the - exit test, the code here to detect that case is very conservative. */ - - for (p = NEXT_INSN (loop_start); - p != end - && GET_CODE (p) != CODE_LABEL && GET_RTX_CLASS (GET_CODE (p)) != 'i' - && (GET_CODE (p) != NOTE - || (NOTE_LINE_NUMBER (p) != NOTE_INSN_LOOP_BEG - && NOTE_LINE_NUMBER (p) != NOTE_INSN_LOOP_END)); - p = NEXT_INSN (p)) - ; - - scan_start = p; - - /* Set up variables describing this loop. */ - prescan_loop (loop_start, end); - threshold = (loop_has_call ? 1 : 2) * (1 + n_non_fixed_regs); - - /* If loop has a jump before the first label, - the true entry is the target of that jump. - Start scan from there. - But record in LOOP_TOP the place where the end-test jumps - back to so we can scan that after the end of the loop. */ - if (GET_CODE (p) == JUMP_INSN) - { - loop_entry_jump = p; - - /* Loop entry must be unconditional jump (and not a RETURN) */ - if (simplejump_p (p) - && JUMP_LABEL (p) != 0 - /* Check to see whether the jump actually - jumps out of the loop (meaning it's no loop). - This case can happen for things like - do {..} while (0). If this label was generated previously - by loop, we can't tell anything about it and have to reject - the loop. */ - && INSN_UID (JUMP_LABEL (p)) < max_uid_for_loop - && INSN_LUID (JUMP_LABEL (p)) >= INSN_LUID (loop_start) - && INSN_LUID (JUMP_LABEL (p)) < INSN_LUID (end)) - { - loop_top = next_label (scan_start); - scan_start = JUMP_LABEL (p); - } - } - - /* If SCAN_START was an insn created by loop, we don't know its luid - as required by loop_reg_used_before_p. So skip such loops. (This - test may never be true, but it's best to play it safe.) - - Also, skip loops where we do not start scanning at a label. This - test also rejects loops starting with a JUMP_INSN that failed the - test above. */ - - if (INSN_UID (scan_start) >= max_uid_for_loop - || GET_CODE (scan_start) != CODE_LABEL) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, "\nLoop from %d to %d is phony.\n\n", - INSN_UID (loop_start), INSN_UID (end)); - return; - } - - /* Count number of times each reg is set during this loop. - Set may_not_optimize[I] if it is not safe to move out - the setting of register I. If this loop has calls, set - reg_single_usage[I]. */ - - bzero (n_times_set, nregs * sizeof (short)); - bzero (may_not_optimize, nregs); - - if (loop_has_call) - { - reg_single_usage = (rtx *) alloca (nregs * sizeof (rtx)); - bzero (reg_single_usage, nregs * sizeof (rtx)); - } - - count_loop_regs_set (loop_top ? loop_top : loop_start, end, - may_not_optimize, reg_single_usage, &insn_count, nregs); - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - may_not_optimize[i] = 1, n_times_set[i] = 1; - bcopy (n_times_set, n_times_used, nregs * sizeof (short)); - - if (loop_dump_stream) - { - fprintf (loop_dump_stream, "\nLoop from %d to %d: %d real insns.\n", - INSN_UID (loop_start), INSN_UID (end), insn_count); - if (loop_continue) - fprintf (loop_dump_stream, "Continue at insn %d.\n", - INSN_UID (loop_continue)); - } - - /* Scan through the loop finding insns that are safe to move. - Set n_times_set negative for the reg being set, so that - this reg will be considered invariant for subsequent insns. - We consider whether subsequent insns use the reg - in deciding whether it is worth actually moving. - - MAYBE_NEVER is nonzero if we have passed a conditional jump insn - and therefore it is possible that the insns we are scanning - would never be executed. At such times, we must make sure - that it is safe to execute the insn once instead of zero times. - When MAYBE_NEVER is 0, all insns will be executed at least once - so that is not a problem. */ - - p = scan_start; - while (1) - { - p = NEXT_INSN (p); - /* At end of a straight-in loop, we are done. - At end of a loop entered at the bottom, scan the top. */ - if (p == scan_start) - break; - if (p == end) - { - if (loop_top != 0) - p = NEXT_INSN (loop_top); - else - break; - if (p == scan_start) - break; - } - - if (GET_RTX_CLASS (GET_CODE (p)) == 'i' - && find_reg_note (p, REG_LIBCALL, NULL_RTX)) - in_libcall = 1; - else if (GET_RTX_CLASS (GET_CODE (p)) == 'i' - && find_reg_note (p, REG_RETVAL, NULL_RTX)) - in_libcall = 0; - - if (GET_CODE (p) == INSN - && (set = single_set (p)) - && GET_CODE (SET_DEST (set)) == REG - && ! may_not_optimize[REGNO (SET_DEST (set))]) - { - int tem1 = 0; - int tem2 = 0; - int move_insn = 0; - rtx src = SET_SRC (set); - rtx dependencies = 0; - - /* Figure out what to use as a source of this insn. If a REG_EQUIV - note is given or if a REG_EQUAL note with a constant operand is - specified, use it as the source and mark that we should move - this insn by calling emit_move_insn rather that duplicating the - insn. - - Otherwise, only use the REG_EQUAL contents if a REG_RETVAL note - is present. */ - temp = find_reg_note (p, REG_EQUIV, NULL_RTX); - if (temp) - src = XEXP (temp, 0), move_insn = 1; - else - { - temp = find_reg_note (p, REG_EQUAL, NULL_RTX); - if (temp && CONSTANT_P (XEXP (temp, 0))) - src = XEXP (temp, 0), move_insn = 1; - if (temp && find_reg_note (p, REG_RETVAL, NULL_RTX)) - { - src = XEXP (temp, 0); - /* A libcall block can use regs that don't appear in - the equivalent expression. To move the libcall, - we must move those regs too. */ - dependencies = libcall_other_reg (p, src); - } - } - - /* Don't try to optimize a register that was made - by loop-optimization for an inner loop. - We don't know its life-span, so we can't compute the benefit. */ - if (REGNO (SET_DEST (set)) >= max_reg_before_loop) - ; - /* In order to move a register, we need to have one of three cases: - (1) it is used only in the same basic block as the set - (2) it is not a user variable and it is not used in the - exit test (this can cause the variable to be used - before it is set just like a user-variable). - (3) the set is guaranteed to be executed once the loop starts, - and the reg is not used until after that. */ - else if (! ((! maybe_never - && ! loop_reg_used_before_p (set, p, loop_start, - scan_start, end)) - || (! REG_USERVAR_P (SET_DEST (PATTERN (p))) - && ! REG_LOOP_TEST_P (SET_DEST (PATTERN (p)))) - || reg_in_basic_block_p (p, SET_DEST (PATTERN (p))))) - ; - else if ((tem = invariant_p (src)) - && (dependencies == 0 - || (tem2 = invariant_p (dependencies)) != 0) - && (n_times_set[REGNO (SET_DEST (set))] == 1 - || (tem1 - = consec_sets_invariant_p (SET_DEST (set), - n_times_set[REGNO (SET_DEST (set))], - p))) - /* If the insn can cause a trap (such as divide by zero), - can't move it unless it's guaranteed to be executed - once loop is entered. Even a function call might - prevent the trap insn from being reached - (since it might exit!) */ - && ! ((maybe_never || call_passed) - && may_trap_p (src))) - { - register struct movable *m; - register int regno = REGNO (SET_DEST (set)); - - /* A potential lossage is where we have a case where two insns - can be combined as long as they are both in the loop, but - we move one of them outside the loop. For large loops, - this can lose. The most common case of this is the address - of a function being called. - - Therefore, if this register is marked as being used exactly - once if we are in a loop with calls (a "large loop"), see if - we can replace the usage of this register with the source - of this SET. If we can, delete this insn. - - Don't do this if P has a REG_RETVAL note or if we have - SMALL_REGISTER_CLASSES and SET_SRC is a hard register. */ - - if (reg_single_usage && reg_single_usage[regno] != 0 - && reg_single_usage[regno] != const0_rtx - && regno_first_uid[regno] == INSN_UID (p) - && (regno_last_uid[regno] - == INSN_UID (reg_single_usage[regno])) - && n_times_set[REGNO (SET_DEST (set))] == 1 - && ! side_effects_p (SET_SRC (set)) - && ! find_reg_note (p, REG_RETVAL, NULL_RTX) -#ifdef SMALL_REGISTER_CLASSES - && ! (GET_CODE (SET_SRC (set)) == REG - && REGNO (SET_SRC (set)) < FIRST_PSEUDO_REGISTER) -#endif - /* This test is not redundant; SET_SRC (set) might be - a call-clobbered register and the life of REGNO - might span a call. */ - && ! modified_between_p (SET_SRC (set), p, - reg_single_usage[regno]) - && validate_replace_rtx (SET_DEST (set), SET_SRC (set), - reg_single_usage[regno])) - { - /* Replace any usage in a REG_EQUAL note. */ - REG_NOTES (reg_single_usage[regno]) - = replace_rtx (REG_NOTES (reg_single_usage[regno]), - SET_DEST (set), SET_SRC (set)); - - PUT_CODE (p, NOTE); - NOTE_LINE_NUMBER (p) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (p) = 0; - n_times_set[regno] = 0; - continue; - } - - m = (struct movable *) alloca (sizeof (struct movable)); - m->next = 0; - m->insn = p; - m->set_src = src; - m->dependencies = dependencies; - m->set_dest = SET_DEST (set); - m->force = 0; - m->consec = n_times_set[REGNO (SET_DEST (set))] - 1; - m->done = 0; - m->forces = 0; - m->partial = 0; - m->move_insn = move_insn; - m->is_equiv = (find_reg_note (p, REG_EQUIV, NULL_RTX) != 0); - m->savemode = VOIDmode; - m->regno = regno; - /* Set M->cond if either invariant_p or consec_sets_invariant_p - returned 2 (only conditionally invariant). */ - m->cond = ((tem | tem1 | tem2) > 1); - m->global = (uid_luid[regno_last_uid[regno]] > INSN_LUID (end) - || uid_luid[regno_first_uid[regno]] < INSN_LUID (loop_start)); - m->match = 0; - m->lifetime = (uid_luid[regno_last_uid[regno]] - - uid_luid[regno_first_uid[regno]]); - m->savings = n_times_used[regno]; - if (find_reg_note (p, REG_RETVAL, NULL_RTX)) - m->savings += libcall_benefit (p); - n_times_set[regno] = move_insn ? -2 : -1; - /* Add M to the end of the chain MOVABLES. */ - if (movables == 0) - movables = m; - else - last_movable->next = m; - last_movable = m; - - if (m->consec > 0) - { - /* Skip this insn, not checking REG_LIBCALL notes. */ - p = NEXT_INSN (p); - /* Skip the consecutive insns, if there are any. */ - p = skip_consec_insns (p, m->consec); - /* Back up to the last insn of the consecutive group. */ - p = prev_nonnote_insn (p); - - /* We must now reset m->move_insn, m->is_equiv, and possibly - m->set_src to correspond to the effects of all the - insns. */ - temp = find_reg_note (p, REG_EQUIV, NULL_RTX); - if (temp) - m->set_src = XEXP (temp, 0), m->move_insn = 1; - else - { - temp = find_reg_note (p, REG_EQUAL, NULL_RTX); - if (temp && CONSTANT_P (XEXP (temp, 0))) - m->set_src = XEXP (temp, 0), m->move_insn = 1; - else - m->move_insn = 0; - - } - m->is_equiv = (find_reg_note (p, REG_EQUIV, NULL_RTX) != 0); - } - } - /* If this register is always set within a STRICT_LOW_PART - or set to zero, then its high bytes are constant. - So clear them outside the loop and within the loop - just load the low bytes. - We must check that the machine has an instruction to do so. - Also, if the value loaded into the register - depends on the same register, this cannot be done. */ - else if (SET_SRC (set) == const0_rtx - && GET_CODE (NEXT_INSN (p)) == INSN - && (set1 = single_set (NEXT_INSN (p))) - && GET_CODE (set1) == SET - && (GET_CODE (SET_DEST (set1)) == STRICT_LOW_PART) - && (GET_CODE (XEXP (SET_DEST (set1), 0)) == SUBREG) - && (SUBREG_REG (XEXP (SET_DEST (set1), 0)) - == SET_DEST (set)) - && !reg_mentioned_p (SET_DEST (set), SET_SRC (set1))) - { - register int regno = REGNO (SET_DEST (set)); - if (n_times_set[regno] == 2) - { - register struct movable *m; - m = (struct movable *) alloca (sizeof (struct movable)); - m->next = 0; - m->insn = p; - m->set_dest = SET_DEST (set); - m->dependencies = 0; - m->force = 0; - m->consec = 0; - m->done = 0; - m->forces = 0; - m->move_insn = 0; - m->partial = 1; - /* If the insn may not be executed on some cycles, - we can't clear the whole reg; clear just high part. - Not even if the reg is used only within this loop. - Consider this: - while (1) - while (s != t) { - if (foo ()) x = *s; - use (x); - } - Clearing x before the inner loop could clobber a value - being saved from the last time around the outer loop. - However, if the reg is not used outside this loop - and all uses of the register are in the same - basic block as the store, there is no problem. - - If this insn was made by loop, we don't know its - INSN_LUID and hence must make a conservative - assumption. */ - m->global = (INSN_UID (p) >= max_uid_for_loop - || (uid_luid[regno_last_uid[regno]] - > INSN_LUID (end)) - || (uid_luid[regno_first_uid[regno]] - < INSN_LUID (p)) - || (labels_in_range_p - (p, uid_luid[regno_first_uid[regno]]))); - if (maybe_never && m->global) - m->savemode = GET_MODE (SET_SRC (set1)); - else - m->savemode = VOIDmode; - m->regno = regno; - m->cond = 0; - m->match = 0; - m->lifetime = (uid_luid[regno_last_uid[regno]] - - uid_luid[regno_first_uid[regno]]); - m->savings = 1; - n_times_set[regno] = -1; - /* Add M to the end of the chain MOVABLES. */ - if (movables == 0) - movables = m; - else - last_movable->next = m; - last_movable = m; - } - } - } - /* Past a call insn, we get to insns which might not be executed - because the call might exit. This matters for insns that trap. - Call insns inside a REG_LIBCALL/REG_RETVAL block always return, - so they don't count. */ - else if (GET_CODE (p) == CALL_INSN && ! in_libcall) - call_passed = 1; - /* Past a label or a jump, we get to insns for which we - can't count on whether or how many times they will be - executed during each iteration. Therefore, we can - only move out sets of trivial variables - (those not used after the loop). */ - /* This code appears in three places, once in scan_loop, and twice - in strength_reduce. */ - else if ((GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN) - /* If we enter the loop in the middle, and scan around to the - beginning, don't set maybe_never for that. This must be an - unconditional jump, otherwise the code at the top of the - loop might never be executed. Unconditional jumps are - followed a by barrier then loop end. */ - && ! (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p) == loop_top - && NEXT_INSN (NEXT_INSN (p)) == end - && simplejump_p (p))) - maybe_never = 1; - /* At the virtual top of a converted loop, insns are again known to - be executed: logically, the loop begins here even though the exit - code has been duplicated. */ - else if (GET_CODE (p) == NOTE - && NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_VTOP) - maybe_never = call_passed = 0; - } - - /* If one movable subsumes another, ignore that other. */ - - ignore_some_movables (movables); - - /* For each movable insn, see if the reg that it loads - leads when it dies right into another conditionally movable insn. - If so, record that the second insn "forces" the first one, - since the second can be moved only if the first is. */ - - force_movables (movables); - - /* See if there are multiple movable insns that load the same value. - If there are, make all but the first point at the first one - through the `match' field, and add the priorities of them - all together as the priority of the first. */ - - combine_movables (movables, nregs); - - /* Now consider each movable insn to decide whether it is worth moving. - Store 0 in n_times_set for each reg that is moved. */ - - move_movables (movables, threshold, - insn_count, loop_start, end, nregs); - - /* Now candidates that still are negative are those not moved. - Change n_times_set to indicate that those are not actually invariant. */ - for (i = 0; i < nregs; i++) - if (n_times_set[i] < 0) - n_times_set[i] = n_times_used[i]; - - if (flag_strength_reduce) - strength_reduce (scan_start, end, loop_top, - insn_count, loop_start, end); -} - -/* Add elements to *OUTPUT to record all the pseudo-regs - mentioned in IN_THIS but not mentioned in NOT_IN_THIS. */ - -void -record_excess_regs (in_this, not_in_this, output) - rtx in_this, not_in_this; - rtx *output; -{ - enum rtx_code code; - char *fmt; - int i; - - code = GET_CODE (in_this); - - switch (code) - { - case PC: - case CC0: - case CONST_INT: - case CONST_DOUBLE: - case CONST: - case SYMBOL_REF: - case LABEL_REF: - return; - - case REG: - if (REGNO (in_this) >= FIRST_PSEUDO_REGISTER - && ! reg_mentioned_p (in_this, not_in_this)) - *output = gen_rtx (EXPR_LIST, VOIDmode, in_this, *output); - return; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - int j; - - switch (fmt[i]) - { - case 'E': - for (j = 0; j < XVECLEN (in_this, i); j++) - record_excess_regs (XVECEXP (in_this, i, j), not_in_this, output); - break; - - case 'e': - record_excess_regs (XEXP (in_this, i), not_in_this, output); - break; - } - } -} - -/* Check what regs are referred to in the libcall block ending with INSN, - aside from those mentioned in the equivalent value. - If there are none, return 0. - If there are one or more, return an EXPR_LIST containing all of them. */ - -static rtx -libcall_other_reg (insn, equiv) - rtx insn, equiv; -{ - rtx note = find_reg_note (insn, REG_RETVAL, NULL_RTX); - rtx p = XEXP (note, 0); - rtx output = 0; - - /* First, find all the regs used in the libcall block - that are not mentioned as inputs to the result. */ - - while (p != insn) - { - if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN - || GET_CODE (p) == CALL_INSN) - record_excess_regs (PATTERN (p), equiv, &output); - p = NEXT_INSN (p); - } - - return output; -} - -/* Return 1 if all uses of REG - are between INSN and the end of the basic block. */ - -static int -reg_in_basic_block_p (insn, reg) - rtx insn, reg; -{ - int regno = REGNO (reg); - rtx p; - - if (regno_first_uid[regno] != INSN_UID (insn)) - return 0; - - /* Search this basic block for the already recorded last use of the reg. */ - for (p = insn; p; p = NEXT_INSN (p)) - { - switch (GET_CODE (p)) - { - case NOTE: - break; - - case INSN: - case CALL_INSN: - /* Ordinary insn: if this is the last use, we win. */ - if (regno_last_uid[regno] == INSN_UID (p)) - return 1; - break; - - case JUMP_INSN: - /* Jump insn: if this is the last use, we win. */ - if (regno_last_uid[regno] == INSN_UID (p)) - return 1; - /* Otherwise, it's the end of the basic block, so we lose. */ - return 0; - - case CODE_LABEL: - case BARRIER: - /* It's the end of the basic block, so we lose. */ - return 0; - } - } - - /* The "last use" doesn't follow the "first use"?? */ - abort (); -} - -/* Compute the benefit of eliminating the insns in the block whose - last insn is LAST. This may be a group of insns used to compute a - value directly or can contain a library call. */ - -static int -libcall_benefit (last) - rtx last; -{ - rtx insn; - int benefit = 0; - - for (insn = XEXP (find_reg_note (last, REG_RETVAL, NULL_RTX), 0); - insn != last; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == CALL_INSN) - benefit += 10; /* Assume at least this many insns in a library - routine. */ - else if (GET_CODE (insn) == INSN - && GET_CODE (PATTERN (insn)) != USE - && GET_CODE (PATTERN (insn)) != CLOBBER) - benefit++; - } - - return benefit; -} - -/* Skip COUNT insns from INSN, counting library calls as 1 insn. */ - -static rtx -skip_consec_insns (insn, count) - rtx insn; - int count; -{ - for (; count > 0; count--) - { - rtx temp; - - /* If first insn of libcall sequence, skip to end. */ - /* Do this at start of loop, since INSN is guaranteed to - be an insn here. */ - if (GET_CODE (insn) != NOTE - && (temp = find_reg_note (insn, REG_LIBCALL, NULL_RTX))) - insn = XEXP (temp, 0); - - do insn = NEXT_INSN (insn); - while (GET_CODE (insn) == NOTE); - } - - return insn; -} - -/* Ignore any movable whose insn falls within a libcall - which is part of another movable. - We make use of the fact that the movable for the libcall value - was made later and so appears later on the chain. */ - -static void -ignore_some_movables (movables) - struct movable *movables; -{ - register struct movable *m, *m1; - - for (m = movables; m; m = m->next) - { - /* Is this a movable for the value of a libcall? */ - rtx note = find_reg_note (m->insn, REG_RETVAL, NULL_RTX); - if (note) - { - rtx insn; - /* Check for earlier movables inside that range, - and mark them invalid. We cannot use LUIDs here because - insns created by loop.c for prior loops don't have LUIDs. - Rather than reject all such insns from movables, we just - explicitly check each insn in the libcall (since invariant - libcalls aren't that common). */ - for (insn = XEXP (note, 0); insn != m->insn; insn = NEXT_INSN (insn)) - for (m1 = movables; m1 != m; m1 = m1->next) - if (m1->insn == insn) - m1->done = 1; - } - } -} - -/* For each movable insn, see if the reg that it loads - leads when it dies right into another conditionally movable insn. - If so, record that the second insn "forces" the first one, - since the second can be moved only if the first is. */ - -static void -force_movables (movables) - struct movable *movables; -{ - register struct movable *m, *m1; - for (m1 = movables; m1; m1 = m1->next) - /* Omit this if moving just the (SET (REG) 0) of a zero-extend. */ - if (!m1->partial && !m1->done) - { - int regno = m1->regno; - for (m = m1->next; m; m = m->next) - /* ??? Could this be a bug? What if CSE caused the - register of M1 to be used after this insn? - Since CSE does not update regno_last_uid, - this insn M->insn might not be where it dies. - But very likely this doesn't matter; what matters is - that M's reg is computed from M1's reg. */ - if (INSN_UID (m->insn) == regno_last_uid[regno] - && !m->done) - break; - if (m != 0 && m->set_src == m1->set_dest - /* If m->consec, m->set_src isn't valid. */ - && m->consec == 0) - m = 0; - - /* Increase the priority of the moving the first insn - since it permits the second to be moved as well. */ - if (m != 0) - { - m->forces = m1; - m1->lifetime += m->lifetime; - m1->savings += m1->savings; - } - } -} - -/* Find invariant expressions that are equal and can be combined into - one register. */ - -static void -combine_movables (movables, nregs) - struct movable *movables; - int nregs; -{ - register struct movable *m; - char *matched_regs = (char *) alloca (nregs); - enum machine_mode mode; - - /* Regs that are set more than once are not allowed to match - or be matched. I'm no longer sure why not. */ - /* Perhaps testing m->consec_sets would be more appropriate here? */ - - for (m = movables; m; m = m->next) - if (m->match == 0 && n_times_used[m->regno] == 1 && !m->partial) - { - register struct movable *m1; - int regno = m->regno; - rtx reg_note, reg_note1; - - bzero (matched_regs, nregs); - matched_regs[regno] = 1; - - for (m1 = movables; m1; m1 = m1->next) - if (m != m1 && m1->match == 0 && n_times_used[m1->regno] == 1 - /* A reg used outside the loop mustn't be eliminated. */ - && !m1->global - /* A reg used for zero-extending mustn't be eliminated. */ - && !m1->partial - && (matched_regs[m1->regno] - || - ( - /* Can combine regs with different modes loaded from the - same constant only if the modes are the same or - if both are integer modes with M wider or the same - width as M1. The check for integer is redundant, but - safe, since the only case of differing destination - modes with equal sources is when both sources are - VOIDmode, i.e., CONST_INT. */ - (GET_MODE (m->set_dest) == GET_MODE (m1->set_dest) - || (GET_MODE_CLASS (GET_MODE (m->set_dest)) == MODE_INT - && GET_MODE_CLASS (GET_MODE (m1->set_dest)) == MODE_INT - && (GET_MODE_BITSIZE (GET_MODE (m->set_dest)) - >= GET_MODE_BITSIZE (GET_MODE (m1->set_dest))))) - /* See if the source of M1 says it matches M. */ - && ((GET_CODE (m1->set_src) == REG - && matched_regs[REGNO (m1->set_src)]) - || rtx_equal_for_loop_p (m->set_src, m1->set_src, - movables)))) - && ((m->dependencies == m1->dependencies) - || rtx_equal_p (m->dependencies, m1->dependencies))) - { - m->lifetime += m1->lifetime; - m->savings += m1->savings; - m1->done = 1; - m1->match = m; - matched_regs[m1->regno] = 1; - } - } - - /* Now combine the regs used for zero-extension. - This can be done for those not marked `global' - provided their lives don't overlap. */ - - for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode; - mode = GET_MODE_WIDER_MODE (mode)) - { - register struct movable *m0 = 0; - - /* Combine all the registers for extension from mode MODE. - Don't combine any that are used outside this loop. */ - for (m = movables; m; m = m->next) - if (m->partial && ! m->global - && mode == GET_MODE (SET_SRC (PATTERN (NEXT_INSN (m->insn))))) - { - register struct movable *m1; - int first = uid_luid[regno_first_uid[m->regno]]; - int last = uid_luid[regno_last_uid[m->regno]]; - - if (m0 == 0) - { - /* First one: don't check for overlap, just record it. */ - m0 = m; - continue; - } - - /* Make sure they extend to the same mode. - (Almost always true.) */ - if (GET_MODE (m->set_dest) != GET_MODE (m0->set_dest)) - continue; - - /* We already have one: check for overlap with those - already combined together. */ - for (m1 = movables; m1 != m; m1 = m1->next) - if (m1 == m0 || (m1->partial && m1->match == m0)) - if (! (uid_luid[regno_first_uid[m1->regno]] > last - || uid_luid[regno_last_uid[m1->regno]] < first)) - goto overlap; - - /* No overlap: we can combine this with the others. */ - m0->lifetime += m->lifetime; - m0->savings += m->savings; - m->done = 1; - m->match = m0; - - overlap: ; - } - } -} - -/* Return 1 if regs X and Y will become the same if moved. */ - -static int -regs_match_p (x, y, movables) - rtx x, y; - struct movable *movables; -{ - int xn = REGNO (x); - int yn = REGNO (y); - struct movable *mx, *my; - - for (mx = movables; mx; mx = mx->next) - if (mx->regno == xn) - break; - - for (my = movables; my; my = my->next) - if (my->regno == yn) - break; - - return (mx && my - && ((mx->match == my->match && mx->match != 0) - || mx->match == my - || mx == my->match)); -} - -/* Return 1 if X and Y are identical-looking rtx's. - This is the Lisp function EQUAL for rtx arguments. - - If two registers are matching movables or a movable register and an - equivalent constant, consider them equal. */ - -static int -rtx_equal_for_loop_p (x, y, movables) - rtx x, y; - struct movable *movables; -{ - register int i; - register int j; - register struct movable *m; - register enum rtx_code code; - register char *fmt; - - if (x == y) - return 1; - if (x == 0 || y == 0) - return 0; - - code = GET_CODE (x); - - /* If we have a register and a constant, they may sometimes be - equal. */ - if (GET_CODE (x) == REG && n_times_set[REGNO (x)] == -2 - && CONSTANT_P (y)) - for (m = movables; m; m = m->next) - if (m->move_insn && m->regno == REGNO (x) - && rtx_equal_p (m->set_src, y)) - return 1; - - else if (GET_CODE (y) == REG && n_times_set[REGNO (y)] == -2 - && CONSTANT_P (x)) - for (m = movables; m; m = m->next) - if (m->move_insn && m->regno == REGNO (y) - && rtx_equal_p (m->set_src, x)) - return 1; - - /* Otherwise, rtx's of different codes cannot be equal. */ - if (code != GET_CODE (y)) - return 0; - - /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. - (REG:SI x) and (REG:HI x) are NOT equivalent. */ - - if (GET_MODE (x) != GET_MODE (y)) - return 0; - - /* These three types of rtx's can be compared nonrecursively. */ - if (code == REG) - return (REGNO (x) == REGNO (y) || regs_match_p (x, y, movables)); - - if (code == LABEL_REF) - return XEXP (x, 0) == XEXP (y, 0); - if (code == SYMBOL_REF) - return XSTR (x, 0) == XSTR (y, 0); - - /* Compare the elements. If any pair of corresponding elements - fail to match, return 0 for the whole things. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - switch (fmt[i]) - { - case 'w': - if (XWINT (x, i) != XWINT (y, i)) - return 0; - break; - - case 'i': - if (XINT (x, i) != XINT (y, i)) - return 0; - break; - - case 'E': - /* Two vectors must have the same length. */ - if (XVECLEN (x, i) != XVECLEN (y, i)) - return 0; - - /* And the corresponding elements must match. */ - for (j = 0; j < XVECLEN (x, i); j++) - if (rtx_equal_for_loop_p (XVECEXP (x, i, j), XVECEXP (y, i, j), movables) == 0) - return 0; - break; - - case 'e': - if (rtx_equal_for_loop_p (XEXP (x, i), XEXP (y, i), movables) == 0) - return 0; - break; - - case 's': - if (strcmp (XSTR (x, i), XSTR (y, i))) - return 0; - break; - - case 'u': - /* These are just backpointers, so they don't matter. */ - break; - - case '0': - break; - - /* It is believed that rtx's at this level will never - contain anything but integers and other rtx's, - except for within LABEL_REFs and SYMBOL_REFs. */ - default: - abort (); - } - } - return 1; -} - -/* If X contains any LABEL_REF's, add REG_LABEL notes for them to all - insns in INSNS which use thet reference. */ - -static void -add_label_notes (x, insns) - rtx x; - rtx insns; -{ - enum rtx_code code = GET_CODE (x); - int i, j; - char *fmt; - rtx insn; - - if (code == LABEL_REF && !LABEL_REF_NONLOCAL_P (x)) - { - rtx next = next_real_insn (XEXP (x, 0)); - - /* Don't record labels that refer to dispatch tables. - This is not necessary, since the tablejump references the same label. - And if we did record them, flow.c would make worse code. */ - if (next == 0 - || ! (GET_CODE (next) == JUMP_INSN - && (GET_CODE (PATTERN (next)) == ADDR_VEC - || GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC))) - { - for (insn = insns; insn; insn = NEXT_INSN (insn)) - if (reg_mentioned_p (XEXP (x, 0), insn)) - REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_LABEL, XEXP (x, 0), - REG_NOTES (insn)); - } - return; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - add_label_notes (XEXP (x, i), insns); - else if (fmt[i] == 'E') - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - add_label_notes (XVECEXP (x, i, j), insns); - } -} - -/* Scan MOVABLES, and move the insns that deserve to be moved. - If two matching movables are combined, replace one reg with the - other throughout. */ - -static void -move_movables (movables, threshold, insn_count, loop_start, end, nregs) - struct movable *movables; - int threshold; - int insn_count; - rtx loop_start; - rtx end; - int nregs; -{ - rtx new_start = 0; - register struct movable *m; - register rtx p; - /* Map of pseudo-register replacements to handle combining - when we move several insns that load the same value - into different pseudo-registers. */ - rtx *reg_map = (rtx *) alloca (nregs * sizeof (rtx)); - char *already_moved = (char *) alloca (nregs); - - bzero (already_moved, nregs); - bzero (reg_map, nregs * sizeof (rtx)); - - num_movables = 0; - - for (m = movables; m; m = m->next) - { - /* Describe this movable insn. */ - - if (loop_dump_stream) - { - fprintf (loop_dump_stream, "Insn %d: regno %d (life %d), ", - INSN_UID (m->insn), m->regno, m->lifetime); - if (m->consec > 0) - fprintf (loop_dump_stream, "consec %d, ", m->consec); - if (m->cond) - fprintf (loop_dump_stream, "cond "); - if (m->force) - fprintf (loop_dump_stream, "force "); - if (m->global) - fprintf (loop_dump_stream, "global "); - if (m->done) - fprintf (loop_dump_stream, "done "); - if (m->move_insn) - fprintf (loop_dump_stream, "move-insn "); - if (m->match) - fprintf (loop_dump_stream, "matches %d ", - INSN_UID (m->match->insn)); - if (m->forces) - fprintf (loop_dump_stream, "forces %d ", - INSN_UID (m->forces->insn)); - } - - /* Count movables. Value used in heuristics in strength_reduce. */ - num_movables++; - - /* Ignore the insn if it's already done (it matched something else). - Otherwise, see if it is now safe to move. */ - - if (!m->done - && (! m->cond - || (1 == invariant_p (m->set_src) - && (m->dependencies == 0 - || 1 == invariant_p (m->dependencies)) - && (m->consec == 0 - || 1 == consec_sets_invariant_p (m->set_dest, - m->consec + 1, - m->insn)))) - && (! m->forces || m->forces->done)) - { - register int regno; - register rtx p; - int savings = m->savings; - - /* We have an insn that is safe to move. - Compute its desirability. */ - - p = m->insn; - regno = m->regno; - - if (loop_dump_stream) - fprintf (loop_dump_stream, "savings %d ", savings); - - if (moved_once[regno]) - { - insn_count *= 2; - - if (loop_dump_stream) - fprintf (loop_dump_stream, "halved since already moved "); - } - - /* An insn MUST be moved if we already moved something else - which is safe only if this one is moved too: that is, - if already_moved[REGNO] is nonzero. */ - - /* An insn is desirable to move if the new lifetime of the - register is no more than THRESHOLD times the old lifetime. - If it's not desirable, it means the loop is so big - that moving won't speed things up much, - and it is liable to make register usage worse. */ - - /* It is also desirable to move if it can be moved at no - extra cost because something else was already moved. */ - - if (already_moved[regno] - || (threshold * savings * m->lifetime) >= insn_count - || (m->forces && m->forces->done - && n_times_used[m->forces->regno] == 1)) - { - int count; - register struct movable *m1; - rtx first; - - /* Now move the insns that set the reg. */ - - if (m->partial && m->match) - { - rtx newpat, i1; - rtx r1, r2; - /* Find the end of this chain of matching regs. - Thus, we load each reg in the chain from that one reg. - And that reg is loaded with 0 directly, - since it has ->match == 0. */ - for (m1 = m; m1->match; m1 = m1->match); - newpat = gen_move_insn (SET_DEST (PATTERN (m->insn)), - SET_DEST (PATTERN (m1->insn))); - i1 = emit_insn_before (newpat, loop_start); - - /* Mark the moved, invariant reg as being allowed to - share a hard reg with the other matching invariant. */ - REG_NOTES (i1) = REG_NOTES (m->insn); - r1 = SET_DEST (PATTERN (m->insn)); - r2 = SET_DEST (PATTERN (m1->insn)); - regs_may_share = gen_rtx (EXPR_LIST, VOIDmode, r1, - gen_rtx (EXPR_LIST, VOIDmode, r2, - regs_may_share)); - delete_insn (m->insn); - - if (new_start == 0) - new_start = i1; - - if (loop_dump_stream) - fprintf (loop_dump_stream, " moved to %d", INSN_UID (i1)); - } - /* If we are to re-generate the item being moved with a - new move insn, first delete what we have and then emit - the move insn before the loop. */ - else if (m->move_insn) - { - rtx i1, temp; - - for (count = m->consec; count >= 0; count--) - { - /* If this is the first insn of a library call sequence, - skip to the end. */ - if (GET_CODE (p) != NOTE - && (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX))) - p = XEXP (temp, 0); - - /* If this is the last insn of a libcall sequence, then - delete every insn in the sequence except the last. - The last insn is handled in the normal manner. */ - if (GET_CODE (p) != NOTE - && (temp = find_reg_note (p, REG_RETVAL, NULL_RTX))) - { - temp = XEXP (temp, 0); - while (temp != p) - temp = delete_insn (temp); - } - - p = delete_insn (p); - } - - start_sequence (); - emit_move_insn (m->set_dest, m->set_src); - temp = get_insns (); - end_sequence (); - - add_label_notes (m->set_src, temp); - - i1 = emit_insns_before (temp, loop_start); - if (! find_reg_note (i1, REG_EQUAL, NULL_RTX)) - REG_NOTES (i1) - = gen_rtx (EXPR_LIST, - m->is_equiv ? REG_EQUIV : REG_EQUAL, - m->set_src, REG_NOTES (i1)); - - if (loop_dump_stream) - fprintf (loop_dump_stream, " moved to %d", INSN_UID (i1)); - - /* The more regs we move, the less we like moving them. */ - threshold -= 3; - } - else - { - for (count = m->consec; count >= 0; count--) - { - rtx i1, temp; - - /* If first insn of libcall sequence, skip to end. */ - /* Do this at start of loop, since p is guaranteed to - be an insn here. */ - if (GET_CODE (p) != NOTE - && (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX))) - p = XEXP (temp, 0); - - /* If last insn of libcall sequence, move all - insns except the last before the loop. The last - insn is handled in the normal manner. */ - if (GET_CODE (p) != NOTE - && (temp = find_reg_note (p, REG_RETVAL, NULL_RTX))) - { - rtx fn_address = 0; - rtx fn_reg = 0; - rtx fn_address_insn = 0; - - first = 0; - for (temp = XEXP (temp, 0); temp != p; - temp = NEXT_INSN (temp)) - { - rtx body; - rtx n; - rtx next; - - if (GET_CODE (temp) == NOTE) - continue; - - body = PATTERN (temp); - - /* Find the next insn after TEMP, - not counting USE or NOTE insns. */ - for (next = NEXT_INSN (temp); next != p; - next = NEXT_INSN (next)) - if (! (GET_CODE (next) == INSN - && GET_CODE (PATTERN (next)) == USE) - && GET_CODE (next) != NOTE) - break; - - /* If that is the call, this may be the insn - that loads the function address. - - Extract the function address from the insn - that loads it into a register. - If this insn was cse'd, we get incorrect code. - - So emit a new move insn that copies the - function address into the register that the - call insn will use. flow.c will delete any - redundant stores that we have created. */ - if (GET_CODE (next) == CALL_INSN - && GET_CODE (body) == SET - && GET_CODE (SET_DEST (body)) == REG - && (n = find_reg_note (temp, REG_EQUAL, - NULL_RTX))) - { - fn_reg = SET_SRC (body); - if (GET_CODE (fn_reg) != REG) - fn_reg = SET_DEST (body); - fn_address = XEXP (n, 0); - fn_address_insn = temp; - } - /* We have the call insn. - If it uses the register we suspect it might, - load it with the correct address directly. */ - if (GET_CODE (temp) == CALL_INSN - && fn_address != 0 - && reg_referenced_p (fn_reg, body)) - emit_insn_after (gen_move_insn (fn_reg, - fn_address), - fn_address_insn); - - if (GET_CODE (temp) == CALL_INSN) - i1 = emit_call_insn_before (body, loop_start); - else - i1 = emit_insn_before (body, loop_start); - if (first == 0) - first = i1; - if (temp == fn_address_insn) - fn_address_insn = i1; - REG_NOTES (i1) = REG_NOTES (temp); - delete_insn (temp); - } - } - if (m->savemode != VOIDmode) - { - /* P sets REG to zero; but we should clear only - the bits that are not covered by the mode - m->savemode. */ - rtx reg = m->set_dest; - rtx sequence; - rtx tem; - - start_sequence (); - tem = expand_binop - (GET_MODE (reg), and_optab, reg, - GEN_INT ((((HOST_WIDE_INT) 1 - << GET_MODE_BITSIZE (m->savemode))) - - 1), - reg, 1, OPTAB_LIB_WIDEN); - if (tem == 0) - abort (); - if (tem != reg) - emit_move_insn (reg, tem); - sequence = gen_sequence (); - end_sequence (); - i1 = emit_insn_before (sequence, loop_start); - } - else if (GET_CODE (p) == CALL_INSN) - i1 = emit_call_insn_before (PATTERN (p), loop_start); - else - i1 = emit_insn_before (PATTERN (p), loop_start); - - REG_NOTES (i1) = REG_NOTES (p); - - if (new_start == 0) - new_start = i1; - - if (loop_dump_stream) - fprintf (loop_dump_stream, " moved to %d", - INSN_UID (i1)); - -#if 0 - /* This isn't needed because REG_NOTES is copied - below and is wrong since P might be a PARALLEL. */ - if (REG_NOTES (i1) == 0 - && ! m->partial /* But not if it's a zero-extend clr. */ - && ! m->global /* and not if used outside the loop - (since it might get set outside). */ - && CONSTANT_P (SET_SRC (PATTERN (p)))) - REG_NOTES (i1) - = gen_rtx (EXPR_LIST, REG_EQUAL, - SET_SRC (PATTERN (p)), REG_NOTES (i1)); -#endif - - /* If library call, now fix the REG_NOTES that contain - insn pointers, namely REG_LIBCALL on FIRST - and REG_RETVAL on I1. */ - if (temp = find_reg_note (i1, REG_RETVAL, NULL_RTX)) - { - XEXP (temp, 0) = first; - temp = find_reg_note (first, REG_LIBCALL, NULL_RTX); - XEXP (temp, 0) = i1; - } - - delete_insn (p); - do p = NEXT_INSN (p); - while (p && GET_CODE (p) == NOTE); - } - - /* The more regs we move, the less we like moving them. */ - threshold -= 3; - } - - /* Any other movable that loads the same register - MUST be moved. */ - already_moved[regno] = 1; - - /* This reg has been moved out of one loop. */ - moved_once[regno] = 1; - - /* The reg set here is now invariant. */ - if (! m->partial) - n_times_set[regno] = 0; - - m->done = 1; - - /* Change the length-of-life info for the register - to say it lives at least the full length of this loop. - This will help guide optimizations in outer loops. */ - - if (uid_luid[regno_first_uid[regno]] > INSN_LUID (loop_start)) - /* This is the old insn before all the moved insns. - We can't use the moved insn because it is out of range - in uid_luid. Only the old insns have luids. */ - regno_first_uid[regno] = INSN_UID (loop_start); - if (uid_luid[regno_last_uid[regno]] < INSN_LUID (end)) - regno_last_uid[regno] = INSN_UID (end); - - /* Combine with this moved insn any other matching movables. */ - - if (! m->partial) - for (m1 = movables; m1; m1 = m1->next) - if (m1->match == m) - { - rtx temp; - - /* Schedule the reg loaded by M1 - for replacement so that shares the reg of M. - If the modes differ (only possible in restricted - circumstances, make a SUBREG. */ - if (GET_MODE (m->set_dest) == GET_MODE (m1->set_dest)) - reg_map[m1->regno] = m->set_dest; - else - reg_map[m1->regno] - = gen_lowpart_common (GET_MODE (m1->set_dest), - m->set_dest); - - /* Get rid of the matching insn - and prevent further processing of it. */ - m1->done = 1; - - /* if library call, delete all insn except last, which - is deleted below */ - if (temp = find_reg_note (m1->insn, REG_RETVAL, - NULL_RTX)) - { - for (temp = XEXP (temp, 0); temp != m1->insn; - temp = NEXT_INSN (temp)) - delete_insn (temp); - } - delete_insn (m1->insn); - - /* Any other movable that loads the same register - MUST be moved. */ - already_moved[m1->regno] = 1; - - /* The reg merged here is now invariant, - if the reg it matches is invariant. */ - if (! m->partial) - n_times_set[m1->regno] = 0; - } - } - else if (loop_dump_stream) - fprintf (loop_dump_stream, "not desirable"); - } - else if (loop_dump_stream && !m->match) - fprintf (loop_dump_stream, "not safe"); - - if (loop_dump_stream) - fprintf (loop_dump_stream, "\n"); - } - - if (new_start == 0) - new_start = loop_start; - - /* Go through all the instructions in the loop, making - all the register substitutions scheduled in REG_MAP. */ - for (p = new_start; p != end; p = NEXT_INSN (p)) - if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN - || GET_CODE (p) == CALL_INSN) - { - replace_regs (PATTERN (p), reg_map, nregs, 0); - replace_regs (REG_NOTES (p), reg_map, nregs, 0); - INSN_CODE (p) = -1; - } -} - -#if 0 -/* Scan X and replace the address of any MEM in it with ADDR. - REG is the address that MEM should have before the replacement. */ - -static void -replace_call_address (x, reg, addr) - rtx x, reg, addr; -{ - register enum rtx_code code; - register int i; - register char *fmt; - - if (x == 0) - return; - code = GET_CODE (x); - switch (code) - { - case PC: - case CC0: - case CONST_INT: - case CONST_DOUBLE: - case CONST: - case SYMBOL_REF: - case LABEL_REF: - case REG: - return; - - case SET: - /* Short cut for very common case. */ - replace_call_address (XEXP (x, 1), reg, addr); - return; - - case CALL: - /* Short cut for very common case. */ - replace_call_address (XEXP (x, 0), reg, addr); - return; - - case MEM: - /* If this MEM uses a reg other than the one we expected, - something is wrong. */ - if (XEXP (x, 0) != reg) - abort (); - XEXP (x, 0) = addr; - return; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - replace_call_address (XEXP (x, i), reg, addr); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - replace_call_address (XVECEXP (x, i, j), reg, addr); - } - } -} -#endif - -/* Return the number of memory refs to addresses that vary - in the rtx X. */ - -static int -count_nonfixed_reads (x) - rtx x; -{ - register enum rtx_code code; - register int i; - register char *fmt; - int value; - - if (x == 0) - return 0; - - code = GET_CODE (x); - switch (code) - { - case PC: - case CC0: - case CONST_INT: - case CONST_DOUBLE: - case CONST: - case SYMBOL_REF: - case LABEL_REF: - case REG: - return 0; - - case MEM: - return ((invariant_p (XEXP (x, 0)) != 1) - + count_nonfixed_reads (XEXP (x, 0))); - } - - value = 0; - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - value += count_nonfixed_reads (XEXP (x, i)); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - value += count_nonfixed_reads (XVECEXP (x, i, j)); - } - } - return value; -} - - -#if 0 -/* P is an instruction that sets a register to the result of a ZERO_EXTEND. - Replace it with an instruction to load just the low bytes - if the machine supports such an instruction, - and insert above LOOP_START an instruction to clear the register. */ - -static void -constant_high_bytes (p, loop_start) - rtx p, loop_start; -{ - register rtx new; - register int insn_code_number; - - /* Try to change (SET (REG ...) (ZERO_EXTEND (..:B ...))) - to (SET (STRICT_LOW_PART (SUBREG:B (REG...))) ...). */ - - new = gen_rtx (SET, VOIDmode, - gen_rtx (STRICT_LOW_PART, VOIDmode, - gen_rtx (SUBREG, GET_MODE (XEXP (SET_SRC (PATTERN (p)), 0)), - SET_DEST (PATTERN (p)), - 0)), - XEXP (SET_SRC (PATTERN (p)), 0)); - insn_code_number = recog (new, p); - - if (insn_code_number) - { - register int i; - - /* Clear destination register before the loop. */ - emit_insn_before (gen_rtx (SET, VOIDmode, - SET_DEST (PATTERN (p)), - const0_rtx), - loop_start); - - /* Inside the loop, just load the low part. */ - PATTERN (p) = new; - } -} -#endif - -/* Scan a loop setting the variables `unknown_address_altered', - `num_mem_sets', `loop_continue', loops_enclosed', `loop_has_call', - and `loop_has_volatile'. - Also, fill in the array `loop_store_mems'. */ - -static void -prescan_loop (start, end) - rtx start, end; -{ - register int level = 1; - register rtx insn; - - unknown_address_altered = 0; - loop_has_call = 0; - loop_has_volatile = 0; - loop_store_mems_idx = 0; - - num_mem_sets = 0; - loops_enclosed = 1; - loop_continue = 0; - - for (insn = NEXT_INSN (start); insn != NEXT_INSN (end); - insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == NOTE) - { - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG) - { - ++level; - /* Count number of loops contained in this one. */ - loops_enclosed++; - } - else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END) - { - --level; - if (level == 0) - { - end = insn; - break; - } - } - else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_CONT) - { - if (level == 1) - loop_continue = insn; - } - } - else if (GET_CODE (insn) == CALL_INSN) - { - unknown_address_altered = 1; - loop_has_call = 1; - } - else - { - if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN) - { - if (volatile_refs_p (PATTERN (insn))) - loop_has_volatile = 1; - - note_stores (PATTERN (insn), note_addr_stored); - } - } - } -} - -/* Scan the function looking for loops. Record the start and end of each loop. - Also mark as invalid loops any loops that contain a setjmp or are branched - to from outside the loop. */ - -static void -find_and_verify_loops (f) - rtx f; -{ - rtx insn, label; - int current_loop = -1; - int next_loop = -1; - int loop; - - /* If there are jumps to undefined labels, - treat them as jumps out of any/all loops. - This also avoids writing past end of tables when there are no loops. */ - uid_loop_num[0] = -1; - - /* Find boundaries of loops, mark which loops are contained within - loops, and invalidate loops that have setjmp. */ - - for (insn = f; insn; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == NOTE) - switch (NOTE_LINE_NUMBER (insn)) - { - case NOTE_INSN_LOOP_BEG: - loop_number_loop_starts[++next_loop] = insn; - loop_number_loop_ends[next_loop] = 0; - loop_outer_loop[next_loop] = current_loop; - loop_invalid[next_loop] = 0; - loop_number_exit_labels[next_loop] = 0; - current_loop = next_loop; - break; - - case NOTE_INSN_SETJMP: - /* In this case, we must invalidate our current loop and any - enclosing loop. */ - for (loop = current_loop; loop != -1; loop = loop_outer_loop[loop]) - { - loop_invalid[loop] = 1; - if (loop_dump_stream) - fprintf (loop_dump_stream, - "\nLoop at %d ignored due to setjmp.\n", - INSN_UID (loop_number_loop_starts[loop])); - } - break; - - case NOTE_INSN_LOOP_END: - if (current_loop == -1) - abort (); - - loop_number_loop_ends[current_loop] = insn; - current_loop = loop_outer_loop[current_loop]; - break; - - } - - /* Note that this will mark the NOTE_INSN_LOOP_END note as being in the - enclosing loop, but this doesn't matter. */ - uid_loop_num[INSN_UID (insn)] = current_loop; - } - - /* Any loop containing a label used in an initializer must be invalidated, - because it can be jumped into from anywhere. */ - - for (label = forced_labels; label; label = XEXP (label, 1)) - { - int loop_num; - - for (loop_num = uid_loop_num[INSN_UID (XEXP (label, 0))]; - loop_num != -1; - loop_num = loop_outer_loop[loop_num]) - loop_invalid[loop_num] = 1; - } - - /* Now scan all insn's in the function. If any JUMP_INSN branches into a - loop that it is not contained within, that loop is marked invalid. - If any INSN or CALL_INSN uses a label's address, then the loop containing - that label is marked invalid, because it could be jumped into from - anywhere. - - Also look for blocks of code ending in an unconditional branch that - exits the loop. If such a block is surrounded by a conditional - branch around the block, move the block elsewhere (see below) and - invert the jump to point to the code block. This may eliminate a - label in our loop and will simplify processing by both us and a - possible second cse pass. */ - - for (insn = f; insn; insn = NEXT_INSN (insn)) - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') - { - int this_loop_num = uid_loop_num[INSN_UID (insn)]; - - if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN) - { - rtx note = find_reg_note (insn, REG_LABEL, NULL_RTX); - if (note) - { - int loop_num; - - for (loop_num = uid_loop_num[INSN_UID (XEXP (note, 0))]; - loop_num != -1; - loop_num = loop_outer_loop[loop_num]) - loop_invalid[loop_num] = 1; - } - } - - if (GET_CODE (insn) != JUMP_INSN) - continue; - - mark_loop_jump (PATTERN (insn), this_loop_num); - - /* See if this is an unconditional branch outside the loop. */ - if (this_loop_num != -1 - && (GET_CODE (PATTERN (insn)) == RETURN - || (simplejump_p (insn) - && (uid_loop_num[INSN_UID (JUMP_LABEL (insn))] - != this_loop_num))) - && get_max_uid () < max_uid_for_loop) - { - rtx p; - rtx our_next = next_real_insn (insn); - - /* Go backwards until we reach the start of the loop, a label, - or a JUMP_INSN. */ - for (p = PREV_INSN (insn); - GET_CODE (p) != CODE_LABEL - && ! (GET_CODE (p) == NOTE - && NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG) - && GET_CODE (p) != JUMP_INSN; - p = PREV_INSN (p)) - ; - - /* If we stopped on a JUMP_INSN to the next insn after INSN, - we have a block of code to try to move. - - We look backward and then forward from the target of INSN - to find a BARRIER at the same loop depth as the target. - If we find such a BARRIER, we make a new label for the start - of the block, invert the jump in P and point it to that label, - and move the block of code to the spot we found. */ - - if (GET_CODE (p) == JUMP_INSN - && JUMP_LABEL (p) != 0 - /* Just ignore jumps to labels that were never emitted. - These always indicate compilation errors. */ - && INSN_UID (JUMP_LABEL (p)) != 0 - && condjump_p (p) - && ! simplejump_p (p) - && next_real_insn (JUMP_LABEL (p)) == our_next) - { - rtx target - = JUMP_LABEL (insn) ? JUMP_LABEL (insn) : get_last_insn (); - int target_loop_num = uid_loop_num[INSN_UID (target)]; - rtx loc; - - for (loc = target; loc; loc = PREV_INSN (loc)) - if (GET_CODE (loc) == BARRIER - && uid_loop_num[INSN_UID (loc)] == target_loop_num) - break; - - if (loc == 0) - for (loc = target; loc; loc = NEXT_INSN (loc)) - if (GET_CODE (loc) == BARRIER - && uid_loop_num[INSN_UID (loc)] == target_loop_num) - break; - - if (loc) - { - rtx cond_label = JUMP_LABEL (p); - rtx new_label = get_label_after (p); - - /* Ensure our label doesn't go away. */ - LABEL_NUSES (cond_label)++; - - /* Verify that uid_loop_num is large enough and that - we can invert P. */ - if (invert_jump (p, new_label)) - { - rtx q, r; - - /* Include the BARRIER after INSN and copy the - block after LOC. */ - new_label = squeeze_notes (new_label, NEXT_INSN (insn)); - reorder_insns (new_label, NEXT_INSN (insn), loc); - - /* All those insns are now in TARGET_LOOP_NUM. */ - for (q = new_label; q != NEXT_INSN (NEXT_INSN (insn)); - q = NEXT_INSN (q)) - uid_loop_num[INSN_UID (q)] = target_loop_num; - - /* The label jumped to by INSN is no longer a loop exit. - Unless INSN does not have a label (e.g., it is a - RETURN insn), search loop_number_exit_labels to find - its label_ref, and remove it. Also turn off - LABEL_OUTSIDE_LOOP_P bit. */ - if (JUMP_LABEL (insn)) - { - for (q = 0, - r = loop_number_exit_labels[this_loop_num]; - r; q = r, r = LABEL_NEXTREF (r)) - if (XEXP (r, 0) == JUMP_LABEL (insn)) - { - LABEL_OUTSIDE_LOOP_P (r) = 0; - if (q) - LABEL_NEXTREF (q) = LABEL_NEXTREF (r); - else - loop_number_exit_labels[this_loop_num] - = LABEL_NEXTREF (r); - break; - } - - /* If we didn't find it, then something is wrong. */ - if (! r) - abort (); - } - - /* P is now a jump outside the loop, so it must be put - in loop_number_exit_labels, and marked as such. - The easiest way to do this is to just call - mark_loop_jump again for P. */ - mark_loop_jump (PATTERN (p), this_loop_num); - - /* If INSN now jumps to the insn after it, - delete INSN. */ - if (JUMP_LABEL (insn) != 0 - && (next_real_insn (JUMP_LABEL (insn)) - == next_real_insn (insn))) - delete_insn (insn); - } - - /* Continue the loop after where the conditional - branch used to jump, since the only branch insn - in the block (if it still remains) is an inter-loop - branch and hence needs no processing. */ - insn = NEXT_INSN (cond_label); - - if (--LABEL_NUSES (cond_label) == 0) - delete_insn (cond_label); - } - } - } - } -} - -/* If any label in X jumps to a loop different from LOOP_NUM and any of the - loops it is contained in, mark the target loop invalid. - - For speed, we assume that X is part of a pattern of a JUMP_INSN. */ - -static void -mark_loop_jump (x, loop_num) - rtx x; - int loop_num; -{ - int dest_loop; - int outer_loop; - int i; - - switch (GET_CODE (x)) - { - case PC: - case USE: - case CLOBBER: - case REG: - case MEM: - case CONST_INT: - case CONST_DOUBLE: - case RETURN: - return; - - case CONST: - /* There could be a label reference in here. */ - mark_loop_jump (XEXP (x, 0), loop_num); - return; - - case PLUS: - case MINUS: - case MULT: - case LSHIFT: - mark_loop_jump (XEXP (x, 0), loop_num); - mark_loop_jump (XEXP (x, 1), loop_num); - return; - - case SIGN_EXTEND: - case ZERO_EXTEND: - mark_loop_jump (XEXP (x, 0), loop_num); - return; - - case LABEL_REF: - dest_loop = uid_loop_num[INSN_UID (XEXP (x, 0))]; - - /* Link together all labels that branch outside the loop. This - is used by final_[bg]iv_value and the loop unrolling code. Also - mark this LABEL_REF so we know that this branch should predict - false. */ - - if (dest_loop != loop_num && loop_num != -1) - { - LABEL_OUTSIDE_LOOP_P (x) = 1; - LABEL_NEXTREF (x) = loop_number_exit_labels[loop_num]; - loop_number_exit_labels[loop_num] = x; - } - - /* If this is inside a loop, but not in the current loop or one enclosed - by it, it invalidates at least one loop. */ - - if (dest_loop == -1) - return; - - /* We must invalidate every nested loop containing the target of this - label, except those that also contain the jump insn. */ - - for (; dest_loop != -1; dest_loop = loop_outer_loop[dest_loop]) - { - /* Stop when we reach a loop that also contains the jump insn. */ - for (outer_loop = loop_num; outer_loop != -1; - outer_loop = loop_outer_loop[outer_loop]) - if (dest_loop == outer_loop) - return; - - /* If we get here, we know we need to invalidate a loop. */ - if (loop_dump_stream && ! loop_invalid[dest_loop]) - fprintf (loop_dump_stream, - "\nLoop at %d ignored due to multiple entry points.\n", - INSN_UID (loop_number_loop_starts[dest_loop])); - - loop_invalid[dest_loop] = 1; - } - return; - - case SET: - /* If this is not setting pc, ignore. */ - if (SET_DEST (x) == pc_rtx) - mark_loop_jump (SET_SRC (x), loop_num); - return; - - case IF_THEN_ELSE: - mark_loop_jump (XEXP (x, 1), loop_num); - mark_loop_jump (XEXP (x, 2), loop_num); - return; - - case PARALLEL: - case ADDR_VEC: - for (i = 0; i < XVECLEN (x, 0); i++) - mark_loop_jump (XVECEXP (x, 0, i), loop_num); - return; - - case ADDR_DIFF_VEC: - for (i = 0; i < XVECLEN (x, 1); i++) - mark_loop_jump (XVECEXP (x, 1, i), loop_num); - return; - - default: - /* Nothing else should occur in a JUMP_INSN. */ - abort (); - } -} - -/* Return nonzero if there is a label in the range from - insn INSN to and including the insn whose luid is END - INSN must have an assigned luid (i.e., it must not have - been previously created by loop.c). */ - -static int -labels_in_range_p (insn, end) - rtx insn; - int end; -{ - while (insn && INSN_LUID (insn) <= end) - { - if (GET_CODE (insn) == CODE_LABEL) - return 1; - insn = NEXT_INSN (insn); - } - - return 0; -} - -/* Record that a memory reference X is being set. */ - -static void -note_addr_stored (x) - rtx x; -{ - register int i; - - if (x == 0 || GET_CODE (x) != MEM) - return; - - /* Count number of memory writes. - This affects heuristics in strength_reduce. */ - num_mem_sets++; - - if (unknown_address_altered) - return; - - for (i = 0; i < loop_store_mems_idx; i++) - if (rtx_equal_p (XEXP (loop_store_mems[i], 0), XEXP (x, 0)) - && MEM_IN_STRUCT_P (x) == MEM_IN_STRUCT_P (loop_store_mems[i])) - { - /* We are storing at the same address as previously noted. Save the - wider reference, treating BLKmode as wider. */ - if (GET_MODE (x) == BLKmode - || (GET_MODE_SIZE (GET_MODE (x)) - > GET_MODE_SIZE (GET_MODE (loop_store_mems[i])))) - loop_store_mems[i] = x; - break; - } - - if (i == NUM_STORES) - unknown_address_altered = 1; - - else if (i == loop_store_mems_idx) - loop_store_mems[loop_store_mems_idx++] = x; -} - -/* Return nonzero if the rtx X is invariant over the current loop. - - The value is 2 if we refer to something only conditionally invariant. - - If `unknown_address_altered' is nonzero, no memory ref is invariant. - Otherwise, a memory ref is invariant if it does not conflict with - anything stored in `loop_store_mems'. */ - -int -invariant_p (x) - register rtx x; -{ - register int i; - register enum rtx_code code; - register char *fmt; - int conditional = 0; - - if (x == 0) - return 1; - code = GET_CODE (x); - switch (code) - { - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case CONST: - return 1; - - case LABEL_REF: - /* A LABEL_REF is normally invariant, however, if we are unrolling - loops, and this label is inside the loop, then it isn't invariant. - This is because each unrolled copy of the loop body will have - a copy of this label. If this was invariant, then an insn loading - the address of this label into a register might get moved outside - the loop, and then each loop body would end up using the same label. - - We don't know the loop bounds here though, so just fail for all - labels. */ - if (flag_unroll_loops) - return 0; - else - return 1; - - case PC: - case CC0: - case UNSPEC_VOLATILE: - return 0; - - case REG: - /* We used to check RTX_UNCHANGING_P (x) here, but that is invalid - since the reg might be set by initialization within the loop. */ - if (x == frame_pointer_rtx || x == arg_pointer_rtx) - return 1; - if (loop_has_call - && REGNO (x) < FIRST_PSEUDO_REGISTER && call_used_regs[REGNO (x)]) - return 0; - if (n_times_set[REGNO (x)] < 0) - return 2; - return n_times_set[REGNO (x)] == 0; - - case MEM: - /* Read-only items (such as constants in a constant pool) are - invariant if their address is. */ - if (RTX_UNCHANGING_P (x)) - break; - - /* If we filled the table (or had a subroutine call), any location - in memory could have been clobbered. */ - if (unknown_address_altered - /* Don't mess with volatile memory references. */ - || MEM_VOLATILE_P (x)) - return 0; - - /* See if there is any dependence between a store and this load. */ - for (i = loop_store_mems_idx - 1; i >= 0; i--) - if (true_dependence (loop_store_mems[i], x)) - return 0; - - /* It's not invalidated by a store in memory - but we must still verify the address is invariant. */ - break; - - case ASM_OPERANDS: - /* Don't mess with insns declared volatile. */ - if (MEM_VOLATILE_P (x)) - return 0; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - int tem = invariant_p (XEXP (x, i)); - if (tem == 0) - return 0; - if (tem == 2) - conditional = 1; - } - else if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - { - int tem = invariant_p (XVECEXP (x, i, j)); - if (tem == 0) - return 0; - if (tem == 2) - conditional = 1; - } - - } - } - - return 1 + conditional; -} - - -/* Return nonzero if all the insns in the loop that set REG - are INSN and the immediately following insns, - and if each of those insns sets REG in an invariant way - (not counting uses of REG in them). - - The value is 2 if some of these insns are only conditionally invariant. - - We assume that INSN itself is the first set of REG - and that its source is invariant. */ - -static int -consec_sets_invariant_p (reg, n_sets, insn) - int n_sets; - rtx reg, insn; -{ - register rtx p = insn; - register int regno = REGNO (reg); - rtx temp; - /* Number of sets we have to insist on finding after INSN. */ - int count = n_sets - 1; - int old = n_times_set[regno]; - int value = 0; - int this; - - /* If N_SETS hit the limit, we can't rely on its value. */ - if (n_sets == 127) - return 0; - - n_times_set[regno] = 0; - - while (count > 0) - { - register enum rtx_code code; - rtx set; - - p = NEXT_INSN (p); - code = GET_CODE (p); - - /* If library call, skip to end of of it. */ - if (code == INSN && (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX))) - p = XEXP (temp, 0); - - this = 0; - if (code == INSN - && (set = single_set (p)) - && GET_CODE (SET_DEST (set)) == REG - && REGNO (SET_DEST (set)) == regno) - { - this = invariant_p (SET_SRC (set)); - if (this != 0) - value |= this; - else if (temp = find_reg_note (p, REG_EQUAL, NULL_RTX)) - { - /* If this is a libcall, then any invariant REG_EQUAL note is OK. - If this is an ordinary insn, then only CONSTANT_P REG_EQUAL - notes are OK. */ - this = (CONSTANT_P (XEXP (temp, 0)) - || (find_reg_note (p, REG_RETVAL, NULL_RTX) - && invariant_p (XEXP (temp, 0)))); - if (this != 0) - value |= this; - } - } - if (this != 0) - count--; - else if (code != NOTE) - { - n_times_set[regno] = old; - return 0; - } - } - - n_times_set[regno] = old; - /* If invariant_p ever returned 2, we return 2. */ - return 1 + (value & 2); -} - -#if 0 -/* I don't think this condition is sufficient to allow INSN - to be moved, so we no longer test it. */ - -/* Return 1 if all insns in the basic block of INSN and following INSN - that set REG are invariant according to TABLE. */ - -static int -all_sets_invariant_p (reg, insn, table) - rtx reg, insn; - short *table; -{ - register rtx p = insn; - register int regno = REGNO (reg); - - while (1) - { - register enum rtx_code code; - p = NEXT_INSN (p); - code = GET_CODE (p); - if (code == CODE_LABEL || code == JUMP_INSN) - return 1; - if (code == INSN && GET_CODE (PATTERN (p)) == SET - && GET_CODE (SET_DEST (PATTERN (p))) == REG - && REGNO (SET_DEST (PATTERN (p))) == regno) - { - if (!invariant_p (SET_SRC (PATTERN (p)), table)) - return 0; - } - } -} -#endif /* 0 */ - -/* Look at all uses (not sets) of registers in X. For each, if it is - the single use, set USAGE[REGNO] to INSN; if there was a previous use in - a different insn, set USAGE[REGNO] to const0_rtx. */ - -static void -find_single_use_in_loop (insn, x, usage) - rtx insn; - rtx x; - rtx *usage; -{ - enum rtx_code code = GET_CODE (x); - char *fmt = GET_RTX_FORMAT (code); - int i, j; - - if (code == REG) - usage[REGNO (x)] - = (usage[REGNO (x)] != 0 && usage[REGNO (x)] != insn) - ? const0_rtx : insn; - - else if (code == SET) - { - /* Don't count SET_DEST if it is a REG; otherwise count things - in SET_DEST because if a register is partially modified, it won't - show up as a potential movable so we don't care how USAGE is set - for it. */ - if (GET_CODE (SET_DEST (x)) != REG) - find_single_use_in_loop (insn, SET_DEST (x), usage); - find_single_use_in_loop (insn, SET_SRC (x), usage); - } - else - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e' && XEXP (x, i) != 0) - find_single_use_in_loop (insn, XEXP (x, i), usage); - else if (fmt[i] == 'E') - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - find_single_use_in_loop (insn, XVECEXP (x, i, j), usage); - } -} - -/* Increment N_TIMES_SET at the index of each register - that is modified by an insn between FROM and TO. - If the value of an element of N_TIMES_SET becomes 127 or more, - stop incrementing it, to avoid overflow. - - Store in SINGLE_USAGE[I] the single insn in which register I is - used, if it is only used once. Otherwise, it is set to 0 (for no - uses) or const0_rtx for more than one use. This parameter may be zero, - in which case this processing is not done. - - Store in *COUNT_PTR the number of actual instruction - in the loop. We use this to decide what is worth moving out. */ - -/* last_set[n] is nonzero iff reg n has been set in the current basic block. - In that case, it is the insn that last set reg n. */ - -static void -count_loop_regs_set (from, to, may_not_move, single_usage, count_ptr, nregs) - register rtx from, to; - char *may_not_move; - rtx *single_usage; - int *count_ptr; - int nregs; -{ - register rtx *last_set = (rtx *) alloca (nregs * sizeof (rtx)); - register rtx insn; - register int count = 0; - register rtx dest; - - bzero (last_set, nregs * sizeof (rtx)); - for (insn = from; insn != to; insn = NEXT_INSN (insn)) - { - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') - { - ++count; - - /* If requested, record registers that have exactly one use. */ - if (single_usage) - { - find_single_use_in_loop (insn, PATTERN (insn), single_usage); - - /* Include uses in REG_EQUAL notes. */ - if (REG_NOTES (insn)) - find_single_use_in_loop (insn, REG_NOTES (insn), single_usage); - } - - if (GET_CODE (PATTERN (insn)) == CLOBBER - && GET_CODE (XEXP (PATTERN (insn), 0)) == REG) - /* Don't move a reg that has an explicit clobber. - We might do so sometimes, but it's not worth the pain. */ - may_not_move[REGNO (XEXP (PATTERN (insn), 0))] = 1; - - if (GET_CODE (PATTERN (insn)) == SET - || GET_CODE (PATTERN (insn)) == CLOBBER) - { - dest = SET_DEST (PATTERN (insn)); - while (GET_CODE (dest) == SUBREG - || GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == STRICT_LOW_PART) - dest = XEXP (dest, 0); - if (GET_CODE (dest) == REG) - { - register int regno = REGNO (dest); - /* If this is the first setting of this reg - in current basic block, and it was set before, - it must be set in two basic blocks, so it cannot - be moved out of the loop. */ - if (n_times_set[regno] > 0 && last_set[regno] == 0) - may_not_move[regno] = 1; - /* If this is not first setting in current basic block, - see if reg was used in between previous one and this. - If so, neither one can be moved. */ - if (last_set[regno] != 0 - && reg_used_between_p (dest, last_set[regno], insn)) - may_not_move[regno] = 1; - if (n_times_set[regno] < 127) - ++n_times_set[regno]; - last_set[regno] = insn; - } - } - else if (GET_CODE (PATTERN (insn)) == PARALLEL) - { - register int i; - for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) - { - register rtx x = XVECEXP (PATTERN (insn), 0, i); - if (GET_CODE (x) == CLOBBER && GET_CODE (XEXP (x, 0)) == REG) - /* Don't move a reg that has an explicit clobber. - It's not worth the pain to try to do it correctly. */ - may_not_move[REGNO (XEXP (x, 0))] = 1; - - if (GET_CODE (x) == SET || GET_CODE (x) == CLOBBER) - { - dest = SET_DEST (x); - while (GET_CODE (dest) == SUBREG - || GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == STRICT_LOW_PART) - dest = XEXP (dest, 0); - if (GET_CODE (dest) == REG) - { - register int regno = REGNO (dest); - if (n_times_set[regno] > 0 && last_set[regno] == 0) - may_not_move[regno] = 1; - if (last_set[regno] != 0 - && reg_used_between_p (dest, last_set[regno], insn)) - may_not_move[regno] = 1; - if (n_times_set[regno] < 127) - ++n_times_set[regno]; - last_set[regno] = insn; - } - } - } - } - } - if (GET_CODE (insn) == CODE_LABEL || GET_CODE (insn) == JUMP_INSN) - bzero (last_set, nregs * sizeof (rtx)); - } - *count_ptr = count; -} - -/* Given a loop that is bounded by LOOP_START and LOOP_END - and that is entered at SCAN_START, - return 1 if the register set in SET contained in insn INSN is used by - any insn that precedes INSN in cyclic order starting - from the loop entry point. - - We don't want to use INSN_LUID here because if we restrict INSN to those - that have a valid INSN_LUID, it means we cannot move an invariant out - from an inner loop past two loops. */ - -static int -loop_reg_used_before_p (set, insn, loop_start, scan_start, loop_end) - rtx set, insn, loop_start, scan_start, loop_end; -{ - rtx reg = SET_DEST (set); - rtx p; - - /* Scan forward checking for register usage. If we hit INSN, we - are done. Otherwise, if we hit LOOP_END, wrap around to LOOP_START. */ - for (p = scan_start; p != insn; p = NEXT_INSN (p)) - { - if (GET_RTX_CLASS (GET_CODE (p)) == 'i' - && reg_overlap_mentioned_p (reg, PATTERN (p))) - return 1; - - if (p == loop_end) - p = loop_start; - } - - return 0; -} - -/* A "basic induction variable" or biv is a pseudo reg that is set - (within this loop) only by incrementing or decrementing it. */ -/* A "general induction variable" or giv is a pseudo reg whose - value is a linear function of a biv. */ - -/* Bivs are recognized by `basic_induction_var'; - Givs by `general_induct_var'. */ - -/* Indexed by register number, indicates whether or not register is an - induction variable, and if so what type. */ - -enum iv_mode *reg_iv_type; - -/* Indexed by register number, contains pointer to `struct induction' - if register is an induction variable. This holds general info for - all induction variables. */ - -struct induction **reg_iv_info; - -/* Indexed by register number, contains pointer to `struct iv_class' - if register is a basic induction variable. This holds info describing - the class (a related group) of induction variables that the biv belongs - to. */ - -struct iv_class **reg_biv_class; - -/* The head of a list which links together (via the next field) - every iv class for the current loop. */ - -struct iv_class *loop_iv_list; - -/* Communication with routines called via `note_stores'. */ - -static rtx note_insn; - -/* Dummy register to have non-zero DEST_REG for DEST_ADDR type givs. */ - -static rtx addr_placeholder; - -/* ??? Unfinished optimizations, and possible future optimizations, - for the strength reduction code. */ - -/* ??? There is one more optimization you might be interested in doing: to - allocate pseudo registers for frequently-accessed memory locations. - If the same memory location is referenced each time around, it might - be possible to copy it into a register before and out after. - This is especially useful when the memory location is a variable which - is in a stack slot because somewhere its address is taken. If the - loop doesn't contain a function call and the variable isn't volatile, - it is safe to keep the value in a register for the duration of the - loop. One tricky thing is that the copying of the value back from the - register has to be done on all exits from the loop. You need to check that - all the exits from the loop go to the same place. */ - -/* ??? The interaction of biv elimination, and recognition of 'constant' - bivs, may cause problems. */ - -/* ??? Add heuristics so that DEST_ADDR strength reduction does not cause - performance problems. - - Perhaps don't eliminate things that can be combined with an addressing - mode. Find all givs that have the same biv, mult_val, and add_val; - then for each giv, check to see if its only use dies in a following - memory address. If so, generate a new memory address and check to see - if it is valid. If it is valid, then store the modified memory address, - otherwise, mark the giv as not done so that it will get its own iv. */ - -/* ??? Could try to optimize branches when it is known that a biv is always - positive. */ - -/* ??? When replace a biv in a compare insn, we should replace with closest - giv so that an optimized branch can still be recognized by the combiner, - e.g. the VAX acb insn. */ - -/* ??? Many of the checks involving uid_luid could be simplified if regscan - was rerun in loop_optimize whenever a register was added or moved. - Also, some of the optimizations could be a little less conservative. */ - -/* Perform strength reduction and induction variable elimination. */ - -/* Pseudo registers created during this function will be beyond the last - valid index in several tables including n_times_set and regno_last_uid. - This does not cause a problem here, because the added registers cannot be - givs outside of their loop, and hence will never be reconsidered. - But scan_loop must check regnos to make sure they are in bounds. */ - -static void -strength_reduce (scan_start, end, loop_top, insn_count, - loop_start, loop_end) - rtx scan_start; - rtx end; - rtx loop_top; - int insn_count; - rtx loop_start; - rtx loop_end; -{ - rtx p; - rtx set; - rtx inc_val; - rtx mult_val; - rtx dest_reg; - /* This is 1 if current insn is not executed at least once for every loop - iteration. */ - int not_every_iteration = 0; - /* This is 1 if current insn may be executed more than once for every - loop iteration. */ - int maybe_multiple = 0; - /* Temporary list pointers for traversing loop_iv_list. */ - struct iv_class *bl, **backbl; - /* Ratio of extra register life span we can justify - for saving an instruction. More if loop doesn't call subroutines - since in that case saving an insn makes more difference - and more registers are available. */ - /* ??? could set this to last value of threshold in move_movables */ - int threshold = (loop_has_call ? 1 : 2) * (3 + n_non_fixed_regs); - /* Map of pseudo-register replacements. */ - rtx *reg_map; - int call_seen; - rtx test; - rtx end_insert_before; - - reg_iv_type = (enum iv_mode *) alloca (max_reg_before_loop - * sizeof (enum iv_mode *)); - bzero ((char *) reg_iv_type, max_reg_before_loop * sizeof (enum iv_mode *)); - reg_iv_info = (struct induction **) - alloca (max_reg_before_loop * sizeof (struct induction *)); - bzero ((char *) reg_iv_info, (max_reg_before_loop - * sizeof (struct induction *))); - reg_biv_class = (struct iv_class **) - alloca (max_reg_before_loop * sizeof (struct iv_class *)); - bzero ((char *) reg_biv_class, (max_reg_before_loop - * sizeof (struct iv_class *))); - - loop_iv_list = 0; - addr_placeholder = gen_reg_rtx (Pmode); - - /* Save insn immediately after the loop_end. Insns inserted after loop_end - must be put before this insn, so that they will appear in the right - order (i.e. loop order). - - If loop_end is the end of the current function, then emit a - NOTE_INSN_DELETED after loop_end and set end_insert_before to the - dummy note insn. */ - if (NEXT_INSN (loop_end) != 0) - end_insert_before = NEXT_INSN (loop_end); - else - end_insert_before = emit_note_after (NOTE_INSN_DELETED, loop_end); - - /* Scan through loop to find all possible bivs. */ - - p = scan_start; - while (1) - { - p = NEXT_INSN (p); - /* At end of a straight-in loop, we are done. - At end of a loop entered at the bottom, scan the top. */ - if (p == scan_start) - break; - if (p == end) - { - if (loop_top != 0) - p = NEXT_INSN (loop_top); - else - break; - if (p == scan_start) - break; - } - - if (GET_CODE (p) == INSN - && (set = single_set (p)) - && GET_CODE (SET_DEST (set)) == REG) - { - dest_reg = SET_DEST (set); - if (REGNO (dest_reg) < max_reg_before_loop - && REGNO (dest_reg) >= FIRST_PSEUDO_REGISTER - && reg_iv_type[REGNO (dest_reg)] != NOT_BASIC_INDUCT) - { - if (basic_induction_var (SET_SRC (set), dest_reg, p, - &inc_val, &mult_val)) - { - /* It is a possible basic induction variable. - Create and initialize an induction structure for it. */ - - struct induction *v - = (struct induction *) alloca (sizeof (struct induction)); - - record_biv (v, p, dest_reg, inc_val, mult_val, - not_every_iteration, maybe_multiple); - reg_iv_type[REGNO (dest_reg)] = BASIC_INDUCT; - } - else if (REGNO (dest_reg) < max_reg_before_loop) - reg_iv_type[REGNO (dest_reg)] = NOT_BASIC_INDUCT; - } - } - - /* Past CODE_LABEL, we get to insns that may be executed multiple - times. The only way we can be sure that they can't is if every - every jump insn between here and the end of the loop either - returns, exits the loop, or is a forward jump. */ - - if (GET_CODE (p) == CODE_LABEL) - { - rtx insn = p; - - maybe_multiple = 0; - - while (1) - { - insn = NEXT_INSN (insn); - if (insn == scan_start) - break; - if (insn == end) - { - if (loop_top != 0) - insn = NEXT_INSN (loop_top); - else - break; - if (insn == scan_start) - break; - } - - if (GET_CODE (insn) == JUMP_INSN - && GET_CODE (PATTERN (insn)) != RETURN - && (! condjump_p (insn) - || (JUMP_LABEL (insn) != 0 - && (INSN_UID (JUMP_LABEL (insn)) >= max_uid_for_loop - || INSN_UID (insn) >= max_uid_for_loop - || (INSN_LUID (JUMP_LABEL (insn)) - < INSN_LUID (insn)))))) - { - maybe_multiple = 1; - break; - } - } - } - - /* Past a label or a jump, we get to insns for which we can't count - on whether or how many times they will be executed during each - iteration. */ - /* This code appears in three places, once in scan_loop, and twice - in strength_reduce. */ - if ((GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN) - /* If we enter the loop in the middle, and scan around to the - beginning, don't set not_every_iteration for that. - This can be any kind of jump, since we want to know if insns - will be executed if the loop is executed. */ - && ! (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p) == loop_top - && ((NEXT_INSN (NEXT_INSN (p)) == loop_end && simplejump_p (p)) - || (NEXT_INSN (p) == loop_end && condjump_p (p))))) - not_every_iteration = 1; - - /* At the virtual top of a converted loop, insns are again known to - be executed each iteration: logically, the loop begins here - even though the exit code has been duplicated. */ - - else if (GET_CODE (p) == NOTE - && NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_VTOP) - not_every_iteration = 0; - - /* Unlike in the code motion pass where MAYBE_NEVER indicates that - an insn may never be executed, NOT_EVERY_ITERATION indicates whether - or not an insn is known to be executed each iteration of the - loop, whether or not any iterations are known to occur. - - Therefore, if we have just passed a label and have no more labels - between here and the test insn of the loop, we know these insns - will be executed each iteration. This can also happen if we - have just passed a jump, for example, when there are nested loops. */ - - if (not_every_iteration && GET_CODE (p) == CODE_LABEL - && no_labels_between_p (p, loop_end)) - not_every_iteration = 0; - } - - /* Scan loop_iv_list to remove all regs that proved not to be bivs. - Make a sanity check against n_times_set. */ - for (backbl = &loop_iv_list, bl = *backbl; bl; bl = bl->next) - { - if (reg_iv_type[bl->regno] != BASIC_INDUCT - /* Above happens if register modified by subreg, etc. */ - /* Make sure it is not recognized as a basic induction var: */ - || n_times_set[bl->regno] != bl->biv_count - /* If never incremented, it is invariant that we decided not to - move. So leave it alone. */ - || ! bl->incremented) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, "Reg %d: biv discarded, %s\n", - bl->regno, - (reg_iv_type[bl->regno] != BASIC_INDUCT - ? "not induction variable" - : (! bl->incremented ? "never incremented" - : "count error"))); - - reg_iv_type[bl->regno] = NOT_BASIC_INDUCT; - *backbl = bl->next; - } - else - { - backbl = &bl->next; - - if (loop_dump_stream) - fprintf (loop_dump_stream, "Reg %d: biv verified\n", bl->regno); - } - } - - /* Exit if there are no bivs. */ - if (! loop_iv_list) - { - /* Can still unroll the loop anyways, but indicate that there is no - strength reduction info available. */ - if (flag_unroll_loops) - unroll_loop (loop_end, insn_count, loop_start, end_insert_before, 0); - - return; - } - - /* Find initial value for each biv by searching backwards from loop_start, - halting at first label. Also record any test condition. */ - - call_seen = 0; - for (p = loop_start; p && GET_CODE (p) != CODE_LABEL; p = PREV_INSN (p)) - { - note_insn = p; - - if (GET_CODE (p) == CALL_INSN) - call_seen = 1; - - if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN - || GET_CODE (p) == CALL_INSN) - note_stores (PATTERN (p), record_initial); - - /* Record any test of a biv that branches around the loop if no store - between it and the start of loop. We only care about tests with - constants and registers and only certain of those. */ - if (GET_CODE (p) == JUMP_INSN - && JUMP_LABEL (p) != 0 - && next_real_insn (JUMP_LABEL (p)) == next_real_insn (loop_end) - && (test = get_condition_for_loop (p)) != 0 - && GET_CODE (XEXP (test, 0)) == REG - && REGNO (XEXP (test, 0)) < max_reg_before_loop - && (bl = reg_biv_class[REGNO (XEXP (test, 0))]) != 0 - && valid_initial_value_p (XEXP (test, 1), p, call_seen, loop_start) - && bl->init_insn == 0) - { - /* If an NE test, we have an initial value! */ - if (GET_CODE (test) == NE) - { - bl->init_insn = p; - bl->init_set = gen_rtx (SET, VOIDmode, - XEXP (test, 0), XEXP (test, 1)); - } - else - bl->initial_test = test; - } - } - - /* Look at the each biv and see if we can say anything better about its - initial value from any initializing insns set up above. (This is done - in two passes to avoid missing SETs in a PARALLEL.) */ - for (bl = loop_iv_list; bl; bl = bl->next) - { - rtx src; - - if (! bl->init_insn) - continue; - - src = SET_SRC (bl->init_set); - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Biv %d initialized at insn %d: initial value ", - bl->regno, INSN_UID (bl->init_insn)); - - if (valid_initial_value_p (src, bl->init_insn, call_seen, loop_start)) - { - bl->initial_value = src; - - if (loop_dump_stream) - { - if (GET_CODE (src) == CONST_INT) - fprintf (loop_dump_stream, "%d\n", INTVAL (src)); - else - { - print_rtl (loop_dump_stream, src); - fprintf (loop_dump_stream, "\n"); - } - } - } - else - { - /* Biv initial value is not simple move, - so let it keep initial value of "itself". */ - - if (loop_dump_stream) - fprintf (loop_dump_stream, "is complex\n"); - } - } - - /* Search the loop for general induction variables. */ - - /* A register is a giv if: it is only set once, it is a function of a - biv and a constant (or invariant), and it is not a biv. */ - - not_every_iteration = 0; - p = scan_start; - while (1) - { - p = NEXT_INSN (p); - /* At end of a straight-in loop, we are done. - At end of a loop entered at the bottom, scan the top. */ - if (p == scan_start) - break; - if (p == end) - { - if (loop_top != 0) - p = NEXT_INSN (loop_top); - else - break; - if (p == scan_start) - break; - } - - /* Look for a general induction variable in a register. */ - if (GET_CODE (p) == INSN - && (set = single_set (p)) - && GET_CODE (SET_DEST (set)) == REG - && ! may_not_optimize[REGNO (SET_DEST (set))]) - { - rtx src_reg; - rtx add_val; - rtx mult_val; - int benefit; - rtx regnote = 0; - - dest_reg = SET_DEST (set); - if (REGNO (dest_reg) < FIRST_PSEUDO_REGISTER) - continue; - - if (/* SET_SRC is a giv. */ - ((benefit = general_induction_var (SET_SRC (set), - &src_reg, &add_val, - &mult_val)) - /* Equivalent expression is a giv. */ - || ((regnote = find_reg_note (p, REG_EQUAL, NULL_RTX)) - && (benefit = general_induction_var (XEXP (regnote, 0), - &src_reg, - &add_val, &mult_val)))) - /* Don't try to handle any regs made by loop optimization. - We have nothing on them in regno_first_uid, etc. */ - && REGNO (dest_reg) < max_reg_before_loop - /* Don't recognize a BASIC_INDUCT_VAR here. */ - && dest_reg != src_reg - /* This must be the only place where the register is set. */ - && (n_times_set[REGNO (dest_reg)] == 1 - /* or all sets must be consecutive and make a giv. */ - || (benefit = consec_sets_giv (benefit, p, - src_reg, dest_reg, - &add_val, &mult_val)))) - { - int count; - struct induction *v - = (struct induction *) alloca (sizeof (struct induction)); - rtx temp; - - /* If this is a library call, increase benefit. */ - if (find_reg_note (p, REG_RETVAL, NULL_RTX)) - benefit += libcall_benefit (p); - - /* Skip the consecutive insns, if there are any. */ - for (count = n_times_set[REGNO (dest_reg)] - 1; - count > 0; count--) - { - /* If first insn of libcall sequence, skip to end. - Do this at start of loop, since INSN is guaranteed to - be an insn here. */ - if (GET_CODE (p) != NOTE - && (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX))) - p = XEXP (temp, 0); - - do p = NEXT_INSN (p); - while (GET_CODE (p) == NOTE); - } - - record_giv (v, p, src_reg, dest_reg, mult_val, add_val, benefit, - DEST_REG, not_every_iteration, NULL_PTR, loop_start, - loop_end); - - } - } - -#ifndef DONT_REDUCE_ADDR - /* Look for givs which are memory addresses. */ - /* This resulted in worse code on a VAX 8600. I wonder if it - still does. */ - if (GET_CODE (p) == INSN) - find_mem_givs (PATTERN (p), p, not_every_iteration, loop_start, - loop_end); -#endif - - /* Update the status of whether giv can derive other givs. This can - change when we pass a label or an insn that updates a biv. */ - if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN - || GET_CODE (p) == CODE_LABEL) - update_giv_derive (p); - - /* Past a label or a jump, we get to insns for which we can't count - on whether or how many times they will be executed during each - iteration. */ - /* This code appears in three places, once in scan_loop, and twice - in strength_reduce. */ - if ((GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN) - /* If we enter the loop in the middle, and scan around - to the beginning, don't set not_every_iteration for that. - This can be any kind of jump, since we want to know if insns - will be executed if the loop is executed. */ - && ! (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p) == loop_top - && ((NEXT_INSN (NEXT_INSN (p)) == loop_end && simplejump_p (p)) - || (NEXT_INSN (p) == loop_end && condjump_p (p))))) - not_every_iteration = 1; - - /* At the virtual top of a converted loop, insns are again known to - be executed each iteration: logically, the loop begins here - even though the exit code has been duplicated. */ - - else if (GET_CODE (p) == NOTE - && NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_VTOP) - not_every_iteration = 0; - - /* Unlike in the code motion pass where MAYBE_NEVER indicates that - an insn may never be executed, NOT_EVERY_ITERATION indicates whether - or not an insn is known to be executed each iteration of the - loop, whether or not any iterations are known to occur. - - Therefore, if we have just passed a label and have no more labels - between here and the test insn of the loop, we know these insns - will be executed each iteration. */ - - if (not_every_iteration && GET_CODE (p) == CODE_LABEL - && no_labels_between_p (p, loop_end)) - not_every_iteration = 0; - } - - /* Try to calculate and save the number of loop iterations. This is - set to zero if the actual number can not be calculated. This must - be called after all giv's have been identified, since otherwise it may - fail if the iteration variable is a giv. */ - - loop_n_iterations = loop_iterations (loop_start, loop_end); - - /* Now for each giv for which we still don't know whether or not it is - replaceable, check to see if it is replaceable because its final value - can be calculated. This must be done after loop_iterations is called, - so that final_giv_value will work correctly. */ - - for (bl = loop_iv_list; bl; bl = bl->next) - { - struct induction *v; - - for (v = bl->giv; v; v = v->next_iv) - if (! v->replaceable && ! v->not_replaceable) - check_final_value (v, loop_start, loop_end); - } - - /* Try to prove that the loop counter variable (if any) is always - nonnegative; if so, record that fact with a REG_NONNEG note - so that "decrement and branch until zero" insn can be used. */ - check_dbra_loop (loop_end, insn_count, loop_start); - - /* Create reg_map to hold substitutions for replaceable giv regs. */ - reg_map = (rtx *) alloca (max_reg_before_loop * sizeof (rtx)); - bzero ((char *) reg_map, max_reg_before_loop * sizeof (rtx)); - - /* Examine each iv class for feasibility of strength reduction/induction - variable elimination. */ - - for (bl = loop_iv_list; bl; bl = bl->next) - { - struct induction *v; - int benefit; - int all_reduced; - rtx final_value = 0; - - /* Test whether it will be possible to eliminate this biv - provided all givs are reduced. This is possible if either - the reg is not used outside the loop, or we can compute - what its final value will be. - - For architectures with a decrement_and_branch_until_zero insn, - don't do this if we put a REG_NONNEG note on the endtest for - this biv. */ - - /* Compare against bl->init_insn rather than loop_start. - We aren't concerned with any uses of the biv between - init_insn and loop_start since these won't be affected - by the value of the biv elsewhere in the function, so - long as init_insn doesn't use the biv itself. - March 14, 1989 -- self@bayes.arc.nasa.gov */ - - if ((uid_luid[regno_last_uid[bl->regno]] < INSN_LUID (loop_end) - && bl->init_insn - && INSN_UID (bl->init_insn) < max_uid_for_loop - && uid_luid[regno_first_uid[bl->regno]] >= INSN_LUID (bl->init_insn) -#ifdef HAVE_decrement_and_branch_until_zero - && ! bl->nonneg -#endif - && ! reg_mentioned_p (bl->biv->dest_reg, SET_SRC (bl->init_set))) - || ((final_value = final_biv_value (bl, loop_start, loop_end)) -#ifdef HAVE_decrement_and_branch_until_zero - && ! bl->nonneg -#endif - )) - bl->eliminable = maybe_eliminate_biv (bl, loop_start, end, 0, - threshold, insn_count); - else - { - if (loop_dump_stream) - { - fprintf (loop_dump_stream, - "Cannot eliminate biv %d.\n", - bl->regno); - fprintf (loop_dump_stream, - "First use: insn %d, last use: insn %d.\n", - regno_first_uid[bl->regno], - regno_last_uid[bl->regno]); - } - } - - /* Combine all giv's for this iv_class. */ - combine_givs (bl); - - /* This will be true at the end, if all givs which depend on this - biv have been strength reduced. - We can't (currently) eliminate the biv unless this is so. */ - all_reduced = 1; - - /* Check each giv in this class to see if we will benefit by reducing - it. Skip giv's combined with others. */ - for (v = bl->giv; v; v = v->next_iv) - { - struct induction *tv; - - if (v->ignore || v->same) - continue; - - benefit = v->benefit; - - /* Reduce benefit if not replaceable, since we will insert - a move-insn to replace the insn that calculates this giv. - Don't do this unless the giv is a user variable, since it - will often be marked non-replaceable because of the duplication - of the exit code outside the loop. In such a case, the copies - we insert are dead and will be deleted. So they don't have - a cost. Similar situations exist. */ - /* ??? The new final_[bg]iv_value code does a much better job - of finding replaceable giv's, and hence this code may no longer - be necessary. */ - if (! v->replaceable && ! bl->eliminable - && REG_USERVAR_P (v->dest_reg)) - benefit -= copy_cost; - - /* Decrease the benefit to count the add-insns that we will - insert to increment the reduced reg for the giv. */ - benefit -= add_cost * bl->biv_count; - - /* Decide whether to strength-reduce this giv or to leave the code - unchanged (recompute it from the biv each time it is used). - This decision can be made independently for each giv. */ - - /* ??? Perhaps attempt to guess whether autoincrement will handle - some of the new add insns; if so, can increase BENEFIT - (undo the subtraction of add_cost that was done above). */ - - /* If an insn is not to be strength reduced, then set its ignore - flag, and clear all_reduced. */ - - if (v->lifetime * threshold * benefit < insn_count) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "giv of insn %d not worth while, %d vs %d.\n", - INSN_UID (v->insn), - v->lifetime * threshold * benefit, insn_count); - v->ignore = 1; - all_reduced = 0; - } - else - { - /* Check that we can increment the reduced giv without a - multiply insn. If not, reject it. */ - - for (tv = bl->biv; tv; tv = tv->next_iv) - if (tv->mult_val == const1_rtx - && ! product_cheap_p (tv->add_val, v->mult_val)) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "giv of insn %d: would need a multiply.\n", - INSN_UID (v->insn)); - v->ignore = 1; - all_reduced = 0; - break; - } - } - } - - /* Reduce each giv that we decided to reduce. */ - - for (v = bl->giv; v; v = v->next_iv) - { - struct induction *tv; - if (! v->ignore && v->same == 0) - { - v->new_reg = gen_reg_rtx (v->mode); - - /* For each place where the biv is incremented, - add an insn to increment the new, reduced reg for the giv. */ - for (tv = bl->biv; tv; tv = tv->next_iv) - { - if (tv->mult_val == const1_rtx) - emit_iv_add_mult (tv->add_val, v->mult_val, - v->new_reg, v->new_reg, tv->insn); - else /* tv->mult_val == const0_rtx */ - /* A multiply is acceptable here - since this is presumed to be seldom executed. */ - emit_iv_add_mult (tv->add_val, v->mult_val, - v->add_val, v->new_reg, tv->insn); - } - - /* Add code at loop start to initialize giv's reduced reg. */ - - emit_iv_add_mult (bl->initial_value, v->mult_val, - v->add_val, v->new_reg, loop_start); - } - } - - /* Rescan all givs. If a giv is the same as a giv not reduced, mark it - as not reduced. - - For each giv register that can be reduced now: if replaceable, - substitute reduced reg wherever the old giv occurs; - else add new move insn "giv_reg = reduced_reg". - - Also check for givs whose first use is their definition and whose - last use is the definition of another giv. If so, it is likely - dead and should not be used to eliminate a biv. */ - for (v = bl->giv; v; v = v->next_iv) - { - if (v->same && v->same->ignore) - v->ignore = 1; - - if (v->ignore) - continue; - - if (v->giv_type == DEST_REG - && regno_first_uid[REGNO (v->dest_reg)] == INSN_UID (v->insn)) - { - struct induction *v1; - - for (v1 = bl->giv; v1; v1 = v1->next_iv) - if (regno_last_uid[REGNO (v->dest_reg)] == INSN_UID (v1->insn)) - v->maybe_dead = 1; - } - - /* Update expression if this was combined, in case other giv was - replaced. */ - if (v->same) - v->new_reg = replace_rtx (v->new_reg, - v->same->dest_reg, v->same->new_reg); - - if (v->giv_type == DEST_ADDR) - /* Store reduced reg as the address in the memref where we found - this giv. */ - *v->location = v->new_reg; - else if (v->replaceable) - { - reg_map[REGNO (v->dest_reg)] = v->new_reg; - -#if 0 - /* I can no longer duplicate the original problem. Perhaps - this is unnecessary now? */ - - /* Replaceable; it isn't strictly necessary to delete the old - insn and emit a new one, because v->dest_reg is now dead. - - However, especially when unrolling loops, the special - handling for (set REG0 REG1) in the second cse pass may - make v->dest_reg live again. To avoid this problem, emit - an insn to set the original giv reg from the reduced giv. - We can not delete the original insn, since it may be part - of a LIBCALL, and the code in flow that eliminates dead - libcalls will fail if it is deleted. */ - emit_insn_after (gen_move_insn (v->dest_reg, v->new_reg), - v->insn); -#endif - } - else - { - /* Not replaceable; emit an insn to set the original giv reg from - the reduced giv, same as above. */ - emit_insn_after (gen_move_insn (v->dest_reg, v->new_reg), - v->insn); - } - - /* When a loop is reversed, givs which depend on the reversed - biv, and which are live outside the loop, must be set to their - correct final value. This insn is only needed if the giv is - not replaceable. The correct final value is the same as the - value that the giv starts the reversed loop with. */ - if (bl->reversed && ! v->replaceable) - emit_iv_add_mult (bl->initial_value, v->mult_val, - v->add_val, v->dest_reg, end_insert_before); - else if (v->final_value) - { - rtx insert_before; - - /* If the loop has multiple exits, emit the insn before the - loop to ensure that it will always be executed no matter - how the loop exits. Otherwise, emit the insn after the loop, - since this is slightly more efficient. */ - if (loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]]) - insert_before = loop_start; - else - insert_before = end_insert_before; - emit_insn_before (gen_move_insn (v->dest_reg, v->final_value), - insert_before); - -#if 0 - /* If the insn to set the final value of the giv was emitted - before the loop, then we must delete the insn inside the loop - that sets it. If this is a LIBCALL, then we must delete - every insn in the libcall. Note, however, that - final_giv_value will only succeed when there are multiple - exits if the giv is dead at each exit, hence it does not - matter that the original insn remains because it is dead - anyways. */ - /* Delete the insn inside the loop that sets the giv since - the giv is now set before (or after) the loop. */ - delete_insn (v->insn); -#endif - } - - if (loop_dump_stream) - { - fprintf (loop_dump_stream, "giv at %d reduced to ", - INSN_UID (v->insn)); - print_rtl (loop_dump_stream, v->new_reg); - fprintf (loop_dump_stream, "\n"); - } - } - - /* All the givs based on the biv bl have been reduced if they - merit it. */ - - /* For each giv not marked as maybe dead that has been combined with a - second giv, clear any "maybe dead" mark on that second giv. - v->new_reg will either be or refer to the register of the giv it - combined with. - - Doing this clearing avoids problems in biv elimination where a - giv's new_reg is a complex value that can't be put in the insn but - the giv combined with (with a reg as new_reg) is marked maybe_dead. - Since the register will be used in either case, we'd prefer it be - used from the simpler giv. */ - - for (v = bl->giv; v; v = v->next_iv) - if (! v->maybe_dead && v->same) - v->same->maybe_dead = 0; - - /* Try to eliminate the biv, if it is a candidate. - This won't work if ! all_reduced, - since the givs we planned to use might not have been reduced. - - We have to be careful that we didn't initially think we could eliminate - this biv because of a giv that we now think may be dead and shouldn't - be used as a biv replacement. - - Also, there is the possibility that we may have a giv that looks - like it can be used to eliminate a biv, but the resulting insn - isn't valid. This can happen, for example, on the 88k, where a - JUMP_INSN can compare a register only with zero. Attempts to - replace it with a compare with a constant will fail. - - Note that in cases where this call fails, we may have replaced some - of the occurrences of the biv with a giv, but no harm was done in - doing so in the rare cases where it can occur. */ - - if (all_reduced == 1 && bl->eliminable - && maybe_eliminate_biv (bl, loop_start, end, 1, - threshold, insn_count)) - - { - /* ?? If we created a new test to bypass the loop entirely, - or otherwise drop straight in, based on this test, then - we might want to rewrite it also. This way some later - pass has more hope of removing the initialization of this - biv entirely. */ - - /* If final_value != 0, then the biv may be used after loop end - and we must emit an insn to set it just in case. - - Reversed bivs already have an insn after the loop setting their - value, so we don't need another one. We can't calculate the - proper final value for such a biv here anyways. */ - if (final_value != 0 && ! bl->reversed) - { - rtx insert_before; - - /* If the loop has multiple exits, emit the insn before the - loop to ensure that it will always be executed no matter - how the loop exits. Otherwise, emit the insn after the - loop, since this is slightly more efficient. */ - if (loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]]) - insert_before = loop_start; - else - insert_before = end_insert_before; - - emit_insn_before (gen_move_insn (bl->biv->dest_reg, final_value), - end_insert_before); - } - -#if 0 - /* Delete all of the instructions inside the loop which set - the biv, as they are all dead. If is safe to delete them, - because an insn setting a biv will never be part of a libcall. */ - /* However, deleting them will invalidate the regno_last_uid info, - so keeping them around is more convenient. Final_biv_value - will only succeed when there are multiple exits if the biv - is dead at each exit, hence it does not matter that the original - insn remains, because it is dead anyways. */ - for (v = bl->biv; v; v = v->next_iv) - delete_insn (v->insn); -#endif - - if (loop_dump_stream) - fprintf (loop_dump_stream, "Reg %d: biv eliminated\n", - bl->regno); - } - } - - /* Go through all the instructions in the loop, making all the - register substitutions scheduled in REG_MAP. */ - - for (p = loop_start; p != end; p = NEXT_INSN (p)) - if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN - || GET_CODE (p) == CALL_INSN) - { - replace_regs (PATTERN (p), reg_map, max_reg_before_loop, 0); - replace_regs (REG_NOTES (p), reg_map, max_reg_before_loop, 0); - INSN_CODE (p) = -1; - } - - /* Unroll loops from within strength reduction so that we can use the - induction variable information that strength_reduce has already - collected. */ - - if (flag_unroll_loops) - unroll_loop (loop_end, insn_count, loop_start, end_insert_before, 1); - - if (loop_dump_stream) - fprintf (loop_dump_stream, "\n"); -} - -/* Return 1 if X is a valid source for an initial value (or as value being - compared against in an initial test). - - X must be either a register or constant and must not be clobbered between - the current insn and the start of the loop. - - INSN is the insn containing X. */ - -static int -valid_initial_value_p (x, insn, call_seen, loop_start) - rtx x; - rtx insn; - int call_seen; - rtx loop_start; -{ - if (CONSTANT_P (x)) - return 1; - - /* Only consider pseudos we know about initialized in insns whose luids - we know. */ - if (GET_CODE (x) != REG - || REGNO (x) >= max_reg_before_loop) - return 0; - - /* Don't use call-clobbered registers across a call which clobbers it. On - some machines, don't use any hard registers at all. */ - if (REGNO (x) < FIRST_PSEUDO_REGISTER -#ifndef SMALL_REGISTER_CLASSES - && call_used_regs[REGNO (x)] && call_seen -#endif - ) - return 0; - - /* Don't use registers that have been clobbered before the start of the - loop. */ - if (reg_set_between_p (x, insn, loop_start)) - return 0; - - return 1; -} - -/* Scan X for memory refs and check each memory address - as a possible giv. INSN is the insn whose pattern X comes from. - NOT_EVERY_ITERATION is 1 if the insn might not be executed during - every loop iteration. */ - -static void -find_mem_givs (x, insn, not_every_iteration, loop_start, loop_end) - rtx x; - rtx insn; - int not_every_iteration; - rtx loop_start, loop_end; -{ - register int i, j; - register enum rtx_code code; - register char *fmt; - - if (x == 0) - return; - - code = GET_CODE (x); - switch (code) - { - case REG: - case CONST_INT: - case CONST: - case CONST_DOUBLE: - case SYMBOL_REF: - case LABEL_REF: - case PC: - case CC0: - case ADDR_VEC: - case ADDR_DIFF_VEC: - case USE: - case CLOBBER: - return; - - case MEM: - { - rtx src_reg; - rtx add_val; - rtx mult_val; - int benefit; - - benefit = general_induction_var (XEXP (x, 0), - &src_reg, &add_val, &mult_val); - - /* Don't make a DEST_ADDR giv with mult_val == 1 && add_val == 0. - Such a giv isn't useful. */ - if (benefit > 0 && (mult_val != const1_rtx || add_val != const0_rtx)) - { - /* Found one; record it. */ - struct induction *v - = (struct induction *) oballoc (sizeof (struct induction)); - - record_giv (v, insn, src_reg, addr_placeholder, mult_val, - add_val, benefit, DEST_ADDR, not_every_iteration, - &XEXP (x, 0), loop_start, loop_end); - - v->mem_mode = GET_MODE (x); - } - return; - } - } - - /* Recursively scan the subexpressions for other mem refs. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - find_mem_givs (XEXP (x, i), insn, not_every_iteration, loop_start, - loop_end); - else if (fmt[i] == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - find_mem_givs (XVECEXP (x, i, j), insn, not_every_iteration, - loop_start, loop_end); -} - -/* Fill in the data about one biv update. - V is the `struct induction' in which we record the biv. (It is - allocated by the caller, with alloca.) - INSN is the insn that sets it. - DEST_REG is the biv's reg. - - MULT_VAL is const1_rtx if the biv is being incremented here, in which case - INC_VAL is the increment. Otherwise, MULT_VAL is const0_rtx and the biv is - being set to INC_VAL. - - NOT_EVERY_ITERATION is nonzero if this biv update is not know to be - executed every iteration; MAYBE_MULTIPLE is nonzero if this biv update - can be executed more than once per iteration. If MAYBE_MULTIPLE - and NOT_EVERY_ITERATION are both zero, we know that the biv update is - executed exactly once per iteration. */ - -static void -record_biv (v, insn, dest_reg, inc_val, mult_val, - not_every_iteration, maybe_multiple) - struct induction *v; - rtx insn; - rtx dest_reg; - rtx inc_val; - rtx mult_val; - int not_every_iteration; - int maybe_multiple; -{ - struct iv_class *bl; - - v->insn = insn; - v->src_reg = dest_reg; - v->dest_reg = dest_reg; - v->mult_val = mult_val; - v->add_val = inc_val; - v->mode = GET_MODE (dest_reg); - v->always_computable = ! not_every_iteration; - v->maybe_multiple = maybe_multiple; - - /* Add this to the reg's iv_class, creating a class - if this is the first incrementation of the reg. */ - - bl = reg_biv_class[REGNO (dest_reg)]; - if (bl == 0) - { - /* Create and initialize new iv_class. */ - - bl = (struct iv_class *) oballoc (sizeof (struct iv_class)); - - bl->regno = REGNO (dest_reg); - bl->biv = 0; - bl->giv = 0; - bl->biv_count = 0; - bl->giv_count = 0; - - /* Set initial value to the reg itself. */ - bl->initial_value = dest_reg; - /* We haven't seen the initializing insn yet */ - bl->init_insn = 0; - bl->init_set = 0; - bl->initial_test = 0; - bl->incremented = 0; - bl->eliminable = 0; - bl->nonneg = 0; - bl->reversed = 0; - bl->total_benefit = 0; - - /* Add this class to loop_iv_list. */ - bl->next = loop_iv_list; - loop_iv_list = bl; - - /* Put it in the array of biv register classes. */ - reg_biv_class[REGNO (dest_reg)] = bl; - } - - /* Update IV_CLASS entry for this biv. */ - v->next_iv = bl->biv; - bl->biv = v; - bl->biv_count++; - if (mult_val == const1_rtx) - bl->incremented = 1; - - if (loop_dump_stream) - { - fprintf (loop_dump_stream, - "Insn %d: possible biv, reg %d,", - INSN_UID (insn), REGNO (dest_reg)); - if (GET_CODE (inc_val) == CONST_INT) - fprintf (loop_dump_stream, " const = %d\n", - INTVAL (inc_val)); - else - { - fprintf (loop_dump_stream, " const = "); - print_rtl (loop_dump_stream, inc_val); - fprintf (loop_dump_stream, "\n"); - } - } -} - -/* Fill in the data about one giv. - V is the `struct induction' in which we record the giv. (It is - allocated by the caller, with alloca.) - INSN is the insn that sets it. - BENEFIT estimates the savings from deleting this insn. - TYPE is DEST_REG or DEST_ADDR; it says whether the giv is computed - into a register or is used as a memory address. - - SRC_REG is the biv reg which the giv is computed from. - DEST_REG is the giv's reg (if the giv is stored in a reg). - MULT_VAL and ADD_VAL are the coefficients used to compute the giv. - LOCATION points to the place where this giv's value appears in INSN. */ - -static void -record_giv (v, insn, src_reg, dest_reg, mult_val, add_val, benefit, - type, not_every_iteration, location, loop_start, loop_end) - struct induction *v; - rtx insn; - rtx src_reg; - rtx dest_reg; - rtx mult_val, add_val; - int benefit; - enum g_types type; - int not_every_iteration; - rtx *location; - rtx loop_start, loop_end; -{ - struct induction *b; - struct iv_class *bl; - rtx set = single_set (insn); - rtx p; - - v->insn = insn; - v->src_reg = src_reg; - v->giv_type = type; - v->dest_reg = dest_reg; - v->mult_val = mult_val; - v->add_val = add_val; - v->benefit = benefit; - v->location = location; - v->cant_derive = 0; - v->combined_with = 0; - v->maybe_multiple = 0; - v->maybe_dead = 0; - v->derive_adjustment = 0; - v->same = 0; - v->ignore = 0; - v->new_reg = 0; - v->final_value = 0; - - /* The v->always_computable field is used in update_giv_derive, to - determine whether a giv can be used to derive another giv. For a - DEST_REG giv, INSN computes a new value for the giv, so its value - isn't computable if INSN insn't executed every iteration. - However, for a DEST_ADDR giv, INSN merely uses the value of the giv; - it does not compute a new value. Hence the value is always computable - regardless of whether INSN is executed each iteration. */ - - if (type == DEST_ADDR) - v->always_computable = 1; - else - v->always_computable = ! not_every_iteration; - - if (type == DEST_ADDR) - { - v->mode = GET_MODE (*location); - v->lifetime = 1; - v->times_used = 1; - } - else /* type == DEST_REG */ - { - v->mode = GET_MODE (SET_DEST (set)); - - v->lifetime = (uid_luid[regno_last_uid[REGNO (dest_reg)]] - - uid_luid[regno_first_uid[REGNO (dest_reg)]]); - - v->times_used = n_times_used[REGNO (dest_reg)]; - - /* If the lifetime is zero, it means that this register is - really a dead store. So mark this as a giv that can be - ignored. This will not prevent the biv from being eliminated. */ - if (v->lifetime == 0) - v->ignore = 1; - - reg_iv_type[REGNO (dest_reg)] = GENERAL_INDUCT; - reg_iv_info[REGNO (dest_reg)] = v; - } - - /* Add the giv to the class of givs computed from one biv. */ - - bl = reg_biv_class[REGNO (src_reg)]; - if (bl) - { - v->next_iv = bl->giv; - bl->giv = v; - /* Don't count DEST_ADDR. This is supposed to count the number of - insns that calculate givs. */ - if (type == DEST_REG) - bl->giv_count++; - bl->total_benefit += benefit; - } - else - /* Fatal error, biv missing for this giv? */ - abort (); - - if (type == DEST_ADDR) - v->replaceable = 1; - else - { - /* The giv can be replaced outright by the reduced register only if all - of the following conditions are true: - - the insn that sets the giv is always executed on any iteration - on which the giv is used at all - (there are two ways to deduce this: - either the insn is executed on every iteration, - or all uses follow that insn in the same basic block), - - the giv is not used outside the loop - - no assignments to the biv occur during the giv's lifetime. */ - - if (regno_first_uid[REGNO (dest_reg)] == INSN_UID (insn) - /* Previous line always fails if INSN was moved by loop opt. */ - && uid_luid[regno_last_uid[REGNO (dest_reg)]] < INSN_LUID (loop_end) - && (! not_every_iteration - || last_use_this_basic_block (dest_reg, insn))) - { - /* Now check that there are no assignments to the biv within the - giv's lifetime. This requires two separate checks. */ - - /* Check each biv update, and fail if any are between the first - and last use of the giv. - - If this loop contains an inner loop that was unrolled, then - the insn modifying the biv may have been emitted by the loop - unrolling code, and hence does not have a valid luid. Just - mark the biv as not replaceable in this case. It is not very - useful as a biv, because it is used in two different loops. - It is very unlikely that we would be able to optimize the giv - using this biv anyways. */ - - v->replaceable = 1; - for (b = bl->biv; b; b = b->next_iv) - { - if (INSN_UID (b->insn) >= max_uid_for_loop - || ((uid_luid[INSN_UID (b->insn)] - >= uid_luid[regno_first_uid[REGNO (dest_reg)]]) - && (uid_luid[INSN_UID (b->insn)] - <= uid_luid[regno_last_uid[REGNO (dest_reg)]]))) - { - v->replaceable = 0; - v->not_replaceable = 1; - break; - } - } - - /* Check each insn between the first and last use of the giv, - and fail if any of them are branches that jump to a named label - outside this range, but still inside the loop. This catches - cases of spaghetti code where the execution order of insns - is not linear, and hence the above test fails. For example, - in the following code, j is not replaceable: - for (i = 0; i < 100; ) { - L0: j = 4*i; goto L1; - L2: k = j; goto L3; - L1: i++; goto L2; - L3: ; } - printf ("k = %d\n", k); } - This test is conservative, but this test succeeds rarely enough - that it isn't a problem. See also check_final_value below. */ - - if (v->replaceable) - for (p = insn; - INSN_UID (p) >= max_uid_for_loop - || INSN_LUID (p) < uid_luid[regno_last_uid[REGNO (dest_reg)]]; - p = NEXT_INSN (p)) - { - if (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p) - && LABEL_NAME (JUMP_LABEL (p)) - && ((INSN_LUID (JUMP_LABEL (p)) > INSN_LUID (loop_start) - && (INSN_LUID (JUMP_LABEL (p)) - < uid_luid[regno_first_uid[REGNO (dest_reg)]])) - || (INSN_LUID (JUMP_LABEL (p)) < INSN_LUID (loop_end) - && (INSN_LUID (JUMP_LABEL (p)) - > uid_luid[regno_last_uid[REGNO (dest_reg)]])))) - { - v->replaceable = 0; - v->not_replaceable = 1; - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Found branch outside giv lifetime.\n"); - - break; - } - } - } - else - { - /* May still be replaceable, we don't have enough info here to - decide. */ - v->replaceable = 0; - v->not_replaceable = 0; - } - } - - if (loop_dump_stream) - { - if (type == DEST_REG) - fprintf (loop_dump_stream, "Insn %d: giv reg %d", - INSN_UID (insn), REGNO (dest_reg)); - else - fprintf (loop_dump_stream, "Insn %d: dest address", - INSN_UID (insn)); - - fprintf (loop_dump_stream, " src reg %d benefit %d", - REGNO (src_reg), v->benefit); - fprintf (loop_dump_stream, " used %d lifetime %d", - v->times_used, v->lifetime); - - if (v->replaceable) - fprintf (loop_dump_stream, " replaceable"); - - if (GET_CODE (mult_val) == CONST_INT) - fprintf (loop_dump_stream, " mult %d", - INTVAL (mult_val)); - else - { - fprintf (loop_dump_stream, " mult "); - print_rtl (loop_dump_stream, mult_val); - } - - if (GET_CODE (add_val) == CONST_INT) - fprintf (loop_dump_stream, " add %d", - INTVAL (add_val)); - else - { - fprintf (loop_dump_stream, " add "); - print_rtl (loop_dump_stream, add_val); - } - } - - if (loop_dump_stream) - fprintf (loop_dump_stream, "\n"); - -} - - -/* All this does is determine whether a giv can be made replaceable because - its final value can be calculated. This code can not be part of record_giv - above, because final_giv_value requires that the number of loop iterations - be known, and that can not be accurately calculated until after all givs - have been identified. */ - -static void -check_final_value (v, loop_start, loop_end) - struct induction *v; - rtx loop_start, loop_end; -{ - struct iv_class *bl; - rtx final_value = 0; - rtx tem; - - bl = reg_biv_class[REGNO (v->src_reg)]; - - /* DEST_ADDR givs will never reach here, because they are always marked - replaceable above in record_giv. */ - - /* The giv can be replaced outright by the reduced register only if all - of the following conditions are true: - - the insn that sets the giv is always executed on any iteration - on which the giv is used at all - (there are two ways to deduce this: - either the insn is executed on every iteration, - or all uses follow that insn in the same basic block), - - its final value can be calculated (this condition is different - than the one above in record_giv) - - no assignments to the biv occur during the giv's lifetime. */ - -#if 0 - /* This is only called now when replaceable is known to be false. */ - /* Clear replaceable, so that it won't confuse final_giv_value. */ - v->replaceable = 0; -#endif - - if ((final_value = final_giv_value (v, loop_start, loop_end)) - && (v->always_computable || last_use_this_basic_block (v->dest_reg, v->insn))) - { - int biv_increment_seen = 0; - rtx p = v->insn; - rtx last_giv_use; - - v->replaceable = 1; - - /* When trying to determine whether or not a biv increment occurs - during the lifetime of the giv, we can ignore uses of the variable - outside the loop because final_value is true. Hence we can not - use regno_last_uid and regno_first_uid as above in record_giv. */ - - /* Search the loop to determine whether any assignments to the - biv occur during the giv's lifetime. Start with the insn - that sets the giv, and search around the loop until we come - back to that insn again. - - Also fail if there is a jump within the giv's lifetime that jumps - to somewhere outside the lifetime but still within the loop. This - catches spaghetti code where the execution order is not linear, and - hence the above test fails. Here we assume that the giv lifetime - does not extend from one iteration of the loop to the next, so as - to make the test easier. Since the lifetime isn't known yet, - this requires two loops. See also record_giv above. */ - - last_giv_use = v->insn; - - while (1) - { - p = NEXT_INSN (p); - if (p == loop_end) - p = NEXT_INSN (loop_start); - if (p == v->insn) - break; - - if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN - || GET_CODE (p) == CALL_INSN) - { - if (biv_increment_seen) - { - if (reg_mentioned_p (v->dest_reg, PATTERN (p))) - { - v->replaceable = 0; - v->not_replaceable = 1; - break; - } - } - else if (GET_CODE (PATTERN (p)) == SET - && SET_DEST (PATTERN (p)) == v->src_reg) - biv_increment_seen = 1; - else if (reg_mentioned_p (v->dest_reg, PATTERN (p))) - last_giv_use = p; - } - } - - /* Now that the lifetime of the giv is known, check for branches - from within the lifetime to outside the lifetime if it is still - replaceable. */ - - if (v->replaceable) - { - p = v->insn; - while (1) - { - p = NEXT_INSN (p); - if (p == loop_end) - p = NEXT_INSN (loop_start); - if (p == last_giv_use) - break; - - if (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p) - && LABEL_NAME (JUMP_LABEL (p)) - && ((INSN_LUID (JUMP_LABEL (p)) < INSN_LUID (v->insn) - && INSN_LUID (JUMP_LABEL (p)) > INSN_LUID (loop_start)) - || (INSN_LUID (JUMP_LABEL (p)) > INSN_LUID (last_giv_use) - && INSN_LUID (JUMP_LABEL (p)) < INSN_LUID (loop_end)))) - { - v->replaceable = 0; - v->not_replaceable = 1; - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Found branch outside giv lifetime.\n"); - - break; - } - } - } - - /* If it is replaceable, then save the final value. */ - if (v->replaceable) - v->final_value = final_value; - } - - if (loop_dump_stream && v->replaceable) - fprintf (loop_dump_stream, "Insn %d: giv reg %d final_value replaceable\n", - INSN_UID (v->insn), REGNO (v->dest_reg)); -} - -/* Update the status of whether a giv can derive other givs. - - We need to do something special if there is or may be an update to the biv - between the time the giv is defined and the time it is used to derive - another giv. - - In addition, a giv that is only conditionally set is not allowed to - derive another giv once a label has been passed. - - The cases we look at are when a label or an update to a biv is passed. */ - -static void -update_giv_derive (p) - rtx p; -{ - struct iv_class *bl; - struct induction *biv, *giv; - rtx tem; - int dummy; - - /* Search all IV classes, then all bivs, and finally all givs. - - There are three cases we are concerned with. First we have the situation - of a giv that is only updated conditionally. In that case, it may not - derive any givs after a label is passed. - - The second case is when a biv update occurs, or may occur, after the - definition of a giv. For certain biv updates (see below) that are - known to occur between the giv definition and use, we can adjust the - giv definition. For others, or when the biv update is conditional, - we must prevent the giv from deriving any other givs. There are two - sub-cases within this case. - - If this is a label, we are concerned with any biv update that is done - conditionally, since it may be done after the giv is defined followed by - a branch here (actually, we need to pass both a jump and a label, but - this extra tracking doesn't seem worth it). - - If this is a jump, we are concerned about any biv update that may be - executed multiple times. We are actually only concerned about - backward jumps, but it is probably not worth performing the test - on the jump again here. - - If this is a biv update, we must adjust the giv status to show that a - subsequent biv update was performed. If this adjustment cannot be done, - the giv cannot derive further givs. */ - - for (bl = loop_iv_list; bl; bl = bl->next) - for (biv = bl->biv; biv; biv = biv->next_iv) - if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN - || biv->insn == p) - { - for (giv = bl->giv; giv; giv = giv->next_iv) - { - /* If cant_derive is already true, there is no point in - checking all of these conditions again. */ - if (giv->cant_derive) - continue; - - /* If this giv is conditionally set and we have passed a label, - it cannot derive anything. */ - if (GET_CODE (p) == CODE_LABEL && ! giv->always_computable) - giv->cant_derive = 1; - - /* Skip givs that have mult_val == 0, since - they are really invariants. Also skip those that are - replaceable, since we know their lifetime doesn't contain - any biv update. */ - else if (giv->mult_val == const0_rtx || giv->replaceable) - continue; - - /* The only way we can allow this giv to derive another - is if this is a biv increment and we can form the product - of biv->add_val and giv->mult_val. In this case, we will - be able to compute a compensation. */ - else if (biv->insn == p) - { - tem = 0; - - if (biv->mult_val == const1_rtx) - tem = simplify_giv_expr (gen_rtx (MULT, giv->mode, - biv->add_val, - giv->mult_val), - &dummy); - - if (tem && giv->derive_adjustment) - tem = simplify_giv_expr (gen_rtx (PLUS, giv->mode, tem, - giv->derive_adjustment), - &dummy); - if (tem) - giv->derive_adjustment = tem; - else - giv->cant_derive = 1; - } - else if ((GET_CODE (p) == CODE_LABEL && ! biv->always_computable) - || (GET_CODE (p) == JUMP_INSN && biv->maybe_multiple)) - giv->cant_derive = 1; - } - } -} - -/* Check whether an insn is an increment legitimate for a basic induction var. - X is the source of insn P. - DEST_REG is the putative biv, also the destination of the insn. - We accept patterns of these forms: - REG = REG + INVARIANT (includes REG = REG - CONSTANT) - REG = INVARIANT + REG - - If X is suitable, we return 1, set *MULT_VAL to CONST1_RTX, - and store the additive term into *INC_VAL. - - If X is an assignment of an invariant into DEST_REG, we set - *MULT_VAL to CONST0_RTX, and store the invariant into *INC_VAL. - - We also want to detect a BIV when it corresponds to a variable - whose mode was promoted via PROMOTED_MODE. In that case, an increment - of the variable may be a PLUS that adds a SUBREG of that variable to - an invariant and then sign- or zero-extends the result of the PLUS - into the variable. - - Most GIVs in such cases will be in the promoted mode, since that is the - probably the natural computation mode (and almost certainly the mode - used for addresses) on the machine. So we view the pseudo-reg containing - the variable as the BIV, as if it were simply incremented. - - Note that treating the entire pseudo as a BIV will result in making - simple increments to any GIVs based on it. However, if the variable - overflows in its declared mode but not its promoted mode, the result will - be incorrect. This is acceptable if the variable is signed, since - overflows in such cases are undefined, but not if it is unsigned, since - those overflows are defined. So we only check for SIGN_EXTEND and - not ZERO_EXTEND. - - If we cannot find a biv, we return 0. */ - -static int -basic_induction_var (x, dest_reg, p, inc_val, mult_val) - register rtx x; - rtx p; - rtx dest_reg; - rtx *inc_val; - rtx *mult_val; -{ - register enum rtx_code code; - rtx arg; - rtx insn, set = 0; - - code = GET_CODE (x); - switch (code) - { - case PLUS: - if (XEXP (x, 0) == dest_reg - || (GET_CODE (XEXP (x, 0)) == SUBREG - && SUBREG_PROMOTED_VAR_P (XEXP (x, 0)) - && SUBREG_REG (XEXP (x, 0)) == dest_reg)) - arg = XEXP (x, 1); - else if (XEXP (x, 1) == dest_reg - || (GET_CODE (XEXP (x, 1)) == SUBREG - && SUBREG_PROMOTED_VAR_P (XEXP (x, 1)) - && SUBREG_REG (XEXP (x, 1)) == dest_reg)) - arg = XEXP (x, 0); - else - return 0; - - if (invariant_p (arg) != 1) - return 0; - - *inc_val = convert_to_mode (GET_MODE (dest_reg), arg, 0);; - *mult_val = const1_rtx; - return 1; - - case SUBREG: - /* If this is a SUBREG for a promoted variable, check the inner - value. */ - if (SUBREG_PROMOTED_VAR_P (x)) - return basic_induction_var (SUBREG_REG (x), dest_reg, p, - inc_val, mult_val); - - case REG: - /* If this register is assigned in the previous insn, look at its - source, but don't go outside the loop or past a label. */ - - for (insn = PREV_INSN (p); - (insn && GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG); - insn = PREV_INSN (insn)) - ; - - if (insn) - set = single_set (insn); - - if (set != 0 && SET_DEST (set) == x) - return basic_induction_var (SET_SRC (set), dest_reg, insn, - inc_val, mult_val); - /* ... fall through ... */ - - /* Can accept constant setting of biv only when inside inner most loop. - Otherwise, a biv of an inner loop may be incorrectly recognized - as a biv of the outer loop, - causing code to be moved INTO the inner loop. */ - case MEM: - if (invariant_p (x) != 1) - return 0; - case CONST_INT: - case SYMBOL_REF: - case CONST: - if (loops_enclosed == 1) - { - *inc_val = convert_to_mode (GET_MODE (dest_reg), x, 0);; - *mult_val = const0_rtx; - return 1; - } - else - return 0; - - case SIGN_EXTEND: - return basic_induction_var (XEXP (x, 0), dest_reg, p, - inc_val, mult_val); - case ASHIFTRT: - /* Similar, since this can be a sign extension. */ - for (insn = PREV_INSN (p); - (insn && GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG); - insn = PREV_INSN (insn)) - ; - - if (insn) - set = single_set (insn); - - if (set && SET_DEST (set) == XEXP (x, 0) - && GET_CODE (XEXP (x, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) >= 0 - && GET_CODE (SET_SRC (set)) == ASHIFT - && XEXP (x, 1) == XEXP (SET_SRC (set), 1)) - return basic_induction_var (XEXP (SET_SRC (set), 0), dest_reg, insn, - inc_val, mult_val); - return 0; - - default: - return 0; - } -} - -/* A general induction variable (giv) is any quantity that is a linear - function of a basic induction variable, - i.e. giv = biv * mult_val + add_val. - The coefficients can be any loop invariant quantity. - A giv need not be computed directly from the biv; - it can be computed by way of other givs. */ - -/* Determine whether X computes a giv. - If it does, return a nonzero value - which is the benefit from eliminating the computation of X; - set *SRC_REG to the register of the biv that it is computed from; - set *ADD_VAL and *MULT_VAL to the coefficients, - such that the value of X is biv * mult + add; */ - -static int -general_induction_var (x, src_reg, add_val, mult_val) - rtx x; - rtx *src_reg; - rtx *add_val; - rtx *mult_val; -{ - rtx orig_x = x; - int benefit = 0; - char *storage; - - /* If this is an invariant, forget it, it isn't a giv. */ - if (invariant_p (x) == 1) - return 0; - - /* See if the expression could be a giv and get its form. - Mark our place on the obstack in case we don't find a giv. */ - storage = (char *) oballoc (0); - x = simplify_giv_expr (x, &benefit); - if (x == 0) - { - obfree (storage); - return 0; - } - - switch (GET_CODE (x)) - { - case USE: - case CONST_INT: - /* Since this is now an invariant and wasn't before, it must be a giv - with MULT_VAL == 0. It doesn't matter which BIV we associate this - with. */ - *src_reg = loop_iv_list->biv->dest_reg; - *mult_val = const0_rtx; - *add_val = x; - break; - - case REG: - /* This is equivalent to a BIV. */ - *src_reg = x; - *mult_val = const1_rtx; - *add_val = const0_rtx; - break; - - case PLUS: - /* Either (plus (biv) (invar)) or - (plus (mult (biv) (invar_1)) (invar_2)). */ - if (GET_CODE (XEXP (x, 0)) == MULT) - { - *src_reg = XEXP (XEXP (x, 0), 0); - *mult_val = XEXP (XEXP (x, 0), 1); - } - else - { - *src_reg = XEXP (x, 0); - *mult_val = const1_rtx; - } - *add_val = XEXP (x, 1); - break; - - case MULT: - /* ADD_VAL is zero. */ - *src_reg = XEXP (x, 0); - *mult_val = XEXP (x, 1); - *add_val = const0_rtx; - break; - - default: - abort (); - } - - /* Remove any enclosing USE from ADD_VAL and MULT_VAL (there will be - unless they are CONST_INT). */ - if (GET_CODE (*add_val) == USE) - *add_val = XEXP (*add_val, 0); - if (GET_CODE (*mult_val) == USE) - *mult_val = XEXP (*mult_val, 0); - - benefit += rtx_cost (orig_x, SET); - - /* Always return some benefit if this is a giv so it will be detected - as such. This allows elimination of bivs that might otherwise - not be eliminated. */ - return benefit == 0 ? 1 : benefit; -} - -/* Given an expression, X, try to form it as a linear function of a biv. - We will canonicalize it to be of the form - (plus (mult (BIV) (invar_1)) - (invar_2)) - with possible degeneracies. - - The invariant expressions must each be of a form that can be used as a - machine operand. We surround then with a USE rtx (a hack, but localized - and certainly unambiguous!) if not a CONST_INT for simplicity in this - routine; it is the caller's responsibility to strip them. - - If no such canonicalization is possible (i.e., two biv's are used or an - expression that is neither invariant nor a biv or giv), this routine - returns 0. - - For a non-zero return, the result will have a code of CONST_INT, USE, - REG (for a BIV), PLUS, or MULT. No other codes will occur. - - *BENEFIT will be incremented by the benefit of any sub-giv encountered. */ - -static rtx -simplify_giv_expr (x, benefit) - rtx x; - int *benefit; -{ - enum machine_mode mode = GET_MODE (x); - rtx arg0, arg1; - rtx tem; - - /* If this is not an integer mode, or if we cannot do arithmetic in this - mode, this can't be a giv. */ - if (mode != VOIDmode - && (GET_MODE_CLASS (mode) != MODE_INT - || GET_MODE_BITSIZE (mode) > HOST_BITS_PER_WIDE_INT)) - return 0; - - switch (GET_CODE (x)) - { - case PLUS: - arg0 = simplify_giv_expr (XEXP (x, 0), benefit); - arg1 = simplify_giv_expr (XEXP (x, 1), benefit); - if (arg0 == 0 || arg1 == 0) - return 0; - - /* Put constant last, CONST_INT last if both constant. */ - if ((GET_CODE (arg0) == USE - || GET_CODE (arg0) == CONST_INT) - && GET_CODE (arg1) != CONST_INT) - tem = arg0, arg0 = arg1, arg1 = tem; - - /* Handle addition of zero, then addition of an invariant. */ - if (arg1 == const0_rtx) - return arg0; - else if (GET_CODE (arg1) == CONST_INT || GET_CODE (arg1) == USE) - switch (GET_CODE (arg0)) - { - case CONST_INT: - case USE: - /* Both invariant. Only valid if sum is machine operand. - First strip off possible USE on first operand. */ - if (GET_CODE (arg0) == USE) - arg0 = XEXP (arg0, 0); - - tem = 0; - if (CONSTANT_P (arg0) && GET_CODE (arg1) == CONST_INT) - { - tem = plus_constant (arg0, INTVAL (arg1)); - if (GET_CODE (tem) != CONST_INT) - tem = gen_rtx (USE, mode, tem); - } - - return tem; - - case REG: - case MULT: - /* biv + invar or mult + invar. Return sum. */ - return gen_rtx (PLUS, mode, arg0, arg1); - - case PLUS: - /* (a + invar_1) + invar_2. Associate. */ - return simplify_giv_expr (gen_rtx (PLUS, mode, - XEXP (arg0, 0), - gen_rtx (PLUS, mode, - XEXP (arg0, 1), arg1)), - benefit); - - default: - abort (); - } - - /* Each argument must be either REG, PLUS, or MULT. Convert REG to - MULT to reduce cases. */ - if (GET_CODE (arg0) == REG) - arg0 = gen_rtx (MULT, mode, arg0, const1_rtx); - if (GET_CODE (arg1) == REG) - arg1 = gen_rtx (MULT, mode, arg1, const1_rtx); - - /* Now have PLUS + PLUS, PLUS + MULT, MULT + PLUS, or MULT + MULT. - Put a MULT first, leaving PLUS + PLUS, MULT + PLUS, or MULT + MULT. - Recurse to associate the second PLUS. */ - if (GET_CODE (arg1) == MULT) - tem = arg0, arg0 = arg1, arg1 = tem; - - if (GET_CODE (arg1) == PLUS) - return simplify_giv_expr (gen_rtx (PLUS, mode, - gen_rtx (PLUS, mode, - arg0, XEXP (arg1, 0)), - XEXP (arg1, 1)), - benefit); - - /* Now must have MULT + MULT. Distribute if same biv, else not giv. */ - if (GET_CODE (arg0) != MULT || GET_CODE (arg1) != MULT) - abort (); - - if (XEXP (arg0, 0) != XEXP (arg1, 0)) - return 0; - - return simplify_giv_expr (gen_rtx (MULT, mode, - XEXP (arg0, 0), - gen_rtx (PLUS, mode, - XEXP (arg0, 1), - XEXP (arg1, 1))), - benefit); - - case MINUS: - /* Handle "a - b" as "a + b * (-1)". */ - return simplify_giv_expr (gen_rtx (PLUS, mode, - XEXP (x, 0), - gen_rtx (MULT, mode, - XEXP (x, 1), constm1_rtx)), - benefit); - - case MULT: - arg0 = simplify_giv_expr (XEXP (x, 0), benefit); - arg1 = simplify_giv_expr (XEXP (x, 1), benefit); - if (arg0 == 0 || arg1 == 0) - return 0; - - /* Put constant last, CONST_INT last if both constant. */ - if ((GET_CODE (arg0) == USE || GET_CODE (arg0) == CONST_INT) - && GET_CODE (arg1) != CONST_INT) - tem = arg0, arg0 = arg1, arg1 = tem; - - /* If second argument is not now constant, not giv. */ - if (GET_CODE (arg1) != USE && GET_CODE (arg1) != CONST_INT) - return 0; - - /* Handle multiply by 0 or 1. */ - if (arg1 == const0_rtx) - return const0_rtx; - - else if (arg1 == const1_rtx) - return arg0; - - switch (GET_CODE (arg0)) - { - case REG: - /* biv * invar. Done. */ - return gen_rtx (MULT, mode, arg0, arg1); - - case CONST_INT: - /* Product of two constants. */ - return GEN_INT (INTVAL (arg0) * INTVAL (arg1)); - - case USE: - /* invar * invar. Not giv. */ - return 0; - - case MULT: - /* (a * invar_1) * invar_2. Associate. */ - return simplify_giv_expr (gen_rtx (MULT, mode, - XEXP (arg0, 0), - gen_rtx (MULT, mode, - XEXP (arg0, 1), arg1)), - benefit); - - case PLUS: - /* (a + invar_1) * invar_2. Distribute. */ - return simplify_giv_expr (gen_rtx (PLUS, mode, - gen_rtx (MULT, mode, - XEXP (arg0, 0), arg1), - gen_rtx (MULT, mode, - XEXP (arg0, 1), arg1)), - benefit); - - default: - abort (); - } - - case ASHIFT: - case LSHIFT: - /* Shift by constant is multiply by power of two. */ - if (GET_CODE (XEXP (x, 1)) != CONST_INT) - return 0; - - return simplify_giv_expr (gen_rtx (MULT, mode, - XEXP (x, 0), - GEN_INT ((HOST_WIDE_INT) 1 - << INTVAL (XEXP (x, 1)))), - benefit); - - case NEG: - /* "-a" is "a * (-1)" */ - return simplify_giv_expr (gen_rtx (MULT, mode, XEXP (x, 0), constm1_rtx), - benefit); - - case NOT: - /* "~a" is "-a - 1". Silly, but easy. */ - return simplify_giv_expr (gen_rtx (MINUS, mode, - gen_rtx (NEG, mode, XEXP (x, 0)), - const1_rtx), - benefit); - - case USE: - /* Already in proper form for invariant. */ - return x; - - case REG: - /* If this is a new register, we can't deal with it. */ - if (REGNO (x) >= max_reg_before_loop) - return 0; - - /* Check for biv or giv. */ - switch (reg_iv_type[REGNO (x)]) - { - case BASIC_INDUCT: - return x; - case GENERAL_INDUCT: - { - struct induction *v = reg_iv_info[REGNO (x)]; - - /* Form expression from giv and add benefit. Ensure this giv - can derive another and subtract any needed adjustment if so. */ - *benefit += v->benefit; - if (v->cant_derive) - return 0; - - tem = gen_rtx (PLUS, mode, gen_rtx (MULT, mode, - v->src_reg, v->mult_val), - v->add_val); - if (v->derive_adjustment) - tem = gen_rtx (MINUS, mode, tem, v->derive_adjustment); - return simplify_giv_expr (tem, benefit); - } - } - - /* Fall through to general case. */ - default: - /* If invariant, return as USE (unless CONST_INT). - Otherwise, not giv. */ - if (GET_CODE (x) == USE) - x = XEXP (x, 0); - - if (invariant_p (x) == 1) - { - if (GET_CODE (x) == CONST_INT) - return x; - else - return gen_rtx (USE, mode, x); - } - else - return 0; - } -} - -/* Help detect a giv that is calculated by several consecutive insns; - for example, - giv = biv * M - giv = giv + A - The caller has already identified the first insn P as having a giv as dest; - we check that all other insns that set the same register follow - immediately after P, that they alter nothing else, - and that the result of the last is still a giv. - - The value is 0 if the reg set in P is not really a giv. - Otherwise, the value is the amount gained by eliminating - all the consecutive insns that compute the value. - - FIRST_BENEFIT is the amount gained by eliminating the first insn, P. - SRC_REG is the reg of the biv; DEST_REG is the reg of the giv. - - The coefficients of the ultimate giv value are stored in - *MULT_VAL and *ADD_VAL. */ - -static int -consec_sets_giv (first_benefit, p, src_reg, dest_reg, - add_val, mult_val) - int first_benefit; - rtx p; - rtx src_reg; - rtx dest_reg; - rtx *add_val; - rtx *mult_val; -{ - int count; - enum rtx_code code; - int benefit; - rtx temp; - rtx set; - - /* Indicate that this is a giv so that we can update the value produced in - each insn of the multi-insn sequence. - - This induction structure will be used only by the call to - general_induction_var below, so we can allocate it on our stack. - If this is a giv, our caller will replace the induct var entry with - a new induction structure. */ - struct induction *v - = (struct induction *) alloca (sizeof (struct induction)); - v->src_reg = src_reg; - v->mult_val = *mult_val; - v->add_val = *add_val; - v->benefit = first_benefit; - v->cant_derive = 0; - v->derive_adjustment = 0; - - reg_iv_type[REGNO (dest_reg)] = GENERAL_INDUCT; - reg_iv_info[REGNO (dest_reg)] = v; - - count = n_times_set[REGNO (dest_reg)] - 1; - - while (count > 0) - { - p = NEXT_INSN (p); - code = GET_CODE (p); - - /* If libcall, skip to end of call sequence. */ - if (code == INSN && (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX))) - p = XEXP (temp, 0); - - if (code == INSN - && (set = single_set (p)) - && GET_CODE (SET_DEST (set)) == REG - && SET_DEST (set) == dest_reg - && ((benefit = general_induction_var (SET_SRC (set), &src_reg, - add_val, mult_val)) - /* Giv created by equivalent expression. */ - || ((temp = find_reg_note (p, REG_EQUAL, NULL_RTX)) - && (benefit = general_induction_var (XEXP (temp, 0), &src_reg, - add_val, mult_val)))) - && src_reg == v->src_reg) - { - if (find_reg_note (p, REG_RETVAL, NULL_RTX)) - benefit += libcall_benefit (p); - - count--; - v->mult_val = *mult_val; - v->add_val = *add_val; - v->benefit = benefit; - } - else if (code != NOTE) - { - /* Allow insns that set something other than this giv to a - constant. Such insns are needed on machines which cannot - include long constants and should not disqualify a giv. */ - if (code == INSN - && (set = single_set (p)) - && SET_DEST (set) != dest_reg - && CONSTANT_P (SET_SRC (set))) - continue; - - reg_iv_type[REGNO (dest_reg)] = UNKNOWN_INDUCT; - return 0; - } - } - - return v->benefit; -} - -/* Return an rtx, if any, that expresses giv G2 as a function of the register - represented by G1. If no such expression can be found, or it is clear that - it cannot possibly be a valid address, 0 is returned. - - To perform the computation, we note that - G1 = a * v + b and - G2 = c * v + d - where `v' is the biv. - - So G2 = (c/a) * G1 + (d - b*c/a) */ - -#ifdef ADDRESS_COST -static rtx -express_from (g1, g2) - struct induction *g1, *g2; -{ - rtx mult, add; - - /* The value that G1 will be multiplied by must be a constant integer. Also, - the only chance we have of getting a valid address is if b*c/a (see above - for notation) is also an integer. */ - if (GET_CODE (g1->mult_val) != CONST_INT - || GET_CODE (g2->mult_val) != CONST_INT - || GET_CODE (g1->add_val) != CONST_INT - || g1->mult_val == const0_rtx - || INTVAL (g2->mult_val) % INTVAL (g1->mult_val) != 0) - return 0; - - mult = GEN_INT (INTVAL (g2->mult_val) / INTVAL (g1->mult_val)); - add = plus_constant (g2->add_val, - INTVAL (g1->add_val) * INTVAL (mult)); - - /* Form simplified final result. */ - if (mult == const0_rtx) - return add; - else if (mult == const1_rtx) - mult = g1->dest_reg; - else - mult = gen_rtx (MULT, g2->mode, g1->dest_reg, mult); - - if (add == const0_rtx) - return mult; - else - return gen_rtx (PLUS, g2->mode, mult, add); -} -#endif - -/* Return 1 if giv G2 can be combined with G1. This means that G2 can use - (either directly or via an address expression) a register used to represent - G1. Set g2->new_reg to a represtation of G1 (normally just - g1->dest_reg). */ - -static int -combine_givs_p (g1, g2) - struct induction *g1, *g2; -{ - rtx tem; - - /* If these givs are identical, they can be combined. */ - if (rtx_equal_p (g1->mult_val, g2->mult_val) - && rtx_equal_p (g1->add_val, g2->add_val)) - { - g2->new_reg = g1->dest_reg; - return 1; - } - -#ifdef ADDRESS_COST - /* If G2 can be expressed as a function of G1 and that function is valid - as an address and no more expensive than using a register for G2, - the expression of G2 in terms of G1 can be used. */ - if (g2->giv_type == DEST_ADDR - && (tem = express_from (g1, g2)) != 0 - && memory_address_p (g2->mem_mode, tem) - && ADDRESS_COST (tem) <= ADDRESS_COST (*g2->location)) - { - g2->new_reg = tem; - return 1; - } -#endif - - return 0; -} - -/* Check all pairs of givs for iv_class BL and see if any can be combined with - any other. If so, point SAME to the giv combined with and set NEW_REG to - be an expression (in terms of the other giv's DEST_REG) equivalent to the - giv. Also, update BENEFIT and related fields for cost/benefit analysis. */ - -static void -combine_givs (bl) - struct iv_class *bl; -{ - struct induction *g1, *g2; - int pass; - - for (g1 = bl->giv; g1; g1 = g1->next_iv) - for (pass = 0; pass <= 1; pass++) - for (g2 = bl->giv; g2; g2 = g2->next_iv) - if (g1 != g2 - /* First try to combine with replaceable givs, then all givs. */ - && (g1->replaceable || pass == 1) - /* If either has already been combined or is to be ignored, can't - combine. */ - && ! g1->ignore && ! g2->ignore && ! g1->same && ! g2->same - /* If something has been based on G2, G2 cannot itself be based - on something else. */ - && ! g2->combined_with - && combine_givs_p (g1, g2)) - { - /* g2->new_reg set by `combine_givs_p' */ - g2->same = g1; - g1->combined_with = 1; - g1->benefit += g2->benefit; - /* ??? The new final_[bg]iv_value code does a much better job - of finding replaceable giv's, and hence this code may no - longer be necessary. */ - if (! g2->replaceable && REG_USERVAR_P (g2->dest_reg)) - g1->benefit -= copy_cost; - g1->lifetime += g2->lifetime; - g1->times_used += g2->times_used; - - if (loop_dump_stream) - fprintf (loop_dump_stream, "giv at %d combined with giv at %d\n", - INSN_UID (g2->insn), INSN_UID (g1->insn)); - } -} - -/* EMIT code before INSERT_BEFORE to set REG = B * M + A. */ - -void -emit_iv_add_mult (b, m, a, reg, insert_before) - rtx b; /* initial value of basic induction variable */ - rtx m; /* multiplicative constant */ - rtx a; /* additive constant */ - rtx reg; /* destination register */ - rtx insert_before; -{ - rtx seq; - rtx result; - - /* Prevent unexpected sharing of these rtx. */ - a = copy_rtx (a); - b = copy_rtx (b); - - /* Increase the lifetime of any invariants moved further in code. */ - update_reg_last_use (a, insert_before); - update_reg_last_use (b, insert_before); - update_reg_last_use (m, insert_before); - - start_sequence (); - result = expand_mult_add (b, reg, m, a, GET_MODE (reg), 0); - if (reg != result) - emit_move_insn (reg, result); - seq = gen_sequence (); - end_sequence (); - - emit_insn_before (seq, insert_before); -} - -/* Test whether A * B can be computed without - an actual multiply insn. Value is 1 if so. */ - -static int -product_cheap_p (a, b) - rtx a; - rtx b; -{ - int i; - rtx tmp; - struct obstack *old_rtl_obstack = rtl_obstack; - char *storage = (char *) obstack_alloc (&temp_obstack, 0); - int win = 1; - - /* If only one is constant, make it B. */ - if (GET_CODE (a) == CONST_INT) - tmp = a, a = b, b = tmp; - - /* If first constant, both constant, so don't need multiply. */ - if (GET_CODE (a) == CONST_INT) - return 1; - - /* If second not constant, neither is constant, so would need multiply. */ - if (GET_CODE (b) != CONST_INT) - return 0; - - /* One operand is constant, so might not need multiply insn. Generate the - code for the multiply and see if a call or multiply, or long sequence - of insns is generated. */ - - rtl_obstack = &temp_obstack; - start_sequence (); - expand_mult (GET_MODE (a), a, b, NULL_RTX, 0); - tmp = gen_sequence (); - end_sequence (); - - if (GET_CODE (tmp) == SEQUENCE) - { - if (XVEC (tmp, 0) == 0) - win = 1; - else if (XVECLEN (tmp, 0) > 3) - win = 0; - else - for (i = 0; i < XVECLEN (tmp, 0); i++) - { - rtx insn = XVECEXP (tmp, 0, i); - - if (GET_CODE (insn) != INSN - || (GET_CODE (PATTERN (insn)) == SET - && GET_CODE (SET_SRC (PATTERN (insn))) == MULT) - || (GET_CODE (PATTERN (insn)) == PARALLEL - && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET - && GET_CODE (SET_SRC (XVECEXP (PATTERN (insn), 0, 0))) == MULT)) - { - win = 0; - break; - } - } - } - else if (GET_CODE (tmp) == SET - && GET_CODE (SET_SRC (tmp)) == MULT) - win = 0; - else if (GET_CODE (tmp) == PARALLEL - && GET_CODE (XVECEXP (tmp, 0, 0)) == SET - && GET_CODE (SET_SRC (XVECEXP (tmp, 0, 0))) == MULT) - win = 0; - - /* Free any storage we obtained in generating this multiply and restore rtl - allocation to its normal obstack. */ - obstack_free (&temp_obstack, storage); - rtl_obstack = old_rtl_obstack; - - return win; -} - -/* Check to see if loop can be terminated by a "decrement and branch until - zero" instruction. If so, add a REG_NONNEG note to the branch insn if so. - Also try reversing an increment loop to a decrement loop - to see if the optimization can be performed. - Value is nonzero if optimization was performed. */ - -/* This is useful even if the architecture doesn't have such an insn, - because it might change a loops which increments from 0 to n to a loop - which decrements from n to 0. A loop that decrements to zero is usually - faster than one that increments from zero. */ - -/* ??? This could be rewritten to use some of the loop unrolling procedures, - such as approx_final_value, biv_total_increment, loop_iterations, and - final_[bg]iv_value. */ - -static int -check_dbra_loop (loop_end, insn_count, loop_start) - rtx loop_end; - int insn_count; - rtx loop_start; -{ - struct iv_class *bl; - rtx reg; - rtx jump_label; - rtx final_value; - rtx start_value; - enum rtx_code branch_code; - rtx new_add_val; - rtx comparison; - rtx before_comparison; - rtx p; - - /* If last insn is a conditional branch, and the insn before tests a - register value, try to optimize it. Otherwise, we can't do anything. */ - - comparison = get_condition_for_loop (PREV_INSN (loop_end)); - if (comparison == 0) - return 0; - - /* Check all of the bivs to see if the compare uses one of them. - Skip biv's set more than once because we can't guarantee that - it will be zero on the last iteration. Also skip if the biv is - used between its update and the test insn. */ - - for (bl = loop_iv_list; bl; bl = bl->next) - { - if (bl->biv_count == 1 - && bl->biv->dest_reg == XEXP (comparison, 0) - && ! reg_used_between_p (regno_reg_rtx[bl->regno], bl->biv->insn, - PREV_INSN (PREV_INSN (loop_end)))) - break; - } - - if (! bl) - return 0; - - /* Look for the case where the basic induction variable is always - nonnegative, and equals zero on the last iteration. - In this case, add a reg_note REG_NONNEG, which allows the - m68k DBRA instruction to be used. */ - - if (((GET_CODE (comparison) == GT - && GET_CODE (XEXP (comparison, 1)) == CONST_INT - && INTVAL (XEXP (comparison, 1)) == -1) - || (GET_CODE (comparison) == NE && XEXP (comparison, 1) == const0_rtx)) - && GET_CODE (bl->biv->add_val) == CONST_INT - && INTVAL (bl->biv->add_val) < 0) - { - /* Initial value must be greater than 0, - init_val % -dec_value == 0 to ensure that it equals zero on - the last iteration */ - - if (GET_CODE (bl->initial_value) == CONST_INT - && INTVAL (bl->initial_value) > 0 - && (INTVAL (bl->initial_value) % - (-INTVAL (bl->biv->add_val))) == 0) - { - /* register always nonnegative, add REG_NOTE to branch */ - REG_NOTES (PREV_INSN (loop_end)) - = gen_rtx (EXPR_LIST, REG_NONNEG, NULL_RTX, - REG_NOTES (PREV_INSN (loop_end))); - bl->nonneg = 1; - - return 1; - } - - /* If the decrement is 1 and the value was tested as >= 0 before - the loop, then we can safely optimize. */ - for (p = loop_start; p; p = PREV_INSN (p)) - { - if (GET_CODE (p) == CODE_LABEL) - break; - if (GET_CODE (p) != JUMP_INSN) - continue; - - before_comparison = get_condition_for_loop (p); - if (before_comparison - && XEXP (before_comparison, 0) == bl->biv->dest_reg - && GET_CODE (before_comparison) == LT - && XEXP (before_comparison, 1) == const0_rtx - && ! reg_set_between_p (bl->biv->dest_reg, p, loop_start) - && INTVAL (bl->biv->add_val) == -1) - { - REG_NOTES (PREV_INSN (loop_end)) - = gen_rtx (EXPR_LIST, REG_NONNEG, NULL_RTX, - REG_NOTES (PREV_INSN (loop_end))); - bl->nonneg = 1; - - return 1; - } - } - } - else if (num_mem_sets <= 1) - { - /* Try to change inc to dec, so can apply above optimization. */ - /* Can do this if: - all registers modified are induction variables or invariant, - all memory references have non-overlapping addresses - (obviously true if only one write) - allow 2 insns for the compare/jump at the end of the loop. */ - int num_nonfixed_reads = 0; - /* 1 if the iteration var is used only to count iterations. */ - int no_use_except_counting = 0; - - for (p = loop_start; p != loop_end; p = NEXT_INSN (p)) - if (GET_RTX_CLASS (GET_CODE (p)) == 'i') - num_nonfixed_reads += count_nonfixed_reads (PATTERN (p)); - - if (bl->giv_count == 0 - && ! loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]]) - { - rtx bivreg = regno_reg_rtx[bl->regno]; - - /* If there are no givs for this biv, and the only exit is the - fall through at the end of the the loop, then - see if perhaps there are no uses except to count. */ - no_use_except_counting = 1; - for (p = loop_start; p != loop_end; p = NEXT_INSN (p)) - if (GET_RTX_CLASS (GET_CODE (p)) == 'i') - { - rtx set = single_set (p); - - if (set && GET_CODE (SET_DEST (set)) == REG - && REGNO (SET_DEST (set)) == bl->regno) - /* An insn that sets the biv is okay. */ - ; - else if (p == prev_nonnote_insn (prev_nonnote_insn (loop_end)) - || p == prev_nonnote_insn (loop_end)) - /* Don't bother about the end test. */ - ; - else if (reg_mentioned_p (bivreg, PATTERN (p))) - /* Any other use of the biv is no good. */ - { - no_use_except_counting = 0; - break; - } - } - } - - /* This code only acts for innermost loops. Also it simplifies - the memory address check by only reversing loops with - zero or one memory access. - Two memory accesses could involve parts of the same array, - and that can't be reversed. */ - - if (num_nonfixed_reads <= 1 - && !loop_has_call - && !loop_has_volatile - && (no_use_except_counting - || (bl->giv_count + bl->biv_count + num_mem_sets - + num_movables + 2 == insn_count))) - { - rtx condition = get_condition_for_loop (PREV_INSN (loop_end)); - int win; - rtx tem; - - /* Loop can be reversed. */ - if (loop_dump_stream) - fprintf (loop_dump_stream, "Can reverse loop\n"); - - /* Now check other conditions: - initial_value must be zero, - final_value % add_val == 0, so that when reversed, the - biv will be zero on the last iteration. - - This test can probably be improved since +/- 1 in the constant - can be obtained by changing LT to LE and vice versa; this is - confusing. */ - - if (comparison && bl->initial_value == const0_rtx - && GET_CODE (XEXP (comparison, 1)) == CONST_INT - /* LE gets turned into LT */ - && GET_CODE (comparison) == LT - && (INTVAL (XEXP (comparison, 1)) - % INTVAL (bl->biv->add_val)) == 0) - { - /* Register will always be nonnegative, with value - 0 on last iteration if loop reversed */ - - /* Save some info needed to produce the new insns. */ - reg = bl->biv->dest_reg; - jump_label = XEXP (SET_SRC (PATTERN (PREV_INSN (loop_end))), 1); - new_add_val = GEN_INT (- INTVAL (bl->biv->add_val)); - - final_value = XEXP (comparison, 1); - start_value = GEN_INT (INTVAL (XEXP (comparison, 1)) - - INTVAL (bl->biv->add_val)); - - /* Initialize biv to start_value before loop start. - The old initializing insn will be deleted as a - dead store by flow.c. */ - emit_insn_before (gen_move_insn (reg, start_value), loop_start); - - /* Add insn to decrement register, and delete insn - that incremented the register. */ - p = emit_insn_before (gen_add2_insn (reg, new_add_val), - bl->biv->insn); - delete_insn (bl->biv->insn); - - /* Update biv info to reflect its new status. */ - bl->biv->insn = p; - bl->initial_value = start_value; - bl->biv->add_val = new_add_val; - - /* Inc LABEL_NUSES so that delete_insn will - not delete the label. */ - LABEL_NUSES (XEXP (jump_label, 0)) ++; - - /* Emit an insn after the end of the loop to set the biv's - proper exit value if it is used anywhere outside the loop. */ - if ((regno_last_uid[bl->regno] - != INSN_UID (PREV_INSN (PREV_INSN (loop_end)))) - || ! bl->init_insn - || regno_first_uid[bl->regno] != INSN_UID (bl->init_insn)) - emit_insn_after (gen_move_insn (reg, final_value), - loop_end); - - /* Delete compare/branch at end of loop. */ - delete_insn (PREV_INSN (loop_end)); - delete_insn (PREV_INSN (loop_end)); - - /* Add new compare/branch insn at end of loop. */ - start_sequence (); - emit_cmp_insn (reg, const0_rtx, GE, NULL_RTX, - GET_MODE (reg), 0, 0); - emit_jump_insn (gen_bge (XEXP (jump_label, 0))); - tem = gen_sequence (); - end_sequence (); - emit_jump_insn_before (tem, loop_end); - - for (tem = PREV_INSN (loop_end); - tem && GET_CODE (tem) != JUMP_INSN; tem = PREV_INSN (tem)) - ; - if (tem) - { - JUMP_LABEL (tem) = XEXP (jump_label, 0); - - /* Increment of LABEL_NUSES done above. */ - /* Register is now always nonnegative, - so add REG_NONNEG note to the branch. */ - REG_NOTES (tem) = gen_rtx (EXPR_LIST, REG_NONNEG, NULL_RTX, - REG_NOTES (tem)); - } - - bl->nonneg = 1; - - /* Mark that this biv has been reversed. Each giv which depends - on this biv, and which is also live past the end of the loop - will have to be fixed up. */ - - bl->reversed = 1; - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Reversed loop and added reg_nonneg\n"); - - return 1; - } - } - } - - return 0; -} - -/* Verify whether the biv BL appears to be eliminable, - based on the insns in the loop that refer to it. - LOOP_START is the first insn of the loop, and END is the end insn. - - If ELIMINATE_P is non-zero, actually do the elimination. - - THRESHOLD and INSN_COUNT are from loop_optimize and are used to - determine whether invariant insns should be placed inside or at the - start of the loop. */ - -static int -maybe_eliminate_biv (bl, loop_start, end, eliminate_p, threshold, insn_count) - struct iv_class *bl; - rtx loop_start; - rtx end; - int eliminate_p; - int threshold, insn_count; -{ - rtx reg = bl->biv->dest_reg; - rtx p, set; - struct induction *v; - - /* Scan all insns in the loop, stopping if we find one that uses the - biv in a way that we cannot eliminate. */ - - for (p = loop_start; p != end; p = NEXT_INSN (p)) - { - enum rtx_code code = GET_CODE (p); - rtx where = threshold >= insn_count ? loop_start : p; - - if ((code == INSN || code == JUMP_INSN || code == CALL_INSN) - && reg_mentioned_p (reg, PATTERN (p)) - && ! maybe_eliminate_biv_1 (PATTERN (p), p, bl, eliminate_p, where)) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Cannot eliminate biv %d: biv used in insn %d.\n", - bl->regno, INSN_UID (p)); - break; - } - } - - if (p == end) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, "biv %d %s eliminated.\n", - bl->regno, eliminate_p ? "was" : "can be"); - return 1; - } - - return 0; -} - -/* If BL appears in X (part of the pattern of INSN), see if we can - eliminate its use. If so, return 1. If not, return 0. - - If BIV does not appear in X, return 1. - - If ELIMINATE_P is non-zero, actually do the elimination. WHERE indicates - where extra insns should be added. Depending on how many items have been - moved out of the loop, it will either be before INSN or at the start of - the loop. */ - -static int -maybe_eliminate_biv_1 (x, insn, bl, eliminate_p, where) - rtx x, insn; - struct iv_class *bl; - int eliminate_p; - rtx where; -{ - enum rtx_code code = GET_CODE (x); - rtx reg = bl->biv->dest_reg; - enum machine_mode mode = GET_MODE (reg); - struct induction *v; - rtx arg, new, tem; - int arg_operand; - char *fmt; - int i, j; - - switch (code) - { - case REG: - /* If we haven't already been able to do something with this BIV, - we can't eliminate it. */ - if (x == reg) - return 0; - return 1; - - case SET: - /* If this sets the BIV, it is not a problem. */ - if (SET_DEST (x) == reg) - return 1; - - /* If this is an insn that defines a giv, it is also ok because - it will go away when the giv is reduced. */ - for (v = bl->giv; v; v = v->next_iv) - if (v->giv_type == DEST_REG && SET_DEST (x) == v->dest_reg) - return 1; - -#ifdef HAVE_cc0 - if (SET_DEST (x) == cc0_rtx && SET_SRC (x) == reg) - { - /* Can replace with any giv that was reduced and - that has (MULT_VAL != 0) and (ADD_VAL == 0). - Require a constant for MULT_VAL, so we know it's nonzero. */ - - for (v = bl->giv; v; v = v->next_iv) - if (CONSTANT_P (v->mult_val) && v->mult_val != const0_rtx - && v->add_val == const0_rtx - && ! v->ignore && ! v->maybe_dead - && v->mode == mode) - { - if (! eliminate_p) - return 1; - - /* If the giv has the opposite direction of change, - then reverse the comparison. */ - if (INTVAL (v->mult_val) < 0) - new = gen_rtx (COMPARE, GET_MODE (v->new_reg), - const0_rtx, v->new_reg); - else - new = v->new_reg; - - /* We can probably test that giv's reduced reg. */ - if (validate_change (insn, &SET_SRC (x), new, 0)) - return 1; - } - - /* Look for a giv with (MULT_VAL != 0) and (ADD_VAL != 0); - replace test insn with a compare insn (cmp REDUCED_GIV ADD_VAL). - Require a constant for MULT_VAL, so we know it's nonzero. */ - - for (v = bl->giv; v; v = v->next_iv) - if (CONSTANT_P (v->mult_val) && v->mult_val != const0_rtx - && ! v->ignore && ! v->maybe_dead - && v->mode == mode) - { - if (! eliminate_p) - return 1; - - /* If the giv has the opposite direction of change, - then reverse the comparison. */ - if (INTVAL (v->mult_val) < 0) - new = gen_rtx (COMPARE, VOIDmode, copy_rtx (v->add_val), - v->new_reg); - else - new = gen_rtx (COMPARE, VOIDmode, v->new_reg, - copy_rtx (v->add_val)); - - /* Replace biv with the giv's reduced register. */ - update_reg_last_use (v->add_val, insn); - if (validate_change (insn, &SET_SRC (PATTERN (insn)), new, 0)) - return 1; - - /* Insn doesn't support that constant or invariant. Copy it - into a register (it will be a loop invariant.) */ - tem = gen_reg_rtx (GET_MODE (v->new_reg)); - - emit_insn_before (gen_move_insn (tem, copy_rtx (v->add_val)), - where); - - if (validate_change (insn, &SET_SRC (PATTERN (insn)), - gen_rtx (COMPARE, VOIDmode, - v->new_reg, tem), 0)) - return 1; - } - } -#endif - break; - - case COMPARE: - case EQ: case NE: - case GT: case GE: case GTU: case GEU: - case LT: case LE: case LTU: case LEU: - /* See if either argument is the biv. */ - if (XEXP (x, 0) == reg) - arg = XEXP (x, 1), arg_operand = 1; - else if (XEXP (x, 1) == reg) - arg = XEXP (x, 0), arg_operand = 0; - else - break; - - if (CONSTANT_P (arg)) - { - /* First try to replace with any giv that has constant positive - mult_val and constant add_val. We might be able to support - negative mult_val, but it seems complex to do it in general. */ - - for (v = bl->giv; v; v = v->next_iv) - if (CONSTANT_P (v->mult_val) && INTVAL (v->mult_val) > 0 - && CONSTANT_P (v->add_val) - && ! v->ignore && ! v->maybe_dead - && v->mode == mode) - { - if (! eliminate_p) - return 1; - - /* Replace biv with the giv's reduced reg. */ - XEXP (x, 1-arg_operand) = v->new_reg; - - /* If all constants are actually constant integers and - the derived constant can be directly placed in the COMPARE, - do so. */ - if (GET_CODE (arg) == CONST_INT - && GET_CODE (v->mult_val) == CONST_INT - && GET_CODE (v->add_val) == CONST_INT - && validate_change (insn, &XEXP (x, arg_operand), - GEN_INT (INTVAL (arg) - * INTVAL (v->mult_val) - + INTVAL (v->add_val)), 0)) - return 1; - - /* Otherwise, load it into a register. */ - tem = gen_reg_rtx (mode); - emit_iv_add_mult (arg, v->mult_val, v->add_val, tem, where); - if (validate_change (insn, &XEXP (x, arg_operand), tem, 0)) - return 1; - - /* If that failed, put back the change we made above. */ - XEXP (x, 1-arg_operand) = reg; - } - - /* Look for giv with positive constant mult_val and nonconst add_val. - Insert insns to calculate new compare value. */ - - for (v = bl->giv; v; v = v->next_iv) - if (CONSTANT_P (v->mult_val) && INTVAL (v->mult_val) > 0 - && ! v->ignore && ! v->maybe_dead - && v->mode == mode) - { - rtx tem; - - if (! eliminate_p) - return 1; - - tem = gen_reg_rtx (mode); - - /* Replace biv with giv's reduced register. */ - validate_change (insn, &XEXP (x, 1 - arg_operand), - v->new_reg, 1); - - /* Compute value to compare against. */ - emit_iv_add_mult (arg, v->mult_val, v->add_val, tem, where); - /* Use it in this insn. */ - validate_change (insn, &XEXP (x, arg_operand), tem, 1); - if (apply_change_group ()) - return 1; - } - } - else if (GET_CODE (arg) == REG || GET_CODE (arg) == MEM) - { - if (invariant_p (arg) == 1) - { - /* Look for giv with constant positive mult_val and nonconst - add_val. Insert insns to compute new compare value. */ - - for (v = bl->giv; v; v = v->next_iv) - if (CONSTANT_P (v->mult_val) && INTVAL (v->mult_val) > 0 - && ! v->ignore && ! v->maybe_dead - && v->mode == mode) - { - rtx tem; - - if (! eliminate_p) - return 1; - - tem = gen_reg_rtx (mode); - - /* Replace biv with giv's reduced register. */ - validate_change (insn, &XEXP (x, 1 - arg_operand), - v->new_reg, 1); - - /* Compute value to compare against. */ - emit_iv_add_mult (arg, v->mult_val, v->add_val, - tem, where); - validate_change (insn, &XEXP (x, arg_operand), tem, 1); - if (apply_change_group ()) - return 1; - } - } - - /* This code has problems. Basically, you can't know when - seeing if we will eliminate BL, whether a particular giv - of ARG will be reduced. If it isn't going to be reduced, - we can't eliminate BL. We can try forcing it to be reduced, - but that can generate poor code. - - The problem is that the benefit of reducing TV, below should - be increased if BL can actually be eliminated, but this means - we might have to do a topological sort of the order in which - we try to process biv. It doesn't seem worthwhile to do - this sort of thing now. */ - -#if 0 - /* Otherwise the reg compared with had better be a biv. */ - if (GET_CODE (arg) != REG - || reg_iv_type[REGNO (arg)] != BASIC_INDUCT) - return 0; - - /* Look for a pair of givs, one for each biv, - with identical coefficients. */ - for (v = bl->giv; v; v = v->next_iv) - { - struct induction *tv; - - if (v->ignore || v->maybe_dead || v->mode != mode) - continue; - - for (tv = reg_biv_class[REGNO (arg)]->giv; tv; tv = tv->next_iv) - if (! tv->ignore && ! tv->maybe_dead - && rtx_equal_p (tv->mult_val, v->mult_val) - && rtx_equal_p (tv->add_val, v->add_val) - && tv->mode == mode) - { - if (! eliminate_p) - return 1; - - /* Replace biv with its giv's reduced reg. */ - XEXP (x, 1-arg_operand) = v->new_reg; - /* Replace other operand with the other giv's - reduced reg. */ - XEXP (x, arg_operand) = tv->new_reg; - return 1; - } - } -#endif - } - - /* If we get here, the biv can't be eliminated. */ - return 0; - - case MEM: - /* If this address is a DEST_ADDR giv, it doesn't matter if the - biv is used in it, since it will be replaced. */ - for (v = bl->giv; v; v = v->next_iv) - if (v->giv_type == DEST_ADDR && v->location == &XEXP (x, 0)) - return 1; - break; - } - - /* See if any subexpression fails elimination. */ - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - switch (fmt[i]) - { - case 'e': - if (! maybe_eliminate_biv_1 (XEXP (x, i), insn, bl, - eliminate_p, where)) - return 0; - break; - - case 'E': - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - if (! maybe_eliminate_biv_1 (XVECEXP (x, i, j), insn, bl, - eliminate_p, where)) - return 0; - break; - } - } - - return 1; -} - -/* Return nonzero if the last use of REG - is in an insn following INSN in the same basic block. */ - -static int -last_use_this_basic_block (reg, insn) - rtx reg; - rtx insn; -{ - rtx n; - for (n = insn; - n && GET_CODE (n) != CODE_LABEL && GET_CODE (n) != JUMP_INSN; - n = NEXT_INSN (n)) - { - if (regno_last_uid[REGNO (reg)] == INSN_UID (n)) - return 1; - } - return 0; -} - -/* Called via `note_stores' to record the initial value of a biv. Here we - just record the location of the set and process it later. */ - -static void -record_initial (dest, set) - rtx dest; - rtx set; -{ - struct iv_class *bl; - - if (GET_CODE (dest) != REG - || REGNO (dest) >= max_reg_before_loop - || reg_iv_type[REGNO (dest)] != BASIC_INDUCT) - return; - - bl = reg_biv_class[REGNO (dest)]; - - /* If this is the first set found, record it. */ - if (bl->init_insn == 0) - { - bl->init_insn = note_insn; - bl->init_set = set; - } -} - -/* If any of the registers in X are "old" and currently have a last use earlier - than INSN, update them to have a last use of INSN. Their actual last use - will be the previous insn but it will not have a valid uid_luid so we can't - use it. */ - -static void -update_reg_last_use (x, insn) - rtx x; - rtx insn; -{ - /* Check for the case where INSN does not have a valid luid. In this case, - there is no need to modify the regno_last_uid, as this can only happen - when code is inserted after the loop_end to set a pseudo's final value, - and hence this insn will never be the last use of x. */ - if (GET_CODE (x) == REG && REGNO (x) < max_reg_before_loop - && INSN_UID (insn) < max_uid_for_loop - && uid_luid[regno_last_uid[REGNO (x)]] < uid_luid[INSN_UID (insn)]) - regno_last_uid[REGNO (x)] = INSN_UID (insn); - else - { - register int i, j; - register char *fmt = GET_RTX_FORMAT (GET_CODE (x)); - for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - update_reg_last_use (XEXP (x, i), insn); - else if (fmt[i] == 'E') - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - update_reg_last_use (XVECEXP (x, i, j), insn); - } - } -} - -/* Given a jump insn JUMP, return the condition that will cause it to branch - to its JUMP_LABEL. If the condition cannot be understood, or is an - inequality floating-point comparison which needs to be reversed, 0 will - be returned. - - If EARLIEST is non-zero, it is a pointer to a place where the earliest - insn used in locating the condition was found. If a replacement test - of the condition is desired, it should be placed in front of that - insn and we will be sure that the inputs are still valid. - - The condition will be returned in a canonical form to simplify testing by - callers. Specifically: - - (1) The code will always be a comparison operation (EQ, NE, GT, etc.). - (2) Both operands will be machine operands; (cc0) will have been replaced. - (3) If an operand is a constant, it will be the second operand. - (4) (LE x const) will be replaced with (LT x ) and similarly - for GE, GEU, and LEU. */ - -rtx -get_condition (jump, earliest) - rtx jump; - rtx *earliest; -{ - enum rtx_code code; - rtx prev = jump; - rtx set; - rtx tem; - rtx op0, op1; - int reverse_code = 0; - int did_reverse_condition = 0; - - /* If this is not a standard conditional jump, we can't parse it. */ - if (GET_CODE (jump) != JUMP_INSN - || ! condjump_p (jump) || simplejump_p (jump)) - return 0; - - code = GET_CODE (XEXP (SET_SRC (PATTERN (jump)), 0)); - op0 = XEXP (XEXP (SET_SRC (PATTERN (jump)), 0), 0); - op1 = XEXP (XEXP (SET_SRC (PATTERN (jump)), 0), 1); - - if (earliest) - *earliest = jump; - - /* If this branches to JUMP_LABEL when the condition is false, reverse - the condition. */ - if (GET_CODE (XEXP (SET_SRC (PATTERN (jump)), 2)) == LABEL_REF - && XEXP (XEXP (SET_SRC (PATTERN (jump)), 2), 0) == JUMP_LABEL (jump)) - code = reverse_condition (code), did_reverse_condition ^= 1; - - /* If we are comparing a register with zero, see if the register is set - in the previous insn to a COMPARE or a comparison operation. Perform - the same tests as a function of STORE_FLAG_VALUE as find_comparison_args - in cse.c */ - - while (GET_RTX_CLASS (code) == '<' && op1 == const0_rtx) - { - /* Set non-zero when we find something of interest. */ - rtx x = 0; - -#ifdef HAVE_cc0 - /* If comparison with cc0, import actual comparison from compare - insn. */ - if (op0 == cc0_rtx) - { - if ((prev = prev_nonnote_insn (prev)) == 0 - || GET_CODE (prev) != INSN - || (set = single_set (prev)) == 0 - || SET_DEST (set) != cc0_rtx) - return 0; - - op0 = SET_SRC (set); - op1 = CONST0_RTX (GET_MODE (op0)); - if (earliest) - *earliest = prev; - } -#endif - - /* If this is a COMPARE, pick up the two things being compared. */ - if (GET_CODE (op0) == COMPARE) - { - op1 = XEXP (op0, 1); - op0 = XEXP (op0, 0); - continue; - } - else if (GET_CODE (op0) != REG) - break; - - /* Go back to the previous insn. Stop if it is not an INSN. We also - stop if it isn't a single set or if it has a REG_INC note because - we don't want to bother dealing with it. */ - - if ((prev = prev_nonnote_insn (prev)) == 0 - || GET_CODE (prev) != INSN - || FIND_REG_INC_NOTE (prev, 0) - || (set = single_set (prev)) == 0) - break; - - /* If this is setting OP0, get what it sets it to if it looks - relevant. */ - if (SET_DEST (set) == op0) - { - enum machine_mode inner_mode = GET_MODE (SET_SRC (set)); - - if ((GET_CODE (SET_SRC (set)) == COMPARE - || (((code == NE - || (code == LT - && GET_MODE_CLASS (inner_mode) == MODE_INT - && (GET_MODE_BITSIZE (inner_mode) - <= HOST_BITS_PER_WIDE_INT) - && (STORE_FLAG_VALUE - & ((HOST_WIDE_INT) 1 - << (GET_MODE_BITSIZE (inner_mode) - 1)))) -#ifdef FLOAT_STORE_FLAG_VALUE - || (code == LT - && GET_MODE_CLASS (inner_mode) == MODE_FLOAT - && FLOAT_STORE_FLAG_VALUE < 0) -#endif - )) - && GET_RTX_CLASS (GET_CODE (SET_SRC (set))) == '<'))) - x = SET_SRC (set); - else if (((code == EQ - || (code == GE - && (GET_MODE_BITSIZE (inner_mode) - <= HOST_BITS_PER_WIDE_INT) - && GET_MODE_CLASS (inner_mode) == MODE_INT - && (STORE_FLAG_VALUE - & ((HOST_WIDE_INT) 1 - << (GET_MODE_BITSIZE (inner_mode) - 1)))) -#ifdef FLOAT_STORE_FLAG_VALUE - || (code == GE - && GET_MODE_CLASS (inner_mode) == MODE_FLOAT - && FLOAT_STORE_FLAG_VALUE < 0) -#endif - )) - && GET_RTX_CLASS (GET_CODE (SET_SRC (set))) == '<') - { - /* We might have reversed a LT to get a GE here. But this wasn't - actually the comparison of data, so we don't flag that we - have had to reverse the condition. */ - did_reverse_condition ^= 1; - reverse_code = 1; - x = SET_SRC (set); - } - } - - else if (reg_set_p (op0, prev)) - /* If this sets OP0, but not directly, we have to give up. */ - break; - - if (x) - { - if (GET_RTX_CLASS (GET_CODE (x)) == '<') - code = GET_CODE (x); - if (reverse_code) - { - code = reverse_condition (code); - did_reverse_condition ^= 1; - reverse_code = 0; - } - - op0 = XEXP (x, 0), op1 = XEXP (x, 1); - if (earliest) - *earliest = prev; - } - } - - /* If constant is first, put it last. */ - if (CONSTANT_P (op0)) - code = swap_condition (code), tem = op0, op0 = op1, op1 = tem; - - /* If OP0 is the result of a comparison, we weren't able to find what - was really being compared, so fail. */ - if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC) - return 0; - - /* Canonicalize any ordered comparison with integers involving equality - if we can do computations in the relevant mode and we do not - overflow. */ - - if (GET_CODE (op1) == CONST_INT - && GET_MODE (op0) != VOIDmode - && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT) - { - HOST_WIDE_INT const_val = INTVAL (op1); - unsigned HOST_WIDE_INT uconst_val = const_val; - unsigned HOST_WIDE_INT max_val - = (unsigned HOST_WIDE_INT) GET_MODE_MASK (GET_MODE (op0)); - - switch (code) - { - case LE: - if (const_val != max_val >> 1) - code = LT, op1 = GEN_INT (const_val + 1); - break; - - case GE: - if (const_val - != (((HOST_WIDE_INT) 1 - << (GET_MODE_BITSIZE (GET_MODE (op0)) - 1)))) - code = GT, op1 = GEN_INT (const_val - 1); - break; - - case LEU: - if (uconst_val != max_val) - code = LTU, op1 = GEN_INT (uconst_val + 1); - break; - - case GEU: - if (uconst_val != 0) - code = GTU, op1 = GEN_INT (uconst_val - 1); - break; - } - } - - /* If this was floating-point and we reversed anything other than an - EQ or NE, return zero. */ - if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT - && did_reverse_condition && code != NE && code != EQ - && GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT) - return 0; - -#ifdef HAVE_cc0 - /* Never return CC0; return zero instead. */ - if (op0 == cc0_rtx) - return 0; -#endif - - return gen_rtx (code, VOIDmode, op0, op1); -} - -/* Similar to above routine, except that we also put an invariant last - unless both operands are invariants. */ - -rtx -get_condition_for_loop (x) - rtx x; -{ - rtx comparison = get_condition (x, NULL_PTR); - - if (comparison == 0 - || ! invariant_p (XEXP (comparison, 0)) - || invariant_p (XEXP (comparison, 1))) - return comparison; - - return gen_rtx (swap_condition (GET_CODE (comparison)), VOIDmode, - XEXP (comparison, 1), XEXP (comparison, 0)); -} diff --git a/gnu/usr.bin/cc/common/real.c b/gnu/usr.bin/cc/common/real.c deleted file mode 100644 index 08811da6d5..0000000000 --- a/gnu/usr.bin/cc/common/real.c +++ /dev/null @@ -1,4978 +0,0 @@ -/* real.c - implementation of REAL_ARITHMETIC, REAL_VALUE_ATOF, -and support for XFmode IEEE extended real floating point arithmetic. -Contributed by Stephen L. Moshier (moshier@world.std.com). - - Copyright (C) 1993 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include -#include "config.h" -#include "tree.h" - -#ifndef errno -extern int errno; -#endif - -/* To enable support of XFmode extended real floating point, define -LONG_DOUBLE_TYPE_SIZE 96 in the tm.h file (m68k.h or i386.h). - -To support cross compilation between IEEE and VAX floating -point formats, define REAL_ARITHMETIC in the tm.h file. - -In either case the machine files (tm.h) must not contain any code -that tries to use host floating point arithmetic to convert -REAL_VALUE_TYPEs from `double' to `float', pass them to fprintf, -etc. In cross-compile situations a REAL_VALUE_TYPE may not -be intelligible to the host computer's native arithmetic. - -The emulator defaults to the host's floating point format so that -its decimal conversion functions can be used if desired (see -real.h). - -The first part of this file interfaces gcc to ieee.c, which is a -floating point arithmetic suite that was not written with gcc in -mind. The interface is followed by ieee.c itself and related -items. Avoid changing ieee.c unless you have suitable test -programs available. A special version of the PARANOIA floating -point arithmetic tester, modified for this purpose, can be found -on usc.edu : /pub/C-numanal/ieeetest.zoo. Some tutorial -information on ieee.c is given in my book: S. L. Moshier, -_Methods and Programs for Mathematical Functions_, Prentice-Hall -or Simon & Schuster Int'l, 1989. A library of XFmode elementary -transcendental functions can be obtained by ftp from -research.att.com: netlib/cephes/ldouble.shar.Z */ - -/* Type of computer arithmetic. - * Only one of DEC, MIEEE, IBMPC, or UNK should get defined. - */ - -/* `MIEEE' refers generically to big-endian IEEE floating-point data - structure. This definition should work in SFmode `float' type and - DFmode `double' type on virtually all big-endian IEEE machines. - If LONG_DOUBLE_TYPE_SIZE has been defined to be 96, then MIEEE - also invokes the particular XFmode (`long double' type) data - structure used by the Motorola 680x0 series processors. - - `IBMPC' refers generally to little-endian IEEE machines. In this - case, if LONG_DOUBLE_TYPE_SIZE has been defined to be 96, then - IBMPC also invokes the particular XFmode `long double' data - structure used by the Intel 80x86 series processors. - - `DEC' refers specifically to the Digital Equipment Corp PDP-11 - and VAX floating point data structure. This model currently - supports no type wider than DFmode. - - If LONG_DOUBLE_TYPE_SIZE = 64 (the default, unless tm.h defines it) - then `long double' and `double' are both implemented, but they - both mean DFmode. In this case, the software floating-point - support available here is activated by writing - #define REAL_ARITHMETIC - in tm.h. - - The case LONG_DOUBLE_TYPE_SIZE = 128 activates TFmode support - (Not Yet Implemented) and may deactivate XFmode since - `long double' is used to refer to both modes. */ - -/* The following converts gcc macros into the ones used by this file. */ - -/* REAL_ARITHMETIC defined means that macros in real.h are - defined to call emulator functions. */ -#ifdef REAL_ARITHMETIC - -#if TARGET_FLOAT_FORMAT == VAX_FLOAT_FORMAT -/* PDP-11, Pro350, VAX: */ -#define DEC 1 -#else /* it's not VAX */ -#if TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT -#if WORDS_BIG_ENDIAN -/* Motorola IEEE, high order words come first (Sun workstation): */ -#define MIEEE 1 -#else /* not big-endian */ -/* Intel IEEE, low order words come first: - */ -#define IBMPC 1 -#endif /* big-endian */ -#else /* it's not IEEE either */ -/* UNKnown arithmetic. We don't support this and can't go on. */ -unknown arithmetic type -#define UNK 1 -#endif /* not IEEE */ -#endif /* not VAX */ - -#else -/* REAL_ARITHMETIC not defined means that the *host's* data - structure will be used. It may differ by endian-ness from the - target machine's structure and will get its ends swapped - accordingly (but not here). Probably only the decimal <-> binary - functions in this file will actually be used in this case. */ -#if HOST_FLOAT_FORMAT == VAX_FLOAT_FORMAT -#define DEC 1 -#else /* it's not VAX */ -#if HOST_FLOAT_FORMAT == IEEE_FLOAT_FORMAT -#ifdef HOST_WORDS_BIG_ENDIAN -#define MIEEE 1 -#else /* not big-endian */ -#define IBMPC 1 -#endif /* big-endian */ -#else /* it's not IEEE either */ -unknown arithmetic type -#define UNK 1 -#endif /* not IEEE */ -#endif /* not VAX */ - -#endif /* REAL_ARITHMETIC not defined */ - -/* Define INFINITY for support of infinity. - Define NANS for support of Not-a-Number's (NaN's). */ -#ifndef DEC -#define INFINITY -#define NANS -#endif - -/* Support of NaNs requires support of infinity. */ -#ifdef NANS -#ifndef INFINITY -#define INFINITY -#endif -#endif - -/* ehead.h - * - * Include file for extended precision arithmetic programs. - */ - -/* Number of 16 bit words in external e type format */ -#define NE 6 - -/* Number of 16 bit words in internal format */ -#define NI (NE+3) - -/* Array offset to exponent */ -#define E 1 - -/* Array offset to high guard word */ -#define M 2 - -/* Number of bits of precision */ -#define NBITS ((NI-4)*16) - -/* Maximum number of decimal digits in ASCII conversion - * = NBITS*log10(2) - */ -#define NDEC (NBITS*8/27) - -/* The exponent of 1.0 */ -#define EXONE (0x3fff) - -/* Find a host integer type that is at least 16 bits wide, - and another type at least twice whatever that size is. */ - -#if HOST_BITS_PER_CHAR >= 16 -#define EMUSHORT char -#define EMUSHORT_SIZE HOST_BITS_PER_CHAR -#define EMULONG_SIZE (2 * HOST_BITS_PER_CHAR) -#else -#if HOST_BITS_PER_SHORT >= 16 -#define EMUSHORT short -#define EMUSHORT_SIZE HOST_BITS_PER_SHORT -#define EMULONG_SIZE (2 * HOST_BITS_PER_SHORT) -#else -#if HOST_BITS_PER_INT >= 16 -#define EMUSHORT int -#define EMUSHORT_SIZE HOST_BITS_PER_INT -#define EMULONG_SIZE (2 * HOST_BITS_PER_INT) -#else -#if HOST_BITS_PER_LONG >= 16 -#define EMUSHORT long -#define EMUSHORT_SIZE HOST_BITS_PER_LONG -#define EMULONG_SIZE (2 * HOST_BITS_PER_LONG) -#else -/* You will have to modify this program to have a smaller unit size. */ -#define EMU_NON_COMPILE -#endif -#endif -#endif -#endif - -#if HOST_BITS_PER_SHORT >= EMULONG_SIZE -#define EMULONG short -#else -#if HOST_BITS_PER_INT >= EMULONG_SIZE -#define EMULONG int -#else -#if HOST_BITS_PER_LONG >= EMULONG_SIZE -#define EMULONG long -#else -#if HOST_BITS_PER_LONG_LONG >= EMULONG_SIZE -#define EMULONG long long int -#else -/* You will have to modify this program to have a smaller unit size. */ -#define EMU_NON_COMPILE -#endif -#endif -#endif -#endif - - -/* The host interface doesn't work if no 16-bit size exists. */ -#if EMUSHORT_SIZE != 16 -#define EMU_NON_COMPILE -#endif - -/* OK to continue compilation. */ -#ifndef EMU_NON_COMPILE - -/* Construct macros to translate between REAL_VALUE_TYPE and e type. - In GET_REAL and PUT_REAL, r and e are pointers. - A REAL_VALUE_TYPE is guaranteed to occupy contiguous locations - in memory, with no holes. */ - -#if LONG_DOUBLE_TYPE_SIZE == 96 -#define GET_REAL(r,e) bcopy (r, e, 2*NE) -#define PUT_REAL(e,r) bcopy (e, r, 2*NE) -#else /* no XFmode */ - -#ifdef REAL_ARITHMETIC -/* Emulator uses target format internally - but host stores it in host endian-ness. */ - -#if defined (HOST_WORDS_BIG_ENDIAN) == WORDS_BIG_ENDIAN -#define GET_REAL(r,e) e53toe ((r), (e)) -#define PUT_REAL(e,r) etoe53 ((e), (r)) - -#else /* endian-ness differs */ -/* emulator uses target endian-ness internally */ -#define GET_REAL(r,e) \ -do { EMUSHORT w[4]; \ - w[3] = ((EMUSHORT *) r)[0]; \ - w[2] = ((EMUSHORT *) r)[1]; \ - w[1] = ((EMUSHORT *) r)[2]; \ - w[0] = ((EMUSHORT *) r)[3]; \ - e53toe (w, (e)); } while (0) - -#define PUT_REAL(e,r) \ -do { EMUSHORT w[4]; \ - etoe53 ((e), w); \ - *((EMUSHORT *) r) = w[3]; \ - *((EMUSHORT *) r + 1) = w[2]; \ - *((EMUSHORT *) r + 2) = w[1]; \ - *((EMUSHORT *) r + 3) = w[0]; } while (0) - -#endif /* endian-ness differs */ - -#else /* not REAL_ARITHMETIC */ - -/* emulator uses host format */ -#define GET_REAL(r,e) e53toe ((r), (e)) -#define PUT_REAL(e,r) etoe53 ((e), (r)) - -#endif /* not REAL_ARITHMETIC */ -#endif /* no XFmode */ - -void warning (); -extern int extra_warnings; -int ecmp (), enormlz (), eshift (); -int eisneg (), eisinf (), eisnan (), eiisinf (), eiisnan (); -void eadd (), esub (), emul (), ediv (); -void eshup1 (), eshup8 (), eshup6 (), eshdn1 (), eshdn8 (), eshdn6 (); -void eabs (), eneg (), emov (), eclear (), einfin (), efloor (); -void eldexp (), efrexp (), eifrac (), euifrac (), ltoe (), ultoe (); -void eround (), ereal_to_decimal (), eiinfin (), einan (); -void esqrt (), elog (), eexp (), etanh (), epow (); -void asctoe (), asctoe24 (), asctoe53 (), asctoe64 (); -void etoasc (), e24toasc (), e53toasc (), e64toasc (); -void etoe64 (), etoe53 (), etoe24 (), e64toe (), e53toe (), e24toe (); -void mtherr (), make_nan (); -void enan (); -extern unsigned EMUSHORT ezero[], ehalf[], eone[], etwo[]; -extern unsigned EMUSHORT elog2[], esqrt2[]; - -/* Pack output array with 32-bit numbers obtained from - array containing 16-bit numbers, swapping ends if required. */ -void -endian (e, x, mode) - unsigned EMUSHORT e[]; - long x[]; - enum machine_mode mode; -{ - unsigned long th, t; - -#if WORDS_BIG_ENDIAN - switch (mode) - { - - case XFmode: - - /* Swap halfwords in the third long. */ - th = (unsigned long) e[4] & 0xffff; - t = (unsigned long) e[5] & 0xffff; - t |= th << 16; - x[2] = (long) t; - /* fall into the double case */ - - case DFmode: - - /* swap halfwords in the second word */ - th = (unsigned long) e[2] & 0xffff; - t = (unsigned long) e[3] & 0xffff; - t |= th << 16; - x[1] = (long) t; - /* fall into the float case */ - - case SFmode: - - /* swap halfwords in the first word */ - th = (unsigned long) e[0] & 0xffff; - t = (unsigned long) e[1] & 0xffff; - t |= th << 16; - x[0] = t; - break; - - default: - abort (); - } - -#else - - /* Pack the output array without swapping. */ - - switch (mode) - { - - case XFmode: - - /* Pack the third long. - Each element of the input REAL_VALUE_TYPE array has 16 bit useful bits - in it. */ - th = (unsigned long) e[5] & 0xffff; - t = (unsigned long) e[4] & 0xffff; - t |= th << 16; - x[2] = (long) t; - /* fall into the double case */ - - case DFmode: - - /* pack the second long */ - th = (unsigned long) e[3] & 0xffff; - t = (unsigned long) e[2] & 0xffff; - t |= th << 16; - x[1] = (long) t; - /* fall into the float case */ - - case SFmode: - - /* pack the first long */ - th = (unsigned long) e[1] & 0xffff; - t = (unsigned long) e[0] & 0xffff; - t |= th << 16; - x[0] = t; - break; - - default: - abort (); - } - -#endif -} - - -/* This is the implementation of the REAL_ARITHMETIC macro. - */ -void -earith (value, icode, r1, r2) - REAL_VALUE_TYPE *value; - int icode; - REAL_VALUE_TYPE *r1; - REAL_VALUE_TYPE *r2; -{ - unsigned EMUSHORT d1[NE], d2[NE], v[NE]; - enum tree_code code; - - GET_REAL (r1, d1); - GET_REAL (r2, d2); -#ifdef NANS -/* Return NaN input back to the caller. */ - if (eisnan (d1)) - { - PUT_REAL (d1, value); - return; - } - if (eisnan (d2)) - { - PUT_REAL (d2, value); - return; - } -#endif - code = (enum tree_code) icode; - switch (code) - { - case PLUS_EXPR: - eadd (d2, d1, v); - break; - - case MINUS_EXPR: - esub (d2, d1, v); /* d1 - d2 */ - break; - - case MULT_EXPR: - emul (d2, d1, v); - break; - - case RDIV_EXPR: -#ifndef REAL_INFINITY - if (ecmp (d2, ezero) == 0) - { -#ifdef NANS - enan (v); - break; -#else - abort (); -#endif - } -#endif - ediv (d2, d1, v); /* d1/d2 */ - break; - - case MIN_EXPR: /* min (d1,d2) */ - if (ecmp (d1, d2) < 0) - emov (d1, v); - else - emov (d2, v); - break; - - case MAX_EXPR: /* max (d1,d2) */ - if (ecmp (d1, d2) > 0) - emov (d1, v); - else - emov (d2, v); - break; - default: - emov (ezero, v); - break; - } -PUT_REAL (v, value); -} - - -/* Truncate REAL_VALUE_TYPE toward zero to signed HOST_WIDE_INT - * implements REAL_VALUE_RNDZINT (x) (etrunci (x)) - */ -REAL_VALUE_TYPE -etrunci (x) - REAL_VALUE_TYPE x; -{ - unsigned EMUSHORT f[NE], g[NE]; - REAL_VALUE_TYPE r; - long l; - - GET_REAL (&x, g); -#ifdef NANS - if (eisnan (g)) - return (x); -#endif - eifrac (g, &l, f); - ltoe (&l, g); - PUT_REAL (g, &r); - return (r); -} - - -/* Truncate REAL_VALUE_TYPE toward zero to unsigned HOST_WIDE_INT - * implements REAL_VALUE_UNSIGNED_RNDZINT (x) (etruncui (x)) - */ -REAL_VALUE_TYPE -etruncui (x) - REAL_VALUE_TYPE x; -{ - unsigned EMUSHORT f[NE], g[NE]; - REAL_VALUE_TYPE r; - unsigned long l; - - GET_REAL (&x, g); -#ifdef NANS - if (eisnan (g)) - return (x); -#endif - euifrac (g, &l, f); - ultoe (&l, g); - PUT_REAL (g, &r); - return (r); -} - - -/* This is the REAL_VALUE_ATOF function. - * It converts a decimal string to binary, rounding off - * as indicated by the machine_mode argument. Then it - * promotes the rounded value to REAL_VALUE_TYPE. - */ -REAL_VALUE_TYPE -ereal_atof (s, t) - char *s; - enum machine_mode t; -{ - unsigned EMUSHORT tem[NE], e[NE]; - REAL_VALUE_TYPE r; - - switch (t) - { - case SFmode: - asctoe24 (s, tem); - e24toe (tem, e); - break; - case DFmode: - asctoe53 (s, tem); - e53toe (tem, e); - break; - case XFmode: - asctoe64 (s, tem); - e64toe (tem, e); - break; - default: - asctoe (s, e); - } - PUT_REAL (e, &r); - return (r); -} - - -/* Expansion of REAL_NEGATE. - */ -REAL_VALUE_TYPE -ereal_negate (x) - REAL_VALUE_TYPE x; -{ - unsigned EMUSHORT e[NE]; - REAL_VALUE_TYPE r; - - GET_REAL (&x, e); -#ifdef NANS - if (eisnan (e)) - return (x); -#endif - eneg (e); - PUT_REAL (e, &r); - return (r); -} - - -/* Round real to int - * implements REAL_VALUE_FIX (x) (eroundi (x)) - * The type of rounding is left unspecified by real.h. - * It is implemented here as round to nearest (add .5 and chop). - */ -int -eroundi (x) - REAL_VALUE_TYPE x; -{ - unsigned EMUSHORT f[NE], g[NE]; - EMULONG l; - - GET_REAL (&x, f); -#ifdef NANS - if (eisnan (f)) - { - warning ("conversion from NaN to int"); - return (-1); - } -#endif - eround (f, g); - eifrac (g, &l, f); - return ((int) l); -} - -/* Round real to nearest unsigned int - * implements REAL_VALUE_UNSIGNED_FIX (x) ((unsigned int) eroundi (x)) - * Negative input returns zero. - * The type of rounding is left unspecified by real.h. - * It is implemented here as round to nearest (add .5 and chop). - */ -unsigned int -eroundui (x) - REAL_VALUE_TYPE x; -{ - unsigned EMUSHORT f[NE], g[NE]; - unsigned EMULONG l; - - GET_REAL (&x, f); -#ifdef NANS - if (eisnan (f)) - { - warning ("conversion from NaN to unsigned int"); - return (-1); - } -#endif - eround (f, g); - euifrac (g, &l, f); - return ((unsigned int)l); -} - - -/* REAL_VALUE_FROM_INT macro. - */ -void -ereal_from_int (d, i, j) - REAL_VALUE_TYPE *d; - long i, j; -{ - unsigned EMUSHORT df[NE], dg[NE]; - long low, high; - int sign; - - sign = 0; - low = i; - if ((high = j) < 0) - { - sign = 1; - /* complement and add 1 */ - high = ~high; - if (low) - low = -low; - else - high += 1; - } - eldexp (eone, HOST_BITS_PER_LONG, df); - ultoe (&high, dg); - emul (dg, df, dg); - ultoe (&low, df); - eadd (df, dg, dg); - if (sign) - eneg (dg); - PUT_REAL (dg, d); -} - - -/* REAL_VALUE_FROM_UNSIGNED_INT macro. - */ -void -ereal_from_uint (d, i, j) - REAL_VALUE_TYPE *d; - unsigned long i, j; -{ - unsigned EMUSHORT df[NE], dg[NE]; - unsigned long low, high; - - low = i; - high = j; - eldexp (eone, HOST_BITS_PER_LONG, df); - ultoe (&high, dg); - emul (dg, df, dg); - ultoe (&low, df); - eadd (df, dg, dg); - PUT_REAL (dg, d); -} - - -/* REAL_VALUE_TO_INT macro - */ -void -ereal_to_int (low, high, rr) - long *low, *high; - REAL_VALUE_TYPE rr; -{ - unsigned EMUSHORT d[NE], df[NE], dg[NE], dh[NE]; - int s; - - GET_REAL (&rr, d); -#ifdef NANS - if (eisnan (d)) - { - warning ("conversion from NaN to int"); - *low = -1; - *high = -1; - return; - } -#endif - /* convert positive value */ - s = 0; - if (eisneg (d)) - { - eneg (d); - s = 1; - } - eldexp (eone, HOST_BITS_PER_LONG, df); - ediv (df, d, dg); /* dg = d / 2^32 is the high word */ - euifrac (dg, high, dh); - emul (df, dh, dg); /* fractional part is the low word */ - euifrac (dg, low, dh); - if (s) - { - /* complement and add 1 */ - *high = ~(*high); - if (*low) - *low = -(*low); - else - *high += 1; - } -} - - -/* REAL_VALUE_LDEXP macro. - */ -REAL_VALUE_TYPE -ereal_ldexp (x, n) - REAL_VALUE_TYPE x; - int n; -{ - unsigned EMUSHORT e[NE], y[NE]; - REAL_VALUE_TYPE r; - - GET_REAL (&x, e); -#ifdef NANS - if (eisnan (e)) - return (x); -#endif - eldexp (e, n, y); - PUT_REAL (y, &r); - return (r); -} - -/* These routines are conditionally compiled because functions - * of the same names may be defined in fold-const.c. */ -#ifdef REAL_ARITHMETIC - -/* Check for infinity in a REAL_VALUE_TYPE. */ -int -target_isinf (x) - REAL_VALUE_TYPE x; -{ - unsigned EMUSHORT e[NE]; - -#ifdef INFINITY - GET_REAL (&x, e); - return (eisinf (e)); -#else - return 0; -#endif -} - - -/* Check whether a REAL_VALUE_TYPE item is a NaN. */ - -int -target_isnan (x) - REAL_VALUE_TYPE x; -{ - unsigned EMUSHORT e[NE]; - -#ifdef NANS - GET_REAL (&x, e); - return (eisnan (e)); -#else - return (0); -#endif -} - - -/* Check for a negative REAL_VALUE_TYPE number. - * this means strictly less than zero, not -0. - */ - -int -target_negative (x) - REAL_VALUE_TYPE x; -{ - unsigned EMUSHORT e[NE]; - - GET_REAL (&x, e); - if (ecmp (e, ezero) == -1) - return (1); - return (0); -} - -/* Expansion of REAL_VALUE_TRUNCATE. - * The result is in floating point, rounded to nearest or even. - */ -REAL_VALUE_TYPE -real_value_truncate (mode, arg) - enum machine_mode mode; - REAL_VALUE_TYPE arg; -{ - unsigned EMUSHORT e[NE], t[NE]; - REAL_VALUE_TYPE r; - - GET_REAL (&arg, e); -#ifdef NANS - if (eisnan (e)) - return (arg); -#endif - eclear (t); - switch (mode) - { - case XFmode: - etoe64 (e, t); - e64toe (t, t); - break; - - case DFmode: - etoe53 (e, t); - e53toe (t, t); - break; - - case SFmode: - etoe24 (e, t); - e24toe (t, t); - break; - - case SImode: - r = etrunci (e); - return (r); - - default: - abort (); - } - PUT_REAL (t, &r); - return (r); -} - -#endif /* REAL_ARITHMETIC defined */ - -/* Target values are arrays of host longs. A long is guaranteed - to be at least 32 bits wide. */ -void -etarldouble (r, l) - REAL_VALUE_TYPE r; - long l[]; -{ - unsigned EMUSHORT e[NE]; - - GET_REAL (&r, e); - etoe64 (e, e); - endian (e, l, XFmode); -} - -void -etardouble (r, l) - REAL_VALUE_TYPE r; - long l[]; -{ - unsigned EMUSHORT e[NE]; - - GET_REAL (&r, e); - etoe53 (e, e); - endian (e, l, DFmode); -} - -long -etarsingle (r) - REAL_VALUE_TYPE r; -{ - unsigned EMUSHORT e[NE]; - unsigned long l; - - GET_REAL (&r, e); - etoe24 (e, e); - endian (e, &l, SFmode); - return ((long) l); -} - -void -ereal_to_decimal (x, s) - REAL_VALUE_TYPE x; - char *s; -{ - unsigned EMUSHORT e[NE]; - - GET_REAL (&x, e); - etoasc (e, s, 20); -} - -int -ereal_cmp (x, y) - REAL_VALUE_TYPE x, y; -{ - unsigned EMUSHORT ex[NE], ey[NE]; - - GET_REAL (&x, ex); - GET_REAL (&y, ey); - return (ecmp (ex, ey)); -} - -int -ereal_isneg (x) - REAL_VALUE_TYPE x; -{ - unsigned EMUSHORT ex[NE]; - - GET_REAL (&x, ex); - return (eisneg (ex)); -} - -/* End of REAL_ARITHMETIC interface */ - -/* ieee.c - * - * Extended precision IEEE binary floating point arithmetic routines - * - * Numbers are stored in C language as arrays of 16-bit unsigned - * short integers. The arguments of the routines are pointers to - * the arrays. - * - * - * External e type data structure, simulates Intel 8087 chip - * temporary real format but possibly with a larger significand: - * - * NE-1 significand words (least significant word first, - * most significant bit is normally set) - * exponent (value = EXONE for 1.0, - * top bit is the sign) - * - * - * Internal data structure of a number (a "word" is 16 bits): - * - * ei[0] sign word (0 for positive, 0xffff for negative) - * ei[1] biased exponent (value = EXONE for the number 1.0) - * ei[2] high guard word (always zero after normalization) - * ei[3] - * to ei[NI-2] significand (NI-4 significand words, - * most significant word first, - * most significant bit is set) - * ei[NI-1] low guard word (0x8000 bit is rounding place) - * - * - * - * Routines for external format numbers - * - * asctoe (string, e) ASCII string to extended double e type - * asctoe64 (string, &d) ASCII string to long double - * asctoe53 (string, &d) ASCII string to double - * asctoe24 (string, &f) ASCII string to single - * asctoeg (string, e, prec) ASCII string to specified precision - * e24toe (&f, e) IEEE single precision to e type - * e53toe (&d, e) IEEE double precision to e type - * e64toe (&d, e) IEEE long double precision to e type - * eabs (e) absolute value - * eadd (a, b, c) c = b + a - * eclear (e) e = 0 - * ecmp (a, b) Returns 1 if a > b, 0 if a == b, - * -1 if a < b, -2 if either a or b is a NaN. - * ediv (a, b, c) c = b / a - * efloor (a, b) truncate to integer, toward -infinity - * efrexp (a, exp, s) extract exponent and significand - * eifrac (e, &l, frac) e to long integer and e type fraction - * euifrac (e, &l, frac) e to unsigned long integer and e type fraction - * einfin (e) set e to infinity, leaving its sign alone - * eldexp (a, n, b) multiply by 2**n - * emov (a, b) b = a - * emul (a, b, c) c = b * a - * eneg (e) e = -e - * eround (a, b) b = nearest integer value to a - * esub (a, b, c) c = b - a - * e24toasc (&f, str, n) single to ASCII string, n digits after decimal - * e53toasc (&d, str, n) double to ASCII string, n digits after decimal - * e64toasc (&d, str, n) long double to ASCII string - * etoasc (e, str, n) e to ASCII string, n digits after decimal - * etoe24 (e, &f) convert e type to IEEE single precision - * etoe53 (e, &d) convert e type to IEEE double precision - * etoe64 (e, &d) convert e type to IEEE long double precision - * ltoe (&l, e) long (32 bit) integer to e type - * ultoe (&l, e) unsigned long (32 bit) integer to e type - * eisneg (e) 1 if sign bit of e != 0, else 0 - * eisinf (e) 1 if e has maximum exponent (non-IEEE) - * or is infinite (IEEE) - * eisnan (e) 1 if e is a NaN - * - * - * Routines for internal format numbers - * - * eaddm (ai, bi) add significands, bi = bi + ai - * ecleaz (ei) ei = 0 - * ecleazs (ei) set ei = 0 but leave its sign alone - * ecmpm (ai, bi) compare significands, return 1, 0, or -1 - * edivm (ai, bi) divide significands, bi = bi / ai - * emdnorm (ai,l,s,exp) normalize and round off - * emovi (a, ai) convert external a to internal ai - * emovo (ai, a) convert internal ai to external a - * emovz (ai, bi) bi = ai, low guard word of bi = 0 - * emulm (ai, bi) multiply significands, bi = bi * ai - * enormlz (ei) left-justify the significand - * eshdn1 (ai) shift significand and guards down 1 bit - * eshdn8 (ai) shift down 8 bits - * eshdn6 (ai) shift down 16 bits - * eshift (ai, n) shift ai n bits up (or down if n < 0) - * eshup1 (ai) shift significand and guards up 1 bit - * eshup8 (ai) shift up 8 bits - * eshup6 (ai) shift up 16 bits - * esubm (ai, bi) subtract significands, bi = bi - ai - * eiisinf (ai) 1 if infinite - * eiisnan (ai) 1 if a NaN - * einan (ai) set ai = NaN - * eiinfin (ai) set ai = infinity - * - * - * The result is always normalized and rounded to NI-4 word precision - * after each arithmetic operation. - * - * Exception flags are NOT fully supported. - * - * Signaling NaN's are NOT supported; they are treated the same - * as quiet NaN's. - * - * Define INFINITY for support of infinity; otherwise a - * saturation arithmetic is implemented. - * - * Define NANS for support of Not-a-Number items; otherwise the - * arithmetic will never produce a NaN output, and might be confused - * by a NaN input. - * If NaN's are supported, the output of `ecmp (a,b)' is -2 if - * either a or b is a NaN. This means asking `if (ecmp (a,b) < 0)' - * may not be legitimate. Use `if (ecmp (a,b) == -1)' for `less than' - * if in doubt. - * - * Denormals are always supported here where appropriate (e.g., not - * for conversion to DEC numbers). - * - */ - - -/* mconf.h - * - * Common include file for math routines - * - * - * - * SYNOPSIS: - * - * #include "mconf.h" - * - * - * - * DESCRIPTION: - * - * This file contains definitions for error codes that are - * passed to the common error handling routine mtherr - * (which see). - * - * The file also includes a conditional assembly definition - * for the type of computer arithmetic (Intel IEEE, DEC, Motorola - * IEEE, or UNKnown). - * - * For Digital Equipment PDP-11 and VAX computers, certain - * IBM systems, and others that use numbers with a 56-bit - * significand, the symbol DEC should be defined. In this - * mode, most floating point constants are given as arrays - * of octal integers to eliminate decimal to binary conversion - * errors that might be introduced by the compiler. - * - * For computers, such as IBM PC, that follow the IEEE - * Standard for Binary Floating Point Arithmetic (ANSI/IEEE - * Std 754-1985), the symbol IBMPC or MIEEE should be defined. - * These numbers have 53-bit significands. In this mode, constants - * are provided as arrays of hexadecimal 16 bit integers. - * - * To accommodate other types of computer arithmetic, all - * constants are also provided in a normal decimal radix - * which one can hope are correctly converted to a suitable - * format by the available C language compiler. To invoke - * this mode, the symbol UNK is defined. - * - * An important difference among these modes is a predefined - * set of machine arithmetic constants for each. The numbers - * MACHEP (the machine roundoff error), MAXNUM (largest number - * represented), and several other parameters are preset by - * the configuration symbol. Check the file const.c to - * ensure that these values are correct for your computer. - * - * For ANSI C compatibility, define ANSIC equal to 1. Currently - * this affects only the atan2 function and others that use it. - */ - -/* Constant definitions for math error conditions. */ - -#define DOMAIN 1 /* argument domain error */ -#define SING 2 /* argument singularity */ -#define OVERFLOW 3 /* overflow range error */ -#define UNDERFLOW 4 /* underflow range error */ -#define TLOSS 5 /* total loss of precision */ -#define PLOSS 6 /* partial loss of precision */ -#define INVALID 7 /* NaN-producing operation */ - -/* e type constants used by high precision check routines */ - -/*include "ehead.h"*/ -/* 0.0 */ -unsigned EMUSHORT ezero[NE] = -{ - 0, 0000000, 0000000, 0000000, 0000000, 0000000,}; -extern unsigned EMUSHORT ezero[]; - -/* 5.0E-1 */ -unsigned EMUSHORT ehalf[NE] = -{ - 0, 0000000, 0000000, 0000000, 0100000, 0x3ffe,}; -extern unsigned EMUSHORT ehalf[]; - -/* 1.0E0 */ -unsigned EMUSHORT eone[NE] = -{ - 0, 0000000, 0000000, 0000000, 0100000, 0x3fff,}; -extern unsigned EMUSHORT eone[]; - -/* 2.0E0 */ -unsigned EMUSHORT etwo[NE] = -{ - 0, 0000000, 0000000, 0000000, 0100000, 0040000,}; -extern unsigned EMUSHORT etwo[]; - -/* 3.2E1 */ -unsigned EMUSHORT e32[NE] = -{ - 0, 0000000, 0000000, 0000000, 0100000, 0040004,}; -extern unsigned EMUSHORT e32[]; - -/* 6.93147180559945309417232121458176568075500134360255E-1 */ -unsigned EMUSHORT elog2[NE] = -{ - 0xc9e4, 0x79ab, 0150717, 0013767, 0130562, 0x3ffe,}; -extern unsigned EMUSHORT elog2[]; - -/* 1.41421356237309504880168872420969807856967187537695E0 */ -unsigned EMUSHORT esqrt2[NE] = -{ - 0x597e, 0x6484, 0174736, 0171463, 0132404, 0x3fff,}; -extern unsigned EMUSHORT esqrt2[]; - -/* 2/sqrt (PI) = - * 1.12837916709551257389615890312154517168810125865800E0 */ -unsigned EMUSHORT eoneopi[NE] = -{ - 0x71d5, 0x688d, 0012333, 0135202, 0110156, 0x3fff,}; -extern unsigned EMUSHORT eoneopi[]; - -/* 3.14159265358979323846264338327950288419716939937511E0 */ -unsigned EMUSHORT epi[NE] = -{ - 0xc4c6, 0xc234, 0020550, 0155242, 0144417, 0040000,}; -extern unsigned EMUSHORT epi[]; - -/* 5.7721566490153286060651209008240243104215933593992E-1 */ -unsigned EMUSHORT eeul[NE] = -{ - 0xd1be, 0xc7a4, 0076660, 0063743, 0111704, 0x3ffe,}; -extern unsigned EMUSHORT eeul[]; - -/* -include "ehead.h" -include "mconf.h" -*/ - - - -/* Control register for rounding precision. - * This can be set to 80 (if NE=6), 64, 56, 53, or 24 bits. - */ -int rndprc = NBITS; -extern int rndprc; - -void eaddm (), esubm (), emdnorm (), asctoeg (); -static void toe24 (), toe53 (), toe64 (); -void eremain (), einit (), eiremain (); -int ecmpm (), edivm (), emulm (); -void emovi (), emovo (), emovz (), ecleaz (), ecleazs (), eadd1 (); -void etodec (), todec (), dectoe (); - - - - -void -einit () -{ -} - -/* -; Clear out entire external format number. -; -; unsigned EMUSHORT x[]; -; eclear (x); -*/ - -void -eclear (x) - register unsigned EMUSHORT *x; -{ - register int i; - - for (i = 0; i < NE; i++) - *x++ = 0; -} - - - -/* Move external format number from a to b. - * - * emov (a, b); - */ - -void -emov (a, b) - register unsigned EMUSHORT *a, *b; -{ - register int i; - - for (i = 0; i < NE; i++) - *b++ = *a++; -} - - -/* -; Absolute value of external format number -; -; EMUSHORT x[NE]; -; eabs (x); -*/ - -void -eabs (x) - unsigned EMUSHORT x[]; /* x is the memory address of a short */ -{ - - x[NE - 1] &= 0x7fff; /* sign is top bit of last word of external format */ -} - - - - -/* -; Negate external format number -; -; unsigned EMUSHORT x[NE]; -; eneg (x); -*/ - -void -eneg (x) - unsigned EMUSHORT x[]; -{ - -#ifdef NANS - if (eisnan (x)) - return; -#endif - x[NE - 1] ^= 0x8000; /* Toggle the sign bit */ -} - - - -/* Return 1 if external format number is negative, - * else return zero, including when it is a NaN. - */ -int -eisneg (x) - unsigned EMUSHORT x[]; -{ - -#ifdef NANS - if (eisnan (x)) - return (0); -#endif - if (x[NE - 1] & 0x8000) - return (1); - else - return (0); -} - - -/* Return 1 if external format number is infinity. - * else return zero. - */ -int -eisinf (x) - unsigned EMUSHORT x[]; -{ - -#ifdef NANS - if (eisnan (x)) - return (0); -#endif - if ((x[NE - 1] & 0x7fff) == 0x7fff) - return (1); - else - return (0); -} - - -/* Check if e-type number is not a number. - The bit pattern is one that we defined, so we know for sure how to - detect it. */ - -int -eisnan (x) - unsigned EMUSHORT x[]; -{ - -#ifdef NANS - int i; -/* NaN has maximum exponent */ - if ((x[NE - 1] & 0x7fff) != 0x7fff) - return (0); -/* ... and non-zero significand field. */ - for (i = 0; i < NE - 1; i++) - { - if (*x++ != 0) - return (1); - } -#endif - return (0); -} - -/* Fill external format number with infinity pattern (IEEE) - or largest possible number (non-IEEE). - Before calling einfin, you should either call eclear - or set up the sign bit by hand. */ - -void -einfin (x) - register unsigned EMUSHORT *x; -{ - register int i; - -#ifdef INFINITY - for (i = 0; i < NE - 1; i++) - *x++ = 0; - *x |= 32767; -#else - for (i = 0; i < NE - 1; i++) - *x++ = 0xffff; - *x |= 32766; - if (rndprc < NBITS) - { - if (rndprc == 64) - { - *(x - 5) = 0; - } - if (rndprc == 53) - { - *(x - 4) = 0xf800; - } - else - { - *(x - 4) = 0; - *(x - 3) = 0; - *(x - 2) = 0xff00; - } - } -#endif -} - - -/* Output an e-type NaN. - This generates Intel's quiet NaN pattern for extended real. - The exponent is 7fff, the leading mantissa word is c000. */ - -void -enan (x) - register unsigned EMUSHORT *x; -{ - register int i; - - for (i = 0; i < NE - 2; i++) - *x++ = 0; - *x++ = 0xc000; - *x = 0x7fff; -} - - -/* Move in external format number, - * converting it to internal format. - */ -void -emovi (a, b) - unsigned EMUSHORT *a, *b; -{ - register unsigned EMUSHORT *p, *q; - int i; - - q = b; - p = a + (NE - 1); /* point to last word of external number */ - /* get the sign bit */ - if (*p & 0x8000) - *q++ = 0xffff; - else - *q++ = 0; - /* get the exponent */ - *q = *p--; - *q++ &= 0x7fff; /* delete the sign bit */ -#ifdef INFINITY - if ((*(q - 1) & 0x7fff) == 0x7fff) - { -#ifdef NANS - if (eisnan (a)) - { - *q++ = 0; - for (i = 3; i < NI; i++) - *q++ = *p--; - return; - } -#endif - for (i = 2; i < NI; i++) - *q++ = 0; - return; - } -#endif - /* clear high guard word */ - *q++ = 0; - /* move in the significand */ - for (i = 0; i < NE - 1; i++) - *q++ = *p--; - /* clear low guard word */ - *q = 0; -} - - -/* Move internal format number out, - * converting it to external format. - */ -void -emovo (a, b) - unsigned EMUSHORT *a, *b; -{ - register unsigned EMUSHORT *p, *q; - unsigned EMUSHORT i; - - p = a; - q = b + (NE - 1); /* point to output exponent */ - /* combine sign and exponent */ - i = *p++; - if (i) - *q-- = *p++ | 0x8000; - else - *q-- = *p++; -#ifdef INFINITY - if (*(p - 1) == 0x7fff) - { -#ifdef NANS - if (eiisnan (a)) - { - enan (b); - return; - } -#endif - einfin (b); - return; - } -#endif - /* skip over guard word */ - ++p; - /* move the significand */ - for (i = 0; i < NE - 1; i++) - *q-- = *p++; -} - - - - -/* Clear out internal format number. - */ - -void -ecleaz (xi) - register unsigned EMUSHORT *xi; -{ - register int i; - - for (i = 0; i < NI; i++) - *xi++ = 0; -} - - -/* same, but don't touch the sign. */ - -void -ecleazs (xi) - register unsigned EMUSHORT *xi; -{ - register int i; - - ++xi; - for (i = 0; i < NI - 1; i++) - *xi++ = 0; -} - - - -/* Move internal format number from a to b. - */ -void -emovz (a, b) - register unsigned EMUSHORT *a, *b; -{ - register int i; - - for (i = 0; i < NI - 1; i++) - *b++ = *a++; - /* clear low guard word */ - *b = 0; -} - -/* Generate internal format NaN. - The explicit pattern for this is maximum exponent and - top two significand bits set. */ - -void -einan (x) - unsigned EMUSHORT x[]; -{ - - ecleaz (x); - x[E] = 0x7fff; - x[M + 1] = 0xc000; -} - -/* Return nonzero if internal format number is a NaN. */ - -int -eiisnan (x) - unsigned EMUSHORT x[]; -{ - int i; - - if ((x[E] & 0x7fff) == 0x7fff) - { - for (i = M + 1; i < NI; i++) - { - if (x[i] != 0) - return (1); - } - } - return (0); -} - -/* Fill internal format number with infinity pattern. - This has maximum exponent and significand all zeros. */ - -void -eiinfin (x) - unsigned EMUSHORT x[]; -{ - - ecleaz (x); - x[E] = 0x7fff; -} - -/* Return nonzero if internal format number is infinite. */ - -int -eiisinf (x) - unsigned EMUSHORT x[]; -{ - -#ifdef NANS - if (eiisnan (x)) - return (0); -#endif - if ((x[E] & 0x7fff) == 0x7fff) - return (1); - return (0); -} - - -/* -; Compare significands of numbers in internal format. -; Guard words are included in the comparison. -; -; unsigned EMUSHORT a[NI], b[NI]; -; cmpm (a, b); -; -; for the significands: -; returns +1 if a > b -; 0 if a == b -; -1 if a < b -*/ -int -ecmpm (a, b) - register unsigned EMUSHORT *a, *b; -{ - int i; - - a += M; /* skip up to significand area */ - b += M; - for (i = M; i < NI; i++) - { - if (*a++ != *b++) - goto difrnt; - } - return (0); - - difrnt: - if (*(--a) > *(--b)) - return (1); - else - return (-1); -} - - -/* -; Shift significand down by 1 bit -*/ - -void -eshdn1 (x) - register unsigned EMUSHORT *x; -{ - register unsigned EMUSHORT bits; - int i; - - x += M; /* point to significand area */ - - bits = 0; - for (i = M; i < NI; i++) - { - if (*x & 1) - bits |= 1; - *x >>= 1; - if (bits & 2) - *x |= 0x8000; - bits <<= 1; - ++x; - } -} - - - -/* -; Shift significand up by 1 bit -*/ - -void -eshup1 (x) - register unsigned EMUSHORT *x; -{ - register unsigned EMUSHORT bits; - int i; - - x += NI - 1; - bits = 0; - - for (i = M; i < NI; i++) - { - if (*x & 0x8000) - bits |= 1; - *x <<= 1; - if (bits & 2) - *x |= 1; - bits <<= 1; - --x; - } -} - - - -/* -; Shift significand down by 8 bits -*/ - -void -eshdn8 (x) - register unsigned EMUSHORT *x; -{ - register unsigned EMUSHORT newbyt, oldbyt; - int i; - - x += M; - oldbyt = 0; - for (i = M; i < NI; i++) - { - newbyt = *x << 8; - *x >>= 8; - *x |= oldbyt; - oldbyt = newbyt; - ++x; - } -} - -/* -; Shift significand up by 8 bits -*/ - -void -eshup8 (x) - register unsigned EMUSHORT *x; -{ - int i; - register unsigned EMUSHORT newbyt, oldbyt; - - x += NI - 1; - oldbyt = 0; - - for (i = M; i < NI; i++) - { - newbyt = *x >> 8; - *x <<= 8; - *x |= oldbyt; - oldbyt = newbyt; - --x; - } -} - -/* -; Shift significand up by 16 bits -*/ - -void -eshup6 (x) - register unsigned EMUSHORT *x; -{ - int i; - register unsigned EMUSHORT *p; - - p = x + M; - x += M + 1; - - for (i = M; i < NI - 1; i++) - *p++ = *x++; - - *p = 0; -} - -/* -; Shift significand down by 16 bits -*/ - -void -eshdn6 (x) - register unsigned EMUSHORT *x; -{ - int i; - register unsigned EMUSHORT *p; - - x += NI - 1; - p = x + 1; - - for (i = M; i < NI - 1; i++) - *(--p) = *(--x); - - *(--p) = 0; -} - -/* -; Add significands -; x + y replaces y -*/ - -void -eaddm (x, y) - unsigned EMUSHORT *x, *y; -{ - register unsigned EMULONG a; - int i; - unsigned int carry; - - x += NI - 1; - y += NI - 1; - carry = 0; - for (i = M; i < NI; i++) - { - a = (unsigned EMULONG) (*x) + (unsigned EMULONG) (*y) + carry; - if (a & 0x10000) - carry = 1; - else - carry = 0; - *y = (unsigned EMUSHORT) a; - --x; - --y; - } -} - -/* -; Subtract significands -; y - x replaces y -*/ - -void -esubm (x, y) - unsigned EMUSHORT *x, *y; -{ - unsigned EMULONG a; - int i; - unsigned int carry; - - x += NI - 1; - y += NI - 1; - carry = 0; - for (i = M; i < NI; i++) - { - a = (unsigned EMULONG) (*y) - (unsigned EMULONG) (*x) - carry; - if (a & 0x10000) - carry = 1; - else - carry = 0; - *y = (unsigned EMUSHORT) a; - --x; - --y; - } -} - - -/* Divide significands */ - -static unsigned EMUSHORT equot[NI]; - -int -edivm (den, num) - unsigned EMUSHORT den[], num[]; -{ - int i; - register unsigned EMUSHORT *p, *q; - unsigned EMUSHORT j; - - p = &equot[0]; - *p++ = num[0]; - *p++ = num[1]; - - for (i = M; i < NI; i++) - { - *p++ = 0; - } - - /* Use faster compare and subtraction if denominator - * has only 15 bits of significance. - */ - p = &den[M + 2]; - if (*p++ == 0) - { - for (i = M + 3; i < NI; i++) - { - if (*p++ != 0) - goto fulldiv; - } - if ((den[M + 1] & 1) != 0) - goto fulldiv; - eshdn1 (num); - eshdn1 (den); - - p = &den[M + 1]; - q = &num[M + 1]; - - for (i = 0; i < NBITS + 2; i++) - { - if (*p <= *q) - { - *q -= *p; - j = 1; - } - else - { - j = 0; - } - eshup1 (equot); - equot[NI - 2] |= j; - eshup1 (num); - } - goto divdon; - } - - /* The number of quotient bits to calculate is - * NBITS + 1 scaling guard bit + 1 roundoff bit. - */ - fulldiv: - - p = &equot[NI - 2]; - for (i = 0; i < NBITS + 2; i++) - { - if (ecmpm (den, num) <= 0) - { - esubm (den, num); - j = 1; /* quotient bit = 1 */ - } - else - j = 0; - eshup1 (equot); - *p |= j; - eshup1 (num); - } - - divdon: - - eshdn1 (equot); - eshdn1 (equot); - - /* test for nonzero remainder after roundoff bit */ - p = &num[M]; - j = 0; - for (i = M; i < NI; i++) - { - j |= *p++; - } - if (j) - j = 1; - - - for (i = 0; i < NI; i++) - num[i] = equot[i]; - return ((int) j); -} - - -/* Multiply significands */ -int -emulm (a, b) - unsigned EMUSHORT a[], b[]; -{ - unsigned EMUSHORT *p, *q; - int i, j, k; - - equot[0] = b[0]; - equot[1] = b[1]; - for (i = M; i < NI; i++) - equot[i] = 0; - - p = &a[NI - 2]; - k = NBITS; - while (*p == 0) /* significand is not supposed to be all zero */ - { - eshdn6 (a); - k -= 16; - } - if ((*p & 0xff) == 0) - { - eshdn8 (a); - k -= 8; - } - - q = &equot[NI - 1]; - j = 0; - for (i = 0; i < k; i++) - { - if (*p & 1) - eaddm (b, equot); - /* remember if there were any nonzero bits shifted out */ - if (*q & 1) - j |= 1; - eshdn1 (a); - eshdn1 (equot); - } - - for (i = 0; i < NI; i++) - b[i] = equot[i]; - - /* return flag for lost nonzero bits */ - return (j); -} - - - -/* - * Normalize and round off. - * - * The internal format number to be rounded is "s". - * Input "lost" indicates whether or not the number is exact. - * This is the so-called sticky bit. - * - * Input "subflg" indicates whether the number was obtained - * by a subtraction operation. In that case if lost is nonzero - * then the number is slightly smaller than indicated. - * - * Input "exp" is the biased exponent, which may be negative. - * the exponent field of "s" is ignored but is replaced by - * "exp" as adjusted by normalization and rounding. - * - * Input "rcntrl" is the rounding control. - */ - -static int rlast = -1; -static int rw = 0; -static unsigned EMUSHORT rmsk = 0; -static unsigned EMUSHORT rmbit = 0; -static unsigned EMUSHORT rebit = 0; -static int re = 0; -static unsigned EMUSHORT rbit[NI]; - -void -emdnorm (s, lost, subflg, exp, rcntrl) - unsigned EMUSHORT s[]; - int lost; - int subflg; - EMULONG exp; - int rcntrl; -{ - int i, j; - unsigned EMUSHORT r; - - /* Normalize */ - j = enormlz (s); - - /* a blank significand could mean either zero or infinity. */ -#ifndef INFINITY - if (j > NBITS) - { - ecleazs (s); - return; - } -#endif - exp -= j; -#ifndef INFINITY - if (exp >= 32767L) - goto overf; -#else - if ((j > NBITS) && (exp < 32767)) - { - ecleazs (s); - return; - } -#endif - if (exp < 0L) - { - if (exp > (EMULONG) (-NBITS - 1)) - { - j = (int) exp; - i = eshift (s, j); - if (i) - lost = 1; - } - else - { - ecleazs (s); - return; - } - } - /* Round off, unless told not to by rcntrl. */ - if (rcntrl == 0) - goto mdfin; - /* Set up rounding parameters if the control register changed. */ - if (rndprc != rlast) - { - ecleaz (rbit); - switch (rndprc) - { - default: - case NBITS: - rw = NI - 1; /* low guard word */ - rmsk = 0xffff; - rmbit = 0x8000; - rbit[rw - 1] = 1; - re = NI - 2; - rebit = 1; - break; - case 64: - rw = 7; - rmsk = 0xffff; - rmbit = 0x8000; - rbit[rw - 1] = 1; - re = rw - 1; - rebit = 1; - break; - /* For DEC arithmetic */ - case 56: - rw = 6; - rmsk = 0xff; - rmbit = 0x80; - rbit[rw] = 0x100; - re = rw; - rebit = 0x100; - break; - case 53: - rw = 6; - rmsk = 0x7ff; - rmbit = 0x0400; - rbit[rw] = 0x800; - re = rw; - rebit = 0x800; - break; - case 24: - rw = 4; - rmsk = 0xff; - rmbit = 0x80; - rbit[rw] = 0x100; - re = rw; - rebit = 0x100; - break; - } - rlast = rndprc; - } - - if (rndprc >= 64) - { - r = s[rw] & rmsk; - if (rndprc == 64) - { - i = rw + 1; - while (i < NI) - { - if (s[i]) - r |= 1; - s[i] = 0; - ++i; - } - } - } - else - { - if (exp <= 0) - eshdn1 (s); - r = s[rw] & rmsk; - /* These tests assume NI = 8 */ - i = rw + 1; - while (i < NI) - { - if (s[i]) - r |= 1; - s[i] = 0; - ++i; - } - /* - if (rndprc == 24) - { - if (s[5] || s[6]) - r |= 1; - s[5] = 0; - s[6] = 0; - } - */ - } - s[rw] &= ~rmsk; - if ((r & rmbit) != 0) - { - if (r == rmbit) - { - if (lost == 0) - { /* round to even */ - if ((s[re] & rebit) == 0) - goto mddone; - } - else - { - if (subflg != 0) - goto mddone; - } - } - eaddm (rbit, s); - } - mddone: - if ((rndprc < 64) && (exp <= 0)) - { - eshup1 (s); - } - if (s[2] != 0) - { /* overflow on roundoff */ - eshdn1 (s); - exp += 1; - } - mdfin: - s[NI - 1] = 0; - if (exp >= 32767L) - { -#ifndef INFINITY - overf: -#endif -#ifdef INFINITY - s[1] = 32767; - for (i = 2; i < NI - 1; i++) - s[i] = 0; - if (extra_warnings) - warning ("floating point overflow"); -#else - s[1] = 32766; - s[2] = 0; - for (i = M + 1; i < NI - 1; i++) - s[i] = 0xffff; - s[NI - 1] = 0; - if (rndprc < 64) - { - s[rw] &= ~rmsk; - if (rndprc == 24) - { - s[5] = 0; - s[6] = 0; - } - } -#endif - return; - } - if (exp < 0) - s[1] = 0; - else - s[1] = (unsigned EMUSHORT) exp; -} - - - -/* -; Subtract external format numbers. -; -; unsigned EMUSHORT a[NE], b[NE], c[NE]; -; esub (a, b, c); c = b - a -*/ - -static int subflg = 0; - -void -esub (a, b, c) - unsigned EMUSHORT *a, *b, *c; -{ - -#ifdef NANS - if (eisnan (a)) - { - emov (a, c); - return; - } - if (eisnan (b)) - { - emov (b, c); - return; - } -/* Infinity minus infinity is a NaN. - Test for subtracting infinities of the same sign. */ - if (eisinf (a) && eisinf (b) - && ((eisneg (a) ^ eisneg (b)) == 0)) - { - mtherr ("esub", INVALID); - enan (c); - return; - } -#endif - subflg = 1; - eadd1 (a, b, c); -} - - -/* -; Add. -; -; unsigned EMUSHORT a[NE], b[NE], c[NE]; -; eadd (a, b, c); c = b + a -*/ -void -eadd (a, b, c) - unsigned EMUSHORT *a, *b, *c; -{ - -#ifdef NANS -/* NaN plus anything is a NaN. */ - if (eisnan (a)) - { - emov (a, c); - return; - } - if (eisnan (b)) - { - emov (b, c); - return; - } -/* Infinity minus infinity is a NaN. - Test for adding infinities of opposite signs. */ - if (eisinf (a) && eisinf (b) - && ((eisneg (a) ^ eisneg (b)) != 0)) - { - mtherr ("esub", INVALID); - enan (c); - return; - } -#endif - subflg = 0; - eadd1 (a, b, c); -} - -void -eadd1 (a, b, c) - unsigned EMUSHORT *a, *b, *c; -{ - unsigned EMUSHORT ai[NI], bi[NI], ci[NI]; - int i, lost, j, k; - EMULONG lt, lta, ltb; - -#ifdef INFINITY - if (eisinf (a)) - { - emov (a, c); - if (subflg) - eneg (c); - return; - } - if (eisinf (b)) - { - emov (b, c); - return; - } -#endif - emovi (a, ai); - emovi (b, bi); - if (subflg) - ai[0] = ~ai[0]; - - /* compare exponents */ - lta = ai[E]; - ltb = bi[E]; - lt = lta - ltb; - if (lt > 0L) - { /* put the larger number in bi */ - emovz (bi, ci); - emovz (ai, bi); - emovz (ci, ai); - ltb = bi[E]; - lt = -lt; - } - lost = 0; - if (lt != 0L) - { - if (lt < (EMULONG) (-NBITS - 1)) - goto done; /* answer same as larger addend */ - k = (int) lt; - lost = eshift (ai, k); /* shift the smaller number down */ - } - else - { - /* exponents were the same, so must compare significands */ - i = ecmpm (ai, bi); - if (i == 0) - { /* the numbers are identical in magnitude */ - /* if different signs, result is zero */ - if (ai[0] != bi[0]) - { - eclear (c); - return; - } - /* if same sign, result is double */ - /* double denomalized tiny number */ - if ((bi[E] == 0) && ((bi[3] & 0x8000) == 0)) - { - eshup1 (bi); - goto done; - } - /* add 1 to exponent unless both are zero! */ - for (j = 1; j < NI - 1; j++) - { - if (bi[j] != 0) - { - /* This could overflow, but let emovo take care of that. */ - ltb += 1; - break; - } - } - bi[E] = (unsigned EMUSHORT) ltb; - goto done; - } - if (i > 0) - { /* put the larger number in bi */ - emovz (bi, ci); - emovz (ai, bi); - emovz (ci, ai); - } - } - if (ai[0] == bi[0]) - { - eaddm (ai, bi); - subflg = 0; - } - else - { - esubm (ai, bi); - subflg = 1; - } - emdnorm (bi, lost, subflg, ltb, 64); - - done: - emovo (bi, c); -} - - - -/* -; Divide. -; -; unsigned EMUSHORT a[NE], b[NE], c[NE]; -; ediv (a, b, c); c = b / a -*/ -void -ediv (a, b, c) - unsigned EMUSHORT *a, *b, *c; -{ - unsigned EMUSHORT ai[NI], bi[NI]; - int i; - EMULONG lt, lta, ltb; - -#ifdef NANS -/* Return any NaN input. */ - if (eisnan (a)) - { - emov (a, c); - return; - } - if (eisnan (b)) - { - emov (b, c); - return; - } -/* Zero over zero, or infinity over infinity, is a NaN. */ - if (((ecmp (a, ezero) == 0) && (ecmp (b, ezero) == 0)) - || (eisinf (a) && eisinf (b))) - { - mtherr ("ediv", INVALID); - enan (c); - return; - } -#endif -/* Infinity over anything else is infinity. */ -#ifdef INFINITY - if (eisinf (b)) - { - if (eisneg (a) ^ eisneg (b)) - *(c + (NE - 1)) = 0x8000; - else - *(c + (NE - 1)) = 0; - einfin (c); - return; - } -/* Anything else over infinity is zero. */ - if (eisinf (a)) - { - eclear (c); - return; - } -#endif - emovi (a, ai); - emovi (b, bi); - lta = ai[E]; - ltb = bi[E]; - if (bi[E] == 0) - { /* See if numerator is zero. */ - for (i = 1; i < NI - 1; i++) - { - if (bi[i] != 0) - { - ltb -= enormlz (bi); - goto dnzro1; - } - } - eclear (c); - return; - } - dnzro1: - - if (ai[E] == 0) - { /* possible divide by zero */ - for (i = 1; i < NI - 1; i++) - { - if (ai[i] != 0) - { - lta -= enormlz (ai); - goto dnzro2; - } - } - if (ai[0] == bi[0]) - *(c + (NE - 1)) = 0; - else - *(c + (NE - 1)) = 0x8000; -/* Divide by zero is not an invalid operation. - It is a divide-by-zero operation! */ - einfin (c); - mtherr ("ediv", SING); - return; - } - dnzro2: - - i = edivm (ai, bi); - /* calculate exponent */ - lt = ltb - lta + EXONE; - emdnorm (bi, i, 0, lt, 64); - /* set the sign */ - if (ai[0] == bi[0]) - bi[0] = 0; - else - bi[0] = 0Xffff; - emovo (bi, c); -} - - - -/* -; Multiply. -; -; unsigned EMUSHORT a[NE], b[NE], c[NE]; -; emul (a, b, c); c = b * a -*/ -void -emul (a, b, c) - unsigned EMUSHORT *a, *b, *c; -{ - unsigned EMUSHORT ai[NI], bi[NI]; - int i, j; - EMULONG lt, lta, ltb; - -#ifdef NANS -/* NaN times anything is the same NaN. */ - if (eisnan (a)) - { - emov (a, c); - return; - } - if (eisnan (b)) - { - emov (b, c); - return; - } -/* Zero times infinity is a NaN. */ - if ((eisinf (a) && (ecmp (b, ezero) == 0)) - || (eisinf (b) && (ecmp (a, ezero) == 0))) - { - mtherr ("emul", INVALID); - enan (c); - return; - } -#endif -/* Infinity times anything else is infinity. */ -#ifdef INFINITY - if (eisinf (a) || eisinf (b)) - { - if (eisneg (a) ^ eisneg (b)) - *(c + (NE - 1)) = 0x8000; - else - *(c + (NE - 1)) = 0; - einfin (c); - return; - } -#endif - emovi (a, ai); - emovi (b, bi); - lta = ai[E]; - ltb = bi[E]; - if (ai[E] == 0) - { - for (i = 1; i < NI - 1; i++) - { - if (ai[i] != 0) - { - lta -= enormlz (ai); - goto mnzer1; - } - } - eclear (c); - return; - } - mnzer1: - - if (bi[E] == 0) - { - for (i = 1; i < NI - 1; i++) - { - if (bi[i] != 0) - { - ltb -= enormlz (bi); - goto mnzer2; - } - } - eclear (c); - return; - } - mnzer2: - - /* Multiply significands */ - j = emulm (ai, bi); - /* calculate exponent */ - lt = lta + ltb - (EXONE - 1); - emdnorm (bi, j, 0, lt, 64); - /* calculate sign of product */ - if (ai[0] == bi[0]) - bi[0] = 0; - else - bi[0] = 0xffff; - emovo (bi, c); -} - - - - -/* -; Convert IEEE double precision to e type -; double d; -; unsigned EMUSHORT x[N+2]; -; e53toe (&d, x); -*/ -void -e53toe (pe, y) - unsigned EMUSHORT *pe, *y; -{ -#ifdef DEC - - dectoe (pe, y); /* see etodec.c */ - -#else - - register unsigned EMUSHORT r; - register unsigned EMUSHORT *e, *p; - unsigned EMUSHORT yy[NI]; - int denorm, k; - - e = pe; - denorm = 0; /* flag if denormalized number */ - ecleaz (yy); -#ifdef IBMPC - e += 3; -#endif - r = *e; - yy[0] = 0; - if (r & 0x8000) - yy[0] = 0xffff; - yy[M] = (r & 0x0f) | 0x10; - r &= ~0x800f; /* strip sign and 4 significand bits */ -#ifdef INFINITY - if (r == 0x7ff0) - { -#ifdef NANS -#ifdef IBMPC - if (((pe[3] & 0xf) != 0) || (pe[2] != 0) - || (pe[1] != 0) || (pe[0] != 0)) - { - enan (y); - return; - } -#else - if (((pe[0] & 0xf) != 0) || (pe[1] != 0) - || (pe[2] != 0) || (pe[3] != 0)) - { - enan (y); - return; - } -#endif -#endif /* NANS */ - eclear (y); - einfin (y); - if (yy[0]) - eneg (y); - return; - } -#endif /* INFINITY */ - r >>= 4; - /* If zero exponent, then the significand is denormalized. - * So, take back the understood high significand bit. */ - if (r == 0) - { - denorm = 1; - yy[M] &= ~0x10; - } - r += EXONE - 01777; - yy[E] = r; - p = &yy[M + 1]; -#ifdef IBMPC - *p++ = *(--e); - *p++ = *(--e); - *p++ = *(--e); -#endif -#ifdef MIEEE - ++e; - *p++ = *e++; - *p++ = *e++; - *p++ = *e++; -#endif - eshift (yy, -5); - if (denorm) - { /* if zero exponent, then normalize the significand */ - if ((k = enormlz (yy)) > NBITS) - ecleazs (yy); - else - yy[E] -= (unsigned EMUSHORT) (k - 1); - } - emovo (yy, y); -#endif /* not DEC */ -} - -void -e64toe (pe, y) - unsigned EMUSHORT *pe, *y; -{ - unsigned EMUSHORT yy[NI]; - unsigned EMUSHORT *e, *p, *q; - int i; - - e = pe; - p = yy; - for (i = 0; i < NE - 5; i++) - *p++ = 0; -#ifdef IBMPC - for (i = 0; i < 5; i++) - *p++ = *e++; -#endif -#ifdef DEC - for (i = 0; i < 5; i++) - *p++ = *e++; -#endif -#ifdef MIEEE - p = &yy[0] + (NE - 1); - *p-- = *e++; - ++e; - for (i = 0; i < 4; i++) - *p-- = *e++; -#endif - p = yy; - q = y; -#ifdef INFINITY - if (*p == 0x7fff) - { -#ifdef NANS -#ifdef IBMPC - for (i = 0; i < 4; i++) - { - if (pe[i] != 0) - { - enan (y); - return; - } - } -#else - for (i = 1; i <= 4; i++) - { - if (pe[i] != 0) - { - enan (y); - return; - } - } -#endif -#endif /* NANS */ - eclear (y); - einfin (y); - if (*p & 0x8000) - eneg (y); - return; - } -#endif /* INFINITY */ - for (i = 0; i < NE; i++) - *q++ = *p++; -} - - -/* -; Convert IEEE single precision to e type -; float d; -; unsigned EMUSHORT x[N+2]; -; dtox (&d, x); -*/ -void -e24toe (pe, y) - unsigned EMUSHORT *pe, *y; -{ - register unsigned EMUSHORT r; - register unsigned EMUSHORT *e, *p; - unsigned EMUSHORT yy[NI]; - int denorm, k; - - e = pe; - denorm = 0; /* flag if denormalized number */ - ecleaz (yy); -#ifdef IBMPC - e += 1; -#endif -#ifdef DEC - e += 1; -#endif - r = *e; - yy[0] = 0; - if (r & 0x8000) - yy[0] = 0xffff; - yy[M] = (r & 0x7f) | 0200; - r &= ~0x807f; /* strip sign and 7 significand bits */ -#ifdef INFINITY - if (r == 0x7f80) - { -#ifdef NANS -#ifdef MIEEE - if (((pe[0] & 0x7f) != 0) || (pe[1] != 0)) - { - enan (y); - return; - } -#else - if (((pe[1] & 0x7f) != 0) || (pe[0] != 0)) - { - enan (y); - return; - } -#endif -#endif /* NANS */ - eclear (y); - einfin (y); - if (yy[0]) - eneg (y); - return; - } -#endif /* INFINITY */ - r >>= 7; - /* If zero exponent, then the significand is denormalized. - * So, take back the understood high significand bit. */ - if (r == 0) - { - denorm = 1; - yy[M] &= ~0200; - } - r += EXONE - 0177; - yy[E] = r; - p = &yy[M + 1]; -#ifdef IBMPC - *p++ = *(--e); -#endif -#ifdef DEC - *p++ = *(--e); -#endif -#ifdef MIEEE - ++e; - *p++ = *e++; -#endif - eshift (yy, -8); - if (denorm) - { /* if zero exponent, then normalize the significand */ - if ((k = enormlz (yy)) > NBITS) - ecleazs (yy); - else - yy[E] -= (unsigned EMUSHORT) (k - 1); - } - emovo (yy, y); -} - - -void -etoe64 (x, e) - unsigned EMUSHORT *x, *e; -{ - unsigned EMUSHORT xi[NI]; - EMULONG exp; - int rndsav; - -#ifdef NANS - if (eisnan (x)) - { - make_nan (e, XFmode); - return; - } -#endif - emovi (x, xi); - /* adjust exponent for offset */ - exp = (EMULONG) xi[E]; -#ifdef INFINITY - if (eisinf (x)) - goto nonorm; -#endif - /* round off to nearest or even */ - rndsav = rndprc; - rndprc = 64; - emdnorm (xi, 0, 0, exp, 64); - rndprc = rndsav; - nonorm: - toe64 (xi, e); -} - -/* move out internal format to ieee long double */ -static void -toe64 (a, b) - unsigned EMUSHORT *a, *b; -{ - register unsigned EMUSHORT *p, *q; - unsigned EMUSHORT i; - -#ifdef NANS - if (eiisnan (a)) - { - make_nan (b, XFmode); - return; - } -#endif - p = a; -#ifdef MIEEE - q = b; -#else - q = b + 4; /* point to output exponent */ -#if LONG_DOUBLE_TYPE_SIZE == 96 - /* Clear the last two bytes of 12-byte Intel format */ - *(q+1) = 0; -#endif -#endif - - /* combine sign and exponent */ - i = *p++; -#ifdef MIEEE - if (i) - *q++ = *p++ | 0x8000; - else - *q++ = *p++; - *q++ = 0; -#else - if (i) - *q-- = *p++ | 0x8000; - else - *q-- = *p++; -#endif - /* skip over guard word */ - ++p; - /* move the significand */ -#ifdef MIEEE - for (i = 0; i < 4; i++) - *q++ = *p++; -#else - for (i = 0; i < 4; i++) - *q-- = *p++; -#endif -} - - -/* -; e type to IEEE double precision -; double d; -; unsigned EMUSHORT x[NE]; -; etoe53 (x, &d); -*/ - -#ifdef DEC - -void -etoe53 (x, e) - unsigned EMUSHORT *x, *e; -{ - etodec (x, e); /* see etodec.c */ -} - -static void -toe53 (x, y) - unsigned EMUSHORT *x, *y; -{ - todec (x, y); -} - -#else - -void -etoe53 (x, e) - unsigned EMUSHORT *x, *e; -{ - unsigned EMUSHORT xi[NI]; - EMULONG exp; - int rndsav; - -#ifdef NANS - if (eisnan (x)) - { - make_nan (e, DFmode); - return; - } -#endif - emovi (x, xi); - /* adjust exponent for offsets */ - exp = (EMULONG) xi[E] - (EXONE - 0x3ff); -#ifdef INFINITY - if (eisinf (x)) - goto nonorm; -#endif - /* round off to nearest or even */ - rndsav = rndprc; - rndprc = 53; - emdnorm (xi, 0, 0, exp, 64); - rndprc = rndsav; - nonorm: - toe53 (xi, e); -} - - -static void -toe53 (x, y) - unsigned EMUSHORT *x, *y; -{ - unsigned EMUSHORT i; - unsigned EMUSHORT *p; - -#ifdef NANS - if (eiisnan (x)) - { - make_nan (y, DFmode); - return; - } -#endif - p = &x[0]; -#ifdef IBMPC - y += 3; -#endif - *y = 0; /* output high order */ - if (*p++) - *y = 0x8000; /* output sign bit */ - - i = *p++; - if (i >= (unsigned int) 2047) - { /* Saturate at largest number less than infinity. */ -#ifdef INFINITY - *y |= 0x7ff0; -#ifdef IBMPC - *(--y) = 0; - *(--y) = 0; - *(--y) = 0; -#endif -#ifdef MIEEE - ++y; - *y++ = 0; - *y++ = 0; - *y++ = 0; -#endif -#else - *y |= (unsigned EMUSHORT) 0x7fef; -#ifdef IBMPC - *(--y) = 0xffff; - *(--y) = 0xffff; - *(--y) = 0xffff; -#endif -#ifdef MIEEE - ++y; - *y++ = 0xffff; - *y++ = 0xffff; - *y++ = 0xffff; -#endif -#endif - return; - } - if (i == 0) - { - eshift (x, 4); - } - else - { - i <<= 4; - eshift (x, 5); - } - i |= *p++ & (unsigned EMUSHORT) 0x0f; /* *p = xi[M] */ - *y |= (unsigned EMUSHORT) i; /* high order output already has sign bit set */ -#ifdef IBMPC - *(--y) = *p++; - *(--y) = *p++; - *(--y) = *p; -#endif -#ifdef MIEEE - ++y; - *y++ = *p++; - *y++ = *p++; - *y++ = *p++; -#endif -} - -#endif /* not DEC */ - - - -/* -; e type to IEEE single precision -; float d; -; unsigned EMUSHORT x[N+2]; -; xtod (x, &d); -*/ -void -etoe24 (x, e) - unsigned EMUSHORT *x, *e; -{ - EMULONG exp; - unsigned EMUSHORT xi[NI]; - int rndsav; - -#ifdef NANS - if (eisnan (x)) - { - make_nan (e, SFmode); - return; - } -#endif - emovi (x, xi); - /* adjust exponent for offsets */ - exp = (EMULONG) xi[E] - (EXONE - 0177); -#ifdef INFINITY - if (eisinf (x)) - goto nonorm; -#endif - /* round off to nearest or even */ - rndsav = rndprc; - rndprc = 24; - emdnorm (xi, 0, 0, exp, 64); - rndprc = rndsav; - nonorm: - toe24 (xi, e); -} - -static void -toe24 (x, y) - unsigned EMUSHORT *x, *y; -{ - unsigned EMUSHORT i; - unsigned EMUSHORT *p; - -#ifdef NANS - if (eiisnan (x)) - { - make_nan (y, SFmode); - return; - } -#endif - p = &x[0]; -#ifdef IBMPC - y += 1; -#endif -#ifdef DEC - y += 1; -#endif - *y = 0; /* output high order */ - if (*p++) - *y = 0x8000; /* output sign bit */ - - i = *p++; -/* Handle overflow cases. */ - if (i >= 255) - { -#ifdef INFINITY - *y |= (unsigned EMUSHORT) 0x7f80; -#ifdef IBMPC - *(--y) = 0; -#endif -#ifdef DEC - *(--y) = 0; -#endif -#ifdef MIEEE - ++y; - *y = 0; -#endif -#else /* no INFINITY */ - *y |= (unsigned EMUSHORT) 0x7f7f; -#ifdef IBMPC - *(--y) = 0xffff; -#endif -#ifdef DEC - *(--y) = 0xffff; -#endif -#ifdef MIEEE - ++y; - *y = 0xffff; -#endif -#ifdef ERANGE - errno = ERANGE; -#endif -#endif /* no INFINITY */ - return; - } - if (i == 0) - { - eshift (x, 7); - } - else - { - i <<= 7; - eshift (x, 8); - } - i |= *p++ & (unsigned EMUSHORT) 0x7f; /* *p = xi[M] */ - *y |= i; /* high order output already has sign bit set */ -#ifdef IBMPC - *(--y) = *p; -#endif -#ifdef DEC - *(--y) = *p; -#endif -#ifdef MIEEE - ++y; - *y = *p; -#endif -} - - -/* Compare two e type numbers. - * - * unsigned EMUSHORT a[NE], b[NE]; - * ecmp (a, b); - * - * returns +1 if a > b - * 0 if a == b - * -1 if a < b - * -2 if either a or b is a NaN. - */ -int -ecmp (a, b) - unsigned EMUSHORT *a, *b; -{ - unsigned EMUSHORT ai[NI], bi[NI]; - register unsigned EMUSHORT *p, *q; - register int i; - int msign; - -#ifdef NANS - if (eisnan (a) || eisnan (b)) - return (-2); -#endif - emovi (a, ai); - p = ai; - emovi (b, bi); - q = bi; - - if (*p != *q) - { /* the signs are different */ - /* -0 equals + 0 */ - for (i = 1; i < NI - 1; i++) - { - if (ai[i] != 0) - goto nzro; - if (bi[i] != 0) - goto nzro; - } - return (0); - nzro: - if (*p == 0) - return (1); - else - return (-1); - } - /* both are the same sign */ - if (*p == 0) - msign = 1; - else - msign = -1; - i = NI - 1; - do - { - if (*p++ != *q++) - { - goto diff; - } - } - while (--i > 0); - - return (0); /* equality */ - - - - diff: - - if (*(--p) > *(--q)) - return (msign); /* p is bigger */ - else - return (-msign); /* p is littler */ -} - - - - -/* Find nearest integer to x = floor (x + 0.5) - * - * unsigned EMUSHORT x[NE], y[NE] - * eround (x, y); - */ -void -eround (x, y) - unsigned EMUSHORT *x, *y; -{ - eadd (ehalf, x, y); - efloor (y, y); -} - - - - -/* -; convert long integer to e type -; -; long l; -; unsigned EMUSHORT x[NE]; -; ltoe (&l, x); -; note &l is the memory address of l -*/ -void -ltoe (lp, y) - long *lp; /* lp is the memory address of a long integer */ - unsigned EMUSHORT *y; /* y is the address of a short */ -{ - unsigned EMUSHORT yi[NI]; - unsigned long ll; - int k; - - ecleaz (yi); - if (*lp < 0) - { - /* make it positive */ - ll = (unsigned long) (-(*lp)); - yi[0] = 0xffff; /* put correct sign in the e type number */ - } - else - { - ll = (unsigned long) (*lp); - } - /* move the long integer to yi significand area */ - yi[M] = (unsigned EMUSHORT) (ll >> 16); - yi[M + 1] = (unsigned EMUSHORT) ll; - - yi[E] = EXONE + 15; /* exponent if normalize shift count were 0 */ - if ((k = enormlz (yi)) > NBITS)/* normalize the significand */ - ecleaz (yi); /* it was zero */ - else - yi[E] -= (unsigned EMUSHORT) k;/* subtract shift count from exponent */ - emovo (yi, y); /* output the answer */ -} - -/* -; convert unsigned long integer to e type -; -; unsigned long l; -; unsigned EMUSHORT x[NE]; -; ltox (&l, x); -; note &l is the memory address of l -*/ -void -ultoe (lp, y) - unsigned long *lp; /* lp is the memory address of a long integer */ - unsigned EMUSHORT *y; /* y is the address of a short */ -{ - unsigned EMUSHORT yi[NI]; - unsigned long ll; - int k; - - ecleaz (yi); - ll = *lp; - - /* move the long integer to ayi significand area */ - yi[M] = (unsigned EMUSHORT) (ll >> 16); - yi[M + 1] = (unsigned EMUSHORT) ll; - - yi[E] = EXONE + 15; /* exponent if normalize shift count were 0 */ - if ((k = enormlz (yi)) > NBITS)/* normalize the significand */ - ecleaz (yi); /* it was zero */ - else - yi[E] -= (unsigned EMUSHORT) k; /* subtract shift count from exponent */ - emovo (yi, y); /* output the answer */ -} - - -/* -; Find long integer and fractional parts - -; long i; -; unsigned EMUSHORT x[NE], frac[NE]; -; xifrac (x, &i, frac); - - The integer output has the sign of the input. The fraction is -the positive fractional part of abs (x). -*/ -void -eifrac (x, i, frac) - unsigned EMUSHORT *x; - long *i; - unsigned EMUSHORT *frac; -{ - unsigned EMUSHORT xi[NI]; - int k; - - emovi (x, xi); - k = (int) xi[E] - (EXONE - 1); - if (k <= 0) - { - /* if exponent <= 0, integer = 0 and real output is fraction */ - *i = 0L; - emovo (xi, frac); - return; - } - if (k > (HOST_BITS_PER_LONG - 1)) - { - /* - ; long integer overflow: output large integer - ; and correct fraction - */ - if (xi[0]) - *i = ((unsigned long) 1) << (HOST_BITS_PER_LONG - 1); - else - *i = (((unsigned long) 1) << (HOST_BITS_PER_LONG - 1)) - 1; - eshift (xi, k); - if (extra_warnings) - warning ("overflow on truncation to integer"); - goto lab11; - } - - if (k > 16) - { - /* - ; shift more than 16 bits: shift up k-16, output the integer, - ; then complete the shift to get the fraction. - */ - k -= 16; - eshift (xi, k); - - *i = (long) (((unsigned long) xi[M] << 16) | xi[M + 1]); - eshup6 (xi); - goto lab10; - } - - /* shift not more than 16 bits */ - eshift (xi, k); - *i = (long) xi[M] & 0xffff; - - lab10: - - if (xi[0]) - *i = -(*i); - lab11: - - xi[0] = 0; - xi[E] = EXONE - 1; - xi[M] = 0; - if ((k = enormlz (xi)) > NBITS) - ecleaz (xi); - else - xi[E] -= (unsigned EMUSHORT) k; - - emovo (xi, frac); -} - - -/* -; Find unsigned long integer and fractional parts - -; unsigned long i; -; unsigned EMUSHORT x[NE], frac[NE]; -; xifrac (x, &i, frac); - - A negative e type input yields integer output = 0 - but correct fraction. -*/ -void -euifrac (x, i, frac) - unsigned EMUSHORT *x; - long *i; - unsigned EMUSHORT *frac; -{ - unsigned EMUSHORT xi[NI]; - int k; - - emovi (x, xi); - k = (int) xi[E] - (EXONE - 1); - if (k <= 0) - { - /* if exponent <= 0, integer = 0 and argument is fraction */ - *i = 0L; - emovo (xi, frac); - return; - } - if (k > 32) - { - /* - ; long integer overflow: output large integer - ; and correct fraction - */ - *i = ~(0L); - eshift (xi, k); - if (extra_warnings) - warning ("overflow on truncation to unsigned integer"); - goto lab10; - } - - if (k > 16) - { - /* - ; shift more than 16 bits: shift up k-16, output the integer, - ; then complete the shift to get the fraction. - */ - k -= 16; - eshift (xi, k); - - *i = (long) (((unsigned long) xi[M] << 16) | xi[M + 1]); - eshup6 (xi); - goto lab10; - } - - /* shift not more than 16 bits */ - eshift (xi, k); - *i = (long) xi[M] & 0xffff; - - lab10: - - if (xi[0]) - *i = 0L; - - xi[0] = 0; - xi[E] = EXONE - 1; - xi[M] = 0; - if ((k = enormlz (xi)) > NBITS) - ecleaz (xi); - else - xi[E] -= (unsigned EMUSHORT) k; - - emovo (xi, frac); -} - - - -/* -; Shift significand -; -; Shifts significand area up or down by the number of bits -; given by the variable sc. -*/ -int -eshift (x, sc) - unsigned EMUSHORT *x; - int sc; -{ - unsigned EMUSHORT lost; - unsigned EMUSHORT *p; - - if (sc == 0) - return (0); - - lost = 0; - p = x + NI - 1; - - if (sc < 0) - { - sc = -sc; - while (sc >= 16) - { - lost |= *p; /* remember lost bits */ - eshdn6 (x); - sc -= 16; - } - - while (sc >= 8) - { - lost |= *p & 0xff; - eshdn8 (x); - sc -= 8; - } - - while (sc > 0) - { - lost |= *p & 1; - eshdn1 (x); - sc -= 1; - } - } - else - { - while (sc >= 16) - { - eshup6 (x); - sc -= 16; - } - - while (sc >= 8) - { - eshup8 (x); - sc -= 8; - } - - while (sc > 0) - { - eshup1 (x); - sc -= 1; - } - } - if (lost) - lost = 1; - return ((int) lost); -} - - - -/* -; normalize -; -; Shift normalizes the significand area pointed to by argument -; shift count (up = positive) is returned. -*/ -int -enormlz (x) - unsigned EMUSHORT x[]; -{ - register unsigned EMUSHORT *p; - int sc; - - sc = 0; - p = &x[M]; - if (*p != 0) - goto normdn; - ++p; - if (*p & 0x8000) - return (0); /* already normalized */ - while (*p == 0) - { - eshup6 (x); - sc += 16; - /* With guard word, there are NBITS+16 bits available. - * return true if all are zero. - */ - if (sc > NBITS) - return (sc); - } - /* see if high byte is zero */ - while ((*p & 0xff00) == 0) - { - eshup8 (x); - sc += 8; - } - /* now shift 1 bit at a time */ - while ((*p & 0x8000) == 0) - { - eshup1 (x); - sc += 1; - if (sc > NBITS) - { - mtherr ("enormlz", UNDERFLOW); - return (sc); - } - } - return (sc); - - /* Normalize by shifting down out of the high guard word - of the significand */ - normdn: - - if (*p & 0xff00) - { - eshdn8 (x); - sc -= 8; - } - while (*p != 0) - { - eshdn1 (x); - sc -= 1; - - if (sc < -NBITS) - { - mtherr ("enormlz", OVERFLOW); - return (sc); - } - } - return (sc); -} - - - - -/* Convert e type number to decimal format ASCII string. - * The constants are for 64 bit precision. - */ - -#define NTEN 12 -#define MAXP 4096 - -static unsigned EMUSHORT etens[NTEN + 1][NE] = -{ - {0xc94c, 0x979a, 0x8a20, 0x5202, 0xc460, 0x7525,}, /* 10**4096 */ - {0xa74d, 0x5de4, 0xc53d, 0x3b5d, 0x9e8b, 0x5a92,}, /* 10**2048 */ - {0x650d, 0x0c17, 0x8175, 0x7586, 0xc976, 0x4d48,}, - {0xcc65, 0x91c6, 0xa60e, 0xa0ae, 0xe319, 0x46a3,}, - {0xddbc, 0xde8d, 0x9df9, 0xebfb, 0xaa7e, 0x4351,}, - {0xc66f, 0x8cdf, 0x80e9, 0x47c9, 0x93ba, 0x41a8,}, - {0x3cbf, 0xa6d5, 0xffcf, 0x1f49, 0xc278, 0x40d3,}, - {0xf020, 0xb59d, 0x2b70, 0xada8, 0x9dc5, 0x4069,}, - {0x0000, 0x0000, 0x0400, 0xc9bf, 0x8e1b, 0x4034,}, - {0x0000, 0x0000, 0x0000, 0x2000, 0xbebc, 0x4019,}, - {0x0000, 0x0000, 0x0000, 0x0000, 0x9c40, 0x400c,}, - {0x0000, 0x0000, 0x0000, 0x0000, 0xc800, 0x4005,}, - {0x0000, 0x0000, 0x0000, 0x0000, 0xa000, 0x4002,}, /* 10**1 */ -}; - -static unsigned EMUSHORT emtens[NTEN + 1][NE] = -{ - {0x2de4, 0x9fde, 0xd2ce, 0x04c8, 0xa6dd, 0x0ad8,}, /* 10**-4096 */ - {0x4925, 0x2de4, 0x3436, 0x534f, 0xceae, 0x256b,}, /* 10**-2048 */ - {0x87a6, 0xc0bd, 0xda57, 0x82a5, 0xa2a6, 0x32b5,}, - {0x7133, 0xd21c, 0xdb23, 0xee32, 0x9049, 0x395a,}, - {0xfa91, 0x1939, 0x637a, 0x4325, 0xc031, 0x3cac,}, - {0xac7d, 0xe4a0, 0x64bc, 0x467c, 0xddd0, 0x3e55,}, - {0x3f24, 0xe9a5, 0xa539, 0xea27, 0xa87f, 0x3f2a,}, - {0x67de, 0x94ba, 0x4539, 0x1ead, 0xcfb1, 0x3f94,}, - {0x4c2f, 0xe15b, 0xc44d, 0x94be, 0xe695, 0x3fc9,}, - {0xfdc2, 0xcefc, 0x8461, 0x7711, 0xabcc, 0x3fe4,}, - {0xd3c3, 0x652b, 0xe219, 0x1758, 0xd1b7, 0x3ff1,}, - {0x3d71, 0xd70a, 0x70a3, 0x0a3d, 0xa3d7, 0x3ff8,}, - {0xcccd, 0xcccc, 0xcccc, 0xcccc, 0xcccc, 0x3ffb,}, /* 10**-1 */ -}; - -void -e24toasc (x, string, ndigs) - unsigned EMUSHORT x[]; - char *string; - int ndigs; -{ - unsigned EMUSHORT w[NI]; - - e24toe (x, w); - etoasc (w, string, ndigs); -} - - -void -e53toasc (x, string, ndigs) - unsigned EMUSHORT x[]; - char *string; - int ndigs; -{ - unsigned EMUSHORT w[NI]; - - e53toe (x, w); - etoasc (w, string, ndigs); -} - - -void -e64toasc (x, string, ndigs) - unsigned EMUSHORT x[]; - char *string; - int ndigs; -{ - unsigned EMUSHORT w[NI]; - - e64toe (x, w); - etoasc (w, string, ndigs); -} - - -static char wstring[80]; /* working storage for ASCII output */ - -void -etoasc (x, string, ndigs) - unsigned EMUSHORT x[]; - char *string; - int ndigs; -{ - EMUSHORT digit; - unsigned EMUSHORT y[NI], t[NI], u[NI], w[NI]; - unsigned EMUSHORT *p, *r, *ten; - unsigned EMUSHORT sign; - int i, j, k, expon, rndsav; - char *s, *ss; - unsigned EMUSHORT m; - - - rndsav = rndprc; - ss = string; - s = wstring; - *ss = '\0'; - *s = '\0'; -#ifdef NANS - if (eisnan (x)) - { - sprintf (wstring, " NaN "); - goto bxit; - } -#endif - rndprc = NBITS; /* set to full precision */ - emov (x, y); /* retain external format */ - if (y[NE - 1] & 0x8000) - { - sign = 0xffff; - y[NE - 1] &= 0x7fff; - } - else - { - sign = 0; - } - expon = 0; - ten = &etens[NTEN][0]; - emov (eone, t); - /* Test for zero exponent */ - if (y[NE - 1] == 0) - { - for (k = 0; k < NE - 1; k++) - { - if (y[k] != 0) - goto tnzro; /* denormalized number */ - } - goto isone; /* legal all zeros */ - } - tnzro: - - /* Test for infinity. */ - if (y[NE - 1] == 0x7fff) - { - if (sign) - sprintf (wstring, " -Infinity "); - else - sprintf (wstring, " Infinity "); - goto bxit; - } - - /* Test for exponent nonzero but significand denormalized. - * This is an error condition. - */ - if ((y[NE - 1] != 0) && ((y[NE - 2] & 0x8000) == 0)) - { - mtherr ("etoasc", DOMAIN); - sprintf (wstring, "NaN"); - goto bxit; - } - - /* Compare to 1.0 */ - i = ecmp (eone, y); - if (i == 0) - goto isone; - - if (i == -2) - abort (); - - if (i < 0) - { /* Number is greater than 1 */ - /* Convert significand to an integer and strip trailing decimal zeros. */ - emov (y, u); - u[NE - 1] = EXONE + NBITS - 1; - - p = &etens[NTEN - 4][0]; - m = 16; - do - { - ediv (p, u, t); - efloor (t, w); - for (j = 0; j < NE - 1; j++) - { - if (t[j] != w[j]) - goto noint; - } - emov (t, u); - expon += (int) m; - noint: - p += NE; - m >>= 1; - } - while (m != 0); - - /* Rescale from integer significand */ - u[NE - 1] += y[NE - 1] - (unsigned int) (EXONE + NBITS - 1); - emov (u, y); - /* Find power of 10 */ - emov (eone, t); - m = MAXP; - p = &etens[0][0]; - /* An unordered compare result shouldn't happen here. */ - while (ecmp (ten, u) <= 0) - { - if (ecmp (p, u) <= 0) - { - ediv (p, u, u); - emul (p, t, t); - expon += (int) m; - } - m >>= 1; - if (m == 0) - break; - p += NE; - } - } - else - { /* Number is less than 1.0 */ - /* Pad significand with trailing decimal zeros. */ - if (y[NE - 1] == 0) - { - while ((y[NE - 2] & 0x8000) == 0) - { - emul (ten, y, y); - expon -= 1; - } - } - else - { - emovi (y, w); - for (i = 0; i < NDEC + 1; i++) - { - if ((w[NI - 1] & 0x7) != 0) - break; - /* multiply by 10 */ - emovz (w, u); - eshdn1 (u); - eshdn1 (u); - eaddm (w, u); - u[1] += 3; - while (u[2] != 0) - { - eshdn1 (u); - u[1] += 1; - } - if (u[NI - 1] != 0) - break; - if (eone[NE - 1] <= u[1]) - break; - emovz (u, w); - expon -= 1; - } - emovo (w, y); - } - k = -MAXP; - p = &emtens[0][0]; - r = &etens[0][0]; - emov (y, w); - emov (eone, t); - while (ecmp (eone, w) > 0) - { - if (ecmp (p, w) >= 0) - { - emul (r, w, w); - emul (r, t, t); - expon += k; - } - k /= 2; - if (k == 0) - break; - p += NE; - r += NE; - } - ediv (t, eone, t); - } - isone: - /* Find the first (leading) digit. */ - emovi (t, w); - emovz (w, t); - emovi (y, w); - emovz (w, y); - eiremain (t, y); - digit = equot[NI - 1]; - while ((digit == 0) && (ecmp (y, ezero) != 0)) - { - eshup1 (y); - emovz (y, u); - eshup1 (u); - eshup1 (u); - eaddm (u, y); - eiremain (t, y); - digit = equot[NI - 1]; - expon -= 1; - } - s = wstring; - if (sign) - *s++ = '-'; - else - *s++ = ' '; - /* Examine number of digits requested by caller. */ - if (ndigs < 0) - ndigs = 0; - if (ndigs > NDEC) - ndigs = NDEC; - if (digit == 10) - { - *s++ = '1'; - *s++ = '.'; - if (ndigs > 0) - { - *s++ = '0'; - ndigs -= 1; - } - expon += 1; - } - else - { - *s++ = (char )digit + '0'; - *s++ = '.'; - } - /* Generate digits after the decimal point. */ - for (k = 0; k <= ndigs; k++) - { - /* multiply current number by 10, without normalizing */ - eshup1 (y); - emovz (y, u); - eshup1 (u); - eshup1 (u); - eaddm (u, y); - eiremain (t, y); - *s++ = (char) equot[NI - 1] + '0'; - } - digit = equot[NI - 1]; - --s; - ss = s; - /* round off the ASCII string */ - if (digit > 4) - { - /* Test for critical rounding case in ASCII output. */ - if (digit == 5) - { - emovo (y, t); - if (ecmp (t, ezero) != 0) - goto roun; /* round to nearest */ - if ((*(s - 1) & 1) == 0) - goto doexp; /* round to even */ - } - /* Round up and propagate carry-outs */ - roun: - --s; - k = *s & 0x7f; - /* Carry out to most significant digit? */ - if (k == '.') - { - --s; - k = *s; - k += 1; - *s = (char) k; - /* Most significant digit carries to 10? */ - if (k > '9') - { - expon += 1; - *s = '1'; - } - goto doexp; - } - /* Round up and carry out from less significant digits */ - k += 1; - *s = (char) k; - if (k > '9') - { - *s = '0'; - goto roun; - } - } - doexp: - /* - if (expon >= 0) - sprintf (ss, "e+%d", expon); - else - sprintf (ss, "e%d", expon); - */ - sprintf (ss, "e%d", expon); - bxit: - rndprc = rndsav; - /* copy out the working string */ - s = string; - ss = wstring; - while (*ss == ' ') /* strip possible leading space */ - ++ss; - while ((*s++ = *ss++) != '\0') - ; -} - - - - -/* -; ASCTOQ -; ASCTOQ.MAC LATEST REV: 11 JAN 84 -; SLM, 3 JAN 78 -; -; Convert ASCII string to quadruple precision floating point -; -; Numeric input is free field decimal number -; with max of 15 digits with or without -; decimal point entered as ASCII from teletype. -; Entering E after the number followed by a second -; number causes the second number to be interpreted -; as a power of 10 to be multiplied by the first number -; (i.e., "scientific" notation). -; -; Usage: -; asctoq (string, q); -*/ - -/* ASCII to single */ -void -asctoe24 (s, y) - char *s; - unsigned EMUSHORT *y; -{ - asctoeg (s, y, 24); -} - - -/* ASCII to double */ -void -asctoe53 (s, y) - char *s; - unsigned EMUSHORT *y; -{ -#ifdef DEC - asctoeg (s, y, 56); -#else - asctoeg (s, y, 53); -#endif -} - - -/* ASCII to long double */ -void -asctoe64 (s, y) - char *s; - unsigned EMUSHORT *y; -{ - asctoeg (s, y, 64); -} - -/* ASCII to super double */ -void -asctoe (s, y) - char *s; - unsigned EMUSHORT *y; -{ - asctoeg (s, y, NBITS); -} - -/* Space to make a copy of the input string: */ -static char lstr[82]; - -void -asctoeg (ss, y, oprec) - char *ss; - unsigned EMUSHORT *y; - int oprec; -{ - unsigned EMUSHORT yy[NI], xt[NI], tt[NI]; - int esign, decflg, sgnflg, nexp, exp, prec, lost; - int k, trail, c, rndsav; - EMULONG lexp; - unsigned EMUSHORT nsign, *p; - char *sp, *s; - - /* Copy the input string. */ - s = ss; - while (*s == ' ') /* skip leading spaces */ - ++s; - sp = lstr; - for (k = 0; k < 79; k++) - { - if ((*sp++ = *s++) == '\0') - break; - } - *sp = '\0'; - s = lstr; - - rndsav = rndprc; - rndprc = NBITS; /* Set to full precision */ - lost = 0; - nsign = 0; - decflg = 0; - sgnflg = 0; - nexp = 0; - exp = 0; - prec = 0; - ecleaz (yy); - trail = 0; - - nxtcom: - k = *s - '0'; - if ((k >= 0) && (k <= 9)) - { - /* Ignore leading zeros */ - if ((prec == 0) && (decflg == 0) && (k == 0)) - goto donchr; - /* Identify and strip trailing zeros after the decimal point. */ - if ((trail == 0) && (decflg != 0)) - { - sp = s; - while ((*sp >= '0') && (*sp <= '9')) - ++sp; - /* Check for syntax error */ - c = *sp & 0x7f; - if ((c != 'e') && (c != 'E') && (c != '\0') - && (c != '\n') && (c != '\r') && (c != ' ') - && (c != ',')) - goto error; - --sp; - while (*sp == '0') - *sp-- = 'z'; - trail = 1; - if (*s == 'z') - goto donchr; - } - /* If enough digits were given to more than fill up the yy register, - * continuing until overflow into the high guard word yy[2] - * guarantees that there will be a roundoff bit at the top - * of the low guard word after normalization. - */ - if (yy[2] == 0) - { - if (decflg) - nexp += 1; /* count digits after decimal point */ - eshup1 (yy); /* multiply current number by 10 */ - emovz (yy, xt); - eshup1 (xt); - eshup1 (xt); - eaddm (xt, yy); - ecleaz (xt); - xt[NI - 2] = (unsigned EMUSHORT) k; - eaddm (xt, yy); - } - else - { - lost |= k; - } - prec += 1; - goto donchr; - } - - switch (*s) - { - case 'z': - break; - case 'E': - case 'e': - goto expnt; - case '.': /* decimal point */ - if (decflg) - goto error; - ++decflg; - break; - case '-': - nsign = 0xffff; - if (sgnflg) - goto error; - ++sgnflg; - break; - case '+': - if (sgnflg) - goto error; - ++sgnflg; - break; - case ',': - case ' ': - case '\0': - case '\n': - case '\r': - goto daldone; - case 'i': - case 'I': - goto infinite; - default: - error: -#ifdef NANS - einan (yy); -#else - mtherr ("asctoe", DOMAIN); - eclear (yy); -#endif - goto aexit; - } - donchr: - ++s; - goto nxtcom; - - /* Exponent interpretation */ - expnt: - - esign = 1; - exp = 0; - ++s; - /* check for + or - */ - if (*s == '-') - { - esign = -1; - ++s; - } - if (*s == '+') - ++s; - while ((*s >= '0') && (*s <= '9')) - { - exp *= 10; - exp += *s++ - '0'; - if (exp > 4956) - { - if (esign < 0) - goto zero; - else - goto infinite; - } - } - if (esign < 0) - exp = -exp; - if (exp > 4932) - { - infinite: - ecleaz (yy); - yy[E] = 0x7fff; /* infinity */ - goto aexit; - } - if (exp < -4956) - { - zero: - ecleaz (yy); - goto aexit; - } - - daldone: - nexp = exp - nexp; - /* Pad trailing zeros to minimize power of 10, per IEEE spec. */ - while ((nexp > 0) && (yy[2] == 0)) - { - emovz (yy, xt); - eshup1 (xt); - eshup1 (xt); - eaddm (yy, xt); - eshup1 (xt); - if (xt[2] != 0) - break; - nexp -= 1; - emovz (xt, yy); - } - if ((k = enormlz (yy)) > NBITS) - { - ecleaz (yy); - goto aexit; - } - lexp = (EXONE - 1 + NBITS) - k; - emdnorm (yy, lost, 0, lexp, 64); - /* convert to external format */ - - - /* Multiply by 10**nexp. If precision is 64 bits, - * the maximum relative error incurred in forming 10**n - * for 0 <= n <= 324 is 8.2e-20, at 10**180. - * For 0 <= n <= 999, the peak relative error is 1.4e-19 at 10**947. - * For 0 >= n >= -999, it is -1.55e-19 at 10**-435. - */ - lexp = yy[E]; - if (nexp == 0) - { - k = 0; - goto expdon; - } - esign = 1; - if (nexp < 0) - { - nexp = -nexp; - esign = -1; - if (nexp > 4096) - { /* Punt. Can't handle this without 2 divides. */ - emovi (etens[0], tt); - lexp -= tt[E]; - k = edivm (tt, yy); - lexp += EXONE; - nexp -= 4096; - } - } - p = &etens[NTEN][0]; - emov (eone, xt); - exp = 1; - do - { - if (exp & nexp) - emul (p, xt, xt); - p -= NE; - exp = exp + exp; - } - while (exp <= MAXP); - - emovi (xt, tt); - if (esign < 0) - { - lexp -= tt[E]; - k = edivm (tt, yy); - lexp += EXONE; - } - else - { - lexp += tt[E]; - k = emulm (tt, yy); - lexp -= EXONE - 1; - } - - expdon: - - /* Round and convert directly to the destination type */ - if (oprec == 53) - lexp -= EXONE - 0x3ff; - else if (oprec == 24) - lexp -= EXONE - 0177; -#ifdef DEC - else if (oprec == 56) - lexp -= EXONE - 0201; -#endif - rndprc = oprec; - emdnorm (yy, k, 0, lexp, 64); - - aexit: - - rndprc = rndsav; - yy[0] = nsign; - switch (oprec) - { -#ifdef DEC - case 56: - todec (yy, y); /* see etodec.c */ - break; -#endif - case 53: - toe53 (yy, y); - break; - case 24: - toe24 (yy, y); - break; - case 64: - toe64 (yy, y); - break; - case NBITS: - emovo (yy, y); - break; - } -} - - - -/* y = largest integer not greater than x - * (truncated toward minus infinity) - * - * unsigned EMUSHORT x[NE], y[NE] - * - * efloor (x, y); - */ -static unsigned EMUSHORT bmask[] = -{ - 0xffff, - 0xfffe, - 0xfffc, - 0xfff8, - 0xfff0, - 0xffe0, - 0xffc0, - 0xff80, - 0xff00, - 0xfe00, - 0xfc00, - 0xf800, - 0xf000, - 0xe000, - 0xc000, - 0x8000, - 0x0000, -}; - -void -efloor (x, y) - unsigned EMUSHORT x[], y[]; -{ - register unsigned EMUSHORT *p; - int e, expon, i; - unsigned EMUSHORT f[NE]; - - emov (x, f); /* leave in external format */ - expon = (int) f[NE - 1]; - e = (expon & 0x7fff) - (EXONE - 1); - if (e <= 0) - { - eclear (y); - goto isitneg; - } - /* number of bits to clear out */ - e = NBITS - e; - emov (f, y); - if (e <= 0) - return; - - p = &y[0]; - while (e >= 16) - { - *p++ = 0; - e -= 16; - } - /* clear the remaining bits */ - *p &= bmask[e]; - /* truncate negatives toward minus infinity */ - isitneg: - - if ((unsigned EMUSHORT) expon & (unsigned EMUSHORT) 0x8000) - { - for (i = 0; i < NE - 1; i++) - { - if (f[i] != y[i]) - { - esub (eone, y, y); - break; - } - } - } -} - - -/* unsigned EMUSHORT x[], s[]; - * int *exp; - * - * efrexp (x, exp, s); - * - * Returns s and exp such that s * 2**exp = x and .5 <= s < 1. - * For example, 1.1 = 0.55 * 2**1 - * Handles denormalized numbers properly using long integer exp. - */ -void -efrexp (x, exp, s) - unsigned EMUSHORT x[]; - int *exp; - unsigned EMUSHORT s[]; -{ - unsigned EMUSHORT xi[NI]; - EMULONG li; - - emovi (x, xi); - li = (EMULONG) ((EMUSHORT) xi[1]); - - if (li == 0) - { - li -= enormlz (xi); - } - xi[1] = 0x3ffe; - emovo (xi, s); - *exp = (int) (li - 0x3ffe); -} - - - -/* unsigned EMUSHORT x[], y[]; - * long pwr2; - * - * eldexp (x, pwr2, y); - * - * Returns y = x * 2**pwr2. - */ -void -eldexp (x, pwr2, y) - unsigned EMUSHORT x[]; - int pwr2; - unsigned EMUSHORT y[]; -{ - unsigned EMUSHORT xi[NI]; - EMULONG li; - int i; - - emovi (x, xi); - li = xi[1]; - li += pwr2; - i = 0; - emdnorm (xi, i, i, li, 64); - emovo (xi, y); -} - - -/* c = remainder after dividing b by a - * Least significant integer quotient bits left in equot[]. - */ -void -eremain (a, b, c) - unsigned EMUSHORT a[], b[], c[]; -{ - unsigned EMUSHORT den[NI], num[NI]; - -#ifdef NANS - if ( eisinf (b) - || (ecmp (a, ezero) == 0) - || eisnan (a) - || eisnan (b)) - { - enan (c); - return; - } -#endif - if (ecmp (a, ezero) == 0) - { - mtherr ("eremain", SING); - eclear (c); - return; - } - emovi (a, den); - emovi (b, num); - eiremain (den, num); - /* Sign of remainder = sign of quotient */ - if (a[0] == b[0]) - num[0] = 0; - else - num[0] = 0xffff; - emovo (num, c); -} - -void -eiremain (den, num) - unsigned EMUSHORT den[], num[]; -{ - EMULONG ld, ln; - unsigned EMUSHORT j; - - ld = den[E]; - ld -= enormlz (den); - ln = num[E]; - ln -= enormlz (num); - ecleaz (equot); - while (ln >= ld) - { - if (ecmpm (den, num) <= 0) - { - esubm (den, num); - j = 1; - } - else - { - j = 0; - } - eshup1 (equot); - equot[NI - 1] |= j; - eshup1 (num); - ln -= 1; - } - emdnorm (num, 0, 0, ln, 0); -} - -/* mtherr.c - * - * Library common error handling routine - * - * - * - * SYNOPSIS: - * - * char *fctnam; - * int code; - * void mtherr (); - * - * mtherr (fctnam, code); - * - * - * - * DESCRIPTION: - * - * This routine may be called to report one of the following - * error conditions (in the include file mconf.h). - * - * Mnemonic Value Significance - * - * DOMAIN 1 argument domain error - * SING 2 function singularity - * OVERFLOW 3 overflow range error - * UNDERFLOW 4 underflow range error - * TLOSS 5 total loss of precision - * PLOSS 6 partial loss of precision - * INVALID 7 NaN - producing operation - * EDOM 33 Unix domain error code - * ERANGE 34 Unix range error code - * - * The default version of the file prints the function name, - * passed to it by the pointer fctnam, followed by the - * error condition. The display is directed to the standard - * output device. The routine then returns to the calling - * program. Users may wish to modify the program to abort by - * calling exit under severe error conditions such as domain - * errors. - * - * Since all error conditions pass control to this function, - * the display may be easily changed, eliminated, or directed - * to an error logging device. - * - * SEE ALSO: - * - * mconf.h - * - */ - -/* -Cephes Math Library Release 2.0: April, 1987 -Copyright 1984, 1987 by Stephen L. Moshier -Direct inquiries to 30 Frost Street, Cambridge, MA 02140 -*/ - -/* include "mconf.h" */ - -/* Notice: the order of appearance of the following - * messages is bound to the error codes defined - * in mconf.h. - */ -#define NMSGS 8 -static char *ermsg[NMSGS] = -{ - "unknown", /* error code 0 */ - "domain", /* error code 1 */ - "singularity", /* et seq. */ - "overflow", - "underflow", - "total loss of precision", - "partial loss of precision", - "invalid operation" -}; - -int merror = 0; -extern int merror; - -void -mtherr (name, code) - char *name; - int code; -{ - char errstr[80]; - - /* Display string passed by calling program, - * which is supposed to be the name of the - * function in which the error occurred. - */ - - /* Display error message defined - * by the code argument. - */ - if ((code <= 0) || (code >= NMSGS)) - code = 0; - sprintf (errstr, " %s %s error", name, ermsg[code]); - if (extra_warnings) - warning (errstr); - /* Set global error message word */ - merror = code + 1; - - /* Return to calling - * program - */ -} - -/* Here is etodec.c . - * - */ - -/* -; convert DEC double precision to e type -; double d; -; EMUSHORT e[NE]; -; dectoe (&d, e); -*/ -void -dectoe (d, e) - unsigned EMUSHORT *d; - unsigned EMUSHORT *e; -{ - unsigned EMUSHORT y[NI]; - register unsigned EMUSHORT r, *p; - - ecleaz (y); /* start with a zero */ - p = y; /* point to our number */ - r = *d; /* get DEC exponent word */ - if (*d & (unsigned int) 0x8000) - *p = 0xffff; /* fill in our sign */ - ++p; /* bump pointer to our exponent word */ - r &= 0x7fff; /* strip the sign bit */ - if (r == 0) /* answer = 0 if high order DEC word = 0 */ - goto done; - - - r >>= 7; /* shift exponent word down 7 bits */ - r += EXONE - 0201; /* subtract DEC exponent offset */ - /* add our e type exponent offset */ - *p++ = r; /* to form our exponent */ - - r = *d++; /* now do the high order mantissa */ - r &= 0177; /* strip off the DEC exponent and sign bits */ - r |= 0200; /* the DEC understood high order mantissa bit */ - *p++ = r; /* put result in our high guard word */ - - *p++ = *d++; /* fill in the rest of our mantissa */ - *p++ = *d++; - *p = *d; - - eshdn8 (y); /* shift our mantissa down 8 bits */ - done: - emovo (y, e); -} - - - -/* -; convert e type to DEC double precision -; double d; -; EMUSHORT e[NE]; -; etodec (e, &d); -*/ -#if 0 -static unsigned EMUSHORT decbit[NI] = {0, 0, 0, 0, 0, 0, 0200, 0}; - -void -etodec (x, d) - unsigned EMUSHORT *x, *d; -{ - unsigned EMUSHORT xi[NI]; - register unsigned EMUSHORT r; - int i, j; - - emovi (x, xi); - *d = 0; - if (xi[0] != 0) - *d = 0100000; - r = xi[E]; - if (r < (EXONE - 128)) - goto zout; - i = xi[M + 4]; - if ((i & 0200) != 0) - { - if ((i & 0377) == 0200) - { - if ((i & 0400) != 0) - { - /* check all less significant bits */ - for (j = M + 5; j < NI; j++) - { - if (xi[j] != 0) - goto yesrnd; - } - } - goto nornd; - } - yesrnd: - eaddm (decbit, xi); - r -= enormlz (xi); - } - - nornd: - - r -= EXONE; - r += 0201; - if (r < 0) - { - zout: - *d++ = 0; - *d++ = 0; - *d++ = 0; - *d++ = 0; - return; - } - if (r >= 0377) - { - *d++ = 077777; - *d++ = -1; - *d++ = -1; - *d++ = -1; - return; - } - r &= 0377; - r <<= 7; - eshup8 (xi); - xi[M] &= 0177; - r |= xi[M]; - *d++ |= r; - *d++ = xi[M + 1]; - *d++ = xi[M + 2]; - *d++ = xi[M + 3]; -} - -#else - -void -etodec (x, d) - unsigned EMUSHORT *x, *d; -{ - unsigned EMUSHORT xi[NI]; - EMULONG exp; - int rndsav; - - emovi (x, xi); - exp = (EMULONG) xi[E] - (EXONE - 0201); /* adjust exponent for offsets */ -/* round off to nearest or even */ - rndsav = rndprc; - rndprc = 56; - emdnorm (xi, 0, 0, exp, 64); - rndprc = rndsav; - todec (xi, d); -} - -void -todec (x, y) - unsigned EMUSHORT *x, *y; -{ - unsigned EMUSHORT i; - unsigned EMUSHORT *p; - - p = x; - *y = 0; - if (*p++) - *y = 0100000; - i = *p++; - if (i == 0) - { - *y++ = 0; - *y++ = 0; - *y++ = 0; - *y++ = 0; - return; - } - if (i > 0377) - { - *y++ |= 077777; - *y++ = 0xffff; - *y++ = 0xffff; - *y++ = 0xffff; -#ifdef ERANGE - errno = ERANGE; -#endif - return; - } - i &= 0377; - i <<= 7; - eshup8 (x); - x[M] &= 0177; - i |= x[M]; - *y++ |= i; - *y++ = x[M + 1]; - *y++ = x[M + 2]; - *y++ = x[M + 3]; -} - -#endif /* not 0 */ - - -/* Output a binary NaN bit pattern in the target machine's format. */ - -/* If special NaN bit patterns are required, define them in tm.h - as arrays of unsigned 16-bit shorts. Otherwise, use the default - patterns here. */ -#ifndef TFMODE_NAN -#ifdef MIEEE -unsigned EMUSHORT TFnan[8] = - {0x7fff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}; -#endif -#ifdef IBMPC -unsigned EMUSHORT TFnan[8] = {0, 0, 0, 0, 0, 0, 0x8000, 0xffff}; -#endif -#endif - -#ifndef XFMODE_NAN -#ifdef MIEEE -unsigned EMUSHORT XFnan[6] = {0x7fff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}; -#endif -#ifdef IBMPC -unsigned EMUSHORT XFnan[6] = {0, 0, 0, 0xc000, 0xffff, 0}; -#endif -#endif - -#ifndef DFMODE_NAN -#ifdef MIEEE -unsigned EMUSHORT DFnan[4] = {0x7fff, 0xffff, 0xffff, 0xffff}; -#endif -#ifdef IBMPC -unsigned EMUSHORT DFnan[4] = {0, 0, 0, 0xfff8}; -#endif -#endif - -#ifndef SFMODE_NAN -#ifdef MIEEE -unsigned EMUSHORT SFnan[2] = {0x7fff, 0xffff}; -#endif -#ifdef IBMPC -unsigned EMUSHORT SFnan[2] = {0, 0xffc0}; -#endif -#endif - - -void -make_nan (nan, mode) -unsigned EMUSHORT *nan; -enum machine_mode mode; -{ - int i, n; - unsigned EMUSHORT *p; - - switch (mode) - { -/* Possibly the `reserved operand' patterns on a VAX can be - used like NaN's, but probably not in the same way as IEEE. */ -#ifndef DEC - case TFmode: - n = 8; - p = TFnan; - break; - case XFmode: - n = 6; - p = XFnan; - break; - case DFmode: - n = 4; - p = DFnan; - break; - case SFmode: - n = 2; - p = SFnan; - break; -#endif - default: - abort (); - } - for (i=0; i < n; i++) - *nan++ = *p++; -} - -#endif /* EMU_NON_COMPILE not defined */ diff --git a/gnu/usr.bin/cc/common/real.h b/gnu/usr.bin/cc/common/real.h deleted file mode 100644 index b9466194ed..0000000000 --- a/gnu/usr.bin/cc/common/real.h +++ /dev/null @@ -1,359 +0,0 @@ -/* Front-end tree definitions for GNU compiler. - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifndef REAL_H_INCLUDED -#define REAL_H_INCLUDED - -/* Define codes for all the float formats that we know of. */ -#define UNKNOWN_FLOAT_FORMAT 0 -#define IEEE_FLOAT_FORMAT 1 -#define VAX_FLOAT_FORMAT 2 -#define IBM_FLOAT_FORMAT 3 - -/* Default to IEEE float if not specified. Nearly all machines use it. */ - -#ifndef TARGET_FLOAT_FORMAT -#define TARGET_FLOAT_FORMAT IEEE_FLOAT_FORMAT -#endif - -#ifndef HOST_FLOAT_FORMAT -#define HOST_FLOAT_FORMAT IEEE_FLOAT_FORMAT -#endif - -#if TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT -#define REAL_INFINITY -#endif - -/* Defining REAL_ARITHMETIC invokes a floating point emulator - that can produce a target machine format differing by more - than just endian-ness from the host's format. The emulator - is also used to support extended real XFmode. */ -#ifndef LONG_DOUBLE_TYPE_SIZE -#define LONG_DOUBLE_TYPE_SIZE 64 -#endif -#if (LONG_DOUBLE_TYPE_SIZE == 96) || defined (REAL_ARITHMETIC) -/* **** Start of software floating point emulator interface macros **** */ - -/* Support 80-bit extended real XFmode if LONG_DOUBLE_TYPE_SIZE - has been defined to be 96 in the tm.h machine file. */ -#if (LONG_DOUBLE_TYPE_SIZE == 96) -#define REAL_IS_NOT_DOUBLE -#define REAL_ARITHMETIC -typedef struct { - HOST_WIDE_INT r[(11 + sizeof (HOST_WIDE_INT))/(sizeof (HOST_WIDE_INT))]; -} realvaluetype; -#define REAL_VALUE_TYPE realvaluetype - -#else /* no XFmode support */ - -#if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT -/* If no XFmode support, then a REAL_VALUE_TYPE is 64 bits wide - but it is not necessarily a host machine double. */ -#define REAL_IS_NOT_DOUBLE -typedef struct { - HOST_WIDE_INT r[(7 + sizeof (HOST_WIDE_INT))/(sizeof (HOST_WIDE_INT))]; -} realvaluetype; -#define REAL_VALUE_TYPE realvaluetype -#else -/* If host and target formats are compatible, then a REAL_VALUE_TYPE - is actually a host machine double. */ -#define REAL_VALUE_TYPE double -#endif -#endif /* no XFmode support */ - -/* If emulation has been enabled by defining REAL_ARITHMETIC or by - setting LONG_DOUBLE_TYPE_SIZE to 96, then define macros so that - they invoke emulator functions. This will succeed only if the machine - files have been updated to use these macros in place of any - references to host machine `double' or `float' types. */ -#ifdef REAL_ARITHMETIC -#undef REAL_ARITHMETIC -#define REAL_ARITHMETIC(value, code, d1, d2) \ - earith (&(value), (code), &(d1), &(d2)) - -/* Declare functions in real.c that are referenced here. */ -void earith (), ereal_from_uint (), ereal_from_int (), ereal_to_int (); -void etarldouble (), etardouble (); -long etarsingle (); -int ereal_cmp (), eroundi (), ereal_isneg (); -unsigned int eroundui (); -REAL_VALUE_TYPE etrunci (), etruncui (), ereal_ldexp (), ereal_atof (); -REAL_VALUE_TYPE ereal_negate (), ereal_truncate (); - -#define REAL_VALUES_EQUAL(x, y) (ereal_cmp ((x), (y)) == 0) -/* true if x < y : */ -#define REAL_VALUES_LESS(x, y) (ereal_cmp ((x), (y)) == -1) -#define REAL_VALUE_LDEXP(x, n) ereal_ldexp (x, n) - -/* These return REAL_VALUE_TYPE: */ -#define REAL_VALUE_RNDZINT(x) (etrunci (x)) -#define REAL_VALUE_UNSIGNED_RNDZINT(x) (etruncui (x)) -extern REAL_VALUE_TYPE real_value_truncate (); -#define REAL_VALUE_TRUNCATE(mode, x) real_value_truncate (mode, x) - -/* These return int: */ -#define REAL_VALUE_FIX(x) (eroundi (x)) -#define REAL_VALUE_UNSIGNED_FIX(x) ((unsigned int) eroundui (x)) - -#define REAL_VALUE_ATOF ereal_atof -#define REAL_VALUE_NEGATE ereal_negate - -#define REAL_VALUE_MINUS_ZERO(x) \ - ((ereal_cmp (x, dconst0) == 0) && (ereal_isneg (x) != 0 )) - -#define REAL_VALUE_TO_INT ereal_to_int -#define REAL_VALUE_FROM_INT(d, i, j) (ereal_from_int (&d, i, j)) -#define REAL_VALUE_FROM_UNSIGNED_INT(d, i, j) (ereal_from_uint (&d, i, j)) - -/* IN is a REAL_VALUE_TYPE. OUT is an array of longs. */ -#define REAL_VALUE_TO_TARGET_LONG_DOUBLE(IN, OUT) (etarldouble ((IN), (OUT))) -#define REAL_VALUE_TO_TARGET_DOUBLE(IN, OUT) (etardouble ((IN), (OUT))) - -/* IN is a REAL_VALUE_TYPE. OUT is a long. */ -#define REAL_VALUE_TO_TARGET_SINGLE(IN, OUT) ((OUT) = etarsingle ((IN))) - -/* Conversions to decimal ASCII string. */ -#define REAL_VALUE_TO_DECIMAL(r, fmt, s) (ereal_to_decimal (r, s)) - -#endif /* REAL_ARITHMETIC defined */ - -/* **** End of software floating point emulator interface macros **** */ -#else /* LONG_DOUBLE_TYPE_SIZE != 96 and REAL_ARITHMETIC not defined */ - -/* old interface */ -#ifdef REAL_ARITHMETIC -/* Defining REAL_IS_NOT_DOUBLE breaks certain initializations - when REAL_ARITHMETIC etc. are not defined. */ - -/* Now see if the host and target machines use the same format. - If not, define REAL_IS_NOT_DOUBLE (even if we end up representing - reals as doubles because we have no better way in this cross compiler.) - This turns off various optimizations that can happen when we know the - compiler's float format matches the target's float format. - */ -#if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT -#define REAL_IS_NOT_DOUBLE -#ifndef REAL_VALUE_TYPE -typedef struct { - HOST_WIDE_INT r[sizeof (double)/sizeof (HOST_WIDE_INT)]; - } realvaluetype; -#define REAL_VALUE_TYPE realvaluetype -#endif /* no REAL_VALUE_TYPE */ -#endif /* formats differ */ -#endif /* 0 */ - -#endif /* emulator not used */ - -/* If we are not cross-compiling, use a `double' to represent the - floating-point value. Otherwise, use some other type - (probably a struct containing an array of longs). */ -#ifndef REAL_VALUE_TYPE -#define REAL_VALUE_TYPE double -#else -#define REAL_IS_NOT_DOUBLE -#endif - -#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT - -/* Convert a type `double' value in host format first to a type `float' - value in host format and then to a single type `long' value which - is the bitwise equivalent of the `float' value. */ -#ifndef REAL_VALUE_TO_TARGET_SINGLE -#define REAL_VALUE_TO_TARGET_SINGLE(IN, OUT) \ -do { float f = (float) (IN); \ - (OUT) = *(long *) &f; \ - } while (0) -#endif - -/* Convert a type `double' value in host format to a pair of type `long' - values which is its bitwise equivalent, but put the two words into - proper word order for the target. */ -#ifndef REAL_VALUE_TO_TARGET_DOUBLE -#if defined (HOST_WORDS_BIG_ENDIAN) == WORDS_BIG_ENDIAN -#define REAL_VALUE_TO_TARGET_DOUBLE(IN, OUT) \ -do { REAL_VALUE_TYPE in = (IN); /* Make sure it's not in a register. */\ - (OUT)[0] = ((long *) &in)[0]; \ - (OUT)[1] = ((long *) &in)[1]; \ - } while (0) -#else -#define REAL_VALUE_TO_TARGET_DOUBLE(IN, OUT) \ -do { REAL_VALUE_TYPE in = (IN); /* Make sure it's not in a register. */\ - (OUT)[1] = ((long *) &in)[0]; \ - (OUT)[0] = ((long *) &in)[1]; \ - } while (0) -#endif -#endif -#endif /* HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT */ - -/* In this configuration, double and long double are the same. */ -#ifndef REAL_VALUE_TO_TARGET_LONG_DOUBLE -#define REAL_VALUE_TO_TARGET_LONG_DOUBLE(a, b) REAL_VALUE_TO_TARGET_DOUBLE (a, b) -#endif - -/* Compare two floating-point values for equality. */ -#ifndef REAL_VALUES_EQUAL -#define REAL_VALUES_EQUAL(x, y) ((x) == (y)) -#endif - -/* Compare two floating-point values for less than. */ -#ifndef REAL_VALUES_LESS -#define REAL_VALUES_LESS(x, y) ((x) < (y)) -#endif - -/* Truncate toward zero to an integer floating-point value. */ -#ifndef REAL_VALUE_RNDZINT -#define REAL_VALUE_RNDZINT(x) ((double) ((int) (x))) -#endif - -/* Truncate toward zero to an unsigned integer floating-point value. */ -#ifndef REAL_VALUE_UNSIGNED_RNDZINT -#define REAL_VALUE_UNSIGNED_RNDZINT(x) ((double) ((unsigned int) (x))) -#endif - -/* Convert a floating-point value to integer, using any rounding mode. */ -#ifndef REAL_VALUE_FIX -#define REAL_VALUE_FIX(x) ((int) (x)) -#endif - -/* Convert a floating-point value to unsigned integer, using any rounding - mode. */ -#ifndef REAL_VALUE_UNSIGNED_FIX -#define REAL_VALUE_UNSIGNED_FIX(x) ((unsigned int) (x)) -#endif - -/* Scale X by Y powers of 2. */ -#ifndef REAL_VALUE_LDEXP -#define REAL_VALUE_LDEXP(x, y) ldexp (x, y) -extern double ldexp (); -#endif - -/* Convert the string X to a floating-point value. */ -#ifndef REAL_VALUE_ATOF -#if 1 -/* Use real.c to convert decimal numbers to binary, ... */ -REAL_VALUE_TYPE ereal_atof (); -#define REAL_VALUE_ATOF(x, s) ereal_atof (x, s) -#else -/* ... or, if you like the host computer's atof, go ahead and use it: */ -#define REAL_VALUE_ATOF(x, s) atof (x) -#if defined (MIPSEL) || defined (MIPSEB) -/* MIPS compiler can't handle parens around the function name. - This problem *does not* appear to be connected with any - macro definition for atof. It does not seem there is one. */ -extern double atof (); -#else -extern double (atof) (); -#endif -#endif -#endif - -/* Negate the floating-point value X. */ -#ifndef REAL_VALUE_NEGATE -#define REAL_VALUE_NEGATE(x) (- (x)) -#endif - -/* Truncate the floating-point value X to mode MODE. This is correct only - for the most common case where the host and target have objects of the same - size and where `float' is SFmode. */ - -/* Don't use REAL_VALUE_TRUNCATE directly--always call real_value_truncate. */ -extern REAL_VALUE_TYPE real_value_truncate (); - -#ifndef REAL_VALUE_TRUNCATE -#define REAL_VALUE_TRUNCATE(mode, x) \ - (GET_MODE_BITSIZE (mode) == sizeof (float) * HOST_BITS_PER_CHAR \ - ? (float) (x) : (x)) -#endif - -/* Determine whether a floating-point value X is infinite. */ -#ifndef REAL_VALUE_ISINF -#define REAL_VALUE_ISINF(x) (target_isinf (x)) -#endif - -/* Determine whether a floating-point value X is a NaN. */ -#ifndef REAL_VALUE_ISNAN -#define REAL_VALUE_ISNAN(x) (target_isnan (x)) -#endif - -/* Determine whether a floating-point value X is negative. */ -#ifndef REAL_VALUE_NEGATIVE -#define REAL_VALUE_NEGATIVE(x) (target_negative (x)) -#endif - -/* Determine whether a floating-point value X is minus 0. */ -#ifndef REAL_VALUE_MINUS_ZERO -#define REAL_VALUE_MINUS_ZERO(x) ((x) == 0 && REAL_VALUE_NEGATIVE (x)) -#endif - -/* Constant real values 0, 1, 2, and -1. */ - -extern REAL_VALUE_TYPE dconst0; -extern REAL_VALUE_TYPE dconst1; -extern REAL_VALUE_TYPE dconst2; -extern REAL_VALUE_TYPE dconstm1; - -/* Union type used for extracting real values from CONST_DOUBLEs - or putting them in. */ - -union real_extract -{ - REAL_VALUE_TYPE d; - HOST_WIDE_INT i[sizeof (REAL_VALUE_TYPE) / sizeof (HOST_WIDE_INT)]; -}; - -/* For a CONST_DOUBLE: - The usual two ints that hold the value. - For a DImode, that is all there are; - and CONST_DOUBLE_LOW is the low-order word and ..._HIGH the high-order. - For a float, the number of ints varies, - and CONST_DOUBLE_LOW is the one that should come first *in memory*. - So use &CONST_DOUBLE_LOW(r) as the address of an array of ints. */ -#define CONST_DOUBLE_LOW(r) XWINT (r, 2) -#define CONST_DOUBLE_HIGH(r) XWINT (r, 3) - -/* Link for chain of all CONST_DOUBLEs in use in current function. */ -#define CONST_DOUBLE_CHAIN(r) XEXP (r, 1) -/* The MEM which represents this CONST_DOUBLE's value in memory, - or const0_rtx if no MEM has been made for it yet, - or cc0_rtx if it is not on the chain. */ -#define CONST_DOUBLE_MEM(r) XEXP (r, 0) - -/* Function to return a real value (not a tree node) - from a given integer constant. */ -REAL_VALUE_TYPE real_value_from_int_cst (); - -/* Given a CONST_DOUBLE in FROM, store into TO the value it represents. */ - -#define REAL_VALUE_FROM_CONST_DOUBLE(to, from) \ -do { union real_extract u; \ - bcopy (&CONST_DOUBLE_LOW ((from)), &u, sizeof u); \ - to = u.d; } while (0) - -/* Return a CONST_DOUBLE with value R and mode M. */ - -#define CONST_DOUBLE_FROM_REAL_VALUE(r, m) immed_real_const_1 (r, m) - -/* Convert a floating point value `r', that can be interpreted - as a host machine float or double, to a decimal ASCII string `s' - using printf format string `fmt'. */ -#ifndef REAL_VALUE_TO_DECIMAL -#define REAL_VALUE_TO_DECIMAL(r, fmt, s) (sprintf (s, fmt, r)) -#endif - -#endif /* Not REAL_H_INCLUDED */ diff --git a/gnu/usr.bin/cc/common/recog.c b/gnu/usr.bin/cc/common/recog.c deleted file mode 100644 index 6c869d9de1..0000000000 --- a/gnu/usr.bin/cc/common/recog.c +++ /dev/null @@ -1,1960 +0,0 @@ -/* Subroutines used by or related to instruction recognition. - Copyright (C) 1987, 1988, 1991, 1992 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "config.h" -#include "rtl.h" -#include -#include "insn-config.h" -#include "insn-attr.h" -#include "insn-flags.h" -#include "insn-codes.h" -#include "recog.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "flags.h" -#include "real.h" - -#ifndef STACK_PUSH_CODE -#ifdef STACK_GROWS_DOWNWARD -#define STACK_PUSH_CODE PRE_DEC -#else -#define STACK_PUSH_CODE PRE_INC -#endif -#endif - -/* Import from final.c: */ -extern rtx alter_subreg (); - -int strict_memory_address_p (); -int memory_address_p (); - -/* Nonzero means allow operands to be volatile. - This should be 0 if you are generating rtl, such as if you are calling - the functions in optabs.c and expmed.c (most of the time). - This should be 1 if all valid insns need to be recognized, - such as in regclass.c and final.c and reload.c. - - init_recog and init_recog_no_volatile are responsible for setting this. */ - -int volatile_ok; - -/* On return from `constrain_operands', indicate which alternative - was satisfied. */ - -int which_alternative; - -/* Nonzero after end of reload pass. - Set to 1 or 0 by toplev.c. - Controls the significance of (SUBREG (MEM)). */ - -int reload_completed; - -/* Initialize data used by the function `recog'. - This must be called once in the compilation of a function - before any insn recognition may be done in the function. */ - -void -init_recog_no_volatile () -{ - volatile_ok = 0; -} - -void -init_recog () -{ - volatile_ok = 1; -} - -/* Try recognizing the instruction INSN, - and return the code number that results. - Remeber the code so that repeated calls do not - need to spend the time for actual rerecognition. - - This function is the normal interface to instruction recognition. - The automatically-generated function `recog' is normally called - through this one. (The only exception is in combine.c.) */ - -int -recog_memoized (insn) - rtx insn; -{ - if (INSN_CODE (insn) < 0) - INSN_CODE (insn) = recog (PATTERN (insn), insn, NULL_PTR); - return INSN_CODE (insn); -} - -/* Check that X is an insn-body for an `asm' with operands - and that the operands mentioned in it are legitimate. */ - -int -check_asm_operands (x) - rtx x; -{ - int noperands = asm_noperands (x); - rtx *operands; - int i; - - if (noperands < 0) - return 0; - if (noperands == 0) - return 1; - - operands = (rtx *) alloca (noperands * sizeof (rtx)); - decode_asm_operands (x, operands, NULL_PTR, NULL_PTR, NULL_PTR); - - for (i = 0; i < noperands; i++) - if (!general_operand (operands[i], VOIDmode)) - return 0; - - return 1; -} - -/* Static data for the next two routines. - - The maximum number of changes supported is defined as the maximum - number of operands times 5. This allows for repeated substitutions - inside complex indexed address, or, alternatively, changes in up - to 5 insns. */ - -#define MAX_CHANGE_LOCS (MAX_RECOG_OPERANDS * 5) - -static rtx change_objects[MAX_CHANGE_LOCS]; -static int change_old_codes[MAX_CHANGE_LOCS]; -static rtx *change_locs[MAX_CHANGE_LOCS]; -static rtx change_olds[MAX_CHANGE_LOCS]; - -static int num_changes = 0; - -/* Validate a proposed change to OBJECT. LOC is the location in the rtl for - at which NEW will be placed. If OBJECT is zero, no validation is done, - the change is simply made. - - Two types of objects are supported: If OBJECT is a MEM, memory_address_p - will be called with the address and mode as parameters. If OBJECT is - an INSN, CALL_INSN, or JUMP_INSN, the insn will be re-recognized with - the change in place. - - IN_GROUP is non-zero if this is part of a group of changes that must be - performed as a group. In that case, the changes will be stored. The - function `apply_change_group' will validate and apply the changes. - - If IN_GROUP is zero, this is a single change. Try to recognize the insn - or validate the memory reference with the change applied. If the result - is not valid for the machine, suppress the change and return zero. - Otherwise, perform the change and return 1. */ - -int -validate_change (object, loc, new, in_group) - rtx object; - rtx *loc; - rtx new; - int in_group; -{ - rtx old = *loc; - - if (old == new || rtx_equal_p (old, new)) - return 1; - - if (num_changes >= MAX_CHANGE_LOCS - || (in_group == 0 && num_changes != 0)) - abort (); - - *loc = new; - - /* Save the information describing this change. */ - change_objects[num_changes] = object; - change_locs[num_changes] = loc; - change_olds[num_changes] = old; - - if (object && GET_CODE (object) != MEM) - { - /* Set INSN_CODE to force rerecognition of insn. Save old code in - case invalid. */ - change_old_codes[num_changes] = INSN_CODE (object); - INSN_CODE (object) = -1; - } - - num_changes++; - - /* If we are making a group of changes, return 1. Otherwise, validate the - change group we made. */ - - if (in_group) - return 1; - else - return apply_change_group (); -} - -/* Apply a group of changes previously issued with `validate_change'. - Return 1 if all changes are valid, zero otherwise. */ - -int -apply_change_group () -{ - int i; - - /* The changes have been applied and all INSN_CODEs have been reset to force - rerecognition. - - The changes are valid if we aren't given an object, or if we are - given a MEM and it still is a valid address, or if this is in insn - and it is recognized. In the latter case, if reload has completed, - we also require that the operands meet the constraints for - the insn. We do not allow modifying an ASM_OPERANDS after reload - has completed because verifying the constraints is too difficult. */ - - for (i = 0; i < num_changes; i++) - { - rtx object = change_objects[i]; - - if (object == 0) - continue; - - if (GET_CODE (object) == MEM) - { - if (! memory_address_p (GET_MODE (object), XEXP (object, 0))) - break; - } - else if ((recog_memoized (object) < 0 - && (asm_noperands (PATTERN (object)) < 0 - || ! check_asm_operands (PATTERN (object)) - || reload_completed)) - || (reload_completed - && (insn_extract (object), - ! constrain_operands (INSN_CODE (object), 1)))) - { - rtx pat = PATTERN (object); - - /* Perhaps we couldn't recognize the insn because there were - extra CLOBBERs at the end. If so, try to re-recognize - without the last CLOBBER (later iterations will cause each of - them to be eliminated, in turn). But don't do this if we - have an ASM_OPERAND. */ - if (GET_CODE (pat) == PARALLEL - && GET_CODE (XVECEXP (pat, 0, XVECLEN (pat, 0) - 1)) == CLOBBER - && asm_noperands (PATTERN (object)) < 0) - { - rtx newpat; - - if (XVECLEN (pat, 0) == 2) - newpat = XVECEXP (pat, 0, 0); - else - { - int j; - - newpat = gen_rtx (PARALLEL, VOIDmode, - gen_rtvec (XVECLEN (pat, 0) - 1)); - for (j = 0; j < XVECLEN (newpat, 0); j++) - XVECEXP (newpat, 0, j) = XVECEXP (pat, 0, j); - } - - /* Add a new change to this group to replace the pattern - with this new pattern. Then consider this change - as having succeeded. The change we added will - cause the entire call to fail if things remain invalid. - - Note that this can lose if a later change than the one - we are processing specified &XVECEXP (PATTERN (object), 0, X) - but this shouldn't occur. */ - - validate_change (object, &PATTERN (object), newpat, 1); - } - else if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER) - /* If this insn is a CLOBBER or USE, it is always valid, but is - never recognized. */ - continue; - else - break; - } - } - - if (i == num_changes) - { - num_changes = 0; - return 1; - } - else - { - cancel_changes (0); - return 0; - } -} - -/* Return the number of changes so far in the current group. */ - -int -num_validated_changes () -{ - return num_changes; -} - -/* Retract the changes numbered NUM and up. */ - -void -cancel_changes (num) - int num; -{ - int i; - - /* Back out all the changes. Do this in the opposite order in which - they were made. */ - for (i = num_changes - 1; i >= num; i--) - { - *change_locs[i] = change_olds[i]; - if (change_objects[i] && GET_CODE (change_objects[i]) != MEM) - INSN_CODE (change_objects[i]) = change_old_codes[i]; - } - num_changes = num; -} - -/* Replace every occurrence of FROM in X with TO. Mark each change with - validate_change passing OBJECT. */ - -static void -validate_replace_rtx_1 (loc, from, to, object) - rtx *loc; - rtx from, to, object; -{ - register int i, j; - register char *fmt; - register rtx x = *loc; - enum rtx_code code = GET_CODE (x); - - /* X matches FROM if it is the same rtx or they are both referring to the - same register in the same mode. Avoid calling rtx_equal_p unless the - operands look similar. */ - - if (x == from - || (GET_CODE (x) == REG && GET_CODE (from) == REG - && GET_MODE (x) == GET_MODE (from) - && REGNO (x) == REGNO (from)) - || (GET_CODE (x) == GET_CODE (from) && GET_MODE (x) == GET_MODE (from) - && rtx_equal_p (x, from))) - { - validate_change (object, loc, to, 1); - return; - } - - /* For commutative or comparison operations, try replacing each argument - separately and seeing if we made any changes. If so, put a constant - argument last.*/ - if (GET_RTX_CLASS (code) == '<' || GET_RTX_CLASS (code) == 'c') - { - int prev_changes = num_changes; - - validate_replace_rtx_1 (&XEXP (x, 0), from, to, object); - validate_replace_rtx_1 (&XEXP (x, 1), from, to, object); - if (prev_changes != num_changes && CONSTANT_P (XEXP (x, 0))) - { - validate_change (object, loc, - gen_rtx (GET_RTX_CLASS (code) == 'c' ? code - : swap_condition (code), - GET_MODE (x), XEXP (x, 1), XEXP (x, 0)), - 1); - x = *loc; - code = GET_CODE (x); - } - } - - switch (code) - { - case PLUS: - /* If we have have a PLUS whose second operand is now a CONST_INT, use - plus_constant to try to simplify it. */ - if (GET_CODE (XEXP (x, 1)) == CONST_INT && XEXP (x, 1) == to) - validate_change (object, loc, - plus_constant (XEXP (x, 0), INTVAL (XEXP (x, 1))), 1); - return; - - case ZERO_EXTEND: - case SIGN_EXTEND: - /* In these cases, the operation to be performed depends on the mode - of the operand. If we are replacing the operand with a VOIDmode - constant, we lose the information. So try to simplify the operation - in that case. If it fails, substitute in something that we know - won't be recognized. */ - if (GET_MODE (to) == VOIDmode - && (XEXP (x, 0) == from - || (GET_CODE (XEXP (x, 0)) == REG && GET_CODE (from) == REG - && GET_MODE (XEXP (x, 0)) == GET_MODE (from) - && REGNO (XEXP (x, 0)) == REGNO (from)))) - { - rtx new = simplify_unary_operation (code, GET_MODE (x), to, - GET_MODE (from)); - if (new == 0) - new = gen_rtx (CLOBBER, GET_MODE (x), const0_rtx); - - validate_change (object, loc, new, 1); - return; - } - break; - - case SUBREG: - /* If we have a SUBREG of a register that we are replacing and we are - replacing it with a MEM, make a new MEM and try replacing the - SUBREG with it. Don't do this if the MEM has a mode-dependent address - or if we would be widening it. */ - - if (SUBREG_REG (x) == from - && GET_CODE (from) == REG - && GET_CODE (to) == MEM - && ! mode_dependent_address_p (XEXP (to, 0)) - && ! MEM_VOLATILE_P (to) - && GET_MODE_SIZE (GET_MODE (x)) <= GET_MODE_SIZE (GET_MODE (to))) - { - int offset = SUBREG_WORD (x) * UNITS_PER_WORD; - enum machine_mode mode = GET_MODE (x); - rtx new; - -#if BYTES_BIG_ENDIAN - offset += (MIN (UNITS_PER_WORD, - GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) - - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))); -#endif - - new = gen_rtx (MEM, mode, plus_constant (XEXP (to, 0), offset)); - MEM_VOLATILE_P (new) = MEM_VOLATILE_P (to); - RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (to); - MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (to); - validate_change (object, loc, new, 1); - return; - } - break; - - case ZERO_EXTRACT: - case SIGN_EXTRACT: - /* If we are replacing a register with memory, try to change the memory - to be the mode required for memory in extract operations (this isn't - likely to be an insertion operation; if it was, nothing bad will - happen, we might just fail in some cases). */ - - if (XEXP (x, 0) == from && GET_CODE (from) == REG && GET_CODE (to) == MEM - && GET_CODE (XEXP (x, 1)) == CONST_INT - && GET_CODE (XEXP (x, 2)) == CONST_INT - && ! mode_dependent_address_p (XEXP (to, 0)) - && ! MEM_VOLATILE_P (to)) - { - enum machine_mode wanted_mode = VOIDmode; - enum machine_mode is_mode = GET_MODE (to); - int width = INTVAL (XEXP (x, 1)); - int pos = INTVAL (XEXP (x, 2)); - -#ifdef HAVE_extzv - if (code == ZERO_EXTRACT) - wanted_mode = insn_operand_mode[(int) CODE_FOR_extzv][1]; -#endif -#ifdef HAVE_extv - if (code == SIGN_EXTRACT) - wanted_mode = insn_operand_mode[(int) CODE_FOR_extv][1]; -#endif - - /* If we have a narrower mode, we can do something. */ - if (wanted_mode != VOIDmode - && GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode)) - { - int offset = pos / BITS_PER_UNIT; - rtx newmem; - - /* If the bytes and bits are counted differently, we - must adjust the offset. */ -#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN - offset = (GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (wanted_mode) - - offset); -#endif - - pos %= GET_MODE_BITSIZE (wanted_mode); - - newmem = gen_rtx (MEM, wanted_mode, - plus_constant (XEXP (to, 0), offset)); - RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (to); - MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (to); - MEM_IN_STRUCT_P (newmem) = MEM_IN_STRUCT_P (to); - - validate_change (object, &XEXP (x, 2), GEN_INT (pos), 1); - validate_change (object, &XEXP (x, 0), newmem, 1); - } - } - - break; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - validate_replace_rtx_1 (&XEXP (x, i), from, to, object); - else if (fmt[i] == 'E') - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - validate_replace_rtx_1 (&XVECEXP (x, i, j), from, to, object); - } -} - -/* Try replacing every occurrence of FROM in INSN with TO. After all - changes have been made, validate by seeing if INSN is still valid. */ - -int -validate_replace_rtx (from, to, insn) - rtx from, to, insn; -{ - validate_replace_rtx_1 (&PATTERN (insn), from, to, insn); - return apply_change_group (); -} - -#ifdef HAVE_cc0 -/* Return 1 if the insn using CC0 set by INSN does not contain - any ordered tests applied to the condition codes. - EQ and NE tests do not count. */ - -int -next_insn_tests_no_inequality (insn) - rtx insn; -{ - register rtx next = next_cc0_user (insn); - - /* If there is no next insn, we have to take the conservative choice. */ - if (next == 0) - return 0; - - return ((GET_CODE (next) == JUMP_INSN - || GET_CODE (next) == INSN - || GET_CODE (next) == CALL_INSN) - && ! inequality_comparisons_p (PATTERN (next))); -} - -#if 0 /* This is useless since the insn that sets the cc's - must be followed immediately by the use of them. */ -/* Return 1 if the CC value set up by INSN is not used. */ - -int -next_insns_test_no_inequality (insn) - rtx insn; -{ - register rtx next = NEXT_INSN (insn); - - for (; next != 0; next = NEXT_INSN (next)) - { - if (GET_CODE (next) == CODE_LABEL - || GET_CODE (next) == BARRIER) - return 1; - if (GET_CODE (next) == NOTE) - continue; - if (inequality_comparisons_p (PATTERN (next))) - return 0; - if (sets_cc0_p (PATTERN (next)) == 1) - return 1; - if (! reg_mentioned_p (cc0_rtx, PATTERN (next))) - return 1; - } - return 1; -} -#endif -#endif - -/* This is used by find_single_use to locate an rtx that contains exactly one - use of DEST, which is typically either a REG or CC0. It returns a - pointer to the innermost rtx expression containing DEST. Appearances of - DEST that are being used to totally replace it are not counted. */ - -static rtx * -find_single_use_1 (dest, loc) - rtx dest; - rtx *loc; -{ - rtx x = *loc; - enum rtx_code code = GET_CODE (x); - rtx *result = 0; - rtx *this_result; - int i; - char *fmt; - - switch (code) - { - case CONST_INT: - case CONST: - case LABEL_REF: - case SYMBOL_REF: - case CONST_DOUBLE: - case CLOBBER: - return 0; - - case SET: - /* If the destination is anything other than CC0, PC, a REG or a SUBREG - of a REG that occupies all of the REG, the insn uses DEST if - it is mentioned in the destination or the source. Otherwise, we - need just check the source. */ - if (GET_CODE (SET_DEST (x)) != CC0 - && GET_CODE (SET_DEST (x)) != PC - && GET_CODE (SET_DEST (x)) != REG - && ! (GET_CODE (SET_DEST (x)) == SUBREG - && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG - && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (x)))) - + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD) - == ((GET_MODE_SIZE (GET_MODE (SET_DEST (x))) - + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)))) - break; - - return find_single_use_1 (dest, &SET_SRC (x)); - - case MEM: - case SUBREG: - return find_single_use_1 (dest, &XEXP (x, 0)); - } - - /* If it wasn't one of the common cases above, check each expression and - vector of this code. Look for a unique usage of DEST. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - if (dest == XEXP (x, i) - || (GET_CODE (dest) == REG && GET_CODE (XEXP (x, i)) == REG - && REGNO (dest) == REGNO (XEXP (x, i)))) - this_result = loc; - else - this_result = find_single_use_1 (dest, &XEXP (x, i)); - - if (result == 0) - result = this_result; - else if (this_result) - /* Duplicate usage. */ - return 0; - } - else if (fmt[i] == 'E') - { - int j; - - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - { - if (XVECEXP (x, i, j) == dest - || (GET_CODE (dest) == REG - && GET_CODE (XVECEXP (x, i, j)) == REG - && REGNO (XVECEXP (x, i, j)) == REGNO (dest))) - this_result = loc; - else - this_result = find_single_use_1 (dest, &XVECEXP (x, i, j)); - - if (result == 0) - result = this_result; - else if (this_result) - return 0; - } - } - } - - return result; -} - -/* See if DEST, produced in INSN, is used only a single time in the - sequel. If so, return a pointer to the innermost rtx expression in which - it is used. - - If PLOC is non-zero, *PLOC is set to the insn containing the single use. - - This routine will return usually zero either before flow is called (because - there will be no LOG_LINKS notes) or after reload (because the REG_DEAD - note can't be trusted). - - If DEST is cc0_rtx, we look only at the next insn. In that case, we don't - care about REG_DEAD notes or LOG_LINKS. - - Otherwise, we find the single use by finding an insn that has a - LOG_LINKS pointing at INSN and has a REG_DEAD note for DEST. If DEST is - only referenced once in that insn, we know that it must be the first - and last insn referencing DEST. */ - -rtx * -find_single_use (dest, insn, ploc) - rtx dest; - rtx insn; - rtx *ploc; -{ - rtx next; - rtx *result; - rtx link; - -#ifdef HAVE_cc0 - if (dest == cc0_rtx) - { - next = NEXT_INSN (insn); - if (next == 0 - || (GET_CODE (next) != INSN && GET_CODE (next) != JUMP_INSN)) - return 0; - - result = find_single_use_1 (dest, &PATTERN (next)); - if (result && ploc) - *ploc = next; - return result; - } -#endif - - if (reload_completed || reload_in_progress || GET_CODE (dest) != REG) - return 0; - - for (next = next_nonnote_insn (insn); - next != 0 && GET_CODE (next) != CODE_LABEL; - next = next_nonnote_insn (next)) - if (GET_RTX_CLASS (GET_CODE (next)) == 'i' && dead_or_set_p (next, dest)) - { - for (link = LOG_LINKS (next); link; link = XEXP (link, 1)) - if (XEXP (link, 0) == insn) - break; - - if (link) - { - result = find_single_use_1 (dest, &PATTERN (next)); - if (ploc) - *ploc = next; - return result; - } - } - - return 0; -} - -/* Return 1 if OP is a valid general operand for machine mode MODE. - This is either a register reference, a memory reference, - or a constant. In the case of a memory reference, the address - is checked for general validity for the target machine. - - Register and memory references must have mode MODE in order to be valid, - but some constants have no machine mode and are valid for any mode. - - If MODE is VOIDmode, OP is checked for validity for whatever mode - it has. - - The main use of this function is as a predicate in match_operand - expressions in the machine description. - - For an explanation of this function's behavior for registers of - class NO_REGS, see the comment for `register_operand'. */ - -int -general_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - register enum rtx_code code = GET_CODE (op); - int mode_altering_drug = 0; - - if (mode == VOIDmode) - mode = GET_MODE (op); - - /* Don't accept CONST_INT or anything similar - if the caller wants something floating. */ - if (GET_MODE (op) == VOIDmode && mode != VOIDmode - && GET_MODE_CLASS (mode) != MODE_INT - && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT) - return 0; - - if (CONSTANT_P (op)) - return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode) -#ifdef LEGITIMATE_PIC_OPERAND_P - && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op)) -#endif - && LEGITIMATE_CONSTANT_P (op)); - - /* Except for certain constants with VOIDmode, already checked for, - OP's mode must match MODE if MODE specifies a mode. */ - - if (GET_MODE (op) != mode) - return 0; - - if (code == SUBREG) - { -#ifdef INSN_SCHEDULING - /* On machines that have insn scheduling, we want all memory - reference to be explicit, so outlaw paradoxical SUBREGs. */ - if (GET_CODE (SUBREG_REG (op)) == MEM - && GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op)))) - return 0; -#endif - - op = SUBREG_REG (op); - code = GET_CODE (op); -#if 0 - /* No longer needed, since (SUBREG (MEM...)) - will load the MEM into a reload reg in the MEM's own mode. */ - mode_altering_drug = 1; -#endif - } - - if (code == REG) - /* A register whose class is NO_REGS is not a general operand. */ - return (REGNO (op) >= FIRST_PSEUDO_REGISTER - || REGNO_REG_CLASS (REGNO (op)) != NO_REGS); - - if (code == MEM) - { - register rtx y = XEXP (op, 0); - if (! volatile_ok && MEM_VOLATILE_P (op)) - return 0; - /* Use the mem's mode, since it will be reloaded thus. */ - mode = GET_MODE (op); - GO_IF_LEGITIMATE_ADDRESS (mode, y, win); - } - return 0; - - win: - if (mode_altering_drug) - return ! mode_dependent_address_p (XEXP (op, 0)); - return 1; -} - -/* Return 1 if OP is a valid memory address for a memory reference - of mode MODE. - - The main use of this function is as a predicate in match_operand - expressions in the machine description. */ - -int -address_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return memory_address_p (mode, op); -} - -/* Return 1 if OP is a register reference of mode MODE. - If MODE is VOIDmode, accept a register in any mode. - - The main use of this function is as a predicate in match_operand - expressions in the machine description. - - As a special exception, registers whose class is NO_REGS are - not accepted by `register_operand'. The reason for this change - is to allow the representation of special architecture artifacts - (such as a condition code register) without extending the rtl - definitions. Since registers of class NO_REGS cannot be used - as registers in any case where register classes are examined, - it is most consistent to keep this function from accepting them. */ - -int -register_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - if (GET_MODE (op) != mode && mode != VOIDmode) - return 0; - - if (GET_CODE (op) == SUBREG) - { - /* Before reload, we can allow (SUBREG (MEM...)) as a register operand - because it is guaranteed to be reloaded into one. - Just make sure the MEM is valid in itself. - (Ideally, (SUBREG (MEM)...) should not exist after reload, - but currently it does result from (SUBREG (REG)...) where the - reg went on the stack.) */ - if (! reload_completed && GET_CODE (SUBREG_REG (op)) == MEM) - return general_operand (op, mode); - op = SUBREG_REG (op); - } - - /* We don't consider registers whose class is NO_REGS - to be a register operand. */ - return (GET_CODE (op) == REG - && (REGNO (op) >= FIRST_PSEUDO_REGISTER - || REGNO_REG_CLASS (REGNO (op)) != NO_REGS)); -} - -/* Return 1 if OP should match a MATCH_SCRATCH, i.e., if it is a SCRATCH - or a hard register. */ - -int -scratch_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return (GET_MODE (op) == mode - && (GET_CODE (op) == SCRATCH - || (GET_CODE (op) == REG - && REGNO (op) < FIRST_PSEUDO_REGISTER))); -} - -/* Return 1 if OP is a valid immediate operand for mode MODE. - - The main use of this function is as a predicate in match_operand - expressions in the machine description. */ - -int -immediate_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - /* Don't accept CONST_INT or anything similar - if the caller wants something floating. */ - if (GET_MODE (op) == VOIDmode && mode != VOIDmode - && GET_MODE_CLASS (mode) != MODE_INT - && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT) - return 0; - - return (CONSTANT_P (op) - && (GET_MODE (op) == mode || mode == VOIDmode - || GET_MODE (op) == VOIDmode) -#ifdef LEGITIMATE_PIC_OPERAND_P - && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op)) -#endif - && LEGITIMATE_CONSTANT_P (op)); -} - -/* Returns 1 if OP is an operand that is a CONST_INT. */ - -int -const_int_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return GET_CODE (op) == CONST_INT; -} - -/* Returns 1 if OP is an operand that is a constant integer or constant - floating-point number. */ - -int -const_double_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - /* Don't accept CONST_INT or anything similar - if the caller wants something floating. */ - if (GET_MODE (op) == VOIDmode && mode != VOIDmode - && GET_MODE_CLASS (mode) != MODE_INT - && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT) - return 0; - - return ((GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT) - && (mode == VOIDmode || GET_MODE (op) == mode - || GET_MODE (op) == VOIDmode)); -} - -/* Return 1 if OP is a general operand that is not an immediate operand. */ - -int -nonimmediate_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return (general_operand (op, mode) && ! CONSTANT_P (op)); -} - -/* Return 1 if OP is a register reference or immediate value of mode MODE. */ - -int -nonmemory_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - if (CONSTANT_P (op)) - { - /* Don't accept CONST_INT or anything similar - if the caller wants something floating. */ - if (GET_MODE (op) == VOIDmode && mode != VOIDmode - && GET_MODE_CLASS (mode) != MODE_INT - && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT) - return 0; - - return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode) -#ifdef LEGITIMATE_PIC_OPERAND_P - && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op)) -#endif - && LEGITIMATE_CONSTANT_P (op)); - } - - if (GET_MODE (op) != mode && mode != VOIDmode) - return 0; - - if (GET_CODE (op) == SUBREG) - { - /* Before reload, we can allow (SUBREG (MEM...)) as a register operand - because it is guaranteed to be reloaded into one. - Just make sure the MEM is valid in itself. - (Ideally, (SUBREG (MEM)...) should not exist after reload, - but currently it does result from (SUBREG (REG)...) where the - reg went on the stack.) */ - if (! reload_completed && GET_CODE (SUBREG_REG (op)) == MEM) - return general_operand (op, mode); - op = SUBREG_REG (op); - } - - /* We don't consider registers whose class is NO_REGS - to be a register operand. */ - return (GET_CODE (op) == REG - && (REGNO (op) >= FIRST_PSEUDO_REGISTER - || REGNO_REG_CLASS (REGNO (op)) != NO_REGS)); -} - -/* Return 1 if OP is a valid operand that stands for pushing a - value of mode MODE onto the stack. - - The main use of this function is as a predicate in match_operand - expressions in the machine description. */ - -int -push_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) != MEM) - return 0; - - if (GET_MODE (op) != mode) - return 0; - - op = XEXP (op, 0); - - if (GET_CODE (op) != STACK_PUSH_CODE) - return 0; - - return XEXP (op, 0) == stack_pointer_rtx; -} - -/* Return 1 if ADDR is a valid memory address for mode MODE. */ - -int -memory_address_p (mode, addr) - enum machine_mode mode; - register rtx addr; -{ - GO_IF_LEGITIMATE_ADDRESS (mode, addr, win); - return 0; - - win: - return 1; -} - -/* Return 1 if OP is a valid memory reference with mode MODE, - including a valid address. - - The main use of this function is as a predicate in match_operand - expressions in the machine description. */ - -int -memory_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - rtx inner; - - if (! reload_completed) - /* Note that no SUBREG is a memory operand before end of reload pass, - because (SUBREG (MEM...)) forces reloading into a register. */ - return GET_CODE (op) == MEM && general_operand (op, mode); - - if (mode != VOIDmode && GET_MODE (op) != mode) - return 0; - - inner = op; - if (GET_CODE (inner) == SUBREG) - inner = SUBREG_REG (inner); - - return (GET_CODE (inner) == MEM && general_operand (op, mode)); -} - -/* Return 1 if OP is a valid indirect memory reference with mode MODE; - that is, a memory reference whose address is a general_operand. */ - -int -indirect_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - /* Before reload, a SUBREG isn't in memory (see memory_operand, above). */ - if (! reload_completed - && GET_CODE (op) == SUBREG && GET_CODE (SUBREG_REG (op)) == MEM) - { - register int offset = SUBREG_WORD (op) * UNITS_PER_WORD; - rtx inner = SUBREG_REG (op); - -#if BYTES_BIG_ENDIAN - offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (op))) - - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (inner)))); -#endif - - /* The only way that we can have a general_operand as the resulting - address is if OFFSET is zero and the address already is an operand - or if the address is (plus Y (const_int -OFFSET)) and Y is an - operand. */ - - return ((offset == 0 && general_operand (XEXP (inner, 0), Pmode)) - || (GET_CODE (XEXP (inner, 0)) == PLUS - && GET_CODE (XEXP (XEXP (inner, 0), 1)) == CONST_INT - && INTVAL (XEXP (XEXP (inner, 0), 1)) == -offset - && general_operand (XEXP (XEXP (inner, 0), 0), Pmode))); - } - - return (GET_CODE (op) == MEM - && memory_operand (op, mode) - && general_operand (XEXP (op, 0), Pmode)); -} - -/* Return 1 if this is a comparison operator. This allows the use of - MATCH_OPERATOR to recognize all the branch insns. */ - -int -comparison_operator (op, mode) - register rtx op; - enum machine_mode mode; -{ - return ((mode == VOIDmode || GET_MODE (op) == mode) - && GET_RTX_CLASS (GET_CODE (op)) == '<'); -} - -/* If BODY is an insn body that uses ASM_OPERANDS, - return the number of operands (both input and output) in the insn. - Otherwise return -1. */ - -int -asm_noperands (body) - rtx body; -{ - if (GET_CODE (body) == ASM_OPERANDS) - /* No output operands: return number of input operands. */ - return ASM_OPERANDS_INPUT_LENGTH (body); - if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS) - /* Single output operand: BODY is (set OUTPUT (asm_operands ...)). */ - return ASM_OPERANDS_INPUT_LENGTH (SET_SRC (body)) + 1; - else if (GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == SET - && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) == ASM_OPERANDS) - { - /* Multiple output operands, or 1 output plus some clobbers: - body is [(set OUTPUT (asm_operands ...))... (clobber (reg ...))...]. */ - int i; - int n_sets; - - /* Count backwards through CLOBBERs to determine number of SETs. */ - for (i = XVECLEN (body, 0); i > 0; i--) - { - if (GET_CODE (XVECEXP (body, 0, i - 1)) == SET) - break; - if (GET_CODE (XVECEXP (body, 0, i - 1)) != CLOBBER) - return -1; - } - - /* N_SETS is now number of output operands. */ - n_sets = i; - - /* Verify that all the SETs we have - came from a single original asm_operands insn - (so that invalid combinations are blocked). */ - for (i = 0; i < n_sets; i++) - { - rtx elt = XVECEXP (body, 0, i); - if (GET_CODE (elt) != SET) - return -1; - if (GET_CODE (SET_SRC (elt)) != ASM_OPERANDS) - return -1; - /* If these ASM_OPERANDS rtx's came from different original insns - then they aren't allowed together. */ - if (ASM_OPERANDS_INPUT_VEC (SET_SRC (elt)) - != ASM_OPERANDS_INPUT_VEC (SET_SRC (XVECEXP (body, 0, 0)))) - return -1; - } - return (ASM_OPERANDS_INPUT_LENGTH (SET_SRC (XVECEXP (body, 0, 0))) - + n_sets); - } - else if (GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS) - { - /* 0 outputs, but some clobbers: - body is [(asm_operands ...) (clobber (reg ...))...]. */ - int i; - - /* Make sure all the other parallel things really are clobbers. */ - for (i = XVECLEN (body, 0) - 1; i > 0; i--) - if (GET_CODE (XVECEXP (body, 0, i)) != CLOBBER) - return -1; - - return ASM_OPERANDS_INPUT_LENGTH (XVECEXP (body, 0, 0)); - } - else - return -1; -} - -/* Assuming BODY is an insn body that uses ASM_OPERANDS, - copy its operands (both input and output) into the vector OPERANDS, - the locations of the operands within the insn into the vector OPERAND_LOCS, - and the constraints for the operands into CONSTRAINTS. - Write the modes of the operands into MODES. - Return the assembler-template. - - If MODES, OPERAND_LOCS, CONSTRAINTS or OPERANDS is 0, - we don't store that info. */ - -char * -decode_asm_operands (body, operands, operand_locs, constraints, modes) - rtx body; - rtx *operands; - rtx **operand_locs; - char **constraints; - enum machine_mode *modes; -{ - register int i; - int noperands; - char *template = 0; - - if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS) - { - rtx asmop = SET_SRC (body); - /* Single output operand: BODY is (set OUTPUT (asm_operands ....)). */ - - noperands = ASM_OPERANDS_INPUT_LENGTH (asmop) + 1; - - for (i = 1; i < noperands; i++) - { - if (operand_locs) - operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i - 1); - if (operands) - operands[i] = ASM_OPERANDS_INPUT (asmop, i - 1); - if (constraints) - constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i - 1); - if (modes) - modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i - 1); - } - - /* The output is in the SET. - Its constraint is in the ASM_OPERANDS itself. */ - if (operands) - operands[0] = SET_DEST (body); - if (operand_locs) - operand_locs[0] = &SET_DEST (body); - if (constraints) - constraints[0] = ASM_OPERANDS_OUTPUT_CONSTRAINT (asmop); - if (modes) - modes[0] = GET_MODE (SET_DEST (body)); - template = ASM_OPERANDS_TEMPLATE (asmop); - } - else if (GET_CODE (body) == ASM_OPERANDS) - { - rtx asmop = body; - /* No output operands: BODY is (asm_operands ....). */ - - noperands = ASM_OPERANDS_INPUT_LENGTH (asmop); - - /* The input operands are found in the 1st element vector. */ - /* Constraints for inputs are in the 2nd element vector. */ - for (i = 0; i < noperands; i++) - { - if (operand_locs) - operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i); - if (operands) - operands[i] = ASM_OPERANDS_INPUT (asmop, i); - if (constraints) - constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i); - if (modes) - modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i); - } - template = ASM_OPERANDS_TEMPLATE (asmop); - } - else if (GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == SET) - { - rtx asmop = SET_SRC (XVECEXP (body, 0, 0)); - int nparallel = XVECLEN (body, 0); /* Includes CLOBBERs. */ - int nin = ASM_OPERANDS_INPUT_LENGTH (asmop); - int nout = 0; /* Does not include CLOBBERs. */ - - /* At least one output, plus some CLOBBERs. */ - - /* The outputs are in the SETs. - Their constraints are in the ASM_OPERANDS itself. */ - for (i = 0; i < nparallel; i++) - { - if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER) - break; /* Past last SET */ - - if (operands) - operands[i] = SET_DEST (XVECEXP (body, 0, i)); - if (operand_locs) - operand_locs[i] = &SET_DEST (XVECEXP (body, 0, i)); - if (constraints) - constraints[i] = XSTR (SET_SRC (XVECEXP (body, 0, i)), 1); - if (modes) - modes[i] = GET_MODE (SET_DEST (XVECEXP (body, 0, i))); - nout++; - } - - for (i = 0; i < nin; i++) - { - if (operand_locs) - operand_locs[i + nout] = &ASM_OPERANDS_INPUT (asmop, i); - if (operands) - operands[i + nout] = ASM_OPERANDS_INPUT (asmop, i); - if (constraints) - constraints[i + nout] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i); - if (modes) - modes[i + nout] = ASM_OPERANDS_INPUT_MODE (asmop, i); - } - - template = ASM_OPERANDS_TEMPLATE (asmop); - } - else if (GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS) - { - /* No outputs, but some CLOBBERs. */ - - rtx asmop = XVECEXP (body, 0, 0); - int nin = ASM_OPERANDS_INPUT_LENGTH (asmop); - - for (i = 0; i < nin; i++) - { - if (operand_locs) - operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i); - if (operands) - operands[i] = ASM_OPERANDS_INPUT (asmop, i); - if (constraints) - constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i); - if (modes) - modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i); - } - - template = ASM_OPERANDS_TEMPLATE (asmop); - } - - return template; -} - -/* Given an rtx *P, if it is a sum containing an integer constant term, - return the location (type rtx *) of the pointer to that constant term. - Otherwise, return a null pointer. */ - -static rtx * -find_constant_term_loc (p) - rtx *p; -{ - register rtx *tem; - register enum rtx_code code = GET_CODE (*p); - - /* If *P IS such a constant term, P is its location. */ - - if (code == CONST_INT || code == SYMBOL_REF || code == LABEL_REF - || code == CONST) - return p; - - /* Otherwise, if not a sum, it has no constant term. */ - - if (GET_CODE (*p) != PLUS) - return 0; - - /* If one of the summands is constant, return its location. */ - - if (XEXP (*p, 0) && CONSTANT_P (XEXP (*p, 0)) - && XEXP (*p, 1) && CONSTANT_P (XEXP (*p, 1))) - return p; - - /* Otherwise, check each summand for containing a constant term. */ - - if (XEXP (*p, 0) != 0) - { - tem = find_constant_term_loc (&XEXP (*p, 0)); - if (tem != 0) - return tem; - } - - if (XEXP (*p, 1) != 0) - { - tem = find_constant_term_loc (&XEXP (*p, 1)); - if (tem != 0) - return tem; - } - - return 0; -} - -/* Return 1 if OP is a memory reference - whose address contains no side effects - and remains valid after the addition - of a positive integer less than the - size of the object being referenced. - - We assume that the original address is valid and do not check it. - - This uses strict_memory_address_p as a subroutine, so - don't use it before reload. */ - -int -offsettable_memref_p (op) - rtx op; -{ - return ((GET_CODE (op) == MEM) - && offsettable_address_p (1, GET_MODE (op), XEXP (op, 0))); -} - -/* Similar, but don't require a strictly valid mem ref: - consider pseudo-regs valid as index or base regs. */ - -int -offsettable_nonstrict_memref_p (op) - rtx op; -{ - return ((GET_CODE (op) == MEM) - && offsettable_address_p (0, GET_MODE (op), XEXP (op, 0))); -} - -/* Return 1 if Y is a memory address which contains no side effects - and would remain valid after the addition of a positive integer - less than the size of that mode. - - We assume that the original address is valid and do not check it. - We do check that it is valid for narrower modes. - - If STRICTP is nonzero, we require a strictly valid address, - for the sake of use in reload.c. */ - -int -offsettable_address_p (strictp, mode, y) - int strictp; - enum machine_mode mode; - register rtx y; -{ - register enum rtx_code ycode = GET_CODE (y); - register rtx z; - rtx y1 = y; - rtx *y2; - int (*addressp) () = (strictp ? strict_memory_address_p : memory_address_p); - - if (CONSTANT_ADDRESS_P (y)) - return 1; - - /* Adjusting an offsettable address involves changing to a narrower mode. - Make sure that's OK. */ - - if (mode_dependent_address_p (y)) - return 0; - - /* If the expression contains a constant term, - see if it remains valid when max possible offset is added. */ - - if ((ycode == PLUS) && (y2 = find_constant_term_loc (&y1))) - { - int good; - - y1 = *y2; - *y2 = plus_constant (*y2, GET_MODE_SIZE (mode) - 1); - /* Use QImode because an odd displacement may be automatically invalid - for any wider mode. But it should be valid for a single byte. */ - good = (*addressp) (QImode, y); - - /* In any case, restore old contents of memory. */ - *y2 = y1; - return good; - } - - if (ycode == PRE_DEC || ycode == PRE_INC - || ycode == POST_DEC || ycode == POST_INC) - return 0; - - /* The offset added here is chosen as the maximum offset that - any instruction could need to add when operating on something - of the specified mode. We assume that if Y and Y+c are - valid addresses then so is Y+d for all 0 0) - { - funny_match[funny_match_index].this = opno; - funny_match[funny_match_index++].other = c - '0'; - } - break; - - case 'p': - /* p is used for address_operands. When we are called by - gen_input_reload, no one will have checked that the - address is strictly valid, i.e., that all pseudos - requiring hard regs have gotten them. */ - if (strict <= 0 - || (strict_memory_address_p - (insn_operand_mode[insn_code_num][opno], op))) - win = 1; - break; - - /* No need to check general_operand again; - it was done in insn-recog.c. */ - case 'g': - /* Anything goes unless it is a REG and really has a hard reg - but the hard reg is not in the class GENERAL_REGS. */ - if (strict < 0 - || GENERAL_REGS == ALL_REGS - || GET_CODE (op) != REG - || (reload_in_progress - && REGNO (op) >= FIRST_PSEUDO_REGISTER) - || reg_fits_class_p (op, GENERAL_REGS, offset, mode)) - win = 1; - break; - - case 'r': - if (strict < 0 - || (strict == 0 - && GET_CODE (op) == REG - && REGNO (op) >= FIRST_PSEUDO_REGISTER) - || (strict == 0 && GET_CODE (op) == SCRATCH) - || (GET_CODE (op) == REG - && ((GENERAL_REGS == ALL_REGS - && REGNO (op) < FIRST_PSEUDO_REGISTER) - || reg_fits_class_p (op, GENERAL_REGS, - offset, mode)))) - win = 1; - break; - - case 'X': - /* This is used for a MATCH_SCRATCH in the cases when we - don't actually need anything. So anything goes any time. */ - win = 1; - break; - - case 'm': - if (GET_CODE (op) == MEM - /* Before reload, accept what reload can turn into mem. */ - || (strict < 0 && CONSTANT_P (op)) - /* During reload, accept a pseudo */ - || (reload_in_progress && GET_CODE (op) == REG - && REGNO (op) >= FIRST_PSEUDO_REGISTER)) - win = 1; - break; - - case '<': - if (GET_CODE (op) == MEM - && (GET_CODE (XEXP (op, 0)) == PRE_DEC - || GET_CODE (XEXP (op, 0)) == POST_DEC)) - win = 1; - break; - - case '>': - if (GET_CODE (op) == MEM - && (GET_CODE (XEXP (op, 0)) == PRE_INC - || GET_CODE (XEXP (op, 0)) == POST_INC)) - win = 1; - break; - - case 'E': - /* Match any CONST_DOUBLE, but only if - we can examine the bits of it reliably. */ - if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT - || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD) - && GET_MODE (op) != VOIDmode && ! flag_pretend_float) - break; - if (GET_CODE (op) == CONST_DOUBLE) - win = 1; - break; - - case 'F': - if (GET_CODE (op) == CONST_DOUBLE) - win = 1; - break; - - case 'G': - case 'H': - if (GET_CODE (op) == CONST_DOUBLE - && CONST_DOUBLE_OK_FOR_LETTER_P (op, c)) - win = 1; - break; - - case 's': - if (GET_CODE (op) == CONST_INT - || (GET_CODE (op) == CONST_DOUBLE - && GET_MODE (op) == VOIDmode)) - break; - case 'i': - if (CONSTANT_P (op)) - win = 1; - break; - - case 'n': - if (GET_CODE (op) == CONST_INT - || (GET_CODE (op) == CONST_DOUBLE - && GET_MODE (op) == VOIDmode)) - win = 1; - break; - - case 'I': - case 'J': - case 'K': - case 'L': - case 'M': - case 'N': - case 'O': - case 'P': - if (GET_CODE (op) == CONST_INT - && CONST_OK_FOR_LETTER_P (INTVAL (op), c)) - win = 1; - break; - -#ifdef EXTRA_CONSTRAINT - case 'Q': - case 'R': - case 'S': - case 'T': - case 'U': - if (EXTRA_CONSTRAINT (op, c)) - win = 1; - break; -#endif - - case 'V': - if (GET_CODE (op) == MEM - && ! offsettable_memref_p (op)) - win = 1; - break; - - case 'o': - if ((strict > 0 && offsettable_memref_p (op)) - || (strict == 0 && offsettable_nonstrict_memref_p (op)) - /* Before reload, accept what reload can handle. */ - || (strict < 0 - && (CONSTANT_P (op) || GET_CODE (op) == MEM)) - /* During reload, accept a pseudo */ - || (reload_in_progress && GET_CODE (op) == REG - && REGNO (op) >= FIRST_PSEUDO_REGISTER)) - win = 1; - break; - - default: - if (strict < 0 - || (strict == 0 - && GET_CODE (op) == REG - && REGNO (op) >= FIRST_PSEUDO_REGISTER) - || (strict == 0 && GET_CODE (op) == SCRATCH) - || (GET_CODE (op) == REG - && reg_fits_class_p (op, REG_CLASS_FROM_LETTER (c), - offset, mode))) - win = 1; - } - - constraints[opno] = p; - /* If this operand did not win somehow, - this alternative loses. */ - if (! win) - lose = 1; - } - /* This alternative won; the operands are ok. - Change whichever operands this alternative says to change. */ - if (! lose) - { - int opno, eopno; - - /* See if any earlyclobber operand conflicts with some other - operand. */ - - if (strict > 0) - for (eopno = 0; eopno < noperands; eopno++) - /* Ignore earlyclobber operands now in memory, - because we would often report failure when we have - two memory operands, one of which was formerly a REG. */ - if (earlyclobber[eopno] - && GET_CODE (recog_operand[eopno]) == REG) - for (opno = 0; opno < noperands; opno++) - if ((GET_CODE (recog_operand[opno]) == MEM - || op_types[opno] != OP_OUT) - && opno != eopno - && constraints[opno] != 0 - && ! (matching_operands[opno] == eopno - && rtx_equal_p (recog_operand[opno], - recog_operand[eopno])) - && ! safe_from_earlyclobber (recog_operand[opno], - recog_operand[eopno])) - lose = 1; - - if (! lose) - { - while (--funny_match_index >= 0) - { - recog_operand[funny_match[funny_match_index].other] - = recog_operand[funny_match[funny_match_index].this]; - } - - return 1; - } - } - - which_alternative++; - } - - /* If we are about to reject this, but we are not to test strictly, - try a very loose test. Only return failure if it fails also. */ - if (strict == 0) - return constrain_operands (insn_code_num, -1); - else - return 0; -} - -/* Return 1 iff OPERAND (assumed to be a REG rtx) - is a hard reg in class CLASS when its regno is offsetted by OFFSET - and changed to mode MODE. - If REG occupies multiple hard regs, all of them must be in CLASS. */ - -int -reg_fits_class_p (operand, class, offset, mode) - rtx operand; - register enum reg_class class; - int offset; - enum machine_mode mode; -{ - register int regno = REGNO (operand); - if (regno < FIRST_PSEUDO_REGISTER - && TEST_HARD_REG_BIT (reg_class_contents[(int) class], - regno + offset)) - { - register int sr; - regno += offset; - for (sr = HARD_REGNO_NREGS (regno, mode) - 1; - sr > 0; sr--) - if (! TEST_HARD_REG_BIT (reg_class_contents[(int) class], - regno + sr)) - break; - return sr == 0; - } - - return 0; -} - -#endif /* REGISTER_CONSTRAINTS */ diff --git a/gnu/usr.bin/cc/common/reg-stack.c b/gnu/usr.bin/cc/common/reg-stack.c deleted file mode 100644 index c3d792e4bd..0000000000 --- a/gnu/usr.bin/cc/common/reg-stack.c +++ /dev/null @@ -1,2873 +0,0 @@ -/* Register to Stack convert for GNU compiler. - Copyright (C) 1992, 1993 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* This pass converts stack-like registers from the "flat register - file" model that gcc uses, to a stack convention that the 387 uses. - - * The form of the input: - - On input, the function consists of insn that have had their - registers fully allocated to a set of "virtual" registers. Note that - the word "virtual" is used differently here than elsewhere in gcc: for - each virtual stack reg, there is a hard reg, but the mapping between - them is not known until this pass is run. On output, hard register - numbers have been substituted, and various pop and exchange insns have - been emitted. The hard register numbers and the virtual register - numbers completely overlap - before this pass, all stack register - numbers are virtual, and afterward they are all hard. - - The virtual registers can be manipulated normally by gcc, and their - semantics are the same as for normal registers. After the hard - register numbers are substituted, the semantics of an insn containing - stack-like regs are not the same as for an insn with normal regs: for - instance, it is not safe to delete an insn that appears to be a no-op - move. In general, no insn containing hard regs should be changed - after this pass is done. - - * The form of the output: - - After this pass, hard register numbers represent the distance from - the current top of stack to the desired register. A reference to - FIRST_STACK_REG references the top of stack, FIRST_STACK_REG + 1, - represents the register just below that, and so forth. Also, REG_DEAD - notes indicate whether or not a stack register should be popped. - - A "swap" insn looks like a parallel of two patterns, where each - pattern is a SET: one sets A to B, the other B to A. - - A "push" or "load" insn is a SET whose SET_DEST is FIRST_STACK_REG - and whose SET_DEST is REG or MEM. Any other SET_DEST, such as PLUS, - will replace the existing stack top, not push a new value. - - A store insn is a SET whose SET_DEST is FIRST_STACK_REG, and whose - SET_SRC is REG or MEM. - - The case where the SET_SRC and SET_DEST are both FIRST_STACK_REG - appears ambiguous. As a special case, the presence of a REG_DEAD note - for FIRST_STACK_REG differentiates between a load insn and a pop. - - If a REG_DEAD is present, the insn represents a "pop" that discards - the top of the register stack. If there is no REG_DEAD note, then the - insn represents a "dup" or a push of the current top of stack onto the - stack. - - * Methodology: - - Existing REG_DEAD and REG_UNUSED notes for stack registers are - deleted and recreated from scratch. REG_DEAD is never created for a - SET_DEST, only REG_UNUSED. - - Before life analysis, the mode of each insn is set based on whether - or not any stack registers are mentioned within that insn. VOIDmode - means that no regs are mentioned anyway, and QImode means that at - least one pattern within the insn mentions stack registers. This - information is valid until after reg_to_stack returns, and is used - from jump_optimize. - - * asm_operands: - - There are several rules on the usage of stack-like regs in - asm_operands insns. These rules apply only to the operands that are - stack-like regs: - - 1. Given a set of input regs that die in an asm_operands, it is - necessary to know which are implicitly popped by the asm, and - which must be explicitly popped by gcc. - - An input reg that is implicitly popped by the asm must be - explicitly clobbered, unless it is constrained to match an - output operand. - - 2. For any input reg that is implicitly popped by an asm, it is - necessary to know how to adjust the stack to compensate for the pop. - If any non-popped input is closer to the top of the reg-stack than - the implicitly popped reg, it would not be possible to know what the - stack looked like - it's not clear how the rest of the stack "slides - up". - - All implicitly popped input regs must be closer to the top of - the reg-stack than any input that is not implicitly popped. - - 3. It is possible that if an input dies in an insn, reload might - use the input reg for an output reload. Consider this example: - - asm ("foo" : "=t" (a) : "f" (b)); - - This asm says that input B is not popped by the asm, and that - the asm pushes a result onto the reg-stack, ie, the stack is one - deeper after the asm than it was before. But, it is possible that - reload will think that it can use the same reg for both the input and - the output, if input B dies in this insn. - - If any input operand uses the "f" constraint, all output reg - constraints must use the "&" earlyclobber. - - The asm above would be written as - - asm ("foo" : "=&t" (a) : "f" (b)); - - 4. Some operands need to be in particular places on the stack. All - output operands fall in this category - there is no other way to - know which regs the outputs appear in unless the user indicates - this in the constraints. - - Output operands must specifically indicate which reg an output - appears in after an asm. "=f" is not allowed: the operand - constraints must select a class with a single reg. - - 5. Output operands may not be "inserted" between existing stack regs. - Since no 387 opcode uses a read/write operand, all output operands - are dead before the asm_operands, and are pushed by the asm_operands. - It makes no sense to push anywhere but the top of the reg-stack. - - Output operands must start at the top of the reg-stack: output - operands may not "skip" a reg. - - 6. Some asm statements may need extra stack space for internal - calculations. This can be guaranteed by clobbering stack registers - unrelated to the inputs and outputs. - - Here are a couple of reasonable asms to want to write. This asm - takes one input, which is internally popped, and produces two outputs. - - asm ("fsincos" : "=t" (cos), "=u" (sin) : "0" (inp)); - - This asm takes two inputs, which are popped by the fyl2xp1 opcode, - and replaces them with one output. The user must code the "st(1)" - clobber for reg-stack.c to know that fyl2xp1 pops both inputs. - - asm ("fyl2xp1" : "=t" (result) : "0" (x), "u" (y) : "st(1)"); - - */ - -#include -#include "config.h" -#include "tree.h" -#include "rtl.h" -#include "insn-config.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "flags.h" - -#ifdef STACK_REGS - -#define REG_STACK_SIZE (LAST_STACK_REG - FIRST_STACK_REG + 1) - -/* True if the current function returns a real value. */ -static int current_function_returns_real; - -/* This is the basic stack record. TOP is an index into REG[] such - that REG[TOP] is the top of stack. If TOP is -1 the stack is empty. - - If TOP is -2, REG[] is not yet initialized. Stack initialization - consists of placing each live reg in array `reg' and setting `top' - appropriately. - - REG_SET indicates which registers are live. */ - -typedef struct stack_def -{ - int top; /* index to top stack element */ - HARD_REG_SET reg_set; /* set of live registers */ - char reg[REG_STACK_SIZE]; /* register - stack mapping */ -} *stack; - -/* highest instruction uid */ -static int max_uid = 0; - -/* Number of basic blocks in the current function. */ -static int blocks; - -/* Element N is first insn in basic block N. - This info lasts until we finish compiling the function. */ -static rtx *block_begin; - -/* Element N is last insn in basic block N. - This info lasts until we finish compiling the function. */ -static rtx *block_end; - -/* Element N is nonzero if control can drop into basic block N */ -static char *block_drops_in; - -/* Element N says all about the stack at entry block N */ -static stack block_stack_in; - -/* Element N says all about the stack life at the end of block N */ -static HARD_REG_SET *block_out_reg_set; - -/* This is where the BLOCK_NUM values are really stored. This is set - up by find_blocks and used there and in life_analysis. It can be used - later, but only to look up an insn that is the head or tail of some - block. life_analysis and the stack register conversion process can - add insns within a block. */ -static int *block_number; - -/* This is the register file for all register after conversion */ -static rtx FP_mode_reg[FIRST_PSEUDO_REGISTER][(int) MAX_MACHINE_MODE]; - -/* Get the basic block number of an insn. See note at block_number - definition are validity of this information. */ - -#define BLOCK_NUM(INSN) \ - (((INSN_UID (INSN) > max_uid) \ - ? (int *)(abort() , 0) \ - : block_number)[INSN_UID (INSN)]) - -extern rtx gen_jump (); -extern rtx gen_movdf (); -extern rtx find_regno_note (); -extern rtx emit_jump_insn_before (); -extern rtx emit_label_after (); - -/* Forward declarations */ - -static void find_blocks (); -static void stack_reg_life_analysis (); -static void change_stack (); -static void convert_regs (); -static void dump_stack_info (); - -/* Return non-zero if any stack register is mentioned somewhere within PAT. */ - -int -stack_regs_mentioned_p (pat) - rtx pat; -{ - register char *fmt; - register int i; - - if (STACK_REG_P (pat)) - return 1; - - fmt = GET_RTX_FORMAT (GET_CODE (pat)); - for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--) - { - if (fmt[i] == 'E') - { - register int j; - - for (j = XVECLEN (pat, i) - 1; j >= 0; j--) - if (stack_regs_mentioned_p (XVECEXP (pat, i, j))) - return 1; - } - else if (fmt[i] == 'e' && stack_regs_mentioned_p (XEXP (pat, i))) - return 1; - } - - return 0; -} - -/* Convert register usage from "flat" register file usage to a "stack - register file. FIRST is the first insn in the function, FILE is the - dump file, if used. - - First compute the beginning and end of each basic block. Do a - register life analysis on the stack registers, recording the result - for the head and tail of each basic block. The convert each insn one - by one. Run a last jump_optimize() pass, if optimizing, to eliminate - any cross-jumping created when the converter inserts pop insns.*/ - -void -reg_to_stack (first, file) - rtx first; - FILE *file; -{ - register rtx insn; - register int i; - int stack_reg_seen = 0; - enum machine_mode mode; - - current_function_returns_real - = TREE_CODE (TREE_TYPE (DECL_RESULT (current_function_decl))) == REAL_TYPE; - - for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); mode != VOIDmode; - mode = GET_MODE_WIDER_MODE (mode)) - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - FP_mode_reg[i][(int) mode] = gen_rtx (REG, mode, i); - - /* Count the basic blocks. Also find maximum insn uid. */ - { - register RTX_CODE prev_code = JUMP_INSN; - register RTX_CODE code; - - max_uid = 0; - blocks = 0; - for (insn = first; insn; insn = NEXT_INSN (insn)) - { - /* Note that this loop must select the same block boundaries - as code in find_blocks. */ - - if (INSN_UID (insn) > max_uid) - max_uid = INSN_UID (insn); - - code = GET_CODE (insn); - - if (code == CODE_LABEL - || (prev_code != INSN - && prev_code != CALL_INSN - && prev_code != CODE_LABEL - && (code == INSN || code == CALL_INSN || code == JUMP_INSN))) - blocks++; - - /* Remember whether or not this insn mentions an FP regs. - Check JUMP_INSNs too, in case someone creates a funny PARALLEL. */ - - if ((GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN - || GET_CODE (insn) == JUMP_INSN) - && stack_regs_mentioned_p (PATTERN (insn))) - { - stack_reg_seen = 1; - PUT_MODE (insn, QImode); - } - else - PUT_MODE (insn, VOIDmode); - - if (code != NOTE) - prev_code = code; - } - } - - /* If no stack register reference exists in this insn, there isn't - anything to convert. */ - - if (! stack_reg_seen) - return; - - /* If there are stack registers, there must be at least one block. */ - - if (! blocks) - abort (); - - /* Allocate some tables that last till end of compiling this function - and some needed only in find_blocks and life_analysis. */ - - block_begin = (rtx *) alloca (blocks * sizeof (rtx)); - block_end = (rtx *) alloca (blocks * sizeof (rtx)); - block_drops_in = (char *) alloca (blocks); - - block_stack_in = (stack) alloca (blocks * sizeof (struct stack_def)); - block_out_reg_set = (HARD_REG_SET *) alloca (blocks * sizeof (HARD_REG_SET)); - bzero (block_stack_in, blocks * sizeof (struct stack_def)); - bzero (block_out_reg_set, blocks * sizeof (HARD_REG_SET)); - - block_number = (int *) alloca ((max_uid + 1) * sizeof (int)); - - find_blocks (first); - stack_reg_life_analysis (first); - - /* Dump the life analysis debug information before jump - optimization, as that will destroy the LABEL_REFS we keep the - information in. */ - - if (file) - dump_stack_info (file); - - convert_regs (); - - if (optimize) - jump_optimize (first, 2, 0, 0); -} - -/* Check PAT, which is in INSN, for LABEL_REFs. Add INSN to the - label's chain of references, and note which insn contains each - reference. */ - -static void -record_label_references (insn, pat) - rtx insn, pat; -{ - register enum rtx_code code = GET_CODE (pat); - register int i; - register char *fmt; - - if (code == LABEL_REF) - { - register rtx label = XEXP (pat, 0); - register rtx ref; - - if (GET_CODE (label) != CODE_LABEL) - abort (); - - /* Don't make a duplicate in the code_label's chain. */ - - for (ref = LABEL_REFS (label); ref != label; ref = LABEL_NEXTREF (ref)) - if (CONTAINING_INSN (ref) == insn) - return; - - CONTAINING_INSN (pat) = insn; - LABEL_NEXTREF (pat) = LABEL_REFS (label); - LABEL_REFS (label) = pat; - - return; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - record_label_references (insn, XEXP (pat, i)); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (pat, i); j++) - record_label_references (insn, XVECEXP (pat, i, j)); - } - } -} - -/* Return a pointer to the REG expression within PAT. If PAT is not a - REG, possible enclosed by a conversion rtx, return the inner part of - PAT that stopped the search. */ - -static rtx * -get_true_reg (pat) - rtx *pat; -{ - while (GET_CODE (*pat) == SUBREG - || GET_CODE (*pat) == FLOAT - || GET_CODE (*pat) == FIX - || GET_CODE (*pat) == FLOAT_EXTEND) - pat = & XEXP (*pat, 0); - - return pat; -} - -/* Scan the OPERANDS and OPERAND_CONSTRAINTS of an asm_operands. - N_OPERANDS is the total number of operands. Return which alternative - matched, or -1 is no alternative matches. - - OPERAND_MATCHES is an array which indicates which operand this - operand matches due to the constraints, or -1 if no match is required. - If two operands match by coincidence, but are not required to match by - the constraints, -1 is returned. - - OPERAND_CLASS is an array which indicates the smallest class - required by the constraints. If the alternative that matches calls - for some class `class', and the operand matches a subclass of `class', - OPERAND_CLASS is set to `class' as required by the constraints, not to - the subclass. If an alternative allows more than one class, - OPERAND_CLASS is set to the smallest class that is a union of the - allowed classes. */ - -static int -constrain_asm_operands (n_operands, operands, operand_constraints, - operand_matches, operand_class) - int n_operands; - rtx *operands; - char **operand_constraints; - int *operand_matches; - enum reg_class *operand_class; -{ - char **constraints = (char **) alloca (n_operands * sizeof (char *)); - char *q; - int this_alternative, this_operand; - int n_alternatives; - int j; - - for (j = 0; j < n_operands; j++) - constraints[j] = operand_constraints[j]; - - /* Compute the number of alternatives in the operands. reload has - already guaranteed that all operands have the same number of - alternatives. */ - - n_alternatives = 1; - for (q = constraints[0]; *q; q++) - n_alternatives += (*q == ','); - - this_alternative = 0; - while (this_alternative < n_alternatives) - { - int lose = 0; - int i; - - /* No operands match, no narrow class requirements yet. */ - for (i = 0; i < n_operands; i++) - { - operand_matches[i] = -1; - operand_class[i] = NO_REGS; - } - - for (this_operand = 0; this_operand < n_operands; this_operand++) - { - rtx op = operands[this_operand]; - enum machine_mode mode = GET_MODE (op); - char *p = constraints[this_operand]; - int offset = 0; - int win = 0; - int c; - - if (GET_CODE (op) == SUBREG) - { - if (GET_CODE (SUBREG_REG (op)) == REG - && REGNO (SUBREG_REG (op)) < FIRST_PSEUDO_REGISTER) - offset = SUBREG_WORD (op); - op = SUBREG_REG (op); - } - - /* An empty constraint or empty alternative - allows anything which matched the pattern. */ - if (*p == 0 || *p == ',') - win = 1; - - while (*p && (c = *p++) != ',') - switch (c) - { - case '=': - case '+': - case '?': - case '&': - case '!': - case '*': - case '%': - /* Ignore these. */ - break; - - case '#': - /* Ignore rest of this alternative. */ - while (*p && *p != ',') p++; - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - /* This operand must be the same as a previous one. - This kind of constraint is used for instructions such - as add when they take only two operands. - - Note that the lower-numbered operand is passed first. */ - - if (operands_match_p (operands[c - '0'], - operands[this_operand])) - { - operand_matches[this_operand] = c - '0'; - win = 1; - } - break; - - case 'p': - /* p is used for address_operands. Since this is an asm, - just to make sure that the operand is valid for Pmode. */ - - if (strict_memory_address_p (Pmode, op)) - win = 1; - break; - - case 'g': - /* Anything goes unless it is a REG and really has a hard reg - but the hard reg is not in the class GENERAL_REGS. */ - if (GENERAL_REGS == ALL_REGS - || GET_CODE (op) != REG - || reg_fits_class_p (op, GENERAL_REGS, offset, mode)) - { - if (GET_CODE (op) == REG) - operand_class[this_operand] - = reg_class_subunion[(int) operand_class[this_operand]][(int) GENERAL_REGS]; - win = 1; - } - break; - - case 'r': - if (GET_CODE (op) == REG - && (GENERAL_REGS == ALL_REGS - || reg_fits_class_p (op, GENERAL_REGS, offset, mode))) - { - operand_class[this_operand] - = reg_class_subunion[(int) operand_class[this_operand]][(int) GENERAL_REGS]; - win = 1; - } - break; - - case 'X': - /* This is used for a MATCH_SCRATCH in the cases when we - don't actually need anything. So anything goes any time. */ - win = 1; - break; - - case 'm': - if (GET_CODE (op) == MEM) - win = 1; - break; - - case '<': - if (GET_CODE (op) == MEM - && (GET_CODE (XEXP (op, 0)) == PRE_DEC - || GET_CODE (XEXP (op, 0)) == POST_DEC)) - win = 1; - break; - - case '>': - if (GET_CODE (op) == MEM - && (GET_CODE (XEXP (op, 0)) == PRE_INC - || GET_CODE (XEXP (op, 0)) == POST_INC)) - win = 1; - break; - - case 'E': - /* Match any CONST_DOUBLE, but only if - we can examine the bits of it reliably. */ - if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT - || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD) - && GET_CODE (op) != VOIDmode && ! flag_pretend_float) - break; - if (GET_CODE (op) == CONST_DOUBLE) - win = 1; - break; - - case 'F': - if (GET_CODE (op) == CONST_DOUBLE) - win = 1; - break; - - case 'G': - case 'H': - if (GET_CODE (op) == CONST_DOUBLE - && CONST_DOUBLE_OK_FOR_LETTER_P (op, c)) - win = 1; - break; - - case 's': - if (GET_CODE (op) == CONST_INT - || (GET_CODE (op) == CONST_DOUBLE - && GET_MODE (op) == VOIDmode)) - break; - /* Fall through */ - case 'i': - if (CONSTANT_P (op)) - win = 1; - break; - - case 'n': - if (GET_CODE (op) == CONST_INT - || (GET_CODE (op) == CONST_DOUBLE - && GET_MODE (op) == VOIDmode)) - win = 1; - break; - - case 'I': - case 'J': - case 'K': - case 'L': - case 'M': - case 'N': - case 'O': - case 'P': - if (GET_CODE (op) == CONST_INT - && CONST_OK_FOR_LETTER_P (INTVAL (op), c)) - win = 1; - break; - -#ifdef EXTRA_CONSTRAINT - case 'Q': - case 'R': - case 'S': - case 'T': - case 'U': - if (EXTRA_CONSTRAINT (op, c)) - win = 1; - break; -#endif - - case 'V': - if (GET_CODE (op) == MEM && ! offsettable_memref_p (op)) - win = 1; - break; - - case 'o': - if (offsettable_memref_p (op)) - win = 1; - break; - - default: - if (GET_CODE (op) == REG - && reg_fits_class_p (op, REG_CLASS_FROM_LETTER (c), - offset, mode)) - { - operand_class[this_operand] - = reg_class_subunion[(int)operand_class[this_operand]][(int) REG_CLASS_FROM_LETTER (c)]; - win = 1; - } - } - - constraints[this_operand] = p; - /* If this operand did not win somehow, - this alternative loses. */ - if (! win) - lose = 1; - } - /* This alternative won; the operands are ok. - Change whichever operands this alternative says to change. */ - if (! lose) - break; - - this_alternative++; - } - - /* For operands constrained to match another operand, copy the other - operand's class to this operand's class. */ - for (j = 0; j < n_operands; j++) - if (operand_matches[j] >= 0) - operand_class[j] = operand_class[operand_matches[j]]; - - return this_alternative == n_alternatives ? -1 : this_alternative; -} - -/* Record the life info of each stack reg in INSN, updating REGSTACK. - N_INPUTS is the number of inputs; N_OUTPUTS the outputs. CONSTRAINTS - is an array of the constraint strings used in the asm statement. - OPERANDS is an array of all operands for the insn, and is assumed to - contain all output operands, then all inputs operands. - - There are many rules that an asm statement for stack-like regs must - follow. Those rules are explained at the top of this file: the rule - numbers below refer to that explanation. */ - -static void -record_asm_reg_life (insn, regstack, operands, constraints, - n_inputs, n_outputs) - rtx insn; - stack regstack; - rtx *operands; - char **constraints; - int n_inputs, n_outputs; -{ - int i; - int n_operands = n_inputs + n_outputs; - int first_input = n_outputs; - int n_clobbers; - int malformed_asm = 0; - rtx body = PATTERN (insn); - - int *operand_matches = (int *) alloca (n_operands * sizeof (int *)); - - enum reg_class *operand_class - = (enum reg_class *) alloca (n_operands * sizeof (enum reg_class *)); - - int reg_used_as_output[FIRST_PSEUDO_REGISTER]; - int implicitly_dies[FIRST_PSEUDO_REGISTER]; - - rtx *clobber_reg; - - /* Find out what the constraints require. If no constraint - alternative matches, this asm is malformed. */ - i = constrain_asm_operands (n_operands, operands, constraints, - operand_matches, operand_class); - if (i < 0) - malformed_asm = 1; - - /* Strip SUBREGs here to make the following code simpler. */ - for (i = 0; i < n_operands; i++) - if (GET_CODE (operands[i]) == SUBREG - && GET_CODE (SUBREG_REG (operands[i])) == REG) - operands[i] = SUBREG_REG (operands[i]); - - /* Set up CLOBBER_REG. */ - - n_clobbers = 0; - - if (GET_CODE (body) == PARALLEL) - { - clobber_reg = (rtx *) alloca (XVECLEN (body, 0) * sizeof (rtx *)); - - for (i = 0; i < XVECLEN (body, 0); i++) - if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER) - { - rtx clobber = XVECEXP (body, 0, i); - rtx reg = XEXP (clobber, 0); - - if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG) - reg = SUBREG_REG (reg); - - if (STACK_REG_P (reg)) - { - clobber_reg[n_clobbers] = reg; - n_clobbers++; - } - } - } - - /* Enforce rule #4: Output operands must specifically indicate which - reg an output appears in after an asm. "=f" is not allowed: the - operand constraints must select a class with a single reg. - - Also enforce rule #5: Output operands must start at the top of - the reg-stack: output operands may not "skip" a reg. */ - - bzero (reg_used_as_output, sizeof (reg_used_as_output)); - for (i = 0; i < n_outputs; i++) - if (STACK_REG_P (operands[i])) - if (reg_class_size[(int) operand_class[i]] != 1) - { - error_for_asm - (insn, "Output constraint %d must specify a single register", i); - malformed_asm = 1; - } - else - reg_used_as_output[REGNO (operands[i])] = 1; - - - /* Search for first non-popped reg. */ - for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++) - if (! reg_used_as_output[i]) - break; - - /* If there are any other popped regs, that's an error. */ - for (; i < LAST_STACK_REG + 1; i++) - if (reg_used_as_output[i]) - break; - - if (i != LAST_STACK_REG + 1) - { - error_for_asm (insn, "Output regs must be grouped at top of stack"); - malformed_asm = 1; - } - - /* Enforce rule #2: All implicitly popped input regs must be closer - to the top of the reg-stack than any input that is not implicitly - popped. */ - - bzero (implicitly_dies, sizeof (implicitly_dies)); - for (i = first_input; i < first_input + n_inputs; i++) - if (STACK_REG_P (operands[i])) - { - /* An input reg is implicitly popped if it is tied to an - output, or if there is a CLOBBER for it. */ - int j; - - for (j = 0; j < n_clobbers; j++) - if (operands_match_p (clobber_reg[j], operands[i])) - break; - - if (j < n_clobbers || operand_matches[i] >= 0) - implicitly_dies[REGNO (operands[i])] = 1; - } - - /* Search for first non-popped reg. */ - for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++) - if (! implicitly_dies[i]) - break; - - /* If there are any other popped regs, that's an error. */ - for (; i < LAST_STACK_REG + 1; i++) - if (implicitly_dies[i]) - break; - - if (i != LAST_STACK_REG + 1) - { - error_for_asm (insn, - "Implicitly popped regs must be grouped at top of stack"); - malformed_asm = 1; - } - - /* Enfore rule #3: If any input operand uses the "f" constraint, all - output constraints must use the "&" earlyclobber. - - ??? Detect this more deterministically by having constraint_asm_operands - record any earlyclobber. */ - - for (i = first_input; i < first_input + n_inputs; i++) - if (operand_matches[i] == -1) - { - int j; - - for (j = 0; j < n_outputs; j++) - if (operands_match_p (operands[j], operands[i])) - { - error_for_asm (insn, - "Output operand %d must use `&' constraint", j); - malformed_asm = 1; - } - } - - if (malformed_asm) - { - /* Avoid further trouble with this insn. */ - PATTERN (insn) = gen_rtx (USE, VOIDmode, const0_rtx); - PUT_MODE (insn, VOIDmode); - return; - } - - /* Process all outputs */ - for (i = 0; i < n_outputs; i++) - { - rtx op = operands[i]; - - if (! STACK_REG_P (op)) - if (stack_regs_mentioned_p (op)) - abort (); - else - continue; - - /* Each destination is dead before this insn. If the - destination is not used after this insn, record this with - REG_UNUSED. */ - - if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (op))) - REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_UNUSED, op, - REG_NOTES (insn)); - - CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (op)); - } - - /* Process all inputs */ - for (i = first_input; i < first_input + n_inputs; i++) - { - if (! STACK_REG_P (operands[i])) - if (stack_regs_mentioned_p (operands[i])) - abort (); - else - continue; - - /* If an input is dead after the insn, record a death note. - But don't record a death note if there is already a death note, - or if the input is also an output. */ - - if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (operands[i])) - && operand_matches[i] == -1 - && find_regno_note (insn, REG_DEAD, REGNO (operands[i])) == NULL_RTX) - REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_DEAD, operands[i], - REG_NOTES (insn)); - - SET_HARD_REG_BIT (regstack->reg_set, REGNO (operands[i])); - } -} - -/* Scan PAT, which is part of INSN, and record registers appearing in - a SET_DEST in DEST, and other registers in SRC. - - This function does not know about SET_DESTs that are both input and - output (such as ZERO_EXTRACT) - this cannot happen on a 387. */ - -void -record_reg_life_pat (pat, src, dest) - rtx pat; - HARD_REG_SET *src, *dest; -{ - register char *fmt; - register int i; - - if (STACK_REG_P (pat)) - { - if (src) - SET_HARD_REG_BIT (*src, REGNO (pat)); - - if (dest) - SET_HARD_REG_BIT (*dest, REGNO (pat)); - - return; - } - - if (GET_CODE (pat) == SET) - { - record_reg_life_pat (XEXP (pat, 0), NULL_PTR, dest); - record_reg_life_pat (XEXP (pat, 1), src, NULL_PTR); - return; - } - - /* We don't need to consider either of these cases. */ - if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER) - return; - - fmt = GET_RTX_FORMAT (GET_CODE (pat)); - for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--) - { - if (fmt[i] == 'E') - { - register int j; - - for (j = XVECLEN (pat, i) - 1; j >= 0; j--) - record_reg_life_pat (XVECEXP (pat, i, j), src, dest); - } - else if (fmt[i] == 'e') - record_reg_life_pat (XEXP (pat, i), src, dest); - } -} - -/* Calculate the number of inputs and outputs in BODY, an - asm_operands. N_OPERANDS is the total number of operands, and - N_INPUTS and N_OUTPUTS are pointers to ints into which the results are - placed. */ - -static void -get_asm_operand_lengths (body, n_operands, n_inputs, n_outputs) - rtx body; - int n_operands; - int *n_inputs, *n_outputs; -{ - if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS) - *n_inputs = ASM_OPERANDS_INPUT_LENGTH (SET_SRC (body)); - - else if (GET_CODE (body) == ASM_OPERANDS) - *n_inputs = ASM_OPERANDS_INPUT_LENGTH (body); - - else if (GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == SET) - *n_inputs = ASM_OPERANDS_INPUT_LENGTH (SET_SRC (XVECEXP (body, 0, 0))); - - else if (GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS) - *n_inputs = ASM_OPERANDS_INPUT_LENGTH (XVECEXP (body, 0, 0)); - else - abort (); - - *n_outputs = n_operands - *n_inputs; -} - -/* Scan INSN, which is in BLOCK, and record the life & death of stack - registers in REGSTACK. This function is called to process insns from - the last insn in a block to the first. The actual scanning is done in - record_reg_life_pat. - - If a register is live after a CALL_INSN, but is not a value return - register for that CALL_INSN, then code is emitted to initialize that - register. The block_end[] data is kept accurate. - - Existing death and unset notes for stack registers are deleted - before processing the insn. */ - -static void -record_reg_life (insn, block, regstack) - rtx insn; - int block; - stack regstack; -{ - rtx note, *note_link; - int n_operands; - - if ((GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN) - || INSN_DELETED_P (insn)) - return; - - /* Strip death notes for stack regs from this insn */ - - note_link = ®_NOTES(insn); - for (note = *note_link; note; note = XEXP (note, 1)) - if (STACK_REG_P (XEXP (note, 0)) - && (REG_NOTE_KIND (note) == REG_DEAD - || REG_NOTE_KIND (note) == REG_UNUSED)) - *note_link = XEXP (note, 1); - else - note_link = &XEXP (note, 1); - - /* Process all patterns in the insn. */ - - n_operands = asm_noperands (PATTERN (insn)); - if (n_operands >= 0) - { - /* This insn is an `asm' with operands. Decode the operands, - decide how many are inputs, and record the life information. */ - - rtx operands[MAX_RECOG_OPERANDS]; - rtx body = PATTERN (insn); - int n_inputs, n_outputs; - char **constraints = (char **) alloca (n_operands * sizeof (char *)); - - decode_asm_operands (body, operands, NULL_PTR, constraints, NULL_PTR); - get_asm_operand_lengths (body, n_operands, &n_inputs, &n_outputs); - record_asm_reg_life (insn, regstack, operands, constraints, - n_inputs, n_outputs); - return; - } - - /* An insn referencing a stack reg has a mode of QImode. */ - if (GET_MODE (insn) == QImode) - { - HARD_REG_SET src, dest; - int regno; - - CLEAR_HARD_REG_SET (src); - CLEAR_HARD_REG_SET (dest); - record_reg_life_pat (PATTERN (insn), &src, &dest); - - for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG; regno++) - if (! TEST_HARD_REG_BIT (regstack->reg_set, regno)) - { - if (TEST_HARD_REG_BIT (src, regno) - && ! TEST_HARD_REG_BIT (dest, regno)) - REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_DEAD, - FP_mode_reg[regno][(int) DFmode], - REG_NOTES (insn)); - else if (TEST_HARD_REG_BIT (dest, regno)) - REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_UNUSED, - FP_mode_reg[regno][(int) DFmode], - REG_NOTES (insn)); - } - - AND_COMPL_HARD_REG_SET (regstack->reg_set, dest); - IOR_HARD_REG_SET (regstack->reg_set, src); - } - - /* There might be a reg that is live after a function call. - Initialize it to zero so that the program does not crash. See comment - towards the end of stack_reg_life_analysis(). */ - - if (GET_CODE (insn) == CALL_INSN) - { - int reg = FIRST_FLOAT_REG; - - /* If a stack reg is mentioned in a CALL_INSN, it must be as the - return value. */ - - if (stack_regs_mentioned_p (PATTERN (insn))) - reg++; - - for (; reg <= LAST_STACK_REG; reg++) - if (TEST_HARD_REG_BIT (regstack->reg_set, reg)) - { - rtx init, pat; - - /* The insn will use virtual register numbers, and so - convert_regs is expected to process these. But BLOCK_NUM - cannot be used on these insns, because they do not appear in - block_number[]. */ - - pat = gen_rtx (SET, VOIDmode, FP_mode_reg[reg][(int) DFmode], - CONST0_RTX (DFmode)); - init = emit_insn_after (pat, insn); - PUT_MODE (init, QImode); - - CLEAR_HARD_REG_BIT (regstack->reg_set, reg); - - /* If the CALL_INSN was the end of a block, move the - block_end to point to the new insn. */ - - if (block_end[block] == insn) - block_end[block] = init; - } - - /* Some regs do not survive a CALL */ - - AND_COMPL_HARD_REG_SET (regstack->reg_set, call_used_reg_set); - } -} - -/* Find all basic blocks of the function, which starts with FIRST. - For each JUMP_INSN, build the chain of LABEL_REFS on each CODE_LABEL. */ - -static void -find_blocks (first) - rtx first; -{ - register rtx insn; - register int block; - register RTX_CODE prev_code = BARRIER; - register RTX_CODE code; - - /* Record where all the blocks start and end. - Record which basic blocks control can drop in to. */ - - block = -1; - for (insn = first; insn; insn = NEXT_INSN (insn)) - { - /* Note that this loop must select the same block boundaries - as code in reg_to_stack. */ - - code = GET_CODE (insn); - - if (code == CODE_LABEL - || (prev_code != INSN - && prev_code != CALL_INSN - && prev_code != CODE_LABEL - && (code == INSN || code == CALL_INSN || code == JUMP_INSN))) - { - block_begin[++block] = insn; - block_end[block] = insn; - block_drops_in[block] = prev_code != BARRIER; - } - else if (code == INSN || code == CALL_INSN || code == JUMP_INSN) - block_end[block] = insn; - - BLOCK_NUM (insn) = block; - - if (code == CODE_LABEL) - LABEL_REFS (insn) = insn; /* delete old chain */ - - if (code != NOTE) - prev_code = code; - } - - if (block + 1 != blocks) - abort (); - - /* generate all label references to the corresponding jump insn */ - for (block = 0; block < blocks; block++) - { - insn = block_end[block]; - - if (GET_CODE (insn) == JUMP_INSN) - record_label_references (insn, PATTERN (insn)); - } -} - -/* Determine the which registers are live at the start of each basic - block of the function whose first insn is FIRST. - - First, if the function returns a real_type, mark the function - return type as live at each return point, as the RTL may not give any - hint that the register is live. - - Then, start with the last block and work back to the first block. - Similarly, work backwards within each block, insn by insn, recording - which regs are die and which are used (and therefore live) in the - hard reg set of block_stack_in[]. - - After processing each basic block, if there is a label at the start - of the block, propagate the live registers to all jumps to this block. - - As a special case, if there are regs live in this block, that are - not live in a block containing a jump to this label, and the block - containing the jump has already been processed, we must propagate this - block's entry register life back to the block containing the jump, and - restart life analysis from there. - - In the worst case, this function may traverse the insns - REG_STACK_SIZE times. This is necessary, since a jump towards the end - of the insns may not know that a reg is live at a target that is early - in the insns. So we back up and start over with the new reg live. - - If there are registers that are live at the start of the function, - insns are emitted to initialize these registers. Something similar is - done after CALL_INSNs in record_reg_life. */ - -static void -stack_reg_life_analysis (first) - rtx first; -{ - int reg, block; - struct stack_def regstack; - - if (current_function_returns_real - && STACK_REG_P (DECL_RTL (DECL_RESULT (current_function_decl)))) - { - /* Find all RETURN insns and mark them. */ - - int value_regno = REGNO (DECL_RTL (DECL_RESULT (current_function_decl))); - - for (block = blocks - 1; block >= 0; block--) - if (GET_CODE (block_end[block]) == JUMP_INSN - && GET_CODE (PATTERN (block_end[block])) == RETURN) - SET_HARD_REG_BIT (block_out_reg_set[block], value_regno); - - /* Mark of the end of last block if we "fall off" the end of the - function into the epilogue. */ - - if (GET_CODE (block_end[blocks-1]) != JUMP_INSN - || GET_CODE (PATTERN (block_end[blocks-1])) == RETURN) - SET_HARD_REG_BIT (block_out_reg_set[blocks-1], value_regno); - } - - /* now scan all blocks backward for stack register use */ - - block = blocks - 1; - while (block >= 0) - { - register rtx insn, prev; - - /* current register status at last instruction */ - - COPY_HARD_REG_SET (regstack.reg_set, block_out_reg_set[block]); - - prev = block_end[block]; - do - { - insn = prev; - prev = PREV_INSN (insn); - - /* If the insn is a CALL_INSN, we need to ensure that - everything dies. But otherwise don't process unless there - are some stack regs present. */ - - if (GET_MODE (insn) == QImode || GET_CODE (insn) == CALL_INSN) - record_reg_life (insn, block, ®stack); - - } while (insn != block_begin[block]); - - /* Set the state at the start of the block. Mark that no - register mapping information known yet. */ - - COPY_HARD_REG_SET (block_stack_in[block].reg_set, regstack.reg_set); - block_stack_in[block].top = -2; - - /* If there is a label, propagate our register life to all jumps - to this label. */ - - if (GET_CODE (insn) == CODE_LABEL) - { - register rtx label; - int must_restart = 0; - - for (label = LABEL_REFS (insn); label != insn; - label = LABEL_NEXTREF (label)) - { - int jump_block = BLOCK_NUM (CONTAINING_INSN (label)); - - if (jump_block < block) - IOR_HARD_REG_SET (block_out_reg_set[jump_block], - block_stack_in[block].reg_set); - else - { - /* The block containing the jump has already been - processed. If there are registers that were not known - to be live then, but are live now, we must back up - and restart life analysis from that point with the new - life information. */ - - GO_IF_HARD_REG_SUBSET (block_stack_in[block].reg_set, - block_out_reg_set[jump_block], - win); - - IOR_HARD_REG_SET (block_out_reg_set[jump_block], - block_stack_in[block].reg_set); - - block = jump_block; - must_restart = 1; - - win: - ; - } - } - if (must_restart) - continue; - } - - if (block_drops_in[block]) - IOR_HARD_REG_SET (block_out_reg_set[block-1], - block_stack_in[block].reg_set); - - block -= 1; - } - - { - /* If any reg is live at the start of the first block of a - function, then we must guarantee that the reg holds some value by - generating our own "load" of that register. Otherwise a 387 would - fault trying to access an empty register. */ - - HARD_REG_SET empty_regs; - CLEAR_HARD_REG_SET (empty_regs); - GO_IF_HARD_REG_SUBSET (block_stack_in[0].reg_set, empty_regs, - no_live_regs); - } - - /* Load zero into each live register. The fact that a register - appears live at the function start does not necessarily imply an error - in the user program: it merely means that we could not determine that - there wasn't such an error, just as -Wunused sometimes gives - "incorrect" warnings. In those cases, these initializations will do - no harm. - - Note that we are inserting virtual register references here: - these insns must be processed by convert_regs later. Also, these - insns will not be in block_number, so BLOCK_NUM() will fail for them. */ - - for (reg = LAST_STACK_REG; reg >= FIRST_STACK_REG; reg--) - if (TEST_HARD_REG_BIT (block_stack_in[0].reg_set, reg)) - { - rtx init_rtx; - - init_rtx = gen_rtx (SET, VOIDmode, FP_mode_reg[reg][(int) DFmode], - CONST0_RTX (DFmode)); - block_begin[0] = emit_insn_after (init_rtx, first); - PUT_MODE (block_begin[0], QImode); - - CLEAR_HARD_REG_BIT (block_stack_in[0].reg_set, reg); - } - - no_live_regs: - ; -} - -/***************************************************************************** - This section deals with stack register substitution, and forms the second - pass over the RTL. - *****************************************************************************/ - -/* Replace REG, which is a pointer to a stack reg RTX, with an RTX for - the desired hard REGNO. */ - -static void -replace_reg (reg, regno) - rtx *reg; - int regno; -{ - if (regno < FIRST_STACK_REG || regno > LAST_STACK_REG - || ! STACK_REG_P (*reg)) - abort (); - - if (GET_MODE_CLASS (GET_MODE (*reg)) != MODE_FLOAT) - abort (); - - *reg = FP_mode_reg[regno][(int) GET_MODE (*reg)]; -} - -/* Remove a note of type NOTE, which must be found, for register - number REGNO from INSN. Remove only one such note. */ - -static void -remove_regno_note (insn, note, regno) - rtx insn; - enum reg_note note; - int regno; -{ - register rtx *note_link, this; - - note_link = ®_NOTES(insn); - for (this = *note_link; this; this = XEXP (this, 1)) - if (REG_NOTE_KIND (this) == note - && REG_P (XEXP (this, 0)) && REGNO (XEXP (this, 0)) == regno) - { - *note_link = XEXP (this, 1); - return; - } - else - note_link = &XEXP (this, 1); - - abort (); -} - -/* Find the hard register number of virtual register REG in REGSTACK. - The hard register number is relative to the top of the stack. -1 is - returned if the register is not found. */ - -static int -get_hard_regnum (regstack, reg) - stack regstack; - rtx reg; -{ - int i; - - if (! STACK_REG_P (reg)) - abort (); - - for (i = regstack->top; i >= 0; i--) - if (regstack->reg[i] == REGNO (reg)) - break; - - return i >= 0 ? (FIRST_STACK_REG + regstack->top - i) : -1; -} - -/* Delete INSN from the RTL. Mark the insn, but don't remove it from - the chain of insns. Doing so could confuse block_begin and block_end - if this were the only insn in the block. */ - -static void -delete_insn_for_stacker (insn) - rtx insn; -{ - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - INSN_DELETED_P (insn) = 1; -} - -/* Emit an insn to pop virtual register REG before or after INSN. - REGSTACK is the stack state after INSN and is updated to reflect this - pop. WHEN is either emit_insn_before or emit_insn_after. A pop insn - is represented as a SET whose destination is the register to be popped - and source is the top of stack. A death note for the top of stack - cases the movdf pattern to pop. */ - -static rtx -emit_pop_insn (insn, regstack, reg, when) - rtx insn; - stack regstack; - rtx reg; - rtx (*when)(); -{ - rtx pop_insn, pop_rtx; - int hard_regno; - - hard_regno = get_hard_regnum (regstack, reg); - - if (hard_regno < FIRST_STACK_REG) - abort (); - - pop_rtx = gen_rtx (SET, VOIDmode, FP_mode_reg[hard_regno][(int) DFmode], - FP_mode_reg[FIRST_STACK_REG][(int) DFmode]); - - pop_insn = (*when) (pop_rtx, insn); - /* ??? This used to be VOIDmode, but that seems wrong. */ - PUT_MODE (pop_insn, QImode); - - REG_NOTES (pop_insn) = gen_rtx (EXPR_LIST, REG_DEAD, - FP_mode_reg[FIRST_STACK_REG][(int) DFmode], - REG_NOTES (pop_insn)); - - regstack->reg[regstack->top - (hard_regno - FIRST_STACK_REG)] - = regstack->reg[regstack->top]; - regstack->top -= 1; - CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (reg)); - - return pop_insn; -} - -/* Emit an insn before or after INSN to swap virtual register REG with the - top of stack. WHEN should be `emit_insn_before' or `emit_insn_before' - REGSTACK is the stack state before the swap, and is updated to reflect - the swap. A swap insn is represented as a PARALLEL of two patterns: - each pattern moves one reg to the other. - - If REG is already at the top of the stack, no insn is emitted. */ - -static void -emit_swap_insn (insn, regstack, reg) - rtx insn; - stack regstack; - rtx reg; -{ - int hard_regno; - rtx gen_swapdf(); - rtx swap_rtx, swap_insn; - int tmp, other_reg; /* swap regno temps */ - rtx i1; /* the stack-reg insn prior to INSN */ - rtx i1set = NULL_RTX; /* the SET rtx within I1 */ - - hard_regno = get_hard_regnum (regstack, reg); - - if (hard_regno < FIRST_STACK_REG) - abort (); - if (hard_regno == FIRST_STACK_REG) - return; - - other_reg = regstack->top - (hard_regno - FIRST_STACK_REG); - - tmp = regstack->reg[other_reg]; - regstack->reg[other_reg] = regstack->reg[regstack->top]; - regstack->reg[regstack->top] = tmp; - - /* Find the previous insn involving stack regs, but don't go past - any labels, calls or jumps. */ - i1 = prev_nonnote_insn (insn); - while (i1 && GET_CODE (i1) == INSN && GET_MODE (i1) != QImode) - i1 = prev_nonnote_insn (i1); - - if (i1) - i1set = single_set (i1); - - if (i1set) - { - rtx i2; /* the stack-reg insn prior to I1 */ - rtx i1src = *get_true_reg (&SET_SRC (i1set)); - rtx i1dest = *get_true_reg (&SET_DEST (i1set)); - - /* If the previous register stack push was from the reg we are to - swap with, omit the swap. */ - - if (GET_CODE (i1dest) == REG && REGNO (i1dest) == FIRST_STACK_REG - && GET_CODE (i1src) == REG && REGNO (i1src) == hard_regno - 1 - && find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX) - return; - - /* If the previous insn wrote to the reg we are to swap with, - omit the swap. */ - - if (GET_CODE (i1dest) == REG && REGNO (i1dest) == hard_regno - && GET_CODE (i1src) == REG && REGNO (i1src) == FIRST_STACK_REG - && find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX) - return; - } - - if (GET_RTX_CLASS (GET_CODE (i1)) == 'i' && sets_cc0_p (PATTERN (i1))) - { - i1 = next_nonnote_insn (i1); - if (i1 == insn) - abort (); - } - - swap_rtx = gen_swapdf (FP_mode_reg[hard_regno][(int) DFmode], - FP_mode_reg[FIRST_STACK_REG][(int) DFmode]); - swap_insn = emit_insn_after (swap_rtx, i1); - /* ??? This used to be VOIDmode, but that seems wrong. */ - PUT_MODE (swap_insn, QImode); -} - -/* Handle a move to or from a stack register in PAT, which is in INSN. - REGSTACK is the current stack. */ - -static void -move_for_stack_reg (insn, regstack, pat) - rtx insn; - stack regstack; - rtx pat; -{ - rtx *src = get_true_reg (&SET_SRC (pat)); - rtx *dest = get_true_reg (&SET_DEST (pat)); - rtx note; - - if (STACK_REG_P (*src) && STACK_REG_P (*dest)) - { - /* Write from one stack reg to another. If SRC dies here, then - just change the register mapping and delete the insn. */ - - note = find_regno_note (insn, REG_DEAD, REGNO (*src)); - if (note) - { - int i; - - /* If this is a no-op move, there must not be a REG_DEAD note. */ - if (REGNO (*src) == REGNO (*dest)) - abort (); - - for (i = regstack->top; i >= 0; i--) - if (regstack->reg[i] == REGNO (*src)) - break; - - /* The source must be live, and the dest must be dead. */ - if (i < 0 || get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG) - abort (); - - /* It is possible that the dest is unused after this insn. - If so, just pop the src. */ - - if (find_regno_note (insn, REG_UNUSED, REGNO (*dest))) - { - emit_pop_insn (insn, regstack, *src, emit_insn_after); - - delete_insn_for_stacker (insn); - return; - } - - regstack->reg[i] = REGNO (*dest); - - SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); - CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src)); - - delete_insn_for_stacker (insn); - - return; - } - - /* The source reg does not die. */ - - /* If this appears to be a no-op move, delete it, or else it - will confuse the machine description output patterns. But if - it is REG_UNUSED, we must pop the reg now, as per-insn processing - for REG_UNUSED will not work for deleted insns. */ - - if (REGNO (*src) == REGNO (*dest)) - { - if (find_regno_note (insn, REG_UNUSED, REGNO (*dest))) - emit_pop_insn (insn, regstack, *dest, emit_insn_after); - - delete_insn_for_stacker (insn); - return; - } - - /* The destination ought to be dead */ - if (get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG) - abort (); - - replace_reg (src, get_hard_regnum (regstack, *src)); - - regstack->reg[++regstack->top] = REGNO (*dest); - SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); - replace_reg (dest, FIRST_STACK_REG); - } - else if (STACK_REG_P (*src)) - { - /* Save from a stack reg to MEM, or possibly integer reg. Since - only top of stack may be saved, emit an exchange first if - needs be. */ - - emit_swap_insn (insn, regstack, *src); - - note = find_regno_note (insn, REG_DEAD, REGNO (*src)); - if (note) - { - replace_reg (&XEXP (note, 0), FIRST_STACK_REG); - regstack->top--; - CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src)); - } - - replace_reg (src, FIRST_STACK_REG); - } - else if (STACK_REG_P (*dest)) - { - /* Load from MEM, or possibly integer REG or constant, into the - stack regs. The actual target is always the top of the - stack. The stack mapping is changed to reflect that DEST is - now at top of stack. */ - - /* The destination ought to be dead */ - if (get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG) - abort (); - - if (regstack->top >= REG_STACK_SIZE) - abort (); - - regstack->reg[++regstack->top] = REGNO (*dest); - SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); - replace_reg (dest, FIRST_STACK_REG); - } - else - abort (); -} - -void -swap_rtx_condition (pat) - rtx pat; -{ - register char *fmt; - register int i; - - if (GET_RTX_CLASS (GET_CODE (pat)) == '<') - { - PUT_CODE (pat, swap_condition (GET_CODE (pat))); - return; - } - - fmt = GET_RTX_FORMAT (GET_CODE (pat)); - for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--) - { - if (fmt[i] == 'E') - { - register int j; - - for (j = XVECLEN (pat, i) - 1; j >= 0; j--) - swap_rtx_condition (XVECEXP (pat, i, j)); - } - else if (fmt[i] == 'e') - swap_rtx_condition (XEXP (pat, i)); - } -} - -/* Handle a comparison. Special care needs to be taken to avoid - causing comparisons that a 387 cannot do correctly, such as EQ. - - Also, a pop insn may need to be emitted. The 387 does have an - `fcompp' insn that can pop two regs, but it is sometimes too expensive - to do this - a `fcomp' followed by a `fstpl %st(0)' may be easier to - set up. */ - -static void -compare_for_stack_reg (insn, regstack, pat) - rtx insn; - stack regstack; - rtx pat; -{ - rtx *src1, *src2; - rtx src1_note, src2_note; - - src1 = get_true_reg (&XEXP (SET_SRC (pat), 0)); - src2 = get_true_reg (&XEXP (SET_SRC (pat), 1)); - - /* ??? If fxch turns out to be cheaper than fstp, give priority to - registers that die in this insn - move those to stack top first. */ - if (! STACK_REG_P (*src1) - || (STACK_REG_P (*src2) - && get_hard_regnum (regstack, *src2) == FIRST_STACK_REG)) - { - rtx temp, next; - - temp = XEXP (SET_SRC (pat), 0); - XEXP (SET_SRC (pat), 0) = XEXP (SET_SRC (pat), 1); - XEXP (SET_SRC (pat), 1) = temp; - - src1 = get_true_reg (&XEXP (SET_SRC (pat), 0)); - src2 = get_true_reg (&XEXP (SET_SRC (pat), 1)); - - next = next_cc0_user (insn); - if (next == NULL_RTX) - abort (); - - swap_rtx_condition (PATTERN (next)); - INSN_CODE (next) = -1; - INSN_CODE (insn) = -1; - } - - /* We will fix any death note later. */ - - src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1)); - - if (STACK_REG_P (*src2)) - src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2)); - else - src2_note = NULL_RTX; - - emit_swap_insn (insn, regstack, *src1); - - replace_reg (src1, FIRST_STACK_REG); - - if (STACK_REG_P (*src2)) - replace_reg (src2, get_hard_regnum (regstack, *src2)); - - if (src1_note) - { - CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (XEXP (src1_note, 0))); - replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG); - regstack->top--; - } - - /* If the second operand dies, handle that. But if the operands are - the same stack register, don't bother, because only one death is - needed, and it was just handled. */ - - if (src2_note - && ! (STACK_REG_P (*src1) && STACK_REG_P (*src2) - && REGNO (*src1) == REGNO (*src2))) - { - /* As a special case, two regs may die in this insn if src2 is - next to top of stack and the top of stack also dies. Since - we have already popped src1, "next to top of stack" is really - at top (FIRST_STACK_REG) now. */ - - if (get_hard_regnum (regstack, XEXP (src2_note, 0)) == FIRST_STACK_REG - && src1_note) - { - CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (XEXP (src2_note, 0))); - replace_reg (&XEXP (src2_note, 0), FIRST_STACK_REG + 1); - regstack->top--; - } - else - { - /* The 386 can only represent death of the first operand in - the case handled above. In all other cases, emit a separate - pop and remove the death note from here. */ - - link_cc0_insns (insn); - - remove_regno_note (insn, REG_DEAD, REGNO (XEXP (src2_note, 0))); - - emit_pop_insn (insn, regstack, XEXP (src2_note, 0), - emit_insn_after); - } - } -} - -/* Substitute new registers in PAT, which is part of INSN. REGSTACK - is the current register layout. */ - -static void -subst_stack_regs_pat (insn, regstack, pat) - rtx insn; - stack regstack; - rtx pat; -{ - rtx *dest, *src; - rtx *src1 = (rtx *) NULL_PTR, *src2; - rtx src1_note, src2_note; - - if (GET_CODE (pat) != SET) - return; - - dest = get_true_reg (&SET_DEST (pat)); - src = get_true_reg (&SET_SRC (pat)); - - /* See if this is a `movM' pattern, and handle elsewhere if so. */ - - if (*dest != cc0_rtx - && (STACK_REG_P (*src) - || (STACK_REG_P (*dest) - && (GET_CODE (*src) == REG || GET_CODE (*src) == MEM - || GET_CODE (*src) == CONST_DOUBLE)))) - move_for_stack_reg (insn, regstack, pat); - else - switch (GET_CODE (SET_SRC (pat))) - { - case COMPARE: - compare_for_stack_reg (insn, regstack, pat); - break; - - case CALL: - regstack->reg[++regstack->top] = REGNO (*dest); - SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); - replace_reg (dest, FIRST_STACK_REG); - break; - - case REG: - /* This is a `tstM2' case. */ - if (*dest != cc0_rtx) - abort (); - - src1 = src; - - /* Fall through. */ - - case FLOAT_TRUNCATE: - case SQRT: - case ABS: - case NEG: - /* These insns only operate on the top of the stack. DEST might - be cc0_rtx if we're processing a tstM pattern. Also, it's - possible that the tstM case results in a REG_DEAD note on the - source. */ - - if (src1 == 0) - src1 = get_true_reg (&XEXP (SET_SRC (pat), 0)); - - emit_swap_insn (insn, regstack, *src1); - - src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1)); - - if (STACK_REG_P (*dest)) - replace_reg (dest, FIRST_STACK_REG); - - if (src1_note) - { - replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG); - regstack->top--; - CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src1)); - } - - replace_reg (src1, FIRST_STACK_REG); - - break; - - case MINUS: - case DIV: - /* On i386, reversed forms of subM3 and divM3 exist for - MODE_FLOAT, so the same code that works for addM3 and mulM3 - can be used. */ - case MULT: - case PLUS: - /* These insns can accept the top of stack as a destination - from a stack reg or mem, or can use the top of stack as a - source and some other stack register (possibly top of stack) - as a destination. */ - - src1 = get_true_reg (&XEXP (SET_SRC (pat), 0)); - src2 = get_true_reg (&XEXP (SET_SRC (pat), 1)); - - /* We will fix any death note later. */ - - if (STACK_REG_P (*src1)) - src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1)); - else - src1_note = NULL_RTX; - if (STACK_REG_P (*src2)) - src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2)); - else - src2_note = NULL_RTX; - - /* If either operand is not a stack register, then the dest - must be top of stack. */ - - if (! STACK_REG_P (*src1) || ! STACK_REG_P (*src2)) - emit_swap_insn (insn, regstack, *dest); - else - { - /* Both operands are REG. If neither operand is already - at the top of stack, choose to make the one that is the dest - the new top of stack. */ - - int src1_hard_regnum, src2_hard_regnum; - - src1_hard_regnum = get_hard_regnum (regstack, *src1); - src2_hard_regnum = get_hard_regnum (regstack, *src2); - if (src1_hard_regnum == -1 || src2_hard_regnum == -1) - abort (); - - if (src1_hard_regnum != FIRST_STACK_REG - && src2_hard_regnum != FIRST_STACK_REG) - emit_swap_insn (insn, regstack, *dest); - } - - if (STACK_REG_P (*src1)) - replace_reg (src1, get_hard_regnum (regstack, *src1)); - if (STACK_REG_P (*src2)) - replace_reg (src2, get_hard_regnum (regstack, *src2)); - - if (src1_note) - { - /* If the register that dies is at the top of stack, then - the destination is somewhere else - merely substitute it. - But if the reg that dies is not at top of stack, then - move the top of stack to the dead reg, as though we had - done the insn and then a store-with-pop. */ - - if (REGNO (XEXP (src1_note, 0)) == regstack->reg[regstack->top]) - { - SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); - replace_reg (dest, get_hard_regnum (regstack, *dest)); - } - else - { - int regno = get_hard_regnum (regstack, XEXP (src1_note, 0)); - - SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); - replace_reg (dest, regno); - - regstack->reg[regstack->top - (regno - FIRST_STACK_REG)] - = regstack->reg[regstack->top]; - } - - CLEAR_HARD_REG_BIT (regstack->reg_set, - REGNO (XEXP (src1_note, 0))); - replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG); - regstack->top--; - } - else if (src2_note) - { - if (REGNO (XEXP (src2_note, 0)) == regstack->reg[regstack->top]) - { - SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); - replace_reg (dest, get_hard_regnum (regstack, *dest)); - } - else - { - int regno = get_hard_regnum (regstack, XEXP (src2_note, 0)); - - SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); - replace_reg (dest, regno); - - regstack->reg[regstack->top - (regno - FIRST_STACK_REG)] - = regstack->reg[regstack->top]; - } - - CLEAR_HARD_REG_BIT (regstack->reg_set, - REGNO (XEXP (src2_note, 0))); - replace_reg (&XEXP (src2_note, 0), FIRST_STACK_REG); - regstack->top--; - } - else - { - SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); - replace_reg (dest, get_hard_regnum (regstack, *dest)); - } - - break; - - case UNSPEC: - switch (XINT (SET_SRC (pat), 1)) - { - case 1: /* sin */ - case 2: /* cos */ - /* These insns only operate on the top of the stack. */ - - src1 = get_true_reg (&XVECEXP (SET_SRC (pat), 0, 0)); - - emit_swap_insn (insn, regstack, *src1); - - src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1)); - - if (STACK_REG_P (*dest)) - replace_reg (dest, FIRST_STACK_REG); - - if (src1_note) - { - replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG); - regstack->top--; - CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src1)); - } - - replace_reg (src1, FIRST_STACK_REG); - - break; - - default: - abort (); - } - break; - - default: - abort (); - } -} - -/* Substitute hard regnums for any stack regs in INSN, which has - N_INPUTS inputs and N_OUTPUTS outputs. REGSTACK is the stack info - before the insn, and is updated with changes made here. CONSTRAINTS is - an array of the constraint strings used in the asm statement. - - OPERANDS is an array of the operands, and OPERANDS_LOC is a - parallel array of where the operands were found. The output operands - all precede the input operands. - - There are several requirements and assumptions about the use of - stack-like regs in asm statements. These rules are enforced by - record_asm_stack_regs; see comments there for details. Any - asm_operands left in the RTL at this point may be assume to meet the - requirements, since record_asm_stack_regs removes any problem asm. */ - -static void -subst_asm_stack_regs (insn, regstack, operands, operands_loc, constraints, - n_inputs, n_outputs) - rtx insn; - stack regstack; - rtx *operands, **operands_loc; - char **constraints; - int n_inputs, n_outputs; -{ - int n_operands = n_inputs + n_outputs; - int first_input = n_outputs; - rtx body = PATTERN (insn); - - int *operand_matches = (int *) alloca (n_operands * sizeof (int *)); - enum reg_class *operand_class - = (enum reg_class *) alloca (n_operands * sizeof (enum reg_class *)); - - rtx *note_reg; /* Array of note contents */ - rtx **note_loc; /* Address of REG field of each note */ - enum reg_note *note_kind; /* The type of each note */ - - rtx *clobber_reg; - rtx **clobber_loc; - - struct stack_def temp_stack; - int n_notes; - int n_clobbers; - rtx note; - int i; - - /* Find out what the constraints required. If no constraint - alternative matches, that is a compiler bug: we should have caught - such an insn during the life analysis pass (and reload should have - caught it regardless). */ - - i = constrain_asm_operands (n_operands, operands, constraints, - operand_matches, operand_class); - if (i < 0) - abort (); - - /* Strip SUBREGs here to make the following code simpler. */ - for (i = 0; i < n_operands; i++) - if (GET_CODE (operands[i]) == SUBREG - && GET_CODE (SUBREG_REG (operands[i])) == REG) - { - operands_loc[i] = & SUBREG_REG (operands[i]); - operands[i] = SUBREG_REG (operands[i]); - } - - /* Set up NOTE_REG, NOTE_LOC and NOTE_KIND. */ - - for (i = 0, note = REG_NOTES (insn); note; note = XEXP (note, 1)) - i++; - - note_reg = (rtx *) alloca (i * sizeof (rtx)); - note_loc = (rtx **) alloca (i * sizeof (rtx *)); - note_kind = (enum reg_note *) alloca (i * sizeof (enum reg_note)); - - n_notes = 0; - for (note = REG_NOTES (insn); note; note = XEXP (note, 1)) - { - rtx reg = XEXP (note, 0); - rtx *loc = & XEXP (note, 0); - - if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG) - { - loc = & SUBREG_REG (reg); - reg = SUBREG_REG (reg); - } - - if (STACK_REG_P (reg) - && (REG_NOTE_KIND (note) == REG_DEAD - || REG_NOTE_KIND (note) == REG_UNUSED)) - { - note_reg[n_notes] = reg; - note_loc[n_notes] = loc; - note_kind[n_notes] = REG_NOTE_KIND (note); - n_notes++; - } - } - - /* Set up CLOBBER_REG and CLOBBER_LOC. */ - - n_clobbers = 0; - - if (GET_CODE (body) == PARALLEL) - { - clobber_reg = (rtx *) alloca (XVECLEN (body, 0) * sizeof (rtx *)); - clobber_loc = (rtx **) alloca (XVECLEN (body, 0) * sizeof (rtx **)); - - for (i = 0; i < XVECLEN (body, 0); i++) - if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER) - { - rtx clobber = XVECEXP (body, 0, i); - rtx reg = XEXP (clobber, 0); - rtx *loc = & XEXP (clobber, 0); - - if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG) - { - loc = & SUBREG_REG (reg); - reg = SUBREG_REG (reg); - } - - if (STACK_REG_P (reg)) - { - clobber_reg[n_clobbers] = reg; - clobber_loc[n_clobbers] = loc; - n_clobbers++; - } - } - } - - bcopy (regstack, &temp_stack, sizeof (temp_stack)); - - /* Put the input regs into the desired place in TEMP_STACK. */ - - for (i = first_input; i < first_input + n_inputs; i++) - if (STACK_REG_P (operands[i]) - && reg_class_subset_p (operand_class[i], FLOAT_REGS) - && operand_class[i] != FLOAT_REGS) - { - /* If an operand needs to be in a particular reg in - FLOAT_REGS, the constraint was either 't' or 'u'. Since - these constraints are for single register classes, and reload - guaranteed that operand[i] is already in that class, we can - just use REGNO (operands[i]) to know which actual reg this - operand needs to be in. */ - - int regno = get_hard_regnum (&temp_stack, operands[i]); - - if (regno < 0) - abort (); - - if (regno != REGNO (operands[i])) - { - /* operands[i] is not in the right place. Find it - and swap it with whatever is already in I's place. - K is where operands[i] is now. J is where it should - be. */ - int j, k, temp; - - k = temp_stack.top - (regno - FIRST_STACK_REG); - j = (temp_stack.top - - (REGNO (operands[i]) - FIRST_STACK_REG)); - - temp = temp_stack.reg[k]; - temp_stack.reg[k] = temp_stack.reg[j]; - temp_stack.reg[j] = temp; - } - } - - /* emit insns before INSN to make sure the reg-stack is in the right - order. */ - - change_stack (insn, regstack, &temp_stack, emit_insn_before); - - /* Make the needed input register substitutions. Do death notes and - clobbers too, because these are for inputs, not outputs. */ - - for (i = first_input; i < first_input + n_inputs; i++) - if (STACK_REG_P (operands[i])) - { - int regnum = get_hard_regnum (regstack, operands[i]); - - if (regnum < 0) - abort (); - - replace_reg (operands_loc[i], regnum); - } - - for (i = 0; i < n_notes; i++) - if (note_kind[i] == REG_DEAD) - { - int regnum = get_hard_regnum (regstack, note_reg[i]); - - if (regnum < 0) - abort (); - - replace_reg (note_loc[i], regnum); - } - - for (i = 0; i < n_clobbers; i++) - { - /* It's OK for a CLOBBER to reference a reg that is not live. - Don't try to replace it in that case. */ - int regnum = get_hard_regnum (regstack, clobber_reg[i]); - - if (regnum >= 0) - { - /* Sigh - clobbers always have QImode. But replace_reg knows - that these regs can't be MODE_INT and will abort. Just put - the right reg there without calling replace_reg. */ - - *clobber_loc[i] = FP_mode_reg[regnum][(int) DFmode]; - } - } - - /* Now remove from REGSTACK any inputs that the asm implicitly popped. */ - - for (i = first_input; i < first_input + n_inputs; i++) - if (STACK_REG_P (operands[i])) - { - /* An input reg is implicitly popped if it is tied to an - output, or if there is a CLOBBER for it. */ - int j; - - for (j = 0; j < n_clobbers; j++) - if (operands_match_p (clobber_reg[j], operands[i])) - break; - - if (j < n_clobbers || operand_matches[i] >= 0) - { - /* operands[i] might not be at the top of stack. But that's OK, - because all we need to do is pop the right number of regs - off of the top of the reg-stack. record_asm_stack_regs - guaranteed that all implicitly popped regs were grouped - at the top of the reg-stack. */ - - CLEAR_HARD_REG_BIT (regstack->reg_set, - regstack->reg[regstack->top]); - regstack->top--; - } - } - - /* Now add to REGSTACK any outputs that the asm implicitly pushed. - Note that there isn't any need to substitute register numbers. - ??? Explain why this is true. */ - - for (i = LAST_STACK_REG; i >= FIRST_STACK_REG; i--) - { - /* See if there is an output for this hard reg. */ - int j; - - for (j = 0; j < n_outputs; j++) - if (STACK_REG_P (operands[j]) && REGNO (operands[j]) == i) - { - regstack->reg[++regstack->top] = i; - SET_HARD_REG_BIT (regstack->reg_set, i); - break; - } - } - - /* Now emit a pop insn for any REG_UNUSED output, or any REG_DEAD - input that the asm didn't implicitly pop. If the asm didn't - implicitly pop an input reg, that reg will still be live. - - Note that we can't use find_regno_note here: the register numbers - in the death notes have already been substituted. */ - - for (i = 0; i < n_outputs; i++) - if (STACK_REG_P (operands[i])) - { - int j; - - for (j = 0; j < n_notes; j++) - if (REGNO (operands[i]) == REGNO (note_reg[j]) - && note_kind[j] == REG_UNUSED) - { - insn = emit_pop_insn (insn, regstack, operands[i], - emit_insn_after); - break; - } - } - - for (i = first_input; i < first_input + n_inputs; i++) - if (STACK_REG_P (operands[i])) - { - int j; - - for (j = 0; j < n_notes; j++) - if (REGNO (operands[i]) == REGNO (note_reg[j]) - && note_kind[j] == REG_DEAD - && TEST_HARD_REG_BIT (regstack->reg_set, REGNO (operands[i]))) - { - insn = emit_pop_insn (insn, regstack, operands[i], - emit_insn_after); - break; - } - } -} - -/* Substitute stack hard reg numbers for stack virtual registers in - INSN. Non-stack register numbers are not changed. REGSTACK is the - current stack content. Insns may be emitted as needed to arrange the - stack for the 387 based on the contents of the insn. */ - -static void -subst_stack_regs (insn, regstack) - rtx insn; - stack regstack; -{ - register rtx *note_link, note; - register int i; - int n_operands; - - if ((GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN) - || INSN_DELETED_P (insn)) - return; - - /* The stack should be empty at a call. */ - - if (GET_CODE (insn) == CALL_INSN) - for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++) - if (TEST_HARD_REG_BIT (regstack->reg_set, i)) - abort (); - - /* Do the actual substitution if any stack regs are mentioned. - Since we only record whether entire insn mentions stack regs, and - subst_stack_regs_pat only works for patterns that contain stack regs, - we must check each pattern in a parallel here. A call_value_pop could - fail otherwise. */ - - if (GET_MODE (insn) == QImode) - { - n_operands = asm_noperands (PATTERN (insn)); - if (n_operands >= 0) - { - /* This insn is an `asm' with operands. Decode the operands, - decide how many are inputs, and do register substitution. - Any REG_UNUSED notes will be handled by subst_asm_stack_regs. */ - - rtx operands[MAX_RECOG_OPERANDS]; - rtx *operands_loc[MAX_RECOG_OPERANDS]; - rtx body = PATTERN (insn); - int n_inputs, n_outputs; - char **constraints - = (char **) alloca (n_operands * sizeof (char *)); - - decode_asm_operands (body, operands, operands_loc, - constraints, NULL_PTR); - get_asm_operand_lengths (body, n_operands, &n_inputs, &n_outputs); - subst_asm_stack_regs (insn, regstack, operands, operands_loc, - constraints, n_inputs, n_outputs); - return; - } - - if (GET_CODE (PATTERN (insn)) == PARALLEL) - for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) - { - if (stack_regs_mentioned_p (XVECEXP (PATTERN (insn), 0, i))) - subst_stack_regs_pat (insn, regstack, - XVECEXP (PATTERN (insn), 0, i)); - } - else - subst_stack_regs_pat (insn, regstack, PATTERN (insn)); - } - - /* subst_stack_regs_pat may have deleted a no-op insn. If so, any - REG_UNUSED will already have been dealt with, so just return. */ - - if (INSN_DELETED_P (insn)) - return; - - /* If there is a REG_UNUSED note on a stack register on this insn, - the indicated reg must be popped. The REG_UNUSED note is removed, - since the form of the newly emitted pop insn references the reg, - making it no longer `unset'. */ - - note_link = ®_NOTES(insn); - for (note = *note_link; note; note = XEXP (note, 1)) - if (REG_NOTE_KIND (note) == REG_UNUSED && STACK_REG_P (XEXP (note, 0))) - { - *note_link = XEXP (note, 1); - insn = emit_pop_insn (insn, regstack, XEXP (note, 0), emit_insn_after); - } - else - note_link = &XEXP (note, 1); -} - -/* Change the organization of the stack so that it fits a new basic - block. Some registers might have to be popped, but there can never be - a register live in the new block that is not now live. - - Insert any needed insns before or after INSN. WHEN is emit_insn_before - or emit_insn_after. OLD is the original stack layout, and NEW is - the desired form. OLD is updated to reflect the code emitted, ie, it - will be the same as NEW upon return. - - This function will not preserve block_end[]. But that information - is no longer needed once this has executed. */ - -static void -change_stack (insn, old, new, when) - rtx insn; - stack old; - stack new; - rtx (*when)(); -{ - int reg; - - /* We will be inserting new insns "backwards", by calling emit_insn_before. - If we are to insert after INSN, find the next insn, and insert before - it. */ - - if (when == emit_insn_after) - insn = NEXT_INSN (insn); - - /* Pop any registers that are not needed in the new block. */ - - for (reg = old->top; reg >= 0; reg--) - if (! TEST_HARD_REG_BIT (new->reg_set, old->reg[reg])) - emit_pop_insn (insn, old, FP_mode_reg[old->reg[reg]][(int) DFmode], - emit_insn_before); - - if (new->top == -2) - { - /* If the new block has never been processed, then it can inherit - the old stack order. */ - - new->top = old->top; - bcopy (old->reg, new->reg, sizeof (new->reg)); - } - else - { - /* This block has been entered before, and we must match the - previously selected stack order. */ - - /* By now, the only difference should be the order of the stack, - not their depth or liveliness. */ - - GO_IF_HARD_REG_EQUAL (old->reg_set, new->reg_set, win); - - abort (); - - win: - - if (old->top != new->top) - abort (); - - /* Loop here emitting swaps until the stack is correct. The - worst case number of swaps emitted is N + 2, where N is the - depth of the stack. In some cases, the reg at the top of - stack may be correct, but swapped anyway in order to fix - other regs. But since we never swap any other reg away from - its correct slot, this algorithm will converge. */ - - do - { - /* Swap the reg at top of stack into the position it is - supposed to be in, until the correct top of stack appears. */ - - while (old->reg[old->top] != new->reg[new->top]) - { - for (reg = new->top; reg >= 0; reg--) - if (new->reg[reg] == old->reg[old->top]) - break; - - if (reg == -1) - abort (); - - emit_swap_insn (insn, old, - FP_mode_reg[old->reg[reg]][(int) DFmode]); - } - - /* See if any regs remain incorrect. If so, bring an - incorrect reg to the top of stack, and let the while loop - above fix it. */ - - for (reg = new->top; reg >= 0; reg--) - if (new->reg[reg] != old->reg[reg]) - { - emit_swap_insn (insn, old, - FP_mode_reg[old->reg[reg]][(int) DFmode]); - break; - } - } while (reg >= 0); - - /* At this point there must be no differences. */ - - for (reg = old->top; reg >= 0; reg--) - if (old->reg[reg] != new->reg[reg]) - abort (); - } -} - -/* Check PAT, which points to RTL in INSN, for a LABEL_REF. If it is - found, ensure that a jump from INSN to the code_label to which the - label_ref points ends up with the same stack as that at the - code_label. Do this by inserting insns just before the code_label to - pop and rotate the stack until it is in the correct order. REGSTACK - is the order of the register stack in INSN. - - Any code that is emitted here must not be later processed as part - of any block, as it will already contain hard register numbers. */ - -static void -goto_block_pat (insn, regstack, pat) - rtx insn; - stack regstack; - rtx pat; -{ - rtx label; - rtx new_jump, new_label, new_barrier; - rtx *ref; - stack label_stack; - struct stack_def temp_stack; - int reg; - - if (GET_CODE (pat) != LABEL_REF) - { - int i, j; - char *fmt = GET_RTX_FORMAT (GET_CODE (pat)); - - for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - goto_block_pat (insn, regstack, XEXP (pat, i)); - if (fmt[i] == 'E') - for (j = 0; j < XVECLEN (pat, i); j++) - goto_block_pat (insn, regstack, XVECEXP (pat, i, j)); - } - return; - } - - label = XEXP (pat, 0); - if (GET_CODE (label) != CODE_LABEL) - abort (); - - /* First, see if in fact anything needs to be done to the stack at all. */ - - label_stack = &block_stack_in[BLOCK_NUM (label)]; - - if (label_stack->top == -2) - { - /* If the target block hasn't had a stack order selected, then - we need merely ensure that no pops are needed. */ - - for (reg = regstack->top; reg >= 0; reg--) - if (! TEST_HARD_REG_BIT (label_stack->reg_set, regstack->reg[reg])) - break; - - if (reg == -1) - { - /* change_stack will not emit any code in this case. */ - - change_stack (label, regstack, label_stack, emit_insn_after); - return; - } - } - else if (label_stack->top == regstack->top) - { - for (reg = label_stack->top; reg >= 0; reg--) - if (label_stack->reg[reg] != regstack->reg[reg]) - break; - - if (reg == -1) - return; - } - - /* At least one insn will need to be inserted before label. Insert - a jump around the code we are about to emit. Emit a label for the new - code, and point the original insn at this new label. We can't use - redirect_jump here, because we're using fld[4] of the code labels as - LABEL_REF chains, no NUSES counters. */ - - new_jump = emit_jump_insn_before (gen_jump (label), label); - record_label_references (new_jump, PATTERN (new_jump)); - JUMP_LABEL (new_jump) = label; - - new_barrier = emit_barrier_after (new_jump); - - new_label = gen_label_rtx (); - emit_label_after (new_label, new_barrier); - LABEL_REFS (new_label) = new_label; - - /* The old label_ref will no longer point to the code_label if now uses, - so strip the label_ref from the code_label's chain of references. */ - - for (ref = &LABEL_REFS (label); *ref != label; ref = &LABEL_NEXTREF (*ref)) - if (*ref == pat) - break; - - if (*ref == label) - abort (); - - *ref = LABEL_NEXTREF (*ref); - - XEXP (pat, 0) = new_label; - record_label_references (insn, PATTERN (insn)); - - if (JUMP_LABEL (insn) == label) - JUMP_LABEL (insn) = new_label; - - /* Now emit the needed code. */ - - temp_stack = *regstack; - - change_stack (new_label, &temp_stack, label_stack, emit_insn_after); -} - -/* Traverse all basic blocks in a function, converting the register - references in each insn from the "flat" register file that gcc uses, to - the stack-like registers the 387 uses. */ - -static void -convert_regs () -{ - register int block, reg; - register rtx insn, next; - struct stack_def regstack; - - for (block = 0; block < blocks; block++) - { - if (block_stack_in[block].top == -2) - { - /* This block has not been previously encountered. Choose a - default mapping for any stack regs live on entry */ - - block_stack_in[block].top = -1; - - for (reg = LAST_STACK_REG; reg >= FIRST_STACK_REG; reg--) - if (TEST_HARD_REG_BIT (block_stack_in[block].reg_set, reg)) - block_stack_in[block].reg[++block_stack_in[block].top] = reg; - } - - /* Process all insns in this block. Keep track of `next' here, - so that we don't process any insns emitted while making - substitutions in INSN. */ - - next = block_begin[block]; - regstack = block_stack_in[block]; - do - { - insn = next; - next = NEXT_INSN (insn); - - /* Don't bother processing unless there is a stack reg - mentioned. - - ??? For now, process CALL_INSNs too to make sure that the - stack regs are dead after a call. Remove this eventually. */ - - if (GET_MODE (insn) == QImode || GET_CODE (insn) == CALL_INSN) - subst_stack_regs (insn, ®stack); - - } while (insn != block_end[block]); - - /* Something failed if the stack life doesn't match. */ - - GO_IF_HARD_REG_EQUAL (regstack.reg_set, block_out_reg_set[block], win); - - abort (); - - win: - - /* Adjust the stack of this block on exit to match the stack of - the target block, or copy stack information into stack of - jump target if the target block's stack order hasn't been set - yet. */ - - if (GET_CODE (insn) == JUMP_INSN) - goto_block_pat (insn, ®stack, PATTERN (insn)); - - /* Likewise handle the case where we fall into the next block. */ - - if ((block < blocks - 1) && block_drops_in[block+1]) - change_stack (insn, ®stack, &block_stack_in[block+1], - emit_insn_after); - } - - /* If the last basic block is the end of a loop, and that loop has - regs live at its start, then the last basic block will have regs live - at its end that need to be popped before the function returns. */ - - for (reg = regstack.top; reg >= 0; reg--) - if (! current_function_returns_real - || regstack.reg[reg] != FIRST_STACK_REG) - insn = emit_pop_insn (insn, ®stack, - FP_mode_reg[regstack.reg[reg]][(int) DFmode], - emit_insn_after); -} - -/* Check expression PAT, which is in INSN, for label references. if - one is found, print the block number of destination to FILE. */ - -static void -print_blocks (file, insn, pat) - FILE *file; - rtx insn, pat; -{ - register RTX_CODE code = GET_CODE (pat); - register int i; - register char *fmt; - - if (code == LABEL_REF) - { - register rtx label = XEXP (pat, 0); - - if (GET_CODE (label) != CODE_LABEL) - abort (); - - fprintf (file, " %d", BLOCK_NUM (label)); - - return; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - print_blocks (file, insn, XEXP (pat, i)); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (pat, i); j++) - print_blocks (file, insn, XVECEXP (pat, i, j)); - } - } -} - -/* Write information about stack registers and stack blocks into FILE. - This is part of making a debugging dump. */ -static void -dump_stack_info (file) - FILE *file; -{ - register int block; - - fprintf (file, "\n%d stack blocks.\n", blocks); - for (block = 0; block < blocks; block++) - { - register rtx head, jump, end; - register int regno; - - fprintf (file, "\nStack block %d: first insn %d, last %d.\n", - block, INSN_UID (block_begin[block]), - INSN_UID (block_end[block])); - - head = block_begin[block]; - - fprintf (file, "Reached from blocks: "); - if (GET_CODE (head) == CODE_LABEL) - for (jump = LABEL_REFS (head); - jump != head; - jump = LABEL_NEXTREF (jump)) - { - register int from_block = BLOCK_NUM (CONTAINING_INSN (jump)); - fprintf (file, " %d", from_block); - } - if (block_drops_in[block]) - fprintf (file, " previous"); - - fprintf (file, "\nlive stack registers on block entry: "); - for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG ; regno++) - { - if (TEST_HARD_REG_BIT (block_stack_in[block].reg_set, regno)) - fprintf (file, "%d ", regno); - } - - fprintf (file, "\nlive stack registers on block exit: "); - for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG ; regno++) - { - if (TEST_HARD_REG_BIT (block_out_reg_set[block], regno)) - fprintf (file, "%d ", regno); - } - - end = block_end[block]; - - fprintf (file, "\nJumps to blocks: "); - if (GET_CODE (end) == JUMP_INSN) - print_blocks (file, end, PATTERN (end)); - - if (block + 1 < blocks && block_drops_in[block+1]) - fprintf (file, " next"); - else if (block + 1 == blocks - || (GET_CODE (end) == JUMP_INSN - && GET_CODE (PATTERN (end)) == RETURN)) - fprintf (file, " return"); - - fprintf (file, "\n"); - } -} -#endif /* STACK_REGS */ diff --git a/gnu/usr.bin/cc/common/reload.c b/gnu/usr.bin/cc/common/reload.c deleted file mode 100644 index 08a21c677f..0000000000 --- a/gnu/usr.bin/cc/common/reload.c +++ /dev/null @@ -1,5425 +0,0 @@ -/* Search an insn for pseudo regs that must be in hard regs and are not. - Copyright (C) 1987, 1988, 1989, 1992, 1993 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This file contains subroutines used only from the file reload1.c. - It knows how to scan one insn for operands and values - that need to be copied into registers to make valid code. - It also finds other operands and values which are valid - but for which equivalent values in registers exist and - ought to be used instead. - - Before processing the first insn of the function, call `init_reload'. - - To scan an insn, call `find_reloads'. This does two things: - 1. sets up tables describing which values must be reloaded - for this insn, and what kind of hard regs they must be reloaded into; - 2. optionally record the locations where those values appear in - the data, so they can be replaced properly later. - This is done only if the second arg to `find_reloads' is nonzero. - - The third arg to `find_reloads' specifies the number of levels - of indirect addressing supported by the machine. If it is zero, - indirect addressing is not valid. If it is one, (MEM (REG n)) - is valid even if (REG n) did not get a hard register; if it is two, - (MEM (MEM (REG n))) is also valid even if (REG n) did not get a - hard register, and similarly for higher values. - - Then you must choose the hard regs to reload those pseudo regs into, - and generate appropriate load insns before this insn and perhaps - also store insns after this insn. Set up the array `reload_reg_rtx' - to contain the REG rtx's for the registers you used. In some - cases `find_reloads' will return a nonzero value in `reload_reg_rtx' - for certain reloads. Then that tells you which register to use, - so you do not need to allocate one. But you still do need to add extra - instructions to copy the value into and out of that register. - - Finally you must call `subst_reloads' to substitute the reload reg rtx's - into the locations already recorded. - -NOTE SIDE EFFECTS: - - find_reloads can alter the operands of the instruction it is called on. - - 1. Two operands of any sort may be interchanged, if they are in a - commutative instruction. - This happens only if find_reloads thinks the instruction will compile - better that way. - - 2. Pseudo-registers that are equivalent to constants are replaced - with those constants if they are not in hard registers. - -1 happens every time find_reloads is called. -2 happens only when REPLACE is 1, which is only when -actually doing the reloads, not when just counting them. - - -Using a reload register for several reloads in one insn: - -When an insn has reloads, it is considered as having three parts: -the input reloads, the insn itself after reloading, and the output reloads. -Reloads of values used in memory addresses are often needed for only one part. - -When this is so, reload_when_needed records which part needs the reload. -Two reloads for different parts of the insn can share the same reload -register. - -When a reload is used for addresses in multiple parts, or when it is -an ordinary operand, it is classified as RELOAD_OTHER, and cannot share -a register with any other reload. */ - -#define REG_OK_STRICT - -#include "config.h" -#include "rtl.h" -#include "insn-config.h" -#include "insn-codes.h" -#include "recog.h" -#include "reload.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "flags.h" -#include "real.h" - -#ifndef REGISTER_MOVE_COST -#define REGISTER_MOVE_COST(x, y) 2 -#endif - -/* The variables set up by `find_reloads' are: - - n_reloads number of distinct reloads needed; max reload # + 1 - tables indexed by reload number - reload_in rtx for value to reload from - reload_out rtx for where to store reload-reg afterward if nec - (often the same as reload_in) - reload_reg_class enum reg_class, saying what regs to reload into - reload_inmode enum machine_mode; mode this operand should have - when reloaded, on input. - reload_outmode enum machine_mode; mode this operand should have - when reloaded, on output. - reload_optional char, nonzero for an optional reload. - Optional reloads are ignored unless the - value is already sitting in a register. - reload_inc int, positive amount to increment or decrement by if - reload_in is a PRE_DEC, PRE_INC, POST_DEC, POST_INC. - Ignored otherwise (don't assume it is zero). - reload_in_reg rtx. A reg for which reload_in is the equivalent. - If reload_in is a symbol_ref which came from - reg_equiv_constant, then this is the pseudo - which has that symbol_ref as equivalent. - reload_reg_rtx rtx. This is the register to reload into. - If it is zero when `find_reloads' returns, - you must find a suitable register in the class - specified by reload_reg_class, and store here - an rtx for that register with mode from - reload_inmode or reload_outmode. - reload_nocombine char, nonzero if this reload shouldn't be - combined with another reload. - reload_opnum int, operand number being reloaded. This is - used to group related reloads and need not always - be equal to the actual operand number in the insn, - though it current will be; for in-out operands, it - is one of the two operand numbers. - reload_when_needed enum, classifies reload as needed either for - addressing an input reload, addressing an output, - for addressing a non-reloaded mem ref, - or for unspecified purposes (i.e., more than one - of the above). - reload_secondary_reload int, gives the reload number of a secondary - reload, when needed; otherwise -1 - reload_secondary_p int, 1 if this is a secondary register for one - or more reloads. - reload_secondary_icode enum insn_code, if a secondary reload is required, - gives the INSN_CODE that uses the secondary - reload as a scratch register, or CODE_FOR_nothing - if the secondary reload register is to be an - intermediate register. */ -int n_reloads; - -rtx reload_in[MAX_RELOADS]; -rtx reload_out[MAX_RELOADS]; -enum reg_class reload_reg_class[MAX_RELOADS]; -enum machine_mode reload_inmode[MAX_RELOADS]; -enum machine_mode reload_outmode[MAX_RELOADS]; -rtx reload_reg_rtx[MAX_RELOADS]; -char reload_optional[MAX_RELOADS]; -int reload_inc[MAX_RELOADS]; -rtx reload_in_reg[MAX_RELOADS]; -char reload_nocombine[MAX_RELOADS]; -int reload_opnum[MAX_RELOADS]; -enum reload_type reload_when_needed[MAX_RELOADS]; -int reload_secondary_reload[MAX_RELOADS]; -int reload_secondary_p[MAX_RELOADS]; -enum insn_code reload_secondary_icode[MAX_RELOADS]; - -/* All the "earlyclobber" operands of the current insn - are recorded here. */ -int n_earlyclobbers; -rtx reload_earlyclobbers[MAX_RECOG_OPERANDS]; - -int reload_n_operands; - -/* Replacing reloads. - - If `replace_reloads' is nonzero, then as each reload is recorded - an entry is made for it in the table `replacements'. - Then later `subst_reloads' can look through that table and - perform all the replacements needed. */ - -/* Nonzero means record the places to replace. */ -static int replace_reloads; - -/* Each replacement is recorded with a structure like this. */ -struct replacement -{ - rtx *where; /* Location to store in */ - rtx *subreg_loc; /* Location of SUBREG if WHERE is inside - a SUBREG; 0 otherwise. */ - int what; /* which reload this is for */ - enum machine_mode mode; /* mode it must have */ -}; - -static struct replacement replacements[MAX_RECOG_OPERANDS * ((MAX_REGS_PER_ADDRESS * 2) + 1)]; - -/* Number of replacements currently recorded. */ -static int n_replacements; - -/* Used to track what is modified by an operand. */ -struct decomposition -{ - int reg_flag; /* Nonzero if referencing a register. */ - int safe; /* Nonzero if this can't conflict with anything. */ - rtx base; /* Base adddress for MEM. */ - HOST_WIDE_INT start; /* Starting offset or register number. */ - HOST_WIDE_INT end; /* Endinf offset or register number. */ -}; - -/* MEM-rtx's created for pseudo-regs in stack slots not directly addressable; - (see reg_equiv_address). */ -static rtx memlocs[MAX_RECOG_OPERANDS * ((MAX_REGS_PER_ADDRESS * 2) + 1)]; -static int n_memlocs; - -#ifdef SECONDARY_MEMORY_NEEDED - -/* Save MEMs needed to copy from one class of registers to another. One MEM - is used per mode, but normally only one or two modes are ever used. - - We keep two versions, before and after register elimination. The one - after register elimination is record separately for each operand. This - is done in case the address is not valid to be sure that we separately - reload each. */ - -static rtx secondary_memlocs[NUM_MACHINE_MODES]; -static rtx secondary_memlocs_elim[NUM_MACHINE_MODES][MAX_RECOG_OPERANDS]; -#endif - -/* The instruction we are doing reloads for; - so we can test whether a register dies in it. */ -static rtx this_insn; - -/* Nonzero if this instruction is a user-specified asm with operands. */ -static int this_insn_is_asm; - -/* If hard_regs_live_known is nonzero, - we can tell which hard regs are currently live, - at least enough to succeed in choosing dummy reloads. */ -static int hard_regs_live_known; - -/* Indexed by hard reg number, - element is nonegative if hard reg has been spilled. - This vector is passed to `find_reloads' as an argument - and is not changed here. */ -static short *static_reload_reg_p; - -/* Set to 1 in subst_reg_equivs if it changes anything. */ -static int subst_reg_equivs_changed; - -/* On return from push_reload, holds the reload-number for the OUT - operand, which can be different for that from the input operand. */ -static int output_reloadnum; - -static enum reg_class find_secondary_reload PROTO((rtx, enum reg_class, - enum machine_mode, int, - enum insn_code *, - enum machine_mode *, - enum reg_class *, - enum insn_code *, - enum machine_mode *)); -static int push_reload PROTO((rtx, rtx, rtx *, rtx *, enum reg_class, - enum machine_mode, enum machine_mode, - int, int, int, enum reload_type)); -static void push_replacement PROTO((rtx *, int, enum machine_mode)); -static void combine_reloads PROTO((void)); -static rtx find_dummy_reload PROTO((rtx, rtx, rtx *, rtx *, - enum reg_class, int)); -static int hard_reg_set_here_p PROTO((int, int, rtx)); -static struct decomposition decompose PROTO((rtx)); -static int immune_p PROTO((rtx, rtx, struct decomposition)); -static int alternative_allows_memconst PROTO((char *, int)); -static rtx find_reloads_toplev PROTO((rtx, int, enum reload_type, int, int)); -static rtx make_memloc PROTO((rtx, int)); -static int find_reloads_address PROTO((enum machine_mode, rtx *, rtx, rtx *, - int, enum reload_type, int)); -static rtx subst_reg_equivs PROTO((rtx)); -static rtx subst_indexed_address PROTO((rtx)); -static int find_reloads_address_1 PROTO((rtx, int, rtx *, int, - enum reload_type,int)); -static void find_reloads_address_part PROTO((rtx, rtx *, enum reg_class, - enum machine_mode, int, - enum reload_type, int)); -static int find_inc_amount PROTO((rtx, rtx)); - -#ifdef HAVE_SECONDARY_RELOADS - -/* Determine if any secondary reloads are needed for loading (if IN_P is - non-zero) or storing (if IN_P is zero) X to or from a reload register of - register class RELOAD_CLASS in mode RELOAD_MODE. - - Return the register class of a secondary reload register, or NO_REGS if - none. *PMODE is set to the mode that the register is required in. - If the reload register is needed as a scratch register instead of an - intermediate register, *PICODE is set to the insn_code of the insn to be - used to load or store the primary reload register; otherwise *PICODE - is set to CODE_FOR_nothing. - - In some cases (such as storing MQ into an external memory location on - the RT), both an intermediate register and a scratch register. In that - case, *PICODE is set to CODE_FOR_nothing, the class for the intermediate - register is returned, and the *PTERTIARY_... variables are set to describe - the scratch register. */ - -static enum reg_class -find_secondary_reload (x, reload_class, reload_mode, in_p, picode, pmode, - ptertiary_class, ptertiary_icode, ptertiary_mode) - rtx x; - enum reg_class reload_class; - enum machine_mode reload_mode; - int in_p; - enum insn_code *picode; - enum machine_mode *pmode; - enum reg_class *ptertiary_class; - enum insn_code *ptertiary_icode; - enum machine_mode *ptertiary_mode; -{ - enum reg_class class = NO_REGS; - enum machine_mode mode = reload_mode; - enum insn_code icode = CODE_FOR_nothing; - enum reg_class t_class = NO_REGS; - enum machine_mode t_mode = VOIDmode; - enum insn_code t_icode = CODE_FOR_nothing; - - /* If X is a pseudo-register that has an equivalent MEM (actually, if it - is still a pseudo-register by now, it *must* have an equivalent MEM - but we don't want to assume that), use that equivalent when seeing if - a secondary reload is needed since whether or not a reload is needed - might be sensitive to the form of the MEM. */ - - if (GET_CODE (x) == REG && REGNO (x) >= FIRST_PSEUDO_REGISTER - && reg_equiv_mem[REGNO (x)] != 0) - x = reg_equiv_mem[REGNO (x)]; - -#ifdef SECONDARY_INPUT_RELOAD_CLASS - if (in_p) - class = SECONDARY_INPUT_RELOAD_CLASS (reload_class, reload_mode, x); -#endif - -#ifdef SECONDARY_OUTPUT_RELOAD_CLASS - if (! in_p) - class = SECONDARY_OUTPUT_RELOAD_CLASS (reload_class, reload_mode, x); -#endif - - /* If we don't need any secondary registers, go away; the rest of the - values won't be used. */ - if (class == NO_REGS) - return NO_REGS; - - /* Get a possible insn to use. If the predicate doesn't accept X, don't - use the insn. */ - - icode = (in_p ? reload_in_optab[(int) reload_mode] - : reload_out_optab[(int) reload_mode]); - - if (icode != CODE_FOR_nothing - && insn_operand_predicate[(int) icode][in_p] - && (! (insn_operand_predicate[(int) icode][in_p]) (x, reload_mode))) - icode = CODE_FOR_nothing; - - /* If we will be using an insn, see if it can directly handle the reload - register we will be using. If it can, the secondary reload is for a - scratch register. If it can't, we will use the secondary reload for - an intermediate register and require a tertiary reload for the scratch - register. */ - - if (icode != CODE_FOR_nothing) - { - /* If IN_P is non-zero, the reload register will be the output in - operand 0. If IN_P is zero, the reload register will be the input - in operand 1. Outputs should have an initial "=", which we must - skip. */ - - char insn_letter = insn_operand_constraint[(int) icode][!in_p][in_p]; - enum reg_class insn_class - = (insn_letter == 'r' ? GENERAL_REGS - : REG_CLASS_FROM_LETTER (insn_letter)); - - if (insn_class == NO_REGS - || (in_p && insn_operand_constraint[(int) icode][!in_p][0] != '=') - /* The scratch register's constraint must start with "=&". */ - || insn_operand_constraint[(int) icode][2][0] != '=' - || insn_operand_constraint[(int) icode][2][1] != '&') - abort (); - - if (reg_class_subset_p (reload_class, insn_class)) - mode = insn_operand_mode[(int) icode][2]; - else - { - char t_letter = insn_operand_constraint[(int) icode][2][2]; - class = insn_class; - t_mode = insn_operand_mode[(int) icode][2]; - t_class = (t_letter == 'r' ? GENERAL_REGS - : REG_CLASS_FROM_LETTER (t_letter)); - t_icode = icode; - icode = CODE_FOR_nothing; - } - } - - *pmode = mode; - *picode = icode; - *ptertiary_class = t_class; - *ptertiary_mode = t_mode; - *ptertiary_icode = t_icode; - - return class; -} -#endif /* HAVE_SECONDARY_RELOADS */ - -#ifdef SECONDARY_MEMORY_NEEDED - -/* Return a memory location that will be used to copy X in mode MODE. - If we haven't already made a location for this mode in this insn, - call find_reloads_address on the location being returned. */ - -rtx -get_secondary_mem (x, mode, opnum, type) - rtx x; - enum machine_mode mode; - int opnum; - enum reload_type type; -{ - rtx loc; - int mem_valid; - - /* If MODE is narrower than a word, widen it. This is required because - most machines that require these memory locations do not support - short load and stores from all registers (e.g., FP registers). We could - possibly conditionalize this, but we lose nothing by doing the wider - mode. */ - - if (GET_MODE_BITSIZE (mode) < BITS_PER_WORD) - mode = mode_for_size (BITS_PER_WORD, GET_MODE_CLASS (mode), 0); - - /* If we already have made a MEM for this operand in MODE, return it. */ - if (secondary_memlocs_elim[(int) mode][opnum] != 0) - return secondary_memlocs_elim[(int) mode][opnum]; - - /* If this is the first time we've tried to get a MEM for this mode, - allocate a new one. `something_changed' in reload will get set - by noticing that the frame size has changed. */ - - if (secondary_memlocs[(int) mode] == 0) - { -#ifdef SECONDARY_MEMORY_NEEDED_RTX - secondary_memlocs[(int) mode] = SECONDARY_MEMORY_NEEDED_RTX (mode); -#else - secondary_memlocs[(int) mode] - = assign_stack_local (mode, GET_MODE_SIZE (mode), 0); -#endif - } - - /* Get a version of the address doing any eliminations needed. If that - didn't give us a new MEM, make a new one if it isn't valid. */ - - loc = eliminate_regs (secondary_memlocs[(int) mode], VOIDmode, NULL_RTX); - mem_valid = strict_memory_address_p (mode, XEXP (loc, 0)); - - if (! mem_valid && loc == secondary_memlocs[(int) mode]) - loc = copy_rtx (loc); - - /* The only time the call below will do anything is if the stack - offset is too large. In that case IND_LEVELS doesn't matter, so we - can just pass a zero. Adjust the type to be the address of the - corresponding object. If the address was valid, save the eliminated - address. If it wasn't valid, we need to make a reload each time, so - don't save it. */ - - if (! mem_valid) - { - type = (type == RELOAD_FOR_INPUT ? RELOAD_FOR_INPUT_ADDRESS - : type == RELOAD_FOR_OUTPUT ? RELOAD_FOR_OUTPUT_ADDRESS - : RELOAD_OTHER); - - find_reloads_address (mode, NULL_PTR, XEXP (loc, 0), &XEXP (loc, 0), - opnum, type, 0); - } - - secondary_memlocs_elim[(int) mode][opnum] = loc; - return loc; -} - -/* Clear any secondary memory locations we've made. */ - -void -clear_secondary_mem () -{ - bzero (secondary_memlocs, sizeof secondary_memlocs); -} -#endif /* SECONDARY_MEMORY_NEEDED */ - -/* Record one reload that needs to be performed. - IN is an rtx saying where the data are to be found before this instruction. - OUT says where they must be stored after the instruction. - (IN is zero for data not read, and OUT is zero for data not written.) - INLOC and OUTLOC point to the places in the instructions where - IN and OUT were found. - If IN and OUT are both non-zero, it means the same register must be used - to reload both IN and OUT. - - CLASS is a register class required for the reloaded data. - INMODE is the machine mode that the instruction requires - for the reg that replaces IN and OUTMODE is likewise for OUT. - - If IN is zero, then OUT's location and mode should be passed as - INLOC and INMODE. - - STRICT_LOW is the 1 if there is a containing STRICT_LOW_PART rtx. - - OPTIONAL nonzero means this reload does not need to be performed: - it can be discarded if that is more convenient. - - OPNUM and TYPE say what the purpose of this reload is. - - The return value is the reload-number for this reload. - - If both IN and OUT are nonzero, in some rare cases we might - want to make two separate reloads. (Actually we never do this now.) - Therefore, the reload-number for OUT is stored in - output_reloadnum when we return; the return value applies to IN. - Usually (presently always), when IN and OUT are nonzero, - the two reload-numbers are equal, but the caller should be careful to - distinguish them. */ - -static int -push_reload (in, out, inloc, outloc, class, - inmode, outmode, strict_low, optional, opnum, type) - register rtx in, out; - rtx *inloc, *outloc; - enum reg_class class; - enum machine_mode inmode, outmode; - int strict_low; - int optional; - int opnum; - enum reload_type type; -{ - register int i; - int dont_share = 0; - rtx *in_subreg_loc = 0, *out_subreg_loc = 0; - int secondary_reload = -1; - enum insn_code secondary_icode = CODE_FOR_nothing; - - /* Compare two RTX's. */ -#define MATCHES(x, y) \ - (x == y || (x != 0 && (GET_CODE (x) == REG \ - ? GET_CODE (y) == REG && REGNO (x) == REGNO (y) \ - : rtx_equal_p (x, y) && ! side_effects_p (x)))) - - /* Indicates if two reloads purposes are for similar enough things that we - can merge their reloads. */ -#define MERGABLE_RELOADS(when1, when2, op1, op2) \ - ((when1) == RELOAD_OTHER || (when2) == RELOAD_OTHER \ - || ((when1) == (when2) && (op1) == (op2)) \ - || ((when1) == RELOAD_FOR_INPUT && (when2) == RELOAD_FOR_INPUT) \ - || ((when1) == RELOAD_FOR_OPERAND_ADDRESS \ - && (when2) == RELOAD_FOR_OPERAND_ADDRESS) \ - || ((when1) == RELOAD_FOR_OTHER_ADDRESS \ - && (when2) == RELOAD_FOR_OTHER_ADDRESS)) - - /* Nonzero if these two reload purposes produce RELOAD_OTHER when merged. */ -#define MERGE_TO_OTHER(when1, when2, op1, op2) \ - ((when1) != (when2) \ - || ! ((op1) == (op2) \ - || (when1) == RELOAD_FOR_INPUT \ - || (when1) == RELOAD_FOR_OPERAND_ADDRESS \ - || (when1) == RELOAD_FOR_OTHER_ADDRESS)) - - /* INMODE and/or OUTMODE could be VOIDmode if no mode - has been specified for the operand. In that case, - use the operand's mode as the mode to reload. */ - if (inmode == VOIDmode && in != 0) - inmode = GET_MODE (in); - if (outmode == VOIDmode && out != 0) - outmode = GET_MODE (out); - - /* If IN is a pseudo register everywhere-equivalent to a constant, and - it is not in a hard register, reload straight from the constant, - since we want to get rid of such pseudo registers. - Often this is done earlier, but not always in find_reloads_address. */ - if (in != 0 && GET_CODE (in) == REG) - { - register int regno = REGNO (in); - - if (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0 - && reg_equiv_constant[regno] != 0) - in = reg_equiv_constant[regno]; - } - - /* Likewise for OUT. Of course, OUT will never be equivalent to - an actual constant, but it might be equivalent to a memory location - (in the case of a parameter). */ - if (out != 0 && GET_CODE (out) == REG) - { - register int regno = REGNO (out); - - if (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0 - && reg_equiv_constant[regno] != 0) - out = reg_equiv_constant[regno]; - } - - /* If we have a read-write operand with an address side-effect, - change either IN or OUT so the side-effect happens only once. */ - if (in != 0 && out != 0 && GET_CODE (in) == MEM && rtx_equal_p (in, out)) - { - if (GET_CODE (XEXP (in, 0)) == POST_INC - || GET_CODE (XEXP (in, 0)) == POST_DEC) - in = gen_rtx (MEM, GET_MODE (in), XEXP (XEXP (in, 0), 0)); - if (GET_CODE (XEXP (in, 0)) == PRE_INC - || GET_CODE (XEXP (in, 0)) == PRE_DEC) - out = gen_rtx (MEM, GET_MODE (out), XEXP (XEXP (out, 0), 0)); - } - - /* If we are reloading a (SUBREG (MEM ...) ...) or (SUBREG constant ...), - really reload just the inside expression in its own mode. - If we have (SUBREG:M1 (REG:M2 ...) ...) with M1 wider than M2 and the - register is a pseudo, this will become the same as the above case. - Similar issue for (SUBREG:M1 (REG:M2 ...) ...) for a hard register R where - either M1 is not valid for R or M2 is wider than a word but we only - need one word to store an M2-sized quantity in R. - (However, if OUT is nonzero, we need to reload the reg *and* - the subreg, so do nothing here, and let following statement handle it.) - - Note that the case of (SUBREG (CONST_INT...)...) is handled elsewhere; - we can't handle it here because CONST_INT does not indicate a mode. - - Similarly, we must reload the inside expression if we have a - STRICT_LOW_PART (presumably, in == out in the cas). - - Also reload the inner expression if it does not require a secondary - reload but the SUBREG does. */ - - if (in != 0 && GET_CODE (in) == SUBREG - && (GET_CODE (SUBREG_REG (in)) != REG - || strict_low - || (GET_CODE (SUBREG_REG (in)) == REG - && REGNO (SUBREG_REG (in)) >= FIRST_PSEUDO_REGISTER - && (GET_MODE_SIZE (inmode) - > GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))))) - || (REGNO (SUBREG_REG (in)) < FIRST_PSEUDO_REGISTER - /* The case where out is nonzero - is handled differently in the following statement. */ - && (out == 0 || SUBREG_WORD (in) == 0) - && (! HARD_REGNO_MODE_OK (REGNO (SUBREG_REG (in)), inmode) - || (GET_MODE_SIZE (inmode) <= UNITS_PER_WORD - && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))) - > UNITS_PER_WORD) - && ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))) - / UNITS_PER_WORD) - != HARD_REGNO_NREGS (REGNO (SUBREG_REG (in)), - GET_MODE (SUBREG_REG (in))))))) -#ifdef SECONDARY_INPUT_RELOAD_CLASS - || (SECONDARY_INPUT_RELOAD_CLASS (class, inmode, in) != NO_REGS - && (SECONDARY_INPUT_RELOAD_CLASS (class, - GET_MODE (SUBREG_REG (in)), - SUBREG_REG (in)) - == NO_REGS)) -#endif - )) - { - in_subreg_loc = inloc; - inloc = &SUBREG_REG (in); - in = *inloc; -#if ! defined(BYTE_LOADS_ZERO_EXTEND) && ! defined(BYTE_LOADS_SIGN_EXTEND) - if (GET_CODE (in) == MEM) - /* This is supposed to happen only for paradoxical subregs made by - combine.c. (SUBREG (MEM)) isn't supposed to occur other ways. */ - if (GET_MODE_SIZE (GET_MODE (in)) > GET_MODE_SIZE (inmode)) - abort (); -#endif - inmode = GET_MODE (in); - } - - /* Similar issue for (SUBREG:M1 (REG:M2 ...) ...) for a hard register R where - either M1 is not valid for R or M2 is wider than a word but we only - need one word to store an M2-sized quantity in R. - - However, we must reload the inner reg *as well as* the subreg in - that case. */ - - if (in != 0 && GET_CODE (in) == SUBREG - && GET_CODE (SUBREG_REG (in)) == REG - && REGNO (SUBREG_REG (in)) < FIRST_PSEUDO_REGISTER - && (! HARD_REGNO_MODE_OK (REGNO (SUBREG_REG (in)), inmode) - || (GET_MODE_SIZE (inmode) <= UNITS_PER_WORD - && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))) - > UNITS_PER_WORD) - && ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))) - / UNITS_PER_WORD) - != HARD_REGNO_NREGS (REGNO (SUBREG_REG (in)), - GET_MODE (SUBREG_REG (in))))))) - { - push_reload (SUBREG_REG (in), NULL_RTX, &SUBREG_REG (in), NULL_PTR, - GENERAL_REGS, VOIDmode, VOIDmode, 0, 0, opnum, type); - } - - - /* Similarly for paradoxical and problematical SUBREGs on the output. - Note that there is no reason we need worry about the previous value - of SUBREG_REG (out); even if wider than out, - storing in a subreg is entitled to clobber it all - (except in the case of STRICT_LOW_PART, - and in that case the constraint should label it input-output.) */ - if (out != 0 && GET_CODE (out) == SUBREG - && (GET_CODE (SUBREG_REG (out)) != REG - || strict_low - || (GET_CODE (SUBREG_REG (out)) == REG - && REGNO (SUBREG_REG (out)) >= FIRST_PSEUDO_REGISTER - && (GET_MODE_SIZE (outmode) - > GET_MODE_SIZE (GET_MODE (SUBREG_REG (out))))) - || (GET_CODE (SUBREG_REG (out)) == REG - && REGNO (SUBREG_REG (out)) < FIRST_PSEUDO_REGISTER - && (! HARD_REGNO_MODE_OK (REGNO (SUBREG_REG (out)), outmode) - || (GET_MODE_SIZE (outmode) <= UNITS_PER_WORD - && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (out))) - > UNITS_PER_WORD) - && ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (out))) - / UNITS_PER_WORD) - != HARD_REGNO_NREGS (REGNO (SUBREG_REG (out)), - GET_MODE (SUBREG_REG (out))))))) -#ifdef SECONDARY_OUTPUT_RELOAD_CLASS - || (SECONDARY_OUTPUT_RELOAD_CLASS (class, outmode, out) != NO_REGS - && (SECONDARY_OUTPUT_RELOAD_CLASS (class, - GET_MODE (SUBREG_REG (out)), - SUBREG_REG (out)) - == NO_REGS)) -#endif - )) - { - out_subreg_loc = outloc; - outloc = &SUBREG_REG (out); - out = *outloc; -#if ! defined(BYTE_LOADS_ZERO_EXTEND) && ! defined(BYTE_LOADS_SIGN_EXTEND) - if (GET_CODE (out) == MEM - && GET_MODE_SIZE (GET_MODE (out)) > GET_MODE_SIZE (outmode)) - abort (); -#endif - outmode = GET_MODE (out); - } - - /* If IN appears in OUT, we can't share any input-only reload for IN. */ - if (in != 0 && out != 0 && GET_CODE (out) == MEM - && (GET_CODE (in) == REG || GET_CODE (in) == MEM) - && reg_overlap_mentioned_for_reload_p (in, XEXP (out, 0))) - dont_share = 1; - - /* If IN is a SUBREG of a hard register, make a new REG. This - simplifies some of the cases below. */ - - if (in != 0 && GET_CODE (in) == SUBREG && GET_CODE (SUBREG_REG (in)) == REG - && REGNO (SUBREG_REG (in)) < FIRST_PSEUDO_REGISTER) - in = gen_rtx (REG, GET_MODE (in), - REGNO (SUBREG_REG (in)) + SUBREG_WORD (in)); - - /* Similarly for OUT. */ - if (out != 0 && GET_CODE (out) == SUBREG - && GET_CODE (SUBREG_REG (out)) == REG - && REGNO (SUBREG_REG (out)) < FIRST_PSEUDO_REGISTER) - out = gen_rtx (REG, GET_MODE (out), - REGNO (SUBREG_REG (out)) + SUBREG_WORD (out)); - - /* Narrow down the class of register wanted if that is - desirable on this machine for efficiency. */ - if (in != 0) - class = PREFERRED_RELOAD_CLASS (in, class); - - /* Output reloads may need analogous treatment, different in detail. */ -#ifdef PREFERRED_OUTPUT_RELOAD_CLASS - if (out != 0) - class = PREFERRED_OUTPUT_RELOAD_CLASS (out, class); -#endif - - /* Make sure we use a class that can handle the actual pseudo - inside any subreg. For example, on the 386, QImode regs - can appear within SImode subregs. Although GENERAL_REGS - can handle SImode, QImode needs a smaller class. */ -#ifdef LIMIT_RELOAD_CLASS - if (in_subreg_loc) - class = LIMIT_RELOAD_CLASS (inmode, class); - else if (in != 0 && GET_CODE (in) == SUBREG) - class = LIMIT_RELOAD_CLASS (GET_MODE (SUBREG_REG (in)), class); - - if (out_subreg_loc) - class = LIMIT_RELOAD_CLASS (outmode, class); - if (out != 0 && GET_CODE (out) == SUBREG) - class = LIMIT_RELOAD_CLASS (GET_MODE (SUBREG_REG (out)), class); -#endif - - /* Verify that this class is at least possible for the mode that - is specified. */ - if (this_insn_is_asm) - { - enum machine_mode mode; - if (GET_MODE_SIZE (inmode) > GET_MODE_SIZE (outmode)) - mode = inmode; - else - mode = outmode; - if (mode == VOIDmode) - { - error_for_asm (this_insn, "cannot reload integer constant operand in `asm'"); - mode = word_mode; - if (in != 0) - inmode = word_mode; - if (out != 0) - outmode = word_mode; - } - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (HARD_REGNO_MODE_OK (i, mode) - && TEST_HARD_REG_BIT (reg_class_contents[(int) class], i)) - { - int nregs = HARD_REGNO_NREGS (i, mode); - - int j; - for (j = 1; j < nregs; j++) - if (! TEST_HARD_REG_BIT (reg_class_contents[(int) class], i + j)) - break; - if (j == nregs) - break; - } - if (i == FIRST_PSEUDO_REGISTER) - { - error_for_asm (this_insn, "impossible register constraint in `asm'"); - class = ALL_REGS; - } - } - - if (class == NO_REGS) - abort (); - - /* We can use an existing reload if the class is right - and at least one of IN and OUT is a match - and the other is at worst neutral. - (A zero compared against anything is neutral.) - - If SMALL_REGISTER_CLASSES, don't use existing reloads unless they are - for the same thing since that can cause us to need more reload registers - than we otherwise would. */ - - for (i = 0; i < n_reloads; i++) - if ((reg_class_subset_p (class, reload_reg_class[i]) - || reg_class_subset_p (reload_reg_class[i], class)) - /* If the existing reload has a register, it must fit our class. */ - && (reload_reg_rtx[i] == 0 - || TEST_HARD_REG_BIT (reg_class_contents[(int) class], - true_regnum (reload_reg_rtx[i]))) - && ((in != 0 && MATCHES (reload_in[i], in) && ! dont_share - && (out == 0 || reload_out[i] == 0 || MATCHES (reload_out[i], out))) - || - (out != 0 && MATCHES (reload_out[i], out) - && (in == 0 || reload_in[i] == 0 || MATCHES (reload_in[i], in)))) - && (reg_class_size[(int) class] == 1 -#ifdef SMALL_REGISTER_CLASSES - || 1 -#endif - ) - && MERGABLE_RELOADS (type, reload_when_needed[i], - opnum, reload_opnum[i])) - break; - - /* Reloading a plain reg for input can match a reload to postincrement - that reg, since the postincrement's value is the right value. - Likewise, it can match a preincrement reload, since we regard - the preincrementation as happening before any ref in this insn - to that register. */ - if (i == n_reloads) - for (i = 0; i < n_reloads; i++) - if ((reg_class_subset_p (class, reload_reg_class[i]) - || reg_class_subset_p (reload_reg_class[i], class)) - /* If the existing reload has a register, it must fit our class. */ - && (reload_reg_rtx[i] == 0 - || TEST_HARD_REG_BIT (reg_class_contents[(int) class], - true_regnum (reload_reg_rtx[i]))) - && out == 0 && reload_out[i] == 0 && reload_in[i] != 0 - && ((GET_CODE (in) == REG - && (GET_CODE (reload_in[i]) == POST_INC - || GET_CODE (reload_in[i]) == POST_DEC - || GET_CODE (reload_in[i]) == PRE_INC - || GET_CODE (reload_in[i]) == PRE_DEC) - && MATCHES (XEXP (reload_in[i], 0), in)) - || - (GET_CODE (reload_in[i]) == REG - && (GET_CODE (in) == POST_INC - || GET_CODE (in) == POST_DEC - || GET_CODE (in) == PRE_INC - || GET_CODE (in) == PRE_DEC) - && MATCHES (XEXP (in, 0), reload_in[i]))) - && (reg_class_size[(int) class] == 1 -#ifdef SMALL_REGISTER_CLASSES - || 1 -#endif - ) - && MERGABLE_RELOADS (type, reload_when_needed[i], - opnum, reload_opnum[i])) - { - /* Make sure reload_in ultimately has the increment, - not the plain register. */ - if (GET_CODE (in) == REG) - in = reload_in[i]; - break; - } - - if (i == n_reloads) - { -#ifdef HAVE_SECONDARY_RELOADS - enum reg_class secondary_class = NO_REGS; - enum reg_class secondary_out_class = NO_REGS; - enum machine_mode secondary_mode = inmode; - enum machine_mode secondary_out_mode = outmode; - enum insn_code secondary_icode; - enum insn_code secondary_out_icode = CODE_FOR_nothing; - enum reg_class tertiary_class = NO_REGS; - enum reg_class tertiary_out_class = NO_REGS; - enum machine_mode tertiary_mode; - enum machine_mode tertiary_out_mode; - enum insn_code tertiary_icode; - enum insn_code tertiary_out_icode = CODE_FOR_nothing; - int tertiary_reload = -1; - - /* See if we need a secondary reload register to move between - CLASS and IN or CLASS and OUT. Get the modes and icodes to - use for each of them if so. */ - -#ifdef SECONDARY_INPUT_RELOAD_CLASS - if (in != 0) - secondary_class - = find_secondary_reload (in, class, inmode, 1, &secondary_icode, - &secondary_mode, &tertiary_class, - &tertiary_icode, &tertiary_mode); -#endif - -#ifdef SECONDARY_OUTPUT_RELOAD_CLASS - if (out != 0 && GET_CODE (out) != SCRATCH) - secondary_out_class - = find_secondary_reload (out, class, outmode, 0, - &secondary_out_icode, &secondary_out_mode, - &tertiary_out_class, &tertiary_out_icode, - &tertiary_out_mode); -#endif - - /* We can only record one secondary and one tertiary reload. If both - IN and OUT need secondary reloads, we can only make an in-out - reload if neither need an insn and if the classes are compatible. - If they aren't, all we can do is abort since making two separate - reloads is invalid. */ - - if (secondary_class != NO_REGS && secondary_out_class != NO_REGS - && reg_class_subset_p (secondary_out_class, secondary_class)) - secondary_class = secondary_out_class; - - if (secondary_class != NO_REGS && secondary_out_class != NO_REGS - && (! reg_class_subset_p (secondary_class, secondary_out_class) - || secondary_icode != CODE_FOR_nothing - || secondary_out_icode != CODE_FOR_nothing)) - abort (); - - /* If we need a secondary reload for OUT but not IN, copy the - information. */ - if (secondary_class == NO_REGS && secondary_out_class != NO_REGS) - { - secondary_class = secondary_out_class; - secondary_icode = secondary_out_icode; - tertiary_class = tertiary_out_class; - tertiary_icode = tertiary_out_icode; - tertiary_mode = tertiary_out_mode; - } - - if (secondary_class != NO_REGS) - { - /* Secondary reloads don't conflict as badly as the primary object - being reload. Specifically, we can always treat them as - being for an input or output address and hence allowed to be - reused in the same manner such address components could be - reused. This is used as the reload_type for our secondary - reloads. */ - - enum reload_type secondary_type - = (type == RELOAD_FOR_INPUT ? RELOAD_FOR_INPUT_ADDRESS - : type == RELOAD_FOR_OUTPUT ? RELOAD_FOR_OUTPUT_ADDRESS - : type); - - /* If we need a tertiary reload, see if we have one we can reuse - or else make one. */ - - if (tertiary_class != NO_REGS) - { - for (tertiary_reload = 0; tertiary_reload < n_reloads; - tertiary_reload++) - if (reload_secondary_p[tertiary_reload] - && (reg_class_subset_p (tertiary_class, - reload_reg_class[tertiary_reload]) - || reg_class_subset_p (reload_reg_class[tertiary_reload], - tertiary_class)) - && ((reload_inmode[tertiary_reload] == tertiary_mode) - || reload_inmode[tertiary_reload] == VOIDmode) - && ((reload_outmode[tertiary_reload] == tertiary_mode) - || reload_outmode[tertiary_reload] == VOIDmode) - && (reload_secondary_icode[tertiary_reload] - == CODE_FOR_nothing) - && (reg_class_size[(int) tertiary_class] == 1 -#ifdef SMALL_REGISTER_CLASSES - || 1 -#endif - ) - && MERGABLE_RELOADS (secondary_type, - reload_when_needed[tertiary_reload], - opnum, reload_opnum[tertiary_reload])) - { - if (tertiary_mode != VOIDmode) - reload_inmode[tertiary_reload] = tertiary_mode; - if (tertiary_out_mode != VOIDmode) - reload_outmode[tertiary_reload] = tertiary_mode; - if (reg_class_subset_p (tertiary_class, - reload_reg_class[tertiary_reload])) - reload_reg_class[tertiary_reload] = tertiary_class; - if (MERGE_TO_OTHER (secondary_type, - reload_when_needed[tertiary_reload], - opnum, - reload_opnum[tertiary_reload])) - reload_when_needed[tertiary_reload] = RELOAD_OTHER; - reload_opnum[tertiary_reload] - = MIN (reload_opnum[tertiary_reload], opnum); - reload_optional[tertiary_reload] &= optional; - reload_secondary_p[tertiary_reload] = 1; - } - - if (tertiary_reload == n_reloads) - { - /* We need to make a new tertiary reload for this register - class. */ - reload_in[tertiary_reload] = reload_out[tertiary_reload] = 0; - reload_reg_class[tertiary_reload] = tertiary_class; - reload_inmode[tertiary_reload] = tertiary_mode; - reload_outmode[tertiary_reload] = tertiary_mode; - reload_reg_rtx[tertiary_reload] = 0; - reload_optional[tertiary_reload] = optional; - reload_inc[tertiary_reload] = 0; - /* Maybe we could combine these, but it seems too tricky. */ - reload_nocombine[tertiary_reload] = 1; - reload_in_reg[tertiary_reload] = 0; - reload_opnum[tertiary_reload] = opnum; - reload_when_needed[tertiary_reload] = secondary_type; - reload_secondary_reload[tertiary_reload] = -1; - reload_secondary_icode[tertiary_reload] = CODE_FOR_nothing; - reload_secondary_p[tertiary_reload] = 1; - - n_reloads++; - i = n_reloads; - } - } - - /* See if we can reuse an existing secondary reload. */ - for (secondary_reload = 0; secondary_reload < n_reloads; - secondary_reload++) - if (reload_secondary_p[secondary_reload] - && (reg_class_subset_p (secondary_class, - reload_reg_class[secondary_reload]) - || reg_class_subset_p (reload_reg_class[secondary_reload], - secondary_class)) - && ((reload_inmode[secondary_reload] == secondary_mode) - || reload_inmode[secondary_reload] == VOIDmode) - && ((reload_outmode[secondary_reload] == secondary_out_mode) - || reload_outmode[secondary_reload] == VOIDmode) - && reload_secondary_reload[secondary_reload] == tertiary_reload - && reload_secondary_icode[secondary_reload] == tertiary_icode - && (reg_class_size[(int) secondary_class] == 1 -#ifdef SMALL_REGISTER_CLASSES - || 1 -#endif - ) - && MERGABLE_RELOADS (secondary_type, - reload_when_needed[secondary_reload], - opnum, reload_opnum[secondary_reload])) - { - if (secondary_mode != VOIDmode) - reload_inmode[secondary_reload] = secondary_mode; - if (secondary_out_mode != VOIDmode) - reload_outmode[secondary_reload] = secondary_out_mode; - if (reg_class_subset_p (secondary_class, - reload_reg_class[secondary_reload])) - reload_reg_class[secondary_reload] = secondary_class; - if (MERGE_TO_OTHER (secondary_type, - reload_when_needed[secondary_reload], - opnum, reload_opnum[secondary_reload])) - reload_when_needed[secondary_reload] = RELOAD_OTHER; - reload_opnum[secondary_reload] - = MIN (reload_opnum[secondary_reload], opnum); - reload_optional[secondary_reload] &= optional; - reload_secondary_p[secondary_reload] = 1; - } - - if (secondary_reload == n_reloads) - { - /* We need to make a new secondary reload for this register - class. */ - reload_in[secondary_reload] = reload_out[secondary_reload] = 0; - reload_reg_class[secondary_reload] = secondary_class; - reload_inmode[secondary_reload] = secondary_mode; - reload_outmode[secondary_reload] = secondary_out_mode; - reload_reg_rtx[secondary_reload] = 0; - reload_optional[secondary_reload] = optional; - reload_inc[secondary_reload] = 0; - /* Maybe we could combine these, but it seems too tricky. */ - reload_nocombine[secondary_reload] = 1; - reload_in_reg[secondary_reload] = 0; - reload_opnum[secondary_reload] = opnum; - reload_when_needed[secondary_reload] = secondary_type; - reload_secondary_reload[secondary_reload] = tertiary_reload; - reload_secondary_icode[secondary_reload] = tertiary_icode; - reload_secondary_p[secondary_reload] = 1; - - n_reloads++; - i = n_reloads; - -#ifdef SECONDARY_MEMORY_NEEDED - /* If we need a memory location to copy between the two - reload regs, set it up now. */ - - if (in != 0 && secondary_icode == CODE_FOR_nothing - && SECONDARY_MEMORY_NEEDED (secondary_class, class, inmode)) - get_secondary_mem (in, inmode, opnum, type); - - if (out != 0 && secondary_icode == CODE_FOR_nothing - && SECONDARY_MEMORY_NEEDED (class, secondary_class, outmode)) - get_secondary_mem (out, outmode, opnum, type); -#endif - } - } -#endif - - /* We found no existing reload suitable for re-use. - So add an additional reload. */ - - reload_in[i] = in; - reload_out[i] = out; - reload_reg_class[i] = class; - reload_inmode[i] = inmode; - reload_outmode[i] = outmode; - reload_reg_rtx[i] = 0; - reload_optional[i] = optional; - reload_inc[i] = 0; - reload_nocombine[i] = 0; - reload_in_reg[i] = inloc ? *inloc : 0; - reload_opnum[i] = opnum; - reload_when_needed[i] = type; - reload_secondary_reload[i] = secondary_reload; - reload_secondary_icode[i] = secondary_icode; - reload_secondary_p[i] = 0; - - n_reloads++; - -#ifdef SECONDARY_MEMORY_NEEDED - /* If a memory location is needed for the copy, make one. */ - if (in != 0 && GET_CODE (in) == REG - && REGNO (in) < FIRST_PSEUDO_REGISTER - && SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (REGNO (in)), - class, inmode)) - get_secondary_mem (in, inmode, opnum, type); - - if (out != 0 && GET_CODE (out) == REG - && REGNO (out) < FIRST_PSEUDO_REGISTER - && SECONDARY_MEMORY_NEEDED (class, REGNO_REG_CLASS (REGNO (out)), - outmode)) - get_secondary_mem (out, outmode, opnum, type); -#endif - } - else - { - /* We are reusing an existing reload, - but we may have additional information for it. - For example, we may now have both IN and OUT - while the old one may have just one of them. */ - - if (inmode != VOIDmode) - reload_inmode[i] = inmode; - if (outmode != VOIDmode) - reload_outmode[i] = outmode; - if (in != 0) - reload_in[i] = in; - if (out != 0) - reload_out[i] = out; - if (reg_class_subset_p (class, reload_reg_class[i])) - reload_reg_class[i] = class; - reload_optional[i] &= optional; - if (MERGE_TO_OTHER (type, reload_when_needed[i], - opnum, reload_opnum[i])) - reload_when_needed[i] = RELOAD_OTHER; - reload_opnum[i] = MIN (reload_opnum[i], opnum); - } - - /* If the ostensible rtx being reload differs from the rtx found - in the location to substitute, this reload is not safe to combine - because we cannot reliably tell whether it appears in the insn. */ - - if (in != 0 && in != *inloc) - reload_nocombine[i] = 1; - -#if 0 - /* This was replaced by changes in find_reloads_address_1 and the new - function inc_for_reload, which go with a new meaning of reload_inc. */ - - /* If this is an IN/OUT reload in an insn that sets the CC, - it must be for an autoincrement. It doesn't work to store - the incremented value after the insn because that would clobber the CC. - So we must do the increment of the value reloaded from, - increment it, store it back, then decrement again. */ - if (out != 0 && sets_cc0_p (PATTERN (this_insn))) - { - out = 0; - reload_out[i] = 0; - reload_inc[i] = find_inc_amount (PATTERN (this_insn), in); - /* If we did not find a nonzero amount-to-increment-by, - that contradicts the belief that IN is being incremented - in an address in this insn. */ - if (reload_inc[i] == 0) - abort (); - } -#endif - - /* If we will replace IN and OUT with the reload-reg, - record where they are located so that substitution need - not do a tree walk. */ - - if (replace_reloads) - { - if (inloc != 0) - { - register struct replacement *r = &replacements[n_replacements++]; - r->what = i; - r->subreg_loc = in_subreg_loc; - r->where = inloc; - r->mode = inmode; - } - if (outloc != 0 && outloc != inloc) - { - register struct replacement *r = &replacements[n_replacements++]; - r->what = i; - r->where = outloc; - r->subreg_loc = out_subreg_loc; - r->mode = outmode; - } - } - - /* If this reload is just being introduced and it has both - an incoming quantity and an outgoing quantity that are - supposed to be made to match, see if either one of the two - can serve as the place to reload into. - - If one of them is acceptable, set reload_reg_rtx[i] - to that one. */ - - if (in != 0 && out != 0 && in != out && reload_reg_rtx[i] == 0) - { - reload_reg_rtx[i] = find_dummy_reload (in, out, inloc, outloc, - reload_reg_class[i], i); - - /* If the outgoing register already contains the same value - as the incoming one, we can dispense with loading it. - The easiest way to tell the caller that is to give a phony - value for the incoming operand (same as outgoing one). */ - if (reload_reg_rtx[i] == out - && (GET_CODE (in) == REG || CONSTANT_P (in)) - && 0 != find_equiv_reg (in, this_insn, 0, REGNO (out), - static_reload_reg_p, i, inmode)) - reload_in[i] = out; - } - - /* If this is an input reload and the operand contains a register that - dies in this insn and is used nowhere else, see if it is the right class - to be used for this reload. Use it if so. (This occurs most commonly - in the case of paradoxical SUBREGs and in-out reloads). We cannot do - this if it is also an output reload that mentions the register unless - the output is a SUBREG that clobbers an entire register. - - Note that the operand might be one of the spill regs, if it is a - pseudo reg and we are in a block where spilling has not taken place. - But if there is no spilling in this block, that is OK. - An explicitly used hard reg cannot be a spill reg. */ - - if (reload_reg_rtx[i] == 0 && in != 0) - { - rtx note; - int regno; - - for (note = REG_NOTES (this_insn); note; note = XEXP (note, 1)) - if (REG_NOTE_KIND (note) == REG_DEAD - && GET_CODE (XEXP (note, 0)) == REG - && (regno = REGNO (XEXP (note, 0))) < FIRST_PSEUDO_REGISTER - && reg_mentioned_p (XEXP (note, 0), in) - && ! refers_to_regno_for_reload_p (regno, - (regno - + HARD_REGNO_NREGS (regno, - inmode)), - PATTERN (this_insn), inloc) - /* If this is also an output reload, IN cannot be used as - the reload register if it is set in this insn unless IN - is also OUT. */ - && (out == 0 || in == out - || ! hard_reg_set_here_p (regno, - (regno - + HARD_REGNO_NREGS (regno, - inmode)), - PATTERN (this_insn))) - /* ??? Why is this code so different from the previous? - Is there any simple coherent way to describe the two together? - What's going on here. */ - && (in != out - || (GET_CODE (in) == SUBREG - && (((GET_MODE_SIZE (GET_MODE (in)) + (UNITS_PER_WORD - 1)) - / UNITS_PER_WORD) - == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))) - + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)))) - /* Make sure the operand fits in the reg that dies. */ - && GET_MODE_SIZE (inmode) <= GET_MODE_SIZE (GET_MODE (XEXP (note, 0))) - && HARD_REGNO_MODE_OK (regno, inmode) - && GET_MODE_SIZE (outmode) <= GET_MODE_SIZE (GET_MODE (XEXP (note, 0))) - && HARD_REGNO_MODE_OK (regno, outmode) - && TEST_HARD_REG_BIT (reg_class_contents[(int) class], regno) - && !fixed_regs[regno]) - { - reload_reg_rtx[i] = gen_rtx (REG, inmode, regno); - break; - } - } - - if (out) - output_reloadnum = i; - - return i; -} - -/* Record an additional place we must replace a value - for which we have already recorded a reload. - RELOADNUM is the value returned by push_reload - when the reload was recorded. - This is used in insn patterns that use match_dup. */ - -static void -push_replacement (loc, reloadnum, mode) - rtx *loc; - int reloadnum; - enum machine_mode mode; -{ - if (replace_reloads) - { - register struct replacement *r = &replacements[n_replacements++]; - r->what = reloadnum; - r->where = loc; - r->subreg_loc = 0; - r->mode = mode; - } -} - -/* Transfer all replacements that used to be in reload FROM to be in - reload TO. */ - -void -transfer_replacements (to, from) - int to, from; -{ - int i; - - for (i = 0; i < n_replacements; i++) - if (replacements[i].what == from) - replacements[i].what = to; -} - -/* If there is only one output reload, and it is not for an earlyclobber - operand, try to combine it with a (logically unrelated) input reload - to reduce the number of reload registers needed. - - This is safe if the input reload does not appear in - the value being output-reloaded, because this implies - it is not needed any more once the original insn completes. - - If that doesn't work, see we can use any of the registers that - die in this insn as a reload register. We can if it is of the right - class and does not appear in the value being output-reloaded. */ - -static void -combine_reloads () -{ - int i; - int output_reload = -1; - rtx note; - - /* Find the output reload; return unless there is exactly one - and that one is mandatory. */ - - for (i = 0; i < n_reloads; i++) - if (reload_out[i] != 0) - { - if (output_reload >= 0) - return; - output_reload = i; - } - - if (output_reload < 0 || reload_optional[output_reload]) - return; - - /* An input-output reload isn't combinable. */ - - if (reload_in[output_reload] != 0) - return; - - /* If this reload is for an earlyclobber operand, we can't do anything. */ - - for (i = 0; i < n_earlyclobbers; i++) - if (reload_out[output_reload] == reload_earlyclobbers[i]) - return; - - /* Check each input reload; can we combine it? */ - - for (i = 0; i < n_reloads; i++) - if (reload_in[i] && ! reload_optional[i] && ! reload_nocombine[i] - /* Life span of this reload must not extend past main insn. */ - && reload_when_needed[i] != RELOAD_FOR_OUTPUT_ADDRESS - && reload_when_needed[i] != RELOAD_OTHER - && (CLASS_MAX_NREGS (reload_reg_class[i], reload_inmode[i]) - == CLASS_MAX_NREGS (reload_reg_class[output_reload], - reload_outmode[output_reload])) - && reload_inc[i] == 0 - && reload_reg_rtx[i] == 0 - /* Don't combine two reloads with different secondary reloads. */ - && (reload_secondary_reload[i] == reload_secondary_reload[output_reload] - || reload_secondary_reload[i] == -1 - || reload_secondary_reload[output_reload] == -1) -#ifdef SECONDARY_MEMORY_NEEDED - /* Likewise for different secondary memory locations. */ - && (secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[i]] == 0 - || secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[output_reload]] == 0 - || rtx_equal_p (secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[i]], - secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[output_reload]])) -#endif -#ifdef SMALL_REGISTER_CLASSES - && reload_reg_class[i] == reload_reg_class[output_reload] -#else - && (reg_class_subset_p (reload_reg_class[i], - reload_reg_class[output_reload]) - || reg_class_subset_p (reload_reg_class[output_reload], - reload_reg_class[i])) -#endif - && (MATCHES (reload_in[i], reload_out[output_reload]) - /* Args reversed because the first arg seems to be - the one that we imagine being modified - while the second is the one that might be affected. */ - || (! reg_overlap_mentioned_for_reload_p (reload_out[output_reload], - reload_in[i]) - /* However, if the input is a register that appears inside - the output, then we also can't share. - Imagine (set (mem (reg 69)) (plus (reg 69) ...)). - If the same reload reg is used for both reg 69 and the - result to be stored in memory, then that result - will clobber the address of the memory ref. */ - && ! (GET_CODE (reload_in[i]) == REG - && reg_overlap_mentioned_for_reload_p (reload_in[i], - reload_out[output_reload])))) - && (reg_class_size[(int) reload_reg_class[i]] -#ifdef SMALL_REGISTER_CLASSES - || 1 -#endif - ) - /* We will allow making things slightly worse by combining an - input and an output, but no worse than that. */ - && (reload_when_needed[i] == RELOAD_FOR_INPUT - || reload_when_needed[i] == RELOAD_FOR_OUTPUT)) - { - int j; - - /* We have found a reload to combine with! */ - reload_out[i] = reload_out[output_reload]; - reload_outmode[i] = reload_outmode[output_reload]; - /* Mark the old output reload as inoperative. */ - reload_out[output_reload] = 0; - /* The combined reload is needed for the entire insn. */ - reload_when_needed[i] = RELOAD_OTHER; - /* If the output reload had a secondary reload, copy it. */ - if (reload_secondary_reload[output_reload] != -1) - reload_secondary_reload[i] = reload_secondary_reload[output_reload]; -#ifdef SECONDARY_MEMORY_NEEDED - /* Copy any secondary MEM. */ - if (secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[output_reload]] != 0) - secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[i]] - = secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[output_reload]]; -#endif - /* If required, minimize the register class. */ - if (reg_class_subset_p (reload_reg_class[output_reload], - reload_reg_class[i])) - reload_reg_class[i] = reload_reg_class[output_reload]; - - /* Transfer all replacements from the old reload to the combined. */ - for (j = 0; j < n_replacements; j++) - if (replacements[j].what == output_reload) - replacements[j].what = i; - - return; - } - - /* If this insn has only one operand that is modified or written (assumed - to be the first), it must be the one corresponding to this reload. It - is safe to use anything that dies in this insn for that output provided - that it does not occur in the output (we already know it isn't an - earlyclobber. If this is an asm insn, give up. */ - - if (INSN_CODE (this_insn) == -1) - return; - - for (i = 1; i < insn_n_operands[INSN_CODE (this_insn)]; i++) - if (insn_operand_constraint[INSN_CODE (this_insn)][i][0] == '=' - || insn_operand_constraint[INSN_CODE (this_insn)][i][0] == '+') - return; - - /* See if some hard register that dies in this insn and is not used in - the output is the right class. Only works if the register we pick - up can fully hold our output reload. */ - for (note = REG_NOTES (this_insn); note; note = XEXP (note, 1)) - if (REG_NOTE_KIND (note) == REG_DEAD - && GET_CODE (XEXP (note, 0)) == REG - && ! reg_overlap_mentioned_for_reload_p (XEXP (note, 0), - reload_out[output_reload]) - && REGNO (XEXP (note, 0)) < FIRST_PSEUDO_REGISTER - && HARD_REGNO_MODE_OK (REGNO (XEXP (note, 0)), reload_outmode[output_reload]) - && TEST_HARD_REG_BIT (reg_class_contents[(int) reload_reg_class[output_reload]], - REGNO (XEXP (note, 0))) - && (HARD_REGNO_NREGS (REGNO (XEXP (note, 0)), reload_outmode[output_reload]) - <= HARD_REGNO_NREGS (REGNO (XEXP (note, 0)), GET_MODE (XEXP (note, 0)))) - && ! fixed_regs[REGNO (XEXP (note, 0))]) - { - reload_reg_rtx[output_reload] = gen_rtx (REG, - reload_outmode[output_reload], - REGNO (XEXP (note, 0))); - return; - } -} - -/* Try to find a reload register for an in-out reload (expressions IN and OUT). - See if one of IN and OUT is a register that may be used; - this is desirable since a spill-register won't be needed. - If so, return the register rtx that proves acceptable. - - INLOC and OUTLOC are locations where IN and OUT appear in the insn. - CLASS is the register class required for the reload. - - If FOR_REAL is >= 0, it is the number of the reload, - and in some cases when it can be discovered that OUT doesn't need - to be computed, clear out reload_out[FOR_REAL]. - - If FOR_REAL is -1, this should not be done, because this call - is just to see if a register can be found, not to find and install it. */ - -static rtx -find_dummy_reload (real_in, real_out, inloc, outloc, class, for_real) - rtx real_in, real_out; - rtx *inloc, *outloc; - enum reg_class class; - int for_real; -{ - rtx in = real_in; - rtx out = real_out; - int in_offset = 0; - int out_offset = 0; - rtx value = 0; - - /* If operands exceed a word, we can't use either of them - unless they have the same size. */ - if (GET_MODE_SIZE (GET_MODE (real_out)) != GET_MODE_SIZE (GET_MODE (real_in)) - && (GET_MODE_SIZE (GET_MODE (real_out)) > UNITS_PER_WORD - || GET_MODE_SIZE (GET_MODE (real_in)) > UNITS_PER_WORD)) - return 0; - - /* Find the inside of any subregs. */ - while (GET_CODE (out) == SUBREG) - { - out_offset = SUBREG_WORD (out); - out = SUBREG_REG (out); - } - while (GET_CODE (in) == SUBREG) - { - in_offset = SUBREG_WORD (in); - in = SUBREG_REG (in); - } - - /* Narrow down the reg class, the same way push_reload will; - otherwise we might find a dummy now, but push_reload won't. */ - class = PREFERRED_RELOAD_CLASS (in, class); - - /* See if OUT will do. */ - if (GET_CODE (out) == REG - && REGNO (out) < FIRST_PSEUDO_REGISTER) - { - register int regno = REGNO (out) + out_offset; - int nwords = HARD_REGNO_NREGS (regno, GET_MODE (real_out)); - rtx saved_rtx; - - /* When we consider whether the insn uses OUT, - ignore references within IN. They don't prevent us - from copying IN into OUT, because those refs would - move into the insn that reloads IN. - - However, we only ignore IN in its role as this reload. - If the insn uses IN elsewhere and it contains OUT, - that counts. We can't be sure it's the "same" operand - so it might not go through this reload. */ - saved_rtx = *inloc; - *inloc = const0_rtx; - - if (regno < FIRST_PSEUDO_REGISTER - /* A fixed reg that can overlap other regs better not be used - for reloading in any way. */ -#ifdef OVERLAPPING_REGNO_P - && ! (fixed_regs[regno] && OVERLAPPING_REGNO_P (regno)) -#endif - && ! refers_to_regno_for_reload_p (regno, regno + nwords, - PATTERN (this_insn), outloc)) - { - int i; - for (i = 0; i < nwords; i++) - if (! TEST_HARD_REG_BIT (reg_class_contents[(int) class], - regno + i)) - break; - - if (i == nwords) - { - if (GET_CODE (real_out) == REG) - value = real_out; - else - value = gen_rtx (REG, GET_MODE (real_out), regno); - } - } - - *inloc = saved_rtx; - } - - /* Consider using IN if OUT was not acceptable - or if OUT dies in this insn (like the quotient in a divmod insn). - We can't use IN unless it is dies in this insn, - which means we must know accurately which hard regs are live. - Also, the result can't go in IN if IN is used within OUT. */ - if (hard_regs_live_known - && GET_CODE (in) == REG - && REGNO (in) < FIRST_PSEUDO_REGISTER - && (value == 0 - || find_reg_note (this_insn, REG_UNUSED, real_out)) - && find_reg_note (this_insn, REG_DEAD, real_in) - && !fixed_regs[REGNO (in)] - && HARD_REGNO_MODE_OK (REGNO (in), GET_MODE (out))) - { - register int regno = REGNO (in) + in_offset; - int nwords = HARD_REGNO_NREGS (regno, GET_MODE (real_in)); - - if (! refers_to_regno_for_reload_p (regno, regno + nwords, out, NULL_PTR) - && ! hard_reg_set_here_p (regno, regno + nwords, - PATTERN (this_insn))) - { - int i; - for (i = 0; i < nwords; i++) - if (! TEST_HARD_REG_BIT (reg_class_contents[(int) class], - regno + i)) - break; - - if (i == nwords) - { - /* If we were going to use OUT as the reload reg - and changed our mind, it means OUT is a dummy that - dies here. So don't bother copying value to it. */ - if (for_real >= 0 && value == real_out) - reload_out[for_real] = 0; - if (GET_CODE (real_in) == REG) - value = real_in; - else - value = gen_rtx (REG, GET_MODE (real_in), regno); - } - } - } - - return value; -} - -/* This page contains subroutines used mainly for determining - whether the IN or an OUT of a reload can serve as the - reload register. */ - -/* Return 1 if expression X alters a hard reg in the range - from BEG_REGNO (inclusive) to END_REGNO (exclusive), - either explicitly or in the guise of a pseudo-reg allocated to REGNO. - X should be the body of an instruction. */ - -static int -hard_reg_set_here_p (beg_regno, end_regno, x) - register int beg_regno, end_regno; - rtx x; -{ - if (GET_CODE (x) == SET || GET_CODE (x) == CLOBBER) - { - register rtx op0 = SET_DEST (x); - while (GET_CODE (op0) == SUBREG) - op0 = SUBREG_REG (op0); - if (GET_CODE (op0) == REG) - { - register int r = REGNO (op0); - /* See if this reg overlaps range under consideration. */ - if (r < end_regno - && r + HARD_REGNO_NREGS (r, GET_MODE (op0)) > beg_regno) - return 1; - } - } - else if (GET_CODE (x) == PARALLEL) - { - register int i = XVECLEN (x, 0) - 1; - for (; i >= 0; i--) - if (hard_reg_set_here_p (beg_regno, end_regno, XVECEXP (x, 0, i))) - return 1; - } - - return 0; -} - -/* Return 1 if ADDR is a valid memory address for mode MODE, - and check that each pseudo reg has the proper kind of - hard reg. */ - -int -strict_memory_address_p (mode, addr) - enum machine_mode mode; - register rtx addr; -{ - GO_IF_LEGITIMATE_ADDRESS (mode, addr, win); - return 0; - - win: - return 1; -} - -/* Like rtx_equal_p except that it allows a REG and a SUBREG to match - if they are the same hard reg, and has special hacks for - autoincrement and autodecrement. - This is specifically intended for find_reloads to use - in determining whether two operands match. - X is the operand whose number is the lower of the two. - - The value is 2 if Y contains a pre-increment that matches - a non-incrementing address in X. */ - -/* ??? To be completely correct, we should arrange to pass - for X the output operand and for Y the input operand. - For now, we assume that the output operand has the lower number - because that is natural in (SET output (... input ...)). */ - -int -operands_match_p (x, y) - register rtx x, y; -{ - register int i; - register RTX_CODE code = GET_CODE (x); - register char *fmt; - int success_2; - - if (x == y) - return 1; - if ((code == REG || (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG)) - && (GET_CODE (y) == REG || (GET_CODE (y) == SUBREG - && GET_CODE (SUBREG_REG (y)) == REG))) - { - register int j; - - if (code == SUBREG) - { - i = REGNO (SUBREG_REG (x)); - if (i >= FIRST_PSEUDO_REGISTER) - goto slow; - i += SUBREG_WORD (x); - } - else - i = REGNO (x); - - if (GET_CODE (y) == SUBREG) - { - j = REGNO (SUBREG_REG (y)); - if (j >= FIRST_PSEUDO_REGISTER) - goto slow; - j += SUBREG_WORD (y); - } - else - j = REGNO (y); - - /* On a WORDS_BIG_ENDIAN machine, point to the last register of a - multiple hard register group, so that for example (reg:DI 0) and - (reg:SI 1) will be considered the same register. */ - if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD - && i < FIRST_PSEUDO_REGISTER) - i += (GET_MODE_SIZE (GET_MODE (x)) / UNITS_PER_WORD) - 1; - if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (GET_MODE (y)) > UNITS_PER_WORD - && j < FIRST_PSEUDO_REGISTER) - j += (GET_MODE_SIZE (GET_MODE (y)) / UNITS_PER_WORD) - 1; - - return i == j; - } - /* If two operands must match, because they are really a single - operand of an assembler insn, then two postincrements are invalid - because the assembler insn would increment only once. - On the other hand, an postincrement matches ordinary indexing - if the postincrement is the output operand. */ - if (code == POST_DEC || code == POST_INC) - return operands_match_p (XEXP (x, 0), y); - /* Two preincrements are invalid - because the assembler insn would increment only once. - On the other hand, an preincrement matches ordinary indexing - if the preincrement is the input operand. - In this case, return 2, since some callers need to do special - things when this happens. */ - if (GET_CODE (y) == PRE_DEC || GET_CODE (y) == PRE_INC) - return operands_match_p (x, XEXP (y, 0)) ? 2 : 0; - - slow: - - /* Now we have disposed of all the cases - in which different rtx codes can match. */ - if (code != GET_CODE (y)) - return 0; - if (code == LABEL_REF) - return XEXP (x, 0) == XEXP (y, 0); - if (code == SYMBOL_REF) - return XSTR (x, 0) == XSTR (y, 0); - - /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */ - - if (GET_MODE (x) != GET_MODE (y)) - return 0; - - /* Compare the elements. If any pair of corresponding elements - fail to match, return 0 for the whole things. */ - - success_2 = 0; - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - int val; - switch (fmt[i]) - { - case 'w': - if (XWINT (x, i) != XWINT (y, i)) - return 0; - break; - - case 'i': - if (XINT (x, i) != XINT (y, i)) - return 0; - break; - - case 'e': - val = operands_match_p (XEXP (x, i), XEXP (y, i)); - if (val == 0) - return 0; - /* If any subexpression returns 2, - we should return 2 if we are successful. */ - if (val == 2) - success_2 = 1; - break; - - case '0': - break; - - /* It is believed that rtx's at this level will never - contain anything but integers and other rtx's, - except for within LABEL_REFs and SYMBOL_REFs. */ - default: - abort (); - } - } - return 1 + success_2; -} - -/* Return the number of times character C occurs in string S. */ - -int -n_occurrences (c, s) - char c; - char *s; -{ - int n = 0; - while (*s) - n += (*s++ == c); - return n; -} - -/* Describe the range of registers or memory referenced by X. - If X is a register, set REG_FLAG and put the first register - number into START and the last plus one into END. - If X is a memory reference, put a base address into BASE - and a range of integer offsets into START and END. - If X is pushing on the stack, we can assume it causes no trouble, - so we set the SAFE field. */ - -static struct decomposition -decompose (x) - rtx x; -{ - struct decomposition val; - int all_const = 0; - - val.reg_flag = 0; - val.safe = 0; - if (GET_CODE (x) == MEM) - { - rtx base, offset = 0; - rtx addr = XEXP (x, 0); - - if (GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC - || GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC) - { - val.base = XEXP (addr, 0); - val.start = - GET_MODE_SIZE (GET_MODE (x)); - val.end = GET_MODE_SIZE (GET_MODE (x)); - val.safe = REGNO (val.base) == STACK_POINTER_REGNUM; - return val; - } - - if (GET_CODE (addr) == CONST) - { - addr = XEXP (addr, 0); - all_const = 1; - } - if (GET_CODE (addr) == PLUS) - { - if (CONSTANT_P (XEXP (addr, 0))) - { - base = XEXP (addr, 1); - offset = XEXP (addr, 0); - } - else if (CONSTANT_P (XEXP (addr, 1))) - { - base = XEXP (addr, 0); - offset = XEXP (addr, 1); - } - } - - if (offset == 0) - { - base = addr; - offset = const0_rtx; - } - if (GET_CODE (offset) == CONST) - offset = XEXP (offset, 0); - if (GET_CODE (offset) == PLUS) - { - if (GET_CODE (XEXP (offset, 0)) == CONST_INT) - { - base = gen_rtx (PLUS, GET_MODE (base), base, XEXP (offset, 1)); - offset = XEXP (offset, 0); - } - else if (GET_CODE (XEXP (offset, 1)) == CONST_INT) - { - base = gen_rtx (PLUS, GET_MODE (base), base, XEXP (offset, 0)); - offset = XEXP (offset, 1); - } - else - { - base = gen_rtx (PLUS, GET_MODE (base), base, offset); - offset = const0_rtx; - } - } - else if (GET_CODE (offset) != CONST_INT) - { - base = gen_rtx (PLUS, GET_MODE (base), base, offset); - offset = const0_rtx; - } - - if (all_const && GET_CODE (base) == PLUS) - base = gen_rtx (CONST, GET_MODE (base), base); - - if (GET_CODE (offset) != CONST_INT) - abort (); - - val.start = INTVAL (offset); - val.end = val.start + GET_MODE_SIZE (GET_MODE (x)); - val.base = base; - return val; - } - else if (GET_CODE (x) == REG) - { - val.reg_flag = 1; - val.start = true_regnum (x); - if (val.start < 0) - { - /* A pseudo with no hard reg. */ - val.start = REGNO (x); - val.end = val.start + 1; - } - else - /* A hard reg. */ - val.end = val.start + HARD_REGNO_NREGS (val.start, GET_MODE (x)); - } - else if (GET_CODE (x) == SUBREG) - { - if (GET_CODE (SUBREG_REG (x)) != REG) - /* This could be more precise, but it's good enough. */ - return decompose (SUBREG_REG (x)); - val.reg_flag = 1; - val.start = true_regnum (x); - if (val.start < 0) - return decompose (SUBREG_REG (x)); - else - /* A hard reg. */ - val.end = val.start + HARD_REGNO_NREGS (val.start, GET_MODE (x)); - } - else if (CONSTANT_P (x) - /* This hasn't been assigned yet, so it can't conflict yet. */ - || GET_CODE (x) == SCRATCH) - val.safe = 1; - else - abort (); - return val; -} - -/* Return 1 if altering Y will not modify the value of X. - Y is also described by YDATA, which should be decompose (Y). */ - -static int -immune_p (x, y, ydata) - rtx x, y; - struct decomposition ydata; -{ - struct decomposition xdata; - - if (ydata.reg_flag) - return !refers_to_regno_for_reload_p (ydata.start, ydata.end, x, NULL_PTR); - if (ydata.safe) - return 1; - - if (GET_CODE (y) != MEM) - abort (); - /* If Y is memory and X is not, Y can't affect X. */ - if (GET_CODE (x) != MEM) - return 1; - - xdata = decompose (x); - - if (! rtx_equal_p (xdata.base, ydata.base)) - { - /* If bases are distinct symbolic constants, there is no overlap. */ - if (CONSTANT_P (xdata.base) && CONSTANT_P (ydata.base)) - return 1; - /* Constants and stack slots never overlap. */ - if (CONSTANT_P (xdata.base) - && (ydata.base == frame_pointer_rtx - || ydata.base == stack_pointer_rtx)) - return 1; - if (CONSTANT_P (ydata.base) - && (xdata.base == frame_pointer_rtx - || xdata.base == stack_pointer_rtx)) - return 1; - /* If either base is variable, we don't know anything. */ - return 0; - } - - - return (xdata.start >= ydata.end || ydata.start >= xdata.end); -} - -/* Similar, but calls decompose. */ - -int -safe_from_earlyclobber (op, clobber) - rtx op, clobber; -{ - struct decomposition early_data; - - early_data = decompose (clobber); - return immune_p (op, clobber, early_data); -} - -/* Main entry point of this file: search the body of INSN - for values that need reloading and record them with push_reload. - REPLACE nonzero means record also where the values occur - so that subst_reloads can be used. - - IND_LEVELS says how many levels of indirection are supported by this - machine; a value of zero means that a memory reference is not a valid - memory address. - - LIVE_KNOWN says we have valid information about which hard - regs are live at each point in the program; this is true when - we are called from global_alloc but false when stupid register - allocation has been done. - - RELOAD_REG_P if nonzero is a vector indexed by hard reg number - which is nonnegative if the reg has been commandeered for reloading into. - It is copied into STATIC_RELOAD_REG_P and referenced from there - by various subroutines. */ - -void -find_reloads (insn, replace, ind_levels, live_known, reload_reg_p) - rtx insn; - int replace, ind_levels; - int live_known; - short *reload_reg_p; -{ -#ifdef REGISTER_CONSTRAINTS - - register int insn_code_number; - register int i, j; - int noperands; - /* These are the constraints for the insn. We don't change them. */ - char *constraints1[MAX_RECOG_OPERANDS]; - /* These start out as the constraints for the insn - and they are chewed up as we consider alternatives. */ - char *constraints[MAX_RECOG_OPERANDS]; - /* These are the preferred classes for an operand, or NO_REGS if it isn't - a register. */ - enum reg_class preferred_class[MAX_RECOG_OPERANDS]; - char pref_or_nothing[MAX_RECOG_OPERANDS]; - /* Nonzero for a MEM operand whose entire address needs a reload. */ - int address_reloaded[MAX_RECOG_OPERANDS]; - /* Value of enum reload_type to use for operand. */ - enum reload_type operand_type[MAX_RECOG_OPERANDS]; - /* Value of enum reload_type to use within address of operand. */ - enum reload_type address_type[MAX_RECOG_OPERANDS]; - /* Save the usage of each operand. */ - enum reload_usage { RELOAD_READ, RELOAD_READ_WRITE, RELOAD_WRITE } modified[MAX_RECOG_OPERANDS]; - int no_input_reloads = 0, no_output_reloads = 0; - int n_alternatives; - int this_alternative[MAX_RECOG_OPERANDS]; - char this_alternative_win[MAX_RECOG_OPERANDS]; - char this_alternative_offmemok[MAX_RECOG_OPERANDS]; - char this_alternative_earlyclobber[MAX_RECOG_OPERANDS]; - int this_alternative_matches[MAX_RECOG_OPERANDS]; - int swapped; - int goal_alternative[MAX_RECOG_OPERANDS]; - int this_alternative_number; - int goal_alternative_number; - int operand_reloadnum[MAX_RECOG_OPERANDS]; - int goal_alternative_matches[MAX_RECOG_OPERANDS]; - int goal_alternative_matched[MAX_RECOG_OPERANDS]; - char goal_alternative_win[MAX_RECOG_OPERANDS]; - char goal_alternative_offmemok[MAX_RECOG_OPERANDS]; - char goal_alternative_earlyclobber[MAX_RECOG_OPERANDS]; - int goal_alternative_swapped; - int best; - int commutative; - char operands_match[MAX_RECOG_OPERANDS][MAX_RECOG_OPERANDS]; - rtx substed_operand[MAX_RECOG_OPERANDS]; - rtx body = PATTERN (insn); - rtx set = single_set (insn); - int goal_earlyclobber, this_earlyclobber; - enum machine_mode operand_mode[MAX_RECOG_OPERANDS]; - - this_insn = insn; - this_insn_is_asm = 0; /* Tentative. */ - n_reloads = 0; - n_replacements = 0; - n_memlocs = 0; - n_earlyclobbers = 0; - replace_reloads = replace; - hard_regs_live_known = live_known; - static_reload_reg_p = reload_reg_p; - - /* JUMP_INSNs and CALL_INSNs are not allowed to have any output reloads; - neither are insns that SET cc0. Insns that use CC0 are not allowed - to have any input reloads. */ - if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CALL_INSN) - no_output_reloads = 1; - -#ifdef HAVE_cc0 - if (reg_referenced_p (cc0_rtx, PATTERN (insn))) - no_input_reloads = 1; - if (reg_set_p (cc0_rtx, PATTERN (insn))) - no_output_reloads = 1; -#endif - -#ifdef SECONDARY_MEMORY_NEEDED - /* The eliminated forms of any secondary memory locations are per-insn, so - clear them out here. */ - - bzero (secondary_memlocs_elim, sizeof secondary_memlocs_elim); -#endif - - /* Find what kind of insn this is. NOPERANDS gets number of operands. - Make OPERANDS point to a vector of operand values. - Make OPERAND_LOCS point to a vector of pointers to - where the operands were found. - Fill CONSTRAINTS and CONSTRAINTS1 with pointers to the - constraint-strings for this insn. - Return if the insn needs no reload processing. */ - - switch (GET_CODE (body)) - { - case USE: - case CLOBBER: - case ASM_INPUT: - case ADDR_VEC: - case ADDR_DIFF_VEC: - return; - - case SET: - /* Dispose quickly of (set (reg..) (reg..)) if both have hard regs and it - is cheap to move between them. If it is not, there may not be an insn - to do the copy, so we may need a reload. */ - if (GET_CODE (SET_DEST (body)) == REG - && REGNO (SET_DEST (body)) < FIRST_PSEUDO_REGISTER - && GET_CODE (SET_SRC (body)) == REG - && REGNO (SET_SRC (body)) < FIRST_PSEUDO_REGISTER - && REGISTER_MOVE_COST (REGNO_REG_CLASS (REGNO (SET_SRC (body))), - REGNO_REG_CLASS (REGNO (SET_DEST (body)))) == 2) - return; - case PARALLEL: - case ASM_OPERANDS: - reload_n_operands = noperands = asm_noperands (body); - if (noperands >= 0) - { - /* This insn is an `asm' with operands. */ - - insn_code_number = -1; - this_insn_is_asm = 1; - - /* expand_asm_operands makes sure there aren't too many operands. */ - if (noperands > MAX_RECOG_OPERANDS) - abort (); - - /* Now get the operand values and constraints out of the insn. */ - - decode_asm_operands (body, recog_operand, recog_operand_loc, - constraints, operand_mode); - if (noperands > 0) - { - bcopy (constraints, constraints1, noperands * sizeof (char *)); - n_alternatives = n_occurrences (',', constraints[0]) + 1; - for (i = 1; i < noperands; i++) - if (n_alternatives != n_occurrences (',', constraints[i]) + 1) - { - error_for_asm (insn, "operand constraints differ in number of alternatives"); - /* Avoid further trouble with this insn. */ - PATTERN (insn) = gen_rtx (USE, VOIDmode, const0_rtx); - n_reloads = 0; - return; - } - } - break; - } - - default: - /* Ordinary insn: recognize it, get the operands via insn_extract - and get the constraints. */ - - insn_code_number = recog_memoized (insn); - if (insn_code_number < 0) - fatal_insn_not_found (insn); - - reload_n_operands = noperands = insn_n_operands[insn_code_number]; - n_alternatives = insn_n_alternatives[insn_code_number]; - /* Just return "no reloads" if insn has no operands with constraints. */ - if (n_alternatives == 0) - return; - insn_extract (insn); - for (i = 0; i < noperands; i++) - { - constraints[i] = constraints1[i] - = insn_operand_constraint[insn_code_number][i]; - operand_mode[i] = insn_operand_mode[insn_code_number][i]; - } - } - - if (noperands == 0) - return; - - commutative = -1; - - /* If we will need to know, later, whether some pair of operands - are the same, we must compare them now and save the result. - Reloading the base and index registers will clobber them - and afterward they will fail to match. */ - - for (i = 0; i < noperands; i++) - { - register char *p; - register int c; - - substed_operand[i] = recog_operand[i]; - p = constraints[i]; - - modified[i] = RELOAD_READ; - - /* Scan this operand's constraint to see if it is an output operand, - an in-out operand, is commutative, or should match another. */ - - while (c = *p++) - { - if (c == '=') - modified[i] = RELOAD_WRITE; - else if (c == '+') - modified[i] = RELOAD_READ_WRITE; - else if (c == '%') - { - /* The last operand should not be marked commutative. */ - if (i == noperands - 1) - { - if (this_insn_is_asm) - warning_for_asm (this_insn, - "`%%' constraint used with last operand"); - else - abort (); - } - else - commutative = i; - } - else if (c >= '0' && c <= '9') - { - c -= '0'; - operands_match[c][i] - = operands_match_p (recog_operand[c], recog_operand[i]); - - /* An operand may not match itself. */ - if (c == i) - { - if (this_insn_is_asm) - warning_for_asm (this_insn, - "operand %d has constraint %d", i, c); - else - abort (); - } - - /* If C can be commuted with C+1, and C might need to match I, - then C+1 might also need to match I. */ - if (commutative >= 0) - { - if (c == commutative || c == commutative + 1) - { - int other = c + (c == commutative ? 1 : -1); - operands_match[other][i] - = operands_match_p (recog_operand[other], recog_operand[i]); - } - if (i == commutative || i == commutative + 1) - { - int other = i + (i == commutative ? 1 : -1); - operands_match[c][other] - = operands_match_p (recog_operand[c], recog_operand[other]); - } - /* Note that C is supposed to be less than I. - No need to consider altering both C and I because in - that case we would alter one into the other. */ - } - } - } - } - - /* Examine each operand that is a memory reference or memory address - and reload parts of the addresses into index registers. - Also here any references to pseudo regs that didn't get hard regs - but are equivalent to constants get replaced in the insn itself - with those constants. Nobody will ever see them again. - - Finally, set up the preferred classes of each operand. */ - - for (i = 0; i < noperands; i++) - { - register RTX_CODE code = GET_CODE (recog_operand[i]); - - address_reloaded[i] = 0; - operand_type[i] = (modified[i] == RELOAD_READ ? RELOAD_FOR_INPUT - : modified[i] == RELOAD_WRITE ? RELOAD_FOR_OUTPUT - : RELOAD_OTHER); - address_type[i] - = (modified[i] == RELOAD_READ ? RELOAD_FOR_INPUT_ADDRESS - : modified[i] == RELOAD_WRITE ? RELOAD_FOR_OUTPUT_ADDRESS - : RELOAD_OTHER); - - if (constraints[i][0] == 'p') - { - find_reloads_address (VOIDmode, NULL_PTR, - recog_operand[i], recog_operand_loc[i], - i, operand_type[i], ind_levels); - substed_operand[i] = recog_operand[i] = *recog_operand_loc[i]; - } - else if (code == MEM) - { - if (find_reloads_address (GET_MODE (recog_operand[i]), - recog_operand_loc[i], - XEXP (recog_operand[i], 0), - &XEXP (recog_operand[i], 0), - i, address_type[i], ind_levels)) - address_reloaded[i] = 1; - substed_operand[i] = recog_operand[i] = *recog_operand_loc[i]; - } - else if (code == SUBREG) - substed_operand[i] = recog_operand[i] = *recog_operand_loc[i] - = find_reloads_toplev (recog_operand[i], i, address_type[i], - ind_levels, - set != 0 - && &SET_DEST (set) == recog_operand_loc[i]); - else if (code == REG) - { - /* This is equivalent to calling find_reloads_toplev. - The code is duplicated for speed. - When we find a pseudo always equivalent to a constant, - we replace it by the constant. We must be sure, however, - that we don't try to replace it in the insn in which it - is being set. */ - register int regno = REGNO (recog_operand[i]); - if (reg_equiv_constant[regno] != 0 - && (set == 0 || &SET_DEST (set) != recog_operand_loc[i])) - substed_operand[i] = recog_operand[i] - = reg_equiv_constant[regno]; -#if 0 /* This might screw code in reload1.c to delete prior output-reload - that feeds this insn. */ - if (reg_equiv_mem[regno] != 0) - substed_operand[i] = recog_operand[i] - = reg_equiv_mem[regno]; -#endif - if (reg_equiv_address[regno] != 0) - { - /* If reg_equiv_address is not a constant address, copy it, - since it may be shared. */ - rtx address = reg_equiv_address[regno]; - - if (rtx_varies_p (address)) - address = copy_rtx (address); - - /* If this is an output operand, we must output a CLOBBER - after INSN so find_equiv_reg knows REGNO is being written. - Mark this insn specially, do we can put our output reloads - after it. */ - - if (modified[i] != RELOAD_READ) - PUT_MODE (emit_insn_after (gen_rtx (CLOBBER, VOIDmode, - recog_operand[i]), - insn), - DImode); - - *recog_operand_loc[i] = recog_operand[i] - = gen_rtx (MEM, GET_MODE (recog_operand[i]), address); - RTX_UNCHANGING_P (recog_operand[i]) - = RTX_UNCHANGING_P (regno_reg_rtx[regno]); - find_reloads_address (GET_MODE (recog_operand[i]), - recog_operand_loc[i], - XEXP (recog_operand[i], 0), - &XEXP (recog_operand[i], 0), - i, address_type[i], ind_levels); - substed_operand[i] = recog_operand[i] = *recog_operand_loc[i]; - } - } - /* If the operand is still a register (we didn't replace it with an - equivalent), get the preferred class to reload it into. */ - code = GET_CODE (recog_operand[i]); - preferred_class[i] - = ((code == REG && REGNO (recog_operand[i]) >= FIRST_PSEUDO_REGISTER) - ? reg_preferred_class (REGNO (recog_operand[i])) : NO_REGS); - pref_or_nothing[i] - = (code == REG && REGNO (recog_operand[i]) >= FIRST_PSEUDO_REGISTER - && reg_alternate_class (REGNO (recog_operand[i])) == NO_REGS); - } - - /* If this is simply a copy from operand 1 to operand 0, merge the - preferred classes for the operands. */ - if (set != 0 && noperands >= 2 && recog_operand[0] == SET_DEST (set) - && recog_operand[1] == SET_SRC (set)) - { - preferred_class[0] = preferred_class[1] - = reg_class_subunion[(int) preferred_class[0]][(int) preferred_class[1]]; - pref_or_nothing[0] |= pref_or_nothing[1]; - pref_or_nothing[1] |= pref_or_nothing[0]; - } - - /* Now see what we need for pseudo-regs that didn't get hard regs - or got the wrong kind of hard reg. For this, we must consider - all the operands together against the register constraints. */ - - best = MAX_RECOG_OPERANDS + 300; - - swapped = 0; - goal_alternative_swapped = 0; - try_swapped: - - /* The constraints are made of several alternatives. - Each operand's constraint looks like foo,bar,... with commas - separating the alternatives. The first alternatives for all - operands go together, the second alternatives go together, etc. - - First loop over alternatives. */ - - for (this_alternative_number = 0; - this_alternative_number < n_alternatives; - this_alternative_number++) - { - /* Loop over operands for one constraint alternative. */ - /* LOSERS counts those that don't fit this alternative - and would require loading. */ - int losers = 0; - /* BAD is set to 1 if it some operand can't fit this alternative - even after reloading. */ - int bad = 0; - /* REJECT is a count of how undesirable this alternative says it is - if any reloading is required. If the alternative matches exactly - then REJECT is ignored, but otherwise it gets this much - counted against it in addition to the reloading needed. Each - ? counts three times here since we want the disparaging caused by - a bad register class to only count 1/3 as much. */ - int reject = 0; - - this_earlyclobber = 0; - - for (i = 0; i < noperands; i++) - { - register char *p = constraints[i]; - register int win = 0; - /* 0 => this operand can be reloaded somehow for this alternative */ - int badop = 1; - /* 0 => this operand can be reloaded if the alternative allows regs. */ - int winreg = 0; - int c; - register rtx operand = recog_operand[i]; - int offset = 0; - /* Nonzero means this is a MEM that must be reloaded into a reg - regardless of what the constraint says. */ - int force_reload = 0; - int offmemok = 0; - int earlyclobber = 0; - - /* If the operand is a SUBREG, extract - the REG or MEM (or maybe even a constant) within. - (Constants can occur as a result of reg_equiv_constant.) */ - - while (GET_CODE (operand) == SUBREG) - { - offset += SUBREG_WORD (operand); - operand = SUBREG_REG (operand); - /* Force reload if this is not a register or if there may may - be a problem accessing the register in the outer mode. */ - if (GET_CODE (operand) != REG -#if defined(BYTE_LOADS_ZERO_EXTEND) || defined(BYTE_LOADS_SIGN_EXTEND) - /* ??? The comment below clearly does not match the code. - What the code below actually does is set force_reload - for a paradoxical subreg of a pseudo. rms and kenner - can't see the point of doing this. */ - /* Nonparadoxical subreg of a pseudoreg. - Don't to load the full width if on this machine - we expected the fetch to extend. */ - || ((GET_MODE_SIZE (operand_mode[i]) - > GET_MODE_SIZE (GET_MODE (operand))) - && REGNO (operand) >= FIRST_PSEUDO_REGISTER) -#endif - /* Subreg of a hard reg which can't handle the subreg's mode - or which would handle that mode in the wrong number of - registers for subregging to work. */ - || (REGNO (operand) < FIRST_PSEUDO_REGISTER - && (! HARD_REGNO_MODE_OK (REGNO (operand), - operand_mode[i]) - || (GET_MODE_SIZE (operand_mode[i]) <= UNITS_PER_WORD - && (GET_MODE_SIZE (GET_MODE (operand)) - > UNITS_PER_WORD) - && ((GET_MODE_SIZE (GET_MODE (operand)) - / UNITS_PER_WORD) - != HARD_REGNO_NREGS (REGNO (operand), - GET_MODE (operand))))))) - force_reload = 1; - } - - this_alternative[i] = (int) NO_REGS; - this_alternative_win[i] = 0; - this_alternative_offmemok[i] = 0; - this_alternative_earlyclobber[i] = 0; - this_alternative_matches[i] = -1; - - /* An empty constraint or empty alternative - allows anything which matched the pattern. */ - if (*p == 0 || *p == ',') - win = 1, badop = 0; - - /* Scan this alternative's specs for this operand; - set WIN if the operand fits any letter in this alternative. - Otherwise, clear BADOP if this operand could - fit some letter after reloads, - or set WINREG if this operand could fit after reloads - provided the constraint allows some registers. */ - - while (*p && (c = *p++) != ',') - switch (c) - { - case '=': - case '+': - case '*': - break; - - case '%': - /* The last operand should not be marked commutative. */ - if (i != noperands - 1) - commutative = i; - break; - - case '?': - reject += 3; - break; - - case '!': - reject = 300; - break; - - case '#': - /* Ignore rest of this alternative as far as - reloading is concerned. */ - while (*p && *p != ',') p++; - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - c -= '0'; - this_alternative_matches[i] = c; - /* We are supposed to match a previous operand. - If we do, we win if that one did. - If we do not, count both of the operands as losers. - (This is too conservative, since most of the time - only a single reload insn will be needed to make - the two operands win. As a result, this alternative - may be rejected when it is actually desirable.) */ - if ((swapped && (c != commutative || i != commutative + 1)) - /* If we are matching as if two operands were swapped, - also pretend that operands_match had been computed - with swapped. - But if I is the second of those and C is the first, - don't exchange them, because operands_match is valid - only on one side of its diagonal. */ - ? (operands_match - [(c == commutative || c == commutative + 1) - ? 2*commutative + 1 - c : c] - [(i == commutative || i == commutative + 1) - ? 2*commutative + 1 - i : i]) - : operands_match[c][i]) - win = this_alternative_win[c]; - else - { - /* Operands don't match. */ - rtx value; - /* Retroactively mark the operand we had to match - as a loser, if it wasn't already. */ - if (this_alternative_win[c]) - losers++; - this_alternative_win[c] = 0; - if (this_alternative[c] == (int) NO_REGS) - bad = 1; - /* But count the pair only once in the total badness of - this alternative, if the pair can be a dummy reload. */ - value - = find_dummy_reload (recog_operand[i], recog_operand[c], - recog_operand_loc[i], recog_operand_loc[c], - this_alternative[c], -1); - - if (value != 0) - losers--; - } - /* This can be fixed with reloads if the operand - we are supposed to match can be fixed with reloads. */ - badop = 0; - this_alternative[i] = this_alternative[c]; - break; - - case 'p': - /* All necessary reloads for an address_operand - were handled in find_reloads_address. */ - this_alternative[i] = (int) ALL_REGS; - win = 1; - break; - - case 'm': - if (force_reload) - break; - if (GET_CODE (operand) == MEM - || (GET_CODE (operand) == REG - && REGNO (operand) >= FIRST_PSEUDO_REGISTER - && reg_renumber[REGNO (operand)] < 0)) - win = 1; - if (CONSTANT_P (operand)) - badop = 0; - break; - - case '<': - if (GET_CODE (operand) == MEM - && ! address_reloaded[i] - && (GET_CODE (XEXP (operand, 0)) == PRE_DEC - || GET_CODE (XEXP (operand, 0)) == POST_DEC)) - win = 1; - break; - - case '>': - if (GET_CODE (operand) == MEM - && ! address_reloaded[i] - && (GET_CODE (XEXP (operand, 0)) == PRE_INC - || GET_CODE (XEXP (operand, 0)) == POST_INC)) - win = 1; - break; - - /* Memory operand whose address is not offsettable. */ - case 'V': - if (force_reload) - break; - if (GET_CODE (operand) == MEM - && ! (ind_levels ? offsettable_memref_p (operand) - : offsettable_nonstrict_memref_p (operand)) - /* Certain mem addresses will become offsettable - after they themselves are reloaded. This is important; - we don't want our own handling of unoffsettables - to override the handling of reg_equiv_address. */ - && !(GET_CODE (XEXP (operand, 0)) == REG - && (ind_levels == 0 - || reg_equiv_address[REGNO (XEXP (operand, 0))] != 0))) - win = 1; - break; - - /* Memory operand whose address is offsettable. */ - case 'o': - if (force_reload) - break; - if ((GET_CODE (operand) == MEM - /* If IND_LEVELS, find_reloads_address won't reload a - pseudo that didn't get a hard reg, so we have to - reject that case. */ - && (ind_levels ? offsettable_memref_p (operand) - : offsettable_nonstrict_memref_p (operand))) - /* Certain mem addresses will become offsettable - after they themselves are reloaded. This is important; - we don't want our own handling of unoffsettables - to override the handling of reg_equiv_address. */ - || (GET_CODE (operand) == MEM - && GET_CODE (XEXP (operand, 0)) == REG - && (ind_levels == 0 - || reg_equiv_address[REGNO (XEXP (operand, 0))] != 0)) - || (GET_CODE (operand) == REG - && REGNO (operand) >= FIRST_PSEUDO_REGISTER - && reg_renumber[REGNO (operand)] < 0)) - win = 1; - if (CONSTANT_P (operand) || GET_CODE (operand) == MEM) - badop = 0; - offmemok = 1; - break; - - case '&': - /* Output operand that is stored before the need for the - input operands (and their index registers) is over. */ - earlyclobber = 1, this_earlyclobber = 1; - break; - - case 'E': - /* Match any floating double constant, but only if - we can examine the bits of it reliably. */ - if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT - || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD) - && GET_MODE (operand) != VOIDmode && ! flag_pretend_float) - break; - if (GET_CODE (operand) == CONST_DOUBLE) - win = 1; - break; - - case 'F': - if (GET_CODE (operand) == CONST_DOUBLE) - win = 1; - break; - - case 'G': - case 'H': - if (GET_CODE (operand) == CONST_DOUBLE - && CONST_DOUBLE_OK_FOR_LETTER_P (operand, c)) - win = 1; - break; - - case 's': - if (GET_CODE (operand) == CONST_INT - || (GET_CODE (operand) == CONST_DOUBLE - && GET_MODE (operand) == VOIDmode)) - break; - case 'i': - if (CONSTANT_P (operand) -#ifdef LEGITIMATE_PIC_OPERAND_P - && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (operand)) -#endif - ) - win = 1; - break; - - case 'n': - if (GET_CODE (operand) == CONST_INT - || (GET_CODE (operand) == CONST_DOUBLE - && GET_MODE (operand) == VOIDmode)) - win = 1; - break; - - case 'I': - case 'J': - case 'K': - case 'L': - case 'M': - case 'N': - case 'O': - case 'P': - if (GET_CODE (operand) == CONST_INT - && CONST_OK_FOR_LETTER_P (INTVAL (operand), c)) - win = 1; - break; - - case 'X': - win = 1; - break; - - case 'g': - if (! force_reload - /* A PLUS is never a valid operand, but reload can make - it from a register when eliminating registers. */ - && GET_CODE (operand) != PLUS - /* A SCRATCH is not a valid operand. */ - && GET_CODE (operand) != SCRATCH -#ifdef LEGITIMATE_PIC_OPERAND_P - && (! CONSTANT_P (operand) - || ! flag_pic - || LEGITIMATE_PIC_OPERAND_P (operand)) -#endif - && (GENERAL_REGS == ALL_REGS - || GET_CODE (operand) != REG - || (REGNO (operand) >= FIRST_PSEUDO_REGISTER - && reg_renumber[REGNO (operand)] < 0))) - win = 1; - /* Drop through into 'r' case */ - - case 'r': - this_alternative[i] - = (int) reg_class_subunion[this_alternative[i]][(int) GENERAL_REGS]; - goto reg; - -#ifdef EXTRA_CONSTRAINT - case 'Q': - case 'R': - case 'S': - case 'T': - case 'U': - if (EXTRA_CONSTRAINT (operand, c)) - win = 1; - break; -#endif - - default: - this_alternative[i] - = (int) reg_class_subunion[this_alternative[i]][(int) REG_CLASS_FROM_LETTER (c)]; - - reg: - if (GET_MODE (operand) == BLKmode) - break; - winreg = 1; - if (GET_CODE (operand) == REG - && reg_fits_class_p (operand, this_alternative[i], - offset, GET_MODE (recog_operand[i]))) - win = 1; - break; - } - - constraints[i] = p; - - /* If this operand could be handled with a reg, - and some reg is allowed, then this operand can be handled. */ - if (winreg && this_alternative[i] != (int) NO_REGS) - badop = 0; - - /* Record which operands fit this alternative. */ - this_alternative_earlyclobber[i] = earlyclobber; - if (win && ! force_reload) - this_alternative_win[i] = 1; - else - { - this_alternative_offmemok[i] = offmemok; - losers++; - if (badop) - bad = 1; - /* Alternative loses if it has no regs for a reg operand. */ - if (GET_CODE (operand) == REG - && this_alternative[i] == (int) NO_REGS - && this_alternative_matches[i] < 0) - bad = 1; - - /* Alternative loses if it requires a type of reload not - permitted for this insn. We can always reload SCRATCH - and objects with a REG_UNUSED note. */ - if (GET_CODE (operand) != SCRATCH - && modified[i] != RELOAD_READ && no_output_reloads - && ! find_reg_note (insn, REG_UNUSED, operand)) - bad = 1; - else if (modified[i] != RELOAD_WRITE && no_input_reloads) - bad = 1; - - /* We prefer to reload pseudos over reloading other things, - since such reloads may be able to be eliminated later. - If we are reloading a SCRATCH, we won't be generating any - insns, just using a register, so it is also preferred. - So bump REJECT in other cases. */ - if (GET_CODE (operand) != REG && GET_CODE (operand) != SCRATCH) - reject++; - } - - /* If this operand is a pseudo register that didn't get a hard - reg and this alternative accepts some register, see if the - class that we want is a subset of the preferred class for this - register. If not, but it intersects that class, use the - preferred class instead. If it does not intersect the preferred - class, show that usage of this alternative should be discouraged; - it will be discouraged more still if the register is `preferred - or nothing'. We do this because it increases the chance of - reusing our spill register in a later insn and avoiding a pair - of memory stores and loads. - - Don't bother with this if this alternative will accept this - operand. - - Don't do this for a multiword operand, if - we have to worry about small classes, because making reg groups - harder to allocate is asking for trouble. - - Don't do this if the preferred class has only one register - because we might otherwise exhaust the class. */ - - - if (! win && this_alternative[i] != (int) NO_REGS -#ifdef SMALL_REGISTER_CLASSES - && GET_MODE_SIZE (operand_mode[i]) <= UNITS_PER_WORD -#endif - && reg_class_size[(int) preferred_class[i]] > 1) - { - if (! reg_class_subset_p (this_alternative[i], - preferred_class[i])) - { - /* Since we don't have a way of forming the intersection, - we just do something special if the preferred class - is a subset of the class we have; that's the most - common case anyway. */ - if (reg_class_subset_p (preferred_class[i], - this_alternative[i])) - this_alternative[i] = (int) preferred_class[i]; - else - reject += (1 + pref_or_nothing[i]); - } - } - } - - /* Now see if any output operands that are marked "earlyclobber" - in this alternative conflict with any input operands - or any memory addresses. */ - - for (i = 0; i < noperands; i++) - if (this_alternative_earlyclobber[i] - && this_alternative_win[i]) - { - struct decomposition early_data; - - early_data = decompose (recog_operand[i]); - - if (modified[i] == RELOAD_READ) - { - if (this_insn_is_asm) - warning_for_asm (this_insn, - "`&' constraint used with input operand"); - else - abort (); - continue; - } - - if (this_alternative[i] == NO_REGS) - { - this_alternative_earlyclobber[i] = 0; - if (this_insn_is_asm) - error_for_asm (this_insn, - "`&' constraint used with no register class"); - else - abort (); - } - - for (j = 0; j < noperands; j++) - /* Is this an input operand or a memory ref? */ - if ((GET_CODE (recog_operand[j]) == MEM - || modified[j] != RELOAD_WRITE) - && j != i - /* Ignore things like match_operator operands. */ - && *constraints1[j] != 0 - /* Don't count an input operand that is constrained to match - the early clobber operand. */ - && ! (this_alternative_matches[j] == i - && rtx_equal_p (recog_operand[i], recog_operand[j])) - /* Is it altered by storing the earlyclobber operand? */ - && !immune_p (recog_operand[j], recog_operand[i], early_data)) - { - /* If the output is in a single-reg class, - it's costly to reload it, so reload the input instead. */ - if (reg_class_size[this_alternative[i]] == 1 - && (GET_CODE (recog_operand[j]) == REG - || GET_CODE (recog_operand[j]) == SUBREG)) - { - losers++; - this_alternative_win[j] = 0; - } - else - break; - } - /* If an earlyclobber operand conflicts with something, - it must be reloaded, so request this and count the cost. */ - if (j != noperands) - { - losers++; - this_alternative_win[i] = 0; - for (j = 0; j < noperands; j++) - if (this_alternative_matches[j] == i - && this_alternative_win[j]) - { - this_alternative_win[j] = 0; - losers++; - } - } - } - - /* If one alternative accepts all the operands, no reload required, - choose that alternative; don't consider the remaining ones. */ - if (losers == 0) - { - /* Unswap these so that they are never swapped at `finish'. */ - if (commutative >= 0) - { - recog_operand[commutative] = substed_operand[commutative]; - recog_operand[commutative + 1] - = substed_operand[commutative + 1]; - } - for (i = 0; i < noperands; i++) - { - goal_alternative_win[i] = 1; - goal_alternative[i] = this_alternative[i]; - goal_alternative_offmemok[i] = this_alternative_offmemok[i]; - goal_alternative_matches[i] = this_alternative_matches[i]; - goal_alternative_earlyclobber[i] - = this_alternative_earlyclobber[i]; - } - goal_alternative_number = this_alternative_number; - goal_alternative_swapped = swapped; - goal_earlyclobber = this_earlyclobber; - goto finish; - } - - /* REJECT, set by the ! and ? constraint characters and when a register - would be reloaded into a non-preferred class, discourages the use of - this alternative for a reload goal. REJECT is incremented by three - for each ? and one for each non-preferred class. */ - losers = losers * 3 + reject; - - /* If this alternative can be made to work by reloading, - and it needs less reloading than the others checked so far, - record it as the chosen goal for reloading. */ - if (! bad && best > losers) - { - for (i = 0; i < noperands; i++) - { - goal_alternative[i] = this_alternative[i]; - goal_alternative_win[i] = this_alternative_win[i]; - goal_alternative_offmemok[i] = this_alternative_offmemok[i]; - goal_alternative_matches[i] = this_alternative_matches[i]; - goal_alternative_earlyclobber[i] - = this_alternative_earlyclobber[i]; - } - goal_alternative_swapped = swapped; - best = losers; - goal_alternative_number = this_alternative_number; - goal_earlyclobber = this_earlyclobber; - } - } - - /* If insn is commutative (it's safe to exchange a certain pair of operands) - then we need to try each alternative twice, - the second time matching those two operands - as if we had exchanged them. - To do this, really exchange them in operands. - - If we have just tried the alternatives the second time, - return operands to normal and drop through. */ - - if (commutative >= 0) - { - swapped = !swapped; - if (swapped) - { - register enum reg_class tclass; - register int t; - - recog_operand[commutative] = substed_operand[commutative + 1]; - recog_operand[commutative + 1] = substed_operand[commutative]; - - tclass = preferred_class[commutative]; - preferred_class[commutative] = preferred_class[commutative + 1]; - preferred_class[commutative + 1] = tclass; - - t = pref_or_nothing[commutative]; - pref_or_nothing[commutative] = pref_or_nothing[commutative + 1]; - pref_or_nothing[commutative + 1] = t; - - bcopy (constraints1, constraints, noperands * sizeof (char *)); - goto try_swapped; - } - else - { - recog_operand[commutative] = substed_operand[commutative]; - recog_operand[commutative + 1] = substed_operand[commutative + 1]; - } - } - - /* The operands don't meet the constraints. - goal_alternative describes the alternative - that we could reach by reloading the fewest operands. - Reload so as to fit it. */ - - if (best == MAX_RECOG_OPERANDS + 300) - { - /* No alternative works with reloads?? */ - if (insn_code_number >= 0) - abort (); - error_for_asm (insn, "inconsistent operand constraints in an `asm'"); - /* Avoid further trouble with this insn. */ - PATTERN (insn) = gen_rtx (USE, VOIDmode, const0_rtx); - n_reloads = 0; - return; - } - - /* Jump to `finish' from above if all operands are valid already. - In that case, goal_alternative_win is all 1. */ - finish: - - /* Right now, for any pair of operands I and J that are required to match, - with I < J, - goal_alternative_matches[J] is I. - Set up goal_alternative_matched as the inverse function: - goal_alternative_matched[I] = J. */ - - for (i = 0; i < noperands; i++) - goal_alternative_matched[i] = -1; - - for (i = 0; i < noperands; i++) - if (! goal_alternative_win[i] - && goal_alternative_matches[i] >= 0) - goal_alternative_matched[goal_alternative_matches[i]] = i; - - /* If the best alternative is with operands 1 and 2 swapped, - consider them swapped before reporting the reloads. Update the - operand numbers of any reloads already pushed. */ - - if (goal_alternative_swapped) - { - register rtx tem; - - tem = substed_operand[commutative]; - substed_operand[commutative] = substed_operand[commutative + 1]; - substed_operand[commutative + 1] = tem; - tem = recog_operand[commutative]; - recog_operand[commutative] = recog_operand[commutative + 1]; - recog_operand[commutative + 1] = tem; - - for (i = 0; i < n_reloads; i++) - { - if (reload_opnum[i] == commutative) - reload_opnum[i] = commutative + 1; - else if (reload_opnum[i] == commutative + 1) - reload_opnum[i] = commutative; - } - } - - /* Perform whatever substitutions on the operands we are supposed - to make due to commutativity or replacement of registers - with equivalent constants or memory slots. */ - - for (i = 0; i < noperands; i++) - { - *recog_operand_loc[i] = substed_operand[i]; - /* While we are looping on operands, initialize this. */ - operand_reloadnum[i] = -1; - - /* If this is an earlyclobber operand, we need to widen the scope. - The reload must remain valid from the start of the insn being - reloaded until after the operand is stored into its destination. - We approximate this with RELOAD_OTHER even though we know that we - do not conflict with RELOAD_FOR_INPUT_ADDRESS reloads. - - One special case that is worth checking is when we have an - output that is earlyclobber but isn't used past the insn (typically - a SCRATCH). In this case, we only need have the reload live - through the insn itself, but not for any of our input or output - reloads. - - In any case, anything needed to address this operand can remain - however they were previously categorized. */ - - if (goal_alternative_earlyclobber[i]) - operand_type[i] - = (find_reg_note (insn, REG_UNUSED, recog_operand[i]) - ? RELOAD_FOR_INSN : RELOAD_OTHER); - } - - /* Any constants that aren't allowed and can't be reloaded - into registers are here changed into memory references. */ - for (i = 0; i < noperands; i++) - if (! goal_alternative_win[i] - && CONSTANT_P (recog_operand[i]) - && (PREFERRED_RELOAD_CLASS (recog_operand[i], - (enum reg_class) goal_alternative[i]) - == NO_REGS) - && operand_mode[i] != VOIDmode) - { - *recog_operand_loc[i] = recog_operand[i] - = find_reloads_toplev (force_const_mem (operand_mode[i], - recog_operand[i]), - i, address_type[i], ind_levels, 0); - if (alternative_allows_memconst (constraints1[i], - goal_alternative_number)) - goal_alternative_win[i] = 1; - } - - /* Now record reloads for all the operands that need them. */ - for (i = 0; i < noperands; i++) - if (! goal_alternative_win[i]) - { - /* Operands that match previous ones have already been handled. */ - if (goal_alternative_matches[i] >= 0) - ; - /* Handle an operand with a nonoffsettable address - appearing where an offsettable address will do - by reloading the address into a base register. */ - else if (goal_alternative_matched[i] == -1 - && goal_alternative_offmemok[i] - && GET_CODE (recog_operand[i]) == MEM) - { - operand_reloadnum[i] - = push_reload (XEXP (recog_operand[i], 0), NULL_RTX, - &XEXP (recog_operand[i], 0), NULL_PTR, - BASE_REG_CLASS, GET_MODE (XEXP (recog_operand[i], 0)), - VOIDmode, 0, 0, i, RELOAD_FOR_INPUT); - reload_inc[operand_reloadnum[i]] - = GET_MODE_SIZE (GET_MODE (recog_operand[i])); - - /* If this operand is an output, we will have made any - reloads for its address as RELOAD_FOR_OUTPUT_ADDRESS, but - now we are treating part of the operand as an input, so - we must change these to RELOAD_FOR_INPUT_ADDRESS. */ - - if (operand_type[i] == RELOAD_FOR_OUTPUT) - for (j = 0; j < n_reloads; j++) - if (reload_opnum[j] == i - && reload_when_needed[j] == RELOAD_FOR_OUTPUT_ADDRESS) - reload_when_needed[j] = RELOAD_FOR_INPUT_ADDRESS; - } - else if (goal_alternative_matched[i] == -1) - operand_reloadnum[i] = - push_reload (modified[i] != RELOAD_WRITE ? recog_operand[i] : 0, - modified[i] != RELOAD_READ ? recog_operand[i] : 0, - (modified[i] != RELOAD_WRITE ? - recog_operand_loc[i] : 0), - modified[i] != RELOAD_READ ? recog_operand_loc[i] : 0, - (enum reg_class) goal_alternative[i], - (modified[i] == RELOAD_WRITE - ? VOIDmode : operand_mode[i]), - (modified[i] == RELOAD_READ - ? VOIDmode : operand_mode[i]), - (insn_code_number < 0 ? 0 - : insn_operand_strict_low[insn_code_number][i]), - 0, i, operand_type[i]); - /* In a matching pair of operands, one must be input only - and the other must be output only. - Pass the input operand as IN and the other as OUT. */ - else if (modified[i] == RELOAD_READ - && modified[goal_alternative_matched[i]] == RELOAD_WRITE) - { - operand_reloadnum[i] - = push_reload (recog_operand[i], - recog_operand[goal_alternative_matched[i]], - recog_operand_loc[i], - recog_operand_loc[goal_alternative_matched[i]], - (enum reg_class) goal_alternative[i], - operand_mode[i], - operand_mode[goal_alternative_matched[i]], - 0, 0, i, RELOAD_OTHER); - operand_reloadnum[goal_alternative_matched[i]] = output_reloadnum; - } - else if (modified[i] == RELOAD_WRITE - && modified[goal_alternative_matched[i]] == RELOAD_READ) - { - operand_reloadnum[goal_alternative_matched[i]] - = push_reload (recog_operand[goal_alternative_matched[i]], - recog_operand[i], - recog_operand_loc[goal_alternative_matched[i]], - recog_operand_loc[i], - (enum reg_class) goal_alternative[i], - operand_mode[goal_alternative_matched[i]], - operand_mode[i], - 0, 0, i, RELOAD_OTHER); - operand_reloadnum[i] = output_reloadnum; - } - else if (insn_code_number >= 0) - abort (); - else - { - error_for_asm (insn, "inconsistent operand constraints in an `asm'"); - /* Avoid further trouble with this insn. */ - PATTERN (insn) = gen_rtx (USE, VOIDmode, const0_rtx); - n_reloads = 0; - return; - } - } - else if (goal_alternative_matched[i] < 0 - && goal_alternative_matches[i] < 0 - && optimize) - { - /* For each non-matching operand that's a MEM or a pseudo-register - that didn't get a hard register, make an optional reload. - This may get done even if the insn needs no reloads otherwise. */ - - rtx operand = recog_operand[i]; - - while (GET_CODE (operand) == SUBREG) - operand = XEXP (operand, 0); - if ((GET_CODE (operand) == MEM - || (GET_CODE (operand) == REG - && REGNO (operand) >= FIRST_PSEUDO_REGISTER)) - && (enum reg_class) goal_alternative[i] != NO_REGS - && ! no_input_reloads - /* Optional output reloads don't do anything and we mustn't - make in-out reloads on insns that are not permitted output - reloads. */ - && (modified[i] == RELOAD_READ - || (modified[i] == RELOAD_READ_WRITE && ! no_output_reloads))) - operand_reloadnum[i] - = push_reload (modified[i] != RELOAD_WRITE ? recog_operand[i] : 0, - modified[i] != RELOAD_READ ? recog_operand[i] : 0, - (modified[i] != RELOAD_WRITE - ? recog_operand_loc[i] : 0), - (modified[i] != RELOAD_READ - ? recog_operand_loc[i] : 0), - (enum reg_class) goal_alternative[i], - (modified[i] == RELOAD_WRITE - ? VOIDmode : operand_mode[i]), - (modified[i] == RELOAD_READ - ? VOIDmode : operand_mode[i]), - (insn_code_number < 0 ? 0 - : insn_operand_strict_low[insn_code_number][i]), - 1, i, operand_type[i]); - } - else if (goal_alternative_matches[i] >= 0 - && goal_alternative_win[goal_alternative_matches[i]] - && modified[i] == RELOAD_READ - && modified[goal_alternative_matches[i]] == RELOAD_WRITE - && ! no_input_reloads && ! no_output_reloads - && optimize) - { - /* Similarly, make an optional reload for a pair of matching - objects that are in MEM or a pseudo that didn't get a hard reg. */ - - rtx operand = recog_operand[i]; - - while (GET_CODE (operand) == SUBREG) - operand = XEXP (operand, 0); - if ((GET_CODE (operand) == MEM - || (GET_CODE (operand) == REG - && REGNO (operand) >= FIRST_PSEUDO_REGISTER)) - && ((enum reg_class) goal_alternative[goal_alternative_matches[i]] - != NO_REGS)) - operand_reloadnum[i] = operand_reloadnum[goal_alternative_matches[i]] - = push_reload (recog_operand[goal_alternative_matches[i]], - recog_operand[i], - recog_operand_loc[goal_alternative_matches[i]], - recog_operand_loc[i], - (enum reg_class) goal_alternative[goal_alternative_matches[i]], - operand_mode[goal_alternative_matches[i]], - operand_mode[i], - 0, 1, goal_alternative_matches[i], RELOAD_OTHER); - } - - /* Record the values of the earlyclobber operands for the caller. */ - if (goal_earlyclobber) - for (i = 0; i < noperands; i++) - if (goal_alternative_earlyclobber[i]) - reload_earlyclobbers[n_earlyclobbers++] = recog_operand[i]; - - /* If this insn pattern contains any MATCH_DUP's, make sure that - they will be substituted if the operands they match are substituted. - Also do now any substitutions we already did on the operands. - - Don't do this if we aren't making replacements because we might be - propagating things allocated by frame pointer elimination into places - it doesn't expect. */ - - if (insn_code_number >= 0 && replace) - for (i = insn_n_dups[insn_code_number] - 1; i >= 0; i--) - { - int opno = recog_dup_num[i]; - *recog_dup_loc[i] = *recog_operand_loc[opno]; - if (operand_reloadnum[opno] >= 0) - push_replacement (recog_dup_loc[i], operand_reloadnum[opno], - insn_operand_mode[insn_code_number][opno]); - } - -#if 0 - /* This loses because reloading of prior insns can invalidate the equivalence - (or at least find_equiv_reg isn't smart enough to find it any more), - causing this insn to need more reload regs than it needed before. - It may be too late to make the reload regs available. - Now this optimization is done safely in choose_reload_regs. */ - - /* For each reload of a reg into some other class of reg, - search for an existing equivalent reg (same value now) in the right class. - We can use it as long as we don't need to change its contents. */ - for (i = 0; i < n_reloads; i++) - if (reload_reg_rtx[i] == 0 - && reload_in[i] != 0 - && GET_CODE (reload_in[i]) == REG - && reload_out[i] == 0) - { - reload_reg_rtx[i] - = find_equiv_reg (reload_in[i], insn, reload_reg_class[i], -1, - static_reload_reg_p, 0, reload_inmode[i]); - /* Prevent generation of insn to load the value - because the one we found already has the value. */ - if (reload_reg_rtx[i]) - reload_in[i] = reload_reg_rtx[i]; - } -#endif - - /* Perhaps an output reload can be combined with another - to reduce needs by one. */ - if (!goal_earlyclobber) - combine_reloads (); - - /* If we have a pair of reloads for parts of an address, they are reloading - the same object, the operands themselves were not reloaded, and they - are for two operands that are supposed to match, merge the reloads and - change the type of the surviving reload to RELOAD_FOR_OPERAND_ADDRESS. */ - - for (i = 0; i < n_reloads; i++) - { - int k; - - for (j = i + 1; j < n_reloads; j++) - if ((reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS - || reload_when_needed[i] == RELOAD_FOR_OUTPUT_ADDRESS) - && (reload_when_needed[j] == RELOAD_FOR_INPUT_ADDRESS - || reload_when_needed[j] == RELOAD_FOR_OUTPUT_ADDRESS) - && rtx_equal_p (reload_in[i], reload_in[j]) - && (operand_reloadnum[reload_opnum[i]] < 0 - || reload_optional[operand_reloadnum[reload_opnum[i]]]) - && (operand_reloadnum[reload_opnum[j]] < 0 - || reload_optional[operand_reloadnum[reload_opnum[j]]]) - && (goal_alternative_matches[reload_opnum[i]] == reload_opnum[j] - || (goal_alternative_matches[reload_opnum[j]] - == reload_opnum[i]))) - { - for (k = 0; k < n_replacements; k++) - if (replacements[k].what == j) - replacements[k].what = i; - - reload_when_needed[i] = RELOAD_FOR_OPERAND_ADDRESS; - reload_in[j] = 0; - } - } - - /* Scan all the reloads and update their type. - If a reload is for the address of an operand and we didn't reload - that operand, change the type. Similarly, change the operand number - of a reload when two operands match. If a reload is optional, treat it - as though the operand isn't reloaded. - - ??? This latter case is somewhat odd because if we do the optional - reload, it means the object is hanging around. Thus we need only - do the address reload if the optional reload was NOT done. - - Change secondary reloads to be the address type of their operand, not - the normal type. - - If an operand's reload is now RELOAD_OTHER, change any - RELOAD_FOR_INPUT_ADDRESS reloads of that operand to - RELOAD_FOR_OTHER_ADDRESS. */ - - for (i = 0; i < n_reloads; i++) - { - if (reload_secondary_p[i] - && reload_when_needed[i] == operand_type[reload_opnum[i]]) - reload_when_needed[i] = address_type[reload_opnum[i]]; - - if ((reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS - || reload_when_needed[i] == RELOAD_FOR_OUTPUT_ADDRESS) - && (operand_reloadnum[reload_opnum[i]] < 0 - || reload_optional[operand_reloadnum[reload_opnum[i]]])) - reload_when_needed[i] = RELOAD_FOR_OPERAND_ADDRESS; - - if (reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS - && operand_reloadnum[reload_opnum[i]] >= 0 - && (reload_when_needed[operand_reloadnum[reload_opnum[i]]] - == RELOAD_OTHER)) - reload_when_needed[i] = RELOAD_FOR_OTHER_ADDRESS; - - if (goal_alternative_matches[reload_opnum[i]] >= 0) - reload_opnum[i] = goal_alternative_matches[reload_opnum[i]]; - } - - /* See if we have any reloads that are now allowed to be merged - because we've changed when the reload is needed to - RELOAD_FOR_OPERAND_ADDRESS or RELOAD_FOR_OTHER_ADDRESS. Only - check for the most common cases. */ - - for (i = 0; i < n_reloads; i++) - if (reload_in[i] != 0 && reload_out[i] == 0 - && (reload_when_needed[i] == RELOAD_FOR_OPERAND_ADDRESS - || reload_when_needed[i] == RELOAD_FOR_OTHER_ADDRESS)) - for (j = 0; j < n_reloads; j++) - if (i != j && reload_in[j] != 0 && reload_out[j] == 0 - && reload_when_needed[j] == reload_when_needed[i] - && MATCHES (reload_in[i], reload_in[j])) - { - reload_opnum[i] = MIN (reload_opnum[i], reload_opnum[j]); - transfer_replacements (i, j); - reload_in[j] = 0; - } - -#else /* no REGISTER_CONSTRAINTS */ - int noperands; - int insn_code_number; - int goal_earlyclobber = 0; /* Always 0, to make combine_reloads happen. */ - register int i; - rtx body = PATTERN (insn); - - n_reloads = 0; - n_replacements = 0; - n_earlyclobbers = 0; - replace_reloads = replace; - this_insn = insn; - - /* Find what kind of insn this is. NOPERANDS gets number of operands. - Store the operand values in RECOG_OPERAND and the locations - of the words in the insn that point to them in RECOG_OPERAND_LOC. - Return if the insn needs no reload processing. */ - - switch (GET_CODE (body)) - { - case USE: - case CLOBBER: - case ASM_INPUT: - case ADDR_VEC: - case ADDR_DIFF_VEC: - return; - - case PARALLEL: - case SET: - noperands = asm_noperands (body); - if (noperands >= 0) - { - /* This insn is an `asm' with operands. - First, find out how many operands, and allocate space. */ - - insn_code_number = -1; - /* ??? This is a bug! ??? - Give up and delete this insn if it has too many operands. */ - if (noperands > MAX_RECOG_OPERANDS) - abort (); - - /* Now get the operand values out of the insn. */ - - decode_asm_operands (body, recog_operand, recog_operand_loc, - NULL_PTR, NULL_PTR); - break; - } - - default: - /* Ordinary insn: recognize it, allocate space for operands and - constraints, and get them out via insn_extract. */ - - insn_code_number = recog_memoized (insn); - noperands = insn_n_operands[insn_code_number]; - insn_extract (insn); - } - - if (noperands == 0) - return; - - for (i = 0; i < noperands; i++) - { - register RTX_CODE code = GET_CODE (recog_operand[i]); - int is_set_dest = GET_CODE (body) == SET && (i == 0); - - if (insn_code_number >= 0) - if (insn_operand_address_p[insn_code_number][i]) - find_reloads_address (VOIDmode, NULL_PTR, - recog_operand[i], recog_operand_loc[i], - i, RELOAD_FOR_INPUT, ind_levels); - - /* In these cases, we can't tell if the operand is an input - or an output, so be conservative. In practice it won't be - problem. */ - - if (code == MEM) - find_reloads_address (GET_MODE (recog_operand[i]), - recog_operand_loc[i], - XEXP (recog_operand[i], 0), - &XEXP (recog_operand[i], 0), - i, RELOAD_OTHER, ind_levels); - if (code == SUBREG) - recog_operand[i] = *recog_operand_loc[i] - = find_reloads_toplev (recog_operand[i], i, RELOAD_OTHER, - ind_levels, is_set_dest); - if (code == REG) - { - register int regno = REGNO (recog_operand[i]); - if (reg_equiv_constant[regno] != 0 && !is_set_dest) - recog_operand[i] = *recog_operand_loc[i] - = reg_equiv_constant[regno]; -#if 0 /* This might screw code in reload1.c to delete prior output-reload - that feeds this insn. */ - if (reg_equiv_mem[regno] != 0) - recog_operand[i] = *recog_operand_loc[i] - = reg_equiv_mem[regno]; -#endif - } - } - - /* Perhaps an output reload can be combined with another - to reduce needs by one. */ - if (!goal_earlyclobber) - combine_reloads (); -#endif /* no REGISTER_CONSTRAINTS */ -} - -/* Return 1 if alternative number ALTNUM in constraint-string CONSTRAINT - accepts a memory operand with constant address. */ - -static int -alternative_allows_memconst (constraint, altnum) - char *constraint; - int altnum; -{ - register int c; - /* Skip alternatives before the one requested. */ - while (altnum > 0) - { - while (*constraint++ != ','); - altnum--; - } - /* Scan the requested alternative for 'm' or 'o'. - If one of them is present, this alternative accepts memory constants. */ - while ((c = *constraint++) && c != ',' && c != '#') - if (c == 'm' || c == 'o') - return 1; - return 0; -} - -/* Scan X for memory references and scan the addresses for reloading. - Also checks for references to "constant" regs that we want to eliminate - and replaces them with the values they stand for. - We may alter X destructively if it contains a reference to such. - If X is just a constant reg, we return the equivalent value - instead of X. - - IND_LEVELS says how many levels of indirect addressing this machine - supports. - - OPNUM and TYPE identify the purpose of the reload. - - IS_SET_DEST is true if X is the destination of a SET, which is not - appropriate to be replaced by a constant. */ - -static rtx -find_reloads_toplev (x, opnum, type, ind_levels, is_set_dest) - rtx x; - int opnum; - enum reload_type type; - int ind_levels; - int is_set_dest; -{ - register RTX_CODE code = GET_CODE (x); - - register char *fmt = GET_RTX_FORMAT (code); - register int i; - - if (code == REG) - { - /* This code is duplicated for speed in find_reloads. */ - register int regno = REGNO (x); - if (reg_equiv_constant[regno] != 0 && !is_set_dest) - x = reg_equiv_constant[regno]; -#if 0 -/* This creates (subreg (mem...)) which would cause an unnecessary - reload of the mem. */ - else if (reg_equiv_mem[regno] != 0) - x = reg_equiv_mem[regno]; -#endif - else if (reg_equiv_address[regno] != 0) - { - /* If reg_equiv_address varies, it may be shared, so copy it. */ - rtx addr = reg_equiv_address[regno]; - - if (rtx_varies_p (addr)) - addr = copy_rtx (addr); - - x = gen_rtx (MEM, GET_MODE (x), addr); - RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (regno_reg_rtx[regno]); - find_reloads_address (GET_MODE (x), NULL_PTR, - XEXP (x, 0), - &XEXP (x, 0), opnum, type, ind_levels); - } - return x; - } - if (code == MEM) - { - rtx tem = x; - find_reloads_address (GET_MODE (x), &tem, XEXP (x, 0), &XEXP (x, 0), - opnum, type, ind_levels); - return tem; - } - - if (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG) - { - /* Check for SUBREG containing a REG that's equivalent to a constant. - If the constant has a known value, truncate it right now. - Similarly if we are extracting a single-word of a multi-word - constant. If the constant is symbolic, allow it to be substituted - normally. push_reload will strip the subreg later. If the - constant is VOIDmode, abort because we will lose the mode of - the register (this should never happen because one of the cases - above should handle it). */ - - register int regno = REGNO (SUBREG_REG (x)); - rtx tem; - - if (subreg_lowpart_p (x) - && regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0 - && reg_equiv_constant[regno] != 0 - && (tem = gen_lowpart_common (GET_MODE (x), - reg_equiv_constant[regno])) != 0) - return tem; - - if (GET_MODE_BITSIZE (GET_MODE (x)) == BITS_PER_WORD - && regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0 - && reg_equiv_constant[regno] != 0 - && (tem = operand_subword (reg_equiv_constant[regno], - SUBREG_WORD (x), 0, - GET_MODE (SUBREG_REG (x)))) != 0) - return tem; - - if (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0 - && reg_equiv_constant[regno] != 0 - && GET_MODE (reg_equiv_constant[regno]) == VOIDmode) - abort (); - - /* If the subreg contains a reg that will be converted to a mem, - convert the subreg to a narrower memref now. - Otherwise, we would get (subreg (mem ...) ...), - which would force reload of the mem. - - We also need to do this if there is an equivalent MEM that is - not offsettable. In that case, alter_subreg would produce an - invalid address on big-endian machines. - - For machines that extend byte loads, we must not reload using - a wider mode if we have a paradoxical SUBREG. find_reloads will - force a reload in that case. So we should not do anything here. */ - - else if (regno >= FIRST_PSEUDO_REGISTER -#if defined(BYTE_LOADS_ZERO_EXTEND) || defined(BYTE_LOADS_SIGN_EXTEND) - && (GET_MODE_SIZE (GET_MODE (x)) - <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) -#endif - && (reg_equiv_address[regno] != 0 - || (reg_equiv_mem[regno] != 0 - && (! strict_memory_address_p (GET_MODE (x), - XEXP (reg_equiv_mem[regno], 0)) - || ! offsettable_memref_p (reg_equiv_mem[regno]))))) - { - int offset = SUBREG_WORD (x) * UNITS_PER_WORD; - rtx addr = (reg_equiv_address[regno] ? reg_equiv_address[regno] - : XEXP (reg_equiv_mem[regno], 0)); -#if BYTES_BIG_ENDIAN - int size; - size = GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))); - offset += MIN (size, UNITS_PER_WORD); - size = GET_MODE_SIZE (GET_MODE (x)); - offset -= MIN (size, UNITS_PER_WORD); -#endif - addr = plus_constant (addr, offset); - x = gen_rtx (MEM, GET_MODE (x), addr); - RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (regno_reg_rtx[regno]); - find_reloads_address (GET_MODE (x), NULL_PTR, - XEXP (x, 0), - &XEXP (x, 0), opnum, type, ind_levels); - } - - } - - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - XEXP (x, i) = find_reloads_toplev (XEXP (x, i), opnum, type, - ind_levels, is_set_dest); - } - return x; -} - -static rtx -make_memloc (ad, regno) - rtx ad; - int regno; -{ - register int i; - rtx tem = reg_equiv_address[regno]; - for (i = 0; i < n_memlocs; i++) - if (rtx_equal_p (tem, XEXP (memlocs[i], 0))) - return memlocs[i]; - - /* If TEM might contain a pseudo, we must copy it to avoid - modifying it when we do the substitution for the reload. */ - if (rtx_varies_p (tem)) - tem = copy_rtx (tem); - - tem = gen_rtx (MEM, GET_MODE (ad), tem); - RTX_UNCHANGING_P (tem) = RTX_UNCHANGING_P (regno_reg_rtx[regno]); - memlocs[n_memlocs++] = tem; - return tem; -} - -/* Record all reloads needed for handling memory address AD - which appears in *LOC in a memory reference to mode MODE - which itself is found in location *MEMREFLOC. - Note that we take shortcuts assuming that no multi-reg machine mode - occurs as part of an address. - - OPNUM and TYPE specify the purpose of this reload. - - IND_LEVELS says how many levels of indirect addressing this machine - supports. - - Value is nonzero if this address is reloaded or replaced as a whole. - This is interesting to the caller if the address is an autoincrement. - - Note that there is no verification that the address will be valid after - this routine does its work. Instead, we rely on the fact that the address - was valid when reload started. So we need only undo things that reload - could have broken. These are wrong register types, pseudos not allocated - to a hard register, and frame pointer elimination. */ - -static int -find_reloads_address (mode, memrefloc, ad, loc, opnum, type, ind_levels) - enum machine_mode mode; - rtx *memrefloc; - rtx ad; - rtx *loc; - int opnum; - enum reload_type type; - int ind_levels; -{ - register int regno; - rtx tem; - - /* If the address is a register, see if it is a legitimate address and - reload if not. We first handle the cases where we need not reload - or where we must reload in a non-standard way. */ - - if (GET_CODE (ad) == REG) - { - regno = REGNO (ad); - - if (reg_equiv_constant[regno] != 0 - && strict_memory_address_p (mode, reg_equiv_constant[regno])) - { - *loc = ad = reg_equiv_constant[regno]; - return 1; - } - - else if (reg_equiv_address[regno] != 0) - { - tem = make_memloc (ad, regno); - find_reloads_address (GET_MODE (tem), NULL_PTR, XEXP (tem, 0), - &XEXP (tem, 0), opnum, type, ind_levels); - push_reload (tem, NULL_RTX, loc, NULL_PTR, BASE_REG_CLASS, - GET_MODE (ad), VOIDmode, 0, 0, - opnum, type); - return 1; - } - - /* We can avoid a reload if the register's equivalent memory expression - is valid as an indirect memory address. */ - - else if (reg_equiv_mem[regno] != 0 && ind_levels > 0 - && strict_memory_address_p (mode, reg_equiv_mem[regno])) - return 0; - - /* The only remaining case where we can avoid a reload is if this is a - hard register that is valid as a base register and which is not the - subject of a CLOBBER in this insn. */ - - else if (regno < FIRST_PSEUDO_REGISTER && REGNO_OK_FOR_BASE_P (regno) - && ! regno_clobbered_p (regno, this_insn)) - return 0; - - /* If we do not have one of the cases above, we must do the reload. */ - push_reload (ad, NULL_RTX, loc, NULL_PTR, BASE_REG_CLASS, - GET_MODE (ad), VOIDmode, 0, 0, opnum, type); - return 1; - } - - if (strict_memory_address_p (mode, ad)) - { - /* The address appears valid, so reloads are not needed. - But the address may contain an eliminable register. - This can happen because a machine with indirect addressing - may consider a pseudo register by itself a valid address even when - it has failed to get a hard reg. - So do a tree-walk to find and eliminate all such regs. */ - - /* But first quickly dispose of a common case. */ - if (GET_CODE (ad) == PLUS - && GET_CODE (XEXP (ad, 1)) == CONST_INT - && GET_CODE (XEXP (ad, 0)) == REG - && reg_equiv_constant[REGNO (XEXP (ad, 0))] == 0) - return 0; - - subst_reg_equivs_changed = 0; - *loc = subst_reg_equivs (ad); - - if (! subst_reg_equivs_changed) - return 0; - - /* Check result for validity after substitution. */ - if (strict_memory_address_p (mode, ad)) - return 0; - } - - /* The address is not valid. We have to figure out why. One possibility - is that it is itself a MEM. This can happen when the frame pointer is - being eliminated, a pseudo is not allocated to a hard register, and the - offset between the frame and stack pointers is not its initial value. - In that case the pseudo will have been replaced by a MEM referring to - the stack pointer. */ - if (GET_CODE (ad) == MEM) - { - /* First ensure that the address in this MEM is valid. Then, unless - indirect addresses are valid, reload the MEM into a register. */ - tem = ad; - find_reloads_address (GET_MODE (ad), &tem, XEXP (ad, 0), &XEXP (ad, 0), - opnum, type, ind_levels == 0 ? 0 : ind_levels - 1); - - /* If tem was changed, then we must create a new memory reference to - hold it and store it back into memrefloc. */ - if (tem != ad && memrefloc) - { - *memrefloc = copy_rtx (*memrefloc); - copy_replacements (tem, XEXP (*memrefloc, 0)); - loc = &XEXP (*memrefloc, 0); - } - - /* Check similar cases as for indirect addresses as above except - that we can allow pseudos and a MEM since they should have been - taken care of above. */ - - if (ind_levels == 0 - || (GET_CODE (XEXP (tem, 0)) == SYMBOL_REF && ! indirect_symref_ok) - || GET_CODE (XEXP (tem, 0)) == MEM - || ! (GET_CODE (XEXP (tem, 0)) == REG - || (GET_CODE (XEXP (tem, 0)) == PLUS - && GET_CODE (XEXP (XEXP (tem, 0), 0)) == REG - && GET_CODE (XEXP (XEXP (tem, 0), 1)) == CONST_INT))) - { - /* Must use TEM here, not AD, since it is the one that will - have any subexpressions reloaded, if needed. */ - push_reload (tem, NULL_RTX, loc, NULL_PTR, - BASE_REG_CLASS, GET_MODE (tem), VOIDmode, 0, - 0, opnum, type); - return 1; - } - else - return 0; - } - - /* If we have address of a stack slot but it's not valid - (displacement is too large), compute the sum in a register. */ - else if (GET_CODE (ad) == PLUS - && (XEXP (ad, 0) == frame_pointer_rtx -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - || XEXP (ad, 0) == arg_pointer_rtx -#endif - || XEXP (ad, 0) == stack_pointer_rtx) - && GET_CODE (XEXP (ad, 1)) == CONST_INT) - { - /* Unshare the MEM rtx so we can safely alter it. */ - if (memrefloc) - { - rtx oldref = *memrefloc; - *memrefloc = copy_rtx (*memrefloc); - loc = &XEXP (*memrefloc, 0); - } - if (double_reg_address_ok) - { - /* Unshare the sum as well. */ - *loc = ad = copy_rtx (ad); - /* Reload the displacement into an index reg. - We assume the frame pointer or arg pointer is a base reg. */ - find_reloads_address_part (XEXP (ad, 1), &XEXP (ad, 1), - INDEX_REG_CLASS, GET_MODE (ad), opnum, - type, ind_levels); - } - else - { - /* If the sum of two regs is not necessarily valid, - reload the sum into a base reg. - That will at least work. */ - find_reloads_address_part (ad, loc, BASE_REG_CLASS, Pmode, - opnum, type, ind_levels); - } - return 1; - } - - /* If we have an indexed stack slot, there are three possible reasons why - it might be invalid: The index might need to be reloaded, the address - might have been made by frame pointer elimination and hence have a - constant out of range, or both reasons might apply. - - We can easily check for an index needing reload, but even if that is the - case, we might also have an invalid constant. To avoid making the - conservative assumption and requiring two reloads, we see if this address - is valid when not interpreted strictly. If it is, the only problem is - that the index needs a reload and find_reloads_address_1 will take care - of it. - - There is still a case when we might generate an extra reload, - however. In certain cases eliminate_regs will return a MEM for a REG - (see the code there for details). In those cases, memory_address_p - applied to our address will return 0 so we will think that our offset - must be too large. But it might indeed be valid and the only problem - is that a MEM is present where a REG should be. This case should be - very rare and there doesn't seem to be any way to avoid it. - - If we decide to do something here, it must be that - `double_reg_address_ok' is true and that this address rtl was made by - eliminate_regs. We generate a reload of the fp/sp/ap + constant and - rework the sum so that the reload register will be added to the index. - This is safe because we know the address isn't shared. - - We check for fp/ap/sp as both the first and second operand of the - innermost PLUS. */ - - else if (GET_CODE (ad) == PLUS && GET_CODE (XEXP (ad, 1)) == CONST_INT - && GET_CODE (XEXP (ad, 0)) == PLUS - && (XEXP (XEXP (ad, 0), 0) == frame_pointer_rtx -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - || XEXP (XEXP (ad, 0), 0) == arg_pointer_rtx -#endif - || XEXP (XEXP (ad, 0), 0) == stack_pointer_rtx) - && ! memory_address_p (mode, ad)) - { - *loc = ad = gen_rtx (PLUS, GET_MODE (ad), - plus_constant (XEXP (XEXP (ad, 0), 0), - INTVAL (XEXP (ad, 1))), - XEXP (XEXP (ad, 0), 1)); - find_reloads_address_part (XEXP (ad, 0), &XEXP (ad, 0), BASE_REG_CLASS, - GET_MODE (ad), opnum, type, ind_levels); - find_reloads_address_1 (XEXP (ad, 1), 1, &XEXP (ad, 1), opnum, type, 0); - - return 1; - } - - else if (GET_CODE (ad) == PLUS && GET_CODE (XEXP (ad, 1)) == CONST_INT - && GET_CODE (XEXP (ad, 0)) == PLUS - && (XEXP (XEXP (ad, 0), 1) == frame_pointer_rtx -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - || XEXP (XEXP (ad, 0), 1) == arg_pointer_rtx -#endif - || XEXP (XEXP (ad, 0), 1) == stack_pointer_rtx) - && ! memory_address_p (mode, ad)) - { - *loc = ad = gen_rtx (PLUS, GET_MODE (ad), - plus_constant (XEXP (XEXP (ad, 0), 1), - INTVAL (XEXP (ad, 1))), - XEXP (XEXP (ad, 0), 0)); - find_reloads_address_part (XEXP (ad, 0), &XEXP (ad, 0), BASE_REG_CLASS, - GET_MODE (ad), opnum, type, ind_levels); - find_reloads_address_1 (XEXP (ad, 1), 1, &XEXP (ad, 1), opnum, type, 0); - - return 1; - } - - /* See if address becomes valid when an eliminable register - in a sum is replaced. */ - - tem = ad; - if (GET_CODE (ad) == PLUS) - tem = subst_indexed_address (ad); - if (tem != ad && strict_memory_address_p (mode, tem)) - { - /* Ok, we win that way. Replace any additional eliminable - registers. */ - - subst_reg_equivs_changed = 0; - tem = subst_reg_equivs (tem); - - /* Make sure that didn't make the address invalid again. */ - - if (! subst_reg_equivs_changed || strict_memory_address_p (mode, tem)) - { - *loc = tem; - return 0; - } - } - - /* If constants aren't valid addresses, reload the constant address - into a register. */ - if (CONSTANT_P (ad) && ! strict_memory_address_p (mode, ad)) - { - /* If AD is in address in the constant pool, the MEM rtx may be shared. - Unshare it so we can safely alter it. */ - if (memrefloc && GET_CODE (ad) == SYMBOL_REF - && CONSTANT_POOL_ADDRESS_P (ad)) - { - rtx oldref = *memrefloc; - *memrefloc = copy_rtx (*memrefloc); - loc = &XEXP (*memrefloc, 0); - } - - find_reloads_address_part (ad, loc, BASE_REG_CLASS, Pmode, opnum, type, - ind_levels); - return 1; - } - - return find_reloads_address_1 (ad, 0, loc, opnum, type, ind_levels); -} - -/* Find all pseudo regs appearing in AD - that are eliminable in favor of equivalent values - and do not have hard regs; replace them by their equivalents. */ - -static rtx -subst_reg_equivs (ad) - rtx ad; -{ - register RTX_CODE code = GET_CODE (ad); - register int i; - register char *fmt; - - switch (code) - { - case HIGH: - case CONST_INT: - case CONST: - case CONST_DOUBLE: - case SYMBOL_REF: - case LABEL_REF: - case PC: - case CC0: - return ad; - - case REG: - { - register int regno = REGNO (ad); - - if (reg_equiv_constant[regno] != 0) - { - subst_reg_equivs_changed = 1; - return reg_equiv_constant[regno]; - } - } - return ad; - - case PLUS: - /* Quickly dispose of a common case. */ - if (XEXP (ad, 0) == frame_pointer_rtx - && GET_CODE (XEXP (ad, 1)) == CONST_INT) - return ad; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - XEXP (ad, i) = subst_reg_equivs (XEXP (ad, i)); - return ad; -} - -/* Compute the sum of X and Y, making canonicalizations assumed in an - address, namely: sum constant integers, surround the sum of two - constants with a CONST, put the constant as the second operand, and - group the constant on the outermost sum. - - This routine assumes both inputs are already in canonical form. */ - -rtx -form_sum (x, y) - rtx x, y; -{ - rtx tem; - enum machine_mode mode = GET_MODE (x); - - if (mode == VOIDmode) - mode = GET_MODE (y); - - if (mode == VOIDmode) - mode = Pmode; - - if (GET_CODE (x) == CONST_INT) - return plus_constant (y, INTVAL (x)); - else if (GET_CODE (y) == CONST_INT) - return plus_constant (x, INTVAL (y)); - else if (CONSTANT_P (x)) - tem = x, x = y, y = tem; - - if (GET_CODE (x) == PLUS && CONSTANT_P (XEXP (x, 1))) - return form_sum (XEXP (x, 0), form_sum (XEXP (x, 1), y)); - - /* Note that if the operands of Y are specified in the opposite - order in the recursive calls below, infinite recursion will occur. */ - if (GET_CODE (y) == PLUS && CONSTANT_P (XEXP (y, 1))) - return form_sum (form_sum (x, XEXP (y, 0)), XEXP (y, 1)); - - /* If both constant, encapsulate sum. Otherwise, just form sum. A - constant will have been placed second. */ - if (CONSTANT_P (x) && CONSTANT_P (y)) - { - if (GET_CODE (x) == CONST) - x = XEXP (x, 0); - if (GET_CODE (y) == CONST) - y = XEXP (y, 0); - - return gen_rtx (CONST, VOIDmode, gen_rtx (PLUS, mode, x, y)); - } - - return gen_rtx (PLUS, mode, x, y); -} - -/* If ADDR is a sum containing a pseudo register that should be - replaced with a constant (from reg_equiv_constant), - return the result of doing so, and also apply the associative - law so that the result is more likely to be a valid address. - (But it is not guaranteed to be one.) - - Note that at most one register is replaced, even if more are - replaceable. Also, we try to put the result into a canonical form - so it is more likely to be a valid address. - - In all other cases, return ADDR. */ - -static rtx -subst_indexed_address (addr) - rtx addr; -{ - rtx op0 = 0, op1 = 0, op2 = 0; - rtx tem; - int regno; - - if (GET_CODE (addr) == PLUS) - { - /* Try to find a register to replace. */ - op0 = XEXP (addr, 0), op1 = XEXP (addr, 1), op2 = 0; - if (GET_CODE (op0) == REG - && (regno = REGNO (op0)) >= FIRST_PSEUDO_REGISTER - && reg_renumber[regno] < 0 - && reg_equiv_constant[regno] != 0) - op0 = reg_equiv_constant[regno]; - else if (GET_CODE (op1) == REG - && (regno = REGNO (op1)) >= FIRST_PSEUDO_REGISTER - && reg_renumber[regno] < 0 - && reg_equiv_constant[regno] != 0) - op1 = reg_equiv_constant[regno]; - else if (GET_CODE (op0) == PLUS - && (tem = subst_indexed_address (op0)) != op0) - op0 = tem; - else if (GET_CODE (op1) == PLUS - && (tem = subst_indexed_address (op1)) != op1) - op1 = tem; - else - return addr; - - /* Pick out up to three things to add. */ - if (GET_CODE (op1) == PLUS) - op2 = XEXP (op1, 1), op1 = XEXP (op1, 0); - else if (GET_CODE (op0) == PLUS) - op2 = op1, op1 = XEXP (op0, 1), op0 = XEXP (op0, 0); - - /* Compute the sum. */ - if (op2 != 0) - op1 = form_sum (op1, op2); - if (op1 != 0) - op0 = form_sum (op0, op1); - - return op0; - } - return addr; -} - -/* Record the pseudo registers we must reload into hard registers - in a subexpression of a would-be memory address, X. - (This function is not called if the address we find is strictly valid.) - CONTEXT = 1 means we are considering regs as index regs, - = 0 means we are considering them as base regs. - - OPNUM and TYPE specify the purpose of any reloads made. - - IND_LEVELS says how many levels of indirect addressing are - supported at this point in the address. - - We return nonzero if X, as a whole, is reloaded or replaced. */ - -/* Note that we take shortcuts assuming that no multi-reg machine mode - occurs as part of an address. - Also, this is not fully machine-customizable; it works for machines - such as vaxes and 68000's and 32000's, but other possible machines - could have addressing modes that this does not handle right. */ - -static int -find_reloads_address_1 (x, context, loc, opnum, type, ind_levels) - rtx x; - int context; - rtx *loc; - int opnum; - enum reload_type type; - int ind_levels; -{ - register RTX_CODE code = GET_CODE (x); - - if (code == PLUS) - { - register rtx op0 = XEXP (x, 0); - register rtx op1 = XEXP (x, 1); - register RTX_CODE code0 = GET_CODE (op0); - register RTX_CODE code1 = GET_CODE (op1); - if (code0 == MULT || code0 == SIGN_EXTEND || code1 == MEM) - { - find_reloads_address_1 (op0, 1, &XEXP (x, 0), opnum, type, - ind_levels); - find_reloads_address_1 (op1, 0, &XEXP (x, 1), opnum, type, - ind_levels); - } - else if (code1 == MULT || code1 == SIGN_EXTEND || code0 == MEM) - { - find_reloads_address_1 (op0, 0, &XEXP (x, 0), opnum, type, - ind_levels); - find_reloads_address_1 (op1, 1, &XEXP (x, 1), opnum, type, - ind_levels); - } - else if (code0 == CONST_INT || code0 == CONST - || code0 == SYMBOL_REF || code0 == LABEL_REF) - find_reloads_address_1 (op1, 0, &XEXP (x, 1), opnum, type, ind_levels); - else if (code1 == CONST_INT || code1 == CONST - || code1 == SYMBOL_REF || code1 == LABEL_REF) - find_reloads_address_1 (op0, 0, &XEXP (x, 0), opnum, type, ind_levels); - else if (code0 == REG && code1 == REG) - { - if (REG_OK_FOR_INDEX_P (op0) - && REG_OK_FOR_BASE_P (op1)) - return 0; - else if (REG_OK_FOR_INDEX_P (op1) - && REG_OK_FOR_BASE_P (op0)) - return 0; - else if (REG_OK_FOR_BASE_P (op1)) - find_reloads_address_1 (op0, 1, &XEXP (x, 0), opnum, type, - ind_levels); - else if (REG_OK_FOR_BASE_P (op0)) - find_reloads_address_1 (op1, 1, &XEXP (x, 1), opnum, type, - ind_levels); - else if (REG_OK_FOR_INDEX_P (op1)) - find_reloads_address_1 (op0, 0, &XEXP (x, 0), opnum, type, - ind_levels); - else if (REG_OK_FOR_INDEX_P (op0)) - find_reloads_address_1 (op1, 0, &XEXP (x, 1), opnum, type, - ind_levels); - else - { - find_reloads_address_1 (op0, 1, &XEXP (x, 0), opnum, type, - ind_levels); - find_reloads_address_1 (op1, 0, &XEXP (x, 1), opnum, type, - ind_levels); - } - } - else if (code0 == REG) - { - find_reloads_address_1 (op0, 1, &XEXP (x, 0), opnum, type, - ind_levels); - find_reloads_address_1 (op1, 0, &XEXP (x, 1), opnum, type, - ind_levels); - } - else if (code1 == REG) - { - find_reloads_address_1 (op1, 1, &XEXP (x, 1), opnum, type, - ind_levels); - find_reloads_address_1 (op0, 0, &XEXP (x, 0), opnum, type, - ind_levels); - } - } - else if (code == POST_INC || code == POST_DEC - || code == PRE_INC || code == PRE_DEC) - { - if (GET_CODE (XEXP (x, 0)) == REG) - { - register int regno = REGNO (XEXP (x, 0)); - int value = 0; - rtx x_orig = x; - - /* A register that is incremented cannot be constant! */ - if (regno >= FIRST_PSEUDO_REGISTER - && reg_equiv_constant[regno] != 0) - abort (); - - /* Handle a register that is equivalent to a memory location - which cannot be addressed directly. */ - if (reg_equiv_address[regno] != 0) - { - rtx tem = make_memloc (XEXP (x, 0), regno); - /* First reload the memory location's address. */ - find_reloads_address (GET_MODE (tem), 0, XEXP (tem, 0), - &XEXP (tem, 0), opnum, type, ind_levels); - /* Put this inside a new increment-expression. */ - x = gen_rtx (GET_CODE (x), GET_MODE (x), tem); - /* Proceed to reload that, as if it contained a register. */ - } - - /* If we have a hard register that is ok as an index, - don't make a reload. If an autoincrement of a nice register - isn't "valid", it must be that no autoincrement is "valid". - If that is true and something made an autoincrement anyway, - this must be a special context where one is allowed. - (For example, a "push" instruction.) - We can't improve this address, so leave it alone. */ - - /* Otherwise, reload the autoincrement into a suitable hard reg - and record how much to increment by. */ - - if (reg_renumber[regno] >= 0) - regno = reg_renumber[regno]; - if ((regno >= FIRST_PSEUDO_REGISTER - || !(context ? REGNO_OK_FOR_INDEX_P (regno) - : REGNO_OK_FOR_BASE_P (regno)))) - { - register rtx link; - - int reloadnum - = push_reload (x, NULL_RTX, loc, NULL_PTR, - context ? INDEX_REG_CLASS : BASE_REG_CLASS, - GET_MODE (x), GET_MODE (x), VOIDmode, 0, - opnum, type); - reload_inc[reloadnum] - = find_inc_amount (PATTERN (this_insn), XEXP (x_orig, 0)); - - value = 1; - -#ifdef AUTO_INC_DEC - /* Update the REG_INC notes. */ - - for (link = REG_NOTES (this_insn); - link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_INC - && REGNO (XEXP (link, 0)) == REGNO (XEXP (x_orig, 0))) - push_replacement (&XEXP (link, 0), reloadnum, VOIDmode); -#endif - } - return value; - } - else if (GET_CODE (XEXP (x, 0)) == MEM) - { - /* This is probably the result of a substitution, by eliminate_regs, - of an equivalent address for a pseudo that was not allocated to a - hard register. Verify that the specified address is valid and - reload it into a register. */ - rtx tem = XEXP (x, 0); - register rtx link; - int reloadnum; - - /* Since we know we are going to reload this item, don't decrement - for the indirection level. - - Note that this is actually conservative: it would be slightly - more efficient to use the value of SPILL_INDIRECT_LEVELS from - reload1.c here. */ - find_reloads_address (GET_MODE (x), &XEXP (x, 0), - XEXP (XEXP (x, 0), 0), &XEXP (XEXP (x, 0), 0), - opnum, type, ind_levels); - - reloadnum = push_reload (x, NULL_RTX, loc, NULL_PTR, - context ? INDEX_REG_CLASS : BASE_REG_CLASS, - GET_MODE (x), VOIDmode, 0, 0, opnum, type); - reload_inc[reloadnum] - = find_inc_amount (PATTERN (this_insn), XEXP (x, 0)); - - link = FIND_REG_INC_NOTE (this_insn, tem); - if (link != 0) - push_replacement (&XEXP (link, 0), reloadnum, VOIDmode); - - return 1; - } - } - else if (code == MEM) - { - /* This is probably the result of a substitution, by eliminate_regs, - of an equivalent address for a pseudo that was not allocated to a - hard register. Verify that the specified address is valid and reload - it into a register. - - Since we know we are going to reload this item, don't decrement - for the indirection level. - - Note that this is actually conservative: it would be slightly more - efficient to use the value of SPILL_INDIRECT_LEVELS from - reload1.c here. */ - - find_reloads_address (GET_MODE (x), loc, XEXP (x, 0), &XEXP (x, 0), - opnum, type, ind_levels); - - push_reload (*loc, NULL_RTX, loc, NULL_PTR, - context ? INDEX_REG_CLASS : BASE_REG_CLASS, - GET_MODE (x), VOIDmode, 0, 0, opnum, type); - return 1; - } - else if (code == REG) - { - register int regno = REGNO (x); - - if (reg_equiv_constant[regno] != 0) - { - find_reloads_address_part (reg_equiv_constant[regno], loc, - (context ? INDEX_REG_CLASS - : BASE_REG_CLASS), - GET_MODE (x), opnum, type, ind_levels); - return 1; - } - -#if 0 /* This might screw code in reload1.c to delete prior output-reload - that feeds this insn. */ - if (reg_equiv_mem[regno] != 0) - { - push_reload (reg_equiv_mem[regno], NULL_RTX, loc, NULL_PTR, - context ? INDEX_REG_CLASS : BASE_REG_CLASS, - GET_MODE (x), VOIDmode, 0, 0, opnum, type); - return 1; - } -#endif - if (reg_equiv_address[regno] != 0) - { - x = make_memloc (x, regno); - find_reloads_address (GET_MODE (x), 0, XEXP (x, 0), &XEXP (x, 0), - opnum, type, ind_levels); - } - - if (reg_renumber[regno] >= 0) - regno = reg_renumber[regno]; - if ((regno >= FIRST_PSEUDO_REGISTER - || !(context ? REGNO_OK_FOR_INDEX_P (regno) - : REGNO_OK_FOR_BASE_P (regno)))) - { - push_reload (x, NULL_RTX, loc, NULL_PTR, - context ? INDEX_REG_CLASS : BASE_REG_CLASS, - GET_MODE (x), VOIDmode, 0, 0, opnum, type); - return 1; - } - - /* If a register appearing in an address is the subject of a CLOBBER - in this insn, reload it into some other register to be safe. - The CLOBBER is supposed to make the register unavailable - from before this insn to after it. */ - if (regno_clobbered_p (regno, this_insn)) - { - push_reload (x, NULL_RTX, loc, NULL_PTR, - context ? INDEX_REG_CLASS : BASE_REG_CLASS, - GET_MODE (x), VOIDmode, 0, 0, opnum, type); - return 1; - } - } - else - { - register char *fmt = GET_RTX_FORMAT (code); - register int i; - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - find_reloads_address_1 (XEXP (x, i), context, &XEXP (x, i), - opnum, type, ind_levels); - } - } - - return 0; -} - -/* X, which is found at *LOC, is a part of an address that needs to be - reloaded into a register of class CLASS. If X is a constant, or if - X is a PLUS that contains a constant, check that the constant is a - legitimate operand and that we are supposed to be able to load - it into the register. - - If not, force the constant into memory and reload the MEM instead. - - MODE is the mode to use, in case X is an integer constant. - - OPNUM and TYPE describe the purpose of any reloads made. - - IND_LEVELS says how many levels of indirect addressing this machine - supports. */ - -static void -find_reloads_address_part (x, loc, class, mode, opnum, type, ind_levels) - rtx x; - rtx *loc; - enum reg_class class; - enum machine_mode mode; - int opnum; - enum reload_type type; - int ind_levels; -{ - if (CONSTANT_P (x) - && (! LEGITIMATE_CONSTANT_P (x) - || PREFERRED_RELOAD_CLASS (x, class) == NO_REGS)) - { - rtx tem = x = force_const_mem (mode, x); - find_reloads_address (mode, &tem, XEXP (tem, 0), &XEXP (tem, 0), - opnum, type, ind_levels); - } - - else if (GET_CODE (x) == PLUS - && CONSTANT_P (XEXP (x, 1)) - && (! LEGITIMATE_CONSTANT_P (XEXP (x, 1)) - || PREFERRED_RELOAD_CLASS (XEXP (x, 1), class) == NO_REGS)) - { - rtx tem = force_const_mem (GET_MODE (x), XEXP (x, 1)); - - x = gen_rtx (PLUS, GET_MODE (x), XEXP (x, 0), tem); - find_reloads_address (mode, &tem, XEXP (tem, 0), &XEXP (tem, 0), - opnum, type, ind_levels); - } - - push_reload (x, NULL_RTX, loc, NULL_PTR, class, - mode, VOIDmode, 0, 0, opnum, type); -} - -/* Substitute into the current INSN the registers into which we have reloaded - the things that need reloading. The array `replacements' - says contains the locations of all pointers that must be changed - and says what to replace them with. - - Return the rtx that X translates into; usually X, but modified. */ - -void -subst_reloads () -{ - register int i; - - for (i = 0; i < n_replacements; i++) - { - register struct replacement *r = &replacements[i]; - register rtx reloadreg = reload_reg_rtx[r->what]; - if (reloadreg) - { - /* Encapsulate RELOADREG so its machine mode matches what - used to be there. */ - if (GET_MODE (reloadreg) != r->mode && r->mode != VOIDmode) - reloadreg = gen_lowpart_common (r->mode, reloadreg); - - /* If we are putting this into a SUBREG and RELOADREG is a - SUBREG, we would be making nested SUBREGs, so we have to fix - this up. Note that r->where == &SUBREG_REG (*r->subreg_loc). */ - - if (r->subreg_loc != 0 && GET_CODE (reloadreg) == SUBREG) - { - if (GET_MODE (*r->subreg_loc) - == GET_MODE (SUBREG_REG (reloadreg))) - *r->subreg_loc = SUBREG_REG (reloadreg); - else - { - *r->where = SUBREG_REG (reloadreg); - SUBREG_WORD (*r->subreg_loc) += SUBREG_WORD (reloadreg); - } - } - else - *r->where = reloadreg; - } - /* If reload got no reg and isn't optional, something's wrong. */ - else if (! reload_optional[r->what]) - abort (); - } -} - -/* Make a copy of any replacements being done into X and move those copies - to locations in Y, a copy of X. We only look at the highest level of - the RTL. */ - -void -copy_replacements (x, y) - rtx x; - rtx y; -{ - int i, j; - enum rtx_code code = GET_CODE (x); - char *fmt = GET_RTX_FORMAT (code); - struct replacement *r; - - /* We can't support X being a SUBREG because we might then need to know its - location if something inside it was replaced. */ - if (code == SUBREG) - abort (); - - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - for (j = 0; j < n_replacements; j++) - { - if (replacements[j].subreg_loc == &XEXP (x, i)) - { - r = &replacements[n_replacements++]; - r->where = replacements[j].where; - r->subreg_loc = &XEXP (y, i); - r->what = replacements[j].what; - r->mode = replacements[j].mode; - } - else if (replacements[j].where == &XEXP (x, i)) - { - r = &replacements[n_replacements++]; - r->where = &XEXP (y, i); - r->subreg_loc = 0; - r->what = replacements[j].what; - r->mode = replacements[j].mode; - } - } -} - -/* If LOC was scheduled to be replaced by something, return the replacement. - Otherwise, return *LOC. */ - -rtx -find_replacement (loc) - rtx *loc; -{ - struct replacement *r; - - for (r = &replacements[0]; r < &replacements[n_replacements]; r++) - { - rtx reloadreg = reload_reg_rtx[r->what]; - - if (reloadreg && r->where == loc) - { - if (r->mode != VOIDmode && GET_MODE (reloadreg) != r->mode) - reloadreg = gen_rtx (REG, r->mode, REGNO (reloadreg)); - - return reloadreg; - } - else if (reloadreg && r->subreg_loc == loc) - { - /* RELOADREG must be either a REG or a SUBREG. - - ??? Is it actually still ever a SUBREG? If so, why? */ - - if (GET_CODE (reloadreg) == REG) - return gen_rtx (REG, GET_MODE (*loc), - REGNO (reloadreg) + SUBREG_WORD (*loc)); - else if (GET_MODE (reloadreg) == GET_MODE (*loc)) - return reloadreg; - else - return gen_rtx (SUBREG, GET_MODE (*loc), SUBREG_REG (reloadreg), - SUBREG_WORD (reloadreg) + SUBREG_WORD (*loc)); - } - } - - return *loc; -} - -/* Return nonzero if register in range [REGNO, ENDREGNO) - appears either explicitly or implicitly in X - other than being stored into. - - References contained within the substructure at LOC do not count. - LOC may be zero, meaning don't ignore anything. - - This is similar to refers_to_regno_p in rtlanal.c except that we - look at equivalences for pseudos that didn't get hard registers. */ - -int -refers_to_regno_for_reload_p (regno, endregno, x, loc) - int regno, endregno; - rtx x; - rtx *loc; -{ - register int i; - register RTX_CODE code; - register char *fmt; - - if (x == 0) - return 0; - - repeat: - code = GET_CODE (x); - - switch (code) - { - case REG: - i = REGNO (x); - - /* If this is a pseudo, a hard register must not have been allocated. - X must therefore either be a constant or be in memory. */ - if (i >= FIRST_PSEUDO_REGISTER) - { - if (reg_equiv_memory_loc[i]) - return refers_to_regno_for_reload_p (regno, endregno, - reg_equiv_memory_loc[i], - NULL_PTR); - - if (reg_equiv_constant[i]) - return 0; - - abort (); - } - - return (endregno > i - && regno < i + (i < FIRST_PSEUDO_REGISTER - ? HARD_REGNO_NREGS (i, GET_MODE (x)) - : 1)); - - case SUBREG: - /* If this is a SUBREG of a hard reg, we can see exactly which - registers are being modified. Otherwise, handle normally. */ - if (GET_CODE (SUBREG_REG (x)) == REG - && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER) - { - int inner_regno = REGNO (SUBREG_REG (x)) + SUBREG_WORD (x); - int inner_endregno - = inner_regno + (inner_regno < FIRST_PSEUDO_REGISTER - ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1); - - return endregno > inner_regno && regno < inner_endregno; - } - break; - - case CLOBBER: - case SET: - if (&SET_DEST (x) != loc - /* Note setting a SUBREG counts as referring to the REG it is in for - a pseudo but not for hard registers since we can - treat each word individually. */ - && ((GET_CODE (SET_DEST (x)) == SUBREG - && loc != &SUBREG_REG (SET_DEST (x)) - && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG - && REGNO (SUBREG_REG (SET_DEST (x))) >= FIRST_PSEUDO_REGISTER - && refers_to_regno_for_reload_p (regno, endregno, - SUBREG_REG (SET_DEST (x)), - loc)) - || (GET_CODE (SET_DEST (x)) != REG - && refers_to_regno_for_reload_p (regno, endregno, - SET_DEST (x), loc)))) - return 1; - - if (code == CLOBBER || loc == &SET_SRC (x)) - return 0; - x = SET_SRC (x); - goto repeat; - } - - /* X does not match, so try its subexpressions. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e' && loc != &XEXP (x, i)) - { - if (i == 0) - { - x = XEXP (x, 0); - goto repeat; - } - else - if (refers_to_regno_for_reload_p (regno, endregno, - XEXP (x, i), loc)) - return 1; - } - else if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >=0; j--) - if (loc != &XVECEXP (x, i, j) - && refers_to_regno_for_reload_p (regno, endregno, - XVECEXP (x, i, j), loc)) - return 1; - } - } - return 0; -} - -/* Nonzero if modifying X will affect IN. If X is a register or a SUBREG, - we check if any register number in X conflicts with the relevant register - numbers. If X is a constant, return 0. If X is a MEM, return 1 iff IN - contains a MEM (we don't bother checking for memory addresses that can't - conflict because we expect this to be a rare case. - - This function is similar to reg_overlap_mention_p in rtlanal.c except - that we look at equivalences for pseudos that didn't get hard registers. */ - -int -reg_overlap_mentioned_for_reload_p (x, in) - rtx x, in; -{ - int regno, endregno; - - if (GET_CODE (x) == SUBREG) - { - regno = REGNO (SUBREG_REG (x)); - if (regno < FIRST_PSEUDO_REGISTER) - regno += SUBREG_WORD (x); - } - else if (GET_CODE (x) == REG) - { - regno = REGNO (x); - - /* If this is a pseudo, it must not have been assigned a hard register. - Therefore, it must either be in memory or be a constant. */ - - if (regno >= FIRST_PSEUDO_REGISTER) - { - if (reg_equiv_memory_loc[regno]) - return refers_to_mem_for_reload_p (in); - else if (reg_equiv_constant[regno]) - return 0; - abort (); - } - } - else if (CONSTANT_P (x)) - return 0; - else if (GET_CODE (x) == MEM) - return refers_to_mem_for_reload_p (in); - else if (GET_CODE (x) == SCRATCH || GET_CODE (x) == PC - || GET_CODE (x) == CC0) - return reg_mentioned_p (x, in); - else - abort (); - - endregno = regno + (regno < FIRST_PSEUDO_REGISTER - ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1); - - return refers_to_regno_for_reload_p (regno, endregno, in, NULL_PTR); -} - -/* Return nonzero if anything in X contains a MEM. Look also for pseudo - registers. */ - -int -refers_to_mem_for_reload_p (x) - rtx x; -{ - char *fmt; - int i; - - if (GET_CODE (x) == MEM) - return 1; - - if (GET_CODE (x) == REG) - return (REGNO (x) >= FIRST_PSEUDO_REGISTER - && reg_equiv_memory_loc[REGNO (x)]); - - fmt = GET_RTX_FORMAT (GET_CODE (x)); - for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) - if (fmt[i] == 'e' - && (GET_CODE (XEXP (x, i)) == MEM - || refers_to_mem_for_reload_p (XEXP (x, i)))) - return 1; - - return 0; -} - -/* Check the insns before INSN to see if there is a suitable register - containing the same value as GOAL. - If OTHER is -1, look for a register in class CLASS. - Otherwise, just see if register number OTHER shares GOAL's value. - - Return an rtx for the register found, or zero if none is found. - - If RELOAD_REG_P is (short *)1, - we reject any hard reg that appears in reload_reg_rtx - because such a hard reg is also needed coming into this insn. - - If RELOAD_REG_P is any other nonzero value, - it is a vector indexed by hard reg number - and we reject any hard reg whose element in the vector is nonnegative - as well as any that appears in reload_reg_rtx. - - If GOAL is zero, then GOALREG is a register number; we look - for an equivalent for that register. - - MODE is the machine mode of the value we want an equivalence for. - If GOAL is nonzero and not VOIDmode, then it must have mode MODE. - - This function is used by jump.c as well as in the reload pass. - - If GOAL is the sum of the stack pointer and a constant, we treat it - as if it were a constant except that sp is required to be unchanging. */ - -rtx -find_equiv_reg (goal, insn, class, other, reload_reg_p, goalreg, mode) - register rtx goal; - rtx insn; - enum reg_class class; - register int other; - short *reload_reg_p; - int goalreg; - enum machine_mode mode; -{ - register rtx p = insn; - rtx goaltry, valtry, value, where; - register rtx pat; - register int regno = -1; - int valueno; - int goal_mem = 0; - int goal_const = 0; - int goal_mem_addr_varies = 0; - int need_stable_sp = 0; - int nregs; - int valuenregs; - - if (goal == 0) - regno = goalreg; - else if (GET_CODE (goal) == REG) - regno = REGNO (goal); - else if (GET_CODE (goal) == MEM) - { - enum rtx_code code = GET_CODE (XEXP (goal, 0)); - if (MEM_VOLATILE_P (goal)) - return 0; - if (flag_float_store && GET_MODE_CLASS (GET_MODE (goal)) == MODE_FLOAT) - return 0; - /* An address with side effects must be reexecuted. */ - switch (code) - { - case POST_INC: - case PRE_INC: - case POST_DEC: - case PRE_DEC: - return 0; - } - goal_mem = 1; - } - else if (CONSTANT_P (goal)) - goal_const = 1; - else if (GET_CODE (goal) == PLUS - && XEXP (goal, 0) == stack_pointer_rtx - && CONSTANT_P (XEXP (goal, 1))) - goal_const = need_stable_sp = 1; - else - return 0; - - /* On some machines, certain regs must always be rejected - because they don't behave the way ordinary registers do. */ - -#ifdef OVERLAPPING_REGNO_P - if (regno >= 0 && regno < FIRST_PSEUDO_REGISTER - && OVERLAPPING_REGNO_P (regno)) - return 0; -#endif - - /* Scan insns back from INSN, looking for one that copies - a value into or out of GOAL. - Stop and give up if we reach a label. */ - - while (1) - { - p = PREV_INSN (p); - if (p == 0 || GET_CODE (p) == CODE_LABEL) - return 0; - if (GET_CODE (p) == INSN - /* If we don't want spill regs ... */ - && (! (reload_reg_p != 0 - && reload_reg_p != (short *) (HOST_WIDE_INT) 1) - /* ... then ignore insns introduced by reload; they aren't useful - and can cause results in reload_as_needed to be different - from what they were when calculating the need for spills. - If we notice an input-reload insn here, we will reject it below, - but it might hide a usable equivalent. That makes bad code. - It may even abort: perhaps no reg was spilled for this insn - because it was assumed we would find that equivalent. */ - || INSN_UID (p) < reload_first_uid)) - { - rtx tem; - pat = single_set (p); - /* First check for something that sets some reg equal to GOAL. */ - if (pat != 0 - && ((regno >= 0 - && true_regnum (SET_SRC (pat)) == regno - && (valueno = true_regnum (valtry = SET_DEST (pat))) >= 0) - || - (regno >= 0 - && true_regnum (SET_DEST (pat)) == regno - && (valueno = true_regnum (valtry = SET_SRC (pat))) >= 0) - || - (goal_const && rtx_equal_p (SET_SRC (pat), goal) - && (valueno = true_regnum (valtry = SET_DEST (pat))) >= 0) - || (goal_mem - && (valueno = true_regnum (valtry = SET_DEST (pat))) >= 0 - && rtx_renumbered_equal_p (goal, SET_SRC (pat))) - || (goal_mem - && (valueno = true_regnum (valtry = SET_SRC (pat))) >= 0 - && rtx_renumbered_equal_p (goal, SET_DEST (pat))) - /* If we are looking for a constant, - and something equivalent to that constant was copied - into a reg, we can use that reg. */ - || (goal_const && (tem = find_reg_note (p, REG_EQUIV, - NULL_RTX)) - && rtx_equal_p (XEXP (tem, 0), goal) - && (valueno = true_regnum (valtry = SET_DEST (pat))) >= 0) - || (goal_const && (tem = find_reg_note (p, REG_EQUIV, - NULL_RTX)) - && GET_CODE (SET_DEST (pat)) == REG - && GET_CODE (XEXP (tem, 0)) == CONST_DOUBLE - && GET_MODE_CLASS (GET_MODE (XEXP (tem, 0))) == MODE_FLOAT - && GET_CODE (goal) == CONST_INT - && 0 != (goaltry = operand_subword (XEXP (tem, 0), 0, 0, - VOIDmode)) - && rtx_equal_p (goal, goaltry) - && (valtry = operand_subword (SET_DEST (pat), 0, 0, - VOIDmode)) - && (valueno = true_regnum (valtry)) >= 0) - || (goal_const && (tem = find_reg_note (p, REG_EQUIV, - NULL_RTX)) - && GET_CODE (SET_DEST (pat)) == REG - && GET_CODE (XEXP (tem, 0)) == CONST_DOUBLE - && GET_MODE_CLASS (GET_MODE (XEXP (tem, 0))) == MODE_FLOAT - && GET_CODE (goal) == CONST_INT - && 0 != (goaltry = operand_subword (XEXP (tem, 0), 1, 0, - VOIDmode)) - && rtx_equal_p (goal, goaltry) - && (valtry - = operand_subword (SET_DEST (pat), 1, 0, VOIDmode)) - && (valueno = true_regnum (valtry)) >= 0))) - if (other >= 0 - ? valueno == other - : ((unsigned) valueno < FIRST_PSEUDO_REGISTER - && TEST_HARD_REG_BIT (reg_class_contents[(int) class], - valueno))) - { - value = valtry; - where = p; - break; - } - } - } - - /* We found a previous insn copying GOAL into a suitable other reg VALUE - (or copying VALUE into GOAL, if GOAL is also a register). - Now verify that VALUE is really valid. */ - - /* VALUENO is the register number of VALUE; a hard register. */ - - /* Don't try to re-use something that is killed in this insn. We want - to be able to trust REG_UNUSED notes. */ - if (find_reg_note (where, REG_UNUSED, value)) - return 0; - - /* If we propose to get the value from the stack pointer or if GOAL is - a MEM based on the stack pointer, we need a stable SP. */ - if (valueno == STACK_POINTER_REGNUM - || (goal_mem && reg_overlap_mentioned_for_reload_p (stack_pointer_rtx, - goal))) - need_stable_sp = 1; - - /* Reject VALUE if the copy-insn moved the wrong sort of datum. */ - if (GET_MODE (value) != mode) - return 0; - - /* Reject VALUE if it was loaded from GOAL - and is also a register that appears in the address of GOAL. */ - - if (goal_mem && value == SET_DEST (PATTERN (where)) - && refers_to_regno_for_reload_p (valueno, - (valueno - + HARD_REGNO_NREGS (valueno, mode)), - goal, NULL_PTR)) - return 0; - - /* Reject registers that overlap GOAL. */ - - if (!goal_mem && !goal_const - && regno + HARD_REGNO_NREGS (regno, mode) > valueno - && regno < valueno + HARD_REGNO_NREGS (valueno, mode)) - return 0; - - /* Reject VALUE if it is one of the regs reserved for reloads. - Reload1 knows how to reuse them anyway, and it would get - confused if we allocated one without its knowledge. - (Now that insns introduced by reload are ignored above, - this case shouldn't happen, but I'm not positive.) */ - - if (reload_reg_p != 0 && reload_reg_p != (short *) (HOST_WIDE_INT) 1 - && reload_reg_p[valueno] >= 0) - return 0; - - /* On some machines, certain regs must always be rejected - because they don't behave the way ordinary registers do. */ - -#ifdef OVERLAPPING_REGNO_P - if (OVERLAPPING_REGNO_P (valueno)) - return 0; -#endif - - nregs = HARD_REGNO_NREGS (regno, mode); - valuenregs = HARD_REGNO_NREGS (valueno, mode); - - /* Reject VALUE if it is a register being used for an input reload - even if it is not one of those reserved. */ - - if (reload_reg_p != 0) - { - int i; - for (i = 0; i < n_reloads; i++) - if (reload_reg_rtx[i] != 0 && reload_in[i]) - { - int regno1 = REGNO (reload_reg_rtx[i]); - int nregs1 = HARD_REGNO_NREGS (regno1, - GET_MODE (reload_reg_rtx[i])); - if (regno1 < valueno + valuenregs - && regno1 + nregs1 > valueno) - return 0; - } - } - - if (goal_mem) - /* We must treat frame pointer as varying here, - since it can vary--in a nonlocal goto as generated by expand_goto. */ - goal_mem_addr_varies = !CONSTANT_ADDRESS_P (XEXP (goal, 0)); - - /* Now verify that the values of GOAL and VALUE remain unaltered - until INSN is reached. */ - - p = insn; - while (1) - { - p = PREV_INSN (p); - if (p == where) - return value; - - /* Don't trust the conversion past a function call - if either of the two is in a call-clobbered register, or memory. */ - if (GET_CODE (p) == CALL_INSN - && ((regno >= 0 && regno < FIRST_PSEUDO_REGISTER - && call_used_regs[regno]) - || - (valueno >= 0 && valueno < FIRST_PSEUDO_REGISTER - && call_used_regs[valueno]) - || - goal_mem - || need_stable_sp)) - return 0; - -#ifdef INSN_CLOBBERS_REGNO_P - if ((valueno >= 0 && valueno < FIRST_PSEUDO_REGISTER - && INSN_CLOBBERS_REGNO_P (p, valueno)) - || (regno >= 0 && regno < FIRST_PSEUDO_REGISTER - && INSN_CLOBBERS_REGNO_P (p, regno))) - return 0; -#endif - - if (GET_RTX_CLASS (GET_CODE (p)) == 'i') - { - /* If this insn P stores in either GOAL or VALUE, return 0. - If GOAL is a memory ref and this insn writes memory, return 0. - If GOAL is a memory ref and its address is not constant, - and this insn P changes a register used in GOAL, return 0. */ - - pat = PATTERN (p); - if (GET_CODE (pat) == SET || GET_CODE (pat) == CLOBBER) - { - register rtx dest = SET_DEST (pat); - while (GET_CODE (dest) == SUBREG - || GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == STRICT_LOW_PART) - dest = XEXP (dest, 0); - if (GET_CODE (dest) == REG) - { - register int xregno = REGNO (dest); - int xnregs; - if (REGNO (dest) < FIRST_PSEUDO_REGISTER) - xnregs = HARD_REGNO_NREGS (xregno, GET_MODE (dest)); - else - xnregs = 1; - if (xregno < regno + nregs && xregno + xnregs > regno) - return 0; - if (xregno < valueno + valuenregs - && xregno + xnregs > valueno) - return 0; - if (goal_mem_addr_varies - && reg_overlap_mentioned_for_reload_p (dest, goal)) - return 0; - } - else if (goal_mem && GET_CODE (dest) == MEM - && ! push_operand (dest, GET_MODE (dest))) - return 0; - else if (need_stable_sp && push_operand (dest, GET_MODE (dest))) - return 0; - } - else if (GET_CODE (pat) == PARALLEL) - { - register int i; - for (i = XVECLEN (pat, 0) - 1; i >= 0; i--) - { - register rtx v1 = XVECEXP (pat, 0, i); - if (GET_CODE (v1) == SET || GET_CODE (v1) == CLOBBER) - { - register rtx dest = SET_DEST (v1); - while (GET_CODE (dest) == SUBREG - || GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == STRICT_LOW_PART) - dest = XEXP (dest, 0); - if (GET_CODE (dest) == REG) - { - register int xregno = REGNO (dest); - int xnregs; - if (REGNO (dest) < FIRST_PSEUDO_REGISTER) - xnregs = HARD_REGNO_NREGS (xregno, GET_MODE (dest)); - else - xnregs = 1; - if (xregno < regno + nregs - && xregno + xnregs > regno) - return 0; - if (xregno < valueno + valuenregs - && xregno + xnregs > valueno) - return 0; - if (goal_mem_addr_varies - && reg_overlap_mentioned_for_reload_p (dest, - goal)) - return 0; - } - else if (goal_mem && GET_CODE (dest) == MEM - && ! push_operand (dest, GET_MODE (dest))) - return 0; - else if (need_stable_sp - && push_operand (dest, GET_MODE (dest))) - return 0; - } - } - } - -#ifdef AUTO_INC_DEC - /* If this insn auto-increments or auto-decrements - either regno or valueno, return 0 now. - If GOAL is a memory ref and its address is not constant, - and this insn P increments a register used in GOAL, return 0. */ - { - register rtx link; - - for (link = REG_NOTES (p); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_INC - && GET_CODE (XEXP (link, 0)) == REG) - { - register int incno = REGNO (XEXP (link, 0)); - if (incno < regno + nregs && incno >= regno) - return 0; - if (incno < valueno + valuenregs && incno >= valueno) - return 0; - if (goal_mem_addr_varies - && reg_overlap_mentioned_for_reload_p (XEXP (link, 0), - goal)) - return 0; - } - } -#endif - } - } -} - -/* Find a place where INCED appears in an increment or decrement operator - within X, and return the amount INCED is incremented or decremented by. - The value is always positive. */ - -static int -find_inc_amount (x, inced) - rtx x, inced; -{ - register enum rtx_code code = GET_CODE (x); - register char *fmt; - register int i; - - if (code == MEM) - { - register rtx addr = XEXP (x, 0); - if ((GET_CODE (addr) == PRE_DEC - || GET_CODE (addr) == POST_DEC - || GET_CODE (addr) == PRE_INC - || GET_CODE (addr) == POST_INC) - && XEXP (addr, 0) == inced) - return GET_MODE_SIZE (GET_MODE (x)); - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - register int tem = find_inc_amount (XEXP (x, i), inced); - if (tem != 0) - return tem; - } - if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - { - register int tem = find_inc_amount (XVECEXP (x, i, j), inced); - if (tem != 0) - return tem; - } - } - } - - return 0; -} - -/* Return 1 if register REGNO is the subject of a clobber in insn INSN. */ - -int -regno_clobbered_p (regno, insn) - int regno; - rtx insn; -{ - if (GET_CODE (PATTERN (insn)) == CLOBBER - && GET_CODE (XEXP (PATTERN (insn), 0)) == REG) - return REGNO (XEXP (PATTERN (insn), 0)) == regno; - - if (GET_CODE (PATTERN (insn)) == PARALLEL) - { - int i = XVECLEN (PATTERN (insn), 0) - 1; - - for (; i >= 0; i--) - { - rtx elt = XVECEXP (PATTERN (insn), 0, i); - if (GET_CODE (elt) == CLOBBER && GET_CODE (XEXP (elt, 0)) == REG - && REGNO (XEXP (elt, 0)) == regno) - return 1; - } - } - - return 0; -} diff --git a/gnu/usr.bin/cc/common/reload1.c b/gnu/usr.bin/cc/common/reload1.c deleted file mode 100644 index 08813427e0..0000000000 --- a/gnu/usr.bin/cc/common/reload1.c +++ /dev/null @@ -1,6766 +0,0 @@ -/* Reload pseudo regs into hard regs for insns that require hard regs. - Copyright (C) 1987, 1988, 1989, 1992, 1993 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include -#include "config.h" -#include "rtl.h" -#include "obstack.h" -#include "insn-config.h" -#include "insn-flags.h" -#include "insn-codes.h" -#include "flags.h" -#include "expr.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "reload.h" -#include "recog.h" -#include "basic-block.h" -#include "output.h" - -/* This file contains the reload pass of the compiler, which is - run after register allocation has been done. It checks that - each insn is valid (operands required to be in registers really - are in registers of the proper class) and fixes up invalid ones - by copying values temporarily into registers for the insns - that need them. - - The results of register allocation are described by the vector - reg_renumber; the insns still contain pseudo regs, but reg_renumber - can be used to find which hard reg, if any, a pseudo reg is in. - - The technique we always use is to free up a few hard regs that are - called ``reload regs'', and for each place where a pseudo reg - must be in a hard reg, copy it temporarily into one of the reload regs. - - All the pseudos that were formerly allocated to the hard regs that - are now in use as reload regs must be ``spilled''. This means - that they go to other hard regs, or to stack slots if no other - available hard regs can be found. Spilling can invalidate more - insns, requiring additional need for reloads, so we must keep checking - until the process stabilizes. - - For machines with different classes of registers, we must keep track - of the register class needed for each reload, and make sure that - we allocate enough reload registers of each class. - - The file reload.c contains the code that checks one insn for - validity and reports the reloads that it needs. This file - is in charge of scanning the entire rtl code, accumulating the - reload needs, spilling, assigning reload registers to use for - fixing up each insn, and generating the new insns to copy values - into the reload registers. */ - - -#ifndef REGISTER_MOVE_COST -#define REGISTER_MOVE_COST(x, y) 2 -#endif - -#ifndef MEMORY_MOVE_COST -#define MEMORY_MOVE_COST(x) 4 -#endif - -/* During reload_as_needed, element N contains a REG rtx for the hard reg - into which reg N has been reloaded (perhaps for a previous insn). */ -static rtx *reg_last_reload_reg; - -/* Elt N nonzero if reg_last_reload_reg[N] has been set in this insn - for an output reload that stores into reg N. */ -static char *reg_has_output_reload; - -/* Indicates which hard regs are reload-registers for an output reload - in the current insn. */ -static HARD_REG_SET reg_is_output_reload; - -/* Element N is the constant value to which pseudo reg N is equivalent, - or zero if pseudo reg N is not equivalent to a constant. - find_reloads looks at this in order to replace pseudo reg N - with the constant it stands for. */ -rtx *reg_equiv_constant; - -/* Element N is a memory location to which pseudo reg N is equivalent, - prior to any register elimination (such as frame pointer to stack - pointer). Depending on whether or not it is a valid address, this value - is transferred to either reg_equiv_address or reg_equiv_mem. */ -rtx *reg_equiv_memory_loc; - -/* Element N is the address of stack slot to which pseudo reg N is equivalent. - This is used when the address is not valid as a memory address - (because its displacement is too big for the machine.) */ -rtx *reg_equiv_address; - -/* Element N is the memory slot to which pseudo reg N is equivalent, - or zero if pseudo reg N is not equivalent to a memory slot. */ -rtx *reg_equiv_mem; - -/* Widest width in which each pseudo reg is referred to (via subreg). */ -static int *reg_max_ref_width; - -/* Element N is the insn that initialized reg N from its equivalent - constant or memory slot. */ -static rtx *reg_equiv_init; - -/* During reload_as_needed, element N contains the last pseudo regno - reloaded into the Nth reload register. This vector is in parallel - with spill_regs. If that pseudo reg occupied more than one register, - reg_reloaded_contents points to that pseudo for each spill register in - use; all of these must remain set for an inheritance to occur. */ -static int reg_reloaded_contents[FIRST_PSEUDO_REGISTER]; - -/* During reload_as_needed, element N contains the insn for which - the Nth reload register was last used. This vector is in parallel - with spill_regs, and its contents are significant only when - reg_reloaded_contents is significant. */ -static rtx reg_reloaded_insn[FIRST_PSEUDO_REGISTER]; - -/* Number of spill-regs so far; number of valid elements of spill_regs. */ -static int n_spills; - -/* In parallel with spill_regs, contains REG rtx's for those regs. - Holds the last rtx used for any given reg, or 0 if it has never - been used for spilling yet. This rtx is reused, provided it has - the proper mode. */ -static rtx spill_reg_rtx[FIRST_PSEUDO_REGISTER]; - -/* In parallel with spill_regs, contains nonzero for a spill reg - that was stored after the last time it was used. - The precise value is the insn generated to do the store. */ -static rtx spill_reg_store[FIRST_PSEUDO_REGISTER]; - -/* This table is the inverse mapping of spill_regs: - indexed by hard reg number, - it contains the position of that reg in spill_regs, - or -1 for something that is not in spill_regs. */ -static short spill_reg_order[FIRST_PSEUDO_REGISTER]; - -/* This reg set indicates registers that may not be used for retrying global - allocation. The registers that may not be used include all spill registers - and the frame pointer (if we are using one). */ -HARD_REG_SET forbidden_regs; - -/* This reg set indicates registers that are not good for spill registers. - They will not be used to complete groups of spill registers. This includes - all fixed registers, registers that may be eliminated, and, if - SMALL_REGISTER_CLASSES is not defined, registers explicitly used in the rtl. - - (spill_reg_order prevents these registers from being used to start a - group.) */ -static HARD_REG_SET bad_spill_regs; - -/* Describes order of use of registers for reloading - of spilled pseudo-registers. `spills' is the number of - elements that are actually valid; new ones are added at the end. */ -static short spill_regs[FIRST_PSEUDO_REGISTER]; - -/* Describes order of preference for putting regs into spill_regs. - Contains the numbers of all the hard regs, in order most preferred first. - This order is different for each function. - It is set up by order_regs_for_reload. - Empty elements at the end contain -1. */ -static short potential_reload_regs[FIRST_PSEUDO_REGISTER]; - -/* 1 for a hard register that appears explicitly in the rtl - (for example, function value registers, special registers - used by insns, structure value pointer registers). */ -static char regs_explicitly_used[FIRST_PSEUDO_REGISTER]; - -/* Indicates if a register was counted against the need for - groups. 0 means it can count against max_nongroup instead. */ -static HARD_REG_SET counted_for_groups; - -/* Indicates if a register was counted against the need for - non-groups. 0 means it can become part of a new group. - During choose_reload_regs, 1 here means don't use this reg - as part of a group, even if it seems to be otherwise ok. */ -static HARD_REG_SET counted_for_nongroups; - -/* Indexed by pseudo reg number N, - says may not delete stores into the real (memory) home of pseudo N. - This is set if we already substituted a memory equivalent in some uses, - which happens when we have to eliminate the fp from it. */ -static char *cannot_omit_stores; - -/* Nonzero if indirect addressing is supported on the machine; this means - that spilling (REG n) does not require reloading it into a register in - order to do (MEM (REG n)) or (MEM (PLUS (REG n) (CONST_INT c))). The - value indicates the level of indirect addressing supported, e.g., two - means that (MEM (MEM (REG n))) is also valid if (REG n) does not get - a hard register. */ - -static char spill_indirect_levels; - -/* Nonzero if indirect addressing is supported when the innermost MEM is - of the form (MEM (SYMBOL_REF sym)). It is assumed that the level to - which these are valid is the same as spill_indirect_levels, above. */ - -char indirect_symref_ok; - -/* Nonzero if an address (plus (reg frame_pointer) (reg ...)) is valid. */ - -char double_reg_address_ok; - -/* Record the stack slot for each spilled hard register. */ - -static rtx spill_stack_slot[FIRST_PSEUDO_REGISTER]; - -/* Width allocated so far for that stack slot. */ - -static int spill_stack_slot_width[FIRST_PSEUDO_REGISTER]; - -/* Indexed by register class and basic block number, nonzero if there is - any need for a spill register of that class in that basic block. - The pointer is 0 if we did stupid allocation and don't know - the structure of basic blocks. */ - -char *basic_block_needs[N_REG_CLASSES]; - -/* First uid used by insns created by reload in this function. - Used in find_equiv_reg. */ -int reload_first_uid; - -/* Flag set by local-alloc or global-alloc if anything is live in - a call-clobbered reg across calls. */ - -int caller_save_needed; - -/* Set to 1 while reload_as_needed is operating. - Required by some machines to handle any generated moves differently. */ - -int reload_in_progress = 0; - -/* These arrays record the insn_code of insns that may be needed to - perform input and output reloads of special objects. They provide a - place to pass a scratch register. */ - -enum insn_code reload_in_optab[NUM_MACHINE_MODES]; -enum insn_code reload_out_optab[NUM_MACHINE_MODES]; - -/* This obstack is used for allocation of rtl during register elimination. - The allocated storage can be freed once find_reloads has processed the - insn. */ - -struct obstack reload_obstack; -char *reload_firstobj; - -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free - -/* List of labels that must never be deleted. */ -extern rtx forced_labels; - -/* This structure is used to record information about register eliminations. - Each array entry describes one possible way of eliminating a register - in favor of another. If there is more than one way of eliminating a - particular register, the most preferred should be specified first. */ - -static struct elim_table -{ - int from; /* Register number to be eliminated. */ - int to; /* Register number used as replacement. */ - int initial_offset; /* Initial difference between values. */ - int can_eliminate; /* Non-zero if this elimination can be done. */ - int can_eliminate_previous; /* Value of CAN_ELIMINATE in previous scan over - insns made by reload. */ - int offset; /* Current offset between the two regs. */ - int max_offset; /* Maximum offset between the two regs. */ - int previous_offset; /* Offset at end of previous insn. */ - int ref_outside_mem; /* "to" has been referenced outside a MEM. */ - rtx from_rtx; /* REG rtx for the register to be eliminated. - We cannot simply compare the number since - we might then spuriously replace a hard - register corresponding to a pseudo - assigned to the reg to be eliminated. */ - rtx to_rtx; /* REG rtx for the replacement. */ -} reg_eliminate[] = - -/* If a set of eliminable registers was specified, define the table from it. - Otherwise, default to the normal case of the frame pointer being - replaced by the stack pointer. */ - -#ifdef ELIMINABLE_REGS - ELIMINABLE_REGS; -#else - {{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}}; -#endif - -#define NUM_ELIMINABLE_REGS (sizeof reg_eliminate / sizeof reg_eliminate[0]) - -/* Record the number of pending eliminations that have an offset not equal - to their initial offset. If non-zero, we use a new copy of each - replacement result in any insns encountered. */ -static int num_not_at_initial_offset; - -/* Count the number of registers that we may be able to eliminate. */ -static int num_eliminable; - -/* For each label, we record the offset of each elimination. If we reach - a label by more than one path and an offset differs, we cannot do the - elimination. This information is indexed by the number of the label. - The first table is an array of flags that records whether we have yet - encountered a label and the second table is an array of arrays, one - entry in the latter array for each elimination. */ - -static char *offsets_known_at; -static int (*offsets_at)[NUM_ELIMINABLE_REGS]; - -/* Number of labels in the current function. */ - -static int num_labels; - -struct hard_reg_n_uses { int regno; int uses; }; - -static int possible_group_p PROTO((int, int *)); -static void count_possible_groups PROTO((int *, enum machine_mode *, - int *)); -static int modes_equiv_for_class_p PROTO((enum machine_mode, - enum machine_mode, - enum reg_class)); -static void spill_failure PROTO((rtx)); -static int new_spill_reg PROTO((int, int, int *, int *, int, - FILE *)); -static void delete_dead_insn PROTO((rtx)); -static void alter_reg PROTO((int, int)); -static void set_label_offsets PROTO((rtx, rtx, int)); -static int eliminate_regs_in_insn PROTO((rtx, int)); -static void mark_not_eliminable PROTO((rtx, rtx)); -static int spill_hard_reg PROTO((int, int, FILE *, int)); -static void scan_paradoxical_subregs PROTO((rtx)); -static int hard_reg_use_compare PROTO((struct hard_reg_n_uses *, - struct hard_reg_n_uses *)); -static void order_regs_for_reload PROTO((void)); -static void reload_as_needed PROTO((rtx, int)); -static void forget_old_reloads_1 PROTO((rtx, rtx)); -static int reload_reg_class_lower PROTO((short *, short *)); -static void mark_reload_reg_in_use PROTO((int, int, enum reload_type, - enum machine_mode)); -static void clear_reload_reg_in_use PROTO((int, int, enum reload_type, - enum machine_mode)); -static int reload_reg_free_p PROTO((int, int, enum reload_type)); -static int reload_reg_free_before_p PROTO((int, int, enum reload_type)); -static int reload_reg_reaches_end_p PROTO((int, int, enum reload_type)); -static int allocate_reload_reg PROTO((int, rtx, int, int)); -static void choose_reload_regs PROTO((rtx, rtx)); -static void merge_assigned_reloads PROTO((rtx)); -static void emit_reload_insns PROTO((rtx)); -static void delete_output_reload PROTO((rtx, int, rtx)); -static void inc_for_reload PROTO((rtx, rtx, int)); -static int constraint_accepts_reg_p PROTO((char *, rtx)); -static int count_occurrences PROTO((rtx, rtx)); - -/* Initialize the reload pass once per compilation. */ - -void -init_reload () -{ - register int i; - - /* Often (MEM (REG n)) is still valid even if (REG n) is put on the stack. - Set spill_indirect_levels to the number of levels such addressing is - permitted, zero if it is not permitted at all. */ - - register rtx tem - = gen_rtx (MEM, Pmode, - gen_rtx (PLUS, Pmode, - gen_rtx (REG, Pmode, LAST_VIRTUAL_REGISTER + 1), - GEN_INT (4))); - spill_indirect_levels = 0; - - while (memory_address_p (QImode, tem)) - { - spill_indirect_levels++; - tem = gen_rtx (MEM, Pmode, tem); - } - - /* See if indirect addressing is valid for (MEM (SYMBOL_REF ...)). */ - - tem = gen_rtx (MEM, Pmode, gen_rtx (SYMBOL_REF, Pmode, "foo")); - indirect_symref_ok = memory_address_p (QImode, tem); - - /* See if reg+reg is a valid (and offsettable) address. */ - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - tem = gen_rtx (PLUS, Pmode, - gen_rtx (REG, Pmode, FRAME_POINTER_REGNUM), - gen_rtx (REG, Pmode, i)); - /* This way, we make sure that reg+reg is an offsettable address. */ - tem = plus_constant (tem, 4); - - if (memory_address_p (QImode, tem)) - { - double_reg_address_ok = 1; - break; - } - } - - /* Initialize obstack for our rtl allocation. */ - gcc_obstack_init (&reload_obstack); - reload_firstobj = (char *) obstack_alloc (&reload_obstack, 0); -} - -/* Main entry point for the reload pass. - - FIRST is the first insn of the function being compiled. - - GLOBAL nonzero means we were called from global_alloc - and should attempt to reallocate any pseudoregs that we - displace from hard regs we will use for reloads. - If GLOBAL is zero, we do not have enough information to do that, - so any pseudo reg that is spilled must go to the stack. - - DUMPFILE is the global-reg debugging dump file stream, or 0. - If it is nonzero, messages are written to it to describe - which registers are seized as reload regs, which pseudo regs - are spilled from them, and where the pseudo regs are reallocated to. - - Return value is nonzero if reload failed - and we must not do any more for this function. */ - -int -reload (first, global, dumpfile) - rtx first; - int global; - FILE *dumpfile; -{ - register int class; - register int i, j; - register rtx insn; - register struct elim_table *ep; - - int something_changed; - int something_needs_reloads; - int something_needs_elimination; - int new_basic_block_needs; - enum reg_class caller_save_spill_class = NO_REGS; - int caller_save_group_size = 1; - - /* Nonzero means we couldn't get enough spill regs. */ - int failure = 0; - - /* The basic block number currently being processed for INSN. */ - int this_block; - - /* Make sure even insns with volatile mem refs are recognizable. */ - init_recog (); - - /* Enable find_equiv_reg to distinguish insns made by reload. */ - reload_first_uid = get_max_uid (); - - for (i = 0; i < N_REG_CLASSES; i++) - basic_block_needs[i] = 0; - -#ifdef SECONDARY_MEMORY_NEEDED - /* Initialize the secondary memory table. */ - clear_secondary_mem (); -#endif - - /* Remember which hard regs appear explicitly - before we merge into `regs_ever_live' the ones in which - pseudo regs have been allocated. */ - bcopy (regs_ever_live, regs_explicitly_used, sizeof regs_ever_live); - - /* We don't have a stack slot for any spill reg yet. */ - bzero (spill_stack_slot, sizeof spill_stack_slot); - bzero (spill_stack_slot_width, sizeof spill_stack_slot_width); - - /* Initialize the save area information for caller-save, in case some - are needed. */ - init_save_areas (); - - /* Compute which hard registers are now in use - as homes for pseudo registers. - This is done here rather than (eg) in global_alloc - because this point is reached even if not optimizing. */ - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - mark_home_live (i); - - /* Make sure that the last insn in the chain - is not something that needs reloading. */ - emit_note (NULL_PTR, NOTE_INSN_DELETED); - - /* Find all the pseudo registers that didn't get hard regs - but do have known equivalent constants or memory slots. - These include parameters (known equivalent to parameter slots) - and cse'd or loop-moved constant memory addresses. - - Record constant equivalents in reg_equiv_constant - so they will be substituted by find_reloads. - Record memory equivalents in reg_mem_equiv so they can - be substituted eventually by altering the REG-rtx's. */ - - reg_equiv_constant = (rtx *) alloca (max_regno * sizeof (rtx)); - bzero (reg_equiv_constant, max_regno * sizeof (rtx)); - reg_equiv_memory_loc = (rtx *) alloca (max_regno * sizeof (rtx)); - bzero (reg_equiv_memory_loc, max_regno * sizeof (rtx)); - reg_equiv_mem = (rtx *) alloca (max_regno * sizeof (rtx)); - bzero (reg_equiv_mem, max_regno * sizeof (rtx)); - reg_equiv_init = (rtx *) alloca (max_regno * sizeof (rtx)); - bzero (reg_equiv_init, max_regno * sizeof (rtx)); - reg_equiv_address = (rtx *) alloca (max_regno * sizeof (rtx)); - bzero (reg_equiv_address, max_regno * sizeof (rtx)); - reg_max_ref_width = (int *) alloca (max_regno * sizeof (int)); - bzero (reg_max_ref_width, max_regno * sizeof (int)); - cannot_omit_stores = (char *) alloca (max_regno); - bzero (cannot_omit_stores, max_regno); - - /* Look for REG_EQUIV notes; record what each pseudo is equivalent to. - Also find all paradoxical subregs - and find largest such for each pseudo. */ - - for (insn = first; insn; insn = NEXT_INSN (insn)) - { - rtx set = single_set (insn); - - if (set != 0 && GET_CODE (SET_DEST (set)) == REG) - { - rtx note = find_reg_note (insn, REG_EQUIV, NULL_RTX); - if (note -#ifdef LEGITIMATE_PIC_OPERAND_P - && (! CONSTANT_P (XEXP (note, 0)) || ! flag_pic - || LEGITIMATE_PIC_OPERAND_P (XEXP (note, 0))) -#endif - ) - { - rtx x = XEXP (note, 0); - i = REGNO (SET_DEST (set)); - if (i > LAST_VIRTUAL_REGISTER) - { - if (GET_CODE (x) == MEM) - reg_equiv_memory_loc[i] = x; - else if (CONSTANT_P (x)) - { - if (LEGITIMATE_CONSTANT_P (x)) - reg_equiv_constant[i] = x; - else - reg_equiv_memory_loc[i] - = force_const_mem (GET_MODE (SET_DEST (set)), x); - } - else - continue; - - /* If this register is being made equivalent to a MEM - and the MEM is not SET_SRC, the equivalencing insn - is one with the MEM as a SET_DEST and it occurs later. - So don't mark this insn now. */ - if (GET_CODE (x) != MEM - || rtx_equal_p (SET_SRC (set), x)) - reg_equiv_init[i] = insn; - } - } - } - - /* If this insn is setting a MEM from a register equivalent to it, - this is the equivalencing insn. */ - else if (set && GET_CODE (SET_DEST (set)) == MEM - && GET_CODE (SET_SRC (set)) == REG - && reg_equiv_memory_loc[REGNO (SET_SRC (set))] - && rtx_equal_p (SET_DEST (set), - reg_equiv_memory_loc[REGNO (SET_SRC (set))])) - reg_equiv_init[REGNO (SET_SRC (set))] = insn; - - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') - scan_paradoxical_subregs (PATTERN (insn)); - } - - /* Does this function require a frame pointer? */ - - frame_pointer_needed = (! flag_omit_frame_pointer -#ifdef EXIT_IGNORE_STACK - /* ?? If EXIT_IGNORE_STACK is set, we will not save - and restore sp for alloca. So we can't eliminate - the frame pointer in that case. At some point, - we should improve this by emitting the - sp-adjusting insns for this case. */ - || (current_function_calls_alloca - && EXIT_IGNORE_STACK) -#endif - || FRAME_POINTER_REQUIRED); - - num_eliminable = 0; - - /* Initialize the table of registers to eliminate. The way we do this - depends on how the eliminable registers were defined. */ -#ifdef ELIMINABLE_REGS - for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) - { - ep->can_eliminate = ep->can_eliminate_previous - = (CAN_ELIMINATE (ep->from, ep->to) - && (ep->from != FRAME_POINTER_REGNUM || ! frame_pointer_needed)); - } -#else - reg_eliminate[0].can_eliminate = reg_eliminate[0].can_eliminate_previous - = ! frame_pointer_needed; -#endif - - /* Count the number of eliminable registers and build the FROM and TO - REG rtx's. Note that code in gen_rtx will cause, e.g., - gen_rtx (REG, Pmode, STACK_POINTER_REGNUM) to equal stack_pointer_rtx. - We depend on this. */ - for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) - { - num_eliminable += ep->can_eliminate; - ep->from_rtx = gen_rtx (REG, Pmode, ep->from); - ep->to_rtx = gen_rtx (REG, Pmode, ep->to); - } - - num_labels = max_label_num () - get_first_label_num (); - - /* Allocate the tables used to store offset information at labels. */ - offsets_known_at = (char *) alloca (num_labels); - offsets_at - = (int (*)[NUM_ELIMINABLE_REGS]) - alloca (num_labels * NUM_ELIMINABLE_REGS * sizeof (int)); - - offsets_known_at -= get_first_label_num (); - offsets_at -= get_first_label_num (); - - /* Alter each pseudo-reg rtx to contain its hard reg number. - Assign stack slots to the pseudos that lack hard regs or equivalents. - Do not touch virtual registers. */ - - for (i = LAST_VIRTUAL_REGISTER + 1; i < max_regno; i++) - alter_reg (i, -1); - - /* Round size of stack frame to BIGGEST_ALIGNMENT. This must be done here - because the stack size may be a part of the offset computation for - register elimination. */ - assign_stack_local (BLKmode, 0, 0); - - /* If we have some registers we think can be eliminated, scan all insns to - see if there is an insn that sets one of these registers to something - other than itself plus a constant. If so, the register cannot be - eliminated. Doing this scan here eliminates an extra pass through the - main reload loop in the most common case where register elimination - cannot be done. */ - for (insn = first; insn && num_eliminable; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN - || GET_CODE (insn) == CALL_INSN) - note_stores (PATTERN (insn), mark_not_eliminable); - -#ifndef REGISTER_CONSTRAINTS - /* If all the pseudo regs have hard regs, - except for those that are never referenced, - we know that no reloads are needed. */ - /* But that is not true if there are register constraints, since - in that case some pseudos might be in the wrong kind of hard reg. */ - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (reg_renumber[i] == -1 && reg_n_refs[i] != 0) - break; - - if (i == max_regno && num_eliminable == 0 && ! caller_save_needed) - return; -#endif - - /* Compute the order of preference for hard registers to spill. - Store them by decreasing preference in potential_reload_regs. */ - - order_regs_for_reload (); - - /* So far, no hard regs have been spilled. */ - n_spills = 0; - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - spill_reg_order[i] = -1; - - /* On most machines, we can't use any register explicitly used in the - rtl as a spill register. But on some, we have to. Those will have - taken care to keep the life of hard regs as short as possible. */ - -#ifdef SMALL_REGISTER_CLASSES - CLEAR_HARD_REG_SET (forbidden_regs); -#else - COPY_HARD_REG_SET (forbidden_regs, bad_spill_regs); -#endif - - /* Spill any hard regs that we know we can't eliminate. */ - for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) - if (! ep->can_eliminate) - { - spill_hard_reg (ep->from, global, dumpfile, 1); - regs_ever_live[ep->from] = 1; - } - - if (global) - for (i = 0; i < N_REG_CLASSES; i++) - { - basic_block_needs[i] = (char *)alloca (n_basic_blocks); - bzero (basic_block_needs[i], n_basic_blocks); - } - - /* From now on, we need to emit any moves without making new pseudos. */ - reload_in_progress = 1; - - /* This loop scans the entire function each go-round - and repeats until one repetition spills no additional hard regs. */ - - /* This flag is set when a pseudo reg is spilled, - to require another pass. Note that getting an additional reload - reg does not necessarily imply any pseudo reg was spilled; - sometimes we find a reload reg that no pseudo reg was allocated in. */ - something_changed = 1; - /* This flag is set if there are any insns that require reloading. */ - something_needs_reloads = 0; - /* This flag is set if there are any insns that require register - eliminations. */ - something_needs_elimination = 0; - while (something_changed) - { - rtx after_call = 0; - - /* For each class, number of reload regs needed in that class. - This is the maximum over all insns of the needs in that class - of the individual insn. */ - int max_needs[N_REG_CLASSES]; - /* For each class, size of group of consecutive regs - that is needed for the reloads of this class. */ - int group_size[N_REG_CLASSES]; - /* For each class, max number of consecutive groups needed. - (Each group contains group_size[CLASS] consecutive registers.) */ - int max_groups[N_REG_CLASSES]; - /* For each class, max number needed of regs that don't belong - to any of the groups. */ - int max_nongroups[N_REG_CLASSES]; - /* For each class, the machine mode which requires consecutive - groups of regs of that class. - If two different modes ever require groups of one class, - they must be the same size and equally restrictive for that class, - otherwise we can't handle the complexity. */ - enum machine_mode group_mode[N_REG_CLASSES]; - /* Record the insn where each maximum need is first found. */ - rtx max_needs_insn[N_REG_CLASSES]; - rtx max_groups_insn[N_REG_CLASSES]; - rtx max_nongroups_insn[N_REG_CLASSES]; - rtx x; - int starting_frame_size = get_frame_size (); - static char *reg_class_names[] = REG_CLASS_NAMES; - - something_changed = 0; - bzero (max_needs, sizeof max_needs); - bzero (max_groups, sizeof max_groups); - bzero (max_nongroups, sizeof max_nongroups); - bzero (max_needs_insn, sizeof max_needs_insn); - bzero (max_groups_insn, sizeof max_groups_insn); - bzero (max_nongroups_insn, sizeof max_nongroups_insn); - bzero (group_size, sizeof group_size); - for (i = 0; i < N_REG_CLASSES; i++) - group_mode[i] = VOIDmode; - - /* Keep track of which basic blocks are needing the reloads. */ - this_block = 0; - - /* Remember whether any element of basic_block_needs - changes from 0 to 1 in this pass. */ - new_basic_block_needs = 0; - - /* Reset all offsets on eliminable registers to their initial values. */ -#ifdef ELIMINABLE_REGS - for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) - { - INITIAL_ELIMINATION_OFFSET (ep->from, ep->to, ep->initial_offset); - ep->previous_offset = ep->offset - = ep->max_offset = ep->initial_offset; - } -#else -#ifdef INITIAL_FRAME_POINTER_OFFSET - INITIAL_FRAME_POINTER_OFFSET (reg_eliminate[0].initial_offset); -#else - if (!FRAME_POINTER_REQUIRED) - abort (); - reg_eliminate[0].initial_offset = 0; -#endif - reg_eliminate[0].previous_offset = reg_eliminate[0].max_offset - = reg_eliminate[0].offset = reg_eliminate[0].initial_offset; -#endif - - num_not_at_initial_offset = 0; - - bzero (&offsets_known_at[get_first_label_num ()], num_labels); - - /* Set a known offset for each forced label to be at the initial offset - of each elimination. We do this because we assume that all - computed jumps occur from a location where each elimination is - at its initial offset. */ - - for (x = forced_labels; x; x = XEXP (x, 1)) - if (XEXP (x, 0)) - set_label_offsets (XEXP (x, 0), NULL_RTX, 1); - - /* For each pseudo register that has an equivalent location defined, - try to eliminate any eliminable registers (such as the frame pointer) - assuming initial offsets for the replacement register, which - is the normal case. - - If the resulting location is directly addressable, substitute - the MEM we just got directly for the old REG. - - If it is not addressable but is a constant or the sum of a hard reg - and constant, it is probably not addressable because the constant is - out of range, in that case record the address; we will generate - hairy code to compute the address in a register each time it is - needed. - - If the location is not addressable, but does not have one of the - above forms, assign a stack slot. We have to do this to avoid the - potential of producing lots of reloads if, e.g., a location involves - a pseudo that didn't get a hard register and has an equivalent memory - location that also involves a pseudo that didn't get a hard register. - - Perhaps at some point we will improve reload_when_needed handling - so this problem goes away. But that's very hairy. */ - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (reg_renumber[i] < 0 && reg_equiv_memory_loc[i]) - { - rtx x = eliminate_regs (reg_equiv_memory_loc[i], 0, NULL_RTX); - - if (strict_memory_address_p (GET_MODE (regno_reg_rtx[i]), - XEXP (x, 0))) - reg_equiv_mem[i] = x, reg_equiv_address[i] = 0; - else if (CONSTANT_P (XEXP (x, 0)) - || (GET_CODE (XEXP (x, 0)) == PLUS - && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG - && (REGNO (XEXP (XEXP (x, 0), 0)) - < FIRST_PSEUDO_REGISTER) - && CONSTANT_P (XEXP (XEXP (x, 0), 1)))) - reg_equiv_address[i] = XEXP (x, 0), reg_equiv_mem[i] = 0; - else - { - /* Make a new stack slot. Then indicate that something - changed so we go back and recompute offsets for - eliminable registers because the allocation of memory - below might change some offset. reg_equiv_{mem,address} - will be set up for this pseudo on the next pass around - the loop. */ - reg_equiv_memory_loc[i] = 0; - reg_equiv_init[i] = 0; - alter_reg (i, -1); - something_changed = 1; - } - } - - /* If we allocated another pseudo to the stack, redo elimination - bookkeeping. */ - if (something_changed) - continue; - - /* If caller-saves needs a group, initialize the group to include - the size and mode required for caller-saves. */ - - if (caller_save_group_size > 1) - { - group_mode[(int) caller_save_spill_class] = Pmode; - group_size[(int) caller_save_spill_class] = caller_save_group_size; - } - - /* Compute the most additional registers needed by any instruction. - Collect information separately for each class of regs. */ - - for (insn = first; insn; insn = NEXT_INSN (insn)) - { - if (global && this_block + 1 < n_basic_blocks - && insn == basic_block_head[this_block+1]) - ++this_block; - - /* If this is a label, a JUMP_INSN, or has REG_NOTES (which - might include REG_LABEL), we need to see what effects this - has on the known offsets at labels. */ - - if (GET_CODE (insn) == CODE_LABEL || GET_CODE (insn) == JUMP_INSN - || (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && REG_NOTES (insn) != 0)) - set_label_offsets (insn, insn, 0); - - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') - { - /* Nonzero means don't use a reload reg that overlaps - the place where a function value can be returned. */ - rtx avoid_return_reg = 0; - - rtx old_body = PATTERN (insn); - int old_code = INSN_CODE (insn); - rtx old_notes = REG_NOTES (insn); - int did_elimination = 0; - int max_total_input_groups = 0, max_total_output_groups = 0; - - /* To compute the number of reload registers of each class - needed for an insn, we must similate what choose_reload_regs - can do. We do this by splitting an insn into an "input" and - an "output" part. RELOAD_OTHER reloads are used in both. - The input part uses those reloads, RELOAD_FOR_INPUT reloads, - which must be live over the entire input section of reloads, - and the maximum of all the RELOAD_FOR_INPUT_ADDRESS and - RELOAD_FOR_OPERAND_ADDRESS reloads, which conflict with the - inputs. - - The registers needed for output are RELOAD_OTHER and - RELOAD_FOR_OUTPUT, which are live for the entire output - portion, and the maximum of all the RELOAD_FOR_OUTPUT_ADDRESS - reloads for each operand. - - The total number of registers needed is the maximum of the - inputs and outputs. */ - - /* These just count RELOAD_OTHER. */ - int insn_needs[N_REG_CLASSES]; - int insn_groups[N_REG_CLASSES]; - int insn_total_groups = 0; - - /* Count RELOAD_FOR_INPUT reloads. */ - int insn_needs_for_inputs[N_REG_CLASSES]; - int insn_groups_for_inputs[N_REG_CLASSES]; - int insn_total_groups_for_inputs = 0; - - /* Count RELOAD_FOR_OUTPUT reloads. */ - int insn_needs_for_outputs[N_REG_CLASSES]; - int insn_groups_for_outputs[N_REG_CLASSES]; - int insn_total_groups_for_outputs = 0; - - /* Count RELOAD_FOR_INSN reloads. */ - int insn_needs_for_insn[N_REG_CLASSES]; - int insn_groups_for_insn[N_REG_CLASSES]; - int insn_total_groups_for_insn = 0; - - /* Count RELOAD_FOR_OTHER_ADDRESS reloads. */ - int insn_needs_for_other_addr[N_REG_CLASSES]; - int insn_groups_for_other_addr[N_REG_CLASSES]; - int insn_total_groups_for_other_addr = 0; - - /* Count RELOAD_FOR_INPUT_ADDRESS reloads. */ - int insn_needs_for_in_addr[MAX_RECOG_OPERANDS][N_REG_CLASSES]; - int insn_groups_for_in_addr[MAX_RECOG_OPERANDS][N_REG_CLASSES]; - int insn_total_groups_for_in_addr[MAX_RECOG_OPERANDS]; - - /* Count RELOAD_FOR_OUTPUT_ADDRESS reloads. */ - int insn_needs_for_out_addr[MAX_RECOG_OPERANDS][N_REG_CLASSES]; - int insn_groups_for_out_addr[MAX_RECOG_OPERANDS][N_REG_CLASSES]; - int insn_total_groups_for_out_addr[MAX_RECOG_OPERANDS]; - - /* Count RELOAD_FOR_OPERAND_ADDRESS reloads. */ - int insn_needs_for_op_addr[N_REG_CLASSES]; - int insn_groups_for_op_addr[N_REG_CLASSES]; - int insn_total_groups_for_op_addr = 0; - -#if 0 /* This wouldn't work nowadays, since optimize_bit_field - looks for non-strict memory addresses. */ - /* Optimization: a bit-field instruction whose field - happens to be a byte or halfword in memory - can be changed to a move instruction. */ - - if (GET_CODE (PATTERN (insn)) == SET) - { - rtx dest = SET_DEST (PATTERN (insn)); - rtx src = SET_SRC (PATTERN (insn)); - - if (GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SIGN_EXTRACT) - optimize_bit_field (PATTERN (insn), insn, reg_equiv_mem); - if (GET_CODE (src) == ZERO_EXTRACT - || GET_CODE (src) == SIGN_EXTRACT) - optimize_bit_field (PATTERN (insn), insn, reg_equiv_mem); - } -#endif - - /* If needed, eliminate any eliminable registers. */ - if (num_eliminable) - did_elimination = eliminate_regs_in_insn (insn, 0); - -#ifdef SMALL_REGISTER_CLASSES - /* Set avoid_return_reg if this is an insn - that might use the value of a function call. */ - if (GET_CODE (insn) == CALL_INSN) - { - if (GET_CODE (PATTERN (insn)) == SET) - after_call = SET_DEST (PATTERN (insn)); - else if (GET_CODE (PATTERN (insn)) == PARALLEL - && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) - after_call = SET_DEST (XVECEXP (PATTERN (insn), 0, 0)); - else - after_call = 0; - } - else if (after_call != 0 - && !(GET_CODE (PATTERN (insn)) == SET - && SET_DEST (PATTERN (insn)) == stack_pointer_rtx)) - { - if (reg_mentioned_p (after_call, PATTERN (insn))) - avoid_return_reg = after_call; - after_call = 0; - } -#endif /* SMALL_REGISTER_CLASSES */ - - /* Analyze the instruction. */ - find_reloads (insn, 0, spill_indirect_levels, global, - spill_reg_order); - - /* Remember for later shortcuts which insns had any reloads or - register eliminations. - - One might think that it would be worthwhile to mark insns - that need register replacements but not reloads, but this is - not safe because find_reloads may do some manipulation of - the insn (such as swapping commutative operands), which would - be lost when we restore the old pattern after register - replacement. So the actions of find_reloads must be redone in - subsequent passes or in reload_as_needed. - - However, it is safe to mark insns that need reloads - but not register replacement. */ - - PUT_MODE (insn, (did_elimination ? QImode - : n_reloads ? HImode - : GET_MODE (insn) == DImode ? DImode - : VOIDmode)); - - /* Discard any register replacements done. */ - if (did_elimination) - { - obstack_free (&reload_obstack, reload_firstobj); - PATTERN (insn) = old_body; - INSN_CODE (insn) = old_code; - REG_NOTES (insn) = old_notes; - something_needs_elimination = 1; - } - - /* If this insn has no reloads, we need not do anything except - in the case of a CALL_INSN when we have caller-saves and - caller-save needs reloads. */ - - if (n_reloads == 0 - && ! (GET_CODE (insn) == CALL_INSN - && caller_save_spill_class != NO_REGS)) - continue; - - something_needs_reloads = 1; - - for (i = 0; i < N_REG_CLASSES; i++) - { - insn_needs[i] = 0, insn_groups[i] = 0; - insn_needs_for_inputs[i] = 0, insn_groups_for_inputs[i] = 0; - insn_needs_for_outputs[i] = 0, insn_groups_for_outputs[i] = 0; - insn_needs_for_insn[i] = 0, insn_groups_for_insn[i] = 0; - insn_needs_for_op_addr[i] = 0, insn_groups_for_op_addr[i] = 0; - insn_needs_for_other_addr[i] = 0; - insn_groups_for_other_addr[i] = 0; - } - - for (i = 0; i < reload_n_operands; i++) - { - insn_total_groups_for_in_addr[i] = 0; - insn_total_groups_for_out_addr[i] = 0; - - for (j = 0; j < N_REG_CLASSES; j++) - { - insn_needs_for_in_addr[i][j] = 0; - insn_needs_for_out_addr[i][j] = 0; - insn_groups_for_in_addr[i][j] = 0; - insn_groups_for_out_addr[i][j] = 0; - } - } - - /* Count each reload once in every class - containing the reload's own class. */ - - for (i = 0; i < n_reloads; i++) - { - register enum reg_class *p; - enum reg_class class = reload_reg_class[i]; - int size; - enum machine_mode mode; - int *this_groups; - int *this_needs; - int *this_total_groups; - - /* Don't count the dummy reloads, for which one of the - regs mentioned in the insn can be used for reloading. - Don't count optional reloads. - Don't count reloads that got combined with others. */ - if (reload_reg_rtx[i] != 0 - || reload_optional[i] != 0 - || (reload_out[i] == 0 && reload_in[i] == 0 - && ! reload_secondary_p[i])) - continue; - - /* Show that a reload register of this class is needed - in this basic block. We do not use insn_needs and - insn_groups because they are overly conservative for - this purpose. */ - if (global && ! basic_block_needs[(int) class][this_block]) - { - basic_block_needs[(int) class][this_block] = 1; - new_basic_block_needs = 1; - } - - /* Decide which time-of-use to count this reload for. */ - switch (reload_when_needed[i]) - { - case RELOAD_OTHER: - this_needs = insn_needs; - this_groups = insn_groups; - this_total_groups = &insn_total_groups; - break; - - case RELOAD_FOR_INPUT: - this_needs = insn_needs_for_inputs; - this_groups = insn_groups_for_inputs; - this_total_groups = &insn_total_groups_for_inputs; - break; - - case RELOAD_FOR_OUTPUT: - this_needs = insn_needs_for_outputs; - this_groups = insn_groups_for_outputs; - this_total_groups = &insn_total_groups_for_outputs; - break; - - case RELOAD_FOR_INSN: - this_needs = insn_needs_for_insn; - this_groups = insn_groups_for_outputs; - this_total_groups = &insn_total_groups_for_insn; - break; - - case RELOAD_FOR_OTHER_ADDRESS: - this_needs = insn_needs_for_other_addr; - this_groups = insn_groups_for_other_addr; - this_total_groups = &insn_total_groups_for_other_addr; - break; - - case RELOAD_FOR_INPUT_ADDRESS: - this_needs = insn_needs_for_in_addr[reload_opnum[i]]; - this_groups = insn_groups_for_in_addr[reload_opnum[i]]; - this_total_groups - = &insn_total_groups_for_in_addr[reload_opnum[i]]; - break; - - case RELOAD_FOR_OUTPUT_ADDRESS: - this_needs = insn_needs_for_out_addr[reload_opnum[i]]; - this_groups = insn_groups_for_out_addr[reload_opnum[i]]; - this_total_groups - = &insn_total_groups_for_out_addr[reload_opnum[i]]; - break; - - case RELOAD_FOR_OPERAND_ADDRESS: - this_needs = insn_needs_for_op_addr; - this_groups = insn_groups_for_op_addr; - this_total_groups = &insn_total_groups_for_op_addr; - break; - } - - mode = reload_inmode[i]; - if (GET_MODE_SIZE (reload_outmode[i]) > GET_MODE_SIZE (mode)) - mode = reload_outmode[i]; - size = CLASS_MAX_NREGS (class, mode); - if (size > 1) - { - enum machine_mode other_mode, allocate_mode; - - /* Count number of groups needed separately from - number of individual regs needed. */ - this_groups[(int) class]++; - p = reg_class_superclasses[(int) class]; - while (*p != LIM_REG_CLASSES) - this_groups[(int) *p++]++; - (*this_total_groups)++; - - /* Record size and mode of a group of this class. */ - /* If more than one size group is needed, - make all groups the largest needed size. */ - if (group_size[(int) class] < size) - { - other_mode = group_mode[(int) class]; - allocate_mode = mode; - - group_size[(int) class] = size; - group_mode[(int) class] = mode; - } - else - { - other_mode = mode; - allocate_mode = group_mode[(int) class]; - } - - /* Crash if two dissimilar machine modes both need - groups of consecutive regs of the same class. */ - - if (other_mode != VOIDmode - && other_mode != allocate_mode - && ! modes_equiv_for_class_p (allocate_mode, - other_mode, - class)) - abort (); - } - else if (size == 1) - { - this_needs[(int) class] += 1; - p = reg_class_superclasses[(int) class]; - while (*p != LIM_REG_CLASSES) - this_needs[(int) *p++] += 1; - } - else - abort (); - } - - /* All reloads have been counted for this insn; - now merge the various times of use. - This sets insn_needs, etc., to the maximum total number - of registers needed at any point in this insn. */ - - for (i = 0; i < N_REG_CLASSES; i++) - { - int in_max, out_max; - - for (in_max = 0, out_max = 0, j = 0; - j < reload_n_operands; j++) - { - in_max = MAX (in_max, insn_needs_for_in_addr[j][i]); - out_max = MAX (out_max, insn_needs_for_out_addr[j][i]); - } - - /* RELOAD_FOR_INSN reloads conflict with inputs, outputs, - and operand addresses but not things used to reload them. - Similarly, RELOAD_FOR_OPERAND_ADDRESS reloads don't - conflict with things needed to reload inputs or - outputs. */ - - in_max = MAX (in_max, insn_needs_for_op_addr[i]); - out_max = MAX (out_max, insn_needs_for_insn[i]); - - insn_needs_for_inputs[i] - = MAX (insn_needs_for_inputs[i] - + insn_needs_for_op_addr[i] - + insn_needs_for_insn[i], - in_max + insn_needs_for_inputs[i]); - - insn_needs_for_outputs[i] += out_max; - insn_needs[i] += MAX (MAX (insn_needs_for_inputs[i], - insn_needs_for_outputs[i]), - insn_needs_for_other_addr[i]); - - for (in_max = 0, out_max = 0, j = 0; - j < reload_n_operands; j++) - { - in_max = MAX (in_max, insn_groups_for_in_addr[j][i]); - out_max = MAX (out_max, insn_groups_for_out_addr[j][i]); - } - - in_max = MAX (in_max, insn_groups_for_op_addr[i]); - out_max = MAX (out_max, insn_groups_for_insn[i]); - - insn_groups_for_inputs[i] - = MAX (insn_groups_for_inputs[i] - + insn_groups_for_op_addr[i] - + insn_groups_for_insn[i], - in_max + insn_groups_for_inputs[i]); - - insn_groups_for_outputs[i] += out_max; - insn_groups[i] += MAX (MAX (insn_groups_for_inputs[i], - insn_groups_for_outputs[i]), - insn_groups_for_other_addr[i]); - } - - for (i = 0; i < reload_n_operands; i++) - { - max_total_input_groups - = MAX (max_total_input_groups, - insn_total_groups_for_in_addr[i]); - max_total_output_groups - = MAX (max_total_output_groups, - insn_total_groups_for_out_addr[i]); - } - - max_total_input_groups = MAX (max_total_input_groups, - insn_total_groups_for_op_addr); - max_total_output_groups = MAX (max_total_output_groups, - insn_total_groups_for_insn); - - insn_total_groups_for_inputs - = MAX (max_total_input_groups + insn_total_groups_for_op_addr - + insn_total_groups_for_insn, - max_total_input_groups + insn_total_groups_for_inputs); - - insn_total_groups_for_outputs += max_total_output_groups; - - insn_total_groups += MAX (MAX (insn_total_groups_for_outputs, - insn_total_groups_for_inputs), - insn_total_groups_for_other_addr); - - /* If this is a CALL_INSN and caller-saves will need - a spill register, act as if the spill register is - needed for this insn. However, the spill register - can be used by any reload of this insn, so we only - need do something if no need for that class has - been recorded. - - The assumption that every CALL_INSN will trigger a - caller-save is highly conservative, however, the number - of cases where caller-saves will need a spill register but - a block containing a CALL_INSN won't need a spill register - of that class should be quite rare. - - If a group is needed, the size and mode of the group will - have been set up at the beginning of this loop. */ - - if (GET_CODE (insn) == CALL_INSN - && caller_save_spill_class != NO_REGS) - { - int *caller_save_needs - = (caller_save_group_size > 1 ? insn_groups : insn_needs); - - if (caller_save_needs[(int) caller_save_spill_class] == 0) - { - register enum reg_class *p - = reg_class_superclasses[(int) caller_save_spill_class]; - - caller_save_needs[(int) caller_save_spill_class]++; - - while (*p != LIM_REG_CLASSES) - caller_save_needs[(int) *p++] += 1; - } - - if (caller_save_group_size > 1) - insn_total_groups = MAX (insn_total_groups, 1); - - - /* Show that this basic block will need a register of - this class. */ - - if (global - && ! (basic_block_needs[(int) caller_save_spill_class] - [this_block])) - { - basic_block_needs[(int) caller_save_spill_class] - [this_block] = 1; - new_basic_block_needs = 1; - } - } - -#ifdef SMALL_REGISTER_CLASSES - /* If this insn stores the value of a function call, - and that value is in a register that has been spilled, - and if the insn needs a reload in a class - that might use that register as the reload register, - then add add an extra need in that class. - This makes sure we have a register available that does - not overlap the return value. */ - if (avoid_return_reg) - { - int regno = REGNO (avoid_return_reg); - int nregs - = HARD_REGNO_NREGS (regno, GET_MODE (avoid_return_reg)); - int r; - int basic_needs[N_REG_CLASSES], basic_groups[N_REG_CLASSES]; - - /* First compute the "basic needs", which counts a - need only in the smallest class in which it - is required. */ - - bcopy (insn_needs, basic_needs, sizeof basic_needs); - bcopy (insn_groups, basic_groups, sizeof basic_groups); - - for (i = 0; i < N_REG_CLASSES; i++) - { - enum reg_class *p; - - if (basic_needs[i] >= 0) - for (p = reg_class_superclasses[i]; - *p != LIM_REG_CLASSES; p++) - basic_needs[(int) *p] -= basic_needs[i]; - - if (basic_groups[i] >= 0) - for (p = reg_class_superclasses[i]; - *p != LIM_REG_CLASSES; p++) - basic_groups[(int) *p] -= basic_groups[i]; - } - - /* Now count extra regs if there might be a conflict with - the return value register. - - ??? This is not quite correct because we don't properly - handle the case of groups, but if we end up doing - something wrong, it either will end up not mattering or - we will abort elsewhere. */ - - for (r = regno; r < regno + nregs; r++) - if (spill_reg_order[r] >= 0) - for (i = 0; i < N_REG_CLASSES; i++) - if (TEST_HARD_REG_BIT (reg_class_contents[i], r)) - { - if (basic_needs[i] > 0 || basic_groups[i] > 0) - { - enum reg_class *p; - - insn_needs[i]++; - p = reg_class_superclasses[i]; - while (*p != LIM_REG_CLASSES) - insn_needs[(int) *p++]++; - } - } - } -#endif /* SMALL_REGISTER_CLASSES */ - - /* For each class, collect maximum need of any insn. */ - - for (i = 0; i < N_REG_CLASSES; i++) - { - if (max_needs[i] < insn_needs[i]) - { - max_needs[i] = insn_needs[i]; - max_needs_insn[i] = insn; - } - if (max_groups[i] < insn_groups[i]) - { - max_groups[i] = insn_groups[i]; - max_groups_insn[i] = insn; - } - if (insn_total_groups > 0) - if (max_nongroups[i] < insn_needs[i]) - { - max_nongroups[i] = insn_needs[i]; - max_nongroups_insn[i] = insn; - } - } - } - /* Note that there is a continue statement above. */ - } - - /* If we allocated any new memory locations, make another pass - since it might have changed elimination offsets. */ - if (starting_frame_size != get_frame_size ()) - something_changed = 1; - - if (dumpfile) - for (i = 0; i < N_REG_CLASSES; i++) - { - if (max_needs[i] > 0) - fprintf (dumpfile, - ";; Need %d reg%s of class %s (for insn %d).\n", - max_needs[i], max_needs[i] == 1 ? "" : "s", - reg_class_names[i], INSN_UID (max_needs_insn[i])); - if (max_nongroups[i] > 0) - fprintf (dumpfile, - ";; Need %d nongroup reg%s of class %s (for insn %d).\n", - max_nongroups[i], max_nongroups[i] == 1 ? "" : "s", - reg_class_names[i], INSN_UID (max_nongroups_insn[i])); - if (max_groups[i] > 0) - fprintf (dumpfile, - ";; Need %d group%s (%smode) of class %s (for insn %d).\n", - max_groups[i], max_groups[i] == 1 ? "" : "s", - mode_name[(int) group_mode[i]], - reg_class_names[i], INSN_UID (max_groups_insn[i])); - } - - /* If we have caller-saves, set up the save areas and see if caller-save - will need a spill register. */ - - if (caller_save_needed - && ! setup_save_areas (&something_changed) - && caller_save_spill_class == NO_REGS) - { - /* The class we will need depends on whether the machine - supports the sum of two registers for an address; see - find_address_reloads for details. */ - - caller_save_spill_class - = double_reg_address_ok ? INDEX_REG_CLASS : BASE_REG_CLASS; - caller_save_group_size - = CLASS_MAX_NREGS (caller_save_spill_class, Pmode); - something_changed = 1; - } - - /* See if anything that happened changes which eliminations are valid. - For example, on the Sparc, whether or not the frame pointer can - be eliminated can depend on what registers have been used. We need - not check some conditions again (such as flag_omit_frame_pointer) - since they can't have changed. */ - - for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) - if ((ep->from == FRAME_POINTER_REGNUM && FRAME_POINTER_REQUIRED) -#ifdef ELIMINABLE_REGS - || ! CAN_ELIMINATE (ep->from, ep->to) -#endif - ) - ep->can_eliminate = 0; - - /* Look for the case where we have discovered that we can't replace - register A with register B and that means that we will now be - trying to replace register A with register C. This means we can - no longer replace register C with register B and we need to disable - such an elimination, if it exists. This occurs often with A == ap, - B == sp, and C == fp. */ - - for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) - { - struct elim_table *op; - register int new_to = -1; - - if (! ep->can_eliminate && ep->can_eliminate_previous) - { - /* Find the current elimination for ep->from, if there is a - new one. */ - for (op = reg_eliminate; - op < ®_eliminate[NUM_ELIMINABLE_REGS]; op++) - if (op->from == ep->from && op->can_eliminate) - { - new_to = op->to; - break; - } - - /* See if there is an elimination of NEW_TO -> EP->TO. If so, - disable it. */ - for (op = reg_eliminate; - op < ®_eliminate[NUM_ELIMINABLE_REGS]; op++) - if (op->from == new_to && op->to == ep->to) - op->can_eliminate = 0; - } - } - - /* See if any registers that we thought we could eliminate the previous - time are no longer eliminable. If so, something has changed and we - must spill the register. Also, recompute the number of eliminable - registers and see if the frame pointer is needed; it is if there is - no elimination of the frame pointer that we can perform. */ - - frame_pointer_needed = 1; - for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) - { - if (ep->can_eliminate && ep->from == FRAME_POINTER_REGNUM) - frame_pointer_needed = 0; - - if (! ep->can_eliminate && ep->can_eliminate_previous) - { - ep->can_eliminate_previous = 0; - spill_hard_reg (ep->from, global, dumpfile, 1); - regs_ever_live[ep->from] = 1; - something_changed = 1; - num_eliminable--; - } - } - - /* If all needs are met, we win. */ - - for (i = 0; i < N_REG_CLASSES; i++) - if (max_needs[i] > 0 || max_groups[i] > 0 || max_nongroups[i] > 0) - break; - if (i == N_REG_CLASSES && !new_basic_block_needs && ! something_changed) - break; - - /* Not all needs are met; must spill some hard regs. */ - - /* Put all registers spilled so far back in potential_reload_regs, but - put them at the front, since we've already spilled most of the - psuedos in them (we might have left some pseudos unspilled if they - were in a block that didn't need any spill registers of a conflicting - class. We used to try to mark off the need for those registers, - but doing so properly is very complex and reallocating them is the - simpler approach. First, "pack" potential_reload_regs by pushing - any nonnegative entries towards the end. That will leave room - for the registers we already spilled. - - Also, undo the marking of the spill registers from the last time - around in FORBIDDEN_REGS since we will be probably be allocating - them again below. - - ??? It is theoretically possible that we might end up not using one - of our previously-spilled registers in this allocation, even though - they are at the head of the list. It's not clear what to do about - this, but it was no better before, when we marked off the needs met - by the previously-spilled registers. With the current code, globals - can be allocated into these registers, but locals cannot. */ - - if (n_spills) - { - for (i = j = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--) - if (potential_reload_regs[i] != -1) - potential_reload_regs[j--] = potential_reload_regs[i]; - - for (i = 0; i < n_spills; i++) - { - potential_reload_regs[i] = spill_regs[i]; - spill_reg_order[spill_regs[i]] = -1; - CLEAR_HARD_REG_BIT (forbidden_regs, spill_regs[i]); - } - - n_spills = 0; - } - - /* Now find more reload regs to satisfy the remaining need - Do it by ascending class number, since otherwise a reg - might be spilled for a big class and might fail to count - for a smaller class even though it belongs to that class. - - Count spilled regs in `spills', and add entries to - `spill_regs' and `spill_reg_order'. - - ??? Note there is a problem here. - When there is a need for a group in a high-numbered class, - and also need for non-group regs that come from a lower class, - the non-group regs are chosen first. If there aren't many regs, - they might leave no room for a group. - - This was happening on the 386. To fix it, we added the code - that calls possible_group_p, so that the lower class won't - break up the last possible group. - - Really fixing the problem would require changes above - in counting the regs already spilled, and in choose_reload_regs. - It might be hard to avoid introducing bugs there. */ - - CLEAR_HARD_REG_SET (counted_for_groups); - CLEAR_HARD_REG_SET (counted_for_nongroups); - - for (class = 0; class < N_REG_CLASSES; class++) - { - /* First get the groups of registers. - If we got single registers first, we might fragment - possible groups. */ - while (max_groups[class] > 0) - { - /* If any single spilled regs happen to form groups, - count them now. Maybe we don't really need - to spill another group. */ - count_possible_groups (group_size, group_mode, max_groups); - - if (max_groups[class] <= 0) - break; - - /* Groups of size 2 (the only groups used on most machines) - are treated specially. */ - if (group_size[class] == 2) - { - /* First, look for a register that will complete a group. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - int other; - - j = potential_reload_regs[i]; - if (j >= 0 && ! TEST_HARD_REG_BIT (bad_spill_regs, j) - && - ((j > 0 && (other = j - 1, spill_reg_order[other] >= 0) - && TEST_HARD_REG_BIT (reg_class_contents[class], j) - && TEST_HARD_REG_BIT (reg_class_contents[class], other) - && HARD_REGNO_MODE_OK (other, group_mode[class]) - && ! TEST_HARD_REG_BIT (counted_for_nongroups, - other) - /* We don't want one part of another group. - We could get "two groups" that overlap! */ - && ! TEST_HARD_REG_BIT (counted_for_groups, other)) - || - (j < FIRST_PSEUDO_REGISTER - 1 - && (other = j + 1, spill_reg_order[other] >= 0) - && TEST_HARD_REG_BIT (reg_class_contents[class], j) - && TEST_HARD_REG_BIT (reg_class_contents[class], other) - && HARD_REGNO_MODE_OK (j, group_mode[class]) - && ! TEST_HARD_REG_BIT (counted_for_nongroups, - other) - && ! TEST_HARD_REG_BIT (counted_for_groups, - other)))) - { - register enum reg_class *p; - - /* We have found one that will complete a group, - so count off one group as provided. */ - max_groups[class]--; - p = reg_class_superclasses[class]; - while (*p != LIM_REG_CLASSES) - max_groups[(int) *p++]--; - - /* Indicate both these regs are part of a group. */ - SET_HARD_REG_BIT (counted_for_groups, j); - SET_HARD_REG_BIT (counted_for_groups, other); - break; - } - } - /* We can't complete a group, so start one. */ - if (i == FIRST_PSEUDO_REGISTER) - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - int k; - j = potential_reload_regs[i]; - /* Verify that J+1 is a potential reload reg. */ - for (k = 0; k < FIRST_PSEUDO_REGISTER; k++) - if (potential_reload_regs[k] == j + 1) - break; - if (j >= 0 && j + 1 < FIRST_PSEUDO_REGISTER - && k < FIRST_PSEUDO_REGISTER - && spill_reg_order[j] < 0 && spill_reg_order[j + 1] < 0 - && TEST_HARD_REG_BIT (reg_class_contents[class], j) - && TEST_HARD_REG_BIT (reg_class_contents[class], j + 1) - && HARD_REGNO_MODE_OK (j, group_mode[class]) - && ! TEST_HARD_REG_BIT (counted_for_nongroups, - j + 1) - && ! TEST_HARD_REG_BIT (bad_spill_regs, j + 1)) - break; - } - - /* I should be the index in potential_reload_regs - of the new reload reg we have found. */ - - if (i >= FIRST_PSEUDO_REGISTER) - { - /* There are no groups left to spill. */ - spill_failure (max_groups_insn[class]); - failure = 1; - goto failed; - } - else - something_changed - |= new_spill_reg (i, class, max_needs, NULL_PTR, - global, dumpfile); - } - else - { - /* For groups of more than 2 registers, - look for a sufficient sequence of unspilled registers, - and spill them all at once. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - int k; - - j = potential_reload_regs[i]; - if (j >= 0 - && j + group_size[class] <= FIRST_PSEUDO_REGISTER - && HARD_REGNO_MODE_OK (j, group_mode[class])) - { - /* Check each reg in the sequence. */ - for (k = 0; k < group_size[class]; k++) - if (! (spill_reg_order[j + k] < 0 - && ! TEST_HARD_REG_BIT (bad_spill_regs, j + k) - && TEST_HARD_REG_BIT (reg_class_contents[class], j + k))) - break; - /* We got a full sequence, so spill them all. */ - if (k == group_size[class]) - { - register enum reg_class *p; - for (k = 0; k < group_size[class]; k++) - { - int idx; - SET_HARD_REG_BIT (counted_for_groups, j + k); - for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++) - if (potential_reload_regs[idx] == j + k) - break; - something_changed - |= new_spill_reg (idx, class, - max_needs, NULL_PTR, - global, dumpfile); - } - - /* We have found one that will complete a group, - so count off one group as provided. */ - max_groups[class]--; - p = reg_class_superclasses[class]; - while (*p != LIM_REG_CLASSES) - max_groups[(int) *p++]--; - - break; - } - } - } - /* We couldn't find any registers for this reload. - Avoid going into an infinite loop. */ - if (i >= FIRST_PSEUDO_REGISTER) - { - /* There are no groups left. */ - spill_failure (max_groups_insn[class]); - failure = 1; - goto failed; - } - } - } - - /* Now similarly satisfy all need for single registers. */ - - while (max_needs[class] > 0 || max_nongroups[class] > 0) - { -#ifdef SMALL_REGISTER_CLASSES - /* This should be right for all machines, but only the 386 - is known to need it, so this conditional plays safe. - ??? For 2.5, try making this unconditional. */ - /* If we spilled enough regs, but they weren't counted - against the non-group need, see if we can count them now. - If so, we can avoid some actual spilling. */ - if (max_needs[class] <= 0 && max_nongroups[class] > 0) - for (i = 0; i < n_spills; i++) - if (TEST_HARD_REG_BIT (reg_class_contents[class], - spill_regs[i]) - && !TEST_HARD_REG_BIT (counted_for_groups, - spill_regs[i]) - && !TEST_HARD_REG_BIT (counted_for_nongroups, - spill_regs[i]) - && max_nongroups[class] > 0) - { - register enum reg_class *p; - - SET_HARD_REG_BIT (counted_for_nongroups, spill_regs[i]); - max_nongroups[class]--; - p = reg_class_superclasses[class]; - while (*p != LIM_REG_CLASSES) - max_nongroups[(int) *p++]--; - } - if (max_needs[class] <= 0 && max_nongroups[class] <= 0) - break; -#endif - - /* Consider the potential reload regs that aren't - yet in use as reload regs, in order of preference. - Find the most preferred one that's in this class. */ - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (potential_reload_regs[i] >= 0 - && TEST_HARD_REG_BIT (reg_class_contents[class], - potential_reload_regs[i]) - /* If this reg will not be available for groups, - pick one that does not foreclose possible groups. - This is a kludge, and not very general, - but it should be sufficient to make the 386 work, - and the problem should not occur on machines with - more registers. */ - && (max_nongroups[class] == 0 - || possible_group_p (potential_reload_regs[i], max_groups))) - break; - - /* If we couldn't get a register, try to get one even if we - might foreclose possible groups. This may cause problems - later, but that's better than aborting now, since it is - possible that we will, in fact, be able to form the needed - group even with this allocation. */ - - if (i >= FIRST_PSEUDO_REGISTER - && (asm_noperands (max_needs[class] > 0 - ? max_needs_insn[class] - : max_nongroups_insn[class]) - < 0)) - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (potential_reload_regs[i] >= 0 - && TEST_HARD_REG_BIT (reg_class_contents[class], - potential_reload_regs[i])) - break; - - /* I should be the index in potential_reload_regs - of the new reload reg we have found. */ - - if (i >= FIRST_PSEUDO_REGISTER) - { - /* There are no possible registers left to spill. */ - spill_failure (max_needs[class] > 0 ? max_needs_insn[class] - : max_nongroups_insn[class]); - failure = 1; - goto failed; - } - else - something_changed - |= new_spill_reg (i, class, max_needs, max_nongroups, - global, dumpfile); - } - } - } - - /* If global-alloc was run, notify it of any register eliminations we have - done. */ - if (global) - for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) - if (ep->can_eliminate) - mark_elimination (ep->from, ep->to); - - /* Insert code to save and restore call-clobbered hard regs - around calls. Tell if what mode to use so that we will process - those insns in reload_as_needed if we have to. */ - - if (caller_save_needed) - save_call_clobbered_regs (num_eliminable ? QImode - : caller_save_spill_class != NO_REGS ? HImode - : VOIDmode); - - /* If a pseudo has no hard reg, delete the insns that made the equivalence. - If that insn didn't set the register (i.e., it copied the register to - memory), just delete that insn instead of the equivalencing insn plus - anything now dead. If we call delete_dead_insn on that insn, we may - delete the insn that actually sets the register if the register die - there and that is incorrect. */ - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (reg_renumber[i] < 0 && reg_equiv_init[i] != 0 - && GET_CODE (reg_equiv_init[i]) != NOTE) - { - if (reg_set_p (regno_reg_rtx[i], PATTERN (reg_equiv_init[i]))) - delete_dead_insn (reg_equiv_init[i]); - else - { - PUT_CODE (reg_equiv_init[i], NOTE); - NOTE_SOURCE_FILE (reg_equiv_init[i]) = 0; - NOTE_LINE_NUMBER (reg_equiv_init[i]) = NOTE_INSN_DELETED; - } - } - - /* Use the reload registers where necessary - by generating move instructions to move the must-be-register - values into or out of the reload registers. */ - - if (something_needs_reloads || something_needs_elimination - || (caller_save_needed && num_eliminable) - || caller_save_spill_class != NO_REGS) - reload_as_needed (first, global); - - /* If we were able to eliminate the frame pointer, show that it is no - longer live at the start of any basic block. If it ls live by - virtue of being in a pseudo, that pseudo will be marked live - and hence the frame pointer will be known to be live via that - pseudo. */ - - if (! frame_pointer_needed) - for (i = 0; i < n_basic_blocks; i++) - basic_block_live_at_start[i][FRAME_POINTER_REGNUM / REGSET_ELT_BITS] - &= ~ ((REGSET_ELT_TYPE) 1 << (FRAME_POINTER_REGNUM % REGSET_ELT_BITS)); - - /* Come here (with failure set nonzero) if we can't get enough spill regs - and we decide not to abort about it. */ - failed: - - reload_in_progress = 0; - - /* Now eliminate all pseudo regs by modifying them into - their equivalent memory references. - The REG-rtx's for the pseudos are modified in place, - so all insns that used to refer to them now refer to memory. - - For a reg that has a reg_equiv_address, all those insns - were changed by reloading so that no insns refer to it any longer; - but the DECL_RTL of a variable decl may refer to it, - and if so this causes the debugging info to mention the variable. */ - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - { - rtx addr = 0; - int in_struct = 0; - if (reg_equiv_mem[i]) - { - addr = XEXP (reg_equiv_mem[i], 0); - in_struct = MEM_IN_STRUCT_P (reg_equiv_mem[i]); - } - if (reg_equiv_address[i]) - addr = reg_equiv_address[i]; - if (addr) - { - if (reg_renumber[i] < 0) - { - rtx reg = regno_reg_rtx[i]; - XEXP (reg, 0) = addr; - REG_USERVAR_P (reg) = 0; - MEM_IN_STRUCT_P (reg) = in_struct; - PUT_CODE (reg, MEM); - } - else if (reg_equiv_mem[i]) - XEXP (reg_equiv_mem[i], 0) = addr; - } - } - -#ifdef PRESERVE_DEATH_INFO_REGNO_P - /* Make a pass over all the insns and remove death notes for things that - are no longer registers or no longer die in the insn (e.g., an input - and output pseudo being tied). */ - - for (insn = first; insn; insn = NEXT_INSN (insn)) - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') - { - rtx note, next; - - for (note = REG_NOTES (insn); note; note = next) - { - next = XEXP (note, 1); - if (REG_NOTE_KIND (note) == REG_DEAD - && (GET_CODE (XEXP (note, 0)) != REG - || reg_set_p (XEXP (note, 0), PATTERN (insn)))) - remove_note (insn, note); - } - } -#endif - - /* Indicate that we no longer have known memory locations or constants. */ - reg_equiv_constant = 0; - reg_equiv_memory_loc = 0; - - return failure; -} - -/* Nonzero if, after spilling reg REGNO for non-groups, - it will still be possible to find a group if we still need one. */ - -static int -possible_group_p (regno, max_groups) - int regno; - int *max_groups; -{ - int i; - int class = (int) NO_REGS; - - for (i = 0; i < (int) N_REG_CLASSES; i++) - if (max_groups[i] > 0) - { - class = i; - break; - } - - if (class == (int) NO_REGS) - return 1; - - /* Consider each pair of consecutive registers. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER - 1; i++) - { - /* Ignore pairs that include reg REGNO. */ - if (i == regno || i + 1 == regno) - continue; - - /* Ignore pairs that are outside the class that needs the group. - ??? Here we fail to handle the case where two different classes - independently need groups. But this never happens with our - current machine descriptions. */ - if (! (TEST_HARD_REG_BIT (reg_class_contents[class], i) - && TEST_HARD_REG_BIT (reg_class_contents[class], i + 1))) - continue; - - /* A pair of consecutive regs we can still spill does the trick. */ - if (spill_reg_order[i] < 0 && spill_reg_order[i + 1] < 0 - && ! TEST_HARD_REG_BIT (bad_spill_regs, i) - && ! TEST_HARD_REG_BIT (bad_spill_regs, i + 1)) - return 1; - - /* A pair of one already spilled and one we can spill does it - provided the one already spilled is not otherwise reserved. */ - if (spill_reg_order[i] < 0 - && ! TEST_HARD_REG_BIT (bad_spill_regs, i) - && spill_reg_order[i + 1] >= 0 - && ! TEST_HARD_REG_BIT (counted_for_groups, i + 1) - && ! TEST_HARD_REG_BIT (counted_for_nongroups, i + 1)) - return 1; - if (spill_reg_order[i + 1] < 0 - && ! TEST_HARD_REG_BIT (bad_spill_regs, i + 1) - && spill_reg_order[i] >= 0 - && ! TEST_HARD_REG_BIT (counted_for_groups, i) - && ! TEST_HARD_REG_BIT (counted_for_nongroups, i)) - return 1; - } - - return 0; -} - -/* Count any groups that can be formed from the registers recently spilled. - This is done class by class, in order of ascending class number. */ - -static void -count_possible_groups (group_size, group_mode, max_groups) - int *group_size; - enum machine_mode *group_mode; - int *max_groups; -{ - int i; - /* Now find all consecutive groups of spilled registers - and mark each group off against the need for such groups. - But don't count them against ordinary need, yet. */ - - for (i = 0; i < N_REG_CLASSES; i++) - if (group_size[i] > 1) - { - HARD_REG_SET new; - int j; - - CLEAR_HARD_REG_SET (new); - - /* Make a mask of all the regs that are spill regs in class I. */ - for (j = 0; j < n_spills; j++) - if (TEST_HARD_REG_BIT (reg_class_contents[i], spill_regs[j]) - && ! TEST_HARD_REG_BIT (counted_for_groups, spill_regs[j]) - && ! TEST_HARD_REG_BIT (counted_for_nongroups, - spill_regs[j])) - SET_HARD_REG_BIT (new, spill_regs[j]); - - /* Find each consecutive group of them. */ - for (j = 0; j < FIRST_PSEUDO_REGISTER && max_groups[i] > 0; j++) - if (TEST_HARD_REG_BIT (new, j) - && j + group_size[i] <= FIRST_PSEUDO_REGISTER - /* Next line in case group-mode for this class - demands an even-odd pair. */ - && HARD_REGNO_MODE_OK (j, group_mode[i])) - { - int k; - for (k = 1; k < group_size[i]; k++) - if (! TEST_HARD_REG_BIT (new, j + k)) - break; - if (k == group_size[i]) - { - /* We found a group. Mark it off against this class's - need for groups, and against each superclass too. */ - register enum reg_class *p; - max_groups[i]--; - p = reg_class_superclasses[i]; - while (*p != LIM_REG_CLASSES) - max_groups[(int) *p++]--; - /* Don't count these registers again. */ - for (k = 0; k < group_size[i]; k++) - SET_HARD_REG_BIT (counted_for_groups, j + k); - } - /* Skip to the last reg in this group. When j is incremented - above, it will then point to the first reg of the next - possible group. */ - j += k - 1; - } - } - -} - -/* ALLOCATE_MODE is a register mode that needs to be reloaded. OTHER_MODE is - another mode that needs to be reloaded for the same register class CLASS. - If any reg in CLASS allows ALLOCATE_MODE but not OTHER_MODE, fail. - ALLOCATE_MODE will never be smaller than OTHER_MODE. - - This code used to also fail if any reg in CLASS allows OTHER_MODE but not - ALLOCATE_MODE. This test is unnecessary, because we will never try to put - something of mode ALLOCATE_MODE into an OTHER_MODE register. Testing this - causes unnecessary failures on machines requiring alignment of register - groups when the two modes are different sizes, because the larger mode has - more strict alignment rules than the smaller mode. */ - -static int -modes_equiv_for_class_p (allocate_mode, other_mode, class) - enum machine_mode allocate_mode, other_mode; - enum reg_class class; -{ - register int regno; - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - { - if (TEST_HARD_REG_BIT (reg_class_contents[(int) class], regno) - && HARD_REGNO_MODE_OK (regno, allocate_mode) - && ! HARD_REGNO_MODE_OK (regno, other_mode)) - return 0; - } - return 1; -} - -/* Handle the failure to find a register to spill. - INSN should be one of the insns which needed this particular spill reg. */ - -static void -spill_failure (insn) - rtx insn; -{ - if (asm_noperands (PATTERN (insn)) >= 0) - error_for_asm (insn, "`asm' needs too many reloads"); - else - abort (); -} - -/* Add a new register to the tables of available spill-registers - (as well as spilling all pseudos allocated to the register). - I is the index of this register in potential_reload_regs. - CLASS is the regclass whose need is being satisfied. - MAX_NEEDS and MAX_NONGROUPS are the vectors of needs, - so that this register can count off against them. - MAX_NONGROUPS is 0 if this register is part of a group. - GLOBAL and DUMPFILE are the same as the args that `reload' got. */ - -static int -new_spill_reg (i, class, max_needs, max_nongroups, global, dumpfile) - int i; - int class; - int *max_needs; - int *max_nongroups; - int global; - FILE *dumpfile; -{ - register enum reg_class *p; - int val; - int regno = potential_reload_regs[i]; - - if (i >= FIRST_PSEUDO_REGISTER) - abort (); /* Caller failed to find any register. */ - - if (fixed_regs[regno] || TEST_HARD_REG_BIT (forbidden_regs, regno)) - fatal ("fixed or forbidden register was spilled.\n\ -This may be due to a compiler bug or to impossible asm statements."); - - /* Make reg REGNO an additional reload reg. */ - - potential_reload_regs[i] = -1; - spill_regs[n_spills] = regno; - spill_reg_order[regno] = n_spills; - if (dumpfile) - fprintf (dumpfile, "Spilling reg %d.\n", spill_regs[n_spills]); - - /* Clear off the needs we just satisfied. */ - - max_needs[class]--; - p = reg_class_superclasses[class]; - while (*p != LIM_REG_CLASSES) - max_needs[(int) *p++]--; - - if (max_nongroups && max_nongroups[class] > 0) - { - SET_HARD_REG_BIT (counted_for_nongroups, regno); - max_nongroups[class]--; - p = reg_class_superclasses[class]; - while (*p != LIM_REG_CLASSES) - max_nongroups[(int) *p++]--; - } - - /* Spill every pseudo reg that was allocated to this reg - or to something that overlaps this reg. */ - - val = spill_hard_reg (spill_regs[n_spills], global, dumpfile, 0); - - /* If there are some registers still to eliminate and this register - wasn't ever used before, additional stack space may have to be - allocated to store this register. Thus, we may have changed the offset - between the stack and frame pointers, so mark that something has changed. - (If new pseudos were spilled, thus requiring more space, VAL would have - been set non-zero by the call to spill_hard_reg above since additional - reloads may be needed in that case. - - One might think that we need only set VAL to 1 if this is a call-used - register. However, the set of registers that must be saved by the - prologue is not identical to the call-used set. For example, the - register used by the call insn for the return PC is a call-used register, - but must be saved by the prologue. */ - if (num_eliminable && ! regs_ever_live[spill_regs[n_spills]]) - val = 1; - - regs_ever_live[spill_regs[n_spills]] = 1; - n_spills++; - - return val; -} - -/* Delete an unneeded INSN and any previous insns who sole purpose is loading - data that is dead in INSN. */ - -static void -delete_dead_insn (insn) - rtx insn; -{ - rtx prev = prev_real_insn (insn); - rtx prev_dest; - - /* If the previous insn sets a register that dies in our insn, delete it - too. */ - if (prev && GET_CODE (PATTERN (prev)) == SET - && (prev_dest = SET_DEST (PATTERN (prev)), GET_CODE (prev_dest) == REG) - && reg_mentioned_p (prev_dest, PATTERN (insn)) - && find_regno_note (insn, REG_DEAD, REGNO (prev_dest))) - delete_dead_insn (prev); - - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; -} - -/* Modify the home of pseudo-reg I. - The new home is present in reg_renumber[I]. - - FROM_REG may be the hard reg that the pseudo-reg is being spilled from; - or it may be -1, meaning there is none or it is not relevant. - This is used so that all pseudos spilled from a given hard reg - can share one stack slot. */ - -static void -alter_reg (i, from_reg) - register int i; - int from_reg; -{ - /* When outputting an inline function, this can happen - for a reg that isn't actually used. */ - if (regno_reg_rtx[i] == 0) - return; - - /* If the reg got changed to a MEM at rtl-generation time, - ignore it. */ - if (GET_CODE (regno_reg_rtx[i]) != REG) - return; - - /* Modify the reg-rtx to contain the new hard reg - number or else to contain its pseudo reg number. */ - REGNO (regno_reg_rtx[i]) - = reg_renumber[i] >= 0 ? reg_renumber[i] : i; - - /* If we have a pseudo that is needed but has no hard reg or equivalent, - allocate a stack slot for it. */ - - if (reg_renumber[i] < 0 - && reg_n_refs[i] > 0 - && reg_equiv_constant[i] == 0 - && reg_equiv_memory_loc[i] == 0) - { - register rtx x; - int inherent_size = PSEUDO_REGNO_BYTES (i); - int total_size = MAX (inherent_size, reg_max_ref_width[i]); - int adjust = 0; - - /* Each pseudo reg has an inherent size which comes from its own mode, - and a total size which provides room for paradoxical subregs - which refer to the pseudo reg in wider modes. - - We can use a slot already allocated if it provides both - enough inherent space and enough total space. - Otherwise, we allocate a new slot, making sure that it has no less - inherent space, and no less total space, then the previous slot. */ - if (from_reg == -1) - { - /* No known place to spill from => no slot to reuse. */ - x = assign_stack_local (GET_MODE (regno_reg_rtx[i]), total_size, -1); -#if BYTES_BIG_ENDIAN - /* Cancel the big-endian correction done in assign_stack_local. - Get the address of the beginning of the slot. - This is so we can do a big-endian correction unconditionally - below. */ - adjust = inherent_size - total_size; -#endif - } - /* Reuse a stack slot if possible. */ - else if (spill_stack_slot[from_reg] != 0 - && spill_stack_slot_width[from_reg] >= total_size - && (GET_MODE_SIZE (GET_MODE (spill_stack_slot[from_reg])) - >= inherent_size)) - x = spill_stack_slot[from_reg]; - /* Allocate a bigger slot. */ - else - { - /* Compute maximum size needed, both for inherent size - and for total size. */ - enum machine_mode mode = GET_MODE (regno_reg_rtx[i]); - if (spill_stack_slot[from_reg]) - { - if (GET_MODE_SIZE (GET_MODE (spill_stack_slot[from_reg])) - > inherent_size) - mode = GET_MODE (spill_stack_slot[from_reg]); - if (spill_stack_slot_width[from_reg] > total_size) - total_size = spill_stack_slot_width[from_reg]; - } - /* Make a slot with that size. */ - x = assign_stack_local (mode, total_size, -1); -#if BYTES_BIG_ENDIAN - /* Cancel the big-endian correction done in assign_stack_local. - Get the address of the beginning of the slot. - This is so we can do a big-endian correction unconditionally - below. */ - adjust = GET_MODE_SIZE (mode) - total_size; -#endif - spill_stack_slot[from_reg] = x; - spill_stack_slot_width[from_reg] = total_size; - } - -#if BYTES_BIG_ENDIAN - /* On a big endian machine, the "address" of the slot - is the address of the low part that fits its inherent mode. */ - if (inherent_size < total_size) - adjust += (total_size - inherent_size); -#endif /* BYTES_BIG_ENDIAN */ - - /* If we have any adjustment to make, or if the stack slot is the - wrong mode, make a new stack slot. */ - if (adjust != 0 || GET_MODE (x) != GET_MODE (regno_reg_rtx[i])) - { - x = gen_rtx (MEM, GET_MODE (regno_reg_rtx[i]), - plus_constant (XEXP (x, 0), adjust)); - RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (regno_reg_rtx[i]); - } - - /* Save the stack slot for later. */ - reg_equiv_memory_loc[i] = x; - } -} - -/* Mark the slots in regs_ever_live for the hard regs - used by pseudo-reg number REGNO. */ - -void -mark_home_live (regno) - int regno; -{ - register int i, lim; - i = reg_renumber[regno]; - if (i < 0) - return; - lim = i + HARD_REGNO_NREGS (i, PSEUDO_REGNO_MODE (regno)); - while (i < lim) - regs_ever_live[i++] = 1; -} - -/* This function handles the tracking of elimination offsets around branches. - - X is a piece of RTL being scanned. - - INSN is the insn that it came from, if any. - - INITIAL_P is non-zero if we are to set the offset to be the initial - offset and zero if we are setting the offset of the label to be the - current offset. */ - -static void -set_label_offsets (x, insn, initial_p) - rtx x; - rtx insn; - int initial_p; -{ - enum rtx_code code = GET_CODE (x); - rtx tem; - int i; - struct elim_table *p; - - switch (code) - { - case LABEL_REF: - if (LABEL_REF_NONLOCAL_P (x)) - return; - - x = XEXP (x, 0); - - /* ... fall through ... */ - - case CODE_LABEL: - /* If we know nothing about this label, set the desired offsets. Note - that this sets the offset at a label to be the offset before a label - if we don't know anything about the label. This is not correct for - the label after a BARRIER, but is the best guess we can make. If - we guessed wrong, we will suppress an elimination that might have - been possible had we been able to guess correctly. */ - - if (! offsets_known_at[CODE_LABEL_NUMBER (x)]) - { - for (i = 0; i < NUM_ELIMINABLE_REGS; i++) - offsets_at[CODE_LABEL_NUMBER (x)][i] - = (initial_p ? reg_eliminate[i].initial_offset - : reg_eliminate[i].offset); - offsets_known_at[CODE_LABEL_NUMBER (x)] = 1; - } - - /* Otherwise, if this is the definition of a label and it is - preceded by a BARRIER, set our offsets to the known offset of - that label. */ - - else if (x == insn - && (tem = prev_nonnote_insn (insn)) != 0 - && GET_CODE (tem) == BARRIER) - { - num_not_at_initial_offset = 0; - for (i = 0; i < NUM_ELIMINABLE_REGS; i++) - { - reg_eliminate[i].offset = reg_eliminate[i].previous_offset - = offsets_at[CODE_LABEL_NUMBER (x)][i]; - if (reg_eliminate[i].can_eliminate - && (reg_eliminate[i].offset - != reg_eliminate[i].initial_offset)) - num_not_at_initial_offset++; - } - } - - else - /* If neither of the above cases is true, compare each offset - with those previously recorded and suppress any eliminations - where the offsets disagree. */ - - for (i = 0; i < NUM_ELIMINABLE_REGS; i++) - if (offsets_at[CODE_LABEL_NUMBER (x)][i] - != (initial_p ? reg_eliminate[i].initial_offset - : reg_eliminate[i].offset)) - reg_eliminate[i].can_eliminate = 0; - - return; - - case JUMP_INSN: - set_label_offsets (PATTERN (insn), insn, initial_p); - - /* ... fall through ... */ - - case INSN: - case CALL_INSN: - /* Any labels mentioned in REG_LABEL notes can be branched to indirectly - and hence must have all eliminations at their initial offsets. */ - for (tem = REG_NOTES (x); tem; tem = XEXP (tem, 1)) - if (REG_NOTE_KIND (tem) == REG_LABEL) - set_label_offsets (XEXP (tem, 0), insn, 1); - return; - - case ADDR_VEC: - case ADDR_DIFF_VEC: - /* Each of the labels in the address vector must be at their initial - offsets. We want the first first for ADDR_VEC and the second - field for ADDR_DIFF_VEC. */ - - for (i = 0; i < XVECLEN (x, code == ADDR_DIFF_VEC); i++) - set_label_offsets (XVECEXP (x, code == ADDR_DIFF_VEC, i), - insn, initial_p); - return; - - case SET: - /* We only care about setting PC. If the source is not RETURN, - IF_THEN_ELSE, or a label, disable any eliminations not at - their initial offsets. Similarly if any arm of the IF_THEN_ELSE - isn't one of those possibilities. For branches to a label, - call ourselves recursively. - - Note that this can disable elimination unnecessarily when we have - a non-local goto since it will look like a non-constant jump to - someplace in the current function. This isn't a significant - problem since such jumps will normally be when all elimination - pairs are back to their initial offsets. */ - - if (SET_DEST (x) != pc_rtx) - return; - - switch (GET_CODE (SET_SRC (x))) - { - case PC: - case RETURN: - return; - - case LABEL_REF: - set_label_offsets (XEXP (SET_SRC (x), 0), insn, initial_p); - return; - - case IF_THEN_ELSE: - tem = XEXP (SET_SRC (x), 1); - if (GET_CODE (tem) == LABEL_REF) - set_label_offsets (XEXP (tem, 0), insn, initial_p); - else if (GET_CODE (tem) != PC && GET_CODE (tem) != RETURN) - break; - - tem = XEXP (SET_SRC (x), 2); - if (GET_CODE (tem) == LABEL_REF) - set_label_offsets (XEXP (tem, 0), insn, initial_p); - else if (GET_CODE (tem) != PC && GET_CODE (tem) != RETURN) - break; - return; - } - - /* If we reach here, all eliminations must be at their initial - offset because we are doing a jump to a variable address. */ - for (p = reg_eliminate; p < ®_eliminate[NUM_ELIMINABLE_REGS]; p++) - if (p->offset != p->initial_offset) - p->can_eliminate = 0; - } -} - -/* Used for communication between the next two function to properly share - the vector for an ASM_OPERANDS. */ - -static struct rtvec_def *old_asm_operands_vec, *new_asm_operands_vec; - -/* Scan X and replace any eliminable registers (such as fp) with a - replacement (such as sp), plus an offset. - - MEM_MODE is the mode of an enclosing MEM. We need this to know how - much to adjust a register for, e.g., PRE_DEC. Also, if we are inside a - MEM, we are allowed to replace a sum of a register and the constant zero - with the register, which we cannot do outside a MEM. In addition, we need - to record the fact that a register is referenced outside a MEM. - - If INSN is nonzero, it is the insn containing X. If we replace a REG - in a SET_DEST with an equivalent MEM and INSN is non-zero, write a - CLOBBER of the pseudo after INSN so find_equiv_regs will know that - that the REG is being modified. - - If we see a modification to a register we know about, take the - appropriate action (see case SET, below). - - REG_EQUIV_MEM and REG_EQUIV_ADDRESS contain address that have had - replacements done assuming all offsets are at their initial values. If - they are not, or if REG_EQUIV_ADDRESS is nonzero for a pseudo we - encounter, return the actual location so that find_reloads will do - the proper thing. */ - -rtx -eliminate_regs (x, mem_mode, insn) - rtx x; - enum machine_mode mem_mode; - rtx insn; -{ - enum rtx_code code = GET_CODE (x); - struct elim_table *ep; - int regno; - rtx new; - int i, j; - char *fmt; - int copied = 0; - - switch (code) - { - case CONST_INT: - case CONST_DOUBLE: - case CONST: - case SYMBOL_REF: - case CODE_LABEL: - case PC: - case CC0: - case ASM_INPUT: - case ADDR_VEC: - case ADDR_DIFF_VEC: - case RETURN: - return x; - - case REG: - regno = REGNO (x); - - /* First handle the case where we encounter a bare register that - is eliminable. Replace it with a PLUS. */ - if (regno < FIRST_PSEUDO_REGISTER) - { - for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; - ep++) - if (ep->from_rtx == x && ep->can_eliminate) - { - if (! mem_mode) - ep->ref_outside_mem = 1; - return plus_constant (ep->to_rtx, ep->previous_offset); - } - - } - else if (reg_equiv_memory_loc && reg_equiv_memory_loc[regno] - && (reg_equiv_address[regno] || num_not_at_initial_offset)) - { - /* In this case, find_reloads would attempt to either use an - incorrect address (if something is not at its initial offset) - or substitute an replaced address into an insn (which loses - if the offset is changed by some later action). So we simply - return the replaced stack slot (assuming it is changed by - elimination) and ignore the fact that this is actually a - reference to the pseudo. Ensure we make a copy of the - address in case it is shared. */ - new = eliminate_regs (reg_equiv_memory_loc[regno], - mem_mode, NULL_RTX); - if (new != reg_equiv_memory_loc[regno]) - { - cannot_omit_stores[regno] = 1; - return copy_rtx (new); - } - } - return x; - - case PLUS: - /* If this is the sum of an eliminable register and a constant, rework - the sum. */ - if (GET_CODE (XEXP (x, 0)) == REG - && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER - && CONSTANT_P (XEXP (x, 1))) - { - for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; - ep++) - if (ep->from_rtx == XEXP (x, 0) && ep->can_eliminate) - { - if (! mem_mode) - ep->ref_outside_mem = 1; - - /* The only time we want to replace a PLUS with a REG (this - occurs when the constant operand of the PLUS is the negative - of the offset) is when we are inside a MEM. We won't want - to do so at other times because that would change the - structure of the insn in a way that reload can't handle. - We special-case the commonest situation in - eliminate_regs_in_insn, so just replace a PLUS with a - PLUS here, unless inside a MEM. */ - if (mem_mode != 0 && GET_CODE (XEXP (x, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) == - ep->previous_offset) - return ep->to_rtx; - else - return gen_rtx (PLUS, Pmode, ep->to_rtx, - plus_constant (XEXP (x, 1), - ep->previous_offset)); - } - - /* If the register is not eliminable, we are done since the other - operand is a constant. */ - return x; - } - - /* If this is part of an address, we want to bring any constant to the - outermost PLUS. We will do this by doing register replacement in - our operands and seeing if a constant shows up in one of them. - - We assume here this is part of an address (or a "load address" insn) - since an eliminable register is not likely to appear in any other - context. - - If we have (plus (eliminable) (reg)), we want to produce - (plus (plus (replacement) (reg) (const))). If this was part of a - normal add insn, (plus (replacement) (reg)) will be pushed as a - reload. This is the desired action. */ - - { - rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, NULL_RTX); - rtx new1 = eliminate_regs (XEXP (x, 1), mem_mode, NULL_RTX); - - if (new0 != XEXP (x, 0) || new1 != XEXP (x, 1)) - { - /* If one side is a PLUS and the other side is a pseudo that - didn't get a hard register but has a reg_equiv_constant, - we must replace the constant here since it may no longer - be in the position of any operand. */ - if (GET_CODE (new0) == PLUS && GET_CODE (new1) == REG - && REGNO (new1) >= FIRST_PSEUDO_REGISTER - && reg_renumber[REGNO (new1)] < 0 - && reg_equiv_constant != 0 - && reg_equiv_constant[REGNO (new1)] != 0) - new1 = reg_equiv_constant[REGNO (new1)]; - else if (GET_CODE (new1) == PLUS && GET_CODE (new0) == REG - && REGNO (new0) >= FIRST_PSEUDO_REGISTER - && reg_renumber[REGNO (new0)] < 0 - && reg_equiv_constant[REGNO (new0)] != 0) - new0 = reg_equiv_constant[REGNO (new0)]; - - new = form_sum (new0, new1); - - /* As above, if we are not inside a MEM we do not want to - turn a PLUS into something else. We might try to do so here - for an addition of 0 if we aren't optimizing. */ - if (! mem_mode && GET_CODE (new) != PLUS) - return gen_rtx (PLUS, GET_MODE (x), new, const0_rtx); - else - return new; - } - } - return x; - - case EXPR_LIST: - /* If we have something in XEXP (x, 0), the usual case, eliminate it. */ - if (XEXP (x, 0)) - { - new = eliminate_regs (XEXP (x, 0), mem_mode, NULL_RTX); - if (new != XEXP (x, 0)) - x = gen_rtx (EXPR_LIST, REG_NOTE_KIND (x), new, XEXP (x, 1)); - } - - /* ... fall through ... */ - - case INSN_LIST: - /* Now do eliminations in the rest of the chain. If this was - an EXPR_LIST, this might result in allocating more memory than is - strictly needed, but it simplifies the code. */ - if (XEXP (x, 1)) - { - new = eliminate_regs (XEXP (x, 1), mem_mode, NULL_RTX); - if (new != XEXP (x, 1)) - return gen_rtx (INSN_LIST, GET_MODE (x), XEXP (x, 0), new); - } - return x; - - case CALL: - case COMPARE: - case MINUS: - case MULT: - case DIV: case UDIV: - case MOD: case UMOD: - case AND: case IOR: case XOR: - case LSHIFT: case ASHIFT: case ROTATE: - case ASHIFTRT: case LSHIFTRT: case ROTATERT: - case NE: case EQ: - case GE: case GT: case GEU: case GTU: - case LE: case LT: case LEU: case LTU: - { - rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, NULL_RTX); - rtx new1 - = XEXP (x, 1) ? eliminate_regs (XEXP (x, 1), mem_mode, NULL_RTX) : 0; - - if (new0 != XEXP (x, 0) || new1 != XEXP (x, 1)) - return gen_rtx (code, GET_MODE (x), new0, new1); - } - return x; - - case PRE_INC: - case POST_INC: - case PRE_DEC: - case POST_DEC: - for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) - if (ep->to_rtx == XEXP (x, 0)) - { - int size = GET_MODE_SIZE (mem_mode); - - /* If more bytes than MEM_MODE are pushed, account for them. */ -#ifdef PUSH_ROUNDING - if (ep->to_rtx == stack_pointer_rtx) - size = PUSH_ROUNDING (size); -#endif - if (code == PRE_DEC || code == POST_DEC) - ep->offset += size; - else - ep->offset -= size; - } - - /* Fall through to generic unary operation case. */ - case USE: - case STRICT_LOW_PART: - case NEG: case NOT: - case SIGN_EXTEND: case ZERO_EXTEND: - case TRUNCATE: case FLOAT_EXTEND: case FLOAT_TRUNCATE: - case FLOAT: case FIX: - case UNSIGNED_FIX: case UNSIGNED_FLOAT: - case ABS: - case SQRT: - case FFS: - new = eliminate_regs (XEXP (x, 0), mem_mode, NULL_RTX); - if (new != XEXP (x, 0)) - return gen_rtx (code, GET_MODE (x), new); - return x; - - case SUBREG: - /* Similar to above processing, but preserve SUBREG_WORD. - Convert (subreg (mem)) to (mem) if not paradoxical. - Also, if we have a non-paradoxical (subreg (pseudo)) and the - pseudo didn't get a hard reg, we must replace this with the - eliminated version of the memory location because push_reloads - may do the replacement in certain circumstances. */ - if (GET_CODE (SUBREG_REG (x)) == REG - && (GET_MODE_SIZE (GET_MODE (x)) - <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) - && reg_equiv_memory_loc != 0 - && reg_equiv_memory_loc[REGNO (SUBREG_REG (x))] != 0) - { - new = eliminate_regs (reg_equiv_memory_loc[REGNO (SUBREG_REG (x))], - mem_mode, NULL_RTX); - - /* If we didn't change anything, we must retain the pseudo. */ - if (new == reg_equiv_memory_loc[REGNO (SUBREG_REG (x))]) - new = XEXP (x, 0); - else - /* Otherwise, ensure NEW isn't shared in case we have to reload - it. */ - new = copy_rtx (new); - } - else - new = eliminate_regs (SUBREG_REG (x), mem_mode, NULL_RTX); - - if (new != XEXP (x, 0)) - { - if (GET_CODE (new) == MEM - && (GET_MODE_SIZE (GET_MODE (x)) - <= GET_MODE_SIZE (GET_MODE (new))) -#if defined(BYTES_LOADS_ZERO_EXTEND) || defined(BYTE_LOADS_SIGN_EXTEND) - /* On these machines we will be reloading what is - inside the SUBREG if it originally was a pseudo and - the inner and outer modes are both a word or - smaller. So leave the SUBREG then. */ - && ! (GET_CODE (SUBREG_REG (x)) == REG - && GET_MODE_SIZE (GET_MODE (x)) <= UNITS_PER_WORD - && GET_MODE_SIZE (GET_MODE (new)) <= UNITS_PER_WORD) -#endif - ) - { - int offset = SUBREG_WORD (x) * UNITS_PER_WORD; - enum machine_mode mode = GET_MODE (x); - -#if BYTES_BIG_ENDIAN - offset += (MIN (UNITS_PER_WORD, - GET_MODE_SIZE (GET_MODE (new))) - - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))); -#endif - - PUT_MODE (new, mode); - XEXP (new, 0) = plus_constant (XEXP (new, 0), offset); - return new; - } - else - return gen_rtx (SUBREG, GET_MODE (x), new, SUBREG_WORD (x)); - } - - return x; - - case CLOBBER: - /* If clobbering a register that is the replacement register for an - elimination we still think can be performed, note that it cannot - be performed. Otherwise, we need not be concerned about it. */ - for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) - if (ep->to_rtx == XEXP (x, 0)) - ep->can_eliminate = 0; - - new = eliminate_regs (XEXP (x, 0), mem_mode, NULL_RTX); - if (new != XEXP (x, 0)) - return gen_rtx (code, GET_MODE (x), new); - return x; - - case ASM_OPERANDS: - { - rtx *temp_vec; - /* Properly handle sharing input and constraint vectors. */ - if (ASM_OPERANDS_INPUT_VEC (x) != old_asm_operands_vec) - { - /* When we come to a new vector not seen before, - scan all its elements; keep the old vector if none - of them changes; otherwise, make a copy. */ - old_asm_operands_vec = ASM_OPERANDS_INPUT_VEC (x); - temp_vec = (rtx *) alloca (XVECLEN (x, 3) * sizeof (rtx)); - for (i = 0; i < ASM_OPERANDS_INPUT_LENGTH (x); i++) - temp_vec[i] = eliminate_regs (ASM_OPERANDS_INPUT (x, i), - mem_mode, NULL_RTX); - - for (i = 0; i < ASM_OPERANDS_INPUT_LENGTH (x); i++) - if (temp_vec[i] != ASM_OPERANDS_INPUT (x, i)) - break; - - if (i == ASM_OPERANDS_INPUT_LENGTH (x)) - new_asm_operands_vec = old_asm_operands_vec; - else - new_asm_operands_vec - = gen_rtvec_v (ASM_OPERANDS_INPUT_LENGTH (x), temp_vec); - } - - /* If we had to copy the vector, copy the entire ASM_OPERANDS. */ - if (new_asm_operands_vec == old_asm_operands_vec) - return x; - - new = gen_rtx (ASM_OPERANDS, VOIDmode, ASM_OPERANDS_TEMPLATE (x), - ASM_OPERANDS_OUTPUT_CONSTRAINT (x), - ASM_OPERANDS_OUTPUT_IDX (x), new_asm_operands_vec, - ASM_OPERANDS_INPUT_CONSTRAINT_VEC (x), - ASM_OPERANDS_SOURCE_FILE (x), - ASM_OPERANDS_SOURCE_LINE (x)); - new->volatil = x->volatil; - return new; - } - - case SET: - /* Check for setting a register that we know about. */ - if (GET_CODE (SET_DEST (x)) == REG) - { - /* See if this is setting the replacement register for an - elimination. - - If DEST is the frame pointer, we do nothing because we assume that - all assignments to the frame pointer are for non-local gotos and - are being done at a time when they are valid and do not disturb - anything else. Some machines want to eliminate a fake argument - pointer with either the frame or stack pointer. Assignments to - the frame pointer must not prevent this elimination. */ - - for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; - ep++) - if (ep->to_rtx == SET_DEST (x) - && SET_DEST (x) != frame_pointer_rtx) - { - /* If it is being incremented, adjust the offset. Otherwise, - this elimination can't be done. */ - rtx src = SET_SRC (x); - - if (GET_CODE (src) == PLUS - && XEXP (src, 0) == SET_DEST (x) - && GET_CODE (XEXP (src, 1)) == CONST_INT) - ep->offset -= INTVAL (XEXP (src, 1)); - else - ep->can_eliminate = 0; - } - - /* Now check to see we are assigning to a register that can be - eliminated. If so, it must be as part of a PARALLEL, since we - will not have been called if this is a single SET. So indicate - that we can no longer eliminate this reg. */ - for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; - ep++) - if (ep->from_rtx == SET_DEST (x) && ep->can_eliminate) - ep->can_eliminate = 0; - } - - /* Now avoid the loop below in this common case. */ - { - rtx new0 = eliminate_regs (SET_DEST (x), 0, NULL_RTX); - rtx new1 = eliminate_regs (SET_SRC (x), 0, NULL_RTX); - - /* If SET_DEST changed from a REG to a MEM and INSN is non-zero, - write a CLOBBER insn. */ - if (GET_CODE (SET_DEST (x)) == REG && GET_CODE (new0) == MEM - && insn != 0) - emit_insn_after (gen_rtx (CLOBBER, VOIDmode, SET_DEST (x)), insn); - - if (new0 != SET_DEST (x) || new1 != SET_SRC (x)) - return gen_rtx (SET, VOIDmode, new0, new1); - } - - return x; - - case MEM: - /* Our only special processing is to pass the mode of the MEM to our - recursive call and copy the flags. While we are here, handle this - case more efficiently. */ - new = eliminate_regs (XEXP (x, 0), GET_MODE (x), NULL_RTX); - if (new != XEXP (x, 0)) - { - new = gen_rtx (MEM, GET_MODE (x), new); - new->volatil = x->volatil; - new->unchanging = x->unchanging; - new->in_struct = x->in_struct; - return new; - } - else - return x; - } - - /* Process each of our operands recursively. If any have changed, make a - copy of the rtx. */ - fmt = GET_RTX_FORMAT (code); - for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++) - { - if (*fmt == 'e') - { - new = eliminate_regs (XEXP (x, i), mem_mode, NULL_RTX); - if (new != XEXP (x, i) && ! copied) - { - rtx new_x = rtx_alloc (code); - bcopy (x, new_x, (sizeof (*new_x) - sizeof (new_x->fld) - + (sizeof (new_x->fld[0]) - * GET_RTX_LENGTH (code)))); - x = new_x; - copied = 1; - } - XEXP (x, i) = new; - } - else if (*fmt == 'E') - { - int copied_vec = 0; - for (j = 0; j < XVECLEN (x, i); j++) - { - new = eliminate_regs (XVECEXP (x, i, j), mem_mode, insn); - if (new != XVECEXP (x, i, j) && ! copied_vec) - { - rtvec new_v = gen_rtvec_v (XVECLEN (x, i), - &XVECEXP (x, i, 0)); - if (! copied) - { - rtx new_x = rtx_alloc (code); - bcopy (x, new_x, (sizeof (*new_x) - sizeof (new_x->fld) - + (sizeof (new_x->fld[0]) - * GET_RTX_LENGTH (code)))); - x = new_x; - copied = 1; - } - XVEC (x, i) = new_v; - copied_vec = 1; - } - XVECEXP (x, i, j) = new; - } - } - } - - return x; -} - -/* Scan INSN and eliminate all eliminable registers in it. - - If REPLACE is nonzero, do the replacement destructively. Also - delete the insn as dead it if it is setting an eliminable register. - - If REPLACE is zero, do all our allocations in reload_obstack. - - If no eliminations were done and this insn doesn't require any elimination - processing (these are not identical conditions: it might be updating sp, - but not referencing fp; this needs to be seen during reload_as_needed so - that the offset between fp and sp can be taken into consideration), zero - is returned. Otherwise, 1 is returned. */ - -static int -eliminate_regs_in_insn (insn, replace) - rtx insn; - int replace; -{ - rtx old_body = PATTERN (insn); - rtx new_body; - int val = 0; - struct elim_table *ep; - - if (! replace) - push_obstacks (&reload_obstack, &reload_obstack); - - if (GET_CODE (old_body) == SET && GET_CODE (SET_DEST (old_body)) == REG - && REGNO (SET_DEST (old_body)) < FIRST_PSEUDO_REGISTER) - { - /* Check for setting an eliminable register. */ - for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) - if (ep->from_rtx == SET_DEST (old_body) && ep->can_eliminate) - { - /* In this case this insn isn't serving a useful purpose. We - will delete it in reload_as_needed once we know that this - elimination is, in fact, being done. - - If REPLACE isn't set, we can't delete this insn, but neededn't - process it since it won't be used unless something changes. */ - if (replace) - delete_dead_insn (insn); - val = 1; - goto done; - } - - /* Check for (set (reg) (plus (reg from) (offset))) where the offset - in the insn is the negative of the offset in FROM. Substitute - (set (reg) (reg to)) for the insn and change its code. - - We have to do this here, rather than in eliminate_regs, do that we can - change the insn code. */ - - if (GET_CODE (SET_SRC (old_body)) == PLUS - && GET_CODE (XEXP (SET_SRC (old_body), 0)) == REG - && GET_CODE (XEXP (SET_SRC (old_body), 1)) == CONST_INT) - for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; - ep++) - if (ep->from_rtx == XEXP (SET_SRC (old_body), 0) - && ep->can_eliminate - && ep->offset == - INTVAL (XEXP (SET_SRC (old_body), 1))) - { - PATTERN (insn) = gen_rtx (SET, VOIDmode, - SET_DEST (old_body), ep->to_rtx); - INSN_CODE (insn) = -1; - val = 1; - goto done; - } - } - - old_asm_operands_vec = 0; - - /* Replace the body of this insn with a substituted form. If we changed - something, return non-zero. If this is the final call for this - insn (REPLACE is non-zero), do the elimination in REG_NOTES as well. - - If we are replacing a body that was a (set X (plus Y Z)), try to - re-recognize the insn. We do this in case we had a simple addition - but now can do this as a load-address. This saves an insn in this - common case. */ - - new_body = eliminate_regs (old_body, 0, replace ? insn : NULL_RTX); - if (new_body != old_body) - { - /* If we aren't replacing things permanently and we changed something, - make another copy to ensure that all the RTL is new. Otherwise - things can go wrong if find_reload swaps commutative operands - and one is inside RTL that has been copied while the other is not. */ - - /* Don't copy an asm_operands because (1) there's no need and (2) - copy_rtx can't do it properly when there are multiple outputs. */ - if (! replace && asm_noperands (old_body) < 0) - new_body = copy_rtx (new_body); - - /* If we had a move insn but now we don't, rerecognize it. */ - if ((GET_CODE (old_body) == SET && GET_CODE (SET_SRC (old_body)) == REG - && (GET_CODE (new_body) != SET - || GET_CODE (SET_SRC (new_body)) != REG)) - /* If this was an add insn before, rerecognize. */ - || - (GET_CODE (old_body) == SET - && GET_CODE (SET_SRC (old_body)) == PLUS)) - { - if (! validate_change (insn, &PATTERN (insn), new_body, 0)) - /* If recognition fails, store the new body anyway. - It's normal to have recognition failures here - due to bizarre memory addresses; reloading will fix them. */ - PATTERN (insn) = new_body; - } - else - PATTERN (insn) = new_body; - - if (replace && REG_NOTES (insn)) - REG_NOTES (insn) = eliminate_regs (REG_NOTES (insn), 0, NULL_RTX); - val = 1; - } - - /* Loop through all elimination pairs. See if any have changed and - recalculate the number not at initial offset. - - Compute the maximum offset (minimum offset if the stack does not - grow downward) for each elimination pair. - - We also detect a cases where register elimination cannot be done, - namely, if a register would be both changed and referenced outside a MEM - in the resulting insn since such an insn is often undefined and, even if - not, we cannot know what meaning will be given to it. Note that it is - valid to have a register used in an address in an insn that changes it - (presumably with a pre- or post-increment or decrement). - - If anything changes, return nonzero. */ - - num_not_at_initial_offset = 0; - for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) - { - if (ep->previous_offset != ep->offset && ep->ref_outside_mem) - ep->can_eliminate = 0; - - ep->ref_outside_mem = 0; - - if (ep->previous_offset != ep->offset) - val = 1; - - ep->previous_offset = ep->offset; - if (ep->can_eliminate && ep->offset != ep->initial_offset) - num_not_at_initial_offset++; - -#ifdef STACK_GROWS_DOWNWARD - ep->max_offset = MAX (ep->max_offset, ep->offset); -#else - ep->max_offset = MIN (ep->max_offset, ep->offset); -#endif - } - - done: - if (! replace) - pop_obstacks (); - - return val; -} - -/* Given X, a SET or CLOBBER of DEST, if DEST is the target of a register - replacement we currently believe is valid, mark it as not eliminable if X - modifies DEST in any way other than by adding a constant integer to it. - - If DEST is the frame pointer, we do nothing because we assume that - all assignments to the frame pointer are nonlocal gotos and are being done - at a time when they are valid and do not disturb anything else. - Some machines want to eliminate a fake argument pointer with either the - frame or stack pointer. Assignments to the frame pointer must not prevent - this elimination. - - Called via note_stores from reload before starting its passes to scan - the insns of the function. */ - -static void -mark_not_eliminable (dest, x) - rtx dest; - rtx x; -{ - register int i; - - /* A SUBREG of a hard register here is just changing its mode. We should - not see a SUBREG of an eliminable hard register, but check just in - case. */ - if (GET_CODE (dest) == SUBREG) - dest = SUBREG_REG (dest); - - if (dest == frame_pointer_rtx) - return; - - for (i = 0; i < NUM_ELIMINABLE_REGS; i++) - if (reg_eliminate[i].can_eliminate && dest == reg_eliminate[i].to_rtx - && (GET_CODE (x) != SET - || GET_CODE (SET_SRC (x)) != PLUS - || XEXP (SET_SRC (x), 0) != dest - || GET_CODE (XEXP (SET_SRC (x), 1)) != CONST_INT)) - { - reg_eliminate[i].can_eliminate_previous - = reg_eliminate[i].can_eliminate = 0; - num_eliminable--; - } -} - -/* Kick all pseudos out of hard register REGNO. - If GLOBAL is nonzero, try to find someplace else to put them. - If DUMPFILE is nonzero, log actions taken on that file. - - If CANT_ELIMINATE is nonzero, it means that we are doing this spill - because we found we can't eliminate some register. In the case, no pseudos - are allowed to be in the register, even if they are only in a block that - doesn't require spill registers, unlike the case when we are spilling this - hard reg to produce another spill register. - - Return nonzero if any pseudos needed to be kicked out. */ - -static int -spill_hard_reg (regno, global, dumpfile, cant_eliminate) - register int regno; - int global; - FILE *dumpfile; - int cant_eliminate; -{ - int something_changed = 0; - register int i; - - SET_HARD_REG_BIT (forbidden_regs, regno); - - /* Spill every pseudo reg that was allocated to this reg - or to something that overlaps this reg. */ - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (reg_renumber[i] >= 0 - && reg_renumber[i] <= regno - && (reg_renumber[i] - + HARD_REGNO_NREGS (reg_renumber[i], - PSEUDO_REGNO_MODE (i)) - > regno)) - { - enum reg_class class = REGNO_REG_CLASS (regno); - - /* If this register belongs solely to a basic block which needed no - spilling of any class that this register is contained in, - leave it be, unless we are spilling this register because - it was a hard register that can't be eliminated. */ - - if (! cant_eliminate - && basic_block_needs[0] - && reg_basic_block[i] >= 0 - && basic_block_needs[(int) class][reg_basic_block[i]] == 0) - { - enum reg_class *p; - - for (p = reg_class_superclasses[(int) class]; - *p != LIM_REG_CLASSES; p++) - if (basic_block_needs[(int) *p][reg_basic_block[i]] > 0) - break; - - if (*p == LIM_REG_CLASSES) - continue; - } - - /* Mark it as no longer having a hard register home. */ - reg_renumber[i] = -1; - /* We will need to scan everything again. */ - something_changed = 1; - if (global) - retry_global_alloc (i, forbidden_regs); - - alter_reg (i, regno); - if (dumpfile) - { - if (reg_renumber[i] == -1) - fprintf (dumpfile, " Register %d now on stack.\n\n", i); - else - fprintf (dumpfile, " Register %d now in %d.\n\n", - i, reg_renumber[i]); - } - } - - return something_changed; -} - -/* Find all paradoxical subregs within X and update reg_max_ref_width. */ - -static void -scan_paradoxical_subregs (x) - register rtx x; -{ - register int i; - register char *fmt; - register enum rtx_code code = GET_CODE (x); - - switch (code) - { - case CONST_INT: - case CONST: - case SYMBOL_REF: - case LABEL_REF: - case CONST_DOUBLE: - case CC0: - case PC: - case REG: - case USE: - case CLOBBER: - return; - - case SUBREG: - if (GET_CODE (SUBREG_REG (x)) == REG - && GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) - reg_max_ref_width[REGNO (SUBREG_REG (x))] - = GET_MODE_SIZE (GET_MODE (x)); - return; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - scan_paradoxical_subregs (XEXP (x, i)); - else if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >=0; j--) - scan_paradoxical_subregs (XVECEXP (x, i, j)); - } - } -} - -static int -hard_reg_use_compare (p1, p2) - struct hard_reg_n_uses *p1, *p2; -{ - int tem = p1->uses - p2->uses; - if (tem != 0) return tem; - /* If regs are equally good, sort by regno, - so that the results of qsort leave nothing to chance. */ - return p1->regno - p2->regno; -} - -/* Choose the order to consider regs for use as reload registers - based on how much trouble would be caused by spilling one. - Store them in order of decreasing preference in potential_reload_regs. */ - -static void -order_regs_for_reload () -{ - register int i; - register int o = 0; - int large = 0; - - struct hard_reg_n_uses hard_reg_n_uses[FIRST_PSEUDO_REGISTER]; - - CLEAR_HARD_REG_SET (bad_spill_regs); - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - potential_reload_regs[i] = -1; - - /* Count number of uses of each hard reg by pseudo regs allocated to it - and then order them by decreasing use. */ - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - hard_reg_n_uses[i].uses = 0; - hard_reg_n_uses[i].regno = i; - } - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - { - int regno = reg_renumber[i]; - if (regno >= 0) - { - int lim = regno + HARD_REGNO_NREGS (regno, PSEUDO_REGNO_MODE (i)); - while (regno < lim) - hard_reg_n_uses[regno++].uses += reg_n_refs[i]; - } - large += reg_n_refs[i]; - } - - /* Now fixed registers (which cannot safely be used for reloading) - get a very high use count so they will be considered least desirable. - Registers used explicitly in the rtl code are almost as bad. */ - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - if (fixed_regs[i]) - { - hard_reg_n_uses[i].uses += 2 * large + 2; - SET_HARD_REG_BIT (bad_spill_regs, i); - } - else if (regs_explicitly_used[i]) - { - hard_reg_n_uses[i].uses += large + 1; -#ifndef SMALL_REGISTER_CLASSES - /* ??? We are doing this here because of the potential that - bad code may be generated if a register explicitly used in - an insn was used as a spill register for that insn. But - not using these are spill registers may lose on some machine. - We'll have to see how this works out. */ - SET_HARD_REG_BIT (bad_spill_regs, i); -#endif - } - } - hard_reg_n_uses[FRAME_POINTER_REGNUM].uses += 2 * large + 2; - SET_HARD_REG_BIT (bad_spill_regs, FRAME_POINTER_REGNUM); - -#ifdef ELIMINABLE_REGS - /* If registers other than the frame pointer are eliminable, mark them as - poor choices. */ - for (i = 0; i < NUM_ELIMINABLE_REGS; i++) - { - hard_reg_n_uses[reg_eliminate[i].from].uses += 2 * large + 2; - SET_HARD_REG_BIT (bad_spill_regs, reg_eliminate[i].from); - } -#endif - - /* Prefer registers not so far used, for use in temporary loading. - Among them, if REG_ALLOC_ORDER is defined, use that order. - Otherwise, prefer registers not preserved by calls. */ - -#ifdef REG_ALLOC_ORDER - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - int regno = reg_alloc_order[i]; - - if (hard_reg_n_uses[regno].uses == 0) - potential_reload_regs[o++] = regno; - } -#else - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - if (hard_reg_n_uses[i].uses == 0 && call_used_regs[i]) - potential_reload_regs[o++] = i; - } - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - if (hard_reg_n_uses[i].uses == 0 && ! call_used_regs[i]) - potential_reload_regs[o++] = i; - } -#endif - - qsort (hard_reg_n_uses, FIRST_PSEUDO_REGISTER, - sizeof hard_reg_n_uses[0], hard_reg_use_compare); - - /* Now add the regs that are already used, - preferring those used less often. The fixed and otherwise forbidden - registers will be at the end of this list. */ - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (hard_reg_n_uses[i].uses != 0) - potential_reload_regs[o++] = hard_reg_n_uses[i].regno; -} - -/* Reload pseudo-registers into hard regs around each insn as needed. - Additional register load insns are output before the insn that needs it - and perhaps store insns after insns that modify the reloaded pseudo reg. - - reg_last_reload_reg and reg_reloaded_contents keep track of - which registers are already available in reload registers. - We update these for the reloads that we perform, - as the insns are scanned. */ - -static void -reload_as_needed (first, live_known) - rtx first; - int live_known; -{ - register rtx insn; - register int i; - int this_block = 0; - rtx x; - rtx after_call = 0; - - bzero (spill_reg_rtx, sizeof spill_reg_rtx); - reg_last_reload_reg = (rtx *) alloca (max_regno * sizeof (rtx)); - bzero (reg_last_reload_reg, max_regno * sizeof (rtx)); - reg_has_output_reload = (char *) alloca (max_regno); - for (i = 0; i < n_spills; i++) - { - reg_reloaded_contents[i] = -1; - reg_reloaded_insn[i] = 0; - } - - /* Reset all offsets on eliminable registers to their initial values. */ -#ifdef ELIMINABLE_REGS - for (i = 0; i < NUM_ELIMINABLE_REGS; i++) - { - INITIAL_ELIMINATION_OFFSET (reg_eliminate[i].from, reg_eliminate[i].to, - reg_eliminate[i].initial_offset); - reg_eliminate[i].previous_offset - = reg_eliminate[i].offset = reg_eliminate[i].initial_offset; - } -#else - INITIAL_FRAME_POINTER_OFFSET (reg_eliminate[0].initial_offset); - reg_eliminate[0].previous_offset - = reg_eliminate[0].offset = reg_eliminate[0].initial_offset; -#endif - - num_not_at_initial_offset = 0; - - for (insn = first; insn;) - { - register rtx next = NEXT_INSN (insn); - - /* Notice when we move to a new basic block. */ - if (live_known && this_block + 1 < n_basic_blocks - && insn == basic_block_head[this_block+1]) - ++this_block; - - /* If we pass a label, copy the offsets from the label information - into the current offsets of each elimination. */ - if (GET_CODE (insn) == CODE_LABEL) - { - num_not_at_initial_offset = 0; - for (i = 0; i < NUM_ELIMINABLE_REGS; i++) - { - reg_eliminate[i].offset = reg_eliminate[i].previous_offset - = offsets_at[CODE_LABEL_NUMBER (insn)][i]; - if (reg_eliminate[i].can_eliminate - && (reg_eliminate[i].offset - != reg_eliminate[i].initial_offset)) - num_not_at_initial_offset++; - } - } - - else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') - { - rtx avoid_return_reg = 0; - -#ifdef SMALL_REGISTER_CLASSES - /* Set avoid_return_reg if this is an insn - that might use the value of a function call. */ - if (GET_CODE (insn) == CALL_INSN) - { - if (GET_CODE (PATTERN (insn)) == SET) - after_call = SET_DEST (PATTERN (insn)); - else if (GET_CODE (PATTERN (insn)) == PARALLEL - && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) - after_call = SET_DEST (XVECEXP (PATTERN (insn), 0, 0)); - else - after_call = 0; - } - else if (after_call != 0 - && !(GET_CODE (PATTERN (insn)) == SET - && SET_DEST (PATTERN (insn)) == stack_pointer_rtx)) - { - if (reg_mentioned_p (after_call, PATTERN (insn))) - avoid_return_reg = after_call; - after_call = 0; - } -#endif /* SMALL_REGISTER_CLASSES */ - - /* If this is a USE and CLOBBER of a MEM, ensure that any - references to eliminable registers have been removed. */ - - if ((GET_CODE (PATTERN (insn)) == USE - || GET_CODE (PATTERN (insn)) == CLOBBER) - && GET_CODE (XEXP (PATTERN (insn), 0)) == MEM) - XEXP (XEXP (PATTERN (insn), 0), 0) - = eliminate_regs (XEXP (XEXP (PATTERN (insn), 0), 0), - GET_MODE (XEXP (PATTERN (insn), 0)), NULL_RTX); - - /* If we need to do register elimination processing, do so. - This might delete the insn, in which case we are done. */ - if (num_eliminable && GET_MODE (insn) == QImode) - { - eliminate_regs_in_insn (insn, 1); - if (GET_CODE (insn) == NOTE) - { - insn = next; - continue; - } - } - - if (GET_MODE (insn) == VOIDmode) - n_reloads = 0; - /* First find the pseudo regs that must be reloaded for this insn. - This info is returned in the tables reload_... (see reload.h). - Also modify the body of INSN by substituting RELOAD - rtx's for those pseudo regs. */ - else - { - bzero (reg_has_output_reload, max_regno); - CLEAR_HARD_REG_SET (reg_is_output_reload); - - find_reloads (insn, 1, spill_indirect_levels, live_known, - spill_reg_order); - } - - if (n_reloads > 0) - { - rtx prev = PREV_INSN (insn), next = NEXT_INSN (insn); - rtx p; - int class; - - /* If this block has not had spilling done for a - particular clas and we have any non-optionals that need a - spill reg in that class, abort. */ - - for (class = 0; class < N_REG_CLASSES; class++) - if (basic_block_needs[class] != 0 - && basic_block_needs[class][this_block] == 0) - for (i = 0; i < n_reloads; i++) - if (class == (int) reload_reg_class[i] - && reload_reg_rtx[i] == 0 - && ! reload_optional[i] - && (reload_in[i] != 0 || reload_out[i] != 0 - || reload_secondary_p[i] != 0)) - abort (); - - /* Now compute which reload regs to reload them into. Perhaps - reusing reload regs from previous insns, or else output - load insns to reload them. Maybe output store insns too. - Record the choices of reload reg in reload_reg_rtx. */ - choose_reload_regs (insn, avoid_return_reg); - -#ifdef SMALL_REGISTER_CLASSES - /* Merge any reloads that we didn't combine for fear of - increasing the number of spill registers needed but now - discover can be safely merged. */ - merge_assigned_reloads (insn); -#endif - - /* Generate the insns to reload operands into or out of - their reload regs. */ - emit_reload_insns (insn); - - /* Substitute the chosen reload regs from reload_reg_rtx - into the insn's body (or perhaps into the bodies of other - load and store insn that we just made for reloading - and that we moved the structure into). */ - subst_reloads (); - - /* If this was an ASM, make sure that all the reload insns - we have generated are valid. If not, give an error - and delete them. */ - - if (asm_noperands (PATTERN (insn)) >= 0) - for (p = NEXT_INSN (prev); p != next; p = NEXT_INSN (p)) - if (p != insn && GET_RTX_CLASS (GET_CODE (p)) == 'i' - && (recog_memoized (p) < 0 - || (insn_extract (p), - ! constrain_operands (INSN_CODE (p), 1)))) - { - error_for_asm (insn, - "`asm' operand requires impossible reload"); - PUT_CODE (p, NOTE); - NOTE_SOURCE_FILE (p) = 0; - NOTE_LINE_NUMBER (p) = NOTE_INSN_DELETED; - } - } - /* Any previously reloaded spilled pseudo reg, stored in this insn, - is no longer validly lying around to save a future reload. - Note that this does not detect pseudos that were reloaded - for this insn in order to be stored in - (obeying register constraints). That is correct; such reload - registers ARE still valid. */ - note_stores (PATTERN (insn), forget_old_reloads_1); - - /* There may have been CLOBBER insns placed after INSN. So scan - between INSN and NEXT and use them to forget old reloads. */ - for (x = NEXT_INSN (insn); x != next; x = NEXT_INSN (x)) - if (GET_CODE (x) == INSN && GET_CODE (PATTERN (x)) == CLOBBER) - note_stores (PATTERN (x), forget_old_reloads_1); - -#ifdef AUTO_INC_DEC - /* Likewise for regs altered by auto-increment in this insn. - But note that the reg-notes are not changed by reloading: - they still contain the pseudo-regs, not the spill regs. */ - for (x = REG_NOTES (insn); x; x = XEXP (x, 1)) - if (REG_NOTE_KIND (x) == REG_INC) - { - /* See if this pseudo reg was reloaded in this insn. - If so, its last-reload info is still valid - because it is based on this insn's reload. */ - for (i = 0; i < n_reloads; i++) - if (reload_out[i] == XEXP (x, 0)) - break; - - if (i == n_reloads) - forget_old_reloads_1 (XEXP (x, 0), NULL_RTX); - } -#endif - } - /* A reload reg's contents are unknown after a label. */ - if (GET_CODE (insn) == CODE_LABEL) - for (i = 0; i < n_spills; i++) - { - reg_reloaded_contents[i] = -1; - reg_reloaded_insn[i] = 0; - } - - /* Don't assume a reload reg is still good after a call insn - if it is a call-used reg. */ - else if (GET_CODE (insn) == CALL_INSN) - for (i = 0; i < n_spills; i++) - if (call_used_regs[spill_regs[i]]) - { - reg_reloaded_contents[i] = -1; - reg_reloaded_insn[i] = 0; - } - - /* In case registers overlap, allow certain insns to invalidate - particular hard registers. */ - -#ifdef INSN_CLOBBERS_REGNO_P - for (i = 0 ; i < n_spills ; i++) - if (INSN_CLOBBERS_REGNO_P (insn, spill_regs[i])) - { - reg_reloaded_contents[i] = -1; - reg_reloaded_insn[i] = 0; - } -#endif - - insn = next; - -#ifdef USE_C_ALLOCA - alloca (0); -#endif - } -} - -/* Discard all record of any value reloaded from X, - or reloaded in X from someplace else; - unless X is an output reload reg of the current insn. - - X may be a hard reg (the reload reg) - or it may be a pseudo reg that was reloaded from. */ - -static void -forget_old_reloads_1 (x, ignored) - rtx x; - rtx ignored; -{ - register int regno; - int nr; - int offset = 0; - - /* note_stores does give us subregs of hard regs. */ - while (GET_CODE (x) == SUBREG) - { - offset += SUBREG_WORD (x); - x = SUBREG_REG (x); - } - - if (GET_CODE (x) != REG) - return; - - regno = REGNO (x) + offset; - - if (regno >= FIRST_PSEUDO_REGISTER) - nr = 1; - else - { - int i; - nr = HARD_REGNO_NREGS (regno, GET_MODE (x)); - /* Storing into a spilled-reg invalidates its contents. - This can happen if a block-local pseudo is allocated to that reg - and it wasn't spilled because this block's total need is 0. - Then some insn might have an optional reload and use this reg. */ - for (i = 0; i < nr; i++) - if (spill_reg_order[regno + i] >= 0 - /* But don't do this if the reg actually serves as an output - reload reg in the current instruction. */ - && (n_reloads == 0 - || ! TEST_HARD_REG_BIT (reg_is_output_reload, regno + i))) - { - reg_reloaded_contents[spill_reg_order[regno + i]] = -1; - reg_reloaded_insn[spill_reg_order[regno + i]] = 0; - } - } - - /* Since value of X has changed, - forget any value previously copied from it. */ - - while (nr-- > 0) - /* But don't forget a copy if this is the output reload - that establishes the copy's validity. */ - if (n_reloads == 0 || reg_has_output_reload[regno + nr] == 0) - reg_last_reload_reg[regno + nr] = 0; -} - -/* For each reload, the mode of the reload register. */ -static enum machine_mode reload_mode[MAX_RELOADS]; - -/* For each reload, the largest number of registers it will require. */ -static int reload_nregs[MAX_RELOADS]; - -/* Comparison function for qsort to decide which of two reloads - should be handled first. *P1 and *P2 are the reload numbers. */ - -static int -reload_reg_class_lower (p1, p2) - short *p1, *p2; -{ - register int r1 = *p1, r2 = *p2; - register int t; - - /* Consider required reloads before optional ones. */ - t = reload_optional[r1] - reload_optional[r2]; - if (t != 0) - return t; - - /* Count all solitary classes before non-solitary ones. */ - t = ((reg_class_size[(int) reload_reg_class[r2]] == 1) - - (reg_class_size[(int) reload_reg_class[r1]] == 1)); - if (t != 0) - return t; - - /* Aside from solitaires, consider all multi-reg groups first. */ - t = reload_nregs[r2] - reload_nregs[r1]; - if (t != 0) - return t; - - /* Consider reloads in order of increasing reg-class number. */ - t = (int) reload_reg_class[r1] - (int) reload_reg_class[r2]; - if (t != 0) - return t; - - /* If reloads are equally urgent, sort by reload number, - so that the results of qsort leave nothing to chance. */ - return r1 - r2; -} - -/* The following HARD_REG_SETs indicate when each hard register is - used for a reload of various parts of the current insn. */ - -/* If reg is in use as a reload reg for a RELOAD_OTHER reload. */ -static HARD_REG_SET reload_reg_used; -/* If reg is in use for a RELOAD_FOR_INPUT_ADDRESS reload for operand I. */ -static HARD_REG_SET reload_reg_used_in_input_addr[MAX_RECOG_OPERANDS]; -/* If reg is in use for a RELOAD_FOR_OUTPUT_ADDRESS reload for operand I. */ -static HARD_REG_SET reload_reg_used_in_output_addr[MAX_RECOG_OPERANDS]; -/* If reg is in use for a RELOAD_FOR_INPUT reload for operand I. */ -static HARD_REG_SET reload_reg_used_in_input[MAX_RECOG_OPERANDS]; -/* If reg is in use for a RELOAD_FOR_OUTPUT reload for operand I. */ -static HARD_REG_SET reload_reg_used_in_output[MAX_RECOG_OPERANDS]; -/* If reg is in use for a RELOAD_FOR_OPERAND_ADDRESS reload. */ -static HARD_REG_SET reload_reg_used_in_op_addr; -/* If reg is in use for a RELOAD_FOR_INSN reload. */ -static HARD_REG_SET reload_reg_used_in_insn; -/* If reg is in use for a RELOAD_FOR_OTHER_ADDRESS reload. */ -static HARD_REG_SET reload_reg_used_in_other_addr; - -/* If reg is in use as a reload reg for any sort of reload. */ -static HARD_REG_SET reload_reg_used_at_all; - -/* If reg is use as an inherited reload. We just mark the first register - in the group. */ -static HARD_REG_SET reload_reg_used_for_inherit; - -/* Mark reg REGNO as in use for a reload of the sort spec'd by OPNUM and - TYPE. MODE is used to indicate how many consecutive regs are - actually used. */ - -static void -mark_reload_reg_in_use (regno, opnum, type, mode) - int regno; - int opnum; - enum reload_type type; - enum machine_mode mode; -{ - int nregs = HARD_REGNO_NREGS (regno, mode); - int i; - - for (i = regno; i < nregs + regno; i++) - { - switch (type) - { - case RELOAD_OTHER: - SET_HARD_REG_BIT (reload_reg_used, i); - break; - - case RELOAD_FOR_INPUT_ADDRESS: - SET_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], i); - break; - - case RELOAD_FOR_OUTPUT_ADDRESS: - SET_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], i); - break; - - case RELOAD_FOR_OPERAND_ADDRESS: - SET_HARD_REG_BIT (reload_reg_used_in_op_addr, i); - break; - - case RELOAD_FOR_OTHER_ADDRESS: - SET_HARD_REG_BIT (reload_reg_used_in_other_addr, i); - break; - - case RELOAD_FOR_INPUT: - SET_HARD_REG_BIT (reload_reg_used_in_input[opnum], i); - break; - - case RELOAD_FOR_OUTPUT: - SET_HARD_REG_BIT (reload_reg_used_in_output[opnum], i); - break; - - case RELOAD_FOR_INSN: - SET_HARD_REG_BIT (reload_reg_used_in_insn, i); - break; - } - - SET_HARD_REG_BIT (reload_reg_used_at_all, i); - } -} - -/* Similarly, but show REGNO is no longer in use for a reload. */ - -static void -clear_reload_reg_in_use (regno, opnum, type, mode) - int regno; - int opnum; - enum reload_type type; - enum machine_mode mode; -{ - int nregs = HARD_REGNO_NREGS (regno, mode); - int i; - - for (i = regno; i < nregs + regno; i++) - { - switch (type) - { - case RELOAD_OTHER: - CLEAR_HARD_REG_BIT (reload_reg_used, i); - break; - - case RELOAD_FOR_INPUT_ADDRESS: - CLEAR_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], i); - break; - - case RELOAD_FOR_OUTPUT_ADDRESS: - CLEAR_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], i); - break; - - case RELOAD_FOR_OPERAND_ADDRESS: - CLEAR_HARD_REG_BIT (reload_reg_used_in_op_addr, i); - break; - - case RELOAD_FOR_OTHER_ADDRESS: - CLEAR_HARD_REG_BIT (reload_reg_used_in_other_addr, i); - break; - - case RELOAD_FOR_INPUT: - CLEAR_HARD_REG_BIT (reload_reg_used_in_input[opnum], i); - break; - - case RELOAD_FOR_OUTPUT: - CLEAR_HARD_REG_BIT (reload_reg_used_in_output[opnum], i); - break; - - case RELOAD_FOR_INSN: - CLEAR_HARD_REG_BIT (reload_reg_used_in_insn, i); - break; - } - } -} - -/* 1 if reg REGNO is free as a reload reg for a reload of the sort - specified by OPNUM and TYPE. */ - -static int -reload_reg_free_p (regno, opnum, type) - int regno; - int opnum; - enum reload_type type; -{ - int i; - - /* In use for a RELOAD_OTHER means it's not available for anything except - RELOAD_FOR_OTHER_ADDRESS. Recall that RELOAD_FOR_OTHER_ADDRESS is known - to be used only for inputs. */ - - if (type != RELOAD_FOR_OTHER_ADDRESS - && TEST_HARD_REG_BIT (reload_reg_used, regno)) - return 0; - - switch (type) - { - case RELOAD_OTHER: - /* In use for anything means not available for a RELOAD_OTHER. */ - return ! TEST_HARD_REG_BIT (reload_reg_used_at_all, regno); - - /* The other kinds of use can sometimes share a register. */ - case RELOAD_FOR_INPUT: - if (TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno) - || TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno)) - return 0; - - /* If it is used for some other input, can't use it. */ - for (i = 0; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) - return 0; - - /* If it is used in a later operand's address, can't use it. */ - for (i = opnum + 1; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)) - return 0; - - return 1; - - case RELOAD_FOR_INPUT_ADDRESS: - /* Can't use a register if it is used for an input address for this - operand or used as an input in an earlier one. */ - if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], regno)) - return 0; - - for (i = 0; i < opnum; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) - return 0; - - return 1; - - case RELOAD_FOR_OUTPUT_ADDRESS: - /* Can't use a register if it is used for an output address for this - operand or used as an output in this or a later operand. */ - if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], regno)) - return 0; - - for (i = opnum; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) - return 0; - - return 1; - - case RELOAD_FOR_OPERAND_ADDRESS: - for (i = 0; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) - return 0; - - return (! TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno) - && ! TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno)); - - case RELOAD_FOR_OUTPUT: - /* This cannot share a register with RELOAD_FOR_INSN reloads, other - outputs, or an operand address for this or an earlier output. */ - if (TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno)) - return 0; - - for (i = 0; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) - return 0; - - for (i = 0; i <= opnum; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)) - return 0; - - return 1; - - case RELOAD_FOR_INSN: - for (i = 0; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno) - || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) - return 0; - - return (! TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno) - && ! TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno)); - - case RELOAD_FOR_OTHER_ADDRESS: - return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno); - } - abort (); -} - -/* Return 1 if the value in reload reg REGNO, as used by a reload - needed for the part of the insn specified by OPNUM and TYPE, - is not in use for a reload in any prior part of the insn. - - We can assume that the reload reg was already tested for availability - at the time it is needed, and we should not check this again, - in case the reg has already been marked in use. */ - -static int -reload_reg_free_before_p (regno, opnum, type) - int regno; - int opnum; - enum reload_type type; -{ - int i; - - switch (type) - { - case RELOAD_FOR_OTHER_ADDRESS: - /* These always come first. */ - return 1; - - case RELOAD_OTHER: - return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno); - - /* If this use is for part of the insn, - check the reg is not in use for any prior part. It is tempting - to try to do this by falling through from objecs that occur - later in the insn to ones that occur earlier, but that will not - correctly take into account the fact that here we MUST ignore - things that would prevent the register from being allocated in - the first place, since we know that it was allocated. */ - - case RELOAD_FOR_OUTPUT_ADDRESS: - /* Earlier reloads are for earlier outputs or their addresses, - any RELOAD_FOR_INSN reloads, any inputs or their addresses, or any - RELOAD_FOR_OTHER_ADDRESS reloads (we know it can't conflict with - RELOAD_OTHER).. */ - for (i = 0; i < opnum; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) - || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) - return 0; - - if (TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno)) - return 0; - - for (i = 0; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) - || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) - return 0; - - return (! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno) - && ! TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno) - && ! TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno)); - - case RELOAD_FOR_OUTPUT: - /* This can't be used in the output address for this operand and - anything that can't be used for it, except that we've already - tested for RELOAD_FOR_INSN objects. */ - - if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], regno)) - return 0; - - for (i = 0; i < opnum; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) - || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) - return 0; - - for (i = 0; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) - || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno) - || TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno)) - return 0; - - return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno); - - case RELOAD_FOR_OPERAND_ADDRESS: - case RELOAD_FOR_INSN: - /* These can't conflict with inputs, or each other, so all we have to - test is input addresses and the addresses of OTHER items. */ - - for (i = 0; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)) - return 0; - - return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno); - - case RELOAD_FOR_INPUT: - /* The only things earlier are the address for this and - earlier inputs, other inputs (which we know we don't conflict - with), and addresses of RELOAD_OTHER objects. */ - - for (i = 0; i <= opnum; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)) - return 0; - - return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno); - - case RELOAD_FOR_INPUT_ADDRESS: - /* Similarly, all we have to check is for use in earlier inputs' - addresses. */ - for (i = 0; i < opnum; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)) - return 0; - - return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno); - } - abort (); -} - -/* Return 1 if the value in reload reg REGNO, as used by a reload - needed for the part of the insn specified by OPNUM and TYPE, - is still available in REGNO at the end of the insn. - - We can assume that the reload reg was already tested for availability - at the time it is needed, and we should not check this again, - in case the reg has already been marked in use. */ - -static int -reload_reg_reaches_end_p (regno, opnum, type) - int regno; - int opnum; - enum reload_type type; -{ - int i; - - switch (type) - { - case RELOAD_OTHER: - /* Since a RELOAD_OTHER reload claims the reg for the entire insn, - its value must reach the end. */ - return 1; - - /* If this use is for part of the insn, - its value reaches if no subsequent part uses the same register. - Just like the above function, don't try to do this with lots - of fallthroughs. */ - - case RELOAD_FOR_OTHER_ADDRESS: - /* Here we check for everything else, since these don't conflict - with anything else and everything comes later. */ - - for (i = 0; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) - || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno) - || TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) - || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) - return 0; - - return (! TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno) - && ! TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno) - && ! TEST_HARD_REG_BIT (reload_reg_used, regno)); - - case RELOAD_FOR_INPUT_ADDRESS: - /* Similar, except that we check only for this and subsequent inputs - and the address of only subsequent inputs and we do not need - to check for RELOAD_OTHER objects since they are known not to - conflict. */ - - for (i = opnum; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) - return 0; - - for (i = opnum + 1; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)) - return 0; - - for (i = 0; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) - || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) - return 0; - - return (! TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno) - && ! TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno)); - - case RELOAD_FOR_INPUT: - /* Similar to input address, except we start at the next operand for - both input and input address and we do not check for - RELOAD_FOR_OPERAND_ADDRESS and RELOAD_FOR_INSN since these - would conflict. */ - - for (i = opnum + 1; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) - || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) - return 0; - - /* ... fall through ... */ - - case RELOAD_FOR_OPERAND_ADDRESS: - /* Check outputs and their addresses. */ - - for (i = 0; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) - || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) - return 0; - - return 1; - - case RELOAD_FOR_INSN: - /* These conflict with other outputs with with RELOAD_OTHER. So - we need only check for output addresses. */ - - opnum = -1; - - /* ... fall through ... */ - - case RELOAD_FOR_OUTPUT: - case RELOAD_FOR_OUTPUT_ADDRESS: - /* We already know these can't conflict with a later output. So the - only thing to check are later output addresses. */ - for (i = opnum + 1; i < reload_n_operands; i++) - if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)) - return 0; - - return 1; - } - - abort (); -} - -/* Vector of reload-numbers showing the order in which the reloads should - be processed. */ -short reload_order[MAX_RELOADS]; - -/* Indexed by reload number, 1 if incoming value - inherited from previous insns. */ -char reload_inherited[MAX_RELOADS]; - -/* For an inherited reload, this is the insn the reload was inherited from, - if we know it. Otherwise, this is 0. */ -rtx reload_inheritance_insn[MAX_RELOADS]; - -/* If non-zero, this is a place to get the value of the reload, - rather than using reload_in. */ -rtx reload_override_in[MAX_RELOADS]; - -/* For each reload, the index in spill_regs of the spill register used, - or -1 if we did not need one of the spill registers for this reload. */ -int reload_spill_index[MAX_RELOADS]; - -/* Index of last register assigned as a spill register. We allocate in - a round-robin fashio. */ - -static int last_spill_reg = 0; - -/* Find a spill register to use as a reload register for reload R. - LAST_RELOAD is non-zero if this is the last reload for the insn being - processed. - - Set reload_reg_rtx[R] to the register allocated. - - If NOERROR is nonzero, we return 1 if successful, - or 0 if we couldn't find a spill reg and we didn't change anything. */ - -static int -allocate_reload_reg (r, insn, last_reload, noerror) - int r; - rtx insn; - int last_reload; - int noerror; -{ - int i; - int pass; - int count; - rtx new; - int regno; - - /* If we put this reload ahead, thinking it is a group, - then insist on finding a group. Otherwise we can grab a - reg that some other reload needs. - (That can happen when we have a 68000 DATA_OR_FP_REG - which is a group of data regs or one fp reg.) - We need not be so restrictive if there are no more reloads - for this insn. - - ??? Really it would be nicer to have smarter handling - for that kind of reg class, where a problem like this is normal. - Perhaps those classes should be avoided for reloading - by use of more alternatives. */ - - int force_group = reload_nregs[r] > 1 && ! last_reload; - - /* If we want a single register and haven't yet found one, - take any reg in the right class and not in use. - If we want a consecutive group, here is where we look for it. - - We use two passes so we can first look for reload regs to - reuse, which are already in use for other reloads in this insn, - and only then use additional registers. - I think that maximizing reuse is needed to make sure we don't - run out of reload regs. Suppose we have three reloads, and - reloads A and B can share regs. These need two regs. - Suppose A and B are given different regs. - That leaves none for C. */ - for (pass = 0; pass < 2; pass++) - { - /* I is the index in spill_regs. - We advance it round-robin between insns to use all spill regs - equally, so that inherited reloads have a chance - of leapfrogging each other. */ - - for (count = 0, i = last_spill_reg; count < n_spills; count++) - { - int class = (int) reload_reg_class[r]; - - i = (i + 1) % n_spills; - - if (reload_reg_free_p (spill_regs[i], reload_opnum[r], - reload_when_needed[r]) - && TEST_HARD_REG_BIT (reg_class_contents[class], spill_regs[i]) - && HARD_REGNO_MODE_OK (spill_regs[i], reload_mode[r]) - /* Look first for regs to share, then for unshared. But - don't share regs used for inherited reloads; they are - the ones we want to preserve. */ - && (pass - || (TEST_HARD_REG_BIT (reload_reg_used_at_all, - spill_regs[i]) - && ! TEST_HARD_REG_BIT (reload_reg_used_for_inherit, - spill_regs[i])))) - { - int nr = HARD_REGNO_NREGS (spill_regs[i], reload_mode[r]); - /* Avoid the problem where spilling a GENERAL_OR_FP_REG - (on 68000) got us two FP regs. If NR is 1, - we would reject both of them. */ - if (force_group) - nr = CLASS_MAX_NREGS (reload_reg_class[r], reload_mode[r]); - /* If we need only one reg, we have already won. */ - if (nr == 1) - { - /* But reject a single reg if we demand a group. */ - if (force_group) - continue; - break; - } - /* Otherwise check that as many consecutive regs as we need - are available here. - Also, don't use for a group registers that are - needed for nongroups. */ - if (! TEST_HARD_REG_BIT (counted_for_nongroups, spill_regs[i])) - while (nr > 1) - { - regno = spill_regs[i] + nr - 1; - if (!(TEST_HARD_REG_BIT (reg_class_contents[class], regno) - && spill_reg_order[regno] >= 0 - && reload_reg_free_p (regno, reload_opnum[r], - reload_when_needed[r]) - && ! TEST_HARD_REG_BIT (counted_for_nongroups, - regno))) - break; - nr--; - } - if (nr == 1) - break; - } - } - - /* If we found something on pass 1, omit pass 2. */ - if (count < n_spills) - break; - } - - /* We should have found a spill register by now. */ - if (count == n_spills) - { - if (noerror) - return 0; - goto failure; - } - - /* I is the index in SPILL_REG_RTX of the reload register we are to - allocate. Get an rtx for it and find its register number. */ - - new = spill_reg_rtx[i]; - - if (new == 0 || GET_MODE (new) != reload_mode[r]) - spill_reg_rtx[i] = new - = gen_rtx (REG, reload_mode[r], spill_regs[i]); - - regno = true_regnum (new); - - /* Detect when the reload reg can't hold the reload mode. - This used to be one `if', but Sequent compiler can't handle that. */ - if (HARD_REGNO_MODE_OK (regno, reload_mode[r])) - { - enum machine_mode test_mode = VOIDmode; - if (reload_in[r]) - test_mode = GET_MODE (reload_in[r]); - /* If reload_in[r] has VOIDmode, it means we will load it - in whatever mode the reload reg has: to wit, reload_mode[r]. - We have already tested that for validity. */ - /* Aside from that, we need to test that the expressions - to reload from or into have modes which are valid for this - reload register. Otherwise the reload insns would be invalid. */ - if (! (reload_in[r] != 0 && test_mode != VOIDmode - && ! HARD_REGNO_MODE_OK (regno, test_mode))) - if (! (reload_out[r] != 0 - && ! HARD_REGNO_MODE_OK (regno, GET_MODE (reload_out[r])))) - { - /* The reg is OK. */ - last_spill_reg = i; - - /* Mark as in use for this insn the reload regs we use - for this. */ - mark_reload_reg_in_use (spill_regs[i], reload_opnum[r], - reload_when_needed[r], reload_mode[r]); - - reload_reg_rtx[r] = new; - reload_spill_index[r] = i; - return 1; - } - } - - /* The reg is not OK. */ - if (noerror) - return 0; - - failure: - if (asm_noperands (PATTERN (insn)) < 0) - /* It's the compiler's fault. */ - abort (); - - /* It's the user's fault; the operand's mode and constraint - don't match. Disable this reload so we don't crash in final. */ - error_for_asm (insn, - "`asm' operand constraint incompatible with operand size"); - reload_in[r] = 0; - reload_out[r] = 0; - reload_reg_rtx[r] = 0; - reload_optional[r] = 1; - reload_secondary_p[r] = 1; - - return 1; -} - -/* Assign hard reg targets for the pseudo-registers we must reload - into hard regs for this insn. - Also output the instructions to copy them in and out of the hard regs. - - For machines with register classes, we are responsible for - finding a reload reg in the proper class. */ - -static void -choose_reload_regs (insn, avoid_return_reg) - rtx insn; - rtx avoid_return_reg; -{ - register int i, j; - int max_group_size = 1; - enum reg_class group_class = NO_REGS; - int inheritance; - - rtx save_reload_reg_rtx[MAX_RELOADS]; - char save_reload_inherited[MAX_RELOADS]; - rtx save_reload_inheritance_insn[MAX_RELOADS]; - rtx save_reload_override_in[MAX_RELOADS]; - int save_reload_spill_index[MAX_RELOADS]; - HARD_REG_SET save_reload_reg_used; - HARD_REG_SET save_reload_reg_used_in_input_addr[MAX_RECOG_OPERANDS]; - HARD_REG_SET save_reload_reg_used_in_output_addr[MAX_RECOG_OPERANDS]; - HARD_REG_SET save_reload_reg_used_in_input[MAX_RECOG_OPERANDS]; - HARD_REG_SET save_reload_reg_used_in_output[MAX_RECOG_OPERANDS]; - HARD_REG_SET save_reload_reg_used_in_op_addr; - HARD_REG_SET save_reload_reg_used_in_insn; - HARD_REG_SET save_reload_reg_used_in_other_addr; - HARD_REG_SET save_reload_reg_used_at_all; - - bzero (reload_inherited, MAX_RELOADS); - bzero (reload_inheritance_insn, MAX_RELOADS * sizeof (rtx)); - bzero (reload_override_in, MAX_RELOADS * sizeof (rtx)); - - CLEAR_HARD_REG_SET (reload_reg_used); - CLEAR_HARD_REG_SET (reload_reg_used_at_all); - CLEAR_HARD_REG_SET (reload_reg_used_in_op_addr); - CLEAR_HARD_REG_SET (reload_reg_used_in_insn); - CLEAR_HARD_REG_SET (reload_reg_used_in_other_addr); - - for (i = 0; i < reload_n_operands; i++) - { - CLEAR_HARD_REG_SET (reload_reg_used_in_output[i]); - CLEAR_HARD_REG_SET (reload_reg_used_in_input[i]); - CLEAR_HARD_REG_SET (reload_reg_used_in_input_addr[i]); - CLEAR_HARD_REG_SET (reload_reg_used_in_output_addr[i]); - } - -#ifdef SMALL_REGISTER_CLASSES - /* Don't bother with avoiding the return reg - if we have no mandatory reload that could use it. */ - if (avoid_return_reg) - { - int do_avoid = 0; - int regno = REGNO (avoid_return_reg); - int nregs - = HARD_REGNO_NREGS (regno, GET_MODE (avoid_return_reg)); - int r; - - for (r = regno; r < regno + nregs; r++) - if (spill_reg_order[r] >= 0) - for (j = 0; j < n_reloads; j++) - if (!reload_optional[j] && reload_reg_rtx[j] == 0 - && (reload_in[j] != 0 || reload_out[j] != 0 - || reload_secondary_p[j]) - && - TEST_HARD_REG_BIT (reg_class_contents[(int) reload_reg_class[j]], r)) - do_avoid = 1; - if (!do_avoid) - avoid_return_reg = 0; - } -#endif /* SMALL_REGISTER_CLASSES */ - -#if 0 /* Not needed, now that we can always retry without inheritance. */ - /* See if we have more mandatory reloads than spill regs. - If so, then we cannot risk optimizations that could prevent - reloads from sharing one spill register. - - Since we will try finding a better register than reload_reg_rtx - unless it is equal to reload_in or reload_out, count such reloads. */ - - { - int tem = 0; -#ifdef SMALL_REGISTER_CLASSES - int tem = (avoid_return_reg != 0); -#endif - for (j = 0; j < n_reloads; j++) - if (! reload_optional[j] - && (reload_in[j] != 0 || reload_out[j] != 0 || reload_secondary_p[j]) - && (reload_reg_rtx[j] == 0 - || (! rtx_equal_p (reload_reg_rtx[j], reload_in[j]) - && ! rtx_equal_p (reload_reg_rtx[j], reload_out[j])))) - tem++; - if (tem > n_spills) - must_reuse = 1; - } -#endif - -#ifdef SMALL_REGISTER_CLASSES - /* Don't use the subroutine call return reg for a reload - if we are supposed to avoid it. */ - if (avoid_return_reg) - { - int regno = REGNO (avoid_return_reg); - int nregs - = HARD_REGNO_NREGS (regno, GET_MODE (avoid_return_reg)); - int r; - - for (r = regno; r < regno + nregs; r++) - if (spill_reg_order[r] >= 0) - SET_HARD_REG_BIT (reload_reg_used, r); - } -#endif /* SMALL_REGISTER_CLASSES */ - - /* In order to be certain of getting the registers we need, - we must sort the reloads into order of increasing register class. - Then our grabbing of reload registers will parallel the process - that provided the reload registers. - - Also note whether any of the reloads wants a consecutive group of regs. - If so, record the maximum size of the group desired and what - register class contains all the groups needed by this insn. */ - - for (j = 0; j < n_reloads; j++) - { - reload_order[j] = j; - reload_spill_index[j] = -1; - - reload_mode[j] - = (reload_inmode[j] == VOIDmode - || (GET_MODE_SIZE (reload_outmode[j]) - > GET_MODE_SIZE (reload_inmode[j]))) - ? reload_outmode[j] : reload_inmode[j]; - - reload_nregs[j] = CLASS_MAX_NREGS (reload_reg_class[j], reload_mode[j]); - - if (reload_nregs[j] > 1) - { - max_group_size = MAX (reload_nregs[j], max_group_size); - group_class = reg_class_superunion[(int)reload_reg_class[j]][(int)group_class]; - } - - /* If we have already decided to use a certain register, - don't use it in another way. */ - if (reload_reg_rtx[j]) - mark_reload_reg_in_use (REGNO (reload_reg_rtx[j]), reload_opnum[j], - reload_when_needed[j], reload_mode[j]); - } - - if (n_reloads > 1) - qsort (reload_order, n_reloads, sizeof (short), reload_reg_class_lower); - - bcopy (reload_reg_rtx, save_reload_reg_rtx, sizeof reload_reg_rtx); - bcopy (reload_inherited, save_reload_inherited, sizeof reload_inherited); - bcopy (reload_inheritance_insn, save_reload_inheritance_insn, - sizeof reload_inheritance_insn); - bcopy (reload_override_in, save_reload_override_in, - sizeof reload_override_in); - bcopy (reload_spill_index, save_reload_spill_index, - sizeof reload_spill_index); - COPY_HARD_REG_SET (save_reload_reg_used, reload_reg_used); - COPY_HARD_REG_SET (save_reload_reg_used_at_all, reload_reg_used_at_all); - COPY_HARD_REG_SET (save_reload_reg_used_in_op_addr, - reload_reg_used_in_op_addr); - COPY_HARD_REG_SET (save_reload_reg_used_in_insn, - reload_reg_used_in_insn); - COPY_HARD_REG_SET (save_reload_reg_used_in_other_addr, - reload_reg_used_in_other_addr); - - for (i = 0; i < reload_n_operands; i++) - { - COPY_HARD_REG_SET (save_reload_reg_used_in_output[i], - reload_reg_used_in_output[i]); - COPY_HARD_REG_SET (save_reload_reg_used_in_input[i], - reload_reg_used_in_input[i]); - COPY_HARD_REG_SET (save_reload_reg_used_in_input_addr[i], - reload_reg_used_in_input_addr[i]); - COPY_HARD_REG_SET (save_reload_reg_used_in_output_addr[i], - reload_reg_used_in_output_addr[i]); - } - - /* If -O, try first with inheritance, then turning it off. - If not -O, don't do inheritance. - Using inheritance when not optimizing leads to paradoxes - with fp on the 68k: fp numbers (not NaNs) fail to be equal to themselves - because one side of the comparison might be inherited. */ - - for (inheritance = optimize > 0; inheritance >= 0; inheritance--) - { - /* Process the reloads in order of preference just found. - Beyond this point, subregs can be found in reload_reg_rtx. - - This used to look for an existing reloaded home for all - of the reloads, and only then perform any new reloads. - But that could lose if the reloads were done out of reg-class order - because a later reload with a looser constraint might have an old - home in a register needed by an earlier reload with a tighter constraint. - - To solve this, we make two passes over the reloads, in the order - described above. In the first pass we try to inherit a reload - from a previous insn. If there is a later reload that needs a - class that is a proper subset of the class being processed, we must - also allocate a spill register during the first pass. - - Then make a second pass over the reloads to allocate any reloads - that haven't been given registers yet. */ - - CLEAR_HARD_REG_SET (reload_reg_used_for_inherit); - - for (j = 0; j < n_reloads; j++) - { - register int r = reload_order[j]; - - /* Ignore reloads that got marked inoperative. */ - if (reload_out[r] == 0 && reload_in[r] == 0 && ! reload_secondary_p[r]) - continue; - - /* If find_reloads chose a to use reload_in or reload_out as a reload - register, we don't need to chose one. Otherwise, try even if it found - one since we might save an insn if we find the value lying around. */ - if (reload_in[r] != 0 && reload_reg_rtx[r] != 0 - && (rtx_equal_p (reload_in[r], reload_reg_rtx[r]) - || rtx_equal_p (reload_out[r], reload_reg_rtx[r]))) - continue; - -#if 0 /* No longer needed for correct operation. - It might give better code, or might not; worth an experiment? */ - /* If this is an optional reload, we can't inherit from earlier insns - until we are sure that any non-optional reloads have been allocated. - The following code takes advantage of the fact that optional reloads - are at the end of reload_order. */ - if (reload_optional[r] != 0) - for (i = 0; i < j; i++) - if ((reload_out[reload_order[i]] != 0 - || reload_in[reload_order[i]] != 0 - || reload_secondary_p[reload_order[i]]) - && ! reload_optional[reload_order[i]] - && reload_reg_rtx[reload_order[i]] == 0) - allocate_reload_reg (reload_order[i], insn, 0, inheritance); -#endif - - /* First see if this pseudo is already available as reloaded - for a previous insn. We cannot try to inherit for reloads - that are smaller than the maximum number of registers needed - for groups unless the register we would allocate cannot be used - for the groups. - - We could check here to see if this is a secondary reload for - an object that is already in a register of the desired class. - This would avoid the need for the secondary reload register. - But this is complex because we can't easily determine what - objects might want to be loaded via this reload. So let a register - be allocated here. In `emit_reload_insns' we suppress one of the - loads in the case described above. */ - - if (inheritance) - { - register int regno = -1; - enum machine_mode mode; - - if (reload_in[r] == 0) - ; - else if (GET_CODE (reload_in[r]) == REG) - { - regno = REGNO (reload_in[r]); - mode = GET_MODE (reload_in[r]); - } - else if (GET_CODE (reload_in_reg[r]) == REG) - { - regno = REGNO (reload_in_reg[r]); - mode = GET_MODE (reload_in_reg[r]); - } -#if 0 - /* This won't work, since REGNO can be a pseudo reg number. - Also, it takes much more hair to keep track of all the things - that can invalidate an inherited reload of part of a pseudoreg. */ - else if (GET_CODE (reload_in[r]) == SUBREG - && GET_CODE (SUBREG_REG (reload_in[r])) == REG) - regno = REGNO (SUBREG_REG (reload_in[r])) + SUBREG_WORD (reload_in[r]); -#endif - - if (regno >= 0 && reg_last_reload_reg[regno] != 0) - { - i = spill_reg_order[REGNO (reg_last_reload_reg[regno])]; - - if (reg_reloaded_contents[i] == regno - && (GET_MODE_SIZE (GET_MODE (reg_last_reload_reg[regno])) - >= GET_MODE_SIZE (mode)) - && HARD_REGNO_MODE_OK (spill_regs[i], reload_mode[r]) - && TEST_HARD_REG_BIT (reg_class_contents[(int) reload_reg_class[r]], - spill_regs[i]) - && (reload_nregs[r] == max_group_size - || ! TEST_HARD_REG_BIT (reg_class_contents[(int) group_class], - spill_regs[i])) - && reload_reg_free_p (spill_regs[i], reload_opnum[r], - reload_when_needed[r]) - && reload_reg_free_before_p (spill_regs[i], - reload_opnum[r], - reload_when_needed[r])) - { - /* If a group is needed, verify that all the subsequent - registers still have their values intact. */ - int nr - = HARD_REGNO_NREGS (spill_regs[i], reload_mode[r]); - int k; - - for (k = 1; k < nr; k++) - if (reg_reloaded_contents[spill_reg_order[spill_regs[i] + k]] - != regno) - break; - - if (k == nr) - { - int i1; - - /* We found a register that contains the - value we need. If this register is the - same as an `earlyclobber' operand of the - current insn, just mark it as a place to - reload from since we can't use it as the - reload register itself. */ - - for (i1 = 0; i1 < n_earlyclobbers; i1++) - if (reg_overlap_mentioned_for_reload_p - (reg_last_reload_reg[regno], - reload_earlyclobbers[i1])) - break; - - if (i1 != n_earlyclobbers - /* Don't really use the inherited spill reg - if we need it wider than we've got it. */ - || (GET_MODE_SIZE (reload_mode[r]) - > GET_MODE_SIZE (mode))) - reload_override_in[r] = reg_last_reload_reg[regno]; - else - { - /* We can use this as a reload reg. */ - /* Mark the register as in use for this part of - the insn. */ - mark_reload_reg_in_use (spill_regs[i], - reload_opnum[r], - reload_when_needed[r], - reload_mode[r]); - reload_reg_rtx[r] = reg_last_reload_reg[regno]; - reload_inherited[r] = 1; - reload_inheritance_insn[r] - = reg_reloaded_insn[i]; - reload_spill_index[r] = i; - SET_HARD_REG_BIT (reload_reg_used_for_inherit, - spill_regs[i]); - } - } - } - } - } - - /* Here's another way to see if the value is already lying around. */ - if (inheritance - && reload_in[r] != 0 - && ! reload_inherited[r] - && reload_out[r] == 0 - && (CONSTANT_P (reload_in[r]) - || GET_CODE (reload_in[r]) == PLUS - || GET_CODE (reload_in[r]) == REG - || GET_CODE (reload_in[r]) == MEM) - && (reload_nregs[r] == max_group_size - || ! reg_classes_intersect_p (reload_reg_class[r], group_class))) - { - register rtx equiv - = find_equiv_reg (reload_in[r], insn, reload_reg_class[r], - -1, NULL_PTR, 0, reload_mode[r]); - int regno; - - if (equiv != 0) - { - if (GET_CODE (equiv) == REG) - regno = REGNO (equiv); - else if (GET_CODE (equiv) == SUBREG) - { - regno = REGNO (SUBREG_REG (equiv)); - if (regno < FIRST_PSEUDO_REGISTER) - regno += SUBREG_WORD (equiv); - } - else - abort (); - } - - /* If we found a spill reg, reject it unless it is free - and of the desired class. */ - if (equiv != 0 - && ((spill_reg_order[regno] >= 0 - && ! reload_reg_free_before_p (regno, reload_opnum[r], - reload_when_needed[r])) - || ! TEST_HARD_REG_BIT (reg_class_contents[(int) reload_reg_class[r]], - regno))) - equiv = 0; - - if (equiv != 0 && TEST_HARD_REG_BIT (reload_reg_used_at_all, regno)) - equiv = 0; - - if (equiv != 0 && ! HARD_REGNO_MODE_OK (regno, reload_mode[r])) - equiv = 0; - - /* We found a register that contains the value we need. - If this register is the same as an `earlyclobber' operand - of the current insn, just mark it as a place to reload from - since we can't use it as the reload register itself. */ - - if (equiv != 0) - for (i = 0; i < n_earlyclobbers; i++) - if (reg_overlap_mentioned_for_reload_p (equiv, - reload_earlyclobbers[i])) - { - reload_override_in[r] = equiv; - equiv = 0; - break; - } - - /* JRV: If the equiv register we have found is explicitly - clobbered in the current insn, mark but don't use, as above. */ - - if (equiv != 0 && regno_clobbered_p (regno, insn)) - { - reload_override_in[r] = equiv; - equiv = 0; - } - - /* If we found an equivalent reg, say no code need be generated - to load it, and use it as our reload reg. */ - if (equiv != 0 && regno != FRAME_POINTER_REGNUM) - { - reload_reg_rtx[r] = equiv; - reload_inherited[r] = 1; - /* If it is a spill reg, - mark the spill reg as in use for this insn. */ - i = spill_reg_order[regno]; - if (i >= 0) - { - mark_reload_reg_in_use (regno, reload_opnum[r], - reload_when_needed[r], - reload_mode[r]); - SET_HARD_REG_BIT (reload_reg_used_for_inherit, regno); - } - } - } - - /* If we found a register to use already, or if this is an optional - reload, we are done. */ - if (reload_reg_rtx[r] != 0 || reload_optional[r] != 0) - continue; - -#if 0 /* No longer needed for correct operation. Might or might not - give better code on the average. Want to experiment? */ - - /* See if there is a later reload that has a class different from our - class that intersects our class or that requires less register - than our reload. If so, we must allocate a register to this - reload now, since that reload might inherit a previous reload - and take the only available register in our class. Don't do this - for optional reloads since they will force all previous reloads - to be allocated. Also don't do this for reloads that have been - turned off. */ - - for (i = j + 1; i < n_reloads; i++) - { - int s = reload_order[i]; - - if ((reload_in[s] == 0 && reload_out[s] == 0 - && ! reload_secondary_p[s]) - || reload_optional[s]) - continue; - - if ((reload_reg_class[s] != reload_reg_class[r] - && reg_classes_intersect_p (reload_reg_class[r], - reload_reg_class[s])) - || reload_nregs[s] < reload_nregs[r]) - break; - } - - if (i == n_reloads) - continue; - - allocate_reload_reg (r, insn, j == n_reloads - 1, inheritance); -#endif - } - - /* Now allocate reload registers for anything non-optional that - didn't get one yet. */ - for (j = 0; j < n_reloads; j++) - { - register int r = reload_order[j]; - - /* Ignore reloads that got marked inoperative. */ - if (reload_out[r] == 0 && reload_in[r] == 0 && ! reload_secondary_p[r]) - continue; - - /* Skip reloads that already have a register allocated or are - optional. */ - if (reload_reg_rtx[r] != 0 || reload_optional[r]) - continue; - - if (! allocate_reload_reg (r, insn, j == n_reloads - 1, inheritance)) - break; - } - - /* If that loop got all the way, we have won. */ - if (j == n_reloads) - break; - - fail: - /* Loop around and try without any inheritance. */ - /* First undo everything done by the failed attempt - to allocate with inheritance. */ - bcopy (save_reload_reg_rtx, reload_reg_rtx, sizeof reload_reg_rtx); - bcopy (save_reload_inherited, reload_inherited, sizeof reload_inherited); - bcopy (save_reload_inheritance_insn, reload_inheritance_insn, - sizeof reload_inheritance_insn); - bcopy (save_reload_override_in, reload_override_in, - sizeof reload_override_in); - bcopy (save_reload_spill_index, reload_spill_index, - sizeof reload_spill_index); - COPY_HARD_REG_SET (reload_reg_used, save_reload_reg_used); - COPY_HARD_REG_SET (reload_reg_used_at_all, save_reload_reg_used_at_all); - COPY_HARD_REG_SET (reload_reg_used_in_op_addr, - save_reload_reg_used_in_op_addr); - COPY_HARD_REG_SET (reload_reg_used_in_insn, - save_reload_reg_used_in_insn); - COPY_HARD_REG_SET (reload_reg_used_in_other_addr, - save_reload_reg_used_in_other_addr); - - for (i = 0; i < reload_n_operands; i++) - { - COPY_HARD_REG_SET (reload_reg_used_in_input[i], - save_reload_reg_used_in_input[i]); - COPY_HARD_REG_SET (reload_reg_used_in_output[i], - save_reload_reg_used_in_output[i]); - COPY_HARD_REG_SET (reload_reg_used_in_input_addr[i], - save_reload_reg_used_in_input_addr[i]); - COPY_HARD_REG_SET (reload_reg_used_in_output_addr[i], - save_reload_reg_used_in_output_addr[i]); - } - } - - /* If we thought we could inherit a reload, because it seemed that - nothing else wanted the same reload register earlier in the insn, - verify that assumption, now that all reloads have been assigned. */ - - for (j = 0; j < n_reloads; j++) - { - register int r = reload_order[j]; - - if (reload_inherited[r] && reload_reg_rtx[r] != 0 - && ! reload_reg_free_before_p (true_regnum (reload_reg_rtx[r]), - reload_opnum[r], - reload_when_needed[r])) - reload_inherited[r] = 0; - - /* If we found a better place to reload from, - validate it in the same fashion, if it is a reload reg. */ - if (reload_override_in[r] - && (GET_CODE (reload_override_in[r]) == REG - || GET_CODE (reload_override_in[r]) == SUBREG)) - { - int regno = true_regnum (reload_override_in[r]); - if (spill_reg_order[regno] >= 0 - && ! reload_reg_free_before_p (regno, reload_opnum[r], - reload_when_needed[r])) - reload_override_in[r] = 0; - } - } - - /* Now that reload_override_in is known valid, - actually override reload_in. */ - for (j = 0; j < n_reloads; j++) - if (reload_override_in[j]) - reload_in[j] = reload_override_in[j]; - - /* If this reload won't be done because it has been cancelled or is - optional and not inherited, clear reload_reg_rtx so other - routines (such as subst_reloads) don't get confused. */ - for (j = 0; j < n_reloads; j++) - if (reload_reg_rtx[j] != 0 - && ((reload_optional[j] && ! reload_inherited[j]) - || (reload_in[j] == 0 && reload_out[j] == 0 - && ! reload_secondary_p[j]))) - { - int regno = true_regnum (reload_reg_rtx[j]); - - if (spill_reg_order[regno] >= 0) - clear_reload_reg_in_use (regno, reload_opnum[j], - reload_when_needed[j], reload_mode[j]); - reload_reg_rtx[j] = 0; - } - - /* Record which pseudos and which spill regs have output reloads. */ - for (j = 0; j < n_reloads; j++) - { - register int r = reload_order[j]; - - i = reload_spill_index[r]; - - /* I is nonneg if this reload used one of the spill regs. - If reload_reg_rtx[r] is 0, this is an optional reload - that we opted to ignore. */ - if (reload_out[r] != 0 && GET_CODE (reload_out[r]) == REG - && reload_reg_rtx[r] != 0) - { - register int nregno = REGNO (reload_out[r]); - int nr = 1; - - if (nregno < FIRST_PSEUDO_REGISTER) - nr = HARD_REGNO_NREGS (nregno, reload_mode[r]); - - while (--nr >= 0) - reg_has_output_reload[nregno + nr] = 1; - - if (i >= 0) - { - nr = HARD_REGNO_NREGS (spill_regs[i], reload_mode[r]); - while (--nr >= 0) - SET_HARD_REG_BIT (reg_is_output_reload, spill_regs[i] + nr); - } - - if (reload_when_needed[r] != RELOAD_OTHER - && reload_when_needed[r] != RELOAD_FOR_OUTPUT - && reload_when_needed[r] != RELOAD_FOR_INSN) - abort (); - } - } -} - -/* If SMALL_REGISTER_CLASSES are defined, we may not have merged two - reloads of the same item for fear that we might not have enough reload - registers. However, normally they will get the same reload register - and hence actually need not be loaded twice. - - Here we check for the most common case of this phenomenon: when we have - a number of reloads for the same object, each of which were allocated - the same reload_reg_rtx, that reload_reg_rtx is not used for any other - reload, and is not modified in the insn itself. If we find such, - merge all the reloads and set the resulting reload to RELOAD_OTHER. - This will not increase the number of spill registers needed and will - prevent redundant code. */ - -#ifdef SMALL_REGISTER_CLASSES - -static void -merge_assigned_reloads (insn) - rtx insn; -{ - int i, j; - - /* Scan all the reloads looking for ones that only load values and - are not already RELOAD_OTHER and ones whose reload_reg_rtx are - assigned and not modified by INSN. */ - - for (i = 0; i < n_reloads; i++) - { - if (reload_in[i] == 0 || reload_when_needed[i] == RELOAD_OTHER - || reload_out[i] != 0 || reload_reg_rtx[i] == 0 - || reg_set_p (reload_reg_rtx[i], insn)) - continue; - - /* Look at all other reloads. Ensure that the only use of this - reload_reg_rtx is in a reload that just loads the same value - as we do. Note that any secondary reloads must be of the identical - class since the values, modes, and result registers are the - same, so we need not do anything with any secondary reloads. */ - - for (j = 0; j < n_reloads; j++) - { - if (i == j || reload_reg_rtx[j] == 0 - || ! reg_overlap_mentioned_p (reload_reg_rtx[j], - reload_reg_rtx[i])) - continue; - - /* If the reload regs aren't exactly the same (e.g, different modes) - or if the values are different, we can't merge anything with this - reload register. */ - - if (! rtx_equal_p (reload_reg_rtx[i], reload_reg_rtx[j]) - || reload_out[j] != 0 || reload_in[j] == 0 - || ! rtx_equal_p (reload_in[i], reload_in[j])) - break; - } - - /* If all is OK, merge the reloads. Only set this to RELOAD_OTHER if - we, in fact, found any matching reloads. */ - - if (j == n_reloads) - { - for (j = 0; j < n_reloads; j++) - if (i != j && reload_reg_rtx[j] != 0 - && rtx_equal_p (reload_reg_rtx[i], reload_reg_rtx[j])) - { - reload_when_needed[i] = RELOAD_OTHER; - reload_in[j] = 0; - transfer_replacements (i, j); - } - - /* If this is now RELOAD_OTHER, look for any reloads that load - parts of this operand and set them to RELOAD_FOR_OTHER_ADDRESS - if they were for inputs, RELOAD_OTHER for outputs. Note that - this test is equivalent to looking for reloads for this operand - number. */ - - if (reload_when_needed[i] == RELOAD_OTHER) - for (j = 0; j < n_reloads; j++) - if (reload_in[j] != 0 - && reload_when_needed[i] != RELOAD_OTHER - && reg_overlap_mentioned_for_reload_p (reload_in[j], - reload_in[i])) - reload_when_needed[j] - = reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS - ? RELOAD_FOR_OTHER_ADDRESS : RELOAD_OTHER; - } - } -} -#endif /* SMALL_RELOAD_CLASSES */ - -/* Output insns to reload values in and out of the chosen reload regs. */ - -static void -emit_reload_insns (insn) - rtx insn; -{ - register int j; - rtx input_reload_insns[MAX_RECOG_OPERANDS]; - rtx other_input_address_reload_insns = 0; - rtx other_input_reload_insns = 0; - rtx input_address_reload_insns[MAX_RECOG_OPERANDS]; - rtx output_reload_insns[MAX_RECOG_OPERANDS]; - rtx output_address_reload_insns[MAX_RECOG_OPERANDS]; - rtx operand_reload_insns = 0; - rtx following_insn = NEXT_INSN (insn); - rtx before_insn = insn; - int special; - /* Values to be put in spill_reg_store are put here first. */ - rtx new_spill_reg_store[FIRST_PSEUDO_REGISTER]; - - for (j = 0; j < reload_n_operands; j++) - input_reload_insns[j] = input_address_reload_insns[j] - = output_reload_insns[j] = output_address_reload_insns[j] = 0; - - /* If this is a CALL_INSN preceded by USE insns, any reload insns - must go in front of the first USE insn, not in front of INSN. */ - - if (GET_CODE (insn) == CALL_INSN && GET_CODE (PREV_INSN (insn)) == INSN - && GET_CODE (PATTERN (PREV_INSN (insn))) == USE) - while (GET_CODE (PREV_INSN (before_insn)) == INSN - && GET_CODE (PATTERN (PREV_INSN (before_insn))) == USE) - before_insn = PREV_INSN (before_insn); - - /* If INSN is followed by any CLOBBER insns made by find_reloads, - put our reloads after them since they may otherwise be - misinterpreted. */ - - while (GET_CODE (following_insn) == INSN - && GET_MODE (following_insn) == DImode - && GET_CODE (PATTERN (following_insn)) == CLOBBER - && NEXT_INSN (following_insn) != 0) - following_insn = NEXT_INSN (following_insn); - - /* Now output the instructions to copy the data into and out of the - reload registers. Do these in the order that the reloads were reported, - since reloads of base and index registers precede reloads of operands - and the operands may need the base and index registers reloaded. */ - - for (j = 0; j < n_reloads; j++) - { - register rtx old; - rtx oldequiv_reg = 0; - rtx store_insn = 0; - - old = reload_in[j]; - if (old != 0 && ! reload_inherited[j] - && ! rtx_equal_p (reload_reg_rtx[j], old) - && reload_reg_rtx[j] != 0) - { - register rtx reloadreg = reload_reg_rtx[j]; - rtx oldequiv = 0; - enum machine_mode mode; - rtx *where; - - /* Determine the mode to reload in. - This is very tricky because we have three to choose from. - There is the mode the insn operand wants (reload_inmode[J]). - There is the mode of the reload register RELOADREG. - There is the intrinsic mode of the operand, which we could find - by stripping some SUBREGs. - It turns out that RELOADREG's mode is irrelevant: - we can change that arbitrarily. - - Consider (SUBREG:SI foo:QI) as an operand that must be SImode; - then the reload reg may not support QImode moves, so use SImode. - If foo is in memory due to spilling a pseudo reg, this is safe, - because the QImode value is in the least significant part of a - slot big enough for a SImode. If foo is some other sort of - memory reference, then it is impossible to reload this case, - so previous passes had better make sure this never happens. - - Then consider a one-word union which has SImode and one of its - members is a float, being fetched as (SUBREG:SF union:SI). - We must fetch that as SFmode because we could be loading into - a float-only register. In this case OLD's mode is correct. - - Consider an immediate integer: it has VOIDmode. Here we need - to get a mode from something else. - - In some cases, there is a fourth mode, the operand's - containing mode. If the insn specifies a containing mode for - this operand, it overrides all others. - - I am not sure whether the algorithm here is always right, - but it does the right things in those cases. */ - - mode = GET_MODE (old); - if (mode == VOIDmode) - mode = reload_inmode[j]; - -#ifdef SECONDARY_INPUT_RELOAD_CLASS - /* If we need a secondary register for this operation, see if - the value is already in a register in that class. Don't - do this if the secondary register will be used as a scratch - register. */ - - if (reload_secondary_reload[j] >= 0 - && reload_secondary_icode[j] == CODE_FOR_nothing - && optimize) - oldequiv - = find_equiv_reg (old, insn, - reload_reg_class[reload_secondary_reload[j]], - -1, NULL_PTR, 0, mode); -#endif - - /* If reloading from memory, see if there is a register - that already holds the same value. If so, reload from there. - We can pass 0 as the reload_reg_p argument because - any other reload has either already been emitted, - in which case find_equiv_reg will see the reload-insn, - or has yet to be emitted, in which case it doesn't matter - because we will use this equiv reg right away. */ - - if (oldequiv == 0 && optimize - && (GET_CODE (old) == MEM - || (GET_CODE (old) == REG - && REGNO (old) >= FIRST_PSEUDO_REGISTER - && reg_renumber[REGNO (old)] < 0))) - oldequiv = find_equiv_reg (old, insn, ALL_REGS, - -1, NULL_PTR, 0, mode); - - if (oldequiv) - { - int regno = true_regnum (oldequiv); - - /* If OLDEQUIV is a spill register, don't use it for this - if any other reload needs it at an earlier stage of this insn - or at this stage. */ - if (spill_reg_order[regno] >= 0 - && (! reload_reg_free_p (regno, reload_opnum[j], - reload_when_needed[j]) - || ! reload_reg_free_before_p (regno, reload_opnum[j], - reload_when_needed[j]))) - oldequiv = 0; - - /* If OLDEQUIV is not a spill register, - don't use it if any other reload wants it. */ - if (spill_reg_order[regno] < 0) - { - int k; - for (k = 0; k < n_reloads; k++) - if (reload_reg_rtx[k] != 0 && k != j - && reg_overlap_mentioned_for_reload_p (reload_reg_rtx[k], - oldequiv)) - { - oldequiv = 0; - break; - } - } - - /* If it is no cheaper to copy from OLDEQUIV into the - reload register than it would be to move from memory, - don't use it. Likewise, if we need a secondary register - or memory. */ - - if (oldequiv != 0 - && ((REGNO_REG_CLASS (regno) != reload_reg_class[j] - && (REGISTER_MOVE_COST (REGNO_REG_CLASS (regno), - reload_reg_class[j]) - >= MEMORY_MOVE_COST (mode))) -#ifdef SECONDARY_INPUT_RELOAD_CLASS - || (SECONDARY_INPUT_RELOAD_CLASS (reload_reg_class[j], - mode, oldequiv) - != NO_REGS) -#endif -#ifdef SECONDARY_MEMORY_NEEDED - || SECONDARY_MEMORY_NEEDED (reload_reg_class[j], - REGNO_REG_CLASS (regno), - mode) -#endif - )) - oldequiv = 0; - } - - if (oldequiv == 0) - oldequiv = old; - else if (GET_CODE (oldequiv) == REG) - oldequiv_reg = oldequiv; - else if (GET_CODE (oldequiv) == SUBREG) - oldequiv_reg = SUBREG_REG (oldequiv); - - /* Encapsulate both RELOADREG and OLDEQUIV into that mode, - then load RELOADREG from OLDEQUIV. */ - - if (GET_MODE (reloadreg) != mode) - reloadreg = gen_lowpart_common (mode, reloadreg); - while (GET_CODE (oldequiv) == SUBREG && GET_MODE (oldequiv) != mode) - oldequiv = SUBREG_REG (oldequiv); - if (GET_MODE (oldequiv) != VOIDmode - && mode != GET_MODE (oldequiv)) - oldequiv = gen_rtx (SUBREG, mode, oldequiv, 0); - - /* Switch to the right place to emit the reload insns. */ - switch (reload_when_needed[j]) - { - case RELOAD_OTHER: - where = &other_input_reload_insns; - break; - case RELOAD_FOR_INPUT: - where = &input_reload_insns[reload_opnum[j]]; - break; - case RELOAD_FOR_INPUT_ADDRESS: - where = &input_address_reload_insns[reload_opnum[j]]; - break; - case RELOAD_FOR_OUTPUT_ADDRESS: - where = &output_address_reload_insns[reload_opnum[j]]; - break; - case RELOAD_FOR_OPERAND_ADDRESS: - where = &operand_reload_insns; - break; - case RELOAD_FOR_OTHER_ADDRESS: - where = &other_input_address_reload_insns; - break; - default: - abort (); - } - - push_to_sequence (*where); - special = 0; - - /* Auto-increment addresses must be reloaded in a special way. */ - if (GET_CODE (oldequiv) == POST_INC - || GET_CODE (oldequiv) == POST_DEC - || GET_CODE (oldequiv) == PRE_INC - || GET_CODE (oldequiv) == PRE_DEC) - { - /* We are not going to bother supporting the case where a - incremented register can't be copied directly from - OLDEQUIV since this seems highly unlikely. */ - if (reload_secondary_reload[j] >= 0) - abort (); - /* Prevent normal processing of this reload. */ - special = 1; - /* Output a special code sequence for this case. */ - inc_for_reload (reloadreg, oldequiv, reload_inc[j]); - } - - /* If we are reloading a pseudo-register that was set by the previous - insn, see if we can get rid of that pseudo-register entirely - by redirecting the previous insn into our reload register. */ - - else if (optimize && GET_CODE (old) == REG - && REGNO (old) >= FIRST_PSEUDO_REGISTER - && dead_or_set_p (insn, old) - /* This is unsafe if some other reload - uses the same reg first. */ - && reload_reg_free_before_p (REGNO (reloadreg), - reload_opnum[j], - reload_when_needed[j])) - { - rtx temp = PREV_INSN (insn); - while (temp && GET_CODE (temp) == NOTE) - temp = PREV_INSN (temp); - if (temp - && GET_CODE (temp) == INSN - && GET_CODE (PATTERN (temp)) == SET - && SET_DEST (PATTERN (temp)) == old - /* Make sure we can access insn_operand_constraint. */ - && asm_noperands (PATTERN (temp)) < 0 - /* This is unsafe if prev insn rejects our reload reg. */ - && constraint_accepts_reg_p (insn_operand_constraint[recog_memoized (temp)][0], - reloadreg) - /* This is unsafe if operand occurs more than once in current - insn. Perhaps some occurrences aren't reloaded. */ - && count_occurrences (PATTERN (insn), old) == 1 - /* Don't risk splitting a matching pair of operands. */ - && ! reg_mentioned_p (old, SET_SRC (PATTERN (temp)))) - { - /* Store into the reload register instead of the pseudo. */ - SET_DEST (PATTERN (temp)) = reloadreg; - /* If these are the only uses of the pseudo reg, - pretend for GDB it lives in the reload reg we used. */ - if (reg_n_deaths[REGNO (old)] == 1 - && reg_n_sets[REGNO (old)] == 1) - { - reg_renumber[REGNO (old)] = REGNO (reload_reg_rtx[j]); - alter_reg (REGNO (old), -1); - } - special = 1; - } - } - - /* We can't do that, so output an insn to load RELOADREG. */ - - if (! special) - { -#ifdef SECONDARY_INPUT_RELOAD_CLASS - rtx second_reload_reg = 0; - enum insn_code icode; - - /* If we have a secondary reload, pick up the secondary register - and icode, if any. If OLDEQUIV and OLD are different or - if this is an in-out reload, recompute whether or not we - still need a secondary register and what the icode should - be. If we still need a secondary register and the class or - icode is different, go back to reloading from OLD if using - OLDEQUIV means that we got the wrong type of register. We - cannot have different class or icode due to an in-out reload - because we don't make such reloads when both the input and - output need secondary reload registers. */ - - if (reload_secondary_reload[j] >= 0) - { - int secondary_reload = reload_secondary_reload[j]; - rtx real_oldequiv = oldequiv; - rtx real_old = old; - - /* If OLDEQUIV is a pseudo with a MEM, get the real MEM - and similarly for OLD. - See comments in find_secondary_reload in reload.c. */ - if (GET_CODE (oldequiv) == REG - && REGNO (oldequiv) >= FIRST_PSEUDO_REGISTER - && reg_equiv_mem[REGNO (oldequiv)] != 0) - real_oldequiv = reg_equiv_mem[REGNO (oldequiv)]; - - if (GET_CODE (old) == REG - && REGNO (old) >= FIRST_PSEUDO_REGISTER - && reg_equiv_mem[REGNO (old)] != 0) - real_old = reg_equiv_mem[REGNO (old)]; - - second_reload_reg = reload_reg_rtx[secondary_reload]; - icode = reload_secondary_icode[j]; - - if ((old != oldequiv && ! rtx_equal_p (old, oldequiv)) - || (reload_in[j] != 0 && reload_out[j] != 0)) - { - enum reg_class new_class - = SECONDARY_INPUT_RELOAD_CLASS (reload_reg_class[j], - mode, real_oldequiv); - - if (new_class == NO_REGS) - second_reload_reg = 0; - else - { - enum insn_code new_icode; - enum machine_mode new_mode; - - if (! TEST_HARD_REG_BIT (reg_class_contents[(int) new_class], - REGNO (second_reload_reg))) - oldequiv = old, real_oldequiv = real_old; - else - { - new_icode = reload_in_optab[(int) mode]; - if (new_icode != CODE_FOR_nothing - && ((insn_operand_predicate[(int) new_icode][0] - && ! ((*insn_operand_predicate[(int) new_icode][0]) - (reloadreg, mode))) - || (insn_operand_predicate[(int) new_icode][1] - && ! ((*insn_operand_predicate[(int) new_icode][1]) - (real_oldequiv, mode))))) - new_icode = CODE_FOR_nothing; - - if (new_icode == CODE_FOR_nothing) - new_mode = mode; - else - new_mode = insn_operand_mode[(int) new_icode][2]; - - if (GET_MODE (second_reload_reg) != new_mode) - { - if (!HARD_REGNO_MODE_OK (REGNO (second_reload_reg), - new_mode)) - oldequiv = old, real_oldequiv = real_old; - else - second_reload_reg - = gen_rtx (REG, new_mode, - REGNO (second_reload_reg)); - } - } - } - } - - /* If we still need a secondary reload register, check - to see if it is being used as a scratch or intermediate - register and generate code appropriately. If we need - a scratch register, use REAL_OLDEQUIV since the form of - the insn may depend on the actual address if it is - a MEM. */ - - if (second_reload_reg) - { - if (icode != CODE_FOR_nothing) - { - emit_insn (GEN_FCN (icode) (reloadreg, real_oldequiv, - second_reload_reg)); - special = 1; - } - else - { - /* See if we need a scratch register to load the - intermediate register (a tertiary reload). */ - enum insn_code tertiary_icode - = reload_secondary_icode[secondary_reload]; - - if (tertiary_icode != CODE_FOR_nothing) - { - rtx third_reload_reg - = reload_reg_rtx[reload_secondary_reload[secondary_reload]]; - - emit_insn ((GEN_FCN (tertiary_icode) - (second_reload_reg, real_oldequiv, - third_reload_reg))); - } - else - gen_input_reload (second_reload_reg, oldequiv, - reload_opnum[j], - reload_when_needed[j]); - - oldequiv = second_reload_reg; - } - } - } -#endif - - if (! special) - gen_input_reload (reloadreg, oldequiv, reload_opnum[j], - reload_when_needed[j]); - -#if defined(SECONDARY_INPUT_RELOAD_CLASS) && defined(PRESERVE_DEATH_INFO_REGNO_P) - /* We may have to make a REG_DEAD note for the secondary reload - register in the insns we just made. Find the last insn that - mentioned the register. */ - if (! special && second_reload_reg - && PRESERVE_DEATH_INFO_REGNO_P (REGNO (second_reload_reg))) - { - rtx prev; - - for (prev = get_last_insn (); prev; - prev = PREV_INSN (prev)) - if (GET_RTX_CLASS (GET_CODE (prev) == 'i') - && reg_overlap_mentioned_for_reload_p (second_reload_reg, - PATTERN (prev))) - { - REG_NOTES (prev) = gen_rtx (EXPR_LIST, REG_DEAD, - second_reload_reg, - REG_NOTES (prev)); - break; - } - } -#endif - } - - /* End this sequence. */ - *where = get_insns (); - end_sequence (); - } - - /* Add a note saying the input reload reg - dies in this insn, if anyone cares. */ -#ifdef PRESERVE_DEATH_INFO_REGNO_P - if (old != 0 - && reload_reg_rtx[j] != old - && reload_reg_rtx[j] != 0 - && reload_out[j] == 0 - && ! reload_inherited[j] - && PRESERVE_DEATH_INFO_REGNO_P (REGNO (reload_reg_rtx[j]))) - { - register rtx reloadreg = reload_reg_rtx[j]; - -#if 0 - /* We can't abort here because we need to support this for sched.c. - It's not terrible to miss a REG_DEAD note, but we should try - to figure out how to do this correctly. */ - /* The code below is incorrect for address-only reloads. */ - if (reload_when_needed[j] != RELOAD_OTHER - && reload_when_needed[j] != RELOAD_FOR_INPUT) - abort (); -#endif - - /* Add a death note to this insn, for an input reload. */ - - if ((reload_when_needed[j] == RELOAD_OTHER - || reload_when_needed[j] == RELOAD_FOR_INPUT) - && ! dead_or_set_p (insn, reloadreg)) - REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_DEAD, - reloadreg, REG_NOTES (insn)); - } - - /* When we inherit a reload, the last marked death of the reload reg - may no longer really be a death. */ - if (reload_reg_rtx[j] != 0 - && PRESERVE_DEATH_INFO_REGNO_P (REGNO (reload_reg_rtx[j])) - && reload_inherited[j]) - { - /* Handle inheriting an output reload. - Remove the death note from the output reload insn. */ - if (reload_spill_index[j] >= 0 - && GET_CODE (reload_in[j]) == REG - && spill_reg_store[reload_spill_index[j]] != 0 - && find_regno_note (spill_reg_store[reload_spill_index[j]], - REG_DEAD, REGNO (reload_reg_rtx[j]))) - remove_death (REGNO (reload_reg_rtx[j]), - spill_reg_store[reload_spill_index[j]]); - /* Likewise for input reloads that were inherited. */ - else if (reload_spill_index[j] >= 0 - && GET_CODE (reload_in[j]) == REG - && spill_reg_store[reload_spill_index[j]] == 0 - && reload_inheritance_insn[j] != 0 - && find_regno_note (reload_inheritance_insn[j], REG_DEAD, - REGNO (reload_reg_rtx[j]))) - remove_death (REGNO (reload_reg_rtx[j]), - reload_inheritance_insn[j]); - else - { - rtx prev; - - /* We got this register from find_equiv_reg. - Search back for its last death note and get rid of it. - But don't search back too far. - Don't go past a place where this reg is set, - since a death note before that remains valid. */ - for (prev = PREV_INSN (insn); - prev && GET_CODE (prev) != CODE_LABEL; - prev = PREV_INSN (prev)) - if (GET_RTX_CLASS (GET_CODE (prev)) == 'i' - && dead_or_set_p (prev, reload_reg_rtx[j])) - { - if (find_regno_note (prev, REG_DEAD, - REGNO (reload_reg_rtx[j]))) - remove_death (REGNO (reload_reg_rtx[j]), prev); - break; - } - } - } - - /* We might have used find_equiv_reg above to choose an alternate - place from which to reload. If so, and it died, we need to remove - that death and move it to one of the insns we just made. */ - - if (oldequiv_reg != 0 - && PRESERVE_DEATH_INFO_REGNO_P (true_regnum (oldequiv_reg))) - { - rtx prev, prev1; - - for (prev = PREV_INSN (insn); prev && GET_CODE (prev) != CODE_LABEL; - prev = PREV_INSN (prev)) - if (GET_RTX_CLASS (GET_CODE (prev)) == 'i' - && dead_or_set_p (prev, oldequiv_reg)) - { - if (find_regno_note (prev, REG_DEAD, REGNO (oldequiv_reg))) - { - for (prev1 = this_reload_insn; - prev1; prev1 = PREV_INSN (prev1)) - if (GET_RTX_CLASS (GET_CODE (prev1) == 'i') - && reg_overlap_mentioned_for_reload_p (oldequiv_reg, - PATTERN (prev1))) - { - REG_NOTES (prev1) = gen_rtx (EXPR_LIST, REG_DEAD, - oldequiv_reg, - REG_NOTES (prev1)); - break; - } - remove_death (REGNO (oldequiv_reg), prev); - } - break; - } - } -#endif - - /* If we are reloading a register that was recently stored in with an - output-reload, see if we can prove there was - actually no need to store the old value in it. */ - - if (optimize && reload_inherited[j] && reload_spill_index[j] >= 0 - && reload_in[j] != 0 - && GET_CODE (reload_in[j]) == REG -#if 0 - /* There doesn't seem to be any reason to restrict this to pseudos - and doing so loses in the case where we are copying from a - register of the wrong class. */ - && REGNO (reload_in[j]) >= FIRST_PSEUDO_REGISTER -#endif - && spill_reg_store[reload_spill_index[j]] != 0 - /* This is unsafe if some other reload uses the same reg first. */ - && reload_reg_free_before_p (spill_regs[reload_spill_index[j]], - reload_opnum[j], reload_when_needed[j]) - && dead_or_set_p (insn, reload_in[j]) - /* This is unsafe if operand occurs more than once in current - insn. Perhaps some occurrences weren't reloaded. */ - && count_occurrences (PATTERN (insn), reload_in[j]) == 1) - delete_output_reload (insn, j, - spill_reg_store[reload_spill_index[j]]); - - /* Input-reloading is done. Now do output-reloading, - storing the value from the reload-register after the main insn - if reload_out[j] is nonzero. - - ??? At some point we need to support handling output reloads of - JUMP_INSNs or insns that set cc0. */ - old = reload_out[j]; - if (old != 0 - && reload_reg_rtx[j] != old - && reload_reg_rtx[j] != 0) - { - register rtx reloadreg = reload_reg_rtx[j]; - register rtx second_reloadreg = 0; - rtx note, p; - enum machine_mode mode; - int special = 0; - - /* An output operand that dies right away does need a reload, - but need not be copied from it. Show the new location in the - REG_UNUSED note. */ - if ((GET_CODE (old) == REG || GET_CODE (old) == SCRATCH) - && (note = find_reg_note (insn, REG_UNUSED, old)) != 0) - { - XEXP (note, 0) = reload_reg_rtx[j]; - continue; - } - else if (GET_CODE (old) == SCRATCH) - /* If we aren't optimizing, there won't be a REG_UNUSED note, - but we don't want to make an output reload. */ - continue; - -#if 0 - /* Strip off of OLD any size-increasing SUBREGs such as - (SUBREG:SI foo:QI 0). */ - - while (GET_CODE (old) == SUBREG && SUBREG_WORD (old) == 0 - && (GET_MODE_SIZE (GET_MODE (old)) - > GET_MODE_SIZE (GET_MODE (SUBREG_REG (old))))) - old = SUBREG_REG (old); -#endif - - /* If is a JUMP_INSN, we can't support output reloads yet. */ - if (GET_CODE (insn) == JUMP_INSN) - abort (); - - push_to_sequence (output_reload_insns[reload_opnum[j]]); - - /* Determine the mode to reload in. - See comments above (for input reloading). */ - - mode = GET_MODE (old); - if (mode == VOIDmode) - { - /* VOIDmode should never happen for an output. */ - if (asm_noperands (PATTERN (insn)) < 0) - /* It's the compiler's fault. */ - abort (); - error_for_asm (insn, "output operand is constant in `asm'"); - /* Prevent crash--use something we know is valid. */ - mode = word_mode; - old = gen_rtx (REG, mode, REGNO (reloadreg)); - } - - if (GET_MODE (reloadreg) != mode) - reloadreg = gen_lowpart_common (mode, reloadreg); - -#ifdef SECONDARY_OUTPUT_RELOAD_CLASS - - /* If we need two reload regs, set RELOADREG to the intermediate - one, since it will be stored into OUT. We might need a secondary - register only for an input reload, so check again here. */ - - if (reload_secondary_reload[j] >= 0) - { - rtx real_old = old; - - if (GET_CODE (old) == REG && REGNO (old) >= FIRST_PSEUDO_REGISTER - && reg_equiv_mem[REGNO (old)] != 0) - real_old = reg_equiv_mem[REGNO (old)]; - - if((SECONDARY_OUTPUT_RELOAD_CLASS (reload_reg_class[j], - mode, real_old) - != NO_REGS)) - { - second_reloadreg = reloadreg; - reloadreg = reload_reg_rtx[reload_secondary_reload[j]]; - - /* See if RELOADREG is to be used as a scratch register - or as an intermediate register. */ - if (reload_secondary_icode[j] != CODE_FOR_nothing) - { - emit_insn ((GEN_FCN (reload_secondary_icode[j]) - (real_old, second_reloadreg, reloadreg))); - special = 1; - } - else - { - /* See if we need both a scratch and intermediate reload - register. */ - int secondary_reload = reload_secondary_reload[j]; - enum insn_code tertiary_icode - = reload_secondary_icode[secondary_reload]; - rtx pat; - - if (GET_MODE (reloadreg) != mode) - reloadreg = gen_rtx (REG, mode, REGNO (reloadreg)); - - if (tertiary_icode != CODE_FOR_nothing) - { - rtx third_reloadreg - = reload_reg_rtx[reload_secondary_reload[secondary_reload]]; - pat = (GEN_FCN (tertiary_icode) - (reloadreg, second_reloadreg, third_reloadreg)); - } -#ifdef SECONDARY_MEMORY_NEEDED - /* If we need a memory location to do the move, do it that way. */ - else if (GET_CODE (reloadreg) == REG - && REGNO (reloadreg) < FIRST_PSEUDO_REGISTER - && SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (REGNO (reloadreg)), - REGNO_REG_CLASS (REGNO (second_reloadreg)), - GET_MODE (second_reloadreg))) - { - /* Get the memory to use and rewrite both registers - to its mode. */ - rtx loc - = get_secondary_mem (reloadreg, - GET_MODE (second_reloadreg), - reload_opnum[j], - reload_when_needed[j]); - rtx tmp_reloadreg; - - if (GET_MODE (loc) != GET_MODE (second_reloadreg)) - second_reloadreg = gen_rtx (REG, GET_MODE (loc), - REGNO (second_reloadreg)); - - if (GET_MODE (loc) != GET_MODE (reloadreg)) - tmp_reloadreg = gen_rtx (REG, GET_MODE (loc), - REGNO (reloadreg)); - else - tmp_reloadreg = reloadreg; - - emit_move_insn (loc, second_reloadreg); - pat = gen_move_insn (tmp_reloadreg, loc); - } -#endif - else - pat = gen_move_insn (reloadreg, second_reloadreg); - - emit_insn (pat); - } - } - } -#endif - - /* Output the last reload insn. */ - if (! special) - { -#ifdef SECONDARY_MEMORY_NEEDED - /* If we need a memory location to do the move, do it that way. */ - if (GET_CODE (old) == REG && REGNO (old) < FIRST_PSEUDO_REGISTER - && SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (REGNO (old)), - REGNO_REG_CLASS (REGNO (reloadreg)), - GET_MODE (reloadreg))) - { - /* Get the memory to use and rewrite both registers to - its mode. */ - rtx loc = get_secondary_mem (old, GET_MODE (reloadreg), - reload_opnum[j], - reload_when_needed[j]); - - if (GET_MODE (loc) != GET_MODE (reloadreg)) - reloadreg = gen_rtx (REG, GET_MODE (loc), - REGNO (reloadreg)); - - if (GET_MODE (loc) != GET_MODE (old)) - old = gen_rtx (REG, GET_MODE (loc), REGNO (old)); - - emit_insn (gen_move_insn (loc, reloadreg)); - emit_insn (gen_move_insn (old, loc)); - } - else -#endif - emit_insn (gen_move_insn (old, reloadreg)); - } - -#ifdef PRESERVE_DEATH_INFO_REGNO_P - /* If final will look at death notes for this reg, - put one on the last output-reload insn to use it. Similarly - for any secondary register. */ - if (PRESERVE_DEATH_INFO_REGNO_P (REGNO (reloadreg))) - for (p = get_last_insn (); p; p = PREV_INSN (p)) - if (GET_RTX_CLASS (GET_CODE (p)) == 'i' - && reg_overlap_mentioned_for_reload_p (reloadreg, - PATTERN (p))) - REG_NOTES (p) = gen_rtx (EXPR_LIST, REG_DEAD, - reloadreg, REG_NOTES (p)); - -#ifdef SECONDARY_OUTPUT_RELOAD_CLASS - if (! special - && PRESERVE_DEATH_INFO_REGNO_P (REGNO (second_reloadreg))) - for (p = get_last_insn (); p; p = PREV_INSN (p)) - if (GET_RTX_CLASS (GET_CODE (p)) == 'i' - && reg_overlap_mentioned_for_reload_p (second_reloadreg, - PATTERN (p))) - REG_NOTES (p) = gen_rtx (EXPR_LIST, REG_DEAD, - second_reloadreg, REG_NOTES (p)); -#endif -#endif - /* Look at all insns we emitted, just to be safe. */ - for (p = get_insns (); p; p = NEXT_INSN (p)) - if (GET_RTX_CLASS (GET_CODE (p)) == 'i') - { - /* If this output reload doesn't come from a spill reg, - clear any memory of reloaded copies of the pseudo reg. - If this output reload comes from a spill reg, - reg_has_output_reload will make this do nothing. */ - note_stores (PATTERN (p), forget_old_reloads_1); - - if (reg_mentioned_p (reload_reg_rtx[j], PATTERN (p))) - store_insn = p; - } - - output_reload_insns[reload_opnum[j]] = get_insns (); - end_sequence (); - - } - - if (reload_spill_index[j] >= 0) - new_spill_reg_store[reload_spill_index[j]] = store_insn; - } - - /* Now write all the insns we made for reloads in the order expected by - the allocation functions. Prior to the insn being reloaded, we write - the following reloads: - - RELOAD_FOR_OTHER_ADDRESS reloads for input addresses. - - RELOAD_OTHER reloads. - - For each operand, any RELOAD_FOR_INPUT_ADDRESS reloads followed by - the RELOAD_FOR_INPUT reload for the operand. - - RELOAD_FOR_OPERAND_ADDRESS reloads. - - After the insn being reloaded, we write the following: - - For each operand, any RELOAD_FOR_OUTPUT_ADDRESS reload followed by - the RELOAD_FOR_OUTPUT reload for that operand. */ - - emit_insns_before (other_input_address_reload_insns, before_insn); - emit_insns_before (other_input_reload_insns, before_insn); - - for (j = 0; j < reload_n_operands; j++) - { - emit_insns_before (input_address_reload_insns[j], before_insn); - emit_insns_before (input_reload_insns[j], before_insn); - } - - emit_insns_before (operand_reload_insns, before_insn); - - for (j = 0; j < reload_n_operands; j++) - { - emit_insns_before (output_address_reload_insns[j], following_insn); - emit_insns_before (output_reload_insns[j], following_insn); - } - - /* Move death notes from INSN - to output-operand-address and output reload insns. */ -#ifdef PRESERVE_DEATH_INFO_REGNO_P - { - rtx insn1; - /* Loop over those insns, last ones first. */ - for (insn1 = PREV_INSN (following_insn); insn1 != insn; - insn1 = PREV_INSN (insn1)) - if (GET_CODE (insn1) == INSN && GET_CODE (PATTERN (insn1)) == SET) - { - rtx source = SET_SRC (PATTERN (insn1)); - rtx dest = SET_DEST (PATTERN (insn1)); - - /* The note we will examine next. */ - rtx reg_notes = REG_NOTES (insn); - /* The place that pointed to this note. */ - rtx *prev_reg_note = ®_NOTES (insn); - - /* If the note is for something used in the source of this - reload insn, or in the output address, move the note. */ - while (reg_notes) - { - rtx next_reg_notes = XEXP (reg_notes, 1); - if (REG_NOTE_KIND (reg_notes) == REG_DEAD - && GET_CODE (XEXP (reg_notes, 0)) == REG - && ((GET_CODE (dest) != REG - && reg_overlap_mentioned_for_reload_p (XEXP (reg_notes, 0), - dest)) - || reg_overlap_mentioned_for_reload_p (XEXP (reg_notes, 0), - source))) - { - *prev_reg_note = next_reg_notes; - XEXP (reg_notes, 1) = REG_NOTES (insn1); - REG_NOTES (insn1) = reg_notes; - } - else - prev_reg_note = &XEXP (reg_notes, 1); - - reg_notes = next_reg_notes; - } - } - } -#endif - - /* For all the spill regs newly reloaded in this instruction, - record what they were reloaded from, so subsequent instructions - can inherit the reloads. - - Update spill_reg_store for the reloads of this insn. - Copy the elements that were updated in the loop above. */ - - for (j = 0; j < n_reloads; j++) - { - register int r = reload_order[j]; - register int i = reload_spill_index[r]; - - /* I is nonneg if this reload used one of the spill regs. - If reload_reg_rtx[r] is 0, this is an optional reload - that we opted to ignore. - - Also ignore reloads that don't reach the end of the insn, - since we will eventually see the one that does. */ - - if (i >= 0 && reload_reg_rtx[r] != 0 - && reload_reg_reaches_end_p (spill_regs[i], reload_opnum[r], - reload_when_needed[r])) - { - /* First, clear out memory of what used to be in this spill reg. - If consecutive registers are used, clear them all. */ - int nr - = HARD_REGNO_NREGS (spill_regs[i], GET_MODE (reload_reg_rtx[r])); - int k; - - for (k = 0; k < nr; k++) - { - reg_reloaded_contents[spill_reg_order[spill_regs[i] + k]] = -1; - reg_reloaded_insn[spill_reg_order[spill_regs[i] + k]] = 0; - } - - /* Maybe the spill reg contains a copy of reload_out. */ - if (reload_out[r] != 0 && GET_CODE (reload_out[r]) == REG) - { - register int nregno = REGNO (reload_out[r]); - int nnr = (nregno >= FIRST_PSEUDO_REGISTER ? 1 - : HARD_REGNO_NREGS (nregno, - GET_MODE (reload_reg_rtx[r]))); - - spill_reg_store[i] = new_spill_reg_store[i]; - reg_last_reload_reg[nregno] = reload_reg_rtx[r]; - - /* If NREGNO is a hard register, it may occupy more than - one register. If it does, say what is in the - rest of the registers assuming that both registers - agree on how many words the object takes. If not, - invalidate the subsequent registers. */ - - if (nregno < FIRST_PSEUDO_REGISTER) - for (k = 1; k < nnr; k++) - reg_last_reload_reg[nregno + k] - = (nr == nnr ? gen_rtx (REG, word_mode, - REGNO (reload_reg_rtx[r]) + k) - : 0); - - /* Now do the inverse operation. */ - for (k = 0; k < nr; k++) - { - reg_reloaded_contents[spill_reg_order[spill_regs[i] + k]] - = (nregno >= FIRST_PSEUDO_REGISTER || nr != nnr ? nregno - : nregno + k); - reg_reloaded_insn[spill_reg_order[spill_regs[i] + k]] = insn; - } - } - - /* Maybe the spill reg contains a copy of reload_in. Only do - something if there will not be an output reload for - the register being reloaded. */ - else if (reload_out[r] == 0 - && reload_in[r] != 0 - && ((GET_CODE (reload_in[r]) == REG - && ! reg_has_output_reload[REGNO (reload_in[r])] - || (GET_CODE (reload_in_reg[r]) == REG - && ! reg_has_output_reload[REGNO (reload_in_reg[r])])))) - { - register int nregno; - int nnr; - - if (GET_CODE (reload_in[r]) == REG) - nregno = REGNO (reload_in[r]); - else - nregno = REGNO (reload_in_reg[r]); - - nnr = (nregno >= FIRST_PSEUDO_REGISTER ? 1 - : HARD_REGNO_NREGS (nregno, - GET_MODE (reload_reg_rtx[r]))); - - reg_last_reload_reg[nregno] = reload_reg_rtx[r]; - - if (nregno < FIRST_PSEUDO_REGISTER) - for (k = 1; k < nnr; k++) - reg_last_reload_reg[nregno + k] - = (nr == nnr ? gen_rtx (REG, word_mode, - REGNO (reload_reg_rtx[r]) + k) - : 0); - - /* Unless we inherited this reload, show we haven't - recently done a store. */ - if (! reload_inherited[r]) - spill_reg_store[i] = 0; - - for (k = 0; k < nr; k++) - { - reg_reloaded_contents[spill_reg_order[spill_regs[i] + k]] - = (nregno >= FIRST_PSEUDO_REGISTER || nr != nnr ? nregno - : nregno + k); - reg_reloaded_insn[spill_reg_order[spill_regs[i] + k]] - = insn; - } - } - } - - /* The following if-statement was #if 0'd in 1.34 (or before...). - It's reenabled in 1.35 because supposedly nothing else - deals with this problem. */ - - /* If a register gets output-reloaded from a non-spill register, - that invalidates any previous reloaded copy of it. - But forget_old_reloads_1 won't get to see it, because - it thinks only about the original insn. So invalidate it here. */ - if (i < 0 && reload_out[r] != 0 && GET_CODE (reload_out[r]) == REG) - { - register int nregno = REGNO (reload_out[r]); - reg_last_reload_reg[nregno] = 0; - } - } -} - -/* Emit code to perform an input reload of IN to RELOADREG. IN is from - operand OPNUM with reload type TYPE. - - Returns first insn emitted. */ - -rtx -gen_input_reload (reloadreg, in, opnum, type) - rtx reloadreg; - rtx in; - int opnum; - enum reload_type type; -{ - rtx last = get_last_insn (); - - /* How to do this reload can get quite tricky. Normally, we are being - asked to reload a simple operand, such as a MEM, a constant, or a pseudo - register that didn't get a hard register. In that case we can just - call emit_move_insn. - - We can also be asked to reload a PLUS that adds either two registers, or - a register and a constant or MEM, or a MEM and a constant. This can - occur during frame pointer elimination and while reloading addresses. - This case is handled by trying to emit a single insn - to perform the add. If it is not valid, we use a two insn sequence. - - Finally, we could be called to handle an 'o' constraint by putting - an address into a register. In that case, we first try to do this - with a named pattern of "reload_load_address". If no such pattern - exists, we just emit a SET insn and hope for the best (it will normally - be valid on machines that use 'o'). - - This entire process is made complex because reload will never - process the insns we generate here and so we must ensure that - they will fit their constraints and also by the fact that parts of - IN might be being reloaded separately and replaced with spill registers. - Because of this, we are, in some sense, just guessing the right approach - here. The one listed above seems to work. - - ??? At some point, this whole thing needs to be rethought. */ - - if (GET_CODE (in) == PLUS - && ((GET_CODE (XEXP (in, 0)) == REG - && (GET_CODE (XEXP (in, 1)) == REG - || CONSTANT_P (XEXP (in, 1)) - || GET_CODE (XEXP (in, 1)) == MEM)) - || (GET_CODE (XEXP (in, 0)) == MEM - && CONSTANT_P (XEXP (in, 1))))) - { - /* We need to compute the sum of what is either a register and a - constant, a register and memory, a hard register and a pseudo - register, or memory and a constant and put it into the reload - register. The best possible way of doing this is if the machine - has a three-operand ADD insn that accepts the required operands. - - The simplest approach is to try to generate such an insn and see if it - is recognized and matches its constraints. If so, it can be used. - - It might be better not to actually emit the insn unless it is valid, - but we need to pass the insn as an operand to `recog' and - `insn_extract' and it is simpler to emit and then delete the insn if - not valid than to dummy things up. */ - - rtx op0, op1, tem, insn; - int code; - - op0 = find_replacement (&XEXP (in, 0)); - op1 = find_replacement (&XEXP (in, 1)); - - /* Since constraint checking is strict, commutativity won't be - checked, so we need to do that here to avoid spurious failure - if the add instruction is two-address and the second operand - of the add is the same as the reload reg, which is frequently - the case. If the insn would be A = B + A, rearrange it so - it will be A = A + B as constrain_operands expects. */ - - if (GET_CODE (XEXP (in, 1)) == REG - && REGNO (reloadreg) == REGNO (XEXP (in, 1))) - tem = op0, op0 = op1, op1 = tem; - - if (op0 != XEXP (in, 0) || op1 != XEXP (in, 1)) - in = gen_rtx (PLUS, GET_MODE (in), op0, op1); - - insn = emit_insn (gen_rtx (SET, VOIDmode, reloadreg, in)); - code = recog_memoized (insn); - - if (code >= 0) - { - insn_extract (insn); - /* We want constrain operands to treat this insn strictly in - its validity determination, i.e., the way it would after reload - has completed. */ - if (constrain_operands (code, 1)) - return insn; - } - - delete_insns_since (last); - - /* If that failed, we must use a conservative two-insn sequence. - use move to copy constant, MEM, or pseudo register to the reload - register since "move" will be able to handle an arbitrary operand, - unlike add which can't, in general. Then add the registers. - - If there is another way to do this for a specific machine, a - DEFINE_PEEPHOLE should be specified that recognizes the sequence - we emit below. */ - - if (CONSTANT_P (op1) || GET_CODE (op1) == MEM - || (GET_CODE (op1) == REG - && REGNO (op1) >= FIRST_PSEUDO_REGISTER)) - tem = op0, op0 = op1, op1 = tem; - - emit_insn (gen_move_insn (reloadreg, op0)); - - /* If OP0 and OP1 are the same, we can use RELOADREG for OP1. - This fixes a problem on the 32K where the stack pointer cannot - be used as an operand of an add insn. */ - - if (rtx_equal_p (op0, op1)) - op1 = reloadreg; - - emit_insn (gen_add2_insn (reloadreg, op1)); - } - -#ifdef SECONDARY_MEMORY_NEEDED - /* If we need a memory location to do the move, do it that way. */ - else if (GET_CODE (in) == REG && REGNO (in) < FIRST_PSEUDO_REGISTER - && SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (REGNO (in)), - REGNO_REG_CLASS (REGNO (reloadreg)), - GET_MODE (reloadreg))) - { - /* Get the memory to use and rewrite both registers to its mode. */ - rtx loc = get_secondary_mem (in, GET_MODE (reloadreg), opnum, type); - - if (GET_MODE (loc) != GET_MODE (reloadreg)) - reloadreg = gen_rtx (REG, GET_MODE (loc), REGNO (reloadreg)); - - if (GET_MODE (loc) != GET_MODE (in)) - in = gen_rtx (REG, GET_MODE (loc), REGNO (in)); - - emit_insn (gen_move_insn (loc, in)); - emit_insn (gen_move_insn (reloadreg, loc)); - } -#endif - - /* If IN is a simple operand, use gen_move_insn. */ - else if (GET_RTX_CLASS (GET_CODE (in)) == 'o' || GET_CODE (in) == SUBREG) - emit_insn (gen_move_insn (reloadreg, in)); - -#ifdef HAVE_reload_load_address - else if (HAVE_reload_load_address) - emit_insn (gen_reload_load_address (reloadreg, in)); -#endif - - /* Otherwise, just write (set REGLOADREG IN) and hope for the best. */ - else - emit_insn (gen_rtx (SET, VOIDmode, reloadreg, in)); - - /* Return the first insn emitted. - We can not just return get_last_insn, because there may have - been multiple instructions emitted. Also note that gen_move_insn may - emit more than one insn itself, so we can not assume that there is one - insn emitted per emit_insn_before call. */ - - return last ? NEXT_INSN (last) : get_insns (); -} - -/* Delete a previously made output-reload - whose result we now believe is not needed. - First we double-check. - - INSN is the insn now being processed. - OUTPUT_RELOAD_INSN is the insn of the output reload. - J is the reload-number for this insn. */ - -static void -delete_output_reload (insn, j, output_reload_insn) - rtx insn; - int j; - rtx output_reload_insn; -{ - register rtx i1; - - /* Get the raw pseudo-register referred to. */ - - rtx reg = reload_in[j]; - while (GET_CODE (reg) == SUBREG) - reg = SUBREG_REG (reg); - - /* If the pseudo-reg we are reloading is no longer referenced - anywhere between the store into it and here, - and no jumps or labels intervene, then the value can get - here through the reload reg alone. - Otherwise, give up--return. */ - for (i1 = NEXT_INSN (output_reload_insn); - i1 != insn; i1 = NEXT_INSN (i1)) - { - if (GET_CODE (i1) == CODE_LABEL || GET_CODE (i1) == JUMP_INSN) - return; - if ((GET_CODE (i1) == INSN || GET_CODE (i1) == CALL_INSN) - && reg_mentioned_p (reg, PATTERN (i1))) - return; - } - - if (cannot_omit_stores[REGNO (reg)]) - return; - - /* If this insn will store in the pseudo again, - the previous store can be removed. */ - if (reload_out[j] == reload_in[j]) - delete_insn (output_reload_insn); - - /* See if the pseudo reg has been completely replaced - with reload regs. If so, delete the store insn - and forget we had a stack slot for the pseudo. */ - else if (reg_n_deaths[REGNO (reg)] == 1 - && reg_basic_block[REGNO (reg)] >= 0 - && find_regno_note (insn, REG_DEAD, REGNO (reg))) - { - rtx i2; - - /* We know that it was used only between here - and the beginning of the current basic block. - (We also know that the last use before INSN was - the output reload we are thinking of deleting, but never mind that.) - Search that range; see if any ref remains. */ - for (i2 = PREV_INSN (insn); i2; i2 = PREV_INSN (i2)) - { - rtx set = single_set (i2); - - /* Uses which just store in the pseudo don't count, - since if they are the only uses, they are dead. */ - if (set != 0 && SET_DEST (set) == reg) - continue; - if (GET_CODE (i2) == CODE_LABEL - || GET_CODE (i2) == JUMP_INSN) - break; - if ((GET_CODE (i2) == INSN || GET_CODE (i2) == CALL_INSN) - && reg_mentioned_p (reg, PATTERN (i2))) - /* Some other ref remains; - we can't do anything. */ - return; - } - - /* Delete the now-dead stores into this pseudo. */ - for (i2 = PREV_INSN (insn); i2; i2 = PREV_INSN (i2)) - { - rtx set = single_set (i2); - - if (set != 0 && SET_DEST (set) == reg) - delete_insn (i2); - if (GET_CODE (i2) == CODE_LABEL - || GET_CODE (i2) == JUMP_INSN) - break; - } - - /* For the debugging info, - say the pseudo lives in this reload reg. */ - reg_renumber[REGNO (reg)] = REGNO (reload_reg_rtx[j]); - alter_reg (REGNO (reg), -1); - } -} - -/* Output reload-insns to reload VALUE into RELOADREG. - VALUE is an autoincrement or autodecrement RTX whose operand - is a register or memory location; - so reloading involves incrementing that location. - - INC_AMOUNT is the number to increment or decrement by (always positive). - This cannot be deduced from VALUE. */ - -static void -inc_for_reload (reloadreg, value, inc_amount) - rtx reloadreg; - rtx value; - int inc_amount; -{ - /* REG or MEM to be copied and incremented. */ - rtx incloc = XEXP (value, 0); - /* Nonzero if increment after copying. */ - int post = (GET_CODE (value) == POST_DEC || GET_CODE (value) == POST_INC); - rtx last; - rtx inc; - rtx add_insn; - int code; - - /* No hard register is equivalent to this register after - inc/dec operation. If REG_LAST_RELOAD_REG were non-zero, - we could inc/dec that register as well (maybe even using it for - the source), but I'm not sure it's worth worrying about. */ - if (GET_CODE (incloc) == REG) - reg_last_reload_reg[REGNO (incloc)] = 0; - - if (GET_CODE (value) == PRE_DEC || GET_CODE (value) == POST_DEC) - inc_amount = - inc_amount; - - inc = GEN_INT (inc_amount); - - /* If this is post-increment, first copy the location to the reload reg. */ - if (post) - emit_insn (gen_move_insn (reloadreg, incloc)); - - /* See if we can directly increment INCLOC. Use a method similar to that - in gen_input_reload. */ - - last = get_last_insn (); - add_insn = emit_insn (gen_rtx (SET, VOIDmode, incloc, - gen_rtx (PLUS, GET_MODE (incloc), - incloc, inc))); - - code = recog_memoized (add_insn); - if (code >= 0) - { - insn_extract (add_insn); - if (constrain_operands (code, 1)) - { - /* If this is a pre-increment and we have incremented the value - where it lives, copy the incremented value to RELOADREG to - be used as an address. */ - - if (! post) - emit_insn (gen_move_insn (reloadreg, incloc)); - - return; - } - } - - delete_insns_since (last); - - /* If couldn't do the increment directly, must increment in RELOADREG. - The way we do this depends on whether this is pre- or post-increment. - For pre-increment, copy INCLOC to the reload register, increment it - there, then save back. */ - - if (! post) - { - emit_insn (gen_move_insn (reloadreg, incloc)); - emit_insn (gen_add2_insn (reloadreg, inc)); - emit_insn (gen_move_insn (incloc, reloadreg)); - } - else - { - /* Postincrement. - Because this might be a jump insn or a compare, and because RELOADREG - may not be available after the insn in an input reload, we must do - the incrementation before the insn being reloaded for. - - We have already copied INCLOC to RELOADREG. Increment the copy in - RELOADREG, save that back, then decrement RELOADREG so it has - the original value. */ - - emit_insn (gen_add2_insn (reloadreg, inc)); - emit_insn (gen_move_insn (incloc, reloadreg)); - emit_insn (gen_add2_insn (reloadreg, GEN_INT (-inc_amount))); - } - - return; -} - -/* Return 1 if we are certain that the constraint-string STRING allows - the hard register REG. Return 0 if we can't be sure of this. */ - -static int -constraint_accepts_reg_p (string, reg) - char *string; - rtx reg; -{ - int value = 0; - int regno = true_regnum (reg); - int c; - - /* Initialize for first alternative. */ - value = 0; - /* Check that each alternative contains `g' or `r'. */ - while (1) - switch (c = *string++) - { - case 0: - /* If an alternative lacks `g' or `r', we lose. */ - return value; - case ',': - /* If an alternative lacks `g' or `r', we lose. */ - if (value == 0) - return 0; - /* Initialize for next alternative. */ - value = 0; - break; - case 'g': - case 'r': - /* Any general reg wins for this alternative. */ - if (TEST_HARD_REG_BIT (reg_class_contents[(int) GENERAL_REGS], regno)) - value = 1; - break; - default: - /* Any reg in specified class wins for this alternative. */ - { - enum reg_class class = REG_CLASS_FROM_LETTER (c); - - if (TEST_HARD_REG_BIT (reg_class_contents[(int) class], regno)) - value = 1; - } - } -} - -/* Return the number of places FIND appears within X, but don't count - an occurrence if some SET_DEST is FIND. */ - -static int -count_occurrences (x, find) - register rtx x, find; -{ - register int i, j; - register enum rtx_code code; - register char *format_ptr; - int count; - - if (x == find) - return 1; - if (x == 0) - return 0; - - code = GET_CODE (x); - - switch (code) - { - case REG: - case QUEUED: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case CODE_LABEL: - case PC: - case CC0: - return 0; - - case SET: - if (SET_DEST (x) == find) - return count_occurrences (SET_SRC (x), find); - break; - } - - format_ptr = GET_RTX_FORMAT (code); - count = 0; - - for (i = 0; i < GET_RTX_LENGTH (code); i++) - { - switch (*format_ptr++) - { - case 'e': - count += count_occurrences (XEXP (x, i), find); - break; - - case 'E': - if (XVEC (x, i) != NULL) - { - for (j = 0; j < XVECLEN (x, i); j++) - count += count_occurrences (XVECEXP (x, i, j), find); - } - break; - } - } - return count; -} diff --git a/gnu/usr.bin/cc/common/sched.c b/gnu/usr.bin/cc/common/sched.c deleted file mode 100644 index 1797d1c736..0000000000 --- a/gnu/usr.bin/cc/common/sched.c +++ /dev/null @@ -1,4674 +0,0 @@ -/* Instruction scheduling pass. - Copyright (C) 1992 Free Software Foundation, Inc. - Contributed by Michael Tiemann (tiemann@cygnus.com) - Enhanced by, and currently maintained by, Jim Wilson (wilson@cygnus.com) - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Instruction scheduling pass. - - This pass implements list scheduling within basic blocks. It is - run after flow analysis, but before register allocation. The - scheduler works as follows: - - We compute insn priorities based on data dependencies. Flow - analysis only creates a fraction of the data-dependencies we must - observe: namely, only those dependencies which the combiner can be - expected to use. For this pass, we must therefore create the - remaining dependencies we need to observe: register dependencies, - memory dependencies, dependencies to keep function calls in order, - and the dependence between a conditional branch and the setting of - condition codes are all dealt with here. - - The scheduler first traverses the data flow graph, starting with - the last instruction, and proceeding to the first, assigning - values to insn_priority as it goes. This sorts the instructions - topologically by data dependence. - - Once priorities have been established, we order the insns using - list scheduling. This works as follows: starting with a list of - all the ready insns, and sorted according to priority number, we - schedule the insn from the end of the list by placing its - predecessors in the list according to their priority order. We - consider this insn scheduled by setting the pointer to the "end" of - the list to point to the previous insn. When an insn has no - predecessors, we either queue it until sufficient time has elapsed - or add it to the ready list. As the instructions are scheduled or - when stalls are introduced, the queue advances and dumps insns into - the ready list. When all insns down to the lowest priority have - been scheduled, the critical path of the basic block has been made - as short as possible. The remaining insns are then scheduled in - remaining slots. - - Function unit conflicts are resolved during reverse list scheduling - by tracking the time when each insn is committed to the schedule - and from that, the time the function units it uses must be free. - As insns on the ready list are considered for scheduling, those - that would result in a blockage of the already committed insns are - queued until no blockage will result. Among the remaining insns on - the ready list to be considered, the first one with the largest - potential for causing a subsequent blockage is chosen. - - The following list shows the order in which we want to break ties - among insns in the ready list: - - 1. choose insn with lowest conflict cost, ties broken by - 2. choose insn with the longest path to end of bb, ties broken by - 3. choose insn that kills the most registers, ties broken by - 4. choose insn that conflicts with the most ready insns, or finally - 5. choose insn with lowest UID. - - Memory references complicate matters. Only if we can be certain - that memory references are not part of the data dependency graph - (via true, anti, or output dependence), can we move operations past - memory references. To first approximation, reads can be done - independently, while writes introduce dependencies. Better - approximations will yield fewer dependencies. - - Dependencies set up by memory references are treated in exactly the - same way as other dependencies, by using LOG_LINKS. - - Having optimized the critical path, we may have also unduly - extended the lifetimes of some registers. If an operation requires - that constants be loaded into registers, it is certainly desirable - to load those constants as early as necessary, but no earlier. - I.e., it will not do to load up a bunch of registers at the - beginning of a basic block only to use them at the end, if they - could be loaded later, since this may result in excessive register - utilization. - - Note that since branches are never in basic blocks, but only end - basic blocks, this pass will not do any branch scheduling. But - that is ok, since we can use GNU's delayed branch scheduling - pass to take care of this case. - - Also note that no further optimizations based on algebraic identities - are performed, so this pass would be a good one to perform instruction - splitting, such as breaking up a multiply instruction into shifts - and adds where that is profitable. - - Given the memory aliasing analysis that this pass should perform, - it should be possible to remove redundant stores to memory, and to - load values from registers instead of hitting memory. - - This pass must update information that subsequent passes expect to be - correct. Namely: reg_n_refs, reg_n_sets, reg_n_deaths, - reg_n_calls_crossed, and reg_live_length. Also, basic_block_head, - basic_block_end. - - The information in the line number notes is carefully retained by this - pass. All other NOTE insns are grouped in their same relative order at - the beginning of basic blocks that have been scheduled. */ - -#include -#include "config.h" -#include "rtl.h" -#include "basic-block.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "flags.h" -#include "insn-config.h" -#include "insn-attr.h" - -#ifdef INSN_SCHEDULING -/* Arrays set up by scheduling for the same respective purposes as - similar-named arrays set up by flow analysis. We work with these - arrays during the scheduling pass so we can compare values against - unscheduled code. - - Values of these arrays are copied at the end of this pass into the - arrays set up by flow analysis. */ -static short *sched_reg_n_deaths; -static int *sched_reg_n_calls_crossed; -static int *sched_reg_live_length; - -/* Element N is the next insn that sets (hard or pseudo) register - N within the current basic block; or zero, if there is no - such insn. Needed for new registers which may be introduced - by splitting insns. */ -static rtx *reg_last_uses; -static rtx *reg_last_sets; - -/* Vector indexed by INSN_UID giving the original ordering of the insns. */ -static int *insn_luid; -#define INSN_LUID(INSN) (insn_luid[INSN_UID (INSN)]) - -/* Vector indexed by INSN_UID giving each instruction a priority. */ -static int *insn_priority; -#define INSN_PRIORITY(INSN) (insn_priority[INSN_UID (INSN)]) - -static short *insn_costs; -#define INSN_COST(INSN) insn_costs[INSN_UID (INSN)] - -/* Vector indexed by INSN_UID giving an encoding of the function units - used. */ -static short *insn_units; -#define INSN_UNIT(INSN) insn_units[INSN_UID (INSN)] - -/* Vector indexed by INSN_UID giving an encoding of the blockage range - function. The unit and the range are encoded. */ -static unsigned int *insn_blockage; -#define INSN_BLOCKAGE(INSN) insn_blockage[INSN_UID (INSN)] -#define UNIT_BITS 5 -#define BLOCKAGE_MASK ((1 << BLOCKAGE_BITS) - 1) -#define ENCODE_BLOCKAGE(U,R) \ - ((((U) << UNIT_BITS) << BLOCKAGE_BITS \ - | MIN_BLOCKAGE_COST (R)) << BLOCKAGE_BITS \ - | MAX_BLOCKAGE_COST (R)) -#define UNIT_BLOCKED(B) ((B) >> (2 * BLOCKAGE_BITS)) -#define BLOCKAGE_RANGE(B) \ - (((((B) >> BLOCKAGE_BITS) & BLOCKAGE_MASK) << (HOST_BITS_PER_INT / 2)) \ - | (B) & BLOCKAGE_MASK) - -/* Encodings of the `_unit_blockage_range' function. */ -#define MIN_BLOCKAGE_COST(R) ((R) >> (HOST_BITS_PER_INT / 2)) -#define MAX_BLOCKAGE_COST(R) ((R) & ((1 << (HOST_BITS_PER_INT / 2)) - 1)) - -#define DONE_PRIORITY -1 -#define MAX_PRIORITY 0x7fffffff -#define TAIL_PRIORITY 0x7ffffffe -#define LAUNCH_PRIORITY 0x7f000001 -#define DONE_PRIORITY_P(INSN) (INSN_PRIORITY (INSN) < 0) -#define LOW_PRIORITY_P(INSN) ((INSN_PRIORITY (INSN) & 0x7f000000) == 0) - -/* Vector indexed by INSN_UID giving number of insns referring to this insn. */ -static int *insn_ref_count; -#define INSN_REF_COUNT(INSN) (insn_ref_count[INSN_UID (INSN)]) - -/* Vector indexed by INSN_UID giving line-number note in effect for each - insn. For line-number notes, this indicates whether the note may be - reused. */ -static rtx *line_note; -#define LINE_NOTE(INSN) (line_note[INSN_UID (INSN)]) - -/* Vector indexed by basic block number giving the starting line-number - for each basic block. */ -static rtx *line_note_head; - -/* List of important notes we must keep around. This is a pointer to the - last element in the list. */ -static rtx note_list; - -/* Regsets telling whether a given register is live or dead before the last - scheduled insn. Must scan the instructions once before scheduling to - determine what registers are live or dead at the end of the block. */ -static regset bb_dead_regs; -static regset bb_live_regs; - -/* Regset telling whether a given register is live after the insn currently - being scheduled. Before processing an insn, this is equal to bb_live_regs - above. This is used so that we can find registers that are newly born/dead - after processing an insn. */ -static regset old_live_regs; - -/* The chain of REG_DEAD notes. REG_DEAD notes are removed from all insns - during the initial scan and reused later. If there are not exactly as - many REG_DEAD notes in the post scheduled code as there were in the - prescheduled code then we trigger an abort because this indicates a bug. */ -static rtx dead_notes; - -/* Queues, etc. */ - -/* An instruction is ready to be scheduled when all insns following it - have already been scheduled. It is important to ensure that all - insns which use its result will not be executed until its result - has been computed. An insn is maintained in one of four structures: - - (P) the "Pending" set of insns which cannot be scheduled until - their dependencies have been satisfied. - (Q) the "Queued" set of insns that can be scheduled when sufficient - time has passed. - (R) the "Ready" list of unscheduled, uncommitted insns. - (S) the "Scheduled" list of insns. - - Initially, all insns are either "Pending" or "Ready" depending on - whether their dependencies are satisfied. - - Insns move from the "Ready" list to the "Scheduled" list as they - are committed to the schedule. As this occurs, the insns in the - "Pending" list have their dependencies satisfied and move to either - the "Ready" list or the "Queued" set depending on whether - sufficient time has passed to make them ready. As time passes, - insns move from the "Queued" set to the "Ready" list. Insns may - move from the "Ready" list to the "Queued" set if they are blocked - due to a function unit conflict. - - The "Pending" list (P) are the insns in the LOG_LINKS of the unscheduled - insns, i.e., those that are ready, queued, and pending. - The "Queued" set (Q) is implemented by the variable `insn_queue'. - The "Ready" list (R) is implemented by the variables `ready' and - `n_ready'. - The "Scheduled" list (S) is the new insn chain built by this pass. - - The transition (R->S) is implemented in the scheduling loop in - `schedule_block' when the best insn to schedule is chosen. - The transition (R->Q) is implemented in `schedule_select' when an - insn is found to to have a function unit conflict with the already - committed insns. - The transitions (P->R and P->Q) are implemented in `schedule_insn' as - insns move from the ready list to the scheduled list. - The transition (Q->R) is implemented at the top of the scheduling - loop in `schedule_block' as time passes or stalls are introduced. */ - -/* Implement a circular buffer to delay instructions until sufficient - time has passed. INSN_QUEUE_SIZE is a power of two larger than - MAX_BLOCKAGE and MAX_READY_COST computed by genattr.c. This is the - longest time an isnsn may be queued. */ -static rtx insn_queue[INSN_QUEUE_SIZE]; -static int q_ptr = 0; -static int q_size = 0; -#define NEXT_Q(X) (((X)+1) & (INSN_QUEUE_SIZE-1)) -#define NEXT_Q_AFTER(X,C) (((X)+C) & (INSN_QUEUE_SIZE-1)) - -/* Vector indexed by INSN_UID giving the minimum clock tick at which - the insn becomes ready. This is used to note timing constraints for - insns in the pending list. */ -static int *insn_tick; -#define INSN_TICK(INSN) (insn_tick[INSN_UID (INSN)]) - -/* Forward declarations. */ -static void sched_analyze_2 (); -static void schedule_block (); - -/* Main entry point of this file. */ -void schedule_insns (); -#endif /* INSN_SCHEDULING */ - -#define SIZE_FOR_MODE(X) (GET_MODE_SIZE (GET_MODE (X))) - -/* Vector indexed by N giving the initial (unchanging) value known - for pseudo-register N. */ -static rtx *reg_known_value; - -/* Vector recording for each reg_known_value whether it is due to a - REG_EQUIV note. Future passes (viz., reload) may replace the - pseudo with the equivalent expression and so we account for the - dependences that would be introduced if that happens. */ -/* ??? This is a problem only on the Convex. The REG_EQUIV notes created in - assign_parms mention the arg pointer, and there are explicit insns in the - RTL that modify the arg pointer. Thus we must ensure that such insns don't - get scheduled across each other because that would invalidate the REG_EQUIV - notes. One could argue that the REG_EQUIV notes are wrong, but solving - the problem in the scheduler will likely give better code, so we do it - here. */ -static char *reg_known_equiv_p; - -/* Indicates number of valid entries in reg_known_value. */ -static int reg_known_value_size; - -static rtx -canon_rtx (x) - rtx x; -{ - if (GET_CODE (x) == REG && REGNO (x) >= FIRST_PSEUDO_REGISTER - && REGNO (x) <= reg_known_value_size) - return reg_known_value[REGNO (x)]; - else if (GET_CODE (x) == PLUS) - { - rtx x0 = canon_rtx (XEXP (x, 0)); - rtx x1 = canon_rtx (XEXP (x, 1)); - - if (x0 != XEXP (x, 0) || x1 != XEXP (x, 1)) - { - /* We can tolerate LO_SUMs being offset here; these - rtl are used for nothing other than comparisons. */ - if (GET_CODE (x0) == CONST_INT) - return plus_constant_for_output (x1, INTVAL (x0)); - else if (GET_CODE (x1) == CONST_INT) - return plus_constant_for_output (x0, INTVAL (x1)); - return gen_rtx (PLUS, GET_MODE (x), x0, x1); - } - } - return x; -} - -/* Set up all info needed to perform alias analysis on memory references. */ - -void -init_alias_analysis () -{ - int maxreg = max_reg_num (); - rtx insn; - rtx note; - rtx set; - - reg_known_value_size = maxreg; - - reg_known_value - = (rtx *) oballoc ((maxreg-FIRST_PSEUDO_REGISTER) * sizeof (rtx)) - - FIRST_PSEUDO_REGISTER; - bzero (reg_known_value+FIRST_PSEUDO_REGISTER, - (maxreg-FIRST_PSEUDO_REGISTER) * sizeof (rtx)); - - reg_known_equiv_p - = (char *) oballoc ((maxreg-FIRST_PSEUDO_REGISTER) * sizeof (char)) - - FIRST_PSEUDO_REGISTER; - bzero (reg_known_equiv_p+FIRST_PSEUDO_REGISTER, - (maxreg-FIRST_PSEUDO_REGISTER) * sizeof (char)); - - /* Fill in the entries with known constant values. */ - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if ((set = single_set (insn)) != 0 - && GET_CODE (SET_DEST (set)) == REG - && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER - && (((note = find_reg_note (insn, REG_EQUAL, 0)) != 0 - && reg_n_sets[REGNO (SET_DEST (set))] == 1) - || (note = find_reg_note (insn, REG_EQUIV, NULL_RTX)) != 0) - && GET_CODE (XEXP (note, 0)) != EXPR_LIST) - { - int regno = REGNO (SET_DEST (set)); - reg_known_value[regno] = XEXP (note, 0); - reg_known_equiv_p[regno] = REG_NOTE_KIND (note) == REG_EQUIV; - } - - /* Fill in the remaining entries. */ - while (--maxreg >= FIRST_PSEUDO_REGISTER) - if (reg_known_value[maxreg] == 0) - reg_known_value[maxreg] = regno_reg_rtx[maxreg]; -} - -/* Return 1 if X and Y are identical-looking rtx's. - - We use the data in reg_known_value above to see if two registers with - different numbers are, in fact, equivalent. */ - -static int -rtx_equal_for_memref_p (x, y) - rtx x, y; -{ - register int i; - register int j; - register enum rtx_code code; - register char *fmt; - - if (x == 0 && y == 0) - return 1; - if (x == 0 || y == 0) - return 0; - x = canon_rtx (x); - y = canon_rtx (y); - - if (x == y) - return 1; - - code = GET_CODE (x); - /* Rtx's of different codes cannot be equal. */ - if (code != GET_CODE (y)) - return 0; - - /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. - (REG:SI x) and (REG:HI x) are NOT equivalent. */ - - if (GET_MODE (x) != GET_MODE (y)) - return 0; - - /* REG, LABEL_REF, and SYMBOL_REF can be compared nonrecursively. */ - - if (code == REG) - return REGNO (x) == REGNO (y); - if (code == LABEL_REF) - return XEXP (x, 0) == XEXP (y, 0); - if (code == SYMBOL_REF) - return XSTR (x, 0) == XSTR (y, 0); - - /* Compare the elements. If any pair of corresponding elements - fail to match, return 0 for the whole things. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - switch (fmt[i]) - { - case 'w': - if (XWINT (x, i) != XWINT (y, i)) - return 0; - break; - - case 'n': - case 'i': - if (XINT (x, i) != XINT (y, i)) - return 0; - break; - - case 'V': - case 'E': - /* Two vectors must have the same length. */ - if (XVECLEN (x, i) != XVECLEN (y, i)) - return 0; - - /* And the corresponding elements must match. */ - for (j = 0; j < XVECLEN (x, i); j++) - if (rtx_equal_for_memref_p (XVECEXP (x, i, j), XVECEXP (y, i, j)) == 0) - return 0; - break; - - case 'e': - if (rtx_equal_for_memref_p (XEXP (x, i), XEXP (y, i)) == 0) - return 0; - break; - - case 'S': - case 's': - if (strcmp (XSTR (x, i), XSTR (y, i))) - return 0; - break; - - case 'u': - /* These are just backpointers, so they don't matter. */ - break; - - case '0': - break; - - /* It is believed that rtx's at this level will never - contain anything but integers and other rtx's, - except for within LABEL_REFs and SYMBOL_REFs. */ - default: - abort (); - } - } - return 1; -} - -/* Given an rtx X, find a SYMBOL_REF or LABEL_REF within - X and return it, or return 0 if none found. */ - -static rtx -find_symbolic_term (x) - rtx x; -{ - register int i; - register enum rtx_code code; - register char *fmt; - - code = GET_CODE (x); - if (code == SYMBOL_REF || code == LABEL_REF) - return x; - if (GET_RTX_CLASS (code) == 'o') - return 0; - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - rtx t; - - if (fmt[i] == 'e') - { - t = find_symbolic_term (XEXP (x, i)); - if (t != 0) - return t; - } - else if (fmt[i] == 'E') - break; - } - return 0; -} - -/* Return nonzero if X and Y (memory addresses) could reference the - same location in memory. C is an offset accumulator. When - C is nonzero, we are testing aliases between X and Y + C. - XSIZE is the size in bytes of the X reference, - similarly YSIZE is the size in bytes for Y. - - If XSIZE or YSIZE is zero, we do not know the amount of memory being - referenced (the reference was BLKmode), so make the most pessimistic - assumptions. - - We recognize the following cases of non-conflicting memory: - - (1) addresses involving the frame pointer cannot conflict - with addresses involving static variables. - (2) static variables with different addresses cannot conflict. - - Nice to notice that varying addresses cannot conflict with fp if no - local variables had their addresses taken, but that's too hard now. */ - -/* ??? In Fortran, references to a array parameter can never conflict with - another array parameter. */ - -static int -memrefs_conflict_p (xsize, x, ysize, y, c) - rtx x, y; - int xsize, ysize; - HOST_WIDE_INT c; -{ - if (GET_CODE (x) == HIGH) - x = XEXP (x, 0); - else if (GET_CODE (x) == LO_SUM) - x = XEXP (x, 1); - else - x = canon_rtx (x); - if (GET_CODE (y) == HIGH) - y = XEXP (y, 0); - else if (GET_CODE (y) == LO_SUM) - y = XEXP (y, 1); - else - y = canon_rtx (y); - - if (rtx_equal_for_memref_p (x, y)) - return (xsize == 0 || ysize == 0 || - (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0)); - - if (y == frame_pointer_rtx || y == stack_pointer_rtx) - { - rtx t = y; - int tsize = ysize; - y = x; ysize = xsize; - x = t; xsize = tsize; - } - - if (x == frame_pointer_rtx || x == stack_pointer_rtx) - { - rtx y1; - - if (CONSTANT_P (y)) - return 0; - - if (GET_CODE (y) == PLUS - && canon_rtx (XEXP (y, 0)) == x - && (y1 = canon_rtx (XEXP (y, 1))) - && GET_CODE (y1) == CONST_INT) - { - c += INTVAL (y1); - return (xsize == 0 || ysize == 0 - || (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0)); - } - - if (GET_CODE (y) == PLUS - && (y1 = canon_rtx (XEXP (y, 0))) - && CONSTANT_P (y1)) - return 0; - - return 1; - } - - if (GET_CODE (x) == PLUS) - { - /* The fact that X is canonicalized means that this - PLUS rtx is canonicalized. */ - rtx x0 = XEXP (x, 0); - rtx x1 = XEXP (x, 1); - - if (GET_CODE (y) == PLUS) - { - /* The fact that Y is canonicalized means that this - PLUS rtx is canonicalized. */ - rtx y0 = XEXP (y, 0); - rtx y1 = XEXP (y, 1); - - if (rtx_equal_for_memref_p (x1, y1)) - return memrefs_conflict_p (xsize, x0, ysize, y0, c); - if (rtx_equal_for_memref_p (x0, y0)) - return memrefs_conflict_p (xsize, x1, ysize, y1, c); - if (GET_CODE (x1) == CONST_INT) - if (GET_CODE (y1) == CONST_INT) - return memrefs_conflict_p (xsize, x0, ysize, y0, - c - INTVAL (x1) + INTVAL (y1)); - else - return memrefs_conflict_p (xsize, x0, ysize, y, c - INTVAL (x1)); - else if (GET_CODE (y1) == CONST_INT) - return memrefs_conflict_p (xsize, x, ysize, y0, c + INTVAL (y1)); - - /* Handle case where we cannot understand iteration operators, - but we notice that the base addresses are distinct objects. */ - x = find_symbolic_term (x); - if (x == 0) - return 1; - y = find_symbolic_term (y); - if (y == 0) - return 1; - return rtx_equal_for_memref_p (x, y); - } - else if (GET_CODE (x1) == CONST_INT) - return memrefs_conflict_p (xsize, x0, ysize, y, c - INTVAL (x1)); - } - else if (GET_CODE (y) == PLUS) - { - /* The fact that Y is canonicalized means that this - PLUS rtx is canonicalized. */ - rtx y0 = XEXP (y, 0); - rtx y1 = XEXP (y, 1); - - if (GET_CODE (y1) == CONST_INT) - return memrefs_conflict_p (xsize, x, ysize, y0, c + INTVAL (y1)); - else - return 1; - } - - if (GET_CODE (x) == GET_CODE (y)) - switch (GET_CODE (x)) - { - case MULT: - { - /* Handle cases where we expect the second operands to be the - same, and check only whether the first operand would conflict - or not. */ - rtx x0, y0; - rtx x1 = canon_rtx (XEXP (x, 1)); - rtx y1 = canon_rtx (XEXP (y, 1)); - if (! rtx_equal_for_memref_p (x1, y1)) - return 1; - x0 = canon_rtx (XEXP (x, 0)); - y0 = canon_rtx (XEXP (y, 0)); - if (rtx_equal_for_memref_p (x0, y0)) - return (xsize == 0 || ysize == 0 - || (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0)); - - /* Can't properly adjust our sizes. */ - if (GET_CODE (x1) != CONST_INT) - return 1; - xsize /= INTVAL (x1); - ysize /= INTVAL (x1); - c /= INTVAL (x1); - return memrefs_conflict_p (xsize, x0, ysize, y0, c); - } - } - - if (CONSTANT_P (x)) - { - if (GET_CODE (x) == CONST_INT && GET_CODE (y) == CONST_INT) - { - c += (INTVAL (y) - INTVAL (x)); - return (xsize == 0 || ysize == 0 - || (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0)); - } - - if (GET_CODE (x) == CONST) - { - if (GET_CODE (y) == CONST) - return memrefs_conflict_p (xsize, canon_rtx (XEXP (x, 0)), - ysize, canon_rtx (XEXP (y, 0)), c); - else - return memrefs_conflict_p (xsize, canon_rtx (XEXP (x, 0)), - ysize, y, c); - } - if (GET_CODE (y) == CONST) - return memrefs_conflict_p (xsize, x, ysize, - canon_rtx (XEXP (y, 0)), c); - - if (CONSTANT_P (y)) - return (rtx_equal_for_memref_p (x, y) - && (xsize == 0 || ysize == 0 - || (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0))); - - return 1; - } - return 1; -} - -/* Functions to compute memory dependencies. - - Since we process the insns in execution order, we can build tables - to keep track of what registers are fixed (and not aliased), what registers - are varying in known ways, and what registers are varying in unknown - ways. - - If both memory references are volatile, then there must always be a - dependence between the two references, since their order can not be - changed. A volatile and non-volatile reference can be interchanged - though. - - A MEM_IN_STRUCT reference at a non-QImode varying address can never - conflict with a non-MEM_IN_STRUCT reference at a fixed address. We must - allow QImode aliasing because the ANSI C standard allows character - pointers to alias anything. We are assuming that characters are - always QImode here. */ - -/* Read dependence: X is read after read in MEM takes place. There can - only be a dependence here if both reads are volatile. */ - -int -read_dependence (mem, x) - rtx mem; - rtx x; -{ - return MEM_VOLATILE_P (x) && MEM_VOLATILE_P (mem); -} - -/* True dependence: X is read after store in MEM takes place. */ - -int -true_dependence (mem, x) - rtx mem; - rtx x; -{ - /* If X is an unchanging read, then it can't possibly conflict with any - non-unchanging store. It may conflict with an unchanging write though, - because there may be a single store to this address to initialize it. - Just fall through to the code below to resolve the case where we have - both an unchanging read and an unchanging write. This won't handle all - cases optimally, but the possible performance loss should be - negligible. */ - if (RTX_UNCHANGING_P (x) && ! RTX_UNCHANGING_P (mem)) - return 0; - - return ((MEM_VOLATILE_P (x) && MEM_VOLATILE_P (mem)) - || (memrefs_conflict_p (SIZE_FOR_MODE (mem), XEXP (mem, 0), - SIZE_FOR_MODE (x), XEXP (x, 0), 0) - && ! (MEM_IN_STRUCT_P (mem) && rtx_addr_varies_p (mem) - && GET_MODE (mem) != QImode - && ! MEM_IN_STRUCT_P (x) && ! rtx_addr_varies_p (x)) - && ! (MEM_IN_STRUCT_P (x) && rtx_addr_varies_p (x) - && GET_MODE (x) != QImode - && ! MEM_IN_STRUCT_P (mem) && ! rtx_addr_varies_p (mem)))); -} - -/* Anti dependence: X is written after read in MEM takes place. */ - -int -anti_dependence (mem, x) - rtx mem; - rtx x; -{ - /* If MEM is an unchanging read, then it can't possibly conflict with - the store to X, because there is at most one store to MEM, and it must - have occured somewhere before MEM. */ - if (RTX_UNCHANGING_P (mem)) - return 0; - - return ((MEM_VOLATILE_P (x) && MEM_VOLATILE_P (mem)) - || (memrefs_conflict_p (SIZE_FOR_MODE (mem), XEXP (mem, 0), - SIZE_FOR_MODE (x), XEXP (x, 0), 0) - && ! (MEM_IN_STRUCT_P (mem) && rtx_addr_varies_p (mem) - && GET_MODE (mem) != QImode - && ! MEM_IN_STRUCT_P (x) && ! rtx_addr_varies_p (x)) - && ! (MEM_IN_STRUCT_P (x) && rtx_addr_varies_p (x) - && GET_MODE (x) != QImode - && ! MEM_IN_STRUCT_P (mem) && ! rtx_addr_varies_p (mem)))); -} - -/* Output dependence: X is written after store in MEM takes place. */ - -int -output_dependence (mem, x) - rtx mem; - rtx x; -{ - return ((MEM_VOLATILE_P (x) && MEM_VOLATILE_P (mem)) - || (memrefs_conflict_p (SIZE_FOR_MODE (mem), XEXP (mem, 0), - SIZE_FOR_MODE (x), XEXP (x, 0), 0) - && ! (MEM_IN_STRUCT_P (mem) && rtx_addr_varies_p (mem) - && GET_MODE (mem) != QImode - && ! MEM_IN_STRUCT_P (x) && ! rtx_addr_varies_p (x)) - && ! (MEM_IN_STRUCT_P (x) && rtx_addr_varies_p (x) - && GET_MODE (x) != QImode - && ! MEM_IN_STRUCT_P (mem) && ! rtx_addr_varies_p (mem)))); -} - -/* Helper functions for instruction scheduling. */ - -/* Add ELEM wrapped in an INSN_LIST with reg note kind DEP_TYPE to the - LOG_LINKS of INSN, if not already there. DEP_TYPE indicates the type - of dependence that this link represents. */ - -void -add_dependence (insn, elem, dep_type) - rtx insn; - rtx elem; - enum reg_note dep_type; -{ - rtx link, next; - - /* Don't depend an insn on itself. */ - if (insn == elem) - return; - - /* If elem is part of a sequence that must be scheduled together, then - make the dependence point to the last insn of the sequence. - When HAVE_cc0, it is possible for NOTEs to exist between users and - setters of the condition codes, so we must skip past notes here. - Otherwise, NOTEs are impossible here. */ - - next = NEXT_INSN (elem); - -#ifdef HAVE_cc0 - while (next && GET_CODE (next) == NOTE) - next = NEXT_INSN (next); -#endif - - if (next && SCHED_GROUP_P (next)) - { - /* Notes will never intervene here though, so don't bother checking - for them. */ - while (NEXT_INSN (next) && SCHED_GROUP_P (NEXT_INSN (next))) - next = NEXT_INSN (next); - - /* Again, don't depend an insn on itself. */ - if (insn == next) - return; - - /* Make the dependence to NEXT, the last insn of the group, instead - of the original ELEM. */ - elem = next; - } - - /* Check that we don't already have this dependence. */ - for (link = LOG_LINKS (insn); link; link = XEXP (link, 1)) - if (XEXP (link, 0) == elem) - { - /* If this is a more restrictive type of dependence than the existing - one, then change the existing dependence to this type. */ - if ((int) dep_type < (int) REG_NOTE_KIND (link)) - PUT_REG_NOTE_KIND (link, dep_type); - return; - } - /* Might want to check one level of transitivity to save conses. */ - - link = rtx_alloc (INSN_LIST); - /* Insn dependency, not data dependency. */ - PUT_REG_NOTE_KIND (link, dep_type); - XEXP (link, 0) = elem; - XEXP (link, 1) = LOG_LINKS (insn); - LOG_LINKS (insn) = link; -} - -/* Remove ELEM wrapped in an INSN_LIST from the LOG_LINKS - of INSN. Abort if not found. */ -void -remove_dependence (insn, elem) - rtx insn; - rtx elem; -{ - rtx prev, link; - int found = 0; - - for (prev = 0, link = LOG_LINKS (insn); link; - prev = link, link = XEXP (link, 1)) - { - if (XEXP (link, 0) == elem) - { - if (prev) - XEXP (prev, 1) = XEXP (link, 1); - else - LOG_LINKS (insn) = XEXP (link, 1); - found = 1; - } - } - - if (! found) - abort (); - return; -} - -#ifndef INSN_SCHEDULING -void schedule_insns () {} -#else -#ifndef __GNUC__ -#define __inline -#endif - -/* Computation of memory dependencies. */ - -/* The *_insns and *_mems are paired lists. Each pending memory operation - will have a pointer to the MEM rtx on one list and a pointer to the - containing insn on the other list in the same place in the list. */ - -/* We can't use add_dependence like the old code did, because a single insn - may have multiple memory accesses, and hence needs to be on the list - once for each memory access. Add_dependence won't let you add an insn - to a list more than once. */ - -/* An INSN_LIST containing all insns with pending read operations. */ -static rtx pending_read_insns; - -/* An EXPR_LIST containing all MEM rtx's which are pending reads. */ -static rtx pending_read_mems; - -/* An INSN_LIST containing all insns with pending write operations. */ -static rtx pending_write_insns; - -/* An EXPR_LIST containing all MEM rtx's which are pending writes. */ -static rtx pending_write_mems; - -/* Indicates the combined length of the two pending lists. We must prevent - these lists from ever growing too large since the number of dependencies - produced is at least O(N*N), and execution time is at least O(4*N*N), as - a function of the length of these pending lists. */ - -static int pending_lists_length; - -/* An INSN_LIST containing all INSN_LISTs allocated but currently unused. */ - -static rtx unused_insn_list; - -/* An EXPR_LIST containing all EXPR_LISTs allocated but currently unused. */ - -static rtx unused_expr_list; - -/* The last insn upon which all memory references must depend. - This is an insn which flushed the pending lists, creating a dependency - between it and all previously pending memory references. This creates - a barrier (or a checkpoint) which no memory reference is allowed to cross. - - This includes all non constant CALL_INSNs. When we do interprocedural - alias analysis, this restriction can be relaxed. - This may also be an INSN that writes memory if the pending lists grow - too large. */ - -static rtx last_pending_memory_flush; - -/* The last function call we have seen. All hard regs, and, of course, - the last function call, must depend on this. */ - -static rtx last_function_call; - -/* The LOG_LINKS field of this is a list of insns which use a pseudo register - that does not already cross a call. We create dependencies between each - of those insn and the next call insn, to ensure that they won't cross a call - after scheduling is done. */ - -static rtx sched_before_next_call; - -/* Pointer to the last instruction scheduled. Used by rank_for_schedule, - so that insns independent of the last scheduled insn will be preferred - over dependent instructions. */ - -static rtx last_scheduled_insn; - -/* Process an insn's memory dependencies. There are four kinds of - dependencies: - - (0) read dependence: read follows read - (1) true dependence: read follows write - (2) anti dependence: write follows read - (3) output dependence: write follows write - - We are careful to build only dependencies which actually exist, and - use transitivity to avoid building too many links. */ - -/* Return the INSN_LIST containing INSN in LIST, or NULL - if LIST does not contain INSN. */ - -__inline static rtx -find_insn_list (insn, list) - rtx insn; - rtx list; -{ - while (list) - { - if (XEXP (list, 0) == insn) - return list; - list = XEXP (list, 1); - } - return 0; -} - -/* Compute the function units used by INSN. This caches the value - returned by function_units_used. A function unit is encoded as the - unit number if the value is non-negative and the compliment of a - mask if the value is negative. A function unit index is the - non-negative encoding. */ - -__inline static int -insn_unit (insn) - rtx insn; -{ - register int unit = INSN_UNIT (insn); - - if (unit == 0) - { - recog_memoized (insn); - - /* A USE insn, or something else we don't need to understand. - We can't pass these directly to function_units_used because it will - trigger a fatal error for unrecognizable insns. */ - if (INSN_CODE (insn) < 0) - unit = -1; - else - { - unit = function_units_used (insn); - /* Increment non-negative values so we can cache zero. */ - if (unit >= 0) unit++; - } - /* We only cache 16 bits of the result, so if the value is out of - range, don't cache it. */ - if (FUNCTION_UNITS_SIZE < HOST_BITS_PER_SHORT - || unit >= 0 - || (~unit & ((1 << (HOST_BITS_PER_SHORT - 1)) - 1)) == 0) - INSN_UNIT (insn) = unit; - } - return (unit > 0 ? unit - 1 : unit); -} - -/* Compute the blockage range for executing INSN on UNIT. This caches - the value returned by the blockage_range_function for the unit. - These values are encoded in an int where the upper half gives the - minimum value and the lower half gives the maximum value. */ - -__inline static unsigned int -blockage_range (unit, insn) - int unit; - rtx insn; -{ - unsigned int blockage = INSN_BLOCKAGE (insn); - unsigned int range; - - if (UNIT_BLOCKED (blockage) != unit + 1) - { - range = function_units[unit].blockage_range_function (insn); - /* We only cache the blockage range for one unit and then only if - the values fit. */ - if (HOST_BITS_PER_INT >= UNIT_BITS + 2 * BLOCKAGE_BITS) - INSN_BLOCKAGE (insn) = ENCODE_BLOCKAGE (unit + 1, range); - } - else - range = BLOCKAGE_RANGE (blockage); - - return range; -} - -/* A vector indexed by function unit instance giving the last insn to use - the unit. The value of the function unit instance index for unit U - instance I is (U + I * FUNCTION_UNITS_SIZE). */ -static rtx unit_last_insn[FUNCTION_UNITS_SIZE * MAX_MULTIPLICITY]; - -/* A vector indexed by function unit instance giving the minimum time when - the unit will unblock based on the maximum blockage cost. */ -static int unit_tick[FUNCTION_UNITS_SIZE * MAX_MULTIPLICITY]; - -/* A vector indexed by function unit number giving the number of insns - that remain to use the unit. */ -static int unit_n_insns[FUNCTION_UNITS_SIZE]; - -/* Reset the function unit state to the null state. */ - -static void -clear_units () -{ - int unit; - - bzero (unit_last_insn, sizeof (unit_last_insn)); - bzero (unit_tick, sizeof (unit_tick)); - bzero (unit_n_insns, sizeof (unit_n_insns)); -} - -/* Record an insn as one that will use the units encoded by UNIT. */ - -__inline static void -prepare_unit (unit) - int unit; -{ - int i; - - if (unit >= 0) - unit_n_insns[unit]++; - else - for (i = 0, unit = ~unit; unit; i++, unit >>= 1) - if ((unit & 1) != 0) - prepare_unit (i); -} - -/* Return the actual hazard cost of executing INSN on the unit UNIT, - instance INSTANCE at time CLOCK if the previous actual hazard cost - was COST. */ - -__inline static int -actual_hazard_this_instance (unit, instance, insn, clock, cost) - int unit, instance, clock, cost; - rtx insn; -{ - int i; - int tick = unit_tick[instance]; - - if (tick - clock > cost) - { - /* The scheduler is operating in reverse, so INSN is the executing - insn and the unit's last insn is the candidate insn. We want a - more exact measure of the blockage if we execute INSN at CLOCK - given when we committed the execution of the unit's last insn. - - The blockage value is given by either the unit's max blockage - constant, blockage range function, or blockage function. Use - the most exact form for the given unit. */ - - if (function_units[unit].blockage_range_function) - { - if (function_units[unit].blockage_function) - tick += (function_units[unit].blockage_function - (insn, unit_last_insn[instance]) - - function_units[unit].max_blockage); - else - tick += ((int) MAX_BLOCKAGE_COST (blockage_range (unit, insn)) - - function_units[unit].max_blockage); - } - if (tick - clock > cost) - cost = tick - clock; - } - return cost; -} - -/* Record INSN as having begun execution on the units encoded by UNIT at - time CLOCK. */ - -__inline static void -schedule_unit (unit, insn, clock) - int unit, clock; - rtx insn; -{ - int i; - - if (unit >= 0) - { - int instance = unit; -#if MAX_MULTIPLICITY > 1 - /* Find the first free instance of the function unit and use that - one. We assume that one is free. */ - for (i = function_units[unit].multiplicity - 1; i > 0; i--) - { - if (! actual_hazard_this_instance (unit, instance, insn, clock, 0)) - break; - instance += FUNCTION_UNITS_SIZE; - } -#endif - unit_last_insn[instance] = insn; - unit_tick[instance] = (clock + function_units[unit].max_blockage); - } - else - for (i = 0, unit = ~unit; unit; i++, unit >>= 1) - if ((unit & 1) != 0) - schedule_unit (i, insn, clock); -} - -/* Return the actual hazard cost of executing INSN on the units encoded by - UNIT at time CLOCK if the previous actual hazard cost was COST. */ - -__inline static int -actual_hazard (unit, insn, clock, cost) - int unit, clock, cost; - rtx insn; -{ - int i; - - if (unit >= 0) - { - /* Find the instance of the function unit with the minimum hazard. */ - int instance = unit; - int best = instance; - int best_cost = actual_hazard_this_instance (unit, instance, insn, - clock, cost); - int this_cost; - -#if MAX_MULTIPLICITY > 1 - if (best_cost > cost) - { - for (i = function_units[unit].multiplicity - 1; i > 0; i--) - { - instance += FUNCTION_UNITS_SIZE; - this_cost = actual_hazard_this_instance (unit, instance, insn, - clock, cost); - if (this_cost < best_cost) - { - best = instance; - best_cost = this_cost; - if (this_cost <= cost) - break; - } - } - } -#endif - cost = MAX (cost, best_cost); - } - else - for (i = 0, unit = ~unit; unit; i++, unit >>= 1) - if ((unit & 1) != 0) - cost = actual_hazard (i, insn, clock, cost); - - return cost; -} - -/* Return the potential hazard cost of executing an instruction on the - units encoded by UNIT if the previous potential hazard cost was COST. - An insn with a large blockage time is chosen in preference to one - with a smaller time; an insn that uses a unit that is more likely - to be used is chosen in preference to one with a unit that is less - used. We are trying to minimize a subsequent actual hazard. */ - -__inline static int -potential_hazard (unit, insn, cost) - int unit, cost; - rtx insn; -{ - int i, ncost; - unsigned int minb, maxb; - - if (unit >= 0) - { - minb = maxb = function_units[unit].max_blockage; - if (maxb > 1) - { - if (function_units[unit].blockage_range_function) - { - maxb = minb = blockage_range (unit, insn); - maxb = MAX_BLOCKAGE_COST (maxb); - minb = MIN_BLOCKAGE_COST (minb); - } - - if (maxb > 1) - { - /* Make the number of instructions left dominate. Make the - minimum delay dominate the maximum delay. If all these - are the same, use the unit number to add an arbitrary - ordering. Other terms can be added. */ - ncost = minb * 0x40 + maxb; - ncost *= (unit_n_insns[unit] - 1) * 0x1000 + unit; - if (ncost > cost) - cost = ncost; - } - } - } - else - for (i = 0, unit = ~unit; unit; i++, unit >>= 1) - if ((unit & 1) != 0) - cost = potential_hazard (i, insn, cost); - - return cost; -} - -/* Compute cost of executing INSN given the dependence LINK on the insn USED. - This is the number of virtual cycles taken between instruction issue and - instruction results. */ - -__inline static int -insn_cost (insn, link, used) - rtx insn, link, used; -{ - register int cost = INSN_COST (insn); - - if (cost == 0) - { - recog_memoized (insn); - - /* A USE insn, or something else we don't need to understand. - We can't pass these directly to result_ready_cost because it will - trigger a fatal error for unrecognizable insns. */ - if (INSN_CODE (insn) < 0) - { - INSN_COST (insn) = 1; - return 1; - } - else - { - cost = result_ready_cost (insn); - - if (cost < 1) - cost = 1; - - INSN_COST (insn) = cost; - } - } - - /* A USE insn should never require the value used to be computed. This - allows the computation of a function's result and parameter values to - overlap the return and call. */ - recog_memoized (used); - if (INSN_CODE (used) < 0) - LINK_COST_FREE (link) = 1; - - /* If some dependencies vary the cost, compute the adjustment. Most - commonly, the adjustment is complete: either the cost is ignored - (in the case of an output- or anti-dependence), or the cost is - unchanged. These values are cached in the link as LINK_COST_FREE - and LINK_COST_ZERO. */ - - if (LINK_COST_FREE (link)) - cost = 1; -#ifdef ADJUST_COST - else if (! LINK_COST_ZERO (link)) - { - int ncost = cost; - - ADJUST_COST (used, link, insn, ncost); - if (ncost <= 1) - LINK_COST_FREE (link) = ncost = 1; - if (cost == ncost) - LINK_COST_ZERO (link) = 1; - cost = ncost; - } -#endif - return cost; -} - -/* Compute the priority number for INSN. */ - -static int -priority (insn) - rtx insn; -{ - if (insn && GET_RTX_CLASS (GET_CODE (insn)) == 'i') - { - int prev_priority; - int max_priority; - int this_priority = INSN_PRIORITY (insn); - rtx prev; - - if (this_priority > 0) - return this_priority; - - max_priority = 1; - - /* Nonzero if these insns must be scheduled together. */ - if (SCHED_GROUP_P (insn)) - { - prev = insn; - while (SCHED_GROUP_P (prev)) - { - prev = PREV_INSN (prev); - INSN_REF_COUNT (prev) += 1; - } - } - - for (prev = LOG_LINKS (insn); prev; prev = XEXP (prev, 1)) - { - rtx x = XEXP (prev, 0); - - /* A dependence pointing to a note is always obsolete, because - sched_analyze_insn will have created any necessary new dependences - which replace it. Notes can be created when instructions are - deleted by insn splitting, or by register allocation. */ - if (GET_CODE (x) == NOTE) - { - remove_dependence (insn, x); - continue; - } - - /* Clear the link cost adjustment bits. */ - LINK_COST_FREE (prev) = 0; -#ifdef ADJUST_COST - LINK_COST_ZERO (prev) = 0; -#endif - - /* This priority calculation was chosen because it results in the - least instruction movement, and does not hurt the performance - of the resulting code compared to the old algorithm. - This makes the sched algorithm more stable, which results - in better code, because there is less register pressure, - cross jumping is more likely to work, and debugging is easier. - - When all instructions have a latency of 1, there is no need to - move any instructions. Subtracting one here ensures that in such - cases all instructions will end up with a priority of one, and - hence no scheduling will be done. - - The original code did not subtract the one, and added the - insn_cost of the current instruction to its priority (e.g. - move the insn_cost call down to the end). */ - - if (REG_NOTE_KIND (prev) == 0) - /* Data dependence. */ - prev_priority = priority (x) + insn_cost (x, prev, insn) - 1; - else - /* Anti or output dependence. Don't add the latency of this - insn's result, because it isn't being used. */ - prev_priority = priority (x); - - if (prev_priority > max_priority) - max_priority = prev_priority; - INSN_REF_COUNT (x) += 1; - } - - prepare_unit (insn_unit (insn)); - INSN_PRIORITY (insn) = max_priority; - return INSN_PRIORITY (insn); - } - return 0; -} - -/* Remove all INSN_LISTs and EXPR_LISTs from the pending lists and add - them to the unused_*_list variables, so that they can be reused. */ - -static void -free_pending_lists () -{ - register rtx link, prev_link; - - if (pending_read_insns) - { - prev_link = pending_read_insns; - link = XEXP (prev_link, 1); - - while (link) - { - prev_link = link; - link = XEXP (link, 1); - } - - XEXP (prev_link, 1) = unused_insn_list; - unused_insn_list = pending_read_insns; - pending_read_insns = 0; - } - - if (pending_write_insns) - { - prev_link = pending_write_insns; - link = XEXP (prev_link, 1); - - while (link) - { - prev_link = link; - link = XEXP (link, 1); - } - - XEXP (prev_link, 1) = unused_insn_list; - unused_insn_list = pending_write_insns; - pending_write_insns = 0; - } - - if (pending_read_mems) - { - prev_link = pending_read_mems; - link = XEXP (prev_link, 1); - - while (link) - { - prev_link = link; - link = XEXP (link, 1); - } - - XEXP (prev_link, 1) = unused_expr_list; - unused_expr_list = pending_read_mems; - pending_read_mems = 0; - } - - if (pending_write_mems) - { - prev_link = pending_write_mems; - link = XEXP (prev_link, 1); - - while (link) - { - prev_link = link; - link = XEXP (link, 1); - } - - XEXP (prev_link, 1) = unused_expr_list; - unused_expr_list = pending_write_mems; - pending_write_mems = 0; - } -} - -/* Add an INSN and MEM reference pair to a pending INSN_LIST and MEM_LIST. - The MEM is a memory reference contained within INSN, which we are saving - so that we can do memory aliasing on it. */ - -static void -add_insn_mem_dependence (insn_list, mem_list, insn, mem) - rtx *insn_list, *mem_list, insn, mem; -{ - register rtx link; - - if (unused_insn_list) - { - link = unused_insn_list; - unused_insn_list = XEXP (link, 1); - } - else - link = rtx_alloc (INSN_LIST); - XEXP (link, 0) = insn; - XEXP (link, 1) = *insn_list; - *insn_list = link; - - if (unused_expr_list) - { - link = unused_expr_list; - unused_expr_list = XEXP (link, 1); - } - else - link = rtx_alloc (EXPR_LIST); - XEXP (link, 0) = mem; - XEXP (link, 1) = *mem_list; - *mem_list = link; - - pending_lists_length++; -} - -/* Make a dependency between every memory reference on the pending lists - and INSN, thus flushing the pending lists. */ - -static void -flush_pending_lists (insn) - rtx insn; -{ - rtx link; - - while (pending_read_insns) - { - add_dependence (insn, XEXP (pending_read_insns, 0), REG_DEP_ANTI); - - link = pending_read_insns; - pending_read_insns = XEXP (pending_read_insns, 1); - XEXP (link, 1) = unused_insn_list; - unused_insn_list = link; - - link = pending_read_mems; - pending_read_mems = XEXP (pending_read_mems, 1); - XEXP (link, 1) = unused_expr_list; - unused_expr_list = link; - } - while (pending_write_insns) - { - add_dependence (insn, XEXP (pending_write_insns, 0), REG_DEP_ANTI); - - link = pending_write_insns; - pending_write_insns = XEXP (pending_write_insns, 1); - XEXP (link, 1) = unused_insn_list; - unused_insn_list = link; - - link = pending_write_mems; - pending_write_mems = XEXP (pending_write_mems, 1); - XEXP (link, 1) = unused_expr_list; - unused_expr_list = link; - } - pending_lists_length = 0; - - if (last_pending_memory_flush) - add_dependence (insn, last_pending_memory_flush, REG_DEP_ANTI); - - last_pending_memory_flush = insn; -} - -/* Analyze a single SET or CLOBBER rtx, X, creating all dependencies generated - by the write to the destination of X, and reads of everything mentioned. */ - -static void -sched_analyze_1 (x, insn) - rtx x; - rtx insn; -{ - register int regno; - register rtx dest = SET_DEST (x); - - if (dest == 0) - return; - - while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SUBREG - || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT) - { - if (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT) - { - /* The second and third arguments are values read by this insn. */ - sched_analyze_2 (XEXP (dest, 1), insn); - sched_analyze_2 (XEXP (dest, 2), insn); - } - dest = SUBREG_REG (dest); - } - - if (GET_CODE (dest) == REG) - { - register int offset, bit, i; - - regno = REGNO (dest); - - /* A hard reg in a wide mode may really be multiple registers. - If so, mark all of them just like the first. */ - if (regno < FIRST_PSEUDO_REGISTER) - { - i = HARD_REGNO_NREGS (regno, GET_MODE (dest)); - while (--i >= 0) - { - rtx u; - - for (u = reg_last_uses[regno+i]; u; u = XEXP (u, 1)) - add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); - reg_last_uses[regno + i] = 0; - if (reg_last_sets[regno + i]) - add_dependence (insn, reg_last_sets[regno + i], - REG_DEP_OUTPUT); - reg_last_sets[regno + i] = insn; - if ((call_used_regs[i] || global_regs[i]) - && last_function_call) - /* Function calls clobber all call_used regs. */ - add_dependence (insn, last_function_call, REG_DEP_ANTI); - } - } - else - { - rtx u; - - for (u = reg_last_uses[regno]; u; u = XEXP (u, 1)) - add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); - reg_last_uses[regno] = 0; - if (reg_last_sets[regno]) - add_dependence (insn, reg_last_sets[regno], REG_DEP_OUTPUT); - reg_last_sets[regno] = insn; - - /* Pseudos that are REG_EQUIV to something may be replaced - by that during reloading. We need only add dependencies for - the address in the REG_EQUIV note. */ - if (! reload_completed - && reg_known_equiv_p[regno] - && GET_CODE (reg_known_value[regno]) == MEM) - sched_analyze_2 (XEXP (reg_known_value[regno], 0), insn); - - /* Don't let it cross a call after scheduling if it doesn't - already cross one. */ - if (reg_n_calls_crossed[regno] == 0 && last_function_call) - add_dependence (insn, last_function_call, REG_DEP_ANTI); - } - } - else if (GET_CODE (dest) == MEM) - { - /* Writing memory. */ - - if (pending_lists_length > 32) - { - /* Flush all pending reads and writes to prevent the pending lists - from getting any larger. Insn scheduling runs too slowly when - these lists get long. The number 32 was chosen because it - seems like a reasonable number. When compiling GCC with itself, - this flush occurs 8 times for sparc, and 10 times for m88k using - the number 32. */ - flush_pending_lists (insn); - } - else - { - rtx pending, pending_mem; - - pending = pending_read_insns; - pending_mem = pending_read_mems; - while (pending) - { - /* If a dependency already exists, don't create a new one. */ - if (! find_insn_list (XEXP (pending, 0), LOG_LINKS (insn))) - if (anti_dependence (XEXP (pending_mem, 0), dest)) - add_dependence (insn, XEXP (pending, 0), REG_DEP_ANTI); - - pending = XEXP (pending, 1); - pending_mem = XEXP (pending_mem, 1); - } - - pending = pending_write_insns; - pending_mem = pending_write_mems; - while (pending) - { - /* If a dependency already exists, don't create a new one. */ - if (! find_insn_list (XEXP (pending, 0), LOG_LINKS (insn))) - if (output_dependence (XEXP (pending_mem, 0), dest)) - add_dependence (insn, XEXP (pending, 0), REG_DEP_OUTPUT); - - pending = XEXP (pending, 1); - pending_mem = XEXP (pending_mem, 1); - } - - if (last_pending_memory_flush) - add_dependence (insn, last_pending_memory_flush, REG_DEP_ANTI); - - add_insn_mem_dependence (&pending_write_insns, &pending_write_mems, - insn, dest); - } - sched_analyze_2 (XEXP (dest, 0), insn); - } - - /* Analyze reads. */ - if (GET_CODE (x) == SET) - sched_analyze_2 (SET_SRC (x), insn); -} - -/* Analyze the uses of memory and registers in rtx X in INSN. */ - -static void -sched_analyze_2 (x, insn) - rtx x; - rtx insn; -{ - register int i; - register int j; - register enum rtx_code code; - register char *fmt; - - if (x == 0) - return; - - code = GET_CODE (x); - - switch (code) - { - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case CONST: - case LABEL_REF: - /* Ignore constants. Note that we must handle CONST_DOUBLE here - because it may have a cc0_rtx in its CONST_DOUBLE_CHAIN field, but - this does not mean that this insn is using cc0. */ - return; - -#ifdef HAVE_cc0 - case CC0: - { - rtx link, prev; - - /* There may be a note before this insn now, but all notes will - be removed before we actually try to schedule the insns, so - it won't cause a problem later. We must avoid it here though. */ - - /* User of CC0 depends on immediately preceding insn. */ - SCHED_GROUP_P (insn) = 1; - - /* Make a copy of all dependencies on the immediately previous insn, - and add to this insn. This is so that all the dependencies will - apply to the group. Remove an explicit dependence on this insn - as SCHED_GROUP_P now represents it. */ - - prev = PREV_INSN (insn); - while (GET_CODE (prev) == NOTE) - prev = PREV_INSN (prev); - - if (find_insn_list (prev, LOG_LINKS (insn))) - remove_dependence (insn, prev); - - for (link = LOG_LINKS (prev); link; link = XEXP (link, 1)) - add_dependence (insn, XEXP (link, 0), REG_NOTE_KIND (link)); - - return; - } -#endif - - case REG: - { - int regno = REGNO (x); - if (regno < FIRST_PSEUDO_REGISTER) - { - int i; - - i = HARD_REGNO_NREGS (regno, GET_MODE (x)); - while (--i >= 0) - { - reg_last_uses[regno + i] - = gen_rtx (INSN_LIST, VOIDmode, - insn, reg_last_uses[regno + i]); - if (reg_last_sets[regno + i]) - add_dependence (insn, reg_last_sets[regno + i], 0); - if ((call_used_regs[regno + i] || global_regs[regno + i]) - && last_function_call) - /* Function calls clobber all call_used regs. */ - add_dependence (insn, last_function_call, REG_DEP_ANTI); - } - } - else - { - reg_last_uses[regno] - = gen_rtx (INSN_LIST, VOIDmode, insn, reg_last_uses[regno]); - if (reg_last_sets[regno]) - add_dependence (insn, reg_last_sets[regno], 0); - - /* Pseudos that are REG_EQUIV to something may be replaced - by that during reloading. We need only add dependencies for - the address in the REG_EQUIV note. */ - if (! reload_completed - && reg_known_equiv_p[regno] - && GET_CODE (reg_known_value[regno]) == MEM) - sched_analyze_2 (XEXP (reg_known_value[regno], 0), insn); - - /* If the register does not already cross any calls, then add this - insn to the sched_before_next_call list so that it will still - not cross calls after scheduling. */ - if (reg_n_calls_crossed[regno] == 0) - add_dependence (sched_before_next_call, insn, REG_DEP_ANTI); - } - return; - } - - case MEM: - { - /* Reading memory. */ - - rtx pending, pending_mem; - - pending = pending_read_insns; - pending_mem = pending_read_mems; - while (pending) - { - /* If a dependency already exists, don't create a new one. */ - if (! find_insn_list (XEXP (pending, 0), LOG_LINKS (insn))) - if (read_dependence (XEXP (pending_mem, 0), x)) - add_dependence (insn, XEXP (pending, 0), REG_DEP_ANTI); - - pending = XEXP (pending, 1); - pending_mem = XEXP (pending_mem, 1); - } - - pending = pending_write_insns; - pending_mem = pending_write_mems; - while (pending) - { - /* If a dependency already exists, don't create a new one. */ - if (! find_insn_list (XEXP (pending, 0), LOG_LINKS (insn))) - if (true_dependence (XEXP (pending_mem, 0), x)) - add_dependence (insn, XEXP (pending, 0), 0); - - pending = XEXP (pending, 1); - pending_mem = XEXP (pending_mem, 1); - } - if (last_pending_memory_flush) - add_dependence (insn, last_pending_memory_flush, REG_DEP_ANTI); - - /* Always add these dependencies to pending_reads, since - this insn may be followed by a write. */ - add_insn_mem_dependence (&pending_read_insns, &pending_read_mems, - insn, x); - - /* Take advantage of tail recursion here. */ - sched_analyze_2 (XEXP (x, 0), insn); - return; - } - - case ASM_OPERANDS: - case ASM_INPUT: - case UNSPEC_VOLATILE: - case TRAP_IF: - { - rtx u; - - /* Traditional and volatile asm instructions must be considered to use - and clobber all hard registers and all of memory. So must - TRAP_IF and UNSPEC_VOLATILE operations. */ - if (code != ASM_OPERANDS || MEM_VOLATILE_P (x)) - { - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - for (u = reg_last_uses[i]; u; u = XEXP (u, 1)) - if (GET_CODE (PATTERN (XEXP (u, 0))) != USE) - add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); - reg_last_uses[i] = 0; - if (reg_last_sets[i] - && GET_CODE (PATTERN (reg_last_sets[i])) != USE) - add_dependence (insn, reg_last_sets[i], 0); - reg_last_sets[i] = insn; - } - - flush_pending_lists (insn); - } - - /* For all ASM_OPERANDS, we must traverse the vector of input operands. - We can not just fall through here since then we would be confused - by the ASM_INPUT rtx inside ASM_OPERANDS, which do not indicate - traditional asms unlike their normal usage. */ - - if (code == ASM_OPERANDS) - { - for (j = 0; j < ASM_OPERANDS_INPUT_LENGTH (x); j++) - sched_analyze_2 (ASM_OPERANDS_INPUT (x, j), insn); - return; - } - break; - } - - case PRE_DEC: - case POST_DEC: - case PRE_INC: - case POST_INC: - /* These both read and modify the result. We must handle them as writes - to get proper dependencies for following instructions. We must handle - them as reads to get proper dependencies from this to previous - instructions. Thus we need to pass them to both sched_analyze_1 - and sched_analyze_2. We must call sched_analyze_2 first in order - to get the proper antecedent for the read. */ - sched_analyze_2 (XEXP (x, 0), insn); - sched_analyze_1 (x, insn); - return; - } - - /* Other cases: walk the insn. */ - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - sched_analyze_2 (XEXP (x, i), insn); - else if (fmt[i] == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - sched_analyze_2 (XVECEXP (x, i, j), insn); - } -} - -/* Analyze an INSN with pattern X to find all dependencies. */ - -static void -sched_analyze_insn (x, insn) - rtx x, insn; -{ - register RTX_CODE code = GET_CODE (x); - rtx link; - - if (code == SET || code == CLOBBER) - sched_analyze_1 (x, insn); - else if (code == PARALLEL) - { - register int i; - for (i = XVECLEN (x, 0) - 1; i >= 0; i--) - { - code = GET_CODE (XVECEXP (x, 0, i)); - if (code == SET || code == CLOBBER) - sched_analyze_1 (XVECEXP (x, 0, i), insn); - else - sched_analyze_2 (XVECEXP (x, 0, i), insn); - } - } - else - sched_analyze_2 (x, insn); - - /* Handle function calls. */ - if (GET_CODE (insn) == CALL_INSN) - { - rtx dep_insn; - rtx prev_dep_insn; - - /* When scheduling instructions, we make sure calls don't lose their - accompanying USE insns by depending them one on another in order. */ - - prev_dep_insn = insn; - dep_insn = PREV_INSN (insn); - while (GET_CODE (dep_insn) == INSN - && GET_CODE (PATTERN (dep_insn)) == USE) - { - SCHED_GROUP_P (prev_dep_insn) = 1; - - /* Make a copy of all dependencies on dep_insn, and add to insn. - This is so that all of the dependencies will apply to the - group. */ - - for (link = LOG_LINKS (dep_insn); link; link = XEXP (link, 1)) - add_dependence (insn, XEXP (link, 0), REG_NOTE_KIND (link)); - - prev_dep_insn = dep_insn; - dep_insn = PREV_INSN (dep_insn); - } - } -} - -/* Analyze every insn between HEAD and TAIL inclusive, creating LOG_LINKS - for every dependency. */ - -static int -sched_analyze (head, tail) - rtx head, tail; -{ - register rtx insn; - register int n_insns = 0; - register rtx u; - register int luid = 0; - - for (insn = head; ; insn = NEXT_INSN (insn)) - { - INSN_LUID (insn) = luid++; - - if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN) - { - sched_analyze_insn (PATTERN (insn), insn); - n_insns += 1; - } - else if (GET_CODE (insn) == CALL_INSN) - { - rtx dest = 0; - rtx x; - register int i; - - /* Any instruction using a hard register which may get clobbered - by a call needs to be marked as dependent on this call. - This prevents a use of a hard return reg from being moved - past a void call (i.e. it does not explicitly set the hard - return reg). */ - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (call_used_regs[i] || global_regs[i]) - { - for (u = reg_last_uses[i]; u; u = XEXP (u, 1)) - if (GET_CODE (PATTERN (XEXP (u, 0))) != USE) - add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); - reg_last_uses[i] = 0; - if (reg_last_sets[i] - && GET_CODE (PATTERN (reg_last_sets[i])) != USE) - add_dependence (insn, reg_last_sets[i], REG_DEP_ANTI); - reg_last_sets[i] = insn; - /* Insn, being a CALL_INSN, magically depends on - `last_function_call' already. */ - } - - /* For each insn which shouldn't cross a call, add a dependence - between that insn and this call insn. */ - x = LOG_LINKS (sched_before_next_call); - while (x) - { - add_dependence (insn, XEXP (x, 0), REG_DEP_ANTI); - x = XEXP (x, 1); - } - LOG_LINKS (sched_before_next_call) = 0; - - sched_analyze_insn (PATTERN (insn), insn); - - /* We don't need to flush memory for a function call which does - not involve memory. */ - if (! CONST_CALL_P (insn)) - { - /* In the absence of interprocedural alias analysis, - we must flush all pending reads and writes, and - start new dependencies starting from here. */ - flush_pending_lists (insn); - } - - /* Depend this function call (actually, the user of this - function call) on all hard register clobberage. */ - last_function_call = insn; - n_insns += 1; - } - - if (insn == tail) - return n_insns; - } -} - -/* Called when we see a set of a register. If death is true, then we are - scanning backwards. Mark that register as unborn. If nobody says - otherwise, that is how things will remain. If death is false, then we - are scanning forwards. Mark that register as being born. */ - -static void -sched_note_set (b, x, death) - int b; - rtx x; - int death; -{ - register int regno, j; - register rtx reg = SET_DEST (x); - int subreg_p = 0; - - if (reg == 0) - return; - - while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == STRICT_LOW_PART - || GET_CODE (reg) == SIGN_EXTRACT || GET_CODE (reg) == ZERO_EXTRACT) - { - /* Must treat modification of just one hardware register of a multi-reg - value or just a byte field of a register exactly the same way that - mark_set_1 in flow.c does, i.e. anything except a paradoxical subreg - does not kill the entire register. */ - if (GET_CODE (reg) != SUBREG - || REG_SIZE (SUBREG_REG (reg)) > REG_SIZE (reg)) - subreg_p = 1; - - reg = SUBREG_REG (reg); - } - - if (GET_CODE (reg) != REG) - return; - - /* Global registers are always live, so the code below does not apply - to them. */ - - regno = REGNO (reg); - if (regno >= FIRST_PSEUDO_REGISTER || ! global_regs[regno]) - { - register int offset = regno / REGSET_ELT_BITS; - register REGSET_ELT_TYPE bit - = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS); - - if (death) - { - /* If we only set part of the register, then this set does not - kill it. */ - if (subreg_p) - return; - - /* Try killing this register. */ - if (regno < FIRST_PSEUDO_REGISTER) - { - int j = HARD_REGNO_NREGS (regno, GET_MODE (reg)); - while (--j >= 0) - { - offset = (regno + j) / REGSET_ELT_BITS; - bit = (REGSET_ELT_TYPE) 1 << ((regno + j) % REGSET_ELT_BITS); - - bb_live_regs[offset] &= ~bit; - bb_dead_regs[offset] |= bit; - } - } - else - { - bb_live_regs[offset] &= ~bit; - bb_dead_regs[offset] |= bit; - } - } - else - { - /* Make the register live again. */ - if (regno < FIRST_PSEUDO_REGISTER) - { - int j = HARD_REGNO_NREGS (regno, GET_MODE (reg)); - while (--j >= 0) - { - offset = (regno + j) / REGSET_ELT_BITS; - bit = (REGSET_ELT_TYPE) 1 << ((regno + j) % REGSET_ELT_BITS); - - bb_live_regs[offset] |= bit; - bb_dead_regs[offset] &= ~bit; - } - } - else - { - bb_live_regs[offset] |= bit; - bb_dead_regs[offset] &= ~bit; - } - } - } -} - -/* Macros and functions for keeping the priority queue sorted, and - dealing with queueing and unqueueing of instructions. */ - -#define SCHED_SORT(READY, NEW_READY, OLD_READY) \ - do { if ((NEW_READY) - (OLD_READY) == 1) \ - swap_sort (READY, NEW_READY); \ - else if ((NEW_READY) - (OLD_READY) > 1) \ - qsort (READY, NEW_READY, sizeof (rtx), rank_for_schedule); } \ - while (0) - -/* Returns a positive value if y is preferred; returns a negative value if - x is preferred. Should never return 0, since that will make the sort - unstable. */ - -static int -rank_for_schedule (x, y) - rtx *x, *y; -{ - rtx tmp = *y; - rtx tmp2 = *x; - rtx link; - int tmp_class, tmp2_class; - int value; - - /* Choose the instruction with the highest priority, if different. */ - if (value = INSN_PRIORITY (tmp) - INSN_PRIORITY (tmp2)) - return value; - - if (last_scheduled_insn) - { - /* Classify the instructions into three classes: - 1) Data dependent on last schedule insn. - 2) Anti/Output dependent on last scheduled insn. - 3) Independent of last scheduled insn, or has latency of one. - Choose the insn from the highest numbered class if different. */ - link = find_insn_list (tmp, LOG_LINKS (last_scheduled_insn)); - if (link == 0 || insn_cost (tmp, link, last_scheduled_insn) == 1) - tmp_class = 3; - else if (REG_NOTE_KIND (link) == 0) /* Data dependence. */ - tmp_class = 1; - else - tmp_class = 2; - - link = find_insn_list (tmp2, LOG_LINKS (last_scheduled_insn)); - if (link == 0 || insn_cost (tmp2, link, last_scheduled_insn) == 1) - tmp2_class = 3; - else if (REG_NOTE_KIND (link) == 0) /* Data dependence. */ - tmp2_class = 1; - else - tmp2_class = 2; - - if (value = tmp_class - tmp2_class) - return value; - } - - /* If insns are equally good, sort by INSN_LUID (original insn order), - so that we make the sort stable. This minimizes instruction movement, - thus minimizing sched's effect on debugging and cross-jumping. */ - return INSN_LUID (tmp) - INSN_LUID (tmp2); -} - -/* Resort the array A in which only element at index N may be out of order. */ - -__inline static void -swap_sort (a, n) - rtx *a; - int n; -{ - rtx insn = a[n-1]; - int i = n-2; - - while (i >= 0 && rank_for_schedule (a+i, &insn) >= 0) - { - a[i+1] = a[i]; - i -= 1; - } - a[i+1] = insn; -} - -static int max_priority; - -/* Add INSN to the insn queue so that it fires at least N_CYCLES - before the currently executing insn. */ - -__inline static void -queue_insn (insn, n_cycles) - rtx insn; - int n_cycles; -{ - int next_q = NEXT_Q_AFTER (q_ptr, n_cycles); - NEXT_INSN (insn) = insn_queue[next_q]; - insn_queue[next_q] = insn; - q_size += 1; -} - -/* Return nonzero if PAT is the pattern of an insn which makes a - register live. */ - -__inline static int -birthing_insn_p (pat) - rtx pat; -{ - int j; - - if (reload_completed == 1) - return 0; - - if (GET_CODE (pat) == SET - && GET_CODE (SET_DEST (pat)) == REG) - { - rtx dest = SET_DEST (pat); - int i = REGNO (dest); - int offset = i / REGSET_ELT_BITS; - REGSET_ELT_TYPE bit = (REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS); - - /* It would be more accurate to use refers_to_regno_p or - reg_mentioned_p to determine when the dest is not live before this - insn. */ - - if (bb_live_regs[offset] & bit) - return (reg_n_sets[i] == 1); - - return 0; - } - if (GET_CODE (pat) == PARALLEL) - { - for (j = 0; j < XVECLEN (pat, 0); j++) - if (birthing_insn_p (XVECEXP (pat, 0, j))) - return 1; - } - return 0; -} - -/* PREV is an insn that is ready to execute. Adjust its priority if that - will help shorten register lifetimes. */ - -__inline static void -adjust_priority (prev) - rtx prev; -{ - /* Trying to shorten register lives after reload has completed - is useless and wrong. It gives inaccurate schedules. */ - if (reload_completed == 0) - { - rtx note; - int n_deaths = 0; - - /* ??? This code has no effect, because REG_DEAD notes are removed - before we ever get here. */ - for (note = REG_NOTES (prev); note; note = XEXP (note, 1)) - if (REG_NOTE_KIND (note) == REG_DEAD) - n_deaths += 1; - - /* Defer scheduling insns which kill registers, since that - shortens register lives. Prefer scheduling insns which - make registers live for the same reason. */ - switch (n_deaths) - { - default: - INSN_PRIORITY (prev) >>= 3; - break; - case 3: - INSN_PRIORITY (prev) >>= 2; - break; - case 2: - case 1: - INSN_PRIORITY (prev) >>= 1; - break; - case 0: - if (birthing_insn_p (PATTERN (prev))) - { - int max = max_priority; - - if (max > INSN_PRIORITY (prev)) - INSN_PRIORITY (prev) = max; - } - break; - } - } -} - -/* INSN is the "currently executing insn". Launch each insn which was - waiting on INSN (in the backwards dataflow sense). READY is a - vector of insns which are ready to fire. N_READY is the number of - elements in READY. CLOCK is the current virtual cycle. */ - -static int -schedule_insn (insn, ready, n_ready, clock) - rtx insn; - rtx *ready; - int n_ready; - int clock; -{ - rtx link; - int new_ready = n_ready; - - if (MAX_BLOCKAGE > 1) - schedule_unit (insn_unit (insn), insn, clock); - - if (LOG_LINKS (insn) == 0) - return n_ready; - - /* This is used by the function adjust_priority above. */ - if (n_ready > 0) - max_priority = MAX (INSN_PRIORITY (ready[0]), INSN_PRIORITY (insn)); - else - max_priority = INSN_PRIORITY (insn); - - for (link = LOG_LINKS (insn); link != 0; link = XEXP (link, 1)) - { - rtx prev = XEXP (link, 0); - int cost = insn_cost (prev, link, insn); - - if ((INSN_REF_COUNT (prev) -= 1) != 0) - { - /* We satisfied one requirement to fire PREV. Record the earliest - time when PREV can fire. No need to do this if the cost is 1, - because PREV can fire no sooner than the next cycle. */ - if (cost > 1) - INSN_TICK (prev) = MAX (INSN_TICK (prev), clock + cost); - } - else - { - /* We satisfied the last requirement to fire PREV. Ensure that all - timing requirements are satisfied. */ - if (INSN_TICK (prev) - clock > cost) - cost = INSN_TICK (prev) - clock; - - /* Adjust the priority of PREV and either put it on the ready - list or queue it. */ - adjust_priority (prev); - if (cost <= 1) - ready[new_ready++] = prev; - else - queue_insn (prev, cost); - } - } - - return new_ready; -} - -/* Given N_READY insns in the ready list READY at time CLOCK, queue - those that are blocked due to function unit hazards and rearrange - the remaining ones to minimize subsequent function unit hazards. */ - -static int -schedule_select (ready, n_ready, clock, file) - rtx *ready; - int n_ready, clock; - FILE *file; -{ - int pri = INSN_PRIORITY (ready[0]); - int i, j, k, q, cost, best_cost, best_insn = 0, new_ready = n_ready; - rtx insn; - - /* Work down the ready list in groups of instructions with the same - priority value. Queue insns in the group that are blocked and - select among those that remain for the one with the largest - potential hazard. */ - for (i = 0; i < n_ready; i = j) - { - int opri = pri; - for (j = i + 1; j < n_ready; j++) - if ((pri = INSN_PRIORITY (ready[j])) != opri) - break; - - /* Queue insns in the group that are blocked. */ - for (k = i, q = 0; k < j; k++) - { - insn = ready[k]; - if ((cost = actual_hazard (insn_unit (insn), insn, clock, 0)) != 0) - { - q++; - ready[k] = 0; - queue_insn (insn, cost); - if (file) - fprintf (file, "\n;; blocking insn %d for %d cycles", - INSN_UID (insn), cost); - } - } - new_ready -= q; - - /* Check the next group if all insns were queued. */ - if (j - i - q == 0) - continue; - - /* If more than one remains, select the first one with the largest - potential hazard. */ - else if (j - i - q > 1) - { - best_cost = -1; - for (k = i; k < j; k++) - { - if ((insn = ready[k]) == 0) - continue; - if ((cost = potential_hazard (insn_unit (insn), insn, 0)) - > best_cost) - { - best_cost = cost; - best_insn = k; - } - } - } - /* We have found a suitable insn to schedule. */ - break; - } - - /* Move the best insn to be front of the ready list. */ - if (best_insn != 0) - { - if (file) - { - fprintf (file, ", now"); - for (i = 0; i < n_ready; i++) - if (ready[i]) - fprintf (file, " %d", INSN_UID (ready[i])); - fprintf (file, "\n;; insn %d has a greater potential hazard", - INSN_UID (ready[best_insn])); - } - for (i = best_insn; i > 0; i--) - { - insn = ready[i-1]; - ready[i-1] = ready[i]; - ready[i] = insn; - } - } - - /* Compact the ready list. */ - if (new_ready < n_ready) - for (i = j = 0; i < n_ready; i++) - if (ready[i]) - ready[j++] = ready[i]; - - return new_ready; -} - -/* Add a REG_DEAD note for REG to INSN, reusing a REG_DEAD note from the - dead_notes list. */ - -static void -create_reg_dead_note (reg, insn) - rtx reg, insn; -{ - rtx link, backlink; - - /* The number of registers killed after scheduling must be the same as the - number of registers killed before scheduling. The number of REG_DEAD - notes may not be conserved, i.e. two SImode hard register REG_DEAD notes - might become one DImode hard register REG_DEAD note, but the number of - registers killed will be conserved. - - We carefully remove REG_DEAD notes from the dead_notes list, so that - there will be none left at the end. If we run out early, then there - is a bug somewhere in flow, combine and/or sched. */ - - if (dead_notes == 0) - { -#if 1 - abort (); -#else - link = rtx_alloc (EXPR_LIST); - PUT_REG_NOTE_KIND (link, REG_DEAD); -#endif - } - else - { - /* Number of regs killed by REG. */ - int regs_killed = (REGNO (reg) >= FIRST_PSEUDO_REGISTER ? 1 - : HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg))); - /* Number of regs killed by REG_DEAD notes taken off the list. */ - int reg_note_regs; - - link = dead_notes; - reg_note_regs = (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1 - : HARD_REGNO_NREGS (REGNO (XEXP (link, 0)), - GET_MODE (XEXP (link, 0)))); - while (reg_note_regs < regs_killed) - { - link = XEXP (link, 1); - reg_note_regs += (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1 - : HARD_REGNO_NREGS (REGNO (XEXP (link, 0)), - GET_MODE (XEXP (link, 0)))); - } - dead_notes = XEXP (link, 1); - - /* If we took too many regs kills off, put the extra ones back. */ - while (reg_note_regs > regs_killed) - { - rtx temp_reg, temp_link; - - temp_reg = gen_rtx (REG, word_mode, 0); - temp_link = rtx_alloc (EXPR_LIST); - PUT_REG_NOTE_KIND (temp_link, REG_DEAD); - XEXP (temp_link, 0) = temp_reg; - XEXP (temp_link, 1) = dead_notes; - dead_notes = temp_link; - reg_note_regs--; - } - } - - XEXP (link, 0) = reg; - XEXP (link, 1) = REG_NOTES (insn); - REG_NOTES (insn) = link; -} - -/* Subroutine on attach_deaths_insn--handles the recursive search - through INSN. If SET_P is true, then x is being modified by the insn. */ - -static void -attach_deaths (x, insn, set_p) - rtx x; - rtx insn; - int set_p; -{ - register int i; - register int j; - register enum rtx_code code; - register char *fmt; - - if (x == 0) - return; - - code = GET_CODE (x); - - switch (code) - { - case CONST_INT: - case CONST_DOUBLE: - case LABEL_REF: - case SYMBOL_REF: - case CONST: - case CODE_LABEL: - case PC: - case CC0: - /* Get rid of the easy cases first. */ - return; - - case REG: - { - /* If the register dies in this insn, queue that note, and mark - this register as needing to die. */ - /* This code is very similar to mark_used_1 (if set_p is false) - and mark_set_1 (if set_p is true) in flow.c. */ - - register int regno = REGNO (x); - register int offset = regno / REGSET_ELT_BITS; - register REGSET_ELT_TYPE bit - = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS); - REGSET_ELT_TYPE all_needed = (old_live_regs[offset] & bit); - REGSET_ELT_TYPE some_needed = (old_live_regs[offset] & bit); - - if (set_p) - return; - - if (regno < FIRST_PSEUDO_REGISTER) - { - int n; - - n = HARD_REGNO_NREGS (regno, GET_MODE (x)); - while (--n > 0) - { - some_needed |= (old_live_regs[(regno + n) / REGSET_ELT_BITS] - & ((REGSET_ELT_TYPE) 1 - << ((regno + n) % REGSET_ELT_BITS))); - all_needed &= (old_live_regs[(regno + n) / REGSET_ELT_BITS] - & ((REGSET_ELT_TYPE) 1 - << ((regno + n) % REGSET_ELT_BITS))); - } - } - - /* If it wasn't live before we started, then add a REG_DEAD note. - We must check the previous lifetime info not the current info, - because we may have to execute this code several times, e.g. - once for a clobber (which doesn't add a note) and later - for a use (which does add a note). - - Always make the register live. We must do this even if it was - live before, because this may be an insn which sets and uses - the same register, in which case the register has already been - killed, so we must make it live again. - - Global registers are always live, and should never have a REG_DEAD - note added for them, so none of the code below applies to them. */ - - if (regno >= FIRST_PSEUDO_REGISTER || ! global_regs[regno]) - { - /* Never add REG_DEAD notes for the FRAME_POINTER_REGNUM or the - STACK_POINTER_REGNUM, since these are always considered to be - live. Similarly for ARG_POINTER_REGNUM if it is fixed. */ - if (regno != FRAME_POINTER_REGNUM -#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM - && ! (regno == ARG_POINTER_REGNUM && fixed_regs[regno]) -#endif - && regno != STACK_POINTER_REGNUM) - { - if (! all_needed && ! dead_or_set_p (insn, x)) - { - /* If none of the words in X is needed, make a REG_DEAD - note. Otherwise, we must make partial REG_DEAD - notes. */ - if (! some_needed) - create_reg_dead_note (x, insn); - else - { - int i; - - /* Don't make a REG_DEAD note for a part of a - register that is set in the insn. */ - for (i = HARD_REGNO_NREGS (regno, GET_MODE (x)) - 1; - i >= 0; i--) - if ((old_live_regs[(regno + i) / REGSET_ELT_BITS] - & ((REGSET_ELT_TYPE) 1 - << ((regno +i) % REGSET_ELT_BITS))) == 0 - && ! dead_or_set_regno_p (insn, regno + i)) - create_reg_dead_note (gen_rtx (REG, word_mode, - regno + i), - insn); - } - } - } - - if (regno < FIRST_PSEUDO_REGISTER) - { - int j = HARD_REGNO_NREGS (regno, GET_MODE (x)); - while (--j >= 0) - { - offset = (regno + j) / REGSET_ELT_BITS; - bit - = (REGSET_ELT_TYPE) 1 << ((regno + j) % REGSET_ELT_BITS); - - bb_dead_regs[offset] &= ~bit; - bb_live_regs[offset] |= bit; - } - } - else - { - bb_dead_regs[offset] &= ~bit; - bb_live_regs[offset] |= bit; - } - } - return; - } - - case MEM: - /* Handle tail-recursive case. */ - attach_deaths (XEXP (x, 0), insn, 0); - return; - - case SUBREG: - case STRICT_LOW_PART: - /* These two cases preserve the value of SET_P, so handle them - separately. */ - attach_deaths (XEXP (x, 0), insn, set_p); - return; - - case ZERO_EXTRACT: - case SIGN_EXTRACT: - /* This case preserves the value of SET_P for the first operand, but - clears it for the other two. */ - attach_deaths (XEXP (x, 0), insn, set_p); - attach_deaths (XEXP (x, 1), insn, 0); - attach_deaths (XEXP (x, 2), insn, 0); - return; - - default: - /* Other cases: walk the insn. */ - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - attach_deaths (XEXP (x, i), insn, 0); - else if (fmt[i] == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - attach_deaths (XVECEXP (x, i, j), insn, 0); - } - } -} - -/* After INSN has executed, add register death notes for each register - that is dead after INSN. */ - -static void -attach_deaths_insn (insn) - rtx insn; -{ - rtx x = PATTERN (insn); - register RTX_CODE code = GET_CODE (x); - - if (code == SET) - { - attach_deaths (SET_SRC (x), insn, 0); - - /* A register might die here even if it is the destination, e.g. - it is the target of a volatile read and is otherwise unused. - Hence we must always call attach_deaths for the SET_DEST. */ - attach_deaths (SET_DEST (x), insn, 1); - } - else if (code == PARALLEL) - { - register int i; - for (i = XVECLEN (x, 0) - 1; i >= 0; i--) - { - code = GET_CODE (XVECEXP (x, 0, i)); - if (code == SET) - { - attach_deaths (SET_SRC (XVECEXP (x, 0, i)), insn, 0); - - attach_deaths (SET_DEST (XVECEXP (x, 0, i)), insn, 1); - } - /* Flow does not add REG_DEAD notes to registers that die in - clobbers, so we can't either. */ - else if (code != CLOBBER) - attach_deaths (XVECEXP (x, 0, i), insn, 0); - } - } - /* Flow does not add REG_DEAD notes to registers that die in - clobbers, so we can't either. */ - else if (code != CLOBBER) - attach_deaths (x, insn, 0); -} - -/* Delete notes beginning with INSN and maybe put them in the chain - of notes ended by NOTE_LIST. - Returns the insn following the notes. */ - -static rtx -unlink_notes (insn, tail) - rtx insn, tail; -{ - rtx prev = PREV_INSN (insn); - - while (insn != tail && GET_CODE (insn) == NOTE) - { - rtx next = NEXT_INSN (insn); - /* Delete the note from its current position. */ - if (prev) - NEXT_INSN (prev) = next; - if (next) - PREV_INSN (next) = prev; - - if (write_symbols != NO_DEBUG && NOTE_LINE_NUMBER (insn) > 0) - /* Record line-number notes so they can be reused. */ - LINE_NOTE (insn) = insn; - else - { - /* Insert the note at the end of the notes list. */ - PREV_INSN (insn) = note_list; - if (note_list) - NEXT_INSN (note_list) = insn; - note_list = insn; - } - - insn = next; - } - return insn; -} - -/* Data structure for keeping track of register information - during that register's life. */ - -struct sometimes -{ - short offset; short bit; - short live_length; short calls_crossed; -}; - -/* Constructor for `sometimes' data structure. */ - -static int -new_sometimes_live (regs_sometimes_live, offset, bit, sometimes_max) - struct sometimes *regs_sometimes_live; - int offset, bit; - int sometimes_max; -{ - register struct sometimes *p; - register int regno = offset * REGSET_ELT_BITS + bit; - int i; - - /* There should never be a register greater than max_regno here. If there - is, it means that a define_split has created a new pseudo reg. This - is not allowed, since there will not be flow info available for any - new register, so catch the error here. */ - if (regno >= max_regno) - abort (); - - p = ®s_sometimes_live[sometimes_max]; - p->offset = offset; - p->bit = bit; - p->live_length = 0; - p->calls_crossed = 0; - sometimes_max++; - return sometimes_max; -} - -/* Count lengths of all regs we are currently tracking, - and find new registers no longer live. */ - -static void -finish_sometimes_live (regs_sometimes_live, sometimes_max) - struct sometimes *regs_sometimes_live; - int sometimes_max; -{ - int i; - - for (i = 0; i < sometimes_max; i++) - { - register struct sometimes *p = ®s_sometimes_live[i]; - int regno; - - regno = p->offset * REGSET_ELT_BITS + p->bit; - - sched_reg_live_length[regno] += p->live_length; - sched_reg_n_calls_crossed[regno] += p->calls_crossed; - } -} - -/* Use modified list scheduling to rearrange insns in basic block - B. FILE, if nonzero, is where we dump interesting output about - this pass. */ - -static void -schedule_block (b, file) - int b; - FILE *file; -{ - rtx insn, last; - rtx last_note = 0; - rtx *ready, link; - int i, j, n_ready = 0, new_ready, n_insns = 0; - int sched_n_insns = 0; - int clock; -#define NEED_NOTHING 0 -#define NEED_HEAD 1 -#define NEED_TAIL 2 - int new_needs; - - /* HEAD and TAIL delimit the region being scheduled. */ - rtx head = basic_block_head[b]; - rtx tail = basic_block_end[b]; - /* PREV_HEAD and NEXT_TAIL are the boundaries of the insns - being scheduled. When the insns have been ordered, - these insns delimit where the new insns are to be - spliced back into the insn chain. */ - rtx next_tail; - rtx prev_head; - - /* Keep life information accurate. */ - register struct sometimes *regs_sometimes_live; - int sometimes_max; - - if (file) - fprintf (file, ";;\t -- basic block number %d from %d to %d --\n", - b, INSN_UID (basic_block_head[b]), INSN_UID (basic_block_end[b])); - - i = max_reg_num (); - reg_last_uses = (rtx *) alloca (i * sizeof (rtx)); - bzero (reg_last_uses, i * sizeof (rtx)); - reg_last_sets = (rtx *) alloca (i * sizeof (rtx)); - bzero (reg_last_sets, i * sizeof (rtx)); - clear_units (); - - /* Remove certain insns at the beginning from scheduling, - by advancing HEAD. */ - - /* At the start of a function, before reload has run, don't delay getting - parameters from hard registers into pseudo registers. */ - if (reload_completed == 0 && b == 0) - { - while (head != tail - && GET_CODE (head) == NOTE - && NOTE_LINE_NUMBER (head) != NOTE_INSN_FUNCTION_BEG) - head = NEXT_INSN (head); - while (head != tail - && GET_CODE (head) == INSN - && GET_CODE (PATTERN (head)) == SET) - { - rtx src = SET_SRC (PATTERN (head)); - while (GET_CODE (src) == SUBREG - || GET_CODE (src) == SIGN_EXTEND - || GET_CODE (src) == ZERO_EXTEND - || GET_CODE (src) == SIGN_EXTRACT - || GET_CODE (src) == ZERO_EXTRACT) - src = XEXP (src, 0); - if (GET_CODE (src) != REG - || REGNO (src) >= FIRST_PSEUDO_REGISTER) - break; - /* Keep this insn from ever being scheduled. */ - INSN_REF_COUNT (head) = 1; - head = NEXT_INSN (head); - } - } - - /* Don't include any notes or labels at the beginning of the - basic block, or notes at the ends of basic blocks. */ - while (head != tail) - { - if (GET_CODE (head) == NOTE) - head = NEXT_INSN (head); - else if (GET_CODE (tail) == NOTE) - tail = PREV_INSN (tail); - else if (GET_CODE (head) == CODE_LABEL) - head = NEXT_INSN (head); - else break; - } - /* If the only insn left is a NOTE or a CODE_LABEL, then there is no need - to schedule this block. */ - if (head == tail - && (GET_CODE (head) == NOTE || GET_CODE (head) == CODE_LABEL)) - return; - -#if 0 - /* This short-cut doesn't work. It does not count call insns crossed by - registers in reg_sometimes_live. It does not mark these registers as - dead if they die in this block. It does not mark these registers live - (or create new reg_sometimes_live entries if necessary) if they are born - in this block. - - The easy solution is to just always schedule a block. This block only - has one insn, so this won't slow down this pass by much. */ - - if (head == tail) - return; -#endif - - /* Now HEAD through TAIL are the insns actually to be rearranged; - Let PREV_HEAD and NEXT_TAIL enclose them. */ - prev_head = PREV_INSN (head); - next_tail = NEXT_INSN (tail); - - /* Initialize basic block data structures. */ - dead_notes = 0; - pending_read_insns = 0; - pending_read_mems = 0; - pending_write_insns = 0; - pending_write_mems = 0; - pending_lists_length = 0; - last_pending_memory_flush = 0; - last_function_call = 0; - last_scheduled_insn = 0; - - LOG_LINKS (sched_before_next_call) = 0; - - n_insns += sched_analyze (head, tail); - if (n_insns == 0) - { - free_pending_lists (); - return; - } - - /* Allocate vector to hold insns to be rearranged (except those - insns which are controlled by an insn with SCHED_GROUP_P set). - All these insns are included between ORIG_HEAD and ORIG_TAIL, - as those variables ultimately are set up. */ - ready = (rtx *) alloca ((n_insns+1) * sizeof (rtx)); - - /* TAIL is now the last of the insns to be rearranged. - Put those insns into the READY vector. */ - insn = tail; - - /* For all branches, calls, uses, and cc0 setters, force them to remain - in order at the end of the block by adding dependencies and giving - the last a high priority. There may be notes present, and prev_head - may also be a note. - - Branches must obviously remain at the end. Calls should remain at the - end since moving them results in worse register allocation. Uses remain - at the end to ensure proper register allocation. cc0 setters remaim - at the end because they can't be moved away from their cc0 user. */ - last = 0; - while (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN - || (GET_CODE (insn) == INSN - && (GET_CODE (PATTERN (insn)) == USE -#ifdef HAVE_cc0 - || sets_cc0_p (PATTERN (insn)) -#endif - )) - || GET_CODE (insn) == NOTE) - { - if (GET_CODE (insn) != NOTE) - { - priority (insn); - if (last == 0) - { - ready[n_ready++] = insn; - INSN_PRIORITY (insn) = TAIL_PRIORITY - i; - INSN_REF_COUNT (insn) = 0; - } - else if (! find_insn_list (insn, LOG_LINKS (last))) - { - add_dependence (last, insn, REG_DEP_ANTI); - INSN_REF_COUNT (insn)++; - } - last = insn; - - /* Skip over insns that are part of a group. */ - while (SCHED_GROUP_P (insn)) - { - insn = prev_nonnote_insn (insn); - priority (insn); - } - } - - insn = PREV_INSN (insn); - /* Don't overrun the bounds of the basic block. */ - if (insn == prev_head) - break; - } - - /* Assign priorities to instructions. Also check whether they - are in priority order already. If so then I will be nonnegative. - We use this shortcut only before reloading. */ -#if 0 - i = reload_completed ? DONE_PRIORITY : MAX_PRIORITY; -#endif - - for (; insn != prev_head; insn = PREV_INSN (insn)) - { - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') - { - priority (insn); - if (INSN_REF_COUNT (insn) == 0) - { - if (last == 0) - ready[n_ready++] = insn; - else - { - /* Make this dependent on the last of the instructions - that must remain in order at the end of the block. */ - add_dependence (last, insn, REG_DEP_ANTI); - INSN_REF_COUNT (insn) = 1; - } - } - if (SCHED_GROUP_P (insn)) - { - while (SCHED_GROUP_P (insn)) - { - insn = PREV_INSN (insn); - while (GET_CODE (insn) == NOTE) - insn = PREV_INSN (insn); - priority (insn); - } - continue; - } -#if 0 - if (i < 0) - continue; - if (INSN_PRIORITY (insn) < i) - i = INSN_PRIORITY (insn); - else if (INSN_PRIORITY (insn) > i) - i = DONE_PRIORITY; -#endif - } - } - -#if 0 - /* This short-cut doesn't work. It does not count call insns crossed by - registers in reg_sometimes_live. It does not mark these registers as - dead if they die in this block. It does not mark these registers live - (or create new reg_sometimes_live entries if necessary) if they are born - in this block. - - The easy solution is to just always schedule a block. These blocks tend - to be very short, so this doesn't slow down this pass by much. */ - - /* If existing order is good, don't bother to reorder. */ - if (i != DONE_PRIORITY) - { - if (file) - fprintf (file, ";; already scheduled\n"); - - if (reload_completed == 0) - { - for (i = 0; i < sometimes_max; i++) - regs_sometimes_live[i].live_length += n_insns; - - finish_sometimes_live (regs_sometimes_live, sometimes_max); - } - free_pending_lists (); - return; - } -#endif - - /* Scan all the insns to be scheduled, removing NOTE insns - and register death notes. - Line number NOTE insns end up in NOTE_LIST. - Register death notes end up in DEAD_NOTES. - - Recreate the register life information for the end of this basic - block. */ - - if (reload_completed == 0) - { - bcopy (basic_block_live_at_start[b], bb_live_regs, regset_bytes); - bzero (bb_dead_regs, regset_bytes); - - if (b == 0) - { - /* This is the first block in the function. There may be insns - before head that we can't schedule. We still need to examine - them though for accurate register lifetime analysis. */ - - /* We don't want to remove any REG_DEAD notes as the code below - does. */ - - for (insn = basic_block_head[b]; insn != head; - insn = NEXT_INSN (insn)) - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') - { - /* See if the register gets born here. */ - /* We must check for registers being born before we check for - registers dying. It is possible for a register to be born - and die in the same insn, e.g. reading from a volatile - memory location into an otherwise unused register. Such - a register must be marked as dead after this insn. */ - if (GET_CODE (PATTERN (insn)) == SET - || GET_CODE (PATTERN (insn)) == CLOBBER) - sched_note_set (b, PATTERN (insn), 0); - else if (GET_CODE (PATTERN (insn)) == PARALLEL) - { - int j; - for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--) - if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET - || GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER) - sched_note_set (b, XVECEXP (PATTERN (insn), 0, j), 0); - - /* ??? This code is obsolete and should be deleted. It - is harmless though, so we will leave it in for now. */ - for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--) - if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == USE) - sched_note_set (b, XVECEXP (PATTERN (insn), 0, j), 0); - } - - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - { - if ((REG_NOTE_KIND (link) == REG_DEAD - || REG_NOTE_KIND (link) == REG_UNUSED) - /* Verify that the REG_NOTE has a legal value. */ - && GET_CODE (XEXP (link, 0)) == REG) - { - register int regno = REGNO (XEXP (link, 0)); - register int offset = regno / REGSET_ELT_BITS; - register REGSET_ELT_TYPE bit - = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS); - - if (regno < FIRST_PSEUDO_REGISTER) - { - int j = HARD_REGNO_NREGS (regno, - GET_MODE (XEXP (link, 0))); - while (--j >= 0) - { - offset = (regno + j) / REGSET_ELT_BITS; - bit = ((REGSET_ELT_TYPE) 1 - << ((regno + j) % REGSET_ELT_BITS)); - - bb_live_regs[offset] &= ~bit; - bb_dead_regs[offset] |= bit; - } - } - else - { - bb_live_regs[offset] &= ~bit; - bb_dead_regs[offset] |= bit; - } - } - } - } - } - } - - /* If debugging information is being produced, keep track of the line - number notes for each insn. */ - if (write_symbols != NO_DEBUG) - { - /* We must use the true line number for the first insn in the block - that was computed and saved at the start of this pass. We can't - use the current line number, because scheduling of the previous - block may have changed the current line number. */ - rtx line = line_note_head[b]; - - for (insn = basic_block_head[b]; - insn != next_tail; - insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0) - line = insn; - else - LINE_NOTE (insn) = line; - } - - for (insn = head; insn != next_tail; insn = NEXT_INSN (insn)) - { - rtx prev, next, link; - - /* Farm out notes. This is needed to keep the debugger from - getting completely deranged. */ - if (GET_CODE (insn) == NOTE) - { - prev = insn; - insn = unlink_notes (insn, next_tail); - if (prev == tail) - abort (); - if (prev == head) - abort (); - if (insn == next_tail) - abort (); - } - - if (reload_completed == 0 - && GET_RTX_CLASS (GET_CODE (insn)) == 'i') - { - /* See if the register gets born here. */ - /* We must check for registers being born before we check for - registers dying. It is possible for a register to be born and - die in the same insn, e.g. reading from a volatile memory - location into an otherwise unused register. Such a register - must be marked as dead after this insn. */ - if (GET_CODE (PATTERN (insn)) == SET - || GET_CODE (PATTERN (insn)) == CLOBBER) - sched_note_set (b, PATTERN (insn), 0); - else if (GET_CODE (PATTERN (insn)) == PARALLEL) - { - int j; - for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--) - if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET - || GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER) - sched_note_set (b, XVECEXP (PATTERN (insn), 0, j), 0); - - /* ??? This code is obsolete and should be deleted. It - is harmless though, so we will leave it in for now. */ - for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--) - if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == USE) - sched_note_set (b, XVECEXP (PATTERN (insn), 0, j), 0); - } - - /* Need to know what registers this insn kills. */ - for (prev = 0, link = REG_NOTES (insn); link; link = next) - { - int regno; - - next = XEXP (link, 1); - if ((REG_NOTE_KIND (link) == REG_DEAD - || REG_NOTE_KIND (link) == REG_UNUSED) - /* Verify that the REG_NOTE has a legal value. */ - && GET_CODE (XEXP (link, 0)) == REG) - { - register int regno = REGNO (XEXP (link, 0)); - register int offset = regno / REGSET_ELT_BITS; - register REGSET_ELT_TYPE bit - = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS); - - /* Only unlink REG_DEAD notes; leave REG_UNUSED notes - alone. */ - if (REG_NOTE_KIND (link) == REG_DEAD) - { - if (prev) - XEXP (prev, 1) = next; - else - REG_NOTES (insn) = next; - XEXP (link, 1) = dead_notes; - dead_notes = link; - } - else - prev = link; - - if (regno < FIRST_PSEUDO_REGISTER) - { - int j = HARD_REGNO_NREGS (regno, - GET_MODE (XEXP (link, 0))); - while (--j >= 0) - { - offset = (regno + j) / REGSET_ELT_BITS; - bit = ((REGSET_ELT_TYPE) 1 - << ((regno + j) % REGSET_ELT_BITS)); - - bb_live_regs[offset] &= ~bit; - bb_dead_regs[offset] |= bit; - } - } - else - { - bb_live_regs[offset] &= ~bit; - bb_dead_regs[offset] |= bit; - } - } - else - prev = link; - } - } - } - - if (reload_completed == 0) - { - /* Keep track of register lives. */ - old_live_regs = (regset) alloca (regset_bytes); - regs_sometimes_live - = (struct sometimes *) alloca (max_regno * sizeof (struct sometimes)); - sometimes_max = 0; - - /* Start with registers live at end. */ - for (j = 0; j < regset_size; j++) - { - REGSET_ELT_TYPE live = bb_live_regs[j]; - old_live_regs[j] = live; - if (live) - { - register REGSET_ELT_TYPE bit; - for (bit = 0; bit < REGSET_ELT_BITS; bit++) - if (live & ((REGSET_ELT_TYPE) 1 << bit)) - sometimes_max = new_sometimes_live (regs_sometimes_live, j, - bit, sometimes_max); - } - } - } - - SCHED_SORT (ready, n_ready, 1); - - if (file) - { - fprintf (file, ";; ready list initially:\n;; "); - for (i = 0; i < n_ready; i++) - fprintf (file, "%d ", INSN_UID (ready[i])); - fprintf (file, "\n\n"); - - for (insn = head; insn != next_tail; insn = NEXT_INSN (insn)) - if (INSN_PRIORITY (insn) > 0) - fprintf (file, ";; insn[%4d]: priority = %4d, ref_count = %4d\n", - INSN_UID (insn), INSN_PRIORITY (insn), - INSN_REF_COUNT (insn)); - } - - /* Now HEAD and TAIL are going to become disconnected - entirely from the insn chain. */ - tail = 0; - - /* Q_SIZE will always be zero here. */ - q_ptr = 0; clock = 0; - bzero (insn_queue, sizeof (insn_queue)); - - /* Now, perform list scheduling. */ - - /* Where we start inserting insns is after TAIL. */ - last = next_tail; - - new_needs = (NEXT_INSN (prev_head) == basic_block_head[b] - ? NEED_HEAD : NEED_NOTHING); - if (PREV_INSN (next_tail) == basic_block_end[b]) - new_needs |= NEED_TAIL; - - new_ready = n_ready; - while (sched_n_insns < n_insns) - { - q_ptr = NEXT_Q (q_ptr); clock++; - - /* Add all pending insns that can be scheduled without stalls to the - ready list. */ - for (insn = insn_queue[q_ptr]; insn; insn = NEXT_INSN (insn)) - { - if (file) - fprintf (file, ";; launching %d before %d with no stalls at T-%d\n", - INSN_UID (insn), INSN_UID (last), clock); - ready[new_ready++] = insn; - q_size -= 1; - } - insn_queue[q_ptr] = 0; - - /* If there are no ready insns, stall until one is ready and add all - of the pending insns at that point to the ready list. */ - if (new_ready == 0) - { - register int stalls; - - for (stalls = 1; stalls < INSN_QUEUE_SIZE; stalls++) - if (insn = insn_queue[NEXT_Q_AFTER (q_ptr, stalls)]) - { - for (; insn; insn = NEXT_INSN (insn)) - { - if (file) - fprintf (file, ";; launching %d before %d with %d stalls at T-%d\n", - INSN_UID (insn), INSN_UID (last), stalls, clock); - ready[new_ready++] = insn; - q_size -= 1; - } - insn_queue[NEXT_Q_AFTER (q_ptr, stalls)] = 0; - break; - } - - q_ptr = NEXT_Q_AFTER (q_ptr, stalls); clock += stalls; - } - - /* There should be some instructions waiting to fire. */ - if (new_ready == 0) - abort (); - - if (file) - { - fprintf (file, ";; ready list at T-%d:", clock); - for (i = 0; i < new_ready; i++) - fprintf (file, " %d (%x)", - INSN_UID (ready[i]), INSN_PRIORITY (ready[i])); - } - - /* Sort the ready list and choose the best insn to schedule. Select - which insn should issue in this cycle and queue those that are - blocked by function unit hazards. - - N_READY holds the number of items that were scheduled the last time, - minus the one instruction scheduled on the last loop iteration; it - is not modified for any other reason in this loop. */ - - SCHED_SORT (ready, new_ready, n_ready); - if (MAX_BLOCKAGE > 1) - { - new_ready = schedule_select (ready, new_ready, clock, file); - if (new_ready == 0) - { - if (file) - fprintf (file, "\n"); - /* We must set n_ready here, to ensure that sorting always - occurs when we come back to the SCHED_SORT line above. */ - n_ready = 0; - continue; - } - } - n_ready = new_ready; - last_scheduled_insn = insn = ready[0]; - - /* The first insn scheduled becomes the new tail. */ - if (tail == 0) - tail = insn; - - if (file) - { - fprintf (file, ", now"); - for (i = 0; i < n_ready; i++) - fprintf (file, " %d", INSN_UID (ready[i])); - fprintf (file, "\n"); - } - - if (DONE_PRIORITY_P (insn)) - abort (); - - if (reload_completed == 0) - { - /* Process this insn, and each insn linked to this one which must - be immediately output after this insn. */ - do - { - /* First we kill registers set by this insn, and then we - make registers used by this insn live. This is the opposite - order used above because we are traversing the instructions - backwards. */ - - /* Strictly speaking, we should scan REG_UNUSED notes and make - every register mentioned there live, however, we will just - kill them again immediately below, so there doesn't seem to - be any reason why we bother to do this. */ - - /* See if this is the last notice we must take of a register. */ - if (GET_CODE (PATTERN (insn)) == SET - || GET_CODE (PATTERN (insn)) == CLOBBER) - sched_note_set (b, PATTERN (insn), 1); - else if (GET_CODE (PATTERN (insn)) == PARALLEL) - { - int j; - for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--) - if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET - || GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER) - sched_note_set (b, XVECEXP (PATTERN (insn), 0, j), 1); - } - - /* This code keeps life analysis information up to date. */ - if (GET_CODE (insn) == CALL_INSN) - { - register struct sometimes *p; - - /* A call kills all call used and global registers, except - for those mentioned in the call pattern which will be - made live again later. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (call_used_regs[i] || global_regs[i]) - { - register int offset = i / REGSET_ELT_BITS; - register REGSET_ELT_TYPE bit - = (REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS); - - bb_live_regs[offset] &= ~bit; - bb_dead_regs[offset] |= bit; - } - - /* Regs live at the time of a call instruction must not - go in a register clobbered by calls. Record this for - all regs now live. Note that insns which are born or - die in a call do not cross a call, so this must be done - after the killings (above) and before the births - (below). */ - p = regs_sometimes_live; - for (i = 0; i < sometimes_max; i++, p++) - if (bb_live_regs[p->offset] - & ((REGSET_ELT_TYPE) 1 << p->bit)) - p->calls_crossed += 1; - } - - /* Make every register used live, and add REG_DEAD notes for - registers which were not live before we started. */ - attach_deaths_insn (insn); - - /* Find registers now made live by that instruction. */ - for (i = 0; i < regset_size; i++) - { - REGSET_ELT_TYPE diff = bb_live_regs[i] & ~old_live_regs[i]; - if (diff) - { - register int bit; - old_live_regs[i] |= diff; - for (bit = 0; bit < REGSET_ELT_BITS; bit++) - if (diff & ((REGSET_ELT_TYPE) 1 << bit)) - sometimes_max - = new_sometimes_live (regs_sometimes_live, i, bit, - sometimes_max); - } - } - - /* Count lengths of all regs we are worrying about now, - and handle registers no longer live. */ - - for (i = 0; i < sometimes_max; i++) - { - register struct sometimes *p = ®s_sometimes_live[i]; - int regno = p->offset*REGSET_ELT_BITS + p->bit; - - p->live_length += 1; - - if ((bb_live_regs[p->offset] - & ((REGSET_ELT_TYPE) 1 << p->bit)) == 0) - { - /* This is the end of one of this register's lifetime - segments. Save the lifetime info collected so far, - and clear its bit in the old_live_regs entry. */ - sched_reg_live_length[regno] += p->live_length; - sched_reg_n_calls_crossed[regno] += p->calls_crossed; - old_live_regs[p->offset] - &= ~((REGSET_ELT_TYPE) 1 << p->bit); - - /* Delete the reg_sometimes_live entry for this reg by - copying the last entry over top of it. */ - *p = regs_sometimes_live[--sometimes_max]; - /* ...and decrement i so that this newly copied entry - will be processed. */ - i--; - } - } - - link = insn; - insn = PREV_INSN (insn); - } - while (SCHED_GROUP_P (link)); - - /* Set INSN back to the insn we are scheduling now. */ - insn = ready[0]; - } - - /* Schedule INSN. Remove it from the ready list. */ - ready += 1; - n_ready -= 1; - - sched_n_insns += 1; - NEXT_INSN (insn) = last; - PREV_INSN (last) = insn; - last = insn; - - /* Everything that precedes INSN now either becomes "ready", if - it can execute immediately before INSN, or "pending", if - there must be a delay. Give INSN high enough priority that - at least one (maybe more) reg-killing insns can be launched - ahead of all others. Mark INSN as scheduled by changing its - priority to -1. */ - INSN_PRIORITY (insn) = LAUNCH_PRIORITY; - new_ready = schedule_insn (insn, ready, n_ready, clock); - INSN_PRIORITY (insn) = DONE_PRIORITY; - - /* Schedule all prior insns that must not be moved. */ - if (SCHED_GROUP_P (insn)) - { - /* Disable these insns from being launched. */ - link = insn; - while (SCHED_GROUP_P (link)) - { - /* Disable these insns from being launched by anybody. */ - link = PREV_INSN (link); - INSN_REF_COUNT (link) = 0; - } - - /* None of these insns can move forward into delay slots. */ - while (SCHED_GROUP_P (insn)) - { - insn = PREV_INSN (insn); - new_ready = schedule_insn (insn, ready, new_ready, clock); - INSN_PRIORITY (insn) = DONE_PRIORITY; - - sched_n_insns += 1; - NEXT_INSN (insn) = last; - PREV_INSN (last) = insn; - last = insn; - } - } - } - if (q_size != 0) - abort (); - - if (reload_completed == 0) - finish_sometimes_live (regs_sometimes_live, sometimes_max); - - /* HEAD is now the first insn in the chain of insns that - been scheduled by the loop above. - TAIL is the last of those insns. */ - head = insn; - - /* NOTE_LIST is the end of a chain of notes previously found - among the insns. Insert them at the beginning of the insns. */ - if (note_list != 0) - { - rtx note_head = note_list; - while (PREV_INSN (note_head)) - note_head = PREV_INSN (note_head); - - PREV_INSN (head) = note_list; - NEXT_INSN (note_list) = head; - head = note_head; - } - - /* There should be no REG_DEAD notes leftover at the end. - In practice, this can occur as the result of bugs in flow, combine.c, - and/or sched.c. The values of the REG_DEAD notes remaining are - meaningless, because dead_notes is just used as a free list. */ -#if 1 - if (dead_notes != 0) - abort (); -#endif - - if (new_needs & NEED_HEAD) - basic_block_head[b] = head; - PREV_INSN (head) = prev_head; - NEXT_INSN (prev_head) = head; - - if (new_needs & NEED_TAIL) - basic_block_end[b] = tail; - NEXT_INSN (tail) = next_tail; - PREV_INSN (next_tail) = tail; - - /* Restore the line-number notes of each insn. */ - if (write_symbols != NO_DEBUG) - { - rtx line, note, prev, new; - int notes = 0; - - head = basic_block_head[b]; - next_tail = NEXT_INSN (basic_block_end[b]); - - /* Determine the current line-number. We want to know the current - line number of the first insn of the block here, in case it is - different from the true line number that was saved earlier. If - different, then we need a line number note before the first insn - of this block. If it happens to be the same, then we don't want to - emit another line number note here. */ - for (line = head; line; line = PREV_INSN (line)) - if (GET_CODE (line) == NOTE && NOTE_LINE_NUMBER (line) > 0) - break; - - /* Walk the insns keeping track of the current line-number and inserting - the line-number notes as needed. */ - for (insn = head; insn != next_tail; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0) - line = insn; - else if (! (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED) - && (note = LINE_NOTE (insn)) != 0 - && note != line - && (line == 0 - || NOTE_LINE_NUMBER (note) != NOTE_LINE_NUMBER (line) - || NOTE_SOURCE_FILE (note) != NOTE_SOURCE_FILE (line))) - { - line = note; - prev = PREV_INSN (insn); - if (LINE_NOTE (note)) - { - /* Re-use the original line-number note. */ - LINE_NOTE (note) = 0; - PREV_INSN (note) = prev; - NEXT_INSN (prev) = note; - PREV_INSN (insn) = note; - NEXT_INSN (note) = insn; - } - else - { - notes++; - new = emit_note_after (NOTE_LINE_NUMBER (note), prev); - NOTE_SOURCE_FILE (new) = NOTE_SOURCE_FILE (note); - } - } - if (file && notes) - fprintf (file, ";; added %d line-number notes\n", notes); - } - - if (file) - { - fprintf (file, ";; total time = %d\n;; new basic block head = %d\n;; new basic block end = %d\n\n", - clock, INSN_UID (basic_block_head[b]), INSN_UID (basic_block_end[b])); - } - - /* Yow! We're done! */ - free_pending_lists (); - - return; -} - -/* Subroutine of split_hard_reg_notes. Searches X for any reference to - REGNO, returning the rtx of the reference found if any. Otherwise, - returns 0. */ - -rtx -regno_use_in (regno, x) - int regno; - rtx x; -{ - register char *fmt; - int i, j; - rtx tem; - - if (GET_CODE (x) == REG && REGNO (x) == regno) - return x; - - fmt = GET_RTX_FORMAT (GET_CODE (x)); - for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - if (tem = regno_use_in (regno, XEXP (x, i))) - return tem; - } - else if (fmt[i] == 'E') - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - if (tem = regno_use_in (regno , XVECEXP (x, i, j))) - return tem; - } - - return 0; -} - -/* Subroutine of update_flow_info. Determines whether any new REG_NOTEs are - needed for the hard register mentioned in the note. This can happen - if the reference to the hard register in the original insn was split into - several smaller hard register references in the split insns. */ - -static void -split_hard_reg_notes (note, first, last, orig_insn) - rtx note, first, last, orig_insn; -{ - rtx reg, temp, link; - int n_regs, i, new_reg; - rtx insn; - - /* Assume that this is a REG_DEAD note. */ - if (REG_NOTE_KIND (note) != REG_DEAD) - abort (); - - reg = XEXP (note, 0); - - n_regs = HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg)); - - for (i = 0; i < n_regs; i++) - { - new_reg = REGNO (reg) + i; - - /* Check for references to new_reg in the split insns. */ - for (insn = last; ; insn = PREV_INSN (insn)) - { - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && (temp = regno_use_in (new_reg, PATTERN (insn)))) - { - /* Create a new reg dead note here. */ - link = rtx_alloc (EXPR_LIST); - PUT_REG_NOTE_KIND (link, REG_DEAD); - XEXP (link, 0) = temp; - XEXP (link, 1) = REG_NOTES (insn); - REG_NOTES (insn) = link; - - /* If killed multiple registers here, then add in the excess. */ - i += HARD_REGNO_NREGS (REGNO (temp), GET_MODE (temp)) - 1; - - break; - } - /* It isn't mentioned anywhere, so no new reg note is needed for - this register. */ - if (insn == first) - break; - } - } -} - -/* Subroutine of update_flow_info. Determines whether a SET or CLOBBER in an - insn created by splitting needs a REG_DEAD or REG_UNUSED note added. */ - -static void -new_insn_dead_notes (pat, insn, last, orig_insn) - rtx pat, insn, last, orig_insn; -{ - rtx dest, tem, set; - - /* PAT is either a CLOBBER or a SET here. */ - dest = XEXP (pat, 0); - - while (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG - || GET_CODE (dest) == STRICT_LOW_PART - || GET_CODE (dest) == SIGN_EXTRACT) - dest = XEXP (dest, 0); - - if (GET_CODE (dest) == REG) - { - for (tem = last; tem != insn; tem = PREV_INSN (tem)) - { - if (GET_RTX_CLASS (GET_CODE (tem)) == 'i' - && reg_overlap_mentioned_p (dest, PATTERN (tem)) - && (set = single_set (tem))) - { - rtx tem_dest = SET_DEST (set); - - while (GET_CODE (tem_dest) == ZERO_EXTRACT - || GET_CODE (tem_dest) == SUBREG - || GET_CODE (tem_dest) == STRICT_LOW_PART - || GET_CODE (tem_dest) == SIGN_EXTRACT) - tem_dest = XEXP (tem_dest, 0); - - if (tem_dest != dest) - { - /* Use the same scheme as combine.c, don't put both REG_DEAD - and REG_UNUSED notes on the same insn. */ - if (! find_regno_note (tem, REG_UNUSED, REGNO (dest)) - && ! find_regno_note (tem, REG_DEAD, REGNO (dest))) - { - rtx note = rtx_alloc (EXPR_LIST); - PUT_REG_NOTE_KIND (note, REG_DEAD); - XEXP (note, 0) = dest; - XEXP (note, 1) = REG_NOTES (tem); - REG_NOTES (tem) = note; - } - /* The reg only dies in one insn, the last one that uses - it. */ - break; - } - else if (reg_overlap_mentioned_p (dest, SET_SRC (set))) - /* We found an instruction that both uses the register, - and sets it, so no new REG_NOTE is needed for this set. */ - break; - } - } - /* If this is a set, it must die somewhere, unless it is the dest of - the original insn, and hence is live after the original insn. Abort - if it isn't supposed to be live after the original insn. - - If this is a clobber, then just add a REG_UNUSED note. */ - if (tem == insn) - { - int live_after_orig_insn = 0; - rtx pattern = PATTERN (orig_insn); - int i; - - if (GET_CODE (pat) == CLOBBER) - { - rtx note = rtx_alloc (EXPR_LIST); - PUT_REG_NOTE_KIND (note, REG_UNUSED); - XEXP (note, 0) = dest; - XEXP (note, 1) = REG_NOTES (insn); - REG_NOTES (insn) = note; - return; - } - - /* The original insn could have multiple sets, so search the - insn for all sets. */ - if (GET_CODE (pattern) == SET) - { - if (reg_overlap_mentioned_p (dest, SET_DEST (pattern))) - live_after_orig_insn = 1; - } - else if (GET_CODE (pattern) == PARALLEL) - { - for (i = 0; i < XVECLEN (pattern, 0); i++) - if (GET_CODE (XVECEXP (pattern, 0, i)) == SET - && reg_overlap_mentioned_p (dest, - SET_DEST (XVECEXP (pattern, - 0, i)))) - live_after_orig_insn = 1; - } - - if (! live_after_orig_insn) - abort (); - } - } -} - -/* Subroutine of update_flow_info. Update the value of reg_n_sets for all - registers modified by X. INC is -1 if the containing insn is being deleted, - and is 1 if the containing insn is a newly generated insn. */ - -static void -update_n_sets (x, inc) - rtx x; - int inc; -{ - rtx dest = SET_DEST (x); - - while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SUBREG - || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT) - dest = SUBREG_REG (dest); - - if (GET_CODE (dest) == REG) - { - int regno = REGNO (dest); - - if (regno < FIRST_PSEUDO_REGISTER) - { - register int i; - int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (dest)); - - for (i = regno; i < endregno; i++) - reg_n_sets[i] += inc; - } - else - reg_n_sets[regno] += inc; - } -} - -/* Updates all flow-analysis related quantities (including REG_NOTES) for - the insns from FIRST to LAST inclusive that were created by splitting - ORIG_INSN. NOTES are the original REG_NOTES. */ - -static void -update_flow_info (notes, first, last, orig_insn) - rtx notes; - rtx first, last; - rtx orig_insn; -{ - rtx insn, note; - rtx next; - rtx orig_dest, temp; - rtx set; - - /* Get and save the destination set by the original insn. */ - - orig_dest = single_set (orig_insn); - if (orig_dest) - orig_dest = SET_DEST (orig_dest); - - /* Move REG_NOTES from the original insn to where they now belong. */ - - for (note = notes; note; note = next) - { - next = XEXP (note, 1); - switch (REG_NOTE_KIND (note)) - { - case REG_DEAD: - case REG_UNUSED: - /* Move these notes from the original insn to the last new insn where - the register is now set. */ - - for (insn = last; ; insn = PREV_INSN (insn)) - { - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && reg_mentioned_p (XEXP (note, 0), PATTERN (insn))) - { - /* If this note refers to a multiple word hard register, it - may have been split into several smaller hard register - references, so handle it specially. */ - temp = XEXP (note, 0); - if (REG_NOTE_KIND (note) == REG_DEAD - && GET_CODE (temp) == REG - && REGNO (temp) < FIRST_PSEUDO_REGISTER - && HARD_REGNO_NREGS (REGNO (temp), GET_MODE (temp)) > 1) - split_hard_reg_notes (note, first, last, orig_insn); - else - { - XEXP (note, 1) = REG_NOTES (insn); - REG_NOTES (insn) = note; - } - - /* Sometimes need to convert REG_UNUSED notes to REG_DEAD - notes. */ - /* ??? This won't handle multiple word registers correctly, - but should be good enough for now. */ - if (REG_NOTE_KIND (note) == REG_UNUSED - && ! dead_or_set_p (insn, XEXP (note, 0))) - PUT_REG_NOTE_KIND (note, REG_DEAD); - - /* The reg only dies in one insn, the last one that uses - it. */ - break; - } - /* It must die somewhere, fail it we couldn't find where it died. - - If this is a REG_UNUSED note, then it must be a temporary - register that was not needed by this instantiation of the - pattern, so we can safely ignore it. */ - if (insn == first) - { - if (REG_NOTE_KIND (note) != REG_UNUSED) - abort (); - - break; - } - } - break; - - case REG_WAS_0: - /* This note applies to the dest of the original insn. Find the - first new insn that now has the same dest, and move the note - there. */ - - if (! orig_dest) - abort (); - - for (insn = first; ; insn = NEXT_INSN (insn)) - { - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && (temp = single_set (insn)) - && rtx_equal_p (SET_DEST (temp), orig_dest)) - { - XEXP (note, 1) = REG_NOTES (insn); - REG_NOTES (insn) = note; - /* The reg is only zero before one insn, the first that - uses it. */ - break; - } - /* It must be set somewhere, fail if we couldn't find where it - was set. */ - if (insn == last) - abort (); - } - break; - - case REG_EQUAL: - case REG_EQUIV: - /* A REG_EQUIV or REG_EQUAL note on an insn with more than one - set is meaningless. Just drop the note. */ - if (! orig_dest) - break; - - case REG_NO_CONFLICT: - /* These notes apply to the dest of the original insn. Find the last - new insn that now has the same dest, and move the note there. */ - - if (! orig_dest) - abort (); - - for (insn = last; ; insn = PREV_INSN (insn)) - { - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && (temp = single_set (insn)) - && rtx_equal_p (SET_DEST (temp), orig_dest)) - { - XEXP (note, 1) = REG_NOTES (insn); - REG_NOTES (insn) = note; - /* Only put this note on one of the new insns. */ - break; - } - - /* The original dest must still be set someplace. Abort if we - couldn't find it. */ - if (insn == first) - abort (); - } - break; - - case REG_LIBCALL: - /* Move a REG_LIBCALL note to the first insn created, and update - the corresponding REG_RETVAL note. */ - XEXP (note, 1) = REG_NOTES (first); - REG_NOTES (first) = note; - - insn = XEXP (note, 0); - note = find_reg_note (insn, REG_RETVAL, NULL_RTX); - if (note) - XEXP (note, 0) = first; - break; - - case REG_RETVAL: - /* Move a REG_RETVAL note to the last insn created, and update - the corresponding REG_LIBCALL note. */ - XEXP (note, 1) = REG_NOTES (last); - REG_NOTES (last) = note; - - insn = XEXP (note, 0); - note = find_reg_note (insn, REG_LIBCALL, NULL_RTX); - if (note) - XEXP (note, 0) = last; - break; - - case REG_NONNEG: - /* This should be moved to whichever instruction is a JUMP_INSN. */ - - for (insn = last; ; insn = PREV_INSN (insn)) - { - if (GET_CODE (insn) == JUMP_INSN) - { - XEXP (note, 1) = REG_NOTES (insn); - REG_NOTES (insn) = note; - /* Only put this note on one of the new insns. */ - break; - } - /* Fail if we couldn't find a JUMP_INSN. */ - if (insn == first) - abort (); - } - break; - - case REG_INC: - /* This should be moved to whichever instruction now has the - increment operation. */ - abort (); - - case REG_LABEL: - /* Should be moved to the new insn(s) which use the label. */ - for (insn = first; insn != NEXT_INSN (last); insn = NEXT_INSN (insn)) - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && reg_mentioned_p (XEXP (note, 0), PATTERN (insn))) - REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_LABEL, - XEXP (note, 0), REG_NOTES (insn)); - break; - - case REG_CC_SETTER: - case REG_CC_USER: - /* These two notes will never appear until after reorg, so we don't - have to handle them here. */ - default: - abort (); - } - } - - /* Each new insn created, except the last, has a new set. If the destination - is a register, then this reg is now live across several insns, whereas - previously the dest reg was born and died within the same insn. To - reflect this, we now need a REG_DEAD note on the insn where this - dest reg dies. - - Similarly, the new insns may have clobbers that need REG_UNUSED notes. */ - - for (insn = first; insn != last; insn = NEXT_INSN (insn)) - { - rtx pat; - int i; - - pat = PATTERN (insn); - if (GET_CODE (pat) == SET || GET_CODE (pat) == CLOBBER) - new_insn_dead_notes (pat, insn, last, orig_insn); - else if (GET_CODE (pat) == PARALLEL) - { - for (i = 0; i < XVECLEN (pat, 0); i++) - if (GET_CODE (XVECEXP (pat, 0, i)) == SET - || GET_CODE (XVECEXP (pat, 0, i)) == CLOBBER) - new_insn_dead_notes (XVECEXP (pat, 0, i), insn, last, orig_insn); - } - } - - /* If any insn, except the last, uses the register set by the last insn, - then we need a new REG_DEAD note on that insn. In this case, there - would not have been a REG_DEAD note for this register in the original - insn because it was used and set within one insn. - - There is no new REG_DEAD note needed if the last insn uses the register - that it is setting. */ - - set = single_set (last); - if (set) - { - rtx dest = SET_DEST (set); - - while (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG - || GET_CODE (dest) == STRICT_LOW_PART - || GET_CODE (dest) == SIGN_EXTRACT) - dest = XEXP (dest, 0); - - if (GET_CODE (dest) == REG - && ! reg_overlap_mentioned_p (dest, SET_SRC (set))) - { - for (insn = PREV_INSN (last); ; insn = PREV_INSN (insn)) - { - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && reg_mentioned_p (dest, PATTERN (insn)) - && (set = single_set (insn))) - { - rtx insn_dest = SET_DEST (set); - - while (GET_CODE (insn_dest) == ZERO_EXTRACT - || GET_CODE (insn_dest) == SUBREG - || GET_CODE (insn_dest) == STRICT_LOW_PART - || GET_CODE (insn_dest) == SIGN_EXTRACT) - insn_dest = XEXP (insn_dest, 0); - - if (insn_dest != dest) - { - note = rtx_alloc (EXPR_LIST); - PUT_REG_NOTE_KIND (note, REG_DEAD); - XEXP (note, 0) = dest; - XEXP (note, 1) = REG_NOTES (insn); - REG_NOTES (insn) = note; - /* The reg only dies in one insn, the last one - that uses it. */ - break; - } - } - if (insn == first) - break; - } - } - } - - /* If the original dest is modifying a multiple register target, and the - original instruction was split such that the original dest is now set - by two or more SUBREG sets, then the split insns no longer kill the - destination of the original insn. - - In this case, if there exists an instruction in the same basic block, - before the split insn, which uses the original dest, and this use is - killed by the original insn, then we must remove the REG_DEAD note on - this insn, because it is now superfluous. - - This does not apply when a hard register gets split, because the code - knows how to handle overlapping hard registers properly. */ - if (orig_dest && GET_CODE (orig_dest) == REG) - { - int found_orig_dest = 0; - int found_split_dest = 0; - - for (insn = first; ; insn = NEXT_INSN (insn)) - { - set = single_set (insn); - if (set) - { - if (GET_CODE (SET_DEST (set)) == REG - && REGNO (SET_DEST (set)) == REGNO (orig_dest)) - { - found_orig_dest = 1; - break; - } - else if (GET_CODE (SET_DEST (set)) == SUBREG - && SUBREG_REG (SET_DEST (set)) == orig_dest) - { - found_split_dest = 1; - break; - } - } - - if (insn == last) - break; - } - - if (found_split_dest) - { - /* Search backwards from FIRST, looking for the first insn that uses - the original dest. Stop if we pass a CODE_LABEL or a JUMP_INSN. - If we find an insn, and it has a REG_DEAD note, then delete the - note. */ - - for (insn = first; insn; insn = PREV_INSN (insn)) - { - if (GET_CODE (insn) == CODE_LABEL - || GET_CODE (insn) == JUMP_INSN) - break; - else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && reg_mentioned_p (orig_dest, insn)) - { - note = find_regno_note (insn, REG_DEAD, REGNO (orig_dest)); - if (note) - remove_note (insn, note); - } - } - } - else if (! found_orig_dest) - { - /* This should never happen. */ - abort (); - } - } - - /* Update reg_n_sets. This is necessary to prevent local alloc from - converting REG_EQUAL notes to REG_EQUIV when splitting has modified - a reg from set once to set multiple times. */ - - { - rtx x = PATTERN (orig_insn); - RTX_CODE code = GET_CODE (x); - - if (code == SET || code == CLOBBER) - update_n_sets (x, -1); - else if (code == PARALLEL) - { - int i; - for (i = XVECLEN (x, 0) - 1; i >= 0; i--) - { - code = GET_CODE (XVECEXP (x, 0, i)); - if (code == SET || code == CLOBBER) - update_n_sets (XVECEXP (x, 0, i), -1); - } - } - - for (insn = first; ; insn = NEXT_INSN (insn)) - { - x = PATTERN (insn); - code = GET_CODE (x); - - if (code == SET || code == CLOBBER) - update_n_sets (x, 1); - else if (code == PARALLEL) - { - int i; - for (i = XVECLEN (x, 0) - 1; i >= 0; i--) - { - code = GET_CODE (XVECEXP (x, 0, i)); - if (code == SET || code == CLOBBER) - update_n_sets (XVECEXP (x, 0, i), 1); - } - } - - if (insn == last) - break; - } - } -} - -/* The one entry point in this file. DUMP_FILE is the dump file for - this pass. */ - -void -schedule_insns (dump_file) - FILE *dump_file; -{ - int max_uid = MAX_INSNS_PER_SPLIT * (get_max_uid () + 1); - int i, b; - rtx insn; - - /* Taking care of this degenerate case makes the rest of - this code simpler. */ - if (n_basic_blocks == 0) - return; - - /* Create an insn here so that we can hang dependencies off of it later. */ - sched_before_next_call - = gen_rtx (INSN, VOIDmode, 0, NULL_RTX, NULL_RTX, - NULL_RTX, 0, NULL_RTX, 0); - - /* Initialize the unused_*_lists. We can't use the ones left over from - the previous function, because gcc has freed that memory. We can use - the ones left over from the first sched pass in the second pass however, - so only clear them on the first sched pass. The first pass is before - reload if flag_schedule_insns is set, otherwise it is afterwards. */ - - if (reload_completed == 0 || ! flag_schedule_insns) - { - unused_insn_list = 0; - unused_expr_list = 0; - } - - /* We create no insns here, only reorder them, so we - remember how far we can cut back the stack on exit. */ - - /* Allocate data for this pass. See comments, above, - for what these vectors do. */ - insn_luid = (int *) alloca (max_uid * sizeof (int)); - insn_priority = (int *) alloca (max_uid * sizeof (int)); - insn_tick = (int *) alloca (max_uid * sizeof (int)); - insn_costs = (short *) alloca (max_uid * sizeof (short)); - insn_units = (short *) alloca (max_uid * sizeof (short)); - insn_blockage = (unsigned int *) alloca (max_uid * sizeof (unsigned int)); - insn_ref_count = (int *) alloca (max_uid * sizeof (int)); - - if (reload_completed == 0) - { - sched_reg_n_deaths = (short *) alloca (max_regno * sizeof (short)); - sched_reg_n_calls_crossed = (int *) alloca (max_regno * sizeof (int)); - sched_reg_live_length = (int *) alloca (max_regno * sizeof (int)); - bb_dead_regs = (regset) alloca (regset_bytes); - bb_live_regs = (regset) alloca (regset_bytes); - bzero (sched_reg_n_calls_crossed, max_regno * sizeof (int)); - bzero (sched_reg_live_length, max_regno * sizeof (int)); - bcopy (reg_n_deaths, sched_reg_n_deaths, max_regno * sizeof (short)); - init_alias_analysis (); - } - else - { - sched_reg_n_deaths = 0; - sched_reg_n_calls_crossed = 0; - sched_reg_live_length = 0; - bb_dead_regs = 0; - bb_live_regs = 0; - if (! flag_schedule_insns) - init_alias_analysis (); - } - - if (write_symbols != NO_DEBUG) - { - rtx line; - - line_note = (rtx *) alloca (max_uid * sizeof (rtx)); - bzero (line_note, max_uid * sizeof (rtx)); - line_note_head = (rtx *) alloca (n_basic_blocks * sizeof (rtx)); - bzero (line_note_head, n_basic_blocks * sizeof (rtx)); - - /* Determine the line-number at the start of each basic block. - This must be computed and saved now, because after a basic block's - predecessor has been scheduled, it is impossible to accurately - determine the correct line number for the first insn of the block. */ - - for (b = 0; b < n_basic_blocks; b++) - for (line = basic_block_head[b]; line; line = PREV_INSN (line)) - if (GET_CODE (line) == NOTE && NOTE_LINE_NUMBER (line) > 0) - { - line_note_head[b] = line; - break; - } - } - - bzero (insn_luid, max_uid * sizeof (int)); - bzero (insn_priority, max_uid * sizeof (int)); - bzero (insn_tick, max_uid * sizeof (int)); - bzero (insn_costs, max_uid * sizeof (short)); - bzero (insn_units, max_uid * sizeof (short)); - bzero (insn_blockage, max_uid * sizeof (unsigned int)); - bzero (insn_ref_count, max_uid * sizeof (int)); - - /* Schedule each basic block, block by block. */ - - if (NEXT_INSN (basic_block_end[n_basic_blocks-1]) == 0 - || (GET_CODE (basic_block_end[n_basic_blocks-1]) != NOTE - && GET_CODE (basic_block_end[n_basic_blocks-1]) != CODE_LABEL)) - emit_note_after (NOTE_INSN_DELETED, basic_block_end[n_basic_blocks-1]); - - for (b = 0; b < n_basic_blocks; b++) - { - rtx insn, next; - rtx insns; - - note_list = 0; - - for (insn = basic_block_head[b]; ; insn = next) - { - rtx prev; - rtx set; - - /* Can't use `next_real_insn' because that - might go across CODE_LABELS and short-out basic blocks. */ - next = NEXT_INSN (insn); - if (GET_CODE (insn) != INSN) - { - if (insn == basic_block_end[b]) - break; - - continue; - } - - /* Don't split no-op move insns. These should silently disappear - later in final. Splitting such insns would break the code - that handles REG_NO_CONFLICT blocks. */ - set = single_set (insn); - if (set && rtx_equal_p (SET_SRC (set), SET_DEST (set))) - { - if (insn == basic_block_end[b]) - break; - - /* Nops get in the way while scheduling, so delete them now if - register allocation has already been done. It is too risky - to try to do this before register allocation, and there are - unlikely to be very many nops then anyways. */ - if (reload_completed) - { - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - } - - continue; - } - - /* Split insns here to get max fine-grain parallelism. */ - prev = PREV_INSN (insn); - if (reload_completed == 0) - { - rtx last, first = PREV_INSN (insn); - rtx notes = REG_NOTES (insn); - - last = try_split (PATTERN (insn), insn, 1); - if (last != insn) - { - /* try_split returns the NOTE that INSN became. */ - first = NEXT_INSN (first); - update_flow_info (notes, first, last, insn); - - PUT_CODE (insn, NOTE); - NOTE_SOURCE_FILE (insn) = 0; - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - if (insn == basic_block_head[b]) - basic_block_head[b] = first; - if (insn == basic_block_end[b]) - { - basic_block_end[b] = last; - break; - } - } - } - - if (insn == basic_block_end[b]) - break; - } - - schedule_block (b, dump_file); - -#ifdef USE_C_ALLOCA - alloca (0); -#endif - } - - /* Reposition the prologue and epilogue notes in case we moved the - prologue/epilogue insns. */ - if (reload_completed) - reposition_prologue_and_epilogue_notes (get_insns ()); - - if (write_symbols != NO_DEBUG) - { - rtx line = 0; - rtx insn = get_insns (); - int active_insn = 0; - int notes = 0; - - /* Walk the insns deleting redundant line-number notes. Many of these - are already present. The remainder tend to occur at basic - block boundaries. */ - for (insn = get_last_insn (); insn; insn = PREV_INSN (insn)) - if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0) - { - /* If there are no active insns following, INSN is redundant. */ - if (active_insn == 0) - { - notes++; - NOTE_SOURCE_FILE (insn) = 0; - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - } - /* If the line number is unchanged, LINE is redundant. */ - else if (line - && NOTE_LINE_NUMBER (line) == NOTE_LINE_NUMBER (insn) - && NOTE_SOURCE_FILE (line) == NOTE_SOURCE_FILE (insn)) - { - notes++; - NOTE_SOURCE_FILE (line) = 0; - NOTE_LINE_NUMBER (line) = NOTE_INSN_DELETED; - line = insn; - } - else - line = insn; - active_insn = 0; - } - else if (! ((GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED) - || (GET_CODE (insn) == INSN - && (GET_CODE (PATTERN (insn)) == USE - || GET_CODE (PATTERN (insn)) == CLOBBER)))) - active_insn++; - - if (dump_file && notes) - fprintf (dump_file, ";; deleted %d line-number notes\n", notes); - } - - if (reload_completed == 0) - { - int regno; - for (regno = 0; regno < max_regno; regno++) - if (sched_reg_live_length[regno]) - { - if (dump_file) - { - if (reg_live_length[regno] > sched_reg_live_length[regno]) - fprintf (dump_file, - ";; register %d life shortened from %d to %d\n", - regno, reg_live_length[regno], - sched_reg_live_length[regno]); - /* Negative values are special; don't overwrite the current - reg_live_length value if it is negative. */ - else if (reg_live_length[regno] < sched_reg_live_length[regno] - && reg_live_length[regno] >= 0) - fprintf (dump_file, - ";; register %d life extended from %d to %d\n", - regno, reg_live_length[regno], - sched_reg_live_length[regno]); - - if (reg_n_calls_crossed[regno] - && ! sched_reg_n_calls_crossed[regno]) - fprintf (dump_file, - ";; register %d no longer crosses calls\n", regno); - else if (! reg_n_calls_crossed[regno] - && sched_reg_n_calls_crossed[regno]) - fprintf (dump_file, - ";; register %d now crosses calls\n", regno); - } - /* Negative values are special; don't overwrite the current - reg_live_length value if it is negative. */ - if (reg_live_length[regno] >= 0) - reg_live_length[regno] = sched_reg_live_length[regno]; - reg_n_calls_crossed[regno] = sched_reg_n_calls_crossed[regno]; - } - } -} -#endif /* INSN_SCHEDULING */ diff --git a/gnu/usr.bin/cc/common/stmt.c b/gnu/usr.bin/cc/common/stmt.c deleted file mode 100644 index 737e217fcf..0000000000 --- a/gnu/usr.bin/cc/common/stmt.c +++ /dev/null @@ -1,4743 +0,0 @@ -/* Expands front end tree to back end RTL for GNU C-Compiler - Copyright (C) 1987, 1988, 1989, 1992 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This file handles the generation of rtl code from tree structure - above the level of expressions, using subroutines in exp*.c and emit-rtl.c. - It also creates the rtl expressions for parameters and auto variables - and has full responsibility for allocating stack slots. - - The functions whose names start with `expand_' are called by the - parser to generate RTL instructions for various kinds of constructs. - - Some control and binding constructs require calling several such - functions at different times. For example, a simple if-then - is expanded by calling `expand_start_cond' (with the condition-expression - as argument) before parsing the then-clause and calling `expand_end_cond' - after parsing the then-clause. */ - -#include "config.h" - -#include -#include - -#include "rtl.h" -#include "tree.h" -#include "flags.h" -#include "function.h" -#include "insn-flags.h" -#include "insn-config.h" -#include "insn-codes.h" -#include "expr.h" -#include "hard-reg-set.h" -#include "obstack.h" -#include "loop.h" -#include "recog.h" - -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free -struct obstack stmt_obstack; - -/* Filename and line number of last line-number note, - whether we actually emitted it or not. */ -char *emit_filename; -int emit_lineno; - -/* Nonzero if within a ({...}) grouping, in which case we must - always compute a value for each expr-stmt in case it is the last one. */ - -int expr_stmts_for_value; - -/* Each time we expand an expression-statement, - record the expr's type and its RTL value here. */ - -static tree last_expr_type; -static rtx last_expr_value; - -/* Each time we expand the end of a binding contour (in `expand_end_bindings') - and we emit a new NOTE_INSN_BLOCK_END note, we save a pointer to it here. - This is used by the `remember_end_note' function to record the endpoint - of each generated block in its associated BLOCK node. */ - -static rtx last_block_end_note; - -/* Number of binding contours started so far in this function. */ - -int block_start_count; - -/* Nonzero if function being compiled needs to - return the address of where it has put a structure value. */ - -extern int current_function_returns_pcc_struct; - -/* Label that will go on parm cleanup code, if any. - Jumping to this label runs cleanup code for parameters, if - such code must be run. Following this code is the logical return label. */ - -extern rtx cleanup_label; - -/* Label that will go on function epilogue. - Jumping to this label serves as a "return" instruction - on machines which require execution of the epilogue on all returns. */ - -extern rtx return_label; - -/* List (chain of EXPR_LISTs) of pseudo-regs of SAVE_EXPRs. - So we can mark them all live at the end of the function, if nonopt. */ -extern rtx save_expr_regs; - -/* Offset to end of allocated area of stack frame. - If stack grows down, this is the address of the last stack slot allocated. - If stack grows up, this is the address for the next slot. */ -extern int frame_offset; - -/* Label to jump back to for tail recursion, or 0 if we have - not yet needed one for this function. */ -extern rtx tail_recursion_label; - -/* Place after which to insert the tail_recursion_label if we need one. */ -extern rtx tail_recursion_reentry; - -/* Location at which to save the argument pointer if it will need to be - referenced. There are two cases where this is done: if nonlocal gotos - exist, or if vars whose is an offset from the argument pointer will be - needed by inner routines. */ - -extern rtx arg_pointer_save_area; - -/* Chain of all RTL_EXPRs that have insns in them. */ -extern tree rtl_expr_chain; - -#if 0 /* Turned off because 0 seems to work just as well. */ -/* Cleanup lists are required for binding levels regardless of whether - that binding level has cleanups or not. This node serves as the - cleanup list whenever an empty list is required. */ -static tree empty_cleanup_list; -#endif - -/* Functions and data structures for expanding case statements. */ - -/* Case label structure, used to hold info on labels within case - statements. We handle "range" labels; for a single-value label - as in C, the high and low limits are the same. - - A chain of case nodes is initially maintained via the RIGHT fields - in the nodes. Nodes with higher case values are later in the list. - - Switch statements can be output in one of two forms. A branch table - is used if there are more than a few labels and the labels are dense - within the range between the smallest and largest case value. If a - branch table is used, no further manipulations are done with the case - node chain. - - The alternative to the use of a branch table is to generate a series - of compare and jump insns. When that is done, we use the LEFT, RIGHT, - and PARENT fields to hold a binary tree. Initially the tree is - totally unbalanced, with everything on the right. We balance the tree - with nodes on the left having lower case values than the parent - and nodes on the right having higher values. We then output the tree - in order. */ - -struct case_node -{ - struct case_node *left; /* Left son in binary tree */ - struct case_node *right; /* Right son in binary tree; also node chain */ - struct case_node *parent; /* Parent of node in binary tree */ - tree low; /* Lowest index value for this label */ - tree high; /* Highest index value for this label */ - tree code_label; /* Label to jump to when node matches */ -}; - -typedef struct case_node case_node; -typedef struct case_node *case_node_ptr; - -/* These are used by estimate_case_costs and balance_case_nodes. */ - -/* This must be a signed type, and non-ANSI compilers lack signed char. */ -static short *cost_table; -static int use_cost_table; - -static int estimate_case_costs (); -static void balance_case_nodes (); -static void emit_case_nodes (); -static void group_case_nodes (); -static void emit_jump_if_reachable (); - -static int warn_if_unused_value (); -static void expand_goto_internal (); -static int expand_fixup (); -void fixup_gotos (); -void free_temp_slots (); -static void expand_cleanups (); -static void expand_null_return_1 (); -static int tail_recursion_args (); -static void do_jump_if_equal (); - -/* Stack of control and binding constructs we are currently inside. - - These constructs begin when you call `expand_start_WHATEVER' - and end when you call `expand_end_WHATEVER'. This stack records - info about how the construct began that tells the end-function - what to do. It also may provide information about the construct - to alter the behavior of other constructs within the body. - For example, they may affect the behavior of C `break' and `continue'. - - Each construct gets one `struct nesting' object. - All of these objects are chained through the `all' field. - `nesting_stack' points to the first object (innermost construct). - The position of an entry on `nesting_stack' is in its `depth' field. - - Each type of construct has its own individual stack. - For example, loops have `loop_stack'. Each object points to the - next object of the same type through the `next' field. - - Some constructs are visible to `break' exit-statements and others - are not. Which constructs are visible depends on the language. - Therefore, the data structure allows each construct to be visible - or not, according to the args given when the construct is started. - The construct is visible if the `exit_label' field is non-null. - In that case, the value should be a CODE_LABEL rtx. */ - -struct nesting -{ - struct nesting *all; - struct nesting *next; - int depth; - rtx exit_label; - union - { - /* For conds (if-then and if-then-else statements). */ - struct - { - /* Label for the end of the if construct. - There is none if EXITFLAG was not set - and no `else' has been seen yet. */ - rtx endif_label; - /* Label for the end of this alternative. - This may be the end of the if or the next else/elseif. */ - rtx next_label; - } cond; - /* For loops. */ - struct - { - /* Label at the top of the loop; place to loop back to. */ - rtx start_label; - /* Label at the end of the whole construct. */ - rtx end_label; - /* Label for `continue' statement to jump to; - this is in front of the stepper of the loop. */ - rtx continue_label; - } loop; - /* For variable binding contours. */ - struct - { - /* Sequence number of this binding contour within the function, - in order of entry. */ - int block_start_count; - /* Nonzero => value to restore stack to on exit. */ - rtx stack_level; - /* The NOTE that starts this contour. - Used by expand_goto to check whether the destination - is within each contour or not. */ - rtx first_insn; - /* Innermost containing binding contour that has a stack level. */ - struct nesting *innermost_stack_block; - /* List of cleanups to be run on exit from this contour. - This is a list of expressions to be evaluated. - The TREE_PURPOSE of each link is the ..._DECL node - which the cleanup pertains to. */ - tree cleanups; - /* List of cleanup-lists of blocks containing this block, - as they were at the locus where this block appears. - There is an element for each containing block, - ordered innermost containing block first. - The tail of this list can be 0 (was empty_cleanup_list), - if all remaining elements would be empty lists. - The element's TREE_VALUE is the cleanup-list of that block, - which may be null. */ - tree outer_cleanups; - /* Chain of labels defined inside this binding contour. - For contours that have stack levels or cleanups. */ - struct label_chain *label_chain; - /* Number of function calls seen, as of start of this block. */ - int function_call_count; - } block; - /* For switch (C) or case (Pascal) statements, - and also for dummies (see `expand_start_case_dummy'). */ - struct - { - /* The insn after which the case dispatch should finally - be emitted. Zero for a dummy. */ - rtx start; - /* A list of case labels, kept in ascending order by value - as the list is built. - During expand_end_case, this list may be rearranged into a - nearly balanced binary tree. */ - struct case_node *case_list; - /* Label to jump to if no case matches. */ - tree default_label; - /* The expression to be dispatched on. */ - tree index_expr; - /* Type that INDEX_EXPR should be converted to. */ - tree nominal_type; - /* Number of range exprs in case statement. */ - int num_ranges; - /* Name of this kind of statement, for warnings. */ - char *printname; - /* Nonzero if a case label has been seen in this case stmt. */ - char seenlabel; - } case_stmt; - /* For exception contours. */ - struct - { - /* List of exceptions raised. This is a TREE_LIST - of whatever you want. */ - tree raised; - /* List of exceptions caught. This is also a TREE_LIST - of whatever you want. As a special case, it has the - value `void_type_node' if it handles default exceptions. */ - tree handled; - - /* First insn of TRY block, in case resumptive model is needed. */ - rtx first_insn; - /* Label for the catch clauses. */ - rtx except_label; - /* Label for unhandled exceptions. */ - rtx unhandled_label; - /* Label at the end of whole construct. */ - rtx after_label; - /* Label which "escapes" the exception construct. - Like EXIT_LABEL for BREAK construct, but for exceptions. */ - rtx escape_label; - } except_stmt; - } data; -}; - -/* Chain of all pending binding contours. */ -struct nesting *block_stack; - -/* If any new stacks are added here, add them to POPSTACKS too. */ - -/* Chain of all pending binding contours that restore stack levels - or have cleanups. */ -struct nesting *stack_block_stack; - -/* Chain of all pending conditional statements. */ -struct nesting *cond_stack; - -/* Chain of all pending loops. */ -struct nesting *loop_stack; - -/* Chain of all pending case or switch statements. */ -struct nesting *case_stack; - -/* Chain of all pending exception contours. */ -struct nesting *except_stack; - -/* Separate chain including all of the above, - chained through the `all' field. */ -struct nesting *nesting_stack; - -/* Number of entries on nesting_stack now. */ -int nesting_depth; - -/* Allocate and return a new `struct nesting'. */ - -#define ALLOC_NESTING() \ - (struct nesting *) obstack_alloc (&stmt_obstack, sizeof (struct nesting)) - -/* Pop the nesting stack element by element until we pop off - the element which is at the top of STACK. - Update all the other stacks, popping off elements from them - as we pop them from nesting_stack. */ - -#define POPSTACK(STACK) \ -do { struct nesting *target = STACK; \ - struct nesting *this; \ - do { this = nesting_stack; \ - if (loop_stack == this) \ - loop_stack = loop_stack->next; \ - if (cond_stack == this) \ - cond_stack = cond_stack->next; \ - if (block_stack == this) \ - block_stack = block_stack->next; \ - if (stack_block_stack == this) \ - stack_block_stack = stack_block_stack->next; \ - if (case_stack == this) \ - case_stack = case_stack->next; \ - if (except_stack == this) \ - except_stack = except_stack->next; \ - nesting_depth = nesting_stack->depth - 1; \ - nesting_stack = this->all; \ - obstack_free (&stmt_obstack, this); } \ - while (this != target); } while (0) - -/* In some cases it is impossible to generate code for a forward goto - until the label definition is seen. This happens when it may be necessary - for the goto to reset the stack pointer: we don't yet know how to do that. - So expand_goto puts an entry on this fixup list. - Each time a binding contour that resets the stack is exited, - we check each fixup. - If the target label has now been defined, we can insert the proper code. */ - -struct goto_fixup -{ - /* Points to following fixup. */ - struct goto_fixup *next; - /* Points to the insn before the jump insn. - If more code must be inserted, it goes after this insn. */ - rtx before_jump; - /* The LABEL_DECL that this jump is jumping to, or 0 - for break, continue or return. */ - tree target; - /* The BLOCK for the place where this goto was found. */ - tree context; - /* The CODE_LABEL rtx that this is jumping to. */ - rtx target_rtl; - /* Number of binding contours started in current function - before the label reference. */ - int block_start_count; - /* The outermost stack level that should be restored for this jump. - Each time a binding contour that resets the stack is exited, - if the target label is *not* yet defined, this slot is updated. */ - rtx stack_level; - /* List of lists of cleanup expressions to be run by this goto. - There is one element for each block that this goto is within. - The tail of this list can be 0 (was empty_cleanup_list), - if all remaining elements would be empty. - The TREE_VALUE contains the cleanup list of that block as of the - time this goto was seen. - The TREE_ADDRESSABLE flag is 1 for a block that has been exited. */ - tree cleanup_list_list; -}; - -static struct goto_fixup *goto_fixup_chain; - -/* Within any binding contour that must restore a stack level, - all labels are recorded with a chain of these structures. */ - -struct label_chain -{ - /* Points to following fixup. */ - struct label_chain *next; - tree label; -}; - -void -init_stmt () -{ - gcc_obstack_init (&stmt_obstack); -#if 0 - empty_cleanup_list = build_tree_list (NULL_TREE, NULL_TREE); -#endif -} - -void -init_stmt_for_function () -{ - /* We are not currently within any block, conditional, loop or case. */ - block_stack = 0; - loop_stack = 0; - case_stack = 0; - cond_stack = 0; - nesting_stack = 0; - nesting_depth = 0; - - block_start_count = 0; - - /* No gotos have been expanded yet. */ - goto_fixup_chain = 0; - - /* We are not processing a ({...}) grouping. */ - expr_stmts_for_value = 0; - last_expr_type = 0; -} - -void -save_stmt_status (p) - struct function *p; -{ - p->block_stack = block_stack; - p->stack_block_stack = stack_block_stack; - p->cond_stack = cond_stack; - p->loop_stack = loop_stack; - p->case_stack = case_stack; - p->nesting_stack = nesting_stack; - p->nesting_depth = nesting_depth; - p->block_start_count = block_start_count; - p->last_expr_type = last_expr_type; - p->last_expr_value = last_expr_value; - p->expr_stmts_for_value = expr_stmts_for_value; - p->emit_filename = emit_filename; - p->emit_lineno = emit_lineno; - p->goto_fixup_chain = goto_fixup_chain; -} - -void -restore_stmt_status (p) - struct function *p; -{ - block_stack = p->block_stack; - stack_block_stack = p->stack_block_stack; - cond_stack = p->cond_stack; - loop_stack = p->loop_stack; - case_stack = p->case_stack; - nesting_stack = p->nesting_stack; - nesting_depth = p->nesting_depth; - block_start_count = p->block_start_count; - last_expr_type = p->last_expr_type; - last_expr_value = p->last_expr_value; - expr_stmts_for_value = p->expr_stmts_for_value; - emit_filename = p->emit_filename; - emit_lineno = p->emit_lineno; - goto_fixup_chain = p->goto_fixup_chain; -} - -/* Emit a no-op instruction. */ - -void -emit_nop () -{ - rtx last_insn = get_last_insn (); - if (!optimize - && (GET_CODE (last_insn) == CODE_LABEL - || prev_real_insn (last_insn) == 0)) - emit_insn (gen_nop ()); -} - -/* Return the rtx-label that corresponds to a LABEL_DECL, - creating it if necessary. */ - -rtx -label_rtx (label) - tree label; -{ - if (TREE_CODE (label) != LABEL_DECL) - abort (); - - if (DECL_RTL (label)) - return DECL_RTL (label); - - return DECL_RTL (label) = gen_label_rtx (); -} - -/* Add an unconditional jump to LABEL as the next sequential instruction. */ - -void -emit_jump (label) - rtx label; -{ - do_pending_stack_adjust (); - emit_jump_insn (gen_jump (label)); - emit_barrier (); -} - -/* Emit code to jump to the address - specified by the pointer expression EXP. */ - -void -expand_computed_goto (exp) - tree exp; -{ - rtx x = expand_expr (exp, NULL_RTX, VOIDmode, 0); - emit_queue (); - emit_indirect_jump (x); -} - -/* Handle goto statements and the labels that they can go to. */ - -/* Specify the location in the RTL code of a label LABEL, - which is a LABEL_DECL tree node. - - This is used for the kind of label that the user can jump to with a - goto statement, and for alternatives of a switch or case statement. - RTL labels generated for loops and conditionals don't go through here; - they are generated directly at the RTL level, by other functions below. - - Note that this has nothing to do with defining label *names*. - Languages vary in how they do that and what that even means. */ - -void -expand_label (label) - tree label; -{ - struct label_chain *p; - - do_pending_stack_adjust (); - emit_label (label_rtx (label)); - if (DECL_NAME (label)) - LABEL_NAME (DECL_RTL (label)) = IDENTIFIER_POINTER (DECL_NAME (label)); - - if (stack_block_stack != 0) - { - p = (struct label_chain *) oballoc (sizeof (struct label_chain)); - p->next = stack_block_stack->data.block.label_chain; - stack_block_stack->data.block.label_chain = p; - p->label = label; - } -} - -/* Declare that LABEL (a LABEL_DECL) may be used for nonlocal gotos - from nested functions. */ - -void -declare_nonlocal_label (label) - tree label; -{ - nonlocal_labels = tree_cons (NULL_TREE, label, nonlocal_labels); - LABEL_PRESERVE_P (label_rtx (label)) = 1; - if (nonlocal_goto_handler_slot == 0) - { - nonlocal_goto_handler_slot - = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0); - emit_stack_save (SAVE_NONLOCAL, - &nonlocal_goto_stack_level, - PREV_INSN (tail_recursion_reentry)); - } -} - -/* Generate RTL code for a `goto' statement with target label LABEL. - LABEL should be a LABEL_DECL tree node that was or will later be - defined with `expand_label'. */ - -void -expand_goto (label) - tree label; -{ - /* Check for a nonlocal goto to a containing function. */ - tree context = decl_function_context (label); - if (context != 0 && context != current_function_decl) - { - struct function *p = find_function_data (context); - rtx label_ref = gen_rtx (LABEL_REF, Pmode, label_rtx (label)); - rtx temp; - - p->has_nonlocal_label = 1; - LABEL_REF_NONLOCAL_P (label_ref) = 1; - - /* Copy the rtl for the slots so that they won't be shared in - case the virtual stack vars register gets instantiated differently - in the parent than in the child. */ - -#if HAVE_nonlocal_goto - if (HAVE_nonlocal_goto) - emit_insn (gen_nonlocal_goto (lookup_static_chain (label), - copy_rtx (p->nonlocal_goto_handler_slot), - copy_rtx (p->nonlocal_goto_stack_level), - label_ref)); - else -#endif - { - rtx addr; - - /* Restore frame pointer for containing function. - This sets the actual hard register used for the frame pointer - to the location of the function's incoming static chain info. - The non-local goto handler will then adjust it to contain the - proper value and reload the argument pointer, if needed. */ - emit_move_insn (frame_pointer_rtx, lookup_static_chain (label)); - - /* We have now loaded the frame pointer hardware register with - the address of that corresponds to the start of the virtual - stack vars. So replace virtual_stack_vars_rtx in all - addresses we use with stack_pointer_rtx. */ - - /* Get addr of containing function's current nonlocal goto handler, - which will do any cleanups and then jump to the label. */ - addr = copy_rtx (p->nonlocal_goto_handler_slot); - temp = copy_to_reg (replace_rtx (addr, virtual_stack_vars_rtx, - frame_pointer_rtx)); - - /* Restore the stack pointer. Note this uses fp just restored. */ - addr = p->nonlocal_goto_stack_level; - if (addr) - addr = replace_rtx (copy_rtx (addr), - virtual_stack_vars_rtx, frame_pointer_rtx); - - emit_stack_restore (SAVE_NONLOCAL, addr, NULL_RTX); - - /* Put in the static chain register the nonlocal label address. */ - emit_move_insn (static_chain_rtx, label_ref); - /* USE of frame_pointer_rtx added for consistency; not clear if - really needed. */ - emit_insn (gen_rtx (USE, VOIDmode, frame_pointer_rtx)); - emit_insn (gen_rtx (USE, VOIDmode, stack_pointer_rtx)); - emit_insn (gen_rtx (USE, VOIDmode, static_chain_rtx)); - emit_indirect_jump (temp); - } - } - else - expand_goto_internal (label, label_rtx (label), NULL_RTX); -} - -/* Generate RTL code for a `goto' statement with target label BODY. - LABEL should be a LABEL_REF. - LAST_INSN, if non-0, is the rtx we should consider as the last - insn emitted (for the purposes of cleaning up a return). */ - -static void -expand_goto_internal (body, label, last_insn) - tree body; - rtx label; - rtx last_insn; -{ - struct nesting *block; - rtx stack_level = 0; - - if (GET_CODE (label) != CODE_LABEL) - abort (); - - /* If label has already been defined, we can tell now - whether and how we must alter the stack level. */ - - if (PREV_INSN (label) != 0) - { - /* Find the innermost pending block that contains the label. - (Check containment by comparing insn-uids.) - Then restore the outermost stack level within that block, - and do cleanups of all blocks contained in it. */ - for (block = block_stack; block; block = block->next) - { - if (INSN_UID (block->data.block.first_insn) < INSN_UID (label)) - break; - if (block->data.block.stack_level != 0) - stack_level = block->data.block.stack_level; - /* Execute the cleanups for blocks we are exiting. */ - if (block->data.block.cleanups != 0) - { - expand_cleanups (block->data.block.cleanups, NULL_TREE); - do_pending_stack_adjust (); - } - } - - if (stack_level) - { - /* Ensure stack adjust isn't done by emit_jump, as this would clobber - the stack pointer. This one should be deleted as dead by flow. */ - clear_pending_stack_adjust (); - do_pending_stack_adjust (); - emit_stack_restore (SAVE_BLOCK, stack_level, NULL_RTX); - } - - if (body != 0 && DECL_TOO_LATE (body)) - error ("jump to `%s' invalidly jumps into binding contour", - IDENTIFIER_POINTER (DECL_NAME (body))); - } - /* Label not yet defined: may need to put this goto - on the fixup list. */ - else if (! expand_fixup (body, label, last_insn)) - { - /* No fixup needed. Record that the label is the target - of at least one goto that has no fixup. */ - if (body != 0) - TREE_ADDRESSABLE (body) = 1; - } - - emit_jump (label); -} - -/* Generate if necessary a fixup for a goto - whose target label in tree structure (if any) is TREE_LABEL - and whose target in rtl is RTL_LABEL. - - If LAST_INSN is nonzero, we pretend that the jump appears - after insn LAST_INSN instead of at the current point in the insn stream. - - The fixup will be used later to insert insns just before the goto. - Those insns will restore the stack level as appropriate for the - target label, and will (in the case of C++) also invoke any object - destructors which have to be invoked when we exit the scopes which - are exited by the goto. - - Value is nonzero if a fixup is made. */ - -static int -expand_fixup (tree_label, rtl_label, last_insn) - tree tree_label; - rtx rtl_label; - rtx last_insn; -{ - struct nesting *block, *end_block; - - /* See if we can recognize which block the label will be output in. - This is possible in some very common cases. - If we succeed, set END_BLOCK to that block. - Otherwise, set it to 0. */ - - if (cond_stack - && (rtl_label == cond_stack->data.cond.endif_label - || rtl_label == cond_stack->data.cond.next_label)) - end_block = cond_stack; - /* If we are in a loop, recognize certain labels which - are likely targets. This reduces the number of fixups - we need to create. */ - else if (loop_stack - && (rtl_label == loop_stack->data.loop.start_label - || rtl_label == loop_stack->data.loop.end_label - || rtl_label == loop_stack->data.loop.continue_label)) - end_block = loop_stack; - else - end_block = 0; - - /* Now set END_BLOCK to the binding level to which we will return. */ - - if (end_block) - { - struct nesting *next_block = end_block->all; - block = block_stack; - - /* First see if the END_BLOCK is inside the innermost binding level. - If so, then no cleanups or stack levels are relevant. */ - while (next_block && next_block != block) - next_block = next_block->all; - - if (next_block) - return 0; - - /* Otherwise, set END_BLOCK to the innermost binding level - which is outside the relevant control-structure nesting. */ - next_block = block_stack->next; - for (block = block_stack; block != end_block; block = block->all) - if (block == next_block) - next_block = next_block->next; - end_block = next_block; - } - - /* Does any containing block have a stack level or cleanups? - If not, no fixup is needed, and that is the normal case - (the only case, for standard C). */ - for (block = block_stack; block != end_block; block = block->next) - if (block->data.block.stack_level != 0 - || block->data.block.cleanups != 0) - break; - - if (block != end_block) - { - /* Ok, a fixup is needed. Add a fixup to the list of such. */ - struct goto_fixup *fixup - = (struct goto_fixup *) oballoc (sizeof (struct goto_fixup)); - /* In case an old stack level is restored, make sure that comes - after any pending stack adjust. */ - /* ?? If the fixup isn't to come at the present position, - doing the stack adjust here isn't useful. Doing it with our - settings at that location isn't useful either. Let's hope - someone does it! */ - if (last_insn == 0) - do_pending_stack_adjust (); - fixup->target = tree_label; - fixup->target_rtl = rtl_label; - - /* Create a BLOCK node and a corresponding matched set of - NOTE_INSN_BEGIN_BLOCK and NOTE_INSN_END_BLOCK notes at - this point. The notes will encapsulate any and all fixup - code which we might later insert at this point in the insn - stream. Also, the BLOCK node will be the parent (i.e. the - `SUPERBLOCK') of any other BLOCK nodes which we might create - later on when we are expanding the fixup code. */ - - { - register rtx original_before_jump - = last_insn ? last_insn : get_last_insn (); - - start_sequence (); - pushlevel (0); - fixup->before_jump = emit_note (NULL_PTR, NOTE_INSN_BLOCK_BEG); - last_block_end_note = emit_note (NULL_PTR, NOTE_INSN_BLOCK_END); - fixup->context = poplevel (1, 0, 0); /* Create the BLOCK node now! */ - end_sequence (); - emit_insns_after (fixup->before_jump, original_before_jump); - } - - fixup->block_start_count = block_start_count; - fixup->stack_level = 0; - fixup->cleanup_list_list - = (((block->data.block.outer_cleanups -#if 0 - && block->data.block.outer_cleanups != empty_cleanup_list -#endif - ) - || block->data.block.cleanups) - ? tree_cons (NULL_TREE, block->data.block.cleanups, - block->data.block.outer_cleanups) - : 0); - fixup->next = goto_fixup_chain; - goto_fixup_chain = fixup; - } - - return block != 0; -} - -/* When exiting a binding contour, process all pending gotos requiring fixups. - THISBLOCK is the structure that describes the block being exited. - STACK_LEVEL is the rtx for the stack level to restore exiting this contour. - CLEANUP_LIST is a list of expressions to evaluate on exiting this contour. - FIRST_INSN is the insn that began this contour. - - Gotos that jump out of this contour must restore the - stack level and do the cleanups before actually jumping. - - DONT_JUMP_IN nonzero means report error there is a jump into this - contour from before the beginning of the contour. - This is also done if STACK_LEVEL is nonzero. */ - -void -fixup_gotos (thisblock, stack_level, cleanup_list, first_insn, dont_jump_in) - struct nesting *thisblock; - rtx stack_level; - tree cleanup_list; - rtx first_insn; - int dont_jump_in; -{ - register struct goto_fixup *f, *prev; - - /* F is the fixup we are considering; PREV is the previous one. */ - /* We run this loop in two passes so that cleanups of exited blocks - are run first, and blocks that are exited are marked so - afterwards. */ - - for (prev = 0, f = goto_fixup_chain; f; prev = f, f = f->next) - { - /* Test for a fixup that is inactive because it is already handled. */ - if (f->before_jump == 0) - { - /* Delete inactive fixup from the chain, if that is easy to do. */ - if (prev != 0) - prev->next = f->next; - } - /* Has this fixup's target label been defined? - If so, we can finalize it. */ - else if (PREV_INSN (f->target_rtl) != 0) - { - register rtx cleanup_insns; - - /* Get the first non-label after the label - this goto jumps to. If that's before this scope begins, - we don't have a jump into the scope. */ - rtx after_label = f->target_rtl; - while (after_label != 0 && GET_CODE (after_label) == CODE_LABEL) - after_label = NEXT_INSN (after_label); - - /* If this fixup jumped into this contour from before the beginning - of this contour, report an error. */ - /* ??? Bug: this does not detect jumping in through intermediate - blocks that have stack levels or cleanups. - It detects only a problem with the innermost block - around the label. */ - if (f->target != 0 - && (dont_jump_in || stack_level || cleanup_list) - /* If AFTER_LABEL is 0, it means the jump goes to the end - of the rtl, which means it jumps into this scope. */ - && (after_label == 0 - || INSN_UID (first_insn) < INSN_UID (after_label)) - && INSN_UID (first_insn) > INSN_UID (f->before_jump) - && ! DECL_REGISTER (f->target)) - { - error_with_decl (f->target, - "label `%s' used before containing binding contour"); - /* Prevent multiple errors for one label. */ - DECL_REGISTER (f->target) = 1; - } - - /* We will expand the cleanups into a sequence of their own and - then later on we will attach this new sequence to the insn - stream just ahead of the actual jump insn. */ - - start_sequence (); - - /* Temporarily restore the lexical context where we will - logically be inserting the fixup code. We do this for the - sake of getting the debugging information right. */ - - pushlevel (0); - set_block (f->context); - - /* Expand the cleanups for blocks this jump exits. */ - if (f->cleanup_list_list) - { - tree lists; - for (lists = f->cleanup_list_list; lists; lists = TREE_CHAIN (lists)) - /* Marked elements correspond to blocks that have been closed. - Do their cleanups. */ - if (TREE_ADDRESSABLE (lists) - && TREE_VALUE (lists) != 0) - { - expand_cleanups (TREE_VALUE (lists), 0); - /* Pop any pushes done in the cleanups, - in case function is about to return. */ - do_pending_stack_adjust (); - } - } - - /* Restore stack level for the biggest contour that this - jump jumps out of. */ - if (f->stack_level) - emit_stack_restore (SAVE_BLOCK, f->stack_level, f->before_jump); - - /* Finish up the sequence containing the insns which implement the - necessary cleanups, and then attach that whole sequence to the - insn stream just ahead of the actual jump insn. Attaching it - at that point insures that any cleanups which are in fact - implicit C++ object destructions (which must be executed upon - leaving the block) appear (to the debugger) to be taking place - in an area of the generated code where the object(s) being - destructed are still "in scope". */ - - cleanup_insns = get_insns (); - poplevel (1, 0, 0); - - end_sequence (); - emit_insns_after (cleanup_insns, f->before_jump); - - - f->before_jump = 0; - } - } - - /* Mark the cleanups of exited blocks so that they are executed - by the code above. */ - for (prev = 0, f = goto_fixup_chain; f; prev = f, f = f->next) - if (f->before_jump != 0 - && PREV_INSN (f->target_rtl) == 0 - /* Label has still not appeared. If we are exiting a block with - a stack level to restore, that started before the fixup, - mark this stack level as needing restoration - when the fixup is later finalized. - Also mark the cleanup_list_list element for F - that corresponds to this block, so that ultimately - this block's cleanups will be executed by the code above. */ - && thisblock != 0 - /* Note: if THISBLOCK == 0 and we have a label that hasn't appeared, - it means the label is undefined. That's erroneous, but possible. */ - && (thisblock->data.block.block_start_count - <= f->block_start_count)) - { - tree lists = f->cleanup_list_list; - for (; lists; lists = TREE_CHAIN (lists)) - /* If the following elt. corresponds to our containing block - then the elt. must be for this block. */ - if (TREE_CHAIN (lists) == thisblock->data.block.outer_cleanups) - TREE_ADDRESSABLE (lists) = 1; - - if (stack_level) - f->stack_level = stack_level; - } -} - -/* Generate RTL for an asm statement (explicit assembler code). - BODY is a STRING_CST node containing the assembler code text, - or an ADDR_EXPR containing a STRING_CST. */ - -void -expand_asm (body) - tree body; -{ - if (TREE_CODE (body) == ADDR_EXPR) - body = TREE_OPERAND (body, 0); - - emit_insn (gen_rtx (ASM_INPUT, VOIDmode, - TREE_STRING_POINTER (body))); - last_expr_type = 0; -} - -/* Generate RTL for an asm statement with arguments. - STRING is the instruction template. - OUTPUTS is a list of output arguments (lvalues); INPUTS a list of inputs. - Each output or input has an expression in the TREE_VALUE and - a constraint-string in the TREE_PURPOSE. - CLOBBERS is a list of STRING_CST nodes each naming a hard register - that is clobbered by this insn. - - Not all kinds of lvalue that may appear in OUTPUTS can be stored directly. - Some elements of OUTPUTS may be replaced with trees representing temporary - values. The caller should copy those temporary values to the originally - specified lvalues. - - VOL nonzero means the insn is volatile; don't optimize it. */ - -void -expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line) - tree string, outputs, inputs, clobbers; - int vol; - char *filename; - int line; -{ - rtvec argvec, constraints; - rtx body; - int ninputs = list_length (inputs); - int noutputs = list_length (outputs); - int nclobbers; - tree tail; - register int i; - /* Vector of RTX's of evaluated output operands. */ - rtx *output_rtx = (rtx *) alloca (noutputs * sizeof (rtx)); - /* The insn we have emitted. */ - rtx insn; - - /* Count the number of meaningful clobbered registers, ignoring what - we would ignore later. */ - nclobbers = 0; - for (tail = clobbers; tail; tail = TREE_CHAIN (tail)) - { - char *regname = TREE_STRING_POINTER (TREE_VALUE (tail)); - i = decode_reg_name (regname); - if (i >= 0 || i == -4) - ++nclobbers; - } - - last_expr_type = 0; - - for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++) - { - tree val = TREE_VALUE (tail); - tree val1; - int j; - int found_equal; - - /* If there's an erroneous arg, emit no insn. */ - if (TREE_TYPE (val) == error_mark_node) - return; - - /* Make sure constraint has `=' and does not have `+'. */ - - found_equal = 0; - for (j = 0; j < TREE_STRING_LENGTH (TREE_PURPOSE (tail)); j++) - { - if (TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '+') - { - error ("output operand constraint contains `+'"); - return; - } - if (TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '=') - found_equal = 1; - } - if (! found_equal) - { - error ("output operand constraint lacks `='"); - return; - } - - /* If an output operand is not a variable or indirect ref, - or a part of one, - create a SAVE_EXPR which is a pseudo-reg - to act as an intermediate temporary. - Make the asm insn write into that, then copy it to - the real output operand. */ - - while (TREE_CODE (val) == COMPONENT_REF - || TREE_CODE (val) == ARRAY_REF) - val = TREE_OPERAND (val, 0); - - if (TREE_CODE (val) != VAR_DECL - && TREE_CODE (val) != PARM_DECL - && TREE_CODE (val) != INDIRECT_REF) - { - TREE_VALUE (tail) = save_expr (TREE_VALUE (tail)); - /* If it's a constant, print error now so don't crash later. */ - if (TREE_CODE (TREE_VALUE (tail)) != SAVE_EXPR) - { - error ("invalid output in `asm'"); - return; - } - } - - output_rtx[i] = expand_expr (TREE_VALUE (tail), NULL_RTX, VOIDmode, 0); - } - - if (ninputs + noutputs > MAX_RECOG_OPERANDS) - { - error ("more than %d operands in `asm'", MAX_RECOG_OPERANDS); - return; - } - - /* Make vectors for the expression-rtx and constraint strings. */ - - argvec = rtvec_alloc (ninputs); - constraints = rtvec_alloc (ninputs); - - body = gen_rtx (ASM_OPERANDS, VOIDmode, - TREE_STRING_POINTER (string), "", 0, argvec, constraints, - filename, line); - MEM_VOLATILE_P (body) = vol; - - /* Eval the inputs and put them into ARGVEC. - Put their constraints into ASM_INPUTs and store in CONSTRAINTS. */ - - i = 0; - for (tail = inputs; tail; tail = TREE_CHAIN (tail)) - { - int j; - - /* If there's an erroneous arg, emit no insn, - because the ASM_INPUT would get VOIDmode - and that could cause a crash in reload. */ - if (TREE_TYPE (TREE_VALUE (tail)) == error_mark_node) - return; - if (TREE_PURPOSE (tail) == NULL_TREE) - { - error ("hard register `%s' listed as input operand to `asm'", - TREE_STRING_POINTER (TREE_VALUE (tail)) ); - return; - } - - /* Make sure constraint has neither `=' nor `+'. */ - - for (j = 0; j < TREE_STRING_LENGTH (TREE_PURPOSE (tail)); j++) - if (TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '=' - || TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '+') - { - error ("input operand constraint contains `%c'", - TREE_STRING_POINTER (TREE_PURPOSE (tail))[j]); - return; - } - - XVECEXP (body, 3, i) /* argvec */ - = expand_expr (TREE_VALUE (tail), NULL_RTX, VOIDmode, 0); - XVECEXP (body, 4, i) /* constraints */ - = gen_rtx (ASM_INPUT, TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))), - TREE_STRING_POINTER (TREE_PURPOSE (tail))); - i++; - } - - /* Protect all the operands from the queue, - now that they have all been evaluated. */ - - for (i = 0; i < ninputs; i++) - XVECEXP (body, 3, i) = protect_from_queue (XVECEXP (body, 3, i), 0); - - for (i = 0; i < noutputs; i++) - output_rtx[i] = protect_from_queue (output_rtx[i], 1); - - /* Now, for each output, construct an rtx - (set OUTPUT (asm_operands INSN OUTPUTNUMBER OUTPUTCONSTRAINT - ARGVEC CONSTRAINTS)) - If there is more than one, put them inside a PARALLEL. */ - - if (noutputs == 1 && nclobbers == 0) - { - XSTR (body, 1) = TREE_STRING_POINTER (TREE_PURPOSE (outputs)); - insn = emit_insn (gen_rtx (SET, VOIDmode, output_rtx[0], body)); - } - else if (noutputs == 0 && nclobbers == 0) - { - /* No output operands: put in a raw ASM_OPERANDS rtx. */ - insn = emit_insn (body); - } - else - { - rtx obody = body; - int num = noutputs; - if (num == 0) num = 1; - body = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (num + nclobbers)); - - /* For each output operand, store a SET. */ - - for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++) - { - XVECEXP (body, 0, i) - = gen_rtx (SET, VOIDmode, - output_rtx[i], - gen_rtx (ASM_OPERANDS, VOIDmode, - TREE_STRING_POINTER (string), - TREE_STRING_POINTER (TREE_PURPOSE (tail)), - i, argvec, constraints, - filename, line)); - MEM_VOLATILE_P (SET_SRC (XVECEXP (body, 0, i))) = vol; - } - - /* If there are no outputs (but there are some clobbers) - store the bare ASM_OPERANDS into the PARALLEL. */ - - if (i == 0) - XVECEXP (body, 0, i++) = obody; - - /* Store (clobber REG) for each clobbered register specified. */ - - for (tail = clobbers; tail; tail = TREE_CHAIN (tail)) - { - char *regname = TREE_STRING_POINTER (TREE_VALUE (tail)); - int j = decode_reg_name (regname); - - if (j < 0) - { - if (j == -3) /* `cc', which is not a register */ - continue; - - if (j == -4) /* `memory', don't cache memory across asm */ - { - XVECEXP (body, 0, i++) - = gen_rtx (CLOBBER, VOIDmode, - gen_rtx (MEM, QImode, - gen_rtx (SCRATCH, VOIDmode, 0))); - continue; - } - - error ("unknown register name `%s' in `asm'", regname); - return; - } - - /* Use QImode since that's guaranteed to clobber just one reg. */ - XVECEXP (body, 0, i++) - = gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, QImode, j)); - } - - insn = emit_insn (body); - } - - free_temp_slots (); -} - -/* Generate RTL to evaluate the expression EXP - and remember it in case this is the VALUE in a ({... VALUE; }) constr. */ - -void -expand_expr_stmt (exp) - tree exp; -{ - /* If -W, warn about statements with no side effects, - except for an explicit cast to void (e.g. for assert()), and - except inside a ({...}) where they may be useful. */ - if (expr_stmts_for_value == 0 && exp != error_mark_node) - { - if (! TREE_SIDE_EFFECTS (exp) && (extra_warnings || warn_unused) - && !(TREE_CODE (exp) == CONVERT_EXPR - && TREE_TYPE (exp) == void_type_node)) - warning_with_file_and_line (emit_filename, emit_lineno, - "statement with no effect"); - else if (warn_unused) - warn_if_unused_value (exp); - } - last_expr_type = TREE_TYPE (exp); - if (! flag_syntax_only) - last_expr_value = expand_expr (exp, - (expr_stmts_for_value - ? NULL_RTX : const0_rtx), - VOIDmode, 0); - - /* If all we do is reference a volatile value in memory, - copy it to a register to be sure it is actually touched. */ - if (last_expr_value != 0 && GET_CODE (last_expr_value) == MEM - && TREE_THIS_VOLATILE (exp)) - { - if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode) - ; - else if (TYPE_MODE (TREE_TYPE (exp)) != BLKmode) - copy_to_reg (last_expr_value); - else - { - rtx lab = gen_label_rtx (); - - /* Compare the value with itself to reference it. */ - emit_cmp_insn (last_expr_value, last_expr_value, EQ, - expand_expr (TYPE_SIZE (last_expr_type), - NULL_RTX, VOIDmode, 0), - BLKmode, 0, - TYPE_ALIGN (last_expr_type) / BITS_PER_UNIT); - emit_jump_insn ((*bcc_gen_fctn[(int) EQ]) (lab)); - emit_label (lab); - } - } - - /* If this expression is part of a ({...}) and is in memory, we may have - to preserve temporaries. */ - preserve_temp_slots (last_expr_value); - - /* Free any temporaries used to evaluate this expression. Any temporary - used as a result of this expression will already have been preserved - above. */ - free_temp_slots (); - - emit_queue (); -} - -/* Warn if EXP contains any computations whose results are not used. - Return 1 if a warning is printed; 0 otherwise. */ - -static int -warn_if_unused_value (exp) - tree exp; -{ - if (TREE_USED (exp)) - return 0; - - switch (TREE_CODE (exp)) - { - case PREINCREMENT_EXPR: - case POSTINCREMENT_EXPR: - case PREDECREMENT_EXPR: - case POSTDECREMENT_EXPR: - case MODIFY_EXPR: - case INIT_EXPR: - case TARGET_EXPR: - case CALL_EXPR: - case METHOD_CALL_EXPR: - case RTL_EXPR: - case WITH_CLEANUP_EXPR: - case EXIT_EXPR: - /* We don't warn about COND_EXPR because it may be a useful - construct if either arm contains a side effect. */ - case COND_EXPR: - return 0; - - case BIND_EXPR: - /* For a binding, warn if no side effect within it. */ - return warn_if_unused_value (TREE_OPERAND (exp, 1)); - - case TRUTH_ORIF_EXPR: - case TRUTH_ANDIF_EXPR: - /* In && or ||, warn if 2nd operand has no side effect. */ - return warn_if_unused_value (TREE_OPERAND (exp, 1)); - - case COMPOUND_EXPR: - if (warn_if_unused_value (TREE_OPERAND (exp, 0))) - return 1; - /* Let people do `(foo (), 0)' without a warning. */ - if (TREE_CONSTANT (TREE_OPERAND (exp, 1))) - return 0; - return warn_if_unused_value (TREE_OPERAND (exp, 1)); - - case NOP_EXPR: - case CONVERT_EXPR: - case NON_LVALUE_EXPR: - /* Don't warn about values cast to void. */ - if (TREE_TYPE (exp) == void_type_node) - return 0; - /* Don't warn about conversions not explicit in the user's program. */ - if (TREE_NO_UNUSED_WARNING (exp)) - return 0; - /* Assignment to a cast usually results in a cast of a modify. - Don't complain about that. */ - if (TREE_CODE (TREE_OPERAND (exp, 0)) == MODIFY_EXPR) - return 0; - /* Sometimes it results in a cast of a cast of a modify. - Don't complain about that. */ - if ((TREE_CODE (TREE_OPERAND (exp, 0)) == CONVERT_EXPR - || TREE_CODE (TREE_OPERAND (exp, 0)) == NOP_EXPR) - && TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) == MODIFY_EXPR) - return 0; - - default: - /* Referencing a volatile value is a side effect, so don't warn. */ - if ((TREE_CODE_CLASS (TREE_CODE (exp)) == 'd' - || TREE_CODE_CLASS (TREE_CODE (exp)) == 'r') - && TREE_THIS_VOLATILE (exp)) - return 0; - warning_with_file_and_line (emit_filename, emit_lineno, - "value computed is not used"); - return 1; - } -} - -/* Clear out the memory of the last expression evaluated. */ - -void -clear_last_expr () -{ - last_expr_type = 0; -} - -/* Begin a statement which will return a value. - Return the RTL_EXPR for this statement expr. - The caller must save that value and pass it to expand_end_stmt_expr. */ - -tree -expand_start_stmt_expr () -{ - /* Make the RTL_EXPR node temporary, not momentary, - so that rtl_expr_chain doesn't become garbage. */ - int momentary = suspend_momentary (); - tree t = make_node (RTL_EXPR); - resume_momentary (momentary); - start_sequence (); - NO_DEFER_POP; - expr_stmts_for_value++; - return t; -} - -/* Restore the previous state at the end of a statement that returns a value. - Returns a tree node representing the statement's value and the - insns to compute the value. - - The nodes of that expression have been freed by now, so we cannot use them. - But we don't want to do that anyway; the expression has already been - evaluated and now we just want to use the value. So generate a RTL_EXPR - with the proper type and RTL value. - - If the last substatement was not an expression, - return something with type `void'. */ - -tree -expand_end_stmt_expr (t) - tree t; -{ - OK_DEFER_POP; - - if (last_expr_type == 0) - { - last_expr_type = void_type_node; - last_expr_value = const0_rtx; - } - else if (last_expr_value == 0) - /* There are some cases where this can happen, such as when the - statement is void type. */ - last_expr_value = const0_rtx; - else if (GET_CODE (last_expr_value) != REG && ! CONSTANT_P (last_expr_value)) - /* Remove any possible QUEUED. */ - last_expr_value = protect_from_queue (last_expr_value, 0); - - emit_queue (); - - TREE_TYPE (t) = last_expr_type; - RTL_EXPR_RTL (t) = last_expr_value; - RTL_EXPR_SEQUENCE (t) = get_insns (); - - rtl_expr_chain = tree_cons (NULL_TREE, t, rtl_expr_chain); - - end_sequence (); - - /* Don't consider deleting this expr or containing exprs at tree level. */ - TREE_SIDE_EFFECTS (t) = 1; - /* Propagate volatility of the actual RTL expr. */ - TREE_THIS_VOLATILE (t) = volatile_refs_p (last_expr_value); - - last_expr_type = 0; - expr_stmts_for_value--; - - return t; -} - -/* The exception handling nesting looks like this: - - <-- Level N-1 - { <-- exception handler block - <-- Level N - <-- in an exception handler - { <-- try block - : <-- in a TRY block - : <-- in an exception handler - : - } - - { <-- except block - : <-- in an except block - : <-- in an exception handler - : - } - - } -*/ - -/* Return nonzero iff in a try block at level LEVEL. */ - -int -in_try_block (level) - int level; -{ - struct nesting *n = except_stack; - while (1) - { - while (n && n->data.except_stmt.after_label != 0) - n = n->next; - if (n == 0) - return 0; - if (level == 0) - return n != 0; - level--; - n = n->next; - } -} - -/* Return nonzero iff in an except block at level LEVEL. */ - -int -in_except_block (level) - int level; -{ - struct nesting *n = except_stack; - while (1) - { - while (n && n->data.except_stmt.after_label == 0) - n = n->next; - if (n == 0) - return 0; - if (level == 0) - return n != 0; - level--; - n = n->next; - } -} - -/* Return nonzero iff in an exception handler at level LEVEL. */ - -int -in_exception_handler (level) - int level; -{ - struct nesting *n = except_stack; - while (n && level--) - n = n->next; - return n != 0; -} - -/* Record the fact that the current exception nesting raises - exception EX. If not in an exception handler, return 0. */ -int -expand_raise (ex) - tree ex; -{ - tree *raises_ptr; - - if (except_stack == 0) - return 0; - raises_ptr = &except_stack->data.except_stmt.raised; - if (! value_member (ex, *raises_ptr)) - *raises_ptr = tree_cons (NULL_TREE, ex, *raises_ptr); - return 1; -} - -/* Generate RTL for the start of a try block. - - TRY_CLAUSE is the condition to test to enter the try block. */ - -void -expand_start_try (try_clause, exitflag, escapeflag) - tree try_clause; - int exitflag; - int escapeflag; -{ - struct nesting *thishandler = ALLOC_NESTING (); - - /* Make an entry on cond_stack for the cond we are entering. */ - - thishandler->next = except_stack; - thishandler->all = nesting_stack; - thishandler->depth = ++nesting_depth; - thishandler->data.except_stmt.raised = 0; - thishandler->data.except_stmt.handled = 0; - thishandler->data.except_stmt.first_insn = get_insns (); - thishandler->data.except_stmt.except_label = gen_label_rtx (); - thishandler->data.except_stmt.unhandled_label = 0; - thishandler->data.except_stmt.after_label = 0; - thishandler->data.except_stmt.escape_label - = escapeflag ? thishandler->data.except_stmt.except_label : 0; - thishandler->exit_label = exitflag ? gen_label_rtx () : 0; - except_stack = thishandler; - nesting_stack = thishandler; - - do_jump (try_clause, thishandler->data.except_stmt.except_label, NULL_RTX); -} - -/* End of a TRY block. Nothing to do for now. */ - -void -expand_end_try () -{ - except_stack->data.except_stmt.after_label = gen_label_rtx (); - expand_goto_internal (NULL_TREE, except_stack->data.except_stmt.after_label, - NULL_RTX); -} - -/* Start an `except' nesting contour. - EXITFLAG says whether this contour should be able to `exit' something. - ESCAPEFLAG says whether this contour should be escapable. */ - -void -expand_start_except (exitflag, escapeflag) - int exitflag; - int escapeflag; -{ - if (exitflag) - { - struct nesting *n; - /* An `exit' from catch clauses goes out to next exit level, - if there is one. Otherwise, it just goes to the end - of the construct. */ - for (n = except_stack->next; n; n = n->next) - if (n->exit_label != 0) - { - except_stack->exit_label = n->exit_label; - break; - } - if (n == 0) - except_stack->exit_label = except_stack->data.except_stmt.after_label; - } - if (escapeflag) - { - struct nesting *n; - /* An `escape' from catch clauses goes out to next escape level, - if there is one. Otherwise, it just goes to the end - of the construct. */ - for (n = except_stack->next; n; n = n->next) - if (n->data.except_stmt.escape_label != 0) - { - except_stack->data.except_stmt.escape_label - = n->data.except_stmt.escape_label; - break; - } - if (n == 0) - except_stack->data.except_stmt.escape_label - = except_stack->data.except_stmt.after_label; - } - do_pending_stack_adjust (); - emit_label (except_stack->data.except_stmt.except_label); -} - -/* Generate code to `escape' from an exception contour. This - is like `exiting', but does not conflict with constructs which - use `exit_label'. - - Return nonzero if this contour is escapable, otherwise - return zero, and language-specific code will emit the - appropriate error message. */ -int -expand_escape_except () -{ - struct nesting *n; - last_expr_type = 0; - for (n = except_stack; n; n = n->next) - if (n->data.except_stmt.escape_label != 0) - { - expand_goto_internal (NULL_TREE, - n->data.except_stmt.escape_label, NULL_RTX); - return 1; - } - - return 0; -} - -/* Finish processing and `except' contour. - Culls out all exceptions which might be raise but not - handled, and returns the list to the caller. - Language-specific code is responsible for dealing with these - exceptions. */ - -tree -expand_end_except () -{ - struct nesting *n; - tree raised = NULL_TREE; - - do_pending_stack_adjust (); - emit_label (except_stack->data.except_stmt.after_label); - - n = except_stack->next; - if (n) - { - /* Propagate exceptions raised but not handled to next - highest level. */ - tree handled = except_stack->data.except_stmt.raised; - if (handled != void_type_node) - { - tree prev = NULL_TREE; - raised = except_stack->data.except_stmt.raised; - while (handled) - { - tree this_raise; - for (this_raise = raised, prev = 0; this_raise; - this_raise = TREE_CHAIN (this_raise)) - { - if (value_member (TREE_VALUE (this_raise), handled)) - { - if (prev) - TREE_CHAIN (prev) = TREE_CHAIN (this_raise); - else - { - raised = TREE_CHAIN (raised); - if (raised == NULL_TREE) - goto nada; - } - } - else - prev = this_raise; - } - handled = TREE_CHAIN (handled); - } - if (prev == NULL_TREE) - prev = raised; - if (prev) - TREE_CHAIN (prev) = n->data.except_stmt.raised; - nada: - n->data.except_stmt.raised = raised; - } - } - - POPSTACK (except_stack); - last_expr_type = 0; - return raised; -} - -/* Record that exception EX is caught by this exception handler. - Return nonzero if in exception handling construct, otherwise return 0. */ -int -expand_catch (ex) - tree ex; -{ - tree *raises_ptr; - - if (except_stack == 0) - return 0; - raises_ptr = &except_stack->data.except_stmt.handled; - if (*raises_ptr != void_type_node - && ex != NULL_TREE - && ! value_member (ex, *raises_ptr)) - *raises_ptr = tree_cons (NULL_TREE, ex, *raises_ptr); - return 1; -} - -/* Record that this exception handler catches all exceptions. - Return nonzero if in exception handling construct, otherwise return 0. */ - -int -expand_catch_default () -{ - if (except_stack == 0) - return 0; - except_stack->data.except_stmt.handled = void_type_node; - return 1; -} - -int -expand_end_catch () -{ - if (except_stack == 0 || except_stack->data.except_stmt.after_label == 0) - return 0; - expand_goto_internal (NULL_TREE, except_stack->data.except_stmt.after_label, - NULL_RTX); - return 1; -} - -/* Generate RTL for the start of an if-then. COND is the expression - whose truth should be tested. - - If EXITFLAG is nonzero, this conditional is visible to - `exit_something'. */ - -void -expand_start_cond (cond, exitflag) - tree cond; - int exitflag; -{ - struct nesting *thiscond = ALLOC_NESTING (); - - /* Make an entry on cond_stack for the cond we are entering. */ - - thiscond->next = cond_stack; - thiscond->all = nesting_stack; - thiscond->depth = ++nesting_depth; - thiscond->data.cond.next_label = gen_label_rtx (); - /* Before we encounter an `else', we don't need a separate exit label - unless there are supposed to be exit statements - to exit this conditional. */ - thiscond->exit_label = exitflag ? gen_label_rtx () : 0; - thiscond->data.cond.endif_label = thiscond->exit_label; - cond_stack = thiscond; - nesting_stack = thiscond; - - do_jump (cond, thiscond->data.cond.next_label, NULL_RTX); -} - -/* Generate RTL between then-clause and the elseif-clause - of an if-then-elseif-.... */ - -void -expand_start_elseif (cond) - tree cond; -{ - if (cond_stack->data.cond.endif_label == 0) - cond_stack->data.cond.endif_label = gen_label_rtx (); - emit_jump (cond_stack->data.cond.endif_label); - emit_label (cond_stack->data.cond.next_label); - cond_stack->data.cond.next_label = gen_label_rtx (); - do_jump (cond, cond_stack->data.cond.next_label, NULL_RTX); -} - -/* Generate RTL between the then-clause and the else-clause - of an if-then-else. */ - -void -expand_start_else () -{ - if (cond_stack->data.cond.endif_label == 0) - cond_stack->data.cond.endif_label = gen_label_rtx (); - emit_jump (cond_stack->data.cond.endif_label); - emit_label (cond_stack->data.cond.next_label); - cond_stack->data.cond.next_label = 0; /* No more _else or _elseif calls. */ -} - -/* Generate RTL for the end of an if-then. - Pop the record for it off of cond_stack. */ - -void -expand_end_cond () -{ - struct nesting *thiscond = cond_stack; - - do_pending_stack_adjust (); - if (thiscond->data.cond.next_label) - emit_label (thiscond->data.cond.next_label); - if (thiscond->data.cond.endif_label) - emit_label (thiscond->data.cond.endif_label); - - POPSTACK (cond_stack); - last_expr_type = 0; -} - -/* Generate RTL for the start of a loop. EXIT_FLAG is nonzero if this - loop should be exited by `exit_something'. This is a loop for which - `expand_continue' will jump to the top of the loop. - - Make an entry on loop_stack to record the labels associated with - this loop. */ - -struct nesting * -expand_start_loop (exit_flag) - int exit_flag; -{ - register struct nesting *thisloop = ALLOC_NESTING (); - - /* Make an entry on loop_stack for the loop we are entering. */ - - thisloop->next = loop_stack; - thisloop->all = nesting_stack; - thisloop->depth = ++nesting_depth; - thisloop->data.loop.start_label = gen_label_rtx (); - thisloop->data.loop.end_label = gen_label_rtx (); - thisloop->data.loop.continue_label = thisloop->data.loop.start_label; - thisloop->exit_label = exit_flag ? thisloop->data.loop.end_label : 0; - loop_stack = thisloop; - nesting_stack = thisloop; - - do_pending_stack_adjust (); - emit_queue (); - emit_note (NULL_PTR, NOTE_INSN_LOOP_BEG); - emit_label (thisloop->data.loop.start_label); - - return thisloop; -} - -/* Like expand_start_loop but for a loop where the continuation point - (for expand_continue_loop) will be specified explicitly. */ - -struct nesting * -expand_start_loop_continue_elsewhere (exit_flag) - int exit_flag; -{ - struct nesting *thisloop = expand_start_loop (exit_flag); - loop_stack->data.loop.continue_label = gen_label_rtx (); - return thisloop; -} - -/* Specify the continuation point for a loop started with - expand_start_loop_continue_elsewhere. - Use this at the point in the code to which a continue statement - should jump. */ - -void -expand_loop_continue_here () -{ - do_pending_stack_adjust (); - emit_note (NULL_PTR, NOTE_INSN_LOOP_CONT); - emit_label (loop_stack->data.loop.continue_label); -} - -/* Finish a loop. Generate a jump back to the top and the loop-exit label. - Pop the block off of loop_stack. */ - -void -expand_end_loop () -{ - register rtx insn = get_last_insn (); - register rtx start_label = loop_stack->data.loop.start_label; - rtx last_test_insn = 0; - int num_insns = 0; - - /* Mark the continue-point at the top of the loop if none elsewhere. */ - if (start_label == loop_stack->data.loop.continue_label) - emit_note_before (NOTE_INSN_LOOP_CONT, start_label); - - do_pending_stack_adjust (); - - /* If optimizing, perhaps reorder the loop. If the loop - starts with a conditional exit, roll that to the end - where it will optimize together with the jump back. - - We look for the last conditional branch to the exit that we encounter - before hitting 30 insns or a CALL_INSN. If we see an unconditional - branch to the exit first, use it. - - We must also stop at NOTE_INSN_BLOCK_BEG and NOTE_INSN_BLOCK_END notes - because moving them is not valid. */ - - if (optimize - && - ! (GET_CODE (insn) == JUMP_INSN - && GET_CODE (PATTERN (insn)) == SET - && SET_DEST (PATTERN (insn)) == pc_rtx - && GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE)) - { - /* Scan insns from the top of the loop looking for a qualified - conditional exit. */ - for (insn = NEXT_INSN (loop_stack->data.loop.start_label); insn; - insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == CODE_LABEL) - break; - - if (GET_CODE (insn) == NOTE - && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG - || NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END)) - break; - - if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == INSN) - num_insns++; - - if (last_test_insn && num_insns > 30) - break; - - if (GET_CODE (insn) == JUMP_INSN && GET_CODE (PATTERN (insn)) == SET - && SET_DEST (PATTERN (insn)) == pc_rtx - && GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE - && ((GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 1)) == LABEL_REF - && (XEXP (XEXP (SET_SRC (PATTERN (insn)), 1), 0) - == loop_stack->data.loop.end_label)) - || (GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 2)) == LABEL_REF - && (XEXP (XEXP (SET_SRC (PATTERN (insn)), 2), 0) - == loop_stack->data.loop.end_label)))) - last_test_insn = insn; - - if (last_test_insn == 0 && GET_CODE (insn) == JUMP_INSN - && GET_CODE (PATTERN (insn)) == SET - && SET_DEST (PATTERN (insn)) == pc_rtx - && GET_CODE (SET_SRC (PATTERN (insn))) == LABEL_REF - && (XEXP (SET_SRC (PATTERN (insn)), 0) - == loop_stack->data.loop.end_label)) - /* Include BARRIER. */ - last_test_insn = NEXT_INSN (insn); - } - - if (last_test_insn != 0 && last_test_insn != get_last_insn ()) - { - /* We found one. Move everything from there up - to the end of the loop, and add a jump into the loop - to jump to there. */ - register rtx newstart_label = gen_label_rtx (); - register rtx start_move = start_label; - - /* If the start label is preceded by a NOTE_INSN_LOOP_CONT note, - then we want to move this note also. */ - if (GET_CODE (PREV_INSN (start_move)) == NOTE - && (NOTE_LINE_NUMBER (PREV_INSN (start_move)) - == NOTE_INSN_LOOP_CONT)) - start_move = PREV_INSN (start_move); - - emit_label_after (newstart_label, PREV_INSN (start_move)); - reorder_insns (start_move, last_test_insn, get_last_insn ()); - emit_jump_insn_after (gen_jump (start_label), - PREV_INSN (newstart_label)); - emit_barrier_after (PREV_INSN (newstart_label)); - start_label = newstart_label; - } - } - - emit_jump (start_label); - emit_note (NULL_PTR, NOTE_INSN_LOOP_END); - emit_label (loop_stack->data.loop.end_label); - - POPSTACK (loop_stack); - - last_expr_type = 0; -} - -/* Generate a jump to the current loop's continue-point. - This is usually the top of the loop, but may be specified - explicitly elsewhere. If not currently inside a loop, - return 0 and do nothing; caller will print an error message. */ - -int -expand_continue_loop (whichloop) - struct nesting *whichloop; -{ - last_expr_type = 0; - if (whichloop == 0) - whichloop = loop_stack; - if (whichloop == 0) - return 0; - expand_goto_internal (NULL_TREE, whichloop->data.loop.continue_label, - NULL_RTX); - return 1; -} - -/* Generate a jump to exit the current loop. If not currently inside a loop, - return 0 and do nothing; caller will print an error message. */ - -int -expand_exit_loop (whichloop) - struct nesting *whichloop; -{ - last_expr_type = 0; - if (whichloop == 0) - whichloop = loop_stack; - if (whichloop == 0) - return 0; - expand_goto_internal (NULL_TREE, whichloop->data.loop.end_label, NULL_RTX); - return 1; -} - -/* Generate a conditional jump to exit the current loop if COND - evaluates to zero. If not currently inside a loop, - return 0 and do nothing; caller will print an error message. */ - -int -expand_exit_loop_if_false (whichloop, cond) - struct nesting *whichloop; - tree cond; -{ - last_expr_type = 0; - if (whichloop == 0) - whichloop = loop_stack; - if (whichloop == 0) - return 0; - do_jump (cond, whichloop->data.loop.end_label, NULL_RTX); - return 1; -} - -/* Return non-zero if we should preserve sub-expressions as separate - pseudos. We never do so if we aren't optimizing. We always do so - if -fexpensive-optimizations. - - Otherwise, we only do so if we are in the "early" part of a loop. I.e., - the loop may still be a small one. */ - -int -preserve_subexpressions_p () -{ - rtx insn; - - if (flag_expensive_optimizations) - return 1; - - if (optimize == 0 || loop_stack == 0) - return 0; - - insn = get_last_insn_anywhere (); - - return (insn - && (INSN_UID (insn) - INSN_UID (loop_stack->data.loop.start_label) - < n_non_fixed_regs * 3)); - -} - -/* Generate a jump to exit the current loop, conditional, binding contour - or case statement. Not all such constructs are visible to this function, - only those started with EXIT_FLAG nonzero. Individual languages use - the EXIT_FLAG parameter to control which kinds of constructs you can - exit this way. - - If not currently inside anything that can be exited, - return 0 and do nothing; caller will print an error message. */ - -int -expand_exit_something () -{ - struct nesting *n; - last_expr_type = 0; - for (n = nesting_stack; n; n = n->all) - if (n->exit_label != 0) - { - expand_goto_internal (NULL_TREE, n->exit_label, NULL_RTX); - return 1; - } - - return 0; -} - -/* Generate RTL to return from the current function, with no value. - (That is, we do not do anything about returning any value.) */ - -void -expand_null_return () -{ - struct nesting *block = block_stack; - rtx last_insn = 0; - - /* Does any pending block have cleanups? */ - - while (block && block->data.block.cleanups == 0) - block = block->next; - - /* If yes, use a goto to return, since that runs cleanups. */ - - expand_null_return_1 (last_insn, block != 0); -} - -/* Generate RTL to return from the current function, with value VAL. */ - -void -expand_value_return (val) - rtx val; -{ - struct nesting *block = block_stack; - rtx last_insn = get_last_insn (); - rtx return_reg = DECL_RTL (DECL_RESULT (current_function_decl)); - - /* Copy the value to the return location - unless it's already there. */ - - if (return_reg != val) - { -#ifdef PROMOTE_FUNCTION_RETURN - enum machine_mode mode = DECL_MODE (DECL_RESULT (current_function_decl)); - tree type = TREE_TYPE (DECL_RESULT (current_function_decl)); - int unsignedp = TREE_UNSIGNED (type); - - if (TREE_CODE (type) == INTEGER_TYPE || TREE_CODE (type) == ENUMERAL_TYPE - || TREE_CODE (type) == BOOLEAN_TYPE || TREE_CODE (type) == CHAR_TYPE - || TREE_CODE (type) == REAL_TYPE || TREE_CODE (type) == POINTER_TYPE - || TREE_CODE (type) == OFFSET_TYPE) - { - PROMOTE_MODE (mode, unsignedp, type); - } - - if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode) - convert_move (return_reg, val, unsignedp); - else -#endif - emit_move_insn (return_reg, val); - } - if (GET_CODE (return_reg) == REG - && REGNO (return_reg) < FIRST_PSEUDO_REGISTER) - emit_insn (gen_rtx (USE, VOIDmode, return_reg)); - - /* Does any pending block have cleanups? */ - - while (block && block->data.block.cleanups == 0) - block = block->next; - - /* If yes, use a goto to return, since that runs cleanups. - Use LAST_INSN to put cleanups *before* the move insn emitted above. */ - - expand_null_return_1 (last_insn, block != 0); -} - -/* Output a return with no value. If LAST_INSN is nonzero, - pretend that the return takes place after LAST_INSN. - If USE_GOTO is nonzero then don't use a return instruction; - go to the return label instead. This causes any cleanups - of pending blocks to be executed normally. */ - -static void -expand_null_return_1 (last_insn, use_goto) - rtx last_insn; - int use_goto; -{ - rtx end_label = cleanup_label ? cleanup_label : return_label; - - clear_pending_stack_adjust (); - do_pending_stack_adjust (); - last_expr_type = 0; - - /* PCC-struct return always uses an epilogue. */ - if (current_function_returns_pcc_struct || use_goto) - { - if (end_label == 0) - end_label = return_label = gen_label_rtx (); - expand_goto_internal (NULL_TREE, end_label, last_insn); - return; - } - - /* Otherwise output a simple return-insn if one is available, - unless it won't do the job. */ -#ifdef HAVE_return - if (HAVE_return && use_goto == 0 && cleanup_label == 0) - { - emit_jump_insn (gen_return ()); - emit_barrier (); - return; - } -#endif - - /* Otherwise jump to the epilogue. */ - expand_goto_internal (NULL_TREE, end_label, last_insn); -} - -/* Generate RTL to evaluate the expression RETVAL and return it - from the current function. */ - -void -expand_return (retval) - tree retval; -{ - /* If there are any cleanups to be performed, then they will - be inserted following LAST_INSN. It is desirable - that the last_insn, for such purposes, should be the - last insn before computing the return value. Otherwise, cleanups - which call functions can clobber the return value. */ - /* ??? rms: I think that is erroneous, because in C++ it would - run destructors on variables that might be used in the subsequent - computation of the return value. */ - rtx last_insn = 0; - register rtx val = 0; - register rtx op0; - tree retval_rhs; - int cleanups; - struct nesting *block; - - /* If function wants no value, give it none. */ - if (TREE_CODE (TREE_TYPE (TREE_TYPE (current_function_decl))) == VOID_TYPE) - { - expand_expr (retval, NULL_RTX, VOIDmode, 0); - emit_queue (); - expand_null_return (); - return; - } - - /* Are any cleanups needed? E.g. C++ destructors to be run? */ - cleanups = any_pending_cleanups (1); - - if (TREE_CODE (retval) == RESULT_DECL) - retval_rhs = retval; - else if ((TREE_CODE (retval) == MODIFY_EXPR || TREE_CODE (retval) == INIT_EXPR) - && TREE_CODE (TREE_OPERAND (retval, 0)) == RESULT_DECL) - retval_rhs = TREE_OPERAND (retval, 1); - else if (TREE_TYPE (retval) == void_type_node) - /* Recognize tail-recursive call to void function. */ - retval_rhs = retval; - else - retval_rhs = NULL_TREE; - - /* Only use `last_insn' if there are cleanups which must be run. */ - if (cleanups || cleanup_label != 0) - last_insn = get_last_insn (); - - /* Distribute return down conditional expr if either of the sides - may involve tail recursion (see test below). This enhances the number - of tail recursions we see. Don't do this always since it can produce - sub-optimal code in some cases and we distribute assignments into - conditional expressions when it would help. */ - - if (optimize && retval_rhs != 0 - && frame_offset == 0 - && TREE_CODE (retval_rhs) == COND_EXPR - && (TREE_CODE (TREE_OPERAND (retval_rhs, 1)) == CALL_EXPR - || TREE_CODE (TREE_OPERAND (retval_rhs, 2)) == CALL_EXPR)) - { - rtx label = gen_label_rtx (); - do_jump (TREE_OPERAND (retval_rhs, 0), label, NULL_RTX); - expand_return (build (MODIFY_EXPR, TREE_TYPE (current_function_decl), - DECL_RESULT (current_function_decl), - TREE_OPERAND (retval_rhs, 1))); - emit_label (label); - expand_return (build (MODIFY_EXPR, TREE_TYPE (current_function_decl), - DECL_RESULT (current_function_decl), - TREE_OPERAND (retval_rhs, 2))); - return; - } - - /* For tail-recursive call to current function, - just jump back to the beginning. - It's unsafe if any auto variable in this function - has its address taken; for simplicity, - require stack frame to be empty. */ - if (optimize && retval_rhs != 0 - && frame_offset == 0 - && TREE_CODE (retval_rhs) == CALL_EXPR - && TREE_CODE (TREE_OPERAND (retval_rhs, 0)) == ADDR_EXPR - && TREE_OPERAND (TREE_OPERAND (retval_rhs, 0), 0) == current_function_decl - /* Finish checking validity, and if valid emit code - to set the argument variables for the new call. */ - && tail_recursion_args (TREE_OPERAND (retval_rhs, 1), - DECL_ARGUMENTS (current_function_decl))) - { - if (tail_recursion_label == 0) - { - tail_recursion_label = gen_label_rtx (); - emit_label_after (tail_recursion_label, - tail_recursion_reentry); - } - emit_queue (); - expand_goto_internal (NULL_TREE, tail_recursion_label, last_insn); - emit_barrier (); - return; - } -#ifdef HAVE_return - /* This optimization is safe if there are local cleanups - because expand_null_return takes care of them. - ??? I think it should also be safe when there is a cleanup label, - because expand_null_return takes care of them, too. - Any reason why not? */ - if (HAVE_return && cleanup_label == 0 - && ! current_function_returns_pcc_struct) - { - /* If this is return x == y; then generate - if (x == y) return 1; else return 0; - if we can do it with explicit return insns. */ - if (retval_rhs) - switch (TREE_CODE (retval_rhs)) - { - case EQ_EXPR: - case NE_EXPR: - case GT_EXPR: - case GE_EXPR: - case LT_EXPR: - case LE_EXPR: - case TRUTH_ANDIF_EXPR: - case TRUTH_ORIF_EXPR: - case TRUTH_AND_EXPR: - case TRUTH_OR_EXPR: - case TRUTH_NOT_EXPR: - case TRUTH_XOR_EXPR: - op0 = gen_label_rtx (); - jumpifnot (retval_rhs, op0); - expand_value_return (const1_rtx); - emit_label (op0); - expand_value_return (const0_rtx); - return; - } - } -#endif /* HAVE_return */ - - if (cleanups - && retval_rhs != 0 - && TREE_TYPE (retval_rhs) != void_type_node - && GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) == REG) - { - /* Calculate the return value into a pseudo reg. */ - val = expand_expr (retval_rhs, NULL_RTX, VOIDmode, 0); - emit_queue (); - /* All temporaries have now been used. */ - free_temp_slots (); - /* Return the calculated value, doing cleanups first. */ - expand_value_return (val); - } - else - { - /* No cleanups or no hard reg used; - calculate value into hard return reg. */ - expand_expr (retval, NULL_RTX, VOIDmode, 0); - emit_queue (); - free_temp_slots (); - expand_value_return (DECL_RTL (DECL_RESULT (current_function_decl))); - } -} - -/* Return 1 if the end of the generated RTX is not a barrier. - This means code already compiled can drop through. */ - -int -drop_through_at_end_p () -{ - rtx insn = get_last_insn (); - while (insn && GET_CODE (insn) == NOTE) - insn = PREV_INSN (insn); - return insn && GET_CODE (insn) != BARRIER; -} - -/* Emit code to alter this function's formal parms for a tail-recursive call. - ACTUALS is a list of actual parameter expressions (chain of TREE_LISTs). - FORMALS is the chain of decls of formals. - Return 1 if this can be done; - otherwise return 0 and do not emit any code. */ - -static int -tail_recursion_args (actuals, formals) - tree actuals, formals; -{ - register tree a = actuals, f = formals; - register int i; - register rtx *argvec; - - /* Check that number and types of actuals are compatible - with the formals. This is not always true in valid C code. - Also check that no formal needs to be addressable - and that all formals are scalars. */ - - /* Also count the args. */ - - for (a = actuals, f = formals, i = 0; a && f; a = TREE_CHAIN (a), f = TREE_CHAIN (f), i++) - { - if (TREE_TYPE (TREE_VALUE (a)) != TREE_TYPE (f)) - return 0; - if (GET_CODE (DECL_RTL (f)) != REG || DECL_MODE (f) == BLKmode) - return 0; - } - if (a != 0 || f != 0) - return 0; - - /* Compute all the actuals. */ - - argvec = (rtx *) alloca (i * sizeof (rtx)); - - for (a = actuals, i = 0; a; a = TREE_CHAIN (a), i++) - argvec[i] = expand_expr (TREE_VALUE (a), NULL_RTX, VOIDmode, 0); - - /* Find which actual values refer to current values of previous formals. - Copy each of them now, before any formal is changed. */ - - for (a = actuals, i = 0; a; a = TREE_CHAIN (a), i++) - { - int copy = 0; - register int j; - for (f = formals, j = 0; j < i; f = TREE_CHAIN (f), j++) - if (reg_mentioned_p (DECL_RTL (f), argvec[i])) - { copy = 1; break; } - if (copy) - argvec[i] = copy_to_reg (argvec[i]); - } - - /* Store the values of the actuals into the formals. */ - - for (f = formals, a = actuals, i = 0; f; - f = TREE_CHAIN (f), a = TREE_CHAIN (a), i++) - { - if (GET_MODE (DECL_RTL (f)) == GET_MODE (argvec[i])) - emit_move_insn (DECL_RTL (f), argvec[i]); - else - convert_move (DECL_RTL (f), argvec[i], - TREE_UNSIGNED (TREE_TYPE (TREE_VALUE (a)))); - } - - free_temp_slots (); - return 1; -} - -/* Generate the RTL code for entering a binding contour. - The variables are declared one by one, by calls to `expand_decl'. - - EXIT_FLAG is nonzero if this construct should be visible to - `exit_something'. */ - -void -expand_start_bindings (exit_flag) - int exit_flag; -{ - struct nesting *thisblock = ALLOC_NESTING (); - - rtx note = emit_note (NULL_PTR, NOTE_INSN_BLOCK_BEG); - - /* Make an entry on block_stack for the block we are entering. */ - - thisblock->next = block_stack; - thisblock->all = nesting_stack; - thisblock->depth = ++nesting_depth; - thisblock->data.block.stack_level = 0; - thisblock->data.block.cleanups = 0; - thisblock->data.block.function_call_count = 0; -#if 0 - if (block_stack) - { - if (block_stack->data.block.cleanups == NULL_TREE - && (block_stack->data.block.outer_cleanups == NULL_TREE - || block_stack->data.block.outer_cleanups == empty_cleanup_list)) - thisblock->data.block.outer_cleanups = empty_cleanup_list; - else - thisblock->data.block.outer_cleanups - = tree_cons (NULL_TREE, block_stack->data.block.cleanups, - block_stack->data.block.outer_cleanups); - } - else - thisblock->data.block.outer_cleanups = 0; -#endif -#if 1 - if (block_stack - && !(block_stack->data.block.cleanups == NULL_TREE - && block_stack->data.block.outer_cleanups == NULL_TREE)) - thisblock->data.block.outer_cleanups - = tree_cons (NULL_TREE, block_stack->data.block.cleanups, - block_stack->data.block.outer_cleanups); - else - thisblock->data.block.outer_cleanups = 0; -#endif - thisblock->data.block.label_chain = 0; - thisblock->data.block.innermost_stack_block = stack_block_stack; - thisblock->data.block.first_insn = note; - thisblock->data.block.block_start_count = ++block_start_count; - thisblock->exit_label = exit_flag ? gen_label_rtx () : 0; - block_stack = thisblock; - nesting_stack = thisblock; - - /* Make a new level for allocating stack slots. */ - push_temp_slots (); -} - -/* Given a pointer to a BLOCK node, save a pointer to the most recently - generated NOTE_INSN_BLOCK_END in the BLOCK_END_NOTE field of the given - BLOCK node. */ - -void -remember_end_note (block) - register tree block; -{ - BLOCK_END_NOTE (block) = last_block_end_note; - last_block_end_note = NULL_RTX; -} - -/* Generate RTL code to terminate a binding contour. - VARS is the chain of VAR_DECL nodes - for the variables bound in this contour. - MARK_ENDS is nonzero if we should put a note at the beginning - and end of this binding contour. - - DONT_JUMP_IN is nonzero if it is not valid to jump into this contour. - (That is true automatically if the contour has a saved stack level.) */ - -void -expand_end_bindings (vars, mark_ends, dont_jump_in) - tree vars; - int mark_ends; - int dont_jump_in; -{ - register struct nesting *thisblock = block_stack; - register tree decl; - - if (warn_unused) - for (decl = vars; decl; decl = TREE_CHAIN (decl)) - if (! TREE_USED (decl) && TREE_CODE (decl) == VAR_DECL - && ! DECL_IN_SYSTEM_HEADER (decl)) - warning_with_decl (decl, "unused variable `%s'"); - - if (thisblock->exit_label) - { - do_pending_stack_adjust (); - emit_label (thisblock->exit_label); - } - - /* If necessary, make a handler for nonlocal gotos taking - place in the function calls in this block. */ - if (function_call_count != thisblock->data.block.function_call_count - && nonlocal_labels - /* Make handler for outermost block - if there were any nonlocal gotos to this function. */ - && (thisblock->next == 0 ? current_function_has_nonlocal_label - /* Make handler for inner block if it has something - special to do when you jump out of it. */ - : (thisblock->data.block.cleanups != 0 - || thisblock->data.block.stack_level != 0))) - { - tree link; - rtx afterward = gen_label_rtx (); - rtx handler_label = gen_label_rtx (); - rtx save_receiver = gen_reg_rtx (Pmode); - - /* Don't let jump_optimize delete the handler. */ - LABEL_PRESERVE_P (handler_label) = 1; - - /* Record the handler address in the stack slot for that purpose, - during this block, saving and restoring the outer value. */ - if (thisblock->next != 0) - { - emit_move_insn (nonlocal_goto_handler_slot, save_receiver); - emit_insn_before (gen_move_insn (save_receiver, - nonlocal_goto_handler_slot), - thisblock->data.block.first_insn); - } - emit_insn_before (gen_move_insn (nonlocal_goto_handler_slot, - gen_rtx (LABEL_REF, Pmode, - handler_label)), - thisblock->data.block.first_insn); - - /* Jump around the handler; it runs only when specially invoked. */ - emit_jump (afterward); - emit_label (handler_label); - -#ifdef HAVE_nonlocal_goto - if (! HAVE_nonlocal_goto) -#endif - /* First adjust our frame pointer to its actual value. It was - previously set to the start of the virtual area corresponding to - the stacked variables when we branched here and now needs to be - adjusted to the actual hardware fp value. - - Assignments are to virtual registers are converted by - instantiate_virtual_regs into the corresponding assignment - to the underlying register (fp in this case) that makes - the original assignment true. - So the following insn will actually be - decrementing fp by STARTING_FRAME_OFFSET. */ - emit_move_insn (virtual_stack_vars_rtx, frame_pointer_rtx); - -#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM - if (fixed_regs[ARG_POINTER_REGNUM]) - { -#ifdef ELIMINABLE_REGS - /* If the argument pointer can be eliminated in favor of the - frame pointer, we don't need to restore it. We assume here - that if such an elimination is present, it can always be used. - This is the case on all known machines; if we don't make this - assumption, we do unnecessary saving on many machines. */ - static struct elims {int from, to;} elim_regs[] = ELIMINABLE_REGS; - int i; - - for (i = 0; i < sizeof elim_regs / sizeof elim_regs[0]; i++) - if (elim_regs[i].from == ARG_POINTER_REGNUM - && elim_regs[i].to == FRAME_POINTER_REGNUM) - break; - - if (i == sizeof elim_regs / sizeof elim_regs [0]) -#endif - { - /* Now restore our arg pointer from the address at which it - was saved in our stack frame. - If there hasn't be space allocated for it yet, make - some now. */ - if (arg_pointer_save_area == 0) - arg_pointer_save_area - = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0); - emit_move_insn (virtual_incoming_args_rtx, - /* We need a pseudo here, or else - instantiate_virtual_regs_1 complains. */ - copy_to_reg (arg_pointer_save_area)); - } - } -#endif - - /* The handler expects the desired label address in the static chain - register. It tests the address and does an appropriate jump - to whatever label is desired. */ - for (link = nonlocal_labels; link; link = TREE_CHAIN (link)) - /* Skip any labels we shouldn't be able to jump to from here. */ - if (! DECL_TOO_LATE (TREE_VALUE (link))) - { - rtx not_this = gen_label_rtx (); - rtx this = gen_label_rtx (); - do_jump_if_equal (static_chain_rtx, - gen_rtx (LABEL_REF, Pmode, DECL_RTL (TREE_VALUE (link))), - this, 0); - emit_jump (not_this); - emit_label (this); - expand_goto (TREE_VALUE (link)); - emit_label (not_this); - } - /* If label is not recognized, abort. */ - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "abort"), 0, - VOIDmode, 0); - emit_label (afterward); - } - - /* Don't allow jumping into a block that has cleanups or a stack level. */ - if (dont_jump_in - || thisblock->data.block.stack_level != 0 - || thisblock->data.block.cleanups != 0) - { - struct label_chain *chain; - - /* Any labels in this block are no longer valid to go to. - Mark them to cause an error message. */ - for (chain = thisblock->data.block.label_chain; chain; chain = chain->next) - { - DECL_TOO_LATE (chain->label) = 1; - /* If any goto without a fixup came to this label, - that must be an error, because gotos without fixups - come from outside all saved stack-levels and all cleanups. */ - if (TREE_ADDRESSABLE (chain->label)) - error_with_decl (chain->label, - "label `%s' used before containing binding contour"); - } - } - - /* Restore stack level in effect before the block - (only if variable-size objects allocated). */ - /* Perform any cleanups associated with the block. */ - - if (thisblock->data.block.stack_level != 0 - || thisblock->data.block.cleanups != 0) - { - /* Don't let cleanups affect ({...}) constructs. */ - int old_expr_stmts_for_value = expr_stmts_for_value; - rtx old_last_expr_value = last_expr_value; - tree old_last_expr_type = last_expr_type; - expr_stmts_for_value = 0; - - /* Do the cleanups. */ - expand_cleanups (thisblock->data.block.cleanups, NULL_TREE); - do_pending_stack_adjust (); - - expr_stmts_for_value = old_expr_stmts_for_value; - last_expr_value = old_last_expr_value; - last_expr_type = old_last_expr_type; - - /* Restore the stack level. */ - - if (thisblock->data.block.stack_level != 0) - { - emit_stack_restore (thisblock->next ? SAVE_BLOCK : SAVE_FUNCTION, - thisblock->data.block.stack_level, NULL_RTX); - if (nonlocal_goto_handler_slot != 0) - emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, - NULL_RTX); - } - - /* Any gotos out of this block must also do these things. - Also report any gotos with fixups that came to labels in this - level. */ - fixup_gotos (thisblock, - thisblock->data.block.stack_level, - thisblock->data.block.cleanups, - thisblock->data.block.first_insn, - dont_jump_in); - } - - /* Mark the beginning and end of the scope if requested. - We do this now, after running cleanups on the variables - just going out of scope, so they are in scope for their cleanups. */ - - if (mark_ends) - last_block_end_note = emit_note (NULL_PTR, NOTE_INSN_BLOCK_END); - else - /* Get rid of the beginning-mark if we don't make an end-mark. */ - NOTE_LINE_NUMBER (thisblock->data.block.first_insn) = NOTE_INSN_DELETED; - - /* If doing stupid register allocation, make sure lives of all - register variables declared here extend thru end of scope. */ - - if (obey_regdecls) - for (decl = vars; decl; decl = TREE_CHAIN (decl)) - { - rtx rtl = DECL_RTL (decl); - if (TREE_CODE (decl) == VAR_DECL && rtl != 0) - use_variable (rtl); - } - - /* Restore block_stack level for containing block. */ - - stack_block_stack = thisblock->data.block.innermost_stack_block; - POPSTACK (block_stack); - - /* Pop the stack slot nesting and free any slots at this level. */ - pop_temp_slots (); -} - -/* Generate RTL for the automatic variable declaration DECL. - (Other kinds of declarations are simply ignored if seen here.) - CLEANUP is an expression to be executed at exit from this binding contour; - for example, in C++, it might call the destructor for this variable. - - If CLEANUP contains any SAVE_EXPRs, then you must preevaluate them - either before or after calling `expand_decl' but before compiling - any subsequent expressions. This is because CLEANUP may be expanded - more than once, on different branches of execution. - For the same reason, CLEANUP may not contain a CALL_EXPR - except as its topmost node--else `preexpand_calls' would get confused. - - If CLEANUP is nonzero and DECL is zero, we record a cleanup - that is not associated with any particular variable. - - There is no special support here for C++ constructors. - They should be handled by the proper code in DECL_INITIAL. */ - -void -expand_decl (decl) - register tree decl; -{ - struct nesting *thisblock = block_stack; - tree type = TREE_TYPE (decl); - - /* Only automatic variables need any expansion done. - Static and external variables, and external functions, - will be handled by `assemble_variable' (called from finish_decl). - TYPE_DECL and CONST_DECL require nothing. - PARM_DECLs are handled in `assign_parms'. */ - - if (TREE_CODE (decl) != VAR_DECL) - return; - if (TREE_STATIC (decl) || DECL_EXTERNAL (decl)) - return; - - /* Create the RTL representation for the variable. */ - - if (type == error_mark_node) - DECL_RTL (decl) = gen_rtx (MEM, BLKmode, const0_rtx); - else if (DECL_SIZE (decl) == 0) - /* Variable with incomplete type. */ - { - if (DECL_INITIAL (decl) == 0) - /* Error message was already done; now avoid a crash. */ - DECL_RTL (decl) = assign_stack_temp (DECL_MODE (decl), 0, 1); - else - /* An initializer is going to decide the size of this array. - Until we know the size, represent its address with a reg. */ - DECL_RTL (decl) = gen_rtx (MEM, BLKmode, gen_reg_rtx (Pmode)); - } - else if (DECL_MODE (decl) != BLKmode - /* If -ffloat-store, don't put explicit float vars - into regs. */ - && !(flag_float_store - && TREE_CODE (type) == REAL_TYPE) - && ! TREE_THIS_VOLATILE (decl) - && ! TREE_ADDRESSABLE (decl) - && (DECL_REGISTER (decl) || ! obey_regdecls)) - { - /* Automatic variable that can go in a register. */ - enum machine_mode reg_mode = DECL_MODE (decl); - int unsignedp = TREE_UNSIGNED (type); - - if (TREE_CODE (type) == INTEGER_TYPE || TREE_CODE (type) == ENUMERAL_TYPE - || TREE_CODE (type) == BOOLEAN_TYPE || TREE_CODE (type) == CHAR_TYPE - || TREE_CODE (type) == REAL_TYPE || TREE_CODE (type) == POINTER_TYPE - || TREE_CODE (type) == OFFSET_TYPE) - { - PROMOTE_MODE (reg_mode, unsignedp, type); - } - - DECL_RTL (decl) = gen_reg_rtx (reg_mode); - if (TREE_CODE (type) == POINTER_TYPE) - mark_reg_pointer (DECL_RTL (decl)); - REG_USERVAR_P (DECL_RTL (decl)) = 1; - } - else if (TREE_CODE (DECL_SIZE (decl)) == INTEGER_CST) - { - /* Variable of fixed size that goes on the stack. */ - rtx oldaddr = 0; - rtx addr; - - /* If we previously made RTL for this decl, it must be an array - whose size was determined by the initializer. - The old address was a register; set that register now - to the proper address. */ - if (DECL_RTL (decl) != 0) - { - if (GET_CODE (DECL_RTL (decl)) != MEM - || GET_CODE (XEXP (DECL_RTL (decl), 0)) != REG) - abort (); - oldaddr = XEXP (DECL_RTL (decl), 0); - } - - DECL_RTL (decl) - = assign_stack_temp (DECL_MODE (decl), - ((TREE_INT_CST_LOW (DECL_SIZE (decl)) - + BITS_PER_UNIT - 1) - / BITS_PER_UNIT), - 1); - - /* Set alignment we actually gave this decl. */ - DECL_ALIGN (decl) = (DECL_MODE (decl) == BLKmode ? BIGGEST_ALIGNMENT - : GET_MODE_BITSIZE (DECL_MODE (decl))); - - if (oldaddr) - { - addr = force_operand (XEXP (DECL_RTL (decl), 0), oldaddr); - if (addr != oldaddr) - emit_move_insn (oldaddr, addr); - } - - /* If this is a memory ref that contains aggregate components, - mark it as such for cse and loop optimize. */ - MEM_IN_STRUCT_P (DECL_RTL (decl)) - = (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE - || TREE_CODE (TREE_TYPE (decl)) == QUAL_UNION_TYPE); -#if 0 - /* If this is in memory because of -ffloat-store, - set the volatile bit, to prevent optimizations from - undoing the effects. */ - if (flag_float_store && TREE_CODE (type) == REAL_TYPE) - MEM_VOLATILE_P (DECL_RTL (decl)) = 1; -#endif - } - else - /* Dynamic-size object: must push space on the stack. */ - { - rtx address, size; - - /* Record the stack pointer on entry to block, if have - not already done so. */ - if (thisblock->data.block.stack_level == 0) - { - do_pending_stack_adjust (); - emit_stack_save (thisblock->next ? SAVE_BLOCK : SAVE_FUNCTION, - &thisblock->data.block.stack_level, - thisblock->data.block.first_insn); - stack_block_stack = thisblock; - } - - /* Compute the variable's size, in bytes. */ - size = expand_expr (size_binop (CEIL_DIV_EXPR, - DECL_SIZE (decl), - size_int (BITS_PER_UNIT)), - NULL_RTX, VOIDmode, 0); - free_temp_slots (); - - /* This is equivalent to calling alloca. */ - current_function_calls_alloca = 1; - - /* Allocate space on the stack for the variable. */ - address = allocate_dynamic_stack_space (size, NULL_RTX, - DECL_ALIGN (decl)); - - if (nonlocal_goto_handler_slot != 0) - emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX); - - /* Reference the variable indirect through that rtx. */ - DECL_RTL (decl) = gen_rtx (MEM, DECL_MODE (decl), address); - - /* If this is a memory ref that contains aggregate components, - mark it as such for cse and loop optimize. */ - MEM_IN_STRUCT_P (DECL_RTL (decl)) - = (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE - || TREE_CODE (TREE_TYPE (decl)) == QUAL_UNION_TYPE); - - /* Indicate the alignment we actually gave this variable. */ -#ifdef STACK_BOUNDARY - DECL_ALIGN (decl) = STACK_BOUNDARY; -#else - DECL_ALIGN (decl) = BIGGEST_ALIGNMENT; -#endif - } - - if (TREE_THIS_VOLATILE (decl)) - MEM_VOLATILE_P (DECL_RTL (decl)) = 1; -#if 0 /* A variable is not necessarily unchanging - just because it is const. RTX_UNCHANGING_P - means no change in the function, - not merely no change in the variable's scope. - It is correct to set RTX_UNCHANGING_P if the variable's scope - is the whole function. There's no convenient way to test that. */ - if (TREE_READONLY (decl)) - RTX_UNCHANGING_P (DECL_RTL (decl)) = 1; -#endif - - /* If doing stupid register allocation, make sure life of any - register variable starts here, at the start of its scope. */ - - if (obey_regdecls) - use_variable (DECL_RTL (decl)); -} - -/* Emit code to perform the initialization of a declaration DECL. */ - -void -expand_decl_init (decl) - tree decl; -{ - int was_used = TREE_USED (decl); - - if (TREE_STATIC (decl)) - return; - - /* Compute and store the initial value now. */ - - if (DECL_INITIAL (decl) == error_mark_node) - { - enum tree_code code = TREE_CODE (TREE_TYPE (decl)); - if (code == INTEGER_TYPE || code == REAL_TYPE || code == ENUMERAL_TYPE - || code == POINTER_TYPE) - expand_assignment (decl, convert (TREE_TYPE (decl), integer_zero_node), - 0, 0); - emit_queue (); - } - else if (DECL_INITIAL (decl) && TREE_CODE (DECL_INITIAL (decl)) != TREE_LIST) - { - emit_line_note (DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl)); - expand_assignment (decl, DECL_INITIAL (decl), 0, 0); - emit_queue (); - } - - /* Don't let the initialization count as "using" the variable. */ - TREE_USED (decl) = was_used; - - /* Free any temporaries we made while initializing the decl. */ - free_temp_slots (); -} - -/* CLEANUP is an expression to be executed at exit from this binding contour; - for example, in C++, it might call the destructor for this variable. - - If CLEANUP contains any SAVE_EXPRs, then you must preevaluate them - either before or after calling `expand_decl' but before compiling - any subsequent expressions. This is because CLEANUP may be expanded - more than once, on different branches of execution. - For the same reason, CLEANUP may not contain a CALL_EXPR - except as its topmost node--else `preexpand_calls' would get confused. - - If CLEANUP is nonzero and DECL is zero, we record a cleanup - that is not associated with any particular variable. */ - -int -expand_decl_cleanup (decl, cleanup) - tree decl, cleanup; -{ - struct nesting *thisblock = block_stack; - - /* Error if we are not in any block. */ - if (thisblock == 0) - return 0; - - /* Record the cleanup if there is one. */ - - if (cleanup != 0) - { - thisblock->data.block.cleanups - = temp_tree_cons (decl, cleanup, thisblock->data.block.cleanups); - /* If this block has a cleanup, it belongs in stack_block_stack. */ - stack_block_stack = thisblock; - } - return 1; -} - -/* DECL is an anonymous union. CLEANUP is a cleanup for DECL. - DECL_ELTS is the list of elements that belong to DECL's type. - In each, the TREE_VALUE is a VAR_DECL, and the TREE_PURPOSE a cleanup. */ - -void -expand_anon_union_decl (decl, cleanup, decl_elts) - tree decl, cleanup, decl_elts; -{ - struct nesting *thisblock = block_stack; - rtx x; - - expand_decl (decl, cleanup); - x = DECL_RTL (decl); - - while (decl_elts) - { - tree decl_elt = TREE_VALUE (decl_elts); - tree cleanup_elt = TREE_PURPOSE (decl_elts); - enum machine_mode mode = TYPE_MODE (TREE_TYPE (decl_elt)); - - /* (SUBREG (MEM ...)) at RTL generation time is invalid, so we - instead create a new MEM rtx with the proper mode. */ - if (GET_CODE (x) == MEM) - { - if (mode == GET_MODE (x)) - DECL_RTL (decl_elt) = x; - else - { - DECL_RTL (decl_elt) = gen_rtx (MEM, mode, copy_rtx (XEXP (x, 0))); - MEM_IN_STRUCT_P (DECL_RTL (decl_elt)) = MEM_IN_STRUCT_P (x); - RTX_UNCHANGING_P (DECL_RTL (decl_elt)) = RTX_UNCHANGING_P (x); - } - } - else if (GET_CODE (x) == REG) - { - if (mode == GET_MODE (x)) - DECL_RTL (decl_elt) = x; - else - DECL_RTL (decl_elt) = gen_rtx (SUBREG, mode, x, 0); - } - else - abort (); - - /* Record the cleanup if there is one. */ - - if (cleanup != 0) - thisblock->data.block.cleanups - = temp_tree_cons (decl_elt, cleanup_elt, - thisblock->data.block.cleanups); - - decl_elts = TREE_CHAIN (decl_elts); - } -} - -/* Expand a list of cleanups LIST. - Elements may be expressions or may be nested lists. - - If DONT_DO is nonnull, then any list-element - whose TREE_PURPOSE matches DONT_DO is omitted. - This is sometimes used to avoid a cleanup associated with - a value that is being returned out of the scope. */ - -static void -expand_cleanups (list, dont_do) - tree list; - tree dont_do; -{ - tree tail; - for (tail = list; tail; tail = TREE_CHAIN (tail)) - if (dont_do == 0 || TREE_PURPOSE (tail) != dont_do) - { - if (TREE_CODE (TREE_VALUE (tail)) == TREE_LIST) - expand_cleanups (TREE_VALUE (tail), dont_do); - else - { - /* Cleanups may be run multiple times. For example, - when exiting a binding contour, we expand the - cleanups associated with that contour. When a goto - within that binding contour has a target outside that - contour, it will expand all cleanups from its scope to - the target. Though the cleanups are expanded multiple - times, the control paths are non-overlapping so the - cleanups will not be executed twice. */ - expand_expr (TREE_VALUE (tail), const0_rtx, VOIDmode, 0); - free_temp_slots (); - } - } -} - -/* Move all cleanups from the current block_stack - to the containing block_stack, where they are assumed to - have been created. If anything can cause a temporary to - be created, but not expanded for more than one level of - block_stacks, then this code will have to change. */ - -void -move_cleanups_up () -{ - struct nesting *block = block_stack; - struct nesting *outer = block->next; - - outer->data.block.cleanups - = chainon (block->data.block.cleanups, - outer->data.block.cleanups); - block->data.block.cleanups = 0; -} - -tree -last_cleanup_this_contour () -{ - if (block_stack == 0) - return 0; - - return block_stack->data.block.cleanups; -} - -/* Return 1 if there are any pending cleanups at this point. - If THIS_CONTOUR is nonzero, check the current contour as well. - Otherwise, look only at the contours that enclose this one. */ - -int -any_pending_cleanups (this_contour) - int this_contour; -{ - struct nesting *block; - - if (block_stack == 0) - return 0; - - if (this_contour && block_stack->data.block.cleanups != NULL) - return 1; - if (block_stack->data.block.cleanups == 0 - && (block_stack->data.block.outer_cleanups == 0 -#if 0 - || block_stack->data.block.outer_cleanups == empty_cleanup_list -#endif - )) - return 0; - - for (block = block_stack->next; block; block = block->next) - if (block->data.block.cleanups != 0) - return 1; - - return 0; -} - -/* Enter a case (Pascal) or switch (C) statement. - Push a block onto case_stack and nesting_stack - to accumulate the case-labels that are seen - and to record the labels generated for the statement. - - EXIT_FLAG is nonzero if `exit_something' should exit this case stmt. - Otherwise, this construct is transparent for `exit_something'. - - EXPR is the index-expression to be dispatched on. - TYPE is its nominal type. We could simply convert EXPR to this type, - but instead we take short cuts. */ - -void -expand_start_case (exit_flag, expr, type, printname) - int exit_flag; - tree expr; - tree type; - char *printname; -{ - register struct nesting *thiscase = ALLOC_NESTING (); - - /* Make an entry on case_stack for the case we are entering. */ - - thiscase->next = case_stack; - thiscase->all = nesting_stack; - thiscase->depth = ++nesting_depth; - thiscase->exit_label = exit_flag ? gen_label_rtx () : 0; - thiscase->data.case_stmt.case_list = 0; - thiscase->data.case_stmt.index_expr = expr; - thiscase->data.case_stmt.nominal_type = type; - thiscase->data.case_stmt.default_label = 0; - thiscase->data.case_stmt.num_ranges = 0; - thiscase->data.case_stmt.printname = printname; - thiscase->data.case_stmt.seenlabel = 0; - case_stack = thiscase; - nesting_stack = thiscase; - - do_pending_stack_adjust (); - - /* Make sure case_stmt.start points to something that won't - need any transformation before expand_end_case. */ - if (GET_CODE (get_last_insn ()) != NOTE) - emit_note (NULL_PTR, NOTE_INSN_DELETED); - - thiscase->data.case_stmt.start = get_last_insn (); -} - -/* Start a "dummy case statement" within which case labels are invalid - and are not connected to any larger real case statement. - This can be used if you don't want to let a case statement jump - into the middle of certain kinds of constructs. */ - -void -expand_start_case_dummy () -{ - register struct nesting *thiscase = ALLOC_NESTING (); - - /* Make an entry on case_stack for the dummy. */ - - thiscase->next = case_stack; - thiscase->all = nesting_stack; - thiscase->depth = ++nesting_depth; - thiscase->exit_label = 0; - thiscase->data.case_stmt.case_list = 0; - thiscase->data.case_stmt.start = 0; - thiscase->data.case_stmt.nominal_type = 0; - thiscase->data.case_stmt.default_label = 0; - thiscase->data.case_stmt.num_ranges = 0; - case_stack = thiscase; - nesting_stack = thiscase; -} - -/* End a dummy case statement. */ - -void -expand_end_case_dummy () -{ - POPSTACK (case_stack); -} - -/* Return the data type of the index-expression - of the innermost case statement, or null if none. */ - -tree -case_index_expr_type () -{ - if (case_stack) - return TREE_TYPE (case_stack->data.case_stmt.index_expr); - return 0; -} - -/* Accumulate one case or default label inside a case or switch statement. - VALUE is the value of the case (a null pointer, for a default label). - - If not currently inside a case or switch statement, return 1 and do - nothing. The caller will print a language-specific error message. - If VALUE is a duplicate or overlaps, return 2 and do nothing - except store the (first) duplicate node in *DUPLICATE. - If VALUE is out of range, return 3 and do nothing. - If we are jumping into the scope of a cleaup or var-sized array, return 5. - Return 0 on success. - - Extended to handle range statements. */ - -int -pushcase (value, label, duplicate) - register tree value; - register tree label; - tree *duplicate; -{ - register struct case_node **l; - register struct case_node *n; - tree index_type; - tree nominal_type; - - /* Fail if not inside a real case statement. */ - if (! (case_stack && case_stack->data.case_stmt.start)) - return 1; - - if (stack_block_stack - && stack_block_stack->depth > case_stack->depth) - return 5; - - index_type = TREE_TYPE (case_stack->data.case_stmt.index_expr); - nominal_type = case_stack->data.case_stmt.nominal_type; - - /* If the index is erroneous, avoid more problems: pretend to succeed. */ - if (index_type == error_mark_node) - return 0; - - /* Convert VALUE to the type in which the comparisons are nominally done. */ - if (value != 0) - value = convert (nominal_type, value); - - /* If this is the first label, warn if any insns have been emitted. */ - if (case_stack->data.case_stmt.seenlabel == 0) - { - rtx insn; - for (insn = case_stack->data.case_stmt.start; - insn; - insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == CODE_LABEL) - break; - if (GET_CODE (insn) != NOTE - && (GET_CODE (insn) != INSN || GET_CODE (PATTERN (insn)) != USE)) - { - warning ("unreachable code at beginning of %s", - case_stack->data.case_stmt.printname); - break; - } - } - } - case_stack->data.case_stmt.seenlabel = 1; - - /* Fail if this value is out of range for the actual type of the index - (which may be narrower than NOMINAL_TYPE). */ - if (value != 0 && ! int_fits_type_p (value, index_type)) - return 3; - - /* Fail if this is a duplicate or overlaps another entry. */ - if (value == 0) - { - if (case_stack->data.case_stmt.default_label != 0) - { - *duplicate = case_stack->data.case_stmt.default_label; - return 2; - } - case_stack->data.case_stmt.default_label = label; - } - else - { - /* Find the elt in the chain before which to insert the new value, - to keep the chain sorted in increasing order. - But report an error if this element is a duplicate. */ - for (l = &case_stack->data.case_stmt.case_list; - /* Keep going past elements distinctly less than VALUE. */ - *l != 0 && tree_int_cst_lt ((*l)->high, value); - l = &(*l)->right) - ; - if (*l) - { - /* Element we will insert before must be distinctly greater; - overlap means error. */ - if (! tree_int_cst_lt (value, (*l)->low)) - { - *duplicate = (*l)->code_label; - return 2; - } - } - - /* Add this label to the chain, and succeed. - Copy VALUE so it is on temporary rather than momentary - obstack and will thus survive till the end of the case statement. */ - n = (struct case_node *) oballoc (sizeof (struct case_node)); - n->left = 0; - n->right = *l; - n->high = n->low = copy_node (value); - n->code_label = label; - *l = n; - } - - expand_label (label); - return 0; -} - -/* Like pushcase but this case applies to all values - between VALUE1 and VALUE2 (inclusive). - The return value is the same as that of pushcase - but there is one additional error code: - 4 means the specified range was empty. */ - -int -pushcase_range (value1, value2, label, duplicate) - register tree value1, value2; - register tree label; - tree *duplicate; -{ - register struct case_node **l; - register struct case_node *n; - tree index_type; - tree nominal_type; - - /* Fail if not inside a real case statement. */ - if (! (case_stack && case_stack->data.case_stmt.start)) - return 1; - - if (stack_block_stack - && stack_block_stack->depth > case_stack->depth) - return 5; - - index_type = TREE_TYPE (case_stack->data.case_stmt.index_expr); - nominal_type = case_stack->data.case_stmt.nominal_type; - - /* If the index is erroneous, avoid more problems: pretend to succeed. */ - if (index_type == error_mark_node) - return 0; - - /* If this is the first label, warn if any insns have been emitted. */ - if (case_stack->data.case_stmt.seenlabel == 0) - { - rtx insn; - for (insn = case_stack->data.case_stmt.start; - insn; - insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == CODE_LABEL) - break; - if (GET_CODE (insn) != NOTE - && (GET_CODE (insn) != INSN || GET_CODE (PATTERN (insn)) != USE)) - { - warning ("unreachable code at beginning of %s", - case_stack->data.case_stmt.printname); - break; - } - } - } - case_stack->data.case_stmt.seenlabel = 1; - - /* Convert VALUEs to type in which the comparisons are nominally done. */ - if (value1 == 0) /* Negative infinity. */ - value1 = TYPE_MIN_VALUE(index_type); - value1 = convert (nominal_type, value1); - - if (value2 == 0) /* Positive infinity. */ - value2 = TYPE_MAX_VALUE(index_type); - value2 = convert (nominal_type, value2); - - /* Fail if these values are out of range. */ - if (! int_fits_type_p (value1, index_type)) - return 3; - - if (! int_fits_type_p (value2, index_type)) - return 3; - - /* Fail if the range is empty. */ - if (tree_int_cst_lt (value2, value1)) - return 4; - - /* If the bounds are equal, turn this into the one-value case. */ - if (tree_int_cst_equal (value1, value2)) - return pushcase (value1, label, duplicate); - - /* Find the elt in the chain before which to insert the new value, - to keep the chain sorted in increasing order. - But report an error if this element is a duplicate. */ - for (l = &case_stack->data.case_stmt.case_list; - /* Keep going past elements distinctly less than this range. */ - *l != 0 && tree_int_cst_lt ((*l)->high, value1); - l = &(*l)->right) - ; - if (*l) - { - /* Element we will insert before must be distinctly greater; - overlap means error. */ - if (! tree_int_cst_lt (value2, (*l)->low)) - { - *duplicate = (*l)->code_label; - return 2; - } - } - - /* Add this label to the chain, and succeed. - Copy VALUE1, VALUE2 so they are on temporary rather than momentary - obstack and will thus survive till the end of the case statement. */ - - n = (struct case_node *) oballoc (sizeof (struct case_node)); - n->left = 0; - n->right = *l; - n->low = copy_node (value1); - n->high = copy_node (value2); - n->code_label = label; - *l = n; - - expand_label (label); - - case_stack->data.case_stmt.num_ranges++; - - return 0; -} - -/* Called when the index of a switch statement is an enumerated type - and there is no default label. - - Checks that all enumeration literals are covered by the case - expressions of a switch. Also, warn if there are any extra - switch cases that are *not* elements of the enumerated type. - - If all enumeration literals were covered by the case expressions, - turn one of the expressions into the default expression since it should - not be possible to fall through such a switch. */ - -void -check_for_full_enumeration_handling (type) - tree type; -{ - register struct case_node *n; - register struct case_node **l; - register tree chain; - int all_values = 1; - - /* The time complexity of this loop is currently O(N * M), with - N being the number of members in the enumerated type, and - M being the number of case expressions in the switch. */ - - for (chain = TYPE_VALUES (type); - chain; - chain = TREE_CHAIN (chain)) - { - /* Find a match between enumeral and case expression, if possible. - Quit looking when we've gone too far (since case expressions - are kept sorted in ascending order). Warn about enumerators not - handled in the switch statement case expression list. */ - - for (n = case_stack->data.case_stmt.case_list; - n && tree_int_cst_lt (n->high, TREE_VALUE (chain)); - n = n->right) - ; - - if (!n || tree_int_cst_lt (TREE_VALUE (chain), n->low)) - { - if (warn_switch) - warning ("enumeration value `%s' not handled in switch", - IDENTIFIER_POINTER (TREE_PURPOSE (chain))); - all_values = 0; - } - } - - /* Now we go the other way around; we warn if there are case - expressions that don't correspond to enumerators. This can - occur since C and C++ don't enforce type-checking of - assignments to enumeration variables. */ - - if (warn_switch) - for (n = case_stack->data.case_stmt.case_list; n; n = n->right) - { - for (chain = TYPE_VALUES (type); - chain && !tree_int_cst_equal (n->low, TREE_VALUE (chain)); - chain = TREE_CHAIN (chain)) - ; - - if (!chain) - { - if (TYPE_NAME (type) == 0) - warning ("case value `%d' not in enumerated type", - TREE_INT_CST_LOW (n->low)); - else - warning ("case value `%d' not in enumerated type `%s'", - TREE_INT_CST_LOW (n->low), - IDENTIFIER_POINTER ((TREE_CODE (TYPE_NAME (type)) - == IDENTIFIER_NODE) - ? TYPE_NAME (type) - : DECL_NAME (TYPE_NAME (type)))); - } - if (!tree_int_cst_equal (n->low, n->high)) - { - for (chain = TYPE_VALUES (type); - chain && !tree_int_cst_equal (n->high, TREE_VALUE (chain)); - chain = TREE_CHAIN (chain)) - ; - - if (!chain) - { - if (TYPE_NAME (type) == 0) - warning ("case value `%d' not in enumerated type", - TREE_INT_CST_LOW (n->high)); - else - warning ("case value `%d' not in enumerated type `%s'", - TREE_INT_CST_LOW (n->high), - IDENTIFIER_POINTER ((TREE_CODE (TYPE_NAME (type)) - == IDENTIFIER_NODE) - ? TYPE_NAME (type) - : DECL_NAME (TYPE_NAME (type)))); - } - } - } - - /* If all values were found as case labels, make one of them the default - label. Thus, this switch will never fall through. We arbitrarily pick - the last one to make the default since this is likely the most - efficient choice. */ - - if (all_values) - { - for (l = &case_stack->data.case_stmt.case_list; - (*l)->right != 0; - l = &(*l)->right) - ; - - case_stack->data.case_stmt.default_label = (*l)->code_label; - *l = 0; - } -} - -/* Terminate a case (Pascal) or switch (C) statement - in which ORIG_INDEX is the expression to be tested. - Generate the code to test it and jump to the right place. */ - -void -expand_end_case (orig_index) - tree orig_index; -{ - tree minval, maxval, range; - rtx default_label = 0; - register struct case_node *n; - int count; - rtx index; - rtx table_label = gen_label_rtx (); - int ncases; - rtx *labelvec; - register int i; - rtx before_case; - register struct nesting *thiscase = case_stack; - tree index_expr = thiscase->data.case_stmt.index_expr; - int unsignedp = TREE_UNSIGNED (TREE_TYPE (index_expr)); - - do_pending_stack_adjust (); - - /* An ERROR_MARK occurs for various reasons including invalid data type. */ - if (TREE_TYPE (index_expr) != error_mark_node) - { - /* If switch expression was an enumerated type, check that all - enumeration literals are covered by the cases. - No sense trying this if there's a default case, however. */ - - if (!thiscase->data.case_stmt.default_label - && TREE_CODE (TREE_TYPE (orig_index)) == ENUMERAL_TYPE - && TREE_CODE (index_expr) != INTEGER_CST) - check_for_full_enumeration_handling (TREE_TYPE (orig_index)); - - /* If this is the first label, warn if any insns have been emitted. */ - if (thiscase->data.case_stmt.seenlabel == 0) - { - rtx insn; - for (insn = get_last_insn (); - insn != case_stack->data.case_stmt.start; - insn = PREV_INSN (insn)) - if (GET_CODE (insn) != NOTE - && (GET_CODE (insn) != INSN || GET_CODE (PATTERN (insn))!= USE)) - { - warning ("unreachable code at beginning of %s", - case_stack->data.case_stmt.printname); - break; - } - } - - /* If we don't have a default-label, create one here, - after the body of the switch. */ - if (thiscase->data.case_stmt.default_label == 0) - { - thiscase->data.case_stmt.default_label - = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); - expand_label (thiscase->data.case_stmt.default_label); - } - default_label = label_rtx (thiscase->data.case_stmt.default_label); - - before_case = get_last_insn (); - - /* Simplify the case-list before we count it. */ - group_case_nodes (thiscase->data.case_stmt.case_list); - - /* Get upper and lower bounds of case values. - Also convert all the case values to the index expr's data type. */ - - count = 0; - for (n = thiscase->data.case_stmt.case_list; n; n = n->right) - { - /* Check low and high label values are integers. */ - if (TREE_CODE (n->low) != INTEGER_CST) - abort (); - if (TREE_CODE (n->high) != INTEGER_CST) - abort (); - - n->low = convert (TREE_TYPE (index_expr), n->low); - n->high = convert (TREE_TYPE (index_expr), n->high); - - /* Count the elements and track the largest and smallest - of them (treating them as signed even if they are not). */ - if (count++ == 0) - { - minval = n->low; - maxval = n->high; - } - else - { - if (INT_CST_LT (n->low, minval)) - minval = n->low; - if (INT_CST_LT (maxval, n->high)) - maxval = n->high; - } - /* A range counts double, since it requires two compares. */ - if (! tree_int_cst_equal (n->low, n->high)) - count++; - } - - /* Compute span of values. */ - if (count != 0) - range = fold (build (MINUS_EXPR, TREE_TYPE (index_expr), - maxval, minval)); - - if (count == 0 || TREE_CODE (TREE_TYPE (index_expr)) == ERROR_MARK) - { - expand_expr (index_expr, const0_rtx, VOIDmode, 0); - emit_queue (); - emit_jump (default_label); - } - /* If range of values is much bigger than number of values, - make a sequence of conditional branches instead of a dispatch. - If the switch-index is a constant, do it this way - because we can optimize it. */ - -#ifndef CASE_VALUES_THRESHOLD -#ifdef HAVE_casesi -#define CASE_VALUES_THRESHOLD (HAVE_casesi ? 4 : 5) -#else - /* If machine does not have a case insn that compares the - bounds, this means extra overhead for dispatch tables - which raises the threshold for using them. */ -#define CASE_VALUES_THRESHOLD 5 -#endif /* HAVE_casesi */ -#endif /* CASE_VALUES_THRESHOLD */ - - else if (TREE_INT_CST_HIGH (range) != 0 - || count < CASE_VALUES_THRESHOLD - || ((unsigned HOST_WIDE_INT) (TREE_INT_CST_LOW (range)) - > 10 * count) - || TREE_CODE (index_expr) == INTEGER_CST - /* These will reduce to a constant. */ - || (TREE_CODE (index_expr) == CALL_EXPR - && TREE_CODE (TREE_OPERAND (index_expr, 0)) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (TREE_OPERAND (index_expr, 0), 0)) == FUNCTION_DECL - && DECL_FUNCTION_CODE (TREE_OPERAND (TREE_OPERAND (index_expr, 0), 0)) == BUILT_IN_CLASSIFY_TYPE) - || (TREE_CODE (index_expr) == COMPOUND_EXPR - && TREE_CODE (TREE_OPERAND (index_expr, 1)) == INTEGER_CST)) - { - index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0); - - /* If the index is a short or char that we do not have - an insn to handle comparisons directly, convert it to - a full integer now, rather than letting each comparison - generate the conversion. */ - - if (GET_MODE_CLASS (GET_MODE (index)) == MODE_INT - && (cmp_optab->handlers[(int) GET_MODE(index)].insn_code - == CODE_FOR_nothing)) - { - enum machine_mode wider_mode; - for (wider_mode = GET_MODE (index); wider_mode != VOIDmode; - wider_mode = GET_MODE_WIDER_MODE (wider_mode)) - if (cmp_optab->handlers[(int) wider_mode].insn_code - != CODE_FOR_nothing) - { - index = convert_to_mode (wider_mode, index, unsignedp); - break; - } - } - - emit_queue (); - do_pending_stack_adjust (); - - index = protect_from_queue (index, 0); - if (GET_CODE (index) == MEM) - index = copy_to_reg (index); - if (GET_CODE (index) == CONST_INT - || TREE_CODE (index_expr) == INTEGER_CST) - { - /* Make a tree node with the proper constant value - if we don't already have one. */ - if (TREE_CODE (index_expr) != INTEGER_CST) - { - index_expr - = build_int_2 (INTVAL (index), - !unsignedp && INTVAL (index) >= 0 ? 0 : -1); - index_expr = convert (TREE_TYPE (index_expr), index_expr); - } - - /* For constant index expressions we need only - issue a unconditional branch to the appropriate - target code. The job of removing any unreachable - code is left to the optimisation phase if the - "-O" option is specified. */ - for (n = thiscase->data.case_stmt.case_list; - n; - n = n->right) - { - if (! tree_int_cst_lt (index_expr, n->low) - && ! tree_int_cst_lt (n->high, index_expr)) - break; - } - if (n) - emit_jump (label_rtx (n->code_label)); - else - emit_jump (default_label); - } - else - { - /* If the index expression is not constant we generate - a binary decision tree to select the appropriate - target code. This is done as follows: - - The list of cases is rearranged into a binary tree, - nearly optimal assuming equal probability for each case. - - The tree is transformed into RTL, eliminating - redundant test conditions at the same time. - - If program flow could reach the end of the - decision tree an unconditional jump to the - default code is emitted. */ - - use_cost_table - = (TREE_CODE (TREE_TYPE (orig_index)) != ENUMERAL_TYPE - && estimate_case_costs (thiscase->data.case_stmt.case_list)); - balance_case_nodes (&thiscase->data.case_stmt.case_list, - NULL_PTR); - emit_case_nodes (index, thiscase->data.case_stmt.case_list, - default_label, TREE_TYPE (index_expr)); - emit_jump_if_reachable (default_label); - } - } - else - { - int win = 0; -#ifdef HAVE_casesi - if (HAVE_casesi) - { - enum machine_mode index_mode = SImode; - int index_bits = GET_MODE_BITSIZE (index_mode); - - /* Convert the index to SImode. */ - if (GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE (index_expr))) - > GET_MODE_BITSIZE (index_mode)) - { - enum machine_mode omode = TYPE_MODE (TREE_TYPE (index_expr)); - rtx rangertx = expand_expr (range, NULL_RTX, VOIDmode, 0); - - /* We must handle the endpoints in the original mode. */ - index_expr = build (MINUS_EXPR, TREE_TYPE (index_expr), - index_expr, minval); - minval = integer_zero_node; - index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0); - emit_cmp_insn (rangertx, index, LTU, NULL_RTX, omode, 0, 0); - emit_jump_insn (gen_bltu (default_label)); - /* Now we can safely truncate. */ - index = convert_to_mode (index_mode, index, 0); - } - else - { - if (TYPE_MODE (TREE_TYPE (index_expr)) != index_mode) - index_expr = convert (type_for_size (index_bits, 0), - index_expr); - index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0); - } - emit_queue (); - index = protect_from_queue (index, 0); - do_pending_stack_adjust (); - - emit_jump_insn (gen_casesi (index, expand_expr (minval, NULL_RTX, - VOIDmode, 0), - expand_expr (range, NULL_RTX, - VOIDmode, 0), - table_label, default_label)); - win = 1; - } -#endif -#ifdef HAVE_tablejump - if (! win && HAVE_tablejump) - { - index_expr = convert (thiscase->data.case_stmt.nominal_type, - fold (build (MINUS_EXPR, - TREE_TYPE (index_expr), - index_expr, minval))); - index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0); - emit_queue (); - index = protect_from_queue (index, 0); - do_pending_stack_adjust (); - - do_tablejump (index, TYPE_MODE (TREE_TYPE (index_expr)), - expand_expr (range, NULL_RTX, VOIDmode, 0), - table_label, default_label); - win = 1; - } -#endif - if (! win) - abort (); - - /* Get table of labels to jump to, in order of case index. */ - - ncases = TREE_INT_CST_LOW (range) + 1; - labelvec = (rtx *) alloca (ncases * sizeof (rtx)); - bzero (labelvec, ncases * sizeof (rtx)); - - for (n = thiscase->data.case_stmt.case_list; n; n = n->right) - { - register HOST_WIDE_INT i - = TREE_INT_CST_LOW (n->low) - TREE_INT_CST_LOW (minval); - - while (1) - { - labelvec[i] - = gen_rtx (LABEL_REF, Pmode, label_rtx (n->code_label)); - if (i + TREE_INT_CST_LOW (minval) - == TREE_INT_CST_LOW (n->high)) - break; - i++; - } - } - - /* Fill in the gaps with the default. */ - for (i = 0; i < ncases; i++) - if (labelvec[i] == 0) - labelvec[i] = gen_rtx (LABEL_REF, Pmode, default_label); - - /* Output the table */ - emit_label (table_label); - - /* This would be a lot nicer if CASE_VECTOR_PC_RELATIVE - were an expression, instead of an #ifdef/#ifndef. */ - if ( -#ifdef CASE_VECTOR_PC_RELATIVE - 1 || -#endif - flag_pic) - emit_jump_insn (gen_rtx (ADDR_DIFF_VEC, CASE_VECTOR_MODE, - gen_rtx (LABEL_REF, Pmode, table_label), - gen_rtvec_v (ncases, labelvec))); - else - emit_jump_insn (gen_rtx (ADDR_VEC, CASE_VECTOR_MODE, - gen_rtvec_v (ncases, labelvec))); - - /* If the case insn drops through the table, - after the table we must jump to the default-label. - Otherwise record no drop-through after the table. */ -#ifdef CASE_DROPS_THROUGH - emit_jump (default_label); -#else - emit_barrier (); -#endif - } - - before_case = squeeze_notes (NEXT_INSN (before_case), get_last_insn ()); - reorder_insns (before_case, get_last_insn (), - thiscase->data.case_stmt.start); - } - if (thiscase->exit_label) - emit_label (thiscase->exit_label); - - POPSTACK (case_stack); - - free_temp_slots (); -} - -/* Generate code to jump to LABEL if OP1 and OP2 are equal. */ - -static void -do_jump_if_equal (op1, op2, label, unsignedp) - rtx op1, op2, label; - int unsignedp; -{ - if (GET_CODE (op1) == CONST_INT - && GET_CODE (op2) == CONST_INT) - { - if (INTVAL (op1) == INTVAL (op2)) - emit_jump (label); - } - else - { - enum machine_mode mode = GET_MODE (op1); - if (mode == VOIDmode) - mode = GET_MODE (op2); - emit_cmp_insn (op1, op2, EQ, NULL_RTX, mode, unsignedp, 0); - emit_jump_insn (gen_beq (label)); - } -} - -/* Not all case values are encountered equally. This function - uses a heuristic to weight case labels, in cases where that - looks like a reasonable thing to do. - - Right now, all we try to guess is text, and we establish the - following weights: - - chars above space: 16 - digits: 16 - default: 12 - space, punct: 8 - tab: 4 - newline: 2 - other "\" chars: 1 - remaining chars: 0 - - If we find any cases in the switch that are not either -1 or in the range - of valid ASCII characters, or are control characters other than those - commonly used with "\", don't treat this switch scanning text. - - Return 1 if these nodes are suitable for cost estimation, otherwise - return 0. */ - -static int -estimate_case_costs (node) - case_node_ptr node; -{ - tree min_ascii = build_int_2 (-1, -1); - tree max_ascii = convert (TREE_TYPE (node->high), build_int_2 (127, 0)); - case_node_ptr n; - int i; - - /* If we haven't already made the cost table, make it now. Note that the - lower bound of the table is -1, not zero. */ - - if (cost_table == NULL) - { - cost_table = ((short *) xmalloc (129 * sizeof (short))) + 1; - bzero (cost_table - 1, 129 * sizeof (short)); - - for (i = 0; i < 128; i++) - { - if (isalnum (i)) - cost_table[i] = 16; - else if (ispunct (i)) - cost_table[i] = 8; - else if (iscntrl (i)) - cost_table[i] = -1; - } - - cost_table[' '] = 8; - cost_table['\t'] = 4; - cost_table['\0'] = 4; - cost_table['\n'] = 2; - cost_table['\f'] = 1; - cost_table['\v'] = 1; - cost_table['\b'] = 1; - } - - /* See if all the case expressions look like text. It is text if the - constant is >= -1 and the highest constant is <= 127. Do all comparisons - as signed arithmetic since we don't want to ever access cost_table with a - value less than -1. Also check that none of the constants in a range - are strange control characters. */ - - for (n = node; n; n = n->right) - { - if ((INT_CST_LT (n->low, min_ascii)) || INT_CST_LT (max_ascii, n->high)) - return 0; - - for (i = TREE_INT_CST_LOW (n->low); i <= TREE_INT_CST_LOW (n->high); i++) - if (cost_table[i] < 0) - return 0; - } - - /* All interesting values are within the range of interesting - ASCII characters. */ - return 1; -} - -/* Scan an ordered list of case nodes - combining those with consecutive values or ranges. - - Eg. three separate entries 1: 2: 3: become one entry 1..3: */ - -static void -group_case_nodes (head) - case_node_ptr head; -{ - case_node_ptr node = head; - - while (node) - { - rtx lb = next_real_insn (label_rtx (node->code_label)); - case_node_ptr np = node; - - /* Try to group the successors of NODE with NODE. */ - while (((np = np->right) != 0) - /* Do they jump to the same place? */ - && next_real_insn (label_rtx (np->code_label)) == lb - /* Are their ranges consecutive? */ - && tree_int_cst_equal (np->low, - fold (build (PLUS_EXPR, - TREE_TYPE (node->high), - node->high, - integer_one_node))) - /* An overflow is not consecutive. */ - && tree_int_cst_lt (node->high, - fold (build (PLUS_EXPR, - TREE_TYPE (node->high), - node->high, - integer_one_node)))) - { - node->high = np->high; - } - /* NP is the first node after NODE which can't be grouped with it. - Delete the nodes in between, and move on to that node. */ - node->right = np; - node = np; - } -} - -/* Take an ordered list of case nodes - and transform them into a near optimal binary tree, - on the assumption that any target code selection value is as - likely as any other. - - The transformation is performed by splitting the ordered - list into two equal sections plus a pivot. The parts are - then attached to the pivot as left and right branches. Each - branch is is then transformed recursively. */ - -static void -balance_case_nodes (head, parent) - case_node_ptr *head; - case_node_ptr parent; -{ - register case_node_ptr np; - - np = *head; - if (np) - { - int cost = 0; - int i = 0; - int ranges = 0; - register case_node_ptr *npp; - case_node_ptr left; - - /* Count the number of entries on branch. Also count the ranges. */ - - while (np) - { - if (!tree_int_cst_equal (np->low, np->high)) - { - ranges++; - if (use_cost_table) - cost += cost_table[TREE_INT_CST_LOW (np->high)]; - } - - if (use_cost_table) - cost += cost_table[TREE_INT_CST_LOW (np->low)]; - - i++; - np = np->right; - } - - if (i > 2) - { - /* Split this list if it is long enough for that to help. */ - npp = head; - left = *npp; - if (use_cost_table) - { - /* Find the place in the list that bisects the list's total cost, - Here I gets half the total cost. */ - int n_moved = 0; - i = (cost + 1) / 2; - while (1) - { - /* Skip nodes while their cost does not reach that amount. */ - if (!tree_int_cst_equal ((*npp)->low, (*npp)->high)) - i -= cost_table[TREE_INT_CST_LOW ((*npp)->high)]; - i -= cost_table[TREE_INT_CST_LOW ((*npp)->low)]; - if (i <= 0) - break; - npp = &(*npp)->right; - n_moved += 1; - } - if (n_moved == 0) - { - /* Leave this branch lopsided, but optimize left-hand - side and fill in `parent' fields for right-hand side. */ - np = *head; - np->parent = parent; - balance_case_nodes (&np->left, np); - for (; np->right; np = np->right) - np->right->parent = np; - return; - } - } - /* If there are just three nodes, split at the middle one. */ - else if (i == 3) - npp = &(*npp)->right; - else - { - /* Find the place in the list that bisects the list's total cost, - where ranges count as 2. - Here I gets half the total cost. */ - i = (i + ranges + 1) / 2; - while (1) - { - /* Skip nodes while their cost does not reach that amount. */ - if (!tree_int_cst_equal ((*npp)->low, (*npp)->high)) - i--; - i--; - if (i <= 0) - break; - npp = &(*npp)->right; - } - } - *head = np = *npp; - *npp = 0; - np->parent = parent; - np->left = left; - - /* Optimize each of the two split parts. */ - balance_case_nodes (&np->left, np); - balance_case_nodes (&np->right, np); - } - else - { - /* Else leave this branch as one level, - but fill in `parent' fields. */ - np = *head; - np->parent = parent; - for (; np->right; np = np->right) - np->right->parent = np; - } - } -} - -/* Search the parent sections of the case node tree - to see if a test for the lower bound of NODE would be redundant. - INDEX_TYPE is the type of the index expression. - - The instructions to generate the case decision tree are - output in the same order as nodes are processed so it is - known that if a parent node checks the range of the current - node minus one that the current node is bounded at its lower - span. Thus the test would be redundant. */ - -static int -node_has_low_bound (node, index_type) - case_node_ptr node; - tree index_type; -{ - tree low_minus_one; - case_node_ptr pnode; - - /* If the lower bound of this node is the lowest value in the index type, - we need not test it. */ - - if (tree_int_cst_equal (node->low, TYPE_MIN_VALUE (index_type))) - return 1; - - /* If this node has a left branch, the value at the left must be less - than that at this node, so it cannot be bounded at the bottom and - we need not bother testing any further. */ - - if (node->left) - return 0; - - low_minus_one = fold (build (MINUS_EXPR, TREE_TYPE (node->low), - node->low, integer_one_node)); - - /* If the subtraction above overflowed, we can't verify anything. - Otherwise, look for a parent that tests our value - 1. */ - - if (! tree_int_cst_lt (low_minus_one, node->low)) - return 0; - - for (pnode = node->parent; pnode; pnode = pnode->parent) - if (tree_int_cst_equal (low_minus_one, pnode->high)) - return 1; - - return 0; -} - -/* Search the parent sections of the case node tree - to see if a test for the upper bound of NODE would be redundant. - INDEX_TYPE is the type of the index expression. - - The instructions to generate the case decision tree are - output in the same order as nodes are processed so it is - known that if a parent node checks the range of the current - node plus one that the current node is bounded at its upper - span. Thus the test would be redundant. */ - -static int -node_has_high_bound (node, index_type) - case_node_ptr node; - tree index_type; -{ - tree high_plus_one; - case_node_ptr pnode; - - /* If the upper bound of this node is the highest value in the type - of the index expression, we need not test against it. */ - - if (tree_int_cst_equal (node->high, TYPE_MAX_VALUE (index_type))) - return 1; - - /* If this node has a right branch, the value at the right must be greater - than that at this node, so it cannot be bounded at the top and - we need not bother testing any further. */ - - if (node->right) - return 0; - - high_plus_one = fold (build (PLUS_EXPR, TREE_TYPE (node->high), - node->high, integer_one_node)); - - /* If the addition above overflowed, we can't verify anything. - Otherwise, look for a parent that tests our value + 1. */ - - if (! tree_int_cst_lt (node->high, high_plus_one)) - return 0; - - for (pnode = node->parent; pnode; pnode = pnode->parent) - if (tree_int_cst_equal (high_plus_one, pnode->low)) - return 1; - - return 0; -} - -/* Search the parent sections of the - case node tree to see if both tests for the upper and lower - bounds of NODE would be redundant. */ - -static int -node_is_bounded (node, index_type) - case_node_ptr node; - tree index_type; -{ - return (node_has_low_bound (node, index_type) - && node_has_high_bound (node, index_type)); -} - -/* Emit an unconditional jump to LABEL unless it would be dead code. */ - -static void -emit_jump_if_reachable (label) - rtx label; -{ - if (GET_CODE (get_last_insn ()) != BARRIER) - emit_jump (label); -} - -/* Emit step-by-step code to select a case for the value of INDEX. - The thus generated decision tree follows the form of the - case-node binary tree NODE, whose nodes represent test conditions. - INDEX_TYPE is the type of the index of the switch. - - Care is taken to prune redundant tests from the decision tree - by detecting any boundary conditions already checked by - emitted rtx. (See node_has_high_bound, node_has_low_bound - and node_is_bounded, above.) - - Where the test conditions can be shown to be redundant we emit - an unconditional jump to the target code. As a further - optimization, the subordinates of a tree node are examined to - check for bounded nodes. In this case conditional and/or - unconditional jumps as a result of the boundary check for the - current node are arranged to target the subordinates associated - code for out of bound conditions on the current node node. - - We can assume that when control reaches the code generated here, - the index value has already been compared with the parents - of this node, and determined to be on the same side of each parent - as this node is. Thus, if this node tests for the value 51, - and a parent tested for 52, we don't need to consider - the possibility of a value greater than 51. If another parent - tests for the value 50, then this node need not test anything. */ - -static void -emit_case_nodes (index, node, default_label, index_type) - rtx index; - case_node_ptr node; - rtx default_label; - tree index_type; -{ - /* If INDEX has an unsigned type, we must make unsigned branches. */ - int unsignedp = TREE_UNSIGNED (index_type); - typedef rtx rtx_function (); - rtx_function *gen_bgt_pat = unsignedp ? gen_bgtu : gen_bgt; - rtx_function *gen_bge_pat = unsignedp ? gen_bgeu : gen_bge; - rtx_function *gen_blt_pat = unsignedp ? gen_bltu : gen_blt; - rtx_function *gen_ble_pat = unsignedp ? gen_bleu : gen_ble; - enum machine_mode mode = GET_MODE (index); - - /* See if our parents have already tested everything for us. - If they have, emit an unconditional jump for this node. */ - if (node_is_bounded (node, index_type)) - emit_jump (label_rtx (node->code_label)); - - else if (tree_int_cst_equal (node->low, node->high)) - { - /* Node is single valued. First see if the index expression matches - this node and then check our children, if any. */ - - do_jump_if_equal (index, expand_expr (node->low, NULL_RTX, VOIDmode, 0), - label_rtx (node->code_label), unsignedp); - - if (node->right != 0 && node->left != 0) - { - /* This node has children on both sides. - Dispatch to one side or the other - by comparing the index value with this node's value. - If one subtree is bounded, check that one first, - so we can avoid real branches in the tree. */ - - if (node_is_bounded (node->right, index_type)) - { - emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, - VOIDmode, 0), - GT, NULL_RTX, mode, unsignedp, 0); - - emit_jump_insn ((*gen_bgt_pat) (label_rtx (node->right->code_label))); - emit_case_nodes (index, node->left, default_label, index_type); - } - - else if (node_is_bounded (node->left, index_type)) - { - emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, - VOIDmode, 0), - LT, NULL_RTX, mode, unsignedp, 0); - emit_jump_insn ((*gen_blt_pat) (label_rtx (node->left->code_label))); - emit_case_nodes (index, node->right, default_label, index_type); - } - - else - { - /* Neither node is bounded. First distinguish the two sides; - then emit the code for one side at a time. */ - - tree test_label - = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); - - /* See if the value is on the right. */ - emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, - VOIDmode, 0), - GT, NULL_RTX, mode, unsignedp, 0); - emit_jump_insn ((*gen_bgt_pat) (label_rtx (test_label))); - - /* Value must be on the left. - Handle the left-hand subtree. */ - emit_case_nodes (index, node->left, default_label, index_type); - /* If left-hand subtree does nothing, - go to default. */ - emit_jump_if_reachable (default_label); - - /* Code branches here for the right-hand subtree. */ - expand_label (test_label); - emit_case_nodes (index, node->right, default_label, index_type); - } - } - - else if (node->right != 0 && node->left == 0) - { - /* Here we have a right child but no left so we issue conditional - branch to default and process the right child. - - Omit the conditional branch to default if we it avoid only one - right child; it costs too much space to save so little time. */ - - if (node->right->right || node->right->left - || !tree_int_cst_equal (node->right->low, node->right->high)) - { - if (!node_has_low_bound (node, index_type)) - { - emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, - VOIDmode, 0), - LT, NULL_RTX, mode, unsignedp, 0); - emit_jump_insn ((*gen_blt_pat) (default_label)); - } - - emit_case_nodes (index, node->right, default_label, index_type); - } - else - /* We cannot process node->right normally - since we haven't ruled out the numbers less than - this node's value. So handle node->right explicitly. */ - do_jump_if_equal (index, - expand_expr (node->right->low, NULL_RTX, - VOIDmode, 0), - label_rtx (node->right->code_label), unsignedp); - } - - else if (node->right == 0 && node->left != 0) - { - /* Just one subtree, on the left. */ - -#if 0 /* The following code and comment were formerly part - of the condition here, but they didn't work - and I don't understand what the idea was. -- rms. */ - /* If our "most probable entry" is less probable - than the default label, emit a jump to - the default label using condition codes - already lying around. With no right branch, - a branch-greater-than will get us to the default - label correctly. */ - if (use_cost_table - && cost_table[TREE_INT_CST_LOW (node->high)] < 12) - ; -#endif /* 0 */ - if (node->left->left || node->left->right - || !tree_int_cst_equal (node->left->low, node->left->high)) - { - if (!node_has_high_bound (node, index_type)) - { - emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, - VOIDmode, 0), - GT, NULL_RTX, mode, unsignedp, 0); - emit_jump_insn ((*gen_bgt_pat) (default_label)); - } - - emit_case_nodes (index, node->left, default_label, index_type); - } - else - /* We cannot process node->left normally - since we haven't ruled out the numbers less than - this node's value. So handle node->left explicitly. */ - do_jump_if_equal (index, - expand_expr (node->left->low, NULL_RTX, - VOIDmode, 0), - label_rtx (node->left->code_label), unsignedp); - } - } - else - { - /* Node is a range. These cases are very similar to those for a single - value, except that we do not start by testing whether this node - is the one to branch to. */ - - if (node->right != 0 && node->left != 0) - { - /* Node has subtrees on both sides. - If the right-hand subtree is bounded, - test for it first, since we can go straight there. - Otherwise, we need to make a branch in the control structure, - then handle the two subtrees. */ - tree test_label = 0; - - emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, - VOIDmode, 0), - GT, NULL_RTX, mode, unsignedp, 0); - - if (node_is_bounded (node->right, index_type)) - /* Right hand node is fully bounded so we can eliminate any - testing and branch directly to the target code. */ - emit_jump_insn ((*gen_bgt_pat) (label_rtx (node->right->code_label))); - else - { - /* Right hand node requires testing. - Branch to a label where we will handle it later. */ - - test_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); - emit_jump_insn ((*gen_bgt_pat) (label_rtx (test_label))); - } - - /* Value belongs to this node or to the left-hand subtree. */ - - emit_cmp_insn (index, expand_expr (node->low, NULL_RTX, VOIDmode, 0), - GE, NULL_RTX, mode, unsignedp, 0); - emit_jump_insn ((*gen_bge_pat) (label_rtx (node->code_label))); - - /* Handle the left-hand subtree. */ - emit_case_nodes (index, node->left, default_label, index_type); - - /* If right node had to be handled later, do that now. */ - - if (test_label) - { - /* If the left-hand subtree fell through, - don't let it fall into the right-hand subtree. */ - emit_jump_if_reachable (default_label); - - expand_label (test_label); - emit_case_nodes (index, node->right, default_label, index_type); - } - } - - else if (node->right != 0 && node->left == 0) - { - /* Deal with values to the left of this node, - if they are possible. */ - if (!node_has_low_bound (node, index_type)) - { - emit_cmp_insn (index, expand_expr (node->low, NULL_RTX, - VOIDmode, 0), - LT, NULL_RTX, mode, unsignedp, 0); - emit_jump_insn ((*gen_blt_pat) (default_label)); - } - - /* Value belongs to this node or to the right-hand subtree. */ - - emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, - VOIDmode, 0), - LE, NULL_RTX, mode, unsignedp, 0); - emit_jump_insn ((*gen_ble_pat) (label_rtx (node->code_label))); - - emit_case_nodes (index, node->right, default_label, index_type); - } - - else if (node->right == 0 && node->left != 0) - { - /* Deal with values to the right of this node, - if they are possible. */ - if (!node_has_high_bound (node, index_type)) - { - emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, - VOIDmode, 0), - GT, NULL_RTX, mode, unsignedp, 0); - emit_jump_insn ((*gen_bgt_pat) (default_label)); - } - - /* Value belongs to this node or to the left-hand subtree. */ - - emit_cmp_insn (index, expand_expr (node->low, NULL_RTX, VOIDmode, 0), - GE, NULL_RTX, mode, unsignedp, 0); - emit_jump_insn ((*gen_bge_pat) (label_rtx (node->code_label))); - - emit_case_nodes (index, node->left, default_label, index_type); - } - - else - { - /* Node has no children so we check low and high bounds to remove - redundant tests. Only one of the bounds can exist, - since otherwise this node is bounded--a case tested already. */ - - if (!node_has_high_bound (node, index_type)) - { - emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, - VOIDmode, 0), - GT, NULL_RTX, mode, unsignedp, 0); - emit_jump_insn ((*gen_bgt_pat) (default_label)); - } - - if (!node_has_low_bound (node, index_type)) - { - emit_cmp_insn (index, expand_expr (node->low, NULL_RTX, - VOIDmode, 0), - LT, NULL_RTX, mode, unsignedp, 0); - emit_jump_insn ((*gen_blt_pat) (default_label)); - } - - emit_jump (label_rtx (node->code_label)); - } - } -} - -/* These routines are used by the loop unrolling code. They copy BLOCK trees - so that the debugging info will be correct for the unrolled loop. */ - -/* Indexed by block number, contains a pointer to the N'th block node. */ - -static tree *block_vector; - -void -find_loop_tree_blocks () -{ - tree block = DECL_INITIAL (current_function_decl); - - /* There first block is for the function body, and does not have - corresponding block notes. Don't include it in the block vector. */ - block = BLOCK_SUBBLOCKS (block); - - block_vector = identify_blocks (block, get_insns ()); -} - -void -unroll_block_trees () -{ - tree block = DECL_INITIAL (current_function_decl); - - reorder_blocks (block_vector, block, get_insns ()); -} - diff --git a/gnu/usr.bin/cc/common/toplev.c b/gnu/usr.bin/cc/common/toplev.c deleted file mode 100644 index a7db37c113..0000000000 --- a/gnu/usr.bin/cc/common/toplev.c +++ /dev/null @@ -1,3505 +0,0 @@ -/* Top level of GNU C compiler - Copyright (C) 1987, 1988, 1989, 1992 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This is the top level of cc1/c++. - It parses command args, opens files, invokes the various passes - in the proper order, and counts the time used by each. - Error messages and low-level interface to malloc also handled here. */ - -#include "config.h" -#include -#include -#include -#include - -#include - -#ifdef USG -#undef FLOAT -#include -/* This is for hpux. It is a real screw. They should change hpux. */ -#undef FLOAT -#include -#include /* Correct for hpux at least. Is it good on other USG? */ -#undef FFS /* Some systems define this in param.h. */ -#else -#ifndef VMS -#include -#include -#endif -#endif - -#include "input.h" -#include "tree.h" -/* #include "c-tree.h" */ -#include "rtl.h" -#include "flags.h" -#include "insn-attr.h" -#include "defaults.h" - -#ifdef XCOFF_DEBUGGING_INFO -#include "xcoffout.h" -#endif - -#ifdef VMS -/* The extra parameters substantially improve the I/O performance. */ -static FILE * -VMS_fopen (fname, type) - char * fname; - char * type; -{ - if (strcmp (type, "w") == 0) - return fopen (fname, type, "mbc=16", "deq=64", "fop=tef", "shr=nil"); - return fopen (fname, type, "mbc=16"); -} -#define fopen VMS_fopen -#endif - -#ifndef DEFAULT_GDB_EXTENSIONS -#define DEFAULT_GDB_EXTENSIONS 1 -#endif - -extern int rtx_equal_function_value_matters; - -#if ! (defined (VMS) || defined (OS2)) -extern char **environ; -#endif -extern char *version_string, *language_string; - -extern void init_lex (); -extern void init_decl_processing (); -extern void init_obstacks (); -extern void init_tree_codes (); -extern void init_rtl (); -extern void init_optabs (); -extern void init_stmt (); -extern void init_reg_sets (); -extern void dump_flow_info (); -extern void dump_sched_info (); -extern void dump_local_alloc (); - -void rest_of_decl_compilation (); -void error (); -void error_with_file_and_line (); -void fancy_abort (); -#ifndef abort -void abort (); -#endif -void set_target_switch (); -static void print_switch_values (); -static char *decl_name (); - -/* Name of program invoked, sans directories. */ - -char *progname; - -/* Copy of arguments to main. */ -int save_argc; -char **save_argv; - -/* Name of current original source file (what was input to cpp). - This comes from each #-command in the actual input. */ - -char *input_filename; - -/* Name of top-level original source file (what was input to cpp). - This comes from the #-command at the beginning of the actual input. - If there isn't any there, then this is the cc1 input file name. */ - -char *main_input_filename; - -/* Stream for reading from the input file. */ - -FILE *finput; - -/* Current line number in real source file. */ - -int lineno; - -/* Stack of currently pending input files. */ - -struct file_stack *input_file_stack; - -/* Incremented on each change to input_file_stack. */ -int input_file_stack_tick; - -/* FUNCTION_DECL for function now being parsed or compiled. */ - -extern tree current_function_decl; - -/* Name to use as base of names for dump output files. */ - -char *dump_base_name; - -/* Bit flags that specify the machine subtype we are compiling for. - Bits are tested using macros TARGET_... defined in the tm.h file - and set by `-m...' switches. Must be defined in rtlanal.c. */ - -extern int target_flags; - -/* Flags saying which kinds of debugging dump have been requested. */ - -int rtl_dump = 0; -int rtl_dump_and_exit = 0; -int jump_opt_dump = 0; -int cse_dump = 0; -int loop_dump = 0; -int cse2_dump = 0; -int flow_dump = 0; -int combine_dump = 0; -int sched_dump = 0; -int local_reg_dump = 0; -int global_reg_dump = 0; -int sched2_dump = 0; -int jump2_opt_dump = 0; -int dbr_sched_dump = 0; -int flag_print_asm_name = 0; -int stack_reg_dump = 0; - -/* Name for output file of assembly code, specified with -o. */ - -char *asm_file_name; - -/* Value of the -G xx switch, and whether it was passed or not. */ -int g_switch_value; -int g_switch_set; - -/* Type(s) of debugging information we are producing (if any). - See flags.h for the definitions of the different possible - types of debugging information. */ -enum debug_info_type write_symbols = NO_DEBUG; - -/* Level of debugging information we are producing. See flags.h - for the definitions of the different possible levels. */ -enum debug_info_level debug_info_level = DINFO_LEVEL_NONE; - -/* Nonzero means use GNU-only extensions in the generated symbolic - debugging information. */ -/* Currently, this only has an effect when write_symbols is set to - DBX_DEBUG, XCOFF_DEBUG, or DWARF_DEBUG. */ -int use_gnu_debug_info_extensions = 0; - -/* Nonzero means do optimizations. -O. - Particular numeric values stand for particular amounts of optimization; - thus, -O2 stores 2 here. However, the optimizations beyond the basic - ones are not controlled directly by this variable. Instead, they are - controlled by individual `flag_...' variables that are defaulted - based on this variable. */ - -int optimize = 0; - -/* Number of error messages and warning messages so far. */ - -int errorcount = 0; -int warningcount = 0; -int sorrycount = 0; - -/* Pointer to function to compute the name to use to print a declaration. */ - -char *(*decl_printable_name) (); - -/* Pointer to function to compute rtl for a language-specific tree code. */ - -struct rtx_def *(*lang_expand_expr) (); - -/* Pointer to function to finish handling an incomplete decl at the - end of compilation. */ - -void (*incomplete_decl_finalize_hook) () = 0; - -/* Nonzero if generating code to do profiling. */ - -int profile_flag = 0; - -/* Nonzero if generating code to do profiling on a line-by-line basis. */ - -int profile_block_flag; - -/* Nonzero for -pedantic switch: warn about anything - that standard spec forbids. */ - -int pedantic = 0; - -/* Temporarily suppress certain warnings. - This is set while reading code from a system header file. */ - -int in_system_header = 0; - -/* Nonzero means do stupid register allocation. - Currently, this is 1 if `optimize' is 0. */ - -int obey_regdecls = 0; - -/* Don't print functions as they are compiled and don't print - times taken by the various passes. -quiet. */ - -int quiet_flag = 0; - -/* -f flags. */ - -/* Nonzero means `char' should be signed. */ - -int flag_signed_char; - -/* Nonzero means give an enum type only as many bytes as it needs. */ - -int flag_short_enums; - -/* Nonzero for -fcaller-saves: allocate values in regs that need to - be saved across function calls, if that produces overall better code. - Optional now, so people can test it. */ - -#ifdef DEFAULT_CALLER_SAVES -int flag_caller_saves = 1; -#else -int flag_caller_saves = 0; -#endif - -/* Nonzero if structures and unions should be returned in memory. - - This should only be defined if compatibility with another compiler or - with an ABI is needed, because it results in slower code. */ - -#ifndef DEFAULT_PCC_STRUCT_RETURN -#define DEFAULT_PCC_STRUCT_RETURN 1 -#endif - -/* Nonzero for -fpcc-struct-return: return values the same way PCC does. */ - -int flag_pcc_struct_return = DEFAULT_PCC_STRUCT_RETURN; - -/* Nonzero for -fforce-mem: load memory value into a register - before arithmetic on it. This makes better cse but slower compilation. */ - -int flag_force_mem = 0; - -/* Nonzero for -fforce-addr: load memory address into a register before - reference to memory. This makes better cse but slower compilation. */ - -int flag_force_addr = 0; - -/* Nonzero for -fdefer-pop: don't pop args after each function call; - instead save them up to pop many calls' args with one insns. */ - -int flag_defer_pop = 0; - -/* Nonzero for -ffloat-store: don't allocate floats and doubles - in extended-precision registers. */ - -int flag_float_store = 0; - -/* Nonzero for -fcse-follow-jumps: - have cse follow jumps to do a more extensive job. */ - -int flag_cse_follow_jumps; - -/* Nonzero for -fcse-skip-blocks: - have cse follow a branch around a block. */ -int flag_cse_skip_blocks; - -/* Nonzero for -fexpensive-optimizations: - perform miscellaneous relatively-expensive optimizations. */ -int flag_expensive_optimizations; - -/* Nonzero for -fthread-jumps: - have jump optimize output of loop. */ - -int flag_thread_jumps; - -/* Nonzero enables strength-reduction in loop.c. */ - -int flag_strength_reduce = 0; - -/* Nonzero enables loop unrolling in unroll.c. Only loops for which the - number of iterations can be calculated at compile-time (UNROLL_COMPLETELY, - UNROLL_MODULO) or at run-time (preconditioned to be UNROLL_MODULO) are - unrolled. */ - -int flag_unroll_loops; - -/* Nonzero enables loop unrolling in unroll.c. All loops are unrolled. - This is generally not a win. */ - -int flag_unroll_all_loops; - -/* Nonzero for -fwritable-strings: - store string constants in data segment and don't uniquize them. */ - -int flag_writable_strings = 0; - -/* Nonzero means don't put addresses of constant functions in registers. - Used for compiling the Unix kernel, where strange substitutions are - done on the assembly output. */ - -int flag_no_function_cse = 0; - -/* Nonzero for -fomit-frame-pointer: - don't make a frame pointer in simple functions that don't require one. */ - -int flag_omit_frame_pointer = 0; - -/* Nonzero to inhibit use of define_optimization peephole opts. */ - -int flag_no_peephole = 0; - -/* Nonzero allows GCC to violate some IEEE or ANSI rules regarding math - operations in the interest of optimization. For example it allows - GCC to assume arguments to sqrt are nonnegative numbers, allowing - faster code for sqrt to be generated. */ - -int flag_fast_math = 0; - -/* Nonzero means all references through pointers are volatile. */ - -int flag_volatile; - -/* Nonzero means treat all global and extern variables as global. */ - -int flag_volatile_global; - -/* Nonzero means just do syntax checking; don't output anything. */ - -int flag_syntax_only = 0; - -/* Nonzero means to rerun cse after loop optimization. This increases - compilation time about 20% and picks up a few more common expressions. */ - -static int flag_rerun_cse_after_loop; - -/* Nonzero for -finline-functions: ok to inline functions that look like - good inline candidates. */ - -int flag_inline_functions; - -/* Nonzero for -fkeep-inline-functions: even if we make a function - go inline everywhere, keep its definition around for debugging - purposes. */ - -int flag_keep_inline_functions; - -/* Nonzero means that functions declared `inline' will be treated - as `static'. Prevents generation of zillions of copies of unused - static inline functions; instead, `inlines' are written out - only when actually used. Used in conjunction with -g. Also - does the right thing with #pragma interface. */ - -int flag_no_inline; - -/* Nonzero means we should be saving declaration info into a .X file. */ - -int flag_gen_aux_info = 0; - -/* Specified name of aux-info file. */ - -static char *aux_info_file_name; - -/* Nonzero means make the text shared if supported. */ - -int flag_shared_data; - -/* Nonzero means schedule into delayed branch slots if supported. */ - -int flag_delayed_branch; - -/* Nonzero if we are compiling pure (sharable) code. - Value is 1 if we are doing reasonable (i.e. simple - offset into offset table) pic. Value is 2 if we can - only perform register offsets. */ - -int flag_pic; - -/* Nonzero means place uninitialized global data in the bss section. */ - -int flag_no_common; - -/* Nonzero means pretend it is OK to examine bits of target floats, - even if that isn't true. The resulting code will have incorrect constants, - but the same series of instructions that the native compiler would make. */ - -int flag_pretend_float; - -/* Nonzero means change certain warnings into errors. - Usually these are warnings about failure to conform to some standard. */ - -int flag_pedantic_errors = 0; - -/* flag_schedule_insns means schedule insns within basic blocks (before - local_alloc). - flag_schedule_insns_after_reload means schedule insns after - global_alloc. */ - -int flag_schedule_insns = 0; -int flag_schedule_insns_after_reload = 0; - -/* -finhibit-size-directive inhibits output of .size for ELF. - This is used only for compiling crtstuff.c, - and it may be extended to other effects - needed for crtstuff.c on other systems. */ -int flag_inhibit_size_directive = 0; - -/* -fverbose-asm causes extra commentary information to be produced in - the generated assembly code (to make it more readable). This option - is generally only of use to those who actually need to read the - generated assembly code (perhaps while debugging the compiler itself). */ - -int flag_verbose_asm = 0; - -/* -fgnu-linker specifies use of the GNU linker for initializations. - (Or, more generally, a linker that handles initializations.) - -fno-gnu-linker says that collect2 will be used. */ -#ifdef USE_COLLECT2 -int flag_gnu_linker = 0; -#else -int flag_gnu_linker = 1; -#endif - -/* Table of language-independent -f options. - STRING is the option name. VARIABLE is the address of the variable. - ON_VALUE is the value to store in VARIABLE - if `-fSTRING' is seen as an option. - (If `-fno-STRING' is seen as an option, the opposite value is stored.) */ - -struct { char *string; int *variable; int on_value;} f_options[] = -{ - {"float-store", &flag_float_store, 1}, - {"volatile", &flag_volatile, 1}, - {"volatile-global", &flag_volatile_global, 1}, - {"defer-pop", &flag_defer_pop, 1}, - {"omit-frame-pointer", &flag_omit_frame_pointer, 1}, - {"cse-follow-jumps", &flag_cse_follow_jumps, 1}, - {"cse-skip-blocks", &flag_cse_skip_blocks, 1}, - {"expensive-optimizations", &flag_expensive_optimizations, 1}, - {"thread-jumps", &flag_thread_jumps, 1}, - {"strength-reduce", &flag_strength_reduce, 1}, - {"unroll-loops", &flag_unroll_loops, 1}, - {"unroll-all-loops", &flag_unroll_all_loops, 1}, - {"writable-strings", &flag_writable_strings, 1}, - {"peephole", &flag_no_peephole, 0}, - {"force-mem", &flag_force_mem, 1}, - {"force-addr", &flag_force_addr, 1}, - {"function-cse", &flag_no_function_cse, 0}, - {"inline-functions", &flag_inline_functions, 1}, - {"keep-inline-functions", &flag_keep_inline_functions, 1}, - {"inline", &flag_no_inline, 0}, - {"syntax-only", &flag_syntax_only, 1}, - {"shared-data", &flag_shared_data, 1}, - {"caller-saves", &flag_caller_saves, 1}, - {"pcc-struct-return", &flag_pcc_struct_return, 1}, - {"reg-struct-return", &flag_pcc_struct_return, 0}, - {"delayed-branch", &flag_delayed_branch, 1}, - {"rerun-cse-after-loop", &flag_rerun_cse_after_loop, 1}, - {"pretend-float", &flag_pretend_float, 1}, - {"schedule-insns", &flag_schedule_insns, 1}, - {"schedule-insns2", &flag_schedule_insns_after_reload, 1}, - {"pic", &flag_pic, 1}, - {"PIC", &flag_pic, 2}, - {"fast-math", &flag_fast_math, 1}, - {"common", &flag_no_common, 0}, - {"inhibit-size-directive", &flag_inhibit_size_directive, 1}, - {"verbose-asm", &flag_verbose_asm, 1}, - {"gnu-linker", &flag_gnu_linker, 1} -}; - -/* Table of language-specific options. */ - -char *lang_options[] = -{ - "-ftraditional", - "-traditional", - "-fnotraditional", - "-fno-traditional", - "-fsigned-char", - "-funsigned-char", - "-fno-signed-char", - "-fno-unsigned-char", - "-fsigned-bitfields", - "-funsigned-bitfields", - "-fno-signed-bitfields", - "-fno-unsigned-bitfields", - "-fshort-enums", - "-fno-short-enums", - "-fcond-mismatch", - "-fno-cond-mismatch", - "-fshort-double", - "-fno-short-double", - "-fasm", - "-fno-asm", - "-fbuiltin", - "-fno-builtin", - "-fno-ident", - "-fident", - "-ansi", - "-Wimplicit", - "-Wno-implicit", - "-Wwrite-strings", - "-Wno-write-strings", - "-Wcast-qual", - "-Wno-cast-qual", - "-Wpointer-arith", - "-Wno-pointer-arith", - "-Wstrict-prototypes", - "-Wno-strict-prototypes", - "-Wmissing-prototypes", - "-Wno-missing-prototypes", - "-Wredundant-decls", - "-Wno-redundant-decls", - "-Wnested-externs", - "-Wno-nested-externs", - "-Wtraditional", - "-Wno-traditional", - "-Wformat", - "-Wno-format", - "-Wchar-subscripts", - "-Wno-char-subscripts", - "-Wconversion", - "-Wno-conversion", - "-Wparentheses", - "-Wno-parentheses", - "-Wcomment", - "-Wno-comment", - "-Wcomments", - "-Wno-comments", - "-Wtrigraphs", - "-Wno-trigraphs", - "-Wimport", - "-Wno-import", - "-Wmissing-braces", - "-Wno-missing-braces", - "-Wall", - - /* These are for C++. */ - "-+e0", /* gcc.c tacks the `-' on the front. */ - "-+e1", - "-+e2", - "-fsave-memoized", - "-fno-save-memoized", - "-fcadillac", - "-fno-cadillac", - "-fgc", - "-fno-gc", - "-flabels-ok", - "-fno-labels-ok", - "-fstats", - "-fno-stats", - "-fthis-is-variable", - "-fno-this-is-variable", - "-fstrict-prototype", - "-fno-strict-prototype", - "-fall-virtual", - "-fno-all-virtual", - "-fmemoize-lookups", - "-fno-memoize-lookups", - "-felide-constructors", - "-fno-elide-constructors", - "-finline-debug", - "-fno-inline-debug", - "-fhandle-exceptions", - "-fno-handle-exceptions", - "-fansi-exceptions", - "-fno-ansi-exceptions", - "-fspring-exceptions", - "-fno-spring-exceptions", - "-fdefault-inline", - "-fno-default-inline", - "-fenum-int-equiv", - "-fno-enum-int-equiv", - "-fdossier", - "-fno-dossier", - "-fxref", - "-fno-xref", - "-fnonnull-objects", - "-fno-nonnull-objects", - "-fimplement-inlines", - "-fno-implement-inlines", - - "-Wreturn-type", - "-Wno-return-type", - "-Woverloaded-virtual", - "-Wno-overloaded-virtual", - "-Wenum-clash", - "-Wno-enum-clash", - "-Wtemplate-debugging", - "-Wno-template-debugging", - - /* these are for obj c */ - "-lang-objc", - "-gen-decls", - "-fgnu-runtime", - "-fno-gnu-runtime", - "-fnext-runtime", - "-fno-next-runtime", - "-Wselector", - "-Wno-selector", - "-Wprotocol", - "-Wno-protocol", - 0 -}; - -/* Options controlling warnings */ - -/* Don't print warning messages. -w. */ - -int inhibit_warnings = 0; - -/* Print various extra warnings. -W. */ - -int extra_warnings = 0; - -/* Treat warnings as errors. -Werror. */ - -int warnings_are_errors = 0; - -/* Nonzero to warn about unused local variables. */ - -int warn_unused; - -/* Nonzero to warn about variables used before they are initialized. */ - -int warn_uninitialized; - -/* Nonzero means warn about all declarations which shadow others. */ - -int warn_shadow; - -/* Warn if a switch on an enum fails to have a case for every enum value. */ - -int warn_switch; - -/* Nonzero means warn about function definitions that default the return type - or that use a null return and have a return-type other than void. */ - -int warn_return_type; - -/* Nonzero means warn about pointer casts that increase the required - alignment of the target type (and might therefore lead to a crash - due to a misaligned access). */ - -int warn_cast_align; - -/* Nonzero means warn about any identifiers that match in the first N - characters. The value N is in `id_clash_len'. */ - -int warn_id_clash; -int id_clash_len; - -/* Nonzero means warn if inline function is too large. */ - -int warn_inline; - -/* Warn if a function returns an aggregate, - since there are often incompatible calling conventions for doing this. */ - -int warn_aggregate_return; - -/* Likewise for -W. */ - -struct { char *string; int *variable; int on_value;} W_options[] = -{ - {"unused", &warn_unused, 1}, - {"error", &warnings_are_errors, 1}, - {"shadow", &warn_shadow, 1}, - {"switch", &warn_switch, 1}, - {"aggregate-return", &warn_aggregate_return, 1}, - {"cast-align", &warn_cast_align, 1}, - {"uninitialized", &warn_uninitialized, 1}, - {"inline", &warn_inline, 1} -}; - -/* Output files for assembler code (real compiler output) - and debugging dumps. */ - -FILE *asm_out_file; -FILE *aux_info_file; -FILE *rtl_dump_file; -FILE *jump_opt_dump_file; -FILE *cse_dump_file; -FILE *loop_dump_file; -FILE *cse2_dump_file; -FILE *flow_dump_file; -FILE *combine_dump_file; -FILE *sched_dump_file; -FILE *local_reg_dump_file; -FILE *global_reg_dump_file; -FILE *sched2_dump_file; -FILE *jump2_opt_dump_file; -FILE *dbr_sched_dump_file; -FILE *stack_reg_dump_file; - -/* Time accumulators, to count the total time spent in various passes. */ - -int parse_time; -int varconst_time; -int integration_time; -int jump_time; -int cse_time; -int loop_time; -int cse2_time; -int flow_time; -int combine_time; -int sched_time; -int local_alloc_time; -int global_alloc_time; -int sched2_time; -int dbr_sched_time; -int shorten_branch_time; -int stack_reg_time; -int final_time; -int symout_time; -int dump_time; - -/* Return time used so far, in microseconds. */ - -int -get_run_time () -{ -#ifdef USG - struct tms tms; -#else -#ifndef VMS - struct rusage rusage; -#else /* VMS */ - struct - { - int proc_user_time; - int proc_system_time; - int child_user_time; - int child_system_time; - } vms_times; -#endif -#endif - - if (quiet_flag) - return 0; - -#ifdef USG - times (&tms); - return (tms.tms_utime + tms.tms_stime) * (1000000 / HZ); -#else -#ifndef VMS - getrusage (0, &rusage); - return (rusage.ru_utime.tv_sec * 1000000 + rusage.ru_utime.tv_usec - + rusage.ru_stime.tv_sec * 1000000 + rusage.ru_stime.tv_usec); -#else /* VMS */ - times (&vms_times); - return (vms_times.proc_user_time + vms_times.proc_system_time) * 10000; -#endif -#endif -} - -#define TIMEVAR(VAR, BODY) \ -do { int otime = get_run_time (); BODY; VAR += get_run_time () - otime; } while (0) - -void -print_time (str, total) - char *str; - int total; -{ - fprintf (stderr, - "time in %s: %d.%06d\n", - str, total / 1000000, total % 1000000); -} - -/* Count an error or warning. Return 1 if the message should be printed. */ - -int -count_error (warningp) - int warningp; -{ - if (warningp && inhibit_warnings) - return 0; - - if (warningp && !warnings_are_errors) - warningcount++; - else - { - static int warning_message = 0; - - if (warningp && !warning_message) - { - fprintf (stderr, "%s: warnings being treated as errors\n", progname); - warning_message = 1; - } - errorcount++; - } - - return 1; -} - -/* Print a fatal error message. NAME is the text. - Also include a system error message based on `errno'. */ - -void -pfatal_with_name (name) - char *name; -{ - fprintf (stderr, "%s: ", progname); - perror (name); - exit (35); -} - -void -fatal_io_error (name) - char *name; -{ - fprintf (stderr, "%s: %s: I/O error\n", progname, name); - exit (35); -} - -void -fatal (s, v) - char *s; - int v; -{ - error (s, v); - exit (34); -} - -/* Called to give a better error message when we don't have an insn to match - what we are looking for or if the insn's constraints aren't satisfied, - rather than just calling abort(). */ - -void -fatal_insn_not_found (insn) - rtx insn; -{ - if (INSN_CODE (insn) < 0) - error ("internal error--unrecognizable insn:", 0); - else - error ("internal error--insn does not satisfy its constraints:", 0); - debug_rtx (insn); - if (asm_out_file) - fflush (asm_out_file); - if (aux_info_file) - fflush (aux_info_file); - if (rtl_dump_file) - fflush (rtl_dump_file); - if (jump_opt_dump_file) - fflush (jump_opt_dump_file); - if (cse_dump_file) - fflush (cse_dump_file); - if (loop_dump_file) - fflush (loop_dump_file); - if (cse2_dump_file) - fflush (cse2_dump_file); - if (flow_dump_file) - fflush (flow_dump_file); - if (combine_dump_file) - fflush (combine_dump_file); - if (sched_dump_file) - fflush (sched_dump_file); - if (local_reg_dump_file) - fflush (local_reg_dump_file); - if (global_reg_dump_file) - fflush (global_reg_dump_file); - if (sched2_dump_file) - fflush (sched2_dump_file); - if (jump2_opt_dump_file) - fflush (jump2_opt_dump_file); - if (dbr_sched_dump_file) - fflush (dbr_sched_dump_file); - if (stack_reg_dump_file) - fflush (stack_reg_dump_file); - abort (); -} - -/* This is the default decl_printable_name function. */ - -static char * -decl_name (decl, kind) - tree decl; - char **kind; -{ - return IDENTIFIER_POINTER (DECL_NAME (decl)); -} - -static int need_error_newline; - -/* Function of last error message; - more generally, function such that if next error message is in it - then we don't have to mention the function name. */ -static tree last_error_function = NULL; - -/* Used to detect when input_file_stack has changed since last described. */ -static int last_error_tick; - -/* Called when the start of a function definition is parsed, - this function prints on stderr the name of the function. */ - -void -announce_function (decl) - tree decl; -{ - if (! quiet_flag) - { - char *junk; - if (rtl_dump_and_exit) - fprintf (stderr, "%s ", IDENTIFIER_POINTER (DECL_NAME (decl))); - else - fprintf (stderr, " %s", (*decl_printable_name) (decl, &junk)); - fflush (stderr); - need_error_newline = 1; - last_error_function = current_function_decl; - } -} - -/* Prints out, if necessary, the name of the current function - which caused an error. Called from all error and warning functions. */ - -void -report_error_function (file) - char *file; -{ - struct file_stack *p; - - if (need_error_newline) - { - fprintf (stderr, "\n"); - need_error_newline = 0; - } - - if (last_error_function != current_function_decl) - { - char *kind = "function"; - if (current_function_decl != 0 - && TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE) - kind = "method"; - - if (file) - fprintf (stderr, "%s: ", file); - - if (current_function_decl == NULL) - fprintf (stderr, "At top level:\n"); - else - { - char *name = (*decl_printable_name) (current_function_decl, &kind); - fprintf (stderr, "In %s `%s':\n", kind, name); - } - - last_error_function = current_function_decl; - } - if (input_file_stack && input_file_stack->next != 0 - && input_file_stack_tick != last_error_tick) - { - fprintf (stderr, "In file included"); - for (p = input_file_stack->next; p; p = p->next) - { - fprintf (stderr, " from %s:%d", p->name, p->line); - if (p->next) - fprintf (stderr, ","); - } - fprintf (stderr, ":\n"); - last_error_tick = input_file_stack_tick; - } -} - -/* Report an error at the current line number. - S is a string and V and V2 are args for `printf'. We use HOST_WIDE_INT - as the type for these args assuming it is wide enough to hold a - pointer. This isn't terribly portable, but is the best we can do - without vprintf universally available. */ - -void -error (s, v, v2) - char *s; - HOST_WIDE_INT v; /* Also used as pointer */ - HOST_WIDE_INT v2; /* Also used as pointer */ -{ - error_with_file_and_line (input_filename, lineno, s, v, v2); -} - -/* Report an error at line LINE of file FILE. - S and V are a string and an arg for `printf'. */ - -void -error_with_file_and_line (file, line, s, v, v2) - char *file; - int line; - char *s; - HOST_WIDE_INT v; - HOST_WIDE_INT v2; -{ - count_error (0); - - report_error_function (file); - - if (file) - fprintf (stderr, "%s:%d: ", file, line); - else - fprintf (stderr, "%s: ", progname); - fprintf (stderr, s, v, v2); - fprintf (stderr, "\n"); -} - -/* Report an error at the declaration DECL. - S and V are a string and an arg which uses %s to substitute - the declaration name. */ - -void -error_with_decl (decl, s, v) - tree decl; - char *s; - HOST_WIDE_INT v; -{ - char *junk; - count_error (0); - - report_error_function (DECL_SOURCE_FILE (decl)); - - fprintf (stderr, "%s:%d: ", - DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl)); - - if (DECL_NAME (decl)) - fprintf (stderr, s, (*decl_printable_name) (decl, &junk), v); - else - fprintf (stderr, s, "((anonymous))", v); - fprintf (stderr, "\n"); -} - -/* Report an error at the line number of the insn INSN. - S and V are a string and an arg for `printf'. - This is used only when INSN is an `asm' with operands, - and each ASM_OPERANDS records its own source file and line. */ - -void -error_for_asm (insn, s, v, v2) - rtx insn; - char *s; - HOST_WIDE_INT v; /* Also used as pointer */ - HOST_WIDE_INT v2; /* Also used as pointer */ -{ - char *filename; - int line; - rtx body = PATTERN (insn); - rtx asmop; - - /* Find the (or one of the) ASM_OPERANDS in the insn. */ - if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS) - asmop = SET_SRC (body); - else if (GET_CODE (body) == ASM_OPERANDS) - asmop = body; - else if (GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == SET) - asmop = SET_SRC (XVECEXP (body, 0, 0)); - else if (GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS) - asmop = XVECEXP (body, 0, 0); - - filename = ASM_OPERANDS_SOURCE_FILE (asmop); - line = ASM_OPERANDS_SOURCE_LINE (asmop); - - error_with_file_and_line (filename, line, s, v, v2); -} - -/* Report a warning at line LINE. - S and V are a string and an arg for `printf'. */ - -void -warning_with_file_and_line (file, line, s, v, v2, v3) - char *file; - int line; - char *s; - HOST_WIDE_INT v, v2, v3; -{ - if (count_error (1) == 0) - return; - - report_error_function (file); - - if (file) - fprintf (stderr, "%s:%d: ", file, line); - else - fprintf (stderr, "%s: ", progname); - - fprintf (stderr, "warning: "); - fprintf (stderr, s, v, v2, v3); - fprintf (stderr, "\n"); -} - -/* Report a warning at the current line number. - S and V are a string and an arg for `printf'. */ - -void -warning (s, v, v2, v3) - char *s; - HOST_WIDE_INT v, v2, v3; /* Also used as pointer */ -{ - warning_with_file_and_line (input_filename, lineno, s, v, v2, v3); -} - -/* Report a warning at the declaration DECL. - S is string which uses %s to substitute the declaration name. - V is a second parameter that S can refer to. */ - -void -warning_with_decl (decl, s, v) - tree decl; - char *s; - HOST_WIDE_INT v; -{ - char *junk; - - if (count_error (1) == 0) - return; - - report_error_function (DECL_SOURCE_FILE (decl)); - - fprintf (stderr, "%s:%d: ", - DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl)); - - fprintf (stderr, "warning: "); - if (DECL_NAME (decl)) - fprintf (stderr, s, (*decl_printable_name) (decl, &junk), v); - else - fprintf (stderr, s, "((anonymous))", v); - fprintf (stderr, "\n"); -} - -/* Report a warning at the line number of the insn INSN. - S and V are a string and an arg for `printf'. - This is used only when INSN is an `asm' with operands, - and each ASM_OPERANDS records its own source file and line. */ - -void -warning_for_asm (insn, s, v, v2) - rtx insn; - char *s; - HOST_WIDE_INT v; /* Also used as pointer */ - HOST_WIDE_INT v2; /* Also used as pointer */ -{ - char *filename; - int line; - rtx body = PATTERN (insn); - rtx asmop; - - /* Find the (or one of the) ASM_OPERANDS in the insn. */ - if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS) - asmop = SET_SRC (body); - else if (GET_CODE (body) == ASM_OPERANDS) - asmop = body; - else if (GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == SET) - asmop = SET_SRC (XVECEXP (body, 0, 0)); - else if (GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS) - asmop = XVECEXP (body, 0, 0); - - filename = ASM_OPERANDS_SOURCE_FILE (asmop); - line = ASM_OPERANDS_SOURCE_LINE (asmop); - - warning_with_file_and_line (filename, line, s, v, v2); -} - -/* These functions issue either warnings or errors depending on - -pedantic-errors. */ - -void -pedwarn (s, v, v2) - char *s; - HOST_WIDE_INT v; /* Also used as pointer */ - HOST_WIDE_INT v2; -{ - if (flag_pedantic_errors) - error (s, v, v2); - else - warning (s, v, v2); -} - -void -pedwarn_with_decl (decl, s, v) - tree decl; - char *s; - HOST_WIDE_INT v; -{ - if (flag_pedantic_errors) - error_with_decl (decl, s, v); - else - warning_with_decl (decl, s, v); -} - -void -pedwarn_with_file_and_line (file, line, s, v, v2) - char *file; - int line; - char *s; - HOST_WIDE_INT v; - HOST_WIDE_INT v2; -{ - if (flag_pedantic_errors) - error_with_file_and_line (file, line, s, v, v2); - else - warning_with_file_and_line (file, line, s, v, v2); -} - -/* Apologize for not implementing some feature. - S, V, and V2 are a string and args for `printf'. */ - -void -sorry (s, v, v2) - char *s; - HOST_WIDE_INT v, v2; -{ - sorrycount++; - if (input_filename) - fprintf (stderr, "%s:%d: ", input_filename, lineno); - else - fprintf (stderr, "%s: ", progname); - - fprintf (stderr, "sorry, not implemented: "); - fprintf (stderr, s, v, v2); - fprintf (stderr, "\n"); -} - -/* Apologize for not implementing some feature, then quit. - S, V, and V2 are a string and args for `printf'. */ - -void -really_sorry (s, v, v2) - char *s; - HOST_WIDE_INT v, v2; -{ - if (input_filename) - fprintf (stderr, "%s:%d: ", input_filename, lineno); - else - fprintf (stderr, "%s: ", progname); - - fprintf (stderr, "sorry, not implemented: "); - fprintf (stderr, s, v, v2); - fatal (" (fatal)\n"); -} - -/* More 'friendly' abort that prints the line and file. - config.h can #define abort fancy_abort if you like that sort of thing. - - I don't think this is actually a good idea. - Other sorts of crashes will look a certain way. - It is a good thing if crashes from calling abort look the same way. - -- RMS */ - -void -fancy_abort () -{ - fatal ("internal gcc abort"); -} - -/* This calls abort and is used to avoid problems when abort if a macro. - It is used when we need to pass the address of abort. */ - -void -do_abort () -{ - abort (); -} - -/* When `malloc.c' is compiled with `rcheck' defined, - it calls this function to report clobberage. */ - -void -botch (s) -{ - abort (); -} - -/* Same as `malloc' but report error if no memory available. */ - -char * -xmalloc (size) - unsigned size; -{ - register char *value = (char *) malloc (size); - if (value == 0) - fatal ("virtual memory exhausted"); - return value; -} - -/* Same as `realloc' but report error if no memory available. */ - -char * -xrealloc (ptr, size) - char *ptr; - int size; -{ - char *result = (char *) realloc (ptr, size); - if (!result) - fatal ("virtual memory exhausted"); - return result; -} - -/* Return the logarithm of X, base 2, considering X unsigned, - if X is a power of 2. Otherwise, returns -1. - - This should be used via the `exact_log2' macro. */ - -int -exact_log2_wide (x) - register unsigned HOST_WIDE_INT x; -{ - register int log = 0; - /* Test for 0 or a power of 2. */ - if (x == 0 || x != (x & -x)) - return -1; - while ((x >>= 1) != 0) - log++; - return log; -} - -/* Given X, an unsigned number, return the largest int Y such that 2**Y <= X. - If X is 0, return -1. - - This should be used via the floor_log2 macro. */ - -int -floor_log2_wide (x) - register unsigned HOST_WIDE_INT x; -{ - register int log = -1; - while (x != 0) - log++, - x >>= 1; - return log; -} - -int float_handled; -jmp_buf float_handler; - -/* Specify where to longjmp to when a floating arithmetic error happens. - If HANDLER is 0, it means don't handle the errors any more. */ - -void -set_float_handler (handler) - jmp_buf handler; -{ - float_handled = (handler != 0); - if (handler) - bcopy (handler, float_handler, sizeof (float_handler)); -} - -/* Specify, in HANDLER, where to longjmp to when a floating arithmetic - error happens, pushing the previous specification into OLD_HANDLER. - Return an indication of whether there was a previous handler in effect. */ - -int -push_float_handler (handler, old_handler) - jmp_buf handler, old_handler; -{ - int was_handled = float_handled; - - float_handled = 1; - if (was_handled) - bcopy (float_handler, old_handler, sizeof (float_handler)); - bcopy (handler, float_handler, sizeof (float_handler)); - return was_handled; -} - -/* Restore the previous specification of whether and where to longjmp to - when a floating arithmetic error happens. */ - -void -pop_float_handler (handled, handler) - int handled; - jmp_buf handler; -{ - float_handled = handled; - if (handled) - bcopy (handler, float_handler, sizeof (float_handler)); -} - -/* Signals actually come here. */ - -static void -float_signal (signo) - /* If this is missing, some compilers complain. */ - int signo; -{ - if (float_handled == 0) - abort (); -#if defined (USG) || defined (hpux) - signal (SIGFPE, float_signal); /* re-enable the signal catcher */ -#endif - float_handled = 0; - signal (SIGFPE, float_signal); - longjmp (float_handler, 1); -} - -/* Handler for SIGPIPE. */ - -static void -pipe_closed (signo) - /* If this is missing, some compilers complain. */ - int signo; -{ - fatal ("output pipe has been closed"); -} - -/* Strip off a legitimate source ending from the input string NAME of - length LEN. */ - -void -strip_off_ending (name, len) - char *name; - int len; -{ - if (len > 2 && ! strcmp (".c", name + len - 2)) - name[len - 2] = 0; - else if (len > 2 && ! strcmp (".m", name + len - 2)) - name[len - 2] = 0; - else if (len > 2 && ! strcmp (".i", name + len - 2)) - name[len - 2] = 0; - else if (len > 3 && ! strcmp (".ii", name + len - 3)) - name[len - 3] = 0; - else if (len > 3 && ! strcmp (".co", name + len - 3)) - name[len - 3] = 0; - else if (len > 3 && ! strcmp (".cc", name + len - 3)) - name[len - 3] = 0; - else if (len > 2 && ! strcmp (".C", name + len - 2)) - name[len - 2] = 0; - else if (len > 4 && ! strcmp (".cxx", name + len - 4)) - name[len - 4] = 0; - else if (len > 2 && ! strcmp (".f", name + len - 2)) - name[len - 2] = 0; - else if (len > 4 && ! strcmp (".ada", name + len - 4)) - name[len - 4] = 0; - else if (len > 4 && ! strcmp (".atr", name + len - 4)) - name[len - 4] = 0; -} - -/* Output a file name in the form wanted by System V. */ - -void -output_file_directive (asm_file, input_name) - FILE *asm_file; - char *input_name; -{ - int len = strlen (input_name); - char *na = input_name + len; - - /* NA gets INPUT_NAME sans directory names. */ - while (na > input_name) - { - if (na[-1] == '/') - break; - na--; - } - -#ifdef ASM_OUTPUT_MAIN_SOURCE_FILENAME - ASM_OUTPUT_MAIN_SOURCE_FILENAME (asm_file, na); -#else -#ifdef ASM_OUTPUT_SOURCE_FILENAME - ASM_OUTPUT_SOURCE_FILENAME (asm_file, na); -#else - fprintf (asm_file, "\t.file\t\"%s\"\n", na); -#endif -#endif -} - -/* Routine to build language identifier for object file. */ -static void -output_lang_identify (asm_out_file) - FILE *asm_out_file; -{ - int len = strlen (lang_identify ()) + sizeof ("__gnu_compiled_") + 1; - char *s = (char *) alloca (len); - sprintf (s, "__gnu_compiled_%s", lang_identify ()); - ASM_OUTPUT_LABEL (asm_out_file, s); -} - -/* Compile an entire file of output from cpp, named NAME. - Write a file of assembly output and various debugging dumps. */ - -static void -compile_file (name) - char *name; -{ - tree globals; - int start_time; - int dump_base_name_length; - - int name_specified = name != 0; - - if (dump_base_name == 0) - dump_base_name = name ? name : "gccdump"; - dump_base_name_length = strlen (dump_base_name); - - parse_time = 0; - varconst_time = 0; - integration_time = 0; - jump_time = 0; - cse_time = 0; - loop_time = 0; - cse2_time = 0; - flow_time = 0; - combine_time = 0; - sched_time = 0; - local_alloc_time = 0; - global_alloc_time = 0; - sched2_time = 0; - dbr_sched_time = 0; - shorten_branch_time = 0; - stack_reg_time = 0; - final_time = 0; - symout_time = 0; - dump_time = 0; - - /* Open input file. */ - - if (name == 0 || !strcmp (name, "-")) - { - finput = stdin; - name = "stdin"; - } - else - finput = fopen (name, "r"); - if (finput == 0) - pfatal_with_name (name); - -#ifdef IO_BUFFER_SIZE - setvbuf (finput, (char *) xmalloc (IO_BUFFER_SIZE), _IOFBF, IO_BUFFER_SIZE); -#endif - - /* Initialize data in various passes. */ - - init_obstacks (); - init_tree_codes (); - init_lex (); - init_rtl (); - init_emit_once (debug_info_level == DINFO_LEVEL_NORMAL - || debug_info_level == DINFO_LEVEL_VERBOSE); - init_decl_processing (); - init_optabs (); - init_stmt (); - init_expmed (); - init_expr_once (); - init_loop (); - init_reload (); - - if (flag_caller_saves) - init_caller_save (); - - /* If auxiliary info generation is desired, open the output file. - This goes in the same directory as the source file--unlike - all the other output files. */ - if (flag_gen_aux_info) - { - aux_info_file = fopen (aux_info_file_name, "w"); - if (aux_info_file == 0) - pfatal_with_name (aux_info_file_name); - } - - /* If rtl dump desired, open the output file. */ - if (rtl_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".rtl"); - rtl_dump_file = fopen (dumpname, "w"); - if (rtl_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If jump_opt dump desired, open the output file. */ - if (jump_opt_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".jump"); - jump_opt_dump_file = fopen (dumpname, "w"); - if (jump_opt_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If cse dump desired, open the output file. */ - if (cse_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".cse"); - cse_dump_file = fopen (dumpname, "w"); - if (cse_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If loop dump desired, open the output file. */ - if (loop_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".loop"); - loop_dump_file = fopen (dumpname, "w"); - if (loop_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If cse2 dump desired, open the output file. */ - if (cse2_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".cse2"); - cse2_dump_file = fopen (dumpname, "w"); - if (cse2_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If flow dump desired, open the output file. */ - if (flow_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".flow"); - flow_dump_file = fopen (dumpname, "w"); - if (flow_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If combine dump desired, open the output file. */ - if (combine_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 10); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".combine"); - combine_dump_file = fopen (dumpname, "w"); - if (combine_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If scheduling dump desired, open the output file. */ - if (sched_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 7); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".sched"); - sched_dump_file = fopen (dumpname, "w"); - if (sched_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If local_reg dump desired, open the output file. */ - if (local_reg_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".lreg"); - local_reg_dump_file = fopen (dumpname, "w"); - if (local_reg_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If global_reg dump desired, open the output file. */ - if (global_reg_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".greg"); - global_reg_dump_file = fopen (dumpname, "w"); - if (global_reg_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If 2nd scheduling dump desired, open the output file. */ - if (sched2_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 8); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".sched2"); - sched2_dump_file = fopen (dumpname, "w"); - if (sched2_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If jump2_opt dump desired, open the output file. */ - if (jump2_opt_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 7); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".jump2"); - jump2_opt_dump_file = fopen (dumpname, "w"); - if (jump2_opt_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If dbr_sched dump desired, open the output file. */ - if (dbr_sched_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 7); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".dbr"); - dbr_sched_dump_file = fopen (dumpname, "w"); - if (dbr_sched_dump_file == 0) - pfatal_with_name (dumpname); - } - -#ifdef STACK_REGS - - /* If stack_reg dump desired, open the output file. */ - if (stack_reg_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 10); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".stack"); - stack_reg_dump_file = fopen (dumpname, "w"); - if (stack_reg_dump_file == 0) - pfatal_with_name (dumpname); - } - -#endif - - /* Open assembler code output file. */ - - if (! name_specified && asm_file_name == 0) - asm_out_file = stdout; - else - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - int len = strlen (dump_base_name); - strcpy (dumpname, dump_base_name); - strip_off_ending (dumpname, len); - strcat (dumpname, ".s"); - if (asm_file_name == 0) - { - asm_file_name = (char *) xmalloc (strlen (dumpname) + 1); - strcpy (asm_file_name, dumpname); - } - if (!strcmp (asm_file_name, "-")) - asm_out_file = stdout; - else - asm_out_file = fopen (asm_file_name, "w"); - if (asm_out_file == 0) - pfatal_with_name (asm_file_name); - } - -#ifdef IO_BUFFER_SIZE - setvbuf (asm_out_file, (char *) xmalloc (IO_BUFFER_SIZE), - _IOFBF, IO_BUFFER_SIZE); -#endif - - input_filename = name; - - /* Perform language-specific initialization. - This may set main_input_filename. */ - lang_init (); - - /* If the input doesn't start with a #line, use the input name - as the official input file name. */ - if (main_input_filename == 0) - main_input_filename = name; - - /* Put an entry on the input file stack for the main input file. */ - input_file_stack - = (struct file_stack *) xmalloc (sizeof (struct file_stack)); - input_file_stack->next = 0; - input_file_stack->name = input_filename; - - ASM_FILE_START (asm_out_file); - - /* Output something to inform GDB that this compilation was by GCC. */ -#ifndef ASM_IDENTIFY_GCC - fprintf (asm_out_file, "gcc2_compiled.:\n"); -#else - ASM_IDENTIFY_GCC (asm_out_file); -#endif - - /* Output something to identify which front-end produced this file. */ -#ifdef ASM_IDENTIFY_LANGUAGE - ASM_IDENTIFY_LANGUAGE (asm_out_file); -#endif - -/* ??? Note: There used to be a conditional here - to call assemble_zeros without fail if DBX_DEBUGGING_INFO is defined. - This was to guarantee separation between gcc_compiled. and - the first function, for the sake of dbx on Suns. - However, having the extra zero here confused the Emacs - code for unexec, and might confuse other programs too. - Therefore, I took out that change. - In future versions we should find another way to solve - that dbx problem. -- rms, 23 May 93. */ - - /* Don't let the first function fall at the same address - as gcc_compiled., if profiling. */ - if (profile_flag || profile_block_flag) - assemble_zeros (UNITS_PER_WORD); - - /* If dbx symbol table desired, initialize writing it - and output the predefined types. */ -#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) - if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) - TIMEVAR (symout_time, dbxout_init (asm_out_file, main_input_filename, - getdecls ())); -#endif -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG) - TIMEVAR (symout_time, sdbout_init (asm_out_file, main_input_filename, - getdecls ())); -#endif -#ifdef DWARF_DEBUGGING_INFO - if (write_symbols == DWARF_DEBUG) - TIMEVAR (symout_time, dwarfout_init (asm_out_file, main_input_filename)); -#endif - - /* Initialize yet another pass. */ - - init_final (main_input_filename); - - start_time = get_run_time (); - - /* Call the parser, which parses the entire file - (calling rest_of_compilation for each function). */ - - if (yyparse () != 0) - if (errorcount == 0) - fprintf (stderr, "Errors detected in input file (your bison.simple is out of date)"); - - /* Compilation is now finished except for writing - what's left of the symbol table output. */ - - parse_time += get_run_time () - start_time; - - parse_time -= integration_time; - parse_time -= varconst_time; - - globals = getdecls (); - - /* Really define vars that have had only a tentative definition. - Really output inline functions that must actually be callable - and have not been output so far. */ - - { - int len = list_length (globals); - tree *vec = (tree *) alloca (sizeof (tree) * len); - int i; - tree decl; - - /* Process the decls in reverse order--earliest first. - Put them into VEC from back to front, then take out from front. */ - - for (i = 0, decl = globals; i < len; i++, decl = TREE_CHAIN (decl)) - vec[len - i - 1] = decl; - - for (i = 0; i < len; i++) - { - decl = vec[i]; - if (TREE_CODE (decl) == VAR_DECL && DECL_SIZE (decl) == 0 - && incomplete_decl_finalize_hook != 0) - (*incomplete_decl_finalize_hook) (decl); - - if (TREE_CODE (decl) == VAR_DECL && TREE_STATIC (decl) - && ! TREE_ASM_WRITTEN (decl)) - { - /* Don't write out static consts, unless we used them. - (This used to write them out only if the address was - taken, but that was wrong; if the variable was simply - referred to, it still needs to exist or else it will - be undefined in the linker.) */ - if (! TREE_READONLY (decl) - || TREE_PUBLIC (decl) - || TREE_USED (decl) - || TREE_ADDRESSABLE (decl) - || TREE_ADDRESSABLE (DECL_ASSEMBLER_NAME (decl))) - rest_of_decl_compilation (decl, NULL_PTR, 1, 1); - else - /* Cancel the RTL for this decl so that, if debugging info - output for global variables is still to come, - this one will be omitted. */ - DECL_RTL (decl) = NULL; - } - - if (TREE_CODE (decl) == FUNCTION_DECL - && ! TREE_ASM_WRITTEN (decl) - && DECL_INITIAL (decl) != 0 - && (TREE_ADDRESSABLE (decl) - || TREE_ADDRESSABLE (DECL_ASSEMBLER_NAME (decl))) - && ! DECL_EXTERNAL (decl)) - output_inline_function (decl); - - /* Warn about any function - declared static but not defined. - We don't warn about variables, - because many programs have static variables - that exist only to get some text into the object file. */ - if ((warn_unused - || TREE_USED (decl) - || (DECL_NAME (decl) && TREE_USED (DECL_NAME (decl)))) - && TREE_CODE (decl) == FUNCTION_DECL - && DECL_INITIAL (decl) == 0 - && DECL_EXTERNAL (decl) - && ! TREE_PUBLIC (decl)) - { - pedwarn_with_decl (decl, - "`%s' declared `static' but never defined"); - /* This symbol is effectively an "extern" declaration now. */ - TREE_PUBLIC (decl) = 1; - assemble_external (decl); - - } - /* Warn about static fns or vars defined but not used, - but not about inline functions - since unused inline statics is normal practice. */ - if (warn_unused - && (TREE_CODE (decl) == FUNCTION_DECL - || TREE_CODE (decl) == VAR_DECL) - && ! DECL_IN_SYSTEM_HEADER (decl) - && ! DECL_EXTERNAL (decl) - && ! TREE_PUBLIC (decl) - && ! TREE_USED (decl) - && ! DECL_INLINE (decl) - && ! DECL_REGISTER (decl) - /* The TREE_USED bit for file-scope decls - is kept in the identifier, to handle multiple - external decls in different scopes. */ - && ! TREE_USED (DECL_NAME (decl))) - warning_with_decl (decl, "`%s' defined but not used"); - -#ifdef SDB_DEBUGGING_INFO - /* The COFF linker can move initialized global vars to the end. - And that can screw up the symbol ordering. - By putting the symbols in that order to begin with, - we avoid a problem. mcsun!unido!fauern!tumuc!pes@uunet.uu.net. */ - if (write_symbols == SDB_DEBUG && TREE_CODE (decl) == VAR_DECL - && TREE_PUBLIC (decl) && DECL_INITIAL (decl) - && DECL_RTL (decl) != 0) - TIMEVAR (symout_time, sdbout_symbol (decl, 0)); - - /* Output COFF information for non-global - file-scope initialized variables. */ - if (write_symbols == SDB_DEBUG - && TREE_CODE (decl) == VAR_DECL - && DECL_INITIAL (decl) - && DECL_RTL (decl) != 0 - && GET_CODE (DECL_RTL (decl)) == MEM) - TIMEVAR (symout_time, sdbout_toplevel_data (decl)); -#endif /* SDB_DEBUGGING_INFO */ -#ifdef DWARF_DEBUGGING_INFO - /* Output DWARF information for file-scope tentative data object - declarations, file-scope (extern) function declarations (which - had no corresponding body) and file-scope tagged type declarations - and definitions which have not yet been forced out. */ - - if (write_symbols == DWARF_DEBUG - && (TREE_CODE (decl) != FUNCTION_DECL || !DECL_INITIAL (decl))) - TIMEVAR (symout_time, dwarfout_file_scope_decl (decl, 1)); -#endif - } - } - - /* Do dbx symbols */ -#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) - if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) - TIMEVAR (symout_time, - { - dbxout_finish (asm_out_file, main_input_filename); - }); -#endif - -#ifdef DWARF_DEBUGGING_INFO - if (write_symbols == DWARF_DEBUG) - TIMEVAR (symout_time, - { - dwarfout_finish (); - }); -#endif - - /* Output some stuff at end of file if nec. */ - - end_final (main_input_filename); - -#ifdef ASM_FILE_END - ASM_FILE_END (asm_out_file); -#endif - - after_finish_compilation: - - /* Language-specific end of compilation actions. */ - - lang_finish (); - - /* Close the dump files. */ - - if (flag_gen_aux_info) - { - fclose (aux_info_file); - if (errorcount) - unlink (aux_info_file_name); - } - - if (rtl_dump) - fclose (rtl_dump_file); - - if (jump_opt_dump) - fclose (jump_opt_dump_file); - - if (cse_dump) - fclose (cse_dump_file); - - if (loop_dump) - fclose (loop_dump_file); - - if (cse2_dump) - fclose (cse2_dump_file); - - if (flow_dump) - fclose (flow_dump_file); - - if (combine_dump) - { - dump_combine_total_stats (combine_dump_file); - fclose (combine_dump_file); - } - - if (sched_dump) - fclose (sched_dump_file); - - if (local_reg_dump) - fclose (local_reg_dump_file); - - if (global_reg_dump) - fclose (global_reg_dump_file); - - if (sched2_dump) - fclose (sched2_dump_file); - - if (jump2_opt_dump) - fclose (jump2_opt_dump_file); - - if (dbr_sched_dump) - fclose (dbr_sched_dump_file); - -#ifdef STACK_REGS - if (stack_reg_dump) - fclose (stack_reg_dump_file); -#endif - - /* Close non-debugging input and output files. Take special care to note - whether fclose returns an error, since the pages might still be on the - buffer chain while the file is open. */ - - fclose (finput); - if (ferror (asm_out_file) != 0 || fclose (asm_out_file) != 0) - fatal_io_error (asm_file_name); - - /* Print the times. */ - - if (! quiet_flag) - { - fprintf (stderr,"\n"); - print_time ("parse", parse_time); - print_time ("integration", integration_time); - print_time ("jump", jump_time); - print_time ("cse", cse_time); - print_time ("loop", loop_time); - print_time ("cse2", cse2_time); - print_time ("flow", flow_time); - print_time ("combine", combine_time); - print_time ("sched", sched_time); - print_time ("local-alloc", local_alloc_time); - print_time ("global-alloc", global_alloc_time); - print_time ("sched2", sched2_time); - print_time ("dbranch", dbr_sched_time); - print_time ("shorten-branch", shorten_branch_time); - print_time ("stack-reg", stack_reg_time); - print_time ("final", final_time); - print_time ("varconst", varconst_time); - print_time ("symout", symout_time); - print_time ("dump", dump_time); - } -} - -/* This is called from various places for FUNCTION_DECL, VAR_DECL, - and TYPE_DECL nodes. - - This does nothing for local (non-static) variables. - Otherwise, it sets up the RTL and outputs any assembler code - (label definition, storage allocation and initialization). - - DECL is the declaration. If ASMSPEC is nonzero, it specifies - the assembler symbol name to be used. TOP_LEVEL is nonzero - if this declaration is not within a function. */ - -void -rest_of_decl_compilation (decl, asmspec, top_level, at_end) - tree decl; - char *asmspec; - int top_level; - int at_end; -{ - /* Declarations of variables, and of functions defined elsewhere. */ - - /* Forward declarations for nested functions are not "external", - but we need to treat them as if they were. */ - if (TREE_STATIC (decl) || DECL_EXTERNAL (decl) - || TREE_CODE (decl) == FUNCTION_DECL) - TIMEVAR (varconst_time, - { - make_decl_rtl (decl, asmspec, top_level); - /* For a user-invisible decl that should be replaced - by its value when used, don't output anything. */ - if (! (TREE_CODE (decl) == VAR_DECL - && DECL_IGNORED_P (decl) && TREE_READONLY (decl) - && DECL_INITIAL (decl) != 0)) - /* Don't output anything - when a tentative file-scope definition is seen. - But at end of compilation, do output code for them. */ - if (! (! at_end && top_level - && (DECL_INITIAL (decl) == 0 - || DECL_INITIAL (decl) == error_mark_node - || DECL_IGNORED_P (decl)))) - assemble_variable (decl, top_level, at_end); - }); - else if (DECL_REGISTER (decl) && asmspec != 0) - { - if (decode_reg_name (asmspec) >= 0) - { - DECL_RTL (decl) = 0; - make_decl_rtl (decl, asmspec, top_level); - } - else - error ("invalid register name `%s' for register variable", asmspec); - } -#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) - else if ((write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) - && TREE_CODE (decl) == TYPE_DECL) - TIMEVAR (symout_time, dbxout_symbol (decl, 0)); -#endif -#ifdef SDB_DEBUGGING_INFO - else if (write_symbols == SDB_DEBUG && top_level - && TREE_CODE (decl) == TYPE_DECL) - TIMEVAR (symout_time, sdbout_symbol (decl, 0)); -#endif -} - -/* Called after finishing a record, union or enumeral type. */ - -void -rest_of_type_compilation (type, toplev) - tree type; - int toplev; -{ -#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) - if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) - TIMEVAR (symout_time, dbxout_symbol (TYPE_STUB_DECL (type), !toplev)); -#endif -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG) - TIMEVAR (symout_time, sdbout_symbol (TYPE_STUB_DECL (type), !toplev)); -#endif -} - -/* This is called from finish_function (within yyparse) - after each top-level definition is parsed. - It is supposed to compile that function or variable - and output the assembler code for it. - After we return, the tree storage is freed. */ - -void -rest_of_compilation (decl) - tree decl; -{ - register rtx insns; - int start_time = get_run_time (); - int tem; - /* Nonzero if we have saved the original DECL_INITIAL of the function, - to be restored after we finish compiling the function - (for use when compiling inline calls to this function). */ - tree saved_block_tree = 0; - /* Likewise, for DECL_ARGUMENTS. */ - tree saved_arguments = 0; - int failure = 0; - - /* If we are reconsidering an inline function - at the end of compilation, skip the stuff for making it inline. */ - - if (DECL_SAVED_INSNS (decl) == 0) - { - int specd = DECL_INLINE (decl); - char *lose; - - /* If requested, consider whether to make this function inline. */ - if (specd || flag_inline_functions) - TIMEVAR (integration_time, - { - lose = function_cannot_inline_p (decl); - if (lose) - { - if (warn_inline && specd) - warning_with_decl (decl, lose); - DECL_INLINE (decl) = 0; - } - else - DECL_INLINE (decl) = 1; - }); - - insns = get_insns (); - - /* Dump the rtl code if we are dumping rtl. */ - - if (rtl_dump) - TIMEVAR (dump_time, - { - fprintf (rtl_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - if (DECL_SAVED_INSNS (decl)) - fprintf (rtl_dump_file, ";; (integrable)\n\n"); - print_rtl (rtl_dump_file, insns); - fflush (rtl_dump_file); - }); - - /* If function is inline, and we don't yet know whether to - compile it by itself, defer decision till end of compilation. - finish_compilation will call rest_of_compilation again - for those functions that need to be output. */ - - if (DECL_INLINE (decl) - && ((! TREE_PUBLIC (decl) && ! TREE_ADDRESSABLE (decl) - && ! flag_keep_inline_functions) - || DECL_EXTERNAL (decl))) - { -#ifdef DWARF_DEBUGGING_INFO - /* Generate the DWARF info for the "abstract" instance - of a function which we may later generate inlined and/or - out-of-line instances of. */ - if (write_symbols == DWARF_DEBUG) - { - set_decl_abstract_flags (decl, 1); - TIMEVAR (symout_time, dwarfout_file_scope_decl (decl, 0)); - set_decl_abstract_flags (decl, 0); - } -#endif - TIMEVAR (integration_time, save_for_inline_nocopy (decl)); - goto exit_rest_of_compilation; - } - - /* If we have to compile the function now, save its rtl and subdecls - so that its compilation will not affect what others get. */ - if (DECL_INLINE (decl)) - { -#ifdef DWARF_DEBUGGING_INFO - /* Generate the DWARF info for the "abstract" instance of - a function which we will generate an out-of-line instance - of almost immediately (and which we may also later generate - various inlined instances of). */ - if (write_symbols == DWARF_DEBUG) - { - set_decl_abstract_flags (decl, 1); - TIMEVAR (symout_time, dwarfout_file_scope_decl (decl, 0)); - set_decl_abstract_flags (decl, 0); - } -#endif - saved_block_tree = DECL_INITIAL (decl); - saved_arguments = DECL_ARGUMENTS (decl); - TIMEVAR (integration_time, save_for_inline_copying (decl)); - } - } - - TREE_ASM_WRITTEN (decl) = 1; - - /* Now that integrate will no longer see our rtl, we need not distinguish - between the return value of this function and the return value of called - functions. */ - rtx_equal_function_value_matters = 0; - - /* Don't return yet if -Wreturn-type; we need to do jump_optimize. */ - if ((rtl_dump_and_exit || flag_syntax_only) && !warn_return_type) - { - goto exit_rest_of_compilation; - } - - /* From now on, allocate rtl in current_obstack, not in saveable_obstack. - Note that that may have been done above, in save_for_inline_copying. - The call to resume_temporary_allocation near the end of this function - goes back to the usual state of affairs. */ - - rtl_in_current_obstack (); - -#ifdef FINALIZE_PIC - /* If we are doing position-independent code generation, now - is the time to output special prologues and epilogues. - We do not want to do this earlier, because it just clutters - up inline functions with meaningless insns. */ - if (flag_pic) - FINALIZE_PIC; -#endif - - insns = get_insns (); - - /* Copy any shared structure that should not be shared. */ - - unshare_all_rtl (insns); - - /* Instantiate all virtual registers. */ - - instantiate_virtual_regs (current_function_decl, get_insns ()); - - /* See if we have allocated stack slots that are not directly addressable. - If so, scan all the insns and create explicit address computation - for all references to such slots. */ -/* fixup_stack_slots (); */ - - /* Do jump optimization the first time, if -opt. - Also do it if -W, but in that case it doesn't change the rtl code, - it only computes whether control can drop off the end of the function. */ - - if (optimize > 0 || extra_warnings || warn_return_type - /* If function is `volatile', we should warn if it tries to return. */ - || TREE_THIS_VOLATILE (decl)) - { - TIMEVAR (jump_time, reg_scan (insns, max_reg_num (), 0)); - TIMEVAR (jump_time, jump_optimize (insns, 0, 0, 1)); - } - - /* Now is when we stop if -fsyntax-only and -Wreturn-type. */ - if (rtl_dump_and_exit || flag_syntax_only) - goto exit_rest_of_compilation; - - /* Dump rtl code after jump, if we are doing that. */ - - if (jump_opt_dump) - TIMEVAR (dump_time, - { - fprintf (jump_opt_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - print_rtl (jump_opt_dump_file, insns); - fflush (jump_opt_dump_file); - }); - - /* Perform common subexpression elimination. - Nonzero value from `cse_main' means that jumps were simplified - and some code may now be unreachable, so do - jump optimization again. */ - - if (cse_dump) - TIMEVAR (dump_time, - { - fprintf (cse_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - }); - - if (optimize > 0) - { - TIMEVAR (cse_time, reg_scan (insns, max_reg_num (), 1)); - - if (flag_thread_jumps) - /* Hacks by tiemann & kenner. */ - TIMEVAR (jump_time, thread_jumps (insns, max_reg_num (), 0)); - - TIMEVAR (cse_time, tem = cse_main (insns, max_reg_num (), - 0, cse_dump_file)); - TIMEVAR (cse_time, delete_dead_from_cse (insns, max_reg_num ())); - - if (tem) - TIMEVAR (jump_time, jump_optimize (insns, 0, 0, 0)); - } - - /* Dump rtl code after cse, if we are doing that. */ - - if (cse_dump) - TIMEVAR (dump_time, - { - print_rtl (cse_dump_file, insns); - fflush (cse_dump_file); - }); - - if (loop_dump) - TIMEVAR (dump_time, - { - fprintf (loop_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - }); - - /* Move constant computations out of loops. */ - - if (optimize > 0) - { - TIMEVAR (loop_time, - { - loop_optimize (insns, loop_dump_file); - }); - } - - /* Dump rtl code after loop opt, if we are doing that. */ - - if (loop_dump) - TIMEVAR (dump_time, - { - print_rtl (loop_dump_file, insns); - fflush (loop_dump_file); - }); - - if (cse2_dump) - TIMEVAR (dump_time, - { - fprintf (cse2_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - }); - - if (optimize > 0 && flag_rerun_cse_after_loop) - { - TIMEVAR (cse2_time, reg_scan (insns, max_reg_num (), 0)); - - TIMEVAR (cse2_time, tem = cse_main (insns, max_reg_num (), - 1, cse2_dump_file)); - if (tem) - TIMEVAR (jump_time, jump_optimize (insns, 0, 0, 0)); - } - - if (optimize > 0 && flag_thread_jumps) - /* This pass of jump threading straightens out code - that was kinked by loop optimization. */ - TIMEVAR (jump_time, thread_jumps (insns, max_reg_num (), 0)); - - /* Dump rtl code after cse, if we are doing that. */ - - if (cse2_dump) - TIMEVAR (dump_time, - { - print_rtl (cse2_dump_file, insns); - fflush (cse2_dump_file); - }); - - /* We are no longer anticipating cse in this function, at least. */ - - cse_not_expected = 1; - - /* Now we choose between stupid (pcc-like) register allocation - (if we got the -noreg switch and not -opt) - and smart register allocation. */ - - if (optimize > 0) /* Stupid allocation probably won't work */ - obey_regdecls = 0; /* if optimizations being done. */ - - regclass_init (); - - /* Print function header into flow dump now - because doing the flow analysis makes some of the dump. */ - - if (flow_dump) - TIMEVAR (dump_time, - { - fprintf (flow_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - }); - - if (obey_regdecls) - { - TIMEVAR (flow_time, - { - regclass (insns, max_reg_num ()); - stupid_life_analysis (insns, max_reg_num (), - flow_dump_file); - }); - } - else - { - /* Do control and data flow analysis, - and write some of the results to dump file. */ - - TIMEVAR (flow_time, flow_analysis (insns, max_reg_num (), - flow_dump_file)); - if (warn_uninitialized) - { - uninitialized_vars_warning (DECL_INITIAL (decl)); - setjmp_args_warning (); - } - } - - /* Dump rtl after flow analysis. */ - - if (flow_dump) - TIMEVAR (dump_time, - { - print_rtl (flow_dump_file, insns); - fflush (flow_dump_file); - }); - - /* If -opt, try combining insns through substitution. */ - - if (optimize > 0) - TIMEVAR (combine_time, combine_instructions (insns, max_reg_num ())); - - /* Dump rtl code after insn combination. */ - - if (combine_dump) - TIMEVAR (dump_time, - { - fprintf (combine_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - dump_combine_stats (combine_dump_file); - print_rtl (combine_dump_file, insns); - fflush (combine_dump_file); - }); - - /* Print function header into sched dump now - because doing the sched analysis makes some of the dump. */ - - if (sched_dump) - TIMEVAR (dump_time, - { - fprintf (sched_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - }); - - if (optimize > 0 && flag_schedule_insns) - { - /* Do control and data sched analysis, - and write some of the results to dump file. */ - - TIMEVAR (sched_time, schedule_insns (sched_dump_file)); - } - - /* Dump rtl after instruction scheduling. */ - - if (sched_dump) - TIMEVAR (dump_time, - { - print_rtl (sched_dump_file, insns); - fflush (sched_dump_file); - }); - - /* Unless we did stupid register allocation, - allocate pseudo-regs that are used only within 1 basic block. */ - - if (!obey_regdecls) - TIMEVAR (local_alloc_time, - { - regclass (insns, max_reg_num ()); - local_alloc (); - }); - - /* Dump rtl code after allocating regs within basic blocks. */ - - if (local_reg_dump) - TIMEVAR (dump_time, - { - fprintf (local_reg_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - dump_flow_info (local_reg_dump_file); - dump_local_alloc (local_reg_dump_file); - print_rtl (local_reg_dump_file, insns); - fflush (local_reg_dump_file); - }); - - if (global_reg_dump) - TIMEVAR (dump_time, - fprintf (global_reg_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl)))); - - /* Unless we did stupid register allocation, - allocate remaining pseudo-regs, then do the reload pass - fixing up any insns that are invalid. */ - - TIMEVAR (global_alloc_time, - { - if (!obey_regdecls) - failure = global_alloc (global_reg_dump_file); - else - failure = reload (insns, 0, global_reg_dump_file); - }); - - if (global_reg_dump) - TIMEVAR (dump_time, - { - dump_global_regs (global_reg_dump_file); - print_rtl (global_reg_dump_file, insns); - fflush (global_reg_dump_file); - }); - - if (failure) - goto exit_rest_of_compilation; - - reload_completed = 1; - - /* On some machines, the prologue and epilogue code, or parts thereof, - can be represented as RTL. Doing so lets us schedule insns between - it and the rest of the code and also allows delayed branch - scheduling to operate in the epilogue. */ - - thread_prologue_and_epilogue_insns (insns); - - if (optimize > 0 && flag_schedule_insns_after_reload) - { - if (sched2_dump) - TIMEVAR (dump_time, - { - fprintf (sched2_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - }); - - /* Do control and data sched analysis again, - and write some more of the results to dump file. */ - - TIMEVAR (sched2_time, schedule_insns (sched2_dump_file)); - - /* Dump rtl after post-reorder instruction scheduling. */ - - if (sched2_dump) - TIMEVAR (dump_time, - { - print_rtl (sched2_dump_file, insns); - fflush (sched2_dump_file); - }); - } - -#ifdef LEAF_REGISTERS - leaf_function = 0; - if (optimize > 0 && only_leaf_regs_used () && leaf_function_p ()) - leaf_function = 1; -#endif - - /* One more attempt to remove jumps to .+1 - left by dead-store-elimination. - Also do cross-jumping this time - and delete no-op move insns. */ - - if (optimize > 0) - { - TIMEVAR (jump_time, jump_optimize (insns, 1, 1, 0)); - } - - /* Dump rtl code after jump, if we are doing that. */ - - if (jump2_opt_dump) - TIMEVAR (dump_time, - { - fprintf (jump2_opt_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - print_rtl (jump2_opt_dump_file, insns); - fflush (jump2_opt_dump_file); - }); - - /* If a scheduling pass for delayed branches is to be done, - call the scheduling code. */ - -#ifdef DELAY_SLOTS - if (optimize > 0 && flag_delayed_branch) - { - TIMEVAR (dbr_sched_time, dbr_schedule (insns, dbr_sched_dump_file)); - if (dbr_sched_dump) - { - TIMEVAR (dump_time, - { - fprintf (dbr_sched_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - print_rtl (dbr_sched_dump_file, insns); - fflush (dbr_sched_dump_file); - }); - } - } -#endif - - if (optimize > 0) - /* Shorten branches. */ - TIMEVAR (shorten_branch_time, - { - shorten_branches (get_insns ()); - }); - -#ifdef STACK_REGS - TIMEVAR (stack_reg_time, reg_to_stack (insns, stack_reg_dump_file)); - if (stack_reg_dump) - { - TIMEVAR (dump_time, - { - fprintf (stack_reg_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - print_rtl (stack_reg_dump_file, insns); - fflush (stack_reg_dump_file); - }); - } -#endif - - /* Now turn the rtl into assembler code. */ - - TIMEVAR (final_time, - { - rtx x; - char *fnname; - - /* Get the function's name, as described by its RTL. - This may be different from the DECL_NAME name used - in the source file. */ - - x = DECL_RTL (decl); - if (GET_CODE (x) != MEM) - abort (); - x = XEXP (x, 0); - if (GET_CODE (x) != SYMBOL_REF) - abort (); - fnname = XSTR (x, 0); - - assemble_start_function (decl, fnname); - final_start_function (insns, asm_out_file, optimize); - final (insns, asm_out_file, optimize, 0); - final_end_function (insns, asm_out_file, optimize); - assemble_end_function (decl, fnname); - fflush (asm_out_file); - }); - - /* Write DBX symbols if requested */ - - /* Note that for those inline functions where we don't initially - know for certain that we will be generating an out-of-line copy, - the first invocation of this routine (rest_of_compilation) will - skip over this code by doing a `goto exit_rest_of_compilation;'. - Later on, finish_compilation will call rest_of_compilation again - for those inline functions that need to have out-of-line copies - generated. During that call, we *will* be routed past here. */ - -#ifdef DBX_DEBUGGING_INFO - if (write_symbols == DBX_DEBUG) - TIMEVAR (symout_time, dbxout_function (decl)); -#endif - -#ifdef DWARF_DEBUGGING_INFO - if (write_symbols == DWARF_DEBUG) - TIMEVAR (symout_time, dwarfout_file_scope_decl (decl, 0)); -#endif - - exit_rest_of_compilation: - - /* In case the function was not output, - don't leave any temporary anonymous types - queued up for sdb output. */ -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG) - sdbout_types (NULL_TREE); -#endif - - /* Put back the tree of subblocks and list of arguments - from before we copied them. - Code generation and the output of debugging info may have modified - the copy, but the original is unchanged. */ - - if (saved_block_tree != 0) - DECL_INITIAL (decl) = saved_block_tree; - if (saved_arguments != 0) - DECL_ARGUMENTS (decl) = saved_arguments; - - reload_completed = 0; - - /* Clear out the real_constant_chain before some of the rtx's - it runs through become garbage. */ - - clear_const_double_mem (); - - /* Cancel the effect of rtl_in_current_obstack. */ - - resume_temporary_allocation (); - - /* The parsing time is all the time spent in yyparse - *except* what is spent in this function. */ - - parse_time -= get_run_time () - start_time; -} - -/* Entry point of cc1/c++. Decode command args, then call compile_file. - Exit code is 35 if can't open files, 34 if fatal error, - 33 if had nonfatal errors, else success. */ - -int -main (argc, argv, envp) - int argc; - char **argv; - char **envp; -{ - register int i; - char *filename = 0; - int flag_print_mem = 0; - int version_flag = 0; - char *p; - - /* save in case md file wants to emit args as a comment. */ - save_argc = argc; - save_argv = argv; - - p = argv[0] + strlen (argv[0]); - while (p != argv[0] && p[-1] != '/') --p; - progname = p; - -#ifdef RLIMIT_STACK - /* Get rid of any avoidable limit on stack size. */ - { - struct rlimit rlim; - - /* Set the stack limit huge so that alloca does not fail. */ - getrlimit (RLIMIT_STACK, &rlim); - rlim.rlim_cur = rlim.rlim_max; - setrlimit (RLIMIT_STACK, &rlim); - } -#endif /* RLIMIT_STACK */ - - signal (SIGFPE, float_signal); - -#ifdef SIGPIPE - signal (SIGPIPE, pipe_closed); -#endif - - decl_printable_name = decl_name; - lang_expand_expr = (struct rtx_def *(*)()) do_abort; - - /* Initialize whether `char' is signed. */ - flag_signed_char = DEFAULT_SIGNED_CHAR; -#ifdef DEFAULT_SHORT_ENUMS - /* Initialize how much space enums occupy, by default. */ - flag_short_enums = DEFAULT_SHORT_ENUMS; -#endif - - /* Scan to see what optimization level has been specified. That will - determine the default value of many flags. */ - for (i = 1; i < argc; i++) - { - if (!strcmp (argv[i], "-O")) - { - optimize = 1; - } - else if (argv[i][0] == '-' && argv[i][1] == 'O') - { - /* Handle -O2, -O3, -O69, ... */ - char *p = &argv[i][2]; - int c; - - while (c = *p++) - if (! (c >= '0' && c <= '9')) - break; - if (c == 0) - optimize = atoi (&argv[i][2]); - } - } - - obey_regdecls = (optimize == 0); - if (optimize == 0) - { - flag_no_inline = 1; - warn_inline = 0; - } - - if (optimize >= 1) - { - flag_defer_pop = 1; - flag_thread_jumps = 1; -#ifdef DELAY_SLOTS - flag_delayed_branch = 1; -#endif - } - - if (optimize >= 2) - { - flag_cse_follow_jumps = 1; - flag_cse_skip_blocks = 1; - flag_expensive_optimizations = 1; - flag_strength_reduce = 1; - flag_rerun_cse_after_loop = 1; - flag_caller_saves = 1; -#ifdef INSN_SCHEDULING - flag_schedule_insns = 1; - flag_schedule_insns_after_reload = 1; -#endif - } - -#ifdef OPTIMIZATION_OPTIONS - /* Allow default optimizations to be specified on a per-machine basis. */ - OPTIMIZATION_OPTIONS (optimize); -#endif - - /* Initialize register usage now so switches may override. */ - init_reg_sets (); - - target_flags = 0; - set_target_switch (""); - - for (i = 1; i < argc; i++) - { - int j; - /* If this is a language-specific option, - decode it in a language-specific way. */ - for (j = 0; lang_options[j] != 0; j++) - if (!strncmp (argv[i], lang_options[j], - strlen (lang_options[j]))) - break; - if (lang_options[j] != 0) - /* If the option is valid for *some* language, - treat it as valid even if this language doesn't understand it. */ - lang_decode_option (argv[i]); - else if (argv[i][0] == '-' && argv[i][1] != 0) - { - register char *str = argv[i] + 1; - if (str[0] == 'Y') - str++; - - if (str[0] == 'm') - set_target_switch (&str[1]); - else if (!strcmp (str, "dumpbase")) - { - dump_base_name = argv[++i]; - } - else if (str[0] == 'd') - { - register char *p = &str[1]; - while (*p) - switch (*p++) - { - case 'a': - combine_dump = 1; - dbr_sched_dump = 1; - flow_dump = 1; - global_reg_dump = 1; - jump_opt_dump = 1; - jump2_opt_dump = 1; - local_reg_dump = 1; - loop_dump = 1; - rtl_dump = 1; - cse_dump = 1, cse2_dump = 1; - sched_dump = 1; - sched2_dump = 1; - stack_reg_dump = 1; - break; - case 'k': - stack_reg_dump = 1; - break; - case 'c': - combine_dump = 1; - break; - case 'd': - dbr_sched_dump = 1; - break; - case 'f': - flow_dump = 1; - break; - case 'g': - global_reg_dump = 1; - break; - case 'j': - jump_opt_dump = 1; - break; - case 'J': - jump2_opt_dump = 1; - break; - case 'l': - local_reg_dump = 1; - break; - case 'L': - loop_dump = 1; - break; - case 'm': - flag_print_mem = 1; - break; - case 'p': - flag_print_asm_name = 1; - break; - case 'r': - rtl_dump = 1; - break; - case 's': - cse_dump = 1; - break; - case 't': - cse2_dump = 1; - break; - case 'S': - sched_dump = 1; - break; - case 'R': - sched2_dump = 1; - break; - case 'y': - set_yydebug (1); - break; - - case 'x': - rtl_dump_and_exit = 1; - break; - } - } - else if (str[0] == 'f') - { - register char *p = &str[1]; - int found = 0; - - /* Some kind of -f option. - P's value is the option sans `-f'. - Search for it in the table of options. */ - - for (j = 0; - !found && j < sizeof (f_options) / sizeof (f_options[0]); - j++) - { - if (!strcmp (p, f_options[j].string)) - { - *f_options[j].variable = f_options[j].on_value; - /* A goto here would be cleaner, - but breaks the vax pcc. */ - found = 1; - } - if (p[0] == 'n' && p[1] == 'o' && p[2] == '-' - && ! strcmp (p+3, f_options[j].string)) - { - *f_options[j].variable = ! f_options[j].on_value; - found = 1; - } - } - - if (found) - ; - else if (!strncmp (p, "fixed-", 6)) - fix_register (&p[6], 1, 1); - else if (!strncmp (p, "call-used-", 10)) - fix_register (&p[10], 0, 1); - else if (!strncmp (p, "call-saved-", 11)) - fix_register (&p[11], 0, 0); - else - error ("Invalid option `%s'", argv[i]); - } - else if (str[0] == 'O') - { - register char *p = str+1; - while (*p && *p >= '0' && *p <= '9') - p++; - if (*p == '\0') - ; - else - error ("Invalid option `%s'", argv[i]); - } - else if (!strcmp (str, "pedantic")) - pedantic = 1; - else if (!strcmp (str, "pedantic-errors")) - flag_pedantic_errors = pedantic = 1; - else if (!strcmp (str, "quiet")) - quiet_flag = 1; - else if (!strcmp (str, "version")) - version_flag = 1; - else if (!strcmp (str, "w")) - inhibit_warnings = 1; - else if (!strcmp (str, "W")) - { - extra_warnings = 1; - warn_uninitialized = 1; - } - else if (str[0] == 'W') - { - register char *p = &str[1]; - int found = 0; - - /* Some kind of -W option. - P's value is the option sans `-W'. - Search for it in the table of options. */ - - for (j = 0; - !found && j < sizeof (W_options) / sizeof (W_options[0]); - j++) - { - if (!strcmp (p, W_options[j].string)) - { - *W_options[j].variable = W_options[j].on_value; - /* A goto here would be cleaner, - but breaks the vax pcc. */ - found = 1; - } - if (p[0] == 'n' && p[1] == 'o' && p[2] == '-' - && ! strcmp (p+3, W_options[j].string)) - { - *W_options[j].variable = ! W_options[j].on_value; - found = 1; - } - } - - if (found) - ; - else if (!strncmp (p, "id-clash-", 9)) - { - char *endp = p + 9; - - while (*endp) - { - if (*endp >= '0' && *endp <= '9') - endp++; - else - { - error ("Invalid option `%s'", argv[i]); - goto id_clash_lose; - } - } - warn_id_clash = 1; - id_clash_len = atoi (str + 10); - id_clash_lose: ; - } - else - error ("Invalid option `%s'", argv[i]); - } - else if (!strcmp (str, "p")) - profile_flag = 1; - else if (!strcmp (str, "a")) - { -#if !defined (BLOCK_PROFILER) || !defined (FUNCTION_BLOCK_PROFILER) - warning ("`-a' option (basic block profile) not supported"); -#else - profile_block_flag = 1; -#endif - } - else if (str[0] == 'g') - { - char *p = str + 1; - char *q; - unsigned len; - unsigned level; - - while (*p && (*p < '0' || *p > '9')) - p++; - len = p - str; - q = p; - while (*q && (*q >= '0' && *q <= '9')) - q++; - if (*p) - level = atoi (p); - else - level = 2; /* default debugging info level */ - if (*q || level > 3) - { - warning ("invalid debug level specification in option: `-%s'", - str); - warning ("no debugging information will be generated"); - level = 0; - } - - /* If more than one debugging type is supported, - you must define PREFERRED_DEBUGGING_TYPE - to choose a format in a system-dependent way. */ - /* This is one long line cause VAXC can't handle a \-newline. */ -#if 1 < (defined (DBX_DEBUGGING_INFO) + defined (SDB_DEBUGGING_INFO) + defined (DWARF_DEBUGGING_INFO) + defined (XCOFF_DEBUGGING_INFO)) -#ifdef PREFERRED_DEBUGGING_TYPE - if (!strncmp (str, "ggdb", len)) - write_symbols = PREFERRED_DEBUGGING_TYPE; -#else /* no PREFERRED_DEBUGGING_TYPE */ -You Lose! You must define PREFERRED_DEBUGGING_TYPE! -#endif /* no PREFERRED_DEBUGGING_TYPE */ -#endif /* More than one debugger format enabled. */ -#ifdef DBX_DEBUGGING_INFO - if (write_symbols != NO_DEBUG) - ; - else if (!strncmp (str, "ggdb", len)) - write_symbols = DBX_DEBUG; - else if (!strncmp (str, "gstabs", len)) - write_symbols = DBX_DEBUG; - else if (!strncmp (str, "gstabs+", len)) - write_symbols = DBX_DEBUG; - - /* Always enable extensions for -ggdb or -gstabs+, - always disable for -gstabs. - For plain -g, use system-specific default. */ - if (write_symbols == DBX_DEBUG && !strncmp (str, "ggdb", len) - && len >= 2) - use_gnu_debug_info_extensions = 1; - else if (write_symbols == DBX_DEBUG && !strncmp (str, "gstabs+", len) - && len >= 7) - use_gnu_debug_info_extensions = 1; - else if (write_symbols == DBX_DEBUG - && !strncmp (str, "gstabs", len) && len >= 2) - use_gnu_debug_info_extensions = 0; - else - use_gnu_debug_info_extensions = DEFAULT_GDB_EXTENSIONS; -#endif /* DBX_DEBUGGING_INFO */ -#ifdef DWARF_DEBUGGING_INFO - if (write_symbols != NO_DEBUG) - ; - else if (!strncmp (str, "g", len)) - write_symbols = DWARF_DEBUG; - else if (!strncmp (str, "ggdb", len)) - write_symbols = DWARF_DEBUG; - else if (!strncmp (str, "gdwarf", len)) - write_symbols = DWARF_DEBUG; - - /* Always enable extensions for -ggdb or -gdwarf+, - always disable for -gdwarf. - For plain -g, use system-specific default. */ - if (write_symbols == DWARF_DEBUG && !strncmp (str, "ggdb", len) - && len >= 2) - use_gnu_debug_info_extensions = 1; - else if (write_symbols == DWARF_DEBUG && !strcmp (str, "gdwarf+")) - use_gnu_debug_info_extensions = 1; - else if (write_symbols == DWARF_DEBUG - && !strncmp (str, "gdwarf", len) && len >= 2) - use_gnu_debug_info_extensions = 0; - else - use_gnu_debug_info_extensions = DEFAULT_GDB_EXTENSIONS; -#endif -#ifdef SDB_DEBUGGING_INFO - if (write_symbols != NO_DEBUG) - ; - else if (!strncmp (str, "g", len)) - write_symbols = SDB_DEBUG; - else if (!strncmp (str, "gdb", len)) - write_symbols = SDB_DEBUG; - else if (!strncmp (str, "gcoff", len)) - write_symbols = SDB_DEBUG; -#endif /* SDB_DEBUGGING_INFO */ -#ifdef XCOFF_DEBUGGING_INFO - if (write_symbols != NO_DEBUG) - ; - else if (!strncmp (str, "g", len)) - write_symbols = XCOFF_DEBUG; - else if (!strncmp (str, "ggdb", len)) - write_symbols = XCOFF_DEBUG; - else if (!strncmp (str, "gxcoff", len)) - write_symbols = XCOFF_DEBUG; - - /* Always enable extensions for -ggdb or -gxcoff+, - always disable for -gxcoff. - For plain -g, use system-specific default. */ - if (write_symbols == XCOFF_DEBUG && !strncmp (str, "ggdb", len) - && len >= 2) - use_gnu_debug_info_extensions = 1; - else if (write_symbols == XCOFF_DEBUG && !strcmp (str, "gxcoff+")) - use_gnu_debug_info_extensions = 1; - else if (write_symbols == XCOFF_DEBUG - && !strncmp (str, "gxcoff", len) && len >= 2) - use_gnu_debug_info_extensions = 0; - else - use_gnu_debug_info_extensions = DEFAULT_GDB_EXTENSIONS; -#endif - if (write_symbols == NO_DEBUG) - warning ("`-%s' option not supported on this version of GCC", str); - else if (level == 0) - write_symbols = NO_DEBUG; - else - debug_info_level = (enum debug_info_level) level; - } - else if (!strcmp (str, "o")) - { - asm_file_name = argv[++i]; - } - else if (str[0] == 'G') - { - g_switch_set = TRUE; - g_switch_value = atoi ((str[1] != '\0') ? str+1 : argv[++i]); - } - else if (!strncmp (str, "aux-info", 8)) - { - flag_gen_aux_info = 1; - aux_info_file_name = (str[8] != '\0' ? str+8 : argv[++i]); - } - else - error ("Invalid option `%s'", argv[i]); - } - else if (argv[i][0] == '+') - error ("Invalid option `%s'", argv[i]); - else - filename = argv[i]; - } - - if (optimize == 0) - { - /* Inlining does not work if not optimizing, - so force it not to be done. */ - flag_no_inline = 1; - warn_inline = 0; - - /* The c_decode_option and lang_decode_option functions set - this to `2' if -Wall is used, so we can avoid giving out - lots of errors for people who don't realize what -Wall does. */ - if (warn_uninitialized == 1) - warning ("-Wuninitialized is not supported without -O"); - } - -#if defined(DWARF_DEBUGGING_INFO) - if (write_symbols == DWARF_DEBUG - && strcmp (language_string, "GNU C++") == 0) - { - warning ("-g option not supported for C++ on SVR4 systems"); - write_symbols = NO_DEBUG; - } -#endif /* defined(DWARF_DEBUGGING_INFO) */ - -#ifdef OVERRIDE_OPTIONS - /* Some machines may reject certain combinations of options. */ - OVERRIDE_OPTIONS; -#endif - - /* Unrolling all loops implies that standard loop unrolling must also - be done. */ - if (flag_unroll_all_loops) - flag_unroll_loops = 1; - /* Loop unrolling requires that strength_reduction be on also. Silently - turn on strength reduction here if it isn't already on. Also, the loop - unrolling code assumes that cse will be run after loop, so that must - be turned on also. */ - if (flag_unroll_loops) - { - flag_strength_reduce = 1; - flag_rerun_cse_after_loop = 1; - } - - /* Warn about options that are not supported on this machine. */ -#ifndef INSN_SCHEDULING - if (flag_schedule_insns || flag_schedule_insns_after_reload) - warning ("instruction scheduling not supported on this target machine"); -#endif -#ifndef DELAY_SLOTS - if (flag_delayed_branch) - warning ("this target machine does not have delayed branches"); -#endif - - /* If we are in verbose mode, write out the version and maybe all the - option flags in use. */ - if (version_flag) - { - fprintf (stderr, "%s version %s", language_string, version_string); -#ifdef TARGET_VERSION - TARGET_VERSION; -#endif -#ifdef __GNUC__ -#ifndef __VERSION__ -#define __VERSION__ "[unknown]" -#endif - fprintf (stderr, " compiled by GNU C version %s.\n", __VERSION__); -#else - fprintf (stderr, " compiled by CC.\n"); -#endif - if (! quiet_flag) - print_switch_values (); - } - - /* Now that register usage is specified, convert it to HARD_REG_SETs. */ - init_reg_sets_1 (); - - compile_file (filename); - -#ifndef OS2 -#ifndef VMS - if (flag_print_mem) - { - char *lim = (char *) sbrk (0); - - fprintf (stderr, "Data size %d.\n", - lim - (char *) &environ); - fflush (stderr); - -#ifdef USG - system ("ps -l 1>&2"); -#else /* not USG */ - system ("ps v"); -#endif /* not USG */ - } -#endif /* not VMS */ -#endif /* not OS2 */ - - if (errorcount) - exit (FATAL_EXIT_CODE); - if (sorrycount) - exit (FATAL_EXIT_CODE); - exit (SUCCESS_EXIT_CODE); - return 34; -} - -/* Decode -m switches. */ - -/* Here is a table, controlled by the tm.h file, listing each -m switch - and which bits in `target_switches' it should set or clear. - If VALUE is positive, it is bits to set. - If VALUE is negative, -VALUE is bits to clear. - (The sign bit is not used so there is no confusion.) */ - -struct {char *name; int value;} target_switches [] - = TARGET_SWITCHES; - -/* This table is similar, but allows the switch to have a value. */ - -#ifdef TARGET_OPTIONS -struct {char *prefix; char ** variable;} target_options [] - = TARGET_OPTIONS; -#endif - -/* Decode the switch -mNAME. */ - -void -set_target_switch (name) - char *name; -{ - register int j; - int valid = 0; - - for (j = 0; j < sizeof target_switches / sizeof target_switches[0]; j++) - if (!strcmp (target_switches[j].name, name)) - { - if (target_switches[j].value < 0) - target_flags &= ~-target_switches[j].value; - else - target_flags |= target_switches[j].value; - valid = 1; - } - -#ifdef TARGET_OPTIONS - if (!valid) - for (j = 0; j < sizeof target_options / sizeof target_options[0]; j++) - { - int len = strlen (target_options[j].prefix); - if (!strncmp (target_options[j].prefix, name, len)) - { - *target_options[j].variable = name + len; - valid = 1; - } - } -#endif - - if (!valid) - error ("Invalid option `%s'", name); -} - -/* Variable used for communication between the following two routines. */ - -static int line_position; - -/* Print an option value and adjust the position in the line. */ - -static void -print_single_switch (type, name) - char *type, *name; -{ - fprintf (stderr, " %s%s", type, name); - - line_position += strlen (type) + strlen (name) + 1; - - if (line_position > 65) - { - fprintf (stderr, "\n\t"); - line_position = 8; - } -} - -/* Print default target switches for -version. */ - -static void -print_switch_values () -{ - register int j; - - fprintf (stderr, "enabled:"); - line_position = 8; - - for (j = 0; j < sizeof f_options / sizeof f_options[0]; j++) - if (*f_options[j].variable == f_options[j].on_value) - print_single_switch ("-f", f_options[j].string); - - for (j = 0; j < sizeof W_options / sizeof W_options[0]; j++) - if (*W_options[j].variable == W_options[j].on_value) - print_single_switch ("-W", W_options[j].string); - - for (j = 0; j < sizeof target_switches / sizeof target_switches[0]; j++) - if (target_switches[j].name[0] != '\0' - && target_switches[j].value > 0 - && ((target_switches[j].value & target_flags) - == target_switches[j].value)) - print_single_switch ("-m", target_switches[j].name); - - fprintf (stderr, "\n"); -} diff --git a/gnu/usr.bin/cc/common/unroll.c b/gnu/usr.bin/cc/common/unroll.c deleted file mode 100644 index 71c26a0935..0000000000 --- a/gnu/usr.bin/cc/common/unroll.c +++ /dev/null @@ -1,3223 +0,0 @@ -/* Try to unroll loops, and split induction variables. - Copyright (C) 1992 Free Software Foundation, Inc. - Contributed by James E. Wilson, Cygnus Support/UC Berkeley. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Try to unroll a loop, and split induction variables. - - Loops for which the number of iterations can be calculated exactly are - handled specially. If the number of iterations times the insn_count is - less than MAX_UNROLLED_INSNS, then the loop is unrolled completely. - Otherwise, we try to unroll the loop a number of times modulo the number - of iterations, so that only one exit test will be needed. It is unrolled - a number of times approximately equal to MAX_UNROLLED_INSNS divided by - the insn count. - - Otherwise, if the number of iterations can be calculated exactly at - run time, and the loop is always entered at the top, then we try to - precondition the loop. That is, at run time, calculate how many times - the loop will execute, and then execute the loop body a few times so - that the remaining iterations will be some multiple of 4 (or 2 if the - loop is large). Then fall through to a loop unrolled 4 (or 2) times, - with only one exit test needed at the end of the loop. - - Otherwise, if the number of iterations can not be calculated exactly, - not even at run time, then we still unroll the loop a number of times - approximately equal to MAX_UNROLLED_INSNS divided by the insn count, - but there must be an exit test after each copy of the loop body. - - For each induction variable, which is dead outside the loop (replaceable) - or for which we can easily calculate the final value, if we can easily - calculate its value at each place where it is set as a function of the - current loop unroll count and the variable's value at loop entry, then - the induction variable is split into `N' different variables, one for - each copy of the loop body. One variable is live across the backward - branch, and the others are all calculated as a function of this variable. - This helps eliminate data dependencies, and leads to further opportunities - for cse. */ - -/* Possible improvements follow: */ - -/* ??? Add an extra pass somewhere to determine whether unrolling will - give any benefit. E.g. after generating all unrolled insns, compute the - cost of all insns and compare against cost of insns in rolled loop. - - - On traditional architectures, unrolling a non-constant bound loop - is a win if there is a giv whose only use is in memory addresses, the - memory addresses can be split, and hence giv increments can be - eliminated. - - It is also a win if the loop is executed many times, and preconditioning - can be performed for the loop. - Add code to check for these and similar cases. */ - -/* ??? Improve control of which loops get unrolled. Could use profiling - info to only unroll the most commonly executed loops. Perhaps have - a user specifyable option to control the amount of code expansion, - or the percent of loops to consider for unrolling. Etc. */ - -/* ??? Look at the register copies inside the loop to see if they form a - simple permutation. If so, iterate the permutation until it gets back to - the start state. This is how many times we should unroll the loop, for - best results, because then all register copies can be eliminated. - For example, the lisp nreverse function should be unrolled 3 times - while (this) - { - next = this->cdr; - this->cdr = prev; - prev = this; - this = next; - } - - ??? The number of times to unroll the loop may also be based on data - references in the loop. For example, if we have a loop that references - x[i-1], x[i], and x[i+1], we should unroll it a multiple of 3 times. */ - -/* ??? Add some simple linear equation solving capability so that we can - determine the number of loop iterations for more complex loops. - For example, consider this loop from gdb - #define SWAP_TARGET_AND_HOST(buffer,len) - { - char tmp; - char *p = (char *) buffer; - char *q = ((char *) buffer) + len - 1; - int iterations = (len + 1) >> 1; - int i; - for (p; p < q; p++, q--;) - { - tmp = *q; - *q = *p; - *p = tmp; - } - } - Note that: - start value = p = &buffer + current_iteration - end value = q = &buffer + len - 1 - current_iteration - Given the loop exit test of "p < q", then there must be "q - p" iterations, - set equal to zero and solve for number of iterations: - q - p = len - 1 - 2*current_iteration = 0 - current_iteration = (len - 1) / 2 - Hence, there are (len - 1) / 2 (rounded up to the nearest integer) - iterations of this loop. */ - -/* ??? Currently, no labels are marked as loop invariant when doing loop - unrolling. This is because an insn inside the loop, that loads the address - of a label inside the loop into a register, could be moved outside the loop - by the invariant code motion pass if labels were invariant. If the loop - is subsequently unrolled, the code will be wrong because each unrolled - body of the loop will use the same address, whereas each actually needs a - different address. A case where this happens is when a loop containing - a switch statement is unrolled. - - It would be better to let labels be considered invariant. When we - unroll loops here, check to see if any insns using a label local to the - loop were moved before the loop. If so, then correct the problem, by - moving the insn back into the loop, or perhaps replicate the insn before - the loop, one copy for each time the loop is unrolled. */ - -/* The prime factors looked for when trying to unroll a loop by some - number which is modulo the total number of iterations. Just checking - for these 4 prime factors will find at least one factor for 75% of - all numbers theoretically. Practically speaking, this will succeed - almost all of the time since loops are generally a multiple of 2 - and/or 5. */ - -#define NUM_FACTORS 4 - -struct _factor { int factor, count; } factors[NUM_FACTORS] - = { {2, 0}, {3, 0}, {5, 0}, {7, 0}}; - -/* Describes the different types of loop unrolling performed. */ - -enum unroll_types { UNROLL_COMPLETELY, UNROLL_MODULO, UNROLL_NAIVE }; - -#include "config.h" -#include "rtl.h" -#include "insn-config.h" -#include "integrate.h" -#include "regs.h" -#include "flags.h" -#include "expr.h" -#include -#include "loop.h" - -/* This controls which loops are unrolled, and by how much we unroll - them. */ - -#ifndef MAX_UNROLLED_INSNS -#define MAX_UNROLLED_INSNS 100 -#endif - -/* Indexed by register number, if non-zero, then it contains a pointer - to a struct induction for a DEST_REG giv which has been combined with - one of more address givs. This is needed because whenever such a DEST_REG - giv is modified, we must modify the value of all split address givs - that were combined with this DEST_REG giv. */ - -static struct induction **addr_combined_regs; - -/* Indexed by register number, if this is a splittable induction variable, - then this will hold the current value of the register, which depends on the - iteration number. */ - -static rtx *splittable_regs; - -/* Indexed by register number, if this is a splittable induction variable, - then this will hold the number of instructions in the loop that modify - the induction variable. Used to ensure that only the last insn modifying - a split iv will update the original iv of the dest. */ - -static int *splittable_regs_updates; - -/* Values describing the current loop's iteration variable. These are set up - by loop_iterations, and used by precondition_loop_p. */ - -static rtx loop_iteration_var; -static rtx loop_initial_value; -static rtx loop_increment; -static rtx loop_final_value; - -/* Forward declarations. */ - -static void init_reg_map (); -static int precondition_loop_p (); -static void copy_loop_body (); -static void iteration_info (); -static rtx approx_final_value (); -static int find_splittable_regs (); -static int find_splittable_givs (); -static rtx fold_rtx_mult_add (); - -/* Try to unroll one loop and split induction variables in the loop. - - The loop is described by the arguments LOOP_END, INSN_COUNT, and - LOOP_START. END_INSERT_BEFORE indicates where insns should be added - which need to be executed when the loop falls through. STRENGTH_REDUCTION_P - indicates whether information generated in the strength reduction pass - is available. - - This function is intended to be called from within `strength_reduce' - in loop.c. */ - -void -unroll_loop (loop_end, insn_count, loop_start, end_insert_before, - strength_reduce_p) - rtx loop_end; - int insn_count; - rtx loop_start; - rtx end_insert_before; - int strength_reduce_p; -{ - int i, j, temp; - int unroll_number = 1; - rtx copy_start, copy_end; - rtx insn, copy, sequence, pattern, tem; - int max_labelno, max_insnno; - rtx insert_before; - struct inline_remap *map; - char *local_label; - int maxregnum; - int new_maxregnum; - rtx exit_label = 0; - rtx start_label; - struct iv_class *bl; - struct induction *v; - int splitting_not_safe = 0; - enum unroll_types unroll_type; - int loop_preconditioned = 0; - rtx safety_label; - /* This points to the last real insn in the loop, which should be either - a JUMP_INSN (for conditional jumps) or a BARRIER (for unconditional - jumps). */ - rtx last_loop_insn; - - /* Don't bother unrolling huge loops. Since the minimum factor is - two, loops greater than one half of MAX_UNROLLED_INSNS will never - be unrolled. */ - if (insn_count > MAX_UNROLLED_INSNS / 2) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, "Unrolling failure: Loop too big.\n"); - return; - } - - /* When emitting debugger info, we can't unroll loops with unequal numbers - of block_beg and block_end notes, because that would unbalance the block - structure of the function. This can happen as a result of the - "if (foo) bar; else break;" optimization in jump.c. */ - - if (write_symbols != NO_DEBUG) - { - int block_begins = 0; - int block_ends = 0; - - for (insn = loop_start; insn != loop_end; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == NOTE) - { - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG) - block_begins++; - else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END) - block_ends++; - } - } - - if (block_begins != block_ends) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Unrolling failure: Unbalanced block notes.\n"); - return; - } - } - - /* Determine type of unroll to perform. Depends on the number of iterations - and the size of the loop. */ - - /* If there is no strength reduce info, then set loop_n_iterations to zero. - This can happen if strength_reduce can't find any bivs in the loop. - A value of zero indicates that the number of iterations could not be - calculated. */ - - if (! strength_reduce_p) - loop_n_iterations = 0; - - if (loop_dump_stream && loop_n_iterations > 0) - fprintf (loop_dump_stream, - "Loop unrolling: %d iterations.\n", loop_n_iterations); - - /* Find and save a pointer to the last nonnote insn in the loop. */ - - last_loop_insn = prev_nonnote_insn (loop_end); - - /* Calculate how many times to unroll the loop. Indicate whether or - not the loop is being completely unrolled. */ - - if (loop_n_iterations == 1) - { - /* If number of iterations is exactly 1, then eliminate the compare and - branch at the end of the loop since they will never be taken. - Then return, since no other action is needed here. */ - - /* If the last instruction is not a BARRIER or a JUMP_INSN, then - don't do anything. */ - - if (GET_CODE (last_loop_insn) == BARRIER) - { - /* Delete the jump insn. This will delete the barrier also. */ - delete_insn (PREV_INSN (last_loop_insn)); - } - else if (GET_CODE (last_loop_insn) == JUMP_INSN) - { -#ifdef HAVE_cc0 - /* The immediately preceding insn is a compare which must be - deleted. */ - delete_insn (last_loop_insn); - delete_insn (PREV_INSN (last_loop_insn)); -#else - /* The immediately preceding insn may not be the compare, so don't - delete it. */ - delete_insn (last_loop_insn); -#endif - } - return; - } - else if (loop_n_iterations > 0 - && loop_n_iterations * insn_count < MAX_UNROLLED_INSNS) - { - unroll_number = loop_n_iterations; - unroll_type = UNROLL_COMPLETELY; - } - else if (loop_n_iterations > 0) - { - /* Try to factor the number of iterations. Don't bother with the - general case, only using 2, 3, 5, and 7 will get 75% of all - numbers theoretically, and almost all in practice. */ - - for (i = 0; i < NUM_FACTORS; i++) - factors[i].count = 0; - - temp = loop_n_iterations; - for (i = NUM_FACTORS - 1; i >= 0; i--) - while (temp % factors[i].factor == 0) - { - factors[i].count++; - temp = temp / factors[i].factor; - } - - /* Start with the larger factors first so that we generally - get lots of unrolling. */ - - unroll_number = 1; - temp = insn_count; - for (i = 3; i >= 0; i--) - while (factors[i].count--) - { - if (temp * factors[i].factor < MAX_UNROLLED_INSNS) - { - unroll_number *= factors[i].factor; - temp *= factors[i].factor; - } - else - break; - } - - /* If we couldn't find any factors, then unroll as in the normal - case. */ - if (unroll_number == 1) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: No factors found.\n"); - } - else - unroll_type = UNROLL_MODULO; - } - - - /* Default case, calculate number of times to unroll loop based on its - size. */ - if (unroll_number == 1) - { - if (8 * insn_count < MAX_UNROLLED_INSNS) - unroll_number = 8; - else if (4 * insn_count < MAX_UNROLLED_INSNS) - unroll_number = 4; - else - unroll_number = 2; - - unroll_type = UNROLL_NAIVE; - } - - /* Now we know how many times to unroll the loop. */ - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Unrolling loop %d times.\n", unroll_number); - - - if (unroll_type == UNROLL_COMPLETELY || unroll_type == UNROLL_MODULO) - { - /* Loops of these types should never start with a jump down to - the exit condition test. For now, check for this case just to - be sure. UNROLL_NAIVE loops can be of this form, this case is - handled below. */ - insn = loop_start; - while (GET_CODE (insn) != CODE_LABEL && GET_CODE (insn) != JUMP_INSN) - insn = NEXT_INSN (insn); - if (GET_CODE (insn) == JUMP_INSN) - abort (); - } - - if (unroll_type == UNROLL_COMPLETELY) - { - /* Completely unrolling the loop: Delete the compare and branch at - the end (the last two instructions). This delete must done at the - very end of loop unrolling, to avoid problems with calls to - back_branch_in_range_p, which is called by find_splittable_regs. - All increments of splittable bivs/givs are changed to load constant - instructions. */ - - copy_start = loop_start; - - /* Set insert_before to the instruction immediately after the JUMP_INSN - (or BARRIER), so that any NOTEs between the JUMP_INSN and the end of - the loop will be correctly handled by copy_loop_body. */ - insert_before = NEXT_INSN (last_loop_insn); - - /* Set copy_end to the insn before the jump at the end of the loop. */ - if (GET_CODE (last_loop_insn) == BARRIER) - copy_end = PREV_INSN (PREV_INSN (last_loop_insn)); - else if (GET_CODE (last_loop_insn) == JUMP_INSN) - { -#ifdef HAVE_cc0 - /* The instruction immediately before the JUMP_INSN is a compare - instruction which we do not want to copy. */ - copy_end = PREV_INSN (PREV_INSN (last_loop_insn)); -#else - /* The instruction immediately before the JUMP_INSN may not be the - compare, so we must copy it. */ - copy_end = PREV_INSN (last_loop_insn); -#endif - } - else - { - /* We currently can't unroll a loop if it doesn't end with a - JUMP_INSN. There would need to be a mechanism that recognizes - this case, and then inserts a jump after each loop body, which - jumps to after the last loop body. */ - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Unrolling failure: loop does not end with a JUMP_INSN.\n"); - return; - } - } - else if (unroll_type == UNROLL_MODULO) - { - /* Partially unrolling the loop: The compare and branch at the end - (the last two instructions) must remain. Don't copy the compare - and branch instructions at the end of the loop. Insert the unrolled - code immediately before the compare/branch at the end so that the - code will fall through to them as before. */ - - copy_start = loop_start; - - /* Set insert_before to the jump insn at the end of the loop. - Set copy_end to before the jump insn at the end of the loop. */ - if (GET_CODE (last_loop_insn) == BARRIER) - { - insert_before = PREV_INSN (last_loop_insn); - copy_end = PREV_INSN (insert_before); - } - else if (GET_CODE (last_loop_insn) == JUMP_INSN) - { -#ifdef HAVE_cc0 - /* The instruction immediately before the JUMP_INSN is a compare - instruction which we do not want to copy or delete. */ - insert_before = PREV_INSN (last_loop_insn); - copy_end = PREV_INSN (insert_before); -#else - /* The instruction immediately before the JUMP_INSN may not be the - compare, so we must copy it. */ - insert_before = last_loop_insn; - copy_end = PREV_INSN (last_loop_insn); -#endif - } - else - { - /* We currently can't unroll a loop if it doesn't end with a - JUMP_INSN. There would need to be a mechanism that recognizes - this case, and then inserts a jump after each loop body, which - jumps to after the last loop body. */ - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Unrolling failure: loop does not end with a JUMP_INSN.\n"); - return; - } - } - else - { - /* Normal case: Must copy the compare and branch instructions at the - end of the loop. */ - - if (GET_CODE (last_loop_insn) == BARRIER) - { - /* Loop ends with an unconditional jump and a barrier. - Handle this like above, don't copy jump and barrier. - This is not strictly necessary, but doing so prevents generating - unconditional jumps to an immediately following label. - - This will be corrected below if the target of this jump is - not the start_label. */ - - insert_before = PREV_INSN (last_loop_insn); - copy_end = PREV_INSN (insert_before); - } - else if (GET_CODE (last_loop_insn) == JUMP_INSN) - { - /* Set insert_before to immediately after the JUMP_INSN, so that - NOTEs at the end of the loop will be correctly handled by - copy_loop_body. */ - insert_before = NEXT_INSN (last_loop_insn); - copy_end = last_loop_insn; - } - else - { - /* We currently can't unroll a loop if it doesn't end with a - JUMP_INSN. There would need to be a mechanism that recognizes - this case, and then inserts a jump after each loop body, which - jumps to after the last loop body. */ - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Unrolling failure: loop does not end with a JUMP_INSN.\n"); - return; - } - - /* If copying exit test branches because they can not be eliminated, - then must convert the fall through case of the branch to a jump past - the end of the loop. Create a label to emit after the loop and save - it for later use. Do not use the label after the loop, if any, since - it might be used by insns outside the loop, or there might be insns - added before it later by final_[bg]iv_value which must be after - the real exit label. */ - exit_label = gen_label_rtx (); - - insn = loop_start; - while (GET_CODE (insn) != CODE_LABEL && GET_CODE (insn) != JUMP_INSN) - insn = NEXT_INSN (insn); - - if (GET_CODE (insn) == JUMP_INSN) - { - /* The loop starts with a jump down to the exit condition test. - Start copying the loop after the barrier following this - jump insn. */ - copy_start = NEXT_INSN (insn); - - /* Splitting induction variables doesn't work when the loop is - entered via a jump to the bottom, because then we end up doing - a comparison against a new register for a split variable, but - we did not execute the set insn for the new register because - it was skipped over. */ - splitting_not_safe = 1; - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Splitting not safe, because loop not entered at top.\n"); - } - else - copy_start = loop_start; - } - - /* This should always be the first label in the loop. */ - start_label = NEXT_INSN (copy_start); - /* There may be a line number note and/or a loop continue note here. */ - while (GET_CODE (start_label) == NOTE) - start_label = NEXT_INSN (start_label); - if (GET_CODE (start_label) != CODE_LABEL) - { - /* This can happen as a result of jump threading. If the first insns in - the loop test the same condition as the loop's backward jump, or the - opposite condition, then the backward jump will be modified to point - to elsewhere, and the loop's start label is deleted. - - This case currently can not be handled by the loop unrolling code. */ - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Unrolling failure: unknown insns between BEG note and loop label.\n"); - return; - } - - if (unroll_type == UNROLL_NAIVE - && GET_CODE (last_loop_insn) == BARRIER - && start_label != JUMP_LABEL (PREV_INSN (last_loop_insn))) - { - /* In this case, we must copy the jump and barrier, because they will - not be converted to jumps to an immediately following label. */ - - insert_before = NEXT_INSN (last_loop_insn); - copy_end = last_loop_insn; - } - - /* Allocate a translation table for the labels and insn numbers. - They will be filled in as we copy the insns in the loop. */ - - max_labelno = max_label_num (); - max_insnno = get_max_uid (); - - map = (struct inline_remap *) alloca (sizeof (struct inline_remap)); - - map->integrating = 0; - - /* Allocate the label map. */ - - if (max_labelno > 0) - { - map->label_map = (rtx *) alloca (max_labelno * sizeof (rtx)); - - local_label = (char *) alloca (max_labelno); - bzero (local_label, max_labelno); - } - else - map->label_map = 0; - - /* Search the loop and mark all local labels, i.e. the ones which have to - be distinct labels when copied. For all labels which might be - non-local, set their label_map entries to point to themselves. - If they happen to be local their label_map entries will be overwritten - before the loop body is copied. The label_map entries for local labels - will be set to a different value each time the loop body is copied. */ - - for (insn = copy_start; insn != loop_end; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == CODE_LABEL) - local_label[CODE_LABEL_NUMBER (insn)] = 1; - else if (GET_CODE (insn) == JUMP_INSN) - { - if (JUMP_LABEL (insn)) - map->label_map[CODE_LABEL_NUMBER (JUMP_LABEL (insn))] - = JUMP_LABEL (insn); - else if (GET_CODE (PATTERN (insn)) == ADDR_VEC - || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC) - { - rtx pat = PATTERN (insn); - int diff_vec_p = GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC; - int len = XVECLEN (pat, diff_vec_p); - rtx label; - - for (i = 0; i < len; i++) - { - label = XEXP (XVECEXP (pat, diff_vec_p, i), 0); - map->label_map[CODE_LABEL_NUMBER (label)] = label; - } - } - } - } - - /* Allocate space for the insn map. */ - - map->insn_map = (rtx *) alloca (max_insnno * sizeof (rtx)); - - /* Set this to zero, to indicate that we are doing loop unrolling, - not function inlining. */ - map->inline_target = 0; - - /* The register and constant maps depend on the number of registers - present, so the final maps can't be created until after - find_splittable_regs is called. However, they are needed for - preconditioning, so we create temporary maps when preconditioning - is performed. */ - - /* The preconditioning code may allocate two new pseudo registers. */ - maxregnum = max_reg_num (); - - /* Allocate and zero out the splittable_regs and addr_combined_regs - arrays. These must be zeroed here because they will be used if - loop preconditioning is performed, and must be zero for that case. - - It is safe to do this here, since the extra registers created by the - preconditioning code and find_splittable_regs will never be used - to access the splittable_regs[] and addr_combined_regs[] arrays. */ - - splittable_regs = (rtx *) alloca (maxregnum * sizeof (rtx)); - bzero (splittable_regs, maxregnum * sizeof (rtx)); - splittable_regs_updates = (int *) alloca (maxregnum * sizeof (int)); - bzero (splittable_regs_updates, maxregnum * sizeof (int)); - addr_combined_regs - = (struct induction **) alloca (maxregnum * sizeof (struct induction *)); - bzero (addr_combined_regs, maxregnum * sizeof (struct induction *)); - - /* If this loop requires exit tests when unrolled, check to see if we - can precondition the loop so as to make the exit tests unnecessary. - Just like variable splitting, this is not safe if the loop is entered - via a jump to the bottom. Also, can not do this if no strength - reduce info, because precondition_loop_p uses this info. */ - - /* Must copy the loop body for preconditioning before the following - find_splittable_regs call since that will emit insns which need to - be after the preconditioned loop copies, but immediately before the - unrolled loop copies. */ - - /* Also, it is not safe to split induction variables for the preconditioned - copies of the loop body. If we split induction variables, then the code - assumes that each induction variable can be represented as a function - of its initial value and the loop iteration number. This is not true - in this case, because the last preconditioned copy of the loop body - could be any iteration from the first up to the `unroll_number-1'th, - depending on the initial value of the iteration variable. Therefore - we can not split induction variables here, because we can not calculate - their value. Hence, this code must occur before find_splittable_regs - is called. */ - - if (unroll_type == UNROLL_NAIVE && ! splitting_not_safe && strength_reduce_p) - { - rtx initial_value, final_value, increment; - - if (precondition_loop_p (&initial_value, &final_value, &increment, - loop_start, loop_end)) - { - register rtx diff, temp; - enum machine_mode mode; - rtx *labels; - int abs_inc, neg_inc; - - map->reg_map = (rtx *) alloca (maxregnum * sizeof (rtx)); - - map->const_equiv_map = (rtx *) alloca (maxregnum * sizeof (rtx)); - map->const_age_map = (unsigned *) alloca (maxregnum - * sizeof (unsigned)); - map->const_equiv_map_size = maxregnum; - global_const_equiv_map = map->const_equiv_map; - - init_reg_map (map, maxregnum); - - /* Limit loop unrolling to 4, since this will make 7 copies of - the loop body. */ - if (unroll_number > 4) - unroll_number = 4; - - /* Save the absolute value of the increment, and also whether or - not it is negative. */ - neg_inc = 0; - abs_inc = INTVAL (increment); - if (abs_inc < 0) - { - abs_inc = - abs_inc; - neg_inc = 1; - } - - start_sequence (); - - /* Decide what mode to do these calculations in. Choose the larger - of final_value's mode and initial_value's mode, or a full-word if - both are constants. */ - mode = GET_MODE (final_value); - if (mode == VOIDmode) - { - mode = GET_MODE (initial_value); - if (mode == VOIDmode) - mode = word_mode; - } - else if (mode != GET_MODE (initial_value) - && (GET_MODE_SIZE (mode) - < GET_MODE_SIZE (GET_MODE (initial_value)))) - mode = GET_MODE (initial_value); - - /* Calculate the difference between the final and initial values. - Final value may be a (plus (reg x) (const_int 1)) rtx. - Let the following cse pass simplify this if initial value is - a constant. - - We must copy the final and initial values here to avoid - improperly shared rtl. */ - - diff = expand_binop (mode, sub_optab, copy_rtx (final_value), - copy_rtx (initial_value), NULL_RTX, 0, - OPTAB_LIB_WIDEN); - - /* Now calculate (diff % (unroll * abs (increment))) by using an - and instruction. */ - diff = expand_binop (GET_MODE (diff), and_optab, diff, - GEN_INT (unroll_number * abs_inc - 1), - NULL_RTX, 0, OPTAB_LIB_WIDEN); - - /* Now emit a sequence of branches to jump to the proper precond - loop entry point. */ - - labels = (rtx *) alloca (sizeof (rtx) * unroll_number); - for (i = 0; i < unroll_number; i++) - labels[i] = gen_label_rtx (); - - /* Assuming the unroll_number is 4, and the increment is 2, then - for a negative increment: for a positive increment: - diff = 0,1 precond 0 diff = 0,7 precond 0 - diff = 2,3 precond 3 diff = 1,2 precond 1 - diff = 4,5 precond 2 diff = 3,4 precond 2 - diff = 6,7 precond 1 diff = 5,6 precond 3 */ - - /* We only need to emit (unroll_number - 1) branches here, the - last case just falls through to the following code. */ - - /* ??? This would give better code if we emitted a tree of branches - instead of the current linear list of branches. */ - - for (i = 0; i < unroll_number - 1; i++) - { - int cmp_const; - - /* For negative increments, must invert the constant compared - against, except when comparing against zero. */ - if (i == 0) - cmp_const = 0; - else if (neg_inc) - cmp_const = unroll_number - i; - else - cmp_const = i; - - emit_cmp_insn (diff, GEN_INT (abs_inc * cmp_const), - EQ, NULL_RTX, mode, 0, 0); - - if (i == 0) - emit_jump_insn (gen_beq (labels[i])); - else if (neg_inc) - emit_jump_insn (gen_bge (labels[i])); - else - emit_jump_insn (gen_ble (labels[i])); - JUMP_LABEL (get_last_insn ()) = labels[i]; - LABEL_NUSES (labels[i])++; - } - - /* If the increment is greater than one, then we need another branch, - to handle other cases equivalent to 0. */ - - /* ??? This should be merged into the code above somehow to help - simplify the code here, and reduce the number of branches emitted. - For the negative increment case, the branch here could easily - be merged with the `0' case branch above. For the positive - increment case, it is not clear how this can be simplified. */ - - if (abs_inc != 1) - { - int cmp_const; - - if (neg_inc) - cmp_const = abs_inc - 1; - else - cmp_const = abs_inc * (unroll_number - 1) + 1; - - emit_cmp_insn (diff, GEN_INT (cmp_const), EQ, NULL_RTX, - mode, 0, 0); - - if (neg_inc) - emit_jump_insn (gen_ble (labels[0])); - else - emit_jump_insn (gen_bge (labels[0])); - JUMP_LABEL (get_last_insn ()) = labels[0]; - LABEL_NUSES (labels[0])++; - } - - sequence = gen_sequence (); - end_sequence (); - emit_insn_before (sequence, loop_start); - - /* Only the last copy of the loop body here needs the exit - test, so set copy_end to exclude the compare/branch here, - and then reset it inside the loop when get to the last - copy. */ - - if (GET_CODE (last_loop_insn) == BARRIER) - copy_end = PREV_INSN (PREV_INSN (last_loop_insn)); - else if (GET_CODE (last_loop_insn) == JUMP_INSN) - { -#ifdef HAVE_cc0 - /* The immediately preceding insn is a compare which we do not - want to copy. */ - copy_end = PREV_INSN (PREV_INSN (last_loop_insn)); -#else - /* The immediately preceding insn may not be a compare, so we - must copy it. */ - copy_end = PREV_INSN (last_loop_insn); -#endif - } - else - abort (); - - for (i = 1; i < unroll_number; i++) - { - emit_label_after (labels[unroll_number - i], - PREV_INSN (loop_start)); - - bzero (map->insn_map, max_insnno * sizeof (rtx)); - bzero (map->const_equiv_map, maxregnum * sizeof (rtx)); - bzero (map->const_age_map, maxregnum * sizeof (unsigned)); - map->const_age = 0; - - for (j = 0; j < max_labelno; j++) - if (local_label[j]) - map->label_map[j] = gen_label_rtx (); - - /* The last copy needs the compare/branch insns at the end, - so reset copy_end here if the loop ends with a conditional - branch. */ - - if (i == unroll_number - 1) - { - if (GET_CODE (last_loop_insn) == BARRIER) - copy_end = PREV_INSN (PREV_INSN (last_loop_insn)); - else - copy_end = last_loop_insn; - } - - /* None of the copies are the `last_iteration', so just - pass zero for that parameter. */ - copy_loop_body (copy_start, copy_end, map, exit_label, 0, - unroll_type, start_label, loop_end, - loop_start, copy_end); - } - emit_label_after (labels[0], PREV_INSN (loop_start)); - - if (GET_CODE (last_loop_insn) == BARRIER) - { - insert_before = PREV_INSN (last_loop_insn); - copy_end = PREV_INSN (insert_before); - } - else - { -#ifdef HAVE_cc0 - /* The immediately preceding insn is a compare which we do not - want to copy. */ - insert_before = PREV_INSN (last_loop_insn); - copy_end = PREV_INSN (insert_before); -#else - /* The immediately preceding insn may not be a compare, so we - must copy it. */ - insert_before = last_loop_insn; - copy_end = PREV_INSN (last_loop_insn); -#endif - } - - /* Set unroll type to MODULO now. */ - unroll_type = UNROLL_MODULO; - loop_preconditioned = 1; - } - } - - /* If reach here, and the loop type is UNROLL_NAIVE, then don't unroll - the loop unless all loops are being unrolled. */ - if (unroll_type == UNROLL_NAIVE && ! flag_unroll_all_loops) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, "Unrolling failure: Naive unrolling not being done.\n"); - return; - } - - /* At this point, we are guaranteed to unroll the loop. */ - - /* For each biv and giv, determine whether it can be safely split into - a different variable for each unrolled copy of the loop body. - We precalculate and save this info here, since computing it is - expensive. - - Do this before deleting any instructions from the loop, so that - back_branch_in_range_p will work correctly. */ - - if (splitting_not_safe) - temp = 0; - else - temp = find_splittable_regs (unroll_type, loop_start, loop_end, - end_insert_before, unroll_number); - - /* find_splittable_regs may have created some new registers, so must - reallocate the reg_map with the new larger size, and must realloc - the constant maps also. */ - - maxregnum = max_reg_num (); - map->reg_map = (rtx *) alloca (maxregnum * sizeof (rtx)); - - init_reg_map (map, maxregnum); - - /* Space is needed in some of the map for new registers, so new_maxregnum - is an (over)estimate of how many registers will exist at the end. */ - new_maxregnum = maxregnum + (temp * unroll_number * 2); - - /* Must realloc space for the constant maps, because the number of registers - may have changed. */ - - map->const_equiv_map = (rtx *) alloca (new_maxregnum * sizeof (rtx)); - map->const_age_map = (unsigned *) alloca (new_maxregnum * sizeof (unsigned)); - - global_const_equiv_map = map->const_equiv_map; - - /* Search the list of bivs and givs to find ones which need to be remapped - when split, and set their reg_map entry appropriately. */ - - for (bl = loop_iv_list; bl; bl = bl->next) - { - if (REGNO (bl->biv->src_reg) != bl->regno) - map->reg_map[bl->regno] = bl->biv->src_reg; -#if 0 - /* Currently, non-reduced/final-value givs are never split. */ - for (v = bl->giv; v; v = v->next_iv) - if (REGNO (v->src_reg) != bl->regno) - map->reg_map[REGNO (v->dest_reg)] = v->src_reg; -#endif - } - - /* If the loop is being partially unrolled, and the iteration variables - are being split, and are being renamed for the split, then must fix up - the compare instruction at the end of the loop to refer to the new - registers. This compare isn't copied, so the registers used in it - will never be replaced if it isn't done here. */ - - if (unroll_type == UNROLL_MODULO) - { - insn = NEXT_INSN (copy_end); - if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SET) - { -#if 0 - /* If non-reduced/final-value givs were split, then this would also - have to remap those givs. */ -#endif - - tem = SET_SRC (PATTERN (insn)); - /* The set source is a register. */ - if (GET_CODE (tem) == REG) - { - if (REGNO (tem) < max_reg_before_loop - && reg_iv_type[REGNO (tem)] == BASIC_INDUCT) - SET_SRC (PATTERN (insn)) - = reg_biv_class[REGNO (tem)]->biv->src_reg; - } - else - { - /* The set source is a compare of some sort. */ - tem = XEXP (SET_SRC (PATTERN (insn)), 0); - if (GET_CODE (tem) == REG - && REGNO (tem) < max_reg_before_loop - && reg_iv_type[REGNO (tem)] == BASIC_INDUCT) - XEXP (SET_SRC (PATTERN (insn)), 0) - = reg_biv_class[REGNO (tem)]->biv->src_reg; - - tem = XEXP (SET_SRC (PATTERN (insn)), 1); - if (GET_CODE (tem) == REG - && REGNO (tem) < max_reg_before_loop - && reg_iv_type[REGNO (tem)] == BASIC_INDUCT) - XEXP (SET_SRC (PATTERN (insn)), 1) - = reg_biv_class[REGNO (tem)]->biv->src_reg; - } - } - } - - /* For unroll_number - 1 times, make a copy of each instruction - between copy_start and copy_end, and insert these new instructions - before the end of the loop. */ - - for (i = 0; i < unroll_number; i++) - { - bzero (map->insn_map, max_insnno * sizeof (rtx)); - bzero (map->const_equiv_map, new_maxregnum * sizeof (rtx)); - bzero (map->const_age_map, new_maxregnum * sizeof (unsigned)); - map->const_age = 0; - - for (j = 0; j < max_labelno; j++) - if (local_label[j]) - map->label_map[j] = gen_label_rtx (); - - /* If loop starts with a branch to the test, then fix it so that - it points to the test of the first unrolled copy of the loop. */ - if (i == 0 && loop_start != copy_start) - { - insn = PREV_INSN (copy_start); - pattern = PATTERN (insn); - - tem = map->label_map[CODE_LABEL_NUMBER - (XEXP (SET_SRC (pattern), 0))]; - SET_SRC (pattern) = gen_rtx (LABEL_REF, VOIDmode, tem); - - /* Set the jump label so that it can be used by later loop unrolling - passes. */ - JUMP_LABEL (insn) = tem; - LABEL_NUSES (tem)++; - } - - copy_loop_body (copy_start, copy_end, map, exit_label, - i == unroll_number - 1, unroll_type, start_label, - loop_end, insert_before, insert_before); - } - - /* Before deleting any insns, emit a CODE_LABEL immediately after the last - insn to be deleted. This prevents any runaway delete_insn call from - more insns that it should, as it always stops at a CODE_LABEL. */ - - /* Delete the compare and branch at the end of the loop if completely - unrolling the loop. Deleting the backward branch at the end also - deletes the code label at the start of the loop. This is done at - the very end to avoid problems with back_branch_in_range_p. */ - - if (unroll_type == UNROLL_COMPLETELY) - safety_label = emit_label_after (gen_label_rtx (), last_loop_insn); - else - safety_label = emit_label_after (gen_label_rtx (), copy_end); - - /* Delete all of the original loop instructions. Don't delete the - LOOP_BEG note, or the first code label in the loop. */ - - insn = NEXT_INSN (copy_start); - while (insn != safety_label) - { - if (insn != start_label) - insn = delete_insn (insn); - else - insn = NEXT_INSN (insn); - } - - /* Can now delete the 'safety' label emitted to protect us from runaway - delete_insn calls. */ - if (INSN_DELETED_P (safety_label)) - abort (); - delete_insn (safety_label); - - /* If exit_label exists, emit it after the loop. Doing the emit here - forces it to have a higher INSN_UID than any insn in the unrolled loop. - This is needed so that mostly_true_jump in reorg.c will treat jumps - to this loop end label correctly, i.e. predict that they are usually - not taken. */ - if (exit_label) - emit_label_after (exit_label, loop_end); -} - -/* Return true if the loop can be safely, and profitably, preconditioned - so that the unrolled copies of the loop body don't need exit tests. - - This only works if final_value, initial_value and increment can be - determined, and if increment is a constant power of 2. - If increment is not a power of 2, then the preconditioning modulo - operation would require a real modulo instead of a boolean AND, and this - is not considered `profitable'. */ - -/* ??? If the loop is known to be executed very many times, or the machine - has a very cheap divide instruction, then preconditioning is a win even - when the increment is not a power of 2. Use RTX_COST to compute - whether divide is cheap. */ - -static int -precondition_loop_p (initial_value, final_value, increment, loop_start, - loop_end) - rtx *initial_value, *final_value, *increment; - rtx loop_start, loop_end; -{ - int unsigned_compare, compare_dir; - - if (loop_n_iterations > 0) - { - *initial_value = const0_rtx; - *increment = const1_rtx; - *final_value = GEN_INT (loop_n_iterations); - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Preconditioning: Success, number of iterations known, %d.\n", - loop_n_iterations); - return 1; - } - - if (loop_initial_value == 0) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Preconditioning: Could not find initial value.\n"); - return 0; - } - else if (loop_increment == 0) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Preconditioning: Could not find increment value.\n"); - return 0; - } - else if (GET_CODE (loop_increment) != CONST_INT) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Preconditioning: Increment not a constant.\n"); - return 0; - } - else if ((exact_log2 (INTVAL (loop_increment)) < 0) - && (exact_log2 (- INTVAL (loop_increment)) < 0)) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Preconditioning: Increment not a constant power of 2.\n"); - return 0; - } - - /* Unsigned_compare and compare_dir can be ignored here, since they do - not matter for preconditioning. */ - - if (loop_final_value == 0) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Preconditioning: EQ comparison loop.\n"); - return 0; - } - - /* Must ensure that final_value is invariant, so call invariant_p to - check. Before doing so, must check regno against max_reg_before_loop - to make sure that the register is in the range covered by invariant_p. - If it isn't, then it is most likely a biv/giv which by definition are - not invariant. */ - if ((GET_CODE (loop_final_value) == REG - && REGNO (loop_final_value) >= max_reg_before_loop) - || (GET_CODE (loop_final_value) == PLUS - && REGNO (XEXP (loop_final_value, 0)) >= max_reg_before_loop) - || ! invariant_p (loop_final_value)) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Preconditioning: Final value not invariant.\n"); - return 0; - } - - /* Fail for floating point values, since the caller of this function - does not have code to deal with them. */ - if (GET_MODE_CLASS (GET_MODE (loop_final_value)) == MODE_FLOAT - || GET_MODE_CLASS (GET_MODE (loop_initial_value)) == MODE_FLOAT) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Preconditioning: Floating point final or initial value.\n"); - return 0; - } - - /* Now set initial_value to be the iteration_var, since that may be a - simpler expression, and is guaranteed to be correct if all of the - above tests succeed. - - We can not use the initial_value as calculated, because it will be - one too small for loops of the form "while (i-- > 0)". We can not - emit code before the loop_skip_over insns to fix this problem as this - will then give a number one too large for loops of the form - "while (--i > 0)". - - Note that all loops that reach here are entered at the top, because - this function is not called if the loop starts with a jump. */ - - /* Fail if loop_iteration_var is not live before loop_start, since we need - to test its value in the preconditioning code. */ - - if (uid_luid[regno_first_uid[REGNO (loop_iteration_var)]] - > INSN_LUID (loop_start)) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Preconditioning: Iteration var not live before loop start.\n"); - return 0; - } - - *initial_value = loop_iteration_var; - *increment = loop_increment; - *final_value = loop_final_value; - - /* Success! */ - if (loop_dump_stream) - fprintf (loop_dump_stream, "Preconditioning: Successful.\n"); - return 1; -} - - -/* All pseudo-registers must be mapped to themselves. Two hard registers - must be mapped, VIRTUAL_STACK_VARS_REGNUM and VIRTUAL_INCOMING_ARGS_ - REGNUM, to avoid function-inlining specific conversions of these - registers. All other hard regs can not be mapped because they may be - used with different - modes. */ - -static void -init_reg_map (map, maxregnum) - struct inline_remap *map; - int maxregnum; -{ - int i; - - for (i = maxregnum - 1; i > LAST_VIRTUAL_REGISTER; i--) - map->reg_map[i] = regno_reg_rtx[i]; - /* Just clear the rest of the entries. */ - for (i = LAST_VIRTUAL_REGISTER; i >= 0; i--) - map->reg_map[i] = 0; - - map->reg_map[VIRTUAL_STACK_VARS_REGNUM] - = regno_reg_rtx[VIRTUAL_STACK_VARS_REGNUM]; - map->reg_map[VIRTUAL_INCOMING_ARGS_REGNUM] - = regno_reg_rtx[VIRTUAL_INCOMING_ARGS_REGNUM]; -} - -/* Strength-reduction will often emit code for optimized biv/givs which - calculates their value in a temporary register, and then copies the result - to the iv. This procedure reconstructs the pattern computing the iv; - verifying that all operands are of the proper form. - - The return value is the amount that the giv is incremented by. */ - -static rtx -calculate_giv_inc (pattern, src_insn, regno) - rtx pattern, src_insn; - int regno; -{ - rtx increment; - - /* Verify that we have an increment insn here. First check for a plus - as the set source. */ - if (GET_CODE (SET_SRC (pattern)) != PLUS) - { - /* SR sometimes computes the new giv value in a temp, then copies it - to the new_reg. */ - src_insn = PREV_INSN (src_insn); - pattern = PATTERN (src_insn); - if (GET_CODE (SET_SRC (pattern)) != PLUS) - abort (); - - /* The last insn emitted is not needed, so delete it to avoid confusing - the second cse pass. This insn sets the giv unnecessarily. */ - delete_insn (get_last_insn ()); - } - - /* Verify that we have a constant as the second operand of the plus. */ - increment = XEXP (SET_SRC (pattern), 1); - if (GET_CODE (increment) != CONST_INT) - { - /* SR sometimes puts the constant in a register, especially if it is - too big to be an add immed operand. */ - increment = SET_SRC (PATTERN (PREV_INSN (src_insn))); - - /* SR may have used LO_SUM to compute the constant if it is too large - for a load immed operand. In this case, the constant is in operand - one of the LO_SUM rtx. */ - if (GET_CODE (increment) == LO_SUM) - increment = XEXP (increment, 1); - - if (GET_CODE (increment) != CONST_INT) - abort (); - - /* The insn loading the constant into a register is not longer needed, - so delete it. */ - delete_insn (get_last_insn ()); - } - - /* Check that the source register is the same as the dest register. */ - if (GET_CODE (XEXP (SET_SRC (pattern), 0)) != REG - || REGNO (XEXP (SET_SRC (pattern), 0)) != regno) - abort (); - - return increment; -} - -/* Copy REG_NOTES, except for insn references, because not all insn_map - entries are valid yet. We do need to copy registers now though, because - the reg_map entries can change during copying. */ - -static rtx -initial_reg_note_copy (notes, map) - rtx notes; - struct inline_remap *map; -{ - rtx copy; - - if (notes == 0) - return 0; - - copy = rtx_alloc (GET_CODE (notes)); - PUT_MODE (copy, GET_MODE (notes)); - - if (GET_CODE (notes) == EXPR_LIST) - XEXP (copy, 0) = copy_rtx_and_substitute (XEXP (notes, 0), map); - else if (GET_CODE (notes) == INSN_LIST) - /* Don't substitute for these yet. */ - XEXP (copy, 0) = XEXP (notes, 0); - else - abort (); - - XEXP (copy, 1) = initial_reg_note_copy (XEXP (notes, 1), map); - - return copy; -} - -/* Fixup insn references in copied REG_NOTES. */ - -static void -final_reg_note_copy (notes, map) - rtx notes; - struct inline_remap *map; -{ - rtx note; - - for (note = notes; note; note = XEXP (note, 1)) - if (GET_CODE (note) == INSN_LIST) - XEXP (note, 0) = map->insn_map[INSN_UID (XEXP (note, 0))]; -} - -/* Copy each instruction in the loop, substituting from map as appropriate. - This is very similar to a loop in expand_inline_function. */ - -static void -copy_loop_body (copy_start, copy_end, map, exit_label, last_iteration, - unroll_type, start_label, loop_end, insert_before, - copy_notes_from) - rtx copy_start, copy_end; - struct inline_remap *map; - rtx exit_label; - int last_iteration; - enum unroll_types unroll_type; - rtx start_label, loop_end, insert_before, copy_notes_from; -{ - rtx insn, pattern; - rtx tem, copy; - int dest_reg_was_split, i; - rtx cc0_insn = 0; - rtx final_label = 0; - rtx giv_inc, giv_dest_reg, giv_src_reg; - - /* If this isn't the last iteration, then map any references to the - start_label to final_label. Final label will then be emitted immediately - after the end of this loop body if it was ever used. - - If this is the last iteration, then map references to the start_label - to itself. */ - if (! last_iteration) - { - final_label = gen_label_rtx (); - map->label_map[CODE_LABEL_NUMBER (start_label)] = final_label; - } - else - map->label_map[CODE_LABEL_NUMBER (start_label)] = start_label; - - start_sequence (); - - insn = copy_start; - do - { - insn = NEXT_INSN (insn); - - map->orig_asm_operands_vector = 0; - - switch (GET_CODE (insn)) - { - case INSN: - pattern = PATTERN (insn); - copy = 0; - giv_inc = 0; - - /* Check to see if this is a giv that has been combined with - some split address givs. (Combined in the sense that - `combine_givs' in loop.c has put two givs in the same register.) - In this case, we must search all givs based on the same biv to - find the address givs. Then split the address givs. - Do this before splitting the giv, since that may map the - SET_DEST to a new register. */ - - if (GET_CODE (pattern) == SET - && GET_CODE (SET_DEST (pattern)) == REG - && addr_combined_regs[REGNO (SET_DEST (pattern))]) - { - struct iv_class *bl; - struct induction *v, *tv; - int regno = REGNO (SET_DEST (pattern)); - - v = addr_combined_regs[REGNO (SET_DEST (pattern))]; - bl = reg_biv_class[REGNO (v->src_reg)]; - - /* Although the giv_inc amount is not needed here, we must call - calculate_giv_inc here since it might try to delete the - last insn emitted. If we wait until later to call it, - we might accidentally delete insns generated immediately - below by emit_unrolled_add. */ - - giv_inc = calculate_giv_inc (pattern, insn, regno); - - /* Now find all address giv's that were combined with this - giv 'v'. */ - for (tv = bl->giv; tv; tv = tv->next_iv) - if (tv->giv_type == DEST_ADDR && tv->same == v) - { - int this_giv_inc = INTVAL (giv_inc); - - /* Scale this_giv_inc if the multiplicative factors of - the two givs are different. */ - if (tv->mult_val != v->mult_val) - this_giv_inc = (this_giv_inc / INTVAL (v->mult_val) - * INTVAL (tv->mult_val)); - - tv->dest_reg = plus_constant (tv->dest_reg, this_giv_inc); - *tv->location = tv->dest_reg; - - if (last_iteration && unroll_type != UNROLL_COMPLETELY) - { - /* Must emit an insn to increment the split address - giv. Add in the const_adjust field in case there - was a constant eliminated from the address. */ - rtx value, dest_reg; - - /* tv->dest_reg will be either a bare register, - or else a register plus a constant. */ - if (GET_CODE (tv->dest_reg) == REG) - dest_reg = tv->dest_reg; - else - dest_reg = XEXP (tv->dest_reg, 0); - - /* tv->dest_reg may actually be a (PLUS (REG) (CONST)) - here, so we must call plus_constant to add - the const_adjust amount before calling - emit_unrolled_add below. */ - value = plus_constant (tv->dest_reg, tv->const_adjust); - - /* The constant could be too large for an add - immediate, so can't directly emit an insn here. */ - emit_unrolled_add (dest_reg, XEXP (value, 0), - XEXP (value, 1)); - - /* Reset the giv to be just the register again, in case - it is used after the set we have just emitted. - We must subtract the const_adjust factor added in - above. */ - tv->dest_reg = plus_constant (dest_reg, - - tv->const_adjust); - *tv->location = tv->dest_reg; - } - } - } - - /* If this is a setting of a splittable variable, then determine - how to split the variable, create a new set based on this split, - and set up the reg_map so that later uses of the variable will - use the new split variable. */ - - dest_reg_was_split = 0; - - if (GET_CODE (pattern) == SET - && GET_CODE (SET_DEST (pattern)) == REG - && splittable_regs[REGNO (SET_DEST (pattern))]) - { - int regno = REGNO (SET_DEST (pattern)); - - dest_reg_was_split = 1; - - /* Compute the increment value for the giv, if it wasn't - already computed above. */ - - if (giv_inc == 0) - giv_inc = calculate_giv_inc (pattern, insn, regno); - giv_dest_reg = SET_DEST (pattern); - giv_src_reg = SET_DEST (pattern); - - if (unroll_type == UNROLL_COMPLETELY) - { - /* Completely unrolling the loop. Set the induction - variable to a known constant value. */ - - /* The value in splittable_regs may be an invariant - value, so we must use plus_constant here. */ - splittable_regs[regno] - = plus_constant (splittable_regs[regno], INTVAL (giv_inc)); - - if (GET_CODE (splittable_regs[regno]) == PLUS) - { - giv_src_reg = XEXP (splittable_regs[regno], 0); - giv_inc = XEXP (splittable_regs[regno], 1); - } - else - { - /* The splittable_regs value must be a REG or a - CONST_INT, so put the entire value in the giv_src_reg - variable. */ - giv_src_reg = splittable_regs[regno]; - giv_inc = const0_rtx; - } - } - else - { - /* Partially unrolling loop. Create a new pseudo - register for the iteration variable, and set it to - be a constant plus the original register. Except - on the last iteration, when the result has to - go back into the original iteration var register. */ - - /* Handle bivs which must be mapped to a new register - when split. This happens for bivs which need their - final value set before loop entry. The new register - for the biv was stored in the biv's first struct - induction entry by find_splittable_regs. */ - - if (regno < max_reg_before_loop - && reg_iv_type[regno] == BASIC_INDUCT) - { - giv_src_reg = reg_biv_class[regno]->biv->src_reg; - giv_dest_reg = giv_src_reg; - } - -#if 0 - /* If non-reduced/final-value givs were split, then - this would have to remap those givs also. See - find_splittable_regs. */ -#endif - - splittable_regs[regno] - = GEN_INT (INTVAL (giv_inc) - + INTVAL (splittable_regs[regno])); - giv_inc = splittable_regs[regno]; - - /* Now split the induction variable by changing the dest - of this insn to a new register, and setting its - reg_map entry to point to this new register. - - If this is the last iteration, and this is the last insn - that will update the iv, then reuse the original dest, - to ensure that the iv will have the proper value when - the loop exits or repeats. - - Using splittable_regs_updates here like this is safe, - because it can only be greater than one if all - instructions modifying the iv are always executed in - order. */ - - if (! last_iteration - || (splittable_regs_updates[regno]-- != 1)) - { - tem = gen_reg_rtx (GET_MODE (giv_src_reg)); - giv_dest_reg = tem; - map->reg_map[regno] = tem; - } - else - map->reg_map[regno] = giv_src_reg; - } - - /* The constant being added could be too large for an add - immediate, so can't directly emit an insn here. */ - emit_unrolled_add (giv_dest_reg, giv_src_reg, giv_inc); - copy = get_last_insn (); - pattern = PATTERN (copy); - } - else - { - pattern = copy_rtx_and_substitute (pattern, map); - copy = emit_insn (pattern); - } - REG_NOTES (copy) = initial_reg_note_copy (REG_NOTES (insn), map); - -#ifdef HAVE_cc0 - /* If this insn is setting CC0, it may need to look at - the insn that uses CC0 to see what type of insn it is. - In that case, the call to recog via validate_change will - fail. So don't substitute constants here. Instead, - do it when we emit the following insn. - - For example, see the pyr.md file. That machine has signed and - unsigned compares. The compare patterns must check the - following branch insn to see which what kind of compare to - emit. - - If the previous insn set CC0, substitute constants on it as - well. */ - if (sets_cc0_p (copy) != 0) - cc0_insn = copy; - else - { - if (cc0_insn) - try_constants (cc0_insn, map); - cc0_insn = 0; - try_constants (copy, map); - } -#else - try_constants (copy, map); -#endif - - /* Make split induction variable constants `permanent' since we - know there are no backward branches across iteration variable - settings which would invalidate this. */ - if (dest_reg_was_split) - { - int regno = REGNO (SET_DEST (pattern)); - - if (map->const_age_map[regno] == map->const_age) - map->const_age_map[regno] = -1; - } - break; - - case JUMP_INSN: - pattern = copy_rtx_and_substitute (PATTERN (insn), map); - copy = emit_jump_insn (pattern); - REG_NOTES (copy) = initial_reg_note_copy (REG_NOTES (insn), map); - - if (JUMP_LABEL (insn) == start_label && insn == copy_end - && ! last_iteration) - { - /* This is a branch to the beginning of the loop; this is the - last insn being copied; and this is not the last iteration. - In this case, we want to change the original fall through - case to be a branch past the end of the loop, and the - original jump label case to fall_through. */ - - if (! invert_exp (pattern, copy) - || ! redirect_exp (&pattern, - map->label_map[CODE_LABEL_NUMBER - (JUMP_LABEL (insn))], - exit_label, copy)) - abort (); - } - -#ifdef HAVE_cc0 - if (cc0_insn) - try_constants (cc0_insn, map); - cc0_insn = 0; -#endif - try_constants (copy, map); - - /* Set the jump label of COPY correctly to avoid problems with - later passes of unroll_loop, if INSN had jump label set. */ - if (JUMP_LABEL (insn)) - { - rtx label = 0; - - /* Can't use the label_map for every insn, since this may be - the backward branch, and hence the label was not mapped. */ - if (GET_CODE (pattern) == SET) - { - tem = SET_SRC (pattern); - if (GET_CODE (tem) == LABEL_REF) - label = XEXP (tem, 0); - else if (GET_CODE (tem) == IF_THEN_ELSE) - { - if (XEXP (tem, 1) != pc_rtx) - label = XEXP (XEXP (tem, 1), 0); - else - label = XEXP (XEXP (tem, 2), 0); - } - } - - if (label && GET_CODE (label) == CODE_LABEL) - JUMP_LABEL (copy) = label; - else - { - /* An unrecognizable jump insn, probably the entry jump - for a switch statement. This label must have been mapped, - so just use the label_map to get the new jump label. */ - JUMP_LABEL (copy) = map->label_map[CODE_LABEL_NUMBER - (JUMP_LABEL (insn))]; - } - - /* If this is a non-local jump, then must increase the label - use count so that the label will not be deleted when the - original jump is deleted. */ - LABEL_NUSES (JUMP_LABEL (copy))++; - } - else if (GET_CODE (PATTERN (copy)) == ADDR_VEC - || GET_CODE (PATTERN (copy)) == ADDR_DIFF_VEC) - { - rtx pat = PATTERN (copy); - int diff_vec_p = GET_CODE (pat) == ADDR_DIFF_VEC; - int len = XVECLEN (pat, diff_vec_p); - int i; - - for (i = 0; i < len; i++) - LABEL_NUSES (XEXP (XVECEXP (pat, diff_vec_p, i), 0))++; - } - - /* If this used to be a conditional jump insn but whose branch - direction is now known, we must do something special. */ - if (condjump_p (insn) && !simplejump_p (insn) && map->last_pc_value) - { -#ifdef HAVE_cc0 - /* The previous insn set cc0 for us. So delete it. */ - delete_insn (PREV_INSN (copy)); -#endif - - /* If this is now a no-op, delete it. */ - if (map->last_pc_value == pc_rtx) - { - delete_insn (copy); - copy = 0; - } - else - /* Otherwise, this is unconditional jump so we must put a - BARRIER after it. We could do some dead code elimination - here, but jump.c will do it just as well. */ - emit_barrier (); - } - break; - - case CALL_INSN: - pattern = copy_rtx_and_substitute (PATTERN (insn), map); - copy = emit_call_insn (pattern); - REG_NOTES (copy) = initial_reg_note_copy (REG_NOTES (insn), map); - -#ifdef HAVE_cc0 - if (cc0_insn) - try_constants (cc0_insn, map); - cc0_insn = 0; -#endif - try_constants (copy, map); - - /* Be lazy and assume CALL_INSNs clobber all hard registers. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - map->const_equiv_map[i] = 0; - break; - - case CODE_LABEL: - /* If this is the loop start label, then we don't need to emit a - copy of this label since no one will use it. */ - - if (insn != start_label) - { - copy = emit_label (map->label_map[CODE_LABEL_NUMBER (insn)]); - map->const_age++; - } - break; - - case BARRIER: - copy = emit_barrier (); - break; - - case NOTE: - /* VTOP notes are valid only before the loop exit test. If placed - anywhere else, loop may generate bad code. */ - - if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED - && (NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_VTOP - || (last_iteration && unroll_type != UNROLL_COMPLETELY))) - copy = emit_note (NOTE_SOURCE_FILE (insn), - NOTE_LINE_NUMBER (insn)); - else - copy = 0; - break; - - default: - abort (); - break; - } - - map->insn_map[INSN_UID (insn)] = copy; - } - while (insn != copy_end); - - /* Now finish coping the REG_NOTES. */ - insn = copy_start; - do - { - insn = NEXT_INSN (insn); - if ((GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN - || GET_CODE (insn) == CALL_INSN) - && map->insn_map[INSN_UID (insn)]) - final_reg_note_copy (REG_NOTES (map->insn_map[INSN_UID (insn)]), map); - } - while (insn != copy_end); - - /* There may be notes between copy_notes_from and loop_end. Emit a copy of - each of these notes here, since there may be some important ones, such as - NOTE_INSN_BLOCK_END notes, in this group. We don't do this on the last - iteration, because the original notes won't be deleted. - - We can't use insert_before here, because when from preconditioning, - insert_before points before the loop. We can't use copy_end, because - there may be insns already inserted after it (which we don't want to - copy) when not from preconditioning code. */ - - if (! last_iteration) - { - for (insn = copy_notes_from; insn != loop_end; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED) - emit_note (NOTE_SOURCE_FILE (insn), NOTE_LINE_NUMBER (insn)); - } - } - - if (final_label && LABEL_NUSES (final_label) > 0) - emit_label (final_label); - - tem = gen_sequence (); - end_sequence (); - emit_insn_before (tem, insert_before); -} - -/* Emit an insn, using the expand_binop to ensure that a valid insn is - emitted. This will correctly handle the case where the increment value - won't fit in the immediate field of a PLUS insns. */ - -void -emit_unrolled_add (dest_reg, src_reg, increment) - rtx dest_reg, src_reg, increment; -{ - rtx result; - - result = expand_binop (GET_MODE (dest_reg), add_optab, src_reg, increment, - dest_reg, 0, OPTAB_LIB_WIDEN); - - if (dest_reg != result) - emit_move_insn (dest_reg, result); -} - -/* Searches the insns between INSN and LOOP_END. Returns 1 if there - is a backward branch in that range that branches to somewhere between - LOOP_START and INSN. Returns 0 otherwise. */ - -/* ??? This is quadratic algorithm. Could be rewritten to be linear. - In practice, this is not a problem, because this function is seldom called, - and uses a negligible amount of CPU time on average. */ - -static int -back_branch_in_range_p (insn, loop_start, loop_end) - rtx insn; - rtx loop_start, loop_end; -{ - rtx p, q, target_insn; - - /* Stop before we get to the backward branch at the end of the loop. */ - loop_end = prev_nonnote_insn (loop_end); - if (GET_CODE (loop_end) == BARRIER) - loop_end = PREV_INSN (loop_end); - - /* Check in case insn has been deleted, search forward for first non - deleted insn following it. */ - while (INSN_DELETED_P (insn)) - insn = NEXT_INSN (insn); - - /* Check for the case where insn is the last insn in the loop. */ - if (insn == loop_end) - return 0; - - for (p = NEXT_INSN (insn); p != loop_end; p = NEXT_INSN (p)) - { - if (GET_CODE (p) == JUMP_INSN) - { - target_insn = JUMP_LABEL (p); - - /* Search from loop_start to insn, to see if one of them is - the target_insn. We can't use INSN_LUID comparisons here, - since insn may not have an LUID entry. */ - for (q = loop_start; q != insn; q = NEXT_INSN (q)) - if (q == target_insn) - return 1; - } - } - - return 0; -} - -/* Try to generate the simplest rtx for the expression - (PLUS (MULT mult1 mult2) add1). This is used to calculate the initial - value of giv's. */ - -static rtx -fold_rtx_mult_add (mult1, mult2, add1, mode) - rtx mult1, mult2, add1; - enum machine_mode mode; -{ - rtx temp, mult_res; - rtx result; - - /* The modes must all be the same. This should always be true. For now, - check to make sure. */ - if ((GET_MODE (mult1) != mode && GET_MODE (mult1) != VOIDmode) - || (GET_MODE (mult2) != mode && GET_MODE (mult2) != VOIDmode) - || (GET_MODE (add1) != mode && GET_MODE (add1) != VOIDmode)) - abort (); - - /* Ensure that if at least one of mult1/mult2 are constant, then mult2 - will be a constant. */ - if (GET_CODE (mult1) == CONST_INT) - { - temp = mult2; - mult2 = mult1; - mult1 = temp; - } - - mult_res = simplify_binary_operation (MULT, mode, mult1, mult2); - if (! mult_res) - mult_res = gen_rtx (MULT, mode, mult1, mult2); - - /* Again, put the constant second. */ - if (GET_CODE (add1) == CONST_INT) - { - temp = add1; - add1 = mult_res; - mult_res = temp; - } - - result = simplify_binary_operation (PLUS, mode, add1, mult_res); - if (! result) - result = gen_rtx (PLUS, mode, add1, mult_res); - - return result; -} - -/* Searches the list of induction struct's for the biv BL, to try to calculate - the total increment value for one iteration of the loop as a constant. - - Returns the increment value as an rtx, simplified as much as possible, - if it can be calculated. Otherwise, returns 0. */ - -rtx -biv_total_increment (bl, loop_start, loop_end) - struct iv_class *bl; - rtx loop_start, loop_end; -{ - struct induction *v; - rtx result; - - /* For increment, must check every instruction that sets it. Each - instruction must be executed only once each time through the loop. - To verify this, we check that the the insn is always executed, and that - there are no backward branches after the insn that branch to before it. - Also, the insn must have a mult_val of one (to make sure it really is - an increment). */ - - result = const0_rtx; - for (v = bl->biv; v; v = v->next_iv) - { - if (v->always_computable && v->mult_val == const1_rtx - && ! back_branch_in_range_p (v->insn, loop_start, loop_end)) - result = fold_rtx_mult_add (result, const1_rtx, v->add_val, v->mode); - else - return 0; - } - - return result; -} - -/* Determine the initial value of the iteration variable, and the amount - that it is incremented each loop. Use the tables constructed by - the strength reduction pass to calculate these values. - - Initial_value and/or increment are set to zero if their values could not - be calculated. */ - -static void -iteration_info (iteration_var, initial_value, increment, loop_start, loop_end) - rtx iteration_var, *initial_value, *increment; - rtx loop_start, loop_end; -{ - struct iv_class *bl; - struct induction *v, *b; - - /* Clear the result values, in case no answer can be found. */ - *initial_value = 0; - *increment = 0; - - /* The iteration variable can be either a giv or a biv. Check to see - which it is, and compute the variable's initial value, and increment - value if possible. */ - - /* If this is a new register, can't handle it since we don't have any - reg_iv_type entry for it. */ - if (REGNO (iteration_var) >= max_reg_before_loop) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: No reg_iv_type entry for iteration var.\n"); - return; - } - /* Reject iteration variables larger than the host long size, since they - could result in a number of iterations greater than the range of our - `unsigned long' variable loop_n_iterations. */ - else if (GET_MODE_BITSIZE (GET_MODE (iteration_var)) > HOST_BITS_PER_LONG) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: Iteration var rejected because mode larger than host long.\n"); - return; - } - else if (GET_MODE_CLASS (GET_MODE (iteration_var)) != MODE_INT) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: Iteration var not an integer.\n"); - return; - } - else if (reg_iv_type[REGNO (iteration_var)] == BASIC_INDUCT) - { - /* Grab initial value, only useful if it is a constant. */ - bl = reg_biv_class[REGNO (iteration_var)]; - *initial_value = bl->initial_value; - - *increment = biv_total_increment (bl, loop_start, loop_end); - } - else if (reg_iv_type[REGNO (iteration_var)] == GENERAL_INDUCT) - { -#if 1 - /* ??? The code below does not work because the incorrect number of - iterations is calculated when the biv is incremented after the giv - is set (which is the usual case). This can probably be accounted - for by biasing the initial_value by subtracting the amount of the - increment that occurs between the giv set and the giv test. However, - a giv as an iterator is very rare, so it does not seem worthwhile - to handle this. */ - /* ??? An example failure is: i = 6; do {;} while (i++ < 9). */ - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: Giv iterators are not handled.\n"); - return; -#else - /* Initial value is mult_val times the biv's initial value plus - add_val. Only useful if it is a constant. */ - v = reg_iv_info[REGNO (iteration_var)]; - bl = reg_biv_class[REGNO (v->src_reg)]; - *initial_value = fold_rtx_mult_add (v->mult_val, bl->initial_value, - v->add_val, v->mode); - - /* Increment value is mult_val times the increment value of the biv. */ - - *increment = biv_total_increment (bl, loop_start, loop_end); - if (*increment) - *increment = fold_rtx_mult_add (v->mult_val, *increment, const0_rtx, - v->mode); -#endif - } - else - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: Not basic or general induction var.\n"); - return; - } -} - -/* Calculate the approximate final value of the iteration variable - which has an loop exit test with code COMPARISON_CODE and comparison value - of COMPARISON_VALUE. Also returns an indication of whether the comparison - was signed or unsigned, and the direction of the comparison. This info is - needed to calculate the number of loop iterations. */ - -static rtx -approx_final_value (comparison_code, comparison_value, unsigned_p, compare_dir) - enum rtx_code comparison_code; - rtx comparison_value; - int *unsigned_p; - int *compare_dir; -{ - /* Calculate the final value of the induction variable. - The exact final value depends on the branch operator, and increment sign. - This is only an approximate value. It will be wrong if the iteration - variable is not incremented by one each time through the loop, and - approx final value - start value % increment != 0. */ - - *unsigned_p = 0; - switch (comparison_code) - { - case LEU: - *unsigned_p = 1; - case LE: - *compare_dir = 1; - return plus_constant (comparison_value, 1); - case GEU: - *unsigned_p = 1; - case GE: - *compare_dir = -1; - return plus_constant (comparison_value, -1); - case EQ: - /* Can not calculate a final value for this case. */ - *compare_dir = 0; - return 0; - case LTU: - *unsigned_p = 1; - case LT: - *compare_dir = 1; - return comparison_value; - break; - case GTU: - *unsigned_p = 1; - case GT: - *compare_dir = -1; - return comparison_value; - case NE: - *compare_dir = 0; - return comparison_value; - default: - abort (); - } -} - -/* For each biv and giv, determine whether it can be safely split into - a different variable for each unrolled copy of the loop body. If it - is safe to split, then indicate that by saving some useful info - in the splittable_regs array. - - If the loop is being completely unrolled, then splittable_regs will hold - the current value of the induction variable while the loop is unrolled. - It must be set to the initial value of the induction variable here. - Otherwise, splittable_regs will hold the difference between the current - value of the induction variable and the value the induction variable had - at the top of the loop. It must be set to the value 0 here. */ - -/* ?? If the loop is only unrolled twice, then most of the restrictions to - constant values are unnecessary, since we can easily calculate increment - values in this case even if nothing is constant. The increment value - should not involve a multiply however. */ - -/* ?? Even if the biv/giv increment values aren't constant, it may still - be beneficial to split the variable if the loop is only unrolled a few - times, since multiplies by small integers (1,2,3,4) are very cheap. */ - -static int -find_splittable_regs (unroll_type, loop_start, loop_end, end_insert_before, - unroll_number) - enum unroll_types unroll_type; - rtx loop_start, loop_end; - rtx end_insert_before; - int unroll_number; -{ - struct iv_class *bl; - struct induction *v; - rtx increment, tem; - rtx biv_final_value; - int biv_splittable; - int result = 0; - - for (bl = loop_iv_list; bl; bl = bl->next) - { - /* Biv_total_increment must return a constant value, - otherwise we can not calculate the split values. */ - - increment = biv_total_increment (bl, loop_start, loop_end); - if (! increment || GET_CODE (increment) != CONST_INT) - continue; - - /* The loop must be unrolled completely, or else have a known number - of iterations and only one exit, or else the biv must be dead - outside the loop, or else the final value must be known. Otherwise, - it is unsafe to split the biv since it may not have the proper - value on loop exit. */ - - /* loop_number_exit_labels is non-zero if the loop has an exit other than - a fall through at the end. */ - - biv_splittable = 1; - biv_final_value = 0; - if (unroll_type != UNROLL_COMPLETELY - && (loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]] - || unroll_type == UNROLL_NAIVE) - && (uid_luid[regno_last_uid[bl->regno]] >= INSN_LUID (loop_end) - || ! bl->init_insn - || INSN_UID (bl->init_insn) >= max_uid_for_loop - || (uid_luid[regno_first_uid[bl->regno]] - < INSN_LUID (bl->init_insn)) - || reg_mentioned_p (bl->biv->dest_reg, SET_SRC (bl->init_set))) - && ! (biv_final_value = final_biv_value (bl, loop_start, loop_end))) - biv_splittable = 0; - - /* If any of the insns setting the BIV don't do so with a simple - PLUS, we don't know how to split it. */ - for (v = bl->biv; biv_splittable && v; v = v->next_iv) - if ((tem = single_set (v->insn)) == 0 - || GET_CODE (SET_DEST (tem)) != REG - || REGNO (SET_DEST (tem)) != bl->regno - || GET_CODE (SET_SRC (tem)) != PLUS) - biv_splittable = 0; - - /* If final value is non-zero, then must emit an instruction which sets - the value of the biv to the proper value. This is done after - handling all of the givs, since some of them may need to use the - biv's value in their initialization code. */ - - /* This biv is splittable. If completely unrolling the loop, save - the biv's initial value. Otherwise, save the constant zero. */ - - if (biv_splittable == 1) - { - if (unroll_type == UNROLL_COMPLETELY) - { - /* If the initial value of the biv is itself (i.e. it is too - complicated for strength_reduce to compute), or is a hard - register, then we must create a new pseudo reg to hold the - initial value of the biv. */ - - if (GET_CODE (bl->initial_value) == REG - && (REGNO (bl->initial_value) == bl->regno - || REGNO (bl->initial_value) < FIRST_PSEUDO_REGISTER)) - { - rtx tem = gen_reg_rtx (bl->biv->mode); - - emit_insn_before (gen_move_insn (tem, bl->biv->src_reg), - loop_start); - - if (loop_dump_stream) - fprintf (loop_dump_stream, "Biv %d initial value remapped to %d.\n", - bl->regno, REGNO (tem)); - - splittable_regs[bl->regno] = tem; - } - else - splittable_regs[bl->regno] = bl->initial_value; - } - else - splittable_regs[bl->regno] = const0_rtx; - - /* Save the number of instructions that modify the biv, so that - we can treat the last one specially. */ - - splittable_regs_updates[bl->regno] = bl->biv_count; - - result++; - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Biv %d safe to split.\n", bl->regno); - } - - /* Check every giv that depends on this biv to see whether it is - splittable also. Even if the biv isn't splittable, givs which - depend on it may be splittable if the biv is live outside the - loop, and the givs aren't. */ - - result = find_splittable_givs (bl, unroll_type, loop_start, loop_end, - increment, unroll_number, result); - - /* If final value is non-zero, then must emit an instruction which sets - the value of the biv to the proper value. This is done after - handling all of the givs, since some of them may need to use the - biv's value in their initialization code. */ - if (biv_final_value) - { - /* If the loop has multiple exits, emit the insns before the - loop to ensure that it will always be executed no matter - how the loop exits. Otherwise emit the insn after the loop, - since this is slightly more efficient. */ - if (! loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]]) - emit_insn_before (gen_move_insn (bl->biv->src_reg, - biv_final_value), - end_insert_before); - else - { - /* Create a new register to hold the value of the biv, and then - set the biv to its final value before the loop start. The biv - is set to its final value before loop start to ensure that - this insn will always be executed, no matter how the loop - exits. */ - rtx tem = gen_reg_rtx (bl->biv->mode); - emit_insn_before (gen_move_insn (tem, bl->biv->src_reg), - loop_start); - emit_insn_before (gen_move_insn (bl->biv->src_reg, - biv_final_value), - loop_start); - - if (loop_dump_stream) - fprintf (loop_dump_stream, "Biv %d mapped to %d for split.\n", - REGNO (bl->biv->src_reg), REGNO (tem)); - - /* Set up the mapping from the original biv register to the new - register. */ - bl->biv->src_reg = tem; - } - } - } - return result; -} - -/* For every giv based on the biv BL, check to determine whether it is - splittable. This is a subroutine to find_splittable_regs (). */ - -static int -find_splittable_givs (bl, unroll_type, loop_start, loop_end, increment, - unroll_number, result) - struct iv_class *bl; - enum unroll_types unroll_type; - rtx loop_start, loop_end; - rtx increment; - int unroll_number, result; -{ - struct induction *v; - rtx final_value; - rtx tem; - - for (v = bl->giv; v; v = v->next_iv) - { - rtx giv_inc, value; - - /* Only split the giv if it has already been reduced, or if the loop is - being completely unrolled. */ - if (unroll_type != UNROLL_COMPLETELY && v->ignore) - continue; - - /* The giv can be split if the insn that sets the giv is executed once - and only once on every iteration of the loop. */ - /* An address giv can always be split. v->insn is just a use not a set, - and hence it does not matter whether it is always executed. All that - matters is that all the biv increments are always executed, and we - won't reach here if they aren't. */ - if (v->giv_type != DEST_ADDR - && (! v->always_computable - || back_branch_in_range_p (v->insn, loop_start, loop_end))) - continue; - - /* The giv increment value must be a constant. */ - giv_inc = fold_rtx_mult_add (v->mult_val, increment, const0_rtx, - v->mode); - if (! giv_inc || GET_CODE (giv_inc) != CONST_INT) - continue; - - /* The loop must be unrolled completely, or else have a known number of - iterations and only one exit, or else the giv must be dead outside - the loop, or else the final value of the giv must be known. - Otherwise, it is not safe to split the giv since it may not have the - proper value on loop exit. */ - - /* The used outside loop test will fail for DEST_ADDR givs. They are - never used outside the loop anyways, so it is always safe to split a - DEST_ADDR giv. */ - - final_value = 0; - if (unroll_type != UNROLL_COMPLETELY - && (loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]] - || unroll_type == UNROLL_NAIVE) - && v->giv_type != DEST_ADDR - && ((regno_first_uid[REGNO (v->dest_reg)] != INSN_UID (v->insn) - /* Check for the case where the pseudo is set by a shift/add - sequence, in which case the first insn setting the pseudo - is the first insn of the shift/add sequence. */ - && (! (tem = find_reg_note (v->insn, REG_RETVAL, NULL_RTX)) - || (regno_first_uid[REGNO (v->dest_reg)] - != INSN_UID (XEXP (tem, 0))))) - /* Line above always fails if INSN was moved by loop opt. */ - || (uid_luid[regno_last_uid[REGNO (v->dest_reg)]] - >= INSN_LUID (loop_end))) - && ! (final_value = v->final_value)) - continue; - -#if 0 - /* Currently, non-reduced/final-value givs are never split. */ - /* Should emit insns after the loop if possible, as the biv final value - code below does. */ - - /* If the final value is non-zero, and the giv has not been reduced, - then must emit an instruction to set the final value. */ - if (final_value && !v->new_reg) - { - /* Create a new register to hold the value of the giv, and then set - the giv to its final value before the loop start. The giv is set - to its final value before loop start to ensure that this insn - will always be executed, no matter how we exit. */ - tem = gen_reg_rtx (v->mode); - emit_insn_before (gen_move_insn (tem, v->dest_reg), loop_start); - emit_insn_before (gen_move_insn (v->dest_reg, final_value), - loop_start); - - if (loop_dump_stream) - fprintf (loop_dump_stream, "Giv %d mapped to %d for split.\n", - REGNO (v->dest_reg), REGNO (tem)); - - v->src_reg = tem; - } -#endif - - /* This giv is splittable. If completely unrolling the loop, save the - giv's initial value. Otherwise, save the constant zero for it. */ - - if (unroll_type == UNROLL_COMPLETELY) - { - /* It is not safe to use bl->initial_value here, because it may not - be invariant. It is safe to use the initial value stored in - the splittable_regs array if it is set. In rare cases, it won't - be set, so then we do exactly the same thing as - find_splittable_regs does to get a safe value. */ - rtx biv_initial_value; - - if (splittable_regs[bl->regno]) - biv_initial_value = splittable_regs[bl->regno]; - else if (GET_CODE (bl->initial_value) != REG - || (REGNO (bl->initial_value) != bl->regno - && REGNO (bl->initial_value) >= FIRST_PSEUDO_REGISTER)) - biv_initial_value = bl->initial_value; - else - { - rtx tem = gen_reg_rtx (bl->biv->mode); - - emit_insn_before (gen_move_insn (tem, bl->biv->src_reg), - loop_start); - biv_initial_value = tem; - } - value = fold_rtx_mult_add (v->mult_val, biv_initial_value, - v->add_val, v->mode); - } - else - value = const0_rtx; - - if (v->new_reg) - { - /* If a giv was combined with another giv, then we can only split - this giv if the giv it was combined with was reduced. This - is because the value of v->new_reg is meaningless in this - case. */ - if (v->same && ! v->same->new_reg) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "giv combined with unreduced giv not split.\n"); - continue; - } - /* If the giv is an address destination, it could be something other - than a simple register, these have to be treated differently. */ - else if (v->giv_type == DEST_REG) - { - /* If value is not a constant, register, or register plus - constant, then compute its value into a register before - loop start. This prevents illegal rtx sharing, and should - generate better code. We can use bl->initial_value here - instead of splittable_regs[bl->regno] because this code - is going before the loop start. */ - if (unroll_type == UNROLL_COMPLETELY - && GET_CODE (value) != CONST_INT - && GET_CODE (value) != REG - && (GET_CODE (value) != PLUS - || GET_CODE (XEXP (value, 0)) != REG - || GET_CODE (XEXP (value, 1)) != CONST_INT)) - { - rtx tem = gen_reg_rtx (v->mode); - emit_iv_add_mult (bl->initial_value, v->mult_val, - v->add_val, tem, loop_start); - value = tem; - } - - splittable_regs[REGNO (v->new_reg)] = value; - } - else - { - /* Splitting address givs is useful since it will often allow us - to eliminate some increment insns for the base giv as - unnecessary. */ - - /* If the addr giv is combined with a dest_reg giv, then all - references to that dest reg will be remapped, which is NOT - what we want for split addr regs. We always create a new - register for the split addr giv, just to be safe. */ - - /* ??? If there are multiple address givs which have been - combined with the same dest_reg giv, then we may only need - one new register for them. Pulling out constants below will - catch some of the common cases of this. Currently, I leave - the work of simplifying multiple address givs to the - following cse pass. */ - - v->const_adjust = 0; - if (unroll_type != UNROLL_COMPLETELY) - { - /* If not completely unrolling the loop, then create a new - register to hold the split value of the DEST_ADDR giv. - Emit insn to initialize its value before loop start. */ - tem = gen_reg_rtx (v->mode); - - /* If the address giv has a constant in its new_reg value, - then this constant can be pulled out and put in value, - instead of being part of the initialization code. */ - - if (GET_CODE (v->new_reg) == PLUS - && GET_CODE (XEXP (v->new_reg, 1)) == CONST_INT) - { - v->dest_reg - = plus_constant (tem, INTVAL (XEXP (v->new_reg,1))); - - /* Only succeed if this will give valid addresses. - Try to validate both the first and the last - address resulting from loop unrolling, if - one fails, then can't do const elim here. */ - if (memory_address_p (v->mem_mode, v->dest_reg) - && memory_address_p (v->mem_mode, - plus_constant (v->dest_reg, - INTVAL (giv_inc) - * (unroll_number - 1)))) - { - /* Save the negative of the eliminated const, so - that we can calculate the dest_reg's increment - value later. */ - v->const_adjust = - INTVAL (XEXP (v->new_reg, 1)); - - v->new_reg = XEXP (v->new_reg, 0); - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Eliminating constant from giv %d\n", - REGNO (tem)); - } - else - v->dest_reg = tem; - } - else - v->dest_reg = tem; - - /* If the address hasn't been checked for validity yet, do so - now, and fail completely if either the first or the last - unrolled copy of the address is not a valid address. */ - if (v->dest_reg == tem - && (! memory_address_p (v->mem_mode, v->dest_reg) - || ! memory_address_p (v->mem_mode, - plus_constant (v->dest_reg, - INTVAL (giv_inc) - * (unroll_number -1))))) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Illegal address for giv at insn %d\n", - INSN_UID (v->insn)); - continue; - } - - /* To initialize the new register, just move the value of - new_reg into it. This is not guaranteed to give a valid - instruction on machines with complex addressing modes. - If we can't recognize it, then delete it and emit insns - to calculate the value from scratch. */ - emit_insn_before (gen_rtx (SET, VOIDmode, tem, - copy_rtx (v->new_reg)), - loop_start); - if (recog_memoized (PREV_INSN (loop_start)) < 0) - { - delete_insn (PREV_INSN (loop_start)); - emit_iv_add_mult (bl->initial_value, v->mult_val, - v->add_val, tem, loop_start); - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Illegal init insn, rewritten.\n"); - } - } - else - { - v->dest_reg = value; - - /* Check the resulting address for validity, and fail - if the resulting address would be illegal. */ - if (! memory_address_p (v->mem_mode, v->dest_reg) - || ! memory_address_p (v->mem_mode, - plus_constant (v->dest_reg, - INTVAL (giv_inc) * - (unroll_number -1)))) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Illegal address for giv at insn %d\n", - INSN_UID (v->insn)); - continue; - } - } - - /* Store the value of dest_reg into the insn. This sharing - will not be a problem as this insn will always be copied - later. */ - - *v->location = v->dest_reg; - - /* If this address giv is combined with a dest reg giv, then - save the base giv's induction pointer so that we will be - able to handle this address giv properly. The base giv - itself does not have to be splittable. */ - - if (v->same && v->same->giv_type == DEST_REG) - addr_combined_regs[REGNO (v->same->new_reg)] = v->same; - - if (GET_CODE (v->new_reg) == REG) - { - /* This giv maybe hasn't been combined with any others. - Make sure that it's giv is marked as splittable here. */ - - splittable_regs[REGNO (v->new_reg)] = value; - - /* Make it appear to depend upon itself, so that the - giv will be properly split in the main loop above. */ - if (! v->same) - { - v->same = v; - addr_combined_regs[REGNO (v->new_reg)] = v; - } - } - - if (loop_dump_stream) - fprintf (loop_dump_stream, "DEST_ADDR giv being split.\n"); - } - } - else - { -#if 0 - /* Currently, unreduced giv's can't be split. This is not too much - of a problem since unreduced giv's are not live across loop - iterations anyways. When unrolling a loop completely though, - it makes sense to reduce&split givs when possible, as this will - result in simpler instructions, and will not require that a reg - be live across loop iterations. */ - - splittable_regs[REGNO (v->dest_reg)] = value; - fprintf (stderr, "Giv %d at insn %d not reduced\n", - REGNO (v->dest_reg), INSN_UID (v->insn)); -#else - continue; -#endif - } - - /* Givs are only updated once by definition. Mark it so if this is - a splittable register. Don't need to do anything for address givs - where this may not be a register. */ - - if (GET_CODE (v->new_reg) == REG) - splittable_regs_updates[REGNO (v->new_reg)] = 1; - - result++; - - if (loop_dump_stream) - { - int regnum; - - if (GET_CODE (v->dest_reg) == CONST_INT) - regnum = -1; - else if (GET_CODE (v->dest_reg) != REG) - regnum = REGNO (XEXP (v->dest_reg, 0)); - else - regnum = REGNO (v->dest_reg); - fprintf (loop_dump_stream, "Giv %d at insn %d safe to split.\n", - regnum, INSN_UID (v->insn)); - } - } - - return result; -} - -/* Try to prove that the register is dead after the loop exits. Trace every - loop exit looking for an insn that will always be executed, which sets - the register to some value, and appears before the first use of the register - is found. If successful, then return 1, otherwise return 0. */ - -/* ?? Could be made more intelligent in the handling of jumps, so that - it can search past if statements and other similar structures. */ - -static int -reg_dead_after_loop (reg, loop_start, loop_end) - rtx reg, loop_start, loop_end; -{ - rtx insn, label; - enum rtx_code code; - int jump_count = 0; - - /* HACK: Must also search the loop fall through exit, create a label_ref - here which points to the loop_end, and append the loop_number_exit_labels - list to it. */ - label = gen_rtx (LABEL_REF, VOIDmode, loop_end); - LABEL_NEXTREF (label) - = loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]]; - - for ( ; label; label = LABEL_NEXTREF (label)) - { - /* Succeed if find an insn which sets the biv or if reach end of - function. Fail if find an insn that uses the biv, or if come to - a conditional jump. */ - - insn = NEXT_INSN (XEXP (label, 0)); - while (insn) - { - code = GET_CODE (insn); - if (GET_RTX_CLASS (code) == 'i') - { - rtx set; - - if (reg_referenced_p (reg, PATTERN (insn))) - return 0; - - set = single_set (insn); - if (set && rtx_equal_p (SET_DEST (set), reg)) - break; - } - - if (code == JUMP_INSN) - { - if (GET_CODE (PATTERN (insn)) == RETURN) - break; - else if (! simplejump_p (insn) - /* Prevent infinite loop following infinite loops. */ - || jump_count++ > 20) - return 0; - else - insn = JUMP_LABEL (insn); - } - - insn = NEXT_INSN (insn); - } - } - - /* Success, the register is dead on all loop exits. */ - return 1; -} - -/* Try to calculate the final value of the biv, the value it will have at - the end of the loop. If we can do it, return that value. */ - -rtx -final_biv_value (bl, loop_start, loop_end) - struct iv_class *bl; - rtx loop_start, loop_end; -{ - rtx increment, tem; - - /* ??? This only works for MODE_INT biv's. Reject all others for now. */ - - if (GET_MODE_CLASS (bl->biv->mode) != MODE_INT) - return 0; - - /* The final value for reversed bivs must be calculated differently than - for ordinary bivs. In this case, there is already an insn after the - loop which sets this biv's final value (if necessary), and there are - no other loop exits, so we can return any value. */ - if (bl->reversed) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Final biv value for %d, reversed biv.\n", bl->regno); - - return const0_rtx; - } - - /* Try to calculate the final value as initial value + (number of iterations - * increment). For this to work, increment must be invariant, the only - exit from the loop must be the fall through at the bottom (otherwise - it may not have its final value when the loop exits), and the initial - value of the biv must be invariant. */ - - if (loop_n_iterations != 0 - && ! loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]] - && invariant_p (bl->initial_value)) - { - increment = biv_total_increment (bl, loop_start, loop_end); - - if (increment && invariant_p (increment)) - { - /* Can calculate the loop exit value, emit insns after loop - end to calculate this value into a temporary register in - case it is needed later. */ - - tem = gen_reg_rtx (bl->biv->mode); - /* Make sure loop_end is not the last insn. */ - if (NEXT_INSN (loop_end) == 0) - emit_note_after (NOTE_INSN_DELETED, loop_end); - emit_iv_add_mult (increment, GEN_INT (loop_n_iterations), - bl->initial_value, tem, NEXT_INSN (loop_end)); - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Final biv value for %d, calculated.\n", bl->regno); - - return tem; - } - } - - /* Check to see if the biv is dead at all loop exits. */ - if (reg_dead_after_loop (bl->biv->src_reg, loop_start, loop_end)) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Final biv value for %d, biv dead after loop exit.\n", - bl->regno); - - return const0_rtx; - } - - return 0; -} - -/* Try to calculate the final value of the giv, the value it will have at - the end of the loop. If we can do it, return that value. */ - -rtx -final_giv_value (v, loop_start, loop_end) - struct induction *v; - rtx loop_start, loop_end; -{ - struct iv_class *bl; - rtx insn; - rtx increment, tem; - enum rtx_code code; - rtx insert_before, seq; - - bl = reg_biv_class[REGNO (v->src_reg)]; - - /* The final value for givs which depend on reversed bivs must be calculated - differently than for ordinary givs. In this case, there is already an - insn after the loop which sets this giv's final value (if necessary), - and there are no other loop exits, so we can return any value. */ - if (bl->reversed) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Final giv value for %d, depends on reversed biv\n", - REGNO (v->dest_reg)); - return const0_rtx; - } - - /* Try to calculate the final value as a function of the biv it depends - upon. The only exit from the loop must be the fall through at the bottom - (otherwise it may not have its final value when the loop exits). */ - - /* ??? Can calculate the final giv value by subtracting off the - extra biv increments times the giv's mult_val. The loop must have - only one exit for this to work, but the loop iterations does not need - to be known. */ - - if (loop_n_iterations != 0 - && ! loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]]) - { - /* ?? It is tempting to use the biv's value here since these insns will - be put after the loop, and hence the biv will have its final value - then. However, this fails if the biv is subsequently eliminated. - Perhaps determine whether biv's are eliminable before trying to - determine whether giv's are replaceable so that we can use the - biv value here if it is not eliminable. */ - - increment = biv_total_increment (bl, loop_start, loop_end); - - if (increment && invariant_p (increment)) - { - /* Can calculate the loop exit value of its biv as - (loop_n_iterations * increment) + initial_value */ - - /* The loop exit value of the giv is then - (final_biv_value - extra increments) * mult_val + add_val. - The extra increments are any increments to the biv which - occur in the loop after the giv's value is calculated. - We must search from the insn that sets the giv to the end - of the loop to calculate this value. */ - - insert_before = NEXT_INSN (loop_end); - - /* Put the final biv value in tem. */ - tem = gen_reg_rtx (bl->biv->mode); - emit_iv_add_mult (increment, GEN_INT (loop_n_iterations), - bl->initial_value, tem, insert_before); - - /* Subtract off extra increments as we find them. */ - for (insn = NEXT_INSN (v->insn); insn != loop_end; - insn = NEXT_INSN (insn)) - { - struct induction *biv; - - for (biv = bl->biv; biv; biv = biv->next_iv) - if (biv->insn == insn) - { - start_sequence (); - tem = expand_binop (GET_MODE (tem), sub_optab, tem, - biv->add_val, NULL_RTX, 0, - OPTAB_LIB_WIDEN); - seq = gen_sequence (); - end_sequence (); - emit_insn_before (seq, insert_before); - } - } - - /* Now calculate the giv's final value. */ - emit_iv_add_mult (tem, v->mult_val, v->add_val, tem, - insert_before); - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Final giv value for %d, calc from biv's value.\n", - REGNO (v->dest_reg)); - - return tem; - } - } - - /* Replaceable giv's should never reach here. */ - if (v->replaceable) - abort (); - - /* Check to see if the biv is dead at all loop exits. */ - if (reg_dead_after_loop (v->dest_reg, loop_start, loop_end)) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Final giv value for %d, giv dead after loop exit.\n", - REGNO (v->dest_reg)); - - return const0_rtx; - } - - return 0; -} - - -/* Calculate the number of loop iterations. Returns the exact number of loop - iterations if it can be calculated, otherwise returns zero. */ - -unsigned HOST_WIDE_INT -loop_iterations (loop_start, loop_end) - rtx loop_start, loop_end; -{ - rtx comparison, comparison_value; - rtx iteration_var, initial_value, increment, final_value; - enum rtx_code comparison_code; - HOST_WIDE_INT i; - int increment_dir; - int unsigned_compare, compare_dir, final_larger; - unsigned long tempu; - rtx last_loop_insn; - - /* First find the iteration variable. If the last insn is a conditional - branch, and the insn before tests a register value, make that the - iteration variable. */ - - loop_initial_value = 0; - loop_increment = 0; - loop_final_value = 0; - loop_iteration_var = 0; - - last_loop_insn = prev_nonnote_insn (loop_end); - - comparison = get_condition_for_loop (last_loop_insn); - if (comparison == 0) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: No final conditional branch found.\n"); - return 0; - } - - /* ??? Get_condition may switch position of induction variable and - invariant register when it canonicalizes the comparison. */ - - comparison_code = GET_CODE (comparison); - iteration_var = XEXP (comparison, 0); - comparison_value = XEXP (comparison, 1); - - if (GET_CODE (iteration_var) != REG) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: Comparison not against register.\n"); - return 0; - } - - /* Loop iterations is always called before any new registers are created - now, so this should never occur. */ - - if (REGNO (iteration_var) >= max_reg_before_loop) - abort (); - - iteration_info (iteration_var, &initial_value, &increment, - loop_start, loop_end); - if (initial_value == 0) - /* iteration_info already printed a message. */ - return 0; - - if (increment == 0) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: Increment value can't be calculated.\n"); - return 0; - } - if (GET_CODE (increment) != CONST_INT) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: Increment value not constant.\n"); - return 0; - } - if (GET_CODE (initial_value) != CONST_INT) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: Initial value not constant.\n"); - return 0; - } - - /* If the comparison value is an invariant register, then try to find - its value from the insns before the start of the loop. */ - - if (GET_CODE (comparison_value) == REG && invariant_p (comparison_value)) - { - rtx insn, set; - - for (insn = PREV_INSN (loop_start); insn ; insn = PREV_INSN (insn)) - { - if (GET_CODE (insn) == CODE_LABEL) - break; - - else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && reg_set_p (comparison_value, insn)) - { - /* We found the last insn before the loop that sets the register. - If it sets the entire register, and has a REG_EQUAL note, - then use the value of the REG_EQUAL note. */ - if ((set = single_set (insn)) - && (SET_DEST (set) == comparison_value)) - { - rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX); - - if (note && GET_CODE (XEXP (note, 0)) != EXPR_LIST) - comparison_value = XEXP (note, 0); - } - break; - } - } - } - - final_value = approx_final_value (comparison_code, comparison_value, - &unsigned_compare, &compare_dir); - - /* Save the calculated values describing this loop's bounds, in case - precondition_loop_p will need them later. These values can not be - recalculated inside precondition_loop_p because strength reduction - optimizations may obscure the loop's structure. */ - - loop_iteration_var = iteration_var; - loop_initial_value = initial_value; - loop_increment = increment; - loop_final_value = final_value; - - if (final_value == 0) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: EQ comparison loop.\n"); - return 0; - } - else if (GET_CODE (final_value) != CONST_INT) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: Final value not constant.\n"); - return 0; - } - - /* ?? Final value and initial value do not have to be constants. - Only their difference has to be constant. When the iteration variable - is an array address, the final value and initial value might both - be addresses with the same base but different constant offsets. - Final value must be invariant for this to work. - - To do this, need some way to find the values of registers which are - invariant. */ - - /* Final_larger is 1 if final larger, 0 if they are equal, otherwise -1. */ - if (unsigned_compare) - final_larger - = ((unsigned HOST_WIDE_INT) INTVAL (final_value) - > (unsigned HOST_WIDE_INT) INTVAL (initial_value)) - - ((unsigned HOST_WIDE_INT) INTVAL (final_value) - < (unsigned HOST_WIDE_INT) INTVAL (initial_value)); - else - final_larger = (INTVAL (final_value) > INTVAL (initial_value)) - - (INTVAL (final_value) < INTVAL (initial_value)); - - if (INTVAL (increment) > 0) - increment_dir = 1; - else if (INTVAL (increment) == 0) - increment_dir = 0; - else - increment_dir = -1; - - /* There are 27 different cases: compare_dir = -1, 0, 1; - final_larger = -1, 0, 1; increment_dir = -1, 0, 1. - There are 4 normal cases, 4 reverse cases (where the iteration variable - will overflow before the loop exits), 4 infinite loop cases, and 15 - immediate exit (0 or 1 iteration depending on loop type) cases. - Only try to optimize the normal cases. */ - - /* (compare_dir/final_larger/increment_dir) - Normal cases: (0/-1/-1), (0/1/1), (-1/-1/-1), (1/1/1) - Reverse cases: (0/-1/1), (0/1/-1), (-1/-1/1), (1/1/-1) - Infinite loops: (0/-1/0), (0/1/0), (-1/-1/0), (1/1/0) - Immediate exit: (0/0/X), (-1/0/X), (-1/1/X), (1/0/X), (1/-1/X) */ - - /* ?? If the meaning of reverse loops (where the iteration variable - will overflow before the loop exits) is undefined, then could - eliminate all of these special checks, and just always assume - the loops are normal/immediate/infinite. Note that this means - the sign of increment_dir does not have to be known. Also, - since it does not really hurt if immediate exit loops or infinite loops - are optimized, then that case could be ignored also, and hence all - loops can be optimized. - - According to ANSI Spec, the reverse loop case result is undefined, - because the action on overflow is undefined. - - See also the special test for NE loops below. */ - - if (final_larger == increment_dir && final_larger != 0 - && (final_larger == compare_dir || compare_dir == 0)) - /* Normal case. */ - ; - else - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Loop unrolling: Not normal loop.\n"); - return 0; - } - - /* Calculate the number of iterations, final_value is only an approximation, - so correct for that. Note that tempu and loop_n_iterations are - unsigned, because they can be as large as 2^n - 1. */ - - i = INTVAL (increment); - if (i > 0) - tempu = INTVAL (final_value) - INTVAL (initial_value); - else if (i < 0) - { - tempu = INTVAL (initial_value) - INTVAL (final_value); - i = -i; - } - else - abort (); - - /* For NE tests, make sure that the iteration variable won't miss the - final value. If tempu mod i is not zero, then the iteration variable - will overflow before the loop exits, and we can not calculate the - number of iterations. */ - if (compare_dir == 0 && (tempu % i) != 0) - return 0; - - return tempu / i + ((tempu % i) != 0); -} diff --git a/gnu/usr.bin/cc/common/varasm.c b/gnu/usr.bin/cc/common/varasm.c deleted file mode 100644 index fea708389a..0000000000 --- a/gnu/usr.bin/cc/common/varasm.c +++ /dev/null @@ -1,3029 +0,0 @@ -/* Output variables, constants and external declarations, for GNU compiler. - Copyright (C) 1987, 1988, 1989, 1992, 1993 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This file handles generation of all the assembler code - *except* the instructions of a function. - This includes declarations of variables and their initial values. - - We also output the assembler code for constants stored in memory - and are responsible for combining constants with the same value. */ - -#include -#include -/* #include */ -#include "config.h" -#include "rtl.h" -#include "tree.h" -#include "flags.h" -#include "function.h" -#include "expr.h" -#include "hard-reg-set.h" -#include "regs.h" -#include "defaults.h" -#include "real.h" - -#include "obstack.h" - -#ifdef XCOFF_DEBUGGING_INFO -#include "xcoffout.h" -#endif - -#ifndef ASM_STABS_OP -#define ASM_STABS_OP ".stabs" -#endif - -/* This macro gets just the user-specified name - out of the string in a SYMBOL_REF. On most machines, - we discard the * if any and that's all. */ -#ifndef STRIP_NAME_ENCODING -#define STRIP_NAME_ENCODING(VAR,SYMBOL_NAME) \ - (VAR) = ((SYMBOL_NAME) + ((SYMBOL_NAME)[0] == '*')) -#endif - -/* File in which assembler code is being written. */ - -extern FILE *asm_out_file; - -/* The (assembler) name of the first globally-visible object output. */ -char *first_global_object_name; - -extern struct obstack *current_obstack; -extern struct obstack *saveable_obstack; -extern struct obstack permanent_obstack; -#define obstack_chunk_alloc xmalloc - -/* Number for making the label on the next - constant that is stored in memory. */ - -int const_labelno; - -/* Number for making the label on the next - static variable internal to a function. */ - -int var_labelno; - -/* Nonzero if at least one function definition has been seen. */ -static int function_defined; - -extern FILE *asm_out_file; - -static char *compare_constant_1 (); -static void record_constant_1 (); -void output_constant_pool (); -void assemble_name (); -int output_addressed_constants (); -void output_constant (); -void output_constructor (); -void text_section (); -void readonly_data_section (); -void data_section (); - -#ifdef EXTRA_SECTIONS -static enum in_section {no_section, in_text, in_data, EXTRA_SECTIONS} in_section - = no_section; -#else -static enum in_section {no_section, in_text, in_data} in_section - = no_section; -#endif - -/* Define functions like text_section for any extra sections. */ -#ifdef EXTRA_SECTION_FUNCTIONS -EXTRA_SECTION_FUNCTIONS -#endif - -/* Tell assembler to switch to text section. */ - -void -text_section () -{ - if (in_section != in_text) - { - fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP); - in_section = in_text; - } -} - -/* Tell assembler to switch to data section. */ - -void -data_section () -{ - if (in_section != in_data) - { - if (flag_shared_data) - { -#ifdef SHARED_SECTION_ASM_OP - fprintf (asm_out_file, "%s\n", SHARED_SECTION_ASM_OP); -#else - fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP); -#endif - } - else - fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP); - - in_section = in_data; - } -} - -/* Tell assembler to switch to read-only data section. This is normally - the text section. */ - -void -readonly_data_section () -{ -#ifdef READONLY_DATA_SECTION - READONLY_DATA_SECTION (); /* Note this can call data_section. */ -#else - text_section (); -#endif -} - -/* Determine if we're in the text section. */ - -int -in_text_section () -{ - return in_section == in_text; -} - -/* Create the rtl to represent a function, for a function definition. - DECL is a FUNCTION_DECL node which describes which function. - The rtl is stored into DECL. */ - -void -make_function_rtl (decl) - tree decl; -{ - char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); - - /* Rename a nested function to avoid conflicts. */ - if (decl_function_context (decl) != 0 - && DECL_INITIAL (decl) != 0 - && DECL_RTL (decl) == 0) - { - char *label; - - name = IDENTIFIER_POINTER (DECL_NAME (decl)); - ASM_FORMAT_PRIVATE_NAME (label, name, var_labelno); - name = obstack_copy0 (saveable_obstack, label, strlen (label)); - var_labelno++; - } - - if (DECL_RTL (decl) == 0) - { - DECL_RTL (decl) - = gen_rtx (MEM, DECL_MODE (decl), - gen_rtx (SYMBOL_REF, Pmode, name)); - - /* Optionally set flags or add text to the name to record information - such as that it is a function name. If the name is changed, the macro - ASM_OUTPUT_LABELREF will have to know how to strip this information. - And if it finds a * at the beginning after doing so, it must handle - that too. */ -#ifdef ENCODE_SECTION_INFO - ENCODE_SECTION_INFO (decl); -#endif - } - - /* Record at least one function has been defined. */ - function_defined = 1; -} - -/* Given NAME, a putative register name, discard any customary prefixes. */ - -static char * -strip_reg_name (name) - char *name; -{ -#ifdef REGISTER_PREFIX - if (!strncmp (name, REGISTER_PREFIX, strlen (REGISTER_PREFIX))) - name += strlen (REGISTER_PREFIX); -#endif - if (name[0] == '%' || name[0] == '#') - name++; - return name; -} - -/* Decode an `asm' spec for a declaration as a register name. - Return the register number, or -1 if nothing specified, - or -2 if the ASMSPEC is not `cc' or `memory' and is not recognized, - or -3 if ASMSPEC is `cc' and is not recognized, - or -4 if ASMSPEC is `memory' and is not recognized. - Accept an exact spelling or a decimal number. - Prefixes such as % are optional. */ - -int -decode_reg_name (asmspec) - char *asmspec; -{ - if (asmspec != 0) - { - int i; - - /* Get rid of confusing prefixes. */ - asmspec = strip_reg_name (asmspec); - - /* Allow a decimal number as a "register name". */ - for (i = strlen (asmspec) - 1; i >= 0; i--) - if (! (asmspec[i] >= '0' && asmspec[i] <= '9')) - break; - if (asmspec[0] != 0 && i < 0) - { - i = atoi (asmspec); - if (i < FIRST_PSEUDO_REGISTER && i >= 0) - return i; - else - return -2; - } - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (reg_names[i][0] - && ! strcmp (asmspec, strip_reg_name (reg_names[i]))) - return i; - -#ifdef ADDITIONAL_REGISTER_NAMES - { - static struct { char *name; int number; } table[] - = ADDITIONAL_REGISTER_NAMES; - - for (i = 0; i < sizeof (table) / sizeof (table[0]); i++) - if (! strcmp (asmspec, table[i].name)) - return table[i].number; - } -#endif /* ADDITIONAL_REGISTER_NAMES */ - - if (!strcmp (asmspec, "memory")) - return -4; - - if (!strcmp (asmspec, "cc")) - return -3; - - return -2; - } - - return -1; -} - -/* Create the DECL_RTL for a declaration for a static or external variable - or static or external function. - ASMSPEC, if not 0, is the string which the user specified - as the assembler symbol name. - TOP_LEVEL is nonzero if this is a file-scope variable. - - This is never called for PARM_DECL nodes. */ - -void -make_decl_rtl (decl, asmspec, top_level) - tree decl; - char *asmspec; - int top_level; -{ - register char *name; - int reg_number = decode_reg_name (asmspec); - - if (DECL_ASSEMBLER_NAME (decl) != NULL_TREE) - name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); - - if (reg_number == -2) - { - /* ASMSPEC is given, and not the name of a register. */ - name = (char *) obstack_alloc (saveable_obstack, - strlen (asmspec) + 2); - name[0] = '*'; - strcpy (&name[1], asmspec); - } - - /* For a duplicate declaration, we can be called twice on the - same DECL node. Don't alter the RTL already made - unless the old mode is wrong (which can happen when - the previous rtl was made when the type was incomplete). */ - if (DECL_RTL (decl) == 0 - || GET_MODE (DECL_RTL (decl)) != DECL_MODE (decl)) - { - DECL_RTL (decl) = 0; - - /* First detect errors in declaring global registers. */ - if (DECL_REGISTER (decl) && reg_number == -1) - error_with_decl (decl, - "register name not specified for `%s'"); - else if (DECL_REGISTER (decl) && reg_number < 0) - error_with_decl (decl, - "invalid register name for `%s'"); - else if ((reg_number >= 0 || reg_number == -3) && ! DECL_REGISTER (decl)) - error_with_decl (decl, - "register name given for non-register variable `%s'"); - else if (DECL_REGISTER (decl) && TREE_CODE (decl) == FUNCTION_DECL) - error ("function declared `register'"); - else if (DECL_REGISTER (decl) && TYPE_MODE (TREE_TYPE (decl)) == BLKmode) - error_with_decl (decl, "data type of `%s' isn't suitable for a register"); - else if (DECL_REGISTER (decl) - && ! HARD_REGNO_MODE_OK (reg_number, TYPE_MODE (TREE_TYPE (decl)))) - error_with_decl (decl, "register number for `%s' isn't suitable for the data type"); - /* Now handle properly declared static register variables. */ - else if (DECL_REGISTER (decl)) - { - int nregs; -#if 0 /* yylex should print the warning for this */ - if (pedantic) - pedwarn ("ANSI C forbids global register variables"); -#endif - if (DECL_INITIAL (decl) != 0 && top_level) - { - DECL_INITIAL (decl) = 0; - error ("global register variable has initial value"); - } - if (fixed_regs[reg_number] == 0 - && function_defined && top_level) - error ("global register variable follows a function definition"); - if (TREE_THIS_VOLATILE (decl)) - warning ("volatile register variables don't work as you might wish"); - - /* If the user specified one of the eliminables registers here, - e.g., FRAME_POINTER_REGNUM, we don't want to get this variable - confused with that register and be eliminated. Although this - usage is somewhat suspect, we nevertheless use the following - kludge to avoid setting DECL_RTL to frame_pointer_rtx. */ - - DECL_RTL (decl) - = gen_rtx (REG, DECL_MODE (decl), FIRST_PSEUDO_REGISTER); - REGNO (DECL_RTL (decl)) = reg_number; - REG_USERVAR_P (DECL_RTL (decl)) = 1; - - if (top_level) - { - /* Make this register fixed, so not usable for anything else. */ - nregs = HARD_REGNO_NREGS (reg_number, DECL_MODE (decl)); - while (nregs > 0) - global_regs[reg_number + --nregs] = 1; - init_reg_sets_1 (); - } - } - - /* Now handle ordinary static variables and functions (in memory). - Also handle vars declared register invalidly. */ - if (DECL_RTL (decl) == 0) - { - /* Can't use just the variable's own name for a variable - whose scope is less than the whole file. - Concatenate a distinguishing number. */ - if (!top_level && !DECL_EXTERNAL (decl) && asmspec == 0) - { - char *label; - - ASM_FORMAT_PRIVATE_NAME (label, name, var_labelno); - name = obstack_copy0 (saveable_obstack, label, strlen (label)); - var_labelno++; - } - - DECL_RTL (decl) = gen_rtx (MEM, DECL_MODE (decl), - gen_rtx (SYMBOL_REF, Pmode, name)); - if (TREE_THIS_VOLATILE (decl) - || (flag_volatile_global && TREE_CODE (decl) == VAR_DECL - && TREE_PUBLIC (decl))) - MEM_VOLATILE_P (DECL_RTL (decl)) = 1; - if (TREE_READONLY (decl)) - RTX_UNCHANGING_P (DECL_RTL (decl)) = 1; - MEM_IN_STRUCT_P (DECL_RTL (decl)) - = (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE - || TREE_CODE (TREE_TYPE (decl)) == QUAL_UNION_TYPE); - - /* Optionally set flags or add text to the name to record information - such as that it is a function name. - If the name is changed, the macro ASM_OUTPUT_LABELREF - will have to know how to strip this information. - And if it finds a * at the beginning after doing so, - it must handle that too. */ -#ifdef ENCODE_SECTION_INFO - ENCODE_SECTION_INFO (decl); -#endif - } - } -} - -/* Make the rtl for variable VAR be volatile. - Use this only for static variables. */ - -void -make_var_volatile (var) - tree var; -{ - if (GET_CODE (DECL_RTL (var)) != MEM) - abort (); - - MEM_VOLATILE_P (DECL_RTL (var)) = 1; -} - -/* Output alignment directive to align for constant expression EXP. */ - -void -assemble_constant_align (exp) - tree exp; -{ - int align; - - /* Align the location counter as required by EXP's data type. */ - align = TYPE_ALIGN (TREE_TYPE (exp)); -#ifdef CONSTANT_ALIGNMENT - align = CONSTANT_ALIGNMENT (exp, align); -#endif - - if (align > BITS_PER_UNIT) - ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); -} - -/* Output a string of literal assembler code - for an `asm' keyword used between functions. */ - -void -assemble_asm (string) - tree string; -{ - app_enable (); - - if (TREE_CODE (string) == ADDR_EXPR) - string = TREE_OPERAND (string, 0); - - fprintf (asm_out_file, "\t%s\n", TREE_STRING_POINTER (string)); -} - -#if 0 /* This should no longer be needed, because - flag_gnu_linker should be 0 on these systems, - which should prevent any output - if ASM_OUTPUT_CONSTRUCTOR and ASM_OUTPUT_DESTRUCTOR are absent. */ -#if !(defined(DBX_DEBUGGING_INFO) && !defined(FASCIST_ASSEMBLER)) -#ifndef ASM_OUTPUT_CONSTRUCTOR -#define ASM_OUTPUT_CONSTRUCTOR(file, name) -#endif -#ifndef ASM_OUTPUT_DESTRUCTOR -#define ASM_OUTPUT_DESTRUCTOR(file, name) -#endif -#endif -#endif /* 0 */ - -/* Record an element in the table of global destructors. - How this is done depends on what sort of assembler and linker - are in use. - - NAME should be the name of a global function to be called - at exit time. This name is output using assemble_name. */ - -void -assemble_destructor (name) - char *name; -{ -#ifdef ASM_OUTPUT_DESTRUCTOR - ASM_OUTPUT_DESTRUCTOR (asm_out_file, name); -#else - if (flag_gnu_linker) - { - /* Now tell GNU LD that this is part of the static destructor set. */ - /* This code works for any machine provided you use GNU as/ld. */ - fprintf (asm_out_file, "%s \"___DTOR_LIST__\",22,0,0,", ASM_STABS_OP); - assemble_name (asm_out_file, name); - fputc ('\n', asm_out_file); - } -#endif -} - -/* Likewise for global constructors. */ - -void -assemble_constructor (name) - char *name; -{ -#ifdef ASM_OUTPUT_CONSTRUCTOR - ASM_OUTPUT_CONSTRUCTOR (asm_out_file, name); -#else - if (flag_gnu_linker) - { - /* Now tell GNU LD that this is part of the static constructor set. */ - /* This code works for any machine provided you use GNU as/ld. */ - fprintf (asm_out_file, "%s \"___CTOR_LIST__\",22,0,0,", ASM_STABS_OP); - assemble_name (asm_out_file, name); - fputc ('\n', asm_out_file); - } -#endif -} - -/* Likewise for entries we want to record for garbage collection. - Garbage collection is still under development. */ - -void -assemble_gc_entry (name) - char *name; -{ -#ifdef ASM_OUTPUT_GC_ENTRY - ASM_OUTPUT_GC_ENTRY (asm_out_file, name); -#else - if (flag_gnu_linker) - { - /* Now tell GNU LD that this is part of the static constructor set. */ - fprintf (asm_out_file, "%s \"___PTR_LIST__\",22,0,0,", ASM_STABS_OP); - assemble_name (asm_out_file, name); - fputc ('\n', asm_out_file); - } -#endif -} - -/* Output assembler code for the constant pool of a function and associated - with defining the name of the function. DECL describes the function. - NAME is the function's name. For the constant pool, we use the current - constant pool data. */ - -void -assemble_start_function (decl, fnname) - tree decl; - char *fnname; -{ - int align; - - /* The following code does not need preprocessing in the assembler. */ - - app_disable (); - - output_constant_pool (fnname, decl); - - text_section (); - - - /* Tell assembler to move to target machine's alignment for functions. */ - align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT); - if (align > 0) - ASM_OUTPUT_ALIGN (asm_out_file, align); - -#ifdef ASM_OUTPUT_FUNCTION_PREFIX - ASM_OUTPUT_FUNCTION_PREFIX (asm_out_file, fnname); -#endif - -#ifdef SDB_DEBUGGING_INFO - /* Output SDB definition of the function. */ - if (write_symbols == SDB_DEBUG) - sdbout_mark_begin_function (); -#endif - -#ifdef DBX_DEBUGGING_INFO - /* Output DBX definition of the function. */ - if (write_symbols == DBX_DEBUG) - dbxout_begin_function (decl); -#endif - - /* Make function name accessible from other files, if appropriate. */ - - if (TREE_PUBLIC (decl)) - { - if (!first_global_object_name) - STRIP_NAME_ENCODING (first_global_object_name, fnname); - ASM_GLOBALIZE_LABEL (asm_out_file, fnname); - } - - /* Do any machine/system dependent processing of the function name */ -#ifdef ASM_DECLARE_FUNCTION_NAME - ASM_DECLARE_FUNCTION_NAME (asm_out_file, fnname, current_function_decl); -#else - /* Standard thing is just output label for the function. */ - ASM_OUTPUT_LABEL (asm_out_file, fnname); -#endif /* ASM_DECLARE_FUNCTION_NAME */ -} - -/* Output assembler code associated with defining the size of the - function. DECL describes the function. NAME is the function's name. */ - -void -assemble_end_function (decl, fnname) - tree decl; - char *fnname; -{ -#ifdef ASM_DECLARE_FUNCTION_SIZE - ASM_DECLARE_FUNCTION_SIZE (asm_out_file, fnname, decl); -#endif -} - -/* Assemble code to leave SIZE bytes of zeros. */ - -void -assemble_zeros (size) - int size; -{ -#ifdef ASM_NO_SKIP_IN_TEXT - /* The `space' pseudo in the text section outputs nop insns rather than 0s, - so we must output 0s explicitly in the text section. */ - if (ASM_NO_SKIP_IN_TEXT && in_text_section ()) - { - int i; - - for (i = 0; i < size - 20; i += 20) - { -#ifdef ASM_BYTE_OP - fprintf (asm_out_file, - "%s 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n", ASM_BYTE_OP); -#else - fprintf (asm_out_file, - "\tbyte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n"); -#endif - } - if (i < size) - { -#ifdef ASM_BYTE_OP - fprintf (asm_out_file, "%s 0", ASM_BYTE_OP); -#else - fprintf (asm_out_file, "\tbyte 0"); -#endif - i++; - for (; i < size; i++) - fprintf (asm_out_file, ",0"); - fprintf (asm_out_file, "\n"); - } - } - else -#endif - if (size > 0) - ASM_OUTPUT_SKIP (asm_out_file, size); -} - -/* Assemble a string constant with the specified C string as contents. */ - -void -assemble_string (p, size) - char *p; - int size; -{ - register int i; - int pos = 0; - int maximum = 2000; - - /* If the string is very long, split it up. */ - - while (pos < size) - { - int thissize = size - pos; - if (thissize > maximum) - thissize = maximum; - - ASM_OUTPUT_ASCII (asm_out_file, p, thissize); - - pos += thissize; - p += thissize; - } -} - -/* Assemble everything that is needed for a variable or function declaration. - Not used for automatic variables, and not used for function definitions. - Should not be called for variables of incomplete structure type. - - TOP_LEVEL is nonzero if this variable has file scope. - AT_END is nonzero if this is the special handling, at end of compilation, - to define things that have had only tentative definitions. */ - -void -assemble_variable (decl, top_level, at_end) - tree decl; - int top_level; - int at_end; -{ - register char *name; - int align; - tree size_tree; - int reloc = 0; - - if (GET_CODE (DECL_RTL (decl)) == REG) - { - /* Do output symbol info for global register variables, but do nothing - else for them. */ - - if (TREE_ASM_WRITTEN (decl)) - return; - TREE_ASM_WRITTEN (decl) = 1; - -#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) - /* File-scope global variables are output here. */ - if ((write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) - && top_level) - dbxout_symbol (decl, 0); -#endif -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG && top_level - /* Leave initialized global vars for end of compilation; - see comment in compile_file. */ - && (TREE_PUBLIC (decl) == 0 || DECL_INITIAL (decl) == 0)) - sdbout_symbol (decl, 0); -#endif - - /* Don't output any DWARF debugging information for variables here. - In the case of local variables, the information for them is output - when we do our recursive traversal of the tree representation for - the entire containing function. In the case of file-scope variables, - we output information for all of them at the very end of compilation - while we are doing our final traversal of the chain of file-scope - declarations. */ - - return; - } - - /* Normally no need to say anything here for external references, - since assemble_external is called by the langauge-specific code - when a declaration is first seen. */ - - if (DECL_EXTERNAL (decl)) - return; - - /* Output no assembler code for a function declaration. - Only definitions of functions output anything. */ - - if (TREE_CODE (decl) == FUNCTION_DECL) - return; - - /* If type was incomplete when the variable was declared, - see if it is complete now. */ - - if (DECL_SIZE (decl) == 0) - layout_decl (decl, 0); - - /* Still incomplete => don't allocate it; treat the tentative defn - (which is what it must have been) as an `extern' reference. */ - - if (DECL_SIZE (decl) == 0) - { - error_with_file_and_line (DECL_SOURCE_FILE (decl), - DECL_SOURCE_LINE (decl), - "storage size of `%s' isn't known", - IDENTIFIER_POINTER (DECL_NAME (decl))); - return; - } - - /* The first declaration of a variable that comes through this function - decides whether it is global (in C, has external linkage) - or local (in C, has internal linkage). So do nothing more - if this function has already run. */ - - if (TREE_ASM_WRITTEN (decl)) - return; - - TREE_ASM_WRITTEN (decl) = 1; - -#ifdef DBX_DEBUGGING_INFO - /* File-scope global variables are output here. */ - if (write_symbols == DBX_DEBUG && top_level) - dbxout_symbol (decl, 0); -#endif -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG && top_level - /* Leave initialized global vars for end of compilation; - see comment in compile_file. */ - && (TREE_PUBLIC (decl) == 0 || DECL_INITIAL (decl) == 0)) - sdbout_symbol (decl, 0); -#endif - - /* Don't output any DWARF debugging information for variables here. - In the case of local variables, the information for them is output - when we do our recursive traversal of the tree representation for - the entire containing function. In the case of file-scope variables, - we output information for all of them at the very end of compilation - while we are doing our final traversal of the chain of file-scope - declarations. */ - - /* If storage size is erroneously variable, just continue. - Error message was already made. */ - - if (TREE_CODE (DECL_SIZE (decl)) != INTEGER_CST) - goto finish; - - app_disable (); - - /* This is better than explicit arithmetic, since it avoids overflow. */ - size_tree = size_binop (CEIL_DIV_EXPR, - DECL_SIZE (decl), size_int (BITS_PER_UNIT)); - - if (TREE_INT_CST_HIGH (size_tree) != 0) - { - error_with_decl (decl, "size of variable `%s' is too large"); - goto finish; - } - - name = XSTR (XEXP (DECL_RTL (decl), 0), 0); - - /* Handle uninitialized definitions. */ - - /* ANSI specifies that a tentative definition which is not merged with - a non-tentative definition behaves exactly like a definition with an - initializer equal to zero. (Section 3.7.2) - -fno-common gives strict ANSI behavior. Usually you don't want it. */ - if (! flag_no_common - && (DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node)) - { - int size = TREE_INT_CST_LOW (size_tree); - int rounded = size; - - if (TREE_INT_CST_HIGH (size_tree) != 0) - error_with_decl (decl, "size of variable `%s' is too large"); - /* Don't allocate zero bytes of common, - since that means "undefined external" in the linker. */ - if (size == 0) rounded = 1; - /* Round size up to multiple of BIGGEST_ALIGNMENT bits - so that each uninitialized object starts on such a boundary. */ - rounded += (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1; - rounded = (rounded / (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); -#if 0 - if (flag_shared_data) - data_section (); -#endif - if (TREE_PUBLIC (decl)) - { -#ifdef ASM_OUTPUT_SHARED_COMMON - if (flag_shared_data) - ASM_OUTPUT_SHARED_COMMON (asm_out_file, name, size, rounded); - else -#endif -#ifdef ASM_OUTPUT_ALIGNED_COMMON - ASM_OUTPUT_ALIGNED_COMMON (asm_out_file, name, size, - DECL_ALIGN (decl)); -#else - ASM_OUTPUT_COMMON (asm_out_file, name, size, rounded); -#endif - } - else - { -#ifdef ASM_OUTPUT_SHARED_LOCAL - if (flag_shared_data) - ASM_OUTPUT_SHARED_LOCAL (asm_out_file, name, size, rounded); - else -#endif -#ifdef ASM_OUTPUT_ALIGNED_LOCAL - ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, - DECL_ALIGN (decl)); -#else - ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded); -#endif - } - goto finish; - } - - /* Handle initialized definitions. */ - - /* First make the assembler name(s) global if appropriate. */ - if (TREE_PUBLIC (decl) && DECL_NAME (decl)) - { - if (!first_global_object_name) - STRIP_NAME_ENCODING(first_global_object_name, name); - ASM_GLOBALIZE_LABEL (asm_out_file, name); - } -#if 0 - for (d = equivalents; d; d = TREE_CHAIN (d)) - { - tree e = TREE_VALUE (d); - if (TREE_PUBLIC (e) && DECL_NAME (e)) - ASM_GLOBALIZE_LABEL (asm_out_file, - XSTR (XEXP (DECL_RTL (e), 0), 0)); - } -#endif - - /* Output any data that we will need to use the address of. */ - if (DECL_INITIAL (decl)) - reloc = output_addressed_constants (DECL_INITIAL (decl)); - - /* Switch to the proper section for this data. */ -#ifdef SELECT_SECTION - SELECT_SECTION (decl, reloc); -#else - if (TREE_READONLY (decl) - && ! TREE_THIS_VOLATILE (decl) - && ! (flag_pic && reloc)) - readonly_data_section (); - else - data_section (); -#endif - - /* Compute and output the alignment of this data. */ - - align = DECL_ALIGN (decl); - /* Some object file formats have a maximum alignment which they support. - In particular, a.out format supports a maximum alignment of 4. */ -#ifndef MAX_OFILE_ALIGNMENT -#define MAX_OFILE_ALIGNMENT BIGGEST_ALIGNMENT -#endif - if (align > MAX_OFILE_ALIGNMENT) - { - warning_with_decl (decl, - "alignment of `%s' is greater than maximum object file alignment"); - align = MAX_OFILE_ALIGNMENT; - } -#ifdef DATA_ALIGNMENT - /* On some machines, it is good to increase alignment sometimes. */ - align = DATA_ALIGNMENT (TREE_TYPE (decl), align); -#endif -#ifdef CONSTANT_ALIGNMENT - if (DECL_INITIAL (decl)) - align = CONSTANT_ALIGNMENT (DECL_INITIAL (decl), align); -#endif - - /* Reset the alignment in case we have made it tighter, so we can benefit - from it in get_pointer_alignment. */ - DECL_ALIGN (decl) = align; - - if (align > BITS_PER_UNIT) - ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); - - /* Do any machine/system dependent processing of the object. */ -#ifdef ASM_DECLARE_OBJECT_NAME - ASM_DECLARE_OBJECT_NAME (asm_out_file, name, decl); -#else - /* Standard thing is just output label for the object. */ - ASM_OUTPUT_LABEL (asm_out_file, name); -#endif /* ASM_DECLARE_OBJECT_NAME */ - -#if 0 - for (d = equivalents; d; d = TREE_CHAIN (d)) - { - tree e = TREE_VALUE (d); - ASM_OUTPUT_LABEL (asm_out_file, XSTR (XEXP (DECL_RTL (e), 0), 0)); - } -#endif - - if (DECL_INITIAL (decl)) - /* Output the actual data. */ - output_constant (DECL_INITIAL (decl), - int_size_in_bytes (TREE_TYPE (decl))); - else - /* Leave space for it. */ - assemble_zeros (int_size_in_bytes (TREE_TYPE (decl))); - - finish: -#ifdef XCOFF_DEBUGGING_INFO - /* Unfortunately, the IBM assembler cannot handle stabx before the actual - declaration. When something like ".stabx "aa:S-2",aa,133,0" is emitted - and `aa' hasn't been output yet, the assembler generates a stab entry with - a value of zero, in addition to creating an unnecessary external entry - for `aa'. Hence, we must postpone dbxout_symbol to here at the end. */ - - /* File-scope global variables are output here. */ - if (write_symbols == XCOFF_DEBUG && top_level) - dbxout_symbol (decl, 0); -#else - /* There must be a statement after a label. */ - ; -#endif -} - -/* Output something to declare an external symbol to the assembler. - (Most assemblers don't need this, so we normally output nothing.) - Do nothing if DECL is not external. */ - -void -assemble_external (decl) - tree decl; -{ -#ifdef ASM_OUTPUT_EXTERNAL - if (TREE_CODE_CLASS (TREE_CODE (decl)) == 'd' - && DECL_EXTERNAL (decl) && TREE_PUBLIC (decl)) - { - rtx rtl = DECL_RTL (decl); - - if (GET_CODE (rtl) == MEM && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF - && ! SYMBOL_REF_USED (XEXP (rtl, 0))) - { - /* Some systems do require some output. */ - SYMBOL_REF_USED (XEXP (rtl, 0)) = 1; - ASM_OUTPUT_EXTERNAL (asm_out_file, decl, XSTR (XEXP (rtl, 0), 0)); - } - } -#endif -} - -/* Similar, for calling a library function FUN. */ - -void -assemble_external_libcall (fun) - rtx fun; -{ -#ifdef ASM_OUTPUT_EXTERNAL_LIBCALL - /* Declare library function name external when first used, if nec. */ - if (! SYMBOL_REF_USED (fun)) - { - SYMBOL_REF_USED (fun) = 1; - ASM_OUTPUT_EXTERNAL_LIBCALL (asm_out_file, fun); - } -#endif -} - -/* Declare the label NAME global. */ - -void -assemble_global (name) - char *name; -{ - ASM_GLOBALIZE_LABEL (asm_out_file, name); -} - -/* Assemble a label named NAME. */ - -void -assemble_label (name) - char *name; -{ - ASM_OUTPUT_LABEL (asm_out_file, name); -} - -/* Output to FILE a reference to the assembler name of a C-level name NAME. - If NAME starts with a *, the rest of NAME is output verbatim. - Otherwise NAME is transformed in an implementation-defined way - (usually by the addition of an underscore). - Many macros in the tm file are defined to call this function. */ - -void -assemble_name (file, name) - FILE *file; - char *name; -{ - if (name[0] == '*') - fputs (&name[1], file); - else - ASM_OUTPUT_LABELREF (file, name); -} - -/* Allocate SIZE bytes writable static space with a gensym name - and return an RTX to refer to its address. */ - -rtx -assemble_static_space (size) - int size; -{ - char name[12]; - char *namestring; - rtx x; - /* Round size up to multiple of BIGGEST_ALIGNMENT bits - so that each uninitialized object starts on such a boundary. */ - int rounded = ((size + (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1) - / (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); - -#if 0 - if (flag_shared_data) - data_section (); -#endif - - ASM_GENERATE_INTERNAL_LABEL (name, "LF", const_labelno); - ++const_labelno; - - namestring = (char *) obstack_alloc (saveable_obstack, - strlen (name) + 2); - strcpy (namestring, name); - - x = gen_rtx (SYMBOL_REF, Pmode, namestring); -#ifdef ASM_OUTPUT_ALIGNED_LOCAL - ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, BIGGEST_ALIGNMENT); -#else - ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded); -#endif - return x; -} - -/* Assemble the static constant template for function entry trampolines. - This is done at most once per compilation. - Returns an RTX for the address of the template. */ - -rtx -assemble_trampoline_template () -{ - char label[256]; - char *name; - int align; - - /* By default, put trampoline templates in read-only data section. */ - -#ifdef TRAMPOLINE_SECTION - TRAMPOLINE_SECTION (); -#else - readonly_data_section (); -#endif - - /* Write the assembler code to define one. */ - align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT); - if (align > 0) - ASM_OUTPUT_ALIGN (asm_out_file, align); - - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LTRAMP", 0); - TRAMPOLINE_TEMPLATE (asm_out_file); - - /* Record the rtl to refer to it. */ - ASM_GENERATE_INTERNAL_LABEL (label, "LTRAMP", 0); - name - = (char *) obstack_copy0 (&permanent_obstack, label, strlen (label)); - return gen_rtx (SYMBOL_REF, Pmode, name); -} - -/* Assemble the integer constant X into an object of SIZE bytes. - X must be either a CONST_INT or CONST_DOUBLE. - - Return 1 if we were able to output the constant, otherwise 0. If FORCE is - non-zero, abort if we can't output the constant. */ - -int -assemble_integer (x, size, force) - rtx x; - int size; - int force; -{ - /* First try to use the standard 1, 2, 4, 8, and 16 byte - ASM_OUTPUT... macros. */ - - switch (size) - { -#ifdef ASM_OUTPUT_CHAR - case 1: - ASM_OUTPUT_CHAR (asm_out_file, x); - return 1; -#endif - -#ifdef ASM_OUTPUT_SHORT - case 2: - ASM_OUTPUT_SHORT (asm_out_file, x); - return 1; -#endif - -#ifdef ASM_OUTPUT_INT - case 4: - ASM_OUTPUT_INT (asm_out_file, x); - return 1; -#endif - -#ifdef ASM_OUTPUT_DOUBLE_INT - case 8: - ASM_OUTPUT_DOUBLE_INT (asm_out_file, x); - return 1; -#endif - -#ifdef ASM_OUTPUT_QUADRUPLE_INT - case 16: - ASM_OUTPUT_QUADRUPLE_INT (asm_out_file, x); - return 1; -#endif - } - - /* If we couldn't do it that way, there are two other possibilities: First, - if the machine can output an explicit byte and this is a 1 byte constant, - we can use ASM_OUTPUT_BYTE. */ - -#ifdef ASM_OUTPUT_BYTE - if (size == 1 && GET_CODE (x) == CONST_INT) - { - ASM_OUTPUT_BYTE (asm_out_file, INTVAL (x)); - return 1; - } -#endif - - /* Finally, if SIZE is larger than a single word, try to output the constant - one word at a time. */ - - if (size > UNITS_PER_WORD) - { - int i; - enum machine_mode mode - = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0); - rtx word; - - for (i = 0; i < size / UNITS_PER_WORD; i++) - { - word = operand_subword (x, i, 0, mode); - - if (word == 0) - break; - - if (! assemble_integer (word, UNITS_PER_WORD, 0)) - break; - } - - if (i == size / UNITS_PER_WORD) - return 1; - /* If we output at least one word and then could not finish, - there is no valid way to continue. */ - if (i > 0) - abort (); - } - - if (force) - abort (); - - return 0; -} - -/* Assemble the floating-point constant D into an object of size MODE. */ - -void -assemble_real (d, mode) - REAL_VALUE_TYPE d; - enum machine_mode mode; -{ - jmp_buf output_constant_handler; - - if (setjmp (output_constant_handler)) - { - error ("floating point trap outputting a constant"); -#ifdef REAL_IS_NOT_DOUBLE - bzero (&d, sizeof d); - d = dconst0; -#else - d = 0; -#endif - } - - set_float_handler (output_constant_handler); - - switch (mode) - { -#ifdef ASM_OUTPUT_BYTE_FLOAT - case QFmode: - ASM_OUTPUT_BYTE_FLOAT (asm_out_file, d); - break; -#endif -#ifdef ASM_OUTPUT_SHORT_FLOAT - case HFmode: - ASM_OUTPUT_SHORT_FLOAT (asm_out_file, d); - break; -#endif -#ifdef ASM_OUTPUT_FLOAT - case SFmode: - ASM_OUTPUT_FLOAT (asm_out_file, d); - break; -#endif - -#ifdef ASM_OUTPUT_DOUBLE - case DFmode: - ASM_OUTPUT_DOUBLE (asm_out_file, d); - break; -#endif - -#ifdef ASM_OUTPUT_LONG_DOUBLE - case XFmode: - case TFmode: - ASM_OUTPUT_LONG_DOUBLE (asm_out_file, d); - break; -#endif - - default: - abort (); - } - - set_float_handler (NULL_PTR); -} - -/* Here we combine duplicate floating constants to make - CONST_DOUBLE rtx's, and force those out to memory when necessary. */ - -/* Chain of all CONST_DOUBLE rtx's constructed for the current function. - They are chained through the CONST_DOUBLE_CHAIN. - A CONST_DOUBLE rtx has CONST_DOUBLE_MEM != cc0_rtx iff it is on this chain. - In that case, CONST_DOUBLE_MEM is either a MEM, - or const0_rtx if no MEM has been made for this CONST_DOUBLE yet. - - (CONST_DOUBLE_MEM is used only for top-level functions. - See force_const_mem for explanation.) */ - -static rtx const_double_chain; - -/* Return a CONST_DOUBLE for a value specified as a pair of ints. - For an integer, I0 is the low-order word and I1 is the high-order word. - For a real number, I0 is the word with the low address - and I1 is the word with the high address. */ - -rtx -immed_double_const (i0, i1, mode) - HOST_WIDE_INT i0, i1; - enum machine_mode mode; -{ - register rtx r; - int in_current_obstack; - - if (GET_MODE_CLASS (mode) == MODE_INT - || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT) - { - /* We clear out all bits that don't belong in MODE, unless they and our - sign bit are all one. So we get either a reasonable negative value - or a reasonable unsigned value for this mode. */ - int width = GET_MODE_BITSIZE (mode); - if (width < HOST_BITS_PER_WIDE_INT - && ((i0 & ((HOST_WIDE_INT) (-1) << (width - 1))) - != ((HOST_WIDE_INT) (-1) << (width - 1)))) - i0 &= ((HOST_WIDE_INT) 1 << width) - 1, i1 = 0; - else if (width == HOST_BITS_PER_WIDE_INT - && ! (i1 == ~0 && i0 < 0)) - i1 = 0; - else if (width > 2 * HOST_BITS_PER_WIDE_INT) - /* We cannot represent this value as a constant. */ - abort (); - - /* If MODE fits within HOST_BITS_PER_WIDE_INT, always use a CONST_INT. - - ??? Strictly speaking, this is wrong if we create a CONST_INT - for a large unsigned constant with the size of MODE being - HOST_BITS_PER_WIDE_INT and later try to interpret that constant in a - wider mode. In that case we will mis-interpret it as a negative - number. - - Unfortunately, the only alternative is to make a CONST_DOUBLE - for any constant in any mode if it is an unsigned constant larger - than the maximum signed integer in an int on the host. However, - doing this will break everyone that always expects to see a CONST_INT - for SImode and smaller. - - We have always been making CONST_INTs in this case, so nothing new - is being broken. */ - - if (width <= HOST_BITS_PER_WIDE_INT) - i1 = (i0 < 0) ? ~0 : 0; - - /* If this integer fits in one word, return a CONST_INT. */ - if ((i1 == 0 && i0 >= 0) - || (i1 == ~0 && i0 < 0)) - return GEN_INT (i0); - - /* We use VOIDmode for integers. */ - mode = VOIDmode; - } - - /* Search the chain for an existing CONST_DOUBLE with the right value. - If one is found, return it. */ - - for (r = const_double_chain; r; r = CONST_DOUBLE_CHAIN (r)) - if (CONST_DOUBLE_LOW (r) == i0 && CONST_DOUBLE_HIGH (r) == i1 - && GET_MODE (r) == mode) - return r; - - /* No; make a new one and add it to the chain. - - We may be called by an optimizer which may be discarding any memory - allocated during its processing (such as combine and loop). However, - we will be leaving this constant on the chain, so we cannot tolerate - freed memory. So switch to saveable_obstack for this allocation - and then switch back if we were in current_obstack. */ - - push_obstacks_nochange (); - rtl_in_saveable_obstack (); - r = gen_rtx (CONST_DOUBLE, mode, 0, i0, i1); - pop_obstacks (); - - /* Don't touch const_double_chain in nested function; - see force_const_mem. */ - if (outer_function_chain == 0) - { - CONST_DOUBLE_CHAIN (r) = const_double_chain; - const_double_chain = r; - } - - /* Store const0_rtx in mem-slot since this CONST_DOUBLE is on the chain. - Actual use of mem-slot is only through force_const_mem. */ - - CONST_DOUBLE_MEM (r) = const0_rtx; - - return r; -} - -/* Return a CONST_DOUBLE for a specified `double' value - and machine mode. */ - -rtx -immed_real_const_1 (d, mode) - REAL_VALUE_TYPE d; - enum machine_mode mode; -{ - union real_extract u; - register rtx r; - int in_current_obstack; - - /* Get the desired `double' value as a sequence of ints - since that is how they are stored in a CONST_DOUBLE. */ - - u.d = d; - - /* Detect special cases. */ - - /* Avoid REAL_VALUES_EQUAL here in order to distinguish minus zero. */ - if (!bcmp (&dconst0, &d, sizeof d)) - return CONST0_RTX (mode); - else if (REAL_VALUES_EQUAL (dconst1, d)) - return CONST1_RTX (mode); - - if (sizeof u == 2 * sizeof (HOST_WIDE_INT)) - return immed_double_const (u.i[0], u.i[1], mode); - - /* The rest of this function handles the case where - a float value requires more than 2 ints of space. - It will be deleted as dead code on machines that don't need it. */ - - /* Search the chain for an existing CONST_DOUBLE with the right value. - If one is found, return it. */ - - for (r = const_double_chain; r; r = CONST_DOUBLE_CHAIN (r)) - if (! bcmp (&CONST_DOUBLE_LOW (r), &u, sizeof u) - && GET_MODE (r) == mode) - return r; - - /* No; make a new one and add it to the chain. - - We may be called by an optimizer which may be discarding any memory - allocated during its processing (such as combine and loop). However, - we will be leaving this constant on the chain, so we cannot tolerate - freed memory. So switch to saveable_obstack for this allocation - and then switch back if we were in current_obstack. */ - - push_obstacks_nochange (); - rtl_in_saveable_obstack (); - r = rtx_alloc (CONST_DOUBLE); - PUT_MODE (r, mode); - bcopy (&u, &CONST_DOUBLE_LOW (r), sizeof u); - pop_obstacks (); - - /* Don't touch const_double_chain in nested function; - see force_const_mem. */ - if (outer_function_chain == 0) - { - CONST_DOUBLE_CHAIN (r) = const_double_chain; - const_double_chain = r; - } - - /* Store const0_rtx in CONST_DOUBLE_MEM since this CONST_DOUBLE is on the - chain, but has not been allocated memory. Actual use of CONST_DOUBLE_MEM - is only through force_const_mem. */ - - CONST_DOUBLE_MEM (r) = const0_rtx; - - return r; -} - -/* Return a CONST_DOUBLE rtx for a value specified by EXP, - which must be a REAL_CST tree node. */ - -rtx -immed_real_const (exp) - tree exp; -{ - return immed_real_const_1 (TREE_REAL_CST (exp), TYPE_MODE (TREE_TYPE (exp))); -} - -/* At the end of a function, forget the memory-constants - previously made for CONST_DOUBLEs. Mark them as not on real_constant_chain. - Also clear out real_constant_chain and clear out all the chain-pointers. */ - -void -clear_const_double_mem () -{ - register rtx r, next; - - /* Don't touch CONST_DOUBLE_MEM for nested functions. - See force_const_mem for explanation. */ - if (outer_function_chain != 0) - return; - - for (r = const_double_chain; r; r = next) - { - next = CONST_DOUBLE_CHAIN (r); - CONST_DOUBLE_CHAIN (r) = 0; - CONST_DOUBLE_MEM (r) = cc0_rtx; - } - const_double_chain = 0; -} - -/* Given an expression EXP with a constant value, - reduce it to the sum of an assembler symbol and an integer. - Store them both in the structure *VALUE. - Abort if EXP does not reduce. */ - -struct addr_const -{ - rtx base; - HOST_WIDE_INT offset; -}; - -static void -decode_addr_const (exp, value) - tree exp; - struct addr_const *value; -{ - register tree target = TREE_OPERAND (exp, 0); - register int offset = 0; - register rtx x; - - while (1) - { - if (TREE_CODE (target) == COMPONENT_REF - && (TREE_CODE (DECL_FIELD_BITPOS (TREE_OPERAND (target, 1))) - == INTEGER_CST)) - { - offset += TREE_INT_CST_LOW (DECL_FIELD_BITPOS (TREE_OPERAND (target, 1))) / BITS_PER_UNIT; - target = TREE_OPERAND (target, 0); - } - else if (TREE_CODE (target) == ARRAY_REF) - { - if (TREE_CODE (TREE_OPERAND (target, 1)) != INTEGER_CST - || TREE_CODE (TYPE_SIZE (TREE_TYPE (target))) != INTEGER_CST) - abort (); - offset += ((TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (target))) - * TREE_INT_CST_LOW (TREE_OPERAND (target, 1))) - / BITS_PER_UNIT); - target = TREE_OPERAND (target, 0); - } - else - break; - } - - switch (TREE_CODE (target)) - { - case VAR_DECL: - case FUNCTION_DECL: - x = DECL_RTL (target); - break; - - case LABEL_DECL: - x = gen_rtx (MEM, FUNCTION_MODE, - gen_rtx (LABEL_REF, VOIDmode, - label_rtx (TREE_OPERAND (exp, 0)))); - break; - - case REAL_CST: - case STRING_CST: - case COMPLEX_CST: - case CONSTRUCTOR: - x = TREE_CST_RTL (target); - break; - - default: - abort (); - } - - if (GET_CODE (x) != MEM) - abort (); - x = XEXP (x, 0); - - value->base = x; - value->offset = offset; -} - -/* Uniquize all constants that appear in memory. - Each constant in memory thus far output is recorded - in `const_hash_table' with a `struct constant_descriptor' - that contains a polish representation of the value of - the constant. - - We cannot store the trees in the hash table - because the trees may be temporary. */ - -struct constant_descriptor -{ - struct constant_descriptor *next; - char *label; - char contents[1]; -}; - -#define HASHBITS 30 -#define MAX_HASH_TABLE 1009 -static struct constant_descriptor *const_hash_table[MAX_HASH_TABLE]; - -/* Compute a hash code for a constant expression. */ - -int -const_hash (exp) - tree exp; -{ - register char *p; - register int len, hi, i; - register enum tree_code code = TREE_CODE (exp); - - if (code == INTEGER_CST) - { - p = (char *) &TREE_INT_CST_LOW (exp); - len = 2 * sizeof TREE_INT_CST_LOW (exp); - } - else if (code == REAL_CST) - { - p = (char *) &TREE_REAL_CST (exp); - len = sizeof TREE_REAL_CST (exp); - } - else if (code == STRING_CST) - p = TREE_STRING_POINTER (exp), len = TREE_STRING_LENGTH (exp); - else if (code == COMPLEX_CST) - return const_hash (TREE_REALPART (exp)) * 5 - + const_hash (TREE_IMAGPART (exp)); - else if (code == CONSTRUCTOR) - { - register tree link; - - /* For record type, include the type in the hashing. - We do not do so for array types - because (1) the sizes of the elements are sufficient - and (2) distinct array types can have the same constructor. - Instead, we include the array size because the constructor could - be shorter. */ - if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE) - hi = ((HOST_WIDE_INT) TREE_TYPE (exp) & ((1 << HASHBITS) - 1)) - % MAX_HASH_TABLE; - else - hi = ((5 + int_size_in_bytes (TREE_TYPE (exp))) - & ((1 << HASHBITS) - 1)) % MAX_HASH_TABLE; - - for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) - if (TREE_VALUE (link)) - hi = (hi * 603 + const_hash (TREE_VALUE (link))) % MAX_HASH_TABLE; - - return hi; - } - else if (code == ADDR_EXPR) - { - struct addr_const value; - decode_addr_const (exp, &value); - if (GET_CODE (value.base) == SYMBOL_REF) - { - /* Don't hash the address of the SYMBOL_REF; - only use the offset and the symbol name. */ - hi = value.offset; - p = XSTR (value.base, 0); - for (i = 0; p[i] != 0; i++) - hi = ((hi * 613) + (unsigned)(p[i])); - } - else if (GET_CODE (value.base) == LABEL_REF) - hi = value.offset + CODE_LABEL_NUMBER (XEXP (value.base, 0)) * 13; - - hi &= (1 << HASHBITS) - 1; - hi %= MAX_HASH_TABLE; - return hi; - } - else if (code == PLUS_EXPR || code == MINUS_EXPR) - return const_hash (TREE_OPERAND (exp, 0)) * 9 - + const_hash (TREE_OPERAND (exp, 1)); - else if (code == NOP_EXPR || code == CONVERT_EXPR) - return const_hash (TREE_OPERAND (exp, 0)) * 7 + 2; - - /* Compute hashing function */ - hi = len; - for (i = 0; i < len; i++) - hi = ((hi * 613) + (unsigned)(p[i])); - - hi &= (1 << HASHBITS) - 1; - hi %= MAX_HASH_TABLE; - return hi; -} - -/* Compare a constant expression EXP with a constant-descriptor DESC. - Return 1 if DESC describes a constant with the same value as EXP. */ - -static int -compare_constant (exp, desc) - tree exp; - struct constant_descriptor *desc; -{ - return 0 != compare_constant_1 (exp, desc->contents); -} - -/* Compare constant expression EXP with a substring P of a constant descriptor. - If they match, return a pointer to the end of the substring matched. - If they do not match, return 0. - - Since descriptors are written in polish prefix notation, - this function can be used recursively to test one operand of EXP - against a subdescriptor, and if it succeeds it returns the - address of the subdescriptor for the next operand. */ - -static char * -compare_constant_1 (exp, p) - tree exp; - char *p; -{ - register char *strp; - register int len; - register enum tree_code code = TREE_CODE (exp); - - if (code != (enum tree_code) *p++) - return 0; - - if (code == INTEGER_CST) - { - /* Integer constants are the same only if the same width of type. */ - if (*p++ != TYPE_PRECISION (TREE_TYPE (exp))) - return 0; - strp = (char *) &TREE_INT_CST_LOW (exp); - len = 2 * sizeof TREE_INT_CST_LOW (exp); - } - else if (code == REAL_CST) - { - /* Real constants are the same only if the same width of type. */ - if (*p++ != TYPE_PRECISION (TREE_TYPE (exp))) - return 0; - strp = (char *) &TREE_REAL_CST (exp); - len = sizeof TREE_REAL_CST (exp); - } - else if (code == STRING_CST) - { - if (flag_writable_strings) - return 0; - strp = TREE_STRING_POINTER (exp); - len = TREE_STRING_LENGTH (exp); - if (bcmp (&TREE_STRING_LENGTH (exp), p, - sizeof TREE_STRING_LENGTH (exp))) - return 0; - p += sizeof TREE_STRING_LENGTH (exp); - } - else if (code == COMPLEX_CST) - { - p = compare_constant_1 (TREE_REALPART (exp), p); - if (p == 0) return 0; - p = compare_constant_1 (TREE_IMAGPART (exp), p); - return p; - } - else if (code == CONSTRUCTOR) - { - register tree link; - int length = list_length (CONSTRUCTOR_ELTS (exp)); - tree type; - - if (bcmp (&length, p, sizeof length)) - return 0; - p += sizeof length; - - /* For record constructors, insist that the types match. - For arrays, just verify both constructors are for arrays. */ - if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE) - type = TREE_TYPE (exp); - else - type = 0; - if (bcmp (&type, p, sizeof type)) - return 0; - p += sizeof type; - - /* For arrays, insist that the size in bytes match. */ - if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE) - { - int size = int_size_in_bytes (TREE_TYPE (exp)); - if (bcmp (&size, p, sizeof size)) - return 0; - p += sizeof size; - } - - for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) - { - if (TREE_VALUE (link)) - { - if ((p = compare_constant_1 (TREE_VALUE (link), p)) == 0) - return 0; - } - else - { - tree zero = 0; - - if (bcmp (&zero, p, sizeof zero)) - return 0; - p += sizeof zero; - } - } - - return p; - } - else if (code == ADDR_EXPR) - { - struct addr_const value; - decode_addr_const (exp, &value); - strp = (char *) &value.offset; - len = sizeof value.offset; - /* Compare the offset. */ - while (--len >= 0) - if (*p++ != *strp++) - return 0; - /* Compare symbol name. */ - strp = XSTR (value.base, 0); - len = strlen (strp) + 1; - } - else if (code == PLUS_EXPR || code == MINUS_EXPR) - { - p = compare_constant_1 (TREE_OPERAND (exp, 0), p); - if (p == 0) return 0; - p = compare_constant_1 (TREE_OPERAND (exp, 1), p); - return p; - } - else if (code == NOP_EXPR || code == CONVERT_EXPR) - { - p = compare_constant_1 (TREE_OPERAND (exp, 0), p); - return p; - } - - /* Compare constant contents. */ - while (--len >= 0) - if (*p++ != *strp++) - return 0; - - return p; -} - -/* Construct a constant descriptor for the expression EXP. - It is up to the caller to enter the descriptor in the hash table. */ - -static struct constant_descriptor * -record_constant (exp) - tree exp; -{ - struct constant_descriptor *next = 0; - char *label = 0; - - /* Make a struct constant_descriptor. The first two pointers will - be filled in later. Here we just leave space for them. */ - - obstack_grow (&permanent_obstack, (char *) &next, sizeof next); - obstack_grow (&permanent_obstack, (char *) &label, sizeof label); - record_constant_1 (exp); - return (struct constant_descriptor *) obstack_finish (&permanent_obstack); -} - -/* Add a description of constant expression EXP - to the object growing in `permanent_obstack'. - No need to return its address; the caller will get that - from the obstack when the object is complete. */ - -static void -record_constant_1 (exp) - tree exp; -{ - register char *strp; - register int len; - register enum tree_code code = TREE_CODE (exp); - - obstack_1grow (&permanent_obstack, (unsigned int) code); - - if (code == INTEGER_CST) - { - obstack_1grow (&permanent_obstack, TYPE_PRECISION (TREE_TYPE (exp))); - strp = (char *) &TREE_INT_CST_LOW (exp); - len = 2 * sizeof TREE_INT_CST_LOW (exp); - } - else if (code == REAL_CST) - { - obstack_1grow (&permanent_obstack, TYPE_PRECISION (TREE_TYPE (exp))); - strp = (char *) &TREE_REAL_CST (exp); - len = sizeof TREE_REAL_CST (exp); - } - else if (code == STRING_CST) - { - if (flag_writable_strings) - return; - strp = TREE_STRING_POINTER (exp); - len = TREE_STRING_LENGTH (exp); - obstack_grow (&permanent_obstack, (char *) &TREE_STRING_LENGTH (exp), - sizeof TREE_STRING_LENGTH (exp)); - } - else if (code == COMPLEX_CST) - { - record_constant_1 (TREE_REALPART (exp)); - record_constant_1 (TREE_IMAGPART (exp)); - return; - } - else if (code == CONSTRUCTOR) - { - register tree link; - int length = list_length (CONSTRUCTOR_ELTS (exp)); - tree type; - - obstack_grow (&permanent_obstack, (char *) &length, sizeof length); - - /* For record constructors, insist that the types match. - For arrays, just verify both constructors are for arrays. */ - if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE) - type = TREE_TYPE (exp); - else - type = 0; - obstack_grow (&permanent_obstack, (char *) &type, sizeof type); - - /* For arrays, insist that the size in bytes match. */ - if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE) - { - int size = int_size_in_bytes (TREE_TYPE (exp)); - obstack_grow (&permanent_obstack, (char *) &size, sizeof size); - } - - for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) - { - if (TREE_VALUE (link)) - record_constant_1 (TREE_VALUE (link)); - else - { - tree zero = 0; - - obstack_grow (&permanent_obstack, (char *) &zero, sizeof zero); - } - } - - return; - } - else if (code == ADDR_EXPR) - { - struct addr_const value; - decode_addr_const (exp, &value); - /* Record the offset. */ - obstack_grow (&permanent_obstack, - (char *) &value.offset, sizeof value.offset); - /* Record the symbol name. */ - obstack_grow (&permanent_obstack, XSTR (value.base, 0), - strlen (XSTR (value.base, 0)) + 1); - return; - } - else if (code == PLUS_EXPR || code == MINUS_EXPR) - { - record_constant_1 (TREE_OPERAND (exp, 0)); - record_constant_1 (TREE_OPERAND (exp, 1)); - return; - } - else if (code == NOP_EXPR || code == CONVERT_EXPR) - { - record_constant_1 (TREE_OPERAND (exp, 0)); - return; - } - - /* Record constant contents. */ - obstack_grow (&permanent_obstack, strp, len); -} - -/* Return an rtx representing a reference to constant data in memory - for the constant expression EXP. - If assembler code for such a constant has already been output, - return an rtx to refer to it. - Otherwise, output such a constant in memory and generate - an rtx for it. The TREE_CST_RTL of EXP is set up to point to that rtx. - The const_hash_table records which constants already have label strings. */ - -rtx -output_constant_def (exp) - tree exp; -{ - register int hash, align; - register struct constant_descriptor *desc; - char label[256]; - char *found = 0; - int reloc; - register rtx def; - - if (TREE_CODE (exp) == INTEGER_CST) - abort (); /* No TREE_CST_RTL slot in these. */ - - if (TREE_CST_RTL (exp)) - return TREE_CST_RTL (exp); - - /* Make sure any other constants whose addresses appear in EXP - are assigned label numbers. */ - - reloc = output_addressed_constants (exp); - - /* Compute hash code of EXP. Search the descriptors for that hash code - to see if any of them describes EXP. If yes, the descriptor records - the label number already assigned. */ - - hash = const_hash (exp) % MAX_HASH_TABLE; - - for (desc = const_hash_table[hash]; desc; desc = desc->next) - if (compare_constant (exp, desc)) - { - found = desc->label; - break; - } - - if (found == 0) - { - /* No constant equal to EXP is known to have been output. - Make a constant descriptor to enter EXP in the hash table. - Assign the label number and record it in the descriptor for - future calls to this function to find. */ - - /* Create a string containing the label name, in LABEL. */ - ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno); - - desc = record_constant (exp); - desc->next = const_hash_table[hash]; - desc->label - = (char *) obstack_copy0 (&permanent_obstack, label, strlen (label)); - const_hash_table[hash] = desc; - } - - /* We have a symbol name; construct the SYMBOL_REF and the MEM. */ - - push_obstacks_nochange (); - if (TREE_PERMANENT (exp)) - end_temporary_allocation (); - - def = gen_rtx (SYMBOL_REF, Pmode, desc->label); - - TREE_CST_RTL (exp) - = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), def); - RTX_UNCHANGING_P (TREE_CST_RTL (exp)) = 1; - if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE) - MEM_IN_STRUCT_P (TREE_CST_RTL (exp)) = 1; - - pop_obstacks (); - - /* Optionally set flags or add text to the name to record information - such as that it is a function name. If the name is changed, the macro - ASM_OUTPUT_LABELREF will have to know how to strip this information. - And if it finds a * at the beginning after doing so, it must handle - that too. */ -#ifdef ENCODE_SECTION_INFO - ENCODE_SECTION_INFO (exp); -#endif - - if (found == 0) - { - /* Now output assembler code to define that label - and follow it with the data of EXP. */ - - /* First switch to text section, except for writable strings. */ -#ifdef SELECT_SECTION - SELECT_SECTION (exp, reloc); -#else - if (((TREE_CODE (exp) == STRING_CST) && flag_writable_strings) - || (flag_pic && reloc)) - data_section (); - else - readonly_data_section (); -#endif - - /* Align the location counter as required by EXP's data type. */ - align = TYPE_ALIGN (TREE_TYPE (exp)); -#ifdef CONSTANT_ALIGNMENT - align = CONSTANT_ALIGNMENT (exp, align); -#endif - - if (align > BITS_PER_UNIT) - ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); - - /* Output the label itself. */ - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LC", const_labelno); - - /* Output the value of EXP. */ - output_constant (exp, - (TREE_CODE (exp) == STRING_CST - ? TREE_STRING_LENGTH (exp) - : int_size_in_bytes (TREE_TYPE (exp)))); - - ++const_labelno; - } - - return TREE_CST_RTL (exp); -} - -/* Similar hash facility for making memory-constants - from constant rtl-expressions. It is used on RISC machines - where immediate integer arguments and constant addresses are restricted - so that such constants must be stored in memory. - - This pool of constants is reinitialized for each function - so each function gets its own constants-pool that comes right before it. - - All structures allocated here are discarded when functions are saved for - inlining, so they do not need to be allocated permanently. */ - -#define MAX_RTX_HASH_TABLE 61 -static struct constant_descriptor **const_rtx_hash_table; - -/* Structure to represent sufficient information about a constant so that - it can be output when the constant pool is output, so that function - integration can be done, and to simplify handling on machines that reference - constant pool as base+displacement. */ - -struct pool_constant -{ - struct constant_descriptor *desc; - struct pool_constant *next; - enum machine_mode mode; - rtx constant; - int labelno; - int align; - int offset; -}; - -/* Pointers to first and last constant in pool. */ - -static struct pool_constant *first_pool, *last_pool; - -/* Current offset in constant pool (does not include any machine-specific - header. */ - -static int pool_offset; - -/* Structure used to maintain hash table mapping symbols used to their - corresponding constants. */ - -struct pool_sym -{ - char *label; - struct pool_constant *pool; - struct pool_sym *next; -}; - -static struct pool_sym **const_rtx_sym_hash_table; - -/* Hash code for a SYMBOL_REF with CONSTANT_POOL_ADDRESS_P true. - The argument is XSTR (... , 0) */ - -#define SYMHASH(LABEL) \ - ((((HOST_WIDE_INT) (LABEL)) & ((1 << HASHBITS) - 1)) % MAX_RTX_HASH_TABLE) - -/* Initialize constant pool hashing for next function. */ - -void -init_const_rtx_hash_table () -{ - const_rtx_hash_table - = ((struct constant_descriptor **) - oballoc (MAX_RTX_HASH_TABLE * sizeof (struct constant_descriptor *))); - const_rtx_sym_hash_table - = ((struct pool_sym **) - oballoc (MAX_RTX_HASH_TABLE * sizeof (struct pool_sym *))); - bzero (const_rtx_hash_table, - MAX_RTX_HASH_TABLE * sizeof (struct constant_descriptor *)); - bzero (const_rtx_sym_hash_table, - MAX_RTX_HASH_TABLE * sizeof (struct pool_sym *)); - - first_pool = last_pool = 0; - pool_offset = 0; -} - -/* Save and restore it for a nested function. */ - -void -save_varasm_status (p) - struct function *p; -{ - p->const_rtx_hash_table = const_rtx_hash_table; - p->const_rtx_sym_hash_table = const_rtx_sym_hash_table; - p->first_pool = first_pool; - p->last_pool = last_pool; - p->pool_offset = pool_offset; -} - -void -restore_varasm_status (p) - struct function *p; -{ - const_rtx_hash_table = p->const_rtx_hash_table; - const_rtx_sym_hash_table = p->const_rtx_sym_hash_table; - first_pool = p->first_pool; - last_pool = p->last_pool; - pool_offset = p->pool_offset; -} - -enum kind { RTX_DOUBLE, RTX_INT }; - -struct rtx_const -{ -#ifdef ONLY_INT_FIELDS - unsigned int kind : 16; - unsigned int mode : 16; -#else - enum kind kind : 16; - enum machine_mode mode : 16; -#endif - union { - union real_extract du; - struct addr_const addr; - } un; -}; - -/* Express an rtx for a constant integer (perhaps symbolic) - as the sum of a symbol or label plus an explicit integer. - They are stored into VALUE. */ - -static void -decode_rtx_const (mode, x, value) - enum machine_mode mode; - rtx x; - struct rtx_const *value; -{ - /* Clear the whole structure, including any gaps. */ - - { - int *p = (int *) value; - int *end = (int *) (value + 1); - while (p < end) - *p++ = 0; - } - - value->kind = RTX_INT; /* Most usual kind. */ - value->mode = mode; - - switch (GET_CODE (x)) - { - case CONST_DOUBLE: - value->kind = RTX_DOUBLE; - value->mode = GET_MODE (x); - bcopy (&CONST_DOUBLE_LOW (x), &value->un.du, sizeof value->un.du); - break; - - case CONST_INT: - value->un.addr.offset = INTVAL (x); - break; - - case SYMBOL_REF: - case LABEL_REF: - case PC: - value->un.addr.base = x; - break; - - case CONST: - x = XEXP (x, 0); - if (GET_CODE (x) == PLUS) - { - value->un.addr.base = XEXP (x, 0); - if (GET_CODE (XEXP (x, 1)) != CONST_INT) - abort (); - value->un.addr.offset = INTVAL (XEXP (x, 1)); - } - else if (GET_CODE (x) == MINUS) - { - value->un.addr.base = XEXP (x, 0); - if (GET_CODE (XEXP (x, 1)) != CONST_INT) - abort (); - value->un.addr.offset = - INTVAL (XEXP (x, 1)); - } - else - abort (); - break; - - default: - abort (); - } - - if (value->kind == RTX_INT && value->un.addr.base != 0) - switch (GET_CODE (value->un.addr.base)) - { - case SYMBOL_REF: - case LABEL_REF: - /* Use the string's address, not the SYMBOL_REF's address, - for the sake of addresses of library routines. - For a LABEL_REF, compare labels. */ - value->un.addr.base = XEXP (value->un.addr.base, 0); - } -} - -/* Given a MINUS expression, simplify it if both sides - include the same symbol. */ - -rtx -simplify_subtraction (x) - rtx x; -{ - struct rtx_const val0, val1; - - decode_rtx_const (GET_MODE (x), XEXP (x, 0), &val0); - decode_rtx_const (GET_MODE (x), XEXP (x, 1), &val1); - - if (val0.un.addr.base == val1.un.addr.base) - return GEN_INT (val0.un.addr.offset - val1.un.addr.offset); - return x; -} - -/* Compute a hash code for a constant RTL expression. */ - -int -const_hash_rtx (mode, x) - enum machine_mode mode; - rtx x; -{ - register int hi, i; - - struct rtx_const value; - decode_rtx_const (mode, x, &value); - - /* Compute hashing function */ - hi = 0; - for (i = 0; i < sizeof value / sizeof (int); i++) - hi += ((int *) &value)[i]; - - hi &= (1 << HASHBITS) - 1; - hi %= MAX_RTX_HASH_TABLE; - return hi; -} - -/* Compare a constant rtl object X with a constant-descriptor DESC. - Return 1 if DESC describes a constant with the same value as X. */ - -static int -compare_constant_rtx (mode, x, desc) - enum machine_mode mode; - rtx x; - struct constant_descriptor *desc; -{ - register int *p = (int *) desc->contents; - register int *strp; - register int len; - struct rtx_const value; - - decode_rtx_const (mode, x, &value); - strp = (int *) &value; - len = sizeof value / sizeof (int); - - /* Compare constant contents. */ - while (--len >= 0) - if (*p++ != *strp++) - return 0; - - return 1; -} - -/* Construct a constant descriptor for the rtl-expression X. - It is up to the caller to enter the descriptor in the hash table. */ - -static struct constant_descriptor * -record_constant_rtx (mode, x) - enum machine_mode mode; - rtx x; -{ - struct constant_descriptor *ptr; - char *label; - struct rtx_const value; - - decode_rtx_const (mode, x, &value); - - obstack_grow (current_obstack, &ptr, sizeof ptr); - obstack_grow (current_obstack, &label, sizeof label); - - /* Record constant contents. */ - obstack_grow (current_obstack, &value, sizeof value); - - return (struct constant_descriptor *) obstack_finish (current_obstack); -} - -/* Given a constant rtx X, make (or find) a memory constant for its value - and return a MEM rtx to refer to it in memory. */ - -rtx -force_const_mem (mode, x) - enum machine_mode mode; - rtx x; -{ - register int hash; - register struct constant_descriptor *desc; - char label[256]; - char *found = 0; - rtx def; - - /* If we want this CONST_DOUBLE in the same mode as it is in memory - (this will always be true for floating CONST_DOUBLEs that have been - placed in memory, but not for VOIDmode (integer) CONST_DOUBLEs), - use the previous copy. Otherwise, make a new one. Note that in - the unlikely event that this same CONST_DOUBLE is used in two different - modes in an alternating fashion, we will allocate a lot of different - memory locations, but this should be extremely rare. */ - - /* Don't use CONST_DOUBLE_MEM in a nested function. - Nested functions have their own constant pools, - so they can't share the same values in CONST_DOUBLE_MEM - with the containing function. */ - if (outer_function_chain == 0) - if (GET_CODE (x) == CONST_DOUBLE - && GET_CODE (CONST_DOUBLE_MEM (x)) == MEM - && GET_MODE (CONST_DOUBLE_MEM (x)) == mode) - return CONST_DOUBLE_MEM (x); - - /* Compute hash code of X. Search the descriptors for that hash code - to see if any of them describes X. If yes, the descriptor records - the label number already assigned. */ - - hash = const_hash_rtx (mode, x); - - for (desc = const_rtx_hash_table[hash]; desc; desc = desc->next) - if (compare_constant_rtx (mode, x, desc)) - { - found = desc->label; - break; - } - - if (found == 0) - { - register struct pool_constant *pool; - register struct pool_sym *sym; - int align; - - /* No constant equal to X is known to have been output. - Make a constant descriptor to enter X in the hash table. - Assign the label number and record it in the descriptor for - future calls to this function to find. */ - - desc = record_constant_rtx (mode, x); - desc->next = const_rtx_hash_table[hash]; - const_rtx_hash_table[hash] = desc; - - /* Align the location counter as required by EXP's data type. */ - align = (mode == VOIDmode) ? UNITS_PER_WORD : GET_MODE_SIZE (mode); - if (align > BIGGEST_ALIGNMENT / BITS_PER_UNIT) - align = BIGGEST_ALIGNMENT / BITS_PER_UNIT; - - pool_offset += align - 1; - pool_offset &= ~ (align - 1); - - /* Allocate a pool constant descriptor, fill it in, and chain it in. */ - - pool = (struct pool_constant *) oballoc (sizeof (struct pool_constant)); - pool->desc = desc; - pool->constant = x; - pool->mode = mode; - pool->labelno = const_labelno; - pool->align = align; - pool->offset = pool_offset; - pool->next = 0; - - if (last_pool == 0) - first_pool = pool; - else - last_pool->next = pool; - - last_pool = pool; - pool_offset += GET_MODE_SIZE (mode); - - /* Create a string containing the label name, in LABEL. */ - ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno); - - ++const_labelno; - - desc->label = found - = (char *) obstack_copy0 (saveable_obstack, label, strlen (label)); - - /* Add label to symbol hash table. */ - hash = SYMHASH (found); - sym = (struct pool_sym *) oballoc (sizeof (struct pool_sym)); - sym->label = found; - sym->pool = pool; - sym->next = const_rtx_sym_hash_table[hash]; - const_rtx_sym_hash_table[hash] = sym; - } - - /* We have a symbol name; construct the SYMBOL_REF and the MEM. */ - - def = gen_rtx (MEM, mode, gen_rtx (SYMBOL_REF, Pmode, found)); - - RTX_UNCHANGING_P (def) = 1; - /* Mark the symbol_ref as belonging to this constants pool. */ - CONSTANT_POOL_ADDRESS_P (XEXP (def, 0)) = 1; - current_function_uses_const_pool = 1; - - if (outer_function_chain == 0) - if (GET_CODE (x) == CONST_DOUBLE) - { - if (CONST_DOUBLE_MEM (x) == cc0_rtx) - { - CONST_DOUBLE_CHAIN (x) = const_double_chain; - const_double_chain = x; - } - CONST_DOUBLE_MEM (x) = def; - } - - return def; -} - -/* Given a SYMBOL_REF with CONSTANT_POOL_ADDRESS_P true, return a pointer to - the corresponding pool_constant structure. */ - -static struct pool_constant * -find_pool_constant (addr) - rtx addr; -{ - struct pool_sym *sym; - char *label = XSTR (addr, 0); - - for (sym = const_rtx_sym_hash_table[SYMHASH (label)]; sym; sym = sym->next) - if (sym->label == label) - return sym->pool; - - abort (); -} - -/* Given a constant pool SYMBOL_REF, return the corresponding constant. */ - -rtx -get_pool_constant (addr) - rtx addr; -{ - return (find_pool_constant (addr))->constant; -} - -/* Similar, return the mode. */ - -enum machine_mode -get_pool_mode (addr) - rtx addr; -{ - return (find_pool_constant (addr))->mode; -} - -/* Similar, return the offset in the constant pool. */ - -int -get_pool_offset (addr) - rtx addr; -{ - return (find_pool_constant (addr))->offset; -} - -/* Return the size of the constant pool. */ - -int -get_pool_size () -{ - return pool_offset; -} - -/* Write all the constants in the constant pool. */ - -void -output_constant_pool (fnname, fndecl) - char *fnname; - tree fndecl; -{ - struct pool_constant *pool; - rtx x; - union real_extract u; - -#ifdef ASM_OUTPUT_POOL_PROLOGUE - ASM_OUTPUT_POOL_PROLOGUE (asm_out_file, fnname, fndecl, pool_offset); -#endif - - for (pool = first_pool; pool; pool = pool->next) - { - x = pool->constant; - - /* See if X is a LABEL_REF (or a CONST referring to a LABEL_REF) - whose CODE_LABEL has been deleted. This can occur if a jump table - is eliminated by optimization. If so, write a constant of zero - instead. Note that this can also happen by turning the - CODE_LABEL into a NOTE. */ - if (((GET_CODE (x) == LABEL_REF - && (INSN_DELETED_P (XEXP (x, 0)) - || GET_CODE (XEXP (x, 0)) == NOTE))) - || (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS - && GET_CODE (XEXP (XEXP (x, 0), 0)) == LABEL_REF - && (INSN_DELETED_P (XEXP (XEXP (XEXP (x, 0), 0), 0)) - || GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == NOTE))) - x = const0_rtx; - - /* First switch to correct section. */ -#ifdef SELECT_RTX_SECTION - SELECT_RTX_SECTION (pool->mode, x); -#else - readonly_data_section (); -#endif - -#ifdef ASM_OUTPUT_SPECIAL_POOL_ENTRY - ASM_OUTPUT_SPECIAL_POOL_ENTRY (asm_out_file, x, pool->mode, - pool->align, pool->labelno, done); -#endif - - if (pool->align > 1) - ASM_OUTPUT_ALIGN (asm_out_file, exact_log2 (pool->align)); - - /* Output the label. */ - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LC", pool->labelno); - - /* Output the value of the constant itself. */ - switch (GET_MODE_CLASS (pool->mode)) - { - case MODE_FLOAT: - if (GET_CODE (x) != CONST_DOUBLE) - abort (); - - bcopy (&CONST_DOUBLE_LOW (x), &u, sizeof u); - assemble_real (u.d, pool->mode); - break; - - case MODE_INT: - case MODE_PARTIAL_INT: - assemble_integer (x, GET_MODE_SIZE (pool->mode), 1); - break; - - default: - abort (); - } - - done: ; - } - - /* Done with this pool. */ - first_pool = last_pool = 0; -} - -/* Find all the constants whose addresses are referenced inside of EXP, - and make sure assembler code with a label has been output for each one. - Indicate whether an ADDR_EXPR has been encountered. */ - -int -output_addressed_constants (exp) - tree exp; -{ - int reloc = 0; - - switch (TREE_CODE (exp)) - { - case ADDR_EXPR: - { - register tree constant = TREE_OPERAND (exp, 0); - - while (TREE_CODE (constant) == COMPONENT_REF) - { - constant = TREE_OPERAND (constant, 0); - } - - if (TREE_CODE_CLASS (TREE_CODE (constant)) == 'c' - || TREE_CODE (constant) == CONSTRUCTOR) - /* No need to do anything here - for addresses of variables or functions. */ - output_constant_def (constant); - } - reloc = 1; - break; - - case PLUS_EXPR: - case MINUS_EXPR: - reloc = output_addressed_constants (TREE_OPERAND (exp, 0)); - reloc |= output_addressed_constants (TREE_OPERAND (exp, 1)); - break; - - case NOP_EXPR: - case CONVERT_EXPR: - case NON_LVALUE_EXPR: - reloc = output_addressed_constants (TREE_OPERAND (exp, 0)); - break; - - case CONSTRUCTOR: - { - register tree link; - for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) - if (TREE_VALUE (link) != 0) - reloc |= output_addressed_constants (TREE_VALUE (link)); - } - break; - - case ERROR_MARK: - break; - } - return reloc; -} - -/* Output assembler code for constant EXP to FILE, with no label. - This includes the pseudo-op such as ".int" or ".byte", and a newline. - Assumes output_addressed_constants has been done on EXP already. - - Generate exactly SIZE bytes of assembler data, padding at the end - with zeros if necessary. SIZE must always be specified. - - SIZE is important for structure constructors, - since trailing members may have been omitted from the constructor. - It is also important for initialization of arrays from string constants - since the full length of the string constant might not be wanted. - It is also needed for initialization of unions, where the initializer's - type is just one member, and that may not be as long as the union. - - There a case in which we would fail to output exactly SIZE bytes: - for a structure constructor that wants to produce more than SIZE bytes. - But such constructors will never be generated for any possible input. */ - -void -output_constant (exp, size) - register tree exp; - register int size; -{ - register enum tree_code code = TREE_CODE (TREE_TYPE (exp)); - rtx x; - - if (size == 0) - return; - - /* Allow a constructor with no elements for any data type. - This means to fill the space with zeros. */ - if (TREE_CODE (exp) == CONSTRUCTOR && CONSTRUCTOR_ELTS (exp) == 0) - { - assemble_zeros (size); - return; - } - - /* Eliminate the NOP_EXPR that makes a cast not be an lvalue. - That way we get the constant (we hope) inside it. */ - if (TREE_CODE (exp) == NOP_EXPR - && TREE_TYPE (exp) == TREE_TYPE (TREE_OPERAND (exp, 0))) - exp = TREE_OPERAND (exp, 0); - - switch (code) - { - case CHAR_TYPE: - case BOOLEAN_TYPE: - case INTEGER_TYPE: - case ENUMERAL_TYPE: - case POINTER_TYPE: - case REFERENCE_TYPE: - /* ??? What about (int)((float)(int)&foo + 4) */ - while (TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR - || TREE_CODE (exp) == NON_LVALUE_EXPR) - exp = TREE_OPERAND (exp, 0); - - if (! assemble_integer (expand_expr (exp, NULL_RTX, VOIDmode, - EXPAND_INITIALIZER), - size, 0)) - error ("initializer for integer value is too complicated"); - size = 0; - break; - - case REAL_TYPE: - if (TREE_CODE (exp) != REAL_CST) - error ("initializer for floating value is not a floating constant"); - - assemble_real (TREE_REAL_CST (exp), - mode_for_size (size * BITS_PER_UNIT, MODE_FLOAT, 0)); - size = 0; - break; - - case COMPLEX_TYPE: - output_constant (TREE_REALPART (exp), size / 2); - output_constant (TREE_IMAGPART (exp), size / 2); - size -= (size / 2) * 2; - break; - - case ARRAY_TYPE: - if (TREE_CODE (exp) == CONSTRUCTOR) - { - output_constructor (exp, size); - return; - } - else if (TREE_CODE (exp) == STRING_CST) - { - int excess = 0; - - if (size > TREE_STRING_LENGTH (exp)) - { - excess = size - TREE_STRING_LENGTH (exp); - size = TREE_STRING_LENGTH (exp); - } - - assemble_string (TREE_STRING_POINTER (exp), size); - size = excess; - } - else - abort (); - break; - - case RECORD_TYPE: - case UNION_TYPE: - if (TREE_CODE (exp) == CONSTRUCTOR) - output_constructor (exp, size); - else - abort (); - return; - } - - if (size > 0) - assemble_zeros (size); -} - -/* Subroutine of output_constant, used for CONSTRUCTORs - (aggregate constants). - Generate at least SIZE bytes, padding if necessary. */ - -void -output_constructor (exp, size) - tree exp; - int size; -{ - register tree link, field = 0; - /* Number of bytes output or skipped so far. - In other words, current position within the constructor. */ - int total_bytes = 0; - /* Non-zero means BYTE contains part of a byte, to be output. */ - int byte_buffer_in_use = 0; - register int byte; - - if (HOST_BITS_PER_WIDE_INT < BITS_PER_UNIT) - abort (); - - if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE) - field = TYPE_FIELDS (TREE_TYPE (exp)); - - /* As LINK goes through the elements of the constant, - FIELD goes through the structure fields, if the constant is a structure. - if the constant is a union, then we override this, - by getting the field from the TREE_LIST element. - But the constant could also be an array. Then FIELD is zero. */ - for (link = CONSTRUCTOR_ELTS (exp); - link; - link = TREE_CHAIN (link), - field = field ? TREE_CHAIN (field) : 0) - { - tree val = TREE_VALUE (link); - /* the element in a union constructor specifies the proper field. */ - if (TREE_PURPOSE (link) != 0) - field = TREE_PURPOSE (link); - - /* Eliminate the marker that makes a cast not be an lvalue. */ - if (val != 0) - STRIP_NOPS (val); - - if (field == 0 || !DECL_BIT_FIELD (field)) - { - register int fieldsize; - /* Since this structure is static, - we know the positions are constant. */ - int bitpos = (field ? (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)) - / BITS_PER_UNIT) - : 0); - - /* An element that is not a bit-field. - Output any buffered-up bit-fields preceding it. */ - if (byte_buffer_in_use) - { - ASM_OUTPUT_BYTE (asm_out_file, byte); - total_bytes++; - byte_buffer_in_use = 0; - } - - /* Advance to offset of this element. - Note no alignment needed in an array, since that is guaranteed - if each element has the proper size. */ - if (field != 0 && bitpos != total_bytes) - { - assemble_zeros (bitpos - total_bytes); - total_bytes = bitpos; - } - - /* Determine size this element should occupy. */ - if (field) - { - if (TREE_CODE (DECL_SIZE (field)) != INTEGER_CST) - abort (); - if (TREE_INT_CST_LOW (DECL_SIZE (field)) > 100000) - { - /* This avoids overflow trouble. */ - tree size_tree = size_binop (CEIL_DIV_EXPR, - DECL_SIZE (field), - size_int (BITS_PER_UNIT)); - fieldsize = TREE_INT_CST_LOW (size_tree); - } - else - { - fieldsize = TREE_INT_CST_LOW (DECL_SIZE (field)); - fieldsize = (fieldsize + BITS_PER_UNIT - 1) / BITS_PER_UNIT; - } - } - else - fieldsize = int_size_in_bytes (TREE_TYPE (TREE_TYPE (exp))); - - /* Output the element's initial value. */ - if (val == 0) - assemble_zeros (fieldsize); - else - output_constant (val, fieldsize); - - /* Count its size. */ - total_bytes += fieldsize; - } - else if (val != 0 && TREE_CODE (val) != INTEGER_CST) - error ("invalid initial value for member `%s'", - IDENTIFIER_POINTER (DECL_NAME (field))); - else - { - /* Element that is a bit-field. */ - - int next_offset = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)); - int end_offset - = (next_offset + TREE_INT_CST_LOW (DECL_SIZE (field))); - - if (val == 0) - val = integer_zero_node; - - /* If this field does not start in this (or, next) byte, - skip some bytes. */ - if (next_offset / BITS_PER_UNIT != total_bytes) - { - /* Output remnant of any bit field in previous bytes. */ - if (byte_buffer_in_use) - { - ASM_OUTPUT_BYTE (asm_out_file, byte); - total_bytes++; - byte_buffer_in_use = 0; - } - - /* If still not at proper byte, advance to there. */ - if (next_offset / BITS_PER_UNIT != total_bytes) - { - assemble_zeros (next_offset / BITS_PER_UNIT - total_bytes); - total_bytes = next_offset / BITS_PER_UNIT; - } - } - - if (! byte_buffer_in_use) - byte = 0; - - /* We must split the element into pieces that fall within - separate bytes, and combine each byte with previous or - following bit-fields. */ - - /* next_offset is the offset n fbits from the beginning of - the structure to the next bit of this element to be processed. - end_offset is the offset of the first bit past the end of - this element. */ - while (next_offset < end_offset) - { - int this_time; - int shift, value; - int next_byte = next_offset / BITS_PER_UNIT; - int next_bit = next_offset % BITS_PER_UNIT; - - /* Advance from byte to byte - within this element when necessary. */ - while (next_byte != total_bytes) - { - ASM_OUTPUT_BYTE (asm_out_file, byte); - total_bytes++; - byte = 0; - } - - /* Number of bits we can process at once - (all part of the same byte). */ - this_time = MIN (end_offset - next_offset, - BITS_PER_UNIT - next_bit); -#if BYTES_BIG_ENDIAN - /* On big-endian machine, take the most significant bits - first (of the bits that are significant) - and put them into bytes from the most significant end. */ - shift = end_offset - next_offset - this_time; - /* Don't try to take a bunch of bits that cross - the word boundary in the INTEGER_CST. */ - if (shift < HOST_BITS_PER_WIDE_INT - && shift + this_time > HOST_BITS_PER_WIDE_INT) - { - this_time -= (HOST_BITS_PER_WIDE_INT - shift); - shift = HOST_BITS_PER_WIDE_INT; - } - - /* Now get the bits from the appropriate constant word. */ - if (shift < HOST_BITS_PER_WIDE_INT) - { - value = TREE_INT_CST_LOW (val); - } - else if (shift < 2 * HOST_BITS_PER_WIDE_INT) - { - value = TREE_INT_CST_HIGH (val); - shift -= HOST_BITS_PER_WIDE_INT; - } - else - abort (); - byte |= (((value >> shift) - & (((HOST_WIDE_INT) 1 << this_time) - 1)) - << (BITS_PER_UNIT - this_time - next_bit)); -#else - /* On little-endian machines, - take first the least significant bits of the value - and pack them starting at the least significant - bits of the bytes. */ - shift = (next_offset - - TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field))); - /* Don't try to take a bunch of bits that cross - the word boundary in the INTEGER_CST. */ - if (shift < HOST_BITS_PER_WIDE_INT - && shift + this_time > HOST_BITS_PER_WIDE_INT) - { - this_time -= (HOST_BITS_PER_WIDE_INT - shift); - shift = HOST_BITS_PER_WIDE_INT; - } - - /* Now get the bits from the appropriate constant word. */ - if (shift < HOST_BITS_PER_INT) - value = TREE_INT_CST_LOW (val); - else if (shift < 2 * HOST_BITS_PER_WIDE_INT) - { - value = TREE_INT_CST_HIGH (val); - shift -= HOST_BITS_PER_WIDE_INT; - } - else - abort (); - byte |= ((value >> shift) - & (((HOST_WIDE_INT) 1 << this_time) - 1)) << next_bit; -#endif - next_offset += this_time; - byte_buffer_in_use = 1; - } - } - } - if (byte_buffer_in_use) - { - ASM_OUTPUT_BYTE (asm_out_file, byte); - total_bytes++; - } - if (total_bytes < size) - assemble_zeros (size - total_bytes); -} diff --git a/gnu/usr.bin/cc/common/version.c b/gnu/usr.bin/cc/common/version.c deleted file mode 100644 index d90ae6e192..0000000000 --- a/gnu/usr.bin/cc/common/version.c +++ /dev/null @@ -1 +0,0 @@ -char *version_string = "2.4.3"; diff --git a/gnu/usr.bin/cc/cpp/Makefile b/gnu/usr.bin/cc/cpp/Makefile index d9b8dae461..fceac76374 100644 --- a/gnu/usr.bin/cc/cpp/Makefile +++ b/gnu/usr.bin/cc/cpp/Makefile @@ -1,16 +1,27 @@ -# @(#)Makefile 6.4 (Berkeley) 2/21/91 +# From: @(#)Makefile 6.4 (Berkeley) 2/21/91 +# $Id: Makefile,v 1.4 1993/08/27 23:37:06 rgrimes Exp $ -PROG= cpp +PROG= gcpp BINDIR= /usr/libexec SRCS= cccp.c cexp.y version.c -CFLAGS+= -g -I. -I$(.CURDIR) -I$(.CURDIR)/../common \ +MAN1= gcpp.1 +CFLAGS+= -I. -I$(.CURDIR) -I$(.CURDIR)/../lib \ -DGCC_INCLUDE_DIR=\"\" \ -DGPLUSPLUS_INCLUDE_DIR=\"/usr/include/g++\" YFLAGS= -.PATH: $(.CURDIR)/../common + +MLINKS= gcpp.1 cpp.1 + +.PATH: $(.CURDIR)/../lib afterinstall: install -c -o $(BINOWN) -g $(BINGRP) -m $(BINMODE) \ - $(.CURDIR)/usr.bin.cpp.sh $(DESTDIR)/usr/bin/cpp + $(.CURDIR)/usr.bin.cpp.sh $(DESTDIR)/usr/bin/gcpp + @/bin/rm -rf $(DESTDIR)/usr/bin/cpp + @cd $(DESTDIR)/usr/bin; ln -s gcpp cpp + @echo $(DESTDIR)/usr/bin/cpp -\> $(DESTDIR)/usr/bin/gcpp + @/bin/rm -rf $(DESTDIR)$(BINDIR)/cpp + @cd $(DESTDIR)$(BINDIR); ln -s gcpp cpp + @echo $(DESTDIR)$(BINDIR)/cpp -\> $(DESTDIR)$(BINDIR)/gcpp .include diff --git a/gnu/usr.bin/cc/cpp/cccp.c b/gnu/usr.bin/cc/cpp/cccp.c index 277961c0cd..86ba2cfaa7 100644 --- a/gnu/usr.bin/cc/cpp/cccp.c +++ b/gnu/usr.bin/cc/cpp/cccp.c @@ -559,21 +559,6 @@ static struct default_include { char *fname; int cplusplus; } include_defaults_a /* This is another place that the target system's headers might be. */ { TOOL_INCLUDE_DIR, 0}, #else /* not CROSS_COMPILE */ - /* This should be /use/local/include and should come before - the fixincludes-fixed header files. */ - -/* I've taken all these out because we want gcc to behave as though it's - the native compiler and only search /usr/include. - Paul Richards 93/6/19 -*/ - - /*{ LOCAL_INCLUDE_DIR, 0},*/ - /* This is here ahead of GCC_INCLUDE_DIR because assert.h goes here. - Likewise, behind LOCAL_INCLUDE_DIR, where glibc puts its assert.h. */ - /*{ TOOL_INCLUDE_DIR, 0},*/ - /* This is the dir for fixincludes. Put it just before - the files that we fix. */ - /*{ GCC_INCLUDE_DIR, 0},*/ /* Some systems have an extra dir of include files. */ #ifdef SYSTEM_INCLUDE_DIR { SYSTEM_INCLUDE_DIR, 0}, diff --git a/gnu/usr.bin/cc/cpp/cpp.1 b/gnu/usr.bin/cc/cpp/gcpp.1 similarity index 100% rename from gnu/usr.bin/cc/cpp/cpp.1 rename to gnu/usr.bin/cc/cpp/gcpp.1 diff --git a/gnu/usr.bin/cc/lib/Makefile b/gnu/usr.bin/cc/lib/Makefile new file mode 100644 index 0000000000..bfd8040a1f --- /dev/null +++ b/gnu/usr.bin/cc/lib/Makefile @@ -0,0 +1,20 @@ +LIB = gcc2 + +CFLAGS += -I${.CURDIR} -DNOFPU +NOPROFILE=no + +SRCS = aux-output.c c-common.c caller-save.c calls.c combine.c \ + convert.c cse.c dbxout.c dwarfout.c emit-rtl.c explow.c \ + expmed.c expr.c final.c flow.c fold-const.c function.c \ + getpwd.c global.c insn-attrtab.c insn-emit.c insn-extract.c \ + insn-opinit.c insn-output.c insn-peep.c insn-recog.c \ + integrate.c jump.c local-alloc.c loop.c obstack.c optabs.c \ + print-rtl.c print-tree.c real.c recog.c reg-stack.c regclass.c \ + reload.c reload1.c reorg.c rtl.c rtlanal.c sched.c sdbout.c \ + stmt.c stor-layout.c stupid.c toplev.c tree.c unroll.c \ + varasm.c version.c xcoffout.c + +install: + @echo -n + +.include "lib.mk" diff --git a/gnu/usr.bin/cc/lib/aux-output.c b/gnu/usr.bin/cc/lib/aux-output.c new file mode 100644 index 0000000000..d0b11961b7 --- /dev/null +++ b/gnu/usr.bin/cc/lib/aux-output.c @@ -0,0 +1,1921 @@ +/* Subroutines for insn-output.c for Intel 80386. + Copyright (C) 1988, 1992 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include "config.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "real.h" +#include "insn-config.h" +#include "conditions.h" +#include "insn-flags.h" +#include "output.h" +#include "insn-attr.h" +#include "tree.h" +#include "flags.h" + +#ifdef EXTRA_CONSTRAINT +/* If EXTRA_CONSTRAINT is defined, then the 'S' + constraint in REG_CLASS_FROM_LETTER will no longer work, and various + asm statements that need 'S' for class SIREG will break. */ + error EXTRA_CONSTRAINT conflicts with S constraint letter +/* The previous line used to be #error, but some compilers barf + even if the conditional was untrue. */ +#endif + +#define AT_BP(mode) (gen_rtx (MEM, (mode), frame_pointer_rtx)) + +extern FILE *asm_out_file; +extern char *strcat (); + +char *singlemove_string (); +char *output_move_const_single (); +char *output_fp_cc0_set (); + +char *hi_reg_name[] = HI_REGISTER_NAMES; +char *qi_reg_name[] = QI_REGISTER_NAMES; +char *qi_high_reg_name[] = QI_HIGH_REGISTER_NAMES; + +/* Array of the smallest class containing reg number REGNO, indexed by + REGNO. Used by REGNO_REG_CLASS in i386.h. */ + +enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] = +{ + /* ax, dx, cx, bx */ + AREG, DREG, CREG, BREG, + /* si, di, bp, sp */ + SIREG, DIREG, INDEX_REGS, GENERAL_REGS, + /* FP registers */ + FP_TOP_REG, FP_SECOND_REG, FLOAT_REGS, FLOAT_REGS, + FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, + /* arg pointer */ + INDEX_REGS +}; + +/* Test and compare insns in i386.md store the information needed to + generate branch and scc insns here. */ + +struct rtx_def *i386_compare_op0, *i386_compare_op1; +struct rtx_def *(*i386_compare_gen)(), *(*i386_compare_gen_eq)(); + +/* Output an insn whose source is a 386 integer register. SRC is the + rtx for the register, and TEMPLATE is the op-code template. SRC may + be either SImode or DImode. + + The template will be output with operands[0] as SRC, and operands[1] + as a pointer to the top of the 386 stack. So a call from floatsidf2 + would look like this: + + output_op_from_reg (operands[1], AS1 (fild%z0,%1)); + + where %z0 corresponds to the caller's operands[1], and is used to + emit the proper size suffix. + + ??? Extend this to handle HImode - a 387 can load and store HImode + values directly. */ + +void +output_op_from_reg (src, template) + rtx src; + char *template; +{ + rtx xops[4]; + + xops[0] = src; + xops[1] = AT_SP (Pmode); + xops[2] = GEN_INT (GET_MODE_SIZE (GET_MODE (src))); + xops[3] = stack_pointer_rtx; + + if (GET_MODE_SIZE (GET_MODE (src)) > UNITS_PER_WORD) + { + rtx high = gen_rtx (REG, SImode, REGNO (src) + 1); + output_asm_insn (AS1 (push%L0,%0), &high); + } + output_asm_insn (AS1 (push%L0,%0), &src); + + output_asm_insn (template, xops); + + output_asm_insn (AS2 (add%L3,%2,%3), xops); +} + +/* Output an insn to pop an value from the 387 top-of-stack to 386 + register DEST. The 387 register stack is popped if DIES is true. If + the mode of DEST is an integer mode, a `fist' integer store is done, + otherwise a `fst' float store is done. */ + +void +output_to_reg (dest, dies) + rtx dest; + int dies; +{ + rtx xops[4]; + + xops[0] = AT_SP (Pmode); + xops[1] = stack_pointer_rtx; + xops[2] = GEN_INT (GET_MODE_SIZE (GET_MODE (dest))); + xops[3] = dest; + + output_asm_insn (AS2 (sub%L1,%2,%1), xops); + + if (GET_MODE_CLASS (GET_MODE (dest)) == MODE_INT) + { + if (dies) + output_asm_insn (AS1 (fistp%z3,%y0), xops); + else + output_asm_insn (AS1 (fist%z3,%y0), xops); + } + else if (GET_MODE_CLASS (GET_MODE (dest)) == MODE_FLOAT) + { + if (dies) + output_asm_insn (AS1 (fstp%z3,%y0), xops); + else + output_asm_insn (AS1 (fst%z3,%y0), xops); + } + else + abort (); + + output_asm_insn (AS1 (pop%L0,%0), &dest); + + if (GET_MODE_SIZE (GET_MODE (dest)) > UNITS_PER_WORD) + { + dest = gen_rtx (REG, SImode, REGNO (dest) + 1); + output_asm_insn (AS1 (pop%L0,%0), &dest); + } +} + +char * +singlemove_string (operands) + rtx *operands; +{ + rtx x; + if (GET_CODE (operands[0]) == MEM + && GET_CODE (x = XEXP (operands[0], 0)) == PRE_DEC) + { + if (XEXP (x, 0) != stack_pointer_rtx) + abort (); + return "push%L1 %1"; + } + else if (GET_CODE (operands[1]) == CONST_DOUBLE) + { + return output_move_const_single (operands); + } + else if (GET_CODE (operands[0]) == REG || GET_CODE (operands[1]) == REG) + return AS2 (mov%L0,%1,%0); + else if (CONSTANT_P (operands[1])) + return AS2 (mov%L0,%1,%0); + else + { + output_asm_insn ("push%L1 %1", operands); + return "pop%L0 %0"; + } +} + +/* Return a REG that occurs in ADDR with coefficient 1. + ADDR can be effectively incremented by incrementing REG. */ + +static rtx +find_addr_reg (addr) + rtx addr; +{ + while (GET_CODE (addr) == PLUS) + { + if (GET_CODE (XEXP (addr, 0)) == REG) + addr = XEXP (addr, 0); + else if (GET_CODE (XEXP (addr, 1)) == REG) + addr = XEXP (addr, 1); + else if (CONSTANT_P (XEXP (addr, 0))) + addr = XEXP (addr, 1); + else if (CONSTANT_P (XEXP (addr, 1))) + addr = XEXP (addr, 0); + else + abort (); + } + if (GET_CODE (addr) == REG) + return addr; + abort (); +} + +/* Output an insn to add the constant N to the register X. */ + +static void +asm_add (n, x) + int n; + rtx x; +{ + rtx xops[2]; + xops[1] = x; + if (n < 0) + { + xops[0] = GEN_INT (-n); + output_asm_insn (AS2 (sub%L0,%0,%1), xops); + } + else if (n > 0) + { + xops[0] = GEN_INT (n); + output_asm_insn (AS2 (add%L0,%0,%1), xops); + } +} + +/* Output assembler code to perform a doubleword move insn + with operands OPERANDS. */ + +char * +output_move_double (operands) + rtx *operands; +{ + enum {REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1; + rtx latehalf[2]; + rtx addreg0 = 0, addreg1 = 0; + int dest_overlapped_low = 0; + + /* First classify both operands. */ + + if (REG_P (operands[0])) + optype0 = REGOP; + else if (offsettable_memref_p (operands[0])) + optype0 = OFFSOP; + else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC) + optype0 = POPOP; + else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) + optype0 = PUSHOP; + else if (GET_CODE (operands[0]) == MEM) + optype0 = MEMOP; + else + optype0 = RNDOP; + + if (REG_P (operands[1])) + optype1 = REGOP; + else if (CONSTANT_P (operands[1])) + optype1 = CNSTOP; + else if (offsettable_memref_p (operands[1])) + optype1 = OFFSOP; + else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC) + optype1 = POPOP; + else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) + optype1 = PUSHOP; + else if (GET_CODE (operands[1]) == MEM) + optype1 = MEMOP; + else + optype1 = RNDOP; + + /* Check for the cases that the operand constraints are not + supposed to allow to happen. Abort if we get one, + because generating code for these cases is painful. */ + + if (optype0 == RNDOP || optype1 == RNDOP) + abort (); + + /* If one operand is decrementing and one is incrementing + decrement the former register explicitly + and change that operand into ordinary indexing. */ + + if (optype0 == PUSHOP && optype1 == POPOP) + { + operands[0] = XEXP (XEXP (operands[0], 0), 0); + asm_add (-8, operands[0]); + operands[0] = gen_rtx (MEM, DImode, operands[0]); + optype0 = OFFSOP; + } + if (optype0 == POPOP && optype1 == PUSHOP) + { + operands[1] = XEXP (XEXP (operands[1], 0), 0); + asm_add (-8, operands[1]); + operands[1] = gen_rtx (MEM, DImode, operands[1]); + optype1 = OFFSOP; + } + + /* If an operand is an unoffsettable memory ref, find a register + we can increment temporarily to make it refer to the second word. */ + + if (optype0 == MEMOP) + addreg0 = find_addr_reg (XEXP (operands[0], 0)); + + if (optype1 == MEMOP) + addreg1 = find_addr_reg (XEXP (operands[1], 0)); + + /* Ok, we can do one word at a time. + Normally we do the low-numbered word first, + but if either operand is autodecrementing then we + do the high-numbered word first. + + In either case, set up in LATEHALF the operands to use + for the high-numbered word and in some cases alter the + operands in OPERANDS to be suitable for the low-numbered word. */ + + if (optype0 == REGOP) + latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); + else if (optype0 == OFFSOP) + latehalf[0] = adj_offsettable_operand (operands[0], 4); + else + latehalf[0] = operands[0]; + + if (optype1 == REGOP) + latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); + else if (optype1 == OFFSOP) + latehalf[1] = adj_offsettable_operand (operands[1], 4); + else if (optype1 == CNSTOP) + { + if (GET_CODE (operands[1]) == CONST_DOUBLE) + split_double (operands[1], &operands[1], &latehalf[1]); + else if (CONSTANT_P (operands[1])) + { + if (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) < 0) + latehalf[1] = constm1_rtx; + else + latehalf[1] = const0_rtx; + } + } + else + latehalf[1] = operands[1]; + + /* If insn is effectively movd N (sp),-(sp) then we will do the + high word first. We should use the adjusted operand 1 (which is N+4 (sp)) + for the low word as well, to compensate for the first decrement of sp. */ + if (optype0 == PUSHOP + && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM + && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) + operands[1] = latehalf[1]; + + /* For (set (reg:DI N) (mem:DI ... (reg:SI N) ...)), + if the upper part of reg N does not appear in the MEM, arrange to + emit the move late-half first. Otherwise, compute the MEM address + into the upper part of N and use that as a pointer to the memory + operand. */ + if (optype0 == REGOP + && (optype1 == OFFSOP || optype1 == MEMOP)) + { + if (reg_mentioned_p (operands[0], XEXP (operands[1], 0)) + && reg_mentioned_p (latehalf[0], XEXP (operands[1], 0))) + { + /* If both halves of dest are used in the src memory address, + compute the address into latehalf of dest. */ + rtx xops[2]; + xops[0] = latehalf[0]; + xops[1] = XEXP (operands[1], 0); + output_asm_insn (AS2 (lea%L0,%a1,%0), xops); + operands[1] = gen_rtx (MEM, DImode, latehalf[0]); + latehalf[1] = adj_offsettable_operand (operands[1], 4); + } + else if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))) + /* If the low half of dest is mentioned in the source memory + address, the arrange to emit the move late half first. */ + dest_overlapped_low = 1; + } + + /* If one or both operands autodecrementing, + do the two words, high-numbered first. */ + + /* Likewise, the first move would clobber the source of the second one, + do them in the other order. This happens only for registers; + such overlap can't happen in memory unless the user explicitly + sets it up, and that is an undefined circumstance. */ + + if (optype0 == PUSHOP || optype1 == PUSHOP + || (optype0 == REGOP && optype1 == REGOP + && REGNO (operands[0]) == REGNO (latehalf[1])) + || dest_overlapped_low) + { + /* Make any unoffsettable addresses point at high-numbered word. */ + if (addreg0) + asm_add (4, addreg0); + if (addreg1) + asm_add (4, addreg1); + + /* Do that word. */ + output_asm_insn (singlemove_string (latehalf), latehalf); + + /* Undo the adds we just did. */ + if (addreg0) + asm_add (-4, addreg0); + if (addreg1) + asm_add (-4, addreg1); + + /* Do low-numbered word. */ + return singlemove_string (operands); + } + + /* Normal case: do the two words, low-numbered first. */ + + output_asm_insn (singlemove_string (operands), operands); + + /* Make any unoffsettable addresses point at high-numbered word. */ + if (addreg0) + asm_add (4, addreg0); + if (addreg1) + asm_add (4, addreg1); + + /* Do that word. */ + output_asm_insn (singlemove_string (latehalf), latehalf); + + /* Undo the adds we just did. */ + if (addreg0) + asm_add (-4, addreg0); + if (addreg1) + asm_add (-4, addreg1); + + return ""; +} + +int +standard_80387_constant_p (x) + rtx x; +{ +#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) + REAL_VALUE_TYPE d; + jmp_buf handler; + int is0, is1; + + if (setjmp (handler)) + return 0; + + set_float_handler (handler); + REAL_VALUE_FROM_CONST_DOUBLE (d, x); + is0 = REAL_VALUES_EQUAL (d, dconst0); + is1 = REAL_VALUES_EQUAL (d, dconst1); + set_float_handler (NULL_PTR); + + if (is0) + return 1; + + if (is1) + return 2; + + /* Note that on the 80387, other constants, such as pi, + are much slower to load as standard constants + than to load from doubles in memory! */ +#endif + + return 0; +} + +char * +output_move_const_single (operands) + rtx *operands; +{ + if (FP_REG_P (operands[0])) + { + int conval = standard_80387_constant_p (operands[1]); + + if (conval == 1) + return "fldz"; + + if (conval == 2) + return "fld1"; + } + if (GET_CODE (operands[1]) == CONST_DOUBLE) + { + union { int i[2]; double d;} u1; + union { int i; float f;} u2; + u1.i[0] = CONST_DOUBLE_LOW (operands[1]); + u1.i[1] = CONST_DOUBLE_HIGH (operands[1]); + u2.f = u1.d; + operands[1] = GEN_INT (u2.i); + } + return singlemove_string (operands); +} + +/* Returns 1 if OP is either a symbol reference or a sum of a symbol + reference and a constant. */ + +int +symbolic_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + switch (GET_CODE (op)) + { + case SYMBOL_REF: + case LABEL_REF: + return 1; + case CONST: + op = XEXP (op, 0); + return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF + || GET_CODE (XEXP (op, 0)) == LABEL_REF) + && GET_CODE (XEXP (op, 1)) == CONST_INT); + default: + return 0; + } +} + +/* Test for a valid operand for a call instruction. + Don't allow the arg pointer register or virtual regs + since they may change into reg + const, which the patterns + can't handle yet. */ + +int +call_insn_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) == MEM + && ((CONSTANT_ADDRESS_P (XEXP (op, 0)) + /* This makes a difference for PIC. */ + && general_operand (XEXP (op, 0), Pmode)) + || (GET_CODE (XEXP (op, 0)) == REG + && XEXP (op, 0) != arg_pointer_rtx + && !(REGNO (XEXP (op, 0)) >= FIRST_PSEUDO_REGISTER + && REGNO (XEXP (op, 0)) <= LAST_VIRTUAL_REGISTER)))) + return 1; + return 0; +} + +/* Like call_insn_operand but allow (mem (symbol_ref ...)) + even if pic. */ + +int +expander_call_insn_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) == MEM + && (CONSTANT_ADDRESS_P (XEXP (op, 0)) + || (GET_CODE (XEXP (op, 0)) == REG + && XEXP (op, 0) != arg_pointer_rtx + && !(REGNO (XEXP (op, 0)) >= FIRST_PSEUDO_REGISTER + && REGNO (XEXP (op, 0)) <= LAST_VIRTUAL_REGISTER)))) + return 1; + return 0; +} + +/* Returns 1 if OP contains a symbol reference */ + +int +symbolic_reference_mentioned_p (op) + rtx op; +{ + register char *fmt; + register int i; + + if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF) + return 1; + + fmt = GET_RTX_FORMAT (GET_CODE (op)); + for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--) + { + if (fmt[i] == 'E') + { + register int j; + + for (j = XVECLEN (op, i) - 1; j >= 0; j--) + if (symbolic_reference_mentioned_p (XVECEXP (op, i, j))) + return 1; + } + else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i))) + return 1; + } + + return 0; +} + +/* Return a legitimate reference for ORIG (an address) using the + register REG. If REG is 0, a new pseudo is generated. + + There are three types of references that must be handled: + + 1. Global data references must load the address from the GOT, via + the PIC reg. An insn is emitted to do this load, and the reg is + returned. + + 2. Static data references must compute the address as an offset + from the GOT, whose base is in the PIC reg. An insn is emitted to + compute the address into a reg, and the reg is returned. Static + data objects have SYMBOL_REF_FLAG set to differentiate them from + global data objects. + + 3. Constant pool addresses must be handled special. They are + considered legitimate addresses, but only if not used with regs. + When printed, the output routines know to print the reference with the + PIC reg, even though the PIC reg doesn't appear in the RTL. + + GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC + reg also appears in the address (except for constant pool references, + noted above). + + "switch" statements also require special handling when generating + PIC code. See comments by the `casesi' insn in i386.md for details. */ + +rtx +legitimize_pic_address (orig, reg) + rtx orig; + rtx reg; +{ + rtx addr = orig; + rtx new = orig; + + if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF) + { + if (GET_CODE (addr) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (addr)) + reg = new = orig; + else + { + if (reg == 0) + reg = gen_reg_rtx (Pmode); + + if (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_FLAG (addr)) + new = gen_rtx (PLUS, Pmode, pic_offset_table_rtx, orig); + else + new = gen_rtx (MEM, Pmode, + gen_rtx (PLUS, Pmode, + pic_offset_table_rtx, orig)); + + emit_move_insn (reg, new); + } + current_function_uses_pic_offset_table = 1; + return reg; + } + else if (GET_CODE (addr) == CONST || GET_CODE (addr) == PLUS) + { + rtx base; + + if (GET_CODE (addr) == CONST) + { + addr = XEXP (addr, 0); + if (GET_CODE (addr) != PLUS) + abort (); + } + + if (XEXP (addr, 0) == pic_offset_table_rtx) + return orig; + + if (reg == 0) + reg = gen_reg_rtx (Pmode); + + base = legitimize_pic_address (XEXP (addr, 0), reg); + addr = legitimize_pic_address (XEXP (addr, 1), + base == reg ? NULL_RTX : reg); + + if (GET_CODE (addr) == CONST_INT) + return plus_constant (base, INTVAL (addr)); + + if (GET_CODE (addr) == PLUS && CONSTANT_P (XEXP (addr, 1))) + { + base = gen_rtx (PLUS, Pmode, base, XEXP (addr, 0)); + addr = XEXP (addr, 1); + } + return gen_rtx (PLUS, Pmode, base, addr); + } + return new; +} + +/* Emit insns to move operands[1] into operands[0]. */ + +void +emit_pic_move (operands, mode) + rtx *operands; + enum machine_mode mode; +{ + rtx temp = reload_in_progress ? operands[0] : gen_reg_rtx (Pmode); + + if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1])) + operands[1] = (rtx) force_reg (SImode, operands[1]); + else + operands[1] = legitimize_pic_address (operands[1], temp); +} + +/* This function generates the assembly code for function entry. + FILE is an stdio stream to output the code to. + SIZE is an int: how many units of temporary storage to allocate. */ + +void +function_prologue (file, size) + FILE *file; + int size; +{ + register int regno; + int limit; + rtx xops[4]; + int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table + || current_function_uses_const_pool); + + xops[0] = stack_pointer_rtx; + xops[1] = frame_pointer_rtx; + xops[2] = GEN_INT (size); + if (frame_pointer_needed) + { + output_asm_insn ("push%L1 %1", xops); + output_asm_insn (AS2 (mov%L0,%0,%1), xops); + } + + if (size) + output_asm_insn (AS2 (sub%L0,%2,%0), xops); + + /* Note If use enter it is NOT reversed args. + This one is not reversed from intel!! + I think enter is slower. Also sdb doesn't like it. + But if you want it the code is: + { + xops[3] = const0_rtx; + output_asm_insn ("enter %2,%3", xops); + } + */ + limit = (frame_pointer_needed ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM); + for (regno = limit - 1; regno >= 0; regno--) + if ((regs_ever_live[regno] && ! call_used_regs[regno]) + || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used)) + { + xops[0] = gen_rtx (REG, SImode, regno); + output_asm_insn ("push%L0 %0", xops); + } + + if (pic_reg_used) + { + xops[0] = pic_offset_table_rtx; + xops[1] = (rtx) gen_label_rtx (); + + output_asm_insn (AS1 (call,%P1), xops); + ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (xops[1])); + output_asm_insn (AS1 (pop%L0,%0), xops); + output_asm_insn ("addl $_GLOBAL_OFFSET_TABLE_+[.-%P1],%0", xops); + } +} + +/* Return 1 if it is appropriate to emit `ret' instructions in the + body of a function. Do this only if the epilogue is simple, needing a + couple of insns. Prior to reloading, we can't tell how many registers + must be saved, so return 0 then. + + If NON_SAVING_SETJMP is defined and true, then it is not possible + for the epilogue to be simple, so return 0. This is a special case + since NON_SAVING_SETJMP will not cause regs_ever_live to change until + final, but jump_optimize may need to know sooner if a `return' is OK. */ + +int +simple_386_epilogue () +{ + int regno; + int nregs = 0; + int reglimit = (frame_pointer_needed + ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM); + int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table + || current_function_uses_const_pool); + +#ifdef NON_SAVING_SETJMP + if (NON_SAVING_SETJMP && current_function_calls_setjmp) + return 0; +#endif + + if (! reload_completed) + return 0; + + for (regno = reglimit - 1; regno >= 0; regno--) + if ((regs_ever_live[regno] && ! call_used_regs[regno]) + || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used)) + nregs++; + + return nregs == 0 || ! frame_pointer_needed; +} + +/* This function generates the assembly code for function exit. + FILE is an stdio stream to output the code to. + SIZE is an int: how many units of temporary storage to deallocate. */ + +void +function_epilogue (file, size) + FILE *file; + int size; +{ + register int regno; + register int nregs, limit; + int offset; + rtx xops[3]; + int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table + || current_function_uses_const_pool); + + /* Compute the number of registers to pop */ + + limit = (frame_pointer_needed + ? FRAME_POINTER_REGNUM + : STACK_POINTER_REGNUM); + + nregs = 0; + + for (regno = limit - 1; regno >= 0; regno--) + if ((regs_ever_live[regno] && ! call_used_regs[regno]) + || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used)) + nregs++; + + /* sp is often unreliable so we must go off the frame pointer, + */ + + /* In reality, we may not care if sp is unreliable, because we can + restore the register relative to the frame pointer. In theory, + since each move is the same speed as a pop, and we don't need the + leal, this is faster. For now restore multiple registers the old + way. */ + + offset = -size - (nregs * UNITS_PER_WORD); + + xops[2] = stack_pointer_rtx; + + if (nregs > 1 || ! frame_pointer_needed) + { + if (frame_pointer_needed) + { + xops[0] = adj_offsettable_operand (AT_BP (Pmode), offset); + output_asm_insn (AS2 (lea%L2,%0,%2), xops); + } + + for (regno = 0; regno < limit; regno++) + if ((regs_ever_live[regno] && ! call_used_regs[regno]) + || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used)) + { + xops[0] = gen_rtx (REG, SImode, regno); + output_asm_insn ("pop%L0 %0", xops); + } + } + else + for (regno = 0; regno < limit; regno++) + if ((regs_ever_live[regno] && ! call_used_regs[regno]) + || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used)) + { + xops[0] = gen_rtx (REG, SImode, regno); + xops[1] = adj_offsettable_operand (AT_BP (Pmode), offset); + output_asm_insn (AS2 (mov%L0,%1,%0), xops); + offset += 4; + } + + if (frame_pointer_needed) + { + /* On i486, mov & pop is faster than "leave". */ + + if (TARGET_486) + { + xops[0] = frame_pointer_rtx; + output_asm_insn (AS2 (mov%L2,%0,%2), xops); + output_asm_insn ("pop%L0 %0", xops); + } + else + output_asm_insn ("leave", xops); + } + else if (size) + { + /* If there is no frame pointer, we must still release the frame. */ + + xops[0] = GEN_INT (size); + output_asm_insn (AS2 (add%L2,%0,%2), xops); + } + + if (current_function_pops_args && current_function_args_size) + { + xops[1] = GEN_INT (current_function_pops_args); + + /* i386 can only pop 32K bytes (maybe 64K? Is it signed?). If + asked to pop more, pop return address, do explicit add, and jump + indirectly to the caller. */ + + if (current_function_pops_args >= 32768) + { + /* ??? Which register to use here? */ + xops[0] = gen_rtx (REG, SImode, 2); + output_asm_insn ("pop%L0 %0", xops); + output_asm_insn (AS2 (add%L2,%1,%2), xops); + output_asm_insn ("jmp %*%0", xops); + } + else + output_asm_insn ("ret %1", xops); + } + else + output_asm_insn ("ret", xops); +} + +/* Print an integer constant expression in assembler syntax. Addition + and subtraction are the only arithmetic that may appear in these + expressions. FILE is the stdio stream to write to, X is the rtx, and + CODE is the operand print code from the output string. */ + +static void +output_pic_addr_const (file, x, code) + FILE *file; + rtx x; + int code; +{ + char buf[256]; + + switch (GET_CODE (x)) + { + case PC: + if (flag_pic) + putc ('.', file); + else + abort (); + break; + + case SYMBOL_REF: + case LABEL_REF: + if (GET_CODE (x) == SYMBOL_REF) + assemble_name (file, XSTR (x, 0)); + else + { + ASM_GENERATE_INTERNAL_LABEL (buf, "L", + CODE_LABEL_NUMBER (XEXP (x, 0))); + assemble_name (asm_out_file, buf); + } + + if (GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x)) + fprintf (file, "@GOTOFF(%%ebx)"); + else if (code == 'P') + fprintf (file, "@PLT"); + else if (GET_CODE (x) == LABEL_REF || ! SYMBOL_REF_FLAG (x)) + fprintf (file, "@GOT"); + else + fprintf (file, "@GOTOFF"); + + break; + + case CODE_LABEL: + ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x)); + assemble_name (asm_out_file, buf); + break; + + case CONST_INT: + fprintf (file, "%d", INTVAL (x)); + break; + + case CONST: + /* This used to output parentheses around the expression, + but that does not work on the 386 (either ATT or BSD assembler). */ + output_pic_addr_const (file, XEXP (x, 0), code); + break; + + case CONST_DOUBLE: + if (GET_MODE (x) == VOIDmode) + { + /* We can use %d if the number is <32 bits and positive. */ + if (CONST_DOUBLE_HIGH (x) || CONST_DOUBLE_LOW (x) < 0) + fprintf (file, "0x%x%08x", + CONST_DOUBLE_HIGH (x), CONST_DOUBLE_LOW (x)); + else + fprintf (file, "%d", CONST_DOUBLE_LOW (x)); + } + else + /* We can't handle floating point constants; + PRINT_OPERAND must handle them. */ + output_operand_lossage ("floating constant misused"); + break; + + case PLUS: + /* Some assemblers need integer constants to appear last (eg masm). */ + if (GET_CODE (XEXP (x, 0)) == CONST_INT) + { + output_pic_addr_const (file, XEXP (x, 1), code); + if (INTVAL (XEXP (x, 0)) >= 0) + fprintf (file, "+"); + output_pic_addr_const (file, XEXP (x, 0), code); + } + else + { + output_pic_addr_const (file, XEXP (x, 0), code); + if (INTVAL (XEXP (x, 1)) >= 0) + fprintf (file, "+"); + output_pic_addr_const (file, XEXP (x, 1), code); + } + break; + + case MINUS: + output_pic_addr_const (file, XEXP (x, 0), code); + fprintf (file, "-"); + output_pic_addr_const (file, XEXP (x, 1), code); + break; + + default: + output_operand_lossage ("invalid expression as operand"); + } +} + +/* Meaning of CODE: + f -- float insn (print a CONST_DOUBLE as a float rather than in hex). + D,L,W,B,Q,S -- print the opcode suffix for specified size of operand. + R -- print the prefix for register names. + z -- print the opcode suffix for the size of the current operand. + * -- print a star (in certain assembler syntax) + w -- print the operand as if it's a "word" (HImode) even if it isn't. + c -- don't print special prefixes before constant operands. +*/ + +void +print_operand (file, x, code) + FILE *file; + rtx x; + int code; +{ + if (code) + { + switch (code) + { + case '*': + if (USE_STAR) + putc ('*', file); + return; + + case 'L': + PUT_OP_SIZE (code, 'l', file); + return; + + case 'W': + PUT_OP_SIZE (code, 'w', file); + return; + + case 'B': + PUT_OP_SIZE (code, 'b', file); + return; + + case 'Q': + PUT_OP_SIZE (code, 'l', file); + return; + + case 'S': + PUT_OP_SIZE (code, 's', file); + return; + + case 'z': + /* 387 opcodes don't get size suffixes if the operands are + registers. */ + + if (STACK_REG_P (x)) + return; + + /* this is the size of op from size of operand */ + switch (GET_MODE_SIZE (GET_MODE (x))) + { + case 1: + PUT_OP_SIZE ('B', 'b', file); + return; + + case 2: + PUT_OP_SIZE ('W', 'w', file); + return; + + case 4: + if (GET_MODE (x) == SFmode) + { + PUT_OP_SIZE ('S', 's', file); + return; + } + else + PUT_OP_SIZE ('L', 'l', file); + return; + + case 8: + if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT) + { +#ifdef GAS_MNEMONICS + PUT_OP_SIZE ('Q', 'q', file); + return; +#else + PUT_OP_SIZE ('Q', 'l', file); /* Fall through */ +#endif + } + + PUT_OP_SIZE ('Q', 'l', file); + return; + } + + case 'b': + case 'w': + case 'k': + case 'h': + case 'y': + case 'P': + break; + + default: + { + char str[50]; + + sprintf (str, "invalid operand code `%c'", code); + output_operand_lossage (str); + } + } + } + if (GET_CODE (x) == REG) + { + PRINT_REG (x, code, file); + } + else if (GET_CODE (x) == MEM) + { + PRINT_PTR (x, file); + if (CONSTANT_ADDRESS_P (XEXP (x, 0))) + { + if (flag_pic) + output_pic_addr_const (file, XEXP (x, 0), code); + else + output_addr_const (file, XEXP (x, 0)); + } + else + output_address (XEXP (x, 0)); + } + else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == SFmode) + { + union { double d; int i[2]; } u; + union { float f; int i; } u1; + u.i[0] = CONST_DOUBLE_LOW (x); + u.i[1] = CONST_DOUBLE_HIGH (x); + u1.f = u.d; + PRINT_IMMED_PREFIX (file); + fprintf (file, "0x%x", u1.i); + } + else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == DFmode) + { + union { double d; int i[2]; } u; + u.i[0] = CONST_DOUBLE_LOW (x); + u.i[1] = CONST_DOUBLE_HIGH (x); + fprintf (file, "%.22e", u.d); + } + else + { + if (code != 'P') + { + if (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE) + PRINT_IMMED_PREFIX (file); + else if (GET_CODE (x) == CONST || GET_CODE (x) == SYMBOL_REF + || GET_CODE (x) == LABEL_REF) + PRINT_OFFSET_PREFIX (file); + } + if (flag_pic) + output_pic_addr_const (file, x, code); + else + output_addr_const (file, x); + } +} + +/* Print a memory operand whose address is ADDR. */ + +void +print_operand_address (file, addr) + FILE *file; + register rtx addr; +{ + register rtx reg1, reg2, breg, ireg; + rtx offset; + + switch (GET_CODE (addr)) + { + case REG: + ADDR_BEG (file); + fprintf (file, "%se", RP); + fputs (hi_reg_name[REGNO (addr)], file); + ADDR_END (file); + break; + + case PLUS: + reg1 = 0; + reg2 = 0; + ireg = 0; + breg = 0; + offset = 0; + if (CONSTANT_ADDRESS_P (XEXP (addr, 0))) + { + offset = XEXP (addr, 0); + addr = XEXP (addr, 1); + } + else if (CONSTANT_ADDRESS_P (XEXP (addr, 1))) + { + offset = XEXP (addr, 1); + addr = XEXP (addr, 0); + } + if (GET_CODE (addr) != PLUS) ; + else if (GET_CODE (XEXP (addr, 0)) == MULT) + { + reg1 = XEXP (addr, 0); + addr = XEXP (addr, 1); + } + else if (GET_CODE (XEXP (addr, 1)) == MULT) + { + reg1 = XEXP (addr, 1); + addr = XEXP (addr, 0); + } + else if (GET_CODE (XEXP (addr, 0)) == REG) + { + reg1 = XEXP (addr, 0); + addr = XEXP (addr, 1); + } + else if (GET_CODE (XEXP (addr, 1)) == REG) + { + reg1 = XEXP (addr, 1); + addr = XEXP (addr, 0); + } + if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT) + { + if (reg1 == 0) reg1 = addr; + else reg2 = addr; + addr = 0; + } + if (offset != 0) + { + if (addr != 0) abort (); + addr = offset; + } + if ((reg1 && GET_CODE (reg1) == MULT) + || (reg2 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg2)))) + { + breg = reg2; + ireg = reg1; + } + else if (reg1 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg1))) + { + breg = reg1; + ireg = reg2; + } + + if (ireg != 0 || breg != 0) + { + int scale = 1; + + if (addr != 0) + { + if (GET_CODE (addr) == LABEL_REF) + output_asm_label (addr); + else + { + if (flag_pic) + output_pic_addr_const (file, addr, 0); + else + output_addr_const (file, addr); + } + } + + if (ireg != 0 && GET_CODE (ireg) == MULT) + { + scale = INTVAL (XEXP (ireg, 1)); + ireg = XEXP (ireg, 0); + } + + /* The stack pointer can only appear as a base register, + never an index register, so exchange the regs if it is wrong. */ + + if (scale == 1 && ireg && REGNO (ireg) == STACK_POINTER_REGNUM) + { + rtx tmp; + + tmp = breg; + breg = ireg; + ireg = tmp; + } + + /* output breg+ireg*scale */ + PRINT_B_I_S (breg, ireg, scale, file); + break; + } + + case MULT: + { + int scale; + if (GET_CODE (XEXP (addr, 0)) == CONST_INT) + { + scale = INTVAL (XEXP (addr, 0)); + ireg = XEXP (addr, 1); + } + else + { + scale = INTVAL (XEXP (addr, 1)); + ireg = XEXP (addr, 0); + } + output_addr_const (file, const0_rtx); + PRINT_B_I_S ((rtx) 0, ireg, scale, file); + } + break; + + default: + if (GET_CODE (addr) == CONST_INT + && INTVAL (addr) < 0x8000 + && INTVAL (addr) >= -0x8000) + fprintf (file, "%d", INTVAL (addr)); + else + { + if (flag_pic) + output_pic_addr_const (file, addr, 0); + else + output_addr_const (file, addr); + } + } +} + +/* Set the cc_status for the results of an insn whose pattern is EXP. + On the 80386, we assume that only test and compare insns, as well + as SI, HI, & DI mode ADD, SUB, NEG, AND, IOR, XOR, ASHIFT, LSHIFT, + ASHIFTRT, and LSHIFTRT instructions set the condition codes usefully. + Also, we assume that jumps, moves and sCOND don't affect the condition + codes. All else clobbers the condition codes, by assumption. + + We assume that ALL integer add, minus, etc. instructions effect the + condition codes. This MUST be consistent with i386.md. + + We don't record any float test or compare - the redundant test & + compare check in final.c does not handle stack-like regs correctly. */ + +void +notice_update_cc (exp) + rtx exp; +{ + if (GET_CODE (exp) == SET) + { + /* Jumps do not alter the cc's. */ + if (SET_DEST (exp) == pc_rtx) + return; + /* Moving register or memory into a register: + it doesn't alter the cc's, but it might invalidate + the RTX's which we remember the cc's came from. + (Note that moving a constant 0 or 1 MAY set the cc's). */ + if (REG_P (SET_DEST (exp)) + && (REG_P (SET_SRC (exp)) || GET_CODE (SET_SRC (exp)) == MEM + || GET_RTX_CLASS (GET_CODE (SET_SRC (exp))) == '<')) + { + if (cc_status.value1 + && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1)) + cc_status.value1 = 0; + if (cc_status.value2 + && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2)) + cc_status.value2 = 0; + return; + } + /* Moving register into memory doesn't alter the cc's. + It may invalidate the RTX's which we remember the cc's came from. */ + if (GET_CODE (SET_DEST (exp)) == MEM + && (REG_P (SET_SRC (exp)) + || GET_RTX_CLASS (GET_CODE (SET_SRC (exp))) == '<')) + { + if (cc_status.value1 && GET_CODE (cc_status.value1) == MEM) + cc_status.value1 = 0; + if (cc_status.value2 && GET_CODE (cc_status.value2) == MEM) + cc_status.value2 = 0; + return; + } + /* Function calls clobber the cc's. */ + else if (GET_CODE (SET_SRC (exp)) == CALL) + { + CC_STATUS_INIT; + return; + } + /* Tests and compares set the cc's in predictable ways. */ + else if (SET_DEST (exp) == cc0_rtx) + { + CC_STATUS_INIT; + cc_status.value1 = SET_SRC (exp); + return; + } + /* Certain instructions effect the condition codes. */ + else if (GET_MODE (SET_SRC (exp)) == SImode + || GET_MODE (SET_SRC (exp)) == HImode + || GET_MODE (SET_SRC (exp)) == QImode) + switch (GET_CODE (SET_SRC (exp))) + { + case ASHIFTRT: case LSHIFTRT: + case ASHIFT: case LSHIFT: + /* Shifts on the 386 don't set the condition codes if the + shift count is zero. */ + if (GET_CODE (XEXP (SET_SRC (exp), 1)) != CONST_INT) + { + CC_STATUS_INIT; + break; + } + /* We assume that the CONST_INT is non-zero (this rtx would + have been deleted if it were zero. */ + + case PLUS: case MINUS: case NEG: + case AND: case IOR: case XOR: + cc_status.flags = CC_NO_OVERFLOW; + cc_status.value1 = SET_SRC (exp); + cc_status.value2 = SET_DEST (exp); + break; + + default: + CC_STATUS_INIT; + } + else + { + CC_STATUS_INIT; + } + } + else if (GET_CODE (exp) == PARALLEL + && GET_CODE (XVECEXP (exp, 0, 0)) == SET) + { + if (SET_DEST (XVECEXP (exp, 0, 0)) == pc_rtx) + return; + if (SET_DEST (XVECEXP (exp, 0, 0)) == cc0_rtx) + { + CC_STATUS_INIT; + if (stack_regs_mentioned_p (SET_SRC (XVECEXP (exp, 0, 0)))) + cc_status.flags |= CC_IN_80387; + else + cc_status.value1 = SET_SRC (XVECEXP (exp, 0, 0)); + return; + } + CC_STATUS_INIT; + } + else + { + CC_STATUS_INIT; + } +} + +/* Split one or more DImode RTL references into pairs of SImode + references. The RTL can be REG, offsettable MEM, integer constant, or + CONST_DOUBLE. "operands" is a pointer to an array of DImode RTL to + split and "num" is its length. lo_half and hi_half are output arrays + that parallel "operands". */ + +void +split_di (operands, num, lo_half, hi_half) + rtx operands[]; + int num; + rtx lo_half[], hi_half[]; +{ + while (num--) + { + if (GET_CODE (operands[num]) == REG) + { + lo_half[num] = gen_rtx (REG, SImode, REGNO (operands[num])); + hi_half[num] = gen_rtx (REG, SImode, REGNO (operands[num]) + 1); + } + else if (CONSTANT_P (operands[num])) + { + split_double (operands[num], &lo_half[num], &hi_half[num]); + } + else if (offsettable_memref_p (operands[num])) + { + lo_half[num] = operands[num]; + hi_half[num] = adj_offsettable_operand (operands[num], 4); + } + else + abort(); + } +} + +/* Return 1 if this is a valid binary operation on a 387. + OP is the expression matched, and MODE is its mode. */ + +int +binary_387_op (op, mode) + register rtx op; + enum machine_mode mode; +{ + if (mode != VOIDmode && mode != GET_MODE (op)) + return 0; + + switch (GET_CODE (op)) + { + case PLUS: + case MINUS: + case MULT: + case DIV: + return GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT; + + default: + return 0; + } +} + +/* Return 1 if this is a valid conversion operation on a 387. + OP is the expression matched, and MODE is its mode. */ + +int +convert_387_op (op, mode) + register rtx op; + enum machine_mode mode; +{ + if (mode != VOIDmode && mode != GET_MODE (op)) + return 0; + + switch (GET_CODE (op)) + { + case FLOAT: + return GET_MODE (XEXP (op, 0)) == SImode; + + case FLOAT_EXTEND: + return mode == DFmode && GET_MODE (XEXP (op, 0)) == SFmode; + + default: + return 0; + } +} + +/* Return 1 if this is a valid shift or rotate operation on a 386. + OP is the expression matched, and MODE is its mode. */ + +int +shift_op (op, mode) + register rtx op; + enum machine_mode mode; +{ + rtx operand = XEXP (op, 0); + + if (mode != VOIDmode && mode != GET_MODE (op)) + return 0; + + if (GET_MODE (operand) != GET_MODE (op) + || GET_MODE_CLASS (GET_MODE (op)) != MODE_INT) + return 0; + + return (GET_CODE (op) == ASHIFT + || GET_CODE (op) == ASHIFTRT + || GET_CODE (op) == LSHIFTRT + || GET_CODE (op) == ROTATE + || GET_CODE (op) == ROTATERT); +} + +/* Return 1 if OP is COMPARE rtx with mode VOIDmode. + MODE is not used. */ + +int +VOIDmode_compare_op (op, mode) + register rtx op; + enum machine_mode mode; +{ + return GET_CODE (op) == COMPARE && GET_MODE (op) == VOIDmode; +} + +/* Output code to perform a 387 binary operation in INSN, one of PLUS, + MINUS, MULT or DIV. OPERANDS are the insn operands, where operands[3] + is the expression of the binary operation. The output may either be + emitted here, or returned to the caller, like all output_* functions. + + There is no guarantee that the operands are the same mode, as they + might be within FLOAT or FLOAT_EXTEND expressions. */ + +char * +output_387_binary_op (insn, operands) + rtx insn; + rtx *operands; +{ + rtx temp; + char *base_op; + static char buf[100]; + + switch (GET_CODE (operands[3])) + { + case PLUS: + if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT + || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT) + base_op = "fiadd"; + else + base_op = "fadd"; + break; + + case MINUS: + if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT + || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT) + base_op = "fisub"; + else + base_op = "fsub"; + break; + + case MULT: + if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT + || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT) + base_op = "fimul"; + else + base_op = "fmul"; + break; + + case DIV: + if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT + || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT) + base_op = "fidiv"; + else + base_op = "fdiv"; + break; + + default: + abort (); + } + + strcpy (buf, base_op); + + switch (GET_CODE (operands[3])) + { + case MULT: + case PLUS: + if (REG_P (operands[2]) && REGNO (operands[0]) == REGNO (operands[2])) + { + temp = operands[2]; + operands[2] = operands[1]; + operands[1] = temp; + } + + if (GET_CODE (operands[2]) == MEM) + return strcat (buf, AS1 (%z2,%2)); + + if (NON_STACK_REG_P (operands[1])) + { + output_op_from_reg (operands[1], strcat (buf, AS1 (%z0,%1))); + RET; + } + else if (NON_STACK_REG_P (operands[2])) + { + output_op_from_reg (operands[2], strcat (buf, AS1 (%z0,%1))); + RET; + } + + if (find_regno_note (insn, REG_DEAD, REGNO (operands[2]))) + return strcat (buf, AS2 (p,%2,%0)); + + if (STACK_TOP_P (operands[0])) + return strcat (buf, AS2 (,%y2,%0)); + else + return strcat (buf, AS2 (,%2,%0)); + + case MINUS: + case DIV: + if (GET_CODE (operands[1]) == MEM) + return strcat (buf, AS1 (r%z1,%1)); + + if (GET_CODE (operands[2]) == MEM) + return strcat (buf, AS1 (%z2,%2)); + + if (NON_STACK_REG_P (operands[1])) + { + output_op_from_reg (operands[1], strcat (buf, AS1 (r%z0,%1))); + RET; + } + else if (NON_STACK_REG_P (operands[2])) + { + output_op_from_reg (operands[2], strcat (buf, AS1 (%z0,%1))); + RET; + } + + if (! STACK_REG_P (operands[1]) || ! STACK_REG_P (operands[2])) + abort (); + + if (find_regno_note (insn, REG_DEAD, REGNO (operands[2]))) + return strcat (buf, AS2 (rp,%2,%0)); + + if (find_regno_note (insn, REG_DEAD, REGNO (operands[1]))) + return strcat (buf, AS2 (p,%1,%0)); + + if (STACK_TOP_P (operands[0])) + { + if (STACK_TOP_P (operands[1])) + return strcat (buf, AS2 (,%y2,%0)); + else + return strcat (buf, AS2 (r,%y1,%0)); + } + else if (STACK_TOP_P (operands[1])) + return strcat (buf, AS2 (,%1,%0)); + else + return strcat (buf, AS2 (r,%2,%0)); + + default: + abort (); + } +} + +/* Output code for INSN to convert a float to a signed int. OPERANDS + are the insn operands. The output may be SFmode or DFmode and the + input operand may be SImode or DImode. As a special case, make sure + that the 387 stack top dies if the output mode is DImode, because the + hardware requires this. */ + +char * +output_fix_trunc (insn, operands) + rtx insn; + rtx *operands; +{ + int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0; + rtx xops[2]; + + if (! STACK_TOP_P (operands[1]) || + (GET_MODE (operands[0]) == DImode && ! stack_top_dies)) + abort (); + + xops[0] = GEN_INT (12); + xops[1] = operands[4]; + + output_asm_insn (AS1 (fnstc%W2,%2), operands); + output_asm_insn (AS2 (mov%L2,%2,%4), operands); + output_asm_insn (AS2 (mov%B1,%0,%h1), xops); + output_asm_insn (AS2 (mov%L4,%4,%3), operands); + output_asm_insn (AS1 (fldc%W3,%3), operands); + + if (NON_STACK_REG_P (operands[0])) + output_to_reg (operands[0], stack_top_dies); + else if (GET_CODE (operands[0]) == MEM) + { + if (stack_top_dies) + output_asm_insn (AS1 (fistp%z0,%0), operands); + else + output_asm_insn (AS1 (fist%z0,%0), operands); + } + else + abort (); + + return AS1 (fldc%W2,%2); +} + +/* Output code for INSN to compare OPERANDS. The two operands might + not have the same mode: one might be within a FLOAT or FLOAT_EXTEND + expression. If the compare is in mode CCFPEQmode, use an opcode that + will not fault if a qNaN is present. */ + +char * +output_float_compare (insn, operands) + rtx insn; + rtx *operands; +{ + int stack_top_dies; + rtx body = XVECEXP (PATTERN (insn), 0, 0); + int unordered_compare = GET_MODE (SET_SRC (body)) == CCFPEQmode; + + if (! STACK_TOP_P (operands[0])) + abort (); + + stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0; + + if (STACK_REG_P (operands[1]) + && stack_top_dies + && find_regno_note (insn, REG_DEAD, REGNO (operands[1])) + && REGNO (operands[1]) != FIRST_STACK_REG) + { + /* If both the top of the 387 stack dies, and the other operand + is also a stack register that dies, then this must be a + `fcompp' float compare */ + + if (unordered_compare) + output_asm_insn ("fucompp", operands); + else + output_asm_insn ("fcompp", operands); + } + else + { + static char buf[100]; + + /* Decide if this is the integer or float compare opcode, or the + unordered float compare. */ + + if (unordered_compare) + strcpy (buf, "fucom"); + else if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_FLOAT) + strcpy (buf, "fcom"); + else + strcpy (buf, "ficom"); + + /* Modify the opcode if the 387 stack is to be popped. */ + + if (stack_top_dies) + strcat (buf, "p"); + + if (NON_STACK_REG_P (operands[1])) + output_op_from_reg (operands[1], strcat (buf, AS1 (%z0,%1))); + else + output_asm_insn (strcat (buf, AS1 (%z1,%y1)), operands); + } + + /* Now retrieve the condition code. */ + + return output_fp_cc0_set (insn); +} + +/* Output opcodes to transfer the results of FP compare or test INSN + from the FPU to the CPU flags. If TARGET_IEEE_FP, ensure that if the + result of the compare or test is unordered, no comparison operator + succeeds except NE. Return an output template, if any. */ + +char * +output_fp_cc0_set (insn) + rtx insn; +{ + rtx xops[3]; + rtx unordered_label; + rtx next; + enum rtx_code code; + + xops[0] = gen_rtx (REG, HImode, 0); + output_asm_insn (AS1 (fnsts%W0,%0), xops); + + if (! TARGET_IEEE_FP) + return "sahf"; + + next = next_cc0_user (insn); + if (next == NULL_RTX) + abort (); + + if (GET_CODE (next) == JUMP_INSN + && GET_CODE (PATTERN (next)) == SET + && SET_DEST (PATTERN (next)) == pc_rtx + && GET_CODE (SET_SRC (PATTERN (next))) == IF_THEN_ELSE) + { + code = GET_CODE (XEXP (SET_SRC (PATTERN (next)), 0)); + } + else if (GET_CODE (PATTERN (next)) == SET) + { + code = GET_CODE (SET_SRC (PATTERN (next))); + } + else + abort (); + + xops[0] = gen_rtx (REG, QImode, 0); + + switch (code) + { + case GT: + xops[1] = GEN_INT (0x45); + output_asm_insn (AS2 (and%B0,%1,%h0), xops); + /* je label */ + break; + + case LT: + xops[1] = GEN_INT (0x45); + xops[2] = GEN_INT (0x01); + output_asm_insn (AS2 (and%B0,%1,%h0), xops); + output_asm_insn (AS2 (cmp%B0,%2,%h0), xops); + /* je label */ + break; + + case GE: + xops[1] = GEN_INT (0x05); + output_asm_insn (AS2 (and%B0,%1,%h0), xops); + /* je label */ + break; + + case LE: + xops[1] = GEN_INT (0x45); + xops[2] = GEN_INT (0x40); + output_asm_insn (AS2 (and%B0,%1,%h0), xops); + output_asm_insn (AS1 (dec%B0,%h0), xops); + output_asm_insn (AS2 (cmp%B0,%2,%h0), xops); + /* jb label */ + break; + + case EQ: + xops[1] = GEN_INT (0x45); + xops[2] = GEN_INT (0x40); + output_asm_insn (AS2 (and%B0,%1,%h0), xops); + output_asm_insn (AS2 (cmp%B0,%2,%h0), xops); + /* je label */ + break; + + case NE: + xops[1] = GEN_INT (0x44); + xops[2] = GEN_INT (0x40); + output_asm_insn (AS2 (and%B0,%1,%h0), xops); + output_asm_insn (AS2 (xor%B0,%2,%h0), xops); + /* jne label */ + break; + + case GTU: + case LTU: + case GEU: + case LEU: + default: + abort (); + } + RET; +} + +#define MAX_386_STACK_LOCALS 2 + +static rtx i386_stack_locals[(int) MAX_MACHINE_MODE][MAX_386_STACK_LOCALS]; + +/* Clear stack slot assignments remembered from previous functions. + This is called from INIT_EXPANDERS once before RTL is emitted for each + function. */ + +void +clear_386_stack_locals () +{ + enum machine_mode mode; + int n; + + for (mode = VOIDmode; (int) mode < (int) MAX_MACHINE_MODE; + mode = (enum machine_mode) ((int) mode + 1)) + for (n = 0; n < MAX_386_STACK_LOCALS; n++) + i386_stack_locals[(int) mode][n] = NULL_RTX; +} + +/* Return a MEM corresponding to a stack slot with mode MODE. + Allocate a new slot if necessary. + + The RTL for a function can have several slots available: N is + which slot to use. */ + +rtx +assign_386_stack_local (mode, n) + enum machine_mode mode; + int n; +{ + if (n < 0 || n >= MAX_386_STACK_LOCALS) + abort (); + + if (i386_stack_locals[(int) mode][n] == NULL_RTX) + i386_stack_locals[(int) mode][n] + = assign_stack_local (mode, GET_MODE_SIZE (mode), 0); + + return i386_stack_locals[(int) mode][n]; +} diff --git a/gnu/usr.bin/cc/common/basic-block.h b/gnu/usr.bin/cc/lib/basic-block.h similarity index 100% rename from gnu/usr.bin/cc/common/basic-block.h rename to gnu/usr.bin/cc/lib/basic-block.h diff --git a/gnu/usr.bin/cc/lib/c-common.c b/gnu/usr.bin/cc/lib/c-common.c new file mode 100644 index 0000000000..7e0691e007 --- /dev/null +++ b/gnu/usr.bin/cc/lib/c-common.c @@ -0,0 +1,1222 @@ +/* Subroutines shared by all languages that are variants of C. + Copyright (C) 1992 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "config.h" +#include "tree.h" +#include "c-lex.h" +#include "c-tree.h" +#include "flags.h" +#include "obstack.h" +#include + +extern struct obstack permanent_obstack; + +/* Make bindings for __FUNCTION__ and __PRETTY_FUNCTION__. */ + +void +declare_function_name () +{ + tree decl, type, init; + char *name, *printable_name; + int len; + + if (current_function_decl == NULL) + { + name = ""; + printable_name = "top level"; + } + else + { + char *kind = "function"; + if (TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE) + kind = "method"; + /* Allow functions to be nameless (such as artificial ones). */ + if (DECL_NAME (current_function_decl)) + name = IDENTIFIER_POINTER (DECL_NAME (current_function_decl)); + else + name = ""; + printable_name = (*decl_printable_name) (current_function_decl, &kind); + } + + /* If the default size of char arrays isn't big enough for the name, + make a bigger one. */ + len = strlen (name) + 1; + type = char_array_type_node; + if (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TREE_TYPE (char_array_type_node))) + < len) + type = build_array_type (char_type_node, + build_index_type (build_int_2 (len, 0))); + + push_obstacks_nochange (); + decl = build_decl (VAR_DECL, get_identifier ("__FUNCTION__"), type); + TREE_STATIC (decl) = 1; + TREE_READONLY (decl) = 1; + DECL_SOURCE_LINE (decl) = 0; + DECL_IN_SYSTEM_HEADER (decl) = 1; + DECL_IGNORED_P (decl) = 1; + init = build_string (len, name); + TREE_TYPE (init) = type; + DECL_INITIAL (decl) = init; + finish_decl (pushdecl (decl), init, NULL_TREE); + + len = strlen (printable_name) + 1; + type = char_array_type_node; + if (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TREE_TYPE (char_array_type_node))) + < len) + type = build_array_type (char_type_node, + build_index_type (build_int_2 (len, 0))); + + push_obstacks_nochange (); + decl = build_decl (VAR_DECL, get_identifier ("__PRETTY_FUNCTION__"), type); + TREE_STATIC (decl) = 1; + TREE_READONLY (decl) = 1; + DECL_SOURCE_LINE (decl) = 0; + DECL_IN_SYSTEM_HEADER (decl) = 1; + DECL_IGNORED_P (decl) = 1; + init = build_string (len, printable_name); + TREE_TYPE (init) = type; + DECL_INITIAL (decl) = init; + finish_decl (pushdecl (decl), init, NULL_TREE); +} + +/* Given a chain of STRING_CST nodes, + concatenate them into one STRING_CST + and give it a suitable array-of-chars data type. */ + +tree +combine_strings (strings) + tree strings; +{ + register tree value, t; + register int length = 1; + int wide_length = 0; + int wide_flag = 0; + int wchar_bytes = TYPE_PRECISION (wchar_type_node) / BITS_PER_UNIT; + int nchars; + + if (TREE_CHAIN (strings)) + { + /* More than one in the chain, so concatenate. */ + register char *p, *q; + + /* Don't include the \0 at the end of each substring, + except for the last one. + Count wide strings and ordinary strings separately. */ + for (t = strings; t; t = TREE_CHAIN (t)) + { + if (TREE_TYPE (t) == wchar_array_type_node) + { + wide_length += (TREE_STRING_LENGTH (t) - wchar_bytes); + wide_flag = 1; + } + else + length += (TREE_STRING_LENGTH (t) - 1); + } + + /* If anything is wide, the non-wides will be converted, + which makes them take more space. */ + if (wide_flag) + length = length * wchar_bytes + wide_length; + + p = savealloc (length); + + /* Copy the individual strings into the new combined string. + If the combined string is wide, convert the chars to ints + for any individual strings that are not wide. */ + + q = p; + for (t = strings; t; t = TREE_CHAIN (t)) + { + int len = (TREE_STRING_LENGTH (t) + - ((TREE_TYPE (t) == wchar_array_type_node) + ? wchar_bytes : 1)); + if ((TREE_TYPE (t) == wchar_array_type_node) == wide_flag) + { + bcopy (TREE_STRING_POINTER (t), q, len); + q += len; + } + else + { + int i; + for (i = 0; i < len; i++) + ((int *) q)[i] = TREE_STRING_POINTER (t)[i]; + q += len * wchar_bytes; + } + } + if (wide_flag) + { + int i; + for (i = 0; i < wchar_bytes; i++) + *q++ = 0; + } + else + *q = 0; + + value = make_node (STRING_CST); + TREE_STRING_POINTER (value) = p; + TREE_STRING_LENGTH (value) = length; + TREE_CONSTANT (value) = 1; + } + else + { + value = strings; + length = TREE_STRING_LENGTH (value); + if (TREE_TYPE (value) == wchar_array_type_node) + wide_flag = 1; + } + + /* Compute the number of elements, for the array type. */ + nchars = wide_flag ? length / wchar_bytes : length; + + /* Create the array type for the string constant. + -Wwrite-strings says make the string constant an array of const char + so that copying it to a non-const pointer will get a warning. */ + if (warn_write_strings + && (! flag_traditional && ! flag_writable_strings)) + { + tree elements + = build_type_variant (wide_flag ? wchar_type_node : char_type_node, + 1, 0); + TREE_TYPE (value) + = build_array_type (elements, + build_index_type (build_int_2 (nchars - 1, 0))); + } + else + TREE_TYPE (value) + = build_array_type (wide_flag ? wchar_type_node : char_type_node, + build_index_type (build_int_2 (nchars - 1, 0))); + TREE_CONSTANT (value) = 1; + TREE_STATIC (value) = 1; + return value; +} + +/* Process the attributes listed in ATTRIBUTES + and install them in DECL. */ + +void +decl_attributes (decl, attributes) + tree decl, attributes; +{ + tree a; + for (a = attributes; a; a = TREE_CHAIN (a)) + if (TREE_VALUE (a) == get_identifier ("packed")) + { + if (TREE_CODE (decl) == FIELD_DECL) + DECL_PACKED (decl) = 1; + /* We can't set DECL_PACKED for a VAR_DECL, because the bit is + used for DECL_REGISTER. It wouldn't mean anything anyway. */ + } + else if (TREE_VALUE (a) != 0 + && TREE_CODE (TREE_VALUE (a)) == TREE_LIST + && TREE_PURPOSE (TREE_VALUE (a)) == get_identifier ("mode")) + { + int i; + char *specified_name + = IDENTIFIER_POINTER (TREE_VALUE (TREE_VALUE (a))); + + /* Give this decl a type with the specified mode. */ + for (i = 0; i < NUM_MACHINE_MODES; i++) + if (!strcmp (specified_name, GET_MODE_NAME (i))) + { + tree type + = type_for_mode (i, TREE_UNSIGNED (TREE_TYPE (decl))); + if (type != 0) + { + TREE_TYPE (decl) = type; + DECL_SIZE (decl) = 0; + layout_decl (decl, 0); + } + else + error ("no data type for mode `%s'", specified_name); + break; + } + if (i == NUM_MACHINE_MODES) + error ("unknown machine mode `%s'", specified_name); + } + else if (TREE_VALUE (a) != 0 + && TREE_CODE (TREE_VALUE (a)) == TREE_LIST + && TREE_PURPOSE (TREE_VALUE (a)) == get_identifier ("aligned")) + { + int align = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (a))) + * BITS_PER_UNIT; + + if (exact_log2 (align) == -1) + error_with_decl (decl, + "requested alignment of `%s' is not a power of 2"); + else if (TREE_CODE (decl) != VAR_DECL + && TREE_CODE (decl) != FIELD_DECL) + error_with_decl (decl, + "alignment specified for `%s'"); + else + DECL_ALIGN (decl) = align; + } + else if (TREE_VALUE (a) != 0 + && TREE_CODE (TREE_VALUE (a)) == TREE_LIST + && TREE_PURPOSE (TREE_VALUE (a)) == get_identifier ("format")) + { + tree list = TREE_VALUE (TREE_VALUE (a)); + tree format_type = TREE_PURPOSE (list); + int format_num = TREE_INT_CST_LOW (TREE_PURPOSE (TREE_VALUE (list))); + int first_arg_num = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (list))); + int is_scan; + tree argument; + int arg_num; + + if (TREE_CODE (decl) != FUNCTION_DECL) + { + error_with_decl (decl, + "argument format specified for non-function `%s'"); + return; + } + + if (format_type == get_identifier ("printf")) + is_scan = 0; + else if (format_type == get_identifier ("scanf")) + is_scan = 1; + else + { + error_with_decl (decl, "unrecognized format specifier for `%s'"); + return; + } + + if (first_arg_num != 0 && first_arg_num <= format_num) + { + error_with_decl (decl, + "format string arg follows the args to be formatted, for `%s'"); + return; + } + + /* Verify that the format_num argument is actually a string, in case + the format attribute is in error. */ + argument = TYPE_ARG_TYPES (TREE_TYPE (decl)); + for (arg_num = 1; ; ++arg_num) + { + if (argument == 0 || arg_num == format_num) + break; + argument = TREE_CHAIN (argument); + } + if (! argument + || TREE_CODE (TREE_VALUE (argument)) != POINTER_TYPE + || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (argument))) + != char_type_node)) + { + error_with_decl (decl, + "format string arg not a string type, for `%s'"); + return; + } + if (first_arg_num != 0) + { + /* Verify that first_arg_num points to the last arg, the ... */ + while (argument) + arg_num++, argument = TREE_CHAIN (argument); + if (arg_num != first_arg_num) + { + error_with_decl (decl, + "args to be formatted is not ..., for `%s'"); + return; + } + } + + record_format_info (DECL_NAME (decl), is_scan, format_num, + first_arg_num); + } +} + +/* Print a warning if a constant expression had overflow in folding. + Invoke this function on every expression that the language + requires to be a constant expression. + Note the ANSI C standard says it is erroneous for a + constant expression to overflow. */ + +void +constant_expression_warning (value) + tree value; +{ + if (TREE_CODE (value) == INTEGER_CST && TREE_CONSTANT_OVERFLOW (value)) + { + /* ??? This is a warning, not a pedwarn, in 2.4, + because it happens in contexts that are not + "constant expressions" in ANSI C. + Fix the problem differently in 2.5. */ + warning ("overflow in constant expression"); + /* Suppress duplicate warnings. */ + TREE_CONSTANT_OVERFLOW (value) = 0; + } +} + +/* Print a warning if an expression had overflow in folding. + Invoke this function on every expression that + (1) appears in the source code, and + (2) might be a constant expression that overflowed, and + (3) is not already checked by convert_and_check; + however, do not invoke this function on operands of explicit casts. */ + +void +overflow_warning (value) + tree value; +{ + if (TREE_CODE (value) == INTEGER_CST && TREE_CONSTANT_OVERFLOW (value)) + { + /* ??? This is a warning, not a pedwarn, in 2.4, + because it happens in contexts that are not + "constant expressions" in ANSI C. + Fix the problem differently in 2.5. */ + warning ("integer overflow in expression"); + TREE_CONSTANT_OVERFLOW (value) = 0; + } +} + +/* Print a warning if a large constant is truncated to unsigned, + or if -Wconversion is used and a constant < 0 is converted to unsigned. + Invoke this function on every expression that might be implicitly + converted to an unsigned type. */ + +void +unsigned_conversion_warning (result, operand) + tree result, operand; +{ + if (TREE_CODE (operand) == INTEGER_CST + && TREE_CODE (TREE_TYPE (result)) == INTEGER_TYPE + && TREE_UNSIGNED (TREE_TYPE (result)) + && !int_fits_type_p (operand, TREE_TYPE (result))) + { + if (!int_fits_type_p (operand, signed_type (TREE_TYPE (result)))) + /* This detects cases like converting -129 or 256 to unsigned char. */ + pedwarn ("large integer implicitly truncated to unsigned type"); + else if (warn_conversion) + pedwarn ("negative integer implicitly converted to unsigned type"); + } +} + +/* Convert EXPR to TYPE, warning about conversion problems with constants. + Invoke this function on every expression that is converted implicitly, + i.e. because of language rules and not because of an explicit cast. */ + +tree +convert_and_check (type, expr) + tree type, expr; +{ + tree t = convert (type, expr); + if (TREE_CODE (t) == INTEGER_CST) + { + if (TREE_UNSIGNED (TREE_TYPE (expr)) + && !TREE_UNSIGNED (type) + && TREE_CODE (TREE_TYPE (expr)) == INTEGER_TYPE + && TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (expr))) + /* No warning for converting 0x80000000 to int. */ + TREE_CONSTANT_OVERFLOW (t) = 0; + else if (TREE_CONSTANT_OVERFLOW (t)) + { + /* ??? This is a warning, not a pedwarn, in 2.4, + because it happens in contexts that are not + "constant expressions" in ANSI C. + Fix the problem differently in 2.5. */ + warning ("overflow in implicit constant conversion"); + TREE_CONSTANT_OVERFLOW (t) = 0; + } + else + unsigned_conversion_warning (t, expr); + } + return t; +} + +void +c_expand_expr_stmt (expr) + tree expr; +{ + /* Do default conversion if safe and possibly important, + in case within ({...}). */ + if ((TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE && lvalue_p (expr)) + || TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE) + expr = default_conversion (expr); + + if (TREE_TYPE (expr) != error_mark_node + && TYPE_SIZE (TREE_TYPE (expr)) == 0 + && TREE_CODE (TREE_TYPE (expr)) != ARRAY_TYPE) + error ("expression statement has incomplete type"); + + expand_expr_stmt (expr); +} + +/* Validate the expression after `case' and apply default promotions. */ + +tree +check_case_value (value) + tree value; +{ + if (value == NULL_TREE) + return value; + + /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */ + STRIP_TYPE_NOPS (value); + + if (TREE_CODE (value) != INTEGER_CST + && value != error_mark_node) + { + error ("case label does not reduce to an integer constant"); + value = error_mark_node; + } + else + /* Promote char or short to int. */ + value = default_conversion (value); + + constant_expression_warning (value); + + return value; +} + +/* Return an integer type with BITS bits of precision, + that is unsigned if UNSIGNEDP is nonzero, otherwise signed. */ + +tree +type_for_size (bits, unsignedp) + unsigned bits; + int unsignedp; +{ + if (bits == TYPE_PRECISION (signed_char_type_node)) + return unsignedp ? unsigned_char_type_node : signed_char_type_node; + + if (bits == TYPE_PRECISION (short_integer_type_node)) + return unsignedp ? short_unsigned_type_node : short_integer_type_node; + + if (bits == TYPE_PRECISION (integer_type_node)) + return unsignedp ? unsigned_type_node : integer_type_node; + + if (bits == TYPE_PRECISION (long_integer_type_node)) + return unsignedp ? long_unsigned_type_node : long_integer_type_node; + + if (bits == TYPE_PRECISION (long_long_integer_type_node)) + return (unsignedp ? long_long_unsigned_type_node + : long_long_integer_type_node); + + if (bits <= TYPE_PRECISION (intQI_type_node)) + return unsignedp ? unsigned_intQI_type_node : intQI_type_node; + + if (bits <= TYPE_PRECISION (intHI_type_node)) + return unsignedp ? unsigned_intHI_type_node : intHI_type_node; + + if (bits <= TYPE_PRECISION (intSI_type_node)) + return unsignedp ? unsigned_intSI_type_node : intSI_type_node; + + if (bits <= TYPE_PRECISION (intDI_type_node)) + return unsignedp ? unsigned_intDI_type_node : intDI_type_node; + + return 0; +} + +/* Return a data type that has machine mode MODE. + If the mode is an integer, + then UNSIGNEDP selects between signed and unsigned types. */ + +tree +type_for_mode (mode, unsignedp) + enum machine_mode mode; + int unsignedp; +{ + if (mode == TYPE_MODE (signed_char_type_node)) + return unsignedp ? unsigned_char_type_node : signed_char_type_node; + + if (mode == TYPE_MODE (short_integer_type_node)) + return unsignedp ? short_unsigned_type_node : short_integer_type_node; + + if (mode == TYPE_MODE (integer_type_node)) + return unsignedp ? unsigned_type_node : integer_type_node; + + if (mode == TYPE_MODE (long_integer_type_node)) + return unsignedp ? long_unsigned_type_node : long_integer_type_node; + + if (mode == TYPE_MODE (long_long_integer_type_node)) + return unsignedp ? long_long_unsigned_type_node : long_long_integer_type_node; + + if (mode == TYPE_MODE (intQI_type_node)) + return unsignedp ? unsigned_intQI_type_node : intQI_type_node; + + if (mode == TYPE_MODE (intHI_type_node)) + return unsignedp ? unsigned_intHI_type_node : intHI_type_node; + + if (mode == TYPE_MODE (intSI_type_node)) + return unsignedp ? unsigned_intSI_type_node : intSI_type_node; + + if (mode == TYPE_MODE (intDI_type_node)) + return unsignedp ? unsigned_intDI_type_node : intDI_type_node; + + if (mode == TYPE_MODE (float_type_node)) + return float_type_node; + + if (mode == TYPE_MODE (double_type_node)) + return double_type_node; + + if (mode == TYPE_MODE (long_double_type_node)) + return long_double_type_node; + + if (mode == TYPE_MODE (build_pointer_type (char_type_node))) + return build_pointer_type (char_type_node); + + if (mode == TYPE_MODE (build_pointer_type (integer_type_node))) + return build_pointer_type (integer_type_node); + + return 0; +} + +/* Print an error message for invalid operands to arith operation CODE. + NOP_EXPR is used as a special case (see truthvalue_conversion). */ + +void +binary_op_error (code) + enum tree_code code; +{ + register char *opname; + switch (code) + { + case NOP_EXPR: + error ("invalid truth-value expression"); + return; + + case PLUS_EXPR: + opname = "+"; break; + case MINUS_EXPR: + opname = "-"; break; + case MULT_EXPR: + opname = "*"; break; + case MAX_EXPR: + opname = "max"; break; + case MIN_EXPR: + opname = "min"; break; + case EQ_EXPR: + opname = "=="; break; + case NE_EXPR: + opname = "!="; break; + case LE_EXPR: + opname = "<="; break; + case GE_EXPR: + opname = ">="; break; + case LT_EXPR: + opname = "<"; break; + case GT_EXPR: + opname = ">"; break; + case LSHIFT_EXPR: + opname = "<<"; break; + case RSHIFT_EXPR: + opname = ">>"; break; + case TRUNC_MOD_EXPR: + case FLOOR_MOD_EXPR: + opname = "%"; break; + case TRUNC_DIV_EXPR: + case FLOOR_DIV_EXPR: + opname = "/"; break; + case BIT_AND_EXPR: + opname = "&"; break; + case BIT_IOR_EXPR: + opname = "|"; break; + case TRUTH_ANDIF_EXPR: + opname = "&&"; break; + case TRUTH_ORIF_EXPR: + opname = "||"; break; + case BIT_XOR_EXPR: + opname = "^"; break; + case LROTATE_EXPR: + case RROTATE_EXPR: + opname = "rotate"; break; + } + error ("invalid operands to binary %s", opname); +} + +/* Subroutine of build_binary_op, used for comparison operations. + See if the operands have both been converted from subword integer types + and, if so, perhaps change them both back to their original type. + + The arguments of this function are all pointers to local variables + of build_binary_op: OP0_PTR is &OP0, OP1_PTR is &OP1, + RESTYPE_PTR is &RESULT_TYPE and RESCODE_PTR is &RESULTCODE. + + If this function returns nonzero, it means that the comparison has + a constant value. What this function returns is an expression for + that value. */ + +tree +shorten_compare (op0_ptr, op1_ptr, restype_ptr, rescode_ptr) + tree *op0_ptr, *op1_ptr; + tree *restype_ptr; + enum tree_code *rescode_ptr; +{ + register tree type; + tree op0 = *op0_ptr; + tree op1 = *op1_ptr; + int unsignedp0, unsignedp1; + int real1, real2; + tree primop0, primop1; + enum tree_code code = *rescode_ptr; + + /* Throw away any conversions to wider types + already present in the operands. */ + + primop0 = get_narrower (op0, &unsignedp0); + primop1 = get_narrower (op1, &unsignedp1); + + /* Handle the case that OP0 does not *contain* a conversion + but it *requires* conversion to FINAL_TYPE. */ + + if (op0 == primop0 && TREE_TYPE (op0) != *restype_ptr) + unsignedp0 = TREE_UNSIGNED (TREE_TYPE (op0)); + if (op1 == primop1 && TREE_TYPE (op1) != *restype_ptr) + unsignedp1 = TREE_UNSIGNED (TREE_TYPE (op1)); + + /* If one of the operands must be floated, we cannot optimize. */ + real1 = TREE_CODE (TREE_TYPE (primop0)) == REAL_TYPE; + real2 = TREE_CODE (TREE_TYPE (primop1)) == REAL_TYPE; + + /* If first arg is constant, swap the args (changing operation + so value is preserved), for canonicalization. */ + + if (TREE_CONSTANT (primop0)) + { + register tree tem = primop0; + register int temi = unsignedp0; + primop0 = primop1; + primop1 = tem; + tem = op0; + op0 = op1; + op1 = tem; + *op0_ptr = op0; + *op1_ptr = op1; + unsignedp0 = unsignedp1; + unsignedp1 = temi; + temi = real1; + real1 = real2; + real2 = temi; + + switch (code) + { + case LT_EXPR: + code = GT_EXPR; + break; + case GT_EXPR: + code = LT_EXPR; + break; + case LE_EXPR: + code = GE_EXPR; + break; + case GE_EXPR: + code = LE_EXPR; + break; + } + *rescode_ptr = code; + } + + /* If comparing an integer against a constant more bits wide, + maybe we can deduce a value of 1 or 0 independent of the data. + Or else truncate the constant now + rather than extend the variable at run time. + + This is only interesting if the constant is the wider arg. + Also, it is not safe if the constant is unsigned and the + variable arg is signed, since in this case the variable + would be sign-extended and then regarded as unsigned. + Our technique fails in this case because the lowest/highest + possible unsigned results don't follow naturally from the + lowest/highest possible values of the variable operand. + For just EQ_EXPR and NE_EXPR there is another technique that + could be used: see if the constant can be faithfully represented + in the other operand's type, by truncating it and reextending it + and see if that preserves the constant's value. */ + + if (!real1 && !real2 + && TREE_CODE (primop1) == INTEGER_CST + && TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr)) + { + int min_gt, max_gt, min_lt, max_lt; + tree maxval, minval; + /* 1 if comparison is nominally unsigned. */ + int unsignedp = TREE_UNSIGNED (*restype_ptr); + tree val; + + type = signed_or_unsigned_type (unsignedp0, TREE_TYPE (primop0)); + + maxval = TYPE_MAX_VALUE (type); + minval = TYPE_MIN_VALUE (type); + + if (unsignedp && !unsignedp0) + *restype_ptr = signed_type (*restype_ptr); + + if (TREE_TYPE (primop1) != *restype_ptr) + primop1 = convert (*restype_ptr, primop1); + if (type != *restype_ptr) + { + minval = convert (*restype_ptr, minval); + maxval = convert (*restype_ptr, maxval); + } + + if (unsignedp && unsignedp0) + { + min_gt = INT_CST_LT_UNSIGNED (primop1, minval); + max_gt = INT_CST_LT_UNSIGNED (primop1, maxval); + min_lt = INT_CST_LT_UNSIGNED (minval, primop1); + max_lt = INT_CST_LT_UNSIGNED (maxval, primop1); + } + else + { + min_gt = INT_CST_LT (primop1, minval); + max_gt = INT_CST_LT (primop1, maxval); + min_lt = INT_CST_LT (minval, primop1); + max_lt = INT_CST_LT (maxval, primop1); + } + + val = 0; + /* This used to be a switch, but Genix compiler can't handle that. */ + if (code == NE_EXPR) + { + if (max_lt || min_gt) + val = integer_one_node; + } + else if (code == EQ_EXPR) + { + if (max_lt || min_gt) + val = integer_zero_node; + } + else if (code == LT_EXPR) + { + if (max_lt) + val = integer_one_node; + if (!min_lt) + val = integer_zero_node; + } + else if (code == GT_EXPR) + { + if (min_gt) + val = integer_one_node; + if (!max_gt) + val = integer_zero_node; + } + else if (code == LE_EXPR) + { + if (!max_gt) + val = integer_one_node; + if (min_gt) + val = integer_zero_node; + } + else if (code == GE_EXPR) + { + if (!min_lt) + val = integer_one_node; + if (max_lt) + val = integer_zero_node; + } + + /* If primop0 was sign-extended and unsigned comparison specd, + we did a signed comparison above using the signed type bounds. + But the comparison we output must be unsigned. + + Also, for inequalities, VAL is no good; but if the signed + comparison had *any* fixed result, it follows that the + unsigned comparison just tests the sign in reverse + (positive values are LE, negative ones GE). + So we can generate an unsigned comparison + against an extreme value of the signed type. */ + + if (unsignedp && !unsignedp0) + { + if (val != 0) + switch (code) + { + case LT_EXPR: + case GE_EXPR: + primop1 = TYPE_MIN_VALUE (type); + val = 0; + break; + + case LE_EXPR: + case GT_EXPR: + primop1 = TYPE_MAX_VALUE (type); + val = 0; + break; + } + type = unsigned_type (type); + } + + if (!max_gt && !unsignedp0) + { + /* This is the case of (char)x >?< 0x80, which people used to use + expecting old C compilers to change the 0x80 into -0x80. */ + if (val == integer_zero_node) + warning ("comparison is always 0 due to limited range of data type"); + if (val == integer_one_node) + warning ("comparison is always 1 due to limited range of data type"); + } + + if (!min_lt && unsignedp0) + { + /* This is the case of (unsigned char)x >?< -1 or < 0. */ + if (val == integer_zero_node) + warning ("comparison is always 0 due to limited range of data type"); + if (val == integer_one_node) + warning ("comparison is always 1 due to limited range of data type"); + } + + if (val != 0) + { + /* Don't forget to evaluate PRIMOP0 if it has side effects. */ + if (TREE_SIDE_EFFECTS (primop0)) + return build (COMPOUND_EXPR, TREE_TYPE (val), primop0, val); + return val; + } + + /* Value is not predetermined, but do the comparison + in the type of the operand that is not constant. + TYPE is already properly set. */ + } + else if (real1 && real2 + && (TYPE_PRECISION (TREE_TYPE (primop0)) + == TYPE_PRECISION (TREE_TYPE (primop1)))) + type = TREE_TYPE (primop0); + + /* If args' natural types are both narrower than nominal type + and both extend in the same manner, compare them + in the type of the wider arg. + Otherwise must actually extend both to the nominal + common type lest different ways of extending + alter the result. + (eg, (short)-1 == (unsigned short)-1 should be 0.) */ + + else if (unsignedp0 == unsignedp1 && real1 == real2 + && TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr) + && TYPE_PRECISION (TREE_TYPE (primop1)) < TYPE_PRECISION (*restype_ptr)) + { + type = common_type (TREE_TYPE (primop0), TREE_TYPE (primop1)); + type = signed_or_unsigned_type (unsignedp0 + || TREE_UNSIGNED (*restype_ptr), + type); + /* Make sure shorter operand is extended the right way + to match the longer operand. */ + primop0 = convert (signed_or_unsigned_type (unsignedp0, TREE_TYPE (primop0)), + primop0); + primop1 = convert (signed_or_unsigned_type (unsignedp1, TREE_TYPE (primop1)), + primop1); + } + else + { + /* Here we must do the comparison on the nominal type + using the args exactly as we received them. */ + type = *restype_ptr; + primop0 = op0; + primop1 = op1; + + if (!real1 && !real2 && integer_zerop (primop1) + && TREE_UNSIGNED (TREE_TYPE (primop0))) + { + tree value = 0; + switch (code) + { + case GE_EXPR: + if (extra_warnings) + warning ("unsigned value >= 0 is always 1"); + value = integer_one_node; + break; + + case LT_EXPR: + if (extra_warnings) + warning ("unsigned value < 0 is always 0"); + value = integer_zero_node; + } + + if (value != 0) + { + /* Don't forget to evaluate PRIMOP0 if it has side effects. */ + if (TREE_SIDE_EFFECTS (primop0)) + return build (COMPOUND_EXPR, TREE_TYPE (value), + primop0, value); + return value; + } + } + } + + *op0_ptr = convert (type, primop0); + *op1_ptr = convert (type, primop1); + + *restype_ptr = integer_type_node; + + return 0; +} + +/* Prepare expr to be an argument of a TRUTH_NOT_EXPR, + or validate its data type for an `if' or `while' statement or ?..: exp. + + This preparation consists of taking the ordinary + representation of an expression expr and producing a valid tree + boolean expression describing whether expr is nonzero. We could + simply always do build_binary_op (NE_EXPR, expr, integer_zero_node, 1), + but we optimize comparisons, &&, ||, and !. + + The resulting type should always be `integer_type_node'. */ + +tree +truthvalue_conversion (expr) + tree expr; +{ + register enum tree_code code; + + if (TREE_CODE (expr) == ERROR_MARK) + return expr; + +#if 0 /* This appears to be wrong for C++. */ + /* These really should return error_mark_node after 2.4 is stable. + But not all callers handle ERROR_MARK properly. */ + switch (TREE_CODE (TREE_TYPE (expr))) + { + case RECORD_TYPE: + error ("struct type value used where scalar is required"); + return integer_zero_node; + + case UNION_TYPE: + error ("union type value used where scalar is required"); + return integer_zero_node; + + case ARRAY_TYPE: + error ("array type value used where scalar is required"); + return integer_zero_node; + + default: + break; + } +#endif /* 0 */ + + switch (TREE_CODE (expr)) + { + /* It is simpler and generates better code to have only TRUTH_*_EXPR + or comparison expressions as truth values at this level. */ +#if 0 + case COMPONENT_REF: + /* A one-bit unsigned bit-field is already acceptable. */ + if (1 == TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (expr, 1))) + && TREE_UNSIGNED (TREE_OPERAND (expr, 1))) + return expr; + break; +#endif + + case EQ_EXPR: + /* It is simpler and generates better code to have only TRUTH_*_EXPR + or comparison expressions as truth values at this level. */ +#if 0 + if (integer_zerop (TREE_OPERAND (expr, 1))) + return build_unary_op (TRUTH_NOT_EXPR, TREE_OPERAND (expr, 0), 0); +#endif + case NE_EXPR: case LE_EXPR: case GE_EXPR: case LT_EXPR: case GT_EXPR: + case TRUTH_ANDIF_EXPR: + case TRUTH_ORIF_EXPR: + case TRUTH_AND_EXPR: + case TRUTH_OR_EXPR: + case TRUTH_XOR_EXPR: + case ERROR_MARK: + return expr; + + case INTEGER_CST: + return integer_zerop (expr) ? integer_zero_node : integer_one_node; + + case REAL_CST: + return real_zerop (expr) ? integer_zero_node : integer_one_node; + + case ADDR_EXPR: + if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 0))) + return build (COMPOUND_EXPR, integer_type_node, + TREE_OPERAND (expr, 0), integer_one_node); + else + return integer_one_node; + + case COMPLEX_EXPR: + return build_binary_op ((TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1)) + ? TRUTH_AND_EXPR : TRUTH_ANDIF_EXPR), + truthvalue_conversion (TREE_OPERAND (expr, 0)), + truthvalue_conversion (TREE_OPERAND (expr, 1)), + 0); + + case NEGATE_EXPR: + case ABS_EXPR: + case FLOAT_EXPR: + case FFS_EXPR: + /* These don't change whether an object is non-zero or zero. */ + return truthvalue_conversion (TREE_OPERAND (expr, 0)); + + case LROTATE_EXPR: + case RROTATE_EXPR: + /* These don't change whether an object is zero or non-zero, but + we can't ignore them if their second arg has side-effects. */ + if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1))) + return build (COMPOUND_EXPR, integer_type_node, TREE_OPERAND (expr, 1), + truthvalue_conversion (TREE_OPERAND (expr, 0))); + else + return truthvalue_conversion (TREE_OPERAND (expr, 0)); + + case COND_EXPR: + /* Distribute the conversion into the arms of a COND_EXPR. */ + return fold (build (COND_EXPR, integer_type_node, TREE_OPERAND (expr, 0), + truthvalue_conversion (TREE_OPERAND (expr, 1)), + truthvalue_conversion (TREE_OPERAND (expr, 2)))); + + case CONVERT_EXPR: + /* Don't cancel the effect of a CONVERT_EXPR from a REFERENCE_TYPE, + since that affects how `default_conversion' will behave. */ + if (TREE_CODE (TREE_TYPE (expr)) == REFERENCE_TYPE + || TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == REFERENCE_TYPE) + break; + /* fall through... */ + case NOP_EXPR: + /* If this is widening the argument, we can ignore it. */ + if (TYPE_PRECISION (TREE_TYPE (expr)) + >= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (expr, 0)))) + return truthvalue_conversion (TREE_OPERAND (expr, 0)); + break; + + case MINUS_EXPR: + /* With IEEE arithmetic, x - x may not equal 0, so we can't optimize + this case. */ + if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT + && TREE_CODE (TREE_TYPE (expr)) == REAL_TYPE) + break; + /* fall through... */ + case BIT_XOR_EXPR: + /* This and MINUS_EXPR can be changed into a comparison of the + two objects. */ + if (TREE_TYPE (TREE_OPERAND (expr, 0)) + == TREE_TYPE (TREE_OPERAND (expr, 1))) + return build_binary_op (NE_EXPR, TREE_OPERAND (expr, 0), + TREE_OPERAND (expr, 1), 1); + return build_binary_op (NE_EXPR, TREE_OPERAND (expr, 0), + fold (build1 (NOP_EXPR, + TREE_TYPE (TREE_OPERAND (expr, 0)), + TREE_OPERAND (expr, 1))), 1); + + case MODIFY_EXPR: + if (warn_parentheses && C_EXP_ORIGINAL_CODE (expr) == MODIFY_EXPR) + warning ("suggest parentheses around assignment used as truth value"); + break; + } + + if (TREE_CODE (TREE_TYPE (expr)) == COMPLEX_TYPE) + return (build_binary_op + ((TREE_SIDE_EFFECTS (expr) + ? TRUTH_AND_EXPR : TRUTH_ANDIF_EXPR), + truthvalue_conversion (build_unary_op (REALPART_EXPR, expr, 0)), + truthvalue_conversion (build_unary_op (IMAGPART_EXPR, expr, 0)), + 0)); + + return build_binary_op (NE_EXPR, expr, integer_zero_node, 1); +} + +/* Read the rest of a #-directive from input stream FINPUT. + In normal use, the directive name and the white space after it + have already been read, so they won't be included in the result. + We allow for the fact that the directive line may contain + a newline embedded within a character or string literal which forms + a part of the directive. + + The value is a string in a reusable buffer. It remains valid + only until the next time this function is called. */ + +char * +get_directive_line (finput) + register FILE *finput; +{ + static char *directive_buffer = NULL; + static unsigned buffer_length = 0; + register char *p; + register char *buffer_limit; + register int looking_for = 0; + register int char_escaped = 0; + + if (buffer_length == 0) + { + directive_buffer = (char *)xmalloc (128); + buffer_length = 128; + } + + buffer_limit = &directive_buffer[buffer_length]; + + for (p = directive_buffer; ; ) + { + int c; + + /* Make buffer bigger if it is full. */ + if (p >= buffer_limit) + { + register unsigned bytes_used = (p - directive_buffer); + + buffer_length *= 2; + directive_buffer + = (char *)xrealloc (directive_buffer, buffer_length); + p = &directive_buffer[bytes_used]; + buffer_limit = &directive_buffer[buffer_length]; + } + + c = getc (finput); + + /* Discard initial whitespace. */ + if ((c == ' ' || c == '\t') && p == directive_buffer) + continue; + + /* Detect the end of the directive. */ + if (c == '\n' && looking_for == 0) + { + ungetc (c, finput); + c = '\0'; + } + + *p++ = c; + + if (c == 0) + return directive_buffer; + + /* Handle string and character constant syntax. */ + if (looking_for) + { + if (looking_for == c && !char_escaped) + looking_for = 0; /* Found terminator... stop looking. */ + } + else + if (c == '\'' || c == '"') + looking_for = c; /* Don't stop buffering until we see another + another one of these (or an EOF). */ + + /* Handle backslash. */ + char_escaped = (c == '\\' && ! char_escaped); + } +} + +/* Make a variant type in the proper way for C/C++, propagating qualifiers + down to the element type of an array. */ + +tree +c_build_type_variant (type, constp, volatilep) + tree type; + int constp, volatilep; +{ + if (TREE_CODE (type) == ARRAY_TYPE) + { + tree real_main_variant = TYPE_MAIN_VARIANT (type); + int permanent = TREE_PERMANENT (type); + + if (permanent) + push_obstacks (&permanent_obstack, &permanent_obstack); + type = build_array_type (c_build_type_variant (TREE_TYPE (type), + constp, volatilep), + TYPE_DOMAIN (type)); + TYPE_MAIN_VARIANT (type) = real_main_variant; + if (permanent) + pop_obstacks (); + } + return build_type_variant (type, constp, volatilep); +} diff --git a/gnu/usr.bin/cc/common/c-lex.h b/gnu/usr.bin/cc/lib/c-lex.h similarity index 100% rename from gnu/usr.bin/cc/common/c-lex.h rename to gnu/usr.bin/cc/lib/c-lex.h diff --git a/gnu/usr.bin/cc/common/c-parse.h b/gnu/usr.bin/cc/lib/c-parse.h similarity index 100% rename from gnu/usr.bin/cc/common/c-parse.h rename to gnu/usr.bin/cc/lib/c-parse.h diff --git a/gnu/usr.bin/cc/common/c-tree.h b/gnu/usr.bin/cc/lib/c-tree.h similarity index 100% rename from gnu/usr.bin/cc/common/c-tree.h rename to gnu/usr.bin/cc/lib/c-tree.h diff --git a/gnu/usr.bin/cc/common/caller-save.c b/gnu/usr.bin/cc/lib/caller-save.c similarity index 100% rename from gnu/usr.bin/cc/common/caller-save.c rename to gnu/usr.bin/cc/lib/caller-save.c diff --git a/gnu/usr.bin/cc/lib/calls.c b/gnu/usr.bin/cc/lib/calls.c new file mode 100644 index 0000000000..dc867aec10 --- /dev/null +++ b/gnu/usr.bin/cc/lib/calls.c @@ -0,0 +1,2891 @@ +/* Convert function calls to rtl insns, for GNU C compiler. + Copyright (C) 1989, 1992 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "config.h" +#include "rtl.h" +#include "tree.h" +#include "flags.h" +#include "expr.h" +#include "gvarargs.h" +#include "insn-flags.h" + +/* Decide whether a function's arguments should be processed + from first to last or from last to first. + + They should if the stack and args grow in opposite directions, but + only if we have push insns. */ + +#ifdef PUSH_ROUNDING + +#if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNARD) +#define PUSH_ARGS_REVERSED /* If it's last to first */ +#endif + +#endif + +/* Like STACK_BOUNDARY but in units of bytes, not bits. */ +#define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT) + +/* Data structure and subroutines used within expand_call. */ + +struct arg_data +{ + /* Tree node for this argument. */ + tree tree_value; + /* Mode for value; TYPE_MODE unless promoted. */ + enum machine_mode mode; + /* Current RTL value for argument, or 0 if it isn't precomputed. */ + rtx value; + /* Initially-compute RTL value for argument; only for const functions. */ + rtx initial_value; + /* Register to pass this argument in, 0 if passed on stack, or an + EXPR_LIST if the arg is to be copied into multiple different + registers. */ + rtx reg; + /* If REG was promoted from the actual mode of the argument expression, + indicates whether the promotion is sign- or zero-extended. */ + int unsignedp; + /* Number of registers to use. 0 means put the whole arg in registers. + Also 0 if not passed in registers. */ + int partial; + /* Non-zero if argument must be passed on stack. + Note that some arguments may be passed on the stack + even though pass_on_stack is zero, just because FUNCTION_ARG says so. + pass_on_stack identifies arguments that *cannot* go in registers. */ + int pass_on_stack; + /* Offset of this argument from beginning of stack-args. */ + struct args_size offset; + /* Similar, but offset to the start of the stack slot. Different from + OFFSET if this arg pads downward. */ + struct args_size slot_offset; + /* Size of this argument on the stack, rounded up for any padding it gets, + parts of the argument passed in registers do not count. + If REG_PARM_STACK_SPACE is defined, then register parms + are counted here as well. */ + struct args_size size; + /* Location on the stack at which parameter should be stored. The store + has already been done if STACK == VALUE. */ + rtx stack; + /* Location on the stack of the start of this argument slot. This can + differ from STACK if this arg pads downward. This location is known + to be aligned to FUNCTION_ARG_BOUNDARY. */ + rtx stack_slot; +#ifdef ACCUMULATE_OUTGOING_ARGS + /* Place that this stack area has been saved, if needed. */ + rtx save_area; +#endif +#ifdef STRICT_ALIGNMENT + /* If an argument's alignment does not permit direct copying into registers, + copy in smaller-sized pieces into pseudos. These are stored in a + block pointed to by this field. The next field says how many + word-sized pseudos we made. */ + rtx *aligned_regs; + int n_aligned_regs; +#endif +}; + +#ifdef ACCUMULATE_OUTGOING_ARGS +/* A vector of one char per byte of stack space. A byte if non-zero if + the corresponding stack location has been used. + This vector is used to prevent a function call within an argument from + clobbering any stack already set up. */ +static char *stack_usage_map; + +/* Size of STACK_USAGE_MAP. */ +static int highest_outgoing_arg_in_use; + +/* stack_arg_under_construction is nonzero when an argument may be + initialized with a constructor call (including a C function that + returns a BLKmode struct) and expand_call must take special action + to make sure the object being constructed does not overlap the + argument list for the constructor call. */ +int stack_arg_under_construction; +#endif + +static int calls_function PROTO((tree, int)); +static void emit_call_1 PROTO((rtx, tree, int, int, rtx, rtx, int, + rtx, int)); +static void store_one_arg PROTO ((struct arg_data *, rtx, int, int, + tree, int)); + +/* If WHICH is 1, return 1 if EXP contains a call to the built-in function + `alloca'. + + If WHICH is 0, return 1 if EXP contains a call to any function. + Actually, we only need return 1 if evaluating EXP would require pushing + arguments on the stack, but that is too difficult to compute, so we just + assume any function call might require the stack. */ + +static int +calls_function (exp, which) + tree exp; + int which; +{ + register int i; + int type = TREE_CODE_CLASS (TREE_CODE (exp)); + int length = tree_code_length[(int) TREE_CODE (exp)]; + + /* Only expressions and references can contain calls. */ + + if (type != 'e' && type != '<' && type != '1' && type != '2' && type != 'r' + && type != 'b') + return 0; + + switch (TREE_CODE (exp)) + { + case CALL_EXPR: + if (which == 0) + return 1; + else if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR + && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) + == FUNCTION_DECL) + && DECL_BUILT_IN (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) + && (DECL_FUNCTION_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) + == BUILT_IN_ALLOCA)) + return 1; + + /* Third operand is RTL. */ + length = 2; + break; + + case SAVE_EXPR: + if (SAVE_EXPR_RTL (exp) != 0) + return 0; + break; + + case BLOCK: + { + register tree local; + + for (local = BLOCK_VARS (exp); local; local = TREE_CHAIN (local)) + if (DECL_INITIAL (local) != 0 + && calls_function (DECL_INITIAL (local), which)) + return 1; + } + { + register tree subblock; + + for (subblock = BLOCK_SUBBLOCKS (exp); + subblock; + subblock = TREE_CHAIN (subblock)) + if (calls_function (subblock, which)) + return 1; + } + return 0; + + case METHOD_CALL_EXPR: + length = 3; + break; + + case WITH_CLEANUP_EXPR: + length = 1; + break; + + case RTL_EXPR: + return 0; + } + + for (i = 0; i < length; i++) + if (TREE_OPERAND (exp, i) != 0 + && calls_function (TREE_OPERAND (exp, i), which)) + return 1; + + return 0; +} + +/* Force FUNEXP into a form suitable for the address of a CALL, + and return that as an rtx. Also load the static chain register + if FNDECL is a nested function. + + USE_INSNS points to a variable holding a chain of USE insns + to which a USE of the static chain + register should be added, if required. */ + +rtx +prepare_call_address (funexp, fndecl, use_insns) + rtx funexp; + tree fndecl; + rtx *use_insns; +{ + rtx static_chain_value = 0; + + funexp = protect_from_queue (funexp, 0); + + if (fndecl != 0) + /* Get possible static chain value for nested function in C. */ + static_chain_value = lookup_static_chain (fndecl); + + /* Make a valid memory address and copy constants thru pseudo-regs, + but not for a constant address if -fno-function-cse. */ + if (GET_CODE (funexp) != SYMBOL_REF) + funexp = memory_address (FUNCTION_MODE, funexp); + else + { +#ifndef NO_FUNCTION_CSE + if (optimize && ! flag_no_function_cse) +#ifdef NO_RECURSIVE_FUNCTION_CSE + if (fndecl != current_function_decl) +#endif + funexp = force_reg (Pmode, funexp); +#endif + } + + if (static_chain_value != 0) + { + emit_move_insn (static_chain_rtx, static_chain_value); + + /* Put the USE insn in the chain we were passed. It will later be + output immediately in front of the CALL insn. */ + push_to_sequence (*use_insns); + emit_insn (gen_rtx (USE, VOIDmode, static_chain_rtx)); + *use_insns = get_insns (); + end_sequence (); + } + + return funexp; +} + +/* Generate instructions to call function FUNEXP, + and optionally pop the results. + The CALL_INSN is the first insn generated. + + FUNTYPE is the data type of the function, or, for a library call, + the identifier for the name of the call. This is given to the + macro RETURN_POPS_ARGS to determine whether this function pops its own args. + + STACK_SIZE is the number of bytes of arguments on the stack, + rounded up to STACK_BOUNDARY; zero if the size is variable. + This is both to put into the call insn and + to generate explicit popping code if necessary. + + STRUCT_VALUE_SIZE is the number of bytes wanted in a structure value. + It is zero if this call doesn't want a structure value. + + NEXT_ARG_REG is the rtx that results from executing + FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1) + just after all the args have had their registers assigned. + This could be whatever you like, but normally it is the first + arg-register beyond those used for args in this call, + or 0 if all the arg-registers are used in this call. + It is passed on to `gen_call' so you can put this info in the call insn. + + VALREG is a hard register in which a value is returned, + or 0 if the call does not return a value. + + OLD_INHIBIT_DEFER_POP is the value that `inhibit_defer_pop' had before + the args to this call were processed. + We restore `inhibit_defer_pop' to that value. + + USE_INSNS is a chain of USE insns to be emitted immediately before + the actual CALL insn. + + IS_CONST is true if this is a `const' call. */ + +static void +emit_call_1 (funexp, funtype, stack_size, struct_value_size, next_arg_reg, + valreg, old_inhibit_defer_pop, use_insns, is_const) + rtx funexp; + tree funtype; + int stack_size; + int struct_value_size; + rtx next_arg_reg; + rtx valreg; + int old_inhibit_defer_pop; + rtx use_insns; + int is_const; +{ + rtx stack_size_rtx = GEN_INT (stack_size); + rtx struct_value_size_rtx = GEN_INT (struct_value_size); + rtx call_insn; + int already_popped = 0; + + /* Ensure address is valid. SYMBOL_REF is already valid, so no need, + and we don't want to load it into a register as an optimization, + because prepare_call_address already did it if it should be done. */ + if (GET_CODE (funexp) != SYMBOL_REF) + funexp = memory_address (FUNCTION_MODE, funexp); + +#ifndef ACCUMULATE_OUTGOING_ARGS +#if defined (HAVE_call_pop) && defined (HAVE_call_value_pop) + if (HAVE_call_pop && HAVE_call_value_pop + && (RETURN_POPS_ARGS (funtype, stack_size) > 0 || stack_size == 0)) + { + rtx n_pop = GEN_INT (RETURN_POPS_ARGS (funtype, stack_size)); + rtx pat; + + /* If this subroutine pops its own args, record that in the call insn + if possible, for the sake of frame pointer elimination. */ + if (valreg) + pat = gen_call_value_pop (valreg, + gen_rtx (MEM, FUNCTION_MODE, funexp), + stack_size_rtx, next_arg_reg, n_pop); + else + pat = gen_call_pop (gen_rtx (MEM, FUNCTION_MODE, funexp), + stack_size_rtx, next_arg_reg, n_pop); + + emit_call_insn (pat); + already_popped = 1; + } + else +#endif +#endif + +#if defined (HAVE_call) && defined (HAVE_call_value) + if (HAVE_call && HAVE_call_value) + { + if (valreg) + emit_call_insn (gen_call_value (valreg, + gen_rtx (MEM, FUNCTION_MODE, funexp), + stack_size_rtx, next_arg_reg, + NULL_RTX)); + else + emit_call_insn (gen_call (gen_rtx (MEM, FUNCTION_MODE, funexp), + stack_size_rtx, next_arg_reg, + struct_value_size_rtx)); + } + else +#endif + abort (); + + /* Find the CALL insn we just emitted and write the USE insns before it. */ + for (call_insn = get_last_insn (); + call_insn && GET_CODE (call_insn) != CALL_INSN; + call_insn = PREV_INSN (call_insn)) + ; + + if (! call_insn) + abort (); + + /* Put the USE insns before the CALL. */ + emit_insns_before (use_insns, call_insn); + + /* If this is a const call, then set the insn's unchanging bit. */ + if (is_const) + CONST_CALL_P (call_insn) = 1; + + /* Restore this now, so that we do defer pops for this call's args + if the context of the call as a whole permits. */ + inhibit_defer_pop = old_inhibit_defer_pop; + +#ifndef ACCUMULATE_OUTGOING_ARGS + /* If returning from the subroutine does not automatically pop the args, + we need an instruction to pop them sooner or later. + Perhaps do it now; perhaps just record how much space to pop later. + + If returning from the subroutine does pop the args, indicate that the + stack pointer will be changed. */ + + if (stack_size != 0 && RETURN_POPS_ARGS (funtype, stack_size) > 0) + { + if (!already_popped) + emit_insn (gen_rtx (CLOBBER, VOIDmode, stack_pointer_rtx)); + stack_size -= RETURN_POPS_ARGS (funtype, stack_size); + stack_size_rtx = GEN_INT (stack_size); + } + + if (stack_size != 0) + { + if (flag_defer_pop && inhibit_defer_pop == 0 && !is_const) + pending_stack_adjust += stack_size; + else + adjust_stack (stack_size_rtx); + } +#endif +} + +/* Generate all the code for a function call + and return an rtx for its value. + Store the value in TARGET (specified as an rtx) if convenient. + If the value is stored in TARGET then TARGET is returned. + If IGNORE is nonzero, then we ignore the value of the function call. */ + +rtx +expand_call (exp, target, ignore) + tree exp; + rtx target; + int ignore; +{ + /* List of actual parameters. */ + tree actparms = TREE_OPERAND (exp, 1); + /* RTX for the function to be called. */ + rtx funexp; + /* Tree node for the function to be called (not the address!). */ + tree funtree; + /* Data type of the function. */ + tree funtype; + /* Declaration of the function being called, + or 0 if the function is computed (not known by name). */ + tree fndecl = 0; + char *name = 0; + + /* Register in which non-BLKmode value will be returned, + or 0 if no value or if value is BLKmode. */ + rtx valreg; + /* Address where we should return a BLKmode value; + 0 if value not BLKmode. */ + rtx structure_value_addr = 0; + /* Nonzero if that address is being passed by treating it as + an extra, implicit first parameter. Otherwise, + it is passed by being copied directly into struct_value_rtx. */ + int structure_value_addr_parm = 0; + /* Size of aggregate value wanted, or zero if none wanted + or if we are using the non-reentrant PCC calling convention + or expecting the value in registers. */ + int struct_value_size = 0; + /* Nonzero if called function returns an aggregate in memory PCC style, + by returning the address of where to find it. */ + int pcc_struct_value = 0; + + /* Number of actual parameters in this call, including struct value addr. */ + int num_actuals; + /* Number of named args. Args after this are anonymous ones + and they must all go on the stack. */ + int n_named_args; + /* Count arg position in order args appear. */ + int argpos; + + /* Vector of information about each argument. + Arguments are numbered in the order they will be pushed, + not the order they are written. */ + struct arg_data *args; + + /* Total size in bytes of all the stack-parms scanned so far. */ + struct args_size args_size; + /* Size of arguments before any adjustments (such as rounding). */ + struct args_size original_args_size; + /* Data on reg parms scanned so far. */ + CUMULATIVE_ARGS args_so_far; + /* Nonzero if a reg parm has been scanned. */ + int reg_parm_seen; + /* Nonzero if this is an indirect function call. */ + int current_call_is_indirect = 0; + + /* Nonzero if we must avoid push-insns in the args for this call. + If stack space is allocated for register parameters, but not by the + caller, then it is preallocated in the fixed part of the stack frame. + So the entire argument block must then be preallocated (i.e., we + ignore PUSH_ROUNDING in that case). */ + +#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE) + int must_preallocate = 1; +#else +#ifdef PUSH_ROUNDING + int must_preallocate = 0; +#else + int must_preallocate = 1; +#endif +#endif + + /* Size of the stack reserved for parameter registers. */ + int reg_parm_stack_space = 0; + + /* 1 if scanning parms front to back, -1 if scanning back to front. */ + int inc; + /* Address of space preallocated for stack parms + (on machines that lack push insns), or 0 if space not preallocated. */ + rtx argblock = 0; + + /* Nonzero if it is plausible that this is a call to alloca. */ + int may_be_alloca; + /* Nonzero if this is a call to setjmp or a related function. */ + int returns_twice; + /* Nonzero if this is a call to `longjmp'. */ + int is_longjmp; + /* Nonzero if this is a call to an inline function. */ + int is_integrable = 0; + /* Nonzero if this is a call to a `const' function. + Note that only explicitly named functions are handled as `const' here. */ + int is_const = 0; + /* Nonzero if this is a call to a `volatile' function. */ + int is_volatile = 0; +#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE) + /* Define the boundary of the register parm stack space that needs to be + save, if any. */ + int low_to_save = -1, high_to_save; + rtx save_area = 0; /* Place that it is saved */ +#endif + +#ifdef ACCUMULATE_OUTGOING_ARGS + int initial_highest_arg_in_use = highest_outgoing_arg_in_use; + char *initial_stack_usage_map = stack_usage_map; +#endif + + rtx old_stack_level = 0; + int old_pending_adj; + int old_stack_arg_under_construction; + int old_inhibit_defer_pop = inhibit_defer_pop; + tree old_cleanups = cleanups_this_call; + + rtx use_insns = 0; + + register tree p; + register int i, j; + + /* See if we can find a DECL-node for the actual function. + As a result, decide whether this is a call to an integrable function. */ + + p = TREE_OPERAND (exp, 0); + if (TREE_CODE (p) == ADDR_EXPR) + { + fndecl = TREE_OPERAND (p, 0); + if (TREE_CODE (fndecl) != FUNCTION_DECL) + { + /* May still be a `const' function if it is + a call through a pointer-to-const. + But we don't handle that. */ + fndecl = 0; + } + else + { + if (!flag_no_inline + && fndecl != current_function_decl + && DECL_SAVED_INSNS (fndecl)) + is_integrable = 1; + else if (! TREE_ADDRESSABLE (fndecl)) + { + /* In case this function later becomes inlinable, + record that there was already a non-inline call to it. + + Use abstraction instead of setting TREE_ADDRESSABLE + directly. */ + if (DECL_INLINE (fndecl) && extra_warnings && warn_inline + && !flag_no_inline) + warning_with_decl (fndecl, "can't inline call to `%s' which was declared inline"); + mark_addressable (fndecl); + } + + if (TREE_READONLY (fndecl) && ! TREE_THIS_VOLATILE (fndecl) + && TYPE_MODE (TREE_TYPE (exp)) != VOIDmode) + is_const = 1; + } + } + + is_volatile = TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (p))); + +#ifdef REG_PARM_STACK_SPACE +#ifdef MAYBE_REG_PARM_STACK_SPACE + reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE; +#else + reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl); +#endif +#endif + + /* Warn if this value is an aggregate type, + regardless of which calling convention we are using for it. */ + if (warn_aggregate_return + && (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE + || TREE_CODE (TREE_TYPE (exp)) == QUAL_UNION_TYPE + || TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE)) + warning ("function call has aggregate value"); + + /* Set up a place to return a structure. */ + + /* Cater to broken compilers. */ + if (aggregate_value_p (exp)) + { + /* This call returns a big structure. */ + is_const = 0; + +#ifdef PCC_STATIC_STRUCT_RETURN + { + pcc_struct_value = 1; + is_integrable = 0; /* Easier than making that case work right. */ + } +#else /* not PCC_STATIC_STRUCT_RETURN */ + { + struct_value_size = int_size_in_bytes (TREE_TYPE (exp)); + + if (struct_value_size < 0) + abort (); + + if (target && GET_CODE (target) == MEM) + structure_value_addr = XEXP (target, 0); + else + { + /* Assign a temporary on the stack to hold the value. */ + + /* For variable-sized objects, we must be called with a target + specified. If we were to allocate space on the stack here, + we would have no way of knowing when to free it. */ + + structure_value_addr + = XEXP (assign_stack_temp (BLKmode, struct_value_size, 1), 0); + target = 0; + } + } +#endif /* not PCC_STATIC_STRUCT_RETURN */ + } + + /* If called function is inline, try to integrate it. */ + + if (is_integrable) + { + rtx temp; + rtx before_call = get_last_insn (); + + temp = expand_inline_function (fndecl, actparms, target, + ignore, TREE_TYPE (exp), + structure_value_addr); + + /* If inlining succeeded, return. */ + if ((HOST_WIDE_INT) temp != -1) + { + /* Perform all cleanups needed for the arguments of this call + (i.e. destructors in C++). It is ok if these destructors + clobber RETURN_VALUE_REG, because the only time we care about + this is when TARGET is that register. But in C++, we take + care to never return that register directly. */ + expand_cleanups_to (old_cleanups); + +#ifdef ACCUMULATE_OUTGOING_ARGS + /* If the outgoing argument list must be preserved, push + the stack before executing the inlined function if it + makes any calls. */ + + for (i = reg_parm_stack_space - 1; i >= 0; i--) + if (i < highest_outgoing_arg_in_use && stack_usage_map[i] != 0) + break; + + if (stack_arg_under_construction || i >= 0) + { + rtx insn = NEXT_INSN (before_call), seq; + + /* Look for a call in the inline function code. + If OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)) is + nonzero then there is a call and it is not necessary + to scan the insns. */ + + if (OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)) == 0) + for (; insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == CALL_INSN) + break; + + if (insn) + { + /* Reserve enough stack space so that the largest + argument list of any function call in the inline + function does not overlap the argument list being + evaluated. This is usually an overestimate because + allocate_dynamic_stack_space reserves space for an + outgoing argument list in addition to the requested + space, but there is no way to ask for stack space such + that an argument list of a certain length can be + safely constructed. */ + + int adjust = OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)); +#ifdef REG_PARM_STACK_SPACE + /* Add the stack space reserved for register arguments + in the inline function. What is really needed is the + largest value of reg_parm_stack_space in the inline + function, but that is not available. Using the current + value of reg_parm_stack_space is wrong, but gives + correct results on all supported machines. */ + adjust += reg_parm_stack_space; +#endif + start_sequence (); + emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); + allocate_dynamic_stack_space (GEN_INT (adjust), + NULL_RTX, BITS_PER_UNIT); + seq = get_insns (); + end_sequence (); + emit_insns_before (seq, NEXT_INSN (before_call)); + emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX); + } + } +#endif + + /* If the result is equivalent to TARGET, return TARGET to simplify + checks in store_expr. They can be equivalent but not equal in the + case of a function that returns BLKmode. */ + if (temp != target && rtx_equal_p (temp, target)) + return target; + return temp; + } + + /* If inlining failed, mark FNDECL as needing to be compiled + separately after all. */ + mark_addressable (fndecl); + } + + /* When calling a const function, we must pop the stack args right away, + so that the pop is deleted or moved with the call. */ + if (is_const) + NO_DEFER_POP; + + function_call_count++; + + if (fndecl && DECL_NAME (fndecl)) + name = IDENTIFIER_POINTER (DECL_NAME (fndecl)); + + /* On some machines (such as the PA) indirect calls have a different + calling convention than normal calls. FUNCTION_ARG in the target + description can look at current_call_is_indirect to determine which + calling convention to use. */ + current_call_is_indirect = (fndecl == 0); +#if 0 + = TREE_CODE (TREE_OPERAND (exp, 0)) == NON_LVALUE_EXPR ? 1 : 0; +#endif + +#if 0 + /* Unless it's a call to a specific function that isn't alloca, + if it has one argument, we must assume it might be alloca. */ + + may_be_alloca = + (!(fndecl != 0 && strcmp (name, "alloca")) + && actparms != 0 + && TREE_CHAIN (actparms) == 0); +#else + /* We assume that alloca will always be called by name. It + makes no sense to pass it as a pointer-to-function to + anything that does not understand its behavior. */ + may_be_alloca = + (name && ((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6 + && name[0] == 'a' + && ! strcmp (name, "alloca")) + || (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16 + && name[0] == '_' + && ! strcmp (name, "__builtin_alloca")))); +#endif + + /* See if this is a call to a function that can return more than once + or a call to longjmp. */ + + returns_twice = 0; + is_longjmp = 0; + + if (name != 0 && IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 15) + { + char *tname = name; + + if (name[0] == '_') + tname += ((name[1] == '_' && name[2] == 'x') ? 3 : 1); + + if (tname[0] == 's') + { + returns_twice + = ((tname[1] == 'e' + && (! strcmp (tname, "setjmp") + || ! strcmp (tname, "setjmp_syscall"))) + || (tname[1] == 'i' + && ! strcmp (tname, "sigsetjmp")) + || (tname[1] == 'a' + && ! strcmp (tname, "savectx"))); + if (tname[1] == 'i' + && ! strcmp (tname, "siglongjmp")) + is_longjmp = 1; + } + else if ((tname[0] == 'q' && tname[1] == 's' + && ! strcmp (tname, "qsetjmp")) + || (tname[0] == 'v' && tname[1] == 'f' + && ! strcmp (tname, "vfork"))) + returns_twice = 1; + + else if (tname[0] == 'l' && tname[1] == 'o' + && ! strcmp (tname, "longjmp")) + is_longjmp = 1; + } + + if (may_be_alloca) + current_function_calls_alloca = 1; + + /* Don't let pending stack adjusts add up to too much. + Also, do all pending adjustments now + if there is any chance this might be a call to alloca. */ + + if (pending_stack_adjust >= 32 + || (pending_stack_adjust > 0 && may_be_alloca)) + do_pending_stack_adjust (); + + /* Operand 0 is a pointer-to-function; get the type of the function. */ + funtype = TREE_TYPE (TREE_OPERAND (exp, 0)); + if (TREE_CODE (funtype) != POINTER_TYPE) + abort (); + funtype = TREE_TYPE (funtype); + + /* Push the temporary stack slot level so that we can free temporaries used + by each of the arguments separately. */ + push_temp_slots (); + + /* Start updating where the next arg would go. */ + INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX); + + /* If struct_value_rtx is 0, it means pass the address + as if it were an extra parameter. */ + if (structure_value_addr && struct_value_rtx == 0) + { +#ifdef ACCUMULATE_OUTGOING_ARGS + /* If the stack will be adjusted, make sure the structure address + does not refer to virtual_outgoing_args_rtx. */ + rtx temp = (stack_arg_under_construction + ? copy_addr_to_reg (structure_value_addr) + : force_reg (Pmode, structure_value_addr)); +#else + rtx temp = force_reg (Pmode, structure_value_addr); +#endif + + actparms + = tree_cons (error_mark_node, + make_tree (build_pointer_type (TREE_TYPE (funtype)), + temp), + actparms); + structure_value_addr_parm = 1; + } + + /* Count the arguments and set NUM_ACTUALS. */ + for (p = actparms, i = 0; p; p = TREE_CHAIN (p)) i++; + num_actuals = i; + + /* Compute number of named args. + Normally, don't include the last named arg if anonymous args follow. + (If no anonymous args follow, the result of list_length + is actually one too large.) + + If SETUP_INCOMING_VARARGS is defined, this machine will be able to + place unnamed args that were passed in registers into the stack. So + treat all args as named. This allows the insns emitting for a specific + argument list to be independent of the function declaration. + + If SETUP_INCOMING_VARARGS is not defined, we do not have any reliable + way to pass unnamed args in registers, so we must force them into + memory. */ +#ifndef SETUP_INCOMING_VARARGS + if (TYPE_ARG_TYPES (funtype) != 0) + n_named_args + = list_length (TYPE_ARG_TYPES (funtype)) - 1 + /* Count the struct value address, if it is passed as a parm. */ + + structure_value_addr_parm; + else +#endif + /* If we know nothing, treat all args as named. */ + n_named_args = num_actuals; + + /* Make a vector to hold all the information about each arg. */ + args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data)); + bzero (args, num_actuals * sizeof (struct arg_data)); + + args_size.constant = 0; + args_size.var = 0; + + /* In this loop, we consider args in the order they are written. + We fill up ARGS from the front of from the back if necessary + so that in any case the first arg to be pushed ends up at the front. */ + +#ifdef PUSH_ARGS_REVERSED + i = num_actuals - 1, inc = -1; + /* In this case, must reverse order of args + so that we compute and push the last arg first. */ +#else + i = 0, inc = 1; +#endif + + /* I counts args in order (to be) pushed; ARGPOS counts in order written. */ + for (p = actparms, argpos = 0; p; p = TREE_CHAIN (p), i += inc, argpos++) + { + tree type = TREE_TYPE (TREE_VALUE (p)); + enum machine_mode mode; + + args[i].tree_value = TREE_VALUE (p); + + /* Replace erroneous argument with constant zero. */ + if (type == error_mark_node || TYPE_SIZE (type) == 0) + args[i].tree_value = integer_zero_node, type = integer_type_node; + + /* Decide where to pass this arg. + + args[i].reg is nonzero if all or part is passed in registers. + + args[i].partial is nonzero if part but not all is passed in registers, + and the exact value says how many words are passed in registers. + + args[i].pass_on_stack is nonzero if the argument must at least be + computed on the stack. It may then be loaded back into registers + if args[i].reg is nonzero. + + These decisions are driven by the FUNCTION_... macros and must agree + with those made by function.c. */ + +#ifdef FUNCTION_ARG_PASS_BY_REFERENCE + /* See if this argument should be passed by invisible reference. */ + if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, TYPE_MODE (type), type, + argpos < n_named_args)) + { +#ifdef FUNCTION_ARG_CALLEE_COPIES + if (FUNCTION_ARG_CALLEE_COPIES (args_so_far, TYPE_MODE (type), type, + argpos < n_named_args) + /* If it's in a register, we must make a copy of it too. */ + /* ??? Is this a sufficient test? Is there a better one? */ + && !(TREE_CODE (args[i].tree_value) == VAR_DECL + && REG_P (DECL_RTL (args[i].tree_value)))) + { + args[i].tree_value = build1 (ADDR_EXPR, + build_pointer_type (type), + args[i].tree_value); + type = build_pointer_type (type); + } + else +#endif + { + /* We make a copy of the object and pass the address to the + function being called. */ + rtx copy; + + if (TYPE_SIZE (type) == 0 + || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) + { + /* This is a variable-sized object. Make space on the stack + for it. */ + rtx size_rtx = expr_size (TREE_VALUE (p)); + + if (old_stack_level == 0) + { + emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); + old_pending_adj = pending_stack_adjust; + pending_stack_adjust = 0; + } + + copy = gen_rtx (MEM, BLKmode, + allocate_dynamic_stack_space (size_rtx, + NULL_RTX, + TYPE_ALIGN (type))); + } + else + { + int size = int_size_in_bytes (type); + copy = assign_stack_temp (TYPE_MODE (type), size, 1); + } + + store_expr (args[i].tree_value, copy, 0); + + args[i].tree_value = build1 (ADDR_EXPR, + build_pointer_type (type), + make_tree (type, copy)); + type = build_pointer_type (type); + } + } +#endif /* FUNCTION_ARG_PASS_BY_REFERENCE */ + + mode = TYPE_MODE (type); + +#ifdef PROMOTE_FUNCTION_ARGS + /* Compute the mode in which the arg is actually to be extended to. */ + if (TREE_CODE (type) == INTEGER_TYPE || TREE_CODE (type) == ENUMERAL_TYPE + || TREE_CODE (type) == BOOLEAN_TYPE || TREE_CODE (type) == CHAR_TYPE + || TREE_CODE (type) == REAL_TYPE || TREE_CODE (type) == POINTER_TYPE + || TREE_CODE (type) == OFFSET_TYPE) + { + int unsignedp = TREE_UNSIGNED (type); + PROMOTE_MODE (mode, unsignedp, type); + args[i].unsignedp = unsignedp; + } +#endif + + args[i].mode = mode; + args[i].reg = FUNCTION_ARG (args_so_far, mode, type, + argpos < n_named_args); +#ifdef FUNCTION_ARG_PARTIAL_NREGS + if (args[i].reg) + args[i].partial + = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, type, + argpos < n_named_args); +#endif + + args[i].pass_on_stack = MUST_PASS_IN_STACK (mode, type); + + /* If FUNCTION_ARG returned an (expr_list (nil) FOO), it means that + we are to pass this arg in the register(s) designated by FOO, but + also to pass it in the stack. */ + if (args[i].reg && GET_CODE (args[i].reg) == EXPR_LIST + && XEXP (args[i].reg, 0) == 0) + args[i].pass_on_stack = 1, args[i].reg = XEXP (args[i].reg, 1); + + /* If this is an addressable type, we must preallocate the stack + since we must evaluate the object into its final location. + + If this is to be passed in both registers and the stack, it is simpler + to preallocate. */ + if (TREE_ADDRESSABLE (type) + || (args[i].pass_on_stack && args[i].reg != 0)) + must_preallocate = 1; + + /* If this is an addressable type, we cannot pre-evaluate it. Thus, + we cannot consider this function call constant. */ + if (TREE_ADDRESSABLE (type)) + is_const = 0; + + /* Compute the stack-size of this argument. */ + if (args[i].reg == 0 || args[i].partial != 0 +#ifdef REG_PARM_STACK_SPACE + || reg_parm_stack_space > 0 +#endif + || args[i].pass_on_stack) + locate_and_pad_parm (mode, type, +#ifdef STACK_PARMS_IN_REG_PARM_AREA + 1, +#else + args[i].reg != 0, +#endif + fndecl, &args_size, &args[i].offset, + &args[i].size); + +#ifndef ARGS_GROW_DOWNWARD + args[i].slot_offset = args_size; +#endif + +#ifndef REG_PARM_STACK_SPACE + /* If a part of the arg was put into registers, + don't include that part in the amount pushed. */ + if (! args[i].pass_on_stack) + args[i].size.constant -= ((args[i].partial * UNITS_PER_WORD) + / (PARM_BOUNDARY / BITS_PER_UNIT) + * (PARM_BOUNDARY / BITS_PER_UNIT)); +#endif + + /* Update ARGS_SIZE, the total stack space for args so far. */ + + args_size.constant += args[i].size.constant; + if (args[i].size.var) + { + ADD_PARM_SIZE (args_size, args[i].size.var); + } + + /* Since the slot offset points to the bottom of the slot, + we must record it after incrementing if the args grow down. */ +#ifdef ARGS_GROW_DOWNWARD + args[i].slot_offset = args_size; + + args[i].slot_offset.constant = -args_size.constant; + if (args_size.var) + { + SUB_PARM_SIZE (args[i].slot_offset, args_size.var); + } +#endif + + /* Increment ARGS_SO_FAR, which has info about which arg-registers + have been used, etc. */ + + FUNCTION_ARG_ADVANCE (args_so_far, TYPE_MODE (type), type, + argpos < n_named_args); + } + +#ifdef FINAL_REG_PARM_STACK_SPACE + reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant, + args_size.var); +#endif + + /* Compute the actual size of the argument block required. The variable + and constant sizes must be combined, the size may have to be rounded, + and there may be a minimum required size. */ + + original_args_size = args_size; + if (args_size.var) + { + /* If this function requires a variable-sized argument list, don't try to + make a cse'able block for this call. We may be able to do this + eventually, but it is too complicated to keep track of what insns go + in the cse'able block and which don't. */ + + is_const = 0; + must_preallocate = 1; + + args_size.var = ARGS_SIZE_TREE (args_size); + args_size.constant = 0; + +#ifdef STACK_BOUNDARY + if (STACK_BOUNDARY != BITS_PER_UNIT) + args_size.var = round_up (args_size.var, STACK_BYTES); +#endif + +#ifdef REG_PARM_STACK_SPACE + if (reg_parm_stack_space > 0) + { + args_size.var + = size_binop (MAX_EXPR, args_size.var, + size_int (REG_PARM_STACK_SPACE (fndecl))); + +#ifndef OUTGOING_REG_PARM_STACK_SPACE + /* The area corresponding to register parameters is not to count in + the size of the block we need. So make the adjustment. */ + args_size.var + = size_binop (MINUS_EXPR, args_size.var, + size_int (reg_parm_stack_space)); +#endif + } +#endif + } + else + { +#ifdef STACK_BOUNDARY + args_size.constant = (((args_size.constant + (STACK_BYTES - 1)) + / STACK_BYTES) * STACK_BYTES); +#endif + +#ifdef REG_PARM_STACK_SPACE + args_size.constant = MAX (args_size.constant, + reg_parm_stack_space); +#ifdef MAYBE_REG_PARM_STACK_SPACE + if (reg_parm_stack_space == 0) + args_size.constant = 0; +#endif +#ifndef OUTGOING_REG_PARM_STACK_SPACE + args_size.constant -= reg_parm_stack_space; +#endif +#endif + } + + /* See if we have or want to preallocate stack space. + + If we would have to push a partially-in-regs parm + before other stack parms, preallocate stack space instead. + + If the size of some parm is not a multiple of the required stack + alignment, we must preallocate. + + If the total size of arguments that would otherwise create a copy in + a temporary (such as a CALL) is more than half the total argument list + size, preallocation is faster. + + Another reason to preallocate is if we have a machine (like the m88k) + where stack alignment is required to be maintained between every + pair of insns, not just when the call is made. However, we assume here + that such machines either do not have push insns (and hence preallocation + would occur anyway) or the problem is taken care of with + PUSH_ROUNDING. */ + + if (! must_preallocate) + { + int partial_seen = 0; + int copy_to_evaluate_size = 0; + + for (i = 0; i < num_actuals && ! must_preallocate; i++) + { + if (args[i].partial > 0 && ! args[i].pass_on_stack) + partial_seen = 1; + else if (partial_seen && args[i].reg == 0) + must_preallocate = 1; + + if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode + && (TREE_CODE (args[i].tree_value) == CALL_EXPR + || TREE_CODE (args[i].tree_value) == TARGET_EXPR + || TREE_CODE (args[i].tree_value) == COND_EXPR + || TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)))) + copy_to_evaluate_size + += int_size_in_bytes (TREE_TYPE (args[i].tree_value)); + } + + if (copy_to_evaluate_size * 2 >= args_size.constant + && args_size.constant > 0) + must_preallocate = 1; + } + + /* If the structure value address will reference the stack pointer, we must + stabilize it. We don't need to do this if we know that we are not going + to adjust the stack pointer in processing this call. */ + + if (structure_value_addr + && (reg_mentioned_p (virtual_stack_dynamic_rtx, structure_value_addr) + || reg_mentioned_p (virtual_outgoing_args_rtx, structure_value_addr)) + && (args_size.var +#ifndef ACCUMULATE_OUTGOING_ARGS + || args_size.constant +#endif + )) + structure_value_addr = copy_to_reg (structure_value_addr); + + /* If this function call is cse'able, precompute all the parameters. + Note that if the parameter is constructed into a temporary, this will + cause an additional copy because the parameter will be constructed + into a temporary location and then copied into the outgoing arguments. + If a parameter contains a call to alloca and this function uses the + stack, precompute the parameter. */ + + /* If we preallocated the stack space, and some arguments must be passed + on the stack, then we must precompute any parameter which contains a + function call which will store arguments on the stack. + Otherwise, evaluating the parameter may clobber previous parameters + which have already been stored into the stack. */ + + for (i = 0; i < num_actuals; i++) + if (is_const + || ((args_size.var != 0 || args_size.constant != 0) + && calls_function (args[i].tree_value, 1)) + || (must_preallocate && (args_size.var != 0 || args_size.constant != 0) + && calls_function (args[i].tree_value, 0))) + { + args[i].initial_value = args[i].value + = expand_expr (args[i].tree_value, NULL_RTX, VOIDmode, 0); + + if (GET_MODE (args[i].value ) != VOIDmode + && GET_MODE (args[i].value) != args[i].mode) + args[i].value = convert_to_mode (args[i].mode, args[i].value, + args[i].unsignedp); + preserve_temp_slots (args[i].value); + + free_temp_slots (); + + /* ANSI doesn't require a sequence point here, + but PCC has one, so this will avoid some problems. */ + emit_queue (); + } + + /* Now we are about to start emitting insns that can be deleted + if a libcall is deleted. */ + if (is_const) + start_sequence (); + + /* If we have no actual push instructions, or shouldn't use them, + make space for all args right now. */ + + if (args_size.var != 0) + { + if (old_stack_level == 0) + { + emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); + old_pending_adj = pending_stack_adjust; + pending_stack_adjust = 0; +#ifdef ACCUMULATE_OUTGOING_ARGS + /* stack_arg_under_construction says whether a stack arg is + being constructed at the old stack level. Pushing the stack + gets a clean outgoing argument block. */ + old_stack_arg_under_construction = stack_arg_under_construction; + stack_arg_under_construction = 0; +#endif + } + argblock = push_block (ARGS_SIZE_RTX (args_size), 0, 0); + } + else if (must_preallocate) + { + /* Note that we must go through the motions of allocating an argument + block even if the size is zero because we may be storing args + in the area reserved for register arguments, which may be part of + the stack frame. */ + int needed = args_size.constant; + +#ifdef ACCUMULATE_OUTGOING_ARGS + /* Store the maximum argument space used. It will be pushed by the + prologue. + + Since the stack pointer will never be pushed, it is possible for + the evaluation of a parm to clobber something we have already + written to the stack. Since most function calls on RISC machines + do not use the stack, this is uncommon, but must work correctly. + + Therefore, we save any area of the stack that was already written + and that we are using. Here we set up to do this by making a new + stack usage map from the old one. The actual save will be done + by store_one_arg. + + Another approach might be to try to reorder the argument + evaluations to avoid this conflicting stack usage. */ + + if (needed > current_function_outgoing_args_size) + current_function_outgoing_args_size = needed; + +#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE) + /* Since we will be writing into the entire argument area, the + map must be allocated for its entire size, not just the part that + is the responsibility of the caller. */ + needed += reg_parm_stack_space; +#endif + +#ifdef ARGS_GROW_DOWNWARD + highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use, + needed + 1); +#else + highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use, needed); +#endif + stack_usage_map = (char *) alloca (highest_outgoing_arg_in_use); + + if (initial_highest_arg_in_use) + bcopy (initial_stack_usage_map, stack_usage_map, + initial_highest_arg_in_use); + + if (initial_highest_arg_in_use != highest_outgoing_arg_in_use) + bzero (&stack_usage_map[initial_highest_arg_in_use], + highest_outgoing_arg_in_use - initial_highest_arg_in_use); + needed = 0; + + /* The address of the outgoing argument list must not be copied to a + register here, because argblock would be left pointing to the + wrong place after the call to allocate_dynamic_stack_space below. */ + + argblock = virtual_outgoing_args_rtx; + +#else /* not ACCUMULATE_OUTGOING_ARGS */ + if (inhibit_defer_pop == 0) + { + /* Try to reuse some or all of the pending_stack_adjust + to get this space. Maybe we can avoid any pushing. */ + if (needed > pending_stack_adjust) + { + needed -= pending_stack_adjust; + pending_stack_adjust = 0; + } + else + { + pending_stack_adjust -= needed; + needed = 0; + } + } + /* Special case this because overhead of `push_block' in this + case is non-trivial. */ + if (needed == 0) + argblock = virtual_outgoing_args_rtx; + else + argblock = push_block (GEN_INT (needed), 0, 0); + + /* We only really need to call `copy_to_reg' in the case where push + insns are going to be used to pass ARGBLOCK to a function + call in ARGS. In that case, the stack pointer changes value + from the allocation point to the call point, and hence + the value of VIRTUAL_OUTGOING_ARGS_RTX changes as well. + But might as well always do it. */ + argblock = copy_to_reg (argblock); +#endif /* not ACCUMULATE_OUTGOING_ARGS */ + } + + +#ifdef ACCUMULATE_OUTGOING_ARGS + /* The save/restore code in store_one_arg handles all cases except one: + a constructor call (including a C function returning a BLKmode struct) + to initialize an argument. */ + if (stack_arg_under_construction) + { +#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE) + rtx push_size = GEN_INT (reg_parm_stack_space + args_size.constant); +#else + rtx push_size = GEN_INT (args_size.constant); +#endif + if (old_stack_level == 0) + { + emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); + old_pending_adj = pending_stack_adjust; + pending_stack_adjust = 0; + /* stack_arg_under_construction says whether a stack arg is + being constructed at the old stack level. Pushing the stack + gets a clean outgoing argument block. */ + old_stack_arg_under_construction = stack_arg_under_construction; + stack_arg_under_construction = 0; + /* Make a new map for the new argument list. */ + stack_usage_map = (char *)alloca (highest_outgoing_arg_in_use); + bzero (stack_usage_map, highest_outgoing_arg_in_use); + highest_outgoing_arg_in_use = 0; + } + allocate_dynamic_stack_space (push_size, NULL_RTX, BITS_PER_UNIT); + } + /* If argument evaluation might modify the stack pointer, copy the + address of the argument list to a register. */ + for (i = 0; i < num_actuals; i++) + if (args[i].pass_on_stack) + { + argblock = copy_addr_to_reg (argblock); + break; + } +#endif + + + /* If we preallocated stack space, compute the address of each argument. + We need not ensure it is a valid memory address here; it will be + validized when it is used. */ + if (argblock) + { + rtx arg_reg = argblock; + int arg_offset = 0; + + if (GET_CODE (argblock) == PLUS) + arg_reg = XEXP (argblock, 0), arg_offset = INTVAL (XEXP (argblock, 1)); + + for (i = 0; i < num_actuals; i++) + { + rtx offset = ARGS_SIZE_RTX (args[i].offset); + rtx slot_offset = ARGS_SIZE_RTX (args[i].slot_offset); + rtx addr; + + /* Skip this parm if it will not be passed on the stack. */ + if (! args[i].pass_on_stack && args[i].reg != 0) + continue; + + if (GET_CODE (offset) == CONST_INT) + addr = plus_constant (arg_reg, INTVAL (offset)); + else + addr = gen_rtx (PLUS, Pmode, arg_reg, offset); + + addr = plus_constant (addr, arg_offset); + args[i].stack = gen_rtx (MEM, args[i].mode, addr); + + if (GET_CODE (slot_offset) == CONST_INT) + addr = plus_constant (arg_reg, INTVAL (slot_offset)); + else + addr = gen_rtx (PLUS, Pmode, arg_reg, slot_offset); + + addr = plus_constant (addr, arg_offset); + args[i].stack_slot = gen_rtx (MEM, args[i].mode, addr); + } + } + +#ifdef PUSH_ARGS_REVERSED +#ifdef STACK_BOUNDARY + /* If we push args individually in reverse order, perform stack alignment + before the first push (the last arg). */ + if (argblock == 0) + anti_adjust_stack (GEN_INT (args_size.constant + - original_args_size.constant)); +#endif +#endif + + /* Don't try to defer pops if preallocating, not even from the first arg, + since ARGBLOCK probably refers to the SP. */ + if (argblock) + NO_DEFER_POP; + + /* Get the function to call, in the form of RTL. */ + if (fndecl) + /* Get a SYMBOL_REF rtx for the function address. */ + funexp = XEXP (DECL_RTL (fndecl), 0); + else + /* Generate an rtx (probably a pseudo-register) for the address. */ + { + funexp = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0); + free_temp_slots (); /* FUNEXP can't be BLKmode */ + emit_queue (); + } + + /* Figure out the register where the value, if any, will come back. */ + valreg = 0; + if (TYPE_MODE (TREE_TYPE (exp)) != VOIDmode + && ! structure_value_addr) + { + if (pcc_struct_value) + valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)), + fndecl); + else + valreg = hard_function_value (TREE_TYPE (exp), fndecl); + } + + /* Precompute all register parameters. It isn't safe to compute anything + once we have started filling any specific hard regs. */ + reg_parm_seen = 0; + for (i = 0; i < num_actuals; i++) + if (args[i].reg != 0 && ! args[i].pass_on_stack) + { + reg_parm_seen = 1; + + if (args[i].value == 0) + { + args[i].value = expand_expr (args[i].tree_value, NULL_RTX, + VOIDmode, 0); + preserve_temp_slots (args[i].value); + free_temp_slots (); + + /* ANSI doesn't require a sequence point here, + but PCC has one, so this will avoid some problems. */ + emit_queue (); + } + + /* If we are to promote the function arg to a wider mode, + do it now. */ + + if (GET_MODE (args[i].value) != VOIDmode + && GET_MODE (args[i].value) != args[i].mode) + args[i].value = convert_to_mode (args[i].mode, args[i].value, + args[i].unsignedp); + } + +#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE) + /* The argument list is the property of the called routine and it + may clobber it. If the fixed area has been used for previous + parameters, we must save and restore it. + + Here we compute the boundary of the that needs to be saved, if any. */ + +#ifdef ARGS_GROW_DOWNWARD + for (i = 0; i < reg_parm_stack_space + 1; i++) +#else + for (i = 0; i < reg_parm_stack_space; i++) +#endif + { + if (i >= highest_outgoing_arg_in_use + || stack_usage_map[i] == 0) + continue; + + if (low_to_save == -1) + low_to_save = i; + + high_to_save = i; + } + + if (low_to_save >= 0) + { + int num_to_save = high_to_save - low_to_save + 1; + enum machine_mode save_mode + = mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1); + rtx stack_area; + + /* If we don't have the required alignment, must do this in BLKmode. */ + if ((low_to_save & (MIN (GET_MODE_SIZE (save_mode), + BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1))) + save_mode = BLKmode; + + stack_area = gen_rtx (MEM, save_mode, + memory_address (save_mode, + +#ifdef ARGS_GROW_DOWNWARD + plus_constant (argblock, + - high_to_save) +#else + plus_constant (argblock, + low_to_save) +#endif + )); + if (save_mode == BLKmode) + { + save_area = assign_stack_temp (BLKmode, num_to_save, 1); + emit_block_move (validize_mem (save_area), stack_area, + GEN_INT (num_to_save), + PARM_BOUNDARY / BITS_PER_UNIT); + } + else + { + save_area = gen_reg_rtx (save_mode); + emit_move_insn (save_area, stack_area); + } + } +#endif + + + /* Now store (and compute if necessary) all non-register parms. + These come before register parms, since they can require block-moves, + which could clobber the registers used for register parms. + Parms which have partial registers are not stored here, + but we do preallocate space here if they want that. */ + + for (i = 0; i < num_actuals; i++) + if (args[i].reg == 0 || args[i].pass_on_stack) + store_one_arg (&args[i], argblock, may_be_alloca, + args_size.var != 0, fndecl, reg_parm_stack_space); + +#ifdef STRICT_ALIGNMENT + /* If we have a parm that is passed in registers but not in memory + and whose alignment does not permit a direct copy into registers, + make a group of pseudos that correspond to each register that we + will later fill. */ + + for (i = 0; i < num_actuals; i++) + if (args[i].reg != 0 && ! args[i].pass_on_stack + && args[i].mode == BLKmode + && (TYPE_ALIGN (TREE_TYPE (args[i].tree_value)) + < MIN (BIGGEST_ALIGNMENT, BITS_PER_WORD))) + { + int bytes = int_size_in_bytes (TREE_TYPE (args[i].tree_value)); + + args[i].n_aligned_regs + = args[i].partial ? args[i].partial + : (bytes + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD; + + args[i].aligned_regs = (rtx *) alloca (sizeof (rtx) + * args[i].n_aligned_regs); + + for (j = 0; j < args[i].n_aligned_regs; j++) + { + rtx reg = gen_reg_rtx (word_mode); + rtx word = operand_subword_force (args[i].value, j, BLKmode); + int bitsize = TYPE_ALIGN (TREE_TYPE (args[i].tree_value)); + int bitpos; + + args[i].aligned_regs[j] = reg; + + /* Clobber REG and move each partword into it. Ensure we don't + go past the end of the structure. Note that the loop below + works because we've already verified that padding + and endianness are compatible. */ + + emit_insn (gen_rtx (CLOBBER, VOIDmode, reg)); + + for (bitpos = 0; + bitpos < BITS_PER_WORD && bytes > 0; + bitpos += bitsize, bytes -= bitsize / BITS_PER_UNIT) + { + int xbitpos = (BYTES_BIG_ENDIAN + ? BITS_PER_WORD - bitpos - bitsize + : bitpos); + + store_bit_field (reg, bitsize, xbitpos, word_mode, + extract_bit_field (word, bitsize, xbitpos, 1, + NULL_RTX, word_mode, + word_mode, + bitsize / BITS_PER_UNIT, + BITS_PER_WORD), + bitsize / BITS_PER_UNIT, BITS_PER_WORD); + } + } + } +#endif + + /* Now store any partially-in-registers parm. + This is the last place a block-move can happen. */ + if (reg_parm_seen) + for (i = 0; i < num_actuals; i++) + if (args[i].partial != 0 && ! args[i].pass_on_stack) + store_one_arg (&args[i], argblock, may_be_alloca, + args_size.var != 0, fndecl, reg_parm_stack_space); + +#ifndef PUSH_ARGS_REVERSED +#ifdef STACK_BOUNDARY + /* If we pushed args in forward order, perform stack alignment + after pushing the last arg. */ + if (argblock == 0) + anti_adjust_stack (GEN_INT (args_size.constant + - original_args_size.constant)); +#endif +#endif + + /* If register arguments require space on the stack and stack space + was not preallocated, allocate stack space here for arguments + passed in registers. */ +#if ! defined(ALLOCATE_OUTGOING_ARGS) && defined(OUTGOING_REG_PARM_STACK_SPACE) + if (must_preallocate == 0 && reg_parm_stack_space > 0) + anti_adjust_stack (GEN_INT (reg_parm_stack_space)); +#endif + + /* Pass the function the address in which to return a structure value. */ + if (structure_value_addr && ! structure_value_addr_parm) + { + emit_move_insn (struct_value_rtx, + force_reg (Pmode, + force_operand (structure_value_addr, + NULL_RTX))); + if (GET_CODE (struct_value_rtx) == REG) + { + push_to_sequence (use_insns); + emit_insn (gen_rtx (USE, VOIDmode, struct_value_rtx)); + use_insns = get_insns (); + end_sequence (); + } + } + + /* Now do the register loads required for any wholly-register parms or any + parms which are passed both on the stack and in a register. Their + expressions were already evaluated. + + Mark all register-parms as living through the call, putting these USE + insns in a list headed by USE_INSNS. */ + + for (i = 0; i < num_actuals; i++) + { + rtx list = args[i].reg; + int partial = args[i].partial; + + while (list) + { + rtx reg; + int nregs; + + /* Process each register that needs to get this arg. */ + if (GET_CODE (list) == EXPR_LIST) + reg = XEXP (list, 0), list = XEXP (list, 1); + else + reg = list, list = 0; + + /* Set to non-zero if must move a word at a time, even if just one + word (e.g, partial == 1 && mode == DFmode). Set to zero if + we just use a normal move insn. */ + nregs = (partial ? partial + : (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode + ? ((int_size_in_bytes (TREE_TYPE (args[i].tree_value)) + + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD) + : 0)); + + /* If simple case, just do move. If normal partial, store_one_arg + has already loaded the register for us. In all other cases, + load the register(s) from memory. */ + + if (nregs == 0) + emit_move_insn (reg, args[i].value); + +#ifdef STRICT_ALIGNMENT + /* If we have pre-computed the values to put in the registers in + the case of non-aligned structures, copy them in now. */ + + else if (args[i].n_aligned_regs != 0) + for (j = 0; j < args[i].n_aligned_regs; j++) + emit_move_insn (gen_rtx (REG, word_mode, REGNO (reg) + j), + args[i].aligned_regs[j]); +#endif + + else if (args[i].partial == 0 || args[i].pass_on_stack) + move_block_to_reg (REGNO (reg), + validize_mem (args[i].value), nregs, + args[i].mode); + + push_to_sequence (use_insns); + if (nregs == 0) + emit_insn (gen_rtx (USE, VOIDmode, reg)); + else + use_regs (REGNO (reg), nregs); + use_insns = get_insns (); + end_sequence (); + + /* PARTIAL referred only to the first register, so clear it for the + next time. */ + partial = 0; + } + } + + /* Perform postincrements before actually calling the function. */ + emit_queue (); + + /* All arguments and registers used for the call must be set up by now! */ + + funexp = prepare_call_address (funexp, fndecl, &use_insns); + + /* Generate the actual call instruction. */ + emit_call_1 (funexp, funtype, args_size.constant, struct_value_size, + FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1), + valreg, old_inhibit_defer_pop, use_insns, is_const); + + /* If call is cse'able, make appropriate pair of reg-notes around it. + Test valreg so we don't crash; may safely ignore `const' + if return type is void. */ + if (is_const && valreg != 0) + { + rtx note = 0; + rtx temp = gen_reg_rtx (GET_MODE (valreg)); + rtx insns; + + /* Construct an "equal form" for the value which mentions all the + arguments in order as well as the function name. */ +#ifdef PUSH_ARGS_REVERSED + for (i = 0; i < num_actuals; i++) + note = gen_rtx (EXPR_LIST, VOIDmode, args[i].initial_value, note); +#else + for (i = num_actuals - 1; i >= 0; i--) + note = gen_rtx (EXPR_LIST, VOIDmode, args[i].initial_value, note); +#endif + note = gen_rtx (EXPR_LIST, VOIDmode, funexp, note); + + insns = get_insns (); + end_sequence (); + + emit_libcall_block (insns, temp, valreg, note); + + valreg = temp; + } + + /* For calls to `setjmp', etc., inform flow.c it should complain + if nonvolatile values are live. */ + + if (returns_twice) + { + emit_note (name, NOTE_INSN_SETJMP); + current_function_calls_setjmp = 1; + } + + if (is_longjmp) + current_function_calls_longjmp = 1; + + /* Notice functions that cannot return. + If optimizing, insns emitted below will be dead. + If not optimizing, they will exist, which is useful + if the user uses the `return' command in the debugger. */ + + if (is_volatile || is_longjmp) + emit_barrier (); + + /* If value type not void, return an rtx for the value. */ + + /* If there are cleanups to be called, don't use a hard reg as target. */ + if (cleanups_this_call != old_cleanups + && target && REG_P (target) + && REGNO (target) < FIRST_PSEUDO_REGISTER) + target = 0; + + if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode + || ignore) + { + target = const0_rtx; + } + else if (structure_value_addr) + { + if (target == 0 || GET_CODE (target) != MEM) + { + target = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), + memory_address (TYPE_MODE (TREE_TYPE (exp)), + structure_value_addr)); + MEM_IN_STRUCT_P (target) + = (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE + || TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE + || TREE_CODE (TREE_TYPE (exp)) == QUAL_UNION_TYPE); + } + } + else if (pcc_struct_value) + { + if (target == 0) + { + /* We used leave the value in the location that it is + returned in, but that causes problems if it is used more + than once in one expression. Rather than trying to track + when a copy is required, we always copy when TARGET is + not specified. This calling sequence is only used on + a few machines and TARGET is usually nonzero. */ + if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode) + { + target = assign_stack_temp (BLKmode, + int_size_in_bytes (TREE_TYPE (exp)), + 0); + + /* Save this temp slot around the pop below. */ + preserve_temp_slots (target); + } + else + target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp))); + } + + if (TYPE_MODE (TREE_TYPE (exp)) != BLKmode) + emit_move_insn (target, gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), + copy_to_reg (valreg))); + else + emit_block_move (target, gen_rtx (MEM, BLKmode, copy_to_reg (valreg)), + expr_size (exp), + TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); + } + else if (target && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp)) + && GET_MODE (target) == GET_MODE (valreg)) + /* TARGET and VALREG cannot be equal at this point because the latter + would not have REG_FUNCTION_VALUE_P true, while the former would if + it were referring to the same register. + + If they refer to the same register, this move will be a no-op, except + when function inlining is being done. */ + emit_move_insn (target, valreg); + else + target = copy_to_reg (valreg); + +#ifdef PROMOTE_FUNCTION_RETURN + /* If we promoted this return value, make the proper SUBREG. TARGET + might be const0_rtx here, so be careful. */ + if (GET_CODE (target) == REG + && GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp))) + { + enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp)); + int unsignedp = TREE_UNSIGNED (TREE_TYPE (exp)); + + if (TREE_CODE (TREE_TYPE (exp)) == INTEGER_TYPE + || TREE_CODE (TREE_TYPE (exp)) == ENUMERAL_TYPE + || TREE_CODE (TREE_TYPE (exp)) == BOOLEAN_TYPE + || TREE_CODE (TREE_TYPE (exp)) == CHAR_TYPE + || TREE_CODE (TREE_TYPE (exp)) == REAL_TYPE + || TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE + || TREE_CODE (TREE_TYPE (exp)) == OFFSET_TYPE) + { + PROMOTE_MODE (mode, unsignedp, TREE_TYPE (exp)); + } + + /* If we didn't promote as expected, something is wrong. */ + if (mode != GET_MODE (target)) + abort (); + + target = gen_rtx (SUBREG, TYPE_MODE (TREE_TYPE (exp)), target, 0); + SUBREG_PROMOTED_VAR_P (target) = 1; + SUBREG_PROMOTED_UNSIGNED_P (target) = unsignedp; + } +#endif + + /* Perform all cleanups needed for the arguments of this call + (i.e. destructors in C++). */ + expand_cleanups_to (old_cleanups); + + /* If size of args is variable or this was a constructor call for a stack + argument, restore saved stack-pointer value. */ + + if (old_stack_level) + { + emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX); + pending_stack_adjust = old_pending_adj; +#ifdef ACCUMULATE_OUTGOING_ARGS + stack_arg_under_construction = old_stack_arg_under_construction; + highest_outgoing_arg_in_use = initial_highest_arg_in_use; + stack_usage_map = initial_stack_usage_map; +#endif + } +#ifdef ACCUMULATE_OUTGOING_ARGS + else + { +#ifdef REG_PARM_STACK_SPACE + if (save_area) + { + enum machine_mode save_mode = GET_MODE (save_area); + rtx stack_area + = gen_rtx (MEM, save_mode, + memory_address (save_mode, +#ifdef ARGS_GROW_DOWNWARD + plus_constant (argblock, - high_to_save) +#else + plus_constant (argblock, low_to_save) +#endif + )); + + if (save_mode != BLKmode) + emit_move_insn (stack_area, save_area); + else + emit_block_move (stack_area, validize_mem (save_area), + GEN_INT (high_to_save - low_to_save + 1), + PARM_BOUNDARY / BITS_PER_UNIT); + } +#endif + + /* If we saved any argument areas, restore them. */ + for (i = 0; i < num_actuals; i++) + if (args[i].save_area) + { + enum machine_mode save_mode = GET_MODE (args[i].save_area); + rtx stack_area + = gen_rtx (MEM, save_mode, + memory_address (save_mode, + XEXP (args[i].stack_slot, 0))); + + if (save_mode != BLKmode) + emit_move_insn (stack_area, args[i].save_area); + else + emit_block_move (stack_area, validize_mem (args[i].save_area), + GEN_INT (args[i].size.constant), + PARM_BOUNDARY / BITS_PER_UNIT); + } + + highest_outgoing_arg_in_use = initial_highest_arg_in_use; + stack_usage_map = initial_stack_usage_map; + } +#endif + + /* If this was alloca, record the new stack level for nonlocal gotos. + Check for the handler slots since we might not have a save area + for non-local gotos. */ + + if (may_be_alloca && nonlocal_goto_handler_slot != 0) + emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX); + + pop_temp_slots (); + + return target; +} + +/* Output a library call to function FUN (a SYMBOL_REF rtx) + (emitting the queue unless NO_QUEUE is nonzero), + for a value of mode OUTMODE, + with NARGS different arguments, passed as alternating rtx values + and machine_modes to convert them to. + The rtx values should have been passed through protect_from_queue already. + + NO_QUEUE will be true if and only if the library call is a `const' call + which will be enclosed in REG_LIBCALL/REG_RETVAL notes; it is equivalent + to the variable is_const in expand_call. + + NO_QUEUE must be true for const calls, because if it isn't, then + any pending increment will be emitted between REG_LIBCALL/REG_RETVAL notes, + and will be lost if the libcall sequence is optimized away. + + NO_QUEUE must be false for non-const calls, because if it isn't, the + call insn will have its CONST_CALL_P bit set, and it will be incorrectly + optimized. For instance, the instruction scheduler may incorrectly + move memory references across the non-const call. */ + +void +emit_library_call (va_alist) + va_dcl +{ + va_list p; + /* Total size in bytes of all the stack-parms scanned so far. */ + struct args_size args_size; + /* Size of arguments before any adjustments (such as rounding). */ + struct args_size original_args_size; + register int argnum; + enum machine_mode outmode; + int nargs; + rtx fun; + rtx orgfun; + int inc; + int count; + rtx argblock = 0; + CUMULATIVE_ARGS args_so_far; + struct arg { rtx value; enum machine_mode mode; rtx reg; int partial; + struct args_size offset; struct args_size size; }; + struct arg *argvec; + int old_inhibit_defer_pop = inhibit_defer_pop; + int no_queue = 0; + rtx use_insns; + /* library calls are never indirect calls. */ + int current_call_is_indirect = 0; + + va_start (p); + orgfun = fun = va_arg (p, rtx); + no_queue = va_arg (p, int); + outmode = va_arg (p, enum machine_mode); + nargs = va_arg (p, int); + + /* Copy all the libcall-arguments out of the varargs data + and into a vector ARGVEC. + + Compute how to pass each argument. We only support a very small subset + of the full argument passing conventions to limit complexity here since + library functions shouldn't have many args. */ + + argvec = (struct arg *) alloca (nargs * sizeof (struct arg)); + + INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun); + + args_size.constant = 0; + args_size.var = 0; + + for (count = 0; count < nargs; count++) + { + rtx val = va_arg (p, rtx); + enum machine_mode mode = va_arg (p, enum machine_mode); + + /* We cannot convert the arg value to the mode the library wants here; + must do it earlier where we know the signedness of the arg. */ + if (mode == BLKmode + || (GET_MODE (val) != mode && GET_MODE (val) != VOIDmode)) + abort (); + + /* On some machines, there's no way to pass a float to a library fcn. + Pass it as a double instead. */ +#ifdef LIBGCC_NEEDS_DOUBLE + if (LIBGCC_NEEDS_DOUBLE && mode == SFmode) + val = convert_to_mode (DFmode, val, 0), mode = DFmode; +#endif + + /* There's no need to call protect_from_queue, because + either emit_move_insn or emit_push_insn will do that. */ + + /* Make sure it is a reasonable operand for a move or push insn. */ + if (GET_CODE (val) != REG && GET_CODE (val) != MEM + && ! (CONSTANT_P (val) && LEGITIMATE_CONSTANT_P (val))) + val = force_operand (val, NULL_RTX); + + argvec[count].value = val; + argvec[count].mode = mode; + +#ifdef FUNCTION_ARG_PASS_BY_REFERENCE + if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, mode, NULL_TREE, 1)) + abort (); +#endif + + argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1); + if (argvec[count].reg && GET_CODE (argvec[count].reg) == EXPR_LIST) + abort (); +#ifdef FUNCTION_ARG_PARTIAL_NREGS + argvec[count].partial + = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, NULL_TREE, 1); +#else + argvec[count].partial = 0; +#endif + + locate_and_pad_parm (mode, NULL_TREE, + argvec[count].reg && argvec[count].partial == 0, + NULL_TREE, &args_size, &argvec[count].offset, + &argvec[count].size); + + if (argvec[count].size.var) + abort (); + +#ifndef REG_PARM_STACK_SPACE + if (argvec[count].partial) + argvec[count].size.constant -= argvec[count].partial * UNITS_PER_WORD; +#endif + + if (argvec[count].reg == 0 || argvec[count].partial != 0 +#ifdef REG_PARM_STACK_SPACE + || 1 +#endif + ) + args_size.constant += argvec[count].size.constant; + +#ifdef ACCUMULATE_OUTGOING_ARGS + /* If this arg is actually passed on the stack, it might be + clobbering something we already put there (this library call might + be inside the evaluation of an argument to a function whose call + requires the stack). This will only occur when the library call + has sufficient args to run out of argument registers. Abort in + this case; if this ever occurs, code must be added to save and + restore the arg slot. */ + + if (argvec[count].reg == 0 || argvec[count].partial != 0) + abort (); +#endif + + FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree)0, 1); + } + va_end (p); + + /* If this machine requires an external definition for library + functions, write one out. */ + assemble_external_libcall (fun); + + original_args_size = args_size; +#ifdef STACK_BOUNDARY + args_size.constant = (((args_size.constant + (STACK_BYTES - 1)) + / STACK_BYTES) * STACK_BYTES); +#endif + +#ifdef REG_PARM_STACK_SPACE + args_size.constant = MAX (args_size.constant, + REG_PARM_STACK_SPACE (NULL_TREE)); +#ifndef OUTGOING_REG_PARM_STACK_SPACE + args_size.constant -= REG_PARM_STACK_SPACE (NULL_TREE); +#endif +#endif + +#ifdef ACCUMULATE_OUTGOING_ARGS + if (args_size.constant > current_function_outgoing_args_size) + current_function_outgoing_args_size = args_size.constant; + args_size.constant = 0; +#endif + +#ifndef PUSH_ROUNDING + argblock = push_block (GEN_INT (args_size.constant), 0, 0); +#endif + +#ifdef PUSH_ARGS_REVERSED +#ifdef STACK_BOUNDARY + /* If we push args individually in reverse order, perform stack alignment + before the first push (the last arg). */ + if (argblock == 0) + anti_adjust_stack (GEN_INT (args_size.constant + - original_args_size.constant)); +#endif +#endif + +#ifdef PUSH_ARGS_REVERSED + inc = -1; + argnum = nargs - 1; +#else + inc = 1; + argnum = 0; +#endif + + /* Push the args that need to be pushed. */ + + for (count = 0; count < nargs; count++, argnum += inc) + { + register enum machine_mode mode = argvec[argnum].mode; + register rtx val = argvec[argnum].value; + rtx reg = argvec[argnum].reg; + int partial = argvec[argnum].partial; + + if (! (reg != 0 && partial == 0)) + emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0, + argblock, GEN_INT (argvec[count].offset.constant)); + NO_DEFER_POP; + } + +#ifndef PUSH_ARGS_REVERSED +#ifdef STACK_BOUNDARY + /* If we pushed args in forward order, perform stack alignment + after pushing the last arg. */ + if (argblock == 0) + anti_adjust_stack (GEN_INT (args_size.constant + - original_args_size.constant)); +#endif +#endif + +#ifdef PUSH_ARGS_REVERSED + argnum = nargs - 1; +#else + argnum = 0; +#endif + + /* Now load any reg parms into their regs. */ + + for (count = 0; count < nargs; count++, argnum += inc) + { + register enum machine_mode mode = argvec[argnum].mode; + register rtx val = argvec[argnum].value; + rtx reg = argvec[argnum].reg; + int partial = argvec[argnum].partial; + + if (reg != 0 && partial == 0) + emit_move_insn (reg, val); + NO_DEFER_POP; + } + + /* For version 1.37, try deleting this entirely. */ + if (! no_queue) + emit_queue (); + + /* Any regs containing parms remain in use through the call. */ + start_sequence (); + for (count = 0; count < nargs; count++) + if (argvec[count].reg != 0) + emit_insn (gen_rtx (USE, VOIDmode, argvec[count].reg)); + + use_insns = get_insns (); + end_sequence (); + + fun = prepare_call_address (fun, NULL_TREE, &use_insns); + + /* Don't allow popping to be deferred, since then + cse'ing of library calls could delete a call and leave the pop. */ + NO_DEFER_POP; + + /* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which + will set inhibit_defer_pop to that value. */ + + emit_call_1 (fun, get_identifier (XSTR (orgfun, 0)), args_size.constant, 0, + FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1), + outmode != VOIDmode ? hard_libcall_value (outmode) : NULL_RTX, + old_inhibit_defer_pop + 1, use_insns, no_queue); + + /* Now restore inhibit_defer_pop to its actual original value. */ + OK_DEFER_POP; +} + +/* Like emit_library_call except that an extra argument, VALUE, + comes second and says where to store the result. + (If VALUE is zero, the result comes in the function value register.) */ + +void +emit_library_call_value (va_alist) + va_dcl +{ + va_list p; + /* Total size in bytes of all the stack-parms scanned so far. */ + struct args_size args_size; + /* Size of arguments before any adjustments (such as rounding). */ + struct args_size original_args_size; + register int argnum; + enum machine_mode outmode; + int nargs; + rtx fun; + rtx orgfun; + int inc; + int count; + rtx argblock = 0; + CUMULATIVE_ARGS args_so_far; + struct arg { rtx value; enum machine_mode mode; rtx reg; int partial; + struct args_size offset; struct args_size size; }; + struct arg *argvec; + int old_inhibit_defer_pop = inhibit_defer_pop; + int no_queue = 0; + rtx use_insns; + rtx value; + rtx mem_value = 0; + /* library calls are never indirect calls. */ + int current_call_is_indirect = 0; + + va_start (p); + orgfun = fun = va_arg (p, rtx); + value = va_arg (p, rtx); + no_queue = va_arg (p, int); + outmode = va_arg (p, enum machine_mode); + nargs = va_arg (p, int); + + /* If this kind of value comes back in memory, + decide where in memory it should come back. */ + if (RETURN_IN_MEMORY (type_for_mode (outmode, 0))) + { + if (GET_CODE (value) == MEM) + mem_value = value; + else + mem_value = assign_stack_temp (outmode, GET_MODE_SIZE (outmode), 0); + } + + /* ??? Unfinished: must pass the memory address as an argument. */ + + /* Copy all the libcall-arguments out of the varargs data + and into a vector ARGVEC. + + Compute how to pass each argument. We only support a very small subset + of the full argument passing conventions to limit complexity here since + library functions shouldn't have many args. */ + + argvec = (struct arg *) alloca ((nargs + 1) * sizeof (struct arg)); + + INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun); + + args_size.constant = 0; + args_size.var = 0; + + count = 0; + + /* If there's a structure value address to be passed, + either pass it in the special place, or pass it as an extra argument. */ + if (mem_value) + { + rtx addr = XEXP (mem_value, 0); + + if (! struct_value_rtx) + { + nargs++; + + /* Make sure it is a reasonable operand for a move or push insn. */ + if (GET_CODE (addr) != REG && GET_CODE (addr) != MEM + && ! (CONSTANT_P (addr) && LEGITIMATE_CONSTANT_P (addr))) + addr = force_operand (addr, NULL_RTX); + + argvec[count].value = addr; + argvec[count].mode = outmode; + argvec[count].partial = 0; + + argvec[count].reg = FUNCTION_ARG (args_so_far, outmode, NULL_TREE, 1); +#ifdef FUNCTION_ARG_PARTIAL_NREGS + if (FUNCTION_ARG_PARTIAL_NREGS (args_so_far, outmode, NULL_TREE, 1)) + abort (); +#endif + + locate_and_pad_parm (outmode, NULL_TREE, + argvec[count].reg && argvec[count].partial == 0, + NULL_TREE, &args_size, &argvec[count].offset, + &argvec[count].size); + + + if (argvec[count].reg == 0 || argvec[count].partial != 0 +#ifdef REG_PARM_STACK_SPACE + || 1 +#endif + ) + args_size.constant += argvec[count].size.constant; + + FUNCTION_ARG_ADVANCE (args_so_far, outmode, (tree)0, 1); + } + } + + for (; count < nargs; count++) + { + rtx val = va_arg (p, rtx); + enum machine_mode mode = va_arg (p, enum machine_mode); + + /* We cannot convert the arg value to the mode the library wants here; + must do it earlier where we know the signedness of the arg. */ + if (mode == BLKmode + || (GET_MODE (val) != mode && GET_MODE (val) != VOIDmode)) + abort (); + + /* On some machines, there's no way to pass a float to a library fcn. + Pass it as a double instead. */ +#ifdef LIBGCC_NEEDS_DOUBLE + if (LIBGCC_NEEDS_DOUBLE && mode == SFmode) + val = convert_to_mode (DFmode, val, 0), mode = DFmode; +#endif + + /* There's no need to call protect_from_queue, because + either emit_move_insn or emit_push_insn will do that. */ + + /* Make sure it is a reasonable operand for a move or push insn. */ + if (GET_CODE (val) != REG && GET_CODE (val) != MEM + && ! (CONSTANT_P (val) && LEGITIMATE_CONSTANT_P (val))) + val = force_operand (val, NULL_RTX); + + argvec[count].value = val; + argvec[count].mode = mode; + +#ifdef FUNCTION_ARG_PASS_BY_REFERENCE + if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, mode, NULL_TREE, 1)) + abort (); +#endif + + argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1); + if (argvec[count].reg && GET_CODE (argvec[count].reg) == EXPR_LIST) + abort (); +#ifdef FUNCTION_ARG_PARTIAL_NREGS + argvec[count].partial + = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, NULL_TREE, 1); +#else + argvec[count].partial = 0; +#endif + + locate_and_pad_parm (mode, NULL_TREE, + argvec[count].reg && argvec[count].partial == 0, + NULL_TREE, &args_size, &argvec[count].offset, + &argvec[count].size); + + if (argvec[count].size.var) + abort (); + +#ifndef REG_PARM_STACK_SPACE + if (argvec[count].partial) + argvec[count].size.constant -= argvec[count].partial * UNITS_PER_WORD; +#endif + + if (argvec[count].reg == 0 || argvec[count].partial != 0 +#ifdef REG_PARM_STACK_SPACE + || 1 +#endif + ) + args_size.constant += argvec[count].size.constant; + +#ifdef ACCUMULATE_OUTGOING_ARGS + /* If this arg is actually passed on the stack, it might be + clobbering something we already put there (this library call might + be inside the evaluation of an argument to a function whose call + requires the stack). This will only occur when the library call + has sufficient args to run out of argument registers. Abort in + this case; if this ever occurs, code must be added to save and + restore the arg slot. */ + + if (argvec[count].reg == 0 || argvec[count].partial != 0) + abort (); +#endif + + FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree)0, 1); + } + va_end (p); + + /* If this machine requires an external definition for library + functions, write one out. */ + assemble_external_libcall (fun); + + original_args_size = args_size; +#ifdef STACK_BOUNDARY + args_size.constant = (((args_size.constant + (STACK_BYTES - 1)) + / STACK_BYTES) * STACK_BYTES); +#endif + +#ifdef REG_PARM_STACK_SPACE + args_size.constant = MAX (args_size.constant, + REG_PARM_STACK_SPACE (NULL_TREE)); +#ifndef OUTGOING_REG_PARM_STACK_SPACE + args_size.constant -= REG_PARM_STACK_SPACE (NULL_TREE); +#endif +#endif + +#ifdef ACCUMULATE_OUTGOING_ARGS + if (args_size.constant > current_function_outgoing_args_size) + current_function_outgoing_args_size = args_size.constant; + args_size.constant = 0; +#endif + +#ifndef PUSH_ROUNDING + argblock = push_block (GEN_INT (args_size.constant), 0, 0); +#endif + +#ifdef PUSH_ARGS_REVERSED +#ifdef STACK_BOUNDARY + /* If we push args individually in reverse order, perform stack alignment + before the first push (the last arg). */ + if (argblock == 0) + anti_adjust_stack (GEN_INT (args_size.constant + - original_args_size.constant)); +#endif +#endif + +#ifdef PUSH_ARGS_REVERSED + inc = -1; + argnum = nargs - 1; +#else + inc = 1; + argnum = 0; +#endif + + /* Push the args that need to be pushed. */ + + for (count = 0; count < nargs; count++, argnum += inc) + { + register enum machine_mode mode = argvec[argnum].mode; + register rtx val = argvec[argnum].value; + rtx reg = argvec[argnum].reg; + int partial = argvec[argnum].partial; + + if (! (reg != 0 && partial == 0)) + emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0, + argblock, GEN_INT (argvec[count].offset.constant)); + NO_DEFER_POP; + } + +#ifndef PUSH_ARGS_REVERSED +#ifdef STACK_BOUNDARY + /* If we pushed args in forward order, perform stack alignment + after pushing the last arg. */ + if (argblock == 0) + anti_adjust_stack (GEN_INT (args_size.constant + - original_args_size.constant)); +#endif +#endif + +#ifdef PUSH_ARGS_REVERSED + argnum = nargs - 1; +#else + argnum = 0; +#endif + + /* Now load any reg parms into their regs. */ + + if (mem_value != 0 && struct_value_rtx != 0) + emit_move_insn (struct_value_rtx, XEXP (mem_value, 0)); + + for (count = 0; count < nargs; count++, argnum += inc) + { + register enum machine_mode mode = argvec[argnum].mode; + register rtx val = argvec[argnum].value; + rtx reg = argvec[argnum].reg; + int partial = argvec[argnum].partial; + + if (reg != 0 && partial == 0) + emit_move_insn (reg, val); + NO_DEFER_POP; + } + +#if 0 + /* For version 1.37, try deleting this entirely. */ + if (! no_queue) + emit_queue (); +#endif + + /* Any regs containing parms remain in use through the call. */ + start_sequence (); + for (count = 0; count < nargs; count++) + if (argvec[count].reg != 0) + emit_insn (gen_rtx (USE, VOIDmode, argvec[count].reg)); + + use_insns = get_insns (); + end_sequence (); + + fun = prepare_call_address (fun, NULL_TREE, &use_insns); + + /* Don't allow popping to be deferred, since then + cse'ing of library calls could delete a call and leave the pop. */ + NO_DEFER_POP; + + /* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which + will set inhibit_defer_pop to that value. */ + + emit_call_1 (fun, get_identifier (XSTR (orgfun, 0)), args_size.constant, 0, + FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1), + outmode != VOIDmode ? hard_libcall_value (outmode) : NULL_RTX, + old_inhibit_defer_pop + 1, use_insns, no_queue); + + /* Now restore inhibit_defer_pop to its actual original value. */ + OK_DEFER_POP; + + /* Copy the value to the right place. */ + if (outmode != VOIDmode) + { + if (mem_value) + { + if (value == 0) + value = hard_libcall_value (outmode); + if (value != mem_value) + emit_move_insn (value, mem_value); + } + else if (value != 0) + emit_move_insn (value, hard_libcall_value (outmode)); + } +} + +#if 0 +/* Return an rtx which represents a suitable home on the stack + given TYPE, the type of the argument looking for a home. + This is called only for BLKmode arguments. + + SIZE is the size needed for this target. + ARGS_ADDR is the address of the bottom of the argument block for this call. + OFFSET describes this parameter's offset into ARGS_ADDR. It is meaningless + if this machine uses push insns. */ + +static rtx +target_for_arg (type, size, args_addr, offset) + tree type; + rtx size; + rtx args_addr; + struct args_size offset; +{ + rtx target; + rtx offset_rtx = ARGS_SIZE_RTX (offset); + + /* We do not call memory_address if possible, + because we want to address as close to the stack + as possible. For non-variable sized arguments, + this will be stack-pointer relative addressing. */ + if (GET_CODE (offset_rtx) == CONST_INT) + target = plus_constant (args_addr, INTVAL (offset_rtx)); + else + { + /* I have no idea how to guarantee that this + will work in the presence of register parameters. */ + target = gen_rtx (PLUS, Pmode, args_addr, offset_rtx); + target = memory_address (QImode, target); + } + + return gen_rtx (MEM, BLKmode, target); +} +#endif + +/* Store a single argument for a function call + into the register or memory area where it must be passed. + *ARG describes the argument value and where to pass it. + + ARGBLOCK is the address of the stack-block for all the arguments, + or 0 on a machine where arguments are pushed individually. + + MAY_BE_ALLOCA nonzero says this could be a call to `alloca' + so must be careful about how the stack is used. + + VARIABLE_SIZE nonzero says that this was a variable-sized outgoing + argument stack. This is used if ACCUMULATE_OUTGOING_ARGS to indicate + that we need not worry about saving and restoring the stack. + + FNDECL is the declaration of the function we are calling. */ + +static void +store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl, + reg_parm_stack_space) + struct arg_data *arg; + rtx argblock; + int may_be_alloca; + int variable_size; + tree fndecl; + int reg_parm_stack_space; +{ + register tree pval = arg->tree_value; + rtx reg = 0; + int partial = 0; + int used = 0; + int i, lower_bound, upper_bound; + + if (TREE_CODE (pval) == ERROR_MARK) + return; + +#ifdef ACCUMULATE_OUTGOING_ARGS + /* If this is being stored into a pre-allocated, fixed-size, stack area, + save any previous data at that location. */ + if (argblock && ! variable_size && arg->stack) + { +#ifdef ARGS_GROW_DOWNWARD + /* stack_slot is negative, but we want to index stack_usage_map */ + /* with positive values. */ + if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS) + upper_bound = -INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1)) + 1; + else + abort (); + + lower_bound = upper_bound - arg->size.constant; +#else + if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS) + lower_bound = INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1)); + else + lower_bound = 0; + + upper_bound = lower_bound + arg->size.constant; +#endif + + for (i = lower_bound; i < upper_bound; i++) + if (stack_usage_map[i] +#ifdef REG_PARM_STACK_SPACE + /* Don't store things in the fixed argument area at this point; + it has already been saved. */ + && i > reg_parm_stack_space +#endif + ) + break; + + if (i != upper_bound) + { + /* We need to make a save area. See what mode we can make it. */ + enum machine_mode save_mode + = mode_for_size (arg->size.constant * BITS_PER_UNIT, MODE_INT, 1); + rtx stack_area + = gen_rtx (MEM, save_mode, + memory_address (save_mode, XEXP (arg->stack_slot, 0))); + + if (save_mode == BLKmode) + { + arg->save_area = assign_stack_temp (BLKmode, + arg->size.constant, 1); + emit_block_move (validize_mem (arg->save_area), stack_area, + GEN_INT (arg->size.constant), + PARM_BOUNDARY / BITS_PER_UNIT); + } + else + { + arg->save_area = gen_reg_rtx (save_mode); + emit_move_insn (arg->save_area, stack_area); + } + } + } +#endif + + /* If this isn't going to be placed on both the stack and in registers, + set up the register and number of words. */ + if (! arg->pass_on_stack) + reg = arg->reg, partial = arg->partial; + + if (reg != 0 && partial == 0) + /* Being passed entirely in a register. We shouldn't be called in + this case. */ + abort (); + +#ifdef STRICT_ALIGNMENT + /* If this arg needs special alignment, don't load the registers + here. */ + if (arg->n_aligned_regs != 0) + reg = 0; +#endif + + /* If this is being partially passed in a register, but multiple locations + are specified, we assume that the one partially used is the one that is + listed first. */ + if (reg && GET_CODE (reg) == EXPR_LIST) + reg = XEXP (reg, 0); + + /* If this is being passed partially in a register, we can't evaluate + it directly into its stack slot. Otherwise, we can. */ + if (arg->value == 0) + { +#ifdef ACCUMULATE_OUTGOING_ARGS + /* stack_arg_under_construction is nonzero if a function argument is + being evaluated directly into the outgoing argument list and + expand_call must take special action to preserve the argument list + if it is called recursively. + + For scalar function arguments stack_usage_map is sufficient to + determine which stack slots must be saved and restored. Scalar + arguments in general have pass_on_stack == 0. + + If this argument is initialized by a function which takes the + address of the argument (a C++ constructor or a C function + returning a BLKmode structure), then stack_usage_map is + insufficient and expand_call must push the stack around the + function call. Such arguments have pass_on_stack == 1. + + Note that it is always safe to set stack_arg_under_construction, + but this generates suboptimal code if set when not needed. */ + + if (arg->pass_on_stack) + stack_arg_under_construction++; +#endif + arg->value = expand_expr (pval, partial ? NULL_RTX : arg->stack, + VOIDmode, 0); + + /* If we are promoting object (or for any other reason) the mode + doesn't agree, convert the mode. */ + + if (GET_MODE (arg->value) != VOIDmode + && GET_MODE (arg->value) != arg->mode) + arg->value = convert_to_mode (arg->mode, arg->value, arg->unsignedp); + +#ifdef ACCUMULATE_OUTGOING_ARGS + if (arg->pass_on_stack) + stack_arg_under_construction--; +#endif + } + + /* Don't allow anything left on stack from computation + of argument to alloca. */ + if (may_be_alloca) + do_pending_stack_adjust (); + + if (arg->value == arg->stack) + /* If the value is already in the stack slot, we are done. */ + ; + else if (arg->mode != BLKmode) + { + register int size; + + /* Argument is a scalar, not entirely passed in registers. + (If part is passed in registers, arg->partial says how much + and emit_push_insn will take care of putting it there.) + + Push it, and if its size is less than the + amount of space allocated to it, + also bump stack pointer by the additional space. + Note that in C the default argument promotions + will prevent such mismatches. */ + + size = GET_MODE_SIZE (arg->mode); + /* Compute how much space the push instruction will push. + On many machines, pushing a byte will advance the stack + pointer by a halfword. */ +#ifdef PUSH_ROUNDING + size = PUSH_ROUNDING (size); +#endif + used = size; + + /* Compute how much space the argument should get: + round up to a multiple of the alignment for arguments. */ + if (none != FUNCTION_ARG_PADDING (arg->mode, TREE_TYPE (pval))) + used = (((size + PARM_BOUNDARY / BITS_PER_UNIT - 1) + / (PARM_BOUNDARY / BITS_PER_UNIT)) + * (PARM_BOUNDARY / BITS_PER_UNIT)); + + /* This isn't already where we want it on the stack, so put it there. + This can either be done with push or copy insns. */ + emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX, + 0, partial, reg, used - size, + argblock, ARGS_SIZE_RTX (arg->offset)); + } + else + { + /* BLKmode, at least partly to be pushed. */ + + register int excess; + rtx size_rtx; + + /* Pushing a nonscalar. + If part is passed in registers, PARTIAL says how much + and emit_push_insn will take care of putting it there. */ + + /* Round its size up to a multiple + of the allocation unit for arguments. */ + + if (arg->size.var != 0) + { + excess = 0; + size_rtx = ARGS_SIZE_RTX (arg->size); + } + else + { + /* PUSH_ROUNDING has no effect on us, because + emit_push_insn for BLKmode is careful to avoid it. */ + excess = (arg->size.constant - int_size_in_bytes (TREE_TYPE (pval)) + + partial * UNITS_PER_WORD); + size_rtx = expr_size (pval); + } + + emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx, + TYPE_ALIGN (TREE_TYPE (pval)) / BITS_PER_UNIT, partial, + reg, excess, argblock, ARGS_SIZE_RTX (arg->offset)); + } + + + /* Unless this is a partially-in-register argument, the argument is now + in the stack. + + ??? Note that this can change arg->value from arg->stack to + arg->stack_slot and it matters when they are not the same. + It isn't totally clear that this is correct in all cases. */ + if (partial == 0) + arg->value = arg->stack_slot; + + /* Once we have pushed something, pops can't safely + be deferred during the rest of the arguments. */ + NO_DEFER_POP; + + /* ANSI doesn't require a sequence point here, + but PCC has one, so this will avoid some problems. */ + emit_queue (); + + /* Free any temporary slots made in processing this argument. */ + free_temp_slots (); + +#ifdef ACCUMULATE_OUTGOING_ARGS + /* Now mark the segment we just used. */ + if (argblock && ! variable_size && arg->stack) + for (i = lower_bound; i < upper_bound; i++) + stack_usage_map[i] = 1; +#endif +} diff --git a/gnu/usr.bin/cc/lib/combine.c b/gnu/usr.bin/cc/lib/combine.c new file mode 100644 index 0000000000..7aa534879f --- /dev/null +++ b/gnu/usr.bin/cc/lib/combine.c @@ -0,0 +1,10025 @@ +/* Optimize by combining instructions for GNU compiler. + Copyright (C) 1987, 1988, 1992, 1993 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* This module is essentially the "combiner" phase of the U. of Arizona + Portable Optimizer, but redone to work on our list-structured + representation for RTL instead of their string representation. + + The LOG_LINKS of each insn identify the most recent assignment + to each REG used in the insn. It is a list of previous insns, + each of which contains a SET for a REG that is used in this insn + and not used or set in between. LOG_LINKs never cross basic blocks. + They were set up by the preceding pass (lifetime analysis). + + We try to combine each pair of insns joined by a logical link. + We also try to combine triples of insns A, B and C when + C has a link back to B and B has a link back to A. + + LOG_LINKS does not have links for use of the CC0. They don't + need to, because the insn that sets the CC0 is always immediately + before the insn that tests it. So we always regard a branch + insn as having a logical link to the preceding insn. The same is true + for an insn explicitly using CC0. + + We check (with use_crosses_set_p) to avoid combining in such a way + as to move a computation to a place where its value would be different. + + Combination is done by mathematically substituting the previous + insn(s) values for the regs they set into the expressions in + the later insns that refer to these regs. If the result is a valid insn + for our target machine, according to the machine description, + we install it, delete the earlier insns, and update the data flow + information (LOG_LINKS and REG_NOTES) for what we did. + + There are a few exceptions where the dataflow information created by + flow.c aren't completely updated: + + - reg_live_length is not updated + - reg_n_refs is not adjusted in the rare case when a register is + no longer required in a computation + - there are extremely rare cases (see distribute_regnotes) when a + REG_DEAD note is lost + - a LOG_LINKS entry that refers to an insn with multiple SETs may be + removed because there is no way to know which register it was + linking + + To simplify substitution, we combine only when the earlier insn(s) + consist of only a single assignment. To simplify updating afterward, + we never combine when a subroutine call appears in the middle. + + Since we do not represent assignments to CC0 explicitly except when that + is all an insn does, there is no LOG_LINKS entry in an insn that uses + the condition code for the insn that set the condition code. + Fortunately, these two insns must be consecutive. + Therefore, every JUMP_INSN is taken to have an implicit logical link + to the preceding insn. This is not quite right, since non-jumps can + also use the condition code; but in practice such insns would not + combine anyway. */ + +#include "config.h" +#include "gvarargs.h" +#include "rtl.h" +#include "flags.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "expr.h" +#include "basic-block.h" +#include "insn-config.h" +#include "insn-flags.h" +#include "insn-codes.h" +#include "insn-attr.h" +#include "recog.h" +#include "real.h" +#include + +/* It is not safe to use ordinary gen_lowpart in combine. + Use gen_lowpart_for_combine instead. See comments there. */ +#define gen_lowpart dont_use_gen_lowpart_you_dummy + +/* If byte loads either zero- or sign- extend, define BYTE_LOADS_EXTEND + for cases when we don't care which is true. Define LOAD_EXTEND to + be ZERO_EXTEND or SIGN_EXTEND, depending on which was defined. */ + +#ifdef BYTE_LOADS_ZERO_EXTEND +#define BYTE_LOADS_EXTEND +#define LOAD_EXTEND ZERO_EXTEND +#endif + +#ifdef BYTE_LOADS_SIGN_EXTEND +#define BYTE_LOADS_EXTEND +#define LOAD_EXTEND SIGN_EXTEND +#endif + +/* Number of attempts to combine instructions in this function. */ + +static int combine_attempts; + +/* Number of attempts that got as far as substitution in this function. */ + +static int combine_merges; + +/* Number of instructions combined with added SETs in this function. */ + +static int combine_extras; + +/* Number of instructions combined in this function. */ + +static int combine_successes; + +/* Totals over entire compilation. */ + +static int total_attempts, total_merges, total_extras, total_successes; + +/* Vector mapping INSN_UIDs to cuids. + The cuids are like uids but increase monotonically always. + Combine always uses cuids so that it can compare them. + But actually renumbering the uids, which we used to do, + proves to be a bad idea because it makes it hard to compare + the dumps produced by earlier passes with those from later passes. */ + +static int *uid_cuid; + +/* Get the cuid of an insn. */ + +#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)]) + +/* Maximum register number, which is the size of the tables below. */ + +static int combine_max_regno; + +/* Record last point of death of (hard or pseudo) register n. */ + +static rtx *reg_last_death; + +/* Record last point of modification of (hard or pseudo) register n. */ + +static rtx *reg_last_set; + +/* Record the cuid of the last insn that invalidated memory + (anything that writes memory, and subroutine calls, but not pushes). */ + +static int mem_last_set; + +/* Record the cuid of the last CALL_INSN + so we can tell whether a potential combination crosses any calls. */ + +static int last_call_cuid; + +/* When `subst' is called, this is the insn that is being modified + (by combining in a previous insn). The PATTERN of this insn + is still the old pattern partially modified and it should not be + looked at, but this may be used to examine the successors of the insn + to judge whether a simplification is valid. */ + +static rtx subst_insn; + +/* This is the lowest CUID that `subst' is currently dealing with. + get_last_value will not return a value if the register was set at or + after this CUID. If not for this mechanism, we could get confused if + I2 or I1 in try_combine were an insn that used the old value of a register + to obtain a new value. In that case, we might erroneously get the + new value of the register when we wanted the old one. */ + +static int subst_low_cuid; + +/* This is the value of undobuf.num_undo when we started processing this + substitution. This will prevent gen_rtx_combine from re-used a piece + from the previous expression. Doing so can produce circular rtl + structures. */ + +static int previous_num_undos; + +/* The next group of arrays allows the recording of the last value assigned + to (hard or pseudo) register n. We use this information to see if a + operation being processed is redundant given a prior operation performed + on the register. For example, an `and' with a constant is redundant if + all the zero bits are already known to be turned off. + + We use an approach similar to that used by cse, but change it in the + following ways: + + (1) We do not want to reinitialize at each label. + (2) It is useful, but not critical, to know the actual value assigned + to a register. Often just its form is helpful. + + Therefore, we maintain the following arrays: + + reg_last_set_value the last value assigned + reg_last_set_label records the value of label_tick when the + register was assigned + reg_last_set_table_tick records the value of label_tick when a + value using the register is assigned + reg_last_set_invalid set to non-zero when it is not valid + to use the value of this register in some + register's value + + To understand the usage of these tables, it is important to understand + the distinction between the value in reg_last_set_value being valid + and the register being validly contained in some other expression in the + table. + + Entry I in reg_last_set_value is valid if it is non-zero, and either + reg_n_sets[i] is 1 or reg_last_set_label[i] == label_tick. + + Register I may validly appear in any expression returned for the value + of another register if reg_n_sets[i] is 1. It may also appear in the + value for register J if reg_last_set_label[i] < reg_last_set_label[j] or + reg_last_set_invalid[j] is zero. + + If an expression is found in the table containing a register which may + not validly appear in an expression, the register is replaced by + something that won't match, (clobber (const_int 0)). + + reg_last_set_invalid[i] is set non-zero when register I is being assigned + to and reg_last_set_table_tick[i] == label_tick. */ + +/* Record last value assigned to (hard or pseudo) register n. */ + +static rtx *reg_last_set_value; + +/* Record the value of label_tick when the value for register n is placed in + reg_last_set_value[n]. */ + +static int *reg_last_set_label; + +/* Record the value of label_tick when an expression involving register n + is placed in reg_last_set_value. */ + +static int *reg_last_set_table_tick; + +/* Set non-zero if references to register n in expressions should not be + used. */ + +static char *reg_last_set_invalid; + +/* Incremented for each label. */ + +static int label_tick; + +/* Some registers that are set more than once and used in more than one + basic block are nevertheless always set in similar ways. For example, + a QImode register may be loaded from memory in two places on a machine + where byte loads zero extend. + + We record in the following array what we know about the nonzero + bits of a register, specifically which bits are known to be zero. + + If an entry is zero, it means that we don't know anything special. */ + +static unsigned HOST_WIDE_INT *reg_nonzero_bits; + +/* Mode used to compute significance in reg_nonzero_bits. It is the largest + integer mode that can fit in HOST_BITS_PER_WIDE_INT. */ + +static enum machine_mode nonzero_bits_mode; + +/* Nonzero if we know that a register has some leading bits that are always + equal to the sign bit. */ + +static char *reg_sign_bit_copies; + +/* Nonzero when reg_nonzero_bits and reg_sign_bit_copies can be safely used. + It is zero while computing them and after combine has completed. This + former test prevents propagating values based on previously set values, + which can be incorrect if a variable is modified in a loop. */ + +static int nonzero_sign_valid; + +/* These arrays are maintained in parallel with reg_last_set_value + and are used to store the mode in which the register was last set, + the bits that were known to be zero when it was last set, and the + number of sign bits copies it was known to have when it was last set. */ + +static enum machine_mode *reg_last_set_mode; +static unsigned HOST_WIDE_INT *reg_last_set_nonzero_bits; +static char *reg_last_set_sign_bit_copies; + +/* Record one modification to rtl structure + to be undone by storing old_contents into *where. + is_int is 1 if the contents are an int. */ + +struct undo +{ + int is_int; + union {rtx rtx; int i;} old_contents; + union {rtx *rtx; int *i;} where; +}; + +/* Record a bunch of changes to be undone, up to MAX_UNDO of them. + num_undo says how many are currently recorded. + + storage is nonzero if we must undo the allocation of new storage. + The value of storage is what to pass to obfree. + + other_insn is nonzero if we have modified some other insn in the process + of working on subst_insn. It must be verified too. */ + +#define MAX_UNDO 50 + +struct undobuf +{ + int num_undo; + char *storage; + struct undo undo[MAX_UNDO]; + rtx other_insn; +}; + +static struct undobuf undobuf; + +/* Substitute NEWVAL, an rtx expression, into INTO, a place in some + insn. The substitution can be undone by undo_all. If INTO is already + set to NEWVAL, do not record this change. Because computing NEWVAL might + also call SUBST, we have to compute it before we put anything into + the undo table. */ + +#define SUBST(INTO, NEWVAL) \ + do { rtx _new = (NEWVAL); \ + if (undobuf.num_undo < MAX_UNDO) \ + { \ + undobuf.undo[undobuf.num_undo].is_int = 0; \ + undobuf.undo[undobuf.num_undo].where.rtx = &INTO; \ + undobuf.undo[undobuf.num_undo].old_contents.rtx = INTO; \ + INTO = _new; \ + if (undobuf.undo[undobuf.num_undo].old_contents.rtx != INTO) \ + undobuf.num_undo++; \ + } \ + } while (0) + +/* Similar to SUBST, but NEWVAL is an int. INTO will normally be an XINT + expression. + Note that substitution for the value of a CONST_INT is not safe. */ + +#define SUBST_INT(INTO, NEWVAL) \ + do { if (undobuf.num_undo < MAX_UNDO) \ +{ \ + undobuf.undo[undobuf.num_undo].is_int = 1; \ + undobuf.undo[undobuf.num_undo].where.i = (int *) &INTO; \ + undobuf.undo[undobuf.num_undo].old_contents.i = INTO; \ + INTO = NEWVAL; \ + if (undobuf.undo[undobuf.num_undo].old_contents.i != INTO) \ + undobuf.num_undo++; \ + } \ + } while (0) + +/* Number of times the pseudo being substituted for + was found and replaced. */ + +static int n_occurrences; + +static void set_nonzero_bits_and_sign_copies (); +static void setup_incoming_promotions (); +static void move_deaths (); +rtx remove_death (); +static void record_value_for_reg (); +static void record_dead_and_set_regs (); +static int use_crosses_set_p (); +static rtx try_combine (); +static rtx *find_split_point (); +static rtx subst (); +static void undo_all (); +static int reg_dead_at_p (); +static rtx expand_compound_operation (); +static rtx expand_field_assignment (); +static rtx make_extraction (); +static int get_pos_from_mask (); +static rtx force_to_mode (); +static rtx known_cond (); +static rtx make_field_assignment (); +static rtx make_compound_operation (); +static rtx apply_distributive_law (); +static rtx simplify_and_const_int (); +static unsigned HOST_WIDE_INT nonzero_bits (); +static int num_sign_bit_copies (); +static int merge_outer_ops (); +static rtx simplify_shift_const (); +static int recog_for_combine (); +static rtx gen_lowpart_for_combine (); +static rtx gen_rtx_combine (); +static rtx gen_binary (); +static rtx gen_unary (); +static enum rtx_code simplify_comparison (); +static int reversible_comparison_p (); +static int get_last_value_validate (); +static rtx get_last_value (); +static void distribute_notes (); +static void distribute_links (); + +/* Main entry point for combiner. F is the first insn of the function. + NREGS is the first unused pseudo-reg number. */ + +void +combine_instructions (f, nregs) + rtx f; + int nregs; +{ + register rtx insn, next, prev; + register int i; + register rtx links, nextlinks; + + combine_attempts = 0; + combine_merges = 0; + combine_extras = 0; + combine_successes = 0; + undobuf.num_undo = previous_num_undos = 0; + + combine_max_regno = nregs; + + reg_last_death = (rtx *) alloca (nregs * sizeof (rtx)); + reg_last_set = (rtx *) alloca (nregs * sizeof (rtx)); + reg_last_set_value = (rtx *) alloca (nregs * sizeof (rtx)); + reg_last_set_table_tick = (int *) alloca (nregs * sizeof (int)); + reg_last_set_label = (int *) alloca (nregs * sizeof (int)); + reg_last_set_invalid = (char *) alloca (nregs * sizeof (char)); + reg_last_set_mode + = (enum machine_mode *) alloca (nregs * sizeof (enum machine_mode)); + reg_last_set_nonzero_bits + = (unsigned HOST_WIDE_INT *) alloca (nregs * sizeof (HOST_WIDE_INT)); + reg_last_set_sign_bit_copies + = (char *) alloca (nregs * sizeof (char)); + + reg_nonzero_bits + = (unsigned HOST_WIDE_INT *) alloca (nregs * sizeof (HOST_WIDE_INT)); + reg_sign_bit_copies = (char *) alloca (nregs * sizeof (char)); + + bzero (reg_last_death, nregs * sizeof (rtx)); + bzero (reg_last_set, nregs * sizeof (rtx)); + bzero (reg_last_set_value, nregs * sizeof (rtx)); + bzero (reg_last_set_table_tick, nregs * sizeof (int)); + bzero (reg_last_set_label, nregs * sizeof (int)); + bzero (reg_last_set_invalid, nregs * sizeof (char)); + bzero (reg_last_set_mode, nregs * sizeof (enum machine_mode)); + bzero (reg_last_set_nonzero_bits, nregs * sizeof (HOST_WIDE_INT)); + bzero (reg_last_set_sign_bit_copies, nregs * sizeof (char)); + bzero (reg_nonzero_bits, nregs * sizeof (HOST_WIDE_INT)); + bzero (reg_sign_bit_copies, nregs * sizeof (char)); + + init_recog_no_volatile (); + + /* Compute maximum uid value so uid_cuid can be allocated. */ + + for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) + if (INSN_UID (insn) > i) + i = INSN_UID (insn); + + uid_cuid = (int *) alloca ((i + 1) * sizeof (int)); + + nonzero_bits_mode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0); + + /* Don't use reg_nonzero_bits when computing it. This can cause problems + when, for example, we have j <<= 1 in a loop. */ + + nonzero_sign_valid = 0; + + /* Compute the mapping from uids to cuids. + Cuids are numbers assigned to insns, like uids, + except that cuids increase monotonically through the code. + + Scan all SETs and see if we can deduce anything about what + bits are known to be zero for some registers and how many copies + of the sign bit are known to exist for those registers. + + Also set any known values so that we can use it while searching + for what bits are known to be set. */ + + label_tick = 1; + + setup_incoming_promotions (); + + for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) + { + INSN_CUID (insn) = ++i; + subst_low_cuid = i; + subst_insn = insn; + + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + note_stores (PATTERN (insn), set_nonzero_bits_and_sign_copies); + record_dead_and_set_regs (insn); + } + + if (GET_CODE (insn) == CODE_LABEL) + label_tick++; + } + + nonzero_sign_valid = 1; + + /* Now scan all the insns in forward order. */ + + label_tick = 1; + last_call_cuid = 0; + mem_last_set = 0; + bzero (reg_last_death, nregs * sizeof (rtx)); + bzero (reg_last_set, nregs * sizeof (rtx)); + bzero (reg_last_set_value, nregs * sizeof (rtx)); + bzero (reg_last_set_table_tick, nregs * sizeof (int)); + bzero (reg_last_set_label, nregs * sizeof (int)); + bzero (reg_last_set_invalid, nregs * sizeof (char)); + + setup_incoming_promotions (); + + for (insn = f; insn; insn = next ? next : NEXT_INSN (insn)) + { + next = 0; + + if (GET_CODE (insn) == CODE_LABEL) + label_tick++; + + else if (GET_CODE (insn) == INSN + || GET_CODE (insn) == CALL_INSN + || GET_CODE (insn) == JUMP_INSN) + { + /* Try this insn with each insn it links back to. */ + + for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) + if ((next = try_combine (insn, XEXP (links, 0), NULL_RTX)) != 0) + goto retry; + + /* Try each sequence of three linked insns ending with this one. */ + + for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) + for (nextlinks = LOG_LINKS (XEXP (links, 0)); nextlinks; + nextlinks = XEXP (nextlinks, 1)) + if ((next = try_combine (insn, XEXP (links, 0), + XEXP (nextlinks, 0))) != 0) + goto retry; + +#ifdef HAVE_cc0 + /* Try to combine a jump insn that uses CC0 + with a preceding insn that sets CC0, and maybe with its + logical predecessor as well. + This is how we make decrement-and-branch insns. + We need this special code because data flow connections + via CC0 do not get entered in LOG_LINKS. */ + + if (GET_CODE (insn) == JUMP_INSN + && (prev = prev_nonnote_insn (insn)) != 0 + && GET_CODE (prev) == INSN + && sets_cc0_p (PATTERN (prev))) + { + if ((next = try_combine (insn, prev, NULL_RTX)) != 0) + goto retry; + + for (nextlinks = LOG_LINKS (prev); nextlinks; + nextlinks = XEXP (nextlinks, 1)) + if ((next = try_combine (insn, prev, + XEXP (nextlinks, 0))) != 0) + goto retry; + } + + /* Do the same for an insn that explicitly references CC0. */ + if (GET_CODE (insn) == INSN + && (prev = prev_nonnote_insn (insn)) != 0 + && GET_CODE (prev) == INSN + && sets_cc0_p (PATTERN (prev)) + && GET_CODE (PATTERN (insn)) == SET + && reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (insn)))) + { + if ((next = try_combine (insn, prev, NULL_RTX)) != 0) + goto retry; + + for (nextlinks = LOG_LINKS (prev); nextlinks; + nextlinks = XEXP (nextlinks, 1)) + if ((next = try_combine (insn, prev, + XEXP (nextlinks, 0))) != 0) + goto retry; + } + + /* Finally, see if any of the insns that this insn links to + explicitly references CC0. If so, try this insn, that insn, + and its predecessor if it sets CC0. */ + for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) + if (GET_CODE (XEXP (links, 0)) == INSN + && GET_CODE (PATTERN (XEXP (links, 0))) == SET + && reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (XEXP (links, 0)))) + && (prev = prev_nonnote_insn (XEXP (links, 0))) != 0 + && GET_CODE (prev) == INSN + && sets_cc0_p (PATTERN (prev)) + && (next = try_combine (insn, XEXP (links, 0), prev)) != 0) + goto retry; +#endif + + /* Try combining an insn with two different insns whose results it + uses. */ + for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) + for (nextlinks = XEXP (links, 1); nextlinks; + nextlinks = XEXP (nextlinks, 1)) + if ((next = try_combine (insn, XEXP (links, 0), + XEXP (nextlinks, 0))) != 0) + goto retry; + + if (GET_CODE (insn) != NOTE) + record_dead_and_set_regs (insn); + + retry: + ; + } + } + + total_attempts += combine_attempts; + total_merges += combine_merges; + total_extras += combine_extras; + total_successes += combine_successes; + + nonzero_sign_valid = 0; +} + +/* Set up any promoted values for incoming argument registers. */ + +static void +setup_incoming_promotions () +{ +#ifdef PROMOTE_FUNCTION_ARGS + int regno; + rtx reg; + enum machine_mode mode; + int unsignedp; + rtx first = get_insns (); + + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (FUNCTION_ARG_REGNO_P (regno) + && (reg = promoted_input_arg (regno, &mode, &unsignedp)) != 0) + record_value_for_reg (reg, first, + gen_rtx (unsignedp ? ZERO_EXTEND : SIGN_EXTEND, + GET_MODE (reg), + gen_rtx (CLOBBER, mode, const0_rtx))); +#endif +} + +/* Called via note_stores. If X is a pseudo that is used in more than + one basic block, is narrower that HOST_BITS_PER_WIDE_INT, and is being + set, record what bits are known zero. If we are clobbering X, + ignore this "set" because the clobbered value won't be used. + + If we are setting only a portion of X and we can't figure out what + portion, assume all bits will be used since we don't know what will + be happening. + + Similarly, set how many bits of X are known to be copies of the sign bit + at all locations in the function. This is the smallest number implied + by any set of X. */ + +static void +set_nonzero_bits_and_sign_copies (x, set) + rtx x; + rtx set; +{ + int num; + + if (GET_CODE (x) == REG + && REGNO (x) >= FIRST_PSEUDO_REGISTER + && reg_n_sets[REGNO (x)] > 1 + && reg_basic_block[REGNO (x)] < 0 + /* If this register is undefined at the start of the file, we can't + say what its contents were. */ + && ! (basic_block_live_at_start[0][REGNO (x) / REGSET_ELT_BITS] + & ((REGSET_ELT_TYPE) 1 << (REGNO (x) % REGSET_ELT_BITS))) + && GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT) + { + if (GET_CODE (set) == CLOBBER) + { + reg_nonzero_bits[REGNO (x)] = GET_MODE_MASK (GET_MODE (x)); + reg_sign_bit_copies[REGNO (x)] = 0; + return; + } + + /* If this is a complex assignment, see if we can convert it into a + simple assignment. */ + set = expand_field_assignment (set); + + /* If this is a simple assignment, or we have a paradoxical SUBREG, + set what we know about X. */ + + if (SET_DEST (set) == x + || (GET_CODE (SET_DEST (set)) == SUBREG + && (GET_MODE_SIZE (GET_MODE (SET_DEST (set))) + > GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (set))))) + && SUBREG_REG (SET_DEST (set)) == x)) + { + rtx src = SET_SRC (set); + +#ifdef SHORT_IMMEDIATES_SIGN_EXTEND + /* If X is narrower than a word and SRC is a non-negative + constant that would appear negative in the mode of X, + sign-extend it for use in reg_nonzero_bits because some + machines (maybe most) will actually do the sign-extension + and this is the conservative approach. + + ??? For 2.5, try to tighten up the MD files in this regard + instead of this kludge. */ + + if (GET_MODE_BITSIZE (GET_MODE (x)) < BITS_PER_WORD + && GET_CODE (src) == CONST_INT + && INTVAL (src) > 0 + && 0 != (INTVAL (src) + & ((HOST_WIDE_INT) 1 + << GET_MODE_BITSIZE (GET_MODE (x))))) + src = GEN_INT (INTVAL (src) + | ((HOST_WIDE_INT) (-1) + << GET_MODE_BITSIZE (GET_MODE (x)))); +#endif + + reg_nonzero_bits[REGNO (x)] + |= nonzero_bits (src, nonzero_bits_mode); + num = num_sign_bit_copies (SET_SRC (set), GET_MODE (x)); + if (reg_sign_bit_copies[REGNO (x)] == 0 + || reg_sign_bit_copies[REGNO (x)] > num) + reg_sign_bit_copies[REGNO (x)] = num; + } + else + { + reg_nonzero_bits[REGNO (x)] = GET_MODE_MASK (GET_MODE (x)); + reg_sign_bit_copies[REGNO (x)] = 0; + } + } +} + +/* See if INSN can be combined into I3. PRED and SUCC are optionally + insns that were previously combined into I3 or that will be combined + into the merger of INSN and I3. + + Return 0 if the combination is not allowed for any reason. + + If the combination is allowed, *PDEST will be set to the single + destination of INSN and *PSRC to the single source, and this function + will return 1. */ + +static int +can_combine_p (insn, i3, pred, succ, pdest, psrc) + rtx insn; + rtx i3; + rtx pred, succ; + rtx *pdest, *psrc; +{ + int i; + rtx set = 0, src, dest; + rtx p, link; + int all_adjacent = (succ ? (next_active_insn (insn) == succ + && next_active_insn (succ) == i3) + : next_active_insn (insn) == i3); + + /* Can combine only if previous insn is a SET of a REG, a SUBREG or CC0. + or a PARALLEL consisting of such a SET and CLOBBERs. + + If INSN has CLOBBER parallel parts, ignore them for our processing. + By definition, these happen during the execution of the insn. When it + is merged with another insn, all bets are off. If they are, in fact, + needed and aren't also supplied in I3, they may be added by + recog_for_combine. Otherwise, it won't match. + + We can also ignore a SET whose SET_DEST is mentioned in a REG_UNUSED + note. + + Get the source and destination of INSN. If more than one, can't + combine. */ + + if (GET_CODE (PATTERN (insn)) == SET) + set = PATTERN (insn); + else if (GET_CODE (PATTERN (insn)) == PARALLEL + && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) + { + for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) + { + rtx elt = XVECEXP (PATTERN (insn), 0, i); + + switch (GET_CODE (elt)) + { + /* We can ignore CLOBBERs. */ + case CLOBBER: + break; + + case SET: + /* Ignore SETs whose result isn't used but not those that + have side-effects. */ + if (find_reg_note (insn, REG_UNUSED, SET_DEST (elt)) + && ! side_effects_p (elt)) + break; + + /* If we have already found a SET, this is a second one and + so we cannot combine with this insn. */ + if (set) + return 0; + + set = elt; + break; + + default: + /* Anything else means we can't combine. */ + return 0; + } + } + + if (set == 0 + /* If SET_SRC is an ASM_OPERANDS we can't throw away these CLOBBERs, + so don't do anything with it. */ + || GET_CODE (SET_SRC (set)) == ASM_OPERANDS) + return 0; + } + else + return 0; + + if (set == 0) + return 0; + + set = expand_field_assignment (set); + src = SET_SRC (set), dest = SET_DEST (set); + + /* Don't eliminate a store in the stack pointer. */ + if (dest == stack_pointer_rtx + /* Don't install a subreg involving two modes not tieable. + It can worsen register allocation, and can even make invalid reload + insns, since the reg inside may need to be copied from in the + outside mode, and that may be invalid if it is an fp reg copied in + integer mode. As a special exception, we can allow this if + I3 is simply copying DEST, a REG, to CC0. */ + || (GET_CODE (src) == SUBREG + && ! MODES_TIEABLE_P (GET_MODE (src), GET_MODE (SUBREG_REG (src))) +#ifdef HAVE_cc0 + && ! (GET_CODE (i3) == INSN && GET_CODE (PATTERN (i3)) == SET + && SET_DEST (PATTERN (i3)) == cc0_rtx + && GET_CODE (dest) == REG && dest == SET_SRC (PATTERN (i3))) +#endif + ) + /* If we couldn't eliminate a field assignment, we can't combine. */ + || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == STRICT_LOW_PART + /* Don't combine with an insn that sets a register to itself if it has + a REG_EQUAL note. This may be part of a REG_NO_CONFLICT sequence. */ + || (rtx_equal_p (src, dest) && find_reg_note (insn, REG_EQUAL, NULL_RTX)) + /* Can't merge a function call. */ + || GET_CODE (src) == CALL + /* Don't substitute into an incremented register. */ + || FIND_REG_INC_NOTE (i3, dest) + || (succ && FIND_REG_INC_NOTE (succ, dest)) + /* Don't combine the end of a libcall into anything. */ + || find_reg_note (insn, REG_RETVAL, NULL_RTX) + /* Make sure that DEST is not used after SUCC but before I3. */ + || (succ && ! all_adjacent + && reg_used_between_p (dest, succ, i3)) + /* Make sure that the value that is to be substituted for the register + does not use any registers whose values alter in between. However, + If the insns are adjacent, a use can't cross a set even though we + think it might (this can happen for a sequence of insns each setting + the same destination; reg_last_set of that register might point to + a NOTE). Also, don't move a volatile asm or UNSPEC_VOLATILE across + any other insns. */ + || (! all_adjacent + && (use_crosses_set_p (src, INSN_CUID (insn)) + || (GET_CODE (src) == ASM_OPERANDS && MEM_VOLATILE_P (src)) + || GET_CODE (src) == UNSPEC_VOLATILE)) + /* If there is a REG_NO_CONFLICT note for DEST in I3 or SUCC, we get + better register allocation by not doing the combine. */ + || find_reg_note (i3, REG_NO_CONFLICT, dest) + || (succ && find_reg_note (succ, REG_NO_CONFLICT, dest)) + /* Don't combine across a CALL_INSN, because that would possibly + change whether the life span of some REGs crosses calls or not, + and it is a pain to update that information. + Exception: if source is a constant, moving it later can't hurt. + Accept that special case, because it helps -fforce-addr a lot. */ + || (INSN_CUID (insn) < last_call_cuid && ! CONSTANT_P (src))) + return 0; + + /* DEST must either be a REG or CC0. */ + if (GET_CODE (dest) == REG) + { + /* If register alignment is being enforced for multi-word items in all + cases except for parameters, it is possible to have a register copy + insn referencing a hard register that is not allowed to contain the + mode being copied and which would not be valid as an operand of most + insns. Eliminate this problem by not combining with such an insn. + + Also, on some machines we don't want to extend the life of a hard + register. */ + + if (GET_CODE (src) == REG + && ((REGNO (dest) < FIRST_PSEUDO_REGISTER + && ! HARD_REGNO_MODE_OK (REGNO (dest), GET_MODE (dest))) +#ifdef SMALL_REGISTER_CLASSES + /* Don't extend the life of a hard register. */ + || REGNO (src) < FIRST_PSEUDO_REGISTER +#else + || (REGNO (src) < FIRST_PSEUDO_REGISTER + && ! HARD_REGNO_MODE_OK (REGNO (src), GET_MODE (src))) +#endif + )) + return 0; + } + else if (GET_CODE (dest) != CC0) + return 0; + + /* Don't substitute for a register intended as a clobberable operand. + Similarly, don't substitute an expression containing a register that + will be clobbered in I3. */ + if (GET_CODE (PATTERN (i3)) == PARALLEL) + for (i = XVECLEN (PATTERN (i3), 0) - 1; i >= 0; i--) + if (GET_CODE (XVECEXP (PATTERN (i3), 0, i)) == CLOBBER + && (reg_overlap_mentioned_p (XEXP (XVECEXP (PATTERN (i3), 0, i), 0), + src) + || rtx_equal_p (XEXP (XVECEXP (PATTERN (i3), 0, i), 0), dest))) + return 0; + + /* If INSN contains anything volatile, or is an `asm' (whether volatile + or not), reject, unless nothing volatile comes between it and I3, + with the exception of SUCC. */ + + if (GET_CODE (src) == ASM_OPERANDS || volatile_refs_p (src)) + for (p = NEXT_INSN (insn); p != i3; p = NEXT_INSN (p)) + if (GET_RTX_CLASS (GET_CODE (p)) == 'i' + && p != succ && volatile_refs_p (PATTERN (p))) + return 0; + + /* If INSN or I2 contains an autoincrement or autodecrement, + make sure that register is not used between there and I3, + and not already used in I3 either. + Also insist that I3 not be a jump; if it were one + and the incremented register were spilled, we would lose. */ + +#ifdef AUTO_INC_DEC + for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) + if (REG_NOTE_KIND (link) == REG_INC + && (GET_CODE (i3) == JUMP_INSN + || reg_used_between_p (XEXP (link, 0), insn, i3) + || reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i3)))) + return 0; +#endif + +#ifdef HAVE_cc0 + /* Don't combine an insn that follows a CC0-setting insn. + An insn that uses CC0 must not be separated from the one that sets it. + We do, however, allow I2 to follow a CC0-setting insn if that insn + is passed as I1; in that case it will be deleted also. + We also allow combining in this case if all the insns are adjacent + because that would leave the two CC0 insns adjacent as well. + It would be more logical to test whether CC0 occurs inside I1 or I2, + but that would be much slower, and this ought to be equivalent. */ + + p = prev_nonnote_insn (insn); + if (p && p != pred && GET_CODE (p) == INSN && sets_cc0_p (PATTERN (p)) + && ! all_adjacent) + return 0; +#endif + + /* If we get here, we have passed all the tests and the combination is + to be allowed. */ + + *pdest = dest; + *psrc = src; + + return 1; +} + +/* LOC is the location within I3 that contains its pattern or the component + of a PARALLEL of the pattern. We validate that it is valid for combining. + + One problem is if I3 modifies its output, as opposed to replacing it + entirely, we can't allow the output to contain I2DEST or I1DEST as doing + so would produce an insn that is not equivalent to the original insns. + + Consider: + + (set (reg:DI 101) (reg:DI 100)) + (set (subreg:SI (reg:DI 101) 0) ) + + This is NOT equivalent to: + + (parallel [(set (subreg:SI (reg:DI 100) 0) ) + (set (reg:DI 101) (reg:DI 100))]) + + Not only does this modify 100 (in which case it might still be valid + if 100 were dead in I2), it sets 101 to the ORIGINAL value of 100. + + We can also run into a problem if I2 sets a register that I1 + uses and I1 gets directly substituted into I3 (not via I2). In that + case, we would be getting the wrong value of I2DEST into I3, so we + must reject the combination. This case occurs when I2 and I1 both + feed into I3, rather than when I1 feeds into I2, which feeds into I3. + If I1_NOT_IN_SRC is non-zero, it means that finding I1 in the source + of a SET must prevent combination from occurring. + + On machines where SMALL_REGISTER_CLASSES is defined, we don't combine + if the destination of a SET is a hard register. + + Before doing the above check, we first try to expand a field assignment + into a set of logical operations. + + If PI3_DEST_KILLED is non-zero, it is a pointer to a location in which + we place a register that is both set and used within I3. If more than one + such register is detected, we fail. + + Return 1 if the combination is valid, zero otherwise. */ + +static int +combinable_i3pat (i3, loc, i2dest, i1dest, i1_not_in_src, pi3dest_killed) + rtx i3; + rtx *loc; + rtx i2dest; + rtx i1dest; + int i1_not_in_src; + rtx *pi3dest_killed; +{ + rtx x = *loc; + + if (GET_CODE (x) == SET) + { + rtx set = expand_field_assignment (x); + rtx dest = SET_DEST (set); + rtx src = SET_SRC (set); + rtx inner_dest = dest, inner_src = src; + + SUBST (*loc, set); + + while (GET_CODE (inner_dest) == STRICT_LOW_PART + || GET_CODE (inner_dest) == SUBREG + || GET_CODE (inner_dest) == ZERO_EXTRACT) + inner_dest = XEXP (inner_dest, 0); + + /* We probably don't need this any more now that LIMIT_RELOAD_CLASS + was added. */ +#if 0 + while (GET_CODE (inner_src) == STRICT_LOW_PART + || GET_CODE (inner_src) == SUBREG + || GET_CODE (inner_src) == ZERO_EXTRACT) + inner_src = XEXP (inner_src, 0); + + /* If it is better that two different modes keep two different pseudos, + avoid combining them. This avoids producing the following pattern + on a 386: + (set (subreg:SI (reg/v:QI 21) 0) + (lshiftrt:SI (reg/v:SI 20) + (const_int 24))) + If that were made, reload could not handle the pair of + reg 20/21, since it would try to get any GENERAL_REGS + but some of them don't handle QImode. */ + + if (rtx_equal_p (inner_src, i2dest) + && GET_CODE (inner_dest) == REG + && ! MODES_TIEABLE_P (GET_MODE (i2dest), GET_MODE (inner_dest))) + return 0; +#endif + + /* Check for the case where I3 modifies its output, as + discussed above. */ + if ((inner_dest != dest + && (reg_overlap_mentioned_p (i2dest, inner_dest) + || (i1dest && reg_overlap_mentioned_p (i1dest, inner_dest)))) + /* This is the same test done in can_combine_p except that we + allow a hard register with SMALL_REGISTER_CLASSES if SRC is a + CALL operation. */ + || (GET_CODE (inner_dest) == REG + && REGNO (inner_dest) < FIRST_PSEUDO_REGISTER +#ifdef SMALL_REGISTER_CLASSES + && GET_CODE (src) != CALL +#else + && ! HARD_REGNO_MODE_OK (REGNO (inner_dest), + GET_MODE (inner_dest)) +#endif + ) + + || (i1_not_in_src && reg_overlap_mentioned_p (i1dest, src))) + return 0; + + /* If DEST is used in I3, it is being killed in this insn, + so record that for later. + Never add REG_DEAD notes for the FRAME_POINTER_REGNUM or the + STACK_POINTER_REGNUM, since these are always considered to be + live. Similarly for ARG_POINTER_REGNUM if it is fixed. */ + if (pi3dest_killed && GET_CODE (dest) == REG + && reg_referenced_p (dest, PATTERN (i3)) + && REGNO (dest) != FRAME_POINTER_REGNUM +#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM + && (REGNO (dest) != ARG_POINTER_REGNUM + || ! fixed_regs [REGNO (dest)]) +#endif + && REGNO (dest) != STACK_POINTER_REGNUM) + { + if (*pi3dest_killed) + return 0; + + *pi3dest_killed = dest; + } + } + + else if (GET_CODE (x) == PARALLEL) + { + int i; + + for (i = 0; i < XVECLEN (x, 0); i++) + if (! combinable_i3pat (i3, &XVECEXP (x, 0, i), i2dest, i1dest, + i1_not_in_src, pi3dest_killed)) + return 0; + } + + return 1; +} + +/* Try to combine the insns I1 and I2 into I3. + Here I1 and I2 appear earlier than I3. + I1 can be zero; then we combine just I2 into I3. + + It we are combining three insns and the resulting insn is not recognized, + try splitting it into two insns. If that happens, I2 and I3 are retained + and I1 is pseudo-deleted by turning it into a NOTE. Otherwise, I1 and I2 + are pseudo-deleted. + + If we created two insns, return I2; otherwise return I3. + Return 0 if the combination does not work. Then nothing is changed. */ + +static rtx +try_combine (i3, i2, i1) + register rtx i3, i2, i1; +{ + /* New patterns for I3 and I3, respectively. */ + rtx newpat, newi2pat = 0; + /* Indicates need to preserve SET in I1 or I2 in I3 if it is not dead. */ + int added_sets_1, added_sets_2; + /* Total number of SETs to put into I3. */ + int total_sets; + /* Nonzero is I2's body now appears in I3. */ + int i2_is_used; + /* INSN_CODEs for new I3, new I2, and user of condition code. */ + int insn_code_number, i2_code_number, other_code_number; + /* Contains I3 if the destination of I3 is used in its source, which means + that the old life of I3 is being killed. If that usage is placed into + I2 and not in I3, a REG_DEAD note must be made. */ + rtx i3dest_killed = 0; + /* SET_DEST and SET_SRC of I2 and I1. */ + rtx i2dest, i2src, i1dest = 0, i1src = 0; + /* PATTERN (I2), or a copy of it in certain cases. */ + rtx i2pat; + /* Indicates if I2DEST or I1DEST is in I2SRC or I1_SRC. */ + int i2dest_in_i2src, i1dest_in_i1src = 0, i2dest_in_i1src = 0; + int i1_feeds_i3 = 0; + /* Notes that must be added to REG_NOTES in I3 and I2. */ + rtx new_i3_notes, new_i2_notes; + + int maxreg; + rtx temp; + register rtx link; + int i; + + /* If any of I1, I2, and I3 isn't really an insn, we can't do anything. + This can occur when flow deletes an insn that it has merged into an + auto-increment address. We also can't do anything if I3 has a + REG_LIBCALL note since we don't want to disrupt the contiguity of a + libcall. */ + + if (GET_RTX_CLASS (GET_CODE (i3)) != 'i' + || GET_RTX_CLASS (GET_CODE (i2)) != 'i' + || (i1 && GET_RTX_CLASS (GET_CODE (i1)) != 'i') + || find_reg_note (i3, REG_LIBCALL, NULL_RTX)) + return 0; + + combine_attempts++; + + undobuf.num_undo = previous_num_undos = 0; + undobuf.other_insn = 0; + + /* Save the current high-water-mark so we can free storage if we didn't + accept this combination. */ + undobuf.storage = (char *) oballoc (0); + + /* If I1 and I2 both feed I3, they can be in any order. To simplify the + code below, set I1 to be the earlier of the two insns. */ + if (i1 && INSN_CUID (i1) > INSN_CUID (i2)) + temp = i1, i1 = i2, i2 = temp; + + /* First check for one important special-case that the code below will + not handle. Namely, the case where I1 is zero, I2 has multiple sets, + and I3 is a SET whose SET_SRC is a SET_DEST in I2. In that case, + we may be able to replace that destination with the destination of I3. + This occurs in the common code where we compute both a quotient and + remainder into a structure, in which case we want to do the computation + directly into the structure to avoid register-register copies. + + We make very conservative checks below and only try to handle the + most common cases of this. For example, we only handle the case + where I2 and I3 are adjacent to avoid making difficult register + usage tests. */ + + if (i1 == 0 && GET_CODE (i3) == INSN && GET_CODE (PATTERN (i3)) == SET + && GET_CODE (SET_SRC (PATTERN (i3))) == REG + && REGNO (SET_SRC (PATTERN (i3))) >= FIRST_PSEUDO_REGISTER +#ifdef SMALL_REGISTER_CLASSES + && (GET_CODE (SET_DEST (PATTERN (i3))) != REG + || REGNO (SET_DEST (PATTERN (i3))) >= FIRST_PSEUDO_REGISTER) +#endif + && find_reg_note (i3, REG_DEAD, SET_SRC (PATTERN (i3))) + && GET_CODE (PATTERN (i2)) == PARALLEL + && ! side_effects_p (SET_DEST (PATTERN (i3))) + /* If the dest of I3 is a ZERO_EXTRACT or STRICT_LOW_PART, the code + below would need to check what is inside (and reg_overlap_mentioned_p + doesn't support those codes anyway). Don't allow those destinations; + the resulting insn isn't likely to be recognized anyway. */ + && GET_CODE (SET_DEST (PATTERN (i3))) != ZERO_EXTRACT + && GET_CODE (SET_DEST (PATTERN (i3))) != STRICT_LOW_PART + && ! reg_overlap_mentioned_p (SET_SRC (PATTERN (i3)), + SET_DEST (PATTERN (i3))) + && next_real_insn (i2) == i3) + { + rtx p2 = PATTERN (i2); + + /* Make sure that the destination of I3, + which we are going to substitute into one output of I2, + is not used within another output of I2. We must avoid making this: + (parallel [(set (mem (reg 69)) ...) + (set (reg 69) ...)]) + which is not well-defined as to order of actions. + (Besides, reload can't handle output reloads for this.) + + The problem can also happen if the dest of I3 is a memory ref, + if another dest in I2 is an indirect memory ref. */ + for (i = 0; i < XVECLEN (p2, 0); i++) + if (GET_CODE (XVECEXP (p2, 0, i)) == SET + && reg_overlap_mentioned_p (SET_DEST (PATTERN (i3)), + SET_DEST (XVECEXP (p2, 0, i)))) + break; + + if (i == XVECLEN (p2, 0)) + for (i = 0; i < XVECLEN (p2, 0); i++) + if (SET_DEST (XVECEXP (p2, 0, i)) == SET_SRC (PATTERN (i3))) + { + combine_merges++; + + subst_insn = i3; + subst_low_cuid = INSN_CUID (i2); + + added_sets_2 = 0; + i2dest = SET_SRC (PATTERN (i3)); + + /* Replace the dest in I2 with our dest and make the resulting + insn the new pattern for I3. Then skip to where we + validate the pattern. Everything was set up above. */ + SUBST (SET_DEST (XVECEXP (p2, 0, i)), + SET_DEST (PATTERN (i3))); + + newpat = p2; + goto validate_replacement; + } + } + +#ifndef HAVE_cc0 + /* If we have no I1 and I2 looks like: + (parallel [(set (reg:CC X) (compare:CC OP (const_int 0))) + (set Y OP)]) + make up a dummy I1 that is + (set Y OP) + and change I2 to be + (set (reg:CC X) (compare:CC Y (const_int 0))) + + (We can ignore any trailing CLOBBERs.) + + This undoes a previous combination and allows us to match a branch-and- + decrement insn. */ + + if (i1 == 0 && GET_CODE (PATTERN (i2)) == PARALLEL + && XVECLEN (PATTERN (i2), 0) >= 2 + && GET_CODE (XVECEXP (PATTERN (i2), 0, 0)) == SET + && (GET_MODE_CLASS (GET_MODE (SET_DEST (XVECEXP (PATTERN (i2), 0, 0)))) + == MODE_CC) + && GET_CODE (SET_SRC (XVECEXP (PATTERN (i2), 0, 0))) == COMPARE + && XEXP (SET_SRC (XVECEXP (PATTERN (i2), 0, 0)), 1) == const0_rtx + && GET_CODE (XVECEXP (PATTERN (i2), 0, 1)) == SET + && GET_CODE (SET_DEST (XVECEXP (PATTERN (i2), 0, 1))) == REG + && rtx_equal_p (XEXP (SET_SRC (XVECEXP (PATTERN (i2), 0, 0)), 0), + SET_SRC (XVECEXP (PATTERN (i2), 0, 1)))) + { + for (i = XVECLEN (PATTERN (i2), 0) - 1; i >= 2; i--) + if (GET_CODE (XVECEXP (PATTERN (i2), 0, i)) != CLOBBER) + break; + + if (i == 1) + { + /* We make I1 with the same INSN_UID as I2. This gives it + the same INSN_CUID for value tracking. Our fake I1 will + never appear in the insn stream so giving it the same INSN_UID + as I2 will not cause a problem. */ + + i1 = gen_rtx (INSN, VOIDmode, INSN_UID (i2), 0, i2, + XVECEXP (PATTERN (i2), 0, 1), -1, 0, 0); + + SUBST (PATTERN (i2), XVECEXP (PATTERN (i2), 0, 0)); + SUBST (XEXP (SET_SRC (PATTERN (i2)), 0), + SET_DEST (PATTERN (i1))); + } + } +#endif + + /* Verify that I2 and I1 are valid for combining. */ + if (! can_combine_p (i2, i3, i1, NULL_RTX, &i2dest, &i2src) + || (i1 && ! can_combine_p (i1, i3, NULL_RTX, i2, &i1dest, &i1src))) + { + undo_all (); + return 0; + } + + /* Record whether I2DEST is used in I2SRC and similarly for the other + cases. Knowing this will help in register status updating below. */ + i2dest_in_i2src = reg_overlap_mentioned_p (i2dest, i2src); + i1dest_in_i1src = i1 && reg_overlap_mentioned_p (i1dest, i1src); + i2dest_in_i1src = i1 && reg_overlap_mentioned_p (i2dest, i1src); + + /* See if I1 directly feeds into I3. It does if I1DEST is not used + in I2SRC. */ + i1_feeds_i3 = i1 && ! reg_overlap_mentioned_p (i1dest, i2src); + + /* Ensure that I3's pattern can be the destination of combines. */ + if (! combinable_i3pat (i3, &PATTERN (i3), i2dest, i1dest, + i1 && i2dest_in_i1src && i1_feeds_i3, + &i3dest_killed)) + { + undo_all (); + return 0; + } + + /* If I3 has an inc, then give up if I1 or I2 uses the reg that is inc'd. + We used to do this EXCEPT in one case: I3 has a post-inc in an + output operand. However, that exception can give rise to insns like + mov r3,(r3)+ + which is a famous insn on the PDP-11 where the value of r3 used as the + source was model-dependent. Avoid this sort of thing. */ + +#if 0 + if (!(GET_CODE (PATTERN (i3)) == SET + && GET_CODE (SET_SRC (PATTERN (i3))) == REG + && GET_CODE (SET_DEST (PATTERN (i3))) == MEM + && (GET_CODE (XEXP (SET_DEST (PATTERN (i3)), 0)) == POST_INC + || GET_CODE (XEXP (SET_DEST (PATTERN (i3)), 0)) == POST_DEC))) + /* It's not the exception. */ +#endif +#ifdef AUTO_INC_DEC + for (link = REG_NOTES (i3); link; link = XEXP (link, 1)) + if (REG_NOTE_KIND (link) == REG_INC + && (reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i2)) + || (i1 != 0 + && reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i1))))) + { + undo_all (); + return 0; + } +#endif + + /* See if the SETs in I1 or I2 need to be kept around in the merged + instruction: whenever the value set there is still needed past I3. + For the SETs in I2, this is easy: we see if I2DEST dies or is set in I3. + + For the SET in I1, we have two cases: If I1 and I2 independently + feed into I3, the set in I1 needs to be kept around if I1DEST dies + or is set in I3. Otherwise (if I1 feeds I2 which feeds I3), the set + in I1 needs to be kept around unless I1DEST dies or is set in either + I2 or I3. We can distinguish these cases by seeing if I2SRC mentions + I1DEST. If so, we know I1 feeds into I2. */ + + added_sets_2 = ! dead_or_set_p (i3, i2dest); + + added_sets_1 + = i1 && ! (i1_feeds_i3 ? dead_or_set_p (i3, i1dest) + : (dead_or_set_p (i3, i1dest) || dead_or_set_p (i2, i1dest))); + + /* If the set in I2 needs to be kept around, we must make a copy of + PATTERN (I2), so that when we substitute I1SRC for I1DEST in + PATTERN (I2), we are only substituting for the original I1DEST, not into + an already-substituted copy. This also prevents making self-referential + rtx. If I2 is a PARALLEL, we just need the piece that assigns I2SRC to + I2DEST. */ + + i2pat = (GET_CODE (PATTERN (i2)) == PARALLEL + ? gen_rtx (SET, VOIDmode, i2dest, i2src) + : PATTERN (i2)); + + if (added_sets_2) + i2pat = copy_rtx (i2pat); + + combine_merges++; + + /* Substitute in the latest insn for the regs set by the earlier ones. */ + + maxreg = max_reg_num (); + + subst_insn = i3; + + /* It is possible that the source of I2 or I1 may be performing an + unneeded operation, such as a ZERO_EXTEND of something that is known + to have the high part zero. Handle that case by letting subst look at + the innermost one of them. + + Another way to do this would be to have a function that tries to + simplify a single insn instead of merging two or more insns. We don't + do this because of the potential of infinite loops and because + of the potential extra memory required. However, doing it the way + we are is a bit of a kludge and doesn't catch all cases. + + But only do this if -fexpensive-optimizations since it slows things down + and doesn't usually win. */ + + if (flag_expensive_optimizations) + { + /* Pass pc_rtx so no substitutions are done, just simplifications. + The cases that we are interested in here do not involve the few + cases were is_replaced is checked. */ + if (i1) + { + subst_low_cuid = INSN_CUID (i1); + i1src = subst (i1src, pc_rtx, pc_rtx, 0, 0); + } + else + { + subst_low_cuid = INSN_CUID (i2); + i2src = subst (i2src, pc_rtx, pc_rtx, 0, 0); + } + + previous_num_undos = undobuf.num_undo; + } + +#ifndef HAVE_cc0 + /* Many machines that don't use CC0 have insns that can both perform an + arithmetic operation and set the condition code. These operations will + be represented as a PARALLEL with the first element of the vector + being a COMPARE of an arithmetic operation with the constant zero. + The second element of the vector will set some pseudo to the result + of the same arithmetic operation. If we simplify the COMPARE, we won't + match such a pattern and so will generate an extra insn. Here we test + for this case, where both the comparison and the operation result are + needed, and make the PARALLEL by just replacing I2DEST in I3SRC with + I2SRC. Later we will make the PARALLEL that contains I2. */ + + if (i1 == 0 && added_sets_2 && GET_CODE (PATTERN (i3)) == SET + && GET_CODE (SET_SRC (PATTERN (i3))) == COMPARE + && XEXP (SET_SRC (PATTERN (i3)), 1) == const0_rtx + && rtx_equal_p (XEXP (SET_SRC (PATTERN (i3)), 0), i2dest)) + { + rtx *cc_use; + enum machine_mode compare_mode; + + newpat = PATTERN (i3); + SUBST (XEXP (SET_SRC (newpat), 0), i2src); + + i2_is_used = 1; + +#ifdef EXTRA_CC_MODES + /* See if a COMPARE with the operand we substituted in should be done + with the mode that is currently being used. If not, do the same + processing we do in `subst' for a SET; namely, if the destination + is used only once, try to replace it with a register of the proper + mode and also replace the COMPARE. */ + if (undobuf.other_insn == 0 + && (cc_use = find_single_use (SET_DEST (newpat), i3, + &undobuf.other_insn)) + && ((compare_mode = SELECT_CC_MODE (GET_CODE (*cc_use), + i2src, const0_rtx)) + != GET_MODE (SET_DEST (newpat)))) + { + int regno = REGNO (SET_DEST (newpat)); + rtx new_dest = gen_rtx (REG, compare_mode, regno); + + if (regno < FIRST_PSEUDO_REGISTER + || (reg_n_sets[regno] == 1 && ! added_sets_2 + && ! REG_USERVAR_P (SET_DEST (newpat)))) + { + if (regno >= FIRST_PSEUDO_REGISTER) + SUBST (regno_reg_rtx[regno], new_dest); + + SUBST (SET_DEST (newpat), new_dest); + SUBST (XEXP (*cc_use, 0), new_dest); + SUBST (SET_SRC (newpat), + gen_rtx_combine (COMPARE, compare_mode, + i2src, const0_rtx)); + } + else + undobuf.other_insn = 0; + } +#endif + } + else +#endif + { + n_occurrences = 0; /* `subst' counts here */ + + /* If I1 feeds into I2 (not into I3) and I1DEST is in I1SRC, we + need to make a unique copy of I2SRC each time we substitute it + to avoid self-referential rtl. */ + + subst_low_cuid = INSN_CUID (i2); + newpat = subst (PATTERN (i3), i2dest, i2src, 0, + ! i1_feeds_i3 && i1dest_in_i1src); + previous_num_undos = undobuf.num_undo; + + /* Record whether i2's body now appears within i3's body. */ + i2_is_used = n_occurrences; + } + + /* If we already got a failure, don't try to do more. Otherwise, + try to substitute in I1 if we have it. */ + + if (i1 && GET_CODE (newpat) != CLOBBER) + { + /* Before we can do this substitution, we must redo the test done + above (see detailed comments there) that ensures that I1DEST + isn't mentioned in any SETs in NEWPAT that are field assignments. */ + + if (! combinable_i3pat (NULL_RTX, &newpat, i1dest, NULL_RTX, + 0, NULL_PTR)) + { + undo_all (); + return 0; + } + + n_occurrences = 0; + subst_low_cuid = INSN_CUID (i1); + newpat = subst (newpat, i1dest, i1src, 0, 0); + previous_num_undos = undobuf.num_undo; + } + + /* Fail if an autoincrement side-effect has been duplicated. Be careful + to count all the ways that I2SRC and I1SRC can be used. */ + if ((FIND_REG_INC_NOTE (i2, NULL_RTX) != 0 + && i2_is_used + added_sets_2 > 1) + || (i1 != 0 && FIND_REG_INC_NOTE (i1, NULL_RTX) != 0 + && (n_occurrences + added_sets_1 + (added_sets_2 && ! i1_feeds_i3) + > 1)) + /* Fail if we tried to make a new register (we used to abort, but there's + really no reason to). */ + || max_reg_num () != maxreg + /* Fail if we couldn't do something and have a CLOBBER. */ + || GET_CODE (newpat) == CLOBBER) + { + undo_all (); + return 0; + } + + /* If the actions of the earlier insns must be kept + in addition to substituting them into the latest one, + we must make a new PARALLEL for the latest insn + to hold additional the SETs. */ + + if (added_sets_1 || added_sets_2) + { + combine_extras++; + + if (GET_CODE (newpat) == PARALLEL) + { + rtvec old = XVEC (newpat, 0); + total_sets = XVECLEN (newpat, 0) + added_sets_1 + added_sets_2; + newpat = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (total_sets)); + bcopy (&old->elem[0], &XVECEXP (newpat, 0, 0), + sizeof (old->elem[0]) * old->num_elem); + } + else + { + rtx old = newpat; + total_sets = 1 + added_sets_1 + added_sets_2; + newpat = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (total_sets)); + XVECEXP (newpat, 0, 0) = old; + } + + if (added_sets_1) + XVECEXP (newpat, 0, --total_sets) + = (GET_CODE (PATTERN (i1)) == PARALLEL + ? gen_rtx (SET, VOIDmode, i1dest, i1src) : PATTERN (i1)); + + if (added_sets_2) + { + /* If there is no I1, use I2's body as is. We used to also not do + the subst call below if I2 was substituted into I3, + but that could lose a simplification. */ + if (i1 == 0) + XVECEXP (newpat, 0, --total_sets) = i2pat; + else + /* See comment where i2pat is assigned. */ + XVECEXP (newpat, 0, --total_sets) + = subst (i2pat, i1dest, i1src, 0, 0); + } + } + + /* We come here when we are replacing a destination in I2 with the + destination of I3. */ + validate_replacement: + + /* Is the result of combination a valid instruction? */ + insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); + + /* If the result isn't valid, see if it is a PARALLEL of two SETs where + the second SET's destination is a register that is unused. In that case, + we just need the first SET. This can occur when simplifying a divmod + insn. We *must* test for this case here because the code below that + splits two independent SETs doesn't handle this case correctly when it + updates the register status. Also check the case where the first + SET's destination is unused. That would not cause incorrect code, but + does cause an unneeded insn to remain. */ + + if (insn_code_number < 0 && GET_CODE (newpat) == PARALLEL + && XVECLEN (newpat, 0) == 2 + && GET_CODE (XVECEXP (newpat, 0, 0)) == SET + && GET_CODE (XVECEXP (newpat, 0, 1)) == SET + && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) == REG + && find_reg_note (i3, REG_UNUSED, SET_DEST (XVECEXP (newpat, 0, 1))) + && ! side_effects_p (SET_SRC (XVECEXP (newpat, 0, 1))) + && asm_noperands (newpat) < 0) + { + newpat = XVECEXP (newpat, 0, 0); + insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); + } + + else if (insn_code_number < 0 && GET_CODE (newpat) == PARALLEL + && XVECLEN (newpat, 0) == 2 + && GET_CODE (XVECEXP (newpat, 0, 0)) == SET + && GET_CODE (XVECEXP (newpat, 0, 1)) == SET + && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) == REG + && find_reg_note (i3, REG_UNUSED, SET_DEST (XVECEXP (newpat, 0, 0))) + && ! side_effects_p (SET_SRC (XVECEXP (newpat, 0, 0))) + && asm_noperands (newpat) < 0) + { + newpat = XVECEXP (newpat, 0, 1); + insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); + } + + /* See if this is an XOR. If so, perhaps the problem is that the + constant is out of range. Replace it with a complemented XOR with + a complemented constant; it might be in range. */ + + else if (insn_code_number < 0 && GET_CODE (newpat) == SET + && GET_CODE (SET_SRC (newpat)) == XOR + && GET_CODE (XEXP (SET_SRC (newpat), 1)) == CONST_INT + && ((temp = simplify_unary_operation (NOT, + GET_MODE (SET_SRC (newpat)), + XEXP (SET_SRC (newpat), 1), + GET_MODE (SET_SRC (newpat)))) + != 0)) + { + enum machine_mode i_mode = GET_MODE (SET_SRC (newpat)); + rtx pat + = gen_rtx_combine (SET, VOIDmode, SET_DEST (newpat), + gen_unary (NOT, i_mode, + gen_binary (XOR, i_mode, + XEXP (SET_SRC (newpat), 0), + temp))); + + insn_code_number = recog_for_combine (&pat, i3, &new_i3_notes); + if (insn_code_number >= 0) + newpat = pat; + } + + /* If we were combining three insns and the result is a simple SET + with no ASM_OPERANDS that wasn't recognized, try to split it into two + insns. There are two ways to do this. It can be split using a + machine-specific method (like when you have an addition of a large + constant) or by combine in the function find_split_point. */ + + if (i1 && insn_code_number < 0 && GET_CODE (newpat) == SET + && asm_noperands (newpat) < 0) + { + rtx m_split, *split; + rtx ni2dest = i2dest; + + /* See if the MD file can split NEWPAT. If it can't, see if letting it + use I2DEST as a scratch register will help. In the latter case, + convert I2DEST to the mode of the source of NEWPAT if we can. */ + + m_split = split_insns (newpat, i3); + + /* We can only use I2DEST as a scratch reg if it doesn't overlap any + inputs of NEWPAT. */ + + /* ??? If I2DEST is not safe, and I1DEST exists, then it would be + possible to try that as a scratch reg. This would require adding + more code to make it work though. */ + + if (m_split == 0 && ! reg_overlap_mentioned_p (ni2dest, newpat)) + { + /* If I2DEST is a hard register or the only use of a pseudo, + we can change its mode. */ + if (GET_MODE (SET_DEST (newpat)) != GET_MODE (i2dest) + && GET_MODE (SET_DEST (newpat)) != VOIDmode + && GET_CODE (i2dest) == REG + && (REGNO (i2dest) < FIRST_PSEUDO_REGISTER + || (reg_n_sets[REGNO (i2dest)] == 1 && ! added_sets_2 + && ! REG_USERVAR_P (i2dest)))) + ni2dest = gen_rtx (REG, GET_MODE (SET_DEST (newpat)), + REGNO (i2dest)); + + m_split = split_insns (gen_rtx (PARALLEL, VOIDmode, + gen_rtvec (2, newpat, + gen_rtx (CLOBBER, + VOIDmode, + ni2dest))), + i3); + } + + if (m_split && GET_CODE (m_split) == SEQUENCE + && XVECLEN (m_split, 0) == 2 + && (next_real_insn (i2) == i3 + || ! use_crosses_set_p (PATTERN (XVECEXP (m_split, 0, 0)), + INSN_CUID (i2)))) + { + rtx i2set, i3set; + rtx newi3pat = PATTERN (XVECEXP (m_split, 0, 1)); + newi2pat = PATTERN (XVECEXP (m_split, 0, 0)); + + i3set = single_set (XVECEXP (m_split, 0, 1)); + i2set = single_set (XVECEXP (m_split, 0, 0)); + + /* In case we changed the mode of I2DEST, replace it in the + pseudo-register table here. We can't do it above in case this + code doesn't get executed and we do a split the other way. */ + + if (REGNO (i2dest) >= FIRST_PSEUDO_REGISTER) + SUBST (regno_reg_rtx[REGNO (i2dest)], ni2dest); + + i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes); + + /* If I2 or I3 has multiple SETs, we won't know how to track + register status, so don't use these insns. */ + + if (i2_code_number >= 0 && i2set && i3set) + insn_code_number = recog_for_combine (&newi3pat, i3, + &new_i3_notes); + + if (insn_code_number >= 0) + newpat = newi3pat; + + /* It is possible that both insns now set the destination of I3. + If so, we must show an extra use of it. */ + + if (insn_code_number >= 0 && GET_CODE (SET_DEST (i3set)) == REG + && GET_CODE (SET_DEST (i2set)) == REG + && REGNO (SET_DEST (i3set)) == REGNO (SET_DEST (i2set))) + reg_n_sets[REGNO (SET_DEST (i2set))]++; + } + + /* If we can split it and use I2DEST, go ahead and see if that + helps things be recognized. Verify that none of the registers + are set between I2 and I3. */ + if (insn_code_number < 0 && (split = find_split_point (&newpat, i3)) != 0 +#ifdef HAVE_cc0 + && GET_CODE (i2dest) == REG +#endif + /* We need I2DEST in the proper mode. If it is a hard register + or the only use of a pseudo, we can change its mode. */ + && (GET_MODE (*split) == GET_MODE (i2dest) + || GET_MODE (*split) == VOIDmode + || REGNO (i2dest) < FIRST_PSEUDO_REGISTER + || (reg_n_sets[REGNO (i2dest)] == 1 && ! added_sets_2 + && ! REG_USERVAR_P (i2dest))) + && (next_real_insn (i2) == i3 + || ! use_crosses_set_p (*split, INSN_CUID (i2))) + /* We can't overwrite I2DEST if its value is still used by + NEWPAT. */ + && ! reg_referenced_p (i2dest, newpat)) + { + rtx newdest = i2dest; + + /* Get NEWDEST as a register in the proper mode. We have already + validated that we can do this. */ + if (GET_MODE (i2dest) != GET_MODE (*split) + && GET_MODE (*split) != VOIDmode) + { + newdest = gen_rtx (REG, GET_MODE (*split), REGNO (i2dest)); + + if (REGNO (i2dest) >= FIRST_PSEUDO_REGISTER) + SUBST (regno_reg_rtx[REGNO (i2dest)], newdest); + } + + /* If *SPLIT is a (mult FOO (const_int pow2)), convert it to + an ASHIFT. This can occur if it was inside a PLUS and hence + appeared to be a memory address. This is a kludge. */ + if (GET_CODE (*split) == MULT + && GET_CODE (XEXP (*split, 1)) == CONST_INT + && (i = exact_log2 (INTVAL (XEXP (*split, 1)))) >= 0) + SUBST (*split, gen_rtx_combine (ASHIFT, GET_MODE (*split), + XEXP (*split, 0), GEN_INT (i))); + +#ifdef INSN_SCHEDULING + /* If *SPLIT is a paradoxical SUBREG, when we split it, it should + be written as a ZERO_EXTEND. */ + if (GET_CODE (*split) == SUBREG + && GET_CODE (SUBREG_REG (*split)) == MEM) + SUBST (*split, gen_rtx_combine (ZERO_EXTEND, GET_MODE (*split), + XEXP (*split, 0))); +#endif + + newi2pat = gen_rtx_combine (SET, VOIDmode, newdest, *split); + SUBST (*split, newdest); + i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes); + if (i2_code_number >= 0) + insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); + } + } + + /* Check for a case where we loaded from memory in a narrow mode and + then sign extended it, but we need both registers. In that case, + we have a PARALLEL with both loads from the same memory location. + We can split this into a load from memory followed by a register-register + copy. This saves at least one insn, more if register allocation can + eliminate the copy. */ + + else if (i1 && insn_code_number < 0 && asm_noperands (newpat) < 0 + && GET_CODE (newpat) == PARALLEL + && XVECLEN (newpat, 0) == 2 + && GET_CODE (XVECEXP (newpat, 0, 0)) == SET + && GET_CODE (SET_SRC (XVECEXP (newpat, 0, 0))) == SIGN_EXTEND + && GET_CODE (XVECEXP (newpat, 0, 1)) == SET + && rtx_equal_p (SET_SRC (XVECEXP (newpat, 0, 1)), + XEXP (SET_SRC (XVECEXP (newpat, 0, 0)), 0)) + && ! use_crosses_set_p (SET_SRC (XVECEXP (newpat, 0, 1)), + INSN_CUID (i2)) + && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != ZERO_EXTRACT + && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != STRICT_LOW_PART + && ! reg_overlap_mentioned_p (SET_DEST (XVECEXP (newpat, 0, 1)), + SET_SRC (XVECEXP (newpat, 0, 1))) + && ! find_reg_note (i3, REG_UNUSED, + SET_DEST (XVECEXP (newpat, 0, 0)))) + { + rtx ni2dest; + + newi2pat = XVECEXP (newpat, 0, 0); + ni2dest = SET_DEST (XVECEXP (newpat, 0, 0)); + newpat = XVECEXP (newpat, 0, 1); + SUBST (SET_SRC (newpat), + gen_lowpart_for_combine (GET_MODE (SET_SRC (newpat)), ni2dest)); + i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes); + if (i2_code_number >= 0) + insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); + + if (insn_code_number >= 0) + { + rtx insn; + rtx link; + + /* If we will be able to accept this, we have made a change to the + destination of I3. This can invalidate a LOG_LINKS pointing + to I3. No other part of combine.c makes such a transformation. + + The new I3 will have a destination that was previously the + destination of I1 or I2 and which was used in i2 or I3. Call + distribute_links to make a LOG_LINK from the next use of + that destination. */ + + PATTERN (i3) = newpat; + distribute_links (gen_rtx (INSN_LIST, VOIDmode, i3, NULL_RTX)); + + /* I3 now uses what used to be its destination and which is + now I2's destination. That means we need a LOG_LINK from + I3 to I2. But we used to have one, so we still will. + + However, some later insn might be using I2's dest and have + a LOG_LINK pointing at I3. We must remove this link. + The simplest way to remove the link is to point it at I1, + which we know will be a NOTE. */ + + for (insn = NEXT_INSN (i3); + insn && GET_CODE (insn) != CODE_LABEL + && GET_CODE (PREV_INSN (insn)) != JUMP_INSN; + insn = NEXT_INSN (insn)) + { + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && reg_referenced_p (ni2dest, PATTERN (insn))) + { + for (link = LOG_LINKS (insn); link; + link = XEXP (link, 1)) + if (XEXP (link, 0) == i3) + XEXP (link, 0) = i1; + + break; + } + } + } + } + + /* Similarly, check for a case where we have a PARALLEL of two independent + SETs but we started with three insns. In this case, we can do the sets + as two separate insns. This case occurs when some SET allows two + other insns to combine, but the destination of that SET is still live. */ + + else if (i1 && insn_code_number < 0 && asm_noperands (newpat) < 0 + && GET_CODE (newpat) == PARALLEL + && XVECLEN (newpat, 0) == 2 + && GET_CODE (XVECEXP (newpat, 0, 0)) == SET + && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != ZERO_EXTRACT + && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != STRICT_LOW_PART + && GET_CODE (XVECEXP (newpat, 0, 1)) == SET + && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != ZERO_EXTRACT + && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != STRICT_LOW_PART + && ! use_crosses_set_p (SET_SRC (XVECEXP (newpat, 0, 1)), + INSN_CUID (i2)) + /* Don't pass sets with (USE (MEM ...)) dests to the following. */ + && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != USE + && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != USE + && ! reg_referenced_p (SET_DEST (XVECEXP (newpat, 0, 1)), + XVECEXP (newpat, 0, 0)) + && ! reg_referenced_p (SET_DEST (XVECEXP (newpat, 0, 0)), + XVECEXP (newpat, 0, 1))) + { + newi2pat = XVECEXP (newpat, 0, 1); + newpat = XVECEXP (newpat, 0, 0); + + i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes); + if (i2_code_number >= 0) + insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); + } + + /* If it still isn't recognized, fail and change things back the way they + were. */ + if ((insn_code_number < 0 + /* Is the result a reasonable ASM_OPERANDS? */ + && (! check_asm_operands (newpat) || added_sets_1 || added_sets_2))) + { + undo_all (); + return 0; + } + + /* If we had to change another insn, make sure it is valid also. */ + if (undobuf.other_insn) + { + rtx other_notes = REG_NOTES (undobuf.other_insn); + rtx other_pat = PATTERN (undobuf.other_insn); + rtx new_other_notes; + rtx note, next; + + other_code_number = recog_for_combine (&other_pat, undobuf.other_insn, + &new_other_notes); + + if (other_code_number < 0 && ! check_asm_operands (other_pat)) + { + undo_all (); + return 0; + } + + PATTERN (undobuf.other_insn) = other_pat; + + /* If any of the notes in OTHER_INSN were REG_UNUSED, ensure that they + are still valid. Then add any non-duplicate notes added by + recog_for_combine. */ + for (note = REG_NOTES (undobuf.other_insn); note; note = next) + { + next = XEXP (note, 1); + + if (REG_NOTE_KIND (note) == REG_UNUSED + && ! reg_set_p (XEXP (note, 0), PATTERN (undobuf.other_insn))) + { + if (GET_CODE (XEXP (note, 0)) == REG) + reg_n_deaths[REGNO (XEXP (note, 0))]--; + + remove_note (undobuf.other_insn, note); + } + } + + for (note = new_other_notes; note; note = XEXP (note, 1)) + if (GET_CODE (XEXP (note, 0)) == REG) + reg_n_deaths[REGNO (XEXP (note, 0))]++; + + distribute_notes (new_other_notes, undobuf.other_insn, + undobuf.other_insn, NULL_RTX, NULL_RTX, NULL_RTX); + } + + /* We now know that we can do this combination. Merge the insns and + update the status of registers and LOG_LINKS. */ + + { + rtx i3notes, i2notes, i1notes = 0; + rtx i3links, i2links, i1links = 0; + rtx midnotes = 0; + int all_adjacent = (next_real_insn (i2) == i3 + && (i1 == 0 || next_real_insn (i1) == i2)); + register int regno; + /* Compute which registers we expect to eliminate. */ + rtx elim_i2 = (newi2pat || i2dest_in_i2src || i2dest_in_i1src + ? 0 : i2dest); + rtx elim_i1 = i1 == 0 || i1dest_in_i1src ? 0 : i1dest; + + /* Get the old REG_NOTES and LOG_LINKS from all our insns and + clear them. */ + i3notes = REG_NOTES (i3), i3links = LOG_LINKS (i3); + i2notes = REG_NOTES (i2), i2links = LOG_LINKS (i2); + if (i1) + i1notes = REG_NOTES (i1), i1links = LOG_LINKS (i1); + + /* Ensure that we do not have something that should not be shared but + occurs multiple times in the new insns. Check this by first + resetting all the `used' flags and then copying anything is shared. */ + + reset_used_flags (i3notes); + reset_used_flags (i2notes); + reset_used_flags (i1notes); + reset_used_flags (newpat); + reset_used_flags (newi2pat); + if (undobuf.other_insn) + reset_used_flags (PATTERN (undobuf.other_insn)); + + i3notes = copy_rtx_if_shared (i3notes); + i2notes = copy_rtx_if_shared (i2notes); + i1notes = copy_rtx_if_shared (i1notes); + newpat = copy_rtx_if_shared (newpat); + newi2pat = copy_rtx_if_shared (newi2pat); + if (undobuf.other_insn) + reset_used_flags (PATTERN (undobuf.other_insn)); + + INSN_CODE (i3) = insn_code_number; + PATTERN (i3) = newpat; + if (undobuf.other_insn) + INSN_CODE (undobuf.other_insn) = other_code_number; + + /* We had one special case above where I2 had more than one set and + we replaced a destination of one of those sets with the destination + of I3. In that case, we have to update LOG_LINKS of insns later + in this basic block. Note that this (expensive) case is rare. */ + + if (GET_CODE (PATTERN (i2)) == PARALLEL) + for (i = 0; i < XVECLEN (PATTERN (i2), 0); i++) + if (GET_CODE (SET_DEST (XVECEXP (PATTERN (i2), 0, i))) == REG + && SET_DEST (XVECEXP (PATTERN (i2), 0, i)) != i2dest + && ! find_reg_note (i2, REG_UNUSED, + SET_DEST (XVECEXP (PATTERN (i2), 0, i)))) + { + register rtx insn; + + for (insn = NEXT_INSN (i2); insn; insn = NEXT_INSN (insn)) + { + if (insn != i3 && GET_RTX_CLASS (GET_CODE (insn)) == 'i') + for (link = LOG_LINKS (insn); link; link = XEXP (link, 1)) + if (XEXP (link, 0) == i2) + XEXP (link, 0) = i3; + + if (GET_CODE (insn) == CODE_LABEL + || GET_CODE (insn) == JUMP_INSN) + break; + } + } + + LOG_LINKS (i3) = 0; + REG_NOTES (i3) = 0; + LOG_LINKS (i2) = 0; + REG_NOTES (i2) = 0; + + if (newi2pat) + { + INSN_CODE (i2) = i2_code_number; + PATTERN (i2) = newi2pat; + } + else + { + PUT_CODE (i2, NOTE); + NOTE_LINE_NUMBER (i2) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (i2) = 0; + } + + if (i1) + { + LOG_LINKS (i1) = 0; + REG_NOTES (i1) = 0; + PUT_CODE (i1, NOTE); + NOTE_LINE_NUMBER (i1) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (i1) = 0; + } + + /* Get death notes for everything that is now used in either I3 or + I2 and used to die in a previous insn. */ + + move_deaths (newpat, i1 ? INSN_CUID (i1) : INSN_CUID (i2), i3, &midnotes); + if (newi2pat) + move_deaths (newi2pat, INSN_CUID (i1), i2, &midnotes); + + /* Distribute all the LOG_LINKS and REG_NOTES from I1, I2, and I3. */ + if (i3notes) + distribute_notes (i3notes, i3, i3, newi2pat ? i2 : NULL_RTX, + elim_i2, elim_i1); + if (i2notes) + distribute_notes (i2notes, i2, i3, newi2pat ? i2 : NULL_RTX, + elim_i2, elim_i1); + if (i1notes) + distribute_notes (i1notes, i1, i3, newi2pat ? i2 : NULL_RTX, + elim_i2, elim_i1); + if (midnotes) + distribute_notes (midnotes, NULL_RTX, i3, newi2pat ? i2 : NULL_RTX, + elim_i2, elim_i1); + + /* Distribute any notes added to I2 or I3 by recog_for_combine. We + know these are REG_UNUSED and want them to go to the desired insn, + so we always pass it as i3. We have not counted the notes in + reg_n_deaths yet, so we need to do so now. */ + + if (newi2pat && new_i2_notes) + { + for (temp = new_i2_notes; temp; temp = XEXP (temp, 1)) + if (GET_CODE (XEXP (temp, 0)) == REG) + reg_n_deaths[REGNO (XEXP (temp, 0))]++; + + distribute_notes (new_i2_notes, i2, i2, NULL_RTX, NULL_RTX, NULL_RTX); + } + + if (new_i3_notes) + { + for (temp = new_i3_notes; temp; temp = XEXP (temp, 1)) + if (GET_CODE (XEXP (temp, 0)) == REG) + reg_n_deaths[REGNO (XEXP (temp, 0))]++; + + distribute_notes (new_i3_notes, i3, i3, NULL_RTX, NULL_RTX, NULL_RTX); + } + + /* If I3DEST was used in I3SRC, it really died in I3. We may need to + put a REG_DEAD note for it somewhere. Similarly for I2 and I1. + Show an additional death due to the REG_DEAD note we make here. If + we discard it in distribute_notes, we will decrement it again. */ + + if (i3dest_killed) + { + if (GET_CODE (i3dest_killed) == REG) + reg_n_deaths[REGNO (i3dest_killed)]++; + + distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i3dest_killed, + NULL_RTX), + NULL_RTX, i3, newi2pat ? i2 : NULL_RTX, + NULL_RTX, NULL_RTX); + } + + /* For I2 and I1, we have to be careful. If NEWI2PAT exists and sets + I2DEST or I1DEST, the death must be somewhere before I2, not I3. If + we passed I3 in that case, it might delete I2. */ + + if (i2dest_in_i2src) + { + if (GET_CODE (i2dest) == REG) + reg_n_deaths[REGNO (i2dest)]++; + + if (newi2pat && reg_set_p (i2dest, newi2pat)) + distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i2dest, NULL_RTX), + NULL_RTX, i2, NULL_RTX, NULL_RTX, NULL_RTX); + else + distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i2dest, NULL_RTX), + NULL_RTX, i3, newi2pat ? i2 : NULL_RTX, + NULL_RTX, NULL_RTX); + } + + if (i1dest_in_i1src) + { + if (GET_CODE (i1dest) == REG) + reg_n_deaths[REGNO (i1dest)]++; + + if (newi2pat && reg_set_p (i1dest, newi2pat)) + distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i1dest, NULL_RTX), + NULL_RTX, i2, NULL_RTX, NULL_RTX, NULL_RTX); + else + distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i1dest, NULL_RTX), + NULL_RTX, i3, newi2pat ? i2 : NULL_RTX, + NULL_RTX, NULL_RTX); + } + + distribute_links (i3links); + distribute_links (i2links); + distribute_links (i1links); + + if (GET_CODE (i2dest) == REG) + { + rtx link; + rtx i2_insn = 0, i2_val = 0, set; + + /* The insn that used to set this register doesn't exist, and + this life of the register may not exist either. See if one of + I3's links points to an insn that sets I2DEST. If it does, + that is now the last known value for I2DEST. If we don't update + this and I2 set the register to a value that depended on its old + contents, we will get confused. If this insn is used, thing + will be set correctly in combine_instructions. */ + + for (link = LOG_LINKS (i3); link; link = XEXP (link, 1)) + if ((set = single_set (XEXP (link, 0))) != 0 + && rtx_equal_p (i2dest, SET_DEST (set))) + i2_insn = XEXP (link, 0), i2_val = SET_SRC (set); + + record_value_for_reg (i2dest, i2_insn, i2_val); + + /* If the reg formerly set in I2 died only once and that was in I3, + zero its use count so it won't make `reload' do any work. */ + if (! added_sets_2 && newi2pat == 0) + { + regno = REGNO (i2dest); + reg_n_sets[regno]--; + if (reg_n_sets[regno] == 0 + && ! (basic_block_live_at_start[0][regno / REGSET_ELT_BITS] + & ((REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS)))) + reg_n_refs[regno] = 0; + } + } + + if (i1 && GET_CODE (i1dest) == REG) + { + rtx link; + rtx i1_insn = 0, i1_val = 0, set; + + for (link = LOG_LINKS (i3); link; link = XEXP (link, 1)) + if ((set = single_set (XEXP (link, 0))) != 0 + && rtx_equal_p (i1dest, SET_DEST (set))) + i1_insn = XEXP (link, 0), i1_val = SET_SRC (set); + + record_value_for_reg (i1dest, i1_insn, i1_val); + + regno = REGNO (i1dest); + if (! added_sets_1) + { + reg_n_sets[regno]--; + if (reg_n_sets[regno] == 0 + && ! (basic_block_live_at_start[0][regno / REGSET_ELT_BITS] + & ((REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS)))) + reg_n_refs[regno] = 0; + } + } + + /* Update reg_nonzero_bits et al for any changes that may have been made + to this insn. */ + + note_stores (newpat, set_nonzero_bits_and_sign_copies); + if (newi2pat) + note_stores (newi2pat, set_nonzero_bits_and_sign_copies); + + /* If I3 is now an unconditional jump, ensure that it has a + BARRIER following it since it may have initially been a + conditional jump. It may also be the last nonnote insn. */ + + if ((GET_CODE (newpat) == RETURN || simplejump_p (i3)) + && ((temp = next_nonnote_insn (i3)) == NULL_RTX + || GET_CODE (temp) != BARRIER)) + emit_barrier_after (i3); + } + + combine_successes++; + + return newi2pat ? i2 : i3; +} + +/* Undo all the modifications recorded in undobuf. */ + +static void +undo_all () +{ + register int i; + if (undobuf.num_undo > MAX_UNDO) + undobuf.num_undo = MAX_UNDO; + for (i = undobuf.num_undo - 1; i >= 0; i--) + { + if (undobuf.undo[i].is_int) + *undobuf.undo[i].where.i = undobuf.undo[i].old_contents.i; + else + *undobuf.undo[i].where.rtx = undobuf.undo[i].old_contents.rtx; + + } + + obfree (undobuf.storage); + undobuf.num_undo = 0; +} + +/* Find the innermost point within the rtx at LOC, possibly LOC itself, + where we have an arithmetic expression and return that point. LOC will + be inside INSN. + + try_combine will call this function to see if an insn can be split into + two insns. */ + +static rtx * +find_split_point (loc, insn) + rtx *loc; + rtx insn; +{ + rtx x = *loc; + enum rtx_code code = GET_CODE (x); + rtx *split; + int len = 0, pos, unsignedp; + rtx inner; + + /* First special-case some codes. */ + switch (code) + { + case SUBREG: +#ifdef INSN_SCHEDULING + /* If we are making a paradoxical SUBREG invalid, it becomes a split + point. */ + if (GET_CODE (SUBREG_REG (x)) == MEM) + return loc; +#endif + return find_split_point (&SUBREG_REG (x), insn); + + case MEM: +#ifdef HAVE_lo_sum + /* If we have (mem (const ..)) or (mem (symbol_ref ...)), split it + using LO_SUM and HIGH. */ + if (GET_CODE (XEXP (x, 0)) == CONST + || GET_CODE (XEXP (x, 0)) == SYMBOL_REF) + { + SUBST (XEXP (x, 0), + gen_rtx_combine (LO_SUM, Pmode, + gen_rtx_combine (HIGH, Pmode, XEXP (x, 0)), + XEXP (x, 0))); + return &XEXP (XEXP (x, 0), 0); + } +#endif + + /* If we have a PLUS whose second operand is a constant and the + address is not valid, perhaps will can split it up using + the machine-specific way to split large constants. We use + the first psuedo-reg (one of the virtual regs) as a placeholder; + it will not remain in the result. */ + if (GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && ! memory_address_p (GET_MODE (x), XEXP (x, 0))) + { + rtx reg = regno_reg_rtx[FIRST_PSEUDO_REGISTER]; + rtx seq = split_insns (gen_rtx (SET, VOIDmode, reg, XEXP (x, 0)), + subst_insn); + + /* This should have produced two insns, each of which sets our + placeholder. If the source of the second is a valid address, + we can make put both sources together and make a split point + in the middle. */ + + if (seq && XVECLEN (seq, 0) == 2 + && GET_CODE (XVECEXP (seq, 0, 0)) == INSN + && GET_CODE (PATTERN (XVECEXP (seq, 0, 0))) == SET + && SET_DEST (PATTERN (XVECEXP (seq, 0, 0))) == reg + && ! reg_mentioned_p (reg, + SET_SRC (PATTERN (XVECEXP (seq, 0, 0)))) + && GET_CODE (XVECEXP (seq, 0, 1)) == INSN + && GET_CODE (PATTERN (XVECEXP (seq, 0, 1))) == SET + && SET_DEST (PATTERN (XVECEXP (seq, 0, 1))) == reg + && memory_address_p (GET_MODE (x), + SET_SRC (PATTERN (XVECEXP (seq, 0, 1))))) + { + rtx src1 = SET_SRC (PATTERN (XVECEXP (seq, 0, 0))); + rtx src2 = SET_SRC (PATTERN (XVECEXP (seq, 0, 1))); + + /* Replace the placeholder in SRC2 with SRC1. If we can + find where in SRC2 it was placed, that can become our + split point and we can replace this address with SRC2. + Just try two obvious places. */ + + src2 = replace_rtx (src2, reg, src1); + split = 0; + if (XEXP (src2, 0) == src1) + split = &XEXP (src2, 0); + else if (GET_RTX_FORMAT (GET_CODE (XEXP (src2, 0)))[0] == 'e' + && XEXP (XEXP (src2, 0), 0) == src1) + split = &XEXP (XEXP (src2, 0), 0); + + if (split) + { + SUBST (XEXP (x, 0), src2); + return split; + } + } + + /* If that didn't work, perhaps the first operand is complex and + needs to be computed separately, so make a split point there. + This will occur on machines that just support REG + CONST + and have a constant moved through some previous computation. */ + + else if (GET_RTX_CLASS (GET_CODE (XEXP (XEXP (x, 0), 0))) != 'o' + && ! (GET_CODE (XEXP (XEXP (x, 0), 0)) == SUBREG + && (GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (XEXP (x, 0), 0)))) + == 'o'))) + return &XEXP (XEXP (x, 0), 0); + } + break; + + case SET: +#ifdef HAVE_cc0 + /* If SET_DEST is CC0 and SET_SRC is not an operand, a COMPARE, or a + ZERO_EXTRACT, the most likely reason why this doesn't match is that + we need to put the operand into a register. So split at that + point. */ + + if (SET_DEST (x) == cc0_rtx + && GET_CODE (SET_SRC (x)) != COMPARE + && GET_CODE (SET_SRC (x)) != ZERO_EXTRACT + && GET_RTX_CLASS (GET_CODE (SET_SRC (x))) != 'o' + && ! (GET_CODE (SET_SRC (x)) == SUBREG + && GET_RTX_CLASS (GET_CODE (SUBREG_REG (SET_SRC (x)))) == 'o')) + return &SET_SRC (x); +#endif + + /* See if we can split SET_SRC as it stands. */ + split = find_split_point (&SET_SRC (x), insn); + if (split && split != &SET_SRC (x)) + return split; + + /* See if this is a bitfield assignment with everything constant. If + so, this is an IOR of an AND, so split it into that. */ + if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT + && (GET_MODE_BITSIZE (GET_MODE (XEXP (SET_DEST (x), 0))) + <= HOST_BITS_PER_WIDE_INT) + && GET_CODE (XEXP (SET_DEST (x), 1)) == CONST_INT + && GET_CODE (XEXP (SET_DEST (x), 2)) == CONST_INT + && GET_CODE (SET_SRC (x)) == CONST_INT + && ((INTVAL (XEXP (SET_DEST (x), 1)) + + INTVAL (XEXP (SET_DEST (x), 2))) + <= GET_MODE_BITSIZE (GET_MODE (XEXP (SET_DEST (x), 0)))) + && ! side_effects_p (XEXP (SET_DEST (x), 0))) + { + int pos = INTVAL (XEXP (SET_DEST (x), 2)); + int len = INTVAL (XEXP (SET_DEST (x), 1)); + int src = INTVAL (SET_SRC (x)); + rtx dest = XEXP (SET_DEST (x), 0); + enum machine_mode mode = GET_MODE (dest); + unsigned HOST_WIDE_INT mask = ((HOST_WIDE_INT) 1 << len) - 1; + +#if BITS_BIG_ENDIAN + pos = GET_MODE_BITSIZE (mode) - len - pos; +#endif + + if (src == mask) + SUBST (SET_SRC (x), + gen_binary (IOR, mode, dest, GEN_INT (src << pos))); + else + SUBST (SET_SRC (x), + gen_binary (IOR, mode, + gen_binary (AND, mode, dest, + GEN_INT (~ (mask << pos) + & GET_MODE_MASK (mode))), + GEN_INT (src << pos))); + + SUBST (SET_DEST (x), dest); + + split = find_split_point (&SET_SRC (x), insn); + if (split && split != &SET_SRC (x)) + return split; + } + + /* Otherwise, see if this is an operation that we can split into two. + If so, try to split that. */ + code = GET_CODE (SET_SRC (x)); + + switch (code) + { + case AND: + /* If we are AND'ing with a large constant that is only a single + bit and the result is only being used in a context where we + need to know if it is zero or non-zero, replace it with a bit + extraction. This will avoid the large constant, which might + have taken more than one insn to make. If the constant were + not a valid argument to the AND but took only one insn to make, + this is no worse, but if it took more than one insn, it will + be better. */ + + if (GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT + && GET_CODE (XEXP (SET_SRC (x), 0)) == REG + && (pos = exact_log2 (INTVAL (XEXP (SET_SRC (x), 1)))) >= 7 + && GET_CODE (SET_DEST (x)) == REG + && (split = find_single_use (SET_DEST (x), insn, NULL_PTR)) != 0 + && (GET_CODE (*split) == EQ || GET_CODE (*split) == NE) + && XEXP (*split, 0) == SET_DEST (x) + && XEXP (*split, 1) == const0_rtx) + { + SUBST (SET_SRC (x), + make_extraction (GET_MODE (SET_DEST (x)), + XEXP (SET_SRC (x), 0), + pos, NULL_RTX, 1, 1, 0, 0)); + return find_split_point (loc, insn); + } + break; + + case SIGN_EXTEND: + inner = XEXP (SET_SRC (x), 0); + pos = 0; + len = GET_MODE_BITSIZE (GET_MODE (inner)); + unsignedp = 0; + break; + + case SIGN_EXTRACT: + case ZERO_EXTRACT: + if (GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT + && GET_CODE (XEXP (SET_SRC (x), 2)) == CONST_INT) + { + inner = XEXP (SET_SRC (x), 0); + len = INTVAL (XEXP (SET_SRC (x), 1)); + pos = INTVAL (XEXP (SET_SRC (x), 2)); + +#if BITS_BIG_ENDIAN + pos = GET_MODE_BITSIZE (GET_MODE (inner)) - len - pos; +#endif + unsignedp = (code == ZERO_EXTRACT); + } + break; + } + + if (len && pos >= 0 && pos + len <= GET_MODE_BITSIZE (GET_MODE (inner))) + { + enum machine_mode mode = GET_MODE (SET_SRC (x)); + + /* For unsigned, we have a choice of a shift followed by an + AND or two shifts. Use two shifts for field sizes where the + constant might be too large. We assume here that we can + always at least get 8-bit constants in an AND insn, which is + true for every current RISC. */ + + if (unsignedp && len <= 8) + { + SUBST (SET_SRC (x), + gen_rtx_combine + (AND, mode, + gen_rtx_combine (LSHIFTRT, mode, + gen_lowpart_for_combine (mode, inner), + GEN_INT (pos)), + GEN_INT (((HOST_WIDE_INT) 1 << len) - 1))); + + split = find_split_point (&SET_SRC (x), insn); + if (split && split != &SET_SRC (x)) + return split; + } + else + { + SUBST (SET_SRC (x), + gen_rtx_combine + (unsignedp ? LSHIFTRT : ASHIFTRT, mode, + gen_rtx_combine (ASHIFT, mode, + gen_lowpart_for_combine (mode, inner), + GEN_INT (GET_MODE_BITSIZE (mode) + - len - pos)), + GEN_INT (GET_MODE_BITSIZE (mode) - len))); + + split = find_split_point (&SET_SRC (x), insn); + if (split && split != &SET_SRC (x)) + return split; + } + } + + /* See if this is a simple operation with a constant as the second + operand. It might be that this constant is out of range and hence + could be used as a split point. */ + if ((GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '2' + || GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == 'c' + || GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '<') + && CONSTANT_P (XEXP (SET_SRC (x), 1)) + && (GET_RTX_CLASS (GET_CODE (XEXP (SET_SRC (x), 0))) == 'o' + || (GET_CODE (XEXP (SET_SRC (x), 0)) == SUBREG + && (GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (SET_SRC (x), 0)))) + == 'o')))) + return &XEXP (SET_SRC (x), 1); + + /* Finally, see if this is a simple operation with its first operand + not in a register. The operation might require this operand in a + register, so return it as a split point. We can always do this + because if the first operand were another operation, we would have + already found it as a split point. */ + if ((GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '2' + || GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == 'c' + || GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '<' + || GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '1') + && ! register_operand (XEXP (SET_SRC (x), 0), VOIDmode)) + return &XEXP (SET_SRC (x), 0); + + return 0; + + case AND: + case IOR: + /* We write NOR as (and (not A) (not B)), but if we don't have a NOR, + it is better to write this as (not (ior A B)) so we can split it. + Similarly for IOR. */ + if (GET_CODE (XEXP (x, 0)) == NOT && GET_CODE (XEXP (x, 1)) == NOT) + { + SUBST (*loc, + gen_rtx_combine (NOT, GET_MODE (x), + gen_rtx_combine (code == IOR ? AND : IOR, + GET_MODE (x), + XEXP (XEXP (x, 0), 0), + XEXP (XEXP (x, 1), 0)))); + return find_split_point (loc, insn); + } + + /* Many RISC machines have a large set of logical insns. If the + second operand is a NOT, put it first so we will try to split the + other operand first. */ + if (GET_CODE (XEXP (x, 1)) == NOT) + { + rtx tem = XEXP (x, 0); + SUBST (XEXP (x, 0), XEXP (x, 1)); + SUBST (XEXP (x, 1), tem); + } + break; + } + + /* Otherwise, select our actions depending on our rtx class. */ + switch (GET_RTX_CLASS (code)) + { + case 'b': /* This is ZERO_EXTRACT and SIGN_EXTRACT. */ + case '3': + split = find_split_point (&XEXP (x, 2), insn); + if (split) + return split; + /* ... fall through ... */ + case '2': + case 'c': + case '<': + split = find_split_point (&XEXP (x, 1), insn); + if (split) + return split; + /* ... fall through ... */ + case '1': + /* Some machines have (and (shift ...) ...) insns. If X is not + an AND, but XEXP (X, 0) is, use it as our split point. */ + if (GET_CODE (x) != AND && GET_CODE (XEXP (x, 0)) == AND) + return &XEXP (x, 0); + + split = find_split_point (&XEXP (x, 0), insn); + if (split) + return split; + return loc; + } + + /* Otherwise, we don't have a split point. */ + return 0; +} + +/* Throughout X, replace FROM with TO, and return the result. + The result is TO if X is FROM; + otherwise the result is X, but its contents may have been modified. + If they were modified, a record was made in undobuf so that + undo_all will (among other things) return X to its original state. + + If the number of changes necessary is too much to record to undo, + the excess changes are not made, so the result is invalid. + The changes already made can still be undone. + undobuf.num_undo is incremented for such changes, so by testing that + the caller can tell whether the result is valid. + + `n_occurrences' is incremented each time FROM is replaced. + + IN_DEST is non-zero if we are processing the SET_DEST of a SET. + + UNIQUE_COPY is non-zero if each substitution must be unique. We do this + by copying if `n_occurrences' is non-zero. */ + +static rtx +subst (x, from, to, in_dest, unique_copy) + register rtx x, from, to; + int in_dest; + int unique_copy; +{ + register char *fmt; + register int len, i; + register enum rtx_code code = GET_CODE (x), orig_code = code; + rtx temp; + enum machine_mode mode = GET_MODE (x); + enum machine_mode op0_mode = VOIDmode; + rtx other_insn; + rtx *cc_use; + int n_restarts = 0; + +/* FAKE_EXTEND_SAFE_P (MODE, FROM) is 1 if (subreg:MODE FROM 0) is a safe + replacement for (zero_extend:MODE FROM) or (sign_extend:MODE FROM). + If it is 0, that cannot be done. We can now do this for any MEM + because (SUBREG (MEM...)) is guaranteed to cause the MEM to be reloaded. + If not for that, MEM's would very rarely be safe. */ + +/* Reject MODEs bigger than a word, because we might not be able + to reference a two-register group starting with an arbitrary register + (and currently gen_lowpart might crash for a SUBREG). */ + +#define FAKE_EXTEND_SAFE_P(MODE, FROM) \ + (GET_MODE_SIZE (MODE) <= UNITS_PER_WORD) + +/* Two expressions are equal if they are identical copies of a shared + RTX or if they are both registers with the same register number + and mode. */ + +#define COMBINE_RTX_EQUAL_P(X,Y) \ + ((X) == (Y) \ + || (GET_CODE (X) == REG && GET_CODE (Y) == REG \ + && REGNO (X) == REGNO (Y) && GET_MODE (X) == GET_MODE (Y))) + + if (! in_dest && COMBINE_RTX_EQUAL_P (x, from)) + { + n_occurrences++; + return (unique_copy && n_occurrences > 1 ? copy_rtx (to) : to); + } + + /* If X and FROM are the same register but different modes, they will + not have been seen as equal above. However, flow.c will make a + LOG_LINKS entry for that case. If we do nothing, we will try to + rerecognize our original insn and, when it succeeds, we will + delete the feeding insn, which is incorrect. + + So force this insn not to match in this (rare) case. */ + if (! in_dest && code == REG && GET_CODE (from) == REG + && REGNO (x) == REGNO (from)) + return gen_rtx (CLOBBER, GET_MODE (x), const0_rtx); + + /* If this is an object, we are done unless it is a MEM or LO_SUM, both + of which may contain things that can be combined. */ + if (code != MEM && code != LO_SUM && GET_RTX_CLASS (code) == 'o') + return x; + + /* It is possible to have a subexpression appear twice in the insn. + Suppose that FROM is a register that appears within TO. + Then, after that subexpression has been scanned once by `subst', + the second time it is scanned, TO may be found. If we were + to scan TO here, we would find FROM within it and create a + self-referent rtl structure which is completely wrong. */ + if (COMBINE_RTX_EQUAL_P (x, to)) + return to; + + len = GET_RTX_LENGTH (code); + fmt = GET_RTX_FORMAT (code); + + /* We don't need to process a SET_DEST that is a register, CC0, or PC, so + set up to skip this common case. All other cases where we want to + suppress replacing something inside a SET_SRC are handled via the + IN_DEST operand. */ + if (code == SET + && (GET_CODE (SET_DEST (x)) == REG + || GET_CODE (SET_DEST (x)) == CC0 + || GET_CODE (SET_DEST (x)) == PC)) + fmt = "ie"; + + /* Get the mode of operand 0 in case X is now a SIGN_EXTEND of a constant. */ + if (fmt[0] == 'e') + op0_mode = GET_MODE (XEXP (x, 0)); + + for (i = 0; i < len; i++) + { + if (fmt[i] == 'E') + { + register int j; + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + { + register rtx new; + if (COMBINE_RTX_EQUAL_P (XVECEXP (x, i, j), from)) + { + new = (unique_copy && n_occurrences ? copy_rtx (to) : to); + n_occurrences++; + } + else + { + new = subst (XVECEXP (x, i, j), from, to, 0, unique_copy); + + /* If this substitution failed, this whole thing fails. */ + if (GET_CODE (new) == CLOBBER && XEXP (new, 0) == const0_rtx) + return new; + } + + SUBST (XVECEXP (x, i, j), new); + } + } + else if (fmt[i] == 'e') + { + register rtx new; + + if (COMBINE_RTX_EQUAL_P (XEXP (x, i), from)) + { + new = (unique_copy && n_occurrences ? copy_rtx (to) : to); + n_occurrences++; + } + else + /* If we are in a SET_DEST, suppress most cases unless we + have gone inside a MEM, in which case we want to + simplify the address. We assume here that things that + are actually part of the destination have their inner + parts in the first expression. This is true for SUBREG, + STRICT_LOW_PART, and ZERO_EXTRACT, which are the only + things aside from REG and MEM that should appear in a + SET_DEST. */ + new = subst (XEXP (x, i), from, to, + (((in_dest + && (code == SUBREG || code == STRICT_LOW_PART + || code == ZERO_EXTRACT)) + || code == SET) + && i == 0), unique_copy); + + /* If we found that we will have to reject this combination, + indicate that by returning the CLOBBER ourselves, rather than + an expression containing it. This will speed things up as + well as prevent accidents where two CLOBBERs are considered + to be equal, thus producing an incorrect simplification. */ + + if (GET_CODE (new) == CLOBBER && XEXP (new, 0) == const0_rtx) + return new; + + SUBST (XEXP (x, i), new); + } + } + + /* We come back to here if we have replaced the expression with one of + a different code and it is likely that further simplification will be + possible. */ + + restart: + + /* If we have restarted more than 4 times, we are probably looping, so + give up. */ + if (++n_restarts > 4) + return x; + + /* If we are restarting at all, it means that we no longer know the + original mode of operand 0 (since we have probably changed the + form of X). */ + + if (n_restarts > 1) + op0_mode = VOIDmode; + + code = GET_CODE (x); + + /* If this is a commutative operation, put a constant last and a complex + expression first. We don't need to do this for comparisons here. */ + if (GET_RTX_CLASS (code) == 'c' + && ((CONSTANT_P (XEXP (x, 0)) && GET_CODE (XEXP (x, 1)) != CONST_INT) + || (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == 'o' + && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) != 'o') + || (GET_CODE (XEXP (x, 0)) == SUBREG + && GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (x, 0)))) == 'o' + && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) != 'o'))) + { + temp = XEXP (x, 0); + SUBST (XEXP (x, 0), XEXP (x, 1)); + SUBST (XEXP (x, 1), temp); + } + + /* If this is a PLUS, MINUS, or MULT, and the first operand is the + sign extension of a PLUS with a constant, reverse the order of the sign + extension and the addition. Note that this not the same as the original + code, but overflow is undefined for signed values. Also note that the + PLUS will have been partially moved "inside" the sign-extension, so that + the first operand of X will really look like: + (ashiftrt (plus (ashift A C4) C5) C4). + We convert this to + (plus (ashiftrt (ashift A C4) C2) C4) + and replace the first operand of X with that expression. Later parts + of this function may simplify the expression further. + + For example, if we start with (mult (sign_extend (plus A C1)) C2), + we swap the SIGN_EXTEND and PLUS. Later code will apply the + distributive law to produce (plus (mult (sign_extend X) C1) C3). + + We do this to simplify address expressions. */ + + if ((code == PLUS || code == MINUS || code == MULT) + && GET_CODE (XEXP (x, 0)) == ASHIFTRT + && GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS + && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == ASHIFT + && GET_CODE (XEXP (XEXP (XEXP (XEXP (x, 0), 0), 0), 1)) == CONST_INT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && XEXP (XEXP (XEXP (XEXP (x, 0), 0), 0), 1) == XEXP (XEXP (x, 0), 1) + && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT + && (temp = simplify_binary_operation (ASHIFTRT, mode, + XEXP (XEXP (XEXP (x, 0), 0), 1), + XEXP (XEXP (x, 0), 1))) != 0) + { + rtx new + = simplify_shift_const (NULL_RTX, ASHIFT, mode, + XEXP (XEXP (XEXP (XEXP (x, 0), 0), 0), 0), + INTVAL (XEXP (XEXP (x, 0), 1))); + + new = simplify_shift_const (NULL_RTX, ASHIFTRT, mode, new, + INTVAL (XEXP (XEXP (x, 0), 1))); + + SUBST (XEXP (x, 0), gen_binary (PLUS, mode, new, temp)); + } + + /* If this is a simple operation applied to an IF_THEN_ELSE, try + applying it to the arms of the IF_THEN_ELSE. This often simplifies + things. Don't deal with operations that change modes here. */ + + if ((GET_RTX_CLASS (code) == '2' || GET_RTX_CLASS (code) == 'c') + && GET_CODE (XEXP (x, 0)) == IF_THEN_ELSE) + { + /* Don't do this by using SUBST inside X since we might be messing + up a shared expression. */ + rtx cond = XEXP (XEXP (x, 0), 0); + rtx t_arm = subst (gen_binary (code, mode, XEXP (XEXP (x, 0), 1), + XEXP (x, 1)), + pc_rtx, pc_rtx, 0, 0); + rtx f_arm = subst (gen_binary (code, mode, XEXP (XEXP (x, 0), 2), + XEXP (x, 1)), + pc_rtx, pc_rtx, 0, 0); + + + x = gen_rtx (IF_THEN_ELSE, mode, cond, t_arm, f_arm); + goto restart; + } + + else if (GET_RTX_CLASS (code) == '1' + && GET_CODE (XEXP (x, 0)) == IF_THEN_ELSE + && GET_MODE (XEXP (x, 0)) == mode) + { + rtx cond = XEXP (XEXP (x, 0), 0); + rtx t_arm = subst (gen_unary (code, mode, XEXP (XEXP (x, 0), 1)), + pc_rtx, pc_rtx, 0, 0); + rtx f_arm = subst (gen_unary (code, mode, XEXP (XEXP (x, 0), 2)), + pc_rtx, pc_rtx, 0, 0); + + x = gen_rtx_combine (IF_THEN_ELSE, mode, cond, t_arm, f_arm); + goto restart; + } + + /* Try to fold this expression in case we have constants that weren't + present before. */ + temp = 0; + switch (GET_RTX_CLASS (code)) + { + case '1': + temp = simplify_unary_operation (code, mode, XEXP (x, 0), op0_mode); + break; + case '<': + temp = simplify_relational_operation (code, op0_mode, + XEXP (x, 0), XEXP (x, 1)); +#ifdef FLOAT_STORE_FLAG_VALUE + if (temp != 0 && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) + temp = ((temp == const0_rtx) ? CONST0_RTX (GET_MODE (x)) + : immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, GET_MODE (x))); +#endif + break; + case 'c': + case '2': + temp = simplify_binary_operation (code, mode, XEXP (x, 0), XEXP (x, 1)); + break; + case 'b': + case '3': + temp = simplify_ternary_operation (code, mode, op0_mode, XEXP (x, 0), + XEXP (x, 1), XEXP (x, 2)); + break; + } + + if (temp) + x = temp, code = GET_CODE (temp); + + /* First see if we can apply the inverse distributive law. */ + if (code == PLUS || code == MINUS || code == IOR || code == XOR) + { + x = apply_distributive_law (x); + code = GET_CODE (x); + } + + /* If CODE is an associative operation not otherwise handled, see if we + can associate some operands. This can win if they are constants or + if they are logically related (i.e. (a & b) & a. */ + if ((code == PLUS || code == MINUS + || code == MULT || code == AND || code == IOR || code == XOR + || code == DIV || code == UDIV + || code == SMAX || code == SMIN || code == UMAX || code == UMIN) + && GET_MODE_CLASS (mode) == MODE_INT) + { + if (GET_CODE (XEXP (x, 0)) == code) + { + rtx other = XEXP (XEXP (x, 0), 0); + rtx inner_op0 = XEXP (XEXP (x, 0), 1); + rtx inner_op1 = XEXP (x, 1); + rtx inner; + + /* Make sure we pass the constant operand if any as the second + one if this is a commutative operation. */ + if (CONSTANT_P (inner_op0) && GET_RTX_CLASS (code) == 'c') + { + rtx tem = inner_op0; + inner_op0 = inner_op1; + inner_op1 = tem; + } + inner = simplify_binary_operation (code == MINUS ? PLUS + : code == DIV ? MULT + : code == UDIV ? MULT + : code, + mode, inner_op0, inner_op1); + + /* For commutative operations, try the other pair if that one + didn't simplify. */ + if (inner == 0 && GET_RTX_CLASS (code) == 'c') + { + other = XEXP (XEXP (x, 0), 1); + inner = simplify_binary_operation (code, mode, + XEXP (XEXP (x, 0), 0), + XEXP (x, 1)); + } + + if (inner) + { + x = gen_binary (code, mode, other, inner); + goto restart; + + } + } + } + + /* A little bit of algebraic simplification here. */ + switch (code) + { + case MEM: + /* Ensure that our address has any ASHIFTs converted to MULT in case + address-recognizing predicates are called later. */ + temp = make_compound_operation (XEXP (x, 0), MEM); + SUBST (XEXP (x, 0), temp); + break; + + case SUBREG: + /* (subreg:A (mem:B X) N) becomes a modified MEM unless the SUBREG + is paradoxical. If we can't do that safely, then it becomes + something nonsensical so that this combination won't take place. */ + + if (GET_CODE (SUBREG_REG (x)) == MEM + && (GET_MODE_SIZE (mode) + <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))) + { + rtx inner = SUBREG_REG (x); + int endian_offset = 0; + /* Don't change the mode of the MEM + if that would change the meaning of the address. */ + if (MEM_VOLATILE_P (SUBREG_REG (x)) + || mode_dependent_address_p (XEXP (inner, 0))) + return gen_rtx (CLOBBER, mode, const0_rtx); + +#if BYTES_BIG_ENDIAN + if (GET_MODE_SIZE (mode) < UNITS_PER_WORD) + endian_offset += UNITS_PER_WORD - GET_MODE_SIZE (mode); + if (GET_MODE_SIZE (GET_MODE (inner)) < UNITS_PER_WORD) + endian_offset -= UNITS_PER_WORD - GET_MODE_SIZE (GET_MODE (inner)); +#endif + /* Note if the plus_constant doesn't make a valid address + then this combination won't be accepted. */ + x = gen_rtx (MEM, mode, + plus_constant (XEXP (inner, 0), + (SUBREG_WORD (x) * UNITS_PER_WORD + + endian_offset))); + MEM_VOLATILE_P (x) = MEM_VOLATILE_P (inner); + RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (inner); + MEM_IN_STRUCT_P (x) = MEM_IN_STRUCT_P (inner); + return x; + } + + /* If we are in a SET_DEST, these other cases can't apply. */ + if (in_dest) + return x; + + /* Changing mode twice with SUBREG => just change it once, + or not at all if changing back to starting mode. */ + if (GET_CODE (SUBREG_REG (x)) == SUBREG) + { + if (mode == GET_MODE (SUBREG_REG (SUBREG_REG (x))) + && SUBREG_WORD (x) == 0 && SUBREG_WORD (SUBREG_REG (x)) == 0) + return SUBREG_REG (SUBREG_REG (x)); + + SUBST_INT (SUBREG_WORD (x), + SUBREG_WORD (x) + SUBREG_WORD (SUBREG_REG (x))); + SUBST (SUBREG_REG (x), SUBREG_REG (SUBREG_REG (x))); + } + + /* SUBREG of a hard register => just change the register number + and/or mode. If the hard register is not valid in that mode, + suppress this combination. If the hard register is the stack, + frame, or argument pointer, leave this as a SUBREG. */ + + if (GET_CODE (SUBREG_REG (x)) == REG + && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER + && REGNO (SUBREG_REG (x)) != FRAME_POINTER_REGNUM +#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + && REGNO (SUBREG_REG (x)) != ARG_POINTER_REGNUM +#endif + && REGNO (SUBREG_REG (x)) != STACK_POINTER_REGNUM) + { + if (HARD_REGNO_MODE_OK (REGNO (SUBREG_REG (x)) + SUBREG_WORD (x), + mode)) + return gen_rtx (REG, mode, + REGNO (SUBREG_REG (x)) + SUBREG_WORD (x)); + else + return gen_rtx (CLOBBER, mode, const0_rtx); + } + + /* For a constant, try to pick up the part we want. Handle a full + word and low-order part. Only do this if we are narrowing + the constant; if it is being widened, we have no idea what + the extra bits will have been set to. */ + + if (CONSTANT_P (SUBREG_REG (x)) && op0_mode != VOIDmode + && GET_MODE_SIZE (mode) == UNITS_PER_WORD + && GET_MODE_SIZE (op0_mode) < UNITS_PER_WORD + && GET_MODE_CLASS (mode) == MODE_INT) + { + temp = operand_subword (SUBREG_REG (x), SUBREG_WORD (x), + 0, op0_mode); + if (temp) + return temp; + } + + /* If we want a subreg of a constant, at offset 0, + take the low bits. On a little-endian machine, that's + always valid. On a big-endian machine, it's valid + only if the constant's mode fits in one word. */ + if (CONSTANT_P (SUBREG_REG (x)) && subreg_lowpart_p (x) + && GET_MODE_SIZE (mode) < GET_MODE_SIZE (op0_mode) +#if WORDS_BIG_ENDIAN + && GET_MODE_BITSIZE (op0_mode) <= BITS_PER_WORD +#endif + ) + return gen_lowpart_for_combine (mode, SUBREG_REG (x)); + + /* If we are narrowing the object, we need to see if we can simplify + the expression for the object knowing that we only need the + low-order bits. */ + + if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) + && subreg_lowpart_p (x)) + return force_to_mode (SUBREG_REG (x), mode, GET_MODE_BITSIZE (mode), + NULL_RTX); + break; + + case NOT: + /* (not (plus X -1)) can become (neg X). */ + if (GET_CODE (XEXP (x, 0)) == PLUS + && XEXP (XEXP (x, 0), 1) == constm1_rtx) + { + x = gen_rtx_combine (NEG, mode, XEXP (XEXP (x, 0), 0)); + goto restart; + } + + /* Similarly, (not (neg X)) is (plus X -1). */ + if (GET_CODE (XEXP (x, 0)) == NEG) + { + x = gen_rtx_combine (PLUS, mode, XEXP (XEXP (x, 0), 0), constm1_rtx); + goto restart; + } + + /* (not (xor X C)) for C constant is (xor X D) with D = ~ C. */ + if (GET_CODE (XEXP (x, 0)) == XOR + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && (temp = simplify_unary_operation (NOT, mode, + XEXP (XEXP (x, 0), 1), + mode)) != 0) + { + SUBST (XEXP (XEXP (x, 0), 1), temp); + return XEXP (x, 0); + } + + /* (not (ashift 1 X)) is (rotate ~1 X). We used to do this for operands + other than 1, but that is not valid. We could do a similar + simplification for (not (lshiftrt C X)) where C is just the sign bit, + but this doesn't seem common enough to bother with. */ + if (GET_CODE (XEXP (x, 0)) == ASHIFT + && XEXP (XEXP (x, 0), 0) == const1_rtx) + { + x = gen_rtx (ROTATE, mode, gen_unary (NOT, mode, const1_rtx), + XEXP (XEXP (x, 0), 1)); + goto restart; + } + + if (GET_CODE (XEXP (x, 0)) == SUBREG + && subreg_lowpart_p (XEXP (x, 0)) + && (GET_MODE_SIZE (GET_MODE (XEXP (x, 0))) + < GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (x, 0))))) + && GET_CODE (SUBREG_REG (XEXP (x, 0))) == ASHIFT + && XEXP (SUBREG_REG (XEXP (x, 0)), 0) == const1_rtx) + { + enum machine_mode inner_mode = GET_MODE (SUBREG_REG (XEXP (x, 0))); + + x = gen_rtx (ROTATE, inner_mode, + gen_unary (NOT, inner_mode, const1_rtx), + XEXP (SUBREG_REG (XEXP (x, 0)), 1)); + x = gen_lowpart_for_combine (mode, x); + goto restart; + } + +#if STORE_FLAG_VALUE == -1 + /* (not (comparison foo bar)) can be done by reversing the comparison + code if valid. */ + if (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' + && reversible_comparison_p (XEXP (x, 0))) + return gen_rtx_combine (reverse_condition (GET_CODE (XEXP (x, 0))), + mode, XEXP (XEXP (x, 0), 0), + XEXP (XEXP (x, 0), 1)); + + /* (ashiftrt foo C) where C is the number of bits in FOO minus 1 + is (lt foo (const_int 0)), so we can perform the above + simplification. */ + + if (XEXP (x, 1) == const1_rtx + && GET_CODE (XEXP (x, 0)) == ASHIFTRT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && INTVAL (XEXP (XEXP (x, 0), 1)) == GET_MODE_BITSIZE (mode) - 1) + return gen_rtx_combine (GE, mode, XEXP (XEXP (x, 0), 0), const0_rtx); +#endif + + /* Apply De Morgan's laws to reduce number of patterns for machines + with negating logical insns (and-not, nand, etc.). If result has + only one NOT, put it first, since that is how the patterns are + coded. */ + + if (GET_CODE (XEXP (x, 0)) == IOR || GET_CODE (XEXP (x, 0)) == AND) + { + rtx in1 = XEXP (XEXP (x, 0), 0), in2 = XEXP (XEXP (x, 0), 1); + + if (GET_CODE (in1) == NOT) + in1 = XEXP (in1, 0); + else + in1 = gen_rtx_combine (NOT, GET_MODE (in1), in1); + + if (GET_CODE (in2) == NOT) + in2 = XEXP (in2, 0); + else if (GET_CODE (in2) == CONST_INT + && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT) + in2 = GEN_INT (GET_MODE_MASK (mode) & ~ INTVAL (in2)); + else + in2 = gen_rtx_combine (NOT, GET_MODE (in2), in2); + + if (GET_CODE (in2) == NOT) + { + rtx tem = in2; + in2 = in1; in1 = tem; + } + + x = gen_rtx_combine (GET_CODE (XEXP (x, 0)) == IOR ? AND : IOR, + mode, in1, in2); + goto restart; + } + break; + + case NEG: + /* (neg (plus X 1)) can become (not X). */ + if (GET_CODE (XEXP (x, 0)) == PLUS + && XEXP (XEXP (x, 0), 1) == const1_rtx) + { + x = gen_rtx_combine (NOT, mode, XEXP (XEXP (x, 0), 0)); + goto restart; + } + + /* Similarly, (neg (not X)) is (plus X 1). */ + if (GET_CODE (XEXP (x, 0)) == NOT) + { + x = gen_rtx_combine (PLUS, mode, XEXP (XEXP (x, 0), 0), const1_rtx); + goto restart; + } + + /* (neg (minus X Y)) can become (minus Y X). */ + if (GET_CODE (XEXP (x, 0)) == MINUS + && (GET_MODE_CLASS (mode) != MODE_FLOAT + /* x-y != -(y-x) with IEEE floating point. */ + || TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT)) + { + x = gen_binary (MINUS, mode, XEXP (XEXP (x, 0), 1), + XEXP (XEXP (x, 0), 0)); + goto restart; + } + + /* (neg (xor A 1)) is (plus A -1) if A is known to be either 0 or 1. */ + if (GET_CODE (XEXP (x, 0)) == XOR && XEXP (XEXP (x, 0), 1) == const1_rtx + && nonzero_bits (XEXP (XEXP (x, 0), 0), mode) == 1) + { + x = gen_binary (PLUS, mode, XEXP (XEXP (x, 0), 0), constm1_rtx); + goto restart; + } + + /* NEG commutes with ASHIFT since it is multiplication. Only do this + if we can then eliminate the NEG (e.g., + if the operand is a constant). */ + + if (GET_CODE (XEXP (x, 0)) == ASHIFT) + { + temp = simplify_unary_operation (NEG, mode, + XEXP (XEXP (x, 0), 0), mode); + if (temp) + { + SUBST (XEXP (XEXP (x, 0), 0), temp); + return XEXP (x, 0); + } + } + + temp = expand_compound_operation (XEXP (x, 0)); + + /* For C equal to the width of MODE minus 1, (neg (ashiftrt X C)) can be + replaced by (lshiftrt X C). This will convert + (neg (sign_extract X 1 Y)) to (zero_extract X 1 Y). */ + + if (GET_CODE (temp) == ASHIFTRT + && GET_CODE (XEXP (temp, 1)) == CONST_INT + && INTVAL (XEXP (temp, 1)) == GET_MODE_BITSIZE (mode) - 1) + { + x = simplify_shift_const (temp, LSHIFTRT, mode, XEXP (temp, 0), + INTVAL (XEXP (temp, 1))); + goto restart; + } + + /* If X has only a single bit that might be nonzero, say, bit I, convert + (neg X) to (ashiftrt (ashift X C-I) C-I) where C is the bitsize of + MODE minus 1. This will convert (neg (zero_extract X 1 Y)) to + (sign_extract X 1 Y). But only do this if TEMP isn't a register + or a SUBREG of one since we'd be making the expression more + complex if it was just a register. */ + + if (GET_CODE (temp) != REG + && ! (GET_CODE (temp) == SUBREG + && GET_CODE (SUBREG_REG (temp)) == REG) + && (i = exact_log2 (nonzero_bits (temp, mode))) >= 0) + { + rtx temp1 = simplify_shift_const + (NULL_RTX, ASHIFTRT, mode, + simplify_shift_const (NULL_RTX, ASHIFT, mode, temp, + GET_MODE_BITSIZE (mode) - 1 - i), + GET_MODE_BITSIZE (mode) - 1 - i); + + /* If all we did was surround TEMP with the two shifts, we + haven't improved anything, so don't use it. Otherwise, + we are better off with TEMP1. */ + if (GET_CODE (temp1) != ASHIFTRT + || GET_CODE (XEXP (temp1, 0)) != ASHIFT + || XEXP (XEXP (temp1, 0), 0) != temp) + { + x = temp1; + goto restart; + } + } + break; + + case FLOAT_TRUNCATE: + /* (float_truncate:SF (float_extend:DF foo:SF)) = foo:SF. */ + if (GET_CODE (XEXP (x, 0)) == FLOAT_EXTEND + && GET_MODE (XEXP (XEXP (x, 0), 0)) == mode) + return XEXP (XEXP (x, 0), 0); + break; + +#ifdef HAVE_cc0 + case COMPARE: + /* Convert (compare FOO (const_int 0)) to FOO unless we aren't + using cc0, in which case we want to leave it as a COMPARE + so we can distinguish it from a register-register-copy. */ + if (XEXP (x, 1) == const0_rtx) + return XEXP (x, 0); + + /* In IEEE floating point, x-0 is not the same as x. */ + if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT + || GET_MODE_CLASS (GET_MODE (XEXP (x, 0))) == MODE_INT) + && XEXP (x, 1) == CONST0_RTX (GET_MODE (XEXP (x, 0)))) + return XEXP (x, 0); + break; +#endif + + case CONST: + /* (const (const X)) can become (const X). Do it this way rather than + returning the inner CONST since CONST can be shared with a + REG_EQUAL note. */ + if (GET_CODE (XEXP (x, 0)) == CONST) + SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0)); + break; + +#ifdef HAVE_lo_sum + case LO_SUM: + /* Convert (lo_sum (high FOO) FOO) to FOO. This is necessary so we + can add in an offset. find_split_point will split this address up + again if it doesn't match. */ + if (GET_CODE (XEXP (x, 0)) == HIGH + && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1))) + return XEXP (x, 1); + break; +#endif + + case PLUS: + /* If we have (plus (plus (A const) B)), associate it so that CONST is + outermost. That's because that's the way indexed addresses are + supposed to appear. This code used to check many more cases, but + they are now checked elsewhere. */ + if (GET_CODE (XEXP (x, 0)) == PLUS + && CONSTANT_ADDRESS_P (XEXP (XEXP (x, 0), 1))) + return gen_binary (PLUS, mode, + gen_binary (PLUS, mode, XEXP (XEXP (x, 0), 0), + XEXP (x, 1)), + XEXP (XEXP (x, 0), 1)); + + /* (plus (xor (and (const_int pow2 - 1)) ) <-c>) + when c is (const_int (pow2 + 1) / 2) is a sign extension of a + bit-field and can be replaced by either a sign_extend or a + sign_extract. The `and' may be a zero_extend. */ + if (GET_CODE (XEXP (x, 0)) == XOR + && GET_CODE (XEXP (x, 1)) == CONST_INT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) == - INTVAL (XEXP (XEXP (x, 0), 1)) + && (i = exact_log2 (INTVAL (XEXP (XEXP (x, 0), 1)))) >= 0 + && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT + && ((GET_CODE (XEXP (XEXP (x, 0), 0)) == AND + && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT + && (INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1)) + == ((HOST_WIDE_INT) 1 << (i + 1)) - 1)) + || (GET_CODE (XEXP (XEXP (x, 0), 0)) == ZERO_EXTEND + && (GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (XEXP (x, 0), 0), 0))) + == i + 1)))) + { + x = simplify_shift_const + (NULL_RTX, ASHIFTRT, mode, + simplify_shift_const (NULL_RTX, ASHIFT, mode, + XEXP (XEXP (XEXP (x, 0), 0), 0), + GET_MODE_BITSIZE (mode) - (i + 1)), + GET_MODE_BITSIZE (mode) - (i + 1)); + goto restart; + } + + /* If only the low-order bit of X is possible nonzero, (plus x -1) + can become (ashiftrt (ashift (xor x 1) C) C) where C is + the bitsize of the mode - 1. This allows simplification of + "a = (b & 8) == 0;" */ + if (XEXP (x, 1) == constm1_rtx + && GET_CODE (XEXP (x, 0)) != REG + && ! (GET_CODE (XEXP (x,0)) == SUBREG + && GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG) + && nonzero_bits (XEXP (x, 0), mode) == 1) + { + x = simplify_shift_const + (NULL_RTX, ASHIFTRT, mode, + simplify_shift_const (NULL_RTX, ASHIFT, mode, + gen_rtx_combine (XOR, mode, + XEXP (x, 0), const1_rtx), + GET_MODE_BITSIZE (mode) - 1), + GET_MODE_BITSIZE (mode) - 1); + goto restart; + } + + /* If we are adding two things that have no bits in common, convert + the addition into an IOR. This will often be further simplified, + for example in cases like ((a & 1) + (a & 2)), which can + become a & 3. */ + + if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT + && (nonzero_bits (XEXP (x, 0), mode) + & nonzero_bits (XEXP (x, 1), mode)) == 0) + { + x = gen_binary (IOR, mode, XEXP (x, 0), XEXP (x, 1)); + goto restart; + } + break; + + case MINUS: + /* (minus (and (const_int -pow2))) becomes + (and (const_int pow2-1)) */ + if (GET_CODE (XEXP (x, 1)) == AND + && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT + && exact_log2 (- INTVAL (XEXP (XEXP (x, 1), 1))) >= 0 + && rtx_equal_p (XEXP (XEXP (x, 1), 0), XEXP (x, 0))) + { + x = simplify_and_const_int (NULL_RTX, mode, XEXP (x, 0), + - INTVAL (XEXP (XEXP (x, 1), 1)) - 1); + goto restart; + } + break; + + case MULT: + /* If we have (mult (plus A B) C), apply the distributive law and then + the inverse distributive law to see if things simplify. This + occurs mostly in addresses, often when unrolling loops. */ + + if (GET_CODE (XEXP (x, 0)) == PLUS) + { + x = apply_distributive_law + (gen_binary (PLUS, mode, + gen_binary (MULT, mode, + XEXP (XEXP (x, 0), 0), XEXP (x, 1)), + gen_binary (MULT, mode, + XEXP (XEXP (x, 0), 1), XEXP (x, 1)))); + + if (GET_CODE (x) != MULT) + goto restart; + } + + /* If this is multiplication by a power of two and its first operand is + a shift, treat the multiply as a shift to allow the shifts to + possibly combine. */ + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && (i = exact_log2 (INTVAL (XEXP (x, 1)))) >= 0 + && (GET_CODE (XEXP (x, 0)) == ASHIFT + || GET_CODE (XEXP (x, 0)) == LSHIFTRT + || GET_CODE (XEXP (x, 0)) == ASHIFTRT + || GET_CODE (XEXP (x, 0)) == ROTATE + || GET_CODE (XEXP (x, 0)) == ROTATERT)) + { + x = simplify_shift_const (NULL_RTX, ASHIFT, mode, XEXP (x, 0), i); + goto restart; + } + + /* Convert (mult (ashift (const_int 1) A) B) to (ashift B A). */ + if (GET_CODE (XEXP (x, 0)) == ASHIFT + && XEXP (XEXP (x, 0), 0) == const1_rtx) + return gen_rtx_combine (ASHIFT, mode, XEXP (x, 1), + XEXP (XEXP (x, 0), 1)); + break; + + case UDIV: + /* If this is a divide by a power of two, treat it as a shift if + its first operand is a shift. */ + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && (i = exact_log2 (INTVAL (XEXP (x, 1)))) >= 0 + && (GET_CODE (XEXP (x, 0)) == ASHIFT + || GET_CODE (XEXP (x, 0)) == LSHIFTRT + || GET_CODE (XEXP (x, 0)) == ASHIFTRT + || GET_CODE (XEXP (x, 0)) == ROTATE + || GET_CODE (XEXP (x, 0)) == ROTATERT)) + { + x = simplify_shift_const (NULL_RTX, LSHIFTRT, mode, XEXP (x, 0), i); + goto restart; + } + break; + + case EQ: case NE: + case GT: case GTU: case GE: case GEU: + case LT: case LTU: case LE: case LEU: + /* If the first operand is a condition code, we can't do anything + with it. */ + if (GET_CODE (XEXP (x, 0)) == COMPARE + || (GET_MODE_CLASS (GET_MODE (XEXP (x, 0))) != MODE_CC +#ifdef HAVE_cc0 + && XEXP (x, 0) != cc0_rtx +#endif + )) + { + rtx op0 = XEXP (x, 0); + rtx op1 = XEXP (x, 1); + enum rtx_code new_code; + + if (GET_CODE (op0) == COMPARE) + op1 = XEXP (op0, 1), op0 = XEXP (op0, 0); + + /* Simplify our comparison, if possible. */ + new_code = simplify_comparison (code, &op0, &op1); + +#if STORE_FLAG_VALUE == 1 + /* If STORE_FLAG_VALUE is 1, we can convert (ne x 0) to simply X + if only the low-order bit is possibly nonzero in X (such as when + X is a ZERO_EXTRACT of one bit. Similarly, we can convert + EQ to (xor X 1). Remove any ZERO_EXTRACT we made when thinking + this was a comparison. It may now be simpler to use, e.g., an + AND. If a ZERO_EXTRACT is indeed appropriate, it will + be placed back by the call to make_compound_operation in the + SET case. */ + if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT + && op1 == const0_rtx + && nonzero_bits (op0, GET_MODE (op0)) == 1) + return gen_lowpart_for_combine (mode, + expand_compound_operation (op0)); + else if (new_code == EQ && GET_MODE_CLASS (mode) == MODE_INT + && op1 == const0_rtx + && nonzero_bits (op0, GET_MODE (op0)) == 1) + { + op0 = expand_compound_operation (op0); + + x = gen_rtx_combine (XOR, mode, + gen_lowpart_for_combine (mode, op0), + const1_rtx); + goto restart; + } +#endif + +#if STORE_FLAG_VALUE == -1 + /* If STORE_FLAG_VALUE is -1, we can convert (ne x 0) + to (neg x) if only the low-order bit of X can be nonzero. + This converts (ne (zero_extract X 1 Y) 0) to + (sign_extract X 1 Y). */ + if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT + && op1 == const0_rtx + && nonzero_bits (op0, GET_MODE (op0)) == 1) + { + op0 = expand_compound_operation (op0); + x = gen_rtx_combine (NEG, mode, + gen_lowpart_for_combine (mode, op0)); + goto restart; + } +#endif + + /* If STORE_FLAG_VALUE says to just test the sign bit and X has just + one bit that might be nonzero, we can convert (ne x 0) to + (ashift x c) where C puts the bit in the sign bit. Remove any + AND with STORE_FLAG_VALUE when we are done, since we are only + going to test the sign bit. */ + if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT + && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT + && (STORE_FLAG_VALUE + == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)) + && op1 == const0_rtx + && mode == GET_MODE (op0) + && (i = exact_log2 (nonzero_bits (op0, GET_MODE (op0)))) >= 0) + { + x = simplify_shift_const (NULL_RTX, ASHIFT, mode, + expand_compound_operation (op0), + GET_MODE_BITSIZE (mode) - 1 - i); + if (GET_CODE (x) == AND && XEXP (x, 1) == const_true_rtx) + return XEXP (x, 0); + else + return x; + } + + /* If the code changed, return a whole new comparison. */ + if (new_code != code) + return gen_rtx_combine (new_code, mode, op0, op1); + + /* Otherwise, keep this operation, but maybe change its operands. + This also converts (ne (compare FOO BAR) 0) to (ne FOO BAR). */ + SUBST (XEXP (x, 0), op0); + SUBST (XEXP (x, 1), op1); + } + break; + + case IF_THEN_ELSE: + /* Sometimes we can simplify the arm of an IF_THEN_ELSE if a register + used in it is being compared against certain values. Get the + true and false comparisons and see if that says anything about the + value of each arm. */ + + if (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' + && reversible_comparison_p (XEXP (x, 0)) + && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG) + { + HOST_WIDE_INT nzb; + rtx from = XEXP (XEXP (x, 0), 0); + enum rtx_code true_code = GET_CODE (XEXP (x, 0)); + enum rtx_code false_code = reverse_condition (true_code); + rtx true_val = XEXP (XEXP (x, 0), 1); + rtx false_val = true_val; + rtx true_arm = XEXP (x, 1); + rtx false_arm = XEXP (x, 2); + int swapped = 0; + + /* If FALSE_CODE is EQ, swap the codes and arms. */ + + if (false_code == EQ) + { + swapped = 1, true_code = EQ, false_code = NE; + true_arm = XEXP (x, 2), false_arm = XEXP (x, 1); + } + + /* If we are comparing against zero and the expression being tested + has only a single bit that might be nonzero, that is its value + when it is not equal to zero. Similarly if it is known to be + -1 or 0. */ + + if (true_code == EQ && true_val == const0_rtx + && exact_log2 (nzb = nonzero_bits (from, GET_MODE (from))) >= 0) + false_code = EQ, false_val = GEN_INT (nzb); + else if (true_code == EQ && true_val == const0_rtx + && (num_sign_bit_copies (from, GET_MODE (from)) + == GET_MODE_BITSIZE (GET_MODE (from)))) + false_code = EQ, false_val = constm1_rtx; + + /* Now simplify an arm if we know the value of the register + in the branch and it is used in the arm. Be carefull due to + the potential of locally-shared RTL. */ + + if (reg_mentioned_p (from, true_arm)) + true_arm = subst (known_cond (copy_rtx (true_arm), true_code, + from, true_val), + pc_rtx, pc_rtx, 0, 0); + if (reg_mentioned_p (from, false_arm)) + false_arm = subst (known_cond (copy_rtx (false_arm), false_code, + from, false_val), + pc_rtx, pc_rtx, 0, 0); + + SUBST (XEXP (x, 1), swapped ? false_arm : true_arm); + SUBST (XEXP (x, 2), swapped ? true_arm : false_arm); + } + + /* If we have (if_then_else FOO (pc) (label_ref BAR)) and FOO can be + reversed, do so to avoid needing two sets of patterns for + subtract-and-branch insns. Similarly if we have a constant in that + position or if the third operand is the same as the first operand + of the comparison. */ + + if (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' + && reversible_comparison_p (XEXP (x, 0)) + && (XEXP (x, 1) == pc_rtx || GET_CODE (XEXP (x, 1)) == CONST_INT + || rtx_equal_p (XEXP (x, 2), XEXP (XEXP (x, 0), 0)))) + { + SUBST (XEXP (x, 0), + gen_binary (reverse_condition (GET_CODE (XEXP (x, 0))), + GET_MODE (XEXP (x, 0)), + XEXP (XEXP (x, 0), 0), XEXP (XEXP (x, 0), 1))); + + temp = XEXP (x, 1); + SUBST (XEXP (x, 1), XEXP (x, 2)); + SUBST (XEXP (x, 2), temp); + } + + /* If the two arms are identical, we don't need the comparison. */ + + if (rtx_equal_p (XEXP (x, 1), XEXP (x, 2)) + && ! side_effects_p (XEXP (x, 0))) + return XEXP (x, 1); + + /* Look for cases where we have (abs x) or (neg (abs X)). */ + + if (GET_MODE_CLASS (mode) == MODE_INT + && GET_CODE (XEXP (x, 2)) == NEG + && rtx_equal_p (XEXP (x, 1), XEXP (XEXP (x, 2), 0)) + && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' + && rtx_equal_p (XEXP (x, 1), XEXP (XEXP (x, 0), 0)) + && ! side_effects_p (XEXP (x, 1))) + switch (GET_CODE (XEXP (x, 0))) + { + case GT: + case GE: + x = gen_unary (ABS, mode, XEXP (x, 1)); + goto restart; + case LT: + case LE: + x = gen_unary (NEG, mode, gen_unary (ABS, mode, XEXP (x, 1))); + goto restart; + } + + /* Look for MIN or MAX. */ + + if (GET_MODE_CLASS (mode) == MODE_INT + && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' + && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1)) + && rtx_equal_p (XEXP (XEXP (x, 0), 1), XEXP (x, 2)) + && ! side_effects_p (XEXP (x, 0))) + switch (GET_CODE (XEXP (x, 0))) + { + case GE: + case GT: + x = gen_binary (SMAX, mode, XEXP (x, 1), XEXP (x, 2)); + goto restart; + case LE: + case LT: + x = gen_binary (SMIN, mode, XEXP (x, 1), XEXP (x, 2)); + goto restart; + case GEU: + case GTU: + x = gen_binary (UMAX, mode, XEXP (x, 1), XEXP (x, 2)); + goto restart; + case LEU: + case LTU: + x = gen_binary (UMIN, mode, XEXP (x, 1), XEXP (x, 2)); + goto restart; + } + + /* If we have something like (if_then_else (ne A 0) (OP X C) X), + A is known to be either 0 or 1, and OP is an identity when its + second operand is zero, this can be done as (OP X (mult A C)). + Similarly if A is known to be 0 or -1 and also similarly if we have + a ZERO_EXTEND or SIGN_EXTEND as long as X is already extended (so + we don't destroy it). */ + + if (mode != VOIDmode + && (GET_CODE (XEXP (x, 0)) == EQ || GET_CODE (XEXP (x, 0)) == NE) + && XEXP (XEXP (x, 0), 1) == const0_rtx + && (nonzero_bits (XEXP (XEXP (x, 0), 0), mode) == 1 + || (num_sign_bit_copies (XEXP (XEXP (x, 0), 0), mode) + == GET_MODE_BITSIZE (mode)))) + { + rtx nz = make_compound_operation (GET_CODE (XEXP (x, 0)) == NE + ? XEXP (x, 1) : XEXP (x, 2)); + rtx z = GET_CODE (XEXP (x, 0)) == NE ? XEXP (x, 2) : XEXP (x, 1); + rtx dir = (nonzero_bits (XEXP (XEXP (x, 0), 0), mode) == 1 + ? const1_rtx : constm1_rtx); + rtx c = 0; + enum machine_mode m = mode; + enum rtx_code op, extend_op = 0; + + if ((GET_CODE (nz) == PLUS || GET_CODE (nz) == MINUS + || GET_CODE (nz) == IOR || GET_CODE (nz) == XOR + || GET_CODE (nz) == ASHIFT + || GET_CODE (nz) == LSHIFTRT || GET_CODE (nz) == ASHIFTRT) + && rtx_equal_p (XEXP (nz, 0), z)) + c = XEXP (nz, 1), op = GET_CODE (nz); + else if (GET_CODE (nz) == SIGN_EXTEND + && (GET_CODE (XEXP (nz, 0)) == PLUS + || GET_CODE (XEXP (nz, 0)) == MINUS + || GET_CODE (XEXP (nz, 0)) == IOR + || GET_CODE (XEXP (nz, 0)) == XOR + || GET_CODE (XEXP (nz, 0)) == ASHIFT + || GET_CODE (XEXP (nz, 0)) == LSHIFTRT + || GET_CODE (XEXP (nz, 0)) == ASHIFTRT) + && GET_CODE (XEXP (XEXP (nz, 0), 0)) == SUBREG + && subreg_lowpart_p (XEXP (XEXP (nz, 0), 0)) + && rtx_equal_p (SUBREG_REG (XEXP (XEXP (nz, 0), 0)), z) + && (num_sign_bit_copies (z, GET_MODE (z)) + >= (GET_MODE_BITSIZE (mode) + - GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (nz, 0), 0)))))) + { + c = XEXP (XEXP (nz, 0), 1); + op = GET_CODE (XEXP (nz, 0)); + extend_op = SIGN_EXTEND; + m = GET_MODE (XEXP (nz, 0)); + } + else if (GET_CODE (nz) == ZERO_EXTEND + && (GET_CODE (XEXP (nz, 0)) == PLUS + || GET_CODE (XEXP (nz, 0)) == MINUS + || GET_CODE (XEXP (nz, 0)) == IOR + || GET_CODE (XEXP (nz, 0)) == XOR + || GET_CODE (XEXP (nz, 0)) == ASHIFT + || GET_CODE (XEXP (nz, 0)) == LSHIFTRT + || GET_CODE (XEXP (nz, 0)) == ASHIFTRT) + && GET_CODE (XEXP (XEXP (nz, 0), 0)) == SUBREG + && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT + && subreg_lowpart_p (XEXP (XEXP (nz, 0), 0)) + && rtx_equal_p (SUBREG_REG (XEXP (XEXP (nz, 0), 0)), z) + && ((nonzero_bits (z, GET_MODE (z)) + & ~ GET_MODE_MASK (GET_MODE (XEXP (XEXP (nz, 0), 0)))) + == 0)) + { + c = XEXP (XEXP (nz, 0), 1); + op = GET_CODE (XEXP (nz, 0)); + extend_op = ZERO_EXTEND; + m = GET_MODE (XEXP (nz, 0)); + } + + if (c && ! side_effects_p (c) && ! side_effects_p (z)) + { + temp + = gen_binary (MULT, m, + gen_lowpart_for_combine (m, + XEXP (XEXP (x, 0), 0)), + gen_binary (MULT, m, c, dir)); + + temp = gen_binary (op, m, gen_lowpart_for_combine (m, z), temp); + + if (extend_op != 0) + temp = gen_unary (extend_op, mode, temp); + + return temp; + } + } + break; + + case ZERO_EXTRACT: + case SIGN_EXTRACT: + case ZERO_EXTEND: + case SIGN_EXTEND: + /* If we are processing SET_DEST, we are done. */ + if (in_dest) + return x; + + x = expand_compound_operation (x); + if (GET_CODE (x) != code) + goto restart; + break; + + case SET: + /* (set (pc) (return)) gets written as (return). */ + if (GET_CODE (SET_DEST (x)) == PC && GET_CODE (SET_SRC (x)) == RETURN) + return SET_SRC (x); + + /* Convert this into a field assignment operation, if possible. */ + x = make_field_assignment (x); + + /* If we are setting CC0 or if the source is a COMPARE, look for the + use of the comparison result and try to simplify it unless we already + have used undobuf.other_insn. */ + if ((GET_CODE (SET_SRC (x)) == COMPARE +#ifdef HAVE_cc0 + || SET_DEST (x) == cc0_rtx +#endif + ) + && (cc_use = find_single_use (SET_DEST (x), subst_insn, + &other_insn)) != 0 + && (undobuf.other_insn == 0 || other_insn == undobuf.other_insn) + && GET_RTX_CLASS (GET_CODE (*cc_use)) == '<' + && XEXP (*cc_use, 0) == SET_DEST (x)) + { + enum rtx_code old_code = GET_CODE (*cc_use); + enum rtx_code new_code; + rtx op0, op1; + int other_changed = 0; + enum machine_mode compare_mode = GET_MODE (SET_DEST (x)); + + if (GET_CODE (SET_SRC (x)) == COMPARE) + op0 = XEXP (SET_SRC (x), 0), op1 = XEXP (SET_SRC (x), 1); + else + op0 = SET_SRC (x), op1 = const0_rtx; + + /* Simplify our comparison, if possible. */ + new_code = simplify_comparison (old_code, &op0, &op1); + +#ifdef EXTRA_CC_MODES + /* If this machine has CC modes other than CCmode, check to see + if we need to use a different CC mode here. */ + compare_mode = SELECT_CC_MODE (new_code, op0, op1); +#endif /* EXTRA_CC_MODES */ + +#if !defined (HAVE_cc0) && defined (EXTRA_CC_MODES) + /* If the mode changed, we have to change SET_DEST, the mode + in the compare, and the mode in the place SET_DEST is used. + If SET_DEST is a hard register, just build new versions with + the proper mode. If it is a pseudo, we lose unless it is only + time we set the pseudo, in which case we can safely change + its mode. */ + if (compare_mode != GET_MODE (SET_DEST (x))) + { + int regno = REGNO (SET_DEST (x)); + rtx new_dest = gen_rtx (REG, compare_mode, regno); + + if (regno < FIRST_PSEUDO_REGISTER + || (reg_n_sets[regno] == 1 + && ! REG_USERVAR_P (SET_DEST (x)))) + { + if (regno >= FIRST_PSEUDO_REGISTER) + SUBST (regno_reg_rtx[regno], new_dest); + + SUBST (SET_DEST (x), new_dest); + SUBST (XEXP (*cc_use, 0), new_dest); + other_changed = 1; + } + } +#endif + + /* If the code changed, we have to build a new comparison + in undobuf.other_insn. */ + if (new_code != old_code) + { + unsigned HOST_WIDE_INT mask; + + SUBST (*cc_use, gen_rtx_combine (new_code, GET_MODE (*cc_use), + SET_DEST (x), const0_rtx)); + + /* If the only change we made was to change an EQ into an + NE or vice versa, OP0 has only one bit that might be nonzero, + and OP1 is zero, check if changing the user of the condition + code will produce a valid insn. If it won't, we can keep + the original code in that insn by surrounding our operation + with an XOR. */ + + if (((old_code == NE && new_code == EQ) + || (old_code == EQ && new_code == NE)) + && ! other_changed && op1 == const0_rtx + && (GET_MODE_BITSIZE (GET_MODE (op0)) + <= HOST_BITS_PER_WIDE_INT) + && (exact_log2 (mask = nonzero_bits (op0, GET_MODE (op0))) + >= 0)) + { + rtx pat = PATTERN (other_insn), note = 0; + + if ((recog_for_combine (&pat, other_insn, ¬e) < 0 + && ! check_asm_operands (pat))) + { + PUT_CODE (*cc_use, old_code); + other_insn = 0; + + op0 = gen_binary (XOR, GET_MODE (op0), op0, + GEN_INT (mask)); + } + } + + other_changed = 1; + } + + if (other_changed) + undobuf.other_insn = other_insn; + +#ifdef HAVE_cc0 + /* If we are now comparing against zero, change our source if + needed. If we do not use cc0, we always have a COMPARE. */ + if (op1 == const0_rtx && SET_DEST (x) == cc0_rtx) + SUBST (SET_SRC (x), op0); + else +#endif + + /* Otherwise, if we didn't previously have a COMPARE in the + correct mode, we need one. */ + if (GET_CODE (SET_SRC (x)) != COMPARE + || GET_MODE (SET_SRC (x)) != compare_mode) + SUBST (SET_SRC (x), gen_rtx_combine (COMPARE, compare_mode, + op0, op1)); + else + { + /* Otherwise, update the COMPARE if needed. */ + SUBST (XEXP (SET_SRC (x), 0), op0); + SUBST (XEXP (SET_SRC (x), 1), op1); + } + } + else + { + /* Get SET_SRC in a form where we have placed back any + compound expressions. Then do the checks below. */ + temp = make_compound_operation (SET_SRC (x), SET); + SUBST (SET_SRC (x), temp); + } + + /* If we have (set x (subreg:m1 (op:m2 ...) 0)) with OP being some + operation, and X being a REG or (subreg (reg)), we may be able to + convert this to (set (subreg:m2 x) (op)). + + We can always do this if M1 is narrower than M2 because that + means that we only care about the low bits of the result. + + However, on most machines (those with neither BYTE_LOADS_ZERO_EXTEND + nor BYTES_LOADS_SIGN_EXTEND defined), we cannot perform a + narrower operation that requested since the high-order bits will + be undefined. On machine where BYTE_LOADS_*_EXTEND is defined, + however, this transformation is safe as long as M1 and M2 have + the same number of words. */ + + if (GET_CODE (SET_SRC (x)) == SUBREG + && subreg_lowpart_p (SET_SRC (x)) + && GET_RTX_CLASS (GET_CODE (SUBREG_REG (SET_SRC (x)))) != 'o' + && (((GET_MODE_SIZE (GET_MODE (SET_SRC (x))) + (UNITS_PER_WORD - 1)) + / UNITS_PER_WORD) + == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_SRC (x)))) + + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)) +#ifndef BYTE_LOADS_EXTEND + && (GET_MODE_SIZE (GET_MODE (SET_SRC (x))) + < GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_SRC (x))))) +#endif + && (GET_CODE (SET_DEST (x)) == REG + || (GET_CODE (SET_DEST (x)) == SUBREG + && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG))) + { + SUBST (SET_DEST (x), + gen_lowpart_for_combine (GET_MODE (SUBREG_REG (SET_SRC (x))), + SET_DEST (x))); + SUBST (SET_SRC (x), SUBREG_REG (SET_SRC (x))); + } + +#ifdef BYTE_LOADS_EXTEND + /* If we have (set FOO (subreg:M (mem:N BAR) 0)) with + M wider than N, this would require a paradoxical subreg. + Replace the subreg with a zero_extend to avoid the reload that + would otherwise be required. */ + + if (GET_CODE (SET_SRC (x)) == SUBREG + && subreg_lowpart_p (SET_SRC (x)) + && SUBREG_WORD (SET_SRC (x)) == 0 + && (GET_MODE_SIZE (GET_MODE (SET_SRC (x))) + > GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_SRC (x))))) + && GET_CODE (SUBREG_REG (SET_SRC (x))) == MEM) + SUBST (SET_SRC (x), gen_rtx_combine (LOAD_EXTEND, + GET_MODE (SET_SRC (x)), + XEXP (SET_SRC (x), 0))); +#endif + +#ifndef HAVE_conditional_move + + /* If we don't have a conditional move, SET_SRC is an IF_THEN_ELSE, + and we are comparing an item known to be 0 or -1 against 0, use a + logical operation instead. Check for one of the arms being an IOR + of the other arm with some value. We compute three terms to be + IOR'ed together. In practice, at most two will be nonzero. Then + we do the IOR's. */ + + if (GET_CODE (SET_DEST (x)) != PC + && GET_CODE (SET_SRC (x)) == IF_THEN_ELSE + && (GET_CODE (XEXP (SET_SRC (x), 0)) == EQ + || GET_CODE (XEXP (SET_SRC (x), 0)) == NE) + && XEXP (XEXP (SET_SRC (x), 0), 1) == const0_rtx + && (num_sign_bit_copies (XEXP (XEXP (SET_SRC (x), 0), 0), + GET_MODE (XEXP (XEXP (SET_SRC (x), 0), 0))) + == GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (SET_SRC (x), 0), 0)))) + && ! side_effects_p (SET_SRC (x))) + { + rtx true = (GET_CODE (XEXP (SET_SRC (x), 0)) == NE + ? XEXP (SET_SRC (x), 1) : XEXP (SET_SRC (x), 2)); + rtx false = (GET_CODE (XEXP (SET_SRC (x), 0)) == NE + ? XEXP (SET_SRC (x), 2) : XEXP (SET_SRC (x), 1)); + rtx term1 = const0_rtx, term2, term3; + + if (GET_CODE (true) == IOR && rtx_equal_p (XEXP (true, 0), false)) + term1 = false, true = XEXP (true, 1), false = const0_rtx; + else if (GET_CODE (true) == IOR + && rtx_equal_p (XEXP (true, 1), false)) + term1 = false, true = XEXP (true, 0), false = const0_rtx; + else if (GET_CODE (false) == IOR + && rtx_equal_p (XEXP (false, 0), true)) + term1 = true, false = XEXP (false, 1), true = const0_rtx; + else if (GET_CODE (false) == IOR + && rtx_equal_p (XEXP (false, 1), true)) + term1 = true, false = XEXP (false, 0), true = const0_rtx; + + term2 = gen_binary (AND, GET_MODE (SET_SRC (x)), + XEXP (XEXP (SET_SRC (x), 0), 0), true); + term3 = gen_binary (AND, GET_MODE (SET_SRC (x)), + gen_unary (NOT, GET_MODE (SET_SRC (x)), + XEXP (XEXP (SET_SRC (x), 0), 0)), + false); + + SUBST (SET_SRC (x), + gen_binary (IOR, GET_MODE (SET_SRC (x)), + gen_binary (IOR, GET_MODE (SET_SRC (x)), + term1, term2), + term3)); + } +#endif + break; + + case AND: + if (GET_CODE (XEXP (x, 1)) == CONST_INT) + { + x = simplify_and_const_int (x, mode, XEXP (x, 0), + INTVAL (XEXP (x, 1))); + + /* If we have (ior (and (X C1) C2)) and the next restart would be + the last, simplify this by making C1 as small as possible + and then exit. */ + if (n_restarts >= 3 && GET_CODE (x) == IOR + && GET_CODE (XEXP (x, 0)) == AND + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && GET_CODE (XEXP (x, 1)) == CONST_INT) + { + temp = gen_binary (AND, mode, XEXP (XEXP (x, 0), 0), + GEN_INT (INTVAL (XEXP (XEXP (x, 0), 1)) + & ~ INTVAL (XEXP (x, 1)))); + return gen_binary (IOR, mode, temp, XEXP (x, 1)); + } + + if (GET_CODE (x) != AND) + goto restart; + } + + /* Convert (A | B) & A to A. */ + if (GET_CODE (XEXP (x, 0)) == IOR + && (rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1)) + || rtx_equal_p (XEXP (XEXP (x, 0), 1), XEXP (x, 1))) + && ! side_effects_p (XEXP (XEXP (x, 0), 0)) + && ! side_effects_p (XEXP (XEXP (x, 0), 1))) + return XEXP (x, 1); + + /* Convert (A ^ B) & A to A & (~ B) since the latter is often a single + insn (and may simplify more). */ + else if (GET_CODE (XEXP (x, 0)) == XOR + && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1)) + && ! side_effects_p (XEXP (x, 1))) + { + x = gen_binary (AND, mode, + gen_unary (NOT, mode, XEXP (XEXP (x, 0), 1)), + XEXP (x, 1)); + goto restart; + } + else if (GET_CODE (XEXP (x, 0)) == XOR + && rtx_equal_p (XEXP (XEXP (x, 0), 1), XEXP (x, 1)) + && ! side_effects_p (XEXP (x, 1))) + { + x = gen_binary (AND, mode, + gen_unary (NOT, mode, XEXP (XEXP (x, 0), 0)), + XEXP (x, 1)); + goto restart; + } + + /* Similarly for (~ (A ^ B)) & A. */ + else if (GET_CODE (XEXP (x, 0)) == NOT + && GET_CODE (XEXP (XEXP (x, 0), 0)) == XOR + && rtx_equal_p (XEXP (XEXP (XEXP (x, 0), 0), 0), XEXP (x, 1)) + && ! side_effects_p (XEXP (x, 1))) + { + x = gen_binary (AND, mode, XEXP (XEXP (XEXP (x, 0), 0), 1), + XEXP (x, 1)); + goto restart; + } + else if (GET_CODE (XEXP (x, 0)) == NOT + && GET_CODE (XEXP (XEXP (x, 0), 0)) == XOR + && rtx_equal_p (XEXP (XEXP (XEXP (x, 0), 0), 1), XEXP (x, 1)) + && ! side_effects_p (XEXP (x, 1))) + { + x = gen_binary (AND, mode, XEXP (XEXP (XEXP (x, 0), 0), 0), + XEXP (x, 1)); + goto restart; + } + + /* If we have (and A B) with A not an object but that is known to + be -1 or 0, this is equivalent to the expression + (if_then_else (ne A (const_int 0)) B (const_int 0)) + We make this conversion because it may allow further + simplifications and then allow use of conditional move insns. + If the machine doesn't have condition moves, code in case SET + will convert the IF_THEN_ELSE back to the logical operation. + We build the IF_THEN_ELSE here in case further simplification + is possible (e.g., we can convert it to ABS). */ + + if (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) != 'o' + && ! (GET_CODE (XEXP (x, 0)) == SUBREG + && GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (x, 0)))) == 'o') + && (num_sign_bit_copies (XEXP (x, 0), GET_MODE (XEXP (x, 0))) + == GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))))) + { + rtx op0 = XEXP (x, 0); + rtx op1 = const0_rtx; + enum rtx_code comp_code + = simplify_comparison (NE, &op0, &op1); + + x = gen_rtx_combine (IF_THEN_ELSE, mode, + gen_binary (comp_code, VOIDmode, op0, op1), + XEXP (x, 1), const0_rtx); + goto restart; + } + + /* In the following group of tests (and those in case IOR below), + we start with some combination of logical operations and apply + the distributive law followed by the inverse distributive law. + Most of the time, this results in no change. However, if some of + the operands are the same or inverses of each other, simplifications + will result. + + For example, (and (ior A B) (not B)) can occur as the result of + expanding a bit field assignment. When we apply the distributive + law to this, we get (ior (and (A (not B))) (and (B (not B)))), + which then simplifies to (and (A (not B))). */ + + /* If we have (and (ior A B) C), apply the distributive law and then + the inverse distributive law to see if things simplify. */ + + if (GET_CODE (XEXP (x, 0)) == IOR || GET_CODE (XEXP (x, 0)) == XOR) + { + x = apply_distributive_law + (gen_binary (GET_CODE (XEXP (x, 0)), mode, + gen_binary (AND, mode, + XEXP (XEXP (x, 0), 0), XEXP (x, 1)), + gen_binary (AND, mode, + XEXP (XEXP (x, 0), 1), XEXP (x, 1)))); + if (GET_CODE (x) != AND) + goto restart; + } + + if (GET_CODE (XEXP (x, 1)) == IOR || GET_CODE (XEXP (x, 1)) == XOR) + { + x = apply_distributive_law + (gen_binary (GET_CODE (XEXP (x, 1)), mode, + gen_binary (AND, mode, + XEXP (XEXP (x, 1), 0), XEXP (x, 0)), + gen_binary (AND, mode, + XEXP (XEXP (x, 1), 1), XEXP (x, 0)))); + if (GET_CODE (x) != AND) + goto restart; + } + + /* Similarly, taking advantage of the fact that + (and (not A) (xor B C)) == (xor (ior A B) (ior A C)) */ + + if (GET_CODE (XEXP (x, 0)) == NOT && GET_CODE (XEXP (x, 1)) == XOR) + { + x = apply_distributive_law + (gen_binary (XOR, mode, + gen_binary (IOR, mode, XEXP (XEXP (x, 0), 0), + XEXP (XEXP (x, 1), 0)), + gen_binary (IOR, mode, XEXP (XEXP (x, 0), 0), + XEXP (XEXP (x, 1), 1)))); + if (GET_CODE (x) != AND) + goto restart; + } + + else if (GET_CODE (XEXP (x, 1)) == NOT && GET_CODE (XEXP (x, 0)) == XOR) + { + x = apply_distributive_law + (gen_binary (XOR, mode, + gen_binary (IOR, mode, XEXP (XEXP (x, 1), 0), + XEXP (XEXP (x, 0), 0)), + gen_binary (IOR, mode, XEXP (XEXP (x, 1), 0), + XEXP (XEXP (x, 0), 1)))); + if (GET_CODE (x) != AND) + goto restart; + } + break; + + case IOR: + /* (ior A C) is C if all bits of A that might be nonzero are on in C. */ + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT + && (nonzero_bits (XEXP (x, 0), mode) & ~ INTVAL (XEXP (x, 1))) == 0) + return XEXP (x, 1); + + /* Convert (A & B) | A to A. */ + if (GET_CODE (XEXP (x, 0)) == AND + && (rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1)) + || rtx_equal_p (XEXP (XEXP (x, 0), 1), XEXP (x, 1))) + && ! side_effects_p (XEXP (XEXP (x, 0), 0)) + && ! side_effects_p (XEXP (XEXP (x, 0), 1))) + return XEXP (x, 1); + + /* If we have (ior (and A B) C), apply the distributive law and then + the inverse distributive law to see if things simplify. */ + + if (GET_CODE (XEXP (x, 0)) == AND) + { + x = apply_distributive_law + (gen_binary (AND, mode, + gen_binary (IOR, mode, + XEXP (XEXP (x, 0), 0), XEXP (x, 1)), + gen_binary (IOR, mode, + XEXP (XEXP (x, 0), 1), XEXP (x, 1)))); + + if (GET_CODE (x) != IOR) + goto restart; + } + + if (GET_CODE (XEXP (x, 1)) == AND) + { + x = apply_distributive_law + (gen_binary (AND, mode, + gen_binary (IOR, mode, + XEXP (XEXP (x, 1), 0), XEXP (x, 0)), + gen_binary (IOR, mode, + XEXP (XEXP (x, 1), 1), XEXP (x, 0)))); + + if (GET_CODE (x) != IOR) + goto restart; + } + + /* Convert (ior (ashift A CX) (lshiftrt A CY)) where CX+CY equals the + mode size to (rotate A CX). */ + + if (((GET_CODE (XEXP (x, 0)) == ASHIFT + && GET_CODE (XEXP (x, 1)) == LSHIFTRT) + || (GET_CODE (XEXP (x, 1)) == ASHIFT + && GET_CODE (XEXP (x, 0)) == LSHIFTRT)) + && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (XEXP (x, 1), 0)) + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT + && (INTVAL (XEXP (XEXP (x, 0), 1)) + INTVAL (XEXP (XEXP (x, 1), 1)) + == GET_MODE_BITSIZE (mode))) + { + rtx shift_count; + + if (GET_CODE (XEXP (x, 0)) == ASHIFT) + shift_count = XEXP (XEXP (x, 0), 1); + else + shift_count = XEXP (XEXP (x, 1), 1); + x = gen_rtx (ROTATE, mode, XEXP (XEXP (x, 0), 0), shift_count); + goto restart; + } + break; + + case XOR: + /* Convert (XOR (NOT x) (NOT y)) to (XOR x y). + Also convert (XOR (NOT x) y) to (NOT (XOR x y)), similarly for + (NOT y). */ + { + int num_negated = 0; + rtx in1 = XEXP (x, 0), in2 = XEXP (x, 1); + + if (GET_CODE (in1) == NOT) + num_negated++, in1 = XEXP (in1, 0); + if (GET_CODE (in2) == NOT) + num_negated++, in2 = XEXP (in2, 0); + + if (num_negated == 2) + { + SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0)); + SUBST (XEXP (x, 1), XEXP (XEXP (x, 1), 0)); + } + else if (num_negated == 1) + { + x = gen_unary (NOT, mode, + gen_binary (XOR, mode, in1, in2)); + goto restart; + } + } + + /* Convert (xor (and A B) B) to (and (not A) B). The latter may + correspond to a machine insn or result in further simplifications + if B is a constant. */ + + if (GET_CODE (XEXP (x, 0)) == AND + && rtx_equal_p (XEXP (XEXP (x, 0), 1), XEXP (x, 1)) + && ! side_effects_p (XEXP (x, 1))) + { + x = gen_binary (AND, mode, + gen_unary (NOT, mode, XEXP (XEXP (x, 0), 0)), + XEXP (x, 1)); + goto restart; + } + else if (GET_CODE (XEXP (x, 0)) == AND + && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1)) + && ! side_effects_p (XEXP (x, 1))) + { + x = gen_binary (AND, mode, + gen_unary (NOT, mode, XEXP (XEXP (x, 0), 1)), + XEXP (x, 1)); + goto restart; + } + + +#if STORE_FLAG_VALUE == 1 + /* (xor (comparison foo bar) (const_int 1)) can become the reversed + comparison. */ + if (XEXP (x, 1) == const1_rtx + && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' + && reversible_comparison_p (XEXP (x, 0))) + return gen_rtx_combine (reverse_condition (GET_CODE (XEXP (x, 0))), + mode, XEXP (XEXP (x, 0), 0), + XEXP (XEXP (x, 0), 1)); + + /* (lshiftrt foo C) where C is the number of bits in FOO minus 1 + is (lt foo (const_int 0)), so we can perform the above + simplification. */ + + if (XEXP (x, 1) == const1_rtx + && GET_CODE (XEXP (x, 0)) == LSHIFTRT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && INTVAL (XEXP (XEXP (x, 0), 1)) == GET_MODE_BITSIZE (mode) - 1) + return gen_rtx_combine (GE, mode, XEXP (XEXP (x, 0), 0), const0_rtx); +#endif + + /* (xor (comparison foo bar) (const_int sign-bit)) + when STORE_FLAG_VALUE is the sign bit. */ + if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT + && (STORE_FLAG_VALUE + == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)) + && XEXP (x, 1) == const_true_rtx + && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<' + && reversible_comparison_p (XEXP (x, 0))) + return gen_rtx_combine (reverse_condition (GET_CODE (XEXP (x, 0))), + mode, XEXP (XEXP (x, 0), 0), + XEXP (XEXP (x, 0), 1)); + break; + + case ABS: + /* (abs (neg )) -> (abs ) */ + if (GET_CODE (XEXP (x, 0)) == NEG) + SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0)); + + /* If operand is something known to be positive, ignore the ABS. */ + if (GET_CODE (XEXP (x, 0)) == FFS || GET_CODE (XEXP (x, 0)) == ABS + || ((GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) + <= HOST_BITS_PER_WIDE_INT) + && ((nonzero_bits (XEXP (x, 0), GET_MODE (XEXP (x, 0))) + & ((HOST_WIDE_INT) 1 + << (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - 1))) + == 0))) + return XEXP (x, 0); + + + /* If operand is known to be only -1 or 0, convert ABS to NEG. */ + if (num_sign_bit_copies (XEXP (x, 0), mode) == GET_MODE_BITSIZE (mode)) + { + x = gen_rtx_combine (NEG, mode, XEXP (x, 0)); + goto restart; + } + break; + + case FFS: + /* (ffs (*_extend )) = (ffs ) */ + if (GET_CODE (XEXP (x, 0)) == SIGN_EXTEND + || GET_CODE (XEXP (x, 0)) == ZERO_EXTEND) + SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0)); + break; + + case FLOAT: + /* (float (sign_extend )) = (float ). */ + if (GET_CODE (XEXP (x, 0)) == SIGN_EXTEND) + SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0)); + break; + + case LSHIFT: + case ASHIFT: + case LSHIFTRT: + case ASHIFTRT: + case ROTATE: + case ROTATERT: + /* If this is a shift by a constant amount, simplify it. */ + if (GET_CODE (XEXP (x, 1)) == CONST_INT) + { + x = simplify_shift_const (x, code, mode, XEXP (x, 0), + INTVAL (XEXP (x, 1))); + if (GET_CODE (x) != code) + goto restart; + } + +#ifdef SHIFT_COUNT_TRUNCATED + else if (GET_CODE (XEXP (x, 1)) != REG) + SUBST (XEXP (x, 1), + force_to_mode (XEXP (x, 1), GET_MODE (x), + exact_log2 (GET_MODE_BITSIZE (GET_MODE (x))), + NULL_RTX)); +#endif + + break; + } + + return x; +} + +/* We consider ZERO_EXTRACT, SIGN_EXTRACT, and SIGN_EXTEND as "compound + operations" because they can be replaced with two more basic operations. + ZERO_EXTEND is also considered "compound" because it can be replaced with + an AND operation, which is simpler, though only one operation. + + The function expand_compound_operation is called with an rtx expression + and will convert it to the appropriate shifts and AND operations, + simplifying at each stage. + + The function make_compound_operation is called to convert an expression + consisting of shifts and ANDs into the equivalent compound expression. + It is the inverse of this function, loosely speaking. */ + +static rtx +expand_compound_operation (x) + rtx x; +{ + int pos = 0, len; + int unsignedp = 0; + int modewidth; + rtx tem; + + switch (GET_CODE (x)) + { + case ZERO_EXTEND: + unsignedp = 1; + case SIGN_EXTEND: + /* We can't necessarily use a const_int for a multiword mode; + it depends on implicitly extending the value. + Since we don't know the right way to extend it, + we can't tell whether the implicit way is right. + + Even for a mode that is no wider than a const_int, + we can't win, because we need to sign extend one of its bits through + the rest of it, and we don't know which bit. */ + if (GET_CODE (XEXP (x, 0)) == CONST_INT) + return x; + + if (! FAKE_EXTEND_SAFE_P (GET_MODE (XEXP (x, 0)), XEXP (x, 0))) + return x; + + len = GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))); + /* If the inner object has VOIDmode (the only way this can happen + is if it is a ASM_OPERANDS), we can't do anything since we don't + know how much masking to do. */ + if (len == 0) + return x; + + break; + + case ZERO_EXTRACT: + unsignedp = 1; + case SIGN_EXTRACT: + /* If the operand is a CLOBBER, just return it. */ + if (GET_CODE (XEXP (x, 0)) == CLOBBER) + return XEXP (x, 0); + + if (GET_CODE (XEXP (x, 1)) != CONST_INT + || GET_CODE (XEXP (x, 2)) != CONST_INT + || GET_MODE (XEXP (x, 0)) == VOIDmode) + return x; + + len = INTVAL (XEXP (x, 1)); + pos = INTVAL (XEXP (x, 2)); + + /* If this goes outside the object being extracted, replace the object + with a (use (mem ...)) construct that only combine understands + and is used only for this purpose. */ + if (len + pos > GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))) + SUBST (XEXP (x, 0), gen_rtx (USE, GET_MODE (x), XEXP (x, 0))); + +#if BITS_BIG_ENDIAN + pos = GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - len - pos; +#endif + break; + + default: + return x; + } + + /* If we reach here, we want to return a pair of shifts. The inner + shift is a left shift of BITSIZE - POS - LEN bits. The outer + shift is a right shift of BITSIZE - LEN bits. It is arithmetic or + logical depending on the value of UNSIGNEDP. + + If this was a ZERO_EXTEND or ZERO_EXTRACT, this pair of shifts will be + converted into an AND of a shift. + + We must check for the case where the left shift would have a negative + count. This can happen in a case like (x >> 31) & 255 on machines + that can't shift by a constant. On those machines, we would first + combine the shift with the AND to produce a variable-position + extraction. Then the constant of 31 would be substituted in to produce + a such a position. */ + + modewidth = GET_MODE_BITSIZE (GET_MODE (x)); + if (modewidth >= pos - len) + tem = simplify_shift_const (NULL_RTX, unsignedp ? LSHIFTRT : ASHIFTRT, + GET_MODE (x), + simplify_shift_const (NULL_RTX, ASHIFT, + GET_MODE (x), + XEXP (x, 0), + modewidth - pos - len), + modewidth - len); + + else if (unsignedp && len < HOST_BITS_PER_WIDE_INT) + tem = simplify_and_const_int (NULL_RTX, GET_MODE (x), + simplify_shift_const (NULL_RTX, LSHIFTRT, + GET_MODE (x), + XEXP (x, 0), pos), + ((HOST_WIDE_INT) 1 << len) - 1); + else + /* Any other cases we can't handle. */ + return x; + + + /* If we couldn't do this for some reason, return the original + expression. */ + if (GET_CODE (tem) == CLOBBER) + return x; + + return tem; +} + +/* X is a SET which contains an assignment of one object into + a part of another (such as a bit-field assignment, STRICT_LOW_PART, + or certain SUBREGS). If possible, convert it into a series of + logical operations. + + We half-heartedly support variable positions, but do not at all + support variable lengths. */ + +static rtx +expand_field_assignment (x) + rtx x; +{ + rtx inner; + rtx pos; /* Always counts from low bit. */ + int len; + rtx mask; + enum machine_mode compute_mode; + + /* Loop until we find something we can't simplify. */ + while (1) + { + if (GET_CODE (SET_DEST (x)) == STRICT_LOW_PART + && GET_CODE (XEXP (SET_DEST (x), 0)) == SUBREG) + { + inner = SUBREG_REG (XEXP (SET_DEST (x), 0)); + len = GET_MODE_BITSIZE (GET_MODE (XEXP (SET_DEST (x), 0))); + pos = const0_rtx; + } + else if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT + && GET_CODE (XEXP (SET_DEST (x), 1)) == CONST_INT) + { + inner = XEXP (SET_DEST (x), 0); + len = INTVAL (XEXP (SET_DEST (x), 1)); + pos = XEXP (SET_DEST (x), 2); + + /* If the position is constant and spans the width of INNER, + surround INNER with a USE to indicate this. */ + if (GET_CODE (pos) == CONST_INT + && INTVAL (pos) + len > GET_MODE_BITSIZE (GET_MODE (inner))) + inner = gen_rtx (USE, GET_MODE (SET_DEST (x)), inner); + +#if BITS_BIG_ENDIAN + if (GET_CODE (pos) == CONST_INT) + pos = GEN_INT (GET_MODE_BITSIZE (GET_MODE (inner)) - len + - INTVAL (pos)); + else if (GET_CODE (pos) == MINUS + && GET_CODE (XEXP (pos, 1)) == CONST_INT + && (INTVAL (XEXP (pos, 1)) + == GET_MODE_BITSIZE (GET_MODE (inner)) - len)) + /* If position is ADJUST - X, new position is X. */ + pos = XEXP (pos, 0); + else + pos = gen_binary (MINUS, GET_MODE (pos), + GEN_INT (GET_MODE_BITSIZE (GET_MODE (inner)) + - len), + pos); +#endif + } + + /* A SUBREG between two modes that occupy the same numbers of words + can be done by moving the SUBREG to the source. */ + else if (GET_CODE (SET_DEST (x)) == SUBREG + && (((GET_MODE_SIZE (GET_MODE (SET_DEST (x))) + + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD) + == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (x)))) + + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD))) + { + x = gen_rtx (SET, VOIDmode, SUBREG_REG (SET_DEST (x)), + gen_lowpart_for_combine (GET_MODE (SUBREG_REG (SET_DEST (x))), + SET_SRC (x))); + continue; + } + else + break; + + while (GET_CODE (inner) == SUBREG && subreg_lowpart_p (inner)) + inner = SUBREG_REG (inner); + + compute_mode = GET_MODE (inner); + + /* Compute a mask of LEN bits, if we can do this on the host machine. */ + if (len < HOST_BITS_PER_WIDE_INT) + mask = GEN_INT (((HOST_WIDE_INT) 1 << len) - 1); + else + break; + + /* Now compute the equivalent expression. Make a copy of INNER + for the SET_DEST in case it is a MEM into which we will substitute; + we don't want shared RTL in that case. */ + x = gen_rtx (SET, VOIDmode, copy_rtx (inner), + gen_binary (IOR, compute_mode, + gen_binary (AND, compute_mode, + gen_unary (NOT, compute_mode, + gen_binary (ASHIFT, + compute_mode, + mask, pos)), + inner), + gen_binary (ASHIFT, compute_mode, + gen_binary (AND, compute_mode, + gen_lowpart_for_combine + (compute_mode, + SET_SRC (x)), + mask), + pos))); + } + + return x; +} + +/* Return an RTX for a reference to LEN bits of INNER. If POS_RTX is nonzero, + it is an RTX that represents a variable starting position; otherwise, + POS is the (constant) starting bit position (counted from the LSB). + + INNER may be a USE. This will occur when we started with a bitfield + that went outside the boundary of the object in memory, which is + allowed on most machines. To isolate this case, we produce a USE + whose mode is wide enough and surround the MEM with it. The only + code that understands the USE is this routine. If it is not removed, + it will cause the resulting insn not to match. + + UNSIGNEDP is non-zero for an unsigned reference and zero for a + signed reference. + + IN_DEST is non-zero if this is a reference in the destination of a + SET. This is used when a ZERO_ or SIGN_EXTRACT isn't needed. If non-zero, + a STRICT_LOW_PART will be used, if zero, ZERO_EXTEND or SIGN_EXTEND will + be used. + + IN_COMPARE is non-zero if we are in a COMPARE. This means that a + ZERO_EXTRACT should be built even for bits starting at bit 0. + + MODE is the desired mode of the result (if IN_DEST == 0). */ + +static rtx +make_extraction (mode, inner, pos, pos_rtx, len, + unsignedp, in_dest, in_compare) + enum machine_mode mode; + rtx inner; + int pos; + rtx pos_rtx; + int len; + int unsignedp; + int in_dest, in_compare; +{ + /* This mode describes the size of the storage area + to fetch the overall value from. Within that, we + ignore the POS lowest bits, etc. */ + enum machine_mode is_mode = GET_MODE (inner); + enum machine_mode inner_mode; + enum machine_mode wanted_mem_mode = byte_mode; + enum machine_mode pos_mode = word_mode; + enum machine_mode extraction_mode = word_mode; + enum machine_mode tmode = mode_for_size (len, MODE_INT, 1); + int spans_byte = 0; + rtx new = 0; + rtx orig_pos_rtx = pos_rtx; + + /* Get some information about INNER and get the innermost object. */ + if (GET_CODE (inner) == USE) + /* (use:SI (mem:QI foo)) stands for (mem:SI foo). */ + /* We don't need to adjust the position because we set up the USE + to pretend that it was a full-word object. */ + spans_byte = 1, inner = XEXP (inner, 0); + else if (GET_CODE (inner) == SUBREG && subreg_lowpart_p (inner)) + { + /* If going from (subreg:SI (mem:QI ...)) to (mem:QI ...), + consider just the QI as the memory to extract from. + The subreg adds or removes high bits; its mode is + irrelevant to the meaning of this extraction, + since POS and LEN count from the lsb. */ + if (GET_CODE (SUBREG_REG (inner)) == MEM) + is_mode = GET_MODE (SUBREG_REG (inner)); + inner = SUBREG_REG (inner); + } + + inner_mode = GET_MODE (inner); + + if (pos_rtx && GET_CODE (pos_rtx) == CONST_INT) + pos = INTVAL (pos_rtx), pos_rtx = 0; + + /* See if this can be done without an extraction. We never can if the + width of the field is not the same as that of some integer mode. For + registers, we can only avoid the extraction if the position is at the + low-order bit and this is either not in the destination or we have the + appropriate STRICT_LOW_PART operation available. + + For MEM, we can avoid an extract if the field starts on an appropriate + boundary and we can change the mode of the memory reference. However, + we cannot directly access the MEM if we have a USE and the underlying + MEM is not TMODE. This combination means that MEM was being used in a + context where bits outside its mode were being referenced; that is only + valid in bit-field insns. */ + + if (tmode != BLKmode + && ! (spans_byte && inner_mode != tmode) + && ((pos_rtx == 0 && pos == 0 && GET_CODE (inner) != MEM + && (! in_dest + || (GET_CODE (inner) == REG + && (movstrict_optab->handlers[(int) tmode].insn_code + != CODE_FOR_nothing)))) + || (GET_CODE (inner) == MEM && pos_rtx == 0 + && (pos + % (STRICT_ALIGNMENT ? GET_MODE_ALIGNMENT (tmode) + : BITS_PER_UNIT)) == 0 + /* We can't do this if we are widening INNER_MODE (it + may not be aligned, for one thing). */ + && GET_MODE_BITSIZE (inner_mode) >= GET_MODE_BITSIZE (tmode) + && (inner_mode == tmode + || (! mode_dependent_address_p (XEXP (inner, 0)) + && ! MEM_VOLATILE_P (inner)))))) + { + /* If INNER is a MEM, make a new MEM that encompasses just the desired + field. If the original and current mode are the same, we need not + adjust the offset. Otherwise, we do if bytes big endian. + + If INNER is not a MEM, get a piece consisting of the just the field + of interest (in this case POS must be 0). */ + + if (GET_CODE (inner) == MEM) + { + int offset; + /* POS counts from lsb, but make OFFSET count in memory order. */ + if (BYTES_BIG_ENDIAN) + offset = (GET_MODE_BITSIZE (is_mode) - len - pos) / BITS_PER_UNIT; + else + offset = pos / BITS_PER_UNIT; + + new = gen_rtx (MEM, tmode, plus_constant (XEXP (inner, 0), offset)); + RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (inner); + MEM_VOLATILE_P (new) = MEM_VOLATILE_P (inner); + MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (inner); + } + else if (GET_CODE (inner) == REG) + /* We can't call gen_lowpart_for_combine here since we always want + a SUBREG and it would sometimes return a new hard register. */ + new = gen_rtx (SUBREG, tmode, inner, + (WORDS_BIG_ENDIAN + && GET_MODE_SIZE (inner_mode) > UNITS_PER_WORD + ? ((GET_MODE_SIZE (inner_mode) - GET_MODE_SIZE (tmode)) + / UNITS_PER_WORD) + : 0)); + else + new = force_to_mode (inner, tmode, len, NULL_RTX); + + /* If this extraction is going into the destination of a SET, + make a STRICT_LOW_PART unless we made a MEM. */ + + if (in_dest) + return (GET_CODE (new) == MEM ? new + : (GET_CODE (new) != SUBREG + ? gen_rtx (CLOBBER, tmode, const0_rtx) + : gen_rtx_combine (STRICT_LOW_PART, VOIDmode, new))); + + /* Otherwise, sign- or zero-extend unless we already are in the + proper mode. */ + + return (mode == tmode ? new + : gen_rtx_combine (unsignedp ? ZERO_EXTEND : SIGN_EXTEND, + mode, new)); + } + + /* Unless this is a COMPARE or we have a funny memory reference, + don't do anything with zero-extending field extracts starting at + the low-order bit since they are simple AND operations. */ + if (pos_rtx == 0 && pos == 0 && ! in_dest + && ! in_compare && ! spans_byte && unsignedp) + return 0; + + /* Get the mode to use should INNER be a MEM, the mode for the position, + and the mode for the result. */ +#ifdef HAVE_insv + if (in_dest) + { + wanted_mem_mode = insn_operand_mode[(int) CODE_FOR_insv][0]; + pos_mode = insn_operand_mode[(int) CODE_FOR_insv][2]; + extraction_mode = insn_operand_mode[(int) CODE_FOR_insv][3]; + } +#endif + +#ifdef HAVE_extzv + if (! in_dest && unsignedp) + { + wanted_mem_mode = insn_operand_mode[(int) CODE_FOR_extzv][1]; + pos_mode = insn_operand_mode[(int) CODE_FOR_extzv][3]; + extraction_mode = insn_operand_mode[(int) CODE_FOR_extzv][0]; + } +#endif + +#ifdef HAVE_extv + if (! in_dest && ! unsignedp) + { + wanted_mem_mode = insn_operand_mode[(int) CODE_FOR_extv][1]; + pos_mode = insn_operand_mode[(int) CODE_FOR_extv][3]; + extraction_mode = insn_operand_mode[(int) CODE_FOR_extv][0]; + } +#endif + + /* Never narrow an object, since that might not be safe. */ + + if (mode != VOIDmode + && GET_MODE_SIZE (extraction_mode) < GET_MODE_SIZE (mode)) + extraction_mode = mode; + + if (pos_rtx && GET_MODE (pos_rtx) != VOIDmode + && GET_MODE_SIZE (pos_mode) < GET_MODE_SIZE (GET_MODE (pos_rtx))) + pos_mode = GET_MODE (pos_rtx); + + /* If this is not from memory or we have to change the mode of memory and + cannot, the desired mode is EXTRACTION_MODE. */ + if (GET_CODE (inner) != MEM + || (inner_mode != wanted_mem_mode + && (mode_dependent_address_p (XEXP (inner, 0)) + || MEM_VOLATILE_P (inner)))) + wanted_mem_mode = extraction_mode; + +#if BITS_BIG_ENDIAN + /* If position is constant, compute new position. Otherwise, build + subtraction. */ + if (pos_rtx == 0) + pos = (MAX (GET_MODE_BITSIZE (is_mode), GET_MODE_BITSIZE (wanted_mem_mode)) + - len - pos); + else + pos_rtx + = gen_rtx_combine (MINUS, GET_MODE (pos_rtx), + GEN_INT (MAX (GET_MODE_BITSIZE (is_mode), + GET_MODE_BITSIZE (wanted_mem_mode)) + - len), + pos_rtx); +#endif + + /* If INNER has a wider mode, make it smaller. If this is a constant + extract, try to adjust the byte to point to the byte containing + the value. */ + if (wanted_mem_mode != VOIDmode + && GET_MODE_SIZE (wanted_mem_mode) < GET_MODE_SIZE (is_mode) + && ((GET_CODE (inner) == MEM + && (inner_mode == wanted_mem_mode + || (! mode_dependent_address_p (XEXP (inner, 0)) + && ! MEM_VOLATILE_P (inner)))))) + { + int offset = 0; + + /* The computations below will be correct if the machine is big + endian in both bits and bytes or little endian in bits and bytes. + If it is mixed, we must adjust. */ + + /* If bytes are big endian and we had a paradoxical SUBREG, we must + adjust OFFSET to compensate. */ +#if BYTES_BIG_ENDIAN + if (! spans_byte + && GET_MODE_SIZE (inner_mode) < GET_MODE_SIZE (is_mode)) + offset -= GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (inner_mode); +#endif + + /* If this is a constant position, we can move to the desired byte. */ + if (pos_rtx == 0) + { + offset += pos / BITS_PER_UNIT; + pos %= GET_MODE_BITSIZE (wanted_mem_mode); + } + +#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN + if (! spans_byte && is_mode != wanted_mem_mode) + offset = (GET_MODE_SIZE (is_mode) + - GET_MODE_SIZE (wanted_mem_mode) - offset); +#endif + + if (offset != 0 || inner_mode != wanted_mem_mode) + { + rtx newmem = gen_rtx (MEM, wanted_mem_mode, + plus_constant (XEXP (inner, 0), offset)); + RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (inner); + MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (inner); + MEM_IN_STRUCT_P (newmem) = MEM_IN_STRUCT_P (inner); + inner = newmem; + } + } + + /* If INNER is not memory, we can always get it into the proper mode. */ + else if (GET_CODE (inner) != MEM) + inner = force_to_mode (inner, extraction_mode, + (pos < 0 ? GET_MODE_BITSIZE (extraction_mode) + : len + pos), + NULL_RTX); + + /* Adjust mode of POS_RTX, if needed. If we want a wider mode, we + have to zero extend. Otherwise, we can just use a SUBREG. */ + if (pos_rtx != 0 + && GET_MODE_SIZE (pos_mode) > GET_MODE_SIZE (GET_MODE (pos_rtx))) + pos_rtx = gen_rtx_combine (ZERO_EXTEND, pos_mode, pos_rtx); + else if (pos_rtx != 0 + && GET_MODE_SIZE (pos_mode) < GET_MODE_SIZE (GET_MODE (pos_rtx))) + pos_rtx = gen_lowpart_for_combine (pos_mode, pos_rtx); + + /* Make POS_RTX unless we already have it and it is correct. If we don't + have a POS_RTX but we do have an ORIG_POS_RTX, the latter must + be a CONST_INT. */ + if (pos_rtx == 0 && orig_pos_rtx != 0 && INTVAL (orig_pos_rtx) == pos) + pos_rtx = orig_pos_rtx; + + else if (pos_rtx == 0) + pos_rtx = GEN_INT (pos); + + /* Make the required operation. See if we can use existing rtx. */ + new = gen_rtx_combine (unsignedp ? ZERO_EXTRACT : SIGN_EXTRACT, + extraction_mode, inner, GEN_INT (len), pos_rtx); + if (! in_dest) + new = gen_lowpart_for_combine (mode, new); + + return new; +} + +/* Look at the expression rooted at X. Look for expressions + equivalent to ZERO_EXTRACT, SIGN_EXTRACT, ZERO_EXTEND, SIGN_EXTEND. + Form these expressions. + + Return the new rtx, usually just X. + + Also, for machines like the Vax that don't have logical shift insns, + try to convert logical to arithmetic shift operations in cases where + they are equivalent. This undoes the canonicalizations to logical + shifts done elsewhere. + + We try, as much as possible, to re-use rtl expressions to save memory. + + IN_CODE says what kind of expression we are processing. Normally, it is + SET. In a memory address (inside a MEM, PLUS or minus, the latter two + being kludges), it is MEM. When processing the arguments of a comparison + or a COMPARE against zero, it is COMPARE. */ + +static rtx +make_compound_operation (x, in_code) + rtx x; + enum rtx_code in_code; +{ + enum rtx_code code = GET_CODE (x); + enum machine_mode mode = GET_MODE (x); + int mode_width = GET_MODE_BITSIZE (mode); + enum rtx_code next_code; + int i, count; + rtx new = 0; + rtx tem; + char *fmt; + + /* Select the code to be used in recursive calls. Once we are inside an + address, we stay there. If we have a comparison, set to COMPARE, + but once inside, go back to our default of SET. */ + + next_code = (code == MEM || code == PLUS || code == MINUS ? MEM + : ((code == COMPARE || GET_RTX_CLASS (code) == '<') + && XEXP (x, 1) == const0_rtx) ? COMPARE + : in_code == COMPARE ? SET : in_code); + + /* Process depending on the code of this operation. If NEW is set + non-zero, it will be returned. */ + + switch (code) + { + case ASHIFT: + case LSHIFT: + /* Convert shifts by constants into multiplications if inside + an address. */ + if (in_code == MEM && GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT + && INTVAL (XEXP (x, 1)) >= 0) + { + new = make_compound_operation (XEXP (x, 0), next_code); + new = gen_rtx_combine (MULT, mode, new, + GEN_INT ((HOST_WIDE_INT) 1 + << INTVAL (XEXP (x, 1)))); + } + break; + + case AND: + /* If the second operand is not a constant, we can't do anything + with it. */ + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + break; + + /* If the constant is a power of two minus one and the first operand + is a logical right shift, make an extraction. */ + if (GET_CODE (XEXP (x, 0)) == LSHIFTRT + && (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0) + { + new = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code); + new = make_extraction (mode, new, 0, XEXP (XEXP (x, 0), 1), i, 1, + 0, in_code == COMPARE); + } + + /* Same as previous, but for (subreg (lshiftrt ...)) in first op. */ + else if (GET_CODE (XEXP (x, 0)) == SUBREG + && subreg_lowpart_p (XEXP (x, 0)) + && GET_CODE (SUBREG_REG (XEXP (x, 0))) == LSHIFTRT + && (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0) + { + new = make_compound_operation (XEXP (SUBREG_REG (XEXP (x, 0)), 0), + next_code); + new = make_extraction (GET_MODE (SUBREG_REG (XEXP (x, 0))), new, 0, + XEXP (SUBREG_REG (XEXP (x, 0)), 1), i, 1, + 0, in_code == COMPARE); + } + + /* If we are have (and (rotate X C) M) and C is larger than the number + of bits in M, this is an extraction. */ + + else if (GET_CODE (XEXP (x, 0)) == ROTATE + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0 + && i <= INTVAL (XEXP (XEXP (x, 0), 1))) + { + new = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code); + new = make_extraction (mode, new, + (GET_MODE_BITSIZE (mode) + - INTVAL (XEXP (XEXP (x, 0), 1))), + NULL_RTX, i, 1, 0, in_code == COMPARE); + } + + /* On machines without logical shifts, if the operand of the AND is + a logical shift and our mask turns off all the propagated sign + bits, we can replace the logical shift with an arithmetic shift. */ + else if (ashr_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing + && (lshr_optab->handlers[(int) mode].insn_code + == CODE_FOR_nothing) + && GET_CODE (XEXP (x, 0)) == LSHIFTRT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && INTVAL (XEXP (XEXP (x, 0), 1)) >= 0 + && INTVAL (XEXP (XEXP (x, 0), 1)) < HOST_BITS_PER_WIDE_INT + && mode_width <= HOST_BITS_PER_WIDE_INT) + { + unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode); + + mask >>= INTVAL (XEXP (XEXP (x, 0), 1)); + if ((INTVAL (XEXP (x, 1)) & ~mask) == 0) + SUBST (XEXP (x, 0), + gen_rtx_combine (ASHIFTRT, mode, + make_compound_operation (XEXP (XEXP (x, 0), 0), + next_code), + XEXP (XEXP (x, 0), 1))); + } + + /* If the constant is one less than a power of two, this might be + representable by an extraction even if no shift is present. + If it doesn't end up being a ZERO_EXTEND, we will ignore it unless + we are in a COMPARE. */ + else if ((i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0) + new = make_extraction (mode, + make_compound_operation (XEXP (x, 0), + next_code), + 0, NULL_RTX, i, 1, 0, in_code == COMPARE); + + /* If we are in a comparison and this is an AND with a power of two, + convert this into the appropriate bit extract. */ + else if (in_code == COMPARE + && (i = exact_log2 (INTVAL (XEXP (x, 1)))) >= 0) + new = make_extraction (mode, + make_compound_operation (XEXP (x, 0), + next_code), + i, NULL_RTX, 1, 1, 0, 1); + + break; + + case LSHIFTRT: + /* If the sign bit is known to be zero, replace this with an + arithmetic shift. */ + if (ashr_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing + && lshr_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing + && mode_width <= HOST_BITS_PER_WIDE_INT + && (nonzero_bits (XEXP (x, 0), mode) & (1 << (mode_width - 1))) == 0) + { + new = gen_rtx_combine (ASHIFTRT, mode, + make_compound_operation (XEXP (x, 0), + next_code), + XEXP (x, 1)); + break; + } + + /* ... fall through ... */ + + case ASHIFTRT: + /* If we have (ashiftrt (ashift foo C1) C2) with C2 >= C1, + this is a SIGN_EXTRACT. */ + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && GET_CODE (XEXP (x, 0)) == ASHIFT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) >= INTVAL (XEXP (XEXP (x, 0), 1))) + { + new = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code); + new = make_extraction (mode, new, + (INTVAL (XEXP (x, 1)) + - INTVAL (XEXP (XEXP (x, 0), 1))), + NULL_RTX, mode_width - INTVAL (XEXP (x, 1)), + code == LSHIFTRT, 0, in_code == COMPARE); + } + + /* Similarly if we have (ashifrt (OP (ashift foo C1) C3) C2). In these + cases, we are better off returning a SIGN_EXTEND of the operation. */ + + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && (GET_CODE (XEXP (x, 0)) == IOR || GET_CODE (XEXP (x, 0)) == AND + || GET_CODE (XEXP (x, 0)) == XOR + || GET_CODE (XEXP (x, 0)) == PLUS) + && GET_CODE (XEXP (XEXP (x, 0), 0)) == ASHIFT + && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT + && INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1)) < HOST_BITS_PER_WIDE_INT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && 0 == (INTVAL (XEXP (XEXP (x, 0), 1)) + & (((HOST_WIDE_INT) 1 + << (MIN (INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1)), + INTVAL (XEXP (x, 1))) + - 1))))) + { + rtx c1 = XEXP (XEXP (XEXP (x, 0), 0), 1); + rtx c2 = XEXP (x, 1); + rtx c3 = XEXP (XEXP (x, 0), 1); + HOST_WIDE_INT newop1; + rtx inner = XEXP (XEXP (XEXP (x, 0), 0), 0); + + /* If C1 > C2, INNER needs to have the shift performed on it + for C1-C2 bits. */ + if (INTVAL (c1) > INTVAL (c2)) + { + inner = gen_binary (ASHIFT, mode, inner, + GEN_INT (INTVAL (c1) - INTVAL (c2))); + c1 = c2; + } + + newop1 = INTVAL (c3) >> INTVAL (c1); + new = make_compound_operation (inner, + GET_CODE (XEXP (x, 0)) == PLUS + ? MEM : GET_CODE (XEXP (x, 0))); + new = make_extraction (mode, + gen_binary (GET_CODE (XEXP (x, 0)), mode, new, + GEN_INT (newop1)), + INTVAL (c2) - INTVAL (c1), + NULL_RTX, mode_width - INTVAL (c2), + code == LSHIFTRT, 0, in_code == COMPARE); + } + + /* Similarly for (ashiftrt (neg (ashift FOO C1)) C2). */ + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && GET_CODE (XEXP (x, 0)) == NEG + && GET_CODE (XEXP (XEXP (x, 0), 0)) == ASHIFT + && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) >= INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1))) + { + new = make_compound_operation (XEXP (XEXP (XEXP (x, 0), 0), 0), + next_code); + new = make_extraction (mode, + gen_unary (GET_CODE (XEXP (x, 0)), mode, + new, 0), + (INTVAL (XEXP (x, 1)) + - INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1))), + NULL_RTX, mode_width - INTVAL (XEXP (x, 1)), + code == LSHIFTRT, 0, in_code == COMPARE); + } + break; + + case SUBREG: + /* Call ourselves recursively on the inner expression. If we are + narrowing the object and it has a different RTL code from + what it originally did, do this SUBREG as a force_to_mode. */ + + tem = make_compound_operation (SUBREG_REG (x), in_code); + if (GET_CODE (tem) != GET_CODE (SUBREG_REG (x)) + && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (tem)) + && subreg_lowpart_p (x)) + { + rtx newer = force_to_mode (tem, mode, + GET_MODE_BITSIZE (mode), NULL_RTX); + + /* If we have something other than a SUBREG, we might have + done an expansion, so rerun outselves. */ + if (GET_CODE (newer) != SUBREG) + newer = make_compound_operation (newer, in_code); + + return newer; + } + } + + if (new) + { + x = gen_lowpart_for_combine (mode, new); + code = GET_CODE (x); + } + + /* Now recursively process each operand of this operation. */ + fmt = GET_RTX_FORMAT (code); + for (i = 0; i < GET_RTX_LENGTH (code); i++) + if (fmt[i] == 'e') + { + new = make_compound_operation (XEXP (x, i), next_code); + SUBST (XEXP (x, i), new); + } + + return x; +} + +/* Given M see if it is a value that would select a field of bits + within an item, but not the entire word. Return -1 if not. + Otherwise, return the starting position of the field, where 0 is the + low-order bit. + + *PLEN is set to the length of the field. */ + +static int +get_pos_from_mask (m, plen) + unsigned HOST_WIDE_INT m; + int *plen; +{ + /* Get the bit number of the first 1 bit from the right, -1 if none. */ + int pos = exact_log2 (m & - m); + + if (pos < 0) + return -1; + + /* Now shift off the low-order zero bits and see if we have a power of + two minus 1. */ + *plen = exact_log2 ((m >> pos) + 1); + + if (*plen <= 0) + return -1; + + return pos; +} + +/* Rewrite X so that it is an expression in MODE. We only care about the + low-order BITS bits so we can ignore AND operations that just clear + higher-order bits. + + Also, if REG is non-zero and X is a register equal in value to REG, + replace X with REG. */ + +static rtx +force_to_mode (x, mode, bits, reg) + rtx x; + enum machine_mode mode; + int bits; + rtx reg; +{ + enum rtx_code code = GET_CODE (x); + enum machine_mode op_mode = mode; + + /* If X is narrower than MODE or if BITS is larger than the size of MODE, + just get X in the proper mode. */ + + if (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (mode) + || bits > GET_MODE_BITSIZE (mode)) + return gen_lowpart_for_combine (mode, x); + + switch (code) + { + case SIGN_EXTEND: + case ZERO_EXTEND: + case ZERO_EXTRACT: + case SIGN_EXTRACT: + x = expand_compound_operation (x); + if (GET_CODE (x) != code) + return force_to_mode (x, mode, bits, reg); + break; + + case REG: + if (reg != 0 && (rtx_equal_p (get_last_value (reg), x) + || rtx_equal_p (reg, get_last_value (x)))) + x = reg; + break; + + case CONST_INT: + if (bits < HOST_BITS_PER_WIDE_INT) + x = GEN_INT (INTVAL (x) & (((HOST_WIDE_INT) 1 << bits) - 1)); + return x; + + case SUBREG: + /* Ignore low-order SUBREGs. */ + if (subreg_lowpart_p (x)) + return force_to_mode (SUBREG_REG (x), mode, bits, reg); + break; + + case AND: + /* If this is an AND with a constant. Otherwise, we fall through to + do the general binary case. */ + + if (GET_CODE (XEXP (x, 1)) == CONST_INT) + { + HOST_WIDE_INT mask = INTVAL (XEXP (x, 1)); + int len = exact_log2 (mask + 1); + rtx op = XEXP (x, 0); + + /* If this is masking some low-order bits, we may be able to + impose a stricter constraint on what bits of the operand are + required. */ + + op = force_to_mode (op, mode, len > 0 ? MIN (len, bits) : bits, + reg); + + if (bits < HOST_BITS_PER_WIDE_INT) + mask &= ((HOST_WIDE_INT) 1 << bits) - 1; + + /* If we have no AND in MODE, use the original mode for the + operation. */ + + if (and_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) + op_mode = GET_MODE (x); + + x = simplify_and_const_int (x, op_mode, op, mask); + + /* If X is still an AND, see if it is an AND with a mask that + is just some low-order bits. If so, and it is BITS wide (it + can't be wider), we don't need it. */ + + if (GET_CODE (x) == AND && GET_CODE (XEXP (x, 1)) == CONST_INT + && bits < HOST_BITS_PER_WIDE_INT + && INTVAL (XEXP (x, 1)) == ((HOST_WIDE_INT) 1 << bits) - 1) + x = XEXP (x, 0); + + break; + } + + /* ... fall through ... */ + + case PLUS: + case MINUS: + case MULT: + case IOR: + case XOR: + /* For most binary operations, just propagate into the operation and + change the mode if we have an operation of that mode. */ + + if ((code == PLUS + && add_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) + || (code == MINUS + && sub_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) + || (code == MULT && (smul_optab->handlers[(int) mode].insn_code + == CODE_FOR_nothing)) + || (code == AND + && and_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) + || (code == IOR + && ior_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) + || (code == XOR && (xor_optab->handlers[(int) mode].insn_code + == CODE_FOR_nothing))) + op_mode = GET_MODE (x); + + x = gen_binary (code, op_mode, + gen_lowpart_for_combine (op_mode, + force_to_mode (XEXP (x, 0), + mode, bits, + reg)), + gen_lowpart_for_combine (op_mode, + force_to_mode (XEXP (x, 1), + mode, bits, + reg))); + break; + + case ASHIFT: + case LSHIFT: + /* For left shifts, do the same, but just for the first operand. + However, we cannot do anything with shifts where we cannot + guarantee that the counts are smaller than the size of the mode + because such a count will have a different meaning in a + wider mode. + + If we can narrow the shift and know the count, we need even fewer + bits of the first operand. */ + + if (! (GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) < GET_MODE_BITSIZE (mode)) + && ! (GET_MODE (XEXP (x, 1)) != VOIDmode + && (nonzero_bits (XEXP (x, 1), GET_MODE (XEXP (x, 1))) + < (unsigned HOST_WIDE_INT) GET_MODE_BITSIZE (mode)))) + break; + + if (GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) < bits) + bits -= INTVAL (XEXP (x, 1)); + + if ((code == ASHIFT + && ashl_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) + || (code == LSHIFT && (lshl_optab->handlers[(int) mode].insn_code + == CODE_FOR_nothing))) + op_mode = GET_MODE (x); + + x = gen_binary (code, op_mode, + gen_lowpart_for_combine (op_mode, + force_to_mode (XEXP (x, 0), + mode, bits, + reg)), + XEXP (x, 1)); + break; + + case LSHIFTRT: + /* Here we can only do something if the shift count is a constant and + the count plus BITS is no larger than the width of MODE. In that + case, we can do the shift in MODE. */ + + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) + bits <= GET_MODE_BITSIZE (mode)) + { + rtx inner = force_to_mode (XEXP (x, 0), mode, + bits + INTVAL (XEXP (x, 1)), reg); + + if (lshr_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) + op_mode = GET_MODE (x); + + x = gen_binary (LSHIFTRT, op_mode, + gen_lowpart_for_combine (op_mode, inner), + XEXP (x, 1)); + } + break; + + case ASHIFTRT: + /* If this is a sign-extension operation that just affects bits + we don't care about, remove it. */ + + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) >= 0 + && INTVAL (XEXP (x, 1)) <= GET_MODE_BITSIZE (GET_MODE (x)) - bits + && GET_CODE (XEXP (x, 0)) == ASHIFT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && INTVAL (XEXP (XEXP (x, 0), 1)) == INTVAL (XEXP (x, 1))) + return force_to_mode (XEXP (XEXP (x, 0), 0), mode, bits, reg); + break; + + case NEG: + case NOT: + if ((code == NEG + && neg_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) + || (code == NOT && (one_cmpl_optab->handlers[(int) mode].insn_code + == CODE_FOR_nothing))) + op_mode = GET_MODE (x); + + /* Handle these similarly to the way we handle most binary operations. */ + x = gen_unary (code, op_mode, + gen_lowpart_for_combine (op_mode, + force_to_mode (XEXP (x, 0), mode, + bits, reg))); + break; + + case IF_THEN_ELSE: + /* We have no way of knowing if the IF_THEN_ELSE can itself be + written in a narrower mode. We play it safe and do not do so. */ + + SUBST (XEXP (x, 1), + gen_lowpart_for_combine (GET_MODE (x), + force_to_mode (XEXP (x, 1), mode, + bits, reg))); + SUBST (XEXP (x, 2), + gen_lowpart_for_combine (GET_MODE (x), + force_to_mode (XEXP (x, 2), mode, + bits, reg))); + break; + } + + /* Ensure we return a value of the proper mode. */ + return gen_lowpart_for_combine (mode, x); +} + +/* Return the value of expression X given the fact that condition COND + is known to be true when applied to REG as its first operand and VAL + as its second. X is known to not be shared and so can be modified in + place. + + We only handle the simplest cases, and specifically those cases that + arise with IF_THEN_ELSE expressions. */ + +static rtx +known_cond (x, cond, reg, val) + rtx x; + enum rtx_code cond; + rtx reg, val; +{ + enum rtx_code code = GET_CODE (x); + rtx new, temp; + char *fmt; + int i, j; + + if (side_effects_p (x)) + return x; + + if (cond == EQ && rtx_equal_p (x, reg)) + return val; + + /* If X is (abs REG) and we know something about REG's relationship + with zero, we may be able to simplify this. */ + + if (code == ABS && rtx_equal_p (XEXP (x, 0), reg) && val == const0_rtx) + switch (cond) + { + case GE: case GT: case EQ: + return XEXP (x, 0); + case LT: case LE: + return gen_unary (NEG, GET_MODE (XEXP (x, 0)), XEXP (x, 0)); + } + + /* The only other cases we handle are MIN, MAX, and comparisons if the + operands are the same as REG and VAL. */ + + else if (GET_RTX_CLASS (code) == '<' || GET_RTX_CLASS (code) == 'c') + { + if (rtx_equal_p (XEXP (x, 0), val)) + cond = swap_condition (cond), temp = val, val = reg, reg = temp; + + if (rtx_equal_p (XEXP (x, 0), reg) && rtx_equal_p (XEXP (x, 1), val)) + { + if (GET_RTX_CLASS (code) == '<') + return (comparison_dominates_p (cond, code) ? const_true_rtx + : (comparison_dominates_p (cond, + reverse_condition (code)) + ? const0_rtx : x)); + + else if (code == SMAX || code == SMIN + || code == UMIN || code == UMAX) + { + int unsignedp = (code == UMIN || code == UMAX); + + if (code == SMAX || code == UMAX) + cond = reverse_condition (cond); + + switch (cond) + { + case GE: case GT: + return unsignedp ? x : XEXP (x, 1); + case LE: case LT: + return unsignedp ? x : XEXP (x, 0); + case GEU: case GTU: + return unsignedp ? XEXP (x, 1) : x; + case LEU: case LTU: + return unsignedp ? XEXP (x, 0) : x; + } + } + } + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + SUBST (XEXP (x, i), known_cond (XEXP (x, i), cond, reg, val)); + else if (fmt[i] == 'E') + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + SUBST (XVECEXP (x, i, j), known_cond (XVECEXP (x, i, j), + cond, reg, val)); + } + + return x; +} + +/* See if X, a SET operation, can be rewritten as a bit-field assignment. + Return that assignment if so. + + We only handle the most common cases. */ + +static rtx +make_field_assignment (x) + rtx x; +{ + rtx dest = SET_DEST (x); + rtx src = SET_SRC (x); + rtx ourdest; + rtx assign; + HOST_WIDE_INT c1; + int pos, len; + rtx other; + enum machine_mode mode; + + /* If SRC was (and (not (ashift (const_int 1) POS)) DEST), this is + a clear of a one-bit field. We will have changed it to + (and (rotate (const_int -2) POS) DEST), so check for that. Also check + for a SUBREG. */ + + if (GET_CODE (src) == AND && GET_CODE (XEXP (src, 0)) == ROTATE + && GET_CODE (XEXP (XEXP (src, 0), 0)) == CONST_INT + && INTVAL (XEXP (XEXP (src, 0), 0)) == -2 + && (rtx_equal_p (dest, XEXP (src, 1)) + || rtx_equal_p (dest, get_last_value (XEXP (src, 1))) + || rtx_equal_p (get_last_value (dest), XEXP (src, 1)))) + { + assign = make_extraction (VOIDmode, dest, 0, XEXP (XEXP (src, 0), 1), + 1, 1, 1, 0); + return gen_rtx (SET, VOIDmode, assign, const0_rtx); + } + + else if (GET_CODE (src) == AND && GET_CODE (XEXP (src, 0)) == SUBREG + && subreg_lowpart_p (XEXP (src, 0)) + && (GET_MODE_SIZE (GET_MODE (XEXP (src, 0))) + < GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (src, 0))))) + && GET_CODE (SUBREG_REG (XEXP (src, 0))) == ROTATE + && INTVAL (XEXP (SUBREG_REG (XEXP (src, 0)), 0)) == -2 + && (rtx_equal_p (dest, XEXP (src, 1)) + || rtx_equal_p (dest, get_last_value (XEXP (src, 1))) + || rtx_equal_p (get_last_value (dest), XEXP (src, 1)))) + { + assign = make_extraction (VOIDmode, dest, 0, + XEXP (SUBREG_REG (XEXP (src, 0)), 1), + 1, 1, 1, 0); + return gen_rtx (SET, VOIDmode, assign, const0_rtx); + } + + /* If SRC is (ior (ashift (const_int 1) POS DEST)), this is a set of a + one-bit field. */ + else if (GET_CODE (src) == IOR && GET_CODE (XEXP (src, 0)) == ASHIFT + && XEXP (XEXP (src, 0), 0) == const1_rtx + && (rtx_equal_p (dest, XEXP (src, 1)) + || rtx_equal_p (dest, get_last_value (XEXP (src, 1))) + || rtx_equal_p (get_last_value (dest), XEXP (src, 1)))) + { + assign = make_extraction (VOIDmode, dest, 0, XEXP (XEXP (src, 0), 1), + 1, 1, 1, 0); + return gen_rtx (SET, VOIDmode, assign, const1_rtx); + } + + /* The other case we handle is assignments into a constant-position + field. They look like (ior (and DEST C1) OTHER). If C1 represents + a mask that has all one bits except for a group of zero bits and + OTHER is known to have zeros where C1 has ones, this is such an + assignment. Compute the position and length from C1. Shift OTHER + to the appropriate position, force it to the required mode, and + make the extraction. Check for the AND in both operands. */ + + if (GET_CODE (src) == IOR && GET_CODE (XEXP (src, 0)) == AND + && GET_CODE (XEXP (XEXP (src, 0), 1)) == CONST_INT + && (rtx_equal_p (XEXP (XEXP (src, 0), 0), dest) + || rtx_equal_p (XEXP (XEXP (src, 0), 0), get_last_value (dest)) + || rtx_equal_p (get_last_value (XEXP (XEXP (src, 0), 1)), dest))) + c1 = INTVAL (XEXP (XEXP (src, 0), 1)), other = XEXP (src, 1); + else if (GET_CODE (src) == IOR && GET_CODE (XEXP (src, 1)) == AND + && GET_CODE (XEXP (XEXP (src, 1), 1)) == CONST_INT + && (rtx_equal_p (XEXP (XEXP (src, 1), 0), dest) + || rtx_equal_p (XEXP (XEXP (src, 1), 0), get_last_value (dest)) + || rtx_equal_p (get_last_value (XEXP (XEXP (src, 1), 0)), + dest))) + c1 = INTVAL (XEXP (XEXP (src, 1), 1)), other = XEXP (src, 0); + else + return x; + + pos = get_pos_from_mask (~c1, &len); + if (pos < 0 || pos + len > GET_MODE_BITSIZE (GET_MODE (dest)) + || (GET_MODE_BITSIZE (GET_MODE (other)) <= HOST_BITS_PER_WIDE_INT + && (c1 & nonzero_bits (other, GET_MODE (other))) != 0)) + return x; + + assign = make_extraction (VOIDmode, dest, pos, NULL_RTX, len, 1, 1, 0); + + /* The mode to use for the source is the mode of the assignment, or of + what is inside a possible STRICT_LOW_PART. */ + mode = (GET_CODE (assign) == STRICT_LOW_PART + ? GET_MODE (XEXP (assign, 0)) : GET_MODE (assign)); + + /* Shift OTHER right POS places and make it the source, restricting it + to the proper length and mode. */ + + src = force_to_mode (simplify_shift_const (NULL_RTX, LSHIFTRT, + GET_MODE (src), other, pos), + mode, len, dest); + + return gen_rtx_combine (SET, VOIDmode, assign, src); +} + +/* See if X is of the form (+ (* a c) (* b c)) and convert to (* (+ a b) c) + if so. */ + +static rtx +apply_distributive_law (x) + rtx x; +{ + enum rtx_code code = GET_CODE (x); + rtx lhs, rhs, other; + rtx tem; + enum rtx_code inner_code; + + /* Distributivity is not true for floating point. + It can change the value. So don't do it. + -- rms and moshier@world.std.com. */ + if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) + return x; + + /* The outer operation can only be one of the following: */ + if (code != IOR && code != AND && code != XOR + && code != PLUS && code != MINUS) + return x; + + lhs = XEXP (x, 0), rhs = XEXP (x, 1); + + /* If either operand is a primitive we can't do anything, so get out fast. */ + if (GET_RTX_CLASS (GET_CODE (lhs)) == 'o' + || GET_RTX_CLASS (GET_CODE (rhs)) == 'o') + return x; + + lhs = expand_compound_operation (lhs); + rhs = expand_compound_operation (rhs); + inner_code = GET_CODE (lhs); + if (inner_code != GET_CODE (rhs)) + return x; + + /* See if the inner and outer operations distribute. */ + switch (inner_code) + { + case LSHIFTRT: + case ASHIFTRT: + case AND: + case IOR: + /* These all distribute except over PLUS. */ + if (code == PLUS || code == MINUS) + return x; + break; + + case MULT: + if (code != PLUS && code != MINUS) + return x; + break; + + case ASHIFT: + case LSHIFT: + /* These are also multiplies, so they distribute over everything. */ + break; + + case SUBREG: + /* Non-paradoxical SUBREGs distributes over all operations, provided + the inner modes and word numbers are the same, this is an extraction + of a low-order part, we don't convert an fp operation to int or + vice versa, and we would not be converting a single-word + operation into a multi-word operation. The latter test is not + required, but it prevents generating unneeded multi-word operations. + Some of the previous tests are redundant given the latter test, but + are retained because they are required for correctness. + + We produce the result slightly differently in this case. */ + + if (GET_MODE (SUBREG_REG (lhs)) != GET_MODE (SUBREG_REG (rhs)) + || SUBREG_WORD (lhs) != SUBREG_WORD (rhs) + || ! subreg_lowpart_p (lhs) + || (GET_MODE_CLASS (GET_MODE (lhs)) + != GET_MODE_CLASS (GET_MODE (SUBREG_REG (lhs)))) + || (GET_MODE_SIZE (GET_MODE (lhs)) + < GET_MODE_SIZE (GET_MODE (SUBREG_REG (lhs)))) + || GET_MODE_SIZE (GET_MODE (SUBREG_REG (lhs))) > UNITS_PER_WORD) + return x; + + tem = gen_binary (code, GET_MODE (SUBREG_REG (lhs)), + SUBREG_REG (lhs), SUBREG_REG (rhs)); + return gen_lowpart_for_combine (GET_MODE (x), tem); + + default: + return x; + } + + /* Set LHS and RHS to the inner operands (A and B in the example + above) and set OTHER to the common operand (C in the example). + These is only one way to do this unless the inner operation is + commutative. */ + if (GET_RTX_CLASS (inner_code) == 'c' + && rtx_equal_p (XEXP (lhs, 0), XEXP (rhs, 0))) + other = XEXP (lhs, 0), lhs = XEXP (lhs, 1), rhs = XEXP (rhs, 1); + else if (GET_RTX_CLASS (inner_code) == 'c' + && rtx_equal_p (XEXP (lhs, 0), XEXP (rhs, 1))) + other = XEXP (lhs, 0), lhs = XEXP (lhs, 1), rhs = XEXP (rhs, 0); + else if (GET_RTX_CLASS (inner_code) == 'c' + && rtx_equal_p (XEXP (lhs, 1), XEXP (rhs, 0))) + other = XEXP (lhs, 1), lhs = XEXP (lhs, 0), rhs = XEXP (rhs, 1); + else if (rtx_equal_p (XEXP (lhs, 1), XEXP (rhs, 1))) + other = XEXP (lhs, 1), lhs = XEXP (lhs, 0), rhs = XEXP (rhs, 0); + else + return x; + + /* Form the new inner operation, seeing if it simplifies first. */ + tem = gen_binary (code, GET_MODE (x), lhs, rhs); + + /* There is one exception to the general way of distributing: + (a ^ b) | (a ^ c) -> (~a) & (b ^ c) */ + if (code == XOR && inner_code == IOR) + { + inner_code = AND; + other = gen_unary (NOT, GET_MODE (x), other); + } + + /* We may be able to continuing distributing the result, so call + ourselves recursively on the inner operation before forming the + outer operation, which we return. */ + return gen_binary (inner_code, GET_MODE (x), + apply_distributive_law (tem), other); +} + +/* We have X, a logical `and' of VAROP with the constant CONSTOP, to be done + in MODE. + + Return an equivalent form, if different from X. Otherwise, return X. If + X is zero, we are to always construct the equivalent form. */ + +static rtx +simplify_and_const_int (x, mode, varop, constop) + rtx x; + enum machine_mode mode; + rtx varop; + unsigned HOST_WIDE_INT constop; +{ + register enum machine_mode tmode; + register rtx temp; + unsigned HOST_WIDE_INT nonzero; + + /* There is a large class of optimizations based on the principle that + some operations produce results where certain bits are known to be zero, + and hence are not significant to the AND. For example, if we have just + done a left shift of one bit, the low-order bit is known to be zero and + hence an AND with a mask of ~1 would not do anything. + + At the end of the following loop, we set: + + VAROP to be the item to be AND'ed with; + CONSTOP to the constant value to AND it with. */ + + while (1) + { + /* If we ever encounter a mode wider than the host machine's widest + integer size, we can't compute the masks accurately, so give up. */ + if (GET_MODE_BITSIZE (GET_MODE (varop)) > HOST_BITS_PER_WIDE_INT) + break; + + /* Unless one of the cases below does a `continue', + a `break' will be executed to exit the loop. */ + + switch (GET_CODE (varop)) + { + case CLOBBER: + /* If VAROP is a (clobber (const_int)), return it since we know + we are generating something that won't match. */ + return varop; + +#if ! BITS_BIG_ENDIAN + case USE: + /* VAROP is a (use (mem ..)) that was made from a bit-field + extraction that spanned the boundary of the MEM. If we are + now masking so it is within that boundary, we don't need the + USE any more. */ + if ((constop & ~ GET_MODE_MASK (GET_MODE (XEXP (varop, 0)))) == 0) + { + varop = XEXP (varop, 0); + continue; + } + break; +#endif + + case SUBREG: + if (subreg_lowpart_p (varop) + /* We can ignore the effect this SUBREG if it narrows the mode + or, on machines where byte operations extend, if the + constant masks to zero all the bits the mode doesn't have. */ + && ((GET_MODE_SIZE (GET_MODE (varop)) + < GET_MODE_SIZE (GET_MODE (SUBREG_REG (varop)))) +#ifdef BYTE_LOADS_EXTEND + || (0 == (constop + & GET_MODE_MASK (GET_MODE (varop)) + & ~ GET_MODE_MASK (GET_MODE (SUBREG_REG (varop))))) +#endif + )) + { + varop = SUBREG_REG (varop); + continue; + } + break; + + case ZERO_EXTRACT: + case SIGN_EXTRACT: + case ZERO_EXTEND: + case SIGN_EXTEND: + /* Try to expand these into a series of shifts and then work + with that result. If we can't, for example, if the extract + isn't at a fixed position, give up. */ + temp = expand_compound_operation (varop); + if (temp != varop) + { + varop = temp; + continue; + } + break; + + case AND: + if (GET_CODE (XEXP (varop, 1)) == CONST_INT) + { + constop &= INTVAL (XEXP (varop, 1)); + varop = XEXP (varop, 0); + continue; + } + break; + + case IOR: + case XOR: + /* If VAROP is (ior (lshiftrt FOO C1) C2), try to commute the IOR and + LSHIFT so we end up with an (and (lshiftrt (ior ...) ...) ...) + operation which may be a bitfield extraction. Ensure + that the constant we form is not wider than the mode of + VAROP. */ + + if (GET_CODE (XEXP (varop, 0)) == LSHIFTRT + && GET_CODE (XEXP (XEXP (varop, 0), 1)) == CONST_INT + && INTVAL (XEXP (XEXP (varop, 0), 1)) >= 0 + && INTVAL (XEXP (XEXP (varop, 0), 1)) < HOST_BITS_PER_WIDE_INT + && GET_CODE (XEXP (varop, 1)) == CONST_INT + && ((INTVAL (XEXP (XEXP (varop, 0), 1)) + + floor_log2 (INTVAL (XEXP (varop, 1)))) + < GET_MODE_BITSIZE (GET_MODE (varop))) + && (INTVAL (XEXP (varop, 1)) + & ~ nonzero_bits (XEXP (varop, 0), GET_MODE (varop)) == 0)) + { + temp = GEN_INT ((INTVAL (XEXP (varop, 1)) & constop) + << INTVAL (XEXP (XEXP (varop, 0), 1))); + temp = gen_binary (GET_CODE (varop), GET_MODE (varop), + XEXP (XEXP (varop, 0), 0), temp); + varop = gen_rtx_combine (LSHIFTRT, GET_MODE (varop), + temp, XEXP (varop, 1)); + continue; + } + + /* Apply the AND to both branches of the IOR or XOR, then try to + apply the distributive law. This may eliminate operations + if either branch can be simplified because of the AND. + It may also make some cases more complex, but those cases + probably won't match a pattern either with or without this. */ + return + gen_lowpart_for_combine + (mode, apply_distributive_law + (gen_rtx_combine + (GET_CODE (varop), GET_MODE (varop), + simplify_and_const_int (NULL_RTX, GET_MODE (varop), + XEXP (varop, 0), constop), + simplify_and_const_int (NULL_RTX, GET_MODE (varop), + XEXP (varop, 1), constop)))); + + case NOT: + /* (and (not FOO)) is (and (xor FOO CONST)), so if FOO is an + LSHIFTRT, we can do the same as above. Ensure that the constant + we form is not wider than the mode of VAROP. */ + + if (GET_CODE (XEXP (varop, 0)) == LSHIFTRT + && GET_CODE (XEXP (XEXP (varop, 0), 1)) == CONST_INT + && INTVAL (XEXP (XEXP (varop, 0), 1)) >= 0 + && (INTVAL (XEXP (XEXP (varop, 0), 1)) + floor_log2 (constop) + < GET_MODE_BITSIZE (GET_MODE (varop))) + && INTVAL (XEXP (XEXP (varop, 0), 1)) < HOST_BITS_PER_WIDE_INT) + { + temp = GEN_INT (constop << INTVAL (XEXP (XEXP (varop, 0), 1))); + temp = gen_binary (XOR, GET_MODE (varop), + XEXP (XEXP (varop, 0), 0), temp); + varop = gen_rtx_combine (LSHIFTRT, GET_MODE (varop), + temp, XEXP (XEXP (varop, 0), 1)); + continue; + } + break; + + case ASHIFTRT: + /* If we are just looking for the sign bit, we don't need this + shift at all, even if it has a variable count. */ + if (constop == ((HOST_WIDE_INT) 1 + << (GET_MODE_BITSIZE (GET_MODE (varop)) - 1))) + { + varop = XEXP (varop, 0); + continue; + } + + /* If this is a shift by a constant, get a mask that contains + those bits that are not copies of the sign bit. We then have + two cases: If CONSTOP only includes those bits, this can be + a logical shift, which may allow simplifications. If CONSTOP + is a single-bit field not within those bits, we are requesting + a copy of the sign bit and hence can shift the sign bit to + the appropriate location. */ + if (GET_CODE (XEXP (varop, 1)) == CONST_INT + && INTVAL (XEXP (varop, 1)) >= 0 + && INTVAL (XEXP (varop, 1)) < HOST_BITS_PER_WIDE_INT) + { + int i = -1; + + nonzero = GET_MODE_MASK (GET_MODE (varop)); + nonzero >>= INTVAL (XEXP (varop, 1)); + + if ((constop & ~ nonzero) == 0 + || (i = exact_log2 (constop)) >= 0) + { + varop = simplify_shift_const + (varop, LSHIFTRT, GET_MODE (varop), XEXP (varop, 0), + i < 0 ? INTVAL (XEXP (varop, 1)) + : GET_MODE_BITSIZE (GET_MODE (varop)) - 1 - i); + if (GET_CODE (varop) != ASHIFTRT) + continue; + } + } + + /* If our mask is 1, convert this to a LSHIFTRT. This can be done + even if the shift count isn't a constant. */ + if (constop == 1) + varop = gen_rtx_combine (LSHIFTRT, GET_MODE (varop), + XEXP (varop, 0), XEXP (varop, 1)); + break; + + case LSHIFTRT: + /* If we have (and (lshiftrt FOO C1) C2) where the combination of the + shift and AND produces only copies of the sign bit (C2 is one less + than a power of two), we can do this with just a shift. */ + + if (GET_CODE (XEXP (varop, 1)) == CONST_INT + && ((INTVAL (XEXP (varop, 1)) + + num_sign_bit_copies (XEXP (varop, 0), + GET_MODE (XEXP (varop, 0)))) + >= GET_MODE_BITSIZE (GET_MODE (varop))) + && exact_log2 (constop + 1) >= 0) + varop + = gen_rtx_combine (LSHIFTRT, GET_MODE (varop), XEXP (varop, 0), + GEN_INT (GET_MODE_BITSIZE (GET_MODE (varop)) + - exact_log2 (constop + 1))); + break; + + case NE: + /* (and (ne FOO 0) CONST) can be (and FOO CONST) if CONST is + included in STORE_FLAG_VALUE and FOO has no bits that might be + nonzero not in CONST. */ + if ((constop & ~ STORE_FLAG_VALUE) == 0 + && XEXP (varop, 0) == const0_rtx + && (nonzero_bits (XEXP (varop, 0), mode) & ~ constop) == 0) + { + varop = XEXP (varop, 0); + continue; + } + break; + + case PLUS: + /* In (and (plus FOO C1) M), if M is a mask that just turns off + low-order bits (as in an alignment operation) and FOO is already + aligned to that boundary, we can convert remove this AND + and possibly the PLUS if it is now adding zero. */ + if (GET_CODE (XEXP (varop, 1)) == CONST_INT + && exact_log2 (-constop) >= 0 + && (nonzero_bits (XEXP (varop, 0), mode) & ~ constop) == 0) + { + varop = plus_constant (XEXP (varop, 0), + INTVAL (XEXP (varop, 1)) & constop); + constop = ~0; + break; + } + + /* ... fall through ... */ + + case MINUS: + /* In (and (plus (and FOO M1) BAR) M2), if M1 and M2 are one + less than powers of two and M2 is narrower than M1, we can + eliminate the inner AND. This occurs when incrementing + bit fields. */ + + if (GET_CODE (XEXP (varop, 0)) == ZERO_EXTRACT + || GET_CODE (XEXP (varop, 0)) == ZERO_EXTEND) + SUBST (XEXP (varop, 0), + expand_compound_operation (XEXP (varop, 0))); + + if (GET_CODE (XEXP (varop, 0)) == AND + && GET_CODE (XEXP (XEXP (varop, 0), 1)) == CONST_INT + && exact_log2 (constop + 1) >= 0 + && exact_log2 (INTVAL (XEXP (XEXP (varop, 0), 1)) + 1) >= 0 + && (~ INTVAL (XEXP (XEXP (varop, 0), 1)) & constop) == 0) + SUBST (XEXP (varop, 0), XEXP (XEXP (varop, 0), 0)); + break; + } + + break; + } + + /* If we have reached a constant, this whole thing is constant. */ + if (GET_CODE (varop) == CONST_INT) + return GEN_INT (constop & INTVAL (varop)); + + /* See what bits may be nonzero in VAROP. Unlike the general case of + a call to nonzero_bits, here we don't care about bits outside + MODE. */ + + nonzero = nonzero_bits (varop, mode) & GET_MODE_MASK (mode); + + /* Turn off all bits in the constant that are known to already be zero. + Thus, if the AND isn't needed at all, we will have CONSTOP == NONZERO_BITS + which is tested below. */ + + constop &= nonzero; + + /* If we don't have any bits left, return zero. */ + if (constop == 0) + return const0_rtx; + + /* Get VAROP in MODE. Try to get a SUBREG if not. Don't make a new SUBREG + if we already had one (just check for the simplest cases). */ + if (x && GET_CODE (XEXP (x, 0)) == SUBREG + && GET_MODE (XEXP (x, 0)) == mode + && SUBREG_REG (XEXP (x, 0)) == varop) + varop = XEXP (x, 0); + else + varop = gen_lowpart_for_combine (mode, varop); + + /* If we can't make the SUBREG, try to return what we were given. */ + if (GET_CODE (varop) == CLOBBER) + return x ? x : varop; + + /* If we are only masking insignificant bits, return VAROP. */ + if (constop == nonzero) + x = varop; + + /* Otherwise, return an AND. See how much, if any, of X we can use. */ + else if (x == 0 || GET_CODE (x) != AND || GET_MODE (x) != mode) + x = gen_rtx_combine (AND, mode, varop, GEN_INT (constop)); + + else + { + if (GET_CODE (XEXP (x, 1)) != CONST_INT + || INTVAL (XEXP (x, 1)) != constop) + SUBST (XEXP (x, 1), GEN_INT (constop)); + + SUBST (XEXP (x, 0), varop); + } + + return x; +} + +/* Given an expression, X, compute which bits in X can be non-zero. + We don't care about bits outside of those defined in MODE. + + For most X this is simply GET_MODE_MASK (GET_MODE (MODE)), but if X is + a shift, AND, or zero_extract, we can do better. */ + +static unsigned HOST_WIDE_INT +nonzero_bits (x, mode) + rtx x; + enum machine_mode mode; +{ + unsigned HOST_WIDE_INT nonzero = GET_MODE_MASK (mode); + unsigned HOST_WIDE_INT inner_nz; + enum rtx_code code; + int mode_width = GET_MODE_BITSIZE (mode); + rtx tem; + + /* If X is wider than MODE, use its mode instead. */ + if (GET_MODE_BITSIZE (GET_MODE (x)) > mode_width) + { + mode = GET_MODE (x); + nonzero = GET_MODE_MASK (mode); + mode_width = GET_MODE_BITSIZE (mode); + } + + if (mode_width > HOST_BITS_PER_WIDE_INT) + /* Our only callers in this case look for single bit values. So + just return the mode mask. Those tests will then be false. */ + return nonzero; + + code = GET_CODE (x); + switch (code) + { + case REG: +#ifdef STACK_BOUNDARY + /* If this is the stack pointer, we may know something about its + alignment. If PUSH_ROUNDING is defined, it is possible for the + stack to be momentarily aligned only to that amount, so we pick + the least alignment. */ + + if (x == stack_pointer_rtx) + { + int sp_alignment = STACK_BOUNDARY / BITS_PER_UNIT; + +#ifdef PUSH_ROUNDING + sp_alignment = MIN (PUSH_ROUNDING (1), sp_alignment); +#endif + + return nonzero & ~ (sp_alignment - 1); + } +#endif + + /* If X is a register whose nonzero bits value is current, use it. + Otherwise, if X is a register whose value we can find, use that + value. Otherwise, use the previously-computed global nonzero bits + for this register. */ + + if (reg_last_set_value[REGNO (x)] != 0 + && reg_last_set_mode[REGNO (x)] == mode + && (reg_n_sets[REGNO (x)] == 1 + || reg_last_set_label[REGNO (x)] == label_tick) + && INSN_CUID (reg_last_set[REGNO (x)]) < subst_low_cuid) + return reg_last_set_nonzero_bits[REGNO (x)]; + + tem = get_last_value (x); + + if (tem) + { +#ifdef SHORT_IMMEDIATES_SIGN_EXTEND + /* If X is narrower than MODE and TEM is a non-negative + constant that would appear negative in the mode of X, + sign-extend it for use in reg_nonzero_bits because some + machines (maybe most) will actually do the sign-extension + and this is the conservative approach. + + ??? For 2.5, try to tighten up the MD files in this regard + instead of this kludge. */ + + if (GET_MODE_BITSIZE (GET_MODE (x)) < mode_width + && GET_CODE (tem) == CONST_INT + && INTVAL (tem) > 0 + && 0 != (INTVAL (tem) + & ((HOST_WIDE_INT) 1 + << GET_MODE_BITSIZE (GET_MODE (x))))) + tem = GEN_INT (INTVAL (tem) + | ((HOST_WIDE_INT) (-1) + << GET_MODE_BITSIZE (GET_MODE (x)))); +#endif + return nonzero_bits (tem, mode); + } + else if (nonzero_sign_valid && reg_nonzero_bits[REGNO (x)]) + return reg_nonzero_bits[REGNO (x)] & nonzero; + else + return nonzero; + + case CONST_INT: +#ifdef SHORT_IMMEDIATES_SIGN_EXTEND + /* If X is negative in MODE, sign-extend the value. */ + if (INTVAL (x) > 0 + && 0 != (INTVAL (x) + & ((HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (GET_MODE (x))))) + return (INTVAL (x) + | ((HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (GET_MODE (x)))); +#endif + + return INTVAL (x); + +#ifdef BYTE_LOADS_ZERO_EXTEND + case MEM: + /* In many, if not most, RISC machines, reading a byte from memory + zeros the rest of the register. Noticing that fact saves a lot + of extra zero-extends. */ + nonzero &= GET_MODE_MASK (GET_MODE (x)); + break; +#endif + +#if STORE_FLAG_VALUE == 1 + case EQ: case NE: + case GT: case GTU: + case LT: case LTU: + case GE: case GEU: + case LE: case LEU: + + if (GET_MODE_CLASS (mode) == MODE_INT) + nonzero = 1; + + /* A comparison operation only sets the bits given by its mode. The + rest are set undefined. */ + if (GET_MODE_SIZE (GET_MODE (x)) < mode_width) + nonzero |= (GET_MODE_MASK (mode) & ~ GET_MODE_MASK (GET_MODE (x))); + break; +#endif + + case NEG: + if (num_sign_bit_copies (XEXP (x, 0), GET_MODE (x)) + == GET_MODE_BITSIZE (GET_MODE (x))) + nonzero = 1; + + if (GET_MODE_SIZE (GET_MODE (x)) < mode_width) + nonzero |= (GET_MODE_MASK (mode) & ~ GET_MODE_MASK (GET_MODE (x))); + break; + + case ABS: + if (num_sign_bit_copies (XEXP (x, 0), GET_MODE (x)) + == GET_MODE_BITSIZE (GET_MODE (x))) + nonzero = 1; + break; + + case TRUNCATE: + nonzero &= (nonzero_bits (XEXP (x, 0), mode) & GET_MODE_MASK (mode)); + break; + + case ZERO_EXTEND: + nonzero &= nonzero_bits (XEXP (x, 0), mode); + if (GET_MODE (XEXP (x, 0)) != VOIDmode) + nonzero &= GET_MODE_MASK (GET_MODE (XEXP (x, 0))); + break; + + case SIGN_EXTEND: + /* If the sign bit is known clear, this is the same as ZERO_EXTEND. + Otherwise, show all the bits in the outer mode but not the inner + may be non-zero. */ + inner_nz = nonzero_bits (XEXP (x, 0), mode); + if (GET_MODE (XEXP (x, 0)) != VOIDmode) + { + inner_nz &= GET_MODE_MASK (GET_MODE (XEXP (x, 0))); + if (inner_nz & + (((HOST_WIDE_INT) 1 + << (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - 1)))) + inner_nz |= (GET_MODE_MASK (mode) + & ~ GET_MODE_MASK (GET_MODE (XEXP (x, 0)))); + } + + nonzero &= inner_nz; + break; + + case AND: + nonzero &= (nonzero_bits (XEXP (x, 0), mode) + & nonzero_bits (XEXP (x, 1), mode)); + break; + + case XOR: case IOR: + case UMIN: case UMAX: case SMIN: case SMAX: + nonzero &= (nonzero_bits (XEXP (x, 0), mode) + | nonzero_bits (XEXP (x, 1), mode)); + break; + + case PLUS: case MINUS: + case MULT: + case DIV: case UDIV: + case MOD: case UMOD: + /* We can apply the rules of arithmetic to compute the number of + high- and low-order zero bits of these operations. We start by + computing the width (position of the highest-order non-zero bit) + and the number of low-order zero bits for each value. */ + { + unsigned HOST_WIDE_INT nz0 = nonzero_bits (XEXP (x, 0), mode); + unsigned HOST_WIDE_INT nz1 = nonzero_bits (XEXP (x, 1), mode); + int width0 = floor_log2 (nz0) + 1; + int width1 = floor_log2 (nz1) + 1; + int low0 = floor_log2 (nz0 & -nz0); + int low1 = floor_log2 (nz1 & -nz1); + int op0_maybe_minusp = (nz0 & ((HOST_WIDE_INT) 1 << (mode_width - 1))); + int op1_maybe_minusp = (nz1 & ((HOST_WIDE_INT) 1 << (mode_width - 1))); + int result_width = mode_width; + int result_low = 0; + + switch (code) + { + case PLUS: + result_width = MAX (width0, width1) + 1; + result_low = MIN (low0, low1); + break; + case MINUS: + result_low = MIN (low0, low1); + break; + case MULT: + result_width = width0 + width1; + result_low = low0 + low1; + break; + case DIV: + if (! op0_maybe_minusp && ! op1_maybe_minusp) + result_width = width0; + break; + case UDIV: + result_width = width0; + break; + case MOD: + if (! op0_maybe_minusp && ! op1_maybe_minusp) + result_width = MIN (width0, width1); + result_low = MIN (low0, low1); + break; + case UMOD: + result_width = MIN (width0, width1); + result_low = MIN (low0, low1); + break; + } + + if (result_width < mode_width) + nonzero &= ((HOST_WIDE_INT) 1 << result_width) - 1; + + if (result_low > 0) + nonzero &= ~ (((HOST_WIDE_INT) 1 << result_low) - 1); + } + break; + + case ZERO_EXTRACT: + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT) + nonzero &= ((HOST_WIDE_INT) 1 << INTVAL (XEXP (x, 1))) - 1; + break; + + case SUBREG: + /* If this is a SUBREG formed for a promoted variable that has + been zero-extended, we know that at least the high-order bits + are zero, though others might be too. */ + + if (SUBREG_PROMOTED_VAR_P (x) && SUBREG_PROMOTED_UNSIGNED_P (x)) + nonzero = (GET_MODE_MASK (GET_MODE (x)) + & nonzero_bits (SUBREG_REG (x), GET_MODE (x))); + + /* If the inner mode is a single word for both the host and target + machines, we can compute this from which bits of the inner + object might be nonzero. */ + if (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) <= BITS_PER_WORD + && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) + <= HOST_BITS_PER_WIDE_INT)) + { + nonzero &= nonzero_bits (SUBREG_REG (x), mode); +#ifndef BYTE_LOADS_EXTEND + /* On many CISC machines, accessing an object in a wider mode + causes the high-order bits to become undefined. So they are + not known to be zero. */ + if (GET_MODE_SIZE (GET_MODE (x)) + > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) + nonzero |= (GET_MODE_MASK (GET_MODE (x)) + & ~ GET_MODE_MASK (GET_MODE (SUBREG_REG (x)))); +#endif + } + break; + + case ASHIFTRT: + case LSHIFTRT: + case ASHIFT: + case LSHIFT: + case ROTATE: + /* The nonzero bits are in two classes: any bits within MODE + that aren't in GET_MODE (x) are always significant. The rest of the + nonzero bits are those that are significant in the operand of + the shift when shifted the appropriate number of bits. This + shows that high-order bits are cleared by the right shift and + low-order bits by left shifts. */ + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) >= 0 + && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT) + { + enum machine_mode inner_mode = GET_MODE (x); + int width = GET_MODE_BITSIZE (inner_mode); + int count = INTVAL (XEXP (x, 1)); + unsigned HOST_WIDE_INT mode_mask = GET_MODE_MASK (inner_mode); + unsigned HOST_WIDE_INT op_nonzero = nonzero_bits (XEXP (x, 0), mode); + unsigned HOST_WIDE_INT inner = op_nonzero & mode_mask; + unsigned HOST_WIDE_INT outer = 0; + + if (mode_width > width) + outer = (op_nonzero & nonzero & ~ mode_mask); + + if (code == LSHIFTRT) + inner >>= count; + else if (code == ASHIFTRT) + { + inner >>= count; + + /* If the sign bit may have been nonzero before the shift, we + need to mark all the places it could have been copied to + by the shift as possibly nonzero. */ + if (inner & ((HOST_WIDE_INT) 1 << (width - 1 - count))) + inner |= (((HOST_WIDE_INT) 1 << count) - 1) << (width - count); + } + else if (code == LSHIFT || code == ASHIFT) + inner <<= count; + else + inner = ((inner << (count % width) + | (inner >> (width - (count % width)))) & mode_mask); + + nonzero &= (outer | inner); + } + break; + + case FFS: + /* This is at most the number of bits in the mode. */ + nonzero = ((HOST_WIDE_INT) 1 << (floor_log2 (mode_width) + 1)) - 1; + break; + + case IF_THEN_ELSE: + nonzero &= (nonzero_bits (XEXP (x, 1), mode) + | nonzero_bits (XEXP (x, 2), mode)); + break; + } + + return nonzero; +} + +/* Return the number of bits at the high-order end of X that are known to + be equal to the sign bit. This number will always be between 1 and + the number of bits in the mode of X. MODE is the mode to be used + if X is VOIDmode. */ + +static int +num_sign_bit_copies (x, mode) + rtx x; + enum machine_mode mode; +{ + enum rtx_code code = GET_CODE (x); + int bitwidth; + int num0, num1, result; + unsigned HOST_WIDE_INT nonzero; + rtx tem; + + /* If we weren't given a mode, use the mode of X. If the mode is still + VOIDmode, we don't know anything. */ + + if (mode == VOIDmode) + mode = GET_MODE (x); + + if (mode == VOIDmode) + return 1; + + bitwidth = GET_MODE_BITSIZE (mode); + + switch (code) + { + case REG: + + if (reg_last_set_value[REGNO (x)] != 0 + && reg_last_set_mode[REGNO (x)] == mode + && (reg_n_sets[REGNO (x)] == 1 + || reg_last_set_label[REGNO (x)] == label_tick) + && INSN_CUID (reg_last_set[REGNO (x)]) < subst_low_cuid) + return reg_last_set_sign_bit_copies[REGNO (x)]; + + tem = get_last_value (x); + if (tem != 0) + return num_sign_bit_copies (tem, mode); + + if (nonzero_sign_valid && reg_sign_bit_copies[REGNO (x)] != 0) + return reg_sign_bit_copies[REGNO (x)]; + break; + +#ifdef BYTE_LOADS_SIGN_EXTEND + case MEM: + /* Some RISC machines sign-extend all loads of smaller than a word. */ + return MAX (1, bitwidth - GET_MODE_BITSIZE (GET_MODE (x)) + 1); +#endif + + case CONST_INT: + /* If the constant is negative, take its 1's complement and remask. + Then see how many zero bits we have. */ + nonzero = INTVAL (x) & GET_MODE_MASK (mode); + if (bitwidth <= HOST_BITS_PER_WIDE_INT + && (nonzero & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0) + nonzero = (~ nonzero) & GET_MODE_MASK (mode); + + return (nonzero == 0 ? bitwidth : bitwidth - floor_log2 (nonzero) - 1); + + case SUBREG: + /* If this is a SUBREG for a promoted object that is sign-extended + and we are looking at it in a wider mode, we know that at least the + high-order bits are known to be sign bit copies. */ + + if (SUBREG_PROMOTED_VAR_P (x) && ! SUBREG_PROMOTED_UNSIGNED_P (x)) + return MAX (bitwidth - GET_MODE_BITSIZE (GET_MODE (x)) + 1, + num_sign_bit_copies (SUBREG_REG (x), mode)); + + /* For a smaller object, just ignore the high bits. */ + if (bitwidth <= GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x)))) + { + num0 = num_sign_bit_copies (SUBREG_REG (x), VOIDmode); + return MAX (1, (num0 + - (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) + - bitwidth))); + } + +#ifdef BYTE_LOADS_EXTEND + /* For paradoxical SUBREGs, just look inside since, on machines with + one of these defined, we assume that operations are actually + performed on the full register. Note that we are passing MODE + to the recursive call, so the number of sign bit copies will + remain relative to that mode, not the inner mode. */ + + if (GET_MODE_SIZE (GET_MODE (x)) + > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) + return num_sign_bit_copies (SUBREG_REG (x), mode); +#endif + + break; + + case SIGN_EXTRACT: + if (GET_CODE (XEXP (x, 1)) == CONST_INT) + return MAX (1, bitwidth - INTVAL (XEXP (x, 1))); + break; + + case SIGN_EXTEND: + return (bitwidth - GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) + + num_sign_bit_copies (XEXP (x, 0), VOIDmode)); + + case TRUNCATE: + /* For a smaller object, just ignore the high bits. */ + num0 = num_sign_bit_copies (XEXP (x, 0), VOIDmode); + return MAX (1, (num0 - (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) + - bitwidth))); + + case NOT: + return num_sign_bit_copies (XEXP (x, 0), mode); + + case ROTATE: case ROTATERT: + /* If we are rotating left by a number of bits less than the number + of sign bit copies, we can just subtract that amount from the + number. */ + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) >= 0 && INTVAL (XEXP (x, 1)) < bitwidth) + { + num0 = num_sign_bit_copies (XEXP (x, 0), mode); + return MAX (1, num0 - (code == ROTATE ? INTVAL (XEXP (x, 1)) + : bitwidth - INTVAL (XEXP (x, 1)))); + } + break; + + case NEG: + /* In general, this subtracts one sign bit copy. But if the value + is known to be positive, the number of sign bit copies is the + same as that of the input. Finally, if the input has just one bit + that might be nonzero, all the bits are copies of the sign bit. */ + nonzero = nonzero_bits (XEXP (x, 0), mode); + if (nonzero == 1) + return bitwidth; + + num0 = num_sign_bit_copies (XEXP (x, 0), mode); + if (num0 > 1 + && bitwidth <= HOST_BITS_PER_WIDE_INT + && (((HOST_WIDE_INT) 1 << (bitwidth - 1)) & nonzero)) + num0--; + + return num0; + + case IOR: case AND: case XOR: + case SMIN: case SMAX: case UMIN: case UMAX: + /* Logical operations will preserve the number of sign-bit copies. + MIN and MAX operations always return one of the operands. */ + num0 = num_sign_bit_copies (XEXP (x, 0), mode); + num1 = num_sign_bit_copies (XEXP (x, 1), mode); + return MIN (num0, num1); + + case PLUS: case MINUS: + /* For addition and subtraction, we can have a 1-bit carry. However, + if we are subtracting 1 from a positive number, there will not + be such a carry. Furthermore, if the positive number is known to + be 0 or 1, we know the result is either -1 or 0. */ + + if (code == PLUS && XEXP (x, 1) == constm1_rtx + && bitwidth <= HOST_BITS_PER_WIDE_INT) + { + nonzero = nonzero_bits (XEXP (x, 0), mode); + if ((((HOST_WIDE_INT) 1 << (bitwidth - 1)) & nonzero) == 0) + return (nonzero == 1 || nonzero == 0 ? bitwidth + : bitwidth - floor_log2 (nonzero) - 1); + } + + num0 = num_sign_bit_copies (XEXP (x, 0), mode); + num1 = num_sign_bit_copies (XEXP (x, 1), mode); + return MAX (1, MIN (num0, num1) - 1); + + case MULT: + /* The number of bits of the product is the sum of the number of + bits of both terms. However, unless one of the terms if known + to be positive, we must allow for an additional bit since negating + a negative number can remove one sign bit copy. */ + + num0 = num_sign_bit_copies (XEXP (x, 0), mode); + num1 = num_sign_bit_copies (XEXP (x, 1), mode); + + result = bitwidth - (bitwidth - num0) - (bitwidth - num1); + if (result > 0 + && bitwidth <= HOST_BITS_PER_WIDE_INT + && ((nonzero_bits (XEXP (x, 0), mode) + & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0) + && (nonzero_bits (XEXP (x, 1), mode) + & ((HOST_WIDE_INT) 1 << (bitwidth - 1)) != 0)) + result--; + + return MAX (1, result); + + case UDIV: + /* The result must be <= the first operand. */ + return num_sign_bit_copies (XEXP (x, 0), mode); + + case UMOD: + /* The result must be <= the scond operand. */ + return num_sign_bit_copies (XEXP (x, 1), mode); + + case DIV: + /* Similar to unsigned division, except that we have to worry about + the case where the divisor is negative, in which case we have + to add 1. */ + result = num_sign_bit_copies (XEXP (x, 0), mode); + if (result > 1 + && bitwidth <= HOST_BITS_PER_WIDE_INT + && (nonzero_bits (XEXP (x, 1), mode) + & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0) + result --; + + return result; + + case MOD: + result = num_sign_bit_copies (XEXP (x, 1), mode); + if (result > 1 + && bitwidth <= HOST_BITS_PER_WIDE_INT + && (nonzero_bits (XEXP (x, 1), mode) + & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0) + result --; + + return result; + + case ASHIFTRT: + /* Shifts by a constant add to the number of bits equal to the + sign bit. */ + num0 = num_sign_bit_copies (XEXP (x, 0), mode); + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) > 0) + num0 = MIN (bitwidth, num0 + INTVAL (XEXP (x, 1))); + + return num0; + + case ASHIFT: + case LSHIFT: + /* Left shifts destroy copies. */ + if (GET_CODE (XEXP (x, 1)) != CONST_INT + || INTVAL (XEXP (x, 1)) < 0 + || INTVAL (XEXP (x, 1)) >= bitwidth) + return 1; + + num0 = num_sign_bit_copies (XEXP (x, 0), mode); + return MAX (1, num0 - INTVAL (XEXP (x, 1))); + + case IF_THEN_ELSE: + num0 = num_sign_bit_copies (XEXP (x, 1), mode); + num1 = num_sign_bit_copies (XEXP (x, 2), mode); + return MIN (num0, num1); + +#if STORE_FLAG_VALUE == -1 + case EQ: case NE: case GE: case GT: case LE: case LT: + case GEU: case GTU: case LEU: case LTU: + return bitwidth; +#endif + } + + /* If we haven't been able to figure it out by one of the above rules, + see if some of the high-order bits are known to be zero. If so, + count those bits and return one less than that amount. If we can't + safely compute the mask for this mode, always return BITWIDTH. */ + + if (bitwidth > HOST_BITS_PER_WIDE_INT) + return 1; + + nonzero = nonzero_bits (x, mode); + return (nonzero & ((HOST_WIDE_INT) 1 << (bitwidth - 1)) + ? 1 : bitwidth - floor_log2 (nonzero) - 1); +} + +/* Return the number of "extended" bits there are in X, when interpreted + as a quantity in MODE whose signedness is indicated by UNSIGNEDP. For + unsigned quantities, this is the number of high-order zero bits. + For signed quantities, this is the number of copies of the sign bit + minus 1. In both case, this function returns the number of "spare" + bits. For example, if two quantities for which this function returns + at least 1 are added, the addition is known not to overflow. + + This function will always return 0 unless called during combine, which + implies that it must be called from a define_split. */ + +int +extended_count (x, mode, unsignedp) + rtx x; + enum machine_mode mode; + int unsignedp; +{ + if (nonzero_sign_valid == 0) + return 0; + + return (unsignedp + ? (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT + && (GET_MODE_BITSIZE (mode) - 1 + - floor_log2 (nonzero_bits (x, mode)))) + : num_sign_bit_copies (x, mode) - 1); +} + +/* This function is called from `simplify_shift_const' to merge two + outer operations. Specifically, we have already found that we need + to perform operation *POP0 with constant *PCONST0 at the outermost + position. We would now like to also perform OP1 with constant CONST1 + (with *POP0 being done last). + + Return 1 if we can do the operation and update *POP0 and *PCONST0 with + the resulting operation. *PCOMP_P is set to 1 if we would need to + complement the innermost operand, otherwise it is unchanged. + + MODE is the mode in which the operation will be done. No bits outside + the width of this mode matter. It is assumed that the width of this mode + is smaller than or equal to HOST_BITS_PER_WIDE_INT. + + If *POP0 or OP1 are NIL, it means no operation is required. Only NEG, PLUS, + IOR, XOR, and AND are supported. We may set *POP0 to SET if the proper + result is simply *PCONST0. + + If the resulting operation cannot be expressed as one operation, we + return 0 and do not change *POP0, *PCONST0, and *PCOMP_P. */ + +static int +merge_outer_ops (pop0, pconst0, op1, const1, mode, pcomp_p) + enum rtx_code *pop0; + HOST_WIDE_INT *pconst0; + enum rtx_code op1; + HOST_WIDE_INT const1; + enum machine_mode mode; + int *pcomp_p; +{ + enum rtx_code op0 = *pop0; + HOST_WIDE_INT const0 = *pconst0; + + const0 &= GET_MODE_MASK (mode); + const1 &= GET_MODE_MASK (mode); + + /* If OP0 is an AND, clear unimportant bits in CONST1. */ + if (op0 == AND) + const1 &= const0; + + /* If OP0 or OP1 is NIL, this is easy. Similarly if they are the same or + if OP0 is SET. */ + + if (op1 == NIL || op0 == SET) + return 1; + + else if (op0 == NIL) + op0 = op1, const0 = const1; + + else if (op0 == op1) + { + switch (op0) + { + case AND: + const0 &= const1; + break; + case IOR: + const0 |= const1; + break; + case XOR: + const0 ^= const1; + break; + case PLUS: + const0 += const1; + break; + case NEG: + op0 = NIL; + break; + } + } + + /* Otherwise, if either is a PLUS or NEG, we can't do anything. */ + else if (op0 == PLUS || op1 == PLUS || op0 == NEG || op1 == NEG) + return 0; + + /* If the two constants aren't the same, we can't do anything. The + remaining six cases can all be done. */ + else if (const0 != const1) + return 0; + + else + switch (op0) + { + case IOR: + if (op1 == AND) + /* (a & b) | b == b */ + op0 = SET; + else /* op1 == XOR */ + /* (a ^ b) | b == a | b */ + ; + break; + + case XOR: + if (op1 == AND) + /* (a & b) ^ b == (~a) & b */ + op0 = AND, *pcomp_p = 1; + else /* op1 == IOR */ + /* (a | b) ^ b == a & ~b */ + op0 = AND, *pconst0 = ~ const0; + break; + + case AND: + if (op1 == IOR) + /* (a | b) & b == b */ + op0 = SET; + else /* op1 == XOR */ + /* (a ^ b) & b) == (~a) & b */ + *pcomp_p = 1; + break; + } + + /* Check for NO-OP cases. */ + const0 &= GET_MODE_MASK (mode); + if (const0 == 0 + && (op0 == IOR || op0 == XOR || op0 == PLUS)) + op0 = NIL; + else if (const0 == 0 && op0 == AND) + op0 = SET; + else if (const0 == GET_MODE_MASK (mode) && op0 == AND) + op0 = NIL; + + *pop0 = op0; + *pconst0 = const0; + + return 1; +} + +/* Simplify a shift of VAROP by COUNT bits. CODE says what kind of shift. + The result of the shift is RESULT_MODE. X, if non-zero, is an expression + that we started with. + + The shift is normally computed in the widest mode we find in VAROP, as + long as it isn't a different number of words than RESULT_MODE. Exceptions + are ASHIFTRT and ROTATE, which are always done in their original mode, */ + +static rtx +simplify_shift_const (x, code, result_mode, varop, count) + rtx x; + enum rtx_code code; + enum machine_mode result_mode; + rtx varop; + int count; +{ + enum rtx_code orig_code = code; + int orig_count = count; + enum machine_mode mode = result_mode; + enum machine_mode shift_mode, tmode; + int mode_words + = (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD; + /* We form (outer_op (code varop count) (outer_const)). */ + enum rtx_code outer_op = NIL; + HOST_WIDE_INT outer_const; + rtx const_rtx; + int complement_p = 0; + rtx new; + + /* If we were given an invalid count, don't do anything except exactly + what was requested. */ + + if (count < 0 || count > GET_MODE_BITSIZE (mode)) + { + if (x) + return x; + + return gen_rtx (code, mode, varop, GEN_INT (count)); + } + + /* Unless one of the branches of the `if' in this loop does a `continue', + we will `break' the loop after the `if'. */ + + while (count != 0) + { + /* If we have an operand of (clobber (const_int 0)), just return that + value. */ + if (GET_CODE (varop) == CLOBBER) + return varop; + + /* If we discovered we had to complement VAROP, leave. Making a NOT + here would cause an infinite loop. */ + if (complement_p) + break; + + /* Convert ROTATETRT to ROTATE. */ + if (code == ROTATERT) + code = ROTATE, count = GET_MODE_BITSIZE (result_mode) - count; + + /* Canonicalize LSHIFT to ASHIFT. */ + if (code == LSHIFT) + code = ASHIFT; + + /* We need to determine what mode we will do the shift in. If the + shift is a ASHIFTRT or ROTATE, we must always do it in the mode it + was originally done in. Otherwise, we can do it in MODE, the widest + mode encountered. */ + shift_mode = (code == ASHIFTRT || code == ROTATE ? result_mode : mode); + + /* Handle cases where the count is greater than the size of the mode + minus 1. For ASHIFT, use the size minus one as the count (this can + occur when simplifying (lshiftrt (ashiftrt ..))). For rotates, + take the count modulo the size. For other shifts, the result is + zero. + + Since these shifts are being produced by the compiler by combining + multiple operations, each of which are defined, we know what the + result is supposed to be. */ + + if (count > GET_MODE_BITSIZE (shift_mode) - 1) + { + if (code == ASHIFTRT) + count = GET_MODE_BITSIZE (shift_mode) - 1; + else if (code == ROTATE || code == ROTATERT) + count %= GET_MODE_BITSIZE (shift_mode); + else + { + /* We can't simply return zero because there may be an + outer op. */ + varop = const0_rtx; + count = 0; + break; + } + } + + /* Negative counts are invalid and should not have been made (a + programmer-specified negative count should have been handled + above). */ + else if (count < 0) + abort (); + + /* An arithmetic right shift of a quantity known to be -1 or 0 + is a no-op. */ + if (code == ASHIFTRT + && (num_sign_bit_copies (varop, shift_mode) + == GET_MODE_BITSIZE (shift_mode))) + { + count = 0; + break; + } + + /* If we are doing an arithmetic right shift and discarding all but + the sign bit copies, this is equivalent to doing a shift by the + bitsize minus one. Convert it into that shift because it will often + allow other simplifications. */ + + if (code == ASHIFTRT + && (count + num_sign_bit_copies (varop, shift_mode) + >= GET_MODE_BITSIZE (shift_mode))) + count = GET_MODE_BITSIZE (shift_mode) - 1; + + /* We simplify the tests below and elsewhere by converting + ASHIFTRT to LSHIFTRT if we know the sign bit is clear. + `make_compound_operation' will convert it to a ASHIFTRT for + those machines (such as Vax) that don't have a LSHIFTRT. */ + if (GET_MODE_BITSIZE (shift_mode) <= HOST_BITS_PER_WIDE_INT + && code == ASHIFTRT + && ((nonzero_bits (varop, shift_mode) + & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (shift_mode) - 1))) + == 0)) + code = LSHIFTRT; + + switch (GET_CODE (varop)) + { + case SIGN_EXTEND: + case ZERO_EXTEND: + case SIGN_EXTRACT: + case ZERO_EXTRACT: + new = expand_compound_operation (varop); + if (new != varop) + { + varop = new; + continue; + } + break; + + case MEM: + /* If we have (xshiftrt (mem ...) C) and C is MODE_WIDTH + minus the width of a smaller mode, we can do this with a + SIGN_EXTEND or ZERO_EXTEND from the narrower memory location. */ + if ((code == ASHIFTRT || code == LSHIFTRT) + && ! mode_dependent_address_p (XEXP (varop, 0)) + && ! MEM_VOLATILE_P (varop) + && (tmode = mode_for_size (GET_MODE_BITSIZE (mode) - count, + MODE_INT, 1)) != BLKmode) + { +#if BYTES_BIG_ENDIAN + new = gen_rtx (MEM, tmode, XEXP (varop, 0)); +#else + new = gen_rtx (MEM, tmode, + plus_constant (XEXP (varop, 0), + count / BITS_PER_UNIT)); + RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (varop); + MEM_VOLATILE_P (new) = MEM_VOLATILE_P (varop); + MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (varop); +#endif + varop = gen_rtx_combine (code == ASHIFTRT ? SIGN_EXTEND + : ZERO_EXTEND, mode, new); + count = 0; + continue; + } + break; + + case USE: + /* Similar to the case above, except that we can only do this if + the resulting mode is the same as that of the underlying + MEM and adjust the address depending on the *bits* endianness + because of the way that bit-field extract insns are defined. */ + if ((code == ASHIFTRT || code == LSHIFTRT) + && (tmode = mode_for_size (GET_MODE_BITSIZE (mode) - count, + MODE_INT, 1)) != BLKmode + && tmode == GET_MODE (XEXP (varop, 0))) + { +#if BITS_BIG_ENDIAN + new = XEXP (varop, 0); +#else + new = copy_rtx (XEXP (varop, 0)); + SUBST (XEXP (new, 0), + plus_constant (XEXP (new, 0), + count / BITS_PER_UNIT)); +#endif + + varop = gen_rtx_combine (code == ASHIFTRT ? SIGN_EXTEND + : ZERO_EXTEND, mode, new); + count = 0; + continue; + } + break; + + case SUBREG: + /* If VAROP is a SUBREG, strip it as long as the inner operand has + the same number of words as what we've seen so far. Then store + the widest mode in MODE. */ + if (subreg_lowpart_p (varop) + && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (varop))) + > GET_MODE_SIZE (GET_MODE (varop))) + && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (varop))) + + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD) + == mode_words)) + { + varop = SUBREG_REG (varop); + if (GET_MODE_SIZE (GET_MODE (varop)) > GET_MODE_SIZE (mode)) + mode = GET_MODE (varop); + continue; + } + break; + + case MULT: + /* Some machines use MULT instead of ASHIFT because MULT + is cheaper. But it is still better on those machines to + merge two shifts into one. */ + if (GET_CODE (XEXP (varop, 1)) == CONST_INT + && exact_log2 (INTVAL (XEXP (varop, 1))) >= 0) + { + varop = gen_binary (ASHIFT, GET_MODE (varop), XEXP (varop, 0), + GEN_INT (exact_log2 (INTVAL (XEXP (varop, 1)))));; + continue; + } + break; + + case UDIV: + /* Similar, for when divides are cheaper. */ + if (GET_CODE (XEXP (varop, 1)) == CONST_INT + && exact_log2 (INTVAL (XEXP (varop, 1))) >= 0) + { + varop = gen_binary (LSHIFTRT, GET_MODE (varop), XEXP (varop, 0), + GEN_INT (exact_log2 (INTVAL (XEXP (varop, 1))))); + continue; + } + break; + + case ASHIFTRT: + /* If we are extracting just the sign bit of an arithmetic right + shift, that shift is not needed. */ + if (code == LSHIFTRT && count == GET_MODE_BITSIZE (result_mode) - 1) + { + varop = XEXP (varop, 0); + continue; + } + + /* ... fall through ... */ + + case LSHIFTRT: + case ASHIFT: + case LSHIFT: + case ROTATE: + /* Here we have two nested shifts. The result is usually the + AND of a new shift with a mask. We compute the result below. */ + if (GET_CODE (XEXP (varop, 1)) == CONST_INT + && INTVAL (XEXP (varop, 1)) >= 0 + && INTVAL (XEXP (varop, 1)) < GET_MODE_BITSIZE (GET_MODE (varop)) + && GET_MODE_BITSIZE (result_mode) <= HOST_BITS_PER_WIDE_INT + && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT) + { + enum rtx_code first_code = GET_CODE (varop); + int first_count = INTVAL (XEXP (varop, 1)); + unsigned HOST_WIDE_INT mask; + rtx mask_rtx; + rtx inner; + + if (first_code == LSHIFT) + first_code = ASHIFT; + + /* We have one common special case. We can't do any merging if + the inner code is an ASHIFTRT of a smaller mode. However, if + we have (ashift:M1 (subreg:M1 (ashiftrt:M2 FOO C1) 0) C2) + with C2 == GET_MODE_BITSIZE (M1) - GET_MODE_BITSIZE (M2), + we can convert it to + (ashiftrt:M1 (ashift:M1 (and:M1 (subreg:M1 FOO 0 C2) C3) C1). + This simplifies certain SIGN_EXTEND operations. */ + if (code == ASHIFT && first_code == ASHIFTRT + && (GET_MODE_BITSIZE (result_mode) + - GET_MODE_BITSIZE (GET_MODE (varop))) == count) + { + /* C3 has the low-order C1 bits zero. */ + + mask = (GET_MODE_MASK (mode) + & ~ (((HOST_WIDE_INT) 1 << first_count) - 1)); + + varop = simplify_and_const_int (NULL_RTX, result_mode, + XEXP (varop, 0), mask); + varop = simplify_shift_const (NULL_RTX, ASHIFT, result_mode, + varop, count); + count = first_count; + code = ASHIFTRT; + continue; + } + + /* If this was (ashiftrt (ashift foo C1) C2) and FOO has more + than C1 high-order bits equal to the sign bit, we can convert + this to either an ASHIFT or a ASHIFTRT depending on the + two counts. + + We cannot do this if VAROP's mode is not SHIFT_MODE. */ + + if (code == ASHIFTRT && first_code == ASHIFT + && GET_MODE (varop) == shift_mode + && (num_sign_bit_copies (XEXP (varop, 0), shift_mode) + > first_count)) + { + count -= first_count; + if (count < 0) + count = - count, code = ASHIFT; + varop = XEXP (varop, 0); + continue; + } + + /* There are some cases we can't do. If CODE is ASHIFTRT, + we can only do this if FIRST_CODE is also ASHIFTRT. + + We can't do the case when CODE is ROTATE and FIRST_CODE is + ASHIFTRT. + + If the mode of this shift is not the mode of the outer shift, + we can't do this if either shift is ASHIFTRT or ROTATE. + + Finally, we can't do any of these if the mode is too wide + unless the codes are the same. + + Handle the case where the shift codes are the same + first. */ + + if (code == first_code) + { + if (GET_MODE (varop) != result_mode + && (code == ASHIFTRT || code == ROTATE)) + break; + + count += first_count; + varop = XEXP (varop, 0); + continue; + } + + if (code == ASHIFTRT + || (code == ROTATE && first_code == ASHIFTRT) + || GET_MODE_BITSIZE (mode) > HOST_BITS_PER_WIDE_INT + || (GET_MODE (varop) != result_mode + && (first_code == ASHIFTRT || first_code == ROTATE + || code == ROTATE))) + break; + + /* To compute the mask to apply after the shift, shift the + nonzero bits of the inner shift the same way the + outer shift will. */ + + mask_rtx = GEN_INT (nonzero_bits (varop, GET_MODE (varop))); + + mask_rtx + = simplify_binary_operation (code, result_mode, mask_rtx, + GEN_INT (count)); + + /* Give up if we can't compute an outer operation to use. */ + if (mask_rtx == 0 + || GET_CODE (mask_rtx) != CONST_INT + || ! merge_outer_ops (&outer_op, &outer_const, AND, + INTVAL (mask_rtx), + result_mode, &complement_p)) + break; + + /* If the shifts are in the same direction, we add the + counts. Otherwise, we subtract them. */ + if ((code == ASHIFTRT || code == LSHIFTRT) + == (first_code == ASHIFTRT || first_code == LSHIFTRT)) + count += first_count; + else + count -= first_count; + + /* If COUNT is positive, the new shift is usually CODE, + except for the two exceptions below, in which case it is + FIRST_CODE. If the count is negative, FIRST_CODE should + always be used */ + if (count > 0 + && ((first_code == ROTATE && code == ASHIFT) + || (first_code == ASHIFTRT && code == LSHIFTRT))) + code = first_code; + else if (count < 0) + code = first_code, count = - count; + + varop = XEXP (varop, 0); + continue; + } + + /* If we have (A << B << C) for any shift, we can convert this to + (A << C << B). This wins if A is a constant. Only try this if + B is not a constant. */ + + else if (GET_CODE (varop) == code + && GET_CODE (XEXP (varop, 1)) != CONST_INT + && 0 != (new + = simplify_binary_operation (code, mode, + XEXP (varop, 0), + GEN_INT (count)))) + { + varop = gen_rtx_combine (code, mode, new, XEXP (varop, 1)); + count = 0; + continue; + } + break; + + case NOT: + /* Make this fit the case below. */ + varop = gen_rtx_combine (XOR, mode, XEXP (varop, 0), + GEN_INT (GET_MODE_MASK (mode))); + continue; + + case IOR: + case AND: + case XOR: + /* If we have (xshiftrt (ior (plus X (const_int -1)) X) C) + with C the size of VAROP - 1 and the shift is logical if + STORE_FLAG_VALUE is 1 and arithmetic if STORE_FLAG_VALUE is -1, + we have an (le X 0) operation. If we have an arithmetic shift + and STORE_FLAG_VALUE is 1 or we have a logical shift with + STORE_FLAG_VALUE of -1, we have a (neg (le X 0)) operation. */ + + if (GET_CODE (varop) == IOR && GET_CODE (XEXP (varop, 0)) == PLUS + && XEXP (XEXP (varop, 0), 1) == constm1_rtx + && (STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1) + && (code == LSHIFTRT || code == ASHIFTRT) + && count == GET_MODE_BITSIZE (GET_MODE (varop)) - 1 + && rtx_equal_p (XEXP (XEXP (varop, 0), 0), XEXP (varop, 1))) + { + count = 0; + varop = gen_rtx_combine (LE, GET_MODE (varop), XEXP (varop, 1), + const0_rtx); + + if (STORE_FLAG_VALUE == 1 ? code == ASHIFTRT : code == LSHIFTRT) + varop = gen_rtx_combine (NEG, GET_MODE (varop), varop); + + continue; + } + + /* If we have (shift (logical)), move the logical to the outside + to allow it to possibly combine with another logical and the + shift to combine with another shift. This also canonicalizes to + what a ZERO_EXTRACT looks like. Also, some machines have + (and (shift)) insns. */ + + if (GET_CODE (XEXP (varop, 1)) == CONST_INT + && (new = simplify_binary_operation (code, result_mode, + XEXP (varop, 1), + GEN_INT (count))) != 0 + && merge_outer_ops (&outer_op, &outer_const, GET_CODE (varop), + INTVAL (new), result_mode, &complement_p)) + { + varop = XEXP (varop, 0); + continue; + } + + /* If we can't do that, try to simplify the shift in each arm of the + logical expression, make a new logical expression, and apply + the inverse distributive law. */ + { + rtx lhs = simplify_shift_const (NULL_RTX, code, result_mode, + XEXP (varop, 0), count); + rtx rhs = simplify_shift_const (NULL_RTX, code, result_mode, + XEXP (varop, 1), count); + + varop = gen_binary (GET_CODE (varop), result_mode, lhs, rhs); + varop = apply_distributive_law (varop); + + count = 0; + } + break; + + case EQ: + /* convert (lshift (eq FOO 0) C) to (xor FOO 1) if STORE_FLAG_VALUE + says that the sign bit can be tested, FOO has mode MODE, C is + GET_MODE_BITSIZE (MODE) - 1, and FOO has only the low-order bit + may be nonzero. */ + if (code == LSHIFT + && XEXP (varop, 1) == const0_rtx + && GET_MODE (XEXP (varop, 0)) == result_mode + && count == GET_MODE_BITSIZE (result_mode) - 1 + && GET_MODE_BITSIZE (result_mode) <= HOST_BITS_PER_WIDE_INT + && ((STORE_FLAG_VALUE + & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (result_mode) - 1)))) + && nonzero_bits (XEXP (varop, 0), result_mode) == 1 + && merge_outer_ops (&outer_op, &outer_const, XOR, + (HOST_WIDE_INT) 1, result_mode, + &complement_p)) + { + varop = XEXP (varop, 0); + count = 0; + continue; + } + break; + + case NEG: + /* (lshiftrt (neg A) C) where A is either 0 or 1 and C is one less + than the number of bits in the mode is equivalent to A. */ + if (code == LSHIFTRT && count == GET_MODE_BITSIZE (result_mode) - 1 + && nonzero_bits (XEXP (varop, 0), result_mode) == 1) + { + varop = XEXP (varop, 0); + count = 0; + continue; + } + + /* NEG commutes with ASHIFT since it is multiplication. Move the + NEG outside to allow shifts to combine. */ + if (code == ASHIFT + && merge_outer_ops (&outer_op, &outer_const, NEG, + (HOST_WIDE_INT) 0, result_mode, + &complement_p)) + { + varop = XEXP (varop, 0); + continue; + } + break; + + case PLUS: + /* (lshiftrt (plus A -1) C) where A is either 0 or 1 and C + is one less than the number of bits in the mode is + equivalent to (xor A 1). */ + if (code == LSHIFTRT && count == GET_MODE_BITSIZE (result_mode) - 1 + && XEXP (varop, 1) == constm1_rtx + && nonzero_bits (XEXP (varop, 0), result_mode) == 1 + && merge_outer_ops (&outer_op, &outer_const, XOR, + (HOST_WIDE_INT) 1, result_mode, + &complement_p)) + { + count = 0; + varop = XEXP (varop, 0); + continue; + } + + /* If we have (xshiftrt (plus FOO BAR) C), and the only bits + that might be nonzero in BAR are those being shifted out and those + bits are known zero in FOO, we can replace the PLUS with FOO. + Similarly in the other operand order. This code occurs when + we are computing the size of a variable-size array. */ + + if ((code == ASHIFTRT || code == LSHIFTRT) + && count < HOST_BITS_PER_WIDE_INT + && nonzero_bits (XEXP (varop, 1), result_mode) >> count == 0 + && (nonzero_bits (XEXP (varop, 1), result_mode) + & nonzero_bits (XEXP (varop, 0), result_mode)) == 0) + { + varop = XEXP (varop, 0); + continue; + } + else if ((code == ASHIFTRT || code == LSHIFTRT) + && count < HOST_BITS_PER_WIDE_INT + && GET_MODE_BITSIZE (result_mode) <= HOST_BITS_PER_WIDE_INT + && 0 == (nonzero_bits (XEXP (varop, 0), result_mode) + >> count) + && 0 == (nonzero_bits (XEXP (varop, 0), result_mode) + & nonzero_bits (XEXP (varop, 1), + result_mode))) + { + varop = XEXP (varop, 1); + continue; + } + + /* (ashift (plus foo C) N) is (plus (ashift foo N) C'). */ + if (code == ASHIFT + && GET_CODE (XEXP (varop, 1)) == CONST_INT + && (new = simplify_binary_operation (ASHIFT, result_mode, + XEXP (varop, 1), + GEN_INT (count))) != 0 + && merge_outer_ops (&outer_op, &outer_const, PLUS, + INTVAL (new), result_mode, &complement_p)) + { + varop = XEXP (varop, 0); + continue; + } + break; + + case MINUS: + /* If we have (xshiftrt (minus (ashiftrt X C)) X) C) + with C the size of VAROP - 1 and the shift is logical if + STORE_FLAG_VALUE is 1 and arithmetic if STORE_FLAG_VALUE is -1, + we have a (gt X 0) operation. If the shift is arithmetic with + STORE_FLAG_VALUE of 1 or logical with STORE_FLAG_VALUE == -1, + we have a (neg (gt X 0)) operation. */ + + if (GET_CODE (XEXP (varop, 0)) == ASHIFTRT + && count == GET_MODE_BITSIZE (GET_MODE (varop)) - 1 + && (STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1) + && (code == LSHIFTRT || code == ASHIFTRT) + && GET_CODE (XEXP (XEXP (varop, 0), 1)) == CONST_INT + && INTVAL (XEXP (XEXP (varop, 0), 1)) == count + && rtx_equal_p (XEXP (XEXP (varop, 0), 0), XEXP (varop, 1))) + { + count = 0; + varop = gen_rtx_combine (GT, GET_MODE (varop), XEXP (varop, 1), + const0_rtx); + + if (STORE_FLAG_VALUE == 1 ? code == ASHIFTRT : code == LSHIFTRT) + varop = gen_rtx_combine (NEG, GET_MODE (varop), varop); + + continue; + } + break; + } + + break; + } + + /* We need to determine what mode to do the shift in. If the shift is + a ASHIFTRT or ROTATE, we must always do it in the mode it was originally + done in. Otherwise, we can do it in MODE, the widest mode encountered. + The code we care about is that of the shift that will actually be done, + not the shift that was originally requested. */ + shift_mode = (code == ASHIFTRT || code == ROTATE ? result_mode : mode); + + /* We have now finished analyzing the shift. The result should be + a shift of type CODE with SHIFT_MODE shifting VAROP COUNT places. If + OUTER_OP is non-NIL, it is an operation that needs to be applied + to the result of the shift. OUTER_CONST is the relevant constant, + but we must turn off all bits turned off in the shift. + + If we were passed a value for X, see if we can use any pieces of + it. If not, make new rtx. */ + + if (x && GET_RTX_CLASS (GET_CODE (x)) == '2' + && GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) == count) + const_rtx = XEXP (x, 1); + else + const_rtx = GEN_INT (count); + + if (x && GET_CODE (XEXP (x, 0)) == SUBREG + && GET_MODE (XEXP (x, 0)) == shift_mode + && SUBREG_REG (XEXP (x, 0)) == varop) + varop = XEXP (x, 0); + else if (GET_MODE (varop) != shift_mode) + varop = gen_lowpart_for_combine (shift_mode, varop); + + /* If we can't make the SUBREG, try to return what we were given. */ + if (GET_CODE (varop) == CLOBBER) + return x ? x : varop; + + new = simplify_binary_operation (code, shift_mode, varop, const_rtx); + if (new != 0) + x = new; + else + { + if (x == 0 || GET_CODE (x) != code || GET_MODE (x) != shift_mode) + x = gen_rtx_combine (code, shift_mode, varop, const_rtx); + + SUBST (XEXP (x, 0), varop); + SUBST (XEXP (x, 1), const_rtx); + } + + /* If we were doing a LSHIFTRT in a wider mode than it was originally, + turn off all the bits that the shift would have turned off. */ + if (orig_code == LSHIFTRT && result_mode != shift_mode) + x = simplify_and_const_int (NULL_RTX, shift_mode, x, + GET_MODE_MASK (result_mode) >> orig_count); + + /* Do the remainder of the processing in RESULT_MODE. */ + x = gen_lowpart_for_combine (result_mode, x); + + /* If COMPLEMENT_P is set, we have to complement X before doing the outer + operation. */ + if (complement_p) + x = gen_unary (NOT, result_mode, x); + + if (outer_op != NIL) + { + if (GET_MODE_BITSIZE (result_mode) < HOST_BITS_PER_WIDE_INT) + outer_const &= GET_MODE_MASK (result_mode); + + if (outer_op == AND) + x = simplify_and_const_int (NULL_RTX, result_mode, x, outer_const); + else if (outer_op == SET) + /* This means that we have determined that the result is + equivalent to a constant. This should be rare. */ + x = GEN_INT (outer_const); + else if (GET_RTX_CLASS (outer_op) == '1') + x = gen_unary (outer_op, result_mode, x); + else + x = gen_binary (outer_op, result_mode, x, GEN_INT (outer_const)); + } + + return x; +} + +/* Like recog, but we receive the address of a pointer to a new pattern. + We try to match the rtx that the pointer points to. + If that fails, we may try to modify or replace the pattern, + storing the replacement into the same pointer object. + + Modifications include deletion or addition of CLOBBERs. + + PNOTES is a pointer to a location where any REG_UNUSED notes added for + the CLOBBERs are placed. + + The value is the final insn code from the pattern ultimately matched, + or -1. */ + +static int +recog_for_combine (pnewpat, insn, pnotes) + rtx *pnewpat; + rtx insn; + rtx *pnotes; +{ + register rtx pat = *pnewpat; + int insn_code_number; + int num_clobbers_to_add = 0; + int i; + rtx notes = 0; + + /* Is the result of combination a valid instruction? */ + insn_code_number = recog (pat, insn, &num_clobbers_to_add); + + /* If it isn't, there is the possibility that we previously had an insn + that clobbered some register as a side effect, but the combined + insn doesn't need to do that. So try once more without the clobbers + unless this represents an ASM insn. */ + + if (insn_code_number < 0 && ! check_asm_operands (pat) + && GET_CODE (pat) == PARALLEL) + { + int pos; + + for (pos = 0, i = 0; i < XVECLEN (pat, 0); i++) + if (GET_CODE (XVECEXP (pat, 0, i)) != CLOBBER) + { + if (i != pos) + SUBST (XVECEXP (pat, 0, pos), XVECEXP (pat, 0, i)); + pos++; + } + + SUBST_INT (XVECLEN (pat, 0), pos); + + if (pos == 1) + pat = XVECEXP (pat, 0, 0); + + insn_code_number = recog (pat, insn, &num_clobbers_to_add); + } + + /* If we had any clobbers to add, make a new pattern than contains + them. Then check to make sure that all of them are dead. */ + if (num_clobbers_to_add) + { + rtx newpat = gen_rtx (PARALLEL, VOIDmode, + gen_rtvec (GET_CODE (pat) == PARALLEL + ? XVECLEN (pat, 0) + num_clobbers_to_add + : num_clobbers_to_add + 1)); + + if (GET_CODE (pat) == PARALLEL) + for (i = 0; i < XVECLEN (pat, 0); i++) + XVECEXP (newpat, 0, i) = XVECEXP (pat, 0, i); + else + XVECEXP (newpat, 0, 0) = pat; + + add_clobbers (newpat, insn_code_number); + + for (i = XVECLEN (newpat, 0) - num_clobbers_to_add; + i < XVECLEN (newpat, 0); i++) + { + if (GET_CODE (XEXP (XVECEXP (newpat, 0, i), 0)) == REG + && ! reg_dead_at_p (XEXP (XVECEXP (newpat, 0, i), 0), insn)) + return -1; + notes = gen_rtx (EXPR_LIST, REG_UNUSED, + XEXP (XVECEXP (newpat, 0, i), 0), notes); + } + pat = newpat; + } + + *pnewpat = pat; + *pnotes = notes; + + return insn_code_number; +} + +/* Like gen_lowpart but for use by combine. In combine it is not possible + to create any new pseudoregs. However, it is safe to create + invalid memory addresses, because combine will try to recognize + them and all they will do is make the combine attempt fail. + + If for some reason this cannot do its job, an rtx + (clobber (const_int 0)) is returned. + An insn containing that will not be recognized. */ + +#undef gen_lowpart + +static rtx +gen_lowpart_for_combine (mode, x) + enum machine_mode mode; + register rtx x; +{ + rtx result; + + if (GET_MODE (x) == mode) + return x; + + /* We can only support MODE being wider than a word if X is a + constant integer or has a mode the same size. */ + + if (GET_MODE_SIZE (mode) > UNITS_PER_WORD + && ! ((GET_MODE (x) == VOIDmode + && (GET_CODE (x) == CONST_INT + || GET_CODE (x) == CONST_DOUBLE)) + || GET_MODE_SIZE (GET_MODE (x)) == GET_MODE_SIZE (mode))) + return gen_rtx (CLOBBER, GET_MODE (x), const0_rtx); + + /* X might be a paradoxical (subreg (mem)). In that case, gen_lowpart + won't know what to do. So we will strip off the SUBREG here and + process normally. */ + if (GET_CODE (x) == SUBREG && GET_CODE (SUBREG_REG (x)) == MEM) + { + x = SUBREG_REG (x); + if (GET_MODE (x) == mode) + return x; + } + + result = gen_lowpart_common (mode, x); + if (result) + return result; + + if (GET_CODE (x) == MEM) + { + register int offset = 0; + rtx new; + + /* Refuse to work on a volatile memory ref or one with a mode-dependent + address. */ + if (MEM_VOLATILE_P (x) || mode_dependent_address_p (XEXP (x, 0))) + return gen_rtx (CLOBBER, GET_MODE (x), const0_rtx); + + /* If we want to refer to something bigger than the original memref, + generate a perverse subreg instead. That will force a reload + of the original memref X. */ + if (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (mode)) + return gen_rtx (SUBREG, mode, x, 0); + +#if WORDS_BIG_ENDIAN + offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD) + - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD)); +#endif +#if BYTES_BIG_ENDIAN + /* Adjust the address so that the address-after-the-data + is unchanged. */ + offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)) + - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x)))); +#endif + new = gen_rtx (MEM, mode, plus_constant (XEXP (x, 0), offset)); + RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (x); + MEM_VOLATILE_P (new) = MEM_VOLATILE_P (x); + MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (x); + return new; + } + + /* If X is a comparison operator, rewrite it in a new mode. This + probably won't match, but may allow further simplifications. */ + else if (GET_RTX_CLASS (GET_CODE (x)) == '<') + return gen_rtx_combine (GET_CODE (x), mode, XEXP (x, 0), XEXP (x, 1)); + + /* If we couldn't simplify X any other way, just enclose it in a + SUBREG. Normally, this SUBREG won't match, but some patterns may + include an explicit SUBREG or we may simplify it further in combine. */ + else + { + int word = 0; + + if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD) + word = ((GET_MODE_SIZE (GET_MODE (x)) + - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD)) + / UNITS_PER_WORD); + return gen_rtx (SUBREG, mode, x, word); + } +} + +/* Make an rtx expression. This is a subset of gen_rtx and only supports + expressions of 1, 2, or 3 operands, each of which are rtx expressions. + + If the identical expression was previously in the insn (in the undobuf), + it will be returned. Only if it is not found will a new expression + be made. */ + +/*VARARGS2*/ +static rtx +gen_rtx_combine (va_alist) + va_dcl +{ + va_list p; + enum rtx_code code; + enum machine_mode mode; + int n_args; + rtx args[3]; + int i, j; + char *fmt; + rtx rt; + + va_start (p); + code = va_arg (p, enum rtx_code); + mode = va_arg (p, enum machine_mode); + n_args = GET_RTX_LENGTH (code); + fmt = GET_RTX_FORMAT (code); + + if (n_args == 0 || n_args > 3) + abort (); + + /* Get each arg and verify that it is supposed to be an expression. */ + for (j = 0; j < n_args; j++) + { + if (*fmt++ != 'e') + abort (); + + args[j] = va_arg (p, rtx); + } + + /* See if this is in undobuf. Be sure we don't use objects that came + from another insn; this could produce circular rtl structures. */ + + for (i = previous_num_undos; i < undobuf.num_undo; i++) + if (!undobuf.undo[i].is_int + && GET_CODE (undobuf.undo[i].old_contents.rtx) == code + && GET_MODE (undobuf.undo[i].old_contents.rtx) == mode) + { + for (j = 0; j < n_args; j++) + if (XEXP (undobuf.undo[i].old_contents.rtx, j) != args[j]) + break; + + if (j == n_args) + return undobuf.undo[i].old_contents.rtx; + } + + /* Otherwise make a new rtx. We know we have 1, 2, or 3 args. + Use rtx_alloc instead of gen_rtx because it's faster on RISC. */ + rt = rtx_alloc (code); + PUT_MODE (rt, mode); + XEXP (rt, 0) = args[0]; + if (n_args > 1) + { + XEXP (rt, 1) = args[1]; + if (n_args > 2) + XEXP (rt, 2) = args[2]; + } + return rt; +} + +/* These routines make binary and unary operations by first seeing if they + fold; if not, a new expression is allocated. */ + +static rtx +gen_binary (code, mode, op0, op1) + enum rtx_code code; + enum machine_mode mode; + rtx op0, op1; +{ + rtx result; + rtx tem; + + if (GET_RTX_CLASS (code) == 'c' + && (GET_CODE (op0) == CONST_INT + || (CONSTANT_P (op0) && GET_CODE (op1) != CONST_INT))) + tem = op0, op0 = op1, op1 = tem; + + if (GET_RTX_CLASS (code) == '<') + { + enum machine_mode op_mode = GET_MODE (op0); + if (op_mode == VOIDmode) + op_mode = GET_MODE (op1); + result = simplify_relational_operation (code, op_mode, op0, op1); + } + else + result = simplify_binary_operation (code, mode, op0, op1); + + if (result) + return result; + + /* Put complex operands first and constants second. */ + if (GET_RTX_CLASS (code) == 'c' + && ((CONSTANT_P (op0) && GET_CODE (op1) != CONST_INT) + || (GET_RTX_CLASS (GET_CODE (op0)) == 'o' + && GET_RTX_CLASS (GET_CODE (op1)) != 'o') + || (GET_CODE (op0) == SUBREG + && GET_RTX_CLASS (GET_CODE (SUBREG_REG (op0))) == 'o' + && GET_RTX_CLASS (GET_CODE (op1)) != 'o'))) + return gen_rtx_combine (code, mode, op1, op0); + + return gen_rtx_combine (code, mode, op0, op1); +} + +static rtx +gen_unary (code, mode, op0) + enum rtx_code code; + enum machine_mode mode; + rtx op0; +{ + rtx result = simplify_unary_operation (code, mode, op0, mode); + + if (result) + return result; + + return gen_rtx_combine (code, mode, op0); +} + +/* Simplify a comparison between *POP0 and *POP1 where CODE is the + comparison code that will be tested. + + The result is a possibly different comparison code to use. *POP0 and + *POP1 may be updated. + + It is possible that we might detect that a comparison is either always + true or always false. However, we do not perform general constant + folding in combine, so this knowledge isn't useful. Such tautologies + should have been detected earlier. Hence we ignore all such cases. */ + +static enum rtx_code +simplify_comparison (code, pop0, pop1) + enum rtx_code code; + rtx *pop0; + rtx *pop1; +{ + rtx op0 = *pop0; + rtx op1 = *pop1; + rtx tem, tem1; + int i; + enum machine_mode mode, tmode; + + /* Try a few ways of applying the same transformation to both operands. */ + while (1) + { + /* If both operands are the same constant shift, see if we can ignore the + shift. We can if the shift is a rotate or if the bits shifted out of + this shift are known to be zero for both inputs and if the type of + comparison is compatible with the shift. */ + if (GET_CODE (op0) == GET_CODE (op1) + && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT + && ((GET_CODE (op0) == ROTATE && (code == NE || code == EQ)) + || ((GET_CODE (op0) == LSHIFTRT + || GET_CODE (op0) == ASHIFT || GET_CODE (op0) == LSHIFT) + && (code != GT && code != LT && code != GE && code != LE)) + || (GET_CODE (op0) == ASHIFTRT + && (code != GTU && code != LTU + && code != GEU && code != GEU))) + && GET_CODE (XEXP (op0, 1)) == CONST_INT + && INTVAL (XEXP (op0, 1)) >= 0 + && INTVAL (XEXP (op0, 1)) < HOST_BITS_PER_WIDE_INT + && XEXP (op0, 1) == XEXP (op1, 1)) + { + enum machine_mode mode = GET_MODE (op0); + unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode); + int shift_count = INTVAL (XEXP (op0, 1)); + + if (GET_CODE (op0) == LSHIFTRT || GET_CODE (op0) == ASHIFTRT) + mask &= (mask >> shift_count) << shift_count; + else if (GET_CODE (op0) == ASHIFT || GET_CODE (op0) == LSHIFT) + mask = (mask & (mask << shift_count)) >> shift_count; + + if ((nonzero_bits (XEXP (op0, 0), mode) & ~ mask) == 0 + && (nonzero_bits (XEXP (op1, 0), mode) & ~ mask) == 0) + op0 = XEXP (op0, 0), op1 = XEXP (op1, 0); + else + break; + } + + /* If both operands are AND's of a paradoxical SUBREG by constant, the + SUBREGs are of the same mode, and, in both cases, the AND would + be redundant if the comparison was done in the narrower mode, + do the comparison in the narrower mode (e.g., we are AND'ing with 1 + and the operand's possibly nonzero bits are 0xffffff01; in that case + if we only care about QImode, we don't need the AND). This case + occurs if the output mode of an scc insn is not SImode and + STORE_FLAG_VALUE == 1 (e.g., the 386). */ + + else if (GET_CODE (op0) == AND && GET_CODE (op1) == AND + && GET_CODE (XEXP (op0, 1)) == CONST_INT + && GET_CODE (XEXP (op1, 1)) == CONST_INT + && GET_CODE (XEXP (op0, 0)) == SUBREG + && GET_CODE (XEXP (op1, 0)) == SUBREG + && (GET_MODE_SIZE (GET_MODE (XEXP (op0, 0))) + > GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (op0, 0))))) + && (GET_MODE (SUBREG_REG (XEXP (op0, 0))) + == GET_MODE (SUBREG_REG (XEXP (op1, 0)))) + && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (XEXP (op0, 0)))) + <= HOST_BITS_PER_WIDE_INT) + && (nonzero_bits (SUBREG_REG (XEXP (op0, 0)), + GET_MODE (SUBREG_REG (XEXP (op0, 0)))) + & ~ INTVAL (XEXP (op0, 1))) == 0 + && (nonzero_bits (SUBREG_REG (XEXP (op1, 0)), + GET_MODE (SUBREG_REG (XEXP (op1, 0)))) + & ~ INTVAL (XEXP (op1, 1))) == 0) + { + op0 = SUBREG_REG (XEXP (op0, 0)); + op1 = SUBREG_REG (XEXP (op1, 0)); + + /* the resulting comparison is always unsigned since we masked off + the original sign bit. */ + code = unsigned_condition (code); + } + else + break; + } + + /* If the first operand is a constant, swap the operands and adjust the + comparison code appropriately. */ + if (CONSTANT_P (op0)) + { + tem = op0, op0 = op1, op1 = tem; + code = swap_condition (code); + } + + /* We now enter a loop during which we will try to simplify the comparison. + For the most part, we only are concerned with comparisons with zero, + but some things may really be comparisons with zero but not start + out looking that way. */ + + while (GET_CODE (op1) == CONST_INT) + { + enum machine_mode mode = GET_MODE (op0); + int mode_width = GET_MODE_BITSIZE (mode); + unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode); + int equality_comparison_p; + int sign_bit_comparison_p; + int unsigned_comparison_p; + HOST_WIDE_INT const_op; + + /* We only want to handle integral modes. This catches VOIDmode, + CCmode, and the floating-point modes. An exception is that we + can handle VOIDmode if OP0 is a COMPARE or a comparison + operation. */ + + if (GET_MODE_CLASS (mode) != MODE_INT + && ! (mode == VOIDmode + && (GET_CODE (op0) == COMPARE + || GET_RTX_CLASS (GET_CODE (op0)) == '<'))) + break; + + /* Get the constant we are comparing against and turn off all bits + not on in our mode. */ + const_op = INTVAL (op1); + if (mode_width <= HOST_BITS_PER_WIDE_INT) + const_op &= mask; + + /* If we are comparing against a constant power of two and the value + being compared can only have that single bit nonzero (e.g., it was + `and'ed with that bit), we can replace this with a comparison + with zero. */ + if (const_op + && (code == EQ || code == NE || code == GE || code == GEU + || code == LT || code == LTU) + && mode_width <= HOST_BITS_PER_WIDE_INT + && exact_log2 (const_op) >= 0 + && nonzero_bits (op0, mode) == const_op) + { + code = (code == EQ || code == GE || code == GEU ? NE : EQ); + op1 = const0_rtx, const_op = 0; + } + + /* Similarly, if we are comparing a value known to be either -1 or + 0 with -1, change it to the opposite comparison against zero. */ + + if (const_op == -1 + && (code == EQ || code == NE || code == GT || code == LE + || code == GEU || code == LTU) + && num_sign_bit_copies (op0, mode) == mode_width) + { + code = (code == EQ || code == LE || code == GEU ? NE : EQ); + op1 = const0_rtx, const_op = 0; + } + + /* Do some canonicalizations based on the comparison code. We prefer + comparisons against zero and then prefer equality comparisons. + If we can reduce the size of a constant, we will do that too. */ + + switch (code) + { + case LT: + /* < C is equivalent to <= (C - 1) */ + if (const_op > 0) + { + const_op -= 1; + op1 = GEN_INT (const_op); + code = LE; + /* ... fall through to LE case below. */ + } + else + break; + + case LE: + /* <= C is equivalent to < (C + 1); we do this for C < 0 */ + if (const_op < 0) + { + const_op += 1; + op1 = GEN_INT (const_op); + code = LT; + } + + /* If we are doing a <= 0 comparison on a value known to have + a zero sign bit, we can replace this with == 0. */ + else if (const_op == 0 + && mode_width <= HOST_BITS_PER_WIDE_INT + && (nonzero_bits (op0, mode) + & ((HOST_WIDE_INT) 1 << (mode_width - 1))) == 0) + code = EQ; + break; + + case GE: + /* >= C is equivalent to > (C - 1). */ + if (const_op > 0) + { + const_op -= 1; + op1 = GEN_INT (const_op); + code = GT; + /* ... fall through to GT below. */ + } + else + break; + + case GT: + /* > C is equivalent to >= (C + 1); we do this for C < 0*/ + if (const_op < 0) + { + const_op += 1; + op1 = GEN_INT (const_op); + code = GE; + } + + /* If we are doing a > 0 comparison on a value known to have + a zero sign bit, we can replace this with != 0. */ + else if (const_op == 0 + && mode_width <= HOST_BITS_PER_WIDE_INT + && (nonzero_bits (op0, mode) + & ((HOST_WIDE_INT) 1 << (mode_width - 1))) == 0) + code = NE; + break; + + case LTU: + /* < C is equivalent to <= (C - 1). */ + if (const_op > 0) + { + const_op -= 1; + op1 = GEN_INT (const_op); + code = LEU; + /* ... fall through ... */ + } + + /* (unsigned) < 0x80000000 is equivalent to >= 0. */ + else if (const_op == (HOST_WIDE_INT) 1 << (mode_width - 1)) + { + const_op = 0, op1 = const0_rtx; + code = GE; + break; + } + else + break; + + case LEU: + /* unsigned <= 0 is equivalent to == 0 */ + if (const_op == 0) + code = EQ; + + /* (unsigned) <= 0x7fffffff is equivalent to >= 0. */ + else if (const_op == ((HOST_WIDE_INT) 1 << (mode_width - 1)) - 1) + { + const_op = 0, op1 = const0_rtx; + code = GE; + } + break; + + case GEU: + /* >= C is equivalent to < (C - 1). */ + if (const_op > 1) + { + const_op -= 1; + op1 = GEN_INT (const_op); + code = GTU; + /* ... fall through ... */ + } + + /* (unsigned) >= 0x80000000 is equivalent to < 0. */ + else if (const_op == (HOST_WIDE_INT) 1 << (mode_width - 1)) + { + const_op = 0, op1 = const0_rtx; + code = LT; + } + else + break; + + case GTU: + /* unsigned > 0 is equivalent to != 0 */ + if (const_op == 0) + code = NE; + + /* (unsigned) > 0x7fffffff is equivalent to < 0. */ + else if (const_op == ((HOST_WIDE_INT) 1 << (mode_width - 1)) - 1) + { + const_op = 0, op1 = const0_rtx; + code = LT; + } + break; + } + + /* Compute some predicates to simplify code below. */ + + equality_comparison_p = (code == EQ || code == NE); + sign_bit_comparison_p = ((code == LT || code == GE) && const_op == 0); + unsigned_comparison_p = (code == LTU || code == LEU || code == GTU + || code == LEU); + + /* Now try cases based on the opcode of OP0. If none of the cases + does a "continue", we exit this loop immediately after the + switch. */ + + switch (GET_CODE (op0)) + { + case ZERO_EXTRACT: + /* If we are extracting a single bit from a variable position in + a constant that has only a single bit set and are comparing it + with zero, we can convert this into an equality comparison + between the position and the location of the single bit. We can't + do this if bit endian and we don't have an extzv since we then + can't know what mode to use for the endianness adjustment. */ + +#if ! BITS_BIG_ENDIAN || defined (HAVE_extzv) + if (GET_CODE (XEXP (op0, 0)) == CONST_INT + && XEXP (op0, 1) == const1_rtx + && equality_comparison_p && const_op == 0 + && (i = exact_log2 (INTVAL (XEXP (op0, 0)))) >= 0) + { +#if BITS_BIG_ENDIAN + i = (GET_MODE_BITSIZE + (insn_operand_mode[(int) CODE_FOR_extzv][1]) - 1 - i); +#endif + + op0 = XEXP (op0, 2); + op1 = GEN_INT (i); + const_op = i; + + /* Result is nonzero iff shift count is equal to I. */ + code = reverse_condition (code); + continue; + } +#endif + + /* ... fall through ... */ + + case SIGN_EXTRACT: + tem = expand_compound_operation (op0); + if (tem != op0) + { + op0 = tem; + continue; + } + break; + + case NOT: + /* If testing for equality, we can take the NOT of the constant. */ + if (equality_comparison_p + && (tem = simplify_unary_operation (NOT, mode, op1, mode)) != 0) + { + op0 = XEXP (op0, 0); + op1 = tem; + continue; + } + + /* If just looking at the sign bit, reverse the sense of the + comparison. */ + if (sign_bit_comparison_p) + { + op0 = XEXP (op0, 0); + code = (code == GE ? LT : GE); + continue; + } + break; + + case NEG: + /* If testing for equality, we can take the NEG of the constant. */ + if (equality_comparison_p + && (tem = simplify_unary_operation (NEG, mode, op1, mode)) != 0) + { + op0 = XEXP (op0, 0); + op1 = tem; + continue; + } + + /* The remaining cases only apply to comparisons with zero. */ + if (const_op != 0) + break; + + /* When X is ABS or is known positive, + (neg X) is < 0 if and only if X != 0. */ + + if (sign_bit_comparison_p + && (GET_CODE (XEXP (op0, 0)) == ABS + || (mode_width <= HOST_BITS_PER_WIDE_INT + && (nonzero_bits (XEXP (op0, 0), mode) + & ((HOST_WIDE_INT) 1 << (mode_width - 1))) == 0))) + { + op0 = XEXP (op0, 0); + code = (code == LT ? NE : EQ); + continue; + } + + /* If we have NEG of something whose two high-order bits are the + same, we know that "(-a) < 0" is equivalent to "a > 0". */ + if (num_sign_bit_copies (op0, mode) >= 2) + { + op0 = XEXP (op0, 0); + code = swap_condition (code); + continue; + } + break; + + case ROTATE: + /* If we are testing equality and our count is a constant, we + can perform the inverse operation on our RHS. */ + if (equality_comparison_p && GET_CODE (XEXP (op0, 1)) == CONST_INT + && (tem = simplify_binary_operation (ROTATERT, mode, + op1, XEXP (op0, 1))) != 0) + { + op0 = XEXP (op0, 0); + op1 = tem; + continue; + } + + /* If we are doing a < 0 or >= 0 comparison, it means we are testing + a particular bit. Convert it to an AND of a constant of that + bit. This will be converted into a ZERO_EXTRACT. */ + if (const_op == 0 && sign_bit_comparison_p + && GET_CODE (XEXP (op0, 1)) == CONST_INT + && mode_width <= HOST_BITS_PER_WIDE_INT) + { + op0 = simplify_and_const_int (NULL_RTX, mode, XEXP (op0, 0), + ((HOST_WIDE_INT) 1 + << (mode_width - 1 + - INTVAL (XEXP (op0, 1))))); + code = (code == LT ? NE : EQ); + continue; + } + + /* ... fall through ... */ + + case ABS: + /* ABS is ignorable inside an equality comparison with zero. */ + if (const_op == 0 && equality_comparison_p) + { + op0 = XEXP (op0, 0); + continue; + } + break; + + + case SIGN_EXTEND: + /* Can simplify (compare (zero/sign_extend FOO) CONST) + to (compare FOO CONST) if CONST fits in FOO's mode and we + are either testing inequality or have an unsigned comparison + with ZERO_EXTEND or a signed comparison with SIGN_EXTEND. */ + if (! unsigned_comparison_p + && (GET_MODE_BITSIZE (GET_MODE (XEXP (op0, 0))) + <= HOST_BITS_PER_WIDE_INT) + && ((unsigned HOST_WIDE_INT) const_op + < (((HOST_WIDE_INT) 1 + << (GET_MODE_BITSIZE (GET_MODE (XEXP (op0, 0))) - 1))))) + { + op0 = XEXP (op0, 0); + continue; + } + break; + + case SUBREG: + /* Check for the case where we are comparing A - C1 with C2, + both constants are smaller than 1/2 the maxium positive + value in MODE, and the comparison is equality or unsigned. + In that case, if A is either zero-extended to MODE or has + sufficient sign bits so that the high-order bit in MODE + is a copy of the sign in the inner mode, we can prove that it is + safe to do the operation in the wider mode. This simplifies + many range checks. */ + + if (mode_width <= HOST_BITS_PER_WIDE_INT + && subreg_lowpart_p (op0) + && GET_CODE (SUBREG_REG (op0)) == PLUS + && GET_CODE (XEXP (SUBREG_REG (op0), 1)) == CONST_INT + && INTVAL (XEXP (SUBREG_REG (op0), 1)) < 0 + && (- INTVAL (XEXP (SUBREG_REG (op0), 1)) + < GET_MODE_MASK (mode) / 2) + && (unsigned HOST_WIDE_INT) const_op < GET_MODE_MASK (mode) / 2 + && (0 == (nonzero_bits (XEXP (SUBREG_REG (op0), 0), + GET_MODE (SUBREG_REG (op0))) + & ~ GET_MODE_MASK (mode)) + || (num_sign_bit_copies (XEXP (SUBREG_REG (op0), 0), + GET_MODE (SUBREG_REG (op0))) + > (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0))) + - GET_MODE_BITSIZE (mode))))) + { + op0 = SUBREG_REG (op0); + continue; + } + + /* If the inner mode is narrower and we are extracting the low part, + we can treat the SUBREG as if it were a ZERO_EXTEND. */ + if (subreg_lowpart_p (op0) + && GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0))) < mode_width) + /* Fall through */ ; + else + break; + + /* ... fall through ... */ + + case ZERO_EXTEND: + if ((unsigned_comparison_p || equality_comparison_p) + && (GET_MODE_BITSIZE (GET_MODE (XEXP (op0, 0))) + <= HOST_BITS_PER_WIDE_INT) + && ((unsigned HOST_WIDE_INT) const_op + < GET_MODE_MASK (GET_MODE (XEXP (op0, 0))))) + { + op0 = XEXP (op0, 0); + continue; + } + break; + + case PLUS: + /* (eq (plus X A) B) -> (eq X (minus B A)). We can only do + this for equality comparisons due to pathological cases involving + overflows. */ + if (equality_comparison_p + && 0 != (tem = simplify_binary_operation (MINUS, mode, + op1, XEXP (op0, 1)))) + { + op0 = XEXP (op0, 0); + op1 = tem; + continue; + } + + /* (plus (abs X) (const_int -1)) is < 0 if and only if X == 0. */ + if (const_op == 0 && XEXP (op0, 1) == constm1_rtx + && GET_CODE (XEXP (op0, 0)) == ABS && sign_bit_comparison_p) + { + op0 = XEXP (XEXP (op0, 0), 0); + code = (code == LT ? EQ : NE); + continue; + } + break; + + case MINUS: + /* (eq (minus A B) C) -> (eq A (plus B C)) or + (eq B (minus A C)), whichever simplifies. We can only do + this for equality comparisons due to pathological cases involving + overflows. */ + if (equality_comparison_p + && 0 != (tem = simplify_binary_operation (PLUS, mode, + XEXP (op0, 1), op1))) + { + op0 = XEXP (op0, 0); + op1 = tem; + continue; + } + + if (equality_comparison_p + && 0 != (tem = simplify_binary_operation (MINUS, mode, + XEXP (op0, 0), op1))) + { + op0 = XEXP (op0, 1); + op1 = tem; + continue; + } + + /* The sign bit of (minus (ashiftrt X C) X), where C is the number + of bits in X minus 1, is one iff X > 0. */ + if (sign_bit_comparison_p && GET_CODE (XEXP (op0, 0)) == ASHIFTRT + && GET_CODE (XEXP (XEXP (op0, 0), 1)) == CONST_INT + && INTVAL (XEXP (XEXP (op0, 0), 1)) == mode_width - 1 + && rtx_equal_p (XEXP (XEXP (op0, 0), 0), XEXP (op0, 1))) + { + op0 = XEXP (op0, 1); + code = (code == GE ? LE : GT); + continue; + } + break; + + case XOR: + /* (eq (xor A B) C) -> (eq A (xor B C)). This is a simplification + if C is zero or B is a constant. */ + if (equality_comparison_p + && 0 != (tem = simplify_binary_operation (XOR, mode, + XEXP (op0, 1), op1))) + { + op0 = XEXP (op0, 0); + op1 = tem; + continue; + } + break; + + case EQ: case NE: + case LT: case LTU: case LE: case LEU: + case GT: case GTU: case GE: case GEU: + /* We can't do anything if OP0 is a condition code value, rather + than an actual data value. */ + if (const_op != 0 +#ifdef HAVE_cc0 + || XEXP (op0, 0) == cc0_rtx +#endif + || GET_MODE_CLASS (GET_MODE (XEXP (op0, 0))) == MODE_CC) + break; + + /* Get the two operands being compared. */ + if (GET_CODE (XEXP (op0, 0)) == COMPARE) + tem = XEXP (XEXP (op0, 0), 0), tem1 = XEXP (XEXP (op0, 0), 1); + else + tem = XEXP (op0, 0), tem1 = XEXP (op0, 1); + + /* Check for the cases where we simply want the result of the + earlier test or the opposite of that result. */ + if (code == NE + || (code == EQ && reversible_comparison_p (op0)) + || (GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT + && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT + && (STORE_FLAG_VALUE + & (((HOST_WIDE_INT) 1 + << (GET_MODE_BITSIZE (GET_MODE (op0)) - 1)))) + && (code == LT + || (code == GE && reversible_comparison_p (op0))))) + { + code = (code == LT || code == NE + ? GET_CODE (op0) : reverse_condition (GET_CODE (op0))); + op0 = tem, op1 = tem1; + continue; + } + break; + + case IOR: + /* The sign bit of (ior (plus X (const_int -1)) X) is non-zero + iff X <= 0. */ + if (sign_bit_comparison_p && GET_CODE (XEXP (op0, 0)) == PLUS + && XEXP (XEXP (op0, 0), 1) == constm1_rtx + && rtx_equal_p (XEXP (XEXP (op0, 0), 0), XEXP (op0, 1))) + { + op0 = XEXP (op0, 1); + code = (code == GE ? GT : LE); + continue; + } + break; + + case AND: + /* Convert (and (xshift 1 X) Y) to (and (lshiftrt Y X) 1). This + will be converted to a ZERO_EXTRACT later. */ + if (const_op == 0 && equality_comparison_p + && (GET_CODE (XEXP (op0, 0)) == ASHIFT + || GET_CODE (XEXP (op0, 0)) == LSHIFT) + && XEXP (XEXP (op0, 0), 0) == const1_rtx) + { + op0 = simplify_and_const_int + (op0, mode, gen_rtx_combine (LSHIFTRT, mode, + XEXP (op0, 1), + XEXP (XEXP (op0, 0), 1)), + (HOST_WIDE_INT) 1); + continue; + } + + /* If we are comparing (and (lshiftrt X C1) C2) for equality with + zero and X is a comparison and C1 and C2 describe only bits set + in STORE_FLAG_VALUE, we can compare with X. */ + if (const_op == 0 && equality_comparison_p + && mode_width <= HOST_BITS_PER_WIDE_INT + && GET_CODE (XEXP (op0, 1)) == CONST_INT + && GET_CODE (XEXP (op0, 0)) == LSHIFTRT + && GET_CODE (XEXP (XEXP (op0, 0), 1)) == CONST_INT + && INTVAL (XEXP (XEXP (op0, 0), 1)) >= 0 + && INTVAL (XEXP (XEXP (op0, 0), 1)) < HOST_BITS_PER_WIDE_INT) + { + mask = ((INTVAL (XEXP (op0, 1)) & GET_MODE_MASK (mode)) + << INTVAL (XEXP (XEXP (op0, 0), 1))); + if ((~ STORE_FLAG_VALUE & mask) == 0 + && (GET_RTX_CLASS (GET_CODE (XEXP (XEXP (op0, 0), 0))) == '<' + || ((tem = get_last_value (XEXP (XEXP (op0, 0), 0))) != 0 + && GET_RTX_CLASS (GET_CODE (tem)) == '<'))) + { + op0 = XEXP (XEXP (op0, 0), 0); + continue; + } + } + + /* If we are doing an equality comparison of an AND of a bit equal + to the sign bit, replace this with a LT or GE comparison of + the underlying value. */ + if (equality_comparison_p + && const_op == 0 + && GET_CODE (XEXP (op0, 1)) == CONST_INT + && mode_width <= HOST_BITS_PER_WIDE_INT + && ((INTVAL (XEXP (op0, 1)) & GET_MODE_MASK (mode)) + == (HOST_WIDE_INT) 1 << (mode_width - 1))) + { + op0 = XEXP (op0, 0); + code = (code == EQ ? GE : LT); + continue; + } + + /* If this AND operation is really a ZERO_EXTEND from a narrower + mode, the constant fits within that mode, and this is either an + equality or unsigned comparison, try to do this comparison in + the narrower mode. */ + if ((equality_comparison_p || unsigned_comparison_p) + && GET_CODE (XEXP (op0, 1)) == CONST_INT + && (i = exact_log2 ((INTVAL (XEXP (op0, 1)) + & GET_MODE_MASK (mode)) + + 1)) >= 0 + && const_op >> i == 0 + && (tmode = mode_for_size (i, MODE_INT, 1)) != BLKmode) + { + op0 = gen_lowpart_for_combine (tmode, XEXP (op0, 0)); + continue; + } + break; + + case ASHIFT: + case LSHIFT: + /* If we have (compare (xshift FOO N) (const_int C)) and + the high order N bits of FOO (N+1 if an inequality comparison) + are known to be zero, we can do this by comparing FOO with C + shifted right N bits so long as the low-order N bits of C are + zero. */ + if (GET_CODE (XEXP (op0, 1)) == CONST_INT + && INTVAL (XEXP (op0, 1)) >= 0 + && ((INTVAL (XEXP (op0, 1)) + ! equality_comparison_p) + < HOST_BITS_PER_WIDE_INT) + && ((const_op + & ((HOST_WIDE_INT) 1 << INTVAL (XEXP (op0, 1))) - 1) == 0) + && mode_width <= HOST_BITS_PER_WIDE_INT + && (nonzero_bits (XEXP (op0, 0), mode) + & ~ (mask >> (INTVAL (XEXP (op0, 1)) + + ! equality_comparison_p))) == 0) + { + const_op >>= INTVAL (XEXP (op0, 1)); + op1 = GEN_INT (const_op); + op0 = XEXP (op0, 0); + continue; + } + + /* If we are doing a sign bit comparison, it means we are testing + a particular bit. Convert it to the appropriate AND. */ + if (sign_bit_comparison_p && GET_CODE (XEXP (op0, 1)) == CONST_INT + && mode_width <= HOST_BITS_PER_WIDE_INT) + { + op0 = simplify_and_const_int (NULL_RTX, mode, XEXP (op0, 0), + ((HOST_WIDE_INT) 1 + << (mode_width - 1 + - INTVAL (XEXP (op0, 1))))); + code = (code == LT ? NE : EQ); + continue; + } + + /* If this an equality comparison with zero and we are shifting + the low bit to the sign bit, we can convert this to an AND of the + low-order bit. */ + if (const_op == 0 && equality_comparison_p + && GET_CODE (XEXP (op0, 1)) == CONST_INT + && INTVAL (XEXP (op0, 1)) == mode_width - 1) + { + op0 = simplify_and_const_int (NULL_RTX, mode, XEXP (op0, 0), + (HOST_WIDE_INT) 1); + continue; + } + break; + + case ASHIFTRT: + /* If this is an equality comparison with zero, we can do this + as a logical shift, which might be much simpler. */ + if (equality_comparison_p && const_op == 0 + && GET_CODE (XEXP (op0, 1)) == CONST_INT) + { + op0 = simplify_shift_const (NULL_RTX, LSHIFTRT, mode, + XEXP (op0, 0), + INTVAL (XEXP (op0, 1))); + continue; + } + + /* If OP0 is a sign extension and CODE is not an unsigned comparison, + do the comparison in a narrower mode. */ + if (! unsigned_comparison_p + && GET_CODE (XEXP (op0, 1)) == CONST_INT + && GET_CODE (XEXP (op0, 0)) == ASHIFT + && XEXP (op0, 1) == XEXP (XEXP (op0, 0), 1) + && (tmode = mode_for_size (mode_width - INTVAL (XEXP (op0, 1)), + MODE_INT, 1)) != BLKmode + && ((unsigned HOST_WIDE_INT) const_op <= GET_MODE_MASK (tmode) + || ((unsigned HOST_WIDE_INT) - const_op + <= GET_MODE_MASK (tmode)))) + { + op0 = gen_lowpart_for_combine (tmode, XEXP (XEXP (op0, 0), 0)); + continue; + } + + /* ... fall through ... */ + case LSHIFTRT: + /* If we have (compare (xshiftrt FOO N) (const_int C)) and + the low order N bits of FOO are known to be zero, we can do this + by comparing FOO with C shifted left N bits so long as no + overflow occurs. */ + if (GET_CODE (XEXP (op0, 1)) == CONST_INT + && INTVAL (XEXP (op0, 1)) >= 0 + && INTVAL (XEXP (op0, 1)) < HOST_BITS_PER_WIDE_INT + && mode_width <= HOST_BITS_PER_WIDE_INT + && (nonzero_bits (XEXP (op0, 0), mode) + & (((HOST_WIDE_INT) 1 << INTVAL (XEXP (op0, 1))) - 1)) == 0 + && (const_op == 0 + || (floor_log2 (const_op) + INTVAL (XEXP (op0, 1)) + < mode_width))) + { + const_op <<= INTVAL (XEXP (op0, 1)); + op1 = GEN_INT (const_op); + op0 = XEXP (op0, 0); + continue; + } + + /* If we are using this shift to extract just the sign bit, we + can replace this with an LT or GE comparison. */ + if (const_op == 0 + && (equality_comparison_p || sign_bit_comparison_p) + && GET_CODE (XEXP (op0, 1)) == CONST_INT + && INTVAL (XEXP (op0, 1)) == mode_width - 1) + { + op0 = XEXP (op0, 0); + code = (code == NE || code == GT ? LT : GE); + continue; + } + break; + } + + break; + } + + /* Now make any compound operations involved in this comparison. Then, + check for an outmost SUBREG on OP0 that isn't doing anything or is + paradoxical. The latter case can only occur when it is known that the + "extra" bits will be zero. Therefore, it is safe to remove the SUBREG. + We can never remove a SUBREG for a non-equality comparison because the + sign bit is in a different place in the underlying object. */ + + op0 = make_compound_operation (op0, op1 == const0_rtx ? COMPARE : SET); + op1 = make_compound_operation (op1, SET); + + if (GET_CODE (op0) == SUBREG && subreg_lowpart_p (op0) + && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT + && (code == NE || code == EQ) + && ((GET_MODE_SIZE (GET_MODE (op0)) + > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)))))) + { + op0 = SUBREG_REG (op0); + op1 = gen_lowpart_for_combine (GET_MODE (op0), op1); + } + + else if (GET_CODE (op0) == SUBREG && subreg_lowpart_p (op0) + && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT + && (code == NE || code == EQ) + && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0))) + <= HOST_BITS_PER_WIDE_INT) + && (nonzero_bits (SUBREG_REG (op0), GET_MODE (SUBREG_REG (op0))) + & ~ GET_MODE_MASK (GET_MODE (op0))) == 0 + && (tem = gen_lowpart_for_combine (GET_MODE (SUBREG_REG (op0)), + op1), + (nonzero_bits (tem, GET_MODE (SUBREG_REG (op0))) + & ~ GET_MODE_MASK (GET_MODE (op0))) == 0)) + op0 = SUBREG_REG (op0), op1 = tem; + + /* We now do the opposite procedure: Some machines don't have compare + insns in all modes. If OP0's mode is an integer mode smaller than a + word and we can't do a compare in that mode, see if there is a larger + mode for which we can do the compare. There are a number of cases in + which we can use the wider mode. */ + + mode = GET_MODE (op0); + if (mode != VOIDmode && GET_MODE_CLASS (mode) == MODE_INT + && GET_MODE_SIZE (mode) < UNITS_PER_WORD + && cmp_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) + for (tmode = GET_MODE_WIDER_MODE (mode); + (tmode != VOIDmode + && GET_MODE_BITSIZE (tmode) <= HOST_BITS_PER_WIDE_INT); + tmode = GET_MODE_WIDER_MODE (tmode)) + if (cmp_optab->handlers[(int) tmode].insn_code != CODE_FOR_nothing) + { + /* If the only nonzero bits in OP0 and OP1 are those in the + narrower mode and this is an equality or unsigned comparison, + we can use the wider mode. Similarly for sign-extended + values and equality or signed comparisons. */ + if (((code == EQ || code == NE + || code == GEU || code == GTU || code == LEU || code == LTU) + && (nonzero_bits (op0, tmode) & ~ GET_MODE_MASK (mode)) == 0 + && (nonzero_bits (op1, tmode) & ~ GET_MODE_MASK (mode)) == 0) + || ((code == EQ || code == NE + || code == GE || code == GT || code == LE || code == LT) + && (num_sign_bit_copies (op0, tmode) + > GET_MODE_BITSIZE (tmode) - GET_MODE_BITSIZE (mode)) + && (num_sign_bit_copies (op1, tmode) + > GET_MODE_BITSIZE (tmode) - GET_MODE_BITSIZE (mode)))) + { + op0 = gen_lowpart_for_combine (tmode, op0); + op1 = gen_lowpart_for_combine (tmode, op1); + break; + } + + /* If this is a test for negative, we can make an explicit + test of the sign bit. */ + + if (op1 == const0_rtx && (code == LT || code == GE) + && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT) + { + op0 = gen_binary (AND, tmode, + gen_lowpart_for_combine (tmode, op0), + GEN_INT ((HOST_WIDE_INT) 1 + << (GET_MODE_BITSIZE (mode) - 1))); + code = (code == LT) ? NE : EQ; + break; + } + } + + *pop0 = op0; + *pop1 = op1; + + return code; +} + +/* Return 1 if we know that X, a comparison operation, is not operating + on a floating-point value or is EQ or NE, meaning that we can safely + reverse it. */ + +static int +reversible_comparison_p (x) + rtx x; +{ + if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT + || GET_CODE (x) == NE || GET_CODE (x) == EQ) + return 1; + + switch (GET_MODE_CLASS (GET_MODE (XEXP (x, 0)))) + { + case MODE_INT: + return 1; + + case MODE_CC: + x = get_last_value (XEXP (x, 0)); + return (x && GET_CODE (x) == COMPARE + && GET_MODE_CLASS (GET_MODE (XEXP (x, 0))) == MODE_INT); + } + + return 0; +} + +/* Utility function for following routine. Called when X is part of a value + being stored into reg_last_set_value. Sets reg_last_set_table_tick + for each register mentioned. Similar to mention_regs in cse.c */ + +static void +update_table_tick (x) + rtx x; +{ + register enum rtx_code code = GET_CODE (x); + register char *fmt = GET_RTX_FORMAT (code); + register int i; + + if (code == REG) + { + int regno = REGNO (x); + int endregno = regno + (regno < FIRST_PSEUDO_REGISTER + ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1); + + for (i = regno; i < endregno; i++) + reg_last_set_table_tick[i] = label_tick; + + return; + } + + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + /* Note that we can't have an "E" in values stored; see + get_last_value_validate. */ + if (fmt[i] == 'e') + update_table_tick (XEXP (x, i)); +} + +/* Record that REG is set to VALUE in insn INSN. If VALUE is zero, we + are saying that the register is clobbered and we no longer know its + value. If INSN is zero, don't update reg_last_set; this is only permitted + with VALUE also zero and is used to invalidate the register. */ + +static void +record_value_for_reg (reg, insn, value) + rtx reg; + rtx insn; + rtx value; +{ + int regno = REGNO (reg); + int endregno = regno + (regno < FIRST_PSEUDO_REGISTER + ? HARD_REGNO_NREGS (regno, GET_MODE (reg)) : 1); + int i; + + /* If VALUE contains REG and we have a previous value for REG, substitute + the previous value. */ + if (value && insn && reg_overlap_mentioned_p (reg, value)) + { + rtx tem; + + /* Set things up so get_last_value is allowed to see anything set up to + our insn. */ + subst_low_cuid = INSN_CUID (insn); + tem = get_last_value (reg); + + if (tem) + value = replace_rtx (copy_rtx (value), reg, tem); + } + + /* For each register modified, show we don't know its value, that + its value has been updated, and that we don't know the location of + the death of the register. */ + for (i = regno; i < endregno; i ++) + { + if (insn) + reg_last_set[i] = insn; + reg_last_set_value[i] = 0; + reg_last_death[i] = 0; + } + + /* Mark registers that are being referenced in this value. */ + if (value) + update_table_tick (value); + + /* Now update the status of each register being set. + If someone is using this register in this block, set this register + to invalid since we will get confused between the two lives in this + basic block. This makes using this register always invalid. In cse, we + scan the table to invalidate all entries using this register, but this + is too much work for us. */ + + for (i = regno; i < endregno; i++) + { + reg_last_set_label[i] = label_tick; + if (value && reg_last_set_table_tick[i] == label_tick) + reg_last_set_invalid[i] = 1; + else + reg_last_set_invalid[i] = 0; + } + + /* The value being assigned might refer to X (like in "x++;"). In that + case, we must replace it with (clobber (const_int 0)) to prevent + infinite loops. */ + if (value && ! get_last_value_validate (&value, + reg_last_set_label[regno], 0)) + { + value = copy_rtx (value); + if (! get_last_value_validate (&value, reg_last_set_label[regno], 1)) + value = 0; + } + + /* For the main register being modified, update the value, the mode, the + nonzero bits, and the number of sign bit copies. */ + + reg_last_set_value[regno] = value; + + if (value) + { + subst_low_cuid = INSN_CUID (insn); + reg_last_set_mode[regno] = GET_MODE (reg); + reg_last_set_nonzero_bits[regno] = nonzero_bits (value, GET_MODE (reg)); + reg_last_set_sign_bit_copies[regno] + = num_sign_bit_copies (value, GET_MODE (reg)); + } +} + +/* Used for communication between the following two routines. */ +static rtx record_dead_insn; + +/* Called via note_stores from record_dead_and_set_regs to handle one + SET or CLOBBER in an insn. */ + +static void +record_dead_and_set_regs_1 (dest, setter) + rtx dest, setter; +{ + if (GET_CODE (dest) == REG) + { + /* If we are setting the whole register, we know its value. Otherwise + show that we don't know the value. We can handle SUBREG in + some cases. */ + if (GET_CODE (setter) == SET && dest == SET_DEST (setter)) + record_value_for_reg (dest, record_dead_insn, SET_SRC (setter)); + else if (GET_CODE (setter) == SET + && GET_CODE (SET_DEST (setter)) == SUBREG + && SUBREG_REG (SET_DEST (setter)) == dest + && subreg_lowpart_p (SET_DEST (setter))) + record_value_for_reg (dest, record_dead_insn, + gen_lowpart_for_combine (GET_MODE (dest), + SET_SRC (setter))); + else + record_value_for_reg (dest, record_dead_insn, NULL_RTX); + } + else if (GET_CODE (dest) == MEM + /* Ignore pushes, they clobber nothing. */ + && ! push_operand (dest, GET_MODE (dest))) + mem_last_set = INSN_CUID (record_dead_insn); +} + +/* Update the records of when each REG was most recently set or killed + for the things done by INSN. This is the last thing done in processing + INSN in the combiner loop. + + We update reg_last_set, reg_last_set_value, reg_last_death, and also the + similar information mem_last_set (which insn most recently modified memory) + and last_call_cuid (which insn was the most recent subroutine call). */ + +static void +record_dead_and_set_regs (insn) + rtx insn; +{ + register rtx link; + int i; + + for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) + { + if (REG_NOTE_KIND (link) == REG_DEAD + && GET_CODE (XEXP (link, 0)) == REG) + { + int regno = REGNO (XEXP (link, 0)); + int endregno + = regno + (regno < FIRST_PSEUDO_REGISTER + ? HARD_REGNO_NREGS (regno, GET_MODE (XEXP (link, 0))) + : 1); + + for (i = regno; i < endregno; i++) + reg_last_death[i] = insn; + } + else if (REG_NOTE_KIND (link) == REG_INC) + record_value_for_reg (XEXP (link, 0), insn, NULL_RTX); + } + + if (GET_CODE (insn) == CALL_INSN) + { + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (call_used_regs[i]) + { + reg_last_set_value[i] = 0; + reg_last_death[i] = 0; + } + + last_call_cuid = mem_last_set = INSN_CUID (insn); + } + + record_dead_insn = insn; + note_stores (PATTERN (insn), record_dead_and_set_regs_1); +} + +/* Utility routine for the following function. Verify that all the registers + mentioned in *LOC are valid when *LOC was part of a value set when + label_tick == TICK. Return 0 if some are not. + + If REPLACE is non-zero, replace the invalid reference with + (clobber (const_int 0)) and return 1. This replacement is useful because + we often can get useful information about the form of a value (e.g., if + it was produced by a shift that always produces -1 or 0) even though + we don't know exactly what registers it was produced from. */ + +static int +get_last_value_validate (loc, tick, replace) + rtx *loc; + int tick; + int replace; +{ + rtx x = *loc; + char *fmt = GET_RTX_FORMAT (GET_CODE (x)); + int len = GET_RTX_LENGTH (GET_CODE (x)); + int i; + + if (GET_CODE (x) == REG) + { + int regno = REGNO (x); + int endregno = regno + (regno < FIRST_PSEUDO_REGISTER + ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1); + int j; + + for (j = regno; j < endregno; j++) + if (reg_last_set_invalid[j] + /* If this is a pseudo-register that was only set once, it is + always valid. */ + || (! (regno >= FIRST_PSEUDO_REGISTER && reg_n_sets[regno] == 1) + && reg_last_set_label[j] > tick)) + { + if (replace) + *loc = gen_rtx (CLOBBER, GET_MODE (x), const0_rtx); + return replace; + } + + return 1; + } + + for (i = 0; i < len; i++) + if ((fmt[i] == 'e' + && get_last_value_validate (&XEXP (x, i), tick, replace) == 0) + /* Don't bother with these. They shouldn't occur anyway. */ + || fmt[i] == 'E') + return 0; + + /* If we haven't found a reason for it to be invalid, it is valid. */ + return 1; +} + +/* Get the last value assigned to X, if known. Some registers + in the value may be replaced with (clobber (const_int 0)) if their value + is known longer known reliably. */ + +static rtx +get_last_value (x) + rtx x; +{ + int regno; + rtx value; + + /* If this is a non-paradoxical SUBREG, get the value of its operand and + then convert it to the desired mode. If this is a paradoxical SUBREG, + we cannot predict what values the "extra" bits might have. */ + if (GET_CODE (x) == SUBREG + && subreg_lowpart_p (x) + && (GET_MODE_SIZE (GET_MODE (x)) + <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) + && (value = get_last_value (SUBREG_REG (x))) != 0) + return gen_lowpart_for_combine (GET_MODE (x), value); + + if (GET_CODE (x) != REG) + return 0; + + regno = REGNO (x); + value = reg_last_set_value[regno]; + + /* If we don't have a value or if it isn't for this basic block, return 0. */ + + if (value == 0 + || (reg_n_sets[regno] != 1 + && reg_last_set_label[regno] != label_tick)) + return 0; + + /* If the value was set in a later insn that the ones we are processing, + we can't use it even if the register was only set once, but make a quick + check to see if the previous insn set it to something. This is commonly + the case when the same pseudo is used by repeated insns. */ + + if (INSN_CUID (reg_last_set[regno]) >= subst_low_cuid) + { + rtx insn, set; + + for (insn = prev_nonnote_insn (subst_insn); + insn && INSN_CUID (insn) >= subst_low_cuid; + insn = prev_nonnote_insn (insn)) + ; + + if (insn + && (set = single_set (insn)) != 0 + && rtx_equal_p (SET_DEST (set), x)) + { + value = SET_SRC (set); + + /* Make sure that VALUE doesn't reference X. Replace any + expliit references with a CLOBBER. If there are any remaining + references (rare), don't use the value. */ + + if (reg_mentioned_p (x, value)) + value = replace_rtx (copy_rtx (value), x, + gen_rtx (CLOBBER, GET_MODE (x), const0_rtx)); + + if (reg_overlap_mentioned_p (x, value)) + return 0; + } + else + return 0; + } + + /* If the value has all its registers valid, return it. */ + if (get_last_value_validate (&value, reg_last_set_label[regno], 0)) + return value; + + /* Otherwise, make a copy and replace any invalid register with + (clobber (const_int 0)). If that fails for some reason, return 0. */ + + value = copy_rtx (value); + if (get_last_value_validate (&value, reg_last_set_label[regno], 1)) + return value; + + return 0; +} + +/* Return nonzero if expression X refers to a REG or to memory + that is set in an instruction more recent than FROM_CUID. */ + +static int +use_crosses_set_p (x, from_cuid) + register rtx x; + int from_cuid; +{ + register char *fmt; + register int i; + register enum rtx_code code = GET_CODE (x); + + if (code == REG) + { + register int regno = REGNO (x); +#ifdef PUSH_ROUNDING + /* Don't allow uses of the stack pointer to be moved, + because we don't know whether the move crosses a push insn. */ + if (regno == STACK_POINTER_REGNUM) + return 1; +#endif + return (reg_last_set[regno] + && INSN_CUID (reg_last_set[regno]) > from_cuid); + } + + if (code == MEM && mem_last_set > from_cuid) + return 1; + + fmt = GET_RTX_FORMAT (code); + + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'E') + { + register int j; + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + if (use_crosses_set_p (XVECEXP (x, i, j), from_cuid)) + return 1; + } + else if (fmt[i] == 'e' + && use_crosses_set_p (XEXP (x, i), from_cuid)) + return 1; + } + return 0; +} + +/* Define three variables used for communication between the following + routines. */ + +static int reg_dead_regno, reg_dead_endregno; +static int reg_dead_flag; + +/* Function called via note_stores from reg_dead_at_p. + + If DEST is within [reg_dead_rengno, reg_dead_endregno), set + reg_dead_flag to 1 if X is a CLOBBER and to -1 it is a SET. */ + +static void +reg_dead_at_p_1 (dest, x) + rtx dest; + rtx x; +{ + int regno, endregno; + + if (GET_CODE (dest) != REG) + return; + + regno = REGNO (dest); + endregno = regno + (regno < FIRST_PSEUDO_REGISTER + ? HARD_REGNO_NREGS (regno, GET_MODE (dest)) : 1); + + if (reg_dead_endregno > regno && reg_dead_regno < endregno) + reg_dead_flag = (GET_CODE (x) == CLOBBER) ? 1 : -1; +} + +/* Return non-zero if REG is known to be dead at INSN. + + We scan backwards from INSN. If we hit a REG_DEAD note or a CLOBBER + referencing REG, it is dead. If we hit a SET referencing REG, it is + live. Otherwise, see if it is live or dead at the start of the basic + block we are in. */ + +static int +reg_dead_at_p (reg, insn) + rtx reg; + rtx insn; +{ + int block, i; + + /* Set variables for reg_dead_at_p_1. */ + reg_dead_regno = REGNO (reg); + reg_dead_endregno = reg_dead_regno + (reg_dead_regno < FIRST_PSEUDO_REGISTER + ? HARD_REGNO_NREGS (reg_dead_regno, + GET_MODE (reg)) + : 1); + + reg_dead_flag = 0; + + /* Scan backwards until we find a REG_DEAD note, SET, CLOBBER, label, or + beginning of function. */ + for (; insn && GET_CODE (insn) != CODE_LABEL; + insn = prev_nonnote_insn (insn)) + { + note_stores (PATTERN (insn), reg_dead_at_p_1); + if (reg_dead_flag) + return reg_dead_flag == 1 ? 1 : 0; + + if (find_regno_note (insn, REG_DEAD, reg_dead_regno)) + return 1; + } + + /* Get the basic block number that we were in. */ + if (insn == 0) + block = 0; + else + { + for (block = 0; block < n_basic_blocks; block++) + if (insn == basic_block_head[block]) + break; + + if (block == n_basic_blocks) + return 0; + } + + for (i = reg_dead_regno; i < reg_dead_endregno; i++) + if (basic_block_live_at_start[block][i / REGSET_ELT_BITS] + & ((REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS))) + return 0; + + return 1; +} + +/* Remove register number REGNO from the dead registers list of INSN. + + Return the note used to record the death, if there was one. */ + +rtx +remove_death (regno, insn) + int regno; + rtx insn; +{ + register rtx note = find_regno_note (insn, REG_DEAD, regno); + + if (note) + { + reg_n_deaths[regno]--; + remove_note (insn, note); + } + + return note; +} + +/* For each register (hardware or pseudo) used within expression X, if its + death is in an instruction with cuid between FROM_CUID (inclusive) and + TO_INSN (exclusive), put a REG_DEAD note for that register in the + list headed by PNOTES. + + This is done when X is being merged by combination into TO_INSN. These + notes will then be distributed as needed. */ + +static void +move_deaths (x, from_cuid, to_insn, pnotes) + rtx x; + int from_cuid; + rtx to_insn; + rtx *pnotes; +{ + register char *fmt; + register int len, i; + register enum rtx_code code = GET_CODE (x); + + if (code == REG) + { + register int regno = REGNO (x); + register rtx where_dead = reg_last_death[regno]; + + if (where_dead && INSN_CUID (where_dead) >= from_cuid + && INSN_CUID (where_dead) < INSN_CUID (to_insn)) + { + rtx note = remove_death (regno, where_dead); + + /* It is possible for the call above to return 0. This can occur + when reg_last_death points to I2 or I1 that we combined with. + In that case make a new note. + + We must also check for the case where X is a hard register + and NOTE is a death note for a range of hard registers + including X. In that case, we must put REG_DEAD notes for + the remaining registers in place of NOTE. */ + + if (note != 0 && regno < FIRST_PSEUDO_REGISTER + && (GET_MODE_SIZE (GET_MODE (XEXP (note, 0))) + != GET_MODE_SIZE (GET_MODE (x)))) + { + int deadregno = REGNO (XEXP (note, 0)); + int deadend + = (deadregno + HARD_REGNO_NREGS (deadregno, + GET_MODE (XEXP (note, 0)))); + int ourend = regno + HARD_REGNO_NREGS (regno, GET_MODE (x)); + int i; + + for (i = deadregno; i < deadend; i++) + if (i < regno || i >= ourend) + REG_NOTES (where_dead) + = gen_rtx (EXPR_LIST, REG_DEAD, + gen_rtx (REG, word_mode, i), + REG_NOTES (where_dead)); + } + + if (note != 0 && GET_MODE (XEXP (note, 0)) == GET_MODE (x)) + { + XEXP (note, 1) = *pnotes; + *pnotes = note; + } + else + *pnotes = gen_rtx (EXPR_LIST, REG_DEAD, x, *pnotes); + + reg_n_deaths[regno]++; + } + + return; + } + + else if (GET_CODE (x) == SET) + { + rtx dest = SET_DEST (x); + + move_deaths (SET_SRC (x), from_cuid, to_insn, pnotes); + + /* In the case of a ZERO_EXTRACT, a STRICT_LOW_PART, or a SUBREG + that accesses one word of a multi-word item, some + piece of everything register in the expression is used by + this insn, so remove any old death. */ + + if (GET_CODE (dest) == ZERO_EXTRACT + || GET_CODE (dest) == STRICT_LOW_PART + || (GET_CODE (dest) == SUBREG + && (((GET_MODE_SIZE (GET_MODE (dest)) + + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) + + UNITS_PER_WORD - 1) / UNITS_PER_WORD)))) + { + move_deaths (dest, from_cuid, to_insn, pnotes); + return; + } + + /* If this is some other SUBREG, we know it replaces the entire + value, so use that as the destination. */ + if (GET_CODE (dest) == SUBREG) + dest = SUBREG_REG (dest); + + /* If this is a MEM, adjust deaths of anything used in the address. + For a REG (the only other possibility), the entire value is + being replaced so the old value is not used in this insn. */ + + if (GET_CODE (dest) == MEM) + move_deaths (XEXP (dest, 0), from_cuid, to_insn, pnotes); + return; + } + + else if (GET_CODE (x) == CLOBBER) + return; + + len = GET_RTX_LENGTH (code); + fmt = GET_RTX_FORMAT (code); + + for (i = 0; i < len; i++) + { + if (fmt[i] == 'E') + { + register int j; + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + move_deaths (XVECEXP (x, i, j), from_cuid, to_insn, pnotes); + } + else if (fmt[i] == 'e') + move_deaths (XEXP (x, i), from_cuid, to_insn, pnotes); + } +} + +/* Return 1 if X is the target of a bit-field assignment in BODY, the + pattern of an insn. X must be a REG. */ + +static int +reg_bitfield_target_p (x, body) + rtx x; + rtx body; +{ + int i; + + if (GET_CODE (body) == SET) + { + rtx dest = SET_DEST (body); + rtx target; + int regno, tregno, endregno, endtregno; + + if (GET_CODE (dest) == ZERO_EXTRACT) + target = XEXP (dest, 0); + else if (GET_CODE (dest) == STRICT_LOW_PART) + target = SUBREG_REG (XEXP (dest, 0)); + else + return 0; + + if (GET_CODE (target) == SUBREG) + target = SUBREG_REG (target); + + if (GET_CODE (target) != REG) + return 0; + + tregno = REGNO (target), regno = REGNO (x); + if (tregno >= FIRST_PSEUDO_REGISTER || regno >= FIRST_PSEUDO_REGISTER) + return target == x; + + endtregno = tregno + HARD_REGNO_NREGS (tregno, GET_MODE (target)); + endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x)); + + return endregno > tregno && regno < endtregno; + } + + else if (GET_CODE (body) == PARALLEL) + for (i = XVECLEN (body, 0) - 1; i >= 0; i--) + if (reg_bitfield_target_p (x, XVECEXP (body, 0, i))) + return 1; + + return 0; +} + +/* Given a chain of REG_NOTES originally from FROM_INSN, try to place them + as appropriate. I3 and I2 are the insns resulting from the combination + insns including FROM (I2 may be zero). + + ELIM_I2 and ELIM_I1 are either zero or registers that we know will + not need REG_DEAD notes because they are being substituted for. This + saves searching in the most common cases. + + Each note in the list is either ignored or placed on some insns, depending + on the type of note. */ + +static void +distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1) + rtx notes; + rtx from_insn; + rtx i3, i2; + rtx elim_i2, elim_i1; +{ + rtx note, next_note; + rtx tem; + + for (note = notes; note; note = next_note) + { + rtx place = 0, place2 = 0; + + /* If this NOTE references a pseudo register, ensure it references + the latest copy of that register. */ + if (XEXP (note, 0) && GET_CODE (XEXP (note, 0)) == REG + && REGNO (XEXP (note, 0)) >= FIRST_PSEUDO_REGISTER) + XEXP (note, 0) = regno_reg_rtx[REGNO (XEXP (note, 0))]; + + next_note = XEXP (note, 1); + switch (REG_NOTE_KIND (note)) + { + case REG_UNUSED: + /* If this register is set or clobbered in I3, put the note there + unless there is one already. */ + if (reg_set_p (XEXP (note, 0), PATTERN (i3))) + { + if (! (GET_CODE (XEXP (note, 0)) == REG + ? find_regno_note (i3, REG_UNUSED, REGNO (XEXP (note, 0))) + : find_reg_note (i3, REG_UNUSED, XEXP (note, 0)))) + place = i3; + } + /* Otherwise, if this register is used by I3, then this register + now dies here, so we must put a REG_DEAD note here unless there + is one already. */ + else if (reg_referenced_p (XEXP (note, 0), PATTERN (i3)) + && ! (GET_CODE (XEXP (note, 0)) == REG + ? find_regno_note (i3, REG_DEAD, REGNO (XEXP (note, 0))) + : find_reg_note (i3, REG_DEAD, XEXP (note, 0)))) + { + PUT_REG_NOTE_KIND (note, REG_DEAD); + place = i3; + } + break; + + case REG_EQUAL: + case REG_EQUIV: + case REG_NONNEG: + /* These notes say something about results of an insn. We can + only support them if they used to be on I3 in which case they + remain on I3. Otherwise they are ignored. + + If the note refers to an expression that is not a constant, we + must also ignore the note since we cannot tell whether the + equivalence is still true. It might be possible to do + slightly better than this (we only have a problem if I2DEST + or I1DEST is present in the expression), but it doesn't + seem worth the trouble. */ + + if (from_insn == i3 + && (XEXP (note, 0) == 0 || CONSTANT_P (XEXP (note, 0)))) + place = i3; + break; + + case REG_INC: + case REG_NO_CONFLICT: + case REG_LABEL: + /* These notes say something about how a register is used. They must + be present on any use of the register in I2 or I3. */ + if (reg_mentioned_p (XEXP (note, 0), PATTERN (i3))) + place = i3; + + if (i2 && reg_mentioned_p (XEXP (note, 0), PATTERN (i2))) + { + if (place) + place2 = i2; + else + place = i2; + } + break; + + case REG_WAS_0: + /* It is too much trouble to try to see if this note is still + correct in all situations. It is better to simply delete it. */ + break; + + case REG_RETVAL: + /* If the insn previously containing this note still exists, + put it back where it was. Otherwise move it to the previous + insn. Adjust the corresponding REG_LIBCALL note. */ + if (GET_CODE (from_insn) != NOTE) + place = from_insn; + else + { + tem = find_reg_note (XEXP (note, 0), REG_LIBCALL, NULL_RTX); + place = prev_real_insn (from_insn); + if (tem && place) + XEXP (tem, 0) = place; + } + break; + + case REG_LIBCALL: + /* This is handled similarly to REG_RETVAL. */ + if (GET_CODE (from_insn) != NOTE) + place = from_insn; + else + { + tem = find_reg_note (XEXP (note, 0), REG_RETVAL, NULL_RTX); + place = next_real_insn (from_insn); + if (tem && place) + XEXP (tem, 0) = place; + } + break; + + case REG_DEAD: + /* If the register is used as an input in I3, it dies there. + Similarly for I2, if it is non-zero and adjacent to I3. + + If the register is not used as an input in either I3 or I2 + and it is not one of the registers we were supposed to eliminate, + there are two possibilities. We might have a non-adjacent I2 + or we might have somehow eliminated an additional register + from a computation. For example, we might have had A & B where + we discover that B will always be zero. In this case we will + eliminate the reference to A. + + In both cases, we must search to see if we can find a previous + use of A and put the death note there. */ + + if (reg_referenced_p (XEXP (note, 0), PATTERN (i3))) + place = i3; + else if (i2 != 0 && next_nonnote_insn (i2) == i3 + && reg_referenced_p (XEXP (note, 0), PATTERN (i2))) + place = i2; + + if (XEXP (note, 0) == elim_i2 || XEXP (note, 0) == elim_i1) + break; + + /* If the register is used in both I2 and I3 and it dies in I3, + we might have added another reference to it. If reg_n_refs + was 2, bump it to 3. This has to be correct since the + register must have been set somewhere. The reason this is + done is because local-alloc.c treats 2 references as a + special case. */ + + if (place == i3 && i2 != 0 && GET_CODE (XEXP (note, 0)) == REG + && reg_n_refs[REGNO (XEXP (note, 0))]== 2 + && reg_referenced_p (XEXP (note, 0), PATTERN (i2))) + reg_n_refs[REGNO (XEXP (note, 0))] = 3; + + if (place == 0) + for (tem = prev_nonnote_insn (i3); + tem && (GET_CODE (tem) == INSN + || GET_CODE (tem) == CALL_INSN); + tem = prev_nonnote_insn (tem)) + { + /* If the register is being set at TEM, see if that is all + TEM is doing. If so, delete TEM. Otherwise, make this + into a REG_UNUSED note instead. */ + if (reg_set_p (XEXP (note, 0), PATTERN (tem))) + { + rtx set = single_set (tem); + + /* Verify that it was the set, and not a clobber that + modified the register. */ + + if (set != 0 && ! side_effects_p (SET_SRC (set)) + && rtx_equal_p (XEXP (note, 0), SET_DEST (set))) + { + /* Move the notes and links of TEM elsewhere. + This might delete other dead insns recursively. + First set the pattern to something that won't use + any register. */ + + PATTERN (tem) = pc_rtx; + + distribute_notes (REG_NOTES (tem), tem, tem, + NULL_RTX, NULL_RTX, NULL_RTX); + distribute_links (LOG_LINKS (tem)); + + PUT_CODE (tem, NOTE); + NOTE_LINE_NUMBER (tem) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (tem) = 0; + } + else + { + PUT_REG_NOTE_KIND (note, REG_UNUSED); + + /* If there isn't already a REG_UNUSED note, put one + here. */ + if (! find_regno_note (tem, REG_UNUSED, + REGNO (XEXP (note, 0)))) + place = tem; + break; + } + } + else if (reg_referenced_p (XEXP (note, 0), PATTERN (tem))) + { + place = tem; + break; + } + } + + /* If the register is set or already dead at PLACE, we needn't do + anything with this note if it is still a REG_DEAD note. + + Note that we cannot use just `dead_or_set_p' here since we can + convert an assignment to a register into a bit-field assignment. + Therefore, we must also omit the note if the register is the + target of a bitfield assignment. */ + + if (place && REG_NOTE_KIND (note) == REG_DEAD) + { + int regno = REGNO (XEXP (note, 0)); + + if (dead_or_set_p (place, XEXP (note, 0)) + || reg_bitfield_target_p (XEXP (note, 0), PATTERN (place))) + { + /* Unless the register previously died in PLACE, clear + reg_last_death. [I no longer understand why this is + being done.] */ + if (reg_last_death[regno] != place) + reg_last_death[regno] = 0; + place = 0; + } + else + reg_last_death[regno] = place; + + /* If this is a death note for a hard reg that is occupying + multiple registers, ensure that we are still using all + parts of the object. If we find a piece of the object + that is unused, we must add a USE for that piece before + PLACE and put the appropriate REG_DEAD note on it. + + An alternative would be to put a REG_UNUSED for the pieces + on the insn that set the register, but that can't be done if + it is not in the same block. It is simpler, though less + efficient, to add the USE insns. */ + + if (place && regno < FIRST_PSEUDO_REGISTER + && HARD_REGNO_NREGS (regno, GET_MODE (XEXP (note, 0))) > 1) + { + int endregno + = regno + HARD_REGNO_NREGS (regno, + GET_MODE (XEXP (note, 0))); + int all_used = 1; + int i; + + for (i = regno; i < endregno; i++) + if (! refers_to_regno_p (i, i + 1, PATTERN (place), 0)) + { + rtx piece = gen_rtx (REG, word_mode, i); + rtx p; + + /* See if we already placed a USE note for this + register in front of PLACE. */ + for (p = place; + GET_CODE (PREV_INSN (p)) == INSN + && GET_CODE (PATTERN (PREV_INSN (p))) == USE; + p = PREV_INSN (p)) + if (rtx_equal_p (piece, + XEXP (PATTERN (PREV_INSN (p)), 0))) + { + p = 0; + break; + } + + if (p) + { + rtx use_insn + = emit_insn_before (gen_rtx (USE, VOIDmode, + piece), + p); + REG_NOTES (use_insn) + = gen_rtx (EXPR_LIST, REG_DEAD, piece, + REG_NOTES (use_insn)); + } + + all_used = 0; + } + + /* Check for the case where the register dying partially + overlaps the register set by this insn. */ + if (all_used) + for (i = regno; i < endregno; i++) + if (dead_or_set_regno_p (place, i)) + { + all_used = 0; + break; + } + + if (! all_used) + { + /* Put only REG_DEAD notes for pieces that are + still used and that are not already dead or set. */ + + for (i = regno; i < endregno; i++) + { + rtx piece = gen_rtx (REG, word_mode, i); + + if (reg_referenced_p (piece, PATTERN (place)) + && ! dead_or_set_p (place, piece) + && ! reg_bitfield_target_p (piece, + PATTERN (place))) + REG_NOTES (place) = gen_rtx (EXPR_LIST, REG_DEAD, + piece, + REG_NOTES (place)); + } + + place = 0; + } + } + } + break; + + default: + /* Any other notes should not be present at this point in the + compilation. */ + abort (); + } + + if (place) + { + XEXP (note, 1) = REG_NOTES (place); + REG_NOTES (place) = note; + } + else if ((REG_NOTE_KIND (note) == REG_DEAD + || REG_NOTE_KIND (note) == REG_UNUSED) + && GET_CODE (XEXP (note, 0)) == REG) + reg_n_deaths[REGNO (XEXP (note, 0))]--; + + if (place2) + { + if ((REG_NOTE_KIND (note) == REG_DEAD + || REG_NOTE_KIND (note) == REG_UNUSED) + && GET_CODE (XEXP (note, 0)) == REG) + reg_n_deaths[REGNO (XEXP (note, 0))]++; + + REG_NOTES (place2) = gen_rtx (GET_CODE (note), REG_NOTE_KIND (note), + XEXP (note, 0), REG_NOTES (place2)); + } + } +} + +/* Similarly to above, distribute the LOG_LINKS that used to be present on + I3, I2, and I1 to new locations. This is also called in one case to + add a link pointing at I3 when I3's destination is changed. */ + +static void +distribute_links (links) + rtx links; +{ + rtx link, next_link; + + for (link = links; link; link = next_link) + { + rtx place = 0; + rtx insn; + rtx set, reg; + + next_link = XEXP (link, 1); + + /* If the insn that this link points to is a NOTE or isn't a single + set, ignore it. In the latter case, it isn't clear what we + can do other than ignore the link, since we can't tell which + register it was for. Such links wouldn't be used by combine + anyway. + + It is not possible for the destination of the target of the link to + have been changed by combine. The only potential of this is if we + replace I3, I2, and I1 by I3 and I2. But in that case the + destination of I2 also remains unchanged. */ + + if (GET_CODE (XEXP (link, 0)) == NOTE + || (set = single_set (XEXP (link, 0))) == 0) + continue; + + reg = SET_DEST (set); + while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == ZERO_EXTRACT + || GET_CODE (reg) == SIGN_EXTRACT + || GET_CODE (reg) == STRICT_LOW_PART) + reg = XEXP (reg, 0); + + /* A LOG_LINK is defined as being placed on the first insn that uses + a register and points to the insn that sets the register. Start + searching at the next insn after the target of the link and stop + when we reach a set of the register or the end of the basic block. + + Note that this correctly handles the link that used to point from + I3 to I2. Also note that not much searching is typically done here + since most links don't point very far away. */ + + for (insn = NEXT_INSN (XEXP (link, 0)); + (insn && GET_CODE (insn) != CODE_LABEL + && GET_CODE (PREV_INSN (insn)) != JUMP_INSN); + insn = NEXT_INSN (insn)) + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && reg_overlap_mentioned_p (reg, PATTERN (insn))) + { + if (reg_referenced_p (reg, PATTERN (insn))) + place = insn; + break; + } + + /* If we found a place to put the link, place it there unless there + is already a link to the same insn as LINK at that point. */ + + if (place) + { + rtx link2; + + for (link2 = LOG_LINKS (place); link2; link2 = XEXP (link2, 1)) + if (XEXP (link2, 0) == XEXP (link, 0)) + break; + + if (link2 == 0) + { + XEXP (link, 1) = LOG_LINKS (place); + LOG_LINKS (place) = link; + } + } + } +} + +void +dump_combine_stats (file) + FILE *file; +{ + fprintf + (file, + ";; Combiner statistics: %d attempts, %d substitutions (%d requiring new space),\n;; %d successes.\n\n", + combine_attempts, combine_merges, combine_extras, combine_successes); +} + +void +dump_combine_total_stats (file) + FILE *file; +{ + fprintf + (file, + "\n;; Combiner totals: %d attempts, %d substitutions (%d requiring new space),\n;; %d successes.\n", + total_attempts, total_merges, total_extras, total_successes); +} diff --git a/gnu/usr.bin/cc/common/conditions.h b/gnu/usr.bin/cc/lib/conditions.h similarity index 100% rename from gnu/usr.bin/cc/common/conditions.h rename to gnu/usr.bin/cc/lib/conditions.h diff --git a/gnu/usr.bin/cc/common/config.h b/gnu/usr.bin/cc/lib/config.h similarity index 100% rename from gnu/usr.bin/cc/common/config.h rename to gnu/usr.bin/cc/lib/config.h diff --git a/gnu/usr.bin/cc/common/convert.c b/gnu/usr.bin/cc/lib/convert.c similarity index 100% rename from gnu/usr.bin/cc/common/convert.c rename to gnu/usr.bin/cc/lib/convert.c diff --git a/gnu/usr.bin/cc/common/convert.h b/gnu/usr.bin/cc/lib/convert.h similarity index 100% rename from gnu/usr.bin/cc/common/convert.h rename to gnu/usr.bin/cc/lib/convert.h diff --git a/gnu/usr.bin/cc/lib/cse.c b/gnu/usr.bin/cc/lib/cse.c new file mode 100644 index 0000000000..792fa7b2f5 --- /dev/null +++ b/gnu/usr.bin/cc/lib/cse.c @@ -0,0 +1,8243 @@ +/* Common subexpression elimination for GNU compiler. + Copyright (C) 1987, 1988, 1989, 1992, 1993 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include "config.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "flags.h" +#include "real.h" +#include "insn-config.h" +#include "recog.h" + +#include +#include + +/* The basic idea of common subexpression elimination is to go + through the code, keeping a record of expressions that would + have the same value at the current scan point, and replacing + expressions encountered with the cheapest equivalent expression. + + It is too complicated to keep track of the different possibilities + when control paths merge; so, at each label, we forget all that is + known and start fresh. This can be described as processing each + basic block separately. Note, however, that these are not quite + the same as the basic blocks found by a later pass and used for + data flow analysis and register packing. We do not need to start fresh + after a conditional jump instruction if there is no label there. + + We use two data structures to record the equivalent expressions: + a hash table for most expressions, and several vectors together + with "quantity numbers" to record equivalent (pseudo) registers. + + The use of the special data structure for registers is desirable + because it is faster. It is possible because registers references + contain a fairly small number, the register number, taken from + a contiguously allocated series, and two register references are + identical if they have the same number. General expressions + do not have any such thing, so the only way to retrieve the + information recorded on an expression other than a register + is to keep it in a hash table. + +Registers and "quantity numbers": + + At the start of each basic block, all of the (hardware and pseudo) + registers used in the function are given distinct quantity + numbers to indicate their contents. During scan, when the code + copies one register into another, we copy the quantity number. + When a register is loaded in any other way, we allocate a new + quantity number to describe the value generated by this operation. + `reg_qty' records what quantity a register is currently thought + of as containing. + + All real quantity numbers are greater than or equal to `max_reg'. + If register N has not been assigned a quantity, reg_qty[N] will equal N. + + Quantity numbers below `max_reg' do not exist and none of the `qty_...' + variables should be referenced with an index below `max_reg'. + + We also maintain a bidirectional chain of registers for each + quantity number. `qty_first_reg', `qty_last_reg', + `reg_next_eqv' and `reg_prev_eqv' hold these chains. + + The first register in a chain is the one whose lifespan is least local. + Among equals, it is the one that was seen first. + We replace any equivalent register with that one. + + If two registers have the same quantity number, it must be true that + REG expressions with `qty_mode' must be in the hash table for both + registers and must be in the same class. + + The converse is not true. Since hard registers may be referenced in + any mode, two REG expressions might be equivalent in the hash table + but not have the same quantity number if the quantity number of one + of the registers is not the same mode as those expressions. + +Constants and quantity numbers + + When a quantity has a known constant value, that value is stored + in the appropriate element of qty_const. This is in addition to + putting the constant in the hash table as is usual for non-regs. + + Whether a reg or a constant is preferred is determined by the configuration + macro CONST_COSTS and will often depend on the constant value. In any + event, expressions containing constants can be simplified, by fold_rtx. + + When a quantity has a known nearly constant value (such as an address + of a stack slot), that value is stored in the appropriate element + of qty_const. + + Integer constants don't have a machine mode. However, cse + determines the intended machine mode from the destination + of the instruction that moves the constant. The machine mode + is recorded in the hash table along with the actual RTL + constant expression so that different modes are kept separate. + +Other expressions: + + To record known equivalences among expressions in general + we use a hash table called `table'. It has a fixed number of buckets + that contain chains of `struct table_elt' elements for expressions. + These chains connect the elements whose expressions have the same + hash codes. + + Other chains through the same elements connect the elements which + currently have equivalent values. + + Register references in an expression are canonicalized before hashing + the expression. This is done using `reg_qty' and `qty_first_reg'. + The hash code of a register reference is computed using the quantity + number, not the register number. + + When the value of an expression changes, it is necessary to remove from the + hash table not just that expression but all expressions whose values + could be different as a result. + + 1. If the value changing is in memory, except in special cases + ANYTHING referring to memory could be changed. That is because + nobody knows where a pointer does not point. + The function `invalidate_memory' removes what is necessary. + + The special cases are when the address is constant or is + a constant plus a fixed register such as the frame pointer + or a static chain pointer. When such addresses are stored in, + we can tell exactly which other such addresses must be invalidated + due to overlap. `invalidate' does this. + All expressions that refer to non-constant + memory addresses are also invalidated. `invalidate_memory' does this. + + 2. If the value changing is a register, all expressions + containing references to that register, and only those, + must be removed. + + Because searching the entire hash table for expressions that contain + a register is very slow, we try to figure out when it isn't necessary. + Precisely, this is necessary only when expressions have been + entered in the hash table using this register, and then the value has + changed, and then another expression wants to be added to refer to + the register's new value. This sequence of circumstances is rare + within any one basic block. + + The vectors `reg_tick' and `reg_in_table' are used to detect this case. + reg_tick[i] is incremented whenever a value is stored in register i. + reg_in_table[i] holds -1 if no references to register i have been + entered in the table; otherwise, it contains the value reg_tick[i] had + when the references were entered. If we want to enter a reference + and reg_in_table[i] != reg_tick[i], we must scan and remove old references. + Until we want to enter a new entry, the mere fact that the two vectors + don't match makes the entries be ignored if anyone tries to match them. + + Registers themselves are entered in the hash table as well as in + the equivalent-register chains. However, the vectors `reg_tick' + and `reg_in_table' do not apply to expressions which are simple + register references. These expressions are removed from the table + immediately when they become invalid, and this can be done even if + we do not immediately search for all the expressions that refer to + the register. + + A CLOBBER rtx in an instruction invalidates its operand for further + reuse. A CLOBBER or SET rtx whose operand is a MEM:BLK + invalidates everything that resides in memory. + +Related expressions: + + Constant expressions that differ only by an additive integer + are called related. When a constant expression is put in + the table, the related expression with no constant term + is also entered. These are made to point at each other + so that it is possible to find out if there exists any + register equivalent to an expression related to a given expression. */ + +/* One plus largest register number used in this function. */ + +static int max_reg; + +/* Length of vectors indexed by quantity number. + We know in advance we will not need a quantity number this big. */ + +static int max_qty; + +/* Next quantity number to be allocated. + This is 1 + the largest number needed so far. */ + +static int next_qty; + +/* Indexed by quantity number, gives the first (or last) (pseudo) register + in the chain of registers that currently contain this quantity. */ + +static int *qty_first_reg; +static int *qty_last_reg; + +/* Index by quantity number, gives the mode of the quantity. */ + +static enum machine_mode *qty_mode; + +/* Indexed by quantity number, gives the rtx of the constant value of the + quantity, or zero if it does not have a known value. + A sum of the frame pointer (or arg pointer) plus a constant + can also be entered here. */ + +static rtx *qty_const; + +/* Indexed by qty number, gives the insn that stored the constant value + recorded in `qty_const'. */ + +static rtx *qty_const_insn; + +/* The next three variables are used to track when a comparison between a + quantity and some constant or register has been passed. In that case, we + know the results of the comparison in case we see it again. These variables + record a comparison that is known to be true. */ + +/* Indexed by qty number, gives the rtx code of a comparison with a known + result involving this quantity. If none, it is UNKNOWN. */ +static enum rtx_code *qty_comparison_code; + +/* Indexed by qty number, gives the constant being compared against in a + comparison of known result. If no such comparison, it is undefined. + If the comparison is not with a constant, it is zero. */ + +static rtx *qty_comparison_const; + +/* Indexed by qty number, gives the quantity being compared against in a + comparison of known result. If no such comparison, if it undefined. + If the comparison is not with a register, it is -1. */ + +static int *qty_comparison_qty; + +#ifdef HAVE_cc0 +/* For machines that have a CC0, we do not record its value in the hash + table since its use is guaranteed to be the insn immediately following + its definition and any other insn is presumed to invalidate it. + + Instead, we store below the value last assigned to CC0. If it should + happen to be a constant, it is stored in preference to the actual + assigned value. In case it is a constant, we store the mode in which + the constant should be interpreted. */ + +static rtx prev_insn_cc0; +static enum machine_mode prev_insn_cc0_mode; +#endif + +/* Previous actual insn. 0 if at first insn of basic block. */ + +static rtx prev_insn; + +/* Insn being scanned. */ + +static rtx this_insn; + +/* Index by (pseudo) register number, gives the quantity number + of the register's current contents. */ + +static int *reg_qty; + +/* Index by (pseudo) register number, gives the number of the next (or + previous) (pseudo) register in the chain of registers sharing the same + value. + + Or -1 if this register is at the end of the chain. + + If reg_qty[N] == N, reg_next_eqv[N] is undefined. */ + +static int *reg_next_eqv; +static int *reg_prev_eqv; + +/* Index by (pseudo) register number, gives the number of times + that register has been altered in the current basic block. */ + +static int *reg_tick; + +/* Index by (pseudo) register number, gives the reg_tick value at which + rtx's containing this register are valid in the hash table. + If this does not equal the current reg_tick value, such expressions + existing in the hash table are invalid. + If this is -1, no expressions containing this register have been + entered in the table. */ + +static int *reg_in_table; + +/* A HARD_REG_SET containing all the hard registers for which there is + currently a REG expression in the hash table. Note the difference + from the above variables, which indicate if the REG is mentioned in some + expression in the table. */ + +static HARD_REG_SET hard_regs_in_table; + +/* A HARD_REG_SET containing all the hard registers that are invalidated + by a CALL_INSN. */ + +static HARD_REG_SET regs_invalidated_by_call; + +/* Two vectors of ints: + one containing max_reg -1's; the other max_reg + 500 (an approximation + for max_qty) elements where element i contains i. + These are used to initialize various other vectors fast. */ + +static int *all_minus_one; +static int *consec_ints; + +/* CUID of insn that starts the basic block currently being cse-processed. */ + +static int cse_basic_block_start; + +/* CUID of insn that ends the basic block currently being cse-processed. */ + +static int cse_basic_block_end; + +/* Vector mapping INSN_UIDs to cuids. + The cuids are like uids but increase monotonically always. + We use them to see whether a reg is used outside a given basic block. */ + +static int *uid_cuid; + +/* Highest UID in UID_CUID. */ +static int max_uid; + +/* Get the cuid of an insn. */ + +#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)]) + +/* Nonzero if cse has altered conditional jump insns + in such a way that jump optimization should be redone. */ + +static int cse_jumps_altered; + +/* canon_hash stores 1 in do_not_record + if it notices a reference to CC0, PC, or some other volatile + subexpression. */ + +static int do_not_record; + +/* canon_hash stores 1 in hash_arg_in_memory + if it notices a reference to memory within the expression being hashed. */ + +static int hash_arg_in_memory; + +/* canon_hash stores 1 in hash_arg_in_struct + if it notices a reference to memory that's part of a structure. */ + +static int hash_arg_in_struct; + +/* The hash table contains buckets which are chains of `struct table_elt's, + each recording one expression's information. + That expression is in the `exp' field. + + Those elements with the same hash code are chained in both directions + through the `next_same_hash' and `prev_same_hash' fields. + + Each set of expressions with equivalent values + are on a two-way chain through the `next_same_value' + and `prev_same_value' fields, and all point with + the `first_same_value' field at the first element in + that chain. The chain is in order of increasing cost. + Each element's cost value is in its `cost' field. + + The `in_memory' field is nonzero for elements that + involve any reference to memory. These elements are removed + whenever a write is done to an unidentified location in memory. + To be safe, we assume that a memory address is unidentified unless + the address is either a symbol constant or a constant plus + the frame pointer or argument pointer. + + The `in_struct' field is nonzero for elements that + involve any reference to memory inside a structure or array. + + The `related_value' field is used to connect related expressions + (that differ by adding an integer). + The related expressions are chained in a circular fashion. + `related_value' is zero for expressions for which this + chain is not useful. + + The `cost' field stores the cost of this element's expression. + + The `is_const' flag is set if the element is a constant (including + a fixed address). + + The `flag' field is used as a temporary during some search routines. + + The `mode' field is usually the same as GET_MODE (`exp'), but + if `exp' is a CONST_INT and has no machine mode then the `mode' + field is the mode it was being used as. Each constant is + recorded separately for each mode it is used with. */ + + +struct table_elt +{ + rtx exp; + struct table_elt *next_same_hash; + struct table_elt *prev_same_hash; + struct table_elt *next_same_value; + struct table_elt *prev_same_value; + struct table_elt *first_same_value; + struct table_elt *related_value; + int cost; + enum machine_mode mode; + char in_memory; + char in_struct; + char is_const; + char flag; +}; + +#define HASHBITS 16 + +/* We don't want a lot of buckets, because we rarely have very many + things stored in the hash table, and a lot of buckets slows + down a lot of loops that happen frequently. */ +#define NBUCKETS 31 + +/* Compute hash code of X in mode M. Special-case case where X is a pseudo + register (hard registers may require `do_not_record' to be set). */ + +#define HASH(X, M) \ + (GET_CODE (X) == REG && REGNO (X) >= FIRST_PSEUDO_REGISTER \ + ? ((((int) REG << 7) + reg_qty[REGNO (X)]) % NBUCKETS) \ + : canon_hash (X, M) % NBUCKETS) + +/* Determine whether register number N is considered a fixed register for CSE. + It is desirable to replace other regs with fixed regs, to reduce need for + non-fixed hard regs. + A reg wins if it is either the frame pointer or designated as fixed, + but not if it is an overlapping register. */ +#ifdef OVERLAPPING_REGNO_P +#define FIXED_REGNO_P(N) \ + (((N) == FRAME_POINTER_REGNUM || fixed_regs[N]) \ + && ! OVERLAPPING_REGNO_P ((N))) +#else +#define FIXED_REGNO_P(N) \ + ((N) == FRAME_POINTER_REGNUM || fixed_regs[N]) +#endif + +/* Compute cost of X, as stored in the `cost' field of a table_elt. Fixed + hard registers and pointers into the frame are the cheapest with a cost + of 0. Next come pseudos with a cost of one and other hard registers with + a cost of 2. Aside from these special cases, call `rtx_cost'. */ + +#define CHEAP_REG(N) \ + ((N) == FRAME_POINTER_REGNUM || (N) == STACK_POINTER_REGNUM \ + || (N) == ARG_POINTER_REGNUM \ + || ((N) >= FIRST_VIRTUAL_REGISTER && (N) <= LAST_VIRTUAL_REGISTER) \ + || ((N) < FIRST_PSEUDO_REGISTER \ + && FIXED_REGNO_P (N) && REGNO_REG_CLASS (N) != NO_REGS)) + +#define COST(X) \ + (GET_CODE (X) == REG \ + ? (CHEAP_REG (REGNO (X)) ? 0 \ + : REGNO (X) >= FIRST_PSEUDO_REGISTER ? 1 \ + : 2) \ + : rtx_cost (X, SET) * 2) + +/* Determine if the quantity number for register X represents a valid index + into the `qty_...' variables. */ + +#define REGNO_QTY_VALID_P(N) (reg_qty[N] != (N)) + +static struct table_elt *table[NBUCKETS]; + +/* Chain of `struct table_elt's made so far for this function + but currently removed from the table. */ + +static struct table_elt *free_element_chain; + +/* Number of `struct table_elt' structures made so far for this function. */ + +static int n_elements_made; + +/* Maximum value `n_elements_made' has had so far in this compilation + for functions previously processed. */ + +static int max_elements_made; + +/* Surviving equivalence class when two equivalence classes are merged + by recording the effects of a jump in the last insn. Zero if the + last insn was not a conditional jump. */ + +static struct table_elt *last_jump_equiv_class; + +/* Set to the cost of a constant pool reference if one was found for a + symbolic constant. If this was found, it means we should try to + convert constants into constant pool entries if they don't fit in + the insn. */ + +static int constant_pool_entries_cost; + +/* Bits describing what kind of values in memory must be invalidated + for a particular instruction. If all three bits are zero, + no memory refs need to be invalidated. Each bit is more powerful + than the preceding ones, and if a bit is set then the preceding + bits are also set. + + Here is how the bits are set: + Pushing onto the stack invalidates only the stack pointer, + writing at a fixed address invalidates only variable addresses, + writing in a structure element at variable address + invalidates all but scalar variables, + and writing in anything else at variable address invalidates everything. */ + +struct write_data +{ + int sp : 1; /* Invalidate stack pointer. */ + int var : 1; /* Invalidate variable addresses. */ + int nonscalar : 1; /* Invalidate all but scalar variables. */ + int all : 1; /* Invalidate all memory refs. */ +}; + +/* Define maximum length of a branch path. */ + +#define PATHLENGTH 10 + +/* This data describes a block that will be processed by cse_basic_block. */ + +struct cse_basic_block_data { + /* Lowest CUID value of insns in block. */ + int low_cuid; + /* Highest CUID value of insns in block. */ + int high_cuid; + /* Total number of SETs in block. */ + int nsets; + /* Last insn in the block. */ + rtx last; + /* Size of current branch path, if any. */ + int path_size; + /* Current branch path, indicating which branches will be taken. */ + struct branch_path { + /* The branch insn. */ + rtx branch; + /* Whether it should be taken or not. AROUND is the same as taken + except that it is used when the destination label is not preceded + by a BARRIER. */ + enum taken {TAKEN, NOT_TAKEN, AROUND} status; + } path[PATHLENGTH]; +}; + +/* Nonzero if X has the form (PLUS frame-pointer integer). We check for + virtual regs here because the simplify_*_operation routines are called + by integrate.c, which is called before virtual register instantiation. */ + +#define FIXED_BASE_PLUS_P(X) \ + ((X) == frame_pointer_rtx || (X) == arg_pointer_rtx \ + || (X) == virtual_stack_vars_rtx \ + || (X) == virtual_incoming_args_rtx \ + || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \ + && (XEXP (X, 0) == frame_pointer_rtx \ + || XEXP (X, 0) == arg_pointer_rtx \ + || XEXP (X, 0) == virtual_stack_vars_rtx \ + || XEXP (X, 0) == virtual_incoming_args_rtx))) + +/* Similar, but also allows reference to the stack pointer. + + This used to include FIXED_BASE_PLUS_P, however, we can't assume that + arg_pointer_rtx by itself is nonzero, because on at least one machine, + the i960, the arg pointer is zero when it is unused. */ + +#define NONZERO_BASE_PLUS_P(X) \ + ((X) == frame_pointer_rtx \ + || (X) == virtual_stack_vars_rtx \ + || (X) == virtual_incoming_args_rtx \ + || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \ + && (XEXP (X, 0) == frame_pointer_rtx \ + || XEXP (X, 0) == arg_pointer_rtx \ + || XEXP (X, 0) == virtual_stack_vars_rtx \ + || XEXP (X, 0) == virtual_incoming_args_rtx)) \ + || (X) == stack_pointer_rtx \ + || (X) == virtual_stack_dynamic_rtx \ + || (X) == virtual_outgoing_args_rtx \ + || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \ + && (XEXP (X, 0) == stack_pointer_rtx \ + || XEXP (X, 0) == virtual_stack_dynamic_rtx \ + || XEXP (X, 0) == virtual_outgoing_args_rtx))) + +static void new_basic_block PROTO((void)); +static void make_new_qty PROTO((int)); +static void make_regs_eqv PROTO((int, int)); +static void delete_reg_equiv PROTO((int)); +static int mention_regs PROTO((rtx)); +static int insert_regs PROTO((rtx, struct table_elt *, int)); +static void free_element PROTO((struct table_elt *)); +static void remove_from_table PROTO((struct table_elt *, int)); +static struct table_elt *get_element PROTO((void)); +static struct table_elt *lookup PROTO((rtx, int, enum machine_mode)), + *lookup_for_remove PROTO((rtx, int, enum machine_mode)); +static rtx lookup_as_function PROTO((rtx, enum rtx_code)); +static struct table_elt *insert PROTO((rtx, struct table_elt *, int, + enum machine_mode)); +static void merge_equiv_classes PROTO((struct table_elt *, + struct table_elt *)); +static void invalidate PROTO((rtx)); +static void remove_invalid_refs PROTO((int)); +static void rehash_using_reg PROTO((rtx)); +static void invalidate_memory PROTO((struct write_data *)); +static void invalidate_for_call PROTO((void)); +static rtx use_related_value PROTO((rtx, struct table_elt *)); +static int canon_hash PROTO((rtx, enum machine_mode)); +static int safe_hash PROTO((rtx, enum machine_mode)); +static int exp_equiv_p PROTO((rtx, rtx, int, int)); +static void set_nonvarying_address_components PROTO((rtx, int, rtx *, + HOST_WIDE_INT *, + HOST_WIDE_INT *)); +static int refers_to_p PROTO((rtx, rtx)); +static int refers_to_mem_p PROTO((rtx, rtx, HOST_WIDE_INT, + HOST_WIDE_INT)); +static int cse_rtx_addr_varies_p PROTO((rtx)); +static rtx canon_reg PROTO((rtx, rtx)); +static void find_best_addr PROTO((rtx, rtx *)); +static enum rtx_code find_comparison_args PROTO((enum rtx_code, rtx *, rtx *, + enum machine_mode *, + enum machine_mode *)); +static rtx cse_gen_binary PROTO((enum rtx_code, enum machine_mode, + rtx, rtx)); +static rtx simplify_plus_minus PROTO((enum rtx_code, enum machine_mode, + rtx, rtx)); +static rtx fold_rtx PROTO((rtx, rtx)); +static rtx equiv_constant PROTO((rtx)); +static void record_jump_equiv PROTO((rtx, int)); +static void record_jump_cond PROTO((enum rtx_code, enum machine_mode, + rtx, rtx, int)); +static void cse_insn PROTO((rtx, int)); +static void note_mem_written PROTO((rtx, struct write_data *)); +static void invalidate_from_clobbers PROTO((struct write_data *, rtx)); +static rtx cse_process_notes PROTO((rtx, rtx)); +static void cse_around_loop PROTO((rtx)); +static void invalidate_skipped_set PROTO((rtx, rtx)); +static void invalidate_skipped_block PROTO((rtx)); +static void cse_check_loop_start PROTO((rtx, rtx)); +static void cse_set_around_loop PROTO((rtx, rtx, rtx)); +static rtx cse_basic_block PROTO((rtx, rtx, struct branch_path *, int)); +static void count_reg_usage PROTO((rtx, int *, int)); + +/* Return an estimate of the cost of computing rtx X. + One use is in cse, to decide which expression to keep in the hash table. + Another is in rtl generation, to pick the cheapest way to multiply. + Other uses like the latter are expected in the future. */ + +/* Return the right cost to give to an operation + to make the cost of the corresponding register-to-register instruction + N times that of a fast register-to-register instruction. */ + +#define COSTS_N_INSNS(N) ((N) * 4 - 2) + +int +rtx_cost (x, outer_code) + rtx x; + enum rtx_code outer_code; +{ + register int i, j; + register enum rtx_code code; + register char *fmt; + register int total; + + if (x == 0) + return 0; + + /* Compute the default costs of certain things. + Note that RTX_COSTS can override the defaults. */ + + code = GET_CODE (x); + switch (code) + { + case MULT: + /* Count multiplication by 2**n as a shift, + because if we are considering it, we would output it as a shift. */ + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && exact_log2 (INTVAL (XEXP (x, 1))) >= 0) + total = 2; + else + total = COSTS_N_INSNS (5); + break; + case DIV: + case UDIV: + case MOD: + case UMOD: + total = COSTS_N_INSNS (7); + break; + case USE: + /* Used in loop.c and combine.c as a marker. */ + total = 0; + break; + case ASM_OPERANDS: + /* We don't want these to be used in substitutions because + we have no way of validating the resulting insn. So assign + anything containing an ASM_OPERANDS a very high cost. */ + total = 1000; + break; + default: + total = 2; + } + + switch (code) + { + case REG: + return ! CHEAP_REG (REGNO (x)); + + case SUBREG: + /* If we can't tie these modes, make this expensive. The larger + the mode, the more expensive it is. */ + if (! MODES_TIEABLE_P (GET_MODE (x), GET_MODE (SUBREG_REG (x)))) + return COSTS_N_INSNS (2 + + GET_MODE_SIZE (GET_MODE (x)) / UNITS_PER_WORD); + return 2; +#ifdef RTX_COSTS + RTX_COSTS (x, code, outer_code); +#endif + CONST_COSTS (x, code, outer_code); + } + + /* Sum the costs of the sub-rtx's, plus cost of this operation, + which is already in total. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + total += rtx_cost (XEXP (x, i), code); + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + total += rtx_cost (XVECEXP (x, i, j), code); + + return total; +} + +/* Clear the hash table and initialize each register with its own quantity, + for a new basic block. */ + +static void +new_basic_block () +{ + register int i; + + next_qty = max_reg; + + bzero (reg_tick, max_reg * sizeof (int)); + + bcopy (all_minus_one, reg_in_table, max_reg * sizeof (int)); + bcopy (consec_ints, reg_qty, max_reg * sizeof (int)); + CLEAR_HARD_REG_SET (hard_regs_in_table); + + /* The per-quantity values used to be initialized here, but it is + much faster to initialize each as it is made in `make_new_qty'. */ + + for (i = 0; i < NBUCKETS; i++) + { + register struct table_elt *this, *next; + for (this = table[i]; this; this = next) + { + next = this->next_same_hash; + free_element (this); + } + } + + bzero (table, sizeof table); + + prev_insn = 0; + +#ifdef HAVE_cc0 + prev_insn_cc0 = 0; +#endif +} + +/* Say that register REG contains a quantity not in any register before + and initialize that quantity. */ + +static void +make_new_qty (reg) + register int reg; +{ + register int q; + + if (next_qty >= max_qty) + abort (); + + q = reg_qty[reg] = next_qty++; + qty_first_reg[q] = reg; + qty_last_reg[q] = reg; + qty_const[q] = qty_const_insn[q] = 0; + qty_comparison_code[q] = UNKNOWN; + + reg_next_eqv[reg] = reg_prev_eqv[reg] = -1; +} + +/* Make reg NEW equivalent to reg OLD. + OLD is not changing; NEW is. */ + +static void +make_regs_eqv (new, old) + register int new, old; +{ + register int lastr, firstr; + register int q = reg_qty[old]; + + /* Nothing should become eqv until it has a "non-invalid" qty number. */ + if (! REGNO_QTY_VALID_P (old)) + abort (); + + reg_qty[new] = q; + firstr = qty_first_reg[q]; + lastr = qty_last_reg[q]; + + /* Prefer fixed hard registers to anything. Prefer pseudo regs to other + hard regs. Among pseudos, if NEW will live longer than any other reg + of the same qty, and that is beyond the current basic block, + make it the new canonical replacement for this qty. */ + if (! (firstr < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (firstr)) + /* Certain fixed registers might be of the class NO_REGS. This means + that not only can they not be allocated by the compiler, but + they cannot be used in substitutions or canonicalizations + either. */ + && (new >= FIRST_PSEUDO_REGISTER || REGNO_REG_CLASS (new) != NO_REGS) + && ((new < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (new)) + || (new >= FIRST_PSEUDO_REGISTER + && (firstr < FIRST_PSEUDO_REGISTER + || ((uid_cuid[regno_last_uid[new]] > cse_basic_block_end + || (uid_cuid[regno_first_uid[new]] + < cse_basic_block_start)) + && (uid_cuid[regno_last_uid[new]] + > uid_cuid[regno_last_uid[firstr]])))))) + { + reg_prev_eqv[firstr] = new; + reg_next_eqv[new] = firstr; + reg_prev_eqv[new] = -1; + qty_first_reg[q] = new; + } + else + { + /* If NEW is a hard reg (known to be non-fixed), insert at end. + Otherwise, insert before any non-fixed hard regs that are at the + end. Registers of class NO_REGS cannot be used as an + equivalent for anything. */ + while (lastr < FIRST_PSEUDO_REGISTER && reg_prev_eqv[lastr] >= 0 + && (REGNO_REG_CLASS (lastr) == NO_REGS || ! FIXED_REGNO_P (lastr)) + && new >= FIRST_PSEUDO_REGISTER) + lastr = reg_prev_eqv[lastr]; + reg_next_eqv[new] = reg_next_eqv[lastr]; + if (reg_next_eqv[lastr] >= 0) + reg_prev_eqv[reg_next_eqv[lastr]] = new; + else + qty_last_reg[q] = new; + reg_next_eqv[lastr] = new; + reg_prev_eqv[new] = lastr; + } +} + +/* Remove REG from its equivalence class. */ + +static void +delete_reg_equiv (reg) + register int reg; +{ + register int n = reg_next_eqv[reg]; + register int p = reg_prev_eqv[reg]; + register int q = reg_qty[reg]; + + /* If invalid, do nothing. N and P above are undefined in that case. */ + if (q == reg) + return; + + if (n != -1) + reg_prev_eqv[n] = p; + else + qty_last_reg[q] = p; + if (p != -1) + reg_next_eqv[p] = n; + else + qty_first_reg[q] = n; + + reg_qty[reg] = reg; +} + +/* Remove any invalid expressions from the hash table + that refer to any of the registers contained in expression X. + + Make sure that newly inserted references to those registers + as subexpressions will be considered valid. + + mention_regs is not called when a register itself + is being stored in the table. + + Return 1 if we have done something that may have changed the hash code + of X. */ + +static int +mention_regs (x) + rtx x; +{ + register enum rtx_code code; + register int i, j; + register char *fmt; + register int changed = 0; + + if (x == 0) + return 0; + + code = GET_CODE (x); + if (code == REG) + { + register int regno = REGNO (x); + register int endregno + = regno + (regno >= FIRST_PSEUDO_REGISTER ? 1 + : HARD_REGNO_NREGS (regno, GET_MODE (x))); + int i; + + for (i = regno; i < endregno; i++) + { + if (reg_in_table[i] >= 0 && reg_in_table[i] != reg_tick[i]) + remove_invalid_refs (i); + + reg_in_table[i] = reg_tick[i]; + } + + return 0; + } + + /* If X is a comparison or a COMPARE and either operand is a register + that does not have a quantity, give it one. This is so that a later + call to record_jump_equiv won't cause X to be assigned a different + hash code and not found in the table after that call. + + It is not necessary to do this here, since rehash_using_reg can + fix up the table later, but doing this here eliminates the need to + call that expensive function in the most common case where the only + use of the register is in the comparison. */ + + if (code == COMPARE || GET_RTX_CLASS (code) == '<') + { + if (GET_CODE (XEXP (x, 0)) == REG + && ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 0)))) + if (insert_regs (XEXP (x, 0), NULL_PTR, 0)) + { + rehash_using_reg (XEXP (x, 0)); + changed = 1; + } + + if (GET_CODE (XEXP (x, 1)) == REG + && ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 1)))) + if (insert_regs (XEXP (x, 1), NULL_PTR, 0)) + { + rehash_using_reg (XEXP (x, 1)); + changed = 1; + } + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + changed |= mention_regs (XEXP (x, i)); + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + changed |= mention_regs (XVECEXP (x, i, j)); + + return changed; +} + +/* Update the register quantities for inserting X into the hash table + with a value equivalent to CLASSP. + (If the class does not contain a REG, it is irrelevant.) + If MODIFIED is nonzero, X is a destination; it is being modified. + Note that delete_reg_equiv should be called on a register + before insert_regs is done on that register with MODIFIED != 0. + + Nonzero value means that elements of reg_qty have changed + so X's hash code may be different. */ + +static int +insert_regs (x, classp, modified) + rtx x; + struct table_elt *classp; + int modified; +{ + if (GET_CODE (x) == REG) + { + register int regno = REGNO (x); + + /* If REGNO is in the equivalence table already but is of the + wrong mode for that equivalence, don't do anything here. */ + + if (REGNO_QTY_VALID_P (regno) + && qty_mode[reg_qty[regno]] != GET_MODE (x)) + return 0; + + if (modified || ! REGNO_QTY_VALID_P (regno)) + { + if (classp) + for (classp = classp->first_same_value; + classp != 0; + classp = classp->next_same_value) + if (GET_CODE (classp->exp) == REG + && GET_MODE (classp->exp) == GET_MODE (x)) + { + make_regs_eqv (regno, REGNO (classp->exp)); + return 1; + } + + make_new_qty (regno); + qty_mode[reg_qty[regno]] = GET_MODE (x); + return 1; + } + } + + /* If X is a SUBREG, we will likely be inserting the inner register in the + table. If that register doesn't have an assigned quantity number at + this point but does later, the insertion that we will be doing now will + not be accessible because its hash code will have changed. So assign + a quantity number now. */ + + else if (GET_CODE (x) == SUBREG && GET_CODE (SUBREG_REG (x)) == REG + && ! REGNO_QTY_VALID_P (REGNO (SUBREG_REG (x)))) + { + insert_regs (SUBREG_REG (x), NULL_PTR, 0); + mention_regs (SUBREG_REG (x)); + return 1; + } + else + return mention_regs (x); +} + +/* Look in or update the hash table. */ + +/* Put the element ELT on the list of free elements. */ + +static void +free_element (elt) + struct table_elt *elt; +{ + elt->next_same_hash = free_element_chain; + free_element_chain = elt; +} + +/* Return an element that is free for use. */ + +static struct table_elt * +get_element () +{ + struct table_elt *elt = free_element_chain; + if (elt) + { + free_element_chain = elt->next_same_hash; + return elt; + } + n_elements_made++; + return (struct table_elt *) oballoc (sizeof (struct table_elt)); +} + +/* Remove table element ELT from use in the table. + HASH is its hash code, made using the HASH macro. + It's an argument because often that is known in advance + and we save much time not recomputing it. */ + +static void +remove_from_table (elt, hash) + register struct table_elt *elt; + int hash; +{ + if (elt == 0) + return; + + /* Mark this element as removed. See cse_insn. */ + elt->first_same_value = 0; + + /* Remove the table element from its equivalence class. */ + + { + register struct table_elt *prev = elt->prev_same_value; + register struct table_elt *next = elt->next_same_value; + + if (next) next->prev_same_value = prev; + + if (prev) + prev->next_same_value = next; + else + { + register struct table_elt *newfirst = next; + while (next) + { + next->first_same_value = newfirst; + next = next->next_same_value; + } + } + } + + /* Remove the table element from its hash bucket. */ + + { + register struct table_elt *prev = elt->prev_same_hash; + register struct table_elt *next = elt->next_same_hash; + + if (next) next->prev_same_hash = prev; + + if (prev) + prev->next_same_hash = next; + else if (table[hash] == elt) + table[hash] = next; + else + { + /* This entry is not in the proper hash bucket. This can happen + when two classes were merged by `merge_equiv_classes'. Search + for the hash bucket that it heads. This happens only very + rarely, so the cost is acceptable. */ + for (hash = 0; hash < NBUCKETS; hash++) + if (table[hash] == elt) + table[hash] = next; + } + } + + /* Remove the table element from its related-value circular chain. */ + + if (elt->related_value != 0 && elt->related_value != elt) + { + register struct table_elt *p = elt->related_value; + while (p->related_value != elt) + p = p->related_value; + p->related_value = elt->related_value; + if (p->related_value == p) + p->related_value = 0; + } + + free_element (elt); +} + +/* Look up X in the hash table and return its table element, + or 0 if X is not in the table. + + MODE is the machine-mode of X, or if X is an integer constant + with VOIDmode then MODE is the mode with which X will be used. + + Here we are satisfied to find an expression whose tree structure + looks like X. */ + +static struct table_elt * +lookup (x, hash, mode) + rtx x; + int hash; + enum machine_mode mode; +{ + register struct table_elt *p; + + for (p = table[hash]; p; p = p->next_same_hash) + if (mode == p->mode && ((x == p->exp && GET_CODE (x) == REG) + || exp_equiv_p (x, p->exp, GET_CODE (x) != REG, 0))) + return p; + + return 0; +} + +/* Like `lookup' but don't care whether the table element uses invalid regs. + Also ignore discrepancies in the machine mode of a register. */ + +static struct table_elt * +lookup_for_remove (x, hash, mode) + rtx x; + int hash; + enum machine_mode mode; +{ + register struct table_elt *p; + + if (GET_CODE (x) == REG) + { + int regno = REGNO (x); + /* Don't check the machine mode when comparing registers; + invalidating (REG:SI 0) also invalidates (REG:DF 0). */ + for (p = table[hash]; p; p = p->next_same_hash) + if (GET_CODE (p->exp) == REG + && REGNO (p->exp) == regno) + return p; + } + else + { + for (p = table[hash]; p; p = p->next_same_hash) + if (mode == p->mode && (x == p->exp || exp_equiv_p (x, p->exp, 0, 0))) + return p; + } + + return 0; +} + +/* Look for an expression equivalent to X and with code CODE. + If one is found, return that expression. */ + +static rtx +lookup_as_function (x, code) + rtx x; + enum rtx_code code; +{ + register struct table_elt *p = lookup (x, safe_hash (x, VOIDmode) % NBUCKETS, + GET_MODE (x)); + if (p == 0) + return 0; + + for (p = p->first_same_value; p; p = p->next_same_value) + { + if (GET_CODE (p->exp) == code + /* Make sure this is a valid entry in the table. */ + && exp_equiv_p (p->exp, p->exp, 1, 0)) + return p->exp; + } + + return 0; +} + +/* Insert X in the hash table, assuming HASH is its hash code + and CLASSP is an element of the class it should go in + (or 0 if a new class should be made). + It is inserted at the proper position to keep the class in + the order cheapest first. + + MODE is the machine-mode of X, or if X is an integer constant + with VOIDmode then MODE is the mode with which X will be used. + + For elements of equal cheapness, the most recent one + goes in front, except that the first element in the list + remains first unless a cheaper element is added. The order of + pseudo-registers does not matter, as canon_reg will be called to + find the cheapest when a register is retrieved from the table. + + The in_memory field in the hash table element is set to 0. + The caller must set it nonzero if appropriate. + + You should call insert_regs (X, CLASSP, MODIFY) before calling here, + and if insert_regs returns a nonzero value + you must then recompute its hash code before calling here. + + If necessary, update table showing constant values of quantities. */ + +#define CHEAPER(X,Y) ((X)->cost < (Y)->cost) + +static struct table_elt * +insert (x, classp, hash, mode) + register rtx x; + register struct table_elt *classp; + int hash; + enum machine_mode mode; +{ + register struct table_elt *elt; + + /* If X is a register and we haven't made a quantity for it, + something is wrong. */ + if (GET_CODE (x) == REG && ! REGNO_QTY_VALID_P (REGNO (x))) + abort (); + + /* If X is a hard register, show it is being put in the table. */ + if (GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER) + { + int regno = REGNO (x); + int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x)); + int i; + + for (i = regno; i < endregno; i++) + SET_HARD_REG_BIT (hard_regs_in_table, i); + } + + + /* Put an element for X into the right hash bucket. */ + + elt = get_element (); + elt->exp = x; + elt->cost = COST (x); + elt->next_same_value = 0; + elt->prev_same_value = 0; + elt->next_same_hash = table[hash]; + elt->prev_same_hash = 0; + elt->related_value = 0; + elt->in_memory = 0; + elt->mode = mode; + elt->is_const = (CONSTANT_P (x) + /* GNU C++ takes advantage of this for `this' + (and other const values). */ + || (RTX_UNCHANGING_P (x) + && GET_CODE (x) == REG + && REGNO (x) >= FIRST_PSEUDO_REGISTER) + || FIXED_BASE_PLUS_P (x)); + + if (table[hash]) + table[hash]->prev_same_hash = elt; + table[hash] = elt; + + /* Put it into the proper value-class. */ + if (classp) + { + classp = classp->first_same_value; + if (CHEAPER (elt, classp)) + /* Insert at the head of the class */ + { + register struct table_elt *p; + elt->next_same_value = classp; + classp->prev_same_value = elt; + elt->first_same_value = elt; + + for (p = classp; p; p = p->next_same_value) + p->first_same_value = elt; + } + else + { + /* Insert not at head of the class. */ + /* Put it after the last element cheaper than X. */ + register struct table_elt *p, *next; + for (p = classp; (next = p->next_same_value) && CHEAPER (next, elt); + p = next); + /* Put it after P and before NEXT. */ + elt->next_same_value = next; + if (next) + next->prev_same_value = elt; + elt->prev_same_value = p; + p->next_same_value = elt; + elt->first_same_value = classp; + } + } + else + elt->first_same_value = elt; + + /* If this is a constant being set equivalent to a register or a register + being set equivalent to a constant, note the constant equivalence. + + If this is a constant, it cannot be equivalent to a different constant, + and a constant is the only thing that can be cheaper than a register. So + we know the register is the head of the class (before the constant was + inserted). + + If this is a register that is not already known equivalent to a + constant, we must check the entire class. + + If this is a register that is already known equivalent to an insn, + update `qty_const_insn' to show that `this_insn' is the latest + insn making that quantity equivalent to the constant. */ + + if (elt->is_const && classp && GET_CODE (classp->exp) == REG) + { + qty_const[reg_qty[REGNO (classp->exp)]] + = gen_lowpart_if_possible (qty_mode[reg_qty[REGNO (classp->exp)]], x); + qty_const_insn[reg_qty[REGNO (classp->exp)]] = this_insn; + } + + else if (GET_CODE (x) == REG && classp && ! qty_const[reg_qty[REGNO (x)]]) + { + register struct table_elt *p; + + for (p = classp; p != 0; p = p->next_same_value) + { + if (p->is_const) + { + qty_const[reg_qty[REGNO (x)]] + = gen_lowpart_if_possible (GET_MODE (x), p->exp); + qty_const_insn[reg_qty[REGNO (x)]] = this_insn; + break; + } + } + } + + else if (GET_CODE (x) == REG && qty_const[reg_qty[REGNO (x)]] + && GET_MODE (x) == qty_mode[reg_qty[REGNO (x)]]) + qty_const_insn[reg_qty[REGNO (x)]] = this_insn; + + /* If this is a constant with symbolic value, + and it has a term with an explicit integer value, + link it up with related expressions. */ + if (GET_CODE (x) == CONST) + { + rtx subexp = get_related_value (x); + int subhash; + struct table_elt *subelt, *subelt_prev; + + if (subexp != 0) + { + /* Get the integer-free subexpression in the hash table. */ + subhash = safe_hash (subexp, mode) % NBUCKETS; + subelt = lookup (subexp, subhash, mode); + if (subelt == 0) + subelt = insert (subexp, NULL_PTR, subhash, mode); + /* Initialize SUBELT's circular chain if it has none. */ + if (subelt->related_value == 0) + subelt->related_value = subelt; + /* Find the element in the circular chain that precedes SUBELT. */ + subelt_prev = subelt; + while (subelt_prev->related_value != subelt) + subelt_prev = subelt_prev->related_value; + /* Put new ELT into SUBELT's circular chain just before SUBELT. + This way the element that follows SUBELT is the oldest one. */ + elt->related_value = subelt_prev->related_value; + subelt_prev->related_value = elt; + } + } + + return elt; +} + +/* Given two equivalence classes, CLASS1 and CLASS2, put all the entries from + CLASS2 into CLASS1. This is done when we have reached an insn which makes + the two classes equivalent. + + CLASS1 will be the surviving class; CLASS2 should not be used after this + call. + + Any invalid entries in CLASS2 will not be copied. */ + +static void +merge_equiv_classes (class1, class2) + struct table_elt *class1, *class2; +{ + struct table_elt *elt, *next, *new; + + /* Ensure we start with the head of the classes. */ + class1 = class1->first_same_value; + class2 = class2->first_same_value; + + /* If they were already equal, forget it. */ + if (class1 == class2) + return; + + for (elt = class2; elt; elt = next) + { + int hash; + rtx exp = elt->exp; + enum machine_mode mode = elt->mode; + + next = elt->next_same_value; + + /* Remove old entry, make a new one in CLASS1's class. + Don't do this for invalid entries as we cannot find their + hash code (it also isn't necessary). */ + if (GET_CODE (exp) == REG || exp_equiv_p (exp, exp, 1, 0)) + { + hash_arg_in_memory = 0; + hash_arg_in_struct = 0; + hash = HASH (exp, mode); + + if (GET_CODE (exp) == REG) + delete_reg_equiv (REGNO (exp)); + + remove_from_table (elt, hash); + + if (insert_regs (exp, class1, 0)) + hash = HASH (exp, mode); + new = insert (exp, class1, hash, mode); + new->in_memory = hash_arg_in_memory; + new->in_struct = hash_arg_in_struct; + } + } +} + +/* Remove from the hash table, or mark as invalid, + all expressions whose values could be altered by storing in X. + X is a register, a subreg, or a memory reference with nonvarying address + (because, when a memory reference with a varying address is stored in, + all memory references are removed by invalidate_memory + so specific invalidation is superfluous). + + A nonvarying address may be just a register or just + a symbol reference, or it may be either of those plus + a numeric offset. */ + +static void +invalidate (x) + rtx x; +{ + register int i; + register struct table_elt *p; + rtx base; + HOST_WIDE_INT start, end; + + /* If X is a register, dependencies on its contents + are recorded through the qty number mechanism. + Just change the qty number of the register, + mark it as invalid for expressions that refer to it, + and remove it itself. */ + + if (GET_CODE (x) == REG) + { + register int regno = REGNO (x); + register int hash = HASH (x, GET_MODE (x)); + + /* Remove REGNO from any quantity list it might be on and indicate + that it's value might have changed. If it is a pseudo, remove its + entry from the hash table. + + For a hard register, we do the first two actions above for any + additional hard registers corresponding to X. Then, if any of these + registers are in the table, we must remove any REG entries that + overlap these registers. */ + + delete_reg_equiv (regno); + reg_tick[regno]++; + + if (regno >= FIRST_PSEUDO_REGISTER) + remove_from_table (lookup_for_remove (x, hash, GET_MODE (x)), hash); + else + { + HOST_WIDE_INT in_table + = TEST_HARD_REG_BIT (hard_regs_in_table, regno); + int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x)); + int tregno, tendregno; + register struct table_elt *p, *next; + + CLEAR_HARD_REG_BIT (hard_regs_in_table, regno); + + for (i = regno + 1; i < endregno; i++) + { + in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, i); + CLEAR_HARD_REG_BIT (hard_regs_in_table, i); + delete_reg_equiv (i); + reg_tick[i]++; + } + + if (in_table) + for (hash = 0; hash < NBUCKETS; hash++) + for (p = table[hash]; p; p = next) + { + next = p->next_same_hash; + + if (GET_CODE (p->exp) != REG + || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER) + continue; + + tregno = REGNO (p->exp); + tendregno + = tregno + HARD_REGNO_NREGS (tregno, GET_MODE (p->exp)); + if (tendregno > regno && tregno < endregno) + remove_from_table (p, hash); + } + } + + return; + } + + if (GET_CODE (x) == SUBREG) + { + if (GET_CODE (SUBREG_REG (x)) != REG) + abort (); + invalidate (SUBREG_REG (x)); + return; + } + + /* X is not a register; it must be a memory reference with + a nonvarying address. Remove all hash table elements + that refer to overlapping pieces of memory. */ + + if (GET_CODE (x) != MEM) + abort (); + + set_nonvarying_address_components (XEXP (x, 0), GET_MODE_SIZE (GET_MODE (x)), + &base, &start, &end); + + for (i = 0; i < NBUCKETS; i++) + { + register struct table_elt *next; + for (p = table[i]; p; p = next) + { + next = p->next_same_hash; + if (refers_to_mem_p (p->exp, base, start, end)) + remove_from_table (p, i); + } + } +} + +/* Remove all expressions that refer to register REGNO, + since they are already invalid, and we are about to + mark that register valid again and don't want the old + expressions to reappear as valid. */ + +static void +remove_invalid_refs (regno) + int regno; +{ + register int i; + register struct table_elt *p, *next; + + for (i = 0; i < NBUCKETS; i++) + for (p = table[i]; p; p = next) + { + next = p->next_same_hash; + if (GET_CODE (p->exp) != REG + && refers_to_regno_p (regno, regno + 1, p->exp, NULL_PTR)) + remove_from_table (p, i); + } +} + +/* Recompute the hash codes of any valid entries in the hash table that + reference X, if X is a register, or SUBREG_REG (X) if X is a SUBREG. + + This is called when we make a jump equivalence. */ + +static void +rehash_using_reg (x) + rtx x; +{ + int i; + struct table_elt *p, *next; + int hash; + + if (GET_CODE (x) == SUBREG) + x = SUBREG_REG (x); + + /* If X is not a register or if the register is known not to be in any + valid entries in the table, we have no work to do. */ + + if (GET_CODE (x) != REG + || reg_in_table[REGNO (x)] < 0 + || reg_in_table[REGNO (x)] != reg_tick[REGNO (x)]) + return; + + /* Scan all hash chains looking for valid entries that mention X. + If we find one and it is in the wrong hash chain, move it. We can skip + objects that are registers, since they are handled specially. */ + + for (i = 0; i < NBUCKETS; i++) + for (p = table[i]; p; p = next) + { + next = p->next_same_hash; + if (GET_CODE (p->exp) != REG && reg_mentioned_p (x, p->exp) + && exp_equiv_p (p->exp, p->exp, 1, 0) + && i != (hash = safe_hash (p->exp, p->mode) % NBUCKETS)) + { + if (p->next_same_hash) + p->next_same_hash->prev_same_hash = p->prev_same_hash; + + if (p->prev_same_hash) + p->prev_same_hash->next_same_hash = p->next_same_hash; + else + table[i] = p->next_same_hash; + + p->next_same_hash = table[hash]; + p->prev_same_hash = 0; + if (table[hash]) + table[hash]->prev_same_hash = p; + table[hash] = p; + } + } +} + +/* Remove from the hash table all expressions that reference memory, + or some of them as specified by *WRITES. */ + +static void +invalidate_memory (writes) + struct write_data *writes; +{ + register int i; + register struct table_elt *p, *next; + int all = writes->all; + int nonscalar = writes->nonscalar; + + for (i = 0; i < NBUCKETS; i++) + for (p = table[i]; p; p = next) + { + next = p->next_same_hash; + if (p->in_memory + && (all + || (nonscalar && p->in_struct) + || cse_rtx_addr_varies_p (p->exp))) + remove_from_table (p, i); + } +} + +/* Remove from the hash table any expression that is a call-clobbered + register. Also update their TICK values. */ + +static void +invalidate_for_call () +{ + int regno, endregno; + int i; + int hash; + struct table_elt *p, *next; + int in_table = 0; + + /* Go through all the hard registers. For each that is clobbered in + a CALL_INSN, remove the register from quantity chains and update + reg_tick if defined. Also see if any of these registers is currently + in the table. */ + + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno)) + { + delete_reg_equiv (regno); + if (reg_tick[regno] >= 0) + reg_tick[regno]++; + + in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, regno); + } + + /* In the case where we have no call-clobbered hard registers in the + table, we are done. Otherwise, scan the table and remove any + entry that overlaps a call-clobbered register. */ + + if (in_table) + for (hash = 0; hash < NBUCKETS; hash++) + for (p = table[hash]; p; p = next) + { + next = p->next_same_hash; + + if (GET_CODE (p->exp) != REG + || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER) + continue; + + regno = REGNO (p->exp); + endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (p->exp)); + + for (i = regno; i < endregno; i++) + if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i)) + { + remove_from_table (p, hash); + break; + } + } +} + +/* Given an expression X of type CONST, + and ELT which is its table entry (or 0 if it + is not in the hash table), + return an alternate expression for X as a register plus integer. + If none can be found, return 0. */ + +static rtx +use_related_value (x, elt) + rtx x; + struct table_elt *elt; +{ + register struct table_elt *relt = 0; + register struct table_elt *p, *q; + HOST_WIDE_INT offset; + + /* First, is there anything related known? + If we have a table element, we can tell from that. + Otherwise, must look it up. */ + + if (elt != 0 && elt->related_value != 0) + relt = elt; + else if (elt == 0 && GET_CODE (x) == CONST) + { + rtx subexp = get_related_value (x); + if (subexp != 0) + relt = lookup (subexp, + safe_hash (subexp, GET_MODE (subexp)) % NBUCKETS, + GET_MODE (subexp)); + } + + if (relt == 0) + return 0; + + /* Search all related table entries for one that has an + equivalent register. */ + + p = relt; + while (1) + { + /* This loop is strange in that it is executed in two different cases. + The first is when X is already in the table. Then it is searching + the RELATED_VALUE list of X's class (RELT). The second case is when + X is not in the table. Then RELT points to a class for the related + value. + + Ensure that, whatever case we are in, that we ignore classes that have + the same value as X. */ + + if (rtx_equal_p (x, p->exp)) + q = 0; + else + for (q = p->first_same_value; q; q = q->next_same_value) + if (GET_CODE (q->exp) == REG) + break; + + if (q) + break; + + p = p->related_value; + + /* We went all the way around, so there is nothing to be found. + Alternatively, perhaps RELT was in the table for some other reason + and it has no related values recorded. */ + if (p == relt || p == 0) + break; + } + + if (q == 0) + return 0; + + offset = (get_integer_term (x) - get_integer_term (p->exp)); + /* Note: OFFSET may be 0 if P->xexp and X are related by commutativity. */ + return plus_constant (q->exp, offset); +} + +/* Hash an rtx. We are careful to make sure the value is never negative. + Equivalent registers hash identically. + MODE is used in hashing for CONST_INTs only; + otherwise the mode of X is used. + + Store 1 in do_not_record if any subexpression is volatile. + + Store 1 in hash_arg_in_memory if X contains a MEM rtx + which does not have the RTX_UNCHANGING_P bit set. + In this case, also store 1 in hash_arg_in_struct + if there is a MEM rtx which has the MEM_IN_STRUCT_P bit set. + + Note that cse_insn knows that the hash code of a MEM expression + is just (int) MEM plus the hash code of the address. */ + +static int +canon_hash (x, mode) + rtx x; + enum machine_mode mode; +{ + register int i, j; + register int hash = 0; + register enum rtx_code code; + register char *fmt; + + /* repeat is used to turn tail-recursion into iteration. */ + repeat: + if (x == 0) + return hash; + + code = GET_CODE (x); + switch (code) + { + case REG: + { + register int regno = REGNO (x); + + /* On some machines, we can't record any non-fixed hard register, + because extending its life will cause reload problems. We + consider ap, fp, and sp to be fixed for this purpose. + On all machines, we can't record any global registers. */ + + if (regno < FIRST_PSEUDO_REGISTER + && (global_regs[regno] +#ifdef SMALL_REGISTER_CLASSES + || (! fixed_regs[regno] + && regno != FRAME_POINTER_REGNUM + && regno != ARG_POINTER_REGNUM + && regno != STACK_POINTER_REGNUM) +#endif + )) + { + do_not_record = 1; + return 0; + } + return hash + ((int) REG << 7) + reg_qty[regno]; + } + + case CONST_INT: + hash += ((int) mode + ((int) CONST_INT << 7) + + INTVAL (x) + (INTVAL (x) >> HASHBITS)); + return ((1 << HASHBITS) - 1) & hash; + + case CONST_DOUBLE: + /* This is like the general case, except that it only counts + the integers representing the constant. */ + hash += (int) code + (int) GET_MODE (x); + { + int i; + for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++) + { + int tem = XINT (x, i); + hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS)); + } + } + return hash; + + /* Assume there is only one rtx object for any given label. */ + case LABEL_REF: + /* Use `and' to ensure a positive number. */ + return (hash + ((HOST_WIDE_INT) LABEL_REF << 7) + + ((HOST_WIDE_INT) XEXP (x, 0) & ((1 << HASHBITS) - 1))); + + case SYMBOL_REF: + return (hash + ((HOST_WIDE_INT) SYMBOL_REF << 7) + + ((HOST_WIDE_INT) XEXP (x, 0) & ((1 << HASHBITS) - 1))); + + case MEM: + if (MEM_VOLATILE_P (x)) + { + do_not_record = 1; + return 0; + } + if (! RTX_UNCHANGING_P (x)) + { + hash_arg_in_memory = 1; + if (MEM_IN_STRUCT_P (x)) hash_arg_in_struct = 1; + } + /* Now that we have already found this special case, + might as well speed it up as much as possible. */ + hash += (int) MEM; + x = XEXP (x, 0); + goto repeat; + + case PRE_DEC: + case PRE_INC: + case POST_DEC: + case POST_INC: + case PC: + case CC0: + case CALL: + case UNSPEC_VOLATILE: + do_not_record = 1; + return 0; + + case ASM_OPERANDS: + if (MEM_VOLATILE_P (x)) + { + do_not_record = 1; + return 0; + } + } + + i = GET_RTX_LENGTH (code) - 1; + hash += (int) code + (int) GET_MODE (x); + fmt = GET_RTX_FORMAT (code); + for (; i >= 0; i--) + { + if (fmt[i] == 'e') + { + rtx tem = XEXP (x, i); + rtx tem1; + + /* If the operand is a REG that is equivalent to a constant, hash + as if we were hashing the constant, since we will be comparing + that way. */ + if (tem != 0 && GET_CODE (tem) == REG + && REGNO_QTY_VALID_P (REGNO (tem)) + && qty_mode[reg_qty[REGNO (tem)]] == GET_MODE (tem) + && (tem1 = qty_const[reg_qty[REGNO (tem)]]) != 0 + && CONSTANT_P (tem1)) + tem = tem1; + + /* If we are about to do the last recursive call + needed at this level, change it into iteration. + This function is called enough to be worth it. */ + if (i == 0) + { + x = tem; + goto repeat; + } + hash += canon_hash (tem, 0); + } + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + hash += canon_hash (XVECEXP (x, i, j), 0); + else if (fmt[i] == 's') + { + register char *p = XSTR (x, i); + if (p) + while (*p) + { + register int tem = *p++; + hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS)); + } + } + else if (fmt[i] == 'i') + { + register int tem = XINT (x, i); + hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS)); + } + else + abort (); + } + return hash; +} + +/* Like canon_hash but with no side effects. */ + +static int +safe_hash (x, mode) + rtx x; + enum machine_mode mode; +{ + int save_do_not_record = do_not_record; + int save_hash_arg_in_memory = hash_arg_in_memory; + int save_hash_arg_in_struct = hash_arg_in_struct; + int hash = canon_hash (x, mode); + hash_arg_in_memory = save_hash_arg_in_memory; + hash_arg_in_struct = save_hash_arg_in_struct; + do_not_record = save_do_not_record; + return hash; +} + +/* Return 1 iff X and Y would canonicalize into the same thing, + without actually constructing the canonicalization of either one. + If VALIDATE is nonzero, + we assume X is an expression being processed from the rtl + and Y was found in the hash table. We check register refs + in Y for being marked as valid. + + If EQUAL_VALUES is nonzero, we allow a register to match a constant value + that is known to be in the register. Ordinarily, we don't allow them + to match, because letting them match would cause unpredictable results + in all the places that search a hash table chain for an equivalent + for a given value. A possible equivalent that has different structure + has its hash code computed from different data. Whether the hash code + is the same as that of the the given value is pure luck. */ + +static int +exp_equiv_p (x, y, validate, equal_values) + rtx x, y; + int validate; + int equal_values; +{ + register int i, j; + register enum rtx_code code; + register char *fmt; + + /* Note: it is incorrect to assume an expression is equivalent to itself + if VALIDATE is nonzero. */ + if (x == y && !validate) + return 1; + if (x == 0 || y == 0) + return x == y; + + code = GET_CODE (x); + if (code != GET_CODE (y)) + { + if (!equal_values) + return 0; + + /* If X is a constant and Y is a register or vice versa, they may be + equivalent. We only have to validate if Y is a register. */ + if (CONSTANT_P (x) && GET_CODE (y) == REG + && REGNO_QTY_VALID_P (REGNO (y)) + && GET_MODE (y) == qty_mode[reg_qty[REGNO (y)]] + && rtx_equal_p (x, qty_const[reg_qty[REGNO (y)]]) + && (! validate || reg_in_table[REGNO (y)] == reg_tick[REGNO (y)])) + return 1; + + if (CONSTANT_P (y) && code == REG + && REGNO_QTY_VALID_P (REGNO (x)) + && GET_MODE (x) == qty_mode[reg_qty[REGNO (x)]] + && rtx_equal_p (y, qty_const[reg_qty[REGNO (x)]])) + return 1; + + return 0; + } + + /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */ + if (GET_MODE (x) != GET_MODE (y)) + return 0; + + switch (code) + { + case PC: + case CC0: + return x == y; + + case CONST_INT: + return INTVAL (x) == INTVAL (y); + + case LABEL_REF: + case SYMBOL_REF: + return XEXP (x, 0) == XEXP (y, 0); + + case REG: + { + int regno = REGNO (y); + int endregno + = regno + (regno >= FIRST_PSEUDO_REGISTER ? 1 + : HARD_REGNO_NREGS (regno, GET_MODE (y))); + int i; + + /* If the quantities are not the same, the expressions are not + equivalent. If there are and we are not to validate, they + are equivalent. Otherwise, ensure all regs are up-to-date. */ + + if (reg_qty[REGNO (x)] != reg_qty[regno]) + return 0; + + if (! validate) + return 1; + + for (i = regno; i < endregno; i++) + if (reg_in_table[i] != reg_tick[i]) + return 0; + + return 1; + } + + /* For commutative operations, check both orders. */ + case PLUS: + case MULT: + case AND: + case IOR: + case XOR: + case NE: + case EQ: + return ((exp_equiv_p (XEXP (x, 0), XEXP (y, 0), validate, equal_values) + && exp_equiv_p (XEXP (x, 1), XEXP (y, 1), + validate, equal_values)) + || (exp_equiv_p (XEXP (x, 0), XEXP (y, 1), + validate, equal_values) + && exp_equiv_p (XEXP (x, 1), XEXP (y, 0), + validate, equal_values))); + } + + /* Compare the elements. If any pair of corresponding elements + fail to match, return 0 for the whole things. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + switch (fmt[i]) + { + case 'e': + if (! exp_equiv_p (XEXP (x, i), XEXP (y, i), validate, equal_values)) + return 0; + break; + + case 'E': + if (XVECLEN (x, i) != XVECLEN (y, i)) + return 0; + for (j = 0; j < XVECLEN (x, i); j++) + if (! exp_equiv_p (XVECEXP (x, i, j), XVECEXP (y, i, j), + validate, equal_values)) + return 0; + break; + + case 's': + if (strcmp (XSTR (x, i), XSTR (y, i))) + return 0; + break; + + case 'i': + if (XINT (x, i) != XINT (y, i)) + return 0; + break; + + case 'w': + if (XWINT (x, i) != XWINT (y, i)) + return 0; + break; + + case '0': + break; + + default: + abort (); + } + } + + return 1; +} + +/* Return 1 iff any subexpression of X matches Y. + Here we do not require that X or Y be valid (for registers referred to) + for being in the hash table. */ + +static int +refers_to_p (x, y) + rtx x, y; +{ + register int i; + register enum rtx_code code; + register char *fmt; + + repeat: + if (x == y) + return 1; + if (x == 0 || y == 0) + return 0; + + code = GET_CODE (x); + /* If X as a whole has the same code as Y, they may match. + If so, return 1. */ + if (code == GET_CODE (y)) + { + if (exp_equiv_p (x, y, 0, 1)) + return 1; + } + + /* X does not match, so try its subexpressions. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + { + if (i == 0) + { + x = XEXP (x, 0); + goto repeat; + } + else + if (refers_to_p (XEXP (x, i), y)) + return 1; + } + else if (fmt[i] == 'E') + { + int j; + for (j = 0; j < XVECLEN (x, i); j++) + if (refers_to_p (XVECEXP (x, i, j), y)) + return 1; + } + + return 0; +} + +/* Given ADDR and SIZE (a memory address, and the size of the memory reference), + set PBASE, PSTART, and PEND which correspond to the base of the address, + the starting offset, and ending offset respectively. + + ADDR is known to be a nonvarying address. + + cse_address_varies_p returns zero for nonvarying addresses. */ + +static void +set_nonvarying_address_components (addr, size, pbase, pstart, pend) + rtx addr; + int size; + rtx *pbase; + HOST_WIDE_INT *pstart, *pend; +{ + rtx base; + int start, end; + + base = addr; + start = 0; + end = 0; + + /* Registers with nonvarying addresses usually have constant equivalents; + but the frame pointer register is also possible. */ + if (GET_CODE (base) == REG + && qty_const != 0 + && REGNO_QTY_VALID_P (REGNO (base)) + && qty_mode[reg_qty[REGNO (base)]] == GET_MODE (base) + && qty_const[reg_qty[REGNO (base)]] != 0) + base = qty_const[reg_qty[REGNO (base)]]; + else if (GET_CODE (base) == PLUS + && GET_CODE (XEXP (base, 1)) == CONST_INT + && GET_CODE (XEXP (base, 0)) == REG + && qty_const != 0 + && REGNO_QTY_VALID_P (REGNO (XEXP (base, 0))) + && (qty_mode[reg_qty[REGNO (XEXP (base, 0))]] + == GET_MODE (XEXP (base, 0))) + && qty_const[reg_qty[REGNO (XEXP (base, 0))]]) + { + start = INTVAL (XEXP (base, 1)); + base = qty_const[reg_qty[REGNO (XEXP (base, 0))]]; + } + + /* By definition, operand1 of a LO_SUM is the associated constant + address. Use the associated constant address as the base instead. */ + if (GET_CODE (base) == LO_SUM) + base = XEXP (base, 1); + + /* Strip off CONST. */ + if (GET_CODE (base) == CONST) + base = XEXP (base, 0); + + if (GET_CODE (base) == PLUS + && GET_CODE (XEXP (base, 1)) == CONST_INT) + { + start += INTVAL (XEXP (base, 1)); + base = XEXP (base, 0); + } + + end = start + size; + + /* Set the return values. */ + *pbase = base; + *pstart = start; + *pend = end; +} + +/* Return 1 iff any subexpression of X refers to memory + at an address of BASE plus some offset + such that any of the bytes' offsets fall between START (inclusive) + and END (exclusive). + + The value is undefined if X is a varying address (as determined by + cse_rtx_addr_varies_p). This function is not used in such cases. + + When used in the cse pass, `qty_const' is nonzero, and it is used + to treat an address that is a register with a known constant value + as if it were that constant value. + In the loop pass, `qty_const' is zero, so this is not done. */ + +static int +refers_to_mem_p (x, base, start, end) + rtx x, base; + HOST_WIDE_INT start, end; +{ + register HOST_WIDE_INT i; + register enum rtx_code code; + register char *fmt; + + if (GET_CODE (base) == CONST_INT) + { + start += INTVAL (base); + end += INTVAL (base); + base = const0_rtx; + } + + repeat: + if (x == 0) + return 0; + + code = GET_CODE (x); + if (code == MEM) + { + register rtx addr = XEXP (x, 0); /* Get the address. */ + rtx mybase; + HOST_WIDE_INT mystart, myend; + + set_nonvarying_address_components (addr, GET_MODE_SIZE (GET_MODE (x)), + &mybase, &mystart, &myend); + + + /* refers_to_mem_p is never called with varying addresses. + If the base addresses are not equal, there is no chance + of the memory addresses conflicting. */ + if (! rtx_equal_p (mybase, base)) + return 0; + + return myend > start && mystart < end; + } + + /* X does not match, so try its subexpressions. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + { + if (i == 0) + { + x = XEXP (x, 0); + goto repeat; + } + else + if (refers_to_mem_p (XEXP (x, i), base, start, end)) + return 1; + } + else if (fmt[i] == 'E') + { + int j; + for (j = 0; j < XVECLEN (x, i); j++) + if (refers_to_mem_p (XVECEXP (x, i, j), base, start, end)) + return 1; + } + + return 0; +} + +/* Nonzero if X refers to memory at a varying address; + except that a register which has at the moment a known constant value + isn't considered variable. */ + +static int +cse_rtx_addr_varies_p (x) + rtx x; +{ + /* We need not check for X and the equivalence class being of the same + mode because if X is equivalent to a constant in some mode, it + doesn't vary in any mode. */ + + if (GET_CODE (x) == MEM + && GET_CODE (XEXP (x, 0)) == REG + && REGNO_QTY_VALID_P (REGNO (XEXP (x, 0))) + && GET_MODE (XEXP (x, 0)) == qty_mode[reg_qty[REGNO (XEXP (x, 0))]] + && qty_const[reg_qty[REGNO (XEXP (x, 0))]] != 0) + return 0; + + if (GET_CODE (x) == MEM + && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG + && REGNO_QTY_VALID_P (REGNO (XEXP (XEXP (x, 0), 0))) + && (GET_MODE (XEXP (XEXP (x, 0), 0)) + == qty_mode[reg_qty[REGNO (XEXP (XEXP (x, 0), 0))]]) + && qty_const[reg_qty[REGNO (XEXP (XEXP (x, 0), 0))]]) + return 0; + + return rtx_addr_varies_p (x); +} + +/* Canonicalize an expression: + replace each register reference inside it + with the "oldest" equivalent register. + + If INSN is non-zero and we are replacing a pseudo with a hard register + or vice versa, validate_change is used to ensure that INSN remains valid + after we make our substitution. The calls are made with IN_GROUP non-zero + so apply_change_group must be called upon the outermost return from this + function (unless INSN is zero). The result of apply_change_group can + generally be discarded since the changes we are making are optional. */ + +static rtx +canon_reg (x, insn) + rtx x; + rtx insn; +{ + register int i; + register enum rtx_code code; + register char *fmt; + + if (x == 0) + return x; + + code = GET_CODE (x); + switch (code) + { + case PC: + case CC0: + case CONST: + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + case LABEL_REF: + case ADDR_VEC: + case ADDR_DIFF_VEC: + return x; + + case REG: + { + register int first; + + /* Never replace a hard reg, because hard regs can appear + in more than one machine mode, and we must preserve the mode + of each occurrence. Also, some hard regs appear in + MEMs that are shared and mustn't be altered. Don't try to + replace any reg that maps to a reg of class NO_REGS. */ + if (REGNO (x) < FIRST_PSEUDO_REGISTER + || ! REGNO_QTY_VALID_P (REGNO (x))) + return x; + + first = qty_first_reg[reg_qty[REGNO (x)]]; + return (first >= FIRST_PSEUDO_REGISTER ? regno_reg_rtx[first] + : REGNO_REG_CLASS (first) == NO_REGS ? x + : gen_rtx (REG, qty_mode[reg_qty[REGNO (x)]], first)); + } + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + register int j; + + if (fmt[i] == 'e') + { + rtx new = canon_reg (XEXP (x, i), insn); + + /* If replacing pseudo with hard reg or vice versa, ensure the + insn remains valid. Likewise if the insn has MATCH_DUPs. */ + if (insn != 0 && new != 0 + && GET_CODE (new) == REG && GET_CODE (XEXP (x, i)) == REG + && (((REGNO (new) < FIRST_PSEUDO_REGISTER) + != (REGNO (XEXP (x, i)) < FIRST_PSEUDO_REGISTER)) + || insn_n_dups[recog_memoized (insn)] > 0)) + validate_change (insn, &XEXP (x, i), new, 1); + else + XEXP (x, i) = new; + } + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + XVECEXP (x, i, j) = canon_reg (XVECEXP (x, i, j), insn); + } + + return x; +} + +/* LOC is a location with INSN that is an operand address (the contents of + a MEM). Find the best equivalent address to use that is valid for this + insn. + + On most CISC machines, complicated address modes are costly, and rtx_cost + is a good approximation for that cost. However, most RISC machines have + only a few (usually only one) memory reference formats. If an address is + valid at all, it is often just as cheap as any other address. Hence, for + RISC machines, we use the configuration macro `ADDRESS_COST' to compare the + costs of various addresses. For two addresses of equal cost, choose the one + with the highest `rtx_cost' value as that has the potential of eliminating + the most insns. For equal costs, we choose the first in the equivalence + class. Note that we ignore the fact that pseudo registers are cheaper + than hard registers here because we would also prefer the pseudo registers. + */ + +static void +find_best_addr (insn, loc) + rtx insn; + rtx *loc; +{ + struct table_elt *elt, *p; + rtx addr = *loc; + int our_cost; + int found_better = 1; + int save_do_not_record = do_not_record; + int save_hash_arg_in_memory = hash_arg_in_memory; + int save_hash_arg_in_struct = hash_arg_in_struct; + int hash_code; + int addr_volatile; + int regno; + + /* Do not try to replace constant addresses or addresses of local and + argument slots. These MEM expressions are made only once and inserted + in many instructions, as well as being used to control symbol table + output. It is not safe to clobber them. + + There are some uncommon cases where the address is already in a register + for some reason, but we cannot take advantage of that because we have + no easy way to unshare the MEM. In addition, looking up all stack + addresses is costly. */ + if ((GET_CODE (addr) == PLUS + && GET_CODE (XEXP (addr, 0)) == REG + && GET_CODE (XEXP (addr, 1)) == CONST_INT + && (regno = REGNO (XEXP (addr, 0)), + regno == FRAME_POINTER_REGNUM || regno == ARG_POINTER_REGNUM)) + || (GET_CODE (addr) == REG + && (regno = REGNO (addr), + regno == FRAME_POINTER_REGNUM || regno == ARG_POINTER_REGNUM)) + || CONSTANT_ADDRESS_P (addr)) + return; + + /* If this address is not simply a register, try to fold it. This will + sometimes simplify the expression. Many simplifications + will not be valid, but some, usually applying the associative rule, will + be valid and produce better code. */ + if (GET_CODE (addr) != REG + && validate_change (insn, loc, fold_rtx (addr, insn), 0)) + addr = *loc; + + /* If this address is not in the hash table, we can't look for equivalences + of the whole address. Also, ignore if volatile. */ + + do_not_record = 0; + hash_code = HASH (addr, Pmode); + addr_volatile = do_not_record; + do_not_record = save_do_not_record; + hash_arg_in_memory = save_hash_arg_in_memory; + hash_arg_in_struct = save_hash_arg_in_struct; + + if (addr_volatile) + return; + + elt = lookup (addr, hash_code, Pmode); + +#ifndef ADDRESS_COST + if (elt) + { + our_cost = elt->cost; + + /* Find the lowest cost below ours that works. */ + for (elt = elt->first_same_value; elt; elt = elt->next_same_value) + if (elt->cost < our_cost + && (GET_CODE (elt->exp) == REG + || exp_equiv_p (elt->exp, elt->exp, 1, 0)) + && validate_change (insn, loc, + canon_reg (copy_rtx (elt->exp), NULL_RTX), 0)) + return; + } +#else + + if (elt) + { + /* We need to find the best (under the criteria documented above) entry + in the class that is valid. We use the `flag' field to indicate + choices that were invalid and iterate until we can't find a better + one that hasn't already been tried. */ + + for (p = elt->first_same_value; p; p = p->next_same_value) + p->flag = 0; + + while (found_better) + { + int best_addr_cost = ADDRESS_COST (*loc); + int best_rtx_cost = (elt->cost + 1) >> 1; + struct table_elt *best_elt = elt; + + found_better = 0; + for (p = elt->first_same_value; p; p = p->next_same_value) + if (! p->flag + && (GET_CODE (p->exp) == REG + || exp_equiv_p (p->exp, p->exp, 1, 0)) + && (ADDRESS_COST (p->exp) < best_addr_cost + || (ADDRESS_COST (p->exp) == best_addr_cost + && (p->cost + 1) >> 1 > best_rtx_cost))) + { + found_better = 1; + best_addr_cost = ADDRESS_COST (p->exp); + best_rtx_cost = (p->cost + 1) >> 1; + best_elt = p; + } + + if (found_better) + { + if (validate_change (insn, loc, + canon_reg (copy_rtx (best_elt->exp), + NULL_RTX), 0)) + return; + else + best_elt->flag = 1; + } + } + } + + /* If the address is a binary operation with the first operand a register + and the second a constant, do the same as above, but looking for + equivalences of the register. Then try to simplify before checking for + the best address to use. This catches a few cases: First is when we + have REG+const and the register is another REG+const. We can often merge + the constants and eliminate one insn and one register. It may also be + that a machine has a cheap REG+REG+const. Finally, this improves the + code on the Alpha for unaligned byte stores. */ + + if (flag_expensive_optimizations + && (GET_RTX_CLASS (GET_CODE (*loc)) == '2' + || GET_RTX_CLASS (GET_CODE (*loc)) == 'c') + && GET_CODE (XEXP (*loc, 0)) == REG + && GET_CODE (XEXP (*loc, 1)) == CONST_INT) + { + rtx c = XEXP (*loc, 1); + + do_not_record = 0; + hash_code = HASH (XEXP (*loc, 0), Pmode); + do_not_record = save_do_not_record; + hash_arg_in_memory = save_hash_arg_in_memory; + hash_arg_in_struct = save_hash_arg_in_struct; + + elt = lookup (XEXP (*loc, 0), hash_code, Pmode); + if (elt == 0) + return; + + /* We need to find the best (under the criteria documented above) entry + in the class that is valid. We use the `flag' field to indicate + choices that were invalid and iterate until we can't find a better + one that hasn't already been tried. */ + + for (p = elt->first_same_value; p; p = p->next_same_value) + p->flag = 0; + + while (found_better) + { + int best_addr_cost = ADDRESS_COST (*loc); + int best_rtx_cost = (COST (*loc) + 1) >> 1; + struct table_elt *best_elt = elt; + rtx best_rtx = *loc; + + found_better = 0; + for (p = elt->first_same_value; p; p = p->next_same_value) + if (! p->flag + && (GET_CODE (p->exp) == REG + || exp_equiv_p (p->exp, p->exp, 1, 0))) + { + rtx new = cse_gen_binary (GET_CODE (*loc), Pmode, p->exp, c); + + if ((ADDRESS_COST (new) < best_addr_cost + || (ADDRESS_COST (new) == best_addr_cost + && (COST (new) + 1) >> 1 > best_rtx_cost))) + { + found_better = 1; + best_addr_cost = ADDRESS_COST (new); + best_rtx_cost = (COST (new) + 1) >> 1; + best_elt = p; + best_rtx = new; + } + } + + if (found_better) + { + if (validate_change (insn, loc, + canon_reg (copy_rtx (best_rtx), + NULL_RTX), 0)) + return; + else + best_elt->flag = 1; + } + } + } +#endif +} + +/* Given an operation (CODE, *PARG1, *PARG2), where code is a comparison + operation (EQ, NE, GT, etc.), follow it back through the hash table and + what values are being compared. + + *PARG1 and *PARG2 are updated to contain the rtx representing the values + actually being compared. For example, if *PARG1 was (cc0) and *PARG2 + was (const_int 0), *PARG1 and *PARG2 will be set to the objects that were + compared to produce cc0. + + The return value is the comparison operator and is either the code of + A or the code corresponding to the inverse of the comparison. */ + +static enum rtx_code +find_comparison_args (code, parg1, parg2, pmode1, pmode2) + enum rtx_code code; + rtx *parg1, *parg2; + enum machine_mode *pmode1, *pmode2; +{ + rtx arg1, arg2; + + arg1 = *parg1, arg2 = *parg2; + + /* If ARG2 is const0_rtx, see what ARG1 is equivalent to. */ + + while (arg2 == CONST0_RTX (GET_MODE (arg1))) + { + /* Set non-zero when we find something of interest. */ + rtx x = 0; + int reverse_code = 0; + struct table_elt *p = 0; + + /* If arg1 is a COMPARE, extract the comparison arguments from it. + On machines with CC0, this is the only case that can occur, since + fold_rtx will return the COMPARE or item being compared with zero + when given CC0. */ + + if (GET_CODE (arg1) == COMPARE && arg2 == const0_rtx) + x = arg1; + + /* If ARG1 is a comparison operator and CODE is testing for + STORE_FLAG_VALUE, get the inner arguments. */ + + else if (GET_RTX_CLASS (GET_CODE (arg1)) == '<') + { + if (code == NE + || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT + && code == LT && STORE_FLAG_VALUE == -1) +#ifdef FLOAT_STORE_FLAG_VALUE + || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_FLOAT + && FLOAT_STORE_FLAG_VALUE < 0) +#endif + ) + x = arg1; + else if (code == EQ + || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT + && code == GE && STORE_FLAG_VALUE == -1) +#ifdef FLOAT_STORE_FLAG_VALUE + || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_FLOAT + && FLOAT_STORE_FLAG_VALUE < 0) +#endif + ) + x = arg1, reverse_code = 1; + } + + /* ??? We could also check for + + (ne (and (eq (...) (const_int 1))) (const_int 0)) + + and related forms, but let's wait until we see them occurring. */ + + if (x == 0) + /* Look up ARG1 in the hash table and see if it has an equivalence + that lets us see what is being compared. */ + p = lookup (arg1, safe_hash (arg1, GET_MODE (arg1)) % NBUCKETS, + GET_MODE (arg1)); + if (p) p = p->first_same_value; + + for (; p; p = p->next_same_value) + { + enum machine_mode inner_mode = GET_MODE (p->exp); + + /* If the entry isn't valid, skip it. */ + if (! exp_equiv_p (p->exp, p->exp, 1, 0)) + continue; + + if (GET_CODE (p->exp) == COMPARE + /* Another possibility is that this machine has a compare insn + that includes the comparison code. In that case, ARG1 would + be equivalent to a comparison operation that would set ARG1 to + either STORE_FLAG_VALUE or zero. If this is an NE operation, + ORIG_CODE is the actual comparison being done; if it is an EQ, + we must reverse ORIG_CODE. On machine with a negative value + for STORE_FLAG_VALUE, also look at LT and GE operations. */ + || ((code == NE + || (code == LT + && GET_MODE_CLASS (inner_mode) == MODE_INT + && (GET_MODE_BITSIZE (inner_mode) + <= HOST_BITS_PER_WIDE_INT) + && (STORE_FLAG_VALUE + & ((HOST_WIDE_INT) 1 + << (GET_MODE_BITSIZE (inner_mode) - 1)))) +#ifdef FLOAT_STORE_FLAG_VALUE + || (code == LT + && GET_MODE_CLASS (inner_mode) == MODE_FLOAT + && FLOAT_STORE_FLAG_VALUE < 0) +#endif + ) + && GET_RTX_CLASS (GET_CODE (p->exp)) == '<')) + { + x = p->exp; + break; + } + else if ((code == EQ + || (code == GE + && GET_MODE_CLASS (inner_mode) == MODE_INT + && (GET_MODE_BITSIZE (inner_mode) + <= HOST_BITS_PER_WIDE_INT) + && (STORE_FLAG_VALUE + & ((HOST_WIDE_INT) 1 + << (GET_MODE_BITSIZE (inner_mode) - 1)))) +#ifdef FLOAT_STORE_FLAG_VALUE + || (code == GE + && GET_MODE_CLASS (inner_mode) == MODE_FLOAT + && FLOAT_STORE_FLAG_VALUE < 0) +#endif + ) + && GET_RTX_CLASS (GET_CODE (p->exp)) == '<') + { + reverse_code = 1; + x = p->exp; + break; + } + + /* If this is fp + constant, the equivalent is a better operand since + it may let us predict the value of the comparison. */ + else if (NONZERO_BASE_PLUS_P (p->exp)) + { + arg1 = p->exp; + continue; + } + } + + /* If we didn't find a useful equivalence for ARG1, we are done. + Otherwise, set up for the next iteration. */ + if (x == 0) + break; + + arg1 = XEXP (x, 0), arg2 = XEXP (x, 1); + if (GET_RTX_CLASS (GET_CODE (x)) == '<') + code = GET_CODE (x); + + if (reverse_code) + code = reverse_condition (code); + } + + /* Return our results. Return the modes from before fold_rtx + because fold_rtx might produce const_int, and then it's too late. */ + *pmode1 = GET_MODE (arg1), *pmode2 = GET_MODE (arg2); + *parg1 = fold_rtx (arg1, 0), *parg2 = fold_rtx (arg2, 0); + + return code; +} + +/* Try to simplify a unary operation CODE whose output mode is to be + MODE with input operand OP whose mode was originally OP_MODE. + Return zero if no simplification can be made. */ + +rtx +simplify_unary_operation (code, mode, op, op_mode) + enum rtx_code code; + enum machine_mode mode; + rtx op; + enum machine_mode op_mode; +{ + register int width = GET_MODE_BITSIZE (mode); + + /* The order of these tests is critical so that, for example, we don't + check the wrong mode (input vs. output) for a conversion operation, + such as FIX. At some point, this should be simplified. */ + +#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) + if (code == FLOAT && GET_CODE (op) == CONST_INT) + { + REAL_VALUE_TYPE d; + +#ifdef REAL_ARITHMETIC + REAL_VALUE_FROM_INT (d, INTVAL (op), INTVAL (op) < 0 ? ~0 : 0); +#else + d = (double) INTVAL (op); +#endif + return CONST_DOUBLE_FROM_REAL_VALUE (d, mode); + } + else if (code == UNSIGNED_FLOAT && GET_CODE (op) == CONST_INT) + { + REAL_VALUE_TYPE d; + +#ifdef REAL_ARITHMETIC + REAL_VALUE_FROM_INT (d, INTVAL (op), 0); +#else + d = (double) (unsigned int) INTVAL (op); +#endif + return CONST_DOUBLE_FROM_REAL_VALUE (d, mode); + } + + else if (code == FLOAT && GET_CODE (op) == CONST_DOUBLE + && GET_MODE (op) == VOIDmode) + { + REAL_VALUE_TYPE d; + +#ifdef REAL_ARITHMETIC + REAL_VALUE_FROM_INT (d, CONST_DOUBLE_LOW (op), CONST_DOUBLE_HIGH (op)); +#else + if (CONST_DOUBLE_HIGH (op) < 0) + { + d = (double) (~ CONST_DOUBLE_HIGH (op)); + d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)) + * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))); + d += (double) (unsigned HOST_WIDE_INT) (~ CONST_DOUBLE_LOW (op)); + d = (- d - 1.0); + } + else + { + d = (double) CONST_DOUBLE_HIGH (op); + d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)) + * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))); + d += (double) (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op); + } +#endif /* REAL_ARITHMETIC */ + return CONST_DOUBLE_FROM_REAL_VALUE (d, mode); + } + else if (code == UNSIGNED_FLOAT && GET_CODE (op) == CONST_DOUBLE + && GET_MODE (op) == VOIDmode) + { + REAL_VALUE_TYPE d; + +#ifdef REAL_ARITHMETIC + REAL_VALUE_FROM_UNSIGNED_INT (d, CONST_DOUBLE_LOW (op), + CONST_DOUBLE_HIGH (op)); +#else + d = (double) CONST_DOUBLE_HIGH (op); + d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)) + * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))); + d += (double) (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op); +#endif /* REAL_ARITHMETIC */ + return CONST_DOUBLE_FROM_REAL_VALUE (d, mode); + } +#endif + + if (GET_CODE (op) == CONST_INT + && width <= HOST_BITS_PER_WIDE_INT && width > 0) + { + register HOST_WIDE_INT arg0 = INTVAL (op); + register HOST_WIDE_INT val; + + switch (code) + { + case NOT: + val = ~ arg0; + break; + + case NEG: + val = - arg0; + break; + + case ABS: + val = (arg0 >= 0 ? arg0 : - arg0); + break; + + case FFS: + /* Don't use ffs here. Instead, get low order bit and then its + number. If arg0 is zero, this will return 0, as desired. */ + arg0 &= GET_MODE_MASK (mode); + val = exact_log2 (arg0 & (- arg0)) + 1; + break; + + case TRUNCATE: + val = arg0; + break; + + case ZERO_EXTEND: + if (op_mode == VOIDmode) + op_mode = mode; + if (GET_MODE_BITSIZE (op_mode) == HOST_BITS_PER_WIDE_INT) + { + /* If we were really extending the mode, + we would have to distinguish between zero-extension + and sign-extension. */ + if (width != GET_MODE_BITSIZE (op_mode)) + abort (); + val = arg0; + } + else if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT) + val = arg0 & ~((HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (op_mode)); + else + return 0; + break; + + case SIGN_EXTEND: + if (op_mode == VOIDmode) + op_mode = mode; + if (GET_MODE_BITSIZE (op_mode) == HOST_BITS_PER_WIDE_INT) + { + /* If we were really extending the mode, + we would have to distinguish between zero-extension + and sign-extension. */ + if (width != GET_MODE_BITSIZE (op_mode)) + abort (); + val = arg0; + } + else if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT) + { + val + = arg0 & ~((HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (op_mode)); + if (val + & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (op_mode) - 1))) + val -= (HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (op_mode); + } + else + return 0; + break; + + case SQRT: + return 0; + + default: + abort (); + } + + /* Clear the bits that don't belong in our mode, + unless they and our sign bit are all one. + So we get either a reasonable negative value or a reasonable + unsigned value for this mode. */ + if (width < HOST_BITS_PER_WIDE_INT + && ((val & ((HOST_WIDE_INT) (-1) << (width - 1))) + != ((HOST_WIDE_INT) (-1) << (width - 1)))) + val &= (1 << width) - 1; + + return GEN_INT (val); + } + + /* We can do some operations on integer CONST_DOUBLEs. Also allow + for a DImode operation on a CONST_INT. */ + else if (GET_MODE (op) == VOIDmode + && (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT)) + { + HOST_WIDE_INT l1, h1, lv, hv; + + if (GET_CODE (op) == CONST_DOUBLE) + l1 = CONST_DOUBLE_LOW (op), h1 = CONST_DOUBLE_HIGH (op); + else + l1 = INTVAL (op), h1 = l1 < 0 ? -1 : 0; + + switch (code) + { + case NOT: + lv = ~ l1; + hv = ~ h1; + break; + + case NEG: + neg_double (l1, h1, &lv, &hv); + break; + + case ABS: + if (h1 < 0) + neg_double (l1, h1, &lv, &hv); + else + lv = l1, hv = h1; + break; + + case FFS: + hv = 0; + if (l1 == 0) + lv = HOST_BITS_PER_WIDE_INT + exact_log2 (h1 & (-h1)) + 1; + else + lv = exact_log2 (l1 & (-l1)) + 1; + break; + + case TRUNCATE: + if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT) + return GEN_INT (l1 & GET_MODE_MASK (mode)); + else + return 0; + break; + + case ZERO_EXTEND: + if (op_mode == VOIDmode + || GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT) + return 0; + + hv = 0; + lv = l1 & GET_MODE_MASK (op_mode); + break; + + case SIGN_EXTEND: + if (op_mode == VOIDmode + || GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT) + return 0; + else + { + lv = l1 & GET_MODE_MASK (op_mode); + if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT + && (lv & ((HOST_WIDE_INT) 1 + << (GET_MODE_BITSIZE (op_mode) - 1))) != 0) + lv -= (HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (op_mode); + + hv = (lv < 0) ? ~ (HOST_WIDE_INT) 0 : 0; + } + break; + + case SQRT: + return 0; + + default: + return 0; + } + + return immed_double_const (lv, hv, mode); + } + +#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) + else if (GET_CODE (op) == CONST_DOUBLE + && GET_MODE_CLASS (mode) == MODE_FLOAT) + { + REAL_VALUE_TYPE d; + jmp_buf handler; + rtx x; + + if (setjmp (handler)) + /* There used to be a warning here, but that is inadvisable. + People may want to cause traps, and the natural way + to do it should not get a warning. */ + return 0; + + set_float_handler (handler); + + REAL_VALUE_FROM_CONST_DOUBLE (d, op); + + switch (code) + { + case NEG: + d = REAL_VALUE_NEGATE (d); + break; + + case ABS: + if (REAL_VALUE_NEGATIVE (d)) + d = REAL_VALUE_NEGATE (d); + break; + + case FLOAT_TRUNCATE: + d = real_value_truncate (mode, d); + break; + + case FLOAT_EXTEND: + /* All this does is change the mode. */ + break; + + case FIX: + d = REAL_VALUE_RNDZINT (d); + break; + + case UNSIGNED_FIX: + d = REAL_VALUE_UNSIGNED_RNDZINT (d); + break; + + case SQRT: + return 0; + + default: + abort (); + } + + x = immed_real_const_1 (d, mode); + set_float_handler (NULL_PTR); + return x; + } + else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE_CLASS (mode) == MODE_INT + && width <= HOST_BITS_PER_WIDE_INT && width > 0) + { + REAL_VALUE_TYPE d; + jmp_buf handler; + rtx x; + HOST_WIDE_INT val; + + if (setjmp (handler)) + return 0; + + set_float_handler (handler); + + REAL_VALUE_FROM_CONST_DOUBLE (d, op); + + switch (code) + { + case FIX: + val = REAL_VALUE_FIX (d); + break; + + case UNSIGNED_FIX: + val = REAL_VALUE_UNSIGNED_FIX (d); + break; + + default: + abort (); + } + + set_float_handler (NULL_PTR); + + /* Clear the bits that don't belong in our mode, + unless they and our sign bit are all one. + So we get either a reasonable negative value or a reasonable + unsigned value for this mode. */ + if (width < HOST_BITS_PER_WIDE_INT + && ((val & ((HOST_WIDE_INT) (-1) << (width - 1))) + != ((HOST_WIDE_INT) (-1) << (width - 1)))) + val &= ((HOST_WIDE_INT) 1 << width) - 1; + + return GEN_INT (val); + } +#endif + /* This was formerly used only for non-IEEE float. + eggert@twinsun.com says it is safe for IEEE also. */ + else + { + /* There are some simplifications we can do even if the operands + aren't constant. */ + switch (code) + { + case NEG: + case NOT: + /* (not (not X)) == X, similarly for NEG. */ + if (GET_CODE (op) == code) + return XEXP (op, 0); + break; + + case SIGN_EXTEND: + /* (sign_extend (truncate (minus (label_ref L1) (label_ref L2)))) + becomes just the MINUS if its mode is MODE. This allows + folding switch statements on machines using casesi (such as + the Vax). */ + if (GET_CODE (op) == TRUNCATE + && GET_MODE (XEXP (op, 0)) == mode + && GET_CODE (XEXP (op, 0)) == MINUS + && GET_CODE (XEXP (XEXP (op, 0), 0)) == LABEL_REF + && GET_CODE (XEXP (XEXP (op, 0), 1)) == LABEL_REF) + return XEXP (op, 0); + break; + } + + return 0; + } +} + +/* Simplify a binary operation CODE with result mode MODE, operating on OP0 + and OP1. Return 0 if no simplification is possible. + + Don't use this for relational operations such as EQ or LT. + Use simplify_relational_operation instead. */ + +rtx +simplify_binary_operation (code, mode, op0, op1) + enum rtx_code code; + enum machine_mode mode; + rtx op0, op1; +{ + register HOST_WIDE_INT arg0, arg1, arg0s, arg1s; + HOST_WIDE_INT val; + int width = GET_MODE_BITSIZE (mode); + rtx tem; + + /* Relational operations don't work here. We must know the mode + of the operands in order to do the comparison correctly. + Assuming a full word can give incorrect results. + Consider comparing 128 with -128 in QImode. */ + + if (GET_RTX_CLASS (code) == '<') + abort (); + +#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) + if (GET_MODE_CLASS (mode) == MODE_FLOAT + && GET_CODE (op0) == CONST_DOUBLE && GET_CODE (op1) == CONST_DOUBLE + && mode == GET_MODE (op0) && mode == GET_MODE (op1)) + { + REAL_VALUE_TYPE f0, f1, value; + jmp_buf handler; + + if (setjmp (handler)) + return 0; + + set_float_handler (handler); + + REAL_VALUE_FROM_CONST_DOUBLE (f0, op0); + REAL_VALUE_FROM_CONST_DOUBLE (f1, op1); + f0 = real_value_truncate (mode, f0); + f1 = real_value_truncate (mode, f1); + +#ifdef REAL_ARITHMETIC + REAL_ARITHMETIC (value, rtx_to_tree_code (code), f0, f1); +#else + switch (code) + { + case PLUS: + value = f0 + f1; + break; + case MINUS: + value = f0 - f1; + break; + case MULT: + value = f0 * f1; + break; + case DIV: +#ifndef REAL_INFINITY + if (f1 == 0) + return 0; +#endif + value = f0 / f1; + break; + case SMIN: + value = MIN (f0, f1); + break; + case SMAX: + value = MAX (f0, f1); + break; + default: + abort (); + } +#endif + + set_float_handler (NULL_PTR); + value = real_value_truncate (mode, value); + return immed_real_const_1 (value, mode); + } +#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */ + + /* We can fold some multi-word operations. */ + if (GET_MODE_CLASS (mode) == MODE_INT + && GET_CODE (op0) == CONST_DOUBLE + && (GET_CODE (op1) == CONST_DOUBLE || GET_CODE (op1) == CONST_INT)) + { + HOST_WIDE_INT l1, l2, h1, h2, lv, hv; + + l1 = CONST_DOUBLE_LOW (op0), h1 = CONST_DOUBLE_HIGH (op0); + + if (GET_CODE (op1) == CONST_DOUBLE) + l2 = CONST_DOUBLE_LOW (op1), h2 = CONST_DOUBLE_HIGH (op1); + else + l2 = INTVAL (op1), h2 = l2 < 0 ? -1 : 0; + + switch (code) + { + case MINUS: + /* A - B == A + (-B). */ + neg_double (l2, h2, &lv, &hv); + l2 = lv, h2 = hv; + + /* .. fall through ... */ + + case PLUS: + add_double (l1, h1, l2, h2, &lv, &hv); + break; + + case MULT: + mul_double (l1, h1, l2, h2, &lv, &hv); + break; + + case DIV: case MOD: case UDIV: case UMOD: + /* We'd need to include tree.h to do this and it doesn't seem worth + it. */ + return 0; + + case AND: + lv = l1 & l2, hv = h1 & h2; + break; + + case IOR: + lv = l1 | l2, hv = h1 | h2; + break; + + case XOR: + lv = l1 ^ l2, hv = h1 ^ h2; + break; + + case SMIN: + if (h1 < h2 + || (h1 == h2 + && ((unsigned HOST_WIDE_INT) l1 + < (unsigned HOST_WIDE_INT) l2))) + lv = l1, hv = h1; + else + lv = l2, hv = h2; + break; + + case SMAX: + if (h1 > h2 + || (h1 == h2 + && ((unsigned HOST_WIDE_INT) l1 + > (unsigned HOST_WIDE_INT) l2))) + lv = l1, hv = h1; + else + lv = l2, hv = h2; + break; + + case UMIN: + if ((unsigned HOST_WIDE_INT) h1 < (unsigned HOST_WIDE_INT) h2 + || (h1 == h2 + && ((unsigned HOST_WIDE_INT) l1 + < (unsigned HOST_WIDE_INT) l2))) + lv = l1, hv = h1; + else + lv = l2, hv = h2; + break; + + case UMAX: + if ((unsigned HOST_WIDE_INT) h1 > (unsigned HOST_WIDE_INT) h2 + || (h1 == h2 + && ((unsigned HOST_WIDE_INT) l1 + > (unsigned HOST_WIDE_INT) l2))) + lv = l1, hv = h1; + else + lv = l2, hv = h2; + break; + + case LSHIFTRT: case ASHIFTRT: + case ASHIFT: case LSHIFT: + case ROTATE: case ROTATERT: +#ifdef SHIFT_COUNT_TRUNCATED + l2 &= (GET_MODE_BITSIZE (mode) - 1), h2 = 0; +#endif + + if (h2 != 0 || l2 < 0 || l2 >= GET_MODE_BITSIZE (mode)) + return 0; + + if (code == LSHIFTRT || code == ASHIFTRT) + rshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv, + code == ASHIFTRT); + else if (code == ASHIFT || code == LSHIFT) + lshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv, + code == ASHIFT); + else if (code == ROTATE) + lrotate_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv); + else /* code == ROTATERT */ + rrotate_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv); + break; + + default: + return 0; + } + + return immed_double_const (lv, hv, mode); + } + + if (GET_CODE (op0) != CONST_INT || GET_CODE (op1) != CONST_INT + || width > HOST_BITS_PER_WIDE_INT || width == 0) + { + /* Even if we can't compute a constant result, + there are some cases worth simplifying. */ + + switch (code) + { + case PLUS: + /* In IEEE floating point, x+0 is not the same as x. Similarly + for the other optimizations below. */ + if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT + && GET_MODE_CLASS (mode) != MODE_INT) + break; + + if (op1 == CONST0_RTX (mode)) + return op0; + + /* ((-a) + b) -> (b - a) and similarly for (a + (-b)) */ + if (GET_CODE (op0) == NEG) + return cse_gen_binary (MINUS, mode, op1, XEXP (op0, 0)); + else if (GET_CODE (op1) == NEG) + return cse_gen_binary (MINUS, mode, op0, XEXP (op1, 0)); + + /* Handle both-operands-constant cases. We can only add + CONST_INTs to constants since the sum of relocatable symbols + can't be handled by most assemblers. */ + + if (CONSTANT_P (op0) && GET_CODE (op1) == CONST_INT) + return plus_constant (op0, INTVAL (op1)); + else if (CONSTANT_P (op1) && GET_CODE (op0) == CONST_INT) + return plus_constant (op1, INTVAL (op0)); + + /* If one of the operands is a PLUS or a MINUS, see if we can + simplify this by the associative law. + Don't use the associative law for floating point. + The inaccuracy makes it nonassociative, + and subtle programs can break if operations are associated. */ + + if ((GET_MODE_CLASS (mode) == MODE_INT + || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT) + && (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS + || GET_CODE (op1) == PLUS || GET_CODE (op1) == MINUS) + && (tem = simplify_plus_minus (code, mode, op0, op1)) != 0) + return tem; + break; + + case COMPARE: +#ifdef HAVE_cc0 + /* Convert (compare FOO (const_int 0)) to FOO unless we aren't + using cc0, in which case we want to leave it as a COMPARE + so we can distinguish it from a register-register-copy. + + In IEEE floating point, x-0 is not the same as x. */ + + if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT + || GET_MODE_CLASS (mode) == MODE_INT) + && op1 == CONST0_RTX (mode)) + return op0; +#else + /* Do nothing here. */ +#endif + break; + + case MINUS: + /* None of these optimizations can be done for IEEE + floating point. */ + if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT + && GET_MODE_CLASS (mode) != MODE_INT + && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT) + break; + + /* We can't assume x-x is 0 even with non-IEEE floating point. */ + if (rtx_equal_p (op0, op1) + && ! side_effects_p (op0) + && GET_MODE_CLASS (mode) != MODE_FLOAT + && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT) + return const0_rtx; + + /* Change subtraction from zero into negation. */ + if (op0 == CONST0_RTX (mode)) + return gen_rtx (NEG, mode, op1); + + /* (-1 - a) is ~a. */ + if (op0 == constm1_rtx) + return gen_rtx (NOT, mode, op1); + + /* Subtracting 0 has no effect. */ + if (op1 == CONST0_RTX (mode)) + return op0; + + /* (a - (-b)) -> (a + b). */ + if (GET_CODE (op1) == NEG) + return cse_gen_binary (PLUS, mode, op0, XEXP (op1, 0)); + + /* If one of the operands is a PLUS or a MINUS, see if we can + simplify this by the associative law. + Don't use the associative law for floating point. + The inaccuracy makes it nonassociative, + and subtle programs can break if operations are associated. */ + + if ((GET_MODE_CLASS (mode) == MODE_INT + || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT) + && (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS + || GET_CODE (op1) == PLUS || GET_CODE (op1) == MINUS) + && (tem = simplify_plus_minus (code, mode, op0, op1)) != 0) + return tem; + + /* Don't let a relocatable value get a negative coeff. */ + if (GET_CODE (op1) == CONST_INT) + return plus_constant (op0, - INTVAL (op1)); + break; + + case MULT: + if (op1 == constm1_rtx) + { + tem = simplify_unary_operation (NEG, mode, op0, mode); + + return tem ? tem : gen_rtx (NEG, mode, op0); + } + + /* In IEEE floating point, x*0 is not always 0. */ + if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT + || GET_MODE_CLASS (mode) == MODE_INT) + && op1 == CONST0_RTX (mode) + && ! side_effects_p (op0)) + return op1; + + /* In IEEE floating point, x*1 is not equivalent to x for nans. + However, ANSI says we can drop signals, + so we can do this anyway. */ + if (op1 == CONST1_RTX (mode)) + return op0; + + /* Convert multiply by constant power of two into shift. */ + if (GET_CODE (op1) == CONST_INT + && (val = exact_log2 (INTVAL (op1))) >= 0) + return gen_rtx (ASHIFT, mode, op0, GEN_INT (val)); + + if (GET_CODE (op1) == CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (op1)) == MODE_FLOAT) + { + REAL_VALUE_TYPE d; + jmp_buf handler; + int op1is2, op1ism1; + + if (setjmp (handler)) + return 0; + + set_float_handler (handler); + REAL_VALUE_FROM_CONST_DOUBLE (d, op1); + op1is2 = REAL_VALUES_EQUAL (d, dconst2); + op1ism1 = REAL_VALUES_EQUAL (d, dconstm1); + set_float_handler (NULL_PTR); + + /* x*2 is x+x and x*(-1) is -x */ + if (op1is2 && GET_MODE (op0) == mode) + return gen_rtx (PLUS, mode, op0, copy_rtx (op0)); + + else if (op1ism1 && GET_MODE (op0) == mode) + return gen_rtx (NEG, mode, op0); + } + break; + + case IOR: + if (op1 == const0_rtx) + return op0; + if (GET_CODE (op1) == CONST_INT + && (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode)) + return op1; + if (rtx_equal_p (op0, op1) && ! side_effects_p (op0)) + return op0; + /* A | (~A) -> -1 */ + if (((GET_CODE (op0) == NOT && rtx_equal_p (XEXP (op0, 0), op1)) + || (GET_CODE (op1) == NOT && rtx_equal_p (XEXP (op1, 0), op0))) + && ! side_effects_p (op0) + && GET_MODE_CLASS (mode) != MODE_CC) + return constm1_rtx; + break; + + case XOR: + if (op1 == const0_rtx) + return op0; + if (GET_CODE (op1) == CONST_INT + && (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode)) + return gen_rtx (NOT, mode, op0); + if (op0 == op1 && ! side_effects_p (op0) + && GET_MODE_CLASS (mode) != MODE_CC) + return const0_rtx; + break; + + case AND: + if (op1 == const0_rtx && ! side_effects_p (op0)) + return const0_rtx; + if (GET_CODE (op1) == CONST_INT + && (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode)) + return op0; + if (op0 == op1 && ! side_effects_p (op0) + && GET_MODE_CLASS (mode) != MODE_CC) + return op0; + /* A & (~A) -> 0 */ + if (((GET_CODE (op0) == NOT && rtx_equal_p (XEXP (op0, 0), op1)) + || (GET_CODE (op1) == NOT && rtx_equal_p (XEXP (op1, 0), op0))) + && ! side_effects_p (op0) + && GET_MODE_CLASS (mode) != MODE_CC) + return const0_rtx; + break; + + case UDIV: + /* Convert divide by power of two into shift (divide by 1 handled + below). */ + if (GET_CODE (op1) == CONST_INT + && (arg1 = exact_log2 (INTVAL (op1))) > 0) + return gen_rtx (LSHIFTRT, mode, op0, GEN_INT (arg1)); + + /* ... fall through ... */ + + case DIV: + if (op1 == CONST1_RTX (mode)) + return op0; + + /* In IEEE floating point, 0/x is not always 0. */ + if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT + || GET_MODE_CLASS (mode) == MODE_INT) + && op0 == CONST0_RTX (mode) + && ! side_effects_p (op1)) + return op0; + +#if 0 /* Turned off till an expert says this is a safe thing to do. */ +#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) + /* Change division by a constant into multiplication. */ + else if (GET_CODE (op1) == CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (op1)) == MODE_FLOAT + && op1 != CONST0_RTX (mode)) + { + REAL_VALUE_TYPE d; + REAL_VALUE_FROM_CONST_DOUBLE (d, op1); + if (REAL_VALUES_EQUAL (d, dconst0)) + abort(); +#if defined (REAL_ARITHMETIC) + REAL_ARITHMETIC (d, (int) RDIV_EXPR, dconst1, d); + return gen_rtx (MULT, mode, op0, + CONST_DOUBLE_FROM_REAL_VALUE (d, mode)); +#else + return gen_rtx (MULT, mode, op0, + CONST_DOUBLE_FROM_REAL_VALUE (1./d, mode)); + } +#endif +#endif +#endif + break; + + case UMOD: + /* Handle modulus by power of two (mod with 1 handled below). */ + if (GET_CODE (op1) == CONST_INT + && exact_log2 (INTVAL (op1)) > 0) + return gen_rtx (AND, mode, op0, GEN_INT (INTVAL (op1) - 1)); + + /* ... fall through ... */ + + case MOD: + if ((op0 == const0_rtx || op1 == const1_rtx) + && ! side_effects_p (op0) && ! side_effects_p (op1)) + return const0_rtx; + break; + + case ROTATERT: + case ROTATE: + /* Rotating ~0 always results in ~0. */ + if (GET_CODE (op0) == CONST_INT && width <= HOST_BITS_PER_WIDE_INT + && INTVAL (op0) == GET_MODE_MASK (mode) + && ! side_effects_p (op1)) + return op0; + + /* ... fall through ... */ + + case LSHIFT: + case ASHIFT: + case ASHIFTRT: + case LSHIFTRT: + if (op1 == const0_rtx) + return op0; + if (op0 == const0_rtx && ! side_effects_p (op1)) + return op0; + break; + + case SMIN: + if (width <= HOST_BITS_PER_WIDE_INT && GET_CODE (op1) == CONST_INT + && INTVAL (op1) == (HOST_WIDE_INT) 1 << (width -1) + && ! side_effects_p (op0)) + return op1; + else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0)) + return op0; + break; + + case SMAX: + if (width <= HOST_BITS_PER_WIDE_INT && GET_CODE (op1) == CONST_INT + && (INTVAL (op1) + == (unsigned HOST_WIDE_INT) GET_MODE_MASK (mode) >> 1) + && ! side_effects_p (op0)) + return op1; + else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0)) + return op0; + break; + + case UMIN: + if (op1 == const0_rtx && ! side_effects_p (op0)) + return op1; + else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0)) + return op0; + break; + + case UMAX: + if (op1 == constm1_rtx && ! side_effects_p (op0)) + return op1; + else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0)) + return op0; + break; + + default: + abort (); + } + + return 0; + } + + /* Get the integer argument values in two forms: + zero-extended in ARG0, ARG1 and sign-extended in ARG0S, ARG1S. */ + + arg0 = INTVAL (op0); + arg1 = INTVAL (op1); + + if (width < HOST_BITS_PER_WIDE_INT) + { + arg0 &= ((HOST_WIDE_INT) 1 << width) - 1; + arg1 &= ((HOST_WIDE_INT) 1 << width) - 1; + + arg0s = arg0; + if (arg0s & ((HOST_WIDE_INT) 1 << (width - 1))) + arg0s |= ((HOST_WIDE_INT) (-1) << width); + + arg1s = arg1; + if (arg1s & ((HOST_WIDE_INT) 1 << (width - 1))) + arg1s |= ((HOST_WIDE_INT) (-1) << width); + } + else + { + arg0s = arg0; + arg1s = arg1; + } + + /* Compute the value of the arithmetic. */ + + switch (code) + { + case PLUS: + val = arg0s + arg1s; + break; + + case MINUS: + val = arg0s - arg1s; + break; + + case MULT: + val = arg0s * arg1s; + break; + + case DIV: + if (arg1s == 0) + return 0; + val = arg0s / arg1s; + break; + + case MOD: + if (arg1s == 0) + return 0; + val = arg0s % arg1s; + break; + + case UDIV: + if (arg1 == 0) + return 0; + val = (unsigned HOST_WIDE_INT) arg0 / arg1; + break; + + case UMOD: + if (arg1 == 0) + return 0; + val = (unsigned HOST_WIDE_INT) arg0 % arg1; + break; + + case AND: + val = arg0 & arg1; + break; + + case IOR: + val = arg0 | arg1; + break; + + case XOR: + val = arg0 ^ arg1; + break; + + case LSHIFTRT: + /* If shift count is undefined, don't fold it; let the machine do + what it wants. But truncate it if the machine will do that. */ + if (arg1 < 0) + return 0; + +#ifdef SHIFT_COUNT_TRUNCATED + arg1 &= (BITS_PER_WORD - 1); +#endif + + if (arg1 >= width) + return 0; + + val = ((unsigned HOST_WIDE_INT) arg0) >> arg1; + break; + + case ASHIFT: + case LSHIFT: + if (arg1 < 0) + return 0; + +#ifdef SHIFT_COUNT_TRUNCATED + arg1 &= (BITS_PER_WORD - 1); +#endif + + if (arg1 >= width) + return 0; + + val = ((unsigned HOST_WIDE_INT) arg0) << arg1; + break; + + case ASHIFTRT: + if (arg1 < 0) + return 0; + +#ifdef SHIFT_COUNT_TRUNCATED + arg1 &= (BITS_PER_WORD - 1); +#endif + + if (arg1 >= width) + return 0; + + val = arg0s >> arg1; + + /* Bootstrap compiler may not have sign extended the right shift. + Manually extend the sign to insure bootstrap cc matches gcc. */ + if (arg0s < 0 && arg1 > 0) + val |= ((HOST_WIDE_INT) -1) << (HOST_BITS_PER_WIDE_INT - arg1); + + break; + + case ROTATERT: + if (arg1 < 0) + return 0; + + arg1 %= width; + val = ((((unsigned HOST_WIDE_INT) arg0) << (width - arg1)) + | (((unsigned HOST_WIDE_INT) arg0) >> arg1)); + break; + + case ROTATE: + if (arg1 < 0) + return 0; + + arg1 %= width; + val = ((((unsigned HOST_WIDE_INT) arg0) << arg1) + | (((unsigned HOST_WIDE_INT) arg0) >> (width - arg1))); + break; + + case COMPARE: + /* Do nothing here. */ + return 0; + + case SMIN: + val = arg0s <= arg1s ? arg0s : arg1s; + break; + + case UMIN: + val = ((unsigned HOST_WIDE_INT) arg0 + <= (unsigned HOST_WIDE_INT) arg1 ? arg0 : arg1); + break; + + case SMAX: + val = arg0s > arg1s ? arg0s : arg1s; + break; + + case UMAX: + val = ((unsigned HOST_WIDE_INT) arg0 + > (unsigned HOST_WIDE_INT) arg1 ? arg0 : arg1); + break; + + default: + abort (); + } + + /* Clear the bits that don't belong in our mode, unless they and our sign + bit are all one. So we get either a reasonable negative value or a + reasonable unsigned value for this mode. */ + if (width < HOST_BITS_PER_WIDE_INT + && ((val & ((HOST_WIDE_INT) (-1) << (width - 1))) + != ((HOST_WIDE_INT) (-1) << (width - 1)))) + val &= ((HOST_WIDE_INT) 1 << width) - 1; + + return GEN_INT (val); +} + +/* Simplify a PLUS or MINUS, at least one of whose operands may be another + PLUS or MINUS. + + Rather than test for specific case, we do this by a brute-force method + and do all possible simplifications until no more changes occur. Then + we rebuild the operation. */ + +static rtx +simplify_plus_minus (code, mode, op0, op1) + enum rtx_code code; + enum machine_mode mode; + rtx op0, op1; +{ + rtx ops[8]; + int negs[8]; + rtx result, tem; + int n_ops = 2, input_ops = 2, input_consts = 0, n_consts = 0; + int first = 1, negate = 0, changed; + int i, j; + + bzero (ops, sizeof ops); + + /* Set up the two operands and then expand them until nothing has been + changed. If we run out of room in our array, give up; this should + almost never happen. */ + + ops[0] = op0, ops[1] = op1, negs[0] = 0, negs[1] = (code == MINUS); + + changed = 1; + while (changed) + { + changed = 0; + + for (i = 0; i < n_ops; i++) + switch (GET_CODE (ops[i])) + { + case PLUS: + case MINUS: + if (n_ops == 7) + return 0; + + ops[n_ops] = XEXP (ops[i], 1); + negs[n_ops++] = GET_CODE (ops[i]) == MINUS ? !negs[i] : negs[i]; + ops[i] = XEXP (ops[i], 0); + input_ops++; + changed = 1; + break; + + case NEG: + ops[i] = XEXP (ops[i], 0); + negs[i] = ! negs[i]; + changed = 1; + break; + + case CONST: + ops[i] = XEXP (ops[i], 0); + input_consts++; + changed = 1; + break; + + case NOT: + /* ~a -> (-a - 1) */ + if (n_ops != 7) + { + ops[n_ops] = constm1_rtx; + negs[n_ops++] = negs[i]; + ops[i] = XEXP (ops[i], 0); + negs[i] = ! negs[i]; + changed = 1; + } + break; + + case CONST_INT: + if (negs[i]) + ops[i] = GEN_INT (- INTVAL (ops[i])), negs[i] = 0, changed = 1; + break; + } + } + + /* If we only have two operands, we can't do anything. */ + if (n_ops <= 2) + return 0; + + /* Now simplify each pair of operands until nothing changes. The first + time through just simplify constants against each other. */ + + changed = 1; + while (changed) + { + changed = first; + + for (i = 0; i < n_ops - 1; i++) + for (j = i + 1; j < n_ops; j++) + if (ops[i] != 0 && ops[j] != 0 + && (! first || (CONSTANT_P (ops[i]) && CONSTANT_P (ops[j])))) + { + rtx lhs = ops[i], rhs = ops[j]; + enum rtx_code ncode = PLUS; + + if (negs[i] && ! negs[j]) + lhs = ops[j], rhs = ops[i], ncode = MINUS; + else if (! negs[i] && negs[j]) + ncode = MINUS; + + tem = simplify_binary_operation (ncode, mode, lhs, rhs); + if (tem) + { + ops[i] = tem, ops[j] = 0; + negs[i] = negs[i] && negs[j]; + if (GET_CODE (tem) == NEG) + ops[i] = XEXP (tem, 0), negs[i] = ! negs[i]; + + if (GET_CODE (ops[i]) == CONST_INT && negs[i]) + ops[i] = GEN_INT (- INTVAL (ops[i])), negs[i] = 0; + changed = 1; + } + } + + first = 0; + } + + /* Pack all the operands to the lower-numbered entries and give up if + we didn't reduce the number of operands we had. Make sure we + count a CONST as two operands. If we have the same number of + operands, but have made more CONSTs than we had, this is also + an improvement, so accept it. */ + + for (i = 0, j = 0; j < n_ops; j++) + if (ops[j] != 0) + { + ops[i] = ops[j], negs[i++] = negs[j]; + if (GET_CODE (ops[j]) == CONST) + n_consts++; + } + + if (i + n_consts > input_ops + || (i + n_consts == input_ops && n_consts <= input_consts)) + return 0; + + n_ops = i; + + /* If we have a CONST_INT, put it last. */ + for (i = 0; i < n_ops - 1; i++) + if (GET_CODE (ops[i]) == CONST_INT) + { + tem = ops[n_ops - 1], ops[n_ops - 1] = ops[i] , ops[i] = tem; + j = negs[n_ops - 1], negs[n_ops - 1] = negs[i], negs[i] = j; + } + + /* Put a non-negated operand first. If there aren't any, make all + operands positive and negate the whole thing later. */ + for (i = 0; i < n_ops && negs[i]; i++) + ; + + if (i == n_ops) + { + for (i = 0; i < n_ops; i++) + negs[i] = 0; + negate = 1; + } + else if (i != 0) + { + tem = ops[0], ops[0] = ops[i], ops[i] = tem; + j = negs[0], negs[0] = negs[i], negs[i] = j; + } + + /* Now make the result by performing the requested operations. */ + result = ops[0]; + for (i = 1; i < n_ops; i++) + result = cse_gen_binary (negs[i] ? MINUS : PLUS, mode, result, ops[i]); + + return negate ? gen_rtx (NEG, mode, result) : result; +} + +/* Make a binary operation by properly ordering the operands and + seeing if the expression folds. */ + +static rtx +cse_gen_binary (code, mode, op0, op1) + enum rtx_code code; + enum machine_mode mode; + rtx op0, op1; +{ + rtx tem; + + /* Put complex operands first and constants second if commutative. */ + if (GET_RTX_CLASS (code) == 'c' + && ((CONSTANT_P (op0) && GET_CODE (op1) != CONST_INT) + || (GET_RTX_CLASS (GET_CODE (op0)) == 'o' + && GET_RTX_CLASS (GET_CODE (op1)) != 'o') + || (GET_CODE (op0) == SUBREG + && GET_RTX_CLASS (GET_CODE (SUBREG_REG (op0))) == 'o' + && GET_RTX_CLASS (GET_CODE (op1)) != 'o'))) + tem = op0, op0 = op1, op1 = tem; + + /* If this simplifies, do it. */ + tem = simplify_binary_operation (code, mode, op0, op1); + + if (tem) + return tem; + + /* Handle addition and subtraction of CONST_INT specially. Otherwise, + just form the operation. */ + + if (code == PLUS && GET_CODE (op1) == CONST_INT + && GET_MODE (op0) != VOIDmode) + return plus_constant (op0, INTVAL (op1)); + else if (code == MINUS && GET_CODE (op1) == CONST_INT + && GET_MODE (op0) != VOIDmode) + return plus_constant (op0, - INTVAL (op1)); + else + return gen_rtx (code, mode, op0, op1); +} + +/* Like simplify_binary_operation except used for relational operators. + MODE is the mode of the operands, not that of the result. */ + +rtx +simplify_relational_operation (code, mode, op0, op1) + enum rtx_code code; + enum machine_mode mode; + rtx op0, op1; +{ + register HOST_WIDE_INT arg0, arg1, arg0s, arg1s; + HOST_WIDE_INT val; + int width = GET_MODE_BITSIZE (mode); + + /* If op0 is a compare, extract the comparison arguments from it. */ + if (GET_CODE (op0) == COMPARE && op1 == const0_rtx) + op1 = XEXP (op0, 1), op0 = XEXP (op0, 0); + + /* What to do with MODE_CC isn't clear yet. + Let's make sure nothing erroneous is done. */ + if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC) + return 0; + + /* Unlike the arithmetic operations, we can do the comparison whether + or not WIDTH is larger than HOST_BITS_PER_WIDE_INT because the + CONST_INTs are to be understood as being infinite precision as + is the comparison. So there is no question of overflow. */ + + if (GET_CODE (op0) != CONST_INT || GET_CODE (op1) != CONST_INT || width == 0) + { + /* Even if we can't compute a constant result, + there are some cases worth simplifying. */ + + /* For non-IEEE floating-point, if the two operands are equal, we know + the result. */ + if (rtx_equal_p (op0, op1) + && (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT + || GET_MODE_CLASS (GET_MODE (op0)) != MODE_FLOAT)) + return (code == EQ || code == GE || code == LE || code == LEU + || code == GEU) ? const_true_rtx : const0_rtx; + +#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) + else if (GET_CODE (op0) == CONST_DOUBLE + && GET_CODE (op1) == CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT) + { + REAL_VALUE_TYPE d0, d1; + jmp_buf handler; + int op0lt, op1lt, equal; + + if (setjmp (handler)) + return 0; + + set_float_handler (handler); + REAL_VALUE_FROM_CONST_DOUBLE (d0, op0); + REAL_VALUE_FROM_CONST_DOUBLE (d1, op1); + equal = REAL_VALUES_EQUAL (d0, d1); + op0lt = REAL_VALUES_LESS (d0, d1); + op1lt = REAL_VALUES_LESS (d1, d0); + set_float_handler (NULL_PTR); + + switch (code) + { + case EQ: + return equal ? const_true_rtx : const0_rtx; + case NE: + return !equal ? const_true_rtx : const0_rtx; + case LE: + return equal || op0lt ? const_true_rtx : const0_rtx; + case LT: + return op0lt ? const_true_rtx : const0_rtx; + case GE: + return equal || op1lt ? const_true_rtx : const0_rtx; + case GT: + return op1lt ? const_true_rtx : const0_rtx; + } + } +#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */ + + else if (GET_MODE_CLASS (mode) == MODE_INT + && width > HOST_BITS_PER_WIDE_INT + && (GET_CODE (op0) == CONST_DOUBLE + || GET_CODE (op0) == CONST_INT) + && (GET_CODE (op1) == CONST_DOUBLE + || GET_CODE (op1) == CONST_INT)) + { + HOST_WIDE_INT h0, l0, h1, l1; + unsigned HOST_WIDE_INT uh0, ul0, uh1, ul1; + int op0lt, op0ltu, equal; + + if (GET_CODE (op0) == CONST_DOUBLE) + l0 = CONST_DOUBLE_LOW (op0), h0 = CONST_DOUBLE_HIGH (op0); + else + l0 = INTVAL (op0), h0 = l0 < 0 ? -1 : 0; + + if (GET_CODE (op1) == CONST_DOUBLE) + l1 = CONST_DOUBLE_LOW (op1), h1 = CONST_DOUBLE_HIGH (op1); + else + l1 = INTVAL (op1), h1 = l1 < 0 ? -1 : 0; + + uh0 = h0, ul0 = l0, uh1 = h1, ul1 = l1; + + equal = (h0 == h1 && l0 == l1); + op0lt = (h0 < h1 || (h0 == h1 && l0 < l1)); + op0ltu = (uh0 < uh1 || (uh0 == uh1 && ul0 < ul1)); + + switch (code) + { + case EQ: + return equal ? const_true_rtx : const0_rtx; + case NE: + return !equal ? const_true_rtx : const0_rtx; + case LE: + return equal || op0lt ? const_true_rtx : const0_rtx; + case LT: + return op0lt ? const_true_rtx : const0_rtx; + case GE: + return !op0lt ? const_true_rtx : const0_rtx; + case GT: + return !equal && !op0lt ? const_true_rtx : const0_rtx; + case LEU: + return equal || op0ltu ? const_true_rtx : const0_rtx; + case LTU: + return op0ltu ? const_true_rtx : const0_rtx; + case GEU: + return !op0ltu ? const_true_rtx : const0_rtx; + case GTU: + return !equal && !op0ltu ? const_true_rtx : const0_rtx; + } + } + + switch (code) + { + case EQ: + { +#if 0 + /* We can't make this assumption due to #pragma weak */ + if (CONSTANT_P (op0) && op1 == const0_rtx) + return const0_rtx; +#endif + if (NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx + /* On some machines, the ap reg can be 0 sometimes. */ + && op0 != arg_pointer_rtx) + return const0_rtx; + break; + } + + case NE: +#if 0 + /* We can't make this assumption due to #pragma weak */ + if (CONSTANT_P (op0) && op1 == const0_rtx) + return const_true_rtx; +#endif + if (NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx + /* On some machines, the ap reg can be 0 sometimes. */ + && op0 != arg_pointer_rtx) + return const_true_rtx; + break; + + case GEU: + /* Unsigned values are never negative, but we must be sure we are + actually comparing a value, not a CC operand. */ + if (op1 == const0_rtx + && GET_MODE_CLASS (mode) == MODE_INT) + return const_true_rtx; + break; + + case LTU: + if (op1 == const0_rtx + && GET_MODE_CLASS (mode) == MODE_INT) + return const0_rtx; + break; + + case LEU: + /* Unsigned values are never greater than the largest + unsigned value. */ + if (GET_CODE (op1) == CONST_INT + && INTVAL (op1) == GET_MODE_MASK (mode) + && GET_MODE_CLASS (mode) == MODE_INT) + return const_true_rtx; + break; + + case GTU: + if (GET_CODE (op1) == CONST_INT + && INTVAL (op1) == GET_MODE_MASK (mode) + && GET_MODE_CLASS (mode) == MODE_INT) + return const0_rtx; + break; + } + + return 0; + } + + /* Get the integer argument values in two forms: + zero-extended in ARG0, ARG1 and sign-extended in ARG0S, ARG1S. */ + + arg0 = INTVAL (op0); + arg1 = INTVAL (op1); + + if (width < HOST_BITS_PER_WIDE_INT) + { + arg0 &= ((HOST_WIDE_INT) 1 << width) - 1; + arg1 &= ((HOST_WIDE_INT) 1 << width) - 1; + + arg0s = arg0; + if (arg0s & ((HOST_WIDE_INT) 1 << (width - 1))) + arg0s |= ((HOST_WIDE_INT) (-1) << width); + + arg1s = arg1; + if (arg1s & ((HOST_WIDE_INT) 1 << (width - 1))) + arg1s |= ((HOST_WIDE_INT) (-1) << width); + } + else + { + arg0s = arg0; + arg1s = arg1; + } + + /* Compute the value of the arithmetic. */ + + switch (code) + { + case NE: + val = arg0 != arg1 ? STORE_FLAG_VALUE : 0; + break; + + case EQ: + val = arg0 == arg1 ? STORE_FLAG_VALUE : 0; + break; + + case LE: + val = arg0s <= arg1s ? STORE_FLAG_VALUE : 0; + break; + + case LT: + val = arg0s < arg1s ? STORE_FLAG_VALUE : 0; + break; + + case GE: + val = arg0s >= arg1s ? STORE_FLAG_VALUE : 0; + break; + + case GT: + val = arg0s > arg1s ? STORE_FLAG_VALUE : 0; + break; + + case LEU: + val = (((unsigned HOST_WIDE_INT) arg0) + <= ((unsigned HOST_WIDE_INT) arg1) ? STORE_FLAG_VALUE : 0); + break; + + case LTU: + val = (((unsigned HOST_WIDE_INT) arg0) + < ((unsigned HOST_WIDE_INT) arg1) ? STORE_FLAG_VALUE : 0); + break; + + case GEU: + val = (((unsigned HOST_WIDE_INT) arg0) + >= ((unsigned HOST_WIDE_INT) arg1) ? STORE_FLAG_VALUE : 0); + break; + + case GTU: + val = (((unsigned HOST_WIDE_INT) arg0) + > ((unsigned HOST_WIDE_INT) arg1) ? STORE_FLAG_VALUE : 0); + break; + + default: + abort (); + } + + /* Clear the bits that don't belong in our mode, unless they and our sign + bit are all one. So we get either a reasonable negative value or a + reasonable unsigned value for this mode. */ + if (width < HOST_BITS_PER_WIDE_INT + && ((val & ((HOST_WIDE_INT) (-1) << (width - 1))) + != ((HOST_WIDE_INT) (-1) << (width - 1)))) + val &= ((HOST_WIDE_INT) 1 << width) - 1; + + return GEN_INT (val); +} + +/* Simplify CODE, an operation with result mode MODE and three operands, + OP0, OP1, and OP2. OP0_MODE was the mode of OP0 before it became + a constant. Return 0 if no simplifications is possible. */ + +rtx +simplify_ternary_operation (code, mode, op0_mode, op0, op1, op2) + enum rtx_code code; + enum machine_mode mode, op0_mode; + rtx op0, op1, op2; +{ + int width = GET_MODE_BITSIZE (mode); + + /* VOIDmode means "infinite" precision. */ + if (width == 0) + width = HOST_BITS_PER_WIDE_INT; + + switch (code) + { + case SIGN_EXTRACT: + case ZERO_EXTRACT: + if (GET_CODE (op0) == CONST_INT + && GET_CODE (op1) == CONST_INT + && GET_CODE (op2) == CONST_INT + && INTVAL (op1) + INTVAL (op2) <= GET_MODE_BITSIZE (op0_mode) + && width <= HOST_BITS_PER_WIDE_INT) + { + /* Extracting a bit-field from a constant */ + HOST_WIDE_INT val = INTVAL (op0); + +#if BITS_BIG_ENDIAN + val >>= (GET_MODE_BITSIZE (op0_mode) - INTVAL (op2) - INTVAL (op1)); +#else + val >>= INTVAL (op2); +#endif + if (HOST_BITS_PER_WIDE_INT != INTVAL (op1)) + { + /* First zero-extend. */ + val &= ((HOST_WIDE_INT) 1 << INTVAL (op1)) - 1; + /* If desired, propagate sign bit. */ + if (code == SIGN_EXTRACT + && (val & ((HOST_WIDE_INT) 1 << (INTVAL (op1) - 1)))) + val |= ~ (((HOST_WIDE_INT) 1 << INTVAL (op1)) - 1); + } + + /* Clear the bits that don't belong in our mode, + unless they and our sign bit are all one. + So we get either a reasonable negative value or a reasonable + unsigned value for this mode. */ + if (width < HOST_BITS_PER_WIDE_INT + && ((val & ((HOST_WIDE_INT) (-1) << (width - 1))) + != ((HOST_WIDE_INT) (-1) << (width - 1)))) + val &= ((HOST_WIDE_INT) 1 << width) - 1; + + return GEN_INT (val); + } + break; + + case IF_THEN_ELSE: + if (GET_CODE (op0) == CONST_INT) + return op0 != const0_rtx ? op1 : op2; + break; + + default: + abort (); + } + + return 0; +} + +/* If X is a nontrivial arithmetic operation on an argument + for which a constant value can be determined, return + the result of operating on that value, as a constant. + Otherwise, return X, possibly with one or more operands + modified by recursive calls to this function. + + If X is a register whose contents are known, we do NOT + return those contents here. equiv_constant is called to + perform that task. + + INSN is the insn that we may be modifying. If it is 0, make a copy + of X before modifying it. */ + +static rtx +fold_rtx (x, insn) + rtx x; + rtx insn; +{ + register enum rtx_code code; + register enum machine_mode mode; + register char *fmt; + register int i; + rtx new = 0; + int copied = 0; + int must_swap = 0; + + /* Folded equivalents of first two operands of X. */ + rtx folded_arg0; + rtx folded_arg1; + + /* Constant equivalents of first three operands of X; + 0 when no such equivalent is known. */ + rtx const_arg0; + rtx const_arg1; + rtx const_arg2; + + /* The mode of the first operand of X. We need this for sign and zero + extends. */ + enum machine_mode mode_arg0; + + if (x == 0) + return x; + + mode = GET_MODE (x); + code = GET_CODE (x); + switch (code) + { + case CONST: + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + case LABEL_REF: + case REG: + /* No use simplifying an EXPR_LIST + since they are used only for lists of args + in a function call's REG_EQUAL note. */ + case EXPR_LIST: + return x; + +#ifdef HAVE_cc0 + case CC0: + return prev_insn_cc0; +#endif + + case PC: + /* If the next insn is a CODE_LABEL followed by a jump table, + PC's value is a LABEL_REF pointing to that label. That + lets us fold switch statements on the Vax. */ + if (insn && GET_CODE (insn) == JUMP_INSN) + { + rtx next = next_nonnote_insn (insn); + + if (next && GET_CODE (next) == CODE_LABEL + && NEXT_INSN (next) != 0 + && GET_CODE (NEXT_INSN (next)) == JUMP_INSN + && (GET_CODE (PATTERN (NEXT_INSN (next))) == ADDR_VEC + || GET_CODE (PATTERN (NEXT_INSN (next))) == ADDR_DIFF_VEC)) + return gen_rtx (LABEL_REF, Pmode, next); + } + break; + + case SUBREG: + /* See if we previously assigned a constant value to this SUBREG. */ + if ((new = lookup_as_function (x, CONST_INT)) != 0 + || (new = lookup_as_function (x, CONST_DOUBLE)) != 0) + return new; + + /* If this is a paradoxical SUBREG, we have no idea what value the + extra bits would have. However, if the operand is equivalent + to a SUBREG whose operand is the same as our mode, and all the + modes are within a word, we can just use the inner operand + because these SUBREGs just say how to treat the register. */ + + if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) + { + enum machine_mode imode = GET_MODE (SUBREG_REG (x)); + struct table_elt *elt; + + if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD + && GET_MODE_SIZE (imode) <= UNITS_PER_WORD + && (elt = lookup (SUBREG_REG (x), HASH (SUBREG_REG (x), imode), + imode)) != 0) + { + for (elt = elt->first_same_value; + elt; elt = elt->next_same_value) + if (GET_CODE (elt->exp) == SUBREG + && GET_MODE (SUBREG_REG (elt->exp)) == mode + && exp_equiv_p (elt->exp, elt->exp, 1, 0)) + return copy_rtx (SUBREG_REG (elt->exp)); + } + + return x; + } + + /* Fold SUBREG_REG. If it changed, see if we can simplify the SUBREG. + We might be able to if the SUBREG is extracting a single word in an + integral mode or extracting the low part. */ + + folded_arg0 = fold_rtx (SUBREG_REG (x), insn); + const_arg0 = equiv_constant (folded_arg0); + if (const_arg0) + folded_arg0 = const_arg0; + + if (folded_arg0 != SUBREG_REG (x)) + { + new = 0; + + if (GET_MODE_CLASS (mode) == MODE_INT + && GET_MODE_SIZE (mode) == UNITS_PER_WORD + && GET_MODE (SUBREG_REG (x)) != VOIDmode) + new = operand_subword (folded_arg0, SUBREG_WORD (x), 0, + GET_MODE (SUBREG_REG (x))); + if (new == 0 && subreg_lowpart_p (x)) + new = gen_lowpart_if_possible (mode, folded_arg0); + if (new) + return new; + } + + /* If this is a narrowing SUBREG and our operand is a REG, see if + we can find an equivalence for REG that is an arithmetic operation + in a wider mode where both operands are paradoxical SUBREGs + from objects of our result mode. In that case, we couldn't report + an equivalent value for that operation, since we don't know what the + extra bits will be. But we can find an equivalence for this SUBREG + by folding that operation is the narrow mode. This allows us to + fold arithmetic in narrow modes when the machine only supports + word-sized arithmetic. + + Also look for a case where we have a SUBREG whose operand is the + same as our result. If both modes are smaller than a word, we + are simply interpreting a register in different modes and we + can use the inner value. */ + + if (GET_CODE (folded_arg0) == REG + && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (folded_arg0)) + && subreg_lowpart_p (x)) + { + struct table_elt *elt; + + /* We can use HASH here since we know that canon_hash won't be + called. */ + elt = lookup (folded_arg0, + HASH (folded_arg0, GET_MODE (folded_arg0)), + GET_MODE (folded_arg0)); + + if (elt) + elt = elt->first_same_value; + + for (; elt; elt = elt->next_same_value) + { + enum rtx_code eltcode = GET_CODE (elt->exp); + + /* Just check for unary and binary operations. */ + if (GET_RTX_CLASS (GET_CODE (elt->exp)) == '1' + && GET_CODE (elt->exp) != SIGN_EXTEND + && GET_CODE (elt->exp) != ZERO_EXTEND + && GET_CODE (XEXP (elt->exp, 0)) == SUBREG + && GET_MODE (SUBREG_REG (XEXP (elt->exp, 0))) == mode) + { + rtx op0 = SUBREG_REG (XEXP (elt->exp, 0)); + + if (GET_CODE (op0) != REG && ! CONSTANT_P (op0)) + op0 = fold_rtx (op0, NULL_RTX); + + op0 = equiv_constant (op0); + if (op0) + new = simplify_unary_operation (GET_CODE (elt->exp), mode, + op0, mode); + } + else if ((GET_RTX_CLASS (GET_CODE (elt->exp)) == '2' + || GET_RTX_CLASS (GET_CODE (elt->exp)) == 'c') + && eltcode != DIV && eltcode != MOD + && eltcode != UDIV && eltcode != UMOD + && eltcode != ASHIFTRT && eltcode != LSHIFTRT + && eltcode != ROTATE && eltcode != ROTATERT + && ((GET_CODE (XEXP (elt->exp, 0)) == SUBREG + && (GET_MODE (SUBREG_REG (XEXP (elt->exp, 0))) + == mode)) + || CONSTANT_P (XEXP (elt->exp, 0))) + && ((GET_CODE (XEXP (elt->exp, 1)) == SUBREG + && (GET_MODE (SUBREG_REG (XEXP (elt->exp, 1))) + == mode)) + || CONSTANT_P (XEXP (elt->exp, 1)))) + { + rtx op0 = gen_lowpart_common (mode, XEXP (elt->exp, 0)); + rtx op1 = gen_lowpart_common (mode, XEXP (elt->exp, 1)); + + if (op0 && GET_CODE (op0) != REG && ! CONSTANT_P (op0)) + op0 = fold_rtx (op0, NULL_RTX); + + if (op0) + op0 = equiv_constant (op0); + + if (op1 && GET_CODE (op1) != REG && ! CONSTANT_P (op1)) + op1 = fold_rtx (op1, NULL_RTX); + + if (op1) + op1 = equiv_constant (op1); + + if (op0 && op1) + new = simplify_binary_operation (GET_CODE (elt->exp), mode, + op0, op1); + } + + else if (GET_CODE (elt->exp) == SUBREG + && GET_MODE (SUBREG_REG (elt->exp)) == mode + && (GET_MODE_SIZE (GET_MODE (folded_arg0)) + <= UNITS_PER_WORD) + && exp_equiv_p (elt->exp, elt->exp, 1, 0)) + new = copy_rtx (SUBREG_REG (elt->exp)); + + if (new) + return new; + } + } + + return x; + + case NOT: + case NEG: + /* If we have (NOT Y), see if Y is known to be (NOT Z). + If so, (NOT Y) simplifies to Z. Similarly for NEG. */ + new = lookup_as_function (XEXP (x, 0), code); + if (new) + return fold_rtx (copy_rtx (XEXP (new, 0)), insn); + break; + + case MEM: + /* If we are not actually processing an insn, don't try to find the + best address. Not only don't we care, but we could modify the + MEM in an invalid way since we have no insn to validate against. */ + if (insn != 0) + find_best_addr (insn, &XEXP (x, 0)); + + { + /* Even if we don't fold in the insn itself, + we can safely do so here, in hopes of getting a constant. */ + rtx addr = fold_rtx (XEXP (x, 0), NULL_RTX); + rtx base = 0; + HOST_WIDE_INT offset = 0; + + if (GET_CODE (addr) == REG + && REGNO_QTY_VALID_P (REGNO (addr)) + && GET_MODE (addr) == qty_mode[reg_qty[REGNO (addr)]] + && qty_const[reg_qty[REGNO (addr)]] != 0) + addr = qty_const[reg_qty[REGNO (addr)]]; + + /* If address is constant, split it into a base and integer offset. */ + if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF) + base = addr; + else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == PLUS + && GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT) + { + base = XEXP (XEXP (addr, 0), 0); + offset = INTVAL (XEXP (XEXP (addr, 0), 1)); + } + else if (GET_CODE (addr) == LO_SUM + && GET_CODE (XEXP (addr, 1)) == SYMBOL_REF) + base = XEXP (addr, 1); + + /* If this is a constant pool reference, we can fold it into its + constant to allow better value tracking. */ + if (base && GET_CODE (base) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (base)) + { + rtx constant = get_pool_constant (base); + enum machine_mode const_mode = get_pool_mode (base); + rtx new; + + if (CONSTANT_P (constant) && GET_CODE (constant) != CONST_INT) + constant_pool_entries_cost = COST (constant); + + /* If we are loading the full constant, we have an equivalence. */ + if (offset == 0 && mode == const_mode) + return constant; + + /* If this actually isn't a constant (wierd!), we can't do + anything. Otherwise, handle the two most common cases: + extracting a word from a multi-word constant, and extracting + the low-order bits. Other cases don't seem common enough to + worry about. */ + if (! CONSTANT_P (constant)) + return x; + + if (GET_MODE_CLASS (mode) == MODE_INT + && GET_MODE_SIZE (mode) == UNITS_PER_WORD + && offset % UNITS_PER_WORD == 0 + && (new = operand_subword (constant, + offset / UNITS_PER_WORD, + 0, const_mode)) != 0) + return new; + + if (((BYTES_BIG_ENDIAN + && offset == GET_MODE_SIZE (GET_MODE (constant)) - 1) + || (! BYTES_BIG_ENDIAN && offset == 0)) + && (new = gen_lowpart_if_possible (mode, constant)) != 0) + return new; + } + + /* If this is a reference to a label at a known position in a jump + table, we also know its value. */ + if (base && GET_CODE (base) == LABEL_REF) + { + rtx label = XEXP (base, 0); + rtx table_insn = NEXT_INSN (label); + + if (table_insn && GET_CODE (table_insn) == JUMP_INSN + && GET_CODE (PATTERN (table_insn)) == ADDR_VEC) + { + rtx table = PATTERN (table_insn); + + if (offset >= 0 + && (offset / GET_MODE_SIZE (GET_MODE (table)) + < XVECLEN (table, 0))) + return XVECEXP (table, 0, + offset / GET_MODE_SIZE (GET_MODE (table))); + } + if (table_insn && GET_CODE (table_insn) == JUMP_INSN + && GET_CODE (PATTERN (table_insn)) == ADDR_DIFF_VEC) + { + rtx table = PATTERN (table_insn); + + if (offset >= 0 + && (offset / GET_MODE_SIZE (GET_MODE (table)) + < XVECLEN (table, 1))) + { + offset /= GET_MODE_SIZE (GET_MODE (table)); + new = gen_rtx (MINUS, Pmode, XVECEXP (table, 1, offset), + XEXP (table, 0)); + + if (GET_MODE (table) != Pmode) + new = gen_rtx (TRUNCATE, GET_MODE (table), new); + + return new; + } + } + } + + return x; + } + } + + const_arg0 = 0; + const_arg1 = 0; + const_arg2 = 0; + mode_arg0 = VOIDmode; + + /* Try folding our operands. + Then see which ones have constant values known. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + { + rtx arg = XEXP (x, i); + rtx folded_arg = arg, const_arg = 0; + enum machine_mode mode_arg = GET_MODE (arg); + rtx cheap_arg, expensive_arg; + rtx replacements[2]; + int j; + + /* Most arguments are cheap, so handle them specially. */ + switch (GET_CODE (arg)) + { + case REG: + /* This is the same as calling equiv_constant; it is duplicated + here for speed. */ + if (REGNO_QTY_VALID_P (REGNO (arg)) + && qty_const[reg_qty[REGNO (arg)]] != 0 + && GET_CODE (qty_const[reg_qty[REGNO (arg)]]) != REG + && GET_CODE (qty_const[reg_qty[REGNO (arg)]]) != PLUS) + const_arg + = gen_lowpart_if_possible (GET_MODE (arg), + qty_const[reg_qty[REGNO (arg)]]); + break; + + case CONST: + case CONST_INT: + case SYMBOL_REF: + case LABEL_REF: + case CONST_DOUBLE: + const_arg = arg; + break; + +#ifdef HAVE_cc0 + case CC0: + folded_arg = prev_insn_cc0; + mode_arg = prev_insn_cc0_mode; + const_arg = equiv_constant (folded_arg); + break; +#endif + + default: + folded_arg = fold_rtx (arg, insn); + const_arg = equiv_constant (folded_arg); + } + + /* For the first three operands, see if the operand + is constant or equivalent to a constant. */ + switch (i) + { + case 0: + folded_arg0 = folded_arg; + const_arg0 = const_arg; + mode_arg0 = mode_arg; + break; + case 1: + folded_arg1 = folded_arg; + const_arg1 = const_arg; + break; + case 2: + const_arg2 = const_arg; + break; + } + + /* Pick the least expensive of the folded argument and an + equivalent constant argument. */ + if (const_arg == 0 || const_arg == folded_arg + || COST (const_arg) > COST (folded_arg)) + cheap_arg = folded_arg, expensive_arg = const_arg; + else + cheap_arg = const_arg, expensive_arg = folded_arg; + + /* Try to replace the operand with the cheapest of the two + possibilities. If it doesn't work and this is either of the first + two operands of a commutative operation, try swapping them. + If THAT fails, try the more expensive, provided it is cheaper + than what is already there. */ + + if (cheap_arg == XEXP (x, i)) + continue; + + if (insn == 0 && ! copied) + { + x = copy_rtx (x); + copied = 1; + } + + replacements[0] = cheap_arg, replacements[1] = expensive_arg; + for (j = 0; + j < 2 && replacements[j] + && COST (replacements[j]) < COST (XEXP (x, i)); + j++) + { + if (validate_change (insn, &XEXP (x, i), replacements[j], 0)) + break; + + if (code == NE || code == EQ || GET_RTX_CLASS (code) == 'c') + { + validate_change (insn, &XEXP (x, i), XEXP (x, 1 - i), 1); + validate_change (insn, &XEXP (x, 1 - i), replacements[j], 1); + + if (apply_change_group ()) + { + /* Swap them back to be invalid so that this loop can + continue and flag them to be swapped back later. */ + rtx tem; + + tem = XEXP (x, 0); XEXP (x, 0) = XEXP (x, 1); + XEXP (x, 1) = tem; + must_swap = 1; + break; + } + } + } + } + + else if (fmt[i] == 'E') + /* Don't try to fold inside of a vector of expressions. + Doing nothing is harmless. */ + ; + + /* If a commutative operation, place a constant integer as the second + operand unless the first operand is also a constant integer. Otherwise, + place any constant second unless the first operand is also a constant. */ + + if (code == EQ || code == NE || GET_RTX_CLASS (code) == 'c') + { + if (must_swap || (const_arg0 + && (const_arg1 == 0 + || (GET_CODE (const_arg0) == CONST_INT + && GET_CODE (const_arg1) != CONST_INT)))) + { + register rtx tem = XEXP (x, 0); + + if (insn == 0 && ! copied) + { + x = copy_rtx (x); + copied = 1; + } + + validate_change (insn, &XEXP (x, 0), XEXP (x, 1), 1); + validate_change (insn, &XEXP (x, 1), tem, 1); + if (apply_change_group ()) + { + tem = const_arg0, const_arg0 = const_arg1, const_arg1 = tem; + tem = folded_arg0, folded_arg0 = folded_arg1, folded_arg1 = tem; + } + } + } + + /* If X is an arithmetic operation, see if we can simplify it. */ + + switch (GET_RTX_CLASS (code)) + { + case '1': + /* We can't simplify extension ops unless we know the original mode. */ + if ((code == ZERO_EXTEND || code == SIGN_EXTEND) + && mode_arg0 == VOIDmode) + break; + new = simplify_unary_operation (code, mode, + const_arg0 ? const_arg0 : folded_arg0, + mode_arg0); + break; + + case '<': + /* See what items are actually being compared and set FOLDED_ARG[01] + to those values and CODE to the actual comparison code. If any are + constant, set CONST_ARG0 and CONST_ARG1 appropriately. We needn't + do anything if both operands are already known to be constant. */ + + if (const_arg0 == 0 || const_arg1 == 0) + { + struct table_elt *p0, *p1; + rtx true = const_true_rtx, false = const0_rtx; + enum machine_mode mode_arg1; + +#ifdef FLOAT_STORE_FLAG_VALUE + if (GET_MODE_CLASS (mode) == MODE_FLOAT) + { + true = immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, mode); + false = CONST0_RTX (mode); + } +#endif + + code = find_comparison_args (code, &folded_arg0, &folded_arg1, + &mode_arg0, &mode_arg1); + const_arg0 = equiv_constant (folded_arg0); + const_arg1 = equiv_constant (folded_arg1); + + /* If the mode is VOIDmode or a MODE_CC mode, we don't know + what kinds of things are being compared, so we can't do + anything with this comparison. */ + + if (mode_arg0 == VOIDmode || GET_MODE_CLASS (mode_arg0) == MODE_CC) + break; + + /* If we do not now have two constants being compared, see if we + can nevertheless deduce some things about the comparison. */ + if (const_arg0 == 0 || const_arg1 == 0) + { + /* Is FOLDED_ARG0 frame-pointer plus a constant? Or non-explicit + constant? These aren't zero, but we don't know their sign. */ + if (const_arg1 == const0_rtx + && (NONZERO_BASE_PLUS_P (folded_arg0) +#if 0 /* Sad to say, on sysvr4, #pragma weak can make a symbol address + come out as 0. */ + || GET_CODE (folded_arg0) == SYMBOL_REF +#endif + || GET_CODE (folded_arg0) == LABEL_REF + || GET_CODE (folded_arg0) == CONST)) + { + if (code == EQ) + return false; + else if (code == NE) + return true; + } + + /* See if the two operands are the same. We don't do this + for IEEE floating-point since we can't assume x == x + since x might be a NaN. */ + + if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT + || GET_MODE_CLASS (mode_arg0) != MODE_FLOAT) + && (folded_arg0 == folded_arg1 + || (GET_CODE (folded_arg0) == REG + && GET_CODE (folded_arg1) == REG + && (reg_qty[REGNO (folded_arg0)] + == reg_qty[REGNO (folded_arg1)])) + || ((p0 = lookup (folded_arg0, + (safe_hash (folded_arg0, mode_arg0) + % NBUCKETS), mode_arg0)) + && (p1 = lookup (folded_arg1, + (safe_hash (folded_arg1, mode_arg0) + % NBUCKETS), mode_arg0)) + && p0->first_same_value == p1->first_same_value))) + return ((code == EQ || code == LE || code == GE + || code == LEU || code == GEU) + ? true : false); + + /* If FOLDED_ARG0 is a register, see if the comparison we are + doing now is either the same as we did before or the reverse + (we only check the reverse if not floating-point). */ + else if (GET_CODE (folded_arg0) == REG) + { + int qty = reg_qty[REGNO (folded_arg0)]; + + if (REGNO_QTY_VALID_P (REGNO (folded_arg0)) + && (comparison_dominates_p (qty_comparison_code[qty], code) + || (comparison_dominates_p (qty_comparison_code[qty], + reverse_condition (code)) + && GET_MODE_CLASS (mode_arg0) == MODE_INT)) + && (rtx_equal_p (qty_comparison_const[qty], folded_arg1) + || (const_arg1 + && rtx_equal_p (qty_comparison_const[qty], + const_arg1)) + || (GET_CODE (folded_arg1) == REG + && (reg_qty[REGNO (folded_arg1)] + == qty_comparison_qty[qty])))) + return (comparison_dominates_p (qty_comparison_code[qty], + code) + ? true : false); + } + } + } + + /* If we are comparing against zero, see if the first operand is + equivalent to an IOR with a constant. If so, we may be able to + determine the result of this comparison. */ + + if (const_arg1 == const0_rtx) + { + rtx y = lookup_as_function (folded_arg0, IOR); + rtx inner_const; + + if (y != 0 + && (inner_const = equiv_constant (XEXP (y, 1))) != 0 + && GET_CODE (inner_const) == CONST_INT + && INTVAL (inner_const) != 0) + { + int sign_bitnum = GET_MODE_BITSIZE (mode_arg0) - 1; + int has_sign = (HOST_BITS_PER_WIDE_INT >= sign_bitnum + && (INTVAL (inner_const) + & ((HOST_WIDE_INT) 1 << sign_bitnum))); + rtx true = const_true_rtx, false = const0_rtx; + +#ifdef FLOAT_STORE_FLAG_VALUE + if (GET_MODE_CLASS (mode) == MODE_FLOAT) + { + true = immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, mode); + false = CONST0_RTX (mode); + } +#endif + + switch (code) + { + case EQ: + return false; + case NE: + return true; + case LT: case LE: + if (has_sign) + return true; + break; + case GT: case GE: + if (has_sign) + return false; + break; + } + } + } + + new = simplify_relational_operation (code, mode_arg0, + const_arg0 ? const_arg0 : folded_arg0, + const_arg1 ? const_arg1 : folded_arg1); +#ifdef FLOAT_STORE_FLAG_VALUE + if (new != 0 && GET_MODE_CLASS (mode) == MODE_FLOAT) + new = ((new == const0_rtx) ? CONST0_RTX (mode) + : immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, mode)); +#endif + break; + + case '2': + case 'c': + switch (code) + { + case PLUS: + /* If the second operand is a LABEL_REF, see if the first is a MINUS + with that LABEL_REF as its second operand. If so, the result is + the first operand of that MINUS. This handles switches with an + ADDR_DIFF_VEC table. */ + if (const_arg1 && GET_CODE (const_arg1) == LABEL_REF) + { + rtx y = lookup_as_function (folded_arg0, MINUS); + + if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF + && XEXP (XEXP (y, 1), 0) == XEXP (const_arg1, 0)) + return XEXP (y, 0); + } + goto from_plus; + + case MINUS: + /* If we have (MINUS Y C), see if Y is known to be (PLUS Z C2). + If so, produce (PLUS Z C2-C). */ + if (const_arg1 != 0 && GET_CODE (const_arg1) == CONST_INT) + { + rtx y = lookup_as_function (XEXP (x, 0), PLUS); + if (y && GET_CODE (XEXP (y, 1)) == CONST_INT) + return fold_rtx (plus_constant (y, -INTVAL (const_arg1)), + NULL_RTX); + } + + /* ... fall through ... */ + + from_plus: + case SMIN: case SMAX: case UMIN: case UMAX: + case IOR: case AND: case XOR: + case MULT: case DIV: case UDIV: + case ASHIFT: case LSHIFTRT: case ASHIFTRT: + /* If we have ( ) for an associative OP and REG + is known to be of similar form, we may be able to replace the + operation with a combined operation. This may eliminate the + intermediate operation if every use is simplified in this way. + Note that the similar optimization done by combine.c only works + if the intermediate operation's result has only one reference. */ + + if (GET_CODE (folded_arg0) == REG + && const_arg1 && GET_CODE (const_arg1) == CONST_INT) + { + int is_shift + = (code == ASHIFT || code == ASHIFTRT || code == LSHIFTRT); + rtx y = lookup_as_function (folded_arg0, code); + rtx inner_const; + enum rtx_code associate_code; + rtx new_const; + + if (y == 0 + || 0 == (inner_const + = equiv_constant (fold_rtx (XEXP (y, 1), 0))) + || GET_CODE (inner_const) != CONST_INT + /* If we have compiled a statement like + "if (x == (x & mask1))", and now are looking at + "x & mask2", we will have a case where the first operand + of Y is the same as our first operand. Unless we detect + this case, an infinite loop will result. */ + || XEXP (y, 0) == folded_arg0) + break; + + /* Don't associate these operations if they are a PLUS with the + same constant and it is a power of two. These might be doable + with a pre- or post-increment. Similarly for two subtracts of + identical powers of two with post decrement. */ + + if (code == PLUS && INTVAL (const_arg1) == INTVAL (inner_const) + && (0 +#if defined(HAVE_PRE_INCREMENT) || defined(HAVE_POST_INCREMENT) + || exact_log2 (INTVAL (const_arg1)) >= 0 +#endif +#if defined(HAVE_PRE_DECREMENT) || defined(HAVE_POST_DECREMENT) + || exact_log2 (- INTVAL (const_arg1)) >= 0 +#endif + )) + break; + + /* Compute the code used to compose the constants. For example, + A/C1/C2 is A/(C1 * C2), so if CODE == DIV, we want MULT. */ + + associate_code + = (code == MULT || code == DIV || code == UDIV ? MULT + : is_shift || code == PLUS || code == MINUS ? PLUS : code); + + new_const = simplify_binary_operation (associate_code, mode, + const_arg1, inner_const); + + if (new_const == 0) + break; + + /* If we are associating shift operations, don't let this + produce a shift of the size of the object or larger. + This could occur when we follow a sign-extend by a right + shift on a machine that does a sign-extend as a pair + of shifts. */ + + if (is_shift && GET_CODE (new_const) == CONST_INT + && INTVAL (new_const) >= GET_MODE_BITSIZE (mode)) + { + /* As an exception, we can turn an ASHIFTRT of this + form into a shift of the number of bits - 1. */ + if (code == ASHIFTRT) + new_const = GEN_INT (GET_MODE_BITSIZE (mode) - 1); + else + break; + } + + y = copy_rtx (XEXP (y, 0)); + + /* If Y contains our first operand (the most common way this + can happen is if Y is a MEM), we would do into an infinite + loop if we tried to fold it. So don't in that case. */ + + if (! reg_mentioned_p (folded_arg0, y)) + y = fold_rtx (y, insn); + + return cse_gen_binary (code, mode, y, new_const); + } + } + + new = simplify_binary_operation (code, mode, + const_arg0 ? const_arg0 : folded_arg0, + const_arg1 ? const_arg1 : folded_arg1); + break; + + case 'o': + /* (lo_sum (high X) X) is simply X. */ + if (code == LO_SUM && const_arg0 != 0 + && GET_CODE (const_arg0) == HIGH + && rtx_equal_p (XEXP (const_arg0, 0), const_arg1)) + return const_arg1; + break; + + case '3': + case 'b': + new = simplify_ternary_operation (code, mode, mode_arg0, + const_arg0 ? const_arg0 : folded_arg0, + const_arg1 ? const_arg1 : folded_arg1, + const_arg2 ? const_arg2 : XEXP (x, 2)); + break; + } + + return new ? new : x; +} + +/* Return a constant value currently equivalent to X. + Return 0 if we don't know one. */ + +static rtx +equiv_constant (x) + rtx x; +{ + if (GET_CODE (x) == REG + && REGNO_QTY_VALID_P (REGNO (x)) + && qty_const[reg_qty[REGNO (x)]]) + x = gen_lowpart_if_possible (GET_MODE (x), qty_const[reg_qty[REGNO (x)]]); + + if (x != 0 && CONSTANT_P (x)) + return x; + + /* If X is a MEM, try to fold it outside the context of any insn to see if + it might be equivalent to a constant. That handles the case where it + is a constant-pool reference. Then try to look it up in the hash table + in case it is something whose value we have seen before. */ + + if (GET_CODE (x) == MEM) + { + struct table_elt *elt; + + x = fold_rtx (x, NULL_RTX); + if (CONSTANT_P (x)) + return x; + + elt = lookup (x, safe_hash (x, GET_MODE (x)) % NBUCKETS, GET_MODE (x)); + if (elt == 0) + return 0; + + for (elt = elt->first_same_value; elt; elt = elt->next_same_value) + if (elt->is_const && CONSTANT_P (elt->exp)) + return elt->exp; + } + + return 0; +} + +/* Assuming that X is an rtx (e.g., MEM, REG or SUBREG) for a fixed-point + number, return an rtx (MEM, SUBREG, or CONST_INT) that refers to the + least-significant part of X. + MODE specifies how big a part of X to return. + + If the requested operation cannot be done, 0 is returned. + + This is similar to gen_lowpart in emit-rtl.c. */ + +rtx +gen_lowpart_if_possible (mode, x) + enum machine_mode mode; + register rtx x; +{ + rtx result = gen_lowpart_common (mode, x); + + if (result) + return result; + else if (GET_CODE (x) == MEM) + { + /* This is the only other case we handle. */ + register int offset = 0; + rtx new; + +#if WORDS_BIG_ENDIAN + offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD) + - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD)); +#endif +#if BYTES_BIG_ENDIAN + /* Adjust the address so that the address-after-the-data + is unchanged. */ + offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)) + - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x)))); +#endif + new = gen_rtx (MEM, mode, plus_constant (XEXP (x, 0), offset)); + if (! memory_address_p (mode, XEXP (new, 0))) + return 0; + MEM_VOLATILE_P (new) = MEM_VOLATILE_P (x); + RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (x); + MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (x); + return new; + } + else + return 0; +} + +/* Given INSN, a jump insn, TAKEN indicates if we are following the "taken" + branch. It will be zero if not. + + In certain cases, this can cause us to add an equivalence. For example, + if we are following the taken case of + if (i == 2) + we can add the fact that `i' and '2' are now equivalent. + + In any case, we can record that this comparison was passed. If the same + comparison is seen later, we will know its value. */ + +static void +record_jump_equiv (insn, taken) + rtx insn; + int taken; +{ + int cond_known_true; + rtx op0, op1; + enum machine_mode mode, mode0, mode1; + int reversed_nonequality = 0; + enum rtx_code code; + + /* Ensure this is the right kind of insn. */ + if (! condjump_p (insn) || simplejump_p (insn)) + return; + + /* See if this jump condition is known true or false. */ + if (taken) + cond_known_true = (XEXP (SET_SRC (PATTERN (insn)), 2) == pc_rtx); + else + cond_known_true = (XEXP (SET_SRC (PATTERN (insn)), 1) == pc_rtx); + + /* Get the type of comparison being done and the operands being compared. + If we had to reverse a non-equality condition, record that fact so we + know that it isn't valid for floating-point. */ + code = GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 0)); + op0 = fold_rtx (XEXP (XEXP (SET_SRC (PATTERN (insn)), 0), 0), insn); + op1 = fold_rtx (XEXP (XEXP (SET_SRC (PATTERN (insn)), 0), 1), insn); + + code = find_comparison_args (code, &op0, &op1, &mode0, &mode1); + if (! cond_known_true) + { + reversed_nonequality = (code != EQ && code != NE); + code = reverse_condition (code); + } + + /* The mode is the mode of the non-constant. */ + mode = mode0; + if (mode1 != VOIDmode) + mode = mode1; + + record_jump_cond (code, mode, op0, op1, reversed_nonequality); +} + +/* We know that comparison CODE applied to OP0 and OP1 in MODE is true. + REVERSED_NONEQUALITY is nonzero if CODE had to be swapped. + Make any useful entries we can with that information. Called from + above function and called recursively. */ + +static void +record_jump_cond (code, mode, op0, op1, reversed_nonequality) + enum rtx_code code; + enum machine_mode mode; + rtx op0, op1; + int reversed_nonequality; +{ + int op0_hash_code, op1_hash_code; + int op0_in_memory, op0_in_struct, op1_in_memory, op1_in_struct; + struct table_elt *op0_elt, *op1_elt; + + /* If OP0 and OP1 are known equal, and either is a paradoxical SUBREG, + we know that they are also equal in the smaller mode (this is also + true for all smaller modes whether or not there is a SUBREG, but + is not worth testing for with no SUBREG. */ + + /* Note that GET_MODE (op0) may not equal MODE. */ + if (code == EQ && GET_CODE (op0) == SUBREG + && (GET_MODE_SIZE (GET_MODE (op0)) + > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0))))) + { + enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op0)); + rtx tem = gen_lowpart_if_possible (inner_mode, op1); + + record_jump_cond (code, mode, SUBREG_REG (op0), + tem ? tem : gen_rtx (SUBREG, inner_mode, op1, 0), + reversed_nonequality); + } + + if (code == EQ && GET_CODE (op1) == SUBREG + && (GET_MODE_SIZE (GET_MODE (op1)) + > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1))))) + { + enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op1)); + rtx tem = gen_lowpart_if_possible (inner_mode, op0); + + record_jump_cond (code, mode, SUBREG_REG (op1), + tem ? tem : gen_rtx (SUBREG, inner_mode, op0, 0), + reversed_nonequality); + } + + /* Similarly, if this is an NE comparison, and either is a SUBREG + making a smaller mode, we know the whole thing is also NE. */ + + /* Note that GET_MODE (op0) may not equal MODE; + if we test MODE instead, we can get an infinite recursion + alternating between two modes each wider than MODE. */ + + if (code == NE && GET_CODE (op0) == SUBREG + && subreg_lowpart_p (op0) + && (GET_MODE_SIZE (GET_MODE (op0)) + < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0))))) + { + enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op0)); + rtx tem = gen_lowpart_if_possible (inner_mode, op1); + + record_jump_cond (code, mode, SUBREG_REG (op0), + tem ? tem : gen_rtx (SUBREG, inner_mode, op1, 0), + reversed_nonequality); + } + + if (code == NE && GET_CODE (op1) == SUBREG + && subreg_lowpart_p (op1) + && (GET_MODE_SIZE (GET_MODE (op1)) + < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1))))) + { + enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op1)); + rtx tem = gen_lowpart_if_possible (inner_mode, op0); + + record_jump_cond (code, mode, SUBREG_REG (op1), + tem ? tem : gen_rtx (SUBREG, inner_mode, op0, 0), + reversed_nonequality); + } + + /* Hash both operands. */ + + do_not_record = 0; + hash_arg_in_memory = 0; + hash_arg_in_struct = 0; + op0_hash_code = HASH (op0, mode); + op0_in_memory = hash_arg_in_memory; + op0_in_struct = hash_arg_in_struct; + + if (do_not_record) + return; + + do_not_record = 0; + hash_arg_in_memory = 0; + hash_arg_in_struct = 0; + op1_hash_code = HASH (op1, mode); + op1_in_memory = hash_arg_in_memory; + op1_in_struct = hash_arg_in_struct; + + if (do_not_record) + return; + + /* Look up both operands. */ + op0_elt = lookup (op0, op0_hash_code, mode); + op1_elt = lookup (op1, op1_hash_code, mode); + + /* If we aren't setting two things equal all we can do is save this + comparison. Similarly if this is floating-point. In the latter + case, OP1 might be zero and both -0.0 and 0.0 are equal to it. + If we record the equality, we might inadvertently delete code + whose intent was to change -0 to +0. */ + + if (code != EQ || GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT) + { + /* If we reversed a floating-point comparison, if OP0 is not a + register, or if OP1 is neither a register or constant, we can't + do anything. */ + + if (GET_CODE (op1) != REG) + op1 = equiv_constant (op1); + + if ((reversed_nonequality && GET_MODE_CLASS (mode) != MODE_INT) + || GET_CODE (op0) != REG || op1 == 0) + return; + + /* Put OP0 in the hash table if it isn't already. This gives it a + new quantity number. */ + if (op0_elt == 0) + { + if (insert_regs (op0, NULL_PTR, 0)) + { + rehash_using_reg (op0); + op0_hash_code = HASH (op0, mode); + } + + op0_elt = insert (op0, NULL_PTR, op0_hash_code, mode); + op0_elt->in_memory = op0_in_memory; + op0_elt->in_struct = op0_in_struct; + } + + qty_comparison_code[reg_qty[REGNO (op0)]] = code; + if (GET_CODE (op1) == REG) + { + /* Look it up again--in case op0 and op1 are the same. */ + op1_elt = lookup (op1, op1_hash_code, mode); + + /* Put OP1 in the hash table so it gets a new quantity number. */ + if (op1_elt == 0) + { + if (insert_regs (op1, NULL_PTR, 0)) + { + rehash_using_reg (op1); + op1_hash_code = HASH (op1, mode); + } + + op1_elt = insert (op1, NULL_PTR, op1_hash_code, mode); + op1_elt->in_memory = op1_in_memory; + op1_elt->in_struct = op1_in_struct; + } + + qty_comparison_qty[reg_qty[REGNO (op0)]] = reg_qty[REGNO (op1)]; + qty_comparison_const[reg_qty[REGNO (op0)]] = 0; + } + else + { + qty_comparison_qty[reg_qty[REGNO (op0)]] = -1; + qty_comparison_const[reg_qty[REGNO (op0)]] = op1; + } + + return; + } + + /* If both are equivalent, merge the two classes. Save this class for + `cse_set_around_loop'. */ + if (op0_elt && op1_elt) + { + merge_equiv_classes (op0_elt, op1_elt); + last_jump_equiv_class = op0_elt; + } + + /* For whichever side doesn't have an equivalence, make one. */ + if (op0_elt == 0) + { + if (insert_regs (op0, op1_elt, 0)) + { + rehash_using_reg (op0); + op0_hash_code = HASH (op0, mode); + } + + op0_elt = insert (op0, op1_elt, op0_hash_code, mode); + op0_elt->in_memory = op0_in_memory; + op0_elt->in_struct = op0_in_struct; + last_jump_equiv_class = op0_elt; + } + + if (op1_elt == 0) + { + if (insert_regs (op1, op0_elt, 0)) + { + rehash_using_reg (op1); + op1_hash_code = HASH (op1, mode); + } + + op1_elt = insert (op1, op0_elt, op1_hash_code, mode); + op1_elt->in_memory = op1_in_memory; + op1_elt->in_struct = op1_in_struct; + last_jump_equiv_class = op1_elt; + } +} + +/* CSE processing for one instruction. + First simplify sources and addresses of all assignments + in the instruction, using previously-computed equivalents values. + Then install the new sources and destinations in the table + of available values. + + If IN_LIBCALL_BLOCK is nonzero, don't record any equivalence made in + the insn. */ + +/* Data on one SET contained in the instruction. */ + +struct set +{ + /* The SET rtx itself. */ + rtx rtl; + /* The SET_SRC of the rtx (the original value, if it is changing). */ + rtx src; + /* The hash-table element for the SET_SRC of the SET. */ + struct table_elt *src_elt; + /* Hash code for the SET_SRC. */ + int src_hash_code; + /* Hash code for the SET_DEST. */ + int dest_hash_code; + /* The SET_DEST, with SUBREG, etc., stripped. */ + rtx inner_dest; + /* Place where the pointer to the INNER_DEST was found. */ + rtx *inner_dest_loc; + /* Nonzero if the SET_SRC is in memory. */ + char src_in_memory; + /* Nonzero if the SET_SRC is in a structure. */ + char src_in_struct; + /* Nonzero if the SET_SRC contains something + whose value cannot be predicted and understood. */ + char src_volatile; + /* Original machine mode, in case it becomes a CONST_INT. */ + enum machine_mode mode; + /* A constant equivalent for SET_SRC, if any. */ + rtx src_const; + /* Hash code of constant equivalent for SET_SRC. */ + int src_const_hash_code; + /* Table entry for constant equivalent for SET_SRC, if any. */ + struct table_elt *src_const_elt; +}; + +static void +cse_insn (insn, in_libcall_block) + rtx insn; + int in_libcall_block; +{ + register rtx x = PATTERN (insn); + rtx tem; + register int i; + register int n_sets = 0; + + /* Records what this insn does to set CC0. */ + rtx this_insn_cc0 = 0; + enum machine_mode this_insn_cc0_mode; + struct write_data writes_memory; + static struct write_data init = {0, 0, 0, 0}; + + rtx src_eqv = 0; + struct table_elt *src_eqv_elt = 0; + int src_eqv_volatile; + int src_eqv_in_memory; + int src_eqv_in_struct; + int src_eqv_hash_code; + + struct set *sets; + + this_insn = insn; + writes_memory = init; + + /* Find all the SETs and CLOBBERs in this instruction. + Record all the SETs in the array `set' and count them. + Also determine whether there is a CLOBBER that invalidates + all memory references, or all references at varying addresses. */ + + if (GET_CODE (x) == SET) + { + sets = (struct set *) alloca (sizeof (struct set)); + sets[0].rtl = x; + + /* Ignore SETs that are unconditional jumps. + They never need cse processing, so this does not hurt. + The reason is not efficiency but rather + so that we can test at the end for instructions + that have been simplified to unconditional jumps + and not be misled by unchanged instructions + that were unconditional jumps to begin with. */ + if (SET_DEST (x) == pc_rtx + && GET_CODE (SET_SRC (x)) == LABEL_REF) + ; + + /* Don't count call-insns, (set (reg 0) (call ...)), as a set. + The hard function value register is used only once, to copy to + someplace else, so it isn't worth cse'ing (and on 80386 is unsafe)! + Ensure we invalidate the destination register. On the 80386 no + other code would invalidate it since it is a fixed_reg. + We need not check the return of apply_change_group; see canon_reg. */ + + else if (GET_CODE (SET_SRC (x)) == CALL) + { + canon_reg (SET_SRC (x), insn); + apply_change_group (); + fold_rtx (SET_SRC (x), insn); + invalidate (SET_DEST (x)); + } + else + n_sets = 1; + } + else if (GET_CODE (x) == PARALLEL) + { + register int lim = XVECLEN (x, 0); + + sets = (struct set *) alloca (lim * sizeof (struct set)); + + /* Find all regs explicitly clobbered in this insn, + and ensure they are not replaced with any other regs + elsewhere in this insn. + When a reg that is clobbered is also used for input, + we should presume that that is for a reason, + and we should not substitute some other register + which is not supposed to be clobbered. + Therefore, this loop cannot be merged into the one below + because a CALL may precede a CLOBBER and refer to the + value clobbered. We must not let a canonicalization do + anything in that case. */ + for (i = 0; i < lim; i++) + { + register rtx y = XVECEXP (x, 0, i); + if (GET_CODE (y) == CLOBBER + && (GET_CODE (XEXP (y, 0)) == REG + || GET_CODE (XEXP (y, 0)) == SUBREG)) + invalidate (XEXP (y, 0)); + } + + for (i = 0; i < lim; i++) + { + register rtx y = XVECEXP (x, 0, i); + if (GET_CODE (y) == SET) + { + /* As above, we ignore unconditional jumps and call-insns and + ignore the result of apply_change_group. */ + if (GET_CODE (SET_SRC (y)) == CALL) + { + canon_reg (SET_SRC (y), insn); + apply_change_group (); + fold_rtx (SET_SRC (y), insn); + invalidate (SET_DEST (y)); + } + else if (SET_DEST (y) == pc_rtx + && GET_CODE (SET_SRC (y)) == LABEL_REF) + ; + else + sets[n_sets++].rtl = y; + } + else if (GET_CODE (y) == CLOBBER) + { + /* If we clobber memory, take note of that, + and canon the address. + This does nothing when a register is clobbered + because we have already invalidated the reg. */ + if (GET_CODE (XEXP (y, 0)) == MEM) + { + canon_reg (XEXP (y, 0), NULL_RTX); + note_mem_written (XEXP (y, 0), &writes_memory); + } + } + else if (GET_CODE (y) == USE + && ! (GET_CODE (XEXP (y, 0)) == REG + && REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER)) + canon_reg (y, NULL_RTX); + else if (GET_CODE (y) == CALL) + { + /* The result of apply_change_group can be ignored; see + canon_reg. */ + canon_reg (y, insn); + apply_change_group (); + fold_rtx (y, insn); + } + } + } + else if (GET_CODE (x) == CLOBBER) + { + if (GET_CODE (XEXP (x, 0)) == MEM) + { + canon_reg (XEXP (x, 0), NULL_RTX); + note_mem_written (XEXP (x, 0), &writes_memory); + } + } + + /* Canonicalize a USE of a pseudo register or memory location. */ + else if (GET_CODE (x) == USE + && ! (GET_CODE (XEXP (x, 0)) == REG + && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER)) + canon_reg (XEXP (x, 0), NULL_RTX); + else if (GET_CODE (x) == CALL) + { + /* The result of apply_change_group can be ignored; see canon_reg. */ + canon_reg (x, insn); + apply_change_group (); + fold_rtx (x, insn); + } + + if (n_sets == 1 && REG_NOTES (insn) != 0) + { + /* Store the equivalent value in SRC_EQV, if different. */ + rtx tem = find_reg_note (insn, REG_EQUAL, NULL_RTX); + + if (tem && ! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))) + src_eqv = canon_reg (XEXP (tem, 0), NULL_RTX); + } + + /* Canonicalize sources and addresses of destinations. + We do this in a separate pass to avoid problems when a MATCH_DUP is + present in the insn pattern. In that case, we want to ensure that + we don't break the duplicate nature of the pattern. So we will replace + both operands at the same time. Otherwise, we would fail to find an + equivalent substitution in the loop calling validate_change below. + + We used to suppress canonicalization of DEST if it appears in SRC, + but we don't do this any more. */ + + for (i = 0; i < n_sets; i++) + { + rtx dest = SET_DEST (sets[i].rtl); + rtx src = SET_SRC (sets[i].rtl); + rtx new = canon_reg (src, insn); + + if ((GET_CODE (new) == REG && GET_CODE (src) == REG + && ((REGNO (new) < FIRST_PSEUDO_REGISTER) + != (REGNO (src) < FIRST_PSEUDO_REGISTER))) + || insn_n_dups[recog_memoized (insn)] > 0) + validate_change (insn, &SET_SRC (sets[i].rtl), new, 1); + else + SET_SRC (sets[i].rtl) = new; + + if (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT) + { + validate_change (insn, &XEXP (dest, 1), + canon_reg (XEXP (dest, 1), insn), 1); + validate_change (insn, &XEXP (dest, 2), + canon_reg (XEXP (dest, 2), insn), 1); + } + + while (GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART + || GET_CODE (dest) == ZERO_EXTRACT + || GET_CODE (dest) == SIGN_EXTRACT) + dest = XEXP (dest, 0); + + if (GET_CODE (dest) == MEM) + canon_reg (dest, insn); + } + + /* Now that we have done all the replacements, we can apply the change + group and see if they all work. Note that this will cause some + canonicalizations that would have worked individually not to be applied + because some other canonicalization didn't work, but this should not + occur often. + + The result of apply_change_group can be ignored; see canon_reg. */ + + apply_change_group (); + + /* Set sets[i].src_elt to the class each source belongs to. + Detect assignments from or to volatile things + and set set[i] to zero so they will be ignored + in the rest of this function. + + Nothing in this loop changes the hash table or the register chains. */ + + for (i = 0; i < n_sets; i++) + { + register rtx src, dest; + register rtx src_folded; + register struct table_elt *elt = 0, *p; + enum machine_mode mode; + rtx src_eqv_here; + rtx src_const = 0; + rtx src_related = 0; + struct table_elt *src_const_elt = 0; + int src_cost = 10000, src_eqv_cost = 10000, src_folded_cost = 10000; + int src_related_cost = 10000, src_elt_cost = 10000; + /* Set non-zero if we need to call force_const_mem on with the + contents of src_folded before using it. */ + int src_folded_force_flag = 0; + + dest = SET_DEST (sets[i].rtl); + src = SET_SRC (sets[i].rtl); + + /* If SRC is a constant that has no machine mode, + hash it with the destination's machine mode. + This way we can keep different modes separate. */ + + mode = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src); + sets[i].mode = mode; + + if (src_eqv) + { + enum machine_mode eqvmode = mode; + if (GET_CODE (dest) == STRICT_LOW_PART) + eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0))); + do_not_record = 0; + hash_arg_in_memory = 0; + hash_arg_in_struct = 0; + src_eqv = fold_rtx (src_eqv, insn); + src_eqv_hash_code = HASH (src_eqv, eqvmode); + + /* Find the equivalence class for the equivalent expression. */ + + if (!do_not_record) + src_eqv_elt = lookup (src_eqv, src_eqv_hash_code, eqvmode); + + src_eqv_volatile = do_not_record; + src_eqv_in_memory = hash_arg_in_memory; + src_eqv_in_struct = hash_arg_in_struct; + } + + /* If this is a STRICT_LOW_PART assignment, src_eqv corresponds to the + value of the INNER register, not the destination. So it is not + a legal substitution for the source. But save it for later. */ + if (GET_CODE (dest) == STRICT_LOW_PART) + src_eqv_here = 0; + else + src_eqv_here = src_eqv; + + /* Simplify and foldable subexpressions in SRC. Then get the fully- + simplified result, which may not necessarily be valid. */ + src_folded = fold_rtx (src, insn); + + /* If storing a constant in a bitfield, pre-truncate the constant + so we will be able to record it later. */ + if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT + || GET_CODE (SET_DEST (sets[i].rtl)) == SIGN_EXTRACT) + { + rtx width = XEXP (SET_DEST (sets[i].rtl), 1); + + if (GET_CODE (src) == CONST_INT + && GET_CODE (width) == CONST_INT + && INTVAL (width) < HOST_BITS_PER_WIDE_INT + && (INTVAL (src) & ((HOST_WIDE_INT) (-1) << INTVAL (width)))) + src_folded + = GEN_INT (INTVAL (src) & (((HOST_WIDE_INT) 1 + << INTVAL (width)) - 1)); + } + + /* Compute SRC's hash code, and also notice if it + should not be recorded at all. In that case, + prevent any further processing of this assignment. */ + do_not_record = 0; + hash_arg_in_memory = 0; + hash_arg_in_struct = 0; + + sets[i].src = src; + sets[i].src_hash_code = HASH (src, mode); + sets[i].src_volatile = do_not_record; + sets[i].src_in_memory = hash_arg_in_memory; + sets[i].src_in_struct = hash_arg_in_struct; + +#if 0 + /* It is no longer clear why we used to do this, but it doesn't + appear to still be needed. So let's try without it since this + code hurts cse'ing widened ops. */ + /* If source is a perverse subreg (such as QI treated as an SI), + treat it as volatile. It may do the work of an SI in one context + where the extra bits are not being used, but cannot replace an SI + in general. */ + if (GET_CODE (src) == SUBREG + && (GET_MODE_SIZE (GET_MODE (src)) + > GET_MODE_SIZE (GET_MODE (SUBREG_REG (src))))) + sets[i].src_volatile = 1; +#endif + + /* Locate all possible equivalent forms for SRC. Try to replace + SRC in the insn with each cheaper equivalent. + + We have the following types of equivalents: SRC itself, a folded + version, a value given in a REG_EQUAL note, or a value related + to a constant. + + Each of these equivalents may be part of an additional class + of equivalents (if more than one is in the table, they must be in + the same class; we check for this). + + If the source is volatile, we don't do any table lookups. + + We note any constant equivalent for possible later use in a + REG_NOTE. */ + + if (!sets[i].src_volatile) + elt = lookup (src, sets[i].src_hash_code, mode); + + sets[i].src_elt = elt; + + if (elt && src_eqv_here && src_eqv_elt) + { + if (elt->first_same_value != src_eqv_elt->first_same_value) + { + /* The REG_EQUAL is indicating that two formerly distinct + classes are now equivalent. So merge them. */ + merge_equiv_classes (elt, src_eqv_elt); + src_eqv_hash_code = HASH (src_eqv, elt->mode); + src_eqv_elt = lookup (src_eqv, src_eqv_hash_code, elt->mode); + } + + src_eqv_here = 0; + } + + else if (src_eqv_elt) + elt = src_eqv_elt; + + /* Try to find a constant somewhere and record it in `src_const'. + Record its table element, if any, in `src_const_elt'. Look in + any known equivalences first. (If the constant is not in the + table, also set `sets[i].src_const_hash_code'). */ + if (elt) + for (p = elt->first_same_value; p; p = p->next_same_value) + if (p->is_const) + { + src_const = p->exp; + src_const_elt = elt; + break; + } + + if (src_const == 0 + && (CONSTANT_P (src_folded) + /* Consider (minus (label_ref L1) (label_ref L2)) as + "constant" here so we will record it. This allows us + to fold switch statements when an ADDR_DIFF_VEC is used. */ + || (GET_CODE (src_folded) == MINUS + && GET_CODE (XEXP (src_folded, 0)) == LABEL_REF + && GET_CODE (XEXP (src_folded, 1)) == LABEL_REF))) + src_const = src_folded, src_const_elt = elt; + else if (src_const == 0 && src_eqv_here && CONSTANT_P (src_eqv_here)) + src_const = src_eqv_here, src_const_elt = src_eqv_elt; + + /* If we don't know if the constant is in the table, get its + hash code and look it up. */ + if (src_const && src_const_elt == 0) + { + sets[i].src_const_hash_code = HASH (src_const, mode); + src_const_elt = lookup (src_const, sets[i].src_const_hash_code, + mode); + } + + sets[i].src_const = src_const; + sets[i].src_const_elt = src_const_elt; + + /* If the constant and our source are both in the table, mark them as + equivalent. Otherwise, if a constant is in the table but the source + isn't, set ELT to it. */ + if (src_const_elt && elt + && src_const_elt->first_same_value != elt->first_same_value) + merge_equiv_classes (elt, src_const_elt); + else if (src_const_elt && elt == 0) + elt = src_const_elt; + + /* See if there is a register linearly related to a constant + equivalent of SRC. */ + if (src_const + && (GET_CODE (src_const) == CONST + || (src_const_elt && src_const_elt->related_value != 0))) + { + src_related = use_related_value (src_const, src_const_elt); + if (src_related) + { + struct table_elt *src_related_elt + = lookup (src_related, HASH (src_related, mode), mode); + if (src_related_elt && elt) + { + if (elt->first_same_value + != src_related_elt->first_same_value) + /* This can occur when we previously saw a CONST + involving a SYMBOL_REF and then see the SYMBOL_REF + twice. Merge the involved classes. */ + merge_equiv_classes (elt, src_related_elt); + + src_related = 0; + src_related_elt = 0; + } + else if (src_related_elt && elt == 0) + elt = src_related_elt; + } + } + + /* See if we have a CONST_INT that is already in a register in a + wider mode. */ + + if (src_const && src_related == 0 && GET_CODE (src_const) == CONST_INT + && GET_MODE_CLASS (mode) == MODE_INT + && GET_MODE_BITSIZE (mode) < BITS_PER_WORD) + { + enum machine_mode wider_mode; + + for (wider_mode = GET_MODE_WIDER_MODE (mode); + GET_MODE_BITSIZE (wider_mode) <= BITS_PER_WORD + && src_related == 0; + wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + { + struct table_elt *const_elt + = lookup (src_const, HASH (src_const, wider_mode), wider_mode); + + if (const_elt == 0) + continue; + + for (const_elt = const_elt->first_same_value; + const_elt; const_elt = const_elt->next_same_value) + if (GET_CODE (const_elt->exp) == REG) + { + src_related = gen_lowpart_if_possible (mode, + const_elt->exp); + break; + } + } + } + + /* Another possibility is that we have an AND with a constant in + a mode narrower than a word. If so, it might have been generated + as part of an "if" which would narrow the AND. If we already + have done the AND in a wider mode, we can use a SUBREG of that + value. */ + + if (flag_expensive_optimizations && ! src_related + && GET_CODE (src) == AND && GET_CODE (XEXP (src, 1)) == CONST_INT + && GET_MODE_SIZE (mode) < UNITS_PER_WORD) + { + enum machine_mode tmode; + rtx new_and = gen_rtx (AND, VOIDmode, NULL_RTX, XEXP (src, 1)); + + for (tmode = GET_MODE_WIDER_MODE (mode); + GET_MODE_SIZE (tmode) <= UNITS_PER_WORD; + tmode = GET_MODE_WIDER_MODE (tmode)) + { + rtx inner = gen_lowpart_if_possible (tmode, XEXP (src, 0)); + struct table_elt *larger_elt; + + if (inner) + { + PUT_MODE (new_and, tmode); + XEXP (new_and, 0) = inner; + larger_elt = lookup (new_and, HASH (new_and, tmode), tmode); + if (larger_elt == 0) + continue; + + for (larger_elt = larger_elt->first_same_value; + larger_elt; larger_elt = larger_elt->next_same_value) + if (GET_CODE (larger_elt->exp) == REG) + { + src_related + = gen_lowpart_if_possible (mode, larger_elt->exp); + break; + } + + if (src_related) + break; + } + } + } + + if (src == src_folded) + src_folded = 0; + + /* At this point, ELT, if non-zero, points to a class of expressions + equivalent to the source of this SET and SRC, SRC_EQV, SRC_FOLDED, + and SRC_RELATED, if non-zero, each contain additional equivalent + expressions. Prune these latter expressions by deleting expressions + already in the equivalence class. + + Check for an equivalent identical to the destination. If found, + this is the preferred equivalent since it will likely lead to + elimination of the insn. Indicate this by placing it in + `src_related'. */ + + if (elt) elt = elt->first_same_value; + for (p = elt; p; p = p->next_same_value) + { + enum rtx_code code = GET_CODE (p->exp); + + /* If the expression is not valid, ignore it. Then we do not + have to check for validity below. In most cases, we can use + `rtx_equal_p', since canonicalization has already been done. */ + if (code != REG && ! exp_equiv_p (p->exp, p->exp, 1, 0)) + continue; + + if (src && GET_CODE (src) == code && rtx_equal_p (src, p->exp)) + src = 0; + else if (src_folded && GET_CODE (src_folded) == code + && rtx_equal_p (src_folded, p->exp)) + src_folded = 0; + else if (src_eqv_here && GET_CODE (src_eqv_here) == code + && rtx_equal_p (src_eqv_here, p->exp)) + src_eqv_here = 0; + else if (src_related && GET_CODE (src_related) == code + && rtx_equal_p (src_related, p->exp)) + src_related = 0; + + /* This is the same as the destination of the insns, we want + to prefer it. Copy it to src_related. The code below will + then give it a negative cost. */ + if (GET_CODE (dest) == code && rtx_equal_p (p->exp, dest)) + src_related = dest; + + } + + /* Find the cheapest valid equivalent, trying all the available + possibilities. Prefer items not in the hash table to ones + that are when they are equal cost. Note that we can never + worsen an insn as the current contents will also succeed. + If we find an equivalent identical to the destination, use it as best, + since this insn will probably be eliminated in that case. */ + if (src) + { + if (rtx_equal_p (src, dest)) + src_cost = -1; + else + src_cost = COST (src); + } + + if (src_eqv_here) + { + if (rtx_equal_p (src_eqv_here, dest)) + src_eqv_cost = -1; + else + src_eqv_cost = COST (src_eqv_here); + } + + if (src_folded) + { + if (rtx_equal_p (src_folded, dest)) + src_folded_cost = -1; + else + src_folded_cost = COST (src_folded); + } + + if (src_related) + { + if (rtx_equal_p (src_related, dest)) + src_related_cost = -1; + else + src_related_cost = COST (src_related); + } + + /* If this was an indirect jump insn, a known label will really be + cheaper even though it looks more expensive. */ + if (dest == pc_rtx && src_const && GET_CODE (src_const) == LABEL_REF) + src_folded = src_const, src_folded_cost = -1; + + /* Terminate loop when replacement made. This must terminate since + the current contents will be tested and will always be valid. */ + while (1) + { + rtx trial; + + /* Skip invalid entries. */ + while (elt && GET_CODE (elt->exp) != REG + && ! exp_equiv_p (elt->exp, elt->exp, 1, 0)) + elt = elt->next_same_value; + + if (elt) src_elt_cost = elt->cost; + + /* Find cheapest and skip it for the next time. For items + of equal cost, use this order: + src_folded, src, src_eqv, src_related and hash table entry. */ + if (src_folded_cost <= src_cost + && src_folded_cost <= src_eqv_cost + && src_folded_cost <= src_related_cost + && src_folded_cost <= src_elt_cost) + { + trial = src_folded, src_folded_cost = 10000; + if (src_folded_force_flag) + trial = force_const_mem (mode, trial); + } + else if (src_cost <= src_eqv_cost + && src_cost <= src_related_cost + && src_cost <= src_elt_cost) + trial = src, src_cost = 10000; + else if (src_eqv_cost <= src_related_cost + && src_eqv_cost <= src_elt_cost) + trial = src_eqv_here, src_eqv_cost = 10000; + else if (src_related_cost <= src_elt_cost) + trial = src_related, src_related_cost = 10000; + else + { + trial = copy_rtx (elt->exp); + elt = elt->next_same_value; + src_elt_cost = 10000; + } + + /* We don't normally have an insn matching (set (pc) (pc)), so + check for this separately here. We will delete such an + insn below. + + Tablejump insns contain a USE of the table, so simply replacing + the operand with the constant won't match. This is simply an + unconditional branch, however, and is therefore valid. Just + insert the substitution here and we will delete and re-emit + the insn later. */ + + if (n_sets == 1 && dest == pc_rtx + && (trial == pc_rtx + || (GET_CODE (trial) == LABEL_REF + && ! condjump_p (insn)))) + { + /* If TRIAL is a label in front of a jump table, we are + really falling through the switch (this is how casesi + insns work), so we must branch around the table. */ + if (GET_CODE (trial) == CODE_LABEL + && NEXT_INSN (trial) != 0 + && GET_CODE (NEXT_INSN (trial)) == JUMP_INSN + && (GET_CODE (PATTERN (NEXT_INSN (trial))) == ADDR_DIFF_VEC + || GET_CODE (PATTERN (NEXT_INSN (trial))) == ADDR_VEC)) + + trial = gen_rtx (LABEL_REF, Pmode, get_label_after (trial)); + + SET_SRC (sets[i].rtl) = trial; + break; + } + + /* Look for a substitution that makes a valid insn. */ + else if (validate_change (insn, &SET_SRC (sets[i].rtl), trial, 0)) + { + /* The result of apply_change_group can be ignored; see + canon_reg. */ + + validate_change (insn, &SET_SRC (sets[i].rtl), + canon_reg (SET_SRC (sets[i].rtl), insn), + 1); + apply_change_group (); + break; + } + + /* If we previously found constant pool entries for + constants and this is a constant, try making a + pool entry. Put it in src_folded unless we already have done + this since that is where it likely came from. */ + + else if (constant_pool_entries_cost + && CONSTANT_P (trial) + && (src_folded == 0 || GET_CODE (src_folded) != MEM) + && GET_MODE_CLASS (mode) != MODE_CC) + { + src_folded_force_flag = 1; + src_folded = trial; + src_folded_cost = constant_pool_entries_cost; + } + } + + src = SET_SRC (sets[i].rtl); + + /* In general, it is good to have a SET with SET_SRC == SET_DEST. + However, there is an important exception: If both are registers + that are not the head of their equivalence class, replace SET_SRC + with the head of the class. If we do not do this, we will have + both registers live over a portion of the basic block. This way, + their lifetimes will likely abut instead of overlapping. */ + if (GET_CODE (dest) == REG + && REGNO_QTY_VALID_P (REGNO (dest)) + && qty_mode[reg_qty[REGNO (dest)]] == GET_MODE (dest) + && qty_first_reg[reg_qty[REGNO (dest)]] != REGNO (dest) + && GET_CODE (src) == REG && REGNO (src) == REGNO (dest) + /* Don't do this if the original insn had a hard reg as + SET_SRC. */ + && (GET_CODE (sets[i].src) != REG + || REGNO (sets[i].src) >= FIRST_PSEUDO_REGISTER)) + /* We can't call canon_reg here because it won't do anything if + SRC is a hard register. */ + { + int first = qty_first_reg[reg_qty[REGNO (src)]]; + + src = SET_SRC (sets[i].rtl) + = first >= FIRST_PSEUDO_REGISTER ? regno_reg_rtx[first] + : gen_rtx (REG, GET_MODE (src), first); + + /* If we had a constant that is cheaper than what we are now + setting SRC to, use that constant. We ignored it when we + thought we could make this into a no-op. */ + if (src_const && COST (src_const) < COST (src) + && validate_change (insn, &SET_SRC (sets[i].rtl), src_const, 0)) + src = src_const; + } + + /* If we made a change, recompute SRC values. */ + if (src != sets[i].src) + { + do_not_record = 0; + hash_arg_in_memory = 0; + hash_arg_in_struct = 0; + sets[i].src = src; + sets[i].src_hash_code = HASH (src, mode); + sets[i].src_volatile = do_not_record; + sets[i].src_in_memory = hash_arg_in_memory; + sets[i].src_in_struct = hash_arg_in_struct; + sets[i].src_elt = lookup (src, sets[i].src_hash_code, mode); + } + + /* If this is a single SET, we are setting a register, and we have an + equivalent constant, we want to add a REG_NOTE. We don't want + to write a REG_EQUAL note for a constant pseudo since verifying that + that pseudo hasn't been eliminated is a pain. Such a note also + won't help anything. */ + if (n_sets == 1 && src_const && GET_CODE (dest) == REG + && GET_CODE (src_const) != REG) + { + rtx tem = find_reg_note (insn, REG_EQUAL, NULL_RTX); + + /* Record the actual constant value in a REG_EQUAL note, making + a new one if one does not already exist. */ + if (tem) + XEXP (tem, 0) = src_const; + else + REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL, + src_const, REG_NOTES (insn)); + + /* If storing a constant value in a register that + previously held the constant value 0, + record this fact with a REG_WAS_0 note on this insn. + + Note that the *register* is required to have previously held 0, + not just any register in the quantity and we must point to the + insn that set that register to zero. + + Rather than track each register individually, we just see if + the last set for this quantity was for this register. */ + + if (REGNO_QTY_VALID_P (REGNO (dest)) + && qty_const[reg_qty[REGNO (dest)]] == const0_rtx) + { + /* See if we previously had a REG_WAS_0 note. */ + rtx note = find_reg_note (insn, REG_WAS_0, NULL_RTX); + rtx const_insn = qty_const_insn[reg_qty[REGNO (dest)]]; + + if ((tem = single_set (const_insn)) != 0 + && rtx_equal_p (SET_DEST (tem), dest)) + { + if (note) + XEXP (note, 0) = const_insn; + else + REG_NOTES (insn) = gen_rtx (INSN_LIST, REG_WAS_0, + const_insn, REG_NOTES (insn)); + } + } + } + + /* Now deal with the destination. */ + do_not_record = 0; + sets[i].inner_dest_loc = &SET_DEST (sets[0].rtl); + + /* Look within any SIGN_EXTRACT or ZERO_EXTRACT + to the MEM or REG within it. */ + while (GET_CODE (dest) == SIGN_EXTRACT + || GET_CODE (dest) == ZERO_EXTRACT + || GET_CODE (dest) == SUBREG + || GET_CODE (dest) == STRICT_LOW_PART) + { + sets[i].inner_dest_loc = &XEXP (dest, 0); + dest = XEXP (dest, 0); + } + + sets[i].inner_dest = dest; + + if (GET_CODE (dest) == MEM) + { + dest = fold_rtx (dest, insn); + + /* Decide whether we invalidate everything in memory, + or just things at non-fixed places. + Writing a large aggregate must invalidate everything + because we don't know how long it is. */ + note_mem_written (dest, &writes_memory); + } + + /* Compute the hash code of the destination now, + before the effects of this instruction are recorded, + since the register values used in the address computation + are those before this instruction. */ + sets[i].dest_hash_code = HASH (dest, mode); + + /* Don't enter a bit-field in the hash table + because the value in it after the store + may not equal what was stored, due to truncation. */ + + if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT + || GET_CODE (SET_DEST (sets[i].rtl)) == SIGN_EXTRACT) + { + rtx width = XEXP (SET_DEST (sets[i].rtl), 1); + + if (src_const != 0 && GET_CODE (src_const) == CONST_INT + && GET_CODE (width) == CONST_INT + && INTVAL (width) < HOST_BITS_PER_WIDE_INT + && ! (INTVAL (src_const) + & ((HOST_WIDE_INT) (-1) << INTVAL (width)))) + /* Exception: if the value is constant, + and it won't be truncated, record it. */ + ; + else + { + /* This is chosen so that the destination will be invalidated + but no new value will be recorded. + We must invalidate because sometimes constant + values can be recorded for bitfields. */ + sets[i].src_elt = 0; + sets[i].src_volatile = 1; + src_eqv = 0; + src_eqv_elt = 0; + } + } + + /* If only one set in a JUMP_INSN and it is now a no-op, we can delete + the insn. */ + else if (n_sets == 1 && dest == pc_rtx && src == pc_rtx) + { + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + cse_jumps_altered = 1; + /* One less use of the label this insn used to jump to. */ + --LABEL_NUSES (JUMP_LABEL (insn)); + /* No more processing for this set. */ + sets[i].rtl = 0; + } + + /* If this SET is now setting PC to a label, we know it used to + be a conditional or computed branch. So we see if we can follow + it. If it was a computed branch, delete it and re-emit. */ + else if (dest == pc_rtx && GET_CODE (src) == LABEL_REF) + { + rtx p; + + /* If this is not in the format for a simple branch and + we are the only SET in it, re-emit it. */ + if (! simplejump_p (insn) && n_sets == 1) + { + rtx new = emit_jump_insn_before (gen_jump (XEXP (src, 0)), insn); + JUMP_LABEL (new) = XEXP (src, 0); + LABEL_NUSES (XEXP (src, 0))++; + delete_insn (insn); + insn = new; + } + else + /* Otherwise, force rerecognition, since it probably had + a different pattern before. + This shouldn't really be necessary, since whatever + changed the source value above should have done this. + Until the right place is found, might as well do this here. */ + INSN_CODE (insn) = -1; + + /* Now that we've converted this jump to an unconditional jump, + there is dead code after it. Delete the dead code until we + reach a BARRIER, the end of the function, or a label. Do + not delete NOTEs except for NOTE_INSN_DELETED since later + phases assume these notes are retained. */ + + p = insn; + + while (NEXT_INSN (p) != 0 + && GET_CODE (NEXT_INSN (p)) != BARRIER + && GET_CODE (NEXT_INSN (p)) != CODE_LABEL) + { + if (GET_CODE (NEXT_INSN (p)) != NOTE + || NOTE_LINE_NUMBER (NEXT_INSN (p)) == NOTE_INSN_DELETED) + delete_insn (NEXT_INSN (p)); + else + p = NEXT_INSN (p); + } + + /* If we don't have a BARRIER immediately after INSN, put one there. + Much code assumes that there are no NOTEs between a JUMP_INSN and + BARRIER. */ + + if (NEXT_INSN (insn) == 0 + || GET_CODE (NEXT_INSN (insn)) != BARRIER) + emit_barrier_after (insn); + + /* We might have two BARRIERs separated by notes. Delete the second + one if so. */ + + if (p != insn && NEXT_INSN (p) != 0 + && GET_CODE (NEXT_INSN (p)) == BARRIER) + delete_insn (NEXT_INSN (p)); + + cse_jumps_altered = 1; + sets[i].rtl = 0; + } + + /* If destination is volatile, invalidate it and then do no further + processing for this assignment. */ + + else if (do_not_record) + { + if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG + || GET_CODE (dest) == MEM) + invalidate (dest); + sets[i].rtl = 0; + } + + if (sets[i].rtl != 0 && dest != SET_DEST (sets[i].rtl)) + sets[i].dest_hash_code = HASH (SET_DEST (sets[i].rtl), mode); + +#ifdef HAVE_cc0 + /* If setting CC0, record what it was set to, or a constant, if it + is equivalent to a constant. If it is being set to a floating-point + value, make a COMPARE with the appropriate constant of 0. If we + don't do this, later code can interpret this as a test against + const0_rtx, which can cause problems if we try to put it into an + insn as a floating-point operand. */ + if (dest == cc0_rtx) + { + this_insn_cc0 = src_const && mode != VOIDmode ? src_const : src; + this_insn_cc0_mode = mode; + if (GET_MODE_CLASS (mode) == MODE_FLOAT) + this_insn_cc0 = gen_rtx (COMPARE, VOIDmode, this_insn_cc0, + CONST0_RTX (mode)); + } +#endif + } + + /* Now enter all non-volatile source expressions in the hash table + if they are not already present. + Record their equivalence classes in src_elt. + This way we can insert the corresponding destinations into + the same classes even if the actual sources are no longer in them + (having been invalidated). */ + + if (src_eqv && src_eqv_elt == 0 && sets[0].rtl != 0 && ! src_eqv_volatile + && ! rtx_equal_p (src_eqv, SET_DEST (sets[0].rtl))) + { + register struct table_elt *elt; + register struct table_elt *classp = sets[0].src_elt; + rtx dest = SET_DEST (sets[0].rtl); + enum machine_mode eqvmode = GET_MODE (dest); + + if (GET_CODE (dest) == STRICT_LOW_PART) + { + eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0))); + classp = 0; + } + if (insert_regs (src_eqv, classp, 0)) + src_eqv_hash_code = HASH (src_eqv, eqvmode); + elt = insert (src_eqv, classp, src_eqv_hash_code, eqvmode); + elt->in_memory = src_eqv_in_memory; + elt->in_struct = src_eqv_in_struct; + src_eqv_elt = elt; + } + + for (i = 0; i < n_sets; i++) + if (sets[i].rtl && ! sets[i].src_volatile + && ! rtx_equal_p (SET_SRC (sets[i].rtl), SET_DEST (sets[i].rtl))) + { + if (GET_CODE (SET_DEST (sets[i].rtl)) == STRICT_LOW_PART) + { + /* REG_EQUAL in setting a STRICT_LOW_PART + gives an equivalent for the entire destination register, + not just for the subreg being stored in now. + This is a more interesting equivalence, so we arrange later + to treat the entire reg as the destination. */ + sets[i].src_elt = src_eqv_elt; + sets[i].src_hash_code = src_eqv_hash_code; + } + else + { + /* Insert source and constant equivalent into hash table, if not + already present. */ + register struct table_elt *classp = src_eqv_elt; + register rtx src = sets[i].src; + register rtx dest = SET_DEST (sets[i].rtl); + enum machine_mode mode + = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src); + + if (sets[i].src_elt == 0) + { + register struct table_elt *elt; + + /* Note that these insert_regs calls cannot remove + any of the src_elt's, because they would have failed to + match if not still valid. */ + if (insert_regs (src, classp, 0)) + sets[i].src_hash_code = HASH (src, mode); + elt = insert (src, classp, sets[i].src_hash_code, mode); + elt->in_memory = sets[i].src_in_memory; + elt->in_struct = sets[i].src_in_struct; + sets[i].src_elt = classp = elt; + } + + if (sets[i].src_const && sets[i].src_const_elt == 0 + && src != sets[i].src_const + && ! rtx_equal_p (sets[i].src_const, src)) + sets[i].src_elt = insert (sets[i].src_const, classp, + sets[i].src_const_hash_code, mode); + } + } + else if (sets[i].src_elt == 0) + /* If we did not insert the source into the hash table (e.g., it was + volatile), note the equivalence class for the REG_EQUAL value, if any, + so that the destination goes into that class. */ + sets[i].src_elt = src_eqv_elt; + + invalidate_from_clobbers (&writes_memory, x); + + /* Some registers are invalidated by subroutine calls. Memory is + invalidated by non-constant calls. */ + + if (GET_CODE (insn) == CALL_INSN) + { + static struct write_data everything = {0, 1, 1, 1}; + + if (! CONST_CALL_P (insn)) + invalidate_memory (&everything); + invalidate_for_call (); + } + + /* Now invalidate everything set by this instruction. + If a SUBREG or other funny destination is being set, + sets[i].rtl is still nonzero, so here we invalidate the reg + a part of which is being set. */ + + for (i = 0; i < n_sets; i++) + if (sets[i].rtl) + { + register rtx dest = sets[i].inner_dest; + + /* Needed for registers to remove the register from its + previous quantity's chain. + Needed for memory if this is a nonvarying address, unless + we have just done an invalidate_memory that covers even those. */ + if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG + || (! writes_memory.all && ! cse_rtx_addr_varies_p (dest))) + invalidate (dest); + } + + /* Make sure registers mentioned in destinations + are safe for use in an expression to be inserted. + This removes from the hash table + any invalid entry that refers to one of these registers. + + We don't care about the return value from mention_regs because + we are going to hash the SET_DEST values unconditionally. */ + + for (i = 0; i < n_sets; i++) + if (sets[i].rtl && GET_CODE (SET_DEST (sets[i].rtl)) != REG) + mention_regs (SET_DEST (sets[i].rtl)); + + /* We may have just removed some of the src_elt's from the hash table. + So replace each one with the current head of the same class. */ + + for (i = 0; i < n_sets; i++) + if (sets[i].rtl) + { + if (sets[i].src_elt && sets[i].src_elt->first_same_value == 0) + /* If elt was removed, find current head of same class, + or 0 if nothing remains of that class. */ + { + register struct table_elt *elt = sets[i].src_elt; + + while (elt && elt->prev_same_value) + elt = elt->prev_same_value; + + while (elt && elt->first_same_value == 0) + elt = elt->next_same_value; + sets[i].src_elt = elt ? elt->first_same_value : 0; + } + } + + /* Now insert the destinations into their equivalence classes. */ + + for (i = 0; i < n_sets; i++) + if (sets[i].rtl) + { + register rtx dest = SET_DEST (sets[i].rtl); + register struct table_elt *elt; + + /* Don't record value if we are not supposed to risk allocating + floating-point values in registers that might be wider than + memory. */ + if ((flag_float_store + && GET_CODE (dest) == MEM + && GET_MODE_CLASS (GET_MODE (dest)) == MODE_FLOAT) + /* Don't record values of destinations set inside a libcall block + since we might delete the libcall. Things should have been set + up so we won't want to reuse such a value, but we play it safe + here. */ + || in_libcall_block + /* If we didn't put a REG_EQUAL value or a source into the hash + table, there is no point is recording DEST. */ + || sets[i].src_elt == 0) + continue; + + /* STRICT_LOW_PART isn't part of the value BEING set, + and neither is the SUBREG inside it. + Note that in this case SETS[I].SRC_ELT is really SRC_EQV_ELT. */ + if (GET_CODE (dest) == STRICT_LOW_PART) + dest = SUBREG_REG (XEXP (dest, 0)); + + if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG) + /* Registers must also be inserted into chains for quantities. */ + if (insert_regs (dest, sets[i].src_elt, 1)) + /* If `insert_regs' changes something, the hash code must be + recalculated. */ + sets[i].dest_hash_code = HASH (dest, GET_MODE (dest)); + + elt = insert (dest, sets[i].src_elt, + sets[i].dest_hash_code, GET_MODE (dest)); + elt->in_memory = GET_CODE (sets[i].inner_dest) == MEM; + if (elt->in_memory) + { + /* This implicitly assumes a whole struct + need not have MEM_IN_STRUCT_P. + But a whole struct is *supposed* to have MEM_IN_STRUCT_P. */ + elt->in_struct = (MEM_IN_STRUCT_P (sets[i].inner_dest) + || sets[i].inner_dest != SET_DEST (sets[i].rtl)); + } + + /* If we have (set (subreg:m1 (reg:m2 foo) 0) (bar:m1)), M1 is no + narrower than M2, and both M1 and M2 are the same number of words, + we are also doing (set (reg:m2 foo) (subreg:m2 (bar:m1) 0)) so + make that equivalence as well. + + However, BAR may have equivalences for which gen_lowpart_if_possible + will produce a simpler value than gen_lowpart_if_possible applied to + BAR (e.g., if BAR was ZERO_EXTENDed from M2), so we will scan all + BAR's equivalences. If we don't get a simplified form, make + the SUBREG. It will not be used in an equivalence, but will + cause two similar assignments to be detected. + + Note the loop below will find SUBREG_REG (DEST) since we have + already entered SRC and DEST of the SET in the table. */ + + if (GET_CODE (dest) == SUBREG + && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) / UNITS_PER_WORD + == GET_MODE_SIZE (GET_MODE (dest)) / UNITS_PER_WORD) + && (GET_MODE_SIZE (GET_MODE (dest)) + >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest)))) + && sets[i].src_elt != 0) + { + enum machine_mode new_mode = GET_MODE (SUBREG_REG (dest)); + struct table_elt *elt, *classp = 0; + + for (elt = sets[i].src_elt->first_same_value; elt; + elt = elt->next_same_value) + { + rtx new_src = 0; + int src_hash; + struct table_elt *src_elt; + + /* Ignore invalid entries. */ + if (GET_CODE (elt->exp) != REG + && ! exp_equiv_p (elt->exp, elt->exp, 1, 0)) + continue; + + new_src = gen_lowpart_if_possible (new_mode, elt->exp); + if (new_src == 0) + new_src = gen_rtx (SUBREG, new_mode, elt->exp, 0); + + src_hash = HASH (new_src, new_mode); + src_elt = lookup (new_src, src_hash, new_mode); + + /* Put the new source in the hash table is if isn't + already. */ + if (src_elt == 0) + { + if (insert_regs (new_src, classp, 0)) + src_hash = HASH (new_src, new_mode); + src_elt = insert (new_src, classp, src_hash, new_mode); + src_elt->in_memory = elt->in_memory; + src_elt->in_struct = elt->in_struct; + } + else if (classp && classp != src_elt->first_same_value) + /* Show that two things that we've seen before are + actually the same. */ + merge_equiv_classes (src_elt, classp); + + classp = src_elt->first_same_value; + } + } + } + + /* Special handling for (set REG0 REG1) + where REG0 is the "cheapest", cheaper than REG1. + After cse, REG1 will probably not be used in the sequel, + so (if easily done) change this insn to (set REG1 REG0) and + replace REG1 with REG0 in the previous insn that computed their value. + Then REG1 will become a dead store and won't cloud the situation + for later optimizations. + + Do not make this change if REG1 is a hard register, because it will + then be used in the sequel and we may be changing a two-operand insn + into a three-operand insn. + + Also do not do this if we are operating on a copy of INSN. */ + + if (n_sets == 1 && sets[0].rtl && GET_CODE (SET_DEST (sets[0].rtl)) == REG + && NEXT_INSN (PREV_INSN (insn)) == insn + && GET_CODE (SET_SRC (sets[0].rtl)) == REG + && REGNO (SET_SRC (sets[0].rtl)) >= FIRST_PSEUDO_REGISTER + && REGNO_QTY_VALID_P (REGNO (SET_SRC (sets[0].rtl))) + && (qty_first_reg[reg_qty[REGNO (SET_SRC (sets[0].rtl))]] + == REGNO (SET_DEST (sets[0].rtl)))) + { + rtx prev = PREV_INSN (insn); + while (prev && GET_CODE (prev) == NOTE) + prev = PREV_INSN (prev); + + if (prev && GET_CODE (prev) == INSN && GET_CODE (PATTERN (prev)) == SET + && SET_DEST (PATTERN (prev)) == SET_SRC (sets[0].rtl)) + { + rtx dest = SET_DEST (sets[0].rtl); + rtx note = find_reg_note (prev, REG_EQUIV, NULL_RTX); + + validate_change (prev, & SET_DEST (PATTERN (prev)), dest, 1); + validate_change (insn, & SET_DEST (sets[0].rtl), + SET_SRC (sets[0].rtl), 1); + validate_change (insn, & SET_SRC (sets[0].rtl), dest, 1); + apply_change_group (); + + /* If REG1 was equivalent to a constant, REG0 is not. */ + if (note) + PUT_REG_NOTE_KIND (note, REG_EQUAL); + + /* If there was a REG_WAS_0 note on PREV, remove it. Move + any REG_WAS_0 note on INSN to PREV. */ + note = find_reg_note (prev, REG_WAS_0, NULL_RTX); + if (note) + remove_note (prev, note); + + note = find_reg_note (insn, REG_WAS_0, NULL_RTX); + if (note) + { + remove_note (insn, note); + XEXP (note, 1) = REG_NOTES (prev); + REG_NOTES (prev) = note; + } + } + } + + /* If this is a conditional jump insn, record any known equivalences due to + the condition being tested. */ + + last_jump_equiv_class = 0; + if (GET_CODE (insn) == JUMP_INSN + && n_sets == 1 && GET_CODE (x) == SET + && GET_CODE (SET_SRC (x)) == IF_THEN_ELSE) + record_jump_equiv (insn, 0); + +#ifdef HAVE_cc0 + /* If the previous insn set CC0 and this insn no longer references CC0, + delete the previous insn. Here we use the fact that nothing expects CC0 + to be valid over an insn, which is true until the final pass. */ + if (prev_insn && GET_CODE (prev_insn) == INSN + && (tem = single_set (prev_insn)) != 0 + && SET_DEST (tem) == cc0_rtx + && ! reg_mentioned_p (cc0_rtx, x)) + { + PUT_CODE (prev_insn, NOTE); + NOTE_LINE_NUMBER (prev_insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (prev_insn) = 0; + } + + prev_insn_cc0 = this_insn_cc0; + prev_insn_cc0_mode = this_insn_cc0_mode; +#endif + + prev_insn = insn; +} + +/* Store 1 in *WRITES_PTR for those categories of memory ref + that must be invalidated when the expression WRITTEN is stored in. + If WRITTEN is null, say everything must be invalidated. */ + +static void +note_mem_written (written, writes_ptr) + rtx written; + struct write_data *writes_ptr; +{ + static struct write_data everything = {0, 1, 1, 1}; + + if (written == 0) + *writes_ptr = everything; + else if (GET_CODE (written) == MEM) + { + /* Pushing or popping the stack invalidates just the stack pointer. */ + rtx addr = XEXP (written, 0); + if ((GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC + || GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC) + && GET_CODE (XEXP (addr, 0)) == REG + && REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM) + { + writes_ptr->sp = 1; + return; + } + else if (GET_MODE (written) == BLKmode) + *writes_ptr = everything; + else if (cse_rtx_addr_varies_p (written)) + { + /* A varying address that is a sum indicates an array element, + and that's just as good as a structure element + in implying that we need not invalidate scalar variables. + However, we must allow QImode aliasing of scalars, because the + ANSI C standard allows character pointers to alias anything. */ + if (! ((MEM_IN_STRUCT_P (written) + || GET_CODE (XEXP (written, 0)) == PLUS) + && GET_MODE (written) != QImode)) + writes_ptr->all = 1; + writes_ptr->nonscalar = 1; + } + writes_ptr->var = 1; + } +} + +/* Perform invalidation on the basis of everything about an insn + except for invalidating the actual places that are SET in it. + This includes the places CLOBBERed, and anything that might + alias with something that is SET or CLOBBERed. + + W points to the writes_memory for this insn, a struct write_data + saying which kinds of memory references must be invalidated. + X is the pattern of the insn. */ + +static void +invalidate_from_clobbers (w, x) + struct write_data *w; + rtx x; +{ + /* If W->var is not set, W specifies no action. + If W->all is set, this step gets all memory refs + so they can be ignored in the rest of this function. */ + if (w->var) + invalidate_memory (w); + + if (w->sp) + { + if (reg_tick[STACK_POINTER_REGNUM] >= 0) + reg_tick[STACK_POINTER_REGNUM]++; + + /* This should be *very* rare. */ + if (TEST_HARD_REG_BIT (hard_regs_in_table, STACK_POINTER_REGNUM)) + invalidate (stack_pointer_rtx); + } + + if (GET_CODE (x) == CLOBBER) + { + rtx ref = XEXP (x, 0); + if (ref + && (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG + || (GET_CODE (ref) == MEM && ! w->all))) + invalidate (ref); + } + else if (GET_CODE (x) == PARALLEL) + { + register int i; + for (i = XVECLEN (x, 0) - 1; i >= 0; i--) + { + register rtx y = XVECEXP (x, 0, i); + if (GET_CODE (y) == CLOBBER) + { + rtx ref = XEXP (y, 0); + if (ref + &&(GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG + || (GET_CODE (ref) == MEM && !w->all))) + invalidate (ref); + } + } + } +} + +/* Process X, part of the REG_NOTES of an insn. Look at any REG_EQUAL notes + and replace any registers in them with either an equivalent constant + or the canonical form of the register. If we are inside an address, + only do this if the address remains valid. + + OBJECT is 0 except when within a MEM in which case it is the MEM. + + Return the replacement for X. */ + +static rtx +cse_process_notes (x, object) + rtx x; + rtx object; +{ + enum rtx_code code = GET_CODE (x); + char *fmt = GET_RTX_FORMAT (code); + int qty; + int i; + + switch (code) + { + case CONST_INT: + case CONST: + case SYMBOL_REF: + case LABEL_REF: + case CONST_DOUBLE: + case PC: + case CC0: + case LO_SUM: + return x; + + case MEM: + XEXP (x, 0) = cse_process_notes (XEXP (x, 0), x); + return x; + + case EXPR_LIST: + case INSN_LIST: + if (REG_NOTE_KIND (x) == REG_EQUAL) + XEXP (x, 0) = cse_process_notes (XEXP (x, 0), NULL_RTX); + if (XEXP (x, 1)) + XEXP (x, 1) = cse_process_notes (XEXP (x, 1), NULL_RTX); + return x; + + case SIGN_EXTEND: + case ZERO_EXTEND: + { + rtx new = cse_process_notes (XEXP (x, 0), object); + /* We don't substitute VOIDmode constants into these rtx, + since they would impede folding. */ + if (GET_MODE (new) != VOIDmode) + validate_change (object, &XEXP (x, 0), new, 0); + return x; + } + + case REG: + i = reg_qty[REGNO (x)]; + + /* Return a constant or a constant register. */ + if (REGNO_QTY_VALID_P (REGNO (x)) + && qty_const[i] != 0 + && (CONSTANT_P (qty_const[i]) + || GET_CODE (qty_const[i]) == REG)) + { + rtx new = gen_lowpart_if_possible (GET_MODE (x), qty_const[i]); + if (new) + return new; + } + + /* Otherwise, canonicalize this register. */ + return canon_reg (x, NULL_RTX); + } + + for (i = 0; i < GET_RTX_LENGTH (code); i++) + if (fmt[i] == 'e') + validate_change (object, &XEXP (x, i), + cse_process_notes (XEXP (x, i), object), 0); + + return x; +} + +/* Find common subexpressions between the end test of a loop and the beginning + of the loop. LOOP_START is the CODE_LABEL at the start of a loop. + + Often we have a loop where an expression in the exit test is used + in the body of the loop. For example "while (*p) *q++ = *p++;". + Because of the way we duplicate the loop exit test in front of the loop, + however, we don't detect that common subexpression. This will be caught + when global cse is implemented, but this is a quite common case. + + This function handles the most common cases of these common expressions. + It is called after we have processed the basic block ending with the + NOTE_INSN_LOOP_END note that ends a loop and the previous JUMP_INSN + jumps to a label used only once. */ + +static void +cse_around_loop (loop_start) + rtx loop_start; +{ + rtx insn; + int i; + struct table_elt *p; + + /* If the jump at the end of the loop doesn't go to the start, we don't + do anything. */ + for (insn = PREV_INSN (loop_start); + insn && (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) >= 0); + insn = PREV_INSN (insn)) + ; + + if (insn == 0 + || GET_CODE (insn) != NOTE + || NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG) + return; + + /* If the last insn of the loop (the end test) was an NE comparison, + we will interpret it as an EQ comparison, since we fell through + the loop. Any equivalences resulting from that comparison are + therefore not valid and must be invalidated. */ + if (last_jump_equiv_class) + for (p = last_jump_equiv_class->first_same_value; p; + p = p->next_same_value) + if (GET_CODE (p->exp) == MEM || GET_CODE (p->exp) == REG + || GET_CODE (p->exp) == SUBREG) + invalidate (p->exp); + + /* Process insns starting after LOOP_START until we hit a CALL_INSN or + a CODE_LABEL (we could handle a CALL_INSN, but it isn't worth it). + + The only thing we do with SET_DEST is invalidate entries, so we + can safely process each SET in order. It is slightly less efficient + to do so, but we only want to handle the most common cases. */ + + for (insn = NEXT_INSN (loop_start); + GET_CODE (insn) != CALL_INSN && GET_CODE (insn) != CODE_LABEL + && ! (GET_CODE (insn) == NOTE + && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END); + insn = NEXT_INSN (insn)) + { + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && (GET_CODE (PATTERN (insn)) == SET + || GET_CODE (PATTERN (insn)) == CLOBBER)) + cse_set_around_loop (PATTERN (insn), insn, loop_start); + else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && GET_CODE (PATTERN (insn)) == PARALLEL) + for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) + if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET + || GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == CLOBBER) + cse_set_around_loop (XVECEXP (PATTERN (insn), 0, i), insn, + loop_start); + } +} + +/* Variable used for communications between the next two routines. */ + +static struct write_data skipped_writes_memory; + +/* Process one SET of an insn that was skipped. We ignore CLOBBERs + since they are done elsewhere. This function is called via note_stores. */ + +static void +invalidate_skipped_set (dest, set) + rtx set; + rtx dest; +{ + if (GET_CODE (set) == CLOBBER +#ifdef HAVE_cc0 + || dest == cc0_rtx +#endif + || dest == pc_rtx) + return; + + if (GET_CODE (dest) == MEM) + note_mem_written (dest, &skipped_writes_memory); + + /* There are times when an address can appear varying and be a PLUS + during this scan when it would be a fixed address were we to know + the proper equivalences. So promote "nonscalar" to be "all". */ + if (skipped_writes_memory.nonscalar) + skipped_writes_memory.all = 1; + + if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG + || (! skipped_writes_memory.all && ! cse_rtx_addr_varies_p (dest))) + invalidate (dest); +} + +/* Invalidate all insns from START up to the end of the function or the + next label. This called when we wish to CSE around a block that is + conditionally executed. */ + +static void +invalidate_skipped_block (start) + rtx start; +{ + rtx insn; + int i; + static struct write_data init = {0, 0, 0, 0}; + static struct write_data everything = {0, 1, 1, 1}; + + for (insn = start; insn && GET_CODE (insn) != CODE_LABEL; + insn = NEXT_INSN (insn)) + { + if (GET_RTX_CLASS (GET_CODE (insn)) != 'i') + continue; + + skipped_writes_memory = init; + + if (GET_CODE (insn) == CALL_INSN) + { + invalidate_for_call (); + skipped_writes_memory = everything; + } + + note_stores (PATTERN (insn), invalidate_skipped_set); + invalidate_from_clobbers (&skipped_writes_memory, PATTERN (insn)); + } +} + +/* Used for communication between the following two routines; contains a + value to be checked for modification. */ + +static rtx cse_check_loop_start_value; + +/* If modifying X will modify the value in CSE_CHECK_LOOP_START_VALUE, + indicate that fact by setting CSE_CHECK_LOOP_START_VALUE to 0. */ + +static void +cse_check_loop_start (x, set) + rtx x; + rtx set; +{ + if (cse_check_loop_start_value == 0 + || GET_CODE (x) == CC0 || GET_CODE (x) == PC) + return; + + if ((GET_CODE (x) == MEM && GET_CODE (cse_check_loop_start_value) == MEM) + || reg_overlap_mentioned_p (x, cse_check_loop_start_value)) + cse_check_loop_start_value = 0; +} + +/* X is a SET or CLOBBER contained in INSN that was found near the start of + a loop that starts with the label at LOOP_START. + + If X is a SET, we see if its SET_SRC is currently in our hash table. + If so, we see if it has a value equal to some register used only in the + loop exit code (as marked by jump.c). + + If those two conditions are true, we search backwards from the start of + the loop to see if that same value was loaded into a register that still + retains its value at the start of the loop. + + If so, we insert an insn after the load to copy the destination of that + load into the equivalent register and (try to) replace our SET_SRC with that + register. + + In any event, we invalidate whatever this SET or CLOBBER modifies. */ + +static void +cse_set_around_loop (x, insn, loop_start) + rtx x; + rtx insn; + rtx loop_start; +{ + rtx p; + struct table_elt *src_elt; + static struct write_data init = {0, 0, 0, 0}; + struct write_data writes_memory; + + writes_memory = init; + + /* If this is a SET, see if we can replace SET_SRC, but ignore SETs that + are setting PC or CC0 or whose SET_SRC is already a register. */ + if (GET_CODE (x) == SET + && GET_CODE (SET_DEST (x)) != PC && GET_CODE (SET_DEST (x)) != CC0 + && GET_CODE (SET_SRC (x)) != REG) + { + src_elt = lookup (SET_SRC (x), + HASH (SET_SRC (x), GET_MODE (SET_DEST (x))), + GET_MODE (SET_DEST (x))); + + if (src_elt) + for (src_elt = src_elt->first_same_value; src_elt; + src_elt = src_elt->next_same_value) + if (GET_CODE (src_elt->exp) == REG && REG_LOOP_TEST_P (src_elt->exp) + && COST (src_elt->exp) < COST (SET_SRC (x))) + { + rtx p, set; + + /* Look for an insn in front of LOOP_START that sets + something in the desired mode to SET_SRC (x) before we hit + a label or CALL_INSN. */ + + for (p = prev_nonnote_insn (loop_start); + p && GET_CODE (p) != CALL_INSN + && GET_CODE (p) != CODE_LABEL; + p = prev_nonnote_insn (p)) + if ((set = single_set (p)) != 0 + && GET_CODE (SET_DEST (set)) == REG + && GET_MODE (SET_DEST (set)) == src_elt->mode + && rtx_equal_p (SET_SRC (set), SET_SRC (x))) + { + /* We now have to ensure that nothing between P + and LOOP_START modified anything referenced in + SET_SRC (x). We know that nothing within the loop + can modify it, or we would have invalidated it in + the hash table. */ + rtx q; + + cse_check_loop_start_value = SET_SRC (x); + for (q = p; q != loop_start; q = NEXT_INSN (q)) + if (GET_RTX_CLASS (GET_CODE (q)) == 'i') + note_stores (PATTERN (q), cse_check_loop_start); + + /* If nothing was changed and we can replace our + SET_SRC, add an insn after P to copy its destination + to what we will be replacing SET_SRC with. */ + if (cse_check_loop_start_value + && validate_change (insn, &SET_SRC (x), + src_elt->exp, 0)) + emit_insn_after (gen_move_insn (src_elt->exp, + SET_DEST (set)), + p); + break; + } + } + } + + /* Now invalidate anything modified by X. */ + note_mem_written (SET_DEST (x), &writes_memory); + + if (writes_memory.var) + invalidate_memory (&writes_memory); + + /* See comment on similar code in cse_insn for explanation of these tests. */ + if (GET_CODE (SET_DEST (x)) == REG || GET_CODE (SET_DEST (x)) == SUBREG + || (GET_CODE (SET_DEST (x)) == MEM && ! writes_memory.all + && ! cse_rtx_addr_varies_p (SET_DEST (x)))) + invalidate (SET_DEST (x)); +} + +/* Find the end of INSN's basic block and return its range, + the total number of SETs in all the insns of the block, the last insn of the + block, and the branch path. + + The branch path indicates which branches should be followed. If a non-zero + path size is specified, the block should be rescanned and a different set + of branches will be taken. The branch path is only used if + FLAG_CSE_FOLLOW_JUMPS or FLAG_CSE_SKIP_BLOCKS is non-zero. + + DATA is a pointer to a struct cse_basic_block_data, defined below, that is + used to describe the block. It is filled in with the information about + the current block. The incoming structure's branch path, if any, is used + to construct the output branch path. */ + +void +cse_end_of_basic_block (insn, data, follow_jumps, after_loop, skip_blocks) + rtx insn; + struct cse_basic_block_data *data; + int follow_jumps; + int after_loop; + int skip_blocks; +{ + rtx p = insn, q; + int nsets = 0; + int low_cuid = INSN_CUID (insn), high_cuid = INSN_CUID (insn); + rtx next = GET_RTX_CLASS (GET_CODE (insn)) == 'i' ? insn : next_real_insn (insn); + int path_size = data->path_size; + int path_entry = 0; + int i; + + /* Update the previous branch path, if any. If the last branch was + previously TAKEN, mark it NOT_TAKEN. If it was previously NOT_TAKEN, + shorten the path by one and look at the previous branch. We know that + at least one branch must have been taken if PATH_SIZE is non-zero. */ + while (path_size > 0) + { + if (data->path[path_size - 1].status != NOT_TAKEN) + { + data->path[path_size - 1].status = NOT_TAKEN; + break; + } + else + path_size--; + } + + /* Scan to end of this basic block. */ + while (p && GET_CODE (p) != CODE_LABEL) + { + /* Don't cse out the end of a loop. This makes a difference + only for the unusual loops that always execute at least once; + all other loops have labels there so we will stop in any case. + Cse'ing out the end of the loop is dangerous because it + might cause an invariant expression inside the loop + to be reused after the end of the loop. This would make it + hard to move the expression out of the loop in loop.c, + especially if it is one of several equivalent expressions + and loop.c would like to eliminate it. + + If we are running after loop.c has finished, we can ignore + the NOTE_INSN_LOOP_END. */ + + if (! after_loop && GET_CODE (p) == NOTE + && NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END) + break; + + /* Don't cse over a call to setjmp; on some machines (eg vax) + the regs restored by the longjmp come from + a later time than the setjmp. */ + if (GET_CODE (p) == NOTE + && NOTE_LINE_NUMBER (p) == NOTE_INSN_SETJMP) + break; + + /* A PARALLEL can have lots of SETs in it, + especially if it is really an ASM_OPERANDS. */ + if (GET_RTX_CLASS (GET_CODE (p)) == 'i' + && GET_CODE (PATTERN (p)) == PARALLEL) + nsets += XVECLEN (PATTERN (p), 0); + else if (GET_CODE (p) != NOTE) + nsets += 1; + + /* Ignore insns made by CSE; they cannot affect the boundaries of + the basic block. */ + + if (INSN_UID (p) <= max_uid && INSN_CUID (p) > high_cuid) + high_cuid = INSN_CUID (p); + if (INSN_UID (p) <= max_uid && INSN_CUID (p) < low_cuid) + low_cuid = INSN_CUID (p); + + /* See if this insn is in our branch path. If it is and we are to + take it, do so. */ + if (path_entry < path_size && data->path[path_entry].branch == p) + { + if (data->path[path_entry].status != NOT_TAKEN) + p = JUMP_LABEL (p); + + /* Point to next entry in path, if any. */ + path_entry++; + } + + /* If this is a conditional jump, we can follow it if -fcse-follow-jumps + was specified, we haven't reached our maximum path length, there are + insns following the target of the jump, this is the only use of the + jump label, and the target label is preceded by a BARRIER. + + Alternatively, we can follow the jump if it branches around a + block of code and there are no other branches into the block. + In this case invalidate_skipped_block will be called to invalidate any + registers set in the block when following the jump. */ + + else if ((follow_jumps || skip_blocks) && path_size < PATHLENGTH - 1 + && GET_CODE (p) == JUMP_INSN + && GET_CODE (PATTERN (p)) == SET + && GET_CODE (SET_SRC (PATTERN (p))) == IF_THEN_ELSE + && LABEL_NUSES (JUMP_LABEL (p)) == 1 + && NEXT_INSN (JUMP_LABEL (p)) != 0) + { + for (q = PREV_INSN (JUMP_LABEL (p)); q; q = PREV_INSN (q)) + if ((GET_CODE (q) != NOTE + || NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_END + || NOTE_LINE_NUMBER (q) == NOTE_INSN_SETJMP) + && (GET_CODE (q) != CODE_LABEL || LABEL_NUSES (q) != 0)) + break; + + /* If we ran into a BARRIER, this code is an extension of the + basic block when the branch is taken. */ + if (follow_jumps && q != 0 && GET_CODE (q) == BARRIER) + { + /* Don't allow ourself to keep walking around an + always-executed loop. */ + if (next_real_insn (q) == next) + { + p = NEXT_INSN (p); + continue; + } + + /* Similarly, don't put a branch in our path more than once. */ + for (i = 0; i < path_entry; i++) + if (data->path[i].branch == p) + break; + + if (i != path_entry) + break; + + data->path[path_entry].branch = p; + data->path[path_entry++].status = TAKEN; + + /* This branch now ends our path. It was possible that we + didn't see this branch the last time around (when the + insn in front of the target was a JUMP_INSN that was + turned into a no-op). */ + path_size = path_entry; + + p = JUMP_LABEL (p); + /* Mark block so we won't scan it again later. */ + PUT_MODE (NEXT_INSN (p), QImode); + } + /* Detect a branch around a block of code. */ + else if (skip_blocks && q != 0 && GET_CODE (q) != CODE_LABEL) + { + register rtx tmp; + + if (next_real_insn (q) == next) + { + p = NEXT_INSN (p); + continue; + } + + for (i = 0; i < path_entry; i++) + if (data->path[i].branch == p) + break; + + if (i != path_entry) + break; + + /* This is no_labels_between_p (p, q) with an added check for + reaching the end of a function (in case Q precedes P). */ + for (tmp = NEXT_INSN (p); tmp && tmp != q; tmp = NEXT_INSN (tmp)) + if (GET_CODE (tmp) == CODE_LABEL) + break; + + if (tmp == q) + { + data->path[path_entry].branch = p; + data->path[path_entry++].status = AROUND; + + path_size = path_entry; + + p = JUMP_LABEL (p); + /* Mark block so we won't scan it again later. */ + PUT_MODE (NEXT_INSN (p), QImode); + } + } + } + p = NEXT_INSN (p); + } + + data->low_cuid = low_cuid; + data->high_cuid = high_cuid; + data->nsets = nsets; + data->last = p; + + /* If all jumps in the path are not taken, set our path length to zero + so a rescan won't be done. */ + for (i = path_size - 1; i >= 0; i--) + if (data->path[i].status != NOT_TAKEN) + break; + + if (i == -1) + data->path_size = 0; + else + data->path_size = path_size; + + /* End the current branch path. */ + data->path[path_size].branch = 0; +} + +/* Perform cse on the instructions of a function. + F is the first instruction. + NREGS is one plus the highest pseudo-reg number used in the instruction. + + AFTER_LOOP is 1 if this is the cse call done after loop optimization + (only if -frerun-cse-after-loop). + + Returns 1 if jump_optimize should be redone due to simplifications + in conditional jump instructions. */ + +int +cse_main (f, nregs, after_loop, file) + rtx f; + int nregs; + int after_loop; + FILE *file; +{ + struct cse_basic_block_data val; + register rtx insn = f; + register int i; + + cse_jumps_altered = 0; + constant_pool_entries_cost = 0; + val.path_size = 0; + + init_recog (); + + max_reg = nregs; + + all_minus_one = (int *) alloca (nregs * sizeof (int)); + consec_ints = (int *) alloca (nregs * sizeof (int)); + + for (i = 0; i < nregs; i++) + { + all_minus_one[i] = -1; + consec_ints[i] = i; + } + + reg_next_eqv = (int *) alloca (nregs * sizeof (int)); + reg_prev_eqv = (int *) alloca (nregs * sizeof (int)); + reg_qty = (int *) alloca (nregs * sizeof (int)); + reg_in_table = (int *) alloca (nregs * sizeof (int)); + reg_tick = (int *) alloca (nregs * sizeof (int)); + + /* Discard all the free elements of the previous function + since they are allocated in the temporarily obstack. */ + bzero (table, sizeof table); + free_element_chain = 0; + n_elements_made = 0; + + /* Find the largest uid. */ + + max_uid = get_max_uid (); + uid_cuid = (int *) alloca ((max_uid + 1) * sizeof (int)); + bzero (uid_cuid, (max_uid + 1) * sizeof (int)); + + /* Compute the mapping from uids to cuids. + CUIDs are numbers assigned to insns, like uids, + except that cuids increase monotonically through the code. + Don't assign cuids to line-number NOTEs, so that the distance in cuids + between two insns is not affected by -g. */ + + for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) != NOTE + || NOTE_LINE_NUMBER (insn) < 0) + INSN_CUID (insn) = ++i; + else + /* Give a line number note the same cuid as preceding insn. */ + INSN_CUID (insn) = i; + } + + /* Initialize which registers are clobbered by calls. */ + + CLEAR_HARD_REG_SET (regs_invalidated_by_call); + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if ((call_used_regs[i] + /* Used to check !fixed_regs[i] here, but that isn't safe; + fixed regs are still call-clobbered, and sched can get + confused if they can "live across calls". + + The frame pointer is always preserved across calls. The arg + pointer is if it is fixed. The stack pointer usually is, unless + RETURN_POPS_ARGS, in which case an explicit CLOBBER + will be present. If we are generating PIC code, the PIC offset + table register is preserved across calls. */ + + && i != STACK_POINTER_REGNUM + && i != FRAME_POINTER_REGNUM +#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM + && ! (i == ARG_POINTER_REGNUM && fixed_regs[i]) +#endif +#ifdef PIC_OFFSET_TABLE_REGNUM + && ! (i == PIC_OFFSET_TABLE_REGNUM && flag_pic) +#endif + ) + || global_regs[i]) + SET_HARD_REG_BIT (regs_invalidated_by_call, i); + + /* Loop over basic blocks. + Compute the maximum number of qty's needed for each basic block + (which is 2 for each SET). */ + insn = f; + while (insn) + { + cse_end_of_basic_block (insn, &val, flag_cse_follow_jumps, after_loop, + flag_cse_skip_blocks); + + /* If this basic block was already processed or has no sets, skip it. */ + if (val.nsets == 0 || GET_MODE (insn) == QImode) + { + PUT_MODE (insn, VOIDmode); + insn = (val.last ? NEXT_INSN (val.last) : 0); + val.path_size = 0; + continue; + } + + cse_basic_block_start = val.low_cuid; + cse_basic_block_end = val.high_cuid; + max_qty = val.nsets * 2; + + if (file) + fprintf (file, ";; Processing block from %d to %d, %d sets.\n", + INSN_UID (insn), val.last ? INSN_UID (val.last) : 0, + val.nsets); + + /* Make MAX_QTY bigger to give us room to optimize + past the end of this basic block, if that should prove useful. */ + if (max_qty < 500) + max_qty = 500; + + max_qty += max_reg; + + /* If this basic block is being extended by following certain jumps, + (see `cse_end_of_basic_block'), we reprocess the code from the start. + Otherwise, we start after this basic block. */ + if (val.path_size > 0) + cse_basic_block (insn, val.last, val.path, 0); + else + { + int old_cse_jumps_altered = cse_jumps_altered; + rtx temp; + + /* When cse changes a conditional jump to an unconditional + jump, we want to reprocess the block, since it will give + us a new branch path to investigate. */ + cse_jumps_altered = 0; + temp = cse_basic_block (insn, val.last, val.path, ! after_loop); + if (cse_jumps_altered == 0 + || (flag_cse_follow_jumps == 0 && flag_cse_skip_blocks == 0)) + insn = temp; + + cse_jumps_altered |= old_cse_jumps_altered; + } + +#ifdef USE_C_ALLOCA + alloca (0); +#endif + } + + /* Tell refers_to_mem_p that qty_const info is not available. */ + qty_const = 0; + + if (max_elements_made < n_elements_made) + max_elements_made = n_elements_made; + + return cse_jumps_altered; +} + +/* Process a single basic block. FROM and TO and the limits of the basic + block. NEXT_BRANCH points to the branch path when following jumps or + a null path when not following jumps. + + AROUND_LOOP is non-zero if we are to try to cse around to the start of a + loop. This is true when we are being called for the last time on a + block and this CSE pass is before loop.c. */ + +static rtx +cse_basic_block (from, to, next_branch, around_loop) + register rtx from, to; + struct branch_path *next_branch; + int around_loop; +{ + register rtx insn; + int to_usage = 0; + int in_libcall_block = 0; + + /* Each of these arrays is undefined before max_reg, so only allocate + the space actually needed and adjust the start below. */ + + qty_first_reg = (int *) alloca ((max_qty - max_reg) * sizeof (int)); + qty_last_reg = (int *) alloca ((max_qty - max_reg) * sizeof (int)); + qty_mode= (enum machine_mode *) alloca ((max_qty - max_reg) * sizeof (enum machine_mode)); + qty_const = (rtx *) alloca ((max_qty - max_reg) * sizeof (rtx)); + qty_const_insn = (rtx *) alloca ((max_qty - max_reg) * sizeof (rtx)); + qty_comparison_code + = (enum rtx_code *) alloca ((max_qty - max_reg) * sizeof (enum rtx_code)); + qty_comparison_qty = (int *) alloca ((max_qty - max_reg) * sizeof (int)); + qty_comparison_const = (rtx *) alloca ((max_qty - max_reg) * sizeof (rtx)); + + qty_first_reg -= max_reg; + qty_last_reg -= max_reg; + qty_mode -= max_reg; + qty_const -= max_reg; + qty_const_insn -= max_reg; + qty_comparison_code -= max_reg; + qty_comparison_qty -= max_reg; + qty_comparison_const -= max_reg; + + new_basic_block (); + + /* TO might be a label. If so, protect it from being deleted. */ + if (to != 0 && GET_CODE (to) == CODE_LABEL) + ++LABEL_NUSES (to); + + for (insn = from; insn != to; insn = NEXT_INSN (insn)) + { + register enum rtx_code code; + + /* See if this is a branch that is part of the path. If so, and it is + to be taken, do so. */ + if (next_branch->branch == insn) + { + enum taken status = next_branch++->status; + if (status != NOT_TAKEN) + { + if (status == TAKEN) + record_jump_equiv (insn, 1); + else + invalidate_skipped_block (NEXT_INSN (insn)); + + /* Set the last insn as the jump insn; it doesn't affect cc0. + Then follow this branch. */ +#ifdef HAVE_cc0 + prev_insn_cc0 = 0; +#endif + prev_insn = insn; + insn = JUMP_LABEL (insn); + continue; + } + } + + code = GET_CODE (insn); + if (GET_MODE (insn) == QImode) + PUT_MODE (insn, VOIDmode); + + if (GET_RTX_CLASS (code) == 'i') + { + /* Process notes first so we have all notes in canonical forms when + looking for duplicate operations. */ + + if (REG_NOTES (insn)) + REG_NOTES (insn) = cse_process_notes (REG_NOTES (insn), NULL_RTX); + + /* Track when we are inside in LIBCALL block. Inside such a block, + we do not want to record destinations. The last insn of a + LIBCALL block is not considered to be part of the block, since + its destination is the result of the block and hence should be + recorded. */ + + if (find_reg_note (insn, REG_LIBCALL, NULL_RTX)) + in_libcall_block = 1; + else if (find_reg_note (insn, REG_RETVAL, NULL_RTX)) + in_libcall_block = 0; + + cse_insn (insn, in_libcall_block); + } + + /* If INSN is now an unconditional jump, skip to the end of our + basic block by pretending that we just did the last insn in the + basic block. If we are jumping to the end of our block, show + that we can have one usage of TO. */ + + if (simplejump_p (insn)) + { + if (to == 0) + return 0; + + if (JUMP_LABEL (insn) == to) + to_usage = 1; + + /* Maybe TO was deleted because the jump is unconditional. + If so, there is nothing left in this basic block. */ + /* ??? Perhaps it would be smarter to set TO + to whatever follows this insn, + and pretend the basic block had always ended here. */ + if (INSN_DELETED_P (to)) + break; + + insn = PREV_INSN (to); + } + + /* See if it is ok to keep on going past the label + which used to end our basic block. Remember that we incremented + the count of that label, so we decrement it here. If we made + a jump unconditional, TO_USAGE will be one; in that case, we don't + want to count the use in that jump. */ + + if (to != 0 && NEXT_INSN (insn) == to + && GET_CODE (to) == CODE_LABEL && --LABEL_NUSES (to) == to_usage) + { + struct cse_basic_block_data val; + + insn = NEXT_INSN (to); + + if (LABEL_NUSES (to) == 0) + delete_insn (to); + + /* Find the end of the following block. Note that we won't be + following branches in this case. If TO was the last insn + in the function, we are done. Similarly, if we deleted the + insn after TO, it must have been because it was preceded by + a BARRIER. In that case, we are done with this block because it + has no continuation. */ + + if (insn == 0 || INSN_DELETED_P (insn)) + return 0; + + to_usage = 0; + val.path_size = 0; + cse_end_of_basic_block (insn, &val, 0, 0, 0); + + /* If the tables we allocated have enough space left + to handle all the SETs in the next basic block, + continue through it. Otherwise, return, + and that block will be scanned individually. */ + if (val.nsets * 2 + next_qty > max_qty) + break; + + cse_basic_block_start = val.low_cuid; + cse_basic_block_end = val.high_cuid; + to = val.last; + + /* Prevent TO from being deleted if it is a label. */ + if (to != 0 && GET_CODE (to) == CODE_LABEL) + ++LABEL_NUSES (to); + + /* Back up so we process the first insn in the extension. */ + insn = PREV_INSN (insn); + } + } + + if (next_qty > max_qty) + abort (); + + /* If we are running before loop.c, we stopped on a NOTE_INSN_LOOP_END, and + the previous insn is the only insn that branches to the head of a loop, + we can cse into the loop. Don't do this if we changed the jump + structure of a loop unless we aren't going to be following jumps. */ + + if ((cse_jumps_altered == 0 + || (flag_cse_follow_jumps == 0 && flag_cse_skip_blocks == 0)) + && around_loop && to != 0 + && GET_CODE (to) == NOTE && NOTE_LINE_NUMBER (to) == NOTE_INSN_LOOP_END + && GET_CODE (PREV_INSN (to)) == JUMP_INSN + && JUMP_LABEL (PREV_INSN (to)) != 0 + && LABEL_NUSES (JUMP_LABEL (PREV_INSN (to))) == 1) + cse_around_loop (JUMP_LABEL (PREV_INSN (to))); + + return to ? NEXT_INSN (to) : 0; +} + +/* Count the number of times registers are used (not set) in X. + COUNTS is an array in which we accumulate the count, INCR is how much + we count each register usage. */ + +static void +count_reg_usage (x, counts, incr) + rtx x; + int *counts; + int incr; +{ + enum rtx_code code = GET_CODE (x); + char *fmt; + int i, j; + + switch (code) + { + case REG: + counts[REGNO (x)] += incr; + return; + + case PC: + case CC0: + case CONST: + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + case LABEL_REF: + case CLOBBER: + return; + + case SET: + /* Unless we are setting a REG, count everything in SET_DEST. */ + if (GET_CODE (SET_DEST (x)) != REG) + count_reg_usage (SET_DEST (x), counts, incr); + count_reg_usage (SET_SRC (x), counts, incr); + return; + + case INSN: + case JUMP_INSN: + case CALL_INSN: + count_reg_usage (PATTERN (x), counts, incr); + + /* Things used in a REG_EQUAL note aren't dead since loop may try to + use them. */ + + if (REG_NOTES (x)) + count_reg_usage (REG_NOTES (x), counts, incr); + return; + + case EXPR_LIST: + case INSN_LIST: + if (REG_NOTE_KIND (x) == REG_EQUAL) + count_reg_usage (XEXP (x, 0), counts, incr); + if (XEXP (x, 1)) + count_reg_usage (XEXP (x, 1), counts, incr); + return; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + count_reg_usage (XEXP (x, i), counts, incr); + else if (fmt[i] == 'E') + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + count_reg_usage (XVECEXP (x, i, j), counts, incr); + } +} + +/* Scan all the insns and delete any that are dead; i.e., they store a register + that is never used or they copy a register to itself. + + This is used to remove insns made obviously dead by cse. It improves the + heuristics in loop since it won't try to move dead invariants out of loops + or make givs for dead quantities. The remaining passes of the compilation + are also sped up. */ + +void +delete_dead_from_cse (insns, nreg) + rtx insns; + int nreg; +{ + int *counts = (int *) alloca (nreg * sizeof (int)); + rtx insn, prev; + rtx tem; + int i; + int in_libcall = 0; + + /* First count the number of times each register is used. */ + bzero (counts, sizeof (int) * nreg); + for (insn = next_real_insn (insns); insn; insn = next_real_insn (insn)) + count_reg_usage (insn, counts, 1); + + /* Go from the last insn to the first and delete insns that only set unused + registers or copy a register to itself. As we delete an insn, remove + usage counts for registers it uses. */ + for (insn = prev_real_insn (get_last_insn ()); insn; insn = prev) + { + int live_insn = 0; + + prev = prev_real_insn (insn); + + /* Don't delete any insns that are part of a libcall block. + Flow or loop might get confused if we did that. Remember + that we are scanning backwards. */ + if (find_reg_note (insn, REG_RETVAL, NULL_RTX)) + in_libcall = 1; + + if (in_libcall) + live_insn = 1; + else if (GET_CODE (PATTERN (insn)) == SET) + { + if (GET_CODE (SET_DEST (PATTERN (insn))) == REG + && SET_DEST (PATTERN (insn)) == SET_SRC (PATTERN (insn))) + ; + +#ifdef HAVE_cc0 + else if (GET_CODE (SET_DEST (PATTERN (insn))) == CC0 + && ! side_effects_p (SET_SRC (PATTERN (insn))) + && ((tem = next_nonnote_insn (insn)) == 0 + || GET_RTX_CLASS (GET_CODE (tem)) != 'i' + || ! reg_referenced_p (cc0_rtx, PATTERN (tem)))) + ; +#endif + else if (GET_CODE (SET_DEST (PATTERN (insn))) != REG + || REGNO (SET_DEST (PATTERN (insn))) < FIRST_PSEUDO_REGISTER + || counts[REGNO (SET_DEST (PATTERN (insn)))] != 0 + || side_effects_p (SET_SRC (PATTERN (insn)))) + live_insn = 1; + } + else if (GET_CODE (PATTERN (insn)) == PARALLEL) + for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) + { + rtx elt = XVECEXP (PATTERN (insn), 0, i); + + if (GET_CODE (elt) == SET) + { + if (GET_CODE (SET_DEST (elt)) == REG + && SET_DEST (elt) == SET_SRC (elt)) + ; + +#ifdef HAVE_cc0 + else if (GET_CODE (SET_DEST (elt)) == CC0 + && ! side_effects_p (SET_SRC (elt)) + && ((tem = next_nonnote_insn (insn)) == 0 + || GET_RTX_CLASS (GET_CODE (tem)) != 'i' + || ! reg_referenced_p (cc0_rtx, PATTERN (tem)))) + ; +#endif + else if (GET_CODE (SET_DEST (elt)) != REG + || REGNO (SET_DEST (elt)) < FIRST_PSEUDO_REGISTER + || counts[REGNO (SET_DEST (elt))] != 0 + || side_effects_p (SET_SRC (elt))) + live_insn = 1; + } + else if (GET_CODE (elt) != CLOBBER && GET_CODE (elt) != USE) + live_insn = 1; + } + else + live_insn = 1; + + /* If this is a dead insn, delete it and show registers in it aren't + being used. */ + + if (! live_insn) + { + count_reg_usage (insn, counts, -1); + delete_insn (insn); + } + + if (find_reg_note (insn, REG_LIBCALL, NULL_RTX)) + in_libcall = 0; + } +} diff --git a/gnu/usr.bin/cc/lib/dbxout.c b/gnu/usr.bin/cc/lib/dbxout.c new file mode 100644 index 0000000000..4f3a348c60 --- /dev/null +++ b/gnu/usr.bin/cc/lib/dbxout.c @@ -0,0 +1,2439 @@ +/* Output dbx-format symbol table information from GNU compiler. + Copyright (C) 1987, 1988, 1992 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* Output dbx-format symbol table data. + This consists of many symbol table entries, each of them + a .stabs assembler pseudo-op with four operands: + a "name" which is really a description of one symbol and its type, + a "code", which is a symbol defined in stab.h whose name starts with N_, + an unused operand always 0, + and a "value" which is an address or an offset. + The name is enclosed in doublequote characters. + + Each function, variable, typedef, and structure tag + has a symbol table entry to define it. + The beginning and end of each level of name scoping within + a function are also marked by special symbol table entries. + + The "name" consists of the symbol name, a colon, a kind-of-symbol letter, + and a data type number. The data type number may be followed by + "=" and a type definition; normally this will happen the first time + the type number is mentioned. The type definition may refer to + other types by number, and those type numbers may be followed + by "=" and nested definitions. + + This can make the "name" quite long. + When a name is more than 80 characters, we split the .stabs pseudo-op + into two .stabs pseudo-ops, both sharing the same "code" and "value". + The first one is marked as continued with a double-backslash at the + end of its "name". + + The kind-of-symbol letter distinguished function names from global + variables from file-scope variables from parameters from auto + variables in memory from typedef names from register variables. + See `dbxout_symbol'. + + The "code" is mostly redundant with the kind-of-symbol letter + that goes in the "name", but not entirely: for symbols located + in static storage, the "code" says which segment the address is in, + which controls how it is relocated. + + The "value" for a symbol in static storage + is the core address of the symbol (actually, the assembler + label for the symbol). For a symbol located in a stack slot + it is the stack offset; for one in a register, the register number. + For a typedef symbol, it is zero. + + If DEBUG_SYMS_TEXT is defined, all debugging symbols must be + output while in the text section. + + For more on data type definitions, see `dbxout_type'. */ + +/* Include these first, because they may define MIN and MAX. */ +#include +#include + +#include "config.h" +#include "tree.h" +#include "rtl.h" +#include "flags.h" +#include "regs.h" +#include "insn-config.h" +#include "reload.h" +#include "defaults.h" +#include "output.h" /* ASM_OUTPUT_SOURCE_LINE may refer to sdb functions. */ + +#ifndef errno +extern int errno; +#endif + +#ifdef XCOFF_DEBUGGING_INFO +#include "xcoffout.h" +#endif + +#ifndef ASM_STABS_OP +#define ASM_STABS_OP ".stabs" +#endif + +#ifndef ASM_STABN_OP +#define ASM_STABN_OP ".stabn" +#endif + +#ifndef DBX_TYPE_DECL_STABS_CODE +#define DBX_TYPE_DECL_STABS_CODE N_LSYM +#endif + +#ifndef DBX_STATIC_CONST_VAR_CODE +#define DBX_STATIC_CONST_VAR_CODE N_FUN +#endif + +#ifndef DBX_REGPARM_STABS_CODE +#define DBX_REGPARM_STABS_CODE N_RSYM +#endif + +#ifndef DBX_REGPARM_STABS_LETTER +#define DBX_REGPARM_STABS_LETTER 'P' +#endif + +#ifndef DBX_MEMPARM_STABS_LETTER +#define DBX_MEMPARM_STABS_LETTER 'p' +#endif + +/* Nonzero means if the type has methods, only output debugging + information if methods are actually written to the asm file. */ + +static int flag_minimal_debug = 1; + +/* Nonzero if we have actually used any of the GDB extensions + to the debugging format. The idea is that we use them for the + first time only if there's a strong reason, but once we have done that, + we use them whenever convenient. */ + +static int have_used_extensions = 0; + +char *getpwd (); + +/* Typical USG systems don't have stab.h, and they also have + no use for DBX-format debugging info. */ + +#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) + +#ifdef DEBUG_SYMS_TEXT +#define FORCE_TEXT text_section (); +#else +#define FORCE_TEXT +#endif + +#if defined (USG) || defined (NO_STAB_H) +#include "gstab.h" /* If doing DBX on sysV, use our own stab.h. */ +#else +#include /* On BSD, use the system's stab.h. */ + +/* This is a GNU extension we need to reference in this file. */ +#ifndef N_CATCH +#define N_CATCH 0x54 +#endif +#endif /* not USG */ + +#ifdef __GNU_STAB__ +#define STAB_CODE_TYPE enum __stab_debug_code +#else +#define STAB_CODE_TYPE int +#endif + +/* 1 if PARM is passed to this function in memory. */ + +#define PARM_PASSED_IN_MEMORY(PARM) \ + (GET_CODE (DECL_INCOMING_RTL (PARM)) == MEM) + +/* A C expression for the integer offset value of an automatic variable + (N_LSYM) having address X (an RTX). */ +#ifndef DEBUGGER_AUTO_OFFSET +#define DEBUGGER_AUTO_OFFSET(X) \ + (GET_CODE (X) == PLUS ? INTVAL (XEXP (X, 1)) : 0) +#endif + +/* A C expression for the integer offset value of an argument (N_PSYM) + having address X (an RTX). The nominal offset is OFFSET. */ +#ifndef DEBUGGER_ARG_OFFSET +#define DEBUGGER_ARG_OFFSET(OFFSET, X) (OFFSET) +#endif + +/* Stream for writing to assembler file. */ + +static FILE *asmfile; + +/* Last source file name mentioned in a NOTE insn. */ + +static char *lastfile; + +/* Current working directory. */ + +static char *cwd; + +enum typestatus {TYPE_UNSEEN, TYPE_XREF, TYPE_DEFINED}; + +/* Vector recording the status of describing C data types. + When we first notice a data type (a tree node), + we assign it a number using next_type_number. + That is its index in this vector. + The vector element says whether we have yet output + the definition of the type. TYPE_XREF says we have + output it as a cross-reference only. */ + +enum typestatus *typevec; + +/* Number of elements of space allocated in `typevec'. */ + +static int typevec_len; + +/* In dbx output, each type gets a unique number. + This is the number for the next type output. + The number, once assigned, is in the TYPE_SYMTAB_ADDRESS field. */ + +static int next_type_number; + +/* In dbx output, we must assign symbol-blocks id numbers + in the order in which their beginnings are encountered. + We output debugging info that refers to the beginning and + end of the ranges of code in each block + with assembler labels LBBn and LBEn, where n is the block number. + The labels are generated in final, which assigns numbers to the + blocks in the same way. */ + +static int next_block_number; + +/* These variables are for dbxout_symbol to communicate to + dbxout_finish_symbol. + current_sym_code is the symbol-type-code, a symbol N_... define in stab.h. + current_sym_value and current_sym_addr are two ways to address the + value to store in the symtab entry. + current_sym_addr if nonzero represents the value as an rtx. + If that is zero, current_sym_value is used. This is used + when the value is an offset (such as for auto variables, + register variables and parms). */ + +static STAB_CODE_TYPE current_sym_code; +static int current_sym_value; +static rtx current_sym_addr; + +/* Number of chars of symbol-description generated so far for the + current symbol. Used by CHARS and CONTIN. */ + +static int current_sym_nchars; + +/* Report having output N chars of the current symbol-description. */ + +#define CHARS(N) (current_sym_nchars += (N)) + +/* Break the current symbol-description, generating a continuation, + if it has become long. */ + +#ifndef DBX_CONTIN_LENGTH +#define DBX_CONTIN_LENGTH 80 +#endif + +#if DBX_CONTIN_LENGTH > 0 +#define CONTIN \ + do {if (current_sym_nchars > DBX_CONTIN_LENGTH) dbxout_continue ();} while (0) +#else +#define CONTIN +#endif + +void dbxout_types (); +void dbxout_args (); +void dbxout_symbol (); +static void dbxout_type_name (); +static void dbxout_type (); +static void dbxout_typedefs (); +static void dbxout_prepare_symbol (); +static void dbxout_finish_symbol (); +static void dbxout_continue (); +static void print_int_cst_octal (); +static void print_octal (); + +#if 0 /* Not clear we will actually need this. */ + +/* Return the absolutized filename for the given relative + filename. Note that if that filename is already absolute, it may + still be returned in a modified form because this routine also + eliminates redundant slashes and single dots and eliminates double + dots to get a shortest possible filename from the given input + filename. The absolutization of relative filenames is made by + assuming that the given filename is to be taken as relative to + the first argument (cwd) or to the current directory if cwd is + NULL. */ + +static char * +abspath (rel_filename) + char *rel_filename; +{ + /* Setup the current working directory as needed. */ + char *abs_buffer + = (char *) alloca (strlen (cwd) + strlen (rel_filename) + 1); + char *endp = abs_buffer; + char *outp, *inp; + char *value; + + /* Copy the filename (possibly preceded by the current working + directory name) into the absolutization buffer. */ + + { + char *src_p; + + if (rel_filename[0] != '/') + { + src_p = cwd; + while (*endp++ = *src_p++) + continue; + *(endp-1) = '/'; /* overwrite null */ + } + src_p = rel_filename; + while (*endp++ = *src_p++) + continue; + if (endp[-1] == '/') + *endp = '\0'; + + /* Now make a copy of abs_buffer into abs_buffer, shortening the + filename (by taking out slashes and dots) as we go. */ + + outp = inp = abs_buffer; + *outp++ = *inp++; /* copy first slash */ + for (;;) + { + if (!inp[0]) + break; + else if (inp[0] == '/' && outp[-1] == '/') + { + inp++; + continue; + } + else if (inp[0] == '.' && outp[-1] == '/') + { + if (!inp[1]) + break; + else if (inp[1] == '/') + { + inp += 2; + continue; + } + else if ((inp[1] == '.') && (inp[2] == 0 || inp[2] == '/')) + { + inp += (inp[2] == '/') ? 3 : 2; + outp -= 2; + while (outp >= abs_buffer && *outp != '/') + outp--; + if (outp < abs_buffer) + { + /* Catch cases like /.. where we try to backup to a + point above the absolute root of the logical file + system. */ + + fprintf (stderr, "%s: invalid file name: %s\n", + pname, rel_filename); + exit (1); + } + *++outp = '\0'; + continue; + } + } + *outp++ = *inp++; + } + + /* On exit, make sure that there is a trailing null, and make sure that + the last character of the returned string is *not* a slash. */ + + *outp = '\0'; + if (outp[-1] == '/') + *--outp = '\0'; + + /* Make a copy (in the heap) of the stuff left in the absolutization + buffer and return a pointer to the copy. */ + + value = (char *) oballoc (strlen (abs_buffer) + 1); + strcpy (value, abs_buffer); + return value; +} +#endif /* 0 */ + +/* At the beginning of compilation, start writing the symbol table. + Initialize `typevec' and output the standard data types of C. */ + +void +dbxout_init (asm_file, input_file_name, syms) + FILE *asm_file; + char *input_file_name; + tree syms; +{ + char ltext_label_name[100]; + + asmfile = asm_file; + + typevec_len = 100; + typevec = (enum typestatus *) xmalloc (typevec_len * sizeof typevec[0]); + bzero (typevec, typevec_len * sizeof typevec[0]); + + /* Convert Ltext into the appropriate format for local labels in case + the system doesn't insert underscores in front of user generated + labels. */ + ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0); + + /* Put the current working directory in an N_SO symbol. */ +#ifndef DBX_WORKING_DIRECTORY /* Only some versions of DBX want this, + but GDB always does. */ + if (use_gnu_debug_info_extensions) +#endif + { + if (cwd || (cwd = getpwd ())) + { +#ifdef DBX_OUTPUT_MAIN_SOURCE_DIRECTORY + DBX_OUTPUT_MAIN_SOURCE_DIRECTORY (asmfile, cwd); +#else /* no DBX_OUTPUT_MAIN_SOURCE_DIRECTORY */ + fprintf (asmfile, "%s \"%s/\",%d,0,0,%s\n", ASM_STABS_OP, + cwd, N_SO, <ext_label_name[1]); +#endif /* no DBX_OUTPUT_MAIN_SOURCE_DIRECTORY */ + } + } + +#ifdef DBX_OUTPUT_MAIN_SOURCE_FILENAME + /* This should NOT be DBX_OUTPUT_SOURCE_FILENAME. That + would give us an N_SOL, and we want an N_SO. */ + DBX_OUTPUT_MAIN_SOURCE_FILENAME (asmfile, input_file_name); +#else /* no DBX_OUTPUT_MAIN_SOURCE_FILENAME */ + /* We include outputting `Ltext:' here, + because that gives you a way to override it. */ + /* Used to put `Ltext:' before the reference, but that loses on sun 4. */ + fprintf (asmfile, "%s \"%s\",%d,0,0,%s\n", ASM_STABS_OP, input_file_name, + N_SO, <ext_label_name[1]); + text_section (); + ASM_OUTPUT_INTERNAL_LABEL (asmfile, "Ltext", 0); +#endif /* no DBX_OUTPUT_MAIN_SOURCE_FILENAME */ + + /* Possibly output something to inform GDB that this compilation was by + GCC. It's easier for GDB to parse it when after the N_SO's. This + is used in Solaris 2. */ +#ifdef ASM_IDENTIFY_GCC_AFTER_SOURCE + ASM_IDENTIFY_GCC_AFTER_SOURCE (asmfile); +#endif + + lastfile = input_file_name; + + next_type_number = 1; + next_block_number = 2; + + /* Make sure that types `int' and `char' have numbers 1 and 2. + Definitions of other integer types will refer to those numbers. + (Actually it should no longer matter what their numbers are. + Also, if any types with tags have been defined, dbxout_symbol + will output them first, so the numbers won't be 1 and 2. That + happens in C++. So it's a good thing it should no longer matter). */ + +#ifdef DBX_OUTPUT_STANDARD_TYPES + DBX_OUTPUT_STANDARD_TYPES (syms); +#else + dbxout_symbol (TYPE_NAME (integer_type_node), 0); + dbxout_symbol (TYPE_NAME (char_type_node), 0); +#endif + + /* Get all permanent types that have typedef names, + and output them all, except for those already output. */ + + dbxout_typedefs (syms); +} + +/* Output any typedef names for types described by TYPE_DECLs in SYMS, + in the reverse order from that which is found in SYMS. */ + +static void +dbxout_typedefs (syms) + tree syms; +{ + if (syms) + { + dbxout_typedefs (TREE_CHAIN (syms)); + if (TREE_CODE (syms) == TYPE_DECL) + { + tree type = TREE_TYPE (syms); + if (TYPE_NAME (type) + && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL + && ! TREE_ASM_WRITTEN (TYPE_NAME (type))) + dbxout_symbol (TYPE_NAME (type), 0); + } + } +} + +/* Output debugging info to FILE to switch to sourcefile FILENAME. */ + +void +dbxout_source_file (file, filename) + FILE *file; + char *filename; +{ + char ltext_label_name[100]; + + if (filename && (lastfile == 0 || strcmp (filename, lastfile))) + { +#ifdef DBX_OUTPUT_SOURCE_FILENAME + DBX_OUTPUT_SOURCE_FILENAME (file, filename); +#else + ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0); + fprintf (file, "%s \"%s\",%d,0,0,%s\n", ASM_STABS_OP, + filename, N_SOL, <ext_label_name[1]); +#endif + lastfile = filename; + } +} + +/* Output a line number symbol entry into output stream FILE, + for source file FILENAME and line number LINENO. */ + +void +dbxout_source_line (file, filename, lineno) + FILE *file; + char *filename; + int lineno; +{ + dbxout_source_file (file, filename); + +#ifdef ASM_OUTPUT_SOURCE_LINE + ASM_OUTPUT_SOURCE_LINE (file, lineno); +#else + fprintf (file, "\t%s %d,0,%d\n", ASM_STABD_OP, N_SLINE, lineno); +#endif +} + +/* At the end of compilation, finish writing the symbol table. + Unless you define DBX_OUTPUT_MAIN_SOURCE_FILE_END, the default is + to do nothing. */ + +void +dbxout_finish (file, filename) + FILE *file; + char *filename; +{ +#ifdef DBX_OUTPUT_MAIN_SOURCE_FILE_END + DBX_OUTPUT_MAIN_SOURCE_FILE_END (file, filename); +#endif /* DBX_OUTPUT_MAIN_SOURCE_FILE_END */ +} + +/* Continue a symbol-description that gets too big. + End one symbol table entry with a double-backslash + and start a new one, eventually producing something like + .stabs "start......\\",code,0,value + .stabs "...rest",code,0,value */ + +static void +dbxout_continue () +{ +#ifdef DBX_CONTIN_CHAR + fprintf (asmfile, "%c", DBX_CONTIN_CHAR); +#else + fprintf (asmfile, "\\\\"); +#endif + dbxout_finish_symbol (NULL_TREE); + fprintf (asmfile, "%s \"", ASM_STABS_OP); + current_sym_nchars = 0; +} + +/* Subroutine of `dbxout_type'. Output the type fields of TYPE. + This must be a separate function because anonymous unions require + recursive calls. */ + +static void +dbxout_type_fields (type) + tree type; +{ + tree tem; + /* Output the name, type, position (in bits), size (in bits) of each + field. */ + for (tem = TYPE_FIELDS (type); tem; tem = TREE_CHAIN (tem)) + { + /* For nameless subunions and subrecords, treat their fields as ours. */ + if (DECL_NAME (tem) == NULL_TREE + && (TREE_CODE (TREE_TYPE (tem)) == UNION_TYPE + || TREE_CODE (TREE_TYPE (tem)) == QUAL_UNION_TYPE + || TREE_CODE (TREE_TYPE (tem)) == RECORD_TYPE)) + dbxout_type_fields (TREE_TYPE (tem)); + /* Omit here local type decls until we know how to support them. */ + else if (TREE_CODE (tem) == TYPE_DECL) + continue; + /* Omit here the nameless fields that are used to skip bits. */ + else if (DECL_NAME (tem) != 0 && TREE_CODE (tem) != CONST_DECL) + { + /* Continue the line if necessary, + but not before the first field. */ + if (tem != TYPE_FIELDS (type)) + CONTIN; + + if (use_gnu_debug_info_extensions + && flag_minimal_debug + && TREE_CODE (tem) == FIELD_DECL + && DECL_VIRTUAL_P (tem) + && DECL_ASSEMBLER_NAME (tem)) + { + have_used_extensions = 1; + CHARS (3 + IDENTIFIER_LENGTH (DECL_NAME (TYPE_NAME (DECL_FCONTEXT (tem))))); + fputs (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (tem)), asmfile); + dbxout_type (DECL_FCONTEXT (tem), 0, 0); + fprintf (asmfile, ":"); + dbxout_type (TREE_TYPE (tem), 0, 0); + fprintf (asmfile, ",%d;", + TREE_INT_CST_LOW (DECL_FIELD_BITPOS (tem))); + continue; + } + + fprintf (asmfile, "%s:", IDENTIFIER_POINTER (DECL_NAME (tem))); + CHARS (2 + IDENTIFIER_LENGTH (DECL_NAME (tem))); + + if (use_gnu_debug_info_extensions + && (TREE_PRIVATE (tem) || TREE_PROTECTED (tem) + || TREE_CODE (tem) != FIELD_DECL)) + { + have_used_extensions = 1; + putc ('/', asmfile); + putc ((TREE_PRIVATE (tem) ? '0' + : TREE_PROTECTED (tem) ? '1' : '2'), + asmfile); + CHARS (2); + } + + dbxout_type ((TREE_CODE (tem) == FIELD_DECL + && DECL_BIT_FIELD_TYPE (tem)) + ? DECL_BIT_FIELD_TYPE (tem) + : TREE_TYPE (tem), 0, 0); + + if (TREE_CODE (tem) == VAR_DECL) + { + if (TREE_STATIC (tem) && use_gnu_debug_info_extensions) + { + char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (tem)); + have_used_extensions = 1; + fprintf (asmfile, ":%s;", name); + CHARS (strlen (name)); + } + else + { + /* If TEM is non-static, GDB won't understand it. */ + fprintf (asmfile, ",0,0;"); + } + } + else if (TREE_CODE (DECL_FIELD_BITPOS (tem)) == INTEGER_CST) + { + fprintf (asmfile, ",%d,%d;", + TREE_INT_CST_LOW (DECL_FIELD_BITPOS (tem)), + TREE_INT_CST_LOW (DECL_SIZE (tem))); + } + else + /* This has yet to be implemented. */ + abort (); + CHARS (23); + } + } +} + +/* Subroutine of `dbxout_type_methods'. Output debug info about the + method described DECL. DEBUG_NAME is an encoding of the method's + type signature. ??? We may be able to do without DEBUG_NAME altogether + now. */ + +static void +dbxout_type_method_1 (decl, debug_name) + tree decl; + char *debug_name; +{ + tree firstarg = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (decl))); + char c1 = 'A', c2; + + if (TREE_CODE (TREE_TYPE (decl)) == FUNCTION_TYPE) + c2 = '?'; + else /* it's a METHOD_TYPE. */ + { + /* A for normal functions. + B for `const' member functions. + C for `volatile' member functions. + D for `const volatile' member functions. */ + if (TYPE_READONLY (TREE_TYPE (firstarg))) + c1 += 1; + if (TYPE_VOLATILE (TREE_TYPE (firstarg))) + c1 += 2; + + if (DECL_VINDEX (decl)) + c2 = '*'; + else + c2 = '.'; + } + + fprintf (asmfile, ":%s;%c%c%c", debug_name, + TREE_PRIVATE (decl) ? '0' : TREE_PROTECTED (decl) ? '1' : '2', c1, c2); + CHARS (IDENTIFIER_LENGTH (DECL_ASSEMBLER_NAME (decl)) + 6 + - (debug_name - IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)))); + if (DECL_VINDEX (decl)) + { + fprintf (asmfile, "%d;", + TREE_INT_CST_LOW (DECL_VINDEX (decl))); + dbxout_type (DECL_CONTEXT (decl), 0, 0); + fprintf (asmfile, ";"); + CHARS (8); + } +} + +/* Subroutine of `dbxout_type'. Output debug info about the methods defined + in TYPE. */ + +static void +dbxout_type_methods (type) + register tree type; +{ + /* C++: put out the method names and their parameter lists */ + tree methods = TYPE_METHODS (type); + tree type_encoding; + register tree fndecl; + register tree last; + char formatted_type_identifier_length[16]; + register int type_identifier_length; + + if (methods == NULL_TREE) + return; + + type_encoding = DECL_NAME (TYPE_NAME (type)); + + /* C++: Template classes break some assumptions made by this code about + the class names, constructor names, and encodings for assembler + label names. For now, disable output of dbx info for them. */ + { + char *ptr = IDENTIFIER_POINTER (type_encoding); + /* This should use index. (mrs) */ + while (*ptr && *ptr != '<') ptr++; + if (*ptr != 0) + { + static int warned; + if (!warned) + { + warned = 1; +#ifdef HAVE_TEMPLATES + if (warn_template_debugging) + warning ("dbx info for template class methods not yet supported"); +#endif + } + return; + } + } + + type_identifier_length = IDENTIFIER_LENGTH (type_encoding); + + sprintf(formatted_type_identifier_length, "%d", type_identifier_length); + + if (TREE_CODE (methods) == FUNCTION_DECL) + fndecl = methods; + else if (TREE_VEC_ELT (methods, 0) != NULL_TREE) + fndecl = TREE_VEC_ELT (methods, 0); + else + fndecl = TREE_VEC_ELT (methods, 1); + + while (fndecl) + { + tree name = DECL_NAME (fndecl); + int need_prefix = 1; + + /* Group together all the methods for the same operation. + These differ in the types of the arguments. */ + for (last = NULL_TREE; + fndecl && (last == NULL_TREE || DECL_NAME (fndecl) == DECL_NAME (last)); + fndecl = TREE_CHAIN (fndecl)) + /* Output the name of the field (after overloading), as + well as the name of the field before overloading, along + with its parameter list */ + { + /* This is the "mangled" name of the method. + It encodes the argument types. */ + char *debug_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (fndecl)); + int destructor = 0; + + CONTIN; + + last = fndecl; + + if (DECL_IGNORED_P (fndecl)) + continue; + + if (flag_minimal_debug) + { + /* Detect ordinary methods because their mangled names + start with the operation name. */ + if (!strncmp (IDENTIFIER_POINTER (name), debug_name, + IDENTIFIER_LENGTH (name))) + { + debug_name += IDENTIFIER_LENGTH (name); + if (debug_name[0] == '_' && debug_name[1] == '_') + { + char *method_name = debug_name + 2; + char *length_ptr = formatted_type_identifier_length; + /* Get past const and volatile qualifiers. */ + while (*method_name == 'C' || *method_name == 'V') + method_name++; + /* Skip digits for length of type_encoding. */ + while (*method_name == *length_ptr && *length_ptr) + length_ptr++, method_name++; + if (! strncmp (method_name, + IDENTIFIER_POINTER (type_encoding), + type_identifier_length)) + method_name += type_identifier_length; + debug_name = method_name; + } + } + /* Detect constructors by their style of name mangling. */ + else if (debug_name[0] == '_' && debug_name[1] == '_') + { + char *ctor_name = debug_name + 2; + char *length_ptr = formatted_type_identifier_length; + while (*ctor_name == 'C' || *ctor_name == 'V') + ctor_name++; + /* Skip digits for length of type_encoding. */ + while (*ctor_name == *length_ptr && *length_ptr) + length_ptr++, ctor_name++; + if (!strncmp (IDENTIFIER_POINTER (type_encoding), ctor_name, + type_identifier_length)) + debug_name = ctor_name + type_identifier_length; + } + /* The other alternative is a destructor. */ + else + destructor = 1; + + /* Output the operation name just once, for the first method + that we output. */ + if (need_prefix) + { + fprintf (asmfile, "%s::", IDENTIFIER_POINTER (name)); + CHARS (IDENTIFIER_LENGTH (name) + 2); + need_prefix = 0; + } + } + + dbxout_type (TREE_TYPE (fndecl), 0, destructor); + + dbxout_type_method_1 (fndecl, debug_name); + } + if (!need_prefix) + { + putc (';', asmfile); + CHARS (1); + } + } +} + +/* Emit a "range" type specification, which has the form: + "r;;;". + TYPE is an INTEGER_TYPE. */ + +static void +dbxout_range_type (type) + tree type; +{ + fprintf (asmfile, "r"); + if (TREE_TYPE (type) && TREE_CODE (TREE_TYPE(type)) != INTEGER_TYPE) + dbxout_type (TREE_TYPE (type), 0, 0); + else + { + /* This used to say `r1' and we used to take care + to make sure that `int' was type number 1. */ + fprintf (asmfile, "%d", TYPE_SYMTAB_ADDRESS (integer_type_node)); + } + if (TREE_CODE (TYPE_MIN_VALUE (type)) == INTEGER_CST) + fprintf (asmfile, ";%d", + TREE_INT_CST_LOW (TYPE_MIN_VALUE (type))); + else + fprintf (asmfile, ";0"); + if (TREE_CODE (TYPE_MAX_VALUE (type)) == INTEGER_CST) + fprintf (asmfile, ";%d;", + TREE_INT_CST_LOW (TYPE_MAX_VALUE (type))); + else + fprintf (asmfile, ";-1;"); +} + +/* Output a reference to a type. If the type has not yet been + described in the dbx output, output its definition now. + For a type already defined, just refer to its definition + using the type number. + + If FULL is nonzero, and the type has been described only with + a forward-reference, output the definition now. + If FULL is zero in this case, just refer to the forward-reference + using the number previously allocated. + + If SHOW_ARG_TYPES is nonzero, we output a description of the argument + types for a METHOD_TYPE. */ + +static void +dbxout_type (type, full, show_arg_types) + tree type; + int full; + int show_arg_types; +{ + register tree tem; + static int anonymous_type_number = 0; + + /* If there was an input error and we don't really have a type, + avoid crashing and write something that is at least valid + by assuming `int'. */ + if (type == error_mark_node) + type = integer_type_node; + else + { + type = TYPE_MAIN_VARIANT (type); + if (TYPE_NAME (type) + && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL + && DECL_IGNORED_P (TYPE_NAME (type))) + full = 0; + } + + if (TYPE_SYMTAB_ADDRESS (type) == 0) + { + /* Type has no dbx number assigned. Assign next available number. */ + TYPE_SYMTAB_ADDRESS (type) = next_type_number++; + + /* Make sure type vector is long enough to record about this type. */ + + if (next_type_number == typevec_len) + { + typevec = (enum typestatus *) xrealloc (typevec, typevec_len * 2 * sizeof typevec[0]); + bzero (typevec + typevec_len, typevec_len * sizeof typevec[0]); + typevec_len *= 2; + } + } + + /* Output the number of this type, to refer to it. */ + fprintf (asmfile, "%d", TYPE_SYMTAB_ADDRESS (type)); + CHARS (3); + +#ifdef DBX_TYPE_DEFINED + if (DBX_TYPE_DEFINED (type)) + return; +#endif + + /* If this type's definition has been output or is now being output, + that is all. */ + + switch (typevec[TYPE_SYMTAB_ADDRESS (type)]) + { + case TYPE_UNSEEN: + break; + case TYPE_XREF: + /* If we have already had a cross reference, + and either that's all we want or that's the best we could do, + don't repeat the cross reference. + Sun dbx crashes if we do. */ + if (! full || TYPE_SIZE (type) == 0 + /* No way in DBX fmt to describe a variable size. */ + || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) + return; + break; + case TYPE_DEFINED: + return; + } + +#ifdef DBX_NO_XREFS + /* For systems where dbx output does not allow the `=xsNAME:' syntax, + leave the type-number completely undefined rather than output + a cross-reference. */ + if (TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE + || TREE_CODE (type) == QUAL_UNION_TYPE + || TREE_CODE (type) == ENUMERAL_TYPE) + + if ((TYPE_NAME (type) != 0 && !full) + || TYPE_SIZE (type) == 0) + { + typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF; + return; + } +#endif + + /* Output a definition now. */ + + fprintf (asmfile, "="); + CHARS (1); + + /* Mark it as defined, so that if it is self-referent + we will not get into an infinite recursion of definitions. */ + + typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_DEFINED; + + switch (TREE_CODE (type)) + { + case VOID_TYPE: + case LANG_TYPE: + /* For a void type, just define it as itself; ie, "5=5". + This makes us consider it defined + without saying what it is. The debugger will make it + a void type when the reference is seen, and nothing will + ever override that default. */ + fprintf (asmfile, "%d", TYPE_SYMTAB_ADDRESS (type)); + CHARS (3); + break; + + case INTEGER_TYPE: + if (type == char_type_node && ! TREE_UNSIGNED (type)) + /* Output the type `char' as a subrange of itself! + I don't understand this definition, just copied it + from the output of pcc. + This used to use `r2' explicitly and we used to + take care to make sure that `char' was type number 2. */ + fprintf (asmfile, "r%d;0;127;", TYPE_SYMTAB_ADDRESS (type)); + else if (use_gnu_debug_info_extensions && TYPE_PRECISION (type) > BITS_PER_WORD) + { + /* This used to say `r1' and we used to take care + to make sure that `int' was type number 1. */ + fprintf (asmfile, "r%d;", TYPE_SYMTAB_ADDRESS (integer_type_node)); + print_int_cst_octal (TYPE_MIN_VALUE (type)); + fprintf (asmfile, ";"); + print_int_cst_octal (TYPE_MAX_VALUE (type)); + fprintf (asmfile, ";"); + } + else /* Output other integer types as subranges of `int'. */ + dbxout_range_type (type); + CHARS (25); + break; + + case REAL_TYPE: + /* This used to say `r1' and we used to take care + to make sure that `int' was type number 1. */ + fprintf (asmfile, "r%d;%d;0;", TYPE_SYMTAB_ADDRESS (integer_type_node), + int_size_in_bytes (type)); + CHARS (16); + break; + + case CHAR_TYPE: + /* Output the type `char' as a subrange of itself. + That is what pcc seems to do. */ + fprintf (asmfile, "r%d;0;%d;", TYPE_SYMTAB_ADDRESS (char_type_node), + TREE_UNSIGNED (type) ? 255 : 127); + CHARS (9); + break; + + case BOOLEAN_TYPE: /* Define as enumeral type (False, True) */ + fprintf (asmfile, "eFalse:0,True:1,;"); + CHARS (17); + break; + + case FILE_TYPE: + putc ('d', asmfile); + CHARS (1); + dbxout_type (TREE_TYPE (type), 0, 0); + break; + + case COMPLEX_TYPE: + /* Differs from the REAL_TYPE by its new data type number */ + + if (TREE_CODE (TREE_TYPE (type)) == REAL_TYPE) + { + fprintf (asmfile, "r%d;%d;0;", + TYPE_SYMTAB_ADDRESS (type), + int_size_in_bytes (TREE_TYPE (type))); + CHARS (15); /* The number is probably incorrect here. */ + } + else + { + /* Output a complex integer type as a structure, + pending some other way to do it. */ + fprintf (asmfile, "s%d", int_size_in_bytes (type)); + + fprintf (asmfile, "real:"); + CHARS (10); + dbxout_type (TREE_TYPE (type), 0, 0); + fprintf (asmfile, ",%d,%d;", + 0, TYPE_PRECISION (TREE_TYPE (type))); + CHARS (8); + fprintf (asmfile, "imag:"); + CHARS (5); + dbxout_type (TREE_TYPE (type), 0, 0); + fprintf (asmfile, ",%d,%d;;", + TYPE_PRECISION (TREE_TYPE (type)), + TYPE_PRECISION (TREE_TYPE (type))); + CHARS (9); + } + break; + + case SET_TYPE: + putc ('S', asmfile); + CHARS (1); + dbxout_type (TREE_TYPE (type), 0, 0); + break; + + case ARRAY_TYPE: + /* Output "a" followed by a range type definition + for the index type of the array + followed by a reference to the target-type. + ar1;0;N;M for a C array of type M and size N+1. */ + tem = TYPE_DOMAIN (type); + if (tem == NULL) + fprintf (asmfile, "ar%d;0;-1;", + TYPE_SYMTAB_ADDRESS (integer_type_node)); + else + { + fprintf (asmfile, "a"); + dbxout_range_type (tem); + } + CHARS (17); + dbxout_type (TREE_TYPE (type), 0, 0); + break; + + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + { + int i, n_baseclasses = 0; + + if (TYPE_BINFO (type) != 0 && TYPE_BINFO_BASETYPES (type) != 0) + n_baseclasses = TREE_VEC_LENGTH (TYPE_BINFO_BASETYPES (type)); + + /* Output a structure type. */ + if ((TYPE_NAME (type) != 0 + /* Long ago, Tiemann said this creates output that "confuses GDB". + In April 93, mrs@cygnus.com said there is no such problem. + The type decls made automatically by struct specifiers + are marked with DECL_IGNORED_P in C++. */ +#if 0 /* This creates output for anonymous classes which confuses GDB. */ + && ! (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL + && DECL_IGNORED_P (TYPE_NAME (type))) +#endif + && !full) + || TYPE_SIZE (type) == 0 + /* No way in DBX fmt to describe a variable size. */ + || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) + { + /* If the type is just a cross reference, output one + and mark the type as partially described. + If it later becomes defined, we will output + its real definition. + If the type has a name, don't nest its definition within + another type's definition; instead, output an xref + and let the definition come when the name is defined. */ + fprintf (asmfile, (TREE_CODE (type) == RECORD_TYPE) ? "xs" : "xu"); + CHARS (3); +#if 0 /* This assertion is legitimately false in C++. */ + /* We shouldn't be outputting a reference to a type before its + definition unless the type has a tag name. + A typedef name without a tag name should be impossible. */ + if (TREE_CODE (TYPE_NAME (type)) != IDENTIFIER_NODE) + abort (); +#endif + if (TYPE_NAME (type) != 0) + dbxout_type_name (type); + else + fprintf (asmfile, "$$%d", anonymous_type_number++); + fprintf (asmfile, ":"); + typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF; + break; + } + + /* Identify record or union, and print its size. */ + fprintf (asmfile, (TREE_CODE (type) == RECORD_TYPE) ? "s%d" : "u%d", + int_size_in_bytes (type)); + + if (use_gnu_debug_info_extensions) + { + if (n_baseclasses) + { + have_used_extensions = 1; + fprintf (asmfile, "!%d,", n_baseclasses); + CHARS (8); + } + } + for (i = 0; i < n_baseclasses; i++) + { + tree child = TREE_VEC_ELT (BINFO_BASETYPES (TYPE_BINFO (type)), i); + if (use_gnu_debug_info_extensions) + { + have_used_extensions = 1; + putc (TREE_VIA_VIRTUAL (child) ? '1' + : '0', + asmfile); + putc (TREE_VIA_PUBLIC (child) ? '2' + : '0', + asmfile); + fprintf (asmfile, "%d,", + TREE_INT_CST_LOW (BINFO_OFFSET (child)) * BITS_PER_UNIT); + CHARS (15); + dbxout_type (BINFO_TYPE (child), 0, 0); + putc (';', asmfile); + } + else + { + /* Print out the base class information with fields + which have the same names at the types they hold. */ + dbxout_type_name (BINFO_TYPE (child)); + putc (':', asmfile); + dbxout_type (BINFO_TYPE (child), full, 0); + fprintf (asmfile, ",%d,%d;", + TREE_INT_CST_LOW (BINFO_OFFSET (child)) * BITS_PER_UNIT, + TREE_INT_CST_LOW (DECL_SIZE (TYPE_NAME (BINFO_TYPE (child)))) * BITS_PER_UNIT); + CHARS (20); + } + } + } + + CHARS (11); + + /* Write out the field declarations. */ + dbxout_type_fields (type); + if (use_gnu_debug_info_extensions && TYPE_METHODS (type) != NULL_TREE) + { + have_used_extensions = 1; + dbxout_type_methods (type); + } + putc (';', asmfile); + + if (use_gnu_debug_info_extensions && TREE_CODE (type) == RECORD_TYPE + /* Avoid the ~ if we don't really need it--it confuses dbx. */ + && TYPE_VFIELD (type)) + { + have_used_extensions = 1; + + /* Tell GDB+ that it may keep reading. */ + putc ('~', asmfile); + + /* We need to write out info about what field this class + uses as its "main" vtable pointer field, because if this + field is inherited from a base class, GDB cannot necessarily + figure out which field it's using in time. */ + if (TYPE_VFIELD (type)) + { + putc ('%', asmfile); + dbxout_type (DECL_FCONTEXT (TYPE_VFIELD (type)), 0, 0); + } + putc (';', asmfile); + CHARS (3); + } + break; + + case ENUMERAL_TYPE: + if ((TYPE_NAME (type) != 0 && !full + && (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL + && ! DECL_IGNORED_P (TYPE_NAME (type)))) + || TYPE_SIZE (type) == 0) + { + fprintf (asmfile, "xe"); + CHARS (3); + dbxout_type_name (type); + typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF; + fprintf (asmfile, ":"); + return; + } +#ifdef DBX_OUTPUT_ENUM + DBX_OUTPUT_ENUM (asmfile, type); +#else + putc ('e', asmfile); + CHARS (1); + for (tem = TYPE_VALUES (type); tem; tem = TREE_CHAIN (tem)) + { + fprintf (asmfile, "%s:%d,", IDENTIFIER_POINTER (TREE_PURPOSE (tem)), + TREE_INT_CST_LOW (TREE_VALUE (tem))); + CHARS (11 + IDENTIFIER_LENGTH (TREE_PURPOSE (tem))); + if (TREE_CHAIN (tem) != 0) + CONTIN; + } + putc (';', asmfile); + CHARS (1); +#endif + break; + + case POINTER_TYPE: + putc ('*', asmfile); + CHARS (1); + dbxout_type (TREE_TYPE (type), 0, 0); + break; + + case METHOD_TYPE: + if (use_gnu_debug_info_extensions) + { + have_used_extensions = 1; + putc ('#', asmfile); + CHARS (1); + if (flag_minimal_debug && !show_arg_types) + { + /* Normally, just output the return type. + The argument types are encoded in the method name. */ + putc ('#', asmfile); + dbxout_type (TREE_TYPE (type), 0, 0); + putc (';', asmfile); + CHARS (1); + } + else + { + /* When outputting destructors, we need to write + the argument types out longhand. */ + dbxout_type (TYPE_METHOD_BASETYPE (type), 0, 0); + putc (',', asmfile); + CHARS (1); + dbxout_type (TREE_TYPE (type), 0, 0); + dbxout_args (TYPE_ARG_TYPES (type)); + putc (';', asmfile); + CHARS (1); + } + } + else + { + /* Treat it as a function type. */ + dbxout_type (TREE_TYPE (type), 0, 0); + } + break; + + case OFFSET_TYPE: + if (use_gnu_debug_info_extensions) + { + have_used_extensions = 1; + putc ('@', asmfile); + CHARS (1); + dbxout_type (TYPE_OFFSET_BASETYPE (type), 0, 0); + putc (',', asmfile); + CHARS (1); + dbxout_type (TREE_TYPE (type), 0, 0); + } + else + { + /* Should print as an int, because it is really + just an offset. */ + dbxout_type (integer_type_node, 0, 0); + } + break; + + case REFERENCE_TYPE: + if (use_gnu_debug_info_extensions) + have_used_extensions = 1; + putc (use_gnu_debug_info_extensions ? '&' : '*', asmfile); + CHARS (1); + dbxout_type (TREE_TYPE (type), 0, 0); + break; + + case FUNCTION_TYPE: + putc ('f', asmfile); + CHARS (1); + dbxout_type (TREE_TYPE (type), 0, 0); + break; + + default: + abort (); + } +} + +/* Print the value of integer constant C, in octal, + handling double precision. */ + +static void +print_int_cst_octal (c) + tree c; +{ + unsigned HOST_WIDE_INT high = TREE_INT_CST_HIGH (c); + unsigned HOST_WIDE_INT low = TREE_INT_CST_LOW (c); + int excess = (3 - (HOST_BITS_PER_WIDE_INT % 3)); + + fprintf (asmfile, "0"); + + if (excess == 3) + { + print_octal (high, HOST_BITS_PER_WIDE_INT / 3); + print_octal (low, HOST_BITS_PER_WIDE_INT / 3); + } + else + { + unsigned HOST_WIDE_INT beg = high >> excess; + unsigned HOST_WIDE_INT middle + = ((high & (((HOST_WIDE_INT) 1 << excess) - 1)) << (3 - excess) + | (low >> (HOST_BITS_PER_WIDE_INT / 3 * 3))); + unsigned HOST_WIDE_INT end + = low & (((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 3 * 3)) - 1); + fprintf (asmfile, "%o%01o", beg, middle); + print_octal (end, HOST_BITS_PER_WIDE_INT / 3); + } +} + +static void +print_octal (value, digits) + unsigned HOST_WIDE_INT value; + int digits; +{ + int i; + + for (i = digits - 1; i >= 0; i--) + fprintf (asmfile, "%01o", ((value >> (3 * i)) & 7)); +} + +/* Output the name of type TYPE, with no punctuation. + Such names can be set up either by typedef declarations + or by struct, enum and union tags. */ + +static void +dbxout_type_name (type) + register tree type; +{ + tree t; + if (TYPE_NAME (type) == 0) + abort (); + if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) + { + t = TYPE_NAME (type); + } + else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL) + { + t = DECL_NAME (TYPE_NAME (type)); + } + else + abort (); + + fprintf (asmfile, "%s", IDENTIFIER_POINTER (t)); + CHARS (IDENTIFIER_LENGTH (t)); +} + +/* Output a .stabs for the symbol defined by DECL, + which must be a ..._DECL node in the normal namespace. + It may be a CONST_DECL, a FUNCTION_DECL, a PARM_DECL or a VAR_DECL. + LOCAL is nonzero if the scope is less than the entire file. */ + +void +dbxout_symbol (decl, local) + tree decl; + int local; +{ + int letter = 0; + tree type = TREE_TYPE (decl); + tree context = NULL_TREE; + int regno = -1; + + /* Cast avoids warning in old compilers. */ + current_sym_code = (STAB_CODE_TYPE) 0; + current_sym_value = 0; + current_sym_addr = 0; + + /* Ignore nameless syms, but don't ignore type tags. */ + + if ((DECL_NAME (decl) == 0 && TREE_CODE (decl) != TYPE_DECL) + || DECL_IGNORED_P (decl)) + return; + + dbxout_prepare_symbol (decl); + + /* The output will always start with the symbol name, + so always count that in the length-output-so-far. */ + + if (DECL_NAME (decl) != 0) + current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (decl)); + + switch (TREE_CODE (decl)) + { + case CONST_DECL: + /* Enum values are defined by defining the enum type. */ + break; + + case FUNCTION_DECL: + if (DECL_RTL (decl) == 0) + return; + if (DECL_EXTERNAL (decl)) + break; + /* Don't mention a nested function under its parent. */ + context = decl_function_context (decl); + if (context == current_function_decl) + break; + if (GET_CODE (DECL_RTL (decl)) != MEM + || GET_CODE (XEXP (DECL_RTL (decl), 0)) != SYMBOL_REF) + break; + FORCE_TEXT; + + fprintf (asmfile, "%s \"%s:%c", ASM_STABS_OP, + IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), + TREE_PUBLIC (decl) ? 'F' : 'f'); + + current_sym_code = N_FUN; + current_sym_addr = XEXP (DECL_RTL (decl), 0); + + if (TREE_TYPE (type)) + dbxout_type (TREE_TYPE (type), 0, 0); + else + dbxout_type (void_type_node, 0, 0); + + /* For a nested function, when that function is compiled, + mention the containing function name + as well as (since dbx wants it) our own assembler-name. */ + if (context != 0) + fprintf (asmfile, ",%s,%s", + IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), + IDENTIFIER_POINTER (DECL_NAME (context))); + + dbxout_finish_symbol (decl); + break; + + case TYPE_DECL: +#if 0 + /* This seems all wrong. Outputting most kinds of types gives no name + at all. A true definition gives no name; a cross-ref for a + structure can give the tag name, but not a type name. + It seems that no typedef name is defined by outputting a type. */ + + /* If this typedef name was defined by outputting the type, + don't duplicate it. */ + if (typevec[TYPE_SYMTAB_ADDRESS (type)] == TYPE_DEFINED + && TYPE_NAME (TREE_TYPE (decl)) == decl) + return; +#endif + /* Don't output the same typedef twice. + And don't output what language-specific stuff doesn't want output. */ + if (TREE_ASM_WRITTEN (decl) || DECL_IGNORED_P (decl)) + return; + + FORCE_TEXT; + + { + int tag_needed = 1; + int did_output = 0; + + if (DECL_NAME (decl)) + { + /* Nonzero means we must output a tag as well as a typedef. */ + tag_needed = 0; + + /* Handle the case of a C++ structure or union + where the TYPE_NAME is a TYPE_DECL + which gives both a typedef name and a tag. */ + /* dbx requires the tag first and the typedef second. */ + if ((TREE_CODE (type) == RECORD_TYPE + || TREE_CODE (type) == UNION_TYPE + || TREE_CODE (type) == QUAL_UNION_TYPE) + && TYPE_NAME (type) == decl + && !(use_gnu_debug_info_extensions && have_used_extensions) + && !TREE_ASM_WRITTEN (TYPE_NAME (type)) + /* Distinguish the implicit typedefs of C++ + from explicit ones that might be found in C. */ + && DECL_SOURCE_LINE (decl) == 0) + { + tree name = TYPE_NAME (type); + if (TREE_CODE (name) == TYPE_DECL) + name = DECL_NAME (name); + + current_sym_code = DBX_TYPE_DECL_STABS_CODE; + current_sym_value = 0; + current_sym_addr = 0; + current_sym_nchars = 2 + IDENTIFIER_LENGTH (name); + + fprintf (asmfile, "%s \"%s:T", ASM_STABS_OP, + IDENTIFIER_POINTER (name)); + dbxout_type (type, 1, 0); + dbxout_finish_symbol (NULL_TREE); + } + + /* Output typedef name. */ + fprintf (asmfile, "%s \"%s:", ASM_STABS_OP, + IDENTIFIER_POINTER (DECL_NAME (decl))); + + /* Short cut way to output a tag also. */ + if ((TREE_CODE (type) == RECORD_TYPE + || TREE_CODE (type) == UNION_TYPE + || TREE_CODE (type) == QUAL_UNION_TYPE) + && TYPE_NAME (type) == decl) + { + if (use_gnu_debug_info_extensions && have_used_extensions) + { + putc ('T', asmfile); + TREE_ASM_WRITTEN (TYPE_NAME (type)) = 1; + } +#if 0 /* Now we generate the tag for this case up above. */ + else + tag_needed = 1; +#endif + } + + putc ('t', asmfile); + current_sym_code = DBX_TYPE_DECL_STABS_CODE; + + dbxout_type (type, 1, 0); + dbxout_finish_symbol (decl); + did_output = 1; + } + + /* Don't output a tag if this is an incomplete type (TYPE_SIZE is + zero). This prevents the sun4 Sun OS 4.x dbx from crashing. */ + + if (tag_needed && TYPE_NAME (type) != 0 && TYPE_SIZE (type) != 0 + && !TREE_ASM_WRITTEN (TYPE_NAME (type))) + { + /* For a TYPE_DECL with no name, but the type has a name, + output a tag. + This is what represents `struct foo' with no typedef. */ + /* In C++, the name of a type is the corresponding typedef. + In C, it is an IDENTIFIER_NODE. */ + tree name = TYPE_NAME (type); + if (TREE_CODE (name) == TYPE_DECL) + name = DECL_NAME (name); + + current_sym_code = DBX_TYPE_DECL_STABS_CODE; + current_sym_value = 0; + current_sym_addr = 0; + current_sym_nchars = 2 + IDENTIFIER_LENGTH (name); + + fprintf (asmfile, "%s \"%s:T", ASM_STABS_OP, + IDENTIFIER_POINTER (name)); + dbxout_type (type, 1, 0); + dbxout_finish_symbol (NULL_TREE); + did_output = 1; + } + + /* If an enum type has no name, it cannot be referred to, + but we must output it anyway, since the enumeration constants + can be referred to. */ + if (!did_output && TREE_CODE (type) == ENUMERAL_TYPE) + { + current_sym_code = DBX_TYPE_DECL_STABS_CODE; + current_sym_value = 0; + current_sym_addr = 0; + current_sym_nchars = 2; + + /* Some debuggers fail when given NULL names, so give this a + harmless name of ` '. */ + fprintf (asmfile, "%s \" :T", ASM_STABS_OP); + dbxout_type (type, 1, 0); + dbxout_finish_symbol (NULL_TREE); + } + + /* Prevent duplicate output of a typedef. */ + TREE_ASM_WRITTEN (decl) = 1; + break; + } + + case PARM_DECL: + /* Parm decls go in their own separate chains + and are output by dbxout_reg_parms and dbxout_parms. */ + abort (); + + case RESULT_DECL: + /* Named return value, treat like a VAR_DECL. */ + case VAR_DECL: + if (DECL_RTL (decl) == 0) + return; + /* Don't mention a variable that is external. + Let the file that defines it describe it. */ + if (DECL_EXTERNAL (decl)) + break; + + /* If the variable is really a constant + and not written in memory, inform the debugger. */ + if (TREE_STATIC (decl) && TREE_READONLY (decl) + && DECL_INITIAL (decl) != 0 + && ! TREE_ASM_WRITTEN (decl) + && (DECL_FIELD_CONTEXT (decl) == NULL_TREE + || TREE_CODE (DECL_FIELD_CONTEXT (decl)) == BLOCK)) + { + if (TREE_PUBLIC (decl) == 0) + { + /* The sun4 assembler does not grok this. */ + char *name = IDENTIFIER_POINTER (DECL_NAME (decl)); + if (TREE_CODE (TREE_TYPE (decl)) == INTEGER_TYPE + || TREE_CODE (TREE_TYPE (decl)) == ENUMERAL_TYPE) + { + HOST_WIDE_INT ival = TREE_INT_CST_LOW (DECL_INITIAL (decl)); +#ifdef DBX_OUTPUT_CONSTANT_SYMBOL + DBX_OUTPUT_CONSTANT_SYMBOL (asmfile, name, ival); +#else + fprintf (asmfile, "%s \"%s:c=i%d\",0x%x,0,0,0\n", + ASM_STABS_OP, name, ival, N_LSYM); +#endif + return; + } + else if (TREE_CODE (TREE_TYPE (decl)) == REAL_TYPE) + { + /* don't know how to do this yet. */ + } + break; + } + /* else it is something we handle like a normal variable. */ + } + + DECL_RTL (decl) = eliminate_regs (DECL_RTL (decl), 0, NULL_RTX); +#ifdef LEAF_REG_REMAP + if (leaf_function) + leaf_renumber_regs_insn (DECL_RTL (decl)); +#endif + + /* Don't mention a variable at all + if it was completely optimized into nothingness. + + If DECL was from an inline function, then it's rtl + is not identically the rtl that was used in this + particular compilation. */ + if (GET_CODE (DECL_RTL (decl)) == REG) + { + regno = REGNO (DECL_RTL (decl)); + if (regno >= FIRST_PSEUDO_REGISTER) + return; + } + else if (GET_CODE (DECL_RTL (decl)) == SUBREG) + { + rtx value = DECL_RTL (decl); + int offset = 0; + while (GET_CODE (value) == SUBREG) + { + offset += SUBREG_WORD (value); + value = SUBREG_REG (value); + } + if (GET_CODE (value) == REG) + { + regno = REGNO (value); + if (regno >= FIRST_PSEUDO_REGISTER) + return; + regno += offset; + } + alter_subreg (DECL_RTL (decl)); + } + + /* The kind-of-variable letter depends on where + the variable is and on the scope of its name: + G and N_GSYM for static storage and global scope, + S for static storage and file scope, + V for static storage and local scope, + for those two, use N_LCSYM if data is in bss segment, + N_STSYM if in data segment, N_FUN otherwise. + (We used N_FUN originally, then changed to N_STSYM + to please GDB. However, it seems that confused ld. + Now GDB has been fixed to like N_FUN, says Kingdon.) + no letter at all, and N_LSYM, for auto variable, + r and N_RSYM for register variable. */ + + if (GET_CODE (DECL_RTL (decl)) == MEM + && GET_CODE (XEXP (DECL_RTL (decl), 0)) == SYMBOL_REF) + { + if (TREE_PUBLIC (decl)) + { + letter = 'G'; + current_sym_code = N_GSYM; + } + else + { + current_sym_addr = XEXP (DECL_RTL (decl), 0); + + letter = decl_function_context (decl) ? 'V' : 'S'; + + if (!DECL_INITIAL (decl)) + current_sym_code = N_LCSYM; + else if (TREE_READONLY (decl) && ! TREE_THIS_VOLATILE (decl)) + /* This is not quite right, but it's the closest + of all the codes that Unix defines. */ + current_sym_code = DBX_STATIC_CONST_VAR_CODE; + else + { +/* Ultrix `as' seems to need this. */ +#ifdef DBX_STATIC_STAB_DATA_SECTION + data_section (); +#endif + current_sym_code = N_STSYM; + } + } + } + else if (regno >= 0) + { + letter = 'r'; + current_sym_code = N_RSYM; + current_sym_value = DBX_REGISTER_NUMBER (regno); + } + else if (GET_CODE (DECL_RTL (decl)) == MEM + && (GET_CODE (XEXP (DECL_RTL (decl), 0)) == MEM + || (GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG + && REGNO (XEXP (DECL_RTL (decl), 0)) != FRAME_POINTER_REGNUM))) + /* If the value is indirect by memory or by a register + that isn't the frame pointer + then it means the object is variable-sized and address through + that register or stack slot. DBX has no way to represent this + so all we can do is output the variable as a pointer. + If it's not a parameter, ignore it. + (VAR_DECLs like this can be made by integrate.c.) */ + { + if (GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG) + { + letter = 'r'; + current_sym_code = N_RSYM; + current_sym_value = DBX_REGISTER_NUMBER (REGNO (XEXP (DECL_RTL (decl), 0))); + } + else + { + current_sym_code = N_LSYM; + /* DECL_RTL looks like (MEM (MEM (PLUS (REG...) (CONST_INT...)))). + We want the value of that CONST_INT. */ + current_sym_value + = DEBUGGER_AUTO_OFFSET (XEXP (XEXP (DECL_RTL (decl), 0), 0)); + } + + /* Effectively do build_pointer_type, but don't cache this type, + since it might be temporary whereas the type it points to + might have been saved for inlining. */ + /* Don't use REFERENCE_TYPE because dbx can't handle that. */ + type = make_node (POINTER_TYPE); + TREE_TYPE (type) = TREE_TYPE (decl); + } + else if (GET_CODE (DECL_RTL (decl)) == MEM + && GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG) + { + current_sym_code = N_LSYM; + current_sym_value = DEBUGGER_AUTO_OFFSET (XEXP (DECL_RTL (decl), 0)); + } + else if (GET_CODE (DECL_RTL (decl)) == MEM + && GET_CODE (XEXP (DECL_RTL (decl), 0)) == PLUS + && GET_CODE (XEXP (XEXP (DECL_RTL (decl), 0), 1)) == CONST_INT) + { + current_sym_code = N_LSYM; + /* DECL_RTL looks like (MEM (PLUS (REG...) (CONST_INT...))) + We want the value of that CONST_INT. */ + current_sym_value = DEBUGGER_AUTO_OFFSET (XEXP (DECL_RTL (decl), 0)); + } + else if (GET_CODE (DECL_RTL (decl)) == MEM + && GET_CODE (XEXP (DECL_RTL (decl), 0)) == CONST) + { + /* Handle an obscure case which can arise when optimizing and + when there are few available registers. (This is *always* + the case for i386/i486 targets). The DECL_RTL looks like + (MEM (CONST ...)) even though this variable is a local `auto' + or a local `register' variable. In effect, what has happened + is that the reload pass has seen that all assignments and + references for one such a local variable can be replaced by + equivalent assignments and references to some static storage + variable, thereby avoiding the need for a register. In such + cases we're forced to lie to debuggers and tell them that + this variable was itself `static'. */ + current_sym_code = N_LCSYM; + letter = 'V'; + current_sym_addr = XEXP (XEXP (DECL_RTL (decl), 0), 0); + } + else + /* Address might be a MEM, when DECL is a variable-sized object. + Or it might be const0_rtx, meaning previous passes + want us to ignore this variable. */ + break; + + /* Ok, start a symtab entry and output the variable name. */ + FORCE_TEXT; + +#ifdef DBX_STATIC_BLOCK_START + DBX_STATIC_BLOCK_START (asmfile, current_sym_code); +#endif + + /* One slight hitch: if this is a VAR_DECL which is a static + class member, we must put out the mangled name instead of the + DECL_NAME. */ + { + char *name; + /* Note also that static member (variable) names DO NOT begin + with underscores in .stabs directives. */ + if (DECL_LANG_SPECIFIC (decl)) + name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + else + name = IDENTIFIER_POINTER (DECL_NAME (decl)); + fprintf (asmfile, "%s \"%s:", ASM_STABS_OP, name); + } + if (letter) putc (letter, asmfile); + dbxout_type (type, 0, 0); + dbxout_finish_symbol (decl); + +#ifdef DBX_STATIC_BLOCK_END + DBX_STATIC_BLOCK_END (asmfile, current_sym_code); +#endif + break; + } +} + +static void +dbxout_prepare_symbol (decl) + tree decl; +{ +#ifdef WINNING_GDB + char *filename = DECL_SOURCE_FILE (decl); + + dbxout_source_file (asmfile, filename); +#endif +} + +static void +dbxout_finish_symbol (sym) + tree sym; +{ +#ifdef DBX_FINISH_SYMBOL + DBX_FINISH_SYMBOL (sym); +#else + int line = 0; + if (use_gnu_debug_info_extensions && sym != 0) + line = DECL_SOURCE_LINE (sym); + + fprintf (asmfile, "\",%d,0,%d,", current_sym_code, line); + if (current_sym_addr) + output_addr_const (asmfile, current_sym_addr); + else + fprintf (asmfile, "%d", current_sym_value); + putc ('\n', asmfile); +#endif +} + +/* Output definitions of all the decls in a chain. */ + +void +dbxout_syms (syms) + tree syms; +{ + while (syms) + { + dbxout_symbol (syms, 1); + syms = TREE_CHAIN (syms); + } +} + +/* The following two functions output definitions of function parameters. + Each parameter gets a definition locating it in the parameter list. + Each parameter that is a register variable gets a second definition + locating it in the register. + + Printing or argument lists in gdb uses the definitions that + locate in the parameter list. But reference to the variable in + expressions uses preferentially the definition as a register. */ + +/* Output definitions, referring to storage in the parmlist, + of all the parms in PARMS, which is a chain of PARM_DECL nodes. */ + +void +dbxout_parms (parms) + tree parms; +{ + for (; parms; parms = TREE_CHAIN (parms)) + if (DECL_NAME (parms) && TREE_TYPE (parms) != error_mark_node) + { + dbxout_prepare_symbol (parms); + + /* Perform any necessary register eliminations on the parameter's rtl, + so that the debugging output will be accurate. */ + DECL_INCOMING_RTL (parms) + = eliminate_regs (DECL_INCOMING_RTL (parms), 0, NULL_RTX); + DECL_RTL (parms) = eliminate_regs (DECL_RTL (parms), 0, NULL_RTX); +#ifdef LEAF_REG_REMAP + if (leaf_function) + { + leaf_renumber_regs_insn (DECL_INCOMING_RTL (parms)); + leaf_renumber_regs_insn (DECL_RTL (parms)); + } +#endif + + if (PARM_PASSED_IN_MEMORY (parms)) + { + rtx addr = XEXP (DECL_INCOMING_RTL (parms), 0); + + /* ??? Here we assume that the parm address is indexed + off the frame pointer or arg pointer. + If that is not true, we produce meaningless results, + but do not crash. */ + if (GET_CODE (addr) == PLUS + && GET_CODE (XEXP (addr, 1)) == CONST_INT) + current_sym_value = INTVAL (XEXP (addr, 1)); + else + current_sym_value = 0; + + current_sym_code = N_PSYM; + current_sym_addr = 0; + + FORCE_TEXT; + if (DECL_NAME (parms)) + { + current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms)); + + fprintf (asmfile, "%s \"%s:%c", ASM_STABS_OP, + IDENTIFIER_POINTER (DECL_NAME (parms)), + DBX_MEMPARM_STABS_LETTER); + } + else + { + current_sym_nchars = 8; + fprintf (asmfile, "%s \"(anon):%c", ASM_STABS_OP, + DBX_MEMPARM_STABS_LETTER); + } + + if (GET_CODE (DECL_RTL (parms)) == REG + && REGNO (DECL_RTL (parms)) >= 0 + && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER) + dbxout_type (DECL_ARG_TYPE (parms), 0, 0); + else + { + int original_value = current_sym_value; + + /* This is the case where the parm is passed as an int or double + and it is converted to a char, short or float and stored back + in the parmlist. In this case, describe the parm + with the variable's declared type, and adjust the address + if the least significant bytes (which we are using) are not + the first ones. */ +#if BYTES_BIG_ENDIAN + if (TREE_TYPE (parms) != DECL_ARG_TYPE (parms)) + current_sym_value += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms))) + - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms)))); +#endif + + if (GET_CODE (DECL_RTL (parms)) == MEM + && GET_CODE (XEXP (DECL_RTL (parms), 0)) == PLUS + && GET_CODE (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == CONST_INT + && INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == current_sym_value) + dbxout_type (TREE_TYPE (parms), 0, 0); + else + { + current_sym_value = original_value; + dbxout_type (DECL_ARG_TYPE (parms), 0, 0); + } + } + current_sym_value = DEBUGGER_ARG_OFFSET (current_sym_value, addr); + dbxout_finish_symbol (parms); + } + else if (GET_CODE (DECL_RTL (parms)) == REG) + { + rtx best_rtl; + char regparm_letter; + /* Parm passed in registers and lives in registers or nowhere. */ + + current_sym_code = DBX_REGPARM_STABS_CODE; + regparm_letter = DBX_REGPARM_STABS_LETTER; + current_sym_addr = 0; + + /* If parm lives in a register, use that register; + pretend the parm was passed there. It would be more consistent + to describe the register where the parm was passed, + but in practice that register usually holds something else. */ + if (REGNO (DECL_RTL (parms)) >= 0 + && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER) + best_rtl = DECL_RTL (parms); + /* If the parm lives nowhere, + use the register where it was passed. */ + else + best_rtl = DECL_INCOMING_RTL (parms); + current_sym_value = DBX_REGISTER_NUMBER (REGNO (best_rtl)); + + FORCE_TEXT; + if (DECL_NAME (parms)) + { + current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms)); + fprintf (asmfile, "%s \"%s:%c", ASM_STABS_OP, + IDENTIFIER_POINTER (DECL_NAME (parms)), + regparm_letter); + } + else + { + current_sym_nchars = 8; + fprintf (asmfile, "%s \"(anon):%c", ASM_STABS_OP, + regparm_letter); + } + + dbxout_type (DECL_ARG_TYPE (parms), 0, 0); + dbxout_finish_symbol (parms); + } + else if (GET_CODE (DECL_RTL (parms)) == MEM + && GET_CODE (XEXP (DECL_RTL (parms), 0)) == REG) +/* && rtx_equal_p (XEXP (DECL_RTL (parms), 0), + DECL_INCOMING_RTL (parms))) */ + { + /* Parm was passed via invisible reference. + That is, its address was passed in a register. + Output it as if it lived in that register. + The debugger will know from the type + that it was actually passed by invisible reference. */ + + char regparm_letter; + /* Parm passed in registers and lives in registers or nowhere. */ + + current_sym_code = DBX_REGPARM_STABS_CODE; + regparm_letter = DBX_REGPARM_STABS_LETTER; + + /* DECL_RTL looks like (MEM (REG...). Get the register number. */ + current_sym_value = REGNO (XEXP (DECL_RTL (parms), 0)); + current_sym_addr = 0; + + FORCE_TEXT; + if (DECL_NAME (parms)) + { + current_sym_nchars = 2 + strlen (IDENTIFIER_POINTER (DECL_NAME (parms))); + + fprintf (asmfile, "%s \"%s:%c", ASM_STABS_OP, + IDENTIFIER_POINTER (DECL_NAME (parms)), + DBX_REGPARM_STABS_LETTER); + } + else + { + current_sym_nchars = 8; + fprintf (asmfile, "%s \"(anon):%c", ASM_STABS_OP, + DBX_REGPARM_STABS_LETTER); + } + + dbxout_type (TREE_TYPE (parms), 0, 0); + dbxout_finish_symbol (parms); + } + else if (GET_CODE (DECL_RTL (parms)) == MEM + && XEXP (DECL_RTL (parms), 0) != const0_rtx + /* ??? A constant address for a parm can happen + when the reg it lives in is equiv to a constant in memory. + Should make this not happen, after 2.4. */ + && ! CONSTANT_P (XEXP (DECL_RTL (parms), 0))) + { + /* Parm was passed in registers but lives on the stack. */ + + current_sym_code = N_PSYM; + /* DECL_RTL looks like (MEM (PLUS (REG...) (CONST_INT...))), + in which case we want the value of that CONST_INT, + or (MEM (REG ...)) or (MEM (MEM ...)), + in which case we use a value of zero. */ + if (GET_CODE (XEXP (DECL_RTL (parms), 0)) == REG + || GET_CODE (XEXP (DECL_RTL (parms), 0)) == MEM) + current_sym_value = 0; + else + current_sym_value = INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)); + current_sym_addr = 0; + + FORCE_TEXT; + if (DECL_NAME (parms)) + { + current_sym_nchars = 2 + strlen (IDENTIFIER_POINTER (DECL_NAME (parms))); + + fprintf (asmfile, "%s \"%s:%c", ASM_STABS_OP, + IDENTIFIER_POINTER (DECL_NAME (parms)), + DBX_MEMPARM_STABS_LETTER); + } + else + { + current_sym_nchars = 8; + fprintf (asmfile, "%s \"(anon):%c", ASM_STABS_OP, + DBX_MEMPARM_STABS_LETTER); + } + + current_sym_value + = DEBUGGER_ARG_OFFSET (current_sym_value, + XEXP (DECL_RTL (parms), 0)); + dbxout_type (TREE_TYPE (parms), 0, 0); + dbxout_finish_symbol (parms); + } + } +} + +/* Output definitions for the places where parms live during the function, + when different from where they were passed, when the parms were passed + in memory. + + It is not useful to do this for parms passed in registers + that live during the function in different registers, because it is + impossible to look in the passed register for the passed value, + so we use the within-the-function register to begin with. + + PARMS is a chain of PARM_DECL nodes. */ + +void +dbxout_reg_parms (parms) + tree parms; +{ + for (; parms; parms = TREE_CHAIN (parms)) + if (DECL_NAME (parms)) + { + dbxout_prepare_symbol (parms); + + /* Report parms that live in registers during the function + but were passed in memory. */ + if (GET_CODE (DECL_RTL (parms)) == REG + && REGNO (DECL_RTL (parms)) >= 0 + && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER + && PARM_PASSED_IN_MEMORY (parms)) + { + current_sym_code = N_RSYM; + current_sym_value = DBX_REGISTER_NUMBER (REGNO (DECL_RTL (parms))); + current_sym_addr = 0; + + FORCE_TEXT; + if (DECL_NAME (parms)) + { + current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms)); + fprintf (asmfile, "%s \"%s:r", ASM_STABS_OP, + IDENTIFIER_POINTER (DECL_NAME (parms))); + } + else + { + current_sym_nchars = 8; + fprintf (asmfile, "%s \"(anon):r", ASM_STABS_OP); + } + dbxout_type (TREE_TYPE (parms), 0, 0); + dbxout_finish_symbol (parms); + } + /* Report parms that live in memory but not where they were passed. */ + else if (GET_CODE (DECL_RTL (parms)) == MEM + && GET_CODE (XEXP (DECL_RTL (parms), 0)) == PLUS + && GET_CODE (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == CONST_INT + && PARM_PASSED_IN_MEMORY (parms) + && ! rtx_equal_p (DECL_RTL (parms), DECL_INCOMING_RTL (parms))) + { +#if 0 /* ??? It is not clear yet what should replace this. */ + int offset = DECL_OFFSET (parms) / BITS_PER_UNIT; + /* A parm declared char is really passed as an int, + so it occupies the least significant bytes. + On a big-endian machine those are not the low-numbered ones. */ +#if BYTES_BIG_ENDIAN + if (offset != -1 && TREE_TYPE (parms) != DECL_ARG_TYPE (parms)) + offset += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms))) + - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms)))); +#endif + if (INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)) != offset) {...} +#endif + current_sym_code = N_LSYM; + current_sym_value = DEBUGGER_AUTO_OFFSET (XEXP (DECL_RTL (parms), 0)); + current_sym_addr = 0; + FORCE_TEXT; + if (DECL_NAME (parms)) + { + current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms)); + fprintf (asmfile, "%s \"%s:", ASM_STABS_OP, + IDENTIFIER_POINTER (DECL_NAME (parms))); + } + else + { + current_sym_nchars = 8; + fprintf (asmfile, "%s \"(anon):", ASM_STABS_OP); + } + dbxout_type (TREE_TYPE (parms), 0, 0); + dbxout_finish_symbol (parms); + } +#if 0 + else if (GET_CODE (DECL_RTL (parms)) == MEM + && GET_CODE (XEXP (DECL_RTL (parms), 0)) == REG) + { + /* Parm was passed via invisible reference. + That is, its address was passed in a register. + Output it as if it lived in that register. + The debugger will know from the type + that it was actually passed by invisible reference. */ + + current_sym_code = N_RSYM; + + /* DECL_RTL looks like (MEM (REG...). Get the register number. */ + current_sym_value = REGNO (XEXP (DECL_RTL (parms), 0)); + current_sym_addr = 0; + + FORCE_TEXT; + if (DECL_NAME (parms)) + { + current_sym_nchars = 2 + strlen (IDENTIFIER_POINTER (DECL_NAME (parms))); + + fprintf (asmfile, "%s \"%s:r", ASM_STABS_OP, + IDENTIFIER_POINTER (DECL_NAME (parms))); + } + else + { + current_sym_nchars = 8; + fprintf (asmfile, "%s \"(anon):r", ASM_STABS_OP); + } + + dbxout_type (TREE_TYPE (parms), 0, 0); + dbxout_finish_symbol (parms); + } +#endif + } +} + +/* Given a chain of ..._TYPE nodes (as come in a parameter list), + output definitions of those names, in raw form */ + +void +dbxout_args (args) + tree args; +{ + while (args) + { + putc (',', asmfile); + dbxout_type (TREE_VALUE (args), 0, 0); + CHARS (1); + args = TREE_CHAIN (args); + } +} + +/* Given a chain of ..._TYPE nodes, + find those which have typedef names and output those names. + This is to ensure those types get output. */ + +void +dbxout_types (types) + register tree types; +{ + while (types) + { + if (TYPE_NAME (types) + && TREE_CODE (TYPE_NAME (types)) == TYPE_DECL + && ! TREE_ASM_WRITTEN (TYPE_NAME (types))) + dbxout_symbol (TYPE_NAME (types), 1); + types = TREE_CHAIN (types); + } +} + +/* Output everything about a symbol block (a BLOCK node + that represents a scope level), + including recursive output of contained blocks. + + BLOCK is the BLOCK node. + DEPTH is its depth within containing symbol blocks. + ARGS is usually zero; but for the outermost block of the + body of a function, it is a chain of PARM_DECLs for the function parameters. + We output definitions of all the register parms + as if they were local variables of that block. + + If -g1 was used, we count blocks just the same, but output nothing + except for the outermost block. + + Actually, BLOCK may be several blocks chained together. + We handle them all in sequence. */ + +static void +dbxout_block (block, depth, args) + register tree block; + int depth; + tree args; +{ + int blocknum; + + while (block) + { + /* Ignore blocks never expanded or otherwise marked as real. */ + if (TREE_USED (block)) + { +#ifndef DBX_LBRAC_FIRST + /* In dbx format, the syms of a block come before the N_LBRAC. */ + if (debug_info_level != DINFO_LEVEL_TERSE || depth == 0) + dbxout_syms (BLOCK_VARS (block)); + if (args) + dbxout_reg_parms (args); +#endif + + /* Now output an N_LBRAC symbol to represent the beginning of + the block. Use the block's tree-walk order to generate + the assembler symbols LBBn and LBEn + that final will define around the code in this block. */ + if (depth > 0 && debug_info_level != DINFO_LEVEL_TERSE) + { + char buf[20]; + blocknum = next_block_number++; + ASM_GENERATE_INTERNAL_LABEL (buf, "LBB", blocknum); + + if (BLOCK_HANDLER_BLOCK (block)) + { + /* A catch block. Must precede N_LBRAC. */ + tree decl = BLOCK_VARS (block); + while (decl) + { +#ifdef DBX_OUTPUT_CATCH + DBX_OUTPUT_CATCH (asmfile, decl, buf); +#else + fprintf (asmfile, "%s \"%s:C1\",%d,0,0,", ASM_STABS_OP, + IDENTIFIER_POINTER (DECL_NAME (decl)), N_CATCH); + assemble_name (asmfile, buf); + fprintf (asmfile, "\n"); +#endif + decl = TREE_CHAIN (decl); + } + } + +#ifdef DBX_OUTPUT_LBRAC + DBX_OUTPUT_LBRAC (asmfile, buf); +#else + fprintf (asmfile, "%s %d,0,0,", ASM_STABN_OP, N_LBRAC); + assemble_name (asmfile, buf); +#if DBX_BLOCKS_FUNCTION_RELATIVE + fputc ('-', asmfile); + assemble_name (asmfile, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); +#endif + fprintf (asmfile, "\n"); +#endif + } + else if (depth > 0) + /* Count blocks the same way regardless of debug_info_level. */ + next_block_number++; + +#ifdef DBX_LBRAC_FIRST + /* On some weird machines, the syms of a block + come after the N_LBRAC. */ + if (debug_info_level != DINFO_LEVEL_TERSE || depth == 0) + dbxout_syms (BLOCK_VARS (block)); + if (args) + dbxout_reg_parms (args); +#endif + + /* Output the subblocks. */ + dbxout_block (BLOCK_SUBBLOCKS (block), depth + 1, NULL_TREE); + + /* Refer to the marker for the end of the block. */ + if (depth > 0 && debug_info_level != DINFO_LEVEL_TERSE) + { + char buf[20]; + ASM_GENERATE_INTERNAL_LABEL (buf, "LBE", blocknum); +#ifdef DBX_OUTPUT_RBRAC + DBX_OUTPUT_RBRAC (asmfile, buf); +#else + fprintf (asmfile, "%s %d,0,0,", ASM_STABN_OP, N_RBRAC); + assemble_name (asmfile, buf); +#if DBX_BLOCKS_FUNCTION_RELATIVE + fputc ('-', asmfile); + assemble_name (asmfile, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); +#endif + fprintf (asmfile, "\n"); +#endif + } + } + block = BLOCK_CHAIN (block); + } +} + +/* Output the information about a function and its arguments and result. + Usually this follows the function's code, + but on some systems, it comes before. */ + +static void +dbxout_really_begin_function (decl) + tree decl; +{ + dbxout_symbol (decl, 0); + dbxout_parms (DECL_ARGUMENTS (decl)); + if (DECL_NAME (DECL_RESULT (decl)) != 0) + dbxout_symbol (DECL_RESULT (decl), 1); +} + +/* Called at beginning of output of function definition. */ + +void +dbxout_begin_function (decl) + tree decl; +{ +#ifdef DBX_FUNCTION_FIRST + dbxout_really_begin_function (decl); +#endif +} + +/* Output dbx data for a function definition. + This includes a definition of the function name itself (a symbol), + definitions of the parameters (locating them in the parameter list) + and then output the block that makes up the function's body + (including all the auto variables of the function). */ + +void +dbxout_function (decl) + tree decl; +{ +#ifndef DBX_FUNCTION_FIRST + dbxout_really_begin_function (decl); +#endif + dbxout_block (DECL_INITIAL (decl), 0, DECL_ARGUMENTS (decl)); +#ifdef DBX_OUTPUT_FUNCTION_END + DBX_OUTPUT_FUNCTION_END (asmfile, decl); +#endif +} +#endif /* DBX_DEBUGGING_INFO */ diff --git a/gnu/usr.bin/cc/common/defaults.h b/gnu/usr.bin/cc/lib/defaults.h similarity index 100% rename from gnu/usr.bin/cc/common/defaults.h rename to gnu/usr.bin/cc/lib/defaults.h diff --git a/gnu/usr.bin/cc/lib/dwarfout.c b/gnu/usr.bin/cc/lib/dwarfout.c new file mode 100644 index 0000000000..cd2dca50a7 --- /dev/null +++ b/gnu/usr.bin/cc/lib/dwarfout.c @@ -0,0 +1,5647 @@ +/* This file contains code written by Ron Guilmette (rfg@ncd.com) for + Network Computing Devices, August, September, October, November 1990. + + Output Dwarf format symbol table information from the GNU C compiler. + Copyright (C) 1992 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "config.h" + +#ifdef DWARF_DEBUGGING_INFO +#include +#include "dwarf.h" +#include "tree.h" +#include "flags.h" +#include "rtl.h" +#include "hard-reg-set.h" +#include "insn-config.h" +#include "reload.h" +#include "output.h" +#include "defaults.h" + +#ifndef DWARF_VERSION +#define DWARF_VERSION 1 +#endif + +/* #define NDEBUG 1 */ +#include "assert.h" + +#if defined(DWARF_TIMESTAMPS) +#if defined(POSIX) +#include +#else /* !defined(POSIX) */ +#include +#if defined(__STDC__) +extern time_t time (time_t *); +#else /* !defined(__STDC__) */ +extern time_t time (); +#endif /* !defined(__STDC__) */ +#endif /* !defined(POSIX) */ +#endif /* defined(DWARF_TIMESTAMPS) */ + +extern char *getpwd (); + +extern char *index (); +extern char *rindex (); + +/* IMPORTANT NOTE: Please see the file README.DWARF for important details + regarding the GNU implementation of Dwarf. */ + +/* NOTE: In the comments in this file, many references are made to + so called "Debugging Information Entries". For the sake of brevity, + this term is abbreviated to `DIE' throughout the remainder of this + file. */ + +/* Note that the implementation of C++ support herein is (as yet) unfinished. + If you want to try to complete it, more power to you. */ + +#if defined(__GNUC__) && (NDEBUG == 1) +#define inline static inline +#else +#define inline static +#endif + +/* How to start an assembler comment. */ +#ifndef ASM_COMMENT_START +#define ASM_COMMENT_START ";#" +#endif + +/* How to print out a register name. */ +#ifndef PRINT_REG +#define PRINT_REG(RTX, CODE, FILE) \ + fprintf ((FILE), "%s", reg_names[REGNO (RTX)]) +#endif + +/* Define a macro which returns non-zero for any tagged type which is + used (directly or indirectly) in the specification of either some + function's return type or some formal parameter of some function. + We use this macro when we are operating in "terse" mode to help us + know what tagged types have to be represented in Dwarf (even in + terse mode) and which ones don't. + + A flag bit with this meaning really should be a part of the normal + GCC ..._TYPE nodes, but at the moment, there is no such bit defined + for these nodes. For now, we have to just fake it. It it safe for + us to simply return zero for all complete tagged types (which will + get forced out anyway if they were used in the specification of some + formal or return type) and non-zero for all incomplete tagged types. +*/ + +#define TYPE_USED_FOR_FUNCTION(tagged_type) (TYPE_SIZE (tagged_type) == 0) + +extern int flag_traditional; +extern char *version_string; +extern char *language_string; + +/* Maximum size (in bytes) of an artificially generated label. */ + +#define MAX_ARTIFICIAL_LABEL_BYTES 30 + +/* Make sure we know the sizes of the various types dwarf can describe. + These are only defaults. If the sizes are different for your target, + you should override these values by defining the appropriate symbols + in your tm.h file. */ + +#ifndef CHAR_TYPE_SIZE +#define CHAR_TYPE_SIZE BITS_PER_UNIT +#endif + +#ifndef SHORT_TYPE_SIZE +#define SHORT_TYPE_SIZE (BITS_PER_UNIT * 2) +#endif + +#ifndef INT_TYPE_SIZE +#define INT_TYPE_SIZE BITS_PER_WORD +#endif + +#ifndef LONG_TYPE_SIZE +#define LONG_TYPE_SIZE BITS_PER_WORD +#endif + +#ifndef LONG_LONG_TYPE_SIZE +#define LONG_LONG_TYPE_SIZE (BITS_PER_WORD * 2) +#endif + +#ifndef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE INT_TYPE_SIZE +#endif + +#ifndef WCHAR_UNSIGNED +#define WCHAR_UNSIGNED 0 +#endif + +#ifndef FLOAT_TYPE_SIZE +#define FLOAT_TYPE_SIZE BITS_PER_WORD +#endif + +#ifndef DOUBLE_TYPE_SIZE +#define DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2) +#endif + +#ifndef LONG_DOUBLE_TYPE_SIZE +#define LONG_DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2) +#endif + +/* Structure to keep track of source filenames. */ + +struct filename_entry { + unsigned number; + char * name; +}; + +typedef struct filename_entry filename_entry; + +/* Pointer to an array of elements, each one having the structure above. */ + +static filename_entry *filename_table; + +/* Total number of entries in the table (i.e. array) pointed to by + `filename_table'. This is the *total* and includes both used and + unused slots. */ + +static unsigned ft_entries_allocated; + +/* Number of entries in the filename_table which are actually in use. */ + +static unsigned ft_entries; + +/* Size (in elements) of increments by which we may expand the filename + table. Actually, a single hunk of space of this size should be enough + for most typical programs. */ + +#define FT_ENTRIES_INCREMENT 64 + +/* Local pointer to the name of the main input file. Initialized in + dwarfout_init. */ + +static char *primary_filename; + +/* Pointer to the most recent filename for which we produced some line info. */ + +static char *last_filename; + +/* For Dwarf output, we must assign lexical-blocks id numbers + in the order in which their beginnings are encountered. + We output Dwarf debugging info that refers to the beginnings + and ends of the ranges of code for each lexical block with + assembler labels ..Bn and ..Bn.e, where n is the block number. + The labels themselves are generated in final.c, which assigns + numbers to the blocks in the same way. */ + +static unsigned next_block_number = 2; + +/* Counter to generate unique names for DIEs. */ + +static unsigned next_unused_dienum = 1; + +/* Number of the DIE which is currently being generated. */ + +static unsigned current_dienum; + +/* Number to use for the special "pubname" label on the next DIE which + represents a function or data object defined in this compilation + unit which has "extern" linkage. */ + +static next_pubname_number = 0; + +#define NEXT_DIE_NUM pending_sibling_stack[pending_siblings-1] + +/* Pointer to a dynamically allocated list of pre-reserved and still + pending sibling DIE numbers. Note that this list will grow as needed. */ + +static unsigned *pending_sibling_stack; + +/* Counter to keep track of the number of pre-reserved and still pending + sibling DIE numbers. */ + +static unsigned pending_siblings; + +/* The currently allocated size of the above list (expressed in number of + list elements). */ + +static unsigned pending_siblings_allocated; + +/* Size (in elements) of increments by which we may expand the pending + sibling stack. Actually, a single hunk of space of this size should + be enough for most typical programs. */ + +#define PENDING_SIBLINGS_INCREMENT 64 + +/* Non-zero if we are performing our file-scope finalization pass and if + we should force out Dwarf descriptions of any and all file-scope + tagged types which are still incomplete types. */ + +static int finalizing = 0; + +/* A pointer to the base of a list of pending types which we haven't + generated DIEs for yet, but which we will have to come back to + later on. */ + +static tree *pending_types_list; + +/* Number of elements currently allocated for the pending_types_list. */ + +static unsigned pending_types_allocated; + +/* Number of elements of pending_types_list currently in use. */ + +static unsigned pending_types; + +/* Size (in elements) of increments by which we may expand the pending + types list. Actually, a single hunk of space of this size should + be enough for most typical programs. */ + +#define PENDING_TYPES_INCREMENT 64 + +/* Pointer to an artificial RECORD_TYPE which we create in dwarfout_init. + This is used in a hack to help us get the DIEs describing types of + formal parameters to come *after* all of the DIEs describing the formal + parameters themselves. That's necessary in order to be compatible + with what the brain-damaged svr4 SDB debugger requires. */ + +static tree fake_containing_scope; + +/* The number of the current function definition that we are generating + debugging information for. These numbers range from 1 up to the maximum + number of function definitions contained within the current compilation + unit. These numbers are used to create unique labels for various things + contained within various function definitions. */ + +static unsigned current_funcdef_number = 1; + +/* A pointer to the ..._DECL node which we have most recently been working + on. We keep this around just in case something about it looks screwy + and we want to tell the user what the source coordinates for the actual + declaration are. */ + +static tree dwarf_last_decl; + +/* Forward declarations for functions defined in this file. */ + +static void output_type (); +static void type_attribute (); +static void output_decls_for_scope (); +static void output_decl (); +static unsigned lookup_filename (); + +/* Definitions of defaults for assembler-dependent names of various + pseudo-ops and section names. + + Theses may be overridden in your tm.h file (if necessary) for your + particular assembler. The default values provided here correspond to + what is expected by "standard" AT&T System V.4 assemblers. */ + +#ifndef FILE_ASM_OP +#define FILE_ASM_OP ".file" +#endif +#ifndef VERSION_ASM_OP +#define VERSION_ASM_OP ".version" +#endif +#ifndef UNALIGNED_SHORT_ASM_OP +#define UNALIGNED_SHORT_ASM_OP ".2byte" +#endif +#ifndef UNALIGNED_INT_ASM_OP +#define UNALIGNED_INT_ASM_OP ".4byte" +#endif +#ifndef ASM_BYTE_OP +#define ASM_BYTE_OP ".byte" +#endif +#ifndef SET_ASM_OP +#define SET_ASM_OP ".set" +#endif + +/* Pseudo-ops for pushing the current section onto the section stack (and + simultaneously changing to a new section) and for poping back to the + section we were in immediately before this one. Note that most svr4 + assemblers only maintain a one level stack... you can push all the + sections you want, but you can only pop out one level. (The sparc + svr4 assembler is an exception to this general rule.) That's + OK because we only use at most one level of the section stack herein. */ + +#ifndef PUSHSECTION_ASM_OP +#define PUSHSECTION_ASM_OP ".section" +#endif +#ifndef POPSECTION_ASM_OP +#define POPSECTION_ASM_OP ".previous" +#endif + +/* The default format used by the ASM_OUTPUT_PUSH_SECTION macro (see below) + to print the PUSHSECTION_ASM_OP and the section name. The default here + works for almost all svr4 assemblers, except for the sparc, where the + section name must be enclosed in double quotes. (See sparcv4.h.) */ + +#ifndef PUSHSECTION_FORMAT +#define PUSHSECTION_FORMAT "%s\t%s\n" +#endif + +#ifndef DEBUG_SECTION +#define DEBUG_SECTION ".debug" +#endif +#ifndef LINE_SECTION +#define LINE_SECTION ".line" +#endif +#ifndef SFNAMES_SECTION +#define SFNAMES_SECTION ".debug_sfnames" +#endif +#ifndef SRCINFO_SECTION +#define SRCINFO_SECTION ".debug_srcinfo" +#endif +#ifndef MACINFO_SECTION +#define MACINFO_SECTION ".debug_macinfo" +#endif +#ifndef PUBNAMES_SECTION +#define PUBNAMES_SECTION ".debug_pubnames" +#endif +#ifndef ARANGES_SECTION +#define ARANGES_SECTION ".debug_aranges" +#endif +#ifndef TEXT_SECTION +#define TEXT_SECTION ".text" +#endif +#ifndef DATA_SECTION +#define DATA_SECTION ".data" +#endif +#ifndef DATA1_SECTION +#define DATA1_SECTION ".data1" +#endif +#ifndef RODATA_SECTION +#define RODATA_SECTION ".rodata" +#endif +#ifndef RODATA1_SECTION +#define RODATA1_SECTION ".rodata1" +#endif +#ifndef BSS_SECTION +#define BSS_SECTION ".bss" +#endif + +/* Definitions of defaults for formats and names of various special + (artificial) labels which may be generated within this file (when + the -g options is used and DWARF_DEBUGGING_INFO is in effect. + + If necessary, these may be overridden from within your tm.h file, + but typically, you should never need to override these. + + These labels have been hacked (temporarily) so that they all begin with + a `.L' sequence so as to appease the stock sparc/svr4 assembler and the + stock m88k/svr4 assembler, both of which need to see .L at the start of + a label in order to prevent that label from going into the linker symbol + table). When I get time, I'll have to fix this the right way so that we + will use ASM_GENERATE_INTERNAL_LABEL and ASM_OUTPUT_INTERNAL_LABEL herein, + but that will require a rather massive set of changes. For the moment, + the following definitions out to produce the right results for all svr4 + and svr3 assemblers. -- rfg +*/ + +#ifndef TEXT_BEGIN_LABEL +#define TEXT_BEGIN_LABEL ".L_text_b" +#endif +#ifndef TEXT_END_LABEL +#define TEXT_END_LABEL ".L_text_e" +#endif + +#ifndef DATA_BEGIN_LABEL +#define DATA_BEGIN_LABEL ".L_data_b" +#endif +#ifndef DATA_END_LABEL +#define DATA_END_LABEL ".L_data_e" +#endif + +#ifndef DATA1_BEGIN_LABEL +#define DATA1_BEGIN_LABEL ".L_data1_b" +#endif +#ifndef DATA1_END_LABEL +#define DATA1_END_LABEL ".L_data1_e" +#endif + +#ifndef RODATA_BEGIN_LABEL +#define RODATA_BEGIN_LABEL ".L_rodata_b" +#endif +#ifndef RODATA_END_LABEL +#define RODATA_END_LABEL ".L_rodata_e" +#endif + +#ifndef RODATA1_BEGIN_LABEL +#define RODATA1_BEGIN_LABEL ".L_rodata1_b" +#endif +#ifndef RODATA1_END_LABEL +#define RODATA1_END_LABEL ".L_rodata1_e" +#endif + +#ifndef BSS_BEGIN_LABEL +#define BSS_BEGIN_LABEL ".L_bss_b" +#endif +#ifndef BSS_END_LABEL +#define BSS_END_LABEL ".L_bss_e" +#endif + +#ifndef LINE_BEGIN_LABEL +#define LINE_BEGIN_LABEL ".L_line_b" +#endif +#ifndef LINE_LAST_ENTRY_LABEL +#define LINE_LAST_ENTRY_LABEL ".L_line_last" +#endif +#ifndef LINE_END_LABEL +#define LINE_END_LABEL ".L_line_e" +#endif + +#ifndef DEBUG_BEGIN_LABEL +#define DEBUG_BEGIN_LABEL ".L_debug_b" +#endif +#ifndef SFNAMES_BEGIN_LABEL +#define SFNAMES_BEGIN_LABEL ".L_sfnames_b" +#endif +#ifndef SRCINFO_BEGIN_LABEL +#define SRCINFO_BEGIN_LABEL ".L_srcinfo_b" +#endif +#ifndef MACINFO_BEGIN_LABEL +#define MACINFO_BEGIN_LABEL ".L_macinfo_b" +#endif + +#ifndef DIE_BEGIN_LABEL_FMT +#define DIE_BEGIN_LABEL_FMT ".L_D%u" +#endif +#ifndef DIE_END_LABEL_FMT +#define DIE_END_LABEL_FMT ".L_D%u_e" +#endif +#ifndef PUB_DIE_LABEL_FMT +#define PUB_DIE_LABEL_FMT ".L_P%u" +#endif +#ifndef INSN_LABEL_FMT +#define INSN_LABEL_FMT ".L_I%u_%u" +#endif +#ifndef BLOCK_BEGIN_LABEL_FMT +#define BLOCK_BEGIN_LABEL_FMT ".L_B%u" +#endif +#ifndef BLOCK_END_LABEL_FMT +#define BLOCK_END_LABEL_FMT ".L_B%u_e" +#endif +#ifndef SS_BEGIN_LABEL_FMT +#define SS_BEGIN_LABEL_FMT ".L_s%u" +#endif +#ifndef SS_END_LABEL_FMT +#define SS_END_LABEL_FMT ".L_s%u_e" +#endif +#ifndef EE_BEGIN_LABEL_FMT +#define EE_BEGIN_LABEL_FMT ".L_e%u" +#endif +#ifndef EE_END_LABEL_FMT +#define EE_END_LABEL_FMT ".L_e%u_e" +#endif +#ifndef MT_BEGIN_LABEL_FMT +#define MT_BEGIN_LABEL_FMT ".L_t%u" +#endif +#ifndef MT_END_LABEL_FMT +#define MT_END_LABEL_FMT ".L_t%u_e" +#endif +#ifndef LOC_BEGIN_LABEL_FMT +#define LOC_BEGIN_LABEL_FMT ".L_l%u" +#endif +#ifndef LOC_END_LABEL_FMT +#define LOC_END_LABEL_FMT ".L_l%u_e" +#endif +#ifndef BOUND_BEGIN_LABEL_FMT +#define BOUND_BEGIN_LABEL_FMT ".L_b%u_%u_%c" +#endif +#ifndef BOUND_END_LABEL_FMT +#define BOUND_END_LABEL_FMT ".L_b%u_%u_%c_e" +#endif +#ifndef DERIV_BEGIN_LABEL_FMT +#define DERIV_BEGIN_LABEL_FMT ".L_d%u" +#endif +#ifndef DERIV_END_LABEL_FMT +#define DERIV_END_LABEL_FMT ".L_d%u_e" +#endif +#ifndef SL_BEGIN_LABEL_FMT +#define SL_BEGIN_LABEL_FMT ".L_sl%u" +#endif +#ifndef SL_END_LABEL_FMT +#define SL_END_LABEL_FMT ".L_sl%u_e" +#endif +#ifndef BODY_BEGIN_LABEL_FMT +#define BODY_BEGIN_LABEL_FMT ".L_b%u" +#endif +#ifndef BODY_END_LABEL_FMT +#define BODY_END_LABEL_FMT ".L_b%u_e" +#endif +#ifndef FUNC_END_LABEL_FMT +#define FUNC_END_LABEL_FMT ".L_f%u_e" +#endif +#ifndef TYPE_NAME_FMT +#define TYPE_NAME_FMT ".L_T%u" +#endif +#ifndef DECL_NAME_FMT +#define DECL_NAME_FMT ".L_E%u" +#endif +#ifndef LINE_CODE_LABEL_FMT +#define LINE_CODE_LABEL_FMT ".L_LC%u" +#endif +#ifndef SFNAMES_ENTRY_LABEL_FMT +#define SFNAMES_ENTRY_LABEL_FMT ".L_F%u" +#endif +#ifndef LINE_ENTRY_LABEL_FMT +#define LINE_ENTRY_LABEL_FMT ".L_LE%u" +#endif + +/* Definitions of defaults for various types of primitive assembly language + output operations. + + If necessary, these may be overridden from within your tm.h file, + but typically, you shouldn't need to override these. One known + exception is ASM_OUTPUT_DEF which has to be different for stock + sparc/svr4 assemblers. +*/ + +#ifndef ASM_OUTPUT_PUSH_SECTION +#define ASM_OUTPUT_PUSH_SECTION(FILE, SECTION) \ + fprintf ((FILE), PUSHSECTION_FORMAT, PUSHSECTION_ASM_OP, SECTION) +#endif + +#ifndef ASM_OUTPUT_POP_SECTION +#define ASM_OUTPUT_POP_SECTION(FILE) \ + fprintf ((FILE), "\t%s\n", POPSECTION_ASM_OP) +#endif + +#ifndef ASM_OUTPUT_SOURCE_FILENAME +#define ASM_OUTPUT_SOURCE_FILENAME(FILE,NAME) \ + fprintf ((FILE), "\t%s\t\"%s\"\n", FILE_ASM_OP, NAME) +#endif + +#ifndef ASM_OUTPUT_DEF +#define ASM_OUTPUT_DEF(FILE,LABEL1,LABEL2) \ + do { fprintf ((FILE), "\t%s\t", SET_ASM_OP); \ + assemble_name (FILE, LABEL1); \ + fprintf (FILE, ","); \ + assemble_name (FILE, LABEL2); \ + fprintf (FILE, "\n"); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_DELTA2 +#define ASM_OUTPUT_DWARF_DELTA2(FILE,LABEL1,LABEL2) \ + do { fprintf ((FILE), "\t%s\t", UNALIGNED_SHORT_ASM_OP); \ + assemble_name (FILE, LABEL1); \ + fprintf (FILE, "-"); \ + assemble_name (FILE, LABEL2); \ + fprintf (FILE, "\n"); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_DELTA4 +#define ASM_OUTPUT_DWARF_DELTA4(FILE,LABEL1,LABEL2) \ + do { fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \ + assemble_name (FILE, LABEL1); \ + fprintf (FILE, "-"); \ + assemble_name (FILE, LABEL2); \ + fprintf (FILE, "\n"); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_TAG +#define ASM_OUTPUT_DWARF_TAG(FILE,TAG) \ + do { \ + fprintf ((FILE), "\t%s\t0x%x", \ + UNALIGNED_SHORT_ASM_OP, (unsigned) TAG); \ + if (flag_verbose_asm) \ + fprintf ((FILE), "\t%s %s", \ + ASM_COMMENT_START, dwarf_tag_name (TAG)); \ + fputc ('\n', (FILE)); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_ATTRIBUTE +#define ASM_OUTPUT_DWARF_ATTRIBUTE(FILE,ATTR) \ + do { \ + fprintf ((FILE), "\t%s\t0x%x", \ + UNALIGNED_SHORT_ASM_OP, (unsigned) ATTR); \ + if (flag_verbose_asm) \ + fprintf ((FILE), "\t%s %s", \ + ASM_COMMENT_START, dwarf_attr_name (ATTR)); \ + fputc ('\n', (FILE)); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_STACK_OP +#define ASM_OUTPUT_DWARF_STACK_OP(FILE,OP) \ + do { \ + fprintf ((FILE), "\t%s\t0x%x", ASM_BYTE_OP, (unsigned) OP); \ + if (flag_verbose_asm) \ + fprintf ((FILE), "\t%s %s", \ + ASM_COMMENT_START, dwarf_stack_op_name (OP)); \ + fputc ('\n', (FILE)); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_FUND_TYPE +#define ASM_OUTPUT_DWARF_FUND_TYPE(FILE,FT) \ + do { \ + fprintf ((FILE), "\t%s\t0x%x", \ + UNALIGNED_SHORT_ASM_OP, (unsigned) FT); \ + if (flag_verbose_asm) \ + fprintf ((FILE), "\t%s %s", \ + ASM_COMMENT_START, dwarf_fund_type_name (FT)); \ + fputc ('\n', (FILE)); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_FMT_BYTE +#define ASM_OUTPUT_DWARF_FMT_BYTE(FILE,FMT) \ + do { \ + fprintf ((FILE), "\t%s\t0x%x", ASM_BYTE_OP, (unsigned) FMT); \ + if (flag_verbose_asm) \ + fprintf ((FILE), "\t%s %s", \ + ASM_COMMENT_START, dwarf_fmt_byte_name (FMT)); \ + fputc ('\n', (FILE)); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_TYPE_MODIFIER +#define ASM_OUTPUT_DWARF_TYPE_MODIFIER(FILE,MOD) \ + do { \ + fprintf ((FILE), "\t%s\t0x%x", ASM_BYTE_OP, (unsigned) MOD); \ + if (flag_verbose_asm) \ + fprintf ((FILE), "\t%s %s", \ + ASM_COMMENT_START, dwarf_typemod_name (MOD)); \ + fputc ('\n', (FILE)); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_ADDR +#define ASM_OUTPUT_DWARF_ADDR(FILE,LABEL) \ + do { fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \ + assemble_name (FILE, LABEL); \ + fprintf (FILE, "\n"); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_ADDR_CONST +#define ASM_OUTPUT_DWARF_ADDR_CONST(FILE,RTX) \ + do { \ + fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \ + output_addr_const ((FILE), (RTX)); \ + fputc ('\n', (FILE)); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_REF +#define ASM_OUTPUT_DWARF_REF(FILE,LABEL) \ + do { fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \ + assemble_name (FILE, LABEL); \ + fprintf (FILE, "\n"); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_DATA1 +#define ASM_OUTPUT_DWARF_DATA1(FILE,VALUE) \ + fprintf ((FILE), "\t%s\t0x%x\n", ASM_BYTE_OP, VALUE) +#endif + +#ifndef ASM_OUTPUT_DWARF_DATA2 +#define ASM_OUTPUT_DWARF_DATA2(FILE,VALUE) \ + fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_SHORT_ASM_OP, (unsigned) VALUE) +#endif + +#ifndef ASM_OUTPUT_DWARF_DATA4 +#define ASM_OUTPUT_DWARF_DATA4(FILE,VALUE) \ + fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_INT_ASM_OP, (unsigned) VALUE) +#endif + +#ifndef ASM_OUTPUT_DWARF_DATA8 +#define ASM_OUTPUT_DWARF_DATA8(FILE,HIGH_VALUE,LOW_VALUE) \ + do { \ + if (WORDS_BIG_ENDIAN) \ + { \ + fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_INT_ASM_OP, HIGH_VALUE); \ + fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_INT_ASM_OP, LOW_VALUE);\ + } \ + else \ + { \ + fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_INT_ASM_OP, LOW_VALUE);\ + fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_INT_ASM_OP, HIGH_VALUE); \ + } \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_STRING +#define ASM_OUTPUT_DWARF_STRING(FILE,P) \ + ASM_OUTPUT_ASCII ((FILE), P, strlen (P)+1) +#endif + +/************************ general utility functions **************************/ + +inline char * +xstrdup (s) + register char *s; +{ + register char *p = (char *) xmalloc (strlen (s) + 1); + + strcpy (p, s); + return p; +} + +inline int +is_pseudo_reg (rtl) + register rtx rtl; +{ + return (((GET_CODE (rtl) == REG) && (REGNO (rtl) >= FIRST_PSEUDO_REGISTER)) + || ((GET_CODE (rtl) == SUBREG) + && (REGNO (XEXP (rtl, 0)) >= FIRST_PSEUDO_REGISTER))); +} + +inline tree +type_main_variant (type) + register tree type; +{ + type = TYPE_MAIN_VARIANT (type); + + /* There really should be only one main variant among any group of variants + of a given type (and all of the MAIN_VARIANT values for all members of + the group should point to that one type) but sometimes the C front-end + messes this up for array types, so we work around that bug here. */ + + if (TREE_CODE (type) == ARRAY_TYPE) + { + while (type != TYPE_MAIN_VARIANT (type)) + type = TYPE_MAIN_VARIANT (type); + } + + return type; +} + +/* Return non-zero if the given type node represents a tagged type. */ + +inline int +is_tagged_type (type) + register tree type; +{ + register enum tree_code code = TREE_CODE (type); + + return (code == RECORD_TYPE || code == UNION_TYPE + || code == QUAL_UNION_TYPE || code == ENUMERAL_TYPE); +} + +static char * +dwarf_tag_name (tag) + register unsigned tag; +{ + switch (tag) + { + case TAG_padding: return "TAG_padding"; + case TAG_array_type: return "TAG_array_type"; + case TAG_class_type: return "TAG_class_type"; + case TAG_entry_point: return "TAG_entry_point"; + case TAG_enumeration_type: return "TAG_enumeration_type"; + case TAG_formal_parameter: return "TAG_formal_parameter"; + case TAG_global_subroutine: return "TAG_global_subroutine"; + case TAG_global_variable: return "TAG_global_variable"; + case TAG_label: return "TAG_label"; + case TAG_lexical_block: return "TAG_lexical_block"; + case TAG_local_variable: return "TAG_local_variable"; + case TAG_member: return "TAG_member"; + case TAG_pointer_type: return "TAG_pointer_type"; + case TAG_reference_type: return "TAG_reference_type"; + case TAG_compile_unit: return "TAG_compile_unit"; + case TAG_string_type: return "TAG_string_type"; + case TAG_structure_type: return "TAG_structure_type"; + case TAG_subroutine: return "TAG_subroutine"; + case TAG_subroutine_type: return "TAG_subroutine_type"; + case TAG_typedef: return "TAG_typedef"; + case TAG_union_type: return "TAG_union_type"; + case TAG_unspecified_parameters: return "TAG_unspecified_parameters"; + case TAG_variant: return "TAG_variant"; + case TAG_common_block: return "TAG_common_block"; + case TAG_common_inclusion: return "TAG_common_inclusion"; + case TAG_inheritance: return "TAG_inheritance"; + case TAG_inlined_subroutine: return "TAG_inlined_subroutine"; + case TAG_module: return "TAG_module"; + case TAG_ptr_to_member_type: return "TAG_ptr_to_member_type"; + case TAG_set_type: return "TAG_set_type"; + case TAG_subrange_type: return "TAG_subrange_type"; + case TAG_with_stmt: return "TAG_with_stmt"; + + /* GNU extensions. */ + + case TAG_format_label: return "TAG_format_label"; + case TAG_namelist: return "TAG_namelist"; + case TAG_function_template: return "TAG_function_template"; + case TAG_class_template: return "TAG_class_template"; + + default: return "TAG_"; + } +} + +static char * +dwarf_attr_name (attr) + register unsigned attr; +{ + switch (attr) + { + case AT_sibling: return "AT_sibling"; + case AT_location: return "AT_location"; + case AT_name: return "AT_name"; + case AT_fund_type: return "AT_fund_type"; + case AT_mod_fund_type: return "AT_mod_fund_type"; + case AT_user_def_type: return "AT_user_def_type"; + case AT_mod_u_d_type: return "AT_mod_u_d_type"; + case AT_ordering: return "AT_ordering"; + case AT_subscr_data: return "AT_subscr_data"; + case AT_byte_size: return "AT_byte_size"; + case AT_bit_offset: return "AT_bit_offset"; + case AT_bit_size: return "AT_bit_size"; + case AT_element_list: return "AT_element_list"; + case AT_stmt_list: return "AT_stmt_list"; + case AT_low_pc: return "AT_low_pc"; + case AT_high_pc: return "AT_high_pc"; + case AT_language: return "AT_language"; + case AT_member: return "AT_member"; + case AT_discr: return "AT_discr"; + case AT_discr_value: return "AT_discr_value"; + case AT_string_length: return "AT_string_length"; + case AT_common_reference: return "AT_common_reference"; + case AT_comp_dir: return "AT_comp_dir"; + case AT_const_value_string: return "AT_const_value_string"; + case AT_const_value_data2: return "AT_const_value_data2"; + case AT_const_value_data4: return "AT_const_value_data4"; + case AT_const_value_data8: return "AT_const_value_data8"; + case AT_const_value_block2: return "AT_const_value_block2"; + case AT_const_value_block4: return "AT_const_value_block4"; + case AT_containing_type: return "AT_containing_type"; + case AT_default_value_addr: return "AT_default_value_addr"; + case AT_default_value_data2: return "AT_default_value_data2"; + case AT_default_value_data4: return "AT_default_value_data4"; + case AT_default_value_data8: return "AT_default_value_data8"; + case AT_default_value_string: return "AT_default_value_string"; + case AT_friends: return "AT_friends"; + case AT_inline: return "AT_inline"; + case AT_is_optional: return "AT_is_optional"; + case AT_lower_bound_ref: return "AT_lower_bound_ref"; + case AT_lower_bound_data2: return "AT_lower_bound_data2"; + case AT_lower_bound_data4: return "AT_lower_bound_data4"; + case AT_lower_bound_data8: return "AT_lower_bound_data8"; + case AT_private: return "AT_private"; + case AT_producer: return "AT_producer"; + case AT_program: return "AT_program"; + case AT_protected: return "AT_protected"; + case AT_prototyped: return "AT_prototyped"; + case AT_public: return "AT_public"; + case AT_pure_virtual: return "AT_pure_virtual"; + case AT_return_addr: return "AT_return_addr"; + case AT_abstract_origin: return "AT_abstract_origin"; + case AT_start_scope: return "AT_start_scope"; + case AT_stride_size: return "AT_stride_size"; + case AT_upper_bound_ref: return "AT_upper_bound_ref"; + case AT_upper_bound_data2: return "AT_upper_bound_data2"; + case AT_upper_bound_data4: return "AT_upper_bound_data4"; + case AT_upper_bound_data8: return "AT_upper_bound_data8"; + case AT_virtual: return "AT_virtual"; + + /* GNU extensions */ + + case AT_sf_names: return "AT_sf_names"; + case AT_src_info: return "AT_src_info"; + case AT_mac_info: return "AT_mac_info"; + case AT_src_coords: return "AT_src_coords"; + case AT_body_begin: return "AT_body_begin"; + case AT_body_end: return "AT_body_end"; + + default: return "AT_"; + } +} + +static char * +dwarf_stack_op_name (op) + register unsigned op; +{ + switch (op) + { + case OP_REG: return "OP_REG"; + case OP_BASEREG: return "OP_BASEREG"; + case OP_ADDR: return "OP_ADDR"; + case OP_CONST: return "OP_CONST"; + case OP_DEREF2: return "OP_DEREF2"; + case OP_DEREF4: return "OP_DEREF4"; + case OP_ADD: return "OP_ADD"; + default: return "OP_"; + } +} + +static char * +dwarf_typemod_name (mod) + register unsigned mod; +{ + switch (mod) + { + case MOD_pointer_to: return "MOD_pointer_to"; + case MOD_reference_to: return "MOD_reference_to"; + case MOD_const: return "MOD_const"; + case MOD_volatile: return "MOD_volatile"; + default: return "MOD_"; + } +} + +static char * +dwarf_fmt_byte_name (fmt) + register unsigned fmt; +{ + switch (fmt) + { + case FMT_FT_C_C: return "FMT_FT_C_C"; + case FMT_FT_C_X: return "FMT_FT_C_X"; + case FMT_FT_X_C: return "FMT_FT_X_C"; + case FMT_FT_X_X: return "FMT_FT_X_X"; + case FMT_UT_C_C: return "FMT_UT_C_C"; + case FMT_UT_C_X: return "FMT_UT_C_X"; + case FMT_UT_X_C: return "FMT_UT_X_C"; + case FMT_UT_X_X: return "FMT_UT_X_X"; + case FMT_ET: return "FMT_ET"; + default: return "FMT_"; + } +} +static char * +dwarf_fund_type_name (ft) + register unsigned ft; +{ + switch (ft) + { + case FT_char: return "FT_char"; + case FT_signed_char: return "FT_signed_char"; + case FT_unsigned_char: return "FT_unsigned_char"; + case FT_short: return "FT_short"; + case FT_signed_short: return "FT_signed_short"; + case FT_unsigned_short: return "FT_unsigned_short"; + case FT_integer: return "FT_integer"; + case FT_signed_integer: return "FT_signed_integer"; + case FT_unsigned_integer: return "FT_unsigned_integer"; + case FT_long: return "FT_long"; + case FT_signed_long: return "FT_signed_long"; + case FT_unsigned_long: return "FT_unsigned_long"; + case FT_pointer: return "FT_pointer"; + case FT_float: return "FT_float"; + case FT_dbl_prec_float: return "FT_dbl_prec_float"; + case FT_ext_prec_float: return "FT_ext_prec_float"; + case FT_complex: return "FT_complex"; + case FT_dbl_prec_complex: return "FT_dbl_prec_complex"; + case FT_void: return "FT_void"; + case FT_boolean: return "FT_boolean"; + case FT_ext_prec_complex: return "FT_ext_prec_complex"; + case FT_label: return "FT_label"; + + /* GNU extensions. */ + + case FT_long_long: return "FT_long_long"; + case FT_signed_long_long: return "FT_signed_long_long"; + case FT_unsigned_long_long: return "FT_unsigned_long_long"; + + case FT_int8: return "FT_int8"; + case FT_signed_int8: return "FT_signed_int8"; + case FT_unsigned_int8: return "FT_unsigned_int8"; + case FT_int16: return "FT_int16"; + case FT_signed_int16: return "FT_signed_int16"; + case FT_unsigned_int16: return "FT_unsigned_int16"; + case FT_int32: return "FT_int32"; + case FT_signed_int32: return "FT_signed_int32"; + case FT_unsigned_int32: return "FT_unsigned_int32"; + case FT_int64: return "FT_int64"; + case FT_signed_int64: return "FT_signed_int64"; + case FT_unsigned_int64: return "FT_signed_int64"; + + case FT_real32: return "FT_real32"; + case FT_real64: return "FT_real64"; + case FT_real96: return "FT_real96"; + case FT_real128: return "FT_real128"; + + default: return "FT_"; + } +} + +/* Determine the "ultimate origin" of a decl. The decl may be an + inlined instance of an inlined instance of a decl which is local + to an inline function, so we have to trace all of the way back + through the origin chain to find out what sort of node actually + served as the original seed for the given block. */ + +static tree +decl_ultimate_origin (decl) + register tree decl; +{ + register tree immediate_origin = DECL_ABSTRACT_ORIGIN (decl); + + if (immediate_origin == NULL) + return NULL; + else + { + register tree ret_val; + register tree lookahead = immediate_origin; + + do + { + ret_val = lookahead; + lookahead = DECL_ABSTRACT_ORIGIN (ret_val); + } + while (lookahead != NULL && lookahead != ret_val); + return ret_val; + } +} + +/* Determine the "ultimate origin" of a block. The block may be an + inlined instance of an inlined instance of a block which is local + to an inline function, so we have to trace all of the way back + through the origin chain to find out what sort of node actually + served as the original seed for the given block. */ + +static tree +block_ultimate_origin (block) + register tree block; +{ + register tree immediate_origin = BLOCK_ABSTRACT_ORIGIN (block); + + if (immediate_origin == NULL) + return NULL; + else + { + register tree ret_val; + register tree lookahead = immediate_origin; + + do + { + ret_val = lookahead; + lookahead = (TREE_CODE (ret_val) == BLOCK) + ? BLOCK_ABSTRACT_ORIGIN (ret_val) + : NULL; + } + while (lookahead != NULL && lookahead != ret_val); + return ret_val; + } +} + +static void +output_unsigned_leb128 (value) + register unsigned long value; +{ + register unsigned long orig_value = value; + + do + { + register unsigned byte = (value & 0x7f); + + value >>= 7; + if (value != 0) /* more bytes to follow */ + byte |= 0x80; + fprintf (asm_out_file, "\t%s\t0x%x", ASM_BYTE_OP, (unsigned) byte); + if (flag_verbose_asm && value == 0) + fprintf (asm_out_file, "\t%s ULEB128 number - value = %u", + ASM_COMMENT_START, orig_value); + fputc ('\n', asm_out_file); + } + while (value != 0); +} + +static void +output_signed_leb128 (value) + register long value; +{ + register long orig_value = value; + register int negative = (value < 0); + register int more; + + do + { + register unsigned byte = (value & 0x7f); + + value >>= 7; + if (negative) + value |= 0xfe000000; /* manually sign extend */ + if (((value == 0) && ((byte & 0x40) == 0)) + || ((value == -1) && ((byte & 0x40) == 1))) + more = 0; + else + { + byte |= 0x80; + more = 1; + } + fprintf (asm_out_file, "\t%s\t0x%x", ASM_BYTE_OP, (unsigned) byte); + if (flag_verbose_asm && more == 0) + fprintf (asm_out_file, "\t%s SLEB128 number - value = %d", + ASM_COMMENT_START, orig_value); + fputc ('\n', asm_out_file); + } + while (more); +} + +/**************** utility functions for attribute functions ******************/ + +/* Given a pointer to a BLOCK node return non-zero if (and only if) the + node in question represents the outermost pair of curly braces (i.e. + the "body block") of a function or method. + + For any BLOCK node representing a "body block" of a function or method, + the BLOCK_SUPERCONTEXT of the node will point to another BLOCK node + which represents the outermost (function) scope for the function or + method (i.e. the one which includes the formal parameters). The + BLOCK_SUPERCONTEXT of *that* node in turn will point to the relevant + FUNCTION_DECL node. +*/ + +inline int +is_body_block (stmt) + register tree stmt; +{ + if (TREE_CODE (stmt) == BLOCK) + { + register tree parent = BLOCK_SUPERCONTEXT (stmt); + + if (TREE_CODE (parent) == BLOCK) + { + register tree grandparent = BLOCK_SUPERCONTEXT (parent); + + if (TREE_CODE (grandparent) == FUNCTION_DECL) + return 1; + } + } + return 0; +} + +/* Given a pointer to a tree node for some type, return a Dwarf fundamental + type code for the given type. + + This routine must only be called for GCC type nodes that correspond to + Dwarf fundamental types. + + The current Dwarf draft specification calls for Dwarf fundamental types + to accurately reflect the fact that a given type was either a "plain" + integral type or an explicitly "signed" integral type. Unfortunately, + we can't always do this, because GCC may already have thrown away the + information about the precise way in which the type was originally + specified, as in: + + typedef signed int my_type; + + struct s { my_type f; }; + + Since we may be stuck here without enought information to do exactly + what is called for in the Dwarf draft specification, we do the best + that we can under the circumstances and always use the "plain" integral + fundamental type codes for int, short, and long types. That's probably + good enough. The additional accuracy called for in the current DWARF + draft specification is probably never even useful in practice. */ + +static int +fundamental_type_code (type) + register tree type; +{ + if (TREE_CODE (type) == ERROR_MARK) + return 0; + + switch (TREE_CODE (type)) + { + case ERROR_MARK: + return FT_void; + + case VOID_TYPE: + return FT_void; + + case INTEGER_TYPE: + /* Carefully distinguish all the standard types of C, + without messing up if the language is not C. + Note that we check only for the names that contain spaces; + other names might occur by coincidence in other languages. */ + if (TYPE_NAME (type) != 0 + && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL + && DECL_NAME (TYPE_NAME (type)) != 0 + && TREE_CODE (DECL_NAME (TYPE_NAME (type))) == IDENTIFIER_NODE) + { + char *name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); + + if (!strcmp (name, "unsigned char")) + return FT_unsigned_char; + if (!strcmp (name, "signed char")) + return FT_signed_char; + if (!strcmp (name, "unsigned int")) + return FT_unsigned_integer; + if (!strcmp (name, "short int")) + return FT_short; + if (!strcmp (name, "short unsigned int")) + return FT_unsigned_short; + if (!strcmp (name, "long int")) + return FT_long; + if (!strcmp (name, "long unsigned int")) + return FT_unsigned_long; + if (!strcmp (name, "long long int")) + return FT_long_long; /* Not grok'ed by svr4 SDB */ + if (!strcmp (name, "long long unsigned int")) + return FT_unsigned_long_long; /* Not grok'ed by svr4 SDB */ + } + + /* Most integer types will be sorted out above, however, for the + sake of special `array index' integer types, the following code + is also provided. */ + + if (TYPE_PRECISION (type) == INT_TYPE_SIZE) + return (TREE_UNSIGNED (type) ? FT_unsigned_integer : FT_integer); + + if (TYPE_PRECISION (type) == LONG_TYPE_SIZE) + return (TREE_UNSIGNED (type) ? FT_unsigned_long : FT_long); + + if (TYPE_PRECISION (type) == LONG_LONG_TYPE_SIZE) + return (TREE_UNSIGNED (type) ? FT_unsigned_long_long : FT_long_long); + + if (TYPE_PRECISION (type) == SHORT_TYPE_SIZE) + return (TREE_UNSIGNED (type) ? FT_unsigned_short : FT_short); + + if (TYPE_PRECISION (type) == CHAR_TYPE_SIZE) + return (TREE_UNSIGNED (type) ? FT_unsigned_char : FT_char); + + abort (); + + case REAL_TYPE: + /* Carefully distinguish all the standard types of C, + without messing up if the language is not C. */ + if (TYPE_NAME (type) != 0 + && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL + && DECL_NAME (TYPE_NAME (type)) != 0 + && TREE_CODE (DECL_NAME (TYPE_NAME (type))) == IDENTIFIER_NODE) + { + char *name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); + + /* Note that here we can run afowl of a serious bug in "classic" + svr4 SDB debuggers. They don't seem to understand the + FT_ext_prec_float type (even though they should). */ + + if (!strcmp (name, "long double")) + return FT_ext_prec_float; + } + + if (TYPE_PRECISION (type) == DOUBLE_TYPE_SIZE) + return FT_dbl_prec_float; + if (TYPE_PRECISION (type) == FLOAT_TYPE_SIZE) + return FT_float; + + /* Note that here we can run afowl of a serious bug in "classic" + svr4 SDB debuggers. They don't seem to understand the + FT_ext_prec_float type (even though they should). */ + + if (TYPE_PRECISION (type) == LONG_DOUBLE_TYPE_SIZE) + return FT_ext_prec_float; + abort (); + + case COMPLEX_TYPE: + return FT_complex; /* GNU FORTRAN COMPLEX type. */ + + case CHAR_TYPE: + return FT_char; /* GNU Pascal CHAR type. Not used in C. */ + + case BOOLEAN_TYPE: + return FT_boolean; /* GNU FORTRAN BOOLEAN type. */ + + default: + abort (); /* No other TREE_CODEs are Dwarf fundamental types. */ + } + return 0; +} + +/* Given a pointer to an arbitrary ..._TYPE tree node, return a pointer to + the Dwarf "root" type for the given input type. The Dwarf "root" type + of a given type is generally the same as the given type, except that if + the given type is a pointer or reference type, then the root type of + the given type is the root type of the "basis" type for the pointer or + reference type. (This definition of the "root" type is recursive.) + Also, the root type of a `const' qualified type or a `volatile' + qualified type is the root type of the given type without the + qualifiers. */ + +static tree +root_type (type) + register tree type; +{ + if (TREE_CODE (type) == ERROR_MARK) + return error_mark_node; + + switch (TREE_CODE (type)) + { + case ERROR_MARK: + return error_mark_node; + + case POINTER_TYPE: + case REFERENCE_TYPE: + return type_main_variant (root_type (TREE_TYPE (type))); + + default: + return type_main_variant (type); + } +} + +/* Given a pointer to an arbitrary ..._TYPE tree node, write out a sequence + of zero or more Dwarf "type-modifier" bytes applicable to the type. */ + +static void +write_modifier_bytes (type, decl_const, decl_volatile) + register tree type; + register int decl_const; + register int decl_volatile; +{ + if (TREE_CODE (type) == ERROR_MARK) + return; + + if (TYPE_READONLY (type) || decl_const) + ASM_OUTPUT_DWARF_TYPE_MODIFIER (asm_out_file, MOD_const); + if (TYPE_VOLATILE (type) || decl_volatile) + ASM_OUTPUT_DWARF_TYPE_MODIFIER (asm_out_file, MOD_volatile); + switch (TREE_CODE (type)) + { + case POINTER_TYPE: + ASM_OUTPUT_DWARF_TYPE_MODIFIER (asm_out_file, MOD_pointer_to); + write_modifier_bytes (TREE_TYPE (type), 0, 0); + return; + + case REFERENCE_TYPE: + ASM_OUTPUT_DWARF_TYPE_MODIFIER (asm_out_file, MOD_reference_to); + write_modifier_bytes (TREE_TYPE (type), 0, 0); + return; + + case ERROR_MARK: + default: + return; + } +} + +/* Given a pointer to an arbitrary ..._TYPE tree node, return non-zero if the + given input type is a Dwarf "fundamental" type. Otherwise return zero. */ + +inline int +type_is_fundamental (type) + register tree type; +{ + switch (TREE_CODE (type)) + { + case ERROR_MARK: + case VOID_TYPE: + case INTEGER_TYPE: + case REAL_TYPE: + case COMPLEX_TYPE: + case BOOLEAN_TYPE: + case CHAR_TYPE: + return 1; + + case SET_TYPE: + case ARRAY_TYPE: + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + case ENUMERAL_TYPE: + case FUNCTION_TYPE: + case METHOD_TYPE: + case POINTER_TYPE: + case REFERENCE_TYPE: + case STRING_TYPE: + case FILE_TYPE: + case OFFSET_TYPE: + case LANG_TYPE: + return 0; + + default: + abort (); + } + return 0; +} + +/* Given a pointer to some ..._DECL tree node, generate an assembly language + equate directive which will associate a symbolic name with the current DIE. + + The name used is an artificial label generated from the DECL_UID number + associated with the given decl node. The name it gets equated to is the + symbolic label that we (previously) output at the start of the DIE that + we are currently generating. + + Calling this function while generating some "decl related" form of DIE + makes it possible to later refer to the DIE which represents the given + decl simply by re-generating the symbolic name from the ..._DECL node's + UID number. */ + +static void +equate_decl_number_to_die_number (decl) + register tree decl; +{ + /* In the case where we are generating a DIE for some ..._DECL node + which represents either some inline function declaration or some + entity declared within an inline function declaration/definition, + setup a symbolic name for the current DIE so that we have a name + for this DIE that we can easily refer to later on within + AT_abstract_origin attributes. */ + + char decl_label[MAX_ARTIFICIAL_LABEL_BYTES]; + char die_label[MAX_ARTIFICIAL_LABEL_BYTES]; + + sprintf (decl_label, DECL_NAME_FMT, DECL_UID (decl)); + sprintf (die_label, DIE_BEGIN_LABEL_FMT, current_dienum); + ASM_OUTPUT_DEF (asm_out_file, decl_label, die_label); +} + +/* Given a pointer to some ..._TYPE tree node, generate an assembly language + equate directive which will associate a symbolic name with the current DIE. + + The name used is an artificial label generated from the TYPE_UID number + associated with the given type node. The name it gets equated to is the + symbolic label that we (previously) output at the start of the DIE that + we are currently generating. + + Calling this function while generating some "type related" form of DIE + makes it easy to later refer to the DIE which represents the given type + simply by re-generating the alternative name from the ..._TYPE node's + UID number. */ + +inline void +equate_type_number_to_die_number (type) + register tree type; +{ + char type_label[MAX_ARTIFICIAL_LABEL_BYTES]; + char die_label[MAX_ARTIFICIAL_LABEL_BYTES]; + + /* We are generating a DIE to represent the main variant of this type + (i.e the type without any const or volatile qualifiers) so in order + to get the equate to come out right, we need to get the main variant + itself here. */ + + type = type_main_variant (type); + + sprintf (type_label, TYPE_NAME_FMT, TYPE_UID (type)); + sprintf (die_label, DIE_BEGIN_LABEL_FMT, current_dienum); + ASM_OUTPUT_DEF (asm_out_file, type_label, die_label); +} + +static void +output_reg_number (rtl) + register rtx rtl; +{ + register unsigned regno = REGNO (rtl); + + if (regno >= FIRST_PSEUDO_REGISTER) + { + warning_with_decl (dwarf_last_decl, "internal regno botch: regno = %d\n", + regno); + regno = 0; + } + fprintf (asm_out_file, "\t%s\t0x%x", + UNALIGNED_INT_ASM_OP, DBX_REGISTER_NUMBER (regno)); + if (flag_verbose_asm) + { + fprintf (asm_out_file, "\t%s ", ASM_COMMENT_START); + PRINT_REG (rtl, 0, asm_out_file); + } + fputc ('\n', asm_out_file); +} + +/* The following routine is a nice and simple transducer. It converts the + RTL for a variable or parameter (resident in memory) into an equivalent + Dwarf representation of a mechanism for getting the address of that same + variable onto the top of a hypothetical "address evaluation" stack. + + When creating memory location descriptors, we are effectively trans- + forming the RTL for a memory-resident object into its Dwarf postfix + expression equivalent. This routine just recursively descends an + RTL tree, turning it into Dwarf postfix code as it goes. */ + +static void +output_mem_loc_descriptor (rtl) + register rtx rtl; +{ + /* Note that for a dynamically sized array, the location we will + generate a description of here will be the lowest numbered location + which is actually within the array. That's *not* necessarily the + same as the zeroth element of the array. */ + + switch (GET_CODE (rtl)) + { + case SUBREG: + + /* The case of a subreg may arise when we have a local (register) + variable or a formal (register) parameter which doesn't quite + fill up an entire register. For now, just assume that it is + legitimate to make the Dwarf info refer to the whole register + which contains the given subreg. */ + + rtl = XEXP (rtl, 0); + /* Drop thru. */ + + case REG: + + /* Whenever a register number forms a part of the description of + the method for calculating the (dynamic) address of a memory + resident object, DWARF rules require the register number to + be referred to as a "base register". This distinction is not + based in any way upon what category of register the hardware + believes the given register belongs to. This is strictly + DWARF terminology we're dealing with here. + + Note that in cases where the location of a memory-resident data + object could be expressed as: + + OP_ADD (OP_BASEREG (basereg), OP_CONST (0)) + + the actual DWARF location descriptor that we generate may just + be OP_BASEREG (basereg). This may look deceptively like the + object in question was allocated to a register (rather than + in memory) so DWARF consumers need to be aware of the subtle + distinction between OP_REG and OP_BASEREG. */ + + ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_BASEREG); + output_reg_number (rtl); + break; + + case MEM: + output_mem_loc_descriptor (XEXP (rtl, 0)); + ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_DEREF4); + break; + + case CONST: + case SYMBOL_REF: + ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_ADDR); + ASM_OUTPUT_DWARF_ADDR_CONST (asm_out_file, rtl); + break; + + case PLUS: + output_mem_loc_descriptor (XEXP (rtl, 0)); + output_mem_loc_descriptor (XEXP (rtl, 1)); + ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_ADD); + break; + + case CONST_INT: + ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_CONST); + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, INTVAL (rtl)); + break; + + default: + abort (); + } +} + +/* Output a proper Dwarf location descriptor for a variable or parameter + which is either allocated in a register or in a memory location. For + a register, we just generate an OP_REG and the register number. For a + memory location we provide a Dwarf postfix expression describing how to + generate the (dynamic) address of the object onto the address stack. */ + +static void +output_loc_descriptor (rtl) + register rtx rtl; +{ + switch (GET_CODE (rtl)) + { + case SUBREG: + + /* The case of a subreg may arise when we have a local (register) + variable or a formal (register) parameter which doesn't quite + fill up an entire register. For now, just assume that it is + legitimate to make the Dwarf info refer to the whole register + which contains the given subreg. */ + + rtl = XEXP (rtl, 0); + /* Drop thru. */ + + case REG: + ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_REG); + output_reg_number (rtl); + break; + + case MEM: + output_mem_loc_descriptor (XEXP (rtl, 0)); + break; + + default: + abort (); /* Should never happen */ + } +} + +/* Given a tree node describing an array bound (either lower or upper) + output a representation for that bound. */ + +static void +output_bound_representation (bound, dim_num, u_or_l) + register tree bound; + register unsigned dim_num; /* For multi-dimensional arrays. */ + register char u_or_l; /* Designates upper or lower bound. */ +{ + switch (TREE_CODE (bound)) + { + + case ERROR_MARK: + return; + + /* All fixed-bounds are represented by INTEGER_CST nodes. */ + + case INTEGER_CST: + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, + (unsigned) TREE_INT_CST_LOW (bound)); + break; + + /* Dynamic bounds may be represented by NOP_EXPR nodes containing + SAVE_EXPR nodes. */ + + case NOP_EXPR: + bound = TREE_OPERAND (bound, 0); + /* ... fall thru... */ + + case SAVE_EXPR: + { + char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; + char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; + + sprintf (begin_label, BOUND_BEGIN_LABEL_FMT, + current_dienum, dim_num, u_or_l); + + sprintf (end_label, BOUND_END_LABEL_FMT, + current_dienum, dim_num, u_or_l); + + ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label); + ASM_OUTPUT_LABEL (asm_out_file, begin_label); + + /* If we are working on a bound for a dynamic dimension in C, + the dynamic dimension in question had better have a static + (zero) lower bound and a dynamic *upper* bound. */ + + if (u_or_l != 'u') + abort (); + + /* If optimization is turned on, the SAVE_EXPRs that describe + how to access the upper bound values are essentially bogus. + They only describe (at best) how to get at these values at + the points in the generated code right after they have just + been computed. Worse yet, in the typical case, the upper + bound values will not even *be* computed in the optimized + code, so these SAVE_EXPRs are entirely bogus. + + In order to compensate for this fact, we check here to see + if optimization is enabled, and if so, we effectively create + an empty location description for the (unknown and unknowable) + upper bound. + + This should not cause too much trouble for existing (stupid?) + debuggers because they have to deal with empty upper bounds + location descriptions anyway in order to be able to deal with + incomplete array types. + + Of course an intelligent debugger (GDB?) should be able to + comprehend that a missing upper bound specification in a + array type used for a storage class `auto' local array variable + indicates that the upper bound is both unknown (at compile- + time) and unknowable (at run-time) due to optimization. + */ + + if (! optimize) + output_loc_descriptor + (eliminate_regs (SAVE_EXPR_RTL (bound), 0, NULL_RTX)); + + ASM_OUTPUT_LABEL (asm_out_file, end_label); + } + break; + + default: + abort (); + } +} + +/* Recursive function to output a sequence of value/name pairs for + enumeration constants in reversed order. This is called from + enumeration_type_die. */ + +static void +output_enumeral_list (link) + register tree link; +{ + if (link) + { + output_enumeral_list (TREE_CHAIN (link)); + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, + (unsigned) TREE_INT_CST_LOW (TREE_VALUE (link))); + ASM_OUTPUT_DWARF_STRING (asm_out_file, + IDENTIFIER_POINTER (TREE_PURPOSE (link))); + } +} + +/* Given an unsigned value, round it up to the lowest multiple of `boundary' + which is not less than the value itself. */ + +inline unsigned +ceiling (value, boundary) + register unsigned value; + register unsigned boundary; +{ + return (((value + boundary - 1) / boundary) * boundary); +} + +/* Given a pointer to what is assumed to be a FIELD_DECL node, return a + pointer to the declared type for the relevant field variable, or return + `integer_type_node' if the given node turns out to be an ERROR_MARK node. */ + +inline tree +field_type (decl) + register tree decl; +{ + register tree type; + + if (TREE_CODE (decl) == ERROR_MARK) + return integer_type_node; + + type = DECL_BIT_FIELD_TYPE (decl); + if (type == NULL) + type = TREE_TYPE (decl); + return type; +} + +/* Given a pointer to a tree node, assumed to be some kind of a ..._TYPE + node, return the alignment in bits for the type, or else return + BITS_PER_WORD if the node actually turns out to be an ERROR_MARK node. */ + +inline unsigned +simple_type_align_in_bits (type) + register tree type; +{ + return (TREE_CODE (type) != ERROR_MARK) ? TYPE_ALIGN (type) : BITS_PER_WORD; +} + +/* Given a pointer to a tree node, assumed to be some kind of a ..._TYPE + node, return the size in bits for the type if it is a constant, or + else return the alignment for the type if the type's size is not + constant, or else return BITS_PER_WORD if the type actually turns out + to be an ERROR_MARK node. */ + +inline unsigned +simple_type_size_in_bits (type) + register tree type; +{ + if (TREE_CODE (type) == ERROR_MARK) + return BITS_PER_WORD; + else + { + register tree type_size_tree = TYPE_SIZE (type); + + if (TREE_CODE (type_size_tree) != INTEGER_CST) + return TYPE_ALIGN (type); + + return (unsigned) TREE_INT_CST_LOW (type_size_tree); + } +} + +/* Given a pointer to what is assumed to be a FIELD_DECL node, compute and + return the byte offset of the lowest addressed byte of the "containing + object" for the given FIELD_DECL, or return 0 if we are unable to deter- + mine what that offset is, either because the argument turns out to be a + pointer to an ERROR_MARK node, or because the offset is actually variable. + (We can't handle the latter case just yet.) */ + +static unsigned +field_byte_offset (decl) + register tree decl; +{ + register unsigned type_align_in_bytes; + register unsigned type_align_in_bits; + register unsigned type_size_in_bits; + register unsigned object_offset_in_align_units; + register unsigned object_offset_in_bits; + register unsigned object_offset_in_bytes; + register tree type; + register tree bitpos_tree; + register tree field_size_tree; + register unsigned bitpos_int; + register unsigned deepest_bitpos; + register unsigned field_size_in_bits; + + if (TREE_CODE (decl) == ERROR_MARK) + return 0; + + if (TREE_CODE (decl) != FIELD_DECL) + abort (); + + type = field_type (decl); + + bitpos_tree = DECL_FIELD_BITPOS (decl); + field_size_tree = DECL_SIZE (decl); + + /* We cannot yet cope with fields whose positions or sizes are variable, + so for now, when we see such things, we simply return 0. Someday, + we may be able to handle such cases, but it will be damn difficult. */ + + if (TREE_CODE (bitpos_tree) != INTEGER_CST) + return 0; + bitpos_int = (unsigned) TREE_INT_CST_LOW (bitpos_tree); + + if (TREE_CODE (field_size_tree) != INTEGER_CST) + return 0; + field_size_in_bits = (unsigned) TREE_INT_CST_LOW (field_size_tree); + + type_size_in_bits = simple_type_size_in_bits (type); + + type_align_in_bits = simple_type_align_in_bits (type); + type_align_in_bytes = type_align_in_bits / BITS_PER_UNIT; + + /* Note that the GCC front-end doesn't make any attempt to keep track + of the starting bit offset (relative to the start of the containing + structure type) of the hypothetical "containing object" for a bit- + field. Thus, when computing the byte offset value for the start of + the "containing object" of a bit-field, we must deduce this infor- + mation on our own. + + This can be rather tricky to do in some cases. For example, handling + the following structure type definition when compiling for an i386/i486 + target (which only aligns long long's to 32-bit boundaries) can be very + tricky: + + struct S { + int field1; + long long field2:31; + }; + + Fortunately, there is a simple rule-of-thumb which can be used in such + cases. When compiling for an i386/i486, GCC will allocate 8 bytes for + the structure shown above. It decides to do this based upon one simple + rule for bit-field allocation. Quite simply, GCC allocates each "con- + taining object" for each bit-field at the first (i.e. lowest addressed) + legitimate alignment boundary (based upon the required minimum alignment + for the declared type of the field) which it can possibly use, subject + to the condition that there is still enough available space remaining + in the containing object (when allocated at the selected point) to + fully accommodate all of the bits of the bit-field itself. + + This simple rule makes it obvious why GCC allocates 8 bytes for each + object of the structure type shown above. When looking for a place to + allocate the "containing object" for `field2', the compiler simply tries + to allocate a 64-bit "containing object" at each successive 32-bit + boundary (starting at zero) until it finds a place to allocate that 64- + bit field such that at least 31 contiguous (and previously unallocated) + bits remain within that selected 64 bit field. (As it turns out, for + the example above, the compiler finds that it is OK to allocate the + "containing object" 64-bit field at bit-offset zero within the + structure type.) + + Here we attempt to work backwards from the limited set of facts we're + given, and we try to deduce from those facts, where GCC must have + believed that the containing object started (within the structure type). + + The value we deduce is then used (by the callers of this routine) to + generate AT_location and AT_bit_offset attributes for fields (both + bit-fields and, in the case of AT_location, regular fields as well). + */ + + /* Figure out the bit-distance from the start of the structure to the + "deepest" bit of the bit-field. */ + deepest_bitpos = bitpos_int + field_size_in_bits; + + /* This is the tricky part. Use some fancy footwork to deduce where the + lowest addressed bit of the containing object must be. */ + object_offset_in_bits + = ceiling (deepest_bitpos, type_align_in_bits) - type_size_in_bits; + + /* Compute the offset of the containing object in "alignment units". */ + object_offset_in_align_units = object_offset_in_bits / type_align_in_bits; + + /* Compute the offset of the containing object in bytes. */ + object_offset_in_bytes = object_offset_in_align_units * type_align_in_bytes; + + return object_offset_in_bytes; +} + +/****************************** attributes *********************************/ + +/* The following routines are responsible for writing out the various types + of Dwarf attributes (and any following data bytes associated with them). + These routines are listed in order based on the numerical codes of their + associated attributes. */ + +/* Generate an AT_sibling attribute. */ + +inline void +sibling_attribute () +{ + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_sibling); + sprintf (label, DIE_BEGIN_LABEL_FMT, NEXT_DIE_NUM); + ASM_OUTPUT_DWARF_REF (asm_out_file, label); +} + +/* Output the form of location attributes suitable for whole variables and + whole parameters. Note that the location attributes for struct fields + are generated by the routine `data_member_location_attribute' below. */ + +static void +location_attribute (rtl) + register rtx rtl; +{ + char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; + char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; + + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_location); + sprintf (begin_label, LOC_BEGIN_LABEL_FMT, current_dienum); + sprintf (end_label, LOC_END_LABEL_FMT, current_dienum); + ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label); + ASM_OUTPUT_LABEL (asm_out_file, begin_label); + + /* Handle a special case. If we are about to output a location descriptor + for a variable or parameter which has been optimized out of existence, + don't do that. Instead we output a zero-length location descriptor + value as part of the location attribute. + + A variable which has been optimized out of existence will have a + DECL_RTL value which denotes a pseudo-reg. + + Currently, in some rare cases, variables can have DECL_RTL values + which look like (MEM (REG pseudo-reg#)). These cases are due to + bugs elsewhere in the compiler. We treat such cases + as if the variable(s) in question had been optimized out of existence. + + Note that in all cases where we wish to express the fact that a + variable has been optimized out of existence, we do not simply + suppress the generation of the entire location attribute because + the absence of a location attribute in certain kinds of DIEs is + used to indicate something else entirely... i.e. that the DIE + represents an object declaration, but not a definition. So sayeth + the PLSIG. + */ + + if (! is_pseudo_reg (rtl) + && (GET_CODE (rtl) != MEM || ! is_pseudo_reg (XEXP (rtl, 0)))) + output_loc_descriptor (eliminate_regs (rtl, 0, NULL_RTX)); + + ASM_OUTPUT_LABEL (asm_out_file, end_label); +} + +/* Output the specialized form of location attribute used for data members + of struct and union types. + + In the special case of a FIELD_DECL node which represents a bit-field, + the "offset" part of this special location descriptor must indicate the + distance in bytes from the lowest-addressed byte of the containing + struct or union type to the lowest-addressed byte of the "containing + object" for the bit-field. (See the `field_byte_offset' function above.) + + For any given bit-field, the "containing object" is a hypothetical + object (of some integral or enum type) within which the given bit-field + lives. The type of this hypothetical "containing object" is always the + same as the declared type of the individual bit-field itself (for GCC + anyway... the DWARF spec doesn't actually mandate this). + + Note that it is the size (in bytes) of the hypothetical "containing + object" which will be given in the AT_byte_size attribute for this + bit-field. (See the `byte_size_attribute' function below.) It is + also used when calculating the value of the AT_bit_offset attribute. + (See the `bit_offset_attribute' function below.) +*/ + +static void +data_member_location_attribute (decl) + register tree decl; +{ + register unsigned object_offset_in_bytes = field_byte_offset (decl); + char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; + char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; + + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_location); + sprintf (begin_label, LOC_BEGIN_LABEL_FMT, current_dienum); + sprintf (end_label, LOC_END_LABEL_FMT, current_dienum); + ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label); + ASM_OUTPUT_LABEL (asm_out_file, begin_label); + ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_CONST); + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, object_offset_in_bytes); + ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_ADD); + ASM_OUTPUT_LABEL (asm_out_file, end_label); +} + +/* Output an AT_const_value attribute for a variable or a parameter which + does not have a "location" either in memory or in a register. These + things can arise in GNU C when a constant is passed as an actual + parameter to an inlined function. They can also arise in C++ where + declared constants do not necessarily get memory "homes". */ + +static void +const_value_attribute (rtl) + register rtx rtl; +{ + char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; + char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; + + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_const_value_block4); + sprintf (begin_label, LOC_BEGIN_LABEL_FMT, current_dienum); + sprintf (end_label, LOC_END_LABEL_FMT, current_dienum); + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, end_label, begin_label); + ASM_OUTPUT_LABEL (asm_out_file, begin_label); + + switch (GET_CODE (rtl)) + { + case CONST_INT: + /* Note that a CONST_INT rtx could represent either an integer or + a floating-point constant. A CONST_INT is used whenever the + constant will fit into a single word. In all such cases, the + original mode of the constant value is wiped out, and the + CONST_INT rtx is assigned VOIDmode. Since we no longer have + precise mode information for these constants, we always just + output them using 4 bytes. */ + + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, (unsigned) INTVAL (rtl)); + break; + + case CONST_DOUBLE: + /* Note that a CONST_DOUBLE rtx could represent either an integer + or a floating-point constant. A CONST_DOUBLE is used whenever + the constant requires more than one word in order to be adequately + represented. In all such cases, the original mode of the constant + value is preserved as the mode of the CONST_DOUBLE rtx, but for + simplicity we always just output CONST_DOUBLEs using 8 bytes. */ + + ASM_OUTPUT_DWARF_DATA8 (asm_out_file, + (unsigned HOST_WIDE_INT) CONST_DOUBLE_HIGH (rtl), + (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (rtl)); + break; + + case CONST_STRING: + ASM_OUTPUT_DWARF_STRING (asm_out_file, XSTR (rtl, 0)); + break; + + case SYMBOL_REF: + case LABEL_REF: + case CONST: + ASM_OUTPUT_DWARF_ADDR_CONST (asm_out_file, rtl); + break; + + case PLUS: + /* In cases where an inlined instance of an inline function is passed + the address of an `auto' variable (which is local to the caller) + we can get a situation where the DECL_RTL of the artificial + local variable (for the inlining) which acts as a stand-in for + the corresponding formal parameter (of the inline function) + will look like (plus:SI (reg:SI FRAME_PTR) (const_int ...)). + This is not exactly a compile-time constant expression, but it + isn't the address of the (artificial) local variable either. + Rather, it represents the *value* which the artificial local + variable always has during its lifetime. We currently have no + way to represent such quasi-constant values in Dwarf, so for now + we just punt and generate an AT_const_value attribute with form + FORM_BLOCK4 and a length of zero. */ + break; + + default: + abort (); /* No other kinds of rtx should be possible here. */ + } + + ASM_OUTPUT_LABEL (asm_out_file, end_label); +} + +/* Generate *either* an AT_location attribute or else an AT_const_value + data attribute for a variable or a parameter. We generate the + AT_const_value attribute only in those cases where the given + variable or parameter does not have a true "location" either in + memory or in a register. This can happen (for example) when a + constant is passed as an actual argument in a call to an inline + function. (It's possible that these things can crop up in other + ways also.) Note that one type of constant value which can be + passed into an inlined function is a constant pointer. This can + happen for example if an actual argument in an inlined function + call evaluates to a compile-time constant address. */ + +static void +location_or_const_value_attribute (decl) + register tree decl; +{ + register rtx rtl; + + if (TREE_CODE (decl) == ERROR_MARK) + return; + + if ((TREE_CODE (decl) != VAR_DECL) && (TREE_CODE (decl) != PARM_DECL)) + { + /* Should never happen. */ + abort (); + return; + } + + /* Here we have to decide where we are going to say the parameter "lives" + (as far as the debugger is concerned). We only have a couple of choices. + GCC provides us with DECL_RTL and with DECL_INCOMING_RTL. DECL_RTL + normally indicates where the parameter lives during most of the activa- + tion of the function. If optimization is enabled however, this could + be either NULL or else a pseudo-reg. Both of those cases indicate that + the parameter doesn't really live anywhere (as far as the code generation + parts of GCC are concerned) during most of the function's activation. + That will happen (for example) if the parameter is never referenced + within the function. + + We could just generate a location descriptor here for all non-NULL + non-pseudo values of DECL_RTL and ignore all of the rest, but we can + be a little nicer than that if we also consider DECL_INCOMING_RTL in + cases where DECL_RTL is NULL or is a pseudo-reg. + + Note however that we can only get away with using DECL_INCOMING_RTL as + a backup substitute for DECL_RTL in certain limited cases. In cases + where DECL_ARG_TYPE(decl) indicates the same type as TREE_TYPE(decl) + we can be sure that the parameter was passed using the same type as it + is declared to have within the function, and that its DECL_INCOMING_RTL + points us to a place where a value of that type is passed. In cases + where DECL_ARG_TYPE(decl) and TREE_TYPE(decl) are different types + however, we cannot (in general) use DECL_INCOMING_RTL as a backup + substitute for DECL_RTL because in these cases, DECL_INCOMING_RTL + points us to a value of some type which is *different* from the type + of the parameter itself. Thus, if we tried to use DECL_INCOMING_RTL + to generate a location attribute in such cases, the debugger would + end up (for example) trying to fetch a `float' from a place which + actually contains the first part of a `double'. That would lead to + really incorrect and confusing output at debug-time, and we don't + want that now do we? + + So in general, we DO NOT use DECL_INCOMING_RTL as a backup for DECL_RTL + in cases where DECL_ARG_TYPE(decl) != TREE_TYPE(decl). There are a + couple of cute exceptions however. On little-endian machines we can + get away with using DECL_INCOMING_RTL even when DECL_ARG_TYPE(decl) is + not the same as TREE_TYPE(decl) but only when DECL_ARG_TYPE(decl) is + an integral type which is smaller than TREE_TYPE(decl). These cases + arise when (on a little-endian machine) a non-prototyped function has + a parameter declared to be of type `short' or `char'. In such cases, + TREE_TYPE(decl) will be `short' or `char', DECL_ARG_TYPE(decl) will be + `int', and DECL_INCOMING_RTL will point to the lowest-order byte of the + passed `int' value. If the debugger then uses that address to fetch a + `short' or a `char' (on a little-endian machine) the result will be the + correct data, so we allow for such exceptional cases below. + + Note that our goal here is to describe the place where the given formal + parameter lives during most of the function's activation (i.e. between + the end of the prologue and the start of the epilogue). We'll do that + as best as we can. Note however that if the given formal parameter is + modified sometime during the execution of the function, then a stack + backtrace (at debug-time) will show the function as having been called + with the *new* value rather than the value which was originally passed + in. This happens rarely enough that it is not a major problem, but it + *is* a problem, and I'd like to fix it. A future version of dwarfout.c + may generate two additional attributes for any given TAG_formal_parameter + DIE which will describe the "passed type" and the "passed location" for + the given formal parameter in addition to the attributes we now generate + to indicate the "declared type" and the "active location" for each + parameter. This additional set of attributes could be used by debuggers + for stack backtraces. + + Separately, note that sometimes DECL_RTL can be NULL and DECL_INCOMING_RTL + can be NULL also. This happens (for example) for inlined-instances of + inline function formal parameters which are never referenced. This really + shouldn't be happening. All PARM_DECL nodes should get valid non-NULL + DECL_INCOMING_RTL values, but integrate.c doesn't currently generate + these values for inlined instances of inline function parameters, so + when we see such cases, we are just SOL (shit-out-of-luck) for the time + being (until integrate.c gets fixed). + */ + + /* Use DECL_RTL as the "location" unless we find something better. */ + rtl = DECL_RTL (decl); + + if (TREE_CODE (decl) == PARM_DECL) + if (rtl == NULL_RTX || is_pseudo_reg (rtl)) + { + /* This decl represents a formal parameter which was optimized out. */ + register tree declared_type = type_main_variant (TREE_TYPE (decl)); + register tree passed_type = type_main_variant (DECL_ARG_TYPE (decl)); + + /* Note that DECL_INCOMING_RTL may be NULL in here, but we handle + *all* cases where (rtl == NULL_RTX) just below. */ + + if (declared_type == passed_type) + rtl = DECL_INCOMING_RTL (decl); +#if (BYTES_BIG_ENDIAN == 0) + else + if (TREE_CODE (declared_type) == INTEGER_TYPE) + if (TYPE_SIZE (declared_type) <= TYPE_SIZE (passed_type)) + rtl = DECL_INCOMING_RTL (decl); +#endif /* (BYTES_BIG_ENDIAN == 0) */ + } + + if (rtl == NULL_RTX) + return; + + switch (GET_CODE (rtl)) + { + case CONST_INT: + case CONST_DOUBLE: + case CONST_STRING: + case SYMBOL_REF: + case LABEL_REF: + case CONST: + case PLUS: /* DECL_RTL could be (plus (reg ...) (const_int ...)) */ + const_value_attribute (rtl); + break; + + case MEM: + case REG: + case SUBREG: + location_attribute (rtl); + break; + + default: + abort (); /* Should never happen. */ + } +} + +/* Generate an AT_name attribute given some string value to be included as + the value of the attribute. */ + +inline void +name_attribute (name_string) + register char *name_string; +{ + if (name_string && *name_string) + { + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_name); + ASM_OUTPUT_DWARF_STRING (asm_out_file, name_string); + } +} + +inline void +fund_type_attribute (ft_code) + register unsigned ft_code; +{ + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_fund_type); + ASM_OUTPUT_DWARF_FUND_TYPE (asm_out_file, ft_code); +} + +static void +mod_fund_type_attribute (type, decl_const, decl_volatile) + register tree type; + register int decl_const; + register int decl_volatile; +{ + char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; + char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; + + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_mod_fund_type); + sprintf (begin_label, MT_BEGIN_LABEL_FMT, current_dienum); + sprintf (end_label, MT_END_LABEL_FMT, current_dienum); + ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label); + ASM_OUTPUT_LABEL (asm_out_file, begin_label); + write_modifier_bytes (type, decl_const, decl_volatile); + ASM_OUTPUT_DWARF_FUND_TYPE (asm_out_file, + fundamental_type_code (root_type (type))); + ASM_OUTPUT_LABEL (asm_out_file, end_label); +} + +inline void +user_def_type_attribute (type) + register tree type; +{ + char ud_type_name[MAX_ARTIFICIAL_LABEL_BYTES]; + + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_user_def_type); + sprintf (ud_type_name, TYPE_NAME_FMT, TYPE_UID (type)); + ASM_OUTPUT_DWARF_REF (asm_out_file, ud_type_name); +} + +static void +mod_u_d_type_attribute (type, decl_const, decl_volatile) + register tree type; + register int decl_const; + register int decl_volatile; +{ + char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; + char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; + char ud_type_name[MAX_ARTIFICIAL_LABEL_BYTES]; + + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_mod_u_d_type); + sprintf (begin_label, MT_BEGIN_LABEL_FMT, current_dienum); + sprintf (end_label, MT_END_LABEL_FMT, current_dienum); + ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label); + ASM_OUTPUT_LABEL (asm_out_file, begin_label); + write_modifier_bytes (type, decl_const, decl_volatile); + sprintf (ud_type_name, TYPE_NAME_FMT, TYPE_UID (root_type (type))); + ASM_OUTPUT_DWARF_REF (asm_out_file, ud_type_name); + ASM_OUTPUT_LABEL (asm_out_file, end_label); +} + +#ifdef USE_ORDERING_ATTRIBUTE +inline void +ordering_attribute (ordering) + register unsigned ordering; +{ + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_ordering); + ASM_OUTPUT_DWARF_DATA2 (asm_out_file, ordering); +} +#endif /* defined(USE_ORDERING_ATTRIBUTE) */ + +/* Note that the block of subscript information for an array type also + includes information about the element type of type given array type. */ + +static void +subscript_data_attribute (type) + register tree type; +{ + register unsigned dimension_number; + char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; + char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; + + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_subscr_data); + sprintf (begin_label, SS_BEGIN_LABEL_FMT, current_dienum); + sprintf (end_label, SS_END_LABEL_FMT, current_dienum); + ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label); + ASM_OUTPUT_LABEL (asm_out_file, begin_label); + + /* The GNU compilers represent multidimensional array types as sequences + of one dimensional array types whose element types are themselves array + types. Here we squish that down, so that each multidimensional array + type gets only one array_type DIE in the Dwarf debugging info. The + draft Dwarf specification say that we are allowed to do this kind + of compression in C (because there is no difference between an + array or arrays and a multidimensional array in C) but for other + source languages (e.g. Ada) we probably shouldn't do this. */ + + for (dimension_number = 0; + TREE_CODE (type) == ARRAY_TYPE; + type = TREE_TYPE (type), dimension_number++) + { + register tree domain = TYPE_DOMAIN (type); + + /* Arrays come in three flavors. Unspecified bounds, fixed + bounds, and (in GNU C only) variable bounds. Handle all + three forms here. */ + + if (domain) + { + /* We have an array type with specified bounds. */ + + register tree lower = TYPE_MIN_VALUE (domain); + register tree upper = TYPE_MAX_VALUE (domain); + + /* Handle only fundamental types as index types for now. */ + + if (! type_is_fundamental (domain)) + abort (); + + /* Output the representation format byte for this dimension. */ + + ASM_OUTPUT_DWARF_FMT_BYTE (asm_out_file, + FMT_CODE (1, + TREE_CODE (lower) == INTEGER_CST, + TREE_CODE (upper) == INTEGER_CST)); + + /* Output the index type for this dimension. */ + + ASM_OUTPUT_DWARF_FUND_TYPE (asm_out_file, + fundamental_type_code (domain)); + + /* Output the representation for the lower bound. */ + + output_bound_representation (lower, dimension_number, 'l'); + + /* Output the representation for the upper bound. */ + + output_bound_representation (upper, dimension_number, 'u'); + } + else + { + /* We have an array type with an unspecified length. For C and + C++ we can assume that this really means that (a) the index + type is an integral type, and (b) the lower bound is zero. + Note that Dwarf defines the representation of an unspecified + (upper) bound as being a zero-length location description. */ + + /* Output the array-bounds format byte. */ + + ASM_OUTPUT_DWARF_FMT_BYTE (asm_out_file, FMT_FT_C_X); + + /* Output the (assumed) index type. */ + + ASM_OUTPUT_DWARF_FUND_TYPE (asm_out_file, FT_integer); + + /* Output the (assumed) lower bound (constant) value. */ + + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0); + + /* Output the (empty) location description for the upper bound. */ + + ASM_OUTPUT_DWARF_DATA2 (asm_out_file, 0); + } + } + + /* Output the prefix byte that says that the element type is comming up. */ + + ASM_OUTPUT_DWARF_FMT_BYTE (asm_out_file, FMT_ET); + + /* Output a representation of the type of the elements of this array type. */ + + type_attribute (type, 0, 0); + + ASM_OUTPUT_LABEL (asm_out_file, end_label); +} + +static void +byte_size_attribute (tree_node) + register tree tree_node; +{ + register unsigned size; + + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_byte_size); + switch (TREE_CODE (tree_node)) + { + case ERROR_MARK: + size = 0; + break; + + case ENUMERAL_TYPE: + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + size = int_size_in_bytes (tree_node); + break; + + case FIELD_DECL: + /* For a data member of a struct or union, the AT_byte_size is + generally given as the number of bytes normally allocated for + an object of the *declared* type of the member itself. This + is true even for bit-fields. */ + size = simple_type_size_in_bits (field_type (tree_node)) + / BITS_PER_UNIT; + break; + + default: + abort (); + } + + /* Note that `size' might be -1 when we get to this point. If it + is, that indicates that the byte size of the entity in question + is variable. We have no good way of expressing this fact in Dwarf + at the present time, so just let the -1 pass on through. */ + + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, size); +} + +/* For a FIELD_DECL node which represents a bit-field, output an attribute + which specifies the distance in bits from the highest order bit of the + "containing object" for the bit-field to the highest order bit of the + bit-field itself. + + For any given bit-field, the "containing object" is a hypothetical + object (of some integral or enum type) within which the given bit-field + lives. The type of this hypothetical "containing object" is always the + same as the declared type of the individual bit-field itself. + + The determination of the exact location of the "containing object" for + a bit-field is rather complicated. It's handled by the `field_byte_offset' + function (above). + + Note that it is the size (in bytes) of the hypothetical "containing + object" which will be given in the AT_byte_size attribute for this + bit-field. (See `byte_size_attribute' above.) +*/ + +inline void +bit_offset_attribute (decl) + register tree decl; +{ + register unsigned object_offset_in_bytes = field_byte_offset (decl); + register tree type = DECL_BIT_FIELD_TYPE (decl); + register tree bitpos_tree = DECL_FIELD_BITPOS (decl); + register unsigned bitpos_int; + register unsigned highest_order_object_bit_offset; + register unsigned highest_order_field_bit_offset; + register unsigned bit_offset; + + assert (TREE_CODE (decl) == FIELD_DECL); /* Must be a field. */ + assert (type); /* Must be a bit field. */ + + /* We can't yet handle bit-fields whose offsets are variable, so if we + encounter such things, just return without generating any attribute + whatsoever. */ + + if (TREE_CODE (bitpos_tree) != INTEGER_CST) + return; + bitpos_int = (unsigned) TREE_INT_CST_LOW (bitpos_tree); + + /* Note that the bit offset is always the distance (in bits) from the + highest-order bit of the "containing object" to the highest-order + bit of the bit-field itself. Since the "high-order end" of any + object or field is different on big-endian and little-endian machines, + the computation below must take account of these differences. */ + + highest_order_object_bit_offset = object_offset_in_bytes * BITS_PER_UNIT; + highest_order_field_bit_offset = bitpos_int; + +#if (BYTES_BIG_ENDIAN == 0) + highest_order_field_bit_offset + += (unsigned) TREE_INT_CST_LOW (DECL_SIZE (decl)); + + highest_order_object_bit_offset += simple_type_size_in_bits (type); +#endif /* (BYTES_BIG_ENDIAN == 0) */ + + bit_offset = +#if (BYTES_BIG_ENDIAN == 0) + highest_order_object_bit_offset - highest_order_field_bit_offset; +#else /* (BYTES_BIG_ENDIAN != 0) */ + highest_order_field_bit_offset - highest_order_object_bit_offset; +#endif /* (BYTES_BIG_ENDIAN != 0) */ + + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_bit_offset); + ASM_OUTPUT_DWARF_DATA2 (asm_out_file, bit_offset); +} + +/* For a FIELD_DECL node which represents a bit field, output an attribute + which specifies the length in bits of the given field. */ + +inline void +bit_size_attribute (decl) + register tree decl; +{ + assert (TREE_CODE (decl) == FIELD_DECL); /* Must be a field. */ + assert (DECL_BIT_FIELD_TYPE (decl)); /* Must be a bit field. */ + + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_bit_size); + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, + (unsigned) TREE_INT_CST_LOW (DECL_SIZE (decl))); +} + +/* The following routine outputs the `element_list' attribute for enumeration + type DIEs. The element_lits attribute includes the names and values of + all of the enumeration constants associated with the given enumeration + type. */ + +inline void +element_list_attribute (element) + register tree element; +{ + char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; + char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; + + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_element_list); + sprintf (begin_label, EE_BEGIN_LABEL_FMT, current_dienum); + sprintf (end_label, EE_END_LABEL_FMT, current_dienum); + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, end_label, begin_label); + ASM_OUTPUT_LABEL (asm_out_file, begin_label); + + /* Here we output a list of value/name pairs for each enumeration constant + defined for this enumeration type (as required), but we do it in REVERSE + order. The order is the one required by the draft #5 Dwarf specification + published by the UI/PLSIG. */ + + output_enumeral_list (element); /* Recursively output the whole list. */ + + ASM_OUTPUT_LABEL (asm_out_file, end_label); +} + +/* Generate an AT_stmt_list attribute. These are normally present only in + DIEs with a TAG_compile_unit tag. */ + +inline void +stmt_list_attribute (label) + register char *label; +{ + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_stmt_list); + /* Don't use ASM_OUTPUT_DWARF_DATA4 here. */ + ASM_OUTPUT_DWARF_ADDR (asm_out_file, label); +} + +/* Generate an AT_low_pc attribute for a label DIE, a lexical_block DIE or + for a subroutine DIE. */ + +inline void +low_pc_attribute (asm_low_label) + register char *asm_low_label; +{ + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_low_pc); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, asm_low_label); +} + +/* Generate an AT_high_pc attribute for a lexical_block DIE or for a + subroutine DIE. */ + +inline void +high_pc_attribute (asm_high_label) + register char *asm_high_label; +{ + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_high_pc); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, asm_high_label); +} + +/* Generate an AT_body_begin attribute for a subroutine DIE. */ + +inline void +body_begin_attribute (asm_begin_label) + register char *asm_begin_label; +{ + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_body_begin); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, asm_begin_label); +} + +/* Generate an AT_body_end attribute for a subroutine DIE. */ + +inline void +body_end_attribute (asm_end_label) + register char *asm_end_label; +{ + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_body_end); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, asm_end_label); +} + +/* Generate an AT_language attribute given a LANG value. These attributes + are used only within TAG_compile_unit DIEs. */ + +inline void +language_attribute (language_code) + register unsigned language_code; +{ + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_language); + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, language_code); +} + +inline void +member_attribute (context) + register tree context; +{ + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + /* Generate this attribute only for members in C++. */ + + if (context != NULL && is_tagged_type (context)) + { + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_member); + sprintf (label, TYPE_NAME_FMT, TYPE_UID (context)); + ASM_OUTPUT_DWARF_REF (asm_out_file, label); + } +} + +inline void +string_length_attribute (upper_bound) + register tree upper_bound; +{ + char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; + char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; + + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_string_length); + sprintf (begin_label, SL_BEGIN_LABEL_FMT, current_dienum); + sprintf (end_label, SL_END_LABEL_FMT, current_dienum); + ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label); + ASM_OUTPUT_LABEL (asm_out_file, begin_label); + output_bound_representation (upper_bound, 0, 'u'); + ASM_OUTPUT_LABEL (asm_out_file, end_label); +} + +inline void +comp_dir_attribute (dirname) + register char *dirname; +{ + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_comp_dir); + ASM_OUTPUT_DWARF_STRING (asm_out_file, dirname); +} + +inline void +sf_names_attribute (sf_names_start_label) + register char *sf_names_start_label; +{ + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_sf_names); + /* Don't use ASM_OUTPUT_DWARF_DATA4 here. */ + ASM_OUTPUT_DWARF_ADDR (asm_out_file, sf_names_start_label); +} + +inline void +src_info_attribute (src_info_start_label) + register char *src_info_start_label; +{ + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_src_info); + /* Don't use ASM_OUTPUT_DWARF_DATA4 here. */ + ASM_OUTPUT_DWARF_ADDR (asm_out_file, src_info_start_label); +} + +inline void +mac_info_attribute (mac_info_start_label) + register char *mac_info_start_label; +{ + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_mac_info); + /* Don't use ASM_OUTPUT_DWARF_DATA4 here. */ + ASM_OUTPUT_DWARF_ADDR (asm_out_file, mac_info_start_label); +} + +inline void +prototyped_attribute (func_type) + register tree func_type; +{ + if ((strcmp (language_string, "GNU C") == 0) + && (TYPE_ARG_TYPES (func_type) != NULL)) + { + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_prototyped); + ASM_OUTPUT_DWARF_STRING (asm_out_file, ""); + } +} + +inline void +producer_attribute (producer) + register char *producer; +{ + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_producer); + ASM_OUTPUT_DWARF_STRING (asm_out_file, producer); +} + +inline void +inline_attribute (decl) + register tree decl; +{ + if (DECL_INLINE (decl)) + { + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_inline); + ASM_OUTPUT_DWARF_STRING (asm_out_file, ""); + } +} + +inline void +containing_type_attribute (containing_type) + register tree containing_type; +{ + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_containing_type); + sprintf (label, TYPE_NAME_FMT, TYPE_UID (containing_type)); + ASM_OUTPUT_DWARF_REF (asm_out_file, label); +} + +inline void +abstract_origin_attribute (origin) + register tree origin; +{ + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_abstract_origin); + switch (TREE_CODE_CLASS (TREE_CODE (origin))) + { + case 'd': + sprintf (label, DECL_NAME_FMT, DECL_UID (origin)); + break; + + case 't': + sprintf (label, TYPE_NAME_FMT, TYPE_UID (origin)); + break; + + default: + abort (); /* Should never happen. */ + + } + ASM_OUTPUT_DWARF_REF (asm_out_file, label); +} + +#ifdef DWARF_DECL_COORDINATES +inline void +src_coords_attribute (src_fileno, src_lineno) + register unsigned src_fileno; + register unsigned src_lineno; +{ + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_src_coords); + ASM_OUTPUT_DWARF_DATA2 (asm_out_file, src_fileno); + ASM_OUTPUT_DWARF_DATA2 (asm_out_file, src_lineno); +} +#endif /* defined(DWARF_DECL_COORDINATES) */ + +inline void +pure_or_virtual_attribute (func_decl) + register tree func_decl; +{ + if (DECL_VIRTUAL_P (func_decl)) + { +#if 0 /* DECL_ABSTRACT_VIRTUAL_P is C++-specific. */ + if (DECL_ABSTRACT_VIRTUAL_P (func_decl)) + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_pure_virtual); + else +#endif + ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_virtual); + ASM_OUTPUT_DWARF_STRING (asm_out_file, ""); + } +} + +/************************* end of attributes *****************************/ + +/********************* utility routines for DIEs *************************/ + +/* Output an AT_name attribute and an AT_src_coords attribute for the + given decl, but only if it actually has a name. */ + +static void +name_and_src_coords_attributes (decl) + register tree decl; +{ + register tree decl_name = DECL_NAME (decl); + + if (decl_name && IDENTIFIER_POINTER (decl_name)) + { + name_attribute (IDENTIFIER_POINTER (decl_name)); +#ifdef DWARF_DECL_COORDINATES + { + register unsigned file_index; + + /* This is annoying, but we have to pop out of the .debug section + for a moment while we call `lookup_filename' because calling it + may cause a temporary switch into the .debug_sfnames section and + most svr4 assemblers are not smart enough be be able to nest + section switches to any depth greater than one. Note that we + also can't skirt this issue by delaying all output to the + .debug_sfnames section unit the end of compilation because that + would cause us to have inter-section forward references and + Fred Fish sez that m68k/svr4 assemblers botch those. */ + + ASM_OUTPUT_POP_SECTION (asm_out_file); + file_index = lookup_filename (DECL_SOURCE_FILE (decl)); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, DEBUG_SECTION); + + src_coords_attribute (file_index, DECL_SOURCE_LINE (decl)); + } +#endif /* defined(DWARF_DECL_COORDINATES) */ + } +} + +/* Many forms of DIEs contain a "type description" part. The following + routine writes out these "type descriptor" parts. */ + +static void +type_attribute (type, decl_const, decl_volatile) + register tree type; + register int decl_const; + register int decl_volatile; +{ + register enum tree_code code = TREE_CODE (type); + register int root_type_modified; + + if (TREE_CODE (type) == ERROR_MARK) + return; + + /* Handle a special case. For functions whose return type is void, + we generate *no* type attribute. (Note that no object may have + type `void', so this only applies to function return types. */ + + if (TREE_CODE (type) == VOID_TYPE) + return; + + root_type_modified = (code == POINTER_TYPE || code == REFERENCE_TYPE + || decl_const || decl_volatile + || TYPE_READONLY (type) || TYPE_VOLATILE (type)); + + if (type_is_fundamental (root_type (type))) + if (root_type_modified) + mod_fund_type_attribute (type, decl_const, decl_volatile); + else + fund_type_attribute (fundamental_type_code (type)); + else + if (root_type_modified) + mod_u_d_type_attribute (type, decl_const, decl_volatile); + else + /* We have to get the type_main_variant here (and pass that to the + `user_def_type_attribute' routine) because the ..._TYPE node we + have might simply be a *copy* of some original type node (where + the copy was created to help us keep track of typedef names) + and that copy might have a different TYPE_UID from the original + ..._TYPE node. (Note that when `equate_type_number_to_die_number' + is labeling a given type DIE for future reference, it always and + only creates labels for DIEs representing *main variants*, and it + never even knows about non-main-variants.) */ + user_def_type_attribute (type_main_variant (type)); +} + +/* Given a tree pointer to a struct, class, union, or enum type node, return + a pointer to the (string) tag name for the given type, or zero if the + type was declared without a tag. */ + +static char * +type_tag (type) + register tree type; +{ + register char *name = 0; + + if (TYPE_NAME (type) != 0) + { + register tree t = 0; + + /* Find the IDENTIFIER_NODE for the type name. */ + if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) + t = TYPE_NAME (type); +#if 0 + /* The g++ front end makes the TYPE_NAME of *each* tagged type point + to a TYPE_DECL node, regardless of whether or not a `typedef' was + involved. This is distinctly different from what the gcc front-end + does. It always makes the TYPE_NAME for each tagged type be either + NULL (signifying an anonymous tagged type) or else a pointer to an + IDENTIFIER_NODE. Obviously, we would like to generate correct Dwarf + for both C and C++, but given this inconsistency in the TREE + representation of tagged types for C and C++ in the GNU front-ends, + we cannot support both languages correctly unless we introduce some + front-end specific code here, and rms objects to that, so we can + only generate correct Dwarf for one of these two languages. C is + more important, so for now we'll do the right thing for C and let + g++ go fish. */ + + else + if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL) + t = DECL_NAME (TYPE_NAME (type)); +#endif + /* Now get the name as a string, or invent one. */ + if (t != 0) + name = IDENTIFIER_POINTER (t); + } + + return (name == 0 || *name == '\0') ? 0 : name; +} + +inline void +dienum_push () +{ + /* Start by checking if the pending_sibling_stack needs to be expanded. + If necessary, expand it. */ + + if (pending_siblings == pending_siblings_allocated) + { + pending_siblings_allocated += PENDING_SIBLINGS_INCREMENT; + pending_sibling_stack + = (unsigned *) xrealloc (pending_sibling_stack, + pending_siblings_allocated * sizeof(unsigned)); + } + + pending_siblings++; + NEXT_DIE_NUM = next_unused_dienum++; +} + +/* Pop the sibling stack so that the most recently pushed DIEnum becomes the + NEXT_DIE_NUM. */ + +inline void +dienum_pop () +{ + pending_siblings--; +} + +inline tree +member_declared_type (member) + register tree member; +{ + return (DECL_BIT_FIELD_TYPE (member)) + ? DECL_BIT_FIELD_TYPE (member) + : TREE_TYPE (member); +} + +/******************************* DIEs ************************************/ + +/* Output routines for individual types of DIEs. */ + +/* Note that every type of DIE (except a null DIE) gets a sibling. */ + +static void +output_array_type_die (arg) + register void *arg; +{ + register tree type = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_array_type); + sibling_attribute (); + equate_type_number_to_die_number (type); + member_attribute (TYPE_CONTEXT (type)); + + /* I believe that we can default the array ordering. SDB will probably + do the right things even if AT_ordering is not present. It's not + even an issue until we start to get into multidimensional arrays + anyway. If SDB is ever caught doing the Wrong Thing for multi- + dimensional arrays, then we'll have to put the AT_ordering attribute + back in. (But if and when we find out that we need to put these in, + we will only do so for multidimensional arrays. After all, we don't + want to waste space in the .debug section now do we?) */ + +#ifdef USE_ORDERING_ATTRIBUTE + ordering_attribute (ORD_row_major); +#endif /* defined(USE_ORDERING_ATTRIBUTE) */ + + subscript_data_attribute (type); +} + +static void +output_set_type_die (arg) + register void *arg; +{ + register tree type = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_set_type); + sibling_attribute (); + equate_type_number_to_die_number (type); + member_attribute (TYPE_CONTEXT (type)); + type_attribute (TREE_TYPE (type), 0, 0); +} + +#if 0 +/* Implement this when there is a GNU FORTRAN or GNU Ada front end. */ +static void +output_entry_point_die (arg) + register void *arg; +{ + register tree decl = arg; + register tree origin = decl_ultimate_origin (decl); + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_entry_point); + sibling_attribute (); + dienum_push (); + if (origin != NULL) + abstract_origin_attribute (origin); + else + { + name_and_src_coords_attributes (decl); + member_attribute (DECL_CONTEXT (decl)); + type_attribute (TREE_TYPE (TREE_TYPE (decl)), 0, 0); + } + if (DECL_ABSTRACT (decl)) + equate_decl_number_to_die_number (decl); + else + low_pc_attribute (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); +} +#endif + +/* Output a DIE to represent an inlined instance of an enumeration type. */ + +static void +output_inlined_enumeration_type_die (arg) + register void *arg; +{ + register tree type = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_enumeration_type); + sibling_attribute (); + assert (TREE_ASM_WRITTEN (type)); + abstract_origin_attribute (type); +} + +/* Output a DIE to represent an inlined instance of a structure type. */ + +static void +output_inlined_structure_type_die (arg) + register void *arg; +{ + register tree type = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_structure_type); + sibling_attribute (); + assert (TREE_ASM_WRITTEN (type)); + abstract_origin_attribute (type); +} + +/* Output a DIE to represent an inlined instance of a union type. */ + +static void +output_inlined_union_type_die (arg) + register void *arg; +{ + register tree type = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_union_type); + sibling_attribute (); + assert (TREE_ASM_WRITTEN (type)); + abstract_origin_attribute (type); +} + +/* Output a DIE to represent an enumeration type. Note that these DIEs + include all of the information about the enumeration values also. + This information is encoded into the element_list attribute. */ + +static void +output_enumeration_type_die (arg) + register void *arg; +{ + register tree type = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_enumeration_type); + sibling_attribute (); + equate_type_number_to_die_number (type); + name_attribute (type_tag (type)); + member_attribute (TYPE_CONTEXT (type)); + + /* Handle a GNU C/C++ extension, i.e. incomplete enum types. If the + given enum type is incomplete, do not generate the AT_byte_size + attribute or the AT_element_list attribute. */ + + if (TYPE_SIZE (type)) + { + byte_size_attribute (type); + element_list_attribute (TYPE_FIELDS (type)); + } +} + +/* Output a DIE to represent either a real live formal parameter decl or + to represent just the type of some formal parameter position in some + function type. + + Note that this routine is a bit unusual because its argument may be + a ..._DECL node (i.e. either a PARM_DECL or perhaps a VAR_DECL which + represents an inlining of some PARM_DECL) or else some sort of a + ..._TYPE node. If it's the former then this function is being called + to output a DIE to represent a formal parameter object (or some inlining + thereof). If it's the latter, then this function is only being called + to output a TAG_formal_parameter DIE to stand as a placeholder for some + formal argument type of some subprogram type. */ + +static void +output_formal_parameter_die (arg) + register void *arg; +{ + register tree node = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_formal_parameter); + sibling_attribute (); + + switch (TREE_CODE_CLASS (TREE_CODE (node))) + { + case 'd': /* We were called with some kind of a ..._DECL node. */ + { + register tree origin = decl_ultimate_origin (node); + + if (origin != NULL) + abstract_origin_attribute (origin); + else + { + name_and_src_coords_attributes (node); + type_attribute (TREE_TYPE (node), + TREE_READONLY (node), TREE_THIS_VOLATILE (node)); + } + if (DECL_ABSTRACT (node)) + equate_decl_number_to_die_number (node); + else + location_or_const_value_attribute (node); + } + break; + + case 't': /* We were called with some kind of a ..._TYPE node. */ + type_attribute (node, 0, 0); + break; + + default: + abort (); /* Should never happen. */ + } +} + +/* Output a DIE to represent a declared function (either file-scope + or block-local) which has "external linkage" (according to ANSI-C). */ + +static void +output_global_subroutine_die (arg) + register void *arg; +{ + register tree decl = arg; + register tree origin = decl_ultimate_origin (decl); + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_global_subroutine); + sibling_attribute (); + dienum_push (); + if (origin != NULL) + abstract_origin_attribute (origin); + else + { + register tree type = TREE_TYPE (decl); + + name_and_src_coords_attributes (decl); + inline_attribute (decl); + prototyped_attribute (type); + member_attribute (DECL_CONTEXT (decl)); + type_attribute (TREE_TYPE (type), 0, 0); + pure_or_virtual_attribute (decl); + } + if (DECL_ABSTRACT (decl)) + equate_decl_number_to_die_number (decl); + else + { + if (! DECL_EXTERNAL (decl)) + { + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + low_pc_attribute (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); + sprintf (label, FUNC_END_LABEL_FMT, current_funcdef_number); + high_pc_attribute (label); + sprintf (label, BODY_BEGIN_LABEL_FMT, current_funcdef_number); + body_begin_attribute (label); + sprintf (label, BODY_END_LABEL_FMT, current_funcdef_number); + body_end_attribute (label); + } + } +} + +/* Output a DIE to represent a declared data object (either file-scope + or block-local) which has "external linkage" (according to ANSI-C). */ + +static void +output_global_variable_die (arg) + register void *arg; +{ + register tree decl = arg; + register tree origin = decl_ultimate_origin (decl); + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_global_variable); + sibling_attribute (); + if (origin != NULL) + abstract_origin_attribute (origin); + else + { + name_and_src_coords_attributes (decl); + member_attribute (DECL_CONTEXT (decl)); + type_attribute (TREE_TYPE (decl), + TREE_READONLY (decl), TREE_THIS_VOLATILE (decl)); + } + if (DECL_ABSTRACT (decl)) + equate_decl_number_to_die_number (decl); + else + { + if (!DECL_EXTERNAL (decl)) + location_or_const_value_attribute (decl); + } +} + +static void +output_label_die (arg) + register void *arg; +{ + register tree decl = arg; + register tree origin = decl_ultimate_origin (decl); + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_label); + sibling_attribute (); + if (origin != NULL) + abstract_origin_attribute (origin); + else + name_and_src_coords_attributes (decl); + if (DECL_ABSTRACT (decl)) + equate_decl_number_to_die_number (decl); + else + { + register rtx insn = DECL_RTL (decl); + + if (GET_CODE (insn) == CODE_LABEL) + { + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + /* When optimization is enabled (via -O) some parts of the compiler + (e.g. jump.c and cse.c) may try to delete CODE_LABEL insns which + represent source-level labels which were explicitly declared by + the user. This really shouldn't be happening though, so catch + it if it ever does happen. */ + + if (INSN_DELETED_P (insn)) + abort (); /* Should never happen. */ + + sprintf (label, INSN_LABEL_FMT, current_funcdef_number, + (unsigned) INSN_UID (insn)); + low_pc_attribute (label); + } + } +} + +static void +output_lexical_block_die (arg) + register void *arg; +{ + register tree stmt = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_lexical_block); + sibling_attribute (); + dienum_push (); + if (! BLOCK_ABSTRACT (stmt)) + { + char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; + char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; + + sprintf (begin_label, BLOCK_BEGIN_LABEL_FMT, next_block_number); + low_pc_attribute (begin_label); + sprintf (end_label, BLOCK_END_LABEL_FMT, next_block_number); + high_pc_attribute (end_label); + } +} + +static void +output_inlined_subroutine_die (arg) + register void *arg; +{ + register tree stmt = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_inlined_subroutine); + sibling_attribute (); + dienum_push (); + abstract_origin_attribute (block_ultimate_origin (stmt)); + if (! BLOCK_ABSTRACT (stmt)) + { + char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; + char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; + + sprintf (begin_label, BLOCK_BEGIN_LABEL_FMT, next_block_number); + low_pc_attribute (begin_label); + sprintf (end_label, BLOCK_END_LABEL_FMT, next_block_number); + high_pc_attribute (end_label); + } +} + +/* Output a DIE to represent a declared data object (either file-scope + or block-local) which has "internal linkage" (according to ANSI-C). */ + +static void +output_local_variable_die (arg) + register void *arg; +{ + register tree decl = arg; + register tree origin = decl_ultimate_origin (decl); + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_local_variable); + sibling_attribute (); + if (origin != NULL) + abstract_origin_attribute (origin); + else + { + name_and_src_coords_attributes (decl); + member_attribute (DECL_CONTEXT (decl)); + type_attribute (TREE_TYPE (decl), + TREE_READONLY (decl), TREE_THIS_VOLATILE (decl)); + } + if (DECL_ABSTRACT (decl)) + equate_decl_number_to_die_number (decl); + else + location_or_const_value_attribute (decl); +} + +static void +output_member_die (arg) + register void *arg; +{ + register tree decl = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_member); + sibling_attribute (); + name_and_src_coords_attributes (decl); + member_attribute (DECL_CONTEXT (decl)); + type_attribute (member_declared_type (decl), + TREE_READONLY (decl), TREE_THIS_VOLATILE (decl)); + if (DECL_BIT_FIELD_TYPE (decl)) /* If this is a bit field... */ + { + byte_size_attribute (decl); + bit_size_attribute (decl); + bit_offset_attribute (decl); + } + data_member_location_attribute (decl); +} + +#if 0 +/* Don't generate either pointer_type DIEs or reference_type DIEs. Use + modified types instead. + + We keep this code here just in case these types of DIEs may be needed + to represent certain things in other languages (e.g. Pascal) someday. +*/ + +static void +output_pointer_type_die (arg) + register void *arg; +{ + register tree type = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_pointer_type); + sibling_attribute (); + equate_type_number_to_die_number (type); + member_attribute (TYPE_CONTEXT (type)); + type_attribute (TREE_TYPE (type), 0, 0); +} + +static void +output_reference_type_die (arg) + register void *arg; +{ + register tree type = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_reference_type); + sibling_attribute (); + equate_type_number_to_die_number (type); + member_attribute (TYPE_CONTEXT (type)); + type_attribute (TREE_TYPE (type), 0, 0); +} +#endif + +static void +output_ptr_to_mbr_type_die (arg) + register void *arg; +{ + register tree type = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_ptr_to_member_type); + sibling_attribute (); + equate_type_number_to_die_number (type); + member_attribute (TYPE_CONTEXT (type)); + containing_type_attribute (TYPE_OFFSET_BASETYPE (type)); + type_attribute (TREE_TYPE (type), 0, 0); +} + +static void +output_compile_unit_die (arg) + register void *arg; +{ + register char *main_input_filename = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_compile_unit); + sibling_attribute (); + dienum_push (); + name_attribute (main_input_filename); + + { + char producer[250]; + + sprintf (producer, "%s %s", language_string, version_string); + producer_attribute (producer); + } + + if (strcmp (language_string, "GNU C++") == 0) + language_attribute (LANG_C_PLUS_PLUS); + else if (flag_traditional) + language_attribute (LANG_C); + else + language_attribute (LANG_C89); + low_pc_attribute (TEXT_BEGIN_LABEL); + high_pc_attribute (TEXT_END_LABEL); + if (debug_info_level >= DINFO_LEVEL_NORMAL) + stmt_list_attribute (LINE_BEGIN_LABEL); + last_filename = xstrdup (main_input_filename); + + { + char *wd = getpwd (); + if (wd) + comp_dir_attribute (wd); + } + + if (debug_info_level >= DINFO_LEVEL_NORMAL) + { + sf_names_attribute (SFNAMES_BEGIN_LABEL); + src_info_attribute (SRCINFO_BEGIN_LABEL); + if (debug_info_level >= DINFO_LEVEL_VERBOSE) + mac_info_attribute (MACINFO_BEGIN_LABEL); + } +} + +static void +output_string_type_die (arg) + register void *arg; +{ + register tree type = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_string_type); + sibling_attribute (); + member_attribute (TYPE_CONTEXT (type)); + + /* Fudge the string length attribute for now. */ + + string_length_attribute (TYPE_MAX_VALUE (TYPE_DOMAIN (type))); +} + +static void +output_structure_type_die (arg) + register void *arg; +{ + register tree type = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_structure_type); + sibling_attribute (); + equate_type_number_to_die_number (type); + name_attribute (type_tag (type)); + member_attribute (TYPE_CONTEXT (type)); + + /* If this type has been completed, then give it a byte_size attribute + and prepare to give a list of members. Otherwise, don't do either of + these things. In the latter case, we will not be generating a list + of members (since we don't have any idea what they might be for an + incomplete type). */ + + if (TYPE_SIZE (type)) + { + dienum_push (); + byte_size_attribute (type); + } +} + +/* Output a DIE to represent a declared function (either file-scope + or block-local) which has "internal linkage" (according to ANSI-C). */ + +static void +output_local_subroutine_die (arg) + register void *arg; +{ + register tree decl = arg; + register tree origin = decl_ultimate_origin (decl); + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_subroutine); + sibling_attribute (); + dienum_push (); + if (origin != NULL) + abstract_origin_attribute (origin); + else + { + register tree type = TREE_TYPE (decl); + + name_and_src_coords_attributes (decl); + inline_attribute (decl); + prototyped_attribute (type); + member_attribute (DECL_CONTEXT (decl)); + type_attribute (TREE_TYPE (type), 0, 0); + pure_or_virtual_attribute (decl); + } + if (DECL_ABSTRACT (decl)) + equate_decl_number_to_die_number (decl); + else + { + /* Avoid getting screwed up in cases where a function was declared + static but where no definition was ever given for it. */ + + if (TREE_ASM_WRITTEN (decl)) + { + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + low_pc_attribute (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); + sprintf (label, FUNC_END_LABEL_FMT, current_funcdef_number); + high_pc_attribute (label); + sprintf (label, BODY_BEGIN_LABEL_FMT, current_funcdef_number); + body_begin_attribute (label); + sprintf (label, BODY_END_LABEL_FMT, current_funcdef_number); + body_end_attribute (label); + } + } +} + +static void +output_subroutine_type_die (arg) + register void *arg; +{ + register tree type = arg; + register tree return_type = TREE_TYPE (type); + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_subroutine_type); + sibling_attribute (); + dienum_push (); + equate_type_number_to_die_number (type); + prototyped_attribute (type); + member_attribute (TYPE_CONTEXT (type)); + type_attribute (return_type, 0, 0); +} + +static void +output_typedef_die (arg) + register void *arg; +{ + register tree decl = arg; + register tree origin = decl_ultimate_origin (decl); + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_typedef); + sibling_attribute (); + if (origin != NULL) + abstract_origin_attribute (origin); + else + { + name_and_src_coords_attributes (decl); + member_attribute (DECL_CONTEXT (decl)); + type_attribute (TREE_TYPE (decl), + TREE_READONLY (decl), TREE_THIS_VOLATILE (decl)); + } + if (DECL_ABSTRACT (decl)) + equate_decl_number_to_die_number (decl); +} + +static void +output_union_type_die (arg) + register void *arg; +{ + register tree type = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_union_type); + sibling_attribute (); + equate_type_number_to_die_number (type); + name_attribute (type_tag (type)); + member_attribute (TYPE_CONTEXT (type)); + + /* If this type has been completed, then give it a byte_size attribute + and prepare to give a list of members. Otherwise, don't do either of + these things. In the latter case, we will not be generating a list + of members (since we don't have any idea what they might be for an + incomplete type). */ + + if (TYPE_SIZE (type)) + { + dienum_push (); + byte_size_attribute (type); + } +} + +/* Generate a special type of DIE used as a stand-in for a trailing ellipsis + at the end of an (ANSI prototyped) formal parameters list. */ + +static void +output_unspecified_parameters_die (arg) + register void *arg; +{ + register tree decl_or_type = arg; + + ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_unspecified_parameters); + sibling_attribute (); + + /* This kludge is here only for the sake of being compatible with what + the USL CI5 C compiler does. The specification of Dwarf Version 1 + doesn't say that TAG_unspecified_parameters DIEs should contain any + attributes other than the AT_sibling attribute, but they are certainly + allowed to contain additional attributes, and the CI5 compiler + generates AT_name, AT_fund_type, and AT_location attributes within + TAG_unspecified_parameters DIEs which appear in the child lists for + DIEs representing function definitions, so we do likewise here. */ + + if (TREE_CODE (decl_or_type) == FUNCTION_DECL && DECL_INITIAL (decl_or_type)) + { + name_attribute ("..."); + fund_type_attribute (FT_pointer); + /* location_attribute (?); */ + } +} + +static void +output_padded_null_die (arg) + register void *arg; +{ + ASM_OUTPUT_ALIGN (asm_out_file, 2); /* 2**2 == 4 */ +} + +/*************************** end of DIEs *********************************/ + +/* Generate some type of DIE. This routine generates the generic outer + wrapper stuff which goes around all types of DIE's (regardless of their + TAGs. All forms of DIEs start with a DIE-specific label, followed by a + DIE-length word, followed by the guts of the DIE itself. After the guts + of the DIE, there must always be a terminator label for the DIE. */ + +static void +output_die (die_specific_output_function, param) + register void (*die_specific_output_function)(); + register void *param; +{ + char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; + char end_label[MAX_ARTIFICIAL_LABEL_BYTES]; + + current_dienum = NEXT_DIE_NUM; + NEXT_DIE_NUM = next_unused_dienum; + + sprintf (begin_label, DIE_BEGIN_LABEL_FMT, current_dienum); + sprintf (end_label, DIE_END_LABEL_FMT, current_dienum); + + /* Write a label which will act as the name for the start of this DIE. */ + + ASM_OUTPUT_LABEL (asm_out_file, begin_label); + + /* Write the DIE-length word. */ + + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, end_label, begin_label); + + /* Fill in the guts of the DIE. */ + + next_unused_dienum++; + die_specific_output_function (param); + + /* Write a label which will act as the name for the end of this DIE. */ + + ASM_OUTPUT_LABEL (asm_out_file, end_label); +} + +static void +end_sibling_chain () +{ + char begin_label[MAX_ARTIFICIAL_LABEL_BYTES]; + + current_dienum = NEXT_DIE_NUM; + NEXT_DIE_NUM = next_unused_dienum; + + sprintf (begin_label, DIE_BEGIN_LABEL_FMT, current_dienum); + + /* Write a label which will act as the name for the start of this DIE. */ + + ASM_OUTPUT_LABEL (asm_out_file, begin_label); + + /* Write the DIE-length word. */ + + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 4); + + dienum_pop (); +} + +/* Generate a list of nameless TAG_formal_parameter DIEs (and perhaps a + TAG_unspecified_parameters DIE) to represent the types of the formal + parameters as specified in some function type specification (except + for those which appear as part of a function *definition*). + + Note that we must be careful here to output all of the parameter DIEs + *before* we output any DIEs needed to represent the types of the formal + parameters. This keeps svr4 SDB happy because it (incorrectly) thinks + that the first non-parameter DIE it sees ends the formal parameter list. +*/ + +static void +output_formal_types (function_or_method_type) + register tree function_or_method_type; +{ + register tree link; + register tree formal_type = NULL; + register tree first_parm_type = TYPE_ARG_TYPES (function_or_method_type); + + /* In the case where we are generating a formal types list for a C++ + non-static member function type, skip over the first thing on the + TYPE_ARG_TYPES list because it only represents the type of the + hidden `this pointer'. The debugger should be able to figure + out (without being explicitly told) that this non-static member + function type takes a `this pointer' and should be able to figure + what the type of that hidden parameter is from the AT_member + attribute of the parent TAG_subroutine_type DIE. */ + + if (TREE_CODE (function_or_method_type) == METHOD_TYPE) + first_parm_type = TREE_CHAIN (first_parm_type); + + /* Make our first pass over the list of formal parameter types and output + a TAG_formal_parameter DIE for each one. */ + + for (link = first_parm_type; link; link = TREE_CHAIN (link)) + { + formal_type = TREE_VALUE (link); + if (formal_type == void_type_node) + break; + + /* Output a (nameless) DIE to represent the formal parameter itself. */ + + output_die (output_formal_parameter_die, formal_type); + } + + /* If this function type has an ellipsis, add a TAG_unspecified_parameters + DIE to the end of the parameter list. */ + + if (formal_type != void_type_node) + output_die (output_unspecified_parameters_die, function_or_method_type); + + /* Make our second (and final) pass over the list of formal parameter types + and output DIEs to represent those types (as necessary). */ + + for (link = TYPE_ARG_TYPES (function_or_method_type); + link; + link = TREE_CHAIN (link)) + { + formal_type = TREE_VALUE (link); + if (formal_type == void_type_node) + break; + + output_type (formal_type, function_or_method_type); + } +} + +/* Remember a type in the pending_types_list. */ + +static void +pend_type (type) + register tree type; +{ + if (pending_types == pending_types_allocated) + { + pending_types_allocated += PENDING_TYPES_INCREMENT; + pending_types_list + = (tree *) xrealloc (pending_types_list, + sizeof (tree) * pending_types_allocated); + } + pending_types_list[pending_types++] = type; + + /* Mark the pending type as having been output already (even though + it hasn't been). This prevents the type from being added to the + pending_types_list more than once. */ + + TREE_ASM_WRITTEN (type) = 1; +} + +/* Return non-zero if it is legitimate to output DIEs to represent a + given type while we are generating the list of child DIEs for some + DIE (e.g. a function or lexical block DIE) associated with a given scope. + + See the comments within the function for a description of when it is + considered legitimate to output DIEs for various kinds of types. + + Note that TYPE_CONTEXT(type) may be NULL (to indicate global scope) + or it may point to a BLOCK node (for types local to a block), or to a + FUNCTION_DECL node (for types local to the heading of some function + definition), or to a FUNCTION_TYPE node (for types local to the + prototyped parameter list of a function type specification), or to a + RECORD_TYPE, UNION_TYPE, or QUAL_UNION_TYPE node + (in the case of C++ nested types). + + The `scope' parameter should likewise be NULL or should point to a + BLOCK node, a FUNCTION_DECL node, a FUNCTION_TYPE node, a RECORD_TYPE + node, a UNION_TYPE node, or a QUAL_UNION_TYPE node. + + This function is used only for deciding when to "pend" and when to + "un-pend" types to/from the pending_types_list. + + Note that we sometimes make use of this "type pending" feature in a + rather twisted way to temporarily delay the production of DIEs for the + types of formal parameters. (We do this just to make svr4 SDB happy.) + It order to delay the production of DIEs representing types of formal + parameters, callers of this function supply `fake_containing_scope' as + the `scope' parameter to this function. Given that fake_containing_scope + is a tagged type which is *not* the containing scope for *any* other type, + the desired effect is achieved, i.e. output of DIEs representing types + is temporarily suspended, and any type DIEs which would have otherwise + been output are instead placed onto the pending_types_list. Later on, + we force these (temporarily pended) types to be output simply by calling + `output_pending_types_for_scope' with an actual argument equal to the + true scope of the types we temporarily pended. +*/ + +inline int +type_ok_for_scope (type, scope) + register tree type; + register tree scope; +{ + /* Tagged types (i.e. struct, union, and enum types) must always be + output only in the scopes where they actually belong (or else the + scoping of their own tag names and the scoping of their member + names will be incorrect). Non-tagged-types on the other hand can + generally be output anywhere, except that svr4 SDB really doesn't + want to see them nested within struct or union types, so here we + say it is always OK to immediately output any such a (non-tagged) + type, so long as we are not within such a context. Note that the + only kinds of non-tagged types which we will be dealing with here + (for C and C++ anyway) will be array types and function types. */ + + return is_tagged_type (type) + ? (TYPE_CONTEXT (type) == scope) + : (scope == NULL_TREE || ! is_tagged_type (scope)); +} + +/* Output any pending types (from the pending_types list) which we can output + now (taking into account the scope that we are working on now). + + For each type output, remove the given type from the pending_types_list + *before* we try to output it. + + Note that we have to process the list in beginning-to-end order, + because the call made here to output_type may cause yet more types + to be added to the end of the list, and we may have to output some + of them too. +*/ + +static void +output_pending_types_for_scope (containing_scope) + register tree containing_scope; +{ + register unsigned i; + + for (i = 0; i < pending_types; ) + { + register tree type = pending_types_list[i]; + + if (type_ok_for_scope (type, containing_scope)) + { + register tree *mover; + register tree *limit; + + pending_types--; + limit = &pending_types_list[pending_types]; + for (mover = &pending_types_list[i]; mover < limit; mover++) + *mover = *(mover+1); + + /* Un-mark the type as having been output already (because it + hasn't been, really). Then call output_type to generate a + Dwarf representation of it. */ + + TREE_ASM_WRITTEN (type) = 0; + output_type (type, containing_scope); + + /* Don't increment the loop counter in this case because we + have shifted all of the subsequent pending types down one + element in the pending_types_list array. */ + } + else + i++; + } +} + +static void +output_type (type, containing_scope) + register tree type; + register tree containing_scope; +{ + if (type == 0 || type == error_mark_node) + return; + + /* We are going to output a DIE to represent the unqualified version of + of this type (i.e. without any const or volatile qualifiers) so get + the main variant (i.e. the unqualified version) of this type now. */ + + type = type_main_variant (type); + + if (TREE_ASM_WRITTEN (type)) + return; + + /* Don't generate any DIEs for this type now unless it is OK to do so + (based upon what `type_ok_for_scope' tells us). */ + + if (! type_ok_for_scope (type, containing_scope)) + { + pend_type (type); + return; + } + + switch (TREE_CODE (type)) + { + case ERROR_MARK: + break; + + case POINTER_TYPE: + case REFERENCE_TYPE: + /* For these types, all that is required is that we output a DIE + (or a set of DIEs) to represent the "basis" type. */ + output_type (TREE_TYPE (type), containing_scope); + break; + + case OFFSET_TYPE: + /* This code is used for C++ pointer-to-data-member types. */ + /* Output a description of the relevant class type. */ + output_type (TYPE_OFFSET_BASETYPE (type), containing_scope); + /* Output a description of the type of the object pointed to. */ + output_type (TREE_TYPE (type), containing_scope); + /* Now output a DIE to represent this pointer-to-data-member type + itself. */ + output_die (output_ptr_to_mbr_type_die, type); + break; + + case SET_TYPE: + output_type (TREE_TYPE (type), containing_scope); + output_die (output_set_type_die, type); + break; + + case FILE_TYPE: + output_type (TREE_TYPE (type), containing_scope); + abort (); /* No way to represent these in Dwarf yet! */ + break; + + case STRING_TYPE: + output_type (TREE_TYPE (type), containing_scope); + output_die (output_string_type_die, type); + break; + + case FUNCTION_TYPE: + /* Force out return type (in case it wasn't forced out already). */ + output_type (TREE_TYPE (type), containing_scope); + output_die (output_subroutine_type_die, type); + output_formal_types (type); + end_sibling_chain (); + break; + + case METHOD_TYPE: + /* Force out return type (in case it wasn't forced out already). */ + output_type (TREE_TYPE (type), containing_scope); + output_die (output_subroutine_type_die, type); + output_formal_types (type); + end_sibling_chain (); + break; + + case ARRAY_TYPE: + { + register tree element_type; + + element_type = TREE_TYPE (type); + while (TREE_CODE (element_type) == ARRAY_TYPE) + element_type = TREE_TYPE (element_type); + + output_type (element_type, containing_scope); + output_die (output_array_type_die, type); + } + break; + + case ENUMERAL_TYPE: + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + + /* For a non-file-scope tagged type, we can always go ahead and + output a Dwarf description of this type right now, even if + the type in question is still incomplete, because if this + local type *was* ever completed anywhere within its scope, + that complete definition would already have been attached to + this RECORD_TYPE, UNION_TYPE, QUAL_UNION_TYPE or ENUMERAL_TYPE + node by the time we reach this point. That's true because of the + way the front-end does its processing of file-scope declarations (of + functions and class types) within which other types might be + nested. The C and C++ front-ends always gobble up such "local + scope" things en-mass before they try to output *any* debugging + information for any of the stuff contained inside them and thus, + we get the benefit here of what is (in effect) a pre-resolution + of forward references to tagged types in local scopes. + + Note however that for file-scope tagged types we cannot assume + that such pre-resolution of forward references has taken place. + A given file-scope tagged type may appear to be incomplete when + we reach this point, but it may yet be given a full definition + (at file-scope) later on during compilation. In order to avoid + generating a premature (and possibly incorrect) set of Dwarf + DIEs for such (as yet incomplete) file-scope tagged types, we + generate nothing at all for as-yet incomplete file-scope tagged + types here unless we are making our special "finalization" pass + for file-scope things at the very end of compilation. At that + time, we will certainly know as much about each file-scope tagged + type as we are ever going to know, so at that point in time, we + can safely generate correct Dwarf descriptions for these file- + scope tagged types. + */ + + if (TYPE_SIZE (type) == 0 && TYPE_CONTEXT (type) == NULL && !finalizing) + return; /* EARLY EXIT! Avoid setting TREE_ASM_WRITTEN. */ + + /* Prevent infinite recursion in cases where the type of some + member of this type is expressed in terms of this type itself. */ + + TREE_ASM_WRITTEN (type) = 1; + + /* Output a DIE to represent the tagged type itself. */ + + switch (TREE_CODE (type)) + { + case ENUMERAL_TYPE: + output_die (output_enumeration_type_die, type); + return; /* a special case -- nothing left to do so just return */ + + case RECORD_TYPE: + output_die (output_structure_type_die, type); + break; + + case UNION_TYPE: + case QUAL_UNION_TYPE: + output_die (output_union_type_die, type); + break; + + default: + abort (); /* Should never happen. */ + } + + /* If this is not an incomplete type, output descriptions of + each of its members. + + Note that as we output the DIEs necessary to represent the + members of this record or union type, we will also be trying + to output DIEs to represent the *types* of those members. + However the `output_type' function (above) will specifically + avoid generating type DIEs for member types *within* the list + of member DIEs for this (containing) type execpt for those + types (of members) which are explicitly marked as also being + members of this (containing) type themselves. The g++ front- + end can force any given type to be treated as a member of some + other (containing) type by setting the TYPE_CONTEXT of the + given (member) type to point to the TREE node representing the + appropriate (containing) type. + */ + + if (TYPE_SIZE (type)) + { + { + register tree normal_member; + + /* First output info about the data members and type members. */ + + for (normal_member = TYPE_FIELDS (type); + normal_member; + normal_member = TREE_CHAIN (normal_member)) + output_decl (normal_member, type); + } + + { + register tree vec_base; + + /* Now output info about the function members (if any). */ + + vec_base = TYPE_METHODS (type); + if (vec_base) + { + register tree first_func_member = TREE_VEC_ELT (vec_base, 0); + register tree func_member; + + /* This isn't documented, but the first element of the + vector of member functions can be NULL in cases where + the class type in question didn't have either a + constructor or a destructor declared for it. We have + to make allowances for that here. */ + + if (first_func_member == NULL) + first_func_member = TREE_VEC_ELT (vec_base, 1); + + for (func_member = first_func_member; + func_member; + func_member = TREE_CHAIN (func_member)) + output_decl (func_member, type); + } + } + + /* RECORD_TYPEs, UNION_TYPEs, and QUAL_UNION_TYPEs are themselves + scopes (at least in C++) so we must now output any nested + pending types which are local just to this type. */ + + output_pending_types_for_scope (type); + + end_sibling_chain (); /* Terminate member chain. */ + } + + break; + + case VOID_TYPE: + case INTEGER_TYPE: + case REAL_TYPE: + case COMPLEX_TYPE: + case BOOLEAN_TYPE: + case CHAR_TYPE: + break; /* No DIEs needed for fundamental types. */ + + case LANG_TYPE: /* No Dwarf representation currently defined. */ + break; + + default: + abort (); + } + + TREE_ASM_WRITTEN (type) = 1; +} + +static void +output_tagged_type_instantiation (type) + register tree type; +{ + if (type == 0 || type == error_mark_node) + return; + + /* We are going to output a DIE to represent the unqualified version of + of this type (i.e. without any const or volatile qualifiers) so make + sure that we have the main variant (i.e. the unqualified version) of + this type now. */ + + assert (type == type_main_variant (type)); + + assert (TREE_ASM_WRITTEN (type)); + + switch (TREE_CODE (type)) + { + case ERROR_MARK: + break; + + case ENUMERAL_TYPE: + output_die (output_inlined_enumeration_type_die, type); + break; + + case RECORD_TYPE: + output_die (output_inlined_structure_type_die, type); + break; + + case UNION_TYPE: + case QUAL_UNION_TYPE: + output_die (output_inlined_union_type_die, type); + break; + + default: + abort (); /* Should never happen. */ + } +} + +/* Output a TAG_lexical_block DIE followed by DIEs to represent all of + the things which are local to the given block. */ + +static void +output_block (stmt) + register tree stmt; +{ + register int must_output_die = 0; + register tree origin; + register enum tree_code origin_code; + + /* Ignore blocks never really used to make RTL. */ + + if (! stmt || ! TREE_USED (stmt)) + return; + + /* Determine the "ultimate origin" of this block. This block may be an + inlined instance of an inlined instance of inline function, so we + have to trace all of the way back through the origin chain to find + out what sort of node actually served as the original seed for the + creation of the current block. */ + + origin = block_ultimate_origin (stmt); + origin_code = (origin != NULL) ? TREE_CODE (origin) : ERROR_MARK; + + /* Determine if we need to output any Dwarf DIEs at all to represent this + block. */ + + if (origin_code == FUNCTION_DECL) + /* The outer scopes for inlinings *must* always be represented. We + generate TAG_inlined_subroutine DIEs for them. (See below.) */ + must_output_die = 1; + else + { + /* In the case where the current block represents an inlining of the + "body block" of an inline function, we must *NOT* output any DIE + for this block because we have already output a DIE to represent + the whole inlined function scope and the "body block" of any + function doesn't really represent a different scope according to + ANSI C rules. So we check here to make sure that this block does + not represent a "body block inlining" before trying to set the + `must_output_die' flag. */ + + if (origin == NULL || ! is_body_block (origin)) + { + /* Determine if this block directly contains any "significant" + local declarations which we will need to output DIEs for. */ + + if (debug_info_level > DINFO_LEVEL_TERSE) + /* We are not in terse mode so *any* local declaration counts + as being a "significant" one. */ + must_output_die = (BLOCK_VARS (stmt) != NULL); + else + { + register tree decl; + + /* We are in terse mode, so only local (nested) function + definitions count as "significant" local declarations. */ + + for (decl = BLOCK_VARS (stmt); decl; decl = TREE_CHAIN (decl)) + if (TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl)) + { + must_output_die = 1; + break; + } + } + } + } + + /* It would be a waste of space to generate a Dwarf TAG_lexical_block + DIE for any block which contains no significant local declarations + at all. Rather, in such cases we just call `output_decls_for_scope' + so that any needed Dwarf info for any sub-blocks will get properly + generated. Note that in terse mode, our definition of what constitutes + a "significant" local declaration gets restricted to include only + inlined function instances and local (nested) function definitions. */ + + if (must_output_die) + { + output_die ((origin_code == FUNCTION_DECL) + ? output_inlined_subroutine_die + : output_lexical_block_die, + stmt); + output_decls_for_scope (stmt); + end_sibling_chain (); + } + else + output_decls_for_scope (stmt); +} + +/* Output all of the decls declared within a given scope (also called + a `binding contour') and (recursively) all of it's sub-blocks. */ + +static void +output_decls_for_scope (stmt) + register tree stmt; +{ + /* Ignore blocks never really used to make RTL. */ + + if (! stmt || ! TREE_USED (stmt)) + return; + + if (! BLOCK_ABSTRACT (stmt)) + next_block_number++; + + /* Output the DIEs to represent all of the data objects, functions, + typedefs, and tagged types declared directly within this block + but not within any nested sub-blocks. */ + + { + register tree decl; + + for (decl = BLOCK_VARS (stmt); decl; decl = TREE_CHAIN (decl)) + output_decl (decl, stmt); + } + + output_pending_types_for_scope (stmt); + + /* Output the DIEs to represent all sub-blocks (and the items declared + therein) of this block. */ + + { + register tree subblocks; + + for (subblocks = BLOCK_SUBBLOCKS (stmt); + subblocks; + subblocks = BLOCK_CHAIN (subblocks)) + output_block (subblocks); + } +} + +/* Output Dwarf .debug information for a decl described by DECL. */ + +static void +output_decl (decl, containing_scope) + register tree decl; + register tree containing_scope; +{ + /* Make a note of the decl node we are going to be working on. We may + need to give the user the source coordinates of where it appeared in + case we notice (later on) that something about it looks screwy. */ + + dwarf_last_decl = decl; + + if (TREE_CODE (decl) == ERROR_MARK) + return; + + /* If this ..._DECL node is marked to be ignored, then ignore it. + But don't ignore a function definition, since that would screw + up our count of blocks, and that it turn will completely screw up the + the labels we will reference in subsequent AT_low_pc and AT_high_pc + attributes (for subsequent blocks). */ + + if (DECL_IGNORED_P (decl) && TREE_CODE (decl) != FUNCTION_DECL) + return; + + switch (TREE_CODE (decl)) + { + case CONST_DECL: + /* The individual enumerators of an enum type get output when we + output the Dwarf representation of the relevant enum type itself. */ + break; + + case FUNCTION_DECL: + /* If we are in terse mode, don't output any DIEs to represent + mere function declarations. Also, if we are conforming + to the DWARF version 1 specification, don't output DIEs for + mere function declarations. */ + + if (DECL_INITIAL (decl) == NULL_TREE) +#if (DWARF_VERSION > 1) + if (debug_info_level <= DINFO_LEVEL_TERSE) +#endif + break; + + /* Before we describe the FUNCTION_DECL itself, make sure that we + have described its return type. */ + + output_type (TREE_TYPE (TREE_TYPE (decl)), containing_scope); + + /* If the following DIE will represent a function definition for a + function with "extern" linkage, output a special "pubnames" DIE + label just ahead of the actual DIE. A reference to this label + was already generated in the .debug_pubnames section sub-entry + for this function definition. */ + + if (TREE_PUBLIC (decl)) + { + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + sprintf (label, PUB_DIE_LABEL_FMT, next_pubname_number++); + ASM_OUTPUT_LABEL (asm_out_file, label); + } + + /* Now output a DIE to represent the function itself. */ + + output_die (TREE_PUBLIC (decl) || DECL_EXTERNAL (decl) + ? output_global_subroutine_die + : output_local_subroutine_die, + decl); + + /* Now output descriptions of the arguments for this function. + This gets (unnecessarily?) complex because of the fact that + the DECL_ARGUMENT list for a FUNCTION_DECL doesn't indicate + cases where there was a trailing `...' at the end of the formal + parameter list. In order to find out if there was a trailing + ellipsis or not, we must instead look at the type associated + with the FUNCTION_DECL. This will be a node of type FUNCTION_TYPE. + If the chain of type nodes hanging off of this FUNCTION_TYPE node + ends with a void_type_node then there should *not* be an ellipsis + at the end. */ + + /* In the case where we are describing a mere function declaration, all + we need to do here (and all we *can* do here) is to describe + the *types* of its formal parameters. */ + + if (DECL_INITIAL (decl) == NULL_TREE) + output_formal_types (TREE_TYPE (decl)); + else + { + register tree arg_decls = DECL_ARGUMENTS (decl); + + { + register tree last_arg; + + last_arg = (arg_decls && TREE_CODE (arg_decls) != ERROR_MARK) + ? tree_last (arg_decls) + : NULL; + + /* Generate DIEs to represent all known formal parameters, but + don't do it if this looks like a varargs function. A given + function is considered to be a varargs function if (and only + if) its last named argument is named `__builtin_va_alist'. */ + + if (! last_arg + || ! DECL_NAME (last_arg) + || strcmp (IDENTIFIER_POINTER (DECL_NAME (last_arg)), + "__builtin_va_alist")) + { + register tree parm; + + /* WARNING! Kludge zone ahead! Here we have a special + hack for svr4 SDB compatibility. Instead of passing the + current FUNCTION_DECL node as the second parameter (i.e. + the `containing_scope' parameter) to `output_decl' (as + we ought to) we instead pass a pointer to our own private + fake_containing_scope node. That node is a RECORD_TYPE + node which NO OTHER TYPE may ever actually be a member of. + + This pointer will ultimately get passed into `output_type' + as its `containing_scope' parameter. `Output_type' will + then perform its part in the hack... i.e. it will pend + the type of the formal parameter onto the pending_types + list. Later on, when we are done generating the whole + sequence of formal parameter DIEs for this function + definition, we will un-pend all previously pended types + of formal parameters for this function definition. + + This whole kludge prevents any type DIEs from being + mixed in with the formal parameter DIEs. That's good + because svr4 SDB believes that the list of formal + parameter DIEs for a function ends wherever the first + non-formal-parameter DIE appears. Thus, we have to + keep the formal parameter DIEs segregated. They must + all appear (consecutively) at the start of the list of + children for the DIE representing the function definition. + Then (and only then) may we output any additional DIEs + needed to represent the types of these formal parameters. + */ + + for (parm = arg_decls; parm; parm = TREE_CHAIN (parm)) + if (TREE_CODE (parm) == PARM_DECL) + output_decl (parm, fake_containing_scope); + + /* Now that we have finished generating all of the DIEs to + represent the formal parameters themselves, force out + any DIEs needed to represent their types. We do this + simply by un-pending all previously pended types which + can legitimately go into the chain of children DIEs for + the current FUNCTION_DECL. */ + + output_pending_types_for_scope (decl); + } + } + + /* Now try to decide if we should put an ellipsis at the end. */ + + { + register int has_ellipsis = TRUE; /* default assumption */ + register tree fn_arg_types = TYPE_ARG_TYPES (TREE_TYPE (decl)); + + if (fn_arg_types) + { + /* This function declaration/definition was prototyped. */ + + /* If the list of formal argument types ends with a + void_type_node, then the formals list did *not* end + with an ellipsis. */ + + if (TREE_VALUE (tree_last (fn_arg_types)) == void_type_node) + has_ellipsis = FALSE; + } + else + { + /* This function declaration/definition was not prototyped. */ + + /* Note that all non-prototyped function *declarations* are + assumed to represent varargs functions (until proven + otherwise). */ + + if (DECL_INITIAL (decl)) /* if this is a func definition */ + { + if (!arg_decls) + has_ellipsis = FALSE; /* no args == (void) */ + else + { + /* For a non-prototyped function definition which + declares one or more formal parameters, if the name + of the first formal parameter is *not* + __builtin_va_alist then we must assume that this + is *not* a varargs function. */ + + if (DECL_NAME (arg_decls) + && strcmp (IDENTIFIER_POINTER (DECL_NAME (arg_decls)), + "__builtin_va_alist")) + has_ellipsis = FALSE; + } + } + } + + if (has_ellipsis) + output_die (output_unspecified_parameters_die, decl); + } + } + + /* Output Dwarf info for all of the stuff within the body of the + function (if it has one - it may be just a declaration). */ + + { + register tree outer_scope = DECL_INITIAL (decl); + + if (outer_scope && TREE_CODE (outer_scope) != ERROR_MARK) + { + /* Note that here, `outer_scope' is a pointer to the outermost + BLOCK node created to represent a function. + This outermost BLOCK actually represents the outermost + binding contour for the function, i.e. the contour in which + the function's formal parameters and labels get declared. + + Curiously, it appears that the front end doesn't actually + put the PARM_DECL nodes for the current function onto the + BLOCK_VARS list for this outer scope. (They are strung + off of the DECL_ARGUMENTS list for the function instead.) + The BLOCK_VARS list for the `outer_scope' does provide us + with a list of the LABEL_DECL nodes for the function however, + and we output DWARF info for those here. + + Just within the `outer_scope' there will be another BLOCK + node representing the function's outermost pair of curly + braces. We musn't generate a lexical_block DIE for this + outermost pair of curly braces because that is not really an + independent scope according to ANSI C rules. Rather, it is + the same scope in which the parameters were declared. */ + + { + register tree label; + + for (label = BLOCK_VARS (outer_scope); + label; + label = TREE_CHAIN (label)) + output_decl (label, outer_scope); + } + + /* Note here that `BLOCK_SUBBLOCKS (outer_scope)' points to a + list of BLOCK nodes which is always only one element long. + That one element represents the outermost pair of curley + braces for the function body. */ + + output_decls_for_scope (BLOCK_SUBBLOCKS (outer_scope)); + + /* Finally, force out any pending types which are local to the + outermost block of this function definition. These will + all have a TYPE_CONTEXT which points to the FUNCTION_DECL + node itself. */ + + output_pending_types_for_scope (decl); + } + } + + /* Generate a terminator for the list of stuff `owned' by this + function. */ + + end_sibling_chain (); + + break; + + case TYPE_DECL: + /* If we are in terse mode, don't generate any DIEs to represent + any actual typedefs. Note that even when we are in terse mode, + we must still output DIEs to represent those tagged types which + are used (directly or indirectly) in the specification of either + a return type or a formal parameter type of some function. */ + + if (debug_info_level <= DINFO_LEVEL_TERSE) + if (DECL_NAME (decl) != NULL + || ! TYPE_USED_FOR_FUNCTION (TREE_TYPE (decl))) + return; + + /* In the special case of a null-named TYPE_DECL node (representing + the declaration of some type tag), if the given TYPE_DECL is + marked as having been instantiated from some other (original) + TYPE_DECL node (e.g. one which was generated within the original + definition of an inline function) we have to generate a special + (abbreviated) TAG_structure_type, TAG_union_type, or + TAG_enumeration-type DIE here. */ + + if (! DECL_NAME (decl) && DECL_ABSTRACT_ORIGIN (decl)) + { + output_tagged_type_instantiation (TREE_TYPE (decl)); + return; + } + + output_type (TREE_TYPE (decl), containing_scope); + + /* Note that unlike the gcc front end (which generates a NULL named + TYPE_DECL node for each complete tagged type, each array type, + and each function type node created) the g++ front end generates + a *named* TYPE_DECL node for each tagged type node created. + Unfortunately, these g++ TYPE_DECL nodes cause us to output many + superfluous and unnecessary TAG_typedef DIEs here. When g++ is + fixed to stop generating these superfluous named TYPE_DECL nodes, + the superfluous TAG_typedef DIEs will likewise cease. */ + + if (DECL_NAME (decl)) + /* Output a DIE to represent the typedef itself. */ + output_die (output_typedef_die, decl); + break; + + case LABEL_DECL: + if (debug_info_level >= DINFO_LEVEL_NORMAL) + output_die (output_label_die, decl); + break; + + case VAR_DECL: + /* If we are conforming to the DWARF version 1 specification, don't + generated any DIEs to represent mere external object declarations. */ + +#if (DWARF_VERSION <= 1) + if (DECL_EXTERNAL (decl) && ! TREE_PUBLIC (decl)) + break; +#endif + + /* If we are in terse mode, don't generate any DIEs to represent + any variable declarations or definitions. */ + + if (debug_info_level <= DINFO_LEVEL_TERSE) + break; + + /* Output any DIEs that are needed to specify the type of this data + object. */ + + output_type (TREE_TYPE (decl), containing_scope); + + /* If the following DIE will represent a data object definition for a + data object with "extern" linkage, output a special "pubnames" DIE + label just ahead of the actual DIE. A reference to this label + was already generated in the .debug_pubnames section sub-entry + for this data object definition. */ + + if (TREE_PUBLIC (decl) && ! DECL_ABSTRACT (decl)) + { + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + sprintf (label, PUB_DIE_LABEL_FMT, next_pubname_number++); + ASM_OUTPUT_LABEL (asm_out_file, label); + } + + /* Now output the DIE to represent the data object itself. This gets + complicated because of the possibility that the VAR_DECL really + represents an inlined instance of a formal parameter for an inline + function. */ + + { + register void (*func) (); + register tree origin = decl_ultimate_origin (decl); + + if (origin != NULL && TREE_CODE (origin) == PARM_DECL) + func = output_formal_parameter_die; + else + { + if (TREE_PUBLIC (decl) || DECL_EXTERNAL (decl)) + func = output_global_variable_die; + else + func = output_local_variable_die; + } + output_die (func, decl); + } + break; + + case FIELD_DECL: + /* Ignore the nameless fields that are used to skip bits. */ + if (DECL_NAME (decl) != 0) + { + output_type (member_declared_type (decl), containing_scope); + output_die (output_member_die, decl); + } + break; + + case PARM_DECL: + /* Force out the type of this formal, if it was not forced out yet. + Note that here we can run afowl of a bug in "classic" svr4 SDB. + It should be able to grok the presence of type DIEs within a list + of TAG_formal_parameter DIEs, but it doesn't. */ + + output_type (TREE_TYPE (decl), containing_scope); + output_die (output_formal_parameter_die, decl); + break; + + default: + abort (); + } +} + +void +dwarfout_file_scope_decl (decl, set_finalizing) + register tree decl; + register int set_finalizing; +{ + if (TREE_CODE (decl) == ERROR_MARK) + return; + + /* If this ..._DECL node is marked to be ignored, then ignore it. We + gotta hope that the node in question doesn't represent a function + definition. If it does, then totally ignoring it is bound to screw + up our count of blocks, and that it turn will completely screw up the + the labels we will reference in subsequent AT_low_pc and AT_high_pc + attributes (for subsequent blocks). (It's too bad that BLOCK nodes + don't carry their own sequence numbers with them!) */ + + if (DECL_IGNORED_P (decl)) + { + if (TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl) != NULL) + abort (); + return; + } + + switch (TREE_CODE (decl)) + { + case FUNCTION_DECL: + + /* Ignore this FUNCTION_DECL if it refers to a builtin declaration of + a builtin function. Explicit programmer-supplied declarations of + these same functions should NOT be ignored however. */ + + if (DECL_EXTERNAL (decl) && DECL_FUNCTION_CODE (decl)) + return; + + /* What we would really like to do here is to filter out all mere + file-scope declarations of file-scope functions which are never + referenced later within this translation unit (and keep all of + ones that *are* referenced later on) but we aren't clarvoiant, + so we have no idea which functions will be referenced in the + future (i.e. later on within the current translation unit). + So here we just ignore all file-scope function declarations + which are not also definitions. If and when the debugger needs + to know something about these funcstion, it wil have to hunt + around and find the DWARF information associated with the + *definition* of the function. + + Note that we can't just check `DECL_EXTERNAL' to find out which + FUNCTION_DECL nodes represent definitions and which ones represent + mere declarations. We have to check `DECL_INITIAL' instead. That's + because the C front-end supports some weird semantics for "extern + inline" function definitions. These can get inlined within the + current translation unit (an thus, we need to generate DWARF info + for their abstract instances so that the DWARF info for the + concrete inlined instances can have something to refer to) but + the compiler never generates any out-of-lines instances of such + things (despite the fact that they *are* definitions). The + important point is that the C front-end marks these "extern inline" + functions as DECL_EXTERNAL, but we need to generate DWARf for them + anyway. + + Note that the C++ front-end also plays some similar games for inline + function definitions appearing within include files which also + contain `#pragma interface' pragmas. */ + + if (DECL_INITIAL (decl) == NULL_TREE) + return; + + if (TREE_PUBLIC (decl) + && ! DECL_EXTERNAL (decl) + && ! DECL_ABSTRACT (decl)) + { + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + /* Output a .debug_pubnames entry for a public function + defined in this compilation unit. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, PUBNAMES_SECTION); + sprintf (label, PUB_DIE_LABEL_FMT, next_pubname_number); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, label); + ASM_OUTPUT_DWARF_STRING (asm_out_file, + IDENTIFIER_POINTER (DECL_NAME (decl))); + ASM_OUTPUT_POP_SECTION (asm_out_file); + } + + break; + + case VAR_DECL: + + /* Ignore this VAR_DECL if it refers to a file-scope extern data + object declaration and if the declaration was never even + referenced from within this entire compilation unit. We + suppress these DIEs in order to save space in the .debug section + (by eliminating entries which are probably useless). Note that + we must not suppress block-local extern declarations (whether + used or not) because that would screw-up the debugger's name + lookup mechanism and cause it to miss things which really ought + to be in scope at a given point. */ + + if (DECL_EXTERNAL (decl) && !TREE_USED (decl)) + return; + + if (TREE_PUBLIC (decl) + && ! DECL_EXTERNAL (decl) + && GET_CODE (DECL_RTL (decl)) == MEM + && ! DECL_ABSTRACT (decl)) + { + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + if (debug_info_level >= DINFO_LEVEL_NORMAL) + { + /* Output a .debug_pubnames entry for a public variable + defined in this compilation unit. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, PUBNAMES_SECTION); + sprintf (label, PUB_DIE_LABEL_FMT, next_pubname_number); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, label); + ASM_OUTPUT_DWARF_STRING (asm_out_file, + IDENTIFIER_POINTER (DECL_NAME (decl))); + ASM_OUTPUT_POP_SECTION (asm_out_file); + } + + if (DECL_INITIAL (decl) == NULL) + { + /* Output a .debug_aranges entry for a public variable + which is tentatively defined in this compilation unit. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, ARANGES_SECTION); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, + IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, + (unsigned) int_size_in_bytes (TREE_TYPE (decl))); + ASM_OUTPUT_POP_SECTION (asm_out_file); + } + } + + /* If we are in terse mode, don't generate any DIEs to represent + any variable declarations or definitions. */ + + if (debug_info_level <= DINFO_LEVEL_TERSE) + return; + + break; + + case TYPE_DECL: + /* Don't bother trying to generate any DIEs to represent any of the + normal built-in types for the language we are compiling, except + in cases where the types in question are *not* DWARF fundamental + types. We make an exception in the case of non-fundamental types + for the sake of objective C (and perhaps C++) because the GNU + front-ends for these languages may in fact create certain "built-in" + types which are (for example) RECORD_TYPEs. In such cases, we + really need to output these (non-fundamental) types because other + DIEs may contain references to them. */ + + if (DECL_SOURCE_LINE (decl) == 0 + && type_is_fundamental (TREE_TYPE (decl))) + return; + + /* If we are in terse mode, don't generate any DIEs to represent + any actual typedefs. Note that even when we are in terse mode, + we must still output DIEs to represent those tagged types which + are used (directly or indirectly) in the specification of either + a return type or a formal parameter type of some function. */ + + if (debug_info_level <= DINFO_LEVEL_TERSE) + if (DECL_NAME (decl) != NULL + || ! TYPE_USED_FOR_FUNCTION (TREE_TYPE (decl))) + return; + + break; + + default: + return; + } + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, DEBUG_SECTION); + finalizing = set_finalizing; + output_decl (decl, NULL_TREE); + + /* NOTE: The call above to `output_decl' may have caused one or more + file-scope named types (i.e. tagged types) to be placed onto the + pending_types_list. We have to get those types off of that list + at some point, and this is the perfect time to do it. If we didn't + take them off now, they might still be on the list when cc1 finally + exits. That might be OK if it weren't for the fact that when we put + types onto the pending_types_list, we set the TREE_ASM_WRITTEN flag + for these types, and that causes them never to be output unless + `output_pending_types_for_scope' takes them off of the list and un-sets + their TREE_ASM_WRITTEN flags. */ + + output_pending_types_for_scope (NULL_TREE); + + /* The above call should have totally emptied the pending_types_list. */ + + assert (pending_types == 0); + + ASM_OUTPUT_POP_SECTION (asm_out_file); + + if (TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl) != NULL) + current_funcdef_number++; +} + +/* Output a marker (i.e. a label) for the beginning of the generated code + for a lexical block. */ + +void +dwarfout_begin_block (blocknum) + register unsigned blocknum; +{ + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + text_section (); + sprintf (label, BLOCK_BEGIN_LABEL_FMT, blocknum); + ASM_OUTPUT_LABEL (asm_out_file, label); +} + +/* Output a marker (i.e. a label) for the end of the generated code + for a lexical block. */ + +void +dwarfout_end_block (blocknum) + register unsigned blocknum; +{ + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + text_section (); + sprintf (label, BLOCK_END_LABEL_FMT, blocknum); + ASM_OUTPUT_LABEL (asm_out_file, label); +} + +/* Output a marker (i.e. a label) at a point in the assembly code which + corresponds to a given source level label. */ + +void +dwarfout_label (insn) + register rtx insn; +{ + if (debug_info_level >= DINFO_LEVEL_NORMAL) + { + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + text_section (); + sprintf (label, INSN_LABEL_FMT, current_funcdef_number, + (unsigned) INSN_UID (insn)); + ASM_OUTPUT_LABEL (asm_out_file, label); + } +} + +/* Output a marker (i.e. a label) for the point in the generated code where + the real body of the function begins (after parameters have been moved + to their home locations). */ + +void +dwarfout_begin_function () +{ + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + text_section (); + sprintf (label, BODY_BEGIN_LABEL_FMT, current_funcdef_number); + ASM_OUTPUT_LABEL (asm_out_file, label); +} + +/* Output a marker (i.e. a label) for the point in the generated code where + the real body of the function ends (just before the epilogue code). */ + +void +dwarfout_end_function () +{ + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + text_section (); + sprintf (label, BODY_END_LABEL_FMT, current_funcdef_number); + ASM_OUTPUT_LABEL (asm_out_file, label); +} + +/* Output a marker (i.e. a label) for the absolute end of the generated code + for a function definition. This gets called *after* the epilogue code + has been generated. */ + +void +dwarfout_end_epilogue () +{ + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + /* Output a label to mark the endpoint of the code generated for this + function. */ + + sprintf (label, FUNC_END_LABEL_FMT, current_funcdef_number); + ASM_OUTPUT_LABEL (asm_out_file, label); +} + +static void +shuffle_filename_entry (new_zeroth) + register filename_entry *new_zeroth; +{ + filename_entry temp_entry; + register filename_entry *limit_p; + register filename_entry *move_p; + + if (new_zeroth == &filename_table[0]) + return; + + temp_entry = *new_zeroth; + + /* Shift entries up in the table to make room at [0]. */ + + limit_p = &filename_table[0]; + for (move_p = new_zeroth; move_p > limit_p; move_p--) + *move_p = *(move_p-1); + + /* Install the found entry at [0]. */ + + filename_table[0] = temp_entry; +} + +/* Create a new (string) entry for the .debug_sfnames section. */ + +static void +generate_new_sfname_entry () +{ + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, SFNAMES_SECTION); + sprintf (label, SFNAMES_ENTRY_LABEL_FMT, filename_table[0].number); + ASM_OUTPUT_LABEL (asm_out_file, label); + ASM_OUTPUT_DWARF_STRING (asm_out_file, + filename_table[0].name + ? filename_table[0].name + : ""); + ASM_OUTPUT_POP_SECTION (asm_out_file); +} + +/* Lookup a filename (in the list of filenames that we know about here in + dwarfout.c) and return its "index". The index of each (known) filename + is just a unique number which is associated with only that one filename. + We need such numbers for the sake of generating labels (in the + .debug_sfnames section) and references to those unique labels (in the + .debug_srcinfo and .debug_macinfo sections). + + If the filename given as an argument is not found in our current list, + add it to the list and assign it the next available unique index number. + + Whatever we do (i.e. whether we find a pre-existing filename or add a new + one), we shuffle the filename found (or added) up to the zeroth entry of + our list of filenames (which is always searched linearly). We do this so + as to optimize the most common case for these filename lookups within + dwarfout.c. The most common case by far is the case where we call + lookup_filename to lookup the very same filename that we did a lookup + on the last time we called lookup_filename. We make sure that this + common case is fast because such cases will constitute 99.9% of the + lookups we ever do (in practice). + + If we add a new filename entry to our table, we go ahead and generate + the corresponding entry in the .debug_sfnames section right away. + Doing so allows us to avoid tickling an assembler bug (present in some + m68k assemblers) which yields assembly-time errors in cases where the + difference of two label addresses is taken and where the two labels + are in a section *other* than the one where the difference is being + calculated, and where at least one of the two symbol references is a + forward reference. (This bug could be tickled by our .debug_srcinfo + entries if we don't output their corresponding .debug_sfnames entries + before them.) +*/ + +static unsigned +lookup_filename (file_name) + char *file_name; +{ + register filename_entry *search_p; + register filename_entry *limit_p = &filename_table[ft_entries]; + + for (search_p = filename_table; search_p < limit_p; search_p++) + if (!strcmp (file_name, search_p->name)) + { + /* When we get here, we have found the filename that we were + looking for in the filename_table. Now we want to make sure + that it gets moved to the zero'th entry in the table (if it + is not already there) so that subsequent attempts to find the + same filename will find it as quickly as possible. */ + + shuffle_filename_entry (search_p); + return filename_table[0].number; + } + + /* We come here whenever we have a new filename which is not registered + in the current table. Here we add it to the table. */ + + /* Prepare to add a new table entry by making sure there is enough space + in the table to do so. If not, expand the current table. */ + + if (ft_entries == ft_entries_allocated) + { + ft_entries_allocated += FT_ENTRIES_INCREMENT; + filename_table + = (filename_entry *) + xrealloc (filename_table, + ft_entries_allocated * sizeof (filename_entry)); + } + + /* Initially, add the new entry at the end of the filename table. */ + + filename_table[ft_entries].number = ft_entries; + filename_table[ft_entries].name = xstrdup (file_name); + + /* Shuffle the new entry into filename_table[0]. */ + + shuffle_filename_entry (&filename_table[ft_entries]); + + if (debug_info_level >= DINFO_LEVEL_NORMAL) + generate_new_sfname_entry (); + + ft_entries++; + return filename_table[0].number; +} + +static void +generate_srcinfo_entry (line_entry_num, files_entry_num) + unsigned line_entry_num; + unsigned files_entry_num; +{ + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, SRCINFO_SECTION); + sprintf (label, LINE_ENTRY_LABEL_FMT, line_entry_num); + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, label, LINE_BEGIN_LABEL); + sprintf (label, SFNAMES_ENTRY_LABEL_FMT, files_entry_num); + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, label, SFNAMES_BEGIN_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); +} + +void +dwarfout_line (filename, line) + register char *filename; + register unsigned line; +{ + if (debug_info_level >= DINFO_LEVEL_NORMAL) + { + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + static unsigned last_line_entry_num = 0; + static unsigned prev_file_entry_num = (unsigned) -1; + register unsigned this_file_entry_num = lookup_filename (filename); + + text_section (); + sprintf (label, LINE_CODE_LABEL_FMT, ++last_line_entry_num); + ASM_OUTPUT_LABEL (asm_out_file, label); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, LINE_SECTION); + + if (this_file_entry_num != prev_file_entry_num) + { + char line_entry_label[MAX_ARTIFICIAL_LABEL_BYTES]; + + sprintf (line_entry_label, LINE_ENTRY_LABEL_FMT, last_line_entry_num); + ASM_OUTPUT_LABEL (asm_out_file, line_entry_label); + } + + { + register char *tail = rindex (filename, '/'); + + if (tail != NULL) + filename = tail; + } + + fprintf (asm_out_file, "\t%s\t%u\t%s %s:%u\n", + UNALIGNED_INT_ASM_OP, line, ASM_COMMENT_START, + filename, line); + ASM_OUTPUT_DWARF_DATA2 (asm_out_file, 0xffff); + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, label, TEXT_BEGIN_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); + + if (this_file_entry_num != prev_file_entry_num) + generate_srcinfo_entry (last_line_entry_num, this_file_entry_num); + prev_file_entry_num = this_file_entry_num; + } +} + +/* Generate an entry in the .debug_macinfo section. */ + +static void +generate_macinfo_entry (type_and_offset, string) + register char *type_and_offset; + register char *string; +{ + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, MACINFO_SECTION); + fprintf (asm_out_file, "\t%s\t%s\n", UNALIGNED_INT_ASM_OP, type_and_offset); + ASM_OUTPUT_DWARF_STRING (asm_out_file, string); + ASM_OUTPUT_POP_SECTION (asm_out_file); +} + +void +dwarfout_start_new_source_file (filename) + register char *filename; +{ + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + char type_and_offset[MAX_ARTIFICIAL_LABEL_BYTES*3]; + + sprintf (label, SFNAMES_ENTRY_LABEL_FMT, lookup_filename (filename)); + sprintf (type_and_offset, "0x%08x+%s-%s", + ((unsigned) MACINFO_start << 24), label, SFNAMES_BEGIN_LABEL); + generate_macinfo_entry (type_and_offset, ""); +} + +void +dwarfout_resume_previous_source_file (lineno) + register unsigned lineno; +{ + char type_and_offset[MAX_ARTIFICIAL_LABEL_BYTES*2]; + + sprintf (type_and_offset, "0x%08x+%u", + ((unsigned) MACINFO_resume << 24), lineno); + generate_macinfo_entry (type_and_offset, ""); +} + +/* Called from check_newline in c-parse.y. The `buffer' parameter + contains the tail part of the directive line, i.e. the part which + is past the initial whitespace, #, whitespace, directive-name, + whitespace part. */ + +void +dwarfout_define (lineno, buffer) + register unsigned lineno; + register char *buffer; +{ + static int initialized = 0; + char type_and_offset[MAX_ARTIFICIAL_LABEL_BYTES*2]; + + if (!initialized) + { + dwarfout_start_new_source_file (primary_filename); + initialized = 1; + } + sprintf (type_and_offset, "0x%08x+%u", + ((unsigned) MACINFO_define << 24), lineno); + generate_macinfo_entry (type_and_offset, buffer); +} + +/* Called from check_newline in c-parse.y. The `buffer' parameter + contains the tail part of the directive line, i.e. the part which + is past the initial whitespace, #, whitespace, directive-name, + whitespace part. */ + +void +dwarfout_undef (lineno, buffer) + register unsigned lineno; + register char *buffer; +{ + char type_and_offset[MAX_ARTIFICIAL_LABEL_BYTES*2]; + + sprintf (type_and_offset, "0x%08x+%u", + ((unsigned) MACINFO_undef << 24), lineno); + generate_macinfo_entry (type_and_offset, buffer); +} + +/* Set up for Dwarf output at the start of compilation. */ + +void +dwarfout_init (asm_out_file, main_input_filename) + register FILE *asm_out_file; + register char *main_input_filename; +{ + /* Remember the name of the primary input file. */ + + primary_filename = main_input_filename; + + /* Allocate the initial hunk of the pending_sibling_stack. */ + + pending_sibling_stack + = (unsigned *) + xmalloc (PENDING_SIBLINGS_INCREMENT * sizeof (unsigned)); + pending_siblings_allocated = PENDING_SIBLINGS_INCREMENT; + pending_siblings = 1; + + /* Allocate the initial hunk of the filename_table. */ + + filename_table + = (filename_entry *) + xmalloc (FT_ENTRIES_INCREMENT * sizeof (filename_entry)); + ft_entries_allocated = FT_ENTRIES_INCREMENT; + ft_entries = 0; + + /* Allocate the initial hunk of the pending_types_list. */ + + pending_types_list + = (tree *) xmalloc (PENDING_TYPES_INCREMENT * sizeof (tree)); + pending_types_allocated = PENDING_TYPES_INCREMENT; + pending_types = 0; + + /* Create an artificial RECORD_TYPE node which we can use in our hack + to get the DIEs representing types of formal parameters to come out + only *after* the DIEs for the formal parameters themselves. */ + + fake_containing_scope = make_node (RECORD_TYPE); + + /* Output a starting label for the .text section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, TEXT_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, TEXT_BEGIN_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); + + /* Output a starting label for the .data section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, DATA_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, DATA_BEGIN_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); + +#if 0 /* GNU C doesn't currently use .data1. */ + /* Output a starting label for the .data1 section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, DATA1_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, DATA1_BEGIN_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); +#endif + + /* Output a starting label for the .rodata section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, RODATA_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, RODATA_BEGIN_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); + +#if 0 /* GNU C doesn't currently use .rodata1. */ + /* Output a starting label for the .rodata1 section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, RODATA1_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, RODATA1_BEGIN_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); +#endif + + /* Output a starting label for the .bss section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, BSS_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, BSS_BEGIN_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); + + if (debug_info_level >= DINFO_LEVEL_NORMAL) + { + /* Output a starting label and an initial (compilation directory) + entry for the .debug_sfnames section. The starting label will be + referenced by the initial entry in the .debug_srcinfo section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, SFNAMES_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, SFNAMES_BEGIN_LABEL); + { + register char *pwd = getpwd (); + register unsigned len = strlen (pwd); + register char *dirname = (char *) xmalloc (len + 2); + + strcpy (dirname, pwd); + strcpy (dirname + len, "/"); + ASM_OUTPUT_DWARF_STRING (asm_out_file, dirname); + free (dirname); + } + ASM_OUTPUT_POP_SECTION (asm_out_file); + + if (debug_info_level >= DINFO_LEVEL_VERBOSE) + { + /* Output a starting label for the .debug_macinfo section. This + label will be referenced by the AT_mac_info attribute in the + TAG_compile_unit DIE. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, MACINFO_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, MACINFO_BEGIN_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); + } + + /* Generate the initial entry for the .line section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, LINE_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, LINE_BEGIN_LABEL); + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, LINE_END_LABEL, LINE_BEGIN_LABEL); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, TEXT_BEGIN_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); + + /* Generate the initial entry for the .debug_srcinfo section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, SRCINFO_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, SRCINFO_BEGIN_LABEL); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, LINE_BEGIN_LABEL); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, SFNAMES_BEGIN_LABEL); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, TEXT_BEGIN_LABEL); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, TEXT_END_LABEL); +#ifdef DWARF_TIMESTAMPS + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, time (NULL)); +#else + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, -1); +#endif + ASM_OUTPUT_POP_SECTION (asm_out_file); + + /* Generate the initial entry for the .debug_pubnames section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, PUBNAMES_SECTION); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, DEBUG_BEGIN_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); + + /* Generate the initial entry for the .debug_aranges section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, ARANGES_SECTION); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, DEBUG_BEGIN_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); + } + + /* Setup first DIE number == 1. */ + NEXT_DIE_NUM = next_unused_dienum++; + + /* Generate the initial DIE for the .debug section. Note that the + (string) value given in the AT_name attribute of the TAG_compile_unit + DIE will (typically) be a relative pathname and that this pathname + should be taken as being relative to the directory from which the + compiler was invoked when the given (base) source file was compiled. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, DEBUG_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, DEBUG_BEGIN_LABEL); + output_die (output_compile_unit_die, main_input_filename); + ASM_OUTPUT_POP_SECTION (asm_out_file); + + fputc ('\n', asm_out_file); +} + +/* Output stuff that dwarf requires at the end of every file. */ + +void +dwarfout_finish () +{ + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, DEBUG_SECTION); + + /* Mark the end of the chain of siblings which represent all file-scope + declarations in this compilation unit. */ + + /* The (null) DIE which represents the terminator for the (sibling linked) + list of file-scope items is *special*. Normally, we would just call + end_sibling_chain at this point in order to output a word with the + value `4' and that word would act as the terminator for the list of + DIEs describing file-scope items. Unfortunately, if we were to simply + do that, the label that would follow this DIE in the .debug section + (i.e. `..D2') would *not* be properly aligned (as it must be on some + machines) to a 4 byte boundary. + + In order to force the label `..D2' to get aligned to a 4 byte boundary, + the trick used is to insert extra (otherwise useless) padding bytes + into the (null) DIE that we know must precede the ..D2 label in the + .debug section. The amount of padding required can be anywhere between + 0 and 3 bytes. The length word at the start of this DIE (i.e. the one + with the padding) would normally contain the value 4, but now it will + also have to include the padding bytes, so it will instead have some + value in the range 4..7. + + Fortunately, the rules of Dwarf say that any DIE whose length word + contains *any* value less than 8 should be treated as a null DIE, so + this trick works out nicely. Clever, eh? Don't give me any credit + (or blame). I didn't think of this scheme. I just conformed to it. + */ + + output_die (output_padded_null_die, (void *)0); + dienum_pop (); + + sprintf (label, DIE_BEGIN_LABEL_FMT, NEXT_DIE_NUM); + ASM_OUTPUT_LABEL (asm_out_file, label); /* should be ..D2 */ + ASM_OUTPUT_POP_SECTION (asm_out_file); + + /* Output a terminator label for the .text section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, TEXT_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, TEXT_END_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); + + /* Output a terminator label for the .data section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, DATA_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, DATA_END_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); + +#if 0 /* GNU C doesn't currently use .data1. */ + /* Output a terminator label for the .data1 section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, DATA1_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, DATA1_END_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); +#endif + + /* Output a terminator label for the .rodata section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, RODATA_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, RODATA_END_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); + +#if 0 /* GNU C doesn't currently use .rodata1. */ + /* Output a terminator label for the .rodata1 section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, RODATA1_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, RODATA1_END_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); +#endif + + /* Output a terminator label for the .bss section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, BSS_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, BSS_END_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); + + if (debug_info_level >= DINFO_LEVEL_NORMAL) + { + /* Output a terminating entry for the .line section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, LINE_SECTION); + ASM_OUTPUT_LABEL (asm_out_file, LINE_LAST_ENTRY_LABEL); + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0); + ASM_OUTPUT_DWARF_DATA2 (asm_out_file, 0xffff); + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, TEXT_END_LABEL, TEXT_BEGIN_LABEL); + ASM_OUTPUT_LABEL (asm_out_file, LINE_END_LABEL); + ASM_OUTPUT_POP_SECTION (asm_out_file); + + /* Output a terminating entry for the .debug_srcinfo section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, SRCINFO_SECTION); + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, + LINE_LAST_ENTRY_LABEL, LINE_BEGIN_LABEL); + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, -1); + ASM_OUTPUT_POP_SECTION (asm_out_file); + + if (debug_info_level >= DINFO_LEVEL_VERBOSE) + { + /* Output terminating entries for the .debug_macinfo section. */ + + dwarfout_resume_previous_source_file (0); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, MACINFO_SECTION); + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0); + ASM_OUTPUT_DWARF_STRING (asm_out_file, ""); + ASM_OUTPUT_POP_SECTION (asm_out_file); + } + + /* Generate the terminating entry for the .debug_pubnames section. */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, PUBNAMES_SECTION); + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0); + ASM_OUTPUT_DWARF_STRING (asm_out_file, ""); + ASM_OUTPUT_POP_SECTION (asm_out_file); + + /* Generate the terminating entries for the .debug_aranges section. + + Note that we want to do this only *after* we have output the end + labels (for the various program sections) which we are going to + refer to here. This allows us to work around a bug in the m68k + svr4 assembler. That assembler gives bogus assembly-time errors + if (within any given section) you try to take the difference of + two relocatable symbols, both of which are located within some + other section, and if one (or both?) of the symbols involved is + being forward-referenced. By generating the .debug_aranges + entries at this late point in the assembly output, we skirt the + issue simply by avoiding forward-references. + */ + + fputc ('\n', asm_out_file); + ASM_OUTPUT_PUSH_SECTION (asm_out_file, ARANGES_SECTION); + + ASM_OUTPUT_DWARF_ADDR (asm_out_file, TEXT_BEGIN_LABEL); + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, TEXT_END_LABEL, TEXT_BEGIN_LABEL); + + ASM_OUTPUT_DWARF_ADDR (asm_out_file, DATA_BEGIN_LABEL); + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, DATA_END_LABEL, DATA_BEGIN_LABEL); + +#if 0 /* GNU C doesn't currently use .data1. */ + ASM_OUTPUT_DWARF_ADDR (asm_out_file, DATA1_BEGIN_LABEL); + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, DATA1_END_LABEL, + DATA1_BEGIN_LABEL); +#endif + + ASM_OUTPUT_DWARF_ADDR (asm_out_file, RODATA_BEGIN_LABEL); + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, RODATA_END_LABEL, + RODATA_BEGIN_LABEL); + +#if 0 /* GNU C doesn't currently use .rodata1. */ + ASM_OUTPUT_DWARF_ADDR (asm_out_file, RODATA1_BEGIN_LABEL); + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, RODATA1_END_LABEL, + RODATA1_BEGIN_LABEL); +#endif + + ASM_OUTPUT_DWARF_ADDR (asm_out_file, BSS_BEGIN_LABEL); + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, BSS_END_LABEL, BSS_BEGIN_LABEL); + + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0); + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0); + + ASM_OUTPUT_POP_SECTION (asm_out_file); + } +} + +#endif /* DWARF_DEBUGGING_INFO */ diff --git a/gnu/usr.bin/cc/lib/emit-rtl.c b/gnu/usr.bin/cc/lib/emit-rtl.c new file mode 100644 index 0000000000..b63f0c8b47 --- /dev/null +++ b/gnu/usr.bin/cc/lib/emit-rtl.c @@ -0,0 +1,3137 @@ +/* Emit RTL for the GNU C-Compiler expander. + Copyright (C) 1987, 1988, 1992, 1993 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* Middle-to-low level generation of rtx code and insns. + + This file contains the functions `gen_rtx', `gen_reg_rtx' + and `gen_label_rtx' that are the usual ways of creating rtl + expressions for most purposes. + + It also has the functions for creating insns and linking + them in the doubly-linked chain. + + The patterns of the insns are created by machine-dependent + routines in insn-emit.c, which is generated automatically from + the machine description. These routines use `gen_rtx' to make + the individual rtx's of the pattern; what is machine dependent + is the kind of rtx's they make and what arguments they use. */ + +#include "config.h" +#include "gvarargs.h" +#include "rtl.h" +#include "flags.h" +#include "function.h" +#include "expr.h" +#include "regs.h" +#include "insn-config.h" +#include "real.h" +#include + +/* This is reset to LAST_VIRTUAL_REGISTER + 1 at the start of each function. + After rtl generation, it is 1 plus the largest register number used. */ + +int reg_rtx_no = LAST_VIRTUAL_REGISTER + 1; + +/* This is *not* reset after each function. It gives each CODE_LABEL + in the entire compilation a unique label number. */ + +static int label_num = 1; + +/* Lowest label number in current function. */ + +static int first_label_num; + +/* Highest label number in current function. + Zero means use the value of label_num instead. + This is nonzero only when belatedly compiling an inline function. */ + +static int last_label_num; + +/* Value label_num had when set_new_first_and_last_label_number was called. + If label_num has not changed since then, last_label_num is valid. */ + +static int base_label_num; + +/* Nonzero means do not generate NOTEs for source line numbers. */ + +static int no_line_numbers; + +/* Commonly used rtx's, so that we only need space for one copy. + These are initialized once for the entire compilation. + All of these except perhaps the floating-point CONST_DOUBLEs + are unique; no other rtx-object will be equal to any of these. */ + +rtx pc_rtx; /* (PC) */ +rtx cc0_rtx; /* (CC0) */ +rtx cc1_rtx; /* (CC1) (not actually used nowadays) */ +rtx const0_rtx; /* (CONST_INT 0) */ +rtx const1_rtx; /* (CONST_INT 1) */ +rtx const2_rtx; /* (CONST_INT 2) */ +rtx constm1_rtx; /* (CONST_INT -1) */ +rtx const_true_rtx; /* (CONST_INT STORE_FLAG_VALUE) */ + +/* We record floating-point CONST_DOUBLEs in each floating-point mode for + the values of 0, 1, and 2. For the integer entries and VOIDmode, we + record a copy of const[012]_rtx. */ + +rtx const_tiny_rtx[3][(int) MAX_MACHINE_MODE]; + +REAL_VALUE_TYPE dconst0; +REAL_VALUE_TYPE dconst1; +REAL_VALUE_TYPE dconst2; +REAL_VALUE_TYPE dconstm1; + +/* All references to the following fixed hard registers go through + these unique rtl objects. On machines where the frame-pointer and + arg-pointer are the same register, they use the same unique object. + + After register allocation, other rtl objects which used to be pseudo-regs + may be clobbered to refer to the frame-pointer register. + But references that were originally to the frame-pointer can be + distinguished from the others because they contain frame_pointer_rtx. + + In an inline procedure, the stack and frame pointer rtxs may not be + used for anything else. */ +rtx stack_pointer_rtx; /* (REG:Pmode STACK_POINTER_REGNUM) */ +rtx frame_pointer_rtx; /* (REG:Pmode FRAME_POINTER_REGNUM) */ +rtx arg_pointer_rtx; /* (REG:Pmode ARG_POINTER_REGNUM) */ +rtx struct_value_rtx; /* (REG:Pmode STRUCT_VALUE_REGNUM) */ +rtx struct_value_incoming_rtx; /* (REG:Pmode STRUCT_VALUE_INCOMING_REGNUM) */ +rtx static_chain_rtx; /* (REG:Pmode STATIC_CHAIN_REGNUM) */ +rtx static_chain_incoming_rtx; /* (REG:Pmode STATIC_CHAIN_INCOMING_REGNUM) */ +rtx pic_offset_table_rtx; /* (REG:Pmode PIC_OFFSET_TABLE_REGNUM) */ + +rtx virtual_incoming_args_rtx; /* (REG:Pmode VIRTUAL_INCOMING_ARGS_REGNUM) */ +rtx virtual_stack_vars_rtx; /* (REG:Pmode VIRTUAL_STACK_VARS_REGNUM) */ +rtx virtual_stack_dynamic_rtx; /* (REG:Pmode VIRTUAL_STACK_DYNAMIC_REGNUM) */ +rtx virtual_outgoing_args_rtx; /* (REG:Pmode VIRTUAL_OUTGOING_ARGS_REGNUM) */ + +/* We make one copy of (const_int C) where C is in + [- MAX_SAVED_CONST_INT, MAX_SAVED_CONST_INT] + to save space during the compilation and simplify comparisons of + integers. */ + +#define MAX_SAVED_CONST_INT 64 + +static rtx const_int_rtx[MAX_SAVED_CONST_INT * 2 + 1]; + +/* The ends of the doubly-linked chain of rtl for the current function. + Both are reset to null at the start of rtl generation for the function. + + start_sequence saves both of these on `sequence_stack' and then + starts a new, nested sequence of insns. */ + +static rtx first_insn = NULL; +static rtx last_insn = NULL; + +/* INSN_UID for next insn emitted. + Reset to 1 for each function compiled. */ + +static int cur_insn_uid = 1; + +/* Line number and source file of the last line-number NOTE emitted. + This is used to avoid generating duplicates. */ + +static int last_linenum = 0; +static char *last_filename = 0; + +/* A vector indexed by pseudo reg number. The allocated length + of this vector is regno_pointer_flag_length. Since this + vector is needed during the expansion phase when the total + number of registers in the function is not yet known, + it is copied and made bigger when necessary. */ + +char *regno_pointer_flag; +int regno_pointer_flag_length; + +/* Indexed by pseudo register number, gives the rtx for that pseudo. + Allocated in parallel with regno_pointer_flag. */ + +rtx *regno_reg_rtx; + +/* Stack of pending (incomplete) sequences saved by `start_sequence'. + Each element describes one pending sequence. + The main insn-chain is saved in the last element of the chain, + unless the chain is empty. */ + +struct sequence_stack *sequence_stack; + +/* start_sequence and gen_sequence can make a lot of rtx expressions which are + shortly thrown away. We use two mechanisms to prevent this waste: + + First, we keep a list of the expressions used to represent the sequence + stack in sequence_element_free_list. + + Second, for sizes up to 5 elements, we keep a SEQUENCE and its associated + rtvec for use by gen_sequence. One entry for each size is sufficient + because most cases are calls to gen_sequence followed by immediately + emitting the SEQUENCE. Reuse is safe since emitting a sequence is + destructive on the insn in it anyway and hence can't be redone. + + We do not bother to save this cached data over nested function calls. + Instead, we just reinitialize them. */ + +#define SEQUENCE_RESULT_SIZE 5 + +static struct sequence_stack *sequence_element_free_list; +static rtx sequence_result[SEQUENCE_RESULT_SIZE]; + +extern int rtx_equal_function_value_matters; + +/* Filename and line number of last line-number note, + whether we actually emitted it or not. */ +extern char *emit_filename; +extern int emit_lineno; + +rtx change_address (); +void init_emit (); + +/* rtx gen_rtx (code, mode, [element1, ..., elementn]) +** +** This routine generates an RTX of the size specified by +** , which is an RTX code. The RTX structure is initialized +** from the arguments through , which are +** interpreted according to the specific RTX type's format. The +** special machine mode associated with the rtx (if any) is specified +** in . +** +** gen_rtx can be invoked in a way which resembles the lisp-like +** rtx it will generate. For example, the following rtx structure: +** +** (plus:QI (mem:QI (reg:SI 1)) +** (mem:QI (plusw:SI (reg:SI 2) (reg:SI 3)))) +** +** ...would be generated by the following C code: +** +** gen_rtx (PLUS, QImode, +** gen_rtx (MEM, QImode, +** gen_rtx (REG, SImode, 1)), +** gen_rtx (MEM, QImode, +** gen_rtx (PLUS, SImode, +** gen_rtx (REG, SImode, 2), +** gen_rtx (REG, SImode, 3)))), +*/ + +/*VARARGS2*/ +rtx +gen_rtx (va_alist) + va_dcl +{ + va_list p; + enum rtx_code code; + enum machine_mode mode; + register int i; /* Array indices... */ + register char *fmt; /* Current rtx's format... */ + register rtx rt_val; /* RTX to return to caller... */ + + va_start (p); + code = va_arg (p, enum rtx_code); + mode = va_arg (p, enum machine_mode); + + if (code == CONST_INT) + { + HOST_WIDE_INT arg = va_arg (p, HOST_WIDE_INT); + + if (arg >= - MAX_SAVED_CONST_INT && arg <= MAX_SAVED_CONST_INT) + return const_int_rtx[arg + MAX_SAVED_CONST_INT]; + + if (const_true_rtx && arg == STORE_FLAG_VALUE) + return const_true_rtx; + + rt_val = rtx_alloc (code); + INTVAL (rt_val) = arg; + } + else if (code == REG) + { + int regno = va_arg (p, int); + + /* In case the MD file explicitly references the frame pointer, have + all such references point to the same frame pointer. This is used + during frame pointer elimination to distinguish the explicit + references to these registers from pseudos that happened to be + assigned to them. + + If we have eliminated the frame pointer or arg pointer, we will + be using it as a normal register, for example as a spill register. + In such cases, we might be accessing it in a mode that is not + Pmode and therefore cannot use the pre-allocated rtx. + + Also don't do this when we are making new REGs in reload, + since we don't want to get confused with the real pointers. */ + + if (frame_pointer_rtx && regno == FRAME_POINTER_REGNUM && mode == Pmode + && ! reload_in_progress) + return frame_pointer_rtx; +#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + if (arg_pointer_rtx && regno == ARG_POINTER_REGNUM && mode == Pmode + && ! reload_in_progress) + return arg_pointer_rtx; +#endif + if (stack_pointer_rtx && regno == STACK_POINTER_REGNUM && mode == Pmode + && ! reload_in_progress) + return stack_pointer_rtx; + else + { + rt_val = rtx_alloc (code); + rt_val->mode = mode; + REGNO (rt_val) = regno; + return rt_val; + } + } + else + { + rt_val = rtx_alloc (code); /* Allocate the storage space. */ + rt_val->mode = mode; /* Store the machine mode... */ + + fmt = GET_RTX_FORMAT (code); /* Find the right format... */ + for (i = 0; i < GET_RTX_LENGTH (code); i++) + { + switch (*fmt++) + { + case '0': /* Unused field. */ + break; + + case 'i': /* An integer? */ + XINT (rt_val, i) = va_arg (p, int); + break; + + case 'w': /* A wide integer? */ + XWINT (rt_val, i) = va_arg (p, HOST_WIDE_INT); + break; + + case 's': /* A string? */ + XSTR (rt_val, i) = va_arg (p, char *); + break; + + case 'e': /* An expression? */ + case 'u': /* An insn? Same except when printing. */ + XEXP (rt_val, i) = va_arg (p, rtx); + break; + + case 'E': /* An RTX vector? */ + XVEC (rt_val, i) = va_arg (p, rtvec); + break; + + default: + abort (); + } + } + } + va_end (p); + return rt_val; /* Return the new RTX... */ +} + +/* gen_rtvec (n, [rt1, ..., rtn]) +** +** This routine creates an rtvec and stores within it the +** pointers to rtx's which are its arguments. +*/ + +/*VARARGS1*/ +rtvec +gen_rtvec (va_alist) + va_dcl +{ + int n, i; + va_list p; + rtx *vector; + + va_start (p); + n = va_arg (p, int); + + if (n == 0) + return NULL_RTVEC; /* Don't allocate an empty rtvec... */ + + vector = (rtx *) alloca (n * sizeof (rtx)); + for (i = 0; i < n; i++) + vector[i] = va_arg (p, rtx); + va_end (p); + + return gen_rtvec_v (n, vector); +} + +rtvec +gen_rtvec_v (n, argp) + int n; + rtx *argp; +{ + register int i; + register rtvec rt_val; + + if (n == 0) + return NULL_RTVEC; /* Don't allocate an empty rtvec... */ + + rt_val = rtvec_alloc (n); /* Allocate an rtvec... */ + + for (i = 0; i < n; i++) + rt_val->elem[i].rtx = *argp++; + + return rt_val; +} + +/* Generate a REG rtx for a new pseudo register of mode MODE. + This pseudo is assigned the next sequential register number. */ + +rtx +gen_reg_rtx (mode) + enum machine_mode mode; +{ + register rtx val; + + /* Don't let anything called by or after reload create new registers + (actually, registers can't be created after flow, but this is a good + approximation). */ + + if (reload_in_progress || reload_completed) + abort (); + + /* Make sure regno_pointer_flag and regno_reg_rtx are large + enough to have an element for this pseudo reg number. */ + + if (reg_rtx_no == regno_pointer_flag_length) + { + rtx *new1; + char *new = + (char *) oballoc (regno_pointer_flag_length * 2); + bzero (new, regno_pointer_flag_length * 2); + bcopy (regno_pointer_flag, new, regno_pointer_flag_length); + regno_pointer_flag = new; + + new1 = (rtx *) oballoc (regno_pointer_flag_length * 2 * sizeof (rtx)); + bzero (new1, regno_pointer_flag_length * 2 * sizeof (rtx)); + bcopy (regno_reg_rtx, new1, regno_pointer_flag_length * sizeof (rtx)); + regno_reg_rtx = new1; + + regno_pointer_flag_length *= 2; + } + + val = gen_rtx (REG, mode, reg_rtx_no); + regno_reg_rtx[reg_rtx_no++] = val; + return val; +} + +/* Identify REG as a probable pointer register. */ + +void +mark_reg_pointer (reg) + rtx reg; +{ + REGNO_POINTER_FLAG (REGNO (reg)) = 1; +} + +/* Return 1 plus largest pseudo reg number used in the current function. */ + +int +max_reg_num () +{ + return reg_rtx_no; +} + +/* Return 1 + the largest label number used so far in the current function. */ + +int +max_label_num () +{ + if (last_label_num && label_num == base_label_num) + return last_label_num; + return label_num; +} + +/* Return first label number used in this function (if any were used). */ + +int +get_first_label_num () +{ + return first_label_num; +} + +/* Return a value representing some low-order bits of X, where the number + of low-order bits is given by MODE. Note that no conversion is done + between floating-point and fixed-point values, rather, the bit + representation is returned. + + This function handles the cases in common between gen_lowpart, below, + and two variants in cse.c and combine.c. These are the cases that can + be safely handled at all points in the compilation. + + If this is not a case we can handle, return 0. */ + +rtx +gen_lowpart_common (mode, x) + enum machine_mode mode; + register rtx x; +{ + int word = 0; + + if (GET_MODE (x) == mode) + return x; + + /* MODE must occupy no more words than the mode of X. */ + if (GET_MODE (x) != VOIDmode + && ((GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD + > ((GET_MODE_SIZE (GET_MODE (x)) + (UNITS_PER_WORD - 1)) + / UNITS_PER_WORD))) + return 0; + + if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD) + word = ((GET_MODE_SIZE (GET_MODE (x)) + - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD)) + / UNITS_PER_WORD); + + if ((GET_CODE (x) == ZERO_EXTEND || GET_CODE (x) == SIGN_EXTEND) + && (GET_MODE_CLASS (mode) == MODE_INT + || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT)) + { + /* If we are getting the low-order part of something that has been + sign- or zero-extended, we can either just use the object being + extended or make a narrower extension. If we want an even smaller + piece than the size of the object being extended, call ourselves + recursively. + + This case is used mostly by combine and cse. */ + + if (GET_MODE (XEXP (x, 0)) == mode) + return XEXP (x, 0); + else if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (XEXP (x, 0)))) + return gen_lowpart_common (mode, XEXP (x, 0)); + else if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (x))) + return gen_rtx (GET_CODE (x), mode, XEXP (x, 0)); + } + else if (GET_CODE (x) == SUBREG + && (GET_MODE_SIZE (mode) <= UNITS_PER_WORD + || GET_MODE_SIZE (mode) == GET_MODE_UNIT_SIZE (GET_MODE (x)))) + return (GET_MODE (SUBREG_REG (x)) == mode && SUBREG_WORD (x) == 0 + ? SUBREG_REG (x) + : gen_rtx (SUBREG, mode, SUBREG_REG (x), SUBREG_WORD (x))); + else if (GET_CODE (x) == REG) + { + /* If the register is not valid for MODE, return 0. If we don't + do this, there is no way to fix up the resulting REG later. */ + if (REGNO (x) < FIRST_PSEUDO_REGISTER + && ! HARD_REGNO_MODE_OK (REGNO (x) + word, mode)) + return 0; + else if (REGNO (x) < FIRST_PSEUDO_REGISTER + /* integrate.c can't handle parts of a return value register. */ + && (! REG_FUNCTION_VALUE_P (x) + || ! rtx_equal_function_value_matters) + /* We want to keep the stack, frame, and arg pointers + special. */ + && REGNO (x) != FRAME_POINTER_REGNUM +#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + && REGNO (x) != ARG_POINTER_REGNUM +#endif + && REGNO (x) != STACK_POINTER_REGNUM) + return gen_rtx (REG, mode, REGNO (x) + word); + else + return gen_rtx (SUBREG, mode, x, word); + } + + /* If X is a CONST_INT or a CONST_DOUBLE, extract the appropriate bits + from the low-order part of the constant. */ + else if ((GET_MODE_CLASS (mode) == MODE_INT + || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT) + && GET_MODE (x) == VOIDmode + && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE)) + { + /* If MODE is twice the host word size, X is already the desired + representation. Otherwise, if MODE is wider than a word, we can't + do this. If MODE is exactly a word, return just one CONST_INT. + If MODE is smaller than a word, clear the bits that don't belong + in our mode, unless they and our sign bit are all one. So we get + either a reasonable negative value or a reasonable unsigned value + for this mode. */ + + if (GET_MODE_BITSIZE (mode) == 2 * HOST_BITS_PER_WIDE_INT) + return x; + else if (GET_MODE_BITSIZE (mode) > HOST_BITS_PER_WIDE_INT) + return 0; + else if (GET_MODE_BITSIZE (mode) == HOST_BITS_PER_WIDE_INT) + return (GET_CODE (x) == CONST_INT ? x + : GEN_INT (CONST_DOUBLE_LOW (x))); + else + { + /* MODE must be narrower than HOST_BITS_PER_INT. */ + int width = GET_MODE_BITSIZE (mode); + HOST_WIDE_INT val = (GET_CODE (x) == CONST_INT ? INTVAL (x) + : CONST_DOUBLE_LOW (x)); + + if (((val & ((HOST_WIDE_INT) (-1) << (width - 1))) + != ((HOST_WIDE_INT) (-1) << (width - 1)))) + val &= ((HOST_WIDE_INT) 1 << width) - 1; + + return (GET_CODE (x) == CONST_INT && INTVAL (x) == val ? x + : GEN_INT (val)); + } + } + + /* If X is an integral constant but we want it in floating-point, it + must be the case that we have a union of an integer and a floating-point + value. If the machine-parameters allow it, simulate that union here + and return the result. The two-word and single-word cases are + different. */ + + else if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT + && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD) + || flag_pretend_float) + && GET_MODE_CLASS (mode) == MODE_FLOAT + && GET_MODE_SIZE (mode) == UNITS_PER_WORD + && GET_CODE (x) == CONST_INT + && sizeof (float) * HOST_BITS_PER_CHAR == HOST_BITS_PER_WIDE_INT) +#ifdef REAL_ARITHMETIC + { + REAL_VALUE_TYPE r; + HOST_WIDE_INT i; + + i = INTVAL (x); + r = REAL_VALUE_FROM_TARGET_SINGLE (i); + return immed_real_const_1 (r, mode); + } +#else + { + union {HOST_WIDE_INT i; float d; } u; + + u.i = INTVAL (x); + return immed_real_const_1 (u.d, mode); + } +#endif + else if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT + && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD) + || flag_pretend_float) + && GET_MODE_CLASS (mode) == MODE_FLOAT + && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD + && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE) + && GET_MODE (x) == VOIDmode + && (sizeof (double) * HOST_BITS_PER_CHAR + == 2 * HOST_BITS_PER_WIDE_INT)) +#ifdef REAL_ARITHMETIC + { + REAL_VALUE_TYPE r; + HOST_WIDE_INT i[2]; + HOST_WIDE_INT low, high; + + if (GET_CODE (x) == CONST_INT) + low = INTVAL (x), high = low >> (HOST_BITS_PER_WIDE_INT -1); + else + low = CONST_DOUBLE_LOW (x), high = CONST_DOUBLE_HIGH (x); + +/* TARGET_DOUBLE takes the addressing order of the target machine. */ +#ifdef WORDS_BIG_ENDIAN + i[0] = high, i[1] = low; +#else + i[0] = low, i[1] = high; +#endif + + r = REAL_VALUE_FROM_TARGET_DOUBLE (i); + return immed_real_const_1 (r, mode); + } +#else + { + union {HOST_WIDE_INT i[2]; double d; } u; + HOST_WIDE_INT low, high; + + if (GET_CODE (x) == CONST_INT) + low = INTVAL (x), high = low >> (HOST_BITS_PER_WIDE_INT -1); + else + low = CONST_DOUBLE_LOW (x), high = CONST_DOUBLE_HIGH (x); + +#ifdef HOST_WORDS_BIG_ENDIAN + u.i[0] = high, u.i[1] = low; +#else + u.i[0] = low, u.i[1] = high; +#endif + + return immed_real_const_1 (u.d, mode); + } +#endif + /* Similarly, if this is converting a floating-point value into a + single-word integer. Only do this is the host and target parameters are + compatible. */ + + else if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT + && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD) + || flag_pretend_float) + && (GET_MODE_CLASS (mode) == MODE_INT + || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT) + && GET_CODE (x) == CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT + && GET_MODE_BITSIZE (mode) == BITS_PER_WORD) + return operand_subword (x, 0, 0, GET_MODE (x)); + + /* Similarly, if this is converting a floating-point value into a + two-word integer, we can do this one word at a time and make an + integer. Only do this is the host and target parameters are + compatible. */ + + else if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT + && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD) + || flag_pretend_float) + && (GET_MODE_CLASS (mode) == MODE_INT + || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT) + && GET_CODE (x) == CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT + && GET_MODE_BITSIZE (mode) == 2 * BITS_PER_WORD) + { + rtx lowpart = operand_subword (x, WORDS_BIG_ENDIAN, 0, GET_MODE (x)); + rtx highpart = operand_subword (x, ! WORDS_BIG_ENDIAN, 0, GET_MODE (x)); + + if (lowpart && GET_CODE (lowpart) == CONST_INT + && highpart && GET_CODE (highpart) == CONST_INT) + return immed_double_const (INTVAL (lowpart), INTVAL (highpart), mode); + } + + /* Otherwise, we can't do this. */ + return 0; +} + +/* Return the real part (which has mode MODE) of a complex value X. + This always comes at the low address in memory. */ + +rtx +gen_realpart (mode, x) + enum machine_mode mode; + register rtx x; +{ + if (WORDS_BIG_ENDIAN) + return gen_highpart (mode, x); + else + return gen_lowpart (mode, x); +} + +/* Return the imaginary part (which has mode MODE) of a complex value X. + This always comes at the high address in memory. */ + +rtx +gen_imagpart (mode, x) + enum machine_mode mode; + register rtx x; +{ + if (WORDS_BIG_ENDIAN) + return gen_lowpart (mode, x); + else + return gen_highpart (mode, x); +} + +/* Assuming that X is an rtx (e.g., MEM, REG or SUBREG) for a value, + return an rtx (MEM, SUBREG, or CONST_INT) that refers to the + least-significant part of X. + MODE specifies how big a part of X to return; + it usually should not be larger than a word. + If X is a MEM whose address is a QUEUED, the value may be so also. */ + +rtx +gen_lowpart (mode, x) + enum machine_mode mode; + register rtx x; +{ + rtx result = gen_lowpart_common (mode, x); + + if (result) + return result; + else if (GET_CODE (x) == MEM) + { + /* The only additional case we can do is MEM. */ + register int offset = 0; + if (WORDS_BIG_ENDIAN) + offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD) + - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD)); + + if (BYTES_BIG_ENDIAN) + /* Adjust the address so that the address-after-the-data + is unchanged. */ + offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)) + - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x)))); + + return change_address (x, mode, plus_constant (XEXP (x, 0), offset)); + } + else + abort (); +} + +/* Like `gen_lowpart', but refer to the most significant part. + This is used to access the imaginary part of a complex number. */ + +rtx +gen_highpart (mode, x) + enum machine_mode mode; + register rtx x; +{ + /* This case loses if X is a subreg. To catch bugs early, + complain if an invalid MODE is used even in other cases. */ + if (GET_MODE_SIZE (mode) > UNITS_PER_WORD + && GET_MODE_SIZE (mode) != GET_MODE_UNIT_SIZE (GET_MODE (x))) + abort (); + if (GET_CODE (x) == CONST_DOUBLE +#if !(TARGET_FLOAT_FORMAT != HOST_FLOAT_FORMAT || defined (REAL_IS_NOT_DOUBLE)) + && GET_MODE_CLASS (GET_MODE (x)) != MODE_FLOAT +#endif + ) + return gen_rtx (CONST_INT, VOIDmode, + CONST_DOUBLE_HIGH (x) & GET_MODE_MASK (mode)); + else if (GET_CODE (x) == CONST_INT) + return const0_rtx; + else if (GET_CODE (x) == MEM) + { + register int offset = 0; +#if !WORDS_BIG_ENDIAN + offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD) + - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD)); +#endif +#if !BYTES_BIG_ENDIAN + if (GET_MODE_SIZE (mode) < UNITS_PER_WORD) + offset -= (GET_MODE_SIZE (mode) + - MIN (UNITS_PER_WORD, + GET_MODE_SIZE (GET_MODE (x)))); +#endif + return change_address (x, mode, plus_constant (XEXP (x, 0), offset)); + } + else if (GET_CODE (x) == SUBREG) + { + /* The only time this should occur is when we are looking at a + multi-word item with a SUBREG whose mode is the same as that of the + item. It isn't clear what we would do if it wasn't. */ + if (SUBREG_WORD (x) != 0) + abort (); + return gen_highpart (mode, SUBREG_REG (x)); + } + else if (GET_CODE (x) == REG) + { + int word = 0; + +#if !WORDS_BIG_ENDIAN + if (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD) + word = ((GET_MODE_SIZE (GET_MODE (x)) + - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD)) + / UNITS_PER_WORD); +#endif + if (REGNO (x) < FIRST_PSEUDO_REGISTER + /* We want to keep the stack, frame, and arg pointers special. */ + && REGNO (x) != FRAME_POINTER_REGNUM +#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + && REGNO (x) != ARG_POINTER_REGNUM +#endif + && REGNO (x) != STACK_POINTER_REGNUM) + return gen_rtx (REG, mode, REGNO (x) + word); + else + return gen_rtx (SUBREG, mode, x, word); + } + else + abort (); +} + +/* Return 1 iff X, assumed to be a SUBREG, + refers to the least significant part of its containing reg. + If X is not a SUBREG, always return 1 (it is its own low part!). */ + +int +subreg_lowpart_p (x) + rtx x; +{ + if (GET_CODE (x) != SUBREG) + return 1; + + if (WORDS_BIG_ENDIAN + && GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) > UNITS_PER_WORD) + return (SUBREG_WORD (x) + == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) + - MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD)) + / UNITS_PER_WORD)); + + return SUBREG_WORD (x) == 0; +} + +/* Return subword I of operand OP. + The word number, I, is interpreted as the word number starting at the + low-order address. Word 0 is the low-order word if not WORDS_BIG_ENDIAN, + otherwise it is the high-order word. + + If we cannot extract the required word, we return zero. Otherwise, an + rtx corresponding to the requested word will be returned. + + VALIDATE_ADDRESS is nonzero if the address should be validated. Before + reload has completed, a valid address will always be returned. After + reload, if a valid address cannot be returned, we return zero. + + If VALIDATE_ADDRESS is zero, we simply form the required address; validating + it is the responsibility of the caller. + + MODE is the mode of OP in case it is a CONST_INT. */ + +rtx +operand_subword (op, i, validate_address, mode) + rtx op; + int i; + int validate_address; + enum machine_mode mode; +{ + HOST_WIDE_INT val; + int size_ratio = HOST_BITS_PER_WIDE_INT / BITS_PER_WORD; + + if (mode == VOIDmode) + mode = GET_MODE (op); + + if (mode == VOIDmode) + abort (); + + /* If OP is narrower than a word or if we want a word outside OP, fail. */ + if (mode != BLKmode + && (GET_MODE_SIZE (mode) < UNITS_PER_WORD + || (i + 1) * UNITS_PER_WORD > GET_MODE_SIZE (mode))) + return 0; + + /* If OP is already an integer word, return it. */ + if (GET_MODE_CLASS (mode) == MODE_INT + && GET_MODE_SIZE (mode) == UNITS_PER_WORD) + return op; + + /* If OP is a REG or SUBREG, we can handle it very simply. */ + if (GET_CODE (op) == REG) + { + /* If the register is not valid for MODE, return 0. If we don't + do this, there is no way to fix up the resulting REG later. */ + if (REGNO (op) < FIRST_PSEUDO_REGISTER + && ! HARD_REGNO_MODE_OK (REGNO (op) + i, word_mode)) + return 0; + else if (REGNO (op) >= FIRST_PSEUDO_REGISTER + || (REG_FUNCTION_VALUE_P (op) + && rtx_equal_function_value_matters) + /* We want to keep the stack, frame, and arg pointers + special. */ + || REGNO (op) == FRAME_POINTER_REGNUM +#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + || REGNO (op) == ARG_POINTER_REGNUM +#endif + || REGNO (op) == STACK_POINTER_REGNUM) + return gen_rtx (SUBREG, word_mode, op, i); + else + return gen_rtx (REG, word_mode, REGNO (op) + i); + } + else if (GET_CODE (op) == SUBREG) + return gen_rtx (SUBREG, word_mode, SUBREG_REG (op), i + SUBREG_WORD (op)); + + /* Form a new MEM at the requested address. */ + if (GET_CODE (op) == MEM) + { + rtx addr = plus_constant (XEXP (op, 0), i * UNITS_PER_WORD); + rtx new; + + if (validate_address) + { + if (reload_completed) + { + if (! strict_memory_address_p (word_mode, addr)) + return 0; + } + else + addr = memory_address (word_mode, addr); + } + + new = gen_rtx (MEM, word_mode, addr); + + MEM_VOLATILE_P (new) = MEM_VOLATILE_P (op); + MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (op); + RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (op); + + return new; + } + + /* The only remaining cases are when OP is a constant. If the host and + target floating formats are the same, handling two-word floating + constants are easy. Note that REAL_VALUE_TO_TARGET_{SINGLE,DOUBLE} + are defined as returning 32 bit and 64-bit values, respectively, + and not values of BITS_PER_WORD and 2 * BITS_PER_WORD bits. */ +#ifdef REAL_ARITHMETIC + if ((HOST_BITS_PER_WIDE_INT == BITS_PER_WORD) + && GET_MODE_CLASS (mode) == MODE_FLOAT + && GET_MODE_BITSIZE (mode) == 64 + && GET_CODE (op) == CONST_DOUBLE) + { + HOST_WIDE_INT k[2]; + REAL_VALUE_TYPE rv; + + REAL_VALUE_FROM_CONST_DOUBLE (rv, op); + REAL_VALUE_TO_TARGET_DOUBLE (rv, k); + + /* We handle 32-bit and 64-bit host words here. Note that the order in + which the words are written depends on the word endianness. + + ??? This is a potential portability problem and should + be fixed at some point. */ + if (HOST_BITS_PER_WIDE_INT == 32) + return GEN_INT (k[i]); + else if (HOST_BITS_PER_WIDE_INT == 64 && i == 0) + return GEN_INT ((k[! WORDS_BIG_ENDIAN] << (HOST_BITS_PER_WIDE_INT / 2)) + | k[WORDS_BIG_ENDIAN]); + else + abort (); + } +#else /* no REAL_ARITHMETIC */ + if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT + && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD) + || flag_pretend_float) + && GET_MODE_CLASS (mode) == MODE_FLOAT + && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD + && GET_CODE (op) == CONST_DOUBLE) + { + /* The constant is stored in the host's word-ordering, + but we want to access it in the target's word-ordering. Some + compilers don't like a conditional inside macro args, so we have two + copies of the return. */ +#ifdef HOST_WORDS_BIG_ENDIAN + return GEN_INT (i == WORDS_BIG_ENDIAN + ? CONST_DOUBLE_HIGH (op) : CONST_DOUBLE_LOW (op)); +#else + return GEN_INT (i != WORDS_BIG_ENDIAN + ? CONST_DOUBLE_HIGH (op) : CONST_DOUBLE_LOW (op)); +#endif + } +#endif /* no REAL_ARITHMETIC */ + + /* Single word float is a little harder, since single- and double-word + values often do not have the same high-order bits. We have already + verified that we want the only defined word of the single-word value. */ +#ifdef REAL_ARITHMETIC + if ((HOST_BITS_PER_WIDE_INT == BITS_PER_WORD) + && GET_MODE_CLASS (mode) == MODE_FLOAT + && GET_MODE_BITSIZE (mode) == 32 + && GET_CODE (op) == CONST_DOUBLE) + { + HOST_WIDE_INT l; + REAL_VALUE_TYPE rv; + + REAL_VALUE_FROM_CONST_DOUBLE (rv, op); + REAL_VALUE_TO_TARGET_SINGLE (rv, l); + return GEN_INT (l); + } +#else + if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT + && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD) + || flag_pretend_float) + && GET_MODE_CLASS (mode) == MODE_FLOAT + && GET_MODE_SIZE (mode) == UNITS_PER_WORD + && GET_CODE (op) == CONST_DOUBLE) + { + double d; + union {float f; HOST_WIDE_INT i; } u; + + REAL_VALUE_FROM_CONST_DOUBLE (d, op); + + u.f = d; + return GEN_INT (u.i); + } +#endif /* no REAL_ARITHMETIC */ + + /* The only remaining cases that we can handle are integers. + Convert to proper endianness now since these cases need it. + At this point, i == 0 means the low-order word. + + We do not want to handle the case when BITS_PER_WORD <= HOST_BITS_PER_INT + in general. However, if OP is (const_int 0), we can just return + it for any word. */ + + if (op == const0_rtx) + return op; + + if (GET_MODE_CLASS (mode) != MODE_INT + || (GET_CODE (op) != CONST_INT && GET_CODE (op) != CONST_DOUBLE) + || BITS_PER_WORD > HOST_BITS_PER_INT) + return 0; + + if (WORDS_BIG_ENDIAN) + i = GET_MODE_SIZE (mode) / UNITS_PER_WORD - 1 - i; + + /* Find out which word on the host machine this value is in and get + it from the constant. */ + val = (i / size_ratio == 0 + ? (GET_CODE (op) == CONST_INT ? INTVAL (op) : CONST_DOUBLE_LOW (op)) + : (GET_CODE (op) == CONST_INT + ? (INTVAL (op) < 0 ? ~0 : 0) : CONST_DOUBLE_HIGH (op))); + + /* If BITS_PER_WORD is smaller than an int, get the appropriate bits. */ + if (BITS_PER_WORD < HOST_BITS_PER_WIDE_INT) + val = ((val >> ((i % size_ratio) * BITS_PER_WORD)) + & (((HOST_WIDE_INT) 1 + << (BITS_PER_WORD % HOST_BITS_PER_WIDE_INT)) - 1)); + + return GEN_INT (val); +} + +/* Similar to `operand_subword', but never return 0. If we can't extract + the required subword, put OP into a register and try again. If that fails, + abort. We always validate the address in this case. It is not valid + to call this function after reload; it is mostly meant for RTL + generation. + + MODE is the mode of OP, in case it is CONST_INT. */ + +rtx +operand_subword_force (op, i, mode) + rtx op; + int i; + enum machine_mode mode; +{ + rtx result = operand_subword (op, i, 1, mode); + + if (result) + return result; + + if (mode != BLKmode && mode != VOIDmode) + op = force_reg (mode, op); + + result = operand_subword (op, i, 1, mode); + if (result == 0) + abort (); + + return result; +} + +/* Given a compare instruction, swap the operands. + A test instruction is changed into a compare of 0 against the operand. */ + +void +reverse_comparison (insn) + rtx insn; +{ + rtx body = PATTERN (insn); + rtx comp; + + if (GET_CODE (body) == SET) + comp = SET_SRC (body); + else + comp = SET_SRC (XVECEXP (body, 0, 0)); + + if (GET_CODE (comp) == COMPARE) + { + rtx op0 = XEXP (comp, 0); + rtx op1 = XEXP (comp, 1); + XEXP (comp, 0) = op1; + XEXP (comp, 1) = op0; + } + else + { + rtx new = gen_rtx (COMPARE, VOIDmode, + CONST0_RTX (GET_MODE (comp)), comp); + if (GET_CODE (body) == SET) + SET_SRC (body) = new; + else + SET_SRC (XVECEXP (body, 0, 0)) = new; + } +} + +/* Return a memory reference like MEMREF, but with its mode changed + to MODE and its address changed to ADDR. + (VOIDmode means don't change the mode. + NULL for ADDR means don't change the address.) */ + +rtx +change_address (memref, mode, addr) + rtx memref; + enum machine_mode mode; + rtx addr; +{ + rtx new; + + if (GET_CODE (memref) != MEM) + abort (); + if (mode == VOIDmode) + mode = GET_MODE (memref); + if (addr == 0) + addr = XEXP (memref, 0); + + /* If reload is in progress or has completed, ADDR must be valid. + Otherwise, we can call memory_address to make it valid. */ + if (reload_completed || reload_in_progress) + { + if (! memory_address_p (mode, addr)) + abort (); + } + else + addr = memory_address (mode, addr); + + new = gen_rtx (MEM, mode, addr); + MEM_VOLATILE_P (new) = MEM_VOLATILE_P (memref); + RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (memref); + MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (memref); + return new; +} + +/* Return a newly created CODE_LABEL rtx with a unique label number. */ + +rtx +gen_label_rtx () +{ + register rtx label = gen_rtx (CODE_LABEL, VOIDmode, 0, 0, 0, + label_num++, NULL_PTR); + LABEL_NUSES (label) = 0; + return label; +} + +/* For procedure integration. */ + +/* Return a newly created INLINE_HEADER rtx. Should allocate this + from a permanent obstack when the opportunity arises. */ + +rtx +gen_inline_header_rtx (first_insn, first_parm_insn, first_labelno, + last_labelno, max_parm_regnum, max_regnum, args_size, + pops_args, stack_slots, function_flags, + outgoing_args_size, original_arg_vector, + original_decl_initial) + rtx first_insn, first_parm_insn; + int first_labelno, last_labelno, max_parm_regnum, max_regnum, args_size; + int pops_args; + rtx stack_slots; + int function_flags; + int outgoing_args_size; + rtvec original_arg_vector; + rtx original_decl_initial; +{ + rtx header = gen_rtx (INLINE_HEADER, VOIDmode, + cur_insn_uid++, NULL_RTX, + first_insn, first_parm_insn, + first_labelno, last_labelno, + max_parm_regnum, max_regnum, args_size, pops_args, + stack_slots, function_flags, outgoing_args_size, + original_arg_vector, original_decl_initial); + return header; +} + +/* Install new pointers to the first and last insns in the chain. + Used for an inline-procedure after copying the insn chain. */ + +void +set_new_first_and_last_insn (first, last) + rtx first, last; +{ + first_insn = first; + last_insn = last; +} + +/* Set the range of label numbers found in the current function. + This is used when belatedly compiling an inline function. */ + +void +set_new_first_and_last_label_num (first, last) + int first, last; +{ + base_label_num = label_num; + first_label_num = first; + last_label_num = last; +} + +/* Save all variables describing the current status into the structure *P. + This is used before starting a nested function. */ + +void +save_emit_status (p) + struct function *p; +{ + p->reg_rtx_no = reg_rtx_no; + p->first_label_num = first_label_num; + p->first_insn = first_insn; + p->last_insn = last_insn; + p->sequence_stack = sequence_stack; + p->cur_insn_uid = cur_insn_uid; + p->last_linenum = last_linenum; + p->last_filename = last_filename; + p->regno_pointer_flag = regno_pointer_flag; + p->regno_pointer_flag_length = regno_pointer_flag_length; + p->regno_reg_rtx = regno_reg_rtx; +} + +/* Restore all variables describing the current status from the structure *P. + This is used after a nested function. */ + +void +restore_emit_status (p) + struct function *p; +{ + int i; + + reg_rtx_no = p->reg_rtx_no; + first_label_num = p->first_label_num; + first_insn = p->first_insn; + last_insn = p->last_insn; + sequence_stack = p->sequence_stack; + cur_insn_uid = p->cur_insn_uid; + last_linenum = p->last_linenum; + last_filename = p->last_filename; + regno_pointer_flag = p->regno_pointer_flag; + regno_pointer_flag_length = p->regno_pointer_flag_length; + regno_reg_rtx = p->regno_reg_rtx; + + /* Clear our cache of rtx expressions for start_sequence and gen_sequence. */ + sequence_element_free_list = 0; + for (i = 0; i < SEQUENCE_RESULT_SIZE; i++) + sequence_result[i] = 0; +} + +/* Go through all the RTL insn bodies and copy any invalid shared structure. + It does not work to do this twice, because the mark bits set here + are not cleared afterwards. */ + +void +unshare_all_rtl (insn) + register rtx insn; +{ + for (; insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN + || GET_CODE (insn) == CALL_INSN) + { + PATTERN (insn) = copy_rtx_if_shared (PATTERN (insn)); + REG_NOTES (insn) = copy_rtx_if_shared (REG_NOTES (insn)); + LOG_LINKS (insn) = copy_rtx_if_shared (LOG_LINKS (insn)); + } + + /* Make sure the addresses of stack slots found outside the insn chain + (such as, in DECL_RTL of a variable) are not shared + with the insn chain. + + This special care is necessary when the stack slot MEM does not + actually appear in the insn chain. If it does appear, its address + is unshared from all else at that point. */ + + copy_rtx_if_shared (stack_slot_list); +} + +/* Mark ORIG as in use, and return a copy of it if it was already in use. + Recursively does the same for subexpressions. */ + +rtx +copy_rtx_if_shared (orig) + rtx orig; +{ + register rtx x = orig; + register int i; + register enum rtx_code code; + register char *format_ptr; + int copied = 0; + + if (x == 0) + return 0; + + code = GET_CODE (x); + + /* These types may be freely shared. */ + + switch (code) + { + case REG: + case QUEUED: + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + case CODE_LABEL: + case PC: + case CC0: + case SCRATCH: + /* SCRATCH must be shared because they represent distinct values. */ + return x; + + case CONST: + /* CONST can be shared if it contains a SYMBOL_REF. If it contains + a LABEL_REF, it isn't sharable. */ + if (GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) + return x; + break; + + case INSN: + case JUMP_INSN: + case CALL_INSN: + case NOTE: + case LABEL_REF: + case BARRIER: + /* The chain of insns is not being copied. */ + return x; + + case MEM: + /* A MEM is allowed to be shared if its address is constant + or is a constant plus one of the special registers. */ + if (CONSTANT_ADDRESS_P (XEXP (x, 0)) + || XEXP (x, 0) == virtual_stack_vars_rtx + || XEXP (x, 0) == virtual_incoming_args_rtx) + return x; + + if (GET_CODE (XEXP (x, 0)) == PLUS + && (XEXP (XEXP (x, 0), 0) == virtual_stack_vars_rtx + || XEXP (XEXP (x, 0), 0) == virtual_incoming_args_rtx) + && CONSTANT_ADDRESS_P (XEXP (XEXP (x, 0), 1))) + { + /* This MEM can appear in more than one place, + but its address better not be shared with anything else. */ + if (! x->used) + XEXP (x, 0) = copy_rtx_if_shared (XEXP (x, 0)); + x->used = 1; + return x; + } + } + + /* This rtx may not be shared. If it has already been seen, + replace it with a copy of itself. */ + + if (x->used) + { + register rtx copy; + + copy = rtx_alloc (code); + bcopy (x, copy, (sizeof (*copy) - sizeof (copy->fld) + + sizeof (copy->fld[0]) * GET_RTX_LENGTH (code))); + x = copy; + copied = 1; + } + x->used = 1; + + /* Now scan the subexpressions recursively. + We can store any replaced subexpressions directly into X + since we know X is not shared! Any vectors in X + must be copied if X was copied. */ + + format_ptr = GET_RTX_FORMAT (code); + + for (i = 0; i < GET_RTX_LENGTH (code); i++) + { + switch (*format_ptr++) + { + case 'e': + XEXP (x, i) = copy_rtx_if_shared (XEXP (x, i)); + break; + + case 'E': + if (XVEC (x, i) != NULL) + { + register int j; + + if (copied) + XVEC (x, i) = gen_rtvec_v (XVECLEN (x, i), &XVECEXP (x, i, 0)); + for (j = 0; j < XVECLEN (x, i); j++) + XVECEXP (x, i, j) + = copy_rtx_if_shared (XVECEXP (x, i, j)); + } + break; + } + } + return x; +} + +/* Clear all the USED bits in X to allow copy_rtx_if_shared to be used + to look for shared sub-parts. */ + +void +reset_used_flags (x) + rtx x; +{ + register int i, j; + register enum rtx_code code; + register char *format_ptr; + int copied = 0; + + if (x == 0) + return; + + code = GET_CODE (x); + + /* These types may be freely shared so we needn't do any reseting + for them. */ + + switch (code) + { + case REG: + case QUEUED: + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + case CODE_LABEL: + case PC: + case CC0: + return; + + case INSN: + case JUMP_INSN: + case CALL_INSN: + case NOTE: + case LABEL_REF: + case BARRIER: + /* The chain of insns is not being copied. */ + return; + } + + x->used = 0; + + format_ptr = GET_RTX_FORMAT (code); + for (i = 0; i < GET_RTX_LENGTH (code); i++) + { + switch (*format_ptr++) + { + case 'e': + reset_used_flags (XEXP (x, i)); + break; + + case 'E': + for (j = 0; j < XVECLEN (x, i); j++) + reset_used_flags (XVECEXP (x, i, j)); + break; + } + } +} + +/* Copy X if necessary so that it won't be altered by changes in OTHER. + Return X or the rtx for the pseudo reg the value of X was copied into. + OTHER must be valid as a SET_DEST. */ + +rtx +make_safe_from (x, other) + rtx x, other; +{ + while (1) + switch (GET_CODE (other)) + { + case SUBREG: + other = SUBREG_REG (other); + break; + case STRICT_LOW_PART: + case SIGN_EXTEND: + case ZERO_EXTEND: + other = XEXP (other, 0); + break; + default: + goto done; + } + done: + if ((GET_CODE (other) == MEM + && ! CONSTANT_P (x) + && GET_CODE (x) != REG + && GET_CODE (x) != SUBREG) + || (GET_CODE (other) == REG + && (REGNO (other) < FIRST_PSEUDO_REGISTER + || reg_mentioned_p (other, x)))) + { + rtx temp = gen_reg_rtx (GET_MODE (x)); + emit_move_insn (temp, x); + return temp; + } + return x; +} + +/* Emission of insns (adding them to the doubly-linked list). */ + +/* Return the first insn of the current sequence or current function. */ + +rtx +get_insns () +{ + return first_insn; +} + +/* Return the last insn emitted in current sequence or current function. */ + +rtx +get_last_insn () +{ + return last_insn; +} + +/* Specify a new insn as the last in the chain. */ + +void +set_last_insn (insn) + rtx insn; +{ + if (NEXT_INSN (insn) != 0) + abort (); + last_insn = insn; +} + +/* Return the last insn emitted, even if it is in a sequence now pushed. */ + +rtx +get_last_insn_anywhere () +{ + struct sequence_stack *stack; + if (last_insn) + return last_insn; + for (stack = sequence_stack; stack; stack = stack->next) + if (stack->last != 0) + return stack->last; + return 0; +} + +/* Return a number larger than any instruction's uid in this function. */ + +int +get_max_uid () +{ + return cur_insn_uid; +} + +/* Return the next insn. If it is a SEQUENCE, return the first insn + of the sequence. */ + +rtx +next_insn (insn) + rtx insn; +{ + if (insn) + { + insn = NEXT_INSN (insn); + if (insn && GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SEQUENCE) + insn = XVECEXP (PATTERN (insn), 0, 0); + } + + return insn; +} + +/* Return the previous insn. If it is a SEQUENCE, return the last insn + of the sequence. */ + +rtx +previous_insn (insn) + rtx insn; +{ + if (insn) + { + insn = PREV_INSN (insn); + if (insn && GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SEQUENCE) + insn = XVECEXP (PATTERN (insn), 0, XVECLEN (PATTERN (insn), 0) - 1); + } + + return insn; +} + +/* Return the next insn after INSN that is not a NOTE. This routine does not + look inside SEQUENCEs. */ + +rtx +next_nonnote_insn (insn) + rtx insn; +{ + while (insn) + { + insn = NEXT_INSN (insn); + if (insn == 0 || GET_CODE (insn) != NOTE) + break; + } + + return insn; +} + +/* Return the previous insn before INSN that is not a NOTE. This routine does + not look inside SEQUENCEs. */ + +rtx +prev_nonnote_insn (insn) + rtx insn; +{ + while (insn) + { + insn = PREV_INSN (insn); + if (insn == 0 || GET_CODE (insn) != NOTE) + break; + } + + return insn; +} + +/* Return the next INSN, CALL_INSN or JUMP_INSN after INSN; + or 0, if there is none. This routine does not look inside + SEQUENCEs. */ + +rtx +next_real_insn (insn) + rtx insn; +{ + while (insn) + { + insn = NEXT_INSN (insn); + if (insn == 0 || GET_CODE (insn) == INSN + || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN) + break; + } + + return insn; +} + +/* Return the last INSN, CALL_INSN or JUMP_INSN before INSN; + or 0, if there is none. This routine does not look inside + SEQUENCEs. */ + +rtx +prev_real_insn (insn) + rtx insn; +{ + while (insn) + { + insn = PREV_INSN (insn); + if (insn == 0 || GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN + || GET_CODE (insn) == JUMP_INSN) + break; + } + + return insn; +} + +/* Find the next insn after INSN that really does something. This routine + does not look inside SEQUENCEs. Until reload has completed, this is the + same as next_real_insn. */ + +rtx +next_active_insn (insn) + rtx insn; +{ + while (insn) + { + insn = NEXT_INSN (insn); + if (insn == 0 + || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN + || (GET_CODE (insn) == INSN + && (! reload_completed + || (GET_CODE (PATTERN (insn)) != USE + && GET_CODE (PATTERN (insn)) != CLOBBER)))) + break; + } + + return insn; +} + +/* Find the last insn before INSN that really does something. This routine + does not look inside SEQUENCEs. Until reload has completed, this is the + same as prev_real_insn. */ + +rtx +prev_active_insn (insn) + rtx insn; +{ + while (insn) + { + insn = PREV_INSN (insn); + if (insn == 0 + || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN + || (GET_CODE (insn) == INSN + && (! reload_completed + || (GET_CODE (PATTERN (insn)) != USE + && GET_CODE (PATTERN (insn)) != CLOBBER)))) + break; + } + + return insn; +} + +/* Return the next CODE_LABEL after the insn INSN, or 0 if there is none. */ + +rtx +next_label (insn) + rtx insn; +{ + while (insn) + { + insn = NEXT_INSN (insn); + if (insn == 0 || GET_CODE (insn) == CODE_LABEL) + break; + } + + return insn; +} + +/* Return the last CODE_LABEL before the insn INSN, or 0 if there is none. */ + +rtx +prev_label (insn) + rtx insn; +{ + while (insn) + { + insn = PREV_INSN (insn); + if (insn == 0 || GET_CODE (insn) == CODE_LABEL) + break; + } + + return insn; +} + +#ifdef HAVE_cc0 +/* INSN uses CC0 and is being moved into a delay slot. Set up REG_CC_SETTER + and REG_CC_USER notes so we can find it. */ + +void +link_cc0_insns (insn) + rtx insn; +{ + rtx user = next_nonnote_insn (insn); + + if (GET_CODE (user) == INSN && GET_CODE (PATTERN (user)) == SEQUENCE) + user = XVECEXP (PATTERN (user), 0, 0); + + REG_NOTES (user) = gen_rtx (INSN_LIST, REG_CC_SETTER, insn, + REG_NOTES (user)); + REG_NOTES (insn) = gen_rtx (INSN_LIST, REG_CC_USER, user, REG_NOTES (insn)); +} + +/* Return the next insn that uses CC0 after INSN, which is assumed to + set it. This is the inverse of prev_cc0_setter (i.e., prev_cc0_setter + applied to the result of this function should yield INSN). + + Normally, this is simply the next insn. However, if a REG_CC_USER note + is present, it contains the insn that uses CC0. + + Return 0 if we can't find the insn. */ + +rtx +next_cc0_user (insn) + rtx insn; +{ + rtx note = find_reg_note (insn, REG_CC_USER, NULL_RTX); + + if (note) + return XEXP (note, 0); + + insn = next_nonnote_insn (insn); + if (insn && GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE) + insn = XVECEXP (PATTERN (insn), 0, 0); + + if (insn && GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && reg_mentioned_p (cc0_rtx, PATTERN (insn))) + return insn; + + return 0; +} + +/* Find the insn that set CC0 for INSN. Unless INSN has a REG_CC_SETTER + note, it is the previous insn. */ + +rtx +prev_cc0_setter (insn) + rtx insn; +{ + rtx note = find_reg_note (insn, REG_CC_SETTER, NULL_RTX); + rtx link; + + if (note) + return XEXP (note, 0); + + insn = prev_nonnote_insn (insn); + if (! sets_cc0_p (PATTERN (insn))) + abort (); + + return insn; +} +#endif + +/* Try splitting insns that can be split for better scheduling. + PAT is the pattern which might split. + TRIAL is the insn providing PAT. + BACKWARDS is non-zero if we are scanning insns from last to first. + + If this routine succeeds in splitting, it returns the first or last + replacement insn depending on the value of BACKWARDS. Otherwise, it + returns TRIAL. If the insn to be returned can be split, it will be. */ + +rtx +try_split (pat, trial, backwards) + rtx pat, trial; + int backwards; +{ + rtx before = PREV_INSN (trial); + rtx after = NEXT_INSN (trial); + rtx seq = split_insns (pat, trial); + int has_barrier = 0; + rtx tem; + + /* If we are splitting a JUMP_INSN, it might be followed by a BARRIER. + We may need to handle this specially. */ + if (after && GET_CODE (after) == BARRIER) + { + has_barrier = 1; + after = NEXT_INSN (after); + } + + if (seq) + { + /* SEQ can either be a SEQUENCE or the pattern of a single insn. + The latter case will normally arise only when being done so that + it, in turn, will be split (SFmode on the 29k is an example). */ + if (GET_CODE (seq) == SEQUENCE) + { + /* If we are splitting a JUMP_INSN, look for the JUMP_INSN in + SEQ and copy our JUMP_LABEL to it. If JUMP_LABEL is non-zero, + increment the usage count so we don't delete the label. */ + int i; + + if (GET_CODE (trial) == JUMP_INSN) + for (i = XVECLEN (seq, 0) - 1; i >= 0; i--) + if (GET_CODE (XVECEXP (seq, 0, i)) == JUMP_INSN) + { + JUMP_LABEL (XVECEXP (seq, 0, i)) = JUMP_LABEL (trial); + + if (JUMP_LABEL (trial)) + LABEL_NUSES (JUMP_LABEL (trial))++; + } + + tem = emit_insn_after (seq, before); + + delete_insn (trial); + if (has_barrier) + emit_barrier_after (tem); + } + /* Avoid infinite loop if the result matches the original pattern. */ + else if (rtx_equal_p (seq, pat)) + return trial; + else + { + PATTERN (trial) = seq; + INSN_CODE (trial) = -1; + } + + /* Set TEM to the insn we should return. */ + tem = backwards ? prev_active_insn (after) : next_active_insn (before); + return try_split (PATTERN (tem), tem, backwards); + } + + return trial; +} + +/* Make and return an INSN rtx, initializing all its slots. + Store PATTERN in the pattern slots. */ + +rtx +make_insn_raw (pattern) + rtx pattern; +{ + register rtx insn; + + insn = rtx_alloc (INSN); + INSN_UID (insn) = cur_insn_uid++; + + PATTERN (insn) = pattern; + INSN_CODE (insn) = -1; + LOG_LINKS (insn) = NULL; + REG_NOTES (insn) = NULL; + + return insn; +} + +/* Like `make_insn' but make a JUMP_INSN instead of an insn. */ + +static rtx +make_jump_insn_raw (pattern) + rtx pattern; +{ + register rtx insn; + + insn = rtx_alloc (JUMP_INSN); + INSN_UID (insn) = cur_insn_uid++; + + PATTERN (insn) = pattern; + INSN_CODE (insn) = -1; + LOG_LINKS (insn) = NULL; + REG_NOTES (insn) = NULL; + JUMP_LABEL (insn) = NULL; + + return insn; +} + +/* Add INSN to the end of the doubly-linked list. + INSN may be an INSN, JUMP_INSN, CALL_INSN, CODE_LABEL, BARRIER or NOTE. */ + +void +add_insn (insn) + register rtx insn; +{ + PREV_INSN (insn) = last_insn; + NEXT_INSN (insn) = 0; + + if (NULL != last_insn) + NEXT_INSN (last_insn) = insn; + + if (NULL == first_insn) + first_insn = insn; + + last_insn = insn; +} + +/* Add INSN into the doubly-linked list after insn AFTER. This should be the + only function called to insert an insn once delay slots have been filled + since only it knows how to update a SEQUENCE. */ + +void +add_insn_after (insn, after) + rtx insn, after; +{ + rtx next = NEXT_INSN (after); + + NEXT_INSN (insn) = next; + PREV_INSN (insn) = after; + + if (next) + { + PREV_INSN (next) = insn; + if (GET_CODE (next) == INSN && GET_CODE (PATTERN (next)) == SEQUENCE) + PREV_INSN (XVECEXP (PATTERN (next), 0, 0)) = insn; + } + else if (last_insn == after) + last_insn = insn; + else + { + struct sequence_stack *stack = sequence_stack; + /* Scan all pending sequences too. */ + for (; stack; stack = stack->next) + if (after == stack->last) + stack->last = insn; + } + + NEXT_INSN (after) = insn; + if (GET_CODE (after) == INSN && GET_CODE (PATTERN (after)) == SEQUENCE) + { + rtx sequence = PATTERN (after); + NEXT_INSN (XVECEXP (sequence, 0, XVECLEN (sequence, 0) - 1)) = insn; + } +} + +/* Delete all insns made since FROM. + FROM becomes the new last instruction. */ + +void +delete_insns_since (from) + rtx from; +{ + if (from == 0) + first_insn = 0; + else + NEXT_INSN (from) = 0; + last_insn = from; +} + +/* Move a consecutive bunch of insns to a different place in the chain. + The insns to be moved are those between FROM and TO. + They are moved to a new position after the insn AFTER. + AFTER must not be FROM or TO or any insn in between. + + This function does not know about SEQUENCEs and hence should not be + called after delay-slot filling has been done. */ + +void +reorder_insns (from, to, after) + rtx from, to, after; +{ + /* Splice this bunch out of where it is now. */ + if (PREV_INSN (from)) + NEXT_INSN (PREV_INSN (from)) = NEXT_INSN (to); + if (NEXT_INSN (to)) + PREV_INSN (NEXT_INSN (to)) = PREV_INSN (from); + if (last_insn == to) + last_insn = PREV_INSN (from); + if (first_insn == from) + first_insn = NEXT_INSN (to); + + /* Make the new neighbors point to it and it to them. */ + if (NEXT_INSN (after)) + PREV_INSN (NEXT_INSN (after)) = to; + + NEXT_INSN (to) = NEXT_INSN (after); + PREV_INSN (from) = after; + NEXT_INSN (after) = from; + if (after == last_insn) + last_insn = to; +} + +/* Return the line note insn preceding INSN. */ + +static rtx +find_line_note (insn) + rtx insn; +{ + if (no_line_numbers) + return 0; + + for (; insn; insn = PREV_INSN (insn)) + if (GET_CODE (insn) == NOTE + && NOTE_LINE_NUMBER (insn) >= 0) + break; + + return insn; +} + +/* Like reorder_insns, but inserts line notes to preserve the line numbers + of the moved insns when debugging. This may insert a note between AFTER + and FROM, and another one after TO. */ + +void +reorder_insns_with_line_notes (from, to, after) + rtx from, to, after; +{ + rtx from_line = find_line_note (from); + rtx after_line = find_line_note (after); + + reorder_insns (from, to, after); + + if (from_line == after_line) + return; + + if (from_line) + emit_line_note_after (NOTE_SOURCE_FILE (from_line), + NOTE_LINE_NUMBER (from_line), + after); + if (after_line) + emit_line_note_after (NOTE_SOURCE_FILE (after_line), + NOTE_LINE_NUMBER (after_line), + to); +} + +/* Emit an insn of given code and pattern + at a specified place within the doubly-linked list. */ + +/* Make an instruction with body PATTERN + and output it before the instruction BEFORE. */ + +rtx +emit_insn_before (pattern, before) + register rtx pattern, before; +{ + register rtx insn = before; + + if (GET_CODE (pattern) == SEQUENCE) + { + register int i; + + for (i = 0; i < XVECLEN (pattern, 0); i++) + { + insn = XVECEXP (pattern, 0, i); + add_insn_after (insn, PREV_INSN (before)); + } + if (XVECLEN (pattern, 0) < SEQUENCE_RESULT_SIZE) + sequence_result[XVECLEN (pattern, 0)] = pattern; + } + else + { + insn = make_insn_raw (pattern); + add_insn_after (insn, PREV_INSN (before)); + } + + return insn; +} + +/* Make an instruction with body PATTERN and code JUMP_INSN + and output it before the instruction BEFORE. */ + +rtx +emit_jump_insn_before (pattern, before) + register rtx pattern, before; +{ + register rtx insn; + + if (GET_CODE (pattern) == SEQUENCE) + insn = emit_insn_before (pattern, before); + else + { + insn = make_jump_insn_raw (pattern); + add_insn_after (insn, PREV_INSN (before)); + } + + return insn; +} + +/* Make an instruction with body PATTERN and code CALL_INSN + and output it before the instruction BEFORE. */ + +rtx +emit_call_insn_before (pattern, before) + register rtx pattern, before; +{ + rtx insn = emit_insn_before (pattern, before); + PUT_CODE (insn, CALL_INSN); + return insn; +} + +/* Make an insn of code BARRIER + and output it before the insn AFTER. */ + +rtx +emit_barrier_before (before) + register rtx before; +{ + register rtx insn = rtx_alloc (BARRIER); + + INSN_UID (insn) = cur_insn_uid++; + + add_insn_after (insn, PREV_INSN (before)); + return insn; +} + +/* Emit a note of subtype SUBTYPE before the insn BEFORE. */ + +rtx +emit_note_before (subtype, before) + int subtype; + rtx before; +{ + register rtx note = rtx_alloc (NOTE); + INSN_UID (note) = cur_insn_uid++; + NOTE_SOURCE_FILE (note) = 0; + NOTE_LINE_NUMBER (note) = subtype; + + add_insn_after (note, PREV_INSN (before)); + return note; +} + +/* Make an insn of code INSN with body PATTERN + and output it after the insn AFTER. */ + +rtx +emit_insn_after (pattern, after) + register rtx pattern, after; +{ + register rtx insn = after; + + if (GET_CODE (pattern) == SEQUENCE) + { + register int i; + + for (i = 0; i < XVECLEN (pattern, 0); i++) + { + insn = XVECEXP (pattern, 0, i); + add_insn_after (insn, after); + after = insn; + } + if (XVECLEN (pattern, 0) < SEQUENCE_RESULT_SIZE) + sequence_result[XVECLEN (pattern, 0)] = pattern; + } + else + { + insn = make_insn_raw (pattern); + add_insn_after (insn, after); + } + + return insn; +} + +/* Similar to emit_insn_after, except that line notes are to be inserted so + as to act as if this insn were at FROM. */ + +void +emit_insn_after_with_line_notes (pattern, after, from) + rtx pattern, after, from; +{ + rtx from_line = find_line_note (from); + rtx after_line = find_line_note (after); + rtx insn = emit_insn_after (pattern, after); + + if (from_line) + emit_line_note_after (NOTE_SOURCE_FILE (from_line), + NOTE_LINE_NUMBER (from_line), + after); + + if (after_line) + emit_line_note_after (NOTE_SOURCE_FILE (after_line), + NOTE_LINE_NUMBER (after_line), + insn); +} + +/* Make an insn of code JUMP_INSN with body PATTERN + and output it after the insn AFTER. */ + +rtx +emit_jump_insn_after (pattern, after) + register rtx pattern, after; +{ + register rtx insn; + + if (GET_CODE (pattern) == SEQUENCE) + insn = emit_insn_after (pattern, after); + else + { + insn = make_jump_insn_raw (pattern); + add_insn_after (insn, after); + } + + return insn; +} + +/* Make an insn of code BARRIER + and output it after the insn AFTER. */ + +rtx +emit_barrier_after (after) + register rtx after; +{ + register rtx insn = rtx_alloc (BARRIER); + + INSN_UID (insn) = cur_insn_uid++; + + add_insn_after (insn, after); + return insn; +} + +/* Emit the label LABEL after the insn AFTER. */ + +rtx +emit_label_after (label, after) + rtx label, after; +{ + /* This can be called twice for the same label + as a result of the confusion that follows a syntax error! + So make it harmless. */ + if (INSN_UID (label) == 0) + { + INSN_UID (label) = cur_insn_uid++; + add_insn_after (label, after); + } + + return label; +} + +/* Emit a note of subtype SUBTYPE after the insn AFTER. */ + +rtx +emit_note_after (subtype, after) + int subtype; + rtx after; +{ + register rtx note = rtx_alloc (NOTE); + INSN_UID (note) = cur_insn_uid++; + NOTE_SOURCE_FILE (note) = 0; + NOTE_LINE_NUMBER (note) = subtype; + add_insn_after (note, after); + return note; +} + +/* Emit a line note for FILE and LINE after the insn AFTER. */ + +rtx +emit_line_note_after (file, line, after) + char *file; + int line; + rtx after; +{ + register rtx note; + + if (no_line_numbers && line > 0) + { + cur_insn_uid++; + return 0; + } + + note = rtx_alloc (NOTE); + INSN_UID (note) = cur_insn_uid++; + NOTE_SOURCE_FILE (note) = file; + NOTE_LINE_NUMBER (note) = line; + add_insn_after (note, after); + return note; +} + +/* Make an insn of code INSN with pattern PATTERN + and add it to the end of the doubly-linked list. + If PATTERN is a SEQUENCE, take the elements of it + and emit an insn for each element. + + Returns the last insn emitted. */ + +rtx +emit_insn (pattern) + rtx pattern; +{ + rtx insn = last_insn; + + if (GET_CODE (pattern) == SEQUENCE) + { + register int i; + + for (i = 0; i < XVECLEN (pattern, 0); i++) + { + insn = XVECEXP (pattern, 0, i); + add_insn (insn); + } + if (XVECLEN (pattern, 0) < SEQUENCE_RESULT_SIZE) + sequence_result[XVECLEN (pattern, 0)] = pattern; + } + else + { + insn = make_insn_raw (pattern); + add_insn (insn); + } + + return insn; +} + +/* Emit the insns in a chain starting with INSN. + Return the last insn emitted. */ + +rtx +emit_insns (insn) + rtx insn; +{ + rtx last = 0; + + while (insn) + { + rtx next = NEXT_INSN (insn); + add_insn (insn); + last = insn; + insn = next; + } + + return last; +} + +/* Emit the insns in a chain starting with INSN and place them in front of + the insn BEFORE. Return the last insn emitted. */ + +rtx +emit_insns_before (insn, before) + rtx insn; + rtx before; +{ + rtx last = 0; + + while (insn) + { + rtx next = NEXT_INSN (insn); + add_insn_after (insn, PREV_INSN (before)); + last = insn; + insn = next; + } + + return last; +} + +/* Emit the insns in a chain starting with FIRST and place them in back of + the insn AFTER. Return the last insn emitted. */ + +rtx +emit_insns_after (first, after) + register rtx first; + register rtx after; +{ + register rtx last; + register rtx after_after; + + if (!after) + abort (); + + if (!first) + return first; + + for (last = first; NEXT_INSN (last); last = NEXT_INSN (last)) + continue; + + after_after = NEXT_INSN (after); + + NEXT_INSN (after) = first; + PREV_INSN (first) = after; + NEXT_INSN (last) = after_after; + if (after_after) + PREV_INSN (after_after) = last; + + if (after == last_insn) + last_insn = last; + return last; +} + +/* Make an insn of code JUMP_INSN with pattern PATTERN + and add it to the end of the doubly-linked list. */ + +rtx +emit_jump_insn (pattern) + rtx pattern; +{ + if (GET_CODE (pattern) == SEQUENCE) + return emit_insn (pattern); + else + { + register rtx insn = make_jump_insn_raw (pattern); + add_insn (insn); + return insn; + } +} + +/* Make an insn of code CALL_INSN with pattern PATTERN + and add it to the end of the doubly-linked list. */ + +rtx +emit_call_insn (pattern) + rtx pattern; +{ + if (GET_CODE (pattern) == SEQUENCE) + return emit_insn (pattern); + else + { + register rtx insn = make_insn_raw (pattern); + add_insn (insn); + PUT_CODE (insn, CALL_INSN); + return insn; + } +} + +/* Add the label LABEL to the end of the doubly-linked list. */ + +rtx +emit_label (label) + rtx label; +{ + /* This can be called twice for the same label + as a result of the confusion that follows a syntax error! + So make it harmless. */ + if (INSN_UID (label) == 0) + { + INSN_UID (label) = cur_insn_uid++; + add_insn (label); + } + return label; +} + +/* Make an insn of code BARRIER + and add it to the end of the doubly-linked list. */ + +rtx +emit_barrier () +{ + register rtx barrier = rtx_alloc (BARRIER); + INSN_UID (barrier) = cur_insn_uid++; + add_insn (barrier); + return barrier; +} + +/* Make an insn of code NOTE + with data-fields specified by FILE and LINE + and add it to the end of the doubly-linked list, + but only if line-numbers are desired for debugging info. */ + +rtx +emit_line_note (file, line) + char *file; + int line; +{ + emit_filename = file; + emit_lineno = line; + +#if 0 + if (no_line_numbers) + return 0; +#endif + + return emit_note (file, line); +} + +/* Make an insn of code NOTE + with data-fields specified by FILE and LINE + and add it to the end of the doubly-linked list. + If it is a line-number NOTE, omit it if it matches the previous one. */ + +rtx +emit_note (file, line) + char *file; + int line; +{ + register rtx note; + + if (line > 0) + { + if (file && last_filename && !strcmp (file, last_filename) + && line == last_linenum) + return 0; + last_filename = file; + last_linenum = line; + } + + if (no_line_numbers && line > 0) + { + cur_insn_uid++; + return 0; + } + + note = rtx_alloc (NOTE); + INSN_UID (note) = cur_insn_uid++; + NOTE_SOURCE_FILE (note) = file; + NOTE_LINE_NUMBER (note) = line; + add_insn (note); + return note; +} + +/* Emit a NOTE, and don't omit it even if LINE it the previous note. */ + +rtx +emit_line_note_force (file, line) + char *file; + int line; +{ + last_linenum = -1; + return emit_line_note (file, line); +} + +/* Cause next statement to emit a line note even if the line number + has not changed. This is used at the beginning of a function. */ + +void +force_next_line_note () +{ + last_linenum = -1; +} + +/* Return an indication of which type of insn should have X as a body. + The value is CODE_LABEL, INSN, CALL_INSN or JUMP_INSN. */ + +enum rtx_code +classify_insn (x) + rtx x; +{ + if (GET_CODE (x) == CODE_LABEL) + return CODE_LABEL; + if (GET_CODE (x) == CALL) + return CALL_INSN; + if (GET_CODE (x) == RETURN) + return JUMP_INSN; + if (GET_CODE (x) == SET) + { + if (SET_DEST (x) == pc_rtx) + return JUMP_INSN; + else if (GET_CODE (SET_SRC (x)) == CALL) + return CALL_INSN; + else + return INSN; + } + if (GET_CODE (x) == PARALLEL) + { + register int j; + for (j = XVECLEN (x, 0) - 1; j >= 0; j--) + if (GET_CODE (XVECEXP (x, 0, j)) == CALL) + return CALL_INSN; + else if (GET_CODE (XVECEXP (x, 0, j)) == SET + && SET_DEST (XVECEXP (x, 0, j)) == pc_rtx) + return JUMP_INSN; + else if (GET_CODE (XVECEXP (x, 0, j)) == SET + && GET_CODE (SET_SRC (XVECEXP (x, 0, j))) == CALL) + return CALL_INSN; + } + return INSN; +} + +/* Emit the rtl pattern X as an appropriate kind of insn. + If X is a label, it is simply added into the insn chain. */ + +rtx +emit (x) + rtx x; +{ + enum rtx_code code = classify_insn (x); + + if (code == CODE_LABEL) + return emit_label (x); + else if (code == INSN) + return emit_insn (x); + else if (code == JUMP_INSN) + { + register rtx insn = emit_jump_insn (x); + if (simplejump_p (insn) || GET_CODE (x) == RETURN) + return emit_barrier (); + return insn; + } + else if (code == CALL_INSN) + return emit_call_insn (x); + else + abort (); +} + +/* Begin emitting insns to a sequence which can be packaged in an RTL_EXPR. */ + +void +start_sequence () +{ + struct sequence_stack *tem; + + if (sequence_element_free_list) + { + /* Reuse a previously-saved struct sequence_stack. */ + tem = sequence_element_free_list; + sequence_element_free_list = tem->next; + } + else + tem = (struct sequence_stack *) permalloc (sizeof (struct sequence_stack)); + + tem->next = sequence_stack; + tem->first = first_insn; + tem->last = last_insn; + + sequence_stack = tem; + + first_insn = 0; + last_insn = 0; +} + +/* Set up the insn chain starting with FIRST + as the current sequence, saving the previously current one. */ + +void +push_to_sequence (first) + rtx first; +{ + rtx last; + + start_sequence (); + + for (last = first; last && NEXT_INSN (last); last = NEXT_INSN (last)); + + first_insn = first; + last_insn = last; +} + +/* Set up the outer-level insn chain + as the current sequence, saving the previously current one. */ + +void +push_topmost_sequence () +{ + struct sequence_stack *stack, *top; + + start_sequence (); + + for (stack = sequence_stack; stack; stack = stack->next) + top = stack; + + first_insn = top->first; + last_insn = top->last; +} + +/* After emitting to the outer-level insn chain, update the outer-level + insn chain, and restore the previous saved state. */ + +void +pop_topmost_sequence () +{ + struct sequence_stack *stack, *top; + + for (stack = sequence_stack; stack; stack = stack->next) + top = stack; + + top->first = first_insn; + top->last = last_insn; + + end_sequence (); +} + +/* After emitting to a sequence, restore previous saved state. + + To get the contents of the sequence just made, + you must call `gen_sequence' *before* calling here. */ + +void +end_sequence () +{ + struct sequence_stack *tem = sequence_stack; + + first_insn = tem->first; + last_insn = tem->last; + sequence_stack = tem->next; + + tem->next = sequence_element_free_list; + sequence_element_free_list = tem; +} + +/* Return 1 if currently emitting into a sequence. */ + +int +in_sequence_p () +{ + return sequence_stack != 0; +} + +/* Generate a SEQUENCE rtx containing the insns already emitted + to the current sequence. + + This is how the gen_... function from a DEFINE_EXPAND + constructs the SEQUENCE that it returns. */ + +rtx +gen_sequence () +{ + rtx result; + rtx tem; + rtvec newvec; + int i; + int len; + + /* Count the insns in the chain. */ + len = 0; + for (tem = first_insn; tem; tem = NEXT_INSN (tem)) + len++; + + /* If only one insn, return its pattern rather than a SEQUENCE. + (Now that we cache SEQUENCE expressions, it isn't worth special-casing + the case of an empty list.) */ + if (len == 1 + && (GET_CODE (first_insn) == INSN + || GET_CODE (first_insn) == JUMP_INSN + || GET_CODE (first_insn) == CALL_INSN)) + return PATTERN (first_insn); + + /* Put them in a vector. See if we already have a SEQUENCE of the + appropriate length around. */ + if (len < SEQUENCE_RESULT_SIZE && (result = sequence_result[len]) != 0) + sequence_result[len] = 0; + else + { + /* Ensure that this rtl goes in saveable_obstack, since we may be + caching it. */ + push_obstacks_nochange (); + rtl_in_saveable_obstack (); + result = gen_rtx (SEQUENCE, VOIDmode, rtvec_alloc (len)); + pop_obstacks (); + } + + for (i = 0, tem = first_insn; tem; tem = NEXT_INSN (tem), i++) + XVECEXP (result, 0, i) = tem; + + return result; +} + +/* Set up regno_reg_rtx, reg_rtx_no and regno_pointer_flag + according to the chain of insns starting with FIRST. + + Also set cur_insn_uid to exceed the largest uid in that chain. + + This is used when an inline function's rtl is saved + and passed to rest_of_compilation later. */ + +static void restore_reg_data_1 (); + +void +restore_reg_data (first) + rtx first; +{ + register rtx insn; + int i; + register int max_uid = 0; + + for (insn = first; insn; insn = NEXT_INSN (insn)) + { + if (INSN_UID (insn) >= max_uid) + max_uid = INSN_UID (insn); + + switch (GET_CODE (insn)) + { + case NOTE: + case CODE_LABEL: + case BARRIER: + break; + + case JUMP_INSN: + case CALL_INSN: + case INSN: + restore_reg_data_1 (PATTERN (insn)); + break; + } + } + + /* Don't duplicate the uids already in use. */ + cur_insn_uid = max_uid + 1; + + /* If any regs are missing, make them up. + + ??? word_mode is not necessarily the right mode. Most likely these REGs + are never used. At some point this should be checked. */ + + for (i = FIRST_PSEUDO_REGISTER; i < reg_rtx_no; i++) + if (regno_reg_rtx[i] == 0) + regno_reg_rtx[i] = gen_rtx (REG, word_mode, i); +} + +static void +restore_reg_data_1 (orig) + rtx orig; +{ + register rtx x = orig; + register int i; + register enum rtx_code code; + register char *format_ptr; + + code = GET_CODE (x); + + switch (code) + { + case QUEUED: + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + case CODE_LABEL: + case PC: + case CC0: + case LABEL_REF: + return; + + case REG: + if (REGNO (x) >= FIRST_PSEUDO_REGISTER) + { + /* Make sure regno_pointer_flag and regno_reg_rtx are large + enough to have an element for this pseudo reg number. */ + if (REGNO (x) >= reg_rtx_no) + { + reg_rtx_no = REGNO (x); + + if (reg_rtx_no >= regno_pointer_flag_length) + { + int newlen = MAX (regno_pointer_flag_length * 2, + reg_rtx_no + 30); + rtx *new1; + char *new = (char *) oballoc (newlen); + bzero (new, newlen); + bcopy (regno_pointer_flag, new, regno_pointer_flag_length); + + new1 = (rtx *) oballoc (newlen * sizeof (rtx)); + bzero (new1, newlen * sizeof (rtx)); + bcopy (regno_reg_rtx, new1, regno_pointer_flag_length * sizeof (rtx)); + + regno_pointer_flag = new; + regno_reg_rtx = new1; + regno_pointer_flag_length = newlen; + } + reg_rtx_no ++; + } + regno_reg_rtx[REGNO (x)] = x; + } + return; + + case MEM: + if (GET_CODE (XEXP (x, 0)) == REG) + mark_reg_pointer (XEXP (x, 0)); + restore_reg_data_1 (XEXP (x, 0)); + return; + } + + /* Now scan the subexpressions recursively. */ + + format_ptr = GET_RTX_FORMAT (code); + + for (i = 0; i < GET_RTX_LENGTH (code); i++) + { + switch (*format_ptr++) + { + case 'e': + restore_reg_data_1 (XEXP (x, i)); + break; + + case 'E': + if (XVEC (x, i) != NULL) + { + register int j; + + for (j = 0; j < XVECLEN (x, i); j++) + restore_reg_data_1 (XVECEXP (x, i, j)); + } + break; + } + } +} + +/* Initialize data structures and variables in this file + before generating rtl for each function. */ + +void +init_emit () +{ + int i; + + first_insn = NULL; + last_insn = NULL; + cur_insn_uid = 1; + reg_rtx_no = LAST_VIRTUAL_REGISTER + 1; + last_linenum = 0; + last_filename = 0; + first_label_num = label_num; + last_label_num = 0; + sequence_stack = NULL; + + /* Clear the start_sequence/gen_sequence cache. */ + sequence_element_free_list = 0; + for (i = 0; i < SEQUENCE_RESULT_SIZE; i++) + sequence_result[i] = 0; + + /* Init the tables that describe all the pseudo regs. */ + + regno_pointer_flag_length = LAST_VIRTUAL_REGISTER + 101; + + regno_pointer_flag + = (char *) oballoc (regno_pointer_flag_length); + bzero (regno_pointer_flag, regno_pointer_flag_length); + + regno_reg_rtx + = (rtx *) oballoc (regno_pointer_flag_length * sizeof (rtx)); + bzero (regno_reg_rtx, regno_pointer_flag_length * sizeof (rtx)); + + /* Put copies of all the virtual register rtx into regno_reg_rtx. */ + regno_reg_rtx[VIRTUAL_INCOMING_ARGS_REGNUM] = virtual_incoming_args_rtx; + regno_reg_rtx[VIRTUAL_STACK_VARS_REGNUM] = virtual_stack_vars_rtx; + regno_reg_rtx[VIRTUAL_STACK_DYNAMIC_REGNUM] = virtual_stack_dynamic_rtx; + regno_reg_rtx[VIRTUAL_OUTGOING_ARGS_REGNUM] = virtual_outgoing_args_rtx; + + /* Indicate that the virtual registers and stack locations are + all pointers. */ + REGNO_POINTER_FLAG (STACK_POINTER_REGNUM) = 1; + REGNO_POINTER_FLAG (FRAME_POINTER_REGNUM) = 1; + REGNO_POINTER_FLAG (ARG_POINTER_REGNUM) = 1; + + REGNO_POINTER_FLAG (VIRTUAL_INCOMING_ARGS_REGNUM) = 1; + REGNO_POINTER_FLAG (VIRTUAL_STACK_VARS_REGNUM) = 1; + REGNO_POINTER_FLAG (VIRTUAL_STACK_DYNAMIC_REGNUM) = 1; + REGNO_POINTER_FLAG (VIRTUAL_OUTGOING_ARGS_REGNUM) = 1; + +#ifdef INIT_EXPANDERS + INIT_EXPANDERS; +#endif +} + +/* Create some permanent unique rtl objects shared between all functions. + LINE_NUMBERS is nonzero if line numbers are to be generated. */ + +void +init_emit_once (line_numbers) + int line_numbers; +{ + int i; + enum machine_mode mode; + + no_line_numbers = ! line_numbers; + + sequence_stack = NULL; + + /* Create the unique rtx's for certain rtx codes and operand values. */ + + pc_rtx = gen_rtx (PC, VOIDmode); + cc0_rtx = gen_rtx (CC0, VOIDmode); + + /* Don't use gen_rtx here since gen_rtx in this case + tries to use these variables. */ + for (i = - MAX_SAVED_CONST_INT; i <= MAX_SAVED_CONST_INT; i++) + { + const_int_rtx[i + MAX_SAVED_CONST_INT] = rtx_alloc (CONST_INT); + PUT_MODE (const_int_rtx[i + MAX_SAVED_CONST_INT], VOIDmode); + INTVAL (const_int_rtx[i + MAX_SAVED_CONST_INT]) = i; + } + + /* These four calls obtain some of the rtx expressions made above. */ + const0_rtx = GEN_INT (0); + const1_rtx = GEN_INT (1); + const2_rtx = GEN_INT (2); + constm1_rtx = GEN_INT (-1); + + /* This will usually be one of the above constants, but may be a new rtx. */ + const_true_rtx = GEN_INT (STORE_FLAG_VALUE); + + dconst0 = REAL_VALUE_ATOF ("0", DFmode); + dconst1 = REAL_VALUE_ATOF ("1", DFmode); + dconst2 = REAL_VALUE_ATOF ("2", DFmode); + dconstm1 = REAL_VALUE_ATOF ("-1", DFmode); + + for (i = 0; i <= 2; i++) + { + for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); mode != VOIDmode; + mode = GET_MODE_WIDER_MODE (mode)) + { + rtx tem = rtx_alloc (CONST_DOUBLE); + union real_extract u; + + bzero (&u, sizeof u); /* Zero any holes in a structure. */ + u.d = i == 0 ? dconst0 : i == 1 ? dconst1 : dconst2; + + bcopy (&u, &CONST_DOUBLE_LOW (tem), sizeof u); + CONST_DOUBLE_MEM (tem) = cc0_rtx; + PUT_MODE (tem, mode); + + const_tiny_rtx[i][(int) mode] = tem; + } + + const_tiny_rtx[i][(int) VOIDmode] = GEN_INT (i); + + for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode; + mode = GET_MODE_WIDER_MODE (mode)) + const_tiny_rtx[i][(int) mode] = GEN_INT (i); + + for (mode = GET_CLASS_NARROWEST_MODE (MODE_PARTIAL_INT); + mode != VOIDmode; + mode = GET_MODE_WIDER_MODE (mode)) + const_tiny_rtx[i][(int) mode] = GEN_INT (i); + } + + for (mode = GET_CLASS_NARROWEST_MODE (MODE_CC); mode != VOIDmode; + mode = GET_MODE_WIDER_MODE (mode)) + const_tiny_rtx[0][(int) mode] = const0_rtx; + + stack_pointer_rtx = gen_rtx (REG, Pmode, STACK_POINTER_REGNUM); + frame_pointer_rtx = gen_rtx (REG, Pmode, FRAME_POINTER_REGNUM); + + if (FRAME_POINTER_REGNUM == ARG_POINTER_REGNUM) + arg_pointer_rtx = frame_pointer_rtx; + else if (STACK_POINTER_REGNUM == ARG_POINTER_REGNUM) + arg_pointer_rtx = stack_pointer_rtx; + else + arg_pointer_rtx = gen_rtx (REG, Pmode, ARG_POINTER_REGNUM); + + /* Create the virtual registers. Do so here since the following objects + might reference them. */ + + virtual_incoming_args_rtx = gen_rtx (REG, Pmode, + VIRTUAL_INCOMING_ARGS_REGNUM); + virtual_stack_vars_rtx = gen_rtx (REG, Pmode, + VIRTUAL_STACK_VARS_REGNUM); + virtual_stack_dynamic_rtx = gen_rtx (REG, Pmode, + VIRTUAL_STACK_DYNAMIC_REGNUM); + virtual_outgoing_args_rtx = gen_rtx (REG, Pmode, + VIRTUAL_OUTGOING_ARGS_REGNUM); + +#ifdef STRUCT_VALUE + struct_value_rtx = STRUCT_VALUE; +#else + struct_value_rtx = gen_rtx (REG, Pmode, STRUCT_VALUE_REGNUM); +#endif + +#ifdef STRUCT_VALUE_INCOMING + struct_value_incoming_rtx = STRUCT_VALUE_INCOMING; +#else +#ifdef STRUCT_VALUE_INCOMING_REGNUM + struct_value_incoming_rtx + = gen_rtx (REG, Pmode, STRUCT_VALUE_INCOMING_REGNUM); +#else + struct_value_incoming_rtx = struct_value_rtx; +#endif +#endif + +#ifdef STATIC_CHAIN_REGNUM + static_chain_rtx = gen_rtx (REG, Pmode, STATIC_CHAIN_REGNUM); + +#ifdef STATIC_CHAIN_INCOMING_REGNUM + if (STATIC_CHAIN_INCOMING_REGNUM != STATIC_CHAIN_REGNUM) + static_chain_incoming_rtx = gen_rtx (REG, Pmode, STATIC_CHAIN_INCOMING_REGNUM); + else +#endif + static_chain_incoming_rtx = static_chain_rtx; +#endif + +#ifdef STATIC_CHAIN + static_chain_rtx = STATIC_CHAIN; + +#ifdef STATIC_CHAIN_INCOMING + static_chain_incoming_rtx = STATIC_CHAIN_INCOMING; +#else + static_chain_incoming_rtx = static_chain_rtx; +#endif +#endif + +#ifdef PIC_OFFSET_TABLE_REGNUM + pic_offset_table_rtx = gen_rtx (REG, Pmode, PIC_OFFSET_TABLE_REGNUM); +#endif +} diff --git a/gnu/usr.bin/cc/common/explow.c b/gnu/usr.bin/cc/lib/explow.c similarity index 100% rename from gnu/usr.bin/cc/common/explow.c rename to gnu/usr.bin/cc/lib/explow.c diff --git a/gnu/usr.bin/cc/lib/expmed.c b/gnu/usr.bin/cc/lib/expmed.c new file mode 100644 index 0000000000..727f888a51 --- /dev/null +++ b/gnu/usr.bin/cc/lib/expmed.c @@ -0,0 +1,3160 @@ +/* Medium-level subroutines: convert bit-field store and extract + and shifts, multiplies and divides to rtl instructions. + Copyright (C) 1987, 1988, 1989, 1992, 1993 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include "config.h" +#include "rtl.h" +#include "tree.h" +#include "flags.h" +#include "insn-flags.h" +#include "insn-codes.h" +#include "insn-config.h" +#include "expr.h" +#include "real.h" +#include "recog.h" + +static rtx extract_split_bit_field (); +static rtx extract_fixed_bit_field (); +static void store_split_bit_field (); +static void store_fixed_bit_field (); +static rtx mask_rtx (); +static rtx lshift_value (); + +#define CEIL(x,y) (((x) + (y) - 1) / (y)) + +/* Non-zero means multiply instructions are cheaper than shifts. */ +int mult_is_very_cheap; + +/* Non-zero means divides or modulus operations are relatively cheap for + powers of two, so don't use branches; emit the operation instead. + Usually, this will mean that the MD file will emit non-branch + sequences. */ + +static int sdiv_pow2_cheap, smod_pow2_cheap; + +/* For compilers that support multiple targets with different word sizes, + MAX_BITS_PER_WORD contains the biggest value of BITS_PER_WORD. An example + is the H8/300(H) compiler. */ + +#ifndef MAX_BITS_PER_WORD +#define MAX_BITS_PER_WORD BITS_PER_WORD +#endif + +/* Cost of various pieces of RTL. */ +static int add_cost, mult_cost, negate_cost, zero_cost; +static int shift_cost[MAX_BITS_PER_WORD]; +static int shiftadd_cost[MAX_BITS_PER_WORD]; +static int shiftsub_cost[MAX_BITS_PER_WORD]; + +void +init_expmed () +{ + char *free_point; + /* This is "some random pseudo register" for purposes of calling recog + to see what insns exist. */ + rtx reg = gen_rtx (REG, word_mode, FIRST_PSEUDO_REGISTER); + rtx shift_insn, shiftadd_insn, shiftsub_insn; + int dummy; + int m; + + start_sequence (); + + /* Since we are on the permanent obstack, we must be sure we save this + spot AFTER we call start_sequence, since it will reuse the rtl it + makes. */ + + free_point = (char *) oballoc (0); + + zero_cost = rtx_cost (const0_rtx, 0); + add_cost = rtx_cost (gen_rtx (PLUS, word_mode, reg, reg), SET); + + shift_insn = emit_insn (gen_rtx (SET, VOIDmode, reg, + gen_rtx (ASHIFT, word_mode, reg, + const0_rtx))); + + shiftadd_insn = emit_insn (gen_rtx (SET, VOIDmode, reg, + gen_rtx (PLUS, word_mode, + gen_rtx (MULT, word_mode, + reg, const0_rtx), + reg))); + + shiftsub_insn = emit_insn (gen_rtx (SET, VOIDmode, reg, + gen_rtx (MINUS, word_mode, + gen_rtx (MULT, word_mode, + reg, const0_rtx), + reg))); + + init_recog (); + + shift_cost[0] = 0; + shiftadd_cost[0] = shiftsub_cost[0] = add_cost; + + for (m = 1; m < BITS_PER_WORD; m++) + { + shift_cost[m] = shiftadd_cost[m] = shiftsub_cost[m] = 32000; + + XEXP (SET_SRC (PATTERN (shift_insn)), 1) = GEN_INT (m); + if (recog (PATTERN (shift_insn), shift_insn, &dummy) >= 0) + shift_cost[m] = rtx_cost (SET_SRC (PATTERN (shift_insn)), SET); + + XEXP (XEXP (SET_SRC (PATTERN (shiftadd_insn)), 0), 1) + = GEN_INT ((HOST_WIDE_INT) 1 << m); + if (recog (PATTERN (shiftadd_insn), shiftadd_insn, &dummy) >= 0) + shiftadd_cost[m] = rtx_cost (SET_SRC (PATTERN (shiftadd_insn)), SET); + + XEXP (XEXP (SET_SRC (PATTERN (shiftsub_insn)), 0), 1) + = GEN_INT ((HOST_WIDE_INT) 1 << m); + if (recog (PATTERN (shiftsub_insn), shiftsub_insn, &dummy) >= 0) + shiftsub_cost[m] = rtx_cost (SET_SRC (PATTERN (shiftsub_insn)), SET); + } + + mult_cost = rtx_cost (gen_rtx (MULT, word_mode, reg, reg), SET); + /* For gcc 2.4 keep MULT_COST small to avoid really slow searches + in synth_mult. */ + mult_cost = MIN (12 * add_cost, mult_cost); + negate_cost = rtx_cost (gen_rtx (NEG, word_mode, reg), SET); + + /* 999999 is chosen to avoid any plausible faster special case. */ + mult_is_very_cheap + = (rtx_cost (gen_rtx (MULT, word_mode, reg, GEN_INT (999999)), SET) + < rtx_cost (gen_rtx (ASHIFT, word_mode, reg, GEN_INT (7)), SET)); + + sdiv_pow2_cheap + = (rtx_cost (gen_rtx (DIV, word_mode, reg, GEN_INT (32)), SET) + <= 2 * add_cost); + smod_pow2_cheap + = (rtx_cost (gen_rtx (MOD, word_mode, reg, GEN_INT (32)), SET) + <= 2 * add_cost); + + /* Free the objects we just allocated. */ + end_sequence (); + obfree (free_point); +} + +/* Return an rtx representing minus the value of X. + MODE is the intended mode of the result, + useful if X is a CONST_INT. */ + +rtx +negate_rtx (mode, x) + enum machine_mode mode; + rtx x; +{ + if (GET_CODE (x) == CONST_INT) + { + HOST_WIDE_INT val = - INTVAL (x); + if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT) + { + /* Sign extend the value from the bits that are significant. */ + if (val & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))) + val |= (HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (mode); + else + val &= ((HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (mode)) - 1; + } + return GEN_INT (val); + } + else + return expand_unop (GET_MODE (x), neg_optab, x, NULL_RTX, 0); +} + +/* Generate code to store value from rtx VALUE + into a bit-field within structure STR_RTX + containing BITSIZE bits starting at bit BITNUM. + FIELDMODE is the machine-mode of the FIELD_DECL node for this field. + ALIGN is the alignment that STR_RTX is known to have, measured in bytes. + TOTAL_SIZE is the size of the structure in bytes, or -1 if varying. */ + +/* ??? Note that there are two different ideas here for how + to determine the size to count bits within, for a register. + One is BITS_PER_WORD, and the other is the size of operand 3 + of the insv pattern. (The latter assumes that an n-bit machine + will be able to insert bit fields up to n bits wide.) + It isn't certain that either of these is right. + extract_bit_field has the same quandary. */ + +rtx +store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size) + rtx str_rtx; + register int bitsize; + int bitnum; + enum machine_mode fieldmode; + rtx value; + int align; + int total_size; +{ + int unit = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD; + register int offset = bitnum / unit; + register int bitpos = bitnum % unit; + register rtx op0 = str_rtx; + + if (GET_CODE (str_rtx) == MEM && ! MEM_IN_STRUCT_P (str_rtx)) + abort (); + + /* Discount the part of the structure before the desired byte. + We need to know how many bytes are safe to reference after it. */ + if (total_size >= 0) + total_size -= (bitpos / BIGGEST_ALIGNMENT + * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); + + while (GET_CODE (op0) == SUBREG) + { + /* The following line once was done only if WORDS_BIG_ENDIAN, + but I think that is a mistake. WORDS_BIG_ENDIAN is + meaningful at a much higher level; when structures are copied + between memory and regs, the higher-numbered regs + always get higher addresses. */ + offset += SUBREG_WORD (op0); + /* We used to adjust BITPOS here, but now we do the whole adjustment + right after the loop. */ + op0 = SUBREG_REG (op0); + } + +#if BYTES_BIG_ENDIAN + /* If OP0 is a register, BITPOS must count within a word. + But as we have it, it counts within whatever size OP0 now has. + On a bigendian machine, these are not the same, so convert. */ + if (GET_CODE (op0) != MEM && unit > GET_MODE_BITSIZE (GET_MODE (op0))) + bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0)); +#endif + + value = protect_from_queue (value, 0); + + if (flag_force_mem) + value = force_not_mem (value); + + /* Note that the adjustment of BITPOS above has no effect on whether + BITPOS is 0 in a REG bigger than a word. */ + if (GET_MODE_SIZE (fieldmode) >= UNITS_PER_WORD + && (! STRICT_ALIGNMENT || GET_CODE (op0) != MEM) + && bitpos == 0 && bitsize == GET_MODE_BITSIZE (fieldmode)) + { + /* Storing in a full-word or multi-word field in a register + can be done with just SUBREG. */ + if (GET_MODE (op0) != fieldmode) + if (GET_CODE (op0) == REG) + op0 = gen_rtx (SUBREG, fieldmode, op0, offset); + else + op0 = change_address (op0, fieldmode, + plus_constant (XEXP (op0, 0), offset)); + emit_move_insn (op0, value); + return value; + } + + /* Storing an lsb-aligned field in a register + can be done with a movestrict instruction. */ + + if (GET_CODE (op0) != MEM +#if BYTES_BIG_ENDIAN + && bitpos + bitsize == unit +#else + && bitpos == 0 +#endif + && bitsize == GET_MODE_BITSIZE (fieldmode) + && (GET_MODE (op0) == fieldmode + || (movstrict_optab->handlers[(int) fieldmode].insn_code + != CODE_FOR_nothing))) + { + /* Get appropriate low part of the value being stored. */ + if (GET_CODE (value) == CONST_INT || GET_CODE (value) == REG) + value = gen_lowpart (fieldmode, value); + else if (!(GET_CODE (value) == SYMBOL_REF + || GET_CODE (value) == LABEL_REF + || GET_CODE (value) == CONST)) + value = convert_to_mode (fieldmode, value, 0); + + if (GET_MODE (op0) == fieldmode) + emit_move_insn (op0, value); + else + { + int icode = movstrict_optab->handlers[(int) fieldmode].insn_code; + if(! (*insn_operand_predicate[icode][1]) (value, fieldmode)) + value = copy_to_mode_reg (fieldmode, value); + emit_insn (GEN_FCN (icode) + (gen_rtx (SUBREG, fieldmode, op0, offset), value)); + } + return value; + } + + /* Handle fields bigger than a word. */ + + if (bitsize > BITS_PER_WORD) + { + /* Here we transfer the words of the field + in the order least significant first. + This is because the most significant word is the one which may + be less than full. */ + + int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD; + int i; + + /* This is the mode we must force value to, so that there will be enough + subwords to extract. Note that fieldmode will often (always?) be + VOIDmode, because that is what store_field uses to indicate that this + is a bit field, but passing VOIDmode to operand_subword_force will + result in an abort. */ + fieldmode = mode_for_size (nwords * BITS_PER_WORD, MODE_INT, 0); + + for (i = 0; i < nwords; i++) + { + /* If I is 0, use the low-order word in both field and target; + if I is 1, use the next to lowest word; and so on. */ + int wordnum = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i); + int bit_offset = (WORDS_BIG_ENDIAN + ? MAX (bitsize - (i + 1) * BITS_PER_WORD, 0) + : i * BITS_PER_WORD); + store_bit_field (op0, MIN (BITS_PER_WORD, + bitsize - i * BITS_PER_WORD), + bitnum + bit_offset, word_mode, + operand_subword_force (value, wordnum, fieldmode), + align, total_size); + } + return value; + } + + /* From here on we can assume that the field to be stored in is + a full-word (whatever type that is), since it is shorter than a word. */ + + /* OFFSET is the number of words or bytes (UNIT says which) + from STR_RTX to the first word or byte containing part of the field. */ + + if (GET_CODE (op0) == REG) + { + if (offset != 0 + || GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD) + op0 = gen_rtx (SUBREG, TYPE_MODE (type_for_size (BITS_PER_WORD, 0)), + op0, offset); + offset = 0; + } + else + { + op0 = protect_from_queue (op0, 1); + } + + /* Now OFFSET is nonzero only if OP0 is memory + and is therefore always measured in bytes. */ + +#ifdef HAVE_insv + if (HAVE_insv + && !(bitsize == 1 && GET_CODE (value) == CONST_INT) + /* Ensure insv's size is wide enough for this field. */ + && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_insv][3]) + >= bitsize)) + { + int xbitpos = bitpos; + rtx value1; + rtx xop0 = op0; + rtx last = get_last_insn (); + rtx pat; + enum machine_mode maxmode + = insn_operand_mode[(int) CODE_FOR_insv][3]; + + int save_volatile_ok = volatile_ok; + volatile_ok = 1; + + /* If this machine's insv can only insert into a register, or if we + are to force MEMs into a register, copy OP0 into a register and + save it back later. */ + if (GET_CODE (op0) == MEM + && (flag_force_mem + || ! ((*insn_operand_predicate[(int) CODE_FOR_insv][0]) + (op0, VOIDmode)))) + { + rtx tempreg; + enum machine_mode bestmode; + + /* Get the mode to use for inserting into this field. If OP0 is + BLKmode, get the smallest mode consistent with the alignment. If + OP0 is a non-BLKmode object that is no wider than MAXMODE, use its + mode. Otherwise, use the smallest mode containing the field. */ + + if (GET_MODE (op0) == BLKmode + || GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (maxmode)) + bestmode + = get_best_mode (bitsize, bitnum, align * BITS_PER_UNIT, maxmode, + MEM_VOLATILE_P (op0)); + else + bestmode = GET_MODE (op0); + + if (bestmode == VOIDmode) + goto insv_loses; + + /* Adjust address to point to the containing unit of that mode. */ + unit = GET_MODE_BITSIZE (bestmode); + /* Compute offset as multiple of this unit, counting in bytes. */ + offset = (bitnum / unit) * GET_MODE_SIZE (bestmode); + bitpos = bitnum % unit; + op0 = change_address (op0, bestmode, + plus_constant (XEXP (op0, 0), offset)); + + /* Fetch that unit, store the bitfield in it, then store the unit. */ + tempreg = copy_to_reg (op0); + store_bit_field (tempreg, bitsize, bitpos, fieldmode, value, + align, total_size); + emit_move_insn (op0, tempreg); + return value; + } + volatile_ok = save_volatile_ok; + + /* Add OFFSET into OP0's address. */ + if (GET_CODE (xop0) == MEM) + xop0 = change_address (xop0, byte_mode, + plus_constant (XEXP (xop0, 0), offset)); + + /* If xop0 is a register, we need it in MAXMODE + to make it acceptable to the format of insv. */ + if (GET_CODE (xop0) == SUBREG) + PUT_MODE (xop0, maxmode); + if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode) + xop0 = gen_rtx (SUBREG, maxmode, xop0, 0); + + /* On big-endian machines, we count bits from the most significant. + If the bit field insn does not, we must invert. */ + +#if BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN + xbitpos = unit - bitsize - xbitpos; +#endif + /* We have been counting XBITPOS within UNIT. + Count instead within the size of the register. */ +#if BITS_BIG_ENDIAN + if (GET_CODE (xop0) != MEM) + xbitpos += GET_MODE_BITSIZE (maxmode) - unit; +#endif + unit = GET_MODE_BITSIZE (maxmode); + + /* Convert VALUE to maxmode (which insv insn wants) in VALUE1. */ + value1 = value; + if (GET_MODE (value) != maxmode) + { + if (GET_MODE_BITSIZE (GET_MODE (value)) >= bitsize) + { + /* Optimization: Don't bother really extending VALUE + if it has all the bits we will actually use. However, + if we must narrow it, be sure we do it correctly. */ + + if (GET_MODE_SIZE (GET_MODE (value)) < GET_MODE_SIZE (maxmode)) + { + /* Avoid making subreg of a subreg, or of a mem. */ + if (GET_CODE (value1) != REG) + value1 = copy_to_reg (value1); + value1 = gen_rtx (SUBREG, maxmode, value1, 0); + } + else + value1 = gen_lowpart (maxmode, value1); + } + else if (!CONSTANT_P (value)) + /* Parse phase is supposed to make VALUE's data type + match that of the component reference, which is a type + at least as wide as the field; so VALUE should have + a mode that corresponds to that type. */ + abort (); + } + + /* If this machine's insv insists on a register, + get VALUE1 into a register. */ + if (! ((*insn_operand_predicate[(int) CODE_FOR_insv][3]) + (value1, maxmode))) + value1 = force_reg (maxmode, value1); + + pat = gen_insv (xop0, GEN_INT (bitsize), GEN_INT (xbitpos), value1); + if (pat) + emit_insn (pat); + else + { + delete_insns_since (last); + store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align); + } + } + else + insv_loses: +#endif + /* Insv is not available; store using shifts and boolean ops. */ + store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align); + return value; +} + +/* Use shifts and boolean operations to store VALUE + into a bit field of width BITSIZE + in a memory location specified by OP0 except offset by OFFSET bytes. + (OFFSET must be 0 if OP0 is a register.) + The field starts at position BITPOS within the byte. + (If OP0 is a register, it may be a full word or a narrower mode, + but BITPOS still counts within a full word, + which is significant on bigendian machines.) + STRUCT_ALIGN is the alignment the structure is known to have (in bytes). + + Note that protect_from_queue has already been done on OP0 and VALUE. */ + +static void +store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align) + register rtx op0; + register int offset, bitsize, bitpos; + register rtx value; + int struct_align; +{ + register enum machine_mode mode; + int total_bits = BITS_PER_WORD; + rtx subtarget, temp; + int all_zero = 0; + int all_one = 0; + + /* Add OFFSET to OP0's address (if it is in memory) + and if a single byte contains the whole bit field + change OP0 to a byte. */ + + /* There is a case not handled here: + a structure with a known alignment of just a halfword + and a field split across two aligned halfwords within the structure. + Or likewise a structure with a known alignment of just a byte + and a field split across two bytes. + Such cases are not supposed to be able to occur. */ + + if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) + { + if (offset != 0) + abort (); + /* Special treatment for a bit field split across two registers. */ + if (bitsize + bitpos > BITS_PER_WORD) + { + store_split_bit_field (op0, bitsize, bitpos, value, BITS_PER_WORD); + return; + } + } + else + { + /* Get the proper mode to use for this field. We want a mode that + includes the entire field. If such a mode would be larger than + a word, we won't be doing the extraction the normal way. */ + + mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT, + struct_align * BITS_PER_UNIT, word_mode, + GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0)); + + if (mode == VOIDmode) + { + /* The only way this should occur is if the field spans word + boundaries. */ + store_split_bit_field (op0, bitsize, bitpos + offset * BITS_PER_UNIT, + value, struct_align); + return; + } + + total_bits = GET_MODE_BITSIZE (mode); + + /* Get ref to an aligned byte, halfword, or word containing the field. + Adjust BITPOS to be position within a word, + and OFFSET to be the offset of that word. + Then alter OP0 to refer to that word. */ + bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT; + offset -= (offset % (total_bits / BITS_PER_UNIT)); + op0 = change_address (op0, mode, + plus_constant (XEXP (op0, 0), offset)); + } + + mode = GET_MODE (op0); + + /* Now MODE is either some integral mode for a MEM as OP0, + or is a full-word for a REG as OP0. TOTAL_BITS corresponds. + The bit field is contained entirely within OP0. + BITPOS is the starting bit number within OP0. + (OP0's mode may actually be narrower than MODE.) */ + +#if BYTES_BIG_ENDIAN + /* BITPOS is the distance between our msb + and that of the containing datum. + Convert it to the distance from the lsb. */ + + bitpos = total_bits - bitsize - bitpos; +#endif + /* Now BITPOS is always the distance between our lsb + and that of OP0. */ + + /* Shift VALUE left by BITPOS bits. If VALUE is not constant, + we must first convert its mode to MODE. */ + + if (GET_CODE (value) == CONST_INT) + { + register HOST_WIDE_INT v = INTVAL (value); + + if (bitsize < HOST_BITS_PER_WIDE_INT) + v &= ((HOST_WIDE_INT) 1 << bitsize) - 1; + + if (v == 0) + all_zero = 1; + else if ((bitsize < HOST_BITS_PER_WIDE_INT + && v == ((HOST_WIDE_INT) 1 << bitsize) - 1) + || (bitsize == HOST_BITS_PER_WIDE_INT && v == -1)) + all_one = 1; + + value = lshift_value (mode, value, bitpos, bitsize); + } + else + { + int must_and = (GET_MODE_BITSIZE (GET_MODE (value)) != bitsize + && bitpos + bitsize != GET_MODE_BITSIZE (mode)); + + if (GET_MODE (value) != mode) + { + /* If VALUE is a floating-point mode, access it as an integer + of the corresponding size, then convert it. This can occur on + a machine with 64 bit registers that uses SFmode for float. */ + if (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT) + { + if (GET_CODE (value) != REG) + value = copy_to_reg (value); + value + = gen_rtx (SUBREG, word_mode, value, 0); + } + + if ((GET_CODE (value) == REG || GET_CODE (value) == SUBREG) + && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (value))) + value = gen_lowpart (mode, value); + else + value = convert_to_mode (mode, value, 1); + } + + if (must_and) + value = expand_binop (mode, and_optab, value, + mask_rtx (mode, 0, bitsize, 0), + NULL_RTX, 1, OPTAB_LIB_WIDEN); + if (bitpos > 0) + value = expand_shift (LSHIFT_EXPR, mode, value, + build_int_2 (bitpos, 0), NULL_RTX, 1); + } + + /* Now clear the chosen bits in OP0, + except that if VALUE is -1 we need not bother. */ + + subtarget = (GET_CODE (op0) == REG || ! flag_force_mem) ? op0 : 0; + + if (! all_one) + { + temp = expand_binop (mode, and_optab, op0, + mask_rtx (mode, bitpos, bitsize, 1), + subtarget, 1, OPTAB_LIB_WIDEN); + subtarget = temp; + } + else + temp = op0; + + /* Now logical-or VALUE into OP0, unless it is zero. */ + + if (! all_zero) + temp = expand_binop (mode, ior_optab, temp, value, + subtarget, 1, OPTAB_LIB_WIDEN); + if (op0 != temp) + emit_move_insn (op0, temp); +} + +/* Store a bit field that is split across two words. + + OP0 is the REG, SUBREG or MEM rtx for the first of the two words. + BITSIZE is the field width; BITPOS the position of its first bit + (within the word). + VALUE is the value to store. */ + +static void +store_split_bit_field (op0, bitsize, bitpos, value, align) + rtx op0; + int bitsize, bitpos; + rtx value; + int align; +{ + /* BITSIZE_1 is size of the part in the first word. */ + int bitsize_1 = BITS_PER_WORD - bitpos % BITS_PER_WORD; + /* BITSIZE_2 is size of the rest (in the following word). */ + int bitsize_2 = bitsize - bitsize_1; + rtx part1, part2; + int unit = GET_CODE (op0) == MEM ? BITS_PER_UNIT : BITS_PER_WORD; + int offset = bitpos / unit; + rtx word; + + /* The field must span exactly one word boundary. */ + if (bitpos / BITS_PER_WORD != (bitpos + bitsize - 1) / BITS_PER_WORD - 1) + abort (); + + if (GET_MODE (value) != VOIDmode) + value = convert_to_mode (word_mode, value, 1); + + if (GET_CODE (value) == CONST_DOUBLE + && (part1 = gen_lowpart_common (word_mode, value)) != 0) + value = part1; + + if (CONSTANT_P (value) && GET_CODE (value) != CONST_INT) + value = copy_to_mode_reg (word_mode, value); + + /* Split the value into two parts: + PART1 gets that which goes in the first word; PART2 the other. */ +#if BYTES_BIG_ENDIAN + /* PART1 gets the more significant part. */ + if (GET_CODE (value) == CONST_INT) + { + part1 = GEN_INT ((unsigned HOST_WIDE_INT) (INTVAL (value)) >> bitsize_2); + part2 = GEN_INT ((unsigned HOST_WIDE_INT) (INTVAL (value)) + & (((HOST_WIDE_INT) 1 << bitsize_2) - 1)); + } + else + { + part1 = extract_fixed_bit_field (word_mode, value, 0, bitsize_1, + BITS_PER_WORD - bitsize, NULL_RTX, 1, + BITS_PER_WORD); + part2 = extract_fixed_bit_field (word_mode, value, 0, bitsize_2, + BITS_PER_WORD - bitsize_2, NULL_RTX, 1, + BITS_PER_WORD); + } +#else + /* PART1 gets the less significant part. */ + if (GET_CODE (value) == CONST_INT) + { + part1 = GEN_INT ((unsigned HOST_WIDE_INT) (INTVAL (value)) + & (((HOST_WIDE_INT) 1 << bitsize_1) - 1)); + part2 = GEN_INT ((unsigned HOST_WIDE_INT) (INTVAL (value)) >> bitsize_1); + } + else + { + part1 = extract_fixed_bit_field (word_mode, value, 0, bitsize_1, 0, + NULL_RTX, 1, BITS_PER_WORD); + part2 = extract_fixed_bit_field (word_mode, value, 0, bitsize_2, + bitsize_1, NULL_RTX, 1, BITS_PER_WORD); + } +#endif + + /* Store PART1 into the first word. If OP0 is a MEM, pass OP0 and the + offset computed above. Otherwise, get the proper word and pass an + offset of zero. */ + word = (GET_CODE (op0) == MEM ? op0 + : operand_subword (op0, offset, 1, GET_MODE (op0))); + if (word == 0) + abort (); + + store_fixed_bit_field (word, GET_CODE (op0) == MEM ? offset : 0, + bitsize_1, bitpos % unit, part1, align); + + /* Offset op0 by 1 word to get to the following one. */ + if (GET_CODE (op0) == SUBREG) + word = operand_subword (SUBREG_REG (op0), SUBREG_WORD (op0) + offset + 1, + 1, VOIDmode); + else if (GET_CODE (op0) == MEM) + word = op0; + else + word = operand_subword (op0, offset + 1, 1, GET_MODE (op0)); + + if (word == 0) + abort (); + + /* Store PART2 into the second word. */ + store_fixed_bit_field (word, + (GET_CODE (op0) == MEM + ? CEIL (offset + 1, UNITS_PER_WORD) * UNITS_PER_WORD + : 0), + bitsize_2, 0, part2, align); +} + +/* Generate code to extract a byte-field from STR_RTX + containing BITSIZE bits, starting at BITNUM, + and put it in TARGET if possible (if TARGET is nonzero). + Regardless of TARGET, we return the rtx for where the value is placed. + It may be a QUEUED. + + STR_RTX is the structure containing the byte (a REG or MEM). + UNSIGNEDP is nonzero if this is an unsigned bit field. + MODE is the natural mode of the field value once extracted. + TMODE is the mode the caller would like the value to have; + but the value may be returned with type MODE instead. + + ALIGN is the alignment that STR_RTX is known to have, measured in bytes. + TOTAL_SIZE is the size in bytes of the containing structure, + or -1 if varying. + + If a TARGET is specified and we can store in it at no extra cost, + we do so, and return TARGET. + Otherwise, we return a REG of mode TMODE or MODE, with TMODE preferred + if they are equally easy. */ + +rtx +extract_bit_field (str_rtx, bitsize, bitnum, unsignedp, + target, mode, tmode, align, total_size) + rtx str_rtx; + register int bitsize; + int bitnum; + int unsignedp; + rtx target; + enum machine_mode mode, tmode; + int align; + int total_size; +{ + int unit = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD; + register int offset = bitnum / unit; + register int bitpos = bitnum % unit; + register rtx op0 = str_rtx; + rtx spec_target = target; + rtx spec_target_subreg = 0; + + if (GET_CODE (str_rtx) == MEM && ! MEM_IN_STRUCT_P (str_rtx)) + abort (); + + /* Discount the part of the structure before the desired byte. + We need to know how many bytes are safe to reference after it. */ + if (total_size >= 0) + total_size -= (bitpos / BIGGEST_ALIGNMENT + * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); + + if (tmode == VOIDmode) + tmode = mode; + while (GET_CODE (op0) == SUBREG) + { + offset += SUBREG_WORD (op0); + op0 = SUBREG_REG (op0); + } + +#if BYTES_BIG_ENDIAN + /* If OP0 is a register, BITPOS must count within a word. + But as we have it, it counts within whatever size OP0 now has. + On a bigendian machine, these are not the same, so convert. */ + if (GET_CODE (op0) != MEM && unit > GET_MODE_BITSIZE (GET_MODE (op0))) + bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0)); +#endif + + /* Extracting a full-word or multi-word value + from a structure in a register. + This can be done with just SUBREG. + So too extracting a subword value in + the least significant part of the register. */ + + if (GET_CODE (op0) == REG + && ((bitsize >= BITS_PER_WORD && bitsize == GET_MODE_BITSIZE (mode) + && bitpos % BITS_PER_WORD == 0) + || (mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0) != BLKmode +#if BYTES_BIG_ENDIAN + && bitpos + bitsize == BITS_PER_WORD +#else + && bitpos == 0 +#endif + ))) + { + enum machine_mode mode1 + = mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0); + + if (mode1 != GET_MODE (op0)) + op0 = gen_rtx (SUBREG, mode1, op0, offset); + + if (mode1 != mode) + return convert_to_mode (tmode, op0, unsignedp); + return op0; + } + + /* Handle fields bigger than a word. */ + + if (bitsize > BITS_PER_WORD) + { + /* Here we transfer the words of the field + in the order least significant first. + This is because the most significant word is the one which may + be less than full. */ + + int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD; + int i; + + if (target == 0 || GET_CODE (target) != REG) + target = gen_reg_rtx (mode); + + for (i = 0; i < nwords; i++) + { + /* If I is 0, use the low-order word in both field and target; + if I is 1, use the next to lowest word; and so on. */ + int wordnum = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i); + int bit_offset = (WORDS_BIG_ENDIAN + ? MAX (0, bitsize - (i + 1) * BITS_PER_WORD) + : i * BITS_PER_WORD); + rtx target_part = operand_subword (target, wordnum, 1, VOIDmode); + rtx result_part + = extract_bit_field (op0, MIN (BITS_PER_WORD, + bitsize - i * BITS_PER_WORD), + bitnum + bit_offset, + 1, target_part, mode, word_mode, + align, total_size); + + if (target_part == 0) + abort (); + + if (result_part != target_part) + emit_move_insn (target_part, result_part); + } + + return target; + } + + /* From here on we know the desired field is smaller than a word + so we can assume it is an integer. So we can safely extract it as one + size of integer, if necessary, and then truncate or extend + to the size that is wanted. */ + + /* OFFSET is the number of words or bytes (UNIT says which) + from STR_RTX to the first word or byte containing part of the field. */ + + if (GET_CODE (op0) == REG) + { + if (offset != 0 + || GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD) + op0 = gen_rtx (SUBREG, TYPE_MODE (type_for_size (BITS_PER_WORD, 0)), + op0, offset); + offset = 0; + } + else + { + op0 = protect_from_queue (str_rtx, 1); + } + + /* Now OFFSET is nonzero only for memory operands. */ + + if (unsignedp) + { +#ifdef HAVE_extzv + if (HAVE_extzv + && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extzv][0]) + >= bitsize)) + { + int xbitpos = bitpos, xoffset = offset; + rtx bitsize_rtx, bitpos_rtx; + rtx last = get_last_insn(); + rtx xop0 = op0; + rtx xtarget = target; + rtx xspec_target = spec_target; + rtx xspec_target_subreg = spec_target_subreg; + rtx pat; + enum machine_mode maxmode + = insn_operand_mode[(int) CODE_FOR_extzv][0]; + + if (GET_CODE (xop0) == MEM) + { + int save_volatile_ok = volatile_ok; + volatile_ok = 1; + + /* Is the memory operand acceptable? */ + if (flag_force_mem + || ! ((*insn_operand_predicate[(int) CODE_FOR_extzv][1]) + (xop0, GET_MODE (xop0)))) + { + /* No, load into a reg and extract from there. */ + enum machine_mode bestmode; + + /* Get the mode to use for inserting into this field. If + OP0 is BLKmode, get the smallest mode consistent with the + alignment. If OP0 is a non-BLKmode object that is no + wider than MAXMODE, use its mode. Otherwise, use the + smallest mode containing the field. */ + + if (GET_MODE (xop0) == BLKmode + || (GET_MODE_SIZE (GET_MODE (op0)) + > GET_MODE_SIZE (maxmode))) + bestmode = get_best_mode (bitsize, bitnum, + align * BITS_PER_UNIT, maxmode, + MEM_VOLATILE_P (xop0)); + else + bestmode = GET_MODE (xop0); + + if (bestmode == VOIDmode) + goto extzv_loses; + + /* Compute offset as multiple of this unit, + counting in bytes. */ + unit = GET_MODE_BITSIZE (bestmode); + xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode); + xbitpos = bitnum % unit; + xop0 = change_address (xop0, bestmode, + plus_constant (XEXP (xop0, 0), + xoffset)); + /* Fetch it to a register in that size. */ + xop0 = force_reg (bestmode, xop0); + + /* XBITPOS counts within UNIT, which is what is expected. */ + } + else + /* Get ref to first byte containing part of the field. */ + xop0 = change_address (xop0, byte_mode, + plus_constant (XEXP (xop0, 0), xoffset)); + + volatile_ok = save_volatile_ok; + } + + /* If op0 is a register, we need it in MAXMODE (which is usually + SImode). to make it acceptable to the format of extzv. */ + if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != maxmode) + abort (); + if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode) + xop0 = gen_rtx (SUBREG, maxmode, xop0, 0); + + /* On big-endian machines, we count bits from the most significant. + If the bit field insn does not, we must invert. */ +#if BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN + xbitpos = unit - bitsize - xbitpos; +#endif + /* Now convert from counting within UNIT to counting in MAXMODE. */ +#if BITS_BIG_ENDIAN + if (GET_CODE (xop0) != MEM) + xbitpos += GET_MODE_BITSIZE (maxmode) - unit; +#endif + unit = GET_MODE_BITSIZE (maxmode); + + if (xtarget == 0 + || (flag_force_mem && GET_CODE (xtarget) == MEM)) + xtarget = xspec_target = gen_reg_rtx (tmode); + + if (GET_MODE (xtarget) != maxmode) + { + if (GET_CODE (xtarget) == REG) + { + int wider = (GET_MODE_SIZE (maxmode) + > GET_MODE_SIZE (GET_MODE (xtarget))); + xtarget = gen_lowpart (maxmode, xtarget); + if (wider) + xspec_target_subreg = xtarget; + } + else + xtarget = gen_reg_rtx (maxmode); + } + + /* If this machine's extzv insists on a register target, + make sure we have one. */ + if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][0]) + (xtarget, maxmode))) + xtarget = gen_reg_rtx (maxmode); + + bitsize_rtx = GEN_INT (bitsize); + bitpos_rtx = GEN_INT (xbitpos); + + pat = gen_extzv (protect_from_queue (xtarget, 1), + xop0, bitsize_rtx, bitpos_rtx); + if (pat) + { + emit_insn (pat); + target = xtarget; + spec_target = xspec_target; + spec_target_subreg = xspec_target_subreg; + } + else + { + delete_insns_since (last); + target = extract_fixed_bit_field (tmode, op0, offset, bitsize, + bitpos, target, 1, align); + } + } + else + extzv_loses: +#endif + target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos, + target, 1, align); + } + else + { +#ifdef HAVE_extv + if (HAVE_extv + && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extv][0]) + >= bitsize)) + { + int xbitpos = bitpos, xoffset = offset; + rtx bitsize_rtx, bitpos_rtx; + rtx last = get_last_insn(); + rtx xop0 = op0, xtarget = target; + rtx xspec_target = spec_target; + rtx xspec_target_subreg = spec_target_subreg; + rtx pat; + enum machine_mode maxmode + = insn_operand_mode[(int) CODE_FOR_extv][0]; + + if (GET_CODE (xop0) == MEM) + { + /* Is the memory operand acceptable? */ + if (! ((*insn_operand_predicate[(int) CODE_FOR_extv][1]) + (xop0, GET_MODE (xop0)))) + { + /* No, load into a reg and extract from there. */ + enum machine_mode bestmode; + + /* Get the mode to use for inserting into this field. If + OP0 is BLKmode, get the smallest mode consistent with the + alignment. If OP0 is a non-BLKmode object that is no + wider than MAXMODE, use its mode. Otherwise, use the + smallest mode containing the field. */ + + if (GET_MODE (xop0) == BLKmode + || (GET_MODE_SIZE (GET_MODE (op0)) + > GET_MODE_SIZE (maxmode))) + bestmode = get_best_mode (bitsize, bitnum, + align * BITS_PER_UNIT, maxmode, + MEM_VOLATILE_P (xop0)); + else + bestmode = GET_MODE (xop0); + + if (bestmode == VOIDmode) + goto extv_loses; + + /* Compute offset as multiple of this unit, + counting in bytes. */ + unit = GET_MODE_BITSIZE (bestmode); + xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode); + xbitpos = bitnum % unit; + xop0 = change_address (xop0, bestmode, + plus_constant (XEXP (xop0, 0), + xoffset)); + /* Fetch it to a register in that size. */ + xop0 = force_reg (bestmode, xop0); + + /* XBITPOS counts within UNIT, which is what is expected. */ + } + else + /* Get ref to first byte containing part of the field. */ + xop0 = change_address (xop0, byte_mode, + plus_constant (XEXP (xop0, 0), xoffset)); + } + + /* If op0 is a register, we need it in MAXMODE (which is usually + SImode) to make it acceptable to the format of extv. */ + if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != maxmode) + abort (); + if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode) + xop0 = gen_rtx (SUBREG, maxmode, xop0, 0); + + /* On big-endian machines, we count bits from the most significant. + If the bit field insn does not, we must invert. */ +#if BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN + xbitpos = unit - bitsize - xbitpos; +#endif + /* XBITPOS counts within a size of UNIT. + Adjust to count within a size of MAXMODE. */ +#if BITS_BIG_ENDIAN + if (GET_CODE (xop0) != MEM) + xbitpos += (GET_MODE_BITSIZE (maxmode) - unit); +#endif + unit = GET_MODE_BITSIZE (maxmode); + + if (xtarget == 0 + || (flag_force_mem && GET_CODE (xtarget) == MEM)) + xtarget = xspec_target = gen_reg_rtx (tmode); + + if (GET_MODE (xtarget) != maxmode) + { + if (GET_CODE (xtarget) == REG) + { + int wider = (GET_MODE_SIZE (maxmode) + > GET_MODE_SIZE (GET_MODE (xtarget))); + xtarget = gen_lowpart (maxmode, xtarget); + if (wider) + xspec_target_subreg = xtarget; + } + else + xtarget = gen_reg_rtx (maxmode); + } + + /* If this machine's extv insists on a register target, + make sure we have one. */ + if (! ((*insn_operand_predicate[(int) CODE_FOR_extv][0]) + (xtarget, maxmode))) + xtarget = gen_reg_rtx (maxmode); + + bitsize_rtx = GEN_INT (bitsize); + bitpos_rtx = GEN_INT (xbitpos); + + pat = gen_extv (protect_from_queue (xtarget, 1), + xop0, bitsize_rtx, bitpos_rtx); + if (pat) + { + emit_insn (pat); + target = xtarget; + spec_target = xspec_target; + spec_target_subreg = xspec_target_subreg; + } + else + { + delete_insns_since (last); + target = extract_fixed_bit_field (tmode, op0, offset, bitsize, + bitpos, target, 0, align); + } + } + else + extv_loses: +#endif + target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos, + target, 0, align); + } + if (target == spec_target) + return target; + if (target == spec_target_subreg) + return spec_target; + if (GET_MODE (target) != tmode && GET_MODE (target) != mode) + { + /* If the target mode is floating-point, first convert to the + integer mode of that size and then access it as a floating-point + value via a SUBREG. */ + if (GET_MODE_CLASS (tmode) == MODE_FLOAT) + { + target = convert_to_mode (mode_for_size (GET_MODE_BITSIZE (tmode), + MODE_INT, 0), + target, unsignedp); + if (GET_CODE (target) != REG) + target = copy_to_reg (target); + return gen_rtx (SUBREG, tmode, target, 0); + } + else + return convert_to_mode (tmode, target, unsignedp); + } + return target; +} + +/* Extract a bit field using shifts and boolean operations + Returns an rtx to represent the value. + OP0 addresses a register (word) or memory (byte). + BITPOS says which bit within the word or byte the bit field starts in. + OFFSET says how many bytes farther the bit field starts; + it is 0 if OP0 is a register. + BITSIZE says how many bits long the bit field is. + (If OP0 is a register, it may be narrower than a full word, + but BITPOS still counts within a full word, + which is significant on bigendian machines.) + + UNSIGNEDP is nonzero for an unsigned bit field (don't sign-extend value). + If TARGET is nonzero, attempts to store the value there + and return TARGET, but this is not guaranteed. + If TARGET is not used, create a pseudo-reg of mode TMODE for the value. + + ALIGN is the alignment that STR_RTX is known to have, measured in bytes. */ + +static rtx +extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos, + target, unsignedp, align) + enum machine_mode tmode; + register rtx op0, target; + register int offset, bitsize, bitpos; + int unsignedp; + int align; +{ + int total_bits = BITS_PER_WORD; + enum machine_mode mode; + + if (GET_CODE (op0) == SUBREG || GET_CODE (op0) == REG) + { + /* Special treatment for a bit field split across two registers. */ + if (bitsize + bitpos > BITS_PER_WORD) + return extract_split_bit_field (op0, bitsize, bitpos, + unsignedp, align); + } + else + { + /* Get the proper mode to use for this field. We want a mode that + includes the entire field. If such a mode would be larger than + a word, we won't be doing the extraction the normal way. */ + + mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT, + align * BITS_PER_UNIT, word_mode, + GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0)); + + if (mode == VOIDmode) + /* The only way this should occur is if the field spans word + boundaries. */ + return extract_split_bit_field (op0, bitsize, + bitpos + offset * BITS_PER_UNIT, + unsignedp, align); + + total_bits = GET_MODE_BITSIZE (mode); + + /* Make sure bitpos is valid for the chosen mode. Adjust BITPOS to + be be in the range 0 to total_bits-1, and put any excess bytes in + OFFSET. */ + if (bitpos >= total_bits) + { + offset += (bitpos / total_bits) * (total_bits / BITS_PER_UNIT); + bitpos -= ((bitpos / total_bits) * (total_bits / BITS_PER_UNIT) + * BITS_PER_UNIT); + } + + /* Get ref to an aligned byte, halfword, or word containing the field. + Adjust BITPOS to be position within a word, + and OFFSET to be the offset of that word. + Then alter OP0 to refer to that word. */ + bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT; + offset -= (offset % (total_bits / BITS_PER_UNIT)); + op0 = change_address (op0, mode, + plus_constant (XEXP (op0, 0), offset)); + } + + mode = GET_MODE (op0); + +#if BYTES_BIG_ENDIAN + /* BITPOS is the distance between our msb and that of OP0. + Convert it to the distance from the lsb. */ + + bitpos = total_bits - bitsize - bitpos; +#endif + /* Now BITPOS is always the distance between the field's lsb and that of OP0. + We have reduced the big-endian case to the little-endian case. */ + + if (unsignedp) + { + if (bitpos) + { + /* If the field does not already start at the lsb, + shift it so it does. */ + tree amount = build_int_2 (bitpos, 0); + /* Maybe propagate the target for the shift. */ + /* But not if we will return it--could confuse integrate.c. */ + rtx subtarget = (target != 0 && GET_CODE (target) == REG + && !REG_FUNCTION_VALUE_P (target) + ? target : 0); + if (tmode != mode) subtarget = 0; + op0 = expand_shift (RSHIFT_EXPR, mode, op0, amount, subtarget, 1); + } + /* Convert the value to the desired mode. */ + if (mode != tmode) + op0 = convert_to_mode (tmode, op0, 1); + + /* Unless the msb of the field used to be the msb when we shifted, + mask out the upper bits. */ + + if (GET_MODE_BITSIZE (mode) != bitpos + bitsize +#if 0 +#ifdef SLOW_ZERO_EXTEND + /* Always generate an `and' if + we just zero-extended op0 and SLOW_ZERO_EXTEND, since it + will combine fruitfully with the zero-extend. */ + || tmode != mode +#endif +#endif + ) + return expand_binop (GET_MODE (op0), and_optab, op0, + mask_rtx (GET_MODE (op0), 0, bitsize, 0), + target, 1, OPTAB_LIB_WIDEN); + return op0; + } + + /* To extract a signed bit-field, first shift its msb to the msb of the word, + then arithmetic-shift its lsb to the lsb of the word. */ + op0 = force_reg (mode, op0); + if (mode != tmode) + target = 0; + + /* Find the narrowest integer mode that contains the field. */ + + for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode; + mode = GET_MODE_WIDER_MODE (mode)) + if (GET_MODE_BITSIZE (mode) >= bitsize + bitpos) + { + op0 = convert_to_mode (mode, op0, 0); + break; + } + + if (GET_MODE_BITSIZE (mode) != (bitsize + bitpos)) + { + tree amount = build_int_2 (GET_MODE_BITSIZE (mode) - (bitsize + bitpos), 0); + /* Maybe propagate the target for the shift. */ + /* But not if we will return the result--could confuse integrate.c. */ + rtx subtarget = (target != 0 && GET_CODE (target) == REG + && ! REG_FUNCTION_VALUE_P (target) + ? target : 0); + op0 = expand_shift (LSHIFT_EXPR, mode, op0, amount, subtarget, 1); + } + + return expand_shift (RSHIFT_EXPR, mode, op0, + build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0), + target, 0); +} + +/* Return a constant integer (CONST_INT or CONST_DOUBLE) mask value + of mode MODE with BITSIZE ones followed by BITPOS zeros, or the + complement of that if COMPLEMENT. The mask is truncated if + necessary to the width of mode MODE. */ + +static rtx +mask_rtx (mode, bitpos, bitsize, complement) + enum machine_mode mode; + int bitpos, bitsize, complement; +{ + HOST_WIDE_INT masklow, maskhigh; + + if (bitpos < HOST_BITS_PER_WIDE_INT) + masklow = (HOST_WIDE_INT) -1 << bitpos; + else + masklow = 0; + + if (bitpos + bitsize < HOST_BITS_PER_WIDE_INT) + masklow &= ((unsigned HOST_WIDE_INT) -1 + >> (HOST_BITS_PER_WIDE_INT - bitpos - bitsize)); + + if (bitpos <= HOST_BITS_PER_WIDE_INT) + maskhigh = -1; + else + maskhigh = (HOST_WIDE_INT) -1 << (bitpos - HOST_BITS_PER_WIDE_INT); + + if (bitpos + bitsize > HOST_BITS_PER_WIDE_INT) + maskhigh &= ((unsigned HOST_WIDE_INT) -1 + >> (2 * HOST_BITS_PER_WIDE_INT - bitpos - bitsize)); + else + maskhigh = 0; + + if (complement) + { + maskhigh = ~maskhigh; + masklow = ~masklow; + } + + return immed_double_const (masklow, maskhigh, mode); +} + +/* Return a constant integer (CONST_INT or CONST_DOUBLE) rtx with the value + VALUE truncated to BITSIZE bits and then shifted left BITPOS bits. */ + +static rtx +lshift_value (mode, value, bitpos, bitsize) + enum machine_mode mode; + rtx value; + int bitpos, bitsize; +{ + unsigned HOST_WIDE_INT v = INTVAL (value); + HOST_WIDE_INT low, high; + + if (bitsize < HOST_BITS_PER_WIDE_INT) + v &= ~((HOST_WIDE_INT) -1 << bitsize); + + if (bitpos < HOST_BITS_PER_WIDE_INT) + { + low = v << bitpos; + high = (bitpos > 0 ? (v >> (HOST_BITS_PER_WIDE_INT - bitpos)) : 0); + } + else + { + low = 0; + high = v << (bitpos - HOST_BITS_PER_WIDE_INT); + } + + return immed_double_const (low, high, mode); +} + +/* Extract a bit field that is split across two words + and return an RTX for the result. + + OP0 is the REG, SUBREG or MEM rtx for the first of the two words. + BITSIZE is the field width; BITPOS, position of its first bit, in the word. + UNSIGNEDP is 1 if should zero-extend the contents; else sign-extend. */ + +static rtx +extract_split_bit_field (op0, bitsize, bitpos, unsignedp, align) + rtx op0; + int bitsize, bitpos, unsignedp, align; +{ + /* BITSIZE_1 is size of the part in the first word. */ + int bitsize_1 = BITS_PER_WORD - bitpos % BITS_PER_WORD; + /* BITSIZE_2 is size of the rest (in the following word). */ + int bitsize_2 = bitsize - bitsize_1; + rtx part1, part2, result; + int unit = GET_CODE (op0) == MEM ? BITS_PER_UNIT : BITS_PER_WORD; + int offset = bitpos / unit; + rtx word; + + /* The field must span exactly one word boundary. */ + if (bitpos / BITS_PER_WORD != (bitpos + bitsize - 1) / BITS_PER_WORD - 1) + abort (); + + /* Get the part of the bit field from the first word. If OP0 is a MEM, + pass OP0 and the offset computed above. Otherwise, get the proper + word and pass an offset of zero. */ + word = (GET_CODE (op0) == MEM ? op0 + : operand_subword_force (op0, offset, GET_MODE (op0))); + part1 = extract_fixed_bit_field (word_mode, word, + GET_CODE (op0) == MEM ? offset : 0, + bitsize_1, bitpos % unit, NULL_RTX, + 1, align); + + /* Offset op0 by 1 word to get to the following one. */ + if (GET_CODE (op0) == SUBREG) + word = operand_subword_force (SUBREG_REG (op0), + SUBREG_WORD (op0) + offset + 1, VOIDmode); + else if (GET_CODE (op0) == MEM) + word = op0; + else + word = operand_subword_force (op0, offset + 1, GET_MODE (op0)); + + /* Get the part of the bit field from the second word. */ + part2 = extract_fixed_bit_field (word_mode, word, + (GET_CODE (op0) == MEM + ? CEIL (offset + 1, UNITS_PER_WORD) * UNITS_PER_WORD + : 0), + bitsize_2, 0, NULL_RTX, 1, align); + + /* Shift the more significant part up to fit above the other part. */ +#if BYTES_BIG_ENDIAN + part1 = expand_shift (LSHIFT_EXPR, word_mode, part1, + build_int_2 (bitsize_2, 0), 0, 1); +#else + part2 = expand_shift (LSHIFT_EXPR, word_mode, part2, + build_int_2 (bitsize_1, 0), 0, 1); +#endif + + /* Combine the two parts with bitwise or. This works + because we extracted both parts as unsigned bit fields. */ + result = expand_binop (word_mode, ior_optab, part1, part2, NULL_RTX, 1, + OPTAB_LIB_WIDEN); + + /* Unsigned bit field: we are done. */ + if (unsignedp) + return result; + /* Signed bit field: sign-extend with two arithmetic shifts. */ + result = expand_shift (LSHIFT_EXPR, word_mode, result, + build_int_2 (BITS_PER_WORD - bitsize, 0), + NULL_RTX, 0); + return expand_shift (RSHIFT_EXPR, word_mode, result, + build_int_2 (BITS_PER_WORD - bitsize, 0), NULL_RTX, 0); +} + +/* Add INC into TARGET. */ + +void +expand_inc (target, inc) + rtx target, inc; +{ + rtx value = expand_binop (GET_MODE (target), add_optab, + target, inc, + target, 0, OPTAB_LIB_WIDEN); + if (value != target) + emit_move_insn (target, value); +} + +/* Subtract DEC from TARGET. */ + +void +expand_dec (target, dec) + rtx target, dec; +{ + rtx value = expand_binop (GET_MODE (target), sub_optab, + target, dec, + target, 0, OPTAB_LIB_WIDEN); + if (value != target) + emit_move_insn (target, value); +} + +/* Output a shift instruction for expression code CODE, + with SHIFTED being the rtx for the value to shift, + and AMOUNT the tree for the amount to shift by. + Store the result in the rtx TARGET, if that is convenient. + If UNSIGNEDP is nonzero, do a logical shift; otherwise, arithmetic. + Return the rtx for where the value is. */ + +rtx +expand_shift (code, mode, shifted, amount, target, unsignedp) + enum tree_code code; + register enum machine_mode mode; + rtx shifted; + tree amount; + register rtx target; + int unsignedp; +{ + register rtx op1, temp = 0; + register int left = (code == LSHIFT_EXPR || code == LROTATE_EXPR); + register int rotate = (code == LROTATE_EXPR || code == RROTATE_EXPR); + int try; + + /* Previously detected shift-counts computed by NEGATE_EXPR + and shifted in the other direction; but that does not work + on all machines. */ + + op1 = expand_expr (amount, NULL_RTX, VOIDmode, 0); + + if (op1 == const0_rtx) + return shifted; + + for (try = 0; temp == 0 && try < 3; try++) + { + enum optab_methods methods; + + if (try == 0) + methods = OPTAB_DIRECT; + else if (try == 1) + methods = OPTAB_WIDEN; + else + methods = OPTAB_LIB_WIDEN; + + if (rotate) + { + /* Widening does not work for rotation. */ + if (methods == OPTAB_WIDEN) + continue; + else if (methods == OPTAB_LIB_WIDEN) + { + /* If we are rotating by a constant that is valid and + we have been unable to open-code this by a rotation, + do it as the IOR of two shifts. I.e., to rotate A + by N bits, compute (A << N) | ((unsigned) A >> (C - N)) + where C is the bitsize of A. + + It is theoretically possible that the target machine might + not be able to perform either shift and hence we would + be making two libcalls rather than just the one for the + shift (similarly if IOR could not be done). We will allow + this extremely unlikely lossage to avoid complicating the + code below. */ + + if (GET_CODE (op1) == CONST_INT && INTVAL (op1) > 0 + && INTVAL (op1) < GET_MODE_BITSIZE (mode)) + { + rtx subtarget = target == shifted ? 0 : target; + rtx temp1; + tree other_amount + = build_int_2 (GET_MODE_BITSIZE (mode) - INTVAL (op1), 0); + + shifted = force_reg (mode, shifted); + + temp = expand_shift (left ? LSHIFT_EXPR : RSHIFT_EXPR, + mode, shifted, amount, subtarget, 1); + temp1 = expand_shift (left ? RSHIFT_EXPR : LSHIFT_EXPR, + mode, shifted, other_amount, 0, 1); + return expand_binop (mode, ior_optab, temp, temp1, target, + unsignedp, methods); + } + else + methods = OPTAB_LIB; + } + + temp = expand_binop (mode, + left ? rotl_optab : rotr_optab, + shifted, op1, target, unsignedp, methods); + + /* If we don't have the rotate, but we are rotating by a constant + that is in range, try a rotate in the opposite direction. */ + + if (temp == 0 && GET_CODE (op1) == CONST_INT + && INTVAL (op1) > 0 && INTVAL (op1) < GET_MODE_BITSIZE (mode)) + temp = expand_binop (mode, + left ? rotr_optab : rotl_optab, + shifted, + GEN_INT (GET_MODE_BITSIZE (mode) + - INTVAL (op1)), + target, unsignedp, methods); + } + else if (unsignedp) + { + temp = expand_binop (mode, + left ? lshl_optab : lshr_optab, + shifted, op1, target, unsignedp, methods); + if (temp == 0 && left) + temp = expand_binop (mode, ashl_optab, + shifted, op1, target, unsignedp, methods); + } + + /* Do arithmetic shifts. + Also, if we are going to widen the operand, we can just as well + use an arithmetic right-shift instead of a logical one. */ + if (temp == 0 && ! rotate + && (! unsignedp || (! left && methods == OPTAB_WIDEN))) + { + enum optab_methods methods1 = methods; + + /* If trying to widen a log shift to an arithmetic shift, + don't accept an arithmetic shift of the same size. */ + if (unsignedp) + methods1 = OPTAB_MUST_WIDEN; + + /* Arithmetic shift */ + + temp = expand_binop (mode, + left ? ashl_optab : ashr_optab, + shifted, op1, target, unsignedp, methods1); + } + +#ifdef HAVE_extzv + /* We can do a logical (unsigned) right shift with a bit-field + extract insn. But first check if one of the above methods worked. */ + if (temp != 0) + return temp; + + if (unsignedp && code == RSHIFT_EXPR && ! BITS_BIG_ENDIAN && HAVE_extzv) + { + enum machine_mode output_mode + = insn_operand_mode[(int) CODE_FOR_extzv][0]; + + if ((methods == OPTAB_DIRECT && mode == output_mode) + || (methods == OPTAB_WIDEN + && GET_MODE_SIZE (mode) < GET_MODE_SIZE (output_mode))) + { + rtx shifted1 = convert_to_mode (output_mode, + protect_from_queue (shifted, 0), + 1); + enum machine_mode length_mode + = insn_operand_mode[(int) CODE_FOR_extzv][2]; + enum machine_mode pos_mode + = insn_operand_mode[(int) CODE_FOR_extzv][3]; + rtx target1 = 0; + rtx last = get_last_insn (); + rtx width; + rtx xop1 = op1; + rtx pat; + + if (target != 0) + target1 = protect_from_queue (target, 1); + + /* We define extract insns as having OUTPUT_MODE in a register + and the mode of operand 1 in memory. Since we want + OUTPUT_MODE, we will always force the operand into a + register. At some point we might want to support MEM + directly. */ + shifted1 = force_reg (output_mode, shifted1); + + /* If we don't have or cannot use a suggested target, + make a place for the result, in the proper mode. */ + if (methods == OPTAB_WIDEN || target1 == 0 + || ! ((*insn_operand_predicate[(int) CODE_FOR_extzv][0]) + (target1, output_mode))) + target1 = gen_reg_rtx (output_mode); + + xop1 = protect_from_queue (xop1, 0); + xop1 = convert_to_mode (pos_mode, xop1, + TREE_UNSIGNED (TREE_TYPE (amount))); + + /* If this machine's extzv insists on a register for + operand 3 (position), arrange for that. */ + if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][3]) + (xop1, pos_mode))) + xop1 = force_reg (pos_mode, xop1); + + /* WIDTH gets the width of the bit field to extract: + wordsize minus # bits to shift by. */ + if (GET_CODE (xop1) == CONST_INT) + width = GEN_INT (GET_MODE_BITSIZE (mode) - INTVAL (op1)); + else + { + /* Now get the width in the proper mode. */ + op1 = protect_from_queue (op1, 0); + width = convert_to_mode (length_mode, op1, + TREE_UNSIGNED (TREE_TYPE (amount))); + + width = expand_binop (length_mode, sub_optab, + GEN_INT (GET_MODE_BITSIZE (mode)), + width, NULL_RTX, 0, OPTAB_LIB_WIDEN); + } + + /* If this machine's extzv insists on a register for + operand 2 (length), arrange for that. */ + if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][2]) + (width, length_mode))) + width = force_reg (length_mode, width); + + /* Now extract with WIDTH, omitting OP1 least sig bits. */ + pat = gen_extzv (target1, shifted1, width, xop1); + if (pat) + { + emit_insn (pat); + temp = convert_to_mode (mode, target1, 1); + } + else + delete_insns_since (last); + } + + /* Can also do logical shift with signed bit-field extract + followed by inserting the bit-field at a different position. + That strategy is not yet implemented. */ + } +#endif /* HAVE_extzv */ + } + + if (temp == 0) + abort (); + return temp; +} + +enum alg_code { alg_zero, alg_m, alg_shift, + alg_add_t_m2, alg_sub_t_m2, + alg_add_factor, alg_sub_factor, + alg_add_t2_m, alg_sub_t2_m, + alg_add, alg_subtract, alg_factor, alg_shiftop }; + +/* This structure records a sequence of operations. + `ops' is the number of operations recorded. + `cost' is their total cost. + The operations are stored in `op' and the corresponding + logarithms of the integer coefficients in `log'. + + These are the operations: + alg_zero total := 0; + alg_m total := multiplicand; + alg_shift total := total * coeff + alg_add_t_m2 total := total + multiplicand * coeff; + alg_sub_t_m2 total := total - multiplicand * coeff; + alg_add_factor total := total * coeff + total; + alg_sub_factor total := total * coeff - total; + alg_add_t2_m total := total * coeff + multiplicand; + alg_sub_t2_m total := total * coeff - multiplicand; + + The first operand must be either alg_zero or alg_m. */ + +struct algorithm +{ + short cost; + short ops; + /* The size of the OP and LOG fields are not directly related to the + word size, but the worst-case algorithms will be if we have few + consecutive ones or zeros, i.e., a multiplicand like 10101010101... + In that case we will generate shift-by-2, add, shift-by-2, add,..., + in total wordsize operations. */ + enum alg_code op[MAX_BITS_PER_WORD]; + char log[MAX_BITS_PER_WORD]; +}; + +/* Compute and return the best algorithm for multiplying by T. + The algorithm must cost less than cost_limit + If retval.cost >= COST_LIMIT, no algorithm was found and all + other field of the returned struct are undefined. */ + +static struct algorithm +synth_mult (t, cost_limit) + unsigned HOST_WIDE_INT t; + int cost_limit; +{ + int m; + struct algorithm *best_alg + = (struct algorithm *)alloca (sizeof (struct algorithm)); + struct algorithm *alg_in + = (struct algorithm *)alloca (sizeof (struct algorithm)); + unsigned int cost; + unsigned HOST_WIDE_INT q; + + /* Indicate that no algorithm is yet found. If no algorithm + is found, this value will be returned and indicate failure. */ + best_alg->cost = cost_limit; + + if (cost_limit <= 0) + return *best_alg; + + /* t == 1 can be done in zero cost. */ + if (t == 1) + { + best_alg->ops = 1; + best_alg->cost = 0; + best_alg->op[0] = alg_m; + return *best_alg; + } + + /* t == 0 sometimes has a cost. If it does and it exceeds our limit, + fail now. */ + + else if (t == 0) + { + if (zero_cost >= cost_limit) + return *best_alg; + else + { + best_alg->ops = 1; + best_alg->cost = zero_cost; + best_alg->op[0] = alg_zero; + return *best_alg; + } + } + + /* If we have a group of zero bits at the low-order part of T, try + multiplying by the remaining bits and then doing a shift. */ + + if ((t & 1) == 0) + { + m = floor_log2 (t & -t); /* m = number of low zero bits */ + q = t >> m; + cost = shift_cost[m]; + if (cost < cost_limit) + { + *alg_in = synth_mult (q, cost_limit - cost); + + cost += alg_in->cost; + if (cost < best_alg->cost) + { + struct algorithm *x; + x = alg_in, alg_in = best_alg, best_alg = x; + best_alg->log[best_alg->ops] = m; + best_alg->op[best_alg->ops++] = alg_shift; + best_alg->cost = cost_limit = cost; + } + } + } + + /* If we have an odd number, add or subtract one. */ + if ((t & 1) != 0) + { + unsigned HOST_WIDE_INT w; + + for (w = 1; (w & t) != 0; w <<= 1) + ; + if (w > 2 + /* Reject the case where t is 3. + Thus we prefer addition in that case. */ + && t != 3) + { + /* T ends with ...111. Multiply by (T + 1) and subtract 1. */ + + cost = add_cost; + *alg_in = synth_mult (t + 1, cost_limit - cost); + + cost += alg_in->cost; + if (cost < best_alg->cost) + { + struct algorithm *x; + x = alg_in, alg_in = best_alg, best_alg = x; + best_alg->log[best_alg->ops] = 0; + best_alg->op[best_alg->ops++] = alg_sub_t_m2; + best_alg->cost = cost_limit = cost; + } + } + else + { + /* T ends with ...01 or ...011. Multiply by (T - 1) and add 1. */ + + cost = add_cost; + *alg_in = synth_mult (t - 1, cost_limit - cost); + + cost += alg_in->cost; + if (cost < best_alg->cost) + { + struct algorithm *x; + x = alg_in, alg_in = best_alg, best_alg = x; + best_alg->log[best_alg->ops] = 0; + best_alg->op[best_alg->ops++] = alg_add_t_m2; + best_alg->cost = cost_limit = cost; + } + } + } + + /* Look for factors of t of the form + t = q(2**m +- 1), 2 <= m <= floor(log2(t - 1)). + If we find such a factor, we can multiply by t using an algorithm that + multiplies by q, shift the result by m and add/subtract it to itself. + + We search for large factors first and loop down, even if large factors + are less probable than small; if we find a large factor we will find a + good sequence quickly, and therefore be able to prune (by decreasing + COST_LIMIT) the search. */ + + for (m = floor_log2 (t - 1); m >= 2; m--) + { + unsigned HOST_WIDE_INT d; + + d = ((unsigned HOST_WIDE_INT) 1 << m) + 1; + if (t % d == 0 && t > d) + { + cost = MIN (shiftadd_cost[m], add_cost + shift_cost[m]); + *alg_in = synth_mult (t / d, cost_limit - cost); + + cost += alg_in->cost; + if (cost < best_alg->cost) + { + struct algorithm *x; + x = alg_in, alg_in = best_alg, best_alg = x; + best_alg->log[best_alg->ops] = m; + best_alg->op[best_alg->ops++] = alg_add_factor; + best_alg->cost = cost_limit = cost; + } + } + + d = ((unsigned HOST_WIDE_INT) 1 << m) - 1; + if (t % d == 0 && t > d) + { + cost = MIN (shiftsub_cost[m], add_cost + shift_cost[m]); + *alg_in = synth_mult (t / d, cost_limit - cost); + + cost += alg_in->cost; + if (cost < best_alg->cost) + { + struct algorithm *x; + x = alg_in, alg_in = best_alg, best_alg = x; + best_alg->log[best_alg->ops] = m; + best_alg->op[best_alg->ops++] = alg_sub_factor; + best_alg->cost = cost_limit = cost; + } + } + } + + /* Try shift-and-add (load effective address) instructions, + i.e. do a*3, a*5, a*9. */ + if ((t & 1) != 0) + { + q = t - 1; + q = q & -q; + m = exact_log2 (q); + if (m >= 0) + { + cost = shiftadd_cost[m]; + *alg_in = synth_mult ((t - 1) >> m, cost_limit - cost); + + cost += alg_in->cost; + if (cost < best_alg->cost) + { + struct algorithm *x; + x = alg_in, alg_in = best_alg, best_alg = x; + best_alg->log[best_alg->ops] = m; + best_alg->op[best_alg->ops++] = alg_add_t2_m; + best_alg->cost = cost_limit = cost; + } + } + + q = t + 1; + q = q & -q; + m = exact_log2 (q); + if (m >= 0) + { + cost = shiftsub_cost[m]; + *alg_in = synth_mult ((t + 1) >> m, cost_limit - cost); + + cost += alg_in->cost; + if (cost < best_alg->cost) + { + struct algorithm *x; + x = alg_in, alg_in = best_alg, best_alg = x; + best_alg->log[best_alg->ops] = m; + best_alg->op[best_alg->ops++] = alg_sub_t2_m; + best_alg->cost = cost_limit = cost; + } + } + } + + /* If we are getting a too long sequence for `struct algorithm' + to record, store a fake cost to make this search fail. */ + if (best_alg->ops == MAX_BITS_PER_WORD) + best_alg->cost = cost_limit; + + return *best_alg; +} + +/* Perform a multiplication and return an rtx for the result. + MODE is mode of value; OP0 and OP1 are what to multiply (rtx's); + TARGET is a suggestion for where to store the result (an rtx). + + We check specially for a constant integer as OP1. + If you want this check for OP0 as well, then before calling + you should swap the two operands if OP0 would be constant. */ + +rtx +expand_mult (mode, op0, op1, target, unsignedp) + enum machine_mode mode; + register rtx op0, op1, target; + int unsignedp; +{ + rtx const_op1 = op1; + + /* If we are multiplying in DImode, it may still be a win + to try to work with shifts and adds. */ + if (GET_CODE (op1) == CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (op1)) == MODE_INT + && HOST_BITS_PER_INT <= BITS_PER_WORD) + { + if ((CONST_DOUBLE_HIGH (op1) == 0 && CONST_DOUBLE_LOW (op1) >= 0) + || (CONST_DOUBLE_HIGH (op1) == -1 && CONST_DOUBLE_LOW (op1) < 0)) + const_op1 = GEN_INT (CONST_DOUBLE_LOW (op1)); + } + + /* We used to test optimize here, on the grounds that it's better to + produce a smaller program when -O is not used. + But this causes such a terrible slowdown sometimes + that it seems better to use synth_mult always. */ + + if (GET_CODE (const_op1) == CONST_INT && ! mult_is_very_cheap) + { + struct algorithm alg; + struct algorithm neg_alg; + int negate = 0; + HOST_WIDE_INT val = INTVAL (op1); + HOST_WIDE_INT val_so_far; + rtx insn; + + /* Try to do the computation two ways: multiply by the negative of OP1 + and then negate, or do the multiplication directly. The latter is + usually faster for positive numbers and the former for negative + numbers, but the opposite can be faster if the original value + has a factor of 2**m +/- 1, while the negated value does not or + vice versa. */ + + alg = synth_mult (val, mult_cost); + neg_alg = synth_mult (- val, + (alg.cost < mult_cost ? alg.cost : mult_cost) + - negate_cost); + + if (neg_alg.cost + negate_cost < alg.cost) + alg = neg_alg, negate = 1; + + if (alg.cost < mult_cost) + { + /* We found something cheaper than a multiply insn. */ + int opno; + rtx accum, tem; + + op0 = protect_from_queue (op0, 0); + + /* Avoid referencing memory over and over. + For speed, but also for correctness when mem is volatile. */ + if (GET_CODE (op0) == MEM) + op0 = force_reg (mode, op0); + + /* ACCUM starts out either as OP0 or as a zero, depending on + the first operation. */ + + if (alg.op[0] == alg_zero) + { + accum = copy_to_mode_reg (mode, const0_rtx); + val_so_far = 0; + } + else if (alg.op[0] == alg_m) + { + accum = copy_to_mode_reg (mode, op0); + val_so_far = 1; + } + else + abort (); + + for (opno = 1; opno < alg.ops; opno++) + { + int log = alg.log[opno]; + rtx shift_subtarget = preserve_subexpressions_p () ? 0 : accum; + rtx add_target = opno == alg.ops - 1 && target != 0 ? target : 0; + + switch (alg.op[opno]) + { + case alg_shift: + accum = expand_shift (LSHIFT_EXPR, mode, accum, + build_int_2 (log, 0), NULL_RTX, 0); + val_so_far <<= log; + break; + + case alg_add_t_m2: + tem = expand_shift (LSHIFT_EXPR, mode, op0, + build_int_2 (log, 0), NULL_RTX, 0); + accum = force_operand (gen_rtx (PLUS, mode, accum, tem), + add_target ? add_target : accum); + val_so_far += (HOST_WIDE_INT) 1 << log; + break; + + case alg_sub_t_m2: + tem = expand_shift (LSHIFT_EXPR, mode, op0, + build_int_2 (log, 0), NULL_RTX, 0); + accum = force_operand (gen_rtx (MINUS, mode, accum, tem), + add_target ? add_target : accum); + val_so_far -= (HOST_WIDE_INT) 1 << log; + break; + + case alg_add_t2_m: + accum = expand_shift (LSHIFT_EXPR, mode, accum, + build_int_2 (log, 0), accum, 0); + accum = force_operand (gen_rtx (PLUS, mode, accum, op0), + add_target ? add_target : accum); + val_so_far = (val_so_far << log) + 1; + break; + + case alg_sub_t2_m: + accum = expand_shift (LSHIFT_EXPR, mode, accum, + build_int_2 (log, 0), accum, 0); + accum = force_operand (gen_rtx (MINUS, mode, accum, op0), + add_target ? add_target : accum); + val_so_far = (val_so_far << log) - 1; + break; + + case alg_add_factor: + tem = expand_shift (LSHIFT_EXPR, mode, accum, + build_int_2 (log, 0), NULL_RTX, 0); + accum = force_operand (gen_rtx (PLUS, mode, accum, tem), + add_target ? add_target : accum); + val_so_far += val_so_far << log; + break; + + case alg_sub_factor: + tem = expand_shift (LSHIFT_EXPR, mode, accum, + build_int_2 (log, 0), NULL_RTX, 0); + accum = force_operand (gen_rtx (MINUS, mode, tem, accum), + add_target ? add_target : tem); + val_so_far = (val_so_far << log) - val_so_far; + break; + + default: + abort ();; + } + + /* Write a REG_EQUAL note on the last insn so that we can cse + multiplication sequences. */ + + insn = get_last_insn (); + REG_NOTES (insn) + = gen_rtx (EXPR_LIST, REG_EQUAL, + gen_rtx (MULT, mode, op0, GEN_INT (val_so_far)), + REG_NOTES (insn)); + } + + if (negate) + { + val_so_far = - val_so_far; + accum = expand_unop (mode, neg_optab, accum, target, 0); + } + + if (val != val_so_far) + abort (); + + return accum; + } + } + + /* This used to use umul_optab if unsigned, + but for non-widening multiply there is no difference + between signed and unsigned. */ + op0 = expand_binop (mode, smul_optab, + op0, op1, target, unsignedp, OPTAB_LIB_WIDEN); + if (op0 == 0) + abort (); + return op0; +} + +/* Emit the code to divide OP0 by OP1, putting the result in TARGET + if that is convenient, and returning where the result is. + You may request either the quotient or the remainder as the result; + specify REM_FLAG nonzero to get the remainder. + + CODE is the expression code for which kind of division this is; + it controls how rounding is done. MODE is the machine mode to use. + UNSIGNEDP nonzero means do unsigned division. */ + +/* ??? For CEIL_MOD_EXPR, can compute incorrect remainder with ANDI + and then correct it by or'ing in missing high bits + if result of ANDI is nonzero. + For ROUND_MOD_EXPR, can use ANDI and then sign-extend the result. + This could optimize to a bfexts instruction. + But C doesn't use these operations, so their optimizations are + left for later. */ + +rtx +expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp) + int rem_flag; + enum tree_code code; + enum machine_mode mode; + register rtx op0, op1, target; + int unsignedp; +{ + register rtx result = 0; + enum machine_mode compute_mode; + int log = -1; + int size; + int can_clobber_op0; + int mod_insn_no_good = 0; + rtx adjusted_op0 = op0; + optab optab1, optab2; + + /* We shouldn't be called with op1 == const1_rtx, but some of the + code below will malfunction if we are, so check here and handle + the special case if so. */ + if (op1 == const1_rtx) + return rem_flag ? const0_rtx : op0; + + /* Don't use the function value register as a target + since we have to read it as well as write it, + and function-inlining gets confused by this. */ + if (target && REG_P (target) && REG_FUNCTION_VALUE_P (target)) + target = 0; + + /* Don't clobber an operand while doing a multi-step calculation. */ + if (target) + if ((rem_flag && (reg_mentioned_p (target, op0) + || (GET_CODE (op0) == MEM && GET_CODE (target) == MEM))) + || reg_mentioned_p (target, op1) + || (GET_CODE (op1) == MEM && GET_CODE (target) == MEM)) + target = 0; + + can_clobber_op0 = (GET_CODE (op0) == REG && op0 == target); + + if (GET_CODE (op1) == CONST_INT) + log = exact_log2 (INTVAL (op1)); + + /* If log is >= 0, we are dividing by 2**log, and will do it by shifting, + which is really floor-division. Otherwise we will really do a divide, + and we assume that is trunc-division. + + We must correct the dividend by adding or subtracting something + based on the divisor, in order to do the kind of rounding specified + by CODE. The correction depends on what kind of rounding is actually + available, and that depends on whether we will shift or divide. + + In many of these cases it is possible to perform the operation by a + clever series of logical operations (shifts and/or exclusive-ors). + Although avoiding the jump has the advantage that it extends the basic + block and allows further optimization, the branch-free code is normally + at least one instruction longer in the (most common) case where the + dividend is non-negative. Performance measurements of the two + alternatives show that the branch-free code is slightly faster on the + IBM ROMP but slower on CISC processors (significantly slower on the + VAX). Accordingly, the jump code has been retained. + + On machines where the jump code is slower, the cost of a DIV or MOD + operation can be set small (less than twice that of an addition); in + that case, we pretend that we don't have a power of two and perform + a normal division or modulus operation. */ + + if ((code == TRUNC_MOD_EXPR || code == TRUNC_DIV_EXPR) + && ! unsignedp + && (rem_flag ? smod_pow2_cheap : sdiv_pow2_cheap)) + log = -1; + + /* Get the mode in which to perform this computation. Normally it will + be MODE, but sometimes we can't do the desired operation in MODE. + If so, pick a wider mode in which we can do the operation. Convert + to that mode at the start to avoid repeated conversions. + + First see what operations we need. These depend on the expression + we are evaluating. (We assume that divxx3 insns exist under the + same conditions that modxx3 insns and that these insns don't normally + fail. If these assumptions are not correct, we may generate less + efficient code in some cases.) + + Then see if we find a mode in which we can open-code that operation + (either a division, modulus, or shift). Finally, check for the smallest + mode for which we can do the operation with a library call. */ + + optab1 = (log >= 0 ? (unsignedp ? lshr_optab : ashr_optab) + : (unsignedp ? udiv_optab : sdiv_optab)); + optab2 = (log >= 0 ? optab1 : (unsignedp ? udivmod_optab : sdivmod_optab)); + + for (compute_mode = mode; compute_mode != VOIDmode; + compute_mode = GET_MODE_WIDER_MODE (compute_mode)) + if (optab1->handlers[(int) compute_mode].insn_code != CODE_FOR_nothing + || optab2->handlers[(int) compute_mode].insn_code != CODE_FOR_nothing) + break; + + if (compute_mode == VOIDmode) + for (compute_mode = mode; compute_mode != VOIDmode; + compute_mode = GET_MODE_WIDER_MODE (compute_mode)) + if (optab1->handlers[(int) compute_mode].libfunc + || optab2->handlers[(int) compute_mode].libfunc) + break; + + /* If we still couldn't find a mode, use MODE; we'll probably abort in + expand_binop. */ + if (compute_mode == VOIDmode) + compute_mode = mode; + + size = GET_MODE_BITSIZE (compute_mode); + + /* Now convert to the best mode to use. Show we made a copy of OP0 + and hence we can clobber it (we cannot use a SUBREG to widen + something. */ + if (compute_mode != mode) + { + adjusted_op0 = op0 = convert_to_mode (compute_mode, op0, unsignedp); + can_clobber_op0 = 1; + op1 = convert_to_mode (compute_mode, op1, unsignedp); + } + + /* If we are computing the remainder and one of the operands is a volatile + MEM, copy it into a register. */ + + if (rem_flag && GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0)) + adjusted_op0 = op0 = force_reg (compute_mode, op0), can_clobber_op0 = 1; + if (rem_flag && GET_CODE (op1) == MEM && MEM_VOLATILE_P (op1)) + op1 = force_reg (compute_mode, op1); + + /* If we are computing the remainder, op0 will be needed later to calculate + X - Y * (X / Y), therefore cannot be clobbered. */ + if (rem_flag) + can_clobber_op0 = 0; + + if (target == 0 || GET_MODE (target) != compute_mode) + target = gen_reg_rtx (compute_mode); + + switch (code) + { + case TRUNC_MOD_EXPR: + case TRUNC_DIV_EXPR: + if (log >= 0 && ! unsignedp) + { + /* Here we need to add OP1-1 if OP0 is negative, 0 otherwise. + This can be computed without jumps by arithmetically shifting + OP0 right LOG-1 places and then shifting right logically + SIZE-LOG bits. The resulting value is unconditionally added + to OP0. */ + if (log == 1 || BRANCH_COST >= 3) + { + rtx temp = gen_reg_rtx (compute_mode); + if (! can_clobber_op0) + /* Copy op0 to a reg, to play safe, + since this is done in the other path. */ + op0 = force_reg (compute_mode, op0); + temp = copy_to_suggested_reg (adjusted_op0, temp, compute_mode); + temp = expand_shift (RSHIFT_EXPR, compute_mode, temp, + build_int_2 (log - 1, 0), NULL_RTX, 0); + temp = expand_shift (RSHIFT_EXPR, compute_mode, temp, + build_int_2 (size - log, 0), + temp, 1); + /* We supply 0 as the target to make a new pseudo + for the value; that helps loop.c optimize the result. */ + adjusted_op0 = expand_binop (compute_mode, add_optab, + adjusted_op0, temp, + 0, 0, OPTAB_LIB_WIDEN); + } + else + { + rtx label = gen_label_rtx (); + if (! can_clobber_op0) + { + adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target, + compute_mode); + /* Copy op0 to a reg, since emit_cmp_insn will call emit_queue + which will screw up mem refs for autoincrements. */ + op0 = force_reg (compute_mode, op0); + } + emit_cmp_insn (adjusted_op0, const0_rtx, GE, + NULL_RTX, compute_mode, 0, 0); + emit_jump_insn (gen_bge (label)); + expand_inc (adjusted_op0, plus_constant (op1, -1)); + emit_label (label); + } + mod_insn_no_good = 1; + } + break; + + case FLOOR_DIV_EXPR: + case FLOOR_MOD_EXPR: + if (log < 0 && ! unsignedp) + { + rtx label = gen_label_rtx (); + if (! can_clobber_op0) + { + adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target, + compute_mode); + /* Copy op0 to a reg, since emit_cmp_insn will call emit_queue + which will screw up mem refs for autoincrements. */ + op0 = force_reg (compute_mode, op0); + } + emit_cmp_insn (adjusted_op0, const0_rtx, GE, + NULL_RTX, compute_mode, 0, 0); + emit_jump_insn (gen_bge (label)); + expand_dec (adjusted_op0, op1); + expand_inc (adjusted_op0, const1_rtx); + emit_label (label); + mod_insn_no_good = 1; + } + break; + + case CEIL_DIV_EXPR: + case CEIL_MOD_EXPR: + if (! can_clobber_op0) + { + adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target, + compute_mode); + /* Copy op0 to a reg, since emit_cmp_insn will call emit_queue + which will screw up mem refs for autoincrements. */ + op0 = force_reg (compute_mode, op0); + } + if (log < 0) + { + rtx label = 0; + if (! unsignedp) + { + label = gen_label_rtx (); + emit_cmp_insn (adjusted_op0, const0_rtx, LE, + NULL_RTX, compute_mode, 0, 0); + emit_jump_insn (gen_ble (label)); + } + expand_inc (adjusted_op0, op1); + expand_dec (adjusted_op0, const1_rtx); + if (! unsignedp) + emit_label (label); + } + else + { + adjusted_op0 = expand_binop (compute_mode, add_optab, + adjusted_op0, plus_constant (op1, -1), + NULL_RTX, 0, OPTAB_LIB_WIDEN); + } + mod_insn_no_good = 1; + break; + + case ROUND_DIV_EXPR: + case ROUND_MOD_EXPR: + if (! can_clobber_op0) + { + adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target, + compute_mode); + /* Copy op0 to a reg, since emit_cmp_insn will call emit_queue + which will screw up mem refs for autoincrements. */ + op0 = force_reg (compute_mode, op0); + } + if (log < 0) + { + op1 = expand_shift (RSHIFT_EXPR, compute_mode, op1, + integer_one_node, NULL_RTX, 0); + if (! unsignedp) + { + if (BRANCH_COST >= 2) + { + /* Negate OP1 if OP0 < 0. Do this by computing a temporary + that has all bits equal to the sign bit and exclusive + or-ing it with OP1. */ + rtx temp = gen_reg_rtx (compute_mode); + temp = copy_to_suggested_reg (adjusted_op0, temp, compute_mode); + temp = expand_shift (RSHIFT_EXPR, compute_mode, temp, + build_int_2 (size - 1, 0), + NULL_RTX, 0); + op1 = expand_binop (compute_mode, xor_optab, op1, temp, op1, + unsignedp, OPTAB_LIB_WIDEN); + } + else + { + rtx label = gen_label_rtx (); + emit_cmp_insn (adjusted_op0, const0_rtx, GE, NULL_RTX, + compute_mode, 0, 0); + emit_jump_insn (gen_bge (label)); + expand_unop (compute_mode, neg_optab, op1, op1, 0); + emit_label (label); + } + } + expand_inc (adjusted_op0, op1); + } + else + { + op1 = GEN_INT (((HOST_WIDE_INT) 1 << log) / 2); + expand_inc (adjusted_op0, op1); + } + mod_insn_no_good = 1; + break; + } + + if (rem_flag && !mod_insn_no_good) + { + /* Try to produce the remainder directly */ + if (log >= 0) + result = expand_binop (compute_mode, and_optab, adjusted_op0, + GEN_INT (((HOST_WIDE_INT) 1 << log) - 1), + target, 1, OPTAB_LIB_WIDEN); + else + { + /* See if we can do remainder without a library call. */ + result = sign_expand_binop (mode, umod_optab, smod_optab, + adjusted_op0, op1, target, + unsignedp, OPTAB_WIDEN); + if (result == 0) + { + /* No luck there. Can we do remainder and divide at once + without a library call? */ + result = gen_reg_rtx (compute_mode); + if (! expand_twoval_binop (unsignedp + ? udivmod_optab : sdivmod_optab, + adjusted_op0, op1, + NULL_RTX, result, unsignedp)) + result = 0; + } + } + } + + if (result) + return gen_lowpart (mode, result); + + /* Produce the quotient. */ + if (log >= 0) + result = expand_shift (RSHIFT_EXPR, compute_mode, adjusted_op0, + build_int_2 (log, 0), target, unsignedp); + else if (rem_flag && !mod_insn_no_good) + /* If producing quotient in order to subtract for remainder, + and a remainder subroutine would be ok, + don't use a divide subroutine. */ + result = sign_expand_binop (compute_mode, udiv_optab, sdiv_optab, + adjusted_op0, op1, NULL_RTX, unsignedp, + OPTAB_WIDEN); + else + { + /* Try a quotient insn, but not a library call. */ + result = sign_expand_binop (compute_mode, udiv_optab, sdiv_optab, + adjusted_op0, op1, + rem_flag ? NULL_RTX : target, + unsignedp, OPTAB_WIDEN); + if (result == 0) + { + /* No luck there. Try a quotient-and-remainder insn, + keeping the quotient alone. */ + result = gen_reg_rtx (mode); + if (! expand_twoval_binop (unsignedp ? udivmod_optab : sdivmod_optab, + adjusted_op0, op1, + result, NULL_RTX, unsignedp)) + result = 0; + } + + /* If still no luck, use a library call. */ + if (result == 0) + result = sign_expand_binop (compute_mode, udiv_optab, sdiv_optab, + adjusted_op0, op1, + rem_flag ? NULL_RTX : target, + unsignedp, OPTAB_LIB_WIDEN); + } + + /* If we really want the remainder, get it by subtraction. */ + if (rem_flag) + { + if (result == 0) + /* No divide instruction either. Use library for remainder. */ + result = sign_expand_binop (compute_mode, umod_optab, smod_optab, + op0, op1, target, + unsignedp, OPTAB_LIB_WIDEN); + else + { + /* We divided. Now finish doing X - Y * (X / Y). */ + result = expand_mult (compute_mode, result, op1, target, unsignedp); + if (! result) abort (); + result = expand_binop (compute_mode, sub_optab, op0, + result, target, unsignedp, OPTAB_LIB_WIDEN); + } + } + + if (result == 0) + abort (); + + return gen_lowpart (mode, result); +} + +/* Return a tree node with data type TYPE, describing the value of X. + Usually this is an RTL_EXPR, if there is no obvious better choice. + X may be an expression, however we only support those expressions + generated by loop.c. */ + +tree +make_tree (type, x) + tree type; + rtx x; +{ + tree t; + + switch (GET_CODE (x)) + { + case CONST_INT: + t = build_int_2 (INTVAL (x), + ! TREE_UNSIGNED (type) && INTVAL (x) >= 0 ? 0 : -1); + TREE_TYPE (t) = type; + return t; + + case CONST_DOUBLE: + if (GET_MODE (x) == VOIDmode) + { + t = build_int_2 (CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x)); + TREE_TYPE (t) = type; + } + else + { + REAL_VALUE_TYPE d; + + REAL_VALUE_FROM_CONST_DOUBLE (d, x); + t = build_real (type, d); + } + + return t; + + case PLUS: + return fold (build (PLUS_EXPR, type, make_tree (type, XEXP (x, 0)), + make_tree (type, XEXP (x, 1)))); + + case MINUS: + return fold (build (MINUS_EXPR, type, make_tree (type, XEXP (x, 0)), + make_tree (type, XEXP (x, 1)))); + + case NEG: + return fold (build1 (NEGATE_EXPR, type, make_tree (type, XEXP (x, 0)))); + + case MULT: + return fold (build (MULT_EXPR, type, make_tree (type, XEXP (x, 0)), + make_tree (type, XEXP (x, 1)))); + + case ASHIFT: + return fold (build (LSHIFT_EXPR, type, make_tree (type, XEXP (x, 0)), + make_tree (type, XEXP (x, 1)))); + + case LSHIFTRT: + return fold (convert (type, + build (RSHIFT_EXPR, unsigned_type (type), + make_tree (unsigned_type (type), + XEXP (x, 0)), + make_tree (type, XEXP (x, 1))))); + + case ASHIFTRT: + return fold (convert (type, + build (RSHIFT_EXPR, signed_type (type), + make_tree (signed_type (type), XEXP (x, 0)), + make_tree (type, XEXP (x, 1))))); + + case DIV: + if (TREE_CODE (type) != REAL_TYPE) + t = signed_type (type); + else + t = type; + + return fold (convert (type, + build (TRUNC_DIV_EXPR, t, + make_tree (t, XEXP (x, 0)), + make_tree (t, XEXP (x, 1))))); + case UDIV: + t = unsigned_type (type); + return fold (convert (type, + build (TRUNC_DIV_EXPR, t, + make_tree (t, XEXP (x, 0)), + make_tree (t, XEXP (x, 1))))); + default: + t = make_node (RTL_EXPR); + TREE_TYPE (t) = type; + RTL_EXPR_RTL (t) = x; + /* There are no insns to be output + when this rtl_expr is used. */ + RTL_EXPR_SEQUENCE (t) = 0; + return t; + } +} + +/* Return an rtx representing the value of X * MULT + ADD. + TARGET is a suggestion for where to store the result (an rtx). + MODE is the machine mode for the computation. + X and MULT must have mode MODE. ADD may have a different mode. + So can X (defaults to same as MODE). + UNSIGNEDP is non-zero to do unsigned multiplication. + This may emit insns. */ + +rtx +expand_mult_add (x, target, mult, add, mode, unsignedp) + rtx x, target, mult, add; + enum machine_mode mode; + int unsignedp; +{ + tree type = type_for_mode (mode, unsignedp); + tree add_type = (GET_MODE (add) == VOIDmode + ? type : type_for_mode (GET_MODE (add), unsignedp)); + tree result = fold (build (PLUS_EXPR, type, + fold (build (MULT_EXPR, type, + make_tree (type, x), + make_tree (type, mult))), + make_tree (add_type, add))); + + return expand_expr (result, target, VOIDmode, 0); +} + +/* Compute the logical-and of OP0 and OP1, storing it in TARGET + and returning TARGET. + + If TARGET is 0, a pseudo-register or constant is returned. */ + +rtx +expand_and (op0, op1, target) + rtx op0, op1, target; +{ + enum machine_mode mode = VOIDmode; + rtx tem; + + if (GET_MODE (op0) != VOIDmode) + mode = GET_MODE (op0); + else if (GET_MODE (op1) != VOIDmode) + mode = GET_MODE (op1); + + if (mode != VOIDmode) + tem = expand_binop (mode, and_optab, op0, op1, target, 0, OPTAB_LIB_WIDEN); + else if (GET_CODE (op0) == CONST_INT && GET_CODE (op1) == CONST_INT) + tem = GEN_INT (INTVAL (op0) & INTVAL (op1)); + else + abort (); + + if (target == 0) + target = tem; + else if (tem != target) + emit_move_insn (target, tem); + return target; +} + +/* Emit a store-flags instruction for comparison CODE on OP0 and OP1 + and storing in TARGET. Normally return TARGET. + Return 0 if that cannot be done. + + MODE is the mode to use for OP0 and OP1 should they be CONST_INTs. If + it is VOIDmode, they cannot both be CONST_INT. + + UNSIGNEDP is for the case where we have to widen the operands + to perform the operation. It says to use zero-extension. + + NORMALIZEP is 1 if we should convert the result to be either zero + or one one. Normalize is -1 if we should convert the result to be + either zero or -1. If NORMALIZEP is zero, the result will be left + "raw" out of the scc insn. */ + +rtx +emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep) + rtx target; + enum rtx_code code; + rtx op0, op1; + enum machine_mode mode; + int unsignedp; + int normalizep; +{ + rtx subtarget; + enum insn_code icode; + enum machine_mode compare_mode; + enum machine_mode target_mode = GET_MODE (target); + rtx tem; + rtx last = 0; + rtx pattern, comparison; + + if (mode == VOIDmode) + mode = GET_MODE (op0); + + /* If one operand is constant, make it the second one. Only do this + if the other operand is not constant as well. */ + + if ((CONSTANT_P (op0) && ! CONSTANT_P (op1)) + || (GET_CODE (op0) == CONST_INT && GET_CODE (op1) != CONST_INT)) + { + tem = op0; + op0 = op1; + op1 = tem; + code = swap_condition (code); + } + + /* For some comparisons with 1 and -1, we can convert this to + comparisons with zero. This will often produce more opportunities for + store-flag insns. */ + + switch (code) + { + case LT: + if (op1 == const1_rtx) + op1 = const0_rtx, code = LE; + break; + case LE: + if (op1 == constm1_rtx) + op1 = const0_rtx, code = LT; + break; + case GE: + if (op1 == const1_rtx) + op1 = const0_rtx, code = GT; + break; + case GT: + if (op1 == constm1_rtx) + op1 = const0_rtx, code = GE; + break; + case GEU: + if (op1 == const1_rtx) + op1 = const0_rtx, code = NE; + break; + case LTU: + if (op1 == const1_rtx) + op1 = const0_rtx, code = EQ; + break; + } + + /* From now on, we won't change CODE, so set ICODE now. */ + icode = setcc_gen_code[(int) code]; + + /* If this is A < 0 or A >= 0, we can do this by taking the ones + complement of A (for GE) and shifting the sign bit to the low bit. */ + if (op1 == const0_rtx && (code == LT || code == GE) + && GET_MODE_CLASS (mode) == MODE_INT + && (normalizep || STORE_FLAG_VALUE == 1 + || (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT + && (STORE_FLAG_VALUE + == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))))) + { + subtarget = target; + + /* If the result is to be wider than OP0, it is best to convert it + first. If it is to be narrower, it is *incorrect* to convert it + first. */ + if (GET_MODE_SIZE (target_mode) > GET_MODE_SIZE (mode)) + { + op0 = protect_from_queue (op0, 0); + op0 = convert_to_mode (target_mode, op0, 0); + mode = target_mode; + } + + if (target_mode != mode) + subtarget = 0; + + if (code == GE) + op0 = expand_unop (mode, one_cmpl_optab, op0, subtarget, 0); + + if (normalizep || STORE_FLAG_VALUE == 1) + /* If we are supposed to produce a 0/1 value, we want to do + a logical shift from the sign bit to the low-order bit; for + a -1/0 value, we do an arithmetic shift. */ + op0 = expand_shift (RSHIFT_EXPR, mode, op0, + size_int (GET_MODE_BITSIZE (mode) - 1), + subtarget, normalizep != -1); + + if (mode != target_mode) + op0 = convert_to_mode (target_mode, op0, 0); + + return op0; + } + + if (icode != CODE_FOR_nothing) + { + /* We think we may be able to do this with a scc insn. Emit the + comparison and then the scc insn. + + compare_from_rtx may call emit_queue, which would be deleted below + if the scc insn fails. So call it ourselves before setting LAST. */ + + emit_queue (); + last = get_last_insn (); + + comparison + = compare_from_rtx (op0, op1, code, unsignedp, mode, NULL_RTX, 0); + if (GET_CODE (comparison) == CONST_INT) + return (comparison == const0_rtx ? const0_rtx + : normalizep == 1 ? const1_rtx + : normalizep == -1 ? constm1_rtx + : const_true_rtx); + + /* If the code of COMPARISON doesn't match CODE, something is + wrong; we can no longer be sure that we have the operation. + We could handle this case, but it should not happen. */ + + if (GET_CODE (comparison) != code) + abort (); + + /* Get a reference to the target in the proper mode for this insn. */ + compare_mode = insn_operand_mode[(int) icode][0]; + subtarget = target; + if (preserve_subexpressions_p () + || ! (*insn_operand_predicate[(int) icode][0]) (subtarget, compare_mode)) + subtarget = gen_reg_rtx (compare_mode); + + pattern = GEN_FCN (icode) (subtarget); + if (pattern) + { + emit_insn (pattern); + + /* If we are converting to a wider mode, first convert to + TARGET_MODE, then normalize. This produces better combining + opportunities on machines that have a SIGN_EXTRACT when we are + testing a single bit. This mostly benefits the 68k. + + If STORE_FLAG_VALUE does not have the sign bit set when + interpreted in COMPARE_MODE, we can do this conversion as + unsigned, which is usually more efficient. */ + if (GET_MODE_SIZE (target_mode) > GET_MODE_SIZE (compare_mode)) + { + convert_move (target, subtarget, + (GET_MODE_BITSIZE (compare_mode) + <= HOST_BITS_PER_WIDE_INT) + && 0 == (STORE_FLAG_VALUE + & ((HOST_WIDE_INT) 1 + << (GET_MODE_BITSIZE (compare_mode) -1)))); + op0 = target; + compare_mode = target_mode; + } + else + op0 = subtarget; + + /* If we want to keep subexpressions around, don't reuse our + last target. */ + + if (preserve_subexpressions_p ()) + subtarget = 0; + + /* Now normalize to the proper value in COMPARE_MODE. Sometimes + we don't have to do anything. */ + if (normalizep == 0 || normalizep == STORE_FLAG_VALUE) + ; + else if (normalizep == - STORE_FLAG_VALUE) + op0 = expand_unop (compare_mode, neg_optab, op0, subtarget, 0); + + /* We don't want to use STORE_FLAG_VALUE < 0 below since this + makes it hard to use a value of just the sign bit due to + ANSI integer constant typing rules. */ + else if (GET_MODE_BITSIZE (compare_mode) <= HOST_BITS_PER_WIDE_INT + && (STORE_FLAG_VALUE + & ((HOST_WIDE_INT) 1 + << (GET_MODE_BITSIZE (compare_mode) - 1)))) + op0 = expand_shift (RSHIFT_EXPR, compare_mode, op0, + size_int (GET_MODE_BITSIZE (compare_mode) - 1), + subtarget, normalizep == 1); + else if (STORE_FLAG_VALUE & 1) + { + op0 = expand_and (op0, const1_rtx, subtarget); + if (normalizep == -1) + op0 = expand_unop (compare_mode, neg_optab, op0, op0, 0); + } + else + abort (); + + /* If we were converting to a smaller mode, do the + conversion now. */ + if (target_mode != compare_mode) + { + convert_move (target, op0, 0); + return target; + } + else + return op0; + } + } + + if (last) + delete_insns_since (last); + + subtarget = target_mode == mode ? target : 0; + + /* If we reached here, we can't do this with a scc insn. However, there + are some comparisons that can be done directly. For example, if + this is an equality comparison of integers, we can try to exclusive-or + (or subtract) the two operands and use a recursive call to try the + comparison with zero. Don't do any of these cases if branches are + very cheap. */ + + if (BRANCH_COST > 0 + && GET_MODE_CLASS (mode) == MODE_INT && (code == EQ || code == NE) + && op1 != const0_rtx) + { + tem = expand_binop (mode, xor_optab, op0, op1, subtarget, 1, + OPTAB_WIDEN); + + if (tem == 0) + tem = expand_binop (mode, sub_optab, op0, op1, subtarget, 1, + OPTAB_WIDEN); + if (tem != 0) + tem = emit_store_flag (target, code, tem, const0_rtx, + mode, unsignedp, normalizep); + if (tem == 0) + delete_insns_since (last); + return tem; + } + + /* Some other cases we can do are EQ, NE, LE, and GT comparisons with + the constant zero. Reject all other comparisons at this point. Only + do LE and GT if branches are expensive since they are expensive on + 2-operand machines. */ + + if (BRANCH_COST == 0 + || GET_MODE_CLASS (mode) != MODE_INT || op1 != const0_rtx + || (code != EQ && code != NE + && (BRANCH_COST <= 1 || (code != LE && code != GT)))) + return 0; + + /* See what we need to return. We can only return a 1, -1, or the + sign bit. */ + + if (normalizep == 0) + { + if (STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1) + normalizep = STORE_FLAG_VALUE; + + else if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT + && (STORE_FLAG_VALUE + == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))) + ; + else + return 0; + } + + /* Try to put the result of the comparison in the sign bit. Assume we can't + do the necessary operation below. */ + + tem = 0; + + /* To see if A <= 0, compute (A | (A - 1)). A <= 0 iff that result has + the sign bit set. */ + + if (code == LE) + { + /* This is destructive, so SUBTARGET can't be OP0. */ + if (rtx_equal_p (subtarget, op0)) + subtarget = 0; + + tem = expand_binop (mode, sub_optab, op0, const1_rtx, subtarget, 0, + OPTAB_WIDEN); + if (tem) + tem = expand_binop (mode, ior_optab, op0, tem, subtarget, 0, + OPTAB_WIDEN); + } + + /* To see if A > 0, compute (((signed) A) << BITS) - A, where BITS is the + number of bits in the mode of OP0, minus one. */ + + if (code == GT) + { + if (rtx_equal_p (subtarget, op0)) + subtarget = 0; + + tem = expand_shift (RSHIFT_EXPR, mode, op0, + size_int (GET_MODE_BITSIZE (mode) - 1), + subtarget, 0); + tem = expand_binop (mode, sub_optab, tem, op0, subtarget, 0, + OPTAB_WIDEN); + } + + if (code == EQ || code == NE) + { + /* For EQ or NE, one way to do the comparison is to apply an operation + that converts the operand into a positive number if it is non-zero + or zero if it was originally zero. Then, for EQ, we subtract 1 and + for NE we negate. This puts the result in the sign bit. Then we + normalize with a shift, if needed. + + Two operations that can do the above actions are ABS and FFS, so try + them. If that doesn't work, and MODE is smaller than a full word, + we can use zero-extension to the wider mode (an unsigned conversion) + as the operation. */ + + if (abs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) + tem = expand_unop (mode, abs_optab, op0, subtarget, 1); + else if (ffs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) + tem = expand_unop (mode, ffs_optab, op0, subtarget, 1); + else if (GET_MODE_SIZE (mode) < UNITS_PER_WORD) + { + mode = word_mode; + op0 = protect_from_queue (op0, 0); + tem = convert_to_mode (mode, op0, 1); + } + + if (tem != 0) + { + if (code == EQ) + tem = expand_binop (mode, sub_optab, tem, const1_rtx, subtarget, + 0, OPTAB_WIDEN); + else + tem = expand_unop (mode, neg_optab, tem, subtarget, 0); + } + + /* If we couldn't do it that way, for NE we can "or" the two's complement + of the value with itself. For EQ, we take the one's complement of + that "or", which is an extra insn, so we only handle EQ if branches + are expensive. */ + + if (tem == 0 && (code == NE || BRANCH_COST > 1)) + { + if (rtx_equal_p (subtarget, op0)) + subtarget = 0; + + tem = expand_unop (mode, neg_optab, op0, subtarget, 0); + tem = expand_binop (mode, ior_optab, tem, op0, subtarget, 0, + OPTAB_WIDEN); + + if (tem && code == EQ) + tem = expand_unop (mode, one_cmpl_optab, tem, subtarget, 0); + } + } + + if (tem && normalizep) + tem = expand_shift (RSHIFT_EXPR, mode, tem, + size_int (GET_MODE_BITSIZE (mode) - 1), + tem, normalizep == 1); + + if (tem && GET_MODE (tem) != target_mode) + { + convert_move (target, tem, 0); + tem = target; + } + + if (tem == 0) + delete_insns_since (last); + + return tem; +} diff --git a/gnu/usr.bin/cc/lib/expr.c b/gnu/usr.bin/cc/lib/expr.c new file mode 100644 index 0000000000..d866d3c98e --- /dev/null +++ b/gnu/usr.bin/cc/lib/expr.c @@ -0,0 +1,7994 @@ +/* Convert tree expression to rtl instructions, for GNU compiler. + Copyright (C) 1988, 1992, 1993 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include "config.h" +#include "rtl.h" +#include "tree.h" +#include "flags.h" +#include "function.h" +#include "insn-flags.h" +#include "insn-codes.h" +#include "expr.h" +#include "insn-config.h" +#include "recog.h" +#include "output.h" +#include "typeclass.h" + +#define CEIL(x,y) (((x) + (y) - 1) / (y)) + +/* Decide whether a function's arguments should be processed + from first to last or from last to first. + + They should if the stack and args grow in opposite directions, but + only if we have push insns. */ + +#ifdef PUSH_ROUNDING + +#if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNARD) +#define PUSH_ARGS_REVERSED /* If it's last to first */ +#endif + +#endif + +#ifndef STACK_PUSH_CODE +#ifdef STACK_GROWS_DOWNWARD +#define STACK_PUSH_CODE PRE_DEC +#else +#define STACK_PUSH_CODE PRE_INC +#endif +#endif + +/* Like STACK_BOUNDARY but in units of bytes, not bits. */ +#define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT) + +/* If this is nonzero, we do not bother generating VOLATILE + around volatile memory references, and we are willing to + output indirect addresses. If cse is to follow, we reject + indirect addresses so a useful potential cse is generated; + if it is used only once, instruction combination will produce + the same indirect address eventually. */ +int cse_not_expected; + +/* Nonzero to generate code for all the subroutines within an + expression before generating the upper levels of the expression. + Nowadays this is never zero. */ +int do_preexpand_calls = 1; + +/* Number of units that we should eventually pop off the stack. + These are the arguments to function calls that have already returned. */ +int pending_stack_adjust; + +/* Nonzero means stack pops must not be deferred, and deferred stack + pops must not be output. It is nonzero inside a function call, + inside a conditional expression, inside a statement expression, + and in other cases as well. */ +int inhibit_defer_pop; + +/* A list of all cleanups which belong to the arguments of + function calls being expanded by expand_call. */ +tree cleanups_this_call; + +/* Nonzero means __builtin_saveregs has already been done in this function. + The value is the pseudoreg containing the value __builtin_saveregs + returned. */ +static rtx saveregs_value; + +/* Similarly for __builtin_apply_args. */ +static rtx apply_args_value; + +/* This structure is used by move_by_pieces to describe the move to + be performed. */ + +struct move_by_pieces +{ + rtx to; + rtx to_addr; + int autinc_to; + int explicit_inc_to; + rtx from; + rtx from_addr; + int autinc_from; + int explicit_inc_from; + int len; + int offset; + int reverse; +}; + +static rtx enqueue_insn PROTO((rtx, rtx)); +static int queued_subexp_p PROTO((rtx)); +static void init_queue PROTO((void)); +static void move_by_pieces PROTO((rtx, rtx, int, int)); +static int move_by_pieces_ninsns PROTO((unsigned int, int)); +static void move_by_pieces_1 PROTO((rtx (*) (), enum machine_mode, + struct move_by_pieces *)); +static void group_insns PROTO((rtx)); +static void store_constructor PROTO((tree, rtx)); +static rtx store_field PROTO((rtx, int, int, enum machine_mode, tree, + enum machine_mode, int, int, int)); +static tree save_noncopied_parts PROTO((tree, tree)); +static tree init_noncopied_parts PROTO((tree, tree)); +static int safe_from_p PROTO((rtx, tree)); +static int fixed_type_p PROTO((tree)); +static int get_pointer_alignment PROTO((tree, unsigned)); +static tree string_constant PROTO((tree, tree *)); +static tree c_strlen PROTO((tree)); +static rtx expand_builtin PROTO((tree, rtx, rtx, enum machine_mode, int)); +static int apply_args_size PROTO((void)); +static int apply_result_size PROTO((void)); +static rtx result_vector PROTO((int, rtx)); +static rtx expand_builtin_apply_args PROTO((void)); +static rtx expand_builtin_apply PROTO((rtx, rtx, rtx)); +static void expand_builtin_return PROTO((rtx)); +static rtx expand_increment PROTO((tree, int)); +static void preexpand_calls PROTO((tree)); +static void do_jump_by_parts_greater PROTO((tree, int, rtx, rtx)); +static void do_jump_by_parts_greater_rtx PROTO((enum machine_mode, int, rtx, rtx, rtx, rtx)); +static void do_jump_by_parts_equality PROTO((tree, rtx, rtx)); +static void do_jump_by_parts_equality_rtx PROTO((rtx, rtx, rtx)); +static void do_jump_for_compare PROTO((rtx, rtx, rtx)); +static rtx compare PROTO((tree, enum rtx_code, enum rtx_code)); +static rtx do_store_flag PROTO((tree, rtx, enum machine_mode, int)); + +/* Record for each mode whether we can move a register directly to or + from an object of that mode in memory. If we can't, we won't try + to use that mode directly when accessing a field of that mode. */ + +static char direct_load[NUM_MACHINE_MODES]; +static char direct_store[NUM_MACHINE_MODES]; + +/* MOVE_RATIO is the number of move instructions that is better than + a block move. */ + +#ifndef MOVE_RATIO +#if defined (HAVE_movstrqi) || defined (HAVE_movstrhi) || defined (HAVE_movstrsi) || defined (HAVE_movstrdi) || defined (HAVE_movstrti) +#define MOVE_RATIO 2 +#else +/* A value of around 6 would minimize code size; infinity would minimize + execution time. */ +#define MOVE_RATIO 15 +#endif +#endif + +/* This array records the insn_code of insns to perform block moves. */ +enum insn_code movstr_optab[NUM_MACHINE_MODES]; + +/* SLOW_UNALIGNED_ACCESS is non-zero if unaligned accesses are very slow. */ + +#ifndef SLOW_UNALIGNED_ACCESS +#define SLOW_UNALIGNED_ACCESS 0 +#endif + +/* Register mappings for target machines without register windows. */ +#ifndef INCOMING_REGNO +#define INCOMING_REGNO(OUT) (OUT) +#endif +#ifndef OUTGOING_REGNO +#define OUTGOING_REGNO(IN) (IN) +#endif + +/* This is run once per compilation to set up which modes can be used + directly in memory and to initialize the block move optab. */ + +void +init_expr_once () +{ + rtx insn, pat; + enum machine_mode mode; + /* Try indexing by frame ptr and try by stack ptr. + It is known that on the Convex the stack ptr isn't a valid index. + With luck, one or the other is valid on any machine. */ + rtx mem = gen_rtx (MEM, VOIDmode, stack_pointer_rtx); + rtx mem1 = gen_rtx (MEM, VOIDmode, frame_pointer_rtx); + + start_sequence (); + insn = emit_insn (gen_rtx (SET, 0, 0)); + pat = PATTERN (insn); + + for (mode = VOIDmode; (int) mode < NUM_MACHINE_MODES; + mode = (enum machine_mode) ((int) mode + 1)) + { + int regno; + rtx reg; + int num_clobbers; + + direct_load[(int) mode] = direct_store[(int) mode] = 0; + PUT_MODE (mem, mode); + PUT_MODE (mem1, mode); + + /* See if there is some register that can be used in this mode and + directly loaded or stored from memory. */ + + if (mode != VOIDmode && mode != BLKmode) + for (regno = 0; regno < FIRST_PSEUDO_REGISTER + && (direct_load[(int) mode] == 0 || direct_store[(int) mode] == 0); + regno++) + { + if (! HARD_REGNO_MODE_OK (regno, mode)) + continue; + + reg = gen_rtx (REG, mode, regno); + + SET_SRC (pat) = mem; + SET_DEST (pat) = reg; + if (recog (pat, insn, &num_clobbers) >= 0) + direct_load[(int) mode] = 1; + + SET_SRC (pat) = mem1; + SET_DEST (pat) = reg; + if (recog (pat, insn, &num_clobbers) >= 0) + direct_load[(int) mode] = 1; + + SET_SRC (pat) = reg; + SET_DEST (pat) = mem; + if (recog (pat, insn, &num_clobbers) >= 0) + direct_store[(int) mode] = 1; + + SET_SRC (pat) = reg; + SET_DEST (pat) = mem1; + if (recog (pat, insn, &num_clobbers) >= 0) + direct_store[(int) mode] = 1; + } + } + + end_sequence (); +} + +/* This is run at the start of compiling a function. */ + +void +init_expr () +{ + init_queue (); + + pending_stack_adjust = 0; + inhibit_defer_pop = 0; + cleanups_this_call = 0; + saveregs_value = 0; + apply_args_value = 0; + forced_labels = 0; +} + +/* Save all variables describing the current status into the structure *P. + This is used before starting a nested function. */ + +void +save_expr_status (p) + struct function *p; +{ + /* Instead of saving the postincrement queue, empty it. */ + emit_queue (); + + p->pending_stack_adjust = pending_stack_adjust; + p->inhibit_defer_pop = inhibit_defer_pop; + p->cleanups_this_call = cleanups_this_call; + p->saveregs_value = saveregs_value; + p->apply_args_value = apply_args_value; + p->forced_labels = forced_labels; + + pending_stack_adjust = 0; + inhibit_defer_pop = 0; + cleanups_this_call = 0; + saveregs_value = 0; + apply_args_value = 0; + forced_labels = 0; +} + +/* Restore all variables describing the current status from the structure *P. + This is used after a nested function. */ + +void +restore_expr_status (p) + struct function *p; +{ + pending_stack_adjust = p->pending_stack_adjust; + inhibit_defer_pop = p->inhibit_defer_pop; + cleanups_this_call = p->cleanups_this_call; + saveregs_value = p->saveregs_value; + apply_args_value = p->apply_args_value; + forced_labels = p->forced_labels; +} + +/* Manage the queue of increment instructions to be output + for POSTINCREMENT_EXPR expressions, etc. */ + +static rtx pending_chain; + +/* Queue up to increment (or change) VAR later. BODY says how: + BODY should be the same thing you would pass to emit_insn + to increment right away. It will go to emit_insn later on. + + The value is a QUEUED expression to be used in place of VAR + where you want to guarantee the pre-incrementation value of VAR. */ + +static rtx +enqueue_insn (var, body) + rtx var, body; +{ + pending_chain = gen_rtx (QUEUED, GET_MODE (var), + var, NULL_RTX, NULL_RTX, body, pending_chain); + return pending_chain; +} + +/* Use protect_from_queue to convert a QUEUED expression + into something that you can put immediately into an instruction. + If the queued incrementation has not happened yet, + protect_from_queue returns the variable itself. + If the incrementation has happened, protect_from_queue returns a temp + that contains a copy of the old value of the variable. + + Any time an rtx which might possibly be a QUEUED is to be put + into an instruction, it must be passed through protect_from_queue first. + QUEUED expressions are not meaningful in instructions. + + Do not pass a value through protect_from_queue and then hold + on to it for a while before putting it in an instruction! + If the queue is flushed in between, incorrect code will result. */ + +rtx +protect_from_queue (x, modify) + register rtx x; + int modify; +{ + register RTX_CODE code = GET_CODE (x); + +#if 0 /* A QUEUED can hang around after the queue is forced out. */ + /* Shortcut for most common case. */ + if (pending_chain == 0) + return x; +#endif + + if (code != QUEUED) + { + /* A special hack for read access to (MEM (QUEUED ...)) + to facilitate use of autoincrement. + Make a copy of the contents of the memory location + rather than a copy of the address, but not + if the value is of mode BLKmode. */ + if (code == MEM && GET_MODE (x) != BLKmode + && GET_CODE (XEXP (x, 0)) == QUEUED && !modify) + { + register rtx y = XEXP (x, 0); + XEXP (x, 0) = QUEUED_VAR (y); + if (QUEUED_INSN (y)) + { + register rtx temp = gen_reg_rtx (GET_MODE (x)); + emit_insn_before (gen_move_insn (temp, x), + QUEUED_INSN (y)); + return temp; + } + return x; + } + /* Otherwise, recursively protect the subexpressions of all + the kinds of rtx's that can contain a QUEUED. */ + if (code == MEM) + XEXP (x, 0) = protect_from_queue (XEXP (x, 0), 0); + else if (code == PLUS || code == MULT) + { + XEXP (x, 0) = protect_from_queue (XEXP (x, 0), 0); + XEXP (x, 1) = protect_from_queue (XEXP (x, 1), 0); + } + return x; + } + /* If the increment has not happened, use the variable itself. */ + if (QUEUED_INSN (x) == 0) + return QUEUED_VAR (x); + /* If the increment has happened and a pre-increment copy exists, + use that copy. */ + if (QUEUED_COPY (x) != 0) + return QUEUED_COPY (x); + /* The increment has happened but we haven't set up a pre-increment copy. + Set one up now, and use it. */ + QUEUED_COPY (x) = gen_reg_rtx (GET_MODE (QUEUED_VAR (x))); + emit_insn_before (gen_move_insn (QUEUED_COPY (x), QUEUED_VAR (x)), + QUEUED_INSN (x)); + return QUEUED_COPY (x); +} + +/* Return nonzero if X contains a QUEUED expression: + if it contains anything that will be altered by a queued increment. + We handle only combinations of MEM, PLUS, MINUS and MULT operators + since memory addresses generally contain only those. */ + +static int +queued_subexp_p (x) + rtx x; +{ + register enum rtx_code code = GET_CODE (x); + switch (code) + { + case QUEUED: + return 1; + case MEM: + return queued_subexp_p (XEXP (x, 0)); + case MULT: + case PLUS: + case MINUS: + return queued_subexp_p (XEXP (x, 0)) + || queued_subexp_p (XEXP (x, 1)); + } + return 0; +} + +/* Perform all the pending incrementations. */ + +void +emit_queue () +{ + register rtx p; + while (p = pending_chain) + { + QUEUED_INSN (p) = emit_insn (QUEUED_BODY (p)); + pending_chain = QUEUED_NEXT (p); + } +} + +static void +init_queue () +{ + if (pending_chain) + abort (); +} + +/* Copy data from FROM to TO, where the machine modes are not the same. + Both modes may be integer, or both may be floating. + UNSIGNEDP should be nonzero if FROM is an unsigned type. + This causes zero-extension instead of sign-extension. */ + +void +convert_move (to, from, unsignedp) + register rtx to, from; + int unsignedp; +{ + enum machine_mode to_mode = GET_MODE (to); + enum machine_mode from_mode = GET_MODE (from); + int to_real = GET_MODE_CLASS (to_mode) == MODE_FLOAT; + int from_real = GET_MODE_CLASS (from_mode) == MODE_FLOAT; + enum insn_code code; + rtx libcall; + + /* rtx code for making an equivalent value. */ + enum rtx_code equiv_code = (unsignedp ? ZERO_EXTEND : SIGN_EXTEND); + + to = protect_from_queue (to, 1); + from = protect_from_queue (from, 0); + + if (to_real != from_real) + abort (); + + /* If FROM is a SUBREG that indicates that we have already done at least + the required extension, strip it. We don't handle such SUBREGs as + TO here. */ + + if (GET_CODE (from) == SUBREG && SUBREG_PROMOTED_VAR_P (from) + && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (from))) + >= GET_MODE_SIZE (to_mode)) + && SUBREG_PROMOTED_UNSIGNED_P (from) == unsignedp) + from = gen_lowpart (to_mode, from), from_mode = to_mode; + + if (GET_CODE (to) == SUBREG && SUBREG_PROMOTED_VAR_P (to)) + abort (); + + if (to_mode == from_mode + || (from_mode == VOIDmode && CONSTANT_P (from))) + { + emit_move_insn (to, from); + return; + } + + if (to_real) + { +#ifdef HAVE_extendqfhf2 + if (HAVE_extendqfsf2 && from_mode == QFmode && to_mode == HFmode) + { + emit_unop_insn (CODE_FOR_extendqfsf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_extendqfsf2 + if (HAVE_extendqfsf2 && from_mode == QFmode && to_mode == SFmode) + { + emit_unop_insn (CODE_FOR_extendqfsf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_extendqfdf2 + if (HAVE_extendqfdf2 && from_mode == QFmode && to_mode == DFmode) + { + emit_unop_insn (CODE_FOR_extendqfdf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_extendqfxf2 + if (HAVE_extendqfxf2 && from_mode == QFmode && to_mode == XFmode) + { + emit_unop_insn (CODE_FOR_extendqfxf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_extendqftf2 + if (HAVE_extendqftf2 && from_mode == QFmode && to_mode == TFmode) + { + emit_unop_insn (CODE_FOR_extendqftf2, to, from, UNKNOWN); + return; + } +#endif + +#ifdef HAVE_extendhfsf2 + if (HAVE_extendhfsf2 && from_mode == HFmode && to_mode == SFmode) + { + emit_unop_insn (CODE_FOR_extendhfsf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_extendhfdf2 + if (HAVE_extendhfdf2 && from_mode == HFmode && to_mode == DFmode) + { + emit_unop_insn (CODE_FOR_extendhfdf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_extendhfxf2 + if (HAVE_extendhfxf2 && from_mode == HFmode && to_mode == XFmode) + { + emit_unop_insn (CODE_FOR_extendhfxf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_extendhftf2 + if (HAVE_extendhftf2 && from_mode == HFmode && to_mode == TFmode) + { + emit_unop_insn (CODE_FOR_extendhftf2, to, from, UNKNOWN); + return; + } +#endif + +#ifdef HAVE_extendsfdf2 + if (HAVE_extendsfdf2 && from_mode == SFmode && to_mode == DFmode) + { + emit_unop_insn (CODE_FOR_extendsfdf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_extendsfxf2 + if (HAVE_extendsfxf2 && from_mode == SFmode && to_mode == XFmode) + { + emit_unop_insn (CODE_FOR_extendsfxf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_extendsftf2 + if (HAVE_extendsftf2 && from_mode == SFmode && to_mode == TFmode) + { + emit_unop_insn (CODE_FOR_extendsftf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_extenddfxf2 + if (HAVE_extenddfxf2 && from_mode == DFmode && to_mode == XFmode) + { + emit_unop_insn (CODE_FOR_extenddfxf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_extenddftf2 + if (HAVE_extenddftf2 && from_mode == DFmode && to_mode == TFmode) + { + emit_unop_insn (CODE_FOR_extenddftf2, to, from, UNKNOWN); + return; + } +#endif + +#ifdef HAVE_trunchfqf2 + if (HAVE_trunchfqf2 && from_mode == HFmode && to_mode == QFmode) + { + emit_unop_insn (CODE_FOR_trunchfqf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_truncsfqf2 + if (HAVE_truncsfqf2 && from_mode == SFmode && to_mode == QFmode) + { + emit_unop_insn (CODE_FOR_truncsfqf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_truncdfqf2 + if (HAVE_truncdfqf2 && from_mode == DFmode && to_mode == QFmode) + { + emit_unop_insn (CODE_FOR_truncdfqf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_truncxfqf2 + if (HAVE_truncxfqf2 && from_mode == XFmode && to_mode == QFmode) + { + emit_unop_insn (CODE_FOR_truncxfqf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_trunctfqf2 + if (HAVE_trunctfqf2 && from_mode == TFmode && to_mode == QFmode) + { + emit_unop_insn (CODE_FOR_trunctfqf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_truncsfhf2 + if (HAVE_truncsfhf2 && from_mode == SFmode && to_mode == HFmode) + { + emit_unop_insn (CODE_FOR_truncsfhf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_truncdfhf2 + if (HAVE_truncdfhf2 && from_mode == DFmode && to_mode == HFmode) + { + emit_unop_insn (CODE_FOR_truncdfhf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_truncxfhf2 + if (HAVE_truncxfhf2 && from_mode == XFmode && to_mode == HFmode) + { + emit_unop_insn (CODE_FOR_truncxfhf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_trunctfhf2 + if (HAVE_trunctfhf2 && from_mode == TFmode && to_mode == HFmode) + { + emit_unop_insn (CODE_FOR_trunctfhf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_truncdfsf2 + if (HAVE_truncdfsf2 && from_mode == DFmode && to_mode == SFmode) + { + emit_unop_insn (CODE_FOR_truncdfsf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_truncxfsf2 + if (HAVE_truncxfsf2 && from_mode == XFmode && to_mode == SFmode) + { + emit_unop_insn (CODE_FOR_truncxfsf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_trunctfsf2 + if (HAVE_trunctfsf2 && from_mode == TFmode && to_mode == SFmode) + { + emit_unop_insn (CODE_FOR_trunctfsf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_truncxfdf2 + if (HAVE_truncxfdf2 && from_mode == XFmode && to_mode == DFmode) + { + emit_unop_insn (CODE_FOR_truncxfdf2, to, from, UNKNOWN); + return; + } +#endif +#ifdef HAVE_trunctfdf2 + if (HAVE_trunctfdf2 && from_mode == TFmode && to_mode == DFmode) + { + emit_unop_insn (CODE_FOR_trunctfdf2, to, from, UNKNOWN); + return; + } +#endif + + libcall = (rtx) 0; + switch (from_mode) + { + case SFmode: + switch (to_mode) + { + case DFmode: + libcall = extendsfdf2_libfunc; + break; + + case XFmode: + libcall = extendsfxf2_libfunc; + break; + + case TFmode: + libcall = extendsftf2_libfunc; + break; + } + break; + + case DFmode: + switch (to_mode) + { + case SFmode: + libcall = truncdfsf2_libfunc; + break; + + case XFmode: + libcall = extenddfxf2_libfunc; + break; + + case TFmode: + libcall = extenddftf2_libfunc; + break; + } + break; + + case XFmode: + switch (to_mode) + { + case SFmode: + libcall = truncxfsf2_libfunc; + break; + + case DFmode: + libcall = truncxfdf2_libfunc; + break; + } + break; + + case TFmode: + switch (to_mode) + { + case SFmode: + libcall = trunctfsf2_libfunc; + break; + + case DFmode: + libcall = trunctfdf2_libfunc; + break; + } + break; + } + + if (libcall == (rtx) 0) + /* This conversion is not implemented yet. */ + abort (); + + emit_library_call (libcall, 1, to_mode, 1, from, from_mode); + emit_move_insn (to, hard_libcall_value (to_mode)); + return; + } + + /* Now both modes are integers. */ + + /* Handle expanding beyond a word. */ + if (GET_MODE_BITSIZE (from_mode) < GET_MODE_BITSIZE (to_mode) + && GET_MODE_BITSIZE (to_mode) > BITS_PER_WORD) + { + rtx insns; + rtx lowpart; + rtx fill_value; + rtx lowfrom; + int i; + enum machine_mode lowpart_mode; + int nwords = CEIL (GET_MODE_SIZE (to_mode), UNITS_PER_WORD); + + /* Try converting directly if the insn is supported. */ + if ((code = can_extend_p (to_mode, from_mode, unsignedp)) + != CODE_FOR_nothing) + { + /* If FROM is a SUBREG, put it into a register. Do this + so that we always generate the same set of insns for + better cse'ing; if an intermediate assignment occurred, + we won't be doing the operation directly on the SUBREG. */ + if (optimize > 0 && GET_CODE (from) == SUBREG) + from = force_reg (from_mode, from); + emit_unop_insn (code, to, from, equiv_code); + return; + } + /* Next, try converting via full word. */ + else if (GET_MODE_BITSIZE (from_mode) < BITS_PER_WORD + && ((code = can_extend_p (to_mode, word_mode, unsignedp)) + != CODE_FOR_nothing)) + { + convert_move (gen_lowpart (word_mode, to), from, unsignedp); + emit_unop_insn (code, to, + gen_lowpart (word_mode, to), equiv_code); + return; + } + + /* No special multiword conversion insn; do it by hand. */ + start_sequence (); + + /* Get a copy of FROM widened to a word, if necessary. */ + if (GET_MODE_BITSIZE (from_mode) < BITS_PER_WORD) + lowpart_mode = word_mode; + else + lowpart_mode = from_mode; + + lowfrom = convert_to_mode (lowpart_mode, from, unsignedp); + + lowpart = gen_lowpart (lowpart_mode, to); + emit_move_insn (lowpart, lowfrom); + + /* Compute the value to put in each remaining word. */ + if (unsignedp) + fill_value = const0_rtx; + else + { +#ifdef HAVE_slt + if (HAVE_slt + && insn_operand_mode[(int) CODE_FOR_slt][0] == word_mode + && STORE_FLAG_VALUE == -1) + { + emit_cmp_insn (lowfrom, const0_rtx, NE, NULL_RTX, + lowpart_mode, 0, 0); + fill_value = gen_reg_rtx (word_mode); + emit_insn (gen_slt (fill_value)); + } + else +#endif + { + fill_value + = expand_shift (RSHIFT_EXPR, lowpart_mode, lowfrom, + size_int (GET_MODE_BITSIZE (lowpart_mode) - 1), + NULL_RTX, 0); + fill_value = convert_to_mode (word_mode, fill_value, 1); + } + } + + /* Fill the remaining words. */ + for (i = GET_MODE_SIZE (lowpart_mode) / UNITS_PER_WORD; i < nwords; i++) + { + int index = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i); + rtx subword = operand_subword (to, index, 1, to_mode); + + if (subword == 0) + abort (); + + if (fill_value != subword) + emit_move_insn (subword, fill_value); + } + + insns = get_insns (); + end_sequence (); + + emit_no_conflict_block (insns, to, from, NULL_RTX, + gen_rtx (equiv_code, to_mode, copy_rtx (from))); + return; + } + + /* Truncating multi-word to a word or less. */ + if (GET_MODE_BITSIZE (from_mode) > BITS_PER_WORD + && GET_MODE_BITSIZE (to_mode) <= BITS_PER_WORD) + { + convert_move (to, gen_lowpart (word_mode, from), 0); + return; + } + + /* Handle pointer conversion */ /* SPEE 900220 */ + if (to_mode == PSImode) + { + if (from_mode != SImode) + from = convert_to_mode (SImode, from, unsignedp); + +#ifdef HAVE_truncsipsi + if (HAVE_truncsipsi) + { + emit_unop_insn (CODE_FOR_truncsipsi, to, from, UNKNOWN); + return; + } +#endif /* HAVE_truncsipsi */ + abort (); + } + + if (from_mode == PSImode) + { + if (to_mode != SImode) + { + from = convert_to_mode (SImode, from, unsignedp); + from_mode = SImode; + } + else + { +#ifdef HAVE_extendpsisi + if (HAVE_extendpsisi) + { + emit_unop_insn (CODE_FOR_extendpsisi, to, from, UNKNOWN); + return; + } +#endif /* HAVE_extendpsisi */ + abort (); + } + } + + /* Now follow all the conversions between integers + no more than a word long. */ + + /* For truncation, usually we can just refer to FROM in a narrower mode. */ + if (GET_MODE_BITSIZE (to_mode) < GET_MODE_BITSIZE (from_mode) + && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (to_mode), + GET_MODE_BITSIZE (from_mode))) + { + if (!((GET_CODE (from) == MEM + && ! MEM_VOLATILE_P (from) + && direct_load[(int) to_mode] + && ! mode_dependent_address_p (XEXP (from, 0))) + || GET_CODE (from) == REG + || GET_CODE (from) == SUBREG)) + from = force_reg (from_mode, from); + emit_move_insn (to, gen_lowpart (to_mode, from)); + return; + } + + /* Handle extension. */ + if (GET_MODE_BITSIZE (to_mode) > GET_MODE_BITSIZE (from_mode)) + { + /* Convert directly if that works. */ + if ((code = can_extend_p (to_mode, from_mode, unsignedp)) + != CODE_FOR_nothing) + { + /* If FROM is a SUBREG, put it into a register. Do this + so that we always generate the same set of insns for + better cse'ing; if an intermediate assignment occurred, + we won't be doing the operation directly on the SUBREG. */ + if (optimize > 0 && GET_CODE (from) == SUBREG) + from = force_reg (from_mode, from); + emit_unop_insn (code, to, from, equiv_code); + return; + } + else + { + enum machine_mode intermediate; + + /* Search for a mode to convert via. */ + for (intermediate = from_mode; intermediate != VOIDmode; + intermediate = GET_MODE_WIDER_MODE (intermediate)) + if ((can_extend_p (to_mode, intermediate, unsignedp) + != CODE_FOR_nothing) + && (can_extend_p (intermediate, from_mode, unsignedp) + != CODE_FOR_nothing)) + { + convert_move (to, convert_to_mode (intermediate, from, + unsignedp), unsignedp); + return; + } + + /* No suitable intermediate mode. */ + abort (); + } + } + + /* Support special truncate insns for certain modes. */ + + if (from_mode == DImode && to_mode == SImode) + { +#ifdef HAVE_truncdisi2 + if (HAVE_truncdisi2) + { + emit_unop_insn (CODE_FOR_truncdisi2, to, from, UNKNOWN); + return; + } +#endif + convert_move (to, force_reg (from_mode, from), unsignedp); + return; + } + + if (from_mode == DImode && to_mode == HImode) + { +#ifdef HAVE_truncdihi2 + if (HAVE_truncdihi2) + { + emit_unop_insn (CODE_FOR_truncdihi2, to, from, UNKNOWN); + return; + } +#endif + convert_move (to, force_reg (from_mode, from), unsignedp); + return; + } + + if (from_mode == DImode && to_mode == QImode) + { +#ifdef HAVE_truncdiqi2 + if (HAVE_truncdiqi2) + { + emit_unop_insn (CODE_FOR_truncdiqi2, to, from, UNKNOWN); + return; + } +#endif + convert_move (to, force_reg (from_mode, from), unsignedp); + return; + } + + if (from_mode == SImode && to_mode == HImode) + { +#ifdef HAVE_truncsihi2 + if (HAVE_truncsihi2) + { + emit_unop_insn (CODE_FOR_truncsihi2, to, from, UNKNOWN); + return; + } +#endif + convert_move (to, force_reg (from_mode, from), unsignedp); + return; + } + + if (from_mode == SImode && to_mode == QImode) + { +#ifdef HAVE_truncsiqi2 + if (HAVE_truncsiqi2) + { + emit_unop_insn (CODE_FOR_truncsiqi2, to, from, UNKNOWN); + return; + } +#endif + convert_move (to, force_reg (from_mode, from), unsignedp); + return; + } + + if (from_mode == HImode && to_mode == QImode) + { +#ifdef HAVE_trunchiqi2 + if (HAVE_trunchiqi2) + { + emit_unop_insn (CODE_FOR_trunchiqi2, to, from, UNKNOWN); + return; + } +#endif + convert_move (to, force_reg (from_mode, from), unsignedp); + return; + } + + /* Handle truncation of volatile memrefs, and so on; + the things that couldn't be truncated directly, + and for which there was no special instruction. */ + if (GET_MODE_BITSIZE (to_mode) < GET_MODE_BITSIZE (from_mode)) + { + rtx temp = force_reg (to_mode, gen_lowpart (to_mode, from)); + emit_move_insn (to, temp); + return; + } + + /* Mode combination is not recognized. */ + abort (); +} + +/* Return an rtx for a value that would result + from converting X to mode MODE. + Both X and MODE may be floating, or both integer. + UNSIGNEDP is nonzero if X is an unsigned value. + This can be done by referring to a part of X in place + or by copying to a new temporary with conversion. + + This function *must not* call protect_from_queue + except when putting X into an insn (in which case convert_move does it). */ + +rtx +convert_to_mode (mode, x, unsignedp) + enum machine_mode mode; + rtx x; + int unsignedp; +{ + register rtx temp; + + /* If FROM is a SUBREG that indicates that we have already done at least + the required extension, strip it. */ + + if (GET_CODE (x) == SUBREG && SUBREG_PROMOTED_VAR_P (x) + && GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) >= GET_MODE_SIZE (mode) + && SUBREG_PROMOTED_UNSIGNED_P (x) == unsignedp) + x = gen_lowpart (mode, x); + + if (mode == GET_MODE (x)) + return x; + + /* There is one case that we must handle specially: If we are converting + a CONST_INT into a mode whose size is twice HOST_BITS_PER_WIDE_INT and + we are to interpret the constant as unsigned, gen_lowpart will do + the wrong if the constant appears negative. What we want to do is + make the high-order word of the constant zero, not all ones. */ + + if (unsignedp && GET_MODE_CLASS (mode) == MODE_INT + && GET_MODE_BITSIZE (mode) == 2 * HOST_BITS_PER_WIDE_INT + && GET_CODE (x) == CONST_INT && INTVAL (x) < 0) + return immed_double_const (INTVAL (x), (HOST_WIDE_INT) 0, mode); + + /* We can do this with a gen_lowpart if both desired and current modes + are integer, and this is either a constant integer, a register, or a + non-volatile MEM. Except for the constant case, we must be narrowing + the operand. */ + + if (GET_CODE (x) == CONST_INT + || (GET_MODE_CLASS (mode) == MODE_INT + && GET_MODE_CLASS (GET_MODE (x)) == MODE_INT + && (GET_CODE (x) == CONST_DOUBLE + || (GET_MODE_SIZE (mode) <= GET_MODE_SIZE (GET_MODE (x)) + && ((GET_CODE (x) == MEM && ! MEM_VOLATILE_P (x)) + && direct_load[(int) mode] + || GET_CODE (x) == REG))))) + return gen_lowpart (mode, x); + + temp = gen_reg_rtx (mode); + convert_move (temp, x, unsignedp); + return temp; +} + +/* Generate several move instructions to copy LEN bytes + from block FROM to block TO. (These are MEM rtx's with BLKmode). + The caller must pass FROM and TO + through protect_from_queue before calling. + ALIGN (in bytes) is maximum alignment we can assume. */ + +static void +move_by_pieces (to, from, len, align) + rtx to, from; + int len, align; +{ + struct move_by_pieces data; + rtx to_addr = XEXP (to, 0), from_addr = XEXP (from, 0); + int max_size = MOVE_MAX + 1; + + data.offset = 0; + data.to_addr = to_addr; + data.from_addr = from_addr; + data.to = to; + data.from = from; + data.autinc_to + = (GET_CODE (to_addr) == PRE_INC || GET_CODE (to_addr) == PRE_DEC + || GET_CODE (to_addr) == POST_INC || GET_CODE (to_addr) == POST_DEC); + data.autinc_from + = (GET_CODE (from_addr) == PRE_INC || GET_CODE (from_addr) == PRE_DEC + || GET_CODE (from_addr) == POST_INC + || GET_CODE (from_addr) == POST_DEC); + + data.explicit_inc_from = 0; + data.explicit_inc_to = 0; + data.reverse + = (GET_CODE (to_addr) == PRE_DEC || GET_CODE (to_addr) == POST_DEC); + if (data.reverse) data.offset = len; + data.len = len; + + /* If copying requires more than two move insns, + copy addresses to registers (to make displacements shorter) + and use post-increment if available. */ + if (!(data.autinc_from && data.autinc_to) + && move_by_pieces_ninsns (len, align) > 2) + { +#ifdef HAVE_PRE_DECREMENT + if (data.reverse && ! data.autinc_from) + { + data.from_addr = copy_addr_to_reg (plus_constant (from_addr, len)); + data.autinc_from = 1; + data.explicit_inc_from = -1; + } +#endif +#ifdef HAVE_POST_INCREMENT + if (! data.autinc_from) + { + data.from_addr = copy_addr_to_reg (from_addr); + data.autinc_from = 1; + data.explicit_inc_from = 1; + } +#endif + if (!data.autinc_from && CONSTANT_P (from_addr)) + data.from_addr = copy_addr_to_reg (from_addr); +#ifdef HAVE_PRE_DECREMENT + if (data.reverse && ! data.autinc_to) + { + data.to_addr = copy_addr_to_reg (plus_constant (to_addr, len)); + data.autinc_to = 1; + data.explicit_inc_to = -1; + } +#endif +#ifdef HAVE_POST_INCREMENT + if (! data.reverse && ! data.autinc_to) + { + data.to_addr = copy_addr_to_reg (to_addr); + data.autinc_to = 1; + data.explicit_inc_to = 1; + } +#endif + if (!data.autinc_to && CONSTANT_P (to_addr)) + data.to_addr = copy_addr_to_reg (to_addr); + } + + if (! (STRICT_ALIGNMENT || SLOW_UNALIGNED_ACCESS) + || align > MOVE_MAX || align >= BIGGEST_ALIGNMENT / BITS_PER_UNIT) + align = MOVE_MAX; + + /* First move what we can in the largest integer mode, then go to + successively smaller modes. */ + + while (max_size > 1) + { + enum machine_mode mode = VOIDmode, tmode; + enum insn_code icode; + + for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT); + tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode)) + if (GET_MODE_SIZE (tmode) < max_size) + mode = tmode; + + if (mode == VOIDmode) + break; + + icode = mov_optab->handlers[(int) mode].insn_code; + if (icode != CODE_FOR_nothing + && align >= MIN (BIGGEST_ALIGNMENT / BITS_PER_UNIT, + GET_MODE_SIZE (mode))) + move_by_pieces_1 (GEN_FCN (icode), mode, &data); + + max_size = GET_MODE_SIZE (mode); + } + + /* The code above should have handled everything. */ + if (data.len != 0) + abort (); +} + +/* Return number of insns required to move L bytes by pieces. + ALIGN (in bytes) is maximum alignment we can assume. */ + +static int +move_by_pieces_ninsns (l, align) + unsigned int l; + int align; +{ + register int n_insns = 0; + int max_size = MOVE_MAX + 1; + + if (! (STRICT_ALIGNMENT || SLOW_UNALIGNED_ACCESS) + || align > MOVE_MAX || align >= BIGGEST_ALIGNMENT / BITS_PER_UNIT) + align = MOVE_MAX; + + while (max_size > 1) + { + enum machine_mode mode = VOIDmode, tmode; + enum insn_code icode; + + for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT); + tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode)) + if (GET_MODE_SIZE (tmode) < max_size) + mode = tmode; + + if (mode == VOIDmode) + break; + + icode = mov_optab->handlers[(int) mode].insn_code; + if (icode != CODE_FOR_nothing + && align >= MIN (BIGGEST_ALIGNMENT / BITS_PER_UNIT, + GET_MODE_SIZE (mode))) + n_insns += l / GET_MODE_SIZE (mode), l %= GET_MODE_SIZE (mode); + + max_size = GET_MODE_SIZE (mode); + } + + return n_insns; +} + +/* Subroutine of move_by_pieces. Move as many bytes as appropriate + with move instructions for mode MODE. GENFUN is the gen_... function + to make a move insn for that mode. DATA has all the other info. */ + +static void +move_by_pieces_1 (genfun, mode, data) + rtx (*genfun) (); + enum machine_mode mode; + struct move_by_pieces *data; +{ + register int size = GET_MODE_SIZE (mode); + register rtx to1, from1; + + while (data->len >= size) + { + if (data->reverse) data->offset -= size; + + to1 = (data->autinc_to + ? gen_rtx (MEM, mode, data->to_addr) + : change_address (data->to, mode, + plus_constant (data->to_addr, data->offset))); + from1 = + (data->autinc_from + ? gen_rtx (MEM, mode, data->from_addr) + : change_address (data->from, mode, + plus_constant (data->from_addr, data->offset))); + +#ifdef HAVE_PRE_DECREMENT + if (data->explicit_inc_to < 0) + emit_insn (gen_add2_insn (data->to_addr, GEN_INT (-size))); + if (data->explicit_inc_from < 0) + emit_insn (gen_add2_insn (data->from_addr, GEN_INT (-size))); +#endif + + emit_insn ((*genfun) (to1, from1)); +#ifdef HAVE_POST_INCREMENT + if (data->explicit_inc_to > 0) + emit_insn (gen_add2_insn (data->to_addr, GEN_INT (size))); + if (data->explicit_inc_from > 0) + emit_insn (gen_add2_insn (data->from_addr, GEN_INT (size))); +#endif + + if (! data->reverse) data->offset += size; + + data->len -= size; + } +} + +/* Emit code to move a block Y to a block X. + This may be done with string-move instructions, + with multiple scalar move instructions, or with a library call. + + Both X and Y must be MEM rtx's (perhaps inside VOLATILE) + with mode BLKmode. + SIZE is an rtx that says how long they are. + ALIGN is the maximum alignment we can assume they have, + measured in bytes. */ + +void +emit_block_move (x, y, size, align) + rtx x, y; + rtx size; + int align; +{ + if (GET_MODE (x) != BLKmode) + abort (); + + if (GET_MODE (y) != BLKmode) + abort (); + + x = protect_from_queue (x, 1); + y = protect_from_queue (y, 0); + size = protect_from_queue (size, 0); + + if (GET_CODE (x) != MEM) + abort (); + if (GET_CODE (y) != MEM) + abort (); + if (size == 0) + abort (); + + if (GET_CODE (size) == CONST_INT + && (move_by_pieces_ninsns (INTVAL (size), align) < MOVE_RATIO)) + move_by_pieces (x, y, INTVAL (size), align); + else + { + /* Try the most limited insn first, because there's no point + including more than one in the machine description unless + the more limited one has some advantage. */ + + rtx opalign = GEN_INT (align); + enum machine_mode mode; + + for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode; + mode = GET_MODE_WIDER_MODE (mode)) + { + enum insn_code code = movstr_optab[(int) mode]; + + if (code != CODE_FOR_nothing + /* We don't need MODE to be narrower than BITS_PER_HOST_WIDE_INT + here because if SIZE is less than the mode mask, as it is + returned by the macro, it will definitely be less than the + actual mode mask. */ + && (unsigned HOST_WIDE_INT) INTVAL (size) <= GET_MODE_MASK (mode) + && (insn_operand_predicate[(int) code][0] == 0 + || (*insn_operand_predicate[(int) code][0]) (x, BLKmode)) + && (insn_operand_predicate[(int) code][1] == 0 + || (*insn_operand_predicate[(int) code][1]) (y, BLKmode)) + && (insn_operand_predicate[(int) code][3] == 0 + || (*insn_operand_predicate[(int) code][3]) (opalign, + VOIDmode))) + { + rtx op2; + rtx last = get_last_insn (); + rtx pat; + + op2 = convert_to_mode (mode, size, 1); + if (insn_operand_predicate[(int) code][2] != 0 + && ! (*insn_operand_predicate[(int) code][2]) (op2, mode)) + op2 = copy_to_mode_reg (mode, op2); + + pat = GEN_FCN ((int) code) (x, y, op2, opalign); + if (pat) + { + emit_insn (pat); + return; + } + else + delete_insns_since (last); + } + } + +#ifdef TARGET_MEM_FUNCTIONS + emit_library_call (memcpy_libfunc, 0, + VOIDmode, 3, XEXP (x, 0), Pmode, + XEXP (y, 0), Pmode, + convert_to_mode (TYPE_MODE (sizetype), size, + TREE_UNSIGNED (sizetype)), + TYPE_MODE (sizetype)); +#else + emit_library_call (bcopy_libfunc, 0, + VOIDmode, 3, XEXP (y, 0), Pmode, + XEXP (x, 0), Pmode, + convert_to_mode (TYPE_MODE (sizetype), size, + TREE_UNSIGNED (sizetype)), + TYPE_MODE (sizetype)); +#endif + } +} + +/* Copy all or part of a value X into registers starting at REGNO. + The number of registers to be filled is NREGS. */ + +void +move_block_to_reg (regno, x, nregs, mode) + int regno; + rtx x; + int nregs; + enum machine_mode mode; +{ + int i; + rtx pat, last; + + if (CONSTANT_P (x) && ! LEGITIMATE_CONSTANT_P (x)) + x = validize_mem (force_const_mem (mode, x)); + + /* See if the machine can do this with a load multiple insn. */ +#ifdef HAVE_load_multiple + last = get_last_insn (); + pat = gen_load_multiple (gen_rtx (REG, word_mode, regno), x, + GEN_INT (nregs)); + if (pat) + { + emit_insn (pat); + return; + } + else + delete_insns_since (last); +#endif + + for (i = 0; i < nregs; i++) + emit_move_insn (gen_rtx (REG, word_mode, regno + i), + operand_subword_force (x, i, mode)); +} + +/* Copy all or part of a BLKmode value X out of registers starting at REGNO. + The number of registers to be filled is NREGS. */ + +void +move_block_from_reg (regno, x, nregs) + int regno; + rtx x; + int nregs; +{ + int i; + rtx pat, last; + + /* See if the machine can do this with a store multiple insn. */ +#ifdef HAVE_store_multiple + last = get_last_insn (); + pat = gen_store_multiple (x, gen_rtx (REG, word_mode, regno), + GEN_INT (nregs)); + if (pat) + { + emit_insn (pat); + return; + } + else + delete_insns_since (last); +#endif + + for (i = 0; i < nregs; i++) + { + rtx tem = operand_subword (x, i, 1, BLKmode); + + if (tem == 0) + abort (); + + emit_move_insn (tem, gen_rtx (REG, word_mode, regno + i)); + } +} + +/* Mark NREGS consecutive regs, starting at REGNO, as being live now. */ + +void +use_regs (regno, nregs) + int regno; + int nregs; +{ + int i; + + for (i = 0; i < nregs; i++) + emit_insn (gen_rtx (USE, VOIDmode, gen_rtx (REG, word_mode, regno + i))); +} + +/* Mark the instructions since PREV as a libcall block. + Add REG_LIBCALL to PREV and add a REG_RETVAL to the most recent insn. */ + +static void +group_insns (prev) + rtx prev; +{ + rtx insn_first; + rtx insn_last; + + /* Find the instructions to mark */ + if (prev) + insn_first = NEXT_INSN (prev); + else + insn_first = get_insns (); + + insn_last = get_last_insn (); + + REG_NOTES (insn_last) = gen_rtx (INSN_LIST, REG_RETVAL, insn_first, + REG_NOTES (insn_last)); + + REG_NOTES (insn_first) = gen_rtx (INSN_LIST, REG_LIBCALL, insn_last, + REG_NOTES (insn_first)); +} + +/* Write zeros through the storage of OBJECT. + If OBJECT has BLKmode, SIZE is its length in bytes. */ + +void +clear_storage (object, size) + rtx object; + int size; +{ + if (GET_MODE (object) == BLKmode) + { +#ifdef TARGET_MEM_FUNCTIONS + emit_library_call (memset_libfunc, 0, + VOIDmode, 3, + XEXP (object, 0), Pmode, const0_rtx, Pmode, + GEN_INT (size), Pmode); +#else + emit_library_call (bzero_libfunc, 0, + VOIDmode, 2, + XEXP (object, 0), Pmode, + GEN_INT (size), Pmode); +#endif + } + else + emit_move_insn (object, const0_rtx); +} + +/* Generate code to copy Y into X. + Both Y and X must have the same mode, except that + Y can be a constant with VOIDmode. + This mode cannot be BLKmode; use emit_block_move for that. + + Return the last instruction emitted. */ + +rtx +emit_move_insn (x, y) + rtx x, y; +{ + enum machine_mode mode = GET_MODE (x); + enum machine_mode submode; + enum mode_class class = GET_MODE_CLASS (mode); + int i; + + x = protect_from_queue (x, 1); + y = protect_from_queue (y, 0); + + if (mode == BLKmode || (GET_MODE (y) != mode && GET_MODE (y) != VOIDmode)) + abort (); + + if (CONSTANT_P (y) && ! LEGITIMATE_CONSTANT_P (y)) + y = force_const_mem (mode, y); + + /* If X or Y are memory references, verify that their addresses are valid + for the machine. */ + if (GET_CODE (x) == MEM + && ((! memory_address_p (GET_MODE (x), XEXP (x, 0)) + && ! push_operand (x, GET_MODE (x))) + || (flag_force_addr + && CONSTANT_ADDRESS_P (XEXP (x, 0))))) + x = change_address (x, VOIDmode, XEXP (x, 0)); + + if (GET_CODE (y) == MEM + && (! memory_address_p (GET_MODE (y), XEXP (y, 0)) + || (flag_force_addr + && CONSTANT_ADDRESS_P (XEXP (y, 0))))) + y = change_address (y, VOIDmode, XEXP (y, 0)); + + if (mode == BLKmode) + abort (); + + return emit_move_insn_1 (x, y); +} + +/* Low level part of emit_move_insn. + Called just like emit_move_insn, but assumes X and Y + are basically valid. */ + +rtx +emit_move_insn_1 (x, y) + rtx x, y; +{ + enum machine_mode mode = GET_MODE (x); + enum machine_mode submode; + enum mode_class class = GET_MODE_CLASS (mode); + int i; + + if (class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT) + submode = mode_for_size (GET_MODE_UNIT_SIZE (mode) * BITS_PER_UNIT, + (class == MODE_COMPLEX_INT + ? MODE_INT : MODE_FLOAT), + 0); + + if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) + return + emit_insn (GEN_FCN (mov_optab->handlers[(int) mode].insn_code) (x, y)); + + /* Expand complex moves by moving real part and imag part, if possible. */ + else if ((class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT) + && submode != BLKmode + && (mov_optab->handlers[(int) submode].insn_code + != CODE_FOR_nothing)) + { + /* Don't split destination if it is a stack push. */ + int stack = push_operand (x, GET_MODE (x)); + rtx prev = get_last_insn (); + + /* Tell flow that the whole of the destination is being set. */ + if (GET_CODE (x) == REG) + emit_insn (gen_rtx (CLOBBER, VOIDmode, x)); + + /* If this is a stack, push the highpart first, so it + will be in the argument order. + + In that case, change_address is used only to convert + the mode, not to change the address. */ + emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code) + ((stack ? change_address (x, submode, (rtx) 0) + : gen_highpart (submode, x)), + gen_highpart (submode, y))); + emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code) + ((stack ? change_address (x, submode, (rtx) 0) + : gen_lowpart (submode, x)), + gen_lowpart (submode, y))); + + group_insns (prev); + + return get_last_insn (); + } + + /* This will handle any multi-word mode that lacks a move_insn pattern. + However, you will get better code if you define such patterns, + even if they must turn into multiple assembler instructions. */ + else if (GET_MODE_SIZE (mode) > UNITS_PER_WORD) + { + rtx last_insn = 0; + rtx prev_insn = get_last_insn (); + + for (i = 0; + i < (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD; + i++) + { + rtx xpart = operand_subword (x, i, 1, mode); + rtx ypart = operand_subword (y, i, 1, mode); + + /* If we can't get a part of Y, put Y into memory if it is a + constant. Otherwise, force it into a register. If we still + can't get a part of Y, abort. */ + if (ypart == 0 && CONSTANT_P (y)) + { + y = force_const_mem (mode, y); + ypart = operand_subword (y, i, 1, mode); + } + else if (ypart == 0) + ypart = operand_subword_force (y, i, mode); + + if (xpart == 0 || ypart == 0) + abort (); + + last_insn = emit_move_insn (xpart, ypart); + } + /* Mark these insns as a libcall block. */ + group_insns (prev_insn); + + return last_insn; + } + else + abort (); +} + +/* Pushing data onto the stack. */ + +/* Push a block of length SIZE (perhaps variable) + and return an rtx to address the beginning of the block. + Note that it is not possible for the value returned to be a QUEUED. + The value may be virtual_outgoing_args_rtx. + + EXTRA is the number of bytes of padding to push in addition to SIZE. + BELOW nonzero means this padding comes at low addresses; + otherwise, the padding comes at high addresses. */ + +rtx +push_block (size, extra, below) + rtx size; + int extra, below; +{ + register rtx temp; + if (CONSTANT_P (size)) + anti_adjust_stack (plus_constant (size, extra)); + else if (GET_CODE (size) == REG && extra == 0) + anti_adjust_stack (size); + else + { + rtx temp = copy_to_mode_reg (Pmode, size); + if (extra != 0) + temp = expand_binop (Pmode, add_optab, temp, GEN_INT (extra), + temp, 0, OPTAB_LIB_WIDEN); + anti_adjust_stack (temp); + } + +#ifdef STACK_GROWS_DOWNWARD + temp = virtual_outgoing_args_rtx; + if (extra != 0 && below) + temp = plus_constant (temp, extra); +#else + if (GET_CODE (size) == CONST_INT) + temp = plus_constant (virtual_outgoing_args_rtx, + - INTVAL (size) - (below ? 0 : extra)); + else if (extra != 0 && !below) + temp = gen_rtx (PLUS, Pmode, virtual_outgoing_args_rtx, + negate_rtx (Pmode, plus_constant (size, extra))); + else + temp = gen_rtx (PLUS, Pmode, virtual_outgoing_args_rtx, + negate_rtx (Pmode, size)); +#endif + + return memory_address (GET_CLASS_NARROWEST_MODE (MODE_INT), temp); +} + +rtx +gen_push_operand () +{ + return gen_rtx (STACK_PUSH_CODE, Pmode, stack_pointer_rtx); +} + +/* Generate code to push X onto the stack, assuming it has mode MODE and + type TYPE. + MODE is redundant except when X is a CONST_INT (since they don't + carry mode info). + SIZE is an rtx for the size of data to be copied (in bytes), + needed only if X is BLKmode. + + ALIGN (in bytes) is maximum alignment we can assume. + + If PARTIAL and REG are both nonzero, then copy that many of the first + words of X into registers starting with REG, and push the rest of X. + The amount of space pushed is decreased by PARTIAL words, + rounded *down* to a multiple of PARM_BOUNDARY. + REG must be a hard register in this case. + If REG is zero but PARTIAL is not, take any all others actions for an + argument partially in registers, but do not actually load any + registers. + + EXTRA is the amount in bytes of extra space to leave next to this arg. + This is ignored if an argument block has already been allocated. + + On a machine that lacks real push insns, ARGS_ADDR is the address of + the bottom of the argument block for this call. We use indexing off there + to store the arg. On machines with push insns, ARGS_ADDR is 0 when a + argument block has not been preallocated. + + ARGS_SO_FAR is the size of args previously pushed for this call. */ + +void +emit_push_insn (x, mode, type, size, align, partial, reg, extra, + args_addr, args_so_far) + register rtx x; + enum machine_mode mode; + tree type; + rtx size; + int align; + int partial; + rtx reg; + int extra; + rtx args_addr; + rtx args_so_far; +{ + rtx xinner; + enum direction stack_direction +#ifdef STACK_GROWS_DOWNWARD + = downward; +#else + = upward; +#endif + + /* Decide where to pad the argument: `downward' for below, + `upward' for above, or `none' for don't pad it. + Default is below for small data on big-endian machines; else above. */ + enum direction where_pad = FUNCTION_ARG_PADDING (mode, type); + + /* Invert direction if stack is post-update. */ + if (STACK_PUSH_CODE == POST_INC || STACK_PUSH_CODE == POST_DEC) + if (where_pad != none) + where_pad = (where_pad == downward ? upward : downward); + + xinner = x = protect_from_queue (x, 0); + + if (mode == BLKmode) + { + /* Copy a block into the stack, entirely or partially. */ + + register rtx temp; + int used = partial * UNITS_PER_WORD; + int offset = used % (PARM_BOUNDARY / BITS_PER_UNIT); + int skip; + + if (size == 0) + abort (); + + used -= offset; + + /* USED is now the # of bytes we need not copy to the stack + because registers will take care of them. */ + + if (partial != 0) + xinner = change_address (xinner, BLKmode, + plus_constant (XEXP (xinner, 0), used)); + + /* If the partial register-part of the arg counts in its stack size, + skip the part of stack space corresponding to the registers. + Otherwise, start copying to the beginning of the stack space, + by setting SKIP to 0. */ +#ifndef REG_PARM_STACK_SPACE + skip = 0; +#else + skip = used; +#endif + +#ifdef PUSH_ROUNDING + /* Do it with several push insns if that doesn't take lots of insns + and if there is no difficulty with push insns that skip bytes + on the stack for alignment purposes. */ + if (args_addr == 0 + && GET_CODE (size) == CONST_INT + && skip == 0 + && (move_by_pieces_ninsns ((unsigned) INTVAL (size) - used, align) + < MOVE_RATIO) + /* Here we avoid the case of a structure whose weak alignment + forces many pushes of a small amount of data, + and such small pushes do rounding that causes trouble. */ + && ((! STRICT_ALIGNMENT && ! SLOW_UNALIGNED_ACCESS) + || align >= BIGGEST_ALIGNMENT / BITS_PER_UNIT + || PUSH_ROUNDING (align) == align) + && PUSH_ROUNDING (INTVAL (size)) == INTVAL (size)) + { + /* Push padding now if padding above and stack grows down, + or if padding below and stack grows up. + But if space already allocated, this has already been done. */ + if (extra && args_addr == 0 + && where_pad != none && where_pad != stack_direction) + anti_adjust_stack (GEN_INT (extra)); + + move_by_pieces (gen_rtx (MEM, BLKmode, gen_push_operand ()), xinner, + INTVAL (size) - used, align); + } + else +#endif /* PUSH_ROUNDING */ + { + /* Otherwise make space on the stack and copy the data + to the address of that space. */ + + /* Deduct words put into registers from the size we must copy. */ + if (partial != 0) + { + if (GET_CODE (size) == CONST_INT) + size = GEN_INT (INTVAL (size) - used); + else + size = expand_binop (GET_MODE (size), sub_optab, size, + GEN_INT (used), NULL_RTX, 0, + OPTAB_LIB_WIDEN); + } + + /* Get the address of the stack space. + In this case, we do not deal with EXTRA separately. + A single stack adjust will do. */ + if (! args_addr) + { + temp = push_block (size, extra, where_pad == downward); + extra = 0; + } + else if (GET_CODE (args_so_far) == CONST_INT) + temp = memory_address (BLKmode, + plus_constant (args_addr, + skip + INTVAL (args_so_far))); + else + temp = memory_address (BLKmode, + plus_constant (gen_rtx (PLUS, Pmode, + args_addr, args_so_far), + skip)); + + /* TEMP is the address of the block. Copy the data there. */ + if (GET_CODE (size) == CONST_INT + && (move_by_pieces_ninsns ((unsigned) INTVAL (size), align) + < MOVE_RATIO)) + { + move_by_pieces (gen_rtx (MEM, BLKmode, temp), xinner, + INTVAL (size), align); + goto ret; + } + /* Try the most limited insn first, because there's no point + including more than one in the machine description unless + the more limited one has some advantage. */ +#ifdef HAVE_movstrqi + if (HAVE_movstrqi + && GET_CODE (size) == CONST_INT + && ((unsigned) INTVAL (size) + < (1 << (GET_MODE_BITSIZE (QImode) - 1)))) + { + rtx pat = gen_movstrqi (gen_rtx (MEM, BLKmode, temp), + xinner, size, GEN_INT (align)); + if (pat != 0) + { + emit_insn (pat); + goto ret; + } + } +#endif +#ifdef HAVE_movstrhi + if (HAVE_movstrhi + && GET_CODE (size) == CONST_INT + && ((unsigned) INTVAL (size) + < (1 << (GET_MODE_BITSIZE (HImode) - 1)))) + { + rtx pat = gen_movstrhi (gen_rtx (MEM, BLKmode, temp), + xinner, size, GEN_INT (align)); + if (pat != 0) + { + emit_insn (pat); + goto ret; + } + } +#endif +#ifdef HAVE_movstrsi + if (HAVE_movstrsi) + { + rtx pat = gen_movstrsi (gen_rtx (MEM, BLKmode, temp), + xinner, size, GEN_INT (align)); + if (pat != 0) + { + emit_insn (pat); + goto ret; + } + } +#endif +#ifdef HAVE_movstrdi + if (HAVE_movstrdi) + { + rtx pat = gen_movstrdi (gen_rtx (MEM, BLKmode, temp), + xinner, size, GEN_INT (align)); + if (pat != 0) + { + emit_insn (pat); + goto ret; + } + } +#endif + +#ifndef ACCUMULATE_OUTGOING_ARGS + /* If the source is referenced relative to the stack pointer, + copy it to another register to stabilize it. We do not need + to do this if we know that we won't be changing sp. */ + + if (reg_mentioned_p (virtual_stack_dynamic_rtx, temp) + || reg_mentioned_p (virtual_outgoing_args_rtx, temp)) + temp = copy_to_reg (temp); +#endif + + /* Make inhibit_defer_pop nonzero around the library call + to force it to pop the bcopy-arguments right away. */ + NO_DEFER_POP; +#ifdef TARGET_MEM_FUNCTIONS + emit_library_call (memcpy_libfunc, 0, + VOIDmode, 3, temp, Pmode, XEXP (xinner, 0), Pmode, + convert_to_mode (TYPE_MODE (sizetype), + size, TREE_UNSIGNED (sizetype)), + TYPE_MODE (sizetype)); +#else + emit_library_call (bcopy_libfunc, 0, + VOIDmode, 3, XEXP (xinner, 0), Pmode, temp, Pmode, + convert_to_mode (TYPE_MODE (sizetype), + size, TREE_UNSIGNED (sizetype)), + TYPE_MODE (sizetype)); +#endif + OK_DEFER_POP; + } + } + else if (partial > 0) + { + /* Scalar partly in registers. */ + + int size = GET_MODE_SIZE (mode) / UNITS_PER_WORD; + int i; + int not_stack; + /* # words of start of argument + that we must make space for but need not store. */ + int offset = partial % (PARM_BOUNDARY / BITS_PER_WORD); + int args_offset = INTVAL (args_so_far); + int skip; + + /* Push padding now if padding above and stack grows down, + or if padding below and stack grows up. + But if space already allocated, this has already been done. */ + if (extra && args_addr == 0 + && where_pad != none && where_pad != stack_direction) + anti_adjust_stack (GEN_INT (extra)); + + /* If we make space by pushing it, we might as well push + the real data. Otherwise, we can leave OFFSET nonzero + and leave the space uninitialized. */ + if (args_addr == 0) + offset = 0; + + /* Now NOT_STACK gets the number of words that we don't need to + allocate on the stack. */ + not_stack = partial - offset; + + /* If the partial register-part of the arg counts in its stack size, + skip the part of stack space corresponding to the registers. + Otherwise, start copying to the beginning of the stack space, + by setting SKIP to 0. */ +#ifndef REG_PARM_STACK_SPACE + skip = 0; +#else + skip = not_stack; +#endif + + if (CONSTANT_P (x) && ! LEGITIMATE_CONSTANT_P (x)) + x = validize_mem (force_const_mem (mode, x)); + + /* If X is a hard register in a non-integer mode, copy it into a pseudo; + SUBREGs of such registers are not allowed. */ + if ((GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER + && GET_MODE_CLASS (GET_MODE (x)) != MODE_INT)) + x = copy_to_reg (x); + + /* Loop over all the words allocated on the stack for this arg. */ + /* We can do it by words, because any scalar bigger than a word + has a size a multiple of a word. */ +#ifndef PUSH_ARGS_REVERSED + for (i = not_stack; i < size; i++) +#else + for (i = size - 1; i >= not_stack; i--) +#endif + if (i >= not_stack + offset) + emit_push_insn (operand_subword_force (x, i, mode), + word_mode, NULL_TREE, NULL_RTX, align, 0, NULL_RTX, + 0, args_addr, + GEN_INT (args_offset + ((i - not_stack + skip) + * UNITS_PER_WORD))); + } + else + { + rtx addr; + + /* Push padding now if padding above and stack grows down, + or if padding below and stack grows up. + But if space already allocated, this has already been done. */ + if (extra && args_addr == 0 + && where_pad != none && where_pad != stack_direction) + anti_adjust_stack (GEN_INT (extra)); + +#ifdef PUSH_ROUNDING + if (args_addr == 0) + addr = gen_push_operand (); + else +#endif + if (GET_CODE (args_so_far) == CONST_INT) + addr + = memory_address (mode, + plus_constant (args_addr, INTVAL (args_so_far))); + else + addr = memory_address (mode, gen_rtx (PLUS, Pmode, args_addr, + args_so_far)); + + emit_move_insn (gen_rtx (MEM, mode, addr), x); + } + + ret: + /* If part should go in registers, copy that part + into the appropriate registers. Do this now, at the end, + since mem-to-mem copies above may do function calls. */ + if (partial > 0 && reg != 0) + move_block_to_reg (REGNO (reg), x, partial, mode); + + if (extra && args_addr == 0 && where_pad == stack_direction) + anti_adjust_stack (GEN_INT (extra)); +} + +/* Expand an assignment that stores the value of FROM into TO. + If WANT_VALUE is nonzero, return an rtx for the value of TO. + (This may contain a QUEUED rtx.) + Otherwise, the returned value is not meaningful. + + SUGGEST_REG is no longer actually used. + It used to mean, copy the value through a register + and return that register, if that is possible. + But now we do this if WANT_VALUE. + + If the value stored is a constant, we return the constant. */ + +rtx +expand_assignment (to, from, want_value, suggest_reg) + tree to, from; + int want_value; + int suggest_reg; +{ + register rtx to_rtx = 0; + rtx result; + + /* Don't crash if the lhs of the assignment was erroneous. */ + + if (TREE_CODE (to) == ERROR_MARK) + return expand_expr (from, NULL_RTX, VOIDmode, 0); + + /* Assignment of a structure component needs special treatment + if the structure component's rtx is not simply a MEM. + Assignment of an array element at a constant index + has the same problem. */ + + if (TREE_CODE (to) == COMPONENT_REF + || TREE_CODE (to) == BIT_FIELD_REF + || (TREE_CODE (to) == ARRAY_REF + && TREE_CODE (TREE_OPERAND (to, 1)) == INTEGER_CST + && TREE_CODE (TYPE_SIZE (TREE_TYPE (to))) == INTEGER_CST)) + { + enum machine_mode mode1; + int bitsize; + int bitpos; + tree offset; + int unsignedp; + int volatilep = 0; + tree tem = get_inner_reference (to, &bitsize, &bitpos, &offset, + &mode1, &unsignedp, &volatilep); + + /* If we are going to use store_bit_field and extract_bit_field, + make sure to_rtx will be safe for multiple use. */ + + if (mode1 == VOIDmode && want_value) + tem = stabilize_reference (tem); + + to_rtx = expand_expr (tem, NULL_RTX, VOIDmode, 0); + if (offset != 0) + { + rtx offset_rtx = expand_expr (offset, NULL_RTX, VOIDmode, 0); + + if (GET_CODE (to_rtx) != MEM) + abort (); + to_rtx = change_address (to_rtx, VOIDmode, + gen_rtx (PLUS, Pmode, XEXP (to_rtx, 0), + force_reg (Pmode, offset_rtx))); + } + if (volatilep) + { + if (GET_CODE (to_rtx) == MEM) + MEM_VOLATILE_P (to_rtx) = 1; +#if 0 /* This was turned off because, when a field is volatile + in an object which is not volatile, the object may be in a register, + and then we would abort over here. */ + else + abort (); +#endif + } + + result = store_field (to_rtx, bitsize, bitpos, mode1, from, + (want_value + /* Spurious cast makes HPUX compiler happy. */ + ? (enum machine_mode) TYPE_MODE (TREE_TYPE (to)) + : VOIDmode), + unsignedp, + /* Required alignment of containing datum. */ + TYPE_ALIGN (TREE_TYPE (tem)) / BITS_PER_UNIT, + int_size_in_bytes (TREE_TYPE (tem))); + preserve_temp_slots (result); + free_temp_slots (); + + /* If we aren't returning a result, just pass on what expand_expr + returned; it was probably const0_rtx. Otherwise, convert RESULT + to the proper mode. */ + return (want_value ? convert_to_mode (TYPE_MODE (TREE_TYPE (to)), result, + TREE_UNSIGNED (TREE_TYPE (to))) + : result); + } + + /* Ordinary treatment. Expand TO to get a REG or MEM rtx. + Don't re-expand if it was expanded already (in COMPONENT_REF case). */ + + if (to_rtx == 0) + to_rtx = expand_expr (to, NULL_RTX, VOIDmode, 0); + + /* Don't move directly into a return register. */ + if (TREE_CODE (to) == RESULT_DECL && GET_CODE (to_rtx) == REG) + { + rtx temp = expand_expr (from, 0, GET_MODE (to_rtx), 0); + emit_move_insn (to_rtx, temp); + preserve_temp_slots (to_rtx); + free_temp_slots (); + return to_rtx; + } + + /* In case we are returning the contents of an object which overlaps + the place the value is being stored, use a safe function when copying + a value through a pointer into a structure value return block. */ + if (TREE_CODE (to) == RESULT_DECL && TREE_CODE (from) == INDIRECT_REF + && current_function_returns_struct + && !current_function_returns_pcc_struct) + { + rtx from_rtx = expand_expr (from, NULL_RTX, VOIDmode, 0); + rtx size = expr_size (from); + +#ifdef TARGET_MEM_FUNCTIONS + emit_library_call (memcpy_libfunc, 0, + VOIDmode, 3, XEXP (to_rtx, 0), Pmode, + XEXP (from_rtx, 0), Pmode, + convert_to_mode (TYPE_MODE (sizetype), + size, TREE_UNSIGNED (sizetype)), + TYPE_MODE (sizetype)); +#else + emit_library_call (bcopy_libfunc, 0, + VOIDmode, 3, XEXP (from_rtx, 0), Pmode, + XEXP (to_rtx, 0), Pmode, + convert_to_mode (TYPE_MODE (sizetype), + size, TREE_UNSIGNED (sizetype)), + TYPE_MODE (sizetype)); +#endif + + preserve_temp_slots (to_rtx); + free_temp_slots (); + return to_rtx; + } + + /* Compute FROM and store the value in the rtx we got. */ + + result = store_expr (from, to_rtx, want_value); + preserve_temp_slots (result); + free_temp_slots (); + return result; +} + +/* Generate code for computing expression EXP, + and storing the value into TARGET. + Returns TARGET or an equivalent value. + TARGET may contain a QUEUED rtx. + + If SUGGEST_REG is nonzero, copy the value through a register + and return that register, if that is possible. + + If the value stored is a constant, we return the constant. */ + +rtx +store_expr (exp, target, suggest_reg) + register tree exp; + register rtx target; + int suggest_reg; +{ + register rtx temp; + int dont_return_target = 0; + + if (TREE_CODE (exp) == COMPOUND_EXPR) + { + /* Perform first part of compound expression, then assign from second + part. */ + expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, 0); + emit_queue (); + return store_expr (TREE_OPERAND (exp, 1), target, suggest_reg); + } + else if (TREE_CODE (exp) == COND_EXPR && GET_MODE (target) == BLKmode) + { + /* For conditional expression, get safe form of the target. Then + test the condition, doing the appropriate assignment on either + side. This avoids the creation of unnecessary temporaries. + For non-BLKmode, it is more efficient not to do this. */ + + rtx lab1 = gen_label_rtx (), lab2 = gen_label_rtx (); + + emit_queue (); + target = protect_from_queue (target, 1); + + NO_DEFER_POP; + jumpifnot (TREE_OPERAND (exp, 0), lab1); + store_expr (TREE_OPERAND (exp, 1), target, suggest_reg); + emit_queue (); + emit_jump_insn (gen_jump (lab2)); + emit_barrier (); + emit_label (lab1); + store_expr (TREE_OPERAND (exp, 2), target, suggest_reg); + emit_queue (); + emit_label (lab2); + OK_DEFER_POP; + return target; + } + else if (suggest_reg && GET_CODE (target) == MEM + && GET_MODE (target) != BLKmode) + /* If target is in memory and caller wants value in a register instead, + arrange that. Pass TARGET as target for expand_expr so that, + if EXP is another assignment, SUGGEST_REG will be nonzero for it. + We know expand_expr will not use the target in that case. */ + { + temp = expand_expr (exp, cse_not_expected ? NULL_RTX : target, + GET_MODE (target), 0); + if (GET_MODE (temp) != BLKmode && GET_MODE (temp) != VOIDmode) + temp = copy_to_reg (temp); + dont_return_target = 1; + } + else if (queued_subexp_p (target)) + /* If target contains a postincrement, it is not safe + to use as the returned value. It would access the wrong + place by the time the queued increment gets output. + So copy the value through a temporary and use that temp + as the result. */ + { + if (GET_MODE (target) != BLKmode && GET_MODE (target) != VOIDmode) + { + /* Expand EXP into a new pseudo. */ + temp = gen_reg_rtx (GET_MODE (target)); + temp = expand_expr (exp, temp, GET_MODE (target), 0); + } + else + temp = expand_expr (exp, NULL_RTX, GET_MODE (target), 0); + dont_return_target = 1; + } + else if (GET_CODE (target) == SUBREG && SUBREG_PROMOTED_VAR_P (target)) + /* If this is an scalar in a register that is stored in a wider mode + than the declared mode, compute the result into its declared mode + and then convert to the wider mode. Our value is the computed + expression. */ + { + temp = expand_expr (exp, NULL_RTX, VOIDmode, 0); + convert_move (SUBREG_REG (target), temp, + SUBREG_PROMOTED_UNSIGNED_P (target)); + return temp; + } + else + { + temp = expand_expr (exp, target, GET_MODE (target), 0); + /* DO return TARGET if it's a specified hardware register. + expand_return relies on this. */ + if (!(target && GET_CODE (target) == REG + && REGNO (target) < FIRST_PSEUDO_REGISTER) + && CONSTANT_P (temp)) + dont_return_target = 1; + } + + /* If value was not generated in the target, store it there. + Convert the value to TARGET's type first if nec. */ + + if (temp != target && TREE_CODE (exp) != ERROR_MARK) + { + target = protect_from_queue (target, 1); + if (GET_MODE (temp) != GET_MODE (target) + && GET_MODE (temp) != VOIDmode) + { + int unsignedp = TREE_UNSIGNED (TREE_TYPE (exp)); + if (dont_return_target) + { + /* In this case, we will return TEMP, + so make sure it has the proper mode. + But don't forget to store the value into TARGET. */ + temp = convert_to_mode (GET_MODE (target), temp, unsignedp); + emit_move_insn (target, temp); + } + else + convert_move (target, temp, unsignedp); + } + + else if (GET_MODE (temp) == BLKmode && TREE_CODE (exp) == STRING_CST) + { + /* Handle copying a string constant into an array. + The string constant may be shorter than the array. + So copy just the string's actual length, and clear the rest. */ + rtx size; + + /* Get the size of the data type of the string, + which is actually the size of the target. */ + size = expr_size (exp); + if (GET_CODE (size) == CONST_INT + && INTVAL (size) < TREE_STRING_LENGTH (exp)) + emit_block_move (target, temp, size, + TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); + else + { + /* Compute the size of the data to copy from the string. */ + tree copy_size + = size_binop (MIN_EXPR, + size_binop (CEIL_DIV_EXPR, + TYPE_SIZE (TREE_TYPE (exp)), + size_int (BITS_PER_UNIT)), + convert (sizetype, + build_int_2 (TREE_STRING_LENGTH (exp), 0))); + rtx copy_size_rtx = expand_expr (copy_size, NULL_RTX, + VOIDmode, 0); + rtx label = 0; + + /* Copy that much. */ + emit_block_move (target, temp, copy_size_rtx, + TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); + + /* Figure out how much is left in TARGET + that we have to clear. */ + if (GET_CODE (copy_size_rtx) == CONST_INT) + { + temp = plus_constant (XEXP (target, 0), + TREE_STRING_LENGTH (exp)); + size = plus_constant (size, + - TREE_STRING_LENGTH (exp)); + } + else + { + enum machine_mode size_mode = Pmode; + + temp = force_reg (Pmode, XEXP (target, 0)); + temp = expand_binop (size_mode, add_optab, temp, + copy_size_rtx, NULL_RTX, 0, + OPTAB_LIB_WIDEN); + + size = expand_binop (size_mode, sub_optab, size, + copy_size_rtx, NULL_RTX, 0, + OPTAB_LIB_WIDEN); + + emit_cmp_insn (size, const0_rtx, LT, NULL_RTX, + GET_MODE (size), 0, 0); + label = gen_label_rtx (); + emit_jump_insn (gen_blt (label)); + } + + if (size != const0_rtx) + { +#ifdef TARGET_MEM_FUNCTIONS + emit_library_call (memset_libfunc, 0, VOIDmode, 3, + temp, Pmode, const0_rtx, Pmode, size, Pmode); +#else + emit_library_call (bzero_libfunc, 0, VOIDmode, 2, + temp, Pmode, size, Pmode); +#endif + } + if (label) + emit_label (label); + } + } + else if (GET_MODE (temp) == BLKmode) + emit_block_move (target, temp, expr_size (exp), + TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); + else + emit_move_insn (target, temp); + } + if (dont_return_target) + return temp; + return target; +} + +/* Store the value of constructor EXP into the rtx TARGET. + TARGET is either a REG or a MEM. */ + +static void +store_constructor (exp, target) + tree exp; + rtx target; +{ + tree type = TREE_TYPE (exp); + + /* We know our target cannot conflict, since safe_from_p has been called. */ +#if 0 + /* Don't try copying piece by piece into a hard register + since that is vulnerable to being clobbered by EXP. + Instead, construct in a pseudo register and then copy it all. */ + if (GET_CODE (target) == REG && REGNO (target) < FIRST_PSEUDO_REGISTER) + { + rtx temp = gen_reg_rtx (GET_MODE (target)); + store_constructor (exp, temp); + emit_move_insn (target, temp); + return; + } +#endif + + if (TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE) + { + register tree elt; + + /* Inform later passes that the whole union value is dead. */ + if (TREE_CODE (type) == UNION_TYPE) + emit_insn (gen_rtx (CLOBBER, VOIDmode, target)); + + /* If we are building a static constructor into a register, + set the initial value as zero so we can fold the value into + a constant. */ + else if (GET_CODE (target) == REG && TREE_STATIC (exp)) + emit_move_insn (target, const0_rtx); + + /* If the constructor has fewer fields than the structure, + clear the whole structure first. */ + else if (list_length (CONSTRUCTOR_ELTS (exp)) + != list_length (TYPE_FIELDS (type))) + clear_storage (target, int_size_in_bytes (type)); + else + /* Inform later passes that the old value is dead. */ + emit_insn (gen_rtx (CLOBBER, VOIDmode, target)); + + /* Store each element of the constructor into + the corresponding field of TARGET. */ + + for (elt = CONSTRUCTOR_ELTS (exp); elt; elt = TREE_CHAIN (elt)) + { + register tree field = TREE_PURPOSE (elt); + register enum machine_mode mode; + int bitsize; + int bitpos; + int unsignedp; + + /* Just ignore missing fields. + We cleared the whole structure, above, + if any fields are missing. */ + if (field == 0) + continue; + + bitsize = TREE_INT_CST_LOW (DECL_SIZE (field)); + unsignedp = TREE_UNSIGNED (field); + mode = DECL_MODE (field); + if (DECL_BIT_FIELD (field)) + mode = VOIDmode; + + if (TREE_CODE (DECL_FIELD_BITPOS (field)) != INTEGER_CST) + /* ??? This case remains to be written. */ + abort (); + + bitpos = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)); + + store_field (target, bitsize, bitpos, mode, TREE_VALUE (elt), + /* The alignment of TARGET is + at least what its type requires. */ + VOIDmode, 0, + TYPE_ALIGN (type) / BITS_PER_UNIT, + int_size_in_bytes (type)); + } + } + else if (TREE_CODE (type) == ARRAY_TYPE) + { + register tree elt; + register int i; + tree domain = TYPE_DOMAIN (type); + HOST_WIDE_INT minelt = TREE_INT_CST_LOW (TYPE_MIN_VALUE (domain)); + HOST_WIDE_INT maxelt = TREE_INT_CST_LOW (TYPE_MAX_VALUE (domain)); + tree elttype = TREE_TYPE (type); + + /* If the constructor has fewer fields than the structure, + clear the whole structure first. Similarly if this this is + static constructor of a non-BLKmode object. */ + + if (list_length (CONSTRUCTOR_ELTS (exp)) < maxelt - minelt + 1 + || (GET_CODE (target) == REG && TREE_STATIC (exp))) + clear_storage (target, int_size_in_bytes (type)); + else + /* Inform later passes that the old value is dead. */ + emit_insn (gen_rtx (CLOBBER, VOIDmode, target)); + + /* Store each element of the constructor into + the corresponding element of TARGET, determined + by counting the elements. */ + for (elt = CONSTRUCTOR_ELTS (exp), i = 0; + elt; + elt = TREE_CHAIN (elt), i++) + { + register enum machine_mode mode; + int bitsize; + int bitpos; + int unsignedp; + + mode = TYPE_MODE (elttype); + bitsize = GET_MODE_BITSIZE (mode); + unsignedp = TREE_UNSIGNED (elttype); + + bitpos = (i * TREE_INT_CST_LOW (TYPE_SIZE (elttype))); + + store_field (target, bitsize, bitpos, mode, TREE_VALUE (elt), + /* The alignment of TARGET is + at least what its type requires. */ + VOIDmode, 0, + TYPE_ALIGN (type) / BITS_PER_UNIT, + int_size_in_bytes (type)); + } + } + + else + abort (); +} + +/* Store the value of EXP (an expression tree) + into a subfield of TARGET which has mode MODE and occupies + BITSIZE bits, starting BITPOS bits from the start of TARGET. + If MODE is VOIDmode, it means that we are storing into a bit-field. + + If VALUE_MODE is VOIDmode, return nothing in particular. + UNSIGNEDP is not used in this case. + + Otherwise, return an rtx for the value stored. This rtx + has mode VALUE_MODE if that is convenient to do. + In this case, UNSIGNEDP must be nonzero if the value is an unsigned type. + + ALIGN is the alignment that TARGET is known to have, measured in bytes. + TOTAL_SIZE is the size in bytes of the structure, or -1 if varying. */ + +static rtx +store_field (target, bitsize, bitpos, mode, exp, value_mode, + unsignedp, align, total_size) + rtx target; + int bitsize, bitpos; + enum machine_mode mode; + tree exp; + enum machine_mode value_mode; + int unsignedp; + int align; + int total_size; +{ + HOST_WIDE_INT width_mask = 0; + + if (bitsize < HOST_BITS_PER_WIDE_INT) + width_mask = ((HOST_WIDE_INT) 1 << bitsize) - 1; + + /* If we are storing into an unaligned field of an aligned union that is + in a register, we may have the mode of TARGET being an integer mode but + MODE == BLKmode. In that case, get an aligned object whose size and + alignment are the same as TARGET and store TARGET into it (we can avoid + the store if the field being stored is the entire width of TARGET). Then + call ourselves recursively to store the field into a BLKmode version of + that object. Finally, load from the object into TARGET. This is not + very efficient in general, but should only be slightly more expensive + than the otherwise-required unaligned accesses. Perhaps this can be + cleaned up later. */ + + if (mode == BLKmode + && (GET_CODE (target) == REG || GET_CODE (target) == SUBREG)) + { + rtx object = assign_stack_temp (GET_MODE (target), + GET_MODE_SIZE (GET_MODE (target)), 0); + rtx blk_object = copy_rtx (object); + + PUT_MODE (blk_object, BLKmode); + + if (bitsize != GET_MODE_BITSIZE (GET_MODE (target))) + emit_move_insn (object, target); + + store_field (blk_object, bitsize, bitpos, mode, exp, VOIDmode, 0, + align, total_size); + + emit_move_insn (target, object); + + return target; + } + + /* If the structure is in a register or if the component + is a bit field, we cannot use addressing to access it. + Use bit-field techniques or SUBREG to store in it. */ + + if (mode == VOIDmode + || (mode != BLKmode && ! direct_store[(int) mode]) + || GET_CODE (target) == REG + || GET_CODE (target) == SUBREG) + { + rtx temp = expand_expr (exp, NULL_RTX, VOIDmode, 0); + /* Store the value in the bitfield. */ + store_bit_field (target, bitsize, bitpos, mode, temp, align, total_size); + if (value_mode != VOIDmode) + { + /* The caller wants an rtx for the value. */ + /* If possible, avoid refetching from the bitfield itself. */ + if (width_mask != 0 + && ! (GET_CODE (target) == MEM && MEM_VOLATILE_P (target))) + { + tree count; + enum machine_mode tmode; + + if (unsignedp) + return expand_and (temp, GEN_INT (width_mask), NULL_RTX); + tmode = GET_MODE (temp); + if (tmode == VOIDmode) + tmode = value_mode; + count = build_int_2 (GET_MODE_BITSIZE (tmode) - bitsize, 0); + temp = expand_shift (LSHIFT_EXPR, tmode, temp, count, 0, 0); + return expand_shift (RSHIFT_EXPR, tmode, temp, count, 0, 0); + } + return extract_bit_field (target, bitsize, bitpos, unsignedp, + NULL_RTX, value_mode, 0, align, + total_size); + } + return const0_rtx; + } + else + { + rtx addr = XEXP (target, 0); + rtx to_rtx; + + /* If a value is wanted, it must be the lhs; + so make the address stable for multiple use. */ + + if (value_mode != VOIDmode && GET_CODE (addr) != REG + && ! CONSTANT_ADDRESS_P (addr) + /* A frame-pointer reference is already stable. */ + && ! (GET_CODE (addr) == PLUS + && GET_CODE (XEXP (addr, 1)) == CONST_INT + && (XEXP (addr, 0) == virtual_incoming_args_rtx + || XEXP (addr, 0) == virtual_stack_vars_rtx))) + addr = copy_to_reg (addr); + + /* Now build a reference to just the desired component. */ + + to_rtx = change_address (target, mode, + plus_constant (addr, (bitpos / BITS_PER_UNIT))); + MEM_IN_STRUCT_P (to_rtx) = 1; + + return store_expr (exp, to_rtx, value_mode != VOIDmode); + } +} + +/* Given an expression EXP that may be a COMPONENT_REF, a BIT_FIELD_REF, + or an ARRAY_REF, look for nested COMPONENT_REFs, BIT_FIELD_REFs, or + ARRAY_REFs and find the ultimate containing object, which we return. + + We set *PBITSIZE to the size in bits that we want, *PBITPOS to the + bit position, and *PUNSIGNEDP to the signedness of the field. + If the position of the field is variable, we store a tree + giving the variable offset (in units) in *POFFSET. + This offset is in addition to the bit position. + If the position is not variable, we store 0 in *POFFSET. + + If any of the extraction expressions is volatile, + we store 1 in *PVOLATILEP. Otherwise we don't change that. + + If the field is a bit-field, *PMODE is set to VOIDmode. Otherwise, it + is a mode that can be used to access the field. In that case, *PBITSIZE + is redundant. + + If the field describes a variable-sized object, *PMODE is set to + VOIDmode and *PBITSIZE is set to -1. An access cannot be made in + this case, but the address of the object can be found. */ + +tree +get_inner_reference (exp, pbitsize, pbitpos, poffset, pmode, + punsignedp, pvolatilep) + tree exp; + int *pbitsize; + int *pbitpos; + tree *poffset; + enum machine_mode *pmode; + int *punsignedp; + int *pvolatilep; +{ + tree size_tree = 0; + enum machine_mode mode = VOIDmode; + tree offset = integer_zero_node; + + if (TREE_CODE (exp) == COMPONENT_REF) + { + size_tree = DECL_SIZE (TREE_OPERAND (exp, 1)); + if (! DECL_BIT_FIELD (TREE_OPERAND (exp, 1))) + mode = DECL_MODE (TREE_OPERAND (exp, 1)); + *punsignedp = TREE_UNSIGNED (TREE_OPERAND (exp, 1)); + } + else if (TREE_CODE (exp) == BIT_FIELD_REF) + { + size_tree = TREE_OPERAND (exp, 1); + *punsignedp = TREE_UNSIGNED (exp); + } + else + { + mode = TYPE_MODE (TREE_TYPE (exp)); + *pbitsize = GET_MODE_BITSIZE (mode); + *punsignedp = TREE_UNSIGNED (TREE_TYPE (exp)); + } + + if (size_tree) + { + if (TREE_CODE (size_tree) != INTEGER_CST) + mode = BLKmode, *pbitsize = -1; + else + *pbitsize = TREE_INT_CST_LOW (size_tree); + } + + /* Compute cumulative bit-offset for nested component-refs and array-refs, + and find the ultimate containing object. */ + + *pbitpos = 0; + + while (1) + { + if (TREE_CODE (exp) == COMPONENT_REF || TREE_CODE (exp) == BIT_FIELD_REF) + { + tree pos = (TREE_CODE (exp) == COMPONENT_REF + ? DECL_FIELD_BITPOS (TREE_OPERAND (exp, 1)) + : TREE_OPERAND (exp, 2)); + + /* If this field hasn't been filled in yet, don't go + past it. This should only happen when folding expressions + made during type construction. */ + if (pos == 0) + break; + + if (TREE_CODE (pos) == PLUS_EXPR) + { + tree constant, var; + if (TREE_CODE (TREE_OPERAND (pos, 0)) == INTEGER_CST) + { + constant = TREE_OPERAND (pos, 0); + var = TREE_OPERAND (pos, 1); + } + else if (TREE_CODE (TREE_OPERAND (pos, 1)) == INTEGER_CST) + { + constant = TREE_OPERAND (pos, 1); + var = TREE_OPERAND (pos, 0); + } + else + abort (); + + *pbitpos += TREE_INT_CST_LOW (constant); + offset = size_binop (PLUS_EXPR, offset, + size_binop (FLOOR_DIV_EXPR, var, + size_int (BITS_PER_UNIT))); + } + else if (TREE_CODE (pos) == INTEGER_CST) + *pbitpos += TREE_INT_CST_LOW (pos); + else + { + /* Assume here that the offset is a multiple of a unit. + If not, there should be an explicitly added constant. */ + offset = size_binop (PLUS_EXPR, offset, + size_binop (FLOOR_DIV_EXPR, pos, + size_int (BITS_PER_UNIT))); + } + } + + else if (TREE_CODE (exp) == ARRAY_REF) + { + /* This code is based on the code in case ARRAY_REF in expand_expr + below. We assume here that the size of an array element is + always an integral multiple of BITS_PER_UNIT. */ + + tree index = TREE_OPERAND (exp, 1); + tree domain = TYPE_DOMAIN (TREE_TYPE (TREE_OPERAND (exp, 0))); + tree low_bound + = domain ? TYPE_MIN_VALUE (domain) : integer_zero_node; + tree index_type = TREE_TYPE (index); + + if (! integer_zerop (low_bound)) + index = fold (build (MINUS_EXPR, index_type, index, low_bound)); + + if (TYPE_PRECISION (index_type) != POINTER_SIZE) + { + index = convert (type_for_size (POINTER_SIZE, 0), index); + index_type = TREE_TYPE (index); + } + + index = fold (build (MULT_EXPR, index_type, index, + TYPE_SIZE (TREE_TYPE (exp)))); + + if (TREE_CODE (index) == INTEGER_CST + && TREE_INT_CST_HIGH (index) == 0) + *pbitpos += TREE_INT_CST_LOW (index); + else + offset = size_binop (PLUS_EXPR, offset, + size_binop (FLOOR_DIV_EXPR, index, + size_int (BITS_PER_UNIT))); + } + else if (TREE_CODE (exp) != NON_LVALUE_EXPR + && ! ((TREE_CODE (exp) == NOP_EXPR + || TREE_CODE (exp) == CONVERT_EXPR) + && (TYPE_MODE (TREE_TYPE (exp)) + == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))))) + break; + + /* If any reference in the chain is volatile, the effect is volatile. */ + if (TREE_THIS_VOLATILE (exp)) + *pvolatilep = 1; + exp = TREE_OPERAND (exp, 0); + } + + /* If this was a bit-field, see if there is a mode that allows direct + access in case EXP is in memory. */ + if (mode == VOIDmode && *pbitsize != 0 && *pbitpos % *pbitsize == 0) + { + mode = mode_for_size (*pbitsize, MODE_INT, 0); + if (mode == BLKmode) + mode = VOIDmode; + } + + if (integer_zerop (offset)) + offset = 0; + + *pmode = mode; + *poffset = offset; +#if 0 + /* We aren't finished fixing the callers to really handle nonzero offset. */ + if (offset != 0) + abort (); +#endif + + return exp; +} + +/* Given an rtx VALUE that may contain additions and multiplications, + return an equivalent value that just refers to a register or memory. + This is done by generating instructions to perform the arithmetic + and returning a pseudo-register containing the value. + + The returned value may be a REG, SUBREG, MEM or constant. */ + +rtx +force_operand (value, target) + rtx value, target; +{ + register optab binoptab = 0; + /* Use a temporary to force order of execution of calls to + `force_operand'. */ + rtx tmp; + register rtx op2; + /* Use subtarget as the target for operand 0 of a binary operation. */ + register rtx subtarget = (target != 0 && GET_CODE (target) == REG ? target : 0); + + if (GET_CODE (value) == PLUS) + binoptab = add_optab; + else if (GET_CODE (value) == MINUS) + binoptab = sub_optab; + else if (GET_CODE (value) == MULT) + { + op2 = XEXP (value, 1); + if (!CONSTANT_P (op2) + && !(GET_CODE (op2) == REG && op2 != subtarget)) + subtarget = 0; + tmp = force_operand (XEXP (value, 0), subtarget); + return expand_mult (GET_MODE (value), tmp, + force_operand (op2, NULL_RTX), + target, 0); + } + + if (binoptab) + { + op2 = XEXP (value, 1); + if (!CONSTANT_P (op2) + && !(GET_CODE (op2) == REG && op2 != subtarget)) + subtarget = 0; + if (binoptab == sub_optab && GET_CODE (op2) == CONST_INT) + { + binoptab = add_optab; + op2 = negate_rtx (GET_MODE (value), op2); + } + + /* Check for an addition with OP2 a constant integer and our first + operand a PLUS of a virtual register and something else. In that + case, we want to emit the sum of the virtual register and the + constant first and then add the other value. This allows virtual + register instantiation to simply modify the constant rather than + creating another one around this addition. */ + if (binoptab == add_optab && GET_CODE (op2) == CONST_INT + && GET_CODE (XEXP (value, 0)) == PLUS + && GET_CODE (XEXP (XEXP (value, 0), 0)) == REG + && REGNO (XEXP (XEXP (value, 0), 0)) >= FIRST_VIRTUAL_REGISTER + && REGNO (XEXP (XEXP (value, 0), 0)) <= LAST_VIRTUAL_REGISTER) + { + rtx temp = expand_binop (GET_MODE (value), binoptab, + XEXP (XEXP (value, 0), 0), op2, + subtarget, 0, OPTAB_LIB_WIDEN); + return expand_binop (GET_MODE (value), binoptab, temp, + force_operand (XEXP (XEXP (value, 0), 1), 0), + target, 0, OPTAB_LIB_WIDEN); + } + + tmp = force_operand (XEXP (value, 0), subtarget); + return expand_binop (GET_MODE (value), binoptab, tmp, + force_operand (op2, NULL_RTX), + target, 0, OPTAB_LIB_WIDEN); + /* We give UNSIGNEDP = 0 to expand_binop + because the only operations we are expanding here are signed ones. */ + } + return value; +} + +/* Subroutine of expand_expr: + save the non-copied parts (LIST) of an expr (LHS), and return a list + which can restore these values to their previous values, + should something modify their storage. */ + +static tree +save_noncopied_parts (lhs, list) + tree lhs; + tree list; +{ + tree tail; + tree parts = 0; + + for (tail = list; tail; tail = TREE_CHAIN (tail)) + if (TREE_CODE (TREE_VALUE (tail)) == TREE_LIST) + parts = chainon (parts, save_noncopied_parts (lhs, TREE_VALUE (tail))); + else + { + tree part = TREE_VALUE (tail); + tree part_type = TREE_TYPE (part); + tree to_be_saved = build (COMPONENT_REF, part_type, lhs, part); + rtx target = assign_stack_temp (TYPE_MODE (part_type), + int_size_in_bytes (part_type), 0); + if (! memory_address_p (TYPE_MODE (part_type), XEXP (target, 0))) + target = change_address (target, TYPE_MODE (part_type), NULL_RTX); + parts = tree_cons (to_be_saved, + build (RTL_EXPR, part_type, NULL_TREE, + (tree) target), + parts); + store_expr (TREE_PURPOSE (parts), RTL_EXPR_RTL (TREE_VALUE (parts)), 0); + } + return parts; +} + +/* Subroutine of expand_expr: + record the non-copied parts (LIST) of an expr (LHS), and return a list + which specifies the initial values of these parts. */ + +static tree +init_noncopied_parts (lhs, list) + tree lhs; + tree list; +{ + tree tail; + tree parts = 0; + + for (tail = list; tail; tail = TREE_CHAIN (tail)) + if (TREE_CODE (TREE_VALUE (tail)) == TREE_LIST) + parts = chainon (parts, init_noncopied_parts (lhs, TREE_VALUE (tail))); + else + { + tree part = TREE_VALUE (tail); + tree part_type = TREE_TYPE (part); + tree to_be_initialized = build (COMPONENT_REF, part_type, lhs, part); + parts = tree_cons (TREE_PURPOSE (tail), to_be_initialized, parts); + } + return parts; +} + +/* Subroutine of expand_expr: return nonzero iff there is no way that + EXP can reference X, which is being modified. */ + +static int +safe_from_p (x, exp) + rtx x; + tree exp; +{ + rtx exp_rtl = 0; + int i, nops; + + if (x == 0) + return 1; + + /* If this is a subreg of a hard register, declare it unsafe, otherwise, + find the underlying pseudo. */ + if (GET_CODE (x) == SUBREG) + { + x = SUBREG_REG (x); + if (GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER) + return 0; + } + + /* If X is a location in the outgoing argument area, it is always safe. */ + if (GET_CODE (x) == MEM + && (XEXP (x, 0) == virtual_outgoing_args_rtx + || (GET_CODE (XEXP (x, 0)) == PLUS + && XEXP (XEXP (x, 0), 0) == virtual_outgoing_args_rtx))) + return 1; + + switch (TREE_CODE_CLASS (TREE_CODE (exp))) + { + case 'd': + exp_rtl = DECL_RTL (exp); + break; + + case 'c': + return 1; + + case 'x': + if (TREE_CODE (exp) == TREE_LIST) + return ((TREE_VALUE (exp) == 0 + || safe_from_p (x, TREE_VALUE (exp))) + && (TREE_CHAIN (exp) == 0 + || safe_from_p (x, TREE_CHAIN (exp)))); + else + return 0; + + case '1': + return safe_from_p (x, TREE_OPERAND (exp, 0)); + + case '2': + case '<': + return (safe_from_p (x, TREE_OPERAND (exp, 0)) + && safe_from_p (x, TREE_OPERAND (exp, 1))); + + case 'e': + case 'r': + /* Now do code-specific tests. EXP_RTL is set to any rtx we find in + the expression. If it is set, we conflict iff we are that rtx or + both are in memory. Otherwise, we check all operands of the + expression recursively. */ + + switch (TREE_CODE (exp)) + { + case ADDR_EXPR: + return staticp (TREE_OPERAND (exp, 0)); + + case INDIRECT_REF: + if (GET_CODE (x) == MEM) + return 0; + break; + + case CALL_EXPR: + exp_rtl = CALL_EXPR_RTL (exp); + if (exp_rtl == 0) + { + /* Assume that the call will clobber all hard registers and + all of memory. */ + if ((GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER) + || GET_CODE (x) == MEM) + return 0; + } + + break; + + case RTL_EXPR: + exp_rtl = RTL_EXPR_RTL (exp); + if (exp_rtl == 0) + /* We don't know what this can modify. */ + return 0; + + break; + + case WITH_CLEANUP_EXPR: + exp_rtl = RTL_EXPR_RTL (exp); + break; + + case SAVE_EXPR: + exp_rtl = SAVE_EXPR_RTL (exp); + break; + + case BIND_EXPR: + /* The only operand we look at is operand 1. The rest aren't + part of the expression. */ + return safe_from_p (x, TREE_OPERAND (exp, 1)); + + case METHOD_CALL_EXPR: + /* This takes a rtx argument, but shouldn't appear here. */ + abort (); + } + + /* If we have an rtx, we do not need to scan our operands. */ + if (exp_rtl) + break; + + nops = tree_code_length[(int) TREE_CODE (exp)]; + for (i = 0; i < nops; i++) + if (TREE_OPERAND (exp, i) != 0 + && ! safe_from_p (x, TREE_OPERAND (exp, i))) + return 0; + } + + /* If we have an rtl, find any enclosed object. Then see if we conflict + with it. */ + if (exp_rtl) + { + if (GET_CODE (exp_rtl) == SUBREG) + { + exp_rtl = SUBREG_REG (exp_rtl); + if (GET_CODE (exp_rtl) == REG + && REGNO (exp_rtl) < FIRST_PSEUDO_REGISTER) + return 0; + } + + /* If the rtl is X, then it is not safe. Otherwise, it is unless both + are memory and EXP is not readonly. */ + return ! (rtx_equal_p (x, exp_rtl) + || (GET_CODE (x) == MEM && GET_CODE (exp_rtl) == MEM + && ! TREE_READONLY (exp))); + } + + /* If we reach here, it is safe. */ + return 1; +} + +/* Subroutine of expand_expr: return nonzero iff EXP is an + expression whose type is statically determinable. */ + +static int +fixed_type_p (exp) + tree exp; +{ + if (TREE_CODE (exp) == PARM_DECL + || TREE_CODE (exp) == VAR_DECL + || TREE_CODE (exp) == CALL_EXPR || TREE_CODE (exp) == TARGET_EXPR + || TREE_CODE (exp) == COMPONENT_REF + || TREE_CODE (exp) == ARRAY_REF) + return 1; + return 0; +} + +/* expand_expr: generate code for computing expression EXP. + An rtx for the computed value is returned. The value is never null. + In the case of a void EXP, const0_rtx is returned. + + The value may be stored in TARGET if TARGET is nonzero. + TARGET is just a suggestion; callers must assume that + the rtx returned may not be the same as TARGET. + + If TARGET is CONST0_RTX, it means that the value will be ignored. + + If TMODE is not VOIDmode, it suggests generating the + result in mode TMODE. But this is done only when convenient. + Otherwise, TMODE is ignored and the value generated in its natural mode. + TMODE is just a suggestion; callers must assume that + the rtx returned may not have mode TMODE. + + EXPAND_CONST_ADDRESS says that it is okay to return a MEM + with a constant address even if that address is not normally legitimate. + EXPAND_INITIALIZER and EXPAND_SUM also have this effect. + + If MODIFIER is EXPAND_SUM then when EXP is an addition + we can return an rtx of the form (MULT (REG ...) (CONST_INT ...)) + or a nest of (PLUS ...) and (MINUS ...) where the terms are + products as above, or REG or MEM, or constant. + Ordinarily in such cases we would output mul or add instructions + and then return a pseudo reg containing the sum. + + EXPAND_INITIALIZER is much like EXPAND_SUM except that + it also marks a label as absolutely required (it can't be dead). + It also makes a ZERO_EXTEND or SIGN_EXTEND instead of emitting extend insns. + This is used for outputting expressions used in initializers. */ + +rtx +expand_expr (exp, target, tmode, modifier) + register tree exp; + rtx target; + enum machine_mode tmode; + enum expand_modifier modifier; +{ + register rtx op0, op1, temp; + tree type = TREE_TYPE (exp); + int unsignedp = TREE_UNSIGNED (type); + register enum machine_mode mode = TYPE_MODE (type); + register enum tree_code code = TREE_CODE (exp); + optab this_optab; + /* Use subtarget as the target for operand 0 of a binary operation. */ + rtx subtarget = (target != 0 && GET_CODE (target) == REG ? target : 0); + rtx original_target = target; + int ignore = target == const0_rtx; + tree context; + + /* Don't use hard regs as subtargets, because the combiner + can only handle pseudo regs. */ + if (subtarget && REGNO (subtarget) < FIRST_PSEUDO_REGISTER) + subtarget = 0; + /* Avoid subtargets inside loops, + since they hide some invariant expressions. */ + if (preserve_subexpressions_p ()) + subtarget = 0; + + if (ignore) target = 0, original_target = 0; + + /* If will do cse, generate all results into pseudo registers + since 1) that allows cse to find more things + and 2) otherwise cse could produce an insn the machine + cannot support. */ + + if (! cse_not_expected && mode != BLKmode && target + && (GET_CODE (target) != REG || REGNO (target) < FIRST_PSEUDO_REGISTER)) + target = subtarget; + + /* Ensure we reference a volatile object even if value is ignored. */ + if (ignore && TREE_THIS_VOLATILE (exp) + && TREE_CODE (exp) != FUNCTION_DECL + && mode != VOIDmode && mode != BLKmode) + { + target = gen_reg_rtx (mode); + temp = expand_expr (exp, target, VOIDmode, modifier); + if (temp != target) + emit_move_insn (target, temp); + return target; + } + + switch (code) + { + case LABEL_DECL: + { + tree function = decl_function_context (exp); + /* Handle using a label in a containing function. */ + if (function != current_function_decl && function != 0) + { + struct function *p = find_function_data (function); + /* Allocate in the memory associated with the function + that the label is in. */ + push_obstacks (p->function_obstack, + p->function_maybepermanent_obstack); + + p->forced_labels = gen_rtx (EXPR_LIST, VOIDmode, + label_rtx (exp), p->forced_labels); + pop_obstacks (); + } + else if (modifier == EXPAND_INITIALIZER) + forced_labels = gen_rtx (EXPR_LIST, VOIDmode, + label_rtx (exp), forced_labels); + temp = gen_rtx (MEM, FUNCTION_MODE, + gen_rtx (LABEL_REF, Pmode, label_rtx (exp))); + if (function != current_function_decl && function != 0) + LABEL_REF_NONLOCAL_P (XEXP (temp, 0)) = 1; + return temp; + } + + case PARM_DECL: + if (DECL_RTL (exp) == 0) + { + error_with_decl (exp, "prior parameter's size depends on `%s'"); + return CONST0_RTX (mode); + } + + case FUNCTION_DECL: + case VAR_DECL: + case RESULT_DECL: + if (DECL_RTL (exp) == 0) + abort (); + /* Ensure variable marked as used + even if it doesn't go through a parser. */ + TREE_USED (exp) = 1; + /* Handle variables inherited from containing functions. */ + context = decl_function_context (exp); + + /* We treat inline_function_decl as an alias for the current function + because that is the inline function whose vars, types, etc. + are being merged into the current function. + See expand_inline_function. */ + if (context != 0 && context != current_function_decl + && context != inline_function_decl + /* If var is static, we don't need a static chain to access it. */ + && ! (GET_CODE (DECL_RTL (exp)) == MEM + && CONSTANT_P (XEXP (DECL_RTL (exp), 0)))) + { + rtx addr; + + /* Mark as non-local and addressable. */ + DECL_NONLOCAL (exp) = 1; + mark_addressable (exp); + if (GET_CODE (DECL_RTL (exp)) != MEM) + abort (); + addr = XEXP (DECL_RTL (exp), 0); + if (GET_CODE (addr) == MEM) + addr = gen_rtx (MEM, Pmode, fix_lexical_addr (XEXP (addr, 0), exp)); + else + addr = fix_lexical_addr (addr, exp); + return change_address (DECL_RTL (exp), mode, addr); + } + + /* This is the case of an array whose size is to be determined + from its initializer, while the initializer is still being parsed. + See expand_decl. */ + if (GET_CODE (DECL_RTL (exp)) == MEM + && GET_CODE (XEXP (DECL_RTL (exp), 0)) == REG) + return change_address (DECL_RTL (exp), GET_MODE (DECL_RTL (exp)), + XEXP (DECL_RTL (exp), 0)); + if (GET_CODE (DECL_RTL (exp)) == MEM + && modifier != EXPAND_CONST_ADDRESS + && modifier != EXPAND_SUM + && modifier != EXPAND_INITIALIZER) + { + /* DECL_RTL probably contains a constant address. + On RISC machines where a constant address isn't valid, + make some insns to get that address into a register. */ + if (!memory_address_p (DECL_MODE (exp), XEXP (DECL_RTL (exp), 0)) + || (flag_force_addr + && CONSTANT_ADDRESS_P (XEXP (DECL_RTL (exp), 0)))) + return change_address (DECL_RTL (exp), VOIDmode, + copy_rtx (XEXP (DECL_RTL (exp), 0))); + } + + /* If the mode of DECL_RTL does not match that of the decl, it + must be a promoted value. We return a SUBREG of the wanted mode, + but mark it so that we know that it was already extended. */ + + if (GET_CODE (DECL_RTL (exp)) == REG + && GET_MODE (DECL_RTL (exp)) != mode) + { + enum machine_mode decl_mode = DECL_MODE (exp); + + /* Get the signedness used for this variable. Ensure we get the + same mode we got when the variable was declared. */ + + PROMOTE_MODE (decl_mode, unsignedp, type); + + if (decl_mode != GET_MODE (DECL_RTL (exp))) + abort (); + + temp = gen_rtx (SUBREG, mode, DECL_RTL (exp), 0); + SUBREG_PROMOTED_VAR_P (temp) = 1; + SUBREG_PROMOTED_UNSIGNED_P (temp) = unsignedp; + return temp; + } + + return DECL_RTL (exp); + + case INTEGER_CST: + return immed_double_const (TREE_INT_CST_LOW (exp), + TREE_INT_CST_HIGH (exp), + mode); + + case CONST_DECL: + return expand_expr (DECL_INITIAL (exp), target, VOIDmode, 0); + + case REAL_CST: + /* If optimized, generate immediate CONST_DOUBLE + which will be turned into memory by reload if necessary. + + We used to force a register so that loop.c could see it. But + this does not allow gen_* patterns to perform optimizations with + the constants. It also produces two insns in cases like "x = 1.0;". + On most machines, floating-point constants are not permitted in + many insns, so we'd end up copying it to a register in any case. + + Now, we do the copying in expand_binop, if appropriate. */ + return immed_real_const (exp); + + case COMPLEX_CST: + case STRING_CST: + if (! TREE_CST_RTL (exp)) + output_constant_def (exp); + + /* TREE_CST_RTL probably contains a constant address. + On RISC machines where a constant address isn't valid, + make some insns to get that address into a register. */ + if (GET_CODE (TREE_CST_RTL (exp)) == MEM + && modifier != EXPAND_CONST_ADDRESS + && modifier != EXPAND_INITIALIZER + && modifier != EXPAND_SUM + && !memory_address_p (mode, XEXP (TREE_CST_RTL (exp), 0))) + return change_address (TREE_CST_RTL (exp), VOIDmode, + copy_rtx (XEXP (TREE_CST_RTL (exp), 0))); + return TREE_CST_RTL (exp); + + case SAVE_EXPR: + context = decl_function_context (exp); + /* We treat inline_function_decl as an alias for the current function + because that is the inline function whose vars, types, etc. + are being merged into the current function. + See expand_inline_function. */ + if (context == current_function_decl || context == inline_function_decl) + context = 0; + + /* If this is non-local, handle it. */ + if (context) + { + temp = SAVE_EXPR_RTL (exp); + if (temp && GET_CODE (temp) == REG) + { + put_var_into_stack (exp); + temp = SAVE_EXPR_RTL (exp); + } + if (temp == 0 || GET_CODE (temp) != MEM) + abort (); + return change_address (temp, mode, + fix_lexical_addr (XEXP (temp, 0), exp)); + } + if (SAVE_EXPR_RTL (exp) == 0) + { + if (mode == BLKmode) + temp + = assign_stack_temp (mode, + int_size_in_bytes (TREE_TYPE (exp)), 0); + else + { + enum machine_mode var_mode = mode; + + if (TREE_CODE (type) == INTEGER_TYPE + || TREE_CODE (type) == ENUMERAL_TYPE + || TREE_CODE (type) == BOOLEAN_TYPE + || TREE_CODE (type) == CHAR_TYPE + || TREE_CODE (type) == REAL_TYPE + || TREE_CODE (type) == POINTER_TYPE + || TREE_CODE (type) == OFFSET_TYPE) + { + PROMOTE_MODE (var_mode, unsignedp, type); + } + + temp = gen_reg_rtx (var_mode); + } + + SAVE_EXPR_RTL (exp) = temp; + if (!optimize && GET_CODE (temp) == REG) + save_expr_regs = gen_rtx (EXPR_LIST, VOIDmode, temp, + save_expr_regs); + + /* If the mode of TEMP does not match that of the expression, it + must be a promoted value. We pass store_expr a SUBREG of the + wanted mode but mark it so that we know that it was already + extended. Note that `unsignedp' was modified above in + this case. */ + + if (GET_CODE (temp) == REG && GET_MODE (temp) != mode) + { + temp = gen_rtx (SUBREG, mode, SAVE_EXPR_RTL (exp), 0); + SUBREG_PROMOTED_VAR_P (temp) = 1; + SUBREG_PROMOTED_UNSIGNED_P (temp) = unsignedp; + } + + store_expr (TREE_OPERAND (exp, 0), temp, 0); + } + + /* If the mode of SAVE_EXPR_RTL does not match that of the expression, it + must be a promoted value. We return a SUBREG of the wanted mode, + but mark it so that we know that it was already extended. Note + that `unsignedp' was modified above in this case. */ + + if (GET_CODE (SAVE_EXPR_RTL (exp)) == REG + && GET_MODE (SAVE_EXPR_RTL (exp)) != mode) + { + temp = gen_rtx (SUBREG, mode, SAVE_EXPR_RTL (exp), 0); + SUBREG_PROMOTED_VAR_P (temp) = 1; + SUBREG_PROMOTED_UNSIGNED_P (temp) = unsignedp; + return temp; + } + + return SAVE_EXPR_RTL (exp); + + case EXIT_EXPR: + /* Exit the current loop if the body-expression is true. */ + { + rtx label = gen_label_rtx (); + do_jump (TREE_OPERAND (exp, 0), label, NULL_RTX); + expand_exit_loop (NULL_PTR); + emit_label (label); + } + return const0_rtx; + + case LOOP_EXPR: + expand_start_loop (1); + expand_expr_stmt (TREE_OPERAND (exp, 0)); + expand_end_loop (); + + return const0_rtx; + + case BIND_EXPR: + { + tree vars = TREE_OPERAND (exp, 0); + int vars_need_expansion = 0; + + /* Need to open a binding contour here because + if there are any cleanups they most be contained here. */ + expand_start_bindings (0); + + /* Mark the corresponding BLOCK for output in its proper place. */ + if (TREE_OPERAND (exp, 2) != 0 + && ! TREE_USED (TREE_OPERAND (exp, 2))) + insert_block (TREE_OPERAND (exp, 2)); + + /* If VARS have not yet been expanded, expand them now. */ + while (vars) + { + if (DECL_RTL (vars) == 0) + { + vars_need_expansion = 1; + expand_decl (vars); + } + expand_decl_init (vars); + vars = TREE_CHAIN (vars); + } + + temp = expand_expr (TREE_OPERAND (exp, 1), target, tmode, modifier); + + expand_end_bindings (TREE_OPERAND (exp, 0), 0, 0); + + return temp; + } + + case RTL_EXPR: + if (RTL_EXPR_SEQUENCE (exp) == const0_rtx) + abort (); + emit_insns (RTL_EXPR_SEQUENCE (exp)); + RTL_EXPR_SEQUENCE (exp) = const0_rtx; + return RTL_EXPR_RTL (exp); + + case CONSTRUCTOR: + /* All elts simple constants => refer to a constant in memory. But + if this is a non-BLKmode mode, let it store a field at a time + since that should make a CONST_INT or CONST_DOUBLE when we + fold. */ + if (TREE_STATIC (exp) && (mode == BLKmode || TREE_ADDRESSABLE (exp))) + { + rtx constructor = output_constant_def (exp); + if (modifier != EXPAND_CONST_ADDRESS + && modifier != EXPAND_INITIALIZER + && modifier != EXPAND_SUM + && !memory_address_p (GET_MODE (constructor), + XEXP (constructor, 0))) + constructor = change_address (constructor, VOIDmode, + XEXP (constructor, 0)); + return constructor; + } + + if (ignore) + { + tree elt; + for (elt = CONSTRUCTOR_ELTS (exp); elt; elt = TREE_CHAIN (elt)) + expand_expr (TREE_VALUE (elt), const0_rtx, VOIDmode, 0); + return const0_rtx; + } + else + { + if (target == 0 || ! safe_from_p (target, exp)) + { + if (mode != BLKmode && ! TREE_ADDRESSABLE (exp)) + target = gen_reg_rtx (mode); + else + { + enum tree_code c = TREE_CODE (type); + target + = assign_stack_temp (mode, int_size_in_bytes (type), 0); + if (c == RECORD_TYPE || c == UNION_TYPE + || c == QUAL_UNION_TYPE || c == ARRAY_TYPE) + MEM_IN_STRUCT_P (target) = 1; + } + } + store_constructor (exp, target); + return target; + } + + case INDIRECT_REF: + { + tree exp1 = TREE_OPERAND (exp, 0); + tree exp2; + + /* A SAVE_EXPR as the address in an INDIRECT_EXPR is generated + for *PTR += ANYTHING where PTR is put inside the SAVE_EXPR. + This code has the same general effect as simply doing + expand_expr on the save expr, except that the expression PTR + is computed for use as a memory address. This means different + code, suitable for indexing, may be generated. */ + if (TREE_CODE (exp1) == SAVE_EXPR + && SAVE_EXPR_RTL (exp1) == 0 + && TREE_CODE (exp2 = TREE_OPERAND (exp1, 0)) != ERROR_MARK + && TYPE_MODE (TREE_TYPE (exp1)) == Pmode + && TYPE_MODE (TREE_TYPE (exp2)) == Pmode) + { + temp = expand_expr (TREE_OPERAND (exp1, 0), NULL_RTX, + VOIDmode, EXPAND_SUM); + op0 = memory_address (mode, temp); + op0 = copy_all_regs (op0); + SAVE_EXPR_RTL (exp1) = op0; + } + else + { + op0 = expand_expr (exp1, NULL_RTX, VOIDmode, EXPAND_SUM); + op0 = memory_address (mode, op0); + } + + temp = gen_rtx (MEM, mode, op0); + /* If address was computed by addition, + mark this as an element of an aggregate. */ + if (TREE_CODE (TREE_OPERAND (exp, 0)) == PLUS_EXPR + || (TREE_CODE (TREE_OPERAND (exp, 0)) == SAVE_EXPR + && TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) == PLUS_EXPR) + || TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE + || TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE + || TREE_CODE (TREE_TYPE (exp)) == QUAL_UNION_TYPE + || (TREE_CODE (exp1) == ADDR_EXPR + && (exp2 = TREE_OPERAND (exp1, 0)) + && (TREE_CODE (TREE_TYPE (exp2)) == ARRAY_TYPE + || TREE_CODE (TREE_TYPE (exp2)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (exp2)) == UNION_TYPE + || TREE_CODE (TREE_TYPE (exp2)) == QUAL_UNION_TYPE))) + MEM_IN_STRUCT_P (temp) = 1; + MEM_VOLATILE_P (temp) = TREE_THIS_VOLATILE (exp); +#if 0 /* It is incorrect to set RTX_UNCHANGING_P here, because the fact that + a location is accessed through a pointer to const does not mean + that the value there can never change. */ + RTX_UNCHANGING_P (temp) = TREE_READONLY (exp); +#endif + return temp; + } + + case ARRAY_REF: + if (TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 0))) != ARRAY_TYPE) + abort (); + + { + tree array = TREE_OPERAND (exp, 0); + tree domain = TYPE_DOMAIN (TREE_TYPE (array)); + tree low_bound = domain ? TYPE_MIN_VALUE (domain) : integer_zero_node; + tree index = TREE_OPERAND (exp, 1); + tree index_type = TREE_TYPE (index); + int i; + + /* Optimize the special-case of a zero lower bound. */ + if (! integer_zerop (low_bound)) + index = fold (build (MINUS_EXPR, index_type, index, low_bound)); + + if (TREE_CODE (index) != INTEGER_CST + || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) + { + /* Nonconstant array index or nonconstant element size. + Generate the tree for *(&array+index) and expand that, + except do it in a language-independent way + and don't complain about non-lvalue arrays. + `mark_addressable' should already have been called + for any array for which this case will be reached. */ + + /* Don't forget the const or volatile flag from the array + element. */ + tree variant_type = build_type_variant (type, + TREE_READONLY (exp), + TREE_THIS_VOLATILE (exp)); + tree array_adr = build1 (ADDR_EXPR, + build_pointer_type (variant_type), array); + tree elt; + + /* Convert the integer argument to a type the same size as a + pointer so the multiply won't overflow spuriously. */ + if (TYPE_PRECISION (index_type) != POINTER_SIZE) + index = convert (type_for_size (POINTER_SIZE, 0), index); + + /* Don't think the address has side effects + just because the array does. + (In some cases the address might have side effects, + and we fail to record that fact here. However, it should not + matter, since expand_expr should not care.) */ + TREE_SIDE_EFFECTS (array_adr) = 0; + + elt = build1 (INDIRECT_REF, type, + fold (build (PLUS_EXPR, + TYPE_POINTER_TO (variant_type), + array_adr, + fold (build (MULT_EXPR, + TYPE_POINTER_TO (variant_type), + index, + size_in_bytes (type)))))); + + /* Volatility, etc., of new expression is same as old + expression. */ + TREE_SIDE_EFFECTS (elt) = TREE_SIDE_EFFECTS (exp); + TREE_THIS_VOLATILE (elt) = TREE_THIS_VOLATILE (exp); + TREE_READONLY (elt) = TREE_READONLY (exp); + + return expand_expr (elt, target, tmode, modifier); + } + + /* Fold an expression like: "foo"[2]. + This is not done in fold so it won't happen inside &. */ + + if (TREE_CODE (array) == STRING_CST + && TREE_CODE (index) == INTEGER_CST + && !TREE_INT_CST_HIGH (index) + && (i = TREE_INT_CST_LOW (index)) < TREE_STRING_LENGTH (array)) + { + if (TREE_TYPE (TREE_TYPE (array)) == integer_type_node) + { + exp = build_int_2 (((int *)TREE_STRING_POINTER (array))[i], 0); + TREE_TYPE (exp) = integer_type_node; + return expand_expr (exp, target, tmode, modifier); + } + if (TREE_TYPE (TREE_TYPE (array)) == char_type_node) + { + exp = build_int_2 (TREE_STRING_POINTER (array)[i], 0); + TREE_TYPE (exp) = integer_type_node; + return expand_expr (convert (TREE_TYPE (TREE_TYPE (array)), + exp), + target, tmode, modifier); + } + } + + /* If this is a constant index into a constant array, + just get the value from the array. Handle both the cases when + we have an explicit constructor and when our operand is a variable + that was declared const. */ + + if (TREE_CODE (array) == CONSTRUCTOR && ! TREE_SIDE_EFFECTS (array)) + { + if (TREE_CODE (index) == INTEGER_CST + && TREE_INT_CST_HIGH (index) == 0) + { + tree elem = CONSTRUCTOR_ELTS (TREE_OPERAND (exp, 0)); + + i = TREE_INT_CST_LOW (index); + while (elem && i--) + elem = TREE_CHAIN (elem); + if (elem) + return expand_expr (fold (TREE_VALUE (elem)), target, + tmode, modifier); + } + } + + else if (optimize >= 1 + && TREE_READONLY (array) && ! TREE_SIDE_EFFECTS (array) + && TREE_CODE (array) == VAR_DECL && DECL_INITIAL (array) + && TREE_CODE (DECL_INITIAL (array)) != ERROR_MARK) + { + if (TREE_CODE (index) == INTEGER_CST + && TREE_INT_CST_HIGH (index) == 0) + { + tree init = DECL_INITIAL (array); + + i = TREE_INT_CST_LOW (index); + if (TREE_CODE (init) == CONSTRUCTOR) + { + tree elem = CONSTRUCTOR_ELTS (init); + + while (elem && i--) + elem = TREE_CHAIN (elem); + if (elem) + return expand_expr (fold (TREE_VALUE (elem)), target, + tmode, modifier); + } + else if (TREE_CODE (init) == STRING_CST + && i < TREE_STRING_LENGTH (init)) + { + temp = GEN_INT (TREE_STRING_POINTER (init)[i]); + return convert_to_mode (mode, temp, 0); + } + } + } + } + + /* Treat array-ref with constant index as a component-ref. */ + + case COMPONENT_REF: + case BIT_FIELD_REF: + /* If the operand is a CONSTRUCTOR, we can just extract the + appropriate field if it is present. */ + if (code != ARRAY_REF + && TREE_CODE (TREE_OPERAND (exp, 0)) == CONSTRUCTOR) + { + tree elt; + + for (elt = CONSTRUCTOR_ELTS (TREE_OPERAND (exp, 0)); elt; + elt = TREE_CHAIN (elt)) + if (TREE_PURPOSE (elt) == TREE_OPERAND (exp, 1)) + return expand_expr (TREE_VALUE (elt), target, tmode, modifier); + } + + { + enum machine_mode mode1; + int bitsize; + int bitpos; + tree offset; + int volatilep = 0; + tree tem = get_inner_reference (exp, &bitsize, &bitpos, &offset, + &mode1, &unsignedp, &volatilep); + + /* If we got back the original object, something is wrong. Perhaps + we are evaluating an expression too early. In any event, don't + infinitely recurse. */ + if (tem == exp) + abort (); + + /* In some cases, we will be offsetting OP0's address by a constant. + So get it as a sum, if possible. If we will be using it + directly in an insn, we validate it. */ + op0 = expand_expr (tem, NULL_RTX, VOIDmode, EXPAND_SUM); + + /* If this is a constant, put it into a register if it is a + legitimate constant and memory if it isn't. */ + if (CONSTANT_P (op0)) + { + enum machine_mode mode = TYPE_MODE (TREE_TYPE (tem)); + if (mode != BLKmode && LEGITIMATE_CONSTANT_P (op0)) + op0 = force_reg (mode, op0); + else + op0 = validize_mem (force_const_mem (mode, op0)); + } + + if (offset != 0) + { + rtx offset_rtx = expand_expr (offset, NULL_RTX, VOIDmode, 0); + + if (GET_CODE (op0) != MEM) + abort (); + op0 = change_address (op0, VOIDmode, + gen_rtx (PLUS, Pmode, XEXP (op0, 0), + force_reg (Pmode, offset_rtx))); + } + + /* Don't forget about volatility even if this is a bitfield. */ + if (GET_CODE (op0) == MEM && volatilep && ! MEM_VOLATILE_P (op0)) + { + op0 = copy_rtx (op0); + MEM_VOLATILE_P (op0) = 1; + } + + if (mode1 == VOIDmode + || (mode1 != BLKmode && ! direct_load[(int) mode1] + && modifier != EXPAND_CONST_ADDRESS + && modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER) + || GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) + { + /* In cases where an aligned union has an unaligned object + as a field, we might be extracting a BLKmode value from + an integer-mode (e.g., SImode) object. Handle this case + by doing the extract into an object as wide as the field + (which we know to be the width of a basic mode), then + storing into memory, and changing the mode to BLKmode. */ + enum machine_mode ext_mode = mode; + + if (ext_mode == BLKmode) + ext_mode = mode_for_size (bitsize, MODE_INT, 1); + + if (ext_mode == BLKmode) + abort (); + + op0 = extract_bit_field (validize_mem (op0), bitsize, bitpos, + unsignedp, target, ext_mode, ext_mode, + TYPE_ALIGN (TREE_TYPE (tem)) / BITS_PER_UNIT, + int_size_in_bytes (TREE_TYPE (tem))); + if (mode == BLKmode) + { + rtx new = assign_stack_temp (ext_mode, + bitsize / BITS_PER_UNIT, 0); + + emit_move_insn (new, op0); + op0 = copy_rtx (new); + PUT_MODE (op0, BLKmode); + } + + return op0; + } + + /* Get a reference to just this component. */ + if (modifier == EXPAND_CONST_ADDRESS + || modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER) + op0 = gen_rtx (MEM, mode1, plus_constant (XEXP (op0, 0), + (bitpos / BITS_PER_UNIT))); + else + op0 = change_address (op0, mode1, + plus_constant (XEXP (op0, 0), + (bitpos / BITS_PER_UNIT))); + MEM_IN_STRUCT_P (op0) = 1; + MEM_VOLATILE_P (op0) |= volatilep; + if (mode == mode1 || mode1 == BLKmode || mode1 == tmode) + return op0; + if (target == 0) + target = gen_reg_rtx (tmode != VOIDmode ? tmode : mode); + convert_move (target, op0, unsignedp); + return target; + } + + case OFFSET_REF: + { + tree base = build1 (ADDR_EXPR, type, TREE_OPERAND (exp, 0)); + tree addr = build (PLUS_EXPR, type, base, TREE_OPERAND (exp, 1)); + op0 = expand_expr (addr, NULL_RTX, VOIDmode, EXPAND_SUM); + temp = gen_rtx (MEM, mode, memory_address (mode, op0)); + MEM_IN_STRUCT_P (temp) = 1; + MEM_VOLATILE_P (temp) = TREE_THIS_VOLATILE (exp); +#if 0 /* It is incorrect to set RTX_UNCHANGING_P here, because the fact that + a location is accessed through a pointer to const does not mean + that the value there can never change. */ + RTX_UNCHANGING_P (temp) = TREE_READONLY (exp); +#endif + return temp; + } + + /* Intended for a reference to a buffer of a file-object in Pascal. + But it's not certain that a special tree code will really be + necessary for these. INDIRECT_REF might work for them. */ + case BUFFER_REF: + abort (); + + /* IN_EXPR: Inlined pascal set IN expression. + + Algorithm: + rlo = set_low - (set_low%bits_per_word); + the_word = set [ (index - rlo)/bits_per_word ]; + bit_index = index % bits_per_word; + bitmask = 1 << bit_index; + return !!(the_word & bitmask); */ + case IN_EXPR: + preexpand_calls (exp); + { + tree set = TREE_OPERAND (exp, 0); + tree index = TREE_OPERAND (exp, 1); + tree set_type = TREE_TYPE (set); + + tree set_low_bound = TYPE_MIN_VALUE (TYPE_DOMAIN (set_type)); + tree set_high_bound = TYPE_MAX_VALUE (TYPE_DOMAIN (set_type)); + + rtx index_val; + rtx lo_r; + rtx hi_r; + rtx rlow; + rtx diff, quo, rem, addr, bit, result; + rtx setval, setaddr; + enum machine_mode index_mode = TYPE_MODE (TREE_TYPE (index)); + + if (target == 0) + target = gen_reg_rtx (mode); + + /* If domain is empty, answer is no. */ + if (tree_int_cst_lt (set_high_bound, set_low_bound)) + return const0_rtx; + + index_val = expand_expr (index, 0, VOIDmode, 0); + lo_r = expand_expr (set_low_bound, 0, VOIDmode, 0); + hi_r = expand_expr (set_high_bound, 0, VOIDmode, 0); + setval = expand_expr (set, 0, VOIDmode, 0); + setaddr = XEXP (setval, 0); + + /* Compare index against bounds, if they are constant. */ + if (GET_CODE (index_val) == CONST_INT + && GET_CODE (lo_r) == CONST_INT + && INTVAL (index_val) < INTVAL (lo_r)) + return const0_rtx; + + if (GET_CODE (index_val) == CONST_INT + && GET_CODE (hi_r) == CONST_INT + && INTVAL (hi_r) < INTVAL (index_val)) + return const0_rtx; + + /* If we get here, we have to generate the code for both cases + (in range and out of range). */ + + op0 = gen_label_rtx (); + op1 = gen_label_rtx (); + + if (! (GET_CODE (index_val) == CONST_INT + && GET_CODE (lo_r) == CONST_INT)) + { + emit_cmp_insn (index_val, lo_r, LT, NULL_RTX, + GET_MODE (index_val), 0, 0); + emit_jump_insn (gen_blt (op1)); + } + + if (! (GET_CODE (index_val) == CONST_INT + && GET_CODE (hi_r) == CONST_INT)) + { + emit_cmp_insn (index_val, hi_r, GT, NULL_RTX, + GET_MODE (index_val), 0, 0); + emit_jump_insn (gen_bgt (op1)); + } + + /* Calculate the element number of bit zero in the first word + of the set. */ + if (GET_CODE (lo_r) == CONST_INT) + rlow = GEN_INT (INTVAL (lo_r) + & ~ ((HOST_WIDE_INT) 1 << BITS_PER_UNIT)); + else + rlow = expand_binop (index_mode, and_optab, lo_r, + GEN_INT (~((HOST_WIDE_INT) 1 << BITS_PER_UNIT)), + NULL_RTX, 0, OPTAB_LIB_WIDEN); + + diff = expand_binop (index_mode, sub_optab, + index_val, rlow, NULL_RTX, 0, OPTAB_LIB_WIDEN); + + quo = expand_divmod (0, TRUNC_DIV_EXPR, index_mode, diff, + GEN_INT (BITS_PER_UNIT), NULL_RTX, 0); + rem = expand_divmod (1, TRUNC_MOD_EXPR, index_mode, index_val, + GEN_INT (BITS_PER_UNIT), NULL_RTX, 0); + addr = memory_address (byte_mode, + expand_binop (index_mode, add_optab, + diff, setaddr, NULL_RTX, 0, + OPTAB_LIB_WIDEN)); + /* Extract the bit we want to examine */ + bit = expand_shift (RSHIFT_EXPR, byte_mode, + gen_rtx (MEM, byte_mode, addr), + make_tree (TREE_TYPE (index), rem), + NULL_RTX, 1); + result = expand_binop (byte_mode, and_optab, bit, const1_rtx, + GET_MODE (target) == byte_mode ? target : 0, + 1, OPTAB_LIB_WIDEN); + + if (result != target) + convert_move (target, result, 1); + + /* Output the code to handle the out-of-range case. */ + emit_jump (op0); + emit_label (op1); + emit_move_insn (target, const0_rtx); + emit_label (op0); + return target; + } + + case WITH_CLEANUP_EXPR: + if (RTL_EXPR_RTL (exp) == 0) + { + RTL_EXPR_RTL (exp) + = expand_expr (TREE_OPERAND (exp, 0), target, tmode, modifier); + cleanups_this_call + = tree_cons (NULL_TREE, TREE_OPERAND (exp, 2), cleanups_this_call); + /* That's it for this cleanup. */ + TREE_OPERAND (exp, 2) = 0; + } + return RTL_EXPR_RTL (exp); + + case CALL_EXPR: + /* Check for a built-in function. */ + if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) == FUNCTION_DECL + && DECL_BUILT_IN (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))) + return expand_builtin (exp, target, subtarget, tmode, ignore); + /* If this call was expanded already by preexpand_calls, + just return the result we got. */ + if (CALL_EXPR_RTL (exp) != 0) + return CALL_EXPR_RTL (exp); + return expand_call (exp, target, ignore); + + case NON_LVALUE_EXPR: + case NOP_EXPR: + case CONVERT_EXPR: + case REFERENCE_EXPR: + if (TREE_CODE (type) == VOID_TYPE || ignore) + { + expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, modifier); + return const0_rtx; + } + if (mode == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) + return expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, modifier); + if (TREE_CODE (type) == UNION_TYPE) + { + tree valtype = TREE_TYPE (TREE_OPERAND (exp, 0)); + if (target == 0) + { + if (mode == BLKmode) + { + if (TYPE_SIZE (type) == 0 + || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) + abort (); + target = assign_stack_temp (BLKmode, + (TREE_INT_CST_LOW (TYPE_SIZE (type)) + + BITS_PER_UNIT - 1) + / BITS_PER_UNIT, 0); + } + else + target = gen_reg_rtx (mode); + } + if (GET_CODE (target) == MEM) + /* Store data into beginning of memory target. */ + store_expr (TREE_OPERAND (exp, 0), + change_address (target, TYPE_MODE (valtype), 0), 0); + + else if (GET_CODE (target) == REG) + /* Store this field into a union of the proper type. */ + store_field (target, GET_MODE_BITSIZE (TYPE_MODE (valtype)), 0, + TYPE_MODE (valtype), TREE_OPERAND (exp, 0), + VOIDmode, 0, 1, + int_size_in_bytes (TREE_TYPE (TREE_OPERAND (exp, 0)))); + else + abort (); + + /* Return the entire union. */ + return target; + } + op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, mode, 0); + if (GET_MODE (op0) == mode) + return op0; + /* If arg is a constant integer being extended from a narrower mode, + we must really truncate to get the extended bits right. Otherwise + (unsigned long) (unsigned char) ("\377"[0]) + would come out as ffffffff. */ + if (GET_MODE (op0) == VOIDmode + && (GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) + < GET_MODE_BITSIZE (mode))) + { + /* MODE must be narrower than HOST_BITS_PER_INT. */ + int width = GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))); + + if (width < HOST_BITS_PER_WIDE_INT) + { + HOST_WIDE_INT val = (GET_CODE (op0) == CONST_INT ? INTVAL (op0) + : CONST_DOUBLE_LOW (op0)); + if (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))) + || !(val & ((HOST_WIDE_INT) 1 << (width - 1)))) + val &= ((HOST_WIDE_INT) 1 << width) - 1; + else + val |= ~(((HOST_WIDE_INT) 1 << width) - 1); + + op0 = GEN_INT (val); + } + else + { + op0 = (simplify_unary_operation + ((TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))) + ? ZERO_EXTEND : SIGN_EXTEND), + mode, op0, + TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))); + if (op0 == 0) + abort (); + } + } + if (GET_MODE (op0) == VOIDmode) + return op0; + if (modifier == EXPAND_INITIALIZER) + return gen_rtx (unsignedp ? ZERO_EXTEND : SIGN_EXTEND, mode, op0); + if (flag_force_mem && GET_CODE (op0) == MEM) + op0 = copy_to_reg (op0); + + if (target == 0) + return convert_to_mode (mode, op0, TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0)))); + else + convert_move (target, op0, TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0)))); + return target; + + case PLUS_EXPR: + /* We come here from MINUS_EXPR when the second operand is a constant. */ + plus_expr: + this_optab = add_optab; + + /* If we are adding a constant, an RTL_EXPR that is sp, fp, or ap, and + something else, make sure we add the register to the constant and + then to the other thing. This case can occur during strength + reduction and doing it this way will produce better code if the + frame pointer or argument pointer is eliminated. + + fold-const.c will ensure that the constant is always in the inner + PLUS_EXPR, so the only case we need to do anything about is if + sp, ap, or fp is our second argument, in which case we must swap + the innermost first argument and our second argument. */ + + if (TREE_CODE (TREE_OPERAND (exp, 0)) == PLUS_EXPR + && TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 1)) == INTEGER_CST + && TREE_CODE (TREE_OPERAND (exp, 1)) == RTL_EXPR + && (RTL_EXPR_RTL (TREE_OPERAND (exp, 1)) == frame_pointer_rtx + || RTL_EXPR_RTL (TREE_OPERAND (exp, 1)) == stack_pointer_rtx + || RTL_EXPR_RTL (TREE_OPERAND (exp, 1)) == arg_pointer_rtx)) + { + tree t = TREE_OPERAND (exp, 1); + + TREE_OPERAND (exp, 1) = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); + TREE_OPERAND (TREE_OPERAND (exp, 0), 0) = t; + } + + /* If the result is to be Pmode and we are adding an integer to + something, we might be forming a constant. So try to use + plus_constant. If it produces a sum and we can't accept it, + use force_operand. This allows P = &ARR[const] to generate + efficient code on machines where a SYMBOL_REF is not a valid + address. + + If this is an EXPAND_SUM call, always return the sum. */ + if (TREE_CODE (TREE_OPERAND (exp, 0)) == INTEGER_CST + && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT + && (modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER + || mode == Pmode)) + { + op1 = expand_expr (TREE_OPERAND (exp, 1), subtarget, VOIDmode, + EXPAND_SUM); + op1 = plus_constant (op1, TREE_INT_CST_LOW (TREE_OPERAND (exp, 0))); + if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER) + op1 = force_operand (op1, target); + return op1; + } + + else if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST + && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_INT + && (modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER + || mode == Pmode)) + { + op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, + EXPAND_SUM); + op0 = plus_constant (op0, TREE_INT_CST_LOW (TREE_OPERAND (exp, 1))); + if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER) + op0 = force_operand (op0, target); + return op0; + } + + /* No sense saving up arithmetic to be done + if it's all in the wrong mode to form part of an address. + And force_operand won't know whether to sign-extend or + zero-extend. */ + if ((modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER) + || mode != Pmode) goto binop; + + preexpand_calls (exp); + if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1))) + subtarget = 0; + + op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, modifier); + op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, modifier); + + /* Make sure any term that's a sum with a constant comes last. */ + if (GET_CODE (op0) == PLUS + && CONSTANT_P (XEXP (op0, 1))) + { + temp = op0; + op0 = op1; + op1 = temp; + } + /* If adding to a sum including a constant, + associate it to put the constant outside. */ + if (GET_CODE (op1) == PLUS + && CONSTANT_P (XEXP (op1, 1))) + { + rtx constant_term = const0_rtx; + + temp = simplify_binary_operation (PLUS, mode, XEXP (op1, 0), op0); + if (temp != 0) + op0 = temp; + /* Ensure that MULT comes first if there is one. */ + else if (GET_CODE (op0) == MULT) + op0 = gen_rtx (PLUS, mode, op0, XEXP (op1, 0)); + else + op0 = gen_rtx (PLUS, mode, XEXP (op1, 0), op0); + + /* Let's also eliminate constants from op0 if possible. */ + op0 = eliminate_constant_term (op0, &constant_term); + + /* CONSTANT_TERM and XEXP (op1, 1) are known to be constant, so + their sum should be a constant. Form it into OP1, since the + result we want will then be OP0 + OP1. */ + + temp = simplify_binary_operation (PLUS, mode, constant_term, + XEXP (op1, 1)); + if (temp != 0) + op1 = temp; + else + op1 = gen_rtx (PLUS, mode, constant_term, XEXP (op1, 1)); + } + + /* Put a constant term last and put a multiplication first. */ + if (CONSTANT_P (op0) || GET_CODE (op1) == MULT) + temp = op1, op1 = op0, op0 = temp; + + temp = simplify_binary_operation (PLUS, mode, op0, op1); + return temp ? temp : gen_rtx (PLUS, mode, op0, op1); + + case MINUS_EXPR: + /* Handle difference of two symbolic constants, + for the sake of an initializer. */ + if ((modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER) + && really_constant_p (TREE_OPERAND (exp, 0)) + && really_constant_p (TREE_OPERAND (exp, 1))) + { + rtx op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, + VOIDmode, modifier); + rtx op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, + VOIDmode, modifier); + return gen_rtx (MINUS, mode, op0, op1); + } + /* Convert A - const to A + (-const). */ + if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST) + { + exp = build (PLUS_EXPR, type, TREE_OPERAND (exp, 0), + fold (build1 (NEGATE_EXPR, type, + TREE_OPERAND (exp, 1)))); + goto plus_expr; + } + this_optab = sub_optab; + goto binop; + + case MULT_EXPR: + preexpand_calls (exp); + /* If first operand is constant, swap them. + Thus the following special case checks need only + check the second operand. */ + if (TREE_CODE (TREE_OPERAND (exp, 0)) == INTEGER_CST) + { + register tree t1 = TREE_OPERAND (exp, 0); + TREE_OPERAND (exp, 0) = TREE_OPERAND (exp, 1); + TREE_OPERAND (exp, 1) = t1; + } + + /* Attempt to return something suitable for generating an + indexed address, for machines that support that. */ + + if (modifier == EXPAND_SUM && mode == Pmode + && TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST + && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT) + { + op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, EXPAND_SUM); + + /* Apply distributive law if OP0 is x+c. */ + if (GET_CODE (op0) == PLUS + && GET_CODE (XEXP (op0, 1)) == CONST_INT) + return gen_rtx (PLUS, mode, + gen_rtx (MULT, mode, XEXP (op0, 0), + GEN_INT (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)))), + GEN_INT (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)) + * INTVAL (XEXP (op0, 1)))); + + if (GET_CODE (op0) != REG) + op0 = force_operand (op0, NULL_RTX); + if (GET_CODE (op0) != REG) + op0 = copy_to_mode_reg (mode, op0); + + return gen_rtx (MULT, mode, op0, + GEN_INT (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)))); + } + + if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1))) + subtarget = 0; + + /* Check for multiplying things that have been extended + from a narrower type. If this machine supports multiplying + in that narrower type with a result in the desired type, + do it that way, and avoid the explicit type-conversion. */ + if (TREE_CODE (TREE_OPERAND (exp, 0)) == NOP_EXPR + && TREE_CODE (type) == INTEGER_TYPE + && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))) + < TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (exp, 0)))) + && ((TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST + && int_fits_type_p (TREE_OPERAND (exp, 1), + TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))) + /* Don't use a widening multiply if a shift will do. */ + && ((GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 1)))) + > HOST_BITS_PER_WIDE_INT) + || exact_log2 (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1))) < 0)) + || + (TREE_CODE (TREE_OPERAND (exp, 1)) == NOP_EXPR + && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 1), 0))) + == + TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))) + /* If both operands are extended, they must either both + be zero-extended or both be sign-extended. */ + && (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 1), 0))) + == + TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))))))) + { + enum machine_mode innermode + = TYPE_MODE (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))); + this_optab = (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))) + ? umul_widen_optab : smul_widen_optab); + if (mode == GET_MODE_WIDER_MODE (innermode) + && this_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) + { + op0 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 0), 0), + NULL_RTX, VOIDmode, 0); + if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST) + op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, + VOIDmode, 0); + else + op1 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 1), 0), + NULL_RTX, VOIDmode, 0); + goto binop2; + } + } + op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); + op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0); + return expand_mult (mode, op0, op1, target, unsignedp); + + case TRUNC_DIV_EXPR: + case FLOOR_DIV_EXPR: + case CEIL_DIV_EXPR: + case ROUND_DIV_EXPR: + case EXACT_DIV_EXPR: + preexpand_calls (exp); + if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1))) + subtarget = 0; + /* Possible optimization: compute the dividend with EXPAND_SUM + then if the divisor is constant can optimize the case + where some terms of the dividend have coeffs divisible by it. */ + op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); + op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0); + return expand_divmod (0, code, mode, op0, op1, target, unsignedp); + + case RDIV_EXPR: + this_optab = flodiv_optab; + goto binop; + + case TRUNC_MOD_EXPR: + case FLOOR_MOD_EXPR: + case CEIL_MOD_EXPR: + case ROUND_MOD_EXPR: + preexpand_calls (exp); + if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1))) + subtarget = 0; + op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); + op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0); + return expand_divmod (1, code, mode, op0, op1, target, unsignedp); + + case FIX_ROUND_EXPR: + case FIX_FLOOR_EXPR: + case FIX_CEIL_EXPR: + abort (); /* Not used for C. */ + + case FIX_TRUNC_EXPR: + op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0); + if (target == 0) + target = gen_reg_rtx (mode); + expand_fix (target, op0, unsignedp); + return target; + + case FLOAT_EXPR: + op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0); + if (target == 0) + target = gen_reg_rtx (mode); + /* expand_float can't figure out what to do if FROM has VOIDmode. + So give it the correct mode. With -O, cse will optimize this. */ + if (GET_MODE (op0) == VOIDmode) + op0 = copy_to_mode_reg (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))), + op0); + expand_float (target, op0, + TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0)))); + return target; + + case NEGATE_EXPR: + op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, 0); + temp = expand_unop (mode, neg_optab, op0, target, 0); + if (temp == 0) + abort (); + return temp; + + case ABS_EXPR: + op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); + + /* Handle complex values specially. */ + { + enum machine_mode opmode + = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))); + + if (GET_MODE_CLASS (opmode) == MODE_COMPLEX_INT + || GET_MODE_CLASS (opmode) == MODE_COMPLEX_FLOAT) + return expand_complex_abs (opmode, op0, target, unsignedp); + } + + /* Unsigned abs is simply the operand. Testing here means we don't + risk generating incorrect code below. */ + if (TREE_UNSIGNED (type)) + return op0; + + /* First try to do it with a special abs instruction. */ + temp = expand_unop (mode, abs_optab, op0, target, 0); + if (temp != 0) + return temp; + + /* If this machine has expensive jumps, we can do integer absolute + value of X as (((signed) x >> (W-1)) ^ x) - ((signed) x >> (W-1)), + where W is the width of MODE. */ + + if (GET_MODE_CLASS (mode) == MODE_INT && BRANCH_COST >= 2) + { + rtx extended = expand_shift (RSHIFT_EXPR, mode, op0, + size_int (GET_MODE_BITSIZE (mode) - 1), + NULL_RTX, 0); + + temp = expand_binop (mode, xor_optab, extended, op0, target, 0, + OPTAB_LIB_WIDEN); + if (temp != 0) + temp = expand_binop (mode, sub_optab, temp, extended, target, 0, + OPTAB_LIB_WIDEN); + + if (temp != 0) + return temp; + } + + /* If that does not win, use conditional jump and negate. */ + target = original_target; + temp = gen_label_rtx (); + if (target == 0 || ! safe_from_p (target, TREE_OPERAND (exp, 0)) + || (GET_CODE (target) == REG + && REGNO (target) < FIRST_PSEUDO_REGISTER)) + target = gen_reg_rtx (mode); + emit_move_insn (target, op0); + emit_cmp_insn (target, + expand_expr (convert (type, integer_zero_node), + NULL_RTX, VOIDmode, 0), + GE, NULL_RTX, mode, 0, 0); + NO_DEFER_POP; + emit_jump_insn (gen_bge (temp)); + op0 = expand_unop (mode, neg_optab, target, target, 0); + if (op0 != target) + emit_move_insn (target, op0); + emit_label (temp); + OK_DEFER_POP; + return target; + + case MAX_EXPR: + case MIN_EXPR: + target = original_target; + if (target == 0 || ! safe_from_p (target, TREE_OPERAND (exp, 1)) + || (GET_CODE (target) == REG + && REGNO (target) < FIRST_PSEUDO_REGISTER)) + target = gen_reg_rtx (mode); + op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0); + op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, 0); + + /* First try to do it with a special MIN or MAX instruction. + If that does not win, use a conditional jump to select the proper + value. */ + this_optab = (TREE_UNSIGNED (type) + ? (code == MIN_EXPR ? umin_optab : umax_optab) + : (code == MIN_EXPR ? smin_optab : smax_optab)); + + temp = expand_binop (mode, this_optab, op0, op1, target, unsignedp, + OPTAB_WIDEN); + if (temp != 0) + return temp; + + if (target != op0) + emit_move_insn (target, op0); + op0 = gen_label_rtx (); + /* If this mode is an integer too wide to compare properly, + compare word by word. Rely on cse to optimize constant cases. */ + if (GET_MODE_CLASS (mode) == MODE_INT + && !can_compare_p (mode)) + { + if (code == MAX_EXPR) + do_jump_by_parts_greater_rtx (mode, TREE_UNSIGNED (type), target, op1, NULL, op0); + else + do_jump_by_parts_greater_rtx (mode, TREE_UNSIGNED (type), op1, target, NULL, op0); + emit_move_insn (target, op1); + } + else + { + if (code == MAX_EXPR) + temp = (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 1))) + ? compare_from_rtx (target, op1, GEU, 1, mode, NULL_RTX, 0) + : compare_from_rtx (target, op1, GE, 0, mode, NULL_RTX, 0)); + else + temp = (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 1))) + ? compare_from_rtx (target, op1, LEU, 1, mode, NULL_RTX, 0) + : compare_from_rtx (target, op1, LE, 0, mode, NULL_RTX, 0)); + if (temp == const0_rtx) + emit_move_insn (target, op1); + else if (temp != const_true_rtx) + { + if (bcc_gen_fctn[(int) GET_CODE (temp)] != 0) + emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (temp)]) (op0)); + else + abort (); + emit_move_insn (target, op1); + } + } + emit_label (op0); + return target; + +/* ??? Can optimize when the operand of this is a bitwise operation, + by using a different bitwise operation. */ + case BIT_NOT_EXPR: + op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); + temp = expand_unop (mode, one_cmpl_optab, op0, target, 1); + if (temp == 0) + abort (); + return temp; + + case FFS_EXPR: + op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); + temp = expand_unop (mode, ffs_optab, op0, target, 1); + if (temp == 0) + abort (); + return temp; + +/* ??? Can optimize bitwise operations with one arg constant. + Can optimize (a bitwise1 n) bitwise2 (a bitwise3 b) + and (a bitwise1 b) bitwise2 b (etc) + but that is probably not worth while. */ + +/* BIT_AND_EXPR is for bitwise anding. + TRUTH_AND_EXPR is for anding two boolean values + when we want in all cases to compute both of them. + In general it is fastest to do TRUTH_AND_EXPR by + computing both operands as actual zero-or-1 values + and then bitwise anding. In cases where there cannot + be any side effects, better code would be made by + treating TRUTH_AND_EXPR like TRUTH_ANDIF_EXPR; + but the question is how to recognize those cases. */ + + case TRUTH_AND_EXPR: + case BIT_AND_EXPR: + this_optab = and_optab; + goto binop; + +/* See comment above about TRUTH_AND_EXPR; it applies here too. */ + case TRUTH_OR_EXPR: + case BIT_IOR_EXPR: + this_optab = ior_optab; + goto binop; + + case TRUTH_XOR_EXPR: + case BIT_XOR_EXPR: + this_optab = xor_optab; + goto binop; + + case LSHIFT_EXPR: + case RSHIFT_EXPR: + case LROTATE_EXPR: + case RROTATE_EXPR: + preexpand_calls (exp); + if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1))) + subtarget = 0; + op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); + return expand_shift (code, mode, op0, TREE_OPERAND (exp, 1), target, + unsignedp); + +/* Could determine the answer when only additive constants differ. + Also, the addition of one can be handled by changing the condition. */ + case LT_EXPR: + case LE_EXPR: + case GT_EXPR: + case GE_EXPR: + case EQ_EXPR: + case NE_EXPR: + preexpand_calls (exp); + temp = do_store_flag (exp, target, tmode != VOIDmode ? tmode : mode, 0); + if (temp != 0) + return temp; + /* For foo != 0, load foo, and if it is nonzero load 1 instead. */ + if (code == NE_EXPR && integer_zerop (TREE_OPERAND (exp, 1)) + && original_target + && GET_CODE (original_target) == REG + && (GET_MODE (original_target) + == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))) + { + temp = expand_expr (TREE_OPERAND (exp, 0), original_target, VOIDmode, 0); + if (temp != original_target) + temp = copy_to_reg (temp); + op1 = gen_label_rtx (); + emit_cmp_insn (temp, const0_rtx, EQ, NULL_RTX, + GET_MODE (temp), unsignedp, 0); + emit_jump_insn (gen_beq (op1)); + emit_move_insn (temp, const1_rtx); + emit_label (op1); + return temp; + } + /* If no set-flag instruction, must generate a conditional + store into a temporary variable. Drop through + and handle this like && and ||. */ + + case TRUTH_ANDIF_EXPR: + case TRUTH_ORIF_EXPR: + if (target == 0 || ! safe_from_p (target, exp) + /* Make sure we don't have a hard reg (such as function's return + value) live across basic blocks, if not optimizing. */ + || (!optimize && GET_CODE (target) == REG + && REGNO (target) < FIRST_PSEUDO_REGISTER)) + target = gen_reg_rtx (tmode != VOIDmode ? tmode : mode); + emit_clr_insn (target); + op1 = gen_label_rtx (); + jumpifnot (exp, op1); + emit_0_to_1_insn (target); + emit_label (op1); + return target; + + case TRUTH_NOT_EXPR: + op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, 0); + /* The parser is careful to generate TRUTH_NOT_EXPR + only with operands that are always zero or one. */ + temp = expand_binop (mode, xor_optab, op0, const1_rtx, + target, 1, OPTAB_LIB_WIDEN); + if (temp == 0) + abort (); + return temp; + + case COMPOUND_EXPR: + expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, 0); + emit_queue (); + return expand_expr (TREE_OPERAND (exp, 1), + (ignore ? const0_rtx : target), + VOIDmode, 0); + + case COND_EXPR: + { + /* Note that COND_EXPRs whose type is a structure or union + are required to be constructed to contain assignments of + a temporary variable, so that we can evaluate them here + for side effect only. If type is void, we must do likewise. */ + + /* If an arm of the branch requires a cleanup, + only that cleanup is performed. */ + + tree singleton = 0; + tree binary_op = 0, unary_op = 0; + tree old_cleanups = cleanups_this_call; + cleanups_this_call = 0; + + /* If this is (A ? 1 : 0) and A is a condition, just evaluate it and + convert it to our mode, if necessary. */ + if (integer_onep (TREE_OPERAND (exp, 1)) + && integer_zerop (TREE_OPERAND (exp, 2)) + && TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == '<') + { + op0 = expand_expr (TREE_OPERAND (exp, 0), target, mode, modifier); + if (GET_MODE (op0) == mode) + return op0; + if (target == 0) + target = gen_reg_rtx (mode); + convert_move (target, op0, unsignedp); + return target; + } + + /* If we are not to produce a result, we have no target. Otherwise, + if a target was specified use it; it will not be used as an + intermediate target unless it is safe. If no target, use a + temporary. */ + + if (mode == VOIDmode || ignore) + temp = 0; + else if (original_target + && safe_from_p (original_target, TREE_OPERAND (exp, 0))) + temp = original_target; + else if (mode == BLKmode) + { + if (TYPE_SIZE (type) == 0 + || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) + abort (); + temp = assign_stack_temp (BLKmode, + (TREE_INT_CST_LOW (TYPE_SIZE (type)) + + BITS_PER_UNIT - 1) + / BITS_PER_UNIT, 0); + } + else + temp = gen_reg_rtx (mode); + + /* Check for X ? A + B : A. If we have this, we can copy + A to the output and conditionally add B. Similarly for unary + operations. Don't do this if X has side-effects because + those side effects might affect A or B and the "?" operation is + a sequence point in ANSI. (We test for side effects later.) */ + + if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 1))) == '2' + && operand_equal_p (TREE_OPERAND (exp, 2), + TREE_OPERAND (TREE_OPERAND (exp, 1), 0), 0)) + singleton = TREE_OPERAND (exp, 2), binary_op = TREE_OPERAND (exp, 1); + else if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 2))) == '2' + && operand_equal_p (TREE_OPERAND (exp, 1), + TREE_OPERAND (TREE_OPERAND (exp, 2), 0), 0)) + singleton = TREE_OPERAND (exp, 1), binary_op = TREE_OPERAND (exp, 2); + else if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 1))) == '1' + && operand_equal_p (TREE_OPERAND (exp, 2), + TREE_OPERAND (TREE_OPERAND (exp, 1), 0), 0)) + singleton = TREE_OPERAND (exp, 2), unary_op = TREE_OPERAND (exp, 1); + else if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 2))) == '1' + && operand_equal_p (TREE_OPERAND (exp, 1), + TREE_OPERAND (TREE_OPERAND (exp, 2), 0), 0)) + singleton = TREE_OPERAND (exp, 1), unary_op = TREE_OPERAND (exp, 2); + + /* If we had X ? A + 1 : A and we can do the test of X as a store-flag + operation, do this as A + (X != 0). Similarly for other simple + binary operators. */ + if (singleton && binary_op + && ! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 0)) + && (TREE_CODE (binary_op) == PLUS_EXPR + || TREE_CODE (binary_op) == MINUS_EXPR + || TREE_CODE (binary_op) == BIT_IOR_EXPR + || TREE_CODE (binary_op) == BIT_XOR_EXPR + || TREE_CODE (binary_op) == BIT_AND_EXPR) + && integer_onep (TREE_OPERAND (binary_op, 1)) + && TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == '<') + { + rtx result; + optab boptab = (TREE_CODE (binary_op) == PLUS_EXPR ? add_optab + : TREE_CODE (binary_op) == MINUS_EXPR ? sub_optab + : TREE_CODE (binary_op) == BIT_IOR_EXPR ? ior_optab + : TREE_CODE (binary_op) == BIT_XOR_EXPR ? xor_optab + : and_optab); + + /* If we had X ? A : A + 1, do this as A + (X == 0). + + We have to invert the truth value here and then put it + back later if do_store_flag fails. We cannot simply copy + TREE_OPERAND (exp, 0) to another variable and modify that + because invert_truthvalue can modify the tree pointed to + by its argument. */ + if (singleton == TREE_OPERAND (exp, 1)) + TREE_OPERAND (exp, 0) + = invert_truthvalue (TREE_OPERAND (exp, 0)); + + result = do_store_flag (TREE_OPERAND (exp, 0), + (safe_from_p (temp, singleton) + ? temp : NULL_RTX), + mode, BRANCH_COST <= 1); + + if (result) + { + op1 = expand_expr (singleton, NULL_RTX, VOIDmode, 0); + return expand_binop (mode, boptab, op1, result, temp, + unsignedp, OPTAB_LIB_WIDEN); + } + else if (singleton == TREE_OPERAND (exp, 1)) + TREE_OPERAND (exp, 0) + = invert_truthvalue (TREE_OPERAND (exp, 0)); + } + + NO_DEFER_POP; + op0 = gen_label_rtx (); + + if (singleton && ! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 0))) + { + if (temp != 0) + { + /* If the target conflicts with the other operand of the + binary op, we can't use it. Also, we can't use the target + if it is a hard register, because evaluating the condition + might clobber it. */ + if ((binary_op + && ! safe_from_p (temp, TREE_OPERAND (binary_op, 1))) + || (GET_CODE (temp) == REG + && REGNO (temp) < FIRST_PSEUDO_REGISTER)) + temp = gen_reg_rtx (mode); + store_expr (singleton, temp, 0); + } + else + expand_expr (singleton, + ignore ? const0_rtx : NULL_RTX, VOIDmode, 0); + if (cleanups_this_call) + { + sorry ("aggregate value in COND_EXPR"); + cleanups_this_call = 0; + } + if (singleton == TREE_OPERAND (exp, 1)) + jumpif (TREE_OPERAND (exp, 0), op0); + else + jumpifnot (TREE_OPERAND (exp, 0), op0); + + if (binary_op && temp == 0) + /* Just touch the other operand. */ + expand_expr (TREE_OPERAND (binary_op, 1), + ignore ? const0_rtx : NULL_RTX, VOIDmode, 0); + else if (binary_op) + store_expr (build (TREE_CODE (binary_op), type, + make_tree (type, temp), + TREE_OPERAND (binary_op, 1)), + temp, 0); + else + store_expr (build1 (TREE_CODE (unary_op), type, + make_tree (type, temp)), + temp, 0); + op1 = op0; + } +#if 0 + /* This is now done in jump.c and is better done there because it + produces shorter register lifetimes. */ + + /* Check for both possibilities either constants or variables + in registers (but not the same as the target!). If so, can + save branches by assigning one, branching, and assigning the + other. */ + else if (temp && GET_MODE (temp) != BLKmode + && (TREE_CONSTANT (TREE_OPERAND (exp, 1)) + || ((TREE_CODE (TREE_OPERAND (exp, 1)) == PARM_DECL + || TREE_CODE (TREE_OPERAND (exp, 1)) == VAR_DECL) + && DECL_RTL (TREE_OPERAND (exp, 1)) + && GET_CODE (DECL_RTL (TREE_OPERAND (exp, 1))) == REG + && DECL_RTL (TREE_OPERAND (exp, 1)) != temp)) + && (TREE_CONSTANT (TREE_OPERAND (exp, 2)) + || ((TREE_CODE (TREE_OPERAND (exp, 2)) == PARM_DECL + || TREE_CODE (TREE_OPERAND (exp, 2)) == VAR_DECL) + && DECL_RTL (TREE_OPERAND (exp, 2)) + && GET_CODE (DECL_RTL (TREE_OPERAND (exp, 2))) == REG + && DECL_RTL (TREE_OPERAND (exp, 2)) != temp))) + { + if (GET_CODE (temp) == REG && REGNO (temp) < FIRST_PSEUDO_REGISTER) + temp = gen_reg_rtx (mode); + store_expr (TREE_OPERAND (exp, 2), temp, 0); + jumpifnot (TREE_OPERAND (exp, 0), op0); + store_expr (TREE_OPERAND (exp, 1), temp, 0); + op1 = op0; + } +#endif + /* Check for A op 0 ? A : FOO and A op 0 ? FOO : A where OP is any + comparison operator. If we have one of these cases, set the + output to A, branch on A (cse will merge these two references), + then set the output to FOO. */ + else if (temp + && TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == '<' + && integer_zerop (TREE_OPERAND (TREE_OPERAND (exp, 0), 1)) + && operand_equal_p (TREE_OPERAND (TREE_OPERAND (exp, 0), 0), + TREE_OPERAND (exp, 1), 0) + && ! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 0)) + && safe_from_p (temp, TREE_OPERAND (exp, 2))) + { + if (GET_CODE (temp) == REG && REGNO (temp) < FIRST_PSEUDO_REGISTER) + temp = gen_reg_rtx (mode); + store_expr (TREE_OPERAND (exp, 1), temp, 0); + jumpif (TREE_OPERAND (exp, 0), op0); + store_expr (TREE_OPERAND (exp, 2), temp, 0); + op1 = op0; + } + else if (temp + && TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == '<' + && integer_zerop (TREE_OPERAND (TREE_OPERAND (exp, 0), 1)) + && operand_equal_p (TREE_OPERAND (TREE_OPERAND (exp, 0), 0), + TREE_OPERAND (exp, 2), 0) + && ! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 0)) + && safe_from_p (temp, TREE_OPERAND (exp, 1))) + { + if (GET_CODE (temp) == REG && REGNO (temp) < FIRST_PSEUDO_REGISTER) + temp = gen_reg_rtx (mode); + store_expr (TREE_OPERAND (exp, 2), temp, 0); + jumpifnot (TREE_OPERAND (exp, 0), op0); + store_expr (TREE_OPERAND (exp, 1), temp, 0); + op1 = op0; + } + else + { + op1 = gen_label_rtx (); + jumpifnot (TREE_OPERAND (exp, 0), op0); + if (temp != 0) + store_expr (TREE_OPERAND (exp, 1), temp, 0); + else + expand_expr (TREE_OPERAND (exp, 1), + ignore ? const0_rtx : NULL_RTX, VOIDmode, 0); + if (cleanups_this_call) + { + sorry ("aggregate value in COND_EXPR"); + cleanups_this_call = 0; + } + + emit_queue (); + emit_jump_insn (gen_jump (op1)); + emit_barrier (); + emit_label (op0); + if (temp != 0) + store_expr (TREE_OPERAND (exp, 2), temp, 0); + else + expand_expr (TREE_OPERAND (exp, 2), + ignore ? const0_rtx : NULL_RTX, VOIDmode, 0); + } + + if (cleanups_this_call) + { + sorry ("aggregate value in COND_EXPR"); + cleanups_this_call = 0; + } + + emit_queue (); + emit_label (op1); + OK_DEFER_POP; + cleanups_this_call = old_cleanups; + return temp; + } + + case TARGET_EXPR: + { + /* Something needs to be initialized, but we didn't know + where that thing was when building the tree. For example, + it could be the return value of a function, or a parameter + to a function which lays down in the stack, or a temporary + variable which must be passed by reference. + + We guarantee that the expression will either be constructed + or copied into our original target. */ + + tree slot = TREE_OPERAND (exp, 0); + tree exp1; + + if (TREE_CODE (slot) != VAR_DECL) + abort (); + + if (target == 0) + { + if (DECL_RTL (slot) != 0) + { + target = DECL_RTL (slot); + /* If we have already expanded the slot, so don't do + it again. (mrs) */ + if (TREE_OPERAND (exp, 1) == NULL_TREE) + return target; + } + else + { + target = assign_stack_temp (mode, int_size_in_bytes (type), 0); + /* All temp slots at this level must not conflict. */ + preserve_temp_slots (target); + DECL_RTL (slot) = target; + } + +#if 0 + /* I bet this needs to be done, and I bet that it needs to + be above, inside the else clause. The reason is + simple, how else is it going to get cleaned up? (mrs) + + The reason is probably did not work before, and was + commented out is because this was re-expanding already + expanded target_exprs (target == 0 and DECL_RTL (slot) + != 0) also cleaning them up many times as well. :-( */ + + /* Since SLOT is not known to the called function + to belong to its stack frame, we must build an explicit + cleanup. This case occurs when we must build up a reference + to pass the reference as an argument. In this case, + it is very likely that such a reference need not be + built here. */ + + if (TREE_OPERAND (exp, 2) == 0) + TREE_OPERAND (exp, 2) = maybe_build_cleanup (slot); + if (TREE_OPERAND (exp, 2)) + cleanups_this_call = tree_cons (NULL_TREE, TREE_OPERAND (exp, 2), + cleanups_this_call); +#endif + } + else + { + /* This case does occur, when expanding a parameter which + needs to be constructed on the stack. The target + is the actual stack address that we want to initialize. + The function we call will perform the cleanup in this case. */ + + /* If we have already assigned it space, use that space, + not target that we were passed in, as our target + parameter is only a hint. */ + if (DECL_RTL (slot) != 0) + { + target = DECL_RTL (slot); + /* If we have already expanded the slot, so don't do + it again. (mrs) */ + if (TREE_OPERAND (exp, 1) == NULL_TREE) + return target; + } + + DECL_RTL (slot) = target; + } + + exp1 = TREE_OPERAND (exp, 1); + /* Mark it as expanded. */ + TREE_OPERAND (exp, 1) = NULL_TREE; + + return expand_expr (exp1, target, tmode, modifier); + } + + case INIT_EXPR: + { + tree lhs = TREE_OPERAND (exp, 0); + tree rhs = TREE_OPERAND (exp, 1); + tree noncopied_parts = 0; + tree lhs_type = TREE_TYPE (lhs); + + temp = expand_assignment (lhs, rhs, ! ignore, original_target != 0); + if (TYPE_NONCOPIED_PARTS (lhs_type) != 0 && !fixed_type_p (rhs)) + noncopied_parts = init_noncopied_parts (stabilize_reference (lhs), + TYPE_NONCOPIED_PARTS (lhs_type)); + while (noncopied_parts != 0) + { + expand_assignment (TREE_VALUE (noncopied_parts), + TREE_PURPOSE (noncopied_parts), 0, 0); + noncopied_parts = TREE_CHAIN (noncopied_parts); + } + return temp; + } + + case MODIFY_EXPR: + { + /* If lhs is complex, expand calls in rhs before computing it. + That's so we don't compute a pointer and save it over a call. + If lhs is simple, compute it first so we can give it as a + target if the rhs is just a call. This avoids an extra temp and copy + and that prevents a partial-subsumption which makes bad code. + Actually we could treat component_ref's of vars like vars. */ + + tree lhs = TREE_OPERAND (exp, 0); + tree rhs = TREE_OPERAND (exp, 1); + tree noncopied_parts = 0; + tree lhs_type = TREE_TYPE (lhs); + + temp = 0; + + if (TREE_CODE (lhs) != VAR_DECL + && TREE_CODE (lhs) != RESULT_DECL + && TREE_CODE (lhs) != PARM_DECL) + preexpand_calls (exp); + + /* Check for |= or &= of a bitfield of size one into another bitfield + of size 1. In this case, (unless we need the result of the + assignment) we can do this more efficiently with a + test followed by an assignment, if necessary. + + ??? At this point, we can't get a BIT_FIELD_REF here. But if + things change so we do, this code should be enhanced to + support it. */ + if (ignore + && TREE_CODE (lhs) == COMPONENT_REF + && (TREE_CODE (rhs) == BIT_IOR_EXPR + || TREE_CODE (rhs) == BIT_AND_EXPR) + && TREE_OPERAND (rhs, 0) == lhs + && TREE_CODE (TREE_OPERAND (rhs, 1)) == COMPONENT_REF + && TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (lhs, 1))) == 1 + && TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (TREE_OPERAND (rhs, 1), 1))) == 1) + { + rtx label = gen_label_rtx (); + + do_jump (TREE_OPERAND (rhs, 1), + TREE_CODE (rhs) == BIT_IOR_EXPR ? label : 0, + TREE_CODE (rhs) == BIT_AND_EXPR ? label : 0); + expand_assignment (lhs, convert (TREE_TYPE (rhs), + (TREE_CODE (rhs) == BIT_IOR_EXPR + ? integer_one_node + : integer_zero_node)), + 0, 0); + do_pending_stack_adjust (); + emit_label (label); + return const0_rtx; + } + + if (TYPE_NONCOPIED_PARTS (lhs_type) != 0 + && ! (fixed_type_p (lhs) && fixed_type_p (rhs))) + noncopied_parts = save_noncopied_parts (stabilize_reference (lhs), + TYPE_NONCOPIED_PARTS (lhs_type)); + + temp = expand_assignment (lhs, rhs, ! ignore, original_target != 0); + while (noncopied_parts != 0) + { + expand_assignment (TREE_PURPOSE (noncopied_parts), + TREE_VALUE (noncopied_parts), 0, 0); + noncopied_parts = TREE_CHAIN (noncopied_parts); + } + return temp; + } + + case PREINCREMENT_EXPR: + case PREDECREMENT_EXPR: + return expand_increment (exp, 0); + + case POSTINCREMENT_EXPR: + case POSTDECREMENT_EXPR: + /* Faster to treat as pre-increment if result is not used. */ + return expand_increment (exp, ! ignore); + + case ADDR_EXPR: + /* Are we taking the address of a nested function? */ + if (TREE_CODE (TREE_OPERAND (exp, 0)) == FUNCTION_DECL + && decl_function_context (TREE_OPERAND (exp, 0)) != 0) + { + op0 = trampoline_address (TREE_OPERAND (exp, 0)); + op0 = force_operand (op0, target); + } + else + { + op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, + (modifier == EXPAND_INITIALIZER + ? modifier : EXPAND_CONST_ADDRESS)); + + /* We would like the object in memory. If it is a constant, + we can have it be statically allocated into memory. For + a non-constant (REG or SUBREG), we need to allocate some + memory and store the value into it. */ + + if (CONSTANT_P (op0)) + op0 = force_const_mem (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))), + op0); + + if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) + { + /* If this object is in a register, it must be not + be BLKmode. */ + tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0)); + enum machine_mode inner_mode = TYPE_MODE (inner_type); + rtx memloc + = assign_stack_temp (inner_mode, + int_size_in_bytes (inner_type), 1); + + emit_move_insn (memloc, op0); + op0 = memloc; + } + + if (GET_CODE (op0) != MEM) + abort (); + + if (modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER) + return XEXP (op0, 0); + op0 = force_operand (XEXP (op0, 0), target); + } + if (flag_force_addr && GET_CODE (op0) != REG) + return force_reg (Pmode, op0); + return op0; + + case ENTRY_VALUE_EXPR: + abort (); + + /* COMPLEX type for Extended Pascal & Fortran */ + case COMPLEX_EXPR: + { + enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp))); + + rtx prev; + + /* Get the rtx code of the operands. */ + op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0); + op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0); + + if (! target) + target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp))); + + prev = get_last_insn (); + + /* Tell flow that the whole of the destination is being set. */ + if (GET_CODE (target) == REG) + emit_insn (gen_rtx (CLOBBER, VOIDmode, target)); + + /* Move the real (op0) and imaginary (op1) parts to their location. */ + emit_move_insn (gen_realpart (mode, target), op0); + emit_move_insn (gen_imagpart (mode, target), op1); + + /* Complex construction should appear as a single unit. */ + group_insns (prev); + + return target; + } + + case REALPART_EXPR: + op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0); + return gen_realpart (mode, op0); + + case IMAGPART_EXPR: + op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0); + return gen_imagpart (mode, op0); + + case CONJ_EXPR: + { + enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp))); + rtx imag_t; + rtx prev; + + op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0); + + if (! target) + target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp))); + + prev = get_last_insn (); + + /* Tell flow that the whole of the destination is being set. */ + if (GET_CODE (target) == REG) + emit_insn (gen_rtx (CLOBBER, VOIDmode, target)); + + /* Store the realpart and the negated imagpart to target. */ + emit_move_insn (gen_realpart (mode, target), gen_realpart (mode, op0)); + + imag_t = gen_imagpart (mode, target); + temp = expand_unop (mode, neg_optab, + gen_imagpart (mode, op0), imag_t, 0); + if (temp != imag_t) + emit_move_insn (imag_t, temp); + + /* Conjugate should appear as a single unit */ + group_insns (prev); + + return target; + } + + case ERROR_MARK: + op0 = CONST0_RTX (tmode); + if (op0 != 0) + return op0; + return const0_rtx; + + default: + return (*lang_expand_expr) (exp, target, tmode, modifier); + } + + /* Here to do an ordinary binary operator, generating an instruction + from the optab already placed in `this_optab'. */ + binop: + preexpand_calls (exp); + if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1))) + subtarget = 0; + op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); + op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0); + binop2: + temp = expand_binop (mode, this_optab, op0, op1, target, + unsignedp, OPTAB_LIB_WIDEN); + if (temp == 0) + abort (); + return temp; +} + +/* Return the alignment in bits of EXP, a pointer valued expression. + But don't return more than MAX_ALIGN no matter what. + The alignment returned is, by default, the alignment of the thing that + EXP points to (if it is not a POINTER_TYPE, 0 is returned). + + Otherwise, look at the expression to see if we can do better, i.e., if the + expression is actually pointing at an object whose alignment is tighter. */ + +static int +get_pointer_alignment (exp, max_align) + tree exp; + unsigned max_align; +{ + unsigned align, inner; + + if (TREE_CODE (TREE_TYPE (exp)) != POINTER_TYPE) + return 0; + + align = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))); + align = MIN (align, max_align); + + while (1) + { + switch (TREE_CODE (exp)) + { + case NOP_EXPR: + case CONVERT_EXPR: + case NON_LVALUE_EXPR: + exp = TREE_OPERAND (exp, 0); + if (TREE_CODE (TREE_TYPE (exp)) != POINTER_TYPE) + return align; + inner = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))); + inner = MIN (inner, max_align); + align = MAX (align, inner); + break; + + case PLUS_EXPR: + /* If sum of pointer + int, restrict our maximum alignment to that + imposed by the integer. If not, we can't do any better than + ALIGN. */ + if (TREE_CODE (TREE_OPERAND (exp, 1)) != INTEGER_CST) + return align; + + while (((TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)) * BITS_PER_UNIT) + & (max_align - 1)) + != 0) + max_align >>= 1; + + exp = TREE_OPERAND (exp, 0); + break; + + case ADDR_EXPR: + /* See what we are pointing at and look at its alignment. */ + exp = TREE_OPERAND (exp, 0); + if (TREE_CODE (exp) == FUNCTION_DECL) + align = MAX (align, FUNCTION_BOUNDARY); + else if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'd') + align = MAX (align, DECL_ALIGN (exp)); +#ifdef CONSTANT_ALIGNMENT + else if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'c') + align = CONSTANT_ALIGNMENT (exp, align); +#endif + return MIN (align, max_align); + + default: + return align; + } + } +} + +/* Return the tree node and offset if a given argument corresponds to + a string constant. */ + +static tree +string_constant (arg, ptr_offset) + tree arg; + tree *ptr_offset; +{ + STRIP_NOPS (arg); + + if (TREE_CODE (arg) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (arg, 0)) == STRING_CST) + { + *ptr_offset = integer_zero_node; + return TREE_OPERAND (arg, 0); + } + else if (TREE_CODE (arg) == PLUS_EXPR) + { + tree arg0 = TREE_OPERAND (arg, 0); + tree arg1 = TREE_OPERAND (arg, 1); + + STRIP_NOPS (arg0); + STRIP_NOPS (arg1); + + if (TREE_CODE (arg0) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (arg0, 0)) == STRING_CST) + { + *ptr_offset = arg1; + return TREE_OPERAND (arg0, 0); + } + else if (TREE_CODE (arg1) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (arg1, 0)) == STRING_CST) + { + *ptr_offset = arg0; + return TREE_OPERAND (arg1, 0); + } + } + + return 0; +} + +/* Compute the length of a C string. TREE_STRING_LENGTH is not the right + way, because it could contain a zero byte in the middle. + TREE_STRING_LENGTH is the size of the character array, not the string. + + Unfortunately, string_constant can't access the values of const char + arrays with initializers, so neither can we do so here. */ + +static tree +c_strlen (src) + tree src; +{ + tree offset_node; + int offset, max; + char *ptr; + + src = string_constant (src, &offset_node); + if (src == 0) + return 0; + max = TREE_STRING_LENGTH (src); + ptr = TREE_STRING_POINTER (src); + if (offset_node && TREE_CODE (offset_node) != INTEGER_CST) + { + /* If the string has an internal zero byte (e.g., "foo\0bar"), we can't + compute the offset to the following null if we don't know where to + start searching for it. */ + int i; + for (i = 0; i < max; i++) + if (ptr[i] == 0) + return 0; + /* We don't know the starting offset, but we do know that the string + has no internal zero bytes. We can assume that the offset falls + within the bounds of the string; otherwise, the programmer deserves + what he gets. Subtract the offset from the length of the string, + and return that. */ + /* This would perhaps not be valid if we were dealing with named + arrays in addition to literal string constants. */ + return size_binop (MINUS_EXPR, size_int (max), offset_node); + } + + /* We have a known offset into the string. Start searching there for + a null character. */ + if (offset_node == 0) + offset = 0; + else + { + /* Did we get a long long offset? If so, punt. */ + if (TREE_INT_CST_HIGH (offset_node) != 0) + return 0; + offset = TREE_INT_CST_LOW (offset_node); + } + /* If the offset is known to be out of bounds, warn, and call strlen at + runtime. */ + if (offset < 0 || offset > max) + { + warning ("offset outside bounds of constant string"); + return 0; + } + /* Use strlen to search for the first zero byte. Since any strings + constructed with build_string will have nulls appended, we win even + if we get handed something like (char[4])"abcd". + + Since OFFSET is our starting index into the string, no further + calculation is needed. */ + return size_int (strlen (ptr + offset)); +} + +/* Expand an expression EXP that calls a built-in function, + with result going to TARGET if that's convenient + (and in mode MODE if that's convenient). + SUBTARGET may be used as the target for computing one of EXP's operands. + IGNORE is nonzero if the value is to be ignored. */ + +static rtx +expand_builtin (exp, target, subtarget, mode, ignore) + tree exp; + rtx target; + rtx subtarget; + enum machine_mode mode; + int ignore; +{ + tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); + tree arglist = TREE_OPERAND (exp, 1); + rtx op0; + rtx lab1, insns; + enum machine_mode value_mode = TYPE_MODE (TREE_TYPE (exp)); + optab builtin_optab; + + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_ABS: + case BUILT_IN_LABS: + case BUILT_IN_FABS: + /* build_function_call changes these into ABS_EXPR. */ + abort (); + + case BUILT_IN_SIN: + case BUILT_IN_COS: + case BUILT_IN_FSQRT: + /* If not optimizing, call the library function. */ + if (! optimize) + break; + + if (arglist == 0 + /* Arg could be wrong type if user redeclared this fcn wrong. */ + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != REAL_TYPE) + return CONST0_RTX (TYPE_MODE (TREE_TYPE (exp))); + + /* Stabilize and compute the argument. */ + if (TREE_CODE (TREE_VALUE (arglist)) != VAR_DECL + && TREE_CODE (TREE_VALUE (arglist)) != PARM_DECL) + { + exp = copy_node (exp); + arglist = copy_node (arglist); + TREE_OPERAND (exp, 1) = arglist; + TREE_VALUE (arglist) = save_expr (TREE_VALUE (arglist)); + } + op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0); + + /* Make a suitable register to place result in. */ + target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp))); + + emit_queue (); + start_sequence (); + + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_SIN: + builtin_optab = sin_optab; break; + case BUILT_IN_COS: + builtin_optab = cos_optab; break; + case BUILT_IN_FSQRT: + builtin_optab = sqrt_optab; break; + default: + abort (); + } + + /* Compute into TARGET. + Set TARGET to wherever the result comes back. */ + target = expand_unop (TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))), + builtin_optab, op0, target, 0); + + /* If we were unable to expand via the builtin, stop the + sequence (without outputting the insns) and break, causing + a call the the library function. */ + if (target == 0) + { + end_sequence (); + break; + } + + /* Check the results by default. But if flag_fast_math is turned on, + then assume sqrt will always be called with valid arguments. */ + + if (! flag_fast_math) + { + /* Don't define the builtin FP instructions + if your machine is not IEEE. */ + if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT) + abort (); + + lab1 = gen_label_rtx (); + + /* Test the result; if it is NaN, set errno=EDOM because + the argument was not in the domain. */ + emit_cmp_insn (target, target, EQ, 0, GET_MODE (target), 0, 0); + emit_jump_insn (gen_beq (lab1)); + +#if TARGET_EDOM + { +#ifdef GEN_ERRNO_RTX + rtx errno_rtx = GEN_ERRNO_RTX; +#else + rtx errno_rtx + = gen_rtx (MEM, word_mode, gen_rtx (SYMBOL_REF, Pmode, "*errno")); +#endif + + emit_move_insn (errno_rtx, GEN_INT (TARGET_EDOM)); + } +#else + /* We can't set errno=EDOM directly; let the library call do it. + Pop the arguments right away in case the call gets deleted. */ + NO_DEFER_POP; + expand_call (exp, target, 0); + OK_DEFER_POP; +#endif + + emit_label (lab1); + } + + /* Output the entire sequence. */ + insns = get_insns (); + end_sequence (); + emit_insns (insns); + + return target; + + /* __builtin_apply_args returns block of memory allocated on + the stack into which is stored the arg pointer, structure + value address, static chain, and all the registers that might + possibly be used in performing a function call. The code is + moved to the start of the function so the incoming values are + saved. */ + case BUILT_IN_APPLY_ARGS: + /* Don't do __builtin_apply_args more than once in a function. + Save the result of the first call and reuse it. */ + if (apply_args_value != 0) + return apply_args_value; + { + /* When this function is called, it means that registers must be + saved on entry to this function. So we migrate the + call to the first insn of this function. */ + rtx temp; + rtx seq; + + start_sequence (); + temp = expand_builtin_apply_args (); + seq = get_insns (); + end_sequence (); + + apply_args_value = temp; + + /* Put the sequence after the NOTE that starts the function. + If this is inside a SEQUENCE, make the outer-level insn + chain current, so the code is placed at the start of the + function. */ + push_topmost_sequence (); + emit_insns_before (seq, NEXT_INSN (get_insns ())); + pop_topmost_sequence (); + return temp; + } + + /* __builtin_apply (FUNCTION, ARGUMENTS, ARGSIZE) invokes + FUNCTION with a copy of the parameters described by + ARGUMENTS, and ARGSIZE. It returns a block of memory + allocated on the stack into which is stored all the registers + that might possibly be used for returning the result of a + function. ARGUMENTS is the value returned by + __builtin_apply_args. ARGSIZE is the number of bytes of + arguments that must be copied. ??? How should this value be + computed? We'll also need a safe worst case value for varargs + functions. */ + case BUILT_IN_APPLY: + if (arglist == 0 + /* Arg could be non-pointer if user redeclared this fcn wrong. */ + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE + || TREE_CHAIN (arglist) == 0 + || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE + || TREE_CHAIN (TREE_CHAIN (arglist)) == 0 + || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))))) != INTEGER_TYPE) + return const0_rtx; + else + { + int i; + tree t; + rtx ops[3]; + + for (t = arglist, i = 0; t; t = TREE_CHAIN (t), i++) + ops[i] = expand_expr (TREE_VALUE (t), NULL_RTX, VOIDmode, 0); + + return expand_builtin_apply (ops[0], ops[1], ops[2]); + } + + /* __builtin_return (RESULT) causes the function to return the + value described by RESULT. RESULT is address of the block of + memory returned by __builtin_apply. */ + case BUILT_IN_RETURN: + if (arglist + /* Arg could be non-pointer if user redeclared this fcn wrong. */ + && TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) == POINTER_TYPE) + expand_builtin_return (expand_expr (TREE_VALUE (arglist), + NULL_RTX, VOIDmode, 0)); + return const0_rtx; + + case BUILT_IN_SAVEREGS: + /* Don't do __builtin_saveregs more than once in a function. + Save the result of the first call and reuse it. */ + if (saveregs_value != 0) + return saveregs_value; + { + /* When this function is called, it means that registers must be + saved on entry to this function. So we migrate the + call to the first insn of this function. */ + rtx temp; + rtx seq; + rtx valreg, saved_valreg; + + /* Now really call the function. `expand_call' does not call + expand_builtin, so there is no danger of infinite recursion here. */ + start_sequence (); + +#ifdef EXPAND_BUILTIN_SAVEREGS + /* Do whatever the machine needs done in this case. */ + temp = EXPAND_BUILTIN_SAVEREGS (arglist); +#else + /* The register where the function returns its value + is likely to have something else in it, such as an argument. + So preserve that register around the call. */ + if (value_mode != VOIDmode) + { + valreg = hard_libcall_value (value_mode); + saved_valreg = gen_reg_rtx (value_mode); + emit_move_insn (saved_valreg, valreg); + } + + /* Generate the call, putting the value in a pseudo. */ + temp = expand_call (exp, target, ignore); + + if (value_mode != VOIDmode) + emit_move_insn (valreg, saved_valreg); +#endif + + seq = get_insns (); + end_sequence (); + + saveregs_value = temp; + + /* Put the sequence after the NOTE that starts the function. + If this is inside a SEQUENCE, make the outer-level insn + chain current, so the code is placed at the start of the + function. */ + push_topmost_sequence (); + emit_insns_before (seq, NEXT_INSN (get_insns ())); + pop_topmost_sequence (); + return temp; + } + + /* __builtin_args_info (N) returns word N of the arg space info + for the current function. The number and meanings of words + is controlled by the definition of CUMULATIVE_ARGS. */ + case BUILT_IN_ARGS_INFO: + { + int nwords = sizeof (CUMULATIVE_ARGS) / sizeof (int); + int i; + int *word_ptr = (int *) ¤t_function_args_info; + tree type, elts, result; + + if (sizeof (CUMULATIVE_ARGS) % sizeof (int) != 0) + fatal ("CUMULATIVE_ARGS type defined badly; see %s, line %d", + __FILE__, __LINE__); + + if (arglist != 0) + { + tree arg = TREE_VALUE (arglist); + if (TREE_CODE (arg) != INTEGER_CST) + error ("argument of `__builtin_args_info' must be constant"); + else + { + int wordnum = TREE_INT_CST_LOW (arg); + + if (wordnum < 0 || wordnum >= nwords || TREE_INT_CST_HIGH (arg)) + error ("argument of `__builtin_args_info' out of range"); + else + return GEN_INT (word_ptr[wordnum]); + } + } + else + error ("missing argument in `__builtin_args_info'"); + + return const0_rtx; + +#if 0 + for (i = 0; i < nwords; i++) + elts = tree_cons (NULL_TREE, build_int_2 (word_ptr[i], 0)); + + type = build_array_type (integer_type_node, + build_index_type (build_int_2 (nwords, 0))); + result = build (CONSTRUCTOR, type, NULL_TREE, nreverse (elts)); + TREE_CONSTANT (result) = 1; + TREE_STATIC (result) = 1; + result = build (INDIRECT_REF, build_pointer_type (type), result); + TREE_CONSTANT (result) = 1; + return expand_expr (result, NULL_RTX, VOIDmode, 0); +#endif + } + + /* Return the address of the first anonymous stack arg. */ + case BUILT_IN_NEXT_ARG: + { + tree fntype = TREE_TYPE (current_function_decl); + if (!(TYPE_ARG_TYPES (fntype) != 0 + && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) + != void_type_node))) + { + error ("`va_start' used in function with fixed args"); + return const0_rtx; + } + } + + return expand_binop (Pmode, add_optab, + current_function_internal_arg_pointer, + current_function_arg_offset_rtx, + NULL_RTX, 0, OPTAB_LIB_WIDEN); + + case BUILT_IN_CLASSIFY_TYPE: + if (arglist != 0) + { + tree type = TREE_TYPE (TREE_VALUE (arglist)); + enum tree_code code = TREE_CODE (type); + if (code == VOID_TYPE) + return GEN_INT (void_type_class); + if (code == INTEGER_TYPE) + return GEN_INT (integer_type_class); + if (code == CHAR_TYPE) + return GEN_INT (char_type_class); + if (code == ENUMERAL_TYPE) + return GEN_INT (enumeral_type_class); + if (code == BOOLEAN_TYPE) + return GEN_INT (boolean_type_class); + if (code == POINTER_TYPE) + return GEN_INT (pointer_type_class); + if (code == REFERENCE_TYPE) + return GEN_INT (reference_type_class); + if (code == OFFSET_TYPE) + return GEN_INT (offset_type_class); + if (code == REAL_TYPE) + return GEN_INT (real_type_class); + if (code == COMPLEX_TYPE) + return GEN_INT (complex_type_class); + if (code == FUNCTION_TYPE) + return GEN_INT (function_type_class); + if (code == METHOD_TYPE) + return GEN_INT (method_type_class); + if (code == RECORD_TYPE) + return GEN_INT (record_type_class); + if (code == UNION_TYPE || code == QUAL_UNION_TYPE) + return GEN_INT (union_type_class); + if (code == ARRAY_TYPE) + return GEN_INT (array_type_class); + if (code == STRING_TYPE) + return GEN_INT (string_type_class); + if (code == SET_TYPE) + return GEN_INT (set_type_class); + if (code == FILE_TYPE) + return GEN_INT (file_type_class); + if (code == LANG_TYPE) + return GEN_INT (lang_type_class); + } + return GEN_INT (no_type_class); + + case BUILT_IN_CONSTANT_P: + if (arglist == 0) + return const0_rtx; + else + return (TREE_CODE_CLASS (TREE_CODE (TREE_VALUE (arglist))) == 'c' + ? const1_rtx : const0_rtx); + + case BUILT_IN_FRAME_ADDRESS: + /* The argument must be a nonnegative integer constant. + It counts the number of frames to scan up the stack. + The value is the address of that frame. */ + case BUILT_IN_RETURN_ADDRESS: + /* The argument must be a nonnegative integer constant. + It counts the number of frames to scan up the stack. + The value is the return address saved in that frame. */ + if (arglist == 0) + /* Warning about missing arg was already issued. */ + return const0_rtx; + else if (TREE_CODE (TREE_VALUE (arglist)) != INTEGER_CST) + { + error ("invalid arg to `__builtin_return_address'"); + return const0_rtx; + } + else if (tree_int_cst_lt (TREE_VALUE (arglist), integer_zero_node)) + { + error ("invalid arg to `__builtin_return_address'"); + return const0_rtx; + } + else + { + int count = TREE_INT_CST_LOW (TREE_VALUE (arglist)); + rtx tem = frame_pointer_rtx; + int i; + + /* Some machines need special handling before we can access arbitrary + frames. For example, on the sparc, we must first flush all + register windows to the stack. */ +#ifdef SETUP_FRAME_ADDRESSES + SETUP_FRAME_ADDRESSES (); +#endif + + /* On the sparc, the return address is not in the frame, it is + in a register. There is no way to access it off of the current + frame pointer, but it can be accessed off the previous frame + pointer by reading the value from the register window save + area. */ +#ifdef RETURN_ADDR_IN_PREVIOUS_FRAME + if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_RETURN_ADDRESS) + count--; +#endif + + /* Scan back COUNT frames to the specified frame. */ + for (i = 0; i < count; i++) + { + /* Assume the dynamic chain pointer is in the word that + the frame address points to, unless otherwise specified. */ +#ifdef DYNAMIC_CHAIN_ADDRESS + tem = DYNAMIC_CHAIN_ADDRESS (tem); +#endif + tem = memory_address (Pmode, tem); + tem = copy_to_reg (gen_rtx (MEM, Pmode, tem)); + } + + /* For __builtin_frame_address, return what we've got. */ + if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS) + return tem; + + /* For __builtin_return_address, + Get the return address from that frame. */ +#ifdef RETURN_ADDR_RTX + return RETURN_ADDR_RTX (count, tem); +#else + tem = memory_address (Pmode, + plus_constant (tem, GET_MODE_SIZE (Pmode))); + return copy_to_reg (gen_rtx (MEM, Pmode, tem)); +#endif + } + + case BUILT_IN_ALLOCA: + if (arglist == 0 + /* Arg could be non-integer if user redeclared this fcn wrong. */ + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE) + return const0_rtx; + current_function_calls_alloca = 1; + /* Compute the argument. */ + op0 = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0); + + /* Allocate the desired space. */ + target = allocate_dynamic_stack_space (op0, target, BITS_PER_UNIT); + + /* Record the new stack level for nonlocal gotos. */ + if (nonlocal_goto_handler_slot != 0) + emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX); + return target; + + case BUILT_IN_FFS: + /* If not optimizing, call the library function. */ + if (!optimize) + break; + + if (arglist == 0 + /* Arg could be non-integer if user redeclared this fcn wrong. */ + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE) + return const0_rtx; + + /* Compute the argument. */ + op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0); + /* Compute ffs, into TARGET if possible. + Set TARGET to wherever the result comes back. */ + target = expand_unop (TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))), + ffs_optab, op0, target, 1); + if (target == 0) + abort (); + return target; + + case BUILT_IN_STRLEN: + /* If not optimizing, call the library function. */ + if (!optimize) + break; + + if (arglist == 0 + /* Arg could be non-pointer if user redeclared this fcn wrong. */ + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE) + return const0_rtx; + else + { + tree src = TREE_VALUE (arglist); + tree len = c_strlen (src); + + int align + = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; + + rtx result, src_rtx, char_rtx; + enum machine_mode insn_mode = value_mode, char_mode; + enum insn_code icode; + + /* If the length is known, just return it. */ + if (len != 0) + return expand_expr (len, target, mode, 0); + + /* If SRC is not a pointer type, don't do this operation inline. */ + if (align == 0) + break; + + /* Call a function if we can't compute strlen in the right mode. */ + + while (insn_mode != VOIDmode) + { + icode = strlen_optab->handlers[(int) insn_mode].insn_code; + if (icode != CODE_FOR_nothing) + break; + + insn_mode = GET_MODE_WIDER_MODE (insn_mode); + } + if (insn_mode == VOIDmode) + break; + + /* Make a place to write the result of the instruction. */ + result = target; + if (! (result != 0 + && GET_CODE (result) == REG + && GET_MODE (result) == insn_mode + && REGNO (result) >= FIRST_PSEUDO_REGISTER)) + result = gen_reg_rtx (insn_mode); + + /* Make sure the operands are acceptable to the predicates. */ + + if (! (*insn_operand_predicate[(int)icode][0]) (result, insn_mode)) + result = gen_reg_rtx (insn_mode); + + src_rtx = memory_address (BLKmode, + expand_expr (src, NULL_RTX, Pmode, + EXPAND_NORMAL)); + if (! (*insn_operand_predicate[(int)icode][1]) (src_rtx, Pmode)) + src_rtx = copy_to_mode_reg (Pmode, src_rtx); + + char_rtx = const0_rtx; + char_mode = insn_operand_mode[(int)icode][2]; + if (! (*insn_operand_predicate[(int)icode][2]) (char_rtx, char_mode)) + char_rtx = copy_to_mode_reg (char_mode, char_rtx); + + emit_insn (GEN_FCN (icode) (result, + gen_rtx (MEM, BLKmode, src_rtx), + char_rtx, GEN_INT (align))); + + /* Return the value in the proper mode for this function. */ + if (GET_MODE (result) == value_mode) + return result; + else if (target != 0) + { + convert_move (target, result, 0); + return target; + } + else + return convert_to_mode (value_mode, result, 0); + } + + case BUILT_IN_STRCPY: + /* If not optimizing, call the library function. */ + if (!optimize) + break; + + if (arglist == 0 + /* Arg could be non-pointer if user redeclared this fcn wrong. */ + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE + || TREE_CHAIN (arglist) == 0 + || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE) + return const0_rtx; + else + { + tree len = c_strlen (TREE_VALUE (TREE_CHAIN (arglist))); + + if (len == 0) + break; + + len = size_binop (PLUS_EXPR, len, integer_one_node); + + chainon (arglist, build_tree_list (NULL_TREE, len)); + } + + /* Drops in. */ + case BUILT_IN_MEMCPY: + /* If not optimizing, call the library function. */ + if (!optimize) + break; + + if (arglist == 0 + /* Arg could be non-pointer if user redeclared this fcn wrong. */ + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE + || TREE_CHAIN (arglist) == 0 + || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE + || TREE_CHAIN (TREE_CHAIN (arglist)) == 0 + || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))))) != INTEGER_TYPE) + return const0_rtx; + else + { + tree dest = TREE_VALUE (arglist); + tree src = TREE_VALUE (TREE_CHAIN (arglist)); + tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + int src_align + = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; + int dest_align + = get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; + rtx dest_rtx, dest_mem, src_mem; + + /* If either SRC or DEST is not a pointer type, don't do + this operation in-line. */ + if (src_align == 0 || dest_align == 0) + { + if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_STRCPY) + TREE_CHAIN (TREE_CHAIN (arglist)) = 0; + break; + } + + dest_rtx = expand_expr (dest, NULL_RTX, Pmode, EXPAND_NORMAL); + dest_mem = gen_rtx (MEM, BLKmode, + memory_address (BLKmode, dest_rtx)); + src_mem = gen_rtx (MEM, BLKmode, + memory_address (BLKmode, + expand_expr (src, NULL_RTX, + Pmode, + EXPAND_NORMAL))); + + /* Copy word part most expediently. */ + emit_block_move (dest_mem, src_mem, + expand_expr (len, NULL_RTX, VOIDmode, 0), + MIN (src_align, dest_align)); + return dest_rtx; + } + +/* These comparison functions need an instruction that returns an actual + index. An ordinary compare that just sets the condition codes + is not enough. */ +#ifdef HAVE_cmpstrsi + case BUILT_IN_STRCMP: + /* If not optimizing, call the library function. */ + if (!optimize) + break; + + if (arglist == 0 + /* Arg could be non-pointer if user redeclared this fcn wrong. */ + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE + || TREE_CHAIN (arglist) == 0 + || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE) + return const0_rtx; + else if (!HAVE_cmpstrsi) + break; + { + tree arg1 = TREE_VALUE (arglist); + tree arg2 = TREE_VALUE (TREE_CHAIN (arglist)); + tree offset; + tree len, len2; + + len = c_strlen (arg1); + if (len) + len = size_binop (PLUS_EXPR, integer_one_node, len); + len2 = c_strlen (arg2); + if (len2) + len2 = size_binop (PLUS_EXPR, integer_one_node, len2); + + /* If we don't have a constant length for the first, use the length + of the second, if we know it. We don't require a constant for + this case; some cost analysis could be done if both are available + but neither is constant. For now, assume they're equally cheap. + + If both strings have constant lengths, use the smaller. This + could arise if optimization results in strcpy being called with + two fixed strings, or if the code was machine-generated. We should + add some code to the `memcmp' handler below to deal with such + situations, someday. */ + if (!len || TREE_CODE (len) != INTEGER_CST) + { + if (len2) + len = len2; + else if (len == 0) + break; + } + else if (len2 && TREE_CODE (len2) == INTEGER_CST) + { + if (tree_int_cst_lt (len2, len)) + len = len2; + } + + chainon (arglist, build_tree_list (NULL_TREE, len)); + } + + /* Drops in. */ + case BUILT_IN_MEMCMP: + /* If not optimizing, call the library function. */ + if (!optimize) + break; + + if (arglist == 0 + /* Arg could be non-pointer if user redeclared this fcn wrong. */ + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE + || TREE_CHAIN (arglist) == 0 + || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE + || TREE_CHAIN (TREE_CHAIN (arglist)) == 0 + || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))))) != INTEGER_TYPE) + return const0_rtx; + else if (!HAVE_cmpstrsi) + break; + { + tree arg1 = TREE_VALUE (arglist); + tree arg2 = TREE_VALUE (TREE_CHAIN (arglist)); + tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + rtx result; + + int arg1_align + = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; + int arg2_align + = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT; + enum machine_mode insn_mode + = insn_operand_mode[(int) CODE_FOR_cmpstrsi][0]; + + /* If we don't have POINTER_TYPE, call the function. */ + if (arg1_align == 0 || arg2_align == 0) + { + if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_STRCMP) + TREE_CHAIN (TREE_CHAIN (arglist)) = 0; + break; + } + + /* Make a place to write the result of the instruction. */ + result = target; + if (! (result != 0 + && GET_CODE (result) == REG && GET_MODE (result) == insn_mode + && REGNO (result) >= FIRST_PSEUDO_REGISTER)) + result = gen_reg_rtx (insn_mode); + + emit_insn (gen_cmpstrsi (result, + gen_rtx (MEM, BLKmode, + expand_expr (arg1, NULL_RTX, Pmode, + EXPAND_NORMAL)), + gen_rtx (MEM, BLKmode, + expand_expr (arg2, NULL_RTX, Pmode, + EXPAND_NORMAL)), + expand_expr (len, NULL_RTX, VOIDmode, 0), + GEN_INT (MIN (arg1_align, arg2_align)))); + + /* Return the value in the proper mode for this function. */ + mode = TYPE_MODE (TREE_TYPE (exp)); + if (GET_MODE (result) == mode) + return result; + else if (target != 0) + { + convert_move (target, result, 0); + return target; + } + else + return convert_to_mode (mode, result, 0); + } +#else + case BUILT_IN_STRCMP: + case BUILT_IN_MEMCMP: + break; +#endif + + default: /* just do library call, if unknown builtin */ + error ("built-in function `%s' not currently supported", + IDENTIFIER_POINTER (DECL_NAME (fndecl))); + } + + /* The switch statement above can drop through to cause the function + to be called normally. */ + + return expand_call (exp, target, ignore); +} + +/* Built-in functions to perform an untyped call and return. */ + +/* For each register that may be used for calling a function, this + gives a mode used to copy the register's value. VOIDmode indicates + the register is not used for calling a function. If the machine + has register windows, this gives only the outbound registers. + INCOMING_REGNO gives the corresponding inbound register. */ +static enum machine_mode apply_args_mode[FIRST_PSEUDO_REGISTER]; + +/* For each register that may be used for returning values, this gives + a mode used to copy the register's value. VOIDmode indicates the + register is not used for returning values. If the machine has + register windows, this gives only the outbound registers. + INCOMING_REGNO gives the corresponding inbound register. */ +static enum machine_mode apply_result_mode[FIRST_PSEUDO_REGISTER]; + +/* Return the size required for the block returned by __builtin_apply_args, + and initialize apply_args_mode. */ +static int +apply_args_size () +{ + static int size = -1; + int align, regno; + enum machine_mode mode; + + /* The values computed by this function never change. */ + if (size < 0) + { + /* The first value is the incoming arg-pointer. */ + size = GET_MODE_SIZE (Pmode); + + /* The second value is the structure value address unless this is + passed as an "invisible" first argument. */ + if (struct_value_rtx) + size += GET_MODE_SIZE (Pmode); + + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (FUNCTION_ARG_REGNO_P (regno)) + { + /* Search for the proper mode for copying this register's + value. I'm not sure this is right, but it works so far. */ + enum machine_mode best_mode = VOIDmode; + + for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); + mode != VOIDmode; + mode = GET_MODE_WIDER_MODE (mode)) + if (HARD_REGNO_MODE_OK (regno, mode) + && HARD_REGNO_NREGS (regno, mode) == 1) + best_mode = mode; + + if (best_mode == VOIDmode) + for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); + mode != VOIDmode; + mode = GET_MODE_WIDER_MODE (mode)) + if (HARD_REGNO_MODE_OK (regno, mode) + && (mov_optab->handlers[(int) mode].insn_code + != CODE_FOR_nothing)) + best_mode = mode; + + mode = best_mode; + if (mode == VOIDmode) + abort (); + + align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; + if (size % align != 0) + size = CEIL (size, align) * align; + size += GET_MODE_SIZE (mode); + apply_args_mode[regno] = mode; + } + else + apply_args_mode[regno] = VOIDmode; + } + return size; +} + +/* Return the size required for the block returned by __builtin_apply, + and initialize apply_result_mode. */ +static int +apply_result_size () +{ + static int size = -1; + int align, regno; + enum machine_mode mode; + + /* The values computed by this function never change. */ + if (size < 0) + { + size = 0; + + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (FUNCTION_VALUE_REGNO_P (regno)) + { + /* Search for the proper mode for copying this register's + value. I'm not sure this is right, but it works so far. */ + enum machine_mode best_mode = VOIDmode; + + for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); + mode != TImode; + mode = GET_MODE_WIDER_MODE (mode)) + if (HARD_REGNO_MODE_OK (regno, mode)) + best_mode = mode; + + if (best_mode == VOIDmode) + for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); + mode != VOIDmode; + mode = GET_MODE_WIDER_MODE (mode)) + if (HARD_REGNO_MODE_OK (regno, mode) + && (mov_optab->handlers[(int) mode].insn_code + != CODE_FOR_nothing)) + best_mode = mode; + + mode = best_mode; + if (mode == VOIDmode) + abort (); + + align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; + if (size % align != 0) + size = CEIL (size, align) * align; + size += GET_MODE_SIZE (mode); + apply_result_mode[regno] = mode; + } + else + apply_result_mode[regno] = VOIDmode; + + /* Allow targets that use untyped_call and untyped_return to override + the size so that machine-specific information can be stored here. */ +#ifdef APPLY_RESULT_SIZE + size = APPLY_RESULT_SIZE; +#endif + } + return size; +} + +#if defined (HAVE_untyped_call) || defined (HAVE_untyped_return) +/* Create a vector describing the result block RESULT. If SAVEP is true, + the result block is used to save the values; otherwise it is used to + restore the values. */ +static rtx +result_vector (savep, result) + int savep; + rtx result; +{ + int regno, size, align, nelts; + enum machine_mode mode; + rtx reg, mem; + rtx *savevec = (rtx *) alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx)); + + size = nelts = 0; + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if ((mode = apply_result_mode[regno]) != VOIDmode) + { + align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; + if (size % align != 0) + size = CEIL (size, align) * align; + reg = gen_rtx (REG, mode, savep ? INCOMING_REGNO (regno) : regno); + mem = change_address (result, mode, + plus_constant (XEXP (result, 0), size)); + savevec[nelts++] = (savep + ? gen_rtx (SET, VOIDmode, mem, reg) + : gen_rtx (SET, VOIDmode, reg, mem)); + size += GET_MODE_SIZE (mode); + } + return gen_rtx (PARALLEL, VOIDmode, gen_rtvec_v (nelts, savevec)); +} +#endif /* HAVE_untyped_call or HAVE_untyped_return */ + + +/* Save the state required to perform an untyped call with the same + arguments as were passed to the current function. */ +static rtx +expand_builtin_apply_args () +{ + rtx registers; + int size, align, regno; + enum machine_mode mode; + + /* Create a block where the arg-pointer, structure value address, + and argument registers can be saved. */ + registers = assign_stack_local (BLKmode, apply_args_size (), -1); + + /* Walk past the arg-pointer and structure value address. */ + size = GET_MODE_SIZE (Pmode); + if (struct_value_rtx) + size += GET_MODE_SIZE (Pmode); + + /* Save each register used in calling a function to the block. */ + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if ((mode = apply_args_mode[regno]) != VOIDmode) + { + align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; + if (size % align != 0) + size = CEIL (size, align) * align; + emit_move_insn (change_address (registers, mode, + plus_constant (XEXP (registers, 0), + size)), + gen_rtx (REG, mode, INCOMING_REGNO (regno))); + size += GET_MODE_SIZE (mode); + } + + /* Save the arg pointer to the block. */ + emit_move_insn (change_address (registers, Pmode, XEXP (registers, 0)), + copy_to_reg (virtual_incoming_args_rtx)); + size = GET_MODE_SIZE (Pmode); + + /* Save the structure value address unless this is passed as an + "invisible" first argument. */ + if (struct_value_incoming_rtx) + { + emit_move_insn (change_address (registers, Pmode, + plus_constant (XEXP (registers, 0), + size)), + copy_to_reg (struct_value_incoming_rtx)); + size += GET_MODE_SIZE (Pmode); + } + + /* Return the address of the block. */ + return copy_addr_to_reg (XEXP (registers, 0)); +} + +/* Perform an untyped call and save the state required to perform an + untyped return of whatever value was returned by the given function. */ +static rtx +expand_builtin_apply (function, arguments, argsize) + rtx function, arguments, argsize; +{ + int size, align, regno; + enum machine_mode mode; + rtx incoming_args, result, reg, dest, call_insn; + rtx old_stack_level = 0; + rtx use_insns = 0; + + /* Create a block where the return registers can be saved. */ + result = assign_stack_local (BLKmode, apply_result_size (), -1); + + /* ??? The argsize value should be adjusted here. */ + + /* Fetch the arg pointer from the ARGUMENTS block. */ + incoming_args = gen_reg_rtx (Pmode); + emit_move_insn (incoming_args, + gen_rtx (MEM, Pmode, arguments)); +#ifndef STACK_GROWS_DOWNWARD + incoming_args = expand_binop (Pmode, add_optab, incoming_args, argsize, + incoming_args, 0, OPTAB_LIB_WIDEN); +#endif + + /* Perform postincrements before actually calling the function. */ + emit_queue (); + + /* Push a new argument block and copy the arguments. */ + do_pending_stack_adjust (); + emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); + + /* Push a block of memory onto the stack to store the memory arguments. + Save the address in a register, and copy the memory arguments. ??? I + haven't figured out how the calling convention macros effect this, + but it's likely that the source and/or destination addresses in + the block copy will need updating in machine specific ways. */ + dest = copy_addr_to_reg (push_block (argsize, 0, 0)); + emit_block_move (gen_rtx (MEM, BLKmode, dest), + gen_rtx (MEM, BLKmode, incoming_args), + argsize, + PARM_BOUNDARY / BITS_PER_UNIT); + + /* Refer to the argument block. */ + apply_args_size (); + arguments = gen_rtx (MEM, BLKmode, arguments); + + /* Walk past the arg-pointer and structure value address. */ + size = GET_MODE_SIZE (Pmode); + if (struct_value_rtx) + size += GET_MODE_SIZE (Pmode); + + /* Restore each of the registers previously saved. Make USE insns + for each of these registers for use in making the call. */ + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if ((mode = apply_args_mode[regno]) != VOIDmode) + { + align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; + if (size % align != 0) + size = CEIL (size, align) * align; + reg = gen_rtx (REG, mode, regno); + emit_move_insn (reg, + change_address (arguments, mode, + plus_constant (XEXP (arguments, 0), + size))); + + push_to_sequence (use_insns); + emit_insn (gen_rtx (USE, VOIDmode, reg)); + use_insns = get_insns (); + end_sequence (); + size += GET_MODE_SIZE (mode); + } + + /* Restore the structure value address unless this is passed as an + "invisible" first argument. */ + size = GET_MODE_SIZE (Pmode); + if (struct_value_rtx) + { + rtx value = gen_reg_rtx (Pmode); + emit_move_insn (value, + change_address (arguments, Pmode, + plus_constant (XEXP (arguments, 0), + size))); + emit_move_insn (struct_value_rtx, value); + if (GET_CODE (struct_value_rtx) == REG) + { + push_to_sequence (use_insns); + emit_insn (gen_rtx (USE, VOIDmode, struct_value_rtx)); + use_insns = get_insns (); + end_sequence (); + } + size += GET_MODE_SIZE (Pmode); + } + + /* All arguments and registers used for the call are set up by now! */ + function = prepare_call_address (function, NULL_TREE, &use_insns); + + /* Ensure address is valid. SYMBOL_REF is already valid, so no need, + and we don't want to load it into a register as an optimization, + because prepare_call_address already did it if it should be done. */ + if (GET_CODE (function) != SYMBOL_REF) + function = memory_address (FUNCTION_MODE, function); + + /* Generate the actual call instruction and save the return value. */ +#ifdef HAVE_untyped_call + if (HAVE_untyped_call) + emit_call_insn (gen_untyped_call (gen_rtx (MEM, FUNCTION_MODE, function), + result, result_vector (1, result))); + else +#endif +#ifdef HAVE_call_value + if (HAVE_call_value) + { + rtx valreg = 0; + + /* Locate the unique return register. It is not possible to + express a call that sets more than one return register using + call_value; use untyped_call for that. In fact, untyped_call + only needs to save the return registers in the given block. */ + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if ((mode = apply_result_mode[regno]) != VOIDmode) + { + if (valreg) + abort (); /* HAVE_untyped_call required. */ + valreg = gen_rtx (REG, mode, regno); + } + + emit_call_insn (gen_call_value (valreg, + gen_rtx (MEM, FUNCTION_MODE, function), + const0_rtx, NULL_RTX, const0_rtx)); + + emit_move_insn (change_address (result, GET_MODE (valreg), + XEXP (result, 0)), + valreg); + } + else +#endif + abort (); + + /* Find the CALL insn we just emitted and write the USE insns before it. */ + for (call_insn = get_last_insn (); + call_insn && GET_CODE (call_insn) != CALL_INSN; + call_insn = PREV_INSN (call_insn)) + ; + + if (! call_insn) + abort (); + + /* Put the USE insns before the CALL. */ + emit_insns_before (use_insns, call_insn); + + /* Restore the stack. */ + emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX); + + /* Return the address of the result block. */ + return copy_addr_to_reg (XEXP (result, 0)); +} + +/* Perform an untyped return. */ +static void +expand_builtin_return (result) + rtx result; +{ + int size, align, regno; + enum machine_mode mode; + rtx reg; + rtx use_insns = 0; + + apply_result_size (); + result = gen_rtx (MEM, BLKmode, result); + +#ifdef HAVE_untyped_return + if (HAVE_untyped_return) + { + emit_jump_insn (gen_untyped_return (result, result_vector (0, result))); + emit_barrier (); + return; + } +#endif + + /* Restore the return value and note that each value is used. */ + size = 0; + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if ((mode = apply_result_mode[regno]) != VOIDmode) + { + align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; + if (size % align != 0) + size = CEIL (size, align) * align; + reg = gen_rtx (REG, mode, INCOMING_REGNO (regno)); + emit_move_insn (reg, + change_address (result, mode, + plus_constant (XEXP (result, 0), + size))); + + push_to_sequence (use_insns); + emit_insn (gen_rtx (USE, VOIDmode, reg)); + use_insns = get_insns (); + end_sequence (); + size += GET_MODE_SIZE (mode); + } + + /* Put the USE insns before the return. */ + emit_insns (use_insns); + + /* Return whatever values was restored by jumping directly to the end + of the function. */ + expand_null_return (); +} + +/* Expand code for a post- or pre- increment or decrement + and return the RTX for the result. + POST is 1 for postinc/decrements and 0 for preinc/decrements. */ + +static rtx +expand_increment (exp, post) + register tree exp; + int post; +{ + register rtx op0, op1; + register rtx temp, value; + register tree incremented = TREE_OPERAND (exp, 0); + optab this_optab = add_optab; + int icode; + enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp)); + int op0_is_copy = 0; + + /* Stabilize any component ref that might need to be + evaluated more than once below. */ + if (!post + || TREE_CODE (incremented) == BIT_FIELD_REF + || (TREE_CODE (incremented) == COMPONENT_REF + && (TREE_CODE (TREE_OPERAND (incremented, 0)) != INDIRECT_REF + || DECL_BIT_FIELD (TREE_OPERAND (incremented, 1))))) + incremented = stabilize_reference (incremented); + + /* Compute the operands as RTX. + Note whether OP0 is the actual lvalue or a copy of it: + I believe it is a copy iff it is a register or subreg + and insns were generated in computing it. */ + + temp = get_last_insn (); + op0 = expand_expr (incremented, NULL_RTX, VOIDmode, 0); + + /* If OP0 is a SUBREG made for a promoted variable, we cannot increment + in place but intead must do sign- or zero-extension during assignment, + so we copy it into a new register and let the code below use it as + a copy. + + Note that we can safely modify this SUBREG since it is know not to be + shared (it was made by the expand_expr call above). */ + + if (GET_CODE (op0) == SUBREG && SUBREG_PROMOTED_VAR_P (op0)) + SUBREG_REG (op0) = copy_to_reg (SUBREG_REG (op0)); + + op0_is_copy = ((GET_CODE (op0) == SUBREG || GET_CODE (op0) == REG) + && temp != get_last_insn ()); + op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0); + + /* Decide whether incrementing or decrementing. */ + if (TREE_CODE (exp) == POSTDECREMENT_EXPR + || TREE_CODE (exp) == PREDECREMENT_EXPR) + this_optab = sub_optab; + + /* If OP0 is not the actual lvalue, but rather a copy in a register, + then we cannot just increment OP0. We must therefore contrive to + increment the original value. Then, for postincrement, we can return + OP0 since it is a copy of the old value. For preincrement, we want + to always expand here, since this generates better or equivalent code. */ + if (!post || op0_is_copy) + { + /* This is the easiest way to increment the value wherever it is. + Problems with multiple evaluation of INCREMENTED are prevented + because either (1) it is a component_ref or preincrement, + in which case it was stabilized above, or (2) it is an array_ref + with constant index in an array in a register, which is + safe to reevaluate. */ + tree newexp = build ((this_optab == add_optab + ? PLUS_EXPR : MINUS_EXPR), + TREE_TYPE (exp), + incremented, + TREE_OPERAND (exp, 1)); + temp = expand_assignment (incremented, newexp, ! post, 0); + return post ? op0 : temp; + } + + /* Convert decrement by a constant into a negative increment. */ + if (this_optab == sub_optab + && GET_CODE (op1) == CONST_INT) + { + op1 = GEN_INT (- INTVAL (op1)); + this_optab = add_optab; + } + + if (post) + { + /* We have a true reference to the value in OP0. + If there is an insn to add or subtract in this mode, queue it. */ + +#if 0 /* Turned off to avoid making extra insn for indexed memref. */ + op0 = stabilize (op0); +#endif + + icode = (int) this_optab->handlers[(int) mode].insn_code; + if (icode != (int) CODE_FOR_nothing + /* Make sure that OP0 is valid for operands 0 and 1 + of the insn we want to queue. */ + && (*insn_operand_predicate[icode][0]) (op0, mode) + && (*insn_operand_predicate[icode][1]) (op0, mode)) + { + if (! (*insn_operand_predicate[icode][2]) (op1, mode)) + op1 = force_reg (mode, op1); + + return enqueue_insn (op0, GEN_FCN (icode) (op0, op0, op1)); + } + } + + /* Preincrement, or we can't increment with one simple insn. */ + if (post) + /* Save a copy of the value before inc or dec, to return it later. */ + temp = value = copy_to_reg (op0); + else + /* Arrange to return the incremented value. */ + /* Copy the rtx because expand_binop will protect from the queue, + and the results of that would be invalid for us to return + if our caller does emit_queue before using our result. */ + temp = copy_rtx (value = op0); + + /* Increment however we can. */ + op1 = expand_binop (mode, this_optab, value, op1, op0, + TREE_UNSIGNED (TREE_TYPE (exp)), OPTAB_LIB_WIDEN); + /* Make sure the value is stored into OP0. */ + if (op1 != op0) + emit_move_insn (op0, op1); + + return temp; +} + +/* Expand all function calls contained within EXP, innermost ones first. + But don't look within expressions that have sequence points. + For each CALL_EXPR, record the rtx for its value + in the CALL_EXPR_RTL field. */ + +static void +preexpand_calls (exp) + tree exp; +{ + register int nops, i; + int type = TREE_CODE_CLASS (TREE_CODE (exp)); + + if (! do_preexpand_calls) + return; + + /* Only expressions and references can contain calls. */ + + if (type != 'e' && type != '<' && type != '1' && type != '2' && type != 'r') + return; + + switch (TREE_CODE (exp)) + { + case CALL_EXPR: + /* Do nothing if already expanded. */ + if (CALL_EXPR_RTL (exp) != 0) + return; + + /* Do nothing to built-in functions. */ + if (TREE_CODE (TREE_OPERAND (exp, 0)) != ADDR_EXPR + || TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) != FUNCTION_DECL + || ! DECL_BUILT_IN (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))) + CALL_EXPR_RTL (exp) = expand_call (exp, NULL_RTX, 0); + return; + + case COMPOUND_EXPR: + case COND_EXPR: + case TRUTH_ANDIF_EXPR: + case TRUTH_ORIF_EXPR: + /* If we find one of these, then we can be sure + the adjust will be done for it (since it makes jumps). + Do it now, so that if this is inside an argument + of a function, we don't get the stack adjustment + after some other args have already been pushed. */ + do_pending_stack_adjust (); + return; + + case BLOCK: + case RTL_EXPR: + case WITH_CLEANUP_EXPR: + return; + + case SAVE_EXPR: + if (SAVE_EXPR_RTL (exp) != 0) + return; + } + + nops = tree_code_length[(int) TREE_CODE (exp)]; + for (i = 0; i < nops; i++) + if (TREE_OPERAND (exp, i) != 0) + { + type = TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, i))); + if (type == 'e' || type == '<' || type == '1' || type == '2' + || type == 'r') + preexpand_calls (TREE_OPERAND (exp, i)); + } +} + +/* At the start of a function, record that we have no previously-pushed + arguments waiting to be popped. */ + +void +init_pending_stack_adjust () +{ + pending_stack_adjust = 0; +} + +/* When exiting from function, if safe, clear out any pending stack adjust + so the adjustment won't get done. */ + +void +clear_pending_stack_adjust () +{ +#ifdef EXIT_IGNORE_STACK + if (! flag_omit_frame_pointer && EXIT_IGNORE_STACK + && ! (DECL_INLINE (current_function_decl) && ! flag_no_inline) + && ! flag_inline_functions) + pending_stack_adjust = 0; +#endif +} + +/* Pop any previously-pushed arguments that have not been popped yet. */ + +void +do_pending_stack_adjust () +{ + if (inhibit_defer_pop == 0) + { + if (pending_stack_adjust != 0) + adjust_stack (GEN_INT (pending_stack_adjust)); + pending_stack_adjust = 0; + } +} + +/* Expand all cleanups up to OLD_CLEANUPS. + Needed here, and also for language-dependent calls. */ + +void +expand_cleanups_to (old_cleanups) + tree old_cleanups; +{ + while (cleanups_this_call != old_cleanups) + { + expand_expr (TREE_VALUE (cleanups_this_call), NULL_RTX, VOIDmode, 0); + cleanups_this_call = TREE_CHAIN (cleanups_this_call); + } +} + +/* Expand conditional expressions. */ + +/* Generate code to evaluate EXP and jump to LABEL if the value is zero. + LABEL is an rtx of code CODE_LABEL, in this function and all the + functions here. */ + +void +jumpifnot (exp, label) + tree exp; + rtx label; +{ + do_jump (exp, label, NULL_RTX); +} + +/* Generate code to evaluate EXP and jump to LABEL if the value is nonzero. */ + +void +jumpif (exp, label) + tree exp; + rtx label; +{ + do_jump (exp, NULL_RTX, label); +} + +/* Generate code to evaluate EXP and jump to IF_FALSE_LABEL if + the result is zero, or IF_TRUE_LABEL if the result is one. + Either of IF_FALSE_LABEL and IF_TRUE_LABEL may be zero, + meaning fall through in that case. + + do_jump always does any pending stack adjust except when it does not + actually perform a jump. An example where there is no jump + is when EXP is `(foo (), 0)' and IF_FALSE_LABEL is null. + + This function is responsible for optimizing cases such as + &&, || and comparison operators in EXP. */ + +void +do_jump (exp, if_false_label, if_true_label) + tree exp; + rtx if_false_label, if_true_label; +{ + register enum tree_code code = TREE_CODE (exp); + /* Some cases need to create a label to jump to + in order to properly fall through. + These cases set DROP_THROUGH_LABEL nonzero. */ + rtx drop_through_label = 0; + rtx temp; + rtx comparison = 0; + int i; + tree type; + + emit_queue (); + + switch (code) + { + case ERROR_MARK: + break; + + case INTEGER_CST: + temp = integer_zerop (exp) ? if_false_label : if_true_label; + if (temp) + emit_jump (temp); + break; + +#if 0 + /* This is not true with #pragma weak */ + case ADDR_EXPR: + /* The address of something can never be zero. */ + if (if_true_label) + emit_jump (if_true_label); + break; +#endif + + case NOP_EXPR: + if (TREE_CODE (TREE_OPERAND (exp, 0)) == COMPONENT_REF + || TREE_CODE (TREE_OPERAND (exp, 0)) == BIT_FIELD_REF + || TREE_CODE (TREE_OPERAND (exp, 0)) == ARRAY_REF) + goto normal; + case CONVERT_EXPR: + /* If we are narrowing the operand, we have to do the compare in the + narrower mode. */ + if ((TYPE_PRECISION (TREE_TYPE (exp)) + < TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (exp, 0))))) + goto normal; + case NON_LVALUE_EXPR: + case REFERENCE_EXPR: + case ABS_EXPR: + case NEGATE_EXPR: + case LROTATE_EXPR: + case RROTATE_EXPR: + /* These cannot change zero->non-zero or vice versa. */ + do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label); + break; + +#if 0 + /* This is never less insns than evaluating the PLUS_EXPR followed by + a test and can be longer if the test is eliminated. */ + case PLUS_EXPR: + /* Reduce to minus. */ + exp = build (MINUS_EXPR, TREE_TYPE (exp), + TREE_OPERAND (exp, 0), + fold (build1 (NEGATE_EXPR, TREE_TYPE (TREE_OPERAND (exp, 1)), + TREE_OPERAND (exp, 1)))); + /* Process as MINUS. */ +#endif + + case MINUS_EXPR: + /* Non-zero iff operands of minus differ. */ + comparison = compare (build (NE_EXPR, TREE_TYPE (exp), + TREE_OPERAND (exp, 0), + TREE_OPERAND (exp, 1)), + NE, NE); + break; + + case BIT_AND_EXPR: + /* If we are AND'ing with a small constant, do this comparison in the + smallest type that fits. If the machine doesn't have comparisons + that small, it will be converted back to the wider comparison. + This helps if we are testing the sign bit of a narrower object. + combine can't do this for us because it can't know whether a + ZERO_EXTRACT or a compare in a smaller mode exists, but we do. */ + + if (! SLOW_BYTE_ACCESS + && TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST + && TYPE_PRECISION (TREE_TYPE (exp)) <= HOST_BITS_PER_WIDE_INT + && (i = floor_log2 (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)))) >= 0 + && (type = type_for_size (i + 1, 1)) != 0 + && TYPE_PRECISION (type) < TYPE_PRECISION (TREE_TYPE (exp)) + && (cmp_optab->handlers[(int) TYPE_MODE (type)].insn_code + != CODE_FOR_nothing)) + { + do_jump (convert (type, exp), if_false_label, if_true_label); + break; + } + goto normal; + + case TRUTH_NOT_EXPR: + do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label); + break; + + case TRUTH_ANDIF_EXPR: + if (if_false_label == 0) + if_false_label = drop_through_label = gen_label_rtx (); + do_jump (TREE_OPERAND (exp, 0), if_false_label, NULL_RTX); + do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label); + break; + + case TRUTH_ORIF_EXPR: + if (if_true_label == 0) + if_true_label = drop_through_label = gen_label_rtx (); + do_jump (TREE_OPERAND (exp, 0), NULL_RTX, if_true_label); + do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label); + break; + + case COMPOUND_EXPR: + expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, 0); + free_temp_slots (); + emit_queue (); + do_pending_stack_adjust (); + do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label); + break; + + case COMPONENT_REF: + case BIT_FIELD_REF: + case ARRAY_REF: + { + int bitsize, bitpos, unsignedp; + enum machine_mode mode; + tree type; + tree offset; + int volatilep = 0; + + /* Get description of this reference. We don't actually care + about the underlying object here. */ + get_inner_reference (exp, &bitsize, &bitpos, &offset, + &mode, &unsignedp, &volatilep); + + type = type_for_size (bitsize, unsignedp); + if (! SLOW_BYTE_ACCESS + && type != 0 && bitsize >= 0 + && TYPE_PRECISION (type) < TYPE_PRECISION (TREE_TYPE (exp)) + && (cmp_optab->handlers[(int) TYPE_MODE (type)].insn_code + != CODE_FOR_nothing)) + { + do_jump (convert (type, exp), if_false_label, if_true_label); + break; + } + goto normal; + } + + case COND_EXPR: + /* Do (a ? 1 : 0) and (a ? 0 : 1) as special cases. */ + if (integer_onep (TREE_OPERAND (exp, 1)) + && integer_zerop (TREE_OPERAND (exp, 2))) + do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label); + + else if (integer_zerop (TREE_OPERAND (exp, 1)) + && integer_onep (TREE_OPERAND (exp, 2))) + do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label); + + else + { + register rtx label1 = gen_label_rtx (); + drop_through_label = gen_label_rtx (); + do_jump (TREE_OPERAND (exp, 0), label1, NULL_RTX); + /* Now the THEN-expression. */ + do_jump (TREE_OPERAND (exp, 1), + if_false_label ? if_false_label : drop_through_label, + if_true_label ? if_true_label : drop_through_label); + /* In case the do_jump just above never jumps. */ + do_pending_stack_adjust (); + emit_label (label1); + /* Now the ELSE-expression. */ + do_jump (TREE_OPERAND (exp, 2), + if_false_label ? if_false_label : drop_through_label, + if_true_label ? if_true_label : drop_through_label); + } + break; + + case EQ_EXPR: + if (integer_zerop (TREE_OPERAND (exp, 1))) + do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label); + else if ((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) + == MODE_INT) + && + !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))) + do_jump_by_parts_equality (exp, if_false_label, if_true_label); + else + comparison = compare (exp, EQ, EQ); + break; + + case NE_EXPR: + if (integer_zerop (TREE_OPERAND (exp, 1))) + do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label); + else if ((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) + == MODE_INT) + && + !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))) + do_jump_by_parts_equality (exp, if_true_label, if_false_label); + else + comparison = compare (exp, NE, NE); + break; + + case LT_EXPR: + if ((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) + == MODE_INT) + && !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))) + do_jump_by_parts_greater (exp, 1, if_false_label, if_true_label); + else + comparison = compare (exp, LT, LTU); + break; + + case LE_EXPR: + if ((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) + == MODE_INT) + && !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))) + do_jump_by_parts_greater (exp, 0, if_true_label, if_false_label); + else + comparison = compare (exp, LE, LEU); + break; + + case GT_EXPR: + if ((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) + == MODE_INT) + && !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))) + do_jump_by_parts_greater (exp, 0, if_false_label, if_true_label); + else + comparison = compare (exp, GT, GTU); + break; + + case GE_EXPR: + if ((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) + == MODE_INT) + && !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))) + do_jump_by_parts_greater (exp, 1, if_true_label, if_false_label); + else + comparison = compare (exp, GE, GEU); + break; + + default: + normal: + temp = expand_expr (exp, NULL_RTX, VOIDmode, 0); +#if 0 + /* This is not needed any more and causes poor code since it causes + comparisons and tests from non-SI objects to have different code + sequences. */ + /* Copy to register to avoid generating bad insns by cse + from (set (mem ...) (arithop)) (set (cc0) (mem ...)). */ + if (!cse_not_expected && GET_CODE (temp) == MEM) + temp = copy_to_reg (temp); +#endif + do_pending_stack_adjust (); + if (GET_CODE (temp) == CONST_INT) + comparison = (temp == const0_rtx ? const0_rtx : const_true_rtx); + else if (GET_CODE (temp) == LABEL_REF) + comparison = const_true_rtx; + else if (GET_MODE_CLASS (GET_MODE (temp)) == MODE_INT + && !can_compare_p (GET_MODE (temp))) + /* Note swapping the labels gives us not-equal. */ + do_jump_by_parts_equality_rtx (temp, if_true_label, if_false_label); + else if (GET_MODE (temp) != VOIDmode) + comparison = compare_from_rtx (temp, CONST0_RTX (GET_MODE (temp)), + NE, TREE_UNSIGNED (TREE_TYPE (exp)), + GET_MODE (temp), NULL_RTX, 0); + else + abort (); + } + + /* Do any postincrements in the expression that was tested. */ + emit_queue (); + + /* If COMPARISON is nonzero here, it is an rtx that can be substituted + straight into a conditional jump instruction as the jump condition. + Otherwise, all the work has been done already. */ + + if (comparison == const_true_rtx) + { + if (if_true_label) + emit_jump (if_true_label); + } + else if (comparison == const0_rtx) + { + if (if_false_label) + emit_jump (if_false_label); + } + else if (comparison) + do_jump_for_compare (comparison, if_false_label, if_true_label); + + free_temp_slots (); + + if (drop_through_label) + { + /* If do_jump produces code that might be jumped around, + do any stack adjusts from that code, before the place + where control merges in. */ + do_pending_stack_adjust (); + emit_label (drop_through_label); + } +} + +/* Given a comparison expression EXP for values too wide to be compared + with one insn, test the comparison and jump to the appropriate label. + The code of EXP is ignored; we always test GT if SWAP is 0, + and LT if SWAP is 1. */ + +static void +do_jump_by_parts_greater (exp, swap, if_false_label, if_true_label) + tree exp; + int swap; + rtx if_false_label, if_true_label; +{ + rtx op0 = expand_expr (TREE_OPERAND (exp, swap), NULL_RTX, VOIDmode, 0); + rtx op1 = expand_expr (TREE_OPERAND (exp, !swap), NULL_RTX, VOIDmode, 0); + enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))); + int nwords = (GET_MODE_SIZE (mode) / UNITS_PER_WORD); + rtx drop_through_label = 0; + int unsignedp = TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))); + int i; + + if (! if_true_label || ! if_false_label) + drop_through_label = gen_label_rtx (); + if (! if_true_label) + if_true_label = drop_through_label; + if (! if_false_label) + if_false_label = drop_through_label; + + /* Compare a word at a time, high order first. */ + for (i = 0; i < nwords; i++) + { + rtx comp; + rtx op0_word, op1_word; + + if (WORDS_BIG_ENDIAN) + { + op0_word = operand_subword_force (op0, i, mode); + op1_word = operand_subword_force (op1, i, mode); + } + else + { + op0_word = operand_subword_force (op0, nwords - 1 - i, mode); + op1_word = operand_subword_force (op1, nwords - 1 - i, mode); + } + + /* All but high-order word must be compared as unsigned. */ + comp = compare_from_rtx (op0_word, op1_word, + (unsignedp || i > 0) ? GTU : GT, + unsignedp, word_mode, NULL_RTX, 0); + if (comp == const_true_rtx) + emit_jump (if_true_label); + else if (comp != const0_rtx) + do_jump_for_compare (comp, NULL_RTX, if_true_label); + + /* Consider lower words only if these are equal. */ + comp = compare_from_rtx (op0_word, op1_word, NE, unsignedp, word_mode, + NULL_RTX, 0); + if (comp == const_true_rtx) + emit_jump (if_false_label); + else if (comp != const0_rtx) + do_jump_for_compare (comp, NULL_RTX, if_false_label); + } + + if (if_false_label) + emit_jump (if_false_label); + if (drop_through_label) + emit_label (drop_through_label); +} + +/* Compare OP0 with OP1, word at a time, in mode MODE. + UNSIGNEDP says to do unsigned comparison. + Jump to IF_TRUE_LABEL if OP0 is greater, IF_FALSE_LABEL otherwise. */ + +static void +do_jump_by_parts_greater_rtx (mode, unsignedp, op0, op1, if_false_label, if_true_label) + enum machine_mode mode; + int unsignedp; + rtx op0, op1; + rtx if_false_label, if_true_label; +{ + int nwords = (GET_MODE_SIZE (mode) / UNITS_PER_WORD); + rtx drop_through_label = 0; + int i; + + if (! if_true_label || ! if_false_label) + drop_through_label = gen_label_rtx (); + if (! if_true_label) + if_true_label = drop_through_label; + if (! if_false_label) + if_false_label = drop_through_label; + + /* Compare a word at a time, high order first. */ + for (i = 0; i < nwords; i++) + { + rtx comp; + rtx op0_word, op1_word; + + if (WORDS_BIG_ENDIAN) + { + op0_word = operand_subword_force (op0, i, mode); + op1_word = operand_subword_force (op1, i, mode); + } + else + { + op0_word = operand_subword_force (op0, nwords - 1 - i, mode); + op1_word = operand_subword_force (op1, nwords - 1 - i, mode); + } + + /* All but high-order word must be compared as unsigned. */ + comp = compare_from_rtx (op0_word, op1_word, + (unsignedp || i > 0) ? GTU : GT, + unsignedp, word_mode, NULL_RTX, 0); + if (comp == const_true_rtx) + emit_jump (if_true_label); + else if (comp != const0_rtx) + do_jump_for_compare (comp, NULL_RTX, if_true_label); + + /* Consider lower words only if these are equal. */ + comp = compare_from_rtx (op0_word, op1_word, NE, unsignedp, word_mode, + NULL_RTX, 0); + if (comp == const_true_rtx) + emit_jump (if_false_label); + else if (comp != const0_rtx) + do_jump_for_compare (comp, NULL_RTX, if_false_label); + } + + if (if_false_label) + emit_jump (if_false_label); + if (drop_through_label) + emit_label (drop_through_label); +} + +/* Given an EQ_EXPR expression EXP for values too wide to be compared + with one insn, test the comparison and jump to the appropriate label. */ + +static void +do_jump_by_parts_equality (exp, if_false_label, if_true_label) + tree exp; + rtx if_false_label, if_true_label; +{ + rtx op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0); + rtx op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0); + enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))); + int nwords = (GET_MODE_SIZE (mode) / UNITS_PER_WORD); + int i; + rtx drop_through_label = 0; + + if (! if_false_label) + drop_through_label = if_false_label = gen_label_rtx (); + + for (i = 0; i < nwords; i++) + { + rtx comp = compare_from_rtx (operand_subword_force (op0, i, mode), + operand_subword_force (op1, i, mode), + EQ, TREE_UNSIGNED (TREE_TYPE (exp)), + word_mode, NULL_RTX, 0); + if (comp == const_true_rtx) + emit_jump (if_false_label); + else if (comp != const0_rtx) + do_jump_for_compare (comp, if_false_label, NULL_RTX); + } + + if (if_true_label) + emit_jump (if_true_label); + if (drop_through_label) + emit_label (drop_through_label); +} + +/* Jump according to whether OP0 is 0. + We assume that OP0 has an integer mode that is too wide + for the available compare insns. */ + +static void +do_jump_by_parts_equality_rtx (op0, if_false_label, if_true_label) + rtx op0; + rtx if_false_label, if_true_label; +{ + int nwords = GET_MODE_SIZE (GET_MODE (op0)) / UNITS_PER_WORD; + int i; + rtx drop_through_label = 0; + + if (! if_false_label) + drop_through_label = if_false_label = gen_label_rtx (); + + for (i = 0; i < nwords; i++) + { + rtx comp = compare_from_rtx (operand_subword_force (op0, i, + GET_MODE (op0)), + const0_rtx, EQ, 1, word_mode, NULL_RTX, 0); + if (comp == const_true_rtx) + emit_jump (if_false_label); + else if (comp != const0_rtx) + do_jump_for_compare (comp, if_false_label, NULL_RTX); + } + + if (if_true_label) + emit_jump (if_true_label); + if (drop_through_label) + emit_label (drop_through_label); +} + +/* Given a comparison expression in rtl form, output conditional branches to + IF_TRUE_LABEL, IF_FALSE_LABEL, or both. */ + +static void +do_jump_for_compare (comparison, if_false_label, if_true_label) + rtx comparison, if_false_label, if_true_label; +{ + if (if_true_label) + { + if (bcc_gen_fctn[(int) GET_CODE (comparison)] != 0) + emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (comparison)]) (if_true_label)); + else + abort (); + + if (if_false_label) + emit_jump (if_false_label); + } + else if (if_false_label) + { + rtx insn; + rtx prev = PREV_INSN (get_last_insn ()); + rtx branch = 0; + + /* Output the branch with the opposite condition. Then try to invert + what is generated. If more than one insn is a branch, or if the + branch is not the last insn written, abort. If we can't invert + the branch, emit make a true label, redirect this jump to that, + emit a jump to the false label and define the true label. */ + + if (bcc_gen_fctn[(int) GET_CODE (comparison)] != 0) + emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (comparison)]) (if_false_label)); + else + abort (); + + /* Here we get the insn before what was just emitted. + On some machines, emitting the branch can discard + the previous compare insn and emit a replacement. */ + if (prev == 0) + /* If there's only one preceding insn... */ + insn = get_insns (); + else + insn = NEXT_INSN (prev); + + for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == JUMP_INSN) + { + if (branch) + abort (); + branch = insn; + } + + if (branch != get_last_insn ()) + abort (); + + if (! invert_jump (branch, if_false_label)) + { + if_true_label = gen_label_rtx (); + redirect_jump (branch, if_true_label); + emit_jump (if_false_label); + emit_label (if_true_label); + } + } +} + +/* Generate code for a comparison expression EXP + (including code to compute the values to be compared) + and set (CC0) according to the result. + SIGNED_CODE should be the rtx operation for this comparison for + signed data; UNSIGNED_CODE, likewise for use if data is unsigned. + + We force a stack adjustment unless there are currently + things pushed on the stack that aren't yet used. */ + +static rtx +compare (exp, signed_code, unsigned_code) + register tree exp; + enum rtx_code signed_code, unsigned_code; +{ + register rtx op0 + = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0); + register rtx op1 + = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0); + register tree type = TREE_TYPE (TREE_OPERAND (exp, 0)); + register enum machine_mode mode = TYPE_MODE (type); + int unsignedp = TREE_UNSIGNED (type); + enum rtx_code code = unsignedp ? unsigned_code : signed_code; + + return compare_from_rtx (op0, op1, code, unsignedp, mode, + ((mode == BLKmode) + ? expr_size (TREE_OPERAND (exp, 0)) : NULL_RTX), + TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); +} + +/* Like compare but expects the values to compare as two rtx's. + The decision as to signed or unsigned comparison must be made by the caller. + + If MODE is BLKmode, SIZE is an RTX giving the size of the objects being + compared. + + If ALIGN is non-zero, it is the alignment of this type; if zero, the + size of MODE should be used. */ + +rtx +compare_from_rtx (op0, op1, code, unsignedp, mode, size, align) + register rtx op0, op1; + enum rtx_code code; + int unsignedp; + enum machine_mode mode; + rtx size; + int align; +{ + rtx tem; + + /* If one operand is constant, make it the second one. Only do this + if the other operand is not constant as well. */ + + if ((CONSTANT_P (op0) && ! CONSTANT_P (op1)) + || (GET_CODE (op0) == CONST_INT && GET_CODE (op1) != CONST_INT)) + { + tem = op0; + op0 = op1; + op1 = tem; + code = swap_condition (code); + } + + if (flag_force_mem) + { + op0 = force_not_mem (op0); + op1 = force_not_mem (op1); + } + + do_pending_stack_adjust (); + + if (GET_CODE (op0) == CONST_INT && GET_CODE (op1) == CONST_INT + && (tem = simplify_relational_operation (code, mode, op0, op1)) != 0) + return tem; + +#if 0 + /* There's no need to do this now that combine.c can eliminate lots of + sign extensions. This can be less efficient in certain cases on other + machines. */ + + /* If this is a signed equality comparison, we can do it as an + unsigned comparison since zero-extension is cheaper than sign + extension and comparisons with zero are done as unsigned. This is + the case even on machines that can do fast sign extension, since + zero-extension is easier to combine with other operations than + sign-extension is. If we are comparing against a constant, we must + convert it to what it would look like unsigned. */ + if ((code == EQ || code == NE) && ! unsignedp + && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT) + { + if (GET_CODE (op1) == CONST_INT + && (INTVAL (op1) & GET_MODE_MASK (GET_MODE (op0))) != INTVAL (op1)) + op1 = GEN_INT (INTVAL (op1) & GET_MODE_MASK (GET_MODE (op0))); + unsignedp = 1; + } +#endif + + emit_cmp_insn (op0, op1, code, size, mode, unsignedp, align); + + return gen_rtx (code, VOIDmode, cc0_rtx, const0_rtx); +} + +/* Generate code to calculate EXP using a store-flag instruction + and return an rtx for the result. EXP is either a comparison + or a TRUTH_NOT_EXPR whose operand is a comparison. + + If TARGET is nonzero, store the result there if convenient. + + If ONLY_CHEAP is non-zero, only do this if it is likely to be very + cheap. + + Return zero if there is no suitable set-flag instruction + available on this machine. + + Once expand_expr has been called on the arguments of the comparison, + we are committed to doing the store flag, since it is not safe to + re-evaluate the expression. We emit the store-flag insn by calling + emit_store_flag, but only expand the arguments if we have a reason + to believe that emit_store_flag will be successful. If we think that + it will, but it isn't, we have to simulate the store-flag with a + set/jump/set sequence. */ + +static rtx +do_store_flag (exp, target, mode, only_cheap) + tree exp; + rtx target; + enum machine_mode mode; + int only_cheap; +{ + enum rtx_code code; + tree arg0, arg1, type; + tree tem; + enum machine_mode operand_mode; + int invert = 0; + int unsignedp; + rtx op0, op1; + enum insn_code icode; + rtx subtarget = target; + rtx result, label, pattern, jump_pat; + + /* If this is a TRUTH_NOT_EXPR, set a flag indicating we must invert the + result at the end. We can't simply invert the test since it would + have already been inverted if it were valid. This case occurs for + some floating-point comparisons. */ + + if (TREE_CODE (exp) == TRUTH_NOT_EXPR) + invert = 1, exp = TREE_OPERAND (exp, 0); + + arg0 = TREE_OPERAND (exp, 0); + arg1 = TREE_OPERAND (exp, 1); + type = TREE_TYPE (arg0); + operand_mode = TYPE_MODE (type); + unsignedp = TREE_UNSIGNED (type); + + /* We won't bother with BLKmode store-flag operations because it would mean + passing a lot of information to emit_store_flag. */ + if (operand_mode == BLKmode) + return 0; + + STRIP_NOPS (arg0); + STRIP_NOPS (arg1); + + /* Get the rtx comparison code to use. We know that EXP is a comparison + operation of some type. Some comparisons against 1 and -1 can be + converted to comparisons with zero. Do so here so that the tests + below will be aware that we have a comparison with zero. These + tests will not catch constants in the first operand, but constants + are rarely passed as the first operand. */ + + switch (TREE_CODE (exp)) + { + case EQ_EXPR: + code = EQ; + break; + case NE_EXPR: + code = NE; + break; + case LT_EXPR: + if (integer_onep (arg1)) + arg1 = integer_zero_node, code = unsignedp ? LEU : LE; + else + code = unsignedp ? LTU : LT; + break; + case LE_EXPR: + if (! unsignedp && integer_all_onesp (arg1)) + arg1 = integer_zero_node, code = LT; + else + code = unsignedp ? LEU : LE; + break; + case GT_EXPR: + if (! unsignedp && integer_all_onesp (arg1)) + arg1 = integer_zero_node, code = GE; + else + code = unsignedp ? GTU : GT; + break; + case GE_EXPR: + if (integer_onep (arg1)) + arg1 = integer_zero_node, code = unsignedp ? GTU : GT; + else + code = unsignedp ? GEU : GE; + break; + default: + abort (); + } + + /* Put a constant second. */ + if (TREE_CODE (arg0) == REAL_CST || TREE_CODE (arg0) == INTEGER_CST) + { + tem = arg0; arg0 = arg1; arg1 = tem; + code = swap_condition (code); + } + + /* If this is an equality or inequality test of a single bit, we can + do this by shifting the bit being tested to the low-order bit and + masking the result with the constant 1. If the condition was EQ, + we xor it with 1. This does not require an scc insn and is faster + than an scc insn even if we have it. */ + + if ((code == NE || code == EQ) + && TREE_CODE (arg0) == BIT_AND_EXPR && integer_zerop (arg1) + && integer_pow2p (TREE_OPERAND (arg0, 1)) + && TYPE_PRECISION (type) <= HOST_BITS_PER_WIDE_INT) + { + int bitnum = exact_log2 (INTVAL (expand_expr (TREE_OPERAND (arg0, 1), + NULL_RTX, VOIDmode, 0))); + + if (subtarget == 0 || GET_CODE (subtarget) != REG + || GET_MODE (subtarget) != operand_mode + || ! safe_from_p (subtarget, TREE_OPERAND (arg0, 0))) + subtarget = 0; + + op0 = expand_expr (TREE_OPERAND (arg0, 0), subtarget, VOIDmode, 0); + + if (bitnum != 0) + op0 = expand_shift (RSHIFT_EXPR, GET_MODE (op0), op0, + size_int (bitnum), target, 1); + + if (GET_MODE (op0) != mode) + op0 = convert_to_mode (mode, op0, 1); + + if (bitnum != TYPE_PRECISION (type) - 1) + op0 = expand_and (op0, const1_rtx, target); + + if ((code == EQ && ! invert) || (code == NE && invert)) + op0 = expand_binop (mode, xor_optab, op0, const1_rtx, target, 0, + OPTAB_LIB_WIDEN); + + return op0; + } + + /* Now see if we are likely to be able to do this. Return if not. */ + if (! can_compare_p (operand_mode)) + return 0; + icode = setcc_gen_code[(int) code]; + if (icode == CODE_FOR_nothing + || (only_cheap && insn_operand_mode[(int) icode][0] != mode)) + { + /* We can only do this if it is one of the special cases that + can be handled without an scc insn. */ + if ((code == LT && integer_zerop (arg1)) + || (! only_cheap && code == GE && integer_zerop (arg1))) + ; + else if (BRANCH_COST >= 0 + && ! only_cheap && (code == NE || code == EQ) + && TREE_CODE (type) != REAL_TYPE + && ((abs_optab->handlers[(int) operand_mode].insn_code + != CODE_FOR_nothing) + || (ffs_optab->handlers[(int) operand_mode].insn_code + != CODE_FOR_nothing))) + ; + else + return 0; + } + + preexpand_calls (exp); + if (subtarget == 0 || GET_CODE (subtarget) != REG + || GET_MODE (subtarget) != operand_mode + || ! safe_from_p (subtarget, arg1)) + subtarget = 0; + + op0 = expand_expr (arg0, subtarget, VOIDmode, 0); + op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0); + + if (target == 0) + target = gen_reg_rtx (mode); + + /* Pass copies of OP0 and OP1 in case they contain a QUEUED. This is safe + because, if the emit_store_flag does anything it will succeed and + OP0 and OP1 will not be used subsequently. */ + + result = emit_store_flag (target, code, + queued_subexp_p (op0) ? copy_rtx (op0) : op0, + queued_subexp_p (op1) ? copy_rtx (op1) : op1, + operand_mode, unsignedp, 1); + + if (result) + { + if (invert) + result = expand_binop (mode, xor_optab, result, const1_rtx, + result, 0, OPTAB_LIB_WIDEN); + return result; + } + + /* If this failed, we have to do this with set/compare/jump/set code. */ + if (target == 0 || GET_CODE (target) != REG + || reg_mentioned_p (target, op0) || reg_mentioned_p (target, op1)) + target = gen_reg_rtx (GET_MODE (target)); + + emit_move_insn (target, invert ? const0_rtx : const1_rtx); + result = compare_from_rtx (op0, op1, code, unsignedp, + operand_mode, NULL_RTX, 0); + if (GET_CODE (result) == CONST_INT) + return (((result == const0_rtx && ! invert) + || (result != const0_rtx && invert)) + ? const0_rtx : const1_rtx); + + label = gen_label_rtx (); + if (bcc_gen_fctn[(int) code] == 0) + abort (); + + emit_jump_insn ((*bcc_gen_fctn[(int) code]) (label)); + emit_move_insn (target, invert ? const1_rtx : const0_rtx); + emit_label (label); + + return target; +} + +/* Generate a tablejump instruction (used for switch statements). */ + +#ifdef HAVE_tablejump + +/* INDEX is the value being switched on, with the lowest value + in the table already subtracted. + MODE is its expected mode (needed if INDEX is constant). + RANGE is the length of the jump table. + TABLE_LABEL is a CODE_LABEL rtx for the table itself. + + DEFAULT_LABEL is a CODE_LABEL rtx to jump to if the + index value is out of range. */ + +void +do_tablejump (index, mode, range, table_label, default_label) + rtx index, range, table_label, default_label; + enum machine_mode mode; +{ + register rtx temp, vector; + + /* Do an unsigned comparison (in the proper mode) between the index + expression and the value which represents the length of the range. + Since we just finished subtracting the lower bound of the range + from the index expression, this comparison allows us to simultaneously + check that the original index expression value is both greater than + or equal to the minimum value of the range and less than or equal to + the maximum value of the range. */ + + emit_cmp_insn (range, index, LTU, NULL_RTX, mode, 1, 0); + emit_jump_insn (gen_bltu (default_label)); + + /* If index is in range, it must fit in Pmode. + Convert to Pmode so we can index with it. */ + if (mode != Pmode) + index = convert_to_mode (Pmode, index, 1); + + /* If flag_force_addr were to affect this address + it could interfere with the tricky assumptions made + about addresses that contain label-refs, + which may be valid only very near the tablejump itself. */ + /* ??? The only correct use of CASE_VECTOR_MODE is the one inside the + GET_MODE_SIZE, because this indicates how large insns are. The other + uses should all be Pmode, because they are addresses. This code + could fail if addresses and insns are not the same size. */ + index = memory_address_noforce + (CASE_VECTOR_MODE, + gen_rtx (PLUS, Pmode, + gen_rtx (MULT, Pmode, index, + GEN_INT (GET_MODE_SIZE (CASE_VECTOR_MODE))), + gen_rtx (LABEL_REF, Pmode, table_label))); + temp = gen_reg_rtx (CASE_VECTOR_MODE); + vector = gen_rtx (MEM, CASE_VECTOR_MODE, index); + RTX_UNCHANGING_P (vector) = 1; + convert_move (temp, vector, 0); + + emit_jump_insn (gen_tablejump (temp, table_label)); + +#ifndef CASE_VECTOR_PC_RELATIVE + /* If we are generating PIC code or if the table is PC-relative, the + table and JUMP_INSN must be adjacent, so don't output a BARRIER. */ + if (! flag_pic) + emit_barrier (); +#endif +} + +#endif /* HAVE_tablejump */ diff --git a/gnu/usr.bin/cc/common/expr.h b/gnu/usr.bin/cc/lib/expr.h similarity index 100% rename from gnu/usr.bin/cc/common/expr.h rename to gnu/usr.bin/cc/lib/expr.h diff --git a/gnu/usr.bin/cc/lib/final.c b/gnu/usr.bin/cc/lib/final.c new file mode 100644 index 0000000000..9773fa8e28 --- /dev/null +++ b/gnu/usr.bin/cc/lib/final.c @@ -0,0 +1,2740 @@ +/* Convert RTL to assembler code and output it, for GNU compiler. + Copyright (C) 1987, 1988, 1989, 1992 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* This is the final pass of the compiler. + It looks at the rtl code for a function and outputs assembler code. + + Call `final_start_function' to output the assembler code for function entry, + `final' to output assembler code for some RTL code, + `final_end_function' to output assembler code for function exit. + If a function is compiled in several pieces, each piece is + output separately with `final'. + + Some optimizations are also done at this level. + Move instructions that were made unnecessary by good register allocation + are detected and omitted from the output. (Though most of these + are removed by the last jump pass.) + + Instructions to set the condition codes are omitted when it can be + seen that the condition codes already had the desired values. + + In some cases it is sufficient if the inherited condition codes + have related values, but this may require the following insn + (the one that tests the condition codes) to be modified. + + The code for the function prologue and epilogue are generated + directly as assembler code by the macros FUNCTION_PROLOGUE and + FUNCTION_EPILOGUE. Those instructions never exist as rtl. */ + +#include "config.h" +#include "gvarargs.h" +#include "rtl.h" +#include "regs.h" +#include "insn-config.h" +#include "insn-flags.h" +#include "insn-attr.h" +#include "insn-codes.h" +#include "recog.h" +#include "conditions.h" +#include "flags.h" +#include "real.h" +#include "hard-reg-set.h" +#include "defaults.h" + +#include + +#include "output.h" + +/* Get N_SLINE and N_SOL from stab.h if we can expect the file to exist. */ +#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) +#if defined (USG) || defined (NO_STAB_H) +#include "gstab.h" /* If doing DBX on sysV, use our own stab.h. */ +#else +#include /* On BSD, use the system's stab.h. */ +#endif /* not USG */ +#endif /* DBX_DEBUGGING_INFO || XCOFF_DEBUGGING_INFO */ + +#ifdef XCOFF_DEBUGGING_INFO +#include "xcoffout.h" +#endif + +/* .stabd code for line number. */ +#ifndef N_SLINE +#define N_SLINE 0x44 +#endif + +/* .stabs code for included file name. */ +#ifndef N_SOL +#define N_SOL 0x84 +#endif + +#ifndef INT_TYPE_SIZE +#define INT_TYPE_SIZE BITS_PER_WORD +#endif + +/* If we aren't using cc0, CC_STATUS_INIT shouldn't exist. So define a + null default for it to save conditionalization later. */ +#ifndef CC_STATUS_INIT +#define CC_STATUS_INIT +#endif + +/* How to start an assembler comment. */ +#ifndef ASM_COMMENT_START +#define ASM_COMMENT_START ";#" +#endif + +rtx peephole (); +void output_asm_insn (); +rtx alter_subreg (); +static int alter_cond (); +void output_asm_label (); +static void output_operand (); +void output_address (); +void output_addr_const (); +static void output_source_line (); +rtx final_scan_insn (); +void profile_function (); +static void profile_after_prologue (); + +#ifdef HAVE_ATTR_length +static int asm_insn_count (); +#endif + +/* Nonzero means this function is a leaf function, with no function calls. + This variable exists to be examined in FUNCTION_PROLOGUE + and FUNCTION_EPILOGUE. Always zero, unless set by some action. */ +int leaf_function; + +int leaf_function_p (); + +#ifdef LEAF_REGISTERS +int only_leaf_regs_used (); +static void leaf_renumber_regs (); +void leaf_renumber_regs_insn (); +#endif + +/* Last insn processed by final_scan_insn. */ +static rtx debug_insn = 0; + +/* Line number of last NOTE. */ +static int last_linenum; + +/* Number of basic blocks seen so far; + used if profile_block_flag is set. */ +static int count_basic_blocks; + +/* Nonzero while outputting an `asm' with operands. + This means that inconsistencies are the user's fault, so don't abort. + The precise value is the insn being output, to pass to error_for_asm. */ +static rtx this_is_asm_operands; + +/* Number of operands of this insn, for an `asm' with operands. */ +static int insn_noperands; + +/* Compare optimization flag. */ + +static rtx last_ignored_compare = 0; + +/* Flag indicating this insn is the start of a new basic block. */ + +static int new_block = 1; + +/* All the symbol-blocks (levels of scoping) in the compilation + are assigned sequence numbers in order of appearance of the + beginnings of the symbol-blocks. Both final and dbxout do this, + and assume that they will both give the same number to each block. + Final uses these sequence numbers to generate assembler label names + LBBnnn and LBEnnn for the beginning and end of the symbol-block. + Dbxout uses the sequence numbers to generate references to the same labels + from the dbx debugging information. + + Sdb records this level at the beginning of each function, + in order to find the current level when recursing down declarations. + It outputs the block beginning and endings + at the point in the asm file where the blocks would begin and end. */ + +int next_block_index; + +/* Assign a unique number to each insn that is output. + This can be used to generate unique local labels. */ + +static int insn_counter = 0; + +#ifdef HAVE_cc0 +/* This variable contains machine-dependent flags (defined in tm.h) + set and examined by output routines + that describe how to interpret the condition codes properly. */ + +CC_STATUS cc_status; + +/* During output of an insn, this contains a copy of cc_status + from before the insn. */ + +CC_STATUS cc_prev_status; +#endif + +/* Indexed by hardware reg number, is 1 if that register is ever + used in the current function. + + In life_analysis, or in stupid_life_analysis, this is set + up to record the hard regs used explicitly. Reload adds + in the hard regs used for holding pseudo regs. Final uses + it to generate the code in the function prologue and epilogue + to save and restore registers as needed. */ + +char regs_ever_live[FIRST_PSEUDO_REGISTER]; + +/* Nonzero means current function must be given a frame pointer. + Set in stmt.c if anything is allocated on the stack there. + Set in reload1.c if anything is allocated on the stack there. */ + +int frame_pointer_needed; + +/* Assign unique numbers to labels generated for profiling. */ + +int profile_label_no; + +/* Length so far allocated in PENDING_BLOCKS. */ + +static int max_block_depth; + +/* Stack of sequence numbers of symbol-blocks of which we have seen the + beginning but not yet the end. Sequence numbers are assigned at + the beginning; this stack allows us to find the sequence number + of a block that is ending. */ + +static int *pending_blocks; + +/* Number of elements currently in use in PENDING_BLOCKS. */ + +static int block_depth; + +/* Nonzero if have enabled APP processing of our assembler output. */ + +static int app_on; + +/* If we are outputting an insn sequence, this contains the sequence rtx. + Zero otherwise. */ + +rtx final_sequence; + +/* Indexed by line number, nonzero if there is a note for that line. */ + +static char *line_note_exists; + +/* Initialize data in final at the beginning of a compilation. */ + +void +init_final (filename) + char *filename; +{ + next_block_index = 2; + app_on = 0; + max_block_depth = 20; + pending_blocks = (int *) xmalloc (20 * sizeof *pending_blocks); + final_sequence = 0; +} + +/* Called at end of source file, + to output the block-profiling table for this entire compilation. */ + +void +end_final (filename) + char *filename; +{ + int i; + + if (profile_block_flag) + { + char name[12]; + + data_section (); + + /* Output the main header, of 6 words: + 0: 1 if this file's initialized, else 0. + 1: address of file name. + 2: address of table of counts. + 4: number of counts in the table. + 5: always 0, for compatibility with Sun. + 6: extra word added by GNU: address of address table + which contains addresses of basic blocks, + in parallel with the table of counts. */ + ASM_OUTPUT_ALIGN (asm_out_file, + exact_log2 (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); + + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 0); + assemble_integer (const0_rtx, UNITS_PER_WORD, 1); + ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 1); + assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1); + ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 2); + assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1); + assemble_integer (GEN_INT (count_basic_blocks), UNITS_PER_WORD, 1); + assemble_integer (const0_rtx, UNITS_PER_WORD, 1); + ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 3); + assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1); + + /* Output the file name. */ + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 1); + { + int len = strlen (filename); + char *data_file = (char *) alloca (len + 3); + strcpy (data_file, filename); + strip_off_ending (data_file, len); + strcat (data_file, ".d"); + assemble_string (data_file, strlen (data_file) + 1); + } + + /* Realign data section. */ + ASM_OUTPUT_ALIGN (asm_out_file, + exact_log2 (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); + + /* Make space for the table of counts. */ + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 2); + if (count_basic_blocks != 0) + assemble_zeros (INT_TYPE_SIZE / BITS_PER_UNIT * count_basic_blocks); + + /* Output the table of addresses. */ + readonly_data_section (); + /* Realign in new section */ + ASM_OUTPUT_ALIGN (asm_out_file, + floor_log2 (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 3); + for (i = 0; i < count_basic_blocks; i++) + { + char name[12]; + ASM_GENERATE_INTERNAL_LABEL (name, "LPB", i); + assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), + UNITS_PER_WORD, 1); + } + + /* End with the address of the table of addresses, + so we can find it easily, as the last word in the file's text. */ + ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 3); + assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1); + } +} + +/* Enable APP processing of subsequent output. + Used before the output from an `asm' statement. */ + +void +app_enable () +{ + if (! app_on) + { + fprintf (asm_out_file, ASM_APP_ON); + app_on = 1; + } +} + +/* Enable APP processing of subsequent output. + Called from varasm.c before most kinds of output. */ + +void +app_disable () +{ + if (app_on) + { + fprintf (asm_out_file, ASM_APP_OFF); + app_on = 0; + } +} + +/* Return the number of slots filled in the current + delayed branch sequence (we don't count the insn needing the + delay slot). Zero if not in a delayed branch sequence. */ + +#ifdef DELAY_SLOTS +int +dbr_sequence_length () +{ + if (final_sequence != 0) + return XVECLEN (final_sequence, 0) - 1; + else + return 0; +} +#endif + +/* The next two pages contain routines used to compute the length of an insn + and to shorten branches. */ + +/* Arrays for insn lengths, and addresses. The latter is referenced by + `insn_current_length'. */ + +static short *insn_lengths; +int *insn_addresses; + +/* Address of insn being processed. Used by `insn_current_length'. */ +int insn_current_address; + +/* Indicate the branch shortening hasn't yet been done. */ + +void +init_insn_lengths () +{ + insn_lengths = 0; +} + +/* Obtain the current length of an insn. If branch shortening has been done, + get its actual length. Otherwise, get its maximum length. */ + +int +get_attr_length (insn) + rtx insn; +{ +#ifdef HAVE_ATTR_length + rtx body; + int i; + int length = 0; + + if (insn_lengths) + return insn_lengths[INSN_UID (insn)]; + else + switch (GET_CODE (insn)) + { + case NOTE: + case BARRIER: + case CODE_LABEL: + return 0; + + case CALL_INSN: + length = insn_default_length (insn); + break; + + case JUMP_INSN: + body = PATTERN (insn); + if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC) + { + /* This only takes room if jump tables go into the text section. */ +#if !defined(READONLY_DATA_SECTION) || defined(JUMP_TABLES_IN_TEXT_SECTION) + length = (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC) + * GET_MODE_SIZE (GET_MODE (body))); + + /* Be pessimistic and assume worst-case alignment. */ + length += (GET_MODE_SIZE (GET_MODE (body)) - 1); +#else + return 0; +#endif + } + else + length = insn_default_length (insn); + break; + + case INSN: + body = PATTERN (insn); + if (GET_CODE (body) == USE || GET_CODE (body) == CLOBBER) + return 0; + + else if (GET_CODE (body) == ASM_INPUT || asm_noperands (body) >= 0) + length = asm_insn_count (insn) * insn_default_length (insn); + else if (GET_CODE (body) == SEQUENCE) + for (i = 0; i < XVECLEN (body, 0); i++) + length += get_attr_length (XVECEXP (body, 0, i)); + else + length = insn_default_length (insn); + } + +#ifdef ADJUST_INSN_LENGTH + ADJUST_INSN_LENGTH (insn, length); +#endif + return length; +#else /* not HAVE_ATTR_length */ + return 0; +#endif /* not HAVE_ATTR_length */ +} + +/* Make a pass over all insns and compute their actual lengths by shortening + any branches of variable length if possible. */ + +/* Give a default value for the lowest address in a function. */ + +#ifndef FIRST_INSN_ADDRESS +#define FIRST_INSN_ADDRESS 0 +#endif + +void +shorten_branches (first) + rtx first; +{ +#ifdef HAVE_ATTR_length + rtx insn; + int something_changed = 1; + int max_uid = 0; + char *varying_length; + rtx body; + int uid; + + /* Compute maximum UID and allocate arrays. */ + for (insn = first; insn; insn = NEXT_INSN (insn)) + if (INSN_UID (insn) > max_uid) + max_uid = INSN_UID (insn); + + max_uid++; + insn_lengths = (short *) oballoc (max_uid * sizeof (short)); + insn_addresses = (int *) oballoc (max_uid * sizeof (int)); + varying_length = (char *) oballoc (max_uid * sizeof (char)); + + /* Compute initial lengths, addresses, and varying flags for each insn. */ + for (insn_current_address = FIRST_INSN_ADDRESS, insn = first; + insn != 0; + insn_current_address += insn_lengths[uid], insn = NEXT_INSN (insn)) + { + uid = INSN_UID (insn); + insn_addresses[uid] = insn_current_address; + insn_lengths[uid] = 0; + varying_length[uid] = 0; + + if (GET_CODE (insn) == NOTE || GET_CODE (insn) == BARRIER + || GET_CODE (insn) == CODE_LABEL) + continue; + + body = PATTERN (insn); + if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC) + { + /* This only takes room if read-only data goes into the text + section. */ +#if !defined(READONLY_DATA_SECTION) || defined(JUMP_TABLES_IN_TEXT_SECTION) + int unitsize = GET_MODE_SIZE (GET_MODE (body)); + + insn_lengths[uid] = (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC) + * GET_MODE_SIZE (GET_MODE (body))); + + /* Account for possible alignment. */ + insn_lengths[uid] + += unitsize - (insn_current_address & (unitsize - 1)); +#else + ; +#endif + } + else if (asm_noperands (body) >= 0) + insn_lengths[uid] = asm_insn_count (body) * insn_default_length (insn); + else if (GET_CODE (body) == SEQUENCE) + { + int i; + int const_delay_slots; +#ifdef DELAY_SLOTS + const_delay_slots = const_num_delay_slots (XVECEXP (body, 0, 0)); +#else + const_delay_slots = 0; +#endif + /* Inside a delay slot sequence, we do not do any branch shortening + if the shortening could change the number of delay slots + of the branch. */ + for (i = 0; i < XVECLEN (body, 0); i++) + { + rtx inner_insn = XVECEXP (body, 0, i); + int inner_uid = INSN_UID (inner_insn); + int inner_length; + + if (asm_noperands (PATTERN (XVECEXP (body, 0, i))) >= 0) + inner_length = (asm_insn_count (PATTERN (inner_insn)) + * insn_default_length (inner_insn)); + else + inner_length = insn_default_length (inner_insn); + + insn_lengths[inner_uid] = inner_length; + if (const_delay_slots) + { + if ((varying_length[inner_uid] + = insn_variable_length_p (inner_insn)) != 0) + varying_length[uid] = 1; + insn_addresses[inner_uid] = (insn_current_address + + insn_lengths[uid]); + } + else + varying_length[inner_uid] = 0; + insn_lengths[uid] += inner_length; + } + } + else if (GET_CODE (body) != USE && GET_CODE (body) != CLOBBER) + { + insn_lengths[uid] = insn_default_length (insn); + varying_length[uid] = insn_variable_length_p (insn); + } + + /* If needed, do any adjustment. */ +#ifdef ADJUST_INSN_LENGTH + ADJUST_INSN_LENGTH (insn, insn_lengths[uid]); +#endif + } + + /* Now loop over all the insns finding varying length insns. For each, + get the current insn length. If it has changed, reflect the change. + When nothing changes for a full pass, we are done. */ + + while (something_changed) + { + something_changed = 0; + for (insn_current_address = FIRST_INSN_ADDRESS, insn = first; + insn != 0; + insn = NEXT_INSN (insn)) + { + int new_length; + int tmp_length; + + uid = INSN_UID (insn); + insn_addresses[uid] = insn_current_address; + if (! varying_length[uid]) + { + insn_current_address += insn_lengths[uid]; + continue; + } + if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE) + { + int i; + + body = PATTERN (insn); + new_length = 0; + for (i = 0; i < XVECLEN (body, 0); i++) + { + rtx inner_insn = XVECEXP (body, 0, i); + int inner_uid = INSN_UID (inner_insn); + int inner_length; + + insn_addresses[inner_uid] = insn_current_address; + + /* insn_current_length returns 0 for insns with a + non-varying length. */ + if (! varying_length[inner_uid]) + inner_length = insn_lengths[inner_uid]; + else + inner_length = insn_current_length (inner_insn); + + if (inner_length != insn_lengths[inner_uid]) + { + insn_lengths[inner_uid] = inner_length; + something_changed = 1; + } + insn_current_address += insn_lengths[inner_uid]; + new_length += inner_length; + } + } + else + { + new_length = insn_current_length (insn); + insn_current_address += new_length; + } + +#ifdef SHORTEN_WITH_ADJUST_INSN_LENGTH +#ifdef ADJUST_INSN_LENGTH + /* If needed, do any adjustment. */ + tmp_length = new_length; + ADJUST_INSN_LENGTH (insn, new_length); + insn_current_address += (new_length - tmp_length); +#endif +#endif + + if (new_length != insn_lengths[uid]) + { + insn_lengths[uid] = new_length; + something_changed = 1; + } + } + } +#endif /* HAVE_ATTR_length */ +} + +#ifdef HAVE_ATTR_length +/* Given the body of an INSN known to be generated by an ASM statement, return + the number of machine instructions likely to be generated for this insn. + This is used to compute its length. */ + +static int +asm_insn_count (body) + rtx body; +{ + char *template; + int count = 1; + + for (template = decode_asm_operands (body, NULL_PTR, NULL_PTR, + NULL_PTR, NULL_PTR); + *template; template++) + if (*template == ';' || *template == '\n') + count++; + + return count; +} +#endif + +/* Output assembler code for the start of a function, + and initialize some of the variables in this file + for the new function. The label for the function and associated + assembler pseudo-ops have already been output in `assemble_start_function'. + + FIRST is the first insn of the rtl for the function being compiled. + FILE is the file to write assembler code to. + OPTIMIZE is nonzero if we should eliminate redundant + test and compare insns. */ + +void +final_start_function (first, file, optimize) + rtx first; + FILE *file; + int optimize; +{ + block_depth = 0; + + this_is_asm_operands = 0; + +#ifdef NON_SAVING_SETJMP + /* A function that calls setjmp should save and restore all the + call-saved registers on a system where longjmp clobbers them. */ + if (NON_SAVING_SETJMP && current_function_calls_setjmp) + { + int i; + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (!call_used_regs[i] && !call_fixed_regs[i]) + regs_ever_live[i] = 1; + } +#endif + + /* Initial line number is supposed to be output + before the function's prologue and label + so that the function's address will not appear to be + in the last statement of the preceding function. */ + if (NOTE_LINE_NUMBER (first) != NOTE_INSN_DELETED) + { + if (write_symbols == SDB_DEBUG) + /* For sdb, let's not, but say we did. + We need to set last_linenum for sdbout_function_begin, + but we can't have an actual line number before the .bf symbol. + (sdb_begin_function_line is not set, + and other compilers don't do it.) */ + last_linenum = NOTE_LINE_NUMBER (first); +#ifdef XCOFF_DEBUGGING_INFO + else if (write_symbols == XCOFF_DEBUG) + { + last_linenum = NOTE_LINE_NUMBER (first); + xcoffout_output_first_source_line (file, last_linenum); + } +#endif + else + output_source_line (file, first); + } + +#ifdef LEAF_REG_REMAP + if (leaf_function) + leaf_renumber_regs (first); +#endif + + /* The Sun386i and perhaps other machines don't work right + if the profiling code comes after the prologue. */ +#ifdef PROFILE_BEFORE_PROLOGUE + if (profile_flag) + profile_function (file); +#endif /* PROFILE_BEFORE_PROLOGUE */ + +#ifdef FUNCTION_PROLOGUE + /* First output the function prologue: code to set up the stack frame. */ + FUNCTION_PROLOGUE (file, get_frame_size ()); +#endif + +#if defined (SDB_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) + if (write_symbols == SDB_DEBUG || write_symbols == XCOFF_DEBUG) + next_block_index = 1; +#endif + + /* If the machine represents the prologue as RTL, the profiling code must + be emitted when NOTE_INSN_PROLOGUE_END is scanned. */ +#ifdef HAVE_prologue + if (! HAVE_prologue) +#endif + profile_after_prologue (file); + + profile_label_no++; +} + +static void +profile_after_prologue (file) + FILE *file; +{ +#ifdef FUNCTION_BLOCK_PROFILER + if (profile_block_flag) + { + FUNCTION_BLOCK_PROFILER (file, profile_label_no); + } +#endif /* FUNCTION_BLOCK_PROFILER */ + +#ifndef PROFILE_BEFORE_PROLOGUE + if (profile_flag) + profile_function (file); +#endif /* not PROFILE_BEFORE_PROLOGUE */ +} + +void +profile_function (file) + FILE *file; +{ + int align = MIN (BIGGEST_ALIGNMENT, INT_TYPE_SIZE); + int sval = current_function_returns_struct; + int cxt = current_function_needs_context; + + data_section (); + ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT)); + ASM_OUTPUT_INTERNAL_LABEL (file, "LP", profile_label_no); + assemble_integer (const0_rtx, UNITS_PER_WORD, 1); + + text_section (); + +#ifdef STRUCT_VALUE_INCOMING_REGNUM + if (sval) + ASM_OUTPUT_REG_PUSH (file, STRUCT_VALUE_INCOMING_REGNUM); +#else +#ifdef STRUCT_VALUE_REGNUM + if (sval) + ASM_OUTPUT_REG_PUSH (file, STRUCT_VALUE_REGNUM); +#endif +#endif + +#if 0 +#ifdef STATIC_CHAIN_INCOMING_REGNUM + if (cxt) + ASM_OUTPUT_REG_PUSH (file, STATIC_CHAIN_INCOMING_REGNUM); +#else +#ifdef STATIC_CHAIN_REGNUM + if (cxt) + ASM_OUTPUT_REG_PUSH (file, STATIC_CHAIN_REGNUM); +#endif +#endif +#endif /* 0 */ + + FUNCTION_PROFILER (file, profile_label_no); + +#if 0 +#ifdef STATIC_CHAIN_INCOMING_REGNUM + if (cxt) + ASM_OUTPUT_REG_POP (file, STATIC_CHAIN_INCOMING_REGNUM); +#else +#ifdef STATIC_CHAIN_REGNUM + if (cxt) + ASM_OUTPUT_REG_POP (file, STATIC_CHAIN_REGNUM); +#endif +#endif +#endif /* 0 */ + +#ifdef STRUCT_VALUE_INCOMING_REGNUM + if (sval) + ASM_OUTPUT_REG_POP (file, STRUCT_VALUE_INCOMING_REGNUM); +#else +#ifdef STRUCT_VALUE_REGNUM + if (sval) + ASM_OUTPUT_REG_POP (file, STRUCT_VALUE_REGNUM); +#endif +#endif +} + +/* Output assembler code for the end of a function. + For clarity, args are same as those of `final_start_function' + even though not all of them are needed. */ + +void +final_end_function (first, file, optimize) + rtx first; + FILE *file; + int optimize; +{ + if (app_on) + { + fprintf (file, ASM_APP_OFF); + app_on = 0; + } + +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG) + sdbout_end_function (last_linenum); +#endif + +#ifdef DWARF_DEBUGGING_INFO + if (write_symbols == DWARF_DEBUG) + dwarfout_end_function (); +#endif + +#ifdef XCOFF_DEBUGGING_INFO + if (write_symbols == XCOFF_DEBUG) + xcoffout_end_function (file, last_linenum); +#endif + +#ifdef FUNCTION_EPILOGUE + /* Finally, output the function epilogue: + code to restore the stack frame and return to the caller. */ + FUNCTION_EPILOGUE (file, get_frame_size ()); +#endif + +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG) + sdbout_end_epilogue (); +#endif + +#ifdef DWARF_DEBUGGING_INFO + if (write_symbols == DWARF_DEBUG) + dwarfout_end_epilogue (); +#endif + +#ifdef XCOFF_DEBUGGING_INFO + if (write_symbols == XCOFF_DEBUG) + xcoffout_end_epilogue (file); +#endif + + /* If FUNCTION_EPILOGUE is not defined, then the function body + itself contains return instructions wherever needed. */ +} + +/* Output assembler code for some insns: all or part of a function. + For description of args, see `final_start_function', above. + + PRESCAN is 1 if we are not really outputting, + just scanning as if we were outputting. + Prescanning deletes and rearranges insns just like ordinary output. + PRESCAN is -2 if we are outputting after having prescanned. + In this case, don't try to delete or rearrange insns + because that has already been done. + Prescanning is done only on certain machines. */ + +void +final (first, file, optimize, prescan) + rtx first; + FILE *file; + int optimize; + int prescan; +{ + register rtx insn; + int max_line = 0; + + last_ignored_compare = 0; + new_block = 1; + + /* Make a map indicating which line numbers appear in this function. + When producing SDB debugging info, delete troublesome line number + notes from inlined functions in other files as well as duplicate + line number notes. */ +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG) + { + rtx last = 0; + for (insn = first; insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0) + { + if ((RTX_INTEGRATED_P (insn) + && strcmp (NOTE_SOURCE_FILE (insn), main_input_filename) != 0) + || (last != 0 + && NOTE_LINE_NUMBER (insn) == NOTE_LINE_NUMBER (last) + && NOTE_SOURCE_FILE (insn) == NOTE_SOURCE_FILE (last))) + { + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + continue; + } + last = insn; + if (NOTE_LINE_NUMBER (insn) > max_line) + max_line = NOTE_LINE_NUMBER (insn); + } + } + else +#endif + { + for (insn = first; insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > max_line) + max_line = NOTE_LINE_NUMBER (insn); + } + + line_note_exists = (char *) oballoc (max_line + 1); + bzero (line_note_exists, max_line + 1); + + for (insn = first; insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0) + line_note_exists[NOTE_LINE_NUMBER (insn)] = 1; + + init_recog (); + + CC_STATUS_INIT; + + /* Output the insns. */ + for (insn = NEXT_INSN (first); insn;) + insn = final_scan_insn (insn, file, optimize, prescan, 0); + + /* Do basic-block profiling here + if the last insn was a conditional branch. */ + if (profile_block_flag && new_block) + { + new_block = 0; + /* Enable the table of basic-block use counts + to point at the code it applies to. */ + ASM_OUTPUT_INTERNAL_LABEL (file, "LPB", count_basic_blocks); + /* Before first insn of this basic block, increment the + count of times it was entered. */ +#ifdef BLOCK_PROFILER + BLOCK_PROFILER (file, count_basic_blocks); + CC_STATUS_INIT; +#endif + count_basic_blocks++; + } +} + +/* The final scan for one insn, INSN. + Args are same as in `final', except that INSN + is the insn being scanned. + Value returned is the next insn to be scanned. + + NOPEEPHOLES is the flag to disallow peephole processing (currently + used for within delayed branch sequence output). */ + +rtx +final_scan_insn (insn, file, optimize, prescan, nopeepholes) + rtx insn; + FILE *file; + int optimize; + int prescan; + int nopeepholes; +{ + register int i; + insn_counter++; + + /* Ignore deleted insns. These can occur when we split insns (due to a + template of "#") while not optimizing. */ + if (INSN_DELETED_P (insn)) + return NEXT_INSN (insn); + + switch (GET_CODE (insn)) + { + case NOTE: + if (prescan > 0) + break; + + /* Align the beginning of a loop, for higher speed + on certain machines. */ + + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG && optimize > 0) + { +#ifdef ASM_OUTPUT_LOOP_ALIGN + rtx next = next_nonnote_insn (insn); + if (next && GET_CODE (next) == CODE_LABEL) + { + ASM_OUTPUT_LOOP_ALIGN (asm_out_file); + } +#endif + break; + } + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END) + break; + + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_PROLOGUE_END) + { +#ifdef FUNCTION_END_PROLOGUE + FUNCTION_END_PROLOGUE (file); +#endif + profile_after_prologue (file); + break; + } + +#ifdef FUNCTION_BEGIN_EPILOGUE + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EPILOGUE_BEG) + { + FUNCTION_BEGIN_EPILOGUE (file); + break; + } +#endif + + if (write_symbols == NO_DEBUG) + break; + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG) + { +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG) + sdbout_begin_function (last_linenum); +#endif +#ifdef XCOFF_DEBUGGING_INFO + if (write_symbols == XCOFF_DEBUG) + xcoffout_begin_function (file, last_linenum); +#endif +#ifdef DWARF_DEBUGGING_INFO + if (write_symbols == DWARF_DEBUG) + dwarfout_begin_function (); +#endif + break; + } + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED) + break; /* An insn that was "deleted" */ + if (app_on) + { + fprintf (file, ASM_APP_OFF); + app_on = 0; + } + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG + && (debug_info_level == DINFO_LEVEL_NORMAL + || debug_info_level == DINFO_LEVEL_VERBOSE +#ifdef DWARF_DEBUGGING_INFO + || write_symbols == DWARF_DEBUG +#endif + ) + ) + { + /* Beginning of a symbol-block. Assign it a sequence number + and push the number onto the stack PENDING_BLOCKS. */ + + if (block_depth == max_block_depth) + { + /* PENDING_BLOCKS is full; make it longer. */ + max_block_depth *= 2; + pending_blocks + = (int *) xrealloc (pending_blocks, + max_block_depth * sizeof (int)); + } + pending_blocks[block_depth++] = next_block_index; + + /* Output debugging info about the symbol-block beginning. */ + +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG) + sdbout_begin_block (file, last_linenum, next_block_index); +#endif +#ifdef XCOFF_DEBUGGING_INFO + if (write_symbols == XCOFF_DEBUG) + xcoffout_begin_block (file, last_linenum, next_block_index); +#endif +#ifdef DBX_DEBUGGING_INFO + if (write_symbols == DBX_DEBUG) + ASM_OUTPUT_INTERNAL_LABEL (file, "LBB", next_block_index); +#endif +#ifdef DWARF_DEBUGGING_INFO + if (write_symbols == DWARF_DEBUG && block_depth > 1) + dwarfout_begin_block (next_block_index); +#endif + + next_block_index++; + } + else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END + && (debug_info_level == DINFO_LEVEL_NORMAL + || debug_info_level == DINFO_LEVEL_VERBOSE +#ifdef DWARF_DEBUGGING_INFO + || write_symbols == DWARF_DEBUG +#endif + ) + ) + { + /* End of a symbol-block. Pop its sequence number off + PENDING_BLOCKS and output debugging info based on that. */ + + --block_depth; + +#ifdef XCOFF_DEBUGGING_INFO + if (write_symbols == XCOFF_DEBUG && block_depth >= 0) + xcoffout_end_block (file, last_linenum, pending_blocks[block_depth]); +#endif +#ifdef DBX_DEBUGGING_INFO + if (write_symbols == DBX_DEBUG && block_depth >= 0) + ASM_OUTPUT_INTERNAL_LABEL (file, "LBE", + pending_blocks[block_depth]); +#endif +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG && block_depth >= 0) + sdbout_end_block (file, last_linenum); +#endif +#ifdef DWARF_DEBUGGING_INFO + if (write_symbols == DWARF_DEBUG && block_depth >= 1) + dwarfout_end_block (pending_blocks[block_depth]); +#endif + } + else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED_LABEL + && (debug_info_level == DINFO_LEVEL_NORMAL + || debug_info_level == DINFO_LEVEL_VERBOSE)) + { +#ifdef DWARF_DEBUGGING_INFO + if (write_symbols == DWARF_DEBUG) + dwarfout_label (insn); +#endif + } + else if (NOTE_LINE_NUMBER (insn) > 0) + /* This note is a line-number. */ + { + register rtx note; + +#if 0 /* This is what we used to do. */ + output_source_line (file, insn); +#endif + int note_after = 0; + + /* If there is anything real after this note, + output it. If another line note follows, omit this one. */ + for (note = NEXT_INSN (insn); note; note = NEXT_INSN (note)) + { + if (GET_CODE (note) != NOTE && GET_CODE (note) != CODE_LABEL) + break; + /* These types of notes can be significant + so make sure the preceding line number stays. */ + else if (GET_CODE (note) == NOTE + && (NOTE_LINE_NUMBER (note) == NOTE_INSN_BLOCK_BEG + || NOTE_LINE_NUMBER (note) == NOTE_INSN_BLOCK_END + || NOTE_LINE_NUMBER (note) == NOTE_INSN_FUNCTION_BEG)) + break; + else if (GET_CODE (note) == NOTE && NOTE_LINE_NUMBER (note) > 0) + { + /* Another line note follows; we can delete this note + if no intervening line numbers have notes elsewhere. */ + int num; + for (num = NOTE_LINE_NUMBER (insn) + 1; + num < NOTE_LINE_NUMBER (note); + num++) + if (line_note_exists[num]) + break; + + if (num >= NOTE_LINE_NUMBER (note)) + note_after = 1; + break; + } + } + + /* Output this line note + if it is the first or the last line note in a row. */ + if (!note_after) + output_source_line (file, insn); + } + break; + + case BARRIER: +#ifdef ASM_OUTPUT_ALIGN_CODE + /* Don't litter the assembler output with needless alignments. A + BARRIER will be placed at the end of every function if HAVE_epilogue + is true. */ + if (NEXT_INSN (insn)) + ASM_OUTPUT_ALIGN_CODE (file); +#endif + break; + + case CODE_LABEL: + CC_STATUS_INIT; + if (prescan > 0) + break; + new_block = 1; +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG && LABEL_NAME (insn)) + sdbout_label (insn); +#endif +#ifdef DWARF_DEBUGGING_INFO + if (write_symbols == DWARF_DEBUG && LABEL_NAME (insn)) + dwarfout_label (insn); +#endif + if (app_on) + { + fprintf (file, ASM_APP_OFF); + app_on = 0; + } + if (NEXT_INSN (insn) != 0 + && GET_CODE (NEXT_INSN (insn)) == JUMP_INSN) + { + rtx nextbody = PATTERN (NEXT_INSN (insn)); + + /* If this label is followed by a jump-table, + make sure we put the label in the read-only section. Also + possibly write the label and jump table together. */ + + if (GET_CODE (nextbody) == ADDR_VEC + || GET_CODE (nextbody) == ADDR_DIFF_VEC) + { +#ifndef JUMP_TABLES_IN_TEXT_SECTION + readonly_data_section (); +#ifdef READONLY_DATA_SECTION + ASM_OUTPUT_ALIGN (file, + exact_log2 (BIGGEST_ALIGNMENT + / BITS_PER_UNIT)); +#endif /* READONLY_DATA_SECTION */ +#else /* JUMP_TABLES_IN_TEXT_SECTION */ + text_section (); +#endif /* JUMP_TABLES_IN_TEXT_SECTION */ +#ifdef ASM_OUTPUT_CASE_LABEL + ASM_OUTPUT_CASE_LABEL (file, "L", CODE_LABEL_NUMBER (insn), + NEXT_INSN (insn)); +#else + ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (insn)); +#endif + break; + } + } + + ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (insn)); + break; + + default: + { + register rtx body = PATTERN (insn); + int insn_code_number; + char *template; + rtx note; + + /* An INSN, JUMP_INSN or CALL_INSN. + First check for special kinds that recog doesn't recognize. */ + + if (GET_CODE (body) == USE /* These are just declarations */ + || GET_CODE (body) == CLOBBER) + break; + +#ifdef HAVE_cc0 + /* If there is a REG_CC_SETTER note on this insn, it means that + the setting of the condition code was done in the delay slot + of the insn that branched here. So recover the cc status + from the insn that set it. */ + + note = find_reg_note (insn, REG_CC_SETTER, NULL_RTX); + if (note) + { + NOTICE_UPDATE_CC (PATTERN (XEXP (note, 0)), XEXP (note, 0)); + cc_prev_status = cc_status; + } +#endif + + /* Detect insns that are really jump-tables + and output them as such. */ + + if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC) + { + register int vlen, idx; + + if (prescan > 0) + break; + + if (app_on) + { + fprintf (file, ASM_APP_OFF); + app_on = 0; + } + + vlen = XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC); + for (idx = 0; idx < vlen; idx++) + { + if (GET_CODE (body) == ADDR_VEC) + { +#ifdef ASM_OUTPUT_ADDR_VEC_ELT + ASM_OUTPUT_ADDR_VEC_ELT + (file, CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 0, idx), 0))); +#else + abort (); +#endif + } + else + { +#ifdef ASM_OUTPUT_ADDR_DIFF_ELT + ASM_OUTPUT_ADDR_DIFF_ELT + (file, + CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 1, idx), 0)), + CODE_LABEL_NUMBER (XEXP (XEXP (body, 0), 0))); +#else + abort (); +#endif + } + } +#ifdef ASM_OUTPUT_CASE_END + ASM_OUTPUT_CASE_END (file, + CODE_LABEL_NUMBER (PREV_INSN (insn)), + insn); +#endif + + text_section (); + + break; + } + + /* Do basic-block profiling when we reach a new block. + Done here to avoid jump tables. */ + if (profile_block_flag && new_block) + { + new_block = 0; + /* Enable the table of basic-block use counts + to point at the code it applies to. */ + ASM_OUTPUT_INTERNAL_LABEL (file, "LPB", count_basic_blocks); + /* Before first insn of this basic block, increment the + count of times it was entered. */ +#ifdef BLOCK_PROFILER + BLOCK_PROFILER (file, count_basic_blocks); + CC_STATUS_INIT; +#endif + count_basic_blocks++; + } + + if (GET_CODE (body) == ASM_INPUT) + { + /* There's no telling what that did to the condition codes. */ + CC_STATUS_INIT; + if (prescan > 0) + break; + if (! app_on) + { + fprintf (file, ASM_APP_ON); + app_on = 1; + } + fprintf (asm_out_file, "\t%s\n", XSTR (body, 0)); + break; + } + + /* Detect `asm' construct with operands. */ + if (asm_noperands (body) >= 0) + { + int noperands = asm_noperands (body); + rtx *ops; + char *string; + + /* There's no telling what that did to the condition codes. */ + CC_STATUS_INIT; + if (prescan > 0) + break; + + /* alloca won't do here, since only return from `final' + would free it. */ + if (noperands > 0) + ops = (rtx *) xmalloc (noperands * sizeof (rtx)); + + if (! app_on) + { + fprintf (file, ASM_APP_ON); + app_on = 1; + } + + /* Get out the operand values. */ + string = decode_asm_operands (body, ops, NULL_PTR, + NULL_PTR, NULL_PTR); + /* Inhibit aborts on what would otherwise be compiler bugs. */ + insn_noperands = noperands; + this_is_asm_operands = insn; + /* Output the insn using them. */ + output_asm_insn (string, ops); + this_is_asm_operands = 0; + if (noperands > 0) + free (ops); + break; + } + + if (prescan <= 0 && app_on) + { + fprintf (file, ASM_APP_OFF); + app_on = 0; + } + + if (GET_CODE (body) == SEQUENCE) + { + /* A delayed-branch sequence */ + register int i; + rtx next; + + if (prescan > 0) + break; + final_sequence = body; + + /* The first insn in this SEQUENCE might be a JUMP_INSN that will + force the restoration of a comparison that was previously + thought unnecessary. If that happens, cancel this sequence + and cause that insn to be restored. */ + + next = final_scan_insn (XVECEXP (body, 0, 0), file, 0, prescan, 1); + if (next != XVECEXP (body, 0, 1)) + { + final_sequence = 0; + return next; + } + + for (i = 1; i < XVECLEN (body, 0); i++) + final_scan_insn (XVECEXP (body, 0, i), file, 0, prescan, 1); +#ifdef DBR_OUTPUT_SEQEND + DBR_OUTPUT_SEQEND (file); +#endif + final_sequence = 0; + + /* If the insn requiring the delay slot was a CALL_INSN, the + insns in the delay slot are actually executed before the + called function. Hence we don't preserve any CC-setting + actions in these insns and the CC must be marked as being + clobbered by the function. */ + if (GET_CODE (XVECEXP (body, 0, 0)) == CALL_INSN) + CC_STATUS_INIT; + + /* Following a conditional branch sequence, we have a new basic + block. */ + if (profile_block_flag) + { + rtx insn = XVECEXP (body, 0, 0); + rtx body = PATTERN (insn); + + if ((GET_CODE (insn) == JUMP_INSN && GET_CODE (body) == SET + && GET_CODE (SET_SRC (body)) != LABEL_REF) + || (GET_CODE (insn) == JUMP_INSN + && GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == SET + && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) != LABEL_REF)) + new_block = 1; + } + break; + } + + /* We have a real machine instruction as rtl. */ + + body = PATTERN (insn); + +#ifdef HAVE_cc0 + /* Check for redundant test and compare instructions + (when the condition codes are already set up as desired). + This is done only when optimizing; if not optimizing, + it should be possible for the user to alter a variable + with the debugger in between statements + and the next statement should reexamine the variable + to compute the condition codes. */ + + if (optimize + && GET_CODE (body) == SET + && GET_CODE (SET_DEST (body)) == CC0 + && insn != last_ignored_compare) + { + if (GET_CODE (SET_SRC (body)) == SUBREG) + SET_SRC (body) = alter_subreg (SET_SRC (body)); + else if (GET_CODE (SET_SRC (body)) == COMPARE) + { + if (GET_CODE (XEXP (SET_SRC (body), 0)) == SUBREG) + XEXP (SET_SRC (body), 0) + = alter_subreg (XEXP (SET_SRC (body), 0)); + if (GET_CODE (XEXP (SET_SRC (body), 1)) == SUBREG) + XEXP (SET_SRC (body), 1) + = alter_subreg (XEXP (SET_SRC (body), 1)); + } + if ((cc_status.value1 != 0 + && rtx_equal_p (SET_SRC (body), cc_status.value1)) + || (cc_status.value2 != 0 + && rtx_equal_p (SET_SRC (body), cc_status.value2))) + { + /* Don't delete insn if it has an addressing side-effect. */ + if (! FIND_REG_INC_NOTE (insn, 0) + /* or if anything in it is volatile. */ + && ! volatile_refs_p (PATTERN (insn))) + { + /* We don't really delete the insn; just ignore it. */ + last_ignored_compare = insn; + break; + } + } + } +#endif + + /* Following a conditional branch, we have a new basic block. + But if we are inside a sequence, the new block starts after the + last insn of the sequence. */ + if (profile_block_flag && final_sequence == 0 + && ((GET_CODE (insn) == JUMP_INSN && GET_CODE (body) == SET + && GET_CODE (SET_SRC (body)) != LABEL_REF) + || (GET_CODE (insn) == JUMP_INSN && GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == SET + && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) != LABEL_REF))) + new_block = 1; + +#ifndef STACK_REGS + /* Don't bother outputting obvious no-ops, even without -O. + This optimization is fast and doesn't interfere with debugging. + Don't do this if the insn is in a delay slot, since this + will cause an improper number of delay insns to be written. */ + if (final_sequence == 0 + && prescan >= 0 + && GET_CODE (insn) == INSN && GET_CODE (body) == SET + && GET_CODE (SET_SRC (body)) == REG + && GET_CODE (SET_DEST (body)) == REG + && REGNO (SET_SRC (body)) == REGNO (SET_DEST (body))) + break; +#endif + +#ifdef HAVE_cc0 + /* If this is a conditional branch, maybe modify it + if the cc's are in a nonstandard state + so that it accomplishes the same thing that it would + do straightforwardly if the cc's were set up normally. */ + + if (cc_status.flags != 0 + && GET_CODE (insn) == JUMP_INSN + && GET_CODE (body) == SET + && SET_DEST (body) == pc_rtx + && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE + /* This is done during prescan; it is not done again + in final scan when prescan has been done. */ + && prescan >= 0) + { + /* This function may alter the contents of its argument + and clear some of the cc_status.flags bits. + It may also return 1 meaning condition now always true + or -1 meaning condition now always false + or 2 meaning condition nontrivial but altered. */ + register int result = alter_cond (XEXP (SET_SRC (body), 0)); + /* If condition now has fixed value, replace the IF_THEN_ELSE + with its then-operand or its else-operand. */ + if (result == 1) + SET_SRC (body) = XEXP (SET_SRC (body), 1); + if (result == -1) + SET_SRC (body) = XEXP (SET_SRC (body), 2); + + /* The jump is now either unconditional or a no-op. + If it has become a no-op, don't try to output it. + (It would not be recognized.) */ + if (SET_SRC (body) == pc_rtx) + { + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + break; + } + else if (GET_CODE (SET_SRC (body)) == RETURN) + /* Replace (set (pc) (return)) with (return). */ + PATTERN (insn) = body = SET_SRC (body); + + /* Rerecognize the instruction if it has changed. */ + if (result != 0) + INSN_CODE (insn) = -1; + } + + /* Make same adjustments to instructions that examine the + condition codes without jumping (if this machine has them). */ + + if (cc_status.flags != 0 + && GET_CODE (body) == SET) + { + switch (GET_CODE (SET_SRC (body))) + { + case GTU: + case GT: + case LTU: + case LT: + case GEU: + case GE: + case LEU: + case LE: + case EQ: + case NE: + { + register int result; + if (XEXP (SET_SRC (body), 0) != cc0_rtx) + break; + result = alter_cond (SET_SRC (body)); + if (result == 1) + validate_change (insn, &SET_SRC (body), const_true_rtx, 0); + else if (result == -1) + validate_change (insn, &SET_SRC (body), const0_rtx, 0); + else if (result == 2) + INSN_CODE (insn) = -1; + } + } + } +#endif + + /* Do machine-specific peephole optimizations if desired. */ + + if (optimize && !flag_no_peephole && !nopeepholes) + { + rtx next = peephole (insn); + /* When peepholing, if there were notes within the peephole, + emit them before the peephole. */ + if (next != 0 && next != NEXT_INSN (insn)) + { + rtx prev = PREV_INSN (insn); + rtx note; + + for (note = NEXT_INSN (insn); note != next; + note = NEXT_INSN (note)) + final_scan_insn (note, file, optimize, prescan, nopeepholes); + + /* In case this is prescan, put the notes + in proper position for later rescan. */ + note = NEXT_INSN (insn); + PREV_INSN (note) = prev; + NEXT_INSN (prev) = note; + NEXT_INSN (PREV_INSN (next)) = insn; + PREV_INSN (insn) = PREV_INSN (next); + NEXT_INSN (insn) = next; + PREV_INSN (next) = insn; + } + + /* PEEPHOLE might have changed this. */ + body = PATTERN (insn); + } + + /* Try to recognize the instruction. + If successful, verify that the operands satisfy the + constraints for the instruction. Crash if they don't, + since `reload' should have changed them so that they do. */ + + insn_code_number = recog_memoized (insn); + insn_extract (insn); + for (i = 0; i < insn_n_operands[insn_code_number]; i++) + { + if (GET_CODE (recog_operand[i]) == SUBREG) + recog_operand[i] = alter_subreg (recog_operand[i]); + } + + for (i = 0; i < insn_n_dups[insn_code_number]; i++) + { + if (GET_CODE (*recog_dup_loc[i]) == SUBREG) + *recog_dup_loc[i] = alter_subreg (*recog_dup_loc[i]); + } + +#ifdef REGISTER_CONSTRAINTS + if (! constrain_operands (insn_code_number, 1)) + fatal_insn_not_found (insn); +#endif + + /* Some target machines need to prescan each insn before + it is output. */ + +#ifdef FINAL_PRESCAN_INSN + FINAL_PRESCAN_INSN (insn, recog_operand, + insn_n_operands[insn_code_number]); +#endif + +#ifdef HAVE_cc0 + cc_prev_status = cc_status; + + /* Update `cc_status' for this instruction. + The instruction's output routine may change it further. + If the output routine for a jump insn needs to depend + on the cc status, it should look at cc_prev_status. */ + + NOTICE_UPDATE_CC (body, insn); +#endif + + debug_insn = insn; + + /* If the proper template needs to be chosen by some C code, + run that code and get the real template. */ + + template = insn_template[insn_code_number]; + if (template == 0) + { + template = (*insn_outfun[insn_code_number]) (recog_operand, insn); + + /* If the C code returns 0, it means that it is a jump insn + which follows a deleted test insn, and that test insn + needs to be reinserted. */ + if (template == 0) + { + if (prev_nonnote_insn (insn) != last_ignored_compare) + abort (); + new_block = 0; + return prev_nonnote_insn (insn); + } + } + + /* If the template is the string "#", it means that this insn must + be split. */ + if (template[0] == '#' && template[1] == '\0') + { + rtx new = try_split (body, insn, 0); + + /* If we didn't split the insn, go away. */ + if (new == insn && PATTERN (new) == body) + abort (); + + new_block = 0; + return new; + } + + if (prescan > 0) + break; + + /* Output assembler code from the template. */ + + output_asm_insn (template, recog_operand); + +#if 0 + /* It's not at all clear why we did this and doing so interferes + with tests we'd like to do to use REG_WAS_0 notes, so let's try + with this out. */ + + /* Mark this insn as having been output. */ + INSN_DELETED_P (insn) = 1; +#endif + + debug_insn = 0; + } + } + return NEXT_INSN (insn); +} + +/* Output debugging info to the assembler file FILE + based on the NOTE-insn INSN, assumed to be a line number. */ + +static void +output_source_line (file, insn) + FILE *file; + rtx insn; +{ + char ltext_label_name[100]; + register char *filename = NOTE_SOURCE_FILE (insn); + + last_linenum = NOTE_LINE_NUMBER (insn); + + if (write_symbols != NO_DEBUG) + { +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG +#if 0 /* People like having line numbers even in wrong file! */ + /* COFF can't handle multiple source files--lose, lose. */ + && !strcmp (filename, main_input_filename) +#endif + /* COFF relative line numbers must be positive. */ + && last_linenum > sdb_begin_function_line) + { +#ifdef ASM_OUTPUT_SOURCE_LINE + ASM_OUTPUT_SOURCE_LINE (file, last_linenum); +#else + fprintf (file, "\t.ln\t%d\n", + ((sdb_begin_function_line > -1) + ? last_linenum - sdb_begin_function_line : 1)); +#endif + } +#endif + +#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) + if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) + dbxout_source_line (file, filename, NOTE_LINE_NUMBER (insn)); +#endif /* DBX_DEBUGGING_INFO || XCOFF_DEBUGGING_INFO */ + +#ifdef DWARF_DEBUGGING_INFO + if (write_symbols == DWARF_DEBUG) + dwarfout_line (filename, NOTE_LINE_NUMBER (insn)); +#endif + } +} + +/* If X is a SUBREG, replace it with a REG or a MEM, + based on the thing it is a subreg of. */ + +rtx +alter_subreg (x) + register rtx x; +{ + register rtx y = SUBREG_REG (x); + if (GET_CODE (y) == SUBREG) + y = alter_subreg (y); + + if (GET_CODE (y) == REG) + { + /* If the containing reg really gets a hard reg, so do we. */ + PUT_CODE (x, REG); + REGNO (x) = REGNO (y) + SUBREG_WORD (x); + } + else if (GET_CODE (y) == MEM) + { + register int offset = SUBREG_WORD (x) * UNITS_PER_WORD; +#if BYTES_BIG_ENDIAN + offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x))) + - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (y)))); +#endif + PUT_CODE (x, MEM); + MEM_VOLATILE_P (x) = MEM_VOLATILE_P (y); + XEXP (x, 0) = plus_constant (XEXP (y, 0), offset); + } + + return x; +} + +/* Do alter_subreg on all the SUBREGs contained in X. */ + +static rtx +walk_alter_subreg (x) + rtx x; +{ + switch (GET_CODE (x)) + { + case PLUS: + case MULT: + XEXP (x, 0) = walk_alter_subreg (XEXP (x, 0)); + XEXP (x, 1) = walk_alter_subreg (XEXP (x, 1)); + break; + + case MEM: + XEXP (x, 0) = walk_alter_subreg (XEXP (x, 0)); + break; + + case SUBREG: + return alter_subreg (x); + } + + return x; +} + +#ifdef HAVE_cc0 + +/* Given BODY, the body of a jump instruction, alter the jump condition + as required by the bits that are set in cc_status.flags. + Not all of the bits there can be handled at this level in all cases. + + The value is normally 0. + 1 means that the condition has become always true. + -1 means that the condition has become always false. + 2 means that COND has been altered. */ + +static int +alter_cond (cond) + register rtx cond; +{ + int value = 0; + + if (cc_status.flags & CC_REVERSED) + { + value = 2; + PUT_CODE (cond, swap_condition (GET_CODE (cond))); + } + + if (cc_status.flags & CC_INVERTED) + { + value = 2; + PUT_CODE (cond, reverse_condition (GET_CODE (cond))); + } + + if (cc_status.flags & CC_NOT_POSITIVE) + switch (GET_CODE (cond)) + { + case LE: + case LEU: + case GEU: + /* Jump becomes unconditional. */ + return 1; + + case GT: + case GTU: + case LTU: + /* Jump becomes no-op. */ + return -1; + + case GE: + PUT_CODE (cond, EQ); + value = 2; + break; + + case LT: + PUT_CODE (cond, NE); + value = 2; + break; + } + + if (cc_status.flags & CC_NOT_NEGATIVE) + switch (GET_CODE (cond)) + { + case GE: + case GEU: + /* Jump becomes unconditional. */ + return 1; + + case LT: + case LTU: + /* Jump becomes no-op. */ + return -1; + + case LE: + case LEU: + PUT_CODE (cond, EQ); + value = 2; + break; + + case GT: + case GTU: + PUT_CODE (cond, NE); + value = 2; + break; + } + + if (cc_status.flags & CC_NO_OVERFLOW) + switch (GET_CODE (cond)) + { + case GEU: + /* Jump becomes unconditional. */ + return 1; + + case LEU: + PUT_CODE (cond, EQ); + value = 2; + break; + + case GTU: + PUT_CODE (cond, NE); + value = 2; + break; + + case LTU: + /* Jump becomes no-op. */ + return -1; + } + + if (cc_status.flags & (CC_Z_IN_NOT_N | CC_Z_IN_N)) + switch (GET_CODE (cond)) + { + case LE: + case LEU: + case GE: + case GEU: + case LT: + case LTU: + case GT: + case GTU: + abort (); + + case NE: + PUT_CODE (cond, cc_status.flags & CC_Z_IN_N ? GE : LT); + value = 2; + break; + + case EQ: + PUT_CODE (cond, cc_status.flags & CC_Z_IN_N ? LT : GE); + value = 2; + break; + } + + if (cc_status.flags & CC_NOT_SIGNED) + /* The flags are valid if signed condition operators are converted + to unsigned. */ + switch (GET_CODE (cond)) + { + case LE: + PUT_CODE (cond, LEU); + value = 2; + break; + + case LT: + PUT_CODE (cond, LTU); + value = 2; + break; + + case GT: + PUT_CODE (cond, GTU); + value = 2; + break; + + case GE: + PUT_CODE (cond, GEU); + value = 2; + break; + } + + return value; +} +#endif + +/* Report inconsistency between the assembler template and the operands. + In an `asm', it's the user's fault; otherwise, the compiler's fault. */ + +void +output_operand_lossage (str) + char *str; +{ + if (this_is_asm_operands) + error_for_asm (this_is_asm_operands, "invalid `asm': %s", str); + else + abort (); +} + +/* Output of assembler code from a template, and its subroutines. */ + +/* Output text from TEMPLATE to the assembler output file, + obeying %-directions to substitute operands taken from + the vector OPERANDS. + + %N (for N a digit) means print operand N in usual manner. + %lN means require operand N to be a CODE_LABEL or LABEL_REF + and print the label name with no punctuation. + %cN means require operand N to be a constant + and print the constant expression with no punctuation. + %aN means expect operand N to be a memory address + (not a memory reference!) and print a reference + to that address. + %nN means expect operand N to be a constant + and print a constant expression for minus the value + of the operand, with no other punctuation. */ + +void +output_asm_insn (template, operands) + char *template; + rtx *operands; +{ + register char *p; + register int c; + + /* An insn may return a null string template + in a case where no assembler code is needed. */ + if (*template == 0) + return; + + p = template; + putc ('\t', asm_out_file); + +#ifdef ASM_OUTPUT_OPCODE + ASM_OUTPUT_OPCODE (asm_out_file, p); +#endif + + while (c = *p++) + { +#ifdef ASM_OUTPUT_OPCODE + if (c == '\n') + { + putc (c, asm_out_file); + while ((c = *p) == '\t') + { + putc (c, asm_out_file); + p++; + } + ASM_OUTPUT_OPCODE (asm_out_file, p); + } + else +#endif + if (c != '%') + putc (c, asm_out_file); + else + { + /* %% outputs a single %. */ + if (*p == '%') + { + p++; + putc (c, asm_out_file); + } + /* %= outputs a number which is unique to each insn in the entire + compilation. This is useful for making local labels that are + referred to more than once in a given insn. */ + else if (*p == '=') + { + p++; + fprintf (asm_out_file, "%d", insn_counter); + } + /* % followed by a letter and some digits + outputs an operand in a special way depending on the letter. + Letters `acln' are implemented directly. + Other letters are passed to `output_operand' so that + the PRINT_OPERAND macro can define them. */ + else if ((*p >= 'a' && *p <= 'z') + || (*p >= 'A' && *p <= 'Z')) + { + int letter = *p++; + c = atoi (p); + + if (! (*p >= '0' && *p <= '9')) + output_operand_lossage ("operand number missing after %-letter"); + else if (this_is_asm_operands && c >= (unsigned) insn_noperands) + output_operand_lossage ("operand number out of range"); + else if (letter == 'l') + output_asm_label (operands[c]); + else if (letter == 'a') + output_address (operands[c]); + else if (letter == 'c') + { + if (CONSTANT_ADDRESS_P (operands[c])) + output_addr_const (asm_out_file, operands[c]); + else + output_operand (operands[c], 'c'); + } + else if (letter == 'n') + { + if (GET_CODE (operands[c]) == CONST_INT) + fprintf (asm_out_file, +#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT + "%d", +#else + "%ld", +#endif + - INTVAL (operands[c])); + else + { + putc ('-', asm_out_file); + output_addr_const (asm_out_file, operands[c]); + } + } + else + output_operand (operands[c], letter); + + while ((c = *p) >= '0' && c <= '9') p++; + } + /* % followed by a digit outputs an operand the default way. */ + else if (*p >= '0' && *p <= '9') + { + c = atoi (p); + if (this_is_asm_operands && c >= (unsigned) insn_noperands) + output_operand_lossage ("operand number out of range"); + else + output_operand (operands[c], 0); + while ((c = *p) >= '0' && c <= '9') p++; + } + /* % followed by punctuation: output something for that + punctuation character alone, with no operand. + The PRINT_OPERAND macro decides what is actually done. */ +#ifdef PRINT_OPERAND_PUNCT_VALID_P + else if (PRINT_OPERAND_PUNCT_VALID_P (*p)) + output_operand (NULL_RTX, *p++); +#endif + else + output_operand_lossage ("invalid %%-code"); + } + } + + if (flag_print_asm_name) + { + /* Annotate the assembly with a comment describing the pattern and + alternative used. */ + if (debug_insn) + { + register int num = INSN_CODE (debug_insn); + fprintf (asm_out_file, " %s %d %s", + ASM_COMMENT_START, INSN_UID (debug_insn), insn_name[num]); + if (insn_n_alternatives[num] > 1) + fprintf (asm_out_file, "/%d", which_alternative + 1); + + /* Clear this so only the first assembler insn + of any rtl insn will get the special comment for -dp. */ + debug_insn = 0; + } + } + + putc ('\n', asm_out_file); +} + +/* Output a LABEL_REF, or a bare CODE_LABEL, as an assembler symbol. */ + +void +output_asm_label (x) + rtx x; +{ + char buf[256]; + + if (GET_CODE (x) == LABEL_REF) + ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (XEXP (x, 0))); + else if (GET_CODE (x) == CODE_LABEL) + ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x)); + else + output_operand_lossage ("`%l' operand isn't a label"); + + assemble_name (asm_out_file, buf); +} + +/* Print operand X using machine-dependent assembler syntax. + The macro PRINT_OPERAND is defined just to control this function. + CODE is a non-digit that preceded the operand-number in the % spec, + such as 'z' if the spec was `%z3'. CODE is 0 if there was no char + between the % and the digits. + When CODE is a non-letter, X is 0. + + The meanings of the letters are machine-dependent and controlled + by PRINT_OPERAND. */ + +static void +output_operand (x, code) + rtx x; + int code; +{ + if (x && GET_CODE (x) == SUBREG) + x = alter_subreg (x); + + /* If X is a pseudo-register, abort now rather than writing trash to the + assembler file. */ + + if (x && GET_CODE (x) == REG && REGNO (x) >= FIRST_PSEUDO_REGISTER) + abort (); + + PRINT_OPERAND (asm_out_file, x, code); +} + +/* Print a memory reference operand for address X + using machine-dependent assembler syntax. + The macro PRINT_OPERAND_ADDRESS exists just to control this function. */ + +void +output_address (x) + rtx x; +{ + walk_alter_subreg (x); + PRINT_OPERAND_ADDRESS (asm_out_file, x); +} + +/* Print an integer constant expression in assembler syntax. + Addition and subtraction are the only arithmetic + that may appear in these expressions. */ + +void +output_addr_const (file, x) + FILE *file; + rtx x; +{ + char buf[256]; + + restart: + switch (GET_CODE (x)) + { + case PC: + if (flag_pic) + putc ('.', file); + else + abort (); + break; + + case SYMBOL_REF: + assemble_name (file, XSTR (x, 0)); + break; + + case LABEL_REF: + ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (XEXP (x, 0))); + assemble_name (file, buf); + break; + + case CODE_LABEL: + ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x)); + assemble_name (file, buf); + break; + + case CONST_INT: + fprintf (file, +#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT + "%d", +#else + "%ld", +#endif + INTVAL (x)); + break; + + case CONST: + /* This used to output parentheses around the expression, + but that does not work on the 386 (either ATT or BSD assembler). */ + output_addr_const (file, XEXP (x, 0)); + break; + + case CONST_DOUBLE: + if (GET_MODE (x) == VOIDmode) + { + /* We can use %d if the number is one word and positive. */ + if (CONST_DOUBLE_HIGH (x)) + fprintf (file, +#if HOST_BITS_PER_WIDE_INT == 64 +#if HOST_BITS_PER_WIDE_INT != HOST_BITS_PER_INT + "0x%lx%016lx", +#else + "0x%x%016x", +#endif +#else +#if HOST_BITS_PER_WIDE_INT != HOST_BITS_PER_INT + "0x%lx%08lx", +#else + "0x%x%08x", +#endif +#endif + CONST_DOUBLE_HIGH (x), CONST_DOUBLE_LOW (x)); + else if (CONST_DOUBLE_LOW (x) < 0) + fprintf (file, +#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT + "0x%x", +#else + "0x%lx", +#endif + CONST_DOUBLE_LOW (x)); + else + fprintf (file, +#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT + "%d", +#else + "%ld", +#endif + CONST_DOUBLE_LOW (x)); + } + else + /* We can't handle floating point constants; + PRINT_OPERAND must handle them. */ + output_operand_lossage ("floating constant misused"); + break; + + case PLUS: + /* Some assemblers need integer constants to appear last (eg masm). */ + if (GET_CODE (XEXP (x, 0)) == CONST_INT) + { + output_addr_const (file, XEXP (x, 1)); + if (INTVAL (XEXP (x, 0)) >= 0) + fprintf (file, "+"); + output_addr_const (file, XEXP (x, 0)); + } + else + { + output_addr_const (file, XEXP (x, 0)); + if (INTVAL (XEXP (x, 1)) >= 0) + fprintf (file, "+"); + output_addr_const (file, XEXP (x, 1)); + } + break; + + case MINUS: + /* Avoid outputting things like x-x or x+5-x, + since some assemblers can't handle that. */ + x = simplify_subtraction (x); + if (GET_CODE (x) != MINUS) + goto restart; + + output_addr_const (file, XEXP (x, 0)); + fprintf (file, "-"); + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) < 0) + { + fprintf (file, ASM_OPEN_PAREN); + output_addr_const (file, XEXP (x, 1)); + fprintf (file, ASM_CLOSE_PAREN); + } + else + output_addr_const (file, XEXP (x, 1)); + break; + + case ZERO_EXTEND: + case SIGN_EXTEND: + output_addr_const (file, XEXP (x, 0)); + break; + + default: + output_operand_lossage ("invalid expression as operand"); + } +} + +/* A poor man's fprintf, with the added features of %I, %R, %L, and %U. + %R prints the value of REGISTER_PREFIX. + %L prints the value of LOCAL_LABEL_PREFIX. + %U prints the value of USER_LABEL_PREFIX. + %I prints the value of IMMEDIATE_PREFIX. + %O runs ASM_OUTPUT_OPCODE to transform what follows in the string. + Also supported are %d, %x, %s, %e, %f, %g and %%. */ + +void +asm_fprintf (va_alist) + va_dcl +{ + va_list argptr; + FILE *file; + char buf[10]; + char *p, *q, c; + + va_start (argptr); + + file = va_arg (argptr, FILE *); + p = va_arg (argptr, char *); + buf[0] = '%'; + + while (c = *p++) + switch (c) + { + case '%': + c = *p++; + q = &buf[1]; + while ((c >= '0' && c <= '9') || c == '.') + { + *q++ = c; + c = *p++; + } + switch (c) + { + case '%': + fprintf (file, "%%"); + break; + + case 'd': case 'i': case 'u': + case 'x': case 'p': case 'X': + case 'o': + *q++ = c; + *q = 0; + fprintf (file, buf, va_arg (argptr, int)); + break; + + case 'e': + case 'f': + case 'g': + *q++ = c; + *q = 0; + fprintf (file, buf, va_arg (argptr, double)); + break; + + case 's': + *q++ = c; + *q = 0; + fprintf (file, buf, va_arg (argptr, char *)); + break; + + case 'O': +#ifdef ASM_OUTPUT_OPCODE + ASM_OUTPUT_OPCODE (asm_out_file, p); +#endif + break; + + case 'R': +#ifdef REGISTER_PREFIX + fprintf (file, "%s", REGISTER_PREFIX); +#endif + break; + + case 'I': +#ifdef IMMEDIATE_PREFIX + fprintf (file, "%s", IMMEDIATE_PREFIX); +#endif + break; + + case 'L': +#ifdef LOCAL_LABEL_PREFIX + fprintf (file, "%s", LOCAL_LABEL_PREFIX); +#endif + break; + + case 'U': +#ifdef USER_LABEL_PREFIX + fprintf (file, "%s", USER_LABEL_PREFIX); +#endif + break; + + default: + abort (); + } + break; + + default: + fputc (c, file); + } +} + +/* Split up a CONST_DOUBLE or integer constant rtx + into two rtx's for single words, + storing in *FIRST the word that comes first in memory in the target + and in *SECOND the other. */ + +void +split_double (value, first, second) + rtx value; + rtx *first, *second; +{ + if (GET_CODE (value) == CONST_INT) + { + /* The rule for using CONST_INT for a wider mode + is that we regard the value as signed. + So sign-extend it. */ + rtx high = (INTVAL (value) < 0 ? constm1_rtx : const0_rtx); +#if WORDS_BIG_ENDIAN + *first = high; + *second = value; +#else + *first = value; + *second = high; +#endif + } + else if (GET_CODE (value) != CONST_DOUBLE) + { +#if WORDS_BIG_ENDIAN + *first = const0_rtx; + *second = value; +#else + *first = value; + *second = const0_rtx; +#endif + } + else if (GET_MODE (value) == VOIDmode + /* This is the old way we did CONST_DOUBLE integers. */ + || GET_MODE_CLASS (GET_MODE (value)) == MODE_INT) + { + /* In an integer, the words are defined as most and least significant. + So order them by the target's convention. */ +#if WORDS_BIG_ENDIAN + *first = GEN_INT (CONST_DOUBLE_HIGH (value)); + *second = GEN_INT (CONST_DOUBLE_LOW (value)); +#else + *first = GEN_INT (CONST_DOUBLE_LOW (value)); + *second = GEN_INT (CONST_DOUBLE_HIGH (value)); +#endif + } + else + { +#ifdef REAL_ARITHMETIC + REAL_VALUE_TYPE r; HOST_WIDE_INT l[2]; + REAL_VALUE_FROM_CONST_DOUBLE (r, value); + REAL_VALUE_TO_TARGET_DOUBLE (r, l); + *first = GEN_INT (l[0]); + *second = GEN_INT (l[1]); +#else + if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT + || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD) + && ! flag_pretend_float) + abort (); + +#if defined (HOST_WORDS_BIG_ENDIAN) == WORDS_BIG_ENDIAN + /* Host and target agree => no need to swap. */ + *first = GEN_INT (CONST_DOUBLE_LOW (value)); + *second = GEN_INT (CONST_DOUBLE_HIGH (value)); +#else + *second = GEN_INT (CONST_DOUBLE_LOW (value)); + *first = GEN_INT (CONST_DOUBLE_HIGH (value)); +#endif +#endif /* no REAL_ARITHMETIC */ + } +} + +/* Return nonzero if this function has no function calls. */ + +int +leaf_function_p () +{ + rtx insn; + + if (profile_flag || profile_block_flag) + return 0; + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == CALL_INSN) + return 0; + if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SEQUENCE + && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == CALL_INSN) + return 0; + } + for (insn = current_function_epilogue_delay_list; insn; insn = XEXP (insn, 1)) + { + if (GET_CODE (XEXP (insn, 0)) == CALL_INSN) + return 0; + if (GET_CODE (XEXP (insn, 0)) == INSN + && GET_CODE (PATTERN (XEXP (insn, 0))) == SEQUENCE + && GET_CODE (XVECEXP (PATTERN (XEXP (insn, 0)), 0, 0)) == CALL_INSN) + return 0; + } + + return 1; +} + +/* On some machines, a function with no call insns + can run faster if it doesn't create its own register window. + When output, the leaf function should use only the "output" + registers. Ordinarily, the function would be compiled to use + the "input" registers to find its arguments; it is a candidate + for leaf treatment if it uses only the "input" registers. + Leaf function treatment means renumbering so the function + uses the "output" registers instead. */ + +#ifdef LEAF_REGISTERS + +static char permitted_reg_in_leaf_functions[] = LEAF_REGISTERS; + +/* Return 1 if this function uses only the registers that can be + safely renumbered. */ + +int +only_leaf_regs_used () +{ + int i; + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + if ((regs_ever_live[i] || global_regs[i]) + && ! permitted_reg_in_leaf_functions[i]) + return 0; + } + return 1; +} + +/* Scan all instructions and renumber all registers into those + available in leaf functions. */ + +static void +leaf_renumber_regs (first) + rtx first; +{ + rtx insn; + + /* Renumber only the actual patterns. + The reg-notes can contain frame pointer refs, + and renumbering them could crash, and should not be needed. */ + for (insn = first; insn; insn = NEXT_INSN (insn)) + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + leaf_renumber_regs_insn (PATTERN (insn)); + for (insn = current_function_epilogue_delay_list; insn; insn = XEXP (insn, 1)) + if (GET_RTX_CLASS (GET_CODE (XEXP (insn, 0))) == 'i') + leaf_renumber_regs_insn (PATTERN (XEXP (insn, 0))); +} + +/* Scan IN_RTX and its subexpressions, and renumber all regs into those + available in leaf functions. */ + +void +leaf_renumber_regs_insn (in_rtx) + register rtx in_rtx; +{ + register int i, j; + register char *format_ptr; + + if (in_rtx == 0) + return; + + /* Renumber all input-registers into output-registers. + renumbered_regs would be 1 for an output-register; + they */ + + if (GET_CODE (in_rtx) == REG) + { + int newreg; + + /* Don't renumber the same reg twice. */ + if (in_rtx->used) + return; + + newreg = REGNO (in_rtx); + /* Don't try to renumber pseudo regs. It is possible for a pseudo reg + to reach here as part of a REG_NOTE. */ + if (newreg >= FIRST_PSEUDO_REGISTER) + { + in_rtx->used = 1; + return; + } + newreg = LEAF_REG_REMAP (newreg); + if (newreg < 0) + abort (); + regs_ever_live[REGNO (in_rtx)] = 0; + regs_ever_live[newreg] = 1; + REGNO (in_rtx) = newreg; + in_rtx->used = 1; + } + + if (GET_RTX_CLASS (GET_CODE (in_rtx)) == 'i') + { + /* Inside a SEQUENCE, we find insns. + Renumber just the patterns of these insns, + just as we do for the top-level insns. */ + leaf_renumber_regs_insn (PATTERN (in_rtx)); + return; + } + + format_ptr = GET_RTX_FORMAT (GET_CODE (in_rtx)); + + for (i = 0; i < GET_RTX_LENGTH (GET_CODE (in_rtx)); i++) + switch (*format_ptr++) + { + case 'e': + leaf_renumber_regs_insn (XEXP (in_rtx, i)); + break; + + case 'E': + if (NULL != XVEC (in_rtx, i)) + { + for (j = 0; j < XVECLEN (in_rtx, i); j++) + leaf_renumber_regs_insn (XVECEXP (in_rtx, i, j)); + } + break; + + case 'S': + case 's': + case '0': + case 'i': + case 'w': + case 'n': + case 'u': + break; + + default: + abort (); + } +} +#endif diff --git a/gnu/usr.bin/cc/common/flags.h b/gnu/usr.bin/cc/lib/flags.h similarity index 100% rename from gnu/usr.bin/cc/common/flags.h rename to gnu/usr.bin/cc/lib/flags.h diff --git a/gnu/usr.bin/cc/common/flow.c b/gnu/usr.bin/cc/lib/flow.c similarity index 100% rename from gnu/usr.bin/cc/common/flow.c rename to gnu/usr.bin/cc/lib/flow.c diff --git a/gnu/usr.bin/cc/common/fold-const.c b/gnu/usr.bin/cc/lib/fold-const.c similarity index 100% rename from gnu/usr.bin/cc/common/fold-const.c rename to gnu/usr.bin/cc/lib/fold-const.c diff --git a/gnu/usr.bin/cc/lib/function.c b/gnu/usr.bin/cc/lib/function.c new file mode 100644 index 0000000000..f9d851713f --- /dev/null +++ b/gnu/usr.bin/cc/lib/function.c @@ -0,0 +1,4913 @@ +/* Expands front end tree to back end RTL for GNU C-Compiler + Copyright (C) 1987, 88, 89, 91, 92, 1993 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* This file handles the generation of rtl code from tree structure + at the level of the function as a whole. + It creates the rtl expressions for parameters and auto variables + and has full responsibility for allocating stack slots. + + `expand_function_start' is called at the beginning of a function, + before the function body is parsed, and `expand_function_end' is + called after parsing the body. + + Call `assign_stack_local' to allocate a stack slot for a local variable. + This is usually done during the RTL generation for the function body, + but it can also be done in the reload pass when a pseudo-register does + not get a hard register. + + Call `put_var_into_stack' when you learn, belatedly, that a variable + previously given a pseudo-register must in fact go in the stack. + This function changes the DECL_RTL to be a stack slot instead of a reg + then scans all the RTL instructions so far generated to correct them. */ + +#include "config.h" + +#include + +#include "rtl.h" +#include "tree.h" +#include "flags.h" +#include "function.h" +#include "insn-flags.h" +#include "expr.h" +#include "insn-codes.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "insn-config.h" +#include "recog.h" +#include "output.h" +#include "basic-block.h" + +/* Round a value to the lowest integer less than it that is a multiple of + the required alignment. Avoid using division in case the value is + negative. Assume the alignment is a power of two. */ +#define FLOOR_ROUND(VALUE,ALIGN) ((VALUE) & ~((ALIGN) - 1)) + +/* Similar, but round to the next highest integer that meets the + alignment. */ +#define CEIL_ROUND(VALUE,ALIGN) (((VALUE) + (ALIGN) - 1) & ~((ALIGN)- 1)) + +/* NEED_SEPARATE_AP means that we cannot derive ap from the value of fp + during rtl generation. If they are different register numbers, this is + always true. It may also be true if + FIRST_PARM_OFFSET - STARTING_FRAME_OFFSET is not a constant during rtl + generation. See fix_lexical_addr for details. */ + +#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM +#define NEED_SEPARATE_AP +#endif + +/* Number of bytes of args popped by function being compiled on its return. + Zero if no bytes are to be popped. + May affect compilation of return insn or of function epilogue. */ + +int current_function_pops_args; + +/* Nonzero if function being compiled needs to be given an address + where the value should be stored. */ + +int current_function_returns_struct; + +/* Nonzero if function being compiled needs to + return the address of where it has put a structure value. */ + +int current_function_returns_pcc_struct; + +/* Nonzero if function being compiled needs to be passed a static chain. */ + +int current_function_needs_context; + +/* Nonzero if function being compiled can call setjmp. */ + +int current_function_calls_setjmp; + +/* Nonzero if function being compiled can call longjmp. */ + +int current_function_calls_longjmp; + +/* Nonzero if function being compiled receives nonlocal gotos + from nested functions. */ + +int current_function_has_nonlocal_label; + +/* Nonzero if function being compiled contains nested functions. */ + +int current_function_contains_functions; + +/* Nonzero if function being compiled can call alloca, + either as a subroutine or builtin. */ + +int current_function_calls_alloca; + +/* Nonzero if the current function returns a pointer type */ + +int current_function_returns_pointer; + +/* If some insns can be deferred to the delay slots of the epilogue, the + delay list for them is recorded here. */ + +rtx current_function_epilogue_delay_list; + +/* If function's args have a fixed size, this is that size, in bytes. + Otherwise, it is -1. + May affect compilation of return insn or of function epilogue. */ + +int current_function_args_size; + +/* # bytes the prologue should push and pretend that the caller pushed them. + The prologue must do this, but only if parms can be passed in registers. */ + +int current_function_pretend_args_size; + +/* # of bytes of outgoing arguments required to be pushed by the prologue. + If this is non-zero, it means that ACCUMULATE_OUTGOING_ARGS was defined + and no stack adjusts will be done on function calls. */ + +int current_function_outgoing_args_size; + +/* This is the offset from the arg pointer to the place where the first + anonymous arg can be found, if there is one. */ + +rtx current_function_arg_offset_rtx; + +/* Nonzero if current function uses varargs.h or equivalent. + Zero for functions that use stdarg.h. */ + +int current_function_varargs; + +/* Quantities of various kinds of registers + used for the current function's args. */ + +CUMULATIVE_ARGS current_function_args_info; + +/* Name of function now being compiled. */ + +char *current_function_name; + +/* If non-zero, an RTL expression for that location at which the current + function returns its result. Always equal to + DECL_RTL (DECL_RESULT (current_function_decl)), but provided + independently of the tree structures. */ + +rtx current_function_return_rtx; + +/* Nonzero if the current function uses the constant pool. */ + +int current_function_uses_const_pool; + +/* Nonzero if the current function uses pic_offset_table_rtx. */ +int current_function_uses_pic_offset_table; + +/* The arg pointer hard register, or the pseudo into which it was copied. */ +rtx current_function_internal_arg_pointer; + +/* The FUNCTION_DECL for an inline function currently being expanded. */ +tree inline_function_decl; + +/* Number of function calls seen so far in current function. */ + +int function_call_count; + +/* List (chain of TREE_LIST) of LABEL_DECLs for all nonlocal labels + (labels to which there can be nonlocal gotos from nested functions) + in this function. */ + +tree nonlocal_labels; + +/* RTX for stack slot that holds the current handler for nonlocal gotos. + Zero when function does not have nonlocal labels. */ + +rtx nonlocal_goto_handler_slot; + +/* RTX for stack slot that holds the stack pointer value to restore + for a nonlocal goto. + Zero when function does not have nonlocal labels. */ + +rtx nonlocal_goto_stack_level; + +/* Label that will go on parm cleanup code, if any. + Jumping to this label runs cleanup code for parameters, if + such code must be run. Following this code is the logical return label. */ + +rtx cleanup_label; + +/* Label that will go on function epilogue. + Jumping to this label serves as a "return" instruction + on machines which require execution of the epilogue on all returns. */ + +rtx return_label; + +/* List (chain of EXPR_LISTs) of pseudo-regs of SAVE_EXPRs. + So we can mark them all live at the end of the function, if nonopt. */ +rtx save_expr_regs; + +/* List (chain of EXPR_LISTs) of all stack slots in this function. + Made for the sake of unshare_all_rtl. */ +rtx stack_slot_list; + +/* Chain of all RTL_EXPRs that have insns in them. */ +tree rtl_expr_chain; + +/* Label to jump back to for tail recursion, or 0 if we have + not yet needed one for this function. */ +rtx tail_recursion_label; + +/* Place after which to insert the tail_recursion_label if we need one. */ +rtx tail_recursion_reentry; + +/* Location at which to save the argument pointer if it will need to be + referenced. There are two cases where this is done: if nonlocal gotos + exist, or if vars stored at an offset from the argument pointer will be + needed by inner routines. */ + +rtx arg_pointer_save_area; + +/* Offset to end of allocated area of stack frame. + If stack grows down, this is the address of the last stack slot allocated. + If stack grows up, this is the address for the next slot. */ +int frame_offset; + +/* List (chain of TREE_LISTs) of static chains for containing functions. + Each link has a FUNCTION_DECL in the TREE_PURPOSE and a reg rtx + in an RTL_EXPR in the TREE_VALUE. */ +static tree context_display; + +/* List (chain of TREE_LISTs) of trampolines for nested functions. + The trampoline sets up the static chain and jumps to the function. + We supply the trampoline's address when the function's address is requested. + + Each link has a FUNCTION_DECL in the TREE_PURPOSE and a reg rtx + in an RTL_EXPR in the TREE_VALUE. */ +static tree trampoline_list; + +/* Insn after which register parms and SAVE_EXPRs are born, if nonopt. */ +static rtx parm_birth_insn; + +#if 0 +/* Nonzero if a stack slot has been generated whose address is not + actually valid. It means that the generated rtl must all be scanned + to detect and correct the invalid addresses where they occur. */ +static int invalid_stack_slot; +#endif + +/* Last insn of those whose job was to put parms into their nominal homes. */ +static rtx last_parm_insn; + +/* 1 + last pseudo register number used for loading a copy + of a parameter of this function. */ +static int max_parm_reg; + +/* Vector indexed by REGNO, containing location on stack in which + to put the parm which is nominally in pseudo register REGNO, + if we discover that that parm must go in the stack. */ +static rtx *parm_reg_stack_loc; + +#if 0 /* Turned off because 0 seems to work just as well. */ +/* Cleanup lists are required for binding levels regardless of whether + that binding level has cleanups or not. This node serves as the + cleanup list whenever an empty list is required. */ +static tree empty_cleanup_list; +#endif + +/* Nonzero once virtual register instantiation has been done. + assign_stack_local uses frame_pointer_rtx when this is nonzero. */ +static int virtuals_instantiated; + +/* Nonzero if we need to distinguish between the return value of this function + and the return value of a function called by this function. This helps + integrate.c */ + +extern int rtx_equal_function_value_matters; + +void fixup_gotos (); + +static tree round_down (); +static rtx round_trampoline_addr (); +static rtx fixup_stack_1 (); +static void fixup_var_refs (); +static void fixup_var_refs_insns (); +static void fixup_var_refs_1 (); +static void optimize_bit_field (); +static void instantiate_decls (); +static void instantiate_decls_1 (); +static void instantiate_decl (); +static int instantiate_virtual_regs_1 (); +static rtx fixup_memory_subreg (); +static rtx walk_fixup_memory_subreg (); + +/* In order to evaluate some expressions, such as function calls returning + structures in memory, we need to temporarily allocate stack locations. + We record each allocated temporary in the following structure. + + Associated with each temporary slot is a nesting level. When we pop up + one level, all temporaries associated with the previous level are freed. + Normally, all temporaries are freed after the execution of the statement + in which they were created. However, if we are inside a ({...}) grouping, + the result may be in a temporary and hence must be preserved. If the + result could be in a temporary, we preserve it if we can determine which + one it is in. If we cannot determine which temporary may contain the + result, all temporaries are preserved. A temporary is preserved by + pretending it was allocated at the previous nesting level. + + Automatic variables are also assigned temporary slots, at the nesting + level where they are defined. They are marked a "kept" so that + free_temp_slots will not free them. */ + +struct temp_slot +{ + /* Points to next temporary slot. */ + struct temp_slot *next; + /* The rtx to used to reference the slot. */ + rtx slot; + /* The size, in units, of the slot. */ + int size; + /* Non-zero if this temporary is currently in use. */ + char in_use; + /* Nesting level at which this slot is being used. */ + int level; + /* Non-zero if this should survive a call to free_temp_slots. */ + int keep; +}; + +/* List of all temporaries allocated, both available and in use. */ + +struct temp_slot *temp_slots; + +/* Current nesting level for temporaries. */ + +int temp_slot_level; + +/* Pointer to chain of `struct function' for containing functions. */ +struct function *outer_function_chain; + +/* Given a function decl for a containing function, + return the `struct function' for it. */ + +struct function * +find_function_data (decl) + tree decl; +{ + struct function *p; + for (p = outer_function_chain; p; p = p->next) + if (p->decl == decl) + return p; + abort (); +} + +/* Save the current context for compilation of a nested function. + This is called from language-specific code. + The caller is responsible for saving any language-specific status, + since this function knows only about language-independent variables. */ + +void +push_function_context () +{ + struct function *p = (struct function *) xmalloc (sizeof (struct function)); + + p->next = outer_function_chain; + outer_function_chain = p; + + p->name = current_function_name; + p->decl = current_function_decl; + p->pops_args = current_function_pops_args; + p->returns_struct = current_function_returns_struct; + p->returns_pcc_struct = current_function_returns_pcc_struct; + p->needs_context = current_function_needs_context; + p->calls_setjmp = current_function_calls_setjmp; + p->calls_longjmp = current_function_calls_longjmp; + p->calls_alloca = current_function_calls_alloca; + p->has_nonlocal_label = current_function_has_nonlocal_label; + p->args_size = current_function_args_size; + p->pretend_args_size = current_function_pretend_args_size; + p->arg_offset_rtx = current_function_arg_offset_rtx; + p->uses_const_pool = current_function_uses_const_pool; + p->uses_pic_offset_table = current_function_uses_pic_offset_table; + p->internal_arg_pointer = current_function_internal_arg_pointer; + p->max_parm_reg = max_parm_reg; + p->parm_reg_stack_loc = parm_reg_stack_loc; + p->outgoing_args_size = current_function_outgoing_args_size; + p->return_rtx = current_function_return_rtx; + p->nonlocal_goto_handler_slot = nonlocal_goto_handler_slot; + p->nonlocal_goto_stack_level = nonlocal_goto_stack_level; + p->nonlocal_labels = nonlocal_labels; + p->cleanup_label = cleanup_label; + p->return_label = return_label; + p->save_expr_regs = save_expr_regs; + p->stack_slot_list = stack_slot_list; + p->parm_birth_insn = parm_birth_insn; + p->frame_offset = frame_offset; + p->tail_recursion_label = tail_recursion_label; + p->tail_recursion_reentry = tail_recursion_reentry; + p->arg_pointer_save_area = arg_pointer_save_area; + p->rtl_expr_chain = rtl_expr_chain; + p->last_parm_insn = last_parm_insn; + p->context_display = context_display; + p->trampoline_list = trampoline_list; + p->function_call_count = function_call_count; + p->temp_slots = temp_slots; + p->temp_slot_level = temp_slot_level; + p->fixup_var_refs_queue = 0; + p->epilogue_delay_list = current_function_epilogue_delay_list; + + save_tree_status (p); + save_storage_status (p); + save_emit_status (p); + init_emit (); + save_expr_status (p); + save_stmt_status (p); + save_varasm_status (p); +} + +/* Restore the last saved context, at the end of a nested function. + This function is called from language-specific code. */ + +void +pop_function_context () +{ + struct function *p = outer_function_chain; + + outer_function_chain = p->next; + + current_function_name = p->name; + current_function_decl = p->decl; + current_function_pops_args = p->pops_args; + current_function_returns_struct = p->returns_struct; + current_function_returns_pcc_struct = p->returns_pcc_struct; + current_function_needs_context = p->needs_context; + current_function_calls_setjmp = p->calls_setjmp; + current_function_calls_longjmp = p->calls_longjmp; + current_function_calls_alloca = p->calls_alloca; + current_function_has_nonlocal_label = p->has_nonlocal_label; + current_function_contains_functions = 1; + current_function_args_size = p->args_size; + current_function_pretend_args_size = p->pretend_args_size; + current_function_arg_offset_rtx = p->arg_offset_rtx; + current_function_uses_const_pool = p->uses_const_pool; + current_function_uses_pic_offset_table = p->uses_pic_offset_table; + current_function_internal_arg_pointer = p->internal_arg_pointer; + max_parm_reg = p->max_parm_reg; + parm_reg_stack_loc = p->parm_reg_stack_loc; + current_function_outgoing_args_size = p->outgoing_args_size; + current_function_return_rtx = p->return_rtx; + nonlocal_goto_handler_slot = p->nonlocal_goto_handler_slot; + nonlocal_goto_stack_level = p->nonlocal_goto_stack_level; + nonlocal_labels = p->nonlocal_labels; + cleanup_label = p->cleanup_label; + return_label = p->return_label; + save_expr_regs = p->save_expr_regs; + stack_slot_list = p->stack_slot_list; + parm_birth_insn = p->parm_birth_insn; + frame_offset = p->frame_offset; + tail_recursion_label = p->tail_recursion_label; + tail_recursion_reentry = p->tail_recursion_reentry; + arg_pointer_save_area = p->arg_pointer_save_area; + rtl_expr_chain = p->rtl_expr_chain; + last_parm_insn = p->last_parm_insn; + context_display = p->context_display; + trampoline_list = p->trampoline_list; + function_call_count = p->function_call_count; + temp_slots = p->temp_slots; + temp_slot_level = p->temp_slot_level; + current_function_epilogue_delay_list = p->epilogue_delay_list; + + restore_tree_status (p); + restore_storage_status (p); + restore_expr_status (p); + restore_emit_status (p); + restore_stmt_status (p); + restore_varasm_status (p); + + /* Finish doing put_var_into_stack for any of our variables + which became addressable during the nested function. */ + { + struct var_refs_queue *queue = p->fixup_var_refs_queue; + for (; queue; queue = queue->next) + fixup_var_refs (queue->modified, queue->promoted_mode, queue->unsignedp); + } + + free (p); + + /* Reset variables that have known state during rtx generation. */ + rtx_equal_function_value_matters = 1; + virtuals_instantiated = 0; +} + +/* Allocate fixed slots in the stack frame of the current function. */ + +/* Return size needed for stack frame based on slots so far allocated. + This size counts from zero. It is not rounded to STACK_BOUNDARY; + the caller may have to do that. */ + +int +get_frame_size () +{ +#ifdef FRAME_GROWS_DOWNWARD + return -frame_offset; +#else + return frame_offset; +#endif +} + +/* Allocate a stack slot of SIZE bytes and return a MEM rtx for it + with machine mode MODE. + + ALIGN controls the amount of alignment for the address of the slot: + 0 means according to MODE, + -1 means use BIGGEST_ALIGNMENT and round size to multiple of that, + positive specifies alignment boundary in bits. + + We do not round to stack_boundary here. */ + +rtx +assign_stack_local (mode, size, align) + enum machine_mode mode; + int size; + int align; +{ + register rtx x, addr; + int bigend_correction = 0; + int alignment; + + if (align == 0) + { + alignment = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; + if (mode == BLKmode) + alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT; + } + else if (align == -1) + { + alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT; + size = CEIL_ROUND (size, alignment); + } + else + alignment = align / BITS_PER_UNIT; + + /* Round frame offset to that alignment. + We must be careful here, since FRAME_OFFSET might be negative and + division with a negative dividend isn't as well defined as we might + like. So we instead assume that ALIGNMENT is a power of two and + use logical operations which are unambiguous. */ +#ifdef FRAME_GROWS_DOWNWARD + frame_offset = FLOOR_ROUND (frame_offset, alignment); +#else + frame_offset = CEIL_ROUND (frame_offset, alignment); +#endif + + /* On a big-endian machine, if we are allocating more space than we will use, + use the least significant bytes of those that are allocated. */ +#if BYTES_BIG_ENDIAN + if (mode != BLKmode) + bigend_correction = size - GET_MODE_SIZE (mode); +#endif + +#ifdef FRAME_GROWS_DOWNWARD + frame_offset -= size; +#endif + + /* If we have already instantiated virtual registers, return the actual + address relative to the frame pointer. */ + if (virtuals_instantiated) + addr = plus_constant (frame_pointer_rtx, + (frame_offset + bigend_correction + + STARTING_FRAME_OFFSET)); + else + addr = plus_constant (virtual_stack_vars_rtx, + frame_offset + bigend_correction); + +#ifndef FRAME_GROWS_DOWNWARD + frame_offset += size; +#endif + + x = gen_rtx (MEM, mode, addr); + + stack_slot_list = gen_rtx (EXPR_LIST, VOIDmode, x, stack_slot_list); + + return x; +} + +/* Assign a stack slot in a containing function. + First three arguments are same as in preceding function. + The last argument specifies the function to allocate in. */ + +rtx +assign_outer_stack_local (mode, size, align, function) + enum machine_mode mode; + int size; + int align; + struct function *function; +{ + register rtx x, addr; + int bigend_correction = 0; + int alignment; + + /* Allocate in the memory associated with the function in whose frame + we are assigning. */ + push_obstacks (function->function_obstack, + function->function_maybepermanent_obstack); + + if (align == 0) + { + alignment = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT; + if (mode == BLKmode) + alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT; + } + else if (align == -1) + { + alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT; + size = CEIL_ROUND (size, alignment); + } + else + alignment = align / BITS_PER_UNIT; + + /* Round frame offset to that alignment. */ +#ifdef FRAME_GROWS_DOWNWARD + function->frame_offset = FLOOR_ROUND (function->frame_offset, alignment); +#else + function->frame_offset = CEIL_ROUND (function->frame_offset, alignment); +#endif + + /* On a big-endian machine, if we are allocating more space than we will use, + use the least significant bytes of those that are allocated. */ +#if BYTES_BIG_ENDIAN + if (mode != BLKmode) + bigend_correction = size - GET_MODE_SIZE (mode); +#endif + +#ifdef FRAME_GROWS_DOWNWARD + function->frame_offset -= size; +#endif + addr = plus_constant (virtual_stack_vars_rtx, + function->frame_offset + bigend_correction); +#ifndef FRAME_GROWS_DOWNWARD + function->frame_offset += size; +#endif + + x = gen_rtx (MEM, mode, addr); + + function->stack_slot_list + = gen_rtx (EXPR_LIST, VOIDmode, x, function->stack_slot_list); + + pop_obstacks (); + + return x; +} + +/* Allocate a temporary stack slot and record it for possible later + reuse. + + MODE is the machine mode to be given to the returned rtx. + + SIZE is the size in units of the space required. We do no rounding here + since assign_stack_local will do any required rounding. + + KEEP is non-zero if this slot is to be retained after a call to + free_temp_slots. Automatic variables for a block are allocated with this + flag. */ + +rtx +assign_stack_temp (mode, size, keep) + enum machine_mode mode; + int size; + int keep; +{ + struct temp_slot *p, *best_p = 0; + + /* First try to find an available, already-allocated temporary that is the + exact size we require. */ + for (p = temp_slots; p; p = p->next) + if (p->size == size && GET_MODE (p->slot) == mode && ! p->in_use) + break; + + /* If we didn't find, one, try one that is larger than what we want. We + find the smallest such. */ + if (p == 0) + for (p = temp_slots; p; p = p->next) + if (p->size > size && GET_MODE (p->slot) == mode && ! p->in_use + && (best_p == 0 || best_p->size > p->size)) + best_p = p; + + /* Make our best, if any, the one to use. */ + if (best_p) + p = best_p; + + /* If we still didn't find one, make a new temporary. */ + if (p == 0) + { + p = (struct temp_slot *) oballoc (sizeof (struct temp_slot)); + p->size = size; + /* If the temp slot mode doesn't indicate the alignment, + use the largest possible, so no one will be disappointed. */ + p->slot = assign_stack_local (mode, size, mode == BLKmode ? -1 : 0); + p->next = temp_slots; + temp_slots = p; + } + + p->in_use = 1; + p->level = temp_slot_level; + p->keep = keep; + return p->slot; +} + +/* If X could be a reference to a temporary slot, mark that slot as belonging + to the to one level higher. If X matched one of our slots, just mark that + one. Otherwise, we can't easily predict which it is, so upgrade all of + them. Kept slots need not be touched. + + This is called when an ({...}) construct occurs and a statement + returns a value in memory. */ + +void +preserve_temp_slots (x) + rtx x; +{ + struct temp_slot *p; + + /* If X is not in memory or is at a constant address, it cannot be in + a temporary slot. */ + if (x == 0 || GET_CODE (x) != MEM || CONSTANT_P (XEXP (x, 0))) + return; + + /* First see if we can find a match. */ + for (p = temp_slots; p; p = p->next) + if (p->in_use && x == p->slot) + { + p->level--; + return; + } + + /* Otherwise, preserve all non-kept slots at this level. */ + for (p = temp_slots; p; p = p->next) + if (p->in_use && p->level == temp_slot_level && ! p->keep) + p->level--; +} + +/* Free all temporaries used so far. This is normally called at the end + of generating code for a statement. */ + +void +free_temp_slots () +{ + struct temp_slot *p; + + for (p = temp_slots; p; p = p->next) + if (p->in_use && p->level == temp_slot_level && ! p->keep) + p->in_use = 0; +} + +/* Push deeper into the nesting level for stack temporaries. */ + +void +push_temp_slots () +{ + /* For GNU C++, we must allow a sequence to be emitted anywhere in + the level where the sequence was started. By not changing levels + when the compiler is inside a sequence, the temporaries for the + sequence and the temporaries will not unwittingly conflict with + the temporaries for other sequences and/or code at that level. */ + if (in_sequence_p ()) + return; + + temp_slot_level++; +} + +/* Pop a temporary nesting level. All slots in use in the current level + are freed. */ + +void +pop_temp_slots () +{ + struct temp_slot *p; + + /* See comment in push_temp_slots about why we don't change levels + in sequences. */ + if (in_sequence_p ()) + return; + + for (p = temp_slots; p; p = p->next) + if (p->in_use && p->level == temp_slot_level) + p->in_use = 0; + + temp_slot_level--; +} + +/* Retroactively move an auto variable from a register to a stack slot. + This is done when an address-reference to the variable is seen. */ + +void +put_var_into_stack (decl) + tree decl; +{ + register rtx reg; + register rtx new = 0; + enum machine_mode promoted_mode, decl_mode; + struct function *function = 0; + tree context = decl_function_context (decl); + + /* Get the current rtl used for this object and it's original mode. */ + reg = TREE_CODE (decl) == SAVE_EXPR ? SAVE_EXPR_RTL (decl) : DECL_RTL (decl); + + /* No need to do anything if decl has no rtx yet + since in that case caller is setting TREE_ADDRESSABLE + and a stack slot will be assigned when the rtl is made. */ + if (reg == 0) + return; + + /* Get the declared mode for this object. */ + decl_mode = (TREE_CODE (decl) == SAVE_EXPR ? TYPE_MODE (TREE_TYPE (decl)) + : DECL_MODE (decl)); + /* Get the mode it's actually stored in. */ + promoted_mode = GET_MODE (reg); + + /* If this variable comes from an outer function, + find that function's saved context. */ + if (context != current_function_decl) + for (function = outer_function_chain; function; function = function->next) + if (function->decl == context) + break; + + /* If this is a variable-size object with a pseudo to address it, + put that pseudo into the stack, if the var is nonlocal. */ + if (DECL_NONLOCAL (decl) + && GET_CODE (reg) == MEM + && GET_CODE (XEXP (reg, 0)) == REG + && REGNO (XEXP (reg, 0)) > LAST_VIRTUAL_REGISTER) + { + reg = XEXP (reg, 0); + decl_mode = promoted_mode = GET_MODE (reg); + } + + if (GET_CODE (reg) != REG) + return; + + if (function) + { + if (REGNO (reg) < function->max_parm_reg) + new = function->parm_reg_stack_loc[REGNO (reg)]; + if (new == 0) + new = assign_outer_stack_local (decl_mode, GET_MODE_SIZE (decl_mode), + 0, function); + } + else + { + if (REGNO (reg) < max_parm_reg) + new = parm_reg_stack_loc[REGNO (reg)]; + if (new == 0) + new = assign_stack_local (decl_mode, GET_MODE_SIZE (decl_mode), 0); + } + + XEXP (reg, 0) = XEXP (new, 0); + /* `volatil' bit means one thing for MEMs, another entirely for REGs. */ + REG_USERVAR_P (reg) = 0; + PUT_CODE (reg, MEM); + PUT_MODE (reg, decl_mode); + + /* If this is a memory ref that contains aggregate components, + mark it as such for cse and loop optimize. */ + MEM_IN_STRUCT_P (reg) + = (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE + || TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE + || TREE_CODE (TREE_TYPE (decl)) == QUAL_UNION_TYPE); + + /* Now make sure that all refs to the variable, previously made + when it was a register, are fixed up to be valid again. */ + if (function) + { + struct var_refs_queue *temp; + + /* Variable is inherited; fix it up when we get back to its function. */ + push_obstacks (function->function_obstack, + function->function_maybepermanent_obstack); + temp + = (struct var_refs_queue *) oballoc (sizeof (struct var_refs_queue)); + temp->modified = reg; + temp->promoted_mode = promoted_mode; + temp->unsignedp = TREE_UNSIGNED (TREE_TYPE (decl)); + temp->next = function->fixup_var_refs_queue; + function->fixup_var_refs_queue = temp; + pop_obstacks (); + } + else + /* Variable is local; fix it up now. */ + fixup_var_refs (reg, promoted_mode, TREE_UNSIGNED (TREE_TYPE (decl))); +} + +static void +fixup_var_refs (var, promoted_mode, unsignedp) + rtx var; + enum machine_mode promoted_mode; + int unsignedp; +{ + tree pending; + rtx first_insn = get_insns (); + struct sequence_stack *stack = sequence_stack; + tree rtl_exps = rtl_expr_chain; + + /* Must scan all insns for stack-refs that exceed the limit. */ + fixup_var_refs_insns (var, promoted_mode, unsignedp, first_insn, stack == 0); + + /* Scan all pending sequences too. */ + for (; stack; stack = stack->next) + { + push_to_sequence (stack->first); + fixup_var_refs_insns (var, promoted_mode, unsignedp, + stack->first, stack->next != 0); + /* Update remembered end of sequence + in case we added an insn at the end. */ + stack->last = get_last_insn (); + end_sequence (); + } + + /* Scan all waiting RTL_EXPRs too. */ + for (pending = rtl_exps; pending; pending = TREE_CHAIN (pending)) + { + rtx seq = RTL_EXPR_SEQUENCE (TREE_VALUE (pending)); + if (seq != const0_rtx && seq != 0) + { + push_to_sequence (seq); + fixup_var_refs_insns (var, promoted_mode, unsignedp, seq, 0); + end_sequence (); + } + } +} + +/* This structure is used by the following two functions to record MEMs or + pseudos used to replace VAR, any SUBREGs of VAR, and any MEMs containing + VAR as an address. We need to maintain this list in case two operands of + an insn were required to match; in that case we must ensure we use the + same replacement. */ + +struct fixup_replacement +{ + rtx old; + rtx new; + struct fixup_replacement *next; +}; + +/* REPLACEMENTS is a pointer to a list of the above structures and X is + some part of an insn. Return a struct fixup_replacement whose OLD + value is equal to X. Allocate a new structure if no such entry exists. */ + +static struct fixup_replacement * +find_fixup_replacement (replacements, x) + struct fixup_replacement **replacements; + rtx x; +{ + struct fixup_replacement *p; + + /* See if we have already replaced this. */ + for (p = *replacements; p && p->old != x; p = p->next) + ; + + if (p == 0) + { + p = (struct fixup_replacement *) oballoc (sizeof (struct fixup_replacement)); + p->old = x; + p->new = 0; + p->next = *replacements; + *replacements = p; + } + + return p; +} + +/* Scan the insn-chain starting with INSN for refs to VAR + and fix them up. TOPLEVEL is nonzero if this chain is the + main chain of insns for the current function. */ + +static void +fixup_var_refs_insns (var, promoted_mode, unsignedp, insn, toplevel) + rtx var; + enum machine_mode promoted_mode; + int unsignedp; + rtx insn; + int toplevel; +{ + rtx call_dest = 0; + + while (insn) + { + rtx next = NEXT_INSN (insn); + rtx note; + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + /* The insn to load VAR from a home in the arglist + is now a no-op. When we see it, just delete it. */ + if (toplevel + && GET_CODE (PATTERN (insn)) == SET + && SET_DEST (PATTERN (insn)) == var + /* If this represents the result of an insn group, + don't delete the insn. */ + && find_reg_note (insn, REG_RETVAL, NULL_RTX) == 0 + && rtx_equal_p (SET_SRC (PATTERN (insn)), var)) + { + /* In unoptimized compilation, we shouldn't call delete_insn + except in jump.c doing warnings. */ + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + if (insn == last_parm_insn) + last_parm_insn = PREV_INSN (next); + } + else + { + struct fixup_replacement *replacements = 0; + rtx next_insn = NEXT_INSN (insn); + +#ifdef SMALL_REGISTER_CLASSES + /* If the insn that copies the results of a CALL_INSN + into a pseudo now references VAR, we have to use an + intermediate pseudo since we want the life of the + return value register to be only a single insn. + + If we don't use an intermediate pseudo, such things as + address computations to make the address of VAR valid + if it is not can be placed beween the CALL_INSN and INSN. + + To make sure this doesn't happen, we record the destination + of the CALL_INSN and see if the next insn uses both that + and VAR. */ + + if (call_dest != 0 && GET_CODE (insn) == INSN + && reg_mentioned_p (var, PATTERN (insn)) + && reg_mentioned_p (call_dest, PATTERN (insn))) + { + rtx temp = gen_reg_rtx (GET_MODE (call_dest)); + + emit_insn_before (gen_move_insn (temp, call_dest), insn); + + PATTERN (insn) = replace_rtx (PATTERN (insn), + call_dest, temp); + } + + if (GET_CODE (insn) == CALL_INSN + && GET_CODE (PATTERN (insn)) == SET) + call_dest = SET_DEST (PATTERN (insn)); + else if (GET_CODE (insn) == CALL_INSN + && GET_CODE (PATTERN (insn)) == PARALLEL + && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) + call_dest = SET_DEST (XVECEXP (PATTERN (insn), 0, 0)); + else + call_dest = 0; +#endif + + /* See if we have to do anything to INSN now that VAR is in + memory. If it needs to be loaded into a pseudo, use a single + pseudo for the entire insn in case there is a MATCH_DUP + between two operands. We pass a pointer to the head of + a list of struct fixup_replacements. If fixup_var_refs_1 + needs to allocate pseudos or replacement MEMs (for SUBREGs), + it will record them in this list. + + If it allocated a pseudo for any replacement, we copy into + it here. */ + + fixup_var_refs_1 (var, promoted_mode, &PATTERN (insn), insn, + &replacements); + + /* If this is last_parm_insn, and any instructions were output + after it to fix it up, then we must set last_parm_insn to + the last such instruction emitted. */ + if (insn == last_parm_insn) + last_parm_insn = PREV_INSN (next_insn); + + while (replacements) + { + if (GET_CODE (replacements->new) == REG) + { + rtx insert_before; + rtx seq; + + /* OLD might be a (subreg (mem)). */ + if (GET_CODE (replacements->old) == SUBREG) + replacements->old + = fixup_memory_subreg (replacements->old, insn, 0); + else + replacements->old + = fixup_stack_1 (replacements->old, insn); + + /* We can not separate USE insns from the CALL_INSN + that they belong to. If this is a CALL_INSN, insert + the move insn before the USE insns preceding it + instead of immediately before the insn. */ + if (GET_CODE (insn) == CALL_INSN) + { + insert_before = insn; + while (GET_CODE (PREV_INSN (insert_before)) == INSN + && GET_CODE (PATTERN (PREV_INSN (insert_before))) == USE) + insert_before = PREV_INSN (insert_before); + } + else + insert_before = insn; + + /* If we are changing the mode, do a conversion. + This might be wasteful, but combine.c will + eliminate much of the waste. */ + + if (GET_MODE (replacements->new) + != GET_MODE (replacements->old)) + { + start_sequence (); + convert_move (replacements->new, + replacements->old, unsignedp); + seq = gen_sequence (); + end_sequence (); + } + else + seq = gen_move_insn (replacements->new, + replacements->old); + + emit_insn_before (seq, insert_before); + } + + replacements = replacements->next; + } + } + + /* Also fix up any invalid exprs in the REG_NOTES of this insn. + But don't touch other insns referred to by reg-notes; + we will get them elsewhere. */ + for (note = REG_NOTES (insn); note; note = XEXP (note, 1)) + if (GET_CODE (note) != INSN_LIST) + XEXP (note, 0) + = walk_fixup_memory_subreg (XEXP (note, 0), insn, 1); + } + insn = next; + } +} + +/* VAR is a MEM that used to be a pseudo register with mode PROMOTED_MODE. + See if the rtx expression at *LOC in INSN needs to be changed. + + REPLACEMENTS is a pointer to a list head that starts out zero, but may + contain a list of original rtx's and replacements. If we find that we need + to modify this insn by replacing a memory reference with a pseudo or by + making a new MEM to implement a SUBREG, we consult that list to see if + we have already chosen a replacement. If none has already been allocated, + we allocate it and update the list. fixup_var_refs_insns will copy VAR + or the SUBREG, as appropriate, to the pseudo. */ + +static void +fixup_var_refs_1 (var, promoted_mode, loc, insn, replacements) + register rtx var; + enum machine_mode promoted_mode; + register rtx *loc; + rtx insn; + struct fixup_replacement **replacements; +{ + register int i; + register rtx x = *loc; + RTX_CODE code = GET_CODE (x); + register char *fmt; + register rtx tem, tem1; + struct fixup_replacement *replacement; + + switch (code) + { + case MEM: + if (var == x) + { + /* If we already have a replacement, use it. Otherwise, + try to fix up this address in case it is invalid. */ + + replacement = find_fixup_replacement (replacements, var); + if (replacement->new) + { + *loc = replacement->new; + return; + } + + *loc = replacement->new = x = fixup_stack_1 (x, insn); + + /* Unless we are forcing memory to register or we changed the mode, + we can leave things the way they are if the insn is valid. */ + + INSN_CODE (insn) = -1; + if (! flag_force_mem && GET_MODE (x) == promoted_mode + && recog_memoized (insn) >= 0) + return; + + *loc = replacement->new = gen_reg_rtx (promoted_mode); + return; + } + + /* If X contains VAR, we need to unshare it here so that we update + each occurrence separately. But all identical MEMs in one insn + must be replaced with the same rtx because of the possibility of + MATCH_DUPs. */ + + if (reg_mentioned_p (var, x)) + { + replacement = find_fixup_replacement (replacements, x); + if (replacement->new == 0) + replacement->new = copy_most_rtx (x, var); + + *loc = x = replacement->new; + } + break; + + case REG: + case CC0: + case PC: + case CONST_INT: + case CONST: + case SYMBOL_REF: + case LABEL_REF: + case CONST_DOUBLE: + return; + + case SIGN_EXTRACT: + case ZERO_EXTRACT: + /* Note that in some cases those types of expressions are altered + by optimize_bit_field, and do not survive to get here. */ + if (XEXP (x, 0) == var + || (GET_CODE (XEXP (x, 0)) == SUBREG + && SUBREG_REG (XEXP (x, 0)) == var)) + { + /* Get TEM as a valid MEM in the mode presently in the insn. + + We don't worry about the possibility of MATCH_DUP here; it + is highly unlikely and would be tricky to handle. */ + + tem = XEXP (x, 0); + if (GET_CODE (tem) == SUBREG) + tem = fixup_memory_subreg (tem, insn, 1); + tem = fixup_stack_1 (tem, insn); + + /* Unless we want to load from memory, get TEM into the proper mode + for an extract from memory. This can only be done if the + extract is at a constant position and length. */ + + if (! flag_force_mem && GET_CODE (XEXP (x, 1)) == CONST_INT + && GET_CODE (XEXP (x, 2)) == CONST_INT + && ! mode_dependent_address_p (XEXP (tem, 0)) + && ! MEM_VOLATILE_P (tem)) + { + enum machine_mode wanted_mode = VOIDmode; + enum machine_mode is_mode = GET_MODE (tem); + int width = INTVAL (XEXP (x, 1)); + int pos = INTVAL (XEXP (x, 2)); + +#ifdef HAVE_extzv + if (GET_CODE (x) == ZERO_EXTRACT) + wanted_mode = insn_operand_mode[(int) CODE_FOR_extzv][1]; +#endif +#ifdef HAVE_extv + if (GET_CODE (x) == SIGN_EXTRACT) + wanted_mode = insn_operand_mode[(int) CODE_FOR_extv][1]; +#endif + /* If we have a narrower mode, we can do something. */ + if (wanted_mode != VOIDmode + && GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode)) + { + int offset = pos / BITS_PER_UNIT; + rtx old_pos = XEXP (x, 2); + rtx newmem; + + /* If the bytes and bits are counted differently, we + must adjust the offset. */ +#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN + offset = (GET_MODE_SIZE (is_mode) + - GET_MODE_SIZE (wanted_mode) - offset); +#endif + + pos %= GET_MODE_BITSIZE (wanted_mode); + + newmem = gen_rtx (MEM, wanted_mode, + plus_constant (XEXP (tem, 0), offset)); + RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (tem); + MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (tem); + MEM_IN_STRUCT_P (newmem) = MEM_IN_STRUCT_P (tem); + + /* Make the change and see if the insn remains valid. */ + INSN_CODE (insn) = -1; + XEXP (x, 0) = newmem; + XEXP (x, 2) = GEN_INT (pos); + + if (recog_memoized (insn) >= 0) + return; + + /* Otherwise, restore old position. XEXP (x, 0) will be + restored later. */ + XEXP (x, 2) = old_pos; + } + } + + /* If we get here, the bitfield extract insn can't accept a memory + reference. Copy the input into a register. */ + + tem1 = gen_reg_rtx (GET_MODE (tem)); + emit_insn_before (gen_move_insn (tem1, tem), insn); + XEXP (x, 0) = tem1; + return; + } + break; + + case SUBREG: + if (SUBREG_REG (x) == var) + { + /* If this is a special SUBREG made because VAR was promoted + from a wider mode, replace it with VAR and call ourself + recursively, this time saying that the object previously + had its current mode (by virtue of the SUBREG). */ + + if (SUBREG_PROMOTED_VAR_P (x)) + { + *loc = var; + fixup_var_refs_1 (var, GET_MODE (var), loc, insn, replacements); + return; + } + + /* If this SUBREG makes VAR wider, it has become a paradoxical + SUBREG with VAR in memory, but these aren't allowed at this + stage of the compilation. So load VAR into a pseudo and take + a SUBREG of that pseudo. */ + if (GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (var))) + { + replacement = find_fixup_replacement (replacements, var); + if (replacement->new == 0) + replacement->new = gen_reg_rtx (GET_MODE (var)); + SUBREG_REG (x) = replacement->new; + return; + } + + /* See if we have already found a replacement for this SUBREG. + If so, use it. Otherwise, make a MEM and see if the insn + is recognized. If not, or if we should force MEM into a register, + make a pseudo for this SUBREG. */ + replacement = find_fixup_replacement (replacements, x); + if (replacement->new) + { + *loc = replacement->new; + return; + } + + replacement->new = *loc = fixup_memory_subreg (x, insn, 0); + + INSN_CODE (insn) = -1; + if (! flag_force_mem && recog_memoized (insn) >= 0) + return; + + *loc = replacement->new = gen_reg_rtx (GET_MODE (x)); + return; + } + break; + + case SET: + /* First do special simplification of bit-field references. */ + if (GET_CODE (SET_DEST (x)) == SIGN_EXTRACT + || GET_CODE (SET_DEST (x)) == ZERO_EXTRACT) + optimize_bit_field (x, insn, 0); + if (GET_CODE (SET_SRC (x)) == SIGN_EXTRACT + || GET_CODE (SET_SRC (x)) == ZERO_EXTRACT) + optimize_bit_field (x, insn, NULL_PTR); + + /* If SET_DEST is now a paradoxical SUBREG, put the result of this + insn into a pseudo and store the low part of the pseudo into VAR. */ + if (GET_CODE (SET_DEST (x)) == SUBREG + && SUBREG_REG (SET_DEST (x)) == var + && (GET_MODE_SIZE (GET_MODE (SET_DEST (x))) + > GET_MODE_SIZE (GET_MODE (var)))) + { + SET_DEST (x) = tem = gen_reg_rtx (GET_MODE (SET_DEST (x))); + emit_insn_after (gen_move_insn (var, gen_lowpart (GET_MODE (var), + tem)), + insn); + break; + } + + { + rtx dest = SET_DEST (x); + rtx src = SET_SRC (x); + rtx outerdest = dest; + + while (GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART + || GET_CODE (dest) == SIGN_EXTRACT + || GET_CODE (dest) == ZERO_EXTRACT) + dest = XEXP (dest, 0); + + if (GET_CODE (src) == SUBREG) + src = XEXP (src, 0); + + /* If VAR does not appear at the top level of the SET + just scan the lower levels of the tree. */ + + if (src != var && dest != var) + break; + + /* We will need to rerecognize this insn. */ + INSN_CODE (insn) = -1; + +#ifdef HAVE_insv + if (GET_CODE (outerdest) == ZERO_EXTRACT && dest == var) + { + /* Since this case will return, ensure we fixup all the + operands here. */ + fixup_var_refs_1 (var, promoted_mode, &XEXP (outerdest, 1), + insn, replacements); + fixup_var_refs_1 (var, promoted_mode, &XEXP (outerdest, 2), + insn, replacements); + fixup_var_refs_1 (var, promoted_mode, &SET_SRC (x), + insn, replacements); + + tem = XEXP (outerdest, 0); + + /* Clean up (SUBREG:SI (MEM:mode ...) 0) + that may appear inside a ZERO_EXTRACT. + This was legitimate when the MEM was a REG. */ + if (GET_CODE (tem) == SUBREG + && SUBREG_REG (tem) == var) + tem = fixup_memory_subreg (tem, insn, 1); + else + tem = fixup_stack_1 (tem, insn); + + if (GET_CODE (XEXP (outerdest, 1)) == CONST_INT + && GET_CODE (XEXP (outerdest, 2)) == CONST_INT + && ! mode_dependent_address_p (XEXP (tem, 0)) + && ! MEM_VOLATILE_P (tem)) + { + enum machine_mode wanted_mode + = insn_operand_mode[(int) CODE_FOR_insv][0]; + enum machine_mode is_mode = GET_MODE (tem); + int width = INTVAL (XEXP (outerdest, 1)); + int pos = INTVAL (XEXP (outerdest, 2)); + + /* If we have a narrower mode, we can do something. */ + if (GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode)) + { + int offset = pos / BITS_PER_UNIT; + rtx old_pos = XEXP (outerdest, 2); + rtx newmem; + +#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN + offset = (GET_MODE_SIZE (is_mode) + - GET_MODE_SIZE (wanted_mode) - offset); +#endif + + pos %= GET_MODE_BITSIZE (wanted_mode); + + newmem = gen_rtx (MEM, wanted_mode, + plus_constant (XEXP (tem, 0), offset)); + RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (tem); + MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (tem); + MEM_IN_STRUCT_P (newmem) = MEM_IN_STRUCT_P (tem); + + /* Make the change and see if the insn remains valid. */ + INSN_CODE (insn) = -1; + XEXP (outerdest, 0) = newmem; + XEXP (outerdest, 2) = GEN_INT (pos); + + if (recog_memoized (insn) >= 0) + return; + + /* Otherwise, restore old position. XEXP (x, 0) will be + restored later. */ + XEXP (outerdest, 2) = old_pos; + } + } + + /* If we get here, the bit-field store doesn't allow memory + or isn't located at a constant position. Load the value into + a register, do the store, and put it back into memory. */ + + tem1 = gen_reg_rtx (GET_MODE (tem)); + emit_insn_before (gen_move_insn (tem1, tem), insn); + emit_insn_after (gen_move_insn (tem, tem1), insn); + XEXP (outerdest, 0) = tem1; + return; + } +#endif + + /* STRICT_LOW_PART is a no-op on memory references + and it can cause combinations to be unrecognizable, + so eliminate it. */ + + if (dest == var && GET_CODE (SET_DEST (x)) == STRICT_LOW_PART) + SET_DEST (x) = XEXP (SET_DEST (x), 0); + + /* A valid insn to copy VAR into or out of a register + must be left alone, to avoid an infinite loop here. + If the reference to VAR is by a subreg, fix that up, + since SUBREG is not valid for a memref. + Also fix up the address of the stack slot. + + Note that we must not try to recognize the insn until + after we know that we have valid addresses and no + (subreg (mem ...) ...) constructs, since these interfere + with determining the validity of the insn. */ + + if ((SET_SRC (x) == var + || (GET_CODE (SET_SRC (x)) == SUBREG + && SUBREG_REG (SET_SRC (x)) == var)) + && (GET_CODE (SET_DEST (x)) == REG + || (GET_CODE (SET_DEST (x)) == SUBREG + && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG)) + && x == single_set (PATTERN (insn))) + { + rtx pat; + + replacement = find_fixup_replacement (replacements, SET_SRC (x)); + if (replacement->new) + SET_SRC (x) = replacement->new; + else if (GET_CODE (SET_SRC (x)) == SUBREG) + SET_SRC (x) = replacement->new + = fixup_memory_subreg (SET_SRC (x), insn, 0); + else + SET_SRC (x) = replacement->new + = fixup_stack_1 (SET_SRC (x), insn); + + if (recog_memoized (insn) >= 0) + return; + + /* INSN is not valid, but we know that we want to + copy SET_SRC (x) to SET_DEST (x) in some way. So + we generate the move and see whether it requires more + than one insn. If it does, we emit those insns and + delete INSN. Otherwise, we an just replace the pattern + of INSN; we have already verified above that INSN has + no other function that to do X. */ + + pat = gen_move_insn (SET_DEST (x), SET_SRC (x)); + if (GET_CODE (pat) == SEQUENCE) + { + emit_insn_after (pat, insn); + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + } + else + PATTERN (insn) = pat; + + return; + } + + if ((SET_DEST (x) == var + || (GET_CODE (SET_DEST (x)) == SUBREG + && SUBREG_REG (SET_DEST (x)) == var)) + && (GET_CODE (SET_SRC (x)) == REG + || (GET_CODE (SET_SRC (x)) == SUBREG + && GET_CODE (SUBREG_REG (SET_SRC (x))) == REG)) + && x == single_set (PATTERN (insn))) + { + rtx pat; + + if (GET_CODE (SET_DEST (x)) == SUBREG) + SET_DEST (x) = fixup_memory_subreg (SET_DEST (x), insn, 0); + else + SET_DEST (x) = fixup_stack_1 (SET_DEST (x), insn); + + if (recog_memoized (insn) >= 0) + return; + + pat = gen_move_insn (SET_DEST (x), SET_SRC (x)); + if (GET_CODE (pat) == SEQUENCE) + { + emit_insn_after (pat, insn); + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + } + else + PATTERN (insn) = pat; + + return; + } + + /* Otherwise, storing into VAR must be handled specially + by storing into a temporary and copying that into VAR + with a new insn after this one. Note that this case + will be used when storing into a promoted scalar since + the insn will now have different modes on the input + and output and hence will be invalid (except for the case + of setting it to a constant, which does not need any + change if it is valid). We generate extra code in that case, + but combine.c will eliminate it. */ + + if (dest == var) + { + rtx temp; + rtx fixeddest = SET_DEST (x); + + /* STRICT_LOW_PART can be discarded, around a MEM. */ + if (GET_CODE (fixeddest) == STRICT_LOW_PART) + fixeddest = XEXP (fixeddest, 0); + /* Convert (SUBREG (MEM)) to a MEM in a changed mode. */ + if (GET_CODE (fixeddest) == SUBREG) + fixeddest = fixup_memory_subreg (fixeddest, insn, 0); + else + fixeddest = fixup_stack_1 (fixeddest, insn); + + temp = gen_reg_rtx (GET_MODE (SET_SRC (x)) == VOIDmode + ? GET_MODE (fixeddest) + : GET_MODE (SET_SRC (x))); + + emit_insn_after (gen_move_insn (fixeddest, + gen_lowpart (GET_MODE (fixeddest), + temp)), + insn); + + SET_DEST (x) = temp; + } + } + } + + /* Nothing special about this RTX; fix its operands. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + fixup_var_refs_1 (var, promoted_mode, &XEXP (x, i), insn, replacements); + if (fmt[i] == 'E') + { + register int j; + for (j = 0; j < XVECLEN (x, i); j++) + fixup_var_refs_1 (var, promoted_mode, &XVECEXP (x, i, j), + insn, replacements); + } + } +} + +/* Given X, an rtx of the form (SUBREG:m1 (MEM:m2 addr)), + return an rtx (MEM:m1 newaddr) which is equivalent. + If any insns must be emitted to compute NEWADDR, put them before INSN. + + UNCRITICAL nonzero means accept paradoxical subregs. + This is used for subregs found inside of ZERO_EXTRACTs and in REG_NOTES. */ + +static rtx +fixup_memory_subreg (x, insn, uncritical) + rtx x; + rtx insn; + int uncritical; +{ + int offset = SUBREG_WORD (x) * UNITS_PER_WORD; + rtx addr = XEXP (SUBREG_REG (x), 0); + enum machine_mode mode = GET_MODE (x); + rtx saved, result; + + /* Paradoxical SUBREGs are usually invalid during RTL generation. */ + if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) + && ! uncritical) + abort (); + +#if BYTES_BIG_ENDIAN + offset += (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) + - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))); +#endif + addr = plus_constant (addr, offset); + if (!flag_force_addr && memory_address_p (mode, addr)) + /* Shortcut if no insns need be emitted. */ + return change_address (SUBREG_REG (x), mode, addr); + start_sequence (); + result = change_address (SUBREG_REG (x), mode, addr); + emit_insn_before (gen_sequence (), insn); + end_sequence (); + return result; +} + +/* Do fixup_memory_subreg on all (SUBREG (MEM ...) ...) contained in X. + Replace subexpressions of X in place. + If X itself is a (SUBREG (MEM ...) ...), return the replacement expression. + Otherwise return X, with its contents possibly altered. + + If any insns must be emitted to compute NEWADDR, put them before INSN. + + UNCRITICAL is as in fixup_memory_subreg. */ + +static rtx +walk_fixup_memory_subreg (x, insn, uncritical) + register rtx x; + rtx insn; + int uncritical; +{ + register enum rtx_code code; + register char *fmt; + register int i; + + if (x == 0) + return 0; + + code = GET_CODE (x); + + if (code == SUBREG && GET_CODE (SUBREG_REG (x)) == MEM) + return fixup_memory_subreg (x, insn, uncritical); + + /* Nothing special about this RTX; fix its operands. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + XEXP (x, i) = walk_fixup_memory_subreg (XEXP (x, i), insn, uncritical); + if (fmt[i] == 'E') + { + register int j; + for (j = 0; j < XVECLEN (x, i); j++) + XVECEXP (x, i, j) + = walk_fixup_memory_subreg (XVECEXP (x, i, j), insn, uncritical); + } + } + return x; +} + +#if 0 +/* Fix up any references to stack slots that are invalid memory addresses + because they exceed the maximum range of a displacement. */ + +void +fixup_stack_slots () +{ + register rtx insn; + + /* Did we generate a stack slot that is out of range + or otherwise has an invalid address? */ + if (invalid_stack_slot) + { + /* Yes. Must scan all insns for stack-refs that exceed the limit. */ + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN + || GET_CODE (insn) == JUMP_INSN) + fixup_stack_1 (PATTERN (insn), insn); + } +} +#endif + +/* For each memory ref within X, if it refers to a stack slot + with an out of range displacement, put the address in a temp register + (emitting new insns before INSN to load these registers) + and alter the memory ref to use that register. + Replace each such MEM rtx with a copy, to avoid clobberage. */ + +static rtx +fixup_stack_1 (x, insn) + rtx x; + rtx insn; +{ + register int i; + register RTX_CODE code = GET_CODE (x); + register char *fmt; + + if (code == MEM) + { + register rtx ad = XEXP (x, 0); + /* If we have address of a stack slot but it's not valid + (displacement is too large), compute the sum in a register. */ + if (GET_CODE (ad) == PLUS + && GET_CODE (XEXP (ad, 0)) == REG + && REGNO (XEXP (ad, 0)) >= FIRST_VIRTUAL_REGISTER + && REGNO (XEXP (ad, 0)) <= LAST_VIRTUAL_REGISTER + && GET_CODE (XEXP (ad, 1)) == CONST_INT) + { + rtx temp, seq; + if (memory_address_p (GET_MODE (x), ad)) + return x; + + start_sequence (); + temp = copy_to_reg (ad); + seq = gen_sequence (); + end_sequence (); + emit_insn_before (seq, insn); + return change_address (x, VOIDmode, temp); + } + return x; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + XEXP (x, i) = fixup_stack_1 (XEXP (x, i), insn); + if (fmt[i] == 'E') + { + register int j; + for (j = 0; j < XVECLEN (x, i); j++) + XVECEXP (x, i, j) = fixup_stack_1 (XVECEXP (x, i, j), insn); + } + } + return x; +} + +/* Optimization: a bit-field instruction whose field + happens to be a byte or halfword in memory + can be changed to a move instruction. + + We call here when INSN is an insn to examine or store into a bit-field. + BODY is the SET-rtx to be altered. + + EQUIV_MEM is the table `reg_equiv_mem' if that is available; else 0. + (Currently this is called only from function.c, and EQUIV_MEM + is always 0.) */ + +static void +optimize_bit_field (body, insn, equiv_mem) + rtx body; + rtx insn; + rtx *equiv_mem; +{ + register rtx bitfield; + int destflag; + rtx seq = 0; + enum machine_mode mode; + + if (GET_CODE (SET_DEST (body)) == SIGN_EXTRACT + || GET_CODE (SET_DEST (body)) == ZERO_EXTRACT) + bitfield = SET_DEST (body), destflag = 1; + else + bitfield = SET_SRC (body), destflag = 0; + + /* First check that the field being stored has constant size and position + and is in fact a byte or halfword suitably aligned. */ + + if (GET_CODE (XEXP (bitfield, 1)) == CONST_INT + && GET_CODE (XEXP (bitfield, 2)) == CONST_INT + && ((mode = mode_for_size (INTVAL (XEXP (bitfield, 1)), MODE_INT, 1)) + != BLKmode) + && INTVAL (XEXP (bitfield, 2)) % INTVAL (XEXP (bitfield, 1)) == 0) + { + register rtx memref = 0; + + /* Now check that the containing word is memory, not a register, + and that it is safe to change the machine mode. */ + + if (GET_CODE (XEXP (bitfield, 0)) == MEM) + memref = XEXP (bitfield, 0); + else if (GET_CODE (XEXP (bitfield, 0)) == REG + && equiv_mem != 0) + memref = equiv_mem[REGNO (XEXP (bitfield, 0))]; + else if (GET_CODE (XEXP (bitfield, 0)) == SUBREG + && GET_CODE (SUBREG_REG (XEXP (bitfield, 0))) == MEM) + memref = SUBREG_REG (XEXP (bitfield, 0)); + else if (GET_CODE (XEXP (bitfield, 0)) == SUBREG + && equiv_mem != 0 + && GET_CODE (SUBREG_REG (XEXP (bitfield, 0))) == REG) + memref = equiv_mem[REGNO (SUBREG_REG (XEXP (bitfield, 0)))]; + + if (memref + && ! mode_dependent_address_p (XEXP (memref, 0)) + && ! MEM_VOLATILE_P (memref)) + { + /* Now adjust the address, first for any subreg'ing + that we are now getting rid of, + and then for which byte of the word is wanted. */ + + register int offset = INTVAL (XEXP (bitfield, 2)); + /* Adjust OFFSET to count bits from low-address byte. */ +#if BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN + offset = (GET_MODE_BITSIZE (GET_MODE (XEXP (bitfield, 0))) + - offset - INTVAL (XEXP (bitfield, 1))); +#endif + /* Adjust OFFSET to count bytes from low-address byte. */ + offset /= BITS_PER_UNIT; + if (GET_CODE (XEXP (bitfield, 0)) == SUBREG) + { + offset += SUBREG_WORD (XEXP (bitfield, 0)) * UNITS_PER_WORD; +#if BYTES_BIG_ENDIAN + offset -= (MIN (UNITS_PER_WORD, + GET_MODE_SIZE (GET_MODE (XEXP (bitfield, 0)))) + - MIN (UNITS_PER_WORD, + GET_MODE_SIZE (GET_MODE (memref)))); +#endif + } + + memref = change_address (memref, mode, + plus_constant (XEXP (memref, 0), offset)); + + /* Store this memory reference where + we found the bit field reference. */ + + if (destflag) + { + validate_change (insn, &SET_DEST (body), memref, 1); + if (! CONSTANT_ADDRESS_P (SET_SRC (body))) + { + rtx src = SET_SRC (body); + while (GET_CODE (src) == SUBREG + && SUBREG_WORD (src) == 0) + src = SUBREG_REG (src); + if (GET_MODE (src) != GET_MODE (memref)) + src = gen_lowpart (GET_MODE (memref), SET_SRC (body)); + validate_change (insn, &SET_SRC (body), src, 1); + } + else if (GET_MODE (SET_SRC (body)) != VOIDmode + && GET_MODE (SET_SRC (body)) != GET_MODE (memref)) + /* This shouldn't happen because anything that didn't have + one of these modes should have got converted explicitly + and then referenced through a subreg. + This is so because the original bit-field was + handled by agg_mode and so its tree structure had + the same mode that memref now has. */ + abort (); + } + else + { + rtx dest = SET_DEST (body); + + while (GET_CODE (dest) == SUBREG + && SUBREG_WORD (dest) == 0) + dest = SUBREG_REG (dest); + + validate_change (insn, &SET_DEST (body), dest, 1); + + if (GET_MODE (dest) == GET_MODE (memref)) + validate_change (insn, &SET_SRC (body), memref, 1); + else + { + /* Convert the mem ref to the destination mode. */ + rtx newreg = gen_reg_rtx (GET_MODE (dest)); + + start_sequence (); + convert_move (newreg, memref, + GET_CODE (SET_SRC (body)) == ZERO_EXTRACT); + seq = get_insns (); + end_sequence (); + + validate_change (insn, &SET_SRC (body), newreg, 1); + } + } + + /* See if we can convert this extraction or insertion into + a simple move insn. We might not be able to do so if this + was, for example, part of a PARALLEL. + + If we succeed, write out any needed conversions. If we fail, + it is hard to guess why we failed, so don't do anything + special; just let the optimization be suppressed. */ + + if (apply_change_group () && seq) + emit_insns_before (seq, insn); + } + } +} + +/* These routines are responsible for converting virtual register references + to the actual hard register references once RTL generation is complete. + + The following four variables are used for communication between the + routines. They contain the offsets of the virtual registers from their + respective hard registers. */ + +static int in_arg_offset; +static int var_offset; +static int dynamic_offset; +static int out_arg_offset; + +/* In most machines, the stack pointer register is equivalent to the bottom + of the stack. */ + +#ifndef STACK_POINTER_OFFSET +#define STACK_POINTER_OFFSET 0 +#endif + +/* If not defined, pick an appropriate default for the offset of dynamically + allocated memory depending on the value of ACCUMULATE_OUTGOING_ARGS, + REG_PARM_STACK_SPACE, and OUTGOING_REG_PARM_STACK_SPACE. */ + +#ifndef STACK_DYNAMIC_OFFSET + +#ifdef ACCUMULATE_OUTGOING_ARGS +/* The bottom of the stack points to the actual arguments. If + REG_PARM_STACK_SPACE is defined, this includes the space for the register + parameters. However, if OUTGOING_REG_PARM_STACK space is not defined, + stack space for register parameters is not pushed by the caller, but + rather part of the fixed stack areas and hence not included in + `current_function_outgoing_args_size'. Nevertheless, we must allow + for it when allocating stack dynamic objects. */ + +#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE) +#define STACK_DYNAMIC_OFFSET(FNDECL) \ +(current_function_outgoing_args_size \ + + REG_PARM_STACK_SPACE (FNDECL) + (STACK_POINTER_OFFSET)) + +#else +#define STACK_DYNAMIC_OFFSET(FNDECL) \ +(current_function_outgoing_args_size + (STACK_POINTER_OFFSET)) +#endif + +#else +#define STACK_DYNAMIC_OFFSET(FNDECL) STACK_POINTER_OFFSET +#endif +#endif + +/* Pass through the INSNS of function FNDECL and convert virtual register + references to hard register references. */ + +void +instantiate_virtual_regs (fndecl, insns) + tree fndecl; + rtx insns; +{ + rtx insn; + + /* Compute the offsets to use for this function. */ + in_arg_offset = FIRST_PARM_OFFSET (fndecl); + var_offset = STARTING_FRAME_OFFSET; + dynamic_offset = STACK_DYNAMIC_OFFSET (fndecl); + out_arg_offset = STACK_POINTER_OFFSET; + + /* Scan all variables and parameters of this function. For each that is + in memory, instantiate all virtual registers if the result is a valid + address. If not, we do it later. That will handle most uses of virtual + regs on many machines. */ + instantiate_decls (fndecl, 1); + + /* Initialize recognition, indicating that volatile is OK. */ + init_recog (); + + /* Scan through all the insns, instantiating every virtual register still + present. */ + for (insn = insns; insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN + || GET_CODE (insn) == CALL_INSN) + { + instantiate_virtual_regs_1 (&PATTERN (insn), insn, 1); + instantiate_virtual_regs_1 (®_NOTES (insn), NULL_RTX, 0); + } + + /* Now instantiate the remaining register equivalences for debugging info. + These will not be valid addresses. */ + instantiate_decls (fndecl, 0); + + /* Indicate that, from now on, assign_stack_local should use + frame_pointer_rtx. */ + virtuals_instantiated = 1; +} + +/* Scan all decls in FNDECL (both variables and parameters) and instantiate + all virtual registers in their DECL_RTL's. + + If VALID_ONLY, do this only if the resulting address is still valid. + Otherwise, always do it. */ + +static void +instantiate_decls (fndecl, valid_only) + tree fndecl; + int valid_only; +{ + tree decl; + + if (DECL_INLINE (fndecl)) + /* When compiling an inline function, the obstack used for + rtl allocation is the maybepermanent_obstack. Calling + `resume_temporary_allocation' switches us back to that + obstack while we process this function's parameters. */ + resume_temporary_allocation (); + + /* Process all parameters of the function. */ + for (decl = DECL_ARGUMENTS (fndecl); decl; decl = TREE_CHAIN (decl)) + { + instantiate_decl (DECL_RTL (decl), int_size_in_bytes (TREE_TYPE (decl)), + valid_only); + instantiate_decl (DECL_INCOMING_RTL (decl), + int_size_in_bytes (TREE_TYPE (decl)), valid_only); + } + + /* Now process all variables defined in the function or its subblocks. */ + instantiate_decls_1 (DECL_INITIAL (fndecl), valid_only); + + if (DECL_INLINE (fndecl)) + { + /* Save all rtl allocated for this function by raising the + high-water mark on the maybepermanent_obstack. */ + preserve_data (); + /* All further rtl allocation is now done in the current_obstack. */ + rtl_in_current_obstack (); + } +} + +/* Subroutine of instantiate_decls: Process all decls in the given + BLOCK node and all its subblocks. */ + +static void +instantiate_decls_1 (let, valid_only) + tree let; + int valid_only; +{ + tree t; + + for (t = BLOCK_VARS (let); t; t = TREE_CHAIN (t)) + instantiate_decl (DECL_RTL (t), int_size_in_bytes (TREE_TYPE (t)), + valid_only); + + /* Process all subblocks. */ + for (t = BLOCK_SUBBLOCKS (let); t; t = TREE_CHAIN (t)) + instantiate_decls_1 (t, valid_only); +} + +/* Subroutine of the preceding procedures: Given RTL representing a + decl and the size of the object, do any instantiation required. + + If VALID_ONLY is non-zero, it means that the RTL should only be + changed if the new address is valid. */ + +static void +instantiate_decl (x, size, valid_only) + rtx x; + int size; + int valid_only; +{ + enum machine_mode mode; + rtx addr; + + /* If this is not a MEM, no need to do anything. Similarly if the + address is a constant or a register that is not a virtual register. */ + + if (x == 0 || GET_CODE (x) != MEM) + return; + + addr = XEXP (x, 0); + if (CONSTANT_P (addr) + || (GET_CODE (addr) == REG + && (REGNO (addr) < FIRST_VIRTUAL_REGISTER + || REGNO (addr) > LAST_VIRTUAL_REGISTER))) + return; + + /* If we should only do this if the address is valid, copy the address. + We need to do this so we can undo any changes that might make the + address invalid. This copy is unfortunate, but probably can't be + avoided. */ + + if (valid_only) + addr = copy_rtx (addr); + + instantiate_virtual_regs_1 (&addr, NULL_RTX, 0); + + if (! valid_only) + return; + + /* Now verify that the resulting address is valid for every integer or + floating-point mode up to and including SIZE bytes long. We do this + since the object might be accessed in any mode and frame addresses + are shared. */ + + for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); + mode != VOIDmode && GET_MODE_SIZE (mode) <= size; + mode = GET_MODE_WIDER_MODE (mode)) + if (! memory_address_p (mode, addr)) + return; + + for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); + mode != VOIDmode && GET_MODE_SIZE (mode) <= size; + mode = GET_MODE_WIDER_MODE (mode)) + if (! memory_address_p (mode, addr)) + return; + + /* Otherwise, put back the address, now that we have updated it and we + know it is valid. */ + + XEXP (x, 0) = addr; +} + +/* Given a pointer to a piece of rtx and an optional pointer to the + containing object, instantiate any virtual registers present in it. + + If EXTRA_INSNS, we always do the replacement and generate + any extra insns before OBJECT. If it zero, we do nothing if replacement + is not valid. + + Return 1 if we either had nothing to do or if we were able to do the + needed replacement. Return 0 otherwise; we only return zero if + EXTRA_INSNS is zero. + + We first try some simple transformations to avoid the creation of extra + pseudos. */ + +static int +instantiate_virtual_regs_1 (loc, object, extra_insns) + rtx *loc; + rtx object; + int extra_insns; +{ + rtx x; + RTX_CODE code; + rtx new = 0; + int offset; + rtx temp; + rtx seq; + int i, j; + char *fmt; + + /* Re-start here to avoid recursion in common cases. */ + restart: + + x = *loc; + if (x == 0) + return 1; + + code = GET_CODE (x); + + /* Check for some special cases. */ + switch (code) + { + case CONST_INT: + case CONST_DOUBLE: + case CONST: + case SYMBOL_REF: + case CODE_LABEL: + case PC: + case CC0: + case ASM_INPUT: + case ADDR_VEC: + case ADDR_DIFF_VEC: + case RETURN: + return 1; + + case SET: + /* We are allowed to set the virtual registers. This means that + that the actual register should receive the source minus the + appropriate offset. This is used, for example, in the handling + of non-local gotos. */ + if (SET_DEST (x) == virtual_incoming_args_rtx) + new = arg_pointer_rtx, offset = - in_arg_offset; + else if (SET_DEST (x) == virtual_stack_vars_rtx) + new = frame_pointer_rtx, offset = - var_offset; + else if (SET_DEST (x) == virtual_stack_dynamic_rtx) + new = stack_pointer_rtx, offset = - dynamic_offset; + else if (SET_DEST (x) == virtual_outgoing_args_rtx) + new = stack_pointer_rtx, offset = - out_arg_offset; + + if (new) + { + /* The only valid sources here are PLUS or REG. Just do + the simplest possible thing to handle them. */ + if (GET_CODE (SET_SRC (x)) != REG + && GET_CODE (SET_SRC (x)) != PLUS) + abort (); + + start_sequence (); + if (GET_CODE (SET_SRC (x)) != REG) + temp = force_operand (SET_SRC (x), NULL_RTX); + else + temp = SET_SRC (x); + temp = force_operand (plus_constant (temp, offset), NULL_RTX); + seq = get_insns (); + end_sequence (); + + emit_insns_before (seq, object); + SET_DEST (x) = new; + + if (!validate_change (object, &SET_SRC (x), temp, 0) + || ! extra_insns) + abort (); + + return 1; + } + + instantiate_virtual_regs_1 (&SET_DEST (x), object, extra_insns); + loc = &SET_SRC (x); + goto restart; + + case PLUS: + /* Handle special case of virtual register plus constant. */ + if (CONSTANT_P (XEXP (x, 1))) + { + rtx old; + + /* Check for (plus (plus VIRT foo) (const_int)) first. */ + if (GET_CODE (XEXP (x, 0)) == PLUS) + { + rtx inner = XEXP (XEXP (x, 0), 0); + + if (inner == virtual_incoming_args_rtx) + new = arg_pointer_rtx, offset = in_arg_offset; + else if (inner == virtual_stack_vars_rtx) + new = frame_pointer_rtx, offset = var_offset; + else if (inner == virtual_stack_dynamic_rtx) + new = stack_pointer_rtx, offset = dynamic_offset; + else if (inner == virtual_outgoing_args_rtx) + new = stack_pointer_rtx, offset = out_arg_offset; + else + { + loc = &XEXP (x, 0); + goto restart; + } + + instantiate_virtual_regs_1 (&XEXP (XEXP (x, 0), 1), object, + extra_insns); + new = gen_rtx (PLUS, Pmode, new, XEXP (XEXP (x, 0), 1)); + } + + else if (XEXP (x, 0) == virtual_incoming_args_rtx) + new = arg_pointer_rtx, offset = in_arg_offset; + else if (XEXP (x, 0) == virtual_stack_vars_rtx) + new = frame_pointer_rtx, offset = var_offset; + else if (XEXP (x, 0) == virtual_stack_dynamic_rtx) + new = stack_pointer_rtx, offset = dynamic_offset; + else if (XEXP (x, 0) == virtual_outgoing_args_rtx) + new = stack_pointer_rtx, offset = out_arg_offset; + else + { + /* We know the second operand is a constant. Unless the + first operand is a REG (which has been already checked), + it needs to be checked. */ + if (GET_CODE (XEXP (x, 0)) != REG) + { + loc = &XEXP (x, 0); + goto restart; + } + return 1; + } + + old = XEXP (x, 0); + XEXP (x, 0) = new; + new = plus_constant (XEXP (x, 1), offset); + + /* If the new constant is zero, try to replace the sum with its + first operand. */ + if (new == const0_rtx + && validate_change (object, loc, XEXP (x, 0), 0)) + return 1; + + /* Next try to replace constant with new one. */ + if (!validate_change (object, &XEXP (x, 1), new, 0)) + { + if (! extra_insns) + { + XEXP (x, 0) = old; + return 0; + } + + /* Otherwise copy the new constant into a register and replace + constant with that register. */ + temp = gen_reg_rtx (Pmode); + if (validate_change (object, &XEXP (x, 1), temp, 0)) + emit_insn_before (gen_move_insn (temp, new), object); + else + { + /* If that didn't work, replace this expression with a + register containing the sum. */ + + new = gen_rtx (PLUS, Pmode, XEXP (x, 0), new); + XEXP (x, 0) = old; + + start_sequence (); + temp = force_operand (new, NULL_RTX); + seq = get_insns (); + end_sequence (); + + emit_insns_before (seq, object); + if (! validate_change (object, loc, temp, 0) + && ! validate_replace_rtx (x, temp, object)) + abort (); + } + } + + return 1; + } + + /* Fall through to generic two-operand expression case. */ + case EXPR_LIST: + case CALL: + case COMPARE: + case MINUS: + case MULT: + case DIV: case UDIV: + case MOD: case UMOD: + case AND: case IOR: case XOR: + case LSHIFT: case ASHIFT: case ROTATE: + case ASHIFTRT: case LSHIFTRT: case ROTATERT: + case NE: case EQ: + case GE: case GT: case GEU: case GTU: + case LE: case LT: case LEU: case LTU: + if (XEXP (x, 1) && ! CONSTANT_P (XEXP (x, 1))) + instantiate_virtual_regs_1 (&XEXP (x, 1), object, extra_insns); + loc = &XEXP (x, 0); + goto restart; + + case MEM: + /* Most cases of MEM that convert to valid addresses have already been + handled by our scan of regno_reg_rtx. The only special handling we + need here is to make a copy of the rtx to ensure it isn't being + shared if we have to change it to a pseudo. + + If the rtx is a simple reference to an address via a virtual register, + it can potentially be shared. In such cases, first try to make it + a valid address, which can also be shared. Otherwise, copy it and + proceed normally. + + First check for common cases that need no processing. These are + usually due to instantiation already being done on a previous instance + of a shared rtx. */ + + temp = XEXP (x, 0); + if (CONSTANT_ADDRESS_P (temp) +#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + || temp == arg_pointer_rtx +#endif + || temp == frame_pointer_rtx) + return 1; + + if (GET_CODE (temp) == PLUS + && CONSTANT_ADDRESS_P (XEXP (temp, 1)) + && (XEXP (temp, 0) == frame_pointer_rtx +#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + || XEXP (temp, 0) == arg_pointer_rtx +#endif + )) + return 1; + + if (temp == virtual_stack_vars_rtx + || temp == virtual_incoming_args_rtx + || (GET_CODE (temp) == PLUS + && CONSTANT_ADDRESS_P (XEXP (temp, 1)) + && (XEXP (temp, 0) == virtual_stack_vars_rtx + || XEXP (temp, 0) == virtual_incoming_args_rtx))) + { + /* This MEM may be shared. If the substitution can be done without + the need to generate new pseudos, we want to do it in place + so all copies of the shared rtx benefit. The call below will + only make substitutions if the resulting address is still + valid. + + Note that we cannot pass X as the object in the recursive call + since the insn being processed may not allow all valid + addresses. However, if we were not passed on object, we can + only modify X without copying it if X will have a valid + address. + + ??? Also note that this can still lose if OBJECT is an insn that + has less restrictions on an address that some other insn. + In that case, we will modify the shared address. This case + doesn't seem very likely, though. */ + + if (instantiate_virtual_regs_1 (&XEXP (x, 0), + object ? object : x, 0)) + return 1; + + /* Otherwise make a copy and process that copy. We copy the entire + RTL expression since it might be a PLUS which could also be + shared. */ + *loc = x = copy_rtx (x); + } + + /* Fall through to generic unary operation case. */ + case USE: + case CLOBBER: + case SUBREG: + case STRICT_LOW_PART: + case NEG: case NOT: + case PRE_DEC: case PRE_INC: case POST_DEC: case POST_INC: + case SIGN_EXTEND: case ZERO_EXTEND: + case TRUNCATE: case FLOAT_EXTEND: case FLOAT_TRUNCATE: + case FLOAT: case FIX: + case UNSIGNED_FIX: case UNSIGNED_FLOAT: + case ABS: + case SQRT: + case FFS: + /* These case either have just one operand or we know that we need not + check the rest of the operands. */ + loc = &XEXP (x, 0); + goto restart; + + case REG: + /* Try to replace with a PLUS. If that doesn't work, compute the sum + in front of this insn and substitute the temporary. */ + if (x == virtual_incoming_args_rtx) + new = arg_pointer_rtx, offset = in_arg_offset; + else if (x == virtual_stack_vars_rtx) + new = frame_pointer_rtx, offset = var_offset; + else if (x == virtual_stack_dynamic_rtx) + new = stack_pointer_rtx, offset = dynamic_offset; + else if (x == virtual_outgoing_args_rtx) + new = stack_pointer_rtx, offset = out_arg_offset; + + if (new) + { + temp = plus_constant (new, offset); + if (!validate_change (object, loc, temp, 0)) + { + if (! extra_insns) + return 0; + + start_sequence (); + temp = force_operand (temp, NULL_RTX); + seq = get_insns (); + end_sequence (); + + emit_insns_before (seq, object); + if (! validate_change (object, loc, temp, 0) + && ! validate_replace_rtx (x, temp, object)) + abort (); + } + } + + return 1; + } + + /* Scan all subexpressions. */ + fmt = GET_RTX_FORMAT (code); + for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++) + if (*fmt == 'e') + { + if (!instantiate_virtual_regs_1 (&XEXP (x, i), object, extra_insns)) + return 0; + } + else if (*fmt == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + if (! instantiate_virtual_regs_1 (&XVECEXP (x, i, j), object, + extra_insns)) + return 0; + + return 1; +} + +/* Optimization: assuming this function does not receive nonlocal gotos, + delete the handlers for such, as well as the insns to establish + and disestablish them. */ + +static void +delete_handlers () +{ + rtx insn; + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + /* Delete the handler by turning off the flag that would + prevent jump_optimize from deleting it. + Also permit deletion of the nonlocal labels themselves + if nothing local refers to them. */ + if (GET_CODE (insn) == CODE_LABEL) + LABEL_PRESERVE_P (insn) = 0; + if (GET_CODE (insn) == INSN + && ((nonlocal_goto_handler_slot != 0 + && reg_mentioned_p (nonlocal_goto_handler_slot, PATTERN (insn))) + || (nonlocal_goto_stack_level != 0 + && reg_mentioned_p (nonlocal_goto_stack_level, + PATTERN (insn))))) + delete_insn (insn); + } +} + +/* Return a list (chain of EXPR_LIST nodes) for the nonlocal labels + of the current function. */ + +rtx +nonlocal_label_rtx_list () +{ + tree t; + rtx x = 0; + + for (t = nonlocal_labels; t; t = TREE_CHAIN (t)) + x = gen_rtx (EXPR_LIST, VOIDmode, label_rtx (TREE_VALUE (t)), x); + + return x; +} + +/* Output a USE for any register use in RTL. + This is used with -noreg to mark the extent of lifespan + of any registers used in a user-visible variable's DECL_RTL. */ + +void +use_variable (rtl) + rtx rtl; +{ + if (GET_CODE (rtl) == REG) + /* This is a register variable. */ + emit_insn (gen_rtx (USE, VOIDmode, rtl)); + else if (GET_CODE (rtl) == MEM + && GET_CODE (XEXP (rtl, 0)) == REG + && (REGNO (XEXP (rtl, 0)) < FIRST_VIRTUAL_REGISTER + || REGNO (XEXP (rtl, 0)) > LAST_VIRTUAL_REGISTER) + && XEXP (rtl, 0) != current_function_internal_arg_pointer) + /* This is a variable-sized structure. */ + emit_insn (gen_rtx (USE, VOIDmode, XEXP (rtl, 0))); +} + +/* Like use_variable except that it outputs the USEs after INSN + instead of at the end of the insn-chain. */ + +void +use_variable_after (rtl, insn) + rtx rtl, insn; +{ + if (GET_CODE (rtl) == REG) + /* This is a register variable. */ + emit_insn_after (gen_rtx (USE, VOIDmode, rtl), insn); + else if (GET_CODE (rtl) == MEM + && GET_CODE (XEXP (rtl, 0)) == REG + && (REGNO (XEXP (rtl, 0)) < FIRST_VIRTUAL_REGISTER + || REGNO (XEXP (rtl, 0)) > LAST_VIRTUAL_REGISTER) + && XEXP (rtl, 0) != current_function_internal_arg_pointer) + /* This is a variable-sized structure. */ + emit_insn_after (gen_rtx (USE, VOIDmode, XEXP (rtl, 0)), insn); +} + +int +max_parm_reg_num () +{ + return max_parm_reg; +} + +/* Return the first insn following those generated by `assign_parms'. */ + +rtx +get_first_nonparm_insn () +{ + if (last_parm_insn) + return NEXT_INSN (last_parm_insn); + return get_insns (); +} + +/* Return the first NOTE_INSN_BLOCK_BEG note in the function. + Crash if there is none. */ + +rtx +get_first_block_beg () +{ + register rtx searcher; + register rtx insn = get_first_nonparm_insn (); + + for (searcher = insn; searcher; searcher = NEXT_INSN (searcher)) + if (GET_CODE (searcher) == NOTE + && NOTE_LINE_NUMBER (searcher) == NOTE_INSN_BLOCK_BEG) + return searcher; + + abort (); /* Invalid call to this function. (See comments above.) */ + return NULL_RTX; +} + +/* Return 1 if EXP returns an aggregate value, for which an address + must be passed to the function or returned by the function. */ + +int +aggregate_value_p (exp) + tree exp; +{ + int i, regno, nregs; + rtx reg; + if (RETURN_IN_MEMORY (TREE_TYPE (exp))) + return 1; + if (flag_pcc_struct_return + && (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE + || TREE_CODE (TREE_TYPE (exp)) == QUAL_UNION_TYPE)) + return 1; + /* Make sure we have suitable call-clobbered regs to return + the value in; if not, we must return it in memory. */ + reg = hard_function_value (TREE_TYPE (exp), 0); + regno = REGNO (reg); + nregs = HARD_REGNO_NREGS (regno, TYPE_MODE (TREE_TYPE (exp))); + for (i = 0; i < nregs; i++) + if (! call_used_regs[regno + i]) + return 1; + return 0; +} + +/* Assign RTL expressions to the function's parameters. + This may involve copying them into registers and using + those registers as the RTL for them. + + If SECOND_TIME is non-zero it means that this function is being + called a second time. This is done by integrate.c when a function's + compilation is deferred. We need to come back here in case the + FUNCTION_ARG macro computes items needed for the rest of the compilation + (such as changing which registers are fixed or caller-saved). But suppress + writing any insns or setting DECL_RTL of anything in this case. */ + +void +assign_parms (fndecl, second_time) + tree fndecl; + int second_time; +{ + register tree parm; + register rtx entry_parm = 0; + register rtx stack_parm = 0; + CUMULATIVE_ARGS args_so_far; + enum machine_mode promoted_mode, passed_mode, nominal_mode; + int unsignedp; + /* Total space needed so far for args on the stack, + given as a constant and a tree-expression. */ + struct args_size stack_args_size; + tree fntype = TREE_TYPE (fndecl); + tree fnargs = DECL_ARGUMENTS (fndecl); + /* This is used for the arg pointer when referring to stack args. */ + rtx internal_arg_pointer; + /* This is a dummy PARM_DECL that we used for the function result if + the function returns a structure. */ + tree function_result_decl = 0; + int nparmregs = list_length (fnargs) + LAST_VIRTUAL_REGISTER + 1; + int varargs_setup = 0; + rtx conversion_insns = 0; + /* FUNCTION_ARG may look at this variable. Since this is not + expanding a call it will always be zero in this function. */ + int current_call_is_indirect = 0; + + /* Nonzero if the last arg is named `__builtin_va_alist', + which is used on some machines for old-fashioned non-ANSI varargs.h; + this should be stuck onto the stack as if it had arrived there. */ + int vararg + = (fnargs + && (parm = tree_last (fnargs)) != 0 + && DECL_NAME (parm) + && (! strcmp (IDENTIFIER_POINTER (DECL_NAME (parm)), + "__builtin_va_alist"))); + + /* Nonzero if function takes extra anonymous args. + This means the last named arg must be on the stack + right before the anonymous ones. */ + int stdarg + = (TYPE_ARG_TYPES (fntype) != 0 + && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) + != void_type_node)); + + /* If the reg that the virtual arg pointer will be translated into is + not a fixed reg or is the stack pointer, make a copy of the virtual + arg pointer, and address parms via the copy. The frame pointer is + considered fixed even though it is not marked as such. + + The second time through, simply use ap to avoid generating rtx. */ + + if ((ARG_POINTER_REGNUM == STACK_POINTER_REGNUM + || ! (fixed_regs[ARG_POINTER_REGNUM] + || ARG_POINTER_REGNUM == FRAME_POINTER_REGNUM)) + && ! second_time) + internal_arg_pointer = copy_to_reg (virtual_incoming_args_rtx); + else + internal_arg_pointer = virtual_incoming_args_rtx; + current_function_internal_arg_pointer = internal_arg_pointer; + + stack_args_size.constant = 0; + stack_args_size.var = 0; + + /* If struct value address is treated as the first argument, make it so. */ + if (aggregate_value_p (DECL_RESULT (fndecl)) + && ! current_function_returns_pcc_struct + && struct_value_incoming_rtx == 0) + { + tree type = build_pointer_type (fntype); + + function_result_decl = build_decl (PARM_DECL, NULL_TREE, type); + + DECL_ARG_TYPE (function_result_decl) = type; + TREE_CHAIN (function_result_decl) = fnargs; + fnargs = function_result_decl; + } + + parm_reg_stack_loc = (rtx *) oballoc (nparmregs * sizeof (rtx)); + bzero (parm_reg_stack_loc, nparmregs * sizeof (rtx)); + +#ifdef INIT_CUMULATIVE_INCOMING_ARGS + INIT_CUMULATIVE_INCOMING_ARGS (args_so_far, fntype, NULL_RTX); +#else + INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX); +#endif + + /* We haven't yet found an argument that we must push and pretend the + caller did. */ + current_function_pretend_args_size = 0; + + for (parm = fnargs; parm; parm = TREE_CHAIN (parm)) + { + int aggregate + = (TREE_CODE (TREE_TYPE (parm)) == ARRAY_TYPE + || TREE_CODE (TREE_TYPE (parm)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (parm)) == UNION_TYPE + || TREE_CODE (TREE_TYPE (parm)) == QUAL_UNION_TYPE); + struct args_size stack_offset; + struct args_size arg_size; + int passed_pointer = 0; + tree passed_type = DECL_ARG_TYPE (parm); + + /* Set LAST_NAMED if this is last named arg before some + anonymous args. We treat it as if it were anonymous too. */ + int last_named = ((TREE_CHAIN (parm) == 0 + || DECL_NAME (TREE_CHAIN (parm)) == 0) + && (vararg || stdarg)); + + if (TREE_TYPE (parm) == error_mark_node + /* This can happen after weird syntax errors + or if an enum type is defined among the parms. */ + || TREE_CODE (parm) != PARM_DECL + || passed_type == NULL) + { + DECL_INCOMING_RTL (parm) = DECL_RTL (parm) = gen_rtx (MEM, BLKmode, + const0_rtx); + TREE_USED (parm) = 1; + continue; + } + + /* For varargs.h function, save info about regs and stack space + used by the individual args, not including the va_alist arg. */ + if (vararg && last_named) + current_function_args_info = args_so_far; + + /* Find mode of arg as it is passed, and mode of arg + as it should be during execution of this function. */ + passed_mode = TYPE_MODE (passed_type); + nominal_mode = TYPE_MODE (TREE_TYPE (parm)); + + /* If the parm's mode is VOID, its value doesn't matter, + and avoid the usual things like emit_move_insn that could crash. */ + if (nominal_mode == VOIDmode) + { + DECL_INCOMING_RTL (parm) = DECL_RTL (parm) = const0_rtx; + continue; + } + +#ifdef FUNCTION_ARG_PASS_BY_REFERENCE + /* See if this arg was passed by invisible reference. */ + if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, passed_mode, + passed_type, ! last_named)) + { + passed_type = build_pointer_type (passed_type); + passed_pointer = 1; + passed_mode = nominal_mode = Pmode; + } +#endif + + promoted_mode = passed_mode; + +#ifdef PROMOTE_FUNCTION_ARGS + /* Compute the mode in which the arg is actually extended to. */ + if (TREE_CODE (passed_type) == INTEGER_TYPE + || TREE_CODE (passed_type) == ENUMERAL_TYPE + || TREE_CODE (passed_type) == BOOLEAN_TYPE + || TREE_CODE (passed_type) == CHAR_TYPE + || TREE_CODE (passed_type) == REAL_TYPE + || TREE_CODE (passed_type) == POINTER_TYPE + || TREE_CODE (passed_type) == OFFSET_TYPE) + { + unsignedp = TREE_UNSIGNED (passed_type); + PROMOTE_MODE (promoted_mode, unsignedp, passed_type); + } +#endif + + /* Let machine desc say which reg (if any) the parm arrives in. + 0 means it arrives on the stack. */ +#ifdef FUNCTION_INCOMING_ARG + entry_parm = FUNCTION_INCOMING_ARG (args_so_far, promoted_mode, + passed_type, ! last_named); +#else + entry_parm = FUNCTION_ARG (args_so_far, promoted_mode, + passed_type, ! last_named); +#endif + + if (entry_parm) + passed_mode = promoted_mode; + +#ifdef SETUP_INCOMING_VARARGS + /* If this is the last named parameter, do any required setup for + varargs or stdargs. We need to know about the case of this being an + addressable type, in which case we skip the registers it + would have arrived in. + + For stdargs, LAST_NAMED will be set for two parameters, the one that + is actually the last named, and the dummy parameter. We only + want to do this action once. + + Also, indicate when RTL generation is to be suppressed. */ + if (last_named && !varargs_setup) + { + SETUP_INCOMING_VARARGS (args_so_far, passed_mode, passed_type, + current_function_pretend_args_size, + second_time); + varargs_setup = 1; + } +#endif + + /* Determine parm's home in the stack, + in case it arrives in the stack or we should pretend it did. + + Compute the stack position and rtx where the argument arrives + and its size. + + There is one complexity here: If this was a parameter that would + have been passed in registers, but wasn't only because it is + __builtin_va_alist, we want locate_and_pad_parm to treat it as if + it came in a register so that REG_PARM_STACK_SPACE isn't skipped. + In this case, we call FUNCTION_ARG with NAMED set to 1 instead of + 0 as it was the previous time. */ + + locate_and_pad_parm (passed_mode, passed_type, +#ifdef STACK_PARMS_IN_REG_PARM_AREA + 1, +#else +#ifdef FUNCTION_INCOMING_ARG + FUNCTION_INCOMING_ARG (args_so_far, passed_mode, + passed_type, + (! last_named + || varargs_setup)) != 0, +#else + FUNCTION_ARG (args_so_far, passed_mode, + passed_type, + ! last_named || varargs_setup) != 0, +#endif +#endif + fndecl, &stack_args_size, &stack_offset, &arg_size); + + if (! second_time) + { + rtx offset_rtx = ARGS_SIZE_RTX (stack_offset); + + if (offset_rtx == const0_rtx) + stack_parm = gen_rtx (MEM, passed_mode, internal_arg_pointer); + else + stack_parm = gen_rtx (MEM, passed_mode, + gen_rtx (PLUS, Pmode, + internal_arg_pointer, offset_rtx)); + + /* If this is a memory ref that contains aggregate components, + mark it as such for cse and loop optimize. */ + MEM_IN_STRUCT_P (stack_parm) = aggregate; + } + + /* If this parameter was passed both in registers and in the stack, + use the copy on the stack. */ + if (MUST_PASS_IN_STACK (passed_mode, passed_type)) + entry_parm = 0; + +#ifdef FUNCTION_ARG_PARTIAL_NREGS + /* If this parm was passed part in regs and part in memory, + pretend it arrived entirely in memory + by pushing the register-part onto the stack. + + In the special case of a DImode or DFmode that is split, + we could put it together in a pseudoreg directly, + but for now that's not worth bothering with. */ + + if (entry_parm) + { + int nregs = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, passed_mode, + passed_type, ! last_named); + + if (nregs > 0) + { + current_function_pretend_args_size + = (((nregs * UNITS_PER_WORD) + (PARM_BOUNDARY / BITS_PER_UNIT) - 1) + / (PARM_BOUNDARY / BITS_PER_UNIT) + * (PARM_BOUNDARY / BITS_PER_UNIT)); + + if (! second_time) + move_block_from_reg (REGNO (entry_parm), + validize_mem (stack_parm), nregs); + entry_parm = stack_parm; + } + } +#endif + + /* If we didn't decide this parm came in a register, + by default it came on the stack. */ + if (entry_parm == 0) + entry_parm = stack_parm; + + /* Record permanently how this parm was passed. */ + if (! second_time) + DECL_INCOMING_RTL (parm) = entry_parm; + + /* If there is actually space on the stack for this parm, + count it in stack_args_size; otherwise set stack_parm to 0 + to indicate there is no preallocated stack slot for the parm. */ + + if (entry_parm == stack_parm +#if defined (REG_PARM_STACK_SPACE) && ! defined (MAYBE_REG_PARM_STACK_SPACE) + /* On some machines, even if a parm value arrives in a register + there is still an (uninitialized) stack slot allocated for it. + + ??? When MAYBE_REG_PARM_STACK_SPACE is defined, we can't tell + whether this parameter already has a stack slot allocated, + because an arg block exists only if current_function_args_size + is larger than some threshhold, and we haven't calculated that + yet. So, for now, we just assume that stack slots never exist + in this case. */ + || REG_PARM_STACK_SPACE (fndecl) > 0 +#endif + ) + { + stack_args_size.constant += arg_size.constant; + if (arg_size.var) + ADD_PARM_SIZE (stack_args_size, arg_size.var); + } + else + /* No stack slot was pushed for this parm. */ + stack_parm = 0; + + /* Update info on where next arg arrives in registers. */ + + FUNCTION_ARG_ADVANCE (args_so_far, passed_mode, + passed_type, ! last_named); + + /* If this is our second time through, we are done with this parm. */ + if (second_time) + continue; + + /* If we can't trust the parm stack slot to be aligned enough + for its ultimate type, don't use that slot after entry. + We'll make another stack slot, if we need one. */ + { + int thisparm_boundary + = FUNCTION_ARG_BOUNDARY (passed_mode, passed_type); + + if (GET_MODE_ALIGNMENT (nominal_mode) > thisparm_boundary) + stack_parm = 0; + } + + /* Now adjust STACK_PARM to the mode and precise location + where this parameter should live during execution, + if we discover that it must live in the stack during execution. + To make debuggers happier on big-endian machines, we store + the value in the last bytes of the space available. */ + + if (nominal_mode != BLKmode && nominal_mode != passed_mode + && stack_parm != 0) + { + rtx offset_rtx; + +#if BYTES_BIG_ENDIAN + if (GET_MODE_SIZE (nominal_mode) < UNITS_PER_WORD) + stack_offset.constant += (GET_MODE_SIZE (passed_mode) + - GET_MODE_SIZE (nominal_mode)); +#endif + + offset_rtx = ARGS_SIZE_RTX (stack_offset); + if (offset_rtx == const0_rtx) + stack_parm = gen_rtx (MEM, nominal_mode, internal_arg_pointer); + else + stack_parm = gen_rtx (MEM, nominal_mode, + gen_rtx (PLUS, Pmode, + internal_arg_pointer, offset_rtx)); + + /* If this is a memory ref that contains aggregate components, + mark it as such for cse and loop optimize. */ + MEM_IN_STRUCT_P (stack_parm) = aggregate; + } + + /* ENTRY_PARM is an RTX for the parameter as it arrives, + in the mode in which it arrives. + STACK_PARM is an RTX for a stack slot where the parameter can live + during the function (in case we want to put it there). + STACK_PARM is 0 if no stack slot was pushed for it. + + Now output code if necessary to convert ENTRY_PARM to + the type in which this function declares it, + and store that result in an appropriate place, + which may be a pseudo reg, may be STACK_PARM, + or may be a local stack slot if STACK_PARM is 0. + + Set DECL_RTL to that place. */ + + if (nominal_mode == BLKmode) + { + /* If a BLKmode arrives in registers, copy it to a stack slot. */ + if (GET_CODE (entry_parm) == REG) + { + int size_stored = CEIL_ROUND (int_size_in_bytes (TREE_TYPE (parm)), + UNITS_PER_WORD); + + /* Note that we will be storing an integral number of words. + So we have to be careful to ensure that we allocate an + integral number of words. We do this below in the + assign_stack_local if space was not allocated in the argument + list. If it was, this will not work if PARM_BOUNDARY is not + a multiple of BITS_PER_WORD. It isn't clear how to fix this + if it becomes a problem. */ + + if (stack_parm == 0) + { + stack_parm + = assign_stack_local (GET_MODE (entry_parm), size_stored, 0); + /* If this is a memory ref that contains aggregate components, + mark it as such for cse and loop optimize. */ + MEM_IN_STRUCT_P (stack_parm) = aggregate; + } + + else if (PARM_BOUNDARY % BITS_PER_WORD != 0) + abort (); + + move_block_from_reg (REGNO (entry_parm), + validize_mem (stack_parm), + size_stored / UNITS_PER_WORD); + } + DECL_RTL (parm) = stack_parm; + } + else if (! ((obey_regdecls && ! DECL_REGISTER (parm) + && ! DECL_INLINE (fndecl)) + /* layout_decl may set this. */ + || TREE_ADDRESSABLE (parm) + || TREE_SIDE_EFFECTS (parm) + /* If -ffloat-store specified, don't put explicit + float variables into registers. */ + || (flag_float_store + && TREE_CODE (TREE_TYPE (parm)) == REAL_TYPE)) + /* Always assign pseudo to structure return or item passed + by invisible reference. */ + || passed_pointer || parm == function_result_decl) + { + /* Store the parm in a pseudoregister during the function, but we + may need to do it in a wider mode. */ + + register rtx parmreg; + + unsignedp = TREE_UNSIGNED (TREE_TYPE (parm)); + if (TREE_CODE (TREE_TYPE (parm)) == INTEGER_TYPE + || TREE_CODE (TREE_TYPE (parm)) == ENUMERAL_TYPE + || TREE_CODE (TREE_TYPE (parm)) == BOOLEAN_TYPE + || TREE_CODE (TREE_TYPE (parm)) == CHAR_TYPE + || TREE_CODE (TREE_TYPE (parm)) == REAL_TYPE + || TREE_CODE (TREE_TYPE (parm)) == POINTER_TYPE + || TREE_CODE (TREE_TYPE (parm)) == OFFSET_TYPE) + { + PROMOTE_MODE (nominal_mode, unsignedp, TREE_TYPE (parm)); + } + + parmreg = gen_reg_rtx (nominal_mode); + REG_USERVAR_P (parmreg) = 1; + + /* If this was an item that we received a pointer to, set DECL_RTL + appropriately. */ + if (passed_pointer) + { + DECL_RTL (parm) = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (passed_type)), parmreg); + MEM_IN_STRUCT_P (DECL_RTL (parm)) = aggregate; + } + else + DECL_RTL (parm) = parmreg; + + /* Copy the value into the register. */ + if (GET_MODE (parmreg) != GET_MODE (entry_parm)) + { + /* If ENTRY_PARM is a hard register, it might be in a register + not valid for operating in its mode (e.g., an odd-numbered + register for a DFmode). In that case, moves are the only + thing valid, so we can't do a convert from there. This + occurs when the calling sequence allow such misaligned + usages. + + In addition, the conversion may involve a call, which could + clobber parameters which haven't been copied to pseudo + registers yet. Therefore, we must first copy the parm to + a pseudo reg here, and save the conversion until after all + parameters have been moved. */ + + rtx tempreg = gen_reg_rtx (GET_MODE (entry_parm)); + + emit_move_insn (tempreg, validize_mem (entry_parm)); + + push_to_sequence (conversion_insns); + convert_move (parmreg, tempreg, unsignedp); + conversion_insns = get_insns (); + end_sequence (); + } + else + emit_move_insn (parmreg, validize_mem (entry_parm)); + + /* If we were passed a pointer but the actual value + can safely live in a register, put it in one. */ + if (passed_pointer && TYPE_MODE (TREE_TYPE (parm)) != BLKmode + && ! ((obey_regdecls && ! DECL_REGISTER (parm) + && ! DECL_INLINE (fndecl)) + /* layout_decl may set this. */ + || TREE_ADDRESSABLE (parm) + || TREE_SIDE_EFFECTS (parm) + /* If -ffloat-store specified, don't put explicit + float variables into registers. */ + || (flag_float_store + && TREE_CODE (TREE_TYPE (parm)) == REAL_TYPE))) + { + /* We can't use nominal_mode, because it will have been set to + Pmode above. We must use the actual mode of the parm. */ + parmreg = gen_reg_rtx (TYPE_MODE (TREE_TYPE (parm))); + emit_move_insn (parmreg, DECL_RTL (parm)); + DECL_RTL (parm) = parmreg; + } +#ifdef FUNCTION_ARG_CALLEE_COPIES + /* If we are passed an arg by reference and it is our responsibility + to make a copy, do it now. + PASSED_TYPE and PASSED mode now refer to the pointer, not the + original argument, so we must recreate them in the call to + FUNCTION_ARG_CALLEE_COPIES. */ + /* ??? Later add code to handle the case that if the argument isn't + modified, don't do the copy. */ + + else if (passed_pointer + && FUNCTION_ARG_CALLEE_COPIES (args_so_far, + TYPE_MODE (DECL_ARG_TYPE (parm)), + DECL_ARG_TYPE (parm), + ! last_named)) + { + rtx copy; + tree type = DECL_ARG_TYPE (parm); + + /* This sequence may involve a library call perhaps clobbering + registers that haven't been copied to pseudos yet. */ + + push_to_sequence (conversion_insns); + + if (TYPE_SIZE (type) == 0 + || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) + { + /* This is a variable sized object. */ + /* ??? Can we use expr_size here? */ + rtx size_rtx = expand_expr (size_in_bytes (type), NULL_RTX, + TYPE_MODE (sizetype), 0); + + copy = gen_rtx (MEM, BLKmode, + allocate_dynamic_stack_space (size_rtx, NULL_RTX, + TYPE_ALIGN (type))); + } + else + { + int size = int_size_in_bytes (type); + copy = assign_stack_temp (TYPE_MODE (type), size, 1); + } + + store_expr (parm, copy, 0); + emit_move_insn (parmreg, XEXP (copy, 0)); + conversion_insns = get_insns (); + end_sequence (); + } +#endif /* FUNCTION_ARG_CALLEE_COPIES */ + + /* In any case, record the parm's desired stack location + in case we later discover it must live in the stack. */ + if (REGNO (parmreg) >= nparmregs) + { + rtx *new; + nparmregs = REGNO (parmreg) + 5; + new = (rtx *) oballoc (nparmregs * sizeof (rtx)); + bcopy (parm_reg_stack_loc, new, nparmregs * sizeof (rtx)); + parm_reg_stack_loc = new; + } + parm_reg_stack_loc[REGNO (parmreg)] = stack_parm; + + /* Mark the register as eliminable if we did no conversion + and it was copied from memory at a fixed offset, + and the arg pointer was not copied to a pseudo-reg. + If the arg pointer is a pseudo reg or the offset formed + an invalid address, such memory-equivalences + as we make here would screw up life analysis for it. */ + if (nominal_mode == passed_mode + && GET_CODE (entry_parm) == MEM + && entry_parm == stack_parm + && stack_offset.var == 0 + && reg_mentioned_p (virtual_incoming_args_rtx, + XEXP (entry_parm, 0))) + REG_NOTES (get_last_insn ()) + = gen_rtx (EXPR_LIST, REG_EQUIV, + entry_parm, REG_NOTES (get_last_insn ())); + + /* For pointer data type, suggest pointer register. */ + if (TREE_CODE (TREE_TYPE (parm)) == POINTER_TYPE) + mark_reg_pointer (parmreg); + } + else + { + /* Value must be stored in the stack slot STACK_PARM + during function execution. */ + + if (passed_mode != nominal_mode) + { + /* Conversion is required. */ + rtx tempreg = gen_reg_rtx (GET_MODE (entry_parm)); + + emit_move_insn (tempreg, validize_mem (entry_parm)); + + push_to_sequence (conversion_insns); + entry_parm = convert_to_mode (nominal_mode, tempreg, + TREE_UNSIGNED (TREE_TYPE (parm))); + conversion_insns = get_insns (); + end_sequence (); + } + + if (entry_parm != stack_parm) + { + if (stack_parm == 0) + { + stack_parm + = assign_stack_local (GET_MODE (entry_parm), + GET_MODE_SIZE (GET_MODE (entry_parm)), 0); + /* If this is a memory ref that contains aggregate components, + mark it as such for cse and loop optimize. */ + MEM_IN_STRUCT_P (stack_parm) = aggregate; + } + + if (passed_mode != nominal_mode) + { + push_to_sequence (conversion_insns); + emit_move_insn (validize_mem (stack_parm), + validize_mem (entry_parm)); + conversion_insns = get_insns (); + end_sequence (); + } + else + emit_move_insn (validize_mem (stack_parm), + validize_mem (entry_parm)); + } + + DECL_RTL (parm) = stack_parm; + } + + /* If this "parameter" was the place where we are receiving the + function's incoming structure pointer, set up the result. */ + if (parm == function_result_decl) + DECL_RTL (DECL_RESULT (fndecl)) + = gen_rtx (MEM, DECL_MODE (DECL_RESULT (fndecl)), DECL_RTL (parm)); + + if (TREE_THIS_VOLATILE (parm)) + MEM_VOLATILE_P (DECL_RTL (parm)) = 1; + if (TREE_READONLY (parm)) + RTX_UNCHANGING_P (DECL_RTL (parm)) = 1; + } + + /* Output all parameter conversion instructions (possibly including calls) + now that all parameters have been copied out of hard registers. */ + emit_insns (conversion_insns); + + max_parm_reg = max_reg_num (); + last_parm_insn = get_last_insn (); + + current_function_args_size = stack_args_size.constant; + + /* Adjust function incoming argument size for alignment and + minimum length. */ + +#ifdef REG_PARM_STACK_SPACE +#ifndef MAYBE_REG_PARM_STACK_SPACE + current_function_args_size = MAX (current_function_args_size, + REG_PARM_STACK_SPACE (fndecl)); +#endif +#endif + +#ifdef STACK_BOUNDARY +#define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT) + + current_function_args_size + = ((current_function_args_size + STACK_BYTES - 1) + / STACK_BYTES) * STACK_BYTES; +#endif + +#ifdef ARGS_GROW_DOWNWARD + current_function_arg_offset_rtx + = (stack_args_size.var == 0 ? GEN_INT (-stack_args_size.constant) + : expand_expr (size_binop (MINUS_EXPR, stack_args_size.var, + size_int (-stack_args_size.constant)), + NULL_RTX, VOIDmode, 0)); +#else + current_function_arg_offset_rtx = ARGS_SIZE_RTX (stack_args_size); +#endif + + /* See how many bytes, if any, of its args a function should try to pop + on return. */ + + current_function_pops_args = RETURN_POPS_ARGS (TREE_TYPE (fndecl), + current_function_args_size); + + /* For stdarg.h function, save info about regs and stack space + used by the named args. */ + + if (stdarg) + current_function_args_info = args_so_far; + + /* Set the rtx used for the function return value. Put this in its + own variable so any optimizers that need this information don't have + to include tree.h. Do this here so it gets done when an inlined + function gets output. */ + + current_function_return_rtx = DECL_RTL (DECL_RESULT (fndecl)); +} + +/* Indicate whether REGNO is an incoming argument to the current function + that was promoted to a wider mode. If so, return the RTX for the + register (to get its mode). PMODE and PUNSIGNEDP are set to the mode + that REGNO is promoted from and whether the promotion was signed or + unsigned. */ + +#ifdef PROMOTE_FUNCTION_ARGS + +rtx +promoted_input_arg (regno, pmode, punsignedp) + int regno; + enum machine_mode *pmode; + int *punsignedp; +{ + tree arg; + + for (arg = DECL_ARGUMENTS (current_function_decl); arg; + arg = TREE_CHAIN (arg)) + if (GET_CODE (DECL_INCOMING_RTL (arg)) == REG + && REGNO (DECL_INCOMING_RTL (arg)) == regno + && (TREE_CODE (TREE_TYPE (arg)) == INTEGER_TYPE + || TREE_CODE (TREE_TYPE (arg)) == ENUMERAL_TYPE + || TREE_CODE (TREE_TYPE (arg)) == BOOLEAN_TYPE + || TREE_CODE (TREE_TYPE (arg)) == CHAR_TYPE + || TREE_CODE (TREE_TYPE (arg)) == REAL_TYPE + || TREE_CODE (TREE_TYPE (arg)) == POINTER_TYPE + || TREE_CODE (TREE_TYPE (arg)) == OFFSET_TYPE)) + { + enum machine_mode mode = TYPE_MODE (TREE_TYPE (arg)); + int unsignedp = TREE_UNSIGNED (TREE_TYPE (arg)); + + PROMOTE_MODE (mode, unsignedp, TREE_TYPE (arg)); + if (mode == GET_MODE (DECL_INCOMING_RTL (arg)) + && mode != DECL_MODE (arg)) + { + *pmode = DECL_MODE (arg); + *punsignedp = unsignedp; + return DECL_INCOMING_RTL (arg); + } + } + + return 0; +} + +#endif + +/* Compute the size and offset from the start of the stacked arguments for a + parm passed in mode PASSED_MODE and with type TYPE. + + INITIAL_OFFSET_PTR points to the current offset into the stacked + arguments. + + The starting offset and size for this parm are returned in *OFFSET_PTR + and *ARG_SIZE_PTR, respectively. + + IN_REGS is non-zero if the argument will be passed in registers. It will + never be set if REG_PARM_STACK_SPACE is not defined. + + FNDECL is the function in which the argument was defined. + + There are two types of rounding that are done. The first, controlled by + FUNCTION_ARG_BOUNDARY, forces the offset from the start of the argument + list to be aligned to the specific boundary (in bits). This rounding + affects the initial and starting offsets, but not the argument size. + + The second, controlled by FUNCTION_ARG_PADDING and PARM_BOUNDARY, + optionally rounds the size of the parm to PARM_BOUNDARY. The + initial offset is not affected by this rounding, while the size always + is and the starting offset may be. */ + +/* offset_ptr will be negative for ARGS_GROW_DOWNWARD case; + initial_offset_ptr is positive because locate_and_pad_parm's + callers pass in the total size of args so far as + initial_offset_ptr. arg_size_ptr is always positive.*/ + +static void pad_to_arg_alignment (), pad_below (); + +void +locate_and_pad_parm (passed_mode, type, in_regs, fndecl, + initial_offset_ptr, offset_ptr, arg_size_ptr) + enum machine_mode passed_mode; + tree type; + int in_regs; + tree fndecl; + struct args_size *initial_offset_ptr; + struct args_size *offset_ptr; + struct args_size *arg_size_ptr; +{ + tree sizetree + = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode)); + enum direction where_pad = FUNCTION_ARG_PADDING (passed_mode, type); + int boundary = FUNCTION_ARG_BOUNDARY (passed_mode, type); + int boundary_in_bytes = boundary / BITS_PER_UNIT; + int reg_parm_stack_space = 0; + +#ifdef REG_PARM_STACK_SPACE + /* If we have found a stack parm before we reach the end of the + area reserved for registers, skip that area. */ + if (! in_regs) + { +#ifdef MAYBE_REG_PARM_STACK_SPACE + reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE; +#else + reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl); +#endif + if (reg_parm_stack_space > 0) + { + if (initial_offset_ptr->var) + { + initial_offset_ptr->var + = size_binop (MAX_EXPR, ARGS_SIZE_TREE (*initial_offset_ptr), + size_int (reg_parm_stack_space)); + initial_offset_ptr->constant = 0; + } + else if (initial_offset_ptr->constant < reg_parm_stack_space) + initial_offset_ptr->constant = reg_parm_stack_space; + } + } +#endif /* REG_PARM_STACK_SPACE */ + + arg_size_ptr->var = 0; + arg_size_ptr->constant = 0; + +#ifdef ARGS_GROW_DOWNWARD + if (initial_offset_ptr->var) + { + offset_ptr->constant = 0; + offset_ptr->var = size_binop (MINUS_EXPR, integer_zero_node, + initial_offset_ptr->var); + } + else + { + offset_ptr->constant = - initial_offset_ptr->constant; + offset_ptr->var = 0; + } + if (where_pad == upward + && (TREE_CODE (sizetree) != INTEGER_CST + || ((TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY))) + sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT); + SUB_PARM_SIZE (*offset_ptr, sizetree); + if (where_pad != downward) + pad_to_arg_alignment (offset_ptr, boundary); + if (initial_offset_ptr->var) + { + arg_size_ptr->var = size_binop (MINUS_EXPR, + size_binop (MINUS_EXPR, + integer_zero_node, + initial_offset_ptr->var), + offset_ptr->var); + } + else + { + arg_size_ptr->constant = (- initial_offset_ptr->constant - + offset_ptr->constant); + } +/* ADD_PARM_SIZE (*arg_size_ptr, sizetree); */ + if (where_pad == downward) + pad_below (arg_size_ptr, passed_mode, sizetree); +#else /* !ARGS_GROW_DOWNWARD */ + pad_to_arg_alignment (initial_offset_ptr, boundary); + *offset_ptr = *initial_offset_ptr; + if (where_pad == downward) + pad_below (offset_ptr, passed_mode, sizetree); + +#ifdef PUSH_ROUNDING + if (passed_mode != BLKmode) + sizetree = size_int (PUSH_ROUNDING (TREE_INT_CST_LOW (sizetree))); +#endif + + if (where_pad != none + && (TREE_CODE (sizetree) != INTEGER_CST + || ((TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY))) + sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT); + + ADD_PARM_SIZE (*arg_size_ptr, sizetree); +#endif /* ARGS_GROW_DOWNWARD */ +} + +/* Round the stack offset in *OFFSET_PTR up to a multiple of BOUNDARY. + BOUNDARY is measured in bits, but must be a multiple of a storage unit. */ + +static void +pad_to_arg_alignment (offset_ptr, boundary) + struct args_size *offset_ptr; + int boundary; +{ + int boundary_in_bytes = boundary / BITS_PER_UNIT; + + if (boundary > BITS_PER_UNIT) + { + if (offset_ptr->var) + { + offset_ptr->var = +#ifdef ARGS_GROW_DOWNWARD + round_down +#else + round_up +#endif + (ARGS_SIZE_TREE (*offset_ptr), + boundary / BITS_PER_UNIT); + offset_ptr->constant = 0; /*?*/ + } + else + offset_ptr->constant = +#ifdef ARGS_GROW_DOWNWARD + FLOOR_ROUND (offset_ptr->constant, boundary_in_bytes); +#else + CEIL_ROUND (offset_ptr->constant, boundary_in_bytes); +#endif + } +} + +static void +pad_below (offset_ptr, passed_mode, sizetree) + struct args_size *offset_ptr; + enum machine_mode passed_mode; + tree sizetree; +{ + if (passed_mode != BLKmode) + { + if (GET_MODE_BITSIZE (passed_mode) % PARM_BOUNDARY) + offset_ptr->constant + += (((GET_MODE_BITSIZE (passed_mode) + PARM_BOUNDARY - 1) + / PARM_BOUNDARY * PARM_BOUNDARY / BITS_PER_UNIT) + - GET_MODE_SIZE (passed_mode)); + } + else + { + if (TREE_CODE (sizetree) != INTEGER_CST + || (TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY) + { + /* Round the size up to multiple of PARM_BOUNDARY bits. */ + tree s2 = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT); + /* Add it in. */ + ADD_PARM_SIZE (*offset_ptr, s2); + SUB_PARM_SIZE (*offset_ptr, sizetree); + } + } +} + +static tree +round_down (value, divisor) + tree value; + int divisor; +{ + return size_binop (MULT_EXPR, + size_binop (FLOOR_DIV_EXPR, value, size_int (divisor)), + size_int (divisor)); +} + +/* Walk the tree of blocks describing the binding levels within a function + and warn about uninitialized variables. + This is done after calling flow_analysis and before global_alloc + clobbers the pseudo-regs to hard regs. */ + +void +uninitialized_vars_warning (block) + tree block; +{ + register tree decl, sub; + for (decl = BLOCK_VARS (block); decl; decl = TREE_CHAIN (decl)) + { + if (TREE_CODE (decl) == VAR_DECL + /* These warnings are unreliable for and aggregates + because assigning the fields one by one can fail to convince + flow.c that the entire aggregate was initialized. + Unions are troublesome because members may be shorter. */ + && TREE_CODE (TREE_TYPE (decl)) != RECORD_TYPE + && TREE_CODE (TREE_TYPE (decl)) != UNION_TYPE + && TREE_CODE (TREE_TYPE (decl)) != QUAL_UNION_TYPE + && TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE + && DECL_RTL (decl) != 0 + && GET_CODE (DECL_RTL (decl)) == REG + && regno_uninitialized (REGNO (DECL_RTL (decl)))) + warning_with_decl (decl, + "`%s' may be used uninitialized in this function"); + if (TREE_CODE (decl) == VAR_DECL + && DECL_RTL (decl) != 0 + && GET_CODE (DECL_RTL (decl)) == REG + && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl)))) + warning_with_decl (decl, + "variable `%s' may be clobbered by `longjmp' or `vfork'"); + } + for (sub = BLOCK_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub)) + uninitialized_vars_warning (sub); +} + +/* Do the appropriate part of uninitialized_vars_warning + but for arguments instead of local variables. */ + +void +setjmp_args_warning (block) + tree block; +{ + register tree decl; + for (decl = DECL_ARGUMENTS (current_function_decl); + decl; decl = TREE_CHAIN (decl)) + if (DECL_RTL (decl) != 0 + && GET_CODE (DECL_RTL (decl)) == REG + && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl)))) + warning_with_decl (decl, "argument `%s' may be clobbered by `longjmp' or `vfork'"); +} + +/* If this function call setjmp, put all vars into the stack + unless they were declared `register'. */ + +void +setjmp_protect (block) + tree block; +{ + register tree decl, sub; + for (decl = BLOCK_VARS (block); decl; decl = TREE_CHAIN (decl)) + if ((TREE_CODE (decl) == VAR_DECL + || TREE_CODE (decl) == PARM_DECL) + && DECL_RTL (decl) != 0 + && GET_CODE (DECL_RTL (decl)) == REG + /* If this variable came from an inline function, it must be + that it's life doesn't overlap the setjmp. If there was a + setjmp in the function, it would already be in memory. We + must exclude such variable because their DECL_RTL might be + set to strange things such as virtual_stack_vars_rtx. */ + && ! DECL_FROM_INLINE (decl) + && ( +#ifdef NON_SAVING_SETJMP + /* If longjmp doesn't restore the registers, + don't put anything in them. */ + NON_SAVING_SETJMP + || +#endif + ! DECL_REGISTER (decl))) + put_var_into_stack (decl); + for (sub = BLOCK_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub)) + setjmp_protect (sub); +} + +/* Like the previous function, but for args instead of local variables. */ + +void +setjmp_protect_args () +{ + register tree decl, sub; + for (decl = DECL_ARGUMENTS (current_function_decl); + decl; decl = TREE_CHAIN (decl)) + if ((TREE_CODE (decl) == VAR_DECL + || TREE_CODE (decl) == PARM_DECL) + && DECL_RTL (decl) != 0 + && GET_CODE (DECL_RTL (decl)) == REG + && ( + /* If longjmp doesn't restore the registers, + don't put anything in them. */ +#ifdef NON_SAVING_SETJMP + NON_SAVING_SETJMP + || +#endif + ! DECL_REGISTER (decl))) + put_var_into_stack (decl); +} + +/* Return the context-pointer register corresponding to DECL, + or 0 if it does not need one. */ + +rtx +lookup_static_chain (decl) + tree decl; +{ + tree context = decl_function_context (decl); + tree link; + + if (context == 0) + return 0; + + /* We treat inline_function_decl as an alias for the current function + because that is the inline function whose vars, types, etc. + are being merged into the current function. + See expand_inline_function. */ + if (context == current_function_decl || context == inline_function_decl) + return virtual_stack_vars_rtx; + + for (link = context_display; link; link = TREE_CHAIN (link)) + if (TREE_PURPOSE (link) == context) + return RTL_EXPR_RTL (TREE_VALUE (link)); + + abort (); +} + +/* Convert a stack slot address ADDR for variable VAR + (from a containing function) + into an address valid in this function (using a static chain). */ + +rtx +fix_lexical_addr (addr, var) + rtx addr; + tree var; +{ + rtx basereg; + int displacement; + tree context = decl_function_context (var); + struct function *fp; + rtx base = 0; + + /* If this is the present function, we need not do anything. */ + if (context == current_function_decl || context == inline_function_decl) + return addr; + + for (fp = outer_function_chain; fp; fp = fp->next) + if (fp->decl == context) + break; + + if (fp == 0) + abort (); + + /* Decode given address as base reg plus displacement. */ + if (GET_CODE (addr) == REG) + basereg = addr, displacement = 0; + else if (GET_CODE (addr) == PLUS && GET_CODE (XEXP (addr, 1)) == CONST_INT) + basereg = XEXP (addr, 0), displacement = INTVAL (XEXP (addr, 1)); + else + abort (); + + /* We accept vars reached via the containing function's + incoming arg pointer and via its stack variables pointer. */ + if (basereg == fp->internal_arg_pointer) + { + /* If reached via arg pointer, get the arg pointer value + out of that function's stack frame. + + There are two cases: If a separate ap is needed, allocate a + slot in the outer function for it and dereference it that way. + This is correct even if the real ap is actually a pseudo. + Otherwise, just adjust the offset from the frame pointer to + compensate. */ + +#ifdef NEED_SEPARATE_AP + rtx addr; + + if (fp->arg_pointer_save_area == 0) + fp->arg_pointer_save_area + = assign_outer_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0, fp); + + addr = fix_lexical_addr (XEXP (fp->arg_pointer_save_area, 0), var); + addr = memory_address (Pmode, addr); + + base = copy_to_reg (gen_rtx (MEM, Pmode, addr)); +#else + displacement += (FIRST_PARM_OFFSET (context) - STARTING_FRAME_OFFSET); + base = lookup_static_chain (var); +#endif + } + + else if (basereg == virtual_stack_vars_rtx) + { + /* This is the same code as lookup_static_chain, duplicated here to + avoid an extra call to decl_function_context. */ + tree link; + + for (link = context_display; link; link = TREE_CHAIN (link)) + if (TREE_PURPOSE (link) == context) + { + base = RTL_EXPR_RTL (TREE_VALUE (link)); + break; + } + } + + if (base == 0) + abort (); + + /* Use same offset, relative to appropriate static chain or argument + pointer. */ + return plus_constant (base, displacement); +} + +/* Return the address of the trampoline for entering nested fn FUNCTION. + If necessary, allocate a trampoline (in the stack frame) + and emit rtl to initialize its contents (at entry to this function). */ + +rtx +trampoline_address (function) + tree function; +{ + tree link; + tree rtlexp; + rtx tramp; + struct function *fp; + tree fn_context; + + /* Find an existing trampoline and return it. */ + for (link = trampoline_list; link; link = TREE_CHAIN (link)) + if (TREE_PURPOSE (link) == function) + return XEXP (RTL_EXPR_RTL (TREE_VALUE (link)), 0); + for (fp = outer_function_chain; fp; fp = fp->next) + for (link = fp->trampoline_list; link; link = TREE_CHAIN (link)) + if (TREE_PURPOSE (link) == function) + { + tramp = fix_lexical_addr (XEXP (RTL_EXPR_RTL (TREE_VALUE (link)), 0), + function); + return round_trampoline_addr (tramp); + } + + /* None exists; we must make one. */ + + /* Find the `struct function' for the function containing FUNCTION. */ + fp = 0; + fn_context = decl_function_context (function); + if (fn_context != current_function_decl) + for (fp = outer_function_chain; fp; fp = fp->next) + if (fp->decl == fn_context) + break; + + /* Allocate run-time space for this trampoline + (usually in the defining function's stack frame). */ +#ifdef ALLOCATE_TRAMPOLINE + tramp = ALLOCATE_TRAMPOLINE (fp); +#else + /* If rounding needed, allocate extra space + to ensure we have TRAMPOLINE_SIZE bytes left after rounding up. */ +#ifdef TRAMPOLINE_ALIGNMENT +#define TRAMPOLINE_REAL_SIZE (TRAMPOLINE_SIZE + TRAMPOLINE_ALIGNMENT - 1) +#else +#define TRAMPOLINE_REAL_SIZE (TRAMPOLINE_SIZE) +#endif + if (fp != 0) + tramp = assign_outer_stack_local (BLKmode, TRAMPOLINE_REAL_SIZE, 0, fp); + else + tramp = assign_stack_local (BLKmode, TRAMPOLINE_REAL_SIZE, 0); +#endif + + /* Record the trampoline for reuse and note it for later initialization + by expand_function_end. */ + if (fp != 0) + { + push_obstacks (fp->current_obstack, fp->function_maybepermanent_obstack); + rtlexp = make_node (RTL_EXPR); + RTL_EXPR_RTL (rtlexp) = tramp; + fp->trampoline_list = tree_cons (function, rtlexp, fp->trampoline_list); + pop_obstacks (); + } + else + { + /* Make the RTL_EXPR node temporary, not momentary, so that the + trampoline_list doesn't become garbage. */ + int momentary = suspend_momentary (); + rtlexp = make_node (RTL_EXPR); + resume_momentary (momentary); + + RTL_EXPR_RTL (rtlexp) = tramp; + trampoline_list = tree_cons (function, rtlexp, trampoline_list); + } + + tramp = fix_lexical_addr (XEXP (tramp, 0), function); + return round_trampoline_addr (tramp); +} + +/* Given a trampoline address, + round it to multiple of TRAMPOLINE_ALIGNMENT. */ + +static rtx +round_trampoline_addr (tramp) + rtx tramp; +{ +#ifdef TRAMPOLINE_ALIGNMENT + /* Round address up to desired boundary. */ + rtx temp = gen_reg_rtx (Pmode); + temp = expand_binop (Pmode, add_optab, tramp, + GEN_INT (TRAMPOLINE_ALIGNMENT - 1), + temp, 0, OPTAB_LIB_WIDEN); + tramp = expand_binop (Pmode, and_optab, temp, + GEN_INT (- TRAMPOLINE_ALIGNMENT), + temp, 0, OPTAB_LIB_WIDEN); +#endif + return tramp; +} + +/* The functions identify_blocks and reorder_blocks provide a way to + reorder the tree of BLOCK nodes, for optimizers that reshuffle or + duplicate portions of the RTL code. Call identify_blocks before + changing the RTL, and call reorder_blocks after. */ + +static int all_blocks (); +static tree blocks_nreverse (); + +/* Put all this function's BLOCK nodes into a vector, and return it. + Also store in each NOTE for the beginning or end of a block + the index of that block in the vector. + The arguments are TOP_BLOCK, the top-level block of the function, + and INSNS, the insn chain of the function. */ + +tree * +identify_blocks (top_block, insns) + tree top_block; + rtx insns; +{ + int n_blocks; + tree *block_vector; + int *block_stack; + int depth = 0; + int next_block_number = 0; + int current_block_number = 0; + rtx insn; + + if (top_block == 0) + return 0; + + n_blocks = all_blocks (top_block, 0); + block_vector = (tree *) xmalloc (n_blocks * sizeof (tree)); + block_stack = (int *) alloca (n_blocks * sizeof (int)); + + all_blocks (top_block, block_vector); + + for (insn = insns; insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == NOTE) + { + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG) + { + block_stack[depth++] = current_block_number; + current_block_number = next_block_number; + NOTE_BLOCK_NUMBER (insn) = next_block_number++; + } + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END) + { + current_block_number = block_stack[--depth]; + NOTE_BLOCK_NUMBER (insn) = current_block_number; + } + } + + return block_vector; +} + +/* Given BLOCK_VECTOR which was returned by identify_blocks, + and a revised instruction chain, rebuild the tree structure + of BLOCK nodes to correspond to the new order of RTL. + The new block tree is inserted below TOP_BLOCK. + Returns the current top-level block. */ + +tree +reorder_blocks (block_vector, top_block, insns) + tree *block_vector; + tree top_block; + rtx insns; +{ + tree current_block = top_block; + rtx insn; + + if (block_vector == 0) + return top_block; + + /* Prune the old tree away, so that it doesn't get in the way. */ + BLOCK_SUBBLOCKS (current_block) = 0; + + for (insn = insns; insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == NOTE) + { + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG) + { + tree block = block_vector[NOTE_BLOCK_NUMBER (insn)]; + /* If we have seen this block before, copy it. */ + if (TREE_ASM_WRITTEN (block)) + block = copy_node (block); + BLOCK_SUBBLOCKS (block) = 0; + TREE_ASM_WRITTEN (block) = 1; + BLOCK_SUPERCONTEXT (block) = current_block; + BLOCK_CHAIN (block) = BLOCK_SUBBLOCKS (current_block); + BLOCK_SUBBLOCKS (current_block) = block; + current_block = block; + NOTE_SOURCE_FILE (insn) = 0; + } + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END) + { + BLOCK_SUBBLOCKS (current_block) + = blocks_nreverse (BLOCK_SUBBLOCKS (current_block)); + current_block = BLOCK_SUPERCONTEXT (current_block); + NOTE_SOURCE_FILE (insn) = 0; + } + } + + return current_block; +} + +/* Reverse the order of elements in the chain T of blocks, + and return the new head of the chain (old last element). */ + +static tree +blocks_nreverse (t) + tree t; +{ + register tree prev = 0, decl, next; + for (decl = t; decl; decl = next) + { + next = BLOCK_CHAIN (decl); + BLOCK_CHAIN (decl) = prev; + prev = decl; + } + return prev; +} + +/* Count the subblocks of BLOCK, and list them all into the vector VECTOR. + Also clear TREE_ASM_WRITTEN in all blocks. */ + +static int +all_blocks (block, vector) + tree block; + tree *vector; +{ + int n_blocks = 1; + tree subblocks; + + TREE_ASM_WRITTEN (block) = 0; + /* Record this block. */ + if (vector) + vector[0] = block; + + /* Record the subblocks, and their subblocks. */ + for (subblocks = BLOCK_SUBBLOCKS (block); + subblocks; subblocks = BLOCK_CHAIN (subblocks)) + n_blocks += all_blocks (subblocks, vector ? vector + n_blocks : 0); + + return n_blocks; +} + +/* Generate RTL for the start of the function SUBR (a FUNCTION_DECL tree node) + and initialize static variables for generating RTL for the statements + of the function. */ + +void +init_function_start (subr, filename, line) + tree subr; + char *filename; + int line; +{ + char *junk; + + init_stmt_for_function (); + + cse_not_expected = ! optimize; + + /* Caller save not needed yet. */ + caller_save_needed = 0; + + /* No stack slots have been made yet. */ + stack_slot_list = 0; + + /* There is no stack slot for handling nonlocal gotos. */ + nonlocal_goto_handler_slot = 0; + nonlocal_goto_stack_level = 0; + + /* No labels have been declared for nonlocal use. */ + nonlocal_labels = 0; + + /* No function calls so far in this function. */ + function_call_count = 0; + + /* No parm regs have been allocated. + (This is important for output_inline_function.) */ + max_parm_reg = LAST_VIRTUAL_REGISTER + 1; + + /* Initialize the RTL mechanism. */ + init_emit (); + + /* Initialize the queue of pending postincrement and postdecrements, + and some other info in expr.c. */ + init_expr (); + + /* We haven't done register allocation yet. */ + reg_renumber = 0; + + init_const_rtx_hash_table (); + + current_function_name = (*decl_printable_name) (subr, &junk); + + /* Nonzero if this is a nested function that uses a static chain. */ + + current_function_needs_context + = (decl_function_context (current_function_decl) != 0); + + /* Set if a call to setjmp is seen. */ + current_function_calls_setjmp = 0; + + /* Set if a call to longjmp is seen. */ + current_function_calls_longjmp = 0; + + current_function_calls_alloca = 0; + current_function_has_nonlocal_label = 0; + current_function_contains_functions = 0; + + current_function_returns_pcc_struct = 0; + current_function_returns_struct = 0; + current_function_epilogue_delay_list = 0; + current_function_uses_const_pool = 0; + current_function_uses_pic_offset_table = 0; + + /* We have not yet needed to make a label to jump to for tail-recursion. */ + tail_recursion_label = 0; + + /* We haven't had a need to make a save area for ap yet. */ + + arg_pointer_save_area = 0; + + /* No stack slots allocated yet. */ + frame_offset = 0; + + /* No SAVE_EXPRs in this function yet. */ + save_expr_regs = 0; + + /* No RTL_EXPRs in this function yet. */ + rtl_expr_chain = 0; + + /* We have not allocated any temporaries yet. */ + temp_slots = 0; + temp_slot_level = 0; + + /* Within function body, compute a type's size as soon it is laid out. */ + immediate_size_expand++; + + init_pending_stack_adjust (); + inhibit_defer_pop = 0; + + current_function_outgoing_args_size = 0; + + /* Initialize the insn lengths. */ + init_insn_lengths (); + + /* Prevent ever trying to delete the first instruction of a function. + Also tell final how to output a linenum before the function prologue. */ + emit_line_note (filename, line); + + /* Make sure first insn is a note even if we don't want linenums. + This makes sure the first insn will never be deleted. + Also, final expects a note to appear there. */ + emit_note (NULL_PTR, NOTE_INSN_DELETED); + + /* Set flags used by final.c. */ + if (aggregate_value_p (DECL_RESULT (subr))) + { +#ifdef PCC_STATIC_STRUCT_RETURN + current_function_returns_pcc_struct = 1; +#endif + current_function_returns_struct = 1; + } + + /* Warn if this value is an aggregate type, + regardless of which calling convention we are using for it. */ + if (warn_aggregate_return + && (TREE_CODE (TREE_TYPE (DECL_RESULT (subr))) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (DECL_RESULT (subr))) == UNION_TYPE + || TREE_CODE (TREE_TYPE (DECL_RESULT (subr))) == QUAL_UNION_TYPE + || TREE_CODE (TREE_TYPE (DECL_RESULT (subr))) == ARRAY_TYPE)) + warning ("function returns an aggregate"); + + current_function_returns_pointer + = (TREE_CODE (TREE_TYPE (DECL_RESULT (subr))) == POINTER_TYPE); + + /* Indicate that we need to distinguish between the return value of the + present function and the return value of a function being called. */ + rtx_equal_function_value_matters = 1; + + /* Indicate that we have not instantiated virtual registers yet. */ + virtuals_instantiated = 0; + + /* Indicate we have no need of a frame pointer yet. */ + frame_pointer_needed = 0; + + /* By default assume not varargs. */ + current_function_varargs = 0; +} + +/* Indicate that the current function uses extra args + not explicitly mentioned in the argument list in any fashion. */ + +void +mark_varargs () +{ + current_function_varargs = 1; +} + +/* Expand a call to __main at the beginning of a possible main function. */ + +void +expand_main_function () +{ +#if !defined (INIT_SECTION_ASM_OP) || defined (INVOKE__main) + emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__main"), 0, + VOIDmode, 0); +#endif /* not INIT_SECTION_ASM_OP or INVOKE__main */ +} + +/* Start the RTL for a new function, and set variables used for + emitting RTL. + SUBR is the FUNCTION_DECL node. + PARMS_HAVE_CLEANUPS is nonzero if there are cleanups associated with + the function's parameters, which must be run at any return statement. */ + +void +expand_function_start (subr, parms_have_cleanups) + tree subr; + int parms_have_cleanups; +{ + register int i; + tree tem; + rtx last_ptr; + + /* Make sure volatile mem refs aren't considered + valid operands of arithmetic insns. */ + init_recog_no_volatile (); + + /* If function gets a static chain arg, store it in the stack frame. + Do this first, so it gets the first stack slot offset. */ + if (current_function_needs_context) + { + last_ptr = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0); + emit_move_insn (last_ptr, static_chain_incoming_rtx); + } + + /* If the parameters of this function need cleaning up, get a label + for the beginning of the code which executes those cleanups. This must + be done before doing anything with return_label. */ + if (parms_have_cleanups) + cleanup_label = gen_label_rtx (); + else + cleanup_label = 0; + + /* Make the label for return statements to jump to, if this machine + does not have a one-instruction return and uses an epilogue, + or if it returns a structure, or if it has parm cleanups. */ +#ifdef HAVE_return + if (cleanup_label == 0 && HAVE_return + && ! current_function_returns_pcc_struct + && ! (current_function_returns_struct && ! optimize)) + return_label = 0; + else + return_label = gen_label_rtx (); +#else + return_label = gen_label_rtx (); +#endif + + /* Initialize rtx used to return the value. */ + /* Do this before assign_parms so that we copy the struct value address + before any library calls that assign parms might generate. */ + + /* Decide whether to return the value in memory or in a register. */ + if (aggregate_value_p (DECL_RESULT (subr))) + { + /* Returning something that won't go in a register. */ + register rtx value_address; + +#ifdef PCC_STATIC_STRUCT_RETURN + if (current_function_returns_pcc_struct) + { + int size = int_size_in_bytes (TREE_TYPE (DECL_RESULT (subr))); + value_address = assemble_static_space (size); + } + else +#endif + { + /* Expect to be passed the address of a place to store the value. + If it is passed as an argument, assign_parms will take care of + it. */ + if (struct_value_incoming_rtx) + { + value_address = gen_reg_rtx (Pmode); + emit_move_insn (value_address, struct_value_incoming_rtx); + } + } + if (value_address) + DECL_RTL (DECL_RESULT (subr)) + = gen_rtx (MEM, DECL_MODE (DECL_RESULT (subr)), + value_address); + } + else if (DECL_MODE (DECL_RESULT (subr)) == VOIDmode) + /* If return mode is void, this decl rtl should not be used. */ + DECL_RTL (DECL_RESULT (subr)) = 0; + else if (parms_have_cleanups) + { + /* If function will end with cleanup code for parms, + compute the return values into a pseudo reg, + which we will copy into the true return register + after the cleanups are done. */ + + enum machine_mode mode = DECL_MODE (DECL_RESULT (subr)); +#ifdef PROMOTE_FUNCTION_RETURN + tree type = TREE_TYPE (DECL_RESULT (subr)); + int unsignedp = TREE_UNSIGNED (type); + + if (TREE_CODE (type) == INTEGER_TYPE || TREE_CODE (type) == ENUMERAL_TYPE + || TREE_CODE (type) == BOOLEAN_TYPE || TREE_CODE (type) == CHAR_TYPE + || TREE_CODE (type) == REAL_TYPE || TREE_CODE (type) == POINTER_TYPE + || TREE_CODE (type) == OFFSET_TYPE) + { + PROMOTE_MODE (mode, unsignedp, type); + } +#endif + + DECL_RTL (DECL_RESULT (subr)) = gen_reg_rtx (mode); + } + else + /* Scalar, returned in a register. */ + { +#ifdef FUNCTION_OUTGOING_VALUE + DECL_RTL (DECL_RESULT (subr)) + = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (subr)), subr); +#else + DECL_RTL (DECL_RESULT (subr)) + = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (subr)), subr); +#endif + + /* Mark this reg as the function's return value. */ + if (GET_CODE (DECL_RTL (DECL_RESULT (subr))) == REG) + { + REG_FUNCTION_VALUE_P (DECL_RTL (DECL_RESULT (subr))) = 1; + /* Needed because we may need to move this to memory + in case it's a named return value whose address is taken. */ + DECL_REGISTER (DECL_RESULT (subr)) = 1; + } + } + + /* Initialize rtx for parameters and local variables. + In some cases this requires emitting insns. */ + + assign_parms (subr, 0); + + /* The following was moved from init_function_start. + The move is supposed to make sdb output more accurate. */ + /* Indicate the beginning of the function body, + as opposed to parm setup. */ + emit_note (NULL_PTR, NOTE_INSN_FUNCTION_BEG); + + /* If doing stupid allocation, mark parms as born here. */ + + if (GET_CODE (get_last_insn ()) != NOTE) + emit_note (NULL_PTR, NOTE_INSN_DELETED); + parm_birth_insn = get_last_insn (); + + if (obey_regdecls) + { + for (i = LAST_VIRTUAL_REGISTER + 1; i < max_parm_reg; i++) + use_variable (regno_reg_rtx[i]); + + if (current_function_internal_arg_pointer != virtual_incoming_args_rtx) + use_variable (current_function_internal_arg_pointer); + } + + /* Fetch static chain values for containing functions. */ + tem = decl_function_context (current_function_decl); + /* If not doing stupid register allocation, then start off with the static + chain pointer in a pseudo register. Otherwise, we use the stack + address that was generated above. */ + if (tem && ! obey_regdecls) + last_ptr = copy_to_reg (static_chain_incoming_rtx); + context_display = 0; + while (tem) + { + tree rtlexp = make_node (RTL_EXPR); + + RTL_EXPR_RTL (rtlexp) = last_ptr; + context_display = tree_cons (tem, rtlexp, context_display); + tem = decl_function_context (tem); + if (tem == 0) + break; + /* Chain thru stack frames, assuming pointer to next lexical frame + is found at the place we always store it. */ +#ifdef FRAME_GROWS_DOWNWARD + last_ptr = plus_constant (last_ptr, - GET_MODE_SIZE (Pmode)); +#endif + last_ptr = copy_to_reg (gen_rtx (MEM, Pmode, + memory_address (Pmode, last_ptr))); + } + + /* After the display initializations is where the tail-recursion label + should go, if we end up needing one. Ensure we have a NOTE here + since some things (like trampolines) get placed before this. */ + tail_recursion_reentry = emit_note (NULL_PTR, NOTE_INSN_DELETED); + + /* Evaluate now the sizes of any types declared among the arguments. */ + for (tem = nreverse (get_pending_sizes ()); tem; tem = TREE_CHAIN (tem)) + expand_expr (TREE_VALUE (tem), const0_rtx, VOIDmode, 0); + + /* Make sure there is a line number after the function entry setup code. */ + force_next_line_note (); +} + +/* Generate RTL for the end of the current function. + FILENAME and LINE are the current position in the source file. */ + +/* It is up to language-specific callers to do cleanups for parameters. */ + +void +expand_function_end (filename, line) + char *filename; + int line; +{ + register int i; + tree link; + + static rtx initial_trampoline; + +#ifdef NON_SAVING_SETJMP + /* Don't put any variables in registers if we call setjmp + on a machine that fails to restore the registers. */ + if (NON_SAVING_SETJMP && current_function_calls_setjmp) + { + setjmp_protect (DECL_INITIAL (current_function_decl)); + setjmp_protect_args (); + } +#endif + + /* Save the argument pointer if a save area was made for it. */ + if (arg_pointer_save_area) + { + rtx x = gen_move_insn (arg_pointer_save_area, virtual_incoming_args_rtx); + emit_insn_before (x, tail_recursion_reentry); + } + + /* Initialize any trampolines required by this function. */ + for (link = trampoline_list; link; link = TREE_CHAIN (link)) + { + tree function = TREE_PURPOSE (link); + rtx context = lookup_static_chain (function); + rtx tramp = RTL_EXPR_RTL (TREE_VALUE (link)); + rtx seq; + + /* First make sure this compilation has a template for + initializing trampolines. */ + if (initial_trampoline == 0) + { + end_temporary_allocation (); + initial_trampoline + = gen_rtx (MEM, BLKmode, assemble_trampoline_template ()); + resume_temporary_allocation (); + } + + /* Generate insns to initialize the trampoline. */ + start_sequence (); + tramp = change_address (initial_trampoline, BLKmode, + round_trampoline_addr (XEXP (tramp, 0))); + emit_block_move (tramp, initial_trampoline, GEN_INT (TRAMPOLINE_SIZE), + FUNCTION_BOUNDARY / BITS_PER_UNIT); + INITIALIZE_TRAMPOLINE (XEXP (tramp, 0), + XEXP (DECL_RTL (function), 0), context); + seq = get_insns (); + end_sequence (); + + /* Put those insns at entry to the containing function (this one). */ + emit_insns_before (seq, tail_recursion_reentry); + } + /* Clear the trampoline_list for the next function. */ + trampoline_list = 0; + +#if 0 /* I think unused parms are legitimate enough. */ + /* Warn about unused parms. */ + if (warn_unused) + { + rtx decl; + + for (decl = DECL_ARGUMENTS (current_function_decl); + decl; decl = TREE_CHAIN (decl)) + if (! TREE_USED (decl) && TREE_CODE (decl) == VAR_DECL) + warning_with_decl (decl, "unused parameter `%s'"); + } +#endif + + /* Delete handlers for nonlocal gotos if nothing uses them. */ + if (nonlocal_goto_handler_slot != 0 && !current_function_has_nonlocal_label) + delete_handlers (); + + /* End any sequences that failed to be closed due to syntax errors. */ + while (in_sequence_p ()) + end_sequence (); + + /* Outside function body, can't compute type's actual size + until next function's body starts. */ + immediate_size_expand--; + + /* If doing stupid register allocation, + mark register parms as dying here. */ + + if (obey_regdecls) + { + rtx tem; + for (i = LAST_VIRTUAL_REGISTER + 1; i < max_parm_reg; i++) + use_variable (regno_reg_rtx[i]); + + /* Likewise for the regs of all the SAVE_EXPRs in the function. */ + + for (tem = save_expr_regs; tem; tem = XEXP (tem, 1)) + { + use_variable (XEXP (tem, 0)); + use_variable_after (XEXP (tem, 0), parm_birth_insn); + } + + if (current_function_internal_arg_pointer != virtual_incoming_args_rtx) + use_variable (current_function_internal_arg_pointer); + } + + clear_pending_stack_adjust (); + do_pending_stack_adjust (); + + /* Mark the end of the function body. + If control reaches this insn, the function can drop through + without returning a value. */ + emit_note (NULL_PTR, NOTE_INSN_FUNCTION_END); + + /* Output a linenumber for the end of the function. + SDB depends on this. */ + emit_line_note_force (filename, line); + + /* Output the label for the actual return from the function, + if one is expected. This happens either because a function epilogue + is used instead of a return instruction, or because a return was done + with a goto in order to run local cleanups, or because of pcc-style + structure returning. */ + + if (return_label) + emit_label (return_label); + + /* If we had calls to alloca, and this machine needs + an accurate stack pointer to exit the function, + insert some code to save and restore the stack pointer. */ +#ifdef EXIT_IGNORE_STACK + if (! EXIT_IGNORE_STACK) +#endif + if (current_function_calls_alloca) + { + rtx tem = 0; + + emit_stack_save (SAVE_FUNCTION, &tem, parm_birth_insn); + emit_stack_restore (SAVE_FUNCTION, tem, NULL_RTX); + } + + /* If scalar return value was computed in a pseudo-reg, + copy that to the hard return register. */ + if (DECL_RTL (DECL_RESULT (current_function_decl)) != 0 + && GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) == REG + && (REGNO (DECL_RTL (DECL_RESULT (current_function_decl))) + >= FIRST_PSEUDO_REGISTER)) + { + rtx real_decl_result; + +#ifdef FUNCTION_OUTGOING_VALUE + real_decl_result + = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (current_function_decl)), + current_function_decl); +#else + real_decl_result + = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (current_function_decl)), + current_function_decl); +#endif + REG_FUNCTION_VALUE_P (real_decl_result) = 1; + emit_move_insn (real_decl_result, + DECL_RTL (DECL_RESULT (current_function_decl))); + emit_insn (gen_rtx (USE, VOIDmode, real_decl_result)); + } + + /* If returning a structure, arrange to return the address of the value + in a place where debuggers expect to find it. + + If returning a structure PCC style, + the caller also depends on this value. + And current_function_returns_pcc_struct is not necessarily set. */ + if (current_function_returns_struct + || current_function_returns_pcc_struct) + { + rtx value_address = XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0); + tree type = TREE_TYPE (DECL_RESULT (current_function_decl)); +#ifdef FUNCTION_OUTGOING_VALUE + rtx outgoing + = FUNCTION_OUTGOING_VALUE (build_pointer_type (type), + current_function_decl); +#else + rtx outgoing + = FUNCTION_VALUE (build_pointer_type (type), + current_function_decl); +#endif + + /* Mark this as a function return value so integrate will delete the + assignment and USE below when inlining this function. */ + REG_FUNCTION_VALUE_P (outgoing) = 1; + + emit_move_insn (outgoing, value_address); + use_variable (outgoing); + } + + /* Output a return insn if we are using one. + Otherwise, let the rtl chain end here, to drop through + into the epilogue. */ + +#ifdef HAVE_return + if (HAVE_return) + { + emit_jump_insn (gen_return ()); + emit_barrier (); + } +#endif + + /* Fix up any gotos that jumped out to the outermost + binding level of the function. + Must follow emitting RETURN_LABEL. */ + + /* If you have any cleanups to do at this point, + and they need to create temporary variables, + then you will lose. */ + fixup_gotos (NULL_PTR, NULL_RTX, NULL_TREE, get_insns (), 0); +} + +/* These arrays record the INSN_UIDs of the prologue and epilogue insns. */ + +static int *prologue; +static int *epilogue; + +/* Create an array that records the INSN_UIDs of INSNS (either a sequence + or a single insn). */ + +static int * +record_insns (insns) + rtx insns; +{ + int *vec; + + if (GET_CODE (insns) == SEQUENCE) + { + int len = XVECLEN (insns, 0); + vec = (int *) oballoc ((len + 1) * sizeof (int)); + vec[len] = 0; + while (--len >= 0) + vec[len] = INSN_UID (XVECEXP (insns, 0, len)); + } + else + { + vec = (int *) oballoc (2 * sizeof (int)); + vec[0] = INSN_UID (insns); + vec[1] = 0; + } + return vec; +} + +/* Determine how many INSN_UIDs in VEC are part of INSN. */ + +static int +contains (insn, vec) + rtx insn; + int *vec; +{ + register int i, j; + + if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SEQUENCE) + { + int count = 0; + for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) + for (j = 0; vec[j]; j++) + if (INSN_UID (XVECEXP (PATTERN (insn), 0, i)) == vec[j]) + count++; + return count; + } + else + { + for (j = 0; vec[j]; j++) + if (INSN_UID (insn) == vec[j]) + return 1; + } + return 0; +} + +/* Generate the prologe and epilogue RTL if the machine supports it. Thread + this into place with notes indicating where the prologue ends and where + the epilogue begins. Update the basic block information when possible. */ + +void +thread_prologue_and_epilogue_insns (f) + rtx f; +{ +#ifdef HAVE_prologue + if (HAVE_prologue) + { + rtx head, seq, insn; + + /* The first insn (a NOTE_INSN_DELETED) is followed by zero or more + prologue insns and a NOTE_INSN_PROLOGUE_END. */ + emit_note_after (NOTE_INSN_PROLOGUE_END, f); + seq = gen_prologue (); + head = emit_insn_after (seq, f); + + /* Include the new prologue insns in the first block. Ignore them + if they form a basic block unto themselves. */ + if (basic_block_head && n_basic_blocks + && GET_CODE (basic_block_head[0]) != CODE_LABEL) + basic_block_head[0] = NEXT_INSN (f); + + /* Retain a map of the prologue insns. */ + prologue = record_insns (GET_CODE (seq) == SEQUENCE ? seq : head); + } + else +#endif + prologue = 0; + +#ifdef HAVE_epilogue + if (HAVE_epilogue) + { + rtx insn = get_last_insn (); + rtx prev = prev_nonnote_insn (insn); + + /* If we end with a BARRIER, we don't need an epilogue. */ + if (! (prev && GET_CODE (prev) == BARRIER)) + { + rtx tail, seq; + + /* The last basic block ends with a NOTE_INSN_EPILOGUE_BEG, + the epilogue insns (this must include the jump insn that + returns), USE insns ad the end of a function, and a BARRIER. */ + + emit_barrier_after (insn); + + /* Place the epilogue before the USE insns at the end of a + function. */ + while (prev + && GET_CODE (prev) == INSN + && GET_CODE (PATTERN (prev)) == USE) + { + insn = PREV_INSN (prev); + prev = prev_nonnote_insn (prev); + } + + seq = gen_epilogue (); + tail = emit_jump_insn_after (seq, insn); + emit_note_after (NOTE_INSN_EPILOGUE_BEG, insn); + + /* Include the new epilogue insns in the last block. Ignore + them if they form a basic block unto themselves. */ + if (basic_block_end && n_basic_blocks + && GET_CODE (basic_block_end[n_basic_blocks - 1]) != JUMP_INSN) + basic_block_end[n_basic_blocks - 1] = tail; + + /* Retain a map of the epilogue insns. */ + epilogue = record_insns (GET_CODE (seq) == SEQUENCE ? seq : tail); + return; + } + } +#endif + epilogue = 0; +} + +/* Reposition the prologue-end and epilogue-begin notes after instruction + scheduling and delayed branch scheduling. */ + +void +reposition_prologue_and_epilogue_notes (f) + rtx f; +{ +#if defined (HAVE_prologue) || defined (HAVE_epilogue) + /* Reposition the prologue and epilogue notes. */ + if (n_basic_blocks) + { + rtx next, prev; + int len; + + if (prologue) + { + register rtx insn, note = 0; + + /* Scan from the beginning until we reach the last prologue insn. + We apparently can't depend on basic_block_{head,end} after + reorg has run. */ + for (len = 0; prologue[len]; len++) + ; + for (insn = f; len && insn; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == NOTE) + { + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_PROLOGUE_END) + note = insn; + } + else if ((len -= contains (insn, prologue)) == 0) + { + /* Find the prologue-end note if we haven't already, and + move it to just after the last prologue insn. */ + if (note == 0) + { + for (note = insn; note = NEXT_INSN (note);) + if (GET_CODE (note) == NOTE + && NOTE_LINE_NUMBER (note) == NOTE_INSN_PROLOGUE_END) + break; + } + next = NEXT_INSN (note); + prev = PREV_INSN (note); + if (prev) + NEXT_INSN (prev) = next; + if (next) + PREV_INSN (next) = prev; + add_insn_after (note, insn); + } + } + } + + if (epilogue) + { + register rtx insn, note = 0; + + /* Scan from the end until we reach the first epilogue insn. + We apparently can't depend on basic_block_{head,end} after + reorg has run. */ + for (len = 0; epilogue[len]; len++) + ; + for (insn = get_last_insn (); len && insn; insn = PREV_INSN (insn)) + { + if (GET_CODE (insn) == NOTE) + { + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EPILOGUE_BEG) + note = insn; + } + else if ((len -= contains (insn, epilogue)) == 0) + { + /* Find the epilogue-begin note if we haven't already, and + move it to just before the first epilogue insn. */ + if (note == 0) + { + for (note = insn; note = PREV_INSN (note);) + if (GET_CODE (note) == NOTE + && NOTE_LINE_NUMBER (note) == NOTE_INSN_EPILOGUE_BEG) + break; + } + next = NEXT_INSN (note); + prev = PREV_INSN (note); + if (prev) + NEXT_INSN (prev) = next; + if (next) + PREV_INSN (next) = prev; + add_insn_after (note, PREV_INSN (insn)); + } + } + } + } +#endif /* HAVE_prologue or HAVE_epilogue */ +} diff --git a/gnu/usr.bin/cc/common/function.h b/gnu/usr.bin/cc/lib/function.h similarity index 100% rename from gnu/usr.bin/cc/common/function.h rename to gnu/usr.bin/cc/lib/function.h diff --git a/gnu/usr.bin/cc/common/gbl-ctors.h b/gnu/usr.bin/cc/lib/gbl-ctors.h similarity index 100% rename from gnu/usr.bin/cc/common/gbl-ctors.h rename to gnu/usr.bin/cc/lib/gbl-ctors.h diff --git a/gnu/usr.bin/cc/common/getpwd.c b/gnu/usr.bin/cc/lib/getpwd.c similarity index 100% rename from gnu/usr.bin/cc/common/getpwd.c rename to gnu/usr.bin/cc/lib/getpwd.c diff --git a/gnu/usr.bin/cc/common/glimits.h b/gnu/usr.bin/cc/lib/glimits.h similarity index 100% rename from gnu/usr.bin/cc/common/glimits.h rename to gnu/usr.bin/cc/lib/glimits.h diff --git a/gnu/usr.bin/cc/common/global.c b/gnu/usr.bin/cc/lib/global.c similarity index 100% rename from gnu/usr.bin/cc/common/global.c rename to gnu/usr.bin/cc/lib/global.c diff --git a/gnu/usr.bin/cc/lib/gstddef.h b/gnu/usr.bin/cc/lib/gstddef.h new file mode 100644 index 0000000000..f1939e33ff --- /dev/null +++ b/gnu/usr.bin/cc/lib/gstddef.h @@ -0,0 +1,217 @@ +#ifndef _STDDEF_H +#ifndef _STDDEF_H_ +#ifndef _ANSI_STDDEF_H + +/* Any one of these symbols __need_* means that GNU libc + wants us just to define one data type. So don't define + the symbols that indicate this file's entire job has been done. */ +#if (!defined(__need_wchar_t) && !defined(__need_size_t) \ + && !defined(__need_ptrdiff_t) && !defined(__need_NULL)) +#define _STDDEF_H +#define _STDDEF_H_ +/* snaroff@next.com says the NeXT needs this. */ +#define _ANSI_STDDEF_H +#endif + +#ifndef __sys_stdtypes_h +/* This avoids lossage on SunOS but only if stdtypes.h comes first. + There's no way to win with the other order! Sun lossage. */ + +/* On 4.3bsd-net2, make sure ansi.h is included, so we have + one less case to deal with in the following. */ +#if defined (__BSD_NET2__) || defined (____386BSD____) +#include +#endif + +/* In 4.3bsd-net2, machine/ansi.h defines these symbols, which are + defined if the corresponding type is *not* defined. */ +#ifdef _ANSI_H_ +#ifndef _SIZE_T_ +#define _SIZE_T +#endif +#ifndef _PTRDIFF_T_ +#define _PTRDIFF_T +#endif +#ifndef _WCHAR_T_ +#define _WCHAR_T +#endif +/* Undef _FOO_T_ if we are supposed to define foo_t. */ +#if defined (__need_ptrdiff_t) || defined (_STDDEF_H_) +#undef _PTRDIFF_T_ +#endif +#if defined (__need_size_t) || defined (_STDDEF_H_) +#undef _SIZE_T_ +#endif +#if defined (__need_wchar_t) || defined (_STDDEF_H_) +#undef _WCHAR_T_ +#endif +#endif /* _ANSI_H_ */ + +/* Sequent's header files use _PTRDIFF_T_ in some conflicting way. + Just ignore it. */ +#if defined (__sequent__) && defined (_PTRDIFF_T_) +#undef _PTRDIFF_T_ +#endif + +/* In case nobody has defined these types, but we aren't running under + GCC 2.00, make sure that __PTRDIFF_TYPE__, __SIZE__TYPE__, and + __WCHAR_TYPE__ have reasonable values. This can happen if the + parts of GCC is compiled by an older compiler, that actually + include gstddef.h, such as collect2. */ + +/* Signed type of difference of two pointers. */ + +/* Define this type if we are doing the whole job, + or if we want this type in particular. */ +#if defined (_STDDEF_H) || defined (__need_ptrdiff_t) +#ifndef _PTRDIFF_T /* in case has defined it. */ +#ifndef _T_PTRDIFF_ +#ifndef _T_PTRDIFF +#ifndef __PTRDIFF_T +#ifndef _PTRDIFF_T_ +#ifndef ___int_ptrdiff_t_h +#ifndef _GCC_PTRDIFF_T +#define _PTRDIFF_T +#define _T_PTRDIFF_ +#define _T_PTRDIFF +#define __PTRDIFF_T +#define _PTRDIFF_T_ +#define ___int_ptrdiff_t_h +#define _GCC_PTRDIFF_T +#ifndef __PTRDIFF_TYPE__ +#define __PTRDIFF_TYPE__ long int +#endif +typedef __PTRDIFF_TYPE__ ptrdiff_t; +#endif /* _GCC_PTRDIFF_T */ +#endif /* ___int_ptrdiff_t_h */ +#endif /* _PTRDIFF_T_ */ +#endif /* __PTRDIFF_T */ +#endif /* _T_PTRDIFF */ +#endif /* _T_PTRDIFF_ */ +#endif /* _PTRDIFF_T */ + +/* If this symbol has done its job, get rid of it. */ +#undef __need_ptrdiff_t + +#endif /* _STDDEF_H or __need_ptrdiff_t. */ + +/* Unsigned type of `sizeof' something. */ + +/* Define this type if we are doing the whole job, + or if we want this type in particular. */ +#if defined (_STDDEF_H) || defined (__need_size_t) +#ifndef _SIZE_T /* in case has defined it. */ +#ifndef _SYS_SIZE_T_H +#ifndef _T_SIZE_ +#ifndef _T_SIZE +#ifndef __SIZE_T +#ifndef _SIZE_T_ +#ifndef ___int_size_t_h +#ifndef _GCC_SIZE_T +#ifndef _SIZET_ +#define _SIZE_T +#define _SYS_SIZE_T_H +#define _T_SIZE_ +#define _T_SIZE +#define __SIZE_T +#define _SIZE_T_ +#define ___int_size_t_h +#define _GCC_SIZE_T +#define _SIZET_ +#ifndef __SIZE_TYPE__ +#define __SIZE_TYPE__ long unsigned int +#endif +#if !(defined (__GNUG__) && defined (size_t)) +typedef __SIZE_TYPE__ size_t; +#endif /* !(defined (__GNUG__) && defined (size_t)) */ +#endif /* _SIZET_ */ +#endif /* _GCC_SIZE_T */ +#endif /* ___int_size_t_h */ +#endif /* _SIZE_T_ */ +#endif /* __SIZE_T */ +#endif /* _T_SIZE */ +#endif /* _T_SIZE_ */ +#endif /* _SYS_SIZE_T_H */ +#endif /* _SIZE_T */ +#undef __need_size_t +#endif /* _STDDEF_H or __need_size_t. */ + + +/* Wide character type. + Locale-writers should change this as necessary to + be big enough to hold unique values not between 0 and 127, + and not (wchar_t) -1, for each defined multibyte character. */ + +/* Define this type if we are doing the whole job, + or if we want this type in particular. */ +#if defined (_STDDEF_H) || defined (__need_wchar_t) +#ifndef _WCHAR_T +#ifndef _T_WCHAR_ +#ifndef _T_WCHAR +#ifndef __WCHAR_T +#ifndef _WCHAR_T_ +#ifndef ___int_wchar_t_h +#ifndef _GCC_WCHAR_T +#define _WCHAR_T +#define _T_WCHAR_ +#define _T_WCHAR +#define __WCHAR_T +#define _WCHAR_T_ +#define ___int_wchar_t_h +#define _GCC_WCHAR_T +#ifndef __WCHAR_TYPE__ +#define __WCHAR_TYPE__ int +#endif +#ifdef __GNUG__ +/* In C++, wchar_t is a distinct basic type, + and we can expect __wchar_t to be defined by cc1plus. */ +typedef __wchar_t wchar_t; +#else +/* In C, cpp tells us which type to make an alias for. */ +typedef __WCHAR_TYPE__ wchar_t; +#endif +#endif +#endif +#endif +#endif +#endif +#endif +#endif +#undef __need_wchar_t +#endif /* _STDDEF_H or __need_wchar_t. */ + +/* In 4.3bsd-net2, leave these undefined to indicate that size_t, etc. + are already defined. */ +#ifdef _ANSI_H_ +#ifdef _GCC_PTRDIFF_T +#undef _PTRDIFF_T_ +#endif +#ifdef _GCC_SIZE_T +#undef _SIZE_T_ +#endif +#ifdef _GCC_WCHAR_T +#undef _WCHAR_T_ +#endif +#endif /* _ANSI_H_ */ + +#endif /* __sys_stdtypes_h */ + +/* A null pointer constant. */ + +#if defined (_STDDEF_H) || defined (__need_NULL) +#undef NULL /* in case has defined it. */ +#define NULL ((void *)0) +#endif /* NULL not defined and or need NULL. */ +#undef __need_NULL + +#ifdef _STDDEF_H + +/* Offset of member MEMBER in a struct of type TYPE. */ + +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#endif /* _STDDEF_H was defined this time */ + +#endif /* _ANSI_STDDEF_H was not defined before */ +#endif /* _STDDEF_H_ was not defined before */ +#endif /* _STDDEF_H was not defined before */ diff --git a/gnu/usr.bin/cc/common/gvarargs.h b/gnu/usr.bin/cc/lib/gvarargs.h similarity index 100% rename from gnu/usr.bin/cc/common/gvarargs.h rename to gnu/usr.bin/cc/lib/gvarargs.h diff --git a/gnu/usr.bin/cc/common/hard-reg-set.h b/gnu/usr.bin/cc/lib/hard-reg-set.h similarity index 100% rename from gnu/usr.bin/cc/common/hard-reg-set.h rename to gnu/usr.bin/cc/lib/hard-reg-set.h diff --git a/gnu/usr.bin/cc/common/i386/bsd.h b/gnu/usr.bin/cc/lib/i386/bsd.h similarity index 100% rename from gnu/usr.bin/cc/common/i386/bsd.h rename to gnu/usr.bin/cc/lib/i386/bsd.h diff --git a/gnu/usr.bin/cc/common/i386/gas.h b/gnu/usr.bin/cc/lib/i386/gas.h similarity index 100% rename from gnu/usr.bin/cc/common/i386/gas.h rename to gnu/usr.bin/cc/lib/i386/gas.h diff --git a/gnu/usr.bin/cc/common/i386/gstabs.h b/gnu/usr.bin/cc/lib/i386/gstabs.h similarity index 100% rename from gnu/usr.bin/cc/common/i386/gstabs.h rename to gnu/usr.bin/cc/lib/i386/gstabs.h diff --git a/gnu/usr.bin/cc/common/i386/i386.h b/gnu/usr.bin/cc/lib/i386/i386.h similarity index 100% rename from gnu/usr.bin/cc/common/i386/i386.h rename to gnu/usr.bin/cc/lib/i386/i386.h diff --git a/gnu/usr.bin/cc/common/i386/perform.h b/gnu/usr.bin/cc/lib/i386/perform.h similarity index 100% rename from gnu/usr.bin/cc/common/i386/perform.h rename to gnu/usr.bin/cc/lib/i386/perform.h diff --git a/gnu/usr.bin/cc/common/i386/unix.h b/gnu/usr.bin/cc/lib/i386/unix.h similarity index 100% rename from gnu/usr.bin/cc/common/i386/unix.h rename to gnu/usr.bin/cc/lib/i386/unix.h diff --git a/gnu/usr.bin/cc/common/input.h b/gnu/usr.bin/cc/lib/input.h similarity index 100% rename from gnu/usr.bin/cc/common/input.h rename to gnu/usr.bin/cc/lib/input.h diff --git a/gnu/usr.bin/cc/common/insn-attr.h b/gnu/usr.bin/cc/lib/insn-attr.h similarity index 100% rename from gnu/usr.bin/cc/common/insn-attr.h rename to gnu/usr.bin/cc/lib/insn-attr.h diff --git a/gnu/usr.bin/cc/common/insn-attrtab.c b/gnu/usr.bin/cc/lib/insn-attrtab.c similarity index 100% rename from gnu/usr.bin/cc/common/insn-attrtab.c rename to gnu/usr.bin/cc/lib/insn-attrtab.c diff --git a/gnu/usr.bin/cc/common/insn-codes.h b/gnu/usr.bin/cc/lib/insn-codes.h similarity index 100% rename from gnu/usr.bin/cc/common/insn-codes.h rename to gnu/usr.bin/cc/lib/insn-codes.h diff --git a/gnu/usr.bin/cc/common/insn-config.h b/gnu/usr.bin/cc/lib/insn-config.h similarity index 100% rename from gnu/usr.bin/cc/common/insn-config.h rename to gnu/usr.bin/cc/lib/insn-config.h diff --git a/gnu/usr.bin/cc/lib/insn-emit.c b/gnu/usr.bin/cc/lib/insn-emit.c new file mode 100644 index 0000000000..8a8d9bd4f8 --- /dev/null +++ b/gnu/usr.bin/cc/lib/insn-emit.c @@ -0,0 +1,2708 @@ +/* Generated automatically by the program `genemit' +from the machine description file `md'. */ + +#include "config.h" +#include "rtl.h" +#include "expr.h" +#include "real.h" +#include "output.h" +#include "insn-config.h" + +#include "insn-flags.h" + +#include "insn-codes.h" + +extern char *insn_operand_constraint[][MAX_RECOG_OPERANDS]; + +extern rtx recog_operand[]; +#define operands emit_operand + +#define FAIL goto _fail + +#define DONE goto _done + +rtx +gen_tstsi_1 (operand0) + rtx operand0; +{ + return gen_rtx (SET, VOIDmode, cc0_rtx, operand0); +} + +rtx +gen_tstsi (operand0) + rtx operand0; +{ + rtx operands[1]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + +{ + i386_compare_gen = gen_tstsi_1; + i386_compare_op0 = operands[0]; + DONE; +} + operand0 = operands[0]; + emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, operand0)); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_tsthi_1 (operand0) + rtx operand0; +{ + return gen_rtx (SET, VOIDmode, cc0_rtx, operand0); +} + +rtx +gen_tsthi (operand0) + rtx operand0; +{ + rtx operands[1]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + +{ + i386_compare_gen = gen_tsthi_1; + i386_compare_op0 = operands[0]; + DONE; +} + operand0 = operands[0]; + emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, operand0)); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_tstqi_1 (operand0) + rtx operand0; +{ + return gen_rtx (SET, VOIDmode, cc0_rtx, operand0); +} + +rtx +gen_tstqi (operand0) + rtx operand0; +{ + rtx operands[1]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + +{ + i386_compare_gen = gen_tstqi_1; + i386_compare_op0 = operands[0]; + DONE; +} + operand0 = operands[0]; + emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, operand0)); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_tstsf_cc (operand0) + rtx operand0; +{ + return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, cc0_rtx, operand0), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0)))); +} + +rtx +gen_tstsf (operand0) + rtx operand0; +{ + rtx operands[1]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + +{ + i386_compare_gen = gen_tstsf_cc; + i386_compare_op0 = operands[0]; + DONE; +} + operand0 = operands[0]; + emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, cc0_rtx, operand0), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0))))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_tstdf_cc (operand0) + rtx operand0; +{ + return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, cc0_rtx, operand0), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0)))); +} + +rtx +gen_tstdf (operand0) + rtx operand0; +{ + rtx operands[1]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + +{ + i386_compare_gen = gen_tstdf_cc; + i386_compare_op0 = operands[0]; + DONE; +} + operand0 = operands[0]; + emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, cc0_rtx, operand0), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0))))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_cmpsi_1 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1)); +} + +rtx +gen_cmpsi (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + +{ + if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) + operands[0] = force_reg (SImode, operands[0]); + + i386_compare_gen = gen_cmpsi_1; + i386_compare_op0 = operands[0]; + i386_compare_op1 = operands[1]; + DONE; +} + operand0 = operands[0]; + operand1 = operands[1]; + emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_cmphi_1 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1)); +} + +rtx +gen_cmphi (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + +{ + if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) + operands[0] = force_reg (HImode, operands[0]); + + i386_compare_gen = gen_cmphi_1; + i386_compare_op0 = operands[0]; + i386_compare_op1 = operands[1]; + DONE; +} + operand0 = operands[0]; + operand1 = operands[1]; + emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_cmpqi_1 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1)); +} + +rtx +gen_cmpqi (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + +{ + if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) + operands[0] = force_reg (QImode, operands[0]); + + i386_compare_gen = gen_cmpqi_1; + i386_compare_op0 = operands[0]; + i386_compare_op1 = operands[1]; + DONE; +} + operand0 = operands[0]; + operand1 = operands[1]; + emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_cmpsf_cc_1 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (GET_CODE (operand2), VOIDmode, + operand0, + operand1)), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0)))); +} + +rtx +gen_cmpdf (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + +{ + i386_compare_gen = gen_cmpdf_cc; + i386_compare_gen_eq = gen_cmpdf_ccfpeq; + i386_compare_op0 = operands[0]; + i386_compare_op1 = operands[1]; + DONE; +} + operand0 = operands[0]; + operand1 = operands[1]; + emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_cmpsf (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + +{ + i386_compare_gen = gen_cmpsf_cc; + i386_compare_gen_eq = gen_cmpsf_ccfpeq; + i386_compare_op0 = operands[0]; + i386_compare_op1 = operands[1]; + DONE; +} + operand0 = operands[0]; + operand1 = operands[1]; + emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_cmpdf_cc (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1)), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0)))); +} + +rtx +gen_cmpdf_ccfpeq (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + +{ + if (! register_operand (operands[1], DFmode)) + operands[1] = copy_to_mode_reg (DFmode, operands[1]); +} + operand0 = operands[0]; + operand1 = operands[1]; + emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, CCFPEQmode, operand0, operand1)), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0))))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_cmpsf_cc (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, operand0, operand1)), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0)))); +} + +rtx +gen_cmpsf_ccfpeq (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + +{ + if (! register_operand (operands[1], SFmode)) + operands[1] = copy_to_mode_reg (SFmode, operands[1]); +} + operand0 = operands[0]; + operand1 = operands[1]; + emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, CCFPEQmode, operand0, operand1)), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0))))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_movsi (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + +{ + extern int flag_pic; + + if (flag_pic && SYMBOLIC_CONST (operands[1])) + emit_pic_move (operands, SImode); +} + operand0 = operands[0]; + operand1 = operands[1]; + emit_insn (gen_rtx (SET, VOIDmode, operand0, operand1)); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_movhi (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, operand1); +} + +rtx +gen_movstricthi (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, gen_rtx (STRICT_LOW_PART, VOIDmode, operand0), operand1); +} + +rtx +gen_movqi (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, operand1); +} + +rtx +gen_movstrictqi (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, gen_rtx (STRICT_LOW_PART, VOIDmode, operand0), operand1); +} + +rtx +gen_movsf (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, operand1); +} + +rtx +gen_swapdf (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, operand0, operand1), + gen_rtx (SET, VOIDmode, operand1, operand0))); +} + +rtx +gen_movdf (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, operand1); +} + +rtx +gen_movdi (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, operand1); +} + +rtx +gen_zero_extendhisi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ZERO_EXTEND, SImode, operand1)); +} + +rtx +gen_zero_extendqihi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ZERO_EXTEND, HImode, operand1)); +} + +rtx +gen_zero_extendqisi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ZERO_EXTEND, SImode, operand1)); +} + +rtx +gen_zero_extendsidi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ZERO_EXTEND, DImode, operand1)); +} + +rtx +gen_extendsidi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (SIGN_EXTEND, DImode, operand1)); +} + +rtx +gen_extendhisi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (SIGN_EXTEND, SImode, operand1)); +} + +rtx +gen_extendqihi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (SIGN_EXTEND, HImode, operand1)); +} + +rtx +gen_extendqisi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (SIGN_EXTEND, SImode, operand1)); +} + +rtx +gen_extendsfdf2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (FLOAT_EXTEND, DFmode, operand1)); +} + +rtx +gen_truncdfsf2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operand2; + rtx operands[3]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + +{ + operands[2] = (rtx) assign_386_stack_local (SFmode, 0); +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, operand0, gen_rtx (FLOAT_TRUNCATE, SFmode, operand1)), + gen_rtx (CLOBBER, VOIDmode, operand2)))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_fixuns_truncdfsi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operand2; + rtx operand3; + rtx operand4; + rtx operand5; + rtx operand6; + rtx operands[7]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + +{ + operands[2] = gen_reg_rtx (DImode); + operands[3] = gen_lowpart (SImode, operands[2]); + operands[4] = gen_reg_rtx (DFmode); + operands[5] = (rtx) assign_386_stack_local (SImode, 0); + operands[6] = (rtx) assign_386_stack_local (SImode, 1); +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + operand3 = operands[3]; + operand4 = operands[4]; + operand5 = operands[5]; + operand6 = operands[6]; + emit_insn (gen_rtx (SET, VOIDmode, operand4, operand1)); + emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (5, + gen_rtx (SET, VOIDmode, operand2, gen_rtx (FIX, DImode, gen_rtx (FIX, DFmode, operand4))), + gen_rtx (CLOBBER, VOIDmode, operand4), + gen_rtx (CLOBBER, VOIDmode, operand5), + gen_rtx (CLOBBER, VOIDmode, operand6), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0))))); + emit_insn (gen_rtx (SET, VOIDmode, operand0, operand3)); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_fixuns_truncsfsi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operand2; + rtx operand3; + rtx operand4; + rtx operand5; + rtx operand6; + rtx operands[7]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + +{ + operands[2] = gen_reg_rtx (DImode); + operands[3] = gen_lowpart (SImode, operands[2]); + operands[4] = gen_reg_rtx (SFmode); + operands[5] = (rtx) assign_386_stack_local (SImode, 0); + operands[6] = (rtx) assign_386_stack_local (SImode, 1); +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + operand3 = operands[3]; + operand4 = operands[4]; + operand5 = operands[5]; + operand6 = operands[6]; + emit_insn (gen_rtx (SET, VOIDmode, operand4, operand1)); + emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (5, + gen_rtx (SET, VOIDmode, operand2, gen_rtx (FIX, DImode, gen_rtx (FIX, SFmode, operand4))), + gen_rtx (CLOBBER, VOIDmode, operand4), + gen_rtx (CLOBBER, VOIDmode, operand5), + gen_rtx (CLOBBER, VOIDmode, operand6), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0))))); + emit_insn (gen_rtx (SET, VOIDmode, operand0, operand3)); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_fix_truncdfdi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operand2; + rtx operand3; + rtx operand4; + rtx operands[5]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + +{ + operands[1] = copy_to_mode_reg (DFmode, operands[1]); + operands[2] = gen_reg_rtx (DFmode); + operands[3] = (rtx) assign_386_stack_local (SImode, 0); + operands[4] = (rtx) assign_386_stack_local (SImode, 1); +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + operand3 = operands[3]; + operand4 = operands[4]; + emit_insn (gen_rtx (SET, VOIDmode, operand2, operand1)); + emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (5, + gen_rtx (SET, VOIDmode, operand0, gen_rtx (FIX, DImode, gen_rtx (FIX, DFmode, operand2))), + gen_rtx (CLOBBER, VOIDmode, operand2), + gen_rtx (CLOBBER, VOIDmode, operand3), + gen_rtx (CLOBBER, VOIDmode, operand4), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0))))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_fix_truncsfdi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operand2; + rtx operand3; + rtx operand4; + rtx operands[5]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + +{ + operands[1] = copy_to_mode_reg (SFmode, operands[1]); + operands[2] = gen_reg_rtx (SFmode); + operands[3] = (rtx) assign_386_stack_local (SImode, 0); + operands[4] = (rtx) assign_386_stack_local (SImode, 1); +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + operand3 = operands[3]; + operand4 = operands[4]; + emit_insn (gen_rtx (SET, VOIDmode, operand2, operand1)); + emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (5, + gen_rtx (SET, VOIDmode, operand0, gen_rtx (FIX, DImode, gen_rtx (FIX, SFmode, operand2))), + gen_rtx (CLOBBER, VOIDmode, operand2), + gen_rtx (CLOBBER, VOIDmode, operand3), + gen_rtx (CLOBBER, VOIDmode, operand4), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0))))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_fix_truncdfsi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operand2; + rtx operand3; + rtx operands[4]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + +{ + operands[2] = (rtx) assign_386_stack_local (SImode, 0); + operands[3] = (rtx) assign_386_stack_local (SImode, 1); +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + operand3 = operands[3]; + emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (4, + gen_rtx (SET, VOIDmode, operand0, gen_rtx (FIX, SImode, gen_rtx (FIX, DFmode, operand1))), + gen_rtx (CLOBBER, VOIDmode, operand2), + gen_rtx (CLOBBER, VOIDmode, operand3), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0))))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_fix_truncsfsi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operand2; + rtx operand3; + rtx operands[4]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + +{ + operands[2] = (rtx) assign_386_stack_local (SImode, 0); + operands[3] = (rtx) assign_386_stack_local (SImode, 1); +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + operand3 = operands[3]; + emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (4, + gen_rtx (SET, VOIDmode, operand0, gen_rtx (FIX, SImode, gen_rtx (FIX, SFmode, operand1))), + gen_rtx (CLOBBER, VOIDmode, operand2), + gen_rtx (CLOBBER, VOIDmode, operand3), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0))))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_floatsisf2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (FLOAT, SFmode, operand1)); +} + +rtx +gen_floatdisf2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (FLOAT, SFmode, operand1)); +} + +rtx +gen_floatsidf2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (FLOAT, DFmode, operand1)); +} + +rtx +gen_floatdidf2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (FLOAT, DFmode, operand1)); +} + +rtx +gen_adddi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (PLUS, DImode, operand1, operand2)); +} + +rtx +gen_addsi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (PLUS, SImode, operand1, operand2)); +} + +rtx +gen_addhi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (PLUS, HImode, operand1, operand2)); +} + +rtx +gen_addqi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (PLUS, QImode, operand1, operand2)); +} + +rtx +gen_adddf3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (PLUS, DFmode, operand1, operand2)); +} + +rtx +gen_addsf3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (PLUS, SFmode, operand1, operand2)); +} + +rtx +gen_subdi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MINUS, DImode, operand1, operand2)); +} + +rtx +gen_subsi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MINUS, SImode, operand1, operand2)); +} + +rtx +gen_subhi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MINUS, HImode, operand1, operand2)); +} + +rtx +gen_subqi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MINUS, QImode, operand1, operand2)); +} + +rtx +gen_subdf3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MINUS, DFmode, operand1, operand2)); +} + +rtx +gen_subsf3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MINUS, SFmode, operand1, operand2)); +} + +rtx +gen_mulhi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MULT, SImode, operand1, operand2)); +} + +rtx +gen_mulsi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MULT, SImode, operand1, operand2)); +} + +rtx +gen_muldf3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MULT, DFmode, operand1, operand2)); +} + +rtx +gen_mulsf3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (MULT, SFmode, operand1, operand2)); +} + +rtx +gen_divqi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (DIV, QImode, operand1, operand2)); +} + +rtx +gen_udivqi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (UDIV, QImode, operand1, operand2)); +} + +rtx +gen_divdf3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (DIV, DFmode, operand1, operand2)); +} + +rtx +gen_divsf3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (DIV, SFmode, operand1, operand2)); +} + +rtx +gen_divmodsi4 (operand0, operand1, operand2, operand3) + rtx operand0; + rtx operand1; + rtx operand2; + rtx operand3; +{ + return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, operand0, gen_rtx (DIV, SImode, operand1, operand2)), + gen_rtx (SET, VOIDmode, operand3, gen_rtx (MOD, SImode, operand1, operand2)))); +} + +rtx +gen_divmodhi4 (operand0, operand1, operand2, operand3) + rtx operand0; + rtx operand1; + rtx operand2; + rtx operand3; +{ + return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, operand0, gen_rtx (DIV, HImode, operand1, operand2)), + gen_rtx (SET, VOIDmode, operand3, gen_rtx (MOD, HImode, operand1, operand2)))); +} + +rtx +gen_udivmodsi4 (operand0, operand1, operand2, operand3) + rtx operand0; + rtx operand1; + rtx operand2; + rtx operand3; +{ + return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, operand0, gen_rtx (UDIV, SImode, operand1, operand2)), + gen_rtx (SET, VOIDmode, operand3, gen_rtx (UMOD, SImode, operand1, operand2)))); +} + +rtx +gen_udivmodhi4 (operand0, operand1, operand2, operand3) + rtx operand0; + rtx operand1; + rtx operand2; + rtx operand3; +{ + return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, operand0, gen_rtx (UDIV, HImode, operand1, operand2)), + gen_rtx (SET, VOIDmode, operand3, gen_rtx (UMOD, HImode, operand1, operand2)))); +} + +rtx +gen_andsi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (AND, SImode, operand1, operand2)); +} + +rtx +gen_andhi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (AND, HImode, operand1, operand2)); +} + +rtx +gen_andqi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (AND, QImode, operand1, operand2)); +} + +rtx +gen_iorsi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (IOR, SImode, operand1, operand2)); +} + +rtx +gen_iorhi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (IOR, HImode, operand1, operand2)); +} + +rtx +gen_iorqi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (IOR, QImode, operand1, operand2)); +} + +rtx +gen_xorsi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (XOR, SImode, operand1, operand2)); +} + +rtx +gen_xorhi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (XOR, HImode, operand1, operand2)); +} + +rtx +gen_xorqi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (XOR, QImode, operand1, operand2)); +} + +rtx +gen_negdi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NEG, DImode, operand1)); +} + +rtx +gen_negsi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NEG, SImode, operand1)); +} + +rtx +gen_neghi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NEG, HImode, operand1)); +} + +rtx +gen_negqi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NEG, QImode, operand1)); +} + +rtx +gen_negsf2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NEG, SFmode, operand1)); +} + +rtx +gen_negdf2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NEG, DFmode, operand1)); +} + +rtx +gen_abssf2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ABS, SFmode, operand1)); +} + +rtx +gen_absdf2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ABS, DFmode, operand1)); +} + +rtx +gen_sqrtsf2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (SQRT, SFmode, operand1)); +} + +rtx +gen_sqrtdf2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (SQRT, DFmode, operand1)); +} + +rtx +gen_sindf2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (UNSPEC, DFmode, gen_rtvec (1, + operand1), 1)); +} + +rtx +gen_sinsf2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (UNSPEC, SFmode, gen_rtvec (1, + operand1), 1)); +} + +rtx +gen_cosdf2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (UNSPEC, DFmode, gen_rtvec (1, + operand1), 2)); +} + +rtx +gen_cossf2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (UNSPEC, SFmode, gen_rtvec (1, + operand1), 2)); +} + +rtx +gen_one_cmplsi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NOT, SImode, operand1)); +} + +rtx +gen_one_cmplhi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NOT, HImode, operand1)); +} + +rtx +gen_one_cmplqi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (NOT, QImode, operand1)); +} + +rtx +gen_ashldi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + rtx operands[3]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + operands[2] = operand2; + +{ + if (GET_CODE (operands[2]) != CONST_INT + || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J')) + { + operands[2] = copy_to_mode_reg (QImode, operands[2]); + emit_insn (gen_ashldi3_non_const_int (operands[0], operands[1], + operands[2])); + } + else + emit_insn (gen_ashldi3_const_int (operands[0], operands[1], operands[2])); + + DONE; +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFT, DImode, operand1, operand2))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_ashldi3_const_int (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFT, DImode, operand1, operand2)); +} + +rtx +gen_ashldi3_non_const_int (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFT, DImode, operand1, operand2)), + gen_rtx (CLOBBER, VOIDmode, operand2))); +} + +rtx +gen_ashlsi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFT, SImode, operand1, operand2)); +} + +rtx +gen_ashlhi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFT, HImode, operand1, operand2)); +} + +rtx +gen_ashlqi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFT, QImode, operand1, operand2)); +} + +rtx +gen_ashrdi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + rtx operands[3]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + operands[2] = operand2; + +{ + if (GET_CODE (operands[2]) != CONST_INT + || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J')) + { + operands[2] = copy_to_mode_reg (QImode, operands[2]); + emit_insn (gen_ashrdi3_non_const_int (operands[0], operands[1], + operands[2])); + } + else + emit_insn (gen_ashrdi3_const_int (operands[0], operands[1], operands[2])); + + DONE; +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFTRT, DImode, operand1, operand2))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_ashrdi3_const_int (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFTRT, DImode, operand1, operand2)); +} + +rtx +gen_ashrdi3_non_const_int (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFTRT, DImode, operand1, operand2)), + gen_rtx (CLOBBER, VOIDmode, operand2))); +} + +rtx +gen_ashrsi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFTRT, SImode, operand1, operand2)); +} + +rtx +gen_ashrhi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFTRT, HImode, operand1, operand2)); +} + +rtx +gen_ashrqi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ASHIFTRT, QImode, operand1, operand2)); +} + +rtx +gen_lshrdi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + rtx operands[3]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + operands[2] = operand2; + +{ + if (GET_CODE (operands[2]) != CONST_INT + || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J')) + { + operands[2] = copy_to_mode_reg (QImode, operands[2]); + emit_insn (gen_lshrdi3_non_const_int (operands[0], operands[1], + operands[2])); + } + else + emit_insn (gen_lshrdi3_const_int (operands[0], operands[1], operands[2])); + + DONE; +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (LSHIFTRT, DImode, operand1, operand2))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_lshrdi3_const_int (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (LSHIFTRT, DImode, operand1, operand2)); +} + +rtx +gen_lshrdi3_non_const_int (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, operand0, gen_rtx (LSHIFTRT, DImode, operand1, operand2)), + gen_rtx (CLOBBER, VOIDmode, operand2))); +} + +rtx +gen_lshrsi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (LSHIFTRT, SImode, operand1, operand2)); +} + +rtx +gen_lshrhi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (LSHIFTRT, HImode, operand1, operand2)); +} + +rtx +gen_lshrqi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (LSHIFTRT, QImode, operand1, operand2)); +} + +rtx +gen_rotlsi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ROTATE, SImode, operand1, operand2)); +} + +rtx +gen_rotlhi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ROTATE, HImode, operand1, operand2)); +} + +rtx +gen_rotlqi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ROTATE, QImode, operand1, operand2)); +} + +rtx +gen_rotrsi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ROTATERT, SImode, operand1, operand2)); +} + +rtx +gen_rotrhi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ROTATERT, HImode, operand1, operand2)); +} + +rtx +gen_rotrqi3 (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + return gen_rtx (SET, VOIDmode, operand0, gen_rtx (ROTATERT, QImode, operand1, operand2)); +} + +rtx +gen_seq (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + +{ + if (TARGET_IEEE_FP + && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT) + operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1); + else + operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); +} + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (EQ, QImode, cc0_rtx, const0_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_sne (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + +{ + if (TARGET_IEEE_FP + && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT) + operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1); + else + operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); +} + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (NE, QImode, cc0_rtx, const0_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_sgt (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; +operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (GT, QImode, cc0_rtx, const0_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_sgtu (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; +operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (GTU, QImode, cc0_rtx, const0_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_slt (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; +operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (LT, QImode, cc0_rtx, const0_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_sltu (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; +operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (LTU, QImode, cc0_rtx, const0_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_sge (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; +operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (GE, QImode, cc0_rtx, const0_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_sgeu (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; +operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (GEU, QImode, cc0_rtx, const0_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_sle (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; +operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (LE, QImode, cc0_rtx, const0_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_sleu (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; +operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (LEU, QImode, cc0_rtx, const0_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_beq (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + +{ + if (TARGET_IEEE_FP + && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT) + operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1); + else + operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); +} + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (EQ, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_bne (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + +{ + if (TARGET_IEEE_FP + && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT) + operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1); + else + operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); +} + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (NE, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_bgt (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; +operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (GT, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_bgtu (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; +operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (GTU, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_blt (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; +operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (LT, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_bltu (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; +operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (LTU, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_bge (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; +operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (GE, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_bgeu (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; +operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (GEU, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_ble (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; +operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (LE, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_bleu (operand0) + rtx operand0; +{ + rtx operand1; + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; +operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand1); + emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (LEU, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), pc_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_jump (operand0) + rtx operand0; +{ + return gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (LABEL_REF, VOIDmode, operand0)); +} + +rtx +gen_indirect_jump (operand0) + rtx operand0; +{ + return gen_rtx (SET, VOIDmode, pc_rtx, operand0); +} + +rtx +gen_casesi (operand0, operand1, operand2, operand3, operand4) + rtx operand0; + rtx operand1; + rtx operand2; + rtx operand3; + rtx operand4; +{ + rtx operand5; + rtx operands[6]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + operands[2] = operand2; + operands[3] = operand3; + operands[4] = operand4; + +{ + operands[5] = gen_reg_rtx (SImode); + current_function_uses_pic_offset_table = 1; +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + operand3 = operands[3]; + operand4 = operands[4]; + operand5 = operands[5]; + emit_insn (gen_rtx (SET, VOIDmode, operand5, gen_rtx (MINUS, SImode, operand0, operand1))); + emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, CCmode, operand5, operand2))); + emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (GTU, VOIDmode, cc0_rtx, const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand4), pc_rtx))); + emit_jump_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (MINUS, SImode, gen_rtx (REG, SImode, 3), gen_rtx (MEM, SImode, gen_rtx (PLUS, SImode, gen_rtx (MULT, SImode, operand5, GEN_INT (4)), gen_rtx (LABEL_REF, VOIDmode, operand3))))), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0))))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_tablejump (operand0, operand1) + rtx operand0; + rtx operand1; +{ + return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, pc_rtx, operand0), + gen_rtx (USE, VOIDmode, gen_rtx (LABEL_REF, VOIDmode, operand1)))); +} + +rtx +gen_call_pop (operand0, operand1, operand2, operand3) + rtx operand0; + rtx operand1; + rtx operand2; + rtx operand3; +{ + rtx operands[4]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + operands[2] = operand2; + operands[3] = operand3; + +{ + rtx addr; + + if (flag_pic) + current_function_uses_pic_offset_table = 1; + + /* With half-pic, force the address into a register. */ + addr = XEXP (operands[0], 0); + if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr)) + XEXP (operands[0], 0) = force_reg (Pmode, addr); + + if (! expander_call_insn_operand (operands[0], QImode)) + operands[0] + = change_address (operands[0], VOIDmode, + copy_to_mode_reg (Pmode, XEXP (operands[0], 0))); +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + operand3 = operands[3]; + emit_call_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (CALL, VOIDmode, operand0, operand1), + gen_rtx (SET, VOIDmode, gen_rtx (REG, SImode, 7), gen_rtx (PLUS, SImode, gen_rtx (REG, SImode, 7), operand3))))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_call (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + +{ + rtx addr; + + if (flag_pic) + current_function_uses_pic_offset_table = 1; + + /* With half-pic, force the address into a register. */ + addr = XEXP (operands[0], 0); + if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr)) + XEXP (operands[0], 0) = force_reg (Pmode, addr); + + if (! expander_call_insn_operand (operands[0], QImode)) + operands[0] + = change_address (operands[0], VOIDmode, + copy_to_mode_reg (Pmode, XEXP (operands[0], 0))); +} + operand0 = operands[0]; + operand1 = operands[1]; + emit_call_insn (gen_rtx (CALL, VOIDmode, operand0, operand1)); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_call_value_pop (operand0, operand1, operand2, operand3, operand4) + rtx operand0; + rtx operand1; + rtx operand2; + rtx operand3; + rtx operand4; +{ + rtx operands[5]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + operands[2] = operand2; + operands[3] = operand3; + operands[4] = operand4; + +{ + rtx addr; + + if (flag_pic) + current_function_uses_pic_offset_table = 1; + + /* With half-pic, force the address into a register. */ + addr = XEXP (operands[1], 0); + if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr)) + XEXP (operands[1], 0) = force_reg (Pmode, addr); + + if (! expander_call_insn_operand (operands[1], QImode)) + operands[1] + = change_address (operands[1], VOIDmode, + copy_to_mode_reg (Pmode, XEXP (operands[1], 0))); +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + operand3 = operands[3]; + operand4 = operands[4]; + emit_call_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, operand0, gen_rtx (CALL, VOIDmode, operand1, operand2)), + gen_rtx (SET, VOIDmode, gen_rtx (REG, SImode, 7), gen_rtx (PLUS, SImode, gen_rtx (REG, SImode, 7), operand4))))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_call_value (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + rtx operands[3]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + operands[2] = operand2; + +{ + rtx addr; + + if (flag_pic) + current_function_uses_pic_offset_table = 1; + + /* With half-pic, force the address into a register. */ + addr = XEXP (operands[1], 0); + if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr)) + XEXP (operands[1], 0) = force_reg (Pmode, addr); + + if (! expander_call_insn_operand (operands[1], QImode)) + operands[1] + = change_address (operands[1], VOIDmode, + copy_to_mode_reg (Pmode, XEXP (operands[1], 0))); +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + emit_call_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (CALL, VOIDmode, operand1, operand2))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_untyped_call (operand0, operand1, operand2) + rtx operand0; + rtx operand1; + rtx operand2; +{ + rtx operands[3]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + operands[2] = operand2; + +{ + rtx addr; + + if (flag_pic) + current_function_uses_pic_offset_table = 1; + + /* With half-pic, force the address into a register. */ + addr = XEXP (operands[0], 0); + if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr)) + XEXP (operands[0], 0) = force_reg (Pmode, addr); + + operands[1] = change_address (operands[1], DImode, XEXP (operands[1], 0)); + if (! expander_call_insn_operand (operands[1], QImode)) + operands[1] + = change_address (operands[1], VOIDmode, + copy_to_mode_reg (Pmode, XEXP (operands[1], 0))); +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + emit_call_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (3, + gen_rtx (CALL, VOIDmode, operand0, const0_rtx), + operand1, + operand2))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_untyped_return (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operands[2]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + +{ + rtx valreg1 = gen_rtx (REG, SImode, 0); + rtx valreg2 = gen_rtx (REG, SImode, 1); + rtx result = operands[0]; + + /* Restore the FPU state. */ + emit_insn (gen_update_return (change_address (result, SImode, + plus_constant (XEXP (result, 0), + 8)))); + + /* Reload the function value registers. */ + emit_move_insn (valreg1, change_address (result, SImode, XEXP (result, 0))); + emit_move_insn (valreg2, + change_address (result, SImode, + plus_constant (XEXP (result, 0), 4))); + + /* Put USE insns before the return. */ + emit_insn (gen_rtx (USE, VOIDmode, valreg1)); + emit_insn (gen_rtx (USE, VOIDmode, valreg2)); + + /* Construct the return. */ + expand_null_return (); + + DONE; +} + operand0 = operands[0]; + operand1 = operands[1]; + emit (operand0); + emit (operand1); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_update_return (operand0) + rtx operand0; +{ + return gen_rtx (UNSPEC, SImode, gen_rtvec (1, + operand0), 0); +} + +rtx +gen_return () +{ + return gen_rtx (RETURN, VOIDmode); +} + +rtx +gen_nop () +{ + return const0_rtx; +} + +rtx +gen_movstrsi (operand0, operand1, operand2, operand3) + rtx operand0; + rtx operand1; + rtx operand2; + rtx operand3; +{ + rtx operand4; + rtx operand5; + rtx operand6; + rtx operands[7]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + operands[2] = operand2; + operands[3] = operand3; + +{ + rtx addr0, addr1; + + if (GET_CODE (operands[2]) != CONST_INT) + FAIL; + + addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0)); + addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0)); + + operands[5] = addr0; + operands[6] = addr1; + + operands[0] = gen_rtx (MEM, BLKmode, addr0); + operands[1] = gen_rtx (MEM, BLKmode, addr1); +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + operand3 = operands[3]; + operand4 = operands[4]; + operand5 = operands[5]; + operand6 = operands[6]; + emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (6, + gen_rtx (SET, VOIDmode, operand0, operand1), + gen_rtx (USE, VOIDmode, operand2), + gen_rtx (USE, VOIDmode, operand3), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0)), + gen_rtx (CLOBBER, VOIDmode, operand5), + gen_rtx (CLOBBER, VOIDmode, operand6)))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_cmpstrsi (operand0, operand1, operand2, operand3, operand4) + rtx operand0; + rtx operand1; + rtx operand2; + rtx operand3; + rtx operand4; +{ + rtx operand5; + rtx operand6; + rtx operands[7]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + operands[2] = operand2; + operands[3] = operand3; + operands[4] = operand4; + +{ + rtx addr1, addr2; + + addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0)); + addr2 = copy_to_mode_reg (Pmode, XEXP (operands[2], 0)); + operands[3] = copy_to_mode_reg (SImode, operands[3]); + + operands[5] = addr1; + operands[6] = addr2; + + operands[1] = gen_rtx (MEM, BLKmode, addr1); + operands[2] = gen_rtx (MEM, BLKmode, addr2); + +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + operand3 = operands[3]; + operand4 = operands[4]; + operand5 = operands[5]; + operand6 = operands[6]; + emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (6, + gen_rtx (SET, VOIDmode, operand0, gen_rtx (COMPARE, SImode, operand1, operand2)), + gen_rtx (USE, VOIDmode, operand3), + gen_rtx (USE, VOIDmode, operand4), + gen_rtx (CLOBBER, VOIDmode, operand5), + gen_rtx (CLOBBER, VOIDmode, operand6), + gen_rtx (CLOBBER, VOIDmode, operand3)))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_ffssi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operand2; + rtx operand3; + rtx operands[4]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; +operands[3] = gen_reg_rtx (SImode); + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + operand3 = operands[3]; + emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, operand3, gen_rtx (PLUS, SImode, gen_rtx (FFS, SImode, operand1), constm1_rtx)), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0))))); + emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (PLUS, SImode, operand3, const1_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_ffshi2 (operand0, operand1) + rtx operand0; + rtx operand1; +{ + rtx operand2; + rtx operand3; + rtx operands[4]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; +operands[3] = gen_reg_rtx (HImode); + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + operand3 = operands[3]; + emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, operand3, gen_rtx (PLUS, HImode, gen_rtx (FFS, HImode, operand1), constm1_rtx)), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0))))); + emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (PLUS, HImode, operand3, const1_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + +rtx +gen_strlensi (operand0, operand1, operand2, operand3) + rtx operand0; + rtx operand1; + rtx operand2; + rtx operand3; +{ + rtx operand4; + rtx operand5; + rtx operands[6]; + rtx _val = 0; + start_sequence (); + operands[0] = operand0; + operands[1] = operand1; + operands[2] = operand2; + operands[3] = operand3; + +{ + operands[1] = copy_to_mode_reg (SImode, XEXP (operands[1], 0)); + operands[4] = gen_reg_rtx (SImode); + operands[5] = gen_reg_rtx (SImode); +} + operand0 = operands[0]; + operand1 = operands[1]; + operand2 = operands[2]; + operand3 = operands[3]; + operand4 = operands[4]; + operand5 = operands[5]; + emit (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, + gen_rtx (SET, VOIDmode, operand4, gen_rtx (UNSPEC, SImode, gen_rtvec (3, + gen_rtx (MEM, BLKmode, operand1), + operand2, + operand3), 0)), + gen_rtx (CLOBBER, VOIDmode, operand1)))); + emit_insn (gen_rtx (SET, VOIDmode, operand5, gen_rtx (NOT, SImode, operand4))); + emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (MINUS, SImode, operand5, const1_rtx))); + _done: + _val = gen_sequence (); + _fail: + end_sequence (); + return _val; +} + + + +void +add_clobbers (pattern, insn_code_number) + rtx pattern; + int insn_code_number; +{ + int i; + + switch (insn_code_number) + { + case 250: + case 223: + XVECEXP (pattern, 0, 1) = gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0)); + break; + + case 72: + case 71: + XVECEXP (pattern, 0, 3) = gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0)); + break; + + case 68: + case 67: + XVECEXP (pattern, 0, 4) = gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0)); + break; + + case 252: + case 25: + case 24: + case 23: + case 22: + case 21: + case 20: + case 19: + case 18: + case 17: + case 16: + case 8: + case 6: + XVECEXP (pattern, 0, 1) = gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, HImode, 0)); + break; + + default: + abort (); + } +} + +void +init_mov_optab () +{ +#ifdef HAVE_movccfpeq + if (HAVE_movccfpeq) + mov_optab->handlers[(int) CCFPEQmode].insn_code = CODE_FOR_movccfpeq; +#endif +} diff --git a/gnu/usr.bin/cc/lib/insn-extract.c b/gnu/usr.bin/cc/lib/insn-extract.c new file mode 100644 index 0000000000..f645b830af --- /dev/null +++ b/gnu/usr.bin/cc/lib/insn-extract.c @@ -0,0 +1,505 @@ +/* Generated automatically by the program `genextract' +from the machine description file `md'. */ + +#include "config.h" +#include "rtl.h" + +static rtx junk; +extern rtx recog_operand[]; +extern rtx *recog_operand_loc[]; +extern rtx *recog_dup_loc[]; +extern char recog_dup_num[]; +extern +#ifdef __GNUC__ +__volatile__ +#endif +void fatal_insn_not_found (); + +void +insn_extract (insn) + rtx insn; +{ + register rtx *ro = recog_operand; + register rtx **ro_loc = recog_operand_loc; + rtx pat = PATTERN (insn); + switch (INSN_CODE (insn)) + { + case -1: + fatal_insn_not_found (insn); + + case 262: + ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0, 0), 0)); + ro[2] = *(ro_loc[2] = &XVECEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0, 1)); + ro[3] = *(ro_loc[3] = &XVECEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0, 2)); + recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 1), 0); + recog_dup_num[0] = 1; + break; + + case 260: + case 257: + case 256: + ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XEXP (XEXP (pat, 1), 1), 0)); + ro[3] = *(ro_loc[3] = &XEXP (pat, 1)); + break; + + case 259: + case 255: + case 254: + ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (pat, 1), 0), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 1)); + ro[3] = *(ro_loc[3] = &XEXP (pat, 1)); + break; + + case 258: + case 253: + ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 1)); + ro[3] = *(ro_loc[3] = &XEXP (pat, 1)); + break; + + case 252: + case 250: + ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0)); + break; + + case 248: + ro[0] = *(ro_loc[0] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0)); + ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 2), 0)); + recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 5), 0); + recog_dup_num[0] = 2; + recog_dup_loc[1] = &XEXP (XVECEXP (pat, 0, 4), 0); + recog_dup_num[1] = 1; + recog_dup_loc[2] = &XEXP (XVECEXP (pat, 0, 3), 0); + recog_dup_num[2] = 0; + break; + + case 247: + ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1), 0)); + ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 1), 0)); + ro[4] = *(ro_loc[4] = &XEXP (XVECEXP (pat, 0, 2), 0)); + recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 5), 0); + recog_dup_num[0] = 3; + recog_dup_loc[1] = &XEXP (XVECEXP (pat, 0, 4), 0); + recog_dup_num[1] = 2; + recog_dup_loc[2] = &XEXP (XVECEXP (pat, 0, 3), 0); + recog_dup_num[2] = 1; + break; + + case 245: + ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0)); + ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 2), 0)); + ro[4] = *(ro_loc[4] = &XEXP (XVECEXP (pat, 0, 3), 0)); + recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 5), 0); + recog_dup_num[0] = 1; + recog_dup_loc[1] = &XEXP (XVECEXP (pat, 0, 4), 0); + recog_dup_num[1] = 0; + break; + + case 243: + case 242: + break; + + case 241: + ro[0] = *(ro_loc[0] = &XVECEXP (pat, 0, 0)); + break; + + case 239: + ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 0), 0)); + ro[1] = *(ro_loc[1] = &XVECEXP (pat, 0, 1)); + ro[2] = *(ro_loc[2] = &XVECEXP (pat, 0, 2)); + break; + + case 238: + ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); + ro[1] = *(ro_loc[1] = &XVECEXP (pat, 0, 1)); + ro[2] = *(ro_loc[2] = &XVECEXP (pat, 0, 2)); + break; + + case 236: + ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (pat, 1), 0), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 1)); + break; + + case 233: + ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1)); + ro[3] = const0_rtx; + ro_loc[3] = &junk; + ro[4] = *(ro_loc[4] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 1)); + break; + + case 232: + ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1)); + ro[3] = const0_rtx; + ro_loc[3] = &junk; + ro[4] = *(ro_loc[4] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 1)); + break; + + case 227: + ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (pat, 0, 0), 1)); + ro[2] = const0_rtx; + ro_loc[2] = &junk; + ro[3] = *(ro_loc[3] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 1)); + break; + + case 226: + ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (pat, 0, 0), 1)); + ro[2] = const0_rtx; + ro_loc[2] = &junk; + ro[3] = *(ro_loc[3] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 1)); + break; + + case 224: + ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 1)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 0), 0)); + break; + + case 223: + ro[0] = *(ro_loc[0] = &XEXP (XEXP (XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1), 0), 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1), 0), 1), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0)); + break; + + case 220: + ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 1), 0)); + break; + + case 219: + case 218: + case 217: + case 216: + case 215: + case 214: + case 213: + case 212: + case 211: + case 210: + ro[0] = *(ro_loc[0] = &XEXP (XEXP (XEXP (pat, 1), 2), 0)); + break; + + case 209: + case 207: + case 205: + case 203: + case 201: + case 199: + case 197: + case 195: + case 193: + case 191: + ro[0] = *(ro_loc[0] = &XEXP (XEXP (XEXP (pat, 1), 1), 0)); + break; + + case 189: + case 187: + case 185: + case 183: + case 181: + case 179: + case 177: + case 175: + case 173: + case 171: + ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); + break; + + case 169: + case 168: + ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 1), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 1)); + ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 2)); + break; + + case 167: + ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 1), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 2)); + break; + + case 166: + ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XEXP (XEXP (pat, 1), 1), 1)); + break; + + case 165: + ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (pat, 1), 0), 1)); + ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 1)); + break; + + case 164: + ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 0), 0)); + ro[1] = const0_rtx; + ro_loc[1] = &junk; + ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 0), 2)); + ro[3] = *(ro_loc[3] = &XEXP (pat, 1)); + break; + + case 154: + case 148: + case 142: + ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1)); + recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 1), 0); + recog_dup_num[0] = 2; + break; + + case 136: + case 133: + ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); + ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (XEXP (pat, 1), 0, 0), 0)); + break; + + case 135: + case 134: + case 132: + case 131: + ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); + ro[1] = *(ro_loc[1] = &XVECEXP (XEXP (pat, 1), 0, 0)); + break; + + case 130: + case 127: + case 124: + ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (pat, 1), 0), 0)); + break; + + case 108: + case 107: + case 106: + case 105: + ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1)); + ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 1), 0)); + recog_dup_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 0); + recog_dup_num[0] = 1; + recog_dup_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 1); + recog_dup_num[1] = 2; + break; + + case 98: + ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (pat, 1), 0), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XEXP (XEXP (pat, 1), 1), 0)); + break; + + case 235: + case 163: + case 162: + case 161: + case 160: + case 159: + case 158: + case 157: + case 156: + case 155: + case 153: + case 151: + case 150: + case 149: + case 147: + case 145: + case 144: + case 143: + case 141: + case 117: + case 116: + case 115: + case 114: + case 113: + case 112: + case 111: + case 110: + case 109: + case 102: + case 101: + case 97: + case 96: + case 95: + case 94: + case 91: + case 90: + case 89: + case 88: + case 84: + case 83: + case 82: + case 81: + ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 1)); + break; + + case 72: + case 71: + ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0)); + ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 2), 0)); + ro[4] = *(ro_loc[4] = &XEXP (XVECEXP (pat, 0, 3), 0)); + break; + + case 68: + case 67: + ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 2), 0)); + ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 3), 0)); + ro[4] = *(ro_loc[4] = &XEXP (XVECEXP (pat, 0, 4), 0)); + recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 1), 0); + recog_dup_num[0] = 1; + break; + + case 62: + ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0)); + break; + + case 139: + case 138: + case 137: + case 129: + case 128: + case 126: + case 125: + case 123: + case 122: + case 121: + case 120: + case 119: + case 118: + case 80: + case 79: + case 78: + case 77: + case 60: + case 59: + case 58: + case 57: + case 56: + case 55: + case 54: + case 53: + case 52: + ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 0)); + break; + + case 48: + ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (pat, 0, 0), 1)); + recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 1), 0); + recog_dup_num[0] = 1; + recog_dup_loc[1] = &XEXP (XVECEXP (pat, 0, 1), 1); + recog_dup_num[1] = 0; + break; + + case 230: + case 44: + case 41: + ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (pat, 1)); + break; + + case 229: + case 85: + case 51: + case 50: + case 49: + case 47: + case 46: + case 45: + case 43: + case 42: + case 40: + case 39: + case 38: + case 36: + case 35: + ro[0] = *(ro_loc[0] = &XEXP (pat, 0)); + ro[1] = *(ro_loc[1] = &XEXP (pat, 1)); + break; + + case 25: + case 21: + ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1)); + ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0)); + break; + + case 24: + case 20: + case 18: + ro[0] = *(ro_loc[0] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1)); + ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 0), 1)); + ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 1), 0)); + break; + + case 23: + case 19: + case 17: + ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1), 0)); + ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 0), 1)); + ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 1), 0)); + break; + + case 22: + case 16: + ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1)); + ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 0), 1)); + ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 1), 0)); + break; + + case 34: + case 33: + case 32: + case 14: + case 12: + case 10: + ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 1), 0)); + ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 1)); + break; + + case 8: + case 6: + ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 1)); + ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (pat, 0, 1), 0)); + break; + + case 221: + case 4: + case 2: + case 0: + ro[0] = *(ro_loc[0] = &XEXP (pat, 1)); + break; + + default: + abort (); + } +} diff --git a/gnu/usr.bin/cc/lib/insn-flags.h b/gnu/usr.bin/cc/lib/insn-flags.h new file mode 100644 index 0000000000..4ffa0df475 --- /dev/null +++ b/gnu/usr.bin/cc/lib/insn-flags.h @@ -0,0 +1,522 @@ +/* Generated automatically by the program `genflags' +from the machine description file `md'. */ + +#define HAVE_tstsi_1 1 +#define HAVE_tstsi 1 +#define HAVE_tsthi_1 1 +#define HAVE_tsthi 1 +#define HAVE_tstqi_1 1 +#define HAVE_tstqi 1 +#define HAVE_tstsf_cc (TARGET_80387 && ! TARGET_IEEE_FP) +#define HAVE_tstsf (TARGET_80387 && ! TARGET_IEEE_FP) +#define HAVE_tstdf_cc (TARGET_80387 && ! TARGET_IEEE_FP) +#define HAVE_tstdf (TARGET_80387 && ! TARGET_IEEE_FP) +#define HAVE_cmpsi_1 (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) +#define HAVE_cmpsi 1 +#define HAVE_cmphi_1 (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) +#define HAVE_cmphi 1 +#define HAVE_cmpqi_1 (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) +#define HAVE_cmpqi 1 +#define HAVE_cmpsf_cc_1 (TARGET_80387 \ + && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)) +#define HAVE_cmpdf (TARGET_80387) +#define HAVE_cmpsf (TARGET_80387) +#define HAVE_cmpdf_cc (TARGET_80387) +#define HAVE_cmpdf_ccfpeq (TARGET_80387) +#define HAVE_cmpsf_cc (TARGET_80387) +#define HAVE_cmpsf_ccfpeq (TARGET_80387) +#define HAVE_movsi 1 +#define HAVE_movhi 1 +#define HAVE_movstricthi 1 +#define HAVE_movqi 1 +#define HAVE_movstrictqi 1 +#define HAVE_movsf 1 +#define HAVE_swapdf 1 +#define HAVE_movdf 1 +#define HAVE_movdi 1 +#define HAVE_zero_extendhisi2 1 +#define HAVE_zero_extendqihi2 1 +#define HAVE_zero_extendqisi2 1 +#define HAVE_zero_extendsidi2 1 +#define HAVE_extendsidi2 1 +#define HAVE_extendhisi2 1 +#define HAVE_extendqihi2 1 +#define HAVE_extendqisi2 1 +#define HAVE_extendsfdf2 (TARGET_80387) +#define HAVE_truncdfsf2 (TARGET_80387) +#define HAVE_fixuns_truncdfsi2 (TARGET_80387) +#define HAVE_fixuns_truncsfsi2 (TARGET_80387) +#define HAVE_fix_truncdfdi2 (TARGET_80387) +#define HAVE_fix_truncsfdi2 (TARGET_80387) +#define HAVE_fix_truncdfsi2 (TARGET_80387) +#define HAVE_fix_truncsfsi2 (TARGET_80387) +#define HAVE_floatsisf2 (TARGET_80387) +#define HAVE_floatdisf2 (TARGET_80387) +#define HAVE_floatsidf2 (TARGET_80387) +#define HAVE_floatdidf2 (TARGET_80387) +#define HAVE_adddi3 1 +#define HAVE_addsi3 1 +#define HAVE_addhi3 1 +#define HAVE_addqi3 1 +#define HAVE_adddf3 (TARGET_80387) +#define HAVE_addsf3 (TARGET_80387) +#define HAVE_subdi3 1 +#define HAVE_subsi3 1 +#define HAVE_subhi3 1 +#define HAVE_subqi3 1 +#define HAVE_subdf3 (TARGET_80387) +#define HAVE_subsf3 (TARGET_80387) +#define HAVE_mulhi3 1 +#define HAVE_mulsi3 1 +#define HAVE_muldf3 (TARGET_80387) +#define HAVE_mulsf3 (TARGET_80387) +#define HAVE_divqi3 1 +#define HAVE_udivqi3 1 +#define HAVE_divdf3 (TARGET_80387) +#define HAVE_divsf3 (TARGET_80387) +#define HAVE_divmodsi4 1 +#define HAVE_divmodhi4 1 +#define HAVE_udivmodsi4 1 +#define HAVE_udivmodhi4 1 +#define HAVE_andsi3 1 +#define HAVE_andhi3 1 +#define HAVE_andqi3 1 +#define HAVE_iorsi3 1 +#define HAVE_iorhi3 1 +#define HAVE_iorqi3 1 +#define HAVE_xorsi3 1 +#define HAVE_xorhi3 1 +#define HAVE_xorqi3 1 +#define HAVE_negdi2 1 +#define HAVE_negsi2 1 +#define HAVE_neghi2 1 +#define HAVE_negqi2 1 +#define HAVE_negsf2 (TARGET_80387) +#define HAVE_negdf2 (TARGET_80387) +#define HAVE_abssf2 (TARGET_80387) +#define HAVE_absdf2 (TARGET_80387) +/* XXX missing emulator instructions. + Use NOFPU to prevent them being generated on non-fpu machines. +*/ +#ifdef NOFPU +#define HAVE_sqrtdf2 0 +#define HAVE_sqrtsf2 0 +#define HAVE_sindf2 0 +#define HAVE_sinsf2 0 +#define HAVE_cosdf2 0 +#define HAVE_cossf2 0 +#else /* Have a fpu */ +#define HAVE_sqrtdf2 (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) +#define HAVE_sqrtsf2 (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) +#define HAVE_sindf2 (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) +#define HAVE_sinsf2 (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) +#define HAVE_cosdf2 (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) +#define HAVE_cossf2 (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) +#endif +#define HAVE_one_cmplsi2 1 +#define HAVE_one_cmplhi2 1 +#define HAVE_one_cmplqi2 1 +#define HAVE_ashldi3 1 +#define HAVE_ashldi3_const_int 1 +#define HAVE_ashldi3_non_const_int 1 +#define HAVE_ashlsi3 1 +#define HAVE_ashlhi3 1 +#define HAVE_ashlqi3 1 +#define HAVE_ashrdi3 1 +#define HAVE_ashrdi3_const_int 1 +#define HAVE_ashrdi3_non_const_int 1 +#define HAVE_ashrsi3 1 +#define HAVE_ashrhi3 1 +#define HAVE_ashrqi3 1 +#define HAVE_lshrdi3 1 +#define HAVE_lshrdi3_const_int 1 +#define HAVE_lshrdi3_non_const_int 1 +#define HAVE_lshrsi3 1 +#define HAVE_lshrhi3 1 +#define HAVE_lshrqi3 1 +#define HAVE_rotlsi3 1 +#define HAVE_rotlhi3 1 +#define HAVE_rotlqi3 1 +#define HAVE_rotrsi3 1 +#define HAVE_rotrhi3 1 +#define HAVE_rotrqi3 1 +#define HAVE_seq 1 +#define HAVE_sne 1 +#define HAVE_sgt 1 +#define HAVE_sgtu 1 +#define HAVE_slt 1 +#define HAVE_sltu 1 +#define HAVE_sge 1 +#define HAVE_sgeu 1 +#define HAVE_sle 1 +#define HAVE_sleu 1 +#define HAVE_beq 1 +#define HAVE_bne 1 +#define HAVE_bgt 1 +#define HAVE_bgtu 1 +#define HAVE_blt 1 +#define HAVE_bltu 1 +#define HAVE_bge 1 +#define HAVE_bgeu 1 +#define HAVE_ble 1 +#define HAVE_bleu 1 +#define HAVE_jump 1 +#define HAVE_indirect_jump 1 +#define HAVE_casesi (flag_pic) +#define HAVE_tablejump 1 +#define HAVE_call_pop 1 +#define HAVE_call 1 +#define HAVE_call_value_pop 1 +#define HAVE_call_value 1 +#define HAVE_untyped_call 1 +#define HAVE_untyped_return 1 +#define HAVE_update_return 1 +#define HAVE_return (simple_386_epilogue ()) +#define HAVE_nop 1 +#define HAVE_movstrsi 1 +#define HAVE_cmpstrsi 1 +#define HAVE_ffssi2 1 +#define HAVE_ffshi2 1 +#define HAVE_strlensi 1 + +#ifndef NO_MD_PROTOTYPES +extern rtx gen_tstsi_1 PROTO((rtx)); +extern rtx gen_tstsi PROTO((rtx)); +extern rtx gen_tsthi_1 PROTO((rtx)); +extern rtx gen_tsthi PROTO((rtx)); +extern rtx gen_tstqi_1 PROTO((rtx)); +extern rtx gen_tstqi PROTO((rtx)); +extern rtx gen_tstsf_cc PROTO((rtx)); +extern rtx gen_tstsf PROTO((rtx)); +extern rtx gen_tstdf_cc PROTO((rtx)); +extern rtx gen_tstdf PROTO((rtx)); +extern rtx gen_cmpsi_1 PROTO((rtx, rtx)); +extern rtx gen_cmpsi PROTO((rtx, rtx)); +extern rtx gen_cmphi_1 PROTO((rtx, rtx)); +extern rtx gen_cmphi PROTO((rtx, rtx)); +extern rtx gen_cmpqi_1 PROTO((rtx, rtx)); +extern rtx gen_cmpqi PROTO((rtx, rtx)); +extern rtx gen_cmpsf_cc_1 PROTO((rtx, rtx, rtx)); +extern rtx gen_cmpdf PROTO((rtx, rtx)); +extern rtx gen_cmpsf PROTO((rtx, rtx)); +extern rtx gen_cmpdf_cc PROTO((rtx, rtx)); +extern rtx gen_cmpdf_ccfpeq PROTO((rtx, rtx)); +extern rtx gen_cmpsf_cc PROTO((rtx, rtx)); +extern rtx gen_cmpsf_ccfpeq PROTO((rtx, rtx)); +extern rtx gen_movsi PROTO((rtx, rtx)); +extern rtx gen_movhi PROTO((rtx, rtx)); +extern rtx gen_movstricthi PROTO((rtx, rtx)); +extern rtx gen_movqi PROTO((rtx, rtx)); +extern rtx gen_movstrictqi PROTO((rtx, rtx)); +extern rtx gen_movsf PROTO((rtx, rtx)); +extern rtx gen_swapdf PROTO((rtx, rtx)); +extern rtx gen_movdf PROTO((rtx, rtx)); +extern rtx gen_movdi PROTO((rtx, rtx)); +extern rtx gen_zero_extendhisi2 PROTO((rtx, rtx)); +extern rtx gen_zero_extendqihi2 PROTO((rtx, rtx)); +extern rtx gen_zero_extendqisi2 PROTO((rtx, rtx)); +extern rtx gen_zero_extendsidi2 PROTO((rtx, rtx)); +extern rtx gen_extendsidi2 PROTO((rtx, rtx)); +extern rtx gen_extendhisi2 PROTO((rtx, rtx)); +extern rtx gen_extendqihi2 PROTO((rtx, rtx)); +extern rtx gen_extendqisi2 PROTO((rtx, rtx)); +extern rtx gen_extendsfdf2 PROTO((rtx, rtx)); +extern rtx gen_truncdfsf2 PROTO((rtx, rtx)); +extern rtx gen_fixuns_truncdfsi2 PROTO((rtx, rtx)); +extern rtx gen_fixuns_truncsfsi2 PROTO((rtx, rtx)); +extern rtx gen_fix_truncdfdi2 PROTO((rtx, rtx)); +extern rtx gen_fix_truncsfdi2 PROTO((rtx, rtx)); +extern rtx gen_fix_truncdfsi2 PROTO((rtx, rtx)); +extern rtx gen_fix_truncsfsi2 PROTO((rtx, rtx)); +extern rtx gen_floatsisf2 PROTO((rtx, rtx)); +extern rtx gen_floatdisf2 PROTO((rtx, rtx)); +extern rtx gen_floatsidf2 PROTO((rtx, rtx)); +extern rtx gen_floatdidf2 PROTO((rtx, rtx)); +extern rtx gen_adddi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_addsi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_addhi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_addqi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_adddf3 PROTO((rtx, rtx, rtx)); +extern rtx gen_addsf3 PROTO((rtx, rtx, rtx)); +extern rtx gen_subdi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_subsi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_subhi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_subqi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_subdf3 PROTO((rtx, rtx, rtx)); +extern rtx gen_subsf3 PROTO((rtx, rtx, rtx)); +extern rtx gen_mulhi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_mulsi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_muldf3 PROTO((rtx, rtx, rtx)); +extern rtx gen_mulsf3 PROTO((rtx, rtx, rtx)); +extern rtx gen_divqi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_udivqi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_divdf3 PROTO((rtx, rtx, rtx)); +extern rtx gen_divsf3 PROTO((rtx, rtx, rtx)); +extern rtx gen_divmodsi4 PROTO((rtx, rtx, rtx, rtx)); +extern rtx gen_divmodhi4 PROTO((rtx, rtx, rtx, rtx)); +extern rtx gen_udivmodsi4 PROTO((rtx, rtx, rtx, rtx)); +extern rtx gen_udivmodhi4 PROTO((rtx, rtx, rtx, rtx)); +extern rtx gen_andsi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_andhi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_andqi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_iorsi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_iorhi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_iorqi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_xorsi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_xorhi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_xorqi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_negdi2 PROTO((rtx, rtx)); +extern rtx gen_negsi2 PROTO((rtx, rtx)); +extern rtx gen_neghi2 PROTO((rtx, rtx)); +extern rtx gen_negqi2 PROTO((rtx, rtx)); +extern rtx gen_negsf2 PROTO((rtx, rtx)); +extern rtx gen_negdf2 PROTO((rtx, rtx)); +extern rtx gen_abssf2 PROTO((rtx, rtx)); +extern rtx gen_absdf2 PROTO((rtx, rtx)); +extern rtx gen_sqrtsf2 PROTO((rtx, rtx)); +extern rtx gen_sqrtdf2 PROTO((rtx, rtx)); +extern rtx gen_sindf2 PROTO((rtx, rtx)); +extern rtx gen_sinsf2 PROTO((rtx, rtx)); +extern rtx gen_cosdf2 PROTO((rtx, rtx)); +extern rtx gen_cossf2 PROTO((rtx, rtx)); +extern rtx gen_one_cmplsi2 PROTO((rtx, rtx)); +extern rtx gen_one_cmplhi2 PROTO((rtx, rtx)); +extern rtx gen_one_cmplqi2 PROTO((rtx, rtx)); +extern rtx gen_ashldi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_ashldi3_const_int PROTO((rtx, rtx, rtx)); +extern rtx gen_ashldi3_non_const_int PROTO((rtx, rtx, rtx)); +extern rtx gen_ashlsi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_ashlhi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_ashlqi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_ashrdi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_ashrdi3_const_int PROTO((rtx, rtx, rtx)); +extern rtx gen_ashrdi3_non_const_int PROTO((rtx, rtx, rtx)); +extern rtx gen_ashrsi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_ashrhi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_ashrqi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_lshrdi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_lshrdi3_const_int PROTO((rtx, rtx, rtx)); +extern rtx gen_lshrdi3_non_const_int PROTO((rtx, rtx, rtx)); +extern rtx gen_lshrsi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_lshrhi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_lshrqi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_rotlsi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_rotlhi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_rotlqi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_rotrsi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_rotrhi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_rotrqi3 PROTO((rtx, rtx, rtx)); +extern rtx gen_seq PROTO((rtx)); +extern rtx gen_sne PROTO((rtx)); +extern rtx gen_sgt PROTO((rtx)); +extern rtx gen_sgtu PROTO((rtx)); +extern rtx gen_slt PROTO((rtx)); +extern rtx gen_sltu PROTO((rtx)); +extern rtx gen_sge PROTO((rtx)); +extern rtx gen_sgeu PROTO((rtx)); +extern rtx gen_sle PROTO((rtx)); +extern rtx gen_sleu PROTO((rtx)); +extern rtx gen_beq PROTO((rtx)); +extern rtx gen_bne PROTO((rtx)); +extern rtx gen_bgt PROTO((rtx)); +extern rtx gen_bgtu PROTO((rtx)); +extern rtx gen_blt PROTO((rtx)); +extern rtx gen_bltu PROTO((rtx)); +extern rtx gen_bge PROTO((rtx)); +extern rtx gen_bgeu PROTO((rtx)); +extern rtx gen_ble PROTO((rtx)); +extern rtx gen_bleu PROTO((rtx)); +extern rtx gen_jump PROTO((rtx)); +extern rtx gen_indirect_jump PROTO((rtx)); +extern rtx gen_casesi PROTO((rtx, rtx, rtx, rtx, rtx)); +extern rtx gen_tablejump PROTO((rtx, rtx)); +extern rtx gen_untyped_call PROTO((rtx, rtx, rtx)); +extern rtx gen_untyped_return PROTO((rtx, rtx)); +extern rtx gen_update_return PROTO((rtx)); +extern rtx gen_return PROTO((void)); +extern rtx gen_nop PROTO((void)); +extern rtx gen_movstrsi PROTO((rtx, rtx, rtx, rtx)); +extern rtx gen_cmpstrsi PROTO((rtx, rtx, rtx, rtx, rtx)); +extern rtx gen_ffssi2 PROTO((rtx, rtx)); +extern rtx gen_ffshi2 PROTO((rtx, rtx)); +extern rtx gen_strlensi PROTO((rtx, rtx, rtx, rtx)); + +#ifdef MD_CALL_PROTOTYPES +extern rtx gen_call_pop PROTO((rtx, rtx, rtx)); +extern rtx gen_call PROTO((rtx, rtx)); +extern rtx gen_call_value_pop PROTO((rtx, rtx, rtx, rtx)); +extern rtx gen_call_value PROTO((rtx, rtx, rtx)); + +#else /* !MD_CALL_PROTOTYPES */ +extern rtx gen_call_pop (); +extern rtx gen_call (); +extern rtx gen_call_value_pop (); +extern rtx gen_call_value (); +#endif /* !MD_CALL_PROTOTYPES */ + +#else /* NO_MD_PROTOTYPES */ +extern rtx gen_tstsi_1 (); +extern rtx gen_tstsi (); +extern rtx gen_tsthi_1 (); +extern rtx gen_tsthi (); +extern rtx gen_tstqi_1 (); +extern rtx gen_tstqi (); +extern rtx gen_tstsf_cc (); +extern rtx gen_tstsf (); +extern rtx gen_tstdf_cc (); +extern rtx gen_tstdf (); +extern rtx gen_cmpsi_1 (); +extern rtx gen_cmpsi (); +extern rtx gen_cmphi_1 (); +extern rtx gen_cmphi (); +extern rtx gen_cmpqi_1 (); +extern rtx gen_cmpqi (); +extern rtx gen_cmpsf_cc_1 (); +extern rtx gen_cmpdf (); +extern rtx gen_cmpsf (); +extern rtx gen_cmpdf_cc (); +extern rtx gen_cmpdf_ccfpeq (); +extern rtx gen_cmpsf_cc (); +extern rtx gen_cmpsf_ccfpeq (); +extern rtx gen_movsi (); +extern rtx gen_movhi (); +extern rtx gen_movstricthi (); +extern rtx gen_movqi (); +extern rtx gen_movstrictqi (); +extern rtx gen_movsf (); +extern rtx gen_swapdf (); +extern rtx gen_movdf (); +extern rtx gen_movdi (); +extern rtx gen_zero_extendhisi2 (); +extern rtx gen_zero_extendqihi2 (); +extern rtx gen_zero_extendqisi2 (); +extern rtx gen_zero_extendsidi2 (); +extern rtx gen_extendsidi2 (); +extern rtx gen_extendhisi2 (); +extern rtx gen_extendqihi2 (); +extern rtx gen_extendqisi2 (); +extern rtx gen_extendsfdf2 (); +extern rtx gen_truncdfsf2 (); +extern rtx gen_fixuns_truncdfsi2 (); +extern rtx gen_fixuns_truncsfsi2 (); +extern rtx gen_fix_truncdfdi2 (); +extern rtx gen_fix_truncsfdi2 (); +extern rtx gen_fix_truncdfsi2 (); +extern rtx gen_fix_truncsfsi2 (); +extern rtx gen_floatsisf2 (); +extern rtx gen_floatdisf2 (); +extern rtx gen_floatsidf2 (); +extern rtx gen_floatdidf2 (); +extern rtx gen_adddi3 (); +extern rtx gen_addsi3 (); +extern rtx gen_addhi3 (); +extern rtx gen_addqi3 (); +extern rtx gen_adddf3 (); +extern rtx gen_addsf3 (); +extern rtx gen_subdi3 (); +extern rtx gen_subsi3 (); +extern rtx gen_subhi3 (); +extern rtx gen_subqi3 (); +extern rtx gen_subdf3 (); +extern rtx gen_subsf3 (); +extern rtx gen_mulhi3 (); +extern rtx gen_mulsi3 (); +extern rtx gen_muldf3 (); +extern rtx gen_mulsf3 (); +extern rtx gen_divqi3 (); +extern rtx gen_udivqi3 (); +extern rtx gen_divdf3 (); +extern rtx gen_divsf3 (); +extern rtx gen_divmodsi4 (); +extern rtx gen_divmodhi4 (); +extern rtx gen_udivmodsi4 (); +extern rtx gen_udivmodhi4 (); +extern rtx gen_andsi3 (); +extern rtx gen_andhi3 (); +extern rtx gen_andqi3 (); +extern rtx gen_iorsi3 (); +extern rtx gen_iorhi3 (); +extern rtx gen_iorqi3 (); +extern rtx gen_xorsi3 (); +extern rtx gen_xorhi3 (); +extern rtx gen_xorqi3 (); +extern rtx gen_negdi2 (); +extern rtx gen_negsi2 (); +extern rtx gen_neghi2 (); +extern rtx gen_negqi2 (); +extern rtx gen_negsf2 (); +extern rtx gen_negdf2 (); +extern rtx gen_abssf2 (); +extern rtx gen_absdf2 (); +extern rtx gen_sqrtsf2 (); +extern rtx gen_sqrtdf2 (); +extern rtx gen_sindf2 (); +extern rtx gen_sinsf2 (); +extern rtx gen_cosdf2 (); +extern rtx gen_cossf2 (); +extern rtx gen_one_cmplsi2 (); +extern rtx gen_one_cmplhi2 (); +extern rtx gen_one_cmplqi2 (); +extern rtx gen_ashldi3 (); +extern rtx gen_ashldi3_const_int (); +extern rtx gen_ashldi3_non_const_int (); +extern rtx gen_ashlsi3 (); +extern rtx gen_ashlhi3 (); +extern rtx gen_ashlqi3 (); +extern rtx gen_ashrdi3 (); +extern rtx gen_ashrdi3_const_int (); +extern rtx gen_ashrdi3_non_const_int (); +extern rtx gen_ashrsi3 (); +extern rtx gen_ashrhi3 (); +extern rtx gen_ashrqi3 (); +extern rtx gen_lshrdi3 (); +extern rtx gen_lshrdi3_const_int (); +extern rtx gen_lshrdi3_non_const_int (); +extern rtx gen_lshrsi3 (); +extern rtx gen_lshrhi3 (); +extern rtx gen_lshrqi3 (); +extern rtx gen_rotlsi3 (); +extern rtx gen_rotlhi3 (); +extern rtx gen_rotlqi3 (); +extern rtx gen_rotrsi3 (); +extern rtx gen_rotrhi3 (); +extern rtx gen_rotrqi3 (); +extern rtx gen_seq (); +extern rtx gen_sne (); +extern rtx gen_sgt (); +extern rtx gen_sgtu (); +extern rtx gen_slt (); +extern rtx gen_sltu (); +extern rtx gen_sge (); +extern rtx gen_sgeu (); +extern rtx gen_sle (); +extern rtx gen_sleu (); +extern rtx gen_beq (); +extern rtx gen_bne (); +extern rtx gen_bgt (); +extern rtx gen_bgtu (); +extern rtx gen_blt (); +extern rtx gen_bltu (); +extern rtx gen_bge (); +extern rtx gen_bgeu (); +extern rtx gen_ble (); +extern rtx gen_bleu (); +extern rtx gen_jump (); +extern rtx gen_indirect_jump (); +extern rtx gen_casesi (); +extern rtx gen_tablejump (); +extern rtx gen_untyped_call (); +extern rtx gen_untyped_return (); +extern rtx gen_update_return (); +extern rtx gen_return (); +extern rtx gen_nop (); +extern rtx gen_movstrsi (); +extern rtx gen_cmpstrsi (); +extern rtx gen_ffssi2 (); +extern rtx gen_ffshi2 (); +extern rtx gen_strlensi (); +extern rtx gen_call_pop (); +extern rtx gen_call (); +extern rtx gen_call_value_pop (); +extern rtx gen_call_value (); +#endif /* NO_MD_PROTOTYPES */ diff --git a/gnu/usr.bin/cc/lib/insn-opinit.c b/gnu/usr.bin/cc/lib/insn-opinit.c new file mode 100644 index 0000000000..7959ae5fb1 --- /dev/null +++ b/gnu/usr.bin/cc/lib/insn-opinit.c @@ -0,0 +1,179 @@ +/* Generated automatically by the program `genopinit' +from the machine description file `md'. */ + +#include "config.h" +#include "rtl.h" +#include "flags.h" +#include "insn-flags.h" +#include "insn-codes.h" +#include "insn-config.h" +#include "recog.h" +#include "expr.h" +#include "reload.h" + +void +init_all_optabs () +{ + tst_optab->handlers[(int) SImode].insn_code = CODE_FOR_tstsi; + tst_optab->handlers[(int) HImode].insn_code = CODE_FOR_tsthi; + tst_optab->handlers[(int) QImode].insn_code = CODE_FOR_tstqi; + if (HAVE_tstsf) + tst_optab->handlers[(int) SFmode].insn_code = CODE_FOR_tstsf; + if (HAVE_tstdf) + tst_optab->handlers[(int) DFmode].insn_code = CODE_FOR_tstdf; + cmp_optab->handlers[(int) SImode].insn_code = CODE_FOR_cmpsi; + cmp_optab->handlers[(int) HImode].insn_code = CODE_FOR_cmphi; + cmp_optab->handlers[(int) QImode].insn_code = CODE_FOR_cmpqi; + if (HAVE_cmpdf) + cmp_optab->handlers[(int) DFmode].insn_code = CODE_FOR_cmpdf; + if (HAVE_cmpsf) + cmp_optab->handlers[(int) SFmode].insn_code = CODE_FOR_cmpsf; + mov_optab->handlers[(int) SImode].insn_code = CODE_FOR_movsi; + mov_optab->handlers[(int) HImode].insn_code = CODE_FOR_movhi; + movstrict_optab->handlers[(int) HImode].insn_code = CODE_FOR_movstricthi; + mov_optab->handlers[(int) QImode].insn_code = CODE_FOR_movqi; + movstrict_optab->handlers[(int) QImode].insn_code = CODE_FOR_movstrictqi; + mov_optab->handlers[(int) SFmode].insn_code = CODE_FOR_movsf; + mov_optab->handlers[(int) DFmode].insn_code = CODE_FOR_movdf; + mov_optab->handlers[(int) DImode].insn_code = CODE_FOR_movdi; + extendtab[(int) SImode][(int) HImode][1] = CODE_FOR_zero_extendhisi2; + extendtab[(int) HImode][(int) QImode][1] = CODE_FOR_zero_extendqihi2; + extendtab[(int) SImode][(int) QImode][1] = CODE_FOR_zero_extendqisi2; + extendtab[(int) DImode][(int) SImode][1] = CODE_FOR_zero_extendsidi2; + extendtab[(int) DImode][(int) SImode][0] = CODE_FOR_extendsidi2; + extendtab[(int) SImode][(int) HImode][0] = CODE_FOR_extendhisi2; + extendtab[(int) HImode][(int) QImode][0] = CODE_FOR_extendqihi2; + extendtab[(int) SImode][(int) QImode][0] = CODE_FOR_extendqisi2; + if (HAVE_extendsfdf2) + extendtab[(int) DFmode][(int) SFmode][0] = CODE_FOR_extendsfdf2; + if (HAVE_fixuns_truncdfsi2) + fixtrunctab[(int) DFmode][(int) SImode][1] = CODE_FOR_fixuns_truncdfsi2; + if (HAVE_fixuns_truncsfsi2) + fixtrunctab[(int) SFmode][(int) SImode][1] = CODE_FOR_fixuns_truncsfsi2; + if (HAVE_fix_truncdfdi2) + fixtrunctab[(int) DFmode][(int) DImode][0] = CODE_FOR_fix_truncdfdi2; + if (HAVE_fix_truncsfdi2) + fixtrunctab[(int) SFmode][(int) DImode][0] = CODE_FOR_fix_truncsfdi2; + if (HAVE_fix_truncdfsi2) + fixtrunctab[(int) DFmode][(int) SImode][0] = CODE_FOR_fix_truncdfsi2; + if (HAVE_fix_truncsfsi2) + fixtrunctab[(int) SFmode][(int) SImode][0] = CODE_FOR_fix_truncsfsi2; + if (HAVE_floatsisf2) + floattab[(int) SFmode][(int) SImode][0] = CODE_FOR_floatsisf2; + if (HAVE_floatdisf2) + floattab[(int) SFmode][(int) DImode][0] = CODE_FOR_floatdisf2; + if (HAVE_floatsidf2) + floattab[(int) DFmode][(int) SImode][0] = CODE_FOR_floatsidf2; + if (HAVE_floatdidf2) + floattab[(int) DFmode][(int) DImode][0] = CODE_FOR_floatdidf2; + add_optab->handlers[(int) DImode].insn_code = CODE_FOR_adddi3; + add_optab->handlers[(int) SImode].insn_code = CODE_FOR_addsi3; + add_optab->handlers[(int) HImode].insn_code = CODE_FOR_addhi3; + add_optab->handlers[(int) QImode].insn_code = CODE_FOR_addqi3; + if (HAVE_adddf3) + add_optab->handlers[(int) DFmode].insn_code = CODE_FOR_adddf3; + if (HAVE_addsf3) + add_optab->handlers[(int) SFmode].insn_code = CODE_FOR_addsf3; + sub_optab->handlers[(int) DImode].insn_code = CODE_FOR_subdi3; + sub_optab->handlers[(int) SImode].insn_code = CODE_FOR_subsi3; + sub_optab->handlers[(int) HImode].insn_code = CODE_FOR_subhi3; + sub_optab->handlers[(int) QImode].insn_code = CODE_FOR_subqi3; + if (HAVE_subdf3) + sub_optab->handlers[(int) DFmode].insn_code = CODE_FOR_subdf3; + if (HAVE_subsf3) + sub_optab->handlers[(int) SFmode].insn_code = CODE_FOR_subsf3; + smul_optab->handlers[(int) HImode].insn_code = CODE_FOR_mulhi3; + smul_optab->handlers[(int) SImode].insn_code = CODE_FOR_mulsi3; + if (HAVE_muldf3) + smul_optab->handlers[(int) DFmode].insn_code = CODE_FOR_muldf3; + if (HAVE_mulsf3) + smul_optab->handlers[(int) SFmode].insn_code = CODE_FOR_mulsf3; + sdiv_optab->handlers[(int) QImode].insn_code = CODE_FOR_divqi3; + udiv_optab->handlers[(int) QImode].insn_code = CODE_FOR_udivqi3; + if (HAVE_divdf3) + flodiv_optab->handlers[(int) DFmode].insn_code = CODE_FOR_divdf3; + if (HAVE_divsf3) + flodiv_optab->handlers[(int) SFmode].insn_code = CODE_FOR_divsf3; + sdivmod_optab->handlers[(int) SImode].insn_code = CODE_FOR_divmodsi4; + sdivmod_optab->handlers[(int) HImode].insn_code = CODE_FOR_divmodhi4; + udivmod_optab->handlers[(int) SImode].insn_code = CODE_FOR_udivmodsi4; + udivmod_optab->handlers[(int) HImode].insn_code = CODE_FOR_udivmodhi4; + and_optab->handlers[(int) SImode].insn_code = CODE_FOR_andsi3; + and_optab->handlers[(int) HImode].insn_code = CODE_FOR_andhi3; + and_optab->handlers[(int) QImode].insn_code = CODE_FOR_andqi3; + ior_optab->handlers[(int) SImode].insn_code = CODE_FOR_iorsi3; + ior_optab->handlers[(int) HImode].insn_code = CODE_FOR_iorhi3; + ior_optab->handlers[(int) QImode].insn_code = CODE_FOR_iorqi3; + xor_optab->handlers[(int) SImode].insn_code = CODE_FOR_xorsi3; + xor_optab->handlers[(int) HImode].insn_code = CODE_FOR_xorhi3; + xor_optab->handlers[(int) QImode].insn_code = CODE_FOR_xorqi3; + neg_optab->handlers[(int) DImode].insn_code = CODE_FOR_negdi2; + neg_optab->handlers[(int) SImode].insn_code = CODE_FOR_negsi2; + neg_optab->handlers[(int) HImode].insn_code = CODE_FOR_neghi2; + neg_optab->handlers[(int) QImode].insn_code = CODE_FOR_negqi2; + if (HAVE_negsf2) + neg_optab->handlers[(int) SFmode].insn_code = CODE_FOR_negsf2; + if (HAVE_negdf2) + neg_optab->handlers[(int) DFmode].insn_code = CODE_FOR_negdf2; + if (HAVE_abssf2) + abs_optab->handlers[(int) SFmode].insn_code = CODE_FOR_abssf2; + if (HAVE_absdf2) + abs_optab->handlers[(int) DFmode].insn_code = CODE_FOR_absdf2; + if (HAVE_sqrtsf2) + sqrt_optab->handlers[(int) SFmode].insn_code = CODE_FOR_sqrtsf2; + if (HAVE_sqrtdf2) + sqrt_optab->handlers[(int) DFmode].insn_code = CODE_FOR_sqrtdf2; + if (HAVE_sindf2) + sin_optab->handlers[(int) DFmode].insn_code = CODE_FOR_sindf2; + if (HAVE_sinsf2) + sin_optab->handlers[(int) SFmode].insn_code = CODE_FOR_sinsf2; + if (HAVE_cosdf2) + cos_optab->handlers[(int) DFmode].insn_code = CODE_FOR_cosdf2; + if (HAVE_cossf2) + cos_optab->handlers[(int) SFmode].insn_code = CODE_FOR_cossf2; + one_cmpl_optab->handlers[(int) SImode].insn_code = CODE_FOR_one_cmplsi2; + one_cmpl_optab->handlers[(int) HImode].insn_code = CODE_FOR_one_cmplhi2; + one_cmpl_optab->handlers[(int) QImode].insn_code = CODE_FOR_one_cmplqi2; + ashl_optab->handlers[(int) DImode].insn_code = CODE_FOR_ashldi3; + ashl_optab->handlers[(int) SImode].insn_code = CODE_FOR_ashlsi3; + ashl_optab->handlers[(int) HImode].insn_code = CODE_FOR_ashlhi3; + ashl_optab->handlers[(int) QImode].insn_code = CODE_FOR_ashlqi3; + ashr_optab->handlers[(int) DImode].insn_code = CODE_FOR_ashrdi3; + ashr_optab->handlers[(int) SImode].insn_code = CODE_FOR_ashrsi3; + ashr_optab->handlers[(int) HImode].insn_code = CODE_FOR_ashrhi3; + ashr_optab->handlers[(int) QImode].insn_code = CODE_FOR_ashrqi3; + lshr_optab->handlers[(int) DImode].insn_code = CODE_FOR_lshrdi3; + lshr_optab->handlers[(int) SImode].insn_code = CODE_FOR_lshrsi3; + lshr_optab->handlers[(int) HImode].insn_code = CODE_FOR_lshrhi3; + lshr_optab->handlers[(int) QImode].insn_code = CODE_FOR_lshrqi3; + rotl_optab->handlers[(int) SImode].insn_code = CODE_FOR_rotlsi3; + rotl_optab->handlers[(int) HImode].insn_code = CODE_FOR_rotlhi3; + rotl_optab->handlers[(int) QImode].insn_code = CODE_FOR_rotlqi3; + rotr_optab->handlers[(int) SImode].insn_code = CODE_FOR_rotrsi3; + rotr_optab->handlers[(int) HImode].insn_code = CODE_FOR_rotrhi3; + rotr_optab->handlers[(int) QImode].insn_code = CODE_FOR_rotrqi3; + setcc_gen_code[(int) EQ] = CODE_FOR_seq; + setcc_gen_code[(int) NE] = CODE_FOR_sne; + setcc_gen_code[(int) GT] = CODE_FOR_sgt; + setcc_gen_code[(int) GTU] = CODE_FOR_sgtu; + setcc_gen_code[(int) LT] = CODE_FOR_slt; + setcc_gen_code[(int) LTU] = CODE_FOR_sltu; + setcc_gen_code[(int) GE] = CODE_FOR_sge; + setcc_gen_code[(int) GEU] = CODE_FOR_sgeu; + setcc_gen_code[(int) LE] = CODE_FOR_sle; + setcc_gen_code[(int) LEU] = CODE_FOR_sleu; + bcc_gen_fctn[(int) EQ] = gen_beq; + bcc_gen_fctn[(int) NE] = gen_bne; + bcc_gen_fctn[(int) GT] = gen_bgt; + bcc_gen_fctn[(int) GTU] = gen_bgtu; + bcc_gen_fctn[(int) LT] = gen_blt; + bcc_gen_fctn[(int) LTU] = gen_bltu; + bcc_gen_fctn[(int) GE] = gen_bge; + bcc_gen_fctn[(int) GEU] = gen_bgeu; + bcc_gen_fctn[(int) LE] = gen_ble; + bcc_gen_fctn[(int) LEU] = gen_bleu; + movstr_optab[(int) SImode] = CODE_FOR_movstrsi; + ffs_optab->handlers[(int) SImode].insn_code = CODE_FOR_ffssi2; + ffs_optab->handlers[(int) HImode].insn_code = CODE_FOR_ffshi2; + strlen_optab->handlers[(int) SImode].insn_code = CODE_FOR_strlensi; +} diff --git a/gnu/usr.bin/cc/lib/insn-output.c b/gnu/usr.bin/cc/lib/insn-output.c new file mode 100644 index 0000000000..c39c4ffcc1 --- /dev/null +++ b/gnu/usr.bin/cc/lib/insn-output.c @@ -0,0 +1,5899 @@ +/* Generated automatically by the program `genoutput' +from the machine description file `md'. */ + +#include "config.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "real.h" +#include "insn-config.h" + +#include "conditions.h" +#include "insn-flags.h" +#include "insn-attr.h" + +#include "insn-codes.h" + +#include "recog.h" + +#include +#include "output.h" + +static char * +output_0 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[0])) + return AS2 (test%L0,%0,%0); + + operands[1] = const0_rtx; + return AS2 (cmp%L0,%1,%0); +} +} + +static char * +output_2 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[0])) + return AS2 (test%W0,%0,%0); + + operands[1] = const0_rtx; + return AS2 (cmp%W0,%1,%0); +} +} + +static char * +output_4 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[0])) + return AS2 (test%B0,%0,%0); + + operands[1] = const0_rtx; + return AS2 (cmp%B0,%1,%0); +} +} + +static char * +output_6 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (! STACK_TOP_P (operands[0])) + abort (); + + output_asm_insn ("ftst", operands); + + if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG)) + output_asm_insn (AS1 (fstp,%y0), operands); + + return (char *) output_fp_cc0_set (insn); +} +} + +static char * +output_8 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (! STACK_TOP_P (operands[0])) + abort (); + + output_asm_insn ("ftst", operands); + + if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG)) + output_asm_insn (AS1 (fstp,%y0), operands); + + return (char *) output_fp_cc0_set (insn); +} +} + +static char * +output_10 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM) + { + cc_status.flags |= CC_REVERSED; + return AS2 (cmp%L0,%0,%1); + } + return AS2 (cmp%L0,%1,%0); +} +} + +static char * +output_12 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM) + { + cc_status.flags |= CC_REVERSED; + return AS2 (cmp%W0,%0,%1); + } + return AS2 (cmp%W0,%1,%0); +} +} + +static char * +output_14 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM) + { + cc_status.flags |= CC_REVERSED; + return AS2 (cmp%B0,%0,%1); + } + return AS2 (cmp%B0,%1,%0); +} +} + +static char * +output_16 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_float_compare (insn, operands); +} + +static char * +output_17 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_float_compare (insn, operands); +} + +static char * +output_18 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_float_compare (insn, operands); +} + +static char * +output_19 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_float_compare (insn, operands); +} + +static char * +output_20 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_float_compare (insn, operands); +} + +static char * +output_21 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_float_compare (insn, operands); +} + +static char * +output_22 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_float_compare (insn, operands); +} + +static char * +output_23 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_float_compare (insn, operands); +} + +static char * +output_24 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_float_compare (insn, operands); +} + +static char * +output_25 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_float_compare (insn, operands); +} + +static char * +output_32 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + /* For small integers, we may actually use testb. */ + if (GET_CODE (operands[1]) == CONST_INT + && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])) + && (! REG_P (operands[0]) || QI_REG_P (operands[0]))) + { + /* We may set the sign bit spuriously. */ + + if ((INTVAL (operands[1]) & ~0xff) == 0) + { + cc_status.flags |= CC_NOT_NEGATIVE; + return AS2 (test%B0,%1,%b0); + } + + if ((INTVAL (operands[1]) & ~0xff00) == 0) + { + cc_status.flags |= CC_NOT_NEGATIVE; + operands[1] = GEN_INT (INTVAL (operands[1]) >> 8); + + if (QI_REG_P (operands[0])) + return AS2 (test%B0,%1,%h0); + else + { + operands[0] = adj_offsettable_operand (operands[0], 1); + return AS2 (test%B0,%1,%b0); + } + } + + if (GET_CODE (operands[0]) == MEM + && (INTVAL (operands[1]) & ~0xff0000) == 0) + { + cc_status.flags |= CC_NOT_NEGATIVE; + operands[1] = GEN_INT (INTVAL (operands[1]) >> 16); + operands[0] = adj_offsettable_operand (operands[0], 2); + return AS2 (test%B0,%1,%b0); + } + + if (GET_CODE (operands[0]) == MEM + && (INTVAL (operands[1]) & ~0xff000000) == 0) + { + operands[1] = GEN_INT ((INTVAL (operands[1]) >> 24) & 0xff); + operands[0] = adj_offsettable_operand (operands[0], 3); + return AS2 (test%B0,%1,%b0); + } + } + + if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM) + return AS2 (test%L0,%1,%0); + + return AS2 (test%L1,%0,%1); +} +} + +static char * +output_33 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (GET_CODE (operands[1]) == CONST_INT + && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])) + && (! REG_P (operands[0]) || QI_REG_P (operands[0]))) + { + if ((INTVAL (operands[1]) & 0xff00) == 0) + { + /* ??? This might not be necessary. */ + if (INTVAL (operands[1]) & 0xffff0000) + operands[1] = GEN_INT (INTVAL (operands[1]) & 0xff); + + /* We may set the sign bit spuriously. */ + cc_status.flags |= CC_NOT_NEGATIVE; + return AS2 (test%B0,%1,%b0); + } + + if ((INTVAL (operands[1]) & 0xff) == 0) + { + operands[1] = GEN_INT ((INTVAL (operands[1]) >> 8) & 0xff); + + if (QI_REG_P (operands[0])) + return AS2 (test%B0,%1,%h0); + else + { + operands[0] = adj_offsettable_operand (operands[0], 1); + return AS2 (test%B0,%1,%b0); + } + } + } + + if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM) + return AS2 (test%W0,%1,%0); + + return AS2 (test%W1,%0,%1); +} +} + +static char * +output_34 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM) + return AS2 (test%B0,%1,%0); + + return AS2 (test%B1,%0,%1); +} +} + +static char * +output_38 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx link; + if (operands[1] == const0_rtx && REG_P (operands[0])) + return AS2 (xor%L0,%0,%0); + + if (operands[1] == const1_rtx + && (link = find_reg_note (insn, REG_WAS_0, 0)) + /* Make sure the insn that stored the 0 is still present. */ + && ! INSN_DELETED_P (XEXP (link, 0)) + && GET_CODE (XEXP (link, 0)) != NOTE + /* Make sure cross jumping didn't happen here. */ + && no_labels_between_p (XEXP (link, 0), insn) + /* Make sure the reg hasn't been clobbered. */ + && ! reg_set_between_p (operands[0], XEXP (link, 0), insn)) + /* Fastest way to change a 0 to a 1. */ + return AS1 (inc%L0,%0); + + return AS2 (mov%L0,%1,%0); +} +} + +static char * +output_40 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx link; + if (REG_P (operands[0]) && operands[1] == const0_rtx) + return AS2 (xor%L0,%k0,%k0); + + if (REG_P (operands[0]) && operands[1] == const1_rtx + && (link = find_reg_note (insn, REG_WAS_0, 0)) + /* Make sure the insn that stored the 0 is still present. */ + && ! INSN_DELETED_P (XEXP (link, 0)) + && GET_CODE (XEXP (link, 0)) != NOTE + /* Make sure cross jumping didn't happen here. */ + && no_labels_between_p (XEXP (link, 0), insn) + /* Make sure the reg hasn't been clobbered. */ + && ! reg_set_between_p (operands[0], XEXP (link, 0), insn)) + /* Fastest way to change a 0 to a 1. */ + return AS1 (inc%L0,%k0); + + if (REG_P (operands[0])) + { + if (REG_P (operands[1])) + return AS2 (mov%L0,%k1,%k0); + else if (CONSTANT_P (operands[1])) + return AS2 (mov%L0,%1,%k0); + } + + return AS2 (mov%W0,%1,%0); +} +} + +static char * +output_41 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx link; + if (operands[1] == const0_rtx && REG_P (operands[0])) + return AS2 (xor%W0,%0,%0); + + if (operands[1] == const1_rtx + && (link = find_reg_note (insn, REG_WAS_0, 0)) + /* Make sure the insn that stored the 0 is still present. */ + && ! INSN_DELETED_P (XEXP (link, 0)) + && GET_CODE (XEXP (link, 0)) != NOTE + /* Make sure cross jumping didn't happen here. */ + && no_labels_between_p (XEXP (link, 0), insn) + /* Make sure the reg hasn't been clobbered. */ + && ! reg_set_between_p (operands[0], XEXP (link, 0), insn)) + /* Fastest way to change a 0 to a 1. */ + return AS1 (inc%W0,%0); + + return AS2 (mov%W0,%1,%0); +} +} + +static char * +output_42 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + operands[1] = gen_rtx (REG, HImode, REGNO (operands[1])); + return AS1 (push%W0,%1); +} +} + +static char * +output_43 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx link; + if (operands[1] == const0_rtx && REG_P (operands[0])) + return AS2 (xor%B0,%0,%0); + + if (operands[1] == const1_rtx + && (link = find_reg_note (insn, REG_WAS_0, 0)) + /* Make sure the insn that stored the 0 is still present. */ + && ! INSN_DELETED_P (XEXP (link, 0)) + && GET_CODE (XEXP (link, 0)) != NOTE + /* Make sure cross jumping didn't happen here. */ + && no_labels_between_p (XEXP (link, 0), insn) + /* Make sure the reg hasn't been clobbered. */ + && ! reg_set_between_p (operands[0], XEXP (link, 0), insn)) + /* Fastest way to change a 0 to a 1. */ + return AS1 (inc%B0,%0); + + /* If mov%B0 isn't allowed for one of these regs, use mov%L0. */ + if (NON_QI_REG_P (operands[0]) || NON_QI_REG_P (operands[1])) + return (AS2 (mov%L0,%k1,%k0)); + + return (AS2 (mov%B0,%1,%0)); +} +} + +static char * +output_44 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx link; + if (operands[1] == const0_rtx && REG_P (operands[0])) + return AS2 (xor%B0,%0,%0); + + if (operands[1] == const1_rtx + && (link = find_reg_note (insn, REG_WAS_0, 0)) + /* Make sure the insn that stored the 0 is still present. */ + && ! INSN_DELETED_P (XEXP (link, 0)) + && GET_CODE (XEXP (link, 0)) != NOTE + /* Make sure cross jumping didn't happen here. */ + && no_labels_between_p (XEXP (link, 0), insn) + /* Make sure the reg hasn't been clobbered. */ + && ! reg_set_between_p (operands[0], XEXP (link, 0), insn)) + /* Fastest way to change a 0 to a 1. */ + return AS1 (inc%B0,%0); + + /* If mov%B0 isn't allowed for one of these regs, use mov%W0. */ + if (NON_QI_REG_P (operands[0]) || NON_QI_REG_P (operands[1])) + { + abort (); + return (AS2 (mov%L0,%k1,%k0)); + } + + return AS2 (mov%B0,%1,%0); +} +} + +static char * +output_45 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (STACK_REG_P (operands[1])) + { + rtx xops[3]; + + if (! STACK_TOP_P (operands[1])) + abort (); + + xops[0] = AT_SP (SFmode); + xops[1] = GEN_INT (4); + xops[2] = stack_pointer_rtx; + + output_asm_insn (AS2 (sub%L2,%1,%2), xops); + + if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG)) + output_asm_insn (AS1 (fstp%S0,%0), xops); + else + output_asm_insn (AS1 (fst%S0,%0), xops); + RET; + } + return AS1 (push%L1,%1); +} +} + +static char * +output_46 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0; + + /* First handle a `pop' insn or a `fld %st(0)' */ + + if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1])) + { + if (stack_top_dies) + return AS1 (fstp,%y0); + else + return AS1 (fld,%y0); + } + + /* Handle a transfer between the 387 and a 386 register */ + + if (STACK_TOP_P (operands[0]) && NON_STACK_REG_P (operands[1])) + { + output_op_from_reg (operands[1], AS1 (fld%z0,%y1)); + RET; + } + + if (STACK_TOP_P (operands[1]) && NON_STACK_REG_P (operands[0])) + { + output_to_reg (operands[0], stack_top_dies); + RET; + } + + /* Handle other kinds of writes from the 387 */ + + if (STACK_TOP_P (operands[1])) + { + if (stack_top_dies) + return AS1 (fstp%z0,%y0); + else + return AS1 (fst%z0,%y0); + } + + /* Handle other kinds of reads to the 387 */ + + if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE) + return (char *) output_move_const_single (operands); + + if (STACK_TOP_P (operands[0])) + return AS1 (fld%z1,%y1); + + /* Handle all SFmode moves not involving the 387 */ + + return (char *) singlemove_string (operands); +} +} + +static char * +output_47 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (STACK_REG_P (operands[1])) + { + rtx xops[3]; + + xops[0] = AT_SP (SFmode); + xops[1] = GEN_INT (8); + xops[2] = stack_pointer_rtx; + + output_asm_insn (AS2 (sub%L2,%1,%2), xops); + + if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG)) + output_asm_insn (AS1 (fstp%Q0,%0), xops); + else + output_asm_insn (AS1 (fst%Q0,%0), xops); + + RET; + } + else + return (char *) output_move_double (operands); +} +} + +static char * +output_48 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (STACK_TOP_P (operands[0])) + return AS1 (fxch,%1); + else + return AS1 (fxch,%0); +} +} + +static char * +output_49 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0; + + /* First handle a `pop' insn or a `fld %st(0)' */ + + if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1])) + { + if (stack_top_dies) + return AS1 (fstp,%y0); + else + return AS1 (fld,%y0); + } + + /* Handle a transfer between the 387 and a 386 register */ + + if (STACK_TOP_P (operands[0]) && NON_STACK_REG_P (operands[1])) + { + output_op_from_reg (operands[1], AS1 (fld%z0,%y1)); + RET; + } + + if (STACK_TOP_P (operands[1]) && NON_STACK_REG_P (operands[0])) + { + output_to_reg (operands[0], stack_top_dies); + RET; + } + + /* Handle other kinds of writes from the 387 */ + + if (STACK_TOP_P (operands[1])) + { + if (stack_top_dies) + return AS1 (fstp%z0,%y0); + else + return AS1 (fst%z0,%y0); + } + + /* Handle other kinds of reads to the 387 */ + + if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE) + return (char *) output_move_const_single (operands); + + if (STACK_TOP_P (operands[0])) + return AS1 (fld%z1,%y1); + + /* Handle all DFmode moves not involving the 387 */ + + return (char *) output_move_double (operands); +} +} + +static char * +output_50 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + return (char *) output_move_double (operands); +} +} + +static char * +output_51 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + return (char *) output_move_double (operands); +} +} + +static char * +output_52 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if ((TARGET_486 || REGNO (operands[0]) == 0) + && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1])) + { + rtx xops[2]; + xops[0] = operands[0]; + xops[1] = GEN_INT (0xffff); + output_asm_insn (AS2 (and%L0,%1,%k0), xops); + RET; + } + +#ifdef INTEL_SYNTAX + return AS2 (movzx,%1,%0); +#else + return AS2 (movz%W0%L0,%1,%0); +#endif +} +} + +static char * +output_53 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if ((TARGET_486 || REGNO (operands[0]) == 0) + && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1])) + { + rtx xops[2]; + xops[0] = operands[0]; + xops[1] = GEN_INT (0xff); + output_asm_insn (AS2 (and%L0,%1,%k0), xops); + RET; + } + +#ifdef INTEL_SYNTAX + return AS2 (movzx,%1,%0); +#else + return AS2 (movz%B0%W0,%1,%0); +#endif +} +} + +static char * +output_54 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if ((TARGET_486 || REGNO (operands[0]) == 0) + && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1])) + { + rtx xops[2]; + xops[0] = operands[0]; + xops[1] = GEN_INT (0xff); + output_asm_insn (AS2 (and%L0,%1,%k0), xops); + RET; + } + +#ifdef INTEL_SYNTAX + return AS2 (movzx,%1,%0); +#else + return AS2 (movz%B0%L0,%1,%0); +#endif +} +} + +static char * +output_55 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); + return AS2 (xor%L0,%0,%0); +} +} + +static char * +output_56 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REGNO (operands[0]) == 0) + { + /* This used to be cwtl, but that extends HI to SI somehow. */ +#ifdef INTEL_SYNTAX + return "cdq"; +#else + return "cltd"; +#endif + } + + operands[1] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); + output_asm_insn (AS2 (mov%L0,%0,%1), operands); + + operands[0] = GEN_INT (31); + return AS2 (sar%L1,%0,%1); +} +} + +static char * +output_57 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REGNO (operands[0]) == 0 + && REG_P (operands[1]) && REGNO (operands[1]) == 0) +#ifdef INTEL_SYNTAX + return "cwde"; +#else + return "cwtl"; +#endif + +#ifdef INTEL_SYNTAX + return AS2 (movsx,%1,%0); +#else + return AS2 (movs%W0%L0,%1,%0); +#endif +} +} + +static char * +output_58 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REGNO (operands[0]) == 0 + && REG_P (operands[1]) && REGNO (operands[1]) == 0) + return "cbtw"; + +#ifdef INTEL_SYNTAX + return AS2 (movsx,%1,%0); +#else + return AS2 (movs%B0%W0,%1,%0); +#endif +} +} + +static char * +output_59 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ +#ifdef INTEL_SYNTAX + return AS2 (movsx,%1,%0); +#else + return AS2 (movs%B0%L0,%1,%0); +#endif +} +} + +static char * +output_60 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0; + + if (NON_STACK_REG_P (operands[1])) + { + output_op_from_reg (operands[1], AS1 (fld%z0,%y1)); + RET; + } + + if (NON_STACK_REG_P (operands[0])) + { + output_to_reg (operands[0], stack_top_dies); + RET; + } + + if (STACK_TOP_P (operands[0])) + return AS1 (fld%z1,%y1); + + if (GET_CODE (operands[0]) == MEM) + { + if (stack_top_dies) + return AS1 (fstp%z0,%y0); + else + return AS1 (fst%z0,%y0); + } + + abort (); +} +} + +static char * +output_62 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0; + + if (GET_CODE (operands[0]) == MEM) + { + if (stack_top_dies) + return AS1 (fstp%z0,%0); + else + return AS1 (fst%z0,%0); + } + else if (STACK_TOP_P (operands[0])) + { + output_asm_insn (AS1 (fstp%z2,%y2), operands); + return AS1 (fld%z2,%y2); + } + else + abort (); +} +} + +static char * +output_67 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_fix_trunc (insn, operands); +} + +static char * +output_68 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_fix_trunc (insn, operands); +} + +static char * +output_71 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_fix_trunc (insn, operands); +} + +static char * +output_72 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_fix_trunc (insn, operands); +} + +static char * +output_77 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (NON_STACK_REG_P (operands[1])) + { + output_op_from_reg (operands[1], AS1 (fild%z0,%1)); + RET; + } + else if (GET_CODE (operands[1]) == MEM) + return AS1 (fild%z1,%1); + else + abort (); +} +} + +static char * +output_78 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (NON_STACK_REG_P (operands[1])) + { + output_op_from_reg (operands[1], AS1 (fild%z0,%1)); + RET; + } + else if (GET_CODE (operands[1]) == MEM) + return AS1 (fild%z1,%1); + else + abort (); +} +} + +static char * +output_79 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (NON_STACK_REG_P (operands[1])) + { + output_op_from_reg (operands[1], AS1 (fild%z0,%1)); + RET; + } + else if (GET_CODE (operands[1]) == MEM) + return AS1 (fild%z1,%1); + else + abort (); +} +} + +static char * +output_80 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (NON_STACK_REG_P (operands[1])) + { + output_op_from_reg (operands[1], AS1 (fild%z0,%1)); + RET; + } + else if (GET_CODE (operands[1]) == MEM) + return AS1 (fild%z1,%1); + else + abort (); +} +} + +static char * +output_81 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx low[3], high[3]; + + CC_STATUS_INIT; + + split_di (operands, 3, low, high); + + if (GET_CODE (low[2]) != CONST_INT || INTVAL (low[2]) != 0) + { + output_asm_insn (AS2 (add%L0,%2,%0), low); + output_asm_insn (AS2 (adc%L0,%2,%0), high); + } + else + output_asm_insn (AS2 (add%L0,%2,%0), high); + RET; +} +} + +static char * +output_82 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[0]) && REGNO (operands[0]) != REGNO (operands[1])) + { + if (REG_P (operands[2]) && REGNO (operands[0]) == REGNO (operands[2])) + return AS2 (add%L0,%1,%0); + + if (! TARGET_486 || ! REG_P (operands[2])) + { + CC_STATUS_INIT; + + if (operands[2] == stack_pointer_rtx) + { + rtx temp; + + temp = operands[1]; + operands[1] = operands[2]; + operands[2] = temp; + } + if (operands[2] != stack_pointer_rtx) + { + operands[1] = SET_SRC (PATTERN (insn)); + return AS2 (lea%L0,%a1,%0); + } + } + + output_asm_insn (AS2 (mov%L0,%1,%0), operands); + } + + if (operands[2] == const1_rtx) + return AS1 (inc%L0,%0); + + if (operands[2] == constm1_rtx) + return AS1 (dec%L0,%0); + + return AS2 (add%L0,%2,%0); +} +} + +static char * +output_83 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (operands[2] == const1_rtx) + return AS1 (inc%W0,%0); + + if (operands[2] == constm1_rtx) + return AS1 (dec%W0,%0); + + return AS2 (add%W0,%2,%0); +} +} + +static char * +output_84 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (operands[2] == const1_rtx) + return AS1 (inc%B0,%0); + + if (operands[2] == constm1_rtx) + return AS1 (dec%B0,%0); + + return AS2 (add%B0,%2,%0); +} +} + +static char * +output_85 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + CC_STATUS_INIT; + /* Adding a constant to a register is faster with an add. */ + /* ??? can this ever happen? */ + if (GET_CODE (operands[1]) == PLUS + && GET_CODE (XEXP (operands[1], 1)) == CONST_INT + && rtx_equal_p (operands[0], XEXP (operands[1], 0))) + { + operands[1] = XEXP (operands[1], 1); + + if (operands[1] == const1_rtx) + return AS1 (inc%L0,%0); + + if (operands[1] == constm1_rtx) + return AS1 (dec%L0,%0); + + return AS2 (add%L0,%1,%0); + } + return AS2 (lea%L0,%a1,%0); +} +} + +static char * +output_88 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx low[3], high[3]; + + CC_STATUS_INIT; + + split_di (operands, 3, low, high); + + if (GET_CODE (low[2]) != CONST_INT || INTVAL (low[2]) != 0) + { + output_asm_insn (AS2 (sub%L0,%2,%0), low); + output_asm_insn (AS2 (sbb%L0,%2,%0), high); + } + else + output_asm_insn (AS2 (sub%L0,%2,%0), high); + + RET; +} +} + +static char * +output_89 (operands, insn) + rtx *operands; + rtx insn; +{ + return AS2 (sub%L0,%2,%0); +} + +static char * +output_90 (operands, insn) + rtx *operands; + rtx insn; +{ + return AS2 (sub%W0,%2,%0); +} + +static char * +output_91 (operands, insn) + rtx *operands; + rtx insn; +{ + return AS2 (sub%B0,%2,%0); +} + +static char * +output_94 (operands, insn) + rtx *operands; + rtx insn; +{ + return AS2 (imul%W0,%2,%0); +} + +static char * +output_95 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (GET_CODE (operands[1]) == REG + && REGNO (operands[1]) == REGNO (operands[0]) + && (GET_CODE (operands[2]) == MEM || GET_CODE (operands[2]) == REG)) + /* Assembler has weird restrictions. */ + return AS2 (imul%W0,%2,%0); + return AS3 (imul%W0,%2,%1,%0); +} +} + +static char * +output_96 (operands, insn) + rtx *operands; + rtx insn; +{ + return AS2 (imul%L0,%2,%0); +} + +static char * +output_97 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (GET_CODE (operands[1]) == REG + && REGNO (operands[1]) == REGNO (operands[0]) + && (GET_CODE (operands[2]) == MEM || GET_CODE (operands[2]) == REG)) + /* Assembler has weird restrictions. */ + return AS2 (imul%L0,%2,%0); + return AS3 (imul%L0,%2,%1,%0); +} +} + +static char * +output_105 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ +#ifdef INTEL_SYNTAX + output_asm_insn ("cdq", operands); +#else + output_asm_insn ("cltd", operands); +#endif + return AS1 (idiv%L0,%2); +} +} + +static char * +output_107 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + output_asm_insn (AS2 (xor%L3,%3,%3), operands); + return AS1 (div%L0,%2); +} +} + +static char * +output_108 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + output_asm_insn (AS2 (xor%W0,%3,%3), operands); + return AS1 (div%W0,%2); +} +} + +static char * +output_109 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (GET_CODE (operands[2]) == CONST_INT + && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) + { + if (INTVAL (operands[2]) == 0xffff && REG_P (operands[0]) + && (! REG_P (operands[1]) + || REGNO (operands[0]) != 0 || REGNO (operands[1]) != 0) + && (! TARGET_486 || ! rtx_equal_p (operands[0], operands[1]))) + { + /* ??? tege: Should forget CC_STATUS only if we clobber a + remembered operand. Fix that later. */ + CC_STATUS_INIT; +#ifdef INTEL_SYNTAX + return AS2 (movzx,%w1,%0); +#else + return AS2 (movz%W0%L0,%w1,%0); +#endif + } + + if (INTVAL (operands[2]) == 0xff && REG_P (operands[0]) + && !(REG_P (operands[1]) && NON_QI_REG_P (operands[1])) + && (! REG_P (operands[1]) + || REGNO (operands[0]) != 0 || REGNO (operands[1]) != 0) + && (! TARGET_486 || ! rtx_equal_p (operands[0], operands[1]))) + { + /* ??? tege: Should forget CC_STATUS only if we clobber a + remembered operand. Fix that later. */ + CC_STATUS_INIT; +#ifdef INTEL_SYNTAX + return AS2 (movzx,%b1,%0); +#else + return AS2 (movz%B0%L0,%b1,%0); +#endif + } + + if (QI_REG_P (operands[0]) && ~(INTVAL (operands[2]) | 0xff) == 0) + { + CC_STATUS_INIT; + + if (INTVAL (operands[2]) == 0xffffff00) + { + operands[2] = const0_rtx; + return AS2 (mov%B0,%2,%b0); + } + + operands[2] = GEN_INT (INTVAL (operands[2]) & 0xff); + return AS2 (and%B0,%2,%b0); + } + + if (QI_REG_P (operands[0]) && ~(INTVAL (operands[2]) | 0xff00) == 0) + { + CC_STATUS_INIT; + + if (INTVAL (operands[2]) == 0xffff00ff) + { + operands[2] = const0_rtx; + return AS2 (mov%B0,%2,%h0); + } + + operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff); + return AS2 (and%B0,%2,%h0); + } + + if (GET_CODE (operands[0]) == MEM && INTVAL (operands[2]) == 0xffff0000) + { + operands[2] = const0_rtx; + return AS2 (mov%W0,%2,%w0); + } + } + + return AS2 (and%L0,%2,%0); +} +} + +static char * +output_110 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (GET_CODE (operands[2]) == CONST_INT + && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) + { + /* Can we ignore the upper byte? */ + if ((! REG_P (operands[0]) || QI_REG_P (operands[0])) + && (INTVAL (operands[2]) & 0xff00) == 0xff00) + { + CC_STATUS_INIT; + + if ((INTVAL (operands[2]) & 0xff) == 0) + { + operands[2] = const0_rtx; + return AS2 (mov%B0,%2,%b0); + } + + operands[2] = GEN_INT (INTVAL (operands[2]) & 0xff); + return AS2 (and%B0,%2,%b0); + } + + /* Can we ignore the lower byte? */ + /* ??? what about offsettable memory references? */ + if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & 0xff) == 0xff) + { + CC_STATUS_INIT; + + if ((INTVAL (operands[2]) & 0xff00) == 0) + { + operands[2] = const0_rtx; + return AS2 (mov%B0,%2,%h0); + } + + operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff); + return AS2 (and%B0,%2,%h0); + } + } + + return AS2 (and%W0,%2,%0); +} +} + +static char * +output_111 (operands, insn) + rtx *operands; + rtx insn; +{ + return AS2 (and%B0,%2,%0); +} + +static char * +output_112 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (GET_CODE (operands[2]) == CONST_INT + && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) + { + if ((! REG_P (operands[0]) || QI_REG_P (operands[0])) + && (INTVAL (operands[2]) & ~0xff) == 0) + { + CC_STATUS_INIT; + + if (INTVAL (operands[2]) == 0xff) + return AS2 (mov%B0,%2,%b0); + + return AS2 (or%B0,%2,%b0); + } + + if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & ~0xff00) == 0) + { + CC_STATUS_INIT; + operands[2] = GEN_INT (INTVAL (operands[2]) >> 8); + + if (INTVAL (operands[2]) == 0xff) + return AS2 (mov%B0,%2,%h0); + + return AS2 (or%B0,%2,%h0); + } + } + + return AS2 (or%L0,%2,%0); +} +} + +static char * +output_113 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (GET_CODE (operands[2]) == CONST_INT + && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) + { + /* Can we ignore the upper byte? */ + if ((! REG_P (operands[0]) || QI_REG_P (operands[0])) + && (INTVAL (operands[2]) & 0xff00) == 0) + { + CC_STATUS_INIT; + if (INTVAL (operands[2]) & 0xffff0000) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff); + + if (INTVAL (operands[2]) == 0xff) + return AS2 (mov%B0,%2,%b0); + + return AS2 (or%B0,%2,%b0); + } + + /* Can we ignore the lower byte? */ + /* ??? what about offsettable memory references? */ + if (QI_REG_P (operands[0]) + && (INTVAL (operands[2]) & 0xff) == 0) + { + CC_STATUS_INIT; + operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff); + + if (INTVAL (operands[2]) == 0xff) + return AS2 (mov%B0,%2,%h0); + + return AS2 (or%B0,%2,%h0); + } + } + + return AS2 (or%W0,%2,%0); +} +} + +static char * +output_114 (operands, insn) + rtx *operands; + rtx insn; +{ + return AS2 (or%B0,%2,%0); +} + +static char * +output_115 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (GET_CODE (operands[2]) == CONST_INT + && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) + { + if ((! REG_P (operands[0]) || QI_REG_P (operands[0])) + && (INTVAL (operands[2]) & ~0xff) == 0) + { + CC_STATUS_INIT; + + if (INTVAL (operands[2]) == 0xff) + return AS1 (not%B0,%b0); + + return AS2 (xor%B0,%2,%b0); + } + + if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & ~0xff00) == 0) + { + CC_STATUS_INIT; + operands[2] = GEN_INT (INTVAL (operands[2]) >> 8); + + if (INTVAL (operands[2]) == 0xff) + return AS1 (not%B0,%h0); + + return AS2 (xor%B0,%2,%h0); + } + } + + return AS2 (xor%L0,%2,%0); +} +} + +static char * +output_116 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (GET_CODE (operands[2]) == CONST_INT + && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) + { + /* Can we ignore the upper byte? */ + if ((! REG_P (operands[0]) || QI_REG_P (operands[0])) + && (INTVAL (operands[2]) & 0xff00) == 0) + { + CC_STATUS_INIT; + if (INTVAL (operands[2]) & 0xffff0000) + operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff); + + if (INTVAL (operands[2]) == 0xff) + return AS1 (not%B0,%b0); + + return AS2 (xor%B0,%2,%b0); + } + + /* Can we ignore the lower byte? */ + /* ??? what about offsettable memory references? */ + if (QI_REG_P (operands[0]) + && (INTVAL (operands[2]) & 0xff) == 0) + { + CC_STATUS_INIT; + operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff); + + if (INTVAL (operands[2]) == 0xff) + return AS1 (not%B0,%h0); + + return AS2 (xor%B0,%2,%h0); + } + } + + return AS2 (xor%W0,%2,%0); +} +} + +static char * +output_117 (operands, insn) + rtx *operands; + rtx insn; +{ + return AS2 (xor%B0,%2,%0); +} + +static char * +output_118 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx xops[2], low[1], high[1]; + + CC_STATUS_INIT; + + split_di (operands, 1, low, high); + xops[0] = const0_rtx; + xops[1] = high[0]; + + output_asm_insn (AS1 (neg%L0,%0), low); + output_asm_insn (AS2 (adc%L1,%0,%1), xops); + output_asm_insn (AS1 (neg%L0,%0), high); + RET; +} +} + +static char * +output_141 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx xops[4], low[1], high[1]; + + CC_STATUS_INIT; + + split_di (operands, 1, low, high); + xops[0] = operands[2]; + xops[1] = const1_rtx; + xops[2] = low[0]; + xops[3] = high[0]; + + if (INTVAL (xops[0]) > 31) + { + output_asm_insn (AS2 (mov%L3,%2,%3), xops); /* Fast shift by 32 */ + output_asm_insn (AS2 (xor%L2,%2,%2), xops); + + if (INTVAL (xops[0]) > 32) + { + xops[0] = GEN_INT (INTVAL (xops[0]) - 32); + output_asm_insn (AS2 (sal%L3,%0,%3), xops); /* Remaining shift */ + } + } + else + { + output_asm_insn (AS3 (shld%L3,%0,%2,%3), xops); + output_asm_insn (AS2 (sal%L2,%0,%2), xops); + } + RET; +} +} + +static char * +output_142 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx xops[4], low[1], high[1]; + + CC_STATUS_INIT; + + split_di (operands, 1, low, high); + xops[0] = operands[2]; + xops[1] = const1_rtx; + xops[2] = low[0]; + xops[3] = high[0]; + + output_asm_insn (AS2 (ror%B0,%1,%0), xops); /* shift count / 2 */ + + output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops); + output_asm_insn (AS2 (sal%L2,%0,%2), xops); + output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops); + output_asm_insn (AS2 (sal%L2,%0,%2), xops); + + xops[1] = GEN_INT (7); /* shift count & 1 */ + + output_asm_insn (AS2 (shr%B0,%1,%0), xops); + + output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops); + output_asm_insn (AS2 (sal%L2,%0,%2), xops); + + RET; +} +} + +static char * +output_143 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[0]) && REGNO (operands[0]) != REGNO (operands[1])) + { + if (TARGET_486 && INTVAL (operands[2]) == 1) + { + output_asm_insn (AS2 (mov%L0,%1,%0), operands); + return AS2 (add%L0,%1,%0); + } + else + { + CC_STATUS_INIT; + + if (operands[1] == stack_pointer_rtx) + { + output_asm_insn (AS2 (mov%L0,%1,%0), operands); + operands[1] = operands[0]; + } + operands[1] = gen_rtx (MULT, SImode, operands[1], + GEN_INT (1 << INTVAL (operands[2]))); + return AS2 (lea%L0,%a1,%0); + } + } + + if (REG_P (operands[2])) + return AS2 (sal%L0,%b2,%0); + + if (REG_P (operands[0]) && operands[2] == const1_rtx) + return AS2 (add%L0,%0,%0); + + return AS2 (sal%L0,%2,%0); +} +} + +static char * +output_144 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[2])) + return AS2 (sal%W0,%b2,%0); + + if (REG_P (operands[0]) && operands[2] == const1_rtx) + return AS2 (add%W0,%0,%0); + + return AS2 (sal%W0,%2,%0); +} +} + +static char * +output_145 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[2])) + return AS2 (sal%B0,%b2,%0); + + if (REG_P (operands[0]) && operands[2] == const1_rtx) + return AS2 (add%B0,%0,%0); + + return AS2 (sal%B0,%2,%0); +} +} + +static char * +output_147 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx xops[4], low[1], high[1]; + + CC_STATUS_INIT; + + split_di (operands, 1, low, high); + xops[0] = operands[2]; + xops[1] = const1_rtx; + xops[2] = low[0]; + xops[3] = high[0]; + + if (INTVAL (xops[0]) > 31) + { + xops[1] = GEN_INT (31); + output_asm_insn (AS2 (mov%L2,%3,%2), xops); + output_asm_insn (AS2 (sar%L3,%1,%3), xops); /* shift by 32 */ + + if (INTVAL (xops[0]) > 32) + { + xops[0] = GEN_INT (INTVAL (xops[0]) - 32); + output_asm_insn (AS2 (sar%L2,%0,%2), xops); /* Remaining shift */ + } + } + else + { + output_asm_insn (AS3 (shrd%L2,%0,%3,%2), xops); + output_asm_insn (AS2 (sar%L3,%0,%3), xops); + } + + RET; +} +} + +static char * +output_148 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx xops[4], low[1], high[1]; + + CC_STATUS_INIT; + + split_di (operands, 1, low, high); + xops[0] = operands[2]; + xops[1] = const1_rtx; + xops[2] = low[0]; + xops[3] = high[0]; + + output_asm_insn (AS2 (ror%B0,%1,%0), xops); /* shift count / 2 */ + + output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); + output_asm_insn (AS2 (sar%L3,%0,%3), xops); + output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); + output_asm_insn (AS2 (sar%L3,%0,%3), xops); + + xops[1] = GEN_INT (7); /* shift count & 1 */ + + output_asm_insn (AS2 (shr%B0,%1,%0), xops); + + output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); + output_asm_insn (AS2 (sar%L3,%0,%3), xops); + + RET; +} +} + +static char * +output_149 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[2])) + return AS2 (sar%L0,%b2,%0); + else + return AS2 (sar%L0,%2,%0); +} +} + +static char * +output_150 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[2])) + return AS2 (sar%W0,%b2,%0); + else + return AS2 (sar%W0,%2,%0); +} +} + +static char * +output_151 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[2])) + return AS2 (sar%B0,%b2,%0); + else + return AS2 (sar%B0,%2,%0); +} +} + +static char * +output_153 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx xops[4], low[1], high[1]; + + CC_STATUS_INIT; + + split_di (operands, 1, low, high); + xops[0] = operands[2]; + xops[1] = const1_rtx; + xops[2] = low[0]; + xops[3] = high[0]; + + if (INTVAL (xops[0]) > 31) + { + output_asm_insn (AS2 (mov%L2,%3,%2), xops); /* Fast shift by 32 */ + output_asm_insn (AS2 (xor%L3,%3,%3), xops); + + if (INTVAL (xops[0]) > 32) + { + xops[0] = GEN_INT (INTVAL (xops[0]) - 32); + output_asm_insn (AS2 (shr%L2,%0,%2), xops); /* Remaining shift */ + } + } + else + { + output_asm_insn (AS3 (shrd%L2,%0,%3,%2), xops); + output_asm_insn (AS2 (shr%L3,%0,%3), xops); + } + + RET; +} +} + +static char * +output_154 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx xops[4], low[1], high[1]; + + CC_STATUS_INIT; + + split_di (operands, 1, low, high); + xops[0] = operands[2]; + xops[1] = const1_rtx; + xops[2] = low[0]; + xops[3] = high[0]; + + output_asm_insn (AS2 (ror%B0,%1,%0), xops); /* shift count / 2 */ + + output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); + output_asm_insn (AS2 (shr%L3,%0,%3), xops); + output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); + output_asm_insn (AS2 (shr%L3,%0,%3), xops); + + xops[1] = GEN_INT (7); /* shift count & 1 */ + + output_asm_insn (AS2 (shr%B0,%1,%0), xops); + + output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); + output_asm_insn (AS2 (shr%L3,%0,%3), xops); + + RET; +} +} + +static char * +output_155 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[2])) + return AS2 (shr%L0,%b2,%0); + else + return AS2 (shr%L0,%2,%1); +} +} + +static char * +output_156 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[2])) + return AS2 (shr%W0,%b2,%0); + else + return AS2 (shr%W0,%2,%0); +} +} + +static char * +output_157 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[2])) + return AS2 (shr%B0,%b2,%0); + else + return AS2 (shr%B0,%2,%0); +} +} + +static char * +output_158 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[2])) + return AS2 (rol%L0,%b2,%0); + else + return AS2 (rol%L0,%2,%0); +} +} + +static char * +output_159 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[2])) + return AS2 (rol%W0,%b2,%0); + else + return AS2 (rol%W0,%2,%0); +} +} + +static char * +output_160 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[2])) + return AS2 (rol%B0,%b2,%0); + else + return AS2 (rol%B0,%2,%0); +} +} + +static char * +output_161 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[2])) + return AS2 (ror%L0,%b2,%0); + else + return AS2 (ror%L0,%2,%0); +} +} + +static char * +output_162 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[2])) + return AS2 (ror%W0,%b2,%0); + else + return AS2 (ror%W0,%2,%0); +} +} + +static char * +output_163 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (REG_P (operands[2])) + return AS2 (ror%B0,%b2,%0); + else + return AS2 (ror%B0,%2,%0); +} +} + +static char * +output_164 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + CC_STATUS_INIT; + + if (INTVAL (operands[3]) == 1) + return AS2 (bts%L0,%2,%0); + else + return AS2 (btr%L0,%2,%0); +} +} + +static char * +output_165 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + CC_STATUS_INIT; + + return AS2 (btc%L0,%1,%0); +} +} + +static char * +output_166 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + CC_STATUS_INIT; + + return AS2 (btc%L0,%2,%0); +} +} + +static char * +output_167 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + cc_status.flags |= CC_Z_IN_NOT_C; + return AS2 (bt%L0,%1,%0); +} +} + +static char * +output_168 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + unsigned int mask; + + mask = ((1 << INTVAL (operands[1])) - 1) << INTVAL (operands[2]); + operands[1] = GEN_INT (mask); + + if (QI_REG_P (operands[0])) + { + if ((mask & ~0xff) == 0) + { + cc_status.flags |= CC_NOT_NEGATIVE; + return AS2 (test%B0,%1,%b0); + } + + if ((mask & ~0xff00) == 0) + { + cc_status.flags |= CC_NOT_NEGATIVE; + operands[1] = GEN_INT (mask >> 8); + return AS2 (test%B0,%1,%h0); + } + } + + return AS2 (test%L0,%1,%0); +} +} + +static char * +output_169 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + unsigned int mask; + + mask = ((1 << INTVAL (operands[1])) - 1) << INTVAL (operands[2]); + operands[1] = GEN_INT (mask); + + if (! REG_P (operands[0]) || QI_REG_P (operands[0])) + { + if ((mask & ~0xff) == 0) + { + cc_status.flags |= CC_NOT_NEGATIVE; + return AS2 (test%B0,%1,%b0); + } + + if ((mask & ~0xff00) == 0) + { + cc_status.flags |= CC_NOT_NEGATIVE; + operands[1] = GEN_INT (mask >> 8); + + if (QI_REG_P (operands[0])) + return AS2 (test%B0,%1,%h0); + else + { + operands[0] = adj_offsettable_operand (operands[0], 1); + return AS2 (test%B0,%1,%b0); + } + } + + if (GET_CODE (operands[0]) == MEM && (mask & ~0xff0000) == 0) + { + cc_status.flags |= CC_NOT_NEGATIVE; + operands[1] = GEN_INT (mask >> 16); + operands[0] = adj_offsettable_operand (operands[0], 2); + return AS2 (test%B0,%1,%b0); + } + + if (GET_CODE (operands[0]) == MEM && (mask & ~0xff000000) == 0) + { + cc_status.flags |= CC_NOT_NEGATIVE; + operands[1] = GEN_INT (mask >> 24); + operands[0] = adj_offsettable_operand (operands[0], 3); + return AS2 (test%B0,%1,%b0); + } + } + + if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM) + return AS2 (test%L0,%1,%0); + + return AS2 (test%L1,%0,%1); +} +} + +static char * +output_171 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (cc_prev_status.flags & CC_Z_IN_NOT_C) + return AS1 (setnb,%0); + else + return AS1 (sete,%0); +} +} + +static char * +output_173 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (cc_prev_status.flags & CC_Z_IN_NOT_C) + return AS1 (setb,%0); + else + return AS1 (setne,%0); +} + +} + +static char * +output_175 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) + return AS1 (sete,%0); + + OUTPUT_JUMP ("setg %0", "seta %0", NULL_PTR); +} +} + +static char * +output_177 (operands, insn) + rtx *operands; + rtx insn; +{ + return "seta %0"; +} + +static char * +output_179 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) + return AS1 (sete,%0); + + OUTPUT_JUMP ("setl %0", "setb %0", "sets %0"); +} +} + +static char * +output_181 (operands, insn) + rtx *operands; + rtx insn; +{ + return "setb %0"; +} + +static char * +output_183 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) + return AS1 (sete,%0); + + OUTPUT_JUMP ("setge %0", "setae %0", "setns %0"); +} +} + +static char * +output_185 (operands, insn) + rtx *operands; + rtx insn; +{ + return "setae %0"; +} + +static char * +output_187 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) + return AS1 (setb,%0); + + OUTPUT_JUMP ("setle %0", "setbe %0", NULL_PTR); +} +} + +static char * +output_189 (operands, insn) + rtx *operands; + rtx insn; +{ + return "setbe %0"; +} + +static char * +output_191 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (cc_prev_status.flags & CC_Z_IN_NOT_C) + return "jnc %l0"; + else + return "je %l0"; +} +} + +static char * +output_193 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (cc_prev_status.flags & CC_Z_IN_NOT_C) + return "jc %l0"; + else + return "jne %l0"; +} +} + +static char * +output_195 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) + return AS1 (je,%l0); + + OUTPUT_JUMP ("jg %l0", "ja %l0", NULL_PTR); +} +} + +static char * +output_199 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) + return AS1 (je,%l0); + + OUTPUT_JUMP ("jl %l0", "jb %l0", "js %l0"); +} +} + +static char * +output_203 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) + return AS1 (je,%l0); + + OUTPUT_JUMP ("jge %l0", "jae %l0", "jns %l0"); +} +} + +static char * +output_207 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) + return AS1 (jb,%l0); + + OUTPUT_JUMP ("jle %l0", "jbe %l0", NULL_PTR); +} +} + +static char * +output_210 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (cc_prev_status.flags & CC_Z_IN_NOT_C) + return "jc %l0"; + else + return "jne %l0"; +} +} + +static char * +output_211 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (cc_prev_status.flags & CC_Z_IN_NOT_C) + return "jnc %l0"; + else + return "je %l0"; +} +} + +static char * +output_212 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) + return AS1 (jne,%l0); + + OUTPUT_JUMP ("jle %l0", "jbe %l0", NULL_PTR); +} +} + +static char * +output_214 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) + return AS1 (jne,%l0); + + OUTPUT_JUMP ("jge %l0", "jae %l0", "jns %l0"); +} +} + +static char * +output_216 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) + return AS1 (jne,%l0); + + OUTPUT_JUMP ("jl %l0", "jb %l0", "js %l0"); +} +} + +static char * +output_218 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) + return AS1 (jae,%l0); + + OUTPUT_JUMP ("jg %l0", "ja %l0", NULL_PTR); +} +} + +static char * +output_221 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + CC_STATUS_INIT; + + return AS1 (jmp,%*%0); +} +} + +static char * +output_223 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx xops[4]; + + xops[0] = operands[0]; + xops[1] = operands[1]; + xops[2] = operands[2]; + xops[3] = pic_offset_table_rtx; + + output_asm_insn (AS2 (mov%L2,%3,%2), xops); + output_asm_insn ("sub%L2 %l1@GOTOFF(%3,%0,4),%2", xops); + output_asm_insn (AS1 (jmp,%*%2), xops); + ASM_OUTPUT_ALIGN_CODE (asm_out_file); + RET; +} +} + +static char * +output_224 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + CC_STATUS_INIT; + + return AS1 (jmp,%*%0); +} +} + +static char * +output_226 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (GET_CODE (operands[0]) == MEM + && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) + { + operands[0] = XEXP (operands[0], 0); + return AS1 (call,%*%0); + } + else + return AS1 (call,%P0); +} +} + +static char * +output_229 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (GET_CODE (operands[0]) == MEM + && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) + { + operands[0] = XEXP (operands[0], 0); + return AS1 (call,%*%0); + } + else + return AS1 (call,%P0); +} +} + +static char * +output_232 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (GET_CODE (operands[1]) == MEM + && ! CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) + { + operands[1] = XEXP (operands[1], 0); + output_asm_insn (AS1 (call,%*%1), operands); + } + else + output_asm_insn (AS1 (call,%P1), operands); + + RET; +} +} + +static char * +output_235 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + if (GET_CODE (operands[1]) == MEM + && ! CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) + { + operands[1] = XEXP (operands[1], 0); + output_asm_insn (AS1 (call,%*%1), operands); + } + else + output_asm_insn (AS1 (call,%P1), operands); + + RET; +} +} + +static char * +output_238 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx addr = operands[1]; + + if (GET_CODE (operands[0]) == MEM + && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) + { + operands[0] = XEXP (operands[0], 0); + output_asm_insn (AS1 (call,%*%0), operands); + } + else + output_asm_insn (AS1 (call,%P0), operands); + + operands[2] = gen_rtx (REG, SImode, 0); + output_asm_insn (AS2 (mov%L2,%2,%1), operands); + + operands[2] = gen_rtx (REG, SImode, 1); + operands[1] = adj_offsettable_operand (addr, 4); + output_asm_insn (AS2 (mov%L2,%2,%1), operands); + + operands[1] = adj_offsettable_operand (addr, 8); + return AS1 (fnsave,%1); +} +} + +static char * +output_239 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx addr = operands[1]; + + output_asm_insn (AS1 (call,%P0), operands); + + operands[2] = gen_rtx (REG, SImode, 0); + output_asm_insn (AS2 (mov%L2,%2,%1), operands); + + operands[2] = gen_rtx (REG, SImode, 1); + operands[1] = adj_offsettable_operand (addr, 4); + output_asm_insn (AS2 (mov%L2,%2,%1), operands); + + operands[1] = adj_offsettable_operand (addr, 8); + return AS1 (fnsave,%1); +} +} + +static char * +output_242 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + function_epilogue (asm_out_file, get_frame_size ()); + RET; +} +} + +static char * +output_245 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx xops[2]; + + output_asm_insn ("cld", operands); + if (GET_CODE (operands[2]) == CONST_INT) + { + if (INTVAL (operands[2]) & ~0x03) + { + xops[0] = GEN_INT ((INTVAL (operands[2]) >> 2) & 0x3fffffff); + xops[1] = operands[4]; + + output_asm_insn (AS2 (mov%L1,%0,%1), xops); +#ifdef INTEL_SYNTAX + output_asm_insn ("rep movsd", xops); +#else + output_asm_insn ("rep\n\tmovsl", xops); +#endif + } + if (INTVAL (operands[2]) & 0x02) + output_asm_insn ("movsw", operands); + if (INTVAL (operands[2]) & 0x01) + output_asm_insn ("movsb", operands); + } + else + abort (); + RET; +} +} + +static char * +output_247 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx xops[4], label; + + label = gen_label_rtx (); + + output_asm_insn ("cld", operands); + output_asm_insn (AS2 (xor%L0,%0,%0), operands); + output_asm_insn ("repz\n\tcmps%B2", operands); + output_asm_insn ("je %l0", &label); + + xops[0] = operands[0]; + xops[1] = gen_rtx (MEM, QImode, + gen_rtx (PLUS, SImode, operands[1], constm1_rtx)); + xops[2] = gen_rtx (MEM, QImode, + gen_rtx (PLUS, SImode, operands[2], constm1_rtx)); + xops[3] = operands[3]; + + output_asm_insn (AS2 (movz%B1%L0,%1,%0), xops); + output_asm_insn (AS2 (movz%B2%L3,%2,%3), xops); + + output_asm_insn (AS2 (sub%L0,%3,%0), xops); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (label)); + RET; +} +} + +static char * +output_248 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx xops[2]; + + cc_status.flags |= CC_NOT_SIGNED; + + xops[0] = gen_rtx (REG, QImode, 0); + xops[1] = CONST0_RTX (QImode); + + output_asm_insn ("cld", operands); + output_asm_insn (AS2 (test%B0,%1,%0), xops); + return "repz\n\tcmps%B2"; +} +} + +static char * +output_250 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx xops[2]; + + xops[0] = const1_rtx; + xops[1] = operands[1]; + output_asm_insn (AS2 (bsf%L1,%1,%0), operands); + output_asm_insn (AS2 (cmp%L1,%0,%1), xops); + output_asm_insn (AS2 (sbb%L0,%2,%2), operands); + output_asm_insn (AS2 (or%L0,%2,%0), operands); + return ""; +} +} + +static char * +output_252 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx xops[2]; + + xops[0] = const1_rtx; + xops[1] = operands[1]; + output_asm_insn (AS2 (bsf%W1,%1,%0), operands); + output_asm_insn (AS2 (cmp%W1,%0,%1), xops); + output_asm_insn (AS2 (sbb%W0,%2,%2), operands); + output_asm_insn (AS2 (or%W0,%2,%0), operands); + return ""; +} +} + +static char * +output_253 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_387_binary_op (insn, operands); +} + +static char * +output_254 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_387_binary_op (insn, operands); +} + +static char * +output_255 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_387_binary_op (insn, operands); +} + +static char * +output_256 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_387_binary_op (insn, operands); +} + +static char * +output_257 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_387_binary_op (insn, operands); +} + +static char * +output_258 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_387_binary_op (insn, operands); +} + +static char * +output_259 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_387_binary_op (insn, operands); +} + +static char * +output_260 (operands, insn) + rtx *operands; + rtx insn; +{ + return (char *) output_387_binary_op (insn, operands); +} + +static char * +output_262 (operands, insn) + rtx *operands; + rtx insn; +{ + +{ + rtx xops[2]; + + xops[0] = operands[0]; + xops[1] = constm1_rtx; + output_asm_insn ("cld", operands); + output_asm_insn (AS2 (mov%L0,%1,%0), xops); + return "repnz\n\tscas%B2"; +} +} + +char * const insn_template[] = + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + "push%L0 %1", + "push%L0 %1", + 0, + 0, + "push%W0 %1", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + "mul%B0 %2", + 0, + 0, + "idiv%B0 %2", + "div%B0 %2", + 0, + 0, + 0, + "cwtd\n\tidiv%W0 %2", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + "neg%L0 %0", + "neg%W0 %0", + "neg%B0 %0", + "fchs", + "fchs", + "fchs", + "fabs", + "fabs", + "fabs", + "fsqrt", + "fsqrt", + "fsqrt", + "fsin", + "fsin", + "fsin", + "fcos", + "fcos", + "fcos", + "not%L0 %0", + "not%W0 %0", + "not%B0 %0", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + "ja %l0", + 0, + 0, + 0, + "jb %l0", + 0, + 0, + 0, + "jae %l0", + 0, + 0, + 0, + "jbe %l0", + 0, + 0, + 0, + "jbe %l0", + 0, + "jae %l0", + 0, + "jb %l0", + 0, + "ja %l0", + "jmp %l0", + 0, + 0, + 0, + 0, + 0, + 0, + "call %P0", + 0, + 0, + "call %P0", + 0, + 0, + "call %P1", + 0, + 0, + "call %P1", + 0, + 0, + 0, + 0, + "frstor %0", + 0, + "nop", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }; + +char *(*const insn_outfun[])() = + { + output_0, + 0, + output_2, + 0, + output_4, + 0, + output_6, + 0, + output_8, + 0, + output_10, + 0, + output_12, + 0, + output_14, + 0, + output_16, + output_17, + output_18, + output_19, + output_20, + output_21, + output_22, + output_23, + output_24, + output_25, + 0, + 0, + 0, + 0, + 0, + 0, + output_32, + output_33, + output_34, + 0, + 0, + 0, + output_38, + 0, + output_40, + output_41, + output_42, + output_43, + output_44, + output_45, + output_46, + output_47, + output_48, + output_49, + output_50, + output_51, + output_52, + output_53, + output_54, + output_55, + output_56, + output_57, + output_58, + output_59, + output_60, + 0, + output_62, + 0, + 0, + 0, + 0, + output_67, + output_68, + 0, + 0, + output_71, + output_72, + 0, + 0, + 0, + 0, + output_77, + output_78, + output_79, + output_80, + output_81, + output_82, + output_83, + output_84, + output_85, + 0, + 0, + output_88, + output_89, + output_90, + output_91, + 0, + 0, + output_94, + output_95, + output_96, + output_97, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + output_105, + 0, + output_107, + output_108, + output_109, + output_110, + output_111, + output_112, + output_113, + output_114, + output_115, + output_116, + output_117, + output_118, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + output_141, + output_142, + output_143, + output_144, + output_145, + 0, + output_147, + output_148, + output_149, + output_150, + output_151, + 0, + output_153, + output_154, + output_155, + output_156, + output_157, + output_158, + output_159, + output_160, + output_161, + output_162, + output_163, + output_164, + output_165, + output_166, + output_167, + output_168, + output_169, + 0, + output_171, + 0, + output_173, + 0, + output_175, + 0, + output_177, + 0, + output_179, + 0, + output_181, + 0, + output_183, + 0, + output_185, + 0, + output_187, + 0, + output_189, + 0, + output_191, + 0, + output_193, + 0, + output_195, + 0, + 0, + 0, + output_199, + 0, + 0, + 0, + output_203, + 0, + 0, + 0, + output_207, + 0, + 0, + output_210, + output_211, + output_212, + 0, + output_214, + 0, + output_216, + 0, + output_218, + 0, + 0, + output_221, + 0, + output_223, + output_224, + 0, + output_226, + 0, + 0, + output_229, + 0, + 0, + output_232, + 0, + 0, + output_235, + 0, + 0, + output_238, + output_239, + 0, + 0, + output_242, + 0, + 0, + output_245, + 0, + output_247, + output_248, + 0, + output_250, + 0, + output_252, + output_253, + output_254, + output_255, + output_256, + output_257, + output_258, + output_259, + output_260, + 0, + output_262, + }; + +rtx (*const insn_gen_function[]) () = + { + gen_tstsi_1, + gen_tstsi, + gen_tsthi_1, + gen_tsthi, + gen_tstqi_1, + gen_tstqi, + gen_tstsf_cc, + gen_tstsf, + gen_tstdf_cc, + gen_tstdf, + gen_cmpsi_1, + gen_cmpsi, + gen_cmphi_1, + gen_cmphi, + gen_cmpqi_1, + gen_cmpqi, + 0, + 0, + 0, + 0, + 0, + 0, + gen_cmpsf_cc_1, + 0, + 0, + 0, + gen_cmpdf, + gen_cmpsf, + gen_cmpdf_cc, + gen_cmpdf_ccfpeq, + gen_cmpsf_cc, + gen_cmpsf_ccfpeq, + 0, + 0, + 0, + 0, + 0, + gen_movsi, + 0, + 0, + gen_movhi, + gen_movstricthi, + 0, + gen_movqi, + gen_movstrictqi, + 0, + gen_movsf, + 0, + gen_swapdf, + gen_movdf, + 0, + gen_movdi, + gen_zero_extendhisi2, + gen_zero_extendqihi2, + gen_zero_extendqisi2, + gen_zero_extendsidi2, + gen_extendsidi2, + gen_extendhisi2, + gen_extendqihi2, + gen_extendqisi2, + gen_extendsfdf2, + gen_truncdfsf2, + 0, + gen_fixuns_truncdfsi2, + gen_fixuns_truncsfsi2, + gen_fix_truncdfdi2, + gen_fix_truncsfdi2, + 0, + 0, + gen_fix_truncdfsi2, + gen_fix_truncsfsi2, + 0, + 0, + gen_floatsisf2, + gen_floatdisf2, + gen_floatsidf2, + gen_floatdidf2, + 0, + 0, + 0, + 0, + gen_adddi3, + gen_addsi3, + gen_addhi3, + gen_addqi3, + 0, + gen_adddf3, + gen_addsf3, + gen_subdi3, + gen_subsi3, + gen_subhi3, + gen_subqi3, + gen_subdf3, + gen_subsf3, + 0, + gen_mulhi3, + 0, + gen_mulsi3, + 0, + gen_muldf3, + gen_mulsf3, + gen_divqi3, + gen_udivqi3, + gen_divdf3, + gen_divsf3, + gen_divmodsi4, + gen_divmodhi4, + gen_udivmodsi4, + gen_udivmodhi4, + gen_andsi3, + gen_andhi3, + gen_andqi3, + gen_iorsi3, + gen_iorhi3, + gen_iorqi3, + gen_xorsi3, + gen_xorhi3, + gen_xorqi3, + gen_negdi2, + gen_negsi2, + gen_neghi2, + gen_negqi2, + gen_negsf2, + gen_negdf2, + 0, + gen_abssf2, + gen_absdf2, + 0, + gen_sqrtsf2, + gen_sqrtdf2, + 0, + gen_sindf2, + gen_sinsf2, + 0, + gen_cosdf2, + gen_cossf2, + 0, + gen_one_cmplsi2, + gen_one_cmplhi2, + gen_one_cmplqi2, + gen_ashldi3, + gen_ashldi3_const_int, + gen_ashldi3_non_const_int, + gen_ashlsi3, + gen_ashlhi3, + gen_ashlqi3, + gen_ashrdi3, + gen_ashrdi3_const_int, + gen_ashrdi3_non_const_int, + gen_ashrsi3, + gen_ashrhi3, + gen_ashrqi3, + gen_lshrdi3, + gen_lshrdi3_const_int, + gen_lshrdi3_non_const_int, + gen_lshrsi3, + gen_lshrhi3, + gen_lshrqi3, + gen_rotlsi3, + gen_rotlhi3, + gen_rotlqi3, + gen_rotrsi3, + gen_rotrhi3, + gen_rotrqi3, + 0, + 0, + 0, + 0, + 0, + 0, + gen_seq, + 0, + gen_sne, + 0, + gen_sgt, + 0, + gen_sgtu, + 0, + gen_slt, + 0, + gen_sltu, + 0, + gen_sge, + 0, + gen_sgeu, + 0, + gen_sle, + 0, + gen_sleu, + 0, + gen_beq, + 0, + gen_bne, + 0, + gen_bgt, + 0, + gen_bgtu, + 0, + gen_blt, + 0, + gen_bltu, + 0, + gen_bge, + 0, + gen_bgeu, + 0, + gen_ble, + 0, + gen_bleu, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + gen_jump, + gen_indirect_jump, + gen_casesi, + 0, + gen_tablejump, + gen_call_pop, + 0, + 0, + gen_call, + 0, + 0, + gen_call_value_pop, + 0, + 0, + gen_call_value, + 0, + 0, + gen_untyped_call, + 0, + 0, + gen_untyped_return, + gen_update_return, + gen_return, + gen_nop, + gen_movstrsi, + 0, + gen_cmpstrsi, + 0, + 0, + gen_ffssi2, + 0, + gen_ffshi2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + gen_strlensi, + 0, + }; + +char *insn_name[] = + { + "tstsi_1", + "tstsi", + "tsthi_1", + "tsthi", + "tstqi_1", + "tstqi", + "tstsf_cc", + "tstsf", + "tstdf_cc", + "tstdf", + "cmpsi_1", + "cmpsi", + "cmphi_1", + "cmphi", + "cmpqi_1", + "cmpqi", + "cmpqi+1", + "cmpqi+2", + "cmpqi+3", + "cmpsf_cc_1-3", + "cmpsf_cc_1-2", + "cmpsf_cc_1-1", + "cmpsf_cc_1", + "cmpsf_cc_1+1", + "cmpsf_cc_1+2", + "cmpdf-1", + "cmpdf", + "cmpsf", + "cmpdf_cc", + "cmpdf_ccfpeq", + "cmpsf_cc", + "cmpsf_ccfpeq", + "cmpsf_ccfpeq+1", + "cmpsf_ccfpeq+2", + "cmpsf_ccfpeq+3", + "movsi-2", + "movsi-1", + "movsi", + "movsi+1", + "movhi-1", + "movhi", + "movstricthi", + "movstricthi+1", + "movqi", + "movstrictqi", + "movstrictqi+1", + "movsf", + "movsf+1", + "swapdf", + "movdf", + "movdf+1", + "movdi", + "zero_extendhisi2", + "zero_extendqihi2", + "zero_extendqisi2", + "zero_extendsidi2", + "extendsidi2", + "extendhisi2", + "extendqihi2", + "extendqisi2", + "extendsfdf2", + "truncdfsf2", + "truncdfsf2+1", + "fixuns_truncdfsi2", + "fixuns_truncsfsi2", + "fix_truncdfdi2", + "fix_truncsfdi2", + "fix_truncsfdi2+1", + "fix_truncdfsi2-1", + "fix_truncdfsi2", + "fix_truncsfsi2", + "fix_truncsfsi2+1", + "floatsisf2-1", + "floatsisf2", + "floatdisf2", + "floatsidf2", + "floatdidf2", + "floatdidf2+1", + "floatdidf2+2", + "adddi3-2", + "adddi3-1", + "adddi3", + "addsi3", + "addhi3", + "addqi3", + "addqi3+1", + "adddf3", + "addsf3", + "subdi3", + "subsi3", + "subhi3", + "subqi3", + "subdf3", + "subsf3", + "subsf3+1", + "mulhi3", + "mulhi3+1", + "mulsi3", + "mulsi3+1", + "muldf3", + "mulsf3", + "divqi3", + "udivqi3", + "divdf3", + "divsf3", + "divmodsi4", + "divmodhi4", + "udivmodsi4", + "udivmodhi4", + "andsi3", + "andhi3", + "andqi3", + "iorsi3", + "iorhi3", + "iorqi3", + "xorsi3", + "xorhi3", + "xorqi3", + "negdi2", + "negsi2", + "neghi2", + "negqi2", + "negsf2", + "negdf2", + "negdf2+1", + "abssf2", + "absdf2", + "absdf2+1", + "sqrtsf2", + "sqrtdf2", + "sqrtdf2+1", + "sindf2", + "sinsf2", + "sinsf2+1", + "cosdf2", + "cossf2", + "cossf2+1", + "one_cmplsi2", + "one_cmplhi2", + "one_cmplqi2", + "ashldi3", + "ashldi3_const_int", + "ashldi3_non_const_int", + "ashlsi3", + "ashlhi3", + "ashlqi3", + "ashrdi3", + "ashrdi3_const_int", + "ashrdi3_non_const_int", + "ashrsi3", + "ashrhi3", + "ashrqi3", + "lshrdi3", + "lshrdi3_const_int", + "lshrdi3_non_const_int", + "lshrsi3", + "lshrhi3", + "lshrqi3", + "rotlsi3", + "rotlhi3", + "rotlqi3", + "rotrsi3", + "rotrhi3", + "rotrqi3", + "rotrqi3+1", + "rotrqi3+2", + "rotrqi3+3", + "seq-3", + "seq-2", + "seq-1", + "seq", + "seq+1", + "sne", + "sne+1", + "sgt", + "sgt+1", + "sgtu", + "sgtu+1", + "slt", + "slt+1", + "sltu", + "sltu+1", + "sge", + "sge+1", + "sgeu", + "sgeu+1", + "sle", + "sle+1", + "sleu", + "sleu+1", + "beq", + "beq+1", + "bne", + "bne+1", + "bgt", + "bgt+1", + "bgtu", + "bgtu+1", + "blt", + "blt+1", + "bltu", + "bltu+1", + "bge", + "bge+1", + "bgeu", + "bgeu+1", + "ble", + "ble+1", + "bleu", + "bleu+1", + "bleu+2", + "bleu+3", + "bleu+4", + "bleu+5", + "bleu+6", + "jump-5", + "jump-4", + "jump-3", + "jump-2", + "jump-1", + "jump", + "indirect_jump", + "casesi", + "casesi+1", + "tablejump", + "call_pop", + "call_pop+1", + "call-1", + "call", + "call+1", + "call_value_pop-1", + "call_value_pop", + "call_value_pop+1", + "call_value-1", + "call_value", + "call_value+1", + "untyped_call-1", + "untyped_call", + "untyped_call+1", + "untyped_return-1", + "untyped_return", + "update_return", + "return", + "nop", + "movstrsi", + "movstrsi+1", + "cmpstrsi", + "cmpstrsi+1", + "ffssi2-1", + "ffssi2", + "ffssi2+1", + "ffshi2", + "ffshi2+1", + "ffshi2+2", + "ffshi2+3", + "ffshi2+4", + "ffshi2+5", + "strlensi-4", + "strlensi-3", + "strlensi-2", + "strlensi-1", + "strlensi", + "strlensi+1", + }; +char **insn_name_ptr = insn_name; + +const int insn_n_operands[] = + { + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 3, + 4, + 4, + 4, + 3, + 2, + 2, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 8, + 8, + 6, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 3, + 3, + 2, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 7, + 3, + 1, + 4, + 4, + 4, + 2, + 2, + 2, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 1, + 0, + 0, + 5, + 5, + 5, + 5, + 4, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + }; + +const int insn_n_dups[] = + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 7, + 7, + 5, + 5, + 1, + 1, + 2, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 2, + 2, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 2, + 3, + 3, + 3, + 2, + 0, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5, + 1, + }; + +char *const insn_operand_constraint[][MAX_RECOG_OPERANDS] = + { + { "rm", }, + { "", }, + { "rm", }, + { "", }, + { "qm", }, + { "", }, + { "f", "=a", }, + { "", "", }, + { "f", "=a", }, + { "", "", }, + { "mr,r", "ri,mr", }, + { "", "", }, + { "mr,r", "ri,mr", }, + { "", "", }, + { "q,mq", "qm,nq", }, + { "", "", }, + { "f,fm", "fm,f", "", "=a,a", }, + { "f", "rm", "", "=a", }, + { "rm", "f", "", "=a", }, + { "f", "fm", "", "=a", }, + { "fm", "f", "", "=a", }, + { "f", "f", "=a", }, + { "f,fm", "fm,f", "", "=a,a", }, + { "f", "rm", "", "=a", }, + { "rm", "f", "", "=a", }, + { "f", "f", "=a", }, + { "", "", }, + { "", "", }, + { "", "", "", }, + { "", "", "", }, + { "", "", "", }, + { "", "", "", }, + { "%ro", "ri", }, + { "%ro", "ri", }, + { "%qm", "qi", }, + { "=<", "g", }, + { "=<", "ri", }, + { "", "", }, + { "=g,r", "ri,m", }, + { "=<", "g", }, + { "=g,r", "ri,m", }, + { "+g,r", "ri,m", }, + { "=<", "q", }, + { "=q,*r,qm", "*g,q,qn", }, + { "+q,qm", "*g,qn", }, + { "=<,<", "gF,f", }, + { "=*rfm,*rf,f,!*rm", "*rf,*rfm,fG,fF", }, + { "=<,<", "gF,f", }, + { "f", "f", }, + { "=*rfm,*rf,f,!*rm", "*rf,*rfm,fG,fF", }, + { "=<", "roiF", }, + { "=r,rm", "m,riF", }, + { "=r", "rm", }, + { "=r", "qm", }, + { "=r", "qm", }, + { "=r", "0", }, + { "=r", "0", }, + { "=r", "rm", }, + { "=r", "qm", }, + { "=r", "qm", }, + { "=fm,f", "f,fm", }, + { "", "", }, + { "=f,m", "0,f", "m,m", }, + { "", "", "", "", "", "", "", "", }, + { "", "", "", "", "", "", "", "", }, + { "", "", "", "", "", "", }, + { "", "", "", "", "", "", }, + { "=rm", "f", "m", "m", "=&q", }, + { "=rm", "f", "m", "m", "=&q", }, + { "", "", "", "", "", }, + { "", "", "", "", "", }, + { "=rm", "f", "m", "m", "=&q", }, + { "=rm", "f", "m", "m", "=&q", }, + { "", "", }, + { "", "", }, + { "", "", }, + { "", "", }, + { "=f", "rm", }, + { "=f", "rm", }, + { "=f", "rm", }, + { "=f", "rm", }, + { "=&r,ro", "%0,0", "o,riF", }, + { "=?r,rm,r", "%r,0,0", "ri,ri,rm", }, + { "=rm,r", "%0,0", "ri,rm", }, + { "=qm,q", "%0,0", "qn,qmn", }, + { "=r", "p", }, + { "", "", "", }, + { "", "", "", }, + { "=&r,ro", "0,0", "o,riF", }, + { "=rm,r", "0,0", "ri,rm", }, + { "=rm,r", "0,0", "ri,rm", }, + { "=qm,q", "0,0", "qn,qmn", }, + { "", "", "", }, + { "", "", "", }, + { "=r", "%0", "r", }, + { "=r,r", "%0,rm", "g,i", }, + { "=r", "%0", "r", }, + { "=r,r", "%0,rm", "g,i", }, + { "=a", "%0", "qm", }, + { "", "", "", }, + { "", "", "", }, + { "=a", "0", "qm", }, + { "=a", "0", "qm", }, + { "", "", "", }, + { "", "", "", }, + { "=a", "0", "rm", "=&d", }, + { "=a", "0", "rm", "=&d", }, + { "=a", "0", "rm", "=&d", }, + { "=a", "0", "rm", "=&d", }, + { "=r,r,rm,r", "%rm,qm,0,0", "L,K,ri,rm", }, + { "=rm,r", "%0,0", "ri,rm", }, + { "=qm,q", "%0,0", "qn,qmn", }, + { "=rm,r", "%0,0", "ri,rm", }, + { "=rm,r", "%0,0", "ri,rm", }, + { "=qm,q", "%0,0", "qn,qmn", }, + { "=rm,r", "%0,0", "ri,rm", }, + { "=rm,r", "%0,0", "ri,rm", }, + { "=qm,q", "%0,0", "qn,qm", }, + { "=&ro", "0", }, + { "=rm", "0", }, + { "=rm", "0", }, + { "=qm", "0", }, + { "=f", "0", }, + { "=f", "0", }, + { "=f", "0", }, + { "=f", "0", }, + { "=f", "0", }, + { "=f", "0", }, + { "=f", "0", }, + { "=f", "0", }, + { "=f", "0", }, + { "=f", "0", }, + { "=f", "0", }, + { "=f", "0", }, + { "=f", "0", }, + { "=f", "0", }, + { "=f", "0", }, + { "=rm", "0", }, + { "=rm", "0", }, + { "=qm", "0", }, + { "", "", "", }, + { "=&r", "0", "J", }, + { "=&r", "0", "c", }, + { "=r,rm", "r,0", "M,cI", }, + { "=rm", "0", "cI", }, + { "=qm", "0", "cI", }, + { "", "", "", }, + { "=&r", "0", "J", }, + { "=&r", "0", "c", }, + { "=rm", "0", "cI", }, + { "=rm", "0", "cI", }, + { "=qm", "0", "cI", }, + { "", "", "", }, + { "=&r", "0", "J", }, + { "=&r", "0", "c", }, + { "=rm", "0", "cI", }, + { "=rm", "0", "cI", }, + { "=qm", "0", "cI", }, + { "=rm", "0", "cI", }, + { "=rm", "0", "cI", }, + { "=qm", "0", "cI", }, + { "=rm", "0", "cI", }, + { "=rm", "0", "cI", }, + { "=qm", "0", "cI", }, + { "+rm", "", "r", "n", }, + { "=rm", "r", "0", }, + { "=rm", "0", "r", }, + { "r", "r", }, + { "r", "n", "n", }, + { "rm", "n", "n", }, + { "", }, + { "=q", }, + { "", }, + { "=q", }, + { "", }, + { "=q", }, + { "", }, + { "=q", }, + { "", }, + { "=q", }, + { "", }, + { "=q", }, + { "", }, + { "=q", }, + { "", }, + { "=q", }, + { "", }, + { "=q", }, + { "", }, + { "=q", }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { "rm", }, + { "", "", "", "", "", "", "", }, + { "r", "", "=&r", }, + { "rm", }, + { "", "", "", "", }, + { "m", "g", "", "i", }, + { "", "g", "", "i", }, + { "", "", }, + { "m", "g", }, + { "", "g", }, + { "", "", "", "", "", }, + { "=rf", "m", "g", "", "i", }, + { "=rf", "", "g", "", "i", }, + { "", "", "", }, + { "=rf", "m", "g", }, + { "=rf", "", "g", }, + { "", "", "", }, + { "m", "o", "", }, + { "", "o", "", }, + { "", "", }, + { "m", }, + { 0 }, + { 0 }, + { "", "", "", "", "", }, + { "D", "S", "n", "i", "=&c", }, + { "", "", "", "", "", }, + { "=&r", "S", "D", "c", "i", }, + { "S", "D", "c", "i", }, + { "", "", "", }, + { "=&r", "rm", "=r", }, + { "", "", "", }, + { "=&r", "rm", "=r", }, + { "=f,f", "0,fm", "fm,0", "", }, + { "=f", "rm", "0", "", }, + { "=f,f", "fm,0", "0,f", "", }, + { "=f", "0", "rm", "", }, + { "=f,f", "0,f", "fm,0", "", }, + { "=f,f", "0,fm", "fm,0", "", }, + { "=f", "rm", "0", "", }, + { "=f", "0", "rm", "", }, + { "", "", "", "", }, + { "=&c", "D", "a", "i", }, + }; + +const enum machine_mode insn_operand_mode[][MAX_RECOG_OPERANDS] = + { + { SImode, }, + { SImode, }, + { HImode, }, + { HImode, }, + { QImode, }, + { QImode, }, + { SFmode, HImode, }, + { SFmode, HImode, }, + { DFmode, HImode, }, + { DFmode, HImode, }, + { SImode, SImode, }, + { SImode, SImode, }, + { HImode, HImode, }, + { HImode, HImode, }, + { QImode, QImode, }, + { QImode, QImode, }, + { DFmode, DFmode, VOIDmode, HImode, }, + { DFmode, SImode, VOIDmode, HImode, }, + { SImode, DFmode, VOIDmode, HImode, }, + { DFmode, SFmode, VOIDmode, HImode, }, + { SFmode, DFmode, VOIDmode, HImode, }, + { DFmode, DFmode, HImode, }, + { SFmode, SFmode, VOIDmode, HImode, }, + { SFmode, SImode, VOIDmode, HImode, }, + { SImode, SFmode, VOIDmode, HImode, }, + { SFmode, SFmode, HImode, }, + { DFmode, DFmode, }, + { SFmode, SFmode, }, + { DFmode, DFmode, HImode, }, + { DFmode, DFmode, HImode, }, + { SFmode, SFmode, HImode, }, + { SFmode, SFmode, HImode, }, + { SImode, SImode, }, + { HImode, HImode, }, + { QImode, QImode, }, + { SImode, SImode, }, + { SImode, SImode, }, + { SImode, SImode, }, + { SImode, SImode, }, + { HImode, HImode, }, + { HImode, HImode, }, + { HImode, HImode, }, + { QImode, QImode, }, + { QImode, QImode, }, + { QImode, QImode, }, + { SFmode, SFmode, }, + { SFmode, SFmode, }, + { DFmode, DFmode, }, + { DFmode, DFmode, }, + { DFmode, DFmode, }, + { DImode, DImode, }, + { DImode, DImode, }, + { SImode, HImode, }, + { HImode, QImode, }, + { SImode, QImode, }, + { DImode, SImode, }, + { DImode, SImode, }, + { SImode, HImode, }, + { HImode, QImode, }, + { SImode, QImode, }, + { DFmode, SFmode, }, + { SFmode, DFmode, }, + { SFmode, DFmode, SFmode, }, + { SImode, DFmode, VOIDmode, VOIDmode, VOIDmode, VOIDmode, VOIDmode, SImode, }, + { SImode, SFmode, VOIDmode, VOIDmode, VOIDmode, VOIDmode, VOIDmode, SImode, }, + { DImode, DFmode, VOIDmode, VOIDmode, VOIDmode, SImode, }, + { DImode, SFmode, VOIDmode, VOIDmode, VOIDmode, SImode, }, + { DImode, DFmode, SImode, SImode, SImode, }, + { DImode, SFmode, SImode, SImode, SImode, }, + { SImode, DFmode, VOIDmode, VOIDmode, SImode, }, + { SImode, SFmode, VOIDmode, VOIDmode, SImode, }, + { SImode, DFmode, SImode, SImode, SImode, }, + { SImode, SFmode, SImode, SImode, SImode, }, + { SFmode, SImode, }, + { SFmode, DImode, }, + { DFmode, SImode, }, + { DFmode, DImode, }, + { DFmode, DImode, }, + { SFmode, DImode, }, + { DFmode, SImode, }, + { SFmode, SImode, }, + { DImode, DImode, DImode, }, + { SImode, SImode, SImode, }, + { HImode, HImode, HImode, }, + { QImode, QImode, QImode, }, + { SImode, QImode, }, + { DFmode, DFmode, DFmode, }, + { SFmode, SFmode, SFmode, }, + { DImode, DImode, DImode, }, + { SImode, SImode, SImode, }, + { HImode, HImode, HImode, }, + { QImode, QImode, QImode, }, + { DFmode, DFmode, DFmode, }, + { SFmode, SFmode, SFmode, }, + { HImode, HImode, HImode, }, + { HImode, HImode, HImode, }, + { SImode, SImode, SImode, }, + { SImode, SImode, SImode, }, + { HImode, QImode, QImode, }, + { DFmode, DFmode, DFmode, }, + { SFmode, SFmode, SFmode, }, + { QImode, HImode, QImode, }, + { QImode, HImode, QImode, }, + { DFmode, DFmode, DFmode, }, + { SFmode, SFmode, SFmode, }, + { SImode, SImode, SImode, SImode, }, + { HImode, HImode, HImode, HImode, }, + { SImode, SImode, SImode, SImode, }, + { HImode, HImode, HImode, HImode, }, + { SImode, SImode, SImode, }, + { HImode, HImode, HImode, }, + { QImode, QImode, QImode, }, + { SImode, SImode, SImode, }, + { HImode, HImode, HImode, }, + { QImode, QImode, QImode, }, + { SImode, SImode, SImode, }, + { HImode, HImode, HImode, }, + { QImode, QImode, QImode, }, + { DImode, DImode, }, + { SImode, SImode, }, + { HImode, HImode, }, + { QImode, QImode, }, + { SFmode, SFmode, }, + { DFmode, DFmode, }, + { DFmode, SFmode, }, + { SFmode, SFmode, }, + { DFmode, DFmode, }, + { DFmode, SFmode, }, + { SFmode, SFmode, }, + { DFmode, DFmode, }, + { DFmode, SFmode, }, + { DFmode, DFmode, }, + { SFmode, SFmode, }, + { DFmode, SFmode, }, + { DFmode, DFmode, }, + { SFmode, SFmode, }, + { DFmode, SFmode, }, + { SImode, SImode, }, + { HImode, HImode, }, + { QImode, QImode, }, + { DImode, DImode, QImode, }, + { DImode, DImode, QImode, }, + { DImode, DImode, QImode, }, + { SImode, SImode, SImode, }, + { HImode, HImode, HImode, }, + { QImode, QImode, QImode, }, + { DImode, DImode, QImode, }, + { DImode, DImode, QImode, }, + { DImode, DImode, QImode, }, + { SImode, SImode, SImode, }, + { HImode, HImode, HImode, }, + { QImode, QImode, QImode, }, + { DImode, DImode, QImode, }, + { DImode, DImode, QImode, }, + { DImode, DImode, QImode, }, + { SImode, SImode, SImode, }, + { HImode, HImode, HImode, }, + { QImode, QImode, QImode, }, + { SImode, SImode, SImode, }, + { HImode, HImode, HImode, }, + { QImode, QImode, QImode, }, + { SImode, SImode, SImode, }, + { HImode, HImode, HImode, }, + { QImode, QImode, QImode, }, + { SImode, VOIDmode, SImode, SImode, }, + { SImode, SImode, SImode, }, + { SImode, SImode, SImode, }, + { SImode, SImode, }, + { SImode, SImode, SImode, }, + { QImode, SImode, SImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { QImode, }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { VOIDmode }, + { SImode, }, + { SImode, SImode, SImode, VOIDmode, VOIDmode, VOIDmode, SImode, }, + { SImode, VOIDmode, SImode, }, + { SImode, }, + { QImode, SImode, VOIDmode, SImode, }, + { QImode, SImode, VOIDmode, SImode, }, + { SImode, SImode, VOIDmode, SImode, }, + { QImode, SImode, }, + { QImode, SImode, }, + { SImode, SImode, }, + { VOIDmode, QImode, SImode, VOIDmode, SImode, }, + { VOIDmode, QImode, SImode, VOIDmode, SImode, }, + { VOIDmode, SImode, SImode, VOIDmode, SImode, }, + { VOIDmode, QImode, SImode, }, + { VOIDmode, QImode, SImode, }, + { VOIDmode, SImode, SImode, }, + { QImode, BLKmode, VOIDmode, }, + { QImode, DImode, VOIDmode, }, + { SImode, DImode, VOIDmode, }, + { BLKmode, VOIDmode, }, + { SImode, }, + { VOIDmode }, + { VOIDmode }, + { BLKmode, BLKmode, SImode, SImode, SImode, }, + { SImode, SImode, SImode, SImode, SImode, }, + { SImode, BLKmode, BLKmode, SImode, SImode, }, + { SImode, SImode, SImode, SImode, SImode, }, + { SImode, SImode, SImode, SImode, }, + { SImode, SImode, SImode, }, + { SImode, SImode, SImode, }, + { HImode, HImode, HImode, }, + { HImode, HImode, HImode, }, + { DFmode, DFmode, DFmode, DFmode, }, + { DFmode, SImode, DFmode, DFmode, }, + { DFmode, SFmode, DFmode, DFmode, }, + { DFmode, DFmode, SImode, DFmode, }, + { DFmode, DFmode, SFmode, DFmode, }, + { SFmode, SFmode, SFmode, SFmode, }, + { SFmode, SImode, SFmode, SFmode, }, + { SFmode, SFmode, SImode, SFmode, }, + { SImode, BLKmode, QImode, SImode, }, + { SImode, SImode, QImode, SImode, }, + }; + +const char insn_operand_strict_low[][MAX_RECOG_OPERANDS] = + { + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 1, 0, }, + { 0, 0, }, + { 0, 0, }, + { 1, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, 0, 0, 0, 0, 0, }, + { 0, 0, 0, 0, 0, 0, 0, 0, }, + { 0, 0, 0, 0, 0, 0, }, + { 0, 0, 0, 0, 0, 0, }, + { 0, 0, 0, 0, 0, }, + { 0, 0, 0, 0, 0, }, + { 0, 0, 0, 0, 0, }, + { 0, 0, 0, 0, 0, }, + { 0, 0, 0, 0, 0, }, + { 0, 0, 0, 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0, }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0, }, + { 0, 0, 0, 0, 0, 0, 0, }, + { 0, 0, 0, }, + { 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, }, + { 0, 0, 0, 0, 0, }, + { 0, 0, 0, 0, 0, }, + { 0, 0, 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, }, + { 0, }, + { 0 }, + { 0 }, + { 0, 0, 0, 0, 0, }, + { 0, 0, 0, 0, 0, }, + { 0, 0, 0, 0, 0, }, + { 0, 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + { 0, 0, 0, 0, }, + }; + +extern int nonimmediate_operand (); +extern int register_operand (); +extern int scratch_operand (); +extern int general_operand (); +extern int VOIDmode_compare_op (); +extern int push_operand (); +extern int memory_operand (); +extern int address_operand (); +extern int nonmemory_operand (); +extern int const_int_operand (); +extern int indirect_operand (); +extern int immediate_operand (); +extern int call_insn_operand (); +extern int symbolic_operand (); +extern int binary_387_op (); + +int (*const insn_operand_predicate[][MAX_RECOG_OPERANDS])() = + { + { nonimmediate_operand, }, + { nonimmediate_operand, }, + { nonimmediate_operand, }, + { nonimmediate_operand, }, + { nonimmediate_operand, }, + { nonimmediate_operand, }, + { register_operand, scratch_operand, }, + { register_operand, scratch_operand, }, + { register_operand, scratch_operand, }, + { register_operand, scratch_operand, }, + { nonimmediate_operand, general_operand, }, + { nonimmediate_operand, general_operand, }, + { nonimmediate_operand, general_operand, }, + { nonimmediate_operand, general_operand, }, + { nonimmediate_operand, general_operand, }, + { nonimmediate_operand, general_operand, }, + { nonimmediate_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, }, + { register_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, }, + { nonimmediate_operand, register_operand, VOIDmode_compare_op, scratch_operand, }, + { register_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, }, + { nonimmediate_operand, register_operand, VOIDmode_compare_op, scratch_operand, }, + { register_operand, register_operand, scratch_operand, }, + { nonimmediate_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, }, + { register_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, }, + { nonimmediate_operand, register_operand, VOIDmode_compare_op, scratch_operand, }, + { register_operand, register_operand, scratch_operand, }, + { register_operand, nonimmediate_operand, }, + { register_operand, nonimmediate_operand, }, + { register_operand, register_operand, scratch_operand, }, + { register_operand, register_operand, scratch_operand, }, + { register_operand, register_operand, scratch_operand, }, + { register_operand, register_operand, scratch_operand, }, + { general_operand, general_operand, }, + { general_operand, general_operand, }, + { general_operand, general_operand, }, + { push_operand, general_operand, }, + { push_operand, general_operand, }, + { general_operand, general_operand, }, + { general_operand, general_operand, }, + { push_operand, general_operand, }, + { general_operand, general_operand, }, + { general_operand, general_operand, }, + { push_operand, general_operand, }, + { general_operand, general_operand, }, + { general_operand, general_operand, }, + { push_operand, general_operand, }, + { general_operand, general_operand, }, + { push_operand, general_operand, }, + { register_operand, register_operand, }, + { general_operand, general_operand, }, + { push_operand, general_operand, }, + { general_operand, general_operand, }, + { general_operand, nonimmediate_operand, }, + { general_operand, nonimmediate_operand, }, + { general_operand, nonimmediate_operand, }, + { register_operand, register_operand, }, + { register_operand, register_operand, }, + { general_operand, nonimmediate_operand, }, + { general_operand, nonimmediate_operand, }, + { general_operand, nonimmediate_operand, }, + { general_operand, general_operand, }, + { nonimmediate_operand, register_operand, }, + { nonimmediate_operand, register_operand, memory_operand, }, + { general_operand, register_operand, 0, 0, 0, 0, 0, scratch_operand, }, + { general_operand, register_operand, 0, 0, 0, 0, 0, scratch_operand, }, + { general_operand, register_operand, 0, 0, 0, scratch_operand, }, + { general_operand, register_operand, 0, 0, 0, scratch_operand, }, + { general_operand, register_operand, memory_operand, memory_operand, scratch_operand, }, + { general_operand, register_operand, memory_operand, memory_operand, scratch_operand, }, + { general_operand, register_operand, 0, 0, scratch_operand, }, + { general_operand, register_operand, 0, 0, scratch_operand, }, + { general_operand, register_operand, memory_operand, memory_operand, scratch_operand, }, + { general_operand, register_operand, memory_operand, memory_operand, scratch_operand, }, + { register_operand, nonimmediate_operand, }, + { register_operand, nonimmediate_operand, }, + { register_operand, nonimmediate_operand, }, + { register_operand, nonimmediate_operand, }, + { register_operand, nonimmediate_operand, }, + { register_operand, nonimmediate_operand, }, + { register_operand, nonimmediate_operand, }, + { register_operand, nonimmediate_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { register_operand, address_operand, }, + { register_operand, nonimmediate_operand, nonimmediate_operand, }, + { register_operand, nonimmediate_operand, nonimmediate_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { register_operand, nonimmediate_operand, nonimmediate_operand, }, + { register_operand, nonimmediate_operand, nonimmediate_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, nonimmediate_operand, nonimmediate_operand, }, + { register_operand, nonimmediate_operand, nonimmediate_operand, }, + { register_operand, nonimmediate_operand, nonimmediate_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { register_operand, nonimmediate_operand, nonimmediate_operand, }, + { register_operand, nonimmediate_operand, nonimmediate_operand, }, + { register_operand, register_operand, general_operand, register_operand, }, + { register_operand, register_operand, general_operand, register_operand, }, + { register_operand, register_operand, general_operand, register_operand, }, + { register_operand, register_operand, general_operand, register_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, }, + { general_operand, general_operand, }, + { general_operand, general_operand, }, + { general_operand, general_operand, }, + { register_operand, general_operand, }, + { register_operand, general_operand, }, + { register_operand, general_operand, }, + { register_operand, general_operand, }, + { register_operand, general_operand, }, + { register_operand, general_operand, }, + { register_operand, general_operand, }, + { register_operand, general_operand, }, + { register_operand, general_operand, }, + { register_operand, register_operand, }, + { register_operand, register_operand, }, + { register_operand, register_operand, }, + { register_operand, register_operand, }, + { register_operand, register_operand, }, + { register_operand, register_operand, }, + { general_operand, general_operand, }, + { general_operand, general_operand, }, + { general_operand, general_operand, }, + { register_operand, register_operand, nonmemory_operand, }, + { register_operand, register_operand, const_int_operand, }, + { register_operand, register_operand, register_operand, }, + { general_operand, general_operand, nonmemory_operand, }, + { general_operand, general_operand, nonmemory_operand, }, + { general_operand, general_operand, nonmemory_operand, }, + { register_operand, register_operand, nonmemory_operand, }, + { register_operand, register_operand, const_int_operand, }, + { register_operand, register_operand, register_operand, }, + { general_operand, general_operand, nonmemory_operand, }, + { general_operand, general_operand, nonmemory_operand, }, + { general_operand, general_operand, nonmemory_operand, }, + { register_operand, register_operand, nonmemory_operand, }, + { register_operand, register_operand, const_int_operand, }, + { register_operand, register_operand, register_operand, }, + { general_operand, general_operand, nonmemory_operand, }, + { general_operand, general_operand, nonmemory_operand, }, + { general_operand, general_operand, nonmemory_operand, }, + { general_operand, general_operand, nonmemory_operand, }, + { general_operand, general_operand, nonmemory_operand, }, + { general_operand, general_operand, nonmemory_operand, }, + { general_operand, general_operand, nonmemory_operand, }, + { general_operand, general_operand, nonmemory_operand, }, + { general_operand, general_operand, nonmemory_operand, }, + { general_operand, 0, general_operand, const_int_operand, }, + { general_operand, general_operand, general_operand, }, + { general_operand, general_operand, general_operand, }, + { register_operand, general_operand, }, + { register_operand, const_int_operand, const_int_operand, }, + { general_operand, const_int_operand, const_int_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { register_operand, }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { general_operand, }, + { general_operand, general_operand, general_operand, 0, 0, 0, scratch_operand, }, + { register_operand, 0, scratch_operand, }, + { general_operand, }, + { indirect_operand, general_operand, 0, immediate_operand, }, + { call_insn_operand, general_operand, 0, immediate_operand, }, + { symbolic_operand, general_operand, 0, immediate_operand, }, + { indirect_operand, general_operand, }, + { call_insn_operand, general_operand, }, + { symbolic_operand, general_operand, }, + { 0, indirect_operand, general_operand, 0, immediate_operand, }, + { 0, call_insn_operand, general_operand, 0, immediate_operand, }, + { 0, symbolic_operand, general_operand, 0, immediate_operand, }, + { 0, indirect_operand, general_operand, }, + { 0, call_insn_operand, general_operand, }, + { 0, symbolic_operand, general_operand, }, + { indirect_operand, memory_operand, 0, }, + { call_insn_operand, memory_operand, 0, }, + { symbolic_operand, memory_operand, 0, }, + { memory_operand, 0, }, + { memory_operand, }, + { 0 }, + { 0 }, + { memory_operand, memory_operand, const_int_operand, const_int_operand, scratch_operand, }, + { address_operand, address_operand, const_int_operand, immediate_operand, scratch_operand, }, + { general_operand, general_operand, general_operand, general_operand, immediate_operand, }, + { general_operand, address_operand, address_operand, register_operand, immediate_operand, }, + { address_operand, address_operand, register_operand, immediate_operand, }, + { general_operand, general_operand, scratch_operand, }, + { register_operand, general_operand, scratch_operand, }, + { general_operand, general_operand, scratch_operand, }, + { register_operand, general_operand, scratch_operand, }, + { register_operand, nonimmediate_operand, nonimmediate_operand, binary_387_op, }, + { register_operand, general_operand, general_operand, binary_387_op, }, + { register_operand, general_operand, general_operand, binary_387_op, }, + { register_operand, general_operand, general_operand, binary_387_op, }, + { register_operand, general_operand, general_operand, binary_387_op, }, + { register_operand, nonimmediate_operand, nonimmediate_operand, binary_387_op, }, + { register_operand, general_operand, general_operand, binary_387_op, }, + { register_operand, general_operand, general_operand, binary_387_op, }, + { register_operand, general_operand, register_operand, immediate_operand, }, + { register_operand, address_operand, register_operand, immediate_operand, }, + }; + +const int insn_n_alternatives[] = + { + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 2, + 0, + 2, + 0, + 2, + 0, + 2, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 2, + 1, + 2, + 2, + 1, + 3, + 2, + 2, + 4, + 2, + 1, + 4, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 0, + 2, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 2, + 3, + 2, + 2, + 1, + 0, + 0, + 2, + 2, + 2, + 2, + 0, + 0, + 1, + 2, + 1, + 2, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 2, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 2, + 1, + 2, + 1, + 2, + 2, + 1, + 1, + 0, + 1, + }; diff --git a/gnu/usr.bin/cc/common/insn-peep.c b/gnu/usr.bin/cc/lib/insn-peep.c similarity index 100% rename from gnu/usr.bin/cc/common/insn-peep.c rename to gnu/usr.bin/cc/lib/insn-peep.c diff --git a/gnu/usr.bin/cc/lib/insn-recog.c b/gnu/usr.bin/cc/lib/insn-recog.c new file mode 100644 index 0000000000..bca21b305f --- /dev/null +++ b/gnu/usr.bin/cc/lib/insn-recog.c @@ -0,0 +1,6158 @@ +/* Generated automatically by the program `genrecog' +from the machine description file `md'. */ + +#include "config.h" +#include "rtl.h" +#include "insn-config.h" +#include "recog.h" +#include "real.h" +#include "output.h" +#include "flags.h" + + +/* `recog' contains a decision tree + that recognizes whether the rtx X0 is a valid instruction. + + recog returns -1 if the rtx is not valid. + If the rtx is valid, recog returns a nonnegative number + which is the insn code number for the pattern that matched. + This is the same as the order in the machine description of + the entry that matched. This number can be used as an index into + entry that matched. This number can be used as an index into various + insn_* tables, such as insn_templates, insn_outfun, and insn_n_operands + (found in insn-output.c). + + The third argument to recog is an optional pointer to an int. + If present, recog will accept a pattern if it matches except for + missing CLOBBER expressions at the end. In that case, the value + pointed to by the optional pointer will be set to the number of + CLOBBERs that need to be added (it should be initialized to zero by + the caller). If it is set nonzero, the caller should allocate a + PARALLEL of the appropriate size, copy the initial entries, and call + add_clobbers (found in insn-emit.c) to fill in the CLOBBERs.*/ + +rtx recog_operand[MAX_RECOG_OPERANDS]; + +rtx *recog_operand_loc[MAX_RECOG_OPERANDS]; + +rtx *recog_dup_loc[MAX_DUP_OPERANDS]; + +char recog_dup_num[MAX_DUP_OPERANDS]; + +#define operands recog_operand + +int +recog_1 (x0, insn, pnum_clobbers) + register rtx x0; + rtx insn; + int *pnum_clobbers; +{ + register rtx *ro = &recog_operand[0]; + register rtx x1, x2, x3, x4, x5, x6; + int tem; + + x1 = XEXP (x0, 1); + switch (GET_MODE (x1)) + { + case SImode: + if (nonimmediate_operand (x1, SImode)) + { + ro[0] = x1; + return 0; + } + break; + case HImode: + if (nonimmediate_operand (x1, HImode)) + { + ro[0] = x1; + return 2; + } + break; + case QImode: + if (nonimmediate_operand (x1, QImode)) + { + ro[0] = x1; + return 4; + } + break; + case SFmode: + if (pnum_clobbers != 0 && register_operand (x1, SFmode)) + { + ro[0] = x1; + if (TARGET_80387 && ! TARGET_IEEE_FP) + { + *pnum_clobbers = 1; + return 6; + } + } + break; + case DFmode: + if (pnum_clobbers != 0 && register_operand (x1, DFmode)) + { + ro[0] = x1; + if (TARGET_80387 && ! TARGET_IEEE_FP) + { + *pnum_clobbers = 1; + return 8; + } + } + } + switch (GET_CODE (x1)) + { + case COMPARE: + goto L30; + case ZERO_EXTRACT: + goto L813; + } + L52: + if (VOIDmode_compare_op (x1, VOIDmode)) + { + ro[2] = x1; + goto L82; + } + L125: + switch (GET_MODE (x1)) + { + case CCFPEQmode: + switch (GET_CODE (x1)) + { + case COMPARE: + goto L126; + } + break; + case SImode: + switch (GET_CODE (x1)) + { + case AND: + goto L187; + } + break; + case HImode: + switch (GET_CODE (x1)) + { + case AND: + goto L192; + } + break; + case QImode: + if (GET_CODE (x1) == AND && 1) + goto L197; + } + goto ret0; + + L30: + x2 = XEXP (x1, 0); + switch (GET_MODE (x2)) + { + case SImode: + if (nonimmediate_operand (x2, SImode)) + { + ro[0] = x2; + goto L31; + } + break; + case HImode: + if (nonimmediate_operand (x2, HImode)) + { + ro[0] = x2; + goto L36; + } + break; + case QImode: + if (nonimmediate_operand (x2, QImode)) + { + ro[0] = x2; + goto L41; + } + } + goto L52; + + L31: + x2 = XEXP (x1, 1); + if (general_operand (x2, SImode)) + { + ro[1] = x2; + if (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) + return 10; + } + goto L52; + + L36: + x2 = XEXP (x1, 1); + if (general_operand (x2, HImode)) + { + ro[1] = x2; + if (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) + return 12; + } + goto L52; + + L41: + x2 = XEXP (x1, 1); + if (general_operand (x2, QImode)) + { + ro[1] = x2; + if (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM) + return 14; + } + goto L52; + + L813: + x2 = XEXP (x1, 0); + switch (GET_MODE (x2)) + { + case SImode: + if (register_operand (x2, SImode)) + { + ro[0] = x2; + goto L814; + } + break; + case QImode: + if (general_operand (x2, QImode)) + { + ro[0] = x2; + goto L826; + } + } + goto L52; + + L814: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) != CONST_INT) + { + goto L52; + } + if (XWINT (x2, 0) == 1 && 1) + goto L815; + L820: + ro[1] = x2; + goto L821; + + L815: + x2 = XEXP (x1, 2); + if (general_operand (x2, SImode)) + { + ro[1] = x2; + if (GET_CODE (operands[1]) != CONST_INT) + return 167; + } + x2 = XEXP (x1, 1); + goto L820; + + L821: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == CONST_INT && 1) + { + ro[2] = x2; + return 168; + } + goto L52; + + L826: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && 1) + { + ro[1] = x2; + goto L827; + } + goto L52; + + L827: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == CONST_INT && 1) + { + ro[2] = x2; + if (GET_CODE (operands[0]) != MEM || ! MEM_VOLATILE_P (operands[0])) + return 169; + } + goto L52; + + L82: + x2 = XEXP (x1, 0); + switch (GET_MODE (x2)) + { + case DFmode: + switch (GET_CODE (x2)) + { + case FLOAT: + goto L83; + case FLOAT_EXTEND: + goto L113; + case SUBREG: + case REG: + case MEM: + if (nonimmediate_operand (x2, DFmode)) + { + ro[0] = x2; + goto L54; + } + } + L67: + if (register_operand (x2, DFmode)) + { + ro[0] = x2; + goto L68; + } + break; + case SFmode: + if (GET_CODE (x2) == FLOAT && 1) + goto L169; + if (nonimmediate_operand (x2, SFmode)) + { + ro[0] = x2; + goto L140; + } + L153: + if (register_operand (x2, SFmode)) + { + ro[0] = x2; + goto L154; + } + } + goto L125; + + L83: + x3 = XEXP (x2, 0); + if (nonimmediate_operand (x3, SImode)) + { + ro[0] = x3; + goto L84; + } + goto L125; + + L84: + x2 = XEXP (x1, 1); + if (pnum_clobbers != 0 && register_operand (x2, DFmode)) + { + ro[1] = x2; + if (TARGET_80387) + { + *pnum_clobbers = 1; + return 18; + } + } + goto L125; + + L113: + x3 = XEXP (x2, 0); + if (nonimmediate_operand (x3, SFmode)) + { + ro[0] = x3; + goto L114; + } + goto L125; + + L114: + x2 = XEXP (x1, 1); + if (pnum_clobbers != 0 && register_operand (x2, DFmode)) + { + ro[1] = x2; + if (TARGET_80387) + { + *pnum_clobbers = 1; + return 20; + } + } + goto L125; + + L54: + x2 = XEXP (x1, 1); + if (pnum_clobbers != 0 && nonimmediate_operand (x2, DFmode)) + { + ro[1] = x2; + if (TARGET_80387 + && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)) + { + *pnum_clobbers = 1; + return 16; + } + } + x2 = XEXP (x1, 0); + goto L67; + + L68: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) != DFmode) + { + goto L125; + } + switch (GET_CODE (x2)) + { + case FLOAT: + goto L69; + case FLOAT_EXTEND: + goto L99; + } + goto L125; + + L69: + x3 = XEXP (x2, 0); + if (pnum_clobbers != 0 && nonimmediate_operand (x3, SImode)) + { + ro[1] = x3; + if (TARGET_80387) + { + *pnum_clobbers = 1; + return 17; + } + } + goto L125; + + L99: + x3 = XEXP (x2, 0); + if (pnum_clobbers != 0 && nonimmediate_operand (x3, SFmode)) + { + ro[1] = x3; + if (TARGET_80387) + { + *pnum_clobbers = 1; + return 19; + } + } + goto L125; + + L169: + x3 = XEXP (x2, 0); + if (nonimmediate_operand (x3, SImode)) + { + ro[0] = x3; + goto L170; + } + goto L125; + + L170: + x2 = XEXP (x1, 1); + if (pnum_clobbers != 0 && register_operand (x2, SFmode)) + { + ro[1] = x2; + if (TARGET_80387) + { + *pnum_clobbers = 1; + return 24; + } + } + goto L125; + + L140: + x2 = XEXP (x1, 1); + if (pnum_clobbers != 0 && nonimmediate_operand (x2, SFmode)) + { + ro[1] = x2; + if (TARGET_80387 + && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)) + { + *pnum_clobbers = 1; + return 22; + } + } + x2 = XEXP (x1, 0); + goto L153; + + L154: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == SFmode && GET_CODE (x2) == FLOAT && 1) + goto L155; + goto L125; + + L155: + x3 = XEXP (x2, 0); + if (pnum_clobbers != 0 && nonimmediate_operand (x3, SImode)) + { + ro[1] = x3; + if (TARGET_80387) + { + *pnum_clobbers = 1; + return 23; + } + } + goto L125; + + L126: + x2 = XEXP (x1, 0); + switch (GET_MODE (x2)) + { + case DFmode: + if (register_operand (x2, DFmode)) + { + ro[0] = x2; + goto L127; + } + break; + case SFmode: + if (register_operand (x2, SFmode)) + { + ro[0] = x2; + goto L183; + } + } + goto ret0; + + L127: + x2 = XEXP (x1, 1); + if (pnum_clobbers != 0 && register_operand (x2, DFmode)) + { + ro[1] = x2; + if (TARGET_80387) + { + *pnum_clobbers = 1; + return 21; + } + } + goto ret0; + + L183: + x2 = XEXP (x1, 1); + if (pnum_clobbers != 0 && register_operand (x2, SFmode)) + { + ro[1] = x2; + if (TARGET_80387) + { + *pnum_clobbers = 1; + return 25; + } + } + goto ret0; + + L187: + x2 = XEXP (x1, 0); + if (general_operand (x2, SImode)) + { + ro[0] = x2; + goto L188; + } + goto ret0; + + L188: + x2 = XEXP (x1, 1); + if (general_operand (x2, SImode)) + { + ro[1] = x2; + return 32; + } + goto ret0; + + L192: + x2 = XEXP (x1, 0); + if (general_operand (x2, HImode)) + { + ro[0] = x2; + goto L193; + } + goto ret0; + + L193: + x2 = XEXP (x1, 1); + if (general_operand (x2, HImode)) + { + ro[1] = x2; + return 33; + } + goto ret0; + + L197: + x2 = XEXP (x1, 0); + if (general_operand (x2, QImode)) + { + ro[0] = x2; + goto L198; + } + goto ret0; + + L198: + x2 = XEXP (x1, 1); + if (general_operand (x2, QImode)) + { + ro[1] = x2; + return 34; + } + goto ret0; + ret0: return -1; +} + +int +recog_2 (x0, insn, pnum_clobbers) + register rtx x0; + rtx insn; + int *pnum_clobbers; +{ + register rtx *ro = &recog_operand[0]; + register rtx x1, x2, x3, x4, x5, x6; + int tem; + + x1 = XEXP (x0, 1); + x2 = XEXP (x1, 0); + switch (GET_CODE (x2)) + { + case EQ: + goto L882; + case NE: + goto L891; + case GT: + goto L900; + case GTU: + goto L909; + case LT: + goto L918; + case LTU: + goto L927; + case GE: + goto L936; + case GEU: + goto L945; + case LE: + goto L954; + case LEU: + goto L963; + } + goto ret0; + + L882: + x3 = XEXP (x2, 0); + if (GET_CODE (x3) == CC0 && 1) + goto L883; + goto ret0; + + L883: + x3 = XEXP (x2, 1); + if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) + goto L884; + goto ret0; + + L884: + x2 = XEXP (x1, 1); + switch (GET_CODE (x2)) + { + case LABEL_REF: + goto L885; + case PC: + goto L975; + } + goto ret0; + + L885: + x3 = XEXP (x2, 0); + ro[0] = x3; + goto L886; + + L886: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == PC && 1) + return 191; + goto ret0; + + L975: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == LABEL_REF && 1) + goto L976; + goto ret0; + + L976: + x3 = XEXP (x2, 0); + ro[0] = x3; + return 210; + + L891: + x3 = XEXP (x2, 0); + if (GET_CODE (x3) == CC0 && 1) + goto L892; + goto ret0; + + L892: + x3 = XEXP (x2, 1); + if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) + goto L893; + goto ret0; + + L893: + x2 = XEXP (x1, 1); + switch (GET_CODE (x2)) + { + case LABEL_REF: + goto L894; + case PC: + goto L984; + } + goto ret0; + + L894: + x3 = XEXP (x2, 0); + ro[0] = x3; + goto L895; + + L895: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == PC && 1) + return 193; + goto ret0; + + L984: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == LABEL_REF && 1) + goto L985; + goto ret0; + + L985: + x3 = XEXP (x2, 0); + ro[0] = x3; + return 211; + + L900: + x3 = XEXP (x2, 0); + if (GET_CODE (x3) == CC0 && 1) + goto L901; + goto ret0; + + L901: + x3 = XEXP (x2, 1); + if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) + goto L902; + goto ret0; + + L902: + x2 = XEXP (x1, 1); + switch (GET_CODE (x2)) + { + case LABEL_REF: + goto L903; + case PC: + goto L993; + } + goto ret0; + + L903: + x3 = XEXP (x2, 0); + ro[0] = x3; + goto L904; + + L904: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == PC && 1) + return 195; + goto ret0; + + L993: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == LABEL_REF && 1) + goto L994; + goto ret0; + + L994: + x3 = XEXP (x2, 0); + ro[0] = x3; + return 212; + + L909: + x3 = XEXP (x2, 0); + if (GET_CODE (x3) == CC0 && 1) + goto L910; + goto ret0; + + L910: + x3 = XEXP (x2, 1); + if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) + goto L911; + goto ret0; + + L911: + x2 = XEXP (x1, 1); + switch (GET_CODE (x2)) + { + case LABEL_REF: + goto L912; + case PC: + goto L1002; + } + goto ret0; + + L912: + x3 = XEXP (x2, 0); + ro[0] = x3; + goto L913; + + L913: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == PC && 1) + return 197; + goto ret0; + + L1002: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == LABEL_REF && 1) + goto L1003; + goto ret0; + + L1003: + x3 = XEXP (x2, 0); + ro[0] = x3; + return 213; + + L918: + x3 = XEXP (x2, 0); + if (GET_CODE (x3) == CC0 && 1) + goto L919; + goto ret0; + + L919: + x3 = XEXP (x2, 1); + if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) + goto L920; + goto ret0; + + L920: + x2 = XEXP (x1, 1); + switch (GET_CODE (x2)) + { + case LABEL_REF: + goto L921; + case PC: + goto L1011; + } + goto ret0; + + L921: + x3 = XEXP (x2, 0); + ro[0] = x3; + goto L922; + + L922: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == PC && 1) + return 199; + goto ret0; + + L1011: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == LABEL_REF && 1) + goto L1012; + goto ret0; + + L1012: + x3 = XEXP (x2, 0); + ro[0] = x3; + return 214; + + L927: + x3 = XEXP (x2, 0); + if (GET_CODE (x3) == CC0 && 1) + goto L928; + goto ret0; + + L928: + x3 = XEXP (x2, 1); + if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) + goto L929; + goto ret0; + + L929: + x2 = XEXP (x1, 1); + switch (GET_CODE (x2)) + { + case LABEL_REF: + goto L930; + case PC: + goto L1020; + } + goto ret0; + + L930: + x3 = XEXP (x2, 0); + ro[0] = x3; + goto L931; + + L931: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == PC && 1) + return 201; + goto ret0; + + L1020: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == LABEL_REF && 1) + goto L1021; + goto ret0; + + L1021: + x3 = XEXP (x2, 0); + ro[0] = x3; + return 215; + + L936: + x3 = XEXP (x2, 0); + if (GET_CODE (x3) == CC0 && 1) + goto L937; + goto ret0; + + L937: + x3 = XEXP (x2, 1); + if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) + goto L938; + goto ret0; + + L938: + x2 = XEXP (x1, 1); + switch (GET_CODE (x2)) + { + case LABEL_REF: + goto L939; + case PC: + goto L1029; + } + goto ret0; + + L939: + x3 = XEXP (x2, 0); + ro[0] = x3; + goto L940; + + L940: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == PC && 1) + return 203; + goto ret0; + + L1029: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == LABEL_REF && 1) + goto L1030; + goto ret0; + + L1030: + x3 = XEXP (x2, 0); + ro[0] = x3; + return 216; + + L945: + x3 = XEXP (x2, 0); + if (GET_CODE (x3) == CC0 && 1) + goto L946; + goto ret0; + + L946: + x3 = XEXP (x2, 1); + if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) + goto L947; + goto ret0; + + L947: + x2 = XEXP (x1, 1); + switch (GET_CODE (x2)) + { + case LABEL_REF: + goto L948; + case PC: + goto L1038; + } + goto ret0; + + L948: + x3 = XEXP (x2, 0); + ro[0] = x3; + goto L949; + + L949: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == PC && 1) + return 205; + goto ret0; + + L1038: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == LABEL_REF && 1) + goto L1039; + goto ret0; + + L1039: + x3 = XEXP (x2, 0); + ro[0] = x3; + return 217; + + L954: + x3 = XEXP (x2, 0); + if (GET_CODE (x3) == CC0 && 1) + goto L955; + goto ret0; + + L955: + x3 = XEXP (x2, 1); + if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) + goto L956; + goto ret0; + + L956: + x2 = XEXP (x1, 1); + switch (GET_CODE (x2)) + { + case LABEL_REF: + goto L957; + case PC: + goto L1047; + } + goto ret0; + + L957: + x3 = XEXP (x2, 0); + ro[0] = x3; + goto L958; + + L958: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == PC && 1) + return 207; + goto ret0; + + L1047: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == LABEL_REF && 1) + goto L1048; + goto ret0; + + L1048: + x3 = XEXP (x2, 0); + ro[0] = x3; + return 218; + + L963: + x3 = XEXP (x2, 0); + if (GET_CODE (x3) == CC0 && 1) + goto L964; + goto ret0; + + L964: + x3 = XEXP (x2, 1); + if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1) + goto L965; + goto ret0; + + L965: + x2 = XEXP (x1, 1); + switch (GET_CODE (x2)) + { + case LABEL_REF: + goto L966; + case PC: + goto L1056; + } + goto ret0; + + L966: + x3 = XEXP (x2, 0); + ro[0] = x3; + goto L967; + + L967: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == PC && 1) + return 209; + goto ret0; + + L1056: + x2 = XEXP (x1, 2); + if (GET_CODE (x2) == LABEL_REF && 1) + goto L1057; + goto ret0; + + L1057: + x3 = XEXP (x2, 0); + ro[0] = x3; + return 219; + ret0: return -1; +} + +int +recog_3 (x0, insn, pnum_clobbers) + register rtx x0; + rtx insn; + int *pnum_clobbers; +{ + register rtx *ro = &recog_operand[0]; + register rtx x1, x2, x3, x4, x5, x6; + int tem; + + x1 = XEXP (x0, 0); + switch (GET_MODE (x1)) + { + case SImode: + switch (GET_CODE (x1)) + { + case MEM: + if (push_operand (x1, SImode)) + { + ro[0] = x1; + goto L201; + } + break; + case ZERO_EXTRACT: + goto L792; + } + L208: + if (general_operand (x1, SImode)) + { + ro[0] = x1; + goto L257; + } + L431: + if (register_operand (x1, SImode)) + { + ro[0] = x1; + goto L432; + } + L439: + if (general_operand (x1, SImode)) + { + ro[0] = x1; + goto L440; + } + break; + case HImode: + if (GET_CODE (x1) == MEM && push_operand (x1, HImode)) + { + ro[0] = x1; + goto L212; + } + L214: + if (general_operand (x1, HImode)) + { + ro[0] = x1; + goto L261; + } + break; + case QImode: + if (GET_CODE (x1) == MEM && push_operand (x1, QImode)) + { + ro[0] = x1; + goto L222; + } + L224: + if (general_operand (x1, QImode)) + { + ro[0] = x1; + goto L427; + } + L829: + if (register_operand (x1, QImode)) + { + ro[0] = x1; + goto L830; + } + break; + case SFmode: + if (GET_CODE (x1) == MEM && push_operand (x1, SFmode)) + { + ro[0] = x1; + goto L232; + } + L234: + if (general_operand (x1, SFmode)) + { + ro[0] = x1; + goto L235; + } + L399: + if (register_operand (x1, SFmode)) + { + ro[0] = x1; + goto L400; + } + break; + case DFmode: + if (GET_CODE (x1) == MEM && push_operand (x1, DFmode)) + { + ro[0] = x1; + goto L238; + } + L247: + if (general_operand (x1, DFmode)) + { + ro[0] = x1; + goto L289; + } + L395: + if (register_operand (x1, DFmode)) + { + ro[0] = x1; + goto L396; + } + break; + case DImode: + if (GET_CODE (x1) == MEM && push_operand (x1, DImode)) + { + ro[0] = x1; + goto L251; + } + L253: + if (general_operand (x1, DImode)) + { + ro[0] = x1; + goto L412; + } + L268: + if (register_operand (x1, DImode)) + { + ro[0] = x1; + goto L269; + } + } + switch (GET_CODE (x1)) + { + case CC0: + goto L2; + case STRICT_LOW_PART: + goto L218; + case PC: + goto L1081; + } + L1147: + ro[0] = x1; + goto L1148; + L1236: + switch (GET_MODE (x1)) + { + case SImode: + if (register_operand (x1, SImode)) + { + ro[0] = x1; + goto L1237; + } + break; + case HImode: + if (register_operand (x1, HImode)) + { + ro[0] = x1; + goto L1252; + } + break; + case DFmode: + if (register_operand (x1, DFmode)) + { + ro[0] = x1; + goto L1258; + } + break; + case SFmode: + if (register_operand (x1, SFmode)) + { + ro[0] = x1; + goto L1287; + } + } + goto ret0; + + L201: + x1 = XEXP (x0, 1); + if (general_operand (x1, SImode)) + goto L205; + x1 = XEXP (x0, 0); + goto L208; + + L205: + ro[1] = x1; + if (! TARGET_486) + return 35; + L206: + ro[1] = x1; + if (TARGET_486) + return 36; + x1 = XEXP (x0, 0); + goto L208; + + L792: + x2 = XEXP (x1, 0); + if (GET_MODE (x2) == SImode && general_operand (x2, SImode)) + { + ro[0] = x2; + goto L793; + } + goto L1147; + + L793: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 1 && 1) + goto L794; + goto L1147; + + L794: + x2 = XEXP (x1, 2); + if (general_operand (x2, SImode)) + { + ro[2] = x2; + goto L795; + } + goto L1147; + + L795: + x1 = XEXP (x0, 1); + if (GET_CODE (x1) == CONST_INT && 1) + { + ro[3] = x1; + if (! TARGET_486 && GET_CODE (operands[2]) != CONST_INT) + return 164; + } + x1 = XEXP (x0, 0); + goto L1147; + + L257: + x1 = XEXP (x0, 1); + switch (GET_MODE (x1)) + { + case SImode: + switch (GET_CODE (x1)) + { + case ZERO_EXTEND: + goto L258; + case SIGN_EXTEND: + goto L278; + case PLUS: + goto L418; + } + } + if (general_operand (x1, SImode)) + { + ro[1] = x1; + return 38; + } + x1 = XEXP (x0, 0); + goto L431; + + L258: + x2 = XEXP (x1, 0); + switch (GET_MODE (x2)) + { + case HImode: + if (nonimmediate_operand (x2, HImode)) + { + ro[1] = x2; + return 52; + } + break; + case QImode: + if (nonimmediate_operand (x2, QImode)) + { + ro[1] = x2; + return 54; + } + } + x1 = XEXP (x0, 0); + goto L431; + + L278: + x2 = XEXP (x1, 0); + switch (GET_MODE (x2)) + { + case HImode: + if (nonimmediate_operand (x2, HImode)) + { + ro[1] = x2; + return 57; + } + break; + case QImode: + if (nonimmediate_operand (x2, QImode)) + { + ro[1] = x2; + return 59; + } + } + x1 = XEXP (x0, 0); + goto L431; + + L418: + x2 = XEXP (x1, 0); + if (general_operand (x2, SImode)) + { + ro[1] = x2; + goto L419; + } + x1 = XEXP (x0, 0); + goto L431; + + L419: + x2 = XEXP (x1, 1); + if (general_operand (x2, SImode)) + { + ro[2] = x2; + return 82; + } + x1 = XEXP (x0, 0); + goto L431; + + L432: + x1 = XEXP (x0, 1); + if (address_operand (x1, QImode)) + { + ro[1] = x1; + return 85; + } + x1 = XEXP (x0, 0); + goto L439; + + L440: + x1 = XEXP (x0, 1); + if (GET_MODE (x1) != SImode) + { + x1 = XEXP (x0, 0); + goto L1147; + } + switch (GET_CODE (x1)) + { + case MINUS: + goto L441; + case MULT: + goto L468; + case AND: + goto L541; + case IOR: + goto L556; + case XOR: + goto L799; + case NEG: + goto L590; + case NOT: + goto L667; + case ASHIFT: + goto L692; + case ASHIFTRT: + goto L720; + case LSHIFTRT: + goto L748; + case ROTATE: + goto L763; + case ROTATERT: + goto L778; + } + x1 = XEXP (x0, 0); + goto L1147; + + L441: + x2 = XEXP (x1, 0); + if (general_operand (x2, SImode)) + { + ro[1] = x2; + goto L442; + } + x1 = XEXP (x0, 0); + goto L1147; + + L442: + x2 = XEXP (x1, 1); + if (general_operand (x2, SImode)) + { + ro[2] = x2; + return 89; + } + x1 = XEXP (x0, 0); + goto L1147; + + L468: + x2 = XEXP (x1, 0); + if (general_operand (x2, SImode)) + { + ro[1] = x2; + goto L469; + } + x1 = XEXP (x0, 0); + goto L1147; + + L469: + x2 = XEXP (x1, 1); + if (general_operand (x2, SImode)) + goto L475; + x1 = XEXP (x0, 0); + goto L1147; + + L475: + ro[2] = x2; + if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x80) + return 96; + L476: + ro[2] = x2; + return 97; + + L541: + x2 = XEXP (x1, 0); + if (general_operand (x2, SImode)) + { + ro[1] = x2; + goto L542; + } + x1 = XEXP (x0, 0); + goto L1147; + + L542: + x2 = XEXP (x1, 1); + if (general_operand (x2, SImode)) + { + ro[2] = x2; + return 109; + } + x1 = XEXP (x0, 0); + goto L1147; + + L556: + x2 = XEXP (x1, 0); + if (general_operand (x2, SImode)) + { + ro[1] = x2; + goto L557; + } + x1 = XEXP (x0, 0); + goto L1147; + + L557: + x2 = XEXP (x1, 1); + if (general_operand (x2, SImode)) + { + ro[2] = x2; + return 112; + } + x1 = XEXP (x0, 0); + goto L1147; + + L799: + x2 = XEXP (x1, 0); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == ASHIFT && 1) + goto L800; + if (general_operand (x2, SImode)) + { + ro[1] = x2; + goto L807; + } + x1 = XEXP (x0, 0); + goto L1147; + + L800: + x3 = XEXP (x2, 0); + if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 1 && 1) + goto L801; + x1 = XEXP (x0, 0); + goto L1147; + + L801: + x3 = XEXP (x2, 1); + if (general_operand (x3, SImode)) + { + ro[1] = x3; + goto L802; + } + x1 = XEXP (x0, 0); + goto L1147; + + L802: + x2 = XEXP (x1, 1); + if (general_operand (x2, SImode)) + { + ro[2] = x2; + if (! TARGET_486 && GET_CODE (operands[1]) != CONST_INT) + return 165; + } + x1 = XEXP (x0, 0); + goto L1147; + + L807: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == ASHIFT && 1) + goto L808; + if (general_operand (x2, SImode)) + { + ro[2] = x2; + return 115; + } + x1 = XEXP (x0, 0); + goto L1147; + + L808: + x3 = XEXP (x2, 0); + if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 1 && 1) + goto L809; + x1 = XEXP (x0, 0); + goto L1147; + + L809: + x3 = XEXP (x2, 1); + if (general_operand (x3, SImode)) + { + ro[2] = x3; + if (! TARGET_486 && GET_CODE (operands[2]) != CONST_INT) + return 166; + } + x1 = XEXP (x0, 0); + goto L1147; + + L590: + x2 = XEXP (x1, 0); + if (general_operand (x2, SImode)) + { + ro[1] = x2; + return 119; + } + x1 = XEXP (x0, 0); + goto L1147; + + L667: + x2 = XEXP (x1, 0); + if (general_operand (x2, SImode)) + { + ro[1] = x2; + return 137; + } + x1 = XEXP (x0, 0); + goto L1147; + + L692: + x2 = XEXP (x1, 0); + if (general_operand (x2, SImode)) + { + ro[1] = x2; + goto L693; + } + x1 = XEXP (x0, 0); + goto L1147; + + L693: + x2 = XEXP (x1, 1); + if (nonmemory_operand (x2, SImode)) + { + ro[2] = x2; + return 143; + } + x1 = XEXP (x0, 0); + goto L1147; + + L720: + x2 = XEXP (x1, 0); + if (general_operand (x2, SImode)) + { + ro[1] = x2; + goto L721; + } + x1 = XEXP (x0, 0); + goto L1147; + + L721: + x2 = XEXP (x1, 1); + if (nonmemory_operand (x2, SImode)) + { + ro[2] = x2; + return 149; + } + x1 = XEXP (x0, 0); + goto L1147; + + L748: + x2 = XEXP (x1, 0); + if (general_operand (x2, SImode)) + { + ro[1] = x2; + goto L749; + } + x1 = XEXP (x0, 0); + goto L1147; + + L749: + x2 = XEXP (x1, 1); + if (nonmemory_operand (x2, SImode)) + { + ro[2] = x2; + return 155; + } + x1 = XEXP (x0, 0); + goto L1147; + + L763: + x2 = XEXP (x1, 0); + if (general_operand (x2, SImode)) + { + ro[1] = x2; + goto L764; + } + x1 = XEXP (x0, 0); + goto L1147; + + L764: + x2 = XEXP (x1, 1); + if (nonmemory_operand (x2, SImode)) + { + ro[2] = x2; + return 158; + } + x1 = XEXP (x0, 0); + goto L1147; + + L778: + x2 = XEXP (x1, 0); + if (general_operand (x2, SImode)) + { + ro[1] = x2; + goto L779; + } + x1 = XEXP (x0, 0); + goto L1147; + + L779: + x2 = XEXP (x1, 1); + if (nonmemory_operand (x2, SImode)) + { + ro[2] = x2; + return 161; + } + x1 = XEXP (x0, 0); + goto L1147; + + L212: + x1 = XEXP (x0, 1); + if (general_operand (x1, HImode)) + { + ro[1] = x1; + return 39; + } + x1 = XEXP (x0, 0); + goto L214; + + L261: + x1 = XEXP (x0, 1); + switch (GET_MODE (x1)) + { + case HImode: + switch (GET_CODE (x1)) + { + case ZERO_EXTEND: + goto L262; + case SIGN_EXTEND: + goto L282; + case PLUS: + goto L423; + case MINUS: + goto L446; + case AND: + goto L546; + case IOR: + goto L561; + case XOR: + goto L576; + case NEG: + goto L594; + case NOT: + goto L671; + case ASHIFT: + goto L697; + case ASHIFTRT: + goto L725; + case LSHIFTRT: + goto L753; + case ROTATE: + goto L768; + case ROTATERT: + goto L783; + } + break; + case SImode: + if (GET_CODE (x1) == MULT && 1) + goto L480; + } + if (general_operand (x1, HImode)) + { + ro[1] = x1; + return 40; + } + x1 = XEXP (x0, 0); + goto L1147; + + L262: + x2 = XEXP (x1, 0); + if (nonimmediate_operand (x2, QImode)) + { + ro[1] = x2; + return 53; + } + x1 = XEXP (x0, 0); + goto L1147; + + L282: + x2 = XEXP (x1, 0); + if (nonimmediate_operand (x2, QImode)) + { + ro[1] = x2; + return 58; + } + x1 = XEXP (x0, 0); + goto L1147; + + L423: + x2 = XEXP (x1, 0); + if (general_operand (x2, HImode)) + { + ro[1] = x2; + goto L424; + } + x1 = XEXP (x0, 0); + goto L1147; + + L424: + x2 = XEXP (x1, 1); + if (general_operand (x2, HImode)) + { + ro[2] = x2; + return 83; + } + x1 = XEXP (x0, 0); + goto L1147; + + L446: + x2 = XEXP (x1, 0); + if (general_operand (x2, HImode)) + { + ro[1] = x2; + goto L447; + } + x1 = XEXP (x0, 0); + goto L1147; + + L447: + x2 = XEXP (x1, 1); + if (general_operand (x2, HImode)) + { + ro[2] = x2; + return 90; + } + x1 = XEXP (x0, 0); + goto L1147; + + L546: + x2 = XEXP (x1, 0); + if (general_operand (x2, HImode)) + { + ro[1] = x2; + goto L547; + } + x1 = XEXP (x0, 0); + goto L1147; + + L547: + x2 = XEXP (x1, 1); + if (general_operand (x2, HImode)) + { + ro[2] = x2; + return 110; + } + x1 = XEXP (x0, 0); + goto L1147; + + L561: + x2 = XEXP (x1, 0); + if (general_operand (x2, HImode)) + { + ro[1] = x2; + goto L562; + } + x1 = XEXP (x0, 0); + goto L1147; + + L562: + x2 = XEXP (x1, 1); + if (general_operand (x2, HImode)) + { + ro[2] = x2; + return 113; + } + x1 = XEXP (x0, 0); + goto L1147; + + L576: + x2 = XEXP (x1, 0); + if (general_operand (x2, HImode)) + { + ro[1] = x2; + goto L577; + } + x1 = XEXP (x0, 0); + goto L1147; + + L577: + x2 = XEXP (x1, 1); + if (general_operand (x2, HImode)) + { + ro[2] = x2; + return 116; + } + x1 = XEXP (x0, 0); + goto L1147; + + L594: + x2 = XEXP (x1, 0); + if (general_operand (x2, HImode)) + { + ro[1] = x2; + return 120; + } + x1 = XEXP (x0, 0); + goto L1147; + + L671: + x2 = XEXP (x1, 0); + if (general_operand (x2, HImode)) + { + ro[1] = x2; + return 138; + } + x1 = XEXP (x0, 0); + goto L1147; + + L697: + x2 = XEXP (x1, 0); + if (general_operand (x2, HImode)) + { + ro[1] = x2; + goto L698; + } + x1 = XEXP (x0, 0); + goto L1147; + + L698: + x2 = XEXP (x1, 1); + if (nonmemory_operand (x2, HImode)) + { + ro[2] = x2; + return 144; + } + x1 = XEXP (x0, 0); + goto L1147; + + L725: + x2 = XEXP (x1, 0); + if (general_operand (x2, HImode)) + { + ro[1] = x2; + goto L726; + } + x1 = XEXP (x0, 0); + goto L1147; + + L726: + x2 = XEXP (x1, 1); + if (nonmemory_operand (x2, HImode)) + { + ro[2] = x2; + return 150; + } + x1 = XEXP (x0, 0); + goto L1147; + + L753: + x2 = XEXP (x1, 0); + if (general_operand (x2, HImode)) + { + ro[1] = x2; + goto L754; + } + x1 = XEXP (x0, 0); + goto L1147; + + L754: + x2 = XEXP (x1, 1); + if (nonmemory_operand (x2, HImode)) + { + ro[2] = x2; + return 156; + } + x1 = XEXP (x0, 0); + goto L1147; + + L768: + x2 = XEXP (x1, 0); + if (general_operand (x2, HImode)) + { + ro[1] = x2; + goto L769; + } + x1 = XEXP (x0, 0); + goto L1147; + + L769: + x2 = XEXP (x1, 1); + if (nonmemory_operand (x2, HImode)) + { + ro[2] = x2; + return 159; + } + x1 = XEXP (x0, 0); + goto L1147; + + L783: + x2 = XEXP (x1, 0); + if (general_operand (x2, HImode)) + { + ro[1] = x2; + goto L784; + } + x1 = XEXP (x0, 0); + goto L1147; + + L784: + x2 = XEXP (x1, 1); + if (nonmemory_operand (x2, HImode)) + { + ro[2] = x2; + return 162; + } + x1 = XEXP (x0, 0); + goto L1147; + + L480: + x2 = XEXP (x1, 0); + if (GET_MODE (x2) == HImode && GET_CODE (x2) == ZERO_EXTEND && 1) + goto L481; + if (general_operand (x2, HImode)) + { + ro[1] = x2; + goto L457; + } + x1 = XEXP (x0, 0); + goto L1147; + + L481: + x3 = XEXP (x2, 0); + if (nonimmediate_operand (x3, QImode)) + { + ro[1] = x3; + goto L482; + } + x1 = XEXP (x0, 0); + goto L1147; + + L482: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == HImode && GET_CODE (x2) == ZERO_EXTEND && 1) + goto L483; + x1 = XEXP (x0, 0); + goto L1147; + + L483: + x3 = XEXP (x2, 0); + if (nonimmediate_operand (x3, QImode)) + { + ro[2] = x3; + return 98; + } + x1 = XEXP (x0, 0); + goto L1147; + + L457: + x2 = XEXP (x1, 1); + if (general_operand (x2, HImode)) + goto L463; + x1 = XEXP (x0, 0); + goto L1147; + + L463: + ro[2] = x2; + if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x80) + return 94; + L464: + ro[2] = x2; + return 95; + + L222: + x1 = XEXP (x0, 1); + if (general_operand (x1, QImode)) + { + ro[1] = x1; + return 42; + } + x1 = XEXP (x0, 0); + goto L224; + + L427: + x1 = XEXP (x0, 1); + switch (GET_MODE (x1)) + { + case QImode: + switch (GET_CODE (x1)) + { + case PLUS: + goto L428; + case MINUS: + goto L451; + case DIV: + goto L487; + case UDIV: + goto L492; + case AND: + goto L551; + case IOR: + goto L566; + case XOR: + goto L581; + case NEG: + goto L598; + case NOT: + goto L675; + case ASHIFT: + goto L702; + case ASHIFTRT: + goto L730; + case LSHIFTRT: + goto L758; + case ROTATE: + goto L773; + case ROTATERT: + goto L788; + } + } + if (general_operand (x1, QImode)) + { + ro[1] = x1; + return 43; + } + x1 = XEXP (x0, 0); + goto L829; + + L428: + x2 = XEXP (x1, 0); + if (general_operand (x2, QImode)) + { + ro[1] = x2; + goto L429; + } + x1 = XEXP (x0, 0); + goto L829; + + L429: + x2 = XEXP (x1, 1); + if (general_operand (x2, QImode)) + { + ro[2] = x2; + return 84; + } + x1 = XEXP (x0, 0); + goto L829; + + L451: + x2 = XEXP (x1, 0); + if (general_operand (x2, QImode)) + { + ro[1] = x2; + goto L452; + } + x1 = XEXP (x0, 0); + goto L829; + + L452: + x2 = XEXP (x1, 1); + if (general_operand (x2, QImode)) + { + ro[2] = x2; + return 91; + } + x1 = XEXP (x0, 0); + goto L829; + + L487: + x2 = XEXP (x1, 0); + if (general_operand (x2, HImode)) + { + ro[1] = x2; + goto L488; + } + x1 = XEXP (x0, 0); + goto L829; + + L488: + x2 = XEXP (x1, 1); + if (general_operand (x2, QImode)) + { + ro[2] = x2; + return 101; + } + x1 = XEXP (x0, 0); + goto L829; + + L492: + x2 = XEXP (x1, 0); + if (general_operand (x2, HImode)) + { + ro[1] = x2; + goto L493; + } + x1 = XEXP (x0, 0); + goto L829; + + L493: + x2 = XEXP (x1, 1); + if (general_operand (x2, QImode)) + { + ro[2] = x2; + return 102; + } + x1 = XEXP (x0, 0); + goto L829; + + L551: + x2 = XEXP (x1, 0); + if (general_operand (x2, QImode)) + { + ro[1] = x2; + goto L552; + } + x1 = XEXP (x0, 0); + goto L829; + + L552: + x2 = XEXP (x1, 1); + if (general_operand (x2, QImode)) + { + ro[2] = x2; + return 111; + } + x1 = XEXP (x0, 0); + goto L829; + + L566: + x2 = XEXP (x1, 0); + if (general_operand (x2, QImode)) + { + ro[1] = x2; + goto L567; + } + x1 = XEXP (x0, 0); + goto L829; + + L567: + x2 = XEXP (x1, 1); + if (general_operand (x2, QImode)) + { + ro[2] = x2; + return 114; + } + x1 = XEXP (x0, 0); + goto L829; + + L581: + x2 = XEXP (x1, 0); + if (general_operand (x2, QImode)) + { + ro[1] = x2; + goto L582; + } + x1 = XEXP (x0, 0); + goto L829; + + L582: + x2 = XEXP (x1, 1); + if (general_operand (x2, QImode)) + { + ro[2] = x2; + return 117; + } + x1 = XEXP (x0, 0); + goto L829; + + L598: + x2 = XEXP (x1, 0); + if (general_operand (x2, QImode)) + { + ro[1] = x2; + return 121; + } + x1 = XEXP (x0, 0); + goto L829; + + L675: + x2 = XEXP (x1, 0); + if (general_operand (x2, QImode)) + { + ro[1] = x2; + return 139; + } + x1 = XEXP (x0, 0); + goto L829; + + L702: + x2 = XEXP (x1, 0); + if (general_operand (x2, QImode)) + { + ro[1] = x2; + goto L703; + } + x1 = XEXP (x0, 0); + goto L829; + + L703: + x2 = XEXP (x1, 1); + if (nonmemory_operand (x2, QImode)) + { + ro[2] = x2; + return 145; + } + x1 = XEXP (x0, 0); + goto L829; + + L730: + x2 = XEXP (x1, 0); + if (general_operand (x2, QImode)) + { + ro[1] = x2; + goto L731; + } + x1 = XEXP (x0, 0); + goto L829; + + L731: + x2 = XEXP (x1, 1); + if (nonmemory_operand (x2, QImode)) + { + ro[2] = x2; + return 151; + } + x1 = XEXP (x0, 0); + goto L829; + + L758: + x2 = XEXP (x1, 0); + if (general_operand (x2, QImode)) + { + ro[1] = x2; + goto L759; + } + x1 = XEXP (x0, 0); + goto L829; + + L759: + x2 = XEXP (x1, 1); + if (nonmemory_operand (x2, QImode)) + { + ro[2] = x2; + return 157; + } + x1 = XEXP (x0, 0); + goto L829; + + L773: + x2 = XEXP (x1, 0); + if (general_operand (x2, QImode)) + { + ro[1] = x2; + goto L774; + } + x1 = XEXP (x0, 0); + goto L829; + + L774: + x2 = XEXP (x1, 1); + if (nonmemory_operand (x2, QImode)) + { + ro[2] = x2; + return 160; + } + x1 = XEXP (x0, 0); + goto L829; + + L788: + x2 = XEXP (x1, 0); + if (general_operand (x2, QImode)) + { + ro[1] = x2; + goto L789; + } + x1 = XEXP (x0, 0); + goto L829; + + L789: + x2 = XEXP (x1, 1); + if (nonmemory_operand (x2, QImode)) + { + ro[2] = x2; + return 163; + } + x1 = XEXP (x0, 0); + goto L829; + + L830: + x1 = XEXP (x0, 1); + if (GET_MODE (x1) != QImode) + { + x1 = XEXP (x0, 0); + goto L1147; + } + switch (GET_CODE (x1)) + { + case EQ: + goto L831; + case NE: + goto L836; + case GT: + goto L841; + case GTU: + goto L846; + case LT: + goto L851; + case LTU: + goto L856; + case GE: + goto L861; + case GEU: + goto L866; + case LE: + goto L871; + case LEU: + goto L876; + } + x1 = XEXP (x0, 0); + goto L1147; + + L831: + x2 = XEXP (x1, 0); + if (GET_CODE (x2) == CC0 && 1) + goto L832; + x1 = XEXP (x0, 0); + goto L1147; + + L832: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) + return 171; + x1 = XEXP (x0, 0); + goto L1147; + + L836: + x2 = XEXP (x1, 0); + if (GET_CODE (x2) == CC0 && 1) + goto L837; + x1 = XEXP (x0, 0); + goto L1147; + + L837: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) + return 173; + x1 = XEXP (x0, 0); + goto L1147; + + L841: + x2 = XEXP (x1, 0); + if (GET_CODE (x2) == CC0 && 1) + goto L842; + x1 = XEXP (x0, 0); + goto L1147; + + L842: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) + return 175; + x1 = XEXP (x0, 0); + goto L1147; + + L846: + x2 = XEXP (x1, 0); + if (GET_CODE (x2) == CC0 && 1) + goto L847; + x1 = XEXP (x0, 0); + goto L1147; + + L847: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) + return 177; + x1 = XEXP (x0, 0); + goto L1147; + + L851: + x2 = XEXP (x1, 0); + if (GET_CODE (x2) == CC0 && 1) + goto L852; + x1 = XEXP (x0, 0); + goto L1147; + + L852: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) + return 179; + x1 = XEXP (x0, 0); + goto L1147; + + L856: + x2 = XEXP (x1, 0); + if (GET_CODE (x2) == CC0 && 1) + goto L857; + x1 = XEXP (x0, 0); + goto L1147; + + L857: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) + return 181; + x1 = XEXP (x0, 0); + goto L1147; + + L861: + x2 = XEXP (x1, 0); + if (GET_CODE (x2) == CC0 && 1) + goto L862; + x1 = XEXP (x0, 0); + goto L1147; + + L862: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) + return 183; + x1 = XEXP (x0, 0); + goto L1147; + + L866: + x2 = XEXP (x1, 0); + if (GET_CODE (x2) == CC0 && 1) + goto L867; + x1 = XEXP (x0, 0); + goto L1147; + + L867: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) + return 185; + x1 = XEXP (x0, 0); + goto L1147; + + L871: + x2 = XEXP (x1, 0); + if (GET_CODE (x2) == CC0 && 1) + goto L872; + x1 = XEXP (x0, 0); + goto L1147; + + L872: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) + return 187; + x1 = XEXP (x0, 0); + goto L1147; + + L876: + x2 = XEXP (x1, 0); + if (GET_CODE (x2) == CC0 && 1) + goto L877; + x1 = XEXP (x0, 0); + goto L1147; + + L877: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) + return 189; + x1 = XEXP (x0, 0); + goto L1147; + + L232: + x1 = XEXP (x0, 1); + if (general_operand (x1, SFmode)) + { + ro[1] = x1; + return 45; + } + x1 = XEXP (x0, 0); + goto L234; + + L235: + x1 = XEXP (x0, 1); + if (general_operand (x1, SFmode)) + { + ro[1] = x1; + return 46; + } + x1 = XEXP (x0, 0); + goto L399; + + L400: + x1 = XEXP (x0, 1); + if (GET_MODE (x1) != SFmode) + { + x1 = XEXP (x0, 0); + goto L1147; + } + switch (GET_CODE (x1)) + { + case FLOAT: + goto L401; + case NEG: + goto L602; + case ABS: + goto L615; + case SQRT: + goto L628; + case UNSPEC: + if (XINT (x1, 1) == 1 && XVECLEN (x1, 0) == 1 && 1) + goto L645; + if (XINT (x1, 1) == 2 && XVECLEN (x1, 0) == 1 && 1) + goto L658; + } + x1 = XEXP (x0, 0); + goto L1147; + + L401: + x2 = XEXP (x1, 0); + switch (GET_MODE (x2)) + { + case DImode: + if (nonimmediate_operand (x2, DImode)) + { + ro[1] = x2; + if (TARGET_80387) + return 78; + } + break; + case SImode: + if (nonimmediate_operand (x2, SImode)) + { + ro[1] = x2; + if (TARGET_80387) + return 80; + } + } + x1 = XEXP (x0, 0); + goto L1147; + + L602: + x2 = XEXP (x1, 0); + if (general_operand (x2, SFmode)) + { + ro[1] = x2; + if (TARGET_80387) + return 122; + } + x1 = XEXP (x0, 0); + goto L1147; + + L615: + x2 = XEXP (x1, 0); + if (general_operand (x2, SFmode)) + { + ro[1] = x2; + if (TARGET_80387) + return 125; + } + x1 = XEXP (x0, 0); + goto L1147; + + L628: + x2 = XEXP (x1, 0); + if (general_operand (x2, SFmode)) + { + ro[1] = x2; + if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) + return 128; + } + x1 = XEXP (x0, 0); + goto L1147; + + L645: + x2 = XVECEXP (x1, 0, 0); + if (register_operand (x2, SFmode)) + { + ro[1] = x2; + if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) + return 132; + } + x1 = XEXP (x0, 0); + goto L1147; + + L658: + x2 = XVECEXP (x1, 0, 0); + if (register_operand (x2, SFmode)) + { + ro[1] = x2; + if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) + return 135; + } + x1 = XEXP (x0, 0); + goto L1147; + + L238: + x1 = XEXP (x0, 1); + if (general_operand (x1, DFmode)) + { + ro[1] = x1; + return 47; + } + x1 = XEXP (x0, 0); + goto L247; + + L289: + x1 = XEXP (x0, 1); + if (GET_MODE (x1) == DFmode && GET_CODE (x1) == FLOAT_EXTEND && 1) + goto L290; + if (general_operand (x1, DFmode)) + { + ro[1] = x1; + return 49; + } + x1 = XEXP (x0, 0); + goto L395; + + L290: + x2 = XEXP (x1, 0); + if (general_operand (x2, SFmode)) + { + ro[1] = x2; + if (TARGET_80387) + return 60; + } + x1 = XEXP (x0, 0); + goto L395; + + L396: + x1 = XEXP (x0, 1); + if (GET_MODE (x1) != DFmode) + { + x1 = XEXP (x0, 0); + goto L1147; + } + switch (GET_CODE (x1)) + { + case FLOAT: + goto L397; + case NEG: + goto L610; + case ABS: + goto L623; + case SQRT: + goto L636; + case UNSPEC: + if (XINT (x1, 1) == 1 && XVECLEN (x1, 0) == 1 && 1) + goto L649; + if (XINT (x1, 1) == 2 && XVECLEN (x1, 0) == 1 && 1) + goto L662; + } + x1 = XEXP (x0, 0); + goto L1147; + + L397: + x2 = XEXP (x1, 0); + switch (GET_MODE (x2)) + { + case DImode: + if (nonimmediate_operand (x2, DImode)) + { + ro[1] = x2; + if (TARGET_80387) + return 77; + } + break; + case SImode: + if (nonimmediate_operand (x2, SImode)) + { + ro[1] = x2; + if (TARGET_80387) + return 79; + } + } + x1 = XEXP (x0, 0); + goto L1147; + + L610: + x2 = XEXP (x1, 0); + if (GET_MODE (x2) == DFmode && GET_CODE (x2) == FLOAT_EXTEND && 1) + goto L611; + if (general_operand (x2, DFmode)) + { + ro[1] = x2; + if (TARGET_80387) + return 123; + } + x1 = XEXP (x0, 0); + goto L1147; + + L611: + x3 = XEXP (x2, 0); + if (general_operand (x3, SFmode)) + { + ro[1] = x3; + if (TARGET_80387) + return 124; + } + x1 = XEXP (x0, 0); + goto L1147; + + L623: + x2 = XEXP (x1, 0); + if (GET_MODE (x2) == DFmode && GET_CODE (x2) == FLOAT_EXTEND && 1) + goto L624; + if (general_operand (x2, DFmode)) + { + ro[1] = x2; + if (TARGET_80387) + return 126; + } + x1 = XEXP (x0, 0); + goto L1147; + + L624: + x3 = XEXP (x2, 0); + if (general_operand (x3, SFmode)) + { + ro[1] = x3; + if (TARGET_80387) + return 127; + } + x1 = XEXP (x0, 0); + goto L1147; + + L636: + x2 = XEXP (x1, 0); + if (GET_MODE (x2) == DFmode && GET_CODE (x2) == FLOAT_EXTEND && 1) + goto L637; + if (general_operand (x2, DFmode)) + { + ro[1] = x2; + if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) + return 129; + } + x1 = XEXP (x0, 0); + goto L1147; + + L637: + x3 = XEXP (x2, 0); + if (general_operand (x3, SFmode)) + { + ro[1] = x3; + if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) + return 130; + } + x1 = XEXP (x0, 0); + goto L1147; + + L649: + x2 = XVECEXP (x1, 0, 0); + if (GET_MODE (x2) != DFmode) + { + x1 = XEXP (x0, 0); + goto L1147; + } + if (GET_CODE (x2) == FLOAT_EXTEND && 1) + goto L650; + if (register_operand (x2, DFmode)) + { + ro[1] = x2; + if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) + return 131; + } + x1 = XEXP (x0, 0); + goto L1147; + + L650: + x3 = XEXP (x2, 0); + if (register_operand (x3, SFmode)) + { + ro[1] = x3; + if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) + return 133; + } + x1 = XEXP (x0, 0); + goto L1147; + + L662: + x2 = XVECEXP (x1, 0, 0); + if (GET_MODE (x2) != DFmode) + { + x1 = XEXP (x0, 0); + goto L1147; + } + if (GET_CODE (x2) == FLOAT_EXTEND && 1) + goto L663; + if (register_operand (x2, DFmode)) + { + ro[1] = x2; + if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) + return 134; + } + x1 = XEXP (x0, 0); + goto L1147; + + L663: + x3 = XEXP (x2, 0); + if (register_operand (x3, SFmode)) + { + ro[1] = x3; + if (TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)) + return 136; + } + x1 = XEXP (x0, 0); + goto L1147; + + L251: + x1 = XEXP (x0, 1); + if (general_operand (x1, DImode)) + { + ro[1] = x1; + return 50; + } + x1 = XEXP (x0, 0); + goto L253; + + L412: + x1 = XEXP (x0, 1); + switch (GET_MODE (x1)) + { + case DImode: + switch (GET_CODE (x1)) + { + case PLUS: + goto L413; + case MINUS: + goto L436; + case NEG: + goto L586; + } + } + if (general_operand (x1, DImode)) + { + ro[1] = x1; + return 51; + } + x1 = XEXP (x0, 0); + goto L268; + + L413: + x2 = XEXP (x1, 0); + if (general_operand (x2, DImode)) + { + ro[1] = x2; + goto L414; + } + x1 = XEXP (x0, 0); + goto L268; + + L414: + x2 = XEXP (x1, 1); + if (general_operand (x2, DImode)) + { + ro[2] = x2; + return 81; + } + x1 = XEXP (x0, 0); + goto L268; + + L436: + x2 = XEXP (x1, 0); + if (general_operand (x2, DImode)) + { + ro[1] = x2; + goto L437; + } + x1 = XEXP (x0, 0); + goto L268; + + L437: + x2 = XEXP (x1, 1); + if (general_operand (x2, DImode)) + { + ro[2] = x2; + return 88; + } + x1 = XEXP (x0, 0); + goto L268; + + L586: + x2 = XEXP (x1, 0); + if (general_operand (x2, DImode)) + { + ro[1] = x2; + return 118; + } + x1 = XEXP (x0, 0); + goto L268; + + L269: + x1 = XEXP (x0, 1); + if (GET_MODE (x1) != DImode) + { + x1 = XEXP (x0, 0); + goto L1147; + } + switch (GET_CODE (x1)) + { + case ZERO_EXTEND: + goto L270; + case SIGN_EXTEND: + goto L274; + case ASHIFT: + goto L679; + case ASHIFTRT: + goto L707; + case LSHIFTRT: + goto L735; + } + x1 = XEXP (x0, 0); + goto L1147; + + L270: + x2 = XEXP (x1, 0); + if (register_operand (x2, SImode)) + { + ro[1] = x2; + return 55; + } + x1 = XEXP (x0, 0); + goto L1147; + + L274: + x2 = XEXP (x1, 0); + if (register_operand (x2, SImode)) + { + ro[1] = x2; + return 56; + } + x1 = XEXP (x0, 0); + goto L1147; + + L679: + x2 = XEXP (x1, 0); + if (register_operand (x2, DImode)) + { + ro[1] = x2; + goto L680; + } + x1 = XEXP (x0, 0); + goto L1147; + + L680: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && 1) + { + ro[2] = x2; + return 141; + } + x1 = XEXP (x0, 0); + goto L1147; + + L707: + x2 = XEXP (x1, 0); + if (register_operand (x2, DImode)) + { + ro[1] = x2; + goto L708; + } + x1 = XEXP (x0, 0); + goto L1147; + + L708: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && 1) + { + ro[2] = x2; + return 147; + } + x1 = XEXP (x0, 0); + goto L1147; + + L735: + x2 = XEXP (x1, 0); + if (register_operand (x2, DImode)) + { + ro[1] = x2; + goto L736; + } + x1 = XEXP (x0, 0); + goto L1147; + + L736: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && 1) + { + ro[2] = x2; + return 153; + } + x1 = XEXP (x0, 0); + goto L1147; + L2: + tem = recog_1 (x0, insn, pnum_clobbers); + if (tem >= 0) return tem; + x1 = XEXP (x0, 0); + goto L1147; + + L218: + x2 = XEXP (x1, 0); + switch (GET_MODE (x2)) + { + case HImode: + if (general_operand (x2, HImode)) + { + ro[0] = x2; + goto L219; + } + break; + case QImode: + if (general_operand (x2, QImode)) + { + ro[0] = x2; + goto L229; + } + } + goto L1147; + + L219: + x1 = XEXP (x0, 1); + if (general_operand (x1, HImode)) + { + ro[1] = x1; + return 41; + } + x1 = XEXP (x0, 0); + goto L1147; + + L229: + x1 = XEXP (x0, 1); + if (general_operand (x1, QImode)) + { + ro[1] = x1; + return 44; + } + x1 = XEXP (x0, 0); + goto L1147; + + L1081: + x1 = XEXP (x0, 1); + switch (GET_CODE (x1)) + { + case MINUS: + if (GET_MODE (x1) == SImode && 1) + goto L1082; + break; + case IF_THEN_ELSE: + goto L881; + case LABEL_REF: + goto L1061; + } + L1064: + if (general_operand (x1, SImode)) + { + ro[0] = x1; + return 221; + } + x1 = XEXP (x0, 0); + goto L1147; + + L1082: + x2 = XEXP (x1, 0); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == REG && XINT (x2, 0) == 3 && 1) + goto L1083; + x1 = XEXP (x0, 0); + goto L1147; + + L1083: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == MEM && 1) + goto L1084; + x1 = XEXP (x0, 0); + goto L1147; + + L1084: + x3 = XEXP (x2, 0); + if (GET_MODE (x3) == SImode && GET_CODE (x3) == PLUS && 1) + goto L1085; + x1 = XEXP (x0, 0); + goto L1147; + + L1085: + x4 = XEXP (x3, 0); + if (GET_MODE (x4) == SImode && GET_CODE (x4) == MULT && 1) + goto L1086; + x1 = XEXP (x0, 0); + goto L1147; + + L1086: + x5 = XEXP (x4, 0); + if (register_operand (x5, SImode)) + { + ro[0] = x5; + goto L1087; + } + x1 = XEXP (x0, 0); + goto L1147; + + L1087: + x5 = XEXP (x4, 1); + if (GET_CODE (x5) == CONST_INT && XWINT (x5, 0) == 4 && 1) + goto L1088; + x1 = XEXP (x0, 0); + goto L1147; + + L1088: + x4 = XEXP (x3, 1); + if (GET_CODE (x4) == LABEL_REF && 1) + goto L1089; + x1 = XEXP (x0, 0); + goto L1147; + + L1089: + x5 = XEXP (x4, 0); + if (pnum_clobbers != 0 && 1) + { + ro[1] = x5; + *pnum_clobbers = 1; + return 223; + } + x1 = XEXP (x0, 0); + goto L1147; + L881: + tem = recog_2 (x0, insn, pnum_clobbers); + if (tem >= 0) return tem; + x1 = XEXP (x0, 0); + goto L1147; + + L1061: + x2 = XEXP (x1, 0); + ro[0] = x2; + return 220; + + L1148: + x1 = XEXP (x0, 1); + if (GET_CODE (x1) == CALL && 1) + goto L1149; + x1 = XEXP (x0, 0); + goto L1236; + + L1149: + x2 = XEXP (x1, 0); + if (call_insn_operand (x2, QImode)) + { + ro[1] = x2; + goto L1150; + } + L1154: + if (GET_MODE (x2) == QImode && GET_CODE (x2) == MEM && 1) + goto L1155; + x1 = XEXP (x0, 0); + goto L1236; + + L1150: + x2 = XEXP (x1, 1); + if (general_operand (x2, SImode)) + { + ro[2] = x2; + return 235; + } + x2 = XEXP (x1, 0); + goto L1154; + + L1155: + x3 = XEXP (x2, 0); + if (symbolic_operand (x3, SImode)) + { + ro[1] = x3; + goto L1156; + } + x1 = XEXP (x0, 0); + goto L1236; + + L1156: + x2 = XEXP (x1, 1); + if (general_operand (x2, SImode)) + { + ro[2] = x2; + if (!HALF_PIC_P ()) + return 236; + } + x1 = XEXP (x0, 0); + goto L1236; + + L1237: + x1 = XEXP (x0, 1); + if (GET_MODE (x1) == SImode && GET_CODE (x1) == PLUS && 1) + goto L1238; + goto ret0; + + L1238: + x2 = XEXP (x1, 0); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == FFS && 1) + goto L1239; + goto ret0; + + L1239: + x3 = XEXP (x2, 0); + if (general_operand (x3, SImode)) + { + ro[1] = x3; + goto L1240; + } + goto ret0; + + L1240: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == -1 && pnum_clobbers != 0 && 1) + { + *pnum_clobbers = 1; + return 250; + } + goto ret0; + + L1252: + x1 = XEXP (x0, 1); + if (GET_MODE (x1) == HImode && GET_CODE (x1) == PLUS && 1) + goto L1253; + goto ret0; + + L1253: + x2 = XEXP (x1, 0); + if (GET_MODE (x2) == HImode && GET_CODE (x2) == FFS && 1) + goto L1254; + goto ret0; + + L1254: + x3 = XEXP (x2, 0); + if (general_operand (x3, HImode)) + { + ro[1] = x3; + goto L1255; + } + goto ret0; + + L1255: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == -1 && pnum_clobbers != 0 && 1) + { + *pnum_clobbers = 1; + return 252; + } + goto ret0; + + L1258: + x1 = XEXP (x0, 1); + if (binary_387_op (x1, DFmode)) + { + ro[3] = x1; + goto L1264; + } + goto ret0; + + L1264: + x2 = XEXP (x1, 0); + switch (GET_MODE (x2)) + { + case DFmode: + switch (GET_CODE (x2)) + { + case FLOAT: + goto L1265; + case FLOAT_EXTEND: + goto L1271; + case SUBREG: + case REG: + case MEM: + if (nonimmediate_operand (x2, DFmode)) + { + ro[1] = x2; + goto L1260; + } + } + } + L1276: + if (general_operand (x2, DFmode)) + { + ro[1] = x2; + goto L1277; + } + goto ret0; + + L1265: + x3 = XEXP (x2, 0); + if (general_operand (x3, SImode)) + { + ro[1] = x3; + goto L1266; + } + goto ret0; + + L1266: + x2 = XEXP (x1, 1); + if (general_operand (x2, DFmode)) + { + ro[2] = x2; + if (TARGET_80387) + return 254; + } + goto ret0; + + L1271: + x3 = XEXP (x2, 0); + if (general_operand (x3, SFmode)) + { + ro[1] = x3; + goto L1272; + } + goto ret0; + + L1272: + x2 = XEXP (x1, 1); + if (general_operand (x2, DFmode)) + { + ro[2] = x2; + if (TARGET_80387) + return 255; + } + goto ret0; + + L1260: + x2 = XEXP (x1, 1); + if (nonimmediate_operand (x2, DFmode)) + { + ro[2] = x2; + if (TARGET_80387) + return 253; + } + x2 = XEXP (x1, 0); + goto L1276; + + L1277: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) != DFmode) + goto ret0; + switch (GET_CODE (x2)) + { + case FLOAT: + goto L1278; + case FLOAT_EXTEND: + goto L1284; + } + goto ret0; + + L1278: + x3 = XEXP (x2, 0); + if (general_operand (x3, SImode)) + { + ro[2] = x3; + if (TARGET_80387) + return 256; + } + goto ret0; + + L1284: + x3 = XEXP (x2, 0); + if (general_operand (x3, SFmode)) + { + ro[2] = x3; + if (TARGET_80387) + return 257; + } + goto ret0; + + L1287: + x1 = XEXP (x0, 1); + if (binary_387_op (x1, SFmode)) + { + ro[3] = x1; + goto L1293; + } + goto ret0; + + L1293: + x2 = XEXP (x1, 0); + switch (GET_MODE (x2)) + { + case SFmode: + if (GET_CODE (x2) == FLOAT && 1) + goto L1294; + if (nonimmediate_operand (x2, SFmode)) + { + ro[1] = x2; + goto L1289; + } + } + L1299: + if (general_operand (x2, SFmode)) + { + ro[1] = x2; + goto L1300; + } + goto ret0; + + L1294: + x3 = XEXP (x2, 0); + if (general_operand (x3, SImode)) + { + ro[1] = x3; + goto L1295; + } + goto ret0; + + L1295: + x2 = XEXP (x1, 1); + if (general_operand (x2, SFmode)) + { + ro[2] = x2; + if (TARGET_80387) + return 259; + } + goto ret0; + + L1289: + x2 = XEXP (x1, 1); + if (nonimmediate_operand (x2, SFmode)) + { + ro[2] = x2; + if (TARGET_80387) + return 258; + } + x2 = XEXP (x1, 0); + goto L1299; + + L1300: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == SFmode && GET_CODE (x2) == FLOAT && 1) + goto L1301; + goto ret0; + + L1301: + x3 = XEXP (x2, 0); + if (general_operand (x3, SImode)) + { + ro[2] = x3; + if (TARGET_80387) + return 260; + } + goto ret0; + ret0: return -1; +} + +int +recog_4 (x0, insn, pnum_clobbers) + register rtx x0; + rtx insn; + int *pnum_clobbers; +{ + register rtx *ro = &recog_operand[0]; + register rtx x1, x2, x3, x4, x5, x6; + int tem; + + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + switch (GET_MODE (x2)) + { + case SFmode: + if (register_operand (x2, SFmode)) + { + ro[0] = x2; + goto L13; + } + break; + case DFmode: + if (register_operand (x2, DFmode)) + { + ro[0] = x2; + goto L22; + } + } + L45: + if (VOIDmode_compare_op (x2, VOIDmode)) + { + ro[2] = x2; + goto L74; + } + L118: + if (GET_MODE (x2) == CCFPEQmode && GET_CODE (x2) == COMPARE && 1) + goto L119; + goto ret0; + + L13: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L14; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + goto L45; + + L14: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, HImode)) + { + ro[1] = x2; + if (TARGET_80387 && ! TARGET_IEEE_FP) + return 6; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + goto L45; + + L22: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L23; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + goto L45; + + L23: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, HImode)) + { + ro[1] = x2; + if (TARGET_80387 && ! TARGET_IEEE_FP) + return 8; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + goto L45; + + L74: + x3 = XEXP (x2, 0); + switch (GET_MODE (x3)) + { + case DFmode: + switch (GET_CODE (x3)) + { + case FLOAT: + goto L75; + case FLOAT_EXTEND: + goto L105; + case SUBREG: + case REG: + case MEM: + if (nonimmediate_operand (x3, DFmode)) + { + ro[0] = x3; + goto L47; + } + } + L59: + if (register_operand (x3, DFmode)) + { + ro[0] = x3; + goto L60; + } + break; + case SFmode: + if (GET_CODE (x3) == FLOAT && 1) + goto L161; + if (nonimmediate_operand (x3, SFmode)) + { + ro[0] = x3; + goto L133; + } + L145: + if (register_operand (x3, SFmode)) + { + ro[0] = x3; + goto L146; + } + } + goto L118; + + L75: + x4 = XEXP (x3, 0); + if (nonimmediate_operand (x4, SImode)) + { + ro[0] = x4; + goto L76; + } + goto L118; + + L76: + x3 = XEXP (x2, 1); + if (register_operand (x3, DFmode)) + { + ro[1] = x3; + goto L77; + } + goto L118; + + L77: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L78; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + goto L118; + + L78: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, HImode)) + { + ro[3] = x2; + if (TARGET_80387) + return 18; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + goto L118; + + L105: + x4 = XEXP (x3, 0); + if (nonimmediate_operand (x4, SFmode)) + { + ro[0] = x4; + goto L106; + } + goto L118; + + L106: + x3 = XEXP (x2, 1); + if (register_operand (x3, DFmode)) + { + ro[1] = x3; + goto L107; + } + goto L118; + + L107: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L108; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + goto L118; + + L108: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, HImode)) + { + ro[3] = x2; + if (TARGET_80387) + return 20; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + goto L118; + + L47: + x3 = XEXP (x2, 1); + if (nonimmediate_operand (x3, DFmode)) + { + ro[1] = x3; + goto L48; + } + x3 = XEXP (x2, 0); + goto L59; + + L48: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L49; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + x3 = XEXP (x2, 0); + goto L59; + + L49: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, HImode)) + { + ro[3] = x2; + if (TARGET_80387 + && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)) + return 16; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + x3 = XEXP (x2, 0); + goto L59; + + L60: + x3 = XEXP (x2, 1); + if (GET_MODE (x3) != DFmode) + { + goto L118; + } + switch (GET_CODE (x3)) + { + case FLOAT: + goto L61; + case FLOAT_EXTEND: + goto L91; + } + goto L118; + + L61: + x4 = XEXP (x3, 0); + if (nonimmediate_operand (x4, SImode)) + { + ro[1] = x4; + goto L62; + } + goto L118; + + L62: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L63; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + goto L118; + + L63: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, HImode)) + { + ro[3] = x2; + if (TARGET_80387) + return 17; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + goto L118; + + L91: + x4 = XEXP (x3, 0); + if (nonimmediate_operand (x4, SFmode)) + { + ro[1] = x4; + goto L92; + } + goto L118; + + L92: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L93; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + goto L118; + + L93: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, HImode)) + { + ro[3] = x2; + if (TARGET_80387) + return 19; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + goto L118; + + L161: + x4 = XEXP (x3, 0); + if (nonimmediate_operand (x4, SImode)) + { + ro[0] = x4; + goto L162; + } + goto L118; + + L162: + x3 = XEXP (x2, 1); + if (register_operand (x3, SFmode)) + { + ro[1] = x3; + goto L163; + } + goto L118; + + L163: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L164; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + goto L118; + + L164: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, HImode)) + { + ro[3] = x2; + if (TARGET_80387) + return 24; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + goto L118; + + L133: + x3 = XEXP (x2, 1); + if (nonimmediate_operand (x3, SFmode)) + { + ro[1] = x3; + goto L134; + } + x3 = XEXP (x2, 0); + goto L145; + + L134: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L135; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + x3 = XEXP (x2, 0); + goto L145; + + L135: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, HImode)) + { + ro[3] = x2; + if (TARGET_80387 + && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)) + return 22; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + x3 = XEXP (x2, 0); + goto L145; + + L146: + x3 = XEXP (x2, 1); + if (GET_MODE (x3) == SFmode && GET_CODE (x3) == FLOAT && 1) + goto L147; + goto L118; + + L147: + x4 = XEXP (x3, 0); + if (nonimmediate_operand (x4, SImode)) + { + ro[1] = x4; + goto L148; + } + goto L118; + + L148: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L149; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + goto L118; + + L149: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, HImode)) + { + ro[3] = x2; + if (TARGET_80387) + return 23; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + goto L118; + + L119: + x3 = XEXP (x2, 0); + switch (GET_MODE (x3)) + { + case DFmode: + if (register_operand (x3, DFmode)) + { + ro[0] = x3; + goto L120; + } + break; + case SFmode: + if (register_operand (x3, SFmode)) + { + ro[0] = x3; + goto L176; + } + } + goto ret0; + + L120: + x3 = XEXP (x2, 1); + if (register_operand (x3, DFmode)) + { + ro[1] = x3; + goto L121; + } + goto ret0; + + L121: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L122; + goto ret0; + + L122: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, HImode)) + { + ro[2] = x2; + if (TARGET_80387) + return 21; + } + goto ret0; + + L176: + x3 = XEXP (x2, 1); + if (register_operand (x3, SFmode)) + { + ro[1] = x3; + goto L177; + } + goto ret0; + + L177: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L178; + goto ret0; + + L178: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, HImode)) + { + ro[2] = x2; + if (TARGET_80387) + return 25; + } + goto ret0; + ret0: return -1; +} + +int +recog_5 (x0, insn, pnum_clobbers) + register rtx x0; + rtx insn; + int *pnum_clobbers; +{ + register rtx *ro = &recog_operand[0]; + register rtx x1, x2, x3, x4, x5, x6; + int tem; + + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + switch (GET_MODE (x2)) + { + case DFmode: + if (register_operand (x2, DFmode)) + { + ro[0] = x2; + goto L242; + } + break; + case SFmode: + if (nonimmediate_operand (x2, SFmode)) + { + ro[0] = x2; + goto L294; + } + break; + case SImode: + if (register_operand (x2, SImode)) + { + ro[0] = x2; + goto L497; + } + break; + case HImode: + if (register_operand (x2, HImode)) + { + ro[0] = x2; + goto L508; + } + break; + case DImode: + if (register_operand (x2, DImode)) + { + ro[0] = x2; + goto L684; + } + } + switch (GET_CODE (x2)) + { + case CC0: + goto L12; + case PC: + goto L1068; + } + L1125: + ro[0] = x2; + goto L1126; + L1228: + switch (GET_MODE (x2)) + { + case SImode: + if (register_operand (x2, SImode)) + { + ro[0] = x2; + goto L1229; + } + break; + case HImode: + if (register_operand (x2, HImode)) + { + ro[0] = x2; + goto L1244; + } + } + goto ret0; + + L242: + x2 = XEXP (x1, 1); + if (register_operand (x2, DFmode)) + { + ro[1] = x2; + goto L243; + } + x2 = XEXP (x1, 0); + goto L1125; + + L243: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == SET && 1) + goto L244; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L244: + x2 = XEXP (x1, 0); + if (rtx_equal_p (x2, ro[1]) && 1) + goto L245; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L245: + x2 = XEXP (x1, 1); + if (rtx_equal_p (x2, ro[0]) && 1) + return 48; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L294: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == SFmode && GET_CODE (x2) == FLOAT_TRUNCATE && 1) + goto L295; + x2 = XEXP (x1, 0); + goto L1125; + + L295: + x3 = XEXP (x2, 0); + if (register_operand (x3, DFmode)) + { + ro[1] = x3; + goto L296; + } + x2 = XEXP (x1, 0); + goto L1125; + + L296: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L297; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L297: + x2 = XEXP (x1, 0); + if (memory_operand (x2, SFmode)) + { + ro[2] = x2; + if (TARGET_80387) + return 62; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L497: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) != SImode) + { + x2 = XEXP (x1, 0); + goto L1125; + } + switch (GET_CODE (x2)) + { + case DIV: + goto L498; + case UDIV: + goto L520; + } + x2 = XEXP (x1, 0); + goto L1125; + + L498: + x3 = XEXP (x2, 0); + if (register_operand (x3, SImode)) + { + ro[1] = x3; + goto L499; + } + x2 = XEXP (x1, 0); + goto L1125; + + L499: + x3 = XEXP (x2, 1); + if (general_operand (x3, SImode)) + { + ro[2] = x3; + goto L500; + } + x2 = XEXP (x1, 0); + goto L1125; + + L500: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == SET && 1) + goto L501; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L501: + x2 = XEXP (x1, 0); + if (register_operand (x2, SImode)) + { + ro[3] = x2; + goto L502; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L502: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == MOD && 1) + goto L503; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L503: + x3 = XEXP (x2, 0); + if (rtx_equal_p (x3, ro[1]) && 1) + goto L504; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L504: + x3 = XEXP (x2, 1); + if (rtx_equal_p (x3, ro[2]) && 1) + return 105; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L520: + x3 = XEXP (x2, 0); + if (register_operand (x3, SImode)) + { + ro[1] = x3; + goto L521; + } + x2 = XEXP (x1, 0); + goto L1125; + + L521: + x3 = XEXP (x2, 1); + if (general_operand (x3, SImode)) + { + ro[2] = x3; + goto L522; + } + x2 = XEXP (x1, 0); + goto L1125; + + L522: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == SET && 1) + goto L523; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L523: + x2 = XEXP (x1, 0); + if (register_operand (x2, SImode)) + { + ro[3] = x2; + goto L524; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L524: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == UMOD && 1) + goto L525; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L525: + x3 = XEXP (x2, 0); + if (rtx_equal_p (x3, ro[1]) && 1) + goto L526; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L526: + x3 = XEXP (x2, 1); + if (rtx_equal_p (x3, ro[2]) && 1) + return 107; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L508: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) != HImode) + { + x2 = XEXP (x1, 0); + goto L1125; + } + switch (GET_CODE (x2)) + { + case DIV: + goto L509; + case UDIV: + goto L531; + } + x2 = XEXP (x1, 0); + goto L1125; + + L509: + x3 = XEXP (x2, 0); + if (register_operand (x3, HImode)) + { + ro[1] = x3; + goto L510; + } + x2 = XEXP (x1, 0); + goto L1125; + + L510: + x3 = XEXP (x2, 1); + if (general_operand (x3, HImode)) + { + ro[2] = x3; + goto L511; + } + x2 = XEXP (x1, 0); + goto L1125; + + L511: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == SET && 1) + goto L512; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L512: + x2 = XEXP (x1, 0); + if (register_operand (x2, HImode)) + { + ro[3] = x2; + goto L513; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L513: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == HImode && GET_CODE (x2) == MOD && 1) + goto L514; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L514: + x3 = XEXP (x2, 0); + if (rtx_equal_p (x3, ro[1]) && 1) + goto L515; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L515: + x3 = XEXP (x2, 1); + if (rtx_equal_p (x3, ro[2]) && 1) + return 106; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L531: + x3 = XEXP (x2, 0); + if (register_operand (x3, HImode)) + { + ro[1] = x3; + goto L532; + } + x2 = XEXP (x1, 0); + goto L1125; + + L532: + x3 = XEXP (x2, 1); + if (general_operand (x3, HImode)) + { + ro[2] = x3; + goto L533; + } + x2 = XEXP (x1, 0); + goto L1125; + + L533: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == SET && 1) + goto L534; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L534: + x2 = XEXP (x1, 0); + if (register_operand (x2, HImode)) + { + ro[3] = x2; + goto L535; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L535: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == HImode && GET_CODE (x2) == UMOD && 1) + goto L536; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L536: + x3 = XEXP (x2, 0); + if (rtx_equal_p (x3, ro[1]) && 1) + goto L537; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L537: + x3 = XEXP (x2, 1); + if (rtx_equal_p (x3, ro[2]) && 1) + return 108; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L684: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) != DImode) + { + x2 = XEXP (x1, 0); + goto L1125; + } + switch (GET_CODE (x2)) + { + case ASHIFT: + goto L685; + case ASHIFTRT: + goto L713; + case LSHIFTRT: + goto L741; + } + x2 = XEXP (x1, 0); + goto L1125; + + L685: + x3 = XEXP (x2, 0); + if (register_operand (x3, DImode)) + { + ro[1] = x3; + goto L686; + } + x2 = XEXP (x1, 0); + goto L1125; + + L686: + x3 = XEXP (x2, 1); + if (register_operand (x3, QImode)) + { + ro[2] = x3; + goto L687; + } + x2 = XEXP (x1, 0); + goto L1125; + + L687: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L688; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L688: + x2 = XEXP (x1, 0); + if (rtx_equal_p (x2, ro[2]) && 1) + return 142; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L713: + x3 = XEXP (x2, 0); + if (register_operand (x3, DImode)) + { + ro[1] = x3; + goto L714; + } + x2 = XEXP (x1, 0); + goto L1125; + + L714: + x3 = XEXP (x2, 1); + if (register_operand (x3, QImode)) + { + ro[2] = x3; + goto L715; + } + x2 = XEXP (x1, 0); + goto L1125; + + L715: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L716; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L716: + x2 = XEXP (x1, 0); + if (rtx_equal_p (x2, ro[2]) && 1) + return 148; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L741: + x3 = XEXP (x2, 0); + if (register_operand (x3, DImode)) + { + ro[1] = x3; + goto L742; + } + x2 = XEXP (x1, 0); + goto L1125; + + L742: + x3 = XEXP (x2, 1); + if (register_operand (x3, QImode)) + { + ro[2] = x3; + goto L743; + } + x2 = XEXP (x1, 0); + goto L1125; + + L743: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L744; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L744: + x2 = XEXP (x1, 0); + if (rtx_equal_p (x2, ro[2]) && 1) + return 154; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + L12: + tem = recog_4 (x0, insn, pnum_clobbers); + if (tem >= 0) return tem; + x2 = XEXP (x1, 0); + goto L1125; + + L1068: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == MINUS && 1) + goto L1069; + if (general_operand (x2, SImode)) + { + ro[0] = x2; + goto L1094; + } + x2 = XEXP (x1, 0); + goto L1125; + + L1069: + x3 = XEXP (x2, 0); + if (GET_MODE (x3) == SImode && GET_CODE (x3) == REG && XINT (x3, 0) == 3 && 1) + goto L1070; + x2 = XEXP (x1, 0); + goto L1125; + + L1070: + x3 = XEXP (x2, 1); + if (GET_MODE (x3) == SImode && GET_CODE (x3) == MEM && 1) + goto L1071; + x2 = XEXP (x1, 0); + goto L1125; + + L1071: + x4 = XEXP (x3, 0); + if (GET_MODE (x4) == SImode && GET_CODE (x4) == PLUS && 1) + goto L1072; + x2 = XEXP (x1, 0); + goto L1125; + + L1072: + x5 = XEXP (x4, 0); + if (GET_MODE (x5) == SImode && GET_CODE (x5) == MULT && 1) + goto L1073; + x2 = XEXP (x1, 0); + goto L1125; + + L1073: + x6 = XEXP (x5, 0); + if (register_operand (x6, SImode)) + { + ro[0] = x6; + goto L1074; + } + x2 = XEXP (x1, 0); + goto L1125; + + L1074: + x6 = XEXP (x5, 1); + if (GET_CODE (x6) == CONST_INT && XWINT (x6, 0) == 4 && 1) + goto L1075; + x2 = XEXP (x1, 0); + goto L1125; + + L1075: + x5 = XEXP (x4, 1); + if (GET_CODE (x5) == LABEL_REF && 1) + goto L1076; + x2 = XEXP (x1, 0); + goto L1125; + + L1076: + x6 = XEXP (x5, 0); + ro[1] = x6; + goto L1077; + + L1077: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L1078; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L1078: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, SImode)) + { + ro[2] = x2; + return 223; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L1094: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == USE && 1) + goto L1095; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L1095: + x2 = XEXP (x1, 0); + if (GET_CODE (x2) == LABEL_REF && 1) + goto L1096; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1125; + + L1096: + x3 = XEXP (x2, 0); + ro[1] = x3; + return 224; + + L1126: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CALL && 1) + goto L1138; + x2 = XEXP (x1, 0); + goto L1228; + + L1138: + x3 = XEXP (x2, 0); + if (GET_MODE (x3) == QImode && GET_CODE (x3) == MEM && 1) + goto L1139; + L1127: + if (call_insn_operand (x3, QImode)) + { + ro[1] = x3; + goto L1128; + } + x2 = XEXP (x1, 0); + goto L1228; + + L1139: + x4 = XEXP (x3, 0); + if (symbolic_operand (x4, SImode)) + { + ro[1] = x4; + goto L1140; + } + goto L1127; + + L1140: + x3 = XEXP (x2, 1); + if (general_operand (x3, SImode)) + { + ro[2] = x3; + goto L1141; + } + x3 = XEXP (x2, 0); + goto L1127; + + L1141: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == SET && 1) + goto L1142; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + x3 = XEXP (x2, 0); + goto L1127; + + L1142: + x2 = XEXP (x1, 0); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == REG && XINT (x2, 0) == 7 && 1) + goto L1143; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + x3 = XEXP (x2, 0); + goto L1127; + + L1143: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == PLUS && 1) + goto L1144; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + x3 = XEXP (x2, 0); + goto L1127; + + L1144: + x3 = XEXP (x2, 0); + if (GET_MODE (x3) == SImode && GET_CODE (x3) == REG && XINT (x3, 0) == 7 && 1) + goto L1145; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + x3 = XEXP (x2, 0); + goto L1127; + + L1145: + x3 = XEXP (x2, 1); + if (immediate_operand (x3, SImode)) + { + ro[4] = x3; + if (!HALF_PIC_P ()) + return 233; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 1); + x3 = XEXP (x2, 0); + goto L1127; + + L1128: + x3 = XEXP (x2, 1); + if (general_operand (x3, SImode)) + { + ro[2] = x3; + goto L1129; + } + x2 = XEXP (x1, 0); + goto L1228; + + L1129: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == SET && 1) + goto L1130; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1228; + + L1130: + x2 = XEXP (x1, 0); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == REG && XINT (x2, 0) == 7 && 1) + goto L1131; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1228; + + L1131: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == PLUS && 1) + goto L1132; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1228; + + L1132: + x3 = XEXP (x2, 0); + if (GET_MODE (x3) == SImode && GET_CODE (x3) == REG && XINT (x3, 0) == 7 && 1) + goto L1133; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1228; + + L1133: + x3 = XEXP (x2, 1); + if (immediate_operand (x3, SImode)) + { + ro[4] = x3; + return 232; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1228; + + L1229: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) != SImode) + goto ret0; + switch (GET_CODE (x2)) + { + case PLUS: + goto L1230; + case UNSPEC: + if (XINT (x2, 1) == 0 && XVECLEN (x2, 0) == 3 && 1) + goto L1306; + } + goto ret0; + + L1230: + x3 = XEXP (x2, 0); + if (GET_MODE (x3) == SImode && GET_CODE (x3) == FFS && 1) + goto L1231; + goto ret0; + + L1231: + x4 = XEXP (x3, 0); + if (general_operand (x4, SImode)) + { + ro[1] = x4; + goto L1232; + } + goto ret0; + + L1232: + x3 = XEXP (x2, 1); + if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == -1 && 1) + goto L1233; + goto ret0; + + L1233: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L1234; + goto ret0; + + L1234: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, SImode)) + { + ro[2] = x2; + return 250; + } + goto ret0; + + L1306: + x3 = XVECEXP (x2, 0, 0); + if (GET_MODE (x3) == BLKmode && GET_CODE (x3) == MEM && 1) + goto L1307; + goto ret0; + + L1307: + x4 = XEXP (x3, 0); + if (address_operand (x4, SImode)) + { + ro[1] = x4; + goto L1308; + } + goto ret0; + + L1308: + x3 = XVECEXP (x2, 0, 1); + if (register_operand (x3, QImode)) + { + ro[2] = x3; + goto L1309; + } + goto ret0; + + L1309: + x3 = XVECEXP (x2, 0, 2); + if (immediate_operand (x3, SImode)) + { + ro[3] = x3; + goto L1310; + } + goto ret0; + + L1310: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L1311; + goto ret0; + + L1311: + x2 = XEXP (x1, 0); + if (rtx_equal_p (x2, ro[1]) && 1) + return 262; + goto ret0; + + L1244: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == HImode && GET_CODE (x2) == PLUS && 1) + goto L1245; + goto ret0; + + L1245: + x3 = XEXP (x2, 0); + if (GET_MODE (x3) == HImode && GET_CODE (x3) == FFS && 1) + goto L1246; + goto ret0; + + L1246: + x4 = XEXP (x3, 0); + if (general_operand (x4, HImode)) + { + ro[1] = x4; + goto L1247; + } + goto ret0; + + L1247: + x3 = XEXP (x2, 1); + if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == -1 && 1) + goto L1248; + goto ret0; + + L1248: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L1249; + goto ret0; + + L1249: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, HImode)) + { + ro[2] = x2; + return 252; + } + goto ret0; + ret0: return -1; +} + +int +recog (x0, insn, pnum_clobbers) + register rtx x0; + rtx insn; + int *pnum_clobbers; +{ + register rtx *ro = &recog_operand[0]; + register rtx x1, x2, x3, x4, x5, x6; + int tem; + + L1170: + switch (GET_CODE (x0)) + { + case UNSPEC: + if (GET_MODE (x0) == SImode && XINT (x0, 1) == 0 && XVECLEN (x0, 0) == 1 && 1) + goto L1171; + break; + case SET: + goto L200; + case PARALLEL: + if (XVECLEN (x0, 0) == 2 && 1) + goto L10; + if (XVECLEN (x0, 0) == 5 && 1) + goto L299; + if (XVECLEN (x0, 0) == 4 && 1) + goto L313; + if (XVECLEN (x0, 0) == 3 && 1) + goto L363; + if (XVECLEN (x0, 0) == 6 && 1) + goto L1175; + break; + case CALL: + goto L1117; + case RETURN: + if (simple_386_epilogue ()) + return 242; + break; + case CONST_INT: + if (XWINT (x0, 0) == 0 && 1) + return 243; + } + goto ret0; + + L1171: + x1 = XVECEXP (x0, 0, 0); + if (memory_operand (x1, SImode)) + { + ro[0] = x1; + return 241; + } + goto ret0; + L200: + return recog_3 (x0, insn, pnum_clobbers); + + L10: + x1 = XVECEXP (x0, 0, 0); + switch (GET_CODE (x1)) + { + case SET: + goto L241; + case CALL: + goto L1108; + } + goto ret0; + L241: + return recog_5 (x0, insn, pnum_clobbers); + + L1108: + x2 = XEXP (x1, 0); + if (GET_MODE (x2) == QImode && GET_CODE (x2) == MEM && 1) + goto L1109; + L1099: + if (call_insn_operand (x2, QImode)) + { + ro[0] = x2; + goto L1100; + } + goto ret0; + + L1109: + x3 = XEXP (x2, 0); + if (symbolic_operand (x3, SImode)) + { + ro[0] = x3; + goto L1110; + } + goto L1099; + + L1110: + x2 = XEXP (x1, 1); + if (general_operand (x2, SImode)) + { + ro[1] = x2; + goto L1111; + } + x2 = XEXP (x1, 0); + goto L1099; + + L1111: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == SET && 1) + goto L1112; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1099; + + L1112: + x2 = XEXP (x1, 0); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == REG && XINT (x2, 0) == 7 && 1) + goto L1113; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1099; + + L1113: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == PLUS && 1) + goto L1114; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1099; + + L1114: + x3 = XEXP (x2, 0); + if (GET_MODE (x3) == SImode && GET_CODE (x3) == REG && XINT (x3, 0) == 7 && 1) + goto L1115; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1099; + + L1115: + x3 = XEXP (x2, 1); + if (immediate_operand (x3, SImode)) + { + ro[3] = x3; + if (!HALF_PIC_P ()) + return 227; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1099; + + L1100: + x2 = XEXP (x1, 1); + if (general_operand (x2, SImode)) + { + ro[1] = x2; + goto L1101; + } + goto ret0; + + L1101: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == SET && 1) + goto L1102; + goto ret0; + + L1102: + x2 = XEXP (x1, 0); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == REG && XINT (x2, 0) == 7 && 1) + goto L1103; + goto ret0; + + L1103: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == PLUS && 1) + goto L1104; + goto ret0; + + L1104: + x3 = XEXP (x2, 0); + if (GET_MODE (x3) == SImode && GET_CODE (x3) == REG && XINT (x3, 0) == 7 && 1) + goto L1105; + goto ret0; + + L1105: + x3 = XEXP (x2, 1); + if (immediate_operand (x3, SImode)) + { + ro[3] = x3; + return 226; + } + goto ret0; + + L299: + x1 = XVECEXP (x0, 0, 0); + if (GET_CODE (x1) == SET && 1) + goto L300; + goto ret0; + + L300: + x2 = XEXP (x1, 0); + if (GET_MODE (x2) == DImode && general_operand (x2, DImode)) + { + ro[0] = x2; + goto L301; + } + goto ret0; + + L301: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == DImode && GET_CODE (x2) == FIX && 1) + goto L302; + goto ret0; + + L302: + x3 = XEXP (x2, 0); + if (GET_CODE (x3) != FIX) + goto ret0; + switch (GET_MODE (x3)) + { + case DFmode: + goto L303; + case SFmode: + goto L329; + } + goto ret0; + + L303: + x4 = XEXP (x3, 0); + if (register_operand (x4, DFmode)) + { + ro[1] = x4; + goto L304; + } + goto ret0; + + L304: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L305; + goto ret0; + + L305: + x2 = XEXP (x1, 0); + if (rtx_equal_p (x2, ro[1]) && 1) + goto L306; + goto ret0; + + L306: + x1 = XVECEXP (x0, 0, 2); + if (GET_CODE (x1) == CLOBBER && 1) + goto L307; + goto ret0; + + L307: + x2 = XEXP (x1, 0); + if (memory_operand (x2, SImode)) + { + ro[2] = x2; + goto L308; + } + goto ret0; + + L308: + x1 = XVECEXP (x0, 0, 3); + if (GET_CODE (x1) == CLOBBER && 1) + goto L309; + goto ret0; + + L309: + x2 = XEXP (x1, 0); + if (memory_operand (x2, SImode)) + { + ro[3] = x2; + goto L310; + } + goto ret0; + + L310: + x1 = XVECEXP (x0, 0, 4); + if (GET_CODE (x1) == CLOBBER && 1) + goto L311; + goto ret0; + + L311: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, SImode)) + { + ro[4] = x2; + if (TARGET_80387) + return 67; + } + goto ret0; + + L329: + x4 = XEXP (x3, 0); + if (register_operand (x4, SFmode)) + { + ro[1] = x4; + goto L330; + } + goto ret0; + + L330: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L331; + goto ret0; + + L331: + x2 = XEXP (x1, 0); + if (rtx_equal_p (x2, ro[1]) && 1) + goto L332; + goto ret0; + + L332: + x1 = XVECEXP (x0, 0, 2); + if (GET_CODE (x1) == CLOBBER && 1) + goto L333; + goto ret0; + + L333: + x2 = XEXP (x1, 0); + if (memory_operand (x2, SImode)) + { + ro[2] = x2; + goto L334; + } + goto ret0; + + L334: + x1 = XVECEXP (x0, 0, 3); + if (GET_CODE (x1) == CLOBBER && 1) + goto L335; + goto ret0; + + L335: + x2 = XEXP (x1, 0); + if (memory_operand (x2, SImode)) + { + ro[3] = x2; + goto L336; + } + goto ret0; + + L336: + x1 = XVECEXP (x0, 0, 4); + if (GET_CODE (x1) == CLOBBER && 1) + goto L337; + goto ret0; + + L337: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, SImode)) + { + ro[4] = x2; + if (TARGET_80387) + return 68; + } + goto ret0; + + L313: + x1 = XVECEXP (x0, 0, 0); + if (GET_CODE (x1) == SET && 1) + goto L314; + goto ret0; + + L314: + x2 = XEXP (x1, 0); + switch (GET_MODE (x2)) + { + case DImode: + if (general_operand (x2, DImode)) + { + ro[0] = x2; + goto L315; + } + break; + case SImode: + if (general_operand (x2, SImode)) + { + ro[0] = x2; + goto L353; + } + } + goto ret0; + + L315: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == DImode && GET_CODE (x2) == FIX && 1) + goto L316; + goto ret0; + + L316: + x3 = XEXP (x2, 0); + if (GET_CODE (x3) != FIX) + goto ret0; + switch (GET_MODE (x3)) + { + case DFmode: + goto L317; + case SFmode: + goto L343; + } + goto ret0; + + L317: + x4 = XEXP (x3, 0); + if (register_operand (x4, DFmode)) + { + ro[1] = x4; + goto L318; + } + goto ret0; + + L318: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L319; + goto ret0; + + L319: + x2 = XEXP (x1, 0); + if (rtx_equal_p (x2, ro[1]) && 1) + goto L320; + goto ret0; + + L320: + x1 = XVECEXP (x0, 0, 2); + if (GET_CODE (x1) == CLOBBER && 1) + goto L321; + goto ret0; + + L321: + x2 = XEXP (x1, 0); + if (memory_operand (x2, SImode)) + { + ro[2] = x2; + goto L322; + } + goto ret0; + + L322: + x1 = XVECEXP (x0, 0, 3); + if (GET_CODE (x1) == CLOBBER && 1) + goto L323; + goto ret0; + + L323: + x2 = XEXP (x1, 0); + if (pnum_clobbers != 0 && memory_operand (x2, SImode)) + { + ro[3] = x2; + if (TARGET_80387) + { + *pnum_clobbers = 1; + return 67; + } + } + goto ret0; + + L343: + x4 = XEXP (x3, 0); + if (register_operand (x4, SFmode)) + { + ro[1] = x4; + goto L344; + } + goto ret0; + + L344: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L345; + goto ret0; + + L345: + x2 = XEXP (x1, 0); + if (rtx_equal_p (x2, ro[1]) && 1) + goto L346; + goto ret0; + + L346: + x1 = XVECEXP (x0, 0, 2); + if (GET_CODE (x1) == CLOBBER && 1) + goto L347; + goto ret0; + + L347: + x2 = XEXP (x1, 0); + if (memory_operand (x2, SImode)) + { + ro[2] = x2; + goto L348; + } + goto ret0; + + L348: + x1 = XVECEXP (x0, 0, 3); + if (GET_CODE (x1) == CLOBBER && 1) + goto L349; + goto ret0; + + L349: + x2 = XEXP (x1, 0); + if (pnum_clobbers != 0 && memory_operand (x2, SImode)) + { + ro[3] = x2; + if (TARGET_80387) + { + *pnum_clobbers = 1; + return 68; + } + } + goto ret0; + + L353: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == FIX && 1) + goto L354; + goto ret0; + + L354: + x3 = XEXP (x2, 0); + if (GET_CODE (x3) != FIX) + goto ret0; + switch (GET_MODE (x3)) + { + case DFmode: + goto L355; + case SFmode: + goto L377; + } + goto ret0; + + L355: + x4 = XEXP (x3, 0); + if (register_operand (x4, DFmode)) + { + ro[1] = x4; + goto L356; + } + goto ret0; + + L356: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L357; + goto ret0; + + L357: + x2 = XEXP (x1, 0); + if (memory_operand (x2, SImode)) + { + ro[2] = x2; + goto L358; + } + goto ret0; + + L358: + x1 = XVECEXP (x0, 0, 2); + if (GET_CODE (x1) == CLOBBER && 1) + goto L359; + goto ret0; + + L359: + x2 = XEXP (x1, 0); + if (memory_operand (x2, SImode)) + { + ro[3] = x2; + goto L360; + } + goto ret0; + + L360: + x1 = XVECEXP (x0, 0, 3); + if (GET_CODE (x1) == CLOBBER && 1) + goto L361; + goto ret0; + + L361: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, SImode)) + { + ro[4] = x2; + if (TARGET_80387) + return 71; + } + goto ret0; + + L377: + x4 = XEXP (x3, 0); + if (register_operand (x4, SFmode)) + { + ro[1] = x4; + goto L378; + } + goto ret0; + + L378: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L379; + goto ret0; + + L379: + x2 = XEXP (x1, 0); + if (memory_operand (x2, SImode)) + { + ro[2] = x2; + goto L380; + } + goto ret0; + + L380: + x1 = XVECEXP (x0, 0, 2); + if (GET_CODE (x1) == CLOBBER && 1) + goto L381; + goto ret0; + + L381: + x2 = XEXP (x1, 0); + if (memory_operand (x2, SImode)) + { + ro[3] = x2; + goto L382; + } + goto ret0; + + L382: + x1 = XVECEXP (x0, 0, 3); + if (GET_CODE (x1) == CLOBBER && 1) + goto L383; + goto ret0; + + L383: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, SImode)) + { + ro[4] = x2; + if (TARGET_80387) + return 72; + } + goto ret0; + + L363: + x1 = XVECEXP (x0, 0, 0); + switch (GET_CODE (x1)) + { + case SET: + goto L364; + case CALL: + goto L1165; + } + goto ret0; + + L364: + x2 = XEXP (x1, 0); + if (GET_MODE (x2) == SImode && general_operand (x2, SImode)) + { + ro[0] = x2; + goto L365; + } + goto ret0; + + L365: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == FIX && 1) + goto L366; + goto ret0; + + L366: + x3 = XEXP (x2, 0); + if (GET_CODE (x3) != FIX) + goto ret0; + switch (GET_MODE (x3)) + { + case DFmode: + goto L367; + case SFmode: + goto L389; + } + goto ret0; + + L367: + x4 = XEXP (x3, 0); + if (register_operand (x4, DFmode)) + { + ro[1] = x4; + goto L368; + } + goto ret0; + + L368: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L369; + goto ret0; + + L369: + x2 = XEXP (x1, 0); + if (memory_operand (x2, SImode)) + { + ro[2] = x2; + goto L370; + } + goto ret0; + + L370: + x1 = XVECEXP (x0, 0, 2); + if (GET_CODE (x1) == CLOBBER && 1) + goto L371; + goto ret0; + + L371: + x2 = XEXP (x1, 0); + if (pnum_clobbers != 0 && memory_operand (x2, SImode)) + { + ro[3] = x2; + if (TARGET_80387) + { + *pnum_clobbers = 1; + return 71; + } + } + goto ret0; + + L389: + x4 = XEXP (x3, 0); + if (register_operand (x4, SFmode)) + { + ro[1] = x4; + goto L390; + } + goto ret0; + + L390: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == CLOBBER && 1) + goto L391; + goto ret0; + + L391: + x2 = XEXP (x1, 0); + if (memory_operand (x2, SImode)) + { + ro[2] = x2; + goto L392; + } + goto ret0; + + L392: + x1 = XVECEXP (x0, 0, 2); + if (GET_CODE (x1) == CLOBBER && 1) + goto L393; + goto ret0; + + L393: + x2 = XEXP (x1, 0); + if (pnum_clobbers != 0 && memory_operand (x2, SImode)) + { + ro[3] = x2; + if (TARGET_80387) + { + *pnum_clobbers = 1; + return 72; + } + } + goto ret0; + + L1165: + x2 = XEXP (x1, 0); + if (GET_MODE (x2) == QImode && GET_CODE (x2) == MEM && 1) + goto L1166; + L1159: + if (call_insn_operand (x2, QImode)) + { + ro[0] = x2; + goto L1160; + } + goto ret0; + + L1166: + x3 = XEXP (x2, 0); + if (symbolic_operand (x3, SImode)) + { + ro[0] = x3; + goto L1167; + } + goto L1159; + + L1167: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) + goto L1168; + x2 = XEXP (x1, 0); + goto L1159; + + L1168: + x1 = XVECEXP (x0, 0, 1); + if (memory_operand (x1, DImode)) + { + ro[1] = x1; + goto L1169; + } + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1159; + + L1169: + x1 = XVECEXP (x0, 0, 2); + ro[2] = x1; + if (!HALF_PIC_P ()) + return 239; + x1 = XVECEXP (x0, 0, 0); + x2 = XEXP (x1, 0); + goto L1159; + + L1160: + x2 = XEXP (x1, 1); + if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1) + goto L1161; + goto ret0; + + L1161: + x1 = XVECEXP (x0, 0, 1); + if (memory_operand (x1, DImode)) + { + ro[1] = x1; + goto L1162; + } + goto ret0; + + L1162: + x1 = XVECEXP (x0, 0, 2); + ro[2] = x1; + return 238; + + L1175: + x1 = XVECEXP (x0, 0, 0); + if (GET_CODE (x1) == SET && 1) + goto L1176; + goto ret0; + + L1176: + x2 = XEXP (x1, 0); + switch (GET_MODE (x2)) + { + case BLKmode: + if (GET_CODE (x2) == MEM && 1) + goto L1177; + break; + case SImode: + if (general_operand (x2, SImode)) + { + ro[0] = x2; + goto L1193; + } + } + if (GET_CODE (x2) == CC0 && 1) + goto L1211; + goto ret0; + + L1177: + x3 = XEXP (x2, 0); + if (address_operand (x3, SImode)) + { + ro[0] = x3; + goto L1178; + } + goto ret0; + + L1178: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == BLKmode && GET_CODE (x2) == MEM && 1) + goto L1179; + goto ret0; + + L1179: + x3 = XEXP (x2, 0); + if (address_operand (x3, SImode)) + { + ro[1] = x3; + goto L1180; + } + goto ret0; + + L1180: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == USE && 1) + goto L1181; + goto ret0; + + L1181: + x2 = XEXP (x1, 0); + if (GET_CODE (x2) == CONST_INT && 1) + { + ro[2] = x2; + goto L1182; + } + goto ret0; + + L1182: + x1 = XVECEXP (x0, 0, 2); + if (GET_CODE (x1) == USE && 1) + goto L1183; + goto ret0; + + L1183: + x2 = XEXP (x1, 0); + if (immediate_operand (x2, SImode)) + { + ro[3] = x2; + goto L1184; + } + goto ret0; + + L1184: + x1 = XVECEXP (x0, 0, 3); + if (GET_CODE (x1) == CLOBBER && 1) + goto L1185; + goto ret0; + + L1185: + x2 = XEXP (x1, 0); + if (scratch_operand (x2, SImode)) + { + ro[4] = x2; + goto L1186; + } + goto ret0; + + L1186: + x1 = XVECEXP (x0, 0, 4); + if (GET_CODE (x1) == CLOBBER && 1) + goto L1187; + goto ret0; + + L1187: + x2 = XEXP (x1, 0); + if (rtx_equal_p (x2, ro[0]) && 1) + goto L1188; + goto ret0; + + L1188: + x1 = XVECEXP (x0, 0, 5); + if (GET_CODE (x1) == CLOBBER && 1) + goto L1189; + goto ret0; + + L1189: + x2 = XEXP (x1, 0); + if (rtx_equal_p (x2, ro[1]) && 1) + return 245; + goto ret0; + + L1193: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == COMPARE && 1) + goto L1194; + goto ret0; + + L1194: + x3 = XEXP (x2, 0); + if (GET_MODE (x3) == BLKmode && GET_CODE (x3) == MEM && 1) + goto L1195; + goto ret0; + + L1195: + x4 = XEXP (x3, 0); + if (address_operand (x4, SImode)) + { + ro[1] = x4; + goto L1196; + } + goto ret0; + + L1196: + x3 = XEXP (x2, 1); + if (GET_MODE (x3) == BLKmode && GET_CODE (x3) == MEM && 1) + goto L1197; + goto ret0; + + L1197: + x4 = XEXP (x3, 0); + if (address_operand (x4, SImode)) + { + ro[2] = x4; + goto L1198; + } + goto ret0; + + L1198: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == USE && 1) + goto L1199; + goto ret0; + + L1199: + x2 = XEXP (x1, 0); + if (register_operand (x2, SImode)) + { + ro[3] = x2; + goto L1200; + } + goto ret0; + + L1200: + x1 = XVECEXP (x0, 0, 2); + if (GET_CODE (x1) == USE && 1) + goto L1201; + goto ret0; + + L1201: + x2 = XEXP (x1, 0); + if (immediate_operand (x2, SImode)) + { + ro[4] = x2; + goto L1202; + } + goto ret0; + + L1202: + x1 = XVECEXP (x0, 0, 3); + if (GET_CODE (x1) == CLOBBER && 1) + goto L1203; + goto ret0; + + L1203: + x2 = XEXP (x1, 0); + if (rtx_equal_p (x2, ro[1]) && 1) + goto L1204; + goto ret0; + + L1204: + x1 = XVECEXP (x0, 0, 4); + if (GET_CODE (x1) == CLOBBER && 1) + goto L1205; + goto ret0; + + L1205: + x2 = XEXP (x1, 0); + if (rtx_equal_p (x2, ro[2]) && 1) + goto L1206; + goto ret0; + + L1206: + x1 = XVECEXP (x0, 0, 5); + if (GET_CODE (x1) == CLOBBER && 1) + goto L1207; + goto ret0; + + L1207: + x2 = XEXP (x1, 0); + if (rtx_equal_p (x2, ro[3]) && 1) + return 247; + goto ret0; + + L1211: + x2 = XEXP (x1, 1); + if (GET_MODE (x2) == SImode && GET_CODE (x2) == COMPARE && 1) + goto L1212; + goto ret0; + + L1212: + x3 = XEXP (x2, 0); + if (GET_MODE (x3) == BLKmode && GET_CODE (x3) == MEM && 1) + goto L1213; + goto ret0; + + L1213: + x4 = XEXP (x3, 0); + if (address_operand (x4, SImode)) + { + ro[0] = x4; + goto L1214; + } + goto ret0; + + L1214: + x3 = XEXP (x2, 1); + if (GET_MODE (x3) == BLKmode && GET_CODE (x3) == MEM && 1) + goto L1215; + goto ret0; + + L1215: + x4 = XEXP (x3, 0); + if (address_operand (x4, SImode)) + { + ro[1] = x4; + goto L1216; + } + goto ret0; + + L1216: + x1 = XVECEXP (x0, 0, 1); + if (GET_CODE (x1) == USE && 1) + goto L1217; + goto ret0; + + L1217: + x2 = XEXP (x1, 0); + if (register_operand (x2, SImode)) + { + ro[2] = x2; + goto L1218; + } + goto ret0; + + L1218: + x1 = XVECEXP (x0, 0, 2); + if (GET_CODE (x1) == USE && 1) + goto L1219; + goto ret0; + + L1219: + x2 = XEXP (x1, 0); + if (immediate_operand (x2, SImode)) + { + ro[3] = x2; + goto L1220; + } + goto ret0; + + L1220: + x1 = XVECEXP (x0, 0, 3); + if (GET_CODE (x1) == CLOBBER && 1) + goto L1221; + goto ret0; + + L1221: + x2 = XEXP (x1, 0); + if (rtx_equal_p (x2, ro[0]) && 1) + goto L1222; + goto ret0; + + L1222: + x1 = XVECEXP (x0, 0, 4); + if (GET_CODE (x1) == CLOBBER && 1) + goto L1223; + goto ret0; + + L1223: + x2 = XEXP (x1, 0); + if (rtx_equal_p (x2, ro[1]) && 1) + goto L1224; + goto ret0; + + L1224: + x1 = XVECEXP (x0, 0, 5); + if (GET_CODE (x1) == CLOBBER && 1) + goto L1225; + goto ret0; + + L1225: + x2 = XEXP (x1, 0); + if (rtx_equal_p (x2, ro[2]) && 1) + return 248; + goto ret0; + + L1117: + x1 = XEXP (x0, 0); + if (call_insn_operand (x1, QImode)) + { + ro[0] = x1; + goto L1118; + } + L1120: + if (GET_MODE (x1) == QImode && GET_CODE (x1) == MEM && 1) + goto L1121; + goto ret0; + + L1118: + x1 = XEXP (x0, 1); + if (general_operand (x1, SImode)) + { + ro[1] = x1; + return 229; + } + x1 = XEXP (x0, 0); + goto L1120; + + L1121: + x2 = XEXP (x1, 0); + if (symbolic_operand (x2, SImode)) + { + ro[0] = x2; + goto L1122; + } + goto ret0; + + L1122: + x1 = XEXP (x0, 1); + if (general_operand (x1, SImode)) + { + ro[1] = x1; + if (!HALF_PIC_P ()) + return 230; + } + goto ret0; + ret0: return -1; +} + +rtx +split_insns (x0, insn) + register rtx x0; + rtx insn; +{ + register rtx *ro = &recog_operand[0]; + register rtx x1, x2, x3, x4, x5, x6; + rtx tem; + + goto ret0; + ret0: return 0; +} + diff --git a/gnu/usr.bin/cc/lib/integrate.c b/gnu/usr.bin/cc/lib/integrate.c new file mode 100644 index 0000000000..73d839baff --- /dev/null +++ b/gnu/usr.bin/cc/lib/integrate.c @@ -0,0 +1,2902 @@ +/* Procedure integration for GNU CC. + Copyright (C) 1988, 1991, 1993 Free Software Foundation, Inc. + Contributed by Michael Tiemann (tiemann@cygnus.com) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include + +#include "config.h" +#include "rtl.h" +#include "tree.h" +#include "flags.h" +#include "insn-config.h" +#include "insn-flags.h" +#include "expr.h" +#include "output.h" +#include "integrate.h" +#include "real.h" +#include "function.h" + +#include "obstack.h" +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free + +extern struct obstack *function_maybepermanent_obstack; + +extern tree pushdecl (); +extern tree poplevel (); + +/* Similar, but round to the next highest integer that meets the + alignment. */ +#define CEIL_ROUND(VALUE,ALIGN) (((VALUE) + (ALIGN) - 1) & ~((ALIGN)- 1)) + +/* Default max number of insns a function can have and still be inline. + This is overridden on RISC machines. */ +#ifndef INTEGRATE_THRESHOLD +#define INTEGRATE_THRESHOLD(DECL) \ + (8 * (8 + list_length (DECL_ARGUMENTS (DECL)))) +#endif + +/* Save any constant pool constants in an insn. */ +static void save_constants (); + +/* Note when parameter registers are the destination of a SET. */ +static void note_modified_parmregs (); + +/* Copy an rtx for save_for_inline_copying. */ +static rtx copy_for_inline (); + +/* Make copies of MEMs in DECL_RTLs. */ +static void copy_decl_rtls (); + +static tree copy_decl_tree (); +static tree copy_decl_list (); + +static void integrate_parm_decls (); +static void integrate_decl_tree (); + +static void subst_constants (); + +/* Zero if the current function (whose FUNCTION_DECL is FNDECL) + is safe and reasonable to integrate into other functions. + Nonzero means value is a warning message with a single %s + for the function's name. */ + +char * +function_cannot_inline_p (fndecl) + register tree fndecl; +{ + register rtx insn; + tree last = tree_last (TYPE_ARG_TYPES (TREE_TYPE (fndecl))); + int max_insns = INTEGRATE_THRESHOLD (fndecl); + register int ninsns = 0; + register tree parms; + + /* No inlines with varargs. `grokdeclarator' gives a warning + message about that if `inline' is specified. This code + it put in to catch the volunteers. */ + if ((last && TREE_VALUE (last) != void_type_node) + || (DECL_ARGUMENTS (fndecl) && DECL_NAME (DECL_ARGUMENTS (fndecl)) + && ! strcmp (IDENTIFIER_POINTER (DECL_NAME (DECL_ARGUMENTS (fndecl))), + "__builtin_va_alist"))) + return "varargs function cannot be inline"; + + if (current_function_calls_alloca) + return "function using alloca cannot be inline"; + + if (current_function_contains_functions) + return "function with nested functions cannot be inline"; + + /* This restriction may be eliminated sometime soon. But for now, don't + worry about remapping the static chain. */ + if (current_function_needs_context) + return "nested function cannot be inline"; + + /* If its not even close, don't even look. */ + if (!DECL_INLINE (fndecl) && get_max_uid () > 3 * max_insns) + return "function too large to be inline"; + +#if 0 + /* Large stacks are OK now that inlined functions can share them. */ + /* Don't inline functions with large stack usage, + since they can make other recursive functions burn up stack. */ + if (!DECL_INLINE (fndecl) && get_frame_size () > 100) + return "function stack frame for inlining"; +#endif + +#if 0 + /* Don't inline functions which do not specify a function prototype and + have BLKmode argument or take the address of a parameter. */ + for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms)) + { + if (TYPE_MODE (TREE_TYPE (parms)) == BLKmode) + TREE_ADDRESSABLE (parms) = 1; + if (last == NULL_TREE && TREE_ADDRESSABLE (parms)) + return "no prototype, and parameter address used; cannot be inline"; + } +#endif + + /* We can't inline functions that return structures + the old-fashioned PCC way, copying into a static block. */ + if (current_function_returns_pcc_struct) + return "inline functions not supported for this return value type"; + + /* We can't inline functions that return structures of varying size. */ + if (int_size_in_bytes (TREE_TYPE (TREE_TYPE (fndecl))) < 0) + return "function with varying-size return value cannot be inline"; + + /* Cannot inline a function with a varying size argument. */ + for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms)) + if (int_size_in_bytes (TREE_TYPE (parms)) < 0) + return "function with varying-size parameter cannot be inline"; + + if (!DECL_INLINE (fndecl) && get_max_uid () > max_insns) + { + for (ninsns = 0, insn = get_first_nonparm_insn (); insn && ninsns < max_insns; + insn = NEXT_INSN (insn)) + { + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + ninsns++; + } + + if (ninsns >= max_insns) + return "function too large to be inline"; + } + + /* We cannot inline this function if forced_labels is non-zero. This + implies that a label in this function was used as an initializer. + Because labels can not be duplicated, all labels in the function + will be renamed when it is inlined. However, there is no way to find + and fix all variables initialized with addresses of labels in this + function, hence inlining is impossible. */ + + if (forced_labels) + return "function with label addresses used in initializers cannot inline"; + + return 0; +} + +/* Variables used within save_for_inline. */ + +/* Mapping from old pseudo-register to new pseudo-registers. + The first element of this map is reg_map[FIRST_PSEUDO_REGISTER]. + It is allocated in `save_for_inline' and `expand_inline_function', + and deallocated on exit from each of those routines. */ +static rtx *reg_map; + +/* Mapping from old code-labels to new code-labels. + The first element of this map is label_map[min_labelno]. + It is allocated in `save_for_inline' and `expand_inline_function', + and deallocated on exit from each of those routines. */ +static rtx *label_map; + +/* Mapping from old insn uid's to copied insns. + It is allocated in `save_for_inline' and `expand_inline_function', + and deallocated on exit from each of those routines. */ +static rtx *insn_map; + +/* Map pseudo reg number into the PARM_DECL for the parm living in the reg. + Zero for a reg that isn't a parm's home. + Only reg numbers less than max_parm_reg are mapped here. */ +static tree *parmdecl_map; + +/* Keep track of first pseudo-register beyond those that are parms. */ +static int max_parm_reg; + +/* When an insn is being copied by copy_for_inline, + this is nonzero if we have copied an ASM_OPERANDS. + In that case, it is the original input-operand vector. */ +static rtvec orig_asm_operands_vector; + +/* When an insn is being copied by copy_for_inline, + this is nonzero if we have copied an ASM_OPERANDS. + In that case, it is the copied input-operand vector. */ +static rtvec copy_asm_operands_vector; + +/* Likewise, this is the copied constraints vector. */ +static rtvec copy_asm_constraints_vector; + +/* In save_for_inline, nonzero if past the parm-initialization insns. */ +static int in_nonparm_insns; + +/* Subroutine for `save_for_inline{copying,nocopy}'. Performs initialization + needed to save FNDECL's insns and info for future inline expansion. */ + +static rtx +initialize_for_inline (fndecl, min_labelno, max_labelno, max_reg, copy) + tree fndecl; + int min_labelno; + int max_labelno; + int max_reg; + int copy; +{ + int function_flags, i; + rtvec arg_vector; + tree parms; + + /* Compute the values of any flags we must restore when inlining this. */ + + function_flags + = (current_function_calls_alloca * FUNCTION_FLAGS_CALLS_ALLOCA + + current_function_calls_setjmp * FUNCTION_FLAGS_CALLS_SETJMP + + current_function_calls_longjmp * FUNCTION_FLAGS_CALLS_LONGJMP + + current_function_returns_struct * FUNCTION_FLAGS_RETURNS_STRUCT + + current_function_returns_pcc_struct * FUNCTION_FLAGS_RETURNS_PCC_STRUCT + + current_function_needs_context * FUNCTION_FLAGS_NEEDS_CONTEXT + + current_function_has_nonlocal_label * FUNCTION_FLAGS_HAS_NONLOCAL_LABEL + + current_function_returns_pointer * FUNCTION_FLAGS_RETURNS_POINTER + + current_function_uses_const_pool * FUNCTION_FLAGS_USES_CONST_POOL + + current_function_uses_pic_offset_table * FUNCTION_FLAGS_USES_PIC_OFFSET_TABLE); + + /* Clear out PARMDECL_MAP. It was allocated in the caller's frame. */ + bzero (parmdecl_map, max_parm_reg * sizeof (tree)); + arg_vector = rtvec_alloc (list_length (DECL_ARGUMENTS (fndecl))); + + for (parms = DECL_ARGUMENTS (fndecl), i = 0; + parms; + parms = TREE_CHAIN (parms), i++) + { + rtx p = DECL_RTL (parms); + + if (GET_CODE (p) == MEM && copy) + { + /* Copy the rtl so that modifications of the addresses + later in compilation won't affect this arg_vector. + Virtual register instantiation can screw the address + of the rtl. */ + rtx new = copy_rtx (p); + + /* Don't leave the old copy anywhere in this decl. */ + if (DECL_RTL (parms) == DECL_INCOMING_RTL (parms) + || (GET_CODE (DECL_RTL (parms)) == MEM + && GET_CODE (DECL_INCOMING_RTL (parms)) == MEM + && (XEXP (DECL_RTL (parms), 0) + == XEXP (DECL_INCOMING_RTL (parms), 0)))) + DECL_INCOMING_RTL (parms) = new; + DECL_RTL (parms) = new; + } + + RTVEC_ELT (arg_vector, i) = p; + + if (GET_CODE (p) == REG) + parmdecl_map[REGNO (p)] = parms; + /* This flag is cleared later + if the function ever modifies the value of the parm. */ + TREE_READONLY (parms) = 1; + } + + /* Assume we start out in the insns that set up the parameters. */ + in_nonparm_insns = 0; + + /* The list of DECL_SAVED_INSNS, starts off with a header which + contains the following information: + + the first insn of the function (not including the insns that copy + parameters into registers). + the first parameter insn of the function, + the first label used by that function, + the last label used by that function, + the highest register number used for parameters, + the total number of registers used, + the size of the incoming stack area for parameters, + the number of bytes popped on return, + the stack slot list, + some flags that are used to restore compiler globals, + the value of current_function_outgoing_args_size, + the original argument vector, + and the original DECL_INITIAL. */ + + return gen_inline_header_rtx (NULL_RTX, NULL_RTX, min_labelno, max_labelno, + max_parm_reg, max_reg, + current_function_args_size, + current_function_pops_args, + stack_slot_list, function_flags, + current_function_outgoing_args_size, + arg_vector, (rtx) DECL_INITIAL (fndecl)); +} + +/* Subroutine for `save_for_inline{copying,nocopy}'. Finishes up the + things that must be done to make FNDECL expandable as an inline function. + HEAD contains the chain of insns to which FNDECL will expand. */ + +static void +finish_inline (fndecl, head) + tree fndecl; + rtx head; +{ + NEXT_INSN (head) = get_first_nonparm_insn (); + FIRST_PARM_INSN (head) = get_insns (); + DECL_SAVED_INSNS (fndecl) = head; + DECL_FRAME_SIZE (fndecl) = get_frame_size (); + DECL_INLINE (fndecl) = 1; +} + +/* Adjust the BLOCK_END_NOTE pointers in a given copied DECL tree so that + they all point to the new (copied) rtxs. */ + +static void +adjust_copied_decl_tree (block) + register tree block; +{ + register tree subblock; + register rtx original_end; + + original_end = BLOCK_END_NOTE (block); + if (original_end) + { + BLOCK_END_NOTE (block) = (rtx) NOTE_SOURCE_FILE (original_end); + NOTE_SOURCE_FILE (original_end) = 0; + } + + /* Process all subblocks. */ + for (subblock = BLOCK_SUBBLOCKS (block); + subblock; + subblock = TREE_CHAIN (subblock)) + adjust_copied_decl_tree (subblock); +} + +/* Make the insns and PARM_DECLs of the current function permanent + and record other information in DECL_SAVED_INSNS to allow inlining + of this function in subsequent calls. + + This function is called when we are going to immediately compile + the insns for FNDECL. The insns in maybepermanent_obstack cannot be + modified by the compilation process, so we copy all of them to + new storage and consider the new insns to be the insn chain to be + compiled. Our caller (rest_of_compilation) saves the original + DECL_INITIAL and DECL_ARGUMENTS; here we copy them. */ + +void +save_for_inline_copying (fndecl) + tree fndecl; +{ + rtx first_insn, last_insn, insn; + rtx head, copy; + int max_labelno, min_labelno, i, len; + int max_reg; + int max_uid; + rtx first_nonparm_insn; + + /* Make and emit a return-label if we have not already done so. + Do this before recording the bounds on label numbers. */ + + if (return_label == 0) + { + return_label = gen_label_rtx (); + emit_label (return_label); + } + + /* Get some bounds on the labels and registers used. */ + + max_labelno = max_label_num (); + min_labelno = get_first_label_num (); + max_reg = max_reg_num (); + + /* Set up PARMDECL_MAP which maps pseudo-reg number to its PARM_DECL. + Later we set TREE_READONLY to 0 if the parm is modified inside the fn. + Also set up ARG_VECTOR, which holds the unmodified DECL_RTX values + for the parms, prior to elimination of virtual registers. + These values are needed for substituting parms properly. */ + + max_parm_reg = max_parm_reg_num (); + parmdecl_map = (tree *) alloca (max_parm_reg * sizeof (tree)); + + head = initialize_for_inline (fndecl, min_labelno, max_labelno, max_reg, 1); + + if (current_function_uses_const_pool) + { + /* Replace any constant pool references with the actual constant. We + will put the constants back in the copy made below. */ + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + save_constants (&PATTERN (insn)); + if (REG_NOTES (insn)) + save_constants (®_NOTES (insn)); + } + + /* Clear out the constant pool so that we can recreate it with the + copied constants below. */ + init_const_rtx_hash_table (); + clear_const_double_mem (); + } + + max_uid = INSN_UID (head); + + /* We have now allocated all that needs to be allocated permanently + on the rtx obstack. Set our high-water mark, so that we + can free the rest of this when the time comes. */ + + preserve_data (); + + /* Copy the chain insns of this function. + Install the copied chain as the insns of this function, + for continued compilation; + the original chain is recorded as the DECL_SAVED_INSNS + for inlining future calls. */ + + /* If there are insns that copy parms from the stack into pseudo registers, + those insns are not copied. `expand_inline_function' must + emit the correct code to handle such things. */ + + insn = get_insns (); + if (GET_CODE (insn) != NOTE) + abort (); + first_insn = rtx_alloc (NOTE); + NOTE_SOURCE_FILE (first_insn) = NOTE_SOURCE_FILE (insn); + NOTE_LINE_NUMBER (first_insn) = NOTE_LINE_NUMBER (insn); + INSN_UID (first_insn) = INSN_UID (insn); + PREV_INSN (first_insn) = NULL; + NEXT_INSN (first_insn) = NULL; + last_insn = first_insn; + + /* Each pseudo-reg in the old insn chain must have a unique rtx in the copy. + Make these new rtx's now, and install them in regno_reg_rtx, so they + will be the official pseudo-reg rtx's for the rest of compilation. */ + + reg_map = (rtx *) alloca ((max_reg + 1) * sizeof (rtx)); + + len = sizeof (struct rtx_def) + (GET_RTX_LENGTH (REG) - 1) * sizeof (rtunion); + for (i = max_reg - 1; i > LAST_VIRTUAL_REGISTER; i--) + reg_map[i] = (rtx)obstack_copy (function_maybepermanent_obstack, + regno_reg_rtx[i], len); + + bcopy (reg_map + LAST_VIRTUAL_REGISTER + 1, + regno_reg_rtx + LAST_VIRTUAL_REGISTER + 1, + (max_reg - (LAST_VIRTUAL_REGISTER + 1)) * sizeof (rtx)); + + /* Likewise each label rtx must have a unique rtx as its copy. */ + + label_map = (rtx *)alloca ((max_labelno - min_labelno) * sizeof (rtx)); + label_map -= min_labelno; + + for (i = min_labelno; i < max_labelno; i++) + label_map[i] = gen_label_rtx (); + + /* Record the mapping of old insns to copied insns. */ + + insn_map = (rtx *) alloca (max_uid * sizeof (rtx)); + bzero (insn_map, max_uid * sizeof (rtx)); + + /* Get the insn which signals the end of parameter setup code. */ + first_nonparm_insn = get_first_nonparm_insn (); + + /* Copy any entries in regno_reg_rtx or DECL_RTLs that reference MEM + (the former occurs when a variable has its address taken) + since these may be shared and can be changed by virtual + register instantiation. DECL_RTL values for our arguments + have already been copied by initialize_for_inline. */ + for (i = LAST_VIRTUAL_REGISTER + 1; i < max_reg; i++) + if (GET_CODE (regno_reg_rtx[i]) == MEM) + XEXP (regno_reg_rtx[i], 0) + = copy_for_inline (XEXP (regno_reg_rtx[i], 0)); + + /* Copy the tree of subblocks of the function, and the decls in them. + We will use the copy for compiling this function, then restore the original + subblocks and decls for use when inlining this function. + + Several parts of the compiler modify BLOCK trees. In particular, + instantiate_virtual_regs will instantiate any virtual regs + mentioned in the DECL_RTLs of the decls, and loop + unrolling will replicate any BLOCK trees inside an unrolled loop. + + The modified subblocks or DECL_RTLs would be incorrect for the original rtl + which we will use for inlining. The rtl might even contain pseudoregs + whose space has been freed. */ + + DECL_INITIAL (fndecl) = copy_decl_tree (DECL_INITIAL (fndecl)); + DECL_ARGUMENTS (fndecl) = copy_decl_list (DECL_ARGUMENTS (fndecl)); + + /* Now copy each DECL_RTL which is a MEM, + so it is safe to modify their addresses. */ + copy_decl_rtls (DECL_INITIAL (fndecl)); + + /* The fndecl node acts as its own progenitor, so mark it as such. */ + DECL_ABSTRACT_ORIGIN (fndecl) = fndecl; + + /* Now copy the chain of insns. Do this twice. The first copy the insn + itself and its body. The second time copy of REG_NOTES. This is because + a REG_NOTE may have a forward pointer to another insn. */ + + for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn)) + { + orig_asm_operands_vector = 0; + + if (insn == first_nonparm_insn) + in_nonparm_insns = 1; + + switch (GET_CODE (insn)) + { + case NOTE: + /* No need to keep these. */ + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED) + continue; + + copy = rtx_alloc (NOTE); + NOTE_LINE_NUMBER (copy) = NOTE_LINE_NUMBER (insn); + if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_BLOCK_END) + NOTE_SOURCE_FILE (copy) = NOTE_SOURCE_FILE (insn); + else + { + NOTE_SOURCE_FILE (insn) = (char *) copy; + NOTE_SOURCE_FILE (copy) = 0; + } + break; + + case INSN: + case CALL_INSN: + case JUMP_INSN: + copy = rtx_alloc (GET_CODE (insn)); + PATTERN (copy) = copy_for_inline (PATTERN (insn)); + INSN_CODE (copy) = -1; + LOG_LINKS (copy) = NULL; + RTX_INTEGRATED_P (copy) = RTX_INTEGRATED_P (insn); + break; + + case CODE_LABEL: + copy = label_map[CODE_LABEL_NUMBER (insn)]; + LABEL_NAME (copy) = LABEL_NAME (insn); + break; + + case BARRIER: + copy = rtx_alloc (BARRIER); + break; + + default: + abort (); + } + INSN_UID (copy) = INSN_UID (insn); + insn_map[INSN_UID (insn)] = copy; + NEXT_INSN (last_insn) = copy; + PREV_INSN (copy) = last_insn; + last_insn = copy; + } + + adjust_copied_decl_tree (DECL_INITIAL (fndecl)); + + /* Now copy the REG_NOTES. */ + for (insn = NEXT_INSN (get_insns ()); insn; insn = NEXT_INSN (insn)) + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && insn_map[INSN_UID(insn)]) + REG_NOTES (insn_map[INSN_UID (insn)]) + = copy_for_inline (REG_NOTES (insn)); + + NEXT_INSN (last_insn) = NULL; + + finish_inline (fndecl, head); + + set_new_first_and_last_insn (first_insn, last_insn); +} + +/* Return a copy of a chain of nodes, chained through the TREE_CHAIN field. + For example, this can copy a list made of TREE_LIST nodes. While copying, + for each node copied which doesn't already have is DECL_ABSTRACT_ORIGIN + set to some non-zero value, set the DECL_ABSTRACT_ORIGIN of the copy to + point to the corresponding (abstract) original node. */ + +static tree +copy_decl_list (list) + tree list; +{ + tree head; + register tree prev, next; + + if (list == 0) + return 0; + + head = prev = copy_node (list); + if (DECL_ABSTRACT_ORIGIN (head) == NULL_TREE) + DECL_ABSTRACT_ORIGIN (head) = list; + next = TREE_CHAIN (list); + while (next) + { + register tree copy; + + copy = copy_node (next); + if (DECL_ABSTRACT_ORIGIN (copy) == NULL_TREE) + DECL_ABSTRACT_ORIGIN (copy) = next; + TREE_CHAIN (prev) = copy; + prev = copy; + next = TREE_CHAIN (next); + } + return head; +} + +/* Make a copy of the entire tree of blocks BLOCK, and return it. */ + +static tree +copy_decl_tree (block) + tree block; +{ + tree t, vars, subblocks; + + vars = copy_decl_list (BLOCK_VARS (block)); + subblocks = 0; + + /* Process all subblocks. */ + for (t = BLOCK_SUBBLOCKS (block); t; t = TREE_CHAIN (t)) + { + tree copy = copy_decl_tree (t); + TREE_CHAIN (copy) = subblocks; + subblocks = copy; + } + + t = copy_node (block); + BLOCK_VARS (t) = vars; + BLOCK_SUBBLOCKS (t) = nreverse (subblocks); + /* If the BLOCK being cloned is already marked as having been instantiated + from something else, then leave that `origin' marking alone. Elsewise, + mark the clone as having originated from the BLOCK we are cloning. */ + if (BLOCK_ABSTRACT_ORIGIN (t) == NULL_TREE) + BLOCK_ABSTRACT_ORIGIN (t) = block; + return t; +} + +/* Copy DECL_RTLs in all decls in the given BLOCK node. */ + +static void +copy_decl_rtls (block) + tree block; +{ + tree t; + + for (t = BLOCK_VARS (block); t; t = TREE_CHAIN (t)) + if (DECL_RTL (t) && GET_CODE (DECL_RTL (t)) == MEM) + DECL_RTL (t) = copy_for_inline (DECL_RTL (t)); + + /* Process all subblocks. */ + for (t = BLOCK_SUBBLOCKS (block); t; t = TREE_CHAIN (t)) + copy_decl_rtls (t); +} + +/* Make the insns and PARM_DECLs of the current function permanent + and record other information in DECL_SAVED_INSNS to allow inlining + of this function in subsequent calls. + + This routine need not copy any insns because we are not going + to immediately compile the insns in the insn chain. There + are two cases when we would compile the insns for FNDECL: + (1) when FNDECL is expanded inline, and (2) when FNDECL needs to + be output at the end of other compilation, because somebody took + its address. In the first case, the insns of FNDECL are copied + as it is expanded inline, so FNDECL's saved insns are not + modified. In the second case, FNDECL is used for the last time, + so modifying the rtl is not a problem. + + ??? Actually, we do not verify that FNDECL is not inline expanded + by other functions which must also be written down at the end + of compilation. We could set flag_no_inline to nonzero when + the time comes to write down such functions. */ + +void +save_for_inline_nocopy (fndecl) + tree fndecl; +{ + rtx insn; + rtx head, copy; + tree parms; + int max_labelno, min_labelno, i, len; + int max_reg; + int max_uid; + rtx first_nonparm_insn; + int function_flags; + + /* Set up PARMDECL_MAP which maps pseudo-reg number to its PARM_DECL. + Later we set TREE_READONLY to 0 if the parm is modified inside the fn. + Also set up ARG_VECTOR, which holds the unmodified DECL_RTX values + for the parms, prior to elimination of virtual registers. + These values are needed for substituting parms properly. */ + + max_parm_reg = max_parm_reg_num (); + parmdecl_map = (tree *) alloca (max_parm_reg * sizeof (tree)); + + /* Make and emit a return-label if we have not already done so. */ + + if (return_label == 0) + { + return_label = gen_label_rtx (); + emit_label (return_label); + } + + head = initialize_for_inline (fndecl, get_first_label_num (), + max_label_num (), max_reg_num (), 0); + + /* If there are insns that copy parms from the stack into pseudo registers, + those insns are not copied. `expand_inline_function' must + emit the correct code to handle such things. */ + + insn = get_insns (); + if (GET_CODE (insn) != NOTE) + abort (); + + /* Get the insn which signals the end of parameter setup code. */ + first_nonparm_insn = get_first_nonparm_insn (); + + /* Now just scan the chain of insns to see what happens to our + PARM_DECLs. If a PARM_DECL is used but never modified, we + can substitute its rtl directly when expanding inline (and + perform constant folding when its incoming value is constant). + Otherwise, we have to copy its value into a new register and track + the new register's life. */ + + for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn)) + { + if (insn == first_nonparm_insn) + in_nonparm_insns = 1; + + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + if (current_function_uses_const_pool) + { + /* Replace any constant pool references with the actual constant. + We will put the constant back if we need to write the + function out after all. */ + save_constants (&PATTERN (insn)); + if (REG_NOTES (insn)) + save_constants (®_NOTES (insn)); + } + + /* Record what interesting things happen to our parameters. */ + note_stores (PATTERN (insn), note_modified_parmregs); + } + } + + /* We have now allocated all that needs to be allocated permanently + on the rtx obstack. Set our high-water mark, so that we + can free the rest of this when the time comes. */ + + preserve_data (); + + finish_inline (fndecl, head); +} + +/* Given PX, a pointer into an insn, search for references to the constant + pool. Replace each with a CONST that has the mode of the original + constant, contains the constant, and has RTX_INTEGRATED_P set. + Similarly, constant pool addresses not enclosed in a MEM are replaced + with an ADDRESS rtx which also gives the constant, mode, and has + RTX_INTEGRATED_P set. */ + +static void +save_constants (px) + rtx *px; +{ + rtx x; + int i, j; + + again: + x = *px; + + /* If this is a CONST_DOUBLE, don't try to fix things up in + CONST_DOUBLE_MEM, because this is an infinite recursion. */ + if (GET_CODE (x) == CONST_DOUBLE) + return; + else if (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (XEXP (x,0))) + { + enum machine_mode const_mode = get_pool_mode (XEXP (x, 0)); + rtx new = gen_rtx (CONST, const_mode, get_pool_constant (XEXP (x, 0))); + RTX_INTEGRATED_P (new) = 1; + + /* If the MEM was in a different mode than the constant (perhaps we + were only looking at the low-order part), surround it with a + SUBREG so we can save both modes. */ + + if (GET_MODE (x) != const_mode) + { + new = gen_rtx (SUBREG, GET_MODE (x), new, 0); + RTX_INTEGRATED_P (new) = 1; + } + + *px = new; + save_constants (&XEXP (*px, 0)); + } + else if (GET_CODE (x) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (x)) + { + *px = gen_rtx (ADDRESS, get_pool_mode (x), get_pool_constant (x)); + save_constants (&XEXP (*px, 0)); + RTX_INTEGRATED_P (*px) = 1; + } + + else + { + char *fmt = GET_RTX_FORMAT (GET_CODE (x)); + int len = GET_RTX_LENGTH (GET_CODE (x)); + + for (i = len-1; i >= 0; i--) + { + switch (fmt[i]) + { + case 'E': + for (j = 0; j < XVECLEN (x, i); j++) + save_constants (&XVECEXP (x, i, j)); + break; + + case 'e': + if (XEXP (x, i) == 0) + continue; + if (i == 0) + { + /* Hack tail-recursion here. */ + px = &XEXP (x, 0); + goto again; + } + save_constants (&XEXP (x, i)); + break; + } + } + } +} + +/* Note whether a parameter is modified or not. */ + +static void +note_modified_parmregs (reg, x) + rtx reg; + rtx x; +{ + if (GET_CODE (reg) == REG && in_nonparm_insns + && REGNO (reg) < max_parm_reg + && REGNO (reg) >= FIRST_PSEUDO_REGISTER + && parmdecl_map[REGNO (reg)] != 0) + TREE_READONLY (parmdecl_map[REGNO (reg)]) = 0; +} + +/* Copy the rtx ORIG recursively, replacing pseudo-regs and labels + according to `reg_map' and `label_map'. The original rtl insns + will be saved for inlining; this is used to make a copy + which is used to finish compiling the inline function itself. + + If we find a "saved" constant pool entry, one which was replaced with + the value of the constant, convert it back to a constant pool entry. + Since the pool wasn't touched, this should simply restore the old + address. + + All other kinds of rtx are copied except those that can never be + changed during compilation. */ + +static rtx +copy_for_inline (orig) + rtx orig; +{ + register rtx x = orig; + register int i; + register enum rtx_code code; + register char *format_ptr; + + if (x == 0) + return x; + + code = GET_CODE (x); + + /* These types may be freely shared. */ + + switch (code) + { + case QUEUED: + case CONST_INT: + case SYMBOL_REF: + case PC: + case CC0: + return x; + + case CONST_DOUBLE: + /* We have to make a new CONST_DOUBLE to ensure that we account for + it correctly. Using the old CONST_DOUBLE_MEM data is wrong. */ + if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) + { + REAL_VALUE_TYPE d; + + REAL_VALUE_FROM_CONST_DOUBLE (d, x); + return immed_real_const_1 (d, GET_MODE (x)); + } + else + return immed_double_const (CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x), + VOIDmode); + + case CONST: + /* Get constant pool entry for constant in the pool. */ + if (RTX_INTEGRATED_P (x)) + return validize_mem (force_const_mem (GET_MODE (x), + copy_for_inline (XEXP (x, 0)))); + break; + + case SUBREG: + /* Get constant pool entry, but access in different mode. */ + if (RTX_INTEGRATED_P (x)) + { + rtx new + = force_const_mem (GET_MODE (SUBREG_REG (x)), + copy_for_inline (XEXP (SUBREG_REG (x), 0))); + + PUT_MODE (new, GET_MODE (x)); + return validize_mem (new); + } + break; + + case ADDRESS: + /* If not special for constant pool error. Else get constant pool + address. */ + if (! RTX_INTEGRATED_P (x)) + abort (); + + return XEXP (force_const_mem (GET_MODE (x), + copy_for_inline (XEXP (x, 0))), 0); + + case ASM_OPERANDS: + /* If a single asm insn contains multiple output operands + then it contains multiple ASM_OPERANDS rtx's that share operand 3. + We must make sure that the copied insn continues to share it. */ + if (orig_asm_operands_vector == XVEC (orig, 3)) + { + x = rtx_alloc (ASM_OPERANDS); + XSTR (x, 0) = XSTR (orig, 0); + XSTR (x, 1) = XSTR (orig, 1); + XINT (x, 2) = XINT (orig, 2); + XVEC (x, 3) = copy_asm_operands_vector; + XVEC (x, 4) = copy_asm_constraints_vector; + XSTR (x, 5) = XSTR (orig, 5); + XINT (x, 6) = XINT (orig, 6); + return x; + } + break; + + case MEM: + /* A MEM is usually allowed to be shared if its address is constant + or is a constant plus one of the special registers. + + We do not allow sharing of addresses that are either a special + register or the sum of a constant and a special register because + it is possible for unshare_all_rtl to copy the address, into memory + that won't be saved. Although the MEM can safely be shared, and + won't be copied there, the address itself cannot be shared, and may + need to be copied. + + There are also two exceptions with constants: The first is if the + constant is a LABEL_REF or the sum of the LABEL_REF + and an integer. This case can happen if we have an inline + function that supplies a constant operand to the call of another + inline function that uses it in a switch statement. In this case, + we will be replacing the LABEL_REF, so we have to replace this MEM + as well. + + The second case is if we have a (const (plus (address ..) ...)). + In that case we need to put back the address of the constant pool + entry. */ + + if (CONSTANT_ADDRESS_P (XEXP (x, 0)) + && GET_CODE (XEXP (x, 0)) != LABEL_REF + && ! (GET_CODE (XEXP (x, 0)) == CONST + && (GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS + && ((GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) + == LABEL_REF) + || (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) + == ADDRESS))))) + return x; + break; + + case LABEL_REF: + { + /* Must point to the new insn. */ + return gen_rtx (LABEL_REF, GET_MODE (orig), + label_map[CODE_LABEL_NUMBER (XEXP (orig, 0))]); + } + + case REG: + if (REGNO (x) > LAST_VIRTUAL_REGISTER) + return reg_map [REGNO (x)]; + else + return x; + + case SET: + /* If a parm that gets modified lives in a pseudo-reg, + clear its TREE_READONLY to prevent certain optimizations. */ + { + rtx dest = SET_DEST (x); + + while (GET_CODE (dest) == STRICT_LOW_PART + || GET_CODE (dest) == ZERO_EXTRACT + || GET_CODE (dest) == SUBREG) + dest = XEXP (dest, 0); + + if (GET_CODE (dest) == REG + && REGNO (dest) < max_parm_reg + && REGNO (dest) >= FIRST_PSEUDO_REGISTER + && parmdecl_map[REGNO (dest)] != 0 + /* The insn to load an arg pseudo from a stack slot + does not count as modifying it. */ + && in_nonparm_insns) + TREE_READONLY (parmdecl_map[REGNO (dest)]) = 0; + } + break; + +#if 0 /* This is a good idea, but here is the wrong place for it. */ + /* Arrange that CONST_INTs always appear as the second operand + if they appear, and that `frame_pointer_rtx' or `arg_pointer_rtx' + always appear as the first. */ + case PLUS: + if (GET_CODE (XEXP (x, 0)) == CONST_INT + || (XEXP (x, 1) == frame_pointer_rtx + || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM + && XEXP (x, 1) == arg_pointer_rtx))) + { + rtx t = XEXP (x, 0); + XEXP (x, 0) = XEXP (x, 1); + XEXP (x, 1) = t; + } + break; +#endif + } + + /* Replace this rtx with a copy of itself. */ + + x = rtx_alloc (code); + bcopy (orig, x, (sizeof (*x) - sizeof (x->fld) + + sizeof (x->fld[0]) * GET_RTX_LENGTH (code))); + + /* Now scan the subexpressions recursively. + We can store any replaced subexpressions directly into X + since we know X is not shared! Any vectors in X + must be copied if X was copied. */ + + format_ptr = GET_RTX_FORMAT (code); + + for (i = 0; i < GET_RTX_LENGTH (code); i++) + { + switch (*format_ptr++) + { + case 'e': + XEXP (x, i) = copy_for_inline (XEXP (x, i)); + break; + + case 'u': + /* Change any references to old-insns to point to the + corresponding copied insns. */ + XEXP (x, i) = insn_map[INSN_UID (XEXP (x, i))]; + break; + + case 'E': + if (XVEC (x, i) != NULL && XVECLEN (x, i) != 0) + { + register int j; + + XVEC (x, i) = gen_rtvec_v (XVECLEN (x, i), &XVECEXP (x, i, 0)); + for (j = 0; j < XVECLEN (x, i); j++) + XVECEXP (x, i, j) + = copy_for_inline (XVECEXP (x, i, j)); + } + break; + } + } + + if (code == ASM_OPERANDS && orig_asm_operands_vector == 0) + { + orig_asm_operands_vector = XVEC (orig, 3); + copy_asm_operands_vector = XVEC (x, 3); + copy_asm_constraints_vector = XVEC (x, 4); + } + + return x; +} + +/* Unfortunately, we need a global copy of const_equiv map for communication + with a function called from note_stores. Be *very* careful that this + is used properly in the presence of recursion. */ + +rtx *global_const_equiv_map; + +#define FIXED_BASE_PLUS_P(X) \ + (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \ + && GET_CODE (XEXP (X, 0)) == REG \ + && REGNO (XEXP (X, 0)) >= FIRST_VIRTUAL_REGISTER \ + && REGNO (XEXP (X, 0)) <= LAST_VIRTUAL_REGISTER) + +/* Integrate the procedure defined by FNDECL. Note that this function + may wind up calling itself. Since the static variables are not + reentrant, we do not assign them until after the possibility + of recursion is eliminated. + + If IGNORE is nonzero, do not produce a value. + Otherwise store the value in TARGET if it is nonzero and that is convenient. + + Value is: + (rtx)-1 if we could not substitute the function + 0 if we substituted it and it does not produce a value + else an rtx for where the value is stored. */ + +rtx +expand_inline_function (fndecl, parms, target, ignore, type, structure_value_addr) + tree fndecl, parms; + rtx target; + int ignore; + tree type; + rtx structure_value_addr; +{ + tree formal, actual, block; + rtx header = DECL_SAVED_INSNS (fndecl); + rtx insns = FIRST_FUNCTION_INSN (header); + rtx parm_insns = FIRST_PARM_INSN (header); + tree *arg_trees; + rtx *arg_vals; + rtx insn; + int max_regno; + register int i; + int min_labelno = FIRST_LABELNO (header); + int max_labelno = LAST_LABELNO (header); + int nargs; + rtx local_return_label = 0; + rtx loc; + rtx temp; + struct inline_remap *map; + rtx cc0_insn = 0; + rtvec arg_vector = ORIGINAL_ARG_VECTOR (header); + + /* Allow for equivalences of the pseudos we make for virtual fp and ap. */ + max_regno = MAX_REGNUM (header) + 3; + if (max_regno < FIRST_PSEUDO_REGISTER) + abort (); + + nargs = list_length (DECL_ARGUMENTS (fndecl)); + + /* We expect PARMS to have the right length; don't crash if not. */ + if (list_length (parms) != nargs) + return (rtx) (HOST_WIDE_INT) -1; + /* Also check that the parms type match. Since the appropriate + conversions or default promotions have already been applied, + the machine modes should match exactly. */ + for (formal = DECL_ARGUMENTS (fndecl), + actual = parms; + formal; + formal = TREE_CHAIN (formal), + actual = TREE_CHAIN (actual)) + { + tree arg = TREE_VALUE (actual); + enum machine_mode mode = TYPE_MODE (DECL_ARG_TYPE (formal)); + if (mode != TYPE_MODE (TREE_TYPE (arg))) + return (rtx) (HOST_WIDE_INT) -1; + /* If they are block mode, the types should match exactly. + They don't match exactly if TREE_TYPE (FORMAL) == ERROR_MARK_NODE, + which could happen if the parameter has incomplete type. */ + if (mode == BLKmode && TREE_TYPE (arg) != TREE_TYPE (formal)) + return (rtx) (HOST_WIDE_INT) -1; + } + + /* Make a binding contour to keep inline cleanups called at + outer function-scope level from looking like they are shadowing + parameter declarations. */ + pushlevel (0); + + /* Make a fresh binding contour that we can easily remove. */ + pushlevel (0); + expand_start_bindings (0); + if (GET_CODE (parm_insns) == NOTE + && NOTE_LINE_NUMBER (parm_insns) > 0) + { + rtx note = emit_note (NOTE_SOURCE_FILE (parm_insns), + NOTE_LINE_NUMBER (parm_insns)); + if (note) + RTX_INTEGRATED_P (note) = 1; + } + + /* Expand the function arguments. Do this first so that any + new registers get created before we allocate the maps. */ + + arg_vals = (rtx *) alloca (nargs * sizeof (rtx)); + arg_trees = (tree *) alloca (nargs * sizeof (tree)); + + for (formal = DECL_ARGUMENTS (fndecl), actual = parms, i = 0; + formal; + formal = TREE_CHAIN (formal), actual = TREE_CHAIN (actual), i++) + { + /* Actual parameter, converted to the type of the argument within the + function. */ + tree arg = convert (TREE_TYPE (formal), TREE_VALUE (actual)); + /* Mode of the variable used within the function. */ + enum machine_mode mode = TYPE_MODE (TREE_TYPE (formal)); + /* Where parameter is located in the function. */ + rtx copy; + + /* Make sure this formal has some correspondence in the users code + * before emitting any line notes for it. */ + if (DECL_SOURCE_LINE (formal)) + { + rtx note = emit_note (DECL_SOURCE_FILE (formal), + DECL_SOURCE_LINE (formal)); + if (note) + RTX_INTEGRATED_P (note) = 1; + } + + arg_trees[i] = arg; + loc = RTVEC_ELT (arg_vector, i); + + /* If this is an object passed by invisible reference, we copy the + object into a stack slot and save its address. If this will go + into memory, we do nothing now. Otherwise, we just expand the + argument. */ + if (GET_CODE (loc) == MEM && GET_CODE (XEXP (loc, 0)) == REG + && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER) + { + rtx stack_slot + = assign_stack_temp (TYPE_MODE (TREE_TYPE (arg)), + int_size_in_bytes (TREE_TYPE (arg)), 1); + + store_expr (arg, stack_slot, 0); + + arg_vals[i] = XEXP (stack_slot, 0); + } + else if (GET_CODE (loc) != MEM) + { + if (GET_MODE (loc) != TYPE_MODE (TREE_TYPE (arg))) + /* The mode if LOC and ARG can differ if LOC was a variable + that had its mode promoted via PROMOTED_MODE. */ + arg_vals[i] = convert_to_mode (GET_MODE (loc), + expand_expr (arg, NULL_RTX, mode, + EXPAND_SUM), + TREE_UNSIGNED (TREE_TYPE (formal))); + else + arg_vals[i] = expand_expr (arg, NULL_RTX, mode, EXPAND_SUM); + } + else + arg_vals[i] = 0; + + if (arg_vals[i] != 0 + && (! TREE_READONLY (formal) + /* If the parameter is not read-only, copy our argument through + a register. Also, we cannot use ARG_VALS[I] if it overlaps + TARGET in any way. In the inline function, they will likely + be two different pseudos, and `safe_from_p' will make all + sorts of smart assumptions about their not conflicting. + But if ARG_VALS[I] overlaps TARGET, these assumptions are + wrong, so put ARG_VALS[I] into a fresh register. */ + || (target != 0 + && (GET_CODE (arg_vals[i]) == REG + || GET_CODE (arg_vals[i]) == SUBREG + || GET_CODE (arg_vals[i]) == MEM) + && reg_overlap_mentioned_p (arg_vals[i], target)) + /* ??? We must always copy a SUBREG into a REG, because it might + get substituted into an address, and not all ports correctly + handle SUBREGs in addresses. */ + || (GET_CODE (arg_vals[i]) == SUBREG))) + arg_vals[i] = copy_to_mode_reg (GET_MODE (loc), arg_vals[i]); + } + + /* Allocate the structures we use to remap things. */ + + map = (struct inline_remap *) alloca (sizeof (struct inline_remap)); + map->fndecl = fndecl; + + map->reg_map = (rtx *) alloca (max_regno * sizeof (rtx)); + bzero (map->reg_map, max_regno * sizeof (rtx)); + + map->label_map = (rtx *)alloca ((max_labelno - min_labelno) * sizeof (rtx)); + map->label_map -= min_labelno; + + map->insn_map = (rtx *) alloca (INSN_UID (header) * sizeof (rtx)); + bzero (map->insn_map, INSN_UID (header) * sizeof (rtx)); + map->min_insnno = 0; + map->max_insnno = INSN_UID (header); + + map->integrating = 1; + + /* const_equiv_map maps pseudos in our routine to constants, so it needs to + be large enough for all our pseudos. This is the number we are currently + using plus the number in the called routine, plus 15 for each arg, + five to compute the virtual frame pointer, and five for the return value. + This should be enough for most cases. We do not reference entries + outside the range of the map. + + ??? These numbers are quite arbitrary and were obtained by + experimentation. At some point, we should try to allocate the + table after all the parameters are set up so we an more accurately + estimate the number of pseudos we will need. */ + + map->const_equiv_map_size + = max_reg_num () + (max_regno - FIRST_PSEUDO_REGISTER) + 15 * nargs + 10; + + map->const_equiv_map + = (rtx *)alloca (map->const_equiv_map_size * sizeof (rtx)); + bzero (map->const_equiv_map, map->const_equiv_map_size * sizeof (rtx)); + + map->const_age_map + = (unsigned *)alloca (map->const_equiv_map_size * sizeof (unsigned)); + bzero (map->const_age_map, map->const_equiv_map_size * sizeof (unsigned)); + map->const_age = 0; + + /* Record the current insn in case we have to set up pointers to frame + and argument memory blocks. */ + map->insns_at_start = get_last_insn (); + + /* Update the outgoing argument size to allow for those in the inlined + function. */ + if (OUTGOING_ARGS_SIZE (header) > current_function_outgoing_args_size) + current_function_outgoing_args_size = OUTGOING_ARGS_SIZE (header); + + /* If the inline function needs to make PIC references, that means + that this function's PIC offset table must be used. */ + if (FUNCTION_FLAGS (header) & FUNCTION_FLAGS_USES_PIC_OFFSET_TABLE) + current_function_uses_pic_offset_table = 1; + + /* Process each argument. For each, set up things so that the function's + reference to the argument will refer to the argument being passed. + We only replace REG with REG here. Any simplifications are done + via const_equiv_map. + + We make two passes: In the first, we deal with parameters that will + be placed into registers, since we need to ensure that the allocated + register number fits in const_equiv_map. Then we store all non-register + parameters into their memory location. */ + + for (i = 0; i < nargs; i++) + { + rtx copy = arg_vals[i]; + + loc = RTVEC_ELT (arg_vector, i); + + /* There are three cases, each handled separately. */ + if (GET_CODE (loc) == MEM && GET_CODE (XEXP (loc, 0)) == REG + && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER) + { + /* This must be an object passed by invisible reference (it could + also be a variable-sized object, but we forbid inlining functions + with variable-sized arguments). COPY is the address of the + actual value (this computation will cause it to be copied). We + map that address for the register, noting the actual address as + an equivalent in case it can be substituted into the insns. */ + + if (GET_CODE (copy) != REG) + { + temp = copy_addr_to_reg (copy); + if (CONSTANT_P (copy) || FIXED_BASE_PLUS_P (copy)) + { + map->const_equiv_map[REGNO (temp)] = copy; + map->const_age_map[REGNO (temp)] = CONST_AGE_PARM; + } + copy = temp; + } + map->reg_map[REGNO (XEXP (loc, 0))] = copy; + } + else if (GET_CODE (loc) == MEM) + { + /* This is the case of a parameter that lives in memory. + It will live in the block we allocate in the called routine's + frame that simulates the incoming argument area. Do nothing + now; we will call store_expr later. */ + ; + } + else if (GET_CODE (loc) == REG) + { + /* This is the good case where the parameter is in a register. + If it is read-only and our argument is a constant, set up the + constant equivalence. + + If LOC is REG_USERVAR_P, the usual case, COPY must also have + that flag set if it is a register. */ + + if ((GET_CODE (copy) != REG && GET_CODE (copy) != SUBREG) + || (GET_CODE (copy) == REG && REG_USERVAR_P (loc) + && ! REG_USERVAR_P (copy))) + { + temp = copy_to_mode_reg (GET_MODE (loc), copy); + REG_USERVAR_P (temp) = REG_USERVAR_P (loc); + if (CONSTANT_P (copy) || FIXED_BASE_PLUS_P (copy)) + { + map->const_equiv_map[REGNO (temp)] = copy; + map->const_age_map[REGNO (temp)] = CONST_AGE_PARM; + } + copy = temp; + } + map->reg_map[REGNO (loc)] = copy; + } + else + abort (); + + /* Free any temporaries we made setting up this parameter. */ + free_temp_slots (); + } + + /* Now do the parameters that will be placed in memory. */ + + for (formal = DECL_ARGUMENTS (fndecl), i = 0; + formal; formal = TREE_CHAIN (formal), i++) + { + rtx copy = arg_vals[i]; + + loc = RTVEC_ELT (arg_vector, i); + + if (GET_CODE (loc) == MEM + /* Exclude case handled above. */ + && ! (GET_CODE (XEXP (loc, 0)) == REG + && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER)) + { + rtx note = emit_note (DECL_SOURCE_FILE (formal), + DECL_SOURCE_LINE (formal)); + if (note) + RTX_INTEGRATED_P (note) = 1; + + /* Compute the address in the area we reserved and store the + value there. */ + temp = copy_rtx_and_substitute (loc, map); + subst_constants (&temp, NULL_RTX, map); + apply_change_group (); + if (! memory_address_p (GET_MODE (temp), XEXP (temp, 0))) + temp = change_address (temp, VOIDmode, XEXP (temp, 0)); + store_expr (arg_trees[i], temp, 0); + + /* Free any temporaries we made setting up this parameter. */ + free_temp_slots (); + } + } + + /* Deal with the places that the function puts its result. + We are driven by what is placed into DECL_RESULT. + + Initially, we assume that we don't have anything special handling for + REG_FUNCTION_RETURN_VALUE_P. */ + + map->inline_target = 0; + loc = DECL_RTL (DECL_RESULT (fndecl)); + if (TYPE_MODE (type) == VOIDmode) + /* There is no return value to worry about. */ + ; + else if (GET_CODE (loc) == MEM) + { + if (! structure_value_addr || ! aggregate_value_p (DECL_RESULT (fndecl))) + abort (); + + /* Pass the function the address in which to return a structure value. + Note that a constructor can cause someone to call us with + STRUCTURE_VALUE_ADDR, but the initialization takes place + via the first parameter, rather than the struct return address. + + We have two cases: If the address is a simple register indirect, + use the mapping mechanism to point that register to our structure + return address. Otherwise, store the structure return value into + the place that it will be referenced from. */ + + if (GET_CODE (XEXP (loc, 0)) == REG) + { + temp = force_reg (Pmode, structure_value_addr); + map->reg_map[REGNO (XEXP (loc, 0))] = temp; + if (CONSTANT_P (structure_value_addr) + || (GET_CODE (structure_value_addr) == PLUS + && XEXP (structure_value_addr, 0) == virtual_stack_vars_rtx + && GET_CODE (XEXP (structure_value_addr, 1)) == CONST_INT)) + { + map->const_equiv_map[REGNO (temp)] = structure_value_addr; + map->const_age_map[REGNO (temp)] = CONST_AGE_PARM; + } + } + else + { + temp = copy_rtx_and_substitute (loc, map); + subst_constants (&temp, NULL_RTX, map); + apply_change_group (); + emit_move_insn (temp, structure_value_addr); + } + } + else if (ignore) + /* We will ignore the result value, so don't look at its structure. + Note that preparations for an aggregate return value + do need to be made (above) even if it will be ignored. */ + ; + else if (GET_CODE (loc) == REG) + { + /* The function returns an object in a register and we use the return + value. Set up our target for remapping. */ + + /* Machine mode function was declared to return. */ + enum machine_mode departing_mode = TYPE_MODE (type); + /* (Possibly wider) machine mode it actually computes + (for the sake of callers that fail to declare it right). */ + enum machine_mode arriving_mode + = TYPE_MODE (TREE_TYPE (DECL_RESULT (fndecl))); + rtx reg_to_map; + + /* Don't use MEMs as direct targets because on some machines + substituting a MEM for a REG makes invalid insns. + Let the combiner substitute the MEM if that is valid. */ + if (target == 0 || GET_CODE (target) != REG + || GET_MODE (target) != departing_mode) + target = gen_reg_rtx (departing_mode); + + /* If function's value was promoted before return, + avoid machine mode mismatch when we substitute INLINE_TARGET. + But TARGET is what we will return to the caller. */ + if (arriving_mode != departing_mode) + reg_to_map = gen_rtx (SUBREG, arriving_mode, target, 0); + else + reg_to_map = target; + + /* Usually, the result value is the machine's return register. + Sometimes it may be a pseudo. Handle both cases. */ + if (REG_FUNCTION_VALUE_P (loc)) + map->inline_target = reg_to_map; + else + map->reg_map[REGNO (loc)] = reg_to_map; + } + + /* Make new label equivalences for the labels in the called function. */ + for (i = min_labelno; i < max_labelno; i++) + map->label_map[i] = gen_label_rtx (); + + /* Perform postincrements before actually calling the function. */ + emit_queue (); + + /* Clean up stack so that variables might have smaller offsets. */ + do_pending_stack_adjust (); + + /* Save a copy of the location of const_equiv_map for mark_stores, called + via note_stores. */ + global_const_equiv_map = map->const_equiv_map; + + /* Now copy the insns one by one. Do this in two passes, first the insns and + then their REG_NOTES, just like save_for_inline. */ + + /* This loop is very similar to the loop in copy_loop_body in unroll.c. */ + + for (insn = insns; insn; insn = NEXT_INSN (insn)) + { + rtx copy, pattern; + + map->orig_asm_operands_vector = 0; + + switch (GET_CODE (insn)) + { + case INSN: + pattern = PATTERN (insn); + copy = 0; + if (GET_CODE (pattern) == USE + && GET_CODE (XEXP (pattern, 0)) == REG + && REG_FUNCTION_VALUE_P (XEXP (pattern, 0))) + /* The (USE (REG n)) at return from the function should + be ignored since we are changing (REG n) into + inline_target. */ + break; + + /* Ignore setting a function value that we don't want to use. */ + if (map->inline_target == 0 + && GET_CODE (pattern) == SET + && GET_CODE (SET_DEST (pattern)) == REG + && REG_FUNCTION_VALUE_P (SET_DEST (pattern))) + { + if (volatile_refs_p (SET_SRC (pattern))) + { + /* If we must not delete the source, + load it into a new temporary. */ + copy = emit_insn (copy_rtx_and_substitute (pattern, map)); + SET_DEST (PATTERN (copy)) + = gen_reg_rtx (GET_MODE (SET_DEST (PATTERN (copy)))); + } + else + break; + } + else + copy = emit_insn (copy_rtx_and_substitute (pattern, map)); + /* REG_NOTES will be copied later. */ + +#ifdef HAVE_cc0 + /* If this insn is setting CC0, it may need to look at + the insn that uses CC0 to see what type of insn it is. + In that case, the call to recog via validate_change will + fail. So don't substitute constants here. Instead, + do it when we emit the following insn. + + For example, see the pyr.md file. That machine has signed and + unsigned compares. The compare patterns must check the + following branch insn to see which what kind of compare to + emit. + + If the previous insn set CC0, substitute constants on it as + well. */ + if (sets_cc0_p (PATTERN (copy)) != 0) + cc0_insn = copy; + else + { + if (cc0_insn) + try_constants (cc0_insn, map); + cc0_insn = 0; + try_constants (copy, map); + } +#else + try_constants (copy, map); +#endif + break; + + case JUMP_INSN: + if (GET_CODE (PATTERN (insn)) == RETURN) + { + if (local_return_label == 0) + local_return_label = gen_label_rtx (); + pattern = gen_jump (local_return_label); + } + else + pattern = copy_rtx_and_substitute (PATTERN (insn), map); + + copy = emit_jump_insn (pattern); + +#ifdef HAVE_cc0 + if (cc0_insn) + try_constants (cc0_insn, map); + cc0_insn = 0; +#endif + try_constants (copy, map); + + /* If this used to be a conditional jump insn but whose branch + direction is now know, we must do something special. */ + if (condjump_p (insn) && ! simplejump_p (insn) && map->last_pc_value) + { +#ifdef HAVE_cc0 + /* The previous insn set cc0 for us. So delete it. */ + delete_insn (PREV_INSN (copy)); +#endif + + /* If this is now a no-op, delete it. */ + if (map->last_pc_value == pc_rtx) + { + delete_insn (copy); + copy = 0; + } + else + /* Otherwise, this is unconditional jump so we must put a + BARRIER after it. We could do some dead code elimination + here, but jump.c will do it just as well. */ + emit_barrier (); + } + break; + + case CALL_INSN: + pattern = copy_rtx_and_substitute (PATTERN (insn), map); + copy = emit_call_insn (pattern); + +#ifdef HAVE_cc0 + if (cc0_insn) + try_constants (cc0_insn, map); + cc0_insn = 0; +#endif + try_constants (copy, map); + + /* Be lazy and assume CALL_INSNs clobber all hard registers. */ + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + map->const_equiv_map[i] = 0; + break; + + case CODE_LABEL: + copy = emit_label (map->label_map[CODE_LABEL_NUMBER (insn)]); + LABEL_NAME (copy) = LABEL_NAME (insn); + map->const_age++; + break; + + case BARRIER: + copy = emit_barrier (); + break; + + case NOTE: + /* It is important to discard function-end and function-beg notes, + so we have only one of each in the current function. + Also, NOTE_INSN_DELETED notes aren't useful (save_for_inline + deleted these in the copy used for continuing compilation, + not the copy used for inlining). */ + if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END + && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_BEG + && NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED) + copy = emit_note (NOTE_SOURCE_FILE (insn), NOTE_LINE_NUMBER (insn)); + else + copy = 0; + break; + + default: + abort (); + break; + } + + if (copy) + RTX_INTEGRATED_P (copy) = 1; + + map->insn_map[INSN_UID (insn)] = copy; + } + + /* Now copy the REG_NOTES. Increment const_age, so that only constants + from parameters can be substituted in. These are the only ones that + are valid across the entire function. */ + map->const_age++; + for (insn = insns; insn; insn = NEXT_INSN (insn)) + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && map->insn_map[INSN_UID (insn)] + && REG_NOTES (insn)) + { + rtx tem = copy_rtx_and_substitute (REG_NOTES (insn), map); + /* We must also do subst_constants, in case one of our parameters + has const type and constant value. */ + subst_constants (&tem, NULL_RTX, map); + apply_change_group (); + REG_NOTES (map->insn_map[INSN_UID (insn)]) = tem; + } + + if (local_return_label) + emit_label (local_return_label); + + /* Make copies of the decls of the symbols in the inline function, so that + the copies of the variables get declared in the current function. Set + up things so that lookup_static_chain knows that to interpret registers + in SAVE_EXPRs for TYPE_SIZEs as local. */ + + inline_function_decl = fndecl; + integrate_parm_decls (DECL_ARGUMENTS (fndecl), map, arg_vector); + integrate_decl_tree ((tree) ORIGINAL_DECL_INITIAL (header), 0, map); + inline_function_decl = 0; + + /* End the scope containing the copied formal parameter variables + and copied LABEL_DECLs. */ + + expand_end_bindings (getdecls (), 1, 1); + block = poplevel (1, 1, 0); + BLOCK_ABSTRACT_ORIGIN (block) = (DECL_ABSTRACT_ORIGIN (fndecl) == NULL + ? fndecl : DECL_ABSTRACT_ORIGIN (fndecl)); + poplevel (0, 0, 0); + emit_line_note (input_filename, lineno); + + if (structure_value_addr) + { + target = gen_rtx (MEM, TYPE_MODE (type), + memory_address (TYPE_MODE (type), structure_value_addr)); + MEM_IN_STRUCT_P (target) = 1; + } + return target; +} + +/* Given a chain of PARM_DECLs, ARGS, copy each decl into a VAR_DECL, + push all of those decls and give each one the corresponding home. */ + +static void +integrate_parm_decls (args, map, arg_vector) + tree args; + struct inline_remap *map; + rtvec arg_vector; +{ + register tree tail; + register int i; + + for (tail = args, i = 0; tail; tail = TREE_CHAIN (tail), i++) + { + register tree decl = build_decl (VAR_DECL, DECL_NAME (tail), + TREE_TYPE (tail)); + rtx new_decl_rtl + = copy_rtx_and_substitute (RTVEC_ELT (arg_vector, i), map); + + DECL_ARG_TYPE (decl) = DECL_ARG_TYPE (tail); + /* We really should be setting DECL_INCOMING_RTL to something reasonable + here, but that's going to require some more work. */ + /* DECL_INCOMING_RTL (decl) = ?; */ + /* These args would always appear unused, if not for this. */ + TREE_USED (decl) = 1; + /* Prevent warning for shadowing with these. */ + DECL_ABSTRACT_ORIGIN (decl) = tail; + pushdecl (decl); + /* Fully instantiate the address with the equivalent form so that the + debugging information contains the actual register, instead of the + virtual register. Do this by not passing an insn to + subst_constants. */ + subst_constants (&new_decl_rtl, NULL_RTX, map); + apply_change_group (); + DECL_RTL (decl) = new_decl_rtl; + } +} + +/* Given a BLOCK node LET, push decls and levels so as to construct in the + current function a tree of contexts isomorphic to the one that is given. + + LEVEL indicates how far down into the BLOCK tree is the node we are + currently traversing. It is always zero except for recursive calls. + + MAP, if nonzero, is a pointer to an inline_remap map which indicates how + registers used in the DECL_RTL field should be remapped. If it is zero, + no mapping is necessary. */ + +static void +integrate_decl_tree (let, level, map) + tree let; + int level; + struct inline_remap *map; +{ + tree t, node; + + if (level > 0) + pushlevel (0); + + for (t = BLOCK_VARS (let); t; t = TREE_CHAIN (t)) + { + tree d = build_decl (TREE_CODE (t), DECL_NAME (t), TREE_TYPE (t)); + DECL_SOURCE_LINE (d) = DECL_SOURCE_LINE (t); + DECL_SOURCE_FILE (d) = DECL_SOURCE_FILE (t); + if (DECL_RTL (t) != 0) + { + DECL_RTL (d) = copy_rtx_and_substitute (DECL_RTL (t), map); + /* Fully instantiate the address with the equivalent form so that the + debugging information contains the actual register, instead of the + virtual register. Do this by not passing an insn to + subst_constants. */ + subst_constants (&DECL_RTL (d), NULL_RTX, map); + apply_change_group (); + } + else if (DECL_RTL (t)) + DECL_RTL (d) = copy_rtx (DECL_RTL (t)); + DECL_EXTERNAL (d) = DECL_EXTERNAL (t); + TREE_STATIC (d) = TREE_STATIC (t); + TREE_PUBLIC (d) = TREE_PUBLIC (t); + TREE_CONSTANT (d) = TREE_CONSTANT (t); + TREE_ADDRESSABLE (d) = TREE_ADDRESSABLE (t); + TREE_READONLY (d) = TREE_READONLY (t); + TREE_SIDE_EFFECTS (d) = TREE_SIDE_EFFECTS (t); + /* These args would always appear unused, if not for this. */ + TREE_USED (d) = 1; + /* Prevent warning for shadowing with these. */ + DECL_ABSTRACT_ORIGIN (d) = t; + pushdecl (d); + } + + for (t = BLOCK_SUBBLOCKS (let); t; t = TREE_CHAIN (t)) + integrate_decl_tree (t, level + 1, map); + + if (level > 0) + { + node = poplevel (1, 0, 0); + if (node) + { + TREE_USED (node) = TREE_USED (let); + BLOCK_ABSTRACT_ORIGIN (node) = let; + } + } +} + +/* Create a new copy of an rtx. + Recursively copies the operands of the rtx, + except for those few rtx codes that are sharable. + + We always return an rtx that is similar to that incoming rtx, with the + exception of possibly changing a REG to a SUBREG or vice versa. No + rtl is ever emitted. + + Handle constants that need to be placed in the constant pool by + calling `force_const_mem'. */ + +rtx +copy_rtx_and_substitute (orig, map) + register rtx orig; + struct inline_remap *map; +{ + register rtx copy, temp; + register int i, j; + register RTX_CODE code; + register enum machine_mode mode; + register char *format_ptr; + int regno; + + if (orig == 0) + return 0; + + code = GET_CODE (orig); + mode = GET_MODE (orig); + + switch (code) + { + case REG: + /* If the stack pointer register shows up, it must be part of + stack-adjustments (*not* because we eliminated the frame pointer!). + Small hard registers are returned as-is. Pseudo-registers + go through their `reg_map'. */ + regno = REGNO (orig); + if (regno <= LAST_VIRTUAL_REGISTER) + { + /* Some hard registers are also mapped, + but others are not translated. */ + if (map->reg_map[regno] != 0) + return map->reg_map[regno]; + + /* If this is the virtual frame pointer, make space in current + function's stack frame for the stack frame of the inline function. + + Copy the address of this area into a pseudo. Map + virtual_stack_vars_rtx to this pseudo and set up a constant + equivalence for it to be the address. This will substitute the + address into insns where it can be substituted and use the new + pseudo where it can't. */ + if (regno == VIRTUAL_STACK_VARS_REGNUM) + { + rtx loc, seq; + int size = DECL_FRAME_SIZE (map->fndecl); + int rounded; + + start_sequence (); + loc = assign_stack_temp (BLKmode, size, 1); + loc = XEXP (loc, 0); +#ifdef FRAME_GROWS_DOWNWARD + /* In this case, virtual_stack_vars_rtx points to one byte + higher than the top of the frame area. So compute the offset + to one byte higher than our substitute frame. + Keep the fake frame pointer aligned like a real one. */ + rounded = CEIL_ROUND (size, BIGGEST_ALIGNMENT / BITS_PER_UNIT); + loc = plus_constant (loc, rounded); +#endif + map->reg_map[regno] = temp + = force_reg (Pmode, force_operand (loc, NULL_RTX)); + map->const_equiv_map[REGNO (temp)] = loc; + map->const_age_map[REGNO (temp)] = CONST_AGE_PARM; + + seq = gen_sequence (); + end_sequence (); + emit_insn_after (seq, map->insns_at_start); + return temp; + } + else if (regno == VIRTUAL_INCOMING_ARGS_REGNUM) + { + /* Do the same for a block to contain any arguments referenced + in memory. */ + rtx loc, seq; + int size = FUNCTION_ARGS_SIZE (DECL_SAVED_INSNS (map->fndecl)); + + start_sequence (); + loc = assign_stack_temp (BLKmode, size, 1); + loc = XEXP (loc, 0); + /* When arguments grow downward, the virtual incoming + args pointer points to the top of the argument block, + so the remapped location better do the same. */ +#ifdef ARGS_GROW_DOWNWARD + loc = plus_constant (loc, size); +#endif + map->reg_map[regno] = temp + = force_reg (Pmode, force_operand (loc, NULL_RTX)); + map->const_equiv_map[REGNO (temp)] = loc; + map->const_age_map[REGNO (temp)] = CONST_AGE_PARM; + + seq = gen_sequence (); + end_sequence (); + emit_insn_after (seq, map->insns_at_start); + return temp; + } + else if (REG_FUNCTION_VALUE_P (orig)) + { + /* This is a reference to the function return value. If + the function doesn't have a return value, error. If the + mode doesn't agree, make a SUBREG. */ + if (map->inline_target == 0) + /* Must be unrolling loops or replicating code if we + reach here, so return the register unchanged. */ + return orig; + else if (mode != GET_MODE (map->inline_target)) + return gen_lowpart (mode, map->inline_target); + else + return map->inline_target; + } + return orig; + } + if (map->reg_map[regno] == NULL) + { + map->reg_map[regno] = gen_reg_rtx (mode); + REG_USERVAR_P (map->reg_map[regno]) = REG_USERVAR_P (orig); + REG_LOOP_TEST_P (map->reg_map[regno]) = REG_LOOP_TEST_P (orig); + RTX_UNCHANGING_P (map->reg_map[regno]) = RTX_UNCHANGING_P (orig); + /* A reg with REG_FUNCTION_VALUE_P true will never reach here. */ + } + return map->reg_map[regno]; + + case SUBREG: + copy = copy_rtx_and_substitute (SUBREG_REG (orig), map); + /* SUBREG is ordinary, but don't make nested SUBREGs. */ + if (GET_CODE (copy) == SUBREG) + return gen_rtx (SUBREG, GET_MODE (orig), SUBREG_REG (copy), + SUBREG_WORD (orig) + SUBREG_WORD (copy)); + else + return gen_rtx (SUBREG, GET_MODE (orig), copy, + SUBREG_WORD (orig)); + + case USE: + case CLOBBER: + /* USE and CLOBBER are ordinary, but we convert (use (subreg foo)) + to (use foo) if the original insn didn't have a subreg. + Removing the subreg distorts the VAX movstrhi pattern + by changing the mode of an operand. */ + copy = copy_rtx_and_substitute (XEXP (orig, 0), map); + if (GET_CODE (copy) == SUBREG && GET_CODE (XEXP (orig, 0)) != SUBREG) + copy = SUBREG_REG (copy); + return gen_rtx (code, VOIDmode, copy); + + case CODE_LABEL: + LABEL_PRESERVE_P (map->label_map[CODE_LABEL_NUMBER (orig)]) + = LABEL_PRESERVE_P (orig); + return map->label_map[CODE_LABEL_NUMBER (orig)]; + + case LABEL_REF: + copy = rtx_alloc (LABEL_REF); + PUT_MODE (copy, mode); + XEXP (copy, 0) = map->label_map[CODE_LABEL_NUMBER (XEXP (orig, 0))]; + LABEL_OUTSIDE_LOOP_P (copy) = LABEL_OUTSIDE_LOOP_P (orig); + return copy; + + case PC: + case CC0: + case CONST_INT: + return orig; + + case SYMBOL_REF: + /* Symbols which represent the address of a label stored in the constant + pool must be modified to point to a constant pool entry for the + remapped label. Otherwise, symbols are returned unchanged. */ + if (CONSTANT_POOL_ADDRESS_P (orig)) + { + rtx constant = get_pool_constant (orig); + if (GET_CODE (constant) == LABEL_REF) + { + copy = rtx_alloc (LABEL_REF); + PUT_MODE (copy, mode); + XEXP (copy, 0) + = map->label_map[CODE_LABEL_NUMBER (XEXP (constant, 0))]; + LABEL_OUTSIDE_LOOP_P (copy) = LABEL_OUTSIDE_LOOP_P (orig); + copy = force_const_mem (Pmode, copy); + return XEXP (copy, 0); + } + } + return orig; + + case CONST_DOUBLE: + /* We have to make a new copy of this CONST_DOUBLE because don't want + to use the old value of CONST_DOUBLE_MEM. Also, this may be a + duplicate of a CONST_DOUBLE we have already seen. */ + if (GET_MODE_CLASS (GET_MODE (orig)) == MODE_FLOAT) + { + REAL_VALUE_TYPE d; + + REAL_VALUE_FROM_CONST_DOUBLE (d, orig); + return immed_real_const_1 (d, GET_MODE (orig)); + } + else + return immed_double_const (CONST_DOUBLE_LOW (orig), + CONST_DOUBLE_HIGH (orig), VOIDmode); + + case CONST: + /* Make new constant pool entry for a constant + that was in the pool of the inline function. */ + if (RTX_INTEGRATED_P (orig)) + { + /* If this was an address of a constant pool entry that itself + had to be placed in the constant pool, it might not be a + valid address. So the recursive call below might turn it + into a register. In that case, it isn't a constant any + more, so return it. This has the potential of changing a + MEM into a REG, but we'll assume that it safe. */ + temp = copy_rtx_and_substitute (XEXP (orig, 0), map); + if (! CONSTANT_P (temp)) + return temp; + return validize_mem (force_const_mem (GET_MODE (orig), temp)); + } + break; + + case ADDRESS: + /* If from constant pool address, make new constant pool entry and + return its address. */ + if (! RTX_INTEGRATED_P (orig)) + abort (); + + temp = force_const_mem (GET_MODE (orig), + copy_rtx_and_substitute (XEXP (orig, 0), map)); + +#if 0 + /* Legitimizing the address here is incorrect. + + The only ADDRESS rtx's that can reach here are ones created by + save_constants. Hence the operand of the ADDRESS is always legal + in this position of the instruction, since the original rtx without + the ADDRESS was legal. + + The reason we don't legitimize the address here is that on the + Sparc, the caller may have a (high ...) surrounding this ADDRESS. + This code forces the operand of the address to a register, which + fails because we can not take the HIGH part of a register. + + Also, change_address may create new registers. These registers + will not have valid reg_map entries. This can cause try_constants() + to fail because assumes that all registers in the rtx have valid + reg_map entries, and it may end up replacing one of these new + registers with junk. */ + + if (! memory_address_p (GET_MODE (temp), XEXP (temp, 0))) + temp = change_address (temp, GET_MODE (temp), XEXP (temp, 0)); +#endif + + return XEXP (temp, 0); + + case ASM_OPERANDS: + /* If a single asm insn contains multiple output operands + then it contains multiple ASM_OPERANDS rtx's that share operand 3. + We must make sure that the copied insn continues to share it. */ + if (map->orig_asm_operands_vector == XVEC (orig, 3)) + { + copy = rtx_alloc (ASM_OPERANDS); + XSTR (copy, 0) = XSTR (orig, 0); + XSTR (copy, 1) = XSTR (orig, 1); + XINT (copy, 2) = XINT (orig, 2); + XVEC (copy, 3) = map->copy_asm_operands_vector; + XVEC (copy, 4) = map->copy_asm_constraints_vector; + XSTR (copy, 5) = XSTR (orig, 5); + XINT (copy, 6) = XINT (orig, 6); + return copy; + } + break; + + case CALL: + /* This is given special treatment because the first + operand of a CALL is a (MEM ...) which may get + forced into a register for cse. This is undesirable + if function-address cse isn't wanted or if we won't do cse. */ +#ifndef NO_FUNCTION_CSE + if (! (optimize && ! flag_no_function_cse)) +#endif + return gen_rtx (CALL, GET_MODE (orig), + gen_rtx (MEM, GET_MODE (XEXP (orig, 0)), + copy_rtx_and_substitute (XEXP (XEXP (orig, 0), 0), map)), + copy_rtx_and_substitute (XEXP (orig, 1), map)); + break; + +#if 0 + /* Must be ifdefed out for loop unrolling to work. */ + case RETURN: + abort (); +#endif + + case SET: + /* If this is setting fp or ap, it means that we have a nonlocal goto. + Don't alter that. + If the nonlocal goto is into the current function, + this will result in unnecessarily bad code, but should work. */ + if (SET_DEST (orig) == virtual_stack_vars_rtx + || SET_DEST (orig) == virtual_incoming_args_rtx) + return gen_rtx (SET, VOIDmode, SET_DEST (orig), + copy_rtx_and_substitute (SET_SRC (orig), map)); + break; + + case MEM: + copy = rtx_alloc (MEM); + PUT_MODE (copy, mode); + XEXP (copy, 0) = copy_rtx_and_substitute (XEXP (orig, 0), map); + MEM_IN_STRUCT_P (copy) = MEM_IN_STRUCT_P (orig); + MEM_VOLATILE_P (copy) = MEM_VOLATILE_P (orig); + + /* If doing function inlining, this MEM might not be const in the + function that it is being inlined into, and thus may not be + unchanging after function inlining. Constant pool references are + handled elsewhere, so this doesn't lose RTX_UNCHANGING_P bits + for them. */ + if (! map->integrating) + RTX_UNCHANGING_P (copy) = RTX_UNCHANGING_P (orig); + + return copy; + } + + copy = rtx_alloc (code); + PUT_MODE (copy, mode); + copy->in_struct = orig->in_struct; + copy->volatil = orig->volatil; + copy->unchanging = orig->unchanging; + + format_ptr = GET_RTX_FORMAT (GET_CODE (copy)); + + for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++) + { + switch (*format_ptr++) + { + case '0': + break; + + case 'e': + XEXP (copy, i) = copy_rtx_and_substitute (XEXP (orig, i), map); + break; + + case 'u': + /* Change any references to old-insns to point to the + corresponding copied insns. */ + XEXP (copy, i) = map->insn_map[INSN_UID (XEXP (orig, i))]; + break; + + case 'E': + XVEC (copy, i) = XVEC (orig, i); + if (XVEC (orig, i) != NULL && XVECLEN (orig, i) != 0) + { + XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i)); + for (j = 0; j < XVECLEN (copy, i); j++) + XVECEXP (copy, i, j) + = copy_rtx_and_substitute (XVECEXP (orig, i, j), map); + } + break; + + case 'w': + XWINT (copy, i) = XWINT (orig, i); + break; + + case 'i': + XINT (copy, i) = XINT (orig, i); + break; + + case 's': + XSTR (copy, i) = XSTR (orig, i); + break; + + default: + abort (); + } + } + + if (code == ASM_OPERANDS && map->orig_asm_operands_vector == 0) + { + map->orig_asm_operands_vector = XVEC (orig, 3); + map->copy_asm_operands_vector = XVEC (copy, 3); + map->copy_asm_constraints_vector = XVEC (copy, 4); + } + + return copy; +} + +/* Substitute known constant values into INSN, if that is valid. */ + +void +try_constants (insn, map) + rtx insn; + struct inline_remap *map; +{ + int i; + + map->num_sets = 0; + subst_constants (&PATTERN (insn), insn, map); + + /* Apply the changes if they are valid; otherwise discard them. */ + apply_change_group (); + + /* Show we don't know the value of anything stored or clobbered. */ + note_stores (PATTERN (insn), mark_stores); + map->last_pc_value = 0; +#ifdef HAVE_cc0 + map->last_cc0_value = 0; +#endif + + /* Set up any constant equivalences made in this insn. */ + for (i = 0; i < map->num_sets; i++) + { + if (GET_CODE (map->equiv_sets[i].dest) == REG) + { + int regno = REGNO (map->equiv_sets[i].dest); + + if (map->const_equiv_map[regno] == 0 + /* Following clause is a hack to make case work where GNU C++ + reassigns a variable to make cse work right. */ + || ! rtx_equal_p (map->const_equiv_map[regno], + map->equiv_sets[i].equiv)) + { + map->const_equiv_map[regno] = map->equiv_sets[i].equiv; + map->const_age_map[regno] = map->const_age; + } + } + else if (map->equiv_sets[i].dest == pc_rtx) + map->last_pc_value = map->equiv_sets[i].equiv; +#ifdef HAVE_cc0 + else if (map->equiv_sets[i].dest == cc0_rtx) + map->last_cc0_value = map->equiv_sets[i].equiv; +#endif + } +} + +/* Substitute known constants for pseudo regs in the contents of LOC, + which are part of INSN. + If INSN is zero, the substitution should always be done (this is used to + update DECL_RTL). + These changes are taken out by try_constants if the result is not valid. + + Note that we are more concerned with determining when the result of a SET + is a constant, for further propagation, than actually inserting constants + into insns; cse will do the latter task better. + + This function is also used to adjust address of items previously addressed + via the virtual stack variable or virtual incoming arguments registers. */ + +static void +subst_constants (loc, insn, map) + rtx *loc; + rtx insn; + struct inline_remap *map; +{ + rtx x = *loc; + register int i; + register enum rtx_code code; + register char *format_ptr; + int num_changes = num_validated_changes (); + rtx new = 0; + enum machine_mode op0_mode; + + code = GET_CODE (x); + + switch (code) + { + case PC: + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + case CONST: + case LABEL_REF: + case ADDRESS: + return; + +#ifdef HAVE_cc0 + case CC0: + validate_change (insn, loc, map->last_cc0_value, 1); + return; +#endif + + case USE: + case CLOBBER: + /* The only thing we can do with a USE or CLOBBER is possibly do + some substitutions in a MEM within it. */ + if (GET_CODE (XEXP (x, 0)) == MEM) + subst_constants (&XEXP (XEXP (x, 0), 0), insn, map); + return; + + case REG: + /* Substitute for parms and known constants. Don't replace + hard regs used as user variables with constants. */ + { + int regno = REGNO (x); + + if (! (regno < FIRST_PSEUDO_REGISTER && REG_USERVAR_P (x)) + && regno < map->const_equiv_map_size + && map->const_equiv_map[regno] != 0 + && map->const_age_map[regno] >= map->const_age) + validate_change (insn, loc, map->const_equiv_map[regno], 1); + return; + } + + case SUBREG: + /* SUBREG applied to something other than a reg + should be treated as ordinary, since that must + be a special hack and we don't know how to treat it specially. + Consider for example mulsidi3 in m68k.md. + Ordinary SUBREG of a REG needs this special treatment. */ + if (GET_CODE (SUBREG_REG (x)) == REG) + { + rtx inner = SUBREG_REG (x); + rtx new = 0; + + /* We can't call subst_constants on &SUBREG_REG (x) because any + constant or SUBREG wouldn't be valid inside our SUBEG. Instead, + see what is inside, try to form the new SUBREG and see if that is + valid. We handle two cases: extracting a full word in an + integral mode and extracting the low part. */ + subst_constants (&inner, NULL_RTX, map); + + if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT + && GET_MODE_SIZE (GET_MODE (x)) == UNITS_PER_WORD + && GET_MODE (SUBREG_REG (x)) != VOIDmode) + new = operand_subword (inner, SUBREG_WORD (x), 0, + GET_MODE (SUBREG_REG (x))); + + if (new == 0 && subreg_lowpart_p (x)) + new = gen_lowpart_common (GET_MODE (x), inner); + + if (new) + validate_change (insn, loc, new, 1); + + return; + } + break; + + case MEM: + subst_constants (&XEXP (x, 0), insn, map); + + /* If a memory address got spoiled, change it back. */ + if (insn != 0 && num_validated_changes () != num_changes + && !memory_address_p (GET_MODE (x), XEXP (x, 0))) + cancel_changes (num_changes); + return; + + case SET: + { + /* Substitute constants in our source, and in any arguments to a + complex (e..g, ZERO_EXTRACT) destination, but not in the destination + itself. */ + rtx *dest_loc = &SET_DEST (x); + rtx dest = *dest_loc; + rtx src, tem; + + subst_constants (&SET_SRC (x), insn, map); + src = SET_SRC (x); + + while (GET_CODE (*dest_loc) == ZERO_EXTRACT + /* By convention, we always use ZERO_EXTRACT in the dest. */ +/* || GET_CODE (*dest_loc) == SIGN_EXTRACT */ + || GET_CODE (*dest_loc) == SUBREG + || GET_CODE (*dest_loc) == STRICT_LOW_PART) + { + if (GET_CODE (*dest_loc) == ZERO_EXTRACT) + { + subst_constants (&XEXP (*dest_loc, 1), insn, map); + subst_constants (&XEXP (*dest_loc, 2), insn, map); + } + dest_loc = &XEXP (*dest_loc, 0); + } + + /* Do substitute in the address of a destination in memory. */ + if (GET_CODE (*dest_loc) == MEM) + subst_constants (&XEXP (*dest_loc, 0), insn, map); + + /* Check for the case of DEST a SUBREG, both it and the underlying + register are less than one word, and the SUBREG has the wider mode. + In the case, we are really setting the underlying register to the + source converted to the mode of DEST. So indicate that. */ + if (GET_CODE (dest) == SUBREG + && GET_MODE_SIZE (GET_MODE (dest)) <= UNITS_PER_WORD + && GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) <= UNITS_PER_WORD + && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) + <= GET_MODE_SIZE (GET_MODE (dest))) + && (tem = gen_lowpart_if_possible (GET_MODE (SUBREG_REG (dest)), + src))) + src = tem, dest = SUBREG_REG (dest); + + /* If storing a recognizable value save it for later recording. */ + if ((map->num_sets < MAX_RECOG_OPERANDS) + && (CONSTANT_P (src) + || (GET_CODE (src) == PLUS + && GET_CODE (XEXP (src, 0)) == REG + && REGNO (XEXP (src, 0)) >= FIRST_VIRTUAL_REGISTER + && REGNO (XEXP (src, 0)) <= LAST_VIRTUAL_REGISTER + && CONSTANT_P (XEXP (src, 1))) + || GET_CODE (src) == COMPARE +#ifdef HAVE_cc0 + || dest == cc0_rtx +#endif + || (dest == pc_rtx + && (src == pc_rtx || GET_CODE (src) == RETURN + || GET_CODE (src) == LABEL_REF)))) + { + /* Normally, this copy won't do anything. But, if SRC is a COMPARE + it will cause us to save the COMPARE with any constants + substituted, which is what we want for later. */ + map->equiv_sets[map->num_sets].equiv = copy_rtx (src); + map->equiv_sets[map->num_sets++].dest = dest; + } + + return; + } + } + + format_ptr = GET_RTX_FORMAT (code); + + /* If the first operand is an expression, save its mode for later. */ + if (*format_ptr == 'e') + op0_mode = GET_MODE (XEXP (x, 0)); + + for (i = 0; i < GET_RTX_LENGTH (code); i++) + { + switch (*format_ptr++) + { + case '0': + break; + + case 'e': + if (XEXP (x, i)) + subst_constants (&XEXP (x, i), insn, map); + break; + + case 'u': + case 'i': + case 's': + case 'w': + break; + + case 'E': + if (XVEC (x, i) != NULL && XVECLEN (x, i) != 0) + { + int j; + for (j = 0; j < XVECLEN (x, i); j++) + subst_constants (&XVECEXP (x, i, j), insn, map); + } + break; + + default: + abort (); + } + } + + /* If this is a commutative operation, move a constant to the second + operand unless the second operand is already a CONST_INT. */ + if ((GET_RTX_CLASS (code) == 'c' || code == NE || code == EQ) + && CONSTANT_P (XEXP (x, 0)) && GET_CODE (XEXP (x, 1)) != CONST_INT) + { + rtx tem = XEXP (x, 0); + validate_change (insn, &XEXP (x, 0), XEXP (x, 1), 1); + validate_change (insn, &XEXP (x, 1), tem, 1); + } + + /* Simplify the expression in case we put in some constants. */ + switch (GET_RTX_CLASS (code)) + { + case '1': + new = simplify_unary_operation (code, GET_MODE (x), + XEXP (x, 0), op0_mode); + break; + + case '<': + { + enum machine_mode op_mode = GET_MODE (XEXP (x, 0)); + if (op_mode == VOIDmode) + op_mode = GET_MODE (XEXP (x, 1)); + new = simplify_relational_operation (code, op_mode, + XEXP (x, 0), XEXP (x, 1)); +#ifdef FLOAT_STORE_FLAG_VALUE + if (new != 0 && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) + new = ((new == const0_rtx) ? CONST0_RTX (GET_MODE (x)) + : immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, GET_MODE (x))); +#endif + break; + } + + case '2': + case 'c': + new = simplify_binary_operation (code, GET_MODE (x), + XEXP (x, 0), XEXP (x, 1)); + break; + + case 'b': + case '3': + new = simplify_ternary_operation (code, GET_MODE (x), op0_mode, + XEXP (x, 0), XEXP (x, 1), XEXP (x, 2)); + break; + } + + if (new) + validate_change (insn, loc, new, 1); +} + +/* Show that register modified no longer contain known constants. We are + called from note_stores with parts of the new insn. */ + +void +mark_stores (dest, x) + rtx dest; + rtx x; +{ + int regno = -1; + enum machine_mode mode; + + /* DEST is always the innermost thing set, except in the case of + SUBREGs of hard registers. */ + + if (GET_CODE (dest) == REG) + regno = REGNO (dest), mode = GET_MODE (dest); + else if (GET_CODE (dest) == SUBREG && GET_CODE (SUBREG_REG (dest)) == REG) + { + regno = REGNO (SUBREG_REG (dest)) + SUBREG_WORD (dest); + mode = GET_MODE (SUBREG_REG (dest)); + } + + if (regno >= 0) + { + int last_reg = (regno >= FIRST_PSEUDO_REGISTER ? regno + : regno + HARD_REGNO_NREGS (regno, mode) - 1); + int i; + + for (i = regno; i <= last_reg; i++) + global_const_equiv_map[i] = 0; + } +} + +/* If any CONST expressions with RTX_INTEGRATED_P are present in the rtx + pointed to by PX, they represent constants in the constant pool. + Replace these with a new memory reference obtained from force_const_mem. + Similarly, ADDRESS expressions with RTX_INTEGRATED_P represent the + address of a constant pool entry. Replace them with the address of + a new constant pool entry obtained from force_const_mem. */ + +static void +restore_constants (px) + rtx *px; +{ + rtx x = *px; + int i, j; + char *fmt; + + if (x == 0) + return; + + if (GET_CODE (x) == CONST_DOUBLE) + { + /* We have to make a new CONST_DOUBLE to ensure that we account for + it correctly. Using the old CONST_DOUBLE_MEM data is wrong. */ + if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) + { + REAL_VALUE_TYPE d; + + REAL_VALUE_FROM_CONST_DOUBLE (d, x); + *px = immed_real_const_1 (d, GET_MODE (x)); + } + else + *px = immed_double_const (CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x), + VOIDmode); + } + + else if (RTX_INTEGRATED_P (x) && GET_CODE (x) == CONST) + { + restore_constants (&XEXP (x, 0)); + *px = validize_mem (force_const_mem (GET_MODE (x), XEXP (x, 0))); + } + else if (RTX_INTEGRATED_P (x) && GET_CODE (x) == SUBREG) + { + /* This must be (subreg/i:M1 (const/i:M2 ...) 0). */ + rtx new = XEXP (SUBREG_REG (x), 0); + + restore_constants (&new); + new = force_const_mem (GET_MODE (SUBREG_REG (x)), new); + PUT_MODE (new, GET_MODE (x)); + *px = validize_mem (new); + } + else if (RTX_INTEGRATED_P (x) && GET_CODE (x) == ADDRESS) + { + restore_constants (&XEXP (x, 0)); + *px = XEXP (force_const_mem (GET_MODE (x), XEXP (x, 0)), 0); + } + else + { + fmt = GET_RTX_FORMAT (GET_CODE (x)); + for (i = 0; i < GET_RTX_LENGTH (GET_CODE (x)); i++) + { + switch (*fmt++) + { + case 'E': + for (j = 0; j < XVECLEN (x, i); j++) + restore_constants (&XVECEXP (x, i, j)); + break; + + case 'e': + restore_constants (&XEXP (x, i)); + break; + } + } + } +} + +/* Given a pointer to some BLOCK node, if the BLOCK_ABSTRACT_ORIGIN for the + given BLOCK node is NULL, set the BLOCK_ABSTRACT_ORIGIN for the node so + that it points to the node itself, thus indicating that the node is its + own (abstract) origin. Additionally, if the BLOCK_ABSTRACT_ORIGIN for + the given node is NULL, recursively descend the decl/block tree which + it is the root of, and for each other ..._DECL or BLOCK node contained + therein whose DECL_ABSTRACT_ORIGINs or BLOCK_ABSTRACT_ORIGINs are also + still NULL, set *their* DECL_ABSTRACT_ORIGIN or BLOCK_ABSTRACT_ORIGIN + values to point to themselves. */ + +static void set_decl_origin_self (); + +static void +set_block_origin_self (stmt) + register tree stmt; +{ + if (BLOCK_ABSTRACT_ORIGIN (stmt) == NULL_TREE) + { + BLOCK_ABSTRACT_ORIGIN (stmt) = stmt; + + { + register tree local_decl; + + for (local_decl = BLOCK_VARS (stmt); + local_decl != NULL_TREE; + local_decl = TREE_CHAIN (local_decl)) + set_decl_origin_self (local_decl); /* Potential recursion. */ + } + + { + register tree subblock; + + for (subblock = BLOCK_SUBBLOCKS (stmt); + subblock != NULL_TREE; + subblock = BLOCK_CHAIN (subblock)) + set_block_origin_self (subblock); /* Recurse. */ + } + } +} + +/* Given a pointer to some ..._DECL node, if the DECL_ABSTRACT_ORIGIN for + the given ..._DECL node is NULL, set the DECL_ABSTRACT_ORIGIN for the + node to so that it points to the node itself, thus indicating that the + node represents its own (abstract) origin. Additionally, if the + DECL_ABSTRACT_ORIGIN for the given node is NULL, recursively descend + the decl/block tree of which the given node is the root of, and for + each other ..._DECL or BLOCK node contained therein whose + DECL_ABSTRACT_ORIGINs or BLOCK_ABSTRACT_ORIGINs are also still NULL, + set *their* DECL_ABSTRACT_ORIGIN or BLOCK_ABSTRACT_ORIGIN values to + point to themselves. */ + +static void +set_decl_origin_self (decl) + register tree decl; +{ + if (DECL_ABSTRACT_ORIGIN (decl) == NULL_TREE) + { + DECL_ABSTRACT_ORIGIN (decl) = decl; + if (TREE_CODE (decl) == FUNCTION_DECL) + { + register tree arg; + + for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg)) + DECL_ABSTRACT_ORIGIN (arg) = arg; + if (DECL_INITIAL (decl) != NULL_TREE) + set_block_origin_self (DECL_INITIAL (decl)); + } + } +} + +/* Given a pointer to some BLOCK node, and a boolean value to set the + "abstract" flags to, set that value into the BLOCK_ABSTRACT flag for + the given block, and for all local decls and all local sub-blocks + (recursively) which are contained therein. */ + +void set_decl_abstract_flags (); + +static void +set_block_abstract_flags (stmt, setting) + register tree stmt; + register int setting; +{ + BLOCK_ABSTRACT (stmt) = setting; + + { + register tree local_decl; + + for (local_decl = BLOCK_VARS (stmt); + local_decl != NULL_TREE; + local_decl = TREE_CHAIN (local_decl)) + set_decl_abstract_flags (local_decl, setting); + } + + { + register tree subblock; + + for (subblock = BLOCK_SUBBLOCKS (stmt); + subblock != NULL_TREE; + subblock = BLOCK_CHAIN (subblock)) + set_block_abstract_flags (subblock, setting); + } +} + +/* Given a pointer to some ..._DECL node, and a boolean value to set the + "abstract" flags to, set that value into the DECL_ABSTRACT flag for the + given decl, and (in the case where the decl is a FUNCTION_DECL) also + set the abstract flags for all of the parameters, local vars, local + blocks and sub-blocks (recursively) to the same setting. */ + +void +set_decl_abstract_flags (decl, setting) + register tree decl; + register int setting; +{ + DECL_ABSTRACT (decl) = setting; + if (TREE_CODE (decl) == FUNCTION_DECL) + { + register tree arg; + + for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg)) + DECL_ABSTRACT (arg) = setting; + if (DECL_INITIAL (decl) != NULL_TREE) + set_block_abstract_flags (DECL_INITIAL (decl), setting); + } +} + +/* Output the assembly language code for the function FNDECL + from its DECL_SAVED_INSNS. Used for inline functions that are output + at end of compilation instead of where they came in the source. */ + +void +output_inline_function (fndecl) + tree fndecl; +{ + rtx head = DECL_SAVED_INSNS (fndecl); + rtx last; + + temporary_allocation (); + + current_function_decl = fndecl; + + /* This call is only used to initialize global variables. */ + init_function_start (fndecl, "lossage", 1); + + /* Redo parameter determinations in case the FUNCTION_... + macros took machine-specific actions that need to be redone. */ + assign_parms (fndecl, 1); + + /* Set stack frame size. */ + assign_stack_local (BLKmode, DECL_FRAME_SIZE (fndecl), 0); + + restore_reg_data (FIRST_PARM_INSN (head)); + + stack_slot_list = STACK_SLOT_LIST (head); + + if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_CALLS_ALLOCA) + current_function_calls_alloca = 1; + + if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_CALLS_SETJMP) + current_function_calls_setjmp = 1; + + if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_CALLS_LONGJMP) + current_function_calls_longjmp = 1; + + if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_RETURNS_STRUCT) + current_function_returns_struct = 1; + + if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_RETURNS_PCC_STRUCT) + current_function_returns_pcc_struct = 1; + + if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_NEEDS_CONTEXT) + current_function_needs_context = 1; + + if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_HAS_NONLOCAL_LABEL) + current_function_has_nonlocal_label = 1; + + if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_RETURNS_POINTER) + current_function_returns_pointer = 1; + + if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_USES_CONST_POOL) + current_function_uses_const_pool = 1; + + if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_USES_PIC_OFFSET_TABLE) + current_function_uses_pic_offset_table = 1; + + current_function_outgoing_args_size = OUTGOING_ARGS_SIZE (head); + current_function_pops_args = POPS_ARGS (head); + + /* There is no need to output a return label again. */ + return_label = 0; + + expand_function_end (DECL_SOURCE_FILE (fndecl), DECL_SOURCE_LINE (fndecl)); + + /* Find last insn and rebuild the constant pool. */ + for (last = FIRST_PARM_INSN (head); + NEXT_INSN (last); last = NEXT_INSN (last)) + { + if (GET_RTX_CLASS (GET_CODE (last)) == 'i') + { + restore_constants (&PATTERN (last)); + restore_constants (®_NOTES (last)); + } + } + + set_new_first_and_last_insn (FIRST_PARM_INSN (head), last); + set_new_first_and_last_label_num (FIRST_LABELNO (head), LAST_LABELNO (head)); + + /* We must have already output DWARF debugging information for the + original (abstract) inline function declaration/definition, so + we want to make sure that the debugging information we generate + for this special instance of the inline function refers back to + the information we already generated. To make sure that happens, + we simply have to set the DECL_ABSTRACT_ORIGIN for the function + node (and for all of the local ..._DECL nodes which are its children) + so that they all point to themselves. */ + + set_decl_origin_self (fndecl); + + /* Compile this function all the way down to assembly code. */ + rest_of_compilation (fndecl); + + current_function_decl = 0; + + permanent_allocation (); +} diff --git a/gnu/usr.bin/cc/common/integrate.h b/gnu/usr.bin/cc/lib/integrate.h similarity index 100% rename from gnu/usr.bin/cc/common/integrate.h rename to gnu/usr.bin/cc/lib/integrate.h diff --git a/gnu/usr.bin/cc/common/jump.c b/gnu/usr.bin/cc/lib/jump.c similarity index 100% rename from gnu/usr.bin/cc/common/jump.c rename to gnu/usr.bin/cc/lib/jump.c diff --git a/gnu/usr.bin/cc/lib/lib.mk b/gnu/usr.bin/cc/lib/lib.mk new file mode 100644 index 0000000000..846aade115 --- /dev/null +++ b/gnu/usr.bin/cc/lib/lib.mk @@ -0,0 +1,188 @@ +# @(#)bsd.lib.mk 5.26 (Berkeley) 5/2/91 +# +# $Log: bsd.lib.mk,v $ +# Revision 1.7 1993/07/23 20:44:38 nate +# Fixed a boo-boo and made the NOMAN environment variable work correctly with +# both programs and libraries. +# +# Revision 1.6 1993/07/09 00:38:35 jkh +# Removed $History$ line from hell (no leading #). +# +# Revision 1.5 1993/07/08 12:17:07 paul +# Removed the core.* before disaster strikes. +# I removed core as well since it's pretty redundant. +# +# Revision 1.4 1993/07/07 21:42:45 nate +# Cleaned up header files and added core.* to clean directives +# +# Revision 1.3 1993/07/02 06:44:30 root +# New manual page system +# +# Revision 1.2 1993/06/17 02:01:11 rgrimes +# Make clean in src/lib/libc failed due to too many arguments to /bin/sh, +# this was fixed for make cleandir in the patchkit, this fixes it for +# make clean. +# + +.if exists(${.CURDIR}/../Makefile.inc) +.include "${.CURDIR}/../Makefile.inc" +.endif + +LIBDIR?= /usr/lib +LINTLIBDIR?= /usr/libdata/lint +LIBGRP?= bin +LIBOWN?= bin +LIBMODE?= 444 + +STRIP?= -s + +BINGRP?= bin +BINOWN?= bin +BINMODE?= 555 + +.MAIN: all + +# prefer .s to a .c, add .po, remove stuff not used in the BSD libraries +.SUFFIXES: .out .o .po .s .c .f .y .l + +.c.o: + ${CC} ${CFLAGS} -c ${.IMPSRC} + @${LD} -x -r ${.TARGET} + @mv a.out ${.TARGET} + +.c.po: + ${CC} -p ${CFLAGS} -c ${.IMPSRC} -o ${.TARGET} + @${LD} -X -r ${.TARGET} + @mv a.out ${.TARGET} + +.s.o: + ${CPP} -E ${CFLAGS:M-[ID]*} ${AINC} ${.IMPSRC} | \ + ${AS} -o ${.TARGET} + @${LD} -x -r ${.TARGET} + @mv a.out ${.TARGET} + +.s.po: + ${CPP} -E -DPROF ${CFLAGS:M-[ID]*} ${AINC} ${.IMPSRC} | \ + ${AS} -o ${.TARGET} + @${LD} -X -r ${.TARGET} + @mv a.out ${.TARGET} + +.if !defined(NOPROFILE) +_LIBS=lib${LIB}.a lib${LIB}_p.a +.else +_LIBS=lib${LIB}.a +.endif + +all: ${_LIBS} # llib-l${LIB}.ln + +OBJS+= ${SRCS:R:S/$/.o/g} + +lib${LIB}.a:: ${OBJS} + @echo building standard ${LIB} library + @rm -f lib${LIB}.a + @${AR} cTq lib${LIB}.a ${OBJS} + ranlib lib${LIB}.a + +POBJS+= ${OBJS:.o=.po} +lib${LIB}_p.a:: ${POBJS} + @echo building profiled ${LIB} library + @rm -f lib${LIB}_p.a + @${AR} cTq lib${LIB}_p.a `lorder ${POBJS} | tsort` ${LDADD} + ranlib lib${LIB}_p.a + +llib-l${LIB}.ln: ${SRCS} + ${LINT} -C${LIB} ${CFLAGS} ${.ALLSRC:M*.c} + +.if !target(clean) +clean: + rm -f a.out Errs errs mklog ${CLEANFILES} ${OBJS} \ + lib${LIB}.a llib-l${LIB}.ln + rm -f ${POBJS} profiled/*.o lib${LIB}_p.a +.endif + +.if !target(cleandir) +cleandir: + rm -f a.out Errs errs mklog ${CLEANFILES} ${OBJS} \ + lib${LIB}.a llib-l${LIB}.ln \ + ${.CURDIR}/tags .depend + rm -f ${POBJS} profiled/*.o lib${LIB}_p.a + cd ${.CURDIR}; rm -rf obj; +.endif + +.if !target(depend) +depend: .depend +.depend: ${SRCS} + mkdep ${CFLAGS:M-[ID+]*} ${AINC} ${.ALLSRC} + @(TMP=/tmp/_depend$$$$; \ + sed -e 's/^\([^\.]*\).o:/\1.o \1.po:/' < .depend > $$TMP; \ + mv $$TMP .depend) +.endif + +.if !target(install) +.if !target(beforeinstall) +beforeinstall: +.endif + +realinstall: beforeinstall + ranlib lib${LIB}.a + install ${COPY} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} lib${LIB}.a \ + ${DESTDIR}${LIBDIR} + ${RANLIB} -t ${DESTDIR}${LIBDIR}/lib${LIB}.a +.if !defined(NOPROFILE) + ranlib lib${LIB}_p.a + install ${COPY} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ + lib${LIB}_p.a ${DESTDIR}${LIBDIR} + ${RANLIB} -t ${DESTDIR}${LIBDIR}/lib${LIB}_p.a +.endif +# install ${COPY} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ +# llib-l${LIB}.ln ${DESTDIR}${LINTLIBDIR} +.if defined(LINKS) && !empty(LINKS) + @set ${LINKS}; \ + while test $$# -ge 2; do \ + l=${DESTDIR}$$1; \ + shift; \ + t=${DESTDIR}$$1; \ + shift; \ + echo $$t -\> $$l; \ + rm -f $$t; \ + ln $$l $$t; \ + done; true +.endif + +install: afterinstall +.if !defined(NOMAN) +afterinstall: realinstall maninstall +.else +afterinstall: realinstall +.endif +.endif + +.if !target(lint) +lint: +.endif + +.if !target(tags) +tags: ${SRCS} + -cd ${.CURDIR}; ctags -f /dev/stdout ${.ALLSRC:M*.c} | \ + sed "s;\${.CURDIR}/;;" > tags +.endif + +.if !defined(NOMAN) +.include +.endif + +.if !target(obj) +.if defined(NOOBJ) +obj: +.else +obj: + @cd ${.CURDIR}; rm -rf obj; \ + here=`pwd`; dest=/usr/obj/`echo $$here | sed 's,/usr/src/,,'`; \ + echo "$$here -> $$dest"; ln -s $$dest obj; \ + if test -d /usr/obj -a ! -d $$dest; then \ + mkdir -p $$dest; \ + else \ + true; \ + fi; +.endif +.endif diff --git a/gnu/usr.bin/cc/common/local-alloc.c b/gnu/usr.bin/cc/lib/local-alloc.c similarity index 100% rename from gnu/usr.bin/cc/common/local-alloc.c rename to gnu/usr.bin/cc/lib/local-alloc.c diff --git a/gnu/usr.bin/cc/common/longlong.h b/gnu/usr.bin/cc/lib/longlong.h similarity index 100% rename from gnu/usr.bin/cc/common/longlong.h rename to gnu/usr.bin/cc/lib/longlong.h diff --git a/gnu/usr.bin/cc/lib/loop.c b/gnu/usr.bin/cc/lib/loop.c new file mode 100644 index 0000000000..ad82384c14 --- /dev/null +++ b/gnu/usr.bin/cc/lib/loop.c @@ -0,0 +1,6508 @@ +/* Move constant computations out of loops. + Copyright (C) 1987, 1988, 1989, 1991, 1992 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* This is the loop optimization pass of the compiler. + It finds invariant computations within loops and moves them + to the beginning of the loop. Then it identifies basic and + general induction variables. Strength reduction is applied to the general + induction variables, and induction variable elimination is applied to + the basic induction variables. + + It also finds cases where + a register is set within the loop by zero-extending a narrower value + and changes these to zero the entire register once before the loop + and merely copy the low part within the loop. + + Most of the complexity is in heuristics to decide when it is worth + while to do these things. */ + +#include +#include "config.h" +#include "rtl.h" +#include "obstack.h" +#include "expr.h" +#include "insn-config.h" +#include "insn-flags.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "recog.h" +#include "flags.h" +#include "real.h" +#include "loop.h" + +/* Vector mapping INSN_UIDs to luids. + The luids are like uids but increase monotonically always. + We use them to see whether a jump comes from outside a given loop. */ + +int *uid_luid; + +/* Indexed by INSN_UID, contains the ordinal giving the (innermost) loop + number the insn is contained in. */ + +int *uid_loop_num; + +/* 1 + largest uid of any insn. */ + +int max_uid_for_loop; + +/* 1 + luid of last insn. */ + +static int max_luid; + +/* Number of loops detected in current function. Used as index to the + next few tables. */ + +static int max_loop_num; + +/* Indexed by loop number, contains the first and last insn of each loop. */ + +static rtx *loop_number_loop_starts, *loop_number_loop_ends; + +/* For each loop, gives the containing loop number, -1 if none. */ + +int *loop_outer_loop; + +/* Indexed by loop number, contains a nonzero value if the "loop" isn't + really a loop (an insn outside the loop branches into it). */ + +static char *loop_invalid; + +/* Indexed by loop number, links together all LABEL_REFs which refer to + code labels outside the loop. Used by routines that need to know all + loop exits, such as final_biv_value and final_giv_value. + + This does not include loop exits due to return instructions. This is + because all bivs and givs are pseudos, and hence must be dead after a + return, so the presense of a return does not affect any of the + optimizations that use this info. It is simpler to just not include return + instructions on this list. */ + +rtx *loop_number_exit_labels; + +/* Holds the number of loop iterations. It is zero if the number could not be + calculated. Must be unsigned since the number of iterations can + be as high as 2^wordsize-1. For loops with a wider iterator, this number + will will be zero if the number of loop iterations is too large for an + unsigned integer to hold. */ + +unsigned HOST_WIDE_INT loop_n_iterations; + +/* Nonzero if there is a subroutine call in the current loop. + (unknown_address_altered is also nonzero in this case.) */ + +static int loop_has_call; + +/* Nonzero if there is a volatile memory reference in the current + loop. */ + +static int loop_has_volatile; + +/* Added loop_continue which is the NOTE_INSN_LOOP_CONT of the + current loop. A continue statement will generate a branch to + NEXT_INSN (loop_continue). */ + +static rtx loop_continue; + +/* Indexed by register number, contains the number of times the reg + is set during the loop being scanned. + During code motion, a negative value indicates a reg that has been + made a candidate; in particular -2 means that it is an candidate that + we know is equal to a constant and -1 means that it is an candidate + not known equal to a constant. + After code motion, regs moved have 0 (which is accurate now) + while the failed candidates have the original number of times set. + + Therefore, at all times, == 0 indicates an invariant register; + < 0 a conditionally invariant one. */ + +static short *n_times_set; + +/* Original value of n_times_set; same except that this value + is not set negative for a reg whose sets have been made candidates + and not set to 0 for a reg that is moved. */ + +static short *n_times_used; + +/* Index by register number, 1 indicates that the register + cannot be moved or strength reduced. */ + +static char *may_not_optimize; + +/* Nonzero means reg N has already been moved out of one loop. + This reduces the desire to move it out of another. */ + +static char *moved_once; + +/* Array of MEMs that are stored in this loop. If there are too many to fit + here, we just turn on unknown_address_altered. */ + +#define NUM_STORES 20 +static rtx loop_store_mems[NUM_STORES]; + +/* Index of first available slot in above array. */ +static int loop_store_mems_idx; + +/* Nonzero if we don't know what MEMs were changed in the current loop. + This happens if the loop contains a call (in which case `loop_has_call' + will also be set) or if we store into more than NUM_STORES MEMs. */ + +static int unknown_address_altered; + +/* Count of movable (i.e. invariant) instructions discovered in the loop. */ +static int num_movables; + +/* Count of memory write instructions discovered in the loop. */ +static int num_mem_sets; + +/* Number of loops contained within the current one, including itself. */ +static int loops_enclosed; + +/* Bound on pseudo register number before loop optimization. + A pseudo has valid regscan info if its number is < max_reg_before_loop. */ +int max_reg_before_loop; + +/* This obstack is used in product_cheap_p to allocate its rtl. It + may call gen_reg_rtx which, in turn, may reallocate regno_reg_rtx. + If we used the same obstack that it did, we would be deallocating + that array. */ + +static struct obstack temp_obstack; + +/* This is where the pointer to the obstack being used for RTL is stored. */ + +extern struct obstack *rtl_obstack; + +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free + +extern char *oballoc (); + +/* During the analysis of a loop, a chain of `struct movable's + is made to record all the movable insns found. + Then the entire chain can be scanned to decide which to move. */ + +struct movable +{ + rtx insn; /* A movable insn */ + rtx set_src; /* The expression this reg is set from. */ + rtx set_dest; /* The destination of this SET. */ + rtx dependencies; /* When INSN is libcall, this is an EXPR_LIST + of any registers used within the LIBCALL. */ + int consec; /* Number of consecutive following insns + that must be moved with this one. */ + int regno; /* The register it sets */ + short lifetime; /* lifetime of that register; + may be adjusted when matching movables + that load the same value are found. */ + short savings; /* Number of insns we can move for this reg, + including other movables that force this + or match this one. */ + unsigned int cond : 1; /* 1 if only conditionally movable */ + unsigned int force : 1; /* 1 means MUST move this insn */ + unsigned int global : 1; /* 1 means reg is live outside this loop */ + /* If PARTIAL is 1, GLOBAL means something different: + that the reg is live outside the range from where it is set + to the following label. */ + unsigned int done : 1; /* 1 inhibits further processing of this */ + + unsigned int partial : 1; /* 1 means this reg is used for zero-extending. + In particular, moving it does not make it + invariant. */ + unsigned int move_insn : 1; /* 1 means that we call emit_move_insn to + load SRC, rather than copying INSN. */ + unsigned int is_equiv : 1; /* 1 means a REG_EQUIV is present on INSN. */ + enum machine_mode savemode; /* Nonzero means it is a mode for a low part + that we should avoid changing when clearing + the rest of the reg. */ + struct movable *match; /* First entry for same value */ + struct movable *forces; /* An insn that must be moved if this is */ + struct movable *next; +}; + +FILE *loop_dump_stream; + +/* Forward declarations. */ + +static void find_and_verify_loops (); +static void mark_loop_jump (); +static void prescan_loop (); +static int reg_in_basic_block_p (); +static int consec_sets_invariant_p (); +static rtx libcall_other_reg (); +static int labels_in_range_p (); +static void count_loop_regs_set (); +static void note_addr_stored (); +static int loop_reg_used_before_p (); +static void scan_loop (); +static void replace_call_address (); +static rtx skip_consec_insns (); +static int libcall_benefit (); +static void ignore_some_movables (); +static void force_movables (); +static void combine_movables (); +static int rtx_equal_for_loop_p (); +static void move_movables (); +static void strength_reduce (); +static int valid_initial_value_p (); +static void find_mem_givs (); +static void record_biv (); +static void check_final_value (); +static void record_giv (); +static void update_giv_derive (); +static int basic_induction_var (); +static rtx simplify_giv_expr (); +static int general_induction_var (); +static int consec_sets_giv (); +static int check_dbra_loop (); +static rtx express_from (); +static int combine_givs_p (); +static void combine_givs (); +static int product_cheap_p (); +static int maybe_eliminate_biv (); +static int maybe_eliminate_biv_1 (); +static int last_use_this_basic_block (); +static void record_initial (); +static void update_reg_last_use (); + +/* Relative gain of eliminating various kinds of operations. */ +int add_cost; +#if 0 +int shift_cost; +int mult_cost; +#endif + +/* Benefit penalty, if a giv is not replaceable, i.e. must emit an insn to + copy the value of the strength reduced giv to its original register. */ +int copy_cost; + +void +init_loop () +{ + char *free_point = (char *) oballoc (1); + rtx reg = gen_rtx (REG, word_mode, 0); + rtx pow2 = GEN_INT (32); + rtx lea; + int i; + + add_cost = rtx_cost (gen_rtx (PLUS, word_mode, reg, reg), SET); + + /* We multiply by 2 to reconcile the difference in scale between + these two ways of computing costs. Otherwise the cost of a copy + will be far less than the cost of an add. */ + + copy_cost = 2 * 2; + + /* Free the objects we just allocated. */ + obfree (free_point); + + /* Initialize the obstack used for rtl in product_cheap_p. */ + gcc_obstack_init (&temp_obstack); +} + +/* Entry point of this file. Perform loop optimization + on the current function. F is the first insn of the function + and DUMPFILE is a stream for output of a trace of actions taken + (or 0 if none should be output). */ + +void +loop_optimize (f, dumpfile) + /* f is the first instruction of a chain of insns for one function */ + rtx f; + FILE *dumpfile; +{ + register rtx insn; + register int i; + rtx end; + rtx last_insn; + + loop_dump_stream = dumpfile; + + init_recog_no_volatile (); + init_alias_analysis (); + + max_reg_before_loop = max_reg_num (); + + moved_once = (char *) alloca (max_reg_before_loop); + bzero (moved_once, max_reg_before_loop); + + regs_may_share = 0; + + /* Count the number of loops. */ + + max_loop_num = 0; + for (insn = f; insn; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == NOTE + && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG) + max_loop_num++; + } + + /* Don't waste time if no loops. */ + if (max_loop_num == 0) + return; + + /* Get size to use for tables indexed by uids. + Leave some space for labels allocated by find_and_verify_loops. */ + max_uid_for_loop = get_max_uid () + 1 + max_loop_num * 32; + + uid_luid = (int *) alloca (max_uid_for_loop * sizeof (int)); + uid_loop_num = (int *) alloca (max_uid_for_loop * sizeof (int)); + + bzero (uid_luid, max_uid_for_loop * sizeof (int)); + bzero (uid_loop_num, max_uid_for_loop * sizeof (int)); + + /* Allocate tables for recording each loop. We set each entry, so they need + not be zeroed. */ + loop_number_loop_starts = (rtx *) alloca (max_loop_num * sizeof (rtx)); + loop_number_loop_ends = (rtx *) alloca (max_loop_num * sizeof (rtx)); + loop_outer_loop = (int *) alloca (max_loop_num * sizeof (int)); + loop_invalid = (char *) alloca (max_loop_num * sizeof (char)); + loop_number_exit_labels = (rtx *) alloca (max_loop_num * sizeof (rtx)); + + /* Find and process each loop. + First, find them, and record them in order of their beginnings. */ + find_and_verify_loops (f); + + /* Now find all register lifetimes. This must be done after + find_and_verify_loops, because it might reorder the insns in the + function. */ + reg_scan (f, max_reg_num (), 1); + + /* See if we went too far. */ + if (get_max_uid () > max_uid_for_loop) + abort (); + + /* Compute the mapping from uids to luids. + LUIDs are numbers assigned to insns, like uids, + except that luids increase monotonically through the code. + Don't assign luids to line-number NOTEs, so that the distance in luids + between two insns is not affected by -g. */ + + for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) + { + last_insn = insn; + if (GET_CODE (insn) != NOTE + || NOTE_LINE_NUMBER (insn) <= 0) + uid_luid[INSN_UID (insn)] = ++i; + else + /* Give a line number note the same luid as preceding insn. */ + uid_luid[INSN_UID (insn)] = i; + } + + max_luid = i + 1; + + /* Don't leave gaps in uid_luid for insns that have been + deleted. It is possible that the first or last insn + using some register has been deleted by cross-jumping. + Make sure that uid_luid for that former insn's uid + points to the general area where that insn used to be. */ + for (i = 0; i < max_uid_for_loop; i++) + { + uid_luid[0] = uid_luid[i]; + if (uid_luid[0] != 0) + break; + } + for (i = 0; i < max_uid_for_loop; i++) + if (uid_luid[i] == 0) + uid_luid[i] = uid_luid[i - 1]; + + /* Create a mapping from loops to BLOCK tree nodes. */ + if (flag_unroll_loops && write_symbols != NO_DEBUG) + find_loop_tree_blocks (); + + /* Now scan the loops, last ones first, since this means inner ones are done + before outer ones. */ + for (i = max_loop_num-1; i >= 0; i--) + if (! loop_invalid[i] && loop_number_loop_ends[i]) + scan_loop (loop_number_loop_starts[i], loop_number_loop_ends[i], + max_reg_num ()); + + /* If debugging and unrolling loops, we must replicate the tree nodes + corresponding to the blocks inside the loop, so that the original one + to one mapping will remain. */ + if (flag_unroll_loops && write_symbols != NO_DEBUG) + unroll_block_trees (); +} + +/* Optimize one loop whose start is LOOP_START and end is END. + LOOP_START is the NOTE_INSN_LOOP_BEG and END is the matching + NOTE_INSN_LOOP_END. */ + +/* ??? Could also move memory writes out of loops if the destination address + is invariant, the source is invariant, the memory write is not volatile, + and if we can prove that no read inside the loop can read this address + before the write occurs. If there is a read of this address after the + write, then we can also mark the memory read as invariant. */ + +static void +scan_loop (loop_start, end, nregs) + rtx loop_start, end; + int nregs; +{ + register int i; + register rtx p; + /* 1 if we are scanning insns that could be executed zero times. */ + int maybe_never = 0; + /* 1 if we are scanning insns that might never be executed + due to a subroutine call which might exit before they are reached. */ + int call_passed = 0; + /* For a rotated loop that is entered near the bottom, + this is the label at the top. Otherwise it is zero. */ + rtx loop_top = 0; + /* Jump insn that enters the loop, or 0 if control drops in. */ + rtx loop_entry_jump = 0; + /* Place in the loop where control enters. */ + rtx scan_start; + /* Number of insns in the loop. */ + int insn_count; + int in_libcall = 0; + int tem; + rtx temp; + /* The SET from an insn, if it is the only SET in the insn. */ + rtx set, set1; + /* Chain describing insns movable in current loop. */ + struct movable *movables = 0; + /* Last element in `movables' -- so we can add elements at the end. */ + struct movable *last_movable = 0; + /* Ratio of extra register life span we can justify + for saving an instruction. More if loop doesn't call subroutines + since in that case saving an insn makes more difference + and more registers are available. */ + int threshold; + /* If we have calls, contains the insn in which a register was used + if it was used exactly once; contains const0_rtx if it was used more + than once. */ + rtx *reg_single_usage = 0; + + n_times_set = (short *) alloca (nregs * sizeof (short)); + n_times_used = (short *) alloca (nregs * sizeof (short)); + may_not_optimize = (char *) alloca (nregs); + + /* Determine whether this loop starts with a jump down to a test at + the end. This will occur for a small number of loops with a test + that is too complex to duplicate in front of the loop. + + We search for the first insn or label in the loop, skipping NOTEs. + However, we must be careful not to skip past a NOTE_INSN_LOOP_BEG + (because we might have a loop executed only once that contains a + loop which starts with a jump to its exit test) or a NOTE_INSN_LOOP_END + (in case we have a degenerate loop). + + Note that if we mistakenly think that a loop is entered at the top + when, in fact, it is entered at the exit test, the only effect will be + slightly poorer optimization. Making the opposite error can generate + incorrect code. Since very few loops now start with a jump to the + exit test, the code here to detect that case is very conservative. */ + + for (p = NEXT_INSN (loop_start); + p != end + && GET_CODE (p) != CODE_LABEL && GET_RTX_CLASS (GET_CODE (p)) != 'i' + && (GET_CODE (p) != NOTE + || (NOTE_LINE_NUMBER (p) != NOTE_INSN_LOOP_BEG + && NOTE_LINE_NUMBER (p) != NOTE_INSN_LOOP_END)); + p = NEXT_INSN (p)) + ; + + scan_start = p; + + /* Set up variables describing this loop. */ + prescan_loop (loop_start, end); + threshold = (loop_has_call ? 1 : 2) * (1 + n_non_fixed_regs); + + /* If loop has a jump before the first label, + the true entry is the target of that jump. + Start scan from there. + But record in LOOP_TOP the place where the end-test jumps + back to so we can scan that after the end of the loop. */ + if (GET_CODE (p) == JUMP_INSN) + { + loop_entry_jump = p; + + /* Loop entry must be unconditional jump (and not a RETURN) */ + if (simplejump_p (p) + && JUMP_LABEL (p) != 0 + /* Check to see whether the jump actually + jumps out of the loop (meaning it's no loop). + This case can happen for things like + do {..} while (0). If this label was generated previously + by loop, we can't tell anything about it and have to reject + the loop. */ + && INSN_UID (JUMP_LABEL (p)) < max_uid_for_loop + && INSN_LUID (JUMP_LABEL (p)) >= INSN_LUID (loop_start) + && INSN_LUID (JUMP_LABEL (p)) < INSN_LUID (end)) + { + loop_top = next_label (scan_start); + scan_start = JUMP_LABEL (p); + } + } + + /* If SCAN_START was an insn created by loop, we don't know its luid + as required by loop_reg_used_before_p. So skip such loops. (This + test may never be true, but it's best to play it safe.) + + Also, skip loops where we do not start scanning at a label. This + test also rejects loops starting with a JUMP_INSN that failed the + test above. */ + + if (INSN_UID (scan_start) >= max_uid_for_loop + || GET_CODE (scan_start) != CODE_LABEL) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, "\nLoop from %d to %d is phony.\n\n", + INSN_UID (loop_start), INSN_UID (end)); + return; + } + + /* Count number of times each reg is set during this loop. + Set may_not_optimize[I] if it is not safe to move out + the setting of register I. If this loop has calls, set + reg_single_usage[I]. */ + + bzero (n_times_set, nregs * sizeof (short)); + bzero (may_not_optimize, nregs); + + if (loop_has_call) + { + reg_single_usage = (rtx *) alloca (nregs * sizeof (rtx)); + bzero (reg_single_usage, nregs * sizeof (rtx)); + } + + count_loop_regs_set (loop_top ? loop_top : loop_start, end, + may_not_optimize, reg_single_usage, &insn_count, nregs); + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + may_not_optimize[i] = 1, n_times_set[i] = 1; + bcopy (n_times_set, n_times_used, nregs * sizeof (short)); + + if (loop_dump_stream) + { + fprintf (loop_dump_stream, "\nLoop from %d to %d: %d real insns.\n", + INSN_UID (loop_start), INSN_UID (end), insn_count); + if (loop_continue) + fprintf (loop_dump_stream, "Continue at insn %d.\n", + INSN_UID (loop_continue)); + } + + /* Scan through the loop finding insns that are safe to move. + Set n_times_set negative for the reg being set, so that + this reg will be considered invariant for subsequent insns. + We consider whether subsequent insns use the reg + in deciding whether it is worth actually moving. + + MAYBE_NEVER is nonzero if we have passed a conditional jump insn + and therefore it is possible that the insns we are scanning + would never be executed. At such times, we must make sure + that it is safe to execute the insn once instead of zero times. + When MAYBE_NEVER is 0, all insns will be executed at least once + so that is not a problem. */ + + p = scan_start; + while (1) + { + p = NEXT_INSN (p); + /* At end of a straight-in loop, we are done. + At end of a loop entered at the bottom, scan the top. */ + if (p == scan_start) + break; + if (p == end) + { + if (loop_top != 0) + p = NEXT_INSN (loop_top); + else + break; + if (p == scan_start) + break; + } + + if (GET_RTX_CLASS (GET_CODE (p)) == 'i' + && find_reg_note (p, REG_LIBCALL, NULL_RTX)) + in_libcall = 1; + else if (GET_RTX_CLASS (GET_CODE (p)) == 'i' + && find_reg_note (p, REG_RETVAL, NULL_RTX)) + in_libcall = 0; + + if (GET_CODE (p) == INSN + && (set = single_set (p)) + && GET_CODE (SET_DEST (set)) == REG + && ! may_not_optimize[REGNO (SET_DEST (set))]) + { + int tem1 = 0; + int tem2 = 0; + int move_insn = 0; + rtx src = SET_SRC (set); + rtx dependencies = 0; + + /* Figure out what to use as a source of this insn. If a REG_EQUIV + note is given or if a REG_EQUAL note with a constant operand is + specified, use it as the source and mark that we should move + this insn by calling emit_move_insn rather that duplicating the + insn. + + Otherwise, only use the REG_EQUAL contents if a REG_RETVAL note + is present. */ + temp = find_reg_note (p, REG_EQUIV, NULL_RTX); + if (temp) + src = XEXP (temp, 0), move_insn = 1; + else + { + temp = find_reg_note (p, REG_EQUAL, NULL_RTX); + if (temp && CONSTANT_P (XEXP (temp, 0))) + src = XEXP (temp, 0), move_insn = 1; + if (temp && find_reg_note (p, REG_RETVAL, NULL_RTX)) + { + src = XEXP (temp, 0); + /* A libcall block can use regs that don't appear in + the equivalent expression. To move the libcall, + we must move those regs too. */ + dependencies = libcall_other_reg (p, src); + } + } + + /* Don't try to optimize a register that was made + by loop-optimization for an inner loop. + We don't know its life-span, so we can't compute the benefit. */ + if (REGNO (SET_DEST (set)) >= max_reg_before_loop) + ; + /* In order to move a register, we need to have one of three cases: + (1) it is used only in the same basic block as the set + (2) it is not a user variable and it is not used in the + exit test (this can cause the variable to be used + before it is set just like a user-variable). + (3) the set is guaranteed to be executed once the loop starts, + and the reg is not used until after that. */ + else if (! ((! maybe_never + && ! loop_reg_used_before_p (set, p, loop_start, + scan_start, end)) + || (! REG_USERVAR_P (SET_DEST (PATTERN (p))) + && ! REG_LOOP_TEST_P (SET_DEST (PATTERN (p)))) + || reg_in_basic_block_p (p, SET_DEST (PATTERN (p))))) + ; + else if ((tem = invariant_p (src)) + && (dependencies == 0 + || (tem2 = invariant_p (dependencies)) != 0) + && (n_times_set[REGNO (SET_DEST (set))] == 1 + || (tem1 + = consec_sets_invariant_p (SET_DEST (set), + n_times_set[REGNO (SET_DEST (set))], + p))) + /* If the insn can cause a trap (such as divide by zero), + can't move it unless it's guaranteed to be executed + once loop is entered. Even a function call might + prevent the trap insn from being reached + (since it might exit!) */ + && ! ((maybe_never || call_passed) + && may_trap_p (src))) + { + register struct movable *m; + register int regno = REGNO (SET_DEST (set)); + + /* A potential lossage is where we have a case where two insns + can be combined as long as they are both in the loop, but + we move one of them outside the loop. For large loops, + this can lose. The most common case of this is the address + of a function being called. + + Therefore, if this register is marked as being used exactly + once if we are in a loop with calls (a "large loop"), see if + we can replace the usage of this register with the source + of this SET. If we can, delete this insn. + + Don't do this if P has a REG_RETVAL note or if we have + SMALL_REGISTER_CLASSES and SET_SRC is a hard register. */ + + if (reg_single_usage && reg_single_usage[regno] != 0 + && reg_single_usage[regno] != const0_rtx + && regno_first_uid[regno] == INSN_UID (p) + && (regno_last_uid[regno] + == INSN_UID (reg_single_usage[regno])) + && n_times_set[REGNO (SET_DEST (set))] == 1 + && ! side_effects_p (SET_SRC (set)) + && ! find_reg_note (p, REG_RETVAL, NULL_RTX) +#ifdef SMALL_REGISTER_CLASSES + && ! (GET_CODE (SET_SRC (set)) == REG + && REGNO (SET_SRC (set)) < FIRST_PSEUDO_REGISTER) +#endif + /* This test is not redundant; SET_SRC (set) might be + a call-clobbered register and the life of REGNO + might span a call. */ + && ! modified_between_p (SET_SRC (set), p, + reg_single_usage[regno]) + && validate_replace_rtx (SET_DEST (set), SET_SRC (set), + reg_single_usage[regno])) + { + /* Replace any usage in a REG_EQUAL note. */ + REG_NOTES (reg_single_usage[regno]) + = replace_rtx (REG_NOTES (reg_single_usage[regno]), + SET_DEST (set), SET_SRC (set)); + + PUT_CODE (p, NOTE); + NOTE_LINE_NUMBER (p) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (p) = 0; + n_times_set[regno] = 0; + continue; + } + + m = (struct movable *) alloca (sizeof (struct movable)); + m->next = 0; + m->insn = p; + m->set_src = src; + m->dependencies = dependencies; + m->set_dest = SET_DEST (set); + m->force = 0; + m->consec = n_times_set[REGNO (SET_DEST (set))] - 1; + m->done = 0; + m->forces = 0; + m->partial = 0; + m->move_insn = move_insn; + m->is_equiv = (find_reg_note (p, REG_EQUIV, NULL_RTX) != 0); + m->savemode = VOIDmode; + m->regno = regno; + /* Set M->cond if either invariant_p or consec_sets_invariant_p + returned 2 (only conditionally invariant). */ + m->cond = ((tem | tem1 | tem2) > 1); + m->global = (uid_luid[regno_last_uid[regno]] > INSN_LUID (end) + || uid_luid[regno_first_uid[regno]] < INSN_LUID (loop_start)); + m->match = 0; + m->lifetime = (uid_luid[regno_last_uid[regno]] + - uid_luid[regno_first_uid[regno]]); + m->savings = n_times_used[regno]; + if (find_reg_note (p, REG_RETVAL, NULL_RTX)) + m->savings += libcall_benefit (p); + n_times_set[regno] = move_insn ? -2 : -1; + /* Add M to the end of the chain MOVABLES. */ + if (movables == 0) + movables = m; + else + last_movable->next = m; + last_movable = m; + + if (m->consec > 0) + { + /* Skip this insn, not checking REG_LIBCALL notes. */ + p = NEXT_INSN (p); + /* Skip the consecutive insns, if there are any. */ + p = skip_consec_insns (p, m->consec); + /* Back up to the last insn of the consecutive group. */ + p = prev_nonnote_insn (p); + + /* We must now reset m->move_insn, m->is_equiv, and possibly + m->set_src to correspond to the effects of all the + insns. */ + temp = find_reg_note (p, REG_EQUIV, NULL_RTX); + if (temp) + m->set_src = XEXP (temp, 0), m->move_insn = 1; + else + { + temp = find_reg_note (p, REG_EQUAL, NULL_RTX); + if (temp && CONSTANT_P (XEXP (temp, 0))) + m->set_src = XEXP (temp, 0), m->move_insn = 1; + else + m->move_insn = 0; + + } + m->is_equiv = (find_reg_note (p, REG_EQUIV, NULL_RTX) != 0); + } + } + /* If this register is always set within a STRICT_LOW_PART + or set to zero, then its high bytes are constant. + So clear them outside the loop and within the loop + just load the low bytes. + We must check that the machine has an instruction to do so. + Also, if the value loaded into the register + depends on the same register, this cannot be done. */ + else if (SET_SRC (set) == const0_rtx + && GET_CODE (NEXT_INSN (p)) == INSN + && (set1 = single_set (NEXT_INSN (p))) + && GET_CODE (set1) == SET + && (GET_CODE (SET_DEST (set1)) == STRICT_LOW_PART) + && (GET_CODE (XEXP (SET_DEST (set1), 0)) == SUBREG) + && (SUBREG_REG (XEXP (SET_DEST (set1), 0)) + == SET_DEST (set)) + && !reg_mentioned_p (SET_DEST (set), SET_SRC (set1))) + { + register int regno = REGNO (SET_DEST (set)); + if (n_times_set[regno] == 2) + { + register struct movable *m; + m = (struct movable *) alloca (sizeof (struct movable)); + m->next = 0; + m->insn = p; + m->set_dest = SET_DEST (set); + m->dependencies = 0; + m->force = 0; + m->consec = 0; + m->done = 0; + m->forces = 0; + m->move_insn = 0; + m->partial = 1; + /* If the insn may not be executed on some cycles, + we can't clear the whole reg; clear just high part. + Not even if the reg is used only within this loop. + Consider this: + while (1) + while (s != t) { + if (foo ()) x = *s; + use (x); + } + Clearing x before the inner loop could clobber a value + being saved from the last time around the outer loop. + However, if the reg is not used outside this loop + and all uses of the register are in the same + basic block as the store, there is no problem. + + If this insn was made by loop, we don't know its + INSN_LUID and hence must make a conservative + assumption. */ + m->global = (INSN_UID (p) >= max_uid_for_loop + || (uid_luid[regno_last_uid[regno]] + > INSN_LUID (end)) + || (uid_luid[regno_first_uid[regno]] + < INSN_LUID (p)) + || (labels_in_range_p + (p, uid_luid[regno_first_uid[regno]]))); + if (maybe_never && m->global) + m->savemode = GET_MODE (SET_SRC (set1)); + else + m->savemode = VOIDmode; + m->regno = regno; + m->cond = 0; + m->match = 0; + m->lifetime = (uid_luid[regno_last_uid[regno]] + - uid_luid[regno_first_uid[regno]]); + m->savings = 1; + n_times_set[regno] = -1; + /* Add M to the end of the chain MOVABLES. */ + if (movables == 0) + movables = m; + else + last_movable->next = m; + last_movable = m; + } + } + } + /* Past a call insn, we get to insns which might not be executed + because the call might exit. This matters for insns that trap. + Call insns inside a REG_LIBCALL/REG_RETVAL block always return, + so they don't count. */ + else if (GET_CODE (p) == CALL_INSN && ! in_libcall) + call_passed = 1; + /* Past a label or a jump, we get to insns for which we + can't count on whether or how many times they will be + executed during each iteration. Therefore, we can + only move out sets of trivial variables + (those not used after the loop). */ + /* This code appears in three places, once in scan_loop, and twice + in strength_reduce. */ + else if ((GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN) + /* If we enter the loop in the middle, and scan around to the + beginning, don't set maybe_never for that. This must be an + unconditional jump, otherwise the code at the top of the + loop might never be executed. Unconditional jumps are + followed a by barrier then loop end. */ + && ! (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p) == loop_top + && NEXT_INSN (NEXT_INSN (p)) == end + && simplejump_p (p))) + maybe_never = 1; + /* At the virtual top of a converted loop, insns are again known to + be executed: logically, the loop begins here even though the exit + code has been duplicated. */ + else if (GET_CODE (p) == NOTE + && NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_VTOP) + maybe_never = call_passed = 0; + } + + /* If one movable subsumes another, ignore that other. */ + + ignore_some_movables (movables); + + /* For each movable insn, see if the reg that it loads + leads when it dies right into another conditionally movable insn. + If so, record that the second insn "forces" the first one, + since the second can be moved only if the first is. */ + + force_movables (movables); + + /* See if there are multiple movable insns that load the same value. + If there are, make all but the first point at the first one + through the `match' field, and add the priorities of them + all together as the priority of the first. */ + + combine_movables (movables, nregs); + + /* Now consider each movable insn to decide whether it is worth moving. + Store 0 in n_times_set for each reg that is moved. */ + + move_movables (movables, threshold, + insn_count, loop_start, end, nregs); + + /* Now candidates that still are negative are those not moved. + Change n_times_set to indicate that those are not actually invariant. */ + for (i = 0; i < nregs; i++) + if (n_times_set[i] < 0) + n_times_set[i] = n_times_used[i]; + + if (flag_strength_reduce) + strength_reduce (scan_start, end, loop_top, + insn_count, loop_start, end); +} + +/* Add elements to *OUTPUT to record all the pseudo-regs + mentioned in IN_THIS but not mentioned in NOT_IN_THIS. */ + +void +record_excess_regs (in_this, not_in_this, output) + rtx in_this, not_in_this; + rtx *output; +{ + enum rtx_code code; + char *fmt; + int i; + + code = GET_CODE (in_this); + + switch (code) + { + case PC: + case CC0: + case CONST_INT: + case CONST_DOUBLE: + case CONST: + case SYMBOL_REF: + case LABEL_REF: + return; + + case REG: + if (REGNO (in_this) >= FIRST_PSEUDO_REGISTER + && ! reg_mentioned_p (in_this, not_in_this)) + *output = gen_rtx (EXPR_LIST, VOIDmode, in_this, *output); + return; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + int j; + + switch (fmt[i]) + { + case 'E': + for (j = 0; j < XVECLEN (in_this, i); j++) + record_excess_regs (XVECEXP (in_this, i, j), not_in_this, output); + break; + + case 'e': + record_excess_regs (XEXP (in_this, i), not_in_this, output); + break; + } + } +} + +/* Check what regs are referred to in the libcall block ending with INSN, + aside from those mentioned in the equivalent value. + If there are none, return 0. + If there are one or more, return an EXPR_LIST containing all of them. */ + +static rtx +libcall_other_reg (insn, equiv) + rtx insn, equiv; +{ + rtx note = find_reg_note (insn, REG_RETVAL, NULL_RTX); + rtx p = XEXP (note, 0); + rtx output = 0; + + /* First, find all the regs used in the libcall block + that are not mentioned as inputs to the result. */ + + while (p != insn) + { + if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN + || GET_CODE (p) == CALL_INSN) + record_excess_regs (PATTERN (p), equiv, &output); + p = NEXT_INSN (p); + } + + return output; +} + +/* Return 1 if all uses of REG + are between INSN and the end of the basic block. */ + +static int +reg_in_basic_block_p (insn, reg) + rtx insn, reg; +{ + int regno = REGNO (reg); + rtx p; + + if (regno_first_uid[regno] != INSN_UID (insn)) + return 0; + + /* Search this basic block for the already recorded last use of the reg. */ + for (p = insn; p; p = NEXT_INSN (p)) + { + switch (GET_CODE (p)) + { + case NOTE: + break; + + case INSN: + case CALL_INSN: + /* Ordinary insn: if this is the last use, we win. */ + if (regno_last_uid[regno] == INSN_UID (p)) + return 1; + break; + + case JUMP_INSN: + /* Jump insn: if this is the last use, we win. */ + if (regno_last_uid[regno] == INSN_UID (p)) + return 1; + /* Otherwise, it's the end of the basic block, so we lose. */ + return 0; + + case CODE_LABEL: + case BARRIER: + /* It's the end of the basic block, so we lose. */ + return 0; + } + } + + /* The "last use" doesn't follow the "first use"?? */ + abort (); +} + +/* Compute the benefit of eliminating the insns in the block whose + last insn is LAST. This may be a group of insns used to compute a + value directly or can contain a library call. */ + +static int +libcall_benefit (last) + rtx last; +{ + rtx insn; + int benefit = 0; + + for (insn = XEXP (find_reg_note (last, REG_RETVAL, NULL_RTX), 0); + insn != last; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == CALL_INSN) + benefit += 10; /* Assume at least this many insns in a library + routine. */ + else if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) != USE + && GET_CODE (PATTERN (insn)) != CLOBBER) + benefit++; + } + + return benefit; +} + +/* Skip COUNT insns from INSN, counting library calls as 1 insn. */ + +static rtx +skip_consec_insns (insn, count) + rtx insn; + int count; +{ + for (; count > 0; count--) + { + rtx temp; + + /* If first insn of libcall sequence, skip to end. */ + /* Do this at start of loop, since INSN is guaranteed to + be an insn here. */ + if (GET_CODE (insn) != NOTE + && (temp = find_reg_note (insn, REG_LIBCALL, NULL_RTX))) + insn = XEXP (temp, 0); + + do insn = NEXT_INSN (insn); + while (GET_CODE (insn) == NOTE); + } + + return insn; +} + +/* Ignore any movable whose insn falls within a libcall + which is part of another movable. + We make use of the fact that the movable for the libcall value + was made later and so appears later on the chain. */ + +static void +ignore_some_movables (movables) + struct movable *movables; +{ + register struct movable *m, *m1; + + for (m = movables; m; m = m->next) + { + /* Is this a movable for the value of a libcall? */ + rtx note = find_reg_note (m->insn, REG_RETVAL, NULL_RTX); + if (note) + { + rtx insn; + /* Check for earlier movables inside that range, + and mark them invalid. We cannot use LUIDs here because + insns created by loop.c for prior loops don't have LUIDs. + Rather than reject all such insns from movables, we just + explicitly check each insn in the libcall (since invariant + libcalls aren't that common). */ + for (insn = XEXP (note, 0); insn != m->insn; insn = NEXT_INSN (insn)) + for (m1 = movables; m1 != m; m1 = m1->next) + if (m1->insn == insn) + m1->done = 1; + } + } +} + +/* For each movable insn, see if the reg that it loads + leads when it dies right into another conditionally movable insn. + If so, record that the second insn "forces" the first one, + since the second can be moved only if the first is. */ + +static void +force_movables (movables) + struct movable *movables; +{ + register struct movable *m, *m1; + for (m1 = movables; m1; m1 = m1->next) + /* Omit this if moving just the (SET (REG) 0) of a zero-extend. */ + if (!m1->partial && !m1->done) + { + int regno = m1->regno; + for (m = m1->next; m; m = m->next) + /* ??? Could this be a bug? What if CSE caused the + register of M1 to be used after this insn? + Since CSE does not update regno_last_uid, + this insn M->insn might not be where it dies. + But very likely this doesn't matter; what matters is + that M's reg is computed from M1's reg. */ + if (INSN_UID (m->insn) == regno_last_uid[regno] + && !m->done) + break; + if (m != 0 && m->set_src == m1->set_dest + /* If m->consec, m->set_src isn't valid. */ + && m->consec == 0) + m = 0; + + /* Increase the priority of the moving the first insn + since it permits the second to be moved as well. */ + if (m != 0) + { + m->forces = m1; + m1->lifetime += m->lifetime; + m1->savings += m1->savings; + } + } +} + +/* Find invariant expressions that are equal and can be combined into + one register. */ + +static void +combine_movables (movables, nregs) + struct movable *movables; + int nregs; +{ + register struct movable *m; + char *matched_regs = (char *) alloca (nregs); + enum machine_mode mode; + + /* Regs that are set more than once are not allowed to match + or be matched. I'm no longer sure why not. */ + /* Perhaps testing m->consec_sets would be more appropriate here? */ + + for (m = movables; m; m = m->next) + if (m->match == 0 && n_times_used[m->regno] == 1 && !m->partial) + { + register struct movable *m1; + int regno = m->regno; + rtx reg_note, reg_note1; + + bzero (matched_regs, nregs); + matched_regs[regno] = 1; + + for (m1 = movables; m1; m1 = m1->next) + if (m != m1 && m1->match == 0 && n_times_used[m1->regno] == 1 + /* A reg used outside the loop mustn't be eliminated. */ + && !m1->global + /* A reg used for zero-extending mustn't be eliminated. */ + && !m1->partial + && (matched_regs[m1->regno] + || + ( + /* Can combine regs with different modes loaded from the + same constant only if the modes are the same or + if both are integer modes with M wider or the same + width as M1. The check for integer is redundant, but + safe, since the only case of differing destination + modes with equal sources is when both sources are + VOIDmode, i.e., CONST_INT. */ + (GET_MODE (m->set_dest) == GET_MODE (m1->set_dest) + || (GET_MODE_CLASS (GET_MODE (m->set_dest)) == MODE_INT + && GET_MODE_CLASS (GET_MODE (m1->set_dest)) == MODE_INT + && (GET_MODE_BITSIZE (GET_MODE (m->set_dest)) + >= GET_MODE_BITSIZE (GET_MODE (m1->set_dest))))) + /* See if the source of M1 says it matches M. */ + && ((GET_CODE (m1->set_src) == REG + && matched_regs[REGNO (m1->set_src)]) + || rtx_equal_for_loop_p (m->set_src, m1->set_src, + movables)))) + && ((m->dependencies == m1->dependencies) + || rtx_equal_p (m->dependencies, m1->dependencies))) + { + m->lifetime += m1->lifetime; + m->savings += m1->savings; + m1->done = 1; + m1->match = m; + matched_regs[m1->regno] = 1; + } + } + + /* Now combine the regs used for zero-extension. + This can be done for those not marked `global' + provided their lives don't overlap. */ + + for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode; + mode = GET_MODE_WIDER_MODE (mode)) + { + register struct movable *m0 = 0; + + /* Combine all the registers for extension from mode MODE. + Don't combine any that are used outside this loop. */ + for (m = movables; m; m = m->next) + if (m->partial && ! m->global + && mode == GET_MODE (SET_SRC (PATTERN (NEXT_INSN (m->insn))))) + { + register struct movable *m1; + int first = uid_luid[regno_first_uid[m->regno]]; + int last = uid_luid[regno_last_uid[m->regno]]; + + if (m0 == 0) + { + /* First one: don't check for overlap, just record it. */ + m0 = m; + continue; + } + + /* Make sure they extend to the same mode. + (Almost always true.) */ + if (GET_MODE (m->set_dest) != GET_MODE (m0->set_dest)) + continue; + + /* We already have one: check for overlap with those + already combined together. */ + for (m1 = movables; m1 != m; m1 = m1->next) + if (m1 == m0 || (m1->partial && m1->match == m0)) + if (! (uid_luid[regno_first_uid[m1->regno]] > last + || uid_luid[regno_last_uid[m1->regno]] < first)) + goto overlap; + + /* No overlap: we can combine this with the others. */ + m0->lifetime += m->lifetime; + m0->savings += m->savings; + m->done = 1; + m->match = m0; + + overlap: ; + } + } +} + +/* Return 1 if regs X and Y will become the same if moved. */ + +static int +regs_match_p (x, y, movables) + rtx x, y; + struct movable *movables; +{ + int xn = REGNO (x); + int yn = REGNO (y); + struct movable *mx, *my; + + for (mx = movables; mx; mx = mx->next) + if (mx->regno == xn) + break; + + for (my = movables; my; my = my->next) + if (my->regno == yn) + break; + + return (mx && my + && ((mx->match == my->match && mx->match != 0) + || mx->match == my + || mx == my->match)); +} + +/* Return 1 if X and Y are identical-looking rtx's. + This is the Lisp function EQUAL for rtx arguments. + + If two registers are matching movables or a movable register and an + equivalent constant, consider them equal. */ + +static int +rtx_equal_for_loop_p (x, y, movables) + rtx x, y; + struct movable *movables; +{ + register int i; + register int j; + register struct movable *m; + register enum rtx_code code; + register char *fmt; + + if (x == y) + return 1; + if (x == 0 || y == 0) + return 0; + + code = GET_CODE (x); + + /* If we have a register and a constant, they may sometimes be + equal. */ + if (GET_CODE (x) == REG && n_times_set[REGNO (x)] == -2 + && CONSTANT_P (y)) + for (m = movables; m; m = m->next) + if (m->move_insn && m->regno == REGNO (x) + && rtx_equal_p (m->set_src, y)) + return 1; + + else if (GET_CODE (y) == REG && n_times_set[REGNO (y)] == -2 + && CONSTANT_P (x)) + for (m = movables; m; m = m->next) + if (m->move_insn && m->regno == REGNO (y) + && rtx_equal_p (m->set_src, x)) + return 1; + + /* Otherwise, rtx's of different codes cannot be equal. */ + if (code != GET_CODE (y)) + return 0; + + /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. + (REG:SI x) and (REG:HI x) are NOT equivalent. */ + + if (GET_MODE (x) != GET_MODE (y)) + return 0; + + /* These three types of rtx's can be compared nonrecursively. */ + if (code == REG) + return (REGNO (x) == REGNO (y) || regs_match_p (x, y, movables)); + + if (code == LABEL_REF) + return XEXP (x, 0) == XEXP (y, 0); + if (code == SYMBOL_REF) + return XSTR (x, 0) == XSTR (y, 0); + + /* Compare the elements. If any pair of corresponding elements + fail to match, return 0 for the whole things. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + switch (fmt[i]) + { + case 'w': + if (XWINT (x, i) != XWINT (y, i)) + return 0; + break; + + case 'i': + if (XINT (x, i) != XINT (y, i)) + return 0; + break; + + case 'E': + /* Two vectors must have the same length. */ + if (XVECLEN (x, i) != XVECLEN (y, i)) + return 0; + + /* And the corresponding elements must match. */ + for (j = 0; j < XVECLEN (x, i); j++) + if (rtx_equal_for_loop_p (XVECEXP (x, i, j), XVECEXP (y, i, j), movables) == 0) + return 0; + break; + + case 'e': + if (rtx_equal_for_loop_p (XEXP (x, i), XEXP (y, i), movables) == 0) + return 0; + break; + + case 's': + if (strcmp (XSTR (x, i), XSTR (y, i))) + return 0; + break; + + case 'u': + /* These are just backpointers, so they don't matter. */ + break; + + case '0': + break; + + /* It is believed that rtx's at this level will never + contain anything but integers and other rtx's, + except for within LABEL_REFs and SYMBOL_REFs. */ + default: + abort (); + } + } + return 1; +} + +/* If X contains any LABEL_REF's, add REG_LABEL notes for them to all + insns in INSNS which use thet reference. */ + +static void +add_label_notes (x, insns) + rtx x; + rtx insns; +{ + enum rtx_code code = GET_CODE (x); + int i, j; + char *fmt; + rtx insn; + + if (code == LABEL_REF && !LABEL_REF_NONLOCAL_P (x)) + { + rtx next = next_real_insn (XEXP (x, 0)); + + /* Don't record labels that refer to dispatch tables. + This is not necessary, since the tablejump references the same label. + And if we did record them, flow.c would make worse code. */ + if (next == 0 + || ! (GET_CODE (next) == JUMP_INSN + && (GET_CODE (PATTERN (next)) == ADDR_VEC + || GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC))) + { + for (insn = insns; insn; insn = NEXT_INSN (insn)) + if (reg_mentioned_p (XEXP (x, 0), insn)) + REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_LABEL, XEXP (x, 0), + REG_NOTES (insn)); + } + return; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + add_label_notes (XEXP (x, i), insns); + else if (fmt[i] == 'E') + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + add_label_notes (XVECEXP (x, i, j), insns); + } +} + +/* Scan MOVABLES, and move the insns that deserve to be moved. + If two matching movables are combined, replace one reg with the + other throughout. */ + +static void +move_movables (movables, threshold, insn_count, loop_start, end, nregs) + struct movable *movables; + int threshold; + int insn_count; + rtx loop_start; + rtx end; + int nregs; +{ + rtx new_start = 0; + register struct movable *m; + register rtx p; + /* Map of pseudo-register replacements to handle combining + when we move several insns that load the same value + into different pseudo-registers. */ + rtx *reg_map = (rtx *) alloca (nregs * sizeof (rtx)); + char *already_moved = (char *) alloca (nregs); + + bzero (already_moved, nregs); + bzero (reg_map, nregs * sizeof (rtx)); + + num_movables = 0; + + for (m = movables; m; m = m->next) + { + /* Describe this movable insn. */ + + if (loop_dump_stream) + { + fprintf (loop_dump_stream, "Insn %d: regno %d (life %d), ", + INSN_UID (m->insn), m->regno, m->lifetime); + if (m->consec > 0) + fprintf (loop_dump_stream, "consec %d, ", m->consec); + if (m->cond) + fprintf (loop_dump_stream, "cond "); + if (m->force) + fprintf (loop_dump_stream, "force "); + if (m->global) + fprintf (loop_dump_stream, "global "); + if (m->done) + fprintf (loop_dump_stream, "done "); + if (m->move_insn) + fprintf (loop_dump_stream, "move-insn "); + if (m->match) + fprintf (loop_dump_stream, "matches %d ", + INSN_UID (m->match->insn)); + if (m->forces) + fprintf (loop_dump_stream, "forces %d ", + INSN_UID (m->forces->insn)); + } + + /* Count movables. Value used in heuristics in strength_reduce. */ + num_movables++; + + /* Ignore the insn if it's already done (it matched something else). + Otherwise, see if it is now safe to move. */ + + if (!m->done + && (! m->cond + || (1 == invariant_p (m->set_src) + && (m->dependencies == 0 + || 1 == invariant_p (m->dependencies)) + && (m->consec == 0 + || 1 == consec_sets_invariant_p (m->set_dest, + m->consec + 1, + m->insn)))) + && (! m->forces || m->forces->done)) + { + register int regno; + register rtx p; + int savings = m->savings; + + /* We have an insn that is safe to move. + Compute its desirability. */ + + p = m->insn; + regno = m->regno; + + if (loop_dump_stream) + fprintf (loop_dump_stream, "savings %d ", savings); + + if (moved_once[regno]) + { + insn_count *= 2; + + if (loop_dump_stream) + fprintf (loop_dump_stream, "halved since already moved "); + } + + /* An insn MUST be moved if we already moved something else + which is safe only if this one is moved too: that is, + if already_moved[REGNO] is nonzero. */ + + /* An insn is desirable to move if the new lifetime of the + register is no more than THRESHOLD times the old lifetime. + If it's not desirable, it means the loop is so big + that moving won't speed things up much, + and it is liable to make register usage worse. */ + + /* It is also desirable to move if it can be moved at no + extra cost because something else was already moved. */ + + if (already_moved[regno] + || (threshold * savings * m->lifetime) >= insn_count + || (m->forces && m->forces->done + && n_times_used[m->forces->regno] == 1)) + { + int count; + register struct movable *m1; + rtx first; + + /* Now move the insns that set the reg. */ + + if (m->partial && m->match) + { + rtx newpat, i1; + rtx r1, r2; + /* Find the end of this chain of matching regs. + Thus, we load each reg in the chain from that one reg. + And that reg is loaded with 0 directly, + since it has ->match == 0. */ + for (m1 = m; m1->match; m1 = m1->match); + newpat = gen_move_insn (SET_DEST (PATTERN (m->insn)), + SET_DEST (PATTERN (m1->insn))); + i1 = emit_insn_before (newpat, loop_start); + + /* Mark the moved, invariant reg as being allowed to + share a hard reg with the other matching invariant. */ + REG_NOTES (i1) = REG_NOTES (m->insn); + r1 = SET_DEST (PATTERN (m->insn)); + r2 = SET_DEST (PATTERN (m1->insn)); + regs_may_share = gen_rtx (EXPR_LIST, VOIDmode, r1, + gen_rtx (EXPR_LIST, VOIDmode, r2, + regs_may_share)); + delete_insn (m->insn); + + if (new_start == 0) + new_start = i1; + + if (loop_dump_stream) + fprintf (loop_dump_stream, " moved to %d", INSN_UID (i1)); + } + /* If we are to re-generate the item being moved with a + new move insn, first delete what we have and then emit + the move insn before the loop. */ + else if (m->move_insn) + { + rtx i1, temp; + + for (count = m->consec; count >= 0; count--) + { + /* If this is the first insn of a library call sequence, + skip to the end. */ + if (GET_CODE (p) != NOTE + && (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX))) + p = XEXP (temp, 0); + + /* If this is the last insn of a libcall sequence, then + delete every insn in the sequence except the last. + The last insn is handled in the normal manner. */ + if (GET_CODE (p) != NOTE + && (temp = find_reg_note (p, REG_RETVAL, NULL_RTX))) + { + temp = XEXP (temp, 0); + while (temp != p) + temp = delete_insn (temp); + } + + p = delete_insn (p); + } + + start_sequence (); + emit_move_insn (m->set_dest, m->set_src); + temp = get_insns (); + end_sequence (); + + add_label_notes (m->set_src, temp); + + i1 = emit_insns_before (temp, loop_start); + if (! find_reg_note (i1, REG_EQUAL, NULL_RTX)) + REG_NOTES (i1) + = gen_rtx (EXPR_LIST, + m->is_equiv ? REG_EQUIV : REG_EQUAL, + m->set_src, REG_NOTES (i1)); + + if (loop_dump_stream) + fprintf (loop_dump_stream, " moved to %d", INSN_UID (i1)); + + /* The more regs we move, the less we like moving them. */ + threshold -= 3; + } + else + { + for (count = m->consec; count >= 0; count--) + { + rtx i1, temp; + + /* If first insn of libcall sequence, skip to end. */ + /* Do this at start of loop, since p is guaranteed to + be an insn here. */ + if (GET_CODE (p) != NOTE + && (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX))) + p = XEXP (temp, 0); + + /* If last insn of libcall sequence, move all + insns except the last before the loop. The last + insn is handled in the normal manner. */ + if (GET_CODE (p) != NOTE + && (temp = find_reg_note (p, REG_RETVAL, NULL_RTX))) + { + rtx fn_address = 0; + rtx fn_reg = 0; + rtx fn_address_insn = 0; + + first = 0; + for (temp = XEXP (temp, 0); temp != p; + temp = NEXT_INSN (temp)) + { + rtx body; + rtx n; + rtx next; + + if (GET_CODE (temp) == NOTE) + continue; + + body = PATTERN (temp); + + /* Find the next insn after TEMP, + not counting USE or NOTE insns. */ + for (next = NEXT_INSN (temp); next != p; + next = NEXT_INSN (next)) + if (! (GET_CODE (next) == INSN + && GET_CODE (PATTERN (next)) == USE) + && GET_CODE (next) != NOTE) + break; + + /* If that is the call, this may be the insn + that loads the function address. + + Extract the function address from the insn + that loads it into a register. + If this insn was cse'd, we get incorrect code. + + So emit a new move insn that copies the + function address into the register that the + call insn will use. flow.c will delete any + redundant stores that we have created. */ + if (GET_CODE (next) == CALL_INSN + && GET_CODE (body) == SET + && GET_CODE (SET_DEST (body)) == REG + && (n = find_reg_note (temp, REG_EQUAL, + NULL_RTX))) + { + fn_reg = SET_SRC (body); + if (GET_CODE (fn_reg) != REG) + fn_reg = SET_DEST (body); + fn_address = XEXP (n, 0); + fn_address_insn = temp; + } + /* We have the call insn. + If it uses the register we suspect it might, + load it with the correct address directly. */ + if (GET_CODE (temp) == CALL_INSN + && fn_address != 0 + && reg_referenced_p (fn_reg, body)) + emit_insn_after (gen_move_insn (fn_reg, + fn_address), + fn_address_insn); + + if (GET_CODE (temp) == CALL_INSN) + i1 = emit_call_insn_before (body, loop_start); + else + i1 = emit_insn_before (body, loop_start); + if (first == 0) + first = i1; + if (temp == fn_address_insn) + fn_address_insn = i1; + REG_NOTES (i1) = REG_NOTES (temp); + delete_insn (temp); + } + } + if (m->savemode != VOIDmode) + { + /* P sets REG to zero; but we should clear only + the bits that are not covered by the mode + m->savemode. */ + rtx reg = m->set_dest; + rtx sequence; + rtx tem; + + start_sequence (); + tem = expand_binop + (GET_MODE (reg), and_optab, reg, + GEN_INT ((((HOST_WIDE_INT) 1 + << GET_MODE_BITSIZE (m->savemode))) + - 1), + reg, 1, OPTAB_LIB_WIDEN); + if (tem == 0) + abort (); + if (tem != reg) + emit_move_insn (reg, tem); + sequence = gen_sequence (); + end_sequence (); + i1 = emit_insn_before (sequence, loop_start); + } + else if (GET_CODE (p) == CALL_INSN) + i1 = emit_call_insn_before (PATTERN (p), loop_start); + else + i1 = emit_insn_before (PATTERN (p), loop_start); + + REG_NOTES (i1) = REG_NOTES (p); + + /* If there is a REG_EQUAL note present whose value is + not loop invariant, then delete it, since it may + cause problems with later optimization passes. + It is possible for cse to create such notes + like this as a result of record_jump_cond. */ + + if ((temp = find_reg_note (i1, REG_EQUAL, NULL_RTX)) + && ! invariant_p (XEXP (temp, 0))) + remove_note (i1, temp); + + if (new_start == 0) + new_start = i1; + + if (loop_dump_stream) + fprintf (loop_dump_stream, " moved to %d", + INSN_UID (i1)); + +#if 0 + /* This isn't needed because REG_NOTES is copied + below and is wrong since P might be a PARALLEL. */ + if (REG_NOTES (i1) == 0 + && ! m->partial /* But not if it's a zero-extend clr. */ + && ! m->global /* and not if used outside the loop + (since it might get set outside). */ + && CONSTANT_P (SET_SRC (PATTERN (p)))) + REG_NOTES (i1) + = gen_rtx (EXPR_LIST, REG_EQUAL, + SET_SRC (PATTERN (p)), REG_NOTES (i1)); +#endif + + /* If library call, now fix the REG_NOTES that contain + insn pointers, namely REG_LIBCALL on FIRST + and REG_RETVAL on I1. */ + if (temp = find_reg_note (i1, REG_RETVAL, NULL_RTX)) + { + XEXP (temp, 0) = first; + temp = find_reg_note (first, REG_LIBCALL, NULL_RTX); + XEXP (temp, 0) = i1; + } + + delete_insn (p); + do p = NEXT_INSN (p); + while (p && GET_CODE (p) == NOTE); + } + + /* The more regs we move, the less we like moving them. */ + threshold -= 3; + } + + /* Any other movable that loads the same register + MUST be moved. */ + already_moved[regno] = 1; + + /* This reg has been moved out of one loop. */ + moved_once[regno] = 1; + + /* The reg set here is now invariant. */ + if (! m->partial) + n_times_set[regno] = 0; + + m->done = 1; + + /* Change the length-of-life info for the register + to say it lives at least the full length of this loop. + This will help guide optimizations in outer loops. */ + + if (uid_luid[regno_first_uid[regno]] > INSN_LUID (loop_start)) + /* This is the old insn before all the moved insns. + We can't use the moved insn because it is out of range + in uid_luid. Only the old insns have luids. */ + regno_first_uid[regno] = INSN_UID (loop_start); + if (uid_luid[regno_last_uid[regno]] < INSN_LUID (end)) + regno_last_uid[regno] = INSN_UID (end); + + /* Combine with this moved insn any other matching movables. */ + + if (! m->partial) + for (m1 = movables; m1; m1 = m1->next) + if (m1->match == m) + { + rtx temp; + + /* Schedule the reg loaded by M1 + for replacement so that shares the reg of M. + If the modes differ (only possible in restricted + circumstances, make a SUBREG. */ + if (GET_MODE (m->set_dest) == GET_MODE (m1->set_dest)) + reg_map[m1->regno] = m->set_dest; + else + reg_map[m1->regno] + = gen_lowpart_common (GET_MODE (m1->set_dest), + m->set_dest); + + /* Get rid of the matching insn + and prevent further processing of it. */ + m1->done = 1; + + /* if library call, delete all insn except last, which + is deleted below */ + if (temp = find_reg_note (m1->insn, REG_RETVAL, + NULL_RTX)) + { + for (temp = XEXP (temp, 0); temp != m1->insn; + temp = NEXT_INSN (temp)) + delete_insn (temp); + } + delete_insn (m1->insn); + + /* Any other movable that loads the same register + MUST be moved. */ + already_moved[m1->regno] = 1; + + /* The reg merged here is now invariant, + if the reg it matches is invariant. */ + if (! m->partial) + n_times_set[m1->regno] = 0; + } + } + else if (loop_dump_stream) + fprintf (loop_dump_stream, "not desirable"); + } + else if (loop_dump_stream && !m->match) + fprintf (loop_dump_stream, "not safe"); + + if (loop_dump_stream) + fprintf (loop_dump_stream, "\n"); + } + + if (new_start == 0) + new_start = loop_start; + + /* Go through all the instructions in the loop, making + all the register substitutions scheduled in REG_MAP. */ + for (p = new_start; p != end; p = NEXT_INSN (p)) + if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN + || GET_CODE (p) == CALL_INSN) + { + replace_regs (PATTERN (p), reg_map, nregs, 0); + replace_regs (REG_NOTES (p), reg_map, nregs, 0); + INSN_CODE (p) = -1; + } +} + +#if 0 +/* Scan X and replace the address of any MEM in it with ADDR. + REG is the address that MEM should have before the replacement. */ + +static void +replace_call_address (x, reg, addr) + rtx x, reg, addr; +{ + register enum rtx_code code; + register int i; + register char *fmt; + + if (x == 0) + return; + code = GET_CODE (x); + switch (code) + { + case PC: + case CC0: + case CONST_INT: + case CONST_DOUBLE: + case CONST: + case SYMBOL_REF: + case LABEL_REF: + case REG: + return; + + case SET: + /* Short cut for very common case. */ + replace_call_address (XEXP (x, 1), reg, addr); + return; + + case CALL: + /* Short cut for very common case. */ + replace_call_address (XEXP (x, 0), reg, addr); + return; + + case MEM: + /* If this MEM uses a reg other than the one we expected, + something is wrong. */ + if (XEXP (x, 0) != reg) + abort (); + XEXP (x, 0) = addr; + return; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + replace_call_address (XEXP (x, i), reg, addr); + if (fmt[i] == 'E') + { + register int j; + for (j = 0; j < XVECLEN (x, i); j++) + replace_call_address (XVECEXP (x, i, j), reg, addr); + } + } +} +#endif + +/* Return the number of memory refs to addresses that vary + in the rtx X. */ + +static int +count_nonfixed_reads (x) + rtx x; +{ + register enum rtx_code code; + register int i; + register char *fmt; + int value; + + if (x == 0) + return 0; + + code = GET_CODE (x); + switch (code) + { + case PC: + case CC0: + case CONST_INT: + case CONST_DOUBLE: + case CONST: + case SYMBOL_REF: + case LABEL_REF: + case REG: + return 0; + + case MEM: + return ((invariant_p (XEXP (x, 0)) != 1) + + count_nonfixed_reads (XEXP (x, 0))); + } + + value = 0; + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + value += count_nonfixed_reads (XEXP (x, i)); + if (fmt[i] == 'E') + { + register int j; + for (j = 0; j < XVECLEN (x, i); j++) + value += count_nonfixed_reads (XVECEXP (x, i, j)); + } + } + return value; +} + + +#if 0 +/* P is an instruction that sets a register to the result of a ZERO_EXTEND. + Replace it with an instruction to load just the low bytes + if the machine supports such an instruction, + and insert above LOOP_START an instruction to clear the register. */ + +static void +constant_high_bytes (p, loop_start) + rtx p, loop_start; +{ + register rtx new; + register int insn_code_number; + + /* Try to change (SET (REG ...) (ZERO_EXTEND (..:B ...))) + to (SET (STRICT_LOW_PART (SUBREG:B (REG...))) ...). */ + + new = gen_rtx (SET, VOIDmode, + gen_rtx (STRICT_LOW_PART, VOIDmode, + gen_rtx (SUBREG, GET_MODE (XEXP (SET_SRC (PATTERN (p)), 0)), + SET_DEST (PATTERN (p)), + 0)), + XEXP (SET_SRC (PATTERN (p)), 0)); + insn_code_number = recog (new, p); + + if (insn_code_number) + { + register int i; + + /* Clear destination register before the loop. */ + emit_insn_before (gen_rtx (SET, VOIDmode, + SET_DEST (PATTERN (p)), + const0_rtx), + loop_start); + + /* Inside the loop, just load the low part. */ + PATTERN (p) = new; + } +} +#endif + +/* Scan a loop setting the variables `unknown_address_altered', + `num_mem_sets', `loop_continue', loops_enclosed', `loop_has_call', + and `loop_has_volatile'. + Also, fill in the array `loop_store_mems'. */ + +static void +prescan_loop (start, end) + rtx start, end; +{ + register int level = 1; + register rtx insn; + + unknown_address_altered = 0; + loop_has_call = 0; + loop_has_volatile = 0; + loop_store_mems_idx = 0; + + num_mem_sets = 0; + loops_enclosed = 1; + loop_continue = 0; + + for (insn = NEXT_INSN (start); insn != NEXT_INSN (end); + insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == NOTE) + { + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG) + { + ++level; + /* Count number of loops contained in this one. */ + loops_enclosed++; + } + else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END) + { + --level; + if (level == 0) + { + end = insn; + break; + } + } + else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_CONT) + { + if (level == 1) + loop_continue = insn; + } + } + else if (GET_CODE (insn) == CALL_INSN) + { + unknown_address_altered = 1; + loop_has_call = 1; + } + else + { + if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN) + { + if (volatile_refs_p (PATTERN (insn))) + loop_has_volatile = 1; + + note_stores (PATTERN (insn), note_addr_stored); + } + } + } +} + +/* Scan the function looking for loops. Record the start and end of each loop. + Also mark as invalid loops any loops that contain a setjmp or are branched + to from outside the loop. */ + +static void +find_and_verify_loops (f) + rtx f; +{ + rtx insn, label; + int current_loop = -1; + int next_loop = -1; + int loop; + + /* If there are jumps to undefined labels, + treat them as jumps out of any/all loops. + This also avoids writing past end of tables when there are no loops. */ + uid_loop_num[0] = -1; + + /* Find boundaries of loops, mark which loops are contained within + loops, and invalidate loops that have setjmp. */ + + for (insn = f; insn; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == NOTE) + switch (NOTE_LINE_NUMBER (insn)) + { + case NOTE_INSN_LOOP_BEG: + loop_number_loop_starts[++next_loop] = insn; + loop_number_loop_ends[next_loop] = 0; + loop_outer_loop[next_loop] = current_loop; + loop_invalid[next_loop] = 0; + loop_number_exit_labels[next_loop] = 0; + current_loop = next_loop; + break; + + case NOTE_INSN_SETJMP: + /* In this case, we must invalidate our current loop and any + enclosing loop. */ + for (loop = current_loop; loop != -1; loop = loop_outer_loop[loop]) + { + loop_invalid[loop] = 1; + if (loop_dump_stream) + fprintf (loop_dump_stream, + "\nLoop at %d ignored due to setjmp.\n", + INSN_UID (loop_number_loop_starts[loop])); + } + break; + + case NOTE_INSN_LOOP_END: + if (current_loop == -1) + abort (); + + loop_number_loop_ends[current_loop] = insn; + current_loop = loop_outer_loop[current_loop]; + break; + + } + + /* Note that this will mark the NOTE_INSN_LOOP_END note as being in the + enclosing loop, but this doesn't matter. */ + uid_loop_num[INSN_UID (insn)] = current_loop; + } + + /* Any loop containing a label used in an initializer must be invalidated, + because it can be jumped into from anywhere. */ + + for (label = forced_labels; label; label = XEXP (label, 1)) + { + int loop_num; + + for (loop_num = uid_loop_num[INSN_UID (XEXP (label, 0))]; + loop_num != -1; + loop_num = loop_outer_loop[loop_num]) + loop_invalid[loop_num] = 1; + } + + /* Now scan all insn's in the function. If any JUMP_INSN branches into a + loop that it is not contained within, that loop is marked invalid. + If any INSN or CALL_INSN uses a label's address, then the loop containing + that label is marked invalid, because it could be jumped into from + anywhere. + + Also look for blocks of code ending in an unconditional branch that + exits the loop. If such a block is surrounded by a conditional + branch around the block, move the block elsewhere (see below) and + invert the jump to point to the code block. This may eliminate a + label in our loop and will simplify processing by both us and a + possible second cse pass. */ + + for (insn = f; insn; insn = NEXT_INSN (insn)) + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + int this_loop_num = uid_loop_num[INSN_UID (insn)]; + + if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN) + { + rtx note = find_reg_note (insn, REG_LABEL, NULL_RTX); + if (note) + { + int loop_num; + + for (loop_num = uid_loop_num[INSN_UID (XEXP (note, 0))]; + loop_num != -1; + loop_num = loop_outer_loop[loop_num]) + loop_invalid[loop_num] = 1; + } + } + + if (GET_CODE (insn) != JUMP_INSN) + continue; + + mark_loop_jump (PATTERN (insn), this_loop_num); + + /* See if this is an unconditional branch outside the loop. */ + if (this_loop_num != -1 + && (GET_CODE (PATTERN (insn)) == RETURN + || (simplejump_p (insn) + && (uid_loop_num[INSN_UID (JUMP_LABEL (insn))] + != this_loop_num))) + && get_max_uid () < max_uid_for_loop) + { + rtx p; + rtx our_next = next_real_insn (insn); + + /* Go backwards until we reach the start of the loop, a label, + or a JUMP_INSN. */ + for (p = PREV_INSN (insn); + GET_CODE (p) != CODE_LABEL + && ! (GET_CODE (p) == NOTE + && NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG) + && GET_CODE (p) != JUMP_INSN; + p = PREV_INSN (p)) + ; + + /* If we stopped on a JUMP_INSN to the next insn after INSN, + we have a block of code to try to move. + + We look backward and then forward from the target of INSN + to find a BARRIER at the same loop depth as the target. + If we find such a BARRIER, we make a new label for the start + of the block, invert the jump in P and point it to that label, + and move the block of code to the spot we found. */ + + if (GET_CODE (p) == JUMP_INSN + && JUMP_LABEL (p) != 0 + /* Just ignore jumps to labels that were never emitted. + These always indicate compilation errors. */ + && INSN_UID (JUMP_LABEL (p)) != 0 + && condjump_p (p) + && ! simplejump_p (p) + && next_real_insn (JUMP_LABEL (p)) == our_next) + { + rtx target + = JUMP_LABEL (insn) ? JUMP_LABEL (insn) : get_last_insn (); + int target_loop_num = uid_loop_num[INSN_UID (target)]; + rtx loc; + + for (loc = target; loc; loc = PREV_INSN (loc)) + if (GET_CODE (loc) == BARRIER + && uid_loop_num[INSN_UID (loc)] == target_loop_num) + break; + + if (loc == 0) + for (loc = target; loc; loc = NEXT_INSN (loc)) + if (GET_CODE (loc) == BARRIER + && uid_loop_num[INSN_UID (loc)] == target_loop_num) + break; + + if (loc) + { + rtx cond_label = JUMP_LABEL (p); + rtx new_label = get_label_after (p); + + /* Ensure our label doesn't go away. */ + LABEL_NUSES (cond_label)++; + + /* Verify that uid_loop_num is large enough and that + we can invert P. */ + if (invert_jump (p, new_label)) + { + rtx q, r; + + /* Include the BARRIER after INSN and copy the + block after LOC. */ + new_label = squeeze_notes (new_label, NEXT_INSN (insn)); + reorder_insns (new_label, NEXT_INSN (insn), loc); + + /* All those insns are now in TARGET_LOOP_NUM. */ + for (q = new_label; q != NEXT_INSN (NEXT_INSN (insn)); + q = NEXT_INSN (q)) + uid_loop_num[INSN_UID (q)] = target_loop_num; + + /* The label jumped to by INSN is no longer a loop exit. + Unless INSN does not have a label (e.g., it is a + RETURN insn), search loop_number_exit_labels to find + its label_ref, and remove it. Also turn off + LABEL_OUTSIDE_LOOP_P bit. */ + if (JUMP_LABEL (insn)) + { + for (q = 0, + r = loop_number_exit_labels[this_loop_num]; + r; q = r, r = LABEL_NEXTREF (r)) + if (XEXP (r, 0) == JUMP_LABEL (insn)) + { + LABEL_OUTSIDE_LOOP_P (r) = 0; + if (q) + LABEL_NEXTREF (q) = LABEL_NEXTREF (r); + else + loop_number_exit_labels[this_loop_num] + = LABEL_NEXTREF (r); + break; + } + + /* If we didn't find it, then something is wrong. */ + if (! r) + abort (); + } + + /* P is now a jump outside the loop, so it must be put + in loop_number_exit_labels, and marked as such. + The easiest way to do this is to just call + mark_loop_jump again for P. */ + mark_loop_jump (PATTERN (p), this_loop_num); + + /* If INSN now jumps to the insn after it, + delete INSN. */ + if (JUMP_LABEL (insn) != 0 + && (next_real_insn (JUMP_LABEL (insn)) + == next_real_insn (insn))) + delete_insn (insn); + } + + /* Continue the loop after where the conditional + branch used to jump, since the only branch insn + in the block (if it still remains) is an inter-loop + branch and hence needs no processing. */ + insn = NEXT_INSN (cond_label); + + if (--LABEL_NUSES (cond_label) == 0) + delete_insn (cond_label); + } + } + } + } +} + +/* If any label in X jumps to a loop different from LOOP_NUM and any of the + loops it is contained in, mark the target loop invalid. + + For speed, we assume that X is part of a pattern of a JUMP_INSN. */ + +static void +mark_loop_jump (x, loop_num) + rtx x; + int loop_num; +{ + int dest_loop; + int outer_loop; + int i; + + switch (GET_CODE (x)) + { + case PC: + case USE: + case CLOBBER: + case REG: + case MEM: + case CONST_INT: + case CONST_DOUBLE: + case RETURN: + return; + + case CONST: + /* There could be a label reference in here. */ + mark_loop_jump (XEXP (x, 0), loop_num); + return; + + case PLUS: + case MINUS: + case MULT: + case LSHIFT: + mark_loop_jump (XEXP (x, 0), loop_num); + mark_loop_jump (XEXP (x, 1), loop_num); + return; + + case SIGN_EXTEND: + case ZERO_EXTEND: + mark_loop_jump (XEXP (x, 0), loop_num); + return; + + case LABEL_REF: + dest_loop = uid_loop_num[INSN_UID (XEXP (x, 0))]; + + /* Link together all labels that branch outside the loop. This + is used by final_[bg]iv_value and the loop unrolling code. Also + mark this LABEL_REF so we know that this branch should predict + false. */ + + if (dest_loop != loop_num && loop_num != -1) + { + LABEL_OUTSIDE_LOOP_P (x) = 1; + LABEL_NEXTREF (x) = loop_number_exit_labels[loop_num]; + loop_number_exit_labels[loop_num] = x; + } + + /* If this is inside a loop, but not in the current loop or one enclosed + by it, it invalidates at least one loop. */ + + if (dest_loop == -1) + return; + + /* We must invalidate every nested loop containing the target of this + label, except those that also contain the jump insn. */ + + for (; dest_loop != -1; dest_loop = loop_outer_loop[dest_loop]) + { + /* Stop when we reach a loop that also contains the jump insn. */ + for (outer_loop = loop_num; outer_loop != -1; + outer_loop = loop_outer_loop[outer_loop]) + if (dest_loop == outer_loop) + return; + + /* If we get here, we know we need to invalidate a loop. */ + if (loop_dump_stream && ! loop_invalid[dest_loop]) + fprintf (loop_dump_stream, + "\nLoop at %d ignored due to multiple entry points.\n", + INSN_UID (loop_number_loop_starts[dest_loop])); + + loop_invalid[dest_loop] = 1; + } + return; + + case SET: + /* If this is not setting pc, ignore. */ + if (SET_DEST (x) == pc_rtx) + mark_loop_jump (SET_SRC (x), loop_num); + return; + + case IF_THEN_ELSE: + mark_loop_jump (XEXP (x, 1), loop_num); + mark_loop_jump (XEXP (x, 2), loop_num); + return; + + case PARALLEL: + case ADDR_VEC: + for (i = 0; i < XVECLEN (x, 0); i++) + mark_loop_jump (XVECEXP (x, 0, i), loop_num); + return; + + case ADDR_DIFF_VEC: + for (i = 0; i < XVECLEN (x, 1); i++) + mark_loop_jump (XVECEXP (x, 1, i), loop_num); + return; + + default: + /* Nothing else should occur in a JUMP_INSN. */ + abort (); + } +} + +/* Return nonzero if there is a label in the range from + insn INSN to and including the insn whose luid is END + INSN must have an assigned luid (i.e., it must not have + been previously created by loop.c). */ + +static int +labels_in_range_p (insn, end) + rtx insn; + int end; +{ + while (insn && INSN_LUID (insn) <= end) + { + if (GET_CODE (insn) == CODE_LABEL) + return 1; + insn = NEXT_INSN (insn); + } + + return 0; +} + +/* Record that a memory reference X is being set. */ + +static void +note_addr_stored (x) + rtx x; +{ + register int i; + + if (x == 0 || GET_CODE (x) != MEM) + return; + + /* Count number of memory writes. + This affects heuristics in strength_reduce. */ + num_mem_sets++; + + if (unknown_address_altered) + return; + + for (i = 0; i < loop_store_mems_idx; i++) + if (rtx_equal_p (XEXP (loop_store_mems[i], 0), XEXP (x, 0)) + && MEM_IN_STRUCT_P (x) == MEM_IN_STRUCT_P (loop_store_mems[i])) + { + /* We are storing at the same address as previously noted. Save the + wider reference, treating BLKmode as wider. */ + if (GET_MODE (x) == BLKmode + || (GET_MODE_SIZE (GET_MODE (x)) + > GET_MODE_SIZE (GET_MODE (loop_store_mems[i])))) + loop_store_mems[i] = x; + break; + } + + if (i == NUM_STORES) + unknown_address_altered = 1; + + else if (i == loop_store_mems_idx) + loop_store_mems[loop_store_mems_idx++] = x; +} + +/* Return nonzero if the rtx X is invariant over the current loop. + + The value is 2 if we refer to something only conditionally invariant. + + If `unknown_address_altered' is nonzero, no memory ref is invariant. + Otherwise, a memory ref is invariant if it does not conflict with + anything stored in `loop_store_mems'. */ + +int +invariant_p (x) + register rtx x; +{ + register int i; + register enum rtx_code code; + register char *fmt; + int conditional = 0; + + if (x == 0) + return 1; + code = GET_CODE (x); + switch (code) + { + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + case CONST: + return 1; + + case LABEL_REF: + /* A LABEL_REF is normally invariant, however, if we are unrolling + loops, and this label is inside the loop, then it isn't invariant. + This is because each unrolled copy of the loop body will have + a copy of this label. If this was invariant, then an insn loading + the address of this label into a register might get moved outside + the loop, and then each loop body would end up using the same label. + + We don't know the loop bounds here though, so just fail for all + labels. */ + if (flag_unroll_loops) + return 0; + else + return 1; + + case PC: + case CC0: + case UNSPEC_VOLATILE: + return 0; + + case REG: + /* We used to check RTX_UNCHANGING_P (x) here, but that is invalid + since the reg might be set by initialization within the loop. */ + if (x == frame_pointer_rtx || x == arg_pointer_rtx) + return 1; + if (loop_has_call + && REGNO (x) < FIRST_PSEUDO_REGISTER && call_used_regs[REGNO (x)]) + return 0; + if (n_times_set[REGNO (x)] < 0) + return 2; + return n_times_set[REGNO (x)] == 0; + + case MEM: + /* Read-only items (such as constants in a constant pool) are + invariant if their address is. */ + if (RTX_UNCHANGING_P (x)) + break; + + /* If we filled the table (or had a subroutine call), any location + in memory could have been clobbered. */ + if (unknown_address_altered + /* Don't mess with volatile memory references. */ + || MEM_VOLATILE_P (x)) + return 0; + + /* See if there is any dependence between a store and this load. */ + for (i = loop_store_mems_idx - 1; i >= 0; i--) + if (true_dependence (loop_store_mems[i], x)) + return 0; + + /* It's not invalidated by a store in memory + but we must still verify the address is invariant. */ + break; + + case ASM_OPERANDS: + /* Don't mess with insns declared volatile. */ + if (MEM_VOLATILE_P (x)) + return 0; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + int tem = invariant_p (XEXP (x, i)); + if (tem == 0) + return 0; + if (tem == 2) + conditional = 1; + } + else if (fmt[i] == 'E') + { + register int j; + for (j = 0; j < XVECLEN (x, i); j++) + { + int tem = invariant_p (XVECEXP (x, i, j)); + if (tem == 0) + return 0; + if (tem == 2) + conditional = 1; + } + + } + } + + return 1 + conditional; +} + + +/* Return nonzero if all the insns in the loop that set REG + are INSN and the immediately following insns, + and if each of those insns sets REG in an invariant way + (not counting uses of REG in them). + + The value is 2 if some of these insns are only conditionally invariant. + + We assume that INSN itself is the first set of REG + and that its source is invariant. */ + +static int +consec_sets_invariant_p (reg, n_sets, insn) + int n_sets; + rtx reg, insn; +{ + register rtx p = insn; + register int regno = REGNO (reg); + rtx temp; + /* Number of sets we have to insist on finding after INSN. */ + int count = n_sets - 1; + int old = n_times_set[regno]; + int value = 0; + int this; + + /* If N_SETS hit the limit, we can't rely on its value. */ + if (n_sets == 127) + return 0; + + n_times_set[regno] = 0; + + while (count > 0) + { + register enum rtx_code code; + rtx set; + + p = NEXT_INSN (p); + code = GET_CODE (p); + + /* If library call, skip to end of of it. */ + if (code == INSN && (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX))) + p = XEXP (temp, 0); + + this = 0; + if (code == INSN + && (set = single_set (p)) + && GET_CODE (SET_DEST (set)) == REG + && REGNO (SET_DEST (set)) == regno) + { + this = invariant_p (SET_SRC (set)); + if (this != 0) + value |= this; + else if (temp = find_reg_note (p, REG_EQUAL, NULL_RTX)) + { + /* If this is a libcall, then any invariant REG_EQUAL note is OK. + If this is an ordinary insn, then only CONSTANT_P REG_EQUAL + notes are OK. */ + this = (CONSTANT_P (XEXP (temp, 0)) + || (find_reg_note (p, REG_RETVAL, NULL_RTX) + && invariant_p (XEXP (temp, 0)))); + if (this != 0) + value |= this; + } + } + if (this != 0) + count--; + else if (code != NOTE) + { + n_times_set[regno] = old; + return 0; + } + } + + n_times_set[regno] = old; + /* If invariant_p ever returned 2, we return 2. */ + return 1 + (value & 2); +} + +#if 0 +/* I don't think this condition is sufficient to allow INSN + to be moved, so we no longer test it. */ + +/* Return 1 if all insns in the basic block of INSN and following INSN + that set REG are invariant according to TABLE. */ + +static int +all_sets_invariant_p (reg, insn, table) + rtx reg, insn; + short *table; +{ + register rtx p = insn; + register int regno = REGNO (reg); + + while (1) + { + register enum rtx_code code; + p = NEXT_INSN (p); + code = GET_CODE (p); + if (code == CODE_LABEL || code == JUMP_INSN) + return 1; + if (code == INSN && GET_CODE (PATTERN (p)) == SET + && GET_CODE (SET_DEST (PATTERN (p))) == REG + && REGNO (SET_DEST (PATTERN (p))) == regno) + { + if (!invariant_p (SET_SRC (PATTERN (p)), table)) + return 0; + } + } +} +#endif /* 0 */ + +/* Look at all uses (not sets) of registers in X. For each, if it is + the single use, set USAGE[REGNO] to INSN; if there was a previous use in + a different insn, set USAGE[REGNO] to const0_rtx. */ + +static void +find_single_use_in_loop (insn, x, usage) + rtx insn; + rtx x; + rtx *usage; +{ + enum rtx_code code = GET_CODE (x); + char *fmt = GET_RTX_FORMAT (code); + int i, j; + + if (code == REG) + usage[REGNO (x)] + = (usage[REGNO (x)] != 0 && usage[REGNO (x)] != insn) + ? const0_rtx : insn; + + else if (code == SET) + { + /* Don't count SET_DEST if it is a REG; otherwise count things + in SET_DEST because if a register is partially modified, it won't + show up as a potential movable so we don't care how USAGE is set + for it. */ + if (GET_CODE (SET_DEST (x)) != REG) + find_single_use_in_loop (insn, SET_DEST (x), usage); + find_single_use_in_loop (insn, SET_SRC (x), usage); + } + else + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e' && XEXP (x, i) != 0) + find_single_use_in_loop (insn, XEXP (x, i), usage); + else if (fmt[i] == 'E') + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + find_single_use_in_loop (insn, XVECEXP (x, i, j), usage); + } +} + +/* Increment N_TIMES_SET at the index of each register + that is modified by an insn between FROM and TO. + If the value of an element of N_TIMES_SET becomes 127 or more, + stop incrementing it, to avoid overflow. + + Store in SINGLE_USAGE[I] the single insn in which register I is + used, if it is only used once. Otherwise, it is set to 0 (for no + uses) or const0_rtx for more than one use. This parameter may be zero, + in which case this processing is not done. + + Store in *COUNT_PTR the number of actual instruction + in the loop. We use this to decide what is worth moving out. */ + +/* last_set[n] is nonzero iff reg n has been set in the current basic block. + In that case, it is the insn that last set reg n. */ + +static void +count_loop_regs_set (from, to, may_not_move, single_usage, count_ptr, nregs) + register rtx from, to; + char *may_not_move; + rtx *single_usage; + int *count_ptr; + int nregs; +{ + register rtx *last_set = (rtx *) alloca (nregs * sizeof (rtx)); + register rtx insn; + register int count = 0; + register rtx dest; + + bzero (last_set, nregs * sizeof (rtx)); + for (insn = from; insn != to; insn = NEXT_INSN (insn)) + { + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + ++count; + + /* If requested, record registers that have exactly one use. */ + if (single_usage) + { + find_single_use_in_loop (insn, PATTERN (insn), single_usage); + + /* Include uses in REG_EQUAL notes. */ + if (REG_NOTES (insn)) + find_single_use_in_loop (insn, REG_NOTES (insn), single_usage); + } + + if (GET_CODE (PATTERN (insn)) == CLOBBER + && GET_CODE (XEXP (PATTERN (insn), 0)) == REG) + /* Don't move a reg that has an explicit clobber. + We might do so sometimes, but it's not worth the pain. */ + may_not_move[REGNO (XEXP (PATTERN (insn), 0))] = 1; + + if (GET_CODE (PATTERN (insn)) == SET + || GET_CODE (PATTERN (insn)) == CLOBBER) + { + dest = SET_DEST (PATTERN (insn)); + while (GET_CODE (dest) == SUBREG + || GET_CODE (dest) == ZERO_EXTRACT + || GET_CODE (dest) == SIGN_EXTRACT + || GET_CODE (dest) == STRICT_LOW_PART) + dest = XEXP (dest, 0); + if (GET_CODE (dest) == REG) + { + register int regno = REGNO (dest); + /* If this is the first setting of this reg + in current basic block, and it was set before, + it must be set in two basic blocks, so it cannot + be moved out of the loop. */ + if (n_times_set[regno] > 0 && last_set[regno] == 0) + may_not_move[regno] = 1; + /* If this is not first setting in current basic block, + see if reg was used in between previous one and this. + If so, neither one can be moved. */ + if (last_set[regno] != 0 + && reg_used_between_p (dest, last_set[regno], insn)) + may_not_move[regno] = 1; + if (n_times_set[regno] < 127) + ++n_times_set[regno]; + last_set[regno] = insn; + } + } + else if (GET_CODE (PATTERN (insn)) == PARALLEL) + { + register int i; + for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) + { + register rtx x = XVECEXP (PATTERN (insn), 0, i); + if (GET_CODE (x) == CLOBBER && GET_CODE (XEXP (x, 0)) == REG) + /* Don't move a reg that has an explicit clobber. + It's not worth the pain to try to do it correctly. */ + may_not_move[REGNO (XEXP (x, 0))] = 1; + + if (GET_CODE (x) == SET || GET_CODE (x) == CLOBBER) + { + dest = SET_DEST (x); + while (GET_CODE (dest) == SUBREG + || GET_CODE (dest) == ZERO_EXTRACT + || GET_CODE (dest) == SIGN_EXTRACT + || GET_CODE (dest) == STRICT_LOW_PART) + dest = XEXP (dest, 0); + if (GET_CODE (dest) == REG) + { + register int regno = REGNO (dest); + if (n_times_set[regno] > 0 && last_set[regno] == 0) + may_not_move[regno] = 1; + if (last_set[regno] != 0 + && reg_used_between_p (dest, last_set[regno], insn)) + may_not_move[regno] = 1; + if (n_times_set[regno] < 127) + ++n_times_set[regno]; + last_set[regno] = insn; + } + } + } + } + } + if (GET_CODE (insn) == CODE_LABEL || GET_CODE (insn) == JUMP_INSN) + bzero (last_set, nregs * sizeof (rtx)); + } + *count_ptr = count; +} + +/* Given a loop that is bounded by LOOP_START and LOOP_END + and that is entered at SCAN_START, + return 1 if the register set in SET contained in insn INSN is used by + any insn that precedes INSN in cyclic order starting + from the loop entry point. + + We don't want to use INSN_LUID here because if we restrict INSN to those + that have a valid INSN_LUID, it means we cannot move an invariant out + from an inner loop past two loops. */ + +static int +loop_reg_used_before_p (set, insn, loop_start, scan_start, loop_end) + rtx set, insn, loop_start, scan_start, loop_end; +{ + rtx reg = SET_DEST (set); + rtx p; + + /* Scan forward checking for register usage. If we hit INSN, we + are done. Otherwise, if we hit LOOP_END, wrap around to LOOP_START. */ + for (p = scan_start; p != insn; p = NEXT_INSN (p)) + { + if (GET_RTX_CLASS (GET_CODE (p)) == 'i' + && reg_overlap_mentioned_p (reg, PATTERN (p))) + return 1; + + if (p == loop_end) + p = loop_start; + } + + return 0; +} + +/* A "basic induction variable" or biv is a pseudo reg that is set + (within this loop) only by incrementing or decrementing it. */ +/* A "general induction variable" or giv is a pseudo reg whose + value is a linear function of a biv. */ + +/* Bivs are recognized by `basic_induction_var'; + Givs by `general_induct_var'. */ + +/* Indexed by register number, indicates whether or not register is an + induction variable, and if so what type. */ + +enum iv_mode *reg_iv_type; + +/* Indexed by register number, contains pointer to `struct induction' + if register is an induction variable. This holds general info for + all induction variables. */ + +struct induction **reg_iv_info; + +/* Indexed by register number, contains pointer to `struct iv_class' + if register is a basic induction variable. This holds info describing + the class (a related group) of induction variables that the biv belongs + to. */ + +struct iv_class **reg_biv_class; + +/* The head of a list which links together (via the next field) + every iv class for the current loop. */ + +struct iv_class *loop_iv_list; + +/* Communication with routines called via `note_stores'. */ + +static rtx note_insn; + +/* Dummy register to have non-zero DEST_REG for DEST_ADDR type givs. */ + +static rtx addr_placeholder; + +/* ??? Unfinished optimizations, and possible future optimizations, + for the strength reduction code. */ + +/* ??? There is one more optimization you might be interested in doing: to + allocate pseudo registers for frequently-accessed memory locations. + If the same memory location is referenced each time around, it might + be possible to copy it into a register before and out after. + This is especially useful when the memory location is a variable which + is in a stack slot because somewhere its address is taken. If the + loop doesn't contain a function call and the variable isn't volatile, + it is safe to keep the value in a register for the duration of the + loop. One tricky thing is that the copying of the value back from the + register has to be done on all exits from the loop. You need to check that + all the exits from the loop go to the same place. */ + +/* ??? The interaction of biv elimination, and recognition of 'constant' + bivs, may cause problems. */ + +/* ??? Add heuristics so that DEST_ADDR strength reduction does not cause + performance problems. + + Perhaps don't eliminate things that can be combined with an addressing + mode. Find all givs that have the same biv, mult_val, and add_val; + then for each giv, check to see if its only use dies in a following + memory address. If so, generate a new memory address and check to see + if it is valid. If it is valid, then store the modified memory address, + otherwise, mark the giv as not done so that it will get its own iv. */ + +/* ??? Could try to optimize branches when it is known that a biv is always + positive. */ + +/* ??? When replace a biv in a compare insn, we should replace with closest + giv so that an optimized branch can still be recognized by the combiner, + e.g. the VAX acb insn. */ + +/* ??? Many of the checks involving uid_luid could be simplified if regscan + was rerun in loop_optimize whenever a register was added or moved. + Also, some of the optimizations could be a little less conservative. */ + +/* Perform strength reduction and induction variable elimination. */ + +/* Pseudo registers created during this function will be beyond the last + valid index in several tables including n_times_set and regno_last_uid. + This does not cause a problem here, because the added registers cannot be + givs outside of their loop, and hence will never be reconsidered. + But scan_loop must check regnos to make sure they are in bounds. */ + +static void +strength_reduce (scan_start, end, loop_top, insn_count, + loop_start, loop_end) + rtx scan_start; + rtx end; + rtx loop_top; + int insn_count; + rtx loop_start; + rtx loop_end; +{ + rtx p; + rtx set; + rtx inc_val; + rtx mult_val; + rtx dest_reg; + /* This is 1 if current insn is not executed at least once for every loop + iteration. */ + int not_every_iteration = 0; + /* This is 1 if current insn may be executed more than once for every + loop iteration. */ + int maybe_multiple = 0; + /* Temporary list pointers for traversing loop_iv_list. */ + struct iv_class *bl, **backbl; + /* Ratio of extra register life span we can justify + for saving an instruction. More if loop doesn't call subroutines + since in that case saving an insn makes more difference + and more registers are available. */ + /* ??? could set this to last value of threshold in move_movables */ + int threshold = (loop_has_call ? 1 : 2) * (3 + n_non_fixed_regs); + /* Map of pseudo-register replacements. */ + rtx *reg_map; + int call_seen; + rtx test; + rtx end_insert_before; + + reg_iv_type = (enum iv_mode *) alloca (max_reg_before_loop + * sizeof (enum iv_mode *)); + bzero ((char *) reg_iv_type, max_reg_before_loop * sizeof (enum iv_mode *)); + reg_iv_info = (struct induction **) + alloca (max_reg_before_loop * sizeof (struct induction *)); + bzero ((char *) reg_iv_info, (max_reg_before_loop + * sizeof (struct induction *))); + reg_biv_class = (struct iv_class **) + alloca (max_reg_before_loop * sizeof (struct iv_class *)); + bzero ((char *) reg_biv_class, (max_reg_before_loop + * sizeof (struct iv_class *))); + + loop_iv_list = 0; + addr_placeholder = gen_reg_rtx (Pmode); + + /* Save insn immediately after the loop_end. Insns inserted after loop_end + must be put before this insn, so that they will appear in the right + order (i.e. loop order). + + If loop_end is the end of the current function, then emit a + NOTE_INSN_DELETED after loop_end and set end_insert_before to the + dummy note insn. */ + if (NEXT_INSN (loop_end) != 0) + end_insert_before = NEXT_INSN (loop_end); + else + end_insert_before = emit_note_after (NOTE_INSN_DELETED, loop_end); + + /* Scan through loop to find all possible bivs. */ + + p = scan_start; + while (1) + { + p = NEXT_INSN (p); + /* At end of a straight-in loop, we are done. + At end of a loop entered at the bottom, scan the top. */ + if (p == scan_start) + break; + if (p == end) + { + if (loop_top != 0) + p = NEXT_INSN (loop_top); + else + break; + if (p == scan_start) + break; + } + + if (GET_CODE (p) == INSN + && (set = single_set (p)) + && GET_CODE (SET_DEST (set)) == REG) + { + dest_reg = SET_DEST (set); + if (REGNO (dest_reg) < max_reg_before_loop + && REGNO (dest_reg) >= FIRST_PSEUDO_REGISTER + && reg_iv_type[REGNO (dest_reg)] != NOT_BASIC_INDUCT) + { + if (basic_induction_var (SET_SRC (set), dest_reg, p, + &inc_val, &mult_val)) + { + /* It is a possible basic induction variable. + Create and initialize an induction structure for it. */ + + struct induction *v + = (struct induction *) alloca (sizeof (struct induction)); + + record_biv (v, p, dest_reg, inc_val, mult_val, + not_every_iteration, maybe_multiple); + reg_iv_type[REGNO (dest_reg)] = BASIC_INDUCT; + } + else if (REGNO (dest_reg) < max_reg_before_loop) + reg_iv_type[REGNO (dest_reg)] = NOT_BASIC_INDUCT; + } + } + + /* Past CODE_LABEL, we get to insns that may be executed multiple + times. The only way we can be sure that they can't is if every + every jump insn between here and the end of the loop either + returns, exits the loop, or is a forward jump. */ + + if (GET_CODE (p) == CODE_LABEL) + { + rtx insn = p; + + maybe_multiple = 0; + + while (1) + { + insn = NEXT_INSN (insn); + if (insn == scan_start) + break; + if (insn == end) + { + if (loop_top != 0) + insn = NEXT_INSN (loop_top); + else + break; + if (insn == scan_start) + break; + } + + if (GET_CODE (insn) == JUMP_INSN + && GET_CODE (PATTERN (insn)) != RETURN + && (! condjump_p (insn) + || (JUMP_LABEL (insn) != 0 + && (INSN_UID (JUMP_LABEL (insn)) >= max_uid_for_loop + || INSN_UID (insn) >= max_uid_for_loop + || (INSN_LUID (JUMP_LABEL (insn)) + < INSN_LUID (insn)))))) + { + maybe_multiple = 1; + break; + } + } + } + + /* Past a label or a jump, we get to insns for which we can't count + on whether or how many times they will be executed during each + iteration. */ + /* This code appears in three places, once in scan_loop, and twice + in strength_reduce. */ + if ((GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN) + /* If we enter the loop in the middle, and scan around to the + beginning, don't set not_every_iteration for that. + This can be any kind of jump, since we want to know if insns + will be executed if the loop is executed. */ + && ! (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p) == loop_top + && ((NEXT_INSN (NEXT_INSN (p)) == loop_end && simplejump_p (p)) + || (NEXT_INSN (p) == loop_end && condjump_p (p))))) + not_every_iteration = 1; + + /* At the virtual top of a converted loop, insns are again known to + be executed each iteration: logically, the loop begins here + even though the exit code has been duplicated. */ + + else if (GET_CODE (p) == NOTE + && NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_VTOP) + not_every_iteration = 0; + + /* Unlike in the code motion pass where MAYBE_NEVER indicates that + an insn may never be executed, NOT_EVERY_ITERATION indicates whether + or not an insn is known to be executed each iteration of the + loop, whether or not any iterations are known to occur. + + Therefore, if we have just passed a label and have no more labels + between here and the test insn of the loop, we know these insns + will be executed each iteration. This can also happen if we + have just passed a jump, for example, when there are nested loops. */ + + if (not_every_iteration && GET_CODE (p) == CODE_LABEL + && no_labels_between_p (p, loop_end)) + not_every_iteration = 0; + } + + /* Scan loop_iv_list to remove all regs that proved not to be bivs. + Make a sanity check against n_times_set. */ + for (backbl = &loop_iv_list, bl = *backbl; bl; bl = bl->next) + { + if (reg_iv_type[bl->regno] != BASIC_INDUCT + /* Above happens if register modified by subreg, etc. */ + /* Make sure it is not recognized as a basic induction var: */ + || n_times_set[bl->regno] != bl->biv_count + /* If never incremented, it is invariant that we decided not to + move. So leave it alone. */ + || ! bl->incremented) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, "Reg %d: biv discarded, %s\n", + bl->regno, + (reg_iv_type[bl->regno] != BASIC_INDUCT + ? "not induction variable" + : (! bl->incremented ? "never incremented" + : "count error"))); + + reg_iv_type[bl->regno] = NOT_BASIC_INDUCT; + *backbl = bl->next; + } + else + { + backbl = &bl->next; + + if (loop_dump_stream) + fprintf (loop_dump_stream, "Reg %d: biv verified\n", bl->regno); + } + } + + /* Exit if there are no bivs. */ + if (! loop_iv_list) + { + /* Can still unroll the loop anyways, but indicate that there is no + strength reduction info available. */ + if (flag_unroll_loops) + unroll_loop (loop_end, insn_count, loop_start, end_insert_before, 0); + + return; + } + + /* Find initial value for each biv by searching backwards from loop_start, + halting at first label. Also record any test condition. */ + + call_seen = 0; + for (p = loop_start; p && GET_CODE (p) != CODE_LABEL; p = PREV_INSN (p)) + { + note_insn = p; + + if (GET_CODE (p) == CALL_INSN) + call_seen = 1; + + if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN + || GET_CODE (p) == CALL_INSN) + note_stores (PATTERN (p), record_initial); + + /* Record any test of a biv that branches around the loop if no store + between it and the start of loop. We only care about tests with + constants and registers and only certain of those. */ + if (GET_CODE (p) == JUMP_INSN + && JUMP_LABEL (p) != 0 + && next_real_insn (JUMP_LABEL (p)) == next_real_insn (loop_end) + && (test = get_condition_for_loop (p)) != 0 + && GET_CODE (XEXP (test, 0)) == REG + && REGNO (XEXP (test, 0)) < max_reg_before_loop + && (bl = reg_biv_class[REGNO (XEXP (test, 0))]) != 0 + && valid_initial_value_p (XEXP (test, 1), p, call_seen, loop_start) + && bl->init_insn == 0) + { + /* If an NE test, we have an initial value! */ + if (GET_CODE (test) == NE) + { + bl->init_insn = p; + bl->init_set = gen_rtx (SET, VOIDmode, + XEXP (test, 0), XEXP (test, 1)); + } + else + bl->initial_test = test; + } + } + + /* Look at the each biv and see if we can say anything better about its + initial value from any initializing insns set up above. (This is done + in two passes to avoid missing SETs in a PARALLEL.) */ + for (bl = loop_iv_list; bl; bl = bl->next) + { + rtx src; + + if (! bl->init_insn) + continue; + + src = SET_SRC (bl->init_set); + + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Biv %d initialized at insn %d: initial value ", + bl->regno, INSN_UID (bl->init_insn)); + + if (valid_initial_value_p (src, bl->init_insn, call_seen, loop_start)) + { + bl->initial_value = src; + + if (loop_dump_stream) + { + if (GET_CODE (src) == CONST_INT) + fprintf (loop_dump_stream, "%d\n", INTVAL (src)); + else + { + print_rtl (loop_dump_stream, src); + fprintf (loop_dump_stream, "\n"); + } + } + } + else + { + /* Biv initial value is not simple move, + so let it keep initial value of "itself". */ + + if (loop_dump_stream) + fprintf (loop_dump_stream, "is complex\n"); + } + } + + /* Search the loop for general induction variables. */ + + /* A register is a giv if: it is only set once, it is a function of a + biv and a constant (or invariant), and it is not a biv. */ + + not_every_iteration = 0; + p = scan_start; + while (1) + { + p = NEXT_INSN (p); + /* At end of a straight-in loop, we are done. + At end of a loop entered at the bottom, scan the top. */ + if (p == scan_start) + break; + if (p == end) + { + if (loop_top != 0) + p = NEXT_INSN (loop_top); + else + break; + if (p == scan_start) + break; + } + + /* Look for a general induction variable in a register. */ + if (GET_CODE (p) == INSN + && (set = single_set (p)) + && GET_CODE (SET_DEST (set)) == REG + && ! may_not_optimize[REGNO (SET_DEST (set))]) + { + rtx src_reg; + rtx add_val; + rtx mult_val; + int benefit; + rtx regnote = 0; + + dest_reg = SET_DEST (set); + if (REGNO (dest_reg) < FIRST_PSEUDO_REGISTER) + continue; + + if (/* SET_SRC is a giv. */ + ((benefit = general_induction_var (SET_SRC (set), + &src_reg, &add_val, + &mult_val)) + /* Equivalent expression is a giv. */ + || ((regnote = find_reg_note (p, REG_EQUAL, NULL_RTX)) + && (benefit = general_induction_var (XEXP (regnote, 0), + &src_reg, + &add_val, &mult_val)))) + /* Don't try to handle any regs made by loop optimization. + We have nothing on them in regno_first_uid, etc. */ + && REGNO (dest_reg) < max_reg_before_loop + /* Don't recognize a BASIC_INDUCT_VAR here. */ + && dest_reg != src_reg + /* This must be the only place where the register is set. */ + && (n_times_set[REGNO (dest_reg)] == 1 + /* or all sets must be consecutive and make a giv. */ + || (benefit = consec_sets_giv (benefit, p, + src_reg, dest_reg, + &add_val, &mult_val)))) + { + int count; + struct induction *v + = (struct induction *) alloca (sizeof (struct induction)); + rtx temp; + + /* If this is a library call, increase benefit. */ + if (find_reg_note (p, REG_RETVAL, NULL_RTX)) + benefit += libcall_benefit (p); + + /* Skip the consecutive insns, if there are any. */ + for (count = n_times_set[REGNO (dest_reg)] - 1; + count > 0; count--) + { + /* If first insn of libcall sequence, skip to end. + Do this at start of loop, since INSN is guaranteed to + be an insn here. */ + if (GET_CODE (p) != NOTE + && (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX))) + p = XEXP (temp, 0); + + do p = NEXT_INSN (p); + while (GET_CODE (p) == NOTE); + } + + record_giv (v, p, src_reg, dest_reg, mult_val, add_val, benefit, + DEST_REG, not_every_iteration, NULL_PTR, loop_start, + loop_end); + + } + } + +#ifndef DONT_REDUCE_ADDR + /* Look for givs which are memory addresses. */ + /* This resulted in worse code on a VAX 8600. I wonder if it + still does. */ + if (GET_CODE (p) == INSN) + find_mem_givs (PATTERN (p), p, not_every_iteration, loop_start, + loop_end); +#endif + + /* Update the status of whether giv can derive other givs. This can + change when we pass a label or an insn that updates a biv. */ + if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN + || GET_CODE (p) == CODE_LABEL) + update_giv_derive (p); + + /* Past a label or a jump, we get to insns for which we can't count + on whether or how many times they will be executed during each + iteration. */ + /* This code appears in three places, once in scan_loop, and twice + in strength_reduce. */ + if ((GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN) + /* If we enter the loop in the middle, and scan around + to the beginning, don't set not_every_iteration for that. + This can be any kind of jump, since we want to know if insns + will be executed if the loop is executed. */ + && ! (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p) == loop_top + && ((NEXT_INSN (NEXT_INSN (p)) == loop_end && simplejump_p (p)) + || (NEXT_INSN (p) == loop_end && condjump_p (p))))) + not_every_iteration = 1; + + /* At the virtual top of a converted loop, insns are again known to + be executed each iteration: logically, the loop begins here + even though the exit code has been duplicated. */ + + else if (GET_CODE (p) == NOTE + && NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_VTOP) + not_every_iteration = 0; + + /* Unlike in the code motion pass where MAYBE_NEVER indicates that + an insn may never be executed, NOT_EVERY_ITERATION indicates whether + or not an insn is known to be executed each iteration of the + loop, whether or not any iterations are known to occur. + + Therefore, if we have just passed a label and have no more labels + between here and the test insn of the loop, we know these insns + will be executed each iteration. */ + + if (not_every_iteration && GET_CODE (p) == CODE_LABEL + && no_labels_between_p (p, loop_end)) + not_every_iteration = 0; + } + + /* Try to calculate and save the number of loop iterations. This is + set to zero if the actual number can not be calculated. This must + be called after all giv's have been identified, since otherwise it may + fail if the iteration variable is a giv. */ + + loop_n_iterations = loop_iterations (loop_start, loop_end); + + /* Now for each giv for which we still don't know whether or not it is + replaceable, check to see if it is replaceable because its final value + can be calculated. This must be done after loop_iterations is called, + so that final_giv_value will work correctly. */ + + for (bl = loop_iv_list; bl; bl = bl->next) + { + struct induction *v; + + for (v = bl->giv; v; v = v->next_iv) + if (! v->replaceable && ! v->not_replaceable) + check_final_value (v, loop_start, loop_end); + } + + /* Try to prove that the loop counter variable (if any) is always + nonnegative; if so, record that fact with a REG_NONNEG note + so that "decrement and branch until zero" insn can be used. */ + check_dbra_loop (loop_end, insn_count, loop_start); + + /* Create reg_map to hold substitutions for replaceable giv regs. */ + reg_map = (rtx *) alloca (max_reg_before_loop * sizeof (rtx)); + bzero ((char *) reg_map, max_reg_before_loop * sizeof (rtx)); + + /* Examine each iv class for feasibility of strength reduction/induction + variable elimination. */ + + for (bl = loop_iv_list; bl; bl = bl->next) + { + struct induction *v; + int benefit; + int all_reduced; + rtx final_value = 0; + + /* Test whether it will be possible to eliminate this biv + provided all givs are reduced. This is possible if either + the reg is not used outside the loop, or we can compute + what its final value will be. + + For architectures with a decrement_and_branch_until_zero insn, + don't do this if we put a REG_NONNEG note on the endtest for + this biv. */ + + /* Compare against bl->init_insn rather than loop_start. + We aren't concerned with any uses of the biv between + init_insn and loop_start since these won't be affected + by the value of the biv elsewhere in the function, so + long as init_insn doesn't use the biv itself. + March 14, 1989 -- self@bayes.arc.nasa.gov */ + + if ((uid_luid[regno_last_uid[bl->regno]] < INSN_LUID (loop_end) + && bl->init_insn + && INSN_UID (bl->init_insn) < max_uid_for_loop + && uid_luid[regno_first_uid[bl->regno]] >= INSN_LUID (bl->init_insn) +#ifdef HAVE_decrement_and_branch_until_zero + && ! bl->nonneg +#endif + && ! reg_mentioned_p (bl->biv->dest_reg, SET_SRC (bl->init_set))) + || ((final_value = final_biv_value (bl, loop_start, loop_end)) +#ifdef HAVE_decrement_and_branch_until_zero + && ! bl->nonneg +#endif + )) + bl->eliminable = maybe_eliminate_biv (bl, loop_start, end, 0, + threshold, insn_count); + else + { + if (loop_dump_stream) + { + fprintf (loop_dump_stream, + "Cannot eliminate biv %d.\n", + bl->regno); + fprintf (loop_dump_stream, + "First use: insn %d, last use: insn %d.\n", + regno_first_uid[bl->regno], + regno_last_uid[bl->regno]); + } + } + + /* Combine all giv's for this iv_class. */ + combine_givs (bl); + + /* This will be true at the end, if all givs which depend on this + biv have been strength reduced. + We can't (currently) eliminate the biv unless this is so. */ + all_reduced = 1; + + /* Check each giv in this class to see if we will benefit by reducing + it. Skip giv's combined with others. */ + for (v = bl->giv; v; v = v->next_iv) + { + struct induction *tv; + + if (v->ignore || v->same) + continue; + + benefit = v->benefit; + + /* Reduce benefit if not replaceable, since we will insert + a move-insn to replace the insn that calculates this giv. + Don't do this unless the giv is a user variable, since it + will often be marked non-replaceable because of the duplication + of the exit code outside the loop. In such a case, the copies + we insert are dead and will be deleted. So they don't have + a cost. Similar situations exist. */ + /* ??? The new final_[bg]iv_value code does a much better job + of finding replaceable giv's, and hence this code may no longer + be necessary. */ + if (! v->replaceable && ! bl->eliminable + && REG_USERVAR_P (v->dest_reg)) + benefit -= copy_cost; + + /* Decrease the benefit to count the add-insns that we will + insert to increment the reduced reg for the giv. */ + benefit -= add_cost * bl->biv_count; + + /* Decide whether to strength-reduce this giv or to leave the code + unchanged (recompute it from the biv each time it is used). + This decision can be made independently for each giv. */ + + /* ??? Perhaps attempt to guess whether autoincrement will handle + some of the new add insns; if so, can increase BENEFIT + (undo the subtraction of add_cost that was done above). */ + + /* If an insn is not to be strength reduced, then set its ignore + flag, and clear all_reduced. */ + + if (v->lifetime * threshold * benefit < insn_count) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "giv of insn %d not worth while, %d vs %d.\n", + INSN_UID (v->insn), + v->lifetime * threshold * benefit, insn_count); + v->ignore = 1; + all_reduced = 0; + } + else + { + /* Check that we can increment the reduced giv without a + multiply insn. If not, reject it. */ + + for (tv = bl->biv; tv; tv = tv->next_iv) + if (tv->mult_val == const1_rtx + && ! product_cheap_p (tv->add_val, v->mult_val)) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "giv of insn %d: would need a multiply.\n", + INSN_UID (v->insn)); + v->ignore = 1; + all_reduced = 0; + break; + } + } + } + + /* Reduce each giv that we decided to reduce. */ + + for (v = bl->giv; v; v = v->next_iv) + { + struct induction *tv; + if (! v->ignore && v->same == 0) + { + v->new_reg = gen_reg_rtx (v->mode); + + /* For each place where the biv is incremented, + add an insn to increment the new, reduced reg for the giv. */ + for (tv = bl->biv; tv; tv = tv->next_iv) + { + if (tv->mult_val == const1_rtx) + emit_iv_add_mult (tv->add_val, v->mult_val, + v->new_reg, v->new_reg, tv->insn); + else /* tv->mult_val == const0_rtx */ + /* A multiply is acceptable here + since this is presumed to be seldom executed. */ + emit_iv_add_mult (tv->add_val, v->mult_val, + v->add_val, v->new_reg, tv->insn); + } + + /* Add code at loop start to initialize giv's reduced reg. */ + + emit_iv_add_mult (bl->initial_value, v->mult_val, + v->add_val, v->new_reg, loop_start); + } + } + + /* Rescan all givs. If a giv is the same as a giv not reduced, mark it + as not reduced. + + For each giv register that can be reduced now: if replaceable, + substitute reduced reg wherever the old giv occurs; + else add new move insn "giv_reg = reduced_reg". + + Also check for givs whose first use is their definition and whose + last use is the definition of another giv. If so, it is likely + dead and should not be used to eliminate a biv. */ + for (v = bl->giv; v; v = v->next_iv) + { + if (v->same && v->same->ignore) + v->ignore = 1; + + if (v->ignore) + continue; + + if (v->giv_type == DEST_REG + && regno_first_uid[REGNO (v->dest_reg)] == INSN_UID (v->insn)) + { + struct induction *v1; + + for (v1 = bl->giv; v1; v1 = v1->next_iv) + if (regno_last_uid[REGNO (v->dest_reg)] == INSN_UID (v1->insn)) + v->maybe_dead = 1; + } + + /* Update expression if this was combined, in case other giv was + replaced. */ + if (v->same) + v->new_reg = replace_rtx (v->new_reg, + v->same->dest_reg, v->same->new_reg); + + if (v->giv_type == DEST_ADDR) + /* Store reduced reg as the address in the memref where we found + this giv. */ + *v->location = v->new_reg; + else if (v->replaceable) + { + reg_map[REGNO (v->dest_reg)] = v->new_reg; + +#if 0 + /* I can no longer duplicate the original problem. Perhaps + this is unnecessary now? */ + + /* Replaceable; it isn't strictly necessary to delete the old + insn and emit a new one, because v->dest_reg is now dead. + + However, especially when unrolling loops, the special + handling for (set REG0 REG1) in the second cse pass may + make v->dest_reg live again. To avoid this problem, emit + an insn to set the original giv reg from the reduced giv. + We can not delete the original insn, since it may be part + of a LIBCALL, and the code in flow that eliminates dead + libcalls will fail if it is deleted. */ + emit_insn_after (gen_move_insn (v->dest_reg, v->new_reg), + v->insn); +#endif + } + else + { + /* Not replaceable; emit an insn to set the original giv reg from + the reduced giv, same as above. */ + emit_insn_after (gen_move_insn (v->dest_reg, v->new_reg), + v->insn); + } + + /* When a loop is reversed, givs which depend on the reversed + biv, and which are live outside the loop, must be set to their + correct final value. This insn is only needed if the giv is + not replaceable. The correct final value is the same as the + value that the giv starts the reversed loop with. */ + if (bl->reversed && ! v->replaceable) + emit_iv_add_mult (bl->initial_value, v->mult_val, + v->add_val, v->dest_reg, end_insert_before); + else if (v->final_value) + { + rtx insert_before; + + /* If the loop has multiple exits, emit the insn before the + loop to ensure that it will always be executed no matter + how the loop exits. Otherwise, emit the insn after the loop, + since this is slightly more efficient. */ + if (loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]]) + insert_before = loop_start; + else + insert_before = end_insert_before; + emit_insn_before (gen_move_insn (v->dest_reg, v->final_value), + insert_before); + +#if 0 + /* If the insn to set the final value of the giv was emitted + before the loop, then we must delete the insn inside the loop + that sets it. If this is a LIBCALL, then we must delete + every insn in the libcall. Note, however, that + final_giv_value will only succeed when there are multiple + exits if the giv is dead at each exit, hence it does not + matter that the original insn remains because it is dead + anyways. */ + /* Delete the insn inside the loop that sets the giv since + the giv is now set before (or after) the loop. */ + delete_insn (v->insn); +#endif + } + + if (loop_dump_stream) + { + fprintf (loop_dump_stream, "giv at %d reduced to ", + INSN_UID (v->insn)); + print_rtl (loop_dump_stream, v->new_reg); + fprintf (loop_dump_stream, "\n"); + } + } + + /* All the givs based on the biv bl have been reduced if they + merit it. */ + + /* For each giv not marked as maybe dead that has been combined with a + second giv, clear any "maybe dead" mark on that second giv. + v->new_reg will either be or refer to the register of the giv it + combined with. + + Doing this clearing avoids problems in biv elimination where a + giv's new_reg is a complex value that can't be put in the insn but + the giv combined with (with a reg as new_reg) is marked maybe_dead. + Since the register will be used in either case, we'd prefer it be + used from the simpler giv. */ + + for (v = bl->giv; v; v = v->next_iv) + if (! v->maybe_dead && v->same) + v->same->maybe_dead = 0; + + /* Try to eliminate the biv, if it is a candidate. + This won't work if ! all_reduced, + since the givs we planned to use might not have been reduced. + + We have to be careful that we didn't initially think we could eliminate + this biv because of a giv that we now think may be dead and shouldn't + be used as a biv replacement. + + Also, there is the possibility that we may have a giv that looks + like it can be used to eliminate a biv, but the resulting insn + isn't valid. This can happen, for example, on the 88k, where a + JUMP_INSN can compare a register only with zero. Attempts to + replace it with a compare with a constant will fail. + + Note that in cases where this call fails, we may have replaced some + of the occurrences of the biv with a giv, but no harm was done in + doing so in the rare cases where it can occur. */ + + if (all_reduced == 1 && bl->eliminable + && maybe_eliminate_biv (bl, loop_start, end, 1, + threshold, insn_count)) + + { + /* ?? If we created a new test to bypass the loop entirely, + or otherwise drop straight in, based on this test, then + we might want to rewrite it also. This way some later + pass has more hope of removing the initialization of this + biv entirely. */ + + /* If final_value != 0, then the biv may be used after loop end + and we must emit an insn to set it just in case. + + Reversed bivs already have an insn after the loop setting their + value, so we don't need another one. We can't calculate the + proper final value for such a biv here anyways. */ + if (final_value != 0 && ! bl->reversed) + { + rtx insert_before; + + /* If the loop has multiple exits, emit the insn before the + loop to ensure that it will always be executed no matter + how the loop exits. Otherwise, emit the insn after the + loop, since this is slightly more efficient. */ + if (loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]]) + insert_before = loop_start; + else + insert_before = end_insert_before; + + emit_insn_before (gen_move_insn (bl->biv->dest_reg, final_value), + end_insert_before); + } + +#if 0 + /* Delete all of the instructions inside the loop which set + the biv, as they are all dead. If is safe to delete them, + because an insn setting a biv will never be part of a libcall. */ + /* However, deleting them will invalidate the regno_last_uid info, + so keeping them around is more convenient. Final_biv_value + will only succeed when there are multiple exits if the biv + is dead at each exit, hence it does not matter that the original + insn remains, because it is dead anyways. */ + for (v = bl->biv; v; v = v->next_iv) + delete_insn (v->insn); +#endif + + if (loop_dump_stream) + fprintf (loop_dump_stream, "Reg %d: biv eliminated\n", + bl->regno); + } + } + + /* Go through all the instructions in the loop, making all the + register substitutions scheduled in REG_MAP. */ + + for (p = loop_start; p != end; p = NEXT_INSN (p)) + if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN + || GET_CODE (p) == CALL_INSN) + { + replace_regs (PATTERN (p), reg_map, max_reg_before_loop, 0); + replace_regs (REG_NOTES (p), reg_map, max_reg_before_loop, 0); + INSN_CODE (p) = -1; + } + + /* Unroll loops from within strength reduction so that we can use the + induction variable information that strength_reduce has already + collected. */ + + if (flag_unroll_loops) + unroll_loop (loop_end, insn_count, loop_start, end_insert_before, 1); + + if (loop_dump_stream) + fprintf (loop_dump_stream, "\n"); +} + +/* Return 1 if X is a valid source for an initial value (or as value being + compared against in an initial test). + + X must be either a register or constant and must not be clobbered between + the current insn and the start of the loop. + + INSN is the insn containing X. */ + +static int +valid_initial_value_p (x, insn, call_seen, loop_start) + rtx x; + rtx insn; + int call_seen; + rtx loop_start; +{ + if (CONSTANT_P (x)) + return 1; + + /* Only consider pseudos we know about initialized in insns whose luids + we know. */ + if (GET_CODE (x) != REG + || REGNO (x) >= max_reg_before_loop) + return 0; + + /* Don't use call-clobbered registers across a call which clobbers it. On + some machines, don't use any hard registers at all. */ + if (REGNO (x) < FIRST_PSEUDO_REGISTER +#ifndef SMALL_REGISTER_CLASSES + && call_used_regs[REGNO (x)] && call_seen +#endif + ) + return 0; + + /* Don't use registers that have been clobbered before the start of the + loop. */ + if (reg_set_between_p (x, insn, loop_start)) + return 0; + + return 1; +} + +/* Scan X for memory refs and check each memory address + as a possible giv. INSN is the insn whose pattern X comes from. + NOT_EVERY_ITERATION is 1 if the insn might not be executed during + every loop iteration. */ + +static void +find_mem_givs (x, insn, not_every_iteration, loop_start, loop_end) + rtx x; + rtx insn; + int not_every_iteration; + rtx loop_start, loop_end; +{ + register int i, j; + register enum rtx_code code; + register char *fmt; + + if (x == 0) + return; + + code = GET_CODE (x); + switch (code) + { + case REG: + case CONST_INT: + case CONST: + case CONST_DOUBLE: + case SYMBOL_REF: + case LABEL_REF: + case PC: + case CC0: + case ADDR_VEC: + case ADDR_DIFF_VEC: + case USE: + case CLOBBER: + return; + + case MEM: + { + rtx src_reg; + rtx add_val; + rtx mult_val; + int benefit; + + benefit = general_induction_var (XEXP (x, 0), + &src_reg, &add_val, &mult_val); + + /* Don't make a DEST_ADDR giv with mult_val == 1 && add_val == 0. + Such a giv isn't useful. */ + if (benefit > 0 && (mult_val != const1_rtx || add_val != const0_rtx)) + { + /* Found one; record it. */ + struct induction *v + = (struct induction *) oballoc (sizeof (struct induction)); + + record_giv (v, insn, src_reg, addr_placeholder, mult_val, + add_val, benefit, DEST_ADDR, not_every_iteration, + &XEXP (x, 0), loop_start, loop_end); + + v->mem_mode = GET_MODE (x); + } + return; + } + } + + /* Recursively scan the subexpressions for other mem refs. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + find_mem_givs (XEXP (x, i), insn, not_every_iteration, loop_start, + loop_end); + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + find_mem_givs (XVECEXP (x, i, j), insn, not_every_iteration, + loop_start, loop_end); +} + +/* Fill in the data about one biv update. + V is the `struct induction' in which we record the biv. (It is + allocated by the caller, with alloca.) + INSN is the insn that sets it. + DEST_REG is the biv's reg. + + MULT_VAL is const1_rtx if the biv is being incremented here, in which case + INC_VAL is the increment. Otherwise, MULT_VAL is const0_rtx and the biv is + being set to INC_VAL. + + NOT_EVERY_ITERATION is nonzero if this biv update is not know to be + executed every iteration; MAYBE_MULTIPLE is nonzero if this biv update + can be executed more than once per iteration. If MAYBE_MULTIPLE + and NOT_EVERY_ITERATION are both zero, we know that the biv update is + executed exactly once per iteration. */ + +static void +record_biv (v, insn, dest_reg, inc_val, mult_val, + not_every_iteration, maybe_multiple) + struct induction *v; + rtx insn; + rtx dest_reg; + rtx inc_val; + rtx mult_val; + int not_every_iteration; + int maybe_multiple; +{ + struct iv_class *bl; + + v->insn = insn; + v->src_reg = dest_reg; + v->dest_reg = dest_reg; + v->mult_val = mult_val; + v->add_val = inc_val; + v->mode = GET_MODE (dest_reg); + v->always_computable = ! not_every_iteration; + v->maybe_multiple = maybe_multiple; + + /* Add this to the reg's iv_class, creating a class + if this is the first incrementation of the reg. */ + + bl = reg_biv_class[REGNO (dest_reg)]; + if (bl == 0) + { + /* Create and initialize new iv_class. */ + + bl = (struct iv_class *) oballoc (sizeof (struct iv_class)); + + bl->regno = REGNO (dest_reg); + bl->biv = 0; + bl->giv = 0; + bl->biv_count = 0; + bl->giv_count = 0; + + /* Set initial value to the reg itself. */ + bl->initial_value = dest_reg; + /* We haven't seen the initializing insn yet */ + bl->init_insn = 0; + bl->init_set = 0; + bl->initial_test = 0; + bl->incremented = 0; + bl->eliminable = 0; + bl->nonneg = 0; + bl->reversed = 0; + bl->total_benefit = 0; + + /* Add this class to loop_iv_list. */ + bl->next = loop_iv_list; + loop_iv_list = bl; + + /* Put it in the array of biv register classes. */ + reg_biv_class[REGNO (dest_reg)] = bl; + } + + /* Update IV_CLASS entry for this biv. */ + v->next_iv = bl->biv; + bl->biv = v; + bl->biv_count++; + if (mult_val == const1_rtx) + bl->incremented = 1; + + if (loop_dump_stream) + { + fprintf (loop_dump_stream, + "Insn %d: possible biv, reg %d,", + INSN_UID (insn), REGNO (dest_reg)); + if (GET_CODE (inc_val) == CONST_INT) + fprintf (loop_dump_stream, " const = %d\n", + INTVAL (inc_val)); + else + { + fprintf (loop_dump_stream, " const = "); + print_rtl (loop_dump_stream, inc_val); + fprintf (loop_dump_stream, "\n"); + } + } +} + +/* Fill in the data about one giv. + V is the `struct induction' in which we record the giv. (It is + allocated by the caller, with alloca.) + INSN is the insn that sets it. + BENEFIT estimates the savings from deleting this insn. + TYPE is DEST_REG or DEST_ADDR; it says whether the giv is computed + into a register or is used as a memory address. + + SRC_REG is the biv reg which the giv is computed from. + DEST_REG is the giv's reg (if the giv is stored in a reg). + MULT_VAL and ADD_VAL are the coefficients used to compute the giv. + LOCATION points to the place where this giv's value appears in INSN. */ + +static void +record_giv (v, insn, src_reg, dest_reg, mult_val, add_val, benefit, + type, not_every_iteration, location, loop_start, loop_end) + struct induction *v; + rtx insn; + rtx src_reg; + rtx dest_reg; + rtx mult_val, add_val; + int benefit; + enum g_types type; + int not_every_iteration; + rtx *location; + rtx loop_start, loop_end; +{ + struct induction *b; + struct iv_class *bl; + rtx set = single_set (insn); + rtx p; + + v->insn = insn; + v->src_reg = src_reg; + v->giv_type = type; + v->dest_reg = dest_reg; + v->mult_val = mult_val; + v->add_val = add_val; + v->benefit = benefit; + v->location = location; + v->cant_derive = 0; + v->combined_with = 0; + v->maybe_multiple = 0; + v->maybe_dead = 0; + v->derive_adjustment = 0; + v->same = 0; + v->ignore = 0; + v->new_reg = 0; + v->final_value = 0; + + /* The v->always_computable field is used in update_giv_derive, to + determine whether a giv can be used to derive another giv. For a + DEST_REG giv, INSN computes a new value for the giv, so its value + isn't computable if INSN insn't executed every iteration. + However, for a DEST_ADDR giv, INSN merely uses the value of the giv; + it does not compute a new value. Hence the value is always computable + regardless of whether INSN is executed each iteration. */ + + if (type == DEST_ADDR) + v->always_computable = 1; + else + v->always_computable = ! not_every_iteration; + + if (type == DEST_ADDR) + { + v->mode = GET_MODE (*location); + v->lifetime = 1; + v->times_used = 1; + } + else /* type == DEST_REG */ + { + v->mode = GET_MODE (SET_DEST (set)); + + v->lifetime = (uid_luid[regno_last_uid[REGNO (dest_reg)]] + - uid_luid[regno_first_uid[REGNO (dest_reg)]]); + + v->times_used = n_times_used[REGNO (dest_reg)]; + + /* If the lifetime is zero, it means that this register is + really a dead store. So mark this as a giv that can be + ignored. This will not prevent the biv from being eliminated. */ + if (v->lifetime == 0) + v->ignore = 1; + + reg_iv_type[REGNO (dest_reg)] = GENERAL_INDUCT; + reg_iv_info[REGNO (dest_reg)] = v; + } + + /* Add the giv to the class of givs computed from one biv. */ + + bl = reg_biv_class[REGNO (src_reg)]; + if (bl) + { + v->next_iv = bl->giv; + bl->giv = v; + /* Don't count DEST_ADDR. This is supposed to count the number of + insns that calculate givs. */ + if (type == DEST_REG) + bl->giv_count++; + bl->total_benefit += benefit; + } + else + /* Fatal error, biv missing for this giv? */ + abort (); + + if (type == DEST_ADDR) + v->replaceable = 1; + else + { + /* The giv can be replaced outright by the reduced register only if all + of the following conditions are true: + - the insn that sets the giv is always executed on any iteration + on which the giv is used at all + (there are two ways to deduce this: + either the insn is executed on every iteration, + or all uses follow that insn in the same basic block), + - the giv is not used outside the loop + - no assignments to the biv occur during the giv's lifetime. */ + + if (regno_first_uid[REGNO (dest_reg)] == INSN_UID (insn) + /* Previous line always fails if INSN was moved by loop opt. */ + && uid_luid[regno_last_uid[REGNO (dest_reg)]] < INSN_LUID (loop_end) + && (! not_every_iteration + || last_use_this_basic_block (dest_reg, insn))) + { + /* Now check that there are no assignments to the biv within the + giv's lifetime. This requires two separate checks. */ + + /* Check each biv update, and fail if any are between the first + and last use of the giv. + + If this loop contains an inner loop that was unrolled, then + the insn modifying the biv may have been emitted by the loop + unrolling code, and hence does not have a valid luid. Just + mark the biv as not replaceable in this case. It is not very + useful as a biv, because it is used in two different loops. + It is very unlikely that we would be able to optimize the giv + using this biv anyways. */ + + v->replaceable = 1; + for (b = bl->biv; b; b = b->next_iv) + { + if (INSN_UID (b->insn) >= max_uid_for_loop + || ((uid_luid[INSN_UID (b->insn)] + >= uid_luid[regno_first_uid[REGNO (dest_reg)]]) + && (uid_luid[INSN_UID (b->insn)] + <= uid_luid[regno_last_uid[REGNO (dest_reg)]]))) + { + v->replaceable = 0; + v->not_replaceable = 1; + break; + } + } + + /* Check each insn between the first and last use of the giv, + and fail if any of them are branches that jump to a named label + outside this range, but still inside the loop. This catches + cases of spaghetti code where the execution order of insns + is not linear, and hence the above test fails. For example, + in the following code, j is not replaceable: + for (i = 0; i < 100; ) { + L0: j = 4*i; goto L1; + L2: k = j; goto L3; + L1: i++; goto L2; + L3: ; } + printf ("k = %d\n", k); } + This test is conservative, but this test succeeds rarely enough + that it isn't a problem. See also check_final_value below. */ + + if (v->replaceable) + for (p = insn; + INSN_UID (p) >= max_uid_for_loop + || INSN_LUID (p) < uid_luid[regno_last_uid[REGNO (dest_reg)]]; + p = NEXT_INSN (p)) + { + if (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p) + && LABEL_NAME (JUMP_LABEL (p)) + && ((INSN_LUID (JUMP_LABEL (p)) > INSN_LUID (loop_start) + && (INSN_LUID (JUMP_LABEL (p)) + < uid_luid[regno_first_uid[REGNO (dest_reg)]])) + || (INSN_LUID (JUMP_LABEL (p)) < INSN_LUID (loop_end) + && (INSN_LUID (JUMP_LABEL (p)) + > uid_luid[regno_last_uid[REGNO (dest_reg)]])))) + { + v->replaceable = 0; + v->not_replaceable = 1; + + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Found branch outside giv lifetime.\n"); + + break; + } + } + } + else + { + /* May still be replaceable, we don't have enough info here to + decide. */ + v->replaceable = 0; + v->not_replaceable = 0; + } + } + + if (loop_dump_stream) + { + if (type == DEST_REG) + fprintf (loop_dump_stream, "Insn %d: giv reg %d", + INSN_UID (insn), REGNO (dest_reg)); + else + fprintf (loop_dump_stream, "Insn %d: dest address", + INSN_UID (insn)); + + fprintf (loop_dump_stream, " src reg %d benefit %d", + REGNO (src_reg), v->benefit); + fprintf (loop_dump_stream, " used %d lifetime %d", + v->times_used, v->lifetime); + + if (v->replaceable) + fprintf (loop_dump_stream, " replaceable"); + + if (GET_CODE (mult_val) == CONST_INT) + fprintf (loop_dump_stream, " mult %d", + INTVAL (mult_val)); + else + { + fprintf (loop_dump_stream, " mult "); + print_rtl (loop_dump_stream, mult_val); + } + + if (GET_CODE (add_val) == CONST_INT) + fprintf (loop_dump_stream, " add %d", + INTVAL (add_val)); + else + { + fprintf (loop_dump_stream, " add "); + print_rtl (loop_dump_stream, add_val); + } + } + + if (loop_dump_stream) + fprintf (loop_dump_stream, "\n"); + +} + + +/* All this does is determine whether a giv can be made replaceable because + its final value can be calculated. This code can not be part of record_giv + above, because final_giv_value requires that the number of loop iterations + be known, and that can not be accurately calculated until after all givs + have been identified. */ + +static void +check_final_value (v, loop_start, loop_end) + struct induction *v; + rtx loop_start, loop_end; +{ + struct iv_class *bl; + rtx final_value = 0; + rtx tem; + + bl = reg_biv_class[REGNO (v->src_reg)]; + + /* DEST_ADDR givs will never reach here, because they are always marked + replaceable above in record_giv. */ + + /* The giv can be replaced outright by the reduced register only if all + of the following conditions are true: + - the insn that sets the giv is always executed on any iteration + on which the giv is used at all + (there are two ways to deduce this: + either the insn is executed on every iteration, + or all uses follow that insn in the same basic block), + - its final value can be calculated (this condition is different + than the one above in record_giv) + - no assignments to the biv occur during the giv's lifetime. */ + +#if 0 + /* This is only called now when replaceable is known to be false. */ + /* Clear replaceable, so that it won't confuse final_giv_value. */ + v->replaceable = 0; +#endif + + if ((final_value = final_giv_value (v, loop_start, loop_end)) + && (v->always_computable || last_use_this_basic_block (v->dest_reg, v->insn))) + { + int biv_increment_seen = 0; + rtx p = v->insn; + rtx last_giv_use; + + v->replaceable = 1; + + /* When trying to determine whether or not a biv increment occurs + during the lifetime of the giv, we can ignore uses of the variable + outside the loop because final_value is true. Hence we can not + use regno_last_uid and regno_first_uid as above in record_giv. */ + + /* Search the loop to determine whether any assignments to the + biv occur during the giv's lifetime. Start with the insn + that sets the giv, and search around the loop until we come + back to that insn again. + + Also fail if there is a jump within the giv's lifetime that jumps + to somewhere outside the lifetime but still within the loop. This + catches spaghetti code where the execution order is not linear, and + hence the above test fails. Here we assume that the giv lifetime + does not extend from one iteration of the loop to the next, so as + to make the test easier. Since the lifetime isn't known yet, + this requires two loops. See also record_giv above. */ + + last_giv_use = v->insn; + + while (1) + { + p = NEXT_INSN (p); + if (p == loop_end) + p = NEXT_INSN (loop_start); + if (p == v->insn) + break; + + if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN + || GET_CODE (p) == CALL_INSN) + { + if (biv_increment_seen) + { + if (reg_mentioned_p (v->dest_reg, PATTERN (p))) + { + v->replaceable = 0; + v->not_replaceable = 1; + break; + } + } + else if (GET_CODE (PATTERN (p)) == SET + && SET_DEST (PATTERN (p)) == v->src_reg) + biv_increment_seen = 1; + else if (reg_mentioned_p (v->dest_reg, PATTERN (p))) + last_giv_use = p; + } + } + + /* Now that the lifetime of the giv is known, check for branches + from within the lifetime to outside the lifetime if it is still + replaceable. */ + + if (v->replaceable) + { + p = v->insn; + while (1) + { + p = NEXT_INSN (p); + if (p == loop_end) + p = NEXT_INSN (loop_start); + if (p == last_giv_use) + break; + + if (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p) + && LABEL_NAME (JUMP_LABEL (p)) + && ((INSN_LUID (JUMP_LABEL (p)) < INSN_LUID (v->insn) + && INSN_LUID (JUMP_LABEL (p)) > INSN_LUID (loop_start)) + || (INSN_LUID (JUMP_LABEL (p)) > INSN_LUID (last_giv_use) + && INSN_LUID (JUMP_LABEL (p)) < INSN_LUID (loop_end)))) + { + v->replaceable = 0; + v->not_replaceable = 1; + + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Found branch outside giv lifetime.\n"); + + break; + } + } + } + + /* If it is replaceable, then save the final value. */ + if (v->replaceable) + v->final_value = final_value; + } + + if (loop_dump_stream && v->replaceable) + fprintf (loop_dump_stream, "Insn %d: giv reg %d final_value replaceable\n", + INSN_UID (v->insn), REGNO (v->dest_reg)); +} + +/* Update the status of whether a giv can derive other givs. + + We need to do something special if there is or may be an update to the biv + between the time the giv is defined and the time it is used to derive + another giv. + + In addition, a giv that is only conditionally set is not allowed to + derive another giv once a label has been passed. + + The cases we look at are when a label or an update to a biv is passed. */ + +static void +update_giv_derive (p) + rtx p; +{ + struct iv_class *bl; + struct induction *biv, *giv; + rtx tem; + int dummy; + + /* Search all IV classes, then all bivs, and finally all givs. + + There are three cases we are concerned with. First we have the situation + of a giv that is only updated conditionally. In that case, it may not + derive any givs after a label is passed. + + The second case is when a biv update occurs, or may occur, after the + definition of a giv. For certain biv updates (see below) that are + known to occur between the giv definition and use, we can adjust the + giv definition. For others, or when the biv update is conditional, + we must prevent the giv from deriving any other givs. There are two + sub-cases within this case. + + If this is a label, we are concerned with any biv update that is done + conditionally, since it may be done after the giv is defined followed by + a branch here (actually, we need to pass both a jump and a label, but + this extra tracking doesn't seem worth it). + + If this is a jump, we are concerned about any biv update that may be + executed multiple times. We are actually only concerned about + backward jumps, but it is probably not worth performing the test + on the jump again here. + + If this is a biv update, we must adjust the giv status to show that a + subsequent biv update was performed. If this adjustment cannot be done, + the giv cannot derive further givs. */ + + for (bl = loop_iv_list; bl; bl = bl->next) + for (biv = bl->biv; biv; biv = biv->next_iv) + if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN + || biv->insn == p) + { + for (giv = bl->giv; giv; giv = giv->next_iv) + { + /* If cant_derive is already true, there is no point in + checking all of these conditions again. */ + if (giv->cant_derive) + continue; + + /* If this giv is conditionally set and we have passed a label, + it cannot derive anything. */ + if (GET_CODE (p) == CODE_LABEL && ! giv->always_computable) + giv->cant_derive = 1; + + /* Skip givs that have mult_val == 0, since + they are really invariants. Also skip those that are + replaceable, since we know their lifetime doesn't contain + any biv update. */ + else if (giv->mult_val == const0_rtx || giv->replaceable) + continue; + + /* The only way we can allow this giv to derive another + is if this is a biv increment and we can form the product + of biv->add_val and giv->mult_val. In this case, we will + be able to compute a compensation. */ + else if (biv->insn == p) + { + tem = 0; + + if (biv->mult_val == const1_rtx) + tem = simplify_giv_expr (gen_rtx (MULT, giv->mode, + biv->add_val, + giv->mult_val), + &dummy); + + if (tem && giv->derive_adjustment) + tem = simplify_giv_expr (gen_rtx (PLUS, giv->mode, tem, + giv->derive_adjustment), + &dummy); + if (tem) + giv->derive_adjustment = tem; + else + giv->cant_derive = 1; + } + else if ((GET_CODE (p) == CODE_LABEL && ! biv->always_computable) + || (GET_CODE (p) == JUMP_INSN && biv->maybe_multiple)) + giv->cant_derive = 1; + } + } +} + +/* Check whether an insn is an increment legitimate for a basic induction var. + X is the source of insn P. + DEST_REG is the putative biv, also the destination of the insn. + We accept patterns of these forms: + REG = REG + INVARIANT (includes REG = REG - CONSTANT) + REG = INVARIANT + REG + + If X is suitable, we return 1, set *MULT_VAL to CONST1_RTX, + and store the additive term into *INC_VAL. + + If X is an assignment of an invariant into DEST_REG, we set + *MULT_VAL to CONST0_RTX, and store the invariant into *INC_VAL. + + We also want to detect a BIV when it corresponds to a variable + whose mode was promoted via PROMOTED_MODE. In that case, an increment + of the variable may be a PLUS that adds a SUBREG of that variable to + an invariant and then sign- or zero-extends the result of the PLUS + into the variable. + + Most GIVs in such cases will be in the promoted mode, since that is the + probably the natural computation mode (and almost certainly the mode + used for addresses) on the machine. So we view the pseudo-reg containing + the variable as the BIV, as if it were simply incremented. + + Note that treating the entire pseudo as a BIV will result in making + simple increments to any GIVs based on it. However, if the variable + overflows in its declared mode but not its promoted mode, the result will + be incorrect. This is acceptable if the variable is signed, since + overflows in such cases are undefined, but not if it is unsigned, since + those overflows are defined. So we only check for SIGN_EXTEND and + not ZERO_EXTEND. + + If we cannot find a biv, we return 0. */ + +static int +basic_induction_var (x, dest_reg, p, inc_val, mult_val) + register rtx x; + rtx p; + rtx dest_reg; + rtx *inc_val; + rtx *mult_val; +{ + register enum rtx_code code; + rtx arg; + rtx insn, set = 0; + + code = GET_CODE (x); + switch (code) + { + case PLUS: + if (XEXP (x, 0) == dest_reg + || (GET_CODE (XEXP (x, 0)) == SUBREG + && SUBREG_PROMOTED_VAR_P (XEXP (x, 0)) + && SUBREG_REG (XEXP (x, 0)) == dest_reg)) + arg = XEXP (x, 1); + else if (XEXP (x, 1) == dest_reg + || (GET_CODE (XEXP (x, 1)) == SUBREG + && SUBREG_PROMOTED_VAR_P (XEXP (x, 1)) + && SUBREG_REG (XEXP (x, 1)) == dest_reg)) + arg = XEXP (x, 0); + else + return 0; + + if (invariant_p (arg) != 1) + return 0; + + *inc_val = convert_to_mode (GET_MODE (dest_reg), arg, 0);; + *mult_val = const1_rtx; + return 1; + + case SUBREG: + /* If this is a SUBREG for a promoted variable, check the inner + value. */ + if (SUBREG_PROMOTED_VAR_P (x)) + return basic_induction_var (SUBREG_REG (x), dest_reg, p, + inc_val, mult_val); + + case REG: + /* If this register is assigned in the previous insn, look at its + source, but don't go outside the loop or past a label. */ + + for (insn = PREV_INSN (p); + (insn && GET_CODE (insn) == NOTE + && NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG); + insn = PREV_INSN (insn)) + ; + + if (insn) + set = single_set (insn); + + if (set != 0 && SET_DEST (set) == x) + return basic_induction_var (SET_SRC (set), dest_reg, insn, + inc_val, mult_val); + /* ... fall through ... */ + + /* Can accept constant setting of biv only when inside inner most loop. + Otherwise, a biv of an inner loop may be incorrectly recognized + as a biv of the outer loop, + causing code to be moved INTO the inner loop. */ + case MEM: + if (invariant_p (x) != 1) + return 0; + case CONST_INT: + case SYMBOL_REF: + case CONST: + if (loops_enclosed == 1) + { + *inc_val = convert_to_mode (GET_MODE (dest_reg), x, 0);; + *mult_val = const0_rtx; + return 1; + } + else + return 0; + + case SIGN_EXTEND: + return basic_induction_var (XEXP (x, 0), dest_reg, p, + inc_val, mult_val); + case ASHIFTRT: + /* Similar, since this can be a sign extension. */ + for (insn = PREV_INSN (p); + (insn && GET_CODE (insn) == NOTE + && NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG); + insn = PREV_INSN (insn)) + ; + + if (insn) + set = single_set (insn); + + if (set && SET_DEST (set) == XEXP (x, 0) + && GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) >= 0 + && GET_CODE (SET_SRC (set)) == ASHIFT + && XEXP (x, 1) == XEXP (SET_SRC (set), 1)) + return basic_induction_var (XEXP (SET_SRC (set), 0), dest_reg, insn, + inc_val, mult_val); + return 0; + + default: + return 0; + } +} + +/* A general induction variable (giv) is any quantity that is a linear + function of a basic induction variable, + i.e. giv = biv * mult_val + add_val. + The coefficients can be any loop invariant quantity. + A giv need not be computed directly from the biv; + it can be computed by way of other givs. */ + +/* Determine whether X computes a giv. + If it does, return a nonzero value + which is the benefit from eliminating the computation of X; + set *SRC_REG to the register of the biv that it is computed from; + set *ADD_VAL and *MULT_VAL to the coefficients, + such that the value of X is biv * mult + add; */ + +static int +general_induction_var (x, src_reg, add_val, mult_val) + rtx x; + rtx *src_reg; + rtx *add_val; + rtx *mult_val; +{ + rtx orig_x = x; + int benefit = 0; + char *storage; + + /* If this is an invariant, forget it, it isn't a giv. */ + if (invariant_p (x) == 1) + return 0; + + /* See if the expression could be a giv and get its form. + Mark our place on the obstack in case we don't find a giv. */ + storage = (char *) oballoc (0); + x = simplify_giv_expr (x, &benefit); + if (x == 0) + { + obfree (storage); + return 0; + } + + switch (GET_CODE (x)) + { + case USE: + case CONST_INT: + /* Since this is now an invariant and wasn't before, it must be a giv + with MULT_VAL == 0. It doesn't matter which BIV we associate this + with. */ + *src_reg = loop_iv_list->biv->dest_reg; + *mult_val = const0_rtx; + *add_val = x; + break; + + case REG: + /* This is equivalent to a BIV. */ + *src_reg = x; + *mult_val = const1_rtx; + *add_val = const0_rtx; + break; + + case PLUS: + /* Either (plus (biv) (invar)) or + (plus (mult (biv) (invar_1)) (invar_2)). */ + if (GET_CODE (XEXP (x, 0)) == MULT) + { + *src_reg = XEXP (XEXP (x, 0), 0); + *mult_val = XEXP (XEXP (x, 0), 1); + } + else + { + *src_reg = XEXP (x, 0); + *mult_val = const1_rtx; + } + *add_val = XEXP (x, 1); + break; + + case MULT: + /* ADD_VAL is zero. */ + *src_reg = XEXP (x, 0); + *mult_val = XEXP (x, 1); + *add_val = const0_rtx; + break; + + default: + abort (); + } + + /* Remove any enclosing USE from ADD_VAL and MULT_VAL (there will be + unless they are CONST_INT). */ + if (GET_CODE (*add_val) == USE) + *add_val = XEXP (*add_val, 0); + if (GET_CODE (*mult_val) == USE) + *mult_val = XEXP (*mult_val, 0); + + benefit += rtx_cost (orig_x, SET); + + /* Always return some benefit if this is a giv so it will be detected + as such. This allows elimination of bivs that might otherwise + not be eliminated. */ + return benefit == 0 ? 1 : benefit; +} + +/* Given an expression, X, try to form it as a linear function of a biv. + We will canonicalize it to be of the form + (plus (mult (BIV) (invar_1)) + (invar_2)) + with possible degeneracies. + + The invariant expressions must each be of a form that can be used as a + machine operand. We surround then with a USE rtx (a hack, but localized + and certainly unambiguous!) if not a CONST_INT for simplicity in this + routine; it is the caller's responsibility to strip them. + + If no such canonicalization is possible (i.e., two biv's are used or an + expression that is neither invariant nor a biv or giv), this routine + returns 0. + + For a non-zero return, the result will have a code of CONST_INT, USE, + REG (for a BIV), PLUS, or MULT. No other codes will occur. + + *BENEFIT will be incremented by the benefit of any sub-giv encountered. */ + +static rtx +simplify_giv_expr (x, benefit) + rtx x; + int *benefit; +{ + enum machine_mode mode = GET_MODE (x); + rtx arg0, arg1; + rtx tem; + + /* If this is not an integer mode, or if we cannot do arithmetic in this + mode, this can't be a giv. */ + if (mode != VOIDmode + && (GET_MODE_CLASS (mode) != MODE_INT + || GET_MODE_BITSIZE (mode) > HOST_BITS_PER_WIDE_INT)) + return 0; + + switch (GET_CODE (x)) + { + case PLUS: + arg0 = simplify_giv_expr (XEXP (x, 0), benefit); + arg1 = simplify_giv_expr (XEXP (x, 1), benefit); + if (arg0 == 0 || arg1 == 0) + return 0; + + /* Put constant last, CONST_INT last if both constant. */ + if ((GET_CODE (arg0) == USE + || GET_CODE (arg0) == CONST_INT) + && GET_CODE (arg1) != CONST_INT) + tem = arg0, arg0 = arg1, arg1 = tem; + + /* Handle addition of zero, then addition of an invariant. */ + if (arg1 == const0_rtx) + return arg0; + else if (GET_CODE (arg1) == CONST_INT || GET_CODE (arg1) == USE) + switch (GET_CODE (arg0)) + { + case CONST_INT: + case USE: + /* Both invariant. Only valid if sum is machine operand. + First strip off possible USE on first operand. */ + if (GET_CODE (arg0) == USE) + arg0 = XEXP (arg0, 0); + + tem = 0; + if (CONSTANT_P (arg0) && GET_CODE (arg1) == CONST_INT) + { + tem = plus_constant (arg0, INTVAL (arg1)); + if (GET_CODE (tem) != CONST_INT) + tem = gen_rtx (USE, mode, tem); + } + + return tem; + + case REG: + case MULT: + /* biv + invar or mult + invar. Return sum. */ + return gen_rtx (PLUS, mode, arg0, arg1); + + case PLUS: + /* (a + invar_1) + invar_2. Associate. */ + return simplify_giv_expr (gen_rtx (PLUS, mode, + XEXP (arg0, 0), + gen_rtx (PLUS, mode, + XEXP (arg0, 1), arg1)), + benefit); + + default: + abort (); + } + + /* Each argument must be either REG, PLUS, or MULT. Convert REG to + MULT to reduce cases. */ + if (GET_CODE (arg0) == REG) + arg0 = gen_rtx (MULT, mode, arg0, const1_rtx); + if (GET_CODE (arg1) == REG) + arg1 = gen_rtx (MULT, mode, arg1, const1_rtx); + + /* Now have PLUS + PLUS, PLUS + MULT, MULT + PLUS, or MULT + MULT. + Put a MULT first, leaving PLUS + PLUS, MULT + PLUS, or MULT + MULT. + Recurse to associate the second PLUS. */ + if (GET_CODE (arg1) == MULT) + tem = arg0, arg0 = arg1, arg1 = tem; + + if (GET_CODE (arg1) == PLUS) + return simplify_giv_expr (gen_rtx (PLUS, mode, + gen_rtx (PLUS, mode, + arg0, XEXP (arg1, 0)), + XEXP (arg1, 1)), + benefit); + + /* Now must have MULT + MULT. Distribute if same biv, else not giv. */ + if (GET_CODE (arg0) != MULT || GET_CODE (arg1) != MULT) + abort (); + + if (XEXP (arg0, 0) != XEXP (arg1, 0)) + return 0; + + return simplify_giv_expr (gen_rtx (MULT, mode, + XEXP (arg0, 0), + gen_rtx (PLUS, mode, + XEXP (arg0, 1), + XEXP (arg1, 1))), + benefit); + + case MINUS: + /* Handle "a - b" as "a + b * (-1)". */ + return simplify_giv_expr (gen_rtx (PLUS, mode, + XEXP (x, 0), + gen_rtx (MULT, mode, + XEXP (x, 1), constm1_rtx)), + benefit); + + case MULT: + arg0 = simplify_giv_expr (XEXP (x, 0), benefit); + arg1 = simplify_giv_expr (XEXP (x, 1), benefit); + if (arg0 == 0 || arg1 == 0) + return 0; + + /* Put constant last, CONST_INT last if both constant. */ + if ((GET_CODE (arg0) == USE || GET_CODE (arg0) == CONST_INT) + && GET_CODE (arg1) != CONST_INT) + tem = arg0, arg0 = arg1, arg1 = tem; + + /* If second argument is not now constant, not giv. */ + if (GET_CODE (arg1) != USE && GET_CODE (arg1) != CONST_INT) + return 0; + + /* Handle multiply by 0 or 1. */ + if (arg1 == const0_rtx) + return const0_rtx; + + else if (arg1 == const1_rtx) + return arg0; + + switch (GET_CODE (arg0)) + { + case REG: + /* biv * invar. Done. */ + return gen_rtx (MULT, mode, arg0, arg1); + + case CONST_INT: + /* Product of two constants. */ + return GEN_INT (INTVAL (arg0) * INTVAL (arg1)); + + case USE: + /* invar * invar. Not giv. */ + return 0; + + case MULT: + /* (a * invar_1) * invar_2. Associate. */ + return simplify_giv_expr (gen_rtx (MULT, mode, + XEXP (arg0, 0), + gen_rtx (MULT, mode, + XEXP (arg0, 1), arg1)), + benefit); + + case PLUS: + /* (a + invar_1) * invar_2. Distribute. */ + return simplify_giv_expr (gen_rtx (PLUS, mode, + gen_rtx (MULT, mode, + XEXP (arg0, 0), arg1), + gen_rtx (MULT, mode, + XEXP (arg0, 1), arg1)), + benefit); + + default: + abort (); + } + + case ASHIFT: + case LSHIFT: + /* Shift by constant is multiply by power of two. */ + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + return 0; + + return simplify_giv_expr (gen_rtx (MULT, mode, + XEXP (x, 0), + GEN_INT ((HOST_WIDE_INT) 1 + << INTVAL (XEXP (x, 1)))), + benefit); + + case NEG: + /* "-a" is "a * (-1)" */ + return simplify_giv_expr (gen_rtx (MULT, mode, XEXP (x, 0), constm1_rtx), + benefit); + + case NOT: + /* "~a" is "-a - 1". Silly, but easy. */ + return simplify_giv_expr (gen_rtx (MINUS, mode, + gen_rtx (NEG, mode, XEXP (x, 0)), + const1_rtx), + benefit); + + case USE: + /* Already in proper form for invariant. */ + return x; + + case REG: + /* If this is a new register, we can't deal with it. */ + if (REGNO (x) >= max_reg_before_loop) + return 0; + + /* Check for biv or giv. */ + switch (reg_iv_type[REGNO (x)]) + { + case BASIC_INDUCT: + return x; + case GENERAL_INDUCT: + { + struct induction *v = reg_iv_info[REGNO (x)]; + + /* Form expression from giv and add benefit. Ensure this giv + can derive another and subtract any needed adjustment if so. */ + *benefit += v->benefit; + if (v->cant_derive) + return 0; + + tem = gen_rtx (PLUS, mode, gen_rtx (MULT, mode, + v->src_reg, v->mult_val), + v->add_val); + if (v->derive_adjustment) + tem = gen_rtx (MINUS, mode, tem, v->derive_adjustment); + return simplify_giv_expr (tem, benefit); + } + } + + /* Fall through to general case. */ + default: + /* If invariant, return as USE (unless CONST_INT). + Otherwise, not giv. */ + if (GET_CODE (x) == USE) + x = XEXP (x, 0); + + if (invariant_p (x) == 1) + { + if (GET_CODE (x) == CONST_INT) + return x; + else + return gen_rtx (USE, mode, x); + } + else + return 0; + } +} + +/* Help detect a giv that is calculated by several consecutive insns; + for example, + giv = biv * M + giv = giv + A + The caller has already identified the first insn P as having a giv as dest; + we check that all other insns that set the same register follow + immediately after P, that they alter nothing else, + and that the result of the last is still a giv. + + The value is 0 if the reg set in P is not really a giv. + Otherwise, the value is the amount gained by eliminating + all the consecutive insns that compute the value. + + FIRST_BENEFIT is the amount gained by eliminating the first insn, P. + SRC_REG is the reg of the biv; DEST_REG is the reg of the giv. + + The coefficients of the ultimate giv value are stored in + *MULT_VAL and *ADD_VAL. */ + +static int +consec_sets_giv (first_benefit, p, src_reg, dest_reg, + add_val, mult_val) + int first_benefit; + rtx p; + rtx src_reg; + rtx dest_reg; + rtx *add_val; + rtx *mult_val; +{ + int count; + enum rtx_code code; + int benefit; + rtx temp; + rtx set; + + /* Indicate that this is a giv so that we can update the value produced in + each insn of the multi-insn sequence. + + This induction structure will be used only by the call to + general_induction_var below, so we can allocate it on our stack. + If this is a giv, our caller will replace the induct var entry with + a new induction structure. */ + struct induction *v + = (struct induction *) alloca (sizeof (struct induction)); + v->src_reg = src_reg; + v->mult_val = *mult_val; + v->add_val = *add_val; + v->benefit = first_benefit; + v->cant_derive = 0; + v->derive_adjustment = 0; + + reg_iv_type[REGNO (dest_reg)] = GENERAL_INDUCT; + reg_iv_info[REGNO (dest_reg)] = v; + + count = n_times_set[REGNO (dest_reg)] - 1; + + while (count > 0) + { + p = NEXT_INSN (p); + code = GET_CODE (p); + + /* If libcall, skip to end of call sequence. */ + if (code == INSN && (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX))) + p = XEXP (temp, 0); + + if (code == INSN + && (set = single_set (p)) + && GET_CODE (SET_DEST (set)) == REG + && SET_DEST (set) == dest_reg + && ((benefit = general_induction_var (SET_SRC (set), &src_reg, + add_val, mult_val)) + /* Giv created by equivalent expression. */ + || ((temp = find_reg_note (p, REG_EQUAL, NULL_RTX)) + && (benefit = general_induction_var (XEXP (temp, 0), &src_reg, + add_val, mult_val)))) + && src_reg == v->src_reg) + { + if (find_reg_note (p, REG_RETVAL, NULL_RTX)) + benefit += libcall_benefit (p); + + count--; + v->mult_val = *mult_val; + v->add_val = *add_val; + v->benefit = benefit; + } + else if (code != NOTE) + { + /* Allow insns that set something other than this giv to a + constant. Such insns are needed on machines which cannot + include long constants and should not disqualify a giv. */ + if (code == INSN + && (set = single_set (p)) + && SET_DEST (set) != dest_reg + && CONSTANT_P (SET_SRC (set))) + continue; + + reg_iv_type[REGNO (dest_reg)] = UNKNOWN_INDUCT; + return 0; + } + } + + return v->benefit; +} + +/* Return an rtx, if any, that expresses giv G2 as a function of the register + represented by G1. If no such expression can be found, or it is clear that + it cannot possibly be a valid address, 0 is returned. + + To perform the computation, we note that + G1 = a * v + b and + G2 = c * v + d + where `v' is the biv. + + So G2 = (c/a) * G1 + (d - b*c/a) */ + +#ifdef ADDRESS_COST +static rtx +express_from (g1, g2) + struct induction *g1, *g2; +{ + rtx mult, add; + + /* The value that G1 will be multiplied by must be a constant integer. Also, + the only chance we have of getting a valid address is if b*c/a (see above + for notation) is also an integer. */ + if (GET_CODE (g1->mult_val) != CONST_INT + || GET_CODE (g2->mult_val) != CONST_INT + || GET_CODE (g1->add_val) != CONST_INT + || g1->mult_val == const0_rtx + || INTVAL (g2->mult_val) % INTVAL (g1->mult_val) != 0) + return 0; + + mult = GEN_INT (INTVAL (g2->mult_val) / INTVAL (g1->mult_val)); + add = plus_constant (g2->add_val, - INTVAL (g1->add_val) * INTVAL (mult)); + + /* Form simplified final result. */ + if (mult == const0_rtx) + return add; + else if (mult == const1_rtx) + mult = g1->dest_reg; + else + mult = gen_rtx (MULT, g2->mode, g1->dest_reg, mult); + + if (add == const0_rtx) + return mult; + else + return gen_rtx (PLUS, g2->mode, mult, add); +} +#endif + +/* Return 1 if giv G2 can be combined with G1. This means that G2 can use + (either directly or via an address expression) a register used to represent + G1. Set g2->new_reg to a represtation of G1 (normally just + g1->dest_reg). */ + +static int +combine_givs_p (g1, g2) + struct induction *g1, *g2; +{ + rtx tem; + + /* If these givs are identical, they can be combined. */ + if (rtx_equal_p (g1->mult_val, g2->mult_val) + && rtx_equal_p (g1->add_val, g2->add_val)) + { + g2->new_reg = g1->dest_reg; + return 1; + } + +#ifdef ADDRESS_COST + /* If G2 can be expressed as a function of G1 and that function is valid + as an address and no more expensive than using a register for G2, + the expression of G2 in terms of G1 can be used. */ + if (g2->giv_type == DEST_ADDR + && (tem = express_from (g1, g2)) != 0 + && memory_address_p (g2->mem_mode, tem) + && ADDRESS_COST (tem) <= ADDRESS_COST (*g2->location)) + { + g2->new_reg = tem; + return 1; + } +#endif + + return 0; +} + +/* Check all pairs of givs for iv_class BL and see if any can be combined with + any other. If so, point SAME to the giv combined with and set NEW_REG to + be an expression (in terms of the other giv's DEST_REG) equivalent to the + giv. Also, update BENEFIT and related fields for cost/benefit analysis. */ + +static void +combine_givs (bl) + struct iv_class *bl; +{ + struct induction *g1, *g2; + int pass; + + for (g1 = bl->giv; g1; g1 = g1->next_iv) + for (pass = 0; pass <= 1; pass++) + for (g2 = bl->giv; g2; g2 = g2->next_iv) + if (g1 != g2 + /* First try to combine with replaceable givs, then all givs. */ + && (g1->replaceable || pass == 1) + /* If either has already been combined or is to be ignored, can't + combine. */ + && ! g1->ignore && ! g2->ignore && ! g1->same && ! g2->same + /* If something has been based on G2, G2 cannot itself be based + on something else. */ + && ! g2->combined_with + && combine_givs_p (g1, g2)) + { + /* g2->new_reg set by `combine_givs_p' */ + g2->same = g1; + g1->combined_with = 1; + g1->benefit += g2->benefit; + /* ??? The new final_[bg]iv_value code does a much better job + of finding replaceable giv's, and hence this code may no + longer be necessary. */ + if (! g2->replaceable && REG_USERVAR_P (g2->dest_reg)) + g1->benefit -= copy_cost; + g1->lifetime += g2->lifetime; + g1->times_used += g2->times_used; + + if (loop_dump_stream) + fprintf (loop_dump_stream, "giv at %d combined with giv at %d\n", + INSN_UID (g2->insn), INSN_UID (g1->insn)); + } +} + +/* EMIT code before INSERT_BEFORE to set REG = B * M + A. */ + +void +emit_iv_add_mult (b, m, a, reg, insert_before) + rtx b; /* initial value of basic induction variable */ + rtx m; /* multiplicative constant */ + rtx a; /* additive constant */ + rtx reg; /* destination register */ + rtx insert_before; +{ + rtx seq; + rtx result; + + /* Prevent unexpected sharing of these rtx. */ + a = copy_rtx (a); + b = copy_rtx (b); + + /* Increase the lifetime of any invariants moved further in code. */ + update_reg_last_use (a, insert_before); + update_reg_last_use (b, insert_before); + update_reg_last_use (m, insert_before); + + start_sequence (); + result = expand_mult_add (b, reg, m, a, GET_MODE (reg), 0); + if (reg != result) + emit_move_insn (reg, result); + seq = gen_sequence (); + end_sequence (); + + emit_insn_before (seq, insert_before); +} + +/* Test whether A * B can be computed without + an actual multiply insn. Value is 1 if so. */ + +static int +product_cheap_p (a, b) + rtx a; + rtx b; +{ + int i; + rtx tmp; + struct obstack *old_rtl_obstack = rtl_obstack; + char *storage = (char *) obstack_alloc (&temp_obstack, 0); + int win = 1; + + /* If only one is constant, make it B. */ + if (GET_CODE (a) == CONST_INT) + tmp = a, a = b, b = tmp; + + /* If first constant, both constant, so don't need multiply. */ + if (GET_CODE (a) == CONST_INT) + return 1; + + /* If second not constant, neither is constant, so would need multiply. */ + if (GET_CODE (b) != CONST_INT) + return 0; + + /* One operand is constant, so might not need multiply insn. Generate the + code for the multiply and see if a call or multiply, or long sequence + of insns is generated. */ + + rtl_obstack = &temp_obstack; + start_sequence (); + expand_mult (GET_MODE (a), a, b, NULL_RTX, 0); + tmp = gen_sequence (); + end_sequence (); + + if (GET_CODE (tmp) == SEQUENCE) + { + if (XVEC (tmp, 0) == 0) + win = 1; + else if (XVECLEN (tmp, 0) > 3) + win = 0; + else + for (i = 0; i < XVECLEN (tmp, 0); i++) + { + rtx insn = XVECEXP (tmp, 0, i); + + if (GET_CODE (insn) != INSN + || (GET_CODE (PATTERN (insn)) == SET + && GET_CODE (SET_SRC (PATTERN (insn))) == MULT) + || (GET_CODE (PATTERN (insn)) == PARALLEL + && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET + && GET_CODE (SET_SRC (XVECEXP (PATTERN (insn), 0, 0))) == MULT)) + { + win = 0; + break; + } + } + } + else if (GET_CODE (tmp) == SET + && GET_CODE (SET_SRC (tmp)) == MULT) + win = 0; + else if (GET_CODE (tmp) == PARALLEL + && GET_CODE (XVECEXP (tmp, 0, 0)) == SET + && GET_CODE (SET_SRC (XVECEXP (tmp, 0, 0))) == MULT) + win = 0; + + /* Free any storage we obtained in generating this multiply and restore rtl + allocation to its normal obstack. */ + obstack_free (&temp_obstack, storage); + rtl_obstack = old_rtl_obstack; + + return win; +} + +/* Check to see if loop can be terminated by a "decrement and branch until + zero" instruction. If so, add a REG_NONNEG note to the branch insn if so. + Also try reversing an increment loop to a decrement loop + to see if the optimization can be performed. + Value is nonzero if optimization was performed. */ + +/* This is useful even if the architecture doesn't have such an insn, + because it might change a loops which increments from 0 to n to a loop + which decrements from n to 0. A loop that decrements to zero is usually + faster than one that increments from zero. */ + +/* ??? This could be rewritten to use some of the loop unrolling procedures, + such as approx_final_value, biv_total_increment, loop_iterations, and + final_[bg]iv_value. */ + +static int +check_dbra_loop (loop_end, insn_count, loop_start) + rtx loop_end; + int insn_count; + rtx loop_start; +{ + struct iv_class *bl; + rtx reg; + rtx jump_label; + rtx final_value; + rtx start_value; + enum rtx_code branch_code; + rtx new_add_val; + rtx comparison; + rtx before_comparison; + rtx p; + + /* If last insn is a conditional branch, and the insn before tests a + register value, try to optimize it. Otherwise, we can't do anything. */ + + comparison = get_condition_for_loop (PREV_INSN (loop_end)); + if (comparison == 0) + return 0; + + /* Check all of the bivs to see if the compare uses one of them. + Skip biv's set more than once because we can't guarantee that + it will be zero on the last iteration. Also skip if the biv is + used between its update and the test insn. */ + + for (bl = loop_iv_list; bl; bl = bl->next) + { + if (bl->biv_count == 1 + && bl->biv->dest_reg == XEXP (comparison, 0) + && ! reg_used_between_p (regno_reg_rtx[bl->regno], bl->biv->insn, + PREV_INSN (PREV_INSN (loop_end)))) + break; + } + + if (! bl) + return 0; + + /* Look for the case where the basic induction variable is always + nonnegative, and equals zero on the last iteration. + In this case, add a reg_note REG_NONNEG, which allows the + m68k DBRA instruction to be used. */ + + if (((GET_CODE (comparison) == GT + && GET_CODE (XEXP (comparison, 1)) == CONST_INT + && INTVAL (XEXP (comparison, 1)) == -1) + || (GET_CODE (comparison) == NE && XEXP (comparison, 1) == const0_rtx)) + && GET_CODE (bl->biv->add_val) == CONST_INT + && INTVAL (bl->biv->add_val) < 0) + { + /* Initial value must be greater than 0, + init_val % -dec_value == 0 to ensure that it equals zero on + the last iteration */ + + if (GET_CODE (bl->initial_value) == CONST_INT + && INTVAL (bl->initial_value) > 0 + && (INTVAL (bl->initial_value) % + (-INTVAL (bl->biv->add_val))) == 0) + { + /* register always nonnegative, add REG_NOTE to branch */ + REG_NOTES (PREV_INSN (loop_end)) + = gen_rtx (EXPR_LIST, REG_NONNEG, NULL_RTX, + REG_NOTES (PREV_INSN (loop_end))); + bl->nonneg = 1; + + return 1; + } + + /* If the decrement is 1 and the value was tested as >= 0 before + the loop, then we can safely optimize. */ + for (p = loop_start; p; p = PREV_INSN (p)) + { + if (GET_CODE (p) == CODE_LABEL) + break; + if (GET_CODE (p) != JUMP_INSN) + continue; + + before_comparison = get_condition_for_loop (p); + if (before_comparison + && XEXP (before_comparison, 0) == bl->biv->dest_reg + && GET_CODE (before_comparison) == LT + && XEXP (before_comparison, 1) == const0_rtx + && ! reg_set_between_p (bl->biv->dest_reg, p, loop_start) + && INTVAL (bl->biv->add_val) == -1) + { + REG_NOTES (PREV_INSN (loop_end)) + = gen_rtx (EXPR_LIST, REG_NONNEG, NULL_RTX, + REG_NOTES (PREV_INSN (loop_end))); + bl->nonneg = 1; + + return 1; + } + } + } + else if (num_mem_sets <= 1) + { + /* Try to change inc to dec, so can apply above optimization. */ + /* Can do this if: + all registers modified are induction variables or invariant, + all memory references have non-overlapping addresses + (obviously true if only one write) + allow 2 insns for the compare/jump at the end of the loop. */ + int num_nonfixed_reads = 0; + /* 1 if the iteration var is used only to count iterations. */ + int no_use_except_counting = 0; + + for (p = loop_start; p != loop_end; p = NEXT_INSN (p)) + if (GET_RTX_CLASS (GET_CODE (p)) == 'i') + num_nonfixed_reads += count_nonfixed_reads (PATTERN (p)); + + if (bl->giv_count == 0 + && ! loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]]) + { + rtx bivreg = regno_reg_rtx[bl->regno]; + + /* If there are no givs for this biv, and the only exit is the + fall through at the end of the the loop, then + see if perhaps there are no uses except to count. */ + no_use_except_counting = 1; + for (p = loop_start; p != loop_end; p = NEXT_INSN (p)) + if (GET_RTX_CLASS (GET_CODE (p)) == 'i') + { + rtx set = single_set (p); + + if (set && GET_CODE (SET_DEST (set)) == REG + && REGNO (SET_DEST (set)) == bl->regno) + /* An insn that sets the biv is okay. */ + ; + else if (p == prev_nonnote_insn (prev_nonnote_insn (loop_end)) + || p == prev_nonnote_insn (loop_end)) + /* Don't bother about the end test. */ + ; + else if (reg_mentioned_p (bivreg, PATTERN (p))) + /* Any other use of the biv is no good. */ + { + no_use_except_counting = 0; + break; + } + } + } + + /* This code only acts for innermost loops. Also it simplifies + the memory address check by only reversing loops with + zero or one memory access. + Two memory accesses could involve parts of the same array, + and that can't be reversed. */ + + if (num_nonfixed_reads <= 1 + && !loop_has_call + && !loop_has_volatile + && (no_use_except_counting + || (bl->giv_count + bl->biv_count + num_mem_sets + + num_movables + 2 == insn_count))) + { + rtx condition = get_condition_for_loop (PREV_INSN (loop_end)); + int win; + rtx tem; + + /* Loop can be reversed. */ + if (loop_dump_stream) + fprintf (loop_dump_stream, "Can reverse loop\n"); + + /* Now check other conditions: + initial_value must be zero, + final_value % add_val == 0, so that when reversed, the + biv will be zero on the last iteration. + + This test can probably be improved since +/- 1 in the constant + can be obtained by changing LT to LE and vice versa; this is + confusing. */ + + if (comparison && bl->initial_value == const0_rtx + && GET_CODE (XEXP (comparison, 1)) == CONST_INT + /* LE gets turned into LT */ + && GET_CODE (comparison) == LT + && (INTVAL (XEXP (comparison, 1)) + % INTVAL (bl->biv->add_val)) == 0) + { + /* Register will always be nonnegative, with value + 0 on last iteration if loop reversed */ + + /* Save some info needed to produce the new insns. */ + reg = bl->biv->dest_reg; + jump_label = XEXP (SET_SRC (PATTERN (PREV_INSN (loop_end))), 1); + new_add_val = GEN_INT (- INTVAL (bl->biv->add_val)); + + final_value = XEXP (comparison, 1); + start_value = GEN_INT (INTVAL (XEXP (comparison, 1)) + - INTVAL (bl->biv->add_val)); + + /* Initialize biv to start_value before loop start. + The old initializing insn will be deleted as a + dead store by flow.c. */ + emit_insn_before (gen_move_insn (reg, start_value), loop_start); + + /* Add insn to decrement register, and delete insn + that incremented the register. */ + p = emit_insn_before (gen_add2_insn (reg, new_add_val), + bl->biv->insn); + delete_insn (bl->biv->insn); + + /* Update biv info to reflect its new status. */ + bl->biv->insn = p; + bl->initial_value = start_value; + bl->biv->add_val = new_add_val; + + /* Inc LABEL_NUSES so that delete_insn will + not delete the label. */ + LABEL_NUSES (XEXP (jump_label, 0)) ++; + + /* Emit an insn after the end of the loop to set the biv's + proper exit value if it is used anywhere outside the loop. */ + if ((regno_last_uid[bl->regno] + != INSN_UID (PREV_INSN (PREV_INSN (loop_end)))) + || ! bl->init_insn + || regno_first_uid[bl->regno] != INSN_UID (bl->init_insn)) + emit_insn_after (gen_move_insn (reg, final_value), + loop_end); + + /* Delete compare/branch at end of loop. */ + delete_insn (PREV_INSN (loop_end)); + delete_insn (PREV_INSN (loop_end)); + + /* Add new compare/branch insn at end of loop. */ + start_sequence (); + emit_cmp_insn (reg, const0_rtx, GE, NULL_RTX, + GET_MODE (reg), 0, 0); + emit_jump_insn (gen_bge (XEXP (jump_label, 0))); + tem = gen_sequence (); + end_sequence (); + emit_jump_insn_before (tem, loop_end); + + for (tem = PREV_INSN (loop_end); + tem && GET_CODE (tem) != JUMP_INSN; tem = PREV_INSN (tem)) + ; + if (tem) + { + JUMP_LABEL (tem) = XEXP (jump_label, 0); + + /* Increment of LABEL_NUSES done above. */ + /* Register is now always nonnegative, + so add REG_NONNEG note to the branch. */ + REG_NOTES (tem) = gen_rtx (EXPR_LIST, REG_NONNEG, NULL_RTX, + REG_NOTES (tem)); + } + + bl->nonneg = 1; + + /* Mark that this biv has been reversed. Each giv which depends + on this biv, and which is also live past the end of the loop + will have to be fixed up. */ + + bl->reversed = 1; + + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Reversed loop and added reg_nonneg\n"); + + return 1; + } + } + } + + return 0; +} + +/* Verify whether the biv BL appears to be eliminable, + based on the insns in the loop that refer to it. + LOOP_START is the first insn of the loop, and END is the end insn. + + If ELIMINATE_P is non-zero, actually do the elimination. + + THRESHOLD and INSN_COUNT are from loop_optimize and are used to + determine whether invariant insns should be placed inside or at the + start of the loop. */ + +static int +maybe_eliminate_biv (bl, loop_start, end, eliminate_p, threshold, insn_count) + struct iv_class *bl; + rtx loop_start; + rtx end; + int eliminate_p; + int threshold, insn_count; +{ + rtx reg = bl->biv->dest_reg; + rtx p, set; + struct induction *v; + + /* Scan all insns in the loop, stopping if we find one that uses the + biv in a way that we cannot eliminate. */ + + for (p = loop_start; p != end; p = NEXT_INSN (p)) + { + enum rtx_code code = GET_CODE (p); + rtx where = threshold >= insn_count ? loop_start : p; + + if ((code == INSN || code == JUMP_INSN || code == CALL_INSN) + && reg_mentioned_p (reg, PATTERN (p)) + && ! maybe_eliminate_biv_1 (PATTERN (p), p, bl, eliminate_p, where)) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Cannot eliminate biv %d: biv used in insn %d.\n", + bl->regno, INSN_UID (p)); + break; + } + } + + if (p == end) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, "biv %d %s eliminated.\n", + bl->regno, eliminate_p ? "was" : "can be"); + return 1; + } + + return 0; +} + +/* If BL appears in X (part of the pattern of INSN), see if we can + eliminate its use. If so, return 1. If not, return 0. + + If BIV does not appear in X, return 1. + + If ELIMINATE_P is non-zero, actually do the elimination. WHERE indicates + where extra insns should be added. Depending on how many items have been + moved out of the loop, it will either be before INSN or at the start of + the loop. */ + +static int +maybe_eliminate_biv_1 (x, insn, bl, eliminate_p, where) + rtx x, insn; + struct iv_class *bl; + int eliminate_p; + rtx where; +{ + enum rtx_code code = GET_CODE (x); + rtx reg = bl->biv->dest_reg; + enum machine_mode mode = GET_MODE (reg); + struct induction *v; + rtx arg, new, tem; + int arg_operand; + char *fmt; + int i, j; + + switch (code) + { + case REG: + /* If we haven't already been able to do something with this BIV, + we can't eliminate it. */ + if (x == reg) + return 0; + return 1; + + case SET: + /* If this sets the BIV, it is not a problem. */ + if (SET_DEST (x) == reg) + return 1; + + /* If this is an insn that defines a giv, it is also ok because + it will go away when the giv is reduced. */ + for (v = bl->giv; v; v = v->next_iv) + if (v->giv_type == DEST_REG && SET_DEST (x) == v->dest_reg) + return 1; + +#ifdef HAVE_cc0 + if (SET_DEST (x) == cc0_rtx && SET_SRC (x) == reg) + { + /* Can replace with any giv that was reduced and + that has (MULT_VAL != 0) and (ADD_VAL == 0). + Require a constant for MULT_VAL, so we know it's nonzero. */ + + for (v = bl->giv; v; v = v->next_iv) + if (CONSTANT_P (v->mult_val) && v->mult_val != const0_rtx + && v->add_val == const0_rtx + && ! v->ignore && ! v->maybe_dead + && v->mode == mode) + { + if (! eliminate_p) + return 1; + + /* If the giv has the opposite direction of change, + then reverse the comparison. */ + if (INTVAL (v->mult_val) < 0) + new = gen_rtx (COMPARE, GET_MODE (v->new_reg), + const0_rtx, v->new_reg); + else + new = v->new_reg; + + /* We can probably test that giv's reduced reg. */ + if (validate_change (insn, &SET_SRC (x), new, 0)) + return 1; + } + + /* Look for a giv with (MULT_VAL != 0) and (ADD_VAL != 0); + replace test insn with a compare insn (cmp REDUCED_GIV ADD_VAL). + Require a constant for MULT_VAL, so we know it's nonzero. */ + + for (v = bl->giv; v; v = v->next_iv) + if (CONSTANT_P (v->mult_val) && v->mult_val != const0_rtx + && ! v->ignore && ! v->maybe_dead + && v->mode == mode) + { + if (! eliminate_p) + return 1; + + /* If the giv has the opposite direction of change, + then reverse the comparison. */ + if (INTVAL (v->mult_val) < 0) + new = gen_rtx (COMPARE, VOIDmode, copy_rtx (v->add_val), + v->new_reg); + else + new = gen_rtx (COMPARE, VOIDmode, v->new_reg, + copy_rtx (v->add_val)); + + /* Replace biv with the giv's reduced register. */ + update_reg_last_use (v->add_val, insn); + if (validate_change (insn, &SET_SRC (PATTERN (insn)), new, 0)) + return 1; + + /* Insn doesn't support that constant or invariant. Copy it + into a register (it will be a loop invariant.) */ + tem = gen_reg_rtx (GET_MODE (v->new_reg)); + + emit_insn_before (gen_move_insn (tem, copy_rtx (v->add_val)), + where); + + if (validate_change (insn, &SET_SRC (PATTERN (insn)), + gen_rtx (COMPARE, VOIDmode, + v->new_reg, tem), 0)) + return 1; + } + } +#endif + break; + + case COMPARE: + case EQ: case NE: + case GT: case GE: case GTU: case GEU: + case LT: case LE: case LTU: case LEU: + /* See if either argument is the biv. */ + if (XEXP (x, 0) == reg) + arg = XEXP (x, 1), arg_operand = 1; + else if (XEXP (x, 1) == reg) + arg = XEXP (x, 0), arg_operand = 0; + else + break; + + if (CONSTANT_P (arg)) + { + /* First try to replace with any giv that has constant positive + mult_val and constant add_val. We might be able to support + negative mult_val, but it seems complex to do it in general. */ + + for (v = bl->giv; v; v = v->next_iv) + if (CONSTANT_P (v->mult_val) && INTVAL (v->mult_val) > 0 + && CONSTANT_P (v->add_val) + && ! v->ignore && ! v->maybe_dead + && v->mode == mode) + { + if (! eliminate_p) + return 1; + + /* Replace biv with the giv's reduced reg. */ + XEXP (x, 1-arg_operand) = v->new_reg; + + /* If all constants are actually constant integers and + the derived constant can be directly placed in the COMPARE, + do so. */ + if (GET_CODE (arg) == CONST_INT + && GET_CODE (v->mult_val) == CONST_INT + && GET_CODE (v->add_val) == CONST_INT + && validate_change (insn, &XEXP (x, arg_operand), + GEN_INT (INTVAL (arg) + * INTVAL (v->mult_val) + + INTVAL (v->add_val)), 0)) + return 1; + + /* Otherwise, load it into a register. */ + tem = gen_reg_rtx (mode); + emit_iv_add_mult (arg, v->mult_val, v->add_val, tem, where); + if (validate_change (insn, &XEXP (x, arg_operand), tem, 0)) + return 1; + + /* If that failed, put back the change we made above. */ + XEXP (x, 1-arg_operand) = reg; + } + + /* Look for giv with positive constant mult_val and nonconst add_val. + Insert insns to calculate new compare value. */ + + for (v = bl->giv; v; v = v->next_iv) + if (CONSTANT_P (v->mult_val) && INTVAL (v->mult_val) > 0 + && ! v->ignore && ! v->maybe_dead + && v->mode == mode) + { + rtx tem; + + if (! eliminate_p) + return 1; + + tem = gen_reg_rtx (mode); + + /* Replace biv with giv's reduced register. */ + validate_change (insn, &XEXP (x, 1 - arg_operand), + v->new_reg, 1); + + /* Compute value to compare against. */ + emit_iv_add_mult (arg, v->mult_val, v->add_val, tem, where); + /* Use it in this insn. */ + validate_change (insn, &XEXP (x, arg_operand), tem, 1); + if (apply_change_group ()) + return 1; + } + } + else if (GET_CODE (arg) == REG || GET_CODE (arg) == MEM) + { + if (invariant_p (arg) == 1) + { + /* Look for giv with constant positive mult_val and nonconst + add_val. Insert insns to compute new compare value. */ + + for (v = bl->giv; v; v = v->next_iv) + if (CONSTANT_P (v->mult_val) && INTVAL (v->mult_val) > 0 + && ! v->ignore && ! v->maybe_dead + && v->mode == mode) + { + rtx tem; + + if (! eliminate_p) + return 1; + + tem = gen_reg_rtx (mode); + + /* Replace biv with giv's reduced register. */ + validate_change (insn, &XEXP (x, 1 - arg_operand), + v->new_reg, 1); + + /* Compute value to compare against. */ + emit_iv_add_mult (arg, v->mult_val, v->add_val, + tem, where); + validate_change (insn, &XEXP (x, arg_operand), tem, 1); + if (apply_change_group ()) + return 1; + } + } + + /* This code has problems. Basically, you can't know when + seeing if we will eliminate BL, whether a particular giv + of ARG will be reduced. If it isn't going to be reduced, + we can't eliminate BL. We can try forcing it to be reduced, + but that can generate poor code. + + The problem is that the benefit of reducing TV, below should + be increased if BL can actually be eliminated, but this means + we might have to do a topological sort of the order in which + we try to process biv. It doesn't seem worthwhile to do + this sort of thing now. */ + +#if 0 + /* Otherwise the reg compared with had better be a biv. */ + if (GET_CODE (arg) != REG + || reg_iv_type[REGNO (arg)] != BASIC_INDUCT) + return 0; + + /* Look for a pair of givs, one for each biv, + with identical coefficients. */ + for (v = bl->giv; v; v = v->next_iv) + { + struct induction *tv; + + if (v->ignore || v->maybe_dead || v->mode != mode) + continue; + + for (tv = reg_biv_class[REGNO (arg)]->giv; tv; tv = tv->next_iv) + if (! tv->ignore && ! tv->maybe_dead + && rtx_equal_p (tv->mult_val, v->mult_val) + && rtx_equal_p (tv->add_val, v->add_val) + && tv->mode == mode) + { + if (! eliminate_p) + return 1; + + /* Replace biv with its giv's reduced reg. */ + XEXP (x, 1-arg_operand) = v->new_reg; + /* Replace other operand with the other giv's + reduced reg. */ + XEXP (x, arg_operand) = tv->new_reg; + return 1; + } + } +#endif + } + + /* If we get here, the biv can't be eliminated. */ + return 0; + + case MEM: + /* If this address is a DEST_ADDR giv, it doesn't matter if the + biv is used in it, since it will be replaced. */ + for (v = bl->giv; v; v = v->next_iv) + if (v->giv_type == DEST_ADDR && v->location == &XEXP (x, 0)) + return 1; + break; + } + + /* See if any subexpression fails elimination. */ + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + switch (fmt[i]) + { + case 'e': + if (! maybe_eliminate_biv_1 (XEXP (x, i), insn, bl, + eliminate_p, where)) + return 0; + break; + + case 'E': + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + if (! maybe_eliminate_biv_1 (XVECEXP (x, i, j), insn, bl, + eliminate_p, where)) + return 0; + break; + } + } + + return 1; +} + +/* Return nonzero if the last use of REG + is in an insn following INSN in the same basic block. */ + +static int +last_use_this_basic_block (reg, insn) + rtx reg; + rtx insn; +{ + rtx n; + for (n = insn; + n && GET_CODE (n) != CODE_LABEL && GET_CODE (n) != JUMP_INSN; + n = NEXT_INSN (n)) + { + if (regno_last_uid[REGNO (reg)] == INSN_UID (n)) + return 1; + } + return 0; +} + +/* Called via `note_stores' to record the initial value of a biv. Here we + just record the location of the set and process it later. */ + +static void +record_initial (dest, set) + rtx dest; + rtx set; +{ + struct iv_class *bl; + + if (GET_CODE (dest) != REG + || REGNO (dest) >= max_reg_before_loop + || reg_iv_type[REGNO (dest)] != BASIC_INDUCT + /* Reject this insn if the source isn't valid for the mode of DEST. */ + || GET_MODE (dest) != GET_MODE (SET_DEST (set))) + return; + + bl = reg_biv_class[REGNO (dest)]; + + /* If this is the first set found, record it. */ + if (bl->init_insn == 0) + { + bl->init_insn = note_insn; + bl->init_set = set; + } +} + +/* If any of the registers in X are "old" and currently have a last use earlier + than INSN, update them to have a last use of INSN. Their actual last use + will be the previous insn but it will not have a valid uid_luid so we can't + use it. */ + +static void +update_reg_last_use (x, insn) + rtx x; + rtx insn; +{ + /* Check for the case where INSN does not have a valid luid. In this case, + there is no need to modify the regno_last_uid, as this can only happen + when code is inserted after the loop_end to set a pseudo's final value, + and hence this insn will never be the last use of x. */ + if (GET_CODE (x) == REG && REGNO (x) < max_reg_before_loop + && INSN_UID (insn) < max_uid_for_loop + && uid_luid[regno_last_uid[REGNO (x)]] < uid_luid[INSN_UID (insn)]) + regno_last_uid[REGNO (x)] = INSN_UID (insn); + else + { + register int i, j; + register char *fmt = GET_RTX_FORMAT (GET_CODE (x)); + for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + update_reg_last_use (XEXP (x, i), insn); + else if (fmt[i] == 'E') + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + update_reg_last_use (XVECEXP (x, i, j), insn); + } + } +} + +/* Given a jump insn JUMP, return the condition that will cause it to branch + to its JUMP_LABEL. If the condition cannot be understood, or is an + inequality floating-point comparison which needs to be reversed, 0 will + be returned. + + If EARLIEST is non-zero, it is a pointer to a place where the earliest + insn used in locating the condition was found. If a replacement test + of the condition is desired, it should be placed in front of that + insn and we will be sure that the inputs are still valid. + + The condition will be returned in a canonical form to simplify testing by + callers. Specifically: + + (1) The code will always be a comparison operation (EQ, NE, GT, etc.). + (2) Both operands will be machine operands; (cc0) will have been replaced. + (3) If an operand is a constant, it will be the second operand. + (4) (LE x const) will be replaced with (LT x ) and similarly + for GE, GEU, and LEU. */ + +rtx +get_condition (jump, earliest) + rtx jump; + rtx *earliest; +{ + enum rtx_code code; + rtx prev = jump; + rtx set; + rtx tem; + rtx op0, op1; + int reverse_code = 0; + int did_reverse_condition = 0; + + /* If this is not a standard conditional jump, we can't parse it. */ + if (GET_CODE (jump) != JUMP_INSN + || ! condjump_p (jump) || simplejump_p (jump)) + return 0; + + code = GET_CODE (XEXP (SET_SRC (PATTERN (jump)), 0)); + op0 = XEXP (XEXP (SET_SRC (PATTERN (jump)), 0), 0); + op1 = XEXP (XEXP (SET_SRC (PATTERN (jump)), 0), 1); + + if (earliest) + *earliest = jump; + + /* If this branches to JUMP_LABEL when the condition is false, reverse + the condition. */ + if (GET_CODE (XEXP (SET_SRC (PATTERN (jump)), 2)) == LABEL_REF + && XEXP (XEXP (SET_SRC (PATTERN (jump)), 2), 0) == JUMP_LABEL (jump)) + code = reverse_condition (code), did_reverse_condition ^= 1; + + /* If we are comparing a register with zero, see if the register is set + in the previous insn to a COMPARE or a comparison operation. Perform + the same tests as a function of STORE_FLAG_VALUE as find_comparison_args + in cse.c */ + + while (GET_RTX_CLASS (code) == '<' && op1 == const0_rtx) + { + /* Set non-zero when we find something of interest. */ + rtx x = 0; + +#ifdef HAVE_cc0 + /* If comparison with cc0, import actual comparison from compare + insn. */ + if (op0 == cc0_rtx) + { + if ((prev = prev_nonnote_insn (prev)) == 0 + || GET_CODE (prev) != INSN + || (set = single_set (prev)) == 0 + || SET_DEST (set) != cc0_rtx) + return 0; + + op0 = SET_SRC (set); + op1 = CONST0_RTX (GET_MODE (op0)); + if (earliest) + *earliest = prev; + } +#endif + + /* If this is a COMPARE, pick up the two things being compared. */ + if (GET_CODE (op0) == COMPARE) + { + op1 = XEXP (op0, 1); + op0 = XEXP (op0, 0); + continue; + } + else if (GET_CODE (op0) != REG) + break; + + /* Go back to the previous insn. Stop if it is not an INSN. We also + stop if it isn't a single set or if it has a REG_INC note because + we don't want to bother dealing with it. */ + + if ((prev = prev_nonnote_insn (prev)) == 0 + || GET_CODE (prev) != INSN + || FIND_REG_INC_NOTE (prev, 0) + || (set = single_set (prev)) == 0) + break; + + /* If this is setting OP0, get what it sets it to if it looks + relevant. */ + if (SET_DEST (set) == op0) + { + enum machine_mode inner_mode = GET_MODE (SET_SRC (set)); + + if ((GET_CODE (SET_SRC (set)) == COMPARE + || (((code == NE + || (code == LT + && GET_MODE_CLASS (inner_mode) == MODE_INT + && (GET_MODE_BITSIZE (inner_mode) + <= HOST_BITS_PER_WIDE_INT) + && (STORE_FLAG_VALUE + & ((HOST_WIDE_INT) 1 + << (GET_MODE_BITSIZE (inner_mode) - 1)))) +#ifdef FLOAT_STORE_FLAG_VALUE + || (code == LT + && GET_MODE_CLASS (inner_mode) == MODE_FLOAT + && FLOAT_STORE_FLAG_VALUE < 0) +#endif + )) + && GET_RTX_CLASS (GET_CODE (SET_SRC (set))) == '<'))) + x = SET_SRC (set); + else if (((code == EQ + || (code == GE + && (GET_MODE_BITSIZE (inner_mode) + <= HOST_BITS_PER_WIDE_INT) + && GET_MODE_CLASS (inner_mode) == MODE_INT + && (STORE_FLAG_VALUE + & ((HOST_WIDE_INT) 1 + << (GET_MODE_BITSIZE (inner_mode) - 1)))) +#ifdef FLOAT_STORE_FLAG_VALUE + || (code == GE + && GET_MODE_CLASS (inner_mode) == MODE_FLOAT + && FLOAT_STORE_FLAG_VALUE < 0) +#endif + )) + && GET_RTX_CLASS (GET_CODE (SET_SRC (set))) == '<') + { + /* We might have reversed a LT to get a GE here. But this wasn't + actually the comparison of data, so we don't flag that we + have had to reverse the condition. */ + did_reverse_condition ^= 1; + reverse_code = 1; + x = SET_SRC (set); + } + } + + else if (reg_set_p (op0, prev)) + /* If this sets OP0, but not directly, we have to give up. */ + break; + + if (x) + { + if (GET_RTX_CLASS (GET_CODE (x)) == '<') + code = GET_CODE (x); + if (reverse_code) + { + code = reverse_condition (code); + did_reverse_condition ^= 1; + reverse_code = 0; + } + + op0 = XEXP (x, 0), op1 = XEXP (x, 1); + if (earliest) + *earliest = prev; + } + } + + /* If constant is first, put it last. */ + if (CONSTANT_P (op0)) + code = swap_condition (code), tem = op0, op0 = op1, op1 = tem; + + /* If OP0 is the result of a comparison, we weren't able to find what + was really being compared, so fail. */ + if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC) + return 0; + + /* Canonicalize any ordered comparison with integers involving equality + if we can do computations in the relevant mode and we do not + overflow. */ + + if (GET_CODE (op1) == CONST_INT + && GET_MODE (op0) != VOIDmode + && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT) + { + HOST_WIDE_INT const_val = INTVAL (op1); + unsigned HOST_WIDE_INT uconst_val = const_val; + unsigned HOST_WIDE_INT max_val + = (unsigned HOST_WIDE_INT) GET_MODE_MASK (GET_MODE (op0)); + + switch (code) + { + case LE: + if (const_val != max_val >> 1) + code = LT, op1 = GEN_INT (const_val + 1); + break; + + case GE: + if (const_val + != (((HOST_WIDE_INT) 1 + << (GET_MODE_BITSIZE (GET_MODE (op0)) - 1)))) + code = GT, op1 = GEN_INT (const_val - 1); + break; + + case LEU: + if (uconst_val != max_val) + code = LTU, op1 = GEN_INT (uconst_val + 1); + break; + + case GEU: + if (uconst_val != 0) + code = GTU, op1 = GEN_INT (uconst_val - 1); + break; + } + } + + /* If this was floating-point and we reversed anything other than an + EQ or NE, return zero. */ + if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT + && did_reverse_condition && code != NE && code != EQ + && GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT) + return 0; + +#ifdef HAVE_cc0 + /* Never return CC0; return zero instead. */ + if (op0 == cc0_rtx) + return 0; +#endif + + return gen_rtx (code, VOIDmode, op0, op1); +} + +/* Similar to above routine, except that we also put an invariant last + unless both operands are invariants. */ + +rtx +get_condition_for_loop (x) + rtx x; +{ + rtx comparison = get_condition (x, NULL_PTR); + + if (comparison == 0 + || ! invariant_p (XEXP (comparison, 0)) + || invariant_p (XEXP (comparison, 1))) + return comparison; + + return gen_rtx (swap_condition (GET_CODE (comparison)), VOIDmode, + XEXP (comparison, 1), XEXP (comparison, 0)); +} diff --git a/gnu/usr.bin/cc/common/loop.h b/gnu/usr.bin/cc/lib/loop.h similarity index 100% rename from gnu/usr.bin/cc/common/loop.h rename to gnu/usr.bin/cc/lib/loop.h diff --git a/gnu/usr.bin/cc/common/machmode.def b/gnu/usr.bin/cc/lib/machmode.def similarity index 100% rename from gnu/usr.bin/cc/common/machmode.def rename to gnu/usr.bin/cc/lib/machmode.def diff --git a/gnu/usr.bin/cc/common/machmode.h b/gnu/usr.bin/cc/lib/machmode.h similarity index 100% rename from gnu/usr.bin/cc/common/machmode.h rename to gnu/usr.bin/cc/lib/machmode.h diff --git a/gnu/usr.bin/cc/common/obstack.c b/gnu/usr.bin/cc/lib/obstack.c similarity index 100% rename from gnu/usr.bin/cc/common/obstack.c rename to gnu/usr.bin/cc/lib/obstack.c diff --git a/gnu/usr.bin/cc/common/obstack.h b/gnu/usr.bin/cc/lib/obstack.h similarity index 100% rename from gnu/usr.bin/cc/common/obstack.h rename to gnu/usr.bin/cc/lib/obstack.h diff --git a/gnu/usr.bin/cc/common/optabs.c b/gnu/usr.bin/cc/lib/optabs.c similarity index 100% rename from gnu/usr.bin/cc/common/optabs.c rename to gnu/usr.bin/cc/lib/optabs.c diff --git a/gnu/usr.bin/cc/common/output.h b/gnu/usr.bin/cc/lib/output.h similarity index 100% rename from gnu/usr.bin/cc/common/output.h rename to gnu/usr.bin/cc/lib/output.h diff --git a/gnu/usr.bin/cc/common/print-rtl.c b/gnu/usr.bin/cc/lib/print-rtl.c similarity index 100% rename from gnu/usr.bin/cc/common/print-rtl.c rename to gnu/usr.bin/cc/lib/print-rtl.c diff --git a/gnu/usr.bin/cc/common/print-tree.c b/gnu/usr.bin/cc/lib/print-tree.c similarity index 100% rename from gnu/usr.bin/cc/common/print-tree.c rename to gnu/usr.bin/cc/lib/print-tree.c diff --git a/gnu/usr.bin/cc/lib/real.c b/gnu/usr.bin/cc/lib/real.c new file mode 100644 index 0000000000..1e4a4fae5c --- /dev/null +++ b/gnu/usr.bin/cc/lib/real.c @@ -0,0 +1,5060 @@ +/* real.c - implementation of REAL_ARITHMETIC, REAL_VALUE_ATOF, +and support for XFmode IEEE extended real floating point arithmetic. +Contributed by Stephen L. Moshier (moshier@world.std.com). + + Copyright (C) 1993 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include "config.h" +#include "tree.h" + +#ifndef errno +extern int errno; +#endif + +/* To enable support of XFmode extended real floating point, define +LONG_DOUBLE_TYPE_SIZE 96 in the tm.h file (m68k.h or i386.h). + +To support cross compilation between IEEE and VAX floating +point formats, define REAL_ARITHMETIC in the tm.h file. + +In either case the machine files (tm.h) must not contain any code +that tries to use host floating point arithmetic to convert +REAL_VALUE_TYPEs from `double' to `float', pass them to fprintf, +etc. In cross-compile situations a REAL_VALUE_TYPE may not +be intelligible to the host computer's native arithmetic. + +The emulator defaults to the host's floating point format so that +its decimal conversion functions can be used if desired (see +real.h). + +The first part of this file interfaces gcc to ieee.c, which is a +floating point arithmetic suite that was not written with gcc in +mind. The interface is followed by ieee.c itself and related +items. Avoid changing ieee.c unless you have suitable test +programs available. A special version of the PARANOIA floating +point arithmetic tester, modified for this purpose, can be found +on usc.edu : /pub/C-numanal/ieeetest.zoo. Some tutorial +information on ieee.c is given in my book: S. L. Moshier, +_Methods and Programs for Mathematical Functions_, Prentice-Hall +or Simon & Schuster Int'l, 1989. A library of XFmode elementary +transcendental functions can be obtained by ftp from +research.att.com: netlib/cephes/ldouble.shar.Z */ + +/* Type of computer arithmetic. + * Only one of DEC, MIEEE, IBMPC, or UNK should get defined. + */ + +/* `MIEEE' refers generically to big-endian IEEE floating-point data + structure. This definition should work in SFmode `float' type and + DFmode `double' type on virtually all big-endian IEEE machines. + If LONG_DOUBLE_TYPE_SIZE has been defined to be 96, then MIEEE + also invokes the particular XFmode (`long double' type) data + structure used by the Motorola 680x0 series processors. + + `IBMPC' refers generally to little-endian IEEE machines. In this + case, if LONG_DOUBLE_TYPE_SIZE has been defined to be 96, then + IBMPC also invokes the particular XFmode `long double' data + structure used by the Intel 80x86 series processors. + + `DEC' refers specifically to the Digital Equipment Corp PDP-11 + and VAX floating point data structure. This model currently + supports no type wider than DFmode. + + If LONG_DOUBLE_TYPE_SIZE = 64 (the default, unless tm.h defines it) + then `long double' and `double' are both implemented, but they + both mean DFmode. In this case, the software floating-point + support available here is activated by writing + #define REAL_ARITHMETIC + in tm.h. + + The case LONG_DOUBLE_TYPE_SIZE = 128 activates TFmode support + (Not Yet Implemented) and may deactivate XFmode since + `long double' is used to refer to both modes. */ + +/* The following converts gcc macros into the ones used by this file. */ + +/* REAL_ARITHMETIC defined means that macros in real.h are + defined to call emulator functions. */ +#ifdef REAL_ARITHMETIC + +#if TARGET_FLOAT_FORMAT == VAX_FLOAT_FORMAT +/* PDP-11, Pro350, VAX: */ +#define DEC 1 +#else /* it's not VAX */ +#if TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT +#if WORDS_BIG_ENDIAN +/* Motorola IEEE, high order words come first (Sun workstation): */ +#define MIEEE 1 +#else /* not big-endian */ +/* Intel IEEE, low order words come first: + */ +#define IBMPC 1 +#endif /* big-endian */ +#else /* it's not IEEE either */ +/* UNKnown arithmetic. We don't support this and can't go on. */ +unknown arithmetic type +#define UNK 1 +#endif /* not IEEE */ +#endif /* not VAX */ + +#else +/* REAL_ARITHMETIC not defined means that the *host's* data + structure will be used. It may differ by endian-ness from the + target machine's structure and will get its ends swapped + accordingly (but not here). Probably only the decimal <-> binary + functions in this file will actually be used in this case. */ +#if HOST_FLOAT_FORMAT == VAX_FLOAT_FORMAT +#define DEC 1 +#else /* it's not VAX */ +#if HOST_FLOAT_FORMAT == IEEE_FLOAT_FORMAT +#ifdef HOST_WORDS_BIG_ENDIAN +#define MIEEE 1 +#else /* not big-endian */ +#define IBMPC 1 +#endif /* big-endian */ +#else /* it's not IEEE either */ +unknown arithmetic type +#define UNK 1 +#endif /* not IEEE */ +#endif /* not VAX */ + +#endif /* REAL_ARITHMETIC not defined */ + +/* Define INFINITY for support of infinity. + Define NANS for support of Not-a-Number's (NaN's). */ +#ifndef DEC +#define INFINITY +#define NANS +#endif + +/* Support of NaNs requires support of infinity. */ +#ifdef NANS +#ifndef INFINITY +#define INFINITY +#endif +#endif + +/* ehead.h + * + * Include file for extended precision arithmetic programs. + */ + +/* Number of 16 bit words in external e type format */ +#define NE 6 + +/* Number of 16 bit words in internal format */ +#define NI (NE+3) + +/* Array offset to exponent */ +#define E 1 + +/* Array offset to high guard word */ +#define M 2 + +/* Number of bits of precision */ +#define NBITS ((NI-4)*16) + +/* Maximum number of decimal digits in ASCII conversion + * = NBITS*log10(2) + */ +#define NDEC (NBITS*8/27) + +/* The exponent of 1.0 */ +#define EXONE (0x3fff) + +/* Find a host integer type that is at least 16 bits wide, + and another type at least twice whatever that size is. */ + +#if HOST_BITS_PER_CHAR >= 16 +#define EMUSHORT char +#define EMUSHORT_SIZE HOST_BITS_PER_CHAR +#define EMULONG_SIZE (2 * HOST_BITS_PER_CHAR) +#else +#if HOST_BITS_PER_SHORT >= 16 +#define EMUSHORT short +#define EMUSHORT_SIZE HOST_BITS_PER_SHORT +#define EMULONG_SIZE (2 * HOST_BITS_PER_SHORT) +#else +#if HOST_BITS_PER_INT >= 16 +#define EMUSHORT int +#define EMUSHORT_SIZE HOST_BITS_PER_INT +#define EMULONG_SIZE (2 * HOST_BITS_PER_INT) +#else +#if HOST_BITS_PER_LONG >= 16 +#define EMUSHORT long +#define EMUSHORT_SIZE HOST_BITS_PER_LONG +#define EMULONG_SIZE (2 * HOST_BITS_PER_LONG) +#else +/* You will have to modify this program to have a smaller unit size. */ +#define EMU_NON_COMPILE +#endif +#endif +#endif +#endif + +#if HOST_BITS_PER_SHORT >= EMULONG_SIZE +#define EMULONG short +#else +#if HOST_BITS_PER_INT >= EMULONG_SIZE +#define EMULONG int +#else +#if HOST_BITS_PER_LONG >= EMULONG_SIZE +#define EMULONG long +#else +#if HOST_BITS_PER_LONG_LONG >= EMULONG_SIZE +#define EMULONG long long int +#else +/* You will have to modify this program to have a smaller unit size. */ +#define EMU_NON_COMPILE +#endif +#endif +#endif +#endif + + +/* The host interface doesn't work if no 16-bit size exists. */ +#if EMUSHORT_SIZE != 16 +#define EMU_NON_COMPILE +#endif + +/* OK to continue compilation. */ +#ifndef EMU_NON_COMPILE + +/* Construct macros to translate between REAL_VALUE_TYPE and e type. + In GET_REAL and PUT_REAL, r and e are pointers. + A REAL_VALUE_TYPE is guaranteed to occupy contiguous locations + in memory, with no holes. */ + +#if LONG_DOUBLE_TYPE_SIZE == 96 +#define GET_REAL(r,e) bcopy (r, e, 2*NE) +#define PUT_REAL(e,r) bcopy (e, r, 2*NE) +#else /* no XFmode */ + +#ifdef REAL_ARITHMETIC +/* Emulator uses target format internally + but host stores it in host endian-ness. */ + +#if defined (HOST_WORDS_BIG_ENDIAN) == WORDS_BIG_ENDIAN +#define GET_REAL(r,e) e53toe ((r), (e)) +#define PUT_REAL(e,r) etoe53 ((e), (r)) + +#else /* endian-ness differs */ +/* emulator uses target endian-ness internally */ +#define GET_REAL(r,e) \ +do { EMUSHORT w[4]; \ + w[3] = ((EMUSHORT *) r)[0]; \ + w[2] = ((EMUSHORT *) r)[1]; \ + w[1] = ((EMUSHORT *) r)[2]; \ + w[0] = ((EMUSHORT *) r)[3]; \ + e53toe (w, (e)); } while (0) + +#define PUT_REAL(e,r) \ +do { EMUSHORT w[4]; \ + etoe53 ((e), w); \ + *((EMUSHORT *) r) = w[3]; \ + *((EMUSHORT *) r + 1) = w[2]; \ + *((EMUSHORT *) r + 2) = w[1]; \ + *((EMUSHORT *) r + 3) = w[0]; } while (0) + +#endif /* endian-ness differs */ + +#else /* not REAL_ARITHMETIC */ + +/* emulator uses host format */ +#define GET_REAL(r,e) e53toe ((r), (e)) +#define PUT_REAL(e,r) etoe53 ((e), (r)) + +#endif /* not REAL_ARITHMETIC */ +#endif /* no XFmode */ + +void warning (); +extern int extra_warnings; +int ecmp (), enormlz (), eshift (); +int eisneg (), eisinf (), eisnan (), eiisinf (), eiisnan (); +void eadd (), esub (), emul (), ediv (); +void eshup1 (), eshup8 (), eshup6 (), eshdn1 (), eshdn8 (), eshdn6 (); +void eabs (), eneg (), emov (), eclear (), einfin (), efloor (); +void eldexp (), efrexp (), eifrac (), euifrac (), ltoe (), ultoe (); +void eround (), ereal_to_decimal (), eiinfin (), einan (); +void esqrt (), elog (), eexp (), etanh (), epow (); +void asctoe (), asctoe24 (), asctoe53 (), asctoe64 (); +void etoasc (), e24toasc (), e53toasc (), e64toasc (); +void etoe64 (), etoe53 (), etoe24 (), e64toe (), e53toe (), e24toe (); +void mtherr (), make_nan (); +void enan (); +extern unsigned EMUSHORT ezero[], ehalf[], eone[], etwo[]; +extern unsigned EMUSHORT elog2[], esqrt2[]; + +/* Pack output array with 32-bit numbers obtained from + array containing 16-bit numbers, swapping ends if required. */ +void +endian (e, x, mode) + unsigned EMUSHORT e[]; + long x[]; + enum machine_mode mode; +{ + unsigned long th, t; + +#if WORDS_BIG_ENDIAN + switch (mode) + { + + case XFmode: + + /* Swap halfwords in the third long. */ + th = (unsigned long) e[4] & 0xffff; + t = (unsigned long) e[5] & 0xffff; + t |= th << 16; + x[2] = (long) t; + /* fall into the double case */ + + case DFmode: + + /* swap halfwords in the second word */ + th = (unsigned long) e[2] & 0xffff; + t = (unsigned long) e[3] & 0xffff; + t |= th << 16; + x[1] = (long) t; + /* fall into the float case */ + + case SFmode: + + /* swap halfwords in the first word */ + th = (unsigned long) e[0] & 0xffff; + t = (unsigned long) e[1] & 0xffff; + t |= th << 16; + x[0] = t; + break; + + default: + abort (); + } + +#else + + /* Pack the output array without swapping. */ + + switch (mode) + { + + case XFmode: + + /* Pack the third long. + Each element of the input REAL_VALUE_TYPE array has 16 bit useful bits + in it. */ + th = (unsigned long) e[5] & 0xffff; + t = (unsigned long) e[4] & 0xffff; + t |= th << 16; + x[2] = (long) t; + /* fall into the double case */ + + case DFmode: + + /* pack the second long */ + th = (unsigned long) e[3] & 0xffff; + t = (unsigned long) e[2] & 0xffff; + t |= th << 16; + x[1] = (long) t; + /* fall into the float case */ + + case SFmode: + + /* pack the first long */ + th = (unsigned long) e[1] & 0xffff; + t = (unsigned long) e[0] & 0xffff; + t |= th << 16; + x[0] = t; + break; + + default: + abort (); + } + +#endif +} + + +/* This is the implementation of the REAL_ARITHMETIC macro. + */ +void +earith (value, icode, r1, r2) + REAL_VALUE_TYPE *value; + int icode; + REAL_VALUE_TYPE *r1; + REAL_VALUE_TYPE *r2; +{ + unsigned EMUSHORT d1[NE], d2[NE], v[NE]; + enum tree_code code; + + GET_REAL (r1, d1); + GET_REAL (r2, d2); +#ifdef NANS +/* Return NaN input back to the caller. */ + if (eisnan (d1)) + { + PUT_REAL (d1, value); + return; + } + if (eisnan (d2)) + { + PUT_REAL (d2, value); + return; + } +#endif + code = (enum tree_code) icode; + switch (code) + { + case PLUS_EXPR: + eadd (d2, d1, v); + break; + + case MINUS_EXPR: + esub (d2, d1, v); /* d1 - d2 */ + break; + + case MULT_EXPR: + emul (d2, d1, v); + break; + + case RDIV_EXPR: +#ifndef REAL_INFINITY + if (ecmp (d2, ezero) == 0) + { +#ifdef NANS + enan (v); + break; +#else + abort (); +#endif + } +#endif + ediv (d2, d1, v); /* d1/d2 */ + break; + + case MIN_EXPR: /* min (d1,d2) */ + if (ecmp (d1, d2) < 0) + emov (d1, v); + else + emov (d2, v); + break; + + case MAX_EXPR: /* max (d1,d2) */ + if (ecmp (d1, d2) > 0) + emov (d1, v); + else + emov (d2, v); + break; + default: + emov (ezero, v); + break; + } +PUT_REAL (v, value); +} + + +/* Truncate REAL_VALUE_TYPE toward zero to signed HOST_WIDE_INT + * implements REAL_VALUE_RNDZINT (x) (etrunci (x)) + */ +REAL_VALUE_TYPE +etrunci (x) + REAL_VALUE_TYPE x; +{ + unsigned EMUSHORT f[NE], g[NE]; + REAL_VALUE_TYPE r; + long l; + + GET_REAL (&x, g); +#ifdef NANS + if (eisnan (g)) + return (x); +#endif + eifrac (g, &l, f); + ltoe (&l, g); + PUT_REAL (g, &r); + return (r); +} + + +/* Truncate REAL_VALUE_TYPE toward zero to unsigned HOST_WIDE_INT + * implements REAL_VALUE_UNSIGNED_RNDZINT (x) (etruncui (x)) + */ +REAL_VALUE_TYPE +etruncui (x) + REAL_VALUE_TYPE x; +{ + unsigned EMUSHORT f[NE], g[NE]; + REAL_VALUE_TYPE r; + unsigned long l; + + GET_REAL (&x, g); +#ifdef NANS + if (eisnan (g)) + return (x); +#endif + euifrac (g, &l, f); + ultoe (&l, g); + PUT_REAL (g, &r); + return (r); +} + + +/* This is the REAL_VALUE_ATOF function. + * It converts a decimal string to binary, rounding off + * as indicated by the machine_mode argument. Then it + * promotes the rounded value to REAL_VALUE_TYPE. + */ +REAL_VALUE_TYPE +ereal_atof (s, t) + char *s; + enum machine_mode t; +{ + unsigned EMUSHORT tem[NE], e[NE]; + REAL_VALUE_TYPE r; + + switch (t) + { + case SFmode: + asctoe24 (s, tem); + e24toe (tem, e); + break; + case DFmode: + asctoe53 (s, tem); + e53toe (tem, e); + break; + case XFmode: + asctoe64 (s, tem); + e64toe (tem, e); + break; + default: + asctoe (s, e); + } + PUT_REAL (e, &r); + return (r); +} + + +/* Expansion of REAL_NEGATE. + */ +REAL_VALUE_TYPE +ereal_negate (x) + REAL_VALUE_TYPE x; +{ + unsigned EMUSHORT e[NE]; + REAL_VALUE_TYPE r; + + GET_REAL (&x, e); +#ifdef NANS + if (eisnan (e)) + return (x); +#endif + eneg (e); + PUT_REAL (e, &r); + return (r); +} + + +/* Round real to int + * implements REAL_VALUE_FIX (x) (eroundi (x)) + * The type of rounding is left unspecified by real.h. + * It is implemented here as round to nearest (add .5 and chop). + */ +int +eroundi (x) + REAL_VALUE_TYPE x; +{ + unsigned EMUSHORT f[NE], g[NE]; + EMULONG l; + + GET_REAL (&x, f); +#ifdef NANS + if (eisnan (f)) + { + warning ("conversion from NaN to int"); + return (-1); + } +#endif + eround (f, g); + eifrac (g, &l, f); + return ((int) l); +} + +/* Round real to nearest unsigned int + * implements REAL_VALUE_UNSIGNED_FIX (x) ((unsigned int) eroundi (x)) + * Negative input returns zero. + * The type of rounding is left unspecified by real.h. + * It is implemented here as round to nearest (add .5 and chop). + */ +unsigned int +eroundui (x) + REAL_VALUE_TYPE x; +{ + unsigned EMUSHORT f[NE], g[NE]; + unsigned EMULONG l; + + GET_REAL (&x, f); +#ifdef NANS + if (eisnan (f)) + { + warning ("conversion from NaN to unsigned int"); + return (-1); + } +#endif + eround (f, g); + euifrac (g, &l, f); + return ((unsigned int)l); +} + + +/* REAL_VALUE_FROM_INT macro. + */ +void +ereal_from_int (d, i, j) + REAL_VALUE_TYPE *d; + long i, j; +{ + unsigned EMUSHORT df[NE], dg[NE]; + long low, high; + int sign; + + sign = 0; + low = i; + if ((high = j) < 0) + { + sign = 1; + /* complement and add 1 */ + high = ~high; + if (low) + low = -low; + else + high += 1; + } + eldexp (eone, HOST_BITS_PER_LONG, df); + ultoe (&high, dg); + emul (dg, df, dg); + ultoe (&low, df); + eadd (df, dg, dg); + if (sign) + eneg (dg); + PUT_REAL (dg, d); +} + + +/* REAL_VALUE_FROM_UNSIGNED_INT macro. + */ +void +ereal_from_uint (d, i, j) + REAL_VALUE_TYPE *d; + unsigned long i, j; +{ + unsigned EMUSHORT df[NE], dg[NE]; + unsigned long low, high; + + low = i; + high = j; + eldexp (eone, HOST_BITS_PER_LONG, df); + ultoe (&high, dg); + emul (dg, df, dg); + ultoe (&low, df); + eadd (df, dg, dg); + PUT_REAL (dg, d); +} + + +/* REAL_VALUE_TO_INT macro + */ +void +ereal_to_int (low, high, rr) + long *low, *high; + REAL_VALUE_TYPE rr; +{ + unsigned EMUSHORT d[NE], df[NE], dg[NE], dh[NE]; + int s; + + GET_REAL (&rr, d); +#ifdef NANS + if (eisnan (d)) + { + warning ("conversion from NaN to int"); + *low = -1; + *high = -1; + return; + } +#endif + /* convert positive value */ + s = 0; + if (eisneg (d)) + { + eneg (d); + s = 1; + } + eldexp (eone, HOST_BITS_PER_LONG, df); + ediv (df, d, dg); /* dg = d / 2^32 is the high word */ + euifrac (dg, high, dh); + emul (df, dh, dg); /* fractional part is the low word */ + euifrac (dg, low, dh); + if (s) + { + /* complement and add 1 */ + *high = ~(*high); + if (*low) + *low = -(*low); + else + *high += 1; + } +} + + +/* REAL_VALUE_LDEXP macro. + */ +REAL_VALUE_TYPE +ereal_ldexp (x, n) + REAL_VALUE_TYPE x; + int n; +{ + unsigned EMUSHORT e[NE], y[NE]; + REAL_VALUE_TYPE r; + + GET_REAL (&x, e); +#ifdef NANS + if (eisnan (e)) + return (x); +#endif + eldexp (e, n, y); + PUT_REAL (y, &r); + return (r); +} + +/* These routines are conditionally compiled because functions + * of the same names may be defined in fold-const.c. */ +#ifdef REAL_ARITHMETIC + +/* Check for infinity in a REAL_VALUE_TYPE. */ +int +target_isinf (x) + REAL_VALUE_TYPE x; +{ + unsigned EMUSHORT e[NE]; + +#ifdef INFINITY + GET_REAL (&x, e); + return (eisinf (e)); +#else + return 0; +#endif +} + + +/* Check whether a REAL_VALUE_TYPE item is a NaN. */ + +int +target_isnan (x) + REAL_VALUE_TYPE x; +{ + unsigned EMUSHORT e[NE]; + +#ifdef NANS + GET_REAL (&x, e); + return (eisnan (e)); +#else + return (0); +#endif +} + + +/* Check for a negative REAL_VALUE_TYPE number. + * this means strictly less than zero, not -0. + */ + +int +target_negative (x) + REAL_VALUE_TYPE x; +{ + unsigned EMUSHORT e[NE]; + + GET_REAL (&x, e); + if (ecmp (e, ezero) == -1) + return (1); + return (0); +} + +/* Expansion of REAL_VALUE_TRUNCATE. + * The result is in floating point, rounded to nearest or even. + */ +REAL_VALUE_TYPE +real_value_truncate (mode, arg) + enum machine_mode mode; + REAL_VALUE_TYPE arg; +{ + unsigned EMUSHORT e[NE], t[NE]; + REAL_VALUE_TYPE r; + + GET_REAL (&arg, e); +#ifdef NANS + if (eisnan (e)) + return (arg); +#endif + eclear (t); + switch (mode) + { + case XFmode: + etoe64 (e, t); + e64toe (t, t); + break; + + case DFmode: + etoe53 (e, t); + e53toe (t, t); + break; + + case SFmode: + etoe24 (e, t); + e24toe (t, t); + break; + + case SImode: + r = etrunci (e); + return (r); + + default: + abort (); + } + PUT_REAL (t, &r); + return (r); +} + +#endif /* REAL_ARITHMETIC defined */ + +/* Target values are arrays of host longs. A long is guaranteed + to be at least 32 bits wide. */ +void +etarldouble (r, l) + REAL_VALUE_TYPE r; + long l[]; +{ + unsigned EMUSHORT e[NE]; + + GET_REAL (&r, e); + etoe64 (e, e); + endian (e, l, XFmode); +} + +void +etardouble (r, l) + REAL_VALUE_TYPE r; + long l[]; +{ + unsigned EMUSHORT e[NE]; + + GET_REAL (&r, e); + etoe53 (e, e); + endian (e, l, DFmode); +} + +long +etarsingle (r) + REAL_VALUE_TYPE r; +{ + unsigned EMUSHORT e[NE]; + unsigned long l; + + GET_REAL (&r, e); + etoe24 (e, e); + endian (e, &l, SFmode); + return ((long) l); +} + +void +ereal_to_decimal (x, s) + REAL_VALUE_TYPE x; + char *s; +{ + unsigned EMUSHORT e[NE]; + + GET_REAL (&x, e); + etoasc (e, s, 20); +} + +int +ereal_cmp (x, y) + REAL_VALUE_TYPE x, y; +{ + unsigned EMUSHORT ex[NE], ey[NE]; + + GET_REAL (&x, ex); + GET_REAL (&y, ey); + return (ecmp (ex, ey)); +} + +int +ereal_isneg (x) + REAL_VALUE_TYPE x; +{ + unsigned EMUSHORT ex[NE]; + + GET_REAL (&x, ex); + return (eisneg (ex)); +} + +/* End of REAL_ARITHMETIC interface */ + +/* ieee.c + * + * Extended precision IEEE binary floating point arithmetic routines + * + * Numbers are stored in C language as arrays of 16-bit unsigned + * short integers. The arguments of the routines are pointers to + * the arrays. + * + * + * External e type data structure, simulates Intel 8087 chip + * temporary real format but possibly with a larger significand: + * + * NE-1 significand words (least significant word first, + * most significant bit is normally set) + * exponent (value = EXONE for 1.0, + * top bit is the sign) + * + * + * Internal data structure of a number (a "word" is 16 bits): + * + * ei[0] sign word (0 for positive, 0xffff for negative) + * ei[1] biased exponent (value = EXONE for the number 1.0) + * ei[2] high guard word (always zero after normalization) + * ei[3] + * to ei[NI-2] significand (NI-4 significand words, + * most significant word first, + * most significant bit is set) + * ei[NI-1] low guard word (0x8000 bit is rounding place) + * + * + * + * Routines for external format numbers + * + * asctoe (string, e) ASCII string to extended double e type + * asctoe64 (string, &d) ASCII string to long double + * asctoe53 (string, &d) ASCII string to double + * asctoe24 (string, &f) ASCII string to single + * asctoeg (string, e, prec) ASCII string to specified precision + * e24toe (&f, e) IEEE single precision to e type + * e53toe (&d, e) IEEE double precision to e type + * e64toe (&d, e) IEEE long double precision to e type + * eabs (e) absolute value + * eadd (a, b, c) c = b + a + * eclear (e) e = 0 + * ecmp (a, b) Returns 1 if a > b, 0 if a == b, + * -1 if a < b, -2 if either a or b is a NaN. + * ediv (a, b, c) c = b / a + * efloor (a, b) truncate to integer, toward -infinity + * efrexp (a, exp, s) extract exponent and significand + * eifrac (e, &l, frac) e to long integer and e type fraction + * euifrac (e, &l, frac) e to unsigned long integer and e type fraction + * einfin (e) set e to infinity, leaving its sign alone + * eldexp (a, n, b) multiply by 2**n + * emov (a, b) b = a + * emul (a, b, c) c = b * a + * eneg (e) e = -e + * eround (a, b) b = nearest integer value to a + * esub (a, b, c) c = b - a + * e24toasc (&f, str, n) single to ASCII string, n digits after decimal + * e53toasc (&d, str, n) double to ASCII string, n digits after decimal + * e64toasc (&d, str, n) long double to ASCII string + * etoasc (e, str, n) e to ASCII string, n digits after decimal + * etoe24 (e, &f) convert e type to IEEE single precision + * etoe53 (e, &d) convert e type to IEEE double precision + * etoe64 (e, &d) convert e type to IEEE long double precision + * ltoe (&l, e) long (32 bit) integer to e type + * ultoe (&l, e) unsigned long (32 bit) integer to e type + * eisneg (e) 1 if sign bit of e != 0, else 0 + * eisinf (e) 1 if e has maximum exponent (non-IEEE) + * or is infinite (IEEE) + * eisnan (e) 1 if e is a NaN + * + * + * Routines for internal format numbers + * + * eaddm (ai, bi) add significands, bi = bi + ai + * ecleaz (ei) ei = 0 + * ecleazs (ei) set ei = 0 but leave its sign alone + * ecmpm (ai, bi) compare significands, return 1, 0, or -1 + * edivm (ai, bi) divide significands, bi = bi / ai + * emdnorm (ai,l,s,exp) normalize and round off + * emovi (a, ai) convert external a to internal ai + * emovo (ai, a) convert internal ai to external a + * emovz (ai, bi) bi = ai, low guard word of bi = 0 + * emulm (ai, bi) multiply significands, bi = bi * ai + * enormlz (ei) left-justify the significand + * eshdn1 (ai) shift significand and guards down 1 bit + * eshdn8 (ai) shift down 8 bits + * eshdn6 (ai) shift down 16 bits + * eshift (ai, n) shift ai n bits up (or down if n < 0) + * eshup1 (ai) shift significand and guards up 1 bit + * eshup8 (ai) shift up 8 bits + * eshup6 (ai) shift up 16 bits + * esubm (ai, bi) subtract significands, bi = bi - ai + * eiisinf (ai) 1 if infinite + * eiisnan (ai) 1 if a NaN + * einan (ai) set ai = NaN + * eiinfin (ai) set ai = infinity + * + * + * The result is always normalized and rounded to NI-4 word precision + * after each arithmetic operation. + * + * Exception flags are NOT fully supported. + * + * Signaling NaN's are NOT supported; they are treated the same + * as quiet NaN's. + * + * Define INFINITY for support of infinity; otherwise a + * saturation arithmetic is implemented. + * + * Define NANS for support of Not-a-Number items; otherwise the + * arithmetic will never produce a NaN output, and might be confused + * by a NaN input. + * If NaN's are supported, the output of `ecmp (a,b)' is -2 if + * either a or b is a NaN. This means asking `if (ecmp (a,b) < 0)' + * may not be legitimate. Use `if (ecmp (a,b) == -1)' for `less than' + * if in doubt. + * + * Denormals are always supported here where appropriate (e.g., not + * for conversion to DEC numbers). + * + */ + + +/* mconf.h + * + * Common include file for math routines + * + * + * + * SYNOPSIS: + * + * #include "mconf.h" + * + * + * + * DESCRIPTION: + * + * This file contains definitions for error codes that are + * passed to the common error handling routine mtherr + * (which see). + * + * The file also includes a conditional assembly definition + * for the type of computer arithmetic (Intel IEEE, DEC, Motorola + * IEEE, or UNKnown). + * + * For Digital Equipment PDP-11 and VAX computers, certain + * IBM systems, and others that use numbers with a 56-bit + * significand, the symbol DEC should be defined. In this + * mode, most floating point constants are given as arrays + * of octal integers to eliminate decimal to binary conversion + * errors that might be introduced by the compiler. + * + * For computers, such as IBM PC, that follow the IEEE + * Standard for Binary Floating Point Arithmetic (ANSI/IEEE + * Std 754-1985), the symbol IBMPC or MIEEE should be defined. + * These numbers have 53-bit significands. In this mode, constants + * are provided as arrays of hexadecimal 16 bit integers. + * + * To accommodate other types of computer arithmetic, all + * constants are also provided in a normal decimal radix + * which one can hope are correctly converted to a suitable + * format by the available C language compiler. To invoke + * this mode, the symbol UNK is defined. + * + * An important difference among these modes is a predefined + * set of machine arithmetic constants for each. The numbers + * MACHEP (the machine roundoff error), MAXNUM (largest number + * represented), and several other parameters are preset by + * the configuration symbol. Check the file const.c to + * ensure that these values are correct for your computer. + * + * For ANSI C compatibility, define ANSIC equal to 1. Currently + * this affects only the atan2 function and others that use it. + */ + +/* Constant definitions for math error conditions. */ + +#define DOMAIN 1 /* argument domain error */ +#define SING 2 /* argument singularity */ +#define OVERFLOW 3 /* overflow range error */ +#define UNDERFLOW 4 /* underflow range error */ +#define TLOSS 5 /* total loss of precision */ +#define PLOSS 6 /* partial loss of precision */ +#define INVALID 7 /* NaN-producing operation */ + +/* e type constants used by high precision check routines */ + +/*include "ehead.h"*/ +/* 0.0 */ +unsigned EMUSHORT ezero[NE] = +{ + 0, 0000000, 0000000, 0000000, 0000000, 0000000,}; +extern unsigned EMUSHORT ezero[]; + +/* 5.0E-1 */ +unsigned EMUSHORT ehalf[NE] = +{ + 0, 0000000, 0000000, 0000000, 0100000, 0x3ffe,}; +extern unsigned EMUSHORT ehalf[]; + +/* 1.0E0 */ +unsigned EMUSHORT eone[NE] = +{ + 0, 0000000, 0000000, 0000000, 0100000, 0x3fff,}; +extern unsigned EMUSHORT eone[]; + +/* 2.0E0 */ +unsigned EMUSHORT etwo[NE] = +{ + 0, 0000000, 0000000, 0000000, 0100000, 0040000,}; +extern unsigned EMUSHORT etwo[]; + +/* 3.2E1 */ +unsigned EMUSHORT e32[NE] = +{ + 0, 0000000, 0000000, 0000000, 0100000, 0040004,}; +extern unsigned EMUSHORT e32[]; + +/* 6.93147180559945309417232121458176568075500134360255E-1 */ +unsigned EMUSHORT elog2[NE] = +{ + 0xc9e4, 0x79ab, 0150717, 0013767, 0130562, 0x3ffe,}; +extern unsigned EMUSHORT elog2[]; + +/* 1.41421356237309504880168872420969807856967187537695E0 */ +unsigned EMUSHORT esqrt2[NE] = +{ + 0x597e, 0x6484, 0174736, 0171463, 0132404, 0x3fff,}; +extern unsigned EMUSHORT esqrt2[]; + +/* 2/sqrt (PI) = + * 1.12837916709551257389615890312154517168810125865800E0 */ +unsigned EMUSHORT eoneopi[NE] = +{ + 0x71d5, 0x688d, 0012333, 0135202, 0110156, 0x3fff,}; +extern unsigned EMUSHORT eoneopi[]; + +/* 3.14159265358979323846264338327950288419716939937511E0 */ +unsigned EMUSHORT epi[NE] = +{ + 0xc4c6, 0xc234, 0020550, 0155242, 0144417, 0040000,}; +extern unsigned EMUSHORT epi[]; + +/* 5.7721566490153286060651209008240243104215933593992E-1 */ +unsigned EMUSHORT eeul[NE] = +{ + 0xd1be, 0xc7a4, 0076660, 0063743, 0111704, 0x3ffe,}; +extern unsigned EMUSHORT eeul[]; + +/* +include "ehead.h" +include "mconf.h" +*/ + + + +/* Control register for rounding precision. + * This can be set to 80 (if NE=6), 64, 56, 53, or 24 bits. + */ +int rndprc = NBITS; +extern int rndprc; + +void eaddm (), esubm (), emdnorm (), asctoeg (); +static void toe24 (), toe53 (), toe64 (); +void eremain (), einit (), eiremain (); +int ecmpm (), edivm (), emulm (); +void emovi (), emovo (), emovz (), ecleaz (), ecleazs (), eadd1 (); +void etodec (), todec (), dectoe (); + + + + +void +einit () +{ +} + +/* +; Clear out entire external format number. +; +; unsigned EMUSHORT x[]; +; eclear (x); +*/ + +void +eclear (x) + register unsigned EMUSHORT *x; +{ + register int i; + + for (i = 0; i < NE; i++) + *x++ = 0; +} + + + +/* Move external format number from a to b. + * + * emov (a, b); + */ + +void +emov (a, b) + register unsigned EMUSHORT *a, *b; +{ + register int i; + + for (i = 0; i < NE; i++) + *b++ = *a++; +} + + +/* +; Absolute value of external format number +; +; EMUSHORT x[NE]; +; eabs (x); +*/ + +void +eabs (x) + unsigned EMUSHORT x[]; /* x is the memory address of a short */ +{ + + x[NE - 1] &= 0x7fff; /* sign is top bit of last word of external format */ +} + + + + +/* +; Negate external format number +; +; unsigned EMUSHORT x[NE]; +; eneg (x); +*/ + +void +eneg (x) + unsigned EMUSHORT x[]; +{ + +#ifdef NANS + if (eisnan (x)) + return; +#endif + x[NE - 1] ^= 0x8000; /* Toggle the sign bit */ +} + + + +/* Return 1 if external format number is negative, + * else return zero, including when it is a NaN. + */ +int +eisneg (x) + unsigned EMUSHORT x[]; +{ + +#ifdef NANS + if (eisnan (x)) + return (0); +#endif + if (x[NE - 1] & 0x8000) + return (1); + else + return (0); +} + + +/* Return 1 if external format number is infinity. + * else return zero. + */ +int +eisinf (x) + unsigned EMUSHORT x[]; +{ + +#ifdef NANS + if (eisnan (x)) + return (0); +#endif + if ((x[NE - 1] & 0x7fff) == 0x7fff) + return (1); + else + return (0); +} + + +/* Check if e-type number is not a number. + The bit pattern is one that we defined, so we know for sure how to + detect it. */ + +int +eisnan (x) + unsigned EMUSHORT x[]; +{ + +#ifdef NANS + int i; +/* NaN has maximum exponent */ + if ((x[NE - 1] & 0x7fff) != 0x7fff) + return (0); +/* ... and non-zero significand field. */ + for (i = 0; i < NE - 1; i++) + { + if (*x++ != 0) + return (1); + } +#endif + return (0); +} + +/* Fill external format number with infinity pattern (IEEE) + or largest possible number (non-IEEE). + Before calling einfin, you should either call eclear + or set up the sign bit by hand. */ + +void +einfin (x) + register unsigned EMUSHORT *x; +{ + register int i; + +#ifdef INFINITY + for (i = 0; i < NE - 1; i++) + *x++ = 0; + *x |= 32767; +#else + for (i = 0; i < NE - 1; i++) + *x++ = 0xffff; + *x |= 32766; + if (rndprc < NBITS) + { + if (rndprc == 64) + { + *(x - 5) = 0; + } + if (rndprc == 53) + { + *(x - 4) = 0xf800; + } + else + { + *(x - 4) = 0; + *(x - 3) = 0; + *(x - 2) = 0xff00; + } + } +#endif +} + + +/* Output an e-type NaN. + This generates Intel's quiet NaN pattern for extended real. + The exponent is 7fff, the leading mantissa word is c000. */ + +void +enan (x) + register unsigned EMUSHORT *x; +{ + register int i; + + for (i = 0; i < NE - 2; i++) + *x++ = 0; + *x++ = 0xc000; + *x = 0x7fff; +} + + +/* Move in external format number, + * converting it to internal format. + */ +void +emovi (a, b) + unsigned EMUSHORT *a, *b; +{ + register unsigned EMUSHORT *p, *q; + int i; + + q = b; + p = a + (NE - 1); /* point to last word of external number */ + /* get the sign bit */ + if (*p & 0x8000) + *q++ = 0xffff; + else + *q++ = 0; + /* get the exponent */ + *q = *p--; + *q++ &= 0x7fff; /* delete the sign bit */ +#ifdef INFINITY + if ((*(q - 1) & 0x7fff) == 0x7fff) + { +#ifdef NANS + if (eisnan (a)) + { + *q++ = 0; + for (i = 3; i < NI; i++) + *q++ = *p--; + return; + } +#endif + for (i = 2; i < NI; i++) + *q++ = 0; + return; + } +#endif + /* clear high guard word */ + *q++ = 0; + /* move in the significand */ + for (i = 0; i < NE - 1; i++) + *q++ = *p--; + /* clear low guard word */ + *q = 0; +} + + +/* Move internal format number out, + * converting it to external format. + */ +void +emovo (a, b) + unsigned EMUSHORT *a, *b; +{ + register unsigned EMUSHORT *p, *q; + unsigned EMUSHORT i; + + p = a; + q = b + (NE - 1); /* point to output exponent */ + /* combine sign and exponent */ + i = *p++; + if (i) + *q-- = *p++ | 0x8000; + else + *q-- = *p++; +#ifdef INFINITY + if (*(p - 1) == 0x7fff) + { +#ifdef NANS + if (eiisnan (a)) + { + enan (b); + return; + } +#endif + einfin (b); + return; + } +#endif + /* skip over guard word */ + ++p; + /* move the significand */ + for (i = 0; i < NE - 1; i++) + *q-- = *p++; +} + + + + +/* Clear out internal format number. + */ + +void +ecleaz (xi) + register unsigned EMUSHORT *xi; +{ + register int i; + + for (i = 0; i < NI; i++) + *xi++ = 0; +} + + +/* same, but don't touch the sign. */ + +void +ecleazs (xi) + register unsigned EMUSHORT *xi; +{ + register int i; + + ++xi; + for (i = 0; i < NI - 1; i++) + *xi++ = 0; +} + + + +/* Move internal format number from a to b. + */ +void +emovz (a, b) + register unsigned EMUSHORT *a, *b; +{ + register int i; + + for (i = 0; i < NI - 1; i++) + *b++ = *a++; + /* clear low guard word */ + *b = 0; +} + +/* Generate internal format NaN. + The explicit pattern for this is maximum exponent and + top two significand bits set. */ + +void +einan (x) + unsigned EMUSHORT x[]; +{ + + ecleaz (x); + x[E] = 0x7fff; + x[M + 1] = 0xc000; +} + +/* Return nonzero if internal format number is a NaN. */ + +int +eiisnan (x) + unsigned EMUSHORT x[]; +{ + int i; + + if ((x[E] & 0x7fff) == 0x7fff) + { + for (i = M + 1; i < NI; i++) + { + if (x[i] != 0) + return (1); + } + } + return (0); +} + +/* Fill internal format number with infinity pattern. + This has maximum exponent and significand all zeros. */ + +void +eiinfin (x) + unsigned EMUSHORT x[]; +{ + + ecleaz (x); + x[E] = 0x7fff; +} + +/* Return nonzero if internal format number is infinite. */ + +int +eiisinf (x) + unsigned EMUSHORT x[]; +{ + +#ifdef NANS + if (eiisnan (x)) + return (0); +#endif + if ((x[E] & 0x7fff) == 0x7fff) + return (1); + return (0); +} + + +/* +; Compare significands of numbers in internal format. +; Guard words are included in the comparison. +; +; unsigned EMUSHORT a[NI], b[NI]; +; cmpm (a, b); +; +; for the significands: +; returns +1 if a > b +; 0 if a == b +; -1 if a < b +*/ +int +ecmpm (a, b) + register unsigned EMUSHORT *a, *b; +{ + int i; + + a += M; /* skip up to significand area */ + b += M; + for (i = M; i < NI; i++) + { + if (*a++ != *b++) + goto difrnt; + } + return (0); + + difrnt: + if (*(--a) > *(--b)) + return (1); + else + return (-1); +} + + +/* +; Shift significand down by 1 bit +*/ + +void +eshdn1 (x) + register unsigned EMUSHORT *x; +{ + register unsigned EMUSHORT bits; + int i; + + x += M; /* point to significand area */ + + bits = 0; + for (i = M; i < NI; i++) + { + if (*x & 1) + bits |= 1; + *x >>= 1; + if (bits & 2) + *x |= 0x8000; + bits <<= 1; + ++x; + } +} + + + +/* +; Shift significand up by 1 bit +*/ + +void +eshup1 (x) + register unsigned EMUSHORT *x; +{ + register unsigned EMUSHORT bits; + int i; + + x += NI - 1; + bits = 0; + + for (i = M; i < NI; i++) + { + if (*x & 0x8000) + bits |= 1; + *x <<= 1; + if (bits & 2) + *x |= 1; + bits <<= 1; + --x; + } +} + + + +/* +; Shift significand down by 8 bits +*/ + +void +eshdn8 (x) + register unsigned EMUSHORT *x; +{ + register unsigned EMUSHORT newbyt, oldbyt; + int i; + + x += M; + oldbyt = 0; + for (i = M; i < NI; i++) + { + newbyt = *x << 8; + *x >>= 8; + *x |= oldbyt; + oldbyt = newbyt; + ++x; + } +} + +/* +; Shift significand up by 8 bits +*/ + +void +eshup8 (x) + register unsigned EMUSHORT *x; +{ + int i; + register unsigned EMUSHORT newbyt, oldbyt; + + x += NI - 1; + oldbyt = 0; + + for (i = M; i < NI; i++) + { + newbyt = *x >> 8; + *x <<= 8; + *x |= oldbyt; + oldbyt = newbyt; + --x; + } +} + +/* +; Shift significand up by 16 bits +*/ + +void +eshup6 (x) + register unsigned EMUSHORT *x; +{ + int i; + register unsigned EMUSHORT *p; + + p = x + M; + x += M + 1; + + for (i = M; i < NI - 1; i++) + *p++ = *x++; + + *p = 0; +} + +/* +; Shift significand down by 16 bits +*/ + +void +eshdn6 (x) + register unsigned EMUSHORT *x; +{ + int i; + register unsigned EMUSHORT *p; + + x += NI - 1; + p = x + 1; + + for (i = M; i < NI - 1; i++) + *(--p) = *(--x); + + *(--p) = 0; +} + +/* +; Add significands +; x + y replaces y +*/ + +void +eaddm (x, y) + unsigned EMUSHORT *x, *y; +{ + register unsigned EMULONG a; + int i; + unsigned int carry; + + x += NI - 1; + y += NI - 1; + carry = 0; + for (i = M; i < NI; i++) + { + a = (unsigned EMULONG) (*x) + (unsigned EMULONG) (*y) + carry; + if (a & 0x10000) + carry = 1; + else + carry = 0; + *y = (unsigned EMUSHORT) a; + --x; + --y; + } +} + +/* +; Subtract significands +; y - x replaces y +*/ + +void +esubm (x, y) + unsigned EMUSHORT *x, *y; +{ + unsigned EMULONG a; + int i; + unsigned int carry; + + x += NI - 1; + y += NI - 1; + carry = 0; + for (i = M; i < NI; i++) + { + a = (unsigned EMULONG) (*y) - (unsigned EMULONG) (*x) - carry; + if (a & 0x10000) + carry = 1; + else + carry = 0; + *y = (unsigned EMUSHORT) a; + --x; + --y; + } +} + + +/* Divide significands */ + +static unsigned EMUSHORT equot[NI]; + +int +edivm (den, num) + unsigned EMUSHORT den[], num[]; +{ + int i; + register unsigned EMUSHORT *p, *q; + unsigned EMUSHORT j; + + p = &equot[0]; + *p++ = num[0]; + *p++ = num[1]; + + for (i = M; i < NI; i++) + { + *p++ = 0; + } + + /* Use faster compare and subtraction if denominator + * has only 15 bits of significance. + */ + p = &den[M + 2]; + if (*p++ == 0) + { + for (i = M + 3; i < NI; i++) + { + if (*p++ != 0) + goto fulldiv; + } + if ((den[M + 1] & 1) != 0) + goto fulldiv; + eshdn1 (num); + eshdn1 (den); + + p = &den[M + 1]; + q = &num[M + 1]; + + for (i = 0; i < NBITS + 2; i++) + { + if (*p <= *q) + { + *q -= *p; + j = 1; + } + else + { + j = 0; + } + eshup1 (equot); + equot[NI - 2] |= j; + eshup1 (num); + } + goto divdon; + } + + /* The number of quotient bits to calculate is + * NBITS + 1 scaling guard bit + 1 roundoff bit. + */ + fulldiv: + + p = &equot[NI - 2]; + for (i = 0; i < NBITS + 2; i++) + { + if (ecmpm (den, num) <= 0) + { + esubm (den, num); + j = 1; /* quotient bit = 1 */ + } + else + j = 0; + eshup1 (equot); + *p |= j; + eshup1 (num); + } + + divdon: + + eshdn1 (equot); + eshdn1 (equot); + + /* test for nonzero remainder after roundoff bit */ + p = &num[M]; + j = 0; + for (i = M; i < NI; i++) + { + j |= *p++; + } + if (j) + j = 1; + + + for (i = 0; i < NI; i++) + num[i] = equot[i]; + return ((int) j); +} + + +/* Multiply significands */ +int +emulm (a, b) + unsigned EMUSHORT a[], b[]; +{ + unsigned EMUSHORT *p, *q; + int i, j, k; + + equot[0] = b[0]; + equot[1] = b[1]; + for (i = M; i < NI; i++) + equot[i] = 0; + + p = &a[NI - 2]; + k = NBITS; + while (*p == 0) /* significand is not supposed to be all zero */ + { + eshdn6 (a); + k -= 16; + } + if ((*p & 0xff) == 0) + { + eshdn8 (a); + k -= 8; + } + + q = &equot[NI - 1]; + j = 0; + for (i = 0; i < k; i++) + { + if (*p & 1) + eaddm (b, equot); + /* remember if there were any nonzero bits shifted out */ + if (*q & 1) + j |= 1; + eshdn1 (a); + eshdn1 (equot); + } + + for (i = 0; i < NI; i++) + b[i] = equot[i]; + + /* return flag for lost nonzero bits */ + return (j); +} + + + +/* + * Normalize and round off. + * + * The internal format number to be rounded is "s". + * Input "lost" indicates whether or not the number is exact. + * This is the so-called sticky bit. + * + * Input "subflg" indicates whether the number was obtained + * by a subtraction operation. In that case if lost is nonzero + * then the number is slightly smaller than indicated. + * + * Input "exp" is the biased exponent, which may be negative. + * the exponent field of "s" is ignored but is replaced by + * "exp" as adjusted by normalization and rounding. + * + * Input "rcntrl" is the rounding control. + */ + +static int rlast = -1; +static int rw = 0; +static unsigned EMUSHORT rmsk = 0; +static unsigned EMUSHORT rmbit = 0; +static unsigned EMUSHORT rebit = 0; +static int re = 0; +static unsigned EMUSHORT rbit[NI]; + +void +emdnorm (s, lost, subflg, exp, rcntrl) + unsigned EMUSHORT s[]; + int lost; + int subflg; + EMULONG exp; + int rcntrl; +{ + int i, j; + unsigned EMUSHORT r; + + /* Normalize */ + j = enormlz (s); + + /* a blank significand could mean either zero or infinity. */ +#ifndef INFINITY + if (j > NBITS) + { + ecleazs (s); + return; + } +#endif + exp -= j; +#ifndef INFINITY + if (exp >= 32767L) + goto overf; +#else + if ((j > NBITS) && (exp < 32767)) + { + ecleazs (s); + return; + } +#endif + if (exp < 0L) + { + if (exp > (EMULONG) (-NBITS - 1)) + { + j = (int) exp; + i = eshift (s, j); + if (i) + lost = 1; + } + else + { + ecleazs (s); + return; + } + } + /* Round off, unless told not to by rcntrl. */ + if (rcntrl == 0) + goto mdfin; + /* Set up rounding parameters if the control register changed. */ + if (rndprc != rlast) + { + ecleaz (rbit); + switch (rndprc) + { + default: + case NBITS: + rw = NI - 1; /* low guard word */ + rmsk = 0xffff; + rmbit = 0x8000; + rbit[rw - 1] = 1; + re = NI - 2; + rebit = 1; + break; + case 64: + rw = 7; + rmsk = 0xffff; + rmbit = 0x8000; + rbit[rw - 1] = 1; + re = rw - 1; + rebit = 1; + break; + /* For DEC arithmetic */ + case 56: + rw = 6; + rmsk = 0xff; + rmbit = 0x80; + rbit[rw] = 0x100; + re = rw; + rebit = 0x100; + break; + case 53: + rw = 6; + rmsk = 0x7ff; + rmbit = 0x0400; + rbit[rw] = 0x800; + re = rw; + rebit = 0x800; + break; + case 24: + rw = 4; + rmsk = 0xff; + rmbit = 0x80; + rbit[rw] = 0x100; + re = rw; + rebit = 0x100; + break; + } + rlast = rndprc; + } + + if (rndprc >= 64) + { + r = s[rw] & rmsk; + if (rndprc == 64) + { + i = rw + 1; + while (i < NI) + { + if (s[i]) + r |= 1; + s[i] = 0; + ++i; + } + } + } + else + { + if (exp <= 0) + eshdn1 (s); + r = s[rw] & rmsk; + /* These tests assume NI = 8 */ + i = rw + 1; + while (i < NI) + { + if (s[i]) + r |= 1; + s[i] = 0; + ++i; + } + /* + if (rndprc == 24) + { + if (s[5] || s[6]) + r |= 1; + s[5] = 0; + s[6] = 0; + } + */ + } + s[rw] &= ~rmsk; + if ((r & rmbit) != 0) + { + if (r == rmbit) + { + if (lost == 0) + { /* round to even */ + if ((s[re] & rebit) == 0) + goto mddone; + } + else + { + if (subflg != 0) + goto mddone; + } + } + eaddm (rbit, s); + } + mddone: + if ((rndprc < 64) && (exp <= 0)) + { + eshup1 (s); + } + if (s[2] != 0) + { /* overflow on roundoff */ + eshdn1 (s); + exp += 1; + } + mdfin: + s[NI - 1] = 0; + if (exp >= 32767L) + { +#ifndef INFINITY + overf: +#endif +#ifdef INFINITY + s[1] = 32767; + for (i = 2; i < NI - 1; i++) + s[i] = 0; + if (extra_warnings) + warning ("floating point overflow"); +#else + s[1] = 32766; + s[2] = 0; + for (i = M + 1; i < NI - 1; i++) + s[i] = 0xffff; + s[NI - 1] = 0; + if (rndprc < 64) + { + s[rw] &= ~rmsk; + if (rndprc == 24) + { + s[5] = 0; + s[6] = 0; + } + } +#endif + return; + } + if (exp < 0) + s[1] = 0; + else + s[1] = (unsigned EMUSHORT) exp; +} + + + +/* +; Subtract external format numbers. +; +; unsigned EMUSHORT a[NE], b[NE], c[NE]; +; esub (a, b, c); c = b - a +*/ + +static int subflg = 0; + +void +esub (a, b, c) + unsigned EMUSHORT *a, *b, *c; +{ + +#ifdef NANS + if (eisnan (a)) + { + emov (a, c); + return; + } + if (eisnan (b)) + { + emov (b, c); + return; + } +/* Infinity minus infinity is a NaN. + Test for subtracting infinities of the same sign. */ + if (eisinf (a) && eisinf (b) + && ((eisneg (a) ^ eisneg (b)) == 0)) + { + mtherr ("esub", INVALID); + enan (c); + return; + } +#endif + subflg = 1; + eadd1 (a, b, c); +} + + +/* +; Add. +; +; unsigned EMUSHORT a[NE], b[NE], c[NE]; +; eadd (a, b, c); c = b + a +*/ +void +eadd (a, b, c) + unsigned EMUSHORT *a, *b, *c; +{ + +#ifdef NANS +/* NaN plus anything is a NaN. */ + if (eisnan (a)) + { + emov (a, c); + return; + } + if (eisnan (b)) + { + emov (b, c); + return; + } +/* Infinity minus infinity is a NaN. + Test for adding infinities of opposite signs. */ + if (eisinf (a) && eisinf (b) + && ((eisneg (a) ^ eisneg (b)) != 0)) + { + mtherr ("esub", INVALID); + enan (c); + return; + } +#endif + subflg = 0; + eadd1 (a, b, c); +} + +void +eadd1 (a, b, c) + unsigned EMUSHORT *a, *b, *c; +{ + unsigned EMUSHORT ai[NI], bi[NI], ci[NI]; + int i, lost, j, k; + EMULONG lt, lta, ltb; + +#ifdef INFINITY + if (eisinf (a)) + { + emov (a, c); + if (subflg) + eneg (c); + return; + } + if (eisinf (b)) + { + emov (b, c); + return; + } +#endif + emovi (a, ai); + emovi (b, bi); + if (subflg) + ai[0] = ~ai[0]; + + /* compare exponents */ + lta = ai[E]; + ltb = bi[E]; + lt = lta - ltb; + if (lt > 0L) + { /* put the larger number in bi */ + emovz (bi, ci); + emovz (ai, bi); + emovz (ci, ai); + ltb = bi[E]; + lt = -lt; + } + lost = 0; + if (lt != 0L) + { + if (lt < (EMULONG) (-NBITS - 1)) + goto done; /* answer same as larger addend */ + k = (int) lt; + lost = eshift (ai, k); /* shift the smaller number down */ + } + else + { + /* exponents were the same, so must compare significands */ + i = ecmpm (ai, bi); + if (i == 0) + { /* the numbers are identical in magnitude */ + /* if different signs, result is zero */ + if (ai[0] != bi[0]) + { + eclear (c); + return; + } + /* if same sign, result is double */ + /* double denomalized tiny number */ + if ((bi[E] == 0) && ((bi[3] & 0x8000) == 0)) + { + eshup1 (bi); + goto done; + } + /* add 1 to exponent unless both are zero! */ + for (j = 1; j < NI - 1; j++) + { + if (bi[j] != 0) + { + /* This could overflow, but let emovo take care of that. */ + ltb += 1; + break; + } + } + bi[E] = (unsigned EMUSHORT) ltb; + goto done; + } + if (i > 0) + { /* put the larger number in bi */ + emovz (bi, ci); + emovz (ai, bi); + emovz (ci, ai); + } + } + if (ai[0] == bi[0]) + { + eaddm (ai, bi); + subflg = 0; + } + else + { + esubm (ai, bi); + subflg = 1; + } + emdnorm (bi, lost, subflg, ltb, 64); + + done: + emovo (bi, c); +} + + + +/* +; Divide. +; +; unsigned EMUSHORT a[NE], b[NE], c[NE]; +; ediv (a, b, c); c = b / a +*/ +void +ediv (a, b, c) + unsigned EMUSHORT *a, *b, *c; +{ + unsigned EMUSHORT ai[NI], bi[NI]; + int i; + EMULONG lt, lta, ltb; + +#ifdef NANS +/* Return any NaN input. */ + if (eisnan (a)) + { + emov (a, c); + return; + } + if (eisnan (b)) + { + emov (b, c); + return; + } +/* Zero over zero, or infinity over infinity, is a NaN. */ + if (((ecmp (a, ezero) == 0) && (ecmp (b, ezero) == 0)) + || (eisinf (a) && eisinf (b))) + { + mtherr ("ediv", INVALID); + enan (c); + return; + } +#endif +/* Infinity over anything else is infinity. */ +#ifdef INFINITY + if (eisinf (b)) + { + if (eisneg (a) ^ eisneg (b)) + *(c + (NE - 1)) = 0x8000; + else + *(c + (NE - 1)) = 0; + einfin (c); + return; + } +/* Anything else over infinity is zero. */ + if (eisinf (a)) + { + eclear (c); + return; + } +#endif + emovi (a, ai); + emovi (b, bi); + lta = ai[E]; + ltb = bi[E]; + if (bi[E] == 0) + { /* See if numerator is zero. */ + for (i = 1; i < NI - 1; i++) + { + if (bi[i] != 0) + { + ltb -= enormlz (bi); + goto dnzro1; + } + } + eclear (c); + return; + } + dnzro1: + + if (ai[E] == 0) + { /* possible divide by zero */ + for (i = 1; i < NI - 1; i++) + { + if (ai[i] != 0) + { + lta -= enormlz (ai); + goto dnzro2; + } + } + if (ai[0] == bi[0]) + *(c + (NE - 1)) = 0; + else + *(c + (NE - 1)) = 0x8000; +/* Divide by zero is not an invalid operation. + It is a divide-by-zero operation! */ + einfin (c); + mtherr ("ediv", SING); + return; + } + dnzro2: + + i = edivm (ai, bi); + /* calculate exponent */ + lt = ltb - lta + EXONE; + emdnorm (bi, i, 0, lt, 64); + /* set the sign */ + if (ai[0] == bi[0]) + bi[0] = 0; + else + bi[0] = 0Xffff; + emovo (bi, c); +} + + + +/* +; Multiply. +; +; unsigned EMUSHORT a[NE], b[NE], c[NE]; +; emul (a, b, c); c = b * a +*/ +void +emul (a, b, c) + unsigned EMUSHORT *a, *b, *c; +{ + unsigned EMUSHORT ai[NI], bi[NI]; + int i, j; + EMULONG lt, lta, ltb; + +#ifdef NANS +/* NaN times anything is the same NaN. */ + if (eisnan (a)) + { + emov (a, c); + return; + } + if (eisnan (b)) + { + emov (b, c); + return; + } +/* Zero times infinity is a NaN. */ + if ((eisinf (a) && (ecmp (b, ezero) == 0)) + || (eisinf (b) && (ecmp (a, ezero) == 0))) + { + mtherr ("emul", INVALID); + enan (c); + return; + } +#endif +/* Infinity times anything else is infinity. */ +#ifdef INFINITY + if (eisinf (a) || eisinf (b)) + { + if (eisneg (a) ^ eisneg (b)) + *(c + (NE - 1)) = 0x8000; + else + *(c + (NE - 1)) = 0; + einfin (c); + return; + } +#endif + emovi (a, ai); + emovi (b, bi); + lta = ai[E]; + ltb = bi[E]; + if (ai[E] == 0) + { + for (i = 1; i < NI - 1; i++) + { + if (ai[i] != 0) + { + lta -= enormlz (ai); + goto mnzer1; + } + } + eclear (c); + return; + } + mnzer1: + + if (bi[E] == 0) + { + for (i = 1; i < NI - 1; i++) + { + if (bi[i] != 0) + { + ltb -= enormlz (bi); + goto mnzer2; + } + } + eclear (c); + return; + } + mnzer2: + + /* Multiply significands */ + j = emulm (ai, bi); + /* calculate exponent */ + lt = lta + ltb - (EXONE - 1); + emdnorm (bi, j, 0, lt, 64); + /* calculate sign of product */ + if (ai[0] == bi[0]) + bi[0] = 0; + else + bi[0] = 0xffff; + emovo (bi, c); +} + + + + +/* +; Convert IEEE double precision to e type +; double d; +; unsigned EMUSHORT x[N+2]; +; e53toe (&d, x); +*/ +void +e53toe (pe, y) + unsigned EMUSHORT *pe, *y; +{ +#ifdef DEC + + dectoe (pe, y); /* see etodec.c */ + +#else + + register unsigned EMUSHORT r; + register unsigned EMUSHORT *e, *p; + unsigned EMUSHORT yy[NI]; + int denorm, k; + + e = pe; + denorm = 0; /* flag if denormalized number */ + ecleaz (yy); +#ifdef IBMPC + e += 3; +#endif + r = *e; + yy[0] = 0; + if (r & 0x8000) + yy[0] = 0xffff; + yy[M] = (r & 0x0f) | 0x10; + r &= ~0x800f; /* strip sign and 4 significand bits */ +#ifdef INFINITY + if (r == 0x7ff0) + { +#ifdef NANS +#ifdef IBMPC + if (((pe[3] & 0xf) != 0) || (pe[2] != 0) + || (pe[1] != 0) || (pe[0] != 0)) + { + enan (y); + return; + } +#else + if (((pe[0] & 0xf) != 0) || (pe[1] != 0) + || (pe[2] != 0) || (pe[3] != 0)) + { + enan (y); + return; + } +#endif +#endif /* NANS */ + eclear (y); + einfin (y); + if (yy[0]) + eneg (y); + return; + } +#endif /* INFINITY */ + r >>= 4; + /* If zero exponent, then the significand is denormalized. + * So, take back the understood high significand bit. */ + if (r == 0) + { + denorm = 1; + yy[M] &= ~0x10; + } + r += EXONE - 01777; + yy[E] = r; + p = &yy[M + 1]; +#ifdef IBMPC + *p++ = *(--e); + *p++ = *(--e); + *p++ = *(--e); +#endif +#ifdef MIEEE + ++e; + *p++ = *e++; + *p++ = *e++; + *p++ = *e++; +#endif + eshift (yy, -5); + if (denorm) + { /* if zero exponent, then normalize the significand */ + if ((k = enormlz (yy)) > NBITS) + ecleazs (yy); + else + yy[E] -= (unsigned EMUSHORT) (k - 1); + } + emovo (yy, y); +#endif /* not DEC */ +} + +void +e64toe (pe, y) + unsigned EMUSHORT *pe, *y; +{ + unsigned EMUSHORT yy[NI]; + unsigned EMUSHORT *e, *p, *q; + int i; + + e = pe; + p = yy; + for (i = 0; i < NE - 5; i++) + *p++ = 0; +#ifdef IBMPC + for (i = 0; i < 5; i++) + *p++ = *e++; +#endif +#ifdef DEC + for (i = 0; i < 5; i++) + *p++ = *e++; +#endif +#ifdef MIEEE + p = &yy[0] + (NE - 1); + *p-- = *e++; + ++e; + for (i = 0; i < 4; i++) + *p-- = *e++; +#endif + p = yy; + q = y; +#ifdef INFINITY + if (*p == 0x7fff) + { +#ifdef NANS +#ifdef IBMPC + for (i = 0; i < 4; i++) + { + if (pe[i] != 0) + { + enan (y); + return; + } + } +#else + for (i = 1; i <= 4; i++) + { + if (pe[i] != 0) + { + enan (y); + return; + } + } +#endif +#endif /* NANS */ + eclear (y); + einfin (y); + if (*p & 0x8000) + eneg (y); + return; + } +#endif /* INFINITY */ + for (i = 0; i < NE; i++) + *q++ = *p++; +} + + +/* +; Convert IEEE single precision to e type +; float d; +; unsigned EMUSHORT x[N+2]; +; dtox (&d, x); +*/ +void +e24toe (pe, y) + unsigned EMUSHORT *pe, *y; +{ + register unsigned EMUSHORT r; + register unsigned EMUSHORT *e, *p; + unsigned EMUSHORT yy[NI]; + int denorm, k; + + e = pe; + denorm = 0; /* flag if denormalized number */ + ecleaz (yy); +#ifdef IBMPC + e += 1; +#endif +#ifdef DEC + e += 1; +#endif + r = *e; + yy[0] = 0; + if (r & 0x8000) + yy[0] = 0xffff; + yy[M] = (r & 0x7f) | 0200; + r &= ~0x807f; /* strip sign and 7 significand bits */ +#ifdef INFINITY + if (r == 0x7f80) + { +#ifdef NANS +#ifdef MIEEE + if (((pe[0] & 0x7f) != 0) || (pe[1] != 0)) + { + enan (y); + return; + } +#else + if (((pe[1] & 0x7f) != 0) || (pe[0] != 0)) + { + enan (y); + return; + } +#endif +#endif /* NANS */ + eclear (y); + einfin (y); + if (yy[0]) + eneg (y); + return; + } +#endif /* INFINITY */ + r >>= 7; + /* If zero exponent, then the significand is denormalized. + * So, take back the understood high significand bit. */ + if (r == 0) + { + denorm = 1; + yy[M] &= ~0200; + } + r += EXONE - 0177; + yy[E] = r; + p = &yy[M + 1]; +#ifdef IBMPC + *p++ = *(--e); +#endif +#ifdef DEC + *p++ = *(--e); +#endif +#ifdef MIEEE + ++e; + *p++ = *e++; +#endif + eshift (yy, -8); + if (denorm) + { /* if zero exponent, then normalize the significand */ + if ((k = enormlz (yy)) > NBITS) + ecleazs (yy); + else + yy[E] -= (unsigned EMUSHORT) (k - 1); + } + emovo (yy, y); +} + + +void +etoe64 (x, e) + unsigned EMUSHORT *x, *e; +{ + unsigned EMUSHORT xi[NI]; + EMULONG exp; + int rndsav; + +#ifdef NANS + if (eisnan (x)) + { + make_nan (e, XFmode); + return; + } +#endif + emovi (x, xi); + /* adjust exponent for offset */ + exp = (EMULONG) xi[E]; +#ifdef INFINITY + if (eisinf (x)) + goto nonorm; +#endif + /* round off to nearest or even */ + rndsav = rndprc; + rndprc = 64; + emdnorm (xi, 0, 0, exp, 64); + rndprc = rndsav; + nonorm: + toe64 (xi, e); +} + +/* move out internal format to ieee long double */ +static void +toe64 (a, b) + unsigned EMUSHORT *a, *b; +{ + register unsigned EMUSHORT *p, *q; + unsigned EMUSHORT i; + +#ifdef NANS + if (eiisnan (a)) + { + make_nan (b, XFmode); + return; + } +#endif + p = a; +#ifdef MIEEE + q = b; +#else + q = b + 4; /* point to output exponent */ +#if LONG_DOUBLE_TYPE_SIZE == 96 + /* Clear the last two bytes of 12-byte Intel format */ + *(q+1) = 0; +#endif +#endif + + /* combine sign and exponent */ + i = *p++; +#ifdef MIEEE + if (i) + *q++ = *p++ | 0x8000; + else + *q++ = *p++; + *q++ = 0; +#else + if (i) + *q-- = *p++ | 0x8000; + else + *q-- = *p++; +#endif + /* skip over guard word */ + ++p; + /* move the significand */ +#ifdef MIEEE + for (i = 0; i < 4; i++) + *q++ = *p++; +#else + for (i = 0; i < 4; i++) + *q-- = *p++; +#endif +} + + +/* +; e type to IEEE double precision +; double d; +; unsigned EMUSHORT x[NE]; +; etoe53 (x, &d); +*/ + +#ifdef DEC + +void +etoe53 (x, e) + unsigned EMUSHORT *x, *e; +{ + etodec (x, e); /* see etodec.c */ +} + +static void +toe53 (x, y) + unsigned EMUSHORT *x, *y; +{ + todec (x, y); +} + +#else + +void +etoe53 (x, e) + unsigned EMUSHORT *x, *e; +{ + unsigned EMUSHORT xi[NI]; + EMULONG exp; + int rndsav; + +#ifdef NANS + if (eisnan (x)) + { + make_nan (e, DFmode); + return; + } +#endif + emovi (x, xi); + /* adjust exponent for offsets */ + exp = (EMULONG) xi[E] - (EXONE - 0x3ff); +#ifdef INFINITY + if (eisinf (x)) + goto nonorm; +#endif + /* round off to nearest or even */ + rndsav = rndprc; + rndprc = 53; + emdnorm (xi, 0, 0, exp, 64); + rndprc = rndsav; + nonorm: + toe53 (xi, e); +} + + +static void +toe53 (x, y) + unsigned EMUSHORT *x, *y; +{ + unsigned EMUSHORT i; + unsigned EMUSHORT *p; + +#ifdef NANS + if (eiisnan (x)) + { + make_nan (y, DFmode); + return; + } +#endif + p = &x[0]; +#ifdef IBMPC + y += 3; +#endif + *y = 0; /* output high order */ + if (*p++) + *y = 0x8000; /* output sign bit */ + + i = *p++; + if (i >= (unsigned int) 2047) + { /* Saturate at largest number less than infinity. */ +#ifdef INFINITY + *y |= 0x7ff0; +#ifdef IBMPC + *(--y) = 0; + *(--y) = 0; + *(--y) = 0; +#endif +#ifdef MIEEE + ++y; + *y++ = 0; + *y++ = 0; + *y++ = 0; +#endif +#else + *y |= (unsigned EMUSHORT) 0x7fef; +#ifdef IBMPC + *(--y) = 0xffff; + *(--y) = 0xffff; + *(--y) = 0xffff; +#endif +#ifdef MIEEE + ++y; + *y++ = 0xffff; + *y++ = 0xffff; + *y++ = 0xffff; +#endif +#endif + return; + } + if (i == 0) + { + eshift (x, 4); + } + else + { + i <<= 4; + eshift (x, 5); + } + i |= *p++ & (unsigned EMUSHORT) 0x0f; /* *p = xi[M] */ + *y |= (unsigned EMUSHORT) i; /* high order output already has sign bit set */ +#ifdef IBMPC + *(--y) = *p++; + *(--y) = *p++; + *(--y) = *p; +#endif +#ifdef MIEEE + ++y; + *y++ = *p++; + *y++ = *p++; + *y++ = *p++; +#endif +} + +#endif /* not DEC */ + + + +/* +; e type to IEEE single precision +; float d; +; unsigned EMUSHORT x[N+2]; +; xtod (x, &d); +*/ +void +etoe24 (x, e) + unsigned EMUSHORT *x, *e; +{ + EMULONG exp; + unsigned EMUSHORT xi[NI]; + int rndsav; + +#ifdef NANS + if (eisnan (x)) + { + make_nan (e, SFmode); + return; + } +#endif + emovi (x, xi); + /* adjust exponent for offsets */ + exp = (EMULONG) xi[E] - (EXONE - 0177); +#ifdef INFINITY + if (eisinf (x)) + goto nonorm; +#endif + /* round off to nearest or even */ + rndsav = rndprc; + rndprc = 24; + emdnorm (xi, 0, 0, exp, 64); + rndprc = rndsav; + nonorm: + toe24 (xi, e); +} + +static void +toe24 (x, y) + unsigned EMUSHORT *x, *y; +{ + unsigned EMUSHORT i; + unsigned EMUSHORT *p; + +#ifdef NANS + if (eiisnan (x)) + { + make_nan (y, SFmode); + return; + } +#endif + p = &x[0]; +#ifdef IBMPC + y += 1; +#endif +#ifdef DEC + y += 1; +#endif + *y = 0; /* output high order */ + if (*p++) + *y = 0x8000; /* output sign bit */ + + i = *p++; +/* Handle overflow cases. */ + if (i >= 255) + { +#ifdef INFINITY + *y |= (unsigned EMUSHORT) 0x7f80; +#ifdef IBMPC + *(--y) = 0; +#endif +#ifdef DEC + *(--y) = 0; +#endif +#ifdef MIEEE + ++y; + *y = 0; +#endif +#else /* no INFINITY */ + *y |= (unsigned EMUSHORT) 0x7f7f; +#ifdef IBMPC + *(--y) = 0xffff; +#endif +#ifdef DEC + *(--y) = 0xffff; +#endif +#ifdef MIEEE + ++y; + *y = 0xffff; +#endif +#ifdef ERANGE + errno = ERANGE; +#endif +#endif /* no INFINITY */ + return; + } + if (i == 0) + { + eshift (x, 7); + } + else + { + i <<= 7; + eshift (x, 8); + } + i |= *p++ & (unsigned EMUSHORT) 0x7f; /* *p = xi[M] */ + *y |= i; /* high order output already has sign bit set */ +#ifdef IBMPC + *(--y) = *p; +#endif +#ifdef DEC + *(--y) = *p; +#endif +#ifdef MIEEE + ++y; + *y = *p; +#endif +} + + +/* Compare two e type numbers. + * + * unsigned EMUSHORT a[NE], b[NE]; + * ecmp (a, b); + * + * returns +1 if a > b + * 0 if a == b + * -1 if a < b + * -2 if either a or b is a NaN. + */ +int +ecmp (a, b) + unsigned EMUSHORT *a, *b; +{ + unsigned EMUSHORT ai[NI], bi[NI]; + register unsigned EMUSHORT *p, *q; + register int i; + int msign; + +#ifdef NANS + if (eisnan (a) || eisnan (b)) + return (-2); +#endif + emovi (a, ai); + p = ai; + emovi (b, bi); + q = bi; + + if (*p != *q) + { /* the signs are different */ + /* -0 equals + 0 */ + for (i = 1; i < NI - 1; i++) + { + if (ai[i] != 0) + goto nzro; + if (bi[i] != 0) + goto nzro; + } + return (0); + nzro: + if (*p == 0) + return (1); + else + return (-1); + } + /* both are the same sign */ + if (*p == 0) + msign = 1; + else + msign = -1; + i = NI - 1; + do + { + if (*p++ != *q++) + { + goto diff; + } + } + while (--i > 0); + + return (0); /* equality */ + + + + diff: + + if (*(--p) > *(--q)) + return (msign); /* p is bigger */ + else + return (-msign); /* p is littler */ +} + + + + +/* Find nearest integer to x = floor (x + 0.5) + * + * unsigned EMUSHORT x[NE], y[NE] + * eround (x, y); + */ +void +eround (x, y) + unsigned EMUSHORT *x, *y; +{ + eadd (ehalf, x, y); + efloor (y, y); +} + + + + +/* +; convert long integer to e type +; +; long l; +; unsigned EMUSHORT x[NE]; +; ltoe (&l, x); +; note &l is the memory address of l +*/ +void +ltoe (lp, y) + long *lp; /* lp is the memory address of a long integer */ + unsigned EMUSHORT *y; /* y is the address of a short */ +{ + unsigned EMUSHORT yi[NI]; + unsigned long ll; + int k; + + ecleaz (yi); + if (*lp < 0) + { + /* make it positive */ + ll = (unsigned long) (-(*lp)); + yi[0] = 0xffff; /* put correct sign in the e type number */ + } + else + { + ll = (unsigned long) (*lp); + } + /* move the long integer to yi significand area */ +#if HOST_BITS_PER_LONG == 64 + yi[M] = (unsigned EMUSHORT) (ll >> 48); + yi[M + 1] = (unsigned EMUSHORT) (ll >> 32); + yi[M + 2] = (unsigned EMUSHORT) (ll >> 16); + yi[M + 3] = (unsigned EMUSHORT) ll; + yi[E] = EXONE + 47; /* exponent if normalize shift count were 0 */ +#else + yi[M] = (unsigned EMUSHORT) (ll >> 16); + yi[M + 1] = (unsigned EMUSHORT) ll; + yi[E] = EXONE + 15; /* exponent if normalize shift count were 0 */ +#endif + + if ((k = enormlz (yi)) > NBITS)/* normalize the significand */ + ecleaz (yi); /* it was zero */ + else + yi[E] -= (unsigned EMUSHORT) k;/* subtract shift count from exponent */ + emovo (yi, y); /* output the answer */ +} + +/* +; convert unsigned long integer to e type +; +; unsigned long l; +; unsigned EMUSHORT x[NE]; +; ltox (&l, x); +; note &l is the memory address of l +*/ +void +ultoe (lp, y) + unsigned long *lp; /* lp is the memory address of a long integer */ + unsigned EMUSHORT *y; /* y is the address of a short */ +{ + unsigned EMUSHORT yi[NI]; + unsigned long ll; + int k; + + ecleaz (yi); + ll = *lp; + + /* move the long integer to ayi significand area */ +#if HOST_BITS_PER_LONG == 64 + yi[M] = (unsigned EMUSHORT) (ll >> 48); + yi[M + 1] = (unsigned EMUSHORT) (ll >> 32); + yi[M + 2] = (unsigned EMUSHORT) (ll >> 16); + yi[M + 3] = (unsigned EMUSHORT) ll; + yi[E] = EXONE + 47; /* exponent if normalize shift count were 0 */ +#else + yi[M] = (unsigned EMUSHORT) (ll >> 16); + yi[M + 1] = (unsigned EMUSHORT) ll; + yi[E] = EXONE + 15; /* exponent if normalize shift count were 0 */ +#endif + + if ((k = enormlz (yi)) > NBITS)/* normalize the significand */ + ecleaz (yi); /* it was zero */ + else + yi[E] -= (unsigned EMUSHORT) k; /* subtract shift count from exponent */ + emovo (yi, y); /* output the answer */ +} + + +/* +; Find long integer and fractional parts + +; long i; +; unsigned EMUSHORT x[NE], frac[NE]; +; xifrac (x, &i, frac); + + The integer output has the sign of the input. The fraction is +the positive fractional part of abs (x). +*/ +void +eifrac (x, i, frac) + unsigned EMUSHORT *x; + long *i; + unsigned EMUSHORT *frac; +{ + unsigned EMUSHORT xi[NI]; + int j, k; + unsigned long ll; + + emovi (x, xi); + k = (int) xi[E] - (EXONE - 1); + if (k <= 0) + { + /* if exponent <= 0, integer = 0 and real output is fraction */ + *i = 0L; + emovo (xi, frac); + return; + } + if (k > (HOST_BITS_PER_LONG - 1)) + { + /* long integer overflow: output large integer + and correct fraction */ + if (xi[0]) + *i = ((unsigned long) 1) << (HOST_BITS_PER_LONG - 1); + else + *i = (((unsigned long) 1) << (HOST_BITS_PER_LONG - 1)) - 1; + eshift (xi, k); + if (extra_warnings) + warning ("overflow on truncation to integer"); + } + else if (k > 16) + { + /* Shift more than 16 bits: first shift up k-16 mod 16, + then shift up by 16's. */ + j = k - ((k >> 4) << 4); + eshift (xi, j); + ll = xi[M]; + k -= j; + do + { + eshup6 (xi); + ll = (ll << 16) | xi[M]; + } + while ((k -= 16) > 0); + *i = ll; + if (xi[0]) + *i = -(*i); + } + else + { + /* shift not more than 16 bits */ + eshift (xi, k); + *i = (long) xi[M] & 0xffff; + if (xi[0]) + *i = -(*i); + } + xi[0] = 0; + xi[E] = EXONE - 1; + xi[M] = 0; + if ((k = enormlz (xi)) > NBITS) + ecleaz (xi); + else + xi[E] -= (unsigned EMUSHORT) k; + + emovo (xi, frac); +} + + +/* Find unsigned long integer and fractional parts. + A negative e type input yields integer output = 0 + but correct fraction. */ + +void +euifrac (x, i, frac) + unsigned EMUSHORT *x; + unsigned long *i; + unsigned EMUSHORT *frac; +{ + unsigned long ll; + unsigned EMUSHORT xi[NI]; + int j, k; + + emovi (x, xi); + k = (int) xi[E] - (EXONE - 1); + if (k <= 0) + { + /* if exponent <= 0, integer = 0 and argument is fraction */ + *i = 0L; + emovo (xi, frac); + return; + } + if (k > HOST_BITS_PER_LONG) + { + /* Long integer overflow: output large integer + and correct fraction. + Note, the BSD microvax compiler says that ~(0UL) + is a syntax error. */ + *i = ~(0L); + eshift (xi, k); + if (extra_warnings) + warning ("overflow on truncation to unsigned integer"); + } + else if (k > 16) + { + /* Shift more than 16 bits: first shift up k-16 mod 16, + then shift up by 16's. */ + j = k - ((k >> 4) << 4); + eshift (xi, j); + ll = xi[M]; + k -= j; + do + { + eshup6 (xi); + ll = (ll << 16) | xi[M]; + } + while ((k -= 16) > 0); + *i = ll; + } + else + { + /* shift not more than 16 bits */ + eshift (xi, k); + *i = (long) xi[M] & 0xffff; + } + + if (xi[0]) /* A negative value yields unsigned integer 0. */ + *i = 0L; + xi[0] = 0; + xi[E] = EXONE - 1; + xi[M] = 0; + if ((k = enormlz (xi)) > NBITS) + ecleaz (xi); + else + xi[E] -= (unsigned EMUSHORT) k; + + emovo (xi, frac); +} + + + +/* +; Shift significand +; +; Shifts significand area up or down by the number of bits +; given by the variable sc. +*/ +int +eshift (x, sc) + unsigned EMUSHORT *x; + int sc; +{ + unsigned EMUSHORT lost; + unsigned EMUSHORT *p; + + if (sc == 0) + return (0); + + lost = 0; + p = x + NI - 1; + + if (sc < 0) + { + sc = -sc; + while (sc >= 16) + { + lost |= *p; /* remember lost bits */ + eshdn6 (x); + sc -= 16; + } + + while (sc >= 8) + { + lost |= *p & 0xff; + eshdn8 (x); + sc -= 8; + } + + while (sc > 0) + { + lost |= *p & 1; + eshdn1 (x); + sc -= 1; + } + } + else + { + while (sc >= 16) + { + eshup6 (x); + sc -= 16; + } + + while (sc >= 8) + { + eshup8 (x); + sc -= 8; + } + + while (sc > 0) + { + eshup1 (x); + sc -= 1; + } + } + if (lost) + lost = 1; + return ((int) lost); +} + + + +/* +; normalize +; +; Shift normalizes the significand area pointed to by argument +; shift count (up = positive) is returned. +*/ +int +enormlz (x) + unsigned EMUSHORT x[]; +{ + register unsigned EMUSHORT *p; + int sc; + + sc = 0; + p = &x[M]; + if (*p != 0) + goto normdn; + ++p; + if (*p & 0x8000) + return (0); /* already normalized */ + while (*p == 0) + { + eshup6 (x); + sc += 16; + /* With guard word, there are NBITS+16 bits available. + * return true if all are zero. + */ + if (sc > NBITS) + return (sc); + } + /* see if high byte is zero */ + while ((*p & 0xff00) == 0) + { + eshup8 (x); + sc += 8; + } + /* now shift 1 bit at a time */ + while ((*p & 0x8000) == 0) + { + eshup1 (x); + sc += 1; + if (sc > NBITS) + { + mtherr ("enormlz", UNDERFLOW); + return (sc); + } + } + return (sc); + + /* Normalize by shifting down out of the high guard word + of the significand */ + normdn: + + if (*p & 0xff00) + { + eshdn8 (x); + sc -= 8; + } + while (*p != 0) + { + eshdn1 (x); + sc -= 1; + + if (sc < -NBITS) + { + mtherr ("enormlz", OVERFLOW); + return (sc); + } + } + return (sc); +} + + + + +/* Convert e type number to decimal format ASCII string. + * The constants are for 64 bit precision. + */ + +#define NTEN 12 +#define MAXP 4096 + +static unsigned EMUSHORT etens[NTEN + 1][NE] = +{ + {0xc94c, 0x979a, 0x8a20, 0x5202, 0xc460, 0x7525,}, /* 10**4096 */ + {0xa74d, 0x5de4, 0xc53d, 0x3b5d, 0x9e8b, 0x5a92,}, /* 10**2048 */ + {0x650d, 0x0c17, 0x8175, 0x7586, 0xc976, 0x4d48,}, + {0xcc65, 0x91c6, 0xa60e, 0xa0ae, 0xe319, 0x46a3,}, + {0xddbc, 0xde8d, 0x9df9, 0xebfb, 0xaa7e, 0x4351,}, + {0xc66f, 0x8cdf, 0x80e9, 0x47c9, 0x93ba, 0x41a8,}, + {0x3cbf, 0xa6d5, 0xffcf, 0x1f49, 0xc278, 0x40d3,}, + {0xf020, 0xb59d, 0x2b70, 0xada8, 0x9dc5, 0x4069,}, + {0x0000, 0x0000, 0x0400, 0xc9bf, 0x8e1b, 0x4034,}, + {0x0000, 0x0000, 0x0000, 0x2000, 0xbebc, 0x4019,}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x9c40, 0x400c,}, + {0x0000, 0x0000, 0x0000, 0x0000, 0xc800, 0x4005,}, + {0x0000, 0x0000, 0x0000, 0x0000, 0xa000, 0x4002,}, /* 10**1 */ +}; + +static unsigned EMUSHORT emtens[NTEN + 1][NE] = +{ + {0x2de4, 0x9fde, 0xd2ce, 0x04c8, 0xa6dd, 0x0ad8,}, /* 10**-4096 */ + {0x4925, 0x2de4, 0x3436, 0x534f, 0xceae, 0x256b,}, /* 10**-2048 */ + {0x87a6, 0xc0bd, 0xda57, 0x82a5, 0xa2a6, 0x32b5,}, + {0x7133, 0xd21c, 0xdb23, 0xee32, 0x9049, 0x395a,}, + {0xfa91, 0x1939, 0x637a, 0x4325, 0xc031, 0x3cac,}, + {0xac7d, 0xe4a0, 0x64bc, 0x467c, 0xddd0, 0x3e55,}, + {0x3f24, 0xe9a5, 0xa539, 0xea27, 0xa87f, 0x3f2a,}, + {0x67de, 0x94ba, 0x4539, 0x1ead, 0xcfb1, 0x3f94,}, + {0x4c2f, 0xe15b, 0xc44d, 0x94be, 0xe695, 0x3fc9,}, + {0xfdc2, 0xcefc, 0x8461, 0x7711, 0xabcc, 0x3fe4,}, + {0xd3c3, 0x652b, 0xe219, 0x1758, 0xd1b7, 0x3ff1,}, + {0x3d71, 0xd70a, 0x70a3, 0x0a3d, 0xa3d7, 0x3ff8,}, + {0xcccd, 0xcccc, 0xcccc, 0xcccc, 0xcccc, 0x3ffb,}, /* 10**-1 */ +}; + +void +e24toasc (x, string, ndigs) + unsigned EMUSHORT x[]; + char *string; + int ndigs; +{ + unsigned EMUSHORT w[NI]; + + e24toe (x, w); + etoasc (w, string, ndigs); +} + + +void +e53toasc (x, string, ndigs) + unsigned EMUSHORT x[]; + char *string; + int ndigs; +{ + unsigned EMUSHORT w[NI]; + + e53toe (x, w); + etoasc (w, string, ndigs); +} + + +void +e64toasc (x, string, ndigs) + unsigned EMUSHORT x[]; + char *string; + int ndigs; +{ + unsigned EMUSHORT w[NI]; + + e64toe (x, w); + etoasc (w, string, ndigs); +} + + +static char wstring[80]; /* working storage for ASCII output */ + +void +etoasc (x, string, ndigs) + unsigned EMUSHORT x[]; + char *string; + int ndigs; +{ + EMUSHORT digit; + unsigned EMUSHORT y[NI], t[NI], u[NI], w[NI]; + unsigned EMUSHORT *p, *r, *ten; + unsigned EMUSHORT sign; + int i, j, k, expon, rndsav; + char *s, *ss; + unsigned EMUSHORT m; + + + rndsav = rndprc; + ss = string; + s = wstring; + *ss = '\0'; + *s = '\0'; +#ifdef NANS + if (eisnan (x)) + { + sprintf (wstring, " NaN "); + goto bxit; + } +#endif + rndprc = NBITS; /* set to full precision */ + emov (x, y); /* retain external format */ + if (y[NE - 1] & 0x8000) + { + sign = 0xffff; + y[NE - 1] &= 0x7fff; + } + else + { + sign = 0; + } + expon = 0; + ten = &etens[NTEN][0]; + emov (eone, t); + /* Test for zero exponent */ + if (y[NE - 1] == 0) + { + for (k = 0; k < NE - 1; k++) + { + if (y[k] != 0) + goto tnzro; /* denormalized number */ + } + goto isone; /* legal all zeros */ + } + tnzro: + + /* Test for infinity. */ + if (y[NE - 1] == 0x7fff) + { + if (sign) + sprintf (wstring, " -Infinity "); + else + sprintf (wstring, " Infinity "); + goto bxit; + } + + /* Test for exponent nonzero but significand denormalized. + * This is an error condition. + */ + if ((y[NE - 1] != 0) && ((y[NE - 2] & 0x8000) == 0)) + { + mtherr ("etoasc", DOMAIN); + sprintf (wstring, "NaN"); + goto bxit; + } + + /* Compare to 1.0 */ + i = ecmp (eone, y); + if (i == 0) + goto isone; + + if (i == -2) + abort (); + + if (i < 0) + { /* Number is greater than 1 */ + /* Convert significand to an integer and strip trailing decimal zeros. */ + emov (y, u); + u[NE - 1] = EXONE + NBITS - 1; + + p = &etens[NTEN - 4][0]; + m = 16; + do + { + ediv (p, u, t); + efloor (t, w); + for (j = 0; j < NE - 1; j++) + { + if (t[j] != w[j]) + goto noint; + } + emov (t, u); + expon += (int) m; + noint: + p += NE; + m >>= 1; + } + while (m != 0); + + /* Rescale from integer significand */ + u[NE - 1] += y[NE - 1] - (unsigned int) (EXONE + NBITS - 1); + emov (u, y); + /* Find power of 10 */ + emov (eone, t); + m = MAXP; + p = &etens[0][0]; + /* An unordered compare result shouldn't happen here. */ + while (ecmp (ten, u) <= 0) + { + if (ecmp (p, u) <= 0) + { + ediv (p, u, u); + emul (p, t, t); + expon += (int) m; + } + m >>= 1; + if (m == 0) + break; + p += NE; + } + } + else + { /* Number is less than 1.0 */ + /* Pad significand with trailing decimal zeros. */ + if (y[NE - 1] == 0) + { + while ((y[NE - 2] & 0x8000) == 0) + { + emul (ten, y, y); + expon -= 1; + } + } + else + { + emovi (y, w); + for (i = 0; i < NDEC + 1; i++) + { + if ((w[NI - 1] & 0x7) != 0) + break; + /* multiply by 10 */ + emovz (w, u); + eshdn1 (u); + eshdn1 (u); + eaddm (w, u); + u[1] += 3; + while (u[2] != 0) + { + eshdn1 (u); + u[1] += 1; + } + if (u[NI - 1] != 0) + break; + if (eone[NE - 1] <= u[1]) + break; + emovz (u, w); + expon -= 1; + } + emovo (w, y); + } + k = -MAXP; + p = &emtens[0][0]; + r = &etens[0][0]; + emov (y, w); + emov (eone, t); + while (ecmp (eone, w) > 0) + { + if (ecmp (p, w) >= 0) + { + emul (r, w, w); + emul (r, t, t); + expon += k; + } + k /= 2; + if (k == 0) + break; + p += NE; + r += NE; + } + ediv (t, eone, t); + } + isone: + /* Find the first (leading) digit. */ + emovi (t, w); + emovz (w, t); + emovi (y, w); + emovz (w, y); + eiremain (t, y); + digit = equot[NI - 1]; + while ((digit == 0) && (ecmp (y, ezero) != 0)) + { + eshup1 (y); + emovz (y, u); + eshup1 (u); + eshup1 (u); + eaddm (u, y); + eiremain (t, y); + digit = equot[NI - 1]; + expon -= 1; + } + s = wstring; + if (sign) + *s++ = '-'; + else + *s++ = ' '; + /* Examine number of digits requested by caller. */ + if (ndigs < 0) + ndigs = 0; + if (ndigs > NDEC) + ndigs = NDEC; + if (digit == 10) + { + *s++ = '1'; + *s++ = '.'; + if (ndigs > 0) + { + *s++ = '0'; + ndigs -= 1; + } + expon += 1; + } + else + { + *s++ = (char )digit + '0'; + *s++ = '.'; + } + /* Generate digits after the decimal point. */ + for (k = 0; k <= ndigs; k++) + { + /* multiply current number by 10, without normalizing */ + eshup1 (y); + emovz (y, u); + eshup1 (u); + eshup1 (u); + eaddm (u, y); + eiremain (t, y); + *s++ = (char) equot[NI - 1] + '0'; + } + digit = equot[NI - 1]; + --s; + ss = s; + /* round off the ASCII string */ + if (digit > 4) + { + /* Test for critical rounding case in ASCII output. */ + if (digit == 5) + { + emovo (y, t); + if (ecmp (t, ezero) != 0) + goto roun; /* round to nearest */ + if ((*(s - 1) & 1) == 0) + goto doexp; /* round to even */ + } + /* Round up and propagate carry-outs */ + roun: + --s; + k = *s & 0x7f; + /* Carry out to most significant digit? */ + if (k == '.') + { + --s; + k = *s; + k += 1; + *s = (char) k; + /* Most significant digit carries to 10? */ + if (k > '9') + { + expon += 1; + *s = '1'; + } + goto doexp; + } + /* Round up and carry out from less significant digits */ + k += 1; + *s = (char) k; + if (k > '9') + { + *s = '0'; + goto roun; + } + } + doexp: + /* + if (expon >= 0) + sprintf (ss, "e+%d", expon); + else + sprintf (ss, "e%d", expon); + */ + sprintf (ss, "e%d", expon); + bxit: + rndprc = rndsav; + /* copy out the working string */ + s = string; + ss = wstring; + while (*ss == ' ') /* strip possible leading space */ + ++ss; + while ((*s++ = *ss++) != '\0') + ; +} + + + + +/* +; ASCTOQ +; ASCTOQ.MAC LATEST REV: 11 JAN 84 +; SLM, 3 JAN 78 +; +; Convert ASCII string to quadruple precision floating point +; +; Numeric input is free field decimal number +; with max of 15 digits with or without +; decimal point entered as ASCII from teletype. +; Entering E after the number followed by a second +; number causes the second number to be interpreted +; as a power of 10 to be multiplied by the first number +; (i.e., "scientific" notation). +; +; Usage: +; asctoq (string, q); +*/ + +/* ASCII to single */ +void +asctoe24 (s, y) + char *s; + unsigned EMUSHORT *y; +{ + asctoeg (s, y, 24); +} + + +/* ASCII to double */ +void +asctoe53 (s, y) + char *s; + unsigned EMUSHORT *y; +{ +#ifdef DEC + asctoeg (s, y, 56); +#else + asctoeg (s, y, 53); +#endif +} + + +/* ASCII to long double */ +void +asctoe64 (s, y) + char *s; + unsigned EMUSHORT *y; +{ + asctoeg (s, y, 64); +} + +/* ASCII to super double */ +void +asctoe (s, y) + char *s; + unsigned EMUSHORT *y; +{ + asctoeg (s, y, NBITS); +} + +/* Space to make a copy of the input string: */ +static char lstr[82]; + +void +asctoeg (ss, y, oprec) + char *ss; + unsigned EMUSHORT *y; + int oprec; +{ + unsigned EMUSHORT yy[NI], xt[NI], tt[NI]; + int esign, decflg, sgnflg, nexp, exp, prec, lost; + int k, trail, c, rndsav; + EMULONG lexp; + unsigned EMUSHORT nsign, *p; + char *sp, *s; + + /* Copy the input string. */ + s = ss; + while (*s == ' ') /* skip leading spaces */ + ++s; + sp = lstr; + for (k = 0; k < 79; k++) + { + if ((*sp++ = *s++) == '\0') + break; + } + *sp = '\0'; + s = lstr; + + rndsav = rndprc; + rndprc = NBITS; /* Set to full precision */ + lost = 0; + nsign = 0; + decflg = 0; + sgnflg = 0; + nexp = 0; + exp = 0; + prec = 0; + ecleaz (yy); + trail = 0; + + nxtcom: + k = *s - '0'; + if ((k >= 0) && (k <= 9)) + { + /* Ignore leading zeros */ + if ((prec == 0) && (decflg == 0) && (k == 0)) + goto donchr; + /* Identify and strip trailing zeros after the decimal point. */ + if ((trail == 0) && (decflg != 0)) + { + sp = s; + while ((*sp >= '0') && (*sp <= '9')) + ++sp; + /* Check for syntax error */ + c = *sp & 0x7f; + if ((c != 'e') && (c != 'E') && (c != '\0') + && (c != '\n') && (c != '\r') && (c != ' ') + && (c != ',')) + goto error; + --sp; + while (*sp == '0') + *sp-- = 'z'; + trail = 1; + if (*s == 'z') + goto donchr; + } + /* If enough digits were given to more than fill up the yy register, + * continuing until overflow into the high guard word yy[2] + * guarantees that there will be a roundoff bit at the top + * of the low guard word after normalization. + */ + if (yy[2] == 0) + { + if (decflg) + nexp += 1; /* count digits after decimal point */ + eshup1 (yy); /* multiply current number by 10 */ + emovz (yy, xt); + eshup1 (xt); + eshup1 (xt); + eaddm (xt, yy); + ecleaz (xt); + xt[NI - 2] = (unsigned EMUSHORT) k; + eaddm (xt, yy); + } + else + { + lost |= k; + } + prec += 1; + goto donchr; + } + + switch (*s) + { + case 'z': + break; + case 'E': + case 'e': + goto expnt; + case '.': /* decimal point */ + if (decflg) + goto error; + ++decflg; + break; + case '-': + nsign = 0xffff; + if (sgnflg) + goto error; + ++sgnflg; + break; + case '+': + if (sgnflg) + goto error; + ++sgnflg; + break; + case ',': + case ' ': + case '\0': + case '\n': + case '\r': + goto daldone; + case 'i': + case 'I': + goto infinite; + default: + error: +#ifdef NANS + einan (yy); +#else + mtherr ("asctoe", DOMAIN); + eclear (yy); +#endif + goto aexit; + } + donchr: + ++s; + goto nxtcom; + + /* Exponent interpretation */ + expnt: + + esign = 1; + exp = 0; + ++s; + /* check for + or - */ + if (*s == '-') + { + esign = -1; + ++s; + } + if (*s == '+') + ++s; + while ((*s >= '0') && (*s <= '9')) + { + exp *= 10; + exp += *s++ - '0'; + if (exp > 4956) + { + if (esign < 0) + goto zero; + else + goto infinite; + } + } + if (esign < 0) + exp = -exp; + if (exp > 4932) + { + infinite: + ecleaz (yy); + yy[E] = 0x7fff; /* infinity */ + goto aexit; + } + if (exp < -4956) + { + zero: + ecleaz (yy); + goto aexit; + } + + daldone: + nexp = exp - nexp; + /* Pad trailing zeros to minimize power of 10, per IEEE spec. */ + while ((nexp > 0) && (yy[2] == 0)) + { + emovz (yy, xt); + eshup1 (xt); + eshup1 (xt); + eaddm (yy, xt); + eshup1 (xt); + if (xt[2] != 0) + break; + nexp -= 1; + emovz (xt, yy); + } + if ((k = enormlz (yy)) > NBITS) + { + ecleaz (yy); + goto aexit; + } + lexp = (EXONE - 1 + NBITS) - k; + emdnorm (yy, lost, 0, lexp, 64); + /* convert to external format */ + + + /* Multiply by 10**nexp. If precision is 64 bits, + * the maximum relative error incurred in forming 10**n + * for 0 <= n <= 324 is 8.2e-20, at 10**180. + * For 0 <= n <= 999, the peak relative error is 1.4e-19 at 10**947. + * For 0 >= n >= -999, it is -1.55e-19 at 10**-435. + */ + lexp = yy[E]; + if (nexp == 0) + { + k = 0; + goto expdon; + } + esign = 1; + if (nexp < 0) + { + nexp = -nexp; + esign = -1; + if (nexp > 4096) + { /* Punt. Can't handle this without 2 divides. */ + emovi (etens[0], tt); + lexp -= tt[E]; + k = edivm (tt, yy); + lexp += EXONE; + nexp -= 4096; + } + } + p = &etens[NTEN][0]; + emov (eone, xt); + exp = 1; + do + { + if (exp & nexp) + emul (p, xt, xt); + p -= NE; + exp = exp + exp; + } + while (exp <= MAXP); + + emovi (xt, tt); + if (esign < 0) + { + lexp -= tt[E]; + k = edivm (tt, yy); + lexp += EXONE; + } + else + { + lexp += tt[E]; + k = emulm (tt, yy); + lexp -= EXONE - 1; + } + + expdon: + + /* Round and convert directly to the destination type */ + if (oprec == 53) + lexp -= EXONE - 0x3ff; + else if (oprec == 24) + lexp -= EXONE - 0177; +#ifdef DEC + else if (oprec == 56) + lexp -= EXONE - 0201; +#endif + rndprc = oprec; + emdnorm (yy, k, 0, lexp, 64); + + aexit: + + rndprc = rndsav; + yy[0] = nsign; + switch (oprec) + { +#ifdef DEC + case 56: + todec (yy, y); /* see etodec.c */ + break; +#endif + case 53: + toe53 (yy, y); + break; + case 24: + toe24 (yy, y); + break; + case 64: + toe64 (yy, y); + break; + case NBITS: + emovo (yy, y); + break; + } +} + + + +/* y = largest integer not greater than x + * (truncated toward minus infinity) + * + * unsigned EMUSHORT x[NE], y[NE] + * + * efloor (x, y); + */ +static unsigned EMUSHORT bmask[] = +{ + 0xffff, + 0xfffe, + 0xfffc, + 0xfff8, + 0xfff0, + 0xffe0, + 0xffc0, + 0xff80, + 0xff00, + 0xfe00, + 0xfc00, + 0xf800, + 0xf000, + 0xe000, + 0xc000, + 0x8000, + 0x0000, +}; + +void +efloor (x, y) + unsigned EMUSHORT x[], y[]; +{ + register unsigned EMUSHORT *p; + int e, expon, i; + unsigned EMUSHORT f[NE]; + + emov (x, f); /* leave in external format */ + expon = (int) f[NE - 1]; + e = (expon & 0x7fff) - (EXONE - 1); + if (e <= 0) + { + eclear (y); + goto isitneg; + } + /* number of bits to clear out */ + e = NBITS - e; + emov (f, y); + if (e <= 0) + return; + + p = &y[0]; + while (e >= 16) + { + *p++ = 0; + e -= 16; + } + /* clear the remaining bits */ + *p &= bmask[e]; + /* truncate negatives toward minus infinity */ + isitneg: + + if ((unsigned EMUSHORT) expon & (unsigned EMUSHORT) 0x8000) + { + for (i = 0; i < NE - 1; i++) + { + if (f[i] != y[i]) + { + esub (eone, y, y); + break; + } + } + } +} + + +/* unsigned EMUSHORT x[], s[]; + * int *exp; + * + * efrexp (x, exp, s); + * + * Returns s and exp such that s * 2**exp = x and .5 <= s < 1. + * For example, 1.1 = 0.55 * 2**1 + * Handles denormalized numbers properly using long integer exp. + */ +void +efrexp (x, exp, s) + unsigned EMUSHORT x[]; + int *exp; + unsigned EMUSHORT s[]; +{ + unsigned EMUSHORT xi[NI]; + EMULONG li; + + emovi (x, xi); + li = (EMULONG) ((EMUSHORT) xi[1]); + + if (li == 0) + { + li -= enormlz (xi); + } + xi[1] = 0x3ffe; + emovo (xi, s); + *exp = (int) (li - 0x3ffe); +} + + + +/* unsigned EMUSHORT x[], y[]; + * long pwr2; + * + * eldexp (x, pwr2, y); + * + * Returns y = x * 2**pwr2. + */ +void +eldexp (x, pwr2, y) + unsigned EMUSHORT x[]; + int pwr2; + unsigned EMUSHORT y[]; +{ + unsigned EMUSHORT xi[NI]; + EMULONG li; + int i; + + emovi (x, xi); + li = xi[1]; + li += pwr2; + i = 0; + emdnorm (xi, i, i, li, 64); + emovo (xi, y); +} + + +/* c = remainder after dividing b by a + * Least significant integer quotient bits left in equot[]. + */ +void +eremain (a, b, c) + unsigned EMUSHORT a[], b[], c[]; +{ + unsigned EMUSHORT den[NI], num[NI]; + +#ifdef NANS + if ( eisinf (b) + || (ecmp (a, ezero) == 0) + || eisnan (a) + || eisnan (b)) + { + enan (c); + return; + } +#endif + if (ecmp (a, ezero) == 0) + { + mtherr ("eremain", SING); + eclear (c); + return; + } + emovi (a, den); + emovi (b, num); + eiremain (den, num); + /* Sign of remainder = sign of quotient */ + if (a[0] == b[0]) + num[0] = 0; + else + num[0] = 0xffff; + emovo (num, c); +} + +void +eiremain (den, num) + unsigned EMUSHORT den[], num[]; +{ + EMULONG ld, ln; + unsigned EMUSHORT j; + + ld = den[E]; + ld -= enormlz (den); + ln = num[E]; + ln -= enormlz (num); + ecleaz (equot); + while (ln >= ld) + { + if (ecmpm (den, num) <= 0) + { + esubm (den, num); + j = 1; + } + else + { + j = 0; + } + eshup1 (equot); + equot[NI - 1] |= j; + eshup1 (num); + ln -= 1; + } + emdnorm (num, 0, 0, ln, 0); +} + +/* mtherr.c + * + * Library common error handling routine + * + * + * + * SYNOPSIS: + * + * char *fctnam; + * int code; + * void mtherr (); + * + * mtherr (fctnam, code); + * + * + * + * DESCRIPTION: + * + * This routine may be called to report one of the following + * error conditions (in the include file mconf.h). + * + * Mnemonic Value Significance + * + * DOMAIN 1 argument domain error + * SING 2 function singularity + * OVERFLOW 3 overflow range error + * UNDERFLOW 4 underflow range error + * TLOSS 5 total loss of precision + * PLOSS 6 partial loss of precision + * INVALID 7 NaN - producing operation + * EDOM 33 Unix domain error code + * ERANGE 34 Unix range error code + * + * The default version of the file prints the function name, + * passed to it by the pointer fctnam, followed by the + * error condition. The display is directed to the standard + * output device. The routine then returns to the calling + * program. Users may wish to modify the program to abort by + * calling exit under severe error conditions such as domain + * errors. + * + * Since all error conditions pass control to this function, + * the display may be easily changed, eliminated, or directed + * to an error logging device. + * + * SEE ALSO: + * + * mconf.h + * + */ + +/* +Cephes Math Library Release 2.0: April, 1987 +Copyright 1984, 1987 by Stephen L. Moshier +Direct inquiries to 30 Frost Street, Cambridge, MA 02140 +*/ + +/* include "mconf.h" */ + +/* Notice: the order of appearance of the following + * messages is bound to the error codes defined + * in mconf.h. + */ +#define NMSGS 8 +static char *ermsg[NMSGS] = +{ + "unknown", /* error code 0 */ + "domain", /* error code 1 */ + "singularity", /* et seq. */ + "overflow", + "underflow", + "total loss of precision", + "partial loss of precision", + "invalid operation" +}; + +int merror = 0; +extern int merror; + +void +mtherr (name, code) + char *name; + int code; +{ + char errstr[80]; + + /* Display string passed by calling program, + * which is supposed to be the name of the + * function in which the error occurred. + */ + + /* Display error message defined + * by the code argument. + */ + if ((code <= 0) || (code >= NMSGS)) + code = 0; + sprintf (errstr, " %s %s error", name, ermsg[code]); + if (extra_warnings) + warning (errstr); + /* Set global error message word */ + merror = code + 1; + + /* Return to calling + * program + */ +} + +/* Here is etodec.c . + * + */ + +/* +; convert DEC double precision to e type +; double d; +; EMUSHORT e[NE]; +; dectoe (&d, e); +*/ +void +dectoe (d, e) + unsigned EMUSHORT *d; + unsigned EMUSHORT *e; +{ + unsigned EMUSHORT y[NI]; + register unsigned EMUSHORT r, *p; + + ecleaz (y); /* start with a zero */ + p = y; /* point to our number */ + r = *d; /* get DEC exponent word */ + if (*d & (unsigned int) 0x8000) + *p = 0xffff; /* fill in our sign */ + ++p; /* bump pointer to our exponent word */ + r &= 0x7fff; /* strip the sign bit */ + if (r == 0) /* answer = 0 if high order DEC word = 0 */ + goto done; + + + r >>= 7; /* shift exponent word down 7 bits */ + r += EXONE - 0201; /* subtract DEC exponent offset */ + /* add our e type exponent offset */ + *p++ = r; /* to form our exponent */ + + r = *d++; /* now do the high order mantissa */ + r &= 0177; /* strip off the DEC exponent and sign bits */ + r |= 0200; /* the DEC understood high order mantissa bit */ + *p++ = r; /* put result in our high guard word */ + + *p++ = *d++; /* fill in the rest of our mantissa */ + *p++ = *d++; + *p = *d; + + eshdn8 (y); /* shift our mantissa down 8 bits */ + done: + emovo (y, e); +} + + + +/* +; convert e type to DEC double precision +; double d; +; EMUSHORT e[NE]; +; etodec (e, &d); +*/ +#if 0 +static unsigned EMUSHORT decbit[NI] = {0, 0, 0, 0, 0, 0, 0200, 0}; + +void +etodec (x, d) + unsigned EMUSHORT *x, *d; +{ + unsigned EMUSHORT xi[NI]; + register unsigned EMUSHORT r; + int i, j; + + emovi (x, xi); + *d = 0; + if (xi[0] != 0) + *d = 0100000; + r = xi[E]; + if (r < (EXONE - 128)) + goto zout; + i = xi[M + 4]; + if ((i & 0200) != 0) + { + if ((i & 0377) == 0200) + { + if ((i & 0400) != 0) + { + /* check all less significant bits */ + for (j = M + 5; j < NI; j++) + { + if (xi[j] != 0) + goto yesrnd; + } + } + goto nornd; + } + yesrnd: + eaddm (decbit, xi); + r -= enormlz (xi); + } + + nornd: + + r -= EXONE; + r += 0201; + if (r < 0) + { + zout: + *d++ = 0; + *d++ = 0; + *d++ = 0; + *d++ = 0; + return; + } + if (r >= 0377) + { + *d++ = 077777; + *d++ = -1; + *d++ = -1; + *d++ = -1; + return; + } + r &= 0377; + r <<= 7; + eshup8 (xi); + xi[M] &= 0177; + r |= xi[M]; + *d++ |= r; + *d++ = xi[M + 1]; + *d++ = xi[M + 2]; + *d++ = xi[M + 3]; +} + +#else + +void +etodec (x, d) + unsigned EMUSHORT *x, *d; +{ + unsigned EMUSHORT xi[NI]; + EMULONG exp; + int rndsav; + + emovi (x, xi); + exp = (EMULONG) xi[E] - (EXONE - 0201); /* adjust exponent for offsets */ +/* round off to nearest or even */ + rndsav = rndprc; + rndprc = 56; + emdnorm (xi, 0, 0, exp, 64); + rndprc = rndsav; + todec (xi, d); +} + +void +todec (x, y) + unsigned EMUSHORT *x, *y; +{ + unsigned EMUSHORT i; + unsigned EMUSHORT *p; + + p = x; + *y = 0; + if (*p++) + *y = 0100000; + i = *p++; + if (i == 0) + { + *y++ = 0; + *y++ = 0; + *y++ = 0; + *y++ = 0; + return; + } + if (i > 0377) + { + *y++ |= 077777; + *y++ = 0xffff; + *y++ = 0xffff; + *y++ = 0xffff; +#ifdef ERANGE + errno = ERANGE; +#endif + return; + } + i &= 0377; + i <<= 7; + eshup8 (x); + x[M] &= 0177; + i |= x[M]; + *y++ |= i; + *y++ = x[M + 1]; + *y++ = x[M + 2]; + *y++ = x[M + 3]; +} + +#endif /* not 0 */ + + +/* Output a binary NaN bit pattern in the target machine's format. */ + +/* If special NaN bit patterns are required, define them in tm.h + as arrays of unsigned 16-bit shorts. Otherwise, use the default + patterns here. */ +#ifdef TFMODE_NAN +TFMODE_NAN; +#else +#ifdef MIEEE +unsigned EMUSHORT TFnan[8] = + {0x7fff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}; +#endif +#ifdef IBMPC +unsigned EMUSHORT TFnan[8] = {0, 0, 0, 0, 0, 0, 0x8000, 0xffff}; +#endif +#endif + +#ifdef XFMODE_NAN +XFMODE_NAN; +#else +#ifdef MIEEE +unsigned EMUSHORT XFnan[6] = {0x7fff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}; +#endif +#ifdef IBMPC +unsigned EMUSHORT XFnan[6] = {0, 0, 0, 0xc000, 0xffff, 0}; +#endif +#endif + +#ifdef DFMODE_NAN +DFMODE_NAN; +#else +#ifdef MIEEE +unsigned EMUSHORT DFnan[4] = {0x7fff, 0xffff, 0xffff, 0xffff}; +#endif +#ifdef IBMPC +unsigned EMUSHORT DFnan[4] = {0, 0, 0, 0xfff8}; +#endif +#endif + +#ifdef SFMODE_NAN +SFMODE_NAN; +#else +#ifdef MIEEE +unsigned EMUSHORT SFnan[2] = {0x7fff, 0xffff}; +#endif +#ifdef IBMPC +unsigned EMUSHORT SFnan[2] = {0, 0xffc0}; +#endif +#endif + + +void +make_nan (nan, mode) +unsigned EMUSHORT *nan; +enum machine_mode mode; +{ + int i, n; + unsigned EMUSHORT *p; + + switch (mode) + { +/* Possibly the `reserved operand' patterns on a VAX can be + used like NaN's, but probably not in the same way as IEEE. */ +#ifndef DEC + case TFmode: + n = 8; + p = TFnan; + break; + case XFmode: + n = 6; + p = XFnan; + break; + case DFmode: + n = 4; + p = DFnan; + break; + case SFmode: + n = 2; + p = SFnan; + break; +#endif + default: + abort (); + } + for (i=0; i < n; i++) + *nan++ = *p++; +} + +/* Convert an SFmode target `float' value to a REAL_VALUE_TYPE. + This is the inverse of the function `etarsingle' invoked by + REAL_VALUE_TO_TARGET_SINGLE. */ + +REAL_VALUE_TYPE +ereal_from_float (f) + unsigned long f; +{ + REAL_VALUE_TYPE r; + unsigned EMUSHORT s[2]; + unsigned EMUSHORT e[NE]; + + /* Convert 32 bit integer to array of 16 bit pieces in target machine order. + This is the inverse operation to what the function `endian' does. */ +#if WORDS_BIG_ENDIAN + s[0] = (unsigned EMUSHORT) (f >> 16); + s[1] = (unsigned EMUSHORT) f; +#else + s[0] = (unsigned EMUSHORT) f; + s[1] = (unsigned EMUSHORT) (f >> 16); +#endif + /* Convert and promote the target float to E-type. */ + e24toe (s, e); + /* Output E-type to REAL_VALUE_TYPE. */ + PUT_REAL (e, &r); + return r; +} + +/* Convert a DFmode target `double' value to a REAL_VALUE_TYPE. + This is the inverse of the function `etardouble' invoked by + REAL_VALUE_TO_TARGET_DOUBLE. + + The DFmode is stored as an array of longs (i.e., HOST_WIDE_INTs) + with 32 bits of the value per each long. The first element + of the input array holds the bits that would come first in the + target computer's memory. */ + +REAL_VALUE_TYPE +ereal_from_double (d) + unsigned long d[]; +{ + REAL_VALUE_TYPE r; + unsigned EMUSHORT s[4]; + unsigned EMUSHORT e[NE]; + + /* Convert array of 32 bit pieces to equivalent array of 16 bit pieces. + This is the inverse of `endian'. */ +#if WORDS_BIG_ENDIAN + s[0] = (unsigned EMUSHORT) (d[0] >> 16); + s[1] = (unsigned EMUSHORT) d[0]; + s[2] = (unsigned EMUSHORT) (d[1] >> 16); + s[3] = (unsigned EMUSHORT) d[1]; +#else + s[0] = (unsigned EMUSHORT) d[0]; + s[1] = (unsigned EMUSHORT) (d[0] >> 16); + s[2] = (unsigned EMUSHORT) d[1]; + s[3] = (unsigned EMUSHORT) (d[1] >> 16); +#endif + /* Convert target double to E-type. */ + e53toe (s, e); + /* Output E-type to REAL_VALUE_TYPE. */ + PUT_REAL (e, &r); + return r; +} +#endif /* EMU_NON_COMPILE not defined */ diff --git a/gnu/usr.bin/cc/lib/real.h b/gnu/usr.bin/cc/lib/real.h new file mode 100644 index 0000000000..4ac1822899 --- /dev/null +++ b/gnu/usr.bin/cc/lib/real.h @@ -0,0 +1,363 @@ +/* Front-end tree definitions for GNU compiler. + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef REAL_H_INCLUDED +#define REAL_H_INCLUDED + +/* Define codes for all the float formats that we know of. */ +#define UNKNOWN_FLOAT_FORMAT 0 +#define IEEE_FLOAT_FORMAT 1 +#define VAX_FLOAT_FORMAT 2 +#define IBM_FLOAT_FORMAT 3 + +/* Default to IEEE float if not specified. Nearly all machines use it. */ + +#ifndef TARGET_FLOAT_FORMAT +#define TARGET_FLOAT_FORMAT IEEE_FLOAT_FORMAT +#endif + +#ifndef HOST_FLOAT_FORMAT +#define HOST_FLOAT_FORMAT IEEE_FLOAT_FORMAT +#endif + +#if TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT +#define REAL_INFINITY +#endif + +/* Defining REAL_ARITHMETIC invokes a floating point emulator + that can produce a target machine format differing by more + than just endian-ness from the host's format. The emulator + is also used to support extended real XFmode. */ +#ifndef LONG_DOUBLE_TYPE_SIZE +#define LONG_DOUBLE_TYPE_SIZE 64 +#endif +#if (LONG_DOUBLE_TYPE_SIZE == 96) || defined (REAL_ARITHMETIC) +/* **** Start of software floating point emulator interface macros **** */ + +/* Support 80-bit extended real XFmode if LONG_DOUBLE_TYPE_SIZE + has been defined to be 96 in the tm.h machine file. */ +#if (LONG_DOUBLE_TYPE_SIZE == 96) +#define REAL_IS_NOT_DOUBLE +#define REAL_ARITHMETIC +typedef struct { + HOST_WIDE_INT r[(11 + sizeof (HOST_WIDE_INT))/(sizeof (HOST_WIDE_INT))]; +} realvaluetype; +#define REAL_VALUE_TYPE realvaluetype + +#else /* no XFmode support */ + +#if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT +/* If no XFmode support, then a REAL_VALUE_TYPE is 64 bits wide + but it is not necessarily a host machine double. */ +#define REAL_IS_NOT_DOUBLE +typedef struct { + HOST_WIDE_INT r[(7 + sizeof (HOST_WIDE_INT))/(sizeof (HOST_WIDE_INT))]; +} realvaluetype; +#define REAL_VALUE_TYPE realvaluetype +#else +/* If host and target formats are compatible, then a REAL_VALUE_TYPE + is actually a host machine double. */ +#define REAL_VALUE_TYPE double +#endif +#endif /* no XFmode support */ + +/* If emulation has been enabled by defining REAL_ARITHMETIC or by + setting LONG_DOUBLE_TYPE_SIZE to 96, then define macros so that + they invoke emulator functions. This will succeed only if the machine + files have been updated to use these macros in place of any + references to host machine `double' or `float' types. */ +#ifdef REAL_ARITHMETIC +#undef REAL_ARITHMETIC +#define REAL_ARITHMETIC(value, code, d1, d2) \ + earith (&(value), (code), &(d1), &(d2)) + +/* Declare functions in real.c that are referenced here. */ +void earith (), ereal_from_uint (), ereal_from_int (), ereal_to_int (); +void etarldouble (), etardouble (); +long etarsingle (); +int ereal_cmp (), eroundi (), ereal_isneg (); +unsigned int eroundui (); +REAL_VALUE_TYPE etrunci (), etruncui (), ereal_ldexp (), ereal_atof (); +REAL_VALUE_TYPE ereal_negate (), ereal_truncate (); +REAL_VALUE_TYPE ereal_from_float (), ereal_from_double (); + +#define REAL_VALUES_EQUAL(x, y) (ereal_cmp ((x), (y)) == 0) +/* true if x < y : */ +#define REAL_VALUES_LESS(x, y) (ereal_cmp ((x), (y)) == -1) +#define REAL_VALUE_LDEXP(x, n) ereal_ldexp (x, n) + +/* These return REAL_VALUE_TYPE: */ +#define REAL_VALUE_RNDZINT(x) (etrunci (x)) +#define REAL_VALUE_UNSIGNED_RNDZINT(x) (etruncui (x)) +extern REAL_VALUE_TYPE real_value_truncate (); +#define REAL_VALUE_TRUNCATE(mode, x) real_value_truncate (mode, x) + +/* These return int: */ +#define REAL_VALUE_FIX(x) (eroundi (x)) +#define REAL_VALUE_UNSIGNED_FIX(x) ((unsigned int) eroundui (x)) + +#define REAL_VALUE_ATOF ereal_atof +#define REAL_VALUE_NEGATE ereal_negate + +#define REAL_VALUE_MINUS_ZERO(x) \ + ((ereal_cmp (x, dconst0) == 0) && (ereal_isneg (x) != 0 )) + +#define REAL_VALUE_TO_INT ereal_to_int +#define REAL_VALUE_FROM_INT(d, i, j) (ereal_from_int (&d, i, j)) +#define REAL_VALUE_FROM_UNSIGNED_INT(d, i, j) (ereal_from_uint (&d, i, j)) + +/* IN is a REAL_VALUE_TYPE. OUT is an array of longs. */ +#define REAL_VALUE_TO_TARGET_LONG_DOUBLE(IN, OUT) (etarldouble ((IN), (OUT))) +#define REAL_VALUE_TO_TARGET_DOUBLE(IN, OUT) (etardouble ((IN), (OUT))) +/* d is an array of longs. */ +#define REAL_VALUE_FROM_TARGET_DOUBLE(d) (ereal_from_double (d)) +/* IN is a REAL_VALUE_TYPE. OUT is a long. */ +#define REAL_VALUE_TO_TARGET_SINGLE(IN, OUT) ((OUT) = etarsingle ((IN))) +/* f is a long. */ +#define REAL_VALUE_FROM_TARGET_SINGLE(f) (ereal_from_float (f)) + +/* Conversions to decimal ASCII string. */ +#define REAL_VALUE_TO_DECIMAL(r, fmt, s) (ereal_to_decimal (r, s)) + +#endif /* REAL_ARITHMETIC defined */ + +/* **** End of software floating point emulator interface macros **** */ +#else /* LONG_DOUBLE_TYPE_SIZE != 96 and REAL_ARITHMETIC not defined */ + +/* old interface */ +#ifdef REAL_ARITHMETIC +/* Defining REAL_IS_NOT_DOUBLE breaks certain initializations + when REAL_ARITHMETIC etc. are not defined. */ + +/* Now see if the host and target machines use the same format. + If not, define REAL_IS_NOT_DOUBLE (even if we end up representing + reals as doubles because we have no better way in this cross compiler.) + This turns off various optimizations that can happen when we know the + compiler's float format matches the target's float format. + */ +#if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT +#define REAL_IS_NOT_DOUBLE +#ifndef REAL_VALUE_TYPE +typedef struct { + HOST_WIDE_INT r[sizeof (double)/sizeof (HOST_WIDE_INT)]; + } realvaluetype; +#define REAL_VALUE_TYPE realvaluetype +#endif /* no REAL_VALUE_TYPE */ +#endif /* formats differ */ +#endif /* 0 */ + +#endif /* emulator not used */ + +/* If we are not cross-compiling, use a `double' to represent the + floating-point value. Otherwise, use some other type + (probably a struct containing an array of longs). */ +#ifndef REAL_VALUE_TYPE +#define REAL_VALUE_TYPE double +#else +#define REAL_IS_NOT_DOUBLE +#endif + +#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT + +/* Convert a type `double' value in host format first to a type `float' + value in host format and then to a single type `long' value which + is the bitwise equivalent of the `float' value. */ +#ifndef REAL_VALUE_TO_TARGET_SINGLE +#define REAL_VALUE_TO_TARGET_SINGLE(IN, OUT) \ +do { float f = (float) (IN); \ + (OUT) = *(long *) &f; \ + } while (0) +#endif + +/* Convert a type `double' value in host format to a pair of type `long' + values which is its bitwise equivalent, but put the two words into + proper word order for the target. */ +#ifndef REAL_VALUE_TO_TARGET_DOUBLE +#if defined (HOST_WORDS_BIG_ENDIAN) == WORDS_BIG_ENDIAN +#define REAL_VALUE_TO_TARGET_DOUBLE(IN, OUT) \ +do { REAL_VALUE_TYPE in = (IN); /* Make sure it's not in a register. */\ + (OUT)[0] = ((long *) &in)[0]; \ + (OUT)[1] = ((long *) &in)[1]; \ + } while (0) +#else +#define REAL_VALUE_TO_TARGET_DOUBLE(IN, OUT) \ +do { REAL_VALUE_TYPE in = (IN); /* Make sure it's not in a register. */\ + (OUT)[1] = ((long *) &in)[0]; \ + (OUT)[0] = ((long *) &in)[1]; \ + } while (0) +#endif +#endif +#endif /* HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT */ + +/* In this configuration, double and long double are the same. */ +#ifndef REAL_VALUE_TO_TARGET_LONG_DOUBLE +#define REAL_VALUE_TO_TARGET_LONG_DOUBLE(a, b) REAL_VALUE_TO_TARGET_DOUBLE (a, b) +#endif + +/* Compare two floating-point values for equality. */ +#ifndef REAL_VALUES_EQUAL +#define REAL_VALUES_EQUAL(x, y) ((x) == (y)) +#endif + +/* Compare two floating-point values for less than. */ +#ifndef REAL_VALUES_LESS +#define REAL_VALUES_LESS(x, y) ((x) < (y)) +#endif + +/* Truncate toward zero to an integer floating-point value. */ +#ifndef REAL_VALUE_RNDZINT +#define REAL_VALUE_RNDZINT(x) ((double) ((int) (x))) +#endif + +/* Truncate toward zero to an unsigned integer floating-point value. */ +#ifndef REAL_VALUE_UNSIGNED_RNDZINT +#define REAL_VALUE_UNSIGNED_RNDZINT(x) ((double) ((unsigned int) (x))) +#endif + +/* Convert a floating-point value to integer, using any rounding mode. */ +#ifndef REAL_VALUE_FIX +#define REAL_VALUE_FIX(x) ((int) (x)) +#endif + +/* Convert a floating-point value to unsigned integer, using any rounding + mode. */ +#ifndef REAL_VALUE_UNSIGNED_FIX +#define REAL_VALUE_UNSIGNED_FIX(x) ((unsigned int) (x)) +#endif + +/* Scale X by Y powers of 2. */ +#ifndef REAL_VALUE_LDEXP +#define REAL_VALUE_LDEXP(x, y) ldexp (x, y) +extern double ldexp (); +#endif + +/* Convert the string X to a floating-point value. */ +#ifndef REAL_VALUE_ATOF +#if 1 +/* Use real.c to convert decimal numbers to binary, ... */ +REAL_VALUE_TYPE ereal_atof (); +#define REAL_VALUE_ATOF(x, s) ereal_atof (x, s) +#else +/* ... or, if you like the host computer's atof, go ahead and use it: */ +#define REAL_VALUE_ATOF(x, s) atof (x) +#if defined (MIPSEL) || defined (MIPSEB) +/* MIPS compiler can't handle parens around the function name. + This problem *does not* appear to be connected with any + macro definition for atof. It does not seem there is one. */ +extern double atof (); +#else +extern double (atof) (); +#endif +#endif +#endif + +/* Negate the floating-point value X. */ +#ifndef REAL_VALUE_NEGATE +#define REAL_VALUE_NEGATE(x) (- (x)) +#endif + +/* Truncate the floating-point value X to mode MODE. This is correct only + for the most common case where the host and target have objects of the same + size and where `float' is SFmode. */ + +/* Don't use REAL_VALUE_TRUNCATE directly--always call real_value_truncate. */ +extern REAL_VALUE_TYPE real_value_truncate (); + +#ifndef REAL_VALUE_TRUNCATE +#define REAL_VALUE_TRUNCATE(mode, x) \ + (GET_MODE_BITSIZE (mode) == sizeof (float) * HOST_BITS_PER_CHAR \ + ? (float) (x) : (x)) +#endif + +/* Determine whether a floating-point value X is infinite. */ +#ifndef REAL_VALUE_ISINF +#define REAL_VALUE_ISINF(x) (target_isinf (x)) +#endif + +/* Determine whether a floating-point value X is a NaN. */ +#ifndef REAL_VALUE_ISNAN +#define REAL_VALUE_ISNAN(x) (target_isnan (x)) +#endif + +/* Determine whether a floating-point value X is negative. */ +#ifndef REAL_VALUE_NEGATIVE +#define REAL_VALUE_NEGATIVE(x) (target_negative (x)) +#endif + +/* Determine whether a floating-point value X is minus 0. */ +#ifndef REAL_VALUE_MINUS_ZERO +#define REAL_VALUE_MINUS_ZERO(x) ((x) == 0 && REAL_VALUE_NEGATIVE (x)) +#endif + +/* Constant real values 0, 1, 2, and -1. */ + +extern REAL_VALUE_TYPE dconst0; +extern REAL_VALUE_TYPE dconst1; +extern REAL_VALUE_TYPE dconst2; +extern REAL_VALUE_TYPE dconstm1; + +/* Union type used for extracting real values from CONST_DOUBLEs + or putting them in. */ + +union real_extract +{ + REAL_VALUE_TYPE d; + HOST_WIDE_INT i[sizeof (REAL_VALUE_TYPE) / sizeof (HOST_WIDE_INT)]; +}; + +/* For a CONST_DOUBLE: + The usual two ints that hold the value. + For a DImode, that is all there are; + and CONST_DOUBLE_LOW is the low-order word and ..._HIGH the high-order. + For a float, the number of ints varies, + and CONST_DOUBLE_LOW is the one that should come first *in memory*. + So use &CONST_DOUBLE_LOW(r) as the address of an array of ints. */ +#define CONST_DOUBLE_LOW(r) XWINT (r, 2) +#define CONST_DOUBLE_HIGH(r) XWINT (r, 3) + +/* Link for chain of all CONST_DOUBLEs in use in current function. */ +#define CONST_DOUBLE_CHAIN(r) XEXP (r, 1) +/* The MEM which represents this CONST_DOUBLE's value in memory, + or const0_rtx if no MEM has been made for it yet, + or cc0_rtx if it is not on the chain. */ +#define CONST_DOUBLE_MEM(r) XEXP (r, 0) + +/* Function to return a real value (not a tree node) + from a given integer constant. */ +REAL_VALUE_TYPE real_value_from_int_cst (); + +/* Given a CONST_DOUBLE in FROM, store into TO the value it represents. */ + +#define REAL_VALUE_FROM_CONST_DOUBLE(to, from) \ +do { union real_extract u; \ + bcopy (&CONST_DOUBLE_LOW ((from)), &u, sizeof u); \ + to = u.d; } while (0) + +/* Return a CONST_DOUBLE with value R and mode M. */ + +#define CONST_DOUBLE_FROM_REAL_VALUE(r, m) immed_real_const_1 (r, m) + +/* Convert a floating point value `r', that can be interpreted + as a host machine float or double, to a decimal ASCII string `s' + using printf format string `fmt'. */ +#ifndef REAL_VALUE_TO_DECIMAL +#define REAL_VALUE_TO_DECIMAL(r, fmt, s) (sprintf (s, fmt, r)) +#endif + +#endif /* Not REAL_H_INCLUDED */ diff --git a/gnu/usr.bin/cc/lib/recog.c b/gnu/usr.bin/cc/lib/recog.c new file mode 100644 index 0000000000..2232db29a1 --- /dev/null +++ b/gnu/usr.bin/cc/lib/recog.c @@ -0,0 +1,1961 @@ +/* Subroutines used by or related to instruction recognition. + Copyright (C) 1987, 1988, 1991, 1992, 1993 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include "config.h" +#include "rtl.h" +#include +#include "insn-config.h" +#include "insn-attr.h" +#include "insn-flags.h" +#include "insn-codes.h" +#include "recog.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "flags.h" +#include "real.h" + +#ifndef STACK_PUSH_CODE +#ifdef STACK_GROWS_DOWNWARD +#define STACK_PUSH_CODE PRE_DEC +#else +#define STACK_PUSH_CODE PRE_INC +#endif +#endif + +/* Import from final.c: */ +extern rtx alter_subreg (); + +int strict_memory_address_p (); +int memory_address_p (); + +/* Nonzero means allow operands to be volatile. + This should be 0 if you are generating rtl, such as if you are calling + the functions in optabs.c and expmed.c (most of the time). + This should be 1 if all valid insns need to be recognized, + such as in regclass.c and final.c and reload.c. + + init_recog and init_recog_no_volatile are responsible for setting this. */ + +int volatile_ok; + +/* On return from `constrain_operands', indicate which alternative + was satisfied. */ + +int which_alternative; + +/* Nonzero after end of reload pass. + Set to 1 or 0 by toplev.c. + Controls the significance of (SUBREG (MEM)). */ + +int reload_completed; + +/* Initialize data used by the function `recog'. + This must be called once in the compilation of a function + before any insn recognition may be done in the function. */ + +void +init_recog_no_volatile () +{ + volatile_ok = 0; +} + +void +init_recog () +{ + volatile_ok = 1; +} + +/* Try recognizing the instruction INSN, + and return the code number that results. + Remeber the code so that repeated calls do not + need to spend the time for actual rerecognition. + + This function is the normal interface to instruction recognition. + The automatically-generated function `recog' is normally called + through this one. (The only exception is in combine.c.) */ + +int +recog_memoized (insn) + rtx insn; +{ + if (INSN_CODE (insn) < 0) + INSN_CODE (insn) = recog (PATTERN (insn), insn, NULL_PTR); + return INSN_CODE (insn); +} + +/* Check that X is an insn-body for an `asm' with operands + and that the operands mentioned in it are legitimate. */ + +int +check_asm_operands (x) + rtx x; +{ + int noperands = asm_noperands (x); + rtx *operands; + int i; + + if (noperands < 0) + return 0; + if (noperands == 0) + return 1; + + operands = (rtx *) alloca (noperands * sizeof (rtx)); + decode_asm_operands (x, operands, NULL_PTR, NULL_PTR, NULL_PTR); + + for (i = 0; i < noperands; i++) + if (!general_operand (operands[i], VOIDmode)) + return 0; + + return 1; +} + +/* Static data for the next two routines. + + The maximum number of changes supported is defined as the maximum + number of operands times 5. This allows for repeated substitutions + inside complex indexed address, or, alternatively, changes in up + to 5 insns. */ + +#define MAX_CHANGE_LOCS (MAX_RECOG_OPERANDS * 5) + +static rtx change_objects[MAX_CHANGE_LOCS]; +static int change_old_codes[MAX_CHANGE_LOCS]; +static rtx *change_locs[MAX_CHANGE_LOCS]; +static rtx change_olds[MAX_CHANGE_LOCS]; + +static int num_changes = 0; + +/* Validate a proposed change to OBJECT. LOC is the location in the rtl for + at which NEW will be placed. If OBJECT is zero, no validation is done, + the change is simply made. + + Two types of objects are supported: If OBJECT is a MEM, memory_address_p + will be called with the address and mode as parameters. If OBJECT is + an INSN, CALL_INSN, or JUMP_INSN, the insn will be re-recognized with + the change in place. + + IN_GROUP is non-zero if this is part of a group of changes that must be + performed as a group. In that case, the changes will be stored. The + function `apply_change_group' will validate and apply the changes. + + If IN_GROUP is zero, this is a single change. Try to recognize the insn + or validate the memory reference with the change applied. If the result + is not valid for the machine, suppress the change and return zero. + Otherwise, perform the change and return 1. */ + +int +validate_change (object, loc, new, in_group) + rtx object; + rtx *loc; + rtx new; + int in_group; +{ + rtx old = *loc; + + if (old == new || rtx_equal_p (old, new)) + return 1; + + if (num_changes >= MAX_CHANGE_LOCS + || (in_group == 0 && num_changes != 0)) + abort (); + + *loc = new; + + /* Save the information describing this change. */ + change_objects[num_changes] = object; + change_locs[num_changes] = loc; + change_olds[num_changes] = old; + + if (object && GET_CODE (object) != MEM) + { + /* Set INSN_CODE to force rerecognition of insn. Save old code in + case invalid. */ + change_old_codes[num_changes] = INSN_CODE (object); + INSN_CODE (object) = -1; + } + + num_changes++; + + /* If we are making a group of changes, return 1. Otherwise, validate the + change group we made. */ + + if (in_group) + return 1; + else + return apply_change_group (); +} + +/* Apply a group of changes previously issued with `validate_change'. + Return 1 if all changes are valid, zero otherwise. */ + +int +apply_change_group () +{ + int i; + + /* The changes have been applied and all INSN_CODEs have been reset to force + rerecognition. + + The changes are valid if we aren't given an object, or if we are + given a MEM and it still is a valid address, or if this is in insn + and it is recognized. In the latter case, if reload has completed, + we also require that the operands meet the constraints for + the insn. We do not allow modifying an ASM_OPERANDS after reload + has completed because verifying the constraints is too difficult. */ + + for (i = 0; i < num_changes; i++) + { + rtx object = change_objects[i]; + + if (object == 0) + continue; + + if (GET_CODE (object) == MEM) + { + if (! memory_address_p (GET_MODE (object), XEXP (object, 0))) + break; + } + else if ((recog_memoized (object) < 0 + && (asm_noperands (PATTERN (object)) < 0 + || ! check_asm_operands (PATTERN (object)) + || reload_completed)) + || (reload_completed + && (insn_extract (object), + ! constrain_operands (INSN_CODE (object), 1)))) + { + rtx pat = PATTERN (object); + + /* Perhaps we couldn't recognize the insn because there were + extra CLOBBERs at the end. If so, try to re-recognize + without the last CLOBBER (later iterations will cause each of + them to be eliminated, in turn). But don't do this if we + have an ASM_OPERAND. */ + if (GET_CODE (pat) == PARALLEL + && GET_CODE (XVECEXP (pat, 0, XVECLEN (pat, 0) - 1)) == CLOBBER + && asm_noperands (PATTERN (object)) < 0) + { + rtx newpat; + + if (XVECLEN (pat, 0) == 2) + newpat = XVECEXP (pat, 0, 0); + else + { + int j; + + newpat = gen_rtx (PARALLEL, VOIDmode, + gen_rtvec (XVECLEN (pat, 0) - 1)); + for (j = 0; j < XVECLEN (newpat, 0); j++) + XVECEXP (newpat, 0, j) = XVECEXP (pat, 0, j); + } + + /* Add a new change to this group to replace the pattern + with this new pattern. Then consider this change + as having succeeded. The change we added will + cause the entire call to fail if things remain invalid. + + Note that this can lose if a later change than the one + we are processing specified &XVECEXP (PATTERN (object), 0, X) + but this shouldn't occur. */ + + validate_change (object, &PATTERN (object), newpat, 1); + } + else if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER) + /* If this insn is a CLOBBER or USE, it is always valid, but is + never recognized. */ + continue; + else + break; + } + } + + if (i == num_changes) + { + num_changes = 0; + return 1; + } + else + { + cancel_changes (0); + return 0; + } +} + +/* Return the number of changes so far in the current group. */ + +int +num_validated_changes () +{ + return num_changes; +} + +/* Retract the changes numbered NUM and up. */ + +void +cancel_changes (num) + int num; +{ + int i; + + /* Back out all the changes. Do this in the opposite order in which + they were made. */ + for (i = num_changes - 1; i >= num; i--) + { + *change_locs[i] = change_olds[i]; + if (change_objects[i] && GET_CODE (change_objects[i]) != MEM) + INSN_CODE (change_objects[i]) = change_old_codes[i]; + } + num_changes = num; +} + +/* Replace every occurrence of FROM in X with TO. Mark each change with + validate_change passing OBJECT. */ + +static void +validate_replace_rtx_1 (loc, from, to, object) + rtx *loc; + rtx from, to, object; +{ + register int i, j; + register char *fmt; + register rtx x = *loc; + enum rtx_code code = GET_CODE (x); + + /* X matches FROM if it is the same rtx or they are both referring to the + same register in the same mode. Avoid calling rtx_equal_p unless the + operands look similar. */ + + if (x == from + || (GET_CODE (x) == REG && GET_CODE (from) == REG + && GET_MODE (x) == GET_MODE (from) + && REGNO (x) == REGNO (from)) + || (GET_CODE (x) == GET_CODE (from) && GET_MODE (x) == GET_MODE (from) + && rtx_equal_p (x, from))) + { + validate_change (object, loc, to, 1); + return; + } + + /* For commutative or comparison operations, try replacing each argument + separately and seeing if we made any changes. If so, put a constant + argument last.*/ + if (GET_RTX_CLASS (code) == '<' || GET_RTX_CLASS (code) == 'c') + { + int prev_changes = num_changes; + + validate_replace_rtx_1 (&XEXP (x, 0), from, to, object); + validate_replace_rtx_1 (&XEXP (x, 1), from, to, object); + if (prev_changes != num_changes && CONSTANT_P (XEXP (x, 0))) + { + validate_change (object, loc, + gen_rtx (GET_RTX_CLASS (code) == 'c' ? code + : swap_condition (code), + GET_MODE (x), XEXP (x, 1), XEXP (x, 0)), + 1); + x = *loc; + code = GET_CODE (x); + } + } + + switch (code) + { + case PLUS: + /* If we have have a PLUS whose second operand is now a CONST_INT, use + plus_constant to try to simplify it. */ + if (GET_CODE (XEXP (x, 1)) == CONST_INT && XEXP (x, 1) == to) + validate_change (object, loc, + plus_constant (XEXP (x, 0), INTVAL (XEXP (x, 1))), 1); + return; + + case ZERO_EXTEND: + case SIGN_EXTEND: + /* In these cases, the operation to be performed depends on the mode + of the operand. If we are replacing the operand with a VOIDmode + constant, we lose the information. So try to simplify the operation + in that case. If it fails, substitute in something that we know + won't be recognized. */ + if (GET_MODE (to) == VOIDmode + && (XEXP (x, 0) == from + || (GET_CODE (XEXP (x, 0)) == REG && GET_CODE (from) == REG + && GET_MODE (XEXP (x, 0)) == GET_MODE (from) + && REGNO (XEXP (x, 0)) == REGNO (from)))) + { + rtx new = simplify_unary_operation (code, GET_MODE (x), to, + GET_MODE (from)); + if (new == 0) + new = gen_rtx (CLOBBER, GET_MODE (x), const0_rtx); + + validate_change (object, loc, new, 1); + return; + } + break; + + case SUBREG: + /* If we have a SUBREG of a register that we are replacing and we are + replacing it with a MEM, make a new MEM and try replacing the + SUBREG with it. Don't do this if the MEM has a mode-dependent address + or if we would be widening it. */ + + if (SUBREG_REG (x) == from + && GET_CODE (from) == REG + && GET_CODE (to) == MEM + && ! mode_dependent_address_p (XEXP (to, 0)) + && ! MEM_VOLATILE_P (to) + && GET_MODE_SIZE (GET_MODE (x)) <= GET_MODE_SIZE (GET_MODE (to))) + { + int offset = SUBREG_WORD (x) * UNITS_PER_WORD; + enum machine_mode mode = GET_MODE (x); + rtx new; + +#if BYTES_BIG_ENDIAN + offset += (MIN (UNITS_PER_WORD, + GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) + - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))); +#endif + + new = gen_rtx (MEM, mode, plus_constant (XEXP (to, 0), offset)); + MEM_VOLATILE_P (new) = MEM_VOLATILE_P (to); + RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (to); + MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (to); + validate_change (object, loc, new, 1); + return; + } + break; + + case ZERO_EXTRACT: + case SIGN_EXTRACT: + /* If we are replacing a register with memory, try to change the memory + to be the mode required for memory in extract operations (this isn't + likely to be an insertion operation; if it was, nothing bad will + happen, we might just fail in some cases). */ + + if (XEXP (x, 0) == from && GET_CODE (from) == REG && GET_CODE (to) == MEM + && GET_CODE (XEXP (x, 1)) == CONST_INT + && GET_CODE (XEXP (x, 2)) == CONST_INT + && ! mode_dependent_address_p (XEXP (to, 0)) + && ! MEM_VOLATILE_P (to)) + { + enum machine_mode wanted_mode = VOIDmode; + enum machine_mode is_mode = GET_MODE (to); + int width = INTVAL (XEXP (x, 1)); + int pos = INTVAL (XEXP (x, 2)); + +#ifdef HAVE_extzv + if (code == ZERO_EXTRACT) + wanted_mode = insn_operand_mode[(int) CODE_FOR_extzv][1]; +#endif +#ifdef HAVE_extv + if (code == SIGN_EXTRACT) + wanted_mode = insn_operand_mode[(int) CODE_FOR_extv][1]; +#endif + + /* If we have a narrower mode, we can do something. */ + if (wanted_mode != VOIDmode + && GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode)) + { + int offset = pos / BITS_PER_UNIT; + rtx newmem; + + /* If the bytes and bits are counted differently, we + must adjust the offset. */ +#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN + offset = (GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (wanted_mode) + - offset); +#endif + + pos %= GET_MODE_BITSIZE (wanted_mode); + + newmem = gen_rtx (MEM, wanted_mode, + plus_constant (XEXP (to, 0), offset)); + RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (to); + MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (to); + MEM_IN_STRUCT_P (newmem) = MEM_IN_STRUCT_P (to); + + validate_change (object, &XEXP (x, 2), GEN_INT (pos), 1); + validate_change (object, &XEXP (x, 0), newmem, 1); + } + } + + break; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + validate_replace_rtx_1 (&XEXP (x, i), from, to, object); + else if (fmt[i] == 'E') + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + validate_replace_rtx_1 (&XVECEXP (x, i, j), from, to, object); + } +} + +/* Try replacing every occurrence of FROM in INSN with TO. After all + changes have been made, validate by seeing if INSN is still valid. */ + +int +validate_replace_rtx (from, to, insn) + rtx from, to, insn; +{ + validate_replace_rtx_1 (&PATTERN (insn), from, to, insn); + return apply_change_group (); +} + +#ifdef HAVE_cc0 +/* Return 1 if the insn using CC0 set by INSN does not contain + any ordered tests applied to the condition codes. + EQ and NE tests do not count. */ + +int +next_insn_tests_no_inequality (insn) + rtx insn; +{ + register rtx next = next_cc0_user (insn); + + /* If there is no next insn, we have to take the conservative choice. */ + if (next == 0) + return 0; + + return ((GET_CODE (next) == JUMP_INSN + || GET_CODE (next) == INSN + || GET_CODE (next) == CALL_INSN) + && ! inequality_comparisons_p (PATTERN (next))); +} + +#if 0 /* This is useless since the insn that sets the cc's + must be followed immediately by the use of them. */ +/* Return 1 if the CC value set up by INSN is not used. */ + +int +next_insns_test_no_inequality (insn) + rtx insn; +{ + register rtx next = NEXT_INSN (insn); + + for (; next != 0; next = NEXT_INSN (next)) + { + if (GET_CODE (next) == CODE_LABEL + || GET_CODE (next) == BARRIER) + return 1; + if (GET_CODE (next) == NOTE) + continue; + if (inequality_comparisons_p (PATTERN (next))) + return 0; + if (sets_cc0_p (PATTERN (next)) == 1) + return 1; + if (! reg_mentioned_p (cc0_rtx, PATTERN (next))) + return 1; + } + return 1; +} +#endif +#endif + +/* This is used by find_single_use to locate an rtx that contains exactly one + use of DEST, which is typically either a REG or CC0. It returns a + pointer to the innermost rtx expression containing DEST. Appearances of + DEST that are being used to totally replace it are not counted. */ + +static rtx * +find_single_use_1 (dest, loc) + rtx dest; + rtx *loc; +{ + rtx x = *loc; + enum rtx_code code = GET_CODE (x); + rtx *result = 0; + rtx *this_result; + int i; + char *fmt; + + switch (code) + { + case CONST_INT: + case CONST: + case LABEL_REF: + case SYMBOL_REF: + case CONST_DOUBLE: + case CLOBBER: + return 0; + + case SET: + /* If the destination is anything other than CC0, PC, a REG or a SUBREG + of a REG that occupies all of the REG, the insn uses DEST if + it is mentioned in the destination or the source. Otherwise, we + need just check the source. */ + if (GET_CODE (SET_DEST (x)) != CC0 + && GET_CODE (SET_DEST (x)) != PC + && GET_CODE (SET_DEST (x)) != REG + && ! (GET_CODE (SET_DEST (x)) == SUBREG + && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG + && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (x)))) + + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD) + == ((GET_MODE_SIZE (GET_MODE (SET_DEST (x))) + + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)))) + break; + + return find_single_use_1 (dest, &SET_SRC (x)); + + case MEM: + case SUBREG: + return find_single_use_1 (dest, &XEXP (x, 0)); + } + + /* If it wasn't one of the common cases above, check each expression and + vector of this code. Look for a unique usage of DEST. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + if (dest == XEXP (x, i) + || (GET_CODE (dest) == REG && GET_CODE (XEXP (x, i)) == REG + && REGNO (dest) == REGNO (XEXP (x, i)))) + this_result = loc; + else + this_result = find_single_use_1 (dest, &XEXP (x, i)); + + if (result == 0) + result = this_result; + else if (this_result) + /* Duplicate usage. */ + return 0; + } + else if (fmt[i] == 'E') + { + int j; + + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + { + if (XVECEXP (x, i, j) == dest + || (GET_CODE (dest) == REG + && GET_CODE (XVECEXP (x, i, j)) == REG + && REGNO (XVECEXP (x, i, j)) == REGNO (dest))) + this_result = loc; + else + this_result = find_single_use_1 (dest, &XVECEXP (x, i, j)); + + if (result == 0) + result = this_result; + else if (this_result) + return 0; + } + } + } + + return result; +} + +/* See if DEST, produced in INSN, is used only a single time in the + sequel. If so, return a pointer to the innermost rtx expression in which + it is used. + + If PLOC is non-zero, *PLOC is set to the insn containing the single use. + + This routine will return usually zero either before flow is called (because + there will be no LOG_LINKS notes) or after reload (because the REG_DEAD + note can't be trusted). + + If DEST is cc0_rtx, we look only at the next insn. In that case, we don't + care about REG_DEAD notes or LOG_LINKS. + + Otherwise, we find the single use by finding an insn that has a + LOG_LINKS pointing at INSN and has a REG_DEAD note for DEST. If DEST is + only referenced once in that insn, we know that it must be the first + and last insn referencing DEST. */ + +rtx * +find_single_use (dest, insn, ploc) + rtx dest; + rtx insn; + rtx *ploc; +{ + rtx next; + rtx *result; + rtx link; + +#ifdef HAVE_cc0 + if (dest == cc0_rtx) + { + next = NEXT_INSN (insn); + if (next == 0 + || (GET_CODE (next) != INSN && GET_CODE (next) != JUMP_INSN)) + return 0; + + result = find_single_use_1 (dest, &PATTERN (next)); + if (result && ploc) + *ploc = next; + return result; + } +#endif + + if (reload_completed || reload_in_progress || GET_CODE (dest) != REG) + return 0; + + for (next = next_nonnote_insn (insn); + next != 0 && GET_CODE (next) != CODE_LABEL; + next = next_nonnote_insn (next)) + if (GET_RTX_CLASS (GET_CODE (next)) == 'i' && dead_or_set_p (next, dest)) + { + for (link = LOG_LINKS (next); link; link = XEXP (link, 1)) + if (XEXP (link, 0) == insn) + break; + + if (link) + { + result = find_single_use_1 (dest, &PATTERN (next)); + if (ploc) + *ploc = next; + return result; + } + } + + return 0; +} + +/* Return 1 if OP is a valid general operand for machine mode MODE. + This is either a register reference, a memory reference, + or a constant. In the case of a memory reference, the address + is checked for general validity for the target machine. + + Register and memory references must have mode MODE in order to be valid, + but some constants have no machine mode and are valid for any mode. + + If MODE is VOIDmode, OP is checked for validity for whatever mode + it has. + + The main use of this function is as a predicate in match_operand + expressions in the machine description. + + For an explanation of this function's behavior for registers of + class NO_REGS, see the comment for `register_operand'. */ + +int +general_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + register enum rtx_code code = GET_CODE (op); + int mode_altering_drug = 0; + + if (mode == VOIDmode) + mode = GET_MODE (op); + + /* Don't accept CONST_INT or anything similar + if the caller wants something floating. */ + if (GET_MODE (op) == VOIDmode && mode != VOIDmode + && GET_MODE_CLASS (mode) != MODE_INT + && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT) + return 0; + + if (CONSTANT_P (op)) + return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode) +#ifdef LEGITIMATE_PIC_OPERAND_P + && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op)) +#endif + && LEGITIMATE_CONSTANT_P (op)); + + /* Except for certain constants with VOIDmode, already checked for, + OP's mode must match MODE if MODE specifies a mode. */ + + if (GET_MODE (op) != mode) + return 0; + + if (code == SUBREG) + { +#ifdef INSN_SCHEDULING + /* On machines that have insn scheduling, we want all memory + reference to be explicit, so outlaw paradoxical SUBREGs. */ + if (GET_CODE (SUBREG_REG (op)) == MEM + && GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op)))) + return 0; +#endif + + op = SUBREG_REG (op); + code = GET_CODE (op); +#if 0 + /* No longer needed, since (SUBREG (MEM...)) + will load the MEM into a reload reg in the MEM's own mode. */ + mode_altering_drug = 1; +#endif + } + + if (code == REG) + /* A register whose class is NO_REGS is not a general operand. */ + return (REGNO (op) >= FIRST_PSEUDO_REGISTER + || REGNO_REG_CLASS (REGNO (op)) != NO_REGS); + + if (code == MEM) + { + register rtx y = XEXP (op, 0); + if (! volatile_ok && MEM_VOLATILE_P (op)) + return 0; + /* Use the mem's mode, since it will be reloaded thus. */ + mode = GET_MODE (op); + GO_IF_LEGITIMATE_ADDRESS (mode, y, win); + } + return 0; + + win: + if (mode_altering_drug) + return ! mode_dependent_address_p (XEXP (op, 0)); + return 1; +} + +/* Return 1 if OP is a valid memory address for a memory reference + of mode MODE. + + The main use of this function is as a predicate in match_operand + expressions in the machine description. */ + +int +address_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + return memory_address_p (mode, op); +} + +/* Return 1 if OP is a register reference of mode MODE. + If MODE is VOIDmode, accept a register in any mode. + + The main use of this function is as a predicate in match_operand + expressions in the machine description. + + As a special exception, registers whose class is NO_REGS are + not accepted by `register_operand'. The reason for this change + is to allow the representation of special architecture artifacts + (such as a condition code register) without extending the rtl + definitions. Since registers of class NO_REGS cannot be used + as registers in any case where register classes are examined, + it is most consistent to keep this function from accepting them. */ + +int +register_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + if (GET_MODE (op) != mode && mode != VOIDmode) + return 0; + + if (GET_CODE (op) == SUBREG) + { + /* Before reload, we can allow (SUBREG (MEM...)) as a register operand + because it is guaranteed to be reloaded into one. + Just make sure the MEM is valid in itself. + (Ideally, (SUBREG (MEM)...) should not exist after reload, + but currently it does result from (SUBREG (REG)...) where the + reg went on the stack.) */ + if (! reload_completed && GET_CODE (SUBREG_REG (op)) == MEM) + return general_operand (op, mode); + op = SUBREG_REG (op); + } + + /* We don't consider registers whose class is NO_REGS + to be a register operand. */ + return (GET_CODE (op) == REG + && (REGNO (op) >= FIRST_PSEUDO_REGISTER + || REGNO_REG_CLASS (REGNO (op)) != NO_REGS)); +} + +/* Return 1 if OP should match a MATCH_SCRATCH, i.e., if it is a SCRATCH + or a hard register. */ + +int +scratch_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + return (GET_MODE (op) == mode + && (GET_CODE (op) == SCRATCH + || (GET_CODE (op) == REG + && REGNO (op) < FIRST_PSEUDO_REGISTER))); +} + +/* Return 1 if OP is a valid immediate operand for mode MODE. + + The main use of this function is as a predicate in match_operand + expressions in the machine description. */ + +int +immediate_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + /* Don't accept CONST_INT or anything similar + if the caller wants something floating. */ + if (GET_MODE (op) == VOIDmode && mode != VOIDmode + && GET_MODE_CLASS (mode) != MODE_INT + && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT) + return 0; + + return (CONSTANT_P (op) + && (GET_MODE (op) == mode || mode == VOIDmode + || GET_MODE (op) == VOIDmode) +#ifdef LEGITIMATE_PIC_OPERAND_P + && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op)) +#endif + && LEGITIMATE_CONSTANT_P (op)); +} + +/* Returns 1 if OP is an operand that is a CONST_INT. */ + +int +const_int_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + return GET_CODE (op) == CONST_INT; +} + +/* Returns 1 if OP is an operand that is a constant integer or constant + floating-point number. */ + +int +const_double_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + /* Don't accept CONST_INT or anything similar + if the caller wants something floating. */ + if (GET_MODE (op) == VOIDmode && mode != VOIDmode + && GET_MODE_CLASS (mode) != MODE_INT + && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT) + return 0; + + return ((GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT) + && (mode == VOIDmode || GET_MODE (op) == mode + || GET_MODE (op) == VOIDmode)); +} + +/* Return 1 if OP is a general operand that is not an immediate operand. */ + +int +nonimmediate_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + return (general_operand (op, mode) && ! CONSTANT_P (op)); +} + +/* Return 1 if OP is a register reference or immediate value of mode MODE. */ + +int +nonmemory_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + if (CONSTANT_P (op)) + { + /* Don't accept CONST_INT or anything similar + if the caller wants something floating. */ + if (GET_MODE (op) == VOIDmode && mode != VOIDmode + && GET_MODE_CLASS (mode) != MODE_INT + && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT) + return 0; + + return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode) +#ifdef LEGITIMATE_PIC_OPERAND_P + && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op)) +#endif + && LEGITIMATE_CONSTANT_P (op)); + } + + if (GET_MODE (op) != mode && mode != VOIDmode) + return 0; + + if (GET_CODE (op) == SUBREG) + { + /* Before reload, we can allow (SUBREG (MEM...)) as a register operand + because it is guaranteed to be reloaded into one. + Just make sure the MEM is valid in itself. + (Ideally, (SUBREG (MEM)...) should not exist after reload, + but currently it does result from (SUBREG (REG)...) where the + reg went on the stack.) */ + if (! reload_completed && GET_CODE (SUBREG_REG (op)) == MEM) + return general_operand (op, mode); + op = SUBREG_REG (op); + } + + /* We don't consider registers whose class is NO_REGS + to be a register operand. */ + return (GET_CODE (op) == REG + && (REGNO (op) >= FIRST_PSEUDO_REGISTER + || REGNO_REG_CLASS (REGNO (op)) != NO_REGS)); +} + +/* Return 1 if OP is a valid operand that stands for pushing a + value of mode MODE onto the stack. + + The main use of this function is as a predicate in match_operand + expressions in the machine description. */ + +int +push_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) != MEM) + return 0; + + if (GET_MODE (op) != mode) + return 0; + + op = XEXP (op, 0); + + if (GET_CODE (op) != STACK_PUSH_CODE) + return 0; + + return XEXP (op, 0) == stack_pointer_rtx; +} + +/* Return 1 if ADDR is a valid memory address for mode MODE. */ + +int +memory_address_p (mode, addr) + enum machine_mode mode; + register rtx addr; +{ + GO_IF_LEGITIMATE_ADDRESS (mode, addr, win); + return 0; + + win: + return 1; +} + +/* Return 1 if OP is a valid memory reference with mode MODE, + including a valid address. + + The main use of this function is as a predicate in match_operand + expressions in the machine description. */ + +int +memory_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + rtx inner; + + if (! reload_completed) + /* Note that no SUBREG is a memory operand before end of reload pass, + because (SUBREG (MEM...)) forces reloading into a register. */ + return GET_CODE (op) == MEM && general_operand (op, mode); + + if (mode != VOIDmode && GET_MODE (op) != mode) + return 0; + + inner = op; + if (GET_CODE (inner) == SUBREG) + inner = SUBREG_REG (inner); + + return (GET_CODE (inner) == MEM && general_operand (op, mode)); +} + +/* Return 1 if OP is a valid indirect memory reference with mode MODE; + that is, a memory reference whose address is a general_operand. */ + +int +indirect_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + /* Before reload, a SUBREG isn't in memory (see memory_operand, above). */ + if (! reload_completed + && GET_CODE (op) == SUBREG && GET_CODE (SUBREG_REG (op)) == MEM) + { + register int offset = SUBREG_WORD (op) * UNITS_PER_WORD; + rtx inner = SUBREG_REG (op); + +#if BYTES_BIG_ENDIAN + offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (op))) + - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (inner)))); +#endif + + /* The only way that we can have a general_operand as the resulting + address is if OFFSET is zero and the address already is an operand + or if the address is (plus Y (const_int -OFFSET)) and Y is an + operand. */ + + return ((offset == 0 && general_operand (XEXP (inner, 0), Pmode)) + || (GET_CODE (XEXP (inner, 0)) == PLUS + && GET_CODE (XEXP (XEXP (inner, 0), 1)) == CONST_INT + && INTVAL (XEXP (XEXP (inner, 0), 1)) == -offset + && general_operand (XEXP (XEXP (inner, 0), 0), Pmode))); + } + + return (GET_CODE (op) == MEM + && memory_operand (op, mode) + && general_operand (XEXP (op, 0), Pmode)); +} + +/* Return 1 if this is a comparison operator. This allows the use of + MATCH_OPERATOR to recognize all the branch insns. */ + +int +comparison_operator (op, mode) + register rtx op; + enum machine_mode mode; +{ + return ((mode == VOIDmode || GET_MODE (op) == mode) + && GET_RTX_CLASS (GET_CODE (op)) == '<'); +} + +/* If BODY is an insn body that uses ASM_OPERANDS, + return the number of operands (both input and output) in the insn. + Otherwise return -1. */ + +int +asm_noperands (body) + rtx body; +{ + if (GET_CODE (body) == ASM_OPERANDS) + /* No output operands: return number of input operands. */ + return ASM_OPERANDS_INPUT_LENGTH (body); + if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS) + /* Single output operand: BODY is (set OUTPUT (asm_operands ...)). */ + return ASM_OPERANDS_INPUT_LENGTH (SET_SRC (body)) + 1; + else if (GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == SET + && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) == ASM_OPERANDS) + { + /* Multiple output operands, or 1 output plus some clobbers: + body is [(set OUTPUT (asm_operands ...))... (clobber (reg ...))...]. */ + int i; + int n_sets; + + /* Count backwards through CLOBBERs to determine number of SETs. */ + for (i = XVECLEN (body, 0); i > 0; i--) + { + if (GET_CODE (XVECEXP (body, 0, i - 1)) == SET) + break; + if (GET_CODE (XVECEXP (body, 0, i - 1)) != CLOBBER) + return -1; + } + + /* N_SETS is now number of output operands. */ + n_sets = i; + + /* Verify that all the SETs we have + came from a single original asm_operands insn + (so that invalid combinations are blocked). */ + for (i = 0; i < n_sets; i++) + { + rtx elt = XVECEXP (body, 0, i); + if (GET_CODE (elt) != SET) + return -1; + if (GET_CODE (SET_SRC (elt)) != ASM_OPERANDS) + return -1; + /* If these ASM_OPERANDS rtx's came from different original insns + then they aren't allowed together. */ + if (ASM_OPERANDS_INPUT_VEC (SET_SRC (elt)) + != ASM_OPERANDS_INPUT_VEC (SET_SRC (XVECEXP (body, 0, 0)))) + return -1; + } + return (ASM_OPERANDS_INPUT_LENGTH (SET_SRC (XVECEXP (body, 0, 0))) + + n_sets); + } + else if (GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS) + { + /* 0 outputs, but some clobbers: + body is [(asm_operands ...) (clobber (reg ...))...]. */ + int i; + + /* Make sure all the other parallel things really are clobbers. */ + for (i = XVECLEN (body, 0) - 1; i > 0; i--) + if (GET_CODE (XVECEXP (body, 0, i)) != CLOBBER) + return -1; + + return ASM_OPERANDS_INPUT_LENGTH (XVECEXP (body, 0, 0)); + } + else + return -1; +} + +/* Assuming BODY is an insn body that uses ASM_OPERANDS, + copy its operands (both input and output) into the vector OPERANDS, + the locations of the operands within the insn into the vector OPERAND_LOCS, + and the constraints for the operands into CONSTRAINTS. + Write the modes of the operands into MODES. + Return the assembler-template. + + If MODES, OPERAND_LOCS, CONSTRAINTS or OPERANDS is 0, + we don't store that info. */ + +char * +decode_asm_operands (body, operands, operand_locs, constraints, modes) + rtx body; + rtx *operands; + rtx **operand_locs; + char **constraints; + enum machine_mode *modes; +{ + register int i; + int noperands; + char *template = 0; + + if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS) + { + rtx asmop = SET_SRC (body); + /* Single output operand: BODY is (set OUTPUT (asm_operands ....)). */ + + noperands = ASM_OPERANDS_INPUT_LENGTH (asmop) + 1; + + for (i = 1; i < noperands; i++) + { + if (operand_locs) + operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i - 1); + if (operands) + operands[i] = ASM_OPERANDS_INPUT (asmop, i - 1); + if (constraints) + constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i - 1); + if (modes) + modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i - 1); + } + + /* The output is in the SET. + Its constraint is in the ASM_OPERANDS itself. */ + if (operands) + operands[0] = SET_DEST (body); + if (operand_locs) + operand_locs[0] = &SET_DEST (body); + if (constraints) + constraints[0] = ASM_OPERANDS_OUTPUT_CONSTRAINT (asmop); + if (modes) + modes[0] = GET_MODE (SET_DEST (body)); + template = ASM_OPERANDS_TEMPLATE (asmop); + } + else if (GET_CODE (body) == ASM_OPERANDS) + { + rtx asmop = body; + /* No output operands: BODY is (asm_operands ....). */ + + noperands = ASM_OPERANDS_INPUT_LENGTH (asmop); + + /* The input operands are found in the 1st element vector. */ + /* Constraints for inputs are in the 2nd element vector. */ + for (i = 0; i < noperands; i++) + { + if (operand_locs) + operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i); + if (operands) + operands[i] = ASM_OPERANDS_INPUT (asmop, i); + if (constraints) + constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i); + if (modes) + modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i); + } + template = ASM_OPERANDS_TEMPLATE (asmop); + } + else if (GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == SET) + { + rtx asmop = SET_SRC (XVECEXP (body, 0, 0)); + int nparallel = XVECLEN (body, 0); /* Includes CLOBBERs. */ + int nin = ASM_OPERANDS_INPUT_LENGTH (asmop); + int nout = 0; /* Does not include CLOBBERs. */ + + /* At least one output, plus some CLOBBERs. */ + + /* The outputs are in the SETs. + Their constraints are in the ASM_OPERANDS itself. */ + for (i = 0; i < nparallel; i++) + { + if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER) + break; /* Past last SET */ + + if (operands) + operands[i] = SET_DEST (XVECEXP (body, 0, i)); + if (operand_locs) + operand_locs[i] = &SET_DEST (XVECEXP (body, 0, i)); + if (constraints) + constraints[i] = XSTR (SET_SRC (XVECEXP (body, 0, i)), 1); + if (modes) + modes[i] = GET_MODE (SET_DEST (XVECEXP (body, 0, i))); + nout++; + } + + for (i = 0; i < nin; i++) + { + if (operand_locs) + operand_locs[i + nout] = &ASM_OPERANDS_INPUT (asmop, i); + if (operands) + operands[i + nout] = ASM_OPERANDS_INPUT (asmop, i); + if (constraints) + constraints[i + nout] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i); + if (modes) + modes[i + nout] = ASM_OPERANDS_INPUT_MODE (asmop, i); + } + + template = ASM_OPERANDS_TEMPLATE (asmop); + } + else if (GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS) + { + /* No outputs, but some CLOBBERs. */ + + rtx asmop = XVECEXP (body, 0, 0); + int nin = ASM_OPERANDS_INPUT_LENGTH (asmop); + + for (i = 0; i < nin; i++) + { + if (operand_locs) + operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i); + if (operands) + operands[i] = ASM_OPERANDS_INPUT (asmop, i); + if (constraints) + constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i); + if (modes) + modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i); + } + + template = ASM_OPERANDS_TEMPLATE (asmop); + } + + return template; +} + +/* Given an rtx *P, if it is a sum containing an integer constant term, + return the location (type rtx *) of the pointer to that constant term. + Otherwise, return a null pointer. */ + +static rtx * +find_constant_term_loc (p) + rtx *p; +{ + register rtx *tem; + register enum rtx_code code = GET_CODE (*p); + + /* If *P IS such a constant term, P is its location. */ + + if (code == CONST_INT || code == SYMBOL_REF || code == LABEL_REF + || code == CONST) + return p; + + /* Otherwise, if not a sum, it has no constant term. */ + + if (GET_CODE (*p) != PLUS) + return 0; + + /* If one of the summands is constant, return its location. */ + + if (XEXP (*p, 0) && CONSTANT_P (XEXP (*p, 0)) + && XEXP (*p, 1) && CONSTANT_P (XEXP (*p, 1))) + return p; + + /* Otherwise, check each summand for containing a constant term. */ + + if (XEXP (*p, 0) != 0) + { + tem = find_constant_term_loc (&XEXP (*p, 0)); + if (tem != 0) + return tem; + } + + if (XEXP (*p, 1) != 0) + { + tem = find_constant_term_loc (&XEXP (*p, 1)); + if (tem != 0) + return tem; + } + + return 0; +} + +/* Return 1 if OP is a memory reference + whose address contains no side effects + and remains valid after the addition + of a positive integer less than the + size of the object being referenced. + + We assume that the original address is valid and do not check it. + + This uses strict_memory_address_p as a subroutine, so + don't use it before reload. */ + +int +offsettable_memref_p (op) + rtx op; +{ + return ((GET_CODE (op) == MEM) + && offsettable_address_p (1, GET_MODE (op), XEXP (op, 0))); +} + +/* Similar, but don't require a strictly valid mem ref: + consider pseudo-regs valid as index or base regs. */ + +int +offsettable_nonstrict_memref_p (op) + rtx op; +{ + return ((GET_CODE (op) == MEM) + && offsettable_address_p (0, GET_MODE (op), XEXP (op, 0))); +} + +/* Return 1 if Y is a memory address which contains no side effects + and would remain valid after the addition of a positive integer + less than the size of that mode. + + We assume that the original address is valid and do not check it. + We do check that it is valid for narrower modes. + + If STRICTP is nonzero, we require a strictly valid address, + for the sake of use in reload.c. */ + +int +offsettable_address_p (strictp, mode, y) + int strictp; + enum machine_mode mode; + register rtx y; +{ + register enum rtx_code ycode = GET_CODE (y); + register rtx z; + rtx y1 = y; + rtx *y2; + int (*addressp) () = (strictp ? strict_memory_address_p : memory_address_p); + + if (CONSTANT_ADDRESS_P (y)) + return 1; + + /* Adjusting an offsettable address involves changing to a narrower mode. + Make sure that's OK. */ + + if (mode_dependent_address_p (y)) + return 0; + + /* If the expression contains a constant term, + see if it remains valid when max possible offset is added. */ + + if ((ycode == PLUS) && (y2 = find_constant_term_loc (&y1))) + { + int good; + + y1 = *y2; + *y2 = plus_constant (*y2, GET_MODE_SIZE (mode) - 1); + /* Use QImode because an odd displacement may be automatically invalid + for any wider mode. But it should be valid for a single byte. */ + good = (*addressp) (QImode, y); + + /* In any case, restore old contents of memory. */ + *y2 = y1; + return good; + } + + if (ycode == PRE_DEC || ycode == PRE_INC + || ycode == POST_DEC || ycode == POST_INC) + return 0; + + /* The offset added here is chosen as the maximum offset that + any instruction could need to add when operating on something + of the specified mode. We assume that if Y and Y+c are + valid addresses then so is Y+d for all 0 0) + { + funny_match[funny_match_index].this = opno; + funny_match[funny_match_index++].other = c - '0'; + } + break; + + case 'p': + /* p is used for address_operands. When we are called by + gen_input_reload, no one will have checked that the + address is strictly valid, i.e., that all pseudos + requiring hard regs have gotten them. */ + if (strict <= 0 + || (strict_memory_address_p + (insn_operand_mode[insn_code_num][opno], op))) + win = 1; + break; + + /* No need to check general_operand again; + it was done in insn-recog.c. */ + case 'g': + /* Anything goes unless it is a REG and really has a hard reg + but the hard reg is not in the class GENERAL_REGS. */ + if (strict < 0 + || GENERAL_REGS == ALL_REGS + || GET_CODE (op) != REG + || (reload_in_progress + && REGNO (op) >= FIRST_PSEUDO_REGISTER) + || reg_fits_class_p (op, GENERAL_REGS, offset, mode)) + win = 1; + break; + + case 'r': + if (strict < 0 + || (strict == 0 + && GET_CODE (op) == REG + && REGNO (op) >= FIRST_PSEUDO_REGISTER) + || (strict == 0 && GET_CODE (op) == SCRATCH) + || (GET_CODE (op) == REG + && ((GENERAL_REGS == ALL_REGS + && REGNO (op) < FIRST_PSEUDO_REGISTER) + || reg_fits_class_p (op, GENERAL_REGS, + offset, mode)))) + win = 1; + break; + + case 'X': + /* This is used for a MATCH_SCRATCH in the cases when we + don't actually need anything. So anything goes any time. */ + win = 1; + break; + + case 'm': + if (GET_CODE (op) == MEM + /* Before reload, accept what reload can turn into mem. */ + || (strict < 0 && CONSTANT_P (op)) + /* During reload, accept a pseudo */ + || (reload_in_progress && GET_CODE (op) == REG + && REGNO (op) >= FIRST_PSEUDO_REGISTER)) + win = 1; + break; + + case '<': + if (GET_CODE (op) == MEM + && (GET_CODE (XEXP (op, 0)) == PRE_DEC + || GET_CODE (XEXP (op, 0)) == POST_DEC)) + win = 1; + break; + + case '>': + if (GET_CODE (op) == MEM + && (GET_CODE (XEXP (op, 0)) == PRE_INC + || GET_CODE (XEXP (op, 0)) == POST_INC)) + win = 1; + break; + + case 'E': + /* Match any CONST_DOUBLE, but only if + we can examine the bits of it reliably. */ + if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT + || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD) + && GET_MODE (op) != VOIDmode && ! flag_pretend_float) + break; + if (GET_CODE (op) == CONST_DOUBLE) + win = 1; + break; + + case 'F': + if (GET_CODE (op) == CONST_DOUBLE) + win = 1; + break; + + case 'G': + case 'H': + if (GET_CODE (op) == CONST_DOUBLE + && CONST_DOUBLE_OK_FOR_LETTER_P (op, c)) + win = 1; + break; + + case 's': + if (GET_CODE (op) == CONST_INT + || (GET_CODE (op) == CONST_DOUBLE + && GET_MODE (op) == VOIDmode)) + break; + case 'i': + if (CONSTANT_P (op)) + win = 1; + break; + + case 'n': + if (GET_CODE (op) == CONST_INT + || (GET_CODE (op) == CONST_DOUBLE + && GET_MODE (op) == VOIDmode)) + win = 1; + break; + + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + if (GET_CODE (op) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (op), c)) + win = 1; + break; + +#ifdef EXTRA_CONSTRAINT + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + if (EXTRA_CONSTRAINT (op, c)) + win = 1; + break; +#endif + + case 'V': + if (GET_CODE (op) == MEM + && ! offsettable_memref_p (op)) + win = 1; + break; + + case 'o': + if ((strict > 0 && offsettable_memref_p (op)) + || (strict == 0 && offsettable_nonstrict_memref_p (op)) + /* Before reload, accept what reload can handle. */ + || (strict < 0 + && (CONSTANT_P (op) || GET_CODE (op) == MEM)) + /* During reload, accept a pseudo */ + || (reload_in_progress && GET_CODE (op) == REG + && REGNO (op) >= FIRST_PSEUDO_REGISTER)) + win = 1; + break; + + default: + if (strict < 0 + || (strict == 0 + && GET_CODE (op) == REG + && REGNO (op) >= FIRST_PSEUDO_REGISTER) + || (strict == 0 && GET_CODE (op) == SCRATCH) + || (GET_CODE (op) == REG + && reg_fits_class_p (op, REG_CLASS_FROM_LETTER (c), + offset, mode))) + win = 1; + } + + constraints[opno] = p; + /* If this operand did not win somehow, + this alternative loses. */ + if (! win) + lose = 1; + } + /* This alternative won; the operands are ok. + Change whichever operands this alternative says to change. */ + if (! lose) + { + int opno, eopno; + + /* See if any earlyclobber operand conflicts with some other + operand. */ + + if (strict > 0) + for (eopno = 0; eopno < noperands; eopno++) + /* Ignore earlyclobber operands now in memory, + because we would often report failure when we have + two memory operands, one of which was formerly a REG. */ + if (earlyclobber[eopno] + && GET_CODE (recog_operand[eopno]) == REG) + for (opno = 0; opno < noperands; opno++) + if ((GET_CODE (recog_operand[opno]) == MEM + || op_types[opno] != OP_OUT) + && opno != eopno + /* Ignore things like match_operator operands. */ + && *constraints[opno] != 0 + && ! (matching_operands[opno] == eopno + && rtx_equal_p (recog_operand[opno], + recog_operand[eopno])) + && ! safe_from_earlyclobber (recog_operand[opno], + recog_operand[eopno])) + lose = 1; + + if (! lose) + { + while (--funny_match_index >= 0) + { + recog_operand[funny_match[funny_match_index].other] + = recog_operand[funny_match[funny_match_index].this]; + } + + return 1; + } + } + + which_alternative++; + } + + /* If we are about to reject this, but we are not to test strictly, + try a very loose test. Only return failure if it fails also. */ + if (strict == 0) + return constrain_operands (insn_code_num, -1); + else + return 0; +} + +/* Return 1 iff OPERAND (assumed to be a REG rtx) + is a hard reg in class CLASS when its regno is offsetted by OFFSET + and changed to mode MODE. + If REG occupies multiple hard regs, all of them must be in CLASS. */ + +int +reg_fits_class_p (operand, class, offset, mode) + rtx operand; + register enum reg_class class; + int offset; + enum machine_mode mode; +{ + register int regno = REGNO (operand); + if (regno < FIRST_PSEUDO_REGISTER + && TEST_HARD_REG_BIT (reg_class_contents[(int) class], + regno + offset)) + { + register int sr; + regno += offset; + for (sr = HARD_REGNO_NREGS (regno, mode) - 1; + sr > 0; sr--) + if (! TEST_HARD_REG_BIT (reg_class_contents[(int) class], + regno + sr)) + break; + return sr == 0; + } + + return 0; +} + +#endif /* REGISTER_CONSTRAINTS */ diff --git a/gnu/usr.bin/cc/common/recog.h b/gnu/usr.bin/cc/lib/recog.h similarity index 100% rename from gnu/usr.bin/cc/common/recog.h rename to gnu/usr.bin/cc/lib/recog.h diff --git a/gnu/usr.bin/cc/lib/reg-stack.c b/gnu/usr.bin/cc/lib/reg-stack.c new file mode 100644 index 0000000000..33bd2689d7 --- /dev/null +++ b/gnu/usr.bin/cc/lib/reg-stack.c @@ -0,0 +1,2897 @@ +/* Register to Stack convert for GNU compiler. + Copyright (C) 1992, 1993 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* This pass converts stack-like registers from the "flat register + file" model that gcc uses, to a stack convention that the 387 uses. + + * The form of the input: + + On input, the function consists of insn that have had their + registers fully allocated to a set of "virtual" registers. Note that + the word "virtual" is used differently here than elsewhere in gcc: for + each virtual stack reg, there is a hard reg, but the mapping between + them is not known until this pass is run. On output, hard register + numbers have been substituted, and various pop and exchange insns have + been emitted. The hard register numbers and the virtual register + numbers completely overlap - before this pass, all stack register + numbers are virtual, and afterward they are all hard. + + The virtual registers can be manipulated normally by gcc, and their + semantics are the same as for normal registers. After the hard + register numbers are substituted, the semantics of an insn containing + stack-like regs are not the same as for an insn with normal regs: for + instance, it is not safe to delete an insn that appears to be a no-op + move. In general, no insn containing hard regs should be changed + after this pass is done. + + * The form of the output: + + After this pass, hard register numbers represent the distance from + the current top of stack to the desired register. A reference to + FIRST_STACK_REG references the top of stack, FIRST_STACK_REG + 1, + represents the register just below that, and so forth. Also, REG_DEAD + notes indicate whether or not a stack register should be popped. + + A "swap" insn looks like a parallel of two patterns, where each + pattern is a SET: one sets A to B, the other B to A. + + A "push" or "load" insn is a SET whose SET_DEST is FIRST_STACK_REG + and whose SET_DEST is REG or MEM. Any other SET_DEST, such as PLUS, + will replace the existing stack top, not push a new value. + + A store insn is a SET whose SET_DEST is FIRST_STACK_REG, and whose + SET_SRC is REG or MEM. + + The case where the SET_SRC and SET_DEST are both FIRST_STACK_REG + appears ambiguous. As a special case, the presence of a REG_DEAD note + for FIRST_STACK_REG differentiates between a load insn and a pop. + + If a REG_DEAD is present, the insn represents a "pop" that discards + the top of the register stack. If there is no REG_DEAD note, then the + insn represents a "dup" or a push of the current top of stack onto the + stack. + + * Methodology: + + Existing REG_DEAD and REG_UNUSED notes for stack registers are + deleted and recreated from scratch. REG_DEAD is never created for a + SET_DEST, only REG_UNUSED. + + Before life analysis, the mode of each insn is set based on whether + or not any stack registers are mentioned within that insn. VOIDmode + means that no regs are mentioned anyway, and QImode means that at + least one pattern within the insn mentions stack registers. This + information is valid until after reg_to_stack returns, and is used + from jump_optimize. + + * asm_operands: + + There are several rules on the usage of stack-like regs in + asm_operands insns. These rules apply only to the operands that are + stack-like regs: + + 1. Given a set of input regs that die in an asm_operands, it is + necessary to know which are implicitly popped by the asm, and + which must be explicitly popped by gcc. + + An input reg that is implicitly popped by the asm must be + explicitly clobbered, unless it is constrained to match an + output operand. + + 2. For any input reg that is implicitly popped by an asm, it is + necessary to know how to adjust the stack to compensate for the pop. + If any non-popped input is closer to the top of the reg-stack than + the implicitly popped reg, it would not be possible to know what the + stack looked like - it's not clear how the rest of the stack "slides + up". + + All implicitly popped input regs must be closer to the top of + the reg-stack than any input that is not implicitly popped. + + 3. It is possible that if an input dies in an insn, reload might + use the input reg for an output reload. Consider this example: + + asm ("foo" : "=t" (a) : "f" (b)); + + This asm says that input B is not popped by the asm, and that + the asm pushes a result onto the reg-stack, ie, the stack is one + deeper after the asm than it was before. But, it is possible that + reload will think that it can use the same reg for both the input and + the output, if input B dies in this insn. + + If any input operand uses the "f" constraint, all output reg + constraints must use the "&" earlyclobber. + + The asm above would be written as + + asm ("foo" : "=&t" (a) : "f" (b)); + + 4. Some operands need to be in particular places on the stack. All + output operands fall in this category - there is no other way to + know which regs the outputs appear in unless the user indicates + this in the constraints. + + Output operands must specifically indicate which reg an output + appears in after an asm. "=f" is not allowed: the operand + constraints must select a class with a single reg. + + 5. Output operands may not be "inserted" between existing stack regs. + Since no 387 opcode uses a read/write operand, all output operands + are dead before the asm_operands, and are pushed by the asm_operands. + It makes no sense to push anywhere but the top of the reg-stack. + + Output operands must start at the top of the reg-stack: output + operands may not "skip" a reg. + + 6. Some asm statements may need extra stack space for internal + calculations. This can be guaranteed by clobbering stack registers + unrelated to the inputs and outputs. + + Here are a couple of reasonable asms to want to write. This asm + takes one input, which is internally popped, and produces two outputs. + + asm ("fsincos" : "=t" (cos), "=u" (sin) : "0" (inp)); + + This asm takes two inputs, which are popped by the fyl2xp1 opcode, + and replaces them with one output. The user must code the "st(1)" + clobber for reg-stack.c to know that fyl2xp1 pops both inputs. + + asm ("fyl2xp1" : "=t" (result) : "0" (x), "u" (y) : "st(1)"); + + */ + +#include +#include "config.h" +#include "tree.h" +#include "rtl.h" +#include "insn-config.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "flags.h" + +#ifdef STACK_REGS + +#define REG_STACK_SIZE (LAST_STACK_REG - FIRST_STACK_REG + 1) + +/* True if the current function returns a real value. */ +static int current_function_returns_real; + +/* This is the basic stack record. TOP is an index into REG[] such + that REG[TOP] is the top of stack. If TOP is -1 the stack is empty. + + If TOP is -2, REG[] is not yet initialized. Stack initialization + consists of placing each live reg in array `reg' and setting `top' + appropriately. + + REG_SET indicates which registers are live. */ + +typedef struct stack_def +{ + int top; /* index to top stack element */ + HARD_REG_SET reg_set; /* set of live registers */ + char reg[REG_STACK_SIZE]; /* register - stack mapping */ +} *stack; + +/* highest instruction uid */ +static int max_uid = 0; + +/* Number of basic blocks in the current function. */ +static int blocks; + +/* Element N is first insn in basic block N. + This info lasts until we finish compiling the function. */ +static rtx *block_begin; + +/* Element N is last insn in basic block N. + This info lasts until we finish compiling the function. */ +static rtx *block_end; + +/* Element N is nonzero if control can drop into basic block N */ +static char *block_drops_in; + +/* Element N says all about the stack at entry block N */ +static stack block_stack_in; + +/* Element N says all about the stack life at the end of block N */ +static HARD_REG_SET *block_out_reg_set; + +/* This is where the BLOCK_NUM values are really stored. This is set + up by find_blocks and used there and in life_analysis. It can be used + later, but only to look up an insn that is the head or tail of some + block. life_analysis and the stack register conversion process can + add insns within a block. */ +static int *block_number; + +/* This is the register file for all register after conversion */ +static rtx FP_mode_reg[FIRST_PSEUDO_REGISTER][(int) MAX_MACHINE_MODE]; + +/* Get the basic block number of an insn. See note at block_number + definition are validity of this information. */ + +#define BLOCK_NUM(INSN) \ + (((INSN_UID (INSN) > max_uid) \ + ? (int *)(abort() , 0) \ + : block_number)[INSN_UID (INSN)]) + +extern rtx gen_jump (); +extern rtx gen_movdf (); +extern rtx find_regno_note (); +extern rtx emit_jump_insn_before (); +extern rtx emit_label_after (); + +/* Forward declarations */ + +static void find_blocks (); +static void stack_reg_life_analysis (); +static void change_stack (); +static void convert_regs (); +static void dump_stack_info (); + +/* Return non-zero if any stack register is mentioned somewhere within PAT. */ + +int +stack_regs_mentioned_p (pat) + rtx pat; +{ + register char *fmt; + register int i; + + if (STACK_REG_P (pat)) + return 1; + + fmt = GET_RTX_FORMAT (GET_CODE (pat)); + for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--) + { + if (fmt[i] == 'E') + { + register int j; + + for (j = XVECLEN (pat, i) - 1; j >= 0; j--) + if (stack_regs_mentioned_p (XVECEXP (pat, i, j))) + return 1; + } + else if (fmt[i] == 'e' && stack_regs_mentioned_p (XEXP (pat, i))) + return 1; + } + + return 0; +} + +/* Convert register usage from "flat" register file usage to a "stack + register file. FIRST is the first insn in the function, FILE is the + dump file, if used. + + First compute the beginning and end of each basic block. Do a + register life analysis on the stack registers, recording the result + for the head and tail of each basic block. The convert each insn one + by one. Run a last jump_optimize() pass, if optimizing, to eliminate + any cross-jumping created when the converter inserts pop insns.*/ + +void +reg_to_stack (first, file) + rtx first; + FILE *file; +{ + register rtx insn; + register int i; + int stack_reg_seen = 0; + enum machine_mode mode; + + current_function_returns_real + = TREE_CODE (TREE_TYPE (DECL_RESULT (current_function_decl))) == REAL_TYPE; + + for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); mode != VOIDmode; + mode = GET_MODE_WIDER_MODE (mode)) + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + FP_mode_reg[i][(int) mode] = gen_rtx (REG, mode, i); + + /* Count the basic blocks. Also find maximum insn uid. */ + { + register RTX_CODE prev_code = JUMP_INSN; + register RTX_CODE code; + + max_uid = 0; + blocks = 0; + for (insn = first; insn; insn = NEXT_INSN (insn)) + { + /* Note that this loop must select the same block boundaries + as code in find_blocks. */ + + if (INSN_UID (insn) > max_uid) + max_uid = INSN_UID (insn); + + code = GET_CODE (insn); + + if (code == CODE_LABEL + || (prev_code != INSN + && prev_code != CALL_INSN + && prev_code != CODE_LABEL + && (code == INSN || code == CALL_INSN || code == JUMP_INSN))) + blocks++; + + /* Remember whether or not this insn mentions an FP regs. + Check JUMP_INSNs too, in case someone creates a funny PARALLEL. */ + + if ((GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN + || GET_CODE (insn) == JUMP_INSN) + && stack_regs_mentioned_p (PATTERN (insn))) + { + stack_reg_seen = 1; + PUT_MODE (insn, QImode); + } + else + PUT_MODE (insn, VOIDmode); + + if (code != NOTE) + prev_code = code; + } + } + + /* If no stack register reference exists in this insn, there isn't + anything to convert. */ + + if (! stack_reg_seen) + return; + + /* If there are stack registers, there must be at least one block. */ + + if (! blocks) + abort (); + + /* Allocate some tables that last till end of compiling this function + and some needed only in find_blocks and life_analysis. */ + + block_begin = (rtx *) alloca (blocks * sizeof (rtx)); + block_end = (rtx *) alloca (blocks * sizeof (rtx)); + block_drops_in = (char *) alloca (blocks); + + block_stack_in = (stack) alloca (blocks * sizeof (struct stack_def)); + block_out_reg_set = (HARD_REG_SET *) alloca (blocks * sizeof (HARD_REG_SET)); + bzero (block_stack_in, blocks * sizeof (struct stack_def)); + bzero (block_out_reg_set, blocks * sizeof (HARD_REG_SET)); + + block_number = (int *) alloca ((max_uid + 1) * sizeof (int)); + + find_blocks (first); + stack_reg_life_analysis (first); + + /* Dump the life analysis debug information before jump + optimization, as that will destroy the LABEL_REFS we keep the + information in. */ + + if (file) + dump_stack_info (file); + + convert_regs (); + + if (optimize) + jump_optimize (first, 2, 0, 0); +} + +/* Check PAT, which is in INSN, for LABEL_REFs. Add INSN to the + label's chain of references, and note which insn contains each + reference. */ + +static void +record_label_references (insn, pat) + rtx insn, pat; +{ + register enum rtx_code code = GET_CODE (pat); + register int i; + register char *fmt; + + if (code == LABEL_REF) + { + register rtx label = XEXP (pat, 0); + register rtx ref; + + if (GET_CODE (label) != CODE_LABEL) + abort (); + + /* Don't make a duplicate in the code_label's chain. */ + + for (ref = LABEL_REFS (label); ref != label; ref = LABEL_NEXTREF (ref)) + if (CONTAINING_INSN (ref) == insn) + return; + + CONTAINING_INSN (pat) = insn; + LABEL_NEXTREF (pat) = LABEL_REFS (label); + LABEL_REFS (label) = pat; + + return; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + record_label_references (insn, XEXP (pat, i)); + if (fmt[i] == 'E') + { + register int j; + for (j = 0; j < XVECLEN (pat, i); j++) + record_label_references (insn, XVECEXP (pat, i, j)); + } + } +} + +/* Return a pointer to the REG expression within PAT. If PAT is not a + REG, possible enclosed by a conversion rtx, return the inner part of + PAT that stopped the search. */ + +static rtx * +get_true_reg (pat) + rtx *pat; +{ + while (GET_CODE (*pat) == SUBREG + || GET_CODE (*pat) == FLOAT + || GET_CODE (*pat) == FIX + || GET_CODE (*pat) == FLOAT_EXTEND) + pat = & XEXP (*pat, 0); + + return pat; +} + +/* Scan the OPERANDS and OPERAND_CONSTRAINTS of an asm_operands. + N_OPERANDS is the total number of operands. Return which alternative + matched, or -1 is no alternative matches. + + OPERAND_MATCHES is an array which indicates which operand this + operand matches due to the constraints, or -1 if no match is required. + If two operands match by coincidence, but are not required to match by + the constraints, -1 is returned. + + OPERAND_CLASS is an array which indicates the smallest class + required by the constraints. If the alternative that matches calls + for some class `class', and the operand matches a subclass of `class', + OPERAND_CLASS is set to `class' as required by the constraints, not to + the subclass. If an alternative allows more than one class, + OPERAND_CLASS is set to the smallest class that is a union of the + allowed classes. */ + +static int +constrain_asm_operands (n_operands, operands, operand_constraints, + operand_matches, operand_class) + int n_operands; + rtx *operands; + char **operand_constraints; + int *operand_matches; + enum reg_class *operand_class; +{ + char **constraints = (char **) alloca (n_operands * sizeof (char *)); + char *q; + int this_alternative, this_operand; + int n_alternatives; + int j; + + for (j = 0; j < n_operands; j++) + constraints[j] = operand_constraints[j]; + + /* Compute the number of alternatives in the operands. reload has + already guaranteed that all operands have the same number of + alternatives. */ + + n_alternatives = 1; + for (q = constraints[0]; *q; q++) + n_alternatives += (*q == ','); + + this_alternative = 0; + while (this_alternative < n_alternatives) + { + int lose = 0; + int i; + + /* No operands match, no narrow class requirements yet. */ + for (i = 0; i < n_operands; i++) + { + operand_matches[i] = -1; + operand_class[i] = NO_REGS; + } + + for (this_operand = 0; this_operand < n_operands; this_operand++) + { + rtx op = operands[this_operand]; + enum machine_mode mode = GET_MODE (op); + char *p = constraints[this_operand]; + int offset = 0; + int win = 0; + int c; + + if (GET_CODE (op) == SUBREG) + { + if (GET_CODE (SUBREG_REG (op)) == REG + && REGNO (SUBREG_REG (op)) < FIRST_PSEUDO_REGISTER) + offset = SUBREG_WORD (op); + op = SUBREG_REG (op); + } + + /* An empty constraint or empty alternative + allows anything which matched the pattern. */ + if (*p == 0 || *p == ',') + win = 1; + + while (*p && (c = *p++) != ',') + switch (c) + { + case '=': + case '+': + case '?': + case '&': + case '!': + case '*': + case '%': + /* Ignore these. */ + break; + + case '#': + /* Ignore rest of this alternative. */ + while (*p && *p != ',') p++; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + /* This operand must be the same as a previous one. + This kind of constraint is used for instructions such + as add when they take only two operands. + + Note that the lower-numbered operand is passed first. */ + + if (operands_match_p (operands[c - '0'], + operands[this_operand])) + { + operand_matches[this_operand] = c - '0'; + win = 1; + } + break; + + case 'p': + /* p is used for address_operands. Since this is an asm, + just to make sure that the operand is valid for Pmode. */ + + if (strict_memory_address_p (Pmode, op)) + win = 1; + break; + + case 'g': + /* Anything goes unless it is a REG and really has a hard reg + but the hard reg is not in the class GENERAL_REGS. */ + if (GENERAL_REGS == ALL_REGS + || GET_CODE (op) != REG + || reg_fits_class_p (op, GENERAL_REGS, offset, mode)) + { + if (GET_CODE (op) == REG) + operand_class[this_operand] + = reg_class_subunion[(int) operand_class[this_operand]][(int) GENERAL_REGS]; + win = 1; + } + break; + + case 'r': + if (GET_CODE (op) == REG + && (GENERAL_REGS == ALL_REGS + || reg_fits_class_p (op, GENERAL_REGS, offset, mode))) + { + operand_class[this_operand] + = reg_class_subunion[(int) operand_class[this_operand]][(int) GENERAL_REGS]; + win = 1; + } + break; + + case 'X': + /* This is used for a MATCH_SCRATCH in the cases when we + don't actually need anything. So anything goes any time. */ + win = 1; + break; + + case 'm': + if (GET_CODE (op) == MEM) + win = 1; + break; + + case '<': + if (GET_CODE (op) == MEM + && (GET_CODE (XEXP (op, 0)) == PRE_DEC + || GET_CODE (XEXP (op, 0)) == POST_DEC)) + win = 1; + break; + + case '>': + if (GET_CODE (op) == MEM + && (GET_CODE (XEXP (op, 0)) == PRE_INC + || GET_CODE (XEXP (op, 0)) == POST_INC)) + win = 1; + break; + + case 'E': + /* Match any CONST_DOUBLE, but only if + we can examine the bits of it reliably. */ + if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT + || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD) + && GET_CODE (op) != VOIDmode && ! flag_pretend_float) + break; + if (GET_CODE (op) == CONST_DOUBLE) + win = 1; + break; + + case 'F': + if (GET_CODE (op) == CONST_DOUBLE) + win = 1; + break; + + case 'G': + case 'H': + if (GET_CODE (op) == CONST_DOUBLE + && CONST_DOUBLE_OK_FOR_LETTER_P (op, c)) + win = 1; + break; + + case 's': + if (GET_CODE (op) == CONST_INT + || (GET_CODE (op) == CONST_DOUBLE + && GET_MODE (op) == VOIDmode)) + break; + /* Fall through */ + case 'i': + if (CONSTANT_P (op)) + win = 1; + break; + + case 'n': + if (GET_CODE (op) == CONST_INT + || (GET_CODE (op) == CONST_DOUBLE + && GET_MODE (op) == VOIDmode)) + win = 1; + break; + + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + if (GET_CODE (op) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (op), c)) + win = 1; + break; + +#ifdef EXTRA_CONSTRAINT + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + if (EXTRA_CONSTRAINT (op, c)) + win = 1; + break; +#endif + + case 'V': + if (GET_CODE (op) == MEM && ! offsettable_memref_p (op)) + win = 1; + break; + + case 'o': + if (offsettable_memref_p (op)) + win = 1; + break; + + default: + if (GET_CODE (op) == REG + && reg_fits_class_p (op, REG_CLASS_FROM_LETTER (c), + offset, mode)) + { + operand_class[this_operand] + = reg_class_subunion[(int)operand_class[this_operand]][(int) REG_CLASS_FROM_LETTER (c)]; + win = 1; + } + } + + constraints[this_operand] = p; + /* If this operand did not win somehow, + this alternative loses. */ + if (! win) + lose = 1; + } + /* This alternative won; the operands are ok. + Change whichever operands this alternative says to change. */ + if (! lose) + break; + + this_alternative++; + } + + /* For operands constrained to match another operand, copy the other + operand's class to this operand's class. */ + for (j = 0; j < n_operands; j++) + if (operand_matches[j] >= 0) + operand_class[j] = operand_class[operand_matches[j]]; + + return this_alternative == n_alternatives ? -1 : this_alternative; +} + +/* Record the life info of each stack reg in INSN, updating REGSTACK. + N_INPUTS is the number of inputs; N_OUTPUTS the outputs. CONSTRAINTS + is an array of the constraint strings used in the asm statement. + OPERANDS is an array of all operands for the insn, and is assumed to + contain all output operands, then all inputs operands. + + There are many rules that an asm statement for stack-like regs must + follow. Those rules are explained at the top of this file: the rule + numbers below refer to that explanation. */ + +static void +record_asm_reg_life (insn, regstack, operands, constraints, + n_inputs, n_outputs) + rtx insn; + stack regstack; + rtx *operands; + char **constraints; + int n_inputs, n_outputs; +{ + int i; + int n_operands = n_inputs + n_outputs; + int first_input = n_outputs; + int n_clobbers; + int malformed_asm = 0; + rtx body = PATTERN (insn); + + int *operand_matches = (int *) alloca (n_operands * sizeof (int *)); + + enum reg_class *operand_class + = (enum reg_class *) alloca (n_operands * sizeof (enum reg_class *)); + + int reg_used_as_output[FIRST_PSEUDO_REGISTER]; + int implicitly_dies[FIRST_PSEUDO_REGISTER]; + + rtx *clobber_reg; + + /* Find out what the constraints require. If no constraint + alternative matches, this asm is malformed. */ + i = constrain_asm_operands (n_operands, operands, constraints, + operand_matches, operand_class); + if (i < 0) + malformed_asm = 1; + + /* Strip SUBREGs here to make the following code simpler. */ + for (i = 0; i < n_operands; i++) + if (GET_CODE (operands[i]) == SUBREG + && GET_CODE (SUBREG_REG (operands[i])) == REG) + operands[i] = SUBREG_REG (operands[i]); + + /* Set up CLOBBER_REG. */ + + n_clobbers = 0; + + if (GET_CODE (body) == PARALLEL) + { + clobber_reg = (rtx *) alloca (XVECLEN (body, 0) * sizeof (rtx *)); + + for (i = 0; i < XVECLEN (body, 0); i++) + if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER) + { + rtx clobber = XVECEXP (body, 0, i); + rtx reg = XEXP (clobber, 0); + + if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG) + reg = SUBREG_REG (reg); + + if (STACK_REG_P (reg)) + { + clobber_reg[n_clobbers] = reg; + n_clobbers++; + } + } + } + + /* Enforce rule #4: Output operands must specifically indicate which + reg an output appears in after an asm. "=f" is not allowed: the + operand constraints must select a class with a single reg. + + Also enforce rule #5: Output operands must start at the top of + the reg-stack: output operands may not "skip" a reg. */ + + bzero (reg_used_as_output, sizeof (reg_used_as_output)); + for (i = 0; i < n_outputs; i++) + if (STACK_REG_P (operands[i])) + if (reg_class_size[(int) operand_class[i]] != 1) + { + error_for_asm + (insn, "Output constraint %d must specify a single register", i); + malformed_asm = 1; + } + else + reg_used_as_output[REGNO (operands[i])] = 1; + + + /* Search for first non-popped reg. */ + for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++) + if (! reg_used_as_output[i]) + break; + + /* If there are any other popped regs, that's an error. */ + for (; i < LAST_STACK_REG + 1; i++) + if (reg_used_as_output[i]) + break; + + if (i != LAST_STACK_REG + 1) + { + error_for_asm (insn, "Output regs must be grouped at top of stack"); + malformed_asm = 1; + } + + /* Enforce rule #2: All implicitly popped input regs must be closer + to the top of the reg-stack than any input that is not implicitly + popped. */ + + bzero (implicitly_dies, sizeof (implicitly_dies)); + for (i = first_input; i < first_input + n_inputs; i++) + if (STACK_REG_P (operands[i])) + { + /* An input reg is implicitly popped if it is tied to an + output, or if there is a CLOBBER for it. */ + int j; + + for (j = 0; j < n_clobbers; j++) + if (operands_match_p (clobber_reg[j], operands[i])) + break; + + if (j < n_clobbers || operand_matches[i] >= 0) + implicitly_dies[REGNO (operands[i])] = 1; + } + + /* Search for first non-popped reg. */ + for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++) + if (! implicitly_dies[i]) + break; + + /* If there are any other popped regs, that's an error. */ + for (; i < LAST_STACK_REG + 1; i++) + if (implicitly_dies[i]) + break; + + if (i != LAST_STACK_REG + 1) + { + error_for_asm (insn, + "Implicitly popped regs must be grouped at top of stack"); + malformed_asm = 1; + } + + /* Enfore rule #3: If any input operand uses the "f" constraint, all + output constraints must use the "&" earlyclobber. + + ??? Detect this more deterministically by having constraint_asm_operands + record any earlyclobber. */ + + for (i = first_input; i < first_input + n_inputs; i++) + if (operand_matches[i] == -1) + { + int j; + + for (j = 0; j < n_outputs; j++) + if (operands_match_p (operands[j], operands[i])) + { + error_for_asm (insn, + "Output operand %d must use `&' constraint", j); + malformed_asm = 1; + } + } + + if (malformed_asm) + { + /* Avoid further trouble with this insn. */ + PATTERN (insn) = gen_rtx (USE, VOIDmode, const0_rtx); + PUT_MODE (insn, VOIDmode); + return; + } + + /* Process all outputs */ + for (i = 0; i < n_outputs; i++) + { + rtx op = operands[i]; + + if (! STACK_REG_P (op)) + if (stack_regs_mentioned_p (op)) + abort (); + else + continue; + + /* Each destination is dead before this insn. If the + destination is not used after this insn, record this with + REG_UNUSED. */ + + if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (op))) + REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_UNUSED, op, + REG_NOTES (insn)); + + CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (op)); + } + + /* Process all inputs */ + for (i = first_input; i < first_input + n_inputs; i++) + { + if (! STACK_REG_P (operands[i])) + if (stack_regs_mentioned_p (operands[i])) + abort (); + else + continue; + + /* If an input is dead after the insn, record a death note. + But don't record a death note if there is already a death note, + or if the input is also an output. */ + + if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (operands[i])) + && operand_matches[i] == -1 + && find_regno_note (insn, REG_DEAD, REGNO (operands[i])) == NULL_RTX) + REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_DEAD, operands[i], + REG_NOTES (insn)); + + SET_HARD_REG_BIT (regstack->reg_set, REGNO (operands[i])); + } +} + +/* Scan PAT, which is part of INSN, and record registers appearing in + a SET_DEST in DEST, and other registers in SRC. + + This function does not know about SET_DESTs that are both input and + output (such as ZERO_EXTRACT) - this cannot happen on a 387. */ + +void +record_reg_life_pat (pat, src, dest) + rtx pat; + HARD_REG_SET *src, *dest; +{ + register char *fmt; + register int i; + + if (STACK_REG_P (pat)) + { + if (src) + SET_HARD_REG_BIT (*src, REGNO (pat)); + + if (dest) + SET_HARD_REG_BIT (*dest, REGNO (pat)); + + return; + } + + if (GET_CODE (pat) == SET) + { + record_reg_life_pat (XEXP (pat, 0), NULL_PTR, dest); + record_reg_life_pat (XEXP (pat, 1), src, NULL_PTR); + return; + } + + /* We don't need to consider either of these cases. */ + if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER) + return; + + fmt = GET_RTX_FORMAT (GET_CODE (pat)); + for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--) + { + if (fmt[i] == 'E') + { + register int j; + + for (j = XVECLEN (pat, i) - 1; j >= 0; j--) + record_reg_life_pat (XVECEXP (pat, i, j), src, dest); + } + else if (fmt[i] == 'e') + record_reg_life_pat (XEXP (pat, i), src, dest); + } +} + +/* Calculate the number of inputs and outputs in BODY, an + asm_operands. N_OPERANDS is the total number of operands, and + N_INPUTS and N_OUTPUTS are pointers to ints into which the results are + placed. */ + +static void +get_asm_operand_lengths (body, n_operands, n_inputs, n_outputs) + rtx body; + int n_operands; + int *n_inputs, *n_outputs; +{ + if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS) + *n_inputs = ASM_OPERANDS_INPUT_LENGTH (SET_SRC (body)); + + else if (GET_CODE (body) == ASM_OPERANDS) + *n_inputs = ASM_OPERANDS_INPUT_LENGTH (body); + + else if (GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == SET) + *n_inputs = ASM_OPERANDS_INPUT_LENGTH (SET_SRC (XVECEXP (body, 0, 0))); + + else if (GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS) + *n_inputs = ASM_OPERANDS_INPUT_LENGTH (XVECEXP (body, 0, 0)); + else + abort (); + + *n_outputs = n_operands - *n_inputs; +} + +/* Scan INSN, which is in BLOCK, and record the life & death of stack + registers in REGSTACK. This function is called to process insns from + the last insn in a block to the first. The actual scanning is done in + record_reg_life_pat. + + If a register is live after a CALL_INSN, but is not a value return + register for that CALL_INSN, then code is emitted to initialize that + register. The block_end[] data is kept accurate. + + Existing death and unset notes for stack registers are deleted + before processing the insn. */ + +static void +record_reg_life (insn, block, regstack) + rtx insn; + int block; + stack regstack; +{ + rtx note, *note_link; + int n_operands; + + if ((GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN) + || INSN_DELETED_P (insn)) + return; + + /* Strip death notes for stack regs from this insn */ + + note_link = ®_NOTES(insn); + for (note = *note_link; note; note = XEXP (note, 1)) + if (STACK_REG_P (XEXP (note, 0)) + && (REG_NOTE_KIND (note) == REG_DEAD + || REG_NOTE_KIND (note) == REG_UNUSED)) + *note_link = XEXP (note, 1); + else + note_link = &XEXP (note, 1); + + /* Process all patterns in the insn. */ + + n_operands = asm_noperands (PATTERN (insn)); + if (n_operands >= 0) + { + /* This insn is an `asm' with operands. Decode the operands, + decide how many are inputs, and record the life information. */ + + rtx operands[MAX_RECOG_OPERANDS]; + rtx body = PATTERN (insn); + int n_inputs, n_outputs; + char **constraints = (char **) alloca (n_operands * sizeof (char *)); + + decode_asm_operands (body, operands, NULL_PTR, constraints, NULL_PTR); + get_asm_operand_lengths (body, n_operands, &n_inputs, &n_outputs); + record_asm_reg_life (insn, regstack, operands, constraints, + n_inputs, n_outputs); + return; + } + + /* An insn referencing a stack reg has a mode of QImode. */ + if (GET_MODE (insn) == QImode) + { + HARD_REG_SET src, dest; + int regno; + + CLEAR_HARD_REG_SET (src); + CLEAR_HARD_REG_SET (dest); + record_reg_life_pat (PATTERN (insn), &src, &dest); + + for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG; regno++) + if (! TEST_HARD_REG_BIT (regstack->reg_set, regno)) + { + if (TEST_HARD_REG_BIT (src, regno) + && ! TEST_HARD_REG_BIT (dest, regno)) + REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_DEAD, + FP_mode_reg[regno][(int) DFmode], + REG_NOTES (insn)); + else if (TEST_HARD_REG_BIT (dest, regno)) + REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_UNUSED, + FP_mode_reg[regno][(int) DFmode], + REG_NOTES (insn)); + } + + AND_COMPL_HARD_REG_SET (regstack->reg_set, dest); + IOR_HARD_REG_SET (regstack->reg_set, src); + } + + /* There might be a reg that is live after a function call. + Initialize it to zero so that the program does not crash. See comment + towards the end of stack_reg_life_analysis(). */ + + if (GET_CODE (insn) == CALL_INSN) + { + int reg = FIRST_FLOAT_REG; + + /* If a stack reg is mentioned in a CALL_INSN, it must be as the + return value. */ + + if (stack_regs_mentioned_p (PATTERN (insn))) + reg++; + + for (; reg <= LAST_STACK_REG; reg++) + if (TEST_HARD_REG_BIT (regstack->reg_set, reg)) + { + rtx init, pat; + + /* The insn will use virtual register numbers, and so + convert_regs is expected to process these. But BLOCK_NUM + cannot be used on these insns, because they do not appear in + block_number[]. */ + + pat = gen_rtx (SET, VOIDmode, FP_mode_reg[reg][(int) DFmode], + CONST0_RTX (DFmode)); + init = emit_insn_after (pat, insn); + PUT_MODE (init, QImode); + + CLEAR_HARD_REG_BIT (regstack->reg_set, reg); + + /* If the CALL_INSN was the end of a block, move the + block_end to point to the new insn. */ + + if (block_end[block] == insn) + block_end[block] = init; + } + + /* Some regs do not survive a CALL */ + + AND_COMPL_HARD_REG_SET (regstack->reg_set, call_used_reg_set); + } +} + +/* Find all basic blocks of the function, which starts with FIRST. + For each JUMP_INSN, build the chain of LABEL_REFS on each CODE_LABEL. */ + +static void +find_blocks (first) + rtx first; +{ + register rtx insn; + register int block; + register RTX_CODE prev_code = BARRIER; + register RTX_CODE code; + + /* Record where all the blocks start and end. + Record which basic blocks control can drop in to. */ + + block = -1; + for (insn = first; insn; insn = NEXT_INSN (insn)) + { + /* Note that this loop must select the same block boundaries + as code in reg_to_stack. */ + + code = GET_CODE (insn); + + if (code == CODE_LABEL + || (prev_code != INSN + && prev_code != CALL_INSN + && prev_code != CODE_LABEL + && (code == INSN || code == CALL_INSN || code == JUMP_INSN))) + { + block_begin[++block] = insn; + block_end[block] = insn; + block_drops_in[block] = prev_code != BARRIER; + } + else if (code == INSN || code == CALL_INSN || code == JUMP_INSN) + block_end[block] = insn; + + BLOCK_NUM (insn) = block; + + if (code == CODE_LABEL) + LABEL_REFS (insn) = insn; /* delete old chain */ + + if (code != NOTE) + prev_code = code; + } + + if (block + 1 != blocks) + abort (); + + /* generate all label references to the corresponding jump insn */ + for (block = 0; block < blocks; block++) + { + insn = block_end[block]; + + if (GET_CODE (insn) == JUMP_INSN) + record_label_references (insn, PATTERN (insn)); + } +} + +/* If current function returns its result in an fp stack register, + return the register number. Otherwise return -1. */ + +static int +stack_result_p (decl) + tree decl; +{ + rtx result = DECL_RTL (DECL_RESULT (decl)); + + if (result != 0 + && !(GET_CODE (result) == REG + && REGNO (result) < FIRST_PSEUDO_REGISTER)) + { +#ifdef FUNCTION_OUTGOING_VALUE + result + = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl); +#else + result = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl); +#endif + } + + return STACK_REG_P (result) ? REGNO (result) : -1; +} + +/* Determine the which registers are live at the start of each basic + block of the function whose first insn is FIRST. + + First, if the function returns a real_type, mark the function + return type as live at each return point, as the RTL may not give any + hint that the register is live. + + Then, start with the last block and work back to the first block. + Similarly, work backwards within each block, insn by insn, recording + which regs are die and which are used (and therefore live) in the + hard reg set of block_stack_in[]. + + After processing each basic block, if there is a label at the start + of the block, propagate the live registers to all jumps to this block. + + As a special case, if there are regs live in this block, that are + not live in a block containing a jump to this label, and the block + containing the jump has already been processed, we must propagate this + block's entry register life back to the block containing the jump, and + restart life analysis from there. + + In the worst case, this function may traverse the insns + REG_STACK_SIZE times. This is necessary, since a jump towards the end + of the insns may not know that a reg is live at a target that is early + in the insns. So we back up and start over with the new reg live. + + If there are registers that are live at the start of the function, + insns are emitted to initialize these registers. Something similar is + done after CALL_INSNs in record_reg_life. */ + +static void +stack_reg_life_analysis (first) + rtx first; +{ + int reg, block; + struct stack_def regstack; + + if (current_function_returns_real + && stack_result_p (current_function_decl) >= 0) + { + /* Find all RETURN insns and mark them. */ + + int value_regno = stack_result_p (current_function_decl); + + for (block = blocks - 1; block >= 0; block--) + if (GET_CODE (block_end[block]) == JUMP_INSN + && GET_CODE (PATTERN (block_end[block])) == RETURN) + SET_HARD_REG_BIT (block_out_reg_set[block], value_regno); + + /* Mark of the end of last block if we "fall off" the end of the + function into the epilogue. */ + + if (GET_CODE (block_end[blocks-1]) != JUMP_INSN + || GET_CODE (PATTERN (block_end[blocks-1])) == RETURN) + SET_HARD_REG_BIT (block_out_reg_set[blocks-1], value_regno); + } + + /* now scan all blocks backward for stack register use */ + + block = blocks - 1; + while (block >= 0) + { + register rtx insn, prev; + + /* current register status at last instruction */ + + COPY_HARD_REG_SET (regstack.reg_set, block_out_reg_set[block]); + + prev = block_end[block]; + do + { + insn = prev; + prev = PREV_INSN (insn); + + /* If the insn is a CALL_INSN, we need to ensure that + everything dies. But otherwise don't process unless there + are some stack regs present. */ + + if (GET_MODE (insn) == QImode || GET_CODE (insn) == CALL_INSN) + record_reg_life (insn, block, ®stack); + + } while (insn != block_begin[block]); + + /* Set the state at the start of the block. Mark that no + register mapping information known yet. */ + + COPY_HARD_REG_SET (block_stack_in[block].reg_set, regstack.reg_set); + block_stack_in[block].top = -2; + + /* If there is a label, propagate our register life to all jumps + to this label. */ + + if (GET_CODE (insn) == CODE_LABEL) + { + register rtx label; + int must_restart = 0; + + for (label = LABEL_REFS (insn); label != insn; + label = LABEL_NEXTREF (label)) + { + int jump_block = BLOCK_NUM (CONTAINING_INSN (label)); + + if (jump_block < block) + IOR_HARD_REG_SET (block_out_reg_set[jump_block], + block_stack_in[block].reg_set); + else + { + /* The block containing the jump has already been + processed. If there are registers that were not known + to be live then, but are live now, we must back up + and restart life analysis from that point with the new + life information. */ + + GO_IF_HARD_REG_SUBSET (block_stack_in[block].reg_set, + block_out_reg_set[jump_block], + win); + + IOR_HARD_REG_SET (block_out_reg_set[jump_block], + block_stack_in[block].reg_set); + + block = jump_block; + must_restart = 1; + + win: + ; + } + } + if (must_restart) + continue; + } + + if (block_drops_in[block]) + IOR_HARD_REG_SET (block_out_reg_set[block-1], + block_stack_in[block].reg_set); + + block -= 1; + } + + { + /* If any reg is live at the start of the first block of a + function, then we must guarantee that the reg holds some value by + generating our own "load" of that register. Otherwise a 387 would + fault trying to access an empty register. */ + + HARD_REG_SET empty_regs; + CLEAR_HARD_REG_SET (empty_regs); + GO_IF_HARD_REG_SUBSET (block_stack_in[0].reg_set, empty_regs, + no_live_regs); + } + + /* Load zero into each live register. The fact that a register + appears live at the function start does not necessarily imply an error + in the user program: it merely means that we could not determine that + there wasn't such an error, just as -Wunused sometimes gives + "incorrect" warnings. In those cases, these initializations will do + no harm. + + Note that we are inserting virtual register references here: + these insns must be processed by convert_regs later. Also, these + insns will not be in block_number, so BLOCK_NUM() will fail for them. */ + + for (reg = LAST_STACK_REG; reg >= FIRST_STACK_REG; reg--) + if (TEST_HARD_REG_BIT (block_stack_in[0].reg_set, reg)) + { + rtx init_rtx; + + init_rtx = gen_rtx (SET, VOIDmode, FP_mode_reg[reg][(int) DFmode], + CONST0_RTX (DFmode)); + block_begin[0] = emit_insn_after (init_rtx, first); + PUT_MODE (block_begin[0], QImode); + + CLEAR_HARD_REG_BIT (block_stack_in[0].reg_set, reg); + } + + no_live_regs: + ; +} + +/***************************************************************************** + This section deals with stack register substitution, and forms the second + pass over the RTL. + *****************************************************************************/ + +/* Replace REG, which is a pointer to a stack reg RTX, with an RTX for + the desired hard REGNO. */ + +static void +replace_reg (reg, regno) + rtx *reg; + int regno; +{ + if (regno < FIRST_STACK_REG || regno > LAST_STACK_REG + || ! STACK_REG_P (*reg)) + abort (); + + if (GET_MODE_CLASS (GET_MODE (*reg)) != MODE_FLOAT) + abort (); + + *reg = FP_mode_reg[regno][(int) GET_MODE (*reg)]; +} + +/* Remove a note of type NOTE, which must be found, for register + number REGNO from INSN. Remove only one such note. */ + +static void +remove_regno_note (insn, note, regno) + rtx insn; + enum reg_note note; + int regno; +{ + register rtx *note_link, this; + + note_link = ®_NOTES(insn); + for (this = *note_link; this; this = XEXP (this, 1)) + if (REG_NOTE_KIND (this) == note + && REG_P (XEXP (this, 0)) && REGNO (XEXP (this, 0)) == regno) + { + *note_link = XEXP (this, 1); + return; + } + else + note_link = &XEXP (this, 1); + + abort (); +} + +/* Find the hard register number of virtual register REG in REGSTACK. + The hard register number is relative to the top of the stack. -1 is + returned if the register is not found. */ + +static int +get_hard_regnum (regstack, reg) + stack regstack; + rtx reg; +{ + int i; + + if (! STACK_REG_P (reg)) + abort (); + + for (i = regstack->top; i >= 0; i--) + if (regstack->reg[i] == REGNO (reg)) + break; + + return i >= 0 ? (FIRST_STACK_REG + regstack->top - i) : -1; +} + +/* Delete INSN from the RTL. Mark the insn, but don't remove it from + the chain of insns. Doing so could confuse block_begin and block_end + if this were the only insn in the block. */ + +static void +delete_insn_for_stacker (insn) + rtx insn; +{ + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + INSN_DELETED_P (insn) = 1; +} + +/* Emit an insn to pop virtual register REG before or after INSN. + REGSTACK is the stack state after INSN and is updated to reflect this + pop. WHEN is either emit_insn_before or emit_insn_after. A pop insn + is represented as a SET whose destination is the register to be popped + and source is the top of stack. A death note for the top of stack + cases the movdf pattern to pop. */ + +static rtx +emit_pop_insn (insn, regstack, reg, when) + rtx insn; + stack regstack; + rtx reg; + rtx (*when)(); +{ + rtx pop_insn, pop_rtx; + int hard_regno; + + hard_regno = get_hard_regnum (regstack, reg); + + if (hard_regno < FIRST_STACK_REG) + abort (); + + pop_rtx = gen_rtx (SET, VOIDmode, FP_mode_reg[hard_regno][(int) DFmode], + FP_mode_reg[FIRST_STACK_REG][(int) DFmode]); + + pop_insn = (*when) (pop_rtx, insn); + /* ??? This used to be VOIDmode, but that seems wrong. */ + PUT_MODE (pop_insn, QImode); + + REG_NOTES (pop_insn) = gen_rtx (EXPR_LIST, REG_DEAD, + FP_mode_reg[FIRST_STACK_REG][(int) DFmode], + REG_NOTES (pop_insn)); + + regstack->reg[regstack->top - (hard_regno - FIRST_STACK_REG)] + = regstack->reg[regstack->top]; + regstack->top -= 1; + CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (reg)); + + return pop_insn; +} + +/* Emit an insn before or after INSN to swap virtual register REG with the + top of stack. WHEN should be `emit_insn_before' or `emit_insn_before' + REGSTACK is the stack state before the swap, and is updated to reflect + the swap. A swap insn is represented as a PARALLEL of two patterns: + each pattern moves one reg to the other. + + If REG is already at the top of the stack, no insn is emitted. */ + +static void +emit_swap_insn (insn, regstack, reg) + rtx insn; + stack regstack; + rtx reg; +{ + int hard_regno; + rtx gen_swapdf(); + rtx swap_rtx, swap_insn; + int tmp, other_reg; /* swap regno temps */ + rtx i1; /* the stack-reg insn prior to INSN */ + rtx i1set = NULL_RTX; /* the SET rtx within I1 */ + + hard_regno = get_hard_regnum (regstack, reg); + + if (hard_regno < FIRST_STACK_REG) + abort (); + if (hard_regno == FIRST_STACK_REG) + return; + + other_reg = regstack->top - (hard_regno - FIRST_STACK_REG); + + tmp = regstack->reg[other_reg]; + regstack->reg[other_reg] = regstack->reg[regstack->top]; + regstack->reg[regstack->top] = tmp; + + /* Find the previous insn involving stack regs, but don't go past + any labels, calls or jumps. */ + i1 = prev_nonnote_insn (insn); + while (i1 && GET_CODE (i1) == INSN && GET_MODE (i1) != QImode) + i1 = prev_nonnote_insn (i1); + + if (i1) + i1set = single_set (i1); + + if (i1set) + { + rtx i2; /* the stack-reg insn prior to I1 */ + rtx i1src = *get_true_reg (&SET_SRC (i1set)); + rtx i1dest = *get_true_reg (&SET_DEST (i1set)); + + /* If the previous register stack push was from the reg we are to + swap with, omit the swap. */ + + if (GET_CODE (i1dest) == REG && REGNO (i1dest) == FIRST_STACK_REG + && GET_CODE (i1src) == REG && REGNO (i1src) == hard_regno - 1 + && find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX) + return; + + /* If the previous insn wrote to the reg we are to swap with, + omit the swap. */ + + if (GET_CODE (i1dest) == REG && REGNO (i1dest) == hard_regno + && GET_CODE (i1src) == REG && REGNO (i1src) == FIRST_STACK_REG + && find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX) + return; + } + + if (GET_RTX_CLASS (GET_CODE (i1)) == 'i' && sets_cc0_p (PATTERN (i1))) + { + i1 = next_nonnote_insn (i1); + if (i1 == insn) + abort (); + } + + swap_rtx = gen_swapdf (FP_mode_reg[hard_regno][(int) DFmode], + FP_mode_reg[FIRST_STACK_REG][(int) DFmode]); + swap_insn = emit_insn_after (swap_rtx, i1); + /* ??? This used to be VOIDmode, but that seems wrong. */ + PUT_MODE (swap_insn, QImode); +} + +/* Handle a move to or from a stack register in PAT, which is in INSN. + REGSTACK is the current stack. */ + +static void +move_for_stack_reg (insn, regstack, pat) + rtx insn; + stack regstack; + rtx pat; +{ + rtx *src = get_true_reg (&SET_SRC (pat)); + rtx *dest = get_true_reg (&SET_DEST (pat)); + rtx note; + + if (STACK_REG_P (*src) && STACK_REG_P (*dest)) + { + /* Write from one stack reg to another. If SRC dies here, then + just change the register mapping and delete the insn. */ + + note = find_regno_note (insn, REG_DEAD, REGNO (*src)); + if (note) + { + int i; + + /* If this is a no-op move, there must not be a REG_DEAD note. */ + if (REGNO (*src) == REGNO (*dest)) + abort (); + + for (i = regstack->top; i >= 0; i--) + if (regstack->reg[i] == REGNO (*src)) + break; + + /* The source must be live, and the dest must be dead. */ + if (i < 0 || get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG) + abort (); + + /* It is possible that the dest is unused after this insn. + If so, just pop the src. */ + + if (find_regno_note (insn, REG_UNUSED, REGNO (*dest))) + { + emit_pop_insn (insn, regstack, *src, emit_insn_after); + + delete_insn_for_stacker (insn); + return; + } + + regstack->reg[i] = REGNO (*dest); + + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src)); + + delete_insn_for_stacker (insn); + + return; + } + + /* The source reg does not die. */ + + /* If this appears to be a no-op move, delete it, or else it + will confuse the machine description output patterns. But if + it is REG_UNUSED, we must pop the reg now, as per-insn processing + for REG_UNUSED will not work for deleted insns. */ + + if (REGNO (*src) == REGNO (*dest)) + { + if (find_regno_note (insn, REG_UNUSED, REGNO (*dest))) + emit_pop_insn (insn, regstack, *dest, emit_insn_after); + + delete_insn_for_stacker (insn); + return; + } + + /* The destination ought to be dead */ + if (get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG) + abort (); + + replace_reg (src, get_hard_regnum (regstack, *src)); + + regstack->reg[++regstack->top] = REGNO (*dest); + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + replace_reg (dest, FIRST_STACK_REG); + } + else if (STACK_REG_P (*src)) + { + /* Save from a stack reg to MEM, or possibly integer reg. Since + only top of stack may be saved, emit an exchange first if + needs be. */ + + emit_swap_insn (insn, regstack, *src); + + note = find_regno_note (insn, REG_DEAD, REGNO (*src)); + if (note) + { + replace_reg (&XEXP (note, 0), FIRST_STACK_REG); + regstack->top--; + CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src)); + } + + replace_reg (src, FIRST_STACK_REG); + } + else if (STACK_REG_P (*dest)) + { + /* Load from MEM, or possibly integer REG or constant, into the + stack regs. The actual target is always the top of the + stack. The stack mapping is changed to reflect that DEST is + now at top of stack. */ + + /* The destination ought to be dead */ + if (get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG) + abort (); + + if (regstack->top >= REG_STACK_SIZE) + abort (); + + regstack->reg[++regstack->top] = REGNO (*dest); + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + replace_reg (dest, FIRST_STACK_REG); + } + else + abort (); +} + +void +swap_rtx_condition (pat) + rtx pat; +{ + register char *fmt; + register int i; + + if (GET_RTX_CLASS (GET_CODE (pat)) == '<') + { + PUT_CODE (pat, swap_condition (GET_CODE (pat))); + return; + } + + fmt = GET_RTX_FORMAT (GET_CODE (pat)); + for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--) + { + if (fmt[i] == 'E') + { + register int j; + + for (j = XVECLEN (pat, i) - 1; j >= 0; j--) + swap_rtx_condition (XVECEXP (pat, i, j)); + } + else if (fmt[i] == 'e') + swap_rtx_condition (XEXP (pat, i)); + } +} + +/* Handle a comparison. Special care needs to be taken to avoid + causing comparisons that a 387 cannot do correctly, such as EQ. + + Also, a pop insn may need to be emitted. The 387 does have an + `fcompp' insn that can pop two regs, but it is sometimes too expensive + to do this - a `fcomp' followed by a `fstpl %st(0)' may be easier to + set up. */ + +static void +compare_for_stack_reg (insn, regstack, pat) + rtx insn; + stack regstack; + rtx pat; +{ + rtx *src1, *src2; + rtx src1_note, src2_note; + + src1 = get_true_reg (&XEXP (SET_SRC (pat), 0)); + src2 = get_true_reg (&XEXP (SET_SRC (pat), 1)); + + /* ??? If fxch turns out to be cheaper than fstp, give priority to + registers that die in this insn - move those to stack top first. */ + if (! STACK_REG_P (*src1) + || (STACK_REG_P (*src2) + && get_hard_regnum (regstack, *src2) == FIRST_STACK_REG)) + { + rtx temp, next; + + temp = XEXP (SET_SRC (pat), 0); + XEXP (SET_SRC (pat), 0) = XEXP (SET_SRC (pat), 1); + XEXP (SET_SRC (pat), 1) = temp; + + src1 = get_true_reg (&XEXP (SET_SRC (pat), 0)); + src2 = get_true_reg (&XEXP (SET_SRC (pat), 1)); + + next = next_cc0_user (insn); + if (next == NULL_RTX) + abort (); + + swap_rtx_condition (PATTERN (next)); + INSN_CODE (next) = -1; + INSN_CODE (insn) = -1; + } + + /* We will fix any death note later. */ + + src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1)); + + if (STACK_REG_P (*src2)) + src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2)); + else + src2_note = NULL_RTX; + + emit_swap_insn (insn, regstack, *src1); + + replace_reg (src1, FIRST_STACK_REG); + + if (STACK_REG_P (*src2)) + replace_reg (src2, get_hard_regnum (regstack, *src2)); + + if (src1_note) + { + CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (XEXP (src1_note, 0))); + replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG); + regstack->top--; + } + + /* If the second operand dies, handle that. But if the operands are + the same stack register, don't bother, because only one death is + needed, and it was just handled. */ + + if (src2_note + && ! (STACK_REG_P (*src1) && STACK_REG_P (*src2) + && REGNO (*src1) == REGNO (*src2))) + { + /* As a special case, two regs may die in this insn if src2 is + next to top of stack and the top of stack also dies. Since + we have already popped src1, "next to top of stack" is really + at top (FIRST_STACK_REG) now. */ + + if (get_hard_regnum (regstack, XEXP (src2_note, 0)) == FIRST_STACK_REG + && src1_note) + { + CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (XEXP (src2_note, 0))); + replace_reg (&XEXP (src2_note, 0), FIRST_STACK_REG + 1); + regstack->top--; + } + else + { + /* The 386 can only represent death of the first operand in + the case handled above. In all other cases, emit a separate + pop and remove the death note from here. */ + + link_cc0_insns (insn); + + remove_regno_note (insn, REG_DEAD, REGNO (XEXP (src2_note, 0))); + + emit_pop_insn (insn, regstack, XEXP (src2_note, 0), + emit_insn_after); + } + } +} + +/* Substitute new registers in PAT, which is part of INSN. REGSTACK + is the current register layout. */ + +static void +subst_stack_regs_pat (insn, regstack, pat) + rtx insn; + stack regstack; + rtx pat; +{ + rtx *dest, *src; + rtx *src1 = (rtx *) NULL_PTR, *src2; + rtx src1_note, src2_note; + + if (GET_CODE (pat) != SET) + return; + + dest = get_true_reg (&SET_DEST (pat)); + src = get_true_reg (&SET_SRC (pat)); + + /* See if this is a `movM' pattern, and handle elsewhere if so. */ + + if (*dest != cc0_rtx + && (STACK_REG_P (*src) + || (STACK_REG_P (*dest) + && (GET_CODE (*src) == REG || GET_CODE (*src) == MEM + || GET_CODE (*src) == CONST_DOUBLE)))) + move_for_stack_reg (insn, regstack, pat); + else + switch (GET_CODE (SET_SRC (pat))) + { + case COMPARE: + compare_for_stack_reg (insn, regstack, pat); + break; + + case CALL: + regstack->reg[++regstack->top] = REGNO (*dest); + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + replace_reg (dest, FIRST_STACK_REG); + break; + + case REG: + /* This is a `tstM2' case. */ + if (*dest != cc0_rtx) + abort (); + + src1 = src; + + /* Fall through. */ + + case FLOAT_TRUNCATE: + case SQRT: + case ABS: + case NEG: + /* These insns only operate on the top of the stack. DEST might + be cc0_rtx if we're processing a tstM pattern. Also, it's + possible that the tstM case results in a REG_DEAD note on the + source. */ + + if (src1 == 0) + src1 = get_true_reg (&XEXP (SET_SRC (pat), 0)); + + emit_swap_insn (insn, regstack, *src1); + + src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1)); + + if (STACK_REG_P (*dest)) + replace_reg (dest, FIRST_STACK_REG); + + if (src1_note) + { + replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG); + regstack->top--; + CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src1)); + } + + replace_reg (src1, FIRST_STACK_REG); + + break; + + case MINUS: + case DIV: + /* On i386, reversed forms of subM3 and divM3 exist for + MODE_FLOAT, so the same code that works for addM3 and mulM3 + can be used. */ + case MULT: + case PLUS: + /* These insns can accept the top of stack as a destination + from a stack reg or mem, or can use the top of stack as a + source and some other stack register (possibly top of stack) + as a destination. */ + + src1 = get_true_reg (&XEXP (SET_SRC (pat), 0)); + src2 = get_true_reg (&XEXP (SET_SRC (pat), 1)); + + /* We will fix any death note later. */ + + if (STACK_REG_P (*src1)) + src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1)); + else + src1_note = NULL_RTX; + if (STACK_REG_P (*src2)) + src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2)); + else + src2_note = NULL_RTX; + + /* If either operand is not a stack register, then the dest + must be top of stack. */ + + if (! STACK_REG_P (*src1) || ! STACK_REG_P (*src2)) + emit_swap_insn (insn, regstack, *dest); + else + { + /* Both operands are REG. If neither operand is already + at the top of stack, choose to make the one that is the dest + the new top of stack. */ + + int src1_hard_regnum, src2_hard_regnum; + + src1_hard_regnum = get_hard_regnum (regstack, *src1); + src2_hard_regnum = get_hard_regnum (regstack, *src2); + if (src1_hard_regnum == -1 || src2_hard_regnum == -1) + abort (); + + if (src1_hard_regnum != FIRST_STACK_REG + && src2_hard_regnum != FIRST_STACK_REG) + emit_swap_insn (insn, regstack, *dest); + } + + if (STACK_REG_P (*src1)) + replace_reg (src1, get_hard_regnum (regstack, *src1)); + if (STACK_REG_P (*src2)) + replace_reg (src2, get_hard_regnum (regstack, *src2)); + + if (src1_note) + { + /* If the register that dies is at the top of stack, then + the destination is somewhere else - merely substitute it. + But if the reg that dies is not at top of stack, then + move the top of stack to the dead reg, as though we had + done the insn and then a store-with-pop. */ + + if (REGNO (XEXP (src1_note, 0)) == regstack->reg[regstack->top]) + { + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + replace_reg (dest, get_hard_regnum (regstack, *dest)); + } + else + { + int regno = get_hard_regnum (regstack, XEXP (src1_note, 0)); + + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + replace_reg (dest, regno); + + regstack->reg[regstack->top - (regno - FIRST_STACK_REG)] + = regstack->reg[regstack->top]; + } + + CLEAR_HARD_REG_BIT (regstack->reg_set, + REGNO (XEXP (src1_note, 0))); + replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG); + regstack->top--; + } + else if (src2_note) + { + if (REGNO (XEXP (src2_note, 0)) == regstack->reg[regstack->top]) + { + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + replace_reg (dest, get_hard_regnum (regstack, *dest)); + } + else + { + int regno = get_hard_regnum (regstack, XEXP (src2_note, 0)); + + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + replace_reg (dest, regno); + + regstack->reg[regstack->top - (regno - FIRST_STACK_REG)] + = regstack->reg[regstack->top]; + } + + CLEAR_HARD_REG_BIT (regstack->reg_set, + REGNO (XEXP (src2_note, 0))); + replace_reg (&XEXP (src2_note, 0), FIRST_STACK_REG); + regstack->top--; + } + else + { + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + replace_reg (dest, get_hard_regnum (regstack, *dest)); + } + + break; + + case UNSPEC: + switch (XINT (SET_SRC (pat), 1)) + { + case 1: /* sin */ + case 2: /* cos */ + /* These insns only operate on the top of the stack. */ + + src1 = get_true_reg (&XVECEXP (SET_SRC (pat), 0, 0)); + + emit_swap_insn (insn, regstack, *src1); + + src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1)); + + if (STACK_REG_P (*dest)) + replace_reg (dest, FIRST_STACK_REG); + + if (src1_note) + { + replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG); + regstack->top--; + CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src1)); + } + + replace_reg (src1, FIRST_STACK_REG); + + break; + + default: + abort (); + } + break; + + default: + abort (); + } +} + +/* Substitute hard regnums for any stack regs in INSN, which has + N_INPUTS inputs and N_OUTPUTS outputs. REGSTACK is the stack info + before the insn, and is updated with changes made here. CONSTRAINTS is + an array of the constraint strings used in the asm statement. + + OPERANDS is an array of the operands, and OPERANDS_LOC is a + parallel array of where the operands were found. The output operands + all precede the input operands. + + There are several requirements and assumptions about the use of + stack-like regs in asm statements. These rules are enforced by + record_asm_stack_regs; see comments there for details. Any + asm_operands left in the RTL at this point may be assume to meet the + requirements, since record_asm_stack_regs removes any problem asm. */ + +static void +subst_asm_stack_regs (insn, regstack, operands, operands_loc, constraints, + n_inputs, n_outputs) + rtx insn; + stack regstack; + rtx *operands, **operands_loc; + char **constraints; + int n_inputs, n_outputs; +{ + int n_operands = n_inputs + n_outputs; + int first_input = n_outputs; + rtx body = PATTERN (insn); + + int *operand_matches = (int *) alloca (n_operands * sizeof (int *)); + enum reg_class *operand_class + = (enum reg_class *) alloca (n_operands * sizeof (enum reg_class *)); + + rtx *note_reg; /* Array of note contents */ + rtx **note_loc; /* Address of REG field of each note */ + enum reg_note *note_kind; /* The type of each note */ + + rtx *clobber_reg; + rtx **clobber_loc; + + struct stack_def temp_stack; + int n_notes; + int n_clobbers; + rtx note; + int i; + + /* Find out what the constraints required. If no constraint + alternative matches, that is a compiler bug: we should have caught + such an insn during the life analysis pass (and reload should have + caught it regardless). */ + + i = constrain_asm_operands (n_operands, operands, constraints, + operand_matches, operand_class); + if (i < 0) + abort (); + + /* Strip SUBREGs here to make the following code simpler. */ + for (i = 0; i < n_operands; i++) + if (GET_CODE (operands[i]) == SUBREG + && GET_CODE (SUBREG_REG (operands[i])) == REG) + { + operands_loc[i] = & SUBREG_REG (operands[i]); + operands[i] = SUBREG_REG (operands[i]); + } + + /* Set up NOTE_REG, NOTE_LOC and NOTE_KIND. */ + + for (i = 0, note = REG_NOTES (insn); note; note = XEXP (note, 1)) + i++; + + note_reg = (rtx *) alloca (i * sizeof (rtx)); + note_loc = (rtx **) alloca (i * sizeof (rtx *)); + note_kind = (enum reg_note *) alloca (i * sizeof (enum reg_note)); + + n_notes = 0; + for (note = REG_NOTES (insn); note; note = XEXP (note, 1)) + { + rtx reg = XEXP (note, 0); + rtx *loc = & XEXP (note, 0); + + if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG) + { + loc = & SUBREG_REG (reg); + reg = SUBREG_REG (reg); + } + + if (STACK_REG_P (reg) + && (REG_NOTE_KIND (note) == REG_DEAD + || REG_NOTE_KIND (note) == REG_UNUSED)) + { + note_reg[n_notes] = reg; + note_loc[n_notes] = loc; + note_kind[n_notes] = REG_NOTE_KIND (note); + n_notes++; + } + } + + /* Set up CLOBBER_REG and CLOBBER_LOC. */ + + n_clobbers = 0; + + if (GET_CODE (body) == PARALLEL) + { + clobber_reg = (rtx *) alloca (XVECLEN (body, 0) * sizeof (rtx *)); + clobber_loc = (rtx **) alloca (XVECLEN (body, 0) * sizeof (rtx **)); + + for (i = 0; i < XVECLEN (body, 0); i++) + if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER) + { + rtx clobber = XVECEXP (body, 0, i); + rtx reg = XEXP (clobber, 0); + rtx *loc = & XEXP (clobber, 0); + + if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG) + { + loc = & SUBREG_REG (reg); + reg = SUBREG_REG (reg); + } + + if (STACK_REG_P (reg)) + { + clobber_reg[n_clobbers] = reg; + clobber_loc[n_clobbers] = loc; + n_clobbers++; + } + } + } + + bcopy (regstack, &temp_stack, sizeof (temp_stack)); + + /* Put the input regs into the desired place in TEMP_STACK. */ + + for (i = first_input; i < first_input + n_inputs; i++) + if (STACK_REG_P (operands[i]) + && reg_class_subset_p (operand_class[i], FLOAT_REGS) + && operand_class[i] != FLOAT_REGS) + { + /* If an operand needs to be in a particular reg in + FLOAT_REGS, the constraint was either 't' or 'u'. Since + these constraints are for single register classes, and reload + guaranteed that operand[i] is already in that class, we can + just use REGNO (operands[i]) to know which actual reg this + operand needs to be in. */ + + int regno = get_hard_regnum (&temp_stack, operands[i]); + + if (regno < 0) + abort (); + + if (regno != REGNO (operands[i])) + { + /* operands[i] is not in the right place. Find it + and swap it with whatever is already in I's place. + K is where operands[i] is now. J is where it should + be. */ + int j, k, temp; + + k = temp_stack.top - (regno - FIRST_STACK_REG); + j = (temp_stack.top + - (REGNO (operands[i]) - FIRST_STACK_REG)); + + temp = temp_stack.reg[k]; + temp_stack.reg[k] = temp_stack.reg[j]; + temp_stack.reg[j] = temp; + } + } + + /* emit insns before INSN to make sure the reg-stack is in the right + order. */ + + change_stack (insn, regstack, &temp_stack, emit_insn_before); + + /* Make the needed input register substitutions. Do death notes and + clobbers too, because these are for inputs, not outputs. */ + + for (i = first_input; i < first_input + n_inputs; i++) + if (STACK_REG_P (operands[i])) + { + int regnum = get_hard_regnum (regstack, operands[i]); + + if (regnum < 0) + abort (); + + replace_reg (operands_loc[i], regnum); + } + + for (i = 0; i < n_notes; i++) + if (note_kind[i] == REG_DEAD) + { + int regnum = get_hard_regnum (regstack, note_reg[i]); + + if (regnum < 0) + abort (); + + replace_reg (note_loc[i], regnum); + } + + for (i = 0; i < n_clobbers; i++) + { + /* It's OK for a CLOBBER to reference a reg that is not live. + Don't try to replace it in that case. */ + int regnum = get_hard_regnum (regstack, clobber_reg[i]); + + if (regnum >= 0) + { + /* Sigh - clobbers always have QImode. But replace_reg knows + that these regs can't be MODE_INT and will abort. Just put + the right reg there without calling replace_reg. */ + + *clobber_loc[i] = FP_mode_reg[regnum][(int) DFmode]; + } + } + + /* Now remove from REGSTACK any inputs that the asm implicitly popped. */ + + for (i = first_input; i < first_input + n_inputs; i++) + if (STACK_REG_P (operands[i])) + { + /* An input reg is implicitly popped if it is tied to an + output, or if there is a CLOBBER for it. */ + int j; + + for (j = 0; j < n_clobbers; j++) + if (operands_match_p (clobber_reg[j], operands[i])) + break; + + if (j < n_clobbers || operand_matches[i] >= 0) + { + /* operands[i] might not be at the top of stack. But that's OK, + because all we need to do is pop the right number of regs + off of the top of the reg-stack. record_asm_stack_regs + guaranteed that all implicitly popped regs were grouped + at the top of the reg-stack. */ + + CLEAR_HARD_REG_BIT (regstack->reg_set, + regstack->reg[regstack->top]); + regstack->top--; + } + } + + /* Now add to REGSTACK any outputs that the asm implicitly pushed. + Note that there isn't any need to substitute register numbers. + ??? Explain why this is true. */ + + for (i = LAST_STACK_REG; i >= FIRST_STACK_REG; i--) + { + /* See if there is an output for this hard reg. */ + int j; + + for (j = 0; j < n_outputs; j++) + if (STACK_REG_P (operands[j]) && REGNO (operands[j]) == i) + { + regstack->reg[++regstack->top] = i; + SET_HARD_REG_BIT (regstack->reg_set, i); + break; + } + } + + /* Now emit a pop insn for any REG_UNUSED output, or any REG_DEAD + input that the asm didn't implicitly pop. If the asm didn't + implicitly pop an input reg, that reg will still be live. + + Note that we can't use find_regno_note here: the register numbers + in the death notes have already been substituted. */ + + for (i = 0; i < n_outputs; i++) + if (STACK_REG_P (operands[i])) + { + int j; + + for (j = 0; j < n_notes; j++) + if (REGNO (operands[i]) == REGNO (note_reg[j]) + && note_kind[j] == REG_UNUSED) + { + insn = emit_pop_insn (insn, regstack, operands[i], + emit_insn_after); + break; + } + } + + for (i = first_input; i < first_input + n_inputs; i++) + if (STACK_REG_P (operands[i])) + { + int j; + + for (j = 0; j < n_notes; j++) + if (REGNO (operands[i]) == REGNO (note_reg[j]) + && note_kind[j] == REG_DEAD + && TEST_HARD_REG_BIT (regstack->reg_set, REGNO (operands[i]))) + { + insn = emit_pop_insn (insn, regstack, operands[i], + emit_insn_after); + break; + } + } +} + +/* Substitute stack hard reg numbers for stack virtual registers in + INSN. Non-stack register numbers are not changed. REGSTACK is the + current stack content. Insns may be emitted as needed to arrange the + stack for the 387 based on the contents of the insn. */ + +static void +subst_stack_regs (insn, regstack) + rtx insn; + stack regstack; +{ + register rtx *note_link, note; + register int i; + int n_operands; + + if ((GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN) + || INSN_DELETED_P (insn)) + return; + + /* The stack should be empty at a call. */ + + if (GET_CODE (insn) == CALL_INSN) + for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++) + if (TEST_HARD_REG_BIT (regstack->reg_set, i)) + abort (); + + /* Do the actual substitution if any stack regs are mentioned. + Since we only record whether entire insn mentions stack regs, and + subst_stack_regs_pat only works for patterns that contain stack regs, + we must check each pattern in a parallel here. A call_value_pop could + fail otherwise. */ + + if (GET_MODE (insn) == QImode) + { + n_operands = asm_noperands (PATTERN (insn)); + if (n_operands >= 0) + { + /* This insn is an `asm' with operands. Decode the operands, + decide how many are inputs, and do register substitution. + Any REG_UNUSED notes will be handled by subst_asm_stack_regs. */ + + rtx operands[MAX_RECOG_OPERANDS]; + rtx *operands_loc[MAX_RECOG_OPERANDS]; + rtx body = PATTERN (insn); + int n_inputs, n_outputs; + char **constraints + = (char **) alloca (n_operands * sizeof (char *)); + + decode_asm_operands (body, operands, operands_loc, + constraints, NULL_PTR); + get_asm_operand_lengths (body, n_operands, &n_inputs, &n_outputs); + subst_asm_stack_regs (insn, regstack, operands, operands_loc, + constraints, n_inputs, n_outputs); + return; + } + + if (GET_CODE (PATTERN (insn)) == PARALLEL) + for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) + { + if (stack_regs_mentioned_p (XVECEXP (PATTERN (insn), 0, i))) + subst_stack_regs_pat (insn, regstack, + XVECEXP (PATTERN (insn), 0, i)); + } + else + subst_stack_regs_pat (insn, regstack, PATTERN (insn)); + } + + /* subst_stack_regs_pat may have deleted a no-op insn. If so, any + REG_UNUSED will already have been dealt with, so just return. */ + + if (INSN_DELETED_P (insn)) + return; + + /* If there is a REG_UNUSED note on a stack register on this insn, + the indicated reg must be popped. The REG_UNUSED note is removed, + since the form of the newly emitted pop insn references the reg, + making it no longer `unset'. */ + + note_link = ®_NOTES(insn); + for (note = *note_link; note; note = XEXP (note, 1)) + if (REG_NOTE_KIND (note) == REG_UNUSED && STACK_REG_P (XEXP (note, 0))) + { + *note_link = XEXP (note, 1); + insn = emit_pop_insn (insn, regstack, XEXP (note, 0), emit_insn_after); + } + else + note_link = &XEXP (note, 1); +} + +/* Change the organization of the stack so that it fits a new basic + block. Some registers might have to be popped, but there can never be + a register live in the new block that is not now live. + + Insert any needed insns before or after INSN. WHEN is emit_insn_before + or emit_insn_after. OLD is the original stack layout, and NEW is + the desired form. OLD is updated to reflect the code emitted, ie, it + will be the same as NEW upon return. + + This function will not preserve block_end[]. But that information + is no longer needed once this has executed. */ + +static void +change_stack (insn, old, new, when) + rtx insn; + stack old; + stack new; + rtx (*when)(); +{ + int reg; + + /* We will be inserting new insns "backwards", by calling emit_insn_before. + If we are to insert after INSN, find the next insn, and insert before + it. */ + + if (when == emit_insn_after) + insn = NEXT_INSN (insn); + + /* Pop any registers that are not needed in the new block. */ + + for (reg = old->top; reg >= 0; reg--) + if (! TEST_HARD_REG_BIT (new->reg_set, old->reg[reg])) + emit_pop_insn (insn, old, FP_mode_reg[old->reg[reg]][(int) DFmode], + emit_insn_before); + + if (new->top == -2) + { + /* If the new block has never been processed, then it can inherit + the old stack order. */ + + new->top = old->top; + bcopy (old->reg, new->reg, sizeof (new->reg)); + } + else + { + /* This block has been entered before, and we must match the + previously selected stack order. */ + + /* By now, the only difference should be the order of the stack, + not their depth or liveliness. */ + + GO_IF_HARD_REG_EQUAL (old->reg_set, new->reg_set, win); + + abort (); + + win: + + if (old->top != new->top) + abort (); + + /* Loop here emitting swaps until the stack is correct. The + worst case number of swaps emitted is N + 2, where N is the + depth of the stack. In some cases, the reg at the top of + stack may be correct, but swapped anyway in order to fix + other regs. But since we never swap any other reg away from + its correct slot, this algorithm will converge. */ + + do + { + /* Swap the reg at top of stack into the position it is + supposed to be in, until the correct top of stack appears. */ + + while (old->reg[old->top] != new->reg[new->top]) + { + for (reg = new->top; reg >= 0; reg--) + if (new->reg[reg] == old->reg[old->top]) + break; + + if (reg == -1) + abort (); + + emit_swap_insn (insn, old, + FP_mode_reg[old->reg[reg]][(int) DFmode]); + } + + /* See if any regs remain incorrect. If so, bring an + incorrect reg to the top of stack, and let the while loop + above fix it. */ + + for (reg = new->top; reg >= 0; reg--) + if (new->reg[reg] != old->reg[reg]) + { + emit_swap_insn (insn, old, + FP_mode_reg[old->reg[reg]][(int) DFmode]); + break; + } + } while (reg >= 0); + + /* At this point there must be no differences. */ + + for (reg = old->top; reg >= 0; reg--) + if (old->reg[reg] != new->reg[reg]) + abort (); + } +} + +/* Check PAT, which points to RTL in INSN, for a LABEL_REF. If it is + found, ensure that a jump from INSN to the code_label to which the + label_ref points ends up with the same stack as that at the + code_label. Do this by inserting insns just before the code_label to + pop and rotate the stack until it is in the correct order. REGSTACK + is the order of the register stack in INSN. + + Any code that is emitted here must not be later processed as part + of any block, as it will already contain hard register numbers. */ + +static void +goto_block_pat (insn, regstack, pat) + rtx insn; + stack regstack; + rtx pat; +{ + rtx label; + rtx new_jump, new_label, new_barrier; + rtx *ref; + stack label_stack; + struct stack_def temp_stack; + int reg; + + if (GET_CODE (pat) != LABEL_REF) + { + int i, j; + char *fmt = GET_RTX_FORMAT (GET_CODE (pat)); + + for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + goto_block_pat (insn, regstack, XEXP (pat, i)); + if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (pat, i); j++) + goto_block_pat (insn, regstack, XVECEXP (pat, i, j)); + } + return; + } + + label = XEXP (pat, 0); + if (GET_CODE (label) != CODE_LABEL) + abort (); + + /* First, see if in fact anything needs to be done to the stack at all. */ + + label_stack = &block_stack_in[BLOCK_NUM (label)]; + + if (label_stack->top == -2) + { + /* If the target block hasn't had a stack order selected, then + we need merely ensure that no pops are needed. */ + + for (reg = regstack->top; reg >= 0; reg--) + if (! TEST_HARD_REG_BIT (label_stack->reg_set, regstack->reg[reg])) + break; + + if (reg == -1) + { + /* change_stack will not emit any code in this case. */ + + change_stack (label, regstack, label_stack, emit_insn_after); + return; + } + } + else if (label_stack->top == regstack->top) + { + for (reg = label_stack->top; reg >= 0; reg--) + if (label_stack->reg[reg] != regstack->reg[reg]) + break; + + if (reg == -1) + return; + } + + /* At least one insn will need to be inserted before label. Insert + a jump around the code we are about to emit. Emit a label for the new + code, and point the original insn at this new label. We can't use + redirect_jump here, because we're using fld[4] of the code labels as + LABEL_REF chains, no NUSES counters. */ + + new_jump = emit_jump_insn_before (gen_jump (label), label); + record_label_references (new_jump, PATTERN (new_jump)); + JUMP_LABEL (new_jump) = label; + + new_barrier = emit_barrier_after (new_jump); + + new_label = gen_label_rtx (); + emit_label_after (new_label, new_barrier); + LABEL_REFS (new_label) = new_label; + + /* The old label_ref will no longer point to the code_label if now uses, + so strip the label_ref from the code_label's chain of references. */ + + for (ref = &LABEL_REFS (label); *ref != label; ref = &LABEL_NEXTREF (*ref)) + if (*ref == pat) + break; + + if (*ref == label) + abort (); + + *ref = LABEL_NEXTREF (*ref); + + XEXP (pat, 0) = new_label; + record_label_references (insn, PATTERN (insn)); + + if (JUMP_LABEL (insn) == label) + JUMP_LABEL (insn) = new_label; + + /* Now emit the needed code. */ + + temp_stack = *regstack; + + change_stack (new_label, &temp_stack, label_stack, emit_insn_after); +} + +/* Traverse all basic blocks in a function, converting the register + references in each insn from the "flat" register file that gcc uses, to + the stack-like registers the 387 uses. */ + +static void +convert_regs () +{ + register int block, reg; + register rtx insn, next; + struct stack_def regstack; + + for (block = 0; block < blocks; block++) + { + if (block_stack_in[block].top == -2) + { + /* This block has not been previously encountered. Choose a + default mapping for any stack regs live on entry */ + + block_stack_in[block].top = -1; + + for (reg = LAST_STACK_REG; reg >= FIRST_STACK_REG; reg--) + if (TEST_HARD_REG_BIT (block_stack_in[block].reg_set, reg)) + block_stack_in[block].reg[++block_stack_in[block].top] = reg; + } + + /* Process all insns in this block. Keep track of `next' here, + so that we don't process any insns emitted while making + substitutions in INSN. */ + + next = block_begin[block]; + regstack = block_stack_in[block]; + do + { + insn = next; + next = NEXT_INSN (insn); + + /* Don't bother processing unless there is a stack reg + mentioned. + + ??? For now, process CALL_INSNs too to make sure that the + stack regs are dead after a call. Remove this eventually. */ + + if (GET_MODE (insn) == QImode || GET_CODE (insn) == CALL_INSN) + subst_stack_regs (insn, ®stack); + + } while (insn != block_end[block]); + + /* Something failed if the stack life doesn't match. */ + + GO_IF_HARD_REG_EQUAL (regstack.reg_set, block_out_reg_set[block], win); + + abort (); + + win: + + /* Adjust the stack of this block on exit to match the stack of + the target block, or copy stack information into stack of + jump target if the target block's stack order hasn't been set + yet. */ + + if (GET_CODE (insn) == JUMP_INSN) + goto_block_pat (insn, ®stack, PATTERN (insn)); + + /* Likewise handle the case where we fall into the next block. */ + + if ((block < blocks - 1) && block_drops_in[block+1]) + change_stack (insn, ®stack, &block_stack_in[block+1], + emit_insn_after); + } + + /* If the last basic block is the end of a loop, and that loop has + regs live at its start, then the last basic block will have regs live + at its end that need to be popped before the function returns. */ + + for (reg = regstack.top; reg >= 0; reg--) + if (! current_function_returns_real + || regstack.reg[reg] != FIRST_STACK_REG) + insn = emit_pop_insn (insn, ®stack, + FP_mode_reg[regstack.reg[reg]][(int) DFmode], + emit_insn_after); +} + +/* Check expression PAT, which is in INSN, for label references. if + one is found, print the block number of destination to FILE. */ + +static void +print_blocks (file, insn, pat) + FILE *file; + rtx insn, pat; +{ + register RTX_CODE code = GET_CODE (pat); + register int i; + register char *fmt; + + if (code == LABEL_REF) + { + register rtx label = XEXP (pat, 0); + + if (GET_CODE (label) != CODE_LABEL) + abort (); + + fprintf (file, " %d", BLOCK_NUM (label)); + + return; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + print_blocks (file, insn, XEXP (pat, i)); + if (fmt[i] == 'E') + { + register int j; + for (j = 0; j < XVECLEN (pat, i); j++) + print_blocks (file, insn, XVECEXP (pat, i, j)); + } + } +} + +/* Write information about stack registers and stack blocks into FILE. + This is part of making a debugging dump. */ +static void +dump_stack_info (file) + FILE *file; +{ + register int block; + + fprintf (file, "\n%d stack blocks.\n", blocks); + for (block = 0; block < blocks; block++) + { + register rtx head, jump, end; + register int regno; + + fprintf (file, "\nStack block %d: first insn %d, last %d.\n", + block, INSN_UID (block_begin[block]), + INSN_UID (block_end[block])); + + head = block_begin[block]; + + fprintf (file, "Reached from blocks: "); + if (GET_CODE (head) == CODE_LABEL) + for (jump = LABEL_REFS (head); + jump != head; + jump = LABEL_NEXTREF (jump)) + { + register int from_block = BLOCK_NUM (CONTAINING_INSN (jump)); + fprintf (file, " %d", from_block); + } + if (block_drops_in[block]) + fprintf (file, " previous"); + + fprintf (file, "\nlive stack registers on block entry: "); + for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG ; regno++) + { + if (TEST_HARD_REG_BIT (block_stack_in[block].reg_set, regno)) + fprintf (file, "%d ", regno); + } + + fprintf (file, "\nlive stack registers on block exit: "); + for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG ; regno++) + { + if (TEST_HARD_REG_BIT (block_out_reg_set[block], regno)) + fprintf (file, "%d ", regno); + } + + end = block_end[block]; + + fprintf (file, "\nJumps to blocks: "); + if (GET_CODE (end) == JUMP_INSN) + print_blocks (file, end, PATTERN (end)); + + if (block + 1 < blocks && block_drops_in[block+1]) + fprintf (file, " next"); + else if (block + 1 == blocks + || (GET_CODE (end) == JUMP_INSN + && GET_CODE (PATTERN (end)) == RETURN)) + fprintf (file, " return"); + + fprintf (file, "\n"); + } +} +#endif /* STACK_REGS */ diff --git a/gnu/usr.bin/cc/common/regclass.c b/gnu/usr.bin/cc/lib/regclass.c similarity index 100% rename from gnu/usr.bin/cc/common/regclass.c rename to gnu/usr.bin/cc/lib/regclass.c diff --git a/gnu/usr.bin/cc/common/regs.h b/gnu/usr.bin/cc/lib/regs.h similarity index 100% rename from gnu/usr.bin/cc/common/regs.h rename to gnu/usr.bin/cc/lib/regs.h diff --git a/gnu/usr.bin/cc/lib/reload.c b/gnu/usr.bin/cc/lib/reload.c new file mode 100644 index 0000000000..868c96780f --- /dev/null +++ b/gnu/usr.bin/cc/lib/reload.c @@ -0,0 +1,5435 @@ +/* Search an insn for pseudo regs that must be in hard regs and are not. + Copyright (C) 1987, 1988, 1989, 1992, 1993 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* This file contains subroutines used only from the file reload1.c. + It knows how to scan one insn for operands and values + that need to be copied into registers to make valid code. + It also finds other operands and values which are valid + but for which equivalent values in registers exist and + ought to be used instead. + + Before processing the first insn of the function, call `init_reload'. + + To scan an insn, call `find_reloads'. This does two things: + 1. sets up tables describing which values must be reloaded + for this insn, and what kind of hard regs they must be reloaded into; + 2. optionally record the locations where those values appear in + the data, so they can be replaced properly later. + This is done only if the second arg to `find_reloads' is nonzero. + + The third arg to `find_reloads' specifies the number of levels + of indirect addressing supported by the machine. If it is zero, + indirect addressing is not valid. If it is one, (MEM (REG n)) + is valid even if (REG n) did not get a hard register; if it is two, + (MEM (MEM (REG n))) is also valid even if (REG n) did not get a + hard register, and similarly for higher values. + + Then you must choose the hard regs to reload those pseudo regs into, + and generate appropriate load insns before this insn and perhaps + also store insns after this insn. Set up the array `reload_reg_rtx' + to contain the REG rtx's for the registers you used. In some + cases `find_reloads' will return a nonzero value in `reload_reg_rtx' + for certain reloads. Then that tells you which register to use, + so you do not need to allocate one. But you still do need to add extra + instructions to copy the value into and out of that register. + + Finally you must call `subst_reloads' to substitute the reload reg rtx's + into the locations already recorded. + +NOTE SIDE EFFECTS: + + find_reloads can alter the operands of the instruction it is called on. + + 1. Two operands of any sort may be interchanged, if they are in a + commutative instruction. + This happens only if find_reloads thinks the instruction will compile + better that way. + + 2. Pseudo-registers that are equivalent to constants are replaced + with those constants if they are not in hard registers. + +1 happens every time find_reloads is called. +2 happens only when REPLACE is 1, which is only when +actually doing the reloads, not when just counting them. + + +Using a reload register for several reloads in one insn: + +When an insn has reloads, it is considered as having three parts: +the input reloads, the insn itself after reloading, and the output reloads. +Reloads of values used in memory addresses are often needed for only one part. + +When this is so, reload_when_needed records which part needs the reload. +Two reloads for different parts of the insn can share the same reload +register. + +When a reload is used for addresses in multiple parts, or when it is +an ordinary operand, it is classified as RELOAD_OTHER, and cannot share +a register with any other reload. */ + +#define REG_OK_STRICT + +#include "config.h" +#include "rtl.h" +#include "insn-config.h" +#include "insn-codes.h" +#include "recog.h" +#include "reload.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "flags.h" +#include "real.h" + +#ifndef REGISTER_MOVE_COST +#define REGISTER_MOVE_COST(x, y) 2 +#endif + +/* The variables set up by `find_reloads' are: + + n_reloads number of distinct reloads needed; max reload # + 1 + tables indexed by reload number + reload_in rtx for value to reload from + reload_out rtx for where to store reload-reg afterward if nec + (often the same as reload_in) + reload_reg_class enum reg_class, saying what regs to reload into + reload_inmode enum machine_mode; mode this operand should have + when reloaded, on input. + reload_outmode enum machine_mode; mode this operand should have + when reloaded, on output. + reload_optional char, nonzero for an optional reload. + Optional reloads are ignored unless the + value is already sitting in a register. + reload_inc int, positive amount to increment or decrement by if + reload_in is a PRE_DEC, PRE_INC, POST_DEC, POST_INC. + Ignored otherwise (don't assume it is zero). + reload_in_reg rtx. A reg for which reload_in is the equivalent. + If reload_in is a symbol_ref which came from + reg_equiv_constant, then this is the pseudo + which has that symbol_ref as equivalent. + reload_reg_rtx rtx. This is the register to reload into. + If it is zero when `find_reloads' returns, + you must find a suitable register in the class + specified by reload_reg_class, and store here + an rtx for that register with mode from + reload_inmode or reload_outmode. + reload_nocombine char, nonzero if this reload shouldn't be + combined with another reload. + reload_opnum int, operand number being reloaded. This is + used to group related reloads and need not always + be equal to the actual operand number in the insn, + though it current will be; for in-out operands, it + is one of the two operand numbers. + reload_when_needed enum, classifies reload as needed either for + addressing an input reload, addressing an output, + for addressing a non-reloaded mem ref, + or for unspecified purposes (i.e., more than one + of the above). + reload_secondary_reload int, gives the reload number of a secondary + reload, when needed; otherwise -1 + reload_secondary_p int, 1 if this is a secondary register for one + or more reloads. + reload_secondary_icode enum insn_code, if a secondary reload is required, + gives the INSN_CODE that uses the secondary + reload as a scratch register, or CODE_FOR_nothing + if the secondary reload register is to be an + intermediate register. */ +int n_reloads; + +rtx reload_in[MAX_RELOADS]; +rtx reload_out[MAX_RELOADS]; +enum reg_class reload_reg_class[MAX_RELOADS]; +enum machine_mode reload_inmode[MAX_RELOADS]; +enum machine_mode reload_outmode[MAX_RELOADS]; +rtx reload_reg_rtx[MAX_RELOADS]; +char reload_optional[MAX_RELOADS]; +int reload_inc[MAX_RELOADS]; +rtx reload_in_reg[MAX_RELOADS]; +char reload_nocombine[MAX_RELOADS]; +int reload_opnum[MAX_RELOADS]; +enum reload_type reload_when_needed[MAX_RELOADS]; +int reload_secondary_reload[MAX_RELOADS]; +int reload_secondary_p[MAX_RELOADS]; +enum insn_code reload_secondary_icode[MAX_RELOADS]; + +/* All the "earlyclobber" operands of the current insn + are recorded here. */ +int n_earlyclobbers; +rtx reload_earlyclobbers[MAX_RECOG_OPERANDS]; + +int reload_n_operands; + +/* Replacing reloads. + + If `replace_reloads' is nonzero, then as each reload is recorded + an entry is made for it in the table `replacements'. + Then later `subst_reloads' can look through that table and + perform all the replacements needed. */ + +/* Nonzero means record the places to replace. */ +static int replace_reloads; + +/* Each replacement is recorded with a structure like this. */ +struct replacement +{ + rtx *where; /* Location to store in */ + rtx *subreg_loc; /* Location of SUBREG if WHERE is inside + a SUBREG; 0 otherwise. */ + int what; /* which reload this is for */ + enum machine_mode mode; /* mode it must have */ +}; + +static struct replacement replacements[MAX_RECOG_OPERANDS * ((MAX_REGS_PER_ADDRESS * 2) + 1)]; + +/* Number of replacements currently recorded. */ +static int n_replacements; + +/* Used to track what is modified by an operand. */ +struct decomposition +{ + int reg_flag; /* Nonzero if referencing a register. */ + int safe; /* Nonzero if this can't conflict with anything. */ + rtx base; /* Base adddress for MEM. */ + HOST_WIDE_INT start; /* Starting offset or register number. */ + HOST_WIDE_INT end; /* Endinf offset or register number. */ +}; + +/* MEM-rtx's created for pseudo-regs in stack slots not directly addressable; + (see reg_equiv_address). */ +static rtx memlocs[MAX_RECOG_OPERANDS * ((MAX_REGS_PER_ADDRESS * 2) + 1)]; +static int n_memlocs; + +#ifdef SECONDARY_MEMORY_NEEDED + +/* Save MEMs needed to copy from one class of registers to another. One MEM + is used per mode, but normally only one or two modes are ever used. + + We keep two versions, before and after register elimination. The one + after register elimination is record separately for each operand. This + is done in case the address is not valid to be sure that we separately + reload each. */ + +static rtx secondary_memlocs[NUM_MACHINE_MODES]; +static rtx secondary_memlocs_elim[NUM_MACHINE_MODES][MAX_RECOG_OPERANDS]; +#endif + +/* The instruction we are doing reloads for; + so we can test whether a register dies in it. */ +static rtx this_insn; + +/* Nonzero if this instruction is a user-specified asm with operands. */ +static int this_insn_is_asm; + +/* If hard_regs_live_known is nonzero, + we can tell which hard regs are currently live, + at least enough to succeed in choosing dummy reloads. */ +static int hard_regs_live_known; + +/* Indexed by hard reg number, + element is nonegative if hard reg has been spilled. + This vector is passed to `find_reloads' as an argument + and is not changed here. */ +static short *static_reload_reg_p; + +/* Set to 1 in subst_reg_equivs if it changes anything. */ +static int subst_reg_equivs_changed; + +/* On return from push_reload, holds the reload-number for the OUT + operand, which can be different for that from the input operand. */ +static int output_reloadnum; + +static enum reg_class find_secondary_reload PROTO((rtx, enum reg_class, + enum machine_mode, int, + enum insn_code *, + enum machine_mode *, + enum reg_class *, + enum insn_code *, + enum machine_mode *)); +static int push_reload PROTO((rtx, rtx, rtx *, rtx *, enum reg_class, + enum machine_mode, enum machine_mode, + int, int, int, enum reload_type)); +static void push_replacement PROTO((rtx *, int, enum machine_mode)); +static void combine_reloads PROTO((void)); +static rtx find_dummy_reload PROTO((rtx, rtx, rtx *, rtx *, + enum reg_class, int)); +static int hard_reg_set_here_p PROTO((int, int, rtx)); +static struct decomposition decompose PROTO((rtx)); +static int immune_p PROTO((rtx, rtx, struct decomposition)); +static int alternative_allows_memconst PROTO((char *, int)); +static rtx find_reloads_toplev PROTO((rtx, int, enum reload_type, int, int)); +static rtx make_memloc PROTO((rtx, int)); +static int find_reloads_address PROTO((enum machine_mode, rtx *, rtx, rtx *, + int, enum reload_type, int)); +static rtx subst_reg_equivs PROTO((rtx)); +static rtx subst_indexed_address PROTO((rtx)); +static int find_reloads_address_1 PROTO((rtx, int, rtx *, int, + enum reload_type,int)); +static void find_reloads_address_part PROTO((rtx, rtx *, enum reg_class, + enum machine_mode, int, + enum reload_type, int)); +static int find_inc_amount PROTO((rtx, rtx)); + +#ifdef HAVE_SECONDARY_RELOADS + +/* Determine if any secondary reloads are needed for loading (if IN_P is + non-zero) or storing (if IN_P is zero) X to or from a reload register of + register class RELOAD_CLASS in mode RELOAD_MODE. + + Return the register class of a secondary reload register, or NO_REGS if + none. *PMODE is set to the mode that the register is required in. + If the reload register is needed as a scratch register instead of an + intermediate register, *PICODE is set to the insn_code of the insn to be + used to load or store the primary reload register; otherwise *PICODE + is set to CODE_FOR_nothing. + + In some cases (such as storing MQ into an external memory location on + the RT), both an intermediate register and a scratch register. In that + case, *PICODE is set to CODE_FOR_nothing, the class for the intermediate + register is returned, and the *PTERTIARY_... variables are set to describe + the scratch register. */ + +static enum reg_class +find_secondary_reload (x, reload_class, reload_mode, in_p, picode, pmode, + ptertiary_class, ptertiary_icode, ptertiary_mode) + rtx x; + enum reg_class reload_class; + enum machine_mode reload_mode; + int in_p; + enum insn_code *picode; + enum machine_mode *pmode; + enum reg_class *ptertiary_class; + enum insn_code *ptertiary_icode; + enum machine_mode *ptertiary_mode; +{ + enum reg_class class = NO_REGS; + enum machine_mode mode = reload_mode; + enum insn_code icode = CODE_FOR_nothing; + enum reg_class t_class = NO_REGS; + enum machine_mode t_mode = VOIDmode; + enum insn_code t_icode = CODE_FOR_nothing; + + /* If X is a pseudo-register that has an equivalent MEM (actually, if it + is still a pseudo-register by now, it *must* have an equivalent MEM + but we don't want to assume that), use that equivalent when seeing if + a secondary reload is needed since whether or not a reload is needed + might be sensitive to the form of the MEM. */ + + if (GET_CODE (x) == REG && REGNO (x) >= FIRST_PSEUDO_REGISTER + && reg_equiv_mem[REGNO (x)] != 0) + x = reg_equiv_mem[REGNO (x)]; + +#ifdef SECONDARY_INPUT_RELOAD_CLASS + if (in_p) + class = SECONDARY_INPUT_RELOAD_CLASS (reload_class, reload_mode, x); +#endif + +#ifdef SECONDARY_OUTPUT_RELOAD_CLASS + if (! in_p) + class = SECONDARY_OUTPUT_RELOAD_CLASS (reload_class, reload_mode, x); +#endif + + /* If we don't need any secondary registers, go away; the rest of the + values won't be used. */ + if (class == NO_REGS) + return NO_REGS; + + /* Get a possible insn to use. If the predicate doesn't accept X, don't + use the insn. */ + + icode = (in_p ? reload_in_optab[(int) reload_mode] + : reload_out_optab[(int) reload_mode]); + + if (icode != CODE_FOR_nothing + && insn_operand_predicate[(int) icode][in_p] + && (! (insn_operand_predicate[(int) icode][in_p]) (x, reload_mode))) + icode = CODE_FOR_nothing; + + /* If we will be using an insn, see if it can directly handle the reload + register we will be using. If it can, the secondary reload is for a + scratch register. If it can't, we will use the secondary reload for + an intermediate register and require a tertiary reload for the scratch + register. */ + + if (icode != CODE_FOR_nothing) + { + /* If IN_P is non-zero, the reload register will be the output in + operand 0. If IN_P is zero, the reload register will be the input + in operand 1. Outputs should have an initial "=", which we must + skip. */ + + char insn_letter = insn_operand_constraint[(int) icode][!in_p][in_p]; + enum reg_class insn_class + = (insn_letter == 'r' ? GENERAL_REGS + : REG_CLASS_FROM_LETTER (insn_letter)); + + if (insn_class == NO_REGS + || (in_p && insn_operand_constraint[(int) icode][!in_p][0] != '=') + /* The scratch register's constraint must start with "=&". */ + || insn_operand_constraint[(int) icode][2][0] != '=' + || insn_operand_constraint[(int) icode][2][1] != '&') + abort (); + + if (reg_class_subset_p (reload_class, insn_class)) + mode = insn_operand_mode[(int) icode][2]; + else + { + char t_letter = insn_operand_constraint[(int) icode][2][2]; + class = insn_class; + t_mode = insn_operand_mode[(int) icode][2]; + t_class = (t_letter == 'r' ? GENERAL_REGS + : REG_CLASS_FROM_LETTER (t_letter)); + t_icode = icode; + icode = CODE_FOR_nothing; + } + } + + *pmode = mode; + *picode = icode; + *ptertiary_class = t_class; + *ptertiary_mode = t_mode; + *ptertiary_icode = t_icode; + + return class; +} +#endif /* HAVE_SECONDARY_RELOADS */ + +#ifdef SECONDARY_MEMORY_NEEDED + +/* Return a memory location that will be used to copy X in mode MODE. + If we haven't already made a location for this mode in this insn, + call find_reloads_address on the location being returned. */ + +rtx +get_secondary_mem (x, mode, opnum, type) + rtx x; + enum machine_mode mode; + int opnum; + enum reload_type type; +{ + rtx loc; + int mem_valid; + + /* If MODE is narrower than a word, widen it. This is required because + most machines that require these memory locations do not support + short load and stores from all registers (e.g., FP registers). We could + possibly conditionalize this, but we lose nothing by doing the wider + mode. */ + + if (GET_MODE_BITSIZE (mode) < BITS_PER_WORD) + mode = mode_for_size (BITS_PER_WORD, GET_MODE_CLASS (mode), 0); + + /* If we already have made a MEM for this operand in MODE, return it. */ + if (secondary_memlocs_elim[(int) mode][opnum] != 0) + return secondary_memlocs_elim[(int) mode][opnum]; + + /* If this is the first time we've tried to get a MEM for this mode, + allocate a new one. `something_changed' in reload will get set + by noticing that the frame size has changed. */ + + if (secondary_memlocs[(int) mode] == 0) + { +#ifdef SECONDARY_MEMORY_NEEDED_RTX + secondary_memlocs[(int) mode] = SECONDARY_MEMORY_NEEDED_RTX (mode); +#else + secondary_memlocs[(int) mode] + = assign_stack_local (mode, GET_MODE_SIZE (mode), 0); +#endif + } + + /* Get a version of the address doing any eliminations needed. If that + didn't give us a new MEM, make a new one if it isn't valid. */ + + loc = eliminate_regs (secondary_memlocs[(int) mode], VOIDmode, NULL_RTX); + mem_valid = strict_memory_address_p (mode, XEXP (loc, 0)); + + if (! mem_valid && loc == secondary_memlocs[(int) mode]) + loc = copy_rtx (loc); + + /* The only time the call below will do anything is if the stack + offset is too large. In that case IND_LEVELS doesn't matter, so we + can just pass a zero. Adjust the type to be the address of the + corresponding object. If the address was valid, save the eliminated + address. If it wasn't valid, we need to make a reload each time, so + don't save it. */ + + if (! mem_valid) + { + type = (type == RELOAD_FOR_INPUT ? RELOAD_FOR_INPUT_ADDRESS + : type == RELOAD_FOR_OUTPUT ? RELOAD_FOR_OUTPUT_ADDRESS + : RELOAD_OTHER); + + find_reloads_address (mode, NULL_PTR, XEXP (loc, 0), &XEXP (loc, 0), + opnum, type, 0); + } + + secondary_memlocs_elim[(int) mode][opnum] = loc; + return loc; +} + +/* Clear any secondary memory locations we've made. */ + +void +clear_secondary_mem () +{ + bzero (secondary_memlocs, sizeof secondary_memlocs); +} +#endif /* SECONDARY_MEMORY_NEEDED */ + +/* Record one reload that needs to be performed. + IN is an rtx saying where the data are to be found before this instruction. + OUT says where they must be stored after the instruction. + (IN is zero for data not read, and OUT is zero for data not written.) + INLOC and OUTLOC point to the places in the instructions where + IN and OUT were found. + If IN and OUT are both non-zero, it means the same register must be used + to reload both IN and OUT. + + CLASS is a register class required for the reloaded data. + INMODE is the machine mode that the instruction requires + for the reg that replaces IN and OUTMODE is likewise for OUT. + + If IN is zero, then OUT's location and mode should be passed as + INLOC and INMODE. + + STRICT_LOW is the 1 if there is a containing STRICT_LOW_PART rtx. + + OPTIONAL nonzero means this reload does not need to be performed: + it can be discarded if that is more convenient. + + OPNUM and TYPE say what the purpose of this reload is. + + The return value is the reload-number for this reload. + + If both IN and OUT are nonzero, in some rare cases we might + want to make two separate reloads. (Actually we never do this now.) + Therefore, the reload-number for OUT is stored in + output_reloadnum when we return; the return value applies to IN. + Usually (presently always), when IN and OUT are nonzero, + the two reload-numbers are equal, but the caller should be careful to + distinguish them. */ + +static int +push_reload (in, out, inloc, outloc, class, + inmode, outmode, strict_low, optional, opnum, type) + register rtx in, out; + rtx *inloc, *outloc; + enum reg_class class; + enum machine_mode inmode, outmode; + int strict_low; + int optional; + int opnum; + enum reload_type type; +{ + register int i; + int dont_share = 0; + rtx *in_subreg_loc = 0, *out_subreg_loc = 0; + int secondary_reload = -1; + enum insn_code secondary_icode = CODE_FOR_nothing; + + /* Compare two RTX's. */ +#define MATCHES(x, y) \ + (x == y || (x != 0 && (GET_CODE (x) == REG \ + ? GET_CODE (y) == REG && REGNO (x) == REGNO (y) \ + : rtx_equal_p (x, y) && ! side_effects_p (x)))) + + /* Indicates if two reloads purposes are for similar enough things that we + can merge their reloads. */ +#define MERGABLE_RELOADS(when1, when2, op1, op2) \ + ((when1) == RELOAD_OTHER || (when2) == RELOAD_OTHER \ + || ((when1) == (when2) && (op1) == (op2)) \ + || ((when1) == RELOAD_FOR_INPUT && (when2) == RELOAD_FOR_INPUT) \ + || ((when1) == RELOAD_FOR_OPERAND_ADDRESS \ + && (when2) == RELOAD_FOR_OPERAND_ADDRESS) \ + || ((when1) == RELOAD_FOR_OTHER_ADDRESS \ + && (when2) == RELOAD_FOR_OTHER_ADDRESS)) + + /* Nonzero if these two reload purposes produce RELOAD_OTHER when merged. */ +#define MERGE_TO_OTHER(when1, when2, op1, op2) \ + ((when1) != (when2) \ + || ! ((op1) == (op2) \ + || (when1) == RELOAD_FOR_INPUT \ + || (when1) == RELOAD_FOR_OPERAND_ADDRESS \ + || (when1) == RELOAD_FOR_OTHER_ADDRESS)) + + /* INMODE and/or OUTMODE could be VOIDmode if no mode + has been specified for the operand. In that case, + use the operand's mode as the mode to reload. */ + if (inmode == VOIDmode && in != 0) + inmode = GET_MODE (in); + if (outmode == VOIDmode && out != 0) + outmode = GET_MODE (out); + + /* If IN is a pseudo register everywhere-equivalent to a constant, and + it is not in a hard register, reload straight from the constant, + since we want to get rid of such pseudo registers. + Often this is done earlier, but not always in find_reloads_address. */ + if (in != 0 && GET_CODE (in) == REG) + { + register int regno = REGNO (in); + + if (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0 + && reg_equiv_constant[regno] != 0) + in = reg_equiv_constant[regno]; + } + + /* Likewise for OUT. Of course, OUT will never be equivalent to + an actual constant, but it might be equivalent to a memory location + (in the case of a parameter). */ + if (out != 0 && GET_CODE (out) == REG) + { + register int regno = REGNO (out); + + if (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0 + && reg_equiv_constant[regno] != 0) + out = reg_equiv_constant[regno]; + } + + /* If we have a read-write operand with an address side-effect, + change either IN or OUT so the side-effect happens only once. */ + if (in != 0 && out != 0 && GET_CODE (in) == MEM && rtx_equal_p (in, out)) + { + if (GET_CODE (XEXP (in, 0)) == POST_INC + || GET_CODE (XEXP (in, 0)) == POST_DEC) + in = gen_rtx (MEM, GET_MODE (in), XEXP (XEXP (in, 0), 0)); + if (GET_CODE (XEXP (in, 0)) == PRE_INC + || GET_CODE (XEXP (in, 0)) == PRE_DEC) + out = gen_rtx (MEM, GET_MODE (out), XEXP (XEXP (out, 0), 0)); + } + + /* If we are reloading a (SUBREG (MEM ...) ...) or (SUBREG constant ...), + really reload just the inside expression in its own mode. + If we have (SUBREG:M1 (REG:M2 ...) ...) with M1 wider than M2 and the + register is a pseudo, this will become the same as the above case. + Similar issue for (SUBREG:M1 (REG:M2 ...) ...) for a hard register R where + either M1 is not valid for R or M2 is wider than a word but we only + need one word to store an M2-sized quantity in R. + (However, if OUT is nonzero, we need to reload the reg *and* + the subreg, so do nothing here, and let following statement handle it.) + + Note that the case of (SUBREG (CONST_INT...)...) is handled elsewhere; + we can't handle it here because CONST_INT does not indicate a mode. + + Similarly, we must reload the inside expression if we have a + STRICT_LOW_PART (presumably, in == out in the cas). + + Also reload the inner expression if it does not require a secondary + reload but the SUBREG does. */ + + if (in != 0 && GET_CODE (in) == SUBREG + && (GET_CODE (SUBREG_REG (in)) != REG + || strict_low + || (GET_CODE (SUBREG_REG (in)) == REG + && REGNO (SUBREG_REG (in)) >= FIRST_PSEUDO_REGISTER + && (GET_MODE_SIZE (inmode) + > GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))))) + || (REGNO (SUBREG_REG (in)) < FIRST_PSEUDO_REGISTER + /* The case where out is nonzero + is handled differently in the following statement. */ + && (out == 0 || SUBREG_WORD (in) == 0) + && (! HARD_REGNO_MODE_OK (REGNO (SUBREG_REG (in)), inmode) + || (GET_MODE_SIZE (inmode) <= UNITS_PER_WORD + && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))) + > UNITS_PER_WORD) + && ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))) + / UNITS_PER_WORD) + != HARD_REGNO_NREGS (REGNO (SUBREG_REG (in)), + GET_MODE (SUBREG_REG (in))))))) +#ifdef SECONDARY_INPUT_RELOAD_CLASS + || (SECONDARY_INPUT_RELOAD_CLASS (class, inmode, in) != NO_REGS + && (SECONDARY_INPUT_RELOAD_CLASS (class, + GET_MODE (SUBREG_REG (in)), + SUBREG_REG (in)) + == NO_REGS)) +#endif + )) + { + in_subreg_loc = inloc; + inloc = &SUBREG_REG (in); + in = *inloc; +#if ! defined(BYTE_LOADS_ZERO_EXTEND) && ! defined(BYTE_LOADS_SIGN_EXTEND) + if (GET_CODE (in) == MEM) + /* This is supposed to happen only for paradoxical subregs made by + combine.c. (SUBREG (MEM)) isn't supposed to occur other ways. */ + if (GET_MODE_SIZE (GET_MODE (in)) > GET_MODE_SIZE (inmode)) + abort (); +#endif + inmode = GET_MODE (in); + } + + /* Similar issue for (SUBREG:M1 (REG:M2 ...) ...) for a hard register R where + either M1 is not valid for R or M2 is wider than a word but we only + need one word to store an M2-sized quantity in R. + + However, we must reload the inner reg *as well as* the subreg in + that case. */ + + if (in != 0 && GET_CODE (in) == SUBREG + && GET_CODE (SUBREG_REG (in)) == REG + && REGNO (SUBREG_REG (in)) < FIRST_PSEUDO_REGISTER + && (! HARD_REGNO_MODE_OK (REGNO (SUBREG_REG (in)), inmode) + || (GET_MODE_SIZE (inmode) <= UNITS_PER_WORD + && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))) + > UNITS_PER_WORD) + && ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))) + / UNITS_PER_WORD) + != HARD_REGNO_NREGS (REGNO (SUBREG_REG (in)), + GET_MODE (SUBREG_REG (in))))))) + { + push_reload (SUBREG_REG (in), NULL_RTX, &SUBREG_REG (in), NULL_PTR, + GENERAL_REGS, VOIDmode, VOIDmode, 0, 0, opnum, type); + } + + + /* Similarly for paradoxical and problematical SUBREGs on the output. + Note that there is no reason we need worry about the previous value + of SUBREG_REG (out); even if wider than out, + storing in a subreg is entitled to clobber it all + (except in the case of STRICT_LOW_PART, + and in that case the constraint should label it input-output.) */ + if (out != 0 && GET_CODE (out) == SUBREG + && (GET_CODE (SUBREG_REG (out)) != REG + || strict_low + || (GET_CODE (SUBREG_REG (out)) == REG + && REGNO (SUBREG_REG (out)) >= FIRST_PSEUDO_REGISTER + && (GET_MODE_SIZE (outmode) + > GET_MODE_SIZE (GET_MODE (SUBREG_REG (out))))) + || (GET_CODE (SUBREG_REG (out)) == REG + && REGNO (SUBREG_REG (out)) < FIRST_PSEUDO_REGISTER + && (! HARD_REGNO_MODE_OK (REGNO (SUBREG_REG (out)), outmode) + || (GET_MODE_SIZE (outmode) <= UNITS_PER_WORD + && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (out))) + > UNITS_PER_WORD) + && ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (out))) + / UNITS_PER_WORD) + != HARD_REGNO_NREGS (REGNO (SUBREG_REG (out)), + GET_MODE (SUBREG_REG (out))))))) +#ifdef SECONDARY_OUTPUT_RELOAD_CLASS + || (SECONDARY_OUTPUT_RELOAD_CLASS (class, outmode, out) != NO_REGS + && (SECONDARY_OUTPUT_RELOAD_CLASS (class, + GET_MODE (SUBREG_REG (out)), + SUBREG_REG (out)) + == NO_REGS)) +#endif + )) + { + out_subreg_loc = outloc; + outloc = &SUBREG_REG (out); + out = *outloc; +#if ! defined(BYTE_LOADS_ZERO_EXTEND) && ! defined(BYTE_LOADS_SIGN_EXTEND) + if (GET_CODE (out) == MEM + && GET_MODE_SIZE (GET_MODE (out)) > GET_MODE_SIZE (outmode)) + abort (); +#endif + outmode = GET_MODE (out); + } + + /* If IN appears in OUT, we can't share any input-only reload for IN. */ + if (in != 0 && out != 0 && GET_CODE (out) == MEM + && (GET_CODE (in) == REG || GET_CODE (in) == MEM) + && reg_overlap_mentioned_for_reload_p (in, XEXP (out, 0))) + dont_share = 1; + + /* If IN is a SUBREG of a hard register, make a new REG. This + simplifies some of the cases below. */ + + if (in != 0 && GET_CODE (in) == SUBREG && GET_CODE (SUBREG_REG (in)) == REG + && REGNO (SUBREG_REG (in)) < FIRST_PSEUDO_REGISTER) + in = gen_rtx (REG, GET_MODE (in), + REGNO (SUBREG_REG (in)) + SUBREG_WORD (in)); + + /* Similarly for OUT. */ + if (out != 0 && GET_CODE (out) == SUBREG + && GET_CODE (SUBREG_REG (out)) == REG + && REGNO (SUBREG_REG (out)) < FIRST_PSEUDO_REGISTER) + out = gen_rtx (REG, GET_MODE (out), + REGNO (SUBREG_REG (out)) + SUBREG_WORD (out)); + + /* Narrow down the class of register wanted if that is + desirable on this machine for efficiency. */ + if (in != 0) + class = PREFERRED_RELOAD_CLASS (in, class); + + /* Output reloads may need analogous treatment, different in detail. */ +#ifdef PREFERRED_OUTPUT_RELOAD_CLASS + if (out != 0) + class = PREFERRED_OUTPUT_RELOAD_CLASS (out, class); +#endif + + /* Make sure we use a class that can handle the actual pseudo + inside any subreg. For example, on the 386, QImode regs + can appear within SImode subregs. Although GENERAL_REGS + can handle SImode, QImode needs a smaller class. */ +#ifdef LIMIT_RELOAD_CLASS + if (in_subreg_loc) + class = LIMIT_RELOAD_CLASS (inmode, class); + else if (in != 0 && GET_CODE (in) == SUBREG) + class = LIMIT_RELOAD_CLASS (GET_MODE (SUBREG_REG (in)), class); + + if (out_subreg_loc) + class = LIMIT_RELOAD_CLASS (outmode, class); + if (out != 0 && GET_CODE (out) == SUBREG) + class = LIMIT_RELOAD_CLASS (GET_MODE (SUBREG_REG (out)), class); +#endif + + /* Verify that this class is at least possible for the mode that + is specified. */ + if (this_insn_is_asm) + { + enum machine_mode mode; + if (GET_MODE_SIZE (inmode) > GET_MODE_SIZE (outmode)) + mode = inmode; + else + mode = outmode; + if (mode == VOIDmode) + { + error_for_asm (this_insn, "cannot reload integer constant operand in `asm'"); + mode = word_mode; + if (in != 0) + inmode = word_mode; + if (out != 0) + outmode = word_mode; + } + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (HARD_REGNO_MODE_OK (i, mode) + && TEST_HARD_REG_BIT (reg_class_contents[(int) class], i)) + { + int nregs = HARD_REGNO_NREGS (i, mode); + + int j; + for (j = 1; j < nregs; j++) + if (! TEST_HARD_REG_BIT (reg_class_contents[(int) class], i + j)) + break; + if (j == nregs) + break; + } + if (i == FIRST_PSEUDO_REGISTER) + { + error_for_asm (this_insn, "impossible register constraint in `asm'"); + class = ALL_REGS; + } + } + + if (class == NO_REGS) + abort (); + + /* We can use an existing reload if the class is right + and at least one of IN and OUT is a match + and the other is at worst neutral. + (A zero compared against anything is neutral.) + + If SMALL_REGISTER_CLASSES, don't use existing reloads unless they are + for the same thing since that can cause us to need more reload registers + than we otherwise would. */ + + for (i = 0; i < n_reloads; i++) + if ((reg_class_subset_p (class, reload_reg_class[i]) + || reg_class_subset_p (reload_reg_class[i], class)) + /* If the existing reload has a register, it must fit our class. */ + && (reload_reg_rtx[i] == 0 + || TEST_HARD_REG_BIT (reg_class_contents[(int) class], + true_regnum (reload_reg_rtx[i]))) + && ((in != 0 && MATCHES (reload_in[i], in) && ! dont_share + && (out == 0 || reload_out[i] == 0 || MATCHES (reload_out[i], out))) + || + (out != 0 && MATCHES (reload_out[i], out) + && (in == 0 || reload_in[i] == 0 || MATCHES (reload_in[i], in)))) + && (reg_class_size[(int) class] == 1 +#ifdef SMALL_REGISTER_CLASSES + || 1 +#endif + ) + && MERGABLE_RELOADS (type, reload_when_needed[i], + opnum, reload_opnum[i])) + break; + + /* Reloading a plain reg for input can match a reload to postincrement + that reg, since the postincrement's value is the right value. + Likewise, it can match a preincrement reload, since we regard + the preincrementation as happening before any ref in this insn + to that register. */ + if (i == n_reloads) + for (i = 0; i < n_reloads; i++) + if ((reg_class_subset_p (class, reload_reg_class[i]) + || reg_class_subset_p (reload_reg_class[i], class)) + /* If the existing reload has a register, it must fit our class. */ + && (reload_reg_rtx[i] == 0 + || TEST_HARD_REG_BIT (reg_class_contents[(int) class], + true_regnum (reload_reg_rtx[i]))) + && out == 0 && reload_out[i] == 0 && reload_in[i] != 0 + && ((GET_CODE (in) == REG + && (GET_CODE (reload_in[i]) == POST_INC + || GET_CODE (reload_in[i]) == POST_DEC + || GET_CODE (reload_in[i]) == PRE_INC + || GET_CODE (reload_in[i]) == PRE_DEC) + && MATCHES (XEXP (reload_in[i], 0), in)) + || + (GET_CODE (reload_in[i]) == REG + && (GET_CODE (in) == POST_INC + || GET_CODE (in) == POST_DEC + || GET_CODE (in) == PRE_INC + || GET_CODE (in) == PRE_DEC) + && MATCHES (XEXP (in, 0), reload_in[i]))) + && (reg_class_size[(int) class] == 1 +#ifdef SMALL_REGISTER_CLASSES + || 1 +#endif + ) + && MERGABLE_RELOADS (type, reload_when_needed[i], + opnum, reload_opnum[i])) + { + /* Make sure reload_in ultimately has the increment, + not the plain register. */ + if (GET_CODE (in) == REG) + in = reload_in[i]; + break; + } + + if (i == n_reloads) + { +#ifdef HAVE_SECONDARY_RELOADS + enum reg_class secondary_class = NO_REGS; + enum reg_class secondary_out_class = NO_REGS; + enum machine_mode secondary_mode = inmode; + enum machine_mode secondary_out_mode = outmode; + enum insn_code secondary_icode; + enum insn_code secondary_out_icode = CODE_FOR_nothing; + enum reg_class tertiary_class = NO_REGS; + enum reg_class tertiary_out_class = NO_REGS; + enum machine_mode tertiary_mode; + enum machine_mode tertiary_out_mode; + enum insn_code tertiary_icode; + enum insn_code tertiary_out_icode = CODE_FOR_nothing; + int tertiary_reload = -1; + + /* See if we need a secondary reload register to move between + CLASS and IN or CLASS and OUT. Get the modes and icodes to + use for each of them if so. */ + +#ifdef SECONDARY_INPUT_RELOAD_CLASS + if (in != 0) + secondary_class + = find_secondary_reload (in, class, inmode, 1, &secondary_icode, + &secondary_mode, &tertiary_class, + &tertiary_icode, &tertiary_mode); +#endif + +#ifdef SECONDARY_OUTPUT_RELOAD_CLASS + if (out != 0 && GET_CODE (out) != SCRATCH) + secondary_out_class + = find_secondary_reload (out, class, outmode, 0, + &secondary_out_icode, &secondary_out_mode, + &tertiary_out_class, &tertiary_out_icode, + &tertiary_out_mode); +#endif + + /* We can only record one secondary and one tertiary reload. If both + IN and OUT need secondary reloads, we can only make an in-out + reload if neither need an insn and if the classes are compatible. + If they aren't, all we can do is abort since making two separate + reloads is invalid. */ + + if (secondary_class != NO_REGS && secondary_out_class != NO_REGS + && reg_class_subset_p (secondary_out_class, secondary_class)) + secondary_class = secondary_out_class; + + if (secondary_class != NO_REGS && secondary_out_class != NO_REGS + && (! reg_class_subset_p (secondary_class, secondary_out_class) + || secondary_icode != CODE_FOR_nothing + || secondary_out_icode != CODE_FOR_nothing)) + abort (); + + /* If we need a secondary reload for OUT but not IN, copy the + information. */ + if (secondary_class == NO_REGS && secondary_out_class != NO_REGS) + { + secondary_class = secondary_out_class; + secondary_icode = secondary_out_icode; + tertiary_class = tertiary_out_class; + tertiary_icode = tertiary_out_icode; + tertiary_mode = tertiary_out_mode; + } + + if (secondary_class != NO_REGS) + { + /* Secondary reloads don't conflict as badly as the primary object + being reload. Specifically, we can always treat them as + being for an input or output address and hence allowed to be + reused in the same manner such address components could be + reused. This is used as the reload_type for our secondary + reloads. */ + + enum reload_type secondary_type + = (type == RELOAD_FOR_INPUT ? RELOAD_FOR_INPUT_ADDRESS + : type == RELOAD_FOR_OUTPUT ? RELOAD_FOR_OUTPUT_ADDRESS + : type); + + /* If we need a tertiary reload, see if we have one we can reuse + or else make one. */ + + if (tertiary_class != NO_REGS) + { + for (tertiary_reload = 0; tertiary_reload < n_reloads; + tertiary_reload++) + if (reload_secondary_p[tertiary_reload] + && (reg_class_subset_p (tertiary_class, + reload_reg_class[tertiary_reload]) + || reg_class_subset_p (reload_reg_class[tertiary_reload], + tertiary_class)) + && ((reload_inmode[tertiary_reload] == tertiary_mode) + || reload_inmode[tertiary_reload] == VOIDmode) + && ((reload_outmode[tertiary_reload] == tertiary_mode) + || reload_outmode[tertiary_reload] == VOIDmode) + && (reload_secondary_icode[tertiary_reload] + == CODE_FOR_nothing) + && (reg_class_size[(int) tertiary_class] == 1 +#ifdef SMALL_REGISTER_CLASSES + || 1 +#endif + ) + && MERGABLE_RELOADS (secondary_type, + reload_when_needed[tertiary_reload], + opnum, reload_opnum[tertiary_reload])) + { + if (tertiary_mode != VOIDmode) + reload_inmode[tertiary_reload] = tertiary_mode; + if (tertiary_out_mode != VOIDmode) + reload_outmode[tertiary_reload] = tertiary_mode; + if (reg_class_subset_p (tertiary_class, + reload_reg_class[tertiary_reload])) + reload_reg_class[tertiary_reload] = tertiary_class; + if (MERGE_TO_OTHER (secondary_type, + reload_when_needed[tertiary_reload], + opnum, + reload_opnum[tertiary_reload])) + reload_when_needed[tertiary_reload] = RELOAD_OTHER; + reload_opnum[tertiary_reload] + = MIN (reload_opnum[tertiary_reload], opnum); + reload_optional[tertiary_reload] &= optional; + reload_secondary_p[tertiary_reload] = 1; + } + + if (tertiary_reload == n_reloads) + { + /* We need to make a new tertiary reload for this register + class. */ + reload_in[tertiary_reload] = reload_out[tertiary_reload] = 0; + reload_reg_class[tertiary_reload] = tertiary_class; + reload_inmode[tertiary_reload] = tertiary_mode; + reload_outmode[tertiary_reload] = tertiary_mode; + reload_reg_rtx[tertiary_reload] = 0; + reload_optional[tertiary_reload] = optional; + reload_inc[tertiary_reload] = 0; + /* Maybe we could combine these, but it seems too tricky. */ + reload_nocombine[tertiary_reload] = 1; + reload_in_reg[tertiary_reload] = 0; + reload_opnum[tertiary_reload] = opnum; + reload_when_needed[tertiary_reload] = secondary_type; + reload_secondary_reload[tertiary_reload] = -1; + reload_secondary_icode[tertiary_reload] = CODE_FOR_nothing; + reload_secondary_p[tertiary_reload] = 1; + + n_reloads++; + i = n_reloads; + } + } + + /* See if we can reuse an existing secondary reload. */ + for (secondary_reload = 0; secondary_reload < n_reloads; + secondary_reload++) + if (reload_secondary_p[secondary_reload] + && (reg_class_subset_p (secondary_class, + reload_reg_class[secondary_reload]) + || reg_class_subset_p (reload_reg_class[secondary_reload], + secondary_class)) + && ((reload_inmode[secondary_reload] == secondary_mode) + || reload_inmode[secondary_reload] == VOIDmode) + && ((reload_outmode[secondary_reload] == secondary_out_mode) + || reload_outmode[secondary_reload] == VOIDmode) + && reload_secondary_reload[secondary_reload] == tertiary_reload + && reload_secondary_icode[secondary_reload] == tertiary_icode + && (reg_class_size[(int) secondary_class] == 1 +#ifdef SMALL_REGISTER_CLASSES + || 1 +#endif + ) + && MERGABLE_RELOADS (secondary_type, + reload_when_needed[secondary_reload], + opnum, reload_opnum[secondary_reload])) + { + if (secondary_mode != VOIDmode) + reload_inmode[secondary_reload] = secondary_mode; + if (secondary_out_mode != VOIDmode) + reload_outmode[secondary_reload] = secondary_out_mode; + if (reg_class_subset_p (secondary_class, + reload_reg_class[secondary_reload])) + reload_reg_class[secondary_reload] = secondary_class; + if (MERGE_TO_OTHER (secondary_type, + reload_when_needed[secondary_reload], + opnum, reload_opnum[secondary_reload])) + reload_when_needed[secondary_reload] = RELOAD_OTHER; + reload_opnum[secondary_reload] + = MIN (reload_opnum[secondary_reload], opnum); + reload_optional[secondary_reload] &= optional; + reload_secondary_p[secondary_reload] = 1; + } + + if (secondary_reload == n_reloads) + { + /* We need to make a new secondary reload for this register + class. */ + reload_in[secondary_reload] = reload_out[secondary_reload] = 0; + reload_reg_class[secondary_reload] = secondary_class; + reload_inmode[secondary_reload] = secondary_mode; + reload_outmode[secondary_reload] = secondary_out_mode; + reload_reg_rtx[secondary_reload] = 0; + reload_optional[secondary_reload] = optional; + reload_inc[secondary_reload] = 0; + /* Maybe we could combine these, but it seems too tricky. */ + reload_nocombine[secondary_reload] = 1; + reload_in_reg[secondary_reload] = 0; + reload_opnum[secondary_reload] = opnum; + reload_when_needed[secondary_reload] = secondary_type; + reload_secondary_reload[secondary_reload] = tertiary_reload; + reload_secondary_icode[secondary_reload] = tertiary_icode; + reload_secondary_p[secondary_reload] = 1; + + n_reloads++; + i = n_reloads; + +#ifdef SECONDARY_MEMORY_NEEDED + /* If we need a memory location to copy between the two + reload regs, set it up now. */ + + if (in != 0 && secondary_icode == CODE_FOR_nothing + && SECONDARY_MEMORY_NEEDED (secondary_class, class, inmode)) + get_secondary_mem (in, inmode, opnum, type); + + if (out != 0 && secondary_icode == CODE_FOR_nothing + && SECONDARY_MEMORY_NEEDED (class, secondary_class, outmode)) + get_secondary_mem (out, outmode, opnum, type); +#endif + } + } +#endif + + /* We found no existing reload suitable for re-use. + So add an additional reload. */ + + reload_in[i] = in; + reload_out[i] = out; + reload_reg_class[i] = class; + reload_inmode[i] = inmode; + reload_outmode[i] = outmode; + reload_reg_rtx[i] = 0; + reload_optional[i] = optional; + reload_inc[i] = 0; + reload_nocombine[i] = 0; + reload_in_reg[i] = inloc ? *inloc : 0; + reload_opnum[i] = opnum; + reload_when_needed[i] = type; + reload_secondary_reload[i] = secondary_reload; + reload_secondary_icode[i] = secondary_icode; + reload_secondary_p[i] = 0; + + n_reloads++; + +#ifdef SECONDARY_MEMORY_NEEDED + /* If a memory location is needed for the copy, make one. */ + if (in != 0 && GET_CODE (in) == REG + && REGNO (in) < FIRST_PSEUDO_REGISTER + && SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (REGNO (in)), + class, inmode)) + get_secondary_mem (in, inmode, opnum, type); + + if (out != 0 && GET_CODE (out) == REG + && REGNO (out) < FIRST_PSEUDO_REGISTER + && SECONDARY_MEMORY_NEEDED (class, REGNO_REG_CLASS (REGNO (out)), + outmode)) + get_secondary_mem (out, outmode, opnum, type); +#endif + } + else + { + /* We are reusing an existing reload, + but we may have additional information for it. + For example, we may now have both IN and OUT + while the old one may have just one of them. */ + + if (inmode != VOIDmode) + reload_inmode[i] = inmode; + if (outmode != VOIDmode) + reload_outmode[i] = outmode; + if (in != 0) + reload_in[i] = in; + if (out != 0) + reload_out[i] = out; + if (reg_class_subset_p (class, reload_reg_class[i])) + reload_reg_class[i] = class; + reload_optional[i] &= optional; + if (MERGE_TO_OTHER (type, reload_when_needed[i], + opnum, reload_opnum[i])) + reload_when_needed[i] = RELOAD_OTHER; + reload_opnum[i] = MIN (reload_opnum[i], opnum); + } + + /* If the ostensible rtx being reload differs from the rtx found + in the location to substitute, this reload is not safe to combine + because we cannot reliably tell whether it appears in the insn. */ + + if (in != 0 && in != *inloc) + reload_nocombine[i] = 1; + +#if 0 + /* This was replaced by changes in find_reloads_address_1 and the new + function inc_for_reload, which go with a new meaning of reload_inc. */ + + /* If this is an IN/OUT reload in an insn that sets the CC, + it must be for an autoincrement. It doesn't work to store + the incremented value after the insn because that would clobber the CC. + So we must do the increment of the value reloaded from, + increment it, store it back, then decrement again. */ + if (out != 0 && sets_cc0_p (PATTERN (this_insn))) + { + out = 0; + reload_out[i] = 0; + reload_inc[i] = find_inc_amount (PATTERN (this_insn), in); + /* If we did not find a nonzero amount-to-increment-by, + that contradicts the belief that IN is being incremented + in an address in this insn. */ + if (reload_inc[i] == 0) + abort (); + } +#endif + + /* If we will replace IN and OUT with the reload-reg, + record where they are located so that substitution need + not do a tree walk. */ + + if (replace_reloads) + { + if (inloc != 0) + { + register struct replacement *r = &replacements[n_replacements++]; + r->what = i; + r->subreg_loc = in_subreg_loc; + r->where = inloc; + r->mode = inmode; + } + if (outloc != 0 && outloc != inloc) + { + register struct replacement *r = &replacements[n_replacements++]; + r->what = i; + r->where = outloc; + r->subreg_loc = out_subreg_loc; + r->mode = outmode; + } + } + + /* If this reload is just being introduced and it has both + an incoming quantity and an outgoing quantity that are + supposed to be made to match, see if either one of the two + can serve as the place to reload into. + + If one of them is acceptable, set reload_reg_rtx[i] + to that one. */ + + if (in != 0 && out != 0 && in != out && reload_reg_rtx[i] == 0) + { + reload_reg_rtx[i] = find_dummy_reload (in, out, inloc, outloc, + reload_reg_class[i], i); + + /* If the outgoing register already contains the same value + as the incoming one, we can dispense with loading it. + The easiest way to tell the caller that is to give a phony + value for the incoming operand (same as outgoing one). */ + if (reload_reg_rtx[i] == out + && (GET_CODE (in) == REG || CONSTANT_P (in)) + && 0 != find_equiv_reg (in, this_insn, 0, REGNO (out), + static_reload_reg_p, i, inmode)) + reload_in[i] = out; + } + + /* If this is an input reload and the operand contains a register that + dies in this insn and is used nowhere else, see if it is the right class + to be used for this reload. Use it if so. (This occurs most commonly + in the case of paradoxical SUBREGs and in-out reloads). We cannot do + this if it is also an output reload that mentions the register unless + the output is a SUBREG that clobbers an entire register. + + Note that the operand might be one of the spill regs, if it is a + pseudo reg and we are in a block where spilling has not taken place. + But if there is no spilling in this block, that is OK. + An explicitly used hard reg cannot be a spill reg. */ + + if (reload_reg_rtx[i] == 0 && in != 0) + { + rtx note; + int regno; + + for (note = REG_NOTES (this_insn); note; note = XEXP (note, 1)) + if (REG_NOTE_KIND (note) == REG_DEAD + && GET_CODE (XEXP (note, 0)) == REG + && (regno = REGNO (XEXP (note, 0))) < FIRST_PSEUDO_REGISTER + && reg_mentioned_p (XEXP (note, 0), in) + && ! refers_to_regno_for_reload_p (regno, + (regno + + HARD_REGNO_NREGS (regno, + inmode)), + PATTERN (this_insn), inloc) + /* If this is also an output reload, IN cannot be used as + the reload register if it is set in this insn unless IN + is also OUT. */ + && (out == 0 || in == out + || ! hard_reg_set_here_p (regno, + (regno + + HARD_REGNO_NREGS (regno, + inmode)), + PATTERN (this_insn))) + /* ??? Why is this code so different from the previous? + Is there any simple coherent way to describe the two together? + What's going on here. */ + && (in != out + || (GET_CODE (in) == SUBREG + && (((GET_MODE_SIZE (GET_MODE (in)) + (UNITS_PER_WORD - 1)) + / UNITS_PER_WORD) + == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))) + + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)))) + /* Make sure the operand fits in the reg that dies. */ + && GET_MODE_SIZE (inmode) <= GET_MODE_SIZE (GET_MODE (XEXP (note, 0))) + && HARD_REGNO_MODE_OK (regno, inmode) + && GET_MODE_SIZE (outmode) <= GET_MODE_SIZE (GET_MODE (XEXP (note, 0))) + && HARD_REGNO_MODE_OK (regno, outmode) + && TEST_HARD_REG_BIT (reg_class_contents[(int) class], regno) + && !fixed_regs[regno]) + { + reload_reg_rtx[i] = gen_rtx (REG, inmode, regno); + break; + } + } + + if (out) + output_reloadnum = i; + + return i; +} + +/* Record an additional place we must replace a value + for which we have already recorded a reload. + RELOADNUM is the value returned by push_reload + when the reload was recorded. + This is used in insn patterns that use match_dup. */ + +static void +push_replacement (loc, reloadnum, mode) + rtx *loc; + int reloadnum; + enum machine_mode mode; +{ + if (replace_reloads) + { + register struct replacement *r = &replacements[n_replacements++]; + r->what = reloadnum; + r->where = loc; + r->subreg_loc = 0; + r->mode = mode; + } +} + +/* Transfer all replacements that used to be in reload FROM to be in + reload TO. */ + +void +transfer_replacements (to, from) + int to, from; +{ + int i; + + for (i = 0; i < n_replacements; i++) + if (replacements[i].what == from) + replacements[i].what = to; +} + +/* If there is only one output reload, and it is not for an earlyclobber + operand, try to combine it with a (logically unrelated) input reload + to reduce the number of reload registers needed. + + This is safe if the input reload does not appear in + the value being output-reloaded, because this implies + it is not needed any more once the original insn completes. + + If that doesn't work, see we can use any of the registers that + die in this insn as a reload register. We can if it is of the right + class and does not appear in the value being output-reloaded. */ + +static void +combine_reloads () +{ + int i; + int output_reload = -1; + rtx note; + + /* Find the output reload; return unless there is exactly one + and that one is mandatory. */ + + for (i = 0; i < n_reloads; i++) + if (reload_out[i] != 0) + { + if (output_reload >= 0) + return; + output_reload = i; + } + + if (output_reload < 0 || reload_optional[output_reload]) + return; + + /* An input-output reload isn't combinable. */ + + if (reload_in[output_reload] != 0) + return; + + /* If this reload is for an earlyclobber operand, we can't do anything. */ + + for (i = 0; i < n_earlyclobbers; i++) + if (reload_out[output_reload] == reload_earlyclobbers[i]) + return; + + /* Check each input reload; can we combine it? */ + + for (i = 0; i < n_reloads; i++) + if (reload_in[i] && ! reload_optional[i] && ! reload_nocombine[i] + /* Life span of this reload must not extend past main insn. */ + && reload_when_needed[i] != RELOAD_FOR_OUTPUT_ADDRESS + && reload_when_needed[i] != RELOAD_OTHER + && (CLASS_MAX_NREGS (reload_reg_class[i], reload_inmode[i]) + == CLASS_MAX_NREGS (reload_reg_class[output_reload], + reload_outmode[output_reload])) + && reload_inc[i] == 0 + && reload_reg_rtx[i] == 0 + /* Don't combine two reloads with different secondary reloads. */ + && (reload_secondary_reload[i] == reload_secondary_reload[output_reload] + || reload_secondary_reload[i] == -1 + || reload_secondary_reload[output_reload] == -1) +#ifdef SECONDARY_MEMORY_NEEDED + /* Likewise for different secondary memory locations. */ + && (secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[i]] == 0 + || secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[output_reload]] == 0 + || rtx_equal_p (secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[i]], + secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[output_reload]])) +#endif +#ifdef SMALL_REGISTER_CLASSES + && reload_reg_class[i] == reload_reg_class[output_reload] +#else + && (reg_class_subset_p (reload_reg_class[i], + reload_reg_class[output_reload]) + || reg_class_subset_p (reload_reg_class[output_reload], + reload_reg_class[i])) +#endif + && (MATCHES (reload_in[i], reload_out[output_reload]) + /* Args reversed because the first arg seems to be + the one that we imagine being modified + while the second is the one that might be affected. */ + || (! reg_overlap_mentioned_for_reload_p (reload_out[output_reload], + reload_in[i]) + /* However, if the input is a register that appears inside + the output, then we also can't share. + Imagine (set (mem (reg 69)) (plus (reg 69) ...)). + If the same reload reg is used for both reg 69 and the + result to be stored in memory, then that result + will clobber the address of the memory ref. */ + && ! (GET_CODE (reload_in[i]) == REG + && reg_overlap_mentioned_for_reload_p (reload_in[i], + reload_out[output_reload])))) + && (reg_class_size[(int) reload_reg_class[i]] +#ifdef SMALL_REGISTER_CLASSES + || 1 +#endif + ) + /* We will allow making things slightly worse by combining an + input and an output, but no worse than that. */ + && (reload_when_needed[i] == RELOAD_FOR_INPUT + || reload_when_needed[i] == RELOAD_FOR_OUTPUT)) + { + int j; + + /* We have found a reload to combine with! */ + reload_out[i] = reload_out[output_reload]; + reload_outmode[i] = reload_outmode[output_reload]; + /* Mark the old output reload as inoperative. */ + reload_out[output_reload] = 0; + /* The combined reload is needed for the entire insn. */ + reload_when_needed[i] = RELOAD_OTHER; + /* If the output reload had a secondary reload, copy it. */ + if (reload_secondary_reload[output_reload] != -1) + reload_secondary_reload[i] = reload_secondary_reload[output_reload]; +#ifdef SECONDARY_MEMORY_NEEDED + /* Copy any secondary MEM. */ + if (secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[output_reload]] != 0) + secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[i]] + = secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[output_reload]]; +#endif + /* If required, minimize the register class. */ + if (reg_class_subset_p (reload_reg_class[output_reload], + reload_reg_class[i])) + reload_reg_class[i] = reload_reg_class[output_reload]; + + /* Transfer all replacements from the old reload to the combined. */ + for (j = 0; j < n_replacements; j++) + if (replacements[j].what == output_reload) + replacements[j].what = i; + + return; + } + + /* If this insn has only one operand that is modified or written (assumed + to be the first), it must be the one corresponding to this reload. It + is safe to use anything that dies in this insn for that output provided + that it does not occur in the output (we already know it isn't an + earlyclobber. If this is an asm insn, give up. */ + + if (INSN_CODE (this_insn) == -1) + return; + + for (i = 1; i < insn_n_operands[INSN_CODE (this_insn)]; i++) + if (insn_operand_constraint[INSN_CODE (this_insn)][i][0] == '=' + || insn_operand_constraint[INSN_CODE (this_insn)][i][0] == '+') + return; + + /* See if some hard register that dies in this insn and is not used in + the output is the right class. Only works if the register we pick + up can fully hold our output reload. */ + for (note = REG_NOTES (this_insn); note; note = XEXP (note, 1)) + if (REG_NOTE_KIND (note) == REG_DEAD + && GET_CODE (XEXP (note, 0)) == REG + && ! reg_overlap_mentioned_for_reload_p (XEXP (note, 0), + reload_out[output_reload]) + && REGNO (XEXP (note, 0)) < FIRST_PSEUDO_REGISTER + && HARD_REGNO_MODE_OK (REGNO (XEXP (note, 0)), reload_outmode[output_reload]) + && TEST_HARD_REG_BIT (reg_class_contents[(int) reload_reg_class[output_reload]], + REGNO (XEXP (note, 0))) + && (HARD_REGNO_NREGS (REGNO (XEXP (note, 0)), reload_outmode[output_reload]) + <= HARD_REGNO_NREGS (REGNO (XEXP (note, 0)), GET_MODE (XEXP (note, 0)))) + && ! fixed_regs[REGNO (XEXP (note, 0))]) + { + reload_reg_rtx[output_reload] = gen_rtx (REG, + reload_outmode[output_reload], + REGNO (XEXP (note, 0))); + return; + } +} + +/* Try to find a reload register for an in-out reload (expressions IN and OUT). + See if one of IN and OUT is a register that may be used; + this is desirable since a spill-register won't be needed. + If so, return the register rtx that proves acceptable. + + INLOC and OUTLOC are locations where IN and OUT appear in the insn. + CLASS is the register class required for the reload. + + If FOR_REAL is >= 0, it is the number of the reload, + and in some cases when it can be discovered that OUT doesn't need + to be computed, clear out reload_out[FOR_REAL]. + + If FOR_REAL is -1, this should not be done, because this call + is just to see if a register can be found, not to find and install it. */ + +static rtx +find_dummy_reload (real_in, real_out, inloc, outloc, class, for_real) + rtx real_in, real_out; + rtx *inloc, *outloc; + enum reg_class class; + int for_real; +{ + rtx in = real_in; + rtx out = real_out; + int in_offset = 0; + int out_offset = 0; + rtx value = 0; + + /* If operands exceed a word, we can't use either of them + unless they have the same size. */ + if (GET_MODE_SIZE (GET_MODE (real_out)) != GET_MODE_SIZE (GET_MODE (real_in)) + && (GET_MODE_SIZE (GET_MODE (real_out)) > UNITS_PER_WORD + || GET_MODE_SIZE (GET_MODE (real_in)) > UNITS_PER_WORD)) + return 0; + + /* Find the inside of any subregs. */ + while (GET_CODE (out) == SUBREG) + { + out_offset = SUBREG_WORD (out); + out = SUBREG_REG (out); + } + while (GET_CODE (in) == SUBREG) + { + in_offset = SUBREG_WORD (in); + in = SUBREG_REG (in); + } + + /* Narrow down the reg class, the same way push_reload will; + otherwise we might find a dummy now, but push_reload won't. */ + class = PREFERRED_RELOAD_CLASS (in, class); + + /* See if OUT will do. */ + if (GET_CODE (out) == REG + && REGNO (out) < FIRST_PSEUDO_REGISTER) + { + register int regno = REGNO (out) + out_offset; + int nwords = HARD_REGNO_NREGS (regno, GET_MODE (real_out)); + rtx saved_rtx; + + /* When we consider whether the insn uses OUT, + ignore references within IN. They don't prevent us + from copying IN into OUT, because those refs would + move into the insn that reloads IN. + + However, we only ignore IN in its role as this reload. + If the insn uses IN elsewhere and it contains OUT, + that counts. We can't be sure it's the "same" operand + so it might not go through this reload. */ + saved_rtx = *inloc; + *inloc = const0_rtx; + + if (regno < FIRST_PSEUDO_REGISTER + /* A fixed reg that can overlap other regs better not be used + for reloading in any way. */ +#ifdef OVERLAPPING_REGNO_P + && ! (fixed_regs[regno] && OVERLAPPING_REGNO_P (regno)) +#endif + && ! refers_to_regno_for_reload_p (regno, regno + nwords, + PATTERN (this_insn), outloc)) + { + int i; + for (i = 0; i < nwords; i++) + if (! TEST_HARD_REG_BIT (reg_class_contents[(int) class], + regno + i)) + break; + + if (i == nwords) + { + if (GET_CODE (real_out) == REG) + value = real_out; + else + value = gen_rtx (REG, GET_MODE (real_out), regno); + } + } + + *inloc = saved_rtx; + } + + /* Consider using IN if OUT was not acceptable + or if OUT dies in this insn (like the quotient in a divmod insn). + We can't use IN unless it is dies in this insn, + which means we must know accurately which hard regs are live. + Also, the result can't go in IN if IN is used within OUT. */ + if (hard_regs_live_known + && GET_CODE (in) == REG + && REGNO (in) < FIRST_PSEUDO_REGISTER + && (value == 0 + || find_reg_note (this_insn, REG_UNUSED, real_out)) + && find_reg_note (this_insn, REG_DEAD, real_in) + && !fixed_regs[REGNO (in)] + && HARD_REGNO_MODE_OK (REGNO (in), GET_MODE (out))) + { + register int regno = REGNO (in) + in_offset; + int nwords = HARD_REGNO_NREGS (regno, GET_MODE (real_in)); + + if (! refers_to_regno_for_reload_p (regno, regno + nwords, out, NULL_PTR) + && ! hard_reg_set_here_p (regno, regno + nwords, + PATTERN (this_insn))) + { + int i; + for (i = 0; i < nwords; i++) + if (! TEST_HARD_REG_BIT (reg_class_contents[(int) class], + regno + i)) + break; + + if (i == nwords) + { + /* If we were going to use OUT as the reload reg + and changed our mind, it means OUT is a dummy that + dies here. So don't bother copying value to it. */ + if (for_real >= 0 && value == real_out) + reload_out[for_real] = 0; + if (GET_CODE (real_in) == REG) + value = real_in; + else + value = gen_rtx (REG, GET_MODE (real_in), regno); + } + } + } + + return value; +} + +/* This page contains subroutines used mainly for determining + whether the IN or an OUT of a reload can serve as the + reload register. */ + +/* Return 1 if expression X alters a hard reg in the range + from BEG_REGNO (inclusive) to END_REGNO (exclusive), + either explicitly or in the guise of a pseudo-reg allocated to REGNO. + X should be the body of an instruction. */ + +static int +hard_reg_set_here_p (beg_regno, end_regno, x) + register int beg_regno, end_regno; + rtx x; +{ + if (GET_CODE (x) == SET || GET_CODE (x) == CLOBBER) + { + register rtx op0 = SET_DEST (x); + while (GET_CODE (op0) == SUBREG) + op0 = SUBREG_REG (op0); + if (GET_CODE (op0) == REG) + { + register int r = REGNO (op0); + /* See if this reg overlaps range under consideration. */ + if (r < end_regno + && r + HARD_REGNO_NREGS (r, GET_MODE (op0)) > beg_regno) + return 1; + } + } + else if (GET_CODE (x) == PARALLEL) + { + register int i = XVECLEN (x, 0) - 1; + for (; i >= 0; i--) + if (hard_reg_set_here_p (beg_regno, end_regno, XVECEXP (x, 0, i))) + return 1; + } + + return 0; +} + +/* Return 1 if ADDR is a valid memory address for mode MODE, + and check that each pseudo reg has the proper kind of + hard reg. */ + +int +strict_memory_address_p (mode, addr) + enum machine_mode mode; + register rtx addr; +{ + GO_IF_LEGITIMATE_ADDRESS (mode, addr, win); + return 0; + + win: + return 1; +} + +/* Like rtx_equal_p except that it allows a REG and a SUBREG to match + if they are the same hard reg, and has special hacks for + autoincrement and autodecrement. + This is specifically intended for find_reloads to use + in determining whether two operands match. + X is the operand whose number is the lower of the two. + + The value is 2 if Y contains a pre-increment that matches + a non-incrementing address in X. */ + +/* ??? To be completely correct, we should arrange to pass + for X the output operand and for Y the input operand. + For now, we assume that the output operand has the lower number + because that is natural in (SET output (... input ...)). */ + +int +operands_match_p (x, y) + register rtx x, y; +{ + register int i; + register RTX_CODE code = GET_CODE (x); + register char *fmt; + int success_2; + + if (x == y) + return 1; + if ((code == REG || (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG)) + && (GET_CODE (y) == REG || (GET_CODE (y) == SUBREG + && GET_CODE (SUBREG_REG (y)) == REG))) + { + register int j; + + if (code == SUBREG) + { + i = REGNO (SUBREG_REG (x)); + if (i >= FIRST_PSEUDO_REGISTER) + goto slow; + i += SUBREG_WORD (x); + } + else + i = REGNO (x); + + if (GET_CODE (y) == SUBREG) + { + j = REGNO (SUBREG_REG (y)); + if (j >= FIRST_PSEUDO_REGISTER) + goto slow; + j += SUBREG_WORD (y); + } + else + j = REGNO (y); + + /* On a WORDS_BIG_ENDIAN machine, point to the last register of a + multiple hard register group, so that for example (reg:DI 0) and + (reg:SI 1) will be considered the same register. */ + if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD + && i < FIRST_PSEUDO_REGISTER) + i += (GET_MODE_SIZE (GET_MODE (x)) / UNITS_PER_WORD) - 1; + if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (GET_MODE (y)) > UNITS_PER_WORD + && j < FIRST_PSEUDO_REGISTER) + j += (GET_MODE_SIZE (GET_MODE (y)) / UNITS_PER_WORD) - 1; + + return i == j; + } + /* If two operands must match, because they are really a single + operand of an assembler insn, then two postincrements are invalid + because the assembler insn would increment only once. + On the other hand, an postincrement matches ordinary indexing + if the postincrement is the output operand. */ + if (code == POST_DEC || code == POST_INC) + return operands_match_p (XEXP (x, 0), y); + /* Two preincrements are invalid + because the assembler insn would increment only once. + On the other hand, an preincrement matches ordinary indexing + if the preincrement is the input operand. + In this case, return 2, since some callers need to do special + things when this happens. */ + if (GET_CODE (y) == PRE_DEC || GET_CODE (y) == PRE_INC) + return operands_match_p (x, XEXP (y, 0)) ? 2 : 0; + + slow: + + /* Now we have disposed of all the cases + in which different rtx codes can match. */ + if (code != GET_CODE (y)) + return 0; + if (code == LABEL_REF) + return XEXP (x, 0) == XEXP (y, 0); + if (code == SYMBOL_REF) + return XSTR (x, 0) == XSTR (y, 0); + + /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */ + + if (GET_MODE (x) != GET_MODE (y)) + return 0; + + /* Compare the elements. If any pair of corresponding elements + fail to match, return 0 for the whole things. */ + + success_2 = 0; + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + int val; + switch (fmt[i]) + { + case 'w': + if (XWINT (x, i) != XWINT (y, i)) + return 0; + break; + + case 'i': + if (XINT (x, i) != XINT (y, i)) + return 0; + break; + + case 'e': + val = operands_match_p (XEXP (x, i), XEXP (y, i)); + if (val == 0) + return 0; + /* If any subexpression returns 2, + we should return 2 if we are successful. */ + if (val == 2) + success_2 = 1; + break; + + case '0': + break; + + /* It is believed that rtx's at this level will never + contain anything but integers and other rtx's, + except for within LABEL_REFs and SYMBOL_REFs. */ + default: + abort (); + } + } + return 1 + success_2; +} + +/* Return the number of times character C occurs in string S. */ + +int +n_occurrences (c, s) + char c; + char *s; +{ + int n = 0; + while (*s) + n += (*s++ == c); + return n; +} + +/* Describe the range of registers or memory referenced by X. + If X is a register, set REG_FLAG and put the first register + number into START and the last plus one into END. + If X is a memory reference, put a base address into BASE + and a range of integer offsets into START and END. + If X is pushing on the stack, we can assume it causes no trouble, + so we set the SAFE field. */ + +static struct decomposition +decompose (x) + rtx x; +{ + struct decomposition val; + int all_const = 0; + + val.reg_flag = 0; + val.safe = 0; + if (GET_CODE (x) == MEM) + { + rtx base, offset = 0; + rtx addr = XEXP (x, 0); + + if (GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC + || GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC) + { + val.base = XEXP (addr, 0); + val.start = - GET_MODE_SIZE (GET_MODE (x)); + val.end = GET_MODE_SIZE (GET_MODE (x)); + val.safe = REGNO (val.base) == STACK_POINTER_REGNUM; + return val; + } + + if (GET_CODE (addr) == CONST) + { + addr = XEXP (addr, 0); + all_const = 1; + } + if (GET_CODE (addr) == PLUS) + { + if (CONSTANT_P (XEXP (addr, 0))) + { + base = XEXP (addr, 1); + offset = XEXP (addr, 0); + } + else if (CONSTANT_P (XEXP (addr, 1))) + { + base = XEXP (addr, 0); + offset = XEXP (addr, 1); + } + } + + if (offset == 0) + { + base = addr; + offset = const0_rtx; + } + if (GET_CODE (offset) == CONST) + offset = XEXP (offset, 0); + if (GET_CODE (offset) == PLUS) + { + if (GET_CODE (XEXP (offset, 0)) == CONST_INT) + { + base = gen_rtx (PLUS, GET_MODE (base), base, XEXP (offset, 1)); + offset = XEXP (offset, 0); + } + else if (GET_CODE (XEXP (offset, 1)) == CONST_INT) + { + base = gen_rtx (PLUS, GET_MODE (base), base, XEXP (offset, 0)); + offset = XEXP (offset, 1); + } + else + { + base = gen_rtx (PLUS, GET_MODE (base), base, offset); + offset = const0_rtx; + } + } + else if (GET_CODE (offset) != CONST_INT) + { + base = gen_rtx (PLUS, GET_MODE (base), base, offset); + offset = const0_rtx; + } + + if (all_const && GET_CODE (base) == PLUS) + base = gen_rtx (CONST, GET_MODE (base), base); + + if (GET_CODE (offset) != CONST_INT) + abort (); + + val.start = INTVAL (offset); + val.end = val.start + GET_MODE_SIZE (GET_MODE (x)); + val.base = base; + return val; + } + else if (GET_CODE (x) == REG) + { + val.reg_flag = 1; + val.start = true_regnum (x); + if (val.start < 0) + { + /* A pseudo with no hard reg. */ + val.start = REGNO (x); + val.end = val.start + 1; + } + else + /* A hard reg. */ + val.end = val.start + HARD_REGNO_NREGS (val.start, GET_MODE (x)); + } + else if (GET_CODE (x) == SUBREG) + { + if (GET_CODE (SUBREG_REG (x)) != REG) + /* This could be more precise, but it's good enough. */ + return decompose (SUBREG_REG (x)); + val.reg_flag = 1; + val.start = true_regnum (x); + if (val.start < 0) + return decompose (SUBREG_REG (x)); + else + /* A hard reg. */ + val.end = val.start + HARD_REGNO_NREGS (val.start, GET_MODE (x)); + } + else if (CONSTANT_P (x) + /* This hasn't been assigned yet, so it can't conflict yet. */ + || GET_CODE (x) == SCRATCH) + val.safe = 1; + else + abort (); + return val; +} + +/* Return 1 if altering Y will not modify the value of X. + Y is also described by YDATA, which should be decompose (Y). */ + +static int +immune_p (x, y, ydata) + rtx x, y; + struct decomposition ydata; +{ + struct decomposition xdata; + + if (ydata.reg_flag) + return !refers_to_regno_for_reload_p (ydata.start, ydata.end, x, NULL_PTR); + if (ydata.safe) + return 1; + + if (GET_CODE (y) != MEM) + abort (); + /* If Y is memory and X is not, Y can't affect X. */ + if (GET_CODE (x) != MEM) + return 1; + + xdata = decompose (x); + + if (! rtx_equal_p (xdata.base, ydata.base)) + { + /* If bases are distinct symbolic constants, there is no overlap. */ + if (CONSTANT_P (xdata.base) && CONSTANT_P (ydata.base)) + return 1; + /* Constants and stack slots never overlap. */ + if (CONSTANT_P (xdata.base) + && (ydata.base == frame_pointer_rtx + || ydata.base == stack_pointer_rtx)) + return 1; + if (CONSTANT_P (ydata.base) + && (xdata.base == frame_pointer_rtx + || xdata.base == stack_pointer_rtx)) + return 1; + /* If either base is variable, we don't know anything. */ + return 0; + } + + + return (xdata.start >= ydata.end || ydata.start >= xdata.end); +} + +/* Similar, but calls decompose. */ + +int +safe_from_earlyclobber (op, clobber) + rtx op, clobber; +{ + struct decomposition early_data; + + early_data = decompose (clobber); + return immune_p (op, clobber, early_data); +} + +/* Main entry point of this file: search the body of INSN + for values that need reloading and record them with push_reload. + REPLACE nonzero means record also where the values occur + so that subst_reloads can be used. + + IND_LEVELS says how many levels of indirection are supported by this + machine; a value of zero means that a memory reference is not a valid + memory address. + + LIVE_KNOWN says we have valid information about which hard + regs are live at each point in the program; this is true when + we are called from global_alloc but false when stupid register + allocation has been done. + + RELOAD_REG_P if nonzero is a vector indexed by hard reg number + which is nonnegative if the reg has been commandeered for reloading into. + It is copied into STATIC_RELOAD_REG_P and referenced from there + by various subroutines. */ + +void +find_reloads (insn, replace, ind_levels, live_known, reload_reg_p) + rtx insn; + int replace, ind_levels; + int live_known; + short *reload_reg_p; +{ +#ifdef REGISTER_CONSTRAINTS + + register int insn_code_number; + register int i, j; + int noperands; + /* These are the constraints for the insn. We don't change them. */ + char *constraints1[MAX_RECOG_OPERANDS]; + /* These start out as the constraints for the insn + and they are chewed up as we consider alternatives. */ + char *constraints[MAX_RECOG_OPERANDS]; + /* These are the preferred classes for an operand, or NO_REGS if it isn't + a register. */ + enum reg_class preferred_class[MAX_RECOG_OPERANDS]; + char pref_or_nothing[MAX_RECOG_OPERANDS]; + /* Nonzero for a MEM operand whose entire address needs a reload. */ + int address_reloaded[MAX_RECOG_OPERANDS]; + /* Value of enum reload_type to use for operand. */ + enum reload_type operand_type[MAX_RECOG_OPERANDS]; + /* Value of enum reload_type to use within address of operand. */ + enum reload_type address_type[MAX_RECOG_OPERANDS]; + /* Save the usage of each operand. */ + enum reload_usage { RELOAD_READ, RELOAD_READ_WRITE, RELOAD_WRITE } modified[MAX_RECOG_OPERANDS]; + int no_input_reloads = 0, no_output_reloads = 0; + int n_alternatives; + int this_alternative[MAX_RECOG_OPERANDS]; + char this_alternative_win[MAX_RECOG_OPERANDS]; + char this_alternative_offmemok[MAX_RECOG_OPERANDS]; + char this_alternative_earlyclobber[MAX_RECOG_OPERANDS]; + int this_alternative_matches[MAX_RECOG_OPERANDS]; + int swapped; + int goal_alternative[MAX_RECOG_OPERANDS]; + int this_alternative_number; + int goal_alternative_number; + int operand_reloadnum[MAX_RECOG_OPERANDS]; + int goal_alternative_matches[MAX_RECOG_OPERANDS]; + int goal_alternative_matched[MAX_RECOG_OPERANDS]; + char goal_alternative_win[MAX_RECOG_OPERANDS]; + char goal_alternative_offmemok[MAX_RECOG_OPERANDS]; + char goal_alternative_earlyclobber[MAX_RECOG_OPERANDS]; + int goal_alternative_swapped; + int best; + int commutative; + char operands_match[MAX_RECOG_OPERANDS][MAX_RECOG_OPERANDS]; + rtx substed_operand[MAX_RECOG_OPERANDS]; + rtx body = PATTERN (insn); + rtx set = single_set (insn); + int goal_earlyclobber, this_earlyclobber; + enum machine_mode operand_mode[MAX_RECOG_OPERANDS]; + + this_insn = insn; + this_insn_is_asm = 0; /* Tentative. */ + n_reloads = 0; + n_replacements = 0; + n_memlocs = 0; + n_earlyclobbers = 0; + replace_reloads = replace; + hard_regs_live_known = live_known; + static_reload_reg_p = reload_reg_p; + + /* JUMP_INSNs and CALL_INSNs are not allowed to have any output reloads; + neither are insns that SET cc0. Insns that use CC0 are not allowed + to have any input reloads. */ + if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CALL_INSN) + no_output_reloads = 1; + +#ifdef HAVE_cc0 + if (reg_referenced_p (cc0_rtx, PATTERN (insn))) + no_input_reloads = 1; + if (reg_set_p (cc0_rtx, PATTERN (insn))) + no_output_reloads = 1; +#endif + +#ifdef SECONDARY_MEMORY_NEEDED + /* The eliminated forms of any secondary memory locations are per-insn, so + clear them out here. */ + + bzero (secondary_memlocs_elim, sizeof secondary_memlocs_elim); +#endif + + /* Find what kind of insn this is. NOPERANDS gets number of operands. + Make OPERANDS point to a vector of operand values. + Make OPERAND_LOCS point to a vector of pointers to + where the operands were found. + Fill CONSTRAINTS and CONSTRAINTS1 with pointers to the + constraint-strings for this insn. + Return if the insn needs no reload processing. */ + + switch (GET_CODE (body)) + { + case USE: + case CLOBBER: + case ASM_INPUT: + case ADDR_VEC: + case ADDR_DIFF_VEC: + return; + + case SET: + /* Dispose quickly of (set (reg..) (reg..)) if both have hard regs and it + is cheap to move between them. If it is not, there may not be an insn + to do the copy, so we may need a reload. */ + if (GET_CODE (SET_DEST (body)) == REG + && REGNO (SET_DEST (body)) < FIRST_PSEUDO_REGISTER + && GET_CODE (SET_SRC (body)) == REG + && REGNO (SET_SRC (body)) < FIRST_PSEUDO_REGISTER + && REGISTER_MOVE_COST (REGNO_REG_CLASS (REGNO (SET_SRC (body))), + REGNO_REG_CLASS (REGNO (SET_DEST (body)))) == 2) + return; + case PARALLEL: + case ASM_OPERANDS: + reload_n_operands = noperands = asm_noperands (body); + if (noperands >= 0) + { + /* This insn is an `asm' with operands. */ + + insn_code_number = -1; + this_insn_is_asm = 1; + + /* expand_asm_operands makes sure there aren't too many operands. */ + if (noperands > MAX_RECOG_OPERANDS) + abort (); + + /* Now get the operand values and constraints out of the insn. */ + + decode_asm_operands (body, recog_operand, recog_operand_loc, + constraints, operand_mode); + if (noperands > 0) + { + bcopy (constraints, constraints1, noperands * sizeof (char *)); + n_alternatives = n_occurrences (',', constraints[0]) + 1; + for (i = 1; i < noperands; i++) + if (n_alternatives != n_occurrences (',', constraints[i]) + 1) + { + error_for_asm (insn, "operand constraints differ in number of alternatives"); + /* Avoid further trouble with this insn. */ + PATTERN (insn) = gen_rtx (USE, VOIDmode, const0_rtx); + n_reloads = 0; + return; + } + } + break; + } + + default: + /* Ordinary insn: recognize it, get the operands via insn_extract + and get the constraints. */ + + insn_code_number = recog_memoized (insn); + if (insn_code_number < 0) + fatal_insn_not_found (insn); + + reload_n_operands = noperands = insn_n_operands[insn_code_number]; + n_alternatives = insn_n_alternatives[insn_code_number]; + /* Just return "no reloads" if insn has no operands with constraints. */ + if (n_alternatives == 0) + return; + insn_extract (insn); + for (i = 0; i < noperands; i++) + { + constraints[i] = constraints1[i] + = insn_operand_constraint[insn_code_number][i]; + operand_mode[i] = insn_operand_mode[insn_code_number][i]; + } + } + + if (noperands == 0) + return; + + commutative = -1; + + /* If we will need to know, later, whether some pair of operands + are the same, we must compare them now and save the result. + Reloading the base and index registers will clobber them + and afterward they will fail to match. */ + + for (i = 0; i < noperands; i++) + { + register char *p; + register int c; + + substed_operand[i] = recog_operand[i]; + p = constraints[i]; + + modified[i] = RELOAD_READ; + + /* Scan this operand's constraint to see if it is an output operand, + an in-out operand, is commutative, or should match another. */ + + while (c = *p++) + { + if (c == '=') + modified[i] = RELOAD_WRITE; + else if (c == '+') + modified[i] = RELOAD_READ_WRITE; + else if (c == '%') + { + /* The last operand should not be marked commutative. */ + if (i == noperands - 1) + { + if (this_insn_is_asm) + warning_for_asm (this_insn, + "`%%' constraint used with last operand"); + else + abort (); + } + else + commutative = i; + } + else if (c >= '0' && c <= '9') + { + c -= '0'; + operands_match[c][i] + = operands_match_p (recog_operand[c], recog_operand[i]); + + /* An operand may not match itself. */ + if (c == i) + { + if (this_insn_is_asm) + warning_for_asm (this_insn, + "operand %d has constraint %d", i, c); + else + abort (); + } + + /* If C can be commuted with C+1, and C might need to match I, + then C+1 might also need to match I. */ + if (commutative >= 0) + { + if (c == commutative || c == commutative + 1) + { + int other = c + (c == commutative ? 1 : -1); + operands_match[other][i] + = operands_match_p (recog_operand[other], recog_operand[i]); + } + if (i == commutative || i == commutative + 1) + { + int other = i + (i == commutative ? 1 : -1); + operands_match[c][other] + = operands_match_p (recog_operand[c], recog_operand[other]); + } + /* Note that C is supposed to be less than I. + No need to consider altering both C and I because in + that case we would alter one into the other. */ + } + } + } + } + + /* Examine each operand that is a memory reference or memory address + and reload parts of the addresses into index registers. + Also here any references to pseudo regs that didn't get hard regs + but are equivalent to constants get replaced in the insn itself + with those constants. Nobody will ever see them again. + + Finally, set up the preferred classes of each operand. */ + + for (i = 0; i < noperands; i++) + { + register RTX_CODE code = GET_CODE (recog_operand[i]); + + address_reloaded[i] = 0; + operand_type[i] = (modified[i] == RELOAD_READ ? RELOAD_FOR_INPUT + : modified[i] == RELOAD_WRITE ? RELOAD_FOR_OUTPUT + : RELOAD_OTHER); + address_type[i] + = (modified[i] == RELOAD_READ ? RELOAD_FOR_INPUT_ADDRESS + : modified[i] == RELOAD_WRITE ? RELOAD_FOR_OUTPUT_ADDRESS + : RELOAD_OTHER); + + if (constraints[i][0] == 'p') + { + find_reloads_address (VOIDmode, NULL_PTR, + recog_operand[i], recog_operand_loc[i], + i, operand_type[i], ind_levels); + substed_operand[i] = recog_operand[i] = *recog_operand_loc[i]; + } + else if (code == MEM) + { + if (find_reloads_address (GET_MODE (recog_operand[i]), + recog_operand_loc[i], + XEXP (recog_operand[i], 0), + &XEXP (recog_operand[i], 0), + i, address_type[i], ind_levels)) + address_reloaded[i] = 1; + substed_operand[i] = recog_operand[i] = *recog_operand_loc[i]; + } + else if (code == SUBREG) + substed_operand[i] = recog_operand[i] = *recog_operand_loc[i] + = find_reloads_toplev (recog_operand[i], i, address_type[i], + ind_levels, + set != 0 + && &SET_DEST (set) == recog_operand_loc[i]); + else if (code == REG) + { + /* This is equivalent to calling find_reloads_toplev. + The code is duplicated for speed. + When we find a pseudo always equivalent to a constant, + we replace it by the constant. We must be sure, however, + that we don't try to replace it in the insn in which it + is being set. */ + register int regno = REGNO (recog_operand[i]); + if (reg_equiv_constant[regno] != 0 + && (set == 0 || &SET_DEST (set) != recog_operand_loc[i])) + substed_operand[i] = recog_operand[i] + = reg_equiv_constant[regno]; +#if 0 /* This might screw code in reload1.c to delete prior output-reload + that feeds this insn. */ + if (reg_equiv_mem[regno] != 0) + substed_operand[i] = recog_operand[i] + = reg_equiv_mem[regno]; +#endif + if (reg_equiv_address[regno] != 0) + { + /* If reg_equiv_address is not a constant address, copy it, + since it may be shared. */ + rtx address = reg_equiv_address[regno]; + + if (rtx_varies_p (address)) + address = copy_rtx (address); + + /* If this is an output operand, we must output a CLOBBER + after INSN so find_equiv_reg knows REGNO is being written. + Mark this insn specially, do we can put our output reloads + after it. */ + + if (modified[i] != RELOAD_READ) + PUT_MODE (emit_insn_after (gen_rtx (CLOBBER, VOIDmode, + recog_operand[i]), + insn), + DImode); + + *recog_operand_loc[i] = recog_operand[i] + = gen_rtx (MEM, GET_MODE (recog_operand[i]), address); + RTX_UNCHANGING_P (recog_operand[i]) + = RTX_UNCHANGING_P (regno_reg_rtx[regno]); + find_reloads_address (GET_MODE (recog_operand[i]), + recog_operand_loc[i], + XEXP (recog_operand[i], 0), + &XEXP (recog_operand[i], 0), + i, address_type[i], ind_levels); + substed_operand[i] = recog_operand[i] = *recog_operand_loc[i]; + } + } + /* If the operand is still a register (we didn't replace it with an + equivalent), get the preferred class to reload it into. */ + code = GET_CODE (recog_operand[i]); + preferred_class[i] + = ((code == REG && REGNO (recog_operand[i]) >= FIRST_PSEUDO_REGISTER) + ? reg_preferred_class (REGNO (recog_operand[i])) : NO_REGS); + pref_or_nothing[i] + = (code == REG && REGNO (recog_operand[i]) >= FIRST_PSEUDO_REGISTER + && reg_alternate_class (REGNO (recog_operand[i])) == NO_REGS); + } + + /* If this is simply a copy from operand 1 to operand 0, merge the + preferred classes for the operands. */ + if (set != 0 && noperands >= 2 && recog_operand[0] == SET_DEST (set) + && recog_operand[1] == SET_SRC (set)) + { + preferred_class[0] = preferred_class[1] + = reg_class_subunion[(int) preferred_class[0]][(int) preferred_class[1]]; + pref_or_nothing[0] |= pref_or_nothing[1]; + pref_or_nothing[1] |= pref_or_nothing[0]; + } + + /* Now see what we need for pseudo-regs that didn't get hard regs + or got the wrong kind of hard reg. For this, we must consider + all the operands together against the register constraints. */ + + best = MAX_RECOG_OPERANDS + 300; + + swapped = 0; + goal_alternative_swapped = 0; + try_swapped: + + /* The constraints are made of several alternatives. + Each operand's constraint looks like foo,bar,... with commas + separating the alternatives. The first alternatives for all + operands go together, the second alternatives go together, etc. + + First loop over alternatives. */ + + for (this_alternative_number = 0; + this_alternative_number < n_alternatives; + this_alternative_number++) + { + /* Loop over operands for one constraint alternative. */ + /* LOSERS counts those that don't fit this alternative + and would require loading. */ + int losers = 0; + /* BAD is set to 1 if it some operand can't fit this alternative + even after reloading. */ + int bad = 0; + /* REJECT is a count of how undesirable this alternative says it is + if any reloading is required. If the alternative matches exactly + then REJECT is ignored, but otherwise it gets this much + counted against it in addition to the reloading needed. Each + ? counts three times here since we want the disparaging caused by + a bad register class to only count 1/3 as much. */ + int reject = 0; + + this_earlyclobber = 0; + + for (i = 0; i < noperands; i++) + { + register char *p = constraints[i]; + register int win = 0; + /* 0 => this operand can be reloaded somehow for this alternative */ + int badop = 1; + /* 0 => this operand can be reloaded if the alternative allows regs. */ + int winreg = 0; + int c; + register rtx operand = recog_operand[i]; + int offset = 0; + /* Nonzero means this is a MEM that must be reloaded into a reg + regardless of what the constraint says. */ + int force_reload = 0; + int offmemok = 0; + int earlyclobber = 0; + + /* If the operand is a SUBREG, extract + the REG or MEM (or maybe even a constant) within. + (Constants can occur as a result of reg_equiv_constant.) */ + + while (GET_CODE (operand) == SUBREG) + { + offset += SUBREG_WORD (operand); + operand = SUBREG_REG (operand); + /* Force reload if this is not a register or if there may may + be a problem accessing the register in the outer mode. */ + if (GET_CODE (operand) != REG +#if defined(BYTE_LOADS_ZERO_EXTEND) || defined(BYTE_LOADS_SIGN_EXTEND) + /* ??? The comment below clearly does not match the code. + What the code below actually does is set force_reload + for a paradoxical subreg of a pseudo. rms and kenner + can't see the point of doing this. */ + /* Nonparadoxical subreg of a pseudoreg. + Don't to load the full width if on this machine + we expected the fetch to extend. */ + || ((GET_MODE_SIZE (operand_mode[i]) + > GET_MODE_SIZE (GET_MODE (operand))) + && REGNO (operand) >= FIRST_PSEUDO_REGISTER) +#endif + /* Subreg of a hard reg which can't handle the subreg's mode + or which would handle that mode in the wrong number of + registers for subregging to work. */ + || (REGNO (operand) < FIRST_PSEUDO_REGISTER + && (! HARD_REGNO_MODE_OK (REGNO (operand), + operand_mode[i]) + || (GET_MODE_SIZE (operand_mode[i]) <= UNITS_PER_WORD + && (GET_MODE_SIZE (GET_MODE (operand)) + > UNITS_PER_WORD) + && ((GET_MODE_SIZE (GET_MODE (operand)) + / UNITS_PER_WORD) + != HARD_REGNO_NREGS (REGNO (operand), + GET_MODE (operand))))))) + force_reload = 1; + } + + this_alternative[i] = (int) NO_REGS; + this_alternative_win[i] = 0; + this_alternative_offmemok[i] = 0; + this_alternative_earlyclobber[i] = 0; + this_alternative_matches[i] = -1; + + /* An empty constraint or empty alternative + allows anything which matched the pattern. */ + if (*p == 0 || *p == ',') + win = 1, badop = 0; + + /* Scan this alternative's specs for this operand; + set WIN if the operand fits any letter in this alternative. + Otherwise, clear BADOP if this operand could + fit some letter after reloads, + or set WINREG if this operand could fit after reloads + provided the constraint allows some registers. */ + + while (*p && (c = *p++) != ',') + switch (c) + { + case '=': + case '+': + case '*': + break; + + case '%': + /* The last operand should not be marked commutative. */ + if (i != noperands - 1) + commutative = i; + break; + + case '?': + reject += 3; + break; + + case '!': + reject = 300; + break; + + case '#': + /* Ignore rest of this alternative as far as + reloading is concerned. */ + while (*p && *p != ',') p++; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + c -= '0'; + this_alternative_matches[i] = c; + /* We are supposed to match a previous operand. + If we do, we win if that one did. + If we do not, count both of the operands as losers. + (This is too conservative, since most of the time + only a single reload insn will be needed to make + the two operands win. As a result, this alternative + may be rejected when it is actually desirable.) */ + if ((swapped && (c != commutative || i != commutative + 1)) + /* If we are matching as if two operands were swapped, + also pretend that operands_match had been computed + with swapped. + But if I is the second of those and C is the first, + don't exchange them, because operands_match is valid + only on one side of its diagonal. */ + ? (operands_match + [(c == commutative || c == commutative + 1) + ? 2*commutative + 1 - c : c] + [(i == commutative || i == commutative + 1) + ? 2*commutative + 1 - i : i]) + : operands_match[c][i]) + win = this_alternative_win[c]; + else + { + /* Operands don't match. */ + rtx value; + /* Retroactively mark the operand we had to match + as a loser, if it wasn't already. */ + if (this_alternative_win[c]) + losers++; + this_alternative_win[c] = 0; + if (this_alternative[c] == (int) NO_REGS) + bad = 1; + /* But count the pair only once in the total badness of + this alternative, if the pair can be a dummy reload. */ + value + = find_dummy_reload (recog_operand[i], recog_operand[c], + recog_operand_loc[i], recog_operand_loc[c], + this_alternative[c], -1); + + if (value != 0) + losers--; + } + /* This can be fixed with reloads if the operand + we are supposed to match can be fixed with reloads. */ + badop = 0; + this_alternative[i] = this_alternative[c]; + break; + + case 'p': + /* All necessary reloads for an address_operand + were handled in find_reloads_address. */ + this_alternative[i] = (int) ALL_REGS; + win = 1; + break; + + case 'm': + if (force_reload) + break; + if (GET_CODE (operand) == MEM + || (GET_CODE (operand) == REG + && REGNO (operand) >= FIRST_PSEUDO_REGISTER + && reg_renumber[REGNO (operand)] < 0)) + win = 1; + if (CONSTANT_P (operand)) + badop = 0; + break; + + case '<': + if (GET_CODE (operand) == MEM + && ! address_reloaded[i] + && (GET_CODE (XEXP (operand, 0)) == PRE_DEC + || GET_CODE (XEXP (operand, 0)) == POST_DEC)) + win = 1; + break; + + case '>': + if (GET_CODE (operand) == MEM + && ! address_reloaded[i] + && (GET_CODE (XEXP (operand, 0)) == PRE_INC + || GET_CODE (XEXP (operand, 0)) == POST_INC)) + win = 1; + break; + + /* Memory operand whose address is not offsettable. */ + case 'V': + if (force_reload) + break; + if (GET_CODE (operand) == MEM + && ! (ind_levels ? offsettable_memref_p (operand) + : offsettable_nonstrict_memref_p (operand)) + /* Certain mem addresses will become offsettable + after they themselves are reloaded. This is important; + we don't want our own handling of unoffsettables + to override the handling of reg_equiv_address. */ + && !(GET_CODE (XEXP (operand, 0)) == REG + && (ind_levels == 0 + || reg_equiv_address[REGNO (XEXP (operand, 0))] != 0))) + win = 1; + break; + + /* Memory operand whose address is offsettable. */ + case 'o': + if (force_reload) + break; + if ((GET_CODE (operand) == MEM + /* If IND_LEVELS, find_reloads_address won't reload a + pseudo that didn't get a hard reg, so we have to + reject that case. */ + && (ind_levels ? offsettable_memref_p (operand) + : offsettable_nonstrict_memref_p (operand))) + /* Certain mem addresses will become offsettable + after they themselves are reloaded. This is important; + we don't want our own handling of unoffsettables + to override the handling of reg_equiv_address. */ + || (GET_CODE (operand) == MEM + && GET_CODE (XEXP (operand, 0)) == REG + && (ind_levels == 0 + || reg_equiv_address[REGNO (XEXP (operand, 0))] != 0)) + || (GET_CODE (operand) == REG + && REGNO (operand) >= FIRST_PSEUDO_REGISTER + && reg_renumber[REGNO (operand)] < 0)) + win = 1; + if (CONSTANT_P (operand) || GET_CODE (operand) == MEM) + badop = 0; + offmemok = 1; + break; + + case '&': + /* Output operand that is stored before the need for the + input operands (and their index registers) is over. */ + earlyclobber = 1, this_earlyclobber = 1; + break; + + case 'E': + /* Match any floating double constant, but only if + we can examine the bits of it reliably. */ + if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT + || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD) + && GET_MODE (operand) != VOIDmode && ! flag_pretend_float) + break; + if (GET_CODE (operand) == CONST_DOUBLE) + win = 1; + break; + + case 'F': + if (GET_CODE (operand) == CONST_DOUBLE) + win = 1; + break; + + case 'G': + case 'H': + if (GET_CODE (operand) == CONST_DOUBLE + && CONST_DOUBLE_OK_FOR_LETTER_P (operand, c)) + win = 1; + break; + + case 's': + if (GET_CODE (operand) == CONST_INT + || (GET_CODE (operand) == CONST_DOUBLE + && GET_MODE (operand) == VOIDmode)) + break; + case 'i': + if (CONSTANT_P (operand) +#ifdef LEGITIMATE_PIC_OPERAND_P + && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (operand)) +#endif + ) + win = 1; + break; + + case 'n': + if (GET_CODE (operand) == CONST_INT + || (GET_CODE (operand) == CONST_DOUBLE + && GET_MODE (operand) == VOIDmode)) + win = 1; + break; + + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + if (GET_CODE (operand) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (operand), c)) + win = 1; + break; + + case 'X': + win = 1; + break; + + case 'g': + if (! force_reload + /* A PLUS is never a valid operand, but reload can make + it from a register when eliminating registers. */ + && GET_CODE (operand) != PLUS + /* A SCRATCH is not a valid operand. */ + && GET_CODE (operand) != SCRATCH +#ifdef LEGITIMATE_PIC_OPERAND_P + && (! CONSTANT_P (operand) + || ! flag_pic + || LEGITIMATE_PIC_OPERAND_P (operand)) +#endif + && (GENERAL_REGS == ALL_REGS + || GET_CODE (operand) != REG + || (REGNO (operand) >= FIRST_PSEUDO_REGISTER + && reg_renumber[REGNO (operand)] < 0))) + win = 1; + /* Drop through into 'r' case */ + + case 'r': + this_alternative[i] + = (int) reg_class_subunion[this_alternative[i]][(int) GENERAL_REGS]; + goto reg; + +#ifdef EXTRA_CONSTRAINT + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + if (EXTRA_CONSTRAINT (operand, c)) + win = 1; + break; +#endif + + default: + this_alternative[i] + = (int) reg_class_subunion[this_alternative[i]][(int) REG_CLASS_FROM_LETTER (c)]; + + reg: + if (GET_MODE (operand) == BLKmode) + break; + winreg = 1; + if (GET_CODE (operand) == REG + && reg_fits_class_p (operand, this_alternative[i], + offset, GET_MODE (recog_operand[i]))) + win = 1; + break; + } + + constraints[i] = p; + + /* If this operand could be handled with a reg, + and some reg is allowed, then this operand can be handled. */ + if (winreg && this_alternative[i] != (int) NO_REGS) + badop = 0; + + /* Record which operands fit this alternative. */ + this_alternative_earlyclobber[i] = earlyclobber; + if (win && ! force_reload) + this_alternative_win[i] = 1; + else + { + this_alternative_offmemok[i] = offmemok; + losers++; + if (badop) + bad = 1; + /* Alternative loses if it has no regs for a reg operand. */ + if (GET_CODE (operand) == REG + && this_alternative[i] == (int) NO_REGS + && this_alternative_matches[i] < 0) + bad = 1; + + /* Alternative loses if it requires a type of reload not + permitted for this insn. We can always reload SCRATCH + and objects with a REG_UNUSED note. */ + if (GET_CODE (operand) != SCRATCH + && modified[i] != RELOAD_READ && no_output_reloads + && ! find_reg_note (insn, REG_UNUSED, operand)) + bad = 1; + else if (modified[i] != RELOAD_WRITE && no_input_reloads) + bad = 1; + + /* We prefer to reload pseudos over reloading other things, + since such reloads may be able to be eliminated later. + If we are reloading a SCRATCH, we won't be generating any + insns, just using a register, so it is also preferred. + So bump REJECT in other cases. */ + if (GET_CODE (operand) != REG && GET_CODE (operand) != SCRATCH) + reject++; + } + + /* If this operand is a pseudo register that didn't get a hard + reg and this alternative accepts some register, see if the + class that we want is a subset of the preferred class for this + register. If not, but it intersects that class, use the + preferred class instead. If it does not intersect the preferred + class, show that usage of this alternative should be discouraged; + it will be discouraged more still if the register is `preferred + or nothing'. We do this because it increases the chance of + reusing our spill register in a later insn and avoiding a pair + of memory stores and loads. + + Don't bother with this if this alternative will accept this + operand. + + Don't do this for a multiword operand, if + we have to worry about small classes, because making reg groups + harder to allocate is asking for trouble. + + Don't do this if the preferred class has only one register + because we might otherwise exhaust the class. */ + + + if (! win && this_alternative[i] != (int) NO_REGS +#ifdef SMALL_REGISTER_CLASSES + && GET_MODE_SIZE (operand_mode[i]) <= UNITS_PER_WORD +#endif + && reg_class_size[(int) preferred_class[i]] > 1) + { + if (! reg_class_subset_p (this_alternative[i], + preferred_class[i])) + { + /* Since we don't have a way of forming the intersection, + we just do something special if the preferred class + is a subset of the class we have; that's the most + common case anyway. */ + if (reg_class_subset_p (preferred_class[i], + this_alternative[i])) + this_alternative[i] = (int) preferred_class[i]; + else + reject += (1 + pref_or_nothing[i]); + } + } + } + + /* Now see if any output operands that are marked "earlyclobber" + in this alternative conflict with any input operands + or any memory addresses. */ + + for (i = 0; i < noperands; i++) + if (this_alternative_earlyclobber[i] + && this_alternative_win[i]) + { + struct decomposition early_data; + + early_data = decompose (recog_operand[i]); + + if (modified[i] == RELOAD_READ) + { + if (this_insn_is_asm) + warning_for_asm (this_insn, + "`&' constraint used with input operand"); + else + abort (); + continue; + } + + if (this_alternative[i] == NO_REGS) + { + this_alternative_earlyclobber[i] = 0; + if (this_insn_is_asm) + error_for_asm (this_insn, + "`&' constraint used with no register class"); + else + abort (); + } + + for (j = 0; j < noperands; j++) + /* Is this an input operand or a memory ref? */ + if ((GET_CODE (recog_operand[j]) == MEM + || modified[j] != RELOAD_WRITE) + && j != i + /* Ignore things like match_operator operands. */ + && *constraints1[j] != 0 + /* Don't count an input operand that is constrained to match + the early clobber operand. */ + && ! (this_alternative_matches[j] == i + && rtx_equal_p (recog_operand[i], recog_operand[j])) + /* Is it altered by storing the earlyclobber operand? */ + && !immune_p (recog_operand[j], recog_operand[i], early_data)) + { + /* If the output is in a single-reg class, + it's costly to reload it, so reload the input instead. */ + if (reg_class_size[this_alternative[i]] == 1 + && (GET_CODE (recog_operand[j]) == REG + || GET_CODE (recog_operand[j]) == SUBREG)) + { + losers++; + this_alternative_win[j] = 0; + } + else + break; + } + /* If an earlyclobber operand conflicts with something, + it must be reloaded, so request this and count the cost. */ + if (j != noperands) + { + losers++; + this_alternative_win[i] = 0; + for (j = 0; j < noperands; j++) + if (this_alternative_matches[j] == i + && this_alternative_win[j]) + { + this_alternative_win[j] = 0; + losers++; + } + } + } + + /* If one alternative accepts all the operands, no reload required, + choose that alternative; don't consider the remaining ones. */ + if (losers == 0) + { + /* Unswap these so that they are never swapped at `finish'. */ + if (commutative >= 0) + { + recog_operand[commutative] = substed_operand[commutative]; + recog_operand[commutative + 1] + = substed_operand[commutative + 1]; + } + for (i = 0; i < noperands; i++) + { + goal_alternative_win[i] = 1; + goal_alternative[i] = this_alternative[i]; + goal_alternative_offmemok[i] = this_alternative_offmemok[i]; + goal_alternative_matches[i] = this_alternative_matches[i]; + goal_alternative_earlyclobber[i] + = this_alternative_earlyclobber[i]; + } + goal_alternative_number = this_alternative_number; + goal_alternative_swapped = swapped; + goal_earlyclobber = this_earlyclobber; + goto finish; + } + + /* REJECT, set by the ! and ? constraint characters and when a register + would be reloaded into a non-preferred class, discourages the use of + this alternative for a reload goal. REJECT is incremented by three + for each ? and one for each non-preferred class. */ + losers = losers * 3 + reject; + + /* If this alternative can be made to work by reloading, + and it needs less reloading than the others checked so far, + record it as the chosen goal for reloading. */ + if (! bad && best > losers) + { + for (i = 0; i < noperands; i++) + { + goal_alternative[i] = this_alternative[i]; + goal_alternative_win[i] = this_alternative_win[i]; + goal_alternative_offmemok[i] = this_alternative_offmemok[i]; + goal_alternative_matches[i] = this_alternative_matches[i]; + goal_alternative_earlyclobber[i] + = this_alternative_earlyclobber[i]; + } + goal_alternative_swapped = swapped; + best = losers; + goal_alternative_number = this_alternative_number; + goal_earlyclobber = this_earlyclobber; + } + } + + /* If insn is commutative (it's safe to exchange a certain pair of operands) + then we need to try each alternative twice, + the second time matching those two operands + as if we had exchanged them. + To do this, really exchange them in operands. + + If we have just tried the alternatives the second time, + return operands to normal and drop through. */ + + if (commutative >= 0) + { + swapped = !swapped; + if (swapped) + { + register enum reg_class tclass; + register int t; + + recog_operand[commutative] = substed_operand[commutative + 1]; + recog_operand[commutative + 1] = substed_operand[commutative]; + + tclass = preferred_class[commutative]; + preferred_class[commutative] = preferred_class[commutative + 1]; + preferred_class[commutative + 1] = tclass; + + t = pref_or_nothing[commutative]; + pref_or_nothing[commutative] = pref_or_nothing[commutative + 1]; + pref_or_nothing[commutative + 1] = t; + + bcopy (constraints1, constraints, noperands * sizeof (char *)); + goto try_swapped; + } + else + { + recog_operand[commutative] = substed_operand[commutative]; + recog_operand[commutative + 1] = substed_operand[commutative + 1]; + } + } + + /* The operands don't meet the constraints. + goal_alternative describes the alternative + that we could reach by reloading the fewest operands. + Reload so as to fit it. */ + + if (best == MAX_RECOG_OPERANDS + 300) + { + /* No alternative works with reloads?? */ + if (insn_code_number >= 0) + abort (); + error_for_asm (insn, "inconsistent operand constraints in an `asm'"); + /* Avoid further trouble with this insn. */ + PATTERN (insn) = gen_rtx (USE, VOIDmode, const0_rtx); + n_reloads = 0; + return; + } + + /* Jump to `finish' from above if all operands are valid already. + In that case, goal_alternative_win is all 1. */ + finish: + + /* Right now, for any pair of operands I and J that are required to match, + with I < J, + goal_alternative_matches[J] is I. + Set up goal_alternative_matched as the inverse function: + goal_alternative_matched[I] = J. */ + + for (i = 0; i < noperands; i++) + goal_alternative_matched[i] = -1; + + for (i = 0; i < noperands; i++) + if (! goal_alternative_win[i] + && goal_alternative_matches[i] >= 0) + goal_alternative_matched[goal_alternative_matches[i]] = i; + + /* If the best alternative is with operands 1 and 2 swapped, + consider them swapped before reporting the reloads. Update the + operand numbers of any reloads already pushed. */ + + if (goal_alternative_swapped) + { + register rtx tem; + + tem = substed_operand[commutative]; + substed_operand[commutative] = substed_operand[commutative + 1]; + substed_operand[commutative + 1] = tem; + tem = recog_operand[commutative]; + recog_operand[commutative] = recog_operand[commutative + 1]; + recog_operand[commutative + 1] = tem; + + for (i = 0; i < n_reloads; i++) + { + if (reload_opnum[i] == commutative) + reload_opnum[i] = commutative + 1; + else if (reload_opnum[i] == commutative + 1) + reload_opnum[i] = commutative; + } + } + + /* Perform whatever substitutions on the operands we are supposed + to make due to commutativity or replacement of registers + with equivalent constants or memory slots. */ + + for (i = 0; i < noperands; i++) + { + *recog_operand_loc[i] = substed_operand[i]; + /* While we are looping on operands, initialize this. */ + operand_reloadnum[i] = -1; + + /* If this is an earlyclobber operand, we need to widen the scope. + The reload must remain valid from the start of the insn being + reloaded until after the operand is stored into its destination. + We approximate this with RELOAD_OTHER even though we know that we + do not conflict with RELOAD_FOR_INPUT_ADDRESS reloads. + + One special case that is worth checking is when we have an + output that is earlyclobber but isn't used past the insn (typically + a SCRATCH). In this case, we only need have the reload live + through the insn itself, but not for any of our input or output + reloads. + + In any case, anything needed to address this operand can remain + however they were previously categorized. */ + + if (goal_alternative_earlyclobber[i]) + operand_type[i] + = (find_reg_note (insn, REG_UNUSED, recog_operand[i]) + ? RELOAD_FOR_INSN : RELOAD_OTHER); + } + + /* Any constants that aren't allowed and can't be reloaded + into registers are here changed into memory references. */ + for (i = 0; i < noperands; i++) + if (! goal_alternative_win[i] + && CONSTANT_P (recog_operand[i]) + && (PREFERRED_RELOAD_CLASS (recog_operand[i], + (enum reg_class) goal_alternative[i]) + == NO_REGS) + && operand_mode[i] != VOIDmode) + { + *recog_operand_loc[i] = recog_operand[i] + = find_reloads_toplev (force_const_mem (operand_mode[i], + recog_operand[i]), + i, address_type[i], ind_levels, 0); + if (alternative_allows_memconst (constraints1[i], + goal_alternative_number)) + goal_alternative_win[i] = 1; + } + + /* Now record reloads for all the operands that need them. */ + for (i = 0; i < noperands; i++) + if (! goal_alternative_win[i]) + { + /* Operands that match previous ones have already been handled. */ + if (goal_alternative_matches[i] >= 0) + ; + /* Handle an operand with a nonoffsettable address + appearing where an offsettable address will do + by reloading the address into a base register. */ + else if (goal_alternative_matched[i] == -1 + && goal_alternative_offmemok[i] + && GET_CODE (recog_operand[i]) == MEM) + { + operand_reloadnum[i] + = push_reload (XEXP (recog_operand[i], 0), NULL_RTX, + &XEXP (recog_operand[i], 0), NULL_PTR, + BASE_REG_CLASS, GET_MODE (XEXP (recog_operand[i], 0)), + VOIDmode, 0, 0, i, RELOAD_FOR_INPUT); + reload_inc[operand_reloadnum[i]] + = GET_MODE_SIZE (GET_MODE (recog_operand[i])); + + /* If this operand is an output, we will have made any + reloads for its address as RELOAD_FOR_OUTPUT_ADDRESS, but + now we are treating part of the operand as an input, so + we must change these to RELOAD_FOR_INPUT_ADDRESS. */ + + if (operand_type[i] == RELOAD_FOR_OUTPUT) + for (j = 0; j < n_reloads; j++) + if (reload_opnum[j] == i + && reload_when_needed[j] == RELOAD_FOR_OUTPUT_ADDRESS) + reload_when_needed[j] = RELOAD_FOR_INPUT_ADDRESS; + } + else if (goal_alternative_matched[i] == -1) + operand_reloadnum[i] = + push_reload (modified[i] != RELOAD_WRITE ? recog_operand[i] : 0, + modified[i] != RELOAD_READ ? recog_operand[i] : 0, + (modified[i] != RELOAD_WRITE ? + recog_operand_loc[i] : 0), + modified[i] != RELOAD_READ ? recog_operand_loc[i] : 0, + (enum reg_class) goal_alternative[i], + (modified[i] == RELOAD_WRITE + ? VOIDmode : operand_mode[i]), + (modified[i] == RELOAD_READ + ? VOIDmode : operand_mode[i]), + (insn_code_number < 0 ? 0 + : insn_operand_strict_low[insn_code_number][i]), + 0, i, operand_type[i]); + /* In a matching pair of operands, one must be input only + and the other must be output only. + Pass the input operand as IN and the other as OUT. */ + else if (modified[i] == RELOAD_READ + && modified[goal_alternative_matched[i]] == RELOAD_WRITE) + { + operand_reloadnum[i] + = push_reload (recog_operand[i], + recog_operand[goal_alternative_matched[i]], + recog_operand_loc[i], + recog_operand_loc[goal_alternative_matched[i]], + (enum reg_class) goal_alternative[i], + operand_mode[i], + operand_mode[goal_alternative_matched[i]], + 0, 0, i, RELOAD_OTHER); + operand_reloadnum[goal_alternative_matched[i]] = output_reloadnum; + } + else if (modified[i] == RELOAD_WRITE + && modified[goal_alternative_matched[i]] == RELOAD_READ) + { + operand_reloadnum[goal_alternative_matched[i]] + = push_reload (recog_operand[goal_alternative_matched[i]], + recog_operand[i], + recog_operand_loc[goal_alternative_matched[i]], + recog_operand_loc[i], + (enum reg_class) goal_alternative[i], + operand_mode[goal_alternative_matched[i]], + operand_mode[i], + 0, 0, i, RELOAD_OTHER); + operand_reloadnum[i] = output_reloadnum; + } + else if (insn_code_number >= 0) + abort (); + else + { + error_for_asm (insn, "inconsistent operand constraints in an `asm'"); + /* Avoid further trouble with this insn. */ + PATTERN (insn) = gen_rtx (USE, VOIDmode, const0_rtx); + n_reloads = 0; + return; + } + } + else if (goal_alternative_matched[i] < 0 + && goal_alternative_matches[i] < 0 + && optimize) + { + /* For each non-matching operand that's a MEM or a pseudo-register + that didn't get a hard register, make an optional reload. + This may get done even if the insn needs no reloads otherwise. */ + + rtx operand = recog_operand[i]; + + while (GET_CODE (operand) == SUBREG) + operand = XEXP (operand, 0); + if ((GET_CODE (operand) == MEM + || (GET_CODE (operand) == REG + && REGNO (operand) >= FIRST_PSEUDO_REGISTER)) + && (enum reg_class) goal_alternative[i] != NO_REGS + && ! no_input_reloads + /* Optional output reloads don't do anything and we mustn't + make in-out reloads on insns that are not permitted output + reloads. */ + && (modified[i] == RELOAD_READ + || (modified[i] == RELOAD_READ_WRITE && ! no_output_reloads))) + operand_reloadnum[i] + = push_reload (modified[i] != RELOAD_WRITE ? recog_operand[i] : 0, + modified[i] != RELOAD_READ ? recog_operand[i] : 0, + (modified[i] != RELOAD_WRITE + ? recog_operand_loc[i] : 0), + (modified[i] != RELOAD_READ + ? recog_operand_loc[i] : 0), + (enum reg_class) goal_alternative[i], + (modified[i] == RELOAD_WRITE + ? VOIDmode : operand_mode[i]), + (modified[i] == RELOAD_READ + ? VOIDmode : operand_mode[i]), + (insn_code_number < 0 ? 0 + : insn_operand_strict_low[insn_code_number][i]), + 1, i, operand_type[i]); + } + else if (goal_alternative_matches[i] >= 0 + && goal_alternative_win[goal_alternative_matches[i]] + && modified[i] == RELOAD_READ + && modified[goal_alternative_matches[i]] == RELOAD_WRITE + && ! no_input_reloads && ! no_output_reloads + && optimize) + { + /* Similarly, make an optional reload for a pair of matching + objects that are in MEM or a pseudo that didn't get a hard reg. */ + + rtx operand = recog_operand[i]; + + while (GET_CODE (operand) == SUBREG) + operand = XEXP (operand, 0); + if ((GET_CODE (operand) == MEM + || (GET_CODE (operand) == REG + && REGNO (operand) >= FIRST_PSEUDO_REGISTER)) + && ((enum reg_class) goal_alternative[goal_alternative_matches[i]] + != NO_REGS)) + operand_reloadnum[i] = operand_reloadnum[goal_alternative_matches[i]] + = push_reload (recog_operand[goal_alternative_matches[i]], + recog_operand[i], + recog_operand_loc[goal_alternative_matches[i]], + recog_operand_loc[i], + (enum reg_class) goal_alternative[goal_alternative_matches[i]], + operand_mode[goal_alternative_matches[i]], + operand_mode[i], + 0, 1, goal_alternative_matches[i], RELOAD_OTHER); + } + + /* Record the values of the earlyclobber operands for the caller. */ + if (goal_earlyclobber) + for (i = 0; i < noperands; i++) + if (goal_alternative_earlyclobber[i]) + reload_earlyclobbers[n_earlyclobbers++] = recog_operand[i]; + + /* If this insn pattern contains any MATCH_DUP's, make sure that + they will be substituted if the operands they match are substituted. + Also do now any substitutions we already did on the operands. + + Don't do this if we aren't making replacements because we might be + propagating things allocated by frame pointer elimination into places + it doesn't expect. */ + + if (insn_code_number >= 0 && replace) + for (i = insn_n_dups[insn_code_number] - 1; i >= 0; i--) + { + int opno = recog_dup_num[i]; + *recog_dup_loc[i] = *recog_operand_loc[opno]; + if (operand_reloadnum[opno] >= 0) + push_replacement (recog_dup_loc[i], operand_reloadnum[opno], + insn_operand_mode[insn_code_number][opno]); + } + +#if 0 + /* This loses because reloading of prior insns can invalidate the equivalence + (or at least find_equiv_reg isn't smart enough to find it any more), + causing this insn to need more reload regs than it needed before. + It may be too late to make the reload regs available. + Now this optimization is done safely in choose_reload_regs. */ + + /* For each reload of a reg into some other class of reg, + search for an existing equivalent reg (same value now) in the right class. + We can use it as long as we don't need to change its contents. */ + for (i = 0; i < n_reloads; i++) + if (reload_reg_rtx[i] == 0 + && reload_in[i] != 0 + && GET_CODE (reload_in[i]) == REG + && reload_out[i] == 0) + { + reload_reg_rtx[i] + = find_equiv_reg (reload_in[i], insn, reload_reg_class[i], -1, + static_reload_reg_p, 0, reload_inmode[i]); + /* Prevent generation of insn to load the value + because the one we found already has the value. */ + if (reload_reg_rtx[i]) + reload_in[i] = reload_reg_rtx[i]; + } +#endif + + /* Perhaps an output reload can be combined with another + to reduce needs by one. */ + if (!goal_earlyclobber) + combine_reloads (); + + /* If we have a pair of reloads for parts of an address, they are reloading + the same object, the operands themselves were not reloaded, and they + are for two operands that are supposed to match, merge the reloads and + change the type of the surviving reload to RELOAD_FOR_OPERAND_ADDRESS. */ + + for (i = 0; i < n_reloads; i++) + { + int k; + + for (j = i + 1; j < n_reloads; j++) + if ((reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS + || reload_when_needed[i] == RELOAD_FOR_OUTPUT_ADDRESS) + && (reload_when_needed[j] == RELOAD_FOR_INPUT_ADDRESS + || reload_when_needed[j] == RELOAD_FOR_OUTPUT_ADDRESS) + && rtx_equal_p (reload_in[i], reload_in[j]) + && (operand_reloadnum[reload_opnum[i]] < 0 + || reload_optional[operand_reloadnum[reload_opnum[i]]]) + && (operand_reloadnum[reload_opnum[j]] < 0 + || reload_optional[operand_reloadnum[reload_opnum[j]]]) + && (goal_alternative_matches[reload_opnum[i]] == reload_opnum[j] + || (goal_alternative_matches[reload_opnum[j]] + == reload_opnum[i]))) + { + for (k = 0; k < n_replacements; k++) + if (replacements[k].what == j) + replacements[k].what = i; + + reload_when_needed[i] = RELOAD_FOR_OPERAND_ADDRESS; + reload_in[j] = 0; + } + } + + /* Scan all the reloads and update their type. + If a reload is for the address of an operand and we didn't reload + that operand, change the type. Similarly, change the operand number + of a reload when two operands match. If a reload is optional, treat it + as though the operand isn't reloaded. + + ??? This latter case is somewhat odd because if we do the optional + reload, it means the object is hanging around. Thus we need only + do the address reload if the optional reload was NOT done. + + Change secondary reloads to be the address type of their operand, not + the normal type. + + If an operand's reload is now RELOAD_OTHER, change any + RELOAD_FOR_INPUT_ADDRESS reloads of that operand to + RELOAD_FOR_OTHER_ADDRESS. */ + + for (i = 0; i < n_reloads; i++) + { + if (reload_secondary_p[i] + && reload_when_needed[i] == operand_type[reload_opnum[i]]) + reload_when_needed[i] = address_type[reload_opnum[i]]; + + if ((reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS + || reload_when_needed[i] == RELOAD_FOR_OUTPUT_ADDRESS) + && (operand_reloadnum[reload_opnum[i]] < 0 + || reload_optional[operand_reloadnum[reload_opnum[i]]])) + reload_when_needed[i] = RELOAD_FOR_OPERAND_ADDRESS; + + if (reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS + && operand_reloadnum[reload_opnum[i]] >= 0 + && (reload_when_needed[operand_reloadnum[reload_opnum[i]]] + == RELOAD_OTHER)) + reload_when_needed[i] = RELOAD_FOR_OTHER_ADDRESS; + + if (goal_alternative_matches[reload_opnum[i]] >= 0) + reload_opnum[i] = goal_alternative_matches[reload_opnum[i]]; + } + + /* See if we have any reloads that are now allowed to be merged + because we've changed when the reload is needed to + RELOAD_FOR_OPERAND_ADDRESS or RELOAD_FOR_OTHER_ADDRESS. Only + check for the most common cases. */ + + for (i = 0; i < n_reloads; i++) + if (reload_in[i] != 0 && reload_out[i] == 0 + && (reload_when_needed[i] == RELOAD_FOR_OPERAND_ADDRESS + || reload_when_needed[i] == RELOAD_FOR_OTHER_ADDRESS)) + for (j = 0; j < n_reloads; j++) + if (i != j && reload_in[j] != 0 && reload_out[j] == 0 + && reload_when_needed[j] == reload_when_needed[i] + && MATCHES (reload_in[i], reload_in[j])) + { + reload_opnum[i] = MIN (reload_opnum[i], reload_opnum[j]); + transfer_replacements (i, j); + reload_in[j] = 0; + } + +#else /* no REGISTER_CONSTRAINTS */ + int noperands; + int insn_code_number; + int goal_earlyclobber = 0; /* Always 0, to make combine_reloads happen. */ + register int i; + rtx body = PATTERN (insn); + + n_reloads = 0; + n_replacements = 0; + n_earlyclobbers = 0; + replace_reloads = replace; + this_insn = insn; + + /* Find what kind of insn this is. NOPERANDS gets number of operands. + Store the operand values in RECOG_OPERAND and the locations + of the words in the insn that point to them in RECOG_OPERAND_LOC. + Return if the insn needs no reload processing. */ + + switch (GET_CODE (body)) + { + case USE: + case CLOBBER: + case ASM_INPUT: + case ADDR_VEC: + case ADDR_DIFF_VEC: + return; + + case PARALLEL: + case SET: + noperands = asm_noperands (body); + if (noperands >= 0) + { + /* This insn is an `asm' with operands. + First, find out how many operands, and allocate space. */ + + insn_code_number = -1; + /* ??? This is a bug! ??? + Give up and delete this insn if it has too many operands. */ + if (noperands > MAX_RECOG_OPERANDS) + abort (); + + /* Now get the operand values out of the insn. */ + + decode_asm_operands (body, recog_operand, recog_operand_loc, + NULL_PTR, NULL_PTR); + break; + } + + default: + /* Ordinary insn: recognize it, allocate space for operands and + constraints, and get them out via insn_extract. */ + + insn_code_number = recog_memoized (insn); + noperands = insn_n_operands[insn_code_number]; + insn_extract (insn); + } + + if (noperands == 0) + return; + + for (i = 0; i < noperands; i++) + { + register RTX_CODE code = GET_CODE (recog_operand[i]); + int is_set_dest = GET_CODE (body) == SET && (i == 0); + + if (insn_code_number >= 0) + if (insn_operand_address_p[insn_code_number][i]) + find_reloads_address (VOIDmode, NULL_PTR, + recog_operand[i], recog_operand_loc[i], + i, RELOAD_FOR_INPUT, ind_levels); + + /* In these cases, we can't tell if the operand is an input + or an output, so be conservative. In practice it won't be + problem. */ + + if (code == MEM) + find_reloads_address (GET_MODE (recog_operand[i]), + recog_operand_loc[i], + XEXP (recog_operand[i], 0), + &XEXP (recog_operand[i], 0), + i, RELOAD_OTHER, ind_levels); + if (code == SUBREG) + recog_operand[i] = *recog_operand_loc[i] + = find_reloads_toplev (recog_operand[i], i, RELOAD_OTHER, + ind_levels, is_set_dest); + if (code == REG) + { + register int regno = REGNO (recog_operand[i]); + if (reg_equiv_constant[regno] != 0 && !is_set_dest) + recog_operand[i] = *recog_operand_loc[i] + = reg_equiv_constant[regno]; +#if 0 /* This might screw code in reload1.c to delete prior output-reload + that feeds this insn. */ + if (reg_equiv_mem[regno] != 0) + recog_operand[i] = *recog_operand_loc[i] + = reg_equiv_mem[regno]; +#endif + } + } + + /* Perhaps an output reload can be combined with another + to reduce needs by one. */ + if (!goal_earlyclobber) + combine_reloads (); +#endif /* no REGISTER_CONSTRAINTS */ +} + +/* Return 1 if alternative number ALTNUM in constraint-string CONSTRAINT + accepts a memory operand with constant address. */ + +static int +alternative_allows_memconst (constraint, altnum) + char *constraint; + int altnum; +{ + register int c; + /* Skip alternatives before the one requested. */ + while (altnum > 0) + { + while (*constraint++ != ','); + altnum--; + } + /* Scan the requested alternative for 'm' or 'o'. + If one of them is present, this alternative accepts memory constants. */ + while ((c = *constraint++) && c != ',' && c != '#') + if (c == 'm' || c == 'o') + return 1; + return 0; +} + +/* Scan X for memory references and scan the addresses for reloading. + Also checks for references to "constant" regs that we want to eliminate + and replaces them with the values they stand for. + We may alter X destructively if it contains a reference to such. + If X is just a constant reg, we return the equivalent value + instead of X. + + IND_LEVELS says how many levels of indirect addressing this machine + supports. + + OPNUM and TYPE identify the purpose of the reload. + + IS_SET_DEST is true if X is the destination of a SET, which is not + appropriate to be replaced by a constant. */ + +static rtx +find_reloads_toplev (x, opnum, type, ind_levels, is_set_dest) + rtx x; + int opnum; + enum reload_type type; + int ind_levels; + int is_set_dest; +{ + register RTX_CODE code = GET_CODE (x); + + register char *fmt = GET_RTX_FORMAT (code); + register int i; + + if (code == REG) + { + /* This code is duplicated for speed in find_reloads. */ + register int regno = REGNO (x); + if (reg_equiv_constant[regno] != 0 && !is_set_dest) + x = reg_equiv_constant[regno]; +#if 0 +/* This creates (subreg (mem...)) which would cause an unnecessary + reload of the mem. */ + else if (reg_equiv_mem[regno] != 0) + x = reg_equiv_mem[regno]; +#endif + else if (reg_equiv_address[regno] != 0) + { + /* If reg_equiv_address varies, it may be shared, so copy it. */ + rtx addr = reg_equiv_address[regno]; + + if (rtx_varies_p (addr)) + addr = copy_rtx (addr); + + x = gen_rtx (MEM, GET_MODE (x), addr); + RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (regno_reg_rtx[regno]); + find_reloads_address (GET_MODE (x), NULL_PTR, + XEXP (x, 0), + &XEXP (x, 0), opnum, type, ind_levels); + } + return x; + } + if (code == MEM) + { + rtx tem = x; + find_reloads_address (GET_MODE (x), &tem, XEXP (x, 0), &XEXP (x, 0), + opnum, type, ind_levels); + return tem; + } + + if (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG) + { + /* Check for SUBREG containing a REG that's equivalent to a constant. + If the constant has a known value, truncate it right now. + Similarly if we are extracting a single-word of a multi-word + constant. If the constant is symbolic, allow it to be substituted + normally. push_reload will strip the subreg later. If the + constant is VOIDmode, abort because we will lose the mode of + the register (this should never happen because one of the cases + above should handle it). */ + + register int regno = REGNO (SUBREG_REG (x)); + rtx tem; + + if (subreg_lowpart_p (x) + && regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0 + && reg_equiv_constant[regno] != 0 + && (tem = gen_lowpart_common (GET_MODE (x), + reg_equiv_constant[regno])) != 0) + return tem; + + if (GET_MODE_BITSIZE (GET_MODE (x)) == BITS_PER_WORD + && regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0 + && reg_equiv_constant[regno] != 0 + && (tem = operand_subword (reg_equiv_constant[regno], + SUBREG_WORD (x), 0, + GET_MODE (SUBREG_REG (x)))) != 0) + return tem; + + if (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0 + && reg_equiv_constant[regno] != 0 + && GET_MODE (reg_equiv_constant[regno]) == VOIDmode) + abort (); + + /* If the subreg contains a reg that will be converted to a mem, + convert the subreg to a narrower memref now. + Otherwise, we would get (subreg (mem ...) ...), + which would force reload of the mem. + + We also need to do this if there is an equivalent MEM that is + not offsettable. In that case, alter_subreg would produce an + invalid address on big-endian machines. + + For machines that extend byte loads, we must not reload using + a wider mode if we have a paradoxical SUBREG. find_reloads will + force a reload in that case. So we should not do anything here. */ + + else if (regno >= FIRST_PSEUDO_REGISTER +#if defined(BYTE_LOADS_ZERO_EXTEND) || defined(BYTE_LOADS_SIGN_EXTEND) + && (GET_MODE_SIZE (GET_MODE (x)) + <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) +#endif + && (reg_equiv_address[regno] != 0 + || (reg_equiv_mem[regno] != 0 + && (! strict_memory_address_p (GET_MODE (x), + XEXP (reg_equiv_mem[regno], 0)) + || ! offsettable_memref_p (reg_equiv_mem[regno]))))) + { + int offset = SUBREG_WORD (x) * UNITS_PER_WORD; + rtx addr = (reg_equiv_address[regno] ? reg_equiv_address[regno] + : XEXP (reg_equiv_mem[regno], 0)); +#if BYTES_BIG_ENDIAN + int size; + size = GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))); + offset += MIN (size, UNITS_PER_WORD); + size = GET_MODE_SIZE (GET_MODE (x)); + offset -= MIN (size, UNITS_PER_WORD); +#endif + addr = plus_constant (addr, offset); + x = gen_rtx (MEM, GET_MODE (x), addr); + RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (regno_reg_rtx[regno]); + find_reloads_address (GET_MODE (x), NULL_PTR, + XEXP (x, 0), + &XEXP (x, 0), opnum, type, ind_levels); + } + + } + + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + XEXP (x, i) = find_reloads_toplev (XEXP (x, i), opnum, type, + ind_levels, is_set_dest); + } + return x; +} + +/* Return a mem ref for the memory equivalent of reg REGNO. + This mem ref is not shared with anything. */ + +static rtx +make_memloc (ad, regno) + rtx ad; + int regno; +{ + register int i; + rtx tem = reg_equiv_address[regno]; + +#if 0 /* We cannot safely reuse a memloc made here; + if the pseudo appears twice, and its mem needs a reload, + it gets two separate reloads assigned, but it only + gets substituted with the second of them; + then it can get used before that reload reg gets loaded up. */ + for (i = 0; i < n_memlocs; i++) + if (rtx_equal_p (tem, XEXP (memlocs[i], 0))) + return memlocs[i]; +#endif + + /* If TEM might contain a pseudo, we must copy it to avoid + modifying it when we do the substitution for the reload. */ + if (rtx_varies_p (tem)) + tem = copy_rtx (tem); + + tem = gen_rtx (MEM, GET_MODE (ad), tem); + RTX_UNCHANGING_P (tem) = RTX_UNCHANGING_P (regno_reg_rtx[regno]); + memlocs[n_memlocs++] = tem; + return tem; +} + +/* Record all reloads needed for handling memory address AD + which appears in *LOC in a memory reference to mode MODE + which itself is found in location *MEMREFLOC. + Note that we take shortcuts assuming that no multi-reg machine mode + occurs as part of an address. + + OPNUM and TYPE specify the purpose of this reload. + + IND_LEVELS says how many levels of indirect addressing this machine + supports. + + Value is nonzero if this address is reloaded or replaced as a whole. + This is interesting to the caller if the address is an autoincrement. + + Note that there is no verification that the address will be valid after + this routine does its work. Instead, we rely on the fact that the address + was valid when reload started. So we need only undo things that reload + could have broken. These are wrong register types, pseudos not allocated + to a hard register, and frame pointer elimination. */ + +static int +find_reloads_address (mode, memrefloc, ad, loc, opnum, type, ind_levels) + enum machine_mode mode; + rtx *memrefloc; + rtx ad; + rtx *loc; + int opnum; + enum reload_type type; + int ind_levels; +{ + register int regno; + rtx tem; + + /* If the address is a register, see if it is a legitimate address and + reload if not. We first handle the cases where we need not reload + or where we must reload in a non-standard way. */ + + if (GET_CODE (ad) == REG) + { + regno = REGNO (ad); + + if (reg_equiv_constant[regno] != 0 + && strict_memory_address_p (mode, reg_equiv_constant[regno])) + { + *loc = ad = reg_equiv_constant[regno]; + return 1; + } + + else if (reg_equiv_address[regno] != 0) + { + tem = make_memloc (ad, regno); + find_reloads_address (GET_MODE (tem), NULL_PTR, XEXP (tem, 0), + &XEXP (tem, 0), opnum, type, ind_levels); + push_reload (tem, NULL_RTX, loc, NULL_PTR, BASE_REG_CLASS, + GET_MODE (ad), VOIDmode, 0, 0, + opnum, type); + return 1; + } + + /* We can avoid a reload if the register's equivalent memory expression + is valid as an indirect memory address. */ + + else if (reg_equiv_mem[regno] != 0 && ind_levels > 0 + && strict_memory_address_p (mode, reg_equiv_mem[regno])) + return 0; + + /* The only remaining case where we can avoid a reload is if this is a + hard register that is valid as a base register and which is not the + subject of a CLOBBER in this insn. */ + + else if (regno < FIRST_PSEUDO_REGISTER && REGNO_OK_FOR_BASE_P (regno) + && ! regno_clobbered_p (regno, this_insn)) + return 0; + + /* If we do not have one of the cases above, we must do the reload. */ + push_reload (ad, NULL_RTX, loc, NULL_PTR, BASE_REG_CLASS, + GET_MODE (ad), VOIDmode, 0, 0, opnum, type); + return 1; + } + + if (strict_memory_address_p (mode, ad)) + { + /* The address appears valid, so reloads are not needed. + But the address may contain an eliminable register. + This can happen because a machine with indirect addressing + may consider a pseudo register by itself a valid address even when + it has failed to get a hard reg. + So do a tree-walk to find and eliminate all such regs. */ + + /* But first quickly dispose of a common case. */ + if (GET_CODE (ad) == PLUS + && GET_CODE (XEXP (ad, 1)) == CONST_INT + && GET_CODE (XEXP (ad, 0)) == REG + && reg_equiv_constant[REGNO (XEXP (ad, 0))] == 0) + return 0; + + subst_reg_equivs_changed = 0; + *loc = subst_reg_equivs (ad); + + if (! subst_reg_equivs_changed) + return 0; + + /* Check result for validity after substitution. */ + if (strict_memory_address_p (mode, ad)) + return 0; + } + + /* The address is not valid. We have to figure out why. One possibility + is that it is itself a MEM. This can happen when the frame pointer is + being eliminated, a pseudo is not allocated to a hard register, and the + offset between the frame and stack pointers is not its initial value. + In that case the pseudo will have been replaced by a MEM referring to + the stack pointer. */ + if (GET_CODE (ad) == MEM) + { + /* First ensure that the address in this MEM is valid. Then, unless + indirect addresses are valid, reload the MEM into a register. */ + tem = ad; + find_reloads_address (GET_MODE (ad), &tem, XEXP (ad, 0), &XEXP (ad, 0), + opnum, type, ind_levels == 0 ? 0 : ind_levels - 1); + + /* If tem was changed, then we must create a new memory reference to + hold it and store it back into memrefloc. */ + if (tem != ad && memrefloc) + { + *memrefloc = copy_rtx (*memrefloc); + copy_replacements (tem, XEXP (*memrefloc, 0)); + loc = &XEXP (*memrefloc, 0); + } + + /* Check similar cases as for indirect addresses as above except + that we can allow pseudos and a MEM since they should have been + taken care of above. */ + + if (ind_levels == 0 + || (GET_CODE (XEXP (tem, 0)) == SYMBOL_REF && ! indirect_symref_ok) + || GET_CODE (XEXP (tem, 0)) == MEM + || ! (GET_CODE (XEXP (tem, 0)) == REG + || (GET_CODE (XEXP (tem, 0)) == PLUS + && GET_CODE (XEXP (XEXP (tem, 0), 0)) == REG + && GET_CODE (XEXP (XEXP (tem, 0), 1)) == CONST_INT))) + { + /* Must use TEM here, not AD, since it is the one that will + have any subexpressions reloaded, if needed. */ + push_reload (tem, NULL_RTX, loc, NULL_PTR, + BASE_REG_CLASS, GET_MODE (tem), VOIDmode, 0, + 0, opnum, type); + return 1; + } + else + return 0; + } + + /* If we have address of a stack slot but it's not valid + (displacement is too large), compute the sum in a register. */ + else if (GET_CODE (ad) == PLUS + && (XEXP (ad, 0) == frame_pointer_rtx +#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + || XEXP (ad, 0) == arg_pointer_rtx +#endif + || XEXP (ad, 0) == stack_pointer_rtx) + && GET_CODE (XEXP (ad, 1)) == CONST_INT) + { + /* Unshare the MEM rtx so we can safely alter it. */ + if (memrefloc) + { + rtx oldref = *memrefloc; + *memrefloc = copy_rtx (*memrefloc); + loc = &XEXP (*memrefloc, 0); + } + if (double_reg_address_ok) + { + /* Unshare the sum as well. */ + *loc = ad = copy_rtx (ad); + /* Reload the displacement into an index reg. + We assume the frame pointer or arg pointer is a base reg. */ + find_reloads_address_part (XEXP (ad, 1), &XEXP (ad, 1), + INDEX_REG_CLASS, GET_MODE (ad), opnum, + type, ind_levels); + } + else + { + /* If the sum of two regs is not necessarily valid, + reload the sum into a base reg. + That will at least work. */ + find_reloads_address_part (ad, loc, BASE_REG_CLASS, Pmode, + opnum, type, ind_levels); + } + return 1; + } + + /* If we have an indexed stack slot, there are three possible reasons why + it might be invalid: The index might need to be reloaded, the address + might have been made by frame pointer elimination and hence have a + constant out of range, or both reasons might apply. + + We can easily check for an index needing reload, but even if that is the + case, we might also have an invalid constant. To avoid making the + conservative assumption and requiring two reloads, we see if this address + is valid when not interpreted strictly. If it is, the only problem is + that the index needs a reload and find_reloads_address_1 will take care + of it. + + There is still a case when we might generate an extra reload, + however. In certain cases eliminate_regs will return a MEM for a REG + (see the code there for details). In those cases, memory_address_p + applied to our address will return 0 so we will think that our offset + must be too large. But it might indeed be valid and the only problem + is that a MEM is present where a REG should be. This case should be + very rare and there doesn't seem to be any way to avoid it. + + If we decide to do something here, it must be that + `double_reg_address_ok' is true and that this address rtl was made by + eliminate_regs. We generate a reload of the fp/sp/ap + constant and + rework the sum so that the reload register will be added to the index. + This is safe because we know the address isn't shared. + + We check for fp/ap/sp as both the first and second operand of the + innermost PLUS. */ + + else if (GET_CODE (ad) == PLUS && GET_CODE (XEXP (ad, 1)) == CONST_INT + && GET_CODE (XEXP (ad, 0)) == PLUS + && (XEXP (XEXP (ad, 0), 0) == frame_pointer_rtx +#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + || XEXP (XEXP (ad, 0), 0) == arg_pointer_rtx +#endif + || XEXP (XEXP (ad, 0), 0) == stack_pointer_rtx) + && ! memory_address_p (mode, ad)) + { + *loc = ad = gen_rtx (PLUS, GET_MODE (ad), + plus_constant (XEXP (XEXP (ad, 0), 0), + INTVAL (XEXP (ad, 1))), + XEXP (XEXP (ad, 0), 1)); + find_reloads_address_part (XEXP (ad, 0), &XEXP (ad, 0), BASE_REG_CLASS, + GET_MODE (ad), opnum, type, ind_levels); + find_reloads_address_1 (XEXP (ad, 1), 1, &XEXP (ad, 1), opnum, type, 0); + + return 1; + } + + else if (GET_CODE (ad) == PLUS && GET_CODE (XEXP (ad, 1)) == CONST_INT + && GET_CODE (XEXP (ad, 0)) == PLUS + && (XEXP (XEXP (ad, 0), 1) == frame_pointer_rtx +#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + || XEXP (XEXP (ad, 0), 1) == arg_pointer_rtx +#endif + || XEXP (XEXP (ad, 0), 1) == stack_pointer_rtx) + && ! memory_address_p (mode, ad)) + { + *loc = ad = gen_rtx (PLUS, GET_MODE (ad), + plus_constant (XEXP (XEXP (ad, 0), 1), + INTVAL (XEXP (ad, 1))), + XEXP (XEXP (ad, 0), 0)); + find_reloads_address_part (XEXP (ad, 0), &XEXP (ad, 0), BASE_REG_CLASS, + GET_MODE (ad), opnum, type, ind_levels); + find_reloads_address_1 (XEXP (ad, 1), 1, &XEXP (ad, 1), opnum, type, 0); + + return 1; + } + + /* See if address becomes valid when an eliminable register + in a sum is replaced. */ + + tem = ad; + if (GET_CODE (ad) == PLUS) + tem = subst_indexed_address (ad); + if (tem != ad && strict_memory_address_p (mode, tem)) + { + /* Ok, we win that way. Replace any additional eliminable + registers. */ + + subst_reg_equivs_changed = 0; + tem = subst_reg_equivs (tem); + + /* Make sure that didn't make the address invalid again. */ + + if (! subst_reg_equivs_changed || strict_memory_address_p (mode, tem)) + { + *loc = tem; + return 0; + } + } + + /* If constants aren't valid addresses, reload the constant address + into a register. */ + if (CONSTANT_P (ad) && ! strict_memory_address_p (mode, ad)) + { + /* If AD is in address in the constant pool, the MEM rtx may be shared. + Unshare it so we can safely alter it. */ + if (memrefloc && GET_CODE (ad) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (ad)) + { + rtx oldref = *memrefloc; + *memrefloc = copy_rtx (*memrefloc); + loc = &XEXP (*memrefloc, 0); + } + + find_reloads_address_part (ad, loc, BASE_REG_CLASS, Pmode, opnum, type, + ind_levels); + return 1; + } + + return find_reloads_address_1 (ad, 0, loc, opnum, type, ind_levels); +} + +/* Find all pseudo regs appearing in AD + that are eliminable in favor of equivalent values + and do not have hard regs; replace them by their equivalents. */ + +static rtx +subst_reg_equivs (ad) + rtx ad; +{ + register RTX_CODE code = GET_CODE (ad); + register int i; + register char *fmt; + + switch (code) + { + case HIGH: + case CONST_INT: + case CONST: + case CONST_DOUBLE: + case SYMBOL_REF: + case LABEL_REF: + case PC: + case CC0: + return ad; + + case REG: + { + register int regno = REGNO (ad); + + if (reg_equiv_constant[regno] != 0) + { + subst_reg_equivs_changed = 1; + return reg_equiv_constant[regno]; + } + } + return ad; + + case PLUS: + /* Quickly dispose of a common case. */ + if (XEXP (ad, 0) == frame_pointer_rtx + && GET_CODE (XEXP (ad, 1)) == CONST_INT) + return ad; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + XEXP (ad, i) = subst_reg_equivs (XEXP (ad, i)); + return ad; +} + +/* Compute the sum of X and Y, making canonicalizations assumed in an + address, namely: sum constant integers, surround the sum of two + constants with a CONST, put the constant as the second operand, and + group the constant on the outermost sum. + + This routine assumes both inputs are already in canonical form. */ + +rtx +form_sum (x, y) + rtx x, y; +{ + rtx tem; + enum machine_mode mode = GET_MODE (x); + + if (mode == VOIDmode) + mode = GET_MODE (y); + + if (mode == VOIDmode) + mode = Pmode; + + if (GET_CODE (x) == CONST_INT) + return plus_constant (y, INTVAL (x)); + else if (GET_CODE (y) == CONST_INT) + return plus_constant (x, INTVAL (y)); + else if (CONSTANT_P (x)) + tem = x, x = y, y = tem; + + if (GET_CODE (x) == PLUS && CONSTANT_P (XEXP (x, 1))) + return form_sum (XEXP (x, 0), form_sum (XEXP (x, 1), y)); + + /* Note that if the operands of Y are specified in the opposite + order in the recursive calls below, infinite recursion will occur. */ + if (GET_CODE (y) == PLUS && CONSTANT_P (XEXP (y, 1))) + return form_sum (form_sum (x, XEXP (y, 0)), XEXP (y, 1)); + + /* If both constant, encapsulate sum. Otherwise, just form sum. A + constant will have been placed second. */ + if (CONSTANT_P (x) && CONSTANT_P (y)) + { + if (GET_CODE (x) == CONST) + x = XEXP (x, 0); + if (GET_CODE (y) == CONST) + y = XEXP (y, 0); + + return gen_rtx (CONST, VOIDmode, gen_rtx (PLUS, mode, x, y)); + } + + return gen_rtx (PLUS, mode, x, y); +} + +/* If ADDR is a sum containing a pseudo register that should be + replaced with a constant (from reg_equiv_constant), + return the result of doing so, and also apply the associative + law so that the result is more likely to be a valid address. + (But it is not guaranteed to be one.) + + Note that at most one register is replaced, even if more are + replaceable. Also, we try to put the result into a canonical form + so it is more likely to be a valid address. + + In all other cases, return ADDR. */ + +static rtx +subst_indexed_address (addr) + rtx addr; +{ + rtx op0 = 0, op1 = 0, op2 = 0; + rtx tem; + int regno; + + if (GET_CODE (addr) == PLUS) + { + /* Try to find a register to replace. */ + op0 = XEXP (addr, 0), op1 = XEXP (addr, 1), op2 = 0; + if (GET_CODE (op0) == REG + && (regno = REGNO (op0)) >= FIRST_PSEUDO_REGISTER + && reg_renumber[regno] < 0 + && reg_equiv_constant[regno] != 0) + op0 = reg_equiv_constant[regno]; + else if (GET_CODE (op1) == REG + && (regno = REGNO (op1)) >= FIRST_PSEUDO_REGISTER + && reg_renumber[regno] < 0 + && reg_equiv_constant[regno] != 0) + op1 = reg_equiv_constant[regno]; + else if (GET_CODE (op0) == PLUS + && (tem = subst_indexed_address (op0)) != op0) + op0 = tem; + else if (GET_CODE (op1) == PLUS + && (tem = subst_indexed_address (op1)) != op1) + op1 = tem; + else + return addr; + + /* Pick out up to three things to add. */ + if (GET_CODE (op1) == PLUS) + op2 = XEXP (op1, 1), op1 = XEXP (op1, 0); + else if (GET_CODE (op0) == PLUS) + op2 = op1, op1 = XEXP (op0, 1), op0 = XEXP (op0, 0); + + /* Compute the sum. */ + if (op2 != 0) + op1 = form_sum (op1, op2); + if (op1 != 0) + op0 = form_sum (op0, op1); + + return op0; + } + return addr; +} + +/* Record the pseudo registers we must reload into hard registers + in a subexpression of a would-be memory address, X. + (This function is not called if the address we find is strictly valid.) + CONTEXT = 1 means we are considering regs as index regs, + = 0 means we are considering them as base regs. + + OPNUM and TYPE specify the purpose of any reloads made. + + IND_LEVELS says how many levels of indirect addressing are + supported at this point in the address. + + We return nonzero if X, as a whole, is reloaded or replaced. */ + +/* Note that we take shortcuts assuming that no multi-reg machine mode + occurs as part of an address. + Also, this is not fully machine-customizable; it works for machines + such as vaxes and 68000's and 32000's, but other possible machines + could have addressing modes that this does not handle right. */ + +static int +find_reloads_address_1 (x, context, loc, opnum, type, ind_levels) + rtx x; + int context; + rtx *loc; + int opnum; + enum reload_type type; + int ind_levels; +{ + register RTX_CODE code = GET_CODE (x); + + if (code == PLUS) + { + register rtx op0 = XEXP (x, 0); + register rtx op1 = XEXP (x, 1); + register RTX_CODE code0 = GET_CODE (op0); + register RTX_CODE code1 = GET_CODE (op1); + if (code0 == MULT || code0 == SIGN_EXTEND || code1 == MEM) + { + find_reloads_address_1 (op0, 1, &XEXP (x, 0), opnum, type, + ind_levels); + find_reloads_address_1 (op1, 0, &XEXP (x, 1), opnum, type, + ind_levels); + } + else if (code1 == MULT || code1 == SIGN_EXTEND || code0 == MEM) + { + find_reloads_address_1 (op0, 0, &XEXP (x, 0), opnum, type, + ind_levels); + find_reloads_address_1 (op1, 1, &XEXP (x, 1), opnum, type, + ind_levels); + } + else if (code0 == CONST_INT || code0 == CONST + || code0 == SYMBOL_REF || code0 == LABEL_REF) + find_reloads_address_1 (op1, 0, &XEXP (x, 1), opnum, type, ind_levels); + else if (code1 == CONST_INT || code1 == CONST + || code1 == SYMBOL_REF || code1 == LABEL_REF) + find_reloads_address_1 (op0, 0, &XEXP (x, 0), opnum, type, ind_levels); + else if (code0 == REG && code1 == REG) + { + if (REG_OK_FOR_INDEX_P (op0) + && REG_OK_FOR_BASE_P (op1)) + return 0; + else if (REG_OK_FOR_INDEX_P (op1) + && REG_OK_FOR_BASE_P (op0)) + return 0; + else if (REG_OK_FOR_BASE_P (op1)) + find_reloads_address_1 (op0, 1, &XEXP (x, 0), opnum, type, + ind_levels); + else if (REG_OK_FOR_BASE_P (op0)) + find_reloads_address_1 (op1, 1, &XEXP (x, 1), opnum, type, + ind_levels); + else if (REG_OK_FOR_INDEX_P (op1)) + find_reloads_address_1 (op0, 0, &XEXP (x, 0), opnum, type, + ind_levels); + else if (REG_OK_FOR_INDEX_P (op0)) + find_reloads_address_1 (op1, 0, &XEXP (x, 1), opnum, type, + ind_levels); + else + { + find_reloads_address_1 (op0, 1, &XEXP (x, 0), opnum, type, + ind_levels); + find_reloads_address_1 (op1, 0, &XEXP (x, 1), opnum, type, + ind_levels); + } + } + else if (code0 == REG) + { + find_reloads_address_1 (op0, 1, &XEXP (x, 0), opnum, type, + ind_levels); + find_reloads_address_1 (op1, 0, &XEXP (x, 1), opnum, type, + ind_levels); + } + else if (code1 == REG) + { + find_reloads_address_1 (op1, 1, &XEXP (x, 1), opnum, type, + ind_levels); + find_reloads_address_1 (op0, 0, &XEXP (x, 0), opnum, type, + ind_levels); + } + } + else if (code == POST_INC || code == POST_DEC + || code == PRE_INC || code == PRE_DEC) + { + if (GET_CODE (XEXP (x, 0)) == REG) + { + register int regno = REGNO (XEXP (x, 0)); + int value = 0; + rtx x_orig = x; + + /* A register that is incremented cannot be constant! */ + if (regno >= FIRST_PSEUDO_REGISTER + && reg_equiv_constant[regno] != 0) + abort (); + + /* Handle a register that is equivalent to a memory location + which cannot be addressed directly. */ + if (reg_equiv_address[regno] != 0) + { + rtx tem = make_memloc (XEXP (x, 0), regno); + /* First reload the memory location's address. */ + find_reloads_address (GET_MODE (tem), 0, XEXP (tem, 0), + &XEXP (tem, 0), opnum, type, ind_levels); + /* Put this inside a new increment-expression. */ + x = gen_rtx (GET_CODE (x), GET_MODE (x), tem); + /* Proceed to reload that, as if it contained a register. */ + } + + /* If we have a hard register that is ok as an index, + don't make a reload. If an autoincrement of a nice register + isn't "valid", it must be that no autoincrement is "valid". + If that is true and something made an autoincrement anyway, + this must be a special context where one is allowed. + (For example, a "push" instruction.) + We can't improve this address, so leave it alone. */ + + /* Otherwise, reload the autoincrement into a suitable hard reg + and record how much to increment by. */ + + if (reg_renumber[regno] >= 0) + regno = reg_renumber[regno]; + if ((regno >= FIRST_PSEUDO_REGISTER + || !(context ? REGNO_OK_FOR_INDEX_P (regno) + : REGNO_OK_FOR_BASE_P (regno)))) + { + register rtx link; + + int reloadnum + = push_reload (x, NULL_RTX, loc, NULL_PTR, + context ? INDEX_REG_CLASS : BASE_REG_CLASS, + GET_MODE (x), GET_MODE (x), VOIDmode, 0, + opnum, type); + reload_inc[reloadnum] + = find_inc_amount (PATTERN (this_insn), XEXP (x_orig, 0)); + + value = 1; + +#ifdef AUTO_INC_DEC + /* Update the REG_INC notes. */ + + for (link = REG_NOTES (this_insn); + link; link = XEXP (link, 1)) + if (REG_NOTE_KIND (link) == REG_INC + && REGNO (XEXP (link, 0)) == REGNO (XEXP (x_orig, 0))) + push_replacement (&XEXP (link, 0), reloadnum, VOIDmode); +#endif + } + return value; + } + else if (GET_CODE (XEXP (x, 0)) == MEM) + { + /* This is probably the result of a substitution, by eliminate_regs, + of an equivalent address for a pseudo that was not allocated to a + hard register. Verify that the specified address is valid and + reload it into a register. */ + rtx tem = XEXP (x, 0); + register rtx link; + int reloadnum; + + /* Since we know we are going to reload this item, don't decrement + for the indirection level. + + Note that this is actually conservative: it would be slightly + more efficient to use the value of SPILL_INDIRECT_LEVELS from + reload1.c here. */ + find_reloads_address (GET_MODE (x), &XEXP (x, 0), + XEXP (XEXP (x, 0), 0), &XEXP (XEXP (x, 0), 0), + opnum, type, ind_levels); + + reloadnum = push_reload (x, NULL_RTX, loc, NULL_PTR, + context ? INDEX_REG_CLASS : BASE_REG_CLASS, + GET_MODE (x), VOIDmode, 0, 0, opnum, type); + reload_inc[reloadnum] + = find_inc_amount (PATTERN (this_insn), XEXP (x, 0)); + + link = FIND_REG_INC_NOTE (this_insn, tem); + if (link != 0) + push_replacement (&XEXP (link, 0), reloadnum, VOIDmode); + + return 1; + } + } + else if (code == MEM) + { + /* This is probably the result of a substitution, by eliminate_regs, + of an equivalent address for a pseudo that was not allocated to a + hard register. Verify that the specified address is valid and reload + it into a register. + + Since we know we are going to reload this item, don't decrement + for the indirection level. + + Note that this is actually conservative: it would be slightly more + efficient to use the value of SPILL_INDIRECT_LEVELS from + reload1.c here. */ + + find_reloads_address (GET_MODE (x), loc, XEXP (x, 0), &XEXP (x, 0), + opnum, type, ind_levels); + + push_reload (*loc, NULL_RTX, loc, NULL_PTR, + context ? INDEX_REG_CLASS : BASE_REG_CLASS, + GET_MODE (x), VOIDmode, 0, 0, opnum, type); + return 1; + } + else if (code == REG) + { + register int regno = REGNO (x); + + if (reg_equiv_constant[regno] != 0) + { + find_reloads_address_part (reg_equiv_constant[regno], loc, + (context ? INDEX_REG_CLASS + : BASE_REG_CLASS), + GET_MODE (x), opnum, type, ind_levels); + return 1; + } + +#if 0 /* This might screw code in reload1.c to delete prior output-reload + that feeds this insn. */ + if (reg_equiv_mem[regno] != 0) + { + push_reload (reg_equiv_mem[regno], NULL_RTX, loc, NULL_PTR, + context ? INDEX_REG_CLASS : BASE_REG_CLASS, + GET_MODE (x), VOIDmode, 0, 0, opnum, type); + return 1; + } +#endif + if (reg_equiv_address[regno] != 0) + { + x = make_memloc (x, regno); + find_reloads_address (GET_MODE (x), 0, XEXP (x, 0), &XEXP (x, 0), + opnum, type, ind_levels); + } + + if (reg_renumber[regno] >= 0) + regno = reg_renumber[regno]; + if ((regno >= FIRST_PSEUDO_REGISTER + || !(context ? REGNO_OK_FOR_INDEX_P (regno) + : REGNO_OK_FOR_BASE_P (regno)))) + { + push_reload (x, NULL_RTX, loc, NULL_PTR, + context ? INDEX_REG_CLASS : BASE_REG_CLASS, + GET_MODE (x), VOIDmode, 0, 0, opnum, type); + return 1; + } + + /* If a register appearing in an address is the subject of a CLOBBER + in this insn, reload it into some other register to be safe. + The CLOBBER is supposed to make the register unavailable + from before this insn to after it. */ + if (regno_clobbered_p (regno, this_insn)) + { + push_reload (x, NULL_RTX, loc, NULL_PTR, + context ? INDEX_REG_CLASS : BASE_REG_CLASS, + GET_MODE (x), VOIDmode, 0, 0, opnum, type); + return 1; + } + } + else + { + register char *fmt = GET_RTX_FORMAT (code); + register int i; + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + find_reloads_address_1 (XEXP (x, i), context, &XEXP (x, i), + opnum, type, ind_levels); + } + } + + return 0; +} + +/* X, which is found at *LOC, is a part of an address that needs to be + reloaded into a register of class CLASS. If X is a constant, or if + X is a PLUS that contains a constant, check that the constant is a + legitimate operand and that we are supposed to be able to load + it into the register. + + If not, force the constant into memory and reload the MEM instead. + + MODE is the mode to use, in case X is an integer constant. + + OPNUM and TYPE describe the purpose of any reloads made. + + IND_LEVELS says how many levels of indirect addressing this machine + supports. */ + +static void +find_reloads_address_part (x, loc, class, mode, opnum, type, ind_levels) + rtx x; + rtx *loc; + enum reg_class class; + enum machine_mode mode; + int opnum; + enum reload_type type; + int ind_levels; +{ + if (CONSTANT_P (x) + && (! LEGITIMATE_CONSTANT_P (x) + || PREFERRED_RELOAD_CLASS (x, class) == NO_REGS)) + { + rtx tem = x = force_const_mem (mode, x); + find_reloads_address (mode, &tem, XEXP (tem, 0), &XEXP (tem, 0), + opnum, type, ind_levels); + } + + else if (GET_CODE (x) == PLUS + && CONSTANT_P (XEXP (x, 1)) + && (! LEGITIMATE_CONSTANT_P (XEXP (x, 1)) + || PREFERRED_RELOAD_CLASS (XEXP (x, 1), class) == NO_REGS)) + { + rtx tem = force_const_mem (GET_MODE (x), XEXP (x, 1)); + + x = gen_rtx (PLUS, GET_MODE (x), XEXP (x, 0), tem); + find_reloads_address (mode, &tem, XEXP (tem, 0), &XEXP (tem, 0), + opnum, type, ind_levels); + } + + push_reload (x, NULL_RTX, loc, NULL_PTR, class, + mode, VOIDmode, 0, 0, opnum, type); +} + +/* Substitute into the current INSN the registers into which we have reloaded + the things that need reloading. The array `replacements' + says contains the locations of all pointers that must be changed + and says what to replace them with. + + Return the rtx that X translates into; usually X, but modified. */ + +void +subst_reloads () +{ + register int i; + + for (i = 0; i < n_replacements; i++) + { + register struct replacement *r = &replacements[i]; + register rtx reloadreg = reload_reg_rtx[r->what]; + if (reloadreg) + { + /* Encapsulate RELOADREG so its machine mode matches what + used to be there. */ + if (GET_MODE (reloadreg) != r->mode && r->mode != VOIDmode) + reloadreg = gen_lowpart_common (r->mode, reloadreg); + + /* If we are putting this into a SUBREG and RELOADREG is a + SUBREG, we would be making nested SUBREGs, so we have to fix + this up. Note that r->where == &SUBREG_REG (*r->subreg_loc). */ + + if (r->subreg_loc != 0 && GET_CODE (reloadreg) == SUBREG) + { + if (GET_MODE (*r->subreg_loc) + == GET_MODE (SUBREG_REG (reloadreg))) + *r->subreg_loc = SUBREG_REG (reloadreg); + else + { + *r->where = SUBREG_REG (reloadreg); + SUBREG_WORD (*r->subreg_loc) += SUBREG_WORD (reloadreg); + } + } + else + *r->where = reloadreg; + } + /* If reload got no reg and isn't optional, something's wrong. */ + else if (! reload_optional[r->what]) + abort (); + } +} + +/* Make a copy of any replacements being done into X and move those copies + to locations in Y, a copy of X. We only look at the highest level of + the RTL. */ + +void +copy_replacements (x, y) + rtx x; + rtx y; +{ + int i, j; + enum rtx_code code = GET_CODE (x); + char *fmt = GET_RTX_FORMAT (code); + struct replacement *r; + + /* We can't support X being a SUBREG because we might then need to know its + location if something inside it was replaced. */ + if (code == SUBREG) + abort (); + + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + for (j = 0; j < n_replacements; j++) + { + if (replacements[j].subreg_loc == &XEXP (x, i)) + { + r = &replacements[n_replacements++]; + r->where = replacements[j].where; + r->subreg_loc = &XEXP (y, i); + r->what = replacements[j].what; + r->mode = replacements[j].mode; + } + else if (replacements[j].where == &XEXP (x, i)) + { + r = &replacements[n_replacements++]; + r->where = &XEXP (y, i); + r->subreg_loc = 0; + r->what = replacements[j].what; + r->mode = replacements[j].mode; + } + } +} + +/* If LOC was scheduled to be replaced by something, return the replacement. + Otherwise, return *LOC. */ + +rtx +find_replacement (loc) + rtx *loc; +{ + struct replacement *r; + + for (r = &replacements[0]; r < &replacements[n_replacements]; r++) + { + rtx reloadreg = reload_reg_rtx[r->what]; + + if (reloadreg && r->where == loc) + { + if (r->mode != VOIDmode && GET_MODE (reloadreg) != r->mode) + reloadreg = gen_rtx (REG, r->mode, REGNO (reloadreg)); + + return reloadreg; + } + else if (reloadreg && r->subreg_loc == loc) + { + /* RELOADREG must be either a REG or a SUBREG. + + ??? Is it actually still ever a SUBREG? If so, why? */ + + if (GET_CODE (reloadreg) == REG) + return gen_rtx (REG, GET_MODE (*loc), + REGNO (reloadreg) + SUBREG_WORD (*loc)); + else if (GET_MODE (reloadreg) == GET_MODE (*loc)) + return reloadreg; + else + return gen_rtx (SUBREG, GET_MODE (*loc), SUBREG_REG (reloadreg), + SUBREG_WORD (reloadreg) + SUBREG_WORD (*loc)); + } + } + + return *loc; +} + +/* Return nonzero if register in range [REGNO, ENDREGNO) + appears either explicitly or implicitly in X + other than being stored into. + + References contained within the substructure at LOC do not count. + LOC may be zero, meaning don't ignore anything. + + This is similar to refers_to_regno_p in rtlanal.c except that we + look at equivalences for pseudos that didn't get hard registers. */ + +int +refers_to_regno_for_reload_p (regno, endregno, x, loc) + int regno, endregno; + rtx x; + rtx *loc; +{ + register int i; + register RTX_CODE code; + register char *fmt; + + if (x == 0) + return 0; + + repeat: + code = GET_CODE (x); + + switch (code) + { + case REG: + i = REGNO (x); + + /* If this is a pseudo, a hard register must not have been allocated. + X must therefore either be a constant or be in memory. */ + if (i >= FIRST_PSEUDO_REGISTER) + { + if (reg_equiv_memory_loc[i]) + return refers_to_regno_for_reload_p (regno, endregno, + reg_equiv_memory_loc[i], + NULL_PTR); + + if (reg_equiv_constant[i]) + return 0; + + abort (); + } + + return (endregno > i + && regno < i + (i < FIRST_PSEUDO_REGISTER + ? HARD_REGNO_NREGS (i, GET_MODE (x)) + : 1)); + + case SUBREG: + /* If this is a SUBREG of a hard reg, we can see exactly which + registers are being modified. Otherwise, handle normally. */ + if (GET_CODE (SUBREG_REG (x)) == REG + && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER) + { + int inner_regno = REGNO (SUBREG_REG (x)) + SUBREG_WORD (x); + int inner_endregno + = inner_regno + (inner_regno < FIRST_PSEUDO_REGISTER + ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1); + + return endregno > inner_regno && regno < inner_endregno; + } + break; + + case CLOBBER: + case SET: + if (&SET_DEST (x) != loc + /* Note setting a SUBREG counts as referring to the REG it is in for + a pseudo but not for hard registers since we can + treat each word individually. */ + && ((GET_CODE (SET_DEST (x)) == SUBREG + && loc != &SUBREG_REG (SET_DEST (x)) + && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG + && REGNO (SUBREG_REG (SET_DEST (x))) >= FIRST_PSEUDO_REGISTER + && refers_to_regno_for_reload_p (regno, endregno, + SUBREG_REG (SET_DEST (x)), + loc)) + || (GET_CODE (SET_DEST (x)) != REG + && refers_to_regno_for_reload_p (regno, endregno, + SET_DEST (x), loc)))) + return 1; + + if (code == CLOBBER || loc == &SET_SRC (x)) + return 0; + x = SET_SRC (x); + goto repeat; + } + + /* X does not match, so try its subexpressions. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e' && loc != &XEXP (x, i)) + { + if (i == 0) + { + x = XEXP (x, 0); + goto repeat; + } + else + if (refers_to_regno_for_reload_p (regno, endregno, + XEXP (x, i), loc)) + return 1; + } + else if (fmt[i] == 'E') + { + register int j; + for (j = XVECLEN (x, i) - 1; j >=0; j--) + if (loc != &XVECEXP (x, i, j) + && refers_to_regno_for_reload_p (regno, endregno, + XVECEXP (x, i, j), loc)) + return 1; + } + } + return 0; +} + +/* Nonzero if modifying X will affect IN. If X is a register or a SUBREG, + we check if any register number in X conflicts with the relevant register + numbers. If X is a constant, return 0. If X is a MEM, return 1 iff IN + contains a MEM (we don't bother checking for memory addresses that can't + conflict because we expect this to be a rare case. + + This function is similar to reg_overlap_mention_p in rtlanal.c except + that we look at equivalences for pseudos that didn't get hard registers. */ + +int +reg_overlap_mentioned_for_reload_p (x, in) + rtx x, in; +{ + int regno, endregno; + + if (GET_CODE (x) == SUBREG) + { + regno = REGNO (SUBREG_REG (x)); + if (regno < FIRST_PSEUDO_REGISTER) + regno += SUBREG_WORD (x); + } + else if (GET_CODE (x) == REG) + { + regno = REGNO (x); + + /* If this is a pseudo, it must not have been assigned a hard register. + Therefore, it must either be in memory or be a constant. */ + + if (regno >= FIRST_PSEUDO_REGISTER) + { + if (reg_equiv_memory_loc[regno]) + return refers_to_mem_for_reload_p (in); + else if (reg_equiv_constant[regno]) + return 0; + abort (); + } + } + else if (CONSTANT_P (x)) + return 0; + else if (GET_CODE (x) == MEM) + return refers_to_mem_for_reload_p (in); + else if (GET_CODE (x) == SCRATCH || GET_CODE (x) == PC + || GET_CODE (x) == CC0) + return reg_mentioned_p (x, in); + else + abort (); + + endregno = regno + (regno < FIRST_PSEUDO_REGISTER + ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1); + + return refers_to_regno_for_reload_p (regno, endregno, in, NULL_PTR); +} + +/* Return nonzero if anything in X contains a MEM. Look also for pseudo + registers. */ + +int +refers_to_mem_for_reload_p (x) + rtx x; +{ + char *fmt; + int i; + + if (GET_CODE (x) == MEM) + return 1; + + if (GET_CODE (x) == REG) + return (REGNO (x) >= FIRST_PSEUDO_REGISTER + && reg_equiv_memory_loc[REGNO (x)]); + + fmt = GET_RTX_FORMAT (GET_CODE (x)); + for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) + if (fmt[i] == 'e' + && (GET_CODE (XEXP (x, i)) == MEM + || refers_to_mem_for_reload_p (XEXP (x, i)))) + return 1; + + return 0; +} + +/* Check the insns before INSN to see if there is a suitable register + containing the same value as GOAL. + If OTHER is -1, look for a register in class CLASS. + Otherwise, just see if register number OTHER shares GOAL's value. + + Return an rtx for the register found, or zero if none is found. + + If RELOAD_REG_P is (short *)1, + we reject any hard reg that appears in reload_reg_rtx + because such a hard reg is also needed coming into this insn. + + If RELOAD_REG_P is any other nonzero value, + it is a vector indexed by hard reg number + and we reject any hard reg whose element in the vector is nonnegative + as well as any that appears in reload_reg_rtx. + + If GOAL is zero, then GOALREG is a register number; we look + for an equivalent for that register. + + MODE is the machine mode of the value we want an equivalence for. + If GOAL is nonzero and not VOIDmode, then it must have mode MODE. + + This function is used by jump.c as well as in the reload pass. + + If GOAL is the sum of the stack pointer and a constant, we treat it + as if it were a constant except that sp is required to be unchanging. */ + +rtx +find_equiv_reg (goal, insn, class, other, reload_reg_p, goalreg, mode) + register rtx goal; + rtx insn; + enum reg_class class; + register int other; + short *reload_reg_p; + int goalreg; + enum machine_mode mode; +{ + register rtx p = insn; + rtx goaltry, valtry, value, where; + register rtx pat; + register int regno = -1; + int valueno; + int goal_mem = 0; + int goal_const = 0; + int goal_mem_addr_varies = 0; + int need_stable_sp = 0; + int nregs; + int valuenregs; + + if (goal == 0) + regno = goalreg; + else if (GET_CODE (goal) == REG) + regno = REGNO (goal); + else if (GET_CODE (goal) == MEM) + { + enum rtx_code code = GET_CODE (XEXP (goal, 0)); + if (MEM_VOLATILE_P (goal)) + return 0; + if (flag_float_store && GET_MODE_CLASS (GET_MODE (goal)) == MODE_FLOAT) + return 0; + /* An address with side effects must be reexecuted. */ + switch (code) + { + case POST_INC: + case PRE_INC: + case POST_DEC: + case PRE_DEC: + return 0; + } + goal_mem = 1; + } + else if (CONSTANT_P (goal)) + goal_const = 1; + else if (GET_CODE (goal) == PLUS + && XEXP (goal, 0) == stack_pointer_rtx + && CONSTANT_P (XEXP (goal, 1))) + goal_const = need_stable_sp = 1; + else + return 0; + + /* On some machines, certain regs must always be rejected + because they don't behave the way ordinary registers do. */ + +#ifdef OVERLAPPING_REGNO_P + if (regno >= 0 && regno < FIRST_PSEUDO_REGISTER + && OVERLAPPING_REGNO_P (regno)) + return 0; +#endif + + /* Scan insns back from INSN, looking for one that copies + a value into or out of GOAL. + Stop and give up if we reach a label. */ + + while (1) + { + p = PREV_INSN (p); + if (p == 0 || GET_CODE (p) == CODE_LABEL) + return 0; + if (GET_CODE (p) == INSN + /* If we don't want spill regs ... */ + && (! (reload_reg_p != 0 + && reload_reg_p != (short *) (HOST_WIDE_INT) 1) + /* ... then ignore insns introduced by reload; they aren't useful + and can cause results in reload_as_needed to be different + from what they were when calculating the need for spills. + If we notice an input-reload insn here, we will reject it below, + but it might hide a usable equivalent. That makes bad code. + It may even abort: perhaps no reg was spilled for this insn + because it was assumed we would find that equivalent. */ + || INSN_UID (p) < reload_first_uid)) + { + rtx tem; + pat = single_set (p); + /* First check for something that sets some reg equal to GOAL. */ + if (pat != 0 + && ((regno >= 0 + && true_regnum (SET_SRC (pat)) == regno + && (valueno = true_regnum (valtry = SET_DEST (pat))) >= 0) + || + (regno >= 0 + && true_regnum (SET_DEST (pat)) == regno + && (valueno = true_regnum (valtry = SET_SRC (pat))) >= 0) + || + (goal_const && rtx_equal_p (SET_SRC (pat), goal) + && (valueno = true_regnum (valtry = SET_DEST (pat))) >= 0) + || (goal_mem + && (valueno = true_regnum (valtry = SET_DEST (pat))) >= 0 + && rtx_renumbered_equal_p (goal, SET_SRC (pat))) + || (goal_mem + && (valueno = true_regnum (valtry = SET_SRC (pat))) >= 0 + && rtx_renumbered_equal_p (goal, SET_DEST (pat))) + /* If we are looking for a constant, + and something equivalent to that constant was copied + into a reg, we can use that reg. */ + || (goal_const && (tem = find_reg_note (p, REG_EQUIV, + NULL_RTX)) + && rtx_equal_p (XEXP (tem, 0), goal) + && (valueno = true_regnum (valtry = SET_DEST (pat))) >= 0) + || (goal_const && (tem = find_reg_note (p, REG_EQUIV, + NULL_RTX)) + && GET_CODE (SET_DEST (pat)) == REG + && GET_CODE (XEXP (tem, 0)) == CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (XEXP (tem, 0))) == MODE_FLOAT + && GET_CODE (goal) == CONST_INT + && 0 != (goaltry = operand_subword (XEXP (tem, 0), 0, 0, + VOIDmode)) + && rtx_equal_p (goal, goaltry) + && (valtry = operand_subword (SET_DEST (pat), 0, 0, + VOIDmode)) + && (valueno = true_regnum (valtry)) >= 0) + || (goal_const && (tem = find_reg_note (p, REG_EQUIV, + NULL_RTX)) + && GET_CODE (SET_DEST (pat)) == REG + && GET_CODE (XEXP (tem, 0)) == CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (XEXP (tem, 0))) == MODE_FLOAT + && GET_CODE (goal) == CONST_INT + && 0 != (goaltry = operand_subword (XEXP (tem, 0), 1, 0, + VOIDmode)) + && rtx_equal_p (goal, goaltry) + && (valtry + = operand_subword (SET_DEST (pat), 1, 0, VOIDmode)) + && (valueno = true_regnum (valtry)) >= 0))) + if (other >= 0 + ? valueno == other + : ((unsigned) valueno < FIRST_PSEUDO_REGISTER + && TEST_HARD_REG_BIT (reg_class_contents[(int) class], + valueno))) + { + value = valtry; + where = p; + break; + } + } + } + + /* We found a previous insn copying GOAL into a suitable other reg VALUE + (or copying VALUE into GOAL, if GOAL is also a register). + Now verify that VALUE is really valid. */ + + /* VALUENO is the register number of VALUE; a hard register. */ + + /* Don't try to re-use something that is killed in this insn. We want + to be able to trust REG_UNUSED notes. */ + if (find_reg_note (where, REG_UNUSED, value)) + return 0; + + /* If we propose to get the value from the stack pointer or if GOAL is + a MEM based on the stack pointer, we need a stable SP. */ + if (valueno == STACK_POINTER_REGNUM + || (goal_mem && reg_overlap_mentioned_for_reload_p (stack_pointer_rtx, + goal))) + need_stable_sp = 1; + + /* Reject VALUE if the copy-insn moved the wrong sort of datum. */ + if (GET_MODE (value) != mode) + return 0; + + /* Reject VALUE if it was loaded from GOAL + and is also a register that appears in the address of GOAL. */ + + if (goal_mem && value == SET_DEST (PATTERN (where)) + && refers_to_regno_for_reload_p (valueno, + (valueno + + HARD_REGNO_NREGS (valueno, mode)), + goal, NULL_PTR)) + return 0; + + /* Reject registers that overlap GOAL. */ + + if (!goal_mem && !goal_const + && regno + HARD_REGNO_NREGS (regno, mode) > valueno + && regno < valueno + HARD_REGNO_NREGS (valueno, mode)) + return 0; + + /* Reject VALUE if it is one of the regs reserved for reloads. + Reload1 knows how to reuse them anyway, and it would get + confused if we allocated one without its knowledge. + (Now that insns introduced by reload are ignored above, + this case shouldn't happen, but I'm not positive.) */ + + if (reload_reg_p != 0 && reload_reg_p != (short *) (HOST_WIDE_INT) 1 + && reload_reg_p[valueno] >= 0) + return 0; + + /* On some machines, certain regs must always be rejected + because they don't behave the way ordinary registers do. */ + +#ifdef OVERLAPPING_REGNO_P + if (OVERLAPPING_REGNO_P (valueno)) + return 0; +#endif + + nregs = HARD_REGNO_NREGS (regno, mode); + valuenregs = HARD_REGNO_NREGS (valueno, mode); + + /* Reject VALUE if it is a register being used for an input reload + even if it is not one of those reserved. */ + + if (reload_reg_p != 0) + { + int i; + for (i = 0; i < n_reloads; i++) + if (reload_reg_rtx[i] != 0 && reload_in[i]) + { + int regno1 = REGNO (reload_reg_rtx[i]); + int nregs1 = HARD_REGNO_NREGS (regno1, + GET_MODE (reload_reg_rtx[i])); + if (regno1 < valueno + valuenregs + && regno1 + nregs1 > valueno) + return 0; + } + } + + if (goal_mem) + /* We must treat frame pointer as varying here, + since it can vary--in a nonlocal goto as generated by expand_goto. */ + goal_mem_addr_varies = !CONSTANT_ADDRESS_P (XEXP (goal, 0)); + + /* Now verify that the values of GOAL and VALUE remain unaltered + until INSN is reached. */ + + p = insn; + while (1) + { + p = PREV_INSN (p); + if (p == where) + return value; + + /* Don't trust the conversion past a function call + if either of the two is in a call-clobbered register, or memory. */ + if (GET_CODE (p) == CALL_INSN + && ((regno >= 0 && regno < FIRST_PSEUDO_REGISTER + && call_used_regs[regno]) + || + (valueno >= 0 && valueno < FIRST_PSEUDO_REGISTER + && call_used_regs[valueno]) + || + goal_mem + || need_stable_sp)) + return 0; + +#ifdef INSN_CLOBBERS_REGNO_P + if ((valueno >= 0 && valueno < FIRST_PSEUDO_REGISTER + && INSN_CLOBBERS_REGNO_P (p, valueno)) + || (regno >= 0 && regno < FIRST_PSEUDO_REGISTER + && INSN_CLOBBERS_REGNO_P (p, regno))) + return 0; +#endif + + if (GET_RTX_CLASS (GET_CODE (p)) == 'i') + { + /* If this insn P stores in either GOAL or VALUE, return 0. + If GOAL is a memory ref and this insn writes memory, return 0. + If GOAL is a memory ref and its address is not constant, + and this insn P changes a register used in GOAL, return 0. */ + + pat = PATTERN (p); + if (GET_CODE (pat) == SET || GET_CODE (pat) == CLOBBER) + { + register rtx dest = SET_DEST (pat); + while (GET_CODE (dest) == SUBREG + || GET_CODE (dest) == ZERO_EXTRACT + || GET_CODE (dest) == SIGN_EXTRACT + || GET_CODE (dest) == STRICT_LOW_PART) + dest = XEXP (dest, 0); + if (GET_CODE (dest) == REG) + { + register int xregno = REGNO (dest); + int xnregs; + if (REGNO (dest) < FIRST_PSEUDO_REGISTER) + xnregs = HARD_REGNO_NREGS (xregno, GET_MODE (dest)); + else + xnregs = 1; + if (xregno < regno + nregs && xregno + xnregs > regno) + return 0; + if (xregno < valueno + valuenregs + && xregno + xnregs > valueno) + return 0; + if (goal_mem_addr_varies + && reg_overlap_mentioned_for_reload_p (dest, goal)) + return 0; + } + else if (goal_mem && GET_CODE (dest) == MEM + && ! push_operand (dest, GET_MODE (dest))) + return 0; + else if (need_stable_sp && push_operand (dest, GET_MODE (dest))) + return 0; + } + else if (GET_CODE (pat) == PARALLEL) + { + register int i; + for (i = XVECLEN (pat, 0) - 1; i >= 0; i--) + { + register rtx v1 = XVECEXP (pat, 0, i); + if (GET_CODE (v1) == SET || GET_CODE (v1) == CLOBBER) + { + register rtx dest = SET_DEST (v1); + while (GET_CODE (dest) == SUBREG + || GET_CODE (dest) == ZERO_EXTRACT + || GET_CODE (dest) == SIGN_EXTRACT + || GET_CODE (dest) == STRICT_LOW_PART) + dest = XEXP (dest, 0); + if (GET_CODE (dest) == REG) + { + register int xregno = REGNO (dest); + int xnregs; + if (REGNO (dest) < FIRST_PSEUDO_REGISTER) + xnregs = HARD_REGNO_NREGS (xregno, GET_MODE (dest)); + else + xnregs = 1; + if (xregno < regno + nregs + && xregno + xnregs > regno) + return 0; + if (xregno < valueno + valuenregs + && xregno + xnregs > valueno) + return 0; + if (goal_mem_addr_varies + && reg_overlap_mentioned_for_reload_p (dest, + goal)) + return 0; + } + else if (goal_mem && GET_CODE (dest) == MEM + && ! push_operand (dest, GET_MODE (dest))) + return 0; + else if (need_stable_sp + && push_operand (dest, GET_MODE (dest))) + return 0; + } + } + } + +#ifdef AUTO_INC_DEC + /* If this insn auto-increments or auto-decrements + either regno or valueno, return 0 now. + If GOAL is a memory ref and its address is not constant, + and this insn P increments a register used in GOAL, return 0. */ + { + register rtx link; + + for (link = REG_NOTES (p); link; link = XEXP (link, 1)) + if (REG_NOTE_KIND (link) == REG_INC + && GET_CODE (XEXP (link, 0)) == REG) + { + register int incno = REGNO (XEXP (link, 0)); + if (incno < regno + nregs && incno >= regno) + return 0; + if (incno < valueno + valuenregs && incno >= valueno) + return 0; + if (goal_mem_addr_varies + && reg_overlap_mentioned_for_reload_p (XEXP (link, 0), + goal)) + return 0; + } + } +#endif + } + } +} + +/* Find a place where INCED appears in an increment or decrement operator + within X, and return the amount INCED is incremented or decremented by. + The value is always positive. */ + +static int +find_inc_amount (x, inced) + rtx x, inced; +{ + register enum rtx_code code = GET_CODE (x); + register char *fmt; + register int i; + + if (code == MEM) + { + register rtx addr = XEXP (x, 0); + if ((GET_CODE (addr) == PRE_DEC + || GET_CODE (addr) == POST_DEC + || GET_CODE (addr) == PRE_INC + || GET_CODE (addr) == POST_INC) + && XEXP (addr, 0) == inced) + return GET_MODE_SIZE (GET_MODE (x)); + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + register int tem = find_inc_amount (XEXP (x, i), inced); + if (tem != 0) + return tem; + } + if (fmt[i] == 'E') + { + register int j; + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + { + register int tem = find_inc_amount (XVECEXP (x, i, j), inced); + if (tem != 0) + return tem; + } + } + } + + return 0; +} + +/* Return 1 if register REGNO is the subject of a clobber in insn INSN. */ + +int +regno_clobbered_p (regno, insn) + int regno; + rtx insn; +{ + if (GET_CODE (PATTERN (insn)) == CLOBBER + && GET_CODE (XEXP (PATTERN (insn), 0)) == REG) + return REGNO (XEXP (PATTERN (insn), 0)) == regno; + + if (GET_CODE (PATTERN (insn)) == PARALLEL) + { + int i = XVECLEN (PATTERN (insn), 0) - 1; + + for (; i >= 0; i--) + { + rtx elt = XVECEXP (PATTERN (insn), 0, i); + if (GET_CODE (elt) == CLOBBER && GET_CODE (XEXP (elt, 0)) == REG + && REGNO (XEXP (elt, 0)) == regno) + return 1; + } + } + + return 0; +} diff --git a/gnu/usr.bin/cc/common/reload.h b/gnu/usr.bin/cc/lib/reload.h similarity index 100% rename from gnu/usr.bin/cc/common/reload.h rename to gnu/usr.bin/cc/lib/reload.h diff --git a/gnu/usr.bin/cc/lib/reload1.c b/gnu/usr.bin/cc/lib/reload1.c new file mode 100644 index 0000000000..87076c8a01 --- /dev/null +++ b/gnu/usr.bin/cc/lib/reload1.c @@ -0,0 +1,6774 @@ +/* Reload pseudo regs into hard regs for insns that require hard regs. + Copyright (C) 1987, 1988, 1989, 1992, 1993 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include +#include "config.h" +#include "rtl.h" +#include "obstack.h" +#include "insn-config.h" +#include "insn-flags.h" +#include "insn-codes.h" +#include "flags.h" +#include "expr.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "reload.h" +#include "recog.h" +#include "basic-block.h" +#include "output.h" + +/* This file contains the reload pass of the compiler, which is + run after register allocation has been done. It checks that + each insn is valid (operands required to be in registers really + are in registers of the proper class) and fixes up invalid ones + by copying values temporarily into registers for the insns + that need them. + + The results of register allocation are described by the vector + reg_renumber; the insns still contain pseudo regs, but reg_renumber + can be used to find which hard reg, if any, a pseudo reg is in. + + The technique we always use is to free up a few hard regs that are + called ``reload regs'', and for each place where a pseudo reg + must be in a hard reg, copy it temporarily into one of the reload regs. + + All the pseudos that were formerly allocated to the hard regs that + are now in use as reload regs must be ``spilled''. This means + that they go to other hard regs, or to stack slots if no other + available hard regs can be found. Spilling can invalidate more + insns, requiring additional need for reloads, so we must keep checking + until the process stabilizes. + + For machines with different classes of registers, we must keep track + of the register class needed for each reload, and make sure that + we allocate enough reload registers of each class. + + The file reload.c contains the code that checks one insn for + validity and reports the reloads that it needs. This file + is in charge of scanning the entire rtl code, accumulating the + reload needs, spilling, assigning reload registers to use for + fixing up each insn, and generating the new insns to copy values + into the reload registers. */ + + +#ifndef REGISTER_MOVE_COST +#define REGISTER_MOVE_COST(x, y) 2 +#endif + +#ifndef MEMORY_MOVE_COST +#define MEMORY_MOVE_COST(x) 4 +#endif + +/* During reload_as_needed, element N contains a REG rtx for the hard reg + into which reg N has been reloaded (perhaps for a previous insn). */ +static rtx *reg_last_reload_reg; + +/* Elt N nonzero if reg_last_reload_reg[N] has been set in this insn + for an output reload that stores into reg N. */ +static char *reg_has_output_reload; + +/* Indicates which hard regs are reload-registers for an output reload + in the current insn. */ +static HARD_REG_SET reg_is_output_reload; + +/* Element N is the constant value to which pseudo reg N is equivalent, + or zero if pseudo reg N is not equivalent to a constant. + find_reloads looks at this in order to replace pseudo reg N + with the constant it stands for. */ +rtx *reg_equiv_constant; + +/* Element N is a memory location to which pseudo reg N is equivalent, + prior to any register elimination (such as frame pointer to stack + pointer). Depending on whether or not it is a valid address, this value + is transferred to either reg_equiv_address or reg_equiv_mem. */ +rtx *reg_equiv_memory_loc; + +/* Element N is the address of stack slot to which pseudo reg N is equivalent. + This is used when the address is not valid as a memory address + (because its displacement is too big for the machine.) */ +rtx *reg_equiv_address; + +/* Element N is the memory slot to which pseudo reg N is equivalent, + or zero if pseudo reg N is not equivalent to a memory slot. */ +rtx *reg_equiv_mem; + +/* Widest width in which each pseudo reg is referred to (via subreg). */ +static int *reg_max_ref_width; + +/* Element N is the insn that initialized reg N from its equivalent + constant or memory slot. */ +static rtx *reg_equiv_init; + +/* During reload_as_needed, element N contains the last pseudo regno + reloaded into the Nth reload register. This vector is in parallel + with spill_regs. If that pseudo reg occupied more than one register, + reg_reloaded_contents points to that pseudo for each spill register in + use; all of these must remain set for an inheritance to occur. */ +static int reg_reloaded_contents[FIRST_PSEUDO_REGISTER]; + +/* During reload_as_needed, element N contains the insn for which + the Nth reload register was last used. This vector is in parallel + with spill_regs, and its contents are significant only when + reg_reloaded_contents is significant. */ +static rtx reg_reloaded_insn[FIRST_PSEUDO_REGISTER]; + +/* Number of spill-regs so far; number of valid elements of spill_regs. */ +static int n_spills; + +/* In parallel with spill_regs, contains REG rtx's for those regs. + Holds the last rtx used for any given reg, or 0 if it has never + been used for spilling yet. This rtx is reused, provided it has + the proper mode. */ +static rtx spill_reg_rtx[FIRST_PSEUDO_REGISTER]; + +/* In parallel with spill_regs, contains nonzero for a spill reg + that was stored after the last time it was used. + The precise value is the insn generated to do the store. */ +static rtx spill_reg_store[FIRST_PSEUDO_REGISTER]; + +/* This table is the inverse mapping of spill_regs: + indexed by hard reg number, + it contains the position of that reg in spill_regs, + or -1 for something that is not in spill_regs. */ +static short spill_reg_order[FIRST_PSEUDO_REGISTER]; + +/* This reg set indicates registers that may not be used for retrying global + allocation. The registers that may not be used include all spill registers + and the frame pointer (if we are using one). */ +HARD_REG_SET forbidden_regs; + +/* This reg set indicates registers that are not good for spill registers. + They will not be used to complete groups of spill registers. This includes + all fixed registers, registers that may be eliminated, and, if + SMALL_REGISTER_CLASSES is not defined, registers explicitly used in the rtl. + + (spill_reg_order prevents these registers from being used to start a + group.) */ +static HARD_REG_SET bad_spill_regs; + +/* Describes order of use of registers for reloading + of spilled pseudo-registers. `spills' is the number of + elements that are actually valid; new ones are added at the end. */ +static short spill_regs[FIRST_PSEUDO_REGISTER]; + +/* Describes order of preference for putting regs into spill_regs. + Contains the numbers of all the hard regs, in order most preferred first. + This order is different for each function. + It is set up by order_regs_for_reload. + Empty elements at the end contain -1. */ +static short potential_reload_regs[FIRST_PSEUDO_REGISTER]; + +/* 1 for a hard register that appears explicitly in the rtl + (for example, function value registers, special registers + used by insns, structure value pointer registers). */ +static char regs_explicitly_used[FIRST_PSEUDO_REGISTER]; + +/* Indicates if a register was counted against the need for + groups. 0 means it can count against max_nongroup instead. */ +static HARD_REG_SET counted_for_groups; + +/* Indicates if a register was counted against the need for + non-groups. 0 means it can become part of a new group. + During choose_reload_regs, 1 here means don't use this reg + as part of a group, even if it seems to be otherwise ok. */ +static HARD_REG_SET counted_for_nongroups; + +/* Indexed by pseudo reg number N, + says may not delete stores into the real (memory) home of pseudo N. + This is set if we already substituted a memory equivalent in some uses, + which happens when we have to eliminate the fp from it. */ +static char *cannot_omit_stores; + +/* Nonzero if indirect addressing is supported on the machine; this means + that spilling (REG n) does not require reloading it into a register in + order to do (MEM (REG n)) or (MEM (PLUS (REG n) (CONST_INT c))). The + value indicates the level of indirect addressing supported, e.g., two + means that (MEM (MEM (REG n))) is also valid if (REG n) does not get + a hard register. */ + +static char spill_indirect_levels; + +/* Nonzero if indirect addressing is supported when the innermost MEM is + of the form (MEM (SYMBOL_REF sym)). It is assumed that the level to + which these are valid is the same as spill_indirect_levels, above. */ + +char indirect_symref_ok; + +/* Nonzero if an address (plus (reg frame_pointer) (reg ...)) is valid. */ + +char double_reg_address_ok; + +/* Record the stack slot for each spilled hard register. */ + +static rtx spill_stack_slot[FIRST_PSEUDO_REGISTER]; + +/* Width allocated so far for that stack slot. */ + +static int spill_stack_slot_width[FIRST_PSEUDO_REGISTER]; + +/* Indexed by register class and basic block number, nonzero if there is + any need for a spill register of that class in that basic block. + The pointer is 0 if we did stupid allocation and don't know + the structure of basic blocks. */ + +char *basic_block_needs[N_REG_CLASSES]; + +/* First uid used by insns created by reload in this function. + Used in find_equiv_reg. */ +int reload_first_uid; + +/* Flag set by local-alloc or global-alloc if anything is live in + a call-clobbered reg across calls. */ + +int caller_save_needed; + +/* Set to 1 while reload_as_needed is operating. + Required by some machines to handle any generated moves differently. */ + +int reload_in_progress = 0; + +/* These arrays record the insn_code of insns that may be needed to + perform input and output reloads of special objects. They provide a + place to pass a scratch register. */ + +enum insn_code reload_in_optab[NUM_MACHINE_MODES]; +enum insn_code reload_out_optab[NUM_MACHINE_MODES]; + +/* This obstack is used for allocation of rtl during register elimination. + The allocated storage can be freed once find_reloads has processed the + insn. */ + +struct obstack reload_obstack; +char *reload_firstobj; + +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free + +/* List of labels that must never be deleted. */ +extern rtx forced_labels; + +/* This structure is used to record information about register eliminations. + Each array entry describes one possible way of eliminating a register + in favor of another. If there is more than one way of eliminating a + particular register, the most preferred should be specified first. */ + +static struct elim_table +{ + int from; /* Register number to be eliminated. */ + int to; /* Register number used as replacement. */ + int initial_offset; /* Initial difference between values. */ + int can_eliminate; /* Non-zero if this elimination can be done. */ + int can_eliminate_previous; /* Value of CAN_ELIMINATE in previous scan over + insns made by reload. */ + int offset; /* Current offset between the two regs. */ + int max_offset; /* Maximum offset between the two regs. */ + int previous_offset; /* Offset at end of previous insn. */ + int ref_outside_mem; /* "to" has been referenced outside a MEM. */ + rtx from_rtx; /* REG rtx for the register to be eliminated. + We cannot simply compare the number since + we might then spuriously replace a hard + register corresponding to a pseudo + assigned to the reg to be eliminated. */ + rtx to_rtx; /* REG rtx for the replacement. */ +} reg_eliminate[] = + +/* If a set of eliminable registers was specified, define the table from it. + Otherwise, default to the normal case of the frame pointer being + replaced by the stack pointer. */ + +#ifdef ELIMINABLE_REGS + ELIMINABLE_REGS; +#else + {{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}}; +#endif + +#define NUM_ELIMINABLE_REGS (sizeof reg_eliminate / sizeof reg_eliminate[0]) + +/* Record the number of pending eliminations that have an offset not equal + to their initial offset. If non-zero, we use a new copy of each + replacement result in any insns encountered. */ +static int num_not_at_initial_offset; + +/* Count the number of registers that we may be able to eliminate. */ +static int num_eliminable; + +/* For each label, we record the offset of each elimination. If we reach + a label by more than one path and an offset differs, we cannot do the + elimination. This information is indexed by the number of the label. + The first table is an array of flags that records whether we have yet + encountered a label and the second table is an array of arrays, one + entry in the latter array for each elimination. */ + +static char *offsets_known_at; +static int (*offsets_at)[NUM_ELIMINABLE_REGS]; + +/* Number of labels in the current function. */ + +static int num_labels; + +struct hard_reg_n_uses { int regno; int uses; }; + +static int possible_group_p PROTO((int, int *)); +static void count_possible_groups PROTO((int *, enum machine_mode *, + int *)); +static int modes_equiv_for_class_p PROTO((enum machine_mode, + enum machine_mode, + enum reg_class)); +static void spill_failure PROTO((rtx)); +static int new_spill_reg PROTO((int, int, int *, int *, int, + FILE *)); +static void delete_dead_insn PROTO((rtx)); +static void alter_reg PROTO((int, int)); +static void set_label_offsets PROTO((rtx, rtx, int)); +static int eliminate_regs_in_insn PROTO((rtx, int)); +static void mark_not_eliminable PROTO((rtx, rtx)); +static int spill_hard_reg PROTO((int, int, FILE *, int)); +static void scan_paradoxical_subregs PROTO((rtx)); +static int hard_reg_use_compare PROTO((struct hard_reg_n_uses *, + struct hard_reg_n_uses *)); +static void order_regs_for_reload PROTO((void)); +static void reload_as_needed PROTO((rtx, int)); +static void forget_old_reloads_1 PROTO((rtx, rtx)); +static int reload_reg_class_lower PROTO((short *, short *)); +static void mark_reload_reg_in_use PROTO((int, int, enum reload_type, + enum machine_mode)); +static void clear_reload_reg_in_use PROTO((int, int, enum reload_type, + enum machine_mode)); +static int reload_reg_free_p PROTO((int, int, enum reload_type)); +static int reload_reg_free_before_p PROTO((int, int, enum reload_type)); +static int reload_reg_reaches_end_p PROTO((int, int, enum reload_type)); +static int allocate_reload_reg PROTO((int, rtx, int, int)); +static void choose_reload_regs PROTO((rtx, rtx)); +static void merge_assigned_reloads PROTO((rtx)); +static void emit_reload_insns PROTO((rtx)); +static void delete_output_reload PROTO((rtx, int, rtx)); +static void inc_for_reload PROTO((rtx, rtx, int)); +static int constraint_accepts_reg_p PROTO((char *, rtx)); +static int count_occurrences PROTO((rtx, rtx)); + +/* Initialize the reload pass once per compilation. */ + +void +init_reload () +{ + register int i; + + /* Often (MEM (REG n)) is still valid even if (REG n) is put on the stack. + Set spill_indirect_levels to the number of levels such addressing is + permitted, zero if it is not permitted at all. */ + + register rtx tem + = gen_rtx (MEM, Pmode, + gen_rtx (PLUS, Pmode, + gen_rtx (REG, Pmode, LAST_VIRTUAL_REGISTER + 1), + GEN_INT (4))); + spill_indirect_levels = 0; + + while (memory_address_p (QImode, tem)) + { + spill_indirect_levels++; + tem = gen_rtx (MEM, Pmode, tem); + } + + /* See if indirect addressing is valid for (MEM (SYMBOL_REF ...)). */ + + tem = gen_rtx (MEM, Pmode, gen_rtx (SYMBOL_REF, Pmode, "foo")); + indirect_symref_ok = memory_address_p (QImode, tem); + + /* See if reg+reg is a valid (and offsettable) address. */ + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + tem = gen_rtx (PLUS, Pmode, + gen_rtx (REG, Pmode, FRAME_POINTER_REGNUM), + gen_rtx (REG, Pmode, i)); + /* This way, we make sure that reg+reg is an offsettable address. */ + tem = plus_constant (tem, 4); + + if (memory_address_p (QImode, tem)) + { + double_reg_address_ok = 1; + break; + } + } + + /* Initialize obstack for our rtl allocation. */ + gcc_obstack_init (&reload_obstack); + reload_firstobj = (char *) obstack_alloc (&reload_obstack, 0); +} + +/* Main entry point for the reload pass. + + FIRST is the first insn of the function being compiled. + + GLOBAL nonzero means we were called from global_alloc + and should attempt to reallocate any pseudoregs that we + displace from hard regs we will use for reloads. + If GLOBAL is zero, we do not have enough information to do that, + so any pseudo reg that is spilled must go to the stack. + + DUMPFILE is the global-reg debugging dump file stream, or 0. + If it is nonzero, messages are written to it to describe + which registers are seized as reload regs, which pseudo regs + are spilled from them, and where the pseudo regs are reallocated to. + + Return value is nonzero if reload failed + and we must not do any more for this function. */ + +int +reload (first, global, dumpfile) + rtx first; + int global; + FILE *dumpfile; +{ + register int class; + register int i, j; + register rtx insn; + register struct elim_table *ep; + + int something_changed; + int something_needs_reloads; + int something_needs_elimination; + int new_basic_block_needs; + enum reg_class caller_save_spill_class = NO_REGS; + int caller_save_group_size = 1; + + /* Nonzero means we couldn't get enough spill regs. */ + int failure = 0; + + /* The basic block number currently being processed for INSN. */ + int this_block; + + /* Make sure even insns with volatile mem refs are recognizable. */ + init_recog (); + + /* Enable find_equiv_reg to distinguish insns made by reload. */ + reload_first_uid = get_max_uid (); + + for (i = 0; i < N_REG_CLASSES; i++) + basic_block_needs[i] = 0; + +#ifdef SECONDARY_MEMORY_NEEDED + /* Initialize the secondary memory table. */ + clear_secondary_mem (); +#endif + + /* Remember which hard regs appear explicitly + before we merge into `regs_ever_live' the ones in which + pseudo regs have been allocated. */ + bcopy (regs_ever_live, regs_explicitly_used, sizeof regs_ever_live); + + /* We don't have a stack slot for any spill reg yet. */ + bzero (spill_stack_slot, sizeof spill_stack_slot); + bzero (spill_stack_slot_width, sizeof spill_stack_slot_width); + + /* Initialize the save area information for caller-save, in case some + are needed. */ + init_save_areas (); + + /* Compute which hard registers are now in use + as homes for pseudo registers. + This is done here rather than (eg) in global_alloc + because this point is reached even if not optimizing. */ + + for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) + mark_home_live (i); + + /* Make sure that the last insn in the chain + is not something that needs reloading. */ + emit_note (NULL_PTR, NOTE_INSN_DELETED); + + /* Find all the pseudo registers that didn't get hard regs + but do have known equivalent constants or memory slots. + These include parameters (known equivalent to parameter slots) + and cse'd or loop-moved constant memory addresses. + + Record constant equivalents in reg_equiv_constant + so they will be substituted by find_reloads. + Record memory equivalents in reg_mem_equiv so they can + be substituted eventually by altering the REG-rtx's. */ + + reg_equiv_constant = (rtx *) alloca (max_regno * sizeof (rtx)); + bzero (reg_equiv_constant, max_regno * sizeof (rtx)); + reg_equiv_memory_loc = (rtx *) alloca (max_regno * sizeof (rtx)); + bzero (reg_equiv_memory_loc, max_regno * sizeof (rtx)); + reg_equiv_mem = (rtx *) alloca (max_regno * sizeof (rtx)); + bzero (reg_equiv_mem, max_regno * sizeof (rtx)); + reg_equiv_init = (rtx *) alloca (max_regno * sizeof (rtx)); + bzero (reg_equiv_init, max_regno * sizeof (rtx)); + reg_equiv_address = (rtx *) alloca (max_regno * sizeof (rtx)); + bzero (reg_equiv_address, max_regno * sizeof (rtx)); + reg_max_ref_width = (int *) alloca (max_regno * sizeof (int)); + bzero (reg_max_ref_width, max_regno * sizeof (int)); + cannot_omit_stores = (char *) alloca (max_regno); + bzero (cannot_omit_stores, max_regno); + + /* Look for REG_EQUIV notes; record what each pseudo is equivalent to. + Also find all paradoxical subregs + and find largest such for each pseudo. */ + + for (insn = first; insn; insn = NEXT_INSN (insn)) + { + rtx set = single_set (insn); + + if (set != 0 && GET_CODE (SET_DEST (set)) == REG) + { + rtx note = find_reg_note (insn, REG_EQUIV, NULL_RTX); + if (note +#ifdef LEGITIMATE_PIC_OPERAND_P + && (! CONSTANT_P (XEXP (note, 0)) || ! flag_pic + || LEGITIMATE_PIC_OPERAND_P (XEXP (note, 0))) +#endif + ) + { + rtx x = XEXP (note, 0); + i = REGNO (SET_DEST (set)); + if (i > LAST_VIRTUAL_REGISTER) + { + if (GET_CODE (x) == MEM) + reg_equiv_memory_loc[i] = x; + else if (CONSTANT_P (x)) + { + if (LEGITIMATE_CONSTANT_P (x)) + reg_equiv_constant[i] = x; + else + reg_equiv_memory_loc[i] + = force_const_mem (GET_MODE (SET_DEST (set)), x); + } + else + continue; + + /* If this register is being made equivalent to a MEM + and the MEM is not SET_SRC, the equivalencing insn + is one with the MEM as a SET_DEST and it occurs later. + So don't mark this insn now. */ + if (GET_CODE (x) != MEM + || rtx_equal_p (SET_SRC (set), x)) + reg_equiv_init[i] = insn; + } + } + } + + /* If this insn is setting a MEM from a register equivalent to it, + this is the equivalencing insn. */ + else if (set && GET_CODE (SET_DEST (set)) == MEM + && GET_CODE (SET_SRC (set)) == REG + && reg_equiv_memory_loc[REGNO (SET_SRC (set))] + && rtx_equal_p (SET_DEST (set), + reg_equiv_memory_loc[REGNO (SET_SRC (set))])) + reg_equiv_init[REGNO (SET_SRC (set))] = insn; + + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + scan_paradoxical_subregs (PATTERN (insn)); + } + + /* Does this function require a frame pointer? */ + + frame_pointer_needed = (! flag_omit_frame_pointer +#ifdef EXIT_IGNORE_STACK + /* ?? If EXIT_IGNORE_STACK is set, we will not save + and restore sp for alloca. So we can't eliminate + the frame pointer in that case. At some point, + we should improve this by emitting the + sp-adjusting insns for this case. */ + || (current_function_calls_alloca + && EXIT_IGNORE_STACK) +#endif + || FRAME_POINTER_REQUIRED); + + num_eliminable = 0; + + /* Initialize the table of registers to eliminate. The way we do this + depends on how the eliminable registers were defined. */ +#ifdef ELIMINABLE_REGS + for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) + { + ep->can_eliminate = ep->can_eliminate_previous + = (CAN_ELIMINATE (ep->from, ep->to) + && (ep->from != FRAME_POINTER_REGNUM || ! frame_pointer_needed)); + } +#else + reg_eliminate[0].can_eliminate = reg_eliminate[0].can_eliminate_previous + = ! frame_pointer_needed; +#endif + + /* Count the number of eliminable registers and build the FROM and TO + REG rtx's. Note that code in gen_rtx will cause, e.g., + gen_rtx (REG, Pmode, STACK_POINTER_REGNUM) to equal stack_pointer_rtx. + We depend on this. */ + for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) + { + num_eliminable += ep->can_eliminate; + ep->from_rtx = gen_rtx (REG, Pmode, ep->from); + ep->to_rtx = gen_rtx (REG, Pmode, ep->to); + } + + num_labels = max_label_num () - get_first_label_num (); + + /* Allocate the tables used to store offset information at labels. */ + offsets_known_at = (char *) alloca (num_labels); + offsets_at + = (int (*)[NUM_ELIMINABLE_REGS]) + alloca (num_labels * NUM_ELIMINABLE_REGS * sizeof (int)); + + offsets_known_at -= get_first_label_num (); + offsets_at -= get_first_label_num (); + + /* Alter each pseudo-reg rtx to contain its hard reg number. + Assign stack slots to the pseudos that lack hard regs or equivalents. + Do not touch virtual registers. */ + + for (i = LAST_VIRTUAL_REGISTER + 1; i < max_regno; i++) + alter_reg (i, -1); + + /* Round size of stack frame to BIGGEST_ALIGNMENT. This must be done here + because the stack size may be a part of the offset computation for + register elimination. */ + assign_stack_local (BLKmode, 0, 0); + + /* If we have some registers we think can be eliminated, scan all insns to + see if there is an insn that sets one of these registers to something + other than itself plus a constant. If so, the register cannot be + eliminated. Doing this scan here eliminates an extra pass through the + main reload loop in the most common case where register elimination + cannot be done. */ + for (insn = first; insn && num_eliminable; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN + || GET_CODE (insn) == CALL_INSN) + note_stores (PATTERN (insn), mark_not_eliminable); + +#ifndef REGISTER_CONSTRAINTS + /* If all the pseudo regs have hard regs, + except for those that are never referenced, + we know that no reloads are needed. */ + /* But that is not true if there are register constraints, since + in that case some pseudos might be in the wrong kind of hard reg. */ + + for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) + if (reg_renumber[i] == -1 && reg_n_refs[i] != 0) + break; + + if (i == max_regno && num_eliminable == 0 && ! caller_save_needed) + return; +#endif + + /* Compute the order of preference for hard registers to spill. + Store them by decreasing preference in potential_reload_regs. */ + + order_regs_for_reload (); + + /* So far, no hard regs have been spilled. */ + n_spills = 0; + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + spill_reg_order[i] = -1; + + /* On most machines, we can't use any register explicitly used in the + rtl as a spill register. But on some, we have to. Those will have + taken care to keep the life of hard regs as short as possible. */ + +#ifdef SMALL_REGISTER_CLASSES + CLEAR_HARD_REG_SET (forbidden_regs); +#else + COPY_HARD_REG_SET (forbidden_regs, bad_spill_regs); +#endif + + /* Spill any hard regs that we know we can't eliminate. */ + for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) + if (! ep->can_eliminate) + { + spill_hard_reg (ep->from, global, dumpfile, 1); + regs_ever_live[ep->from] = 1; + } + + if (global) + for (i = 0; i < N_REG_CLASSES; i++) + { + basic_block_needs[i] = (char *)alloca (n_basic_blocks); + bzero (basic_block_needs[i], n_basic_blocks); + } + + /* From now on, we need to emit any moves without making new pseudos. */ + reload_in_progress = 1; + + /* This loop scans the entire function each go-round + and repeats until one repetition spills no additional hard regs. */ + + /* This flag is set when a pseudo reg is spilled, + to require another pass. Note that getting an additional reload + reg does not necessarily imply any pseudo reg was spilled; + sometimes we find a reload reg that no pseudo reg was allocated in. */ + something_changed = 1; + /* This flag is set if there are any insns that require reloading. */ + something_needs_reloads = 0; + /* This flag is set if there are any insns that require register + eliminations. */ + something_needs_elimination = 0; + while (something_changed) + { + rtx after_call = 0; + + /* For each class, number of reload regs needed in that class. + This is the maximum over all insns of the needs in that class + of the individual insn. */ + int max_needs[N_REG_CLASSES]; + /* For each class, size of group of consecutive regs + that is needed for the reloads of this class. */ + int group_size[N_REG_CLASSES]; + /* For each class, max number of consecutive groups needed. + (Each group contains group_size[CLASS] consecutive registers.) */ + int max_groups[N_REG_CLASSES]; + /* For each class, max number needed of regs that don't belong + to any of the groups. */ + int max_nongroups[N_REG_CLASSES]; + /* For each class, the machine mode which requires consecutive + groups of regs of that class. + If two different modes ever require groups of one class, + they must be the same size and equally restrictive for that class, + otherwise we can't handle the complexity. */ + enum machine_mode group_mode[N_REG_CLASSES]; + /* Record the insn where each maximum need is first found. */ + rtx max_needs_insn[N_REG_CLASSES]; + rtx max_groups_insn[N_REG_CLASSES]; + rtx max_nongroups_insn[N_REG_CLASSES]; + rtx x; + int starting_frame_size = get_frame_size (); + static char *reg_class_names[] = REG_CLASS_NAMES; + + something_changed = 0; + bzero (max_needs, sizeof max_needs); + bzero (max_groups, sizeof max_groups); + bzero (max_nongroups, sizeof max_nongroups); + bzero (max_needs_insn, sizeof max_needs_insn); + bzero (max_groups_insn, sizeof max_groups_insn); + bzero (max_nongroups_insn, sizeof max_nongroups_insn); + bzero (group_size, sizeof group_size); + for (i = 0; i < N_REG_CLASSES; i++) + group_mode[i] = VOIDmode; + + /* Keep track of which basic blocks are needing the reloads. */ + this_block = 0; + + /* Remember whether any element of basic_block_needs + changes from 0 to 1 in this pass. */ + new_basic_block_needs = 0; + + /* Reset all offsets on eliminable registers to their initial values. */ +#ifdef ELIMINABLE_REGS + for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) + { + INITIAL_ELIMINATION_OFFSET (ep->from, ep->to, ep->initial_offset); + ep->previous_offset = ep->offset + = ep->max_offset = ep->initial_offset; + } +#else +#ifdef INITIAL_FRAME_POINTER_OFFSET + INITIAL_FRAME_POINTER_OFFSET (reg_eliminate[0].initial_offset); +#else + if (!FRAME_POINTER_REQUIRED) + abort (); + reg_eliminate[0].initial_offset = 0; +#endif + reg_eliminate[0].previous_offset = reg_eliminate[0].max_offset + = reg_eliminate[0].offset = reg_eliminate[0].initial_offset; +#endif + + num_not_at_initial_offset = 0; + + bzero (&offsets_known_at[get_first_label_num ()], num_labels); + + /* Set a known offset for each forced label to be at the initial offset + of each elimination. We do this because we assume that all + computed jumps occur from a location where each elimination is + at its initial offset. */ + + for (x = forced_labels; x; x = XEXP (x, 1)) + if (XEXP (x, 0)) + set_label_offsets (XEXP (x, 0), NULL_RTX, 1); + + /* For each pseudo register that has an equivalent location defined, + try to eliminate any eliminable registers (such as the frame pointer) + assuming initial offsets for the replacement register, which + is the normal case. + + If the resulting location is directly addressable, substitute + the MEM we just got directly for the old REG. + + If it is not addressable but is a constant or the sum of a hard reg + and constant, it is probably not addressable because the constant is + out of range, in that case record the address; we will generate + hairy code to compute the address in a register each time it is + needed. + + If the location is not addressable, but does not have one of the + above forms, assign a stack slot. We have to do this to avoid the + potential of producing lots of reloads if, e.g., a location involves + a pseudo that didn't get a hard register and has an equivalent memory + location that also involves a pseudo that didn't get a hard register. + + Perhaps at some point we will improve reload_when_needed handling + so this problem goes away. But that's very hairy. */ + + for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) + if (reg_renumber[i] < 0 && reg_equiv_memory_loc[i]) + { + rtx x = eliminate_regs (reg_equiv_memory_loc[i], 0, NULL_RTX); + + if (strict_memory_address_p (GET_MODE (regno_reg_rtx[i]), + XEXP (x, 0))) + reg_equiv_mem[i] = x, reg_equiv_address[i] = 0; + else if (CONSTANT_P (XEXP (x, 0)) + || (GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG + && (REGNO (XEXP (XEXP (x, 0), 0)) + < FIRST_PSEUDO_REGISTER) + && CONSTANT_P (XEXP (XEXP (x, 0), 1)))) + reg_equiv_address[i] = XEXP (x, 0), reg_equiv_mem[i] = 0; + else + { + /* Make a new stack slot. Then indicate that something + changed so we go back and recompute offsets for + eliminable registers because the allocation of memory + below might change some offset. reg_equiv_{mem,address} + will be set up for this pseudo on the next pass around + the loop. */ + reg_equiv_memory_loc[i] = 0; + reg_equiv_init[i] = 0; + alter_reg (i, -1); + something_changed = 1; + } + } + + /* If we allocated another pseudo to the stack, redo elimination + bookkeeping. */ + if (something_changed) + continue; + + /* If caller-saves needs a group, initialize the group to include + the size and mode required for caller-saves. */ + + if (caller_save_group_size > 1) + { + group_mode[(int) caller_save_spill_class] = Pmode; + group_size[(int) caller_save_spill_class] = caller_save_group_size; + } + + /* Compute the most additional registers needed by any instruction. + Collect information separately for each class of regs. */ + + for (insn = first; insn; insn = NEXT_INSN (insn)) + { + if (global && this_block + 1 < n_basic_blocks + && insn == basic_block_head[this_block+1]) + ++this_block; + + /* If this is a label, a JUMP_INSN, or has REG_NOTES (which + might include REG_LABEL), we need to see what effects this + has on the known offsets at labels. */ + + if (GET_CODE (insn) == CODE_LABEL || GET_CODE (insn) == JUMP_INSN + || (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && REG_NOTES (insn) != 0)) + set_label_offsets (insn, insn, 0); + + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + /* Nonzero means don't use a reload reg that overlaps + the place where a function value can be returned. */ + rtx avoid_return_reg = 0; + + rtx old_body = PATTERN (insn); + int old_code = INSN_CODE (insn); + rtx old_notes = REG_NOTES (insn); + int did_elimination = 0; + int max_total_input_groups = 0, max_total_output_groups = 0; + + /* To compute the number of reload registers of each class + needed for an insn, we must similate what choose_reload_regs + can do. We do this by splitting an insn into an "input" and + an "output" part. RELOAD_OTHER reloads are used in both. + The input part uses those reloads, RELOAD_FOR_INPUT reloads, + which must be live over the entire input section of reloads, + and the maximum of all the RELOAD_FOR_INPUT_ADDRESS and + RELOAD_FOR_OPERAND_ADDRESS reloads, which conflict with the + inputs. + + The registers needed for output are RELOAD_OTHER and + RELOAD_FOR_OUTPUT, which are live for the entire output + portion, and the maximum of all the RELOAD_FOR_OUTPUT_ADDRESS + reloads for each operand. + + The total number of registers needed is the maximum of the + inputs and outputs. */ + + /* These just count RELOAD_OTHER. */ + int insn_needs[N_REG_CLASSES]; + int insn_groups[N_REG_CLASSES]; + int insn_total_groups = 0; + + /* Count RELOAD_FOR_INPUT reloads. */ + int insn_needs_for_inputs[N_REG_CLASSES]; + int insn_groups_for_inputs[N_REG_CLASSES]; + int insn_total_groups_for_inputs = 0; + + /* Count RELOAD_FOR_OUTPUT reloads. */ + int insn_needs_for_outputs[N_REG_CLASSES]; + int insn_groups_for_outputs[N_REG_CLASSES]; + int insn_total_groups_for_outputs = 0; + + /* Count RELOAD_FOR_INSN reloads. */ + int insn_needs_for_insn[N_REG_CLASSES]; + int insn_groups_for_insn[N_REG_CLASSES]; + int insn_total_groups_for_insn = 0; + + /* Count RELOAD_FOR_OTHER_ADDRESS reloads. */ + int insn_needs_for_other_addr[N_REG_CLASSES]; + int insn_groups_for_other_addr[N_REG_CLASSES]; + int insn_total_groups_for_other_addr = 0; + + /* Count RELOAD_FOR_INPUT_ADDRESS reloads. */ + int insn_needs_for_in_addr[MAX_RECOG_OPERANDS][N_REG_CLASSES]; + int insn_groups_for_in_addr[MAX_RECOG_OPERANDS][N_REG_CLASSES]; + int insn_total_groups_for_in_addr[MAX_RECOG_OPERANDS]; + + /* Count RELOAD_FOR_OUTPUT_ADDRESS reloads. */ + int insn_needs_for_out_addr[MAX_RECOG_OPERANDS][N_REG_CLASSES]; + int insn_groups_for_out_addr[MAX_RECOG_OPERANDS][N_REG_CLASSES]; + int insn_total_groups_for_out_addr[MAX_RECOG_OPERANDS]; + + /* Count RELOAD_FOR_OPERAND_ADDRESS reloads. */ + int insn_needs_for_op_addr[N_REG_CLASSES]; + int insn_groups_for_op_addr[N_REG_CLASSES]; + int insn_total_groups_for_op_addr = 0; + +#if 0 /* This wouldn't work nowadays, since optimize_bit_field + looks for non-strict memory addresses. */ + /* Optimization: a bit-field instruction whose field + happens to be a byte or halfword in memory + can be changed to a move instruction. */ + + if (GET_CODE (PATTERN (insn)) == SET) + { + rtx dest = SET_DEST (PATTERN (insn)); + rtx src = SET_SRC (PATTERN (insn)); + + if (GET_CODE (dest) == ZERO_EXTRACT + || GET_CODE (dest) == SIGN_EXTRACT) + optimize_bit_field (PATTERN (insn), insn, reg_equiv_mem); + if (GET_CODE (src) == ZERO_EXTRACT + || GET_CODE (src) == SIGN_EXTRACT) + optimize_bit_field (PATTERN (insn), insn, reg_equiv_mem); + } +#endif + + /* If needed, eliminate any eliminable registers. */ + if (num_eliminable) + did_elimination = eliminate_regs_in_insn (insn, 0); + +#ifdef SMALL_REGISTER_CLASSES + /* Set avoid_return_reg if this is an insn + that might use the value of a function call. */ + if (GET_CODE (insn) == CALL_INSN) + { + if (GET_CODE (PATTERN (insn)) == SET) + after_call = SET_DEST (PATTERN (insn)); + else if (GET_CODE (PATTERN (insn)) == PARALLEL + && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) + after_call = SET_DEST (XVECEXP (PATTERN (insn), 0, 0)); + else + after_call = 0; + } + else if (after_call != 0 + && !(GET_CODE (PATTERN (insn)) == SET + && SET_DEST (PATTERN (insn)) == stack_pointer_rtx)) + { + if (reg_mentioned_p (after_call, PATTERN (insn))) + avoid_return_reg = after_call; + after_call = 0; + } +#endif /* SMALL_REGISTER_CLASSES */ + + /* Analyze the instruction. */ + find_reloads (insn, 0, spill_indirect_levels, global, + spill_reg_order); + + /* Remember for later shortcuts which insns had any reloads or + register eliminations. + + One might think that it would be worthwhile to mark insns + that need register replacements but not reloads, but this is + not safe because find_reloads may do some manipulation of + the insn (such as swapping commutative operands), which would + be lost when we restore the old pattern after register + replacement. So the actions of find_reloads must be redone in + subsequent passes or in reload_as_needed. + + However, it is safe to mark insns that need reloads + but not register replacement. */ + + PUT_MODE (insn, (did_elimination ? QImode + : n_reloads ? HImode + : GET_MODE (insn) == DImode ? DImode + : VOIDmode)); + + /* Discard any register replacements done. */ + if (did_elimination) + { + obstack_free (&reload_obstack, reload_firstobj); + PATTERN (insn) = old_body; + INSN_CODE (insn) = old_code; + REG_NOTES (insn) = old_notes; + something_needs_elimination = 1; + } + + /* If this insn has no reloads, we need not do anything except + in the case of a CALL_INSN when we have caller-saves and + caller-save needs reloads. */ + + if (n_reloads == 0 + && ! (GET_CODE (insn) == CALL_INSN + && caller_save_spill_class != NO_REGS)) + continue; + + something_needs_reloads = 1; + + for (i = 0; i < N_REG_CLASSES; i++) + { + insn_needs[i] = 0, insn_groups[i] = 0; + insn_needs_for_inputs[i] = 0, insn_groups_for_inputs[i] = 0; + insn_needs_for_outputs[i] = 0, insn_groups_for_outputs[i] = 0; + insn_needs_for_insn[i] = 0, insn_groups_for_insn[i] = 0; + insn_needs_for_op_addr[i] = 0, insn_groups_for_op_addr[i] = 0; + insn_needs_for_other_addr[i] = 0; + insn_groups_for_other_addr[i] = 0; + } + + for (i = 0; i < reload_n_operands; i++) + { + insn_total_groups_for_in_addr[i] = 0; + insn_total_groups_for_out_addr[i] = 0; + + for (j = 0; j < N_REG_CLASSES; j++) + { + insn_needs_for_in_addr[i][j] = 0; + insn_needs_for_out_addr[i][j] = 0; + insn_groups_for_in_addr[i][j] = 0; + insn_groups_for_out_addr[i][j] = 0; + } + } + + /* Count each reload once in every class + containing the reload's own class. */ + + for (i = 0; i < n_reloads; i++) + { + register enum reg_class *p; + enum reg_class class = reload_reg_class[i]; + int size; + enum machine_mode mode; + int *this_groups; + int *this_needs; + int *this_total_groups; + + /* Don't count the dummy reloads, for which one of the + regs mentioned in the insn can be used for reloading. + Don't count optional reloads. + Don't count reloads that got combined with others. */ + if (reload_reg_rtx[i] != 0 + || reload_optional[i] != 0 + || (reload_out[i] == 0 && reload_in[i] == 0 + && ! reload_secondary_p[i])) + continue; + + /* Show that a reload register of this class is needed + in this basic block. We do not use insn_needs and + insn_groups because they are overly conservative for + this purpose. */ + if (global && ! basic_block_needs[(int) class][this_block]) + { + basic_block_needs[(int) class][this_block] = 1; + new_basic_block_needs = 1; + } + + /* Decide which time-of-use to count this reload for. */ + switch (reload_when_needed[i]) + { + case RELOAD_OTHER: + this_needs = insn_needs; + this_groups = insn_groups; + this_total_groups = &insn_total_groups; + break; + + case RELOAD_FOR_INPUT: + this_needs = insn_needs_for_inputs; + this_groups = insn_groups_for_inputs; + this_total_groups = &insn_total_groups_for_inputs; + break; + + case RELOAD_FOR_OUTPUT: + this_needs = insn_needs_for_outputs; + this_groups = insn_groups_for_outputs; + this_total_groups = &insn_total_groups_for_outputs; + break; + + case RELOAD_FOR_INSN: + this_needs = insn_needs_for_insn; + this_groups = insn_groups_for_outputs; + this_total_groups = &insn_total_groups_for_insn; + break; + + case RELOAD_FOR_OTHER_ADDRESS: + this_needs = insn_needs_for_other_addr; + this_groups = insn_groups_for_other_addr; + this_total_groups = &insn_total_groups_for_other_addr; + break; + + case RELOAD_FOR_INPUT_ADDRESS: + this_needs = insn_needs_for_in_addr[reload_opnum[i]]; + this_groups = insn_groups_for_in_addr[reload_opnum[i]]; + this_total_groups + = &insn_total_groups_for_in_addr[reload_opnum[i]]; + break; + + case RELOAD_FOR_OUTPUT_ADDRESS: + this_needs = insn_needs_for_out_addr[reload_opnum[i]]; + this_groups = insn_groups_for_out_addr[reload_opnum[i]]; + this_total_groups + = &insn_total_groups_for_out_addr[reload_opnum[i]]; + break; + + case RELOAD_FOR_OPERAND_ADDRESS: + this_needs = insn_needs_for_op_addr; + this_groups = insn_groups_for_op_addr; + this_total_groups = &insn_total_groups_for_op_addr; + break; + } + + mode = reload_inmode[i]; + if (GET_MODE_SIZE (reload_outmode[i]) > GET_MODE_SIZE (mode)) + mode = reload_outmode[i]; + size = CLASS_MAX_NREGS (class, mode); + if (size > 1) + { + enum machine_mode other_mode, allocate_mode; + + /* Count number of groups needed separately from + number of individual regs needed. */ + this_groups[(int) class]++; + p = reg_class_superclasses[(int) class]; + while (*p != LIM_REG_CLASSES) + this_groups[(int) *p++]++; + (*this_total_groups)++; + + /* Record size and mode of a group of this class. */ + /* If more than one size group is needed, + make all groups the largest needed size. */ + if (group_size[(int) class] < size) + { + other_mode = group_mode[(int) class]; + allocate_mode = mode; + + group_size[(int) class] = size; + group_mode[(int) class] = mode; + } + else + { + other_mode = mode; + allocate_mode = group_mode[(int) class]; + } + + /* Crash if two dissimilar machine modes both need + groups of consecutive regs of the same class. */ + + if (other_mode != VOIDmode + && other_mode != allocate_mode + && ! modes_equiv_for_class_p (allocate_mode, + other_mode, + class)) + abort (); + } + else if (size == 1) + { + this_needs[(int) class] += 1; + p = reg_class_superclasses[(int) class]; + while (*p != LIM_REG_CLASSES) + this_needs[(int) *p++] += 1; + } + else + abort (); + } + + /* All reloads have been counted for this insn; + now merge the various times of use. + This sets insn_needs, etc., to the maximum total number + of registers needed at any point in this insn. */ + + for (i = 0; i < N_REG_CLASSES; i++) + { + int in_max, out_max; + + for (in_max = 0, out_max = 0, j = 0; + j < reload_n_operands; j++) + { + in_max = MAX (in_max, insn_needs_for_in_addr[j][i]); + out_max = MAX (out_max, insn_needs_for_out_addr[j][i]); + } + + /* RELOAD_FOR_INSN reloads conflict with inputs, outputs, + and operand addresses but not things used to reload them. + Similarly, RELOAD_FOR_OPERAND_ADDRESS reloads don't + conflict with things needed to reload inputs or + outputs. */ + + in_max = MAX (in_max, insn_needs_for_op_addr[i]); + out_max = MAX (out_max, insn_needs_for_insn[i]); + + insn_needs_for_inputs[i] + = MAX (insn_needs_for_inputs[i] + + insn_needs_for_op_addr[i] + + insn_needs_for_insn[i], + in_max + insn_needs_for_inputs[i]); + + insn_needs_for_outputs[i] += out_max; + insn_needs[i] += MAX (MAX (insn_needs_for_inputs[i], + insn_needs_for_outputs[i]), + insn_needs_for_other_addr[i]); + + for (in_max = 0, out_max = 0, j = 0; + j < reload_n_operands; j++) + { + in_max = MAX (in_max, insn_groups_for_in_addr[j][i]); + out_max = MAX (out_max, insn_groups_for_out_addr[j][i]); + } + + in_max = MAX (in_max, insn_groups_for_op_addr[i]); + out_max = MAX (out_max, insn_groups_for_insn[i]); + + insn_groups_for_inputs[i] + = MAX (insn_groups_for_inputs[i] + + insn_groups_for_op_addr[i] + + insn_groups_for_insn[i], + in_max + insn_groups_for_inputs[i]); + + insn_groups_for_outputs[i] += out_max; + insn_groups[i] += MAX (MAX (insn_groups_for_inputs[i], + insn_groups_for_outputs[i]), + insn_groups_for_other_addr[i]); + } + + for (i = 0; i < reload_n_operands; i++) + { + max_total_input_groups + = MAX (max_total_input_groups, + insn_total_groups_for_in_addr[i]); + max_total_output_groups + = MAX (max_total_output_groups, + insn_total_groups_for_out_addr[i]); + } + + max_total_input_groups = MAX (max_total_input_groups, + insn_total_groups_for_op_addr); + max_total_output_groups = MAX (max_total_output_groups, + insn_total_groups_for_insn); + + insn_total_groups_for_inputs + = MAX (max_total_input_groups + insn_total_groups_for_op_addr + + insn_total_groups_for_insn, + max_total_input_groups + insn_total_groups_for_inputs); + + insn_total_groups_for_outputs += max_total_output_groups; + + insn_total_groups += MAX (MAX (insn_total_groups_for_outputs, + insn_total_groups_for_inputs), + insn_total_groups_for_other_addr); + + /* If this is a CALL_INSN and caller-saves will need + a spill register, act as if the spill register is + needed for this insn. However, the spill register + can be used by any reload of this insn, so we only + need do something if no need for that class has + been recorded. + + The assumption that every CALL_INSN will trigger a + caller-save is highly conservative, however, the number + of cases where caller-saves will need a spill register but + a block containing a CALL_INSN won't need a spill register + of that class should be quite rare. + + If a group is needed, the size and mode of the group will + have been set up at the beginning of this loop. */ + + if (GET_CODE (insn) == CALL_INSN + && caller_save_spill_class != NO_REGS) + { + int *caller_save_needs + = (caller_save_group_size > 1 ? insn_groups : insn_needs); + + if (caller_save_needs[(int) caller_save_spill_class] == 0) + { + register enum reg_class *p + = reg_class_superclasses[(int) caller_save_spill_class]; + + caller_save_needs[(int) caller_save_spill_class]++; + + while (*p != LIM_REG_CLASSES) + caller_save_needs[(int) *p++] += 1; + } + + if (caller_save_group_size > 1) + insn_total_groups = MAX (insn_total_groups, 1); + + + /* Show that this basic block will need a register of + this class. */ + + if (global + && ! (basic_block_needs[(int) caller_save_spill_class] + [this_block])) + { + basic_block_needs[(int) caller_save_spill_class] + [this_block] = 1; + new_basic_block_needs = 1; + } + } + +#ifdef SMALL_REGISTER_CLASSES + /* If this insn stores the value of a function call, + and that value is in a register that has been spilled, + and if the insn needs a reload in a class + that might use that register as the reload register, + then add add an extra need in that class. + This makes sure we have a register available that does + not overlap the return value. */ + if (avoid_return_reg) + { + int regno = REGNO (avoid_return_reg); + int nregs + = HARD_REGNO_NREGS (regno, GET_MODE (avoid_return_reg)); + int r; + int basic_needs[N_REG_CLASSES], basic_groups[N_REG_CLASSES]; + + /* First compute the "basic needs", which counts a + need only in the smallest class in which it + is required. */ + + bcopy (insn_needs, basic_needs, sizeof basic_needs); + bcopy (insn_groups, basic_groups, sizeof basic_groups); + + for (i = 0; i < N_REG_CLASSES; i++) + { + enum reg_class *p; + + if (basic_needs[i] >= 0) + for (p = reg_class_superclasses[i]; + *p != LIM_REG_CLASSES; p++) + basic_needs[(int) *p] -= basic_needs[i]; + + if (basic_groups[i] >= 0) + for (p = reg_class_superclasses[i]; + *p != LIM_REG_CLASSES; p++) + basic_groups[(int) *p] -= basic_groups[i]; + } + + /* Now count extra regs if there might be a conflict with + the return value register. + + ??? This is not quite correct because we don't properly + handle the case of groups, but if we end up doing + something wrong, it either will end up not mattering or + we will abort elsewhere. */ + + for (r = regno; r < regno + nregs; r++) + if (spill_reg_order[r] >= 0) + for (i = 0; i < N_REG_CLASSES; i++) + if (TEST_HARD_REG_BIT (reg_class_contents[i], r)) + { + if (basic_needs[i] > 0 || basic_groups[i] > 0) + { + enum reg_class *p; + + insn_needs[i]++; + p = reg_class_superclasses[i]; + while (*p != LIM_REG_CLASSES) + insn_needs[(int) *p++]++; + } + } + } +#endif /* SMALL_REGISTER_CLASSES */ + + /* For each class, collect maximum need of any insn. */ + + for (i = 0; i < N_REG_CLASSES; i++) + { + if (max_needs[i] < insn_needs[i]) + { + max_needs[i] = insn_needs[i]; + max_needs_insn[i] = insn; + } + if (max_groups[i] < insn_groups[i]) + { + max_groups[i] = insn_groups[i]; + max_groups_insn[i] = insn; + } + if (insn_total_groups > 0) + if (max_nongroups[i] < insn_needs[i]) + { + max_nongroups[i] = insn_needs[i]; + max_nongroups_insn[i] = insn; + } + } + } + /* Note that there is a continue statement above. */ + } + + /* If we allocated any new memory locations, make another pass + since it might have changed elimination offsets. */ + if (starting_frame_size != get_frame_size ()) + something_changed = 1; + + if (dumpfile) + for (i = 0; i < N_REG_CLASSES; i++) + { + if (max_needs[i] > 0) + fprintf (dumpfile, + ";; Need %d reg%s of class %s (for insn %d).\n", + max_needs[i], max_needs[i] == 1 ? "" : "s", + reg_class_names[i], INSN_UID (max_needs_insn[i])); + if (max_nongroups[i] > 0) + fprintf (dumpfile, + ";; Need %d nongroup reg%s of class %s (for insn %d).\n", + max_nongroups[i], max_nongroups[i] == 1 ? "" : "s", + reg_class_names[i], INSN_UID (max_nongroups_insn[i])); + if (max_groups[i] > 0) + fprintf (dumpfile, + ";; Need %d group%s (%smode) of class %s (for insn %d).\n", + max_groups[i], max_groups[i] == 1 ? "" : "s", + mode_name[(int) group_mode[i]], + reg_class_names[i], INSN_UID (max_groups_insn[i])); + } + + /* If we have caller-saves, set up the save areas and see if caller-save + will need a spill register. */ + + if (caller_save_needed + && ! setup_save_areas (&something_changed) + && caller_save_spill_class == NO_REGS) + { + /* The class we will need depends on whether the machine + supports the sum of two registers for an address; see + find_address_reloads for details. */ + + caller_save_spill_class + = double_reg_address_ok ? INDEX_REG_CLASS : BASE_REG_CLASS; + caller_save_group_size + = CLASS_MAX_NREGS (caller_save_spill_class, Pmode); + something_changed = 1; + } + + /* See if anything that happened changes which eliminations are valid. + For example, on the Sparc, whether or not the frame pointer can + be eliminated can depend on what registers have been used. We need + not check some conditions again (such as flag_omit_frame_pointer) + since they can't have changed. */ + + for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) + if ((ep->from == FRAME_POINTER_REGNUM && FRAME_POINTER_REQUIRED) +#ifdef ELIMINABLE_REGS + || ! CAN_ELIMINATE (ep->from, ep->to) +#endif + ) + ep->can_eliminate = 0; + + /* Look for the case where we have discovered that we can't replace + register A with register B and that means that we will now be + trying to replace register A with register C. This means we can + no longer replace register C with register B and we need to disable + such an elimination, if it exists. This occurs often with A == ap, + B == sp, and C == fp. */ + + for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) + { + struct elim_table *op; + register int new_to = -1; + + if (! ep->can_eliminate && ep->can_eliminate_previous) + { + /* Find the current elimination for ep->from, if there is a + new one. */ + for (op = reg_eliminate; + op < ®_eliminate[NUM_ELIMINABLE_REGS]; op++) + if (op->from == ep->from && op->can_eliminate) + { + new_to = op->to; + break; + } + + /* See if there is an elimination of NEW_TO -> EP->TO. If so, + disable it. */ + for (op = reg_eliminate; + op < ®_eliminate[NUM_ELIMINABLE_REGS]; op++) + if (op->from == new_to && op->to == ep->to) + op->can_eliminate = 0; + } + } + + /* See if any registers that we thought we could eliminate the previous + time are no longer eliminable. If so, something has changed and we + must spill the register. Also, recompute the number of eliminable + registers and see if the frame pointer is needed; it is if there is + no elimination of the frame pointer that we can perform. */ + + frame_pointer_needed = 1; + for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) + { + if (ep->can_eliminate && ep->from == FRAME_POINTER_REGNUM) + frame_pointer_needed = 0; + + if (! ep->can_eliminate && ep->can_eliminate_previous) + { + ep->can_eliminate_previous = 0; + spill_hard_reg (ep->from, global, dumpfile, 1); + regs_ever_live[ep->from] = 1; + something_changed = 1; + num_eliminable--; + } + } + + /* If all needs are met, we win. */ + + for (i = 0; i < N_REG_CLASSES; i++) + if (max_needs[i] > 0 || max_groups[i] > 0 || max_nongroups[i] > 0) + break; + if (i == N_REG_CLASSES && !new_basic_block_needs && ! something_changed) + break; + + /* Not all needs are met; must spill some hard regs. */ + + /* Put all registers spilled so far back in potential_reload_regs, but + put them at the front, since we've already spilled most of the + psuedos in them (we might have left some pseudos unspilled if they + were in a block that didn't need any spill registers of a conflicting + class. We used to try to mark off the need for those registers, + but doing so properly is very complex and reallocating them is the + simpler approach. First, "pack" potential_reload_regs by pushing + any nonnegative entries towards the end. That will leave room + for the registers we already spilled. + + Also, undo the marking of the spill registers from the last time + around in FORBIDDEN_REGS since we will be probably be allocating + them again below. + + ??? It is theoretically possible that we might end up not using one + of our previously-spilled registers in this allocation, even though + they are at the head of the list. It's not clear what to do about + this, but it was no better before, when we marked off the needs met + by the previously-spilled registers. With the current code, globals + can be allocated into these registers, but locals cannot. */ + + if (n_spills) + { + for (i = j = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--) + if (potential_reload_regs[i] != -1) + potential_reload_regs[j--] = potential_reload_regs[i]; + + for (i = 0; i < n_spills; i++) + { + potential_reload_regs[i] = spill_regs[i]; + spill_reg_order[spill_regs[i]] = -1; + CLEAR_HARD_REG_BIT (forbidden_regs, spill_regs[i]); + } + + n_spills = 0; + } + + /* Now find more reload regs to satisfy the remaining need + Do it by ascending class number, since otherwise a reg + might be spilled for a big class and might fail to count + for a smaller class even though it belongs to that class. + + Count spilled regs in `spills', and add entries to + `spill_regs' and `spill_reg_order'. + + ??? Note there is a problem here. + When there is a need for a group in a high-numbered class, + and also need for non-group regs that come from a lower class, + the non-group regs are chosen first. If there aren't many regs, + they might leave no room for a group. + + This was happening on the 386. To fix it, we added the code + that calls possible_group_p, so that the lower class won't + break up the last possible group. + + Really fixing the problem would require changes above + in counting the regs already spilled, and in choose_reload_regs. + It might be hard to avoid introducing bugs there. */ + + CLEAR_HARD_REG_SET (counted_for_groups); + CLEAR_HARD_REG_SET (counted_for_nongroups); + + for (class = 0; class < N_REG_CLASSES; class++) + { + /* First get the groups of registers. + If we got single registers first, we might fragment + possible groups. */ + while (max_groups[class] > 0) + { + /* If any single spilled regs happen to form groups, + count them now. Maybe we don't really need + to spill another group. */ + count_possible_groups (group_size, group_mode, max_groups); + + if (max_groups[class] <= 0) + break; + + /* Groups of size 2 (the only groups used on most machines) + are treated specially. */ + if (group_size[class] == 2) + { + /* First, look for a register that will complete a group. */ + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + int other; + + j = potential_reload_regs[i]; + if (j >= 0 && ! TEST_HARD_REG_BIT (bad_spill_regs, j) + && + ((j > 0 && (other = j - 1, spill_reg_order[other] >= 0) + && TEST_HARD_REG_BIT (reg_class_contents[class], j) + && TEST_HARD_REG_BIT (reg_class_contents[class], other) + && HARD_REGNO_MODE_OK (other, group_mode[class]) + && ! TEST_HARD_REG_BIT (counted_for_nongroups, + other) + /* We don't want one part of another group. + We could get "two groups" that overlap! */ + && ! TEST_HARD_REG_BIT (counted_for_groups, other)) + || + (j < FIRST_PSEUDO_REGISTER - 1 + && (other = j + 1, spill_reg_order[other] >= 0) + && TEST_HARD_REG_BIT (reg_class_contents[class], j) + && TEST_HARD_REG_BIT (reg_class_contents[class], other) + && HARD_REGNO_MODE_OK (j, group_mode[class]) + && ! TEST_HARD_REG_BIT (counted_for_nongroups, + other) + && ! TEST_HARD_REG_BIT (counted_for_groups, + other)))) + { + register enum reg_class *p; + + /* We have found one that will complete a group, + so count off one group as provided. */ + max_groups[class]--; + p = reg_class_superclasses[class]; + while (*p != LIM_REG_CLASSES) + max_groups[(int) *p++]--; + + /* Indicate both these regs are part of a group. */ + SET_HARD_REG_BIT (counted_for_groups, j); + SET_HARD_REG_BIT (counted_for_groups, other); + break; + } + } + /* We can't complete a group, so start one. */ + if (i == FIRST_PSEUDO_REGISTER) + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + int k; + j = potential_reload_regs[i]; + /* Verify that J+1 is a potential reload reg. */ + for (k = 0; k < FIRST_PSEUDO_REGISTER; k++) + if (potential_reload_regs[k] == j + 1) + break; + if (j >= 0 && j + 1 < FIRST_PSEUDO_REGISTER + && k < FIRST_PSEUDO_REGISTER + && spill_reg_order[j] < 0 && spill_reg_order[j + 1] < 0 + && TEST_HARD_REG_BIT (reg_class_contents[class], j) + && TEST_HARD_REG_BIT (reg_class_contents[class], j + 1) + && HARD_REGNO_MODE_OK (j, group_mode[class]) + && ! TEST_HARD_REG_BIT (counted_for_nongroups, + j + 1) + && ! TEST_HARD_REG_BIT (bad_spill_regs, j + 1)) + break; + } + + /* I should be the index in potential_reload_regs + of the new reload reg we have found. */ + + if (i >= FIRST_PSEUDO_REGISTER) + { + /* There are no groups left to spill. */ + spill_failure (max_groups_insn[class]); + failure = 1; + goto failed; + } + else + something_changed + |= new_spill_reg (i, class, max_needs, NULL_PTR, + global, dumpfile); + } + else + { + /* For groups of more than 2 registers, + look for a sufficient sequence of unspilled registers, + and spill them all at once. */ + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + int k; + + j = potential_reload_regs[i]; + if (j >= 0 + && j + group_size[class] <= FIRST_PSEUDO_REGISTER + && HARD_REGNO_MODE_OK (j, group_mode[class])) + { + /* Check each reg in the sequence. */ + for (k = 0; k < group_size[class]; k++) + if (! (spill_reg_order[j + k] < 0 + && ! TEST_HARD_REG_BIT (bad_spill_regs, j + k) + && TEST_HARD_REG_BIT (reg_class_contents[class], j + k))) + break; + /* We got a full sequence, so spill them all. */ + if (k == group_size[class]) + { + register enum reg_class *p; + for (k = 0; k < group_size[class]; k++) + { + int idx; + SET_HARD_REG_BIT (counted_for_groups, j + k); + for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++) + if (potential_reload_regs[idx] == j + k) + break; + something_changed + |= new_spill_reg (idx, class, + max_needs, NULL_PTR, + global, dumpfile); + } + + /* We have found one that will complete a group, + so count off one group as provided. */ + max_groups[class]--; + p = reg_class_superclasses[class]; + while (*p != LIM_REG_CLASSES) + max_groups[(int) *p++]--; + + break; + } + } + } + /* We couldn't find any registers for this reload. + Avoid going into an infinite loop. */ + if (i >= FIRST_PSEUDO_REGISTER) + { + /* There are no groups left. */ + spill_failure (max_groups_insn[class]); + failure = 1; + goto failed; + } + } + } + + /* Now similarly satisfy all need for single registers. */ + + while (max_needs[class] > 0 || max_nongroups[class] > 0) + { +#ifdef SMALL_REGISTER_CLASSES + /* This should be right for all machines, but only the 386 + is known to need it, so this conditional plays safe. + ??? For 2.5, try making this unconditional. */ + /* If we spilled enough regs, but they weren't counted + against the non-group need, see if we can count them now. + If so, we can avoid some actual spilling. */ + if (max_needs[class] <= 0 && max_nongroups[class] > 0) + for (i = 0; i < n_spills; i++) + if (TEST_HARD_REG_BIT (reg_class_contents[class], + spill_regs[i]) + && !TEST_HARD_REG_BIT (counted_for_groups, + spill_regs[i]) + && !TEST_HARD_REG_BIT (counted_for_nongroups, + spill_regs[i]) + && max_nongroups[class] > 0) + { + register enum reg_class *p; + + SET_HARD_REG_BIT (counted_for_nongroups, spill_regs[i]); + max_nongroups[class]--; + p = reg_class_superclasses[class]; + while (*p != LIM_REG_CLASSES) + max_nongroups[(int) *p++]--; + } + if (max_needs[class] <= 0 && max_nongroups[class] <= 0) + break; +#endif + + /* Consider the potential reload regs that aren't + yet in use as reload regs, in order of preference. + Find the most preferred one that's in this class. */ + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (potential_reload_regs[i] >= 0 + && TEST_HARD_REG_BIT (reg_class_contents[class], + potential_reload_regs[i]) + /* If this reg will not be available for groups, + pick one that does not foreclose possible groups. + This is a kludge, and not very general, + but it should be sufficient to make the 386 work, + and the problem should not occur on machines with + more registers. */ + && (max_nongroups[class] == 0 + || possible_group_p (potential_reload_regs[i], max_groups))) + break; + + /* If we couldn't get a register, try to get one even if we + might foreclose possible groups. This may cause problems + later, but that's better than aborting now, since it is + possible that we will, in fact, be able to form the needed + group even with this allocation. */ + + if (i >= FIRST_PSEUDO_REGISTER + && (asm_noperands (max_needs[class] > 0 + ? max_needs_insn[class] + : max_nongroups_insn[class]) + < 0)) + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (potential_reload_regs[i] >= 0 + && TEST_HARD_REG_BIT (reg_class_contents[class], + potential_reload_regs[i])) + break; + + /* I should be the index in potential_reload_regs + of the new reload reg we have found. */ + + if (i >= FIRST_PSEUDO_REGISTER) + { + /* There are no possible registers left to spill. */ + spill_failure (max_needs[class] > 0 ? max_needs_insn[class] + : max_nongroups_insn[class]); + failure = 1; + goto failed; + } + else + something_changed + |= new_spill_reg (i, class, max_needs, max_nongroups, + global, dumpfile); + } + } + } + + /* If global-alloc was run, notify it of any register eliminations we have + done. */ + if (global) + for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) + if (ep->can_eliminate) + mark_elimination (ep->from, ep->to); + + /* Insert code to save and restore call-clobbered hard regs + around calls. Tell if what mode to use so that we will process + those insns in reload_as_needed if we have to. */ + + if (caller_save_needed) + save_call_clobbered_regs (num_eliminable ? QImode + : caller_save_spill_class != NO_REGS ? HImode + : VOIDmode); + + /* If a pseudo has no hard reg, delete the insns that made the equivalence. + If that insn didn't set the register (i.e., it copied the register to + memory), just delete that insn instead of the equivalencing insn plus + anything now dead. If we call delete_dead_insn on that insn, we may + delete the insn that actually sets the register if the register die + there and that is incorrect. */ + + for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) + if (reg_renumber[i] < 0 && reg_equiv_init[i] != 0 + && GET_CODE (reg_equiv_init[i]) != NOTE) + { + if (reg_set_p (regno_reg_rtx[i], PATTERN (reg_equiv_init[i]))) + delete_dead_insn (reg_equiv_init[i]); + else + { + PUT_CODE (reg_equiv_init[i], NOTE); + NOTE_SOURCE_FILE (reg_equiv_init[i]) = 0; + NOTE_LINE_NUMBER (reg_equiv_init[i]) = NOTE_INSN_DELETED; + } + } + + /* Use the reload registers where necessary + by generating move instructions to move the must-be-register + values into or out of the reload registers. */ + + if (something_needs_reloads || something_needs_elimination + || (caller_save_needed && num_eliminable) + || caller_save_spill_class != NO_REGS) + reload_as_needed (first, global); + + /* If we were able to eliminate the frame pointer, show that it is no + longer live at the start of any basic block. If it ls live by + virtue of being in a pseudo, that pseudo will be marked live + and hence the frame pointer will be known to be live via that + pseudo. */ + + if (! frame_pointer_needed) + for (i = 0; i < n_basic_blocks; i++) + basic_block_live_at_start[i][FRAME_POINTER_REGNUM / REGSET_ELT_BITS] + &= ~ ((REGSET_ELT_TYPE) 1 << (FRAME_POINTER_REGNUM % REGSET_ELT_BITS)); + + /* Come here (with failure set nonzero) if we can't get enough spill regs + and we decide not to abort about it. */ + failed: + + reload_in_progress = 0; + + /* Now eliminate all pseudo regs by modifying them into + their equivalent memory references. + The REG-rtx's for the pseudos are modified in place, + so all insns that used to refer to them now refer to memory. + + For a reg that has a reg_equiv_address, all those insns + were changed by reloading so that no insns refer to it any longer; + but the DECL_RTL of a variable decl may refer to it, + and if so this causes the debugging info to mention the variable. */ + + for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) + { + rtx addr = 0; + int in_struct = 0; + if (reg_equiv_mem[i]) + { + addr = XEXP (reg_equiv_mem[i], 0); + in_struct = MEM_IN_STRUCT_P (reg_equiv_mem[i]); + } + if (reg_equiv_address[i]) + addr = reg_equiv_address[i]; + if (addr) + { + if (reg_renumber[i] < 0) + { + rtx reg = regno_reg_rtx[i]; + XEXP (reg, 0) = addr; + REG_USERVAR_P (reg) = 0; + MEM_IN_STRUCT_P (reg) = in_struct; + PUT_CODE (reg, MEM); + } + else if (reg_equiv_mem[i]) + XEXP (reg_equiv_mem[i], 0) = addr; + } + } + +#ifdef PRESERVE_DEATH_INFO_REGNO_P + /* Make a pass over all the insns and remove death notes for things that + are no longer registers or no longer die in the insn (e.g., an input + and output pseudo being tied). */ + + for (insn = first; insn; insn = NEXT_INSN (insn)) + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + rtx note, next; + + for (note = REG_NOTES (insn); note; note = next) + { + next = XEXP (note, 1); + if (REG_NOTE_KIND (note) == REG_DEAD + && (GET_CODE (XEXP (note, 0)) != REG + || reg_set_p (XEXP (note, 0), PATTERN (insn)))) + remove_note (insn, note); + } + } +#endif + + /* Indicate that we no longer have known memory locations or constants. */ + reg_equiv_constant = 0; + reg_equiv_memory_loc = 0; + + return failure; +} + +/* Nonzero if, after spilling reg REGNO for non-groups, + it will still be possible to find a group if we still need one. */ + +static int +possible_group_p (regno, max_groups) + int regno; + int *max_groups; +{ + int i; + int class = (int) NO_REGS; + + for (i = 0; i < (int) N_REG_CLASSES; i++) + if (max_groups[i] > 0) + { + class = i; + break; + } + + if (class == (int) NO_REGS) + return 1; + + /* Consider each pair of consecutive registers. */ + for (i = 0; i < FIRST_PSEUDO_REGISTER - 1; i++) + { + /* Ignore pairs that include reg REGNO. */ + if (i == regno || i + 1 == regno) + continue; + + /* Ignore pairs that are outside the class that needs the group. + ??? Here we fail to handle the case where two different classes + independently need groups. But this never happens with our + current machine descriptions. */ + if (! (TEST_HARD_REG_BIT (reg_class_contents[class], i) + && TEST_HARD_REG_BIT (reg_class_contents[class], i + 1))) + continue; + + /* A pair of consecutive regs we can still spill does the trick. */ + if (spill_reg_order[i] < 0 && spill_reg_order[i + 1] < 0 + && ! TEST_HARD_REG_BIT (bad_spill_regs, i) + && ! TEST_HARD_REG_BIT (bad_spill_regs, i + 1)) + return 1; + + /* A pair of one already spilled and one we can spill does it + provided the one already spilled is not otherwise reserved. */ + if (spill_reg_order[i] < 0 + && ! TEST_HARD_REG_BIT (bad_spill_regs, i) + && spill_reg_order[i + 1] >= 0 + && ! TEST_HARD_REG_BIT (counted_for_groups, i + 1) + && ! TEST_HARD_REG_BIT (counted_for_nongroups, i + 1)) + return 1; + if (spill_reg_order[i + 1] < 0 + && ! TEST_HARD_REG_BIT (bad_spill_regs, i + 1) + && spill_reg_order[i] >= 0 + && ! TEST_HARD_REG_BIT (counted_for_groups, i) + && ! TEST_HARD_REG_BIT (counted_for_nongroups, i)) + return 1; + } + + return 0; +} + +/* Count any groups that can be formed from the registers recently spilled. + This is done class by class, in order of ascending class number. */ + +static void +count_possible_groups (group_size, group_mode, max_groups) + int *group_size; + enum machine_mode *group_mode; + int *max_groups; +{ + int i; + /* Now find all consecutive groups of spilled registers + and mark each group off against the need for such groups. + But don't count them against ordinary need, yet. */ + + for (i = 0; i < N_REG_CLASSES; i++) + if (group_size[i] > 1) + { + HARD_REG_SET new; + int j; + + CLEAR_HARD_REG_SET (new); + + /* Make a mask of all the regs that are spill regs in class I. */ + for (j = 0; j < n_spills; j++) + if (TEST_HARD_REG_BIT (reg_class_contents[i], spill_regs[j]) + && ! TEST_HARD_REG_BIT (counted_for_groups, spill_regs[j]) + && ! TEST_HARD_REG_BIT (counted_for_nongroups, + spill_regs[j])) + SET_HARD_REG_BIT (new, spill_regs[j]); + + /* Find each consecutive group of them. */ + for (j = 0; j < FIRST_PSEUDO_REGISTER && max_groups[i] > 0; j++) + if (TEST_HARD_REG_BIT (new, j) + && j + group_size[i] <= FIRST_PSEUDO_REGISTER + /* Next line in case group-mode for this class + demands an even-odd pair. */ + && HARD_REGNO_MODE_OK (j, group_mode[i])) + { + int k; + for (k = 1; k < group_size[i]; k++) + if (! TEST_HARD_REG_BIT (new, j + k)) + break; + if (k == group_size[i]) + { + /* We found a group. Mark it off against this class's + need for groups, and against each superclass too. */ + register enum reg_class *p; + max_groups[i]--; + p = reg_class_superclasses[i]; + while (*p != LIM_REG_CLASSES) + max_groups[(int) *p++]--; + /* Don't count these registers again. */ + for (k = 0; k < group_size[i]; k++) + SET_HARD_REG_BIT (counted_for_groups, j + k); + } + /* Skip to the last reg in this group. When j is incremented + above, it will then point to the first reg of the next + possible group. */ + j += k - 1; + } + } + +} + +/* ALLOCATE_MODE is a register mode that needs to be reloaded. OTHER_MODE is + another mode that needs to be reloaded for the same register class CLASS. + If any reg in CLASS allows ALLOCATE_MODE but not OTHER_MODE, fail. + ALLOCATE_MODE will never be smaller than OTHER_MODE. + + This code used to also fail if any reg in CLASS allows OTHER_MODE but not + ALLOCATE_MODE. This test is unnecessary, because we will never try to put + something of mode ALLOCATE_MODE into an OTHER_MODE register. Testing this + causes unnecessary failures on machines requiring alignment of register + groups when the two modes are different sizes, because the larger mode has + more strict alignment rules than the smaller mode. */ + +static int +modes_equiv_for_class_p (allocate_mode, other_mode, class) + enum machine_mode allocate_mode, other_mode; + enum reg_class class; +{ + register int regno; + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + if (TEST_HARD_REG_BIT (reg_class_contents[(int) class], regno) + && HARD_REGNO_MODE_OK (regno, allocate_mode) + && ! HARD_REGNO_MODE_OK (regno, other_mode)) + return 0; + } + return 1; +} + +/* Handle the failure to find a register to spill. + INSN should be one of the insns which needed this particular spill reg. */ + +static void +spill_failure (insn) + rtx insn; +{ + if (asm_noperands (PATTERN (insn)) >= 0) + error_for_asm (insn, "`asm' needs too many reloads"); + else + abort (); +} + +/* Add a new register to the tables of available spill-registers + (as well as spilling all pseudos allocated to the register). + I is the index of this register in potential_reload_regs. + CLASS is the regclass whose need is being satisfied. + MAX_NEEDS and MAX_NONGROUPS are the vectors of needs, + so that this register can count off against them. + MAX_NONGROUPS is 0 if this register is part of a group. + GLOBAL and DUMPFILE are the same as the args that `reload' got. */ + +static int +new_spill_reg (i, class, max_needs, max_nongroups, global, dumpfile) + int i; + int class; + int *max_needs; + int *max_nongroups; + int global; + FILE *dumpfile; +{ + register enum reg_class *p; + int val; + int regno = potential_reload_regs[i]; + + if (i >= FIRST_PSEUDO_REGISTER) + abort (); /* Caller failed to find any register. */ + + if (fixed_regs[regno] || TEST_HARD_REG_BIT (forbidden_regs, regno)) + fatal ("fixed or forbidden register was spilled.\n\ +This may be due to a compiler bug or to impossible asm statements."); + + /* Make reg REGNO an additional reload reg. */ + + potential_reload_regs[i] = -1; + spill_regs[n_spills] = regno; + spill_reg_order[regno] = n_spills; + if (dumpfile) + fprintf (dumpfile, "Spilling reg %d.\n", spill_regs[n_spills]); + + /* Clear off the needs we just satisfied. */ + + max_needs[class]--; + p = reg_class_superclasses[class]; + while (*p != LIM_REG_CLASSES) + max_needs[(int) *p++]--; + + if (max_nongroups && max_nongroups[class] > 0) + { + SET_HARD_REG_BIT (counted_for_nongroups, regno); + max_nongroups[class]--; + p = reg_class_superclasses[class]; + while (*p != LIM_REG_CLASSES) + max_nongroups[(int) *p++]--; + } + + /* Spill every pseudo reg that was allocated to this reg + or to something that overlaps this reg. */ + + val = spill_hard_reg (spill_regs[n_spills], global, dumpfile, 0); + + /* If there are some registers still to eliminate and this register + wasn't ever used before, additional stack space may have to be + allocated to store this register. Thus, we may have changed the offset + between the stack and frame pointers, so mark that something has changed. + (If new pseudos were spilled, thus requiring more space, VAL would have + been set non-zero by the call to spill_hard_reg above since additional + reloads may be needed in that case. + + One might think that we need only set VAL to 1 if this is a call-used + register. However, the set of registers that must be saved by the + prologue is not identical to the call-used set. For example, the + register used by the call insn for the return PC is a call-used register, + but must be saved by the prologue. */ + if (num_eliminable && ! regs_ever_live[spill_regs[n_spills]]) + val = 1; + + regs_ever_live[spill_regs[n_spills]] = 1; + n_spills++; + + return val; +} + +/* Delete an unneeded INSN and any previous insns who sole purpose is loading + data that is dead in INSN. */ + +static void +delete_dead_insn (insn) + rtx insn; +{ + rtx prev = prev_real_insn (insn); + rtx prev_dest; + + /* If the previous insn sets a register that dies in our insn, delete it + too. */ + if (prev && GET_CODE (PATTERN (prev)) == SET + && (prev_dest = SET_DEST (PATTERN (prev)), GET_CODE (prev_dest) == REG) + && reg_mentioned_p (prev_dest, PATTERN (insn)) + && find_regno_note (insn, REG_DEAD, REGNO (prev_dest))) + delete_dead_insn (prev); + + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; +} + +/* Modify the home of pseudo-reg I. + The new home is present in reg_renumber[I]. + + FROM_REG may be the hard reg that the pseudo-reg is being spilled from; + or it may be -1, meaning there is none or it is not relevant. + This is used so that all pseudos spilled from a given hard reg + can share one stack slot. */ + +static void +alter_reg (i, from_reg) + register int i; + int from_reg; +{ + /* When outputting an inline function, this can happen + for a reg that isn't actually used. */ + if (regno_reg_rtx[i] == 0) + return; + + /* If the reg got changed to a MEM at rtl-generation time, + ignore it. */ + if (GET_CODE (regno_reg_rtx[i]) != REG) + return; + + /* Modify the reg-rtx to contain the new hard reg + number or else to contain its pseudo reg number. */ + REGNO (regno_reg_rtx[i]) + = reg_renumber[i] >= 0 ? reg_renumber[i] : i; + + /* If we have a pseudo that is needed but has no hard reg or equivalent, + allocate a stack slot for it. */ + + if (reg_renumber[i] < 0 + && reg_n_refs[i] > 0 + && reg_equiv_constant[i] == 0 + && reg_equiv_memory_loc[i] == 0) + { + register rtx x; + int inherent_size = PSEUDO_REGNO_BYTES (i); + int total_size = MAX (inherent_size, reg_max_ref_width[i]); + int adjust = 0; + + /* Each pseudo reg has an inherent size which comes from its own mode, + and a total size which provides room for paradoxical subregs + which refer to the pseudo reg in wider modes. + + We can use a slot already allocated if it provides both + enough inherent space and enough total space. + Otherwise, we allocate a new slot, making sure that it has no less + inherent space, and no less total space, then the previous slot. */ + if (from_reg == -1) + { + /* No known place to spill from => no slot to reuse. */ + x = assign_stack_local (GET_MODE (regno_reg_rtx[i]), total_size, -1); +#if BYTES_BIG_ENDIAN + /* Cancel the big-endian correction done in assign_stack_local. + Get the address of the beginning of the slot. + This is so we can do a big-endian correction unconditionally + below. */ + adjust = inherent_size - total_size; +#endif + } + /* Reuse a stack slot if possible. */ + else if (spill_stack_slot[from_reg] != 0 + && spill_stack_slot_width[from_reg] >= total_size + && (GET_MODE_SIZE (GET_MODE (spill_stack_slot[from_reg])) + >= inherent_size)) + x = spill_stack_slot[from_reg]; + /* Allocate a bigger slot. */ + else + { + /* Compute maximum size needed, both for inherent size + and for total size. */ + enum machine_mode mode = GET_MODE (regno_reg_rtx[i]); + if (spill_stack_slot[from_reg]) + { + if (GET_MODE_SIZE (GET_MODE (spill_stack_slot[from_reg])) + > inherent_size) + mode = GET_MODE (spill_stack_slot[from_reg]); + if (spill_stack_slot_width[from_reg] > total_size) + total_size = spill_stack_slot_width[from_reg]; + } + /* Make a slot with that size. */ + x = assign_stack_local (mode, total_size, -1); +#if BYTES_BIG_ENDIAN + /* Cancel the big-endian correction done in assign_stack_local. + Get the address of the beginning of the slot. + This is so we can do a big-endian correction unconditionally + below. */ + adjust = GET_MODE_SIZE (mode) - total_size; +#endif + spill_stack_slot[from_reg] = x; + spill_stack_slot_width[from_reg] = total_size; + } + +#if BYTES_BIG_ENDIAN + /* On a big endian machine, the "address" of the slot + is the address of the low part that fits its inherent mode. */ + if (inherent_size < total_size) + adjust += (total_size - inherent_size); +#endif /* BYTES_BIG_ENDIAN */ + + /* If we have any adjustment to make, or if the stack slot is the + wrong mode, make a new stack slot. */ + if (adjust != 0 || GET_MODE (x) != GET_MODE (regno_reg_rtx[i])) + { + x = gen_rtx (MEM, GET_MODE (regno_reg_rtx[i]), + plus_constant (XEXP (x, 0), adjust)); + RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (regno_reg_rtx[i]); + } + + /* Save the stack slot for later. */ + reg_equiv_memory_loc[i] = x; + } +} + +/* Mark the slots in regs_ever_live for the hard regs + used by pseudo-reg number REGNO. */ + +void +mark_home_live (regno) + int regno; +{ + register int i, lim; + i = reg_renumber[regno]; + if (i < 0) + return; + lim = i + HARD_REGNO_NREGS (i, PSEUDO_REGNO_MODE (regno)); + while (i < lim) + regs_ever_live[i++] = 1; +} + +/* This function handles the tracking of elimination offsets around branches. + + X is a piece of RTL being scanned. + + INSN is the insn that it came from, if any. + + INITIAL_P is non-zero if we are to set the offset to be the initial + offset and zero if we are setting the offset of the label to be the + current offset. */ + +static void +set_label_offsets (x, insn, initial_p) + rtx x; + rtx insn; + int initial_p; +{ + enum rtx_code code = GET_CODE (x); + rtx tem; + int i; + struct elim_table *p; + + switch (code) + { + case LABEL_REF: + if (LABEL_REF_NONLOCAL_P (x)) + return; + + x = XEXP (x, 0); + + /* ... fall through ... */ + + case CODE_LABEL: + /* If we know nothing about this label, set the desired offsets. Note + that this sets the offset at a label to be the offset before a label + if we don't know anything about the label. This is not correct for + the label after a BARRIER, but is the best guess we can make. If + we guessed wrong, we will suppress an elimination that might have + been possible had we been able to guess correctly. */ + + if (! offsets_known_at[CODE_LABEL_NUMBER (x)]) + { + for (i = 0; i < NUM_ELIMINABLE_REGS; i++) + offsets_at[CODE_LABEL_NUMBER (x)][i] + = (initial_p ? reg_eliminate[i].initial_offset + : reg_eliminate[i].offset); + offsets_known_at[CODE_LABEL_NUMBER (x)] = 1; + } + + /* Otherwise, if this is the definition of a label and it is + preceded by a BARRIER, set our offsets to the known offset of + that label. */ + + else if (x == insn + && (tem = prev_nonnote_insn (insn)) != 0 + && GET_CODE (tem) == BARRIER) + { + num_not_at_initial_offset = 0; + for (i = 0; i < NUM_ELIMINABLE_REGS; i++) + { + reg_eliminate[i].offset = reg_eliminate[i].previous_offset + = offsets_at[CODE_LABEL_NUMBER (x)][i]; + if (reg_eliminate[i].can_eliminate + && (reg_eliminate[i].offset + != reg_eliminate[i].initial_offset)) + num_not_at_initial_offset++; + } + } + + else + /* If neither of the above cases is true, compare each offset + with those previously recorded and suppress any eliminations + where the offsets disagree. */ + + for (i = 0; i < NUM_ELIMINABLE_REGS; i++) + if (offsets_at[CODE_LABEL_NUMBER (x)][i] + != (initial_p ? reg_eliminate[i].initial_offset + : reg_eliminate[i].offset)) + reg_eliminate[i].can_eliminate = 0; + + return; + + case JUMP_INSN: + set_label_offsets (PATTERN (insn), insn, initial_p); + + /* ... fall through ... */ + + case INSN: + case CALL_INSN: + /* Any labels mentioned in REG_LABEL notes can be branched to indirectly + and hence must have all eliminations at their initial offsets. */ + for (tem = REG_NOTES (x); tem; tem = XEXP (tem, 1)) + if (REG_NOTE_KIND (tem) == REG_LABEL) + set_label_offsets (XEXP (tem, 0), insn, 1); + return; + + case ADDR_VEC: + case ADDR_DIFF_VEC: + /* Each of the labels in the address vector must be at their initial + offsets. We want the first first for ADDR_VEC and the second + field for ADDR_DIFF_VEC. */ + + for (i = 0; i < XVECLEN (x, code == ADDR_DIFF_VEC); i++) + set_label_offsets (XVECEXP (x, code == ADDR_DIFF_VEC, i), + insn, initial_p); + return; + + case SET: + /* We only care about setting PC. If the source is not RETURN, + IF_THEN_ELSE, or a label, disable any eliminations not at + their initial offsets. Similarly if any arm of the IF_THEN_ELSE + isn't one of those possibilities. For branches to a label, + call ourselves recursively. + + Note that this can disable elimination unnecessarily when we have + a non-local goto since it will look like a non-constant jump to + someplace in the current function. This isn't a significant + problem since such jumps will normally be when all elimination + pairs are back to their initial offsets. */ + + if (SET_DEST (x) != pc_rtx) + return; + + switch (GET_CODE (SET_SRC (x))) + { + case PC: + case RETURN: + return; + + case LABEL_REF: + set_label_offsets (XEXP (SET_SRC (x), 0), insn, initial_p); + return; + + case IF_THEN_ELSE: + tem = XEXP (SET_SRC (x), 1); + if (GET_CODE (tem) == LABEL_REF) + set_label_offsets (XEXP (tem, 0), insn, initial_p); + else if (GET_CODE (tem) != PC && GET_CODE (tem) != RETURN) + break; + + tem = XEXP (SET_SRC (x), 2); + if (GET_CODE (tem) == LABEL_REF) + set_label_offsets (XEXP (tem, 0), insn, initial_p); + else if (GET_CODE (tem) != PC && GET_CODE (tem) != RETURN) + break; + return; + } + + /* If we reach here, all eliminations must be at their initial + offset because we are doing a jump to a variable address. */ + for (p = reg_eliminate; p < ®_eliminate[NUM_ELIMINABLE_REGS]; p++) + if (p->offset != p->initial_offset) + p->can_eliminate = 0; + } +} + +/* Used for communication between the next two function to properly share + the vector for an ASM_OPERANDS. */ + +static struct rtvec_def *old_asm_operands_vec, *new_asm_operands_vec; + +/* Scan X and replace any eliminable registers (such as fp) with a + replacement (such as sp), plus an offset. + + MEM_MODE is the mode of an enclosing MEM. We need this to know how + much to adjust a register for, e.g., PRE_DEC. Also, if we are inside a + MEM, we are allowed to replace a sum of a register and the constant zero + with the register, which we cannot do outside a MEM. In addition, we need + to record the fact that a register is referenced outside a MEM. + + If INSN is nonzero, it is the insn containing X. If we replace a REG + in a SET_DEST with an equivalent MEM and INSN is non-zero, write a + CLOBBER of the pseudo after INSN so find_equiv_regs will know that + that the REG is being modified. + + If we see a modification to a register we know about, take the + appropriate action (see case SET, below). + + REG_EQUIV_MEM and REG_EQUIV_ADDRESS contain address that have had + replacements done assuming all offsets are at their initial values. If + they are not, or if REG_EQUIV_ADDRESS is nonzero for a pseudo we + encounter, return the actual location so that find_reloads will do + the proper thing. */ + +rtx +eliminate_regs (x, mem_mode, insn) + rtx x; + enum machine_mode mem_mode; + rtx insn; +{ + enum rtx_code code = GET_CODE (x); + struct elim_table *ep; + int regno; + rtx new; + int i, j; + char *fmt; + int copied = 0; + + switch (code) + { + case CONST_INT: + case CONST_DOUBLE: + case CONST: + case SYMBOL_REF: + case CODE_LABEL: + case PC: + case CC0: + case ASM_INPUT: + case ADDR_VEC: + case ADDR_DIFF_VEC: + case RETURN: + return x; + + case REG: + regno = REGNO (x); + + /* First handle the case where we encounter a bare register that + is eliminable. Replace it with a PLUS. */ + if (regno < FIRST_PSEUDO_REGISTER) + { + for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; + ep++) + if (ep->from_rtx == x && ep->can_eliminate) + { + if (! mem_mode) + ep->ref_outside_mem = 1; + return plus_constant (ep->to_rtx, ep->previous_offset); + } + + } + else if (reg_equiv_memory_loc && reg_equiv_memory_loc[regno] + && (reg_equiv_address[regno] || num_not_at_initial_offset)) + { + /* In this case, find_reloads would attempt to either use an + incorrect address (if something is not at its initial offset) + or substitute an replaced address into an insn (which loses + if the offset is changed by some later action). So we simply + return the replaced stack slot (assuming it is changed by + elimination) and ignore the fact that this is actually a + reference to the pseudo. Ensure we make a copy of the + address in case it is shared. */ + new = eliminate_regs (reg_equiv_memory_loc[regno], + mem_mode, NULL_RTX); + if (new != reg_equiv_memory_loc[regno]) + { + cannot_omit_stores[regno] = 1; + return copy_rtx (new); + } + } + return x; + + case PLUS: + /* If this is the sum of an eliminable register and a constant, rework + the sum. */ + if (GET_CODE (XEXP (x, 0)) == REG + && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER + && CONSTANT_P (XEXP (x, 1))) + { + for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; + ep++) + if (ep->from_rtx == XEXP (x, 0) && ep->can_eliminate) + { + if (! mem_mode) + ep->ref_outside_mem = 1; + + /* The only time we want to replace a PLUS with a REG (this + occurs when the constant operand of the PLUS is the negative + of the offset) is when we are inside a MEM. We won't want + to do so at other times because that would change the + structure of the insn in a way that reload can't handle. + We special-case the commonest situation in + eliminate_regs_in_insn, so just replace a PLUS with a + PLUS here, unless inside a MEM. */ + if (mem_mode != 0 && GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) == - ep->previous_offset) + return ep->to_rtx; + else + return gen_rtx (PLUS, Pmode, ep->to_rtx, + plus_constant (XEXP (x, 1), + ep->previous_offset)); + } + + /* If the register is not eliminable, we are done since the other + operand is a constant. */ + return x; + } + + /* If this is part of an address, we want to bring any constant to the + outermost PLUS. We will do this by doing register replacement in + our operands and seeing if a constant shows up in one of them. + + We assume here this is part of an address (or a "load address" insn) + since an eliminable register is not likely to appear in any other + context. + + If we have (plus (eliminable) (reg)), we want to produce + (plus (plus (replacement) (reg) (const))). If this was part of a + normal add insn, (plus (replacement) (reg)) will be pushed as a + reload. This is the desired action. */ + + { + rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, NULL_RTX); + rtx new1 = eliminate_regs (XEXP (x, 1), mem_mode, NULL_RTX); + + if (new0 != XEXP (x, 0) || new1 != XEXP (x, 1)) + { + /* If one side is a PLUS and the other side is a pseudo that + didn't get a hard register but has a reg_equiv_constant, + we must replace the constant here since it may no longer + be in the position of any operand. */ + if (GET_CODE (new0) == PLUS && GET_CODE (new1) == REG + && REGNO (new1) >= FIRST_PSEUDO_REGISTER + && reg_renumber[REGNO (new1)] < 0 + && reg_equiv_constant != 0 + && reg_equiv_constant[REGNO (new1)] != 0) + new1 = reg_equiv_constant[REGNO (new1)]; + else if (GET_CODE (new1) == PLUS && GET_CODE (new0) == REG + && REGNO (new0) >= FIRST_PSEUDO_REGISTER + && reg_renumber[REGNO (new0)] < 0 + && reg_equiv_constant[REGNO (new0)] != 0) + new0 = reg_equiv_constant[REGNO (new0)]; + + new = form_sum (new0, new1); + + /* As above, if we are not inside a MEM we do not want to + turn a PLUS into something else. We might try to do so here + for an addition of 0 if we aren't optimizing. */ + if (! mem_mode && GET_CODE (new) != PLUS) + return gen_rtx (PLUS, GET_MODE (x), new, const0_rtx); + else + return new; + } + } + return x; + + case EXPR_LIST: + /* If we have something in XEXP (x, 0), the usual case, eliminate it. */ + if (XEXP (x, 0)) + { + new = eliminate_regs (XEXP (x, 0), mem_mode, NULL_RTX); + if (new != XEXP (x, 0)) + x = gen_rtx (EXPR_LIST, REG_NOTE_KIND (x), new, XEXP (x, 1)); + } + + /* ... fall through ... */ + + case INSN_LIST: + /* Now do eliminations in the rest of the chain. If this was + an EXPR_LIST, this might result in allocating more memory than is + strictly needed, but it simplifies the code. */ + if (XEXP (x, 1)) + { + new = eliminate_regs (XEXP (x, 1), mem_mode, NULL_RTX); + if (new != XEXP (x, 1)) + return gen_rtx (INSN_LIST, GET_MODE (x), XEXP (x, 0), new); + } + return x; + + case CALL: + case COMPARE: + case MINUS: + case MULT: + case DIV: case UDIV: + case MOD: case UMOD: + case AND: case IOR: case XOR: + case LSHIFT: case ASHIFT: case ROTATE: + case ASHIFTRT: case LSHIFTRT: case ROTATERT: + case NE: case EQ: + case GE: case GT: case GEU: case GTU: + case LE: case LT: case LEU: case LTU: + { + rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, NULL_RTX); + rtx new1 + = XEXP (x, 1) ? eliminate_regs (XEXP (x, 1), mem_mode, NULL_RTX) : 0; + + if (new0 != XEXP (x, 0) || new1 != XEXP (x, 1)) + return gen_rtx (code, GET_MODE (x), new0, new1); + } + return x; + + case PRE_INC: + case POST_INC: + case PRE_DEC: + case POST_DEC: + for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) + if (ep->to_rtx == XEXP (x, 0)) + { + int size = GET_MODE_SIZE (mem_mode); + + /* If more bytes than MEM_MODE are pushed, account for them. */ +#ifdef PUSH_ROUNDING + if (ep->to_rtx == stack_pointer_rtx) + size = PUSH_ROUNDING (size); +#endif + if (code == PRE_DEC || code == POST_DEC) + ep->offset += size; + else + ep->offset -= size; + } + + /* Fall through to generic unary operation case. */ + case USE: + case STRICT_LOW_PART: + case NEG: case NOT: + case SIGN_EXTEND: case ZERO_EXTEND: + case TRUNCATE: case FLOAT_EXTEND: case FLOAT_TRUNCATE: + case FLOAT: case FIX: + case UNSIGNED_FIX: case UNSIGNED_FLOAT: + case ABS: + case SQRT: + case FFS: + new = eliminate_regs (XEXP (x, 0), mem_mode, NULL_RTX); + if (new != XEXP (x, 0)) + return gen_rtx (code, GET_MODE (x), new); + return x; + + case SUBREG: + /* Similar to above processing, but preserve SUBREG_WORD. + Convert (subreg (mem)) to (mem) if not paradoxical. + Also, if we have a non-paradoxical (subreg (pseudo)) and the + pseudo didn't get a hard reg, we must replace this with the + eliminated version of the memory location because push_reloads + may do the replacement in certain circumstances. */ + if (GET_CODE (SUBREG_REG (x)) == REG + && (GET_MODE_SIZE (GET_MODE (x)) + <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) + && reg_equiv_memory_loc != 0 + && reg_equiv_memory_loc[REGNO (SUBREG_REG (x))] != 0) + { + new = eliminate_regs (reg_equiv_memory_loc[REGNO (SUBREG_REG (x))], + mem_mode, NULL_RTX); + + /* If we didn't change anything, we must retain the pseudo. */ + if (new == reg_equiv_memory_loc[REGNO (SUBREG_REG (x))]) + new = XEXP (x, 0); + else + /* Otherwise, ensure NEW isn't shared in case we have to reload + it. */ + new = copy_rtx (new); + } + else + new = eliminate_regs (SUBREG_REG (x), mem_mode, NULL_RTX); + + if (new != XEXP (x, 0)) + { + if (GET_CODE (new) == MEM + && (GET_MODE_SIZE (GET_MODE (x)) + <= GET_MODE_SIZE (GET_MODE (new))) +#if defined(BYTES_LOADS_ZERO_EXTEND) || defined(BYTE_LOADS_SIGN_EXTEND) + /* On these machines we will be reloading what is + inside the SUBREG if it originally was a pseudo and + the inner and outer modes are both a word or + smaller. So leave the SUBREG then. */ + && ! (GET_CODE (SUBREG_REG (x)) == REG + && GET_MODE_SIZE (GET_MODE (x)) <= UNITS_PER_WORD + && GET_MODE_SIZE (GET_MODE (new)) <= UNITS_PER_WORD) +#endif + ) + { + int offset = SUBREG_WORD (x) * UNITS_PER_WORD; + enum machine_mode mode = GET_MODE (x); + +#if BYTES_BIG_ENDIAN + offset += (MIN (UNITS_PER_WORD, + GET_MODE_SIZE (GET_MODE (new))) + - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))); +#endif + + PUT_MODE (new, mode); + XEXP (new, 0) = plus_constant (XEXP (new, 0), offset); + return new; + } + else + return gen_rtx (SUBREG, GET_MODE (x), new, SUBREG_WORD (x)); + } + + return x; + + case CLOBBER: + /* If clobbering a register that is the replacement register for an + elimination we still think can be performed, note that it cannot + be performed. Otherwise, we need not be concerned about it. */ + for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) + if (ep->to_rtx == XEXP (x, 0)) + ep->can_eliminate = 0; + + new = eliminate_regs (XEXP (x, 0), mem_mode, NULL_RTX); + if (new != XEXP (x, 0)) + return gen_rtx (code, GET_MODE (x), new); + return x; + + case ASM_OPERANDS: + { + rtx *temp_vec; + /* Properly handle sharing input and constraint vectors. */ + if (ASM_OPERANDS_INPUT_VEC (x) != old_asm_operands_vec) + { + /* When we come to a new vector not seen before, + scan all its elements; keep the old vector if none + of them changes; otherwise, make a copy. */ + old_asm_operands_vec = ASM_OPERANDS_INPUT_VEC (x); + temp_vec = (rtx *) alloca (XVECLEN (x, 3) * sizeof (rtx)); + for (i = 0; i < ASM_OPERANDS_INPUT_LENGTH (x); i++) + temp_vec[i] = eliminate_regs (ASM_OPERANDS_INPUT (x, i), + mem_mode, NULL_RTX); + + for (i = 0; i < ASM_OPERANDS_INPUT_LENGTH (x); i++) + if (temp_vec[i] != ASM_OPERANDS_INPUT (x, i)) + break; + + if (i == ASM_OPERANDS_INPUT_LENGTH (x)) + new_asm_operands_vec = old_asm_operands_vec; + else + new_asm_operands_vec + = gen_rtvec_v (ASM_OPERANDS_INPUT_LENGTH (x), temp_vec); + } + + /* If we had to copy the vector, copy the entire ASM_OPERANDS. */ + if (new_asm_operands_vec == old_asm_operands_vec) + return x; + + new = gen_rtx (ASM_OPERANDS, VOIDmode, ASM_OPERANDS_TEMPLATE (x), + ASM_OPERANDS_OUTPUT_CONSTRAINT (x), + ASM_OPERANDS_OUTPUT_IDX (x), new_asm_operands_vec, + ASM_OPERANDS_INPUT_CONSTRAINT_VEC (x), + ASM_OPERANDS_SOURCE_FILE (x), + ASM_OPERANDS_SOURCE_LINE (x)); + new->volatil = x->volatil; + return new; + } + + case SET: + /* Check for setting a register that we know about. */ + if (GET_CODE (SET_DEST (x)) == REG) + { + /* See if this is setting the replacement register for an + elimination. + + If DEST is the frame pointer, we do nothing because we assume that + all assignments to the frame pointer are for non-local gotos and + are being done at a time when they are valid and do not disturb + anything else. Some machines want to eliminate a fake argument + pointer with either the frame or stack pointer. Assignments to + the frame pointer must not prevent this elimination. */ + + for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; + ep++) + if (ep->to_rtx == SET_DEST (x) + && SET_DEST (x) != frame_pointer_rtx) + { + /* If it is being incremented, adjust the offset. Otherwise, + this elimination can't be done. */ + rtx src = SET_SRC (x); + + if (GET_CODE (src) == PLUS + && XEXP (src, 0) == SET_DEST (x) + && GET_CODE (XEXP (src, 1)) == CONST_INT) + ep->offset -= INTVAL (XEXP (src, 1)); + else + ep->can_eliminate = 0; + } + + /* Now check to see we are assigning to a register that can be + eliminated. If so, it must be as part of a PARALLEL, since we + will not have been called if this is a single SET. So indicate + that we can no longer eliminate this reg. */ + for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; + ep++) + if (ep->from_rtx == SET_DEST (x) && ep->can_eliminate) + ep->can_eliminate = 0; + } + + /* Now avoid the loop below in this common case. */ + { + rtx new0 = eliminate_regs (SET_DEST (x), 0, NULL_RTX); + rtx new1 = eliminate_regs (SET_SRC (x), 0, NULL_RTX); + + /* If SET_DEST changed from a REG to a MEM and INSN is non-zero, + write a CLOBBER insn. */ + if (GET_CODE (SET_DEST (x)) == REG && GET_CODE (new0) == MEM + && insn != 0) + emit_insn_after (gen_rtx (CLOBBER, VOIDmode, SET_DEST (x)), insn); + + if (new0 != SET_DEST (x) || new1 != SET_SRC (x)) + return gen_rtx (SET, VOIDmode, new0, new1); + } + + return x; + + case MEM: + /* Our only special processing is to pass the mode of the MEM to our + recursive call and copy the flags. While we are here, handle this + case more efficiently. */ + new = eliminate_regs (XEXP (x, 0), GET_MODE (x), NULL_RTX); + if (new != XEXP (x, 0)) + { + new = gen_rtx (MEM, GET_MODE (x), new); + new->volatil = x->volatil; + new->unchanging = x->unchanging; + new->in_struct = x->in_struct; + return new; + } + else + return x; + } + + /* Process each of our operands recursively. If any have changed, make a + copy of the rtx. */ + fmt = GET_RTX_FORMAT (code); + for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++) + { + if (*fmt == 'e') + { + new = eliminate_regs (XEXP (x, i), mem_mode, NULL_RTX); + if (new != XEXP (x, i) && ! copied) + { + rtx new_x = rtx_alloc (code); + bcopy (x, new_x, (sizeof (*new_x) - sizeof (new_x->fld) + + (sizeof (new_x->fld[0]) + * GET_RTX_LENGTH (code)))); + x = new_x; + copied = 1; + } + XEXP (x, i) = new; + } + else if (*fmt == 'E') + { + int copied_vec = 0; + for (j = 0; j < XVECLEN (x, i); j++) + { + new = eliminate_regs (XVECEXP (x, i, j), mem_mode, insn); + if (new != XVECEXP (x, i, j) && ! copied_vec) + { + rtvec new_v = gen_rtvec_v (XVECLEN (x, i), + &XVECEXP (x, i, 0)); + if (! copied) + { + rtx new_x = rtx_alloc (code); + bcopy (x, new_x, (sizeof (*new_x) - sizeof (new_x->fld) + + (sizeof (new_x->fld[0]) + * GET_RTX_LENGTH (code)))); + x = new_x; + copied = 1; + } + XVEC (x, i) = new_v; + copied_vec = 1; + } + XVECEXP (x, i, j) = new; + } + } + } + + return x; +} + +/* Scan INSN and eliminate all eliminable registers in it. + + If REPLACE is nonzero, do the replacement destructively. Also + delete the insn as dead it if it is setting an eliminable register. + + If REPLACE is zero, do all our allocations in reload_obstack. + + If no eliminations were done and this insn doesn't require any elimination + processing (these are not identical conditions: it might be updating sp, + but not referencing fp; this needs to be seen during reload_as_needed so + that the offset between fp and sp can be taken into consideration), zero + is returned. Otherwise, 1 is returned. */ + +static int +eliminate_regs_in_insn (insn, replace) + rtx insn; + int replace; +{ + rtx old_body = PATTERN (insn); + rtx new_body; + int val = 0; + struct elim_table *ep; + + if (! replace) + push_obstacks (&reload_obstack, &reload_obstack); + + if (GET_CODE (old_body) == SET && GET_CODE (SET_DEST (old_body)) == REG + && REGNO (SET_DEST (old_body)) < FIRST_PSEUDO_REGISTER) + { + /* Check for setting an eliminable register. */ + for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) + if (ep->from_rtx == SET_DEST (old_body) && ep->can_eliminate) + { + /* In this case this insn isn't serving a useful purpose. We + will delete it in reload_as_needed once we know that this + elimination is, in fact, being done. + + If REPLACE isn't set, we can't delete this insn, but neededn't + process it since it won't be used unless something changes. */ + if (replace) + delete_dead_insn (insn); + val = 1; + goto done; + } + + /* Check for (set (reg) (plus (reg from) (offset))) where the offset + in the insn is the negative of the offset in FROM. Substitute + (set (reg) (reg to)) for the insn and change its code. + + We have to do this here, rather than in eliminate_regs, do that we can + change the insn code. */ + + if (GET_CODE (SET_SRC (old_body)) == PLUS + && GET_CODE (XEXP (SET_SRC (old_body), 0)) == REG + && GET_CODE (XEXP (SET_SRC (old_body), 1)) == CONST_INT) + for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; + ep++) + if (ep->from_rtx == XEXP (SET_SRC (old_body), 0) + && ep->can_eliminate) + { + /* We must stop at the first elimination that will be used. + If this one would replace the PLUS with a REG, do it + now. Otherwise, quit the loop and let eliminate_regs + do its normal replacement. */ + if (ep->offset == - INTVAL (XEXP (SET_SRC (old_body), 1))) + { + PATTERN (insn) = gen_rtx (SET, VOIDmode, + SET_DEST (old_body), ep->to_rtx); + INSN_CODE (insn) = -1; + val = 1; + goto done; + } + + break; + } + } + + old_asm_operands_vec = 0; + + /* Replace the body of this insn with a substituted form. If we changed + something, return non-zero. If this is the final call for this + insn (REPLACE is non-zero), do the elimination in REG_NOTES as well. + + If we are replacing a body that was a (set X (plus Y Z)), try to + re-recognize the insn. We do this in case we had a simple addition + but now can do this as a load-address. This saves an insn in this + common case. */ + + new_body = eliminate_regs (old_body, 0, replace ? insn : NULL_RTX); + if (new_body != old_body) + { + /* If we aren't replacing things permanently and we changed something, + make another copy to ensure that all the RTL is new. Otherwise + things can go wrong if find_reload swaps commutative operands + and one is inside RTL that has been copied while the other is not. */ + + /* Don't copy an asm_operands because (1) there's no need and (2) + copy_rtx can't do it properly when there are multiple outputs. */ + if (! replace && asm_noperands (old_body) < 0) + new_body = copy_rtx (new_body); + + /* If we had a move insn but now we don't, rerecognize it. */ + if ((GET_CODE (old_body) == SET && GET_CODE (SET_SRC (old_body)) == REG + && (GET_CODE (new_body) != SET + || GET_CODE (SET_SRC (new_body)) != REG)) + /* If this was an add insn before, rerecognize. */ + || + (GET_CODE (old_body) == SET + && GET_CODE (SET_SRC (old_body)) == PLUS)) + { + if (! validate_change (insn, &PATTERN (insn), new_body, 0)) + /* If recognition fails, store the new body anyway. + It's normal to have recognition failures here + due to bizarre memory addresses; reloading will fix them. */ + PATTERN (insn) = new_body; + } + else + PATTERN (insn) = new_body; + + if (replace && REG_NOTES (insn)) + REG_NOTES (insn) = eliminate_regs (REG_NOTES (insn), 0, NULL_RTX); + val = 1; + } + + /* Loop through all elimination pairs. See if any have changed and + recalculate the number not at initial offset. + + Compute the maximum offset (minimum offset if the stack does not + grow downward) for each elimination pair. + + We also detect a cases where register elimination cannot be done, + namely, if a register would be both changed and referenced outside a MEM + in the resulting insn since such an insn is often undefined and, even if + not, we cannot know what meaning will be given to it. Note that it is + valid to have a register used in an address in an insn that changes it + (presumably with a pre- or post-increment or decrement). + + If anything changes, return nonzero. */ + + num_not_at_initial_offset = 0; + for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) + { + if (ep->previous_offset != ep->offset && ep->ref_outside_mem) + ep->can_eliminate = 0; + + ep->ref_outside_mem = 0; + + if (ep->previous_offset != ep->offset) + val = 1; + + ep->previous_offset = ep->offset; + if (ep->can_eliminate && ep->offset != ep->initial_offset) + num_not_at_initial_offset++; + +#ifdef STACK_GROWS_DOWNWARD + ep->max_offset = MAX (ep->max_offset, ep->offset); +#else + ep->max_offset = MIN (ep->max_offset, ep->offset); +#endif + } + + done: + if (! replace) + pop_obstacks (); + + return val; +} + +/* Given X, a SET or CLOBBER of DEST, if DEST is the target of a register + replacement we currently believe is valid, mark it as not eliminable if X + modifies DEST in any way other than by adding a constant integer to it. + + If DEST is the frame pointer, we do nothing because we assume that + all assignments to the frame pointer are nonlocal gotos and are being done + at a time when they are valid and do not disturb anything else. + Some machines want to eliminate a fake argument pointer with either the + frame or stack pointer. Assignments to the frame pointer must not prevent + this elimination. + + Called via note_stores from reload before starting its passes to scan + the insns of the function. */ + +static void +mark_not_eliminable (dest, x) + rtx dest; + rtx x; +{ + register int i; + + /* A SUBREG of a hard register here is just changing its mode. We should + not see a SUBREG of an eliminable hard register, but check just in + case. */ + if (GET_CODE (dest) == SUBREG) + dest = SUBREG_REG (dest); + + if (dest == frame_pointer_rtx) + return; + + for (i = 0; i < NUM_ELIMINABLE_REGS; i++) + if (reg_eliminate[i].can_eliminate && dest == reg_eliminate[i].to_rtx + && (GET_CODE (x) != SET + || GET_CODE (SET_SRC (x)) != PLUS + || XEXP (SET_SRC (x), 0) != dest + || GET_CODE (XEXP (SET_SRC (x), 1)) != CONST_INT)) + { + reg_eliminate[i].can_eliminate_previous + = reg_eliminate[i].can_eliminate = 0; + num_eliminable--; + } +} + +/* Kick all pseudos out of hard register REGNO. + If GLOBAL is nonzero, try to find someplace else to put them. + If DUMPFILE is nonzero, log actions taken on that file. + + If CANT_ELIMINATE is nonzero, it means that we are doing this spill + because we found we can't eliminate some register. In the case, no pseudos + are allowed to be in the register, even if they are only in a block that + doesn't require spill registers, unlike the case when we are spilling this + hard reg to produce another spill register. + + Return nonzero if any pseudos needed to be kicked out. */ + +static int +spill_hard_reg (regno, global, dumpfile, cant_eliminate) + register int regno; + int global; + FILE *dumpfile; + int cant_eliminate; +{ + int something_changed = 0; + register int i; + + SET_HARD_REG_BIT (forbidden_regs, regno); + + /* Spill every pseudo reg that was allocated to this reg + or to something that overlaps this reg. */ + + for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) + if (reg_renumber[i] >= 0 + && reg_renumber[i] <= regno + && (reg_renumber[i] + + HARD_REGNO_NREGS (reg_renumber[i], + PSEUDO_REGNO_MODE (i)) + > regno)) + { + enum reg_class class = REGNO_REG_CLASS (regno); + + /* If this register belongs solely to a basic block which needed no + spilling of any class that this register is contained in, + leave it be, unless we are spilling this register because + it was a hard register that can't be eliminated. */ + + if (! cant_eliminate + && basic_block_needs[0] + && reg_basic_block[i] >= 0 + && basic_block_needs[(int) class][reg_basic_block[i]] == 0) + { + enum reg_class *p; + + for (p = reg_class_superclasses[(int) class]; + *p != LIM_REG_CLASSES; p++) + if (basic_block_needs[(int) *p][reg_basic_block[i]] > 0) + break; + + if (*p == LIM_REG_CLASSES) + continue; + } + + /* Mark it as no longer having a hard register home. */ + reg_renumber[i] = -1; + /* We will need to scan everything again. */ + something_changed = 1; + if (global) + retry_global_alloc (i, forbidden_regs); + + alter_reg (i, regno); + if (dumpfile) + { + if (reg_renumber[i] == -1) + fprintf (dumpfile, " Register %d now on stack.\n\n", i); + else + fprintf (dumpfile, " Register %d now in %d.\n\n", + i, reg_renumber[i]); + } + } + + return something_changed; +} + +/* Find all paradoxical subregs within X and update reg_max_ref_width. */ + +static void +scan_paradoxical_subregs (x) + register rtx x; +{ + register int i; + register char *fmt; + register enum rtx_code code = GET_CODE (x); + + switch (code) + { + case CONST_INT: + case CONST: + case SYMBOL_REF: + case LABEL_REF: + case CONST_DOUBLE: + case CC0: + case PC: + case REG: + case USE: + case CLOBBER: + return; + + case SUBREG: + if (GET_CODE (SUBREG_REG (x)) == REG + && GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) + reg_max_ref_width[REGNO (SUBREG_REG (x))] + = GET_MODE_SIZE (GET_MODE (x)); + return; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + scan_paradoxical_subregs (XEXP (x, i)); + else if (fmt[i] == 'E') + { + register int j; + for (j = XVECLEN (x, i) - 1; j >=0; j--) + scan_paradoxical_subregs (XVECEXP (x, i, j)); + } + } +} + +static int +hard_reg_use_compare (p1, p2) + struct hard_reg_n_uses *p1, *p2; +{ + int tem = p1->uses - p2->uses; + if (tem != 0) return tem; + /* If regs are equally good, sort by regno, + so that the results of qsort leave nothing to chance. */ + return p1->regno - p2->regno; +} + +/* Choose the order to consider regs for use as reload registers + based on how much trouble would be caused by spilling one. + Store them in order of decreasing preference in potential_reload_regs. */ + +static void +order_regs_for_reload () +{ + register int i; + register int o = 0; + int large = 0; + + struct hard_reg_n_uses hard_reg_n_uses[FIRST_PSEUDO_REGISTER]; + + CLEAR_HARD_REG_SET (bad_spill_regs); + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + potential_reload_regs[i] = -1; + + /* Count number of uses of each hard reg by pseudo regs allocated to it + and then order them by decreasing use. */ + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + hard_reg_n_uses[i].uses = 0; + hard_reg_n_uses[i].regno = i; + } + + for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) + { + int regno = reg_renumber[i]; + if (regno >= 0) + { + int lim = regno + HARD_REGNO_NREGS (regno, PSEUDO_REGNO_MODE (i)); + while (regno < lim) + hard_reg_n_uses[regno++].uses += reg_n_refs[i]; + } + large += reg_n_refs[i]; + } + + /* Now fixed registers (which cannot safely be used for reloading) + get a very high use count so they will be considered least desirable. + Registers used explicitly in the rtl code are almost as bad. */ + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + if (fixed_regs[i]) + { + hard_reg_n_uses[i].uses += 2 * large + 2; + SET_HARD_REG_BIT (bad_spill_regs, i); + } + else if (regs_explicitly_used[i]) + { + hard_reg_n_uses[i].uses += large + 1; +#ifndef SMALL_REGISTER_CLASSES + /* ??? We are doing this here because of the potential that + bad code may be generated if a register explicitly used in + an insn was used as a spill register for that insn. But + not using these are spill registers may lose on some machine. + We'll have to see how this works out. */ + SET_HARD_REG_BIT (bad_spill_regs, i); +#endif + } + } + hard_reg_n_uses[FRAME_POINTER_REGNUM].uses += 2 * large + 2; + SET_HARD_REG_BIT (bad_spill_regs, FRAME_POINTER_REGNUM); + +#ifdef ELIMINABLE_REGS + /* If registers other than the frame pointer are eliminable, mark them as + poor choices. */ + for (i = 0; i < NUM_ELIMINABLE_REGS; i++) + { + hard_reg_n_uses[reg_eliminate[i].from].uses += 2 * large + 2; + SET_HARD_REG_BIT (bad_spill_regs, reg_eliminate[i].from); + } +#endif + + /* Prefer registers not so far used, for use in temporary loading. + Among them, if REG_ALLOC_ORDER is defined, use that order. + Otherwise, prefer registers not preserved by calls. */ + +#ifdef REG_ALLOC_ORDER + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + int regno = reg_alloc_order[i]; + + if (hard_reg_n_uses[regno].uses == 0) + potential_reload_regs[o++] = regno; + } +#else + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + if (hard_reg_n_uses[i].uses == 0 && call_used_regs[i]) + potential_reload_regs[o++] = i; + } + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + if (hard_reg_n_uses[i].uses == 0 && ! call_used_regs[i]) + potential_reload_regs[o++] = i; + } +#endif + + qsort (hard_reg_n_uses, FIRST_PSEUDO_REGISTER, + sizeof hard_reg_n_uses[0], hard_reg_use_compare); + + /* Now add the regs that are already used, + preferring those used less often. The fixed and otherwise forbidden + registers will be at the end of this list. */ + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (hard_reg_n_uses[i].uses != 0) + potential_reload_regs[o++] = hard_reg_n_uses[i].regno; +} + +/* Reload pseudo-registers into hard regs around each insn as needed. + Additional register load insns are output before the insn that needs it + and perhaps store insns after insns that modify the reloaded pseudo reg. + + reg_last_reload_reg and reg_reloaded_contents keep track of + which registers are already available in reload registers. + We update these for the reloads that we perform, + as the insns are scanned. */ + +static void +reload_as_needed (first, live_known) + rtx first; + int live_known; +{ + register rtx insn; + register int i; + int this_block = 0; + rtx x; + rtx after_call = 0; + + bzero (spill_reg_rtx, sizeof spill_reg_rtx); + reg_last_reload_reg = (rtx *) alloca (max_regno * sizeof (rtx)); + bzero (reg_last_reload_reg, max_regno * sizeof (rtx)); + reg_has_output_reload = (char *) alloca (max_regno); + for (i = 0; i < n_spills; i++) + { + reg_reloaded_contents[i] = -1; + reg_reloaded_insn[i] = 0; + } + + /* Reset all offsets on eliminable registers to their initial values. */ +#ifdef ELIMINABLE_REGS + for (i = 0; i < NUM_ELIMINABLE_REGS; i++) + { + INITIAL_ELIMINATION_OFFSET (reg_eliminate[i].from, reg_eliminate[i].to, + reg_eliminate[i].initial_offset); + reg_eliminate[i].previous_offset + = reg_eliminate[i].offset = reg_eliminate[i].initial_offset; + } +#else + INITIAL_FRAME_POINTER_OFFSET (reg_eliminate[0].initial_offset); + reg_eliminate[0].previous_offset + = reg_eliminate[0].offset = reg_eliminate[0].initial_offset; +#endif + + num_not_at_initial_offset = 0; + + for (insn = first; insn;) + { + register rtx next = NEXT_INSN (insn); + + /* Notice when we move to a new basic block. */ + if (live_known && this_block + 1 < n_basic_blocks + && insn == basic_block_head[this_block+1]) + ++this_block; + + /* If we pass a label, copy the offsets from the label information + into the current offsets of each elimination. */ + if (GET_CODE (insn) == CODE_LABEL) + { + num_not_at_initial_offset = 0; + for (i = 0; i < NUM_ELIMINABLE_REGS; i++) + { + reg_eliminate[i].offset = reg_eliminate[i].previous_offset + = offsets_at[CODE_LABEL_NUMBER (insn)][i]; + if (reg_eliminate[i].can_eliminate + && (reg_eliminate[i].offset + != reg_eliminate[i].initial_offset)) + num_not_at_initial_offset++; + } + } + + else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + rtx avoid_return_reg = 0; + +#ifdef SMALL_REGISTER_CLASSES + /* Set avoid_return_reg if this is an insn + that might use the value of a function call. */ + if (GET_CODE (insn) == CALL_INSN) + { + if (GET_CODE (PATTERN (insn)) == SET) + after_call = SET_DEST (PATTERN (insn)); + else if (GET_CODE (PATTERN (insn)) == PARALLEL + && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) + after_call = SET_DEST (XVECEXP (PATTERN (insn), 0, 0)); + else + after_call = 0; + } + else if (after_call != 0 + && !(GET_CODE (PATTERN (insn)) == SET + && SET_DEST (PATTERN (insn)) == stack_pointer_rtx)) + { + if (reg_mentioned_p (after_call, PATTERN (insn))) + avoid_return_reg = after_call; + after_call = 0; + } +#endif /* SMALL_REGISTER_CLASSES */ + + /* If this is a USE and CLOBBER of a MEM, ensure that any + references to eliminable registers have been removed. */ + + if ((GET_CODE (PATTERN (insn)) == USE + || GET_CODE (PATTERN (insn)) == CLOBBER) + && GET_CODE (XEXP (PATTERN (insn), 0)) == MEM) + XEXP (XEXP (PATTERN (insn), 0), 0) + = eliminate_regs (XEXP (XEXP (PATTERN (insn), 0), 0), + GET_MODE (XEXP (PATTERN (insn), 0)), NULL_RTX); + + /* If we need to do register elimination processing, do so. + This might delete the insn, in which case we are done. */ + if (num_eliminable && GET_MODE (insn) == QImode) + { + eliminate_regs_in_insn (insn, 1); + if (GET_CODE (insn) == NOTE) + { + insn = next; + continue; + } + } + + if (GET_MODE (insn) == VOIDmode) + n_reloads = 0; + /* First find the pseudo regs that must be reloaded for this insn. + This info is returned in the tables reload_... (see reload.h). + Also modify the body of INSN by substituting RELOAD + rtx's for those pseudo regs. */ + else + { + bzero (reg_has_output_reload, max_regno); + CLEAR_HARD_REG_SET (reg_is_output_reload); + + find_reloads (insn, 1, spill_indirect_levels, live_known, + spill_reg_order); + } + + if (n_reloads > 0) + { + rtx prev = PREV_INSN (insn), next = NEXT_INSN (insn); + rtx p; + int class; + + /* If this block has not had spilling done for a + particular clas and we have any non-optionals that need a + spill reg in that class, abort. */ + + for (class = 0; class < N_REG_CLASSES; class++) + if (basic_block_needs[class] != 0 + && basic_block_needs[class][this_block] == 0) + for (i = 0; i < n_reloads; i++) + if (class == (int) reload_reg_class[i] + && reload_reg_rtx[i] == 0 + && ! reload_optional[i] + && (reload_in[i] != 0 || reload_out[i] != 0 + || reload_secondary_p[i] != 0)) + abort (); + + /* Now compute which reload regs to reload them into. Perhaps + reusing reload regs from previous insns, or else output + load insns to reload them. Maybe output store insns too. + Record the choices of reload reg in reload_reg_rtx. */ + choose_reload_regs (insn, avoid_return_reg); + +#ifdef SMALL_REGISTER_CLASSES + /* Merge any reloads that we didn't combine for fear of + increasing the number of spill registers needed but now + discover can be safely merged. */ + merge_assigned_reloads (insn); +#endif + + /* Generate the insns to reload operands into or out of + their reload regs. */ + emit_reload_insns (insn); + + /* Substitute the chosen reload regs from reload_reg_rtx + into the insn's body (or perhaps into the bodies of other + load and store insn that we just made for reloading + and that we moved the structure into). */ + subst_reloads (); + + /* If this was an ASM, make sure that all the reload insns + we have generated are valid. If not, give an error + and delete them. */ + + if (asm_noperands (PATTERN (insn)) >= 0) + for (p = NEXT_INSN (prev); p != next; p = NEXT_INSN (p)) + if (p != insn && GET_RTX_CLASS (GET_CODE (p)) == 'i' + && (recog_memoized (p) < 0 + || (insn_extract (p), + ! constrain_operands (INSN_CODE (p), 1)))) + { + error_for_asm (insn, + "`asm' operand requires impossible reload"); + PUT_CODE (p, NOTE); + NOTE_SOURCE_FILE (p) = 0; + NOTE_LINE_NUMBER (p) = NOTE_INSN_DELETED; + } + } + /* Any previously reloaded spilled pseudo reg, stored in this insn, + is no longer validly lying around to save a future reload. + Note that this does not detect pseudos that were reloaded + for this insn in order to be stored in + (obeying register constraints). That is correct; such reload + registers ARE still valid. */ + note_stores (PATTERN (insn), forget_old_reloads_1); + + /* There may have been CLOBBER insns placed after INSN. So scan + between INSN and NEXT and use them to forget old reloads. */ + for (x = NEXT_INSN (insn); x != next; x = NEXT_INSN (x)) + if (GET_CODE (x) == INSN && GET_CODE (PATTERN (x)) == CLOBBER) + note_stores (PATTERN (x), forget_old_reloads_1); + +#ifdef AUTO_INC_DEC + /* Likewise for regs altered by auto-increment in this insn. + But note that the reg-notes are not changed by reloading: + they still contain the pseudo-regs, not the spill regs. */ + for (x = REG_NOTES (insn); x; x = XEXP (x, 1)) + if (REG_NOTE_KIND (x) == REG_INC) + { + /* See if this pseudo reg was reloaded in this insn. + If so, its last-reload info is still valid + because it is based on this insn's reload. */ + for (i = 0; i < n_reloads; i++) + if (reload_out[i] == XEXP (x, 0)) + break; + + if (i == n_reloads) + forget_old_reloads_1 (XEXP (x, 0), NULL_RTX); + } +#endif + } + /* A reload reg's contents are unknown after a label. */ + if (GET_CODE (insn) == CODE_LABEL) + for (i = 0; i < n_spills; i++) + { + reg_reloaded_contents[i] = -1; + reg_reloaded_insn[i] = 0; + } + + /* Don't assume a reload reg is still good after a call insn + if it is a call-used reg. */ + else if (GET_CODE (insn) == CALL_INSN) + for (i = 0; i < n_spills; i++) + if (call_used_regs[spill_regs[i]]) + { + reg_reloaded_contents[i] = -1; + reg_reloaded_insn[i] = 0; + } + + /* In case registers overlap, allow certain insns to invalidate + particular hard registers. */ + +#ifdef INSN_CLOBBERS_REGNO_P + for (i = 0 ; i < n_spills ; i++) + if (INSN_CLOBBERS_REGNO_P (insn, spill_regs[i])) + { + reg_reloaded_contents[i] = -1; + reg_reloaded_insn[i] = 0; + } +#endif + + insn = next; + +#ifdef USE_C_ALLOCA + alloca (0); +#endif + } +} + +/* Discard all record of any value reloaded from X, + or reloaded in X from someplace else; + unless X is an output reload reg of the current insn. + + X may be a hard reg (the reload reg) + or it may be a pseudo reg that was reloaded from. */ + +static void +forget_old_reloads_1 (x, ignored) + rtx x; + rtx ignored; +{ + register int regno; + int nr; + int offset = 0; + + /* note_stores does give us subregs of hard regs. */ + while (GET_CODE (x) == SUBREG) + { + offset += SUBREG_WORD (x); + x = SUBREG_REG (x); + } + + if (GET_CODE (x) != REG) + return; + + regno = REGNO (x) + offset; + + if (regno >= FIRST_PSEUDO_REGISTER) + nr = 1; + else + { + int i; + nr = HARD_REGNO_NREGS (regno, GET_MODE (x)); + /* Storing into a spilled-reg invalidates its contents. + This can happen if a block-local pseudo is allocated to that reg + and it wasn't spilled because this block's total need is 0. + Then some insn might have an optional reload and use this reg. */ + for (i = 0; i < nr; i++) + if (spill_reg_order[regno + i] >= 0 + /* But don't do this if the reg actually serves as an output + reload reg in the current instruction. */ + && (n_reloads == 0 + || ! TEST_HARD_REG_BIT (reg_is_output_reload, regno + i))) + { + reg_reloaded_contents[spill_reg_order[regno + i]] = -1; + reg_reloaded_insn[spill_reg_order[regno + i]] = 0; + } + } + + /* Since value of X has changed, + forget any value previously copied from it. */ + + while (nr-- > 0) + /* But don't forget a copy if this is the output reload + that establishes the copy's validity. */ + if (n_reloads == 0 || reg_has_output_reload[regno + nr] == 0) + reg_last_reload_reg[regno + nr] = 0; +} + +/* For each reload, the mode of the reload register. */ +static enum machine_mode reload_mode[MAX_RELOADS]; + +/* For each reload, the largest number of registers it will require. */ +static int reload_nregs[MAX_RELOADS]; + +/* Comparison function for qsort to decide which of two reloads + should be handled first. *P1 and *P2 are the reload numbers. */ + +static int +reload_reg_class_lower (p1, p2) + short *p1, *p2; +{ + register int r1 = *p1, r2 = *p2; + register int t; + + /* Consider required reloads before optional ones. */ + t = reload_optional[r1] - reload_optional[r2]; + if (t != 0) + return t; + + /* Count all solitary classes before non-solitary ones. */ + t = ((reg_class_size[(int) reload_reg_class[r2]] == 1) + - (reg_class_size[(int) reload_reg_class[r1]] == 1)); + if (t != 0) + return t; + + /* Aside from solitaires, consider all multi-reg groups first. */ + t = reload_nregs[r2] - reload_nregs[r1]; + if (t != 0) + return t; + + /* Consider reloads in order of increasing reg-class number. */ + t = (int) reload_reg_class[r1] - (int) reload_reg_class[r2]; + if (t != 0) + return t; + + /* If reloads are equally urgent, sort by reload number, + so that the results of qsort leave nothing to chance. */ + return r1 - r2; +} + +/* The following HARD_REG_SETs indicate when each hard register is + used for a reload of various parts of the current insn. */ + +/* If reg is in use as a reload reg for a RELOAD_OTHER reload. */ +static HARD_REG_SET reload_reg_used; +/* If reg is in use for a RELOAD_FOR_INPUT_ADDRESS reload for operand I. */ +static HARD_REG_SET reload_reg_used_in_input_addr[MAX_RECOG_OPERANDS]; +/* If reg is in use for a RELOAD_FOR_OUTPUT_ADDRESS reload for operand I. */ +static HARD_REG_SET reload_reg_used_in_output_addr[MAX_RECOG_OPERANDS]; +/* If reg is in use for a RELOAD_FOR_INPUT reload for operand I. */ +static HARD_REG_SET reload_reg_used_in_input[MAX_RECOG_OPERANDS]; +/* If reg is in use for a RELOAD_FOR_OUTPUT reload for operand I. */ +static HARD_REG_SET reload_reg_used_in_output[MAX_RECOG_OPERANDS]; +/* If reg is in use for a RELOAD_FOR_OPERAND_ADDRESS reload. */ +static HARD_REG_SET reload_reg_used_in_op_addr; +/* If reg is in use for a RELOAD_FOR_INSN reload. */ +static HARD_REG_SET reload_reg_used_in_insn; +/* If reg is in use for a RELOAD_FOR_OTHER_ADDRESS reload. */ +static HARD_REG_SET reload_reg_used_in_other_addr; + +/* If reg is in use as a reload reg for any sort of reload. */ +static HARD_REG_SET reload_reg_used_at_all; + +/* If reg is use as an inherited reload. We just mark the first register + in the group. */ +static HARD_REG_SET reload_reg_used_for_inherit; + +/* Mark reg REGNO as in use for a reload of the sort spec'd by OPNUM and + TYPE. MODE is used to indicate how many consecutive regs are + actually used. */ + +static void +mark_reload_reg_in_use (regno, opnum, type, mode) + int regno; + int opnum; + enum reload_type type; + enum machine_mode mode; +{ + int nregs = HARD_REGNO_NREGS (regno, mode); + int i; + + for (i = regno; i < nregs + regno; i++) + { + switch (type) + { + case RELOAD_OTHER: + SET_HARD_REG_BIT (reload_reg_used, i); + break; + + case RELOAD_FOR_INPUT_ADDRESS: + SET_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], i); + break; + + case RELOAD_FOR_OUTPUT_ADDRESS: + SET_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], i); + break; + + case RELOAD_FOR_OPERAND_ADDRESS: + SET_HARD_REG_BIT (reload_reg_used_in_op_addr, i); + break; + + case RELOAD_FOR_OTHER_ADDRESS: + SET_HARD_REG_BIT (reload_reg_used_in_other_addr, i); + break; + + case RELOAD_FOR_INPUT: + SET_HARD_REG_BIT (reload_reg_used_in_input[opnum], i); + break; + + case RELOAD_FOR_OUTPUT: + SET_HARD_REG_BIT (reload_reg_used_in_output[opnum], i); + break; + + case RELOAD_FOR_INSN: + SET_HARD_REG_BIT (reload_reg_used_in_insn, i); + break; + } + + SET_HARD_REG_BIT (reload_reg_used_at_all, i); + } +} + +/* Similarly, but show REGNO is no longer in use for a reload. */ + +static void +clear_reload_reg_in_use (regno, opnum, type, mode) + int regno; + int opnum; + enum reload_type type; + enum machine_mode mode; +{ + int nregs = HARD_REGNO_NREGS (regno, mode); + int i; + + for (i = regno; i < nregs + regno; i++) + { + switch (type) + { + case RELOAD_OTHER: + CLEAR_HARD_REG_BIT (reload_reg_used, i); + break; + + case RELOAD_FOR_INPUT_ADDRESS: + CLEAR_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], i); + break; + + case RELOAD_FOR_OUTPUT_ADDRESS: + CLEAR_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], i); + break; + + case RELOAD_FOR_OPERAND_ADDRESS: + CLEAR_HARD_REG_BIT (reload_reg_used_in_op_addr, i); + break; + + case RELOAD_FOR_OTHER_ADDRESS: + CLEAR_HARD_REG_BIT (reload_reg_used_in_other_addr, i); + break; + + case RELOAD_FOR_INPUT: + CLEAR_HARD_REG_BIT (reload_reg_used_in_input[opnum], i); + break; + + case RELOAD_FOR_OUTPUT: + CLEAR_HARD_REG_BIT (reload_reg_used_in_output[opnum], i); + break; + + case RELOAD_FOR_INSN: + CLEAR_HARD_REG_BIT (reload_reg_used_in_insn, i); + break; + } + } +} + +/* 1 if reg REGNO is free as a reload reg for a reload of the sort + specified by OPNUM and TYPE. */ + +static int +reload_reg_free_p (regno, opnum, type) + int regno; + int opnum; + enum reload_type type; +{ + int i; + + /* In use for a RELOAD_OTHER means it's not available for anything except + RELOAD_FOR_OTHER_ADDRESS. Recall that RELOAD_FOR_OTHER_ADDRESS is known + to be used only for inputs. */ + + if (type != RELOAD_FOR_OTHER_ADDRESS + && TEST_HARD_REG_BIT (reload_reg_used, regno)) + return 0; + + switch (type) + { + case RELOAD_OTHER: + /* In use for anything means not available for a RELOAD_OTHER. */ + return ! TEST_HARD_REG_BIT (reload_reg_used_at_all, regno); + + /* The other kinds of use can sometimes share a register. */ + case RELOAD_FOR_INPUT: + if (TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno)) + return 0; + + /* If it is used for some other input, can't use it. */ + for (i = 0; i < reload_n_operands; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) + return 0; + + /* If it is used in a later operand's address, can't use it. */ + for (i = opnum + 1; i < reload_n_operands; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)) + return 0; + + return 1; + + case RELOAD_FOR_INPUT_ADDRESS: + /* Can't use a register if it is used for an input address for this + operand or used as an input in an earlier one. */ + if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], regno)) + return 0; + + for (i = 0; i < opnum; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) + return 0; + + return 1; + + case RELOAD_FOR_OUTPUT_ADDRESS: + /* Can't use a register if it is used for an output address for this + operand or used as an output in this or a later operand. */ + if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], regno)) + return 0; + + for (i = opnum; i < reload_n_operands; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) + return 0; + + return 1; + + case RELOAD_FOR_OPERAND_ADDRESS: + for (i = 0; i < reload_n_operands; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) + return 0; + + return (! TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno) + && ! TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno)); + + case RELOAD_FOR_OUTPUT: + /* This cannot share a register with RELOAD_FOR_INSN reloads, other + outputs, or an operand address for this or an earlier output. */ + if (TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno)) + return 0; + + for (i = 0; i < reload_n_operands; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) + return 0; + + for (i = 0; i <= opnum; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)) + return 0; + + return 1; + + case RELOAD_FOR_INSN: + for (i = 0; i < reload_n_operands; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) + return 0; + + return (! TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno) + && ! TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno)); + + case RELOAD_FOR_OTHER_ADDRESS: + return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno); + } + abort (); +} + +/* Return 1 if the value in reload reg REGNO, as used by a reload + needed for the part of the insn specified by OPNUM and TYPE, + is not in use for a reload in any prior part of the insn. + + We can assume that the reload reg was already tested for availability + at the time it is needed, and we should not check this again, + in case the reg has already been marked in use. */ + +static int +reload_reg_free_before_p (regno, opnum, type) + int regno; + int opnum; + enum reload_type type; +{ + int i; + + switch (type) + { + case RELOAD_FOR_OTHER_ADDRESS: + /* These always come first. */ + return 1; + + case RELOAD_OTHER: + return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno); + + /* If this use is for part of the insn, + check the reg is not in use for any prior part. It is tempting + to try to do this by falling through from objecs that occur + later in the insn to ones that occur earlier, but that will not + correctly take into account the fact that here we MUST ignore + things that would prevent the register from being allocated in + the first place, since we know that it was allocated. */ + + case RELOAD_FOR_OUTPUT_ADDRESS: + /* Earlier reloads are for earlier outputs or their addresses, + any RELOAD_FOR_INSN reloads, any inputs or their addresses, or any + RELOAD_FOR_OTHER_ADDRESS reloads (we know it can't conflict with + RELOAD_OTHER).. */ + for (i = 0; i < opnum; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) + return 0; + + if (TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno)) + return 0; + + for (i = 0; i < reload_n_operands; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) + return 0; + + return (! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno) + && ! TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno) + && ! TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno)); + + case RELOAD_FOR_OUTPUT: + /* This can't be used in the output address for this operand and + anything that can't be used for it, except that we've already + tested for RELOAD_FOR_INSN objects. */ + + if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], regno)) + return 0; + + for (i = 0; i < opnum; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) + return 0; + + for (i = 0; i < reload_n_operands; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno)) + return 0; + + return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno); + + case RELOAD_FOR_OPERAND_ADDRESS: + case RELOAD_FOR_INSN: + /* These can't conflict with inputs, or each other, so all we have to + test is input addresses and the addresses of OTHER items. */ + + for (i = 0; i < reload_n_operands; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)) + return 0; + + return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno); + + case RELOAD_FOR_INPUT: + /* The only things earlier are the address for this and + earlier inputs, other inputs (which we know we don't conflict + with), and addresses of RELOAD_OTHER objects. */ + + for (i = 0; i <= opnum; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)) + return 0; + + return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno); + + case RELOAD_FOR_INPUT_ADDRESS: + /* Similarly, all we have to check is for use in earlier inputs' + addresses. */ + for (i = 0; i < opnum; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)) + return 0; + + return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno); + } + abort (); +} + +/* Return 1 if the value in reload reg REGNO, as used by a reload + needed for the part of the insn specified by OPNUM and TYPE, + is still available in REGNO at the end of the insn. + + We can assume that the reload reg was already tested for availability + at the time it is needed, and we should not check this again, + in case the reg has already been marked in use. */ + +static int +reload_reg_reaches_end_p (regno, opnum, type) + int regno; + int opnum; + enum reload_type type; +{ + int i; + + switch (type) + { + case RELOAD_OTHER: + /* Since a RELOAD_OTHER reload claims the reg for the entire insn, + its value must reach the end. */ + return 1; + + /* If this use is for part of the insn, + its value reaches if no subsequent part uses the same register. + Just like the above function, don't try to do this with lots + of fallthroughs. */ + + case RELOAD_FOR_OTHER_ADDRESS: + /* Here we check for everything else, since these don't conflict + with anything else and everything comes later. */ + + for (i = 0; i < reload_n_operands; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) + return 0; + + return (! TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno) + && ! TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno) + && ! TEST_HARD_REG_BIT (reload_reg_used, regno)); + + case RELOAD_FOR_INPUT_ADDRESS: + /* Similar, except that we check only for this and subsequent inputs + and the address of only subsequent inputs and we do not need + to check for RELOAD_OTHER objects since they are known not to + conflict. */ + + for (i = opnum; i < reload_n_operands; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) + return 0; + + for (i = opnum + 1; i < reload_n_operands; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)) + return 0; + + for (i = 0; i < reload_n_operands; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) + return 0; + + return (! TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno) + && ! TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno)); + + case RELOAD_FOR_INPUT: + /* Similar to input address, except we start at the next operand for + both input and input address and we do not check for + RELOAD_FOR_OPERAND_ADDRESS and RELOAD_FOR_INSN since these + would conflict. */ + + for (i = opnum + 1; i < reload_n_operands; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)) + return 0; + + /* ... fall through ... */ + + case RELOAD_FOR_OPERAND_ADDRESS: + /* Check outputs and their addresses. */ + + for (i = 0; i < reload_n_operands; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno) + || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)) + return 0; + + return 1; + + case RELOAD_FOR_INSN: + /* These conflict with other outputs with with RELOAD_OTHER. So + we need only check for output addresses. */ + + opnum = -1; + + /* ... fall through ... */ + + case RELOAD_FOR_OUTPUT: + case RELOAD_FOR_OUTPUT_ADDRESS: + /* We already know these can't conflict with a later output. So the + only thing to check are later output addresses. */ + for (i = opnum + 1; i < reload_n_operands; i++) + if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)) + return 0; + + return 1; + } + + abort (); +} + +/* Vector of reload-numbers showing the order in which the reloads should + be processed. */ +short reload_order[MAX_RELOADS]; + +/* Indexed by reload number, 1 if incoming value + inherited from previous insns. */ +char reload_inherited[MAX_RELOADS]; + +/* For an inherited reload, this is the insn the reload was inherited from, + if we know it. Otherwise, this is 0. */ +rtx reload_inheritance_insn[MAX_RELOADS]; + +/* If non-zero, this is a place to get the value of the reload, + rather than using reload_in. */ +rtx reload_override_in[MAX_RELOADS]; + +/* For each reload, the index in spill_regs of the spill register used, + or -1 if we did not need one of the spill registers for this reload. */ +int reload_spill_index[MAX_RELOADS]; + +/* Index of last register assigned as a spill register. We allocate in + a round-robin fashio. */ + +static int last_spill_reg = 0; + +/* Find a spill register to use as a reload register for reload R. + LAST_RELOAD is non-zero if this is the last reload for the insn being + processed. + + Set reload_reg_rtx[R] to the register allocated. + + If NOERROR is nonzero, we return 1 if successful, + or 0 if we couldn't find a spill reg and we didn't change anything. */ + +static int +allocate_reload_reg (r, insn, last_reload, noerror) + int r; + rtx insn; + int last_reload; + int noerror; +{ + int i; + int pass; + int count; + rtx new; + int regno; + + /* If we put this reload ahead, thinking it is a group, + then insist on finding a group. Otherwise we can grab a + reg that some other reload needs. + (That can happen when we have a 68000 DATA_OR_FP_REG + which is a group of data regs or one fp reg.) + We need not be so restrictive if there are no more reloads + for this insn. + + ??? Really it would be nicer to have smarter handling + for that kind of reg class, where a problem like this is normal. + Perhaps those classes should be avoided for reloading + by use of more alternatives. */ + + int force_group = reload_nregs[r] > 1 && ! last_reload; + + /* If we want a single register and haven't yet found one, + take any reg in the right class and not in use. + If we want a consecutive group, here is where we look for it. + + We use two passes so we can first look for reload regs to + reuse, which are already in use for other reloads in this insn, + and only then use additional registers. + I think that maximizing reuse is needed to make sure we don't + run out of reload regs. Suppose we have three reloads, and + reloads A and B can share regs. These need two regs. + Suppose A and B are given different regs. + That leaves none for C. */ + for (pass = 0; pass < 2; pass++) + { + /* I is the index in spill_regs. + We advance it round-robin between insns to use all spill regs + equally, so that inherited reloads have a chance + of leapfrogging each other. */ + + for (count = 0, i = last_spill_reg; count < n_spills; count++) + { + int class = (int) reload_reg_class[r]; + + i = (i + 1) % n_spills; + + if (reload_reg_free_p (spill_regs[i], reload_opnum[r], + reload_when_needed[r]) + && TEST_HARD_REG_BIT (reg_class_contents[class], spill_regs[i]) + && HARD_REGNO_MODE_OK (spill_regs[i], reload_mode[r]) + /* Look first for regs to share, then for unshared. But + don't share regs used for inherited reloads; they are + the ones we want to preserve. */ + && (pass + || (TEST_HARD_REG_BIT (reload_reg_used_at_all, + spill_regs[i]) + && ! TEST_HARD_REG_BIT (reload_reg_used_for_inherit, + spill_regs[i])))) + { + int nr = HARD_REGNO_NREGS (spill_regs[i], reload_mode[r]); + /* Avoid the problem where spilling a GENERAL_OR_FP_REG + (on 68000) got us two FP regs. If NR is 1, + we would reject both of them. */ + if (force_group) + nr = CLASS_MAX_NREGS (reload_reg_class[r], reload_mode[r]); + /* If we need only one reg, we have already won. */ + if (nr == 1) + { + /* But reject a single reg if we demand a group. */ + if (force_group) + continue; + break; + } + /* Otherwise check that as many consecutive regs as we need + are available here. + Also, don't use for a group registers that are + needed for nongroups. */ + if (! TEST_HARD_REG_BIT (counted_for_nongroups, spill_regs[i])) + while (nr > 1) + { + regno = spill_regs[i] + nr - 1; + if (!(TEST_HARD_REG_BIT (reg_class_contents[class], regno) + && spill_reg_order[regno] >= 0 + && reload_reg_free_p (regno, reload_opnum[r], + reload_when_needed[r]) + && ! TEST_HARD_REG_BIT (counted_for_nongroups, + regno))) + break; + nr--; + } + if (nr == 1) + break; + } + } + + /* If we found something on pass 1, omit pass 2. */ + if (count < n_spills) + break; + } + + /* We should have found a spill register by now. */ + if (count == n_spills) + { + if (noerror) + return 0; + goto failure; + } + + /* I is the index in SPILL_REG_RTX of the reload register we are to + allocate. Get an rtx for it and find its register number. */ + + new = spill_reg_rtx[i]; + + if (new == 0 || GET_MODE (new) != reload_mode[r]) + spill_reg_rtx[i] = new + = gen_rtx (REG, reload_mode[r], spill_regs[i]); + + regno = true_regnum (new); + + /* Detect when the reload reg can't hold the reload mode. + This used to be one `if', but Sequent compiler can't handle that. */ + if (HARD_REGNO_MODE_OK (regno, reload_mode[r])) + { + enum machine_mode test_mode = VOIDmode; + if (reload_in[r]) + test_mode = GET_MODE (reload_in[r]); + /* If reload_in[r] has VOIDmode, it means we will load it + in whatever mode the reload reg has: to wit, reload_mode[r]. + We have already tested that for validity. */ + /* Aside from that, we need to test that the expressions + to reload from or into have modes which are valid for this + reload register. Otherwise the reload insns would be invalid. */ + if (! (reload_in[r] != 0 && test_mode != VOIDmode + && ! HARD_REGNO_MODE_OK (regno, test_mode))) + if (! (reload_out[r] != 0 + && ! HARD_REGNO_MODE_OK (regno, GET_MODE (reload_out[r])))) + { + /* The reg is OK. */ + last_spill_reg = i; + + /* Mark as in use for this insn the reload regs we use + for this. */ + mark_reload_reg_in_use (spill_regs[i], reload_opnum[r], + reload_when_needed[r], reload_mode[r]); + + reload_reg_rtx[r] = new; + reload_spill_index[r] = i; + return 1; + } + } + + /* The reg is not OK. */ + if (noerror) + return 0; + + failure: + if (asm_noperands (PATTERN (insn)) < 0) + /* It's the compiler's fault. */ + abort (); + + /* It's the user's fault; the operand's mode and constraint + don't match. Disable this reload so we don't crash in final. */ + error_for_asm (insn, + "`asm' operand constraint incompatible with operand size"); + reload_in[r] = 0; + reload_out[r] = 0; + reload_reg_rtx[r] = 0; + reload_optional[r] = 1; + reload_secondary_p[r] = 1; + + return 1; +} + +/* Assign hard reg targets for the pseudo-registers we must reload + into hard regs for this insn. + Also output the instructions to copy them in and out of the hard regs. + + For machines with register classes, we are responsible for + finding a reload reg in the proper class. */ + +static void +choose_reload_regs (insn, avoid_return_reg) + rtx insn; + rtx avoid_return_reg; +{ + register int i, j; + int max_group_size = 1; + enum reg_class group_class = NO_REGS; + int inheritance; + + rtx save_reload_reg_rtx[MAX_RELOADS]; + char save_reload_inherited[MAX_RELOADS]; + rtx save_reload_inheritance_insn[MAX_RELOADS]; + rtx save_reload_override_in[MAX_RELOADS]; + int save_reload_spill_index[MAX_RELOADS]; + HARD_REG_SET save_reload_reg_used; + HARD_REG_SET save_reload_reg_used_in_input_addr[MAX_RECOG_OPERANDS]; + HARD_REG_SET save_reload_reg_used_in_output_addr[MAX_RECOG_OPERANDS]; + HARD_REG_SET save_reload_reg_used_in_input[MAX_RECOG_OPERANDS]; + HARD_REG_SET save_reload_reg_used_in_output[MAX_RECOG_OPERANDS]; + HARD_REG_SET save_reload_reg_used_in_op_addr; + HARD_REG_SET save_reload_reg_used_in_insn; + HARD_REG_SET save_reload_reg_used_in_other_addr; + HARD_REG_SET save_reload_reg_used_at_all; + + bzero (reload_inherited, MAX_RELOADS); + bzero (reload_inheritance_insn, MAX_RELOADS * sizeof (rtx)); + bzero (reload_override_in, MAX_RELOADS * sizeof (rtx)); + + CLEAR_HARD_REG_SET (reload_reg_used); + CLEAR_HARD_REG_SET (reload_reg_used_at_all); + CLEAR_HARD_REG_SET (reload_reg_used_in_op_addr); + CLEAR_HARD_REG_SET (reload_reg_used_in_insn); + CLEAR_HARD_REG_SET (reload_reg_used_in_other_addr); + + for (i = 0; i < reload_n_operands; i++) + { + CLEAR_HARD_REG_SET (reload_reg_used_in_output[i]); + CLEAR_HARD_REG_SET (reload_reg_used_in_input[i]); + CLEAR_HARD_REG_SET (reload_reg_used_in_input_addr[i]); + CLEAR_HARD_REG_SET (reload_reg_used_in_output_addr[i]); + } + +#ifdef SMALL_REGISTER_CLASSES + /* Don't bother with avoiding the return reg + if we have no mandatory reload that could use it. */ + if (avoid_return_reg) + { + int do_avoid = 0; + int regno = REGNO (avoid_return_reg); + int nregs + = HARD_REGNO_NREGS (regno, GET_MODE (avoid_return_reg)); + int r; + + for (r = regno; r < regno + nregs; r++) + if (spill_reg_order[r] >= 0) + for (j = 0; j < n_reloads; j++) + if (!reload_optional[j] && reload_reg_rtx[j] == 0 + && (reload_in[j] != 0 || reload_out[j] != 0 + || reload_secondary_p[j]) + && + TEST_HARD_REG_BIT (reg_class_contents[(int) reload_reg_class[j]], r)) + do_avoid = 1; + if (!do_avoid) + avoid_return_reg = 0; + } +#endif /* SMALL_REGISTER_CLASSES */ + +#if 0 /* Not needed, now that we can always retry without inheritance. */ + /* See if we have more mandatory reloads than spill regs. + If so, then we cannot risk optimizations that could prevent + reloads from sharing one spill register. + + Since we will try finding a better register than reload_reg_rtx + unless it is equal to reload_in or reload_out, count such reloads. */ + + { + int tem = 0; +#ifdef SMALL_REGISTER_CLASSES + int tem = (avoid_return_reg != 0); +#endif + for (j = 0; j < n_reloads; j++) + if (! reload_optional[j] + && (reload_in[j] != 0 || reload_out[j] != 0 || reload_secondary_p[j]) + && (reload_reg_rtx[j] == 0 + || (! rtx_equal_p (reload_reg_rtx[j], reload_in[j]) + && ! rtx_equal_p (reload_reg_rtx[j], reload_out[j])))) + tem++; + if (tem > n_spills) + must_reuse = 1; + } +#endif + +#ifdef SMALL_REGISTER_CLASSES + /* Don't use the subroutine call return reg for a reload + if we are supposed to avoid it. */ + if (avoid_return_reg) + { + int regno = REGNO (avoid_return_reg); + int nregs + = HARD_REGNO_NREGS (regno, GET_MODE (avoid_return_reg)); + int r; + + for (r = regno; r < regno + nregs; r++) + if (spill_reg_order[r] >= 0) + SET_HARD_REG_BIT (reload_reg_used, r); + } +#endif /* SMALL_REGISTER_CLASSES */ + + /* In order to be certain of getting the registers we need, + we must sort the reloads into order of increasing register class. + Then our grabbing of reload registers will parallel the process + that provided the reload registers. + + Also note whether any of the reloads wants a consecutive group of regs. + If so, record the maximum size of the group desired and what + register class contains all the groups needed by this insn. */ + + for (j = 0; j < n_reloads; j++) + { + reload_order[j] = j; + reload_spill_index[j] = -1; + + reload_mode[j] + = (reload_inmode[j] == VOIDmode + || (GET_MODE_SIZE (reload_outmode[j]) + > GET_MODE_SIZE (reload_inmode[j]))) + ? reload_outmode[j] : reload_inmode[j]; + + reload_nregs[j] = CLASS_MAX_NREGS (reload_reg_class[j], reload_mode[j]); + + if (reload_nregs[j] > 1) + { + max_group_size = MAX (reload_nregs[j], max_group_size); + group_class = reg_class_superunion[(int)reload_reg_class[j]][(int)group_class]; + } + + /* If we have already decided to use a certain register, + don't use it in another way. */ + if (reload_reg_rtx[j]) + mark_reload_reg_in_use (REGNO (reload_reg_rtx[j]), reload_opnum[j], + reload_when_needed[j], reload_mode[j]); + } + + if (n_reloads > 1) + qsort (reload_order, n_reloads, sizeof (short), reload_reg_class_lower); + + bcopy (reload_reg_rtx, save_reload_reg_rtx, sizeof reload_reg_rtx); + bcopy (reload_inherited, save_reload_inherited, sizeof reload_inherited); + bcopy (reload_inheritance_insn, save_reload_inheritance_insn, + sizeof reload_inheritance_insn); + bcopy (reload_override_in, save_reload_override_in, + sizeof reload_override_in); + bcopy (reload_spill_index, save_reload_spill_index, + sizeof reload_spill_index); + COPY_HARD_REG_SET (save_reload_reg_used, reload_reg_used); + COPY_HARD_REG_SET (save_reload_reg_used_at_all, reload_reg_used_at_all); + COPY_HARD_REG_SET (save_reload_reg_used_in_op_addr, + reload_reg_used_in_op_addr); + COPY_HARD_REG_SET (save_reload_reg_used_in_insn, + reload_reg_used_in_insn); + COPY_HARD_REG_SET (save_reload_reg_used_in_other_addr, + reload_reg_used_in_other_addr); + + for (i = 0; i < reload_n_operands; i++) + { + COPY_HARD_REG_SET (save_reload_reg_used_in_output[i], + reload_reg_used_in_output[i]); + COPY_HARD_REG_SET (save_reload_reg_used_in_input[i], + reload_reg_used_in_input[i]); + COPY_HARD_REG_SET (save_reload_reg_used_in_input_addr[i], + reload_reg_used_in_input_addr[i]); + COPY_HARD_REG_SET (save_reload_reg_used_in_output_addr[i], + reload_reg_used_in_output_addr[i]); + } + + /* If -O, try first with inheritance, then turning it off. + If not -O, don't do inheritance. + Using inheritance when not optimizing leads to paradoxes + with fp on the 68k: fp numbers (not NaNs) fail to be equal to themselves + because one side of the comparison might be inherited. */ + + for (inheritance = optimize > 0; inheritance >= 0; inheritance--) + { + /* Process the reloads in order of preference just found. + Beyond this point, subregs can be found in reload_reg_rtx. + + This used to look for an existing reloaded home for all + of the reloads, and only then perform any new reloads. + But that could lose if the reloads were done out of reg-class order + because a later reload with a looser constraint might have an old + home in a register needed by an earlier reload with a tighter constraint. + + To solve this, we make two passes over the reloads, in the order + described above. In the first pass we try to inherit a reload + from a previous insn. If there is a later reload that needs a + class that is a proper subset of the class being processed, we must + also allocate a spill register during the first pass. + + Then make a second pass over the reloads to allocate any reloads + that haven't been given registers yet. */ + + CLEAR_HARD_REG_SET (reload_reg_used_for_inherit); + + for (j = 0; j < n_reloads; j++) + { + register int r = reload_order[j]; + + /* Ignore reloads that got marked inoperative. */ + if (reload_out[r] == 0 && reload_in[r] == 0 && ! reload_secondary_p[r]) + continue; + + /* If find_reloads chose a to use reload_in or reload_out as a reload + register, we don't need to chose one. Otherwise, try even if it found + one since we might save an insn if we find the value lying around. */ + if (reload_in[r] != 0 && reload_reg_rtx[r] != 0 + && (rtx_equal_p (reload_in[r], reload_reg_rtx[r]) + || rtx_equal_p (reload_out[r], reload_reg_rtx[r]))) + continue; + +#if 0 /* No longer needed for correct operation. + It might give better code, or might not; worth an experiment? */ + /* If this is an optional reload, we can't inherit from earlier insns + until we are sure that any non-optional reloads have been allocated. + The following code takes advantage of the fact that optional reloads + are at the end of reload_order. */ + if (reload_optional[r] != 0) + for (i = 0; i < j; i++) + if ((reload_out[reload_order[i]] != 0 + || reload_in[reload_order[i]] != 0 + || reload_secondary_p[reload_order[i]]) + && ! reload_optional[reload_order[i]] + && reload_reg_rtx[reload_order[i]] == 0) + allocate_reload_reg (reload_order[i], insn, 0, inheritance); +#endif + + /* First see if this pseudo is already available as reloaded + for a previous insn. We cannot try to inherit for reloads + that are smaller than the maximum number of registers needed + for groups unless the register we would allocate cannot be used + for the groups. + + We could check here to see if this is a secondary reload for + an object that is already in a register of the desired class. + This would avoid the need for the secondary reload register. + But this is complex because we can't easily determine what + objects might want to be loaded via this reload. So let a register + be allocated here. In `emit_reload_insns' we suppress one of the + loads in the case described above. */ + + if (inheritance) + { + register int regno = -1; + enum machine_mode mode; + + if (reload_in[r] == 0) + ; + else if (GET_CODE (reload_in[r]) == REG) + { + regno = REGNO (reload_in[r]); + mode = GET_MODE (reload_in[r]); + } + else if (GET_CODE (reload_in_reg[r]) == REG) + { + regno = REGNO (reload_in_reg[r]); + mode = GET_MODE (reload_in_reg[r]); + } +#if 0 + /* This won't work, since REGNO can be a pseudo reg number. + Also, it takes much more hair to keep track of all the things + that can invalidate an inherited reload of part of a pseudoreg. */ + else if (GET_CODE (reload_in[r]) == SUBREG + && GET_CODE (SUBREG_REG (reload_in[r])) == REG) + regno = REGNO (SUBREG_REG (reload_in[r])) + SUBREG_WORD (reload_in[r]); +#endif + + if (regno >= 0 && reg_last_reload_reg[regno] != 0) + { + i = spill_reg_order[REGNO (reg_last_reload_reg[regno])]; + + if (reg_reloaded_contents[i] == regno + && (GET_MODE_SIZE (GET_MODE (reg_last_reload_reg[regno])) + >= GET_MODE_SIZE (mode)) + && HARD_REGNO_MODE_OK (spill_regs[i], reload_mode[r]) + && TEST_HARD_REG_BIT (reg_class_contents[(int) reload_reg_class[r]], + spill_regs[i]) + && (reload_nregs[r] == max_group_size + || ! TEST_HARD_REG_BIT (reg_class_contents[(int) group_class], + spill_regs[i])) + && reload_reg_free_p (spill_regs[i], reload_opnum[r], + reload_when_needed[r]) + && reload_reg_free_before_p (spill_regs[i], + reload_opnum[r], + reload_when_needed[r])) + { + /* If a group is needed, verify that all the subsequent + registers still have their values intact. */ + int nr + = HARD_REGNO_NREGS (spill_regs[i], reload_mode[r]); + int k; + + for (k = 1; k < nr; k++) + if (reg_reloaded_contents[spill_reg_order[spill_regs[i] + k]] + != regno) + break; + + if (k == nr) + { + int i1; + + /* We found a register that contains the + value we need. If this register is the + same as an `earlyclobber' operand of the + current insn, just mark it as a place to + reload from since we can't use it as the + reload register itself. */ + + for (i1 = 0; i1 < n_earlyclobbers; i1++) + if (reg_overlap_mentioned_for_reload_p + (reg_last_reload_reg[regno], + reload_earlyclobbers[i1])) + break; + + if (i1 != n_earlyclobbers + /* Don't really use the inherited spill reg + if we need it wider than we've got it. */ + || (GET_MODE_SIZE (reload_mode[r]) + > GET_MODE_SIZE (mode))) + reload_override_in[r] = reg_last_reload_reg[regno]; + else + { + /* We can use this as a reload reg. */ + /* Mark the register as in use for this part of + the insn. */ + mark_reload_reg_in_use (spill_regs[i], + reload_opnum[r], + reload_when_needed[r], + reload_mode[r]); + reload_reg_rtx[r] = reg_last_reload_reg[regno]; + reload_inherited[r] = 1; + reload_inheritance_insn[r] + = reg_reloaded_insn[i]; + reload_spill_index[r] = i; + SET_HARD_REG_BIT (reload_reg_used_for_inherit, + spill_regs[i]); + } + } + } + } + } + + /* Here's another way to see if the value is already lying around. */ + if (inheritance + && reload_in[r] != 0 + && ! reload_inherited[r] + && reload_out[r] == 0 + && (CONSTANT_P (reload_in[r]) + || GET_CODE (reload_in[r]) == PLUS + || GET_CODE (reload_in[r]) == REG + || GET_CODE (reload_in[r]) == MEM) + && (reload_nregs[r] == max_group_size + || ! reg_classes_intersect_p (reload_reg_class[r], group_class))) + { + register rtx equiv + = find_equiv_reg (reload_in[r], insn, reload_reg_class[r], + -1, NULL_PTR, 0, reload_mode[r]); + int regno; + + if (equiv != 0) + { + if (GET_CODE (equiv) == REG) + regno = REGNO (equiv); + else if (GET_CODE (equiv) == SUBREG) + { + regno = REGNO (SUBREG_REG (equiv)); + if (regno < FIRST_PSEUDO_REGISTER) + regno += SUBREG_WORD (equiv); + } + else + abort (); + } + + /* If we found a spill reg, reject it unless it is free + and of the desired class. */ + if (equiv != 0 + && ((spill_reg_order[regno] >= 0 + && ! reload_reg_free_before_p (regno, reload_opnum[r], + reload_when_needed[r])) + || ! TEST_HARD_REG_BIT (reg_class_contents[(int) reload_reg_class[r]], + regno))) + equiv = 0; + + if (equiv != 0 && TEST_HARD_REG_BIT (reload_reg_used_at_all, regno)) + equiv = 0; + + if (equiv != 0 && ! HARD_REGNO_MODE_OK (regno, reload_mode[r])) + equiv = 0; + + /* We found a register that contains the value we need. + If this register is the same as an `earlyclobber' operand + of the current insn, just mark it as a place to reload from + since we can't use it as the reload register itself. */ + + if (equiv != 0) + for (i = 0; i < n_earlyclobbers; i++) + if (reg_overlap_mentioned_for_reload_p (equiv, + reload_earlyclobbers[i])) + { + reload_override_in[r] = equiv; + equiv = 0; + break; + } + + /* JRV: If the equiv register we have found is explicitly + clobbered in the current insn, mark but don't use, as above. */ + + if (equiv != 0 && regno_clobbered_p (regno, insn)) + { + reload_override_in[r] = equiv; + equiv = 0; + } + + /* If we found an equivalent reg, say no code need be generated + to load it, and use it as our reload reg. */ + if (equiv != 0 && regno != FRAME_POINTER_REGNUM) + { + reload_reg_rtx[r] = equiv; + reload_inherited[r] = 1; + /* If it is a spill reg, + mark the spill reg as in use for this insn. */ + i = spill_reg_order[regno]; + if (i >= 0) + { + mark_reload_reg_in_use (regno, reload_opnum[r], + reload_when_needed[r], + reload_mode[r]); + SET_HARD_REG_BIT (reload_reg_used_for_inherit, regno); + } + } + } + + /* If we found a register to use already, or if this is an optional + reload, we are done. */ + if (reload_reg_rtx[r] != 0 || reload_optional[r] != 0) + continue; + +#if 0 /* No longer needed for correct operation. Might or might not + give better code on the average. Want to experiment? */ + + /* See if there is a later reload that has a class different from our + class that intersects our class or that requires less register + than our reload. If so, we must allocate a register to this + reload now, since that reload might inherit a previous reload + and take the only available register in our class. Don't do this + for optional reloads since they will force all previous reloads + to be allocated. Also don't do this for reloads that have been + turned off. */ + + for (i = j + 1; i < n_reloads; i++) + { + int s = reload_order[i]; + + if ((reload_in[s] == 0 && reload_out[s] == 0 + && ! reload_secondary_p[s]) + || reload_optional[s]) + continue; + + if ((reload_reg_class[s] != reload_reg_class[r] + && reg_classes_intersect_p (reload_reg_class[r], + reload_reg_class[s])) + || reload_nregs[s] < reload_nregs[r]) + break; + } + + if (i == n_reloads) + continue; + + allocate_reload_reg (r, insn, j == n_reloads - 1, inheritance); +#endif + } + + /* Now allocate reload registers for anything non-optional that + didn't get one yet. */ + for (j = 0; j < n_reloads; j++) + { + register int r = reload_order[j]; + + /* Ignore reloads that got marked inoperative. */ + if (reload_out[r] == 0 && reload_in[r] == 0 && ! reload_secondary_p[r]) + continue; + + /* Skip reloads that already have a register allocated or are + optional. */ + if (reload_reg_rtx[r] != 0 || reload_optional[r]) + continue; + + if (! allocate_reload_reg (r, insn, j == n_reloads - 1, inheritance)) + break; + } + + /* If that loop got all the way, we have won. */ + if (j == n_reloads) + break; + + fail: + /* Loop around and try without any inheritance. */ + /* First undo everything done by the failed attempt + to allocate with inheritance. */ + bcopy (save_reload_reg_rtx, reload_reg_rtx, sizeof reload_reg_rtx); + bcopy (save_reload_inherited, reload_inherited, sizeof reload_inherited); + bcopy (save_reload_inheritance_insn, reload_inheritance_insn, + sizeof reload_inheritance_insn); + bcopy (save_reload_override_in, reload_override_in, + sizeof reload_override_in); + bcopy (save_reload_spill_index, reload_spill_index, + sizeof reload_spill_index); + COPY_HARD_REG_SET (reload_reg_used, save_reload_reg_used); + COPY_HARD_REG_SET (reload_reg_used_at_all, save_reload_reg_used_at_all); + COPY_HARD_REG_SET (reload_reg_used_in_op_addr, + save_reload_reg_used_in_op_addr); + COPY_HARD_REG_SET (reload_reg_used_in_insn, + save_reload_reg_used_in_insn); + COPY_HARD_REG_SET (reload_reg_used_in_other_addr, + save_reload_reg_used_in_other_addr); + + for (i = 0; i < reload_n_operands; i++) + { + COPY_HARD_REG_SET (reload_reg_used_in_input[i], + save_reload_reg_used_in_input[i]); + COPY_HARD_REG_SET (reload_reg_used_in_output[i], + save_reload_reg_used_in_output[i]); + COPY_HARD_REG_SET (reload_reg_used_in_input_addr[i], + save_reload_reg_used_in_input_addr[i]); + COPY_HARD_REG_SET (reload_reg_used_in_output_addr[i], + save_reload_reg_used_in_output_addr[i]); + } + } + + /* If we thought we could inherit a reload, because it seemed that + nothing else wanted the same reload register earlier in the insn, + verify that assumption, now that all reloads have been assigned. */ + + for (j = 0; j < n_reloads; j++) + { + register int r = reload_order[j]; + + if (reload_inherited[r] && reload_reg_rtx[r] != 0 + && ! reload_reg_free_before_p (true_regnum (reload_reg_rtx[r]), + reload_opnum[r], + reload_when_needed[r])) + reload_inherited[r] = 0; + + /* If we found a better place to reload from, + validate it in the same fashion, if it is a reload reg. */ + if (reload_override_in[r] + && (GET_CODE (reload_override_in[r]) == REG + || GET_CODE (reload_override_in[r]) == SUBREG)) + { + int regno = true_regnum (reload_override_in[r]); + if (spill_reg_order[regno] >= 0 + && ! reload_reg_free_before_p (regno, reload_opnum[r], + reload_when_needed[r])) + reload_override_in[r] = 0; + } + } + + /* Now that reload_override_in is known valid, + actually override reload_in. */ + for (j = 0; j < n_reloads; j++) + if (reload_override_in[j]) + reload_in[j] = reload_override_in[j]; + + /* If this reload won't be done because it has been cancelled or is + optional and not inherited, clear reload_reg_rtx so other + routines (such as subst_reloads) don't get confused. */ + for (j = 0; j < n_reloads; j++) + if (reload_reg_rtx[j] != 0 + && ((reload_optional[j] && ! reload_inherited[j]) + || (reload_in[j] == 0 && reload_out[j] == 0 + && ! reload_secondary_p[j]))) + { + int regno = true_regnum (reload_reg_rtx[j]); + + if (spill_reg_order[regno] >= 0) + clear_reload_reg_in_use (regno, reload_opnum[j], + reload_when_needed[j], reload_mode[j]); + reload_reg_rtx[j] = 0; + } + + /* Record which pseudos and which spill regs have output reloads. */ + for (j = 0; j < n_reloads; j++) + { + register int r = reload_order[j]; + + i = reload_spill_index[r]; + + /* I is nonneg if this reload used one of the spill regs. + If reload_reg_rtx[r] is 0, this is an optional reload + that we opted to ignore. */ + if (reload_out[r] != 0 && GET_CODE (reload_out[r]) == REG + && reload_reg_rtx[r] != 0) + { + register int nregno = REGNO (reload_out[r]); + int nr = 1; + + if (nregno < FIRST_PSEUDO_REGISTER) + nr = HARD_REGNO_NREGS (nregno, reload_mode[r]); + + while (--nr >= 0) + reg_has_output_reload[nregno + nr] = 1; + + if (i >= 0) + { + nr = HARD_REGNO_NREGS (spill_regs[i], reload_mode[r]); + while (--nr >= 0) + SET_HARD_REG_BIT (reg_is_output_reload, spill_regs[i] + nr); + } + + if (reload_when_needed[r] != RELOAD_OTHER + && reload_when_needed[r] != RELOAD_FOR_OUTPUT + && reload_when_needed[r] != RELOAD_FOR_INSN) + abort (); + } + } +} + +/* If SMALL_REGISTER_CLASSES are defined, we may not have merged two + reloads of the same item for fear that we might not have enough reload + registers. However, normally they will get the same reload register + and hence actually need not be loaded twice. + + Here we check for the most common case of this phenomenon: when we have + a number of reloads for the same object, each of which were allocated + the same reload_reg_rtx, that reload_reg_rtx is not used for any other + reload, and is not modified in the insn itself. If we find such, + merge all the reloads and set the resulting reload to RELOAD_OTHER. + This will not increase the number of spill registers needed and will + prevent redundant code. */ + +#ifdef SMALL_REGISTER_CLASSES + +static void +merge_assigned_reloads (insn) + rtx insn; +{ + int i, j; + + /* Scan all the reloads looking for ones that only load values and + are not already RELOAD_OTHER and ones whose reload_reg_rtx are + assigned and not modified by INSN. */ + + for (i = 0; i < n_reloads; i++) + { + if (reload_in[i] == 0 || reload_when_needed[i] == RELOAD_OTHER + || reload_out[i] != 0 || reload_reg_rtx[i] == 0 + || reg_set_p (reload_reg_rtx[i], insn)) + continue; + + /* Look at all other reloads. Ensure that the only use of this + reload_reg_rtx is in a reload that just loads the same value + as we do. Note that any secondary reloads must be of the identical + class since the values, modes, and result registers are the + same, so we need not do anything with any secondary reloads. */ + + for (j = 0; j < n_reloads; j++) + { + if (i == j || reload_reg_rtx[j] == 0 + || ! reg_overlap_mentioned_p (reload_reg_rtx[j], + reload_reg_rtx[i])) + continue; + + /* If the reload regs aren't exactly the same (e.g, different modes) + or if the values are different, we can't merge anything with this + reload register. */ + + if (! rtx_equal_p (reload_reg_rtx[i], reload_reg_rtx[j]) + || reload_out[j] != 0 || reload_in[j] == 0 + || ! rtx_equal_p (reload_in[i], reload_in[j])) + break; + } + + /* If all is OK, merge the reloads. Only set this to RELOAD_OTHER if + we, in fact, found any matching reloads. */ + + if (j == n_reloads) + { + for (j = 0; j < n_reloads; j++) + if (i != j && reload_reg_rtx[j] != 0 + && rtx_equal_p (reload_reg_rtx[i], reload_reg_rtx[j])) + { + reload_when_needed[i] = RELOAD_OTHER; + reload_in[j] = 0; + transfer_replacements (i, j); + } + + /* If this is now RELOAD_OTHER, look for any reloads that load + parts of this operand and set them to RELOAD_FOR_OTHER_ADDRESS + if they were for inputs, RELOAD_OTHER for outputs. Note that + this test is equivalent to looking for reloads for this operand + number. */ + + if (reload_when_needed[i] == RELOAD_OTHER) + for (j = 0; j < n_reloads; j++) + if (reload_in[j] != 0 + && reload_when_needed[i] != RELOAD_OTHER + && reg_overlap_mentioned_for_reload_p (reload_in[j], + reload_in[i])) + reload_when_needed[j] + = reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS + ? RELOAD_FOR_OTHER_ADDRESS : RELOAD_OTHER; + } + } +} +#endif /* SMALL_RELOAD_CLASSES */ + +/* Output insns to reload values in and out of the chosen reload regs. */ + +static void +emit_reload_insns (insn) + rtx insn; +{ + register int j; + rtx input_reload_insns[MAX_RECOG_OPERANDS]; + rtx other_input_address_reload_insns = 0; + rtx other_input_reload_insns = 0; + rtx input_address_reload_insns[MAX_RECOG_OPERANDS]; + rtx output_reload_insns[MAX_RECOG_OPERANDS]; + rtx output_address_reload_insns[MAX_RECOG_OPERANDS]; + rtx operand_reload_insns = 0; + rtx following_insn = NEXT_INSN (insn); + rtx before_insn = insn; + int special; + /* Values to be put in spill_reg_store are put here first. */ + rtx new_spill_reg_store[FIRST_PSEUDO_REGISTER]; + + for (j = 0; j < reload_n_operands; j++) + input_reload_insns[j] = input_address_reload_insns[j] + = output_reload_insns[j] = output_address_reload_insns[j] = 0; + + /* If this is a CALL_INSN preceded by USE insns, any reload insns + must go in front of the first USE insn, not in front of INSN. */ + + if (GET_CODE (insn) == CALL_INSN && GET_CODE (PREV_INSN (insn)) == INSN + && GET_CODE (PATTERN (PREV_INSN (insn))) == USE) + while (GET_CODE (PREV_INSN (before_insn)) == INSN + && GET_CODE (PATTERN (PREV_INSN (before_insn))) == USE) + before_insn = PREV_INSN (before_insn); + + /* If INSN is followed by any CLOBBER insns made by find_reloads, + put our reloads after them since they may otherwise be + misinterpreted. */ + + while (GET_CODE (following_insn) == INSN + && GET_MODE (following_insn) == DImode + && GET_CODE (PATTERN (following_insn)) == CLOBBER + && NEXT_INSN (following_insn) != 0) + following_insn = NEXT_INSN (following_insn); + + /* Now output the instructions to copy the data into and out of the + reload registers. Do these in the order that the reloads were reported, + since reloads of base and index registers precede reloads of operands + and the operands may need the base and index registers reloaded. */ + + for (j = 0; j < n_reloads; j++) + { + register rtx old; + rtx oldequiv_reg = 0; + rtx store_insn = 0; + + old = reload_in[j]; + if (old != 0 && ! reload_inherited[j] + && ! rtx_equal_p (reload_reg_rtx[j], old) + && reload_reg_rtx[j] != 0) + { + register rtx reloadreg = reload_reg_rtx[j]; + rtx oldequiv = 0; + enum machine_mode mode; + rtx *where; + + /* Determine the mode to reload in. + This is very tricky because we have three to choose from. + There is the mode the insn operand wants (reload_inmode[J]). + There is the mode of the reload register RELOADREG. + There is the intrinsic mode of the operand, which we could find + by stripping some SUBREGs. + It turns out that RELOADREG's mode is irrelevant: + we can change that arbitrarily. + + Consider (SUBREG:SI foo:QI) as an operand that must be SImode; + then the reload reg may not support QImode moves, so use SImode. + If foo is in memory due to spilling a pseudo reg, this is safe, + because the QImode value is in the least significant part of a + slot big enough for a SImode. If foo is some other sort of + memory reference, then it is impossible to reload this case, + so previous passes had better make sure this never happens. + + Then consider a one-word union which has SImode and one of its + members is a float, being fetched as (SUBREG:SF union:SI). + We must fetch that as SFmode because we could be loading into + a float-only register. In this case OLD's mode is correct. + + Consider an immediate integer: it has VOIDmode. Here we need + to get a mode from something else. + + In some cases, there is a fourth mode, the operand's + containing mode. If the insn specifies a containing mode for + this operand, it overrides all others. + + I am not sure whether the algorithm here is always right, + but it does the right things in those cases. */ + + mode = GET_MODE (old); + if (mode == VOIDmode) + mode = reload_inmode[j]; + +#ifdef SECONDARY_INPUT_RELOAD_CLASS + /* If we need a secondary register for this operation, see if + the value is already in a register in that class. Don't + do this if the secondary register will be used as a scratch + register. */ + + if (reload_secondary_reload[j] >= 0 + && reload_secondary_icode[j] == CODE_FOR_nothing + && optimize) + oldequiv + = find_equiv_reg (old, insn, + reload_reg_class[reload_secondary_reload[j]], + -1, NULL_PTR, 0, mode); +#endif + + /* If reloading from memory, see if there is a register + that already holds the same value. If so, reload from there. + We can pass 0 as the reload_reg_p argument because + any other reload has either already been emitted, + in which case find_equiv_reg will see the reload-insn, + or has yet to be emitted, in which case it doesn't matter + because we will use this equiv reg right away. */ + + if (oldequiv == 0 && optimize + && (GET_CODE (old) == MEM + || (GET_CODE (old) == REG + && REGNO (old) >= FIRST_PSEUDO_REGISTER + && reg_renumber[REGNO (old)] < 0))) + oldequiv = find_equiv_reg (old, insn, ALL_REGS, + -1, NULL_PTR, 0, mode); + + if (oldequiv) + { + int regno = true_regnum (oldequiv); + + /* If OLDEQUIV is a spill register, don't use it for this + if any other reload needs it at an earlier stage of this insn + or at this stage. */ + if (spill_reg_order[regno] >= 0 + && (! reload_reg_free_p (regno, reload_opnum[j], + reload_when_needed[j]) + || ! reload_reg_free_before_p (regno, reload_opnum[j], + reload_when_needed[j]))) + oldequiv = 0; + + /* If OLDEQUIV is not a spill register, + don't use it if any other reload wants it. */ + if (spill_reg_order[regno] < 0) + { + int k; + for (k = 0; k < n_reloads; k++) + if (reload_reg_rtx[k] != 0 && k != j + && reg_overlap_mentioned_for_reload_p (reload_reg_rtx[k], + oldequiv)) + { + oldequiv = 0; + break; + } + } + + /* If it is no cheaper to copy from OLDEQUIV into the + reload register than it would be to move from memory, + don't use it. Likewise, if we need a secondary register + or memory. */ + + if (oldequiv != 0 + && ((REGNO_REG_CLASS (regno) != reload_reg_class[j] + && (REGISTER_MOVE_COST (REGNO_REG_CLASS (regno), + reload_reg_class[j]) + >= MEMORY_MOVE_COST (mode))) +#ifdef SECONDARY_INPUT_RELOAD_CLASS + || (SECONDARY_INPUT_RELOAD_CLASS (reload_reg_class[j], + mode, oldequiv) + != NO_REGS) +#endif +#ifdef SECONDARY_MEMORY_NEEDED + || SECONDARY_MEMORY_NEEDED (reload_reg_class[j], + REGNO_REG_CLASS (regno), + mode) +#endif + )) + oldequiv = 0; + } + + if (oldequiv == 0) + oldequiv = old; + else if (GET_CODE (oldequiv) == REG) + oldequiv_reg = oldequiv; + else if (GET_CODE (oldequiv) == SUBREG) + oldequiv_reg = SUBREG_REG (oldequiv); + + /* Encapsulate both RELOADREG and OLDEQUIV into that mode, + then load RELOADREG from OLDEQUIV. */ + + if (GET_MODE (reloadreg) != mode) + reloadreg = gen_lowpart_common (mode, reloadreg); + while (GET_CODE (oldequiv) == SUBREG && GET_MODE (oldequiv) != mode) + oldequiv = SUBREG_REG (oldequiv); + if (GET_MODE (oldequiv) != VOIDmode + && mode != GET_MODE (oldequiv)) + oldequiv = gen_rtx (SUBREG, mode, oldequiv, 0); + + /* Switch to the right place to emit the reload insns. */ + switch (reload_when_needed[j]) + { + case RELOAD_OTHER: + where = &other_input_reload_insns; + break; + case RELOAD_FOR_INPUT: + where = &input_reload_insns[reload_opnum[j]]; + break; + case RELOAD_FOR_INPUT_ADDRESS: + where = &input_address_reload_insns[reload_opnum[j]]; + break; + case RELOAD_FOR_OUTPUT_ADDRESS: + where = &output_address_reload_insns[reload_opnum[j]]; + break; + case RELOAD_FOR_OPERAND_ADDRESS: + where = &operand_reload_insns; + break; + case RELOAD_FOR_OTHER_ADDRESS: + where = &other_input_address_reload_insns; + break; + default: + abort (); + } + + push_to_sequence (*where); + special = 0; + + /* Auto-increment addresses must be reloaded in a special way. */ + if (GET_CODE (oldequiv) == POST_INC + || GET_CODE (oldequiv) == POST_DEC + || GET_CODE (oldequiv) == PRE_INC + || GET_CODE (oldequiv) == PRE_DEC) + { + /* We are not going to bother supporting the case where a + incremented register can't be copied directly from + OLDEQUIV since this seems highly unlikely. */ + if (reload_secondary_reload[j] >= 0) + abort (); + /* Prevent normal processing of this reload. */ + special = 1; + /* Output a special code sequence for this case. */ + inc_for_reload (reloadreg, oldequiv, reload_inc[j]); + } + + /* If we are reloading a pseudo-register that was set by the previous + insn, see if we can get rid of that pseudo-register entirely + by redirecting the previous insn into our reload register. */ + + else if (optimize && GET_CODE (old) == REG + && REGNO (old) >= FIRST_PSEUDO_REGISTER + && dead_or_set_p (insn, old) + /* This is unsafe if some other reload + uses the same reg first. */ + && reload_reg_free_before_p (REGNO (reloadreg), + reload_opnum[j], + reload_when_needed[j])) + { + rtx temp = PREV_INSN (insn); + while (temp && GET_CODE (temp) == NOTE) + temp = PREV_INSN (temp); + if (temp + && GET_CODE (temp) == INSN + && GET_CODE (PATTERN (temp)) == SET + && SET_DEST (PATTERN (temp)) == old + /* Make sure we can access insn_operand_constraint. */ + && asm_noperands (PATTERN (temp)) < 0 + /* This is unsafe if prev insn rejects our reload reg. */ + && constraint_accepts_reg_p (insn_operand_constraint[recog_memoized (temp)][0], + reloadreg) + /* This is unsafe if operand occurs more than once in current + insn. Perhaps some occurrences aren't reloaded. */ + && count_occurrences (PATTERN (insn), old) == 1 + /* Don't risk splitting a matching pair of operands. */ + && ! reg_mentioned_p (old, SET_SRC (PATTERN (temp)))) + { + /* Store into the reload register instead of the pseudo. */ + SET_DEST (PATTERN (temp)) = reloadreg; + /* If these are the only uses of the pseudo reg, + pretend for GDB it lives in the reload reg we used. */ + if (reg_n_deaths[REGNO (old)] == 1 + && reg_n_sets[REGNO (old)] == 1) + { + reg_renumber[REGNO (old)] = REGNO (reload_reg_rtx[j]); + alter_reg (REGNO (old), -1); + } + special = 1; + } + } + + /* We can't do that, so output an insn to load RELOADREG. */ + + if (! special) + { +#ifdef SECONDARY_INPUT_RELOAD_CLASS + rtx second_reload_reg = 0; + enum insn_code icode; + + /* If we have a secondary reload, pick up the secondary register + and icode, if any. If OLDEQUIV and OLD are different or + if this is an in-out reload, recompute whether or not we + still need a secondary register and what the icode should + be. If we still need a secondary register and the class or + icode is different, go back to reloading from OLD if using + OLDEQUIV means that we got the wrong type of register. We + cannot have different class or icode due to an in-out reload + because we don't make such reloads when both the input and + output need secondary reload registers. */ + + if (reload_secondary_reload[j] >= 0) + { + int secondary_reload = reload_secondary_reload[j]; + rtx real_oldequiv = oldequiv; + rtx real_old = old; + + /* If OLDEQUIV is a pseudo with a MEM, get the real MEM + and similarly for OLD. + See comments in find_secondary_reload in reload.c. */ + if (GET_CODE (oldequiv) == REG + && REGNO (oldequiv) >= FIRST_PSEUDO_REGISTER + && reg_equiv_mem[REGNO (oldequiv)] != 0) + real_oldequiv = reg_equiv_mem[REGNO (oldequiv)]; + + if (GET_CODE (old) == REG + && REGNO (old) >= FIRST_PSEUDO_REGISTER + && reg_equiv_mem[REGNO (old)] != 0) + real_old = reg_equiv_mem[REGNO (old)]; + + second_reload_reg = reload_reg_rtx[secondary_reload]; + icode = reload_secondary_icode[j]; + + if ((old != oldequiv && ! rtx_equal_p (old, oldequiv)) + || (reload_in[j] != 0 && reload_out[j] != 0)) + { + enum reg_class new_class + = SECONDARY_INPUT_RELOAD_CLASS (reload_reg_class[j], + mode, real_oldequiv); + + if (new_class == NO_REGS) + second_reload_reg = 0; + else + { + enum insn_code new_icode; + enum machine_mode new_mode; + + if (! TEST_HARD_REG_BIT (reg_class_contents[(int) new_class], + REGNO (second_reload_reg))) + oldequiv = old, real_oldequiv = real_old; + else + { + new_icode = reload_in_optab[(int) mode]; + if (new_icode != CODE_FOR_nothing + && ((insn_operand_predicate[(int) new_icode][0] + && ! ((*insn_operand_predicate[(int) new_icode][0]) + (reloadreg, mode))) + || (insn_operand_predicate[(int) new_icode][1] + && ! ((*insn_operand_predicate[(int) new_icode][1]) + (real_oldequiv, mode))))) + new_icode = CODE_FOR_nothing; + + if (new_icode == CODE_FOR_nothing) + new_mode = mode; + else + new_mode = insn_operand_mode[(int) new_icode][2]; + + if (GET_MODE (second_reload_reg) != new_mode) + { + if (!HARD_REGNO_MODE_OK (REGNO (second_reload_reg), + new_mode)) + oldequiv = old, real_oldequiv = real_old; + else + second_reload_reg + = gen_rtx (REG, new_mode, + REGNO (second_reload_reg)); + } + } + } + } + + /* If we still need a secondary reload register, check + to see if it is being used as a scratch or intermediate + register and generate code appropriately. If we need + a scratch register, use REAL_OLDEQUIV since the form of + the insn may depend on the actual address if it is + a MEM. */ + + if (second_reload_reg) + { + if (icode != CODE_FOR_nothing) + { + emit_insn (GEN_FCN (icode) (reloadreg, real_oldequiv, + second_reload_reg)); + special = 1; + } + else + { + /* See if we need a scratch register to load the + intermediate register (a tertiary reload). */ + enum insn_code tertiary_icode + = reload_secondary_icode[secondary_reload]; + + if (tertiary_icode != CODE_FOR_nothing) + { + rtx third_reload_reg + = reload_reg_rtx[reload_secondary_reload[secondary_reload]]; + + emit_insn ((GEN_FCN (tertiary_icode) + (second_reload_reg, real_oldequiv, + third_reload_reg))); + } + else + gen_input_reload (second_reload_reg, oldequiv, + reload_opnum[j], + reload_when_needed[j]); + + oldequiv = second_reload_reg; + } + } + } +#endif + + if (! special) + gen_input_reload (reloadreg, oldequiv, reload_opnum[j], + reload_when_needed[j]); + +#if defined(SECONDARY_INPUT_RELOAD_CLASS) && defined(PRESERVE_DEATH_INFO_REGNO_P) + /* We may have to make a REG_DEAD note for the secondary reload + register in the insns we just made. Find the last insn that + mentioned the register. */ + if (! special && second_reload_reg + && PRESERVE_DEATH_INFO_REGNO_P (REGNO (second_reload_reg))) + { + rtx prev; + + for (prev = get_last_insn (); prev; + prev = PREV_INSN (prev)) + if (GET_RTX_CLASS (GET_CODE (prev) == 'i') + && reg_overlap_mentioned_for_reload_p (second_reload_reg, + PATTERN (prev))) + { + REG_NOTES (prev) = gen_rtx (EXPR_LIST, REG_DEAD, + second_reload_reg, + REG_NOTES (prev)); + break; + } + } +#endif + } + + /* End this sequence. */ + *where = get_insns (); + end_sequence (); + } + + /* Add a note saying the input reload reg + dies in this insn, if anyone cares. */ +#ifdef PRESERVE_DEATH_INFO_REGNO_P + if (old != 0 + && reload_reg_rtx[j] != old + && reload_reg_rtx[j] != 0 + && reload_out[j] == 0 + && ! reload_inherited[j] + && PRESERVE_DEATH_INFO_REGNO_P (REGNO (reload_reg_rtx[j]))) + { + register rtx reloadreg = reload_reg_rtx[j]; + +#if 0 + /* We can't abort here because we need to support this for sched.c. + It's not terrible to miss a REG_DEAD note, but we should try + to figure out how to do this correctly. */ + /* The code below is incorrect for address-only reloads. */ + if (reload_when_needed[j] != RELOAD_OTHER + && reload_when_needed[j] != RELOAD_FOR_INPUT) + abort (); +#endif + + /* Add a death note to this insn, for an input reload. */ + + if ((reload_when_needed[j] == RELOAD_OTHER + || reload_when_needed[j] == RELOAD_FOR_INPUT) + && ! dead_or_set_p (insn, reloadreg)) + REG_NOTES (insn) + = gen_rtx (EXPR_LIST, REG_DEAD, + reloadreg, REG_NOTES (insn)); + } + + /* When we inherit a reload, the last marked death of the reload reg + may no longer really be a death. */ + if (reload_reg_rtx[j] != 0 + && PRESERVE_DEATH_INFO_REGNO_P (REGNO (reload_reg_rtx[j])) + && reload_inherited[j]) + { + /* Handle inheriting an output reload. + Remove the death note from the output reload insn. */ + if (reload_spill_index[j] >= 0 + && GET_CODE (reload_in[j]) == REG + && spill_reg_store[reload_spill_index[j]] != 0 + && find_regno_note (spill_reg_store[reload_spill_index[j]], + REG_DEAD, REGNO (reload_reg_rtx[j]))) + remove_death (REGNO (reload_reg_rtx[j]), + spill_reg_store[reload_spill_index[j]]); + /* Likewise for input reloads that were inherited. */ + else if (reload_spill_index[j] >= 0 + && GET_CODE (reload_in[j]) == REG + && spill_reg_store[reload_spill_index[j]] == 0 + && reload_inheritance_insn[j] != 0 + && find_regno_note (reload_inheritance_insn[j], REG_DEAD, + REGNO (reload_reg_rtx[j]))) + remove_death (REGNO (reload_reg_rtx[j]), + reload_inheritance_insn[j]); + else + { + rtx prev; + + /* We got this register from find_equiv_reg. + Search back for its last death note and get rid of it. + But don't search back too far. + Don't go past a place where this reg is set, + since a death note before that remains valid. */ + for (prev = PREV_INSN (insn); + prev && GET_CODE (prev) != CODE_LABEL; + prev = PREV_INSN (prev)) + if (GET_RTX_CLASS (GET_CODE (prev)) == 'i' + && dead_or_set_p (prev, reload_reg_rtx[j])) + { + if (find_regno_note (prev, REG_DEAD, + REGNO (reload_reg_rtx[j]))) + remove_death (REGNO (reload_reg_rtx[j]), prev); + break; + } + } + } + + /* We might have used find_equiv_reg above to choose an alternate + place from which to reload. If so, and it died, we need to remove + that death and move it to one of the insns we just made. */ + + if (oldequiv_reg != 0 + && PRESERVE_DEATH_INFO_REGNO_P (true_regnum (oldequiv_reg))) + { + rtx prev, prev1; + + for (prev = PREV_INSN (insn); prev && GET_CODE (prev) != CODE_LABEL; + prev = PREV_INSN (prev)) + if (GET_RTX_CLASS (GET_CODE (prev)) == 'i' + && dead_or_set_p (prev, oldequiv_reg)) + { + if (find_regno_note (prev, REG_DEAD, REGNO (oldequiv_reg))) + { + for (prev1 = this_reload_insn; + prev1; prev1 = PREV_INSN (prev1)) + if (GET_RTX_CLASS (GET_CODE (prev1) == 'i') + && reg_overlap_mentioned_for_reload_p (oldequiv_reg, + PATTERN (prev1))) + { + REG_NOTES (prev1) = gen_rtx (EXPR_LIST, REG_DEAD, + oldequiv_reg, + REG_NOTES (prev1)); + break; + } + remove_death (REGNO (oldequiv_reg), prev); + } + break; + } + } +#endif + + /* If we are reloading a register that was recently stored in with an + output-reload, see if we can prove there was + actually no need to store the old value in it. */ + + if (optimize && reload_inherited[j] && reload_spill_index[j] >= 0 + && reload_in[j] != 0 + && GET_CODE (reload_in[j]) == REG +#if 0 + /* There doesn't seem to be any reason to restrict this to pseudos + and doing so loses in the case where we are copying from a + register of the wrong class. */ + && REGNO (reload_in[j]) >= FIRST_PSEUDO_REGISTER +#endif + && spill_reg_store[reload_spill_index[j]] != 0 + /* This is unsafe if some other reload uses the same reg first. */ + && reload_reg_free_before_p (spill_regs[reload_spill_index[j]], + reload_opnum[j], reload_when_needed[j]) + && dead_or_set_p (insn, reload_in[j]) + /* This is unsafe if operand occurs more than once in current + insn. Perhaps some occurrences weren't reloaded. */ + && count_occurrences (PATTERN (insn), reload_in[j]) == 1) + delete_output_reload (insn, j, + spill_reg_store[reload_spill_index[j]]); + + /* Input-reloading is done. Now do output-reloading, + storing the value from the reload-register after the main insn + if reload_out[j] is nonzero. + + ??? At some point we need to support handling output reloads of + JUMP_INSNs or insns that set cc0. */ + old = reload_out[j]; + if (old != 0 + && reload_reg_rtx[j] != old + && reload_reg_rtx[j] != 0) + { + register rtx reloadreg = reload_reg_rtx[j]; + register rtx second_reloadreg = 0; + rtx note, p; + enum machine_mode mode; + int special = 0; + + /* An output operand that dies right away does need a reload, + but need not be copied from it. Show the new location in the + REG_UNUSED note. */ + if ((GET_CODE (old) == REG || GET_CODE (old) == SCRATCH) + && (note = find_reg_note (insn, REG_UNUSED, old)) != 0) + { + XEXP (note, 0) = reload_reg_rtx[j]; + continue; + } + else if (GET_CODE (old) == SCRATCH) + /* If we aren't optimizing, there won't be a REG_UNUSED note, + but we don't want to make an output reload. */ + continue; + +#if 0 + /* Strip off of OLD any size-increasing SUBREGs such as + (SUBREG:SI foo:QI 0). */ + + while (GET_CODE (old) == SUBREG && SUBREG_WORD (old) == 0 + && (GET_MODE_SIZE (GET_MODE (old)) + > GET_MODE_SIZE (GET_MODE (SUBREG_REG (old))))) + old = SUBREG_REG (old); +#endif + + /* If is a JUMP_INSN, we can't support output reloads yet. */ + if (GET_CODE (insn) == JUMP_INSN) + abort (); + + push_to_sequence (output_reload_insns[reload_opnum[j]]); + + /* Determine the mode to reload in. + See comments above (for input reloading). */ + + mode = GET_MODE (old); + if (mode == VOIDmode) + { + /* VOIDmode should never happen for an output. */ + if (asm_noperands (PATTERN (insn)) < 0) + /* It's the compiler's fault. */ + abort (); + error_for_asm (insn, "output operand is constant in `asm'"); + /* Prevent crash--use something we know is valid. */ + mode = word_mode; + old = gen_rtx (REG, mode, REGNO (reloadreg)); + } + + if (GET_MODE (reloadreg) != mode) + reloadreg = gen_lowpart_common (mode, reloadreg); + +#ifdef SECONDARY_OUTPUT_RELOAD_CLASS + + /* If we need two reload regs, set RELOADREG to the intermediate + one, since it will be stored into OUT. We might need a secondary + register only for an input reload, so check again here. */ + + if (reload_secondary_reload[j] >= 0) + { + rtx real_old = old; + + if (GET_CODE (old) == REG && REGNO (old) >= FIRST_PSEUDO_REGISTER + && reg_equiv_mem[REGNO (old)] != 0) + real_old = reg_equiv_mem[REGNO (old)]; + + if((SECONDARY_OUTPUT_RELOAD_CLASS (reload_reg_class[j], + mode, real_old) + != NO_REGS)) + { + second_reloadreg = reloadreg; + reloadreg = reload_reg_rtx[reload_secondary_reload[j]]; + + /* See if RELOADREG is to be used as a scratch register + or as an intermediate register. */ + if (reload_secondary_icode[j] != CODE_FOR_nothing) + { + emit_insn ((GEN_FCN (reload_secondary_icode[j]) + (real_old, second_reloadreg, reloadreg))); + special = 1; + } + else + { + /* See if we need both a scratch and intermediate reload + register. */ + int secondary_reload = reload_secondary_reload[j]; + enum insn_code tertiary_icode + = reload_secondary_icode[secondary_reload]; + rtx pat; + + if (GET_MODE (reloadreg) != mode) + reloadreg = gen_rtx (REG, mode, REGNO (reloadreg)); + + if (tertiary_icode != CODE_FOR_nothing) + { + rtx third_reloadreg + = reload_reg_rtx[reload_secondary_reload[secondary_reload]]; + pat = (GEN_FCN (tertiary_icode) + (reloadreg, second_reloadreg, third_reloadreg)); + } +#ifdef SECONDARY_MEMORY_NEEDED + /* If we need a memory location to do the move, do it that way. */ + else if (GET_CODE (reloadreg) == REG + && REGNO (reloadreg) < FIRST_PSEUDO_REGISTER + && SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (REGNO (reloadreg)), + REGNO_REG_CLASS (REGNO (second_reloadreg)), + GET_MODE (second_reloadreg))) + { + /* Get the memory to use and rewrite both registers + to its mode. */ + rtx loc + = get_secondary_mem (reloadreg, + GET_MODE (second_reloadreg), + reload_opnum[j], + reload_when_needed[j]); + rtx tmp_reloadreg; + + if (GET_MODE (loc) != GET_MODE (second_reloadreg)) + second_reloadreg = gen_rtx (REG, GET_MODE (loc), + REGNO (second_reloadreg)); + + if (GET_MODE (loc) != GET_MODE (reloadreg)) + tmp_reloadreg = gen_rtx (REG, GET_MODE (loc), + REGNO (reloadreg)); + else + tmp_reloadreg = reloadreg; + + emit_move_insn (loc, second_reloadreg); + pat = gen_move_insn (tmp_reloadreg, loc); + } +#endif + else + pat = gen_move_insn (reloadreg, second_reloadreg); + + emit_insn (pat); + } + } + } +#endif + + /* Output the last reload insn. */ + if (! special) + { +#ifdef SECONDARY_MEMORY_NEEDED + /* If we need a memory location to do the move, do it that way. */ + if (GET_CODE (old) == REG && REGNO (old) < FIRST_PSEUDO_REGISTER + && SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (REGNO (old)), + REGNO_REG_CLASS (REGNO (reloadreg)), + GET_MODE (reloadreg))) + { + /* Get the memory to use and rewrite both registers to + its mode. */ + rtx loc = get_secondary_mem (old, GET_MODE (reloadreg), + reload_opnum[j], + reload_when_needed[j]); + + if (GET_MODE (loc) != GET_MODE (reloadreg)) + reloadreg = gen_rtx (REG, GET_MODE (loc), + REGNO (reloadreg)); + + if (GET_MODE (loc) != GET_MODE (old)) + old = gen_rtx (REG, GET_MODE (loc), REGNO (old)); + + emit_insn (gen_move_insn (loc, reloadreg)); + emit_insn (gen_move_insn (old, loc)); + } + else +#endif + emit_insn (gen_move_insn (old, reloadreg)); + } + +#ifdef PRESERVE_DEATH_INFO_REGNO_P + /* If final will look at death notes for this reg, + put one on the last output-reload insn to use it. Similarly + for any secondary register. */ + if (PRESERVE_DEATH_INFO_REGNO_P (REGNO (reloadreg))) + for (p = get_last_insn (); p; p = PREV_INSN (p)) + if (GET_RTX_CLASS (GET_CODE (p)) == 'i' + && reg_overlap_mentioned_for_reload_p (reloadreg, + PATTERN (p))) + REG_NOTES (p) = gen_rtx (EXPR_LIST, REG_DEAD, + reloadreg, REG_NOTES (p)); + +#ifdef SECONDARY_OUTPUT_RELOAD_CLASS + if (! special + && PRESERVE_DEATH_INFO_REGNO_P (REGNO (second_reloadreg))) + for (p = get_last_insn (); p; p = PREV_INSN (p)) + if (GET_RTX_CLASS (GET_CODE (p)) == 'i' + && reg_overlap_mentioned_for_reload_p (second_reloadreg, + PATTERN (p))) + REG_NOTES (p) = gen_rtx (EXPR_LIST, REG_DEAD, + second_reloadreg, REG_NOTES (p)); +#endif +#endif + /* Look at all insns we emitted, just to be safe. */ + for (p = get_insns (); p; p = NEXT_INSN (p)) + if (GET_RTX_CLASS (GET_CODE (p)) == 'i') + { + /* If this output reload doesn't come from a spill reg, + clear any memory of reloaded copies of the pseudo reg. + If this output reload comes from a spill reg, + reg_has_output_reload will make this do nothing. */ + note_stores (PATTERN (p), forget_old_reloads_1); + + if (reg_mentioned_p (reload_reg_rtx[j], PATTERN (p))) + store_insn = p; + } + + output_reload_insns[reload_opnum[j]] = get_insns (); + end_sequence (); + + } + + if (reload_spill_index[j] >= 0) + new_spill_reg_store[reload_spill_index[j]] = store_insn; + } + + /* Now write all the insns we made for reloads in the order expected by + the allocation functions. Prior to the insn being reloaded, we write + the following reloads: + + RELOAD_FOR_OTHER_ADDRESS reloads for input addresses. + + RELOAD_OTHER reloads. + + For each operand, any RELOAD_FOR_INPUT_ADDRESS reloads followed by + the RELOAD_FOR_INPUT reload for the operand. + + RELOAD_FOR_OPERAND_ADDRESS reloads. + + After the insn being reloaded, we write the following: + + For each operand, any RELOAD_FOR_OUTPUT_ADDRESS reload followed by + the RELOAD_FOR_OUTPUT reload for that operand. */ + + emit_insns_before (other_input_address_reload_insns, before_insn); + emit_insns_before (other_input_reload_insns, before_insn); + + for (j = 0; j < reload_n_operands; j++) + { + emit_insns_before (input_address_reload_insns[j], before_insn); + emit_insns_before (input_reload_insns[j], before_insn); + } + + emit_insns_before (operand_reload_insns, before_insn); + + for (j = 0; j < reload_n_operands; j++) + { + emit_insns_before (output_address_reload_insns[j], following_insn); + emit_insns_before (output_reload_insns[j], following_insn); + } + + /* Move death notes from INSN + to output-operand-address and output reload insns. */ +#ifdef PRESERVE_DEATH_INFO_REGNO_P + { + rtx insn1; + /* Loop over those insns, last ones first. */ + for (insn1 = PREV_INSN (following_insn); insn1 != insn; + insn1 = PREV_INSN (insn1)) + if (GET_CODE (insn1) == INSN && GET_CODE (PATTERN (insn1)) == SET) + { + rtx source = SET_SRC (PATTERN (insn1)); + rtx dest = SET_DEST (PATTERN (insn1)); + + /* The note we will examine next. */ + rtx reg_notes = REG_NOTES (insn); + /* The place that pointed to this note. */ + rtx *prev_reg_note = ®_NOTES (insn); + + /* If the note is for something used in the source of this + reload insn, or in the output address, move the note. */ + while (reg_notes) + { + rtx next_reg_notes = XEXP (reg_notes, 1); + if (REG_NOTE_KIND (reg_notes) == REG_DEAD + && GET_CODE (XEXP (reg_notes, 0)) == REG + && ((GET_CODE (dest) != REG + && reg_overlap_mentioned_for_reload_p (XEXP (reg_notes, 0), + dest)) + || reg_overlap_mentioned_for_reload_p (XEXP (reg_notes, 0), + source))) + { + *prev_reg_note = next_reg_notes; + XEXP (reg_notes, 1) = REG_NOTES (insn1); + REG_NOTES (insn1) = reg_notes; + } + else + prev_reg_note = &XEXP (reg_notes, 1); + + reg_notes = next_reg_notes; + } + } + } +#endif + + /* For all the spill regs newly reloaded in this instruction, + record what they were reloaded from, so subsequent instructions + can inherit the reloads. + + Update spill_reg_store for the reloads of this insn. + Copy the elements that were updated in the loop above. */ + + for (j = 0; j < n_reloads; j++) + { + register int r = reload_order[j]; + register int i = reload_spill_index[r]; + + /* I is nonneg if this reload used one of the spill regs. + If reload_reg_rtx[r] is 0, this is an optional reload + that we opted to ignore. + + Also ignore reloads that don't reach the end of the insn, + since we will eventually see the one that does. */ + + if (i >= 0 && reload_reg_rtx[r] != 0 + && reload_reg_reaches_end_p (spill_regs[i], reload_opnum[r], + reload_when_needed[r])) + { + /* First, clear out memory of what used to be in this spill reg. + If consecutive registers are used, clear them all. */ + int nr + = HARD_REGNO_NREGS (spill_regs[i], GET_MODE (reload_reg_rtx[r])); + int k; + + for (k = 0; k < nr; k++) + { + reg_reloaded_contents[spill_reg_order[spill_regs[i] + k]] = -1; + reg_reloaded_insn[spill_reg_order[spill_regs[i] + k]] = 0; + } + + /* Maybe the spill reg contains a copy of reload_out. */ + if (reload_out[r] != 0 && GET_CODE (reload_out[r]) == REG) + { + register int nregno = REGNO (reload_out[r]); + int nnr = (nregno >= FIRST_PSEUDO_REGISTER ? 1 + : HARD_REGNO_NREGS (nregno, + GET_MODE (reload_reg_rtx[r]))); + + spill_reg_store[i] = new_spill_reg_store[i]; + reg_last_reload_reg[nregno] = reload_reg_rtx[r]; + + /* If NREGNO is a hard register, it may occupy more than + one register. If it does, say what is in the + rest of the registers assuming that both registers + agree on how many words the object takes. If not, + invalidate the subsequent registers. */ + + if (nregno < FIRST_PSEUDO_REGISTER) + for (k = 1; k < nnr; k++) + reg_last_reload_reg[nregno + k] + = (nr == nnr ? gen_rtx (REG, word_mode, + REGNO (reload_reg_rtx[r]) + k) + : 0); + + /* Now do the inverse operation. */ + for (k = 0; k < nr; k++) + { + reg_reloaded_contents[spill_reg_order[spill_regs[i] + k]] + = (nregno >= FIRST_PSEUDO_REGISTER || nr != nnr ? nregno + : nregno + k); + reg_reloaded_insn[spill_reg_order[spill_regs[i] + k]] = insn; + } + } + + /* Maybe the spill reg contains a copy of reload_in. Only do + something if there will not be an output reload for + the register being reloaded. */ + else if (reload_out[r] == 0 + && reload_in[r] != 0 + && ((GET_CODE (reload_in[r]) == REG + && ! reg_has_output_reload[REGNO (reload_in[r])] + || (GET_CODE (reload_in_reg[r]) == REG + && ! reg_has_output_reload[REGNO (reload_in_reg[r])])))) + { + register int nregno; + int nnr; + + if (GET_CODE (reload_in[r]) == REG) + nregno = REGNO (reload_in[r]); + else + nregno = REGNO (reload_in_reg[r]); + + nnr = (nregno >= FIRST_PSEUDO_REGISTER ? 1 + : HARD_REGNO_NREGS (nregno, + GET_MODE (reload_reg_rtx[r]))); + + reg_last_reload_reg[nregno] = reload_reg_rtx[r]; + + if (nregno < FIRST_PSEUDO_REGISTER) + for (k = 1; k < nnr; k++) + reg_last_reload_reg[nregno + k] + = (nr == nnr ? gen_rtx (REG, word_mode, + REGNO (reload_reg_rtx[r]) + k) + : 0); + + /* Unless we inherited this reload, show we haven't + recently done a store. */ + if (! reload_inherited[r]) + spill_reg_store[i] = 0; + + for (k = 0; k < nr; k++) + { + reg_reloaded_contents[spill_reg_order[spill_regs[i] + k]] + = (nregno >= FIRST_PSEUDO_REGISTER || nr != nnr ? nregno + : nregno + k); + reg_reloaded_insn[spill_reg_order[spill_regs[i] + k]] + = insn; + } + } + } + + /* The following if-statement was #if 0'd in 1.34 (or before...). + It's reenabled in 1.35 because supposedly nothing else + deals with this problem. */ + + /* If a register gets output-reloaded from a non-spill register, + that invalidates any previous reloaded copy of it. + But forget_old_reloads_1 won't get to see it, because + it thinks only about the original insn. So invalidate it here. */ + if (i < 0 && reload_out[r] != 0 && GET_CODE (reload_out[r]) == REG) + { + register int nregno = REGNO (reload_out[r]); + reg_last_reload_reg[nregno] = 0; + } + } +} + +/* Emit code to perform an input reload of IN to RELOADREG. IN is from + operand OPNUM with reload type TYPE. + + Returns first insn emitted. */ + +rtx +gen_input_reload (reloadreg, in, opnum, type) + rtx reloadreg; + rtx in; + int opnum; + enum reload_type type; +{ + rtx last = get_last_insn (); + + /* How to do this reload can get quite tricky. Normally, we are being + asked to reload a simple operand, such as a MEM, a constant, or a pseudo + register that didn't get a hard register. In that case we can just + call emit_move_insn. + + We can also be asked to reload a PLUS that adds either two registers, or + a register and a constant or MEM, or a MEM and a constant. This can + occur during frame pointer elimination and while reloading addresses. + This case is handled by trying to emit a single insn + to perform the add. If it is not valid, we use a two insn sequence. + + Finally, we could be called to handle an 'o' constraint by putting + an address into a register. In that case, we first try to do this + with a named pattern of "reload_load_address". If no such pattern + exists, we just emit a SET insn and hope for the best (it will normally + be valid on machines that use 'o'). + + This entire process is made complex because reload will never + process the insns we generate here and so we must ensure that + they will fit their constraints and also by the fact that parts of + IN might be being reloaded separately and replaced with spill registers. + Because of this, we are, in some sense, just guessing the right approach + here. The one listed above seems to work. + + ??? At some point, this whole thing needs to be rethought. */ + + if (GET_CODE (in) == PLUS + && ((GET_CODE (XEXP (in, 0)) == REG + && (GET_CODE (XEXP (in, 1)) == REG + || CONSTANT_P (XEXP (in, 1)) + || GET_CODE (XEXP (in, 1)) == MEM)) + || (GET_CODE (XEXP (in, 0)) == MEM + && CONSTANT_P (XEXP (in, 1))))) + { + /* We need to compute the sum of what is either a register and a + constant, a register and memory, a hard register and a pseudo + register, or memory and a constant and put it into the reload + register. The best possible way of doing this is if the machine + has a three-operand ADD insn that accepts the required operands. + + The simplest approach is to try to generate such an insn and see if it + is recognized and matches its constraints. If so, it can be used. + + It might be better not to actually emit the insn unless it is valid, + but we need to pass the insn as an operand to `recog' and + `insn_extract' and it is simpler to emit and then delete the insn if + not valid than to dummy things up. */ + + rtx op0, op1, tem, insn; + int code; + + op0 = find_replacement (&XEXP (in, 0)); + op1 = find_replacement (&XEXP (in, 1)); + + /* Since constraint checking is strict, commutativity won't be + checked, so we need to do that here to avoid spurious failure + if the add instruction is two-address and the second operand + of the add is the same as the reload reg, which is frequently + the case. If the insn would be A = B + A, rearrange it so + it will be A = A + B as constrain_operands expects. */ + + if (GET_CODE (XEXP (in, 1)) == REG + && REGNO (reloadreg) == REGNO (XEXP (in, 1))) + tem = op0, op0 = op1, op1 = tem; + + if (op0 != XEXP (in, 0) || op1 != XEXP (in, 1)) + in = gen_rtx (PLUS, GET_MODE (in), op0, op1); + + insn = emit_insn (gen_rtx (SET, VOIDmode, reloadreg, in)); + code = recog_memoized (insn); + + if (code >= 0) + { + insn_extract (insn); + /* We want constrain operands to treat this insn strictly in + its validity determination, i.e., the way it would after reload + has completed. */ + if (constrain_operands (code, 1)) + return insn; + } + + delete_insns_since (last); + + /* If that failed, we must use a conservative two-insn sequence. + use move to copy constant, MEM, or pseudo register to the reload + register since "move" will be able to handle an arbitrary operand, + unlike add which can't, in general. Then add the registers. + + If there is another way to do this for a specific machine, a + DEFINE_PEEPHOLE should be specified that recognizes the sequence + we emit below. */ + + if (CONSTANT_P (op1) || GET_CODE (op1) == MEM + || (GET_CODE (op1) == REG + && REGNO (op1) >= FIRST_PSEUDO_REGISTER)) + tem = op0, op0 = op1, op1 = tem; + + emit_insn (gen_move_insn (reloadreg, op0)); + + /* If OP0 and OP1 are the same, we can use RELOADREG for OP1. + This fixes a problem on the 32K where the stack pointer cannot + be used as an operand of an add insn. */ + + if (rtx_equal_p (op0, op1)) + op1 = reloadreg; + + emit_insn (gen_add2_insn (reloadreg, op1)); + } + +#ifdef SECONDARY_MEMORY_NEEDED + /* If we need a memory location to do the move, do it that way. */ + else if (GET_CODE (in) == REG && REGNO (in) < FIRST_PSEUDO_REGISTER + && SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (REGNO (in)), + REGNO_REG_CLASS (REGNO (reloadreg)), + GET_MODE (reloadreg))) + { + /* Get the memory to use and rewrite both registers to its mode. */ + rtx loc = get_secondary_mem (in, GET_MODE (reloadreg), opnum, type); + + if (GET_MODE (loc) != GET_MODE (reloadreg)) + reloadreg = gen_rtx (REG, GET_MODE (loc), REGNO (reloadreg)); + + if (GET_MODE (loc) != GET_MODE (in)) + in = gen_rtx (REG, GET_MODE (loc), REGNO (in)); + + emit_insn (gen_move_insn (loc, in)); + emit_insn (gen_move_insn (reloadreg, loc)); + } +#endif + + /* If IN is a simple operand, use gen_move_insn. */ + else if (GET_RTX_CLASS (GET_CODE (in)) == 'o' || GET_CODE (in) == SUBREG) + emit_insn (gen_move_insn (reloadreg, in)); + +#ifdef HAVE_reload_load_address + else if (HAVE_reload_load_address) + emit_insn (gen_reload_load_address (reloadreg, in)); +#endif + + /* Otherwise, just write (set REGLOADREG IN) and hope for the best. */ + else + emit_insn (gen_rtx (SET, VOIDmode, reloadreg, in)); + + /* Return the first insn emitted. + We can not just return get_last_insn, because there may have + been multiple instructions emitted. Also note that gen_move_insn may + emit more than one insn itself, so we can not assume that there is one + insn emitted per emit_insn_before call. */ + + return last ? NEXT_INSN (last) : get_insns (); +} + +/* Delete a previously made output-reload + whose result we now believe is not needed. + First we double-check. + + INSN is the insn now being processed. + OUTPUT_RELOAD_INSN is the insn of the output reload. + J is the reload-number for this insn. */ + +static void +delete_output_reload (insn, j, output_reload_insn) + rtx insn; + int j; + rtx output_reload_insn; +{ + register rtx i1; + + /* Get the raw pseudo-register referred to. */ + + rtx reg = reload_in[j]; + while (GET_CODE (reg) == SUBREG) + reg = SUBREG_REG (reg); + + /* If the pseudo-reg we are reloading is no longer referenced + anywhere between the store into it and here, + and no jumps or labels intervene, then the value can get + here through the reload reg alone. + Otherwise, give up--return. */ + for (i1 = NEXT_INSN (output_reload_insn); + i1 != insn; i1 = NEXT_INSN (i1)) + { + if (GET_CODE (i1) == CODE_LABEL || GET_CODE (i1) == JUMP_INSN) + return; + if ((GET_CODE (i1) == INSN || GET_CODE (i1) == CALL_INSN) + && reg_mentioned_p (reg, PATTERN (i1))) + return; + } + + if (cannot_omit_stores[REGNO (reg)]) + return; + + /* If this insn will store in the pseudo again, + the previous store can be removed. */ + if (reload_out[j] == reload_in[j]) + delete_insn (output_reload_insn); + + /* See if the pseudo reg has been completely replaced + with reload regs. If so, delete the store insn + and forget we had a stack slot for the pseudo. */ + else if (reg_n_deaths[REGNO (reg)] == 1 + && reg_basic_block[REGNO (reg)] >= 0 + && find_regno_note (insn, REG_DEAD, REGNO (reg))) + { + rtx i2; + + /* We know that it was used only between here + and the beginning of the current basic block. + (We also know that the last use before INSN was + the output reload we are thinking of deleting, but never mind that.) + Search that range; see if any ref remains. */ + for (i2 = PREV_INSN (insn); i2; i2 = PREV_INSN (i2)) + { + rtx set = single_set (i2); + + /* Uses which just store in the pseudo don't count, + since if they are the only uses, they are dead. */ + if (set != 0 && SET_DEST (set) == reg) + continue; + if (GET_CODE (i2) == CODE_LABEL + || GET_CODE (i2) == JUMP_INSN) + break; + if ((GET_CODE (i2) == INSN || GET_CODE (i2) == CALL_INSN) + && reg_mentioned_p (reg, PATTERN (i2))) + /* Some other ref remains; + we can't do anything. */ + return; + } + + /* Delete the now-dead stores into this pseudo. */ + for (i2 = PREV_INSN (insn); i2; i2 = PREV_INSN (i2)) + { + rtx set = single_set (i2); + + if (set != 0 && SET_DEST (set) == reg) + delete_insn (i2); + if (GET_CODE (i2) == CODE_LABEL + || GET_CODE (i2) == JUMP_INSN) + break; + } + + /* For the debugging info, + say the pseudo lives in this reload reg. */ + reg_renumber[REGNO (reg)] = REGNO (reload_reg_rtx[j]); + alter_reg (REGNO (reg), -1); + } +} + +/* Output reload-insns to reload VALUE into RELOADREG. + VALUE is an autoincrement or autodecrement RTX whose operand + is a register or memory location; + so reloading involves incrementing that location. + + INC_AMOUNT is the number to increment or decrement by (always positive). + This cannot be deduced from VALUE. */ + +static void +inc_for_reload (reloadreg, value, inc_amount) + rtx reloadreg; + rtx value; + int inc_amount; +{ + /* REG or MEM to be copied and incremented. */ + rtx incloc = XEXP (value, 0); + /* Nonzero if increment after copying. */ + int post = (GET_CODE (value) == POST_DEC || GET_CODE (value) == POST_INC); + rtx last; + rtx inc; + rtx add_insn; + int code; + + /* No hard register is equivalent to this register after + inc/dec operation. If REG_LAST_RELOAD_REG were non-zero, + we could inc/dec that register as well (maybe even using it for + the source), but I'm not sure it's worth worrying about. */ + if (GET_CODE (incloc) == REG) + reg_last_reload_reg[REGNO (incloc)] = 0; + + if (GET_CODE (value) == PRE_DEC || GET_CODE (value) == POST_DEC) + inc_amount = - inc_amount; + + inc = GEN_INT (inc_amount); + + /* If this is post-increment, first copy the location to the reload reg. */ + if (post) + emit_insn (gen_move_insn (reloadreg, incloc)); + + /* See if we can directly increment INCLOC. Use a method similar to that + in gen_input_reload. */ + + last = get_last_insn (); + add_insn = emit_insn (gen_rtx (SET, VOIDmode, incloc, + gen_rtx (PLUS, GET_MODE (incloc), + incloc, inc))); + + code = recog_memoized (add_insn); + if (code >= 0) + { + insn_extract (add_insn); + if (constrain_operands (code, 1)) + { + /* If this is a pre-increment and we have incremented the value + where it lives, copy the incremented value to RELOADREG to + be used as an address. */ + + if (! post) + emit_insn (gen_move_insn (reloadreg, incloc)); + + return; + } + } + + delete_insns_since (last); + + /* If couldn't do the increment directly, must increment in RELOADREG. + The way we do this depends on whether this is pre- or post-increment. + For pre-increment, copy INCLOC to the reload register, increment it + there, then save back. */ + + if (! post) + { + emit_insn (gen_move_insn (reloadreg, incloc)); + emit_insn (gen_add2_insn (reloadreg, inc)); + emit_insn (gen_move_insn (incloc, reloadreg)); + } + else + { + /* Postincrement. + Because this might be a jump insn or a compare, and because RELOADREG + may not be available after the insn in an input reload, we must do + the incrementation before the insn being reloaded for. + + We have already copied INCLOC to RELOADREG. Increment the copy in + RELOADREG, save that back, then decrement RELOADREG so it has + the original value. */ + + emit_insn (gen_add2_insn (reloadreg, inc)); + emit_insn (gen_move_insn (incloc, reloadreg)); + emit_insn (gen_add2_insn (reloadreg, GEN_INT (-inc_amount))); + } + + return; +} + +/* Return 1 if we are certain that the constraint-string STRING allows + the hard register REG. Return 0 if we can't be sure of this. */ + +static int +constraint_accepts_reg_p (string, reg) + char *string; + rtx reg; +{ + int value = 0; + int regno = true_regnum (reg); + int c; + + /* Initialize for first alternative. */ + value = 0; + /* Check that each alternative contains `g' or `r'. */ + while (1) + switch (c = *string++) + { + case 0: + /* If an alternative lacks `g' or `r', we lose. */ + return value; + case ',': + /* If an alternative lacks `g' or `r', we lose. */ + if (value == 0) + return 0; + /* Initialize for next alternative. */ + value = 0; + break; + case 'g': + case 'r': + /* Any general reg wins for this alternative. */ + if (TEST_HARD_REG_BIT (reg_class_contents[(int) GENERAL_REGS], regno)) + value = 1; + break; + default: + /* Any reg in specified class wins for this alternative. */ + { + enum reg_class class = REG_CLASS_FROM_LETTER (c); + + if (TEST_HARD_REG_BIT (reg_class_contents[(int) class], regno)) + value = 1; + } + } +} + +/* Return the number of places FIND appears within X, but don't count + an occurrence if some SET_DEST is FIND. */ + +static int +count_occurrences (x, find) + register rtx x, find; +{ + register int i, j; + register enum rtx_code code; + register char *format_ptr; + int count; + + if (x == find) + return 1; + if (x == 0) + return 0; + + code = GET_CODE (x); + + switch (code) + { + case REG: + case QUEUED: + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + case CODE_LABEL: + case PC: + case CC0: + return 0; + + case SET: + if (SET_DEST (x) == find) + return count_occurrences (SET_SRC (x), find); + break; + } + + format_ptr = GET_RTX_FORMAT (code); + count = 0; + + for (i = 0; i < GET_RTX_LENGTH (code); i++) + { + switch (*format_ptr++) + { + case 'e': + count += count_occurrences (XEXP (x, i), find); + break; + + case 'E': + if (XVEC (x, i) != NULL) + { + for (j = 0; j < XVECLEN (x, i); j++) + count += count_occurrences (XVECEXP (x, i, j), find); + } + break; + } + } + return count; +} diff --git a/gnu/usr.bin/cc/common/reorg.c b/gnu/usr.bin/cc/lib/reorg.c similarity index 100% rename from gnu/usr.bin/cc/common/reorg.c rename to gnu/usr.bin/cc/lib/reorg.c diff --git a/gnu/usr.bin/cc/common/rtl.c b/gnu/usr.bin/cc/lib/rtl.c similarity index 100% rename from gnu/usr.bin/cc/common/rtl.c rename to gnu/usr.bin/cc/lib/rtl.c diff --git a/gnu/usr.bin/cc/common/rtl.def b/gnu/usr.bin/cc/lib/rtl.def similarity index 100% rename from gnu/usr.bin/cc/common/rtl.def rename to gnu/usr.bin/cc/lib/rtl.def diff --git a/gnu/usr.bin/cc/common/rtl.h b/gnu/usr.bin/cc/lib/rtl.h similarity index 100% rename from gnu/usr.bin/cc/common/rtl.h rename to gnu/usr.bin/cc/lib/rtl.h diff --git a/gnu/usr.bin/cc/common/rtlanal.c b/gnu/usr.bin/cc/lib/rtlanal.c similarity index 100% rename from gnu/usr.bin/cc/common/rtlanal.c rename to gnu/usr.bin/cc/lib/rtlanal.c diff --git a/gnu/usr.bin/cc/lib/sched.c b/gnu/usr.bin/cc/lib/sched.c new file mode 100644 index 0000000000..2fec9bcf9b --- /dev/null +++ b/gnu/usr.bin/cc/lib/sched.c @@ -0,0 +1,4675 @@ +/* Instruction scheduling pass. + Copyright (C) 1992 Free Software Foundation, Inc. + Contributed by Michael Tiemann (tiemann@cygnus.com) + Enhanced by, and currently maintained by, Jim Wilson (wilson@cygnus.com) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Instruction scheduling pass. + + This pass implements list scheduling within basic blocks. It is + run after flow analysis, but before register allocation. The + scheduler works as follows: + + We compute insn priorities based on data dependencies. Flow + analysis only creates a fraction of the data-dependencies we must + observe: namely, only those dependencies which the combiner can be + expected to use. For this pass, we must therefore create the + remaining dependencies we need to observe: register dependencies, + memory dependencies, dependencies to keep function calls in order, + and the dependence between a conditional branch and the setting of + condition codes are all dealt with here. + + The scheduler first traverses the data flow graph, starting with + the last instruction, and proceeding to the first, assigning + values to insn_priority as it goes. This sorts the instructions + topologically by data dependence. + + Once priorities have been established, we order the insns using + list scheduling. This works as follows: starting with a list of + all the ready insns, and sorted according to priority number, we + schedule the insn from the end of the list by placing its + predecessors in the list according to their priority order. We + consider this insn scheduled by setting the pointer to the "end" of + the list to point to the previous insn. When an insn has no + predecessors, we either queue it until sufficient time has elapsed + or add it to the ready list. As the instructions are scheduled or + when stalls are introduced, the queue advances and dumps insns into + the ready list. When all insns down to the lowest priority have + been scheduled, the critical path of the basic block has been made + as short as possible. The remaining insns are then scheduled in + remaining slots. + + Function unit conflicts are resolved during reverse list scheduling + by tracking the time when each insn is committed to the schedule + and from that, the time the function units it uses must be free. + As insns on the ready list are considered for scheduling, those + that would result in a blockage of the already committed insns are + queued until no blockage will result. Among the remaining insns on + the ready list to be considered, the first one with the largest + potential for causing a subsequent blockage is chosen. + + The following list shows the order in which we want to break ties + among insns in the ready list: + + 1. choose insn with lowest conflict cost, ties broken by + 2. choose insn with the longest path to end of bb, ties broken by + 3. choose insn that kills the most registers, ties broken by + 4. choose insn that conflicts with the most ready insns, or finally + 5. choose insn with lowest UID. + + Memory references complicate matters. Only if we can be certain + that memory references are not part of the data dependency graph + (via true, anti, or output dependence), can we move operations past + memory references. To first approximation, reads can be done + independently, while writes introduce dependencies. Better + approximations will yield fewer dependencies. + + Dependencies set up by memory references are treated in exactly the + same way as other dependencies, by using LOG_LINKS. + + Having optimized the critical path, we may have also unduly + extended the lifetimes of some registers. If an operation requires + that constants be loaded into registers, it is certainly desirable + to load those constants as early as necessary, but no earlier. + I.e., it will not do to load up a bunch of registers at the + beginning of a basic block only to use them at the end, if they + could be loaded later, since this may result in excessive register + utilization. + + Note that since branches are never in basic blocks, but only end + basic blocks, this pass will not do any branch scheduling. But + that is ok, since we can use GNU's delayed branch scheduling + pass to take care of this case. + + Also note that no further optimizations based on algebraic identities + are performed, so this pass would be a good one to perform instruction + splitting, such as breaking up a multiply instruction into shifts + and adds where that is profitable. + + Given the memory aliasing analysis that this pass should perform, + it should be possible to remove redundant stores to memory, and to + load values from registers instead of hitting memory. + + This pass must update information that subsequent passes expect to be + correct. Namely: reg_n_refs, reg_n_sets, reg_n_deaths, + reg_n_calls_crossed, and reg_live_length. Also, basic_block_head, + basic_block_end. + + The information in the line number notes is carefully retained by this + pass. All other NOTE insns are grouped in their same relative order at + the beginning of basic blocks that have been scheduled. */ + +#include +#include "config.h" +#include "rtl.h" +#include "basic-block.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "flags.h" +#include "insn-config.h" +#include "insn-attr.h" + +#ifdef INSN_SCHEDULING +/* Arrays set up by scheduling for the same respective purposes as + similar-named arrays set up by flow analysis. We work with these + arrays during the scheduling pass so we can compare values against + unscheduled code. + + Values of these arrays are copied at the end of this pass into the + arrays set up by flow analysis. */ +static short *sched_reg_n_deaths; +static int *sched_reg_n_calls_crossed; +static int *sched_reg_live_length; + +/* Element N is the next insn that sets (hard or pseudo) register + N within the current basic block; or zero, if there is no + such insn. Needed for new registers which may be introduced + by splitting insns. */ +static rtx *reg_last_uses; +static rtx *reg_last_sets; + +/* Vector indexed by INSN_UID giving the original ordering of the insns. */ +static int *insn_luid; +#define INSN_LUID(INSN) (insn_luid[INSN_UID (INSN)]) + +/* Vector indexed by INSN_UID giving each instruction a priority. */ +static int *insn_priority; +#define INSN_PRIORITY(INSN) (insn_priority[INSN_UID (INSN)]) + +static short *insn_costs; +#define INSN_COST(INSN) insn_costs[INSN_UID (INSN)] + +/* Vector indexed by INSN_UID giving an encoding of the function units + used. */ +static short *insn_units; +#define INSN_UNIT(INSN) insn_units[INSN_UID (INSN)] + +/* Vector indexed by INSN_UID giving an encoding of the blockage range + function. The unit and the range are encoded. */ +static unsigned int *insn_blockage; +#define INSN_BLOCKAGE(INSN) insn_blockage[INSN_UID (INSN)] +#define UNIT_BITS 5 +#define BLOCKAGE_MASK ((1 << BLOCKAGE_BITS) - 1) +#define ENCODE_BLOCKAGE(U,R) \ + ((((U) << UNIT_BITS) << BLOCKAGE_BITS \ + | MIN_BLOCKAGE_COST (R)) << BLOCKAGE_BITS \ + | MAX_BLOCKAGE_COST (R)) +#define UNIT_BLOCKED(B) ((B) >> (2 * BLOCKAGE_BITS)) +#define BLOCKAGE_RANGE(B) \ + (((((B) >> BLOCKAGE_BITS) & BLOCKAGE_MASK) << (HOST_BITS_PER_INT / 2)) \ + | (B) & BLOCKAGE_MASK) + +/* Encodings of the `_unit_blockage_range' function. */ +#define MIN_BLOCKAGE_COST(R) ((R) >> (HOST_BITS_PER_INT / 2)) +#define MAX_BLOCKAGE_COST(R) ((R) & ((1 << (HOST_BITS_PER_INT / 2)) - 1)) + +#define DONE_PRIORITY -1 +#define MAX_PRIORITY 0x7fffffff +#define TAIL_PRIORITY 0x7ffffffe +#define LAUNCH_PRIORITY 0x7f000001 +#define DONE_PRIORITY_P(INSN) (INSN_PRIORITY (INSN) < 0) +#define LOW_PRIORITY_P(INSN) ((INSN_PRIORITY (INSN) & 0x7f000000) == 0) + +/* Vector indexed by INSN_UID giving number of insns referring to this insn. */ +static int *insn_ref_count; +#define INSN_REF_COUNT(INSN) (insn_ref_count[INSN_UID (INSN)]) + +/* Vector indexed by INSN_UID giving line-number note in effect for each + insn. For line-number notes, this indicates whether the note may be + reused. */ +static rtx *line_note; +#define LINE_NOTE(INSN) (line_note[INSN_UID (INSN)]) + +/* Vector indexed by basic block number giving the starting line-number + for each basic block. */ +static rtx *line_note_head; + +/* List of important notes we must keep around. This is a pointer to the + last element in the list. */ +static rtx note_list; + +/* Regsets telling whether a given register is live or dead before the last + scheduled insn. Must scan the instructions once before scheduling to + determine what registers are live or dead at the end of the block. */ +static regset bb_dead_regs; +static regset bb_live_regs; + +/* Regset telling whether a given register is live after the insn currently + being scheduled. Before processing an insn, this is equal to bb_live_regs + above. This is used so that we can find registers that are newly born/dead + after processing an insn. */ +static regset old_live_regs; + +/* The chain of REG_DEAD notes. REG_DEAD notes are removed from all insns + during the initial scan and reused later. If there are not exactly as + many REG_DEAD notes in the post scheduled code as there were in the + prescheduled code then we trigger an abort because this indicates a bug. */ +static rtx dead_notes; + +/* Queues, etc. */ + +/* An instruction is ready to be scheduled when all insns following it + have already been scheduled. It is important to ensure that all + insns which use its result will not be executed until its result + has been computed. An insn is maintained in one of four structures: + + (P) the "Pending" set of insns which cannot be scheduled until + their dependencies have been satisfied. + (Q) the "Queued" set of insns that can be scheduled when sufficient + time has passed. + (R) the "Ready" list of unscheduled, uncommitted insns. + (S) the "Scheduled" list of insns. + + Initially, all insns are either "Pending" or "Ready" depending on + whether their dependencies are satisfied. + + Insns move from the "Ready" list to the "Scheduled" list as they + are committed to the schedule. As this occurs, the insns in the + "Pending" list have their dependencies satisfied and move to either + the "Ready" list or the "Queued" set depending on whether + sufficient time has passed to make them ready. As time passes, + insns move from the "Queued" set to the "Ready" list. Insns may + move from the "Ready" list to the "Queued" set if they are blocked + due to a function unit conflict. + + The "Pending" list (P) are the insns in the LOG_LINKS of the unscheduled + insns, i.e., those that are ready, queued, and pending. + The "Queued" set (Q) is implemented by the variable `insn_queue'. + The "Ready" list (R) is implemented by the variables `ready' and + `n_ready'. + The "Scheduled" list (S) is the new insn chain built by this pass. + + The transition (R->S) is implemented in the scheduling loop in + `schedule_block' when the best insn to schedule is chosen. + The transition (R->Q) is implemented in `schedule_select' when an + insn is found to to have a function unit conflict with the already + committed insns. + The transitions (P->R and P->Q) are implemented in `schedule_insn' as + insns move from the ready list to the scheduled list. + The transition (Q->R) is implemented at the top of the scheduling + loop in `schedule_block' as time passes or stalls are introduced. */ + +/* Implement a circular buffer to delay instructions until sufficient + time has passed. INSN_QUEUE_SIZE is a power of two larger than + MAX_BLOCKAGE and MAX_READY_COST computed by genattr.c. This is the + longest time an isnsn may be queued. */ +static rtx insn_queue[INSN_QUEUE_SIZE]; +static int q_ptr = 0; +static int q_size = 0; +#define NEXT_Q(X) (((X)+1) & (INSN_QUEUE_SIZE-1)) +#define NEXT_Q_AFTER(X,C) (((X)+C) & (INSN_QUEUE_SIZE-1)) + +/* Vector indexed by INSN_UID giving the minimum clock tick at which + the insn becomes ready. This is used to note timing constraints for + insns in the pending list. */ +static int *insn_tick; +#define INSN_TICK(INSN) (insn_tick[INSN_UID (INSN)]) + +/* Forward declarations. */ +static void sched_analyze_2 (); +static void schedule_block (); + +/* Main entry point of this file. */ +void schedule_insns (); +#endif /* INSN_SCHEDULING */ + +#define SIZE_FOR_MODE(X) (GET_MODE_SIZE (GET_MODE (X))) + +/* Vector indexed by N giving the initial (unchanging) value known + for pseudo-register N. */ +static rtx *reg_known_value; + +/* Vector recording for each reg_known_value whether it is due to a + REG_EQUIV note. Future passes (viz., reload) may replace the + pseudo with the equivalent expression and so we account for the + dependences that would be introduced if that happens. */ +/* ??? This is a problem only on the Convex. The REG_EQUIV notes created in + assign_parms mention the arg pointer, and there are explicit insns in the + RTL that modify the arg pointer. Thus we must ensure that such insns don't + get scheduled across each other because that would invalidate the REG_EQUIV + notes. One could argue that the REG_EQUIV notes are wrong, but solving + the problem in the scheduler will likely give better code, so we do it + here. */ +static char *reg_known_equiv_p; + +/* Indicates number of valid entries in reg_known_value. */ +static int reg_known_value_size; + +static rtx +canon_rtx (x) + rtx x; +{ + if (GET_CODE (x) == REG && REGNO (x) >= FIRST_PSEUDO_REGISTER + && REGNO (x) <= reg_known_value_size) + return reg_known_value[REGNO (x)]; + else if (GET_CODE (x) == PLUS) + { + rtx x0 = canon_rtx (XEXP (x, 0)); + rtx x1 = canon_rtx (XEXP (x, 1)); + + if (x0 != XEXP (x, 0) || x1 != XEXP (x, 1)) + { + /* We can tolerate LO_SUMs being offset here; these + rtl are used for nothing other than comparisons. */ + if (GET_CODE (x0) == CONST_INT) + return plus_constant_for_output (x1, INTVAL (x0)); + else if (GET_CODE (x1) == CONST_INT) + return plus_constant_for_output (x0, INTVAL (x1)); + return gen_rtx (PLUS, GET_MODE (x), x0, x1); + } + } + return x; +} + +/* Set up all info needed to perform alias analysis on memory references. */ + +void +init_alias_analysis () +{ + int maxreg = max_reg_num (); + rtx insn; + rtx note; + rtx set; + + reg_known_value_size = maxreg; + + reg_known_value + = (rtx *) oballoc ((maxreg-FIRST_PSEUDO_REGISTER) * sizeof (rtx)) + - FIRST_PSEUDO_REGISTER; + bzero (reg_known_value+FIRST_PSEUDO_REGISTER, + (maxreg-FIRST_PSEUDO_REGISTER) * sizeof (rtx)); + + reg_known_equiv_p + = (char *) oballoc ((maxreg-FIRST_PSEUDO_REGISTER) * sizeof (char)) + - FIRST_PSEUDO_REGISTER; + bzero (reg_known_equiv_p+FIRST_PSEUDO_REGISTER, + (maxreg-FIRST_PSEUDO_REGISTER) * sizeof (char)); + + /* Fill in the entries with known constant values. */ + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if ((set = single_set (insn)) != 0 + && GET_CODE (SET_DEST (set)) == REG + && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER + && (((note = find_reg_note (insn, REG_EQUAL, 0)) != 0 + && reg_n_sets[REGNO (SET_DEST (set))] == 1) + || (note = find_reg_note (insn, REG_EQUIV, NULL_RTX)) != 0) + && GET_CODE (XEXP (note, 0)) != EXPR_LIST) + { + int regno = REGNO (SET_DEST (set)); + reg_known_value[regno] = XEXP (note, 0); + reg_known_equiv_p[regno] = REG_NOTE_KIND (note) == REG_EQUIV; + } + + /* Fill in the remaining entries. */ + while (--maxreg >= FIRST_PSEUDO_REGISTER) + if (reg_known_value[maxreg] == 0) + reg_known_value[maxreg] = regno_reg_rtx[maxreg]; +} + +/* Return 1 if X and Y are identical-looking rtx's. + + We use the data in reg_known_value above to see if two registers with + different numbers are, in fact, equivalent. */ + +static int +rtx_equal_for_memref_p (x, y) + rtx x, y; +{ + register int i; + register int j; + register enum rtx_code code; + register char *fmt; + + if (x == 0 && y == 0) + return 1; + if (x == 0 || y == 0) + return 0; + x = canon_rtx (x); + y = canon_rtx (y); + + if (x == y) + return 1; + + code = GET_CODE (x); + /* Rtx's of different codes cannot be equal. */ + if (code != GET_CODE (y)) + return 0; + + /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. + (REG:SI x) and (REG:HI x) are NOT equivalent. */ + + if (GET_MODE (x) != GET_MODE (y)) + return 0; + + /* REG, LABEL_REF, and SYMBOL_REF can be compared nonrecursively. */ + + if (code == REG) + return REGNO (x) == REGNO (y); + if (code == LABEL_REF) + return XEXP (x, 0) == XEXP (y, 0); + if (code == SYMBOL_REF) + return XSTR (x, 0) == XSTR (y, 0); + + /* Compare the elements. If any pair of corresponding elements + fail to match, return 0 for the whole things. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + switch (fmt[i]) + { + case 'w': + if (XWINT (x, i) != XWINT (y, i)) + return 0; + break; + + case 'n': + case 'i': + if (XINT (x, i) != XINT (y, i)) + return 0; + break; + + case 'V': + case 'E': + /* Two vectors must have the same length. */ + if (XVECLEN (x, i) != XVECLEN (y, i)) + return 0; + + /* And the corresponding elements must match. */ + for (j = 0; j < XVECLEN (x, i); j++) + if (rtx_equal_for_memref_p (XVECEXP (x, i, j), XVECEXP (y, i, j)) == 0) + return 0; + break; + + case 'e': + if (rtx_equal_for_memref_p (XEXP (x, i), XEXP (y, i)) == 0) + return 0; + break; + + case 'S': + case 's': + if (strcmp (XSTR (x, i), XSTR (y, i))) + return 0; + break; + + case 'u': + /* These are just backpointers, so they don't matter. */ + break; + + case '0': + break; + + /* It is believed that rtx's at this level will never + contain anything but integers and other rtx's, + except for within LABEL_REFs and SYMBOL_REFs. */ + default: + abort (); + } + } + return 1; +} + +/* Given an rtx X, find a SYMBOL_REF or LABEL_REF within + X and return it, or return 0 if none found. */ + +static rtx +find_symbolic_term (x) + rtx x; +{ + register int i; + register enum rtx_code code; + register char *fmt; + + code = GET_CODE (x); + if (code == SYMBOL_REF || code == LABEL_REF) + return x; + if (GET_RTX_CLASS (code) == 'o') + return 0; + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + rtx t; + + if (fmt[i] == 'e') + { + t = find_symbolic_term (XEXP (x, i)); + if (t != 0) + return t; + } + else if (fmt[i] == 'E') + break; + } + return 0; +} + +/* Return nonzero if X and Y (memory addresses) could reference the + same location in memory. C is an offset accumulator. When + C is nonzero, we are testing aliases between X and Y + C. + XSIZE is the size in bytes of the X reference, + similarly YSIZE is the size in bytes for Y. + + If XSIZE or YSIZE is zero, we do not know the amount of memory being + referenced (the reference was BLKmode), so make the most pessimistic + assumptions. + + We recognize the following cases of non-conflicting memory: + + (1) addresses involving the frame pointer cannot conflict + with addresses involving static variables. + (2) static variables with different addresses cannot conflict. + + Nice to notice that varying addresses cannot conflict with fp if no + local variables had their addresses taken, but that's too hard now. */ + +/* ??? In Fortran, references to a array parameter can never conflict with + another array parameter. */ + +static int +memrefs_conflict_p (xsize, x, ysize, y, c) + rtx x, y; + int xsize, ysize; + HOST_WIDE_INT c; +{ + if (GET_CODE (x) == HIGH) + x = XEXP (x, 0); + else if (GET_CODE (x) == LO_SUM) + x = XEXP (x, 1); + else + x = canon_rtx (x); + if (GET_CODE (y) == HIGH) + y = XEXP (y, 0); + else if (GET_CODE (y) == LO_SUM) + y = XEXP (y, 1); + else + y = canon_rtx (y); + + if (rtx_equal_for_memref_p (x, y)) + return (xsize == 0 || ysize == 0 || + (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0)); + + if (y == frame_pointer_rtx || y == stack_pointer_rtx) + { + rtx t = y; + int tsize = ysize; + y = x; ysize = xsize; + x = t; xsize = tsize; + } + + if (x == frame_pointer_rtx || x == stack_pointer_rtx) + { + rtx y1; + + if (CONSTANT_P (y)) + return 0; + + if (GET_CODE (y) == PLUS + && canon_rtx (XEXP (y, 0)) == x + && (y1 = canon_rtx (XEXP (y, 1))) + && GET_CODE (y1) == CONST_INT) + { + c += INTVAL (y1); + return (xsize == 0 || ysize == 0 + || (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0)); + } + + if (GET_CODE (y) == PLUS + && (y1 = canon_rtx (XEXP (y, 0))) + && CONSTANT_P (y1)) + return 0; + + return 1; + } + + if (GET_CODE (x) == PLUS) + { + /* The fact that X is canonicalized means that this + PLUS rtx is canonicalized. */ + rtx x0 = XEXP (x, 0); + rtx x1 = XEXP (x, 1); + + if (GET_CODE (y) == PLUS) + { + /* The fact that Y is canonicalized means that this + PLUS rtx is canonicalized. */ + rtx y0 = XEXP (y, 0); + rtx y1 = XEXP (y, 1); + + if (rtx_equal_for_memref_p (x1, y1)) + return memrefs_conflict_p (xsize, x0, ysize, y0, c); + if (rtx_equal_for_memref_p (x0, y0)) + return memrefs_conflict_p (xsize, x1, ysize, y1, c); + if (GET_CODE (x1) == CONST_INT) + if (GET_CODE (y1) == CONST_INT) + return memrefs_conflict_p (xsize, x0, ysize, y0, + c - INTVAL (x1) + INTVAL (y1)); + else + return memrefs_conflict_p (xsize, x0, ysize, y, c - INTVAL (x1)); + else if (GET_CODE (y1) == CONST_INT) + return memrefs_conflict_p (xsize, x, ysize, y0, c + INTVAL (y1)); + + /* Handle case where we cannot understand iteration operators, + but we notice that the base addresses are distinct objects. */ + x = find_symbolic_term (x); + if (x == 0) + return 1; + y = find_symbolic_term (y); + if (y == 0) + return 1; + return rtx_equal_for_memref_p (x, y); + } + else if (GET_CODE (x1) == CONST_INT) + return memrefs_conflict_p (xsize, x0, ysize, y, c - INTVAL (x1)); + } + else if (GET_CODE (y) == PLUS) + { + /* The fact that Y is canonicalized means that this + PLUS rtx is canonicalized. */ + rtx y0 = XEXP (y, 0); + rtx y1 = XEXP (y, 1); + + if (GET_CODE (y1) == CONST_INT) + return memrefs_conflict_p (xsize, x, ysize, y0, c + INTVAL (y1)); + else + return 1; + } + + if (GET_CODE (x) == GET_CODE (y)) + switch (GET_CODE (x)) + { + case MULT: + { + /* Handle cases where we expect the second operands to be the + same, and check only whether the first operand would conflict + or not. */ + rtx x0, y0; + rtx x1 = canon_rtx (XEXP (x, 1)); + rtx y1 = canon_rtx (XEXP (y, 1)); + if (! rtx_equal_for_memref_p (x1, y1)) + return 1; + x0 = canon_rtx (XEXP (x, 0)); + y0 = canon_rtx (XEXP (y, 0)); + if (rtx_equal_for_memref_p (x0, y0)) + return (xsize == 0 || ysize == 0 + || (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0)); + + /* Can't properly adjust our sizes. */ + if (GET_CODE (x1) != CONST_INT) + return 1; + xsize /= INTVAL (x1); + ysize /= INTVAL (x1); + c /= INTVAL (x1); + return memrefs_conflict_p (xsize, x0, ysize, y0, c); + } + } + + if (CONSTANT_P (x)) + { + if (GET_CODE (x) == CONST_INT && GET_CODE (y) == CONST_INT) + { + c += (INTVAL (y) - INTVAL (x)); + return (xsize == 0 || ysize == 0 + || (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0)); + } + + if (GET_CODE (x) == CONST) + { + if (GET_CODE (y) == CONST) + return memrefs_conflict_p (xsize, canon_rtx (XEXP (x, 0)), + ysize, canon_rtx (XEXP (y, 0)), c); + else + return memrefs_conflict_p (xsize, canon_rtx (XEXP (x, 0)), + ysize, y, c); + } + if (GET_CODE (y) == CONST) + return memrefs_conflict_p (xsize, x, ysize, + canon_rtx (XEXP (y, 0)), c); + + if (CONSTANT_P (y)) + return (rtx_equal_for_memref_p (x, y) + && (xsize == 0 || ysize == 0 + || (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0))); + + return 1; + } + return 1; +} + +/* Functions to compute memory dependencies. + + Since we process the insns in execution order, we can build tables + to keep track of what registers are fixed (and not aliased), what registers + are varying in known ways, and what registers are varying in unknown + ways. + + If both memory references are volatile, then there must always be a + dependence between the two references, since their order can not be + changed. A volatile and non-volatile reference can be interchanged + though. + + A MEM_IN_STRUCT reference at a non-QImode varying address can never + conflict with a non-MEM_IN_STRUCT reference at a fixed address. We must + allow QImode aliasing because the ANSI C standard allows character + pointers to alias anything. We are assuming that characters are + always QImode here. */ + +/* Read dependence: X is read after read in MEM takes place. There can + only be a dependence here if both reads are volatile. */ + +int +read_dependence (mem, x) + rtx mem; + rtx x; +{ + return MEM_VOLATILE_P (x) && MEM_VOLATILE_P (mem); +} + +/* True dependence: X is read after store in MEM takes place. */ + +int +true_dependence (mem, x) + rtx mem; + rtx x; +{ + /* If X is an unchanging read, then it can't possibly conflict with any + non-unchanging store. It may conflict with an unchanging write though, + because there may be a single store to this address to initialize it. + Just fall through to the code below to resolve the case where we have + both an unchanging read and an unchanging write. This won't handle all + cases optimally, but the possible performance loss should be + negligible. */ + if (RTX_UNCHANGING_P (x) && ! RTX_UNCHANGING_P (mem)) + return 0; + + return ((MEM_VOLATILE_P (x) && MEM_VOLATILE_P (mem)) + || (memrefs_conflict_p (SIZE_FOR_MODE (mem), XEXP (mem, 0), + SIZE_FOR_MODE (x), XEXP (x, 0), 0) + && ! (MEM_IN_STRUCT_P (mem) && rtx_addr_varies_p (mem) + && GET_MODE (mem) != QImode + && ! MEM_IN_STRUCT_P (x) && ! rtx_addr_varies_p (x)) + && ! (MEM_IN_STRUCT_P (x) && rtx_addr_varies_p (x) + && GET_MODE (x) != QImode + && ! MEM_IN_STRUCT_P (mem) && ! rtx_addr_varies_p (mem)))); +} + +/* Anti dependence: X is written after read in MEM takes place. */ + +int +anti_dependence (mem, x) + rtx mem; + rtx x; +{ + /* If MEM is an unchanging read, then it can't possibly conflict with + the store to X, because there is at most one store to MEM, and it must + have occured somewhere before MEM. */ + if (RTX_UNCHANGING_P (mem)) + return 0; + + return ((MEM_VOLATILE_P (x) && MEM_VOLATILE_P (mem)) + || (memrefs_conflict_p (SIZE_FOR_MODE (mem), XEXP (mem, 0), + SIZE_FOR_MODE (x), XEXP (x, 0), 0) + && ! (MEM_IN_STRUCT_P (mem) && rtx_addr_varies_p (mem) + && GET_MODE (mem) != QImode + && ! MEM_IN_STRUCT_P (x) && ! rtx_addr_varies_p (x)) + && ! (MEM_IN_STRUCT_P (x) && rtx_addr_varies_p (x) + && GET_MODE (x) != QImode + && ! MEM_IN_STRUCT_P (mem) && ! rtx_addr_varies_p (mem)))); +} + +/* Output dependence: X is written after store in MEM takes place. */ + +int +output_dependence (mem, x) + rtx mem; + rtx x; +{ + return ((MEM_VOLATILE_P (x) && MEM_VOLATILE_P (mem)) + || (memrefs_conflict_p (SIZE_FOR_MODE (mem), XEXP (mem, 0), + SIZE_FOR_MODE (x), XEXP (x, 0), 0) + && ! (MEM_IN_STRUCT_P (mem) && rtx_addr_varies_p (mem) + && GET_MODE (mem) != QImode + && ! MEM_IN_STRUCT_P (x) && ! rtx_addr_varies_p (x)) + && ! (MEM_IN_STRUCT_P (x) && rtx_addr_varies_p (x) + && GET_MODE (x) != QImode + && ! MEM_IN_STRUCT_P (mem) && ! rtx_addr_varies_p (mem)))); +} + +/* Helper functions for instruction scheduling. */ + +/* Add ELEM wrapped in an INSN_LIST with reg note kind DEP_TYPE to the + LOG_LINKS of INSN, if not already there. DEP_TYPE indicates the type + of dependence that this link represents. */ + +void +add_dependence (insn, elem, dep_type) + rtx insn; + rtx elem; + enum reg_note dep_type; +{ + rtx link, next; + + /* Don't depend an insn on itself. */ + if (insn == elem) + return; + + /* If elem is part of a sequence that must be scheduled together, then + make the dependence point to the last insn of the sequence. + When HAVE_cc0, it is possible for NOTEs to exist between users and + setters of the condition codes, so we must skip past notes here. + Otherwise, NOTEs are impossible here. */ + + next = NEXT_INSN (elem); + +#ifdef HAVE_cc0 + while (next && GET_CODE (next) == NOTE) + next = NEXT_INSN (next); +#endif + + if (next && SCHED_GROUP_P (next)) + { + /* Notes will never intervene here though, so don't bother checking + for them. */ + /* We must reject CODE_LABELs, so that we don't get confused by one + that has LABEL_PRESERVE_P set, which is represented by the same + bit in the rtl as SCHED_GROUP_P. A CODE_LABEL can never be + SCHED_GROUP_P. */ + while (NEXT_INSN (next) && SCHED_GROUP_P (NEXT_INSN (next)) + && GET_CODE (NEXT_INSN (next)) != CODE_LABEL) + next = NEXT_INSN (next); + + /* Again, don't depend an insn on itself. */ + if (insn == next) + return; + + /* Make the dependence to NEXT, the last insn of the group, instead + of the original ELEM. */ + elem = next; + } + + /* Check that we don't already have this dependence. */ + for (link = LOG_LINKS (insn); link; link = XEXP (link, 1)) + if (XEXP (link, 0) == elem) + { + /* If this is a more restrictive type of dependence than the existing + one, then change the existing dependence to this type. */ + if ((int) dep_type < (int) REG_NOTE_KIND (link)) + PUT_REG_NOTE_KIND (link, dep_type); + return; + } + /* Might want to check one level of transitivity to save conses. */ + + link = rtx_alloc (INSN_LIST); + /* Insn dependency, not data dependency. */ + PUT_REG_NOTE_KIND (link, dep_type); + XEXP (link, 0) = elem; + XEXP (link, 1) = LOG_LINKS (insn); + LOG_LINKS (insn) = link; +} + +/* Remove ELEM wrapped in an INSN_LIST from the LOG_LINKS + of INSN. Abort if not found. */ +void +remove_dependence (insn, elem) + rtx insn; + rtx elem; +{ + rtx prev, link; + int found = 0; + + for (prev = 0, link = LOG_LINKS (insn); link; + prev = link, link = XEXP (link, 1)) + { + if (XEXP (link, 0) == elem) + { + if (prev) + XEXP (prev, 1) = XEXP (link, 1); + else + LOG_LINKS (insn) = XEXP (link, 1); + found = 1; + } + } + + if (! found) + abort (); + return; +} + +#ifndef INSN_SCHEDULING +void schedule_insns () {} +#else +#ifndef __GNUC__ +#define __inline +#endif + +/* Computation of memory dependencies. */ + +/* The *_insns and *_mems are paired lists. Each pending memory operation + will have a pointer to the MEM rtx on one list and a pointer to the + containing insn on the other list in the same place in the list. */ + +/* We can't use add_dependence like the old code did, because a single insn + may have multiple memory accesses, and hence needs to be on the list + once for each memory access. Add_dependence won't let you add an insn + to a list more than once. */ + +/* An INSN_LIST containing all insns with pending read operations. */ +static rtx pending_read_insns; + +/* An EXPR_LIST containing all MEM rtx's which are pending reads. */ +static rtx pending_read_mems; + +/* An INSN_LIST containing all insns with pending write operations. */ +static rtx pending_write_insns; + +/* An EXPR_LIST containing all MEM rtx's which are pending writes. */ +static rtx pending_write_mems; + +/* Indicates the combined length of the two pending lists. We must prevent + these lists from ever growing too large since the number of dependencies + produced is at least O(N*N), and execution time is at least O(4*N*N), as + a function of the length of these pending lists. */ + +static int pending_lists_length; + +/* An INSN_LIST containing all INSN_LISTs allocated but currently unused. */ + +static rtx unused_insn_list; + +/* An EXPR_LIST containing all EXPR_LISTs allocated but currently unused. */ + +static rtx unused_expr_list; + +/* The last insn upon which all memory references must depend. + This is an insn which flushed the pending lists, creating a dependency + between it and all previously pending memory references. This creates + a barrier (or a checkpoint) which no memory reference is allowed to cross. + + This includes all non constant CALL_INSNs. When we do interprocedural + alias analysis, this restriction can be relaxed. + This may also be an INSN that writes memory if the pending lists grow + too large. */ + +static rtx last_pending_memory_flush; + +/* The last function call we have seen. All hard regs, and, of course, + the last function call, must depend on this. */ + +static rtx last_function_call; + +/* The LOG_LINKS field of this is a list of insns which use a pseudo register + that does not already cross a call. We create dependencies between each + of those insn and the next call insn, to ensure that they won't cross a call + after scheduling is done. */ + +static rtx sched_before_next_call; + +/* Pointer to the last instruction scheduled. Used by rank_for_schedule, + so that insns independent of the last scheduled insn will be preferred + over dependent instructions. */ + +static rtx last_scheduled_insn; + +/* Process an insn's memory dependencies. There are four kinds of + dependencies: + + (0) read dependence: read follows read + (1) true dependence: read follows write + (2) anti dependence: write follows read + (3) output dependence: write follows write + + We are careful to build only dependencies which actually exist, and + use transitivity to avoid building too many links. */ + +/* Return the INSN_LIST containing INSN in LIST, or NULL + if LIST does not contain INSN. */ + +__inline static rtx +find_insn_list (insn, list) + rtx insn; + rtx list; +{ + while (list) + { + if (XEXP (list, 0) == insn) + return list; + list = XEXP (list, 1); + } + return 0; +} + +/* Compute the function units used by INSN. This caches the value + returned by function_units_used. A function unit is encoded as the + unit number if the value is non-negative and the compliment of a + mask if the value is negative. A function unit index is the + non-negative encoding. */ + +__inline static int +insn_unit (insn) + rtx insn; +{ + register int unit = INSN_UNIT (insn); + + if (unit == 0) + { + recog_memoized (insn); + + /* A USE insn, or something else we don't need to understand. + We can't pass these directly to function_units_used because it will + trigger a fatal error for unrecognizable insns. */ + if (INSN_CODE (insn) < 0) + unit = -1; + else + { + unit = function_units_used (insn); + /* Increment non-negative values so we can cache zero. */ + if (unit >= 0) unit++; + } + /* We only cache 16 bits of the result, so if the value is out of + range, don't cache it. */ + if (FUNCTION_UNITS_SIZE < HOST_BITS_PER_SHORT + || unit >= 0 + || (~unit & ((1 << (HOST_BITS_PER_SHORT - 1)) - 1)) == 0) + INSN_UNIT (insn) = unit; + } + return (unit > 0 ? unit - 1 : unit); +} + +/* Compute the blockage range for executing INSN on UNIT. This caches + the value returned by the blockage_range_function for the unit. + These values are encoded in an int where the upper half gives the + minimum value and the lower half gives the maximum value. */ + +__inline static unsigned int +blockage_range (unit, insn) + int unit; + rtx insn; +{ + unsigned int blockage = INSN_BLOCKAGE (insn); + unsigned int range; + + if (UNIT_BLOCKED (blockage) != unit + 1) + { + range = function_units[unit].blockage_range_function (insn); + /* We only cache the blockage range for one unit and then only if + the values fit. */ + if (HOST_BITS_PER_INT >= UNIT_BITS + 2 * BLOCKAGE_BITS) + INSN_BLOCKAGE (insn) = ENCODE_BLOCKAGE (unit + 1, range); + } + else + range = BLOCKAGE_RANGE (blockage); + + return range; +} + +/* A vector indexed by function unit instance giving the last insn to use + the unit. The value of the function unit instance index for unit U + instance I is (U + I * FUNCTION_UNITS_SIZE). */ +static rtx unit_last_insn[FUNCTION_UNITS_SIZE * MAX_MULTIPLICITY]; + +/* A vector indexed by function unit instance giving the minimum time when + the unit will unblock based on the maximum blockage cost. */ +static int unit_tick[FUNCTION_UNITS_SIZE * MAX_MULTIPLICITY]; + +/* A vector indexed by function unit number giving the number of insns + that remain to use the unit. */ +static int unit_n_insns[FUNCTION_UNITS_SIZE]; + +/* Reset the function unit state to the null state. */ + +static void +clear_units () +{ + int unit; + + bzero (unit_last_insn, sizeof (unit_last_insn)); + bzero (unit_tick, sizeof (unit_tick)); + bzero (unit_n_insns, sizeof (unit_n_insns)); +} + +/* Record an insn as one that will use the units encoded by UNIT. */ + +__inline static void +prepare_unit (unit) + int unit; +{ + int i; + + if (unit >= 0) + unit_n_insns[unit]++; + else + for (i = 0, unit = ~unit; unit; i++, unit >>= 1) + if ((unit & 1) != 0) + prepare_unit (i); +} + +/* Return the actual hazard cost of executing INSN on the unit UNIT, + instance INSTANCE at time CLOCK if the previous actual hazard cost + was COST. */ + +__inline static int +actual_hazard_this_instance (unit, instance, insn, clock, cost) + int unit, instance, clock, cost; + rtx insn; +{ + int i; + int tick = unit_tick[instance]; + + if (tick - clock > cost) + { + /* The scheduler is operating in reverse, so INSN is the executing + insn and the unit's last insn is the candidate insn. We want a + more exact measure of the blockage if we execute INSN at CLOCK + given when we committed the execution of the unit's last insn. + + The blockage value is given by either the unit's max blockage + constant, blockage range function, or blockage function. Use + the most exact form for the given unit. */ + + if (function_units[unit].blockage_range_function) + { + if (function_units[unit].blockage_function) + tick += (function_units[unit].blockage_function + (insn, unit_last_insn[instance]) + - function_units[unit].max_blockage); + else + tick += ((int) MAX_BLOCKAGE_COST (blockage_range (unit, insn)) + - function_units[unit].max_blockage); + } + if (tick - clock > cost) + cost = tick - clock; + } + return cost; +} + +/* Record INSN as having begun execution on the units encoded by UNIT at + time CLOCK. */ + +__inline static void +schedule_unit (unit, insn, clock) + int unit, clock; + rtx insn; +{ + int i; + + if (unit >= 0) + { + int instance = unit; +#if MAX_MULTIPLICITY > 1 + /* Find the first free instance of the function unit and use that + one. We assume that one is free. */ + for (i = function_units[unit].multiplicity - 1; i > 0; i--) + { + if (! actual_hazard_this_instance (unit, instance, insn, clock, 0)) + break; + instance += FUNCTION_UNITS_SIZE; + } +#endif + unit_last_insn[instance] = insn; + unit_tick[instance] = (clock + function_units[unit].max_blockage); + } + else + for (i = 0, unit = ~unit; unit; i++, unit >>= 1) + if ((unit & 1) != 0) + schedule_unit (i, insn, clock); +} + +/* Return the actual hazard cost of executing INSN on the units encoded by + UNIT at time CLOCK if the previous actual hazard cost was COST. */ + +__inline static int +actual_hazard (unit, insn, clock, cost) + int unit, clock, cost; + rtx insn; +{ + int i; + + if (unit >= 0) + { + /* Find the instance of the function unit with the minimum hazard. */ + int instance = unit; + int best = instance; + int best_cost = actual_hazard_this_instance (unit, instance, insn, + clock, cost); + int this_cost; + +#if MAX_MULTIPLICITY > 1 + if (best_cost > cost) + { + for (i = function_units[unit].multiplicity - 1; i > 0; i--) + { + instance += FUNCTION_UNITS_SIZE; + this_cost = actual_hazard_this_instance (unit, instance, insn, + clock, cost); + if (this_cost < best_cost) + { + best = instance; + best_cost = this_cost; + if (this_cost <= cost) + break; + } + } + } +#endif + cost = MAX (cost, best_cost); + } + else + for (i = 0, unit = ~unit; unit; i++, unit >>= 1) + if ((unit & 1) != 0) + cost = actual_hazard (i, insn, clock, cost); + + return cost; +} + +/* Return the potential hazard cost of executing an instruction on the + units encoded by UNIT if the previous potential hazard cost was COST. + An insn with a large blockage time is chosen in preference to one + with a smaller time; an insn that uses a unit that is more likely + to be used is chosen in preference to one with a unit that is less + used. We are trying to minimize a subsequent actual hazard. */ + +__inline static int +potential_hazard (unit, insn, cost) + int unit, cost; + rtx insn; +{ + int i, ncost; + unsigned int minb, maxb; + + if (unit >= 0) + { + minb = maxb = function_units[unit].max_blockage; + if (maxb > 1) + { + if (function_units[unit].blockage_range_function) + { + maxb = minb = blockage_range (unit, insn); + maxb = MAX_BLOCKAGE_COST (maxb); + minb = MIN_BLOCKAGE_COST (minb); + } + + if (maxb > 1) + { + /* Make the number of instructions left dominate. Make the + minimum delay dominate the maximum delay. If all these + are the same, use the unit number to add an arbitrary + ordering. Other terms can be added. */ + ncost = minb * 0x40 + maxb; + ncost *= (unit_n_insns[unit] - 1) * 0x1000 + unit; + if (ncost > cost) + cost = ncost; + } + } + } + else + for (i = 0, unit = ~unit; unit; i++, unit >>= 1) + if ((unit & 1) != 0) + cost = potential_hazard (i, insn, cost); + + return cost; +} + +/* Compute cost of executing INSN given the dependence LINK on the insn USED. + This is the number of virtual cycles taken between instruction issue and + instruction results. */ + +__inline static int +insn_cost (insn, link, used) + rtx insn, link, used; +{ + register int cost = INSN_COST (insn); + + if (cost == 0) + { + recog_memoized (insn); + + /* A USE insn, or something else we don't need to understand. + We can't pass these directly to result_ready_cost because it will + trigger a fatal error for unrecognizable insns. */ + if (INSN_CODE (insn) < 0) + { + INSN_COST (insn) = 1; + return 1; + } + else + { + cost = result_ready_cost (insn); + + if (cost < 1) + cost = 1; + + INSN_COST (insn) = cost; + } + } + + /* A USE insn should never require the value used to be computed. This + allows the computation of a function's result and parameter values to + overlap the return and call. */ + recog_memoized (used); + if (INSN_CODE (used) < 0) + LINK_COST_FREE (link) = 1; + + /* If some dependencies vary the cost, compute the adjustment. Most + commonly, the adjustment is complete: either the cost is ignored + (in the case of an output- or anti-dependence), or the cost is + unchanged. These values are cached in the link as LINK_COST_FREE + and LINK_COST_ZERO. */ + + if (LINK_COST_FREE (link)) + cost = 1; +#ifdef ADJUST_COST + else if (! LINK_COST_ZERO (link)) + { + int ncost = cost; + + ADJUST_COST (used, link, insn, ncost); + if (ncost <= 1) + LINK_COST_FREE (link) = ncost = 1; + if (cost == ncost) + LINK_COST_ZERO (link) = 1; + cost = ncost; + } +#endif + return cost; +} + +/* Compute the priority number for INSN. */ + +static int +priority (insn) + rtx insn; +{ + if (insn && GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + int prev_priority; + int max_priority; + int this_priority = INSN_PRIORITY (insn); + rtx prev; + + if (this_priority > 0) + return this_priority; + + max_priority = 1; + + /* Nonzero if these insns must be scheduled together. */ + if (SCHED_GROUP_P (insn)) + { + prev = insn; + while (SCHED_GROUP_P (prev)) + { + prev = PREV_INSN (prev); + INSN_REF_COUNT (prev) += 1; + } + } + + for (prev = LOG_LINKS (insn); prev; prev = XEXP (prev, 1)) + { + rtx x = XEXP (prev, 0); + + /* A dependence pointing to a note is always obsolete, because + sched_analyze_insn will have created any necessary new dependences + which replace it. Notes can be created when instructions are + deleted by insn splitting, or by register allocation. */ + if (GET_CODE (x) == NOTE) + { + remove_dependence (insn, x); + continue; + } + + /* Clear the link cost adjustment bits. */ + LINK_COST_FREE (prev) = 0; +#ifdef ADJUST_COST + LINK_COST_ZERO (prev) = 0; +#endif + + /* This priority calculation was chosen because it results in the + least instruction movement, and does not hurt the performance + of the resulting code compared to the old algorithm. + This makes the sched algorithm more stable, which results + in better code, because there is less register pressure, + cross jumping is more likely to work, and debugging is easier. + + When all instructions have a latency of 1, there is no need to + move any instructions. Subtracting one here ensures that in such + cases all instructions will end up with a priority of one, and + hence no scheduling will be done. + + The original code did not subtract the one, and added the + insn_cost of the current instruction to its priority (e.g. + move the insn_cost call down to the end). */ + + if (REG_NOTE_KIND (prev) == 0) + /* Data dependence. */ + prev_priority = priority (x) + insn_cost (x, prev, insn) - 1; + else + /* Anti or output dependence. Don't add the latency of this + insn's result, because it isn't being used. */ + prev_priority = priority (x); + + if (prev_priority > max_priority) + max_priority = prev_priority; + INSN_REF_COUNT (x) += 1; + } + + prepare_unit (insn_unit (insn)); + INSN_PRIORITY (insn) = max_priority; + return INSN_PRIORITY (insn); + } + return 0; +} + +/* Remove all INSN_LISTs and EXPR_LISTs from the pending lists and add + them to the unused_*_list variables, so that they can be reused. */ + +static void +free_pending_lists () +{ + register rtx link, prev_link; + + if (pending_read_insns) + { + prev_link = pending_read_insns; + link = XEXP (prev_link, 1); + + while (link) + { + prev_link = link; + link = XEXP (link, 1); + } + + XEXP (prev_link, 1) = unused_insn_list; + unused_insn_list = pending_read_insns; + pending_read_insns = 0; + } + + if (pending_write_insns) + { + prev_link = pending_write_insns; + link = XEXP (prev_link, 1); + + while (link) + { + prev_link = link; + link = XEXP (link, 1); + } + + XEXP (prev_link, 1) = unused_insn_list; + unused_insn_list = pending_write_insns; + pending_write_insns = 0; + } + + if (pending_read_mems) + { + prev_link = pending_read_mems; + link = XEXP (prev_link, 1); + + while (link) + { + prev_link = link; + link = XEXP (link, 1); + } + + XEXP (prev_link, 1) = unused_expr_list; + unused_expr_list = pending_read_mems; + pending_read_mems = 0; + } + + if (pending_write_mems) + { + prev_link = pending_write_mems; + link = XEXP (prev_link, 1); + + while (link) + { + prev_link = link; + link = XEXP (link, 1); + } + + XEXP (prev_link, 1) = unused_expr_list; + unused_expr_list = pending_write_mems; + pending_write_mems = 0; + } +} + +/* Add an INSN and MEM reference pair to a pending INSN_LIST and MEM_LIST. + The MEM is a memory reference contained within INSN, which we are saving + so that we can do memory aliasing on it. */ + +static void +add_insn_mem_dependence (insn_list, mem_list, insn, mem) + rtx *insn_list, *mem_list, insn, mem; +{ + register rtx link; + + if (unused_insn_list) + { + link = unused_insn_list; + unused_insn_list = XEXP (link, 1); + } + else + link = rtx_alloc (INSN_LIST); + XEXP (link, 0) = insn; + XEXP (link, 1) = *insn_list; + *insn_list = link; + + if (unused_expr_list) + { + link = unused_expr_list; + unused_expr_list = XEXP (link, 1); + } + else + link = rtx_alloc (EXPR_LIST); + XEXP (link, 0) = mem; + XEXP (link, 1) = *mem_list; + *mem_list = link; + + pending_lists_length++; +} + +/* Make a dependency between every memory reference on the pending lists + and INSN, thus flushing the pending lists. */ + +static void +flush_pending_lists (insn) + rtx insn; +{ + rtx link; + + while (pending_read_insns) + { + add_dependence (insn, XEXP (pending_read_insns, 0), REG_DEP_ANTI); + + link = pending_read_insns; + pending_read_insns = XEXP (pending_read_insns, 1); + XEXP (link, 1) = unused_insn_list; + unused_insn_list = link; + + link = pending_read_mems; + pending_read_mems = XEXP (pending_read_mems, 1); + XEXP (link, 1) = unused_expr_list; + unused_expr_list = link; + } + while (pending_write_insns) + { + add_dependence (insn, XEXP (pending_write_insns, 0), REG_DEP_ANTI); + + link = pending_write_insns; + pending_write_insns = XEXP (pending_write_insns, 1); + XEXP (link, 1) = unused_insn_list; + unused_insn_list = link; + + link = pending_write_mems; + pending_write_mems = XEXP (pending_write_mems, 1); + XEXP (link, 1) = unused_expr_list; + unused_expr_list = link; + } + pending_lists_length = 0; + + if (last_pending_memory_flush) + add_dependence (insn, last_pending_memory_flush, REG_DEP_ANTI); + + last_pending_memory_flush = insn; +} + +/* Analyze a single SET or CLOBBER rtx, X, creating all dependencies generated + by the write to the destination of X, and reads of everything mentioned. */ + +static void +sched_analyze_1 (x, insn) + rtx x; + rtx insn; +{ + register int regno; + register rtx dest = SET_DEST (x); + + if (dest == 0) + return; + + while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SUBREG + || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT) + { + if (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT) + { + /* The second and third arguments are values read by this insn. */ + sched_analyze_2 (XEXP (dest, 1), insn); + sched_analyze_2 (XEXP (dest, 2), insn); + } + dest = SUBREG_REG (dest); + } + + if (GET_CODE (dest) == REG) + { + register int offset, bit, i; + + regno = REGNO (dest); + + /* A hard reg in a wide mode may really be multiple registers. + If so, mark all of them just like the first. */ + if (regno < FIRST_PSEUDO_REGISTER) + { + i = HARD_REGNO_NREGS (regno, GET_MODE (dest)); + while (--i >= 0) + { + rtx u; + + for (u = reg_last_uses[regno+i]; u; u = XEXP (u, 1)) + add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); + reg_last_uses[regno + i] = 0; + if (reg_last_sets[regno + i]) + add_dependence (insn, reg_last_sets[regno + i], + REG_DEP_OUTPUT); + reg_last_sets[regno + i] = insn; + if ((call_used_regs[i] || global_regs[i]) + && last_function_call) + /* Function calls clobber all call_used regs. */ + add_dependence (insn, last_function_call, REG_DEP_ANTI); + } + } + else + { + rtx u; + + for (u = reg_last_uses[regno]; u; u = XEXP (u, 1)) + add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); + reg_last_uses[regno] = 0; + if (reg_last_sets[regno]) + add_dependence (insn, reg_last_sets[regno], REG_DEP_OUTPUT); + reg_last_sets[regno] = insn; + + /* Pseudos that are REG_EQUIV to something may be replaced + by that during reloading. We need only add dependencies for + the address in the REG_EQUIV note. */ + if (! reload_completed + && reg_known_equiv_p[regno] + && GET_CODE (reg_known_value[regno]) == MEM) + sched_analyze_2 (XEXP (reg_known_value[regno], 0), insn); + + /* Don't let it cross a call after scheduling if it doesn't + already cross one. */ + if (reg_n_calls_crossed[regno] == 0 && last_function_call) + add_dependence (insn, last_function_call, REG_DEP_ANTI); + } + } + else if (GET_CODE (dest) == MEM) + { + /* Writing memory. */ + + if (pending_lists_length > 32) + { + /* Flush all pending reads and writes to prevent the pending lists + from getting any larger. Insn scheduling runs too slowly when + these lists get long. The number 32 was chosen because it + seems like a reasonable number. When compiling GCC with itself, + this flush occurs 8 times for sparc, and 10 times for m88k using + the number 32. */ + flush_pending_lists (insn); + } + else + { + rtx pending, pending_mem; + + pending = pending_read_insns; + pending_mem = pending_read_mems; + while (pending) + { + /* If a dependency already exists, don't create a new one. */ + if (! find_insn_list (XEXP (pending, 0), LOG_LINKS (insn))) + if (anti_dependence (XEXP (pending_mem, 0), dest)) + add_dependence (insn, XEXP (pending, 0), REG_DEP_ANTI); + + pending = XEXP (pending, 1); + pending_mem = XEXP (pending_mem, 1); + } + + pending = pending_write_insns; + pending_mem = pending_write_mems; + while (pending) + { + /* If a dependency already exists, don't create a new one. */ + if (! find_insn_list (XEXP (pending, 0), LOG_LINKS (insn))) + if (output_dependence (XEXP (pending_mem, 0), dest)) + add_dependence (insn, XEXP (pending, 0), REG_DEP_OUTPUT); + + pending = XEXP (pending, 1); + pending_mem = XEXP (pending_mem, 1); + } + + if (last_pending_memory_flush) + add_dependence (insn, last_pending_memory_flush, REG_DEP_ANTI); + + add_insn_mem_dependence (&pending_write_insns, &pending_write_mems, + insn, dest); + } + sched_analyze_2 (XEXP (dest, 0), insn); + } + + /* Analyze reads. */ + if (GET_CODE (x) == SET) + sched_analyze_2 (SET_SRC (x), insn); +} + +/* Analyze the uses of memory and registers in rtx X in INSN. */ + +static void +sched_analyze_2 (x, insn) + rtx x; + rtx insn; +{ + register int i; + register int j; + register enum rtx_code code; + register char *fmt; + + if (x == 0) + return; + + code = GET_CODE (x); + + switch (code) + { + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + case CONST: + case LABEL_REF: + /* Ignore constants. Note that we must handle CONST_DOUBLE here + because it may have a cc0_rtx in its CONST_DOUBLE_CHAIN field, but + this does not mean that this insn is using cc0. */ + return; + +#ifdef HAVE_cc0 + case CC0: + { + rtx link, prev; + + /* There may be a note before this insn now, but all notes will + be removed before we actually try to schedule the insns, so + it won't cause a problem later. We must avoid it here though. */ + + /* User of CC0 depends on immediately preceding insn. */ + SCHED_GROUP_P (insn) = 1; + + /* Make a copy of all dependencies on the immediately previous insn, + and add to this insn. This is so that all the dependencies will + apply to the group. Remove an explicit dependence on this insn + as SCHED_GROUP_P now represents it. */ + + prev = PREV_INSN (insn); + while (GET_CODE (prev) == NOTE) + prev = PREV_INSN (prev); + + if (find_insn_list (prev, LOG_LINKS (insn))) + remove_dependence (insn, prev); + + for (link = LOG_LINKS (prev); link; link = XEXP (link, 1)) + add_dependence (insn, XEXP (link, 0), REG_NOTE_KIND (link)); + + return; + } +#endif + + case REG: + { + int regno = REGNO (x); + if (regno < FIRST_PSEUDO_REGISTER) + { + int i; + + i = HARD_REGNO_NREGS (regno, GET_MODE (x)); + while (--i >= 0) + { + reg_last_uses[regno + i] + = gen_rtx (INSN_LIST, VOIDmode, + insn, reg_last_uses[regno + i]); + if (reg_last_sets[regno + i]) + add_dependence (insn, reg_last_sets[regno + i], 0); + if ((call_used_regs[regno + i] || global_regs[regno + i]) + && last_function_call) + /* Function calls clobber all call_used regs. */ + add_dependence (insn, last_function_call, REG_DEP_ANTI); + } + } + else + { + reg_last_uses[regno] + = gen_rtx (INSN_LIST, VOIDmode, insn, reg_last_uses[regno]); + if (reg_last_sets[regno]) + add_dependence (insn, reg_last_sets[regno], 0); + + /* Pseudos that are REG_EQUIV to something may be replaced + by that during reloading. We need only add dependencies for + the address in the REG_EQUIV note. */ + if (! reload_completed + && reg_known_equiv_p[regno] + && GET_CODE (reg_known_value[regno]) == MEM) + sched_analyze_2 (XEXP (reg_known_value[regno], 0), insn); + + /* If the register does not already cross any calls, then add this + insn to the sched_before_next_call list so that it will still + not cross calls after scheduling. */ + if (reg_n_calls_crossed[regno] == 0) + add_dependence (sched_before_next_call, insn, REG_DEP_ANTI); + } + return; + } + + case MEM: + { + /* Reading memory. */ + + rtx pending, pending_mem; + + pending = pending_read_insns; + pending_mem = pending_read_mems; + while (pending) + { + /* If a dependency already exists, don't create a new one. */ + if (! find_insn_list (XEXP (pending, 0), LOG_LINKS (insn))) + if (read_dependence (XEXP (pending_mem, 0), x)) + add_dependence (insn, XEXP (pending, 0), REG_DEP_ANTI); + + pending = XEXP (pending, 1); + pending_mem = XEXP (pending_mem, 1); + } + + pending = pending_write_insns; + pending_mem = pending_write_mems; + while (pending) + { + /* If a dependency already exists, don't create a new one. */ + if (! find_insn_list (XEXP (pending, 0), LOG_LINKS (insn))) + if (true_dependence (XEXP (pending_mem, 0), x)) + add_dependence (insn, XEXP (pending, 0), 0); + + pending = XEXP (pending, 1); + pending_mem = XEXP (pending_mem, 1); + } + if (last_pending_memory_flush) + add_dependence (insn, last_pending_memory_flush, REG_DEP_ANTI); + + /* Always add these dependencies to pending_reads, since + this insn may be followed by a write. */ + add_insn_mem_dependence (&pending_read_insns, &pending_read_mems, + insn, x); + + /* Take advantage of tail recursion here. */ + sched_analyze_2 (XEXP (x, 0), insn); + return; + } + + case ASM_OPERANDS: + case ASM_INPUT: + case UNSPEC_VOLATILE: + case TRAP_IF: + { + rtx u; + + /* Traditional and volatile asm instructions must be considered to use + and clobber all hard registers and all of memory. So must + TRAP_IF and UNSPEC_VOLATILE operations. */ + if (code != ASM_OPERANDS || MEM_VOLATILE_P (x)) + { + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + for (u = reg_last_uses[i]; u; u = XEXP (u, 1)) + add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); + reg_last_uses[i] = 0; + if (reg_last_sets[i]) + add_dependence (insn, reg_last_sets[i], 0); + reg_last_sets[i] = insn; + } + + flush_pending_lists (insn); + } + + /* For all ASM_OPERANDS, we must traverse the vector of input operands. + We can not just fall through here since then we would be confused + by the ASM_INPUT rtx inside ASM_OPERANDS, which do not indicate + traditional asms unlike their normal usage. */ + + if (code == ASM_OPERANDS) + { + for (j = 0; j < ASM_OPERANDS_INPUT_LENGTH (x); j++) + sched_analyze_2 (ASM_OPERANDS_INPUT (x, j), insn); + return; + } + break; + } + + case PRE_DEC: + case POST_DEC: + case PRE_INC: + case POST_INC: + /* These both read and modify the result. We must handle them as writes + to get proper dependencies for following instructions. We must handle + them as reads to get proper dependencies from this to previous + instructions. Thus we need to pass them to both sched_analyze_1 + and sched_analyze_2. We must call sched_analyze_2 first in order + to get the proper antecedent for the read. */ + sched_analyze_2 (XEXP (x, 0), insn); + sched_analyze_1 (x, insn); + return; + } + + /* Other cases: walk the insn. */ + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + sched_analyze_2 (XEXP (x, i), insn); + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + sched_analyze_2 (XVECEXP (x, i, j), insn); + } +} + +/* Analyze an INSN with pattern X to find all dependencies. */ + +static void +sched_analyze_insn (x, insn) + rtx x, insn; +{ + register RTX_CODE code = GET_CODE (x); + rtx link; + + if (code == SET || code == CLOBBER) + sched_analyze_1 (x, insn); + else if (code == PARALLEL) + { + register int i; + for (i = XVECLEN (x, 0) - 1; i >= 0; i--) + { + code = GET_CODE (XVECEXP (x, 0, i)); + if (code == SET || code == CLOBBER) + sched_analyze_1 (XVECEXP (x, 0, i), insn); + else + sched_analyze_2 (XVECEXP (x, 0, i), insn); + } + } + else + sched_analyze_2 (x, insn); + + /* Handle function calls. */ + if (GET_CODE (insn) == CALL_INSN) + { + rtx dep_insn; + rtx prev_dep_insn; + + /* When scheduling instructions, we make sure calls don't lose their + accompanying USE insns by depending them one on another in order. */ + + prev_dep_insn = insn; + dep_insn = PREV_INSN (insn); + while (GET_CODE (dep_insn) == INSN + && GET_CODE (PATTERN (dep_insn)) == USE) + { + SCHED_GROUP_P (prev_dep_insn) = 1; + + /* Make a copy of all dependencies on dep_insn, and add to insn. + This is so that all of the dependencies will apply to the + group. */ + + for (link = LOG_LINKS (dep_insn); link; link = XEXP (link, 1)) + add_dependence (insn, XEXP (link, 0), REG_NOTE_KIND (link)); + + prev_dep_insn = dep_insn; + dep_insn = PREV_INSN (dep_insn); + } + } +} + +/* Analyze every insn between HEAD and TAIL inclusive, creating LOG_LINKS + for every dependency. */ + +static int +sched_analyze (head, tail) + rtx head, tail; +{ + register rtx insn; + register int n_insns = 0; + register rtx u; + register int luid = 0; + + for (insn = head; ; insn = NEXT_INSN (insn)) + { + INSN_LUID (insn) = luid++; + + if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN) + { + sched_analyze_insn (PATTERN (insn), insn); + n_insns += 1; + } + else if (GET_CODE (insn) == CALL_INSN) + { + rtx dest = 0; + rtx x; + register int i; + + /* Any instruction using a hard register which may get clobbered + by a call needs to be marked as dependent on this call. + This prevents a use of a hard return reg from being moved + past a void call (i.e. it does not explicitly set the hard + return reg). */ + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (call_used_regs[i] || global_regs[i]) + { + for (u = reg_last_uses[i]; u; u = XEXP (u, 1)) + add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); + reg_last_uses[i] = 0; + if (reg_last_sets[i]) + add_dependence (insn, reg_last_sets[i], REG_DEP_ANTI); + reg_last_sets[i] = insn; + /* Insn, being a CALL_INSN, magically depends on + `last_function_call' already. */ + } + + /* For each insn which shouldn't cross a call, add a dependence + between that insn and this call insn. */ + x = LOG_LINKS (sched_before_next_call); + while (x) + { + add_dependence (insn, XEXP (x, 0), REG_DEP_ANTI); + x = XEXP (x, 1); + } + LOG_LINKS (sched_before_next_call) = 0; + + sched_analyze_insn (PATTERN (insn), insn); + + /* We don't need to flush memory for a function call which does + not involve memory. */ + if (! CONST_CALL_P (insn)) + { + /* In the absence of interprocedural alias analysis, + we must flush all pending reads and writes, and + start new dependencies starting from here. */ + flush_pending_lists (insn); + } + + /* Depend this function call (actually, the user of this + function call) on all hard register clobberage. */ + last_function_call = insn; + n_insns += 1; + } + + if (insn == tail) + return n_insns; + } +} + +/* Called when we see a set of a register. If death is true, then we are + scanning backwards. Mark that register as unborn. If nobody says + otherwise, that is how things will remain. If death is false, then we + are scanning forwards. Mark that register as being born. */ + +static void +sched_note_set (b, x, death) + int b; + rtx x; + int death; +{ + register int regno, j; + register rtx reg = SET_DEST (x); + int subreg_p = 0; + + if (reg == 0) + return; + + while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == STRICT_LOW_PART + || GET_CODE (reg) == SIGN_EXTRACT || GET_CODE (reg) == ZERO_EXTRACT) + { + /* Must treat modification of just one hardware register of a multi-reg + value or just a byte field of a register exactly the same way that + mark_set_1 in flow.c does, i.e. anything except a paradoxical subreg + does not kill the entire register. */ + if (GET_CODE (reg) != SUBREG + || REG_SIZE (SUBREG_REG (reg)) > REG_SIZE (reg)) + subreg_p = 1; + + reg = SUBREG_REG (reg); + } + + if (GET_CODE (reg) != REG) + return; + + /* Global registers are always live, so the code below does not apply + to them. */ + + regno = REGNO (reg); + if (regno >= FIRST_PSEUDO_REGISTER || ! global_regs[regno]) + { + register int offset = regno / REGSET_ELT_BITS; + register REGSET_ELT_TYPE bit + = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS); + + if (death) + { + /* If we only set part of the register, then this set does not + kill it. */ + if (subreg_p) + return; + + /* Try killing this register. */ + if (regno < FIRST_PSEUDO_REGISTER) + { + int j = HARD_REGNO_NREGS (regno, GET_MODE (reg)); + while (--j >= 0) + { + offset = (regno + j) / REGSET_ELT_BITS; + bit = (REGSET_ELT_TYPE) 1 << ((regno + j) % REGSET_ELT_BITS); + + bb_live_regs[offset] &= ~bit; + bb_dead_regs[offset] |= bit; + } + } + else + { + bb_live_regs[offset] &= ~bit; + bb_dead_regs[offset] |= bit; + } + } + else + { + /* Make the register live again. */ + if (regno < FIRST_PSEUDO_REGISTER) + { + int j = HARD_REGNO_NREGS (regno, GET_MODE (reg)); + while (--j >= 0) + { + offset = (regno + j) / REGSET_ELT_BITS; + bit = (REGSET_ELT_TYPE) 1 << ((regno + j) % REGSET_ELT_BITS); + + bb_live_regs[offset] |= bit; + bb_dead_regs[offset] &= ~bit; + } + } + else + { + bb_live_regs[offset] |= bit; + bb_dead_regs[offset] &= ~bit; + } + } + } +} + +/* Macros and functions for keeping the priority queue sorted, and + dealing with queueing and unqueueing of instructions. */ + +#define SCHED_SORT(READY, NEW_READY, OLD_READY) \ + do { if ((NEW_READY) - (OLD_READY) == 1) \ + swap_sort (READY, NEW_READY); \ + else if ((NEW_READY) - (OLD_READY) > 1) \ + qsort (READY, NEW_READY, sizeof (rtx), rank_for_schedule); } \ + while (0) + +/* Returns a positive value if y is preferred; returns a negative value if + x is preferred. Should never return 0, since that will make the sort + unstable. */ + +static int +rank_for_schedule (x, y) + rtx *x, *y; +{ + rtx tmp = *y; + rtx tmp2 = *x; + rtx link; + int tmp_class, tmp2_class; + int value; + + /* Choose the instruction with the highest priority, if different. */ + if (value = INSN_PRIORITY (tmp) - INSN_PRIORITY (tmp2)) + return value; + + if (last_scheduled_insn) + { + /* Classify the instructions into three classes: + 1) Data dependent on last schedule insn. + 2) Anti/Output dependent on last scheduled insn. + 3) Independent of last scheduled insn, or has latency of one. + Choose the insn from the highest numbered class if different. */ + link = find_insn_list (tmp, LOG_LINKS (last_scheduled_insn)); + if (link == 0 || insn_cost (tmp, link, last_scheduled_insn) == 1) + tmp_class = 3; + else if (REG_NOTE_KIND (link) == 0) /* Data dependence. */ + tmp_class = 1; + else + tmp_class = 2; + + link = find_insn_list (tmp2, LOG_LINKS (last_scheduled_insn)); + if (link == 0 || insn_cost (tmp2, link, last_scheduled_insn) == 1) + tmp2_class = 3; + else if (REG_NOTE_KIND (link) == 0) /* Data dependence. */ + tmp2_class = 1; + else + tmp2_class = 2; + + if (value = tmp_class - tmp2_class) + return value; + } + + /* If insns are equally good, sort by INSN_LUID (original insn order), + so that we make the sort stable. This minimizes instruction movement, + thus minimizing sched's effect on debugging and cross-jumping. */ + return INSN_LUID (tmp) - INSN_LUID (tmp2); +} + +/* Resort the array A in which only element at index N may be out of order. */ + +__inline static void +swap_sort (a, n) + rtx *a; + int n; +{ + rtx insn = a[n-1]; + int i = n-2; + + while (i >= 0 && rank_for_schedule (a+i, &insn) >= 0) + { + a[i+1] = a[i]; + i -= 1; + } + a[i+1] = insn; +} + +static int max_priority; + +/* Add INSN to the insn queue so that it fires at least N_CYCLES + before the currently executing insn. */ + +__inline static void +queue_insn (insn, n_cycles) + rtx insn; + int n_cycles; +{ + int next_q = NEXT_Q_AFTER (q_ptr, n_cycles); + NEXT_INSN (insn) = insn_queue[next_q]; + insn_queue[next_q] = insn; + q_size += 1; +} + +/* Return nonzero if PAT is the pattern of an insn which makes a + register live. */ + +__inline static int +birthing_insn_p (pat) + rtx pat; +{ + int j; + + if (reload_completed == 1) + return 0; + + if (GET_CODE (pat) == SET + && GET_CODE (SET_DEST (pat)) == REG) + { + rtx dest = SET_DEST (pat); + int i = REGNO (dest); + int offset = i / REGSET_ELT_BITS; + REGSET_ELT_TYPE bit = (REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS); + + /* It would be more accurate to use refers_to_regno_p or + reg_mentioned_p to determine when the dest is not live before this + insn. */ + + if (bb_live_regs[offset] & bit) + return (reg_n_sets[i] == 1); + + return 0; + } + if (GET_CODE (pat) == PARALLEL) + { + for (j = 0; j < XVECLEN (pat, 0); j++) + if (birthing_insn_p (XVECEXP (pat, 0, j))) + return 1; + } + return 0; +} + +/* PREV is an insn that is ready to execute. Adjust its priority if that + will help shorten register lifetimes. */ + +__inline static void +adjust_priority (prev) + rtx prev; +{ + /* Trying to shorten register lives after reload has completed + is useless and wrong. It gives inaccurate schedules. */ + if (reload_completed == 0) + { + rtx note; + int n_deaths = 0; + + /* ??? This code has no effect, because REG_DEAD notes are removed + before we ever get here. */ + for (note = REG_NOTES (prev); note; note = XEXP (note, 1)) + if (REG_NOTE_KIND (note) == REG_DEAD) + n_deaths += 1; + + /* Defer scheduling insns which kill registers, since that + shortens register lives. Prefer scheduling insns which + make registers live for the same reason. */ + switch (n_deaths) + { + default: + INSN_PRIORITY (prev) >>= 3; + break; + case 3: + INSN_PRIORITY (prev) >>= 2; + break; + case 2: + case 1: + INSN_PRIORITY (prev) >>= 1; + break; + case 0: + if (birthing_insn_p (PATTERN (prev))) + { + int max = max_priority; + + if (max > INSN_PRIORITY (prev)) + INSN_PRIORITY (prev) = max; + } + break; + } + } +} + +/* INSN is the "currently executing insn". Launch each insn which was + waiting on INSN (in the backwards dataflow sense). READY is a + vector of insns which are ready to fire. N_READY is the number of + elements in READY. CLOCK is the current virtual cycle. */ + +static int +schedule_insn (insn, ready, n_ready, clock) + rtx insn; + rtx *ready; + int n_ready; + int clock; +{ + rtx link; + int new_ready = n_ready; + + if (MAX_BLOCKAGE > 1) + schedule_unit (insn_unit (insn), insn, clock); + + if (LOG_LINKS (insn) == 0) + return n_ready; + + /* This is used by the function adjust_priority above. */ + if (n_ready > 0) + max_priority = MAX (INSN_PRIORITY (ready[0]), INSN_PRIORITY (insn)); + else + max_priority = INSN_PRIORITY (insn); + + for (link = LOG_LINKS (insn); link != 0; link = XEXP (link, 1)) + { + rtx prev = XEXP (link, 0); + int cost = insn_cost (prev, link, insn); + + if ((INSN_REF_COUNT (prev) -= 1) != 0) + { + /* We satisfied one requirement to fire PREV. Record the earliest + time when PREV can fire. No need to do this if the cost is 1, + because PREV can fire no sooner than the next cycle. */ + if (cost > 1) + INSN_TICK (prev) = MAX (INSN_TICK (prev), clock + cost); + } + else + { + /* We satisfied the last requirement to fire PREV. Ensure that all + timing requirements are satisfied. */ + if (INSN_TICK (prev) - clock > cost) + cost = INSN_TICK (prev) - clock; + + /* Adjust the priority of PREV and either put it on the ready + list or queue it. */ + adjust_priority (prev); + if (cost <= 1) + ready[new_ready++] = prev; + else + queue_insn (prev, cost); + } + } + + return new_ready; +} + +/* Given N_READY insns in the ready list READY at time CLOCK, queue + those that are blocked due to function unit hazards and rearrange + the remaining ones to minimize subsequent function unit hazards. */ + +static int +schedule_select (ready, n_ready, clock, file) + rtx *ready; + int n_ready, clock; + FILE *file; +{ + int pri = INSN_PRIORITY (ready[0]); + int i, j, k, q, cost, best_cost, best_insn = 0, new_ready = n_ready; + rtx insn; + + /* Work down the ready list in groups of instructions with the same + priority value. Queue insns in the group that are blocked and + select among those that remain for the one with the largest + potential hazard. */ + for (i = 0; i < n_ready; i = j) + { + int opri = pri; + for (j = i + 1; j < n_ready; j++) + if ((pri = INSN_PRIORITY (ready[j])) != opri) + break; + + /* Queue insns in the group that are blocked. */ + for (k = i, q = 0; k < j; k++) + { + insn = ready[k]; + if ((cost = actual_hazard (insn_unit (insn), insn, clock, 0)) != 0) + { + q++; + ready[k] = 0; + queue_insn (insn, cost); + if (file) + fprintf (file, "\n;; blocking insn %d for %d cycles", + INSN_UID (insn), cost); + } + } + new_ready -= q; + + /* Check the next group if all insns were queued. */ + if (j - i - q == 0) + continue; + + /* If more than one remains, select the first one with the largest + potential hazard. */ + else if (j - i - q > 1) + { + best_cost = -1; + for (k = i; k < j; k++) + { + if ((insn = ready[k]) == 0) + continue; + if ((cost = potential_hazard (insn_unit (insn), insn, 0)) + > best_cost) + { + best_cost = cost; + best_insn = k; + } + } + } + /* We have found a suitable insn to schedule. */ + break; + } + + /* Move the best insn to be front of the ready list. */ + if (best_insn != 0) + { + if (file) + { + fprintf (file, ", now"); + for (i = 0; i < n_ready; i++) + if (ready[i]) + fprintf (file, " %d", INSN_UID (ready[i])); + fprintf (file, "\n;; insn %d has a greater potential hazard", + INSN_UID (ready[best_insn])); + } + for (i = best_insn; i > 0; i--) + { + insn = ready[i-1]; + ready[i-1] = ready[i]; + ready[i] = insn; + } + } + + /* Compact the ready list. */ + if (new_ready < n_ready) + for (i = j = 0; i < n_ready; i++) + if (ready[i]) + ready[j++] = ready[i]; + + return new_ready; +} + +/* Add a REG_DEAD note for REG to INSN, reusing a REG_DEAD note from the + dead_notes list. */ + +static void +create_reg_dead_note (reg, insn) + rtx reg, insn; +{ + rtx link, backlink; + + /* The number of registers killed after scheduling must be the same as the + number of registers killed before scheduling. The number of REG_DEAD + notes may not be conserved, i.e. two SImode hard register REG_DEAD notes + might become one DImode hard register REG_DEAD note, but the number of + registers killed will be conserved. + + We carefully remove REG_DEAD notes from the dead_notes list, so that + there will be none left at the end. If we run out early, then there + is a bug somewhere in flow, combine and/or sched. */ + + if (dead_notes == 0) + { +#if 1 + abort (); +#else + link = rtx_alloc (EXPR_LIST); + PUT_REG_NOTE_KIND (link, REG_DEAD); +#endif + } + else + { + /* Number of regs killed by REG. */ + int regs_killed = (REGNO (reg) >= FIRST_PSEUDO_REGISTER ? 1 + : HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg))); + /* Number of regs killed by REG_DEAD notes taken off the list. */ + int reg_note_regs; + + link = dead_notes; + reg_note_regs = (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1 + : HARD_REGNO_NREGS (REGNO (XEXP (link, 0)), + GET_MODE (XEXP (link, 0)))); + while (reg_note_regs < regs_killed) + { + link = XEXP (link, 1); + reg_note_regs += (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1 + : HARD_REGNO_NREGS (REGNO (XEXP (link, 0)), + GET_MODE (XEXP (link, 0)))); + } + dead_notes = XEXP (link, 1); + + /* If we took too many regs kills off, put the extra ones back. */ + while (reg_note_regs > regs_killed) + { + rtx temp_reg, temp_link; + + temp_reg = gen_rtx (REG, word_mode, 0); + temp_link = rtx_alloc (EXPR_LIST); + PUT_REG_NOTE_KIND (temp_link, REG_DEAD); + XEXP (temp_link, 0) = temp_reg; + XEXP (temp_link, 1) = dead_notes; + dead_notes = temp_link; + reg_note_regs--; + } + } + + XEXP (link, 0) = reg; + XEXP (link, 1) = REG_NOTES (insn); + REG_NOTES (insn) = link; +} + +/* Subroutine on attach_deaths_insn--handles the recursive search + through INSN. If SET_P is true, then x is being modified by the insn. */ + +static void +attach_deaths (x, insn, set_p) + rtx x; + rtx insn; + int set_p; +{ + register int i; + register int j; + register enum rtx_code code; + register char *fmt; + + if (x == 0) + return; + + code = GET_CODE (x); + + switch (code) + { + case CONST_INT: + case CONST_DOUBLE: + case LABEL_REF: + case SYMBOL_REF: + case CONST: + case CODE_LABEL: + case PC: + case CC0: + /* Get rid of the easy cases first. */ + return; + + case REG: + { + /* If the register dies in this insn, queue that note, and mark + this register as needing to die. */ + /* This code is very similar to mark_used_1 (if set_p is false) + and mark_set_1 (if set_p is true) in flow.c. */ + + register int regno = REGNO (x); + register int offset = regno / REGSET_ELT_BITS; + register REGSET_ELT_TYPE bit + = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS); + REGSET_ELT_TYPE all_needed = (old_live_regs[offset] & bit); + REGSET_ELT_TYPE some_needed = (old_live_regs[offset] & bit); + + if (set_p) + return; + + if (regno < FIRST_PSEUDO_REGISTER) + { + int n; + + n = HARD_REGNO_NREGS (regno, GET_MODE (x)); + while (--n > 0) + { + some_needed |= (old_live_regs[(regno + n) / REGSET_ELT_BITS] + & ((REGSET_ELT_TYPE) 1 + << ((regno + n) % REGSET_ELT_BITS))); + all_needed &= (old_live_regs[(regno + n) / REGSET_ELT_BITS] + & ((REGSET_ELT_TYPE) 1 + << ((regno + n) % REGSET_ELT_BITS))); + } + } + + /* If it wasn't live before we started, then add a REG_DEAD note. + We must check the previous lifetime info not the current info, + because we may have to execute this code several times, e.g. + once for a clobber (which doesn't add a note) and later + for a use (which does add a note). + + Always make the register live. We must do this even if it was + live before, because this may be an insn which sets and uses + the same register, in which case the register has already been + killed, so we must make it live again. + + Global registers are always live, and should never have a REG_DEAD + note added for them, so none of the code below applies to them. */ + + if (regno >= FIRST_PSEUDO_REGISTER || ! global_regs[regno]) + { + /* Never add REG_DEAD notes for the FRAME_POINTER_REGNUM or the + STACK_POINTER_REGNUM, since these are always considered to be + live. Similarly for ARG_POINTER_REGNUM if it is fixed. */ + if (regno != FRAME_POINTER_REGNUM +#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM + && ! (regno == ARG_POINTER_REGNUM && fixed_regs[regno]) +#endif + && regno != STACK_POINTER_REGNUM) + { + if (! all_needed && ! dead_or_set_p (insn, x)) + { + /* If none of the words in X is needed, make a REG_DEAD + note. Otherwise, we must make partial REG_DEAD + notes. */ + if (! some_needed) + create_reg_dead_note (x, insn); + else + { + int i; + + /* Don't make a REG_DEAD note for a part of a + register that is set in the insn. */ + for (i = HARD_REGNO_NREGS (regno, GET_MODE (x)) - 1; + i >= 0; i--) + if ((old_live_regs[(regno + i) / REGSET_ELT_BITS] + & ((REGSET_ELT_TYPE) 1 + << ((regno +i) % REGSET_ELT_BITS))) == 0 + && ! dead_or_set_regno_p (insn, regno + i)) + create_reg_dead_note (gen_rtx (REG, word_mode, + regno + i), + insn); + } + } + } + + if (regno < FIRST_PSEUDO_REGISTER) + { + int j = HARD_REGNO_NREGS (regno, GET_MODE (x)); + while (--j >= 0) + { + offset = (regno + j) / REGSET_ELT_BITS; + bit + = (REGSET_ELT_TYPE) 1 << ((regno + j) % REGSET_ELT_BITS); + + bb_dead_regs[offset] &= ~bit; + bb_live_regs[offset] |= bit; + } + } + else + { + bb_dead_regs[offset] &= ~bit; + bb_live_regs[offset] |= bit; + } + } + return; + } + + case MEM: + /* Handle tail-recursive case. */ + attach_deaths (XEXP (x, 0), insn, 0); + return; + + case SUBREG: + case STRICT_LOW_PART: + /* These two cases preserve the value of SET_P, so handle them + separately. */ + attach_deaths (XEXP (x, 0), insn, set_p); + return; + + case ZERO_EXTRACT: + case SIGN_EXTRACT: + /* This case preserves the value of SET_P for the first operand, but + clears it for the other two. */ + attach_deaths (XEXP (x, 0), insn, set_p); + attach_deaths (XEXP (x, 1), insn, 0); + attach_deaths (XEXP (x, 2), insn, 0); + return; + + default: + /* Other cases: walk the insn. */ + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + attach_deaths (XEXP (x, i), insn, 0); + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + attach_deaths (XVECEXP (x, i, j), insn, 0); + } + } +} + +/* After INSN has executed, add register death notes for each register + that is dead after INSN. */ + +static void +attach_deaths_insn (insn) + rtx insn; +{ + rtx x = PATTERN (insn); + register RTX_CODE code = GET_CODE (x); + + if (code == SET) + { + attach_deaths (SET_SRC (x), insn, 0); + + /* A register might die here even if it is the destination, e.g. + it is the target of a volatile read and is otherwise unused. + Hence we must always call attach_deaths for the SET_DEST. */ + attach_deaths (SET_DEST (x), insn, 1); + } + else if (code == PARALLEL) + { + register int i; + for (i = XVECLEN (x, 0) - 1; i >= 0; i--) + { + code = GET_CODE (XVECEXP (x, 0, i)); + if (code == SET) + { + attach_deaths (SET_SRC (XVECEXP (x, 0, i)), insn, 0); + + attach_deaths (SET_DEST (XVECEXP (x, 0, i)), insn, 1); + } + /* Flow does not add REG_DEAD notes to registers that die in + clobbers, so we can't either. */ + else if (code != CLOBBER) + attach_deaths (XVECEXP (x, 0, i), insn, 0); + } + } + /* Flow does not add REG_DEAD notes to registers that die in + clobbers, so we can't either. */ + else if (code != CLOBBER) + attach_deaths (x, insn, 0); +} + +/* Delete notes beginning with INSN and maybe put them in the chain + of notes ended by NOTE_LIST. + Returns the insn following the notes. */ + +static rtx +unlink_notes (insn, tail) + rtx insn, tail; +{ + rtx prev = PREV_INSN (insn); + + while (insn != tail && GET_CODE (insn) == NOTE) + { + rtx next = NEXT_INSN (insn); + /* Delete the note from its current position. */ + if (prev) + NEXT_INSN (prev) = next; + if (next) + PREV_INSN (next) = prev; + + if (write_symbols != NO_DEBUG && NOTE_LINE_NUMBER (insn) > 0) + /* Record line-number notes so they can be reused. */ + LINE_NOTE (insn) = insn; + else + { + /* Insert the note at the end of the notes list. */ + PREV_INSN (insn) = note_list; + if (note_list) + NEXT_INSN (note_list) = insn; + note_list = insn; + } + + insn = next; + } + return insn; +} + +/* Data structure for keeping track of register information + during that register's life. */ + +struct sometimes +{ + short offset; short bit; + short live_length; short calls_crossed; +}; + +/* Constructor for `sometimes' data structure. */ + +static int +new_sometimes_live (regs_sometimes_live, offset, bit, sometimes_max) + struct sometimes *regs_sometimes_live; + int offset, bit; + int sometimes_max; +{ + register struct sometimes *p; + register int regno = offset * REGSET_ELT_BITS + bit; + int i; + + /* There should never be a register greater than max_regno here. If there + is, it means that a define_split has created a new pseudo reg. This + is not allowed, since there will not be flow info available for any + new register, so catch the error here. */ + if (regno >= max_regno) + abort (); + + p = ®s_sometimes_live[sometimes_max]; + p->offset = offset; + p->bit = bit; + p->live_length = 0; + p->calls_crossed = 0; + sometimes_max++; + return sometimes_max; +} + +/* Count lengths of all regs we are currently tracking, + and find new registers no longer live. */ + +static void +finish_sometimes_live (regs_sometimes_live, sometimes_max) + struct sometimes *regs_sometimes_live; + int sometimes_max; +{ + int i; + + for (i = 0; i < sometimes_max; i++) + { + register struct sometimes *p = ®s_sometimes_live[i]; + int regno; + + regno = p->offset * REGSET_ELT_BITS + p->bit; + + sched_reg_live_length[regno] += p->live_length; + sched_reg_n_calls_crossed[regno] += p->calls_crossed; + } +} + +/* Use modified list scheduling to rearrange insns in basic block + B. FILE, if nonzero, is where we dump interesting output about + this pass. */ + +static void +schedule_block (b, file) + int b; + FILE *file; +{ + rtx insn, last; + rtx last_note = 0; + rtx *ready, link; + int i, j, n_ready = 0, new_ready, n_insns = 0; + int sched_n_insns = 0; + int clock; +#define NEED_NOTHING 0 +#define NEED_HEAD 1 +#define NEED_TAIL 2 + int new_needs; + + /* HEAD and TAIL delimit the region being scheduled. */ + rtx head = basic_block_head[b]; + rtx tail = basic_block_end[b]; + /* PREV_HEAD and NEXT_TAIL are the boundaries of the insns + being scheduled. When the insns have been ordered, + these insns delimit where the new insns are to be + spliced back into the insn chain. */ + rtx next_tail; + rtx prev_head; + + /* Keep life information accurate. */ + register struct sometimes *regs_sometimes_live; + int sometimes_max; + + if (file) + fprintf (file, ";;\t -- basic block number %d from %d to %d --\n", + b, INSN_UID (basic_block_head[b]), INSN_UID (basic_block_end[b])); + + i = max_reg_num (); + reg_last_uses = (rtx *) alloca (i * sizeof (rtx)); + bzero (reg_last_uses, i * sizeof (rtx)); + reg_last_sets = (rtx *) alloca (i * sizeof (rtx)); + bzero (reg_last_sets, i * sizeof (rtx)); + clear_units (); + + /* Remove certain insns at the beginning from scheduling, + by advancing HEAD. */ + + /* At the start of a function, before reload has run, don't delay getting + parameters from hard registers into pseudo registers. */ + if (reload_completed == 0 && b == 0) + { + while (head != tail + && GET_CODE (head) == NOTE + && NOTE_LINE_NUMBER (head) != NOTE_INSN_FUNCTION_BEG) + head = NEXT_INSN (head); + while (head != tail + && GET_CODE (head) == INSN + && GET_CODE (PATTERN (head)) == SET) + { + rtx src = SET_SRC (PATTERN (head)); + while (GET_CODE (src) == SUBREG + || GET_CODE (src) == SIGN_EXTEND + || GET_CODE (src) == ZERO_EXTEND + || GET_CODE (src) == SIGN_EXTRACT + || GET_CODE (src) == ZERO_EXTRACT) + src = XEXP (src, 0); + if (GET_CODE (src) != REG + || REGNO (src) >= FIRST_PSEUDO_REGISTER) + break; + /* Keep this insn from ever being scheduled. */ + INSN_REF_COUNT (head) = 1; + head = NEXT_INSN (head); + } + } + + /* Don't include any notes or labels at the beginning of the + basic block, or notes at the ends of basic blocks. */ + while (head != tail) + { + if (GET_CODE (head) == NOTE) + head = NEXT_INSN (head); + else if (GET_CODE (tail) == NOTE) + tail = PREV_INSN (tail); + else if (GET_CODE (head) == CODE_LABEL) + head = NEXT_INSN (head); + else break; + } + /* If the only insn left is a NOTE or a CODE_LABEL, then there is no need + to schedule this block. */ + if (head == tail + && (GET_CODE (head) == NOTE || GET_CODE (head) == CODE_LABEL)) + return; + +#if 0 + /* This short-cut doesn't work. It does not count call insns crossed by + registers in reg_sometimes_live. It does not mark these registers as + dead if they die in this block. It does not mark these registers live + (or create new reg_sometimes_live entries if necessary) if they are born + in this block. + + The easy solution is to just always schedule a block. This block only + has one insn, so this won't slow down this pass by much. */ + + if (head == tail) + return; +#endif + + /* Now HEAD through TAIL are the insns actually to be rearranged; + Let PREV_HEAD and NEXT_TAIL enclose them. */ + prev_head = PREV_INSN (head); + next_tail = NEXT_INSN (tail); + + /* Initialize basic block data structures. */ + dead_notes = 0; + pending_read_insns = 0; + pending_read_mems = 0; + pending_write_insns = 0; + pending_write_mems = 0; + pending_lists_length = 0; + last_pending_memory_flush = 0; + last_function_call = 0; + last_scheduled_insn = 0; + + LOG_LINKS (sched_before_next_call) = 0; + + n_insns += sched_analyze (head, tail); + if (n_insns == 0) + { + free_pending_lists (); + return; + } + + /* Allocate vector to hold insns to be rearranged (except those + insns which are controlled by an insn with SCHED_GROUP_P set). + All these insns are included between ORIG_HEAD and ORIG_TAIL, + as those variables ultimately are set up. */ + ready = (rtx *) alloca ((n_insns+1) * sizeof (rtx)); + + /* TAIL is now the last of the insns to be rearranged. + Put those insns into the READY vector. */ + insn = tail; + + /* For all branches, calls, uses, and cc0 setters, force them to remain + in order at the end of the block by adding dependencies and giving + the last a high priority. There may be notes present, and prev_head + may also be a note. + + Branches must obviously remain at the end. Calls should remain at the + end since moving them results in worse register allocation. Uses remain + at the end to ensure proper register allocation. cc0 setters remaim + at the end because they can't be moved away from their cc0 user. */ + last = 0; + while (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN + || (GET_CODE (insn) == INSN + && (GET_CODE (PATTERN (insn)) == USE +#ifdef HAVE_cc0 + || sets_cc0_p (PATTERN (insn)) +#endif + )) + || GET_CODE (insn) == NOTE) + { + if (GET_CODE (insn) != NOTE) + { + priority (insn); + if (last == 0) + { + ready[n_ready++] = insn; + INSN_PRIORITY (insn) = TAIL_PRIORITY - i; + INSN_REF_COUNT (insn) = 0; + } + else if (! find_insn_list (insn, LOG_LINKS (last))) + { + add_dependence (last, insn, REG_DEP_ANTI); + INSN_REF_COUNT (insn)++; + } + last = insn; + + /* Skip over insns that are part of a group. */ + while (SCHED_GROUP_P (insn)) + { + insn = prev_nonnote_insn (insn); + priority (insn); + } + } + + insn = PREV_INSN (insn); + /* Don't overrun the bounds of the basic block. */ + if (insn == prev_head) + break; + } + + /* Assign priorities to instructions. Also check whether they + are in priority order already. If so then I will be nonnegative. + We use this shortcut only before reloading. */ +#if 0 + i = reload_completed ? DONE_PRIORITY : MAX_PRIORITY; +#endif + + for (; insn != prev_head; insn = PREV_INSN (insn)) + { + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + priority (insn); + if (INSN_REF_COUNT (insn) == 0) + { + if (last == 0) + ready[n_ready++] = insn; + else + { + /* Make this dependent on the last of the instructions + that must remain in order at the end of the block. */ + add_dependence (last, insn, REG_DEP_ANTI); + INSN_REF_COUNT (insn) = 1; + } + } + if (SCHED_GROUP_P (insn)) + { + while (SCHED_GROUP_P (insn)) + { + insn = PREV_INSN (insn); + while (GET_CODE (insn) == NOTE) + insn = PREV_INSN (insn); + priority (insn); + } + continue; + } +#if 0 + if (i < 0) + continue; + if (INSN_PRIORITY (insn) < i) + i = INSN_PRIORITY (insn); + else if (INSN_PRIORITY (insn) > i) + i = DONE_PRIORITY; +#endif + } + } + +#if 0 + /* This short-cut doesn't work. It does not count call insns crossed by + registers in reg_sometimes_live. It does not mark these registers as + dead if they die in this block. It does not mark these registers live + (or create new reg_sometimes_live entries if necessary) if they are born + in this block. + + The easy solution is to just always schedule a block. These blocks tend + to be very short, so this doesn't slow down this pass by much. */ + + /* If existing order is good, don't bother to reorder. */ + if (i != DONE_PRIORITY) + { + if (file) + fprintf (file, ";; already scheduled\n"); + + if (reload_completed == 0) + { + for (i = 0; i < sometimes_max; i++) + regs_sometimes_live[i].live_length += n_insns; + + finish_sometimes_live (regs_sometimes_live, sometimes_max); + } + free_pending_lists (); + return; + } +#endif + + /* Scan all the insns to be scheduled, removing NOTE insns + and register death notes. + Line number NOTE insns end up in NOTE_LIST. + Register death notes end up in DEAD_NOTES. + + Recreate the register life information for the end of this basic + block. */ + + if (reload_completed == 0) + { + bcopy (basic_block_live_at_start[b], bb_live_regs, regset_bytes); + bzero (bb_dead_regs, regset_bytes); + + if (b == 0) + { + /* This is the first block in the function. There may be insns + before head that we can't schedule. We still need to examine + them though for accurate register lifetime analysis. */ + + /* We don't want to remove any REG_DEAD notes as the code below + does. */ + + for (insn = basic_block_head[b]; insn != head; + insn = NEXT_INSN (insn)) + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + /* See if the register gets born here. */ + /* We must check for registers being born before we check for + registers dying. It is possible for a register to be born + and die in the same insn, e.g. reading from a volatile + memory location into an otherwise unused register. Such + a register must be marked as dead after this insn. */ + if (GET_CODE (PATTERN (insn)) == SET + || GET_CODE (PATTERN (insn)) == CLOBBER) + sched_note_set (b, PATTERN (insn), 0); + else if (GET_CODE (PATTERN (insn)) == PARALLEL) + { + int j; + for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--) + if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET + || GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER) + sched_note_set (b, XVECEXP (PATTERN (insn), 0, j), 0); + + /* ??? This code is obsolete and should be deleted. It + is harmless though, so we will leave it in for now. */ + for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--) + if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == USE) + sched_note_set (b, XVECEXP (PATTERN (insn), 0, j), 0); + } + + for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) + { + if ((REG_NOTE_KIND (link) == REG_DEAD + || REG_NOTE_KIND (link) == REG_UNUSED) + /* Verify that the REG_NOTE has a legal value. */ + && GET_CODE (XEXP (link, 0)) == REG) + { + register int regno = REGNO (XEXP (link, 0)); + register int offset = regno / REGSET_ELT_BITS; + register REGSET_ELT_TYPE bit + = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS); + + if (regno < FIRST_PSEUDO_REGISTER) + { + int j = HARD_REGNO_NREGS (regno, + GET_MODE (XEXP (link, 0))); + while (--j >= 0) + { + offset = (regno + j) / REGSET_ELT_BITS; + bit = ((REGSET_ELT_TYPE) 1 + << ((regno + j) % REGSET_ELT_BITS)); + + bb_live_regs[offset] &= ~bit; + bb_dead_regs[offset] |= bit; + } + } + else + { + bb_live_regs[offset] &= ~bit; + bb_dead_regs[offset] |= bit; + } + } + } + } + } + } + + /* If debugging information is being produced, keep track of the line + number notes for each insn. */ + if (write_symbols != NO_DEBUG) + { + /* We must use the true line number for the first insn in the block + that was computed and saved at the start of this pass. We can't + use the current line number, because scheduling of the previous + block may have changed the current line number. */ + rtx line = line_note_head[b]; + + for (insn = basic_block_head[b]; + insn != next_tail; + insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0) + line = insn; + else + LINE_NOTE (insn) = line; + } + + for (insn = head; insn != next_tail; insn = NEXT_INSN (insn)) + { + rtx prev, next, link; + + /* Farm out notes. This is needed to keep the debugger from + getting completely deranged. */ + if (GET_CODE (insn) == NOTE) + { + prev = insn; + insn = unlink_notes (insn, next_tail); + if (prev == tail) + abort (); + if (prev == head) + abort (); + if (insn == next_tail) + abort (); + } + + if (reload_completed == 0 + && GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + /* See if the register gets born here. */ + /* We must check for registers being born before we check for + registers dying. It is possible for a register to be born and + die in the same insn, e.g. reading from a volatile memory + location into an otherwise unused register. Such a register + must be marked as dead after this insn. */ + if (GET_CODE (PATTERN (insn)) == SET + || GET_CODE (PATTERN (insn)) == CLOBBER) + sched_note_set (b, PATTERN (insn), 0); + else if (GET_CODE (PATTERN (insn)) == PARALLEL) + { + int j; + for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--) + if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET + || GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER) + sched_note_set (b, XVECEXP (PATTERN (insn), 0, j), 0); + + /* ??? This code is obsolete and should be deleted. It + is harmless though, so we will leave it in for now. */ + for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--) + if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == USE) + sched_note_set (b, XVECEXP (PATTERN (insn), 0, j), 0); + } + + /* Need to know what registers this insn kills. */ + for (prev = 0, link = REG_NOTES (insn); link; link = next) + { + int regno; + + next = XEXP (link, 1); + if ((REG_NOTE_KIND (link) == REG_DEAD + || REG_NOTE_KIND (link) == REG_UNUSED) + /* Verify that the REG_NOTE has a legal value. */ + && GET_CODE (XEXP (link, 0)) == REG) + { + register int regno = REGNO (XEXP (link, 0)); + register int offset = regno / REGSET_ELT_BITS; + register REGSET_ELT_TYPE bit + = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS); + + /* Only unlink REG_DEAD notes; leave REG_UNUSED notes + alone. */ + if (REG_NOTE_KIND (link) == REG_DEAD) + { + if (prev) + XEXP (prev, 1) = next; + else + REG_NOTES (insn) = next; + XEXP (link, 1) = dead_notes; + dead_notes = link; + } + else + prev = link; + + if (regno < FIRST_PSEUDO_REGISTER) + { + int j = HARD_REGNO_NREGS (regno, + GET_MODE (XEXP (link, 0))); + while (--j >= 0) + { + offset = (regno + j) / REGSET_ELT_BITS; + bit = ((REGSET_ELT_TYPE) 1 + << ((regno + j) % REGSET_ELT_BITS)); + + bb_live_regs[offset] &= ~bit; + bb_dead_regs[offset] |= bit; + } + } + else + { + bb_live_regs[offset] &= ~bit; + bb_dead_regs[offset] |= bit; + } + } + else + prev = link; + } + } + } + + if (reload_completed == 0) + { + /* Keep track of register lives. */ + old_live_regs = (regset) alloca (regset_bytes); + regs_sometimes_live + = (struct sometimes *) alloca (max_regno * sizeof (struct sometimes)); + sometimes_max = 0; + + /* Start with registers live at end. */ + for (j = 0; j < regset_size; j++) + { + REGSET_ELT_TYPE live = bb_live_regs[j]; + old_live_regs[j] = live; + if (live) + { + register REGSET_ELT_TYPE bit; + for (bit = 0; bit < REGSET_ELT_BITS; bit++) + if (live & ((REGSET_ELT_TYPE) 1 << bit)) + sometimes_max = new_sometimes_live (regs_sometimes_live, j, + bit, sometimes_max); + } + } + } + + SCHED_SORT (ready, n_ready, 1); + + if (file) + { + fprintf (file, ";; ready list initially:\n;; "); + for (i = 0; i < n_ready; i++) + fprintf (file, "%d ", INSN_UID (ready[i])); + fprintf (file, "\n\n"); + + for (insn = head; insn != next_tail; insn = NEXT_INSN (insn)) + if (INSN_PRIORITY (insn) > 0) + fprintf (file, ";; insn[%4d]: priority = %4d, ref_count = %4d\n", + INSN_UID (insn), INSN_PRIORITY (insn), + INSN_REF_COUNT (insn)); + } + + /* Now HEAD and TAIL are going to become disconnected + entirely from the insn chain. */ + tail = 0; + + /* Q_SIZE will always be zero here. */ + q_ptr = 0; clock = 0; + bzero (insn_queue, sizeof (insn_queue)); + + /* Now, perform list scheduling. */ + + /* Where we start inserting insns is after TAIL. */ + last = next_tail; + + new_needs = (NEXT_INSN (prev_head) == basic_block_head[b] + ? NEED_HEAD : NEED_NOTHING); + if (PREV_INSN (next_tail) == basic_block_end[b]) + new_needs |= NEED_TAIL; + + new_ready = n_ready; + while (sched_n_insns < n_insns) + { + q_ptr = NEXT_Q (q_ptr); clock++; + + /* Add all pending insns that can be scheduled without stalls to the + ready list. */ + for (insn = insn_queue[q_ptr]; insn; insn = NEXT_INSN (insn)) + { + if (file) + fprintf (file, ";; launching %d before %d with no stalls at T-%d\n", + INSN_UID (insn), INSN_UID (last), clock); + ready[new_ready++] = insn; + q_size -= 1; + } + insn_queue[q_ptr] = 0; + + /* If there are no ready insns, stall until one is ready and add all + of the pending insns at that point to the ready list. */ + if (new_ready == 0) + { + register int stalls; + + for (stalls = 1; stalls < INSN_QUEUE_SIZE; stalls++) + if (insn = insn_queue[NEXT_Q_AFTER (q_ptr, stalls)]) + { + for (; insn; insn = NEXT_INSN (insn)) + { + if (file) + fprintf (file, ";; launching %d before %d with %d stalls at T-%d\n", + INSN_UID (insn), INSN_UID (last), stalls, clock); + ready[new_ready++] = insn; + q_size -= 1; + } + insn_queue[NEXT_Q_AFTER (q_ptr, stalls)] = 0; + break; + } + + q_ptr = NEXT_Q_AFTER (q_ptr, stalls); clock += stalls; + } + + /* There should be some instructions waiting to fire. */ + if (new_ready == 0) + abort (); + + if (file) + { + fprintf (file, ";; ready list at T-%d:", clock); + for (i = 0; i < new_ready; i++) + fprintf (file, " %d (%x)", + INSN_UID (ready[i]), INSN_PRIORITY (ready[i])); + } + + /* Sort the ready list and choose the best insn to schedule. Select + which insn should issue in this cycle and queue those that are + blocked by function unit hazards. + + N_READY holds the number of items that were scheduled the last time, + minus the one instruction scheduled on the last loop iteration; it + is not modified for any other reason in this loop. */ + + SCHED_SORT (ready, new_ready, n_ready); + if (MAX_BLOCKAGE > 1) + { + new_ready = schedule_select (ready, new_ready, clock, file); + if (new_ready == 0) + { + if (file) + fprintf (file, "\n"); + /* We must set n_ready here, to ensure that sorting always + occurs when we come back to the SCHED_SORT line above. */ + n_ready = 0; + continue; + } + } + n_ready = new_ready; + last_scheduled_insn = insn = ready[0]; + + /* The first insn scheduled becomes the new tail. */ + if (tail == 0) + tail = insn; + + if (file) + { + fprintf (file, ", now"); + for (i = 0; i < n_ready; i++) + fprintf (file, " %d", INSN_UID (ready[i])); + fprintf (file, "\n"); + } + + if (DONE_PRIORITY_P (insn)) + abort (); + + if (reload_completed == 0) + { + /* Process this insn, and each insn linked to this one which must + be immediately output after this insn. */ + do + { + /* First we kill registers set by this insn, and then we + make registers used by this insn live. This is the opposite + order used above because we are traversing the instructions + backwards. */ + + /* Strictly speaking, we should scan REG_UNUSED notes and make + every register mentioned there live, however, we will just + kill them again immediately below, so there doesn't seem to + be any reason why we bother to do this. */ + + /* See if this is the last notice we must take of a register. */ + if (GET_CODE (PATTERN (insn)) == SET + || GET_CODE (PATTERN (insn)) == CLOBBER) + sched_note_set (b, PATTERN (insn), 1); + else if (GET_CODE (PATTERN (insn)) == PARALLEL) + { + int j; + for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--) + if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET + || GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER) + sched_note_set (b, XVECEXP (PATTERN (insn), 0, j), 1); + } + + /* This code keeps life analysis information up to date. */ + if (GET_CODE (insn) == CALL_INSN) + { + register struct sometimes *p; + + /* A call kills all call used and global registers, except + for those mentioned in the call pattern which will be + made live again later. */ + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (call_used_regs[i] || global_regs[i]) + { + register int offset = i / REGSET_ELT_BITS; + register REGSET_ELT_TYPE bit + = (REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS); + + bb_live_regs[offset] &= ~bit; + bb_dead_regs[offset] |= bit; + } + + /* Regs live at the time of a call instruction must not + go in a register clobbered by calls. Record this for + all regs now live. Note that insns which are born or + die in a call do not cross a call, so this must be done + after the killings (above) and before the births + (below). */ + p = regs_sometimes_live; + for (i = 0; i < sometimes_max; i++, p++) + if (bb_live_regs[p->offset] + & ((REGSET_ELT_TYPE) 1 << p->bit)) + p->calls_crossed += 1; + } + + /* Make every register used live, and add REG_DEAD notes for + registers which were not live before we started. */ + attach_deaths_insn (insn); + + /* Find registers now made live by that instruction. */ + for (i = 0; i < regset_size; i++) + { + REGSET_ELT_TYPE diff = bb_live_regs[i] & ~old_live_regs[i]; + if (diff) + { + register int bit; + old_live_regs[i] |= diff; + for (bit = 0; bit < REGSET_ELT_BITS; bit++) + if (diff & ((REGSET_ELT_TYPE) 1 << bit)) + sometimes_max + = new_sometimes_live (regs_sometimes_live, i, bit, + sometimes_max); + } + } + + /* Count lengths of all regs we are worrying about now, + and handle registers no longer live. */ + + for (i = 0; i < sometimes_max; i++) + { + register struct sometimes *p = ®s_sometimes_live[i]; + int regno = p->offset*REGSET_ELT_BITS + p->bit; + + p->live_length += 1; + + if ((bb_live_regs[p->offset] + & ((REGSET_ELT_TYPE) 1 << p->bit)) == 0) + { + /* This is the end of one of this register's lifetime + segments. Save the lifetime info collected so far, + and clear its bit in the old_live_regs entry. */ + sched_reg_live_length[regno] += p->live_length; + sched_reg_n_calls_crossed[regno] += p->calls_crossed; + old_live_regs[p->offset] + &= ~((REGSET_ELT_TYPE) 1 << p->bit); + + /* Delete the reg_sometimes_live entry for this reg by + copying the last entry over top of it. */ + *p = regs_sometimes_live[--sometimes_max]; + /* ...and decrement i so that this newly copied entry + will be processed. */ + i--; + } + } + + link = insn; + insn = PREV_INSN (insn); + } + while (SCHED_GROUP_P (link)); + + /* Set INSN back to the insn we are scheduling now. */ + insn = ready[0]; + } + + /* Schedule INSN. Remove it from the ready list. */ + ready += 1; + n_ready -= 1; + + sched_n_insns += 1; + NEXT_INSN (insn) = last; + PREV_INSN (last) = insn; + last = insn; + + /* Everything that precedes INSN now either becomes "ready", if + it can execute immediately before INSN, or "pending", if + there must be a delay. Give INSN high enough priority that + at least one (maybe more) reg-killing insns can be launched + ahead of all others. Mark INSN as scheduled by changing its + priority to -1. */ + INSN_PRIORITY (insn) = LAUNCH_PRIORITY; + new_ready = schedule_insn (insn, ready, n_ready, clock); + INSN_PRIORITY (insn) = DONE_PRIORITY; + + /* Schedule all prior insns that must not be moved. */ + if (SCHED_GROUP_P (insn)) + { + /* Disable these insns from being launched. */ + link = insn; + while (SCHED_GROUP_P (link)) + { + /* Disable these insns from being launched by anybody. */ + link = PREV_INSN (link); + INSN_REF_COUNT (link) = 0; + } + + /* None of these insns can move forward into delay slots. */ + while (SCHED_GROUP_P (insn)) + { + insn = PREV_INSN (insn); + new_ready = schedule_insn (insn, ready, new_ready, clock); + INSN_PRIORITY (insn) = DONE_PRIORITY; + + sched_n_insns += 1; + NEXT_INSN (insn) = last; + PREV_INSN (last) = insn; + last = insn; + } + } + } + if (q_size != 0) + abort (); + + if (reload_completed == 0) + finish_sometimes_live (regs_sometimes_live, sometimes_max); + + /* HEAD is now the first insn in the chain of insns that + been scheduled by the loop above. + TAIL is the last of those insns. */ + head = insn; + + /* NOTE_LIST is the end of a chain of notes previously found + among the insns. Insert them at the beginning of the insns. */ + if (note_list != 0) + { + rtx note_head = note_list; + while (PREV_INSN (note_head)) + note_head = PREV_INSN (note_head); + + PREV_INSN (head) = note_list; + NEXT_INSN (note_list) = head; + head = note_head; + } + + /* There should be no REG_DEAD notes leftover at the end. + In practice, this can occur as the result of bugs in flow, combine.c, + and/or sched.c. The values of the REG_DEAD notes remaining are + meaningless, because dead_notes is just used as a free list. */ +#if 1 + if (dead_notes != 0) + abort (); +#endif + + if (new_needs & NEED_HEAD) + basic_block_head[b] = head; + PREV_INSN (head) = prev_head; + NEXT_INSN (prev_head) = head; + + if (new_needs & NEED_TAIL) + basic_block_end[b] = tail; + NEXT_INSN (tail) = next_tail; + PREV_INSN (next_tail) = tail; + + /* Restore the line-number notes of each insn. */ + if (write_symbols != NO_DEBUG) + { + rtx line, note, prev, new; + int notes = 0; + + head = basic_block_head[b]; + next_tail = NEXT_INSN (basic_block_end[b]); + + /* Determine the current line-number. We want to know the current + line number of the first insn of the block here, in case it is + different from the true line number that was saved earlier. If + different, then we need a line number note before the first insn + of this block. If it happens to be the same, then we don't want to + emit another line number note here. */ + for (line = head; line; line = PREV_INSN (line)) + if (GET_CODE (line) == NOTE && NOTE_LINE_NUMBER (line) > 0) + break; + + /* Walk the insns keeping track of the current line-number and inserting + the line-number notes as needed. */ + for (insn = head; insn != next_tail; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0) + line = insn; + else if (! (GET_CODE (insn) == NOTE + && NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED) + && (note = LINE_NOTE (insn)) != 0 + && note != line + && (line == 0 + || NOTE_LINE_NUMBER (note) != NOTE_LINE_NUMBER (line) + || NOTE_SOURCE_FILE (note) != NOTE_SOURCE_FILE (line))) + { + line = note; + prev = PREV_INSN (insn); + if (LINE_NOTE (note)) + { + /* Re-use the original line-number note. */ + LINE_NOTE (note) = 0; + PREV_INSN (note) = prev; + NEXT_INSN (prev) = note; + PREV_INSN (insn) = note; + NEXT_INSN (note) = insn; + } + else + { + notes++; + new = emit_note_after (NOTE_LINE_NUMBER (note), prev); + NOTE_SOURCE_FILE (new) = NOTE_SOURCE_FILE (note); + } + } + if (file && notes) + fprintf (file, ";; added %d line-number notes\n", notes); + } + + if (file) + { + fprintf (file, ";; total time = %d\n;; new basic block head = %d\n;; new basic block end = %d\n\n", + clock, INSN_UID (basic_block_head[b]), INSN_UID (basic_block_end[b])); + } + + /* Yow! We're done! */ + free_pending_lists (); + + return; +} + +/* Subroutine of split_hard_reg_notes. Searches X for any reference to + REGNO, returning the rtx of the reference found if any. Otherwise, + returns 0. */ + +rtx +regno_use_in (regno, x) + int regno; + rtx x; +{ + register char *fmt; + int i, j; + rtx tem; + + if (GET_CODE (x) == REG && REGNO (x) == regno) + return x; + + fmt = GET_RTX_FORMAT (GET_CODE (x)); + for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + if (tem = regno_use_in (regno, XEXP (x, i))) + return tem; + } + else if (fmt[i] == 'E') + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + if (tem = regno_use_in (regno , XVECEXP (x, i, j))) + return tem; + } + + return 0; +} + +/* Subroutine of update_flow_info. Determines whether any new REG_NOTEs are + needed for the hard register mentioned in the note. This can happen + if the reference to the hard register in the original insn was split into + several smaller hard register references in the split insns. */ + +static void +split_hard_reg_notes (note, first, last, orig_insn) + rtx note, first, last, orig_insn; +{ + rtx reg, temp, link; + int n_regs, i, new_reg; + rtx insn; + + /* Assume that this is a REG_DEAD note. */ + if (REG_NOTE_KIND (note) != REG_DEAD) + abort (); + + reg = XEXP (note, 0); + + n_regs = HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg)); + + for (i = 0; i < n_regs; i++) + { + new_reg = REGNO (reg) + i; + + /* Check for references to new_reg in the split insns. */ + for (insn = last; ; insn = PREV_INSN (insn)) + { + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && (temp = regno_use_in (new_reg, PATTERN (insn)))) + { + /* Create a new reg dead note here. */ + link = rtx_alloc (EXPR_LIST); + PUT_REG_NOTE_KIND (link, REG_DEAD); + XEXP (link, 0) = temp; + XEXP (link, 1) = REG_NOTES (insn); + REG_NOTES (insn) = link; + + /* If killed multiple registers here, then add in the excess. */ + i += HARD_REGNO_NREGS (REGNO (temp), GET_MODE (temp)) - 1; + + break; + } + /* It isn't mentioned anywhere, so no new reg note is needed for + this register. */ + if (insn == first) + break; + } + } +} + +/* Subroutine of update_flow_info. Determines whether a SET or CLOBBER in an + insn created by splitting needs a REG_DEAD or REG_UNUSED note added. */ + +static void +new_insn_dead_notes (pat, insn, last, orig_insn) + rtx pat, insn, last, orig_insn; +{ + rtx dest, tem, set; + + /* PAT is either a CLOBBER or a SET here. */ + dest = XEXP (pat, 0); + + while (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG + || GET_CODE (dest) == STRICT_LOW_PART + || GET_CODE (dest) == SIGN_EXTRACT) + dest = XEXP (dest, 0); + + if (GET_CODE (dest) == REG) + { + for (tem = last; tem != insn; tem = PREV_INSN (tem)) + { + if (GET_RTX_CLASS (GET_CODE (tem)) == 'i' + && reg_overlap_mentioned_p (dest, PATTERN (tem)) + && (set = single_set (tem))) + { + rtx tem_dest = SET_DEST (set); + + while (GET_CODE (tem_dest) == ZERO_EXTRACT + || GET_CODE (tem_dest) == SUBREG + || GET_CODE (tem_dest) == STRICT_LOW_PART + || GET_CODE (tem_dest) == SIGN_EXTRACT) + tem_dest = XEXP (tem_dest, 0); + + if (tem_dest != dest) + { + /* Use the same scheme as combine.c, don't put both REG_DEAD + and REG_UNUSED notes on the same insn. */ + if (! find_regno_note (tem, REG_UNUSED, REGNO (dest)) + && ! find_regno_note (tem, REG_DEAD, REGNO (dest))) + { + rtx note = rtx_alloc (EXPR_LIST); + PUT_REG_NOTE_KIND (note, REG_DEAD); + XEXP (note, 0) = dest; + XEXP (note, 1) = REG_NOTES (tem); + REG_NOTES (tem) = note; + } + /* The reg only dies in one insn, the last one that uses + it. */ + break; + } + else if (reg_overlap_mentioned_p (dest, SET_SRC (set))) + /* We found an instruction that both uses the register, + and sets it, so no new REG_NOTE is needed for this set. */ + break; + } + } + /* If this is a set, it must die somewhere, unless it is the dest of + the original insn, and hence is live after the original insn. Abort + if it isn't supposed to be live after the original insn. + + If this is a clobber, then just add a REG_UNUSED note. */ + if (tem == insn) + { + int live_after_orig_insn = 0; + rtx pattern = PATTERN (orig_insn); + int i; + + if (GET_CODE (pat) == CLOBBER) + { + rtx note = rtx_alloc (EXPR_LIST); + PUT_REG_NOTE_KIND (note, REG_UNUSED); + XEXP (note, 0) = dest; + XEXP (note, 1) = REG_NOTES (insn); + REG_NOTES (insn) = note; + return; + } + + /* The original insn could have multiple sets, so search the + insn for all sets. */ + if (GET_CODE (pattern) == SET) + { + if (reg_overlap_mentioned_p (dest, SET_DEST (pattern))) + live_after_orig_insn = 1; + } + else if (GET_CODE (pattern) == PARALLEL) + { + for (i = 0; i < XVECLEN (pattern, 0); i++) + if (GET_CODE (XVECEXP (pattern, 0, i)) == SET + && reg_overlap_mentioned_p (dest, + SET_DEST (XVECEXP (pattern, + 0, i)))) + live_after_orig_insn = 1; + } + + if (! live_after_orig_insn) + abort (); + } + } +} + +/* Subroutine of update_flow_info. Update the value of reg_n_sets for all + registers modified by X. INC is -1 if the containing insn is being deleted, + and is 1 if the containing insn is a newly generated insn. */ + +static void +update_n_sets (x, inc) + rtx x; + int inc; +{ + rtx dest = SET_DEST (x); + + while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SUBREG + || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT) + dest = SUBREG_REG (dest); + + if (GET_CODE (dest) == REG) + { + int regno = REGNO (dest); + + if (regno < FIRST_PSEUDO_REGISTER) + { + register int i; + int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (dest)); + + for (i = regno; i < endregno; i++) + reg_n_sets[i] += inc; + } + else + reg_n_sets[regno] += inc; + } +} + +/* Updates all flow-analysis related quantities (including REG_NOTES) for + the insns from FIRST to LAST inclusive that were created by splitting + ORIG_INSN. NOTES are the original REG_NOTES. */ + +static void +update_flow_info (notes, first, last, orig_insn) + rtx notes; + rtx first, last; + rtx orig_insn; +{ + rtx insn, note; + rtx next; + rtx orig_dest, temp; + rtx set; + + /* Get and save the destination set by the original insn. */ + + orig_dest = single_set (orig_insn); + if (orig_dest) + orig_dest = SET_DEST (orig_dest); + + /* Move REG_NOTES from the original insn to where they now belong. */ + + for (note = notes; note; note = next) + { + next = XEXP (note, 1); + switch (REG_NOTE_KIND (note)) + { + case REG_DEAD: + case REG_UNUSED: + /* Move these notes from the original insn to the last new insn where + the register is now set. */ + + for (insn = last; ; insn = PREV_INSN (insn)) + { + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && reg_mentioned_p (XEXP (note, 0), PATTERN (insn))) + { + /* If this note refers to a multiple word hard register, it + may have been split into several smaller hard register + references, so handle it specially. */ + temp = XEXP (note, 0); + if (REG_NOTE_KIND (note) == REG_DEAD + && GET_CODE (temp) == REG + && REGNO (temp) < FIRST_PSEUDO_REGISTER + && HARD_REGNO_NREGS (REGNO (temp), GET_MODE (temp)) > 1) + split_hard_reg_notes (note, first, last, orig_insn); + else + { + XEXP (note, 1) = REG_NOTES (insn); + REG_NOTES (insn) = note; + } + + /* Sometimes need to convert REG_UNUSED notes to REG_DEAD + notes. */ + /* ??? This won't handle multiple word registers correctly, + but should be good enough for now. */ + if (REG_NOTE_KIND (note) == REG_UNUSED + && ! dead_or_set_p (insn, XEXP (note, 0))) + PUT_REG_NOTE_KIND (note, REG_DEAD); + + /* The reg only dies in one insn, the last one that uses + it. */ + break; + } + /* It must die somewhere, fail it we couldn't find where it died. + + If this is a REG_UNUSED note, then it must be a temporary + register that was not needed by this instantiation of the + pattern, so we can safely ignore it. */ + if (insn == first) + { + if (REG_NOTE_KIND (note) != REG_UNUSED) + abort (); + + break; + } + } + break; + + case REG_WAS_0: + /* This note applies to the dest of the original insn. Find the + first new insn that now has the same dest, and move the note + there. */ + + if (! orig_dest) + abort (); + + for (insn = first; ; insn = NEXT_INSN (insn)) + { + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && (temp = single_set (insn)) + && rtx_equal_p (SET_DEST (temp), orig_dest)) + { + XEXP (note, 1) = REG_NOTES (insn); + REG_NOTES (insn) = note; + /* The reg is only zero before one insn, the first that + uses it. */ + break; + } + /* It must be set somewhere, fail if we couldn't find where it + was set. */ + if (insn == last) + abort (); + } + break; + + case REG_EQUAL: + case REG_EQUIV: + /* A REG_EQUIV or REG_EQUAL note on an insn with more than one + set is meaningless. Just drop the note. */ + if (! orig_dest) + break; + + case REG_NO_CONFLICT: + /* These notes apply to the dest of the original insn. Find the last + new insn that now has the same dest, and move the note there. */ + + if (! orig_dest) + abort (); + + for (insn = last; ; insn = PREV_INSN (insn)) + { + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && (temp = single_set (insn)) + && rtx_equal_p (SET_DEST (temp), orig_dest)) + { + XEXP (note, 1) = REG_NOTES (insn); + REG_NOTES (insn) = note; + /* Only put this note on one of the new insns. */ + break; + } + + /* The original dest must still be set someplace. Abort if we + couldn't find it. */ + if (insn == first) + abort (); + } + break; + + case REG_LIBCALL: + /* Move a REG_LIBCALL note to the first insn created, and update + the corresponding REG_RETVAL note. */ + XEXP (note, 1) = REG_NOTES (first); + REG_NOTES (first) = note; + + insn = XEXP (note, 0); + note = find_reg_note (insn, REG_RETVAL, NULL_RTX); + if (note) + XEXP (note, 0) = first; + break; + + case REG_RETVAL: + /* Move a REG_RETVAL note to the last insn created, and update + the corresponding REG_LIBCALL note. */ + XEXP (note, 1) = REG_NOTES (last); + REG_NOTES (last) = note; + + insn = XEXP (note, 0); + note = find_reg_note (insn, REG_LIBCALL, NULL_RTX); + if (note) + XEXP (note, 0) = last; + break; + + case REG_NONNEG: + /* This should be moved to whichever instruction is a JUMP_INSN. */ + + for (insn = last; ; insn = PREV_INSN (insn)) + { + if (GET_CODE (insn) == JUMP_INSN) + { + XEXP (note, 1) = REG_NOTES (insn); + REG_NOTES (insn) = note; + /* Only put this note on one of the new insns. */ + break; + } + /* Fail if we couldn't find a JUMP_INSN. */ + if (insn == first) + abort (); + } + break; + + case REG_INC: + /* This should be moved to whichever instruction now has the + increment operation. */ + abort (); + + case REG_LABEL: + /* Should be moved to the new insn(s) which use the label. */ + for (insn = first; insn != NEXT_INSN (last); insn = NEXT_INSN (insn)) + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && reg_mentioned_p (XEXP (note, 0), PATTERN (insn))) + REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_LABEL, + XEXP (note, 0), REG_NOTES (insn)); + break; + + case REG_CC_SETTER: + case REG_CC_USER: + /* These two notes will never appear until after reorg, so we don't + have to handle them here. */ + default: + abort (); + } + } + + /* Each new insn created, except the last, has a new set. If the destination + is a register, then this reg is now live across several insns, whereas + previously the dest reg was born and died within the same insn. To + reflect this, we now need a REG_DEAD note on the insn where this + dest reg dies. + + Similarly, the new insns may have clobbers that need REG_UNUSED notes. */ + + for (insn = first; insn != last; insn = NEXT_INSN (insn)) + { + rtx pat; + int i; + + pat = PATTERN (insn); + if (GET_CODE (pat) == SET || GET_CODE (pat) == CLOBBER) + new_insn_dead_notes (pat, insn, last, orig_insn); + else if (GET_CODE (pat) == PARALLEL) + { + for (i = 0; i < XVECLEN (pat, 0); i++) + if (GET_CODE (XVECEXP (pat, 0, i)) == SET + || GET_CODE (XVECEXP (pat, 0, i)) == CLOBBER) + new_insn_dead_notes (XVECEXP (pat, 0, i), insn, last, orig_insn); + } + } + + /* If any insn, except the last, uses the register set by the last insn, + then we need a new REG_DEAD note on that insn. In this case, there + would not have been a REG_DEAD note for this register in the original + insn because it was used and set within one insn. + + There is no new REG_DEAD note needed if the last insn uses the register + that it is setting. */ + + set = single_set (last); + if (set) + { + rtx dest = SET_DEST (set); + + while (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG + || GET_CODE (dest) == STRICT_LOW_PART + || GET_CODE (dest) == SIGN_EXTRACT) + dest = XEXP (dest, 0); + + if (GET_CODE (dest) == REG + && ! reg_overlap_mentioned_p (dest, SET_SRC (set))) + { + for (insn = PREV_INSN (last); ; insn = PREV_INSN (insn)) + { + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && reg_mentioned_p (dest, PATTERN (insn)) + && (set = single_set (insn))) + { + rtx insn_dest = SET_DEST (set); + + while (GET_CODE (insn_dest) == ZERO_EXTRACT + || GET_CODE (insn_dest) == SUBREG + || GET_CODE (insn_dest) == STRICT_LOW_PART + || GET_CODE (insn_dest) == SIGN_EXTRACT) + insn_dest = XEXP (insn_dest, 0); + + if (insn_dest != dest) + { + note = rtx_alloc (EXPR_LIST); + PUT_REG_NOTE_KIND (note, REG_DEAD); + XEXP (note, 0) = dest; + XEXP (note, 1) = REG_NOTES (insn); + REG_NOTES (insn) = note; + /* The reg only dies in one insn, the last one + that uses it. */ + break; + } + } + if (insn == first) + break; + } + } + } + + /* If the original dest is modifying a multiple register target, and the + original instruction was split such that the original dest is now set + by two or more SUBREG sets, then the split insns no longer kill the + destination of the original insn. + + In this case, if there exists an instruction in the same basic block, + before the split insn, which uses the original dest, and this use is + killed by the original insn, then we must remove the REG_DEAD note on + this insn, because it is now superfluous. + + This does not apply when a hard register gets split, because the code + knows how to handle overlapping hard registers properly. */ + if (orig_dest && GET_CODE (orig_dest) == REG) + { + int found_orig_dest = 0; + int found_split_dest = 0; + + for (insn = first; ; insn = NEXT_INSN (insn)) + { + set = single_set (insn); + if (set) + { + if (GET_CODE (SET_DEST (set)) == REG + && REGNO (SET_DEST (set)) == REGNO (orig_dest)) + { + found_orig_dest = 1; + break; + } + else if (GET_CODE (SET_DEST (set)) == SUBREG + && SUBREG_REG (SET_DEST (set)) == orig_dest) + { + found_split_dest = 1; + break; + } + } + + if (insn == last) + break; + } + + if (found_split_dest) + { + /* Search backwards from FIRST, looking for the first insn that uses + the original dest. Stop if we pass a CODE_LABEL or a JUMP_INSN. + If we find an insn, and it has a REG_DEAD note, then delete the + note. */ + + for (insn = first; insn; insn = PREV_INSN (insn)) + { + if (GET_CODE (insn) == CODE_LABEL + || GET_CODE (insn) == JUMP_INSN) + break; + else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && reg_mentioned_p (orig_dest, insn)) + { + note = find_regno_note (insn, REG_DEAD, REGNO (orig_dest)); + if (note) + remove_note (insn, note); + } + } + } + else if (! found_orig_dest) + { + /* This should never happen. */ + abort (); + } + } + + /* Update reg_n_sets. This is necessary to prevent local alloc from + converting REG_EQUAL notes to REG_EQUIV when splitting has modified + a reg from set once to set multiple times. */ + + { + rtx x = PATTERN (orig_insn); + RTX_CODE code = GET_CODE (x); + + if (code == SET || code == CLOBBER) + update_n_sets (x, -1); + else if (code == PARALLEL) + { + int i; + for (i = XVECLEN (x, 0) - 1; i >= 0; i--) + { + code = GET_CODE (XVECEXP (x, 0, i)); + if (code == SET || code == CLOBBER) + update_n_sets (XVECEXP (x, 0, i), -1); + } + } + + for (insn = first; ; insn = NEXT_INSN (insn)) + { + x = PATTERN (insn); + code = GET_CODE (x); + + if (code == SET || code == CLOBBER) + update_n_sets (x, 1); + else if (code == PARALLEL) + { + int i; + for (i = XVECLEN (x, 0) - 1; i >= 0; i--) + { + code = GET_CODE (XVECEXP (x, 0, i)); + if (code == SET || code == CLOBBER) + update_n_sets (XVECEXP (x, 0, i), 1); + } + } + + if (insn == last) + break; + } + } +} + +/* The one entry point in this file. DUMP_FILE is the dump file for + this pass. */ + +void +schedule_insns (dump_file) + FILE *dump_file; +{ + int max_uid = MAX_INSNS_PER_SPLIT * (get_max_uid () + 1); + int i, b; + rtx insn; + + /* Taking care of this degenerate case makes the rest of + this code simpler. */ + if (n_basic_blocks == 0) + return; + + /* Create an insn here so that we can hang dependencies off of it later. */ + sched_before_next_call + = gen_rtx (INSN, VOIDmode, 0, NULL_RTX, NULL_RTX, + NULL_RTX, 0, NULL_RTX, 0); + + /* Initialize the unused_*_lists. We can't use the ones left over from + the previous function, because gcc has freed that memory. We can use + the ones left over from the first sched pass in the second pass however, + so only clear them on the first sched pass. The first pass is before + reload if flag_schedule_insns is set, otherwise it is afterwards. */ + + if (reload_completed == 0 || ! flag_schedule_insns) + { + unused_insn_list = 0; + unused_expr_list = 0; + } + + /* We create no insns here, only reorder them, so we + remember how far we can cut back the stack on exit. */ + + /* Allocate data for this pass. See comments, above, + for what these vectors do. */ + insn_luid = (int *) alloca (max_uid * sizeof (int)); + insn_priority = (int *) alloca (max_uid * sizeof (int)); + insn_tick = (int *) alloca (max_uid * sizeof (int)); + insn_costs = (short *) alloca (max_uid * sizeof (short)); + insn_units = (short *) alloca (max_uid * sizeof (short)); + insn_blockage = (unsigned int *) alloca (max_uid * sizeof (unsigned int)); + insn_ref_count = (int *) alloca (max_uid * sizeof (int)); + + if (reload_completed == 0) + { + sched_reg_n_deaths = (short *) alloca (max_regno * sizeof (short)); + sched_reg_n_calls_crossed = (int *) alloca (max_regno * sizeof (int)); + sched_reg_live_length = (int *) alloca (max_regno * sizeof (int)); + bb_dead_regs = (regset) alloca (regset_bytes); + bb_live_regs = (regset) alloca (regset_bytes); + bzero (sched_reg_n_calls_crossed, max_regno * sizeof (int)); + bzero (sched_reg_live_length, max_regno * sizeof (int)); + bcopy (reg_n_deaths, sched_reg_n_deaths, max_regno * sizeof (short)); + init_alias_analysis (); + } + else + { + sched_reg_n_deaths = 0; + sched_reg_n_calls_crossed = 0; + sched_reg_live_length = 0; + bb_dead_regs = 0; + bb_live_regs = 0; + if (! flag_schedule_insns) + init_alias_analysis (); + } + + if (write_symbols != NO_DEBUG) + { + rtx line; + + line_note = (rtx *) alloca (max_uid * sizeof (rtx)); + bzero (line_note, max_uid * sizeof (rtx)); + line_note_head = (rtx *) alloca (n_basic_blocks * sizeof (rtx)); + bzero (line_note_head, n_basic_blocks * sizeof (rtx)); + + /* Determine the line-number at the start of each basic block. + This must be computed and saved now, because after a basic block's + predecessor has been scheduled, it is impossible to accurately + determine the correct line number for the first insn of the block. */ + + for (b = 0; b < n_basic_blocks; b++) + for (line = basic_block_head[b]; line; line = PREV_INSN (line)) + if (GET_CODE (line) == NOTE && NOTE_LINE_NUMBER (line) > 0) + { + line_note_head[b] = line; + break; + } + } + + bzero (insn_luid, max_uid * sizeof (int)); + bzero (insn_priority, max_uid * sizeof (int)); + bzero (insn_tick, max_uid * sizeof (int)); + bzero (insn_costs, max_uid * sizeof (short)); + bzero (insn_units, max_uid * sizeof (short)); + bzero (insn_blockage, max_uid * sizeof (unsigned int)); + bzero (insn_ref_count, max_uid * sizeof (int)); + + /* Schedule each basic block, block by block. */ + + if (NEXT_INSN (basic_block_end[n_basic_blocks-1]) == 0 + || (GET_CODE (basic_block_end[n_basic_blocks-1]) != NOTE + && GET_CODE (basic_block_end[n_basic_blocks-1]) != CODE_LABEL)) + emit_note_after (NOTE_INSN_DELETED, basic_block_end[n_basic_blocks-1]); + + for (b = 0; b < n_basic_blocks; b++) + { + rtx insn, next; + rtx insns; + + note_list = 0; + + for (insn = basic_block_head[b]; ; insn = next) + { + rtx prev; + rtx set; + + /* Can't use `next_real_insn' because that + might go across CODE_LABELS and short-out basic blocks. */ + next = NEXT_INSN (insn); + if (GET_CODE (insn) != INSN) + { + if (insn == basic_block_end[b]) + break; + + continue; + } + + /* Don't split no-op move insns. These should silently disappear + later in final. Splitting such insns would break the code + that handles REG_NO_CONFLICT blocks. */ + set = single_set (insn); + if (set && rtx_equal_p (SET_SRC (set), SET_DEST (set))) + { + if (insn == basic_block_end[b]) + break; + + /* Nops get in the way while scheduling, so delete them now if + register allocation has already been done. It is too risky + to try to do this before register allocation, and there are + unlikely to be very many nops then anyways. */ + if (reload_completed) + { + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + } + + continue; + } + + /* Split insns here to get max fine-grain parallelism. */ + prev = PREV_INSN (insn); + if (reload_completed == 0) + { + rtx last, first = PREV_INSN (insn); + rtx notes = REG_NOTES (insn); + + last = try_split (PATTERN (insn), insn, 1); + if (last != insn) + { + /* try_split returns the NOTE that INSN became. */ + first = NEXT_INSN (first); + update_flow_info (notes, first, last, insn); + + PUT_CODE (insn, NOTE); + NOTE_SOURCE_FILE (insn) = 0; + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + if (insn == basic_block_head[b]) + basic_block_head[b] = first; + if (insn == basic_block_end[b]) + { + basic_block_end[b] = last; + break; + } + } + } + + if (insn == basic_block_end[b]) + break; + } + + schedule_block (b, dump_file); + +#ifdef USE_C_ALLOCA + alloca (0); +#endif + } + + /* Reposition the prologue and epilogue notes in case we moved the + prologue/epilogue insns. */ + if (reload_completed) + reposition_prologue_and_epilogue_notes (get_insns ()); + + if (write_symbols != NO_DEBUG) + { + rtx line = 0; + rtx insn = get_insns (); + int active_insn = 0; + int notes = 0; + + /* Walk the insns deleting redundant line-number notes. Many of these + are already present. The remainder tend to occur at basic + block boundaries. */ + for (insn = get_last_insn (); insn; insn = PREV_INSN (insn)) + if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0) + { + /* If there are no active insns following, INSN is redundant. */ + if (active_insn == 0) + { + notes++; + NOTE_SOURCE_FILE (insn) = 0; + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + } + /* If the line number is unchanged, LINE is redundant. */ + else if (line + && NOTE_LINE_NUMBER (line) == NOTE_LINE_NUMBER (insn) + && NOTE_SOURCE_FILE (line) == NOTE_SOURCE_FILE (insn)) + { + notes++; + NOTE_SOURCE_FILE (line) = 0; + NOTE_LINE_NUMBER (line) = NOTE_INSN_DELETED; + line = insn; + } + else + line = insn; + active_insn = 0; + } + else if (! ((GET_CODE (insn) == NOTE + && NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED) + || (GET_CODE (insn) == INSN + && (GET_CODE (PATTERN (insn)) == USE + || GET_CODE (PATTERN (insn)) == CLOBBER)))) + active_insn++; + + if (dump_file && notes) + fprintf (dump_file, ";; deleted %d line-number notes\n", notes); + } + + if (reload_completed == 0) + { + int regno; + for (regno = 0; regno < max_regno; regno++) + if (sched_reg_live_length[regno]) + { + if (dump_file) + { + if (reg_live_length[regno] > sched_reg_live_length[regno]) + fprintf (dump_file, + ";; register %d life shortened from %d to %d\n", + regno, reg_live_length[regno], + sched_reg_live_length[regno]); + /* Negative values are special; don't overwrite the current + reg_live_length value if it is negative. */ + else if (reg_live_length[regno] < sched_reg_live_length[regno] + && reg_live_length[regno] >= 0) + fprintf (dump_file, + ";; register %d life extended from %d to %d\n", + regno, reg_live_length[regno], + sched_reg_live_length[regno]); + + if (reg_n_calls_crossed[regno] + && ! sched_reg_n_calls_crossed[regno]) + fprintf (dump_file, + ";; register %d no longer crosses calls\n", regno); + else if (! reg_n_calls_crossed[regno] + && sched_reg_n_calls_crossed[regno]) + fprintf (dump_file, + ";; register %d now crosses calls\n", regno); + } + /* Negative values are special; don't overwrite the current + reg_live_length value if it is negative. */ + if (reg_live_length[regno] >= 0) + reg_live_length[regno] = sched_reg_live_length[regno]; + reg_n_calls_crossed[regno] = sched_reg_n_calls_crossed[regno]; + } + } +} +#endif /* INSN_SCHEDULING */ diff --git a/gnu/usr.bin/cc/common/sdbout.c b/gnu/usr.bin/cc/lib/sdbout.c similarity index 100% rename from gnu/usr.bin/cc/common/sdbout.c rename to gnu/usr.bin/cc/lib/sdbout.c diff --git a/gnu/usr.bin/cc/lib/stmt.c b/gnu/usr.bin/cc/lib/stmt.c new file mode 100644 index 0000000000..5647d26ce9 --- /dev/null +++ b/gnu/usr.bin/cc/lib/stmt.c @@ -0,0 +1,4749 @@ +/* Expands front end tree to back end RTL for GNU C-Compiler + Copyright (C) 1987, 1988, 1989, 1992 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* This file handles the generation of rtl code from tree structure + above the level of expressions, using subroutines in exp*.c and emit-rtl.c. + It also creates the rtl expressions for parameters and auto variables + and has full responsibility for allocating stack slots. + + The functions whose names start with `expand_' are called by the + parser to generate RTL instructions for various kinds of constructs. + + Some control and binding constructs require calling several such + functions at different times. For example, a simple if-then + is expanded by calling `expand_start_cond' (with the condition-expression + as argument) before parsing the then-clause and calling `expand_end_cond' + after parsing the then-clause. */ + +#include "config.h" + +#include +#include + +#include "rtl.h" +#include "tree.h" +#include "flags.h" +#include "function.h" +#include "insn-flags.h" +#include "insn-config.h" +#include "insn-codes.h" +#include "expr.h" +#include "hard-reg-set.h" +#include "obstack.h" +#include "loop.h" +#include "recog.h" + +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free +struct obstack stmt_obstack; + +/* Filename and line number of last line-number note, + whether we actually emitted it or not. */ +char *emit_filename; +int emit_lineno; + +/* Nonzero if within a ({...}) grouping, in which case we must + always compute a value for each expr-stmt in case it is the last one. */ + +int expr_stmts_for_value; + +/* Each time we expand an expression-statement, + record the expr's type and its RTL value here. */ + +static tree last_expr_type; +static rtx last_expr_value; + +/* Each time we expand the end of a binding contour (in `expand_end_bindings') + and we emit a new NOTE_INSN_BLOCK_END note, we save a pointer to it here. + This is used by the `remember_end_note' function to record the endpoint + of each generated block in its associated BLOCK node. */ + +static rtx last_block_end_note; + +/* Number of binding contours started so far in this function. */ + +int block_start_count; + +/* Nonzero if function being compiled needs to + return the address of where it has put a structure value. */ + +extern int current_function_returns_pcc_struct; + +/* Label that will go on parm cleanup code, if any. + Jumping to this label runs cleanup code for parameters, if + such code must be run. Following this code is the logical return label. */ + +extern rtx cleanup_label; + +/* Label that will go on function epilogue. + Jumping to this label serves as a "return" instruction + on machines which require execution of the epilogue on all returns. */ + +extern rtx return_label; + +/* List (chain of EXPR_LISTs) of pseudo-regs of SAVE_EXPRs. + So we can mark them all live at the end of the function, if nonopt. */ +extern rtx save_expr_regs; + +/* Offset to end of allocated area of stack frame. + If stack grows down, this is the address of the last stack slot allocated. + If stack grows up, this is the address for the next slot. */ +extern int frame_offset; + +/* Label to jump back to for tail recursion, or 0 if we have + not yet needed one for this function. */ +extern rtx tail_recursion_label; + +/* Place after which to insert the tail_recursion_label if we need one. */ +extern rtx tail_recursion_reentry; + +/* Location at which to save the argument pointer if it will need to be + referenced. There are two cases where this is done: if nonlocal gotos + exist, or if vars whose is an offset from the argument pointer will be + needed by inner routines. */ + +extern rtx arg_pointer_save_area; + +/* Chain of all RTL_EXPRs that have insns in them. */ +extern tree rtl_expr_chain; + +#if 0 /* Turned off because 0 seems to work just as well. */ +/* Cleanup lists are required for binding levels regardless of whether + that binding level has cleanups or not. This node serves as the + cleanup list whenever an empty list is required. */ +static tree empty_cleanup_list; +#endif + +/* Functions and data structures for expanding case statements. */ + +/* Case label structure, used to hold info on labels within case + statements. We handle "range" labels; for a single-value label + as in C, the high and low limits are the same. + + A chain of case nodes is initially maintained via the RIGHT fields + in the nodes. Nodes with higher case values are later in the list. + + Switch statements can be output in one of two forms. A branch table + is used if there are more than a few labels and the labels are dense + within the range between the smallest and largest case value. If a + branch table is used, no further manipulations are done with the case + node chain. + + The alternative to the use of a branch table is to generate a series + of compare and jump insns. When that is done, we use the LEFT, RIGHT, + and PARENT fields to hold a binary tree. Initially the tree is + totally unbalanced, with everything on the right. We balance the tree + with nodes on the left having lower case values than the parent + and nodes on the right having higher values. We then output the tree + in order. */ + +struct case_node +{ + struct case_node *left; /* Left son in binary tree */ + struct case_node *right; /* Right son in binary tree; also node chain */ + struct case_node *parent; /* Parent of node in binary tree */ + tree low; /* Lowest index value for this label */ + tree high; /* Highest index value for this label */ + tree code_label; /* Label to jump to when node matches */ +}; + +typedef struct case_node case_node; +typedef struct case_node *case_node_ptr; + +/* These are used by estimate_case_costs and balance_case_nodes. */ + +/* This must be a signed type, and non-ANSI compilers lack signed char. */ +static short *cost_table; +static int use_cost_table; + +static int estimate_case_costs (); +static void balance_case_nodes (); +static void emit_case_nodes (); +static void group_case_nodes (); +static void emit_jump_if_reachable (); + +static int warn_if_unused_value (); +static void expand_goto_internal (); +static int expand_fixup (); +void fixup_gotos (); +void free_temp_slots (); +static void expand_cleanups (); +static void expand_null_return_1 (); +static int tail_recursion_args (); +static void do_jump_if_equal (); + +/* Stack of control and binding constructs we are currently inside. + + These constructs begin when you call `expand_start_WHATEVER' + and end when you call `expand_end_WHATEVER'. This stack records + info about how the construct began that tells the end-function + what to do. It also may provide information about the construct + to alter the behavior of other constructs within the body. + For example, they may affect the behavior of C `break' and `continue'. + + Each construct gets one `struct nesting' object. + All of these objects are chained through the `all' field. + `nesting_stack' points to the first object (innermost construct). + The position of an entry on `nesting_stack' is in its `depth' field. + + Each type of construct has its own individual stack. + For example, loops have `loop_stack'. Each object points to the + next object of the same type through the `next' field. + + Some constructs are visible to `break' exit-statements and others + are not. Which constructs are visible depends on the language. + Therefore, the data structure allows each construct to be visible + or not, according to the args given when the construct is started. + The construct is visible if the `exit_label' field is non-null. + In that case, the value should be a CODE_LABEL rtx. */ + +struct nesting +{ + struct nesting *all; + struct nesting *next; + int depth; + rtx exit_label; + union + { + /* For conds (if-then and if-then-else statements). */ + struct + { + /* Label for the end of the if construct. + There is none if EXITFLAG was not set + and no `else' has been seen yet. */ + rtx endif_label; + /* Label for the end of this alternative. + This may be the end of the if or the next else/elseif. */ + rtx next_label; + } cond; + /* For loops. */ + struct + { + /* Label at the top of the loop; place to loop back to. */ + rtx start_label; + /* Label at the end of the whole construct. */ + rtx end_label; + /* Label for `continue' statement to jump to; + this is in front of the stepper of the loop. */ + rtx continue_label; + } loop; + /* For variable binding contours. */ + struct + { + /* Sequence number of this binding contour within the function, + in order of entry. */ + int block_start_count; + /* Nonzero => value to restore stack to on exit. */ + rtx stack_level; + /* The NOTE that starts this contour. + Used by expand_goto to check whether the destination + is within each contour or not. */ + rtx first_insn; + /* Innermost containing binding contour that has a stack level. */ + struct nesting *innermost_stack_block; + /* List of cleanups to be run on exit from this contour. + This is a list of expressions to be evaluated. + The TREE_PURPOSE of each link is the ..._DECL node + which the cleanup pertains to. */ + tree cleanups; + /* List of cleanup-lists of blocks containing this block, + as they were at the locus where this block appears. + There is an element for each containing block, + ordered innermost containing block first. + The tail of this list can be 0 (was empty_cleanup_list), + if all remaining elements would be empty lists. + The element's TREE_VALUE is the cleanup-list of that block, + which may be null. */ + tree outer_cleanups; + /* Chain of labels defined inside this binding contour. + For contours that have stack levels or cleanups. */ + struct label_chain *label_chain; + /* Number of function calls seen, as of start of this block. */ + int function_call_count; + } block; + /* For switch (C) or case (Pascal) statements, + and also for dummies (see `expand_start_case_dummy'). */ + struct + { + /* The insn after which the case dispatch should finally + be emitted. Zero for a dummy. */ + rtx start; + /* A list of case labels, kept in ascending order by value + as the list is built. + During expand_end_case, this list may be rearranged into a + nearly balanced binary tree. */ + struct case_node *case_list; + /* Label to jump to if no case matches. */ + tree default_label; + /* The expression to be dispatched on. */ + tree index_expr; + /* Type that INDEX_EXPR should be converted to. */ + tree nominal_type; + /* Number of range exprs in case statement. */ + int num_ranges; + /* Name of this kind of statement, for warnings. */ + char *printname; + /* Nonzero if a case label has been seen in this case stmt. */ + char seenlabel; + } case_stmt; + /* For exception contours. */ + struct + { + /* List of exceptions raised. This is a TREE_LIST + of whatever you want. */ + tree raised; + /* List of exceptions caught. This is also a TREE_LIST + of whatever you want. As a special case, it has the + value `void_type_node' if it handles default exceptions. */ + tree handled; + + /* First insn of TRY block, in case resumptive model is needed. */ + rtx first_insn; + /* Label for the catch clauses. */ + rtx except_label; + /* Label for unhandled exceptions. */ + rtx unhandled_label; + /* Label at the end of whole construct. */ + rtx after_label; + /* Label which "escapes" the exception construct. + Like EXIT_LABEL for BREAK construct, but for exceptions. */ + rtx escape_label; + } except_stmt; + } data; +}; + +/* Chain of all pending binding contours. */ +struct nesting *block_stack; + +/* If any new stacks are added here, add them to POPSTACKS too. */ + +/* Chain of all pending binding contours that restore stack levels + or have cleanups. */ +struct nesting *stack_block_stack; + +/* Chain of all pending conditional statements. */ +struct nesting *cond_stack; + +/* Chain of all pending loops. */ +struct nesting *loop_stack; + +/* Chain of all pending case or switch statements. */ +struct nesting *case_stack; + +/* Chain of all pending exception contours. */ +struct nesting *except_stack; + +/* Separate chain including all of the above, + chained through the `all' field. */ +struct nesting *nesting_stack; + +/* Number of entries on nesting_stack now. */ +int nesting_depth; + +/* Allocate and return a new `struct nesting'. */ + +#define ALLOC_NESTING() \ + (struct nesting *) obstack_alloc (&stmt_obstack, sizeof (struct nesting)) + +/* Pop the nesting stack element by element until we pop off + the element which is at the top of STACK. + Update all the other stacks, popping off elements from them + as we pop them from nesting_stack. */ + +#define POPSTACK(STACK) \ +do { struct nesting *target = STACK; \ + struct nesting *this; \ + do { this = nesting_stack; \ + if (loop_stack == this) \ + loop_stack = loop_stack->next; \ + if (cond_stack == this) \ + cond_stack = cond_stack->next; \ + if (block_stack == this) \ + block_stack = block_stack->next; \ + if (stack_block_stack == this) \ + stack_block_stack = stack_block_stack->next; \ + if (case_stack == this) \ + case_stack = case_stack->next; \ + if (except_stack == this) \ + except_stack = except_stack->next; \ + nesting_depth = nesting_stack->depth - 1; \ + nesting_stack = this->all; \ + obstack_free (&stmt_obstack, this); } \ + while (this != target); } while (0) + +/* In some cases it is impossible to generate code for a forward goto + until the label definition is seen. This happens when it may be necessary + for the goto to reset the stack pointer: we don't yet know how to do that. + So expand_goto puts an entry on this fixup list. + Each time a binding contour that resets the stack is exited, + we check each fixup. + If the target label has now been defined, we can insert the proper code. */ + +struct goto_fixup +{ + /* Points to following fixup. */ + struct goto_fixup *next; + /* Points to the insn before the jump insn. + If more code must be inserted, it goes after this insn. */ + rtx before_jump; + /* The LABEL_DECL that this jump is jumping to, or 0 + for break, continue or return. */ + tree target; + /* The BLOCK for the place where this goto was found. */ + tree context; + /* The CODE_LABEL rtx that this is jumping to. */ + rtx target_rtl; + /* Number of binding contours started in current function + before the label reference. */ + int block_start_count; + /* The outermost stack level that should be restored for this jump. + Each time a binding contour that resets the stack is exited, + if the target label is *not* yet defined, this slot is updated. */ + rtx stack_level; + /* List of lists of cleanup expressions to be run by this goto. + There is one element for each block that this goto is within. + The tail of this list can be 0 (was empty_cleanup_list), + if all remaining elements would be empty. + The TREE_VALUE contains the cleanup list of that block as of the + time this goto was seen. + The TREE_ADDRESSABLE flag is 1 for a block that has been exited. */ + tree cleanup_list_list; +}; + +static struct goto_fixup *goto_fixup_chain; + +/* Within any binding contour that must restore a stack level, + all labels are recorded with a chain of these structures. */ + +struct label_chain +{ + /* Points to following fixup. */ + struct label_chain *next; + tree label; +}; + +void +init_stmt () +{ + gcc_obstack_init (&stmt_obstack); +#if 0 + empty_cleanup_list = build_tree_list (NULL_TREE, NULL_TREE); +#endif +} + +void +init_stmt_for_function () +{ + /* We are not currently within any block, conditional, loop or case. */ + block_stack = 0; + loop_stack = 0; + case_stack = 0; + cond_stack = 0; + nesting_stack = 0; + nesting_depth = 0; + + block_start_count = 0; + + /* No gotos have been expanded yet. */ + goto_fixup_chain = 0; + + /* We are not processing a ({...}) grouping. */ + expr_stmts_for_value = 0; + last_expr_type = 0; +} + +void +save_stmt_status (p) + struct function *p; +{ + p->block_stack = block_stack; + p->stack_block_stack = stack_block_stack; + p->cond_stack = cond_stack; + p->loop_stack = loop_stack; + p->case_stack = case_stack; + p->nesting_stack = nesting_stack; + p->nesting_depth = nesting_depth; + p->block_start_count = block_start_count; + p->last_expr_type = last_expr_type; + p->last_expr_value = last_expr_value; + p->expr_stmts_for_value = expr_stmts_for_value; + p->emit_filename = emit_filename; + p->emit_lineno = emit_lineno; + p->goto_fixup_chain = goto_fixup_chain; +} + +void +restore_stmt_status (p) + struct function *p; +{ + block_stack = p->block_stack; + stack_block_stack = p->stack_block_stack; + cond_stack = p->cond_stack; + loop_stack = p->loop_stack; + case_stack = p->case_stack; + nesting_stack = p->nesting_stack; + nesting_depth = p->nesting_depth; + block_start_count = p->block_start_count; + last_expr_type = p->last_expr_type; + last_expr_value = p->last_expr_value; + expr_stmts_for_value = p->expr_stmts_for_value; + emit_filename = p->emit_filename; + emit_lineno = p->emit_lineno; + goto_fixup_chain = p->goto_fixup_chain; +} + +/* Emit a no-op instruction. */ + +void +emit_nop () +{ + rtx last_insn = get_last_insn (); + if (!optimize + && (GET_CODE (last_insn) == CODE_LABEL + || prev_real_insn (last_insn) == 0)) + emit_insn (gen_nop ()); +} + +/* Return the rtx-label that corresponds to a LABEL_DECL, + creating it if necessary. */ + +rtx +label_rtx (label) + tree label; +{ + if (TREE_CODE (label) != LABEL_DECL) + abort (); + + if (DECL_RTL (label)) + return DECL_RTL (label); + + return DECL_RTL (label) = gen_label_rtx (); +} + +/* Add an unconditional jump to LABEL as the next sequential instruction. */ + +void +emit_jump (label) + rtx label; +{ + do_pending_stack_adjust (); + emit_jump_insn (gen_jump (label)); + emit_barrier (); +} + +/* Emit code to jump to the address + specified by the pointer expression EXP. */ + +void +expand_computed_goto (exp) + tree exp; +{ + rtx x = expand_expr (exp, NULL_RTX, VOIDmode, 0); + emit_queue (); + emit_indirect_jump (x); +} + +/* Handle goto statements and the labels that they can go to. */ + +/* Specify the location in the RTL code of a label LABEL, + which is a LABEL_DECL tree node. + + This is used for the kind of label that the user can jump to with a + goto statement, and for alternatives of a switch or case statement. + RTL labels generated for loops and conditionals don't go through here; + they are generated directly at the RTL level, by other functions below. + + Note that this has nothing to do with defining label *names*. + Languages vary in how they do that and what that even means. */ + +void +expand_label (label) + tree label; +{ + struct label_chain *p; + + do_pending_stack_adjust (); + emit_label (label_rtx (label)); + if (DECL_NAME (label)) + LABEL_NAME (DECL_RTL (label)) = IDENTIFIER_POINTER (DECL_NAME (label)); + + if (stack_block_stack != 0) + { + p = (struct label_chain *) oballoc (sizeof (struct label_chain)); + p->next = stack_block_stack->data.block.label_chain; + stack_block_stack->data.block.label_chain = p; + p->label = label; + } +} + +/* Declare that LABEL (a LABEL_DECL) may be used for nonlocal gotos + from nested functions. */ + +void +declare_nonlocal_label (label) + tree label; +{ + nonlocal_labels = tree_cons (NULL_TREE, label, nonlocal_labels); + LABEL_PRESERVE_P (label_rtx (label)) = 1; + if (nonlocal_goto_handler_slot == 0) + { + nonlocal_goto_handler_slot + = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0); + emit_stack_save (SAVE_NONLOCAL, + &nonlocal_goto_stack_level, + PREV_INSN (tail_recursion_reentry)); + } +} + +/* Generate RTL code for a `goto' statement with target label LABEL. + LABEL should be a LABEL_DECL tree node that was or will later be + defined with `expand_label'. */ + +void +expand_goto (label) + tree label; +{ + /* Check for a nonlocal goto to a containing function. */ + tree context = decl_function_context (label); + if (context != 0 && context != current_function_decl) + { + struct function *p = find_function_data (context); + rtx label_ref = gen_rtx (LABEL_REF, Pmode, label_rtx (label)); + rtx temp; + + p->has_nonlocal_label = 1; + LABEL_REF_NONLOCAL_P (label_ref) = 1; + + /* Copy the rtl for the slots so that they won't be shared in + case the virtual stack vars register gets instantiated differently + in the parent than in the child. */ + +#if HAVE_nonlocal_goto + if (HAVE_nonlocal_goto) + emit_insn (gen_nonlocal_goto (lookup_static_chain (label), + copy_rtx (p->nonlocal_goto_handler_slot), + copy_rtx (p->nonlocal_goto_stack_level), + label_ref)); + else +#endif + { + rtx addr; + + /* Restore frame pointer for containing function. + This sets the actual hard register used for the frame pointer + to the location of the function's incoming static chain info. + The non-local goto handler will then adjust it to contain the + proper value and reload the argument pointer, if needed. */ + emit_move_insn (frame_pointer_rtx, lookup_static_chain (label)); + + /* We have now loaded the frame pointer hardware register with + the address of that corresponds to the start of the virtual + stack vars. So replace virtual_stack_vars_rtx in all + addresses we use with stack_pointer_rtx. */ + + /* Get addr of containing function's current nonlocal goto handler, + which will do any cleanups and then jump to the label. */ + addr = copy_rtx (p->nonlocal_goto_handler_slot); + temp = copy_to_reg (replace_rtx (addr, virtual_stack_vars_rtx, + frame_pointer_rtx)); + + /* Restore the stack pointer. Note this uses fp just restored. */ + addr = p->nonlocal_goto_stack_level; + if (addr) + addr = replace_rtx (copy_rtx (addr), + virtual_stack_vars_rtx, frame_pointer_rtx); + + emit_stack_restore (SAVE_NONLOCAL, addr, NULL_RTX); + + /* Put in the static chain register the nonlocal label address. */ + emit_move_insn (static_chain_rtx, label_ref); + /* USE of frame_pointer_rtx added for consistency; not clear if + really needed. */ + emit_insn (gen_rtx (USE, VOIDmode, frame_pointer_rtx)); + emit_insn (gen_rtx (USE, VOIDmode, stack_pointer_rtx)); + emit_insn (gen_rtx (USE, VOIDmode, static_chain_rtx)); + emit_indirect_jump (temp); + } + } + else + expand_goto_internal (label, label_rtx (label), NULL_RTX); +} + +/* Generate RTL code for a `goto' statement with target label BODY. + LABEL should be a LABEL_REF. + LAST_INSN, if non-0, is the rtx we should consider as the last + insn emitted (for the purposes of cleaning up a return). */ + +static void +expand_goto_internal (body, label, last_insn) + tree body; + rtx label; + rtx last_insn; +{ + struct nesting *block; + rtx stack_level = 0; + + if (GET_CODE (label) != CODE_LABEL) + abort (); + + /* If label has already been defined, we can tell now + whether and how we must alter the stack level. */ + + if (PREV_INSN (label) != 0) + { + /* Find the innermost pending block that contains the label. + (Check containment by comparing insn-uids.) + Then restore the outermost stack level within that block, + and do cleanups of all blocks contained in it. */ + for (block = block_stack; block; block = block->next) + { + if (INSN_UID (block->data.block.first_insn) < INSN_UID (label)) + break; + if (block->data.block.stack_level != 0) + stack_level = block->data.block.stack_level; + /* Execute the cleanups for blocks we are exiting. */ + if (block->data.block.cleanups != 0) + { + expand_cleanups (block->data.block.cleanups, NULL_TREE); + do_pending_stack_adjust (); + } + } + + if (stack_level) + { + /* Ensure stack adjust isn't done by emit_jump, as this would clobber + the stack pointer. This one should be deleted as dead by flow. */ + clear_pending_stack_adjust (); + do_pending_stack_adjust (); + emit_stack_restore (SAVE_BLOCK, stack_level, NULL_RTX); + } + + if (body != 0 && DECL_TOO_LATE (body)) + error ("jump to `%s' invalidly jumps into binding contour", + IDENTIFIER_POINTER (DECL_NAME (body))); + } + /* Label not yet defined: may need to put this goto + on the fixup list. */ + else if (! expand_fixup (body, label, last_insn)) + { + /* No fixup needed. Record that the label is the target + of at least one goto that has no fixup. */ + if (body != 0) + TREE_ADDRESSABLE (body) = 1; + } + + emit_jump (label); +} + +/* Generate if necessary a fixup for a goto + whose target label in tree structure (if any) is TREE_LABEL + and whose target in rtl is RTL_LABEL. + + If LAST_INSN is nonzero, we pretend that the jump appears + after insn LAST_INSN instead of at the current point in the insn stream. + + The fixup will be used later to insert insns just before the goto. + Those insns will restore the stack level as appropriate for the + target label, and will (in the case of C++) also invoke any object + destructors which have to be invoked when we exit the scopes which + are exited by the goto. + + Value is nonzero if a fixup is made. */ + +static int +expand_fixup (tree_label, rtl_label, last_insn) + tree tree_label; + rtx rtl_label; + rtx last_insn; +{ + struct nesting *block, *end_block; + + /* See if we can recognize which block the label will be output in. + This is possible in some very common cases. + If we succeed, set END_BLOCK to that block. + Otherwise, set it to 0. */ + + if (cond_stack + && (rtl_label == cond_stack->data.cond.endif_label + || rtl_label == cond_stack->data.cond.next_label)) + end_block = cond_stack; + /* If we are in a loop, recognize certain labels which + are likely targets. This reduces the number of fixups + we need to create. */ + else if (loop_stack + && (rtl_label == loop_stack->data.loop.start_label + || rtl_label == loop_stack->data.loop.end_label + || rtl_label == loop_stack->data.loop.continue_label)) + end_block = loop_stack; + else + end_block = 0; + + /* Now set END_BLOCK to the binding level to which we will return. */ + + if (end_block) + { + struct nesting *next_block = end_block->all; + block = block_stack; + + /* First see if the END_BLOCK is inside the innermost binding level. + If so, then no cleanups or stack levels are relevant. */ + while (next_block && next_block != block) + next_block = next_block->all; + + if (next_block) + return 0; + + /* Otherwise, set END_BLOCK to the innermost binding level + which is outside the relevant control-structure nesting. */ + next_block = block_stack->next; + for (block = block_stack; block != end_block; block = block->all) + if (block == next_block) + next_block = next_block->next; + end_block = next_block; + } + + /* Does any containing block have a stack level or cleanups? + If not, no fixup is needed, and that is the normal case + (the only case, for standard C). */ + for (block = block_stack; block != end_block; block = block->next) + if (block->data.block.stack_level != 0 + || block->data.block.cleanups != 0) + break; + + if (block != end_block) + { + /* Ok, a fixup is needed. Add a fixup to the list of such. */ + struct goto_fixup *fixup + = (struct goto_fixup *) oballoc (sizeof (struct goto_fixup)); + /* In case an old stack level is restored, make sure that comes + after any pending stack adjust. */ + /* ?? If the fixup isn't to come at the present position, + doing the stack adjust here isn't useful. Doing it with our + settings at that location isn't useful either. Let's hope + someone does it! */ + if (last_insn == 0) + do_pending_stack_adjust (); + fixup->target = tree_label; + fixup->target_rtl = rtl_label; + + /* Create a BLOCK node and a corresponding matched set of + NOTE_INSN_BEGIN_BLOCK and NOTE_INSN_END_BLOCK notes at + this point. The notes will encapsulate any and all fixup + code which we might later insert at this point in the insn + stream. Also, the BLOCK node will be the parent (i.e. the + `SUPERBLOCK') of any other BLOCK nodes which we might create + later on when we are expanding the fixup code. */ + + { + register rtx original_before_jump + = last_insn ? last_insn : get_last_insn (); + + start_sequence (); + pushlevel (0); + fixup->before_jump = emit_note (NULL_PTR, NOTE_INSN_BLOCK_BEG); + last_block_end_note = emit_note (NULL_PTR, NOTE_INSN_BLOCK_END); + fixup->context = poplevel (1, 0, 0); /* Create the BLOCK node now! */ + end_sequence (); + emit_insns_after (fixup->before_jump, original_before_jump); + } + + fixup->block_start_count = block_start_count; + fixup->stack_level = 0; + fixup->cleanup_list_list + = (((block->data.block.outer_cleanups +#if 0 + && block->data.block.outer_cleanups != empty_cleanup_list +#endif + ) + || block->data.block.cleanups) + ? tree_cons (NULL_TREE, block->data.block.cleanups, + block->data.block.outer_cleanups) + : 0); + fixup->next = goto_fixup_chain; + goto_fixup_chain = fixup; + } + + return block != 0; +} + +/* When exiting a binding contour, process all pending gotos requiring fixups. + THISBLOCK is the structure that describes the block being exited. + STACK_LEVEL is the rtx for the stack level to restore exiting this contour. + CLEANUP_LIST is a list of expressions to evaluate on exiting this contour. + FIRST_INSN is the insn that began this contour. + + Gotos that jump out of this contour must restore the + stack level and do the cleanups before actually jumping. + + DONT_JUMP_IN nonzero means report error there is a jump into this + contour from before the beginning of the contour. + This is also done if STACK_LEVEL is nonzero. */ + +void +fixup_gotos (thisblock, stack_level, cleanup_list, first_insn, dont_jump_in) + struct nesting *thisblock; + rtx stack_level; + tree cleanup_list; + rtx first_insn; + int dont_jump_in; +{ + register struct goto_fixup *f, *prev; + + /* F is the fixup we are considering; PREV is the previous one. */ + /* We run this loop in two passes so that cleanups of exited blocks + are run first, and blocks that are exited are marked so + afterwards. */ + + for (prev = 0, f = goto_fixup_chain; f; prev = f, f = f->next) + { + /* Test for a fixup that is inactive because it is already handled. */ + if (f->before_jump == 0) + { + /* Delete inactive fixup from the chain, if that is easy to do. */ + if (prev != 0) + prev->next = f->next; + } + /* Has this fixup's target label been defined? + If so, we can finalize it. */ + else if (PREV_INSN (f->target_rtl) != 0) + { + register rtx cleanup_insns; + + /* Get the first non-label after the label + this goto jumps to. If that's before this scope begins, + we don't have a jump into the scope. */ + rtx after_label = f->target_rtl; + while (after_label != 0 && GET_CODE (after_label) == CODE_LABEL) + after_label = NEXT_INSN (after_label); + + /* If this fixup jumped into this contour from before the beginning + of this contour, report an error. */ + /* ??? Bug: this does not detect jumping in through intermediate + blocks that have stack levels or cleanups. + It detects only a problem with the innermost block + around the label. */ + if (f->target != 0 + && (dont_jump_in || stack_level || cleanup_list) + /* If AFTER_LABEL is 0, it means the jump goes to the end + of the rtl, which means it jumps into this scope. */ + && (after_label == 0 + || INSN_UID (first_insn) < INSN_UID (after_label)) + && INSN_UID (first_insn) > INSN_UID (f->before_jump) + && ! DECL_REGISTER (f->target)) + { + error_with_decl (f->target, + "label `%s' used before containing binding contour"); + /* Prevent multiple errors for one label. */ + DECL_REGISTER (f->target) = 1; + } + + /* We will expand the cleanups into a sequence of their own and + then later on we will attach this new sequence to the insn + stream just ahead of the actual jump insn. */ + + start_sequence (); + + /* Temporarily restore the lexical context where we will + logically be inserting the fixup code. We do this for the + sake of getting the debugging information right. */ + + pushlevel (0); + set_block (f->context); + + /* Expand the cleanups for blocks this jump exits. */ + if (f->cleanup_list_list) + { + tree lists; + for (lists = f->cleanup_list_list; lists; lists = TREE_CHAIN (lists)) + /* Marked elements correspond to blocks that have been closed. + Do their cleanups. */ + if (TREE_ADDRESSABLE (lists) + && TREE_VALUE (lists) != 0) + { + expand_cleanups (TREE_VALUE (lists), 0); + /* Pop any pushes done in the cleanups, + in case function is about to return. */ + do_pending_stack_adjust (); + } + } + + /* Restore stack level for the biggest contour that this + jump jumps out of. */ + if (f->stack_level) + emit_stack_restore (SAVE_BLOCK, f->stack_level, f->before_jump); + + /* Finish up the sequence containing the insns which implement the + necessary cleanups, and then attach that whole sequence to the + insn stream just ahead of the actual jump insn. Attaching it + at that point insures that any cleanups which are in fact + implicit C++ object destructions (which must be executed upon + leaving the block) appear (to the debugger) to be taking place + in an area of the generated code where the object(s) being + destructed are still "in scope". */ + + cleanup_insns = get_insns (); + poplevel (1, 0, 0); + + end_sequence (); + emit_insns_after (cleanup_insns, f->before_jump); + + + f->before_jump = 0; + } + } + + /* Mark the cleanups of exited blocks so that they are executed + by the code above. */ + for (prev = 0, f = goto_fixup_chain; f; prev = f, f = f->next) + if (f->before_jump != 0 + && PREV_INSN (f->target_rtl) == 0 + /* Label has still not appeared. If we are exiting a block with + a stack level to restore, that started before the fixup, + mark this stack level as needing restoration + when the fixup is later finalized. + Also mark the cleanup_list_list element for F + that corresponds to this block, so that ultimately + this block's cleanups will be executed by the code above. */ + && thisblock != 0 + /* Note: if THISBLOCK == 0 and we have a label that hasn't appeared, + it means the label is undefined. That's erroneous, but possible. */ + && (thisblock->data.block.block_start_count + <= f->block_start_count)) + { + tree lists = f->cleanup_list_list; + for (; lists; lists = TREE_CHAIN (lists)) + /* If the following elt. corresponds to our containing block + then the elt. must be for this block. */ + if (TREE_CHAIN (lists) == thisblock->data.block.outer_cleanups) + TREE_ADDRESSABLE (lists) = 1; + + if (stack_level) + f->stack_level = stack_level; + } +} + +/* Generate RTL for an asm statement (explicit assembler code). + BODY is a STRING_CST node containing the assembler code text, + or an ADDR_EXPR containing a STRING_CST. */ + +void +expand_asm (body) + tree body; +{ + if (TREE_CODE (body) == ADDR_EXPR) + body = TREE_OPERAND (body, 0); + + emit_insn (gen_rtx (ASM_INPUT, VOIDmode, + TREE_STRING_POINTER (body))); + last_expr_type = 0; +} + +/* Generate RTL for an asm statement with arguments. + STRING is the instruction template. + OUTPUTS is a list of output arguments (lvalues); INPUTS a list of inputs. + Each output or input has an expression in the TREE_VALUE and + a constraint-string in the TREE_PURPOSE. + CLOBBERS is a list of STRING_CST nodes each naming a hard register + that is clobbered by this insn. + + Not all kinds of lvalue that may appear in OUTPUTS can be stored directly. + Some elements of OUTPUTS may be replaced with trees representing temporary + values. The caller should copy those temporary values to the originally + specified lvalues. + + VOL nonzero means the insn is volatile; don't optimize it. */ + +void +expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line) + tree string, outputs, inputs, clobbers; + int vol; + char *filename; + int line; +{ + rtvec argvec, constraints; + rtx body; + int ninputs = list_length (inputs); + int noutputs = list_length (outputs); + int nclobbers; + tree tail; + register int i; + /* Vector of RTX's of evaluated output operands. */ + rtx *output_rtx = (rtx *) alloca (noutputs * sizeof (rtx)); + /* The insn we have emitted. */ + rtx insn; + + /* Count the number of meaningful clobbered registers, ignoring what + we would ignore later. */ + nclobbers = 0; + for (tail = clobbers; tail; tail = TREE_CHAIN (tail)) + { + char *regname = TREE_STRING_POINTER (TREE_VALUE (tail)); + i = decode_reg_name (regname); + if (i >= 0 || i == -4) + ++nclobbers; + } + + last_expr_type = 0; + + for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++) + { + tree val = TREE_VALUE (tail); + tree val1; + int j; + int found_equal; + + /* If there's an erroneous arg, emit no insn. */ + if (TREE_TYPE (val) == error_mark_node) + return; + + /* Make sure constraint has `=' and does not have `+'. */ + + found_equal = 0; + for (j = 0; j < TREE_STRING_LENGTH (TREE_PURPOSE (tail)); j++) + { + if (TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '+') + { + error ("output operand constraint contains `+'"); + return; + } + if (TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '=') + found_equal = 1; + } + if (! found_equal) + { + error ("output operand constraint lacks `='"); + return; + } + + /* If an output operand is not a variable or indirect ref, + or a part of one, + create a SAVE_EXPR which is a pseudo-reg + to act as an intermediate temporary. + Make the asm insn write into that, then copy it to + the real output operand. */ + + while (TREE_CODE (val) == COMPONENT_REF + || TREE_CODE (val) == ARRAY_REF) + val = TREE_OPERAND (val, 0); + + if (TREE_CODE (val) != VAR_DECL + && TREE_CODE (val) != PARM_DECL + && TREE_CODE (val) != INDIRECT_REF) + { + TREE_VALUE (tail) = save_expr (TREE_VALUE (tail)); + /* If it's a constant, print error now so don't crash later. */ + if (TREE_CODE (TREE_VALUE (tail)) != SAVE_EXPR) + { + error ("invalid output in `asm'"); + return; + } + } + + output_rtx[i] = expand_expr (TREE_VALUE (tail), NULL_RTX, VOIDmode, 0); + } + + if (ninputs + noutputs > MAX_RECOG_OPERANDS) + { + error ("more than %d operands in `asm'", MAX_RECOG_OPERANDS); + return; + } + + /* Make vectors for the expression-rtx and constraint strings. */ + + argvec = rtvec_alloc (ninputs); + constraints = rtvec_alloc (ninputs); + + body = gen_rtx (ASM_OPERANDS, VOIDmode, + TREE_STRING_POINTER (string), "", 0, argvec, constraints, + filename, line); + MEM_VOLATILE_P (body) = vol; + + /* Eval the inputs and put them into ARGVEC. + Put their constraints into ASM_INPUTs and store in CONSTRAINTS. */ + + i = 0; + for (tail = inputs; tail; tail = TREE_CHAIN (tail)) + { + int j; + + /* If there's an erroneous arg, emit no insn, + because the ASM_INPUT would get VOIDmode + and that could cause a crash in reload. */ + if (TREE_TYPE (TREE_VALUE (tail)) == error_mark_node) + return; + if (TREE_PURPOSE (tail) == NULL_TREE) + { + error ("hard register `%s' listed as input operand to `asm'", + TREE_STRING_POINTER (TREE_VALUE (tail)) ); + return; + } + + /* Make sure constraint has neither `=' nor `+'. */ + + for (j = 0; j < TREE_STRING_LENGTH (TREE_PURPOSE (tail)); j++) + if (TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '=' + || TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '+') + { + error ("input operand constraint contains `%c'", + TREE_STRING_POINTER (TREE_PURPOSE (tail))[j]); + return; + } + + XVECEXP (body, 3, i) /* argvec */ + = expand_expr (TREE_VALUE (tail), NULL_RTX, VOIDmode, 0); + XVECEXP (body, 4, i) /* constraints */ + = gen_rtx (ASM_INPUT, TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))), + TREE_STRING_POINTER (TREE_PURPOSE (tail))); + i++; + } + + /* Protect all the operands from the queue, + now that they have all been evaluated. */ + + for (i = 0; i < ninputs; i++) + XVECEXP (body, 3, i) = protect_from_queue (XVECEXP (body, 3, i), 0); + + for (i = 0; i < noutputs; i++) + output_rtx[i] = protect_from_queue (output_rtx[i], 1); + + /* Now, for each output, construct an rtx + (set OUTPUT (asm_operands INSN OUTPUTNUMBER OUTPUTCONSTRAINT + ARGVEC CONSTRAINTS)) + If there is more than one, put them inside a PARALLEL. */ + + if (noutputs == 1 && nclobbers == 0) + { + XSTR (body, 1) = TREE_STRING_POINTER (TREE_PURPOSE (outputs)); + insn = emit_insn (gen_rtx (SET, VOIDmode, output_rtx[0], body)); + } + else if (noutputs == 0 && nclobbers == 0) + { + /* No output operands: put in a raw ASM_OPERANDS rtx. */ + insn = emit_insn (body); + } + else + { + rtx obody = body; + int num = noutputs; + if (num == 0) num = 1; + body = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (num + nclobbers)); + + /* For each output operand, store a SET. */ + + for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++) + { + XVECEXP (body, 0, i) + = gen_rtx (SET, VOIDmode, + output_rtx[i], + gen_rtx (ASM_OPERANDS, VOIDmode, + TREE_STRING_POINTER (string), + TREE_STRING_POINTER (TREE_PURPOSE (tail)), + i, argvec, constraints, + filename, line)); + MEM_VOLATILE_P (SET_SRC (XVECEXP (body, 0, i))) = vol; + } + + /* If there are no outputs (but there are some clobbers) + store the bare ASM_OPERANDS into the PARALLEL. */ + + if (i == 0) + XVECEXP (body, 0, i++) = obody; + + /* Store (clobber REG) for each clobbered register specified. */ + + for (tail = clobbers; tail; tail = TREE_CHAIN (tail)) + { + char *regname = TREE_STRING_POINTER (TREE_VALUE (tail)); + int j = decode_reg_name (regname); + + if (j < 0) + { + if (j == -3) /* `cc', which is not a register */ + continue; + + if (j == -4) /* `memory', don't cache memory across asm */ + { + XVECEXP (body, 0, i++) + = gen_rtx (CLOBBER, VOIDmode, + gen_rtx (MEM, QImode, + gen_rtx (SCRATCH, VOIDmode, 0))); + continue; + } + + error ("unknown register name `%s' in `asm'", regname); + return; + } + + /* Use QImode since that's guaranteed to clobber just one reg. */ + XVECEXP (body, 0, i++) + = gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, QImode, j)); + } + + insn = emit_insn (body); + } + + free_temp_slots (); +} + +/* Generate RTL to evaluate the expression EXP + and remember it in case this is the VALUE in a ({... VALUE; }) constr. */ + +void +expand_expr_stmt (exp) + tree exp; +{ + /* If -W, warn about statements with no side effects, + except for an explicit cast to void (e.g. for assert()), and + except inside a ({...}) where they may be useful. */ + if (expr_stmts_for_value == 0 && exp != error_mark_node) + { + if (! TREE_SIDE_EFFECTS (exp) && (extra_warnings || warn_unused) + && !(TREE_CODE (exp) == CONVERT_EXPR + && TREE_TYPE (exp) == void_type_node)) + warning_with_file_and_line (emit_filename, emit_lineno, + "statement with no effect"); + else if (warn_unused) + warn_if_unused_value (exp); + } + last_expr_type = TREE_TYPE (exp); + if (! flag_syntax_only) + last_expr_value = expand_expr (exp, + (expr_stmts_for_value + ? NULL_RTX : const0_rtx), + VOIDmode, 0); + + /* If all we do is reference a volatile value in memory, + copy it to a register to be sure it is actually touched. */ + if (last_expr_value != 0 && GET_CODE (last_expr_value) == MEM + && TREE_THIS_VOLATILE (exp)) + { + if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode) + ; + else if (TYPE_MODE (TREE_TYPE (exp)) != BLKmode) + copy_to_reg (last_expr_value); + else + { + rtx lab = gen_label_rtx (); + + /* Compare the value with itself to reference it. */ + emit_cmp_insn (last_expr_value, last_expr_value, EQ, + expand_expr (TYPE_SIZE (last_expr_type), + NULL_RTX, VOIDmode, 0), + BLKmode, 0, + TYPE_ALIGN (last_expr_type) / BITS_PER_UNIT); + emit_jump_insn ((*bcc_gen_fctn[(int) EQ]) (lab)); + emit_label (lab); + } + } + + /* If this expression is part of a ({...}) and is in memory, we may have + to preserve temporaries. */ + preserve_temp_slots (last_expr_value); + + /* Free any temporaries used to evaluate this expression. Any temporary + used as a result of this expression will already have been preserved + above. */ + free_temp_slots (); + + emit_queue (); +} + +/* Warn if EXP contains any computations whose results are not used. + Return 1 if a warning is printed; 0 otherwise. */ + +static int +warn_if_unused_value (exp) + tree exp; +{ + if (TREE_USED (exp)) + return 0; + + switch (TREE_CODE (exp)) + { + case PREINCREMENT_EXPR: + case POSTINCREMENT_EXPR: + case PREDECREMENT_EXPR: + case POSTDECREMENT_EXPR: + case MODIFY_EXPR: + case INIT_EXPR: + case TARGET_EXPR: + case CALL_EXPR: + case METHOD_CALL_EXPR: + case RTL_EXPR: + case WITH_CLEANUP_EXPR: + case EXIT_EXPR: + /* We don't warn about COND_EXPR because it may be a useful + construct if either arm contains a side effect. */ + case COND_EXPR: + return 0; + + case BIND_EXPR: + /* For a binding, warn if no side effect within it. */ + return warn_if_unused_value (TREE_OPERAND (exp, 1)); + + case TRUTH_ORIF_EXPR: + case TRUTH_ANDIF_EXPR: + /* In && or ||, warn if 2nd operand has no side effect. */ + return warn_if_unused_value (TREE_OPERAND (exp, 1)); + + case COMPOUND_EXPR: + if (warn_if_unused_value (TREE_OPERAND (exp, 0))) + return 1; + /* Let people do `(foo (), 0)' without a warning. */ + if (TREE_CONSTANT (TREE_OPERAND (exp, 1))) + return 0; + return warn_if_unused_value (TREE_OPERAND (exp, 1)); + + case NOP_EXPR: + case CONVERT_EXPR: + case NON_LVALUE_EXPR: + /* Don't warn about values cast to void. */ + if (TREE_TYPE (exp) == void_type_node) + return 0; + /* Don't warn about conversions not explicit in the user's program. */ + if (TREE_NO_UNUSED_WARNING (exp)) + return 0; + /* Assignment to a cast usually results in a cast of a modify. + Don't complain about that. */ + if (TREE_CODE (TREE_OPERAND (exp, 0)) == MODIFY_EXPR) + return 0; + /* Sometimes it results in a cast of a cast of a modify. + Don't complain about that. */ + if ((TREE_CODE (TREE_OPERAND (exp, 0)) == CONVERT_EXPR + || TREE_CODE (TREE_OPERAND (exp, 0)) == NOP_EXPR) + && TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) == MODIFY_EXPR) + return 0; + + default: + /* Referencing a volatile value is a side effect, so don't warn. */ + if ((TREE_CODE_CLASS (TREE_CODE (exp)) == 'd' + || TREE_CODE_CLASS (TREE_CODE (exp)) == 'r') + && TREE_THIS_VOLATILE (exp)) + return 0; + warning_with_file_and_line (emit_filename, emit_lineno, + "value computed is not used"); + return 1; + } +} + +/* Clear out the memory of the last expression evaluated. */ + +void +clear_last_expr () +{ + last_expr_type = 0; +} + +/* Begin a statement which will return a value. + Return the RTL_EXPR for this statement expr. + The caller must save that value and pass it to expand_end_stmt_expr. */ + +tree +expand_start_stmt_expr () +{ + /* Make the RTL_EXPR node temporary, not momentary, + so that rtl_expr_chain doesn't become garbage. */ + int momentary = suspend_momentary (); + tree t = make_node (RTL_EXPR); + resume_momentary (momentary); + start_sequence (); + NO_DEFER_POP; + expr_stmts_for_value++; + return t; +} + +/* Restore the previous state at the end of a statement that returns a value. + Returns a tree node representing the statement's value and the + insns to compute the value. + + The nodes of that expression have been freed by now, so we cannot use them. + But we don't want to do that anyway; the expression has already been + evaluated and now we just want to use the value. So generate a RTL_EXPR + with the proper type and RTL value. + + If the last substatement was not an expression, + return something with type `void'. */ + +tree +expand_end_stmt_expr (t) + tree t; +{ + OK_DEFER_POP; + + if (last_expr_type == 0) + { + last_expr_type = void_type_node; + last_expr_value = const0_rtx; + } + else if (last_expr_value == 0) + /* There are some cases where this can happen, such as when the + statement is void type. */ + last_expr_value = const0_rtx; + else if (GET_CODE (last_expr_value) != REG && ! CONSTANT_P (last_expr_value)) + /* Remove any possible QUEUED. */ + last_expr_value = protect_from_queue (last_expr_value, 0); + + emit_queue (); + + TREE_TYPE (t) = last_expr_type; + RTL_EXPR_RTL (t) = last_expr_value; + RTL_EXPR_SEQUENCE (t) = get_insns (); + + rtl_expr_chain = tree_cons (NULL_TREE, t, rtl_expr_chain); + + end_sequence (); + + /* Don't consider deleting this expr or containing exprs at tree level. */ + TREE_SIDE_EFFECTS (t) = 1; + /* Propagate volatility of the actual RTL expr. */ + TREE_THIS_VOLATILE (t) = volatile_refs_p (last_expr_value); + + last_expr_type = 0; + expr_stmts_for_value--; + + return t; +} + +/* The exception handling nesting looks like this: + + <-- Level N-1 + { <-- exception handler block + <-- Level N + <-- in an exception handler + { <-- try block + : <-- in a TRY block + : <-- in an exception handler + : + } + + { <-- except block + : <-- in an except block + : <-- in an exception handler + : + } + + } +*/ + +/* Return nonzero iff in a try block at level LEVEL. */ + +int +in_try_block (level) + int level; +{ + struct nesting *n = except_stack; + while (1) + { + while (n && n->data.except_stmt.after_label != 0) + n = n->next; + if (n == 0) + return 0; + if (level == 0) + return n != 0; + level--; + n = n->next; + } +} + +/* Return nonzero iff in an except block at level LEVEL. */ + +int +in_except_block (level) + int level; +{ + struct nesting *n = except_stack; + while (1) + { + while (n && n->data.except_stmt.after_label == 0) + n = n->next; + if (n == 0) + return 0; + if (level == 0) + return n != 0; + level--; + n = n->next; + } +} + +/* Return nonzero iff in an exception handler at level LEVEL. */ + +int +in_exception_handler (level) + int level; +{ + struct nesting *n = except_stack; + while (n && level--) + n = n->next; + return n != 0; +} + +/* Record the fact that the current exception nesting raises + exception EX. If not in an exception handler, return 0. */ +int +expand_raise (ex) + tree ex; +{ + tree *raises_ptr; + + if (except_stack == 0) + return 0; + raises_ptr = &except_stack->data.except_stmt.raised; + if (! value_member (ex, *raises_ptr)) + *raises_ptr = tree_cons (NULL_TREE, ex, *raises_ptr); + return 1; +} + +/* Generate RTL for the start of a try block. + + TRY_CLAUSE is the condition to test to enter the try block. */ + +void +expand_start_try (try_clause, exitflag, escapeflag) + tree try_clause; + int exitflag; + int escapeflag; +{ + struct nesting *thishandler = ALLOC_NESTING (); + + /* Make an entry on cond_stack for the cond we are entering. */ + + thishandler->next = except_stack; + thishandler->all = nesting_stack; + thishandler->depth = ++nesting_depth; + thishandler->data.except_stmt.raised = 0; + thishandler->data.except_stmt.handled = 0; + thishandler->data.except_stmt.first_insn = get_insns (); + thishandler->data.except_stmt.except_label = gen_label_rtx (); + thishandler->data.except_stmt.unhandled_label = 0; + thishandler->data.except_stmt.after_label = 0; + thishandler->data.except_stmt.escape_label + = escapeflag ? thishandler->data.except_stmt.except_label : 0; + thishandler->exit_label = exitflag ? gen_label_rtx () : 0; + except_stack = thishandler; + nesting_stack = thishandler; + + do_jump (try_clause, thishandler->data.except_stmt.except_label, NULL_RTX); +} + +/* End of a TRY block. Nothing to do for now. */ + +void +expand_end_try () +{ + except_stack->data.except_stmt.after_label = gen_label_rtx (); + expand_goto_internal (NULL_TREE, except_stack->data.except_stmt.after_label, + NULL_RTX); +} + +/* Start an `except' nesting contour. + EXITFLAG says whether this contour should be able to `exit' something. + ESCAPEFLAG says whether this contour should be escapable. */ + +void +expand_start_except (exitflag, escapeflag) + int exitflag; + int escapeflag; +{ + if (exitflag) + { + struct nesting *n; + /* An `exit' from catch clauses goes out to next exit level, + if there is one. Otherwise, it just goes to the end + of the construct. */ + for (n = except_stack->next; n; n = n->next) + if (n->exit_label != 0) + { + except_stack->exit_label = n->exit_label; + break; + } + if (n == 0) + except_stack->exit_label = except_stack->data.except_stmt.after_label; + } + if (escapeflag) + { + struct nesting *n; + /* An `escape' from catch clauses goes out to next escape level, + if there is one. Otherwise, it just goes to the end + of the construct. */ + for (n = except_stack->next; n; n = n->next) + if (n->data.except_stmt.escape_label != 0) + { + except_stack->data.except_stmt.escape_label + = n->data.except_stmt.escape_label; + break; + } + if (n == 0) + except_stack->data.except_stmt.escape_label + = except_stack->data.except_stmt.after_label; + } + do_pending_stack_adjust (); + emit_label (except_stack->data.except_stmt.except_label); +} + +/* Generate code to `escape' from an exception contour. This + is like `exiting', but does not conflict with constructs which + use `exit_label'. + + Return nonzero if this contour is escapable, otherwise + return zero, and language-specific code will emit the + appropriate error message. */ +int +expand_escape_except () +{ + struct nesting *n; + last_expr_type = 0; + for (n = except_stack; n; n = n->next) + if (n->data.except_stmt.escape_label != 0) + { + expand_goto_internal (NULL_TREE, + n->data.except_stmt.escape_label, NULL_RTX); + return 1; + } + + return 0; +} + +/* Finish processing and `except' contour. + Culls out all exceptions which might be raise but not + handled, and returns the list to the caller. + Language-specific code is responsible for dealing with these + exceptions. */ + +tree +expand_end_except () +{ + struct nesting *n; + tree raised = NULL_TREE; + + do_pending_stack_adjust (); + emit_label (except_stack->data.except_stmt.after_label); + + n = except_stack->next; + if (n) + { + /* Propagate exceptions raised but not handled to next + highest level. */ + tree handled = except_stack->data.except_stmt.raised; + if (handled != void_type_node) + { + tree prev = NULL_TREE; + raised = except_stack->data.except_stmt.raised; + while (handled) + { + tree this_raise; + for (this_raise = raised, prev = 0; this_raise; + this_raise = TREE_CHAIN (this_raise)) + { + if (value_member (TREE_VALUE (this_raise), handled)) + { + if (prev) + TREE_CHAIN (prev) = TREE_CHAIN (this_raise); + else + { + raised = TREE_CHAIN (raised); + if (raised == NULL_TREE) + goto nada; + } + } + else + prev = this_raise; + } + handled = TREE_CHAIN (handled); + } + if (prev == NULL_TREE) + prev = raised; + if (prev) + TREE_CHAIN (prev) = n->data.except_stmt.raised; + nada: + n->data.except_stmt.raised = raised; + } + } + + POPSTACK (except_stack); + last_expr_type = 0; + return raised; +} + +/* Record that exception EX is caught by this exception handler. + Return nonzero if in exception handling construct, otherwise return 0. */ +int +expand_catch (ex) + tree ex; +{ + tree *raises_ptr; + + if (except_stack == 0) + return 0; + raises_ptr = &except_stack->data.except_stmt.handled; + if (*raises_ptr != void_type_node + && ex != NULL_TREE + && ! value_member (ex, *raises_ptr)) + *raises_ptr = tree_cons (NULL_TREE, ex, *raises_ptr); + return 1; +} + +/* Record that this exception handler catches all exceptions. + Return nonzero if in exception handling construct, otherwise return 0. */ + +int +expand_catch_default () +{ + if (except_stack == 0) + return 0; + except_stack->data.except_stmt.handled = void_type_node; + return 1; +} + +int +expand_end_catch () +{ + if (except_stack == 0 || except_stack->data.except_stmt.after_label == 0) + return 0; + expand_goto_internal (NULL_TREE, except_stack->data.except_stmt.after_label, + NULL_RTX); + return 1; +} + +/* Generate RTL for the start of an if-then. COND is the expression + whose truth should be tested. + + If EXITFLAG is nonzero, this conditional is visible to + `exit_something'. */ + +void +expand_start_cond (cond, exitflag) + tree cond; + int exitflag; +{ + struct nesting *thiscond = ALLOC_NESTING (); + + /* Make an entry on cond_stack for the cond we are entering. */ + + thiscond->next = cond_stack; + thiscond->all = nesting_stack; + thiscond->depth = ++nesting_depth; + thiscond->data.cond.next_label = gen_label_rtx (); + /* Before we encounter an `else', we don't need a separate exit label + unless there are supposed to be exit statements + to exit this conditional. */ + thiscond->exit_label = exitflag ? gen_label_rtx () : 0; + thiscond->data.cond.endif_label = thiscond->exit_label; + cond_stack = thiscond; + nesting_stack = thiscond; + + do_jump (cond, thiscond->data.cond.next_label, NULL_RTX); +} + +/* Generate RTL between then-clause and the elseif-clause + of an if-then-elseif-.... */ + +void +expand_start_elseif (cond) + tree cond; +{ + if (cond_stack->data.cond.endif_label == 0) + cond_stack->data.cond.endif_label = gen_label_rtx (); + emit_jump (cond_stack->data.cond.endif_label); + emit_label (cond_stack->data.cond.next_label); + cond_stack->data.cond.next_label = gen_label_rtx (); + do_jump (cond, cond_stack->data.cond.next_label, NULL_RTX); +} + +/* Generate RTL between the then-clause and the else-clause + of an if-then-else. */ + +void +expand_start_else () +{ + if (cond_stack->data.cond.endif_label == 0) + cond_stack->data.cond.endif_label = gen_label_rtx (); + emit_jump (cond_stack->data.cond.endif_label); + emit_label (cond_stack->data.cond.next_label); + cond_stack->data.cond.next_label = 0; /* No more _else or _elseif calls. */ +} + +/* Generate RTL for the end of an if-then. + Pop the record for it off of cond_stack. */ + +void +expand_end_cond () +{ + struct nesting *thiscond = cond_stack; + + do_pending_stack_adjust (); + if (thiscond->data.cond.next_label) + emit_label (thiscond->data.cond.next_label); + if (thiscond->data.cond.endif_label) + emit_label (thiscond->data.cond.endif_label); + + POPSTACK (cond_stack); + last_expr_type = 0; +} + +/* Generate RTL for the start of a loop. EXIT_FLAG is nonzero if this + loop should be exited by `exit_something'. This is a loop for which + `expand_continue' will jump to the top of the loop. + + Make an entry on loop_stack to record the labels associated with + this loop. */ + +struct nesting * +expand_start_loop (exit_flag) + int exit_flag; +{ + register struct nesting *thisloop = ALLOC_NESTING (); + + /* Make an entry on loop_stack for the loop we are entering. */ + + thisloop->next = loop_stack; + thisloop->all = nesting_stack; + thisloop->depth = ++nesting_depth; + thisloop->data.loop.start_label = gen_label_rtx (); + thisloop->data.loop.end_label = gen_label_rtx (); + thisloop->data.loop.continue_label = thisloop->data.loop.start_label; + thisloop->exit_label = exit_flag ? thisloop->data.loop.end_label : 0; + loop_stack = thisloop; + nesting_stack = thisloop; + + do_pending_stack_adjust (); + emit_queue (); + emit_note (NULL_PTR, NOTE_INSN_LOOP_BEG); + emit_label (thisloop->data.loop.start_label); + + return thisloop; +} + +/* Like expand_start_loop but for a loop where the continuation point + (for expand_continue_loop) will be specified explicitly. */ + +struct nesting * +expand_start_loop_continue_elsewhere (exit_flag) + int exit_flag; +{ + struct nesting *thisloop = expand_start_loop (exit_flag); + loop_stack->data.loop.continue_label = gen_label_rtx (); + return thisloop; +} + +/* Specify the continuation point for a loop started with + expand_start_loop_continue_elsewhere. + Use this at the point in the code to which a continue statement + should jump. */ + +void +expand_loop_continue_here () +{ + do_pending_stack_adjust (); + emit_note (NULL_PTR, NOTE_INSN_LOOP_CONT); + emit_label (loop_stack->data.loop.continue_label); +} + +/* Finish a loop. Generate a jump back to the top and the loop-exit label. + Pop the block off of loop_stack. */ + +void +expand_end_loop () +{ + register rtx insn = get_last_insn (); + register rtx start_label = loop_stack->data.loop.start_label; + rtx last_test_insn = 0; + int num_insns = 0; + + /* Mark the continue-point at the top of the loop if none elsewhere. */ + if (start_label == loop_stack->data.loop.continue_label) + emit_note_before (NOTE_INSN_LOOP_CONT, start_label); + + do_pending_stack_adjust (); + + /* If optimizing, perhaps reorder the loop. If the loop + starts with a conditional exit, roll that to the end + where it will optimize together with the jump back. + + We look for the last conditional branch to the exit that we encounter + before hitting 30 insns or a CALL_INSN. If we see an unconditional + branch to the exit first, use it. + + We must also stop at NOTE_INSN_BLOCK_BEG and NOTE_INSN_BLOCK_END notes + because moving them is not valid. */ + + if (optimize + && + ! (GET_CODE (insn) == JUMP_INSN + && GET_CODE (PATTERN (insn)) == SET + && SET_DEST (PATTERN (insn)) == pc_rtx + && GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE)) + { + /* Scan insns from the top of the loop looking for a qualified + conditional exit. */ + for (insn = NEXT_INSN (loop_stack->data.loop.start_label); insn; + insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == CODE_LABEL) + break; + + if (GET_CODE (insn) == NOTE + && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG + || NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END)) + break; + + if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == INSN) + num_insns++; + + if (last_test_insn && num_insns > 30) + break; + + if (GET_CODE (insn) == JUMP_INSN && GET_CODE (PATTERN (insn)) == SET + && SET_DEST (PATTERN (insn)) == pc_rtx + && GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE + && ((GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 1)) == LABEL_REF + && (XEXP (XEXP (SET_SRC (PATTERN (insn)), 1), 0) + == loop_stack->data.loop.end_label)) + || (GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 2)) == LABEL_REF + && (XEXP (XEXP (SET_SRC (PATTERN (insn)), 2), 0) + == loop_stack->data.loop.end_label)))) + last_test_insn = insn; + + if (last_test_insn == 0 && GET_CODE (insn) == JUMP_INSN + && GET_CODE (PATTERN (insn)) == SET + && SET_DEST (PATTERN (insn)) == pc_rtx + && GET_CODE (SET_SRC (PATTERN (insn))) == LABEL_REF + && (XEXP (SET_SRC (PATTERN (insn)), 0) + == loop_stack->data.loop.end_label)) + /* Include BARRIER. */ + last_test_insn = NEXT_INSN (insn); + } + + if (last_test_insn != 0 && last_test_insn != get_last_insn ()) + { + /* We found one. Move everything from there up + to the end of the loop, and add a jump into the loop + to jump to there. */ + register rtx newstart_label = gen_label_rtx (); + register rtx start_move = start_label; + + /* If the start label is preceded by a NOTE_INSN_LOOP_CONT note, + then we want to move this note also. */ + if (GET_CODE (PREV_INSN (start_move)) == NOTE + && (NOTE_LINE_NUMBER (PREV_INSN (start_move)) + == NOTE_INSN_LOOP_CONT)) + start_move = PREV_INSN (start_move); + + emit_label_after (newstart_label, PREV_INSN (start_move)); + reorder_insns (start_move, last_test_insn, get_last_insn ()); + emit_jump_insn_after (gen_jump (start_label), + PREV_INSN (newstart_label)); + emit_barrier_after (PREV_INSN (newstart_label)); + start_label = newstart_label; + } + } + + emit_jump (start_label); + emit_note (NULL_PTR, NOTE_INSN_LOOP_END); + emit_label (loop_stack->data.loop.end_label); + + POPSTACK (loop_stack); + + last_expr_type = 0; +} + +/* Generate a jump to the current loop's continue-point. + This is usually the top of the loop, but may be specified + explicitly elsewhere. If not currently inside a loop, + return 0 and do nothing; caller will print an error message. */ + +int +expand_continue_loop (whichloop) + struct nesting *whichloop; +{ + last_expr_type = 0; + if (whichloop == 0) + whichloop = loop_stack; + if (whichloop == 0) + return 0; + expand_goto_internal (NULL_TREE, whichloop->data.loop.continue_label, + NULL_RTX); + return 1; +} + +/* Generate a jump to exit the current loop. If not currently inside a loop, + return 0 and do nothing; caller will print an error message. */ + +int +expand_exit_loop (whichloop) + struct nesting *whichloop; +{ + last_expr_type = 0; + if (whichloop == 0) + whichloop = loop_stack; + if (whichloop == 0) + return 0; + expand_goto_internal (NULL_TREE, whichloop->data.loop.end_label, NULL_RTX); + return 1; +} + +/* Generate a conditional jump to exit the current loop if COND + evaluates to zero. If not currently inside a loop, + return 0 and do nothing; caller will print an error message. */ + +int +expand_exit_loop_if_false (whichloop, cond) + struct nesting *whichloop; + tree cond; +{ + last_expr_type = 0; + if (whichloop == 0) + whichloop = loop_stack; + if (whichloop == 0) + return 0; + do_jump (cond, whichloop->data.loop.end_label, NULL_RTX); + return 1; +} + +/* Return non-zero if we should preserve sub-expressions as separate + pseudos. We never do so if we aren't optimizing. We always do so + if -fexpensive-optimizations. + + Otherwise, we only do so if we are in the "early" part of a loop. I.e., + the loop may still be a small one. */ + +int +preserve_subexpressions_p () +{ + rtx insn; + + if (flag_expensive_optimizations) + return 1; + + if (optimize == 0 || loop_stack == 0) + return 0; + + insn = get_last_insn_anywhere (); + + return (insn + && (INSN_UID (insn) - INSN_UID (loop_stack->data.loop.start_label) + < n_non_fixed_regs * 3)); + +} + +/* Generate a jump to exit the current loop, conditional, binding contour + or case statement. Not all such constructs are visible to this function, + only those started with EXIT_FLAG nonzero. Individual languages use + the EXIT_FLAG parameter to control which kinds of constructs you can + exit this way. + + If not currently inside anything that can be exited, + return 0 and do nothing; caller will print an error message. */ + +int +expand_exit_something () +{ + struct nesting *n; + last_expr_type = 0; + for (n = nesting_stack; n; n = n->all) + if (n->exit_label != 0) + { + expand_goto_internal (NULL_TREE, n->exit_label, NULL_RTX); + return 1; + } + + return 0; +} + +/* Generate RTL to return from the current function, with no value. + (That is, we do not do anything about returning any value.) */ + +void +expand_null_return () +{ + struct nesting *block = block_stack; + rtx last_insn = 0; + + /* Does any pending block have cleanups? */ + + while (block && block->data.block.cleanups == 0) + block = block->next; + + /* If yes, use a goto to return, since that runs cleanups. */ + + expand_null_return_1 (last_insn, block != 0); +} + +/* Generate RTL to return from the current function, with value VAL. */ + +void +expand_value_return (val) + rtx val; +{ + struct nesting *block = block_stack; + rtx last_insn = get_last_insn (); + rtx return_reg = DECL_RTL (DECL_RESULT (current_function_decl)); + + /* Copy the value to the return location + unless it's already there. */ + + if (return_reg != val) + { +#ifdef PROMOTE_FUNCTION_RETURN + enum machine_mode mode = DECL_MODE (DECL_RESULT (current_function_decl)); + tree type = TREE_TYPE (DECL_RESULT (current_function_decl)); + int unsignedp = TREE_UNSIGNED (type); + + if (TREE_CODE (type) == INTEGER_TYPE || TREE_CODE (type) == ENUMERAL_TYPE + || TREE_CODE (type) == BOOLEAN_TYPE || TREE_CODE (type) == CHAR_TYPE + || TREE_CODE (type) == REAL_TYPE || TREE_CODE (type) == POINTER_TYPE + || TREE_CODE (type) == OFFSET_TYPE) + { + PROMOTE_MODE (mode, unsignedp, type); + } + + if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode) + convert_move (return_reg, val, unsignedp); + else +#endif + emit_move_insn (return_reg, val); + } + if (GET_CODE (return_reg) == REG + && REGNO (return_reg) < FIRST_PSEUDO_REGISTER) + emit_insn (gen_rtx (USE, VOIDmode, return_reg)); + + /* Does any pending block have cleanups? */ + + while (block && block->data.block.cleanups == 0) + block = block->next; + + /* If yes, use a goto to return, since that runs cleanups. + Use LAST_INSN to put cleanups *before* the move insn emitted above. */ + + expand_null_return_1 (last_insn, block != 0); +} + +/* Output a return with no value. If LAST_INSN is nonzero, + pretend that the return takes place after LAST_INSN. + If USE_GOTO is nonzero then don't use a return instruction; + go to the return label instead. This causes any cleanups + of pending blocks to be executed normally. */ + +static void +expand_null_return_1 (last_insn, use_goto) + rtx last_insn; + int use_goto; +{ + rtx end_label = cleanup_label ? cleanup_label : return_label; + + clear_pending_stack_adjust (); + do_pending_stack_adjust (); + last_expr_type = 0; + + /* PCC-struct return always uses an epilogue. */ + if (current_function_returns_pcc_struct || use_goto) + { + if (end_label == 0) + end_label = return_label = gen_label_rtx (); + expand_goto_internal (NULL_TREE, end_label, last_insn); + return; + } + + /* Otherwise output a simple return-insn if one is available, + unless it won't do the job. */ +#ifdef HAVE_return + if (HAVE_return && use_goto == 0 && cleanup_label == 0) + { + emit_jump_insn (gen_return ()); + emit_barrier (); + return; + } +#endif + + /* Otherwise jump to the epilogue. */ + expand_goto_internal (NULL_TREE, end_label, last_insn); +} + +/* Generate RTL to evaluate the expression RETVAL and return it + from the current function. */ + +void +expand_return (retval) + tree retval; +{ + /* If there are any cleanups to be performed, then they will + be inserted following LAST_INSN. It is desirable + that the last_insn, for such purposes, should be the + last insn before computing the return value. Otherwise, cleanups + which call functions can clobber the return value. */ + /* ??? rms: I think that is erroneous, because in C++ it would + run destructors on variables that might be used in the subsequent + computation of the return value. */ + rtx last_insn = 0; + register rtx val = 0; + register rtx op0; + tree retval_rhs; + int cleanups; + struct nesting *block; + + /* If function wants no value, give it none. */ + if (TREE_CODE (TREE_TYPE (TREE_TYPE (current_function_decl))) == VOID_TYPE) + { + expand_expr (retval, NULL_RTX, VOIDmode, 0); + emit_queue (); + expand_null_return (); + return; + } + + /* Are any cleanups needed? E.g. C++ destructors to be run? */ + cleanups = any_pending_cleanups (1); + + if (TREE_CODE (retval) == RESULT_DECL) + retval_rhs = retval; + else if ((TREE_CODE (retval) == MODIFY_EXPR || TREE_CODE (retval) == INIT_EXPR) + && TREE_CODE (TREE_OPERAND (retval, 0)) == RESULT_DECL) + retval_rhs = TREE_OPERAND (retval, 1); + else if (TREE_TYPE (retval) == void_type_node) + /* Recognize tail-recursive call to void function. */ + retval_rhs = retval; + else + retval_rhs = NULL_TREE; + + /* Only use `last_insn' if there are cleanups which must be run. */ + if (cleanups || cleanup_label != 0) + last_insn = get_last_insn (); + + /* Distribute return down conditional expr if either of the sides + may involve tail recursion (see test below). This enhances the number + of tail recursions we see. Don't do this always since it can produce + sub-optimal code in some cases and we distribute assignments into + conditional expressions when it would help. */ + + if (optimize && retval_rhs != 0 + && frame_offset == 0 + && TREE_CODE (retval_rhs) == COND_EXPR + && (TREE_CODE (TREE_OPERAND (retval_rhs, 1)) == CALL_EXPR + || TREE_CODE (TREE_OPERAND (retval_rhs, 2)) == CALL_EXPR)) + { + rtx label = gen_label_rtx (); + do_jump (TREE_OPERAND (retval_rhs, 0), label, NULL_RTX); + expand_return (build (MODIFY_EXPR, TREE_TYPE (current_function_decl), + DECL_RESULT (current_function_decl), + TREE_OPERAND (retval_rhs, 1))); + emit_label (label); + expand_return (build (MODIFY_EXPR, TREE_TYPE (current_function_decl), + DECL_RESULT (current_function_decl), + TREE_OPERAND (retval_rhs, 2))); + return; + } + + /* For tail-recursive call to current function, + just jump back to the beginning. + It's unsafe if any auto variable in this function + has its address taken; for simplicity, + require stack frame to be empty. */ + if (optimize && retval_rhs != 0 + && frame_offset == 0 + && TREE_CODE (retval_rhs) == CALL_EXPR + && TREE_CODE (TREE_OPERAND (retval_rhs, 0)) == ADDR_EXPR + && TREE_OPERAND (TREE_OPERAND (retval_rhs, 0), 0) == current_function_decl + /* Finish checking validity, and if valid emit code + to set the argument variables for the new call. */ + && tail_recursion_args (TREE_OPERAND (retval_rhs, 1), + DECL_ARGUMENTS (current_function_decl))) + { + if (tail_recursion_label == 0) + { + tail_recursion_label = gen_label_rtx (); + emit_label_after (tail_recursion_label, + tail_recursion_reentry); + } + emit_queue (); + expand_goto_internal (NULL_TREE, tail_recursion_label, last_insn); + emit_barrier (); + return; + } +#ifdef HAVE_return + /* This optimization is safe if there are local cleanups + because expand_null_return takes care of them. + ??? I think it should also be safe when there is a cleanup label, + because expand_null_return takes care of them, too. + Any reason why not? */ + if (HAVE_return && cleanup_label == 0 + && ! current_function_returns_pcc_struct) + { + /* If this is return x == y; then generate + if (x == y) return 1; else return 0; + if we can do it with explicit return insns. */ + if (retval_rhs) + switch (TREE_CODE (retval_rhs)) + { + case EQ_EXPR: + case NE_EXPR: + case GT_EXPR: + case GE_EXPR: + case LT_EXPR: + case LE_EXPR: + case TRUTH_ANDIF_EXPR: + case TRUTH_ORIF_EXPR: + case TRUTH_AND_EXPR: + case TRUTH_OR_EXPR: + case TRUTH_NOT_EXPR: + case TRUTH_XOR_EXPR: + op0 = gen_label_rtx (); + jumpifnot (retval_rhs, op0); + expand_value_return (const1_rtx); + emit_label (op0); + expand_value_return (const0_rtx); + return; + } + } +#endif /* HAVE_return */ + + if (cleanups + && retval_rhs != 0 + && TREE_TYPE (retval_rhs) != void_type_node + && GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) == REG) + { + /* Calculate the return value into a pseudo reg. */ + val = expand_expr (retval_rhs, NULL_RTX, VOIDmode, 0); + emit_queue (); + /* All temporaries have now been used. */ + free_temp_slots (); + /* Return the calculated value, doing cleanups first. */ + expand_value_return (val); + } + else + { + /* No cleanups or no hard reg used; + calculate value into hard return reg. */ + expand_expr (retval, NULL_RTX, VOIDmode, 0); + emit_queue (); + free_temp_slots (); + expand_value_return (DECL_RTL (DECL_RESULT (current_function_decl))); + } +} + +/* Return 1 if the end of the generated RTX is not a barrier. + This means code already compiled can drop through. */ + +int +drop_through_at_end_p () +{ + rtx insn = get_last_insn (); + while (insn && GET_CODE (insn) == NOTE) + insn = PREV_INSN (insn); + return insn && GET_CODE (insn) != BARRIER; +} + +/* Emit code to alter this function's formal parms for a tail-recursive call. + ACTUALS is a list of actual parameter expressions (chain of TREE_LISTs). + FORMALS is the chain of decls of formals. + Return 1 if this can be done; + otherwise return 0 and do not emit any code. */ + +static int +tail_recursion_args (actuals, formals) + tree actuals, formals; +{ + register tree a = actuals, f = formals; + register int i; + register rtx *argvec; + + /* Check that number and types of actuals are compatible + with the formals. This is not always true in valid C code. + Also check that no formal needs to be addressable + and that all formals are scalars. */ + + /* Also count the args. */ + + for (a = actuals, f = formals, i = 0; a && f; a = TREE_CHAIN (a), f = TREE_CHAIN (f), i++) + { + if (TREE_TYPE (TREE_VALUE (a)) != TREE_TYPE (f)) + return 0; + if (GET_CODE (DECL_RTL (f)) != REG || DECL_MODE (f) == BLKmode) + return 0; + } + if (a != 0 || f != 0) + return 0; + + /* Compute all the actuals. */ + + argvec = (rtx *) alloca (i * sizeof (rtx)); + + for (a = actuals, i = 0; a; a = TREE_CHAIN (a), i++) + argvec[i] = expand_expr (TREE_VALUE (a), NULL_RTX, VOIDmode, 0); + + /* Find which actual values refer to current values of previous formals. + Copy each of them now, before any formal is changed. */ + + for (a = actuals, i = 0; a; a = TREE_CHAIN (a), i++) + { + int copy = 0; + register int j; + for (f = formals, j = 0; j < i; f = TREE_CHAIN (f), j++) + if (reg_mentioned_p (DECL_RTL (f), argvec[i])) + { copy = 1; break; } + if (copy) + argvec[i] = copy_to_reg (argvec[i]); + } + + /* Store the values of the actuals into the formals. */ + + for (f = formals, a = actuals, i = 0; f; + f = TREE_CHAIN (f), a = TREE_CHAIN (a), i++) + { + if (GET_MODE (DECL_RTL (f)) == GET_MODE (argvec[i])) + emit_move_insn (DECL_RTL (f), argvec[i]); + else + convert_move (DECL_RTL (f), argvec[i], + TREE_UNSIGNED (TREE_TYPE (TREE_VALUE (a)))); + } + + free_temp_slots (); + return 1; +} + +/* Generate the RTL code for entering a binding contour. + The variables are declared one by one, by calls to `expand_decl'. + + EXIT_FLAG is nonzero if this construct should be visible to + `exit_something'. */ + +void +expand_start_bindings (exit_flag) + int exit_flag; +{ + struct nesting *thisblock = ALLOC_NESTING (); + + rtx note = emit_note (NULL_PTR, NOTE_INSN_BLOCK_BEG); + + /* Make an entry on block_stack for the block we are entering. */ + + thisblock->next = block_stack; + thisblock->all = nesting_stack; + thisblock->depth = ++nesting_depth; + thisblock->data.block.stack_level = 0; + thisblock->data.block.cleanups = 0; + thisblock->data.block.function_call_count = 0; +#if 0 + if (block_stack) + { + if (block_stack->data.block.cleanups == NULL_TREE + && (block_stack->data.block.outer_cleanups == NULL_TREE + || block_stack->data.block.outer_cleanups == empty_cleanup_list)) + thisblock->data.block.outer_cleanups = empty_cleanup_list; + else + thisblock->data.block.outer_cleanups + = tree_cons (NULL_TREE, block_stack->data.block.cleanups, + block_stack->data.block.outer_cleanups); + } + else + thisblock->data.block.outer_cleanups = 0; +#endif +#if 1 + if (block_stack + && !(block_stack->data.block.cleanups == NULL_TREE + && block_stack->data.block.outer_cleanups == NULL_TREE)) + thisblock->data.block.outer_cleanups + = tree_cons (NULL_TREE, block_stack->data.block.cleanups, + block_stack->data.block.outer_cleanups); + else + thisblock->data.block.outer_cleanups = 0; +#endif + thisblock->data.block.label_chain = 0; + thisblock->data.block.innermost_stack_block = stack_block_stack; + thisblock->data.block.first_insn = note; + thisblock->data.block.block_start_count = ++block_start_count; + thisblock->exit_label = exit_flag ? gen_label_rtx () : 0; + block_stack = thisblock; + nesting_stack = thisblock; + + /* Make a new level for allocating stack slots. */ + push_temp_slots (); +} + +/* Given a pointer to a BLOCK node, save a pointer to the most recently + generated NOTE_INSN_BLOCK_END in the BLOCK_END_NOTE field of the given + BLOCK node. */ + +void +remember_end_note (block) + register tree block; +{ + BLOCK_END_NOTE (block) = last_block_end_note; + last_block_end_note = NULL_RTX; +} + +/* Generate RTL code to terminate a binding contour. + VARS is the chain of VAR_DECL nodes + for the variables bound in this contour. + MARK_ENDS is nonzero if we should put a note at the beginning + and end of this binding contour. + + DONT_JUMP_IN is nonzero if it is not valid to jump into this contour. + (That is true automatically if the contour has a saved stack level.) */ + +void +expand_end_bindings (vars, mark_ends, dont_jump_in) + tree vars; + int mark_ends; + int dont_jump_in; +{ + register struct nesting *thisblock = block_stack; + register tree decl; + + if (warn_unused) + for (decl = vars; decl; decl = TREE_CHAIN (decl)) + if (! TREE_USED (decl) && TREE_CODE (decl) == VAR_DECL + && ! DECL_IN_SYSTEM_HEADER (decl)) + warning_with_decl (decl, "unused variable `%s'"); + + if (thisblock->exit_label) + { + do_pending_stack_adjust (); + emit_label (thisblock->exit_label); + } + + /* If necessary, make a handler for nonlocal gotos taking + place in the function calls in this block. */ + if (function_call_count != thisblock->data.block.function_call_count + && nonlocal_labels + /* Make handler for outermost block + if there were any nonlocal gotos to this function. */ + && (thisblock->next == 0 ? current_function_has_nonlocal_label + /* Make handler for inner block if it has something + special to do when you jump out of it. */ + : (thisblock->data.block.cleanups != 0 + || thisblock->data.block.stack_level != 0))) + { + tree link; + rtx afterward = gen_label_rtx (); + rtx handler_label = gen_label_rtx (); + rtx save_receiver = gen_reg_rtx (Pmode); + + /* Don't let jump_optimize delete the handler. */ + LABEL_PRESERVE_P (handler_label) = 1; + + /* Record the handler address in the stack slot for that purpose, + during this block, saving and restoring the outer value. */ + if (thisblock->next != 0) + { + emit_move_insn (nonlocal_goto_handler_slot, save_receiver); + emit_insn_before (gen_move_insn (save_receiver, + nonlocal_goto_handler_slot), + thisblock->data.block.first_insn); + } + emit_insn_before (gen_move_insn (nonlocal_goto_handler_slot, + gen_rtx (LABEL_REF, Pmode, + handler_label)), + thisblock->data.block.first_insn); + + /* Jump around the handler; it runs only when specially invoked. */ + emit_jump (afterward); + emit_label (handler_label); + +#ifdef HAVE_nonlocal_goto + if (! HAVE_nonlocal_goto) +#endif + /* First adjust our frame pointer to its actual value. It was + previously set to the start of the virtual area corresponding to + the stacked variables when we branched here and now needs to be + adjusted to the actual hardware fp value. + + Assignments are to virtual registers are converted by + instantiate_virtual_regs into the corresponding assignment + to the underlying register (fp in this case) that makes + the original assignment true. + So the following insn will actually be + decrementing fp by STARTING_FRAME_OFFSET. */ + emit_move_insn (virtual_stack_vars_rtx, frame_pointer_rtx); + +#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM + if (fixed_regs[ARG_POINTER_REGNUM]) + { +#ifdef ELIMINABLE_REGS + /* If the argument pointer can be eliminated in favor of the + frame pointer, we don't need to restore it. We assume here + that if such an elimination is present, it can always be used. + This is the case on all known machines; if we don't make this + assumption, we do unnecessary saving on many machines. */ + static struct elims {int from, to;} elim_regs[] = ELIMINABLE_REGS; + int i; + + for (i = 0; i < sizeof elim_regs / sizeof elim_regs[0]; i++) + if (elim_regs[i].from == ARG_POINTER_REGNUM + && elim_regs[i].to == FRAME_POINTER_REGNUM) + break; + + if (i == sizeof elim_regs / sizeof elim_regs [0]) +#endif + { + /* Now restore our arg pointer from the address at which it + was saved in our stack frame. + If there hasn't be space allocated for it yet, make + some now. */ + if (arg_pointer_save_area == 0) + arg_pointer_save_area + = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0); + emit_move_insn (virtual_incoming_args_rtx, + /* We need a pseudo here, or else + instantiate_virtual_regs_1 complains. */ + copy_to_reg (arg_pointer_save_area)); + } + } +#endif + + /* The handler expects the desired label address in the static chain + register. It tests the address and does an appropriate jump + to whatever label is desired. */ + for (link = nonlocal_labels; link; link = TREE_CHAIN (link)) + /* Skip any labels we shouldn't be able to jump to from here. */ + if (! DECL_TOO_LATE (TREE_VALUE (link))) + { + rtx not_this = gen_label_rtx (); + rtx this = gen_label_rtx (); + do_jump_if_equal (static_chain_rtx, + gen_rtx (LABEL_REF, Pmode, DECL_RTL (TREE_VALUE (link))), + this, 0); + emit_jump (not_this); + emit_label (this); + expand_goto (TREE_VALUE (link)); + emit_label (not_this); + } + /* If label is not recognized, abort. */ + emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "abort"), 0, + VOIDmode, 0); + emit_label (afterward); + } + + /* Don't allow jumping into a block that has cleanups or a stack level. */ + if (dont_jump_in + || thisblock->data.block.stack_level != 0 + || thisblock->data.block.cleanups != 0) + { + struct label_chain *chain; + + /* Any labels in this block are no longer valid to go to. + Mark them to cause an error message. */ + for (chain = thisblock->data.block.label_chain; chain; chain = chain->next) + { + DECL_TOO_LATE (chain->label) = 1; + /* If any goto without a fixup came to this label, + that must be an error, because gotos without fixups + come from outside all saved stack-levels and all cleanups. */ + if (TREE_ADDRESSABLE (chain->label)) + error_with_decl (chain->label, + "label `%s' used before containing binding contour"); + } + } + + /* Restore stack level in effect before the block + (only if variable-size objects allocated). */ + /* Perform any cleanups associated with the block. */ + + if (thisblock->data.block.stack_level != 0 + || thisblock->data.block.cleanups != 0) + { + /* Don't let cleanups affect ({...}) constructs. */ + int old_expr_stmts_for_value = expr_stmts_for_value; + rtx old_last_expr_value = last_expr_value; + tree old_last_expr_type = last_expr_type; + expr_stmts_for_value = 0; + + /* Do the cleanups. */ + expand_cleanups (thisblock->data.block.cleanups, NULL_TREE); + do_pending_stack_adjust (); + + expr_stmts_for_value = old_expr_stmts_for_value; + last_expr_value = old_last_expr_value; + last_expr_type = old_last_expr_type; + + /* Restore the stack level. */ + + if (thisblock->data.block.stack_level != 0) + { + emit_stack_restore (thisblock->next ? SAVE_BLOCK : SAVE_FUNCTION, + thisblock->data.block.stack_level, NULL_RTX); + if (nonlocal_goto_handler_slot != 0) + emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, + NULL_RTX); + } + + /* Any gotos out of this block must also do these things. + Also report any gotos with fixups that came to labels in this + level. */ + fixup_gotos (thisblock, + thisblock->data.block.stack_level, + thisblock->data.block.cleanups, + thisblock->data.block.first_insn, + dont_jump_in); + } + + /* Mark the beginning and end of the scope if requested. + We do this now, after running cleanups on the variables + just going out of scope, so they are in scope for their cleanups. */ + + if (mark_ends) + last_block_end_note = emit_note (NULL_PTR, NOTE_INSN_BLOCK_END); + else + /* Get rid of the beginning-mark if we don't make an end-mark. */ + NOTE_LINE_NUMBER (thisblock->data.block.first_insn) = NOTE_INSN_DELETED; + + /* If doing stupid register allocation, make sure lives of all + register variables declared here extend thru end of scope. */ + + if (obey_regdecls) + for (decl = vars; decl; decl = TREE_CHAIN (decl)) + { + rtx rtl = DECL_RTL (decl); + if (TREE_CODE (decl) == VAR_DECL && rtl != 0) + use_variable (rtl); + } + + /* Restore block_stack level for containing block. */ + + stack_block_stack = thisblock->data.block.innermost_stack_block; + POPSTACK (block_stack); + + /* Pop the stack slot nesting and free any slots at this level. */ + pop_temp_slots (); +} + +/* Generate RTL for the automatic variable declaration DECL. + (Other kinds of declarations are simply ignored if seen here.) + CLEANUP is an expression to be executed at exit from this binding contour; + for example, in C++, it might call the destructor for this variable. + + If CLEANUP contains any SAVE_EXPRs, then you must preevaluate them + either before or after calling `expand_decl' but before compiling + any subsequent expressions. This is because CLEANUP may be expanded + more than once, on different branches of execution. + For the same reason, CLEANUP may not contain a CALL_EXPR + except as its topmost node--else `preexpand_calls' would get confused. + + If CLEANUP is nonzero and DECL is zero, we record a cleanup + that is not associated with any particular variable. + + There is no special support here for C++ constructors. + They should be handled by the proper code in DECL_INITIAL. */ + +void +expand_decl (decl) + register tree decl; +{ + struct nesting *thisblock = block_stack; + tree type = TREE_TYPE (decl); + + /* Only automatic variables need any expansion done. + Static and external variables, and external functions, + will be handled by `assemble_variable' (called from finish_decl). + TYPE_DECL and CONST_DECL require nothing. + PARM_DECLs are handled in `assign_parms'. */ + + if (TREE_CODE (decl) != VAR_DECL) + return; + if (TREE_STATIC (decl) || DECL_EXTERNAL (decl)) + return; + + /* Create the RTL representation for the variable. */ + + if (type == error_mark_node) + DECL_RTL (decl) = gen_rtx (MEM, BLKmode, const0_rtx); + else if (DECL_SIZE (decl) == 0) + /* Variable with incomplete type. */ + { + if (DECL_INITIAL (decl) == 0) + /* Error message was already done; now avoid a crash. */ + DECL_RTL (decl) = assign_stack_temp (DECL_MODE (decl), 0, 1); + else + /* An initializer is going to decide the size of this array. + Until we know the size, represent its address with a reg. */ + DECL_RTL (decl) = gen_rtx (MEM, BLKmode, gen_reg_rtx (Pmode)); + } + else if (DECL_MODE (decl) != BLKmode + /* If -ffloat-store, don't put explicit float vars + into regs. */ + && !(flag_float_store + && TREE_CODE (type) == REAL_TYPE) + && ! TREE_THIS_VOLATILE (decl) + && ! TREE_ADDRESSABLE (decl) + && (DECL_REGISTER (decl) || ! obey_regdecls)) + { + /* Automatic variable that can go in a register. */ + enum machine_mode reg_mode = DECL_MODE (decl); + int unsignedp = TREE_UNSIGNED (type); + + if (TREE_CODE (type) == INTEGER_TYPE || TREE_CODE (type) == ENUMERAL_TYPE + || TREE_CODE (type) == BOOLEAN_TYPE || TREE_CODE (type) == CHAR_TYPE + || TREE_CODE (type) == REAL_TYPE || TREE_CODE (type) == POINTER_TYPE + || TREE_CODE (type) == OFFSET_TYPE) + { + PROMOTE_MODE (reg_mode, unsignedp, type); + } + + DECL_RTL (decl) = gen_reg_rtx (reg_mode); + if (TREE_CODE (type) == POINTER_TYPE) + mark_reg_pointer (DECL_RTL (decl)); + REG_USERVAR_P (DECL_RTL (decl)) = 1; + } + else if (TREE_CODE (DECL_SIZE (decl)) == INTEGER_CST) + { + /* Variable of fixed size that goes on the stack. */ + rtx oldaddr = 0; + rtx addr; + + /* If we previously made RTL for this decl, it must be an array + whose size was determined by the initializer. + The old address was a register; set that register now + to the proper address. */ + if (DECL_RTL (decl) != 0) + { + if (GET_CODE (DECL_RTL (decl)) != MEM + || GET_CODE (XEXP (DECL_RTL (decl), 0)) != REG) + abort (); + oldaddr = XEXP (DECL_RTL (decl), 0); + } + + DECL_RTL (decl) + = assign_stack_temp (DECL_MODE (decl), + ((TREE_INT_CST_LOW (DECL_SIZE (decl)) + + BITS_PER_UNIT - 1) + / BITS_PER_UNIT), + 1); + + /* Set alignment we actually gave this decl. */ + DECL_ALIGN (decl) = (DECL_MODE (decl) == BLKmode ? BIGGEST_ALIGNMENT + : GET_MODE_BITSIZE (DECL_MODE (decl))); + + if (oldaddr) + { + addr = force_operand (XEXP (DECL_RTL (decl), 0), oldaddr); + if (addr != oldaddr) + emit_move_insn (oldaddr, addr); + } + + /* If this is a memory ref that contains aggregate components, + mark it as such for cse and loop optimize. */ + MEM_IN_STRUCT_P (DECL_RTL (decl)) + = (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE + || TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE + || TREE_CODE (TREE_TYPE (decl)) == QUAL_UNION_TYPE); +#if 0 + /* If this is in memory because of -ffloat-store, + set the volatile bit, to prevent optimizations from + undoing the effects. */ + if (flag_float_store && TREE_CODE (type) == REAL_TYPE) + MEM_VOLATILE_P (DECL_RTL (decl)) = 1; +#endif + } + else + /* Dynamic-size object: must push space on the stack. */ + { + rtx address, size; + + /* Record the stack pointer on entry to block, if have + not already done so. */ + if (thisblock->data.block.stack_level == 0) + { + do_pending_stack_adjust (); + emit_stack_save (thisblock->next ? SAVE_BLOCK : SAVE_FUNCTION, + &thisblock->data.block.stack_level, + thisblock->data.block.first_insn); + stack_block_stack = thisblock; + } + + /* Compute the variable's size, in bytes. */ + size = expand_expr (size_binop (CEIL_DIV_EXPR, + DECL_SIZE (decl), + size_int (BITS_PER_UNIT)), + NULL_RTX, VOIDmode, 0); + free_temp_slots (); + + /* This is equivalent to calling alloca. */ + current_function_calls_alloca = 1; + + /* Allocate space on the stack for the variable. */ + address = allocate_dynamic_stack_space (size, NULL_RTX, + DECL_ALIGN (decl)); + + if (nonlocal_goto_handler_slot != 0) + emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX); + + /* Reference the variable indirect through that rtx. */ + DECL_RTL (decl) = gen_rtx (MEM, DECL_MODE (decl), address); + + /* If this is a memory ref that contains aggregate components, + mark it as such for cse and loop optimize. */ + MEM_IN_STRUCT_P (DECL_RTL (decl)) + = (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE + || TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE + || TREE_CODE (TREE_TYPE (decl)) == QUAL_UNION_TYPE); + + /* Indicate the alignment we actually gave this variable. */ +#ifdef STACK_BOUNDARY + DECL_ALIGN (decl) = STACK_BOUNDARY; +#else + DECL_ALIGN (decl) = BIGGEST_ALIGNMENT; +#endif + } + + if (TREE_THIS_VOLATILE (decl)) + MEM_VOLATILE_P (DECL_RTL (decl)) = 1; +#if 0 /* A variable is not necessarily unchanging + just because it is const. RTX_UNCHANGING_P + means no change in the function, + not merely no change in the variable's scope. + It is correct to set RTX_UNCHANGING_P if the variable's scope + is the whole function. There's no convenient way to test that. */ + if (TREE_READONLY (decl)) + RTX_UNCHANGING_P (DECL_RTL (decl)) = 1; +#endif + + /* If doing stupid register allocation, make sure life of any + register variable starts here, at the start of its scope. */ + + if (obey_regdecls) + use_variable (DECL_RTL (decl)); +} + +/* Emit code to perform the initialization of a declaration DECL. */ + +void +expand_decl_init (decl) + tree decl; +{ + int was_used = TREE_USED (decl); + + if (TREE_STATIC (decl)) + return; + + /* Compute and store the initial value now. */ + + if (DECL_INITIAL (decl) == error_mark_node) + { + enum tree_code code = TREE_CODE (TREE_TYPE (decl)); + if (code == INTEGER_TYPE || code == REAL_TYPE || code == ENUMERAL_TYPE + || code == POINTER_TYPE) + expand_assignment (decl, convert (TREE_TYPE (decl), integer_zero_node), + 0, 0); + emit_queue (); + } + else if (DECL_INITIAL (decl) && TREE_CODE (DECL_INITIAL (decl)) != TREE_LIST) + { + emit_line_note (DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl)); + expand_assignment (decl, DECL_INITIAL (decl), 0, 0); + emit_queue (); + } + + /* Don't let the initialization count as "using" the variable. */ + TREE_USED (decl) = was_used; + + /* Free any temporaries we made while initializing the decl. */ + free_temp_slots (); +} + +/* CLEANUP is an expression to be executed at exit from this binding contour; + for example, in C++, it might call the destructor for this variable. + + If CLEANUP contains any SAVE_EXPRs, then you must preevaluate them + either before or after calling `expand_decl' but before compiling + any subsequent expressions. This is because CLEANUP may be expanded + more than once, on different branches of execution. + For the same reason, CLEANUP may not contain a CALL_EXPR + except as its topmost node--else `preexpand_calls' would get confused. + + If CLEANUP is nonzero and DECL is zero, we record a cleanup + that is not associated with any particular variable. */ + +int +expand_decl_cleanup (decl, cleanup) + tree decl, cleanup; +{ + struct nesting *thisblock = block_stack; + + /* Error if we are not in any block. */ + if (thisblock == 0) + return 0; + + /* Record the cleanup if there is one. */ + + if (cleanup != 0) + { + thisblock->data.block.cleanups + = temp_tree_cons (decl, cleanup, thisblock->data.block.cleanups); + /* If this block has a cleanup, it belongs in stack_block_stack. */ + stack_block_stack = thisblock; + } + return 1; +} + +/* DECL is an anonymous union. CLEANUP is a cleanup for DECL. + DECL_ELTS is the list of elements that belong to DECL's type. + In each, the TREE_VALUE is a VAR_DECL, and the TREE_PURPOSE a cleanup. */ + +void +expand_anon_union_decl (decl, cleanup, decl_elts) + tree decl, cleanup, decl_elts; +{ + struct nesting *thisblock = block_stack; + rtx x; + + expand_decl (decl, cleanup); + x = DECL_RTL (decl); + + while (decl_elts) + { + tree decl_elt = TREE_VALUE (decl_elts); + tree cleanup_elt = TREE_PURPOSE (decl_elts); + enum machine_mode mode = TYPE_MODE (TREE_TYPE (decl_elt)); + + /* (SUBREG (MEM ...)) at RTL generation time is invalid, so we + instead create a new MEM rtx with the proper mode. */ + if (GET_CODE (x) == MEM) + { + if (mode == GET_MODE (x)) + DECL_RTL (decl_elt) = x; + else + { + DECL_RTL (decl_elt) = gen_rtx (MEM, mode, copy_rtx (XEXP (x, 0))); + MEM_IN_STRUCT_P (DECL_RTL (decl_elt)) = MEM_IN_STRUCT_P (x); + RTX_UNCHANGING_P (DECL_RTL (decl_elt)) = RTX_UNCHANGING_P (x); + } + } + else if (GET_CODE (x) == REG) + { + if (mode == GET_MODE (x)) + DECL_RTL (decl_elt) = x; + else + DECL_RTL (decl_elt) = gen_rtx (SUBREG, mode, x, 0); + } + else + abort (); + + /* Record the cleanup if there is one. */ + + if (cleanup != 0) + thisblock->data.block.cleanups + = temp_tree_cons (decl_elt, cleanup_elt, + thisblock->data.block.cleanups); + + decl_elts = TREE_CHAIN (decl_elts); + } +} + +/* Expand a list of cleanups LIST. + Elements may be expressions or may be nested lists. + + If DONT_DO is nonnull, then any list-element + whose TREE_PURPOSE matches DONT_DO is omitted. + This is sometimes used to avoid a cleanup associated with + a value that is being returned out of the scope. */ + +static void +expand_cleanups (list, dont_do) + tree list; + tree dont_do; +{ + tree tail; + for (tail = list; tail; tail = TREE_CHAIN (tail)) + if (dont_do == 0 || TREE_PURPOSE (tail) != dont_do) + { + if (TREE_CODE (TREE_VALUE (tail)) == TREE_LIST) + expand_cleanups (TREE_VALUE (tail), dont_do); + else + { + /* Cleanups may be run multiple times. For example, + when exiting a binding contour, we expand the + cleanups associated with that contour. When a goto + within that binding contour has a target outside that + contour, it will expand all cleanups from its scope to + the target. Though the cleanups are expanded multiple + times, the control paths are non-overlapping so the + cleanups will not be executed twice. */ + expand_expr (TREE_VALUE (tail), const0_rtx, VOIDmode, 0); + free_temp_slots (); + } + } +} + +/* Move all cleanups from the current block_stack + to the containing block_stack, where they are assumed to + have been created. If anything can cause a temporary to + be created, but not expanded for more than one level of + block_stacks, then this code will have to change. */ + +void +move_cleanups_up () +{ + struct nesting *block = block_stack; + struct nesting *outer = block->next; + + outer->data.block.cleanups + = chainon (block->data.block.cleanups, + outer->data.block.cleanups); + block->data.block.cleanups = 0; +} + +tree +last_cleanup_this_contour () +{ + if (block_stack == 0) + return 0; + + return block_stack->data.block.cleanups; +} + +/* Return 1 if there are any pending cleanups at this point. + If THIS_CONTOUR is nonzero, check the current contour as well. + Otherwise, look only at the contours that enclose this one. */ + +int +any_pending_cleanups (this_contour) + int this_contour; +{ + struct nesting *block; + + if (block_stack == 0) + return 0; + + if (this_contour && block_stack->data.block.cleanups != NULL) + return 1; + if (block_stack->data.block.cleanups == 0 + && (block_stack->data.block.outer_cleanups == 0 +#if 0 + || block_stack->data.block.outer_cleanups == empty_cleanup_list +#endif + )) + return 0; + + for (block = block_stack->next; block; block = block->next) + if (block->data.block.cleanups != 0) + return 1; + + return 0; +} + +/* Enter a case (Pascal) or switch (C) statement. + Push a block onto case_stack and nesting_stack + to accumulate the case-labels that are seen + and to record the labels generated for the statement. + + EXIT_FLAG is nonzero if `exit_something' should exit this case stmt. + Otherwise, this construct is transparent for `exit_something'. + + EXPR is the index-expression to be dispatched on. + TYPE is its nominal type. We could simply convert EXPR to this type, + but instead we take short cuts. */ + +void +expand_start_case (exit_flag, expr, type, printname) + int exit_flag; + tree expr; + tree type; + char *printname; +{ + register struct nesting *thiscase = ALLOC_NESTING (); + + /* Make an entry on case_stack for the case we are entering. */ + + thiscase->next = case_stack; + thiscase->all = nesting_stack; + thiscase->depth = ++nesting_depth; + thiscase->exit_label = exit_flag ? gen_label_rtx () : 0; + thiscase->data.case_stmt.case_list = 0; + thiscase->data.case_stmt.index_expr = expr; + thiscase->data.case_stmt.nominal_type = type; + thiscase->data.case_stmt.default_label = 0; + thiscase->data.case_stmt.num_ranges = 0; + thiscase->data.case_stmt.printname = printname; + thiscase->data.case_stmt.seenlabel = 0; + case_stack = thiscase; + nesting_stack = thiscase; + + do_pending_stack_adjust (); + + /* Make sure case_stmt.start points to something that won't + need any transformation before expand_end_case. */ + if (GET_CODE (get_last_insn ()) != NOTE) + emit_note (NULL_PTR, NOTE_INSN_DELETED); + + thiscase->data.case_stmt.start = get_last_insn (); +} + +/* Start a "dummy case statement" within which case labels are invalid + and are not connected to any larger real case statement. + This can be used if you don't want to let a case statement jump + into the middle of certain kinds of constructs. */ + +void +expand_start_case_dummy () +{ + register struct nesting *thiscase = ALLOC_NESTING (); + + /* Make an entry on case_stack for the dummy. */ + + thiscase->next = case_stack; + thiscase->all = nesting_stack; + thiscase->depth = ++nesting_depth; + thiscase->exit_label = 0; + thiscase->data.case_stmt.case_list = 0; + thiscase->data.case_stmt.start = 0; + thiscase->data.case_stmt.nominal_type = 0; + thiscase->data.case_stmt.default_label = 0; + thiscase->data.case_stmt.num_ranges = 0; + case_stack = thiscase; + nesting_stack = thiscase; +} + +/* End a dummy case statement. */ + +void +expand_end_case_dummy () +{ + POPSTACK (case_stack); +} + +/* Return the data type of the index-expression + of the innermost case statement, or null if none. */ + +tree +case_index_expr_type () +{ + if (case_stack) + return TREE_TYPE (case_stack->data.case_stmt.index_expr); + return 0; +} + +/* Accumulate one case or default label inside a case or switch statement. + VALUE is the value of the case (a null pointer, for a default label). + + If not currently inside a case or switch statement, return 1 and do + nothing. The caller will print a language-specific error message. + If VALUE is a duplicate or overlaps, return 2 and do nothing + except store the (first) duplicate node in *DUPLICATE. + If VALUE is out of range, return 3 and do nothing. + If we are jumping into the scope of a cleaup or var-sized array, return 5. + Return 0 on success. + + Extended to handle range statements. */ + +int +pushcase (value, label, duplicate) + register tree value; + register tree label; + tree *duplicate; +{ + register struct case_node **l; + register struct case_node *n; + tree index_type; + tree nominal_type; + + /* Fail if not inside a real case statement. */ + if (! (case_stack && case_stack->data.case_stmt.start)) + return 1; + + if (stack_block_stack + && stack_block_stack->depth > case_stack->depth) + return 5; + + index_type = TREE_TYPE (case_stack->data.case_stmt.index_expr); + nominal_type = case_stack->data.case_stmt.nominal_type; + + /* If the index is erroneous, avoid more problems: pretend to succeed. */ + if (index_type == error_mark_node) + return 0; + + /* Convert VALUE to the type in which the comparisons are nominally done. */ + if (value != 0) + value = convert (nominal_type, value); + + /* If this is the first label, warn if any insns have been emitted. */ + if (case_stack->data.case_stmt.seenlabel == 0) + { + rtx insn; + for (insn = case_stack->data.case_stmt.start; + insn; + insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == CODE_LABEL) + break; + if (GET_CODE (insn) != NOTE + && (GET_CODE (insn) != INSN || GET_CODE (PATTERN (insn)) != USE)) + { + warning ("unreachable code at beginning of %s", + case_stack->data.case_stmt.printname); + break; + } + } + } + case_stack->data.case_stmt.seenlabel = 1; + + /* Fail if this value is out of range for the actual type of the index + (which may be narrower than NOMINAL_TYPE). */ + if (value != 0 && ! int_fits_type_p (value, index_type)) + return 3; + + /* Fail if this is a duplicate or overlaps another entry. */ + if (value == 0) + { + if (case_stack->data.case_stmt.default_label != 0) + { + *duplicate = case_stack->data.case_stmt.default_label; + return 2; + } + case_stack->data.case_stmt.default_label = label; + } + else + { + /* Find the elt in the chain before which to insert the new value, + to keep the chain sorted in increasing order. + But report an error if this element is a duplicate. */ + for (l = &case_stack->data.case_stmt.case_list; + /* Keep going past elements distinctly less than VALUE. */ + *l != 0 && tree_int_cst_lt ((*l)->high, value); + l = &(*l)->right) + ; + if (*l) + { + /* Element we will insert before must be distinctly greater; + overlap means error. */ + if (! tree_int_cst_lt (value, (*l)->low)) + { + *duplicate = (*l)->code_label; + return 2; + } + } + + /* Add this label to the chain, and succeed. + Copy VALUE so it is on temporary rather than momentary + obstack and will thus survive till the end of the case statement. */ + n = (struct case_node *) oballoc (sizeof (struct case_node)); + n->left = 0; + n->right = *l; + n->high = n->low = copy_node (value); + n->code_label = label; + *l = n; + } + + expand_label (label); + return 0; +} + +/* Like pushcase but this case applies to all values + between VALUE1 and VALUE2 (inclusive). + The return value is the same as that of pushcase + but there is one additional error code: + 4 means the specified range was empty. */ + +int +pushcase_range (value1, value2, label, duplicate) + register tree value1, value2; + register tree label; + tree *duplicate; +{ + register struct case_node **l; + register struct case_node *n; + tree index_type; + tree nominal_type; + + /* Fail if not inside a real case statement. */ + if (! (case_stack && case_stack->data.case_stmt.start)) + return 1; + + if (stack_block_stack + && stack_block_stack->depth > case_stack->depth) + return 5; + + index_type = TREE_TYPE (case_stack->data.case_stmt.index_expr); + nominal_type = case_stack->data.case_stmt.nominal_type; + + /* If the index is erroneous, avoid more problems: pretend to succeed. */ + if (index_type == error_mark_node) + return 0; + + /* If this is the first label, warn if any insns have been emitted. */ + if (case_stack->data.case_stmt.seenlabel == 0) + { + rtx insn; + for (insn = case_stack->data.case_stmt.start; + insn; + insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == CODE_LABEL) + break; + if (GET_CODE (insn) != NOTE + && (GET_CODE (insn) != INSN || GET_CODE (PATTERN (insn)) != USE)) + { + warning ("unreachable code at beginning of %s", + case_stack->data.case_stmt.printname); + break; + } + } + } + case_stack->data.case_stmt.seenlabel = 1; + + /* Convert VALUEs to type in which the comparisons are nominally done. */ + if (value1 == 0) /* Negative infinity. */ + value1 = TYPE_MIN_VALUE(index_type); + value1 = convert (nominal_type, value1); + + if (value2 == 0) /* Positive infinity. */ + value2 = TYPE_MAX_VALUE(index_type); + value2 = convert (nominal_type, value2); + + /* Fail if these values are out of range. */ + if (! int_fits_type_p (value1, index_type)) + return 3; + + if (! int_fits_type_p (value2, index_type)) + return 3; + + /* Fail if the range is empty. */ + if (tree_int_cst_lt (value2, value1)) + return 4; + + /* If the bounds are equal, turn this into the one-value case. */ + if (tree_int_cst_equal (value1, value2)) + return pushcase (value1, label, duplicate); + + /* Find the elt in the chain before which to insert the new value, + to keep the chain sorted in increasing order. + But report an error if this element is a duplicate. */ + for (l = &case_stack->data.case_stmt.case_list; + /* Keep going past elements distinctly less than this range. */ + *l != 0 && tree_int_cst_lt ((*l)->high, value1); + l = &(*l)->right) + ; + if (*l) + { + /* Element we will insert before must be distinctly greater; + overlap means error. */ + if (! tree_int_cst_lt (value2, (*l)->low)) + { + *duplicate = (*l)->code_label; + return 2; + } + } + + /* Add this label to the chain, and succeed. + Copy VALUE1, VALUE2 so they are on temporary rather than momentary + obstack and will thus survive till the end of the case statement. */ + + n = (struct case_node *) oballoc (sizeof (struct case_node)); + n->left = 0; + n->right = *l; + n->low = copy_node (value1); + n->high = copy_node (value2); + n->code_label = label; + *l = n; + + expand_label (label); + + case_stack->data.case_stmt.num_ranges++; + + return 0; +} + +/* Called when the index of a switch statement is an enumerated type + and there is no default label. + + Checks that all enumeration literals are covered by the case + expressions of a switch. Also, warn if there are any extra + switch cases that are *not* elements of the enumerated type. + + If all enumeration literals were covered by the case expressions, + turn one of the expressions into the default expression since it should + not be possible to fall through such a switch. */ + +void +check_for_full_enumeration_handling (type) + tree type; +{ + register struct case_node *n; + register struct case_node **l; + register tree chain; + int all_values = 1; + + /* The time complexity of this loop is currently O(N * M), with + N being the number of members in the enumerated type, and + M being the number of case expressions in the switch. */ + + for (chain = TYPE_VALUES (type); + chain; + chain = TREE_CHAIN (chain)) + { + /* Find a match between enumeral and case expression, if possible. + Quit looking when we've gone too far (since case expressions + are kept sorted in ascending order). Warn about enumerators not + handled in the switch statement case expression list. */ + + for (n = case_stack->data.case_stmt.case_list; + n && tree_int_cst_lt (n->high, TREE_VALUE (chain)); + n = n->right) + ; + + if (!n || tree_int_cst_lt (TREE_VALUE (chain), n->low)) + { + if (warn_switch) + warning ("enumeration value `%s' not handled in switch", + IDENTIFIER_POINTER (TREE_PURPOSE (chain))); + all_values = 0; + } + } + + /* Now we go the other way around; we warn if there are case + expressions that don't correspond to enumerators. This can + occur since C and C++ don't enforce type-checking of + assignments to enumeration variables. */ + + if (warn_switch) + for (n = case_stack->data.case_stmt.case_list; n; n = n->right) + { + for (chain = TYPE_VALUES (type); + chain && !tree_int_cst_equal (n->low, TREE_VALUE (chain)); + chain = TREE_CHAIN (chain)) + ; + + if (!chain) + { + if (TYPE_NAME (type) == 0) + warning ("case value `%d' not in enumerated type", + TREE_INT_CST_LOW (n->low)); + else + warning ("case value `%d' not in enumerated type `%s'", + TREE_INT_CST_LOW (n->low), + IDENTIFIER_POINTER ((TREE_CODE (TYPE_NAME (type)) + == IDENTIFIER_NODE) + ? TYPE_NAME (type) + : DECL_NAME (TYPE_NAME (type)))); + } + if (!tree_int_cst_equal (n->low, n->high)) + { + for (chain = TYPE_VALUES (type); + chain && !tree_int_cst_equal (n->high, TREE_VALUE (chain)); + chain = TREE_CHAIN (chain)) + ; + + if (!chain) + { + if (TYPE_NAME (type) == 0) + warning ("case value `%d' not in enumerated type", + TREE_INT_CST_LOW (n->high)); + else + warning ("case value `%d' not in enumerated type `%s'", + TREE_INT_CST_LOW (n->high), + IDENTIFIER_POINTER ((TREE_CODE (TYPE_NAME (type)) + == IDENTIFIER_NODE) + ? TYPE_NAME (type) + : DECL_NAME (TYPE_NAME (type)))); + } + } + } + +#if 0 + /* ??? This optimization is disabled because it causes valid programs to + fail. ANSI C does not guarantee that an expression with enum type + will have a value that is the same as one of the enumation literals. */ + + /* If all values were found as case labels, make one of them the default + label. Thus, this switch will never fall through. We arbitrarily pick + the last one to make the default since this is likely the most + efficient choice. */ + + if (all_values) + { + for (l = &case_stack->data.case_stmt.case_list; + (*l)->right != 0; + l = &(*l)->right) + ; + + case_stack->data.case_stmt.default_label = (*l)->code_label; + *l = 0; + } +#endif /* 0 */ +} + +/* Terminate a case (Pascal) or switch (C) statement + in which ORIG_INDEX is the expression to be tested. + Generate the code to test it and jump to the right place. */ + +void +expand_end_case (orig_index) + tree orig_index; +{ + tree minval, maxval, range; + rtx default_label = 0; + register struct case_node *n; + int count; + rtx index; + rtx table_label = gen_label_rtx (); + int ncases; + rtx *labelvec; + register int i; + rtx before_case; + register struct nesting *thiscase = case_stack; + tree index_expr = thiscase->data.case_stmt.index_expr; + int unsignedp = TREE_UNSIGNED (TREE_TYPE (index_expr)); + + do_pending_stack_adjust (); + + /* An ERROR_MARK occurs for various reasons including invalid data type. */ + if (TREE_TYPE (index_expr) != error_mark_node) + { + /* If switch expression was an enumerated type, check that all + enumeration literals are covered by the cases. + No sense trying this if there's a default case, however. */ + + if (!thiscase->data.case_stmt.default_label + && TREE_CODE (TREE_TYPE (orig_index)) == ENUMERAL_TYPE + && TREE_CODE (index_expr) != INTEGER_CST) + check_for_full_enumeration_handling (TREE_TYPE (orig_index)); + + /* If this is the first label, warn if any insns have been emitted. */ + if (thiscase->data.case_stmt.seenlabel == 0) + { + rtx insn; + for (insn = get_last_insn (); + insn != case_stack->data.case_stmt.start; + insn = PREV_INSN (insn)) + if (GET_CODE (insn) != NOTE + && (GET_CODE (insn) != INSN || GET_CODE (PATTERN (insn))!= USE)) + { + warning ("unreachable code at beginning of %s", + case_stack->data.case_stmt.printname); + break; + } + } + + /* If we don't have a default-label, create one here, + after the body of the switch. */ + if (thiscase->data.case_stmt.default_label == 0) + { + thiscase->data.case_stmt.default_label + = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + expand_label (thiscase->data.case_stmt.default_label); + } + default_label = label_rtx (thiscase->data.case_stmt.default_label); + + before_case = get_last_insn (); + + /* Simplify the case-list before we count it. */ + group_case_nodes (thiscase->data.case_stmt.case_list); + + /* Get upper and lower bounds of case values. + Also convert all the case values to the index expr's data type. */ + + count = 0; + for (n = thiscase->data.case_stmt.case_list; n; n = n->right) + { + /* Check low and high label values are integers. */ + if (TREE_CODE (n->low) != INTEGER_CST) + abort (); + if (TREE_CODE (n->high) != INTEGER_CST) + abort (); + + n->low = convert (TREE_TYPE (index_expr), n->low); + n->high = convert (TREE_TYPE (index_expr), n->high); + + /* Count the elements and track the largest and smallest + of them (treating them as signed even if they are not). */ + if (count++ == 0) + { + minval = n->low; + maxval = n->high; + } + else + { + if (INT_CST_LT (n->low, minval)) + minval = n->low; + if (INT_CST_LT (maxval, n->high)) + maxval = n->high; + } + /* A range counts double, since it requires two compares. */ + if (! tree_int_cst_equal (n->low, n->high)) + count++; + } + + /* Compute span of values. */ + if (count != 0) + range = fold (build (MINUS_EXPR, TREE_TYPE (index_expr), + maxval, minval)); + + if (count == 0 || TREE_CODE (TREE_TYPE (index_expr)) == ERROR_MARK) + { + expand_expr (index_expr, const0_rtx, VOIDmode, 0); + emit_queue (); + emit_jump (default_label); + } + /* If range of values is much bigger than number of values, + make a sequence of conditional branches instead of a dispatch. + If the switch-index is a constant, do it this way + because we can optimize it. */ + +#ifndef CASE_VALUES_THRESHOLD +#ifdef HAVE_casesi +#define CASE_VALUES_THRESHOLD (HAVE_casesi ? 4 : 5) +#else + /* If machine does not have a case insn that compares the + bounds, this means extra overhead for dispatch tables + which raises the threshold for using them. */ +#define CASE_VALUES_THRESHOLD 5 +#endif /* HAVE_casesi */ +#endif /* CASE_VALUES_THRESHOLD */ + + else if (TREE_INT_CST_HIGH (range) != 0 + || count < CASE_VALUES_THRESHOLD + || ((unsigned HOST_WIDE_INT) (TREE_INT_CST_LOW (range)) + > 10 * count) + || TREE_CODE (index_expr) == INTEGER_CST + /* These will reduce to a constant. */ + || (TREE_CODE (index_expr) == CALL_EXPR + && TREE_CODE (TREE_OPERAND (index_expr, 0)) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (TREE_OPERAND (index_expr, 0), 0)) == FUNCTION_DECL + && DECL_FUNCTION_CODE (TREE_OPERAND (TREE_OPERAND (index_expr, 0), 0)) == BUILT_IN_CLASSIFY_TYPE) + || (TREE_CODE (index_expr) == COMPOUND_EXPR + && TREE_CODE (TREE_OPERAND (index_expr, 1)) == INTEGER_CST)) + { + index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0); + + /* If the index is a short or char that we do not have + an insn to handle comparisons directly, convert it to + a full integer now, rather than letting each comparison + generate the conversion. */ + + if (GET_MODE_CLASS (GET_MODE (index)) == MODE_INT + && (cmp_optab->handlers[(int) GET_MODE(index)].insn_code + == CODE_FOR_nothing)) + { + enum machine_mode wider_mode; + for (wider_mode = GET_MODE (index); wider_mode != VOIDmode; + wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + if (cmp_optab->handlers[(int) wider_mode].insn_code + != CODE_FOR_nothing) + { + index = convert_to_mode (wider_mode, index, unsignedp); + break; + } + } + + emit_queue (); + do_pending_stack_adjust (); + + index = protect_from_queue (index, 0); + if (GET_CODE (index) == MEM) + index = copy_to_reg (index); + if (GET_CODE (index) == CONST_INT + || TREE_CODE (index_expr) == INTEGER_CST) + { + /* Make a tree node with the proper constant value + if we don't already have one. */ + if (TREE_CODE (index_expr) != INTEGER_CST) + { + index_expr + = build_int_2 (INTVAL (index), + !unsignedp && INTVAL (index) >= 0 ? 0 : -1); + index_expr = convert (TREE_TYPE (index_expr), index_expr); + } + + /* For constant index expressions we need only + issue a unconditional branch to the appropriate + target code. The job of removing any unreachable + code is left to the optimisation phase if the + "-O" option is specified. */ + for (n = thiscase->data.case_stmt.case_list; + n; + n = n->right) + { + if (! tree_int_cst_lt (index_expr, n->low) + && ! tree_int_cst_lt (n->high, index_expr)) + break; + } + if (n) + emit_jump (label_rtx (n->code_label)); + else + emit_jump (default_label); + } + else + { + /* If the index expression is not constant we generate + a binary decision tree to select the appropriate + target code. This is done as follows: + + The list of cases is rearranged into a binary tree, + nearly optimal assuming equal probability for each case. + + The tree is transformed into RTL, eliminating + redundant test conditions at the same time. + + If program flow could reach the end of the + decision tree an unconditional jump to the + default code is emitted. */ + + use_cost_table + = (TREE_CODE (TREE_TYPE (orig_index)) != ENUMERAL_TYPE + && estimate_case_costs (thiscase->data.case_stmt.case_list)); + balance_case_nodes (&thiscase->data.case_stmt.case_list, + NULL_PTR); + emit_case_nodes (index, thiscase->data.case_stmt.case_list, + default_label, TREE_TYPE (index_expr)); + emit_jump_if_reachable (default_label); + } + } + else + { + int win = 0; +#ifdef HAVE_casesi + if (HAVE_casesi) + { + enum machine_mode index_mode = SImode; + int index_bits = GET_MODE_BITSIZE (index_mode); + + /* Convert the index to SImode. */ + if (GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE (index_expr))) + > GET_MODE_BITSIZE (index_mode)) + { + enum machine_mode omode = TYPE_MODE (TREE_TYPE (index_expr)); + rtx rangertx = expand_expr (range, NULL_RTX, VOIDmode, 0); + + /* We must handle the endpoints in the original mode. */ + index_expr = build (MINUS_EXPR, TREE_TYPE (index_expr), + index_expr, minval); + minval = integer_zero_node; + index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0); + emit_cmp_insn (rangertx, index, LTU, NULL_RTX, omode, 0, 0); + emit_jump_insn (gen_bltu (default_label)); + /* Now we can safely truncate. */ + index = convert_to_mode (index_mode, index, 0); + } + else + { + if (TYPE_MODE (TREE_TYPE (index_expr)) != index_mode) + index_expr = convert (type_for_size (index_bits, 0), + index_expr); + index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0); + } + emit_queue (); + index = protect_from_queue (index, 0); + do_pending_stack_adjust (); + + emit_jump_insn (gen_casesi (index, expand_expr (minval, NULL_RTX, + VOIDmode, 0), + expand_expr (range, NULL_RTX, + VOIDmode, 0), + table_label, default_label)); + win = 1; + } +#endif +#ifdef HAVE_tablejump + if (! win && HAVE_tablejump) + { + index_expr = convert (thiscase->data.case_stmt.nominal_type, + fold (build (MINUS_EXPR, + TREE_TYPE (index_expr), + index_expr, minval))); + index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0); + emit_queue (); + index = protect_from_queue (index, 0); + do_pending_stack_adjust (); + + do_tablejump (index, TYPE_MODE (TREE_TYPE (index_expr)), + expand_expr (range, NULL_RTX, VOIDmode, 0), + table_label, default_label); + win = 1; + } +#endif + if (! win) + abort (); + + /* Get table of labels to jump to, in order of case index. */ + + ncases = TREE_INT_CST_LOW (range) + 1; + labelvec = (rtx *) alloca (ncases * sizeof (rtx)); + bzero (labelvec, ncases * sizeof (rtx)); + + for (n = thiscase->data.case_stmt.case_list; n; n = n->right) + { + register HOST_WIDE_INT i + = TREE_INT_CST_LOW (n->low) - TREE_INT_CST_LOW (minval); + + while (1) + { + labelvec[i] + = gen_rtx (LABEL_REF, Pmode, label_rtx (n->code_label)); + if (i + TREE_INT_CST_LOW (minval) + == TREE_INT_CST_LOW (n->high)) + break; + i++; + } + } + + /* Fill in the gaps with the default. */ + for (i = 0; i < ncases; i++) + if (labelvec[i] == 0) + labelvec[i] = gen_rtx (LABEL_REF, Pmode, default_label); + + /* Output the table */ + emit_label (table_label); + + /* This would be a lot nicer if CASE_VECTOR_PC_RELATIVE + were an expression, instead of an #ifdef/#ifndef. */ + if ( +#ifdef CASE_VECTOR_PC_RELATIVE + 1 || +#endif + flag_pic) + emit_jump_insn (gen_rtx (ADDR_DIFF_VEC, CASE_VECTOR_MODE, + gen_rtx (LABEL_REF, Pmode, table_label), + gen_rtvec_v (ncases, labelvec))); + else + emit_jump_insn (gen_rtx (ADDR_VEC, CASE_VECTOR_MODE, + gen_rtvec_v (ncases, labelvec))); + + /* If the case insn drops through the table, + after the table we must jump to the default-label. + Otherwise record no drop-through after the table. */ +#ifdef CASE_DROPS_THROUGH + emit_jump (default_label); +#else + emit_barrier (); +#endif + } + + before_case = squeeze_notes (NEXT_INSN (before_case), get_last_insn ()); + reorder_insns (before_case, get_last_insn (), + thiscase->data.case_stmt.start); + } + if (thiscase->exit_label) + emit_label (thiscase->exit_label); + + POPSTACK (case_stack); + + free_temp_slots (); +} + +/* Generate code to jump to LABEL if OP1 and OP2 are equal. */ + +static void +do_jump_if_equal (op1, op2, label, unsignedp) + rtx op1, op2, label; + int unsignedp; +{ + if (GET_CODE (op1) == CONST_INT + && GET_CODE (op2) == CONST_INT) + { + if (INTVAL (op1) == INTVAL (op2)) + emit_jump (label); + } + else + { + enum machine_mode mode = GET_MODE (op1); + if (mode == VOIDmode) + mode = GET_MODE (op2); + emit_cmp_insn (op1, op2, EQ, NULL_RTX, mode, unsignedp, 0); + emit_jump_insn (gen_beq (label)); + } +} + +/* Not all case values are encountered equally. This function + uses a heuristic to weight case labels, in cases where that + looks like a reasonable thing to do. + + Right now, all we try to guess is text, and we establish the + following weights: + + chars above space: 16 + digits: 16 + default: 12 + space, punct: 8 + tab: 4 + newline: 2 + other "\" chars: 1 + remaining chars: 0 + + If we find any cases in the switch that are not either -1 or in the range + of valid ASCII characters, or are control characters other than those + commonly used with "\", don't treat this switch scanning text. + + Return 1 if these nodes are suitable for cost estimation, otherwise + return 0. */ + +static int +estimate_case_costs (node) + case_node_ptr node; +{ + tree min_ascii = build_int_2 (-1, -1); + tree max_ascii = convert (TREE_TYPE (node->high), build_int_2 (127, 0)); + case_node_ptr n; + int i; + + /* If we haven't already made the cost table, make it now. Note that the + lower bound of the table is -1, not zero. */ + + if (cost_table == NULL) + { + cost_table = ((short *) xmalloc (129 * sizeof (short))) + 1; + bzero (cost_table - 1, 129 * sizeof (short)); + + for (i = 0; i < 128; i++) + { + if (isalnum (i)) + cost_table[i] = 16; + else if (ispunct (i)) + cost_table[i] = 8; + else if (iscntrl (i)) + cost_table[i] = -1; + } + + cost_table[' '] = 8; + cost_table['\t'] = 4; + cost_table['\0'] = 4; + cost_table['\n'] = 2; + cost_table['\f'] = 1; + cost_table['\v'] = 1; + cost_table['\b'] = 1; + } + + /* See if all the case expressions look like text. It is text if the + constant is >= -1 and the highest constant is <= 127. Do all comparisons + as signed arithmetic since we don't want to ever access cost_table with a + value less than -1. Also check that none of the constants in a range + are strange control characters. */ + + for (n = node; n; n = n->right) + { + if ((INT_CST_LT (n->low, min_ascii)) || INT_CST_LT (max_ascii, n->high)) + return 0; + + for (i = TREE_INT_CST_LOW (n->low); i <= TREE_INT_CST_LOW (n->high); i++) + if (cost_table[i] < 0) + return 0; + } + + /* All interesting values are within the range of interesting + ASCII characters. */ + return 1; +} + +/* Scan an ordered list of case nodes + combining those with consecutive values or ranges. + + Eg. three separate entries 1: 2: 3: become one entry 1..3: */ + +static void +group_case_nodes (head) + case_node_ptr head; +{ + case_node_ptr node = head; + + while (node) + { + rtx lb = next_real_insn (label_rtx (node->code_label)); + case_node_ptr np = node; + + /* Try to group the successors of NODE with NODE. */ + while (((np = np->right) != 0) + /* Do they jump to the same place? */ + && next_real_insn (label_rtx (np->code_label)) == lb + /* Are their ranges consecutive? */ + && tree_int_cst_equal (np->low, + fold (build (PLUS_EXPR, + TREE_TYPE (node->high), + node->high, + integer_one_node))) + /* An overflow is not consecutive. */ + && tree_int_cst_lt (node->high, + fold (build (PLUS_EXPR, + TREE_TYPE (node->high), + node->high, + integer_one_node)))) + { + node->high = np->high; + } + /* NP is the first node after NODE which can't be grouped with it. + Delete the nodes in between, and move on to that node. */ + node->right = np; + node = np; + } +} + +/* Take an ordered list of case nodes + and transform them into a near optimal binary tree, + on the assumption that any target code selection value is as + likely as any other. + + The transformation is performed by splitting the ordered + list into two equal sections plus a pivot. The parts are + then attached to the pivot as left and right branches. Each + branch is is then transformed recursively. */ + +static void +balance_case_nodes (head, parent) + case_node_ptr *head; + case_node_ptr parent; +{ + register case_node_ptr np; + + np = *head; + if (np) + { + int cost = 0; + int i = 0; + int ranges = 0; + register case_node_ptr *npp; + case_node_ptr left; + + /* Count the number of entries on branch. Also count the ranges. */ + + while (np) + { + if (!tree_int_cst_equal (np->low, np->high)) + { + ranges++; + if (use_cost_table) + cost += cost_table[TREE_INT_CST_LOW (np->high)]; + } + + if (use_cost_table) + cost += cost_table[TREE_INT_CST_LOW (np->low)]; + + i++; + np = np->right; + } + + if (i > 2) + { + /* Split this list if it is long enough for that to help. */ + npp = head; + left = *npp; + if (use_cost_table) + { + /* Find the place in the list that bisects the list's total cost, + Here I gets half the total cost. */ + int n_moved = 0; + i = (cost + 1) / 2; + while (1) + { + /* Skip nodes while their cost does not reach that amount. */ + if (!tree_int_cst_equal ((*npp)->low, (*npp)->high)) + i -= cost_table[TREE_INT_CST_LOW ((*npp)->high)]; + i -= cost_table[TREE_INT_CST_LOW ((*npp)->low)]; + if (i <= 0) + break; + npp = &(*npp)->right; + n_moved += 1; + } + if (n_moved == 0) + { + /* Leave this branch lopsided, but optimize left-hand + side and fill in `parent' fields for right-hand side. */ + np = *head; + np->parent = parent; + balance_case_nodes (&np->left, np); + for (; np->right; np = np->right) + np->right->parent = np; + return; + } + } + /* If there are just three nodes, split at the middle one. */ + else if (i == 3) + npp = &(*npp)->right; + else + { + /* Find the place in the list that bisects the list's total cost, + where ranges count as 2. + Here I gets half the total cost. */ + i = (i + ranges + 1) / 2; + while (1) + { + /* Skip nodes while their cost does not reach that amount. */ + if (!tree_int_cst_equal ((*npp)->low, (*npp)->high)) + i--; + i--; + if (i <= 0) + break; + npp = &(*npp)->right; + } + } + *head = np = *npp; + *npp = 0; + np->parent = parent; + np->left = left; + + /* Optimize each of the two split parts. */ + balance_case_nodes (&np->left, np); + balance_case_nodes (&np->right, np); + } + else + { + /* Else leave this branch as one level, + but fill in `parent' fields. */ + np = *head; + np->parent = parent; + for (; np->right; np = np->right) + np->right->parent = np; + } + } +} + +/* Search the parent sections of the case node tree + to see if a test for the lower bound of NODE would be redundant. + INDEX_TYPE is the type of the index expression. + + The instructions to generate the case decision tree are + output in the same order as nodes are processed so it is + known that if a parent node checks the range of the current + node minus one that the current node is bounded at its lower + span. Thus the test would be redundant. */ + +static int +node_has_low_bound (node, index_type) + case_node_ptr node; + tree index_type; +{ + tree low_minus_one; + case_node_ptr pnode; + + /* If the lower bound of this node is the lowest value in the index type, + we need not test it. */ + + if (tree_int_cst_equal (node->low, TYPE_MIN_VALUE (index_type))) + return 1; + + /* If this node has a left branch, the value at the left must be less + than that at this node, so it cannot be bounded at the bottom and + we need not bother testing any further. */ + + if (node->left) + return 0; + + low_minus_one = fold (build (MINUS_EXPR, TREE_TYPE (node->low), + node->low, integer_one_node)); + + /* If the subtraction above overflowed, we can't verify anything. + Otherwise, look for a parent that tests our value - 1. */ + + if (! tree_int_cst_lt (low_minus_one, node->low)) + return 0; + + for (pnode = node->parent; pnode; pnode = pnode->parent) + if (tree_int_cst_equal (low_minus_one, pnode->high)) + return 1; + + return 0; +} + +/* Search the parent sections of the case node tree + to see if a test for the upper bound of NODE would be redundant. + INDEX_TYPE is the type of the index expression. + + The instructions to generate the case decision tree are + output in the same order as nodes are processed so it is + known that if a parent node checks the range of the current + node plus one that the current node is bounded at its upper + span. Thus the test would be redundant. */ + +static int +node_has_high_bound (node, index_type) + case_node_ptr node; + tree index_type; +{ + tree high_plus_one; + case_node_ptr pnode; + + /* If the upper bound of this node is the highest value in the type + of the index expression, we need not test against it. */ + + if (tree_int_cst_equal (node->high, TYPE_MAX_VALUE (index_type))) + return 1; + + /* If this node has a right branch, the value at the right must be greater + than that at this node, so it cannot be bounded at the top and + we need not bother testing any further. */ + + if (node->right) + return 0; + + high_plus_one = fold (build (PLUS_EXPR, TREE_TYPE (node->high), + node->high, integer_one_node)); + + /* If the addition above overflowed, we can't verify anything. + Otherwise, look for a parent that tests our value + 1. */ + + if (! tree_int_cst_lt (node->high, high_plus_one)) + return 0; + + for (pnode = node->parent; pnode; pnode = pnode->parent) + if (tree_int_cst_equal (high_plus_one, pnode->low)) + return 1; + + return 0; +} + +/* Search the parent sections of the + case node tree to see if both tests for the upper and lower + bounds of NODE would be redundant. */ + +static int +node_is_bounded (node, index_type) + case_node_ptr node; + tree index_type; +{ + return (node_has_low_bound (node, index_type) + && node_has_high_bound (node, index_type)); +} + +/* Emit an unconditional jump to LABEL unless it would be dead code. */ + +static void +emit_jump_if_reachable (label) + rtx label; +{ + if (GET_CODE (get_last_insn ()) != BARRIER) + emit_jump (label); +} + +/* Emit step-by-step code to select a case for the value of INDEX. + The thus generated decision tree follows the form of the + case-node binary tree NODE, whose nodes represent test conditions. + INDEX_TYPE is the type of the index of the switch. + + Care is taken to prune redundant tests from the decision tree + by detecting any boundary conditions already checked by + emitted rtx. (See node_has_high_bound, node_has_low_bound + and node_is_bounded, above.) + + Where the test conditions can be shown to be redundant we emit + an unconditional jump to the target code. As a further + optimization, the subordinates of a tree node are examined to + check for bounded nodes. In this case conditional and/or + unconditional jumps as a result of the boundary check for the + current node are arranged to target the subordinates associated + code for out of bound conditions on the current node node. + + We can assume that when control reaches the code generated here, + the index value has already been compared with the parents + of this node, and determined to be on the same side of each parent + as this node is. Thus, if this node tests for the value 51, + and a parent tested for 52, we don't need to consider + the possibility of a value greater than 51. If another parent + tests for the value 50, then this node need not test anything. */ + +static void +emit_case_nodes (index, node, default_label, index_type) + rtx index; + case_node_ptr node; + rtx default_label; + tree index_type; +{ + /* If INDEX has an unsigned type, we must make unsigned branches. */ + int unsignedp = TREE_UNSIGNED (index_type); + typedef rtx rtx_function (); + rtx_function *gen_bgt_pat = unsignedp ? gen_bgtu : gen_bgt; + rtx_function *gen_bge_pat = unsignedp ? gen_bgeu : gen_bge; + rtx_function *gen_blt_pat = unsignedp ? gen_bltu : gen_blt; + rtx_function *gen_ble_pat = unsignedp ? gen_bleu : gen_ble; + enum machine_mode mode = GET_MODE (index); + + /* See if our parents have already tested everything for us. + If they have, emit an unconditional jump for this node. */ + if (node_is_bounded (node, index_type)) + emit_jump (label_rtx (node->code_label)); + + else if (tree_int_cst_equal (node->low, node->high)) + { + /* Node is single valued. First see if the index expression matches + this node and then check our children, if any. */ + + do_jump_if_equal (index, expand_expr (node->low, NULL_RTX, VOIDmode, 0), + label_rtx (node->code_label), unsignedp); + + if (node->right != 0 && node->left != 0) + { + /* This node has children on both sides. + Dispatch to one side or the other + by comparing the index value with this node's value. + If one subtree is bounded, check that one first, + so we can avoid real branches in the tree. */ + + if (node_is_bounded (node->right, index_type)) + { + emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, + VOIDmode, 0), + GT, NULL_RTX, mode, unsignedp, 0); + + emit_jump_insn ((*gen_bgt_pat) (label_rtx (node->right->code_label))); + emit_case_nodes (index, node->left, default_label, index_type); + } + + else if (node_is_bounded (node->left, index_type)) + { + emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, + VOIDmode, 0), + LT, NULL_RTX, mode, unsignedp, 0); + emit_jump_insn ((*gen_blt_pat) (label_rtx (node->left->code_label))); + emit_case_nodes (index, node->right, default_label, index_type); + } + + else + { + /* Neither node is bounded. First distinguish the two sides; + then emit the code for one side at a time. */ + + tree test_label + = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + + /* See if the value is on the right. */ + emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, + VOIDmode, 0), + GT, NULL_RTX, mode, unsignedp, 0); + emit_jump_insn ((*gen_bgt_pat) (label_rtx (test_label))); + + /* Value must be on the left. + Handle the left-hand subtree. */ + emit_case_nodes (index, node->left, default_label, index_type); + /* If left-hand subtree does nothing, + go to default. */ + emit_jump_if_reachable (default_label); + + /* Code branches here for the right-hand subtree. */ + expand_label (test_label); + emit_case_nodes (index, node->right, default_label, index_type); + } + } + + else if (node->right != 0 && node->left == 0) + { + /* Here we have a right child but no left so we issue conditional + branch to default and process the right child. + + Omit the conditional branch to default if we it avoid only one + right child; it costs too much space to save so little time. */ + + if (node->right->right || node->right->left + || !tree_int_cst_equal (node->right->low, node->right->high)) + { + if (!node_has_low_bound (node, index_type)) + { + emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, + VOIDmode, 0), + LT, NULL_RTX, mode, unsignedp, 0); + emit_jump_insn ((*gen_blt_pat) (default_label)); + } + + emit_case_nodes (index, node->right, default_label, index_type); + } + else + /* We cannot process node->right normally + since we haven't ruled out the numbers less than + this node's value. So handle node->right explicitly. */ + do_jump_if_equal (index, + expand_expr (node->right->low, NULL_RTX, + VOIDmode, 0), + label_rtx (node->right->code_label), unsignedp); + } + + else if (node->right == 0 && node->left != 0) + { + /* Just one subtree, on the left. */ + +#if 0 /* The following code and comment were formerly part + of the condition here, but they didn't work + and I don't understand what the idea was. -- rms. */ + /* If our "most probable entry" is less probable + than the default label, emit a jump to + the default label using condition codes + already lying around. With no right branch, + a branch-greater-than will get us to the default + label correctly. */ + if (use_cost_table + && cost_table[TREE_INT_CST_LOW (node->high)] < 12) + ; +#endif /* 0 */ + if (node->left->left || node->left->right + || !tree_int_cst_equal (node->left->low, node->left->high)) + { + if (!node_has_high_bound (node, index_type)) + { + emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, + VOIDmode, 0), + GT, NULL_RTX, mode, unsignedp, 0); + emit_jump_insn ((*gen_bgt_pat) (default_label)); + } + + emit_case_nodes (index, node->left, default_label, index_type); + } + else + /* We cannot process node->left normally + since we haven't ruled out the numbers less than + this node's value. So handle node->left explicitly. */ + do_jump_if_equal (index, + expand_expr (node->left->low, NULL_RTX, + VOIDmode, 0), + label_rtx (node->left->code_label), unsignedp); + } + } + else + { + /* Node is a range. These cases are very similar to those for a single + value, except that we do not start by testing whether this node + is the one to branch to. */ + + if (node->right != 0 && node->left != 0) + { + /* Node has subtrees on both sides. + If the right-hand subtree is bounded, + test for it first, since we can go straight there. + Otherwise, we need to make a branch in the control structure, + then handle the two subtrees. */ + tree test_label = 0; + + emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, + VOIDmode, 0), + GT, NULL_RTX, mode, unsignedp, 0); + + if (node_is_bounded (node->right, index_type)) + /* Right hand node is fully bounded so we can eliminate any + testing and branch directly to the target code. */ + emit_jump_insn ((*gen_bgt_pat) (label_rtx (node->right->code_label))); + else + { + /* Right hand node requires testing. + Branch to a label where we will handle it later. */ + + test_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + emit_jump_insn ((*gen_bgt_pat) (label_rtx (test_label))); + } + + /* Value belongs to this node or to the left-hand subtree. */ + + emit_cmp_insn (index, expand_expr (node->low, NULL_RTX, VOIDmode, 0), + GE, NULL_RTX, mode, unsignedp, 0); + emit_jump_insn ((*gen_bge_pat) (label_rtx (node->code_label))); + + /* Handle the left-hand subtree. */ + emit_case_nodes (index, node->left, default_label, index_type); + + /* If right node had to be handled later, do that now. */ + + if (test_label) + { + /* If the left-hand subtree fell through, + don't let it fall into the right-hand subtree. */ + emit_jump_if_reachable (default_label); + + expand_label (test_label); + emit_case_nodes (index, node->right, default_label, index_type); + } + } + + else if (node->right != 0 && node->left == 0) + { + /* Deal with values to the left of this node, + if they are possible. */ + if (!node_has_low_bound (node, index_type)) + { + emit_cmp_insn (index, expand_expr (node->low, NULL_RTX, + VOIDmode, 0), + LT, NULL_RTX, mode, unsignedp, 0); + emit_jump_insn ((*gen_blt_pat) (default_label)); + } + + /* Value belongs to this node or to the right-hand subtree. */ + + emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, + VOIDmode, 0), + LE, NULL_RTX, mode, unsignedp, 0); + emit_jump_insn ((*gen_ble_pat) (label_rtx (node->code_label))); + + emit_case_nodes (index, node->right, default_label, index_type); + } + + else if (node->right == 0 && node->left != 0) + { + /* Deal with values to the right of this node, + if they are possible. */ + if (!node_has_high_bound (node, index_type)) + { + emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, + VOIDmode, 0), + GT, NULL_RTX, mode, unsignedp, 0); + emit_jump_insn ((*gen_bgt_pat) (default_label)); + } + + /* Value belongs to this node or to the left-hand subtree. */ + + emit_cmp_insn (index, expand_expr (node->low, NULL_RTX, VOIDmode, 0), + GE, NULL_RTX, mode, unsignedp, 0); + emit_jump_insn ((*gen_bge_pat) (label_rtx (node->code_label))); + + emit_case_nodes (index, node->left, default_label, index_type); + } + + else + { + /* Node has no children so we check low and high bounds to remove + redundant tests. Only one of the bounds can exist, + since otherwise this node is bounded--a case tested already. */ + + if (!node_has_high_bound (node, index_type)) + { + emit_cmp_insn (index, expand_expr (node->high, NULL_RTX, + VOIDmode, 0), + GT, NULL_RTX, mode, unsignedp, 0); + emit_jump_insn ((*gen_bgt_pat) (default_label)); + } + + if (!node_has_low_bound (node, index_type)) + { + emit_cmp_insn (index, expand_expr (node->low, NULL_RTX, + VOIDmode, 0), + LT, NULL_RTX, mode, unsignedp, 0); + emit_jump_insn ((*gen_blt_pat) (default_label)); + } + + emit_jump (label_rtx (node->code_label)); + } + } +} + +/* These routines are used by the loop unrolling code. They copy BLOCK trees + so that the debugging info will be correct for the unrolled loop. */ + +/* Indexed by block number, contains a pointer to the N'th block node. */ + +static tree *block_vector; + +void +find_loop_tree_blocks () +{ + tree block = DECL_INITIAL (current_function_decl); + + /* There first block is for the function body, and does not have + corresponding block notes. Don't include it in the block vector. */ + block = BLOCK_SUBBLOCKS (block); + + block_vector = identify_blocks (block, get_insns ()); +} + +void +unroll_block_trees () +{ + tree block = DECL_INITIAL (current_function_decl); + + reorder_blocks (block_vector, block, get_insns ()); +} + diff --git a/gnu/usr.bin/cc/common/stor-layout.c b/gnu/usr.bin/cc/lib/stor-layout.c similarity index 100% rename from gnu/usr.bin/cc/common/stor-layout.c rename to gnu/usr.bin/cc/lib/stor-layout.c diff --git a/gnu/usr.bin/cc/common/stupid.c b/gnu/usr.bin/cc/lib/stupid.c similarity index 100% rename from gnu/usr.bin/cc/common/stupid.c rename to gnu/usr.bin/cc/lib/stupid.c diff --git a/gnu/usr.bin/cc/common/tconfig.h b/gnu/usr.bin/cc/lib/tconfig.h similarity index 100% rename from gnu/usr.bin/cc/common/tconfig.h rename to gnu/usr.bin/cc/lib/tconfig.h diff --git a/gnu/usr.bin/cc/common/tm.h b/gnu/usr.bin/cc/lib/tm.h similarity index 100% rename from gnu/usr.bin/cc/common/tm.h rename to gnu/usr.bin/cc/lib/tm.h diff --git a/gnu/usr.bin/cc/lib/toplev.c b/gnu/usr.bin/cc/lib/toplev.c new file mode 100644 index 0000000000..1e33806ee6 --- /dev/null +++ b/gnu/usr.bin/cc/lib/toplev.c @@ -0,0 +1,3509 @@ +/* Top level of GNU C compiler + Copyright (C) 1987, 1988, 1989, 1992 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* This is the top level of cc1/c++. + It parses command args, opens files, invokes the various passes + in the proper order, and counts the time used by each. + Error messages and low-level interface to malloc also handled here. */ + +#include "config.h" +#include +#include +#include +#include + +#include + +#ifdef USG +#undef FLOAT +#include +/* This is for hpux. It is a real screw. They should change hpux. */ +#undef FLOAT +#include +#include /* Correct for hpux at least. Is it good on other USG? */ +#undef FFS /* Some systems define this in param.h. */ +#else +#ifndef VMS +#include +#include +#endif +#endif + +#include "input.h" +#include "tree.h" +/* #include "c-tree.h" */ +#include "rtl.h" +#include "flags.h" +#include "insn-attr.h" +#include "defaults.h" + +#ifdef XCOFF_DEBUGGING_INFO +#include "xcoffout.h" +#endif + +#ifdef VMS +/* The extra parameters substantially improve the I/O performance. */ +static FILE * +VMS_fopen (fname, type) + char * fname; + char * type; +{ + if (strcmp (type, "w") == 0) + return fopen (fname, type, "mbc=16", "deq=64", "fop=tef", "shr=nil"); + return fopen (fname, type, "mbc=16"); +} +#define fopen VMS_fopen +#endif + +#ifndef DEFAULT_GDB_EXTENSIONS +#define DEFAULT_GDB_EXTENSIONS 1 +#endif + +extern int rtx_equal_function_value_matters; + +#if ! (defined (VMS) || defined (OS2)) +extern char **environ; +#endif +extern char *version_string, *language_string; + +extern void init_lex (); +extern void init_decl_processing (); +extern void init_obstacks (); +extern void init_tree_codes (); +extern void init_rtl (); +extern void init_optabs (); +extern void init_stmt (); +extern void init_reg_sets (); +extern void dump_flow_info (); +extern void dump_sched_info (); +extern void dump_local_alloc (); + +void rest_of_decl_compilation (); +void error (); +void error_with_file_and_line (); +void fancy_abort (); +#ifndef abort +void abort (); +#endif +void set_target_switch (); +static void print_switch_values (); +static char *decl_name (); + +/* Name of program invoked, sans directories. */ + +char *progname; + +/* Copy of arguments to main. */ +int save_argc; +char **save_argv; + +/* Name of current original source file (what was input to cpp). + This comes from each #-command in the actual input. */ + +char *input_filename; + +/* Name of top-level original source file (what was input to cpp). + This comes from the #-command at the beginning of the actual input. + If there isn't any there, then this is the cc1 input file name. */ + +char *main_input_filename; + +/* Stream for reading from the input file. */ + +FILE *finput; + +/* Current line number in real source file. */ + +int lineno; + +/* Stack of currently pending input files. */ + +struct file_stack *input_file_stack; + +/* Incremented on each change to input_file_stack. */ +int input_file_stack_tick; + +/* FUNCTION_DECL for function now being parsed or compiled. */ + +extern tree current_function_decl; + +/* Name to use as base of names for dump output files. */ + +char *dump_base_name; + +/* Bit flags that specify the machine subtype we are compiling for. + Bits are tested using macros TARGET_... defined in the tm.h file + and set by `-m...' switches. Must be defined in rtlanal.c. */ + +extern int target_flags; + +/* Flags saying which kinds of debugging dump have been requested. */ + +int rtl_dump = 0; +int rtl_dump_and_exit = 0; +int jump_opt_dump = 0; +int cse_dump = 0; +int loop_dump = 0; +int cse2_dump = 0; +int flow_dump = 0; +int combine_dump = 0; +int sched_dump = 0; +int local_reg_dump = 0; +int global_reg_dump = 0; +int sched2_dump = 0; +int jump2_opt_dump = 0; +int dbr_sched_dump = 0; +int flag_print_asm_name = 0; +int stack_reg_dump = 0; + +/* Name for output file of assembly code, specified with -o. */ + +char *asm_file_name; + +/* Value of the -G xx switch, and whether it was passed or not. */ +int g_switch_value; +int g_switch_set; + +/* Type(s) of debugging information we are producing (if any). + See flags.h for the definitions of the different possible + types of debugging information. */ +enum debug_info_type write_symbols = NO_DEBUG; + +/* Level of debugging information we are producing. See flags.h + for the definitions of the different possible levels. */ +enum debug_info_level debug_info_level = DINFO_LEVEL_NONE; + +/* Nonzero means use GNU-only extensions in the generated symbolic + debugging information. */ +/* Currently, this only has an effect when write_symbols is set to + DBX_DEBUG, XCOFF_DEBUG, or DWARF_DEBUG. */ +int use_gnu_debug_info_extensions = 0; + +/* Nonzero means do optimizations. -O. + Particular numeric values stand for particular amounts of optimization; + thus, -O2 stores 2 here. However, the optimizations beyond the basic + ones are not controlled directly by this variable. Instead, they are + controlled by individual `flag_...' variables that are defaulted + based on this variable. */ + +int optimize = 0; + +/* Number of error messages and warning messages so far. */ + +int errorcount = 0; +int warningcount = 0; +int sorrycount = 0; + +/* Pointer to function to compute the name to use to print a declaration. */ + +char *(*decl_printable_name) (); + +/* Pointer to function to compute rtl for a language-specific tree code. */ + +struct rtx_def *(*lang_expand_expr) (); + +/* Pointer to function to finish handling an incomplete decl at the + end of compilation. */ + +void (*incomplete_decl_finalize_hook) () = 0; + +/* Nonzero if generating code to do profiling. */ + +int profile_flag = 0; + +/* Nonzero if generating code to do profiling on a line-by-line basis. */ + +int profile_block_flag; + +/* Nonzero for -pedantic switch: warn about anything + that standard spec forbids. */ + +int pedantic = 0; + +/* Temporarily suppress certain warnings. + This is set while reading code from a system header file. */ + +int in_system_header = 0; + +/* Nonzero means do stupid register allocation. + Currently, this is 1 if `optimize' is 0. */ + +int obey_regdecls = 0; + +/* Don't print functions as they are compiled and don't print + times taken by the various passes. -quiet. */ + +int quiet_flag = 0; + +/* -f flags. */ + +/* Nonzero means `char' should be signed. */ + +int flag_signed_char; + +/* Nonzero means give an enum type only as many bytes as it needs. */ + +int flag_short_enums; + +/* Nonzero for -fcaller-saves: allocate values in regs that need to + be saved across function calls, if that produces overall better code. + Optional now, so people can test it. */ + +#ifdef DEFAULT_CALLER_SAVES +int flag_caller_saves = 1; +#else +int flag_caller_saves = 0; +#endif + +/* Nonzero if structures and unions should be returned in memory. + + This should only be defined if compatibility with another compiler or + with an ABI is needed, because it results in slower code. */ + +#ifndef DEFAULT_PCC_STRUCT_RETURN +#define DEFAULT_PCC_STRUCT_RETURN 1 +#endif + +/* Nonzero for -fpcc-struct-return: return values the same way PCC does. */ + +int flag_pcc_struct_return = DEFAULT_PCC_STRUCT_RETURN; + +/* Nonzero for -fforce-mem: load memory value into a register + before arithmetic on it. This makes better cse but slower compilation. */ + +int flag_force_mem = 0; + +/* Nonzero for -fforce-addr: load memory address into a register before + reference to memory. This makes better cse but slower compilation. */ + +int flag_force_addr = 0; + +/* Nonzero for -fdefer-pop: don't pop args after each function call; + instead save them up to pop many calls' args with one insns. */ + +int flag_defer_pop = 0; + +/* Nonzero for -ffloat-store: don't allocate floats and doubles + in extended-precision registers. */ + +int flag_float_store = 0; + +/* Nonzero for -fcse-follow-jumps: + have cse follow jumps to do a more extensive job. */ + +int flag_cse_follow_jumps; + +/* Nonzero for -fcse-skip-blocks: + have cse follow a branch around a block. */ +int flag_cse_skip_blocks; + +/* Nonzero for -fexpensive-optimizations: + perform miscellaneous relatively-expensive optimizations. */ +int flag_expensive_optimizations; + +/* Nonzero for -fthread-jumps: + have jump optimize output of loop. */ + +int flag_thread_jumps; + +/* Nonzero enables strength-reduction in loop.c. */ + +int flag_strength_reduce = 0; + +/* Nonzero enables loop unrolling in unroll.c. Only loops for which the + number of iterations can be calculated at compile-time (UNROLL_COMPLETELY, + UNROLL_MODULO) or at run-time (preconditioned to be UNROLL_MODULO) are + unrolled. */ + +int flag_unroll_loops; + +/* Nonzero enables loop unrolling in unroll.c. All loops are unrolled. + This is generally not a win. */ + +int flag_unroll_all_loops; + +/* Nonzero for -fwritable-strings: + store string constants in data segment and don't uniquize them. */ + +int flag_writable_strings = 0; + +/* Nonzero means don't put addresses of constant functions in registers. + Used for compiling the Unix kernel, where strange substitutions are + done on the assembly output. */ + +int flag_no_function_cse = 0; + +/* Nonzero for -fomit-frame-pointer: + don't make a frame pointer in simple functions that don't require one. */ + +int flag_omit_frame_pointer = 0; + +/* Nonzero to inhibit use of define_optimization peephole opts. */ + +int flag_no_peephole = 0; + +/* Nonzero allows GCC to violate some IEEE or ANSI rules regarding math + operations in the interest of optimization. For example it allows + GCC to assume arguments to sqrt are nonnegative numbers, allowing + faster code for sqrt to be generated. */ + +int flag_fast_math = 0; + +/* Nonzero means all references through pointers are volatile. */ + +int flag_volatile; + +/* Nonzero means treat all global and extern variables as global. */ + +int flag_volatile_global; + +/* Nonzero means just do syntax checking; don't output anything. */ + +int flag_syntax_only = 0; + +/* Nonzero means to rerun cse after loop optimization. This increases + compilation time about 20% and picks up a few more common expressions. */ + +static int flag_rerun_cse_after_loop; + +/* Nonzero for -finline-functions: ok to inline functions that look like + good inline candidates. */ + +int flag_inline_functions; + +/* Nonzero for -fkeep-inline-functions: even if we make a function + go inline everywhere, keep its definition around for debugging + purposes. */ + +int flag_keep_inline_functions; + +/* Nonzero means that functions declared `inline' will be treated + as `static'. Prevents generation of zillions of copies of unused + static inline functions; instead, `inlines' are written out + only when actually used. Used in conjunction with -g. Also + does the right thing with #pragma interface. */ + +int flag_no_inline; + +/* Nonzero means we should be saving declaration info into a .X file. */ + +int flag_gen_aux_info = 0; + +/* Specified name of aux-info file. */ + +static char *aux_info_file_name; + +/* Nonzero means make the text shared if supported. */ + +int flag_shared_data; + +/* Nonzero means schedule into delayed branch slots if supported. */ + +int flag_delayed_branch; + +/* Nonzero if we are compiling pure (sharable) code. + Value is 1 if we are doing reasonable (i.e. simple + offset into offset table) pic. Value is 2 if we can + only perform register offsets. */ + +int flag_pic; + +/* Nonzero means place uninitialized global data in the bss section. */ + +int flag_no_common; + +/* Nonzero means pretend it is OK to examine bits of target floats, + even if that isn't true. The resulting code will have incorrect constants, + but the same series of instructions that the native compiler would make. */ + +int flag_pretend_float; + +/* Nonzero means change certain warnings into errors. + Usually these are warnings about failure to conform to some standard. */ + +int flag_pedantic_errors = 0; + +/* flag_schedule_insns means schedule insns within basic blocks (before + local_alloc). + flag_schedule_insns_after_reload means schedule insns after + global_alloc. */ + +int flag_schedule_insns = 0; +int flag_schedule_insns_after_reload = 0; + +/* -finhibit-size-directive inhibits output of .size for ELF. + This is used only for compiling crtstuff.c, + and it may be extended to other effects + needed for crtstuff.c on other systems. */ +int flag_inhibit_size_directive = 0; + +/* -fverbose-asm causes extra commentary information to be produced in + the generated assembly code (to make it more readable). This option + is generally only of use to those who actually need to read the + generated assembly code (perhaps while debugging the compiler itself). */ + +int flag_verbose_asm = 0; + +/* -fgnu-linker specifies use of the GNU linker for initializations. + (Or, more generally, a linker that handles initializations.) + -fno-gnu-linker says that collect2 will be used. */ +#ifdef USE_COLLECT2 +int flag_gnu_linker = 0; +#else +int flag_gnu_linker = 1; +#endif + +/* Table of language-independent -f options. + STRING is the option name. VARIABLE is the address of the variable. + ON_VALUE is the value to store in VARIABLE + if `-fSTRING' is seen as an option. + (If `-fno-STRING' is seen as an option, the opposite value is stored.) */ + +struct { char *string; int *variable; int on_value;} f_options[] = +{ + {"float-store", &flag_float_store, 1}, + {"volatile", &flag_volatile, 1}, + {"volatile-global", &flag_volatile_global, 1}, + {"defer-pop", &flag_defer_pop, 1}, + {"omit-frame-pointer", &flag_omit_frame_pointer, 1}, + {"cse-follow-jumps", &flag_cse_follow_jumps, 1}, + {"cse-skip-blocks", &flag_cse_skip_blocks, 1}, + {"expensive-optimizations", &flag_expensive_optimizations, 1}, + {"thread-jumps", &flag_thread_jumps, 1}, + {"strength-reduce", &flag_strength_reduce, 1}, + {"unroll-loops", &flag_unroll_loops, 1}, + {"unroll-all-loops", &flag_unroll_all_loops, 1}, + {"writable-strings", &flag_writable_strings, 1}, + {"peephole", &flag_no_peephole, 0}, + {"force-mem", &flag_force_mem, 1}, + {"force-addr", &flag_force_addr, 1}, + {"function-cse", &flag_no_function_cse, 0}, + {"inline-functions", &flag_inline_functions, 1}, + {"keep-inline-functions", &flag_keep_inline_functions, 1}, + {"inline", &flag_no_inline, 0}, + {"syntax-only", &flag_syntax_only, 1}, + {"shared-data", &flag_shared_data, 1}, + {"caller-saves", &flag_caller_saves, 1}, + {"pcc-struct-return", &flag_pcc_struct_return, 1}, + {"reg-struct-return", &flag_pcc_struct_return, 0}, + {"delayed-branch", &flag_delayed_branch, 1}, + {"rerun-cse-after-loop", &flag_rerun_cse_after_loop, 1}, + {"pretend-float", &flag_pretend_float, 1}, + {"schedule-insns", &flag_schedule_insns, 1}, + {"schedule-insns2", &flag_schedule_insns_after_reload, 1}, + {"pic", &flag_pic, 1}, + {"PIC", &flag_pic, 2}, + {"fast-math", &flag_fast_math, 1}, + {"common", &flag_no_common, 0}, + {"inhibit-size-directive", &flag_inhibit_size_directive, 1}, + {"verbose-asm", &flag_verbose_asm, 1}, + {"gnu-linker", &flag_gnu_linker, 1} +}; + +/* Table of language-specific options. */ + +char *lang_options[] = +{ + "-ftraditional", + "-traditional", + "-fnotraditional", + "-fno-traditional", + "-fsigned-char", + "-funsigned-char", + "-fno-signed-char", + "-fno-unsigned-char", + "-fsigned-bitfields", + "-funsigned-bitfields", + "-fno-signed-bitfields", + "-fno-unsigned-bitfields", + "-fshort-enums", + "-fno-short-enums", + "-fcond-mismatch", + "-fno-cond-mismatch", + "-fshort-double", + "-fno-short-double", + "-fasm", + "-fno-asm", + "-fbuiltin", + "-fno-builtin", + "-fno-ident", + "-fident", + "-ansi", + "-Wimplicit", + "-Wno-implicit", + "-Wwrite-strings", + "-Wno-write-strings", + "-Wcast-qual", + "-Wno-cast-qual", + "-Wpointer-arith", + "-Wno-pointer-arith", + "-Wstrict-prototypes", + "-Wno-strict-prototypes", + "-Wmissing-prototypes", + "-Wno-missing-prototypes", + "-Wredundant-decls", + "-Wno-redundant-decls", + "-Wnested-externs", + "-Wno-nested-externs", + "-Wtraditional", + "-Wno-traditional", + "-Wformat", + "-Wno-format", + "-Wchar-subscripts", + "-Wno-char-subscripts", + "-Wconversion", + "-Wno-conversion", + "-Wparentheses", + "-Wno-parentheses", + "-Wcomment", + "-Wno-comment", + "-Wcomments", + "-Wno-comments", + "-Wtrigraphs", + "-Wno-trigraphs", + "-Wimport", + "-Wno-import", + "-Wmissing-braces", + "-Wno-missing-braces", + "-Wall", + + /* These are for C++. */ + "-+e0", /* gcc.c tacks the `-' on the front. */ + "-+e1", + "-+e2", + "-fsave-memoized", + "-fno-save-memoized", + "-fcadillac", + "-fno-cadillac", + "-fgc", + "-fno-gc", + "-flabels-ok", + "-fno-labels-ok", + "-fstats", + "-fno-stats", + "-fthis-is-variable", + "-fno-this-is-variable", + "-fstrict-prototype", + "-fno-strict-prototype", + "-fall-virtual", + "-fno-all-virtual", + "-fmemoize-lookups", + "-fno-memoize-lookups", + "-felide-constructors", + "-fno-elide-constructors", + "-finline-debug", + "-fno-inline-debug", + "-fhandle-exceptions", + "-fno-handle-exceptions", + "-fansi-exceptions", + "-fno-ansi-exceptions", + "-fspring-exceptions", + "-fno-spring-exceptions", + "-fdefault-inline", + "-fno-default-inline", + "-fenum-int-equiv", + "-fno-enum-int-equiv", + "-fdossier", + "-fno-dossier", + "-fxref", + "-fno-xref", + "-fnonnull-objects", + "-fno-nonnull-objects", + "-fimplement-inlines", + "-fno-implement-inlines", + + "-Wreturn-type", + "-Wno-return-type", + "-Woverloaded-virtual", + "-Wno-overloaded-virtual", + "-Wenum-clash", + "-Wno-enum-clash", + "-Wtemplate-debugging", + "-Wno-template-debugging", + + /* these are for obj c */ + "-lang-objc", + "-gen-decls", + "-fgnu-runtime", + "-fno-gnu-runtime", + "-fnext-runtime", + "-fno-next-runtime", + "-Wselector", + "-Wno-selector", + "-Wprotocol", + "-Wno-protocol", + 0 +}; + +/* Options controlling warnings */ + +/* Don't print warning messages. -w. */ + +int inhibit_warnings = 0; + +/* Print various extra warnings. -W. */ + +int extra_warnings = 0; + +/* Treat warnings as errors. -Werror. */ + +int warnings_are_errors = 0; + +/* Nonzero to warn about unused local variables. */ + +int warn_unused; + +/* Nonzero to warn about variables used before they are initialized. */ + +int warn_uninitialized; + +/* Nonzero means warn about all declarations which shadow others. */ + +int warn_shadow; + +/* Warn if a switch on an enum fails to have a case for every enum value. */ + +int warn_switch; + +/* Nonzero means warn about function definitions that default the return type + or that use a null return and have a return-type other than void. */ + +int warn_return_type; + +/* Nonzero means warn about pointer casts that increase the required + alignment of the target type (and might therefore lead to a crash + due to a misaligned access). */ + +int warn_cast_align; + +/* Nonzero means warn about any identifiers that match in the first N + characters. The value N is in `id_clash_len'. */ + +int warn_id_clash; +int id_clash_len; + +/* Nonzero means warn if inline function is too large. */ + +int warn_inline; + +/* Warn if a function returns an aggregate, + since there are often incompatible calling conventions for doing this. */ + +int warn_aggregate_return; + +/* Likewise for -W. */ + +struct { char *string; int *variable; int on_value;} W_options[] = +{ + {"unused", &warn_unused, 1}, + {"error", &warnings_are_errors, 1}, + {"shadow", &warn_shadow, 1}, + {"switch", &warn_switch, 1}, + {"aggregate-return", &warn_aggregate_return, 1}, + {"cast-align", &warn_cast_align, 1}, + {"uninitialized", &warn_uninitialized, 1}, + {"inline", &warn_inline, 1} +}; + +/* Output files for assembler code (real compiler output) + and debugging dumps. */ + +FILE *asm_out_file; +FILE *aux_info_file; +FILE *rtl_dump_file; +FILE *jump_opt_dump_file; +FILE *cse_dump_file; +FILE *loop_dump_file; +FILE *cse2_dump_file; +FILE *flow_dump_file; +FILE *combine_dump_file; +FILE *sched_dump_file; +FILE *local_reg_dump_file; +FILE *global_reg_dump_file; +FILE *sched2_dump_file; +FILE *jump2_opt_dump_file; +FILE *dbr_sched_dump_file; +FILE *stack_reg_dump_file; + +/* Time accumulators, to count the total time spent in various passes. */ + +int parse_time; +int varconst_time; +int integration_time; +int jump_time; +int cse_time; +int loop_time; +int cse2_time; +int flow_time; +int combine_time; +int sched_time; +int local_alloc_time; +int global_alloc_time; +int sched2_time; +int dbr_sched_time; +int shorten_branch_time; +int stack_reg_time; +int final_time; +int symout_time; +int dump_time; + +/* Return time used so far, in microseconds. */ + +int +get_run_time () +{ +#ifdef USG + struct tms tms; +#else +#ifndef VMS + struct rusage rusage; +#else /* VMS */ + struct + { + int proc_user_time; + int proc_system_time; + int child_user_time; + int child_system_time; + } vms_times; +#endif +#endif + + if (quiet_flag) + return 0; + +#ifdef USG + times (&tms); + return (tms.tms_utime + tms.tms_stime) * (1000000 / HZ); +#else +#ifndef VMS + getrusage (0, &rusage); + return (rusage.ru_utime.tv_sec * 1000000 + rusage.ru_utime.tv_usec + + rusage.ru_stime.tv_sec * 1000000 + rusage.ru_stime.tv_usec); +#else /* VMS */ + times (&vms_times); + return (vms_times.proc_user_time + vms_times.proc_system_time) * 10000; +#endif +#endif +} + +#define TIMEVAR(VAR, BODY) \ +do { int otime = get_run_time (); BODY; VAR += get_run_time () - otime; } while (0) + +void +print_time (str, total) + char *str; + int total; +{ + fprintf (stderr, + "time in %s: %d.%06d\n", + str, total / 1000000, total % 1000000); +} + +/* Count an error or warning. Return 1 if the message should be printed. */ + +int +count_error (warningp) + int warningp; +{ + if (warningp && inhibit_warnings) + return 0; + + if (warningp && !warnings_are_errors) + warningcount++; + else + { + static int warning_message = 0; + + if (warningp && !warning_message) + { + fprintf (stderr, "%s: warnings being treated as errors\n", progname); + warning_message = 1; + } + errorcount++; + } + + return 1; +} + +/* Print a fatal error message. NAME is the text. + Also include a system error message based on `errno'. */ + +void +pfatal_with_name (name) + char *name; +{ + fprintf (stderr, "%s: ", progname); + perror (name); + exit (35); +} + +void +fatal_io_error (name) + char *name; +{ + fprintf (stderr, "%s: %s: I/O error\n", progname, name); + exit (35); +} + +void +fatal (s, v) + char *s; + int v; +{ + error (s, v); + exit (34); +} + +/* Called to give a better error message when we don't have an insn to match + what we are looking for or if the insn's constraints aren't satisfied, + rather than just calling abort(). */ + +void +fatal_insn_not_found (insn) + rtx insn; +{ + if (INSN_CODE (insn) < 0) + error ("internal error--unrecognizable insn:", 0); + else + error ("internal error--insn does not satisfy its constraints:", 0); + debug_rtx (insn); + if (asm_out_file) + fflush (asm_out_file); + if (aux_info_file) + fflush (aux_info_file); + if (rtl_dump_file) + fflush (rtl_dump_file); + if (jump_opt_dump_file) + fflush (jump_opt_dump_file); + if (cse_dump_file) + fflush (cse_dump_file); + if (loop_dump_file) + fflush (loop_dump_file); + if (cse2_dump_file) + fflush (cse2_dump_file); + if (flow_dump_file) + fflush (flow_dump_file); + if (combine_dump_file) + fflush (combine_dump_file); + if (sched_dump_file) + fflush (sched_dump_file); + if (local_reg_dump_file) + fflush (local_reg_dump_file); + if (global_reg_dump_file) + fflush (global_reg_dump_file); + if (sched2_dump_file) + fflush (sched2_dump_file); + if (jump2_opt_dump_file) + fflush (jump2_opt_dump_file); + if (dbr_sched_dump_file) + fflush (dbr_sched_dump_file); + if (stack_reg_dump_file) + fflush (stack_reg_dump_file); + abort (); +} + +/* This is the default decl_printable_name function. */ + +static char * +decl_name (decl, kind) + tree decl; + char **kind; +{ + return IDENTIFIER_POINTER (DECL_NAME (decl)); +} + +static int need_error_newline; + +/* Function of last error message; + more generally, function such that if next error message is in it + then we don't have to mention the function name. */ +static tree last_error_function = NULL; + +/* Used to detect when input_file_stack has changed since last described. */ +static int last_error_tick; + +/* Called when the start of a function definition is parsed, + this function prints on stderr the name of the function. */ + +void +announce_function (decl) + tree decl; +{ + if (! quiet_flag) + { + char *junk; + if (rtl_dump_and_exit) + fprintf (stderr, "%s ", IDENTIFIER_POINTER (DECL_NAME (decl))); + else + fprintf (stderr, " %s", (*decl_printable_name) (decl, &junk)); + fflush (stderr); + need_error_newline = 1; + last_error_function = current_function_decl; + } +} + +/* Prints out, if necessary, the name of the current function + which caused an error. Called from all error and warning functions. */ + +void +report_error_function (file) + char *file; +{ + struct file_stack *p; + + if (need_error_newline) + { + fprintf (stderr, "\n"); + need_error_newline = 0; + } + + if (last_error_function != current_function_decl) + { + char *kind = "function"; + if (current_function_decl != 0 + && TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE) + kind = "method"; + + if (file) + fprintf (stderr, "%s: ", file); + + if (current_function_decl == NULL) + fprintf (stderr, "At top level:\n"); + else + { + char *name = (*decl_printable_name) (current_function_decl, &kind); + fprintf (stderr, "In %s `%s':\n", kind, name); + } + + last_error_function = current_function_decl; + } + if (input_file_stack && input_file_stack->next != 0 + && input_file_stack_tick != last_error_tick) + { + fprintf (stderr, "In file included"); + for (p = input_file_stack->next; p; p = p->next) + { + fprintf (stderr, " from %s:%d", p->name, p->line); + if (p->next) + fprintf (stderr, ","); + } + fprintf (stderr, ":\n"); + last_error_tick = input_file_stack_tick; + } +} + +/* Report an error at the current line number. + S is a string and V and V2 are args for `printf'. We use HOST_WIDE_INT + as the type for these args assuming it is wide enough to hold a + pointer. This isn't terribly portable, but is the best we can do + without vprintf universally available. */ + +void +error (s, v, v2) + char *s; + HOST_WIDE_INT v; /* Also used as pointer */ + HOST_WIDE_INT v2; /* Also used as pointer */ +{ + error_with_file_and_line (input_filename, lineno, s, v, v2); +} + +/* Report an error at line LINE of file FILE. + S and V are a string and an arg for `printf'. */ + +void +error_with_file_and_line (file, line, s, v, v2) + char *file; + int line; + char *s; + HOST_WIDE_INT v; + HOST_WIDE_INT v2; +{ + count_error (0); + + report_error_function (file); + + if (file) + fprintf (stderr, "%s:%d: ", file, line); + else + fprintf (stderr, "%s: ", progname); + fprintf (stderr, s, v, v2); + fprintf (stderr, "\n"); +} + +/* Report an error at the declaration DECL. + S and V are a string and an arg which uses %s to substitute + the declaration name. */ + +void +error_with_decl (decl, s, v) + tree decl; + char *s; + HOST_WIDE_INT v; +{ + char *junk; + count_error (0); + + report_error_function (DECL_SOURCE_FILE (decl)); + + fprintf (stderr, "%s:%d: ", + DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl)); + + if (DECL_NAME (decl)) + fprintf (stderr, s, (*decl_printable_name) (decl, &junk), v); + else + fprintf (stderr, s, "((anonymous))", v); + fprintf (stderr, "\n"); +} + +/* Report an error at the line number of the insn INSN. + S and V are a string and an arg for `printf'. + This is used only when INSN is an `asm' with operands, + and each ASM_OPERANDS records its own source file and line. */ + +void +error_for_asm (insn, s, v, v2) + rtx insn; + char *s; + HOST_WIDE_INT v; /* Also used as pointer */ + HOST_WIDE_INT v2; /* Also used as pointer */ +{ + char *filename; + int line; + rtx body = PATTERN (insn); + rtx asmop; + + /* Find the (or one of the) ASM_OPERANDS in the insn. */ + if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS) + asmop = SET_SRC (body); + else if (GET_CODE (body) == ASM_OPERANDS) + asmop = body; + else if (GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == SET) + asmop = SET_SRC (XVECEXP (body, 0, 0)); + else if (GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS) + asmop = XVECEXP (body, 0, 0); + + filename = ASM_OPERANDS_SOURCE_FILE (asmop); + line = ASM_OPERANDS_SOURCE_LINE (asmop); + + error_with_file_and_line (filename, line, s, v, v2); +} + +/* Report a warning at line LINE. + S and V are a string and an arg for `printf'. */ + +void +warning_with_file_and_line (file, line, s, v, v2, v3) + char *file; + int line; + char *s; + HOST_WIDE_INT v, v2, v3; +{ + if (count_error (1) == 0) + return; + + report_error_function (file); + + if (file) + fprintf (stderr, "%s:%d: ", file, line); + else + fprintf (stderr, "%s: ", progname); + + fprintf (stderr, "warning: "); + fprintf (stderr, s, v, v2, v3); + fprintf (stderr, "\n"); +} + +/* Report a warning at the current line number. + S and V are a string and an arg for `printf'. */ + +void +warning (s, v, v2, v3) + char *s; + HOST_WIDE_INT v, v2, v3; /* Also used as pointer */ +{ + warning_with_file_and_line (input_filename, lineno, s, v, v2, v3); +} + +/* Report a warning at the declaration DECL. + S is string which uses %s to substitute the declaration name. + V is a second parameter that S can refer to. */ + +void +warning_with_decl (decl, s, v) + tree decl; + char *s; + HOST_WIDE_INT v; +{ + char *junk; + + if (count_error (1) == 0) + return; + + report_error_function (DECL_SOURCE_FILE (decl)); + + fprintf (stderr, "%s:%d: ", + DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl)); + + fprintf (stderr, "warning: "); + if (DECL_NAME (decl)) + fprintf (stderr, s, (*decl_printable_name) (decl, &junk), v); + else + fprintf (stderr, s, "((anonymous))", v); + fprintf (stderr, "\n"); +} + +/* Report a warning at the line number of the insn INSN. + S and V are a string and an arg for `printf'. + This is used only when INSN is an `asm' with operands, + and each ASM_OPERANDS records its own source file and line. */ + +void +warning_for_asm (insn, s, v, v2) + rtx insn; + char *s; + HOST_WIDE_INT v; /* Also used as pointer */ + HOST_WIDE_INT v2; /* Also used as pointer */ +{ + char *filename; + int line; + rtx body = PATTERN (insn); + rtx asmop; + + /* Find the (or one of the) ASM_OPERANDS in the insn. */ + if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS) + asmop = SET_SRC (body); + else if (GET_CODE (body) == ASM_OPERANDS) + asmop = body; + else if (GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == SET) + asmop = SET_SRC (XVECEXP (body, 0, 0)); + else if (GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS) + asmop = XVECEXP (body, 0, 0); + + filename = ASM_OPERANDS_SOURCE_FILE (asmop); + line = ASM_OPERANDS_SOURCE_LINE (asmop); + + warning_with_file_and_line (filename, line, s, v, v2); +} + +/* These functions issue either warnings or errors depending on + -pedantic-errors. */ + +void +pedwarn (s, v, v2) + char *s; + HOST_WIDE_INT v; /* Also used as pointer */ + HOST_WIDE_INT v2; +{ + if (flag_pedantic_errors) + error (s, v, v2); + else + warning (s, v, v2); +} + +void +pedwarn_with_decl (decl, s, v) + tree decl; + char *s; + HOST_WIDE_INT v; +{ + if (flag_pedantic_errors) + error_with_decl (decl, s, v); + else + warning_with_decl (decl, s, v); +} + +void +pedwarn_with_file_and_line (file, line, s, v, v2) + char *file; + int line; + char *s; + HOST_WIDE_INT v; + HOST_WIDE_INT v2; +{ + if (flag_pedantic_errors) + error_with_file_and_line (file, line, s, v, v2); + else + warning_with_file_and_line (file, line, s, v, v2); +} + +/* Apologize for not implementing some feature. + S, V, and V2 are a string and args for `printf'. */ + +void +sorry (s, v, v2) + char *s; + HOST_WIDE_INT v, v2; +{ + sorrycount++; + if (input_filename) + fprintf (stderr, "%s:%d: ", input_filename, lineno); + else + fprintf (stderr, "%s: ", progname); + + fprintf (stderr, "sorry, not implemented: "); + fprintf (stderr, s, v, v2); + fprintf (stderr, "\n"); +} + +/* Apologize for not implementing some feature, then quit. + S, V, and V2 are a string and args for `printf'. */ + +void +really_sorry (s, v, v2) + char *s; + HOST_WIDE_INT v, v2; +{ + if (input_filename) + fprintf (stderr, "%s:%d: ", input_filename, lineno); + else + fprintf (stderr, "%s: ", progname); + + fprintf (stderr, "sorry, not implemented: "); + fprintf (stderr, s, v, v2); + fatal (" (fatal)\n"); +} + +/* More 'friendly' abort that prints the line and file. + config.h can #define abort fancy_abort if you like that sort of thing. + + I don't think this is actually a good idea. + Other sorts of crashes will look a certain way. + It is a good thing if crashes from calling abort look the same way. + -- RMS */ + +void +fancy_abort () +{ + fatal ("internal gcc abort"); +} + +/* This calls abort and is used to avoid problems when abort if a macro. + It is used when we need to pass the address of abort. */ + +void +do_abort () +{ + abort (); +} + +/* When `malloc.c' is compiled with `rcheck' defined, + it calls this function to report clobberage. */ + +void +botch (s) +{ + abort (); +} + +/* Same as `malloc' but report error if no memory available. */ + +char * +xmalloc (size) + unsigned size; +{ + register char *value = (char *) malloc (size); + if (value == 0) + fatal ("virtual memory exhausted"); + return value; +} + +/* Same as `realloc' but report error if no memory available. */ + +char * +xrealloc (ptr, size) + char *ptr; + int size; +{ + char *result = (char *) realloc (ptr, size); + if (!result) + fatal ("virtual memory exhausted"); + return result; +} + +/* Return the logarithm of X, base 2, considering X unsigned, + if X is a power of 2. Otherwise, returns -1. + + This should be used via the `exact_log2' macro. */ + +int +exact_log2_wide (x) + register unsigned HOST_WIDE_INT x; +{ + register int log = 0; + /* Test for 0 or a power of 2. */ + if (x == 0 || x != (x & -x)) + return -1; + while ((x >>= 1) != 0) + log++; + return log; +} + +/* Given X, an unsigned number, return the largest int Y such that 2**Y <= X. + If X is 0, return -1. + + This should be used via the floor_log2 macro. */ + +int +floor_log2_wide (x) + register unsigned HOST_WIDE_INT x; +{ + register int log = -1; + while (x != 0) + log++, + x >>= 1; + return log; +} + +int float_handled; +jmp_buf float_handler; + +/* Specify where to longjmp to when a floating arithmetic error happens. + If HANDLER is 0, it means don't handle the errors any more. */ + +void +set_float_handler (handler) + jmp_buf handler; +{ + float_handled = (handler != 0); + if (handler) + bcopy (handler, float_handler, sizeof (float_handler)); +} + +/* Specify, in HANDLER, where to longjmp to when a floating arithmetic + error happens, pushing the previous specification into OLD_HANDLER. + Return an indication of whether there was a previous handler in effect. */ + +int +push_float_handler (handler, old_handler) + jmp_buf handler, old_handler; +{ + int was_handled = float_handled; + + float_handled = 1; + if (was_handled) + bcopy (float_handler, old_handler, sizeof (float_handler)); + bcopy (handler, float_handler, sizeof (float_handler)); + return was_handled; +} + +/* Restore the previous specification of whether and where to longjmp to + when a floating arithmetic error happens. */ + +void +pop_float_handler (handled, handler) + int handled; + jmp_buf handler; +{ + float_handled = handled; + if (handled) + bcopy (handler, float_handler, sizeof (float_handler)); +} + +/* Signals actually come here. */ + +static void +float_signal (signo) + /* If this is missing, some compilers complain. */ + int signo; +{ + if (float_handled == 0) + abort (); +#if defined (USG) || defined (hpux) + signal (SIGFPE, float_signal); /* re-enable the signal catcher */ +#endif + float_handled = 0; + signal (SIGFPE, float_signal); + longjmp (float_handler, 1); +} + +/* Handler for SIGPIPE. */ + +static void +pipe_closed (signo) + /* If this is missing, some compilers complain. */ + int signo; +{ + fatal ("output pipe has been closed"); +} + +/* Strip off a legitimate source ending from the input string NAME of + length LEN. */ + +void +strip_off_ending (name, len) + char *name; + int len; +{ + if (len > 2 && ! strcmp (".c", name + len - 2)) + name[len - 2] = 0; + else if (len > 2 && ! strcmp (".m", name + len - 2)) + name[len - 2] = 0; + else if (len > 2 && ! strcmp (".i", name + len - 2)) + name[len - 2] = 0; + else if (len > 3 && ! strcmp (".ii", name + len - 3)) + name[len - 3] = 0; + else if (len > 3 && ! strcmp (".co", name + len - 3)) + name[len - 3] = 0; + else if (len > 3 && ! strcmp (".cc", name + len - 3)) + name[len - 3] = 0; + else if (len > 2 && ! strcmp (".C", name + len - 2)) + name[len - 2] = 0; + else if (len > 4 && ! strcmp (".cxx", name + len - 4)) + name[len - 4] = 0; + else if (len > 2 && ! strcmp (".f", name + len - 2)) + name[len - 2] = 0; + else if (len > 4 && ! strcmp (".ada", name + len - 4)) + name[len - 4] = 0; + else if (len > 4 && ! strcmp (".atr", name + len - 4)) + name[len - 4] = 0; +} + +/* Output a file name in the form wanted by System V. */ + +void +output_file_directive (asm_file, input_name) + FILE *asm_file; + char *input_name; +{ + int len = strlen (input_name); + char *na = input_name + len; + + /* NA gets INPUT_NAME sans directory names. */ + while (na > input_name) + { + if (na[-1] == '/') + break; + na--; + } + +#ifdef ASM_OUTPUT_MAIN_SOURCE_FILENAME + ASM_OUTPUT_MAIN_SOURCE_FILENAME (asm_file, na); +#else +#ifdef ASM_OUTPUT_SOURCE_FILENAME + ASM_OUTPUT_SOURCE_FILENAME (asm_file, na); +#else + fprintf (asm_file, "\t.file\t\"%s\"\n", na); +#endif +#endif +} + +/* Routine to build language identifier for object file. */ +static void +output_lang_identify (asm_out_file) + FILE *asm_out_file; +{ + int len = strlen (lang_identify ()) + sizeof ("__gnu_compiled_") + 1; + char *s = (char *) alloca (len); + sprintf (s, "__gnu_compiled_%s", lang_identify ()); + ASM_OUTPUT_LABEL (asm_out_file, s); +} + +/* Compile an entire file of output from cpp, named NAME. + Write a file of assembly output and various debugging dumps. */ + +static void +compile_file (name) + char *name; +{ + tree globals; + int start_time; + int dump_base_name_length; + + int name_specified = name != 0; + + if (dump_base_name == 0) + dump_base_name = name ? name : "gccdump"; + dump_base_name_length = strlen (dump_base_name); + + parse_time = 0; + varconst_time = 0; + integration_time = 0; + jump_time = 0; + cse_time = 0; + loop_time = 0; + cse2_time = 0; + flow_time = 0; + combine_time = 0; + sched_time = 0; + local_alloc_time = 0; + global_alloc_time = 0; + sched2_time = 0; + dbr_sched_time = 0; + shorten_branch_time = 0; + stack_reg_time = 0; + final_time = 0; + symout_time = 0; + dump_time = 0; + + /* Open input file. */ + + if (name == 0 || !strcmp (name, "-")) + { + finput = stdin; + name = "stdin"; + } + else + finput = fopen (name, "r"); + if (finput == 0) + pfatal_with_name (name); + +#ifdef IO_BUFFER_SIZE + setvbuf (finput, (char *) xmalloc (IO_BUFFER_SIZE), _IOFBF, IO_BUFFER_SIZE); +#endif + + /* Initialize data in various passes. */ + + init_obstacks (); + init_tree_codes (); + init_lex (); + init_rtl (); + init_emit_once (debug_info_level == DINFO_LEVEL_NORMAL + || debug_info_level == DINFO_LEVEL_VERBOSE); + init_decl_processing (); + init_optabs (); + init_stmt (); + init_expmed (); + init_expr_once (); + init_loop (); + init_reload (); + + if (flag_caller_saves) + init_caller_save (); + + /* If auxiliary info generation is desired, open the output file. + This goes in the same directory as the source file--unlike + all the other output files. */ + if (flag_gen_aux_info) + { + aux_info_file = fopen (aux_info_file_name, "w"); + if (aux_info_file == 0) + pfatal_with_name (aux_info_file_name); + } + + /* If rtl dump desired, open the output file. */ + if (rtl_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".rtl"); + rtl_dump_file = fopen (dumpname, "w"); + if (rtl_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If jump_opt dump desired, open the output file. */ + if (jump_opt_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".jump"); + jump_opt_dump_file = fopen (dumpname, "w"); + if (jump_opt_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If cse dump desired, open the output file. */ + if (cse_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".cse"); + cse_dump_file = fopen (dumpname, "w"); + if (cse_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If loop dump desired, open the output file. */ + if (loop_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".loop"); + loop_dump_file = fopen (dumpname, "w"); + if (loop_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If cse2 dump desired, open the output file. */ + if (cse2_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".cse2"); + cse2_dump_file = fopen (dumpname, "w"); + if (cse2_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If flow dump desired, open the output file. */ + if (flow_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".flow"); + flow_dump_file = fopen (dumpname, "w"); + if (flow_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If combine dump desired, open the output file. */ + if (combine_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 10); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".combine"); + combine_dump_file = fopen (dumpname, "w"); + if (combine_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If scheduling dump desired, open the output file. */ + if (sched_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 7); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".sched"); + sched_dump_file = fopen (dumpname, "w"); + if (sched_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If local_reg dump desired, open the output file. */ + if (local_reg_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".lreg"); + local_reg_dump_file = fopen (dumpname, "w"); + if (local_reg_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If global_reg dump desired, open the output file. */ + if (global_reg_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".greg"); + global_reg_dump_file = fopen (dumpname, "w"); + if (global_reg_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If 2nd scheduling dump desired, open the output file. */ + if (sched2_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 8); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".sched2"); + sched2_dump_file = fopen (dumpname, "w"); + if (sched2_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If jump2_opt dump desired, open the output file. */ + if (jump2_opt_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 7); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".jump2"); + jump2_opt_dump_file = fopen (dumpname, "w"); + if (jump2_opt_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If dbr_sched dump desired, open the output file. */ + if (dbr_sched_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 7); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".dbr"); + dbr_sched_dump_file = fopen (dumpname, "w"); + if (dbr_sched_dump_file == 0) + pfatal_with_name (dumpname); + } + +#ifdef STACK_REGS + + /* If stack_reg dump desired, open the output file. */ + if (stack_reg_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 10); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".stack"); + stack_reg_dump_file = fopen (dumpname, "w"); + if (stack_reg_dump_file == 0) + pfatal_with_name (dumpname); + } + +#endif + + /* Open assembler code output file. */ + + if (! name_specified && asm_file_name == 0) + asm_out_file = stdout; + else + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + int len = strlen (dump_base_name); + strcpy (dumpname, dump_base_name); + strip_off_ending (dumpname, len); + strcat (dumpname, ".s"); + if (asm_file_name == 0) + { + asm_file_name = (char *) xmalloc (strlen (dumpname) + 1); + strcpy (asm_file_name, dumpname); + } + if (!strcmp (asm_file_name, "-")) + asm_out_file = stdout; + else + asm_out_file = fopen (asm_file_name, "w"); + if (asm_out_file == 0) + pfatal_with_name (asm_file_name); + } + +#ifdef IO_BUFFER_SIZE + setvbuf (asm_out_file, (char *) xmalloc (IO_BUFFER_SIZE), + _IOFBF, IO_BUFFER_SIZE); +#endif + + input_filename = name; + + /* Perform language-specific initialization. + This may set main_input_filename. */ + lang_init (); + + /* If the input doesn't start with a #line, use the input name + as the official input file name. */ + if (main_input_filename == 0) + main_input_filename = name; + + /* Put an entry on the input file stack for the main input file. */ + input_file_stack + = (struct file_stack *) xmalloc (sizeof (struct file_stack)); + input_file_stack->next = 0; + input_file_stack->name = input_filename; + + ASM_FILE_START (asm_out_file); + + /* Output something to inform GDB that this compilation was by GCC. */ +#ifndef ASM_IDENTIFY_GCC + fprintf (asm_out_file, "gcc2_compiled.:\n"); +#else + ASM_IDENTIFY_GCC (asm_out_file); +#endif + + /* Output something to identify which front-end produced this file. */ +#ifdef ASM_IDENTIFY_LANGUAGE + ASM_IDENTIFY_LANGUAGE (asm_out_file); +#endif + +/* ??? Note: There used to be a conditional here + to call assemble_zeros without fail if DBX_DEBUGGING_INFO is defined. + This was to guarantee separation between gcc_compiled. and + the first function, for the sake of dbx on Suns. + However, having the extra zero here confused the Emacs + code for unexec, and might confuse other programs too. + Therefore, I took out that change. + In future versions we should find another way to solve + that dbx problem. -- rms, 23 May 93. */ + + /* Don't let the first function fall at the same address + as gcc_compiled., if profiling. */ + if (profile_flag || profile_block_flag) + assemble_zeros (UNITS_PER_WORD); + + /* If dbx symbol table desired, initialize writing it + and output the predefined types. */ +#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) + if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) + TIMEVAR (symout_time, dbxout_init (asm_out_file, main_input_filename, + getdecls ())); +#endif +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG) + TIMEVAR (symout_time, sdbout_init (asm_out_file, main_input_filename, + getdecls ())); +#endif +#ifdef DWARF_DEBUGGING_INFO + if (write_symbols == DWARF_DEBUG) + TIMEVAR (symout_time, dwarfout_init (asm_out_file, main_input_filename)); +#endif + + /* Initialize yet another pass. */ + + init_final (main_input_filename); + + start_time = get_run_time (); + + /* Call the parser, which parses the entire file + (calling rest_of_compilation for each function). */ + + if (yyparse () != 0) + if (errorcount == 0) + fprintf (stderr, "Errors detected in input file (your bison.simple is out of date)"); + + /* Compilation is now finished except for writing + what's left of the symbol table output. */ + + parse_time += get_run_time () - start_time; + + parse_time -= integration_time; + parse_time -= varconst_time; + + globals = getdecls (); + + /* Really define vars that have had only a tentative definition. + Really output inline functions that must actually be callable + and have not been output so far. */ + + { + int len = list_length (globals); + tree *vec = (tree *) alloca (sizeof (tree) * len); + int i; + tree decl; + + /* Process the decls in reverse order--earliest first. + Put them into VEC from back to front, then take out from front. */ + + for (i = 0, decl = globals; i < len; i++, decl = TREE_CHAIN (decl)) + vec[len - i - 1] = decl; + + for (i = 0; i < len; i++) + { + decl = vec[i]; + if (TREE_CODE (decl) == VAR_DECL && DECL_SIZE (decl) == 0 + && incomplete_decl_finalize_hook != 0) + (*incomplete_decl_finalize_hook) (decl); + + if (TREE_CODE (decl) == VAR_DECL && TREE_STATIC (decl) + && ! TREE_ASM_WRITTEN (decl)) + { + /* Don't write out static consts, unless we used them. + (This used to write them out only if the address was + taken, but that was wrong; if the variable was simply + referred to, it still needs to exist or else it will + be undefined in the linker.) */ + if (! TREE_READONLY (decl) + || TREE_PUBLIC (decl) + || TREE_USED (decl) + || TREE_ADDRESSABLE (decl) + || TREE_ADDRESSABLE (DECL_ASSEMBLER_NAME (decl))) + rest_of_decl_compilation (decl, NULL_PTR, 1, 1); + else + /* Cancel the RTL for this decl so that, if debugging info + output for global variables is still to come, + this one will be omitted. */ + DECL_RTL (decl) = NULL; + } + + if (TREE_CODE (decl) == FUNCTION_DECL + && ! TREE_ASM_WRITTEN (decl) + && DECL_INITIAL (decl) != 0 + && (TREE_ADDRESSABLE (decl) + || TREE_ADDRESSABLE (DECL_ASSEMBLER_NAME (decl))) + && ! DECL_EXTERNAL (decl)) + output_inline_function (decl); + + /* Warn about any function + declared static but not defined. + We don't warn about variables, + because many programs have static variables + that exist only to get some text into the object file. */ + if ((warn_unused + || TREE_USED (decl) + || (DECL_NAME (decl) && TREE_USED (DECL_NAME (decl)))) + && TREE_CODE (decl) == FUNCTION_DECL + && DECL_INITIAL (decl) == 0 + && DECL_EXTERNAL (decl) + && ! TREE_PUBLIC (decl)) + { + /* This should be a pedwarn, except that there is + no easy way to prevent it from happening when the + name is used only inside a sizeof. + This at least avoids being incorrect. */ + warning_with_decl (decl, + "`%s' declared `static' but never defined"); + /* This symbol is effectively an "extern" declaration now. */ + TREE_PUBLIC (decl) = 1; + assemble_external (decl); + + } + /* Warn about static fns or vars defined but not used, + but not about inline functions + since unused inline statics is normal practice. */ + if (warn_unused + && (TREE_CODE (decl) == FUNCTION_DECL + || TREE_CODE (decl) == VAR_DECL) + && ! DECL_IN_SYSTEM_HEADER (decl) + && ! DECL_EXTERNAL (decl) + && ! TREE_PUBLIC (decl) + && ! TREE_USED (decl) + && ! DECL_INLINE (decl) + && ! DECL_REGISTER (decl) + /* The TREE_USED bit for file-scope decls + is kept in the identifier, to handle multiple + external decls in different scopes. */ + && ! TREE_USED (DECL_NAME (decl))) + warning_with_decl (decl, "`%s' defined but not used"); + +#ifdef SDB_DEBUGGING_INFO + /* The COFF linker can move initialized global vars to the end. + And that can screw up the symbol ordering. + By putting the symbols in that order to begin with, + we avoid a problem. mcsun!unido!fauern!tumuc!pes@uunet.uu.net. */ + if (write_symbols == SDB_DEBUG && TREE_CODE (decl) == VAR_DECL + && TREE_PUBLIC (decl) && DECL_INITIAL (decl) + && DECL_RTL (decl) != 0) + TIMEVAR (symout_time, sdbout_symbol (decl, 0)); + + /* Output COFF information for non-global + file-scope initialized variables. */ + if (write_symbols == SDB_DEBUG + && TREE_CODE (decl) == VAR_DECL + && DECL_INITIAL (decl) + && DECL_RTL (decl) != 0 + && GET_CODE (DECL_RTL (decl)) == MEM) + TIMEVAR (symout_time, sdbout_toplevel_data (decl)); +#endif /* SDB_DEBUGGING_INFO */ +#ifdef DWARF_DEBUGGING_INFO + /* Output DWARF information for file-scope tentative data object + declarations, file-scope (extern) function declarations (which + had no corresponding body) and file-scope tagged type declarations + and definitions which have not yet been forced out. */ + + if (write_symbols == DWARF_DEBUG + && (TREE_CODE (decl) != FUNCTION_DECL || !DECL_INITIAL (decl))) + TIMEVAR (symout_time, dwarfout_file_scope_decl (decl, 1)); +#endif + } + } + + /* Do dbx symbols */ +#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) + if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) + TIMEVAR (symout_time, + { + dbxout_finish (asm_out_file, main_input_filename); + }); +#endif + +#ifdef DWARF_DEBUGGING_INFO + if (write_symbols == DWARF_DEBUG) + TIMEVAR (symout_time, + { + dwarfout_finish (); + }); +#endif + + /* Output some stuff at end of file if nec. */ + + end_final (main_input_filename); + +#ifdef ASM_FILE_END + ASM_FILE_END (asm_out_file); +#endif + + after_finish_compilation: + + /* Language-specific end of compilation actions. */ + + lang_finish (); + + /* Close the dump files. */ + + if (flag_gen_aux_info) + { + fclose (aux_info_file); + if (errorcount) + unlink (aux_info_file_name); + } + + if (rtl_dump) + fclose (rtl_dump_file); + + if (jump_opt_dump) + fclose (jump_opt_dump_file); + + if (cse_dump) + fclose (cse_dump_file); + + if (loop_dump) + fclose (loop_dump_file); + + if (cse2_dump) + fclose (cse2_dump_file); + + if (flow_dump) + fclose (flow_dump_file); + + if (combine_dump) + { + dump_combine_total_stats (combine_dump_file); + fclose (combine_dump_file); + } + + if (sched_dump) + fclose (sched_dump_file); + + if (local_reg_dump) + fclose (local_reg_dump_file); + + if (global_reg_dump) + fclose (global_reg_dump_file); + + if (sched2_dump) + fclose (sched2_dump_file); + + if (jump2_opt_dump) + fclose (jump2_opt_dump_file); + + if (dbr_sched_dump) + fclose (dbr_sched_dump_file); + +#ifdef STACK_REGS + if (stack_reg_dump) + fclose (stack_reg_dump_file); +#endif + + /* Close non-debugging input and output files. Take special care to note + whether fclose returns an error, since the pages might still be on the + buffer chain while the file is open. */ + + fclose (finput); + if (ferror (asm_out_file) != 0 || fclose (asm_out_file) != 0) + fatal_io_error (asm_file_name); + + /* Print the times. */ + + if (! quiet_flag) + { + fprintf (stderr,"\n"); + print_time ("parse", parse_time); + print_time ("integration", integration_time); + print_time ("jump", jump_time); + print_time ("cse", cse_time); + print_time ("loop", loop_time); + print_time ("cse2", cse2_time); + print_time ("flow", flow_time); + print_time ("combine", combine_time); + print_time ("sched", sched_time); + print_time ("local-alloc", local_alloc_time); + print_time ("global-alloc", global_alloc_time); + print_time ("sched2", sched2_time); + print_time ("dbranch", dbr_sched_time); + print_time ("shorten-branch", shorten_branch_time); + print_time ("stack-reg", stack_reg_time); + print_time ("final", final_time); + print_time ("varconst", varconst_time); + print_time ("symout", symout_time); + print_time ("dump", dump_time); + } +} + +/* This is called from various places for FUNCTION_DECL, VAR_DECL, + and TYPE_DECL nodes. + + This does nothing for local (non-static) variables. + Otherwise, it sets up the RTL and outputs any assembler code + (label definition, storage allocation and initialization). + + DECL is the declaration. If ASMSPEC is nonzero, it specifies + the assembler symbol name to be used. TOP_LEVEL is nonzero + if this declaration is not within a function. */ + +void +rest_of_decl_compilation (decl, asmspec, top_level, at_end) + tree decl; + char *asmspec; + int top_level; + int at_end; +{ + /* Declarations of variables, and of functions defined elsewhere. */ + + /* Forward declarations for nested functions are not "external", + but we need to treat them as if they were. */ + if (TREE_STATIC (decl) || DECL_EXTERNAL (decl) + || TREE_CODE (decl) == FUNCTION_DECL) + TIMEVAR (varconst_time, + { + make_decl_rtl (decl, asmspec, top_level); + /* For a user-invisible decl that should be replaced + by its value when used, don't output anything. */ + if (! (TREE_CODE (decl) == VAR_DECL + && DECL_IGNORED_P (decl) && TREE_READONLY (decl) + && DECL_INITIAL (decl) != 0)) + /* Don't output anything + when a tentative file-scope definition is seen. + But at end of compilation, do output code for them. */ + if (! (! at_end && top_level + && (DECL_INITIAL (decl) == 0 + || DECL_INITIAL (decl) == error_mark_node + || DECL_IGNORED_P (decl)))) + assemble_variable (decl, top_level, at_end); + }); + else if (DECL_REGISTER (decl) && asmspec != 0) + { + if (decode_reg_name (asmspec) >= 0) + { + DECL_RTL (decl) = 0; + make_decl_rtl (decl, asmspec, top_level); + } + else + error ("invalid register name `%s' for register variable", asmspec); + } +#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) + else if ((write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) + && TREE_CODE (decl) == TYPE_DECL) + TIMEVAR (symout_time, dbxout_symbol (decl, 0)); +#endif +#ifdef SDB_DEBUGGING_INFO + else if (write_symbols == SDB_DEBUG && top_level + && TREE_CODE (decl) == TYPE_DECL) + TIMEVAR (symout_time, sdbout_symbol (decl, 0)); +#endif +} + +/* Called after finishing a record, union or enumeral type. */ + +void +rest_of_type_compilation (type, toplev) + tree type; + int toplev; +{ +#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) + if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) + TIMEVAR (symout_time, dbxout_symbol (TYPE_STUB_DECL (type), !toplev)); +#endif +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG) + TIMEVAR (symout_time, sdbout_symbol (TYPE_STUB_DECL (type), !toplev)); +#endif +} + +/* This is called from finish_function (within yyparse) + after each top-level definition is parsed. + It is supposed to compile that function or variable + and output the assembler code for it. + After we return, the tree storage is freed. */ + +void +rest_of_compilation (decl) + tree decl; +{ + register rtx insns; + int start_time = get_run_time (); + int tem; + /* Nonzero if we have saved the original DECL_INITIAL of the function, + to be restored after we finish compiling the function + (for use when compiling inline calls to this function). */ + tree saved_block_tree = 0; + /* Likewise, for DECL_ARGUMENTS. */ + tree saved_arguments = 0; + int failure = 0; + + /* If we are reconsidering an inline function + at the end of compilation, skip the stuff for making it inline. */ + + if (DECL_SAVED_INSNS (decl) == 0) + { + int specd = DECL_INLINE (decl); + char *lose; + + /* If requested, consider whether to make this function inline. */ + if (specd || flag_inline_functions) + TIMEVAR (integration_time, + { + lose = function_cannot_inline_p (decl); + if (lose) + { + if (warn_inline && specd) + warning_with_decl (decl, lose); + DECL_INLINE (decl) = 0; + } + else + DECL_INLINE (decl) = 1; + }); + + insns = get_insns (); + + /* Dump the rtl code if we are dumping rtl. */ + + if (rtl_dump) + TIMEVAR (dump_time, + { + fprintf (rtl_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + if (DECL_SAVED_INSNS (decl)) + fprintf (rtl_dump_file, ";; (integrable)\n\n"); + print_rtl (rtl_dump_file, insns); + fflush (rtl_dump_file); + }); + + /* If function is inline, and we don't yet know whether to + compile it by itself, defer decision till end of compilation. + finish_compilation will call rest_of_compilation again + for those functions that need to be output. */ + + if (DECL_INLINE (decl) + && ((! TREE_PUBLIC (decl) && ! TREE_ADDRESSABLE (decl) + && ! flag_keep_inline_functions) + || DECL_EXTERNAL (decl))) + { +#ifdef DWARF_DEBUGGING_INFO + /* Generate the DWARF info for the "abstract" instance + of a function which we may later generate inlined and/or + out-of-line instances of. */ + if (write_symbols == DWARF_DEBUG) + { + set_decl_abstract_flags (decl, 1); + TIMEVAR (symout_time, dwarfout_file_scope_decl (decl, 0)); + set_decl_abstract_flags (decl, 0); + } +#endif + TIMEVAR (integration_time, save_for_inline_nocopy (decl)); + goto exit_rest_of_compilation; + } + + /* If we have to compile the function now, save its rtl and subdecls + so that its compilation will not affect what others get. */ + if (DECL_INLINE (decl)) + { +#ifdef DWARF_DEBUGGING_INFO + /* Generate the DWARF info for the "abstract" instance of + a function which we will generate an out-of-line instance + of almost immediately (and which we may also later generate + various inlined instances of). */ + if (write_symbols == DWARF_DEBUG) + { + set_decl_abstract_flags (decl, 1); + TIMEVAR (symout_time, dwarfout_file_scope_decl (decl, 0)); + set_decl_abstract_flags (decl, 0); + } +#endif + saved_block_tree = DECL_INITIAL (decl); + saved_arguments = DECL_ARGUMENTS (decl); + TIMEVAR (integration_time, save_for_inline_copying (decl)); + } + } + + TREE_ASM_WRITTEN (decl) = 1; + + /* Now that integrate will no longer see our rtl, we need not distinguish + between the return value of this function and the return value of called + functions. */ + rtx_equal_function_value_matters = 0; + + /* Don't return yet if -Wreturn-type; we need to do jump_optimize. */ + if ((rtl_dump_and_exit || flag_syntax_only) && !warn_return_type) + { + goto exit_rest_of_compilation; + } + + /* From now on, allocate rtl in current_obstack, not in saveable_obstack. + Note that that may have been done above, in save_for_inline_copying. + The call to resume_temporary_allocation near the end of this function + goes back to the usual state of affairs. */ + + rtl_in_current_obstack (); + +#ifdef FINALIZE_PIC + /* If we are doing position-independent code generation, now + is the time to output special prologues and epilogues. + We do not want to do this earlier, because it just clutters + up inline functions with meaningless insns. */ + if (flag_pic) + FINALIZE_PIC; +#endif + + insns = get_insns (); + + /* Copy any shared structure that should not be shared. */ + + unshare_all_rtl (insns); + + /* Instantiate all virtual registers. */ + + instantiate_virtual_regs (current_function_decl, get_insns ()); + + /* See if we have allocated stack slots that are not directly addressable. + If so, scan all the insns and create explicit address computation + for all references to such slots. */ +/* fixup_stack_slots (); */ + + /* Do jump optimization the first time, if -opt. + Also do it if -W, but in that case it doesn't change the rtl code, + it only computes whether control can drop off the end of the function. */ + + if (optimize > 0 || extra_warnings || warn_return_type + /* If function is `volatile', we should warn if it tries to return. */ + || TREE_THIS_VOLATILE (decl)) + { + TIMEVAR (jump_time, reg_scan (insns, max_reg_num (), 0)); + TIMEVAR (jump_time, jump_optimize (insns, 0, 0, 1)); + } + + /* Now is when we stop if -fsyntax-only and -Wreturn-type. */ + if (rtl_dump_and_exit || flag_syntax_only) + goto exit_rest_of_compilation; + + /* Dump rtl code after jump, if we are doing that. */ + + if (jump_opt_dump) + TIMEVAR (dump_time, + { + fprintf (jump_opt_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + print_rtl (jump_opt_dump_file, insns); + fflush (jump_opt_dump_file); + }); + + /* Perform common subexpression elimination. + Nonzero value from `cse_main' means that jumps were simplified + and some code may now be unreachable, so do + jump optimization again. */ + + if (cse_dump) + TIMEVAR (dump_time, + { + fprintf (cse_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + }); + + if (optimize > 0) + { + TIMEVAR (cse_time, reg_scan (insns, max_reg_num (), 1)); + + if (flag_thread_jumps) + /* Hacks by tiemann & kenner. */ + TIMEVAR (jump_time, thread_jumps (insns, max_reg_num (), 0)); + + TIMEVAR (cse_time, tem = cse_main (insns, max_reg_num (), + 0, cse_dump_file)); + TIMEVAR (cse_time, delete_dead_from_cse (insns, max_reg_num ())); + + if (tem) + TIMEVAR (jump_time, jump_optimize (insns, 0, 0, 0)); + } + + /* Dump rtl code after cse, if we are doing that. */ + + if (cse_dump) + TIMEVAR (dump_time, + { + print_rtl (cse_dump_file, insns); + fflush (cse_dump_file); + }); + + if (loop_dump) + TIMEVAR (dump_time, + { + fprintf (loop_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + }); + + /* Move constant computations out of loops. */ + + if (optimize > 0) + { + TIMEVAR (loop_time, + { + loop_optimize (insns, loop_dump_file); + }); + } + + /* Dump rtl code after loop opt, if we are doing that. */ + + if (loop_dump) + TIMEVAR (dump_time, + { + print_rtl (loop_dump_file, insns); + fflush (loop_dump_file); + }); + + if (cse2_dump) + TIMEVAR (dump_time, + { + fprintf (cse2_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + }); + + if (optimize > 0 && flag_rerun_cse_after_loop) + { + TIMEVAR (cse2_time, reg_scan (insns, max_reg_num (), 0)); + + TIMEVAR (cse2_time, tem = cse_main (insns, max_reg_num (), + 1, cse2_dump_file)); + if (tem) + TIMEVAR (jump_time, jump_optimize (insns, 0, 0, 0)); + } + + if (optimize > 0 && flag_thread_jumps) + /* This pass of jump threading straightens out code + that was kinked by loop optimization. */ + TIMEVAR (jump_time, thread_jumps (insns, max_reg_num (), 0)); + + /* Dump rtl code after cse, if we are doing that. */ + + if (cse2_dump) + TIMEVAR (dump_time, + { + print_rtl (cse2_dump_file, insns); + fflush (cse2_dump_file); + }); + + /* We are no longer anticipating cse in this function, at least. */ + + cse_not_expected = 1; + + /* Now we choose between stupid (pcc-like) register allocation + (if we got the -noreg switch and not -opt) + and smart register allocation. */ + + if (optimize > 0) /* Stupid allocation probably won't work */ + obey_regdecls = 0; /* if optimizations being done. */ + + regclass_init (); + + /* Print function header into flow dump now + because doing the flow analysis makes some of the dump. */ + + if (flow_dump) + TIMEVAR (dump_time, + { + fprintf (flow_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + }); + + if (obey_regdecls) + { + TIMEVAR (flow_time, + { + regclass (insns, max_reg_num ()); + stupid_life_analysis (insns, max_reg_num (), + flow_dump_file); + }); + } + else + { + /* Do control and data flow analysis, + and write some of the results to dump file. */ + + TIMEVAR (flow_time, flow_analysis (insns, max_reg_num (), + flow_dump_file)); + if (warn_uninitialized) + { + uninitialized_vars_warning (DECL_INITIAL (decl)); + setjmp_args_warning (); + } + } + + /* Dump rtl after flow analysis. */ + + if (flow_dump) + TIMEVAR (dump_time, + { + print_rtl (flow_dump_file, insns); + fflush (flow_dump_file); + }); + + /* If -opt, try combining insns through substitution. */ + + if (optimize > 0) + TIMEVAR (combine_time, combine_instructions (insns, max_reg_num ())); + + /* Dump rtl code after insn combination. */ + + if (combine_dump) + TIMEVAR (dump_time, + { + fprintf (combine_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + dump_combine_stats (combine_dump_file); + print_rtl (combine_dump_file, insns); + fflush (combine_dump_file); + }); + + /* Print function header into sched dump now + because doing the sched analysis makes some of the dump. */ + + if (sched_dump) + TIMEVAR (dump_time, + { + fprintf (sched_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + }); + + if (optimize > 0 && flag_schedule_insns) + { + /* Do control and data sched analysis, + and write some of the results to dump file. */ + + TIMEVAR (sched_time, schedule_insns (sched_dump_file)); + } + + /* Dump rtl after instruction scheduling. */ + + if (sched_dump) + TIMEVAR (dump_time, + { + print_rtl (sched_dump_file, insns); + fflush (sched_dump_file); + }); + + /* Unless we did stupid register allocation, + allocate pseudo-regs that are used only within 1 basic block. */ + + if (!obey_regdecls) + TIMEVAR (local_alloc_time, + { + regclass (insns, max_reg_num ()); + local_alloc (); + }); + + /* Dump rtl code after allocating regs within basic blocks. */ + + if (local_reg_dump) + TIMEVAR (dump_time, + { + fprintf (local_reg_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + dump_flow_info (local_reg_dump_file); + dump_local_alloc (local_reg_dump_file); + print_rtl (local_reg_dump_file, insns); + fflush (local_reg_dump_file); + }); + + if (global_reg_dump) + TIMEVAR (dump_time, + fprintf (global_reg_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl)))); + + /* Unless we did stupid register allocation, + allocate remaining pseudo-regs, then do the reload pass + fixing up any insns that are invalid. */ + + TIMEVAR (global_alloc_time, + { + if (!obey_regdecls) + failure = global_alloc (global_reg_dump_file); + else + failure = reload (insns, 0, global_reg_dump_file); + }); + + if (global_reg_dump) + TIMEVAR (dump_time, + { + dump_global_regs (global_reg_dump_file); + print_rtl (global_reg_dump_file, insns); + fflush (global_reg_dump_file); + }); + + if (failure) + goto exit_rest_of_compilation; + + reload_completed = 1; + + /* On some machines, the prologue and epilogue code, or parts thereof, + can be represented as RTL. Doing so lets us schedule insns between + it and the rest of the code and also allows delayed branch + scheduling to operate in the epilogue. */ + + thread_prologue_and_epilogue_insns (insns); + + if (optimize > 0 && flag_schedule_insns_after_reload) + { + if (sched2_dump) + TIMEVAR (dump_time, + { + fprintf (sched2_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + }); + + /* Do control and data sched analysis again, + and write some more of the results to dump file. */ + + TIMEVAR (sched2_time, schedule_insns (sched2_dump_file)); + + /* Dump rtl after post-reorder instruction scheduling. */ + + if (sched2_dump) + TIMEVAR (dump_time, + { + print_rtl (sched2_dump_file, insns); + fflush (sched2_dump_file); + }); + } + +#ifdef LEAF_REGISTERS + leaf_function = 0; + if (optimize > 0 && only_leaf_regs_used () && leaf_function_p ()) + leaf_function = 1; +#endif + + /* One more attempt to remove jumps to .+1 + left by dead-store-elimination. + Also do cross-jumping this time + and delete no-op move insns. */ + + if (optimize > 0) + { + TIMEVAR (jump_time, jump_optimize (insns, 1, 1, 0)); + } + + /* Dump rtl code after jump, if we are doing that. */ + + if (jump2_opt_dump) + TIMEVAR (dump_time, + { + fprintf (jump2_opt_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + print_rtl (jump2_opt_dump_file, insns); + fflush (jump2_opt_dump_file); + }); + + /* If a scheduling pass for delayed branches is to be done, + call the scheduling code. */ + +#ifdef DELAY_SLOTS + if (optimize > 0 && flag_delayed_branch) + { + TIMEVAR (dbr_sched_time, dbr_schedule (insns, dbr_sched_dump_file)); + if (dbr_sched_dump) + { + TIMEVAR (dump_time, + { + fprintf (dbr_sched_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + print_rtl (dbr_sched_dump_file, insns); + fflush (dbr_sched_dump_file); + }); + } + } +#endif + + if (optimize > 0) + /* Shorten branches. */ + TIMEVAR (shorten_branch_time, + { + shorten_branches (get_insns ()); + }); + +#ifdef STACK_REGS + TIMEVAR (stack_reg_time, reg_to_stack (insns, stack_reg_dump_file)); + if (stack_reg_dump) + { + TIMEVAR (dump_time, + { + fprintf (stack_reg_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + print_rtl (stack_reg_dump_file, insns); + fflush (stack_reg_dump_file); + }); + } +#endif + + /* Now turn the rtl into assembler code. */ + + TIMEVAR (final_time, + { + rtx x; + char *fnname; + + /* Get the function's name, as described by its RTL. + This may be different from the DECL_NAME name used + in the source file. */ + + x = DECL_RTL (decl); + if (GET_CODE (x) != MEM) + abort (); + x = XEXP (x, 0); + if (GET_CODE (x) != SYMBOL_REF) + abort (); + fnname = XSTR (x, 0); + + assemble_start_function (decl, fnname); + final_start_function (insns, asm_out_file, optimize); + final (insns, asm_out_file, optimize, 0); + final_end_function (insns, asm_out_file, optimize); + assemble_end_function (decl, fnname); + fflush (asm_out_file); + }); + + /* Write DBX symbols if requested */ + + /* Note that for those inline functions where we don't initially + know for certain that we will be generating an out-of-line copy, + the first invocation of this routine (rest_of_compilation) will + skip over this code by doing a `goto exit_rest_of_compilation;'. + Later on, finish_compilation will call rest_of_compilation again + for those inline functions that need to have out-of-line copies + generated. During that call, we *will* be routed past here. */ + +#ifdef DBX_DEBUGGING_INFO + if (write_symbols == DBX_DEBUG) + TIMEVAR (symout_time, dbxout_function (decl)); +#endif + +#ifdef DWARF_DEBUGGING_INFO + if (write_symbols == DWARF_DEBUG) + TIMEVAR (symout_time, dwarfout_file_scope_decl (decl, 0)); +#endif + + exit_rest_of_compilation: + + /* In case the function was not output, + don't leave any temporary anonymous types + queued up for sdb output. */ +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG) + sdbout_types (NULL_TREE); +#endif + + /* Put back the tree of subblocks and list of arguments + from before we copied them. + Code generation and the output of debugging info may have modified + the copy, but the original is unchanged. */ + + if (saved_block_tree != 0) + DECL_INITIAL (decl) = saved_block_tree; + if (saved_arguments != 0) + DECL_ARGUMENTS (decl) = saved_arguments; + + reload_completed = 0; + + /* Clear out the real_constant_chain before some of the rtx's + it runs through become garbage. */ + + clear_const_double_mem (); + + /* Cancel the effect of rtl_in_current_obstack. */ + + resume_temporary_allocation (); + + /* The parsing time is all the time spent in yyparse + *except* what is spent in this function. */ + + parse_time -= get_run_time () - start_time; +} + +/* Entry point of cc1/c++. Decode command args, then call compile_file. + Exit code is 35 if can't open files, 34 if fatal error, + 33 if had nonfatal errors, else success. */ + +int +main (argc, argv, envp) + int argc; + char **argv; + char **envp; +{ + register int i; + char *filename = 0; + int flag_print_mem = 0; + int version_flag = 0; + char *p; + + /* save in case md file wants to emit args as a comment. */ + save_argc = argc; + save_argv = argv; + + p = argv[0] + strlen (argv[0]); + while (p != argv[0] && p[-1] != '/') --p; + progname = p; + +#ifdef RLIMIT_STACK + /* Get rid of any avoidable limit on stack size. */ + { + struct rlimit rlim; + + /* Set the stack limit huge so that alloca does not fail. */ + getrlimit (RLIMIT_STACK, &rlim); + rlim.rlim_cur = rlim.rlim_max; + setrlimit (RLIMIT_STACK, &rlim); + } +#endif /* RLIMIT_STACK */ + + signal (SIGFPE, float_signal); + +#ifdef SIGPIPE + signal (SIGPIPE, pipe_closed); +#endif + + decl_printable_name = decl_name; + lang_expand_expr = (struct rtx_def *(*)()) do_abort; + + /* Initialize whether `char' is signed. */ + flag_signed_char = DEFAULT_SIGNED_CHAR; +#ifdef DEFAULT_SHORT_ENUMS + /* Initialize how much space enums occupy, by default. */ + flag_short_enums = DEFAULT_SHORT_ENUMS; +#endif + + /* Scan to see what optimization level has been specified. That will + determine the default value of many flags. */ + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-O")) + { + optimize = 1; + } + else if (argv[i][0] == '-' && argv[i][1] == 'O') + { + /* Handle -O2, -O3, -O69, ... */ + char *p = &argv[i][2]; + int c; + + while (c = *p++) + if (! (c >= '0' && c <= '9')) + break; + if (c == 0) + optimize = atoi (&argv[i][2]); + } + } + + obey_regdecls = (optimize == 0); + if (optimize == 0) + { + flag_no_inline = 1; + warn_inline = 0; + } + + if (optimize >= 1) + { + flag_defer_pop = 1; + flag_thread_jumps = 1; +#ifdef DELAY_SLOTS + flag_delayed_branch = 1; +#endif + } + + if (optimize >= 2) + { + flag_cse_follow_jumps = 1; + flag_cse_skip_blocks = 1; + flag_expensive_optimizations = 1; + flag_strength_reduce = 1; + flag_rerun_cse_after_loop = 1; + flag_caller_saves = 1; +#ifdef INSN_SCHEDULING + flag_schedule_insns = 1; + flag_schedule_insns_after_reload = 1; +#endif + } + +#ifdef OPTIMIZATION_OPTIONS + /* Allow default optimizations to be specified on a per-machine basis. */ + OPTIMIZATION_OPTIONS (optimize); +#endif + + /* Initialize register usage now so switches may override. */ + init_reg_sets (); + + target_flags = 0; + set_target_switch (""); + + for (i = 1; i < argc; i++) + { + int j; + /* If this is a language-specific option, + decode it in a language-specific way. */ + for (j = 0; lang_options[j] != 0; j++) + if (!strncmp (argv[i], lang_options[j], + strlen (lang_options[j]))) + break; + if (lang_options[j] != 0) + /* If the option is valid for *some* language, + treat it as valid even if this language doesn't understand it. */ + lang_decode_option (argv[i]); + else if (argv[i][0] == '-' && argv[i][1] != 0) + { + register char *str = argv[i] + 1; + if (str[0] == 'Y') + str++; + + if (str[0] == 'm') + set_target_switch (&str[1]); + else if (!strcmp (str, "dumpbase")) + { + dump_base_name = argv[++i]; + } + else if (str[0] == 'd') + { + register char *p = &str[1]; + while (*p) + switch (*p++) + { + case 'a': + combine_dump = 1; + dbr_sched_dump = 1; + flow_dump = 1; + global_reg_dump = 1; + jump_opt_dump = 1; + jump2_opt_dump = 1; + local_reg_dump = 1; + loop_dump = 1; + rtl_dump = 1; + cse_dump = 1, cse2_dump = 1; + sched_dump = 1; + sched2_dump = 1; + stack_reg_dump = 1; + break; + case 'k': + stack_reg_dump = 1; + break; + case 'c': + combine_dump = 1; + break; + case 'd': + dbr_sched_dump = 1; + break; + case 'f': + flow_dump = 1; + break; + case 'g': + global_reg_dump = 1; + break; + case 'j': + jump_opt_dump = 1; + break; + case 'J': + jump2_opt_dump = 1; + break; + case 'l': + local_reg_dump = 1; + break; + case 'L': + loop_dump = 1; + break; + case 'm': + flag_print_mem = 1; + break; + case 'p': + flag_print_asm_name = 1; + break; + case 'r': + rtl_dump = 1; + break; + case 's': + cse_dump = 1; + break; + case 't': + cse2_dump = 1; + break; + case 'S': + sched_dump = 1; + break; + case 'R': + sched2_dump = 1; + break; + case 'y': + set_yydebug (1); + break; + + case 'x': + rtl_dump_and_exit = 1; + break; + } + } + else if (str[0] == 'f') + { + register char *p = &str[1]; + int found = 0; + + /* Some kind of -f option. + P's value is the option sans `-f'. + Search for it in the table of options. */ + + for (j = 0; + !found && j < sizeof (f_options) / sizeof (f_options[0]); + j++) + { + if (!strcmp (p, f_options[j].string)) + { + *f_options[j].variable = f_options[j].on_value; + /* A goto here would be cleaner, + but breaks the vax pcc. */ + found = 1; + } + if (p[0] == 'n' && p[1] == 'o' && p[2] == '-' + && ! strcmp (p+3, f_options[j].string)) + { + *f_options[j].variable = ! f_options[j].on_value; + found = 1; + } + } + + if (found) + ; + else if (!strncmp (p, "fixed-", 6)) + fix_register (&p[6], 1, 1); + else if (!strncmp (p, "call-used-", 10)) + fix_register (&p[10], 0, 1); + else if (!strncmp (p, "call-saved-", 11)) + fix_register (&p[11], 0, 0); + else + error ("Invalid option `%s'", argv[i]); + } + else if (str[0] == 'O') + { + register char *p = str+1; + while (*p && *p >= '0' && *p <= '9') + p++; + if (*p == '\0') + ; + else + error ("Invalid option `%s'", argv[i]); + } + else if (!strcmp (str, "pedantic")) + pedantic = 1; + else if (!strcmp (str, "pedantic-errors")) + flag_pedantic_errors = pedantic = 1; + else if (!strcmp (str, "quiet")) + quiet_flag = 1; + else if (!strcmp (str, "version")) + version_flag = 1; + else if (!strcmp (str, "w")) + inhibit_warnings = 1; + else if (!strcmp (str, "W")) + { + extra_warnings = 1; + warn_uninitialized = 1; + } + else if (str[0] == 'W') + { + register char *p = &str[1]; + int found = 0; + + /* Some kind of -W option. + P's value is the option sans `-W'. + Search for it in the table of options. */ + + for (j = 0; + !found && j < sizeof (W_options) / sizeof (W_options[0]); + j++) + { + if (!strcmp (p, W_options[j].string)) + { + *W_options[j].variable = W_options[j].on_value; + /* A goto here would be cleaner, + but breaks the vax pcc. */ + found = 1; + } + if (p[0] == 'n' && p[1] == 'o' && p[2] == '-' + && ! strcmp (p+3, W_options[j].string)) + { + *W_options[j].variable = ! W_options[j].on_value; + found = 1; + } + } + + if (found) + ; + else if (!strncmp (p, "id-clash-", 9)) + { + char *endp = p + 9; + + while (*endp) + { + if (*endp >= '0' && *endp <= '9') + endp++; + else + { + error ("Invalid option `%s'", argv[i]); + goto id_clash_lose; + } + } + warn_id_clash = 1; + id_clash_len = atoi (str + 10); + id_clash_lose: ; + } + else + error ("Invalid option `%s'", argv[i]); + } + else if (!strcmp (str, "p")) + profile_flag = 1; + else if (!strcmp (str, "a")) + { +#if !defined (BLOCK_PROFILER) || !defined (FUNCTION_BLOCK_PROFILER) + warning ("`-a' option (basic block profile) not supported"); +#else + profile_block_flag = 1; +#endif + } + else if (str[0] == 'g') + { + char *p = str + 1; + char *q; + unsigned len; + unsigned level; + + while (*p && (*p < '0' || *p > '9')) + p++; + len = p - str; + q = p; + while (*q && (*q >= '0' && *q <= '9')) + q++; + if (*p) + level = atoi (p); + else + level = 2; /* default debugging info level */ + if (*q || level > 3) + { + warning ("invalid debug level specification in option: `-%s'", + str); + warning ("no debugging information will be generated"); + level = 0; + } + + /* If more than one debugging type is supported, + you must define PREFERRED_DEBUGGING_TYPE + to choose a format in a system-dependent way. */ + /* This is one long line cause VAXC can't handle a \-newline. */ +#if 1 < (defined (DBX_DEBUGGING_INFO) + defined (SDB_DEBUGGING_INFO) + defined (DWARF_DEBUGGING_INFO) + defined (XCOFF_DEBUGGING_INFO)) +#ifdef PREFERRED_DEBUGGING_TYPE + if (!strncmp (str, "ggdb", len)) + write_symbols = PREFERRED_DEBUGGING_TYPE; +#else /* no PREFERRED_DEBUGGING_TYPE */ +You Lose! You must define PREFERRED_DEBUGGING_TYPE! +#endif /* no PREFERRED_DEBUGGING_TYPE */ +#endif /* More than one debugger format enabled. */ +#ifdef DBX_DEBUGGING_INFO + if (write_symbols != NO_DEBUG) + ; + else if (!strncmp (str, "ggdb", len)) + write_symbols = DBX_DEBUG; + else if (!strncmp (str, "gstabs", len)) + write_symbols = DBX_DEBUG; + else if (!strncmp (str, "gstabs+", len)) + write_symbols = DBX_DEBUG; + + /* Always enable extensions for -ggdb or -gstabs+, + always disable for -gstabs. + For plain -g, use system-specific default. */ + if (write_symbols == DBX_DEBUG && !strncmp (str, "ggdb", len) + && len >= 2) + use_gnu_debug_info_extensions = 1; + else if (write_symbols == DBX_DEBUG && !strncmp (str, "gstabs+", len) + && len >= 7) + use_gnu_debug_info_extensions = 1; + else if (write_symbols == DBX_DEBUG + && !strncmp (str, "gstabs", len) && len >= 2) + use_gnu_debug_info_extensions = 0; + else + use_gnu_debug_info_extensions = DEFAULT_GDB_EXTENSIONS; +#endif /* DBX_DEBUGGING_INFO */ +#ifdef DWARF_DEBUGGING_INFO + if (write_symbols != NO_DEBUG) + ; + else if (!strncmp (str, "g", len)) + write_symbols = DWARF_DEBUG; + else if (!strncmp (str, "ggdb", len)) + write_symbols = DWARF_DEBUG; + else if (!strncmp (str, "gdwarf", len)) + write_symbols = DWARF_DEBUG; + + /* Always enable extensions for -ggdb or -gdwarf+, + always disable for -gdwarf. + For plain -g, use system-specific default. */ + if (write_symbols == DWARF_DEBUG && !strncmp (str, "ggdb", len) + && len >= 2) + use_gnu_debug_info_extensions = 1; + else if (write_symbols == DWARF_DEBUG && !strcmp (str, "gdwarf+")) + use_gnu_debug_info_extensions = 1; + else if (write_symbols == DWARF_DEBUG + && !strncmp (str, "gdwarf", len) && len >= 2) + use_gnu_debug_info_extensions = 0; + else + use_gnu_debug_info_extensions = DEFAULT_GDB_EXTENSIONS; +#endif +#ifdef SDB_DEBUGGING_INFO + if (write_symbols != NO_DEBUG) + ; + else if (!strncmp (str, "g", len)) + write_symbols = SDB_DEBUG; + else if (!strncmp (str, "gdb", len)) + write_symbols = SDB_DEBUG; + else if (!strncmp (str, "gcoff", len)) + write_symbols = SDB_DEBUG; +#endif /* SDB_DEBUGGING_INFO */ +#ifdef XCOFF_DEBUGGING_INFO + if (write_symbols != NO_DEBUG) + ; + else if (!strncmp (str, "g", len)) + write_symbols = XCOFF_DEBUG; + else if (!strncmp (str, "ggdb", len)) + write_symbols = XCOFF_DEBUG; + else if (!strncmp (str, "gxcoff", len)) + write_symbols = XCOFF_DEBUG; + + /* Always enable extensions for -ggdb or -gxcoff+, + always disable for -gxcoff. + For plain -g, use system-specific default. */ + if (write_symbols == XCOFF_DEBUG && !strncmp (str, "ggdb", len) + && len >= 2) + use_gnu_debug_info_extensions = 1; + else if (write_symbols == XCOFF_DEBUG && !strcmp (str, "gxcoff+")) + use_gnu_debug_info_extensions = 1; + else if (write_symbols == XCOFF_DEBUG + && !strncmp (str, "gxcoff", len) && len >= 2) + use_gnu_debug_info_extensions = 0; + else + use_gnu_debug_info_extensions = DEFAULT_GDB_EXTENSIONS; +#endif + if (write_symbols == NO_DEBUG) + warning ("`-%s' option not supported on this version of GCC", str); + else if (level == 0) + write_symbols = NO_DEBUG; + else + debug_info_level = (enum debug_info_level) level; + } + else if (!strcmp (str, "o")) + { + asm_file_name = argv[++i]; + } + else if (str[0] == 'G') + { + g_switch_set = TRUE; + g_switch_value = atoi ((str[1] != '\0') ? str+1 : argv[++i]); + } + else if (!strncmp (str, "aux-info", 8)) + { + flag_gen_aux_info = 1; + aux_info_file_name = (str[8] != '\0' ? str+8 : argv[++i]); + } + else + error ("Invalid option `%s'", argv[i]); + } + else if (argv[i][0] == '+') + error ("Invalid option `%s'", argv[i]); + else + filename = argv[i]; + } + + if (optimize == 0) + { + /* Inlining does not work if not optimizing, + so force it not to be done. */ + flag_no_inline = 1; + warn_inline = 0; + + /* The c_decode_option and lang_decode_option functions set + this to `2' if -Wall is used, so we can avoid giving out + lots of errors for people who don't realize what -Wall does. */ + if (warn_uninitialized == 1) + warning ("-Wuninitialized is not supported without -O"); + } + +#if defined(DWARF_DEBUGGING_INFO) + if (write_symbols == DWARF_DEBUG + && strcmp (language_string, "GNU C++") == 0) + { + warning ("-g option not supported for C++ on SVR4 systems"); + write_symbols = NO_DEBUG; + } +#endif /* defined(DWARF_DEBUGGING_INFO) */ + +#ifdef OVERRIDE_OPTIONS + /* Some machines may reject certain combinations of options. */ + OVERRIDE_OPTIONS; +#endif + + /* Unrolling all loops implies that standard loop unrolling must also + be done. */ + if (flag_unroll_all_loops) + flag_unroll_loops = 1; + /* Loop unrolling requires that strength_reduction be on also. Silently + turn on strength reduction here if it isn't already on. Also, the loop + unrolling code assumes that cse will be run after loop, so that must + be turned on also. */ + if (flag_unroll_loops) + { + flag_strength_reduce = 1; + flag_rerun_cse_after_loop = 1; + } + + /* Warn about options that are not supported on this machine. */ +#ifndef INSN_SCHEDULING + if (flag_schedule_insns || flag_schedule_insns_after_reload) + warning ("instruction scheduling not supported on this target machine"); +#endif +#ifndef DELAY_SLOTS + if (flag_delayed_branch) + warning ("this target machine does not have delayed branches"); +#endif + + /* If we are in verbose mode, write out the version and maybe all the + option flags in use. */ + if (version_flag) + { + fprintf (stderr, "%s version %s", language_string, version_string); +#ifdef TARGET_VERSION + TARGET_VERSION; +#endif +#ifdef __GNUC__ +#ifndef __VERSION__ +#define __VERSION__ "[unknown]" +#endif + fprintf (stderr, " compiled by GNU C version %s.\n", __VERSION__); +#else + fprintf (stderr, " compiled by CC.\n"); +#endif + if (! quiet_flag) + print_switch_values (); + } + + /* Now that register usage is specified, convert it to HARD_REG_SETs. */ + init_reg_sets_1 (); + + compile_file (filename); + +#ifndef OS2 +#ifndef VMS + if (flag_print_mem) + { + char *lim = (char *) sbrk (0); + + fprintf (stderr, "Data size %d.\n", + lim - (char *) &environ); + fflush (stderr); + +#ifdef USG + system ("ps -l 1>&2"); +#else /* not USG */ + system ("ps v"); +#endif /* not USG */ + } +#endif /* not VMS */ +#endif /* not OS2 */ + + if (errorcount) + exit (FATAL_EXIT_CODE); + if (sorrycount) + exit (FATAL_EXIT_CODE); + exit (SUCCESS_EXIT_CODE); + return 34; +} + +/* Decode -m switches. */ + +/* Here is a table, controlled by the tm.h file, listing each -m switch + and which bits in `target_switches' it should set or clear. + If VALUE is positive, it is bits to set. + If VALUE is negative, -VALUE is bits to clear. + (The sign bit is not used so there is no confusion.) */ + +struct {char *name; int value;} target_switches [] + = TARGET_SWITCHES; + +/* This table is similar, but allows the switch to have a value. */ + +#ifdef TARGET_OPTIONS +struct {char *prefix; char ** variable;} target_options [] + = TARGET_OPTIONS; +#endif + +/* Decode the switch -mNAME. */ + +void +set_target_switch (name) + char *name; +{ + register int j; + int valid = 0; + + for (j = 0; j < sizeof target_switches / sizeof target_switches[0]; j++) + if (!strcmp (target_switches[j].name, name)) + { + if (target_switches[j].value < 0) + target_flags &= ~-target_switches[j].value; + else + target_flags |= target_switches[j].value; + valid = 1; + } + +#ifdef TARGET_OPTIONS + if (!valid) + for (j = 0; j < sizeof target_options / sizeof target_options[0]; j++) + { + int len = strlen (target_options[j].prefix); + if (!strncmp (target_options[j].prefix, name, len)) + { + *target_options[j].variable = name + len; + valid = 1; + } + } +#endif + + if (!valid) + error ("Invalid option `%s'", name); +} + +/* Variable used for communication between the following two routines. */ + +static int line_position; + +/* Print an option value and adjust the position in the line. */ + +static void +print_single_switch (type, name) + char *type, *name; +{ + fprintf (stderr, " %s%s", type, name); + + line_position += strlen (type) + strlen (name) + 1; + + if (line_position > 65) + { + fprintf (stderr, "\n\t"); + line_position = 8; + } +} + +/* Print default target switches for -version. */ + +static void +print_switch_values () +{ + register int j; + + fprintf (stderr, "enabled:"); + line_position = 8; + + for (j = 0; j < sizeof f_options / sizeof f_options[0]; j++) + if (*f_options[j].variable == f_options[j].on_value) + print_single_switch ("-f", f_options[j].string); + + for (j = 0; j < sizeof W_options / sizeof W_options[0]; j++) + if (*W_options[j].variable == W_options[j].on_value) + print_single_switch ("-W", W_options[j].string); + + for (j = 0; j < sizeof target_switches / sizeof target_switches[0]; j++) + if (target_switches[j].name[0] != '\0' + && target_switches[j].value > 0 + && ((target_switches[j].value & target_flags) + == target_switches[j].value)) + print_single_switch ("-m", target_switches[j].name); + + fprintf (stderr, "\n"); +} diff --git a/gnu/usr.bin/cc/common/tree.c b/gnu/usr.bin/cc/lib/tree.c similarity index 100% rename from gnu/usr.bin/cc/common/tree.c rename to gnu/usr.bin/cc/lib/tree.c diff --git a/gnu/usr.bin/cc/common/tree.def b/gnu/usr.bin/cc/lib/tree.def similarity index 100% rename from gnu/usr.bin/cc/common/tree.def rename to gnu/usr.bin/cc/lib/tree.def diff --git a/gnu/usr.bin/cc/common/tree.h b/gnu/usr.bin/cc/lib/tree.h similarity index 100% rename from gnu/usr.bin/cc/common/tree.h rename to gnu/usr.bin/cc/lib/tree.h diff --git a/gnu/usr.bin/cc/common/typeclass.h b/gnu/usr.bin/cc/lib/typeclass.h similarity index 100% rename from gnu/usr.bin/cc/common/typeclass.h rename to gnu/usr.bin/cc/lib/typeclass.h diff --git a/gnu/usr.bin/cc/lib/unroll.c b/gnu/usr.bin/cc/lib/unroll.c new file mode 100644 index 0000000000..fc8a3f69f1 --- /dev/null +++ b/gnu/usr.bin/cc/lib/unroll.c @@ -0,0 +1,3251 @@ +/* Try to unroll loops, and split induction variables. + Copyright (C) 1992, 1993 Free Software Foundation, Inc. + Contributed by James E. Wilson, Cygnus Support/UC Berkeley. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Try to unroll a loop, and split induction variables. + + Loops for which the number of iterations can be calculated exactly are + handled specially. If the number of iterations times the insn_count is + less than MAX_UNROLLED_INSNS, then the loop is unrolled completely. + Otherwise, we try to unroll the loop a number of times modulo the number + of iterations, so that only one exit test will be needed. It is unrolled + a number of times approximately equal to MAX_UNROLLED_INSNS divided by + the insn count. + + Otherwise, if the number of iterations can be calculated exactly at + run time, and the loop is always entered at the top, then we try to + precondition the loop. That is, at run time, calculate how many times + the loop will execute, and then execute the loop body a few times so + that the remaining iterations will be some multiple of 4 (or 2 if the + loop is large). Then fall through to a loop unrolled 4 (or 2) times, + with only one exit test needed at the end of the loop. + + Otherwise, if the number of iterations can not be calculated exactly, + not even at run time, then we still unroll the loop a number of times + approximately equal to MAX_UNROLLED_INSNS divided by the insn count, + but there must be an exit test after each copy of the loop body. + + For each induction variable, which is dead outside the loop (replaceable) + or for which we can easily calculate the final value, if we can easily + calculate its value at each place where it is set as a function of the + current loop unroll count and the variable's value at loop entry, then + the induction variable is split into `N' different variables, one for + each copy of the loop body. One variable is live across the backward + branch, and the others are all calculated as a function of this variable. + This helps eliminate data dependencies, and leads to further opportunities + for cse. */ + +/* Possible improvements follow: */ + +/* ??? Add an extra pass somewhere to determine whether unrolling will + give any benefit. E.g. after generating all unrolled insns, compute the + cost of all insns and compare against cost of insns in rolled loop. + + - On traditional architectures, unrolling a non-constant bound loop + is a win if there is a giv whose only use is in memory addresses, the + memory addresses can be split, and hence giv increments can be + eliminated. + - It is also a win if the loop is executed many times, and preconditioning + can be performed for the loop. + Add code to check for these and similar cases. */ + +/* ??? Improve control of which loops get unrolled. Could use profiling + info to only unroll the most commonly executed loops. Perhaps have + a user specifyable option to control the amount of code expansion, + or the percent of loops to consider for unrolling. Etc. */ + +/* ??? Look at the register copies inside the loop to see if they form a + simple permutation. If so, iterate the permutation until it gets back to + the start state. This is how many times we should unroll the loop, for + best results, because then all register copies can be eliminated. + For example, the lisp nreverse function should be unrolled 3 times + while (this) + { + next = this->cdr; + this->cdr = prev; + prev = this; + this = next; + } + + ??? The number of times to unroll the loop may also be based on data + references in the loop. For example, if we have a loop that references + x[i-1], x[i], and x[i+1], we should unroll it a multiple of 3 times. */ + +/* ??? Add some simple linear equation solving capability so that we can + determine the number of loop iterations for more complex loops. + For example, consider this loop from gdb + #define SWAP_TARGET_AND_HOST(buffer,len) + { + char tmp; + char *p = (char *) buffer; + char *q = ((char *) buffer) + len - 1; + int iterations = (len + 1) >> 1; + int i; + for (p; p < q; p++, q--;) + { + tmp = *q; + *q = *p; + *p = tmp; + } + } + Note that: + start value = p = &buffer + current_iteration + end value = q = &buffer + len - 1 - current_iteration + Given the loop exit test of "p < q", then there must be "q - p" iterations, + set equal to zero and solve for number of iterations: + q - p = len - 1 - 2*current_iteration = 0 + current_iteration = (len - 1) / 2 + Hence, there are (len - 1) / 2 (rounded up to the nearest integer) + iterations of this loop. */ + +/* ??? Currently, no labels are marked as loop invariant when doing loop + unrolling. This is because an insn inside the loop, that loads the address + of a label inside the loop into a register, could be moved outside the loop + by the invariant code motion pass if labels were invariant. If the loop + is subsequently unrolled, the code will be wrong because each unrolled + body of the loop will use the same address, whereas each actually needs a + different address. A case where this happens is when a loop containing + a switch statement is unrolled. + + It would be better to let labels be considered invariant. When we + unroll loops here, check to see if any insns using a label local to the + loop were moved before the loop. If so, then correct the problem, by + moving the insn back into the loop, or perhaps replicate the insn before + the loop, one copy for each time the loop is unrolled. */ + +/* The prime factors looked for when trying to unroll a loop by some + number which is modulo the total number of iterations. Just checking + for these 4 prime factors will find at least one factor for 75% of + all numbers theoretically. Practically speaking, this will succeed + almost all of the time since loops are generally a multiple of 2 + and/or 5. */ + +#define NUM_FACTORS 4 + +struct _factor { int factor, count; } factors[NUM_FACTORS] + = { {2, 0}, {3, 0}, {5, 0}, {7, 0}}; + +/* Describes the different types of loop unrolling performed. */ + +enum unroll_types { UNROLL_COMPLETELY, UNROLL_MODULO, UNROLL_NAIVE }; + +#include "config.h" +#include "rtl.h" +#include "insn-config.h" +#include "integrate.h" +#include "regs.h" +#include "flags.h" +#include "expr.h" +#include +#include "loop.h" + +/* This controls which loops are unrolled, and by how much we unroll + them. */ + +#ifndef MAX_UNROLLED_INSNS +#define MAX_UNROLLED_INSNS 100 +#endif + +/* Indexed by register number, if non-zero, then it contains a pointer + to a struct induction for a DEST_REG giv which has been combined with + one of more address givs. This is needed because whenever such a DEST_REG + giv is modified, we must modify the value of all split address givs + that were combined with this DEST_REG giv. */ + +static struct induction **addr_combined_regs; + +/* Indexed by register number, if this is a splittable induction variable, + then this will hold the current value of the register, which depends on the + iteration number. */ + +static rtx *splittable_regs; + +/* Indexed by register number, if this is a splittable induction variable, + then this will hold the number of instructions in the loop that modify + the induction variable. Used to ensure that only the last insn modifying + a split iv will update the original iv of the dest. */ + +static int *splittable_regs_updates; + +/* Values describing the current loop's iteration variable. These are set up + by loop_iterations, and used by precondition_loop_p. */ + +static rtx loop_iteration_var; +static rtx loop_initial_value; +static rtx loop_increment; +static rtx loop_final_value; + +/* Forward declarations. */ + +static void init_reg_map (); +static int precondition_loop_p (); +static void copy_loop_body (); +static void iteration_info (); +static rtx approx_final_value (); +static int find_splittable_regs (); +static int find_splittable_givs (); +static rtx fold_rtx_mult_add (); + +/* Try to unroll one loop and split induction variables in the loop. + + The loop is described by the arguments LOOP_END, INSN_COUNT, and + LOOP_START. END_INSERT_BEFORE indicates where insns should be added + which need to be executed when the loop falls through. STRENGTH_REDUCTION_P + indicates whether information generated in the strength reduction pass + is available. + + This function is intended to be called from within `strength_reduce' + in loop.c. */ + +void +unroll_loop (loop_end, insn_count, loop_start, end_insert_before, + strength_reduce_p) + rtx loop_end; + int insn_count; + rtx loop_start; + rtx end_insert_before; + int strength_reduce_p; +{ + int i, j, temp; + int unroll_number = 1; + rtx copy_start, copy_end; + rtx insn, copy, sequence, pattern, tem; + int max_labelno, max_insnno; + rtx insert_before; + struct inline_remap *map; + char *local_label; + int maxregnum; + int new_maxregnum; + rtx exit_label = 0; + rtx start_label; + struct iv_class *bl; + struct induction *v; + int splitting_not_safe = 0; + enum unroll_types unroll_type; + int loop_preconditioned = 0; + rtx safety_label; + /* This points to the last real insn in the loop, which should be either + a JUMP_INSN (for conditional jumps) or a BARRIER (for unconditional + jumps). */ + rtx last_loop_insn; + + /* Don't bother unrolling huge loops. Since the minimum factor is + two, loops greater than one half of MAX_UNROLLED_INSNS will never + be unrolled. */ + if (insn_count > MAX_UNROLLED_INSNS / 2) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, "Unrolling failure: Loop too big.\n"); + return; + } + + /* When emitting debugger info, we can't unroll loops with unequal numbers + of block_beg and block_end notes, because that would unbalance the block + structure of the function. This can happen as a result of the + "if (foo) bar; else break;" optimization in jump.c. */ + + if (write_symbols != NO_DEBUG) + { + int block_begins = 0; + int block_ends = 0; + + for (insn = loop_start; insn != loop_end; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == NOTE) + { + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG) + block_begins++; + else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END) + block_ends++; + } + } + + if (block_begins != block_ends) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Unrolling failure: Unbalanced block notes.\n"); + return; + } + } + + /* Determine type of unroll to perform. Depends on the number of iterations + and the size of the loop. */ + + /* If there is no strength reduce info, then set loop_n_iterations to zero. + This can happen if strength_reduce can't find any bivs in the loop. + A value of zero indicates that the number of iterations could not be + calculated. */ + + if (! strength_reduce_p) + loop_n_iterations = 0; + + if (loop_dump_stream && loop_n_iterations > 0) + fprintf (loop_dump_stream, + "Loop unrolling: %d iterations.\n", loop_n_iterations); + + /* Find and save a pointer to the last nonnote insn in the loop. */ + + last_loop_insn = prev_nonnote_insn (loop_end); + + /* Calculate how many times to unroll the loop. Indicate whether or + not the loop is being completely unrolled. */ + + if (loop_n_iterations == 1) + { + /* If number of iterations is exactly 1, then eliminate the compare and + branch at the end of the loop since they will never be taken. + Then return, since no other action is needed here. */ + + /* If the last instruction is not a BARRIER or a JUMP_INSN, then + don't do anything. */ + + if (GET_CODE (last_loop_insn) == BARRIER) + { + /* Delete the jump insn. This will delete the barrier also. */ + delete_insn (PREV_INSN (last_loop_insn)); + } + else if (GET_CODE (last_loop_insn) == JUMP_INSN) + { +#ifdef HAVE_cc0 + /* The immediately preceding insn is a compare which must be + deleted. */ + delete_insn (last_loop_insn); + delete_insn (PREV_INSN (last_loop_insn)); +#else + /* The immediately preceding insn may not be the compare, so don't + delete it. */ + delete_insn (last_loop_insn); +#endif + } + return; + } + else if (loop_n_iterations > 0 + && loop_n_iterations * insn_count < MAX_UNROLLED_INSNS) + { + unroll_number = loop_n_iterations; + unroll_type = UNROLL_COMPLETELY; + } + else if (loop_n_iterations > 0) + { + /* Try to factor the number of iterations. Don't bother with the + general case, only using 2, 3, 5, and 7 will get 75% of all + numbers theoretically, and almost all in practice. */ + + for (i = 0; i < NUM_FACTORS; i++) + factors[i].count = 0; + + temp = loop_n_iterations; + for (i = NUM_FACTORS - 1; i >= 0; i--) + while (temp % factors[i].factor == 0) + { + factors[i].count++; + temp = temp / factors[i].factor; + } + + /* Start with the larger factors first so that we generally + get lots of unrolling. */ + + unroll_number = 1; + temp = insn_count; + for (i = 3; i >= 0; i--) + while (factors[i].count--) + { + if (temp * factors[i].factor < MAX_UNROLLED_INSNS) + { + unroll_number *= factors[i].factor; + temp *= factors[i].factor; + } + else + break; + } + + /* If we couldn't find any factors, then unroll as in the normal + case. */ + if (unroll_number == 1) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Loop unrolling: No factors found.\n"); + } + else + unroll_type = UNROLL_MODULO; + } + + + /* Default case, calculate number of times to unroll loop based on its + size. */ + if (unroll_number == 1) + { + if (8 * insn_count < MAX_UNROLLED_INSNS) + unroll_number = 8; + else if (4 * insn_count < MAX_UNROLLED_INSNS) + unroll_number = 4; + else + unroll_number = 2; + + unroll_type = UNROLL_NAIVE; + } + + /* Now we know how many times to unroll the loop. */ + + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Unrolling loop %d times.\n", unroll_number); + + + if (unroll_type == UNROLL_COMPLETELY || unroll_type == UNROLL_MODULO) + { + /* Loops of these types should never start with a jump down to + the exit condition test. For now, check for this case just to + be sure. UNROLL_NAIVE loops can be of this form, this case is + handled below. */ + insn = loop_start; + while (GET_CODE (insn) != CODE_LABEL && GET_CODE (insn) != JUMP_INSN) + insn = NEXT_INSN (insn); + if (GET_CODE (insn) == JUMP_INSN) + abort (); + } + + if (unroll_type == UNROLL_COMPLETELY) + { + /* Completely unrolling the loop: Delete the compare and branch at + the end (the last two instructions). This delete must done at the + very end of loop unrolling, to avoid problems with calls to + back_branch_in_range_p, which is called by find_splittable_regs. + All increments of splittable bivs/givs are changed to load constant + instructions. */ + + copy_start = loop_start; + + /* Set insert_before to the instruction immediately after the JUMP_INSN + (or BARRIER), so that any NOTEs between the JUMP_INSN and the end of + the loop will be correctly handled by copy_loop_body. */ + insert_before = NEXT_INSN (last_loop_insn); + + /* Set copy_end to the insn before the jump at the end of the loop. */ + if (GET_CODE (last_loop_insn) == BARRIER) + copy_end = PREV_INSN (PREV_INSN (last_loop_insn)); + else if (GET_CODE (last_loop_insn) == JUMP_INSN) + { +#ifdef HAVE_cc0 + /* The instruction immediately before the JUMP_INSN is a compare + instruction which we do not want to copy. */ + copy_end = PREV_INSN (PREV_INSN (last_loop_insn)); +#else + /* The instruction immediately before the JUMP_INSN may not be the + compare, so we must copy it. */ + copy_end = PREV_INSN (last_loop_insn); +#endif + } + else + { + /* We currently can't unroll a loop if it doesn't end with a + JUMP_INSN. There would need to be a mechanism that recognizes + this case, and then inserts a jump after each loop body, which + jumps to after the last loop body. */ + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Unrolling failure: loop does not end with a JUMP_INSN.\n"); + return; + } + } + else if (unroll_type == UNROLL_MODULO) + { + /* Partially unrolling the loop: The compare and branch at the end + (the last two instructions) must remain. Don't copy the compare + and branch instructions at the end of the loop. Insert the unrolled + code immediately before the compare/branch at the end so that the + code will fall through to them as before. */ + + copy_start = loop_start; + + /* Set insert_before to the jump insn at the end of the loop. + Set copy_end to before the jump insn at the end of the loop. */ + if (GET_CODE (last_loop_insn) == BARRIER) + { + insert_before = PREV_INSN (last_loop_insn); + copy_end = PREV_INSN (insert_before); + } + else if (GET_CODE (last_loop_insn) == JUMP_INSN) + { +#ifdef HAVE_cc0 + /* The instruction immediately before the JUMP_INSN is a compare + instruction which we do not want to copy or delete. */ + insert_before = PREV_INSN (last_loop_insn); + copy_end = PREV_INSN (insert_before); +#else + /* The instruction immediately before the JUMP_INSN may not be the + compare, so we must copy it. */ + insert_before = last_loop_insn; + copy_end = PREV_INSN (last_loop_insn); +#endif + } + else + { + /* We currently can't unroll a loop if it doesn't end with a + JUMP_INSN. There would need to be a mechanism that recognizes + this case, and then inserts a jump after each loop body, which + jumps to after the last loop body. */ + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Unrolling failure: loop does not end with a JUMP_INSN.\n"); + return; + } + } + else + { + /* Normal case: Must copy the compare and branch instructions at the + end of the loop. */ + + if (GET_CODE (last_loop_insn) == BARRIER) + { + /* Loop ends with an unconditional jump and a barrier. + Handle this like above, don't copy jump and barrier. + This is not strictly necessary, but doing so prevents generating + unconditional jumps to an immediately following label. + + This will be corrected below if the target of this jump is + not the start_label. */ + + insert_before = PREV_INSN (last_loop_insn); + copy_end = PREV_INSN (insert_before); + } + else if (GET_CODE (last_loop_insn) == JUMP_INSN) + { + /* Set insert_before to immediately after the JUMP_INSN, so that + NOTEs at the end of the loop will be correctly handled by + copy_loop_body. */ + insert_before = NEXT_INSN (last_loop_insn); + copy_end = last_loop_insn; + } + else + { + /* We currently can't unroll a loop if it doesn't end with a + JUMP_INSN. There would need to be a mechanism that recognizes + this case, and then inserts a jump after each loop body, which + jumps to after the last loop body. */ + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Unrolling failure: loop does not end with a JUMP_INSN.\n"); + return; + } + + /* If copying exit test branches because they can not be eliminated, + then must convert the fall through case of the branch to a jump past + the end of the loop. Create a label to emit after the loop and save + it for later use. Do not use the label after the loop, if any, since + it might be used by insns outside the loop, or there might be insns + added before it later by final_[bg]iv_value which must be after + the real exit label. */ + exit_label = gen_label_rtx (); + + insn = loop_start; + while (GET_CODE (insn) != CODE_LABEL && GET_CODE (insn) != JUMP_INSN) + insn = NEXT_INSN (insn); + + if (GET_CODE (insn) == JUMP_INSN) + { + /* The loop starts with a jump down to the exit condition test. + Start copying the loop after the barrier following this + jump insn. */ + copy_start = NEXT_INSN (insn); + + /* Splitting induction variables doesn't work when the loop is + entered via a jump to the bottom, because then we end up doing + a comparison against a new register for a split variable, but + we did not execute the set insn for the new register because + it was skipped over. */ + splitting_not_safe = 1; + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Splitting not safe, because loop not entered at top.\n"); + } + else + copy_start = loop_start; + } + + /* This should always be the first label in the loop. */ + start_label = NEXT_INSN (copy_start); + /* There may be a line number note and/or a loop continue note here. */ + while (GET_CODE (start_label) == NOTE) + start_label = NEXT_INSN (start_label); + if (GET_CODE (start_label) != CODE_LABEL) + { + /* This can happen as a result of jump threading. If the first insns in + the loop test the same condition as the loop's backward jump, or the + opposite condition, then the backward jump will be modified to point + to elsewhere, and the loop's start label is deleted. + + This case currently can not be handled by the loop unrolling code. */ + + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Unrolling failure: unknown insns between BEG note and loop label.\n"); + return; + } + + if (unroll_type == UNROLL_NAIVE + && GET_CODE (last_loop_insn) == BARRIER + && start_label != JUMP_LABEL (PREV_INSN (last_loop_insn))) + { + /* In this case, we must copy the jump and barrier, because they will + not be converted to jumps to an immediately following label. */ + + insert_before = NEXT_INSN (last_loop_insn); + copy_end = last_loop_insn; + } + + /* Allocate a translation table for the labels and insn numbers. + They will be filled in as we copy the insns in the loop. */ + + max_labelno = max_label_num (); + max_insnno = get_max_uid (); + + map = (struct inline_remap *) alloca (sizeof (struct inline_remap)); + + map->integrating = 0; + + /* Allocate the label map. */ + + if (max_labelno > 0) + { + map->label_map = (rtx *) alloca (max_labelno * sizeof (rtx)); + + local_label = (char *) alloca (max_labelno); + bzero (local_label, max_labelno); + } + else + map->label_map = 0; + + /* Search the loop and mark all local labels, i.e. the ones which have to + be distinct labels when copied. For all labels which might be + non-local, set their label_map entries to point to themselves. + If they happen to be local their label_map entries will be overwritten + before the loop body is copied. The label_map entries for local labels + will be set to a different value each time the loop body is copied. */ + + for (insn = copy_start; insn != loop_end; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == CODE_LABEL) + local_label[CODE_LABEL_NUMBER (insn)] = 1; + else if (GET_CODE (insn) == JUMP_INSN) + { + if (JUMP_LABEL (insn)) + map->label_map[CODE_LABEL_NUMBER (JUMP_LABEL (insn))] + = JUMP_LABEL (insn); + else if (GET_CODE (PATTERN (insn)) == ADDR_VEC + || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC) + { + rtx pat = PATTERN (insn); + int diff_vec_p = GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC; + int len = XVECLEN (pat, diff_vec_p); + rtx label; + + for (i = 0; i < len; i++) + { + label = XEXP (XVECEXP (pat, diff_vec_p, i), 0); + map->label_map[CODE_LABEL_NUMBER (label)] = label; + } + } + } + } + + /* Allocate space for the insn map. */ + + map->insn_map = (rtx *) alloca (max_insnno * sizeof (rtx)); + + /* Set this to zero, to indicate that we are doing loop unrolling, + not function inlining. */ + map->inline_target = 0; + + /* The register and constant maps depend on the number of registers + present, so the final maps can't be created until after + find_splittable_regs is called. However, they are needed for + preconditioning, so we create temporary maps when preconditioning + is performed. */ + + /* The preconditioning code may allocate two new pseudo registers. */ + maxregnum = max_reg_num (); + + /* Allocate and zero out the splittable_regs and addr_combined_regs + arrays. These must be zeroed here because they will be used if + loop preconditioning is performed, and must be zero for that case. + + It is safe to do this here, since the extra registers created by the + preconditioning code and find_splittable_regs will never be used + to access the splittable_regs[] and addr_combined_regs[] arrays. */ + + splittable_regs = (rtx *) alloca (maxregnum * sizeof (rtx)); + bzero (splittable_regs, maxregnum * sizeof (rtx)); + splittable_regs_updates = (int *) alloca (maxregnum * sizeof (int)); + bzero (splittable_regs_updates, maxregnum * sizeof (int)); + addr_combined_regs + = (struct induction **) alloca (maxregnum * sizeof (struct induction *)); + bzero (addr_combined_regs, maxregnum * sizeof (struct induction *)); + + /* If this loop requires exit tests when unrolled, check to see if we + can precondition the loop so as to make the exit tests unnecessary. + Just like variable splitting, this is not safe if the loop is entered + via a jump to the bottom. Also, can not do this if no strength + reduce info, because precondition_loop_p uses this info. */ + + /* Must copy the loop body for preconditioning before the following + find_splittable_regs call since that will emit insns which need to + be after the preconditioned loop copies, but immediately before the + unrolled loop copies. */ + + /* Also, it is not safe to split induction variables for the preconditioned + copies of the loop body. If we split induction variables, then the code + assumes that each induction variable can be represented as a function + of its initial value and the loop iteration number. This is not true + in this case, because the last preconditioned copy of the loop body + could be any iteration from the first up to the `unroll_number-1'th, + depending on the initial value of the iteration variable. Therefore + we can not split induction variables here, because we can not calculate + their value. Hence, this code must occur before find_splittable_regs + is called. */ + + if (unroll_type == UNROLL_NAIVE && ! splitting_not_safe && strength_reduce_p) + { + rtx initial_value, final_value, increment; + + if (precondition_loop_p (&initial_value, &final_value, &increment, + loop_start, loop_end)) + { + register rtx diff, temp; + enum machine_mode mode; + rtx *labels; + int abs_inc, neg_inc; + + map->reg_map = (rtx *) alloca (maxregnum * sizeof (rtx)); + + map->const_equiv_map = (rtx *) alloca (maxregnum * sizeof (rtx)); + map->const_age_map = (unsigned *) alloca (maxregnum + * sizeof (unsigned)); + map->const_equiv_map_size = maxregnum; + global_const_equiv_map = map->const_equiv_map; + + init_reg_map (map, maxregnum); + + /* Limit loop unrolling to 4, since this will make 7 copies of + the loop body. */ + if (unroll_number > 4) + unroll_number = 4; + + /* Save the absolute value of the increment, and also whether or + not it is negative. */ + neg_inc = 0; + abs_inc = INTVAL (increment); + if (abs_inc < 0) + { + abs_inc = - abs_inc; + neg_inc = 1; + } + + start_sequence (); + + /* Decide what mode to do these calculations in. Choose the larger + of final_value's mode and initial_value's mode, or a full-word if + both are constants. */ + mode = GET_MODE (final_value); + if (mode == VOIDmode) + { + mode = GET_MODE (initial_value); + if (mode == VOIDmode) + mode = word_mode; + } + else if (mode != GET_MODE (initial_value) + && (GET_MODE_SIZE (mode) + < GET_MODE_SIZE (GET_MODE (initial_value)))) + mode = GET_MODE (initial_value); + + /* Calculate the difference between the final and initial values. + Final value may be a (plus (reg x) (const_int 1)) rtx. + Let the following cse pass simplify this if initial value is + a constant. + + We must copy the final and initial values here to avoid + improperly shared rtl. */ + + diff = expand_binop (mode, sub_optab, copy_rtx (final_value), + copy_rtx (initial_value), NULL_RTX, 0, + OPTAB_LIB_WIDEN); + + /* Now calculate (diff % (unroll * abs (increment))) by using an + and instruction. */ + diff = expand_binop (GET_MODE (diff), and_optab, diff, + GEN_INT (unroll_number * abs_inc - 1), + NULL_RTX, 0, OPTAB_LIB_WIDEN); + + /* Now emit a sequence of branches to jump to the proper precond + loop entry point. */ + + labels = (rtx *) alloca (sizeof (rtx) * unroll_number); + for (i = 0; i < unroll_number; i++) + labels[i] = gen_label_rtx (); + + /* Assuming the unroll_number is 4, and the increment is 2, then + for a negative increment: for a positive increment: + diff = 0,1 precond 0 diff = 0,7 precond 0 + diff = 2,3 precond 3 diff = 1,2 precond 1 + diff = 4,5 precond 2 diff = 3,4 precond 2 + diff = 6,7 precond 1 diff = 5,6 precond 3 */ + + /* We only need to emit (unroll_number - 1) branches here, the + last case just falls through to the following code. */ + + /* ??? This would give better code if we emitted a tree of branches + instead of the current linear list of branches. */ + + for (i = 0; i < unroll_number - 1; i++) + { + int cmp_const; + + /* For negative increments, must invert the constant compared + against, except when comparing against zero. */ + if (i == 0) + cmp_const = 0; + else if (neg_inc) + cmp_const = unroll_number - i; + else + cmp_const = i; + + emit_cmp_insn (diff, GEN_INT (abs_inc * cmp_const), + EQ, NULL_RTX, mode, 0, 0); + + if (i == 0) + emit_jump_insn (gen_beq (labels[i])); + else if (neg_inc) + emit_jump_insn (gen_bge (labels[i])); + else + emit_jump_insn (gen_ble (labels[i])); + JUMP_LABEL (get_last_insn ()) = labels[i]; + LABEL_NUSES (labels[i])++; + } + + /* If the increment is greater than one, then we need another branch, + to handle other cases equivalent to 0. */ + + /* ??? This should be merged into the code above somehow to help + simplify the code here, and reduce the number of branches emitted. + For the negative increment case, the branch here could easily + be merged with the `0' case branch above. For the positive + increment case, it is not clear how this can be simplified. */ + + if (abs_inc != 1) + { + int cmp_const; + + if (neg_inc) + cmp_const = abs_inc - 1; + else + cmp_const = abs_inc * (unroll_number - 1) + 1; + + emit_cmp_insn (diff, GEN_INT (cmp_const), EQ, NULL_RTX, + mode, 0, 0); + + if (neg_inc) + emit_jump_insn (gen_ble (labels[0])); + else + emit_jump_insn (gen_bge (labels[0])); + JUMP_LABEL (get_last_insn ()) = labels[0]; + LABEL_NUSES (labels[0])++; + } + + sequence = gen_sequence (); + end_sequence (); + emit_insn_before (sequence, loop_start); + + /* Only the last copy of the loop body here needs the exit + test, so set copy_end to exclude the compare/branch here, + and then reset it inside the loop when get to the last + copy. */ + + if (GET_CODE (last_loop_insn) == BARRIER) + copy_end = PREV_INSN (PREV_INSN (last_loop_insn)); + else if (GET_CODE (last_loop_insn) == JUMP_INSN) + { +#ifdef HAVE_cc0 + /* The immediately preceding insn is a compare which we do not + want to copy. */ + copy_end = PREV_INSN (PREV_INSN (last_loop_insn)); +#else + /* The immediately preceding insn may not be a compare, so we + must copy it. */ + copy_end = PREV_INSN (last_loop_insn); +#endif + } + else + abort (); + + for (i = 1; i < unroll_number; i++) + { + emit_label_after (labels[unroll_number - i], + PREV_INSN (loop_start)); + + bzero (map->insn_map, max_insnno * sizeof (rtx)); + bzero (map->const_equiv_map, maxregnum * sizeof (rtx)); + bzero (map->const_age_map, maxregnum * sizeof (unsigned)); + map->const_age = 0; + + for (j = 0; j < max_labelno; j++) + if (local_label[j]) + map->label_map[j] = gen_label_rtx (); + + /* The last copy needs the compare/branch insns at the end, + so reset copy_end here if the loop ends with a conditional + branch. */ + + if (i == unroll_number - 1) + { + if (GET_CODE (last_loop_insn) == BARRIER) + copy_end = PREV_INSN (PREV_INSN (last_loop_insn)); + else + copy_end = last_loop_insn; + } + + /* None of the copies are the `last_iteration', so just + pass zero for that parameter. */ + copy_loop_body (copy_start, copy_end, map, exit_label, 0, + unroll_type, start_label, loop_end, + loop_start, copy_end); + } + emit_label_after (labels[0], PREV_INSN (loop_start)); + + if (GET_CODE (last_loop_insn) == BARRIER) + { + insert_before = PREV_INSN (last_loop_insn); + copy_end = PREV_INSN (insert_before); + } + else + { +#ifdef HAVE_cc0 + /* The immediately preceding insn is a compare which we do not + want to copy. */ + insert_before = PREV_INSN (last_loop_insn); + copy_end = PREV_INSN (insert_before); +#else + /* The immediately preceding insn may not be a compare, so we + must copy it. */ + insert_before = last_loop_insn; + copy_end = PREV_INSN (last_loop_insn); +#endif + } + + /* Set unroll type to MODULO now. */ + unroll_type = UNROLL_MODULO; + loop_preconditioned = 1; + } + } + + /* If reach here, and the loop type is UNROLL_NAIVE, then don't unroll + the loop unless all loops are being unrolled. */ + if (unroll_type == UNROLL_NAIVE && ! flag_unroll_all_loops) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, "Unrolling failure: Naive unrolling not being done.\n"); + return; + } + + /* At this point, we are guaranteed to unroll the loop. */ + + /* For each biv and giv, determine whether it can be safely split into + a different variable for each unrolled copy of the loop body. + We precalculate and save this info here, since computing it is + expensive. + + Do this before deleting any instructions from the loop, so that + back_branch_in_range_p will work correctly. */ + + if (splitting_not_safe) + temp = 0; + else + temp = find_splittable_regs (unroll_type, loop_start, loop_end, + end_insert_before, unroll_number); + + /* find_splittable_regs may have created some new registers, so must + reallocate the reg_map with the new larger size, and must realloc + the constant maps also. */ + + maxregnum = max_reg_num (); + map->reg_map = (rtx *) alloca (maxregnum * sizeof (rtx)); + + init_reg_map (map, maxregnum); + + /* Space is needed in some of the map for new registers, so new_maxregnum + is an (over)estimate of how many registers will exist at the end. */ + new_maxregnum = maxregnum + (temp * unroll_number * 2); + + /* Must realloc space for the constant maps, because the number of registers + may have changed. */ + + map->const_equiv_map = (rtx *) alloca (new_maxregnum * sizeof (rtx)); + map->const_age_map = (unsigned *) alloca (new_maxregnum * sizeof (unsigned)); + + global_const_equiv_map = map->const_equiv_map; + + /* Search the list of bivs and givs to find ones which need to be remapped + when split, and set their reg_map entry appropriately. */ + + for (bl = loop_iv_list; bl; bl = bl->next) + { + if (REGNO (bl->biv->src_reg) != bl->regno) + map->reg_map[bl->regno] = bl->biv->src_reg; +#if 0 + /* Currently, non-reduced/final-value givs are never split. */ + for (v = bl->giv; v; v = v->next_iv) + if (REGNO (v->src_reg) != bl->regno) + map->reg_map[REGNO (v->dest_reg)] = v->src_reg; +#endif + } + + /* If the loop is being partially unrolled, and the iteration variables + are being split, and are being renamed for the split, then must fix up + the compare instruction at the end of the loop to refer to the new + registers. This compare isn't copied, so the registers used in it + will never be replaced if it isn't done here. */ + + if (unroll_type == UNROLL_MODULO) + { + insn = NEXT_INSN (copy_end); + if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SET) + { +#if 0 + /* If non-reduced/final-value givs were split, then this would also + have to remap those givs. */ +#endif + + tem = SET_SRC (PATTERN (insn)); + /* The set source is a register. */ + if (GET_CODE (tem) == REG) + { + if (REGNO (tem) < max_reg_before_loop + && reg_iv_type[REGNO (tem)] == BASIC_INDUCT) + SET_SRC (PATTERN (insn)) + = reg_biv_class[REGNO (tem)]->biv->src_reg; + } + else + { + /* The set source is a compare of some sort. */ + tem = XEXP (SET_SRC (PATTERN (insn)), 0); + if (GET_CODE (tem) == REG + && REGNO (tem) < max_reg_before_loop + && reg_iv_type[REGNO (tem)] == BASIC_INDUCT) + XEXP (SET_SRC (PATTERN (insn)), 0) + = reg_biv_class[REGNO (tem)]->biv->src_reg; + + tem = XEXP (SET_SRC (PATTERN (insn)), 1); + if (GET_CODE (tem) == REG + && REGNO (tem) < max_reg_before_loop + && reg_iv_type[REGNO (tem)] == BASIC_INDUCT) + XEXP (SET_SRC (PATTERN (insn)), 1) + = reg_biv_class[REGNO (tem)]->biv->src_reg; + } + } + } + + /* For unroll_number - 1 times, make a copy of each instruction + between copy_start and copy_end, and insert these new instructions + before the end of the loop. */ + + for (i = 0; i < unroll_number; i++) + { + bzero (map->insn_map, max_insnno * sizeof (rtx)); + bzero (map->const_equiv_map, new_maxregnum * sizeof (rtx)); + bzero (map->const_age_map, new_maxregnum * sizeof (unsigned)); + map->const_age = 0; + + for (j = 0; j < max_labelno; j++) + if (local_label[j]) + map->label_map[j] = gen_label_rtx (); + + /* If loop starts with a branch to the test, then fix it so that + it points to the test of the first unrolled copy of the loop. */ + if (i == 0 && loop_start != copy_start) + { + insn = PREV_INSN (copy_start); + pattern = PATTERN (insn); + + tem = map->label_map[CODE_LABEL_NUMBER + (XEXP (SET_SRC (pattern), 0))]; + SET_SRC (pattern) = gen_rtx (LABEL_REF, VOIDmode, tem); + + /* Set the jump label so that it can be used by later loop unrolling + passes. */ + JUMP_LABEL (insn) = tem; + LABEL_NUSES (tem)++; + } + + copy_loop_body (copy_start, copy_end, map, exit_label, + i == unroll_number - 1, unroll_type, start_label, + loop_end, insert_before, insert_before); + } + + /* Before deleting any insns, emit a CODE_LABEL immediately after the last + insn to be deleted. This prevents any runaway delete_insn call from + more insns that it should, as it always stops at a CODE_LABEL. */ + + /* Delete the compare and branch at the end of the loop if completely + unrolling the loop. Deleting the backward branch at the end also + deletes the code label at the start of the loop. This is done at + the very end to avoid problems with back_branch_in_range_p. */ + + if (unroll_type == UNROLL_COMPLETELY) + safety_label = emit_label_after (gen_label_rtx (), last_loop_insn); + else + safety_label = emit_label_after (gen_label_rtx (), copy_end); + + /* Delete all of the original loop instructions. Don't delete the + LOOP_BEG note, or the first code label in the loop. */ + + insn = NEXT_INSN (copy_start); + while (insn != safety_label) + { + if (insn != start_label) + insn = delete_insn (insn); + else + insn = NEXT_INSN (insn); + } + + /* Can now delete the 'safety' label emitted to protect us from runaway + delete_insn calls. */ + if (INSN_DELETED_P (safety_label)) + abort (); + delete_insn (safety_label); + + /* If exit_label exists, emit it after the loop. Doing the emit here + forces it to have a higher INSN_UID than any insn in the unrolled loop. + This is needed so that mostly_true_jump in reorg.c will treat jumps + to this loop end label correctly, i.e. predict that they are usually + not taken. */ + if (exit_label) + emit_label_after (exit_label, loop_end); +} + +/* Return true if the loop can be safely, and profitably, preconditioned + so that the unrolled copies of the loop body don't need exit tests. + + This only works if final_value, initial_value and increment can be + determined, and if increment is a constant power of 2. + If increment is not a power of 2, then the preconditioning modulo + operation would require a real modulo instead of a boolean AND, and this + is not considered `profitable'. */ + +/* ??? If the loop is known to be executed very many times, or the machine + has a very cheap divide instruction, then preconditioning is a win even + when the increment is not a power of 2. Use RTX_COST to compute + whether divide is cheap. */ + +static int +precondition_loop_p (initial_value, final_value, increment, loop_start, + loop_end) + rtx *initial_value, *final_value, *increment; + rtx loop_start, loop_end; +{ + int unsigned_compare, compare_dir; + + if (loop_n_iterations > 0) + { + *initial_value = const0_rtx; + *increment = const1_rtx; + *final_value = GEN_INT (loop_n_iterations); + + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Preconditioning: Success, number of iterations known, %d.\n", + loop_n_iterations); + return 1; + } + + if (loop_initial_value == 0) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Preconditioning: Could not find initial value.\n"); + return 0; + } + else if (loop_increment == 0) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Preconditioning: Could not find increment value.\n"); + return 0; + } + else if (GET_CODE (loop_increment) != CONST_INT) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Preconditioning: Increment not a constant.\n"); + return 0; + } + else if ((exact_log2 (INTVAL (loop_increment)) < 0) + && (exact_log2 (- INTVAL (loop_increment)) < 0)) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Preconditioning: Increment not a constant power of 2.\n"); + return 0; + } + + /* Unsigned_compare and compare_dir can be ignored here, since they do + not matter for preconditioning. */ + + if (loop_final_value == 0) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Preconditioning: EQ comparison loop.\n"); + return 0; + } + + /* Must ensure that final_value is invariant, so call invariant_p to + check. Before doing so, must check regno against max_reg_before_loop + to make sure that the register is in the range covered by invariant_p. + If it isn't, then it is most likely a biv/giv which by definition are + not invariant. */ + if ((GET_CODE (loop_final_value) == REG + && REGNO (loop_final_value) >= max_reg_before_loop) + || (GET_CODE (loop_final_value) == PLUS + && REGNO (XEXP (loop_final_value, 0)) >= max_reg_before_loop) + || ! invariant_p (loop_final_value)) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Preconditioning: Final value not invariant.\n"); + return 0; + } + + /* Fail for floating point values, since the caller of this function + does not have code to deal with them. */ + if (GET_MODE_CLASS (GET_MODE (loop_final_value)) == MODE_FLOAT + || GET_MODE_CLASS (GET_MODE (loop_initial_value)) == MODE_FLOAT) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Preconditioning: Floating point final or initial value.\n"); + return 0; + } + + /* Now set initial_value to be the iteration_var, since that may be a + simpler expression, and is guaranteed to be correct if all of the + above tests succeed. + + We can not use the initial_value as calculated, because it will be + one too small for loops of the form "while (i-- > 0)". We can not + emit code before the loop_skip_over insns to fix this problem as this + will then give a number one too large for loops of the form + "while (--i > 0)". + + Note that all loops that reach here are entered at the top, because + this function is not called if the loop starts with a jump. */ + + /* Fail if loop_iteration_var is not live before loop_start, since we need + to test its value in the preconditioning code. */ + + if (uid_luid[regno_first_uid[REGNO (loop_iteration_var)]] + > INSN_LUID (loop_start)) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Preconditioning: Iteration var not live before loop start.\n"); + return 0; + } + + *initial_value = loop_iteration_var; + *increment = loop_increment; + *final_value = loop_final_value; + + /* Success! */ + if (loop_dump_stream) + fprintf (loop_dump_stream, "Preconditioning: Successful.\n"); + return 1; +} + + +/* All pseudo-registers must be mapped to themselves. Two hard registers + must be mapped, VIRTUAL_STACK_VARS_REGNUM and VIRTUAL_INCOMING_ARGS_ + REGNUM, to avoid function-inlining specific conversions of these + registers. All other hard regs can not be mapped because they may be + used with different + modes. */ + +static void +init_reg_map (map, maxregnum) + struct inline_remap *map; + int maxregnum; +{ + int i; + + for (i = maxregnum - 1; i > LAST_VIRTUAL_REGISTER; i--) + map->reg_map[i] = regno_reg_rtx[i]; + /* Just clear the rest of the entries. */ + for (i = LAST_VIRTUAL_REGISTER; i >= 0; i--) + map->reg_map[i] = 0; + + map->reg_map[VIRTUAL_STACK_VARS_REGNUM] + = regno_reg_rtx[VIRTUAL_STACK_VARS_REGNUM]; + map->reg_map[VIRTUAL_INCOMING_ARGS_REGNUM] + = regno_reg_rtx[VIRTUAL_INCOMING_ARGS_REGNUM]; +} + +/* Strength-reduction will often emit code for optimized biv/givs which + calculates their value in a temporary register, and then copies the result + to the iv. This procedure reconstructs the pattern computing the iv; + verifying that all operands are of the proper form. + + The return value is the amount that the giv is incremented by. */ + +static rtx +calculate_giv_inc (pattern, src_insn, regno) + rtx pattern, src_insn; + int regno; +{ + rtx increment; + rtx increment_total = 0; + int tries = 0; + + retry: + /* Verify that we have an increment insn here. First check for a plus + as the set source. */ + if (GET_CODE (SET_SRC (pattern)) != PLUS) + { + /* SR sometimes computes the new giv value in a temp, then copies it + to the new_reg. */ + src_insn = PREV_INSN (src_insn); + pattern = PATTERN (src_insn); + if (GET_CODE (SET_SRC (pattern)) != PLUS) + abort (); + + /* The last insn emitted is not needed, so delete it to avoid confusing + the second cse pass. This insn sets the giv unnecessarily. */ + delete_insn (get_last_insn ()); + } + + /* Verify that we have a constant as the second operand of the plus. */ + increment = XEXP (SET_SRC (pattern), 1); + if (GET_CODE (increment) != CONST_INT) + { + /* SR sometimes puts the constant in a register, especially if it is + too big to be an add immed operand. */ + src_insn = PREV_INSN (src_insn); + increment = SET_SRC (PATTERN (src_insn)); + + /* SR may have used LO_SUM to compute the constant if it is too large + for a load immed operand. In this case, the constant is in operand + one of the LO_SUM rtx. */ + if (GET_CODE (increment) == LO_SUM) + increment = XEXP (increment, 1); + + if (GET_CODE (increment) != CONST_INT) + abort (); + + /* The insn loading the constant into a register is not longer needed, + so delete it. */ + delete_insn (get_last_insn ()); + } + + if (increment_total) + increment_total = GEN_INT (INTVAL (increment_total) + INTVAL (increment)); + else + increment_total = increment; + + /* Check that the source register is the same as the register we expected + to see as the source. If not, something is seriously wrong. */ + if (GET_CODE (XEXP (SET_SRC (pattern), 0)) != REG + || REGNO (XEXP (SET_SRC (pattern), 0)) != regno) + { + /* Some machines (e.g. the romp), may emit two add instructions for + certain constants, so lets try looking for another add immediately + before this one if we have only seen one add insn so far. */ + + if (tries == 0) + { + tries++; + + src_insn = PREV_INSN (src_insn); + pattern = PATTERN (src_insn); + + delete_insn (get_last_insn ()); + + goto retry; + } + + abort (); + } + + return increment_total; +} + +/* Copy REG_NOTES, except for insn references, because not all insn_map + entries are valid yet. We do need to copy registers now though, because + the reg_map entries can change during copying. */ + +static rtx +initial_reg_note_copy (notes, map) + rtx notes; + struct inline_remap *map; +{ + rtx copy; + + if (notes == 0) + return 0; + + copy = rtx_alloc (GET_CODE (notes)); + PUT_MODE (copy, GET_MODE (notes)); + + if (GET_CODE (notes) == EXPR_LIST) + XEXP (copy, 0) = copy_rtx_and_substitute (XEXP (notes, 0), map); + else if (GET_CODE (notes) == INSN_LIST) + /* Don't substitute for these yet. */ + XEXP (copy, 0) = XEXP (notes, 0); + else + abort (); + + XEXP (copy, 1) = initial_reg_note_copy (XEXP (notes, 1), map); + + return copy; +} + +/* Fixup insn references in copied REG_NOTES. */ + +static void +final_reg_note_copy (notes, map) + rtx notes; + struct inline_remap *map; +{ + rtx note; + + for (note = notes; note; note = XEXP (note, 1)) + if (GET_CODE (note) == INSN_LIST) + XEXP (note, 0) = map->insn_map[INSN_UID (XEXP (note, 0))]; +} + +/* Copy each instruction in the loop, substituting from map as appropriate. + This is very similar to a loop in expand_inline_function. */ + +static void +copy_loop_body (copy_start, copy_end, map, exit_label, last_iteration, + unroll_type, start_label, loop_end, insert_before, + copy_notes_from) + rtx copy_start, copy_end; + struct inline_remap *map; + rtx exit_label; + int last_iteration; + enum unroll_types unroll_type; + rtx start_label, loop_end, insert_before, copy_notes_from; +{ + rtx insn, pattern; + rtx tem, copy; + int dest_reg_was_split, i; + rtx cc0_insn = 0; + rtx final_label = 0; + rtx giv_inc, giv_dest_reg, giv_src_reg; + + /* If this isn't the last iteration, then map any references to the + start_label to final_label. Final label will then be emitted immediately + after the end of this loop body if it was ever used. + + If this is the last iteration, then map references to the start_label + to itself. */ + if (! last_iteration) + { + final_label = gen_label_rtx (); + map->label_map[CODE_LABEL_NUMBER (start_label)] = final_label; + } + else + map->label_map[CODE_LABEL_NUMBER (start_label)] = start_label; + + start_sequence (); + + insn = copy_start; + do + { + insn = NEXT_INSN (insn); + + map->orig_asm_operands_vector = 0; + + switch (GET_CODE (insn)) + { + case INSN: + pattern = PATTERN (insn); + copy = 0; + giv_inc = 0; + + /* Check to see if this is a giv that has been combined with + some split address givs. (Combined in the sense that + `combine_givs' in loop.c has put two givs in the same register.) + In this case, we must search all givs based on the same biv to + find the address givs. Then split the address givs. + Do this before splitting the giv, since that may map the + SET_DEST to a new register. */ + + if (GET_CODE (pattern) == SET + && GET_CODE (SET_DEST (pattern)) == REG + && addr_combined_regs[REGNO (SET_DEST (pattern))]) + { + struct iv_class *bl; + struct induction *v, *tv; + int regno = REGNO (SET_DEST (pattern)); + + v = addr_combined_regs[REGNO (SET_DEST (pattern))]; + bl = reg_biv_class[REGNO (v->src_reg)]; + + /* Although the giv_inc amount is not needed here, we must call + calculate_giv_inc here since it might try to delete the + last insn emitted. If we wait until later to call it, + we might accidentally delete insns generated immediately + below by emit_unrolled_add. */ + + giv_inc = calculate_giv_inc (pattern, insn, regno); + + /* Now find all address giv's that were combined with this + giv 'v'. */ + for (tv = bl->giv; tv; tv = tv->next_iv) + if (tv->giv_type == DEST_ADDR && tv->same == v) + { + int this_giv_inc = INTVAL (giv_inc); + + /* Scale this_giv_inc if the multiplicative factors of + the two givs are different. */ + if (tv->mult_val != v->mult_val) + this_giv_inc = (this_giv_inc / INTVAL (v->mult_val) + * INTVAL (tv->mult_val)); + + tv->dest_reg = plus_constant (tv->dest_reg, this_giv_inc); + *tv->location = tv->dest_reg; + + if (last_iteration && unroll_type != UNROLL_COMPLETELY) + { + /* Must emit an insn to increment the split address + giv. Add in the const_adjust field in case there + was a constant eliminated from the address. */ + rtx value, dest_reg; + + /* tv->dest_reg will be either a bare register, + or else a register plus a constant. */ + if (GET_CODE (tv->dest_reg) == REG) + dest_reg = tv->dest_reg; + else + dest_reg = XEXP (tv->dest_reg, 0); + + /* tv->dest_reg may actually be a (PLUS (REG) (CONST)) + here, so we must call plus_constant to add + the const_adjust amount before calling + emit_unrolled_add below. */ + value = plus_constant (tv->dest_reg, tv->const_adjust); + + /* The constant could be too large for an add + immediate, so can't directly emit an insn here. */ + emit_unrolled_add (dest_reg, XEXP (value, 0), + XEXP (value, 1)); + + /* Reset the giv to be just the register again, in case + it is used after the set we have just emitted. + We must subtract the const_adjust factor added in + above. */ + tv->dest_reg = plus_constant (dest_reg, + - tv->const_adjust); + *tv->location = tv->dest_reg; + } + } + } + + /* If this is a setting of a splittable variable, then determine + how to split the variable, create a new set based on this split, + and set up the reg_map so that later uses of the variable will + use the new split variable. */ + + dest_reg_was_split = 0; + + if (GET_CODE (pattern) == SET + && GET_CODE (SET_DEST (pattern)) == REG + && splittable_regs[REGNO (SET_DEST (pattern))]) + { + int regno = REGNO (SET_DEST (pattern)); + + dest_reg_was_split = 1; + + /* Compute the increment value for the giv, if it wasn't + already computed above. */ + + if (giv_inc == 0) + giv_inc = calculate_giv_inc (pattern, insn, regno); + giv_dest_reg = SET_DEST (pattern); + giv_src_reg = SET_DEST (pattern); + + if (unroll_type == UNROLL_COMPLETELY) + { + /* Completely unrolling the loop. Set the induction + variable to a known constant value. */ + + /* The value in splittable_regs may be an invariant + value, so we must use plus_constant here. */ + splittable_regs[regno] + = plus_constant (splittable_regs[regno], INTVAL (giv_inc)); + + if (GET_CODE (splittable_regs[regno]) == PLUS) + { + giv_src_reg = XEXP (splittable_regs[regno], 0); + giv_inc = XEXP (splittable_regs[regno], 1); + } + else + { + /* The splittable_regs value must be a REG or a + CONST_INT, so put the entire value in the giv_src_reg + variable. */ + giv_src_reg = splittable_regs[regno]; + giv_inc = const0_rtx; + } + } + else + { + /* Partially unrolling loop. Create a new pseudo + register for the iteration variable, and set it to + be a constant plus the original register. Except + on the last iteration, when the result has to + go back into the original iteration var register. */ + + /* Handle bivs which must be mapped to a new register + when split. This happens for bivs which need their + final value set before loop entry. The new register + for the biv was stored in the biv's first struct + induction entry by find_splittable_regs. */ + + if (regno < max_reg_before_loop + && reg_iv_type[regno] == BASIC_INDUCT) + { + giv_src_reg = reg_biv_class[regno]->biv->src_reg; + giv_dest_reg = giv_src_reg; + } + +#if 0 + /* If non-reduced/final-value givs were split, then + this would have to remap those givs also. See + find_splittable_regs. */ +#endif + + splittable_regs[regno] + = GEN_INT (INTVAL (giv_inc) + + INTVAL (splittable_regs[regno])); + giv_inc = splittable_regs[regno]; + + /* Now split the induction variable by changing the dest + of this insn to a new register, and setting its + reg_map entry to point to this new register. + + If this is the last iteration, and this is the last insn + that will update the iv, then reuse the original dest, + to ensure that the iv will have the proper value when + the loop exits or repeats. + + Using splittable_regs_updates here like this is safe, + because it can only be greater than one if all + instructions modifying the iv are always executed in + order. */ + + if (! last_iteration + || (splittable_regs_updates[regno]-- != 1)) + { + tem = gen_reg_rtx (GET_MODE (giv_src_reg)); + giv_dest_reg = tem; + map->reg_map[regno] = tem; + } + else + map->reg_map[regno] = giv_src_reg; + } + + /* The constant being added could be too large for an add + immediate, so can't directly emit an insn here. */ + emit_unrolled_add (giv_dest_reg, giv_src_reg, giv_inc); + copy = get_last_insn (); + pattern = PATTERN (copy); + } + else + { + pattern = copy_rtx_and_substitute (pattern, map); + copy = emit_insn (pattern); + } + REG_NOTES (copy) = initial_reg_note_copy (REG_NOTES (insn), map); + +#ifdef HAVE_cc0 + /* If this insn is setting CC0, it may need to look at + the insn that uses CC0 to see what type of insn it is. + In that case, the call to recog via validate_change will + fail. So don't substitute constants here. Instead, + do it when we emit the following insn. + + For example, see the pyr.md file. That machine has signed and + unsigned compares. The compare patterns must check the + following branch insn to see which what kind of compare to + emit. + + If the previous insn set CC0, substitute constants on it as + well. */ + if (sets_cc0_p (copy) != 0) + cc0_insn = copy; + else + { + if (cc0_insn) + try_constants (cc0_insn, map); + cc0_insn = 0; + try_constants (copy, map); + } +#else + try_constants (copy, map); +#endif + + /* Make split induction variable constants `permanent' since we + know there are no backward branches across iteration variable + settings which would invalidate this. */ + if (dest_reg_was_split) + { + int regno = REGNO (SET_DEST (pattern)); + + if (map->const_age_map[regno] == map->const_age) + map->const_age_map[regno] = -1; + } + break; + + case JUMP_INSN: + pattern = copy_rtx_and_substitute (PATTERN (insn), map); + copy = emit_jump_insn (pattern); + REG_NOTES (copy) = initial_reg_note_copy (REG_NOTES (insn), map); + + if (JUMP_LABEL (insn) == start_label && insn == copy_end + && ! last_iteration) + { + /* This is a branch to the beginning of the loop; this is the + last insn being copied; and this is not the last iteration. + In this case, we want to change the original fall through + case to be a branch past the end of the loop, and the + original jump label case to fall_through. */ + + if (! invert_exp (pattern, copy) + || ! redirect_exp (&pattern, + map->label_map[CODE_LABEL_NUMBER + (JUMP_LABEL (insn))], + exit_label, copy)) + abort (); + } + +#ifdef HAVE_cc0 + if (cc0_insn) + try_constants (cc0_insn, map); + cc0_insn = 0; +#endif + try_constants (copy, map); + + /* Set the jump label of COPY correctly to avoid problems with + later passes of unroll_loop, if INSN had jump label set. */ + if (JUMP_LABEL (insn)) + { + rtx label = 0; + + /* Can't use the label_map for every insn, since this may be + the backward branch, and hence the label was not mapped. */ + if (GET_CODE (pattern) == SET) + { + tem = SET_SRC (pattern); + if (GET_CODE (tem) == LABEL_REF) + label = XEXP (tem, 0); + else if (GET_CODE (tem) == IF_THEN_ELSE) + { + if (XEXP (tem, 1) != pc_rtx) + label = XEXP (XEXP (tem, 1), 0); + else + label = XEXP (XEXP (tem, 2), 0); + } + } + + if (label && GET_CODE (label) == CODE_LABEL) + JUMP_LABEL (copy) = label; + else + { + /* An unrecognizable jump insn, probably the entry jump + for a switch statement. This label must have been mapped, + so just use the label_map to get the new jump label. */ + JUMP_LABEL (copy) = map->label_map[CODE_LABEL_NUMBER + (JUMP_LABEL (insn))]; + } + + /* If this is a non-local jump, then must increase the label + use count so that the label will not be deleted when the + original jump is deleted. */ + LABEL_NUSES (JUMP_LABEL (copy))++; + } + else if (GET_CODE (PATTERN (copy)) == ADDR_VEC + || GET_CODE (PATTERN (copy)) == ADDR_DIFF_VEC) + { + rtx pat = PATTERN (copy); + int diff_vec_p = GET_CODE (pat) == ADDR_DIFF_VEC; + int len = XVECLEN (pat, diff_vec_p); + int i; + + for (i = 0; i < len; i++) + LABEL_NUSES (XEXP (XVECEXP (pat, diff_vec_p, i), 0))++; + } + + /* If this used to be a conditional jump insn but whose branch + direction is now known, we must do something special. */ + if (condjump_p (insn) && !simplejump_p (insn) && map->last_pc_value) + { +#ifdef HAVE_cc0 + /* The previous insn set cc0 for us. So delete it. */ + delete_insn (PREV_INSN (copy)); +#endif + + /* If this is now a no-op, delete it. */ + if (map->last_pc_value == pc_rtx) + { + delete_insn (copy); + copy = 0; + } + else + /* Otherwise, this is unconditional jump so we must put a + BARRIER after it. We could do some dead code elimination + here, but jump.c will do it just as well. */ + emit_barrier (); + } + break; + + case CALL_INSN: + pattern = copy_rtx_and_substitute (PATTERN (insn), map); + copy = emit_call_insn (pattern); + REG_NOTES (copy) = initial_reg_note_copy (REG_NOTES (insn), map); + +#ifdef HAVE_cc0 + if (cc0_insn) + try_constants (cc0_insn, map); + cc0_insn = 0; +#endif + try_constants (copy, map); + + /* Be lazy and assume CALL_INSNs clobber all hard registers. */ + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + map->const_equiv_map[i] = 0; + break; + + case CODE_LABEL: + /* If this is the loop start label, then we don't need to emit a + copy of this label since no one will use it. */ + + if (insn != start_label) + { + copy = emit_label (map->label_map[CODE_LABEL_NUMBER (insn)]); + map->const_age++; + } + break; + + case BARRIER: + copy = emit_barrier (); + break; + + case NOTE: + /* VTOP notes are valid only before the loop exit test. If placed + anywhere else, loop may generate bad code. */ + + if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED + && (NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_VTOP + || (last_iteration && unroll_type != UNROLL_COMPLETELY))) + copy = emit_note (NOTE_SOURCE_FILE (insn), + NOTE_LINE_NUMBER (insn)); + else + copy = 0; + break; + + default: + abort (); + break; + } + + map->insn_map[INSN_UID (insn)] = copy; + } + while (insn != copy_end); + + /* Now finish coping the REG_NOTES. */ + insn = copy_start; + do + { + insn = NEXT_INSN (insn); + if ((GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN + || GET_CODE (insn) == CALL_INSN) + && map->insn_map[INSN_UID (insn)]) + final_reg_note_copy (REG_NOTES (map->insn_map[INSN_UID (insn)]), map); + } + while (insn != copy_end); + + /* There may be notes between copy_notes_from and loop_end. Emit a copy of + each of these notes here, since there may be some important ones, such as + NOTE_INSN_BLOCK_END notes, in this group. We don't do this on the last + iteration, because the original notes won't be deleted. + + We can't use insert_before here, because when from preconditioning, + insert_before points before the loop. We can't use copy_end, because + there may be insns already inserted after it (which we don't want to + copy) when not from preconditioning code. */ + + if (! last_iteration) + { + for (insn = copy_notes_from; insn != loop_end; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == NOTE + && NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED) + emit_note (NOTE_SOURCE_FILE (insn), NOTE_LINE_NUMBER (insn)); + } + } + + if (final_label && LABEL_NUSES (final_label) > 0) + emit_label (final_label); + + tem = gen_sequence (); + end_sequence (); + emit_insn_before (tem, insert_before); +} + +/* Emit an insn, using the expand_binop to ensure that a valid insn is + emitted. This will correctly handle the case where the increment value + won't fit in the immediate field of a PLUS insns. */ + +void +emit_unrolled_add (dest_reg, src_reg, increment) + rtx dest_reg, src_reg, increment; +{ + rtx result; + + result = expand_binop (GET_MODE (dest_reg), add_optab, src_reg, increment, + dest_reg, 0, OPTAB_LIB_WIDEN); + + if (dest_reg != result) + emit_move_insn (dest_reg, result); +} + +/* Searches the insns between INSN and LOOP_END. Returns 1 if there + is a backward branch in that range that branches to somewhere between + LOOP_START and INSN. Returns 0 otherwise. */ + +/* ??? This is quadratic algorithm. Could be rewritten to be linear. + In practice, this is not a problem, because this function is seldom called, + and uses a negligible amount of CPU time on average. */ + +static int +back_branch_in_range_p (insn, loop_start, loop_end) + rtx insn; + rtx loop_start, loop_end; +{ + rtx p, q, target_insn; + + /* Stop before we get to the backward branch at the end of the loop. */ + loop_end = prev_nonnote_insn (loop_end); + if (GET_CODE (loop_end) == BARRIER) + loop_end = PREV_INSN (loop_end); + + /* Check in case insn has been deleted, search forward for first non + deleted insn following it. */ + while (INSN_DELETED_P (insn)) + insn = NEXT_INSN (insn); + + /* Check for the case where insn is the last insn in the loop. */ + if (insn == loop_end) + return 0; + + for (p = NEXT_INSN (insn); p != loop_end; p = NEXT_INSN (p)) + { + if (GET_CODE (p) == JUMP_INSN) + { + target_insn = JUMP_LABEL (p); + + /* Search from loop_start to insn, to see if one of them is + the target_insn. We can't use INSN_LUID comparisons here, + since insn may not have an LUID entry. */ + for (q = loop_start; q != insn; q = NEXT_INSN (q)) + if (q == target_insn) + return 1; + } + } + + return 0; +} + +/* Try to generate the simplest rtx for the expression + (PLUS (MULT mult1 mult2) add1). This is used to calculate the initial + value of giv's. */ + +static rtx +fold_rtx_mult_add (mult1, mult2, add1, mode) + rtx mult1, mult2, add1; + enum machine_mode mode; +{ + rtx temp, mult_res; + rtx result; + + /* The modes must all be the same. This should always be true. For now, + check to make sure. */ + if ((GET_MODE (mult1) != mode && GET_MODE (mult1) != VOIDmode) + || (GET_MODE (mult2) != mode && GET_MODE (mult2) != VOIDmode) + || (GET_MODE (add1) != mode && GET_MODE (add1) != VOIDmode)) + abort (); + + /* Ensure that if at least one of mult1/mult2 are constant, then mult2 + will be a constant. */ + if (GET_CODE (mult1) == CONST_INT) + { + temp = mult2; + mult2 = mult1; + mult1 = temp; + } + + mult_res = simplify_binary_operation (MULT, mode, mult1, mult2); + if (! mult_res) + mult_res = gen_rtx (MULT, mode, mult1, mult2); + + /* Again, put the constant second. */ + if (GET_CODE (add1) == CONST_INT) + { + temp = add1; + add1 = mult_res; + mult_res = temp; + } + + result = simplify_binary_operation (PLUS, mode, add1, mult_res); + if (! result) + result = gen_rtx (PLUS, mode, add1, mult_res); + + return result; +} + +/* Searches the list of induction struct's for the biv BL, to try to calculate + the total increment value for one iteration of the loop as a constant. + + Returns the increment value as an rtx, simplified as much as possible, + if it can be calculated. Otherwise, returns 0. */ + +rtx +biv_total_increment (bl, loop_start, loop_end) + struct iv_class *bl; + rtx loop_start, loop_end; +{ + struct induction *v; + rtx result; + + /* For increment, must check every instruction that sets it. Each + instruction must be executed only once each time through the loop. + To verify this, we check that the the insn is always executed, and that + there are no backward branches after the insn that branch to before it. + Also, the insn must have a mult_val of one (to make sure it really is + an increment). */ + + result = const0_rtx; + for (v = bl->biv; v; v = v->next_iv) + { + if (v->always_computable && v->mult_val == const1_rtx + && ! back_branch_in_range_p (v->insn, loop_start, loop_end)) + result = fold_rtx_mult_add (result, const1_rtx, v->add_val, v->mode); + else + return 0; + } + + return result; +} + +/* Determine the initial value of the iteration variable, and the amount + that it is incremented each loop. Use the tables constructed by + the strength reduction pass to calculate these values. + + Initial_value and/or increment are set to zero if their values could not + be calculated. */ + +static void +iteration_info (iteration_var, initial_value, increment, loop_start, loop_end) + rtx iteration_var, *initial_value, *increment; + rtx loop_start, loop_end; +{ + struct iv_class *bl; + struct induction *v, *b; + + /* Clear the result values, in case no answer can be found. */ + *initial_value = 0; + *increment = 0; + + /* The iteration variable can be either a giv or a biv. Check to see + which it is, and compute the variable's initial value, and increment + value if possible. */ + + /* If this is a new register, can't handle it since we don't have any + reg_iv_type entry for it. */ + if (REGNO (iteration_var) >= max_reg_before_loop) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Loop unrolling: No reg_iv_type entry for iteration var.\n"); + return; + } + /* Reject iteration variables larger than the host long size, since they + could result in a number of iterations greater than the range of our + `unsigned long' variable loop_n_iterations. */ + else if (GET_MODE_BITSIZE (GET_MODE (iteration_var)) > HOST_BITS_PER_LONG) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Loop unrolling: Iteration var rejected because mode larger than host long.\n"); + return; + } + else if (GET_MODE_CLASS (GET_MODE (iteration_var)) != MODE_INT) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Loop unrolling: Iteration var not an integer.\n"); + return; + } + else if (reg_iv_type[REGNO (iteration_var)] == BASIC_INDUCT) + { + /* Grab initial value, only useful if it is a constant. */ + bl = reg_biv_class[REGNO (iteration_var)]; + *initial_value = bl->initial_value; + + *increment = biv_total_increment (bl, loop_start, loop_end); + } + else if (reg_iv_type[REGNO (iteration_var)] == GENERAL_INDUCT) + { +#if 1 + /* ??? The code below does not work because the incorrect number of + iterations is calculated when the biv is incremented after the giv + is set (which is the usual case). This can probably be accounted + for by biasing the initial_value by subtracting the amount of the + increment that occurs between the giv set and the giv test. However, + a giv as an iterator is very rare, so it does not seem worthwhile + to handle this. */ + /* ??? An example failure is: i = 6; do {;} while (i++ < 9). */ + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Loop unrolling: Giv iterators are not handled.\n"); + return; +#else + /* Initial value is mult_val times the biv's initial value plus + add_val. Only useful if it is a constant. */ + v = reg_iv_info[REGNO (iteration_var)]; + bl = reg_biv_class[REGNO (v->src_reg)]; + *initial_value = fold_rtx_mult_add (v->mult_val, bl->initial_value, + v->add_val, v->mode); + + /* Increment value is mult_val times the increment value of the biv. */ + + *increment = biv_total_increment (bl, loop_start, loop_end); + if (*increment) + *increment = fold_rtx_mult_add (v->mult_val, *increment, const0_rtx, + v->mode); +#endif + } + else + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Loop unrolling: Not basic or general induction var.\n"); + return; + } +} + +/* Calculate the approximate final value of the iteration variable + which has an loop exit test with code COMPARISON_CODE and comparison value + of COMPARISON_VALUE. Also returns an indication of whether the comparison + was signed or unsigned, and the direction of the comparison. This info is + needed to calculate the number of loop iterations. */ + +static rtx +approx_final_value (comparison_code, comparison_value, unsigned_p, compare_dir) + enum rtx_code comparison_code; + rtx comparison_value; + int *unsigned_p; + int *compare_dir; +{ + /* Calculate the final value of the induction variable. + The exact final value depends on the branch operator, and increment sign. + This is only an approximate value. It will be wrong if the iteration + variable is not incremented by one each time through the loop, and + approx final value - start value % increment != 0. */ + + *unsigned_p = 0; + switch (comparison_code) + { + case LEU: + *unsigned_p = 1; + case LE: + *compare_dir = 1; + return plus_constant (comparison_value, 1); + case GEU: + *unsigned_p = 1; + case GE: + *compare_dir = -1; + return plus_constant (comparison_value, -1); + case EQ: + /* Can not calculate a final value for this case. */ + *compare_dir = 0; + return 0; + case LTU: + *unsigned_p = 1; + case LT: + *compare_dir = 1; + return comparison_value; + break; + case GTU: + *unsigned_p = 1; + case GT: + *compare_dir = -1; + return comparison_value; + case NE: + *compare_dir = 0; + return comparison_value; + default: + abort (); + } +} + +/* For each biv and giv, determine whether it can be safely split into + a different variable for each unrolled copy of the loop body. If it + is safe to split, then indicate that by saving some useful info + in the splittable_regs array. + + If the loop is being completely unrolled, then splittable_regs will hold + the current value of the induction variable while the loop is unrolled. + It must be set to the initial value of the induction variable here. + Otherwise, splittable_regs will hold the difference between the current + value of the induction variable and the value the induction variable had + at the top of the loop. It must be set to the value 0 here. */ + +/* ?? If the loop is only unrolled twice, then most of the restrictions to + constant values are unnecessary, since we can easily calculate increment + values in this case even if nothing is constant. The increment value + should not involve a multiply however. */ + +/* ?? Even if the biv/giv increment values aren't constant, it may still + be beneficial to split the variable if the loop is only unrolled a few + times, since multiplies by small integers (1,2,3,4) are very cheap. */ + +static int +find_splittable_regs (unroll_type, loop_start, loop_end, end_insert_before, + unroll_number) + enum unroll_types unroll_type; + rtx loop_start, loop_end; + rtx end_insert_before; + int unroll_number; +{ + struct iv_class *bl; + struct induction *v; + rtx increment, tem; + rtx biv_final_value; + int biv_splittable; + int result = 0; + + for (bl = loop_iv_list; bl; bl = bl->next) + { + /* Biv_total_increment must return a constant value, + otherwise we can not calculate the split values. */ + + increment = biv_total_increment (bl, loop_start, loop_end); + if (! increment || GET_CODE (increment) != CONST_INT) + continue; + + /* The loop must be unrolled completely, or else have a known number + of iterations and only one exit, or else the biv must be dead + outside the loop, or else the final value must be known. Otherwise, + it is unsafe to split the biv since it may not have the proper + value on loop exit. */ + + /* loop_number_exit_labels is non-zero if the loop has an exit other than + a fall through at the end. */ + + biv_splittable = 1; + biv_final_value = 0; + if (unroll_type != UNROLL_COMPLETELY + && (loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]] + || unroll_type == UNROLL_NAIVE) + && (uid_luid[regno_last_uid[bl->regno]] >= INSN_LUID (loop_end) + || ! bl->init_insn + || INSN_UID (bl->init_insn) >= max_uid_for_loop + || (uid_luid[regno_first_uid[bl->regno]] + < INSN_LUID (bl->init_insn)) + || reg_mentioned_p (bl->biv->dest_reg, SET_SRC (bl->init_set))) + && ! (biv_final_value = final_biv_value (bl, loop_start, loop_end))) + biv_splittable = 0; + + /* If any of the insns setting the BIV don't do so with a simple + PLUS, we don't know how to split it. */ + for (v = bl->biv; biv_splittable && v; v = v->next_iv) + if ((tem = single_set (v->insn)) == 0 + || GET_CODE (SET_DEST (tem)) != REG + || REGNO (SET_DEST (tem)) != bl->regno + || GET_CODE (SET_SRC (tem)) != PLUS) + biv_splittable = 0; + + /* If final value is non-zero, then must emit an instruction which sets + the value of the biv to the proper value. This is done after + handling all of the givs, since some of them may need to use the + biv's value in their initialization code. */ + + /* This biv is splittable. If completely unrolling the loop, save + the biv's initial value. Otherwise, save the constant zero. */ + + if (biv_splittable == 1) + { + if (unroll_type == UNROLL_COMPLETELY) + { + /* If the initial value of the biv is itself (i.e. it is too + complicated for strength_reduce to compute), or is a hard + register, then we must create a new pseudo reg to hold the + initial value of the biv. */ + + if (GET_CODE (bl->initial_value) == REG + && (REGNO (bl->initial_value) == bl->regno + || REGNO (bl->initial_value) < FIRST_PSEUDO_REGISTER)) + { + rtx tem = gen_reg_rtx (bl->biv->mode); + + emit_insn_before (gen_move_insn (tem, bl->biv->src_reg), + loop_start); + + if (loop_dump_stream) + fprintf (loop_dump_stream, "Biv %d initial value remapped to %d.\n", + bl->regno, REGNO (tem)); + + splittable_regs[bl->regno] = tem; + } + else + splittable_regs[bl->regno] = bl->initial_value; + } + else + splittable_regs[bl->regno] = const0_rtx; + + /* Save the number of instructions that modify the biv, so that + we can treat the last one specially. */ + + splittable_regs_updates[bl->regno] = bl->biv_count; + + result++; + + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Biv %d safe to split.\n", bl->regno); + } + + /* Check every giv that depends on this biv to see whether it is + splittable also. Even if the biv isn't splittable, givs which + depend on it may be splittable if the biv is live outside the + loop, and the givs aren't. */ + + result = find_splittable_givs (bl, unroll_type, loop_start, loop_end, + increment, unroll_number, result); + + /* If final value is non-zero, then must emit an instruction which sets + the value of the biv to the proper value. This is done after + handling all of the givs, since some of them may need to use the + biv's value in their initialization code. */ + if (biv_final_value) + { + /* If the loop has multiple exits, emit the insns before the + loop to ensure that it will always be executed no matter + how the loop exits. Otherwise emit the insn after the loop, + since this is slightly more efficient. */ + if (! loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]]) + emit_insn_before (gen_move_insn (bl->biv->src_reg, + biv_final_value), + end_insert_before); + else + { + /* Create a new register to hold the value of the biv, and then + set the biv to its final value before the loop start. The biv + is set to its final value before loop start to ensure that + this insn will always be executed, no matter how the loop + exits. */ + rtx tem = gen_reg_rtx (bl->biv->mode); + emit_insn_before (gen_move_insn (tem, bl->biv->src_reg), + loop_start); + emit_insn_before (gen_move_insn (bl->biv->src_reg, + biv_final_value), + loop_start); + + if (loop_dump_stream) + fprintf (loop_dump_stream, "Biv %d mapped to %d for split.\n", + REGNO (bl->biv->src_reg), REGNO (tem)); + + /* Set up the mapping from the original biv register to the new + register. */ + bl->biv->src_reg = tem; + } + } + } + return result; +} + +/* For every giv based on the biv BL, check to determine whether it is + splittable. This is a subroutine to find_splittable_regs (). */ + +static int +find_splittable_givs (bl, unroll_type, loop_start, loop_end, increment, + unroll_number, result) + struct iv_class *bl; + enum unroll_types unroll_type; + rtx loop_start, loop_end; + rtx increment; + int unroll_number, result; +{ + struct induction *v; + rtx final_value; + rtx tem; + + for (v = bl->giv; v; v = v->next_iv) + { + rtx giv_inc, value; + + /* Only split the giv if it has already been reduced, or if the loop is + being completely unrolled. */ + if (unroll_type != UNROLL_COMPLETELY && v->ignore) + continue; + + /* The giv can be split if the insn that sets the giv is executed once + and only once on every iteration of the loop. */ + /* An address giv can always be split. v->insn is just a use not a set, + and hence it does not matter whether it is always executed. All that + matters is that all the biv increments are always executed, and we + won't reach here if they aren't. */ + if (v->giv_type != DEST_ADDR + && (! v->always_computable + || back_branch_in_range_p (v->insn, loop_start, loop_end))) + continue; + + /* The giv increment value must be a constant. */ + giv_inc = fold_rtx_mult_add (v->mult_val, increment, const0_rtx, + v->mode); + if (! giv_inc || GET_CODE (giv_inc) != CONST_INT) + continue; + + /* The loop must be unrolled completely, or else have a known number of + iterations and only one exit, or else the giv must be dead outside + the loop, or else the final value of the giv must be known. + Otherwise, it is not safe to split the giv since it may not have the + proper value on loop exit. */ + + /* The used outside loop test will fail for DEST_ADDR givs. They are + never used outside the loop anyways, so it is always safe to split a + DEST_ADDR giv. */ + + final_value = 0; + if (unroll_type != UNROLL_COMPLETELY + && (loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]] + || unroll_type == UNROLL_NAIVE) + && v->giv_type != DEST_ADDR + && ((regno_first_uid[REGNO (v->dest_reg)] != INSN_UID (v->insn) + /* Check for the case where the pseudo is set by a shift/add + sequence, in which case the first insn setting the pseudo + is the first insn of the shift/add sequence. */ + && (! (tem = find_reg_note (v->insn, REG_RETVAL, NULL_RTX)) + || (regno_first_uid[REGNO (v->dest_reg)] + != INSN_UID (XEXP (tem, 0))))) + /* Line above always fails if INSN was moved by loop opt. */ + || (uid_luid[regno_last_uid[REGNO (v->dest_reg)]] + >= INSN_LUID (loop_end))) + && ! (final_value = v->final_value)) + continue; + +#if 0 + /* Currently, non-reduced/final-value givs are never split. */ + /* Should emit insns after the loop if possible, as the biv final value + code below does. */ + + /* If the final value is non-zero, and the giv has not been reduced, + then must emit an instruction to set the final value. */ + if (final_value && !v->new_reg) + { + /* Create a new register to hold the value of the giv, and then set + the giv to its final value before the loop start. The giv is set + to its final value before loop start to ensure that this insn + will always be executed, no matter how we exit. */ + tem = gen_reg_rtx (v->mode); + emit_insn_before (gen_move_insn (tem, v->dest_reg), loop_start); + emit_insn_before (gen_move_insn (v->dest_reg, final_value), + loop_start); + + if (loop_dump_stream) + fprintf (loop_dump_stream, "Giv %d mapped to %d for split.\n", + REGNO (v->dest_reg), REGNO (tem)); + + v->src_reg = tem; + } +#endif + + /* This giv is splittable. If completely unrolling the loop, save the + giv's initial value. Otherwise, save the constant zero for it. */ + + if (unroll_type == UNROLL_COMPLETELY) + { + /* It is not safe to use bl->initial_value here, because it may not + be invariant. It is safe to use the initial value stored in + the splittable_regs array if it is set. In rare cases, it won't + be set, so then we do exactly the same thing as + find_splittable_regs does to get a safe value. */ + rtx biv_initial_value; + + if (splittable_regs[bl->regno]) + biv_initial_value = splittable_regs[bl->regno]; + else if (GET_CODE (bl->initial_value) != REG + || (REGNO (bl->initial_value) != bl->regno + && REGNO (bl->initial_value) >= FIRST_PSEUDO_REGISTER)) + biv_initial_value = bl->initial_value; + else + { + rtx tem = gen_reg_rtx (bl->biv->mode); + + emit_insn_before (gen_move_insn (tem, bl->biv->src_reg), + loop_start); + biv_initial_value = tem; + } + value = fold_rtx_mult_add (v->mult_val, biv_initial_value, + v->add_val, v->mode); + } + else + value = const0_rtx; + + if (v->new_reg) + { + /* If a giv was combined with another giv, then we can only split + this giv if the giv it was combined with was reduced. This + is because the value of v->new_reg is meaningless in this + case. */ + if (v->same && ! v->same->new_reg) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "giv combined with unreduced giv not split.\n"); + continue; + } + /* If the giv is an address destination, it could be something other + than a simple register, these have to be treated differently. */ + else if (v->giv_type == DEST_REG) + { + /* If value is not a constant, register, or register plus + constant, then compute its value into a register before + loop start. This prevents illegal rtx sharing, and should + generate better code. We can use bl->initial_value here + instead of splittable_regs[bl->regno] because this code + is going before the loop start. */ + if (unroll_type == UNROLL_COMPLETELY + && GET_CODE (value) != CONST_INT + && GET_CODE (value) != REG + && (GET_CODE (value) != PLUS + || GET_CODE (XEXP (value, 0)) != REG + || GET_CODE (XEXP (value, 1)) != CONST_INT)) + { + rtx tem = gen_reg_rtx (v->mode); + emit_iv_add_mult (bl->initial_value, v->mult_val, + v->add_val, tem, loop_start); + value = tem; + } + + splittable_regs[REGNO (v->new_reg)] = value; + } + else + { + /* Splitting address givs is useful since it will often allow us + to eliminate some increment insns for the base giv as + unnecessary. */ + + /* If the addr giv is combined with a dest_reg giv, then all + references to that dest reg will be remapped, which is NOT + what we want for split addr regs. We always create a new + register for the split addr giv, just to be safe. */ + + /* ??? If there are multiple address givs which have been + combined with the same dest_reg giv, then we may only need + one new register for them. Pulling out constants below will + catch some of the common cases of this. Currently, I leave + the work of simplifying multiple address givs to the + following cse pass. */ + + v->const_adjust = 0; + if (unroll_type != UNROLL_COMPLETELY) + { + /* If not completely unrolling the loop, then create a new + register to hold the split value of the DEST_ADDR giv. + Emit insn to initialize its value before loop start. */ + tem = gen_reg_rtx (v->mode); + + /* If the address giv has a constant in its new_reg value, + then this constant can be pulled out and put in value, + instead of being part of the initialization code. */ + + if (GET_CODE (v->new_reg) == PLUS + && GET_CODE (XEXP (v->new_reg, 1)) == CONST_INT) + { + v->dest_reg + = plus_constant (tem, INTVAL (XEXP (v->new_reg,1))); + + /* Only succeed if this will give valid addresses. + Try to validate both the first and the last + address resulting from loop unrolling, if + one fails, then can't do const elim here. */ + if (memory_address_p (v->mem_mode, v->dest_reg) + && memory_address_p (v->mem_mode, + plus_constant (v->dest_reg, + INTVAL (giv_inc) + * (unroll_number - 1)))) + { + /* Save the negative of the eliminated const, so + that we can calculate the dest_reg's increment + value later. */ + v->const_adjust = - INTVAL (XEXP (v->new_reg, 1)); + + v->new_reg = XEXP (v->new_reg, 0); + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Eliminating constant from giv %d\n", + REGNO (tem)); + } + else + v->dest_reg = tem; + } + else + v->dest_reg = tem; + + /* If the address hasn't been checked for validity yet, do so + now, and fail completely if either the first or the last + unrolled copy of the address is not a valid address. */ + if (v->dest_reg == tem + && (! memory_address_p (v->mem_mode, v->dest_reg) + || ! memory_address_p (v->mem_mode, + plus_constant (v->dest_reg, + INTVAL (giv_inc) + * (unroll_number -1))))) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Illegal address for giv at insn %d\n", + INSN_UID (v->insn)); + continue; + } + + /* To initialize the new register, just move the value of + new_reg into it. This is not guaranteed to give a valid + instruction on machines with complex addressing modes. + If we can't recognize it, then delete it and emit insns + to calculate the value from scratch. */ + emit_insn_before (gen_rtx (SET, VOIDmode, tem, + copy_rtx (v->new_reg)), + loop_start); + if (recog_memoized (PREV_INSN (loop_start)) < 0) + { + delete_insn (PREV_INSN (loop_start)); + emit_iv_add_mult (bl->initial_value, v->mult_val, + v->add_val, tem, loop_start); + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Illegal init insn, rewritten.\n"); + } + } + else + { + v->dest_reg = value; + + /* Check the resulting address for validity, and fail + if the resulting address would be illegal. */ + if (! memory_address_p (v->mem_mode, v->dest_reg) + || ! memory_address_p (v->mem_mode, + plus_constant (v->dest_reg, + INTVAL (giv_inc) * + (unroll_number -1)))) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Illegal address for giv at insn %d\n", + INSN_UID (v->insn)); + continue; + } + } + + /* Store the value of dest_reg into the insn. This sharing + will not be a problem as this insn will always be copied + later. */ + + *v->location = v->dest_reg; + + /* If this address giv is combined with a dest reg giv, then + save the base giv's induction pointer so that we will be + able to handle this address giv properly. The base giv + itself does not have to be splittable. */ + + if (v->same && v->same->giv_type == DEST_REG) + addr_combined_regs[REGNO (v->same->new_reg)] = v->same; + + if (GET_CODE (v->new_reg) == REG) + { + /* This giv maybe hasn't been combined with any others. + Make sure that it's giv is marked as splittable here. */ + + splittable_regs[REGNO (v->new_reg)] = value; + + /* Make it appear to depend upon itself, so that the + giv will be properly split in the main loop above. */ + if (! v->same) + { + v->same = v; + addr_combined_regs[REGNO (v->new_reg)] = v; + } + } + + if (loop_dump_stream) + fprintf (loop_dump_stream, "DEST_ADDR giv being split.\n"); + } + } + else + { +#if 0 + /* Currently, unreduced giv's can't be split. This is not too much + of a problem since unreduced giv's are not live across loop + iterations anyways. When unrolling a loop completely though, + it makes sense to reduce&split givs when possible, as this will + result in simpler instructions, and will not require that a reg + be live across loop iterations. */ + + splittable_regs[REGNO (v->dest_reg)] = value; + fprintf (stderr, "Giv %d at insn %d not reduced\n", + REGNO (v->dest_reg), INSN_UID (v->insn)); +#else + continue; +#endif + } + + /* Givs are only updated once by definition. Mark it so if this is + a splittable register. Don't need to do anything for address givs + where this may not be a register. */ + + if (GET_CODE (v->new_reg) == REG) + splittable_regs_updates[REGNO (v->new_reg)] = 1; + + result++; + + if (loop_dump_stream) + { + int regnum; + + if (GET_CODE (v->dest_reg) == CONST_INT) + regnum = -1; + else if (GET_CODE (v->dest_reg) != REG) + regnum = REGNO (XEXP (v->dest_reg, 0)); + else + regnum = REGNO (v->dest_reg); + fprintf (loop_dump_stream, "Giv %d at insn %d safe to split.\n", + regnum, INSN_UID (v->insn)); + } + } + + return result; +} + +/* Try to prove that the register is dead after the loop exits. Trace every + loop exit looking for an insn that will always be executed, which sets + the register to some value, and appears before the first use of the register + is found. If successful, then return 1, otherwise return 0. */ + +/* ?? Could be made more intelligent in the handling of jumps, so that + it can search past if statements and other similar structures. */ + +static int +reg_dead_after_loop (reg, loop_start, loop_end) + rtx reg, loop_start, loop_end; +{ + rtx insn, label; + enum rtx_code code; + int jump_count = 0; + + /* HACK: Must also search the loop fall through exit, create a label_ref + here which points to the loop_end, and append the loop_number_exit_labels + list to it. */ + label = gen_rtx (LABEL_REF, VOIDmode, loop_end); + LABEL_NEXTREF (label) + = loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]]; + + for ( ; label; label = LABEL_NEXTREF (label)) + { + /* Succeed if find an insn which sets the biv or if reach end of + function. Fail if find an insn that uses the biv, or if come to + a conditional jump. */ + + insn = NEXT_INSN (XEXP (label, 0)); + while (insn) + { + code = GET_CODE (insn); + if (GET_RTX_CLASS (code) == 'i') + { + rtx set; + + if (reg_referenced_p (reg, PATTERN (insn))) + return 0; + + set = single_set (insn); + if (set && rtx_equal_p (SET_DEST (set), reg)) + break; + } + + if (code == JUMP_INSN) + { + if (GET_CODE (PATTERN (insn)) == RETURN) + break; + else if (! simplejump_p (insn) + /* Prevent infinite loop following infinite loops. */ + || jump_count++ > 20) + return 0; + else + insn = JUMP_LABEL (insn); + } + + insn = NEXT_INSN (insn); + } + } + + /* Success, the register is dead on all loop exits. */ + return 1; +} + +/* Try to calculate the final value of the biv, the value it will have at + the end of the loop. If we can do it, return that value. */ + +rtx +final_biv_value (bl, loop_start, loop_end) + struct iv_class *bl; + rtx loop_start, loop_end; +{ + rtx increment, tem; + + /* ??? This only works for MODE_INT biv's. Reject all others for now. */ + + if (GET_MODE_CLASS (bl->biv->mode) != MODE_INT) + return 0; + + /* The final value for reversed bivs must be calculated differently than + for ordinary bivs. In this case, there is already an insn after the + loop which sets this biv's final value (if necessary), and there are + no other loop exits, so we can return any value. */ + if (bl->reversed) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Final biv value for %d, reversed biv.\n", bl->regno); + + return const0_rtx; + } + + /* Try to calculate the final value as initial value + (number of iterations + * increment). For this to work, increment must be invariant, the only + exit from the loop must be the fall through at the bottom (otherwise + it may not have its final value when the loop exits), and the initial + value of the biv must be invariant. */ + + if (loop_n_iterations != 0 + && ! loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]] + && invariant_p (bl->initial_value)) + { + increment = biv_total_increment (bl, loop_start, loop_end); + + if (increment && invariant_p (increment)) + { + /* Can calculate the loop exit value, emit insns after loop + end to calculate this value into a temporary register in + case it is needed later. */ + + tem = gen_reg_rtx (bl->biv->mode); + /* Make sure loop_end is not the last insn. */ + if (NEXT_INSN (loop_end) == 0) + emit_note_after (NOTE_INSN_DELETED, loop_end); + emit_iv_add_mult (increment, GEN_INT (loop_n_iterations), + bl->initial_value, tem, NEXT_INSN (loop_end)); + + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Final biv value for %d, calculated.\n", bl->regno); + + return tem; + } + } + + /* Check to see if the biv is dead at all loop exits. */ + if (reg_dead_after_loop (bl->biv->src_reg, loop_start, loop_end)) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Final biv value for %d, biv dead after loop exit.\n", + bl->regno); + + return const0_rtx; + } + + return 0; +} + +/* Try to calculate the final value of the giv, the value it will have at + the end of the loop. If we can do it, return that value. */ + +rtx +final_giv_value (v, loop_start, loop_end) + struct induction *v; + rtx loop_start, loop_end; +{ + struct iv_class *bl; + rtx insn; + rtx increment, tem; + enum rtx_code code; + rtx insert_before, seq; + + bl = reg_biv_class[REGNO (v->src_reg)]; + + /* The final value for givs which depend on reversed bivs must be calculated + differently than for ordinary givs. In this case, there is already an + insn after the loop which sets this giv's final value (if necessary), + and there are no other loop exits, so we can return any value. */ + if (bl->reversed) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Final giv value for %d, depends on reversed biv\n", + REGNO (v->dest_reg)); + return const0_rtx; + } + + /* Try to calculate the final value as a function of the biv it depends + upon. The only exit from the loop must be the fall through at the bottom + (otherwise it may not have its final value when the loop exits). */ + + /* ??? Can calculate the final giv value by subtracting off the + extra biv increments times the giv's mult_val. The loop must have + only one exit for this to work, but the loop iterations does not need + to be known. */ + + if (loop_n_iterations != 0 + && ! loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]]) + { + /* ?? It is tempting to use the biv's value here since these insns will + be put after the loop, and hence the biv will have its final value + then. However, this fails if the biv is subsequently eliminated. + Perhaps determine whether biv's are eliminable before trying to + determine whether giv's are replaceable so that we can use the + biv value here if it is not eliminable. */ + + increment = biv_total_increment (bl, loop_start, loop_end); + + if (increment && invariant_p (increment)) + { + /* Can calculate the loop exit value of its biv as + (loop_n_iterations * increment) + initial_value */ + + /* The loop exit value of the giv is then + (final_biv_value - extra increments) * mult_val + add_val. + The extra increments are any increments to the biv which + occur in the loop after the giv's value is calculated. + We must search from the insn that sets the giv to the end + of the loop to calculate this value. */ + + insert_before = NEXT_INSN (loop_end); + + /* Put the final biv value in tem. */ + tem = gen_reg_rtx (bl->biv->mode); + emit_iv_add_mult (increment, GEN_INT (loop_n_iterations), + bl->initial_value, tem, insert_before); + + /* Subtract off extra increments as we find them. */ + for (insn = NEXT_INSN (v->insn); insn != loop_end; + insn = NEXT_INSN (insn)) + { + struct induction *biv; + + for (biv = bl->biv; biv; biv = biv->next_iv) + if (biv->insn == insn) + { + start_sequence (); + tem = expand_binop (GET_MODE (tem), sub_optab, tem, + biv->add_val, NULL_RTX, 0, + OPTAB_LIB_WIDEN); + seq = gen_sequence (); + end_sequence (); + emit_insn_before (seq, insert_before); + } + } + + /* Now calculate the giv's final value. */ + emit_iv_add_mult (tem, v->mult_val, v->add_val, tem, + insert_before); + + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Final giv value for %d, calc from biv's value.\n", + REGNO (v->dest_reg)); + + return tem; + } + } + + /* Replaceable giv's should never reach here. */ + if (v->replaceable) + abort (); + + /* Check to see if the biv is dead at all loop exits. */ + if (reg_dead_after_loop (v->dest_reg, loop_start, loop_end)) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Final giv value for %d, giv dead after loop exit.\n", + REGNO (v->dest_reg)); + + return const0_rtx; + } + + return 0; +} + + +/* Calculate the number of loop iterations. Returns the exact number of loop + iterations if it can be calculated, otherwise returns zero. */ + +unsigned HOST_WIDE_INT +loop_iterations (loop_start, loop_end) + rtx loop_start, loop_end; +{ + rtx comparison, comparison_value; + rtx iteration_var, initial_value, increment, final_value; + enum rtx_code comparison_code; + HOST_WIDE_INT i; + int increment_dir; + int unsigned_compare, compare_dir, final_larger; + unsigned long tempu; + rtx last_loop_insn; + + /* First find the iteration variable. If the last insn is a conditional + branch, and the insn before tests a register value, make that the + iteration variable. */ + + loop_initial_value = 0; + loop_increment = 0; + loop_final_value = 0; + loop_iteration_var = 0; + + last_loop_insn = prev_nonnote_insn (loop_end); + + comparison = get_condition_for_loop (last_loop_insn); + if (comparison == 0) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Loop unrolling: No final conditional branch found.\n"); + return 0; + } + + /* ??? Get_condition may switch position of induction variable and + invariant register when it canonicalizes the comparison. */ + + comparison_code = GET_CODE (comparison); + iteration_var = XEXP (comparison, 0); + comparison_value = XEXP (comparison, 1); + + if (GET_CODE (iteration_var) != REG) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Loop unrolling: Comparison not against register.\n"); + return 0; + } + + /* Loop iterations is always called before any new registers are created + now, so this should never occur. */ + + if (REGNO (iteration_var) >= max_reg_before_loop) + abort (); + + iteration_info (iteration_var, &initial_value, &increment, + loop_start, loop_end); + if (initial_value == 0) + /* iteration_info already printed a message. */ + return 0; + + if (increment == 0) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Loop unrolling: Increment value can't be calculated.\n"); + return 0; + } + if (GET_CODE (increment) != CONST_INT) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Loop unrolling: Increment value not constant.\n"); + return 0; + } + if (GET_CODE (initial_value) != CONST_INT) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Loop unrolling: Initial value not constant.\n"); + return 0; + } + + /* If the comparison value is an invariant register, then try to find + its value from the insns before the start of the loop. */ + + if (GET_CODE (comparison_value) == REG && invariant_p (comparison_value)) + { + rtx insn, set; + + for (insn = PREV_INSN (loop_start); insn ; insn = PREV_INSN (insn)) + { + if (GET_CODE (insn) == CODE_LABEL) + break; + + else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && reg_set_p (comparison_value, insn)) + { + /* We found the last insn before the loop that sets the register. + If it sets the entire register, and has a REG_EQUAL note, + then use the value of the REG_EQUAL note. */ + if ((set = single_set (insn)) + && (SET_DEST (set) == comparison_value)) + { + rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX); + + if (note && GET_CODE (XEXP (note, 0)) != EXPR_LIST) + comparison_value = XEXP (note, 0); + } + break; + } + } + } + + final_value = approx_final_value (comparison_code, comparison_value, + &unsigned_compare, &compare_dir); + + /* Save the calculated values describing this loop's bounds, in case + precondition_loop_p will need them later. These values can not be + recalculated inside precondition_loop_p because strength reduction + optimizations may obscure the loop's structure. */ + + loop_iteration_var = iteration_var; + loop_initial_value = initial_value; + loop_increment = increment; + loop_final_value = final_value; + + if (final_value == 0) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Loop unrolling: EQ comparison loop.\n"); + return 0; + } + else if (GET_CODE (final_value) != CONST_INT) + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Loop unrolling: Final value not constant.\n"); + return 0; + } + + /* ?? Final value and initial value do not have to be constants. + Only their difference has to be constant. When the iteration variable + is an array address, the final value and initial value might both + be addresses with the same base but different constant offsets. + Final value must be invariant for this to work. + + To do this, need some way to find the values of registers which are + invariant. */ + + /* Final_larger is 1 if final larger, 0 if they are equal, otherwise -1. */ + if (unsigned_compare) + final_larger + = ((unsigned HOST_WIDE_INT) INTVAL (final_value) + > (unsigned HOST_WIDE_INT) INTVAL (initial_value)) + - ((unsigned HOST_WIDE_INT) INTVAL (final_value) + < (unsigned HOST_WIDE_INT) INTVAL (initial_value)); + else + final_larger = (INTVAL (final_value) > INTVAL (initial_value)) + - (INTVAL (final_value) < INTVAL (initial_value)); + + if (INTVAL (increment) > 0) + increment_dir = 1; + else if (INTVAL (increment) == 0) + increment_dir = 0; + else + increment_dir = -1; + + /* There are 27 different cases: compare_dir = -1, 0, 1; + final_larger = -1, 0, 1; increment_dir = -1, 0, 1. + There are 4 normal cases, 4 reverse cases (where the iteration variable + will overflow before the loop exits), 4 infinite loop cases, and 15 + immediate exit (0 or 1 iteration depending on loop type) cases. + Only try to optimize the normal cases. */ + + /* (compare_dir/final_larger/increment_dir) + Normal cases: (0/-1/-1), (0/1/1), (-1/-1/-1), (1/1/1) + Reverse cases: (0/-1/1), (0/1/-1), (-1/-1/1), (1/1/-1) + Infinite loops: (0/-1/0), (0/1/0), (-1/-1/0), (1/1/0) + Immediate exit: (0/0/X), (-1/0/X), (-1/1/X), (1/0/X), (1/-1/X) */ + + /* ?? If the meaning of reverse loops (where the iteration variable + will overflow before the loop exits) is undefined, then could + eliminate all of these special checks, and just always assume + the loops are normal/immediate/infinite. Note that this means + the sign of increment_dir does not have to be known. Also, + since it does not really hurt if immediate exit loops or infinite loops + are optimized, then that case could be ignored also, and hence all + loops can be optimized. + + According to ANSI Spec, the reverse loop case result is undefined, + because the action on overflow is undefined. + + See also the special test for NE loops below. */ + + if (final_larger == increment_dir && final_larger != 0 + && (final_larger == compare_dir || compare_dir == 0)) + /* Normal case. */ + ; + else + { + if (loop_dump_stream) + fprintf (loop_dump_stream, + "Loop unrolling: Not normal loop.\n"); + return 0; + } + + /* Calculate the number of iterations, final_value is only an approximation, + so correct for that. Note that tempu and loop_n_iterations are + unsigned, because they can be as large as 2^n - 1. */ + + i = INTVAL (increment); + if (i > 0) + tempu = INTVAL (final_value) - INTVAL (initial_value); + else if (i < 0) + { + tempu = INTVAL (initial_value) - INTVAL (final_value); + i = -i; + } + else + abort (); + + /* For NE tests, make sure that the iteration variable won't miss the + final value. If tempu mod i is not zero, then the iteration variable + will overflow before the loop exits, and we can not calculate the + number of iterations. */ + if (compare_dir == 0 && (tempu % i) != 0) + return 0; + + return tempu / i + ((tempu % i) != 0); +} diff --git a/gnu/usr.bin/cc/lib/varasm.c b/gnu/usr.bin/cc/lib/varasm.c new file mode 100644 index 0000000000..6795444223 --- /dev/null +++ b/gnu/usr.bin/cc/lib/varasm.c @@ -0,0 +1,3033 @@ +/* Output variables, constants and external declarations, for GNU compiler. + Copyright (C) 1987, 1988, 1989, 1992, 1993 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* This file handles generation of all the assembler code + *except* the instructions of a function. + This includes declarations of variables and their initial values. + + We also output the assembler code for constants stored in memory + and are responsible for combining constants with the same value. */ + +#include +#include +/* #include */ +#include "config.h" +#include "rtl.h" +#include "tree.h" +#include "flags.h" +#include "function.h" +#include "expr.h" +#include "hard-reg-set.h" +#include "regs.h" +#include "defaults.h" +#include "real.h" + +#include "obstack.h" + +#ifdef XCOFF_DEBUGGING_INFO +#include "xcoffout.h" +#endif + +#ifndef ASM_STABS_OP +#define ASM_STABS_OP ".stabs" +#endif + +/* This macro gets just the user-specified name + out of the string in a SYMBOL_REF. On most machines, + we discard the * if any and that's all. */ +#ifndef STRIP_NAME_ENCODING +#define STRIP_NAME_ENCODING(VAR,SYMBOL_NAME) \ + (VAR) = ((SYMBOL_NAME) + ((SYMBOL_NAME)[0] == '*')) +#endif + +/* File in which assembler code is being written. */ + +extern FILE *asm_out_file; + +/* The (assembler) name of the first globally-visible object output. */ +char *first_global_object_name; + +extern struct obstack *current_obstack; +extern struct obstack *saveable_obstack; +extern struct obstack permanent_obstack; +#define obstack_chunk_alloc xmalloc + +/* Number for making the label on the next + constant that is stored in memory. */ + +int const_labelno; + +/* Number for making the label on the next + static variable internal to a function. */ + +int var_labelno; + +/* Nonzero if at least one function definition has been seen. */ +static int function_defined; + +extern FILE *asm_out_file; + +static char *compare_constant_1 (); +static void record_constant_1 (); +void output_constant_pool (); +void assemble_name (); +int output_addressed_constants (); +void output_constant (); +void output_constructor (); +void text_section (); +void readonly_data_section (); +void data_section (); + +#ifdef EXTRA_SECTIONS +static enum in_section {no_section, in_text, in_data, EXTRA_SECTIONS} in_section + = no_section; +#else +static enum in_section {no_section, in_text, in_data} in_section + = no_section; +#endif + +/* Define functions like text_section for any extra sections. */ +#ifdef EXTRA_SECTION_FUNCTIONS +EXTRA_SECTION_FUNCTIONS +#endif + +/* Tell assembler to switch to text section. */ + +void +text_section () +{ + if (in_section != in_text) + { + fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP); + in_section = in_text; + } +} + +/* Tell assembler to switch to data section. */ + +void +data_section () +{ + if (in_section != in_data) + { + if (flag_shared_data) + { +#ifdef SHARED_SECTION_ASM_OP + fprintf (asm_out_file, "%s\n", SHARED_SECTION_ASM_OP); +#else + fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP); +#endif + } + else + fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP); + + in_section = in_data; + } +} + +/* Tell assembler to switch to read-only data section. This is normally + the text section. */ + +void +readonly_data_section () +{ +#ifdef READONLY_DATA_SECTION + READONLY_DATA_SECTION (); /* Note this can call data_section. */ +#else + text_section (); +#endif +} + +/* Determine if we're in the text section. */ + +int +in_text_section () +{ + return in_section == in_text; +} + +/* Create the rtl to represent a function, for a function definition. + DECL is a FUNCTION_DECL node which describes which function. + The rtl is stored into DECL. */ + +void +make_function_rtl (decl) + tree decl; +{ + char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + + /* Rename a nested function to avoid conflicts. */ + if (decl_function_context (decl) != 0 + && DECL_INITIAL (decl) != 0 + && DECL_RTL (decl) == 0) + { + char *label; + + name = IDENTIFIER_POINTER (DECL_NAME (decl)); + ASM_FORMAT_PRIVATE_NAME (label, name, var_labelno); + name = obstack_copy0 (saveable_obstack, label, strlen (label)); + var_labelno++; + } + + if (DECL_RTL (decl) == 0) + { + DECL_RTL (decl) + = gen_rtx (MEM, DECL_MODE (decl), + gen_rtx (SYMBOL_REF, Pmode, name)); + + /* Optionally set flags or add text to the name to record information + such as that it is a function name. If the name is changed, the macro + ASM_OUTPUT_LABELREF will have to know how to strip this information. + And if it finds a * at the beginning after doing so, it must handle + that too. */ +#ifdef ENCODE_SECTION_INFO + ENCODE_SECTION_INFO (decl); +#endif + } + + /* Record at least one function has been defined. */ + function_defined = 1; +} + +/* Given NAME, a putative register name, discard any customary prefixes. */ + +static char * +strip_reg_name (name) + char *name; +{ +#ifdef REGISTER_PREFIX + if (!strncmp (name, REGISTER_PREFIX, strlen (REGISTER_PREFIX))) + name += strlen (REGISTER_PREFIX); +#endif + if (name[0] == '%' || name[0] == '#') + name++; + return name; +} + +/* Decode an `asm' spec for a declaration as a register name. + Return the register number, or -1 if nothing specified, + or -2 if the ASMSPEC is not `cc' or `memory' and is not recognized, + or -3 if ASMSPEC is `cc' and is not recognized, + or -4 if ASMSPEC is `memory' and is not recognized. + Accept an exact spelling or a decimal number. + Prefixes such as % are optional. */ + +int +decode_reg_name (asmspec) + char *asmspec; +{ + if (asmspec != 0) + { + int i; + + /* Get rid of confusing prefixes. */ + asmspec = strip_reg_name (asmspec); + + /* Allow a decimal number as a "register name". */ + for (i = strlen (asmspec) - 1; i >= 0; i--) + if (! (asmspec[i] >= '0' && asmspec[i] <= '9')) + break; + if (asmspec[0] != 0 && i < 0) + { + i = atoi (asmspec); + if (i < FIRST_PSEUDO_REGISTER && i >= 0) + return i; + else + return -2; + } + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (reg_names[i][0] + && ! strcmp (asmspec, strip_reg_name (reg_names[i]))) + return i; + +#ifdef ADDITIONAL_REGISTER_NAMES + { + static struct { char *name; int number; } table[] + = ADDITIONAL_REGISTER_NAMES; + + for (i = 0; i < sizeof (table) / sizeof (table[0]); i++) + if (! strcmp (asmspec, table[i].name)) + return table[i].number; + } +#endif /* ADDITIONAL_REGISTER_NAMES */ + + if (!strcmp (asmspec, "memory")) + return -4; + + if (!strcmp (asmspec, "cc")) + return -3; + + return -2; + } + + return -1; +} + +/* Create the DECL_RTL for a declaration for a static or external variable + or static or external function. + ASMSPEC, if not 0, is the string which the user specified + as the assembler symbol name. + TOP_LEVEL is nonzero if this is a file-scope variable. + + This is never called for PARM_DECL nodes. */ + +void +make_decl_rtl (decl, asmspec, top_level) + tree decl; + char *asmspec; + int top_level; +{ + register char *name; + int reg_number = decode_reg_name (asmspec); + + if (DECL_ASSEMBLER_NAME (decl) != NULL_TREE) + name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + + if (reg_number == -2) + { + /* ASMSPEC is given, and not the name of a register. */ + name = (char *) obstack_alloc (saveable_obstack, + strlen (asmspec) + 2); + name[0] = '*'; + strcpy (&name[1], asmspec); + } + + /* For a duplicate declaration, we can be called twice on the + same DECL node. Don't alter the RTL already made + unless the old mode is wrong (which can happen when + the previous rtl was made when the type was incomplete). */ + if (DECL_RTL (decl) == 0 + || GET_MODE (DECL_RTL (decl)) != DECL_MODE (decl)) + { + DECL_RTL (decl) = 0; + + /* First detect errors in declaring global registers. */ + if (DECL_REGISTER (decl) && reg_number == -1) + error_with_decl (decl, + "register name not specified for `%s'"); + else if (DECL_REGISTER (decl) && reg_number < 0) + error_with_decl (decl, + "invalid register name for `%s'"); + else if ((reg_number >= 0 || reg_number == -3) && ! DECL_REGISTER (decl)) + error_with_decl (decl, + "register name given for non-register variable `%s'"); + else if (DECL_REGISTER (decl) && TREE_CODE (decl) == FUNCTION_DECL) + error ("function declared `register'"); + else if (DECL_REGISTER (decl) && TYPE_MODE (TREE_TYPE (decl)) == BLKmode) + error_with_decl (decl, "data type of `%s' isn't suitable for a register"); + else if (DECL_REGISTER (decl) + && ! HARD_REGNO_MODE_OK (reg_number, TYPE_MODE (TREE_TYPE (decl)))) + error_with_decl (decl, "register number for `%s' isn't suitable for the data type"); + /* Now handle properly declared static register variables. */ + else if (DECL_REGISTER (decl)) + { + int nregs; +#if 0 /* yylex should print the warning for this */ + if (pedantic) + pedwarn ("ANSI C forbids global register variables"); +#endif + if (DECL_INITIAL (decl) != 0 && top_level) + { + DECL_INITIAL (decl) = 0; + error ("global register variable has initial value"); + } + if (fixed_regs[reg_number] == 0 + && function_defined && top_level) + error ("global register variable follows a function definition"); + if (TREE_THIS_VOLATILE (decl)) + warning ("volatile register variables don't work as you might wish"); + + /* If the user specified one of the eliminables registers here, + e.g., FRAME_POINTER_REGNUM, we don't want to get this variable + confused with that register and be eliminated. Although this + usage is somewhat suspect, we nevertheless use the following + kludge to avoid setting DECL_RTL to frame_pointer_rtx. */ + + DECL_RTL (decl) + = gen_rtx (REG, DECL_MODE (decl), FIRST_PSEUDO_REGISTER); + REGNO (DECL_RTL (decl)) = reg_number; + REG_USERVAR_P (DECL_RTL (decl)) = 1; + + if (top_level) + { + /* Make this register fixed, so not usable for anything else. */ + nregs = HARD_REGNO_NREGS (reg_number, DECL_MODE (decl)); + while (nregs > 0) + global_regs[reg_number + --nregs] = 1; + init_reg_sets_1 (); + } + } + + /* Now handle ordinary static variables and functions (in memory). + Also handle vars declared register invalidly. */ + if (DECL_RTL (decl) == 0) + { + /* Can't use just the variable's own name for a variable + whose scope is less than the whole file. + Concatenate a distinguishing number. */ + if (!top_level && !DECL_EXTERNAL (decl) && asmspec == 0) + { + char *label; + + ASM_FORMAT_PRIVATE_NAME (label, name, var_labelno); + name = obstack_copy0 (saveable_obstack, label, strlen (label)); + var_labelno++; + } + + DECL_RTL (decl) = gen_rtx (MEM, DECL_MODE (decl), + gen_rtx (SYMBOL_REF, Pmode, name)); + if (TREE_THIS_VOLATILE (decl) + || (flag_volatile_global && TREE_CODE (decl) == VAR_DECL + && TREE_PUBLIC (decl))) + MEM_VOLATILE_P (DECL_RTL (decl)) = 1; + if (TREE_READONLY (decl)) + RTX_UNCHANGING_P (DECL_RTL (decl)) = 1; + MEM_IN_STRUCT_P (DECL_RTL (decl)) + = (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE + || TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE + || TREE_CODE (TREE_TYPE (decl)) == QUAL_UNION_TYPE); + + /* Optionally set flags or add text to the name to record information + such as that it is a function name. + If the name is changed, the macro ASM_OUTPUT_LABELREF + will have to know how to strip this information. + And if it finds a * at the beginning after doing so, + it must handle that too. */ +#ifdef ENCODE_SECTION_INFO + ENCODE_SECTION_INFO (decl); +#endif + } + } +} + +/* Make the rtl for variable VAR be volatile. + Use this only for static variables. */ + +void +make_var_volatile (var) + tree var; +{ + if (GET_CODE (DECL_RTL (var)) != MEM) + abort (); + + MEM_VOLATILE_P (DECL_RTL (var)) = 1; +} + +/* Output alignment directive to align for constant expression EXP. */ + +void +assemble_constant_align (exp) + tree exp; +{ + int align; + + /* Align the location counter as required by EXP's data type. */ + align = TYPE_ALIGN (TREE_TYPE (exp)); +#ifdef CONSTANT_ALIGNMENT + align = CONSTANT_ALIGNMENT (exp, align); +#endif + + if (align > BITS_PER_UNIT) + ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); +} + +/* Output a string of literal assembler code + for an `asm' keyword used between functions. */ + +void +assemble_asm (string) + tree string; +{ + app_enable (); + + if (TREE_CODE (string) == ADDR_EXPR) + string = TREE_OPERAND (string, 0); + + fprintf (asm_out_file, "\t%s\n", TREE_STRING_POINTER (string)); +} + +#if 0 /* This should no longer be needed, because + flag_gnu_linker should be 0 on these systems, + which should prevent any output + if ASM_OUTPUT_CONSTRUCTOR and ASM_OUTPUT_DESTRUCTOR are absent. */ +#if !(defined(DBX_DEBUGGING_INFO) && !defined(FASCIST_ASSEMBLER)) +#ifndef ASM_OUTPUT_CONSTRUCTOR +#define ASM_OUTPUT_CONSTRUCTOR(file, name) +#endif +#ifndef ASM_OUTPUT_DESTRUCTOR +#define ASM_OUTPUT_DESTRUCTOR(file, name) +#endif +#endif +#endif /* 0 */ + +/* Record an element in the table of global destructors. + How this is done depends on what sort of assembler and linker + are in use. + + NAME should be the name of a global function to be called + at exit time. This name is output using assemble_name. */ + +void +assemble_destructor (name) + char *name; +{ +#ifdef ASM_OUTPUT_DESTRUCTOR + ASM_OUTPUT_DESTRUCTOR (asm_out_file, name); +#else + if (flag_gnu_linker) + { + /* Now tell GNU LD that this is part of the static destructor set. */ + /* This code works for any machine provided you use GNU as/ld. */ + fprintf (asm_out_file, "%s \"___DTOR_LIST__\",22,0,0,", ASM_STABS_OP); + assemble_name (asm_out_file, name); + fputc ('\n', asm_out_file); + } +#endif +} + +/* Likewise for global constructors. */ + +void +assemble_constructor (name) + char *name; +{ +#ifdef ASM_OUTPUT_CONSTRUCTOR + ASM_OUTPUT_CONSTRUCTOR (asm_out_file, name); +#else + if (flag_gnu_linker) + { + /* Now tell GNU LD that this is part of the static constructor set. */ + /* This code works for any machine provided you use GNU as/ld. */ + fprintf (asm_out_file, "%s \"___CTOR_LIST__\",22,0,0,", ASM_STABS_OP); + assemble_name (asm_out_file, name); + fputc ('\n', asm_out_file); + } +#endif +} + +/* Likewise for entries we want to record for garbage collection. + Garbage collection is still under development. */ + +void +assemble_gc_entry (name) + char *name; +{ +#ifdef ASM_OUTPUT_GC_ENTRY + ASM_OUTPUT_GC_ENTRY (asm_out_file, name); +#else + if (flag_gnu_linker) + { + /* Now tell GNU LD that this is part of the static constructor set. */ + fprintf (asm_out_file, "%s \"___PTR_LIST__\",22,0,0,", ASM_STABS_OP); + assemble_name (asm_out_file, name); + fputc ('\n', asm_out_file); + } +#endif +} + +/* Output assembler code for the constant pool of a function and associated + with defining the name of the function. DECL describes the function. + NAME is the function's name. For the constant pool, we use the current + constant pool data. */ + +void +assemble_start_function (decl, fnname) + tree decl; + char *fnname; +{ + int align; + + /* The following code does not need preprocessing in the assembler. */ + + app_disable (); + + output_constant_pool (fnname, decl); + + text_section (); + + + /* Tell assembler to move to target machine's alignment for functions. */ + align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT); + if (align > 0) + ASM_OUTPUT_ALIGN (asm_out_file, align); + +#ifdef ASM_OUTPUT_FUNCTION_PREFIX + ASM_OUTPUT_FUNCTION_PREFIX (asm_out_file, fnname); +#endif + +#ifdef SDB_DEBUGGING_INFO + /* Output SDB definition of the function. */ + if (write_symbols == SDB_DEBUG) + sdbout_mark_begin_function (); +#endif + +#ifdef DBX_DEBUGGING_INFO + /* Output DBX definition of the function. */ + if (write_symbols == DBX_DEBUG) + dbxout_begin_function (decl); +#endif + + /* Make function name accessible from other files, if appropriate. */ + + if (TREE_PUBLIC (decl)) + { + if (!first_global_object_name) + STRIP_NAME_ENCODING (first_global_object_name, fnname); + ASM_GLOBALIZE_LABEL (asm_out_file, fnname); + } + + /* Do any machine/system dependent processing of the function name */ +#ifdef ASM_DECLARE_FUNCTION_NAME + ASM_DECLARE_FUNCTION_NAME (asm_out_file, fnname, current_function_decl); +#else + /* Standard thing is just output label for the function. */ + ASM_OUTPUT_LABEL (asm_out_file, fnname); +#endif /* ASM_DECLARE_FUNCTION_NAME */ +} + +/* Output assembler code associated with defining the size of the + function. DECL describes the function. NAME is the function's name. */ + +void +assemble_end_function (decl, fnname) + tree decl; + char *fnname; +{ +#ifdef ASM_DECLARE_FUNCTION_SIZE + ASM_DECLARE_FUNCTION_SIZE (asm_out_file, fnname, decl); +#endif +} + +/* Assemble code to leave SIZE bytes of zeros. */ + +void +assemble_zeros (size) + int size; +{ +#ifdef ASM_NO_SKIP_IN_TEXT + /* The `space' pseudo in the text section outputs nop insns rather than 0s, + so we must output 0s explicitly in the text section. */ + if (ASM_NO_SKIP_IN_TEXT && in_text_section ()) + { + int i; + + for (i = 0; i < size - 20; i += 20) + { +#ifdef ASM_BYTE_OP + fprintf (asm_out_file, + "%s 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n", ASM_BYTE_OP); +#else + fprintf (asm_out_file, + "\tbyte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n"); +#endif + } + if (i < size) + { +#ifdef ASM_BYTE_OP + fprintf (asm_out_file, "%s 0", ASM_BYTE_OP); +#else + fprintf (asm_out_file, "\tbyte 0"); +#endif + i++; + for (; i < size; i++) + fprintf (asm_out_file, ",0"); + fprintf (asm_out_file, "\n"); + } + } + else +#endif + if (size > 0) + ASM_OUTPUT_SKIP (asm_out_file, size); +} + +/* Assemble a string constant with the specified C string as contents. */ + +void +assemble_string (p, size) + char *p; + int size; +{ + register int i; + int pos = 0; + int maximum = 2000; + + /* If the string is very long, split it up. */ + + while (pos < size) + { + int thissize = size - pos; + if (thissize > maximum) + thissize = maximum; + + ASM_OUTPUT_ASCII (asm_out_file, p, thissize); + + pos += thissize; + p += thissize; + } +} + +/* Assemble everything that is needed for a variable or function declaration. + Not used for automatic variables, and not used for function definitions. + Should not be called for variables of incomplete structure type. + + TOP_LEVEL is nonzero if this variable has file scope. + AT_END is nonzero if this is the special handling, at end of compilation, + to define things that have had only tentative definitions. */ + +void +assemble_variable (decl, top_level, at_end) + tree decl; + int top_level; + int at_end; +{ + register char *name; + int align; + tree size_tree; + int reloc = 0; + + if (GET_CODE (DECL_RTL (decl)) == REG) + { + /* Do output symbol info for global register variables, but do nothing + else for them. */ + + if (TREE_ASM_WRITTEN (decl)) + return; + TREE_ASM_WRITTEN (decl) = 1; + +#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) + /* File-scope global variables are output here. */ + if ((write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) + && top_level) + dbxout_symbol (decl, 0); +#endif +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG && top_level + /* Leave initialized global vars for end of compilation; + see comment in compile_file. */ + && (TREE_PUBLIC (decl) == 0 || DECL_INITIAL (decl) == 0)) + sdbout_symbol (decl, 0); +#endif + + /* Don't output any DWARF debugging information for variables here. + In the case of local variables, the information for them is output + when we do our recursive traversal of the tree representation for + the entire containing function. In the case of file-scope variables, + we output information for all of them at the very end of compilation + while we are doing our final traversal of the chain of file-scope + declarations. */ + + return; + } + + /* Normally no need to say anything here for external references, + since assemble_external is called by the langauge-specific code + when a declaration is first seen. */ + + if (DECL_EXTERNAL (decl)) + return; + + /* Output no assembler code for a function declaration. + Only definitions of functions output anything. */ + + if (TREE_CODE (decl) == FUNCTION_DECL) + return; + + /* If type was incomplete when the variable was declared, + see if it is complete now. */ + + if (DECL_SIZE (decl) == 0) + layout_decl (decl, 0); + + /* Still incomplete => don't allocate it; treat the tentative defn + (which is what it must have been) as an `extern' reference. */ + + if (DECL_SIZE (decl) == 0) + { + error_with_file_and_line (DECL_SOURCE_FILE (decl), + DECL_SOURCE_LINE (decl), + "storage size of `%s' isn't known", + IDENTIFIER_POINTER (DECL_NAME (decl))); + return; + } + + /* The first declaration of a variable that comes through this function + decides whether it is global (in C, has external linkage) + or local (in C, has internal linkage). So do nothing more + if this function has already run. */ + + if (TREE_ASM_WRITTEN (decl)) + return; + + TREE_ASM_WRITTEN (decl) = 1; + +#ifdef DBX_DEBUGGING_INFO + /* File-scope global variables are output here. */ + if (write_symbols == DBX_DEBUG && top_level) + dbxout_symbol (decl, 0); +#endif +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG && top_level + /* Leave initialized global vars for end of compilation; + see comment in compile_file. */ + && (TREE_PUBLIC (decl) == 0 || DECL_INITIAL (decl) == 0)) + sdbout_symbol (decl, 0); +#endif + + /* Don't output any DWARF debugging information for variables here. + In the case of local variables, the information for them is output + when we do our recursive traversal of the tree representation for + the entire containing function. In the case of file-scope variables, + we output information for all of them at the very end of compilation + while we are doing our final traversal of the chain of file-scope + declarations. */ + + /* If storage size is erroneously variable, just continue. + Error message was already made. */ + + if (TREE_CODE (DECL_SIZE (decl)) != INTEGER_CST) + goto finish; + + app_disable (); + + /* This is better than explicit arithmetic, since it avoids overflow. */ + size_tree = size_binop (CEIL_DIV_EXPR, + DECL_SIZE (decl), size_int (BITS_PER_UNIT)); + + if (TREE_INT_CST_HIGH (size_tree) != 0) + { + error_with_decl (decl, "size of variable `%s' is too large"); + goto finish; + } + + name = XSTR (XEXP (DECL_RTL (decl), 0), 0); + + /* Handle uninitialized definitions. */ + + /* ANSI specifies that a tentative definition which is not merged with + a non-tentative definition behaves exactly like a definition with an + initializer equal to zero. (Section 3.7.2) + -fno-common gives strict ANSI behavior. Usually you don't want it. */ + if (! flag_no_common + && (DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node)) + { + int size = TREE_INT_CST_LOW (size_tree); + int rounded = size; + + if (TREE_INT_CST_HIGH (size_tree) != 0) + error_with_decl (decl, "size of variable `%s' is too large"); + /* Don't allocate zero bytes of common, + since that means "undefined external" in the linker. */ + if (size == 0) rounded = 1; + /* Round size up to multiple of BIGGEST_ALIGNMENT bits + so that each uninitialized object starts on such a boundary. */ + rounded += (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1; + rounded = (rounded / (BIGGEST_ALIGNMENT / BITS_PER_UNIT) + * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); +#if 0 + if (flag_shared_data) + data_section (); +#endif + if (TREE_PUBLIC (decl)) + { +#ifdef ASM_OUTPUT_SHARED_COMMON + if (flag_shared_data) + ASM_OUTPUT_SHARED_COMMON (asm_out_file, name, size, rounded); + else +#endif +#ifdef ASM_OUTPUT_ALIGNED_COMMON + ASM_OUTPUT_ALIGNED_COMMON (asm_out_file, name, size, + DECL_ALIGN (decl)); +#else + ASM_OUTPUT_COMMON (asm_out_file, name, size, rounded); +#endif + } + else + { +#ifdef ASM_OUTPUT_SHARED_LOCAL + if (flag_shared_data) + ASM_OUTPUT_SHARED_LOCAL (asm_out_file, name, size, rounded); + else +#endif +#ifdef ASM_OUTPUT_ALIGNED_LOCAL + ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, + DECL_ALIGN (decl)); +#else + ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded); +#endif + } + goto finish; + } + + /* Handle initialized definitions. */ + + /* First make the assembler name(s) global if appropriate. */ + if (TREE_PUBLIC (decl) && DECL_NAME (decl)) + { + if (!first_global_object_name) + STRIP_NAME_ENCODING(first_global_object_name, name); + ASM_GLOBALIZE_LABEL (asm_out_file, name); + } +#if 0 + for (d = equivalents; d; d = TREE_CHAIN (d)) + { + tree e = TREE_VALUE (d); + if (TREE_PUBLIC (e) && DECL_NAME (e)) + ASM_GLOBALIZE_LABEL (asm_out_file, + XSTR (XEXP (DECL_RTL (e), 0), 0)); + } +#endif + + /* Output any data that we will need to use the address of. */ + if (DECL_INITIAL (decl)) + reloc = output_addressed_constants (DECL_INITIAL (decl)); + + /* Switch to the proper section for this data. */ +#ifdef SELECT_SECTION + SELECT_SECTION (decl, reloc); +#else + if (TREE_READONLY (decl) + && ! TREE_THIS_VOLATILE (decl) + && ! (flag_pic && reloc)) + readonly_data_section (); + else + data_section (); +#endif + + /* Compute and output the alignment of this data. */ + + align = DECL_ALIGN (decl); + /* Some object file formats have a maximum alignment which they support. + In particular, a.out format supports a maximum alignment of 4. */ +#ifndef MAX_OFILE_ALIGNMENT +#define MAX_OFILE_ALIGNMENT BIGGEST_ALIGNMENT +#endif + if (align > MAX_OFILE_ALIGNMENT) + { + warning_with_decl (decl, + "alignment of `%s' is greater than maximum object file alignment"); + align = MAX_OFILE_ALIGNMENT; + } +#ifdef DATA_ALIGNMENT + /* On some machines, it is good to increase alignment sometimes. */ + align = DATA_ALIGNMENT (TREE_TYPE (decl), align); +#endif +#ifdef CONSTANT_ALIGNMENT + if (DECL_INITIAL (decl)) + align = CONSTANT_ALIGNMENT (DECL_INITIAL (decl), align); +#endif + + /* Reset the alignment in case we have made it tighter, so we can benefit + from it in get_pointer_alignment. */ + DECL_ALIGN (decl) = align; + + if (align > BITS_PER_UNIT) + ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); + + /* Do any machine/system dependent processing of the object. */ +#ifdef ASM_DECLARE_OBJECT_NAME + ASM_DECLARE_OBJECT_NAME (asm_out_file, name, decl); +#else + /* Standard thing is just output label for the object. */ + ASM_OUTPUT_LABEL (asm_out_file, name); +#endif /* ASM_DECLARE_OBJECT_NAME */ + +#if 0 + for (d = equivalents; d; d = TREE_CHAIN (d)) + { + tree e = TREE_VALUE (d); + ASM_OUTPUT_LABEL (asm_out_file, XSTR (XEXP (DECL_RTL (e), 0), 0)); + } +#endif + + if (DECL_INITIAL (decl)) + /* Output the actual data. */ + output_constant (DECL_INITIAL (decl), + int_size_in_bytes (TREE_TYPE (decl))); + else + /* Leave space for it. */ + assemble_zeros (int_size_in_bytes (TREE_TYPE (decl))); + + finish: +#ifdef XCOFF_DEBUGGING_INFO + /* Unfortunately, the IBM assembler cannot handle stabx before the actual + declaration. When something like ".stabx "aa:S-2",aa,133,0" is emitted + and `aa' hasn't been output yet, the assembler generates a stab entry with + a value of zero, in addition to creating an unnecessary external entry + for `aa'. Hence, we must postpone dbxout_symbol to here at the end. */ + + /* File-scope global variables are output here. */ + if (write_symbols == XCOFF_DEBUG && top_level) + dbxout_symbol (decl, 0); +#else + /* There must be a statement after a label. */ + ; +#endif +} + +/* Output something to declare an external symbol to the assembler. + (Most assemblers don't need this, so we normally output nothing.) + Do nothing if DECL is not external. */ + +void +assemble_external (decl) + tree decl; +{ +#ifdef ASM_OUTPUT_EXTERNAL + if (TREE_CODE_CLASS (TREE_CODE (decl)) == 'd' + && DECL_EXTERNAL (decl) && TREE_PUBLIC (decl)) + { + rtx rtl = DECL_RTL (decl); + + if (GET_CODE (rtl) == MEM && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF + && ! SYMBOL_REF_USED (XEXP (rtl, 0))) + { + /* Some systems do require some output. */ + SYMBOL_REF_USED (XEXP (rtl, 0)) = 1; + ASM_OUTPUT_EXTERNAL (asm_out_file, decl, XSTR (XEXP (rtl, 0), 0)); + } + } +#endif +} + +/* Similar, for calling a library function FUN. */ + +void +assemble_external_libcall (fun) + rtx fun; +{ +#ifdef ASM_OUTPUT_EXTERNAL_LIBCALL + /* Declare library function name external when first used, if nec. */ + if (! SYMBOL_REF_USED (fun)) + { + SYMBOL_REF_USED (fun) = 1; + ASM_OUTPUT_EXTERNAL_LIBCALL (asm_out_file, fun); + } +#endif +} + +/* Declare the label NAME global. */ + +void +assemble_global (name) + char *name; +{ + ASM_GLOBALIZE_LABEL (asm_out_file, name); +} + +/* Assemble a label named NAME. */ + +void +assemble_label (name) + char *name; +{ + ASM_OUTPUT_LABEL (asm_out_file, name); +} + +/* Output to FILE a reference to the assembler name of a C-level name NAME. + If NAME starts with a *, the rest of NAME is output verbatim. + Otherwise NAME is transformed in an implementation-defined way + (usually by the addition of an underscore). + Many macros in the tm file are defined to call this function. */ + +void +assemble_name (file, name) + FILE *file; + char *name; +{ + if (name[0] == '*') + fputs (&name[1], file); + else + ASM_OUTPUT_LABELREF (file, name); +} + +/* Allocate SIZE bytes writable static space with a gensym name + and return an RTX to refer to its address. */ + +rtx +assemble_static_space (size) + int size; +{ + char name[12]; + char *namestring; + rtx x; + /* Round size up to multiple of BIGGEST_ALIGNMENT bits + so that each uninitialized object starts on such a boundary. */ + int rounded = ((size + (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1) + / (BIGGEST_ALIGNMENT / BITS_PER_UNIT) + * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); + +#if 0 + if (flag_shared_data) + data_section (); +#endif + + ASM_GENERATE_INTERNAL_LABEL (name, "LF", const_labelno); + ++const_labelno; + + namestring = (char *) obstack_alloc (saveable_obstack, + strlen (name) + 2); + strcpy (namestring, name); + + x = gen_rtx (SYMBOL_REF, Pmode, namestring); +#ifdef ASM_OUTPUT_ALIGNED_LOCAL + ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, BIGGEST_ALIGNMENT); +#else + ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded); +#endif + return x; +} + +/* Assemble the static constant template for function entry trampolines. + This is done at most once per compilation. + Returns an RTX for the address of the template. */ + +rtx +assemble_trampoline_template () +{ + char label[256]; + char *name; + int align; + + /* By default, put trampoline templates in read-only data section. */ + +#ifdef TRAMPOLINE_SECTION + TRAMPOLINE_SECTION (); +#else + readonly_data_section (); +#endif + + /* Write the assembler code to define one. */ + align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT); + if (align > 0) + ASM_OUTPUT_ALIGN (asm_out_file, align); + + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LTRAMP", 0); + TRAMPOLINE_TEMPLATE (asm_out_file); + + /* Record the rtl to refer to it. */ + ASM_GENERATE_INTERNAL_LABEL (label, "LTRAMP", 0); + name + = (char *) obstack_copy0 (&permanent_obstack, label, strlen (label)); + return gen_rtx (SYMBOL_REF, Pmode, name); +} + +/* Assemble the integer constant X into an object of SIZE bytes. + X must be either a CONST_INT or CONST_DOUBLE. + + Return 1 if we were able to output the constant, otherwise 0. If FORCE is + non-zero, abort if we can't output the constant. */ + +int +assemble_integer (x, size, force) + rtx x; + int size; + int force; +{ + /* First try to use the standard 1, 2, 4, 8, and 16 byte + ASM_OUTPUT... macros. */ + + switch (size) + { +#ifdef ASM_OUTPUT_CHAR + case 1: + ASM_OUTPUT_CHAR (asm_out_file, x); + return 1; +#endif + +#ifdef ASM_OUTPUT_SHORT + case 2: + ASM_OUTPUT_SHORT (asm_out_file, x); + return 1; +#endif + +#ifdef ASM_OUTPUT_INT + case 4: + ASM_OUTPUT_INT (asm_out_file, x); + return 1; +#endif + +#ifdef ASM_OUTPUT_DOUBLE_INT + case 8: + ASM_OUTPUT_DOUBLE_INT (asm_out_file, x); + return 1; +#endif + +#ifdef ASM_OUTPUT_QUADRUPLE_INT + case 16: + ASM_OUTPUT_QUADRUPLE_INT (asm_out_file, x); + return 1; +#endif + } + + /* If we couldn't do it that way, there are two other possibilities: First, + if the machine can output an explicit byte and this is a 1 byte constant, + we can use ASM_OUTPUT_BYTE. */ + +#ifdef ASM_OUTPUT_BYTE + if (size == 1 && GET_CODE (x) == CONST_INT) + { + ASM_OUTPUT_BYTE (asm_out_file, INTVAL (x)); + return 1; + } +#endif + + /* Finally, if SIZE is larger than a single word, try to output the constant + one word at a time. */ + + if (size > UNITS_PER_WORD) + { + int i; + enum machine_mode mode + = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0); + rtx word; + + for (i = 0; i < size / UNITS_PER_WORD; i++) + { + word = operand_subword (x, i, 0, mode); + + if (word == 0) + break; + + if (! assemble_integer (word, UNITS_PER_WORD, 0)) + break; + } + + if (i == size / UNITS_PER_WORD) + return 1; + /* If we output at least one word and then could not finish, + there is no valid way to continue. */ + if (i > 0) + abort (); + } + + if (force) + abort (); + + return 0; +} + +/* Assemble the floating-point constant D into an object of size MODE. */ + +void +assemble_real (d, mode) + REAL_VALUE_TYPE d; + enum machine_mode mode; +{ + jmp_buf output_constant_handler; + + if (setjmp (output_constant_handler)) + { + error ("floating point trap outputting a constant"); +#ifdef REAL_IS_NOT_DOUBLE + bzero (&d, sizeof d); + d = dconst0; +#else + d = 0; +#endif + } + + set_float_handler (output_constant_handler); + + switch (mode) + { +#ifdef ASM_OUTPUT_BYTE_FLOAT + case QFmode: + ASM_OUTPUT_BYTE_FLOAT (asm_out_file, d); + break; +#endif +#ifdef ASM_OUTPUT_SHORT_FLOAT + case HFmode: + ASM_OUTPUT_SHORT_FLOAT (asm_out_file, d); + break; +#endif +#ifdef ASM_OUTPUT_FLOAT + case SFmode: + ASM_OUTPUT_FLOAT (asm_out_file, d); + break; +#endif + +#ifdef ASM_OUTPUT_DOUBLE + case DFmode: + ASM_OUTPUT_DOUBLE (asm_out_file, d); + break; +#endif + +#ifdef ASM_OUTPUT_LONG_DOUBLE + case XFmode: + case TFmode: + ASM_OUTPUT_LONG_DOUBLE (asm_out_file, d); + break; +#endif + + default: + abort (); + } + + set_float_handler (NULL_PTR); +} + +/* Here we combine duplicate floating constants to make + CONST_DOUBLE rtx's, and force those out to memory when necessary. */ + +/* Chain of all CONST_DOUBLE rtx's constructed for the current function. + They are chained through the CONST_DOUBLE_CHAIN. + A CONST_DOUBLE rtx has CONST_DOUBLE_MEM != cc0_rtx iff it is on this chain. + In that case, CONST_DOUBLE_MEM is either a MEM, + or const0_rtx if no MEM has been made for this CONST_DOUBLE yet. + + (CONST_DOUBLE_MEM is used only for top-level functions. + See force_const_mem for explanation.) */ + +static rtx const_double_chain; + +/* Return a CONST_DOUBLE for a value specified as a pair of ints. + For an integer, I0 is the low-order word and I1 is the high-order word. + For a real number, I0 is the word with the low address + and I1 is the word with the high address. */ + +rtx +immed_double_const (i0, i1, mode) + HOST_WIDE_INT i0, i1; + enum machine_mode mode; +{ + register rtx r; + int in_current_obstack; + + if (GET_MODE_CLASS (mode) == MODE_INT + || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT) + { + /* We clear out all bits that don't belong in MODE, unless they and our + sign bit are all one. So we get either a reasonable negative value + or a reasonable unsigned value for this mode. */ + int width = GET_MODE_BITSIZE (mode); + if (width < HOST_BITS_PER_WIDE_INT + && ((i0 & ((HOST_WIDE_INT) (-1) << (width - 1))) + != ((HOST_WIDE_INT) (-1) << (width - 1)))) + i0 &= ((HOST_WIDE_INT) 1 << width) - 1, i1 = 0; + else if (width == HOST_BITS_PER_WIDE_INT + && ! (i1 == ~0 && i0 < 0)) + i1 = 0; + else if (width > 2 * HOST_BITS_PER_WIDE_INT) + /* We cannot represent this value as a constant. */ + abort (); + + /* If MODE fits within HOST_BITS_PER_WIDE_INT, always use a CONST_INT. + + ??? Strictly speaking, this is wrong if we create a CONST_INT + for a large unsigned constant with the size of MODE being + HOST_BITS_PER_WIDE_INT and later try to interpret that constant in a + wider mode. In that case we will mis-interpret it as a negative + number. + + Unfortunately, the only alternative is to make a CONST_DOUBLE + for any constant in any mode if it is an unsigned constant larger + than the maximum signed integer in an int on the host. However, + doing this will break everyone that always expects to see a CONST_INT + for SImode and smaller. + + We have always been making CONST_INTs in this case, so nothing new + is being broken. */ + + if (width <= HOST_BITS_PER_WIDE_INT) + i1 = (i0 < 0) ? ~0 : 0; + + /* If this integer fits in one word, return a CONST_INT. */ + if ((i1 == 0 && i0 >= 0) + || (i1 == ~0 && i0 < 0)) + return GEN_INT (i0); + + /* We use VOIDmode for integers. */ + mode = VOIDmode; + } + + /* Search the chain for an existing CONST_DOUBLE with the right value. + If one is found, return it. */ + + for (r = const_double_chain; r; r = CONST_DOUBLE_CHAIN (r)) + if (CONST_DOUBLE_LOW (r) == i0 && CONST_DOUBLE_HIGH (r) == i1 + && GET_MODE (r) == mode) + return r; + + /* No; make a new one and add it to the chain. + + We may be called by an optimizer which may be discarding any memory + allocated during its processing (such as combine and loop). However, + we will be leaving this constant on the chain, so we cannot tolerate + freed memory. So switch to saveable_obstack for this allocation + and then switch back if we were in current_obstack. */ + + push_obstacks_nochange (); + rtl_in_saveable_obstack (); + r = gen_rtx (CONST_DOUBLE, mode, 0, i0, i1); + pop_obstacks (); + + /* Don't touch const_double_chain in nested function; + see force_const_mem. */ + if (outer_function_chain == 0) + { + CONST_DOUBLE_CHAIN (r) = const_double_chain; + const_double_chain = r; + } + + /* Store const0_rtx in mem-slot since this CONST_DOUBLE is on the chain. + Actual use of mem-slot is only through force_const_mem. */ + + CONST_DOUBLE_MEM (r) = const0_rtx; + + return r; +} + +/* Return a CONST_DOUBLE for a specified `double' value + and machine mode. */ + +rtx +immed_real_const_1 (d, mode) + REAL_VALUE_TYPE d; + enum machine_mode mode; +{ + union real_extract u; + register rtx r; + int in_current_obstack; + + /* Get the desired `double' value as a sequence of ints + since that is how they are stored in a CONST_DOUBLE. */ + + u.d = d; + + /* Detect special cases. */ + + /* Avoid REAL_VALUES_EQUAL here in order to distinguish minus zero. */ + if (!bcmp (&dconst0, &d, sizeof d)) + return CONST0_RTX (mode); + /* Check for NaN first, because some ports (specifically the i386) do not + emit correct ieee-fp code by default, and thus will generate a core + dump here if we pass a NaN to REAL_VALUES_EQUAL and if REAL_VALUES_EQUAL + does a floating point comparison. */ + else if (! REAL_VALUE_ISNAN (d) && REAL_VALUES_EQUAL (dconst1, d)) + return CONST1_RTX (mode); + + if (sizeof u == 2 * sizeof (HOST_WIDE_INT)) + return immed_double_const (u.i[0], u.i[1], mode); + + /* The rest of this function handles the case where + a float value requires more than 2 ints of space. + It will be deleted as dead code on machines that don't need it. */ + + /* Search the chain for an existing CONST_DOUBLE with the right value. + If one is found, return it. */ + + for (r = const_double_chain; r; r = CONST_DOUBLE_CHAIN (r)) + if (! bcmp (&CONST_DOUBLE_LOW (r), &u, sizeof u) + && GET_MODE (r) == mode) + return r; + + /* No; make a new one and add it to the chain. + + We may be called by an optimizer which may be discarding any memory + allocated during its processing (such as combine and loop). However, + we will be leaving this constant on the chain, so we cannot tolerate + freed memory. So switch to saveable_obstack for this allocation + and then switch back if we were in current_obstack. */ + + push_obstacks_nochange (); + rtl_in_saveable_obstack (); + r = rtx_alloc (CONST_DOUBLE); + PUT_MODE (r, mode); + bcopy (&u, &CONST_DOUBLE_LOW (r), sizeof u); + pop_obstacks (); + + /* Don't touch const_double_chain in nested function; + see force_const_mem. */ + if (outer_function_chain == 0) + { + CONST_DOUBLE_CHAIN (r) = const_double_chain; + const_double_chain = r; + } + + /* Store const0_rtx in CONST_DOUBLE_MEM since this CONST_DOUBLE is on the + chain, but has not been allocated memory. Actual use of CONST_DOUBLE_MEM + is only through force_const_mem. */ + + CONST_DOUBLE_MEM (r) = const0_rtx; + + return r; +} + +/* Return a CONST_DOUBLE rtx for a value specified by EXP, + which must be a REAL_CST tree node. */ + +rtx +immed_real_const (exp) + tree exp; +{ + return immed_real_const_1 (TREE_REAL_CST (exp), TYPE_MODE (TREE_TYPE (exp))); +} + +/* At the end of a function, forget the memory-constants + previously made for CONST_DOUBLEs. Mark them as not on real_constant_chain. + Also clear out real_constant_chain and clear out all the chain-pointers. */ + +void +clear_const_double_mem () +{ + register rtx r, next; + + /* Don't touch CONST_DOUBLE_MEM for nested functions. + See force_const_mem for explanation. */ + if (outer_function_chain != 0) + return; + + for (r = const_double_chain; r; r = next) + { + next = CONST_DOUBLE_CHAIN (r); + CONST_DOUBLE_CHAIN (r) = 0; + CONST_DOUBLE_MEM (r) = cc0_rtx; + } + const_double_chain = 0; +} + +/* Given an expression EXP with a constant value, + reduce it to the sum of an assembler symbol and an integer. + Store them both in the structure *VALUE. + Abort if EXP does not reduce. */ + +struct addr_const +{ + rtx base; + HOST_WIDE_INT offset; +}; + +static void +decode_addr_const (exp, value) + tree exp; + struct addr_const *value; +{ + register tree target = TREE_OPERAND (exp, 0); + register int offset = 0; + register rtx x; + + while (1) + { + if (TREE_CODE (target) == COMPONENT_REF + && (TREE_CODE (DECL_FIELD_BITPOS (TREE_OPERAND (target, 1))) + == INTEGER_CST)) + { + offset += TREE_INT_CST_LOW (DECL_FIELD_BITPOS (TREE_OPERAND (target, 1))) / BITS_PER_UNIT; + target = TREE_OPERAND (target, 0); + } + else if (TREE_CODE (target) == ARRAY_REF) + { + if (TREE_CODE (TREE_OPERAND (target, 1)) != INTEGER_CST + || TREE_CODE (TYPE_SIZE (TREE_TYPE (target))) != INTEGER_CST) + abort (); + offset += ((TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (target))) + * TREE_INT_CST_LOW (TREE_OPERAND (target, 1))) + / BITS_PER_UNIT); + target = TREE_OPERAND (target, 0); + } + else + break; + } + + switch (TREE_CODE (target)) + { + case VAR_DECL: + case FUNCTION_DECL: + x = DECL_RTL (target); + break; + + case LABEL_DECL: + x = gen_rtx (MEM, FUNCTION_MODE, + gen_rtx (LABEL_REF, VOIDmode, + label_rtx (TREE_OPERAND (exp, 0)))); + break; + + case REAL_CST: + case STRING_CST: + case COMPLEX_CST: + case CONSTRUCTOR: + x = TREE_CST_RTL (target); + break; + + default: + abort (); + } + + if (GET_CODE (x) != MEM) + abort (); + x = XEXP (x, 0); + + value->base = x; + value->offset = offset; +} + +/* Uniquize all constants that appear in memory. + Each constant in memory thus far output is recorded + in `const_hash_table' with a `struct constant_descriptor' + that contains a polish representation of the value of + the constant. + + We cannot store the trees in the hash table + because the trees may be temporary. */ + +struct constant_descriptor +{ + struct constant_descriptor *next; + char *label; + char contents[1]; +}; + +#define HASHBITS 30 +#define MAX_HASH_TABLE 1009 +static struct constant_descriptor *const_hash_table[MAX_HASH_TABLE]; + +/* Compute a hash code for a constant expression. */ + +int +const_hash (exp) + tree exp; +{ + register char *p; + register int len, hi, i; + register enum tree_code code = TREE_CODE (exp); + + if (code == INTEGER_CST) + { + p = (char *) &TREE_INT_CST_LOW (exp); + len = 2 * sizeof TREE_INT_CST_LOW (exp); + } + else if (code == REAL_CST) + { + p = (char *) &TREE_REAL_CST (exp); + len = sizeof TREE_REAL_CST (exp); + } + else if (code == STRING_CST) + p = TREE_STRING_POINTER (exp), len = TREE_STRING_LENGTH (exp); + else if (code == COMPLEX_CST) + return const_hash (TREE_REALPART (exp)) * 5 + + const_hash (TREE_IMAGPART (exp)); + else if (code == CONSTRUCTOR) + { + register tree link; + + /* For record type, include the type in the hashing. + We do not do so for array types + because (1) the sizes of the elements are sufficient + and (2) distinct array types can have the same constructor. + Instead, we include the array size because the constructor could + be shorter. */ + if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE) + hi = ((HOST_WIDE_INT) TREE_TYPE (exp) & ((1 << HASHBITS) - 1)) + % MAX_HASH_TABLE; + else + hi = ((5 + int_size_in_bytes (TREE_TYPE (exp))) + & ((1 << HASHBITS) - 1)) % MAX_HASH_TABLE; + + for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) + if (TREE_VALUE (link)) + hi = (hi * 603 + const_hash (TREE_VALUE (link))) % MAX_HASH_TABLE; + + return hi; + } + else if (code == ADDR_EXPR) + { + struct addr_const value; + decode_addr_const (exp, &value); + if (GET_CODE (value.base) == SYMBOL_REF) + { + /* Don't hash the address of the SYMBOL_REF; + only use the offset and the symbol name. */ + hi = value.offset; + p = XSTR (value.base, 0); + for (i = 0; p[i] != 0; i++) + hi = ((hi * 613) + (unsigned)(p[i])); + } + else if (GET_CODE (value.base) == LABEL_REF) + hi = value.offset + CODE_LABEL_NUMBER (XEXP (value.base, 0)) * 13; + + hi &= (1 << HASHBITS) - 1; + hi %= MAX_HASH_TABLE; + return hi; + } + else if (code == PLUS_EXPR || code == MINUS_EXPR) + return const_hash (TREE_OPERAND (exp, 0)) * 9 + + const_hash (TREE_OPERAND (exp, 1)); + else if (code == NOP_EXPR || code == CONVERT_EXPR) + return const_hash (TREE_OPERAND (exp, 0)) * 7 + 2; + + /* Compute hashing function */ + hi = len; + for (i = 0; i < len; i++) + hi = ((hi * 613) + (unsigned)(p[i])); + + hi &= (1 << HASHBITS) - 1; + hi %= MAX_HASH_TABLE; + return hi; +} + +/* Compare a constant expression EXP with a constant-descriptor DESC. + Return 1 if DESC describes a constant with the same value as EXP. */ + +static int +compare_constant (exp, desc) + tree exp; + struct constant_descriptor *desc; +{ + return 0 != compare_constant_1 (exp, desc->contents); +} + +/* Compare constant expression EXP with a substring P of a constant descriptor. + If they match, return a pointer to the end of the substring matched. + If they do not match, return 0. + + Since descriptors are written in polish prefix notation, + this function can be used recursively to test one operand of EXP + against a subdescriptor, and if it succeeds it returns the + address of the subdescriptor for the next operand. */ + +static char * +compare_constant_1 (exp, p) + tree exp; + char *p; +{ + register char *strp; + register int len; + register enum tree_code code = TREE_CODE (exp); + + if (code != (enum tree_code) *p++) + return 0; + + if (code == INTEGER_CST) + { + /* Integer constants are the same only if the same width of type. */ + if (*p++ != TYPE_PRECISION (TREE_TYPE (exp))) + return 0; + strp = (char *) &TREE_INT_CST_LOW (exp); + len = 2 * sizeof TREE_INT_CST_LOW (exp); + } + else if (code == REAL_CST) + { + /* Real constants are the same only if the same width of type. */ + if (*p++ != TYPE_PRECISION (TREE_TYPE (exp))) + return 0; + strp = (char *) &TREE_REAL_CST (exp); + len = sizeof TREE_REAL_CST (exp); + } + else if (code == STRING_CST) + { + if (flag_writable_strings) + return 0; + strp = TREE_STRING_POINTER (exp); + len = TREE_STRING_LENGTH (exp); + if (bcmp (&TREE_STRING_LENGTH (exp), p, + sizeof TREE_STRING_LENGTH (exp))) + return 0; + p += sizeof TREE_STRING_LENGTH (exp); + } + else if (code == COMPLEX_CST) + { + p = compare_constant_1 (TREE_REALPART (exp), p); + if (p == 0) return 0; + p = compare_constant_1 (TREE_IMAGPART (exp), p); + return p; + } + else if (code == CONSTRUCTOR) + { + register tree link; + int length = list_length (CONSTRUCTOR_ELTS (exp)); + tree type; + + if (bcmp (&length, p, sizeof length)) + return 0; + p += sizeof length; + + /* For record constructors, insist that the types match. + For arrays, just verify both constructors are for arrays. */ + if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE) + type = TREE_TYPE (exp); + else + type = 0; + if (bcmp (&type, p, sizeof type)) + return 0; + p += sizeof type; + + /* For arrays, insist that the size in bytes match. */ + if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE) + { + int size = int_size_in_bytes (TREE_TYPE (exp)); + if (bcmp (&size, p, sizeof size)) + return 0; + p += sizeof size; + } + + for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) + { + if (TREE_VALUE (link)) + { + if ((p = compare_constant_1 (TREE_VALUE (link), p)) == 0) + return 0; + } + else + { + tree zero = 0; + + if (bcmp (&zero, p, sizeof zero)) + return 0; + p += sizeof zero; + } + } + + return p; + } + else if (code == ADDR_EXPR) + { + struct addr_const value; + decode_addr_const (exp, &value); + strp = (char *) &value.offset; + len = sizeof value.offset; + /* Compare the offset. */ + while (--len >= 0) + if (*p++ != *strp++) + return 0; + /* Compare symbol name. */ + strp = XSTR (value.base, 0); + len = strlen (strp) + 1; + } + else if (code == PLUS_EXPR || code == MINUS_EXPR) + { + p = compare_constant_1 (TREE_OPERAND (exp, 0), p); + if (p == 0) return 0; + p = compare_constant_1 (TREE_OPERAND (exp, 1), p); + return p; + } + else if (code == NOP_EXPR || code == CONVERT_EXPR) + { + p = compare_constant_1 (TREE_OPERAND (exp, 0), p); + return p; + } + + /* Compare constant contents. */ + while (--len >= 0) + if (*p++ != *strp++) + return 0; + + return p; +} + +/* Construct a constant descriptor for the expression EXP. + It is up to the caller to enter the descriptor in the hash table. */ + +static struct constant_descriptor * +record_constant (exp) + tree exp; +{ + struct constant_descriptor *next = 0; + char *label = 0; + + /* Make a struct constant_descriptor. The first two pointers will + be filled in later. Here we just leave space for them. */ + + obstack_grow (&permanent_obstack, (char *) &next, sizeof next); + obstack_grow (&permanent_obstack, (char *) &label, sizeof label); + record_constant_1 (exp); + return (struct constant_descriptor *) obstack_finish (&permanent_obstack); +} + +/* Add a description of constant expression EXP + to the object growing in `permanent_obstack'. + No need to return its address; the caller will get that + from the obstack when the object is complete. */ + +static void +record_constant_1 (exp) + tree exp; +{ + register char *strp; + register int len; + register enum tree_code code = TREE_CODE (exp); + + obstack_1grow (&permanent_obstack, (unsigned int) code); + + if (code == INTEGER_CST) + { + obstack_1grow (&permanent_obstack, TYPE_PRECISION (TREE_TYPE (exp))); + strp = (char *) &TREE_INT_CST_LOW (exp); + len = 2 * sizeof TREE_INT_CST_LOW (exp); + } + else if (code == REAL_CST) + { + obstack_1grow (&permanent_obstack, TYPE_PRECISION (TREE_TYPE (exp))); + strp = (char *) &TREE_REAL_CST (exp); + len = sizeof TREE_REAL_CST (exp); + } + else if (code == STRING_CST) + { + if (flag_writable_strings) + return; + strp = TREE_STRING_POINTER (exp); + len = TREE_STRING_LENGTH (exp); + obstack_grow (&permanent_obstack, (char *) &TREE_STRING_LENGTH (exp), + sizeof TREE_STRING_LENGTH (exp)); + } + else if (code == COMPLEX_CST) + { + record_constant_1 (TREE_REALPART (exp)); + record_constant_1 (TREE_IMAGPART (exp)); + return; + } + else if (code == CONSTRUCTOR) + { + register tree link; + int length = list_length (CONSTRUCTOR_ELTS (exp)); + tree type; + + obstack_grow (&permanent_obstack, (char *) &length, sizeof length); + + /* For record constructors, insist that the types match. + For arrays, just verify both constructors are for arrays. */ + if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE) + type = TREE_TYPE (exp); + else + type = 0; + obstack_grow (&permanent_obstack, (char *) &type, sizeof type); + + /* For arrays, insist that the size in bytes match. */ + if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE) + { + int size = int_size_in_bytes (TREE_TYPE (exp)); + obstack_grow (&permanent_obstack, (char *) &size, sizeof size); + } + + for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) + { + if (TREE_VALUE (link)) + record_constant_1 (TREE_VALUE (link)); + else + { + tree zero = 0; + + obstack_grow (&permanent_obstack, (char *) &zero, sizeof zero); + } + } + + return; + } + else if (code == ADDR_EXPR) + { + struct addr_const value; + decode_addr_const (exp, &value); + /* Record the offset. */ + obstack_grow (&permanent_obstack, + (char *) &value.offset, sizeof value.offset); + /* Record the symbol name. */ + obstack_grow (&permanent_obstack, XSTR (value.base, 0), + strlen (XSTR (value.base, 0)) + 1); + return; + } + else if (code == PLUS_EXPR || code == MINUS_EXPR) + { + record_constant_1 (TREE_OPERAND (exp, 0)); + record_constant_1 (TREE_OPERAND (exp, 1)); + return; + } + else if (code == NOP_EXPR || code == CONVERT_EXPR) + { + record_constant_1 (TREE_OPERAND (exp, 0)); + return; + } + + /* Record constant contents. */ + obstack_grow (&permanent_obstack, strp, len); +} + +/* Return an rtx representing a reference to constant data in memory + for the constant expression EXP. + If assembler code for such a constant has already been output, + return an rtx to refer to it. + Otherwise, output such a constant in memory and generate + an rtx for it. The TREE_CST_RTL of EXP is set up to point to that rtx. + The const_hash_table records which constants already have label strings. */ + +rtx +output_constant_def (exp) + tree exp; +{ + register int hash, align; + register struct constant_descriptor *desc; + char label[256]; + char *found = 0; + int reloc; + register rtx def; + + if (TREE_CODE (exp) == INTEGER_CST) + abort (); /* No TREE_CST_RTL slot in these. */ + + if (TREE_CST_RTL (exp)) + return TREE_CST_RTL (exp); + + /* Make sure any other constants whose addresses appear in EXP + are assigned label numbers. */ + + reloc = output_addressed_constants (exp); + + /* Compute hash code of EXP. Search the descriptors for that hash code + to see if any of them describes EXP. If yes, the descriptor records + the label number already assigned. */ + + hash = const_hash (exp) % MAX_HASH_TABLE; + + for (desc = const_hash_table[hash]; desc; desc = desc->next) + if (compare_constant (exp, desc)) + { + found = desc->label; + break; + } + + if (found == 0) + { + /* No constant equal to EXP is known to have been output. + Make a constant descriptor to enter EXP in the hash table. + Assign the label number and record it in the descriptor for + future calls to this function to find. */ + + /* Create a string containing the label name, in LABEL. */ + ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno); + + desc = record_constant (exp); + desc->next = const_hash_table[hash]; + desc->label + = (char *) obstack_copy0 (&permanent_obstack, label, strlen (label)); + const_hash_table[hash] = desc; + } + + /* We have a symbol name; construct the SYMBOL_REF and the MEM. */ + + push_obstacks_nochange (); + if (TREE_PERMANENT (exp)) + end_temporary_allocation (); + + def = gen_rtx (SYMBOL_REF, Pmode, desc->label); + + TREE_CST_RTL (exp) + = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), def); + RTX_UNCHANGING_P (TREE_CST_RTL (exp)) = 1; + if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE) + MEM_IN_STRUCT_P (TREE_CST_RTL (exp)) = 1; + + pop_obstacks (); + + /* Optionally set flags or add text to the name to record information + such as that it is a function name. If the name is changed, the macro + ASM_OUTPUT_LABELREF will have to know how to strip this information. + And if it finds a * at the beginning after doing so, it must handle + that too. */ +#ifdef ENCODE_SECTION_INFO + ENCODE_SECTION_INFO (exp); +#endif + + if (found == 0) + { + /* Now output assembler code to define that label + and follow it with the data of EXP. */ + + /* First switch to text section, except for writable strings. */ +#ifdef SELECT_SECTION + SELECT_SECTION (exp, reloc); +#else + if (((TREE_CODE (exp) == STRING_CST) && flag_writable_strings) + || (flag_pic && reloc)) + data_section (); + else + readonly_data_section (); +#endif + + /* Align the location counter as required by EXP's data type. */ + align = TYPE_ALIGN (TREE_TYPE (exp)); +#ifdef CONSTANT_ALIGNMENT + align = CONSTANT_ALIGNMENT (exp, align); +#endif + + if (align > BITS_PER_UNIT) + ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); + + /* Output the label itself. */ + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LC", const_labelno); + + /* Output the value of EXP. */ + output_constant (exp, + (TREE_CODE (exp) == STRING_CST + ? TREE_STRING_LENGTH (exp) + : int_size_in_bytes (TREE_TYPE (exp)))); + + ++const_labelno; + } + + return TREE_CST_RTL (exp); +} + +/* Similar hash facility for making memory-constants + from constant rtl-expressions. It is used on RISC machines + where immediate integer arguments and constant addresses are restricted + so that such constants must be stored in memory. + + This pool of constants is reinitialized for each function + so each function gets its own constants-pool that comes right before it. + + All structures allocated here are discarded when functions are saved for + inlining, so they do not need to be allocated permanently. */ + +#define MAX_RTX_HASH_TABLE 61 +static struct constant_descriptor **const_rtx_hash_table; + +/* Structure to represent sufficient information about a constant so that + it can be output when the constant pool is output, so that function + integration can be done, and to simplify handling on machines that reference + constant pool as base+displacement. */ + +struct pool_constant +{ + struct constant_descriptor *desc; + struct pool_constant *next; + enum machine_mode mode; + rtx constant; + int labelno; + int align; + int offset; +}; + +/* Pointers to first and last constant in pool. */ + +static struct pool_constant *first_pool, *last_pool; + +/* Current offset in constant pool (does not include any machine-specific + header. */ + +static int pool_offset; + +/* Structure used to maintain hash table mapping symbols used to their + corresponding constants. */ + +struct pool_sym +{ + char *label; + struct pool_constant *pool; + struct pool_sym *next; +}; + +static struct pool_sym **const_rtx_sym_hash_table; + +/* Hash code for a SYMBOL_REF with CONSTANT_POOL_ADDRESS_P true. + The argument is XSTR (... , 0) */ + +#define SYMHASH(LABEL) \ + ((((HOST_WIDE_INT) (LABEL)) & ((1 << HASHBITS) - 1)) % MAX_RTX_HASH_TABLE) + +/* Initialize constant pool hashing for next function. */ + +void +init_const_rtx_hash_table () +{ + const_rtx_hash_table + = ((struct constant_descriptor **) + oballoc (MAX_RTX_HASH_TABLE * sizeof (struct constant_descriptor *))); + const_rtx_sym_hash_table + = ((struct pool_sym **) + oballoc (MAX_RTX_HASH_TABLE * sizeof (struct pool_sym *))); + bzero (const_rtx_hash_table, + MAX_RTX_HASH_TABLE * sizeof (struct constant_descriptor *)); + bzero (const_rtx_sym_hash_table, + MAX_RTX_HASH_TABLE * sizeof (struct pool_sym *)); + + first_pool = last_pool = 0; + pool_offset = 0; +} + +/* Save and restore it for a nested function. */ + +void +save_varasm_status (p) + struct function *p; +{ + p->const_rtx_hash_table = const_rtx_hash_table; + p->const_rtx_sym_hash_table = const_rtx_sym_hash_table; + p->first_pool = first_pool; + p->last_pool = last_pool; + p->pool_offset = pool_offset; +} + +void +restore_varasm_status (p) + struct function *p; +{ + const_rtx_hash_table = p->const_rtx_hash_table; + const_rtx_sym_hash_table = p->const_rtx_sym_hash_table; + first_pool = p->first_pool; + last_pool = p->last_pool; + pool_offset = p->pool_offset; +} + +enum kind { RTX_DOUBLE, RTX_INT }; + +struct rtx_const +{ +#ifdef ONLY_INT_FIELDS + unsigned int kind : 16; + unsigned int mode : 16; +#else + enum kind kind : 16; + enum machine_mode mode : 16; +#endif + union { + union real_extract du; + struct addr_const addr; + } un; +}; + +/* Express an rtx for a constant integer (perhaps symbolic) + as the sum of a symbol or label plus an explicit integer. + They are stored into VALUE. */ + +static void +decode_rtx_const (mode, x, value) + enum machine_mode mode; + rtx x; + struct rtx_const *value; +{ + /* Clear the whole structure, including any gaps. */ + + { + int *p = (int *) value; + int *end = (int *) (value + 1); + while (p < end) + *p++ = 0; + } + + value->kind = RTX_INT; /* Most usual kind. */ + value->mode = mode; + + switch (GET_CODE (x)) + { + case CONST_DOUBLE: + value->kind = RTX_DOUBLE; + value->mode = GET_MODE (x); + bcopy (&CONST_DOUBLE_LOW (x), &value->un.du, sizeof value->un.du); + break; + + case CONST_INT: + value->un.addr.offset = INTVAL (x); + break; + + case SYMBOL_REF: + case LABEL_REF: + case PC: + value->un.addr.base = x; + break; + + case CONST: + x = XEXP (x, 0); + if (GET_CODE (x) == PLUS) + { + value->un.addr.base = XEXP (x, 0); + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + abort (); + value->un.addr.offset = INTVAL (XEXP (x, 1)); + } + else if (GET_CODE (x) == MINUS) + { + value->un.addr.base = XEXP (x, 0); + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + abort (); + value->un.addr.offset = - INTVAL (XEXP (x, 1)); + } + else + abort (); + break; + + default: + abort (); + } + + if (value->kind == RTX_INT && value->un.addr.base != 0) + switch (GET_CODE (value->un.addr.base)) + { + case SYMBOL_REF: + case LABEL_REF: + /* Use the string's address, not the SYMBOL_REF's address, + for the sake of addresses of library routines. + For a LABEL_REF, compare labels. */ + value->un.addr.base = XEXP (value->un.addr.base, 0); + } +} + +/* Given a MINUS expression, simplify it if both sides + include the same symbol. */ + +rtx +simplify_subtraction (x) + rtx x; +{ + struct rtx_const val0, val1; + + decode_rtx_const (GET_MODE (x), XEXP (x, 0), &val0); + decode_rtx_const (GET_MODE (x), XEXP (x, 1), &val1); + + if (val0.un.addr.base == val1.un.addr.base) + return GEN_INT (val0.un.addr.offset - val1.un.addr.offset); + return x; +} + +/* Compute a hash code for a constant RTL expression. */ + +int +const_hash_rtx (mode, x) + enum machine_mode mode; + rtx x; +{ + register int hi, i; + + struct rtx_const value; + decode_rtx_const (mode, x, &value); + + /* Compute hashing function */ + hi = 0; + for (i = 0; i < sizeof value / sizeof (int); i++) + hi += ((int *) &value)[i]; + + hi &= (1 << HASHBITS) - 1; + hi %= MAX_RTX_HASH_TABLE; + return hi; +} + +/* Compare a constant rtl object X with a constant-descriptor DESC. + Return 1 if DESC describes a constant with the same value as X. */ + +static int +compare_constant_rtx (mode, x, desc) + enum machine_mode mode; + rtx x; + struct constant_descriptor *desc; +{ + register int *p = (int *) desc->contents; + register int *strp; + register int len; + struct rtx_const value; + + decode_rtx_const (mode, x, &value); + strp = (int *) &value; + len = sizeof value / sizeof (int); + + /* Compare constant contents. */ + while (--len >= 0) + if (*p++ != *strp++) + return 0; + + return 1; +} + +/* Construct a constant descriptor for the rtl-expression X. + It is up to the caller to enter the descriptor in the hash table. */ + +static struct constant_descriptor * +record_constant_rtx (mode, x) + enum machine_mode mode; + rtx x; +{ + struct constant_descriptor *ptr; + char *label; + struct rtx_const value; + + decode_rtx_const (mode, x, &value); + + obstack_grow (current_obstack, &ptr, sizeof ptr); + obstack_grow (current_obstack, &label, sizeof label); + + /* Record constant contents. */ + obstack_grow (current_obstack, &value, sizeof value); + + return (struct constant_descriptor *) obstack_finish (current_obstack); +} + +/* Given a constant rtx X, make (or find) a memory constant for its value + and return a MEM rtx to refer to it in memory. */ + +rtx +force_const_mem (mode, x) + enum machine_mode mode; + rtx x; +{ + register int hash; + register struct constant_descriptor *desc; + char label[256]; + char *found = 0; + rtx def; + + /* If we want this CONST_DOUBLE in the same mode as it is in memory + (this will always be true for floating CONST_DOUBLEs that have been + placed in memory, but not for VOIDmode (integer) CONST_DOUBLEs), + use the previous copy. Otherwise, make a new one. Note that in + the unlikely event that this same CONST_DOUBLE is used in two different + modes in an alternating fashion, we will allocate a lot of different + memory locations, but this should be extremely rare. */ + + /* Don't use CONST_DOUBLE_MEM in a nested function. + Nested functions have their own constant pools, + so they can't share the same values in CONST_DOUBLE_MEM + with the containing function. */ + if (outer_function_chain == 0) + if (GET_CODE (x) == CONST_DOUBLE + && GET_CODE (CONST_DOUBLE_MEM (x)) == MEM + && GET_MODE (CONST_DOUBLE_MEM (x)) == mode) + return CONST_DOUBLE_MEM (x); + + /* Compute hash code of X. Search the descriptors for that hash code + to see if any of them describes X. If yes, the descriptor records + the label number already assigned. */ + + hash = const_hash_rtx (mode, x); + + for (desc = const_rtx_hash_table[hash]; desc; desc = desc->next) + if (compare_constant_rtx (mode, x, desc)) + { + found = desc->label; + break; + } + + if (found == 0) + { + register struct pool_constant *pool; + register struct pool_sym *sym; + int align; + + /* No constant equal to X is known to have been output. + Make a constant descriptor to enter X in the hash table. + Assign the label number and record it in the descriptor for + future calls to this function to find. */ + + desc = record_constant_rtx (mode, x); + desc->next = const_rtx_hash_table[hash]; + const_rtx_hash_table[hash] = desc; + + /* Align the location counter as required by EXP's data type. */ + align = (mode == VOIDmode) ? UNITS_PER_WORD : GET_MODE_SIZE (mode); + if (align > BIGGEST_ALIGNMENT / BITS_PER_UNIT) + align = BIGGEST_ALIGNMENT / BITS_PER_UNIT; + + pool_offset += align - 1; + pool_offset &= ~ (align - 1); + + /* Allocate a pool constant descriptor, fill it in, and chain it in. */ + + pool = (struct pool_constant *) oballoc (sizeof (struct pool_constant)); + pool->desc = desc; + pool->constant = x; + pool->mode = mode; + pool->labelno = const_labelno; + pool->align = align; + pool->offset = pool_offset; + pool->next = 0; + + if (last_pool == 0) + first_pool = pool; + else + last_pool->next = pool; + + last_pool = pool; + pool_offset += GET_MODE_SIZE (mode); + + /* Create a string containing the label name, in LABEL. */ + ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno); + + ++const_labelno; + + desc->label = found + = (char *) obstack_copy0 (saveable_obstack, label, strlen (label)); + + /* Add label to symbol hash table. */ + hash = SYMHASH (found); + sym = (struct pool_sym *) oballoc (sizeof (struct pool_sym)); + sym->label = found; + sym->pool = pool; + sym->next = const_rtx_sym_hash_table[hash]; + const_rtx_sym_hash_table[hash] = sym; + } + + /* We have a symbol name; construct the SYMBOL_REF and the MEM. */ + + def = gen_rtx (MEM, mode, gen_rtx (SYMBOL_REF, Pmode, found)); + + RTX_UNCHANGING_P (def) = 1; + /* Mark the symbol_ref as belonging to this constants pool. */ + CONSTANT_POOL_ADDRESS_P (XEXP (def, 0)) = 1; + current_function_uses_const_pool = 1; + + if (outer_function_chain == 0) + if (GET_CODE (x) == CONST_DOUBLE) + { + if (CONST_DOUBLE_MEM (x) == cc0_rtx) + { + CONST_DOUBLE_CHAIN (x) = const_double_chain; + const_double_chain = x; + } + CONST_DOUBLE_MEM (x) = def; + } + + return def; +} + +/* Given a SYMBOL_REF with CONSTANT_POOL_ADDRESS_P true, return a pointer to + the corresponding pool_constant structure. */ + +static struct pool_constant * +find_pool_constant (addr) + rtx addr; +{ + struct pool_sym *sym; + char *label = XSTR (addr, 0); + + for (sym = const_rtx_sym_hash_table[SYMHASH (label)]; sym; sym = sym->next) + if (sym->label == label) + return sym->pool; + + abort (); +} + +/* Given a constant pool SYMBOL_REF, return the corresponding constant. */ + +rtx +get_pool_constant (addr) + rtx addr; +{ + return (find_pool_constant (addr))->constant; +} + +/* Similar, return the mode. */ + +enum machine_mode +get_pool_mode (addr) + rtx addr; +{ + return (find_pool_constant (addr))->mode; +} + +/* Similar, return the offset in the constant pool. */ + +int +get_pool_offset (addr) + rtx addr; +{ + return (find_pool_constant (addr))->offset; +} + +/* Return the size of the constant pool. */ + +int +get_pool_size () +{ + return pool_offset; +} + +/* Write all the constants in the constant pool. */ + +void +output_constant_pool (fnname, fndecl) + char *fnname; + tree fndecl; +{ + struct pool_constant *pool; + rtx x; + union real_extract u; + +#ifdef ASM_OUTPUT_POOL_PROLOGUE + ASM_OUTPUT_POOL_PROLOGUE (asm_out_file, fnname, fndecl, pool_offset); +#endif + + for (pool = first_pool; pool; pool = pool->next) + { + x = pool->constant; + + /* See if X is a LABEL_REF (or a CONST referring to a LABEL_REF) + whose CODE_LABEL has been deleted. This can occur if a jump table + is eliminated by optimization. If so, write a constant of zero + instead. Note that this can also happen by turning the + CODE_LABEL into a NOTE. */ + if (((GET_CODE (x) == LABEL_REF + && (INSN_DELETED_P (XEXP (x, 0)) + || GET_CODE (XEXP (x, 0)) == NOTE))) + || (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 0)) == LABEL_REF + && (INSN_DELETED_P (XEXP (XEXP (XEXP (x, 0), 0), 0)) + || GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == NOTE))) + x = const0_rtx; + + /* First switch to correct section. */ +#ifdef SELECT_RTX_SECTION + SELECT_RTX_SECTION (pool->mode, x); +#else + readonly_data_section (); +#endif + +#ifdef ASM_OUTPUT_SPECIAL_POOL_ENTRY + ASM_OUTPUT_SPECIAL_POOL_ENTRY (asm_out_file, x, pool->mode, + pool->align, pool->labelno, done); +#endif + + if (pool->align > 1) + ASM_OUTPUT_ALIGN (asm_out_file, exact_log2 (pool->align)); + + /* Output the label. */ + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LC", pool->labelno); + + /* Output the value of the constant itself. */ + switch (GET_MODE_CLASS (pool->mode)) + { + case MODE_FLOAT: + if (GET_CODE (x) != CONST_DOUBLE) + abort (); + + bcopy (&CONST_DOUBLE_LOW (x), &u, sizeof u); + assemble_real (u.d, pool->mode); + break; + + case MODE_INT: + case MODE_PARTIAL_INT: + assemble_integer (x, GET_MODE_SIZE (pool->mode), 1); + break; + + default: + abort (); + } + + done: ; + } + + /* Done with this pool. */ + first_pool = last_pool = 0; +} + +/* Find all the constants whose addresses are referenced inside of EXP, + and make sure assembler code with a label has been output for each one. + Indicate whether an ADDR_EXPR has been encountered. */ + +int +output_addressed_constants (exp) + tree exp; +{ + int reloc = 0; + + switch (TREE_CODE (exp)) + { + case ADDR_EXPR: + { + register tree constant = TREE_OPERAND (exp, 0); + + while (TREE_CODE (constant) == COMPONENT_REF) + { + constant = TREE_OPERAND (constant, 0); + } + + if (TREE_CODE_CLASS (TREE_CODE (constant)) == 'c' + || TREE_CODE (constant) == CONSTRUCTOR) + /* No need to do anything here + for addresses of variables or functions. */ + output_constant_def (constant); + } + reloc = 1; + break; + + case PLUS_EXPR: + case MINUS_EXPR: + reloc = output_addressed_constants (TREE_OPERAND (exp, 0)); + reloc |= output_addressed_constants (TREE_OPERAND (exp, 1)); + break; + + case NOP_EXPR: + case CONVERT_EXPR: + case NON_LVALUE_EXPR: + reloc = output_addressed_constants (TREE_OPERAND (exp, 0)); + break; + + case CONSTRUCTOR: + { + register tree link; + for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) + if (TREE_VALUE (link) != 0) + reloc |= output_addressed_constants (TREE_VALUE (link)); + } + break; + + case ERROR_MARK: + break; + } + return reloc; +} + +/* Output assembler code for constant EXP to FILE, with no label. + This includes the pseudo-op such as ".int" or ".byte", and a newline. + Assumes output_addressed_constants has been done on EXP already. + + Generate exactly SIZE bytes of assembler data, padding at the end + with zeros if necessary. SIZE must always be specified. + + SIZE is important for structure constructors, + since trailing members may have been omitted from the constructor. + It is also important for initialization of arrays from string constants + since the full length of the string constant might not be wanted. + It is also needed for initialization of unions, where the initializer's + type is just one member, and that may not be as long as the union. + + There a case in which we would fail to output exactly SIZE bytes: + for a structure constructor that wants to produce more than SIZE bytes. + But such constructors will never be generated for any possible input. */ + +void +output_constant (exp, size) + register tree exp; + register int size; +{ + register enum tree_code code = TREE_CODE (TREE_TYPE (exp)); + rtx x; + + if (size == 0) + return; + + /* Allow a constructor with no elements for any data type. + This means to fill the space with zeros. */ + if (TREE_CODE (exp) == CONSTRUCTOR && CONSTRUCTOR_ELTS (exp) == 0) + { + assemble_zeros (size); + return; + } + + /* Eliminate the NOP_EXPR that makes a cast not be an lvalue. + That way we get the constant (we hope) inside it. */ + if (TREE_CODE (exp) == NOP_EXPR + && TREE_TYPE (exp) == TREE_TYPE (TREE_OPERAND (exp, 0))) + exp = TREE_OPERAND (exp, 0); + + switch (code) + { + case CHAR_TYPE: + case BOOLEAN_TYPE: + case INTEGER_TYPE: + case ENUMERAL_TYPE: + case POINTER_TYPE: + case REFERENCE_TYPE: + /* ??? What about (int)((float)(int)&foo + 4) */ + while (TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR + || TREE_CODE (exp) == NON_LVALUE_EXPR) + exp = TREE_OPERAND (exp, 0); + + if (! assemble_integer (expand_expr (exp, NULL_RTX, VOIDmode, + EXPAND_INITIALIZER), + size, 0)) + error ("initializer for integer value is too complicated"); + size = 0; + break; + + case REAL_TYPE: + if (TREE_CODE (exp) != REAL_CST) + error ("initializer for floating value is not a floating constant"); + + assemble_real (TREE_REAL_CST (exp), + mode_for_size (size * BITS_PER_UNIT, MODE_FLOAT, 0)); + size = 0; + break; + + case COMPLEX_TYPE: + output_constant (TREE_REALPART (exp), size / 2); + output_constant (TREE_IMAGPART (exp), size / 2); + size -= (size / 2) * 2; + break; + + case ARRAY_TYPE: + if (TREE_CODE (exp) == CONSTRUCTOR) + { + output_constructor (exp, size); + return; + } + else if (TREE_CODE (exp) == STRING_CST) + { + int excess = 0; + + if (size > TREE_STRING_LENGTH (exp)) + { + excess = size - TREE_STRING_LENGTH (exp); + size = TREE_STRING_LENGTH (exp); + } + + assemble_string (TREE_STRING_POINTER (exp), size); + size = excess; + } + else + abort (); + break; + + case RECORD_TYPE: + case UNION_TYPE: + if (TREE_CODE (exp) == CONSTRUCTOR) + output_constructor (exp, size); + else + abort (); + return; + } + + if (size > 0) + assemble_zeros (size); +} + +/* Subroutine of output_constant, used for CONSTRUCTORs + (aggregate constants). + Generate at least SIZE bytes, padding if necessary. */ + +void +output_constructor (exp, size) + tree exp; + int size; +{ + register tree link, field = 0; + /* Number of bytes output or skipped so far. + In other words, current position within the constructor. */ + int total_bytes = 0; + /* Non-zero means BYTE contains part of a byte, to be output. */ + int byte_buffer_in_use = 0; + register int byte; + + if (HOST_BITS_PER_WIDE_INT < BITS_PER_UNIT) + abort (); + + if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE) + field = TYPE_FIELDS (TREE_TYPE (exp)); + + /* As LINK goes through the elements of the constant, + FIELD goes through the structure fields, if the constant is a structure. + if the constant is a union, then we override this, + by getting the field from the TREE_LIST element. + But the constant could also be an array. Then FIELD is zero. */ + for (link = CONSTRUCTOR_ELTS (exp); + link; + link = TREE_CHAIN (link), + field = field ? TREE_CHAIN (field) : 0) + { + tree val = TREE_VALUE (link); + /* the element in a union constructor specifies the proper field. */ + if (TREE_PURPOSE (link) != 0) + field = TREE_PURPOSE (link); + + /* Eliminate the marker that makes a cast not be an lvalue. */ + if (val != 0) + STRIP_NOPS (val); + + if (field == 0 || !DECL_BIT_FIELD (field)) + { + register int fieldsize; + /* Since this structure is static, + we know the positions are constant. */ + int bitpos = (field ? (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)) + / BITS_PER_UNIT) + : 0); + + /* An element that is not a bit-field. + Output any buffered-up bit-fields preceding it. */ + if (byte_buffer_in_use) + { + ASM_OUTPUT_BYTE (asm_out_file, byte); + total_bytes++; + byte_buffer_in_use = 0; + } + + /* Advance to offset of this element. + Note no alignment needed in an array, since that is guaranteed + if each element has the proper size. */ + if (field != 0 && bitpos != total_bytes) + { + assemble_zeros (bitpos - total_bytes); + total_bytes = bitpos; + } + + /* Determine size this element should occupy. */ + if (field) + { + if (TREE_CODE (DECL_SIZE (field)) != INTEGER_CST) + abort (); + if (TREE_INT_CST_LOW (DECL_SIZE (field)) > 100000) + { + /* This avoids overflow trouble. */ + tree size_tree = size_binop (CEIL_DIV_EXPR, + DECL_SIZE (field), + size_int (BITS_PER_UNIT)); + fieldsize = TREE_INT_CST_LOW (size_tree); + } + else + { + fieldsize = TREE_INT_CST_LOW (DECL_SIZE (field)); + fieldsize = (fieldsize + BITS_PER_UNIT - 1) / BITS_PER_UNIT; + } + } + else + fieldsize = int_size_in_bytes (TREE_TYPE (TREE_TYPE (exp))); + + /* Output the element's initial value. */ + if (val == 0) + assemble_zeros (fieldsize); + else + output_constant (val, fieldsize); + + /* Count its size. */ + total_bytes += fieldsize; + } + else if (val != 0 && TREE_CODE (val) != INTEGER_CST) + error ("invalid initial value for member `%s'", + IDENTIFIER_POINTER (DECL_NAME (field))); + else + { + /* Element that is a bit-field. */ + + int next_offset = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)); + int end_offset + = (next_offset + TREE_INT_CST_LOW (DECL_SIZE (field))); + + if (val == 0) + val = integer_zero_node; + + /* If this field does not start in this (or, next) byte, + skip some bytes. */ + if (next_offset / BITS_PER_UNIT != total_bytes) + { + /* Output remnant of any bit field in previous bytes. */ + if (byte_buffer_in_use) + { + ASM_OUTPUT_BYTE (asm_out_file, byte); + total_bytes++; + byte_buffer_in_use = 0; + } + + /* If still not at proper byte, advance to there. */ + if (next_offset / BITS_PER_UNIT != total_bytes) + { + assemble_zeros (next_offset / BITS_PER_UNIT - total_bytes); + total_bytes = next_offset / BITS_PER_UNIT; + } + } + + if (! byte_buffer_in_use) + byte = 0; + + /* We must split the element into pieces that fall within + separate bytes, and combine each byte with previous or + following bit-fields. */ + + /* next_offset is the offset n fbits from the beginning of + the structure to the next bit of this element to be processed. + end_offset is the offset of the first bit past the end of + this element. */ + while (next_offset < end_offset) + { + int this_time; + int shift, value; + int next_byte = next_offset / BITS_PER_UNIT; + int next_bit = next_offset % BITS_PER_UNIT; + + /* Advance from byte to byte + within this element when necessary. */ + while (next_byte != total_bytes) + { + ASM_OUTPUT_BYTE (asm_out_file, byte); + total_bytes++; + byte = 0; + } + + /* Number of bits we can process at once + (all part of the same byte). */ + this_time = MIN (end_offset - next_offset, + BITS_PER_UNIT - next_bit); +#if BYTES_BIG_ENDIAN + /* On big-endian machine, take the most significant bits + first (of the bits that are significant) + and put them into bytes from the most significant end. */ + shift = end_offset - next_offset - this_time; + /* Don't try to take a bunch of bits that cross + the word boundary in the INTEGER_CST. */ + if (shift < HOST_BITS_PER_WIDE_INT + && shift + this_time > HOST_BITS_PER_WIDE_INT) + { + this_time -= (HOST_BITS_PER_WIDE_INT - shift); + shift = HOST_BITS_PER_WIDE_INT; + } + + /* Now get the bits from the appropriate constant word. */ + if (shift < HOST_BITS_PER_WIDE_INT) + { + value = TREE_INT_CST_LOW (val); + } + else if (shift < 2 * HOST_BITS_PER_WIDE_INT) + { + value = TREE_INT_CST_HIGH (val); + shift -= HOST_BITS_PER_WIDE_INT; + } + else + abort (); + byte |= (((value >> shift) + & (((HOST_WIDE_INT) 1 << this_time) - 1)) + << (BITS_PER_UNIT - this_time - next_bit)); +#else + /* On little-endian machines, + take first the least significant bits of the value + and pack them starting at the least significant + bits of the bytes. */ + shift = (next_offset + - TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field))); + /* Don't try to take a bunch of bits that cross + the word boundary in the INTEGER_CST. */ + if (shift < HOST_BITS_PER_WIDE_INT + && shift + this_time > HOST_BITS_PER_WIDE_INT) + { + this_time -= (HOST_BITS_PER_WIDE_INT - shift); + shift = HOST_BITS_PER_WIDE_INT; + } + + /* Now get the bits from the appropriate constant word. */ + if (shift < HOST_BITS_PER_INT) + value = TREE_INT_CST_LOW (val); + else if (shift < 2 * HOST_BITS_PER_WIDE_INT) + { + value = TREE_INT_CST_HIGH (val); + shift -= HOST_BITS_PER_WIDE_INT; + } + else + abort (); + byte |= ((value >> shift) + & (((HOST_WIDE_INT) 1 << this_time) - 1)) << next_bit; +#endif + next_offset += this_time; + byte_buffer_in_use = 1; + } + } + } + if (byte_buffer_in_use) + { + ASM_OUTPUT_BYTE (asm_out_file, byte); + total_bytes++; + } + if (total_bytes < size) + assemble_zeros (size - total_bytes); +} diff --git a/gnu/usr.bin/cc/lib/version.c b/gnu/usr.bin/cc/lib/version.c new file mode 100644 index 0000000000..47bd2c0787 --- /dev/null +++ b/gnu/usr.bin/cc/lib/version.c @@ -0,0 +1 @@ +char *version_string = "2.4.5"; diff --git a/gnu/usr.bin/cc/common/xcoffout.c b/gnu/usr.bin/cc/lib/xcoffout.c similarity index 100% rename from gnu/usr.bin/cc/common/xcoffout.c rename to gnu/usr.bin/cc/lib/xcoffout.c diff --git a/gnu/usr.bin/cc/libgcc/Makefile b/gnu/usr.bin/cc/libgcc/Makefile index db0b4a0d7f..e6c44dd8a9 100644 --- a/gnu/usr.bin/cc/libgcc/Makefile +++ b/gnu/usr.bin/cc/libgcc/Makefile @@ -1,5 +1,5 @@ LIB= gcc -CFLAGS= -I../common +CC= gcc SRCS= _mulsi3.c _udivsi3.c _divsi3.c _umodsi3.c _modsi3.c \ _lshrsi3.c _lshlsi3.c _ashrsi3.c _ashlsi3.c \ @@ -48,7 +48,7 @@ libgcc1: for name in $(LIB1FUNCS); \ do \ echo $${name}; \ - gcc -E -I../common -DL$${name} libgcc1.c | \ + gcc -E -I../lib -DL$${name} libgcc1.c | \ sed -f clean.sed >$${name}.c; \ done @@ -57,6 +57,6 @@ libgcc2: for name in $(LIB2FUNCS); \ do \ echo $${name}; \ - gcc -E -I../common -DL$${name} libgcc2.c | \ + gcc -E -I../lib -DL$${name} libgcc2.c | \ sed -f clean.sed >$${name}.c; \ done diff --git a/gnu/usr.bin/cc/libgcc/_eprintf.c b/gnu/usr.bin/cc/libgcc/_eprintf.c index f285c82381..b3ef209b6e 100644 --- a/gnu/usr.bin/cc/libgcc/_eprintf.c +++ b/gnu/usr.bin/cc/libgcc/_eprintf.c @@ -82,9 +82,6 @@ typedef union } DIunion; extern DItype __fixunssfdi (SFtype a); extern DItype __fixunsdfdi (DFtype a); -typedef void *__gnuc_va_list; -typedef size_t; - typedef long fpos_t; struct __sbuf { unsigned char *_base; @@ -159,9 +156,9 @@ int sscanf (char *, const char *, ...) ; FILE *tmpfile (void) ; char *tmpnam (char *) ; int ungetc (int, FILE *) ; -int vfprintf (FILE *, const char *, __gnuc_va_list) ; -int vprintf (const char *, __gnuc_va_list) ; -int vsprintf (char *, const char *, __gnuc_va_list) ; +int vfprintf (FILE *, const char *, char * ) ; +int vprintf (const char *, char * ) ; +int vsprintf (char *, const char *, char * ) ; char *ctermid (char *) ; FILE *fdopen (int, const char *) ; int fileno (FILE *) ; @@ -175,18 +172,18 @@ void setbuffer (FILE *, char *, int) ; int setlinebuf (FILE *) ; char *tempnam (const char *, const char *) ; int snprintf (char *, size_t, const char *, ...) ; -int vsnprintf (char *, size_t, const char *, __gnuc_va_list) ; -int vscanf (const char *, __gnuc_va_list) ; -int vsscanf (const char *, const char *, __gnuc_va_list) ; +int vsnprintf (char *, size_t, const char *, char * ) ; +int vscanf (const char *, char * ) ; +int vsscanf (const char *, const char *, char * ) ; FILE *funopen (const void *, int (*)(void *, char *, int), int (*)(void *, const char *, int), fpos_t (*)(void *, fpos_t, int), int (*)(void *)) ; int __srget (FILE *) ; -int __svfscanf (FILE *, const char *, __gnuc_va_list) ; +int __svfscanf (FILE *, const char *, char * ) ; int __swbuf (int, FILE *) ; -static inline int __sputc(int _c, FILE *_p) { +static __inline__ int __sputc(int _c, FILE *_p) { if (--_p->_w >= 0 || (_p->_w >= _p->_lbfsize && (char)_c != '\n')) return (*_p->_p++ = _c); else diff --git a/gnu/usr.bin/cc/libgcc/_new_handler.c b/gnu/usr.bin/cc/libgcc/_new_handler.c index b9e5c88b8d..b14f84ee84 100644 --- a/gnu/usr.bin/cc/libgcc/_new_handler.c +++ b/gnu/usr.bin/cc/libgcc/_new_handler.c @@ -82,9 +82,6 @@ typedef union } DIunion; extern DItype __fixunssfdi (SFtype a); extern DItype __fixunsdfdi (DFtype a); -typedef void *__gnuc_va_list; -typedef size_t; - typedef long fpos_t; struct __sbuf { unsigned char *_base; @@ -159,9 +156,9 @@ int sscanf (char *, const char *, ...) ; FILE *tmpfile (void) ; char *tmpnam (char *) ; int ungetc (int, FILE *) ; -int vfprintf (FILE *, const char *, __gnuc_va_list) ; -int vprintf (const char *, __gnuc_va_list) ; -int vsprintf (char *, const char *, __gnuc_va_list) ; +int vfprintf (FILE *, const char *, char * ) ; +int vprintf (const char *, char * ) ; +int vsprintf (char *, const char *, char * ) ; char *ctermid (char *) ; FILE *fdopen (int, const char *) ; int fileno (FILE *) ; @@ -175,18 +172,18 @@ void setbuffer (FILE *, char *, int) ; int setlinebuf (FILE *) ; char *tempnam (const char *, const char *) ; int snprintf (char *, size_t, const char *, ...) ; -int vsnprintf (char *, size_t, const char *, __gnuc_va_list) ; -int vscanf (const char *, __gnuc_va_list) ; -int vsscanf (const char *, const char *, __gnuc_va_list) ; +int vsnprintf (char *, size_t, const char *, char * ) ; +int vscanf (const char *, char * ) ; +int vsscanf (const char *, const char *, char * ) ; FILE *funopen (const void *, int (*)(void *, char *, int), int (*)(void *, const char *, int), fpos_t (*)(void *, fpos_t, int), int (*)(void *)) ; int __srget (FILE *) ; -int __svfscanf (FILE *, const char *, __gnuc_va_list) ; +int __svfscanf (FILE *, const char *, char * ) ; int __swbuf (int, FILE *) ; -static inline int __sputc(int _c, FILE *_p) { +static __inline__ int __sputc(int _c, FILE *_p) { if (--_p->_w >= 0 || (_p->_w >= _p->_lbfsize && (char)_c != '\n')) return (*_p->_p++ = _c); else diff --git a/gnu/usr.bin/cc/libobjc/Makefile b/gnu/usr.bin/cc/libobjc/Makefile index 879413863a..05bba2f27e 100644 --- a/gnu/usr.bin/cc/libobjc/Makefile +++ b/gnu/usr.bin/cc/libobjc/Makefile @@ -1,11 +1,95 @@ -LIB= objc -SRCS= hash.c sarray.c class.c sendmsg.c init.c archive.c\ - selector.c objects.c misc.c Object.m Protocol.m -CFLAGS= -I$(.CURDIR)/../common -I$(.CURDIR) -I.. -.include +# GNU Objective C Runtime Makefile +# Copyright (C) 1993 Free Software Foundation, Inc. +# +# This file is part of GNU CC. +# +# GNU CC is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2, or (at your option) any later version. +# +# GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# GNU CC; see the file COPYING. If not, write to the Free Software +# Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +# This makefile is run by the parent dir's makefile. +# thisdir1=`pwd`; \ +# srcdir1=`cd $(srcdir); pwd`; \ +# cd objc; \ +# $(MAKE) $(MAKEFLAGS) -f $$srcdir1/objc/Makefile libobjc.a \ +# srcdir=$$srcdir1 tooldir=$(tooldir) AR="$(AR)" AR_FLAGS="$(AR_FLAGS)" \ +# GCC_FOR_TARGET="$$thisdir1/xgcc -B$$thisdir1/" \ +# GCC_CFLAGS="$(GCC_CFLAGS)" incinstalldir=$$thisdir1/include +# Two targets are used by ../Makefile: `all' and `mostlyclean'. + .SUFFIXES: .m +OPTIMIZE= -O + +VPATH = $(srcdir)/objc + +AR = ar +AR_FLAGS = rc + +# Always search these dirs when compiling. +SUBDIR_INCLUDES = -I. -I.. -I$(srcdir) -I$(srcdir)/config + +.c.o: + $(GCC_FOR_TARGET) $(OPTIMIZE) \ + -c $(GCC_CFLAGS) $(SUBDIR_INCLUDES) $< + .m.o: - $(CC) -c $(CFLAGS) $< -.m.po: - $(CC) -p -c $(CFLAGS) -o ${.TARGET} $< + $(GCC_FOR_TARGET) $(OPTIMIZE) -fgnu-runtime \ + -c $(GCC_CFLAGS) $(SUBDIR_INCLUDES) $< + +# If we were not invoked from the parent dir, +# invoke make in the parent dir and have reinvoke this makefile. +# That's necessary to get the right values for srcdir, etc. +all: + cd ..; $(MAKE) sublibobjc.a + +OBJC_O = hash.o sarray.o class.o sendmsg.o init.o archive.o \ + selector.o objects.o misc.o Object.o Protocol.o + +libobjc.a: $(OBJC_O) + -rm -f libobjc.a + $(AR) rc libobjc.a $(OBJC_O) +# ranlib is run in the parent directory's makefile. + +OBJC_H = hash.h list.h sarray.h objc.h \ + objc-api.h \ + Object.h Protocol.h mutex.h \ + typedstream.h + +# copy objc headers to installation include directory +copy-headers: + -rm -fr $(incinstalldir)/objc + -mkdir $(incinstalldir)/objc + for file in $(OBJC_H); do \ + realfile=$(srcdir)/objc/$$file; \ + cp $$realfile $(incinstalldir)/objc; \ + chmod a+r $(incinstalldir)/objc/$$file; \ + done + +sendmsg.o: sendmsg.c fflags + $(GCC_FOR_TARGET) `cat fflags` -c $(GCC_CFLAGS) $(SUBDIR_INCLUDES) $(srcdir)/objc/sendmsg.c + +## Next to are for heuristics on forwarding mechanism... +xforward: xforward.c + -$(GCC_FOR_TARGET) -c $(GCC_CFLAGS) $(SUBDIR_INCLUDES) $(srcdir)/objc/xforward.c + -$(GCC_FOR_TARGET) ./xforward.o -o xforward + if [ ! -f ./xforward ]; then touch ./xforward; else true; fi + +fflags: xforward + -rm -f fflags + -if [ -s ./xforward ]; then ./xforward > fflags; else touch fflags; fi + +mostlyclean: + -rm -f *.o libobjc.a xforward fflags +clean: mostlyclean +distclean: mostlyclean +extraclean: mostlyclean diff --git a/gnu/usr.bin/cc/libobjc/sendmsg.c b/gnu/usr.bin/cc/libobjc/sendmsg.c index c7947ecbf8..e2c52ba4c7 100644 --- a/gnu/usr.bin/cc/libobjc/sendmsg.c +++ b/gnu/usr.bin/cc/libobjc/sendmsg.c @@ -97,7 +97,8 @@ objc_msg_sendv(id object, SEL op, size_t frame_size, arglist_t arg_frame) frame_size); #else #warning performv:: will not work - (*_objc_error)(object, "objc_msg_sendv (performv::) not supported\n", 0); + va_list nothing; + (*_objc_error)(object, "objc_msg_sendv (performv::) not supported\n", nothing); return 0; #endif } diff --git a/gnu/usr.bin/cpio/COPYING b/gnu/usr.bin/cpio/COPYING new file mode 100644 index 0000000000..a43ea2126f --- /dev/null +++ b/gnu/usr.bin/cpio/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/gnu/usr.bin/cpio/COPYING.LIB b/gnu/usr.bin/cpio/COPYING.LIB new file mode 100644 index 0000000000..eb685a5ec9 --- /dev/null +++ b/gnu/usr.bin/cpio/COPYING.LIB @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/gnu/usr.bin/cpio/ChangeLog b/gnu/usr.bin/cpio/ChangeLog new file mode 100644 index 0000000000..9b18d4f61b --- /dev/null +++ b/gnu/usr.bin/cpio/ChangeLog @@ -0,0 +1,781 @@ +Mon Jul 5 14:54:08 1993 John Oleynick (juo@spiff.gnu.ai.mit.edu) + + * cpio.1: Updated man page for 2.3. + * Makefile.in: Create distribution with .gz extension, instead of .z. + +Tue Jun 29 18:54:37 1993 John Oleynick (juo@goldman.gnu.ai.mit.edu) + + * Makefile.in: Added installdirs target (using mkinstalldirs). + * Added mkinstalldirs script. + * main.c, mt.c: Added --help option. Changed usage() to + take a stream and exit value (so --help can print on stdout + and return a 0 exit status). + * extern.h: Removed usage()'s prototype (it was out of date, + and only used in main.c). + +Thu May 6 00:22:22 1993 John Oleynick (juo@hal.gnu.ai.mit.edu) + + * cpio.1: Added hpbin and hpodc. + +Tue May 4 00:32:29 1993 John Oleynick (juo@hal.gnu.ai.mit.edu) + + * copyin.c (process_copy_in), copypass.c (process_copy_pass): When + deleting an existing file, if the file is a directory, use rmdir() + instead of unlink(). + +Thu Apr 29 14:43:56 1993 John Oleynick (juo@goldman.gnu.ai.mit.edu) + + * tar.c (read_in_tar_header): Clear non-protection bits from + mode, in case tar has left some device bits in there. + +Wed Apr 28 10:36:53 1993 John Oleynick (juo@goldman.gnu.ai.mit.edu) + + * util.c: Added code to try and work around broken tape drivers + that have problems with tapes > 2Gb. + + * copyout.c (process_copy_out): Pass file_hdr to + writeout_other_defers() and add_link_defer() by reference, + not by value. + + * copyin.c (process_copy_in): Pass file_hdr to defer_copyin() + and create_defered_links() by reference, not by value. + + * defer.c: include (to build on BSD 4.3 on HP300) + +Fri Apr 16 18:01:17 1993 John Oleynick (juo@goldman.gnu.ai.mit.edu) + + * mt.c, util.c: Include if HAVE_SYS_MTIO_H is + defined, not HAVE_MTIO_H. + +Wed Apr 14 17:37:46 1993 John Oleynick (juo@goldman.gnu.ai.mit.edu) + + * util.c: Include if HAVE_SYS_IO_TRIOCTL_H + is defined. + + * mt.c: Only include if HAVE_SYS_MTIO_H is defined. + +Fri Apr 2 13:09:11 1993 John Oleynick (juo@goldman.gnu.ai.mit.edu) + + * configure.in: Added fnmatch to AC_REPLACE_FUNCS. Added + sys/io/trioctl.h to AC_HAVE_HEADERS. + + * Makefile.in: Removed fnmatch.o from OBJS. + + * copyin.c: Only include "fnmatch.h" if FNM_PATHNAME isn't + defined yet. + + * mt.c: Include if HAVE_SYS_IO_TRIOCTL_H is + defined. + +Mon Mar 29 17:04:06 1993 John Oleynick (juo@hal.gnu.ai.mit.edu) + + * Many changes for supporting HPUX Context Dependent Files; + also some bug fixes to fix problems with multiply (hard) linked + device files; minor changes to support HPUX format archives + (slightly broken?) System V.4 posix tar archives and HPUX + posix tar archives. + + * Makefile.in: New files defer.o, defer,c and defer.h; added + -DSYMLINK_USES_UMASK and -DHPUX_CDF comments; changed dist rule + to use gzip with tar, instead of compress. + + * copyin.c: changes for new arf_hpbinary and arf_hpascii formats; + HPUX CDF's; DEBUG_CPIO; fixes to properly handle multiple + links in newc and crc format archives (new routines defer_copyin(), + create_defered_links(), create_final_defers()); move most + multiple (hard) link code to new routines link_name() and + link_to_maj_min_ino(); use new macro UMASKED_SYMLINK instead of + symlink(). + + * copyout.c: fixes to properly handle multiple links in newc + and crc format archives (new routines last_link(), + count_defered_links_to_dev_ino(), add_link_defer(), + writeout_other_defers(), writeout_final_defers(), + writeout_defered_file()); support for new arf_hpbinary and + arf_hpascii formats; support for HPUX CDF's. + + * copypass.c: move most multiple link code to new routines + link_name() and link_to_maj_min_ino(); use new macro UMASKED_SYMLINK + instead of symlink(); support for HPUX CDF's. + + * extern.h: added arf_hpascii and arf_hpbinary archive enum types; + added debug_flag. + + * global.c: added debug_flag. + + * main.c: added debug_flag; support for hpodc and hpbin formats. + + * makepath.c: split from standard makpath.c to add support + for HPUX CDF's. + + * mt.c: added !defined(__osf__) (from Andrew Marquis + ). + + * system.h: new macro UMASKED_SYMLINK + + * tar.c: minor changes to read (slightly broken?) System V.4 posix + tar archives and HPUX posix tar archives. + + * util.c: HPUX CDF support (including new routines + add_cdf_double_slashes() and islasparentcdf()); new routine + umasked_symlink(). + +Sun Mar 14 23:00:14 1993 Jim Meyering (meyering@comco.com) + + * copypass.c (process_copy_pass): Use <=, not just <, when comparing + mtimes. From Pieter Bowman . + +Fri Jan 15 14:35:37 1993 David J. MacKenzie (djm@kropotkin.gnu.ai.mit.edu) + + * copyin.c: Move include of fnmatch.h to get right FNM* macros. + +Tue Nov 24 08:45:32 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * Version 2.2. + + * copyout.c (process_copy_out): Add parens for gcc -Wall. + From Jim Meyering. + + * system.h: Use HAVE_FCNTL_H, not USG. + + * dstring.c, mt.c, system.h: Use HAVE_STRING_H, not USG. + +Fri Nov 20 22:47:18 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * copyin.c (read_in_binary): Copy the dev and ino that are + already in `file_hdr' into `short_hdr'. + From dao@abars.att.com (David A Oshinsky). + + * system.h [!_POSIX_VERSION]: Declare lseek as off_t, not long. + From Karl Berry. + +Wed Oct 14 13:53:41 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * Version 2.1. + +Tue Oct 13 22:51:34 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * main.c: Add --swap equivalent to -b. + + * mt.c: Add f_force_local variable and -V --version option. + +Fri Oct 2 18:42:27 1992 David J. MacKenzie (djm@kropotkin.gnu.ai.mit.edu) + + * main.c (long_opts, usage): Add --force-local option. + +Thu Oct 1 23:23:43 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * main.c (process_args) [__MSDOS__]: Don't call geteuid. + + * copyin.c (read_in_{old,new}_ascii): Use `l' for sscanf into longs. + * copyout.c (write_out_header): Ditto for sprintf. + * global.c, extern.h: Make input_size and output_size long. + +Thu Sep 10 23:39:30 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * global.c, extern.h: Add new var f_force_local to work with + rmt.h change from tar. + +Sun Aug 23 00:18:20 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu) + + * Version 2.0. + + * tar.c (otoa): Compute value in an unsigned long, not an int. + * copyout.c (write_out_header) [__MSDOS__]: Don't use dev_t. + + * main.c (process_args): By default, don't chown for non-root users. + +Sat Aug 22 14:17:54 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * global.c, extern.h: Use uid_t and gid_t. + + * main.c (main) [__EMX__]: Expand wildcards. + * system.h [__EMX__]: Alias some error names. From Kai Uwe Rommel. + + * extern.h [__STDC__]: Use prototypes. + + * copyin.c (process_copy_in), copyout.c (process_copy_out), + copypass.c (process_copy_pass): Open all files with O_BINARY. + Add cast to chmod call. + * util.c: Add cast to bcopy calls. Make hash_insert static. + From Kai Uwe Rommel. + +Thu Aug 20 22:03:49 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * util.c (peek_in_buf): Don't print "end of file" before + getting the next reel of medium. + + * copyin.c (read_in_old_ascii): Allocate space for NUL terminator. + Print newline for dot line when done, even if appending. + +Thu Jul 23 16:34:53 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * tar.c (write_out_tar_header, read_in_tar_header) + [__MSDOS__]: Don't try to get user and group names. + * extern.h: Don't declare the functions to do it (need uid_t). + + * main.c [__MSDOS__]: Ignore the -R option. + + * system.h: Define makedev if defining major and minor. + + * copyin.c, copyout.c [__MSDOS__]: setmode on archive_des, not + 0 and 1. + +Sat Jul 18 14:30:55 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * tar.c, stripslash.c, userspec.c, cpiohdr.h, tar.h, tarhdr.h, + system.h: New files. + * Move portability stuff from various files to system.h. + * cpio.h: Rename header structure and members, and add + new structure for SVR4 format. + * copyin.c, copyout.c: Use the new structure internally, the + old one only for I/O in the old formats. + * copyin.c (read_in_header): Recognize the new archive formats. + (read_in_new_ascii, read_pattern_file, skip_padding): New functions. + (swab_array): Do the swapping using char pointers instead of + bitwise arithmetic. + (process_copy_in): Handle byte and halfword swapping and new formats. + Ok if a directory we want to make already exists, but set its perms. + Do chmod after chown to fix any set[ug]id bits. + Use `struct utimbuf' instead of a long array. + * copyout.c (write_out_header): Handle new formats. + (process_copy_out): Use `struct utimbuf'. + Handle appending and new formats. + Remove any leading `./' from filenames. + (read_for_checksum, clear_rest_of_block, pad_output): New functions. + * copypass.c (process_copy_pass): Use `struct utimbuf'. + Ok if a directory we want to make already exists, but set its perms. + Do chmod after chown to fix any set[ug]id bits. + Don't change perms of `.'. + * extern.h, global.c: Replace the separate format flags with + one variable. Add new variables for the new options. + * main.c: Add new options -A --append, -H --format, -C --io-size, + -M --message, --no-preserve-owner, -R --owner, -E --pattern-file, + -V --dot, -s --swap-bytes, -S --swap-halfwords, -b, -I, -k, -O. + (usage): Document them. + (process_args): Recognize them. Use open_archive. + (initialize_buffers): Allow room for tar archives and double buffers. + * util.c (empty_output_buffer_swap): New function. + (empty_output_buffer): Call it if swapping current file. + Check additional end of media indicators. + (swahw_array, peek_in_buf, prepare_append, open_archive, + set_new_media_message): New functions. + (fill_input_buffer): Don't print error message if end of media. + (toss_input): Don't seek, always read. + (copy_files): Update crc if needed. + (find_inode_file, add_inode): Check major and minor numbers as + well as dev. + (get_next_reel): Prompt user if archive name is unknown. + Print fancy messages. + Close the archive and reopen it. + + Above primarily from John Oleynick . + + * util.c (find_inode_file): Use modulus when computing initial + loop index. + (add_inode): Zero out new entry. + From scott@sctc.com (Scott Hammond). + + * cpio.h, copyin.c, copyout.c: Rename `struct cpio_header' + members from h_foo to c_foo. + +Wed May 20 00:09:26 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu) + + * copyin.c: If we include a header file specifically to get + major et al., assume we have them. + +Mon Mar 9 19:29:20 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * mt.c (main): rmtclose the tape file descriptor. + + * main.c (main): rmtclose the archive, if not in copy-pass mode. + + * util.c (create_all_directories): Don't print a message when + creating a directory, for UNIX compat. + + * copyin.c (process_copy_in), copypass.c (process_copy_pass): + Skip file if it has the same timestamp as existing file, not just + if it is older than existing file, for UNIX compat. + +Tue Mar 3 12:06:58 1992 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu) + + * main.c, mt.c (usage): Document long options as starting with + -- instead of +. + + * extern.h: Only declare lseek if not _POSIX_VERSION. + +Tue Dec 24 00:19:45 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * copyin.c: Use MAJOR_IN_MKDEV and MAJOR_IN_SYSMACROS instead + of USG and _POSIX_VERSION to find major and minor macros. + + * mt.c: Use unistd.h and stdlib.h if available. + + * copyin.c, copyout.c, copypass.c, util.c, extern.h: Change + POSIX ifdefs to HAVE_UNISTD_H and _POSIX_VERSION. + +Sun Aug 25 06:31:08 1991 David J. MacKenzie (djm at apple-gunkies) + + * Version 1.5. + + * bcopy.c: New file (moved from util.c). + + * mt.c (print_status): Not all hpux machines have mt_fileno + and mt_blkno; rather than trying to track HP's product line, + just assume none of them have them. + + * util.c (copy_buf_out, copy_in_buf): Use more efficient + copying technique for a big speedup. + +Fri Aug 2 04:06:45 1991 David J. MacKenzie (djm at apple-gunkies) + + * configure: Support +srcdir. Create config.status. + Remove it and Makefile if interrupted while creating them. + +Thu Jul 18 09:43:40 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * Many files: use __MSDOS__ instead of MSDOS. + + * util.c, configure: Use NO_MTIO instead of HAVE_MTIO, to keep + up with tar and rtapelib.c. + +Mon Jul 15 13:45:30 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * configure: Also look in sys/signal.h for signal decl. + +Thu Jul 11 01:50:32 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * Version 1.4. + + * configure: Remove /etc and /usr/etc from PATH to avoid + finding /etc/install. + +Wed Jul 10 01:40:07 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * makefile.pc: Rewrite for Turbo C 2.0. + * util.c [__TURBOC__] (utime): New function. + * alloca.c, tcexparg.c: New files. + + * extern.h [STDC_HEADERS]: Don't declare malloc and realloc. + + * main.c [MSDOS]: Make binary mode the default. + * copyin.c, copyout.c: Make stdin or stdout binary mode as + appropriate (so cpio archives don't get corrupted). + + * Many files: Use if STDC_HEADERS as well as if USG. + + * configure, Makefile.in: $(INSTALLPROG) -> $(INSTALL), + $(INSTALLTEXT) -> $(INSTALLDATA). + +Mon Jul 8 23:18:28 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * configure: For some library functions that might be missing, + conditionally add the .o files to Makefile instead of + defining func_MISSING. + * mkdir.c: Renamed from mkrmdir.c. + +Sat Jul 6 02:27:22 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu) + + * configure: echo messages to stdout, not stderr. + Use a test program to see if alloca needs -lPW. + +Thu Jun 27 16:15:15 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu) + + * copyin.c (process_copy_in), copyout.c (process_copy_out), + copypass.c (process_copy_pass): Check close return value for + delayed error notification because of NFS. + +Thu Jun 20 02:43:33 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu) + + * configure: Include $DEFS when compiling test programs. + + * util.c: Only declare getpwuid and getgrgid if not POSIX. + + * Version 1.3. + + * copyin.c: Use time_t, not long, for time values. + + * mt.c (print_status): Special cases for HP-UX and Ultrix. + + * util.c: Compile bcopy if USG or STDC_HEADERS, not BCOPY_MISSING. + +Tue Jun 11 16:40:02 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu) + + * copyin.c: Don't include sys/sysmacros.h if _POSIX_SOURCE. + + * copyin.c, copyout.c, copypass.c: Don't include sys/file.h if POSIX. + + * util.c: Include sys/types.h before, not after, pwd.h and grp.h. + + * configure: New shell script to aid configuration and create + Makefile from Makefile.in. + + * copyin.c (process_copy_in): Use POSIX.2 fnmatch instead of + glob_match. + +Mon Jun 10 22:11:19 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu) + + * global.c, extern.h: New variable, name_end. + * main.c (process_args, usage): Add -0 +null option to set it. + * copypass.c (process_copy_pass), copyout.c (process_copy_out): + Use it. + + * dstring.c (ds_fgetstr): New function made from ds_fgets. + (ds_fgets, ds_fgetname): Implement as front ends to ds_fgetstr. + +Sun Jun 2 15:45:24 1991 David J. MacKenzie (djm at wheat-chex) + + * most files: use GPL version 2. + +Sat May 18 11:39:22 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu) + + * copyin.c, copypass.c: Take out #ifdef MSDOS around chown. + * util.c [MSDOS]: Provide dummy chown. + +Fri May 17 21:29:05 1991 David J. MacKenzie (djm at churchy.gnu.ai.mit.edu) + + * Version 1.2. + + * makefile.pc, cpio.cs: Update for new source and object files. + +Fri Mar 15 05:48:36 1991 David J. MacKenzie (djm at geech.ai.mit.edu) + + * global.c, extern.h: New variable `archive_desc'. + * main.c (process_args): Set it. + * copyout.c (process_copy_out), copyin.c (process_copy_in): + Use it. + + * copyout.c (process_copy_out), copyin.c (process_copy_in): + Remote tapes are special and not seekable; don't fstat them. + + * main.c (main, usage): Add -F, +file option. Use rmtopen. + (main): Exit after printing version number. + * util.c (empty_output_buffer): Use rmtwrite instead of write. + (fill_input_buffer): Use rmtread instead of read. + (tape_offline): Use rmtioctl instead of ioctl. + Test HAVE_MTIO instead of MTIO_MISSING, for tar compatibility. + +Thu Mar 14 17:49:57 1991 David J. MacKenzie (djm at geech.ai.mit.edu) + + * util.c (create_all_directories): Use make_path to do the work. + +Sat Jan 12 15:32:15 1991 David J. MacKenzie (djm at geech.ai.mit.edu) + + * copyin.c, copyout.c, copypass.c, util.c: Only declare + `errno' if not MSDOS. Some Unix errno.h do, some don't . . . . + + * global.c, extern.h: Make `input_size' and `output_size' + unsigned, for 16 bit machines. + + * copyin.c (print_name_with_quoting): All non-ctrl chars are + printable on MS-DOS. + + * util.c (empty_output_buffer): Never make sparse files; + can create unrunnable executables. + * copyin.c, copyout.c, copypass.c: Callers changed. + * util.c (finish_output_file): Function removed. + +Tue Nov 6 15:47:16 1990 David J. MacKenzie (djm at apple-gunkies) + + * copyin.c, util.c, extern.h: Rename copystring to xstrdup. + +Mon Oct 29 02:24:41 1990 David J. MacKenzie (djm at apple-gunkies) + + * util.c (empty_output_buffer): Only make sparse files if + NO_SPARSE_FILES is undefined, to accomodate dumb kernels. + +Wed Jul 25 18:48:35 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * util.c (getuser, getgroup): Make uid and gid unsigned short, + not int. + +Sat Jul 21 00:44:44 1990 David J. MacKenzie (djm at apple-gunkies) + + * copyin.c, copyout.c, copypass.c, util.c, cpio.h: Add ifdefs + for MSDOS. + +Sun Jul 15 23:51:48 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * copyin.c, copyout.c, copypass.c, global.c, extern.h, util.c: + Use longs where appropriate, for 16 bit machines. + +Sun Jul 8 22:58:06 1990 David J. MacKenzie (djm at apple-gunkies) + + * main.c (process_args, usage): Change -b option to -O (old), to + allow adding byte swapping later. + +Sat Jul 7 14:48:35 1990 David J. MacKenzie (dave at edfmd) + + * Version 1.1. + + * cpio.h: Make `mtime' and `filesize' unsigned long. + * copyin.c (read_in_binary), copyout.c (write_out_header): + High short-word of `mtime' and `filesize' always comes first. + + * (read_in_ascii, read_in_binary): New functions, from code in + read_in_header. + (read_in_header): Search for valid magic number, then fill in + rest of header using read_in_ascii and read_in_binary. + * global.c, extern.h: New variable, `binary_flag'. + * main.c (process_args): Recognize new -b +binary option. + * util.c [BCOPY_MISSING] (bcopy): New function. + +Wed Jul 4 00:40:58 1990 David J. MacKenzie (djm at apple-gunkies) + + * main.c (process_args): Add local pointers to functions to + work around a pcc bug found on a Convex. + + * copyin.c (process_copy_in), util.c (toss_input, + create_all_directories, add_inode): Don't use `index' as a + variable name. + +Tue Jul 3 02:33:36 1990 David J. MacKenzie (djm at apple-gunkies) + + * version 1.0. + +Mon Jul 2 23:18:56 1990 David J. MacKenzie (djm at twiddle) + + * copyin.c (process_copy_in), copyout.c (process_copy_out), + copypass.c (process_copy_pass): Print "1 block", not "1 blocks". + + * copyin.c (process_copy_in), copypass.c (process_copy_pass): + Unlink existing dest. file unless either it is newer and + not unconditional, or it is a directory. + +Mon Jul 2 03:57:41 1990 David J. MacKenzie (dave at edfmd) + + * util.c (xrealloc): New function. + * dstring.c (ds_resize): Use xrealloc instead of free and + xmalloc. Never shrink the string. + + * copypass.c (process_copy_pass): More efficient + string handling while constructing output filename. + + * global.c, extern.h, main.c, cpio.h: Change from an enum, + `copy_command', to a pointer to a void function, `copy_function'. + + * cpio.h (struct cpio_header): Make most fields unsigned. + Rename h_filesize to h_filesizes and h_mtime to h_mtimes, and + add new `long' fields with the old names at the end of the + structure. + * copyin.c (read_in_header): Set the long fields from the + short arrays, making sure longs are aligned properly. + (process_copy_in, long_format): Use the long fields. + * copyout.c (write_out_header): Set the short arrays from the + long fields, making sure longs are aligned properly. + (process_copy_out): Use the long fields. + + * global.c, extern.h: New variable `output_is_seekable'. + * util.c (empty_output_buffer): If output_is_seekable, use + lseek to write blocks of zeros. + (finish_output_file): New function. + * copyin.c (process_copy_in), copyout.c (process_copy_out), + copypass.c (process_copy_pass): Set `output_is_seekable' + correctly and call finish_output_file. + * main.c (initialize_buffers): Allocate space for sentinel in + `output_buffer'. + + * global.c, extern.h: New variable `numeric_uid'. + * main.c (process_args): Accept -n +numeric-uid-gid option, like ls. + * copyin.c (long_format): Use numeric_uid. + + * copyin.c (process_copy_in), copyout.c (process_copy_out), + copypass.c (process_copy_pass): Don't (for verbose) print the + names of files that are not copied because of errors. Try to + create missing directories for all file types. Free temporary + buffers on error. + +Sat Jun 30 14:28:45 1990 David J. MacKenzie (djm at apple-gunkies) + + * version.c: New file. + * main.c: Add -V, +version option. + * Makefile [dist]: Extract version number from version.c. + +Sat Jun 30 12:44:47 1990 David J. MacKenzie (dave at edfmd) + + * global.c, extern.h, copyin.c, copyout.c, util.c: Rename + `{input,output}_is_regular' to `{input,output}_is_special' and + reverse the truth value. + + * global.c, extern.h: New variable `input_is_seekable' to + control whether to skip data with lseek or read. + * copyin.c (process_copy_in): Set it. + * util.c (toss_input): Use it. + + * global.c, extern.h: New variable `xstat' that selects stat + or lstat for input files. + * main.c (process_args): New option -L, +dereference to set + xstat to stat instead of lstat. + (usage): Document it. + * copyout.c (process_copy_out), copypass.c + (process_copy_pass): Use *xstat on input file. + +Sat Jun 30 01:53:12 1990 David J. MacKenzie (dave at edfmd) + + * dstring.c (ds_init): Return void because return value was + never used. + (ds_resize): Ditto, and free old value instead of new one. + + * util.c (empty_output_buffer, fill_input_buffer, + copy_out_buf, copy_in_buf, toss_input, copy_files): Return + void instead of an error value and make errors fatal + immediately instead of several levels up, to prevent printing + of multiple error messages by different levels of functions. + + * copyin.c (read_in_header): Return void, because the error + handling all happens at lower levels. + (print_name_with_quoting): New function. + (long_format): Call print_name_with_quoting. Take additional + arg for name of linked-to file, and print it if nonzero. + (process_copy_in): For verbose listing of symlinks, read in + the linkname and pass it to long_format. + + * extern.h: Declare some more functions. + +Thu Jun 28 16:07:15 1990 David J. MacKenzie (dave at edfmd) + + * copypass.c (process_copy_pass): Warn about unknown file types. + + * copyout.c (process_copy_out): Check fstat return for error. + Record filesize of 0 for special files. Warn about unknown + file types. + + * copyin.c (process_copy_in): Warn about unknown file types. + (read_in_header): Warn about byte-reversed binary headers. + +Sat Jun 23 22:50:45 1990 David J. MacKenzie (dave at edfmd) + + * main.c (main): Set umask to 0 so permissions of created + files are preserved. + + * copyin.c, copyout.c, copypass.c, util.c: Pass file + descriptors as ints, not pointers to ints. + Cast file timestamps and sizes to long *, not int *, for 16 + bit machines. + Use lstat instead of stat, if available. + Handle FIFO's, sockets, and symlinks, if supported by O.S. + + * copyin.c (process_copy_in), copyout.c (process_copy_out): + Don't consider FIFO'S, sockets, etc. to be possible tape drives. + + * util.c (create_all_directories): Fix incorrect loop + termination check. Only copy string if it contains slashes. + Don't check whether directory "" exists. + (tape_offline): Code moved from get_next_reel. + (get_next_reel): Print message before taking tape offline. + Read a line of arbitrary length. + + * copyout.c, copyin.c, copypass.c: Always use utime, not utimes. + + * copyin.c (swab_short): New macro. + (swab_array): New function. + (read_in_header): In binary mode, if a byte-swapped header is + read, swap the bytes back. + (process_copy_in, process_copy_pass): Don't stat each file to + create unless !unconditional_flag. Create device files correctly. + Don't temporarily allow files being created to be read by + other users. Don't unnecessarily chmod special files. + +Thu May 31 20:51:43 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * copyin.c (long_format): Use mode_string to format + file protections instead of doing it ourselves. + (protections): Function removed. + +Sat Apr 14 02:31:01 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * cpio.h (struct cpio_header): Make inode, mode, uid, gid + fields unsigned. + + * util.c (getgroup): New function. + * copyin.c (long_format): Print group name of files. + Print file size, etc. as unsigned integers, not signed. + + * main.c (process_args): If -t is given and neither -i, -o, or + -p is given, assume -i. + + * Add -f, +nonmatching option. + * main.c: Rename +out to +create, +in to +extract, + +modification-time to +preserve-modification-time, + +pass to +pass-through. + + * copyin.c (process_copy_in), copypass.c (process_copy_pass): + Don't complain in chown fails because the user doesn't have + permission. + +Fri Apr 13 13:53:20 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * Add ifdefs for USG/Xenix. + * util.c (cpio_error): Function removed. + * Use error instead of cpio_error, so system error messages + will be included. + * cpio.h: Rename 'hdr_struct' to 'struct cpio_header'. + * Move definition of xmalloc from dstring.c to util.c. + * global.c, extern.c: Add global `program_name'. + * main.c (main): Set program_name. + (process_args): Rename +reset-atime to +reset-access-time, + +table to +list. + Have +block-size take an argument. + +Thu Apr 12 13:33:32 1990 David J. MacKenzie (djm at rice-chex) + + * util.c (find_inode_file): Make inode an int, not a short. + + * Make functions that don't return a value have type void. + Add some casts to function calls. + +Wed Apr 11 14:55:28 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * main.c (process_args): -i, -o, and -p don't take arguments. + + * main.c (process_args): Get the non-option args from the + correct elements of argv. + +Tue Apr 10 00:20:26 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * Indent source code and update copyrights. + + * cpio.c (usage): Change `collection' to `archive' in message. + +Thu Dec 28 03:03:55 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * dstring.c (xmalloc): Don't return a null pointer if size is 0, + on the assumption that trying to allocate 0 bytes is a bug that + should be trapped. + +Wed Dec 20 03:24:48 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * All files: Change from GNU CPIO General Public License to + GNU General Public License. + +Mon Dec 18 13:18:36 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * Makefile: Add clean target and defines for CC and LDFLAGS. + Add dist target and SRCS, DISTFILES macros. Add tags and TAGS targets. + * dstring.c (ds_fgets): Read characters into an int, not char. + (xmalloc): New function. + (out_of_memory): Function removed. + Global: use xmalloc instead of malloc and out_of_memory. + * extern.h, global.c: Make flag variables ints instead of chars for + compatibility with getopt_long. + * extern.h: Declare more functions. + * main.c (usage): Put the whole usage message into a single string + and fix errors. + * util.c (create_all_directories): Remove unused variable. + (get_next_reel): Ditto. + * dstring.h: Declare function. + +Sat Dec 2 13:22:37 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * main.c: Change +copy-pass option to +pass, +copy-in to +in, + +copy-out to +out, and +mkdir to +make-directories, and add null + option to terminate table. + (process_args): Use the same code to handle long and short named + options. + (usage): Mention long options in message. + +Local Variables: +mode: indented-text +left-margin: 8 +version-control: never +End: diff --git a/gnu/usr.bin/cpio/Makefile b/gnu/usr.bin/cpio/Makefile new file mode 100644 index 0000000000..104fe3cabd --- /dev/null +++ b/gnu/usr.bin/cpio/Makefile @@ -0,0 +1,9 @@ +PROG= cpio +CFLAGS+= -I${.CURDIR} -DRETSIGTYPE=void -DHAVE_SYS_MTIO_H=1 -DSTDC_HEADERS=1 -DHAVE_UNISTD_H=1 -DHAVE_STRING_H=1 -DHAVE_FCNTL_H=1 -DHAVE_UTIME_H=1 -DHAVE_STRERROR=1 -DHAVE_VPRINTF=1 -DDIRENT=1 + +SRCS = copyin.c copyout.c copypass.c defer.c dstring.c fnmatch.c global.c \ + main.c tar.c util.c error.c filemode.c getopt.c getopt1.c version.c \ + rtapelib.c dirname.c idcache.c makepath.c xmalloc.c stripslash.c \ + userspec.c xstrdup.c + +.include diff --git a/gnu/usr.bin/cpio/NEWS b/gnu/usr.bin/cpio/NEWS new file mode 100644 index 0000000000..2648c843f9 --- /dev/null +++ b/gnu/usr.bin/cpio/NEWS @@ -0,0 +1,55 @@ + +Major changes in version 2.3: + +* in newc and crc format archives, only store 1 copy of multiply linked files +* handle multiply linked devices properly +* handle multiply linked files with cpio -pl even when the source and + destination are on different file systems +* support HPUX Context Dependent Files +* read and write HPUX cpio archives +* read System V.4 POSIX tar archives and HPUX POSIX tar archives +* use rmdir, instead of unlink, to delete existing directories + +Major changes in version 2.2: + +* handle link counts correctly when reading binary cpio archives +* configure checks for some libraries that SVR4 needs + +Major changes in version 2.1: + +* cpio can access remote non-device files as well as remote devices +* fix bugs in the MS-DOS port +* add --swap equivalent to -b option + +Version 2.0 adds the following features: + +Support for the SVR4 cpio formats, which can store inodes >65535, and +for traditional and POSIX tar archives. Also adds these options: + +-A --append append to instead of replacing the archive +-V --dot print a dot for each file processed +-H --format select archive format +-C --io-size select I/O block size in bytes +-M --message print a message at end of media volumes +--no-preserve-owner don't change files' owners when extracting +-R --owner set files' owners when extracting +-E --pattern-file list of shell filename patterns to process +-s --swap-bytes handle byte-order differences when extracting files +-S --swap-halfwords ditto +-b like -sS +-I input archive filename +-k recognize corrupted archives (we alawys do it, though) +-O output archive filename + +Some options of previous versions have been renamed in 2.0: + +--binary was replaced by --format=bin +--portability was replaced by --format=odc + +Some options have changed meaning in 2.0, for SVR4 compatibility: + +-O used to select the binary archive format, now selects the output file +-V used to print the version number, now prints a dot for each file + +Version 2.0 also fixes several bugs in the handling of files with +multiple links and of multi-volume archives on floppy disks. diff --git a/gnu/usr.bin/cpio/README b/gnu/usr.bin/cpio/README new file mode 100644 index 0000000000..83b5228717 --- /dev/null +++ b/gnu/usr.bin/cpio/README @@ -0,0 +1,56 @@ +This is GNU cpio, a program to manage archives of files. +As of version 2.0, it supports the features of the System V release 4 +cpio, including support for tar archives. + +The main advantages of GNU cpio over Unix versions are: + +* It can access tape drives on other hosts using TCP/IP. + +* `-o' and `-p' can copy symbolic links either as symbolic links or, +with `-L', as the files they point to. + +* `-i' automatically recognizes the archive format and tries to +recover from corrupted archives. + +* The output of '-itv' looks like 'ls -l'. + +* It accepts long-named options as well as traditional +single-character options. + +A few features of other versions of cpio are missing from GNU cpio, including: + +* The `-6' option to support Sixth Edition Unix cpio archives with `-i'. + +* An option to limit volume size, like afio -s. + + +GNU cpio supports the POSIX.1 "ustar" tar format. GNU tar supports a +somewhat different, early draft of that format. That draft format has +a slightly different magic number in the tar header and doesn't +include the path prefix part of the header, which allows storing file +names that are longer than 100 characters. GNU cpio knows to +recognize the nonstandard GNU tar "ustar" archives. + +The following patch to GNU tar 1.11.1 makes GNU tar recognize standard +"ustar" archives, such as GNU cpio produces, except that it won't use +the path prefix. Without this patch, GNU tar thinks that standard +"ustar" archives are old-format tar archives and can not use the extra +information that "ustar" format contains. If you use this patch, +remember that you will lose the beginnings of paths that are longer +than 100 characters. That's why it's not an official part of GNU tar. +(Adding support for the path prefix to GNU tar is not trivial.) + +--- list.c.orig Mon Sep 14 17:04:03 1992 ++++ list.c Wed Oct 14 14:02:28 1992 +@@ -439,7 +439,7 @@ + st->st_ctime = from_oct(1+12, header->header.ctime); + } + +- if (0==strcmp(header->header.magic, TMAGIC)) { ++ if (0==strncmp(header->header.magic, TMAGIC, 5)) { + /* Unix Standard tar archive */ + *stdp = 1; + if (wantug) { + +Mail suggestions and bug reports for GNU cpio to +bug-gnu-utils@prep.ai.mit.edu. diff --git a/gnu/usr.bin/cpio/alloca.c b/gnu/usr.bin/cpio/alloca.c new file mode 100644 index 0000000000..c04c0efebd --- /dev/null +++ b/gnu/usr.bin/cpio/alloca.c @@ -0,0 +1,475 @@ +/* alloca.c -- allocate automatically reclaimed memory + (Mostly) portable public-domain implementation -- D A Gwyn + + This implementation of the PWB library alloca function, + which is used to allocate space off the run-time stack so + that it is automatically reclaimed upon procedure exit, + was inspired by discussions with J. Q. Johnson of Cornell. + J.Otto Tennant contributed the Cray support. + + There are some preprocessor constants that can + be defined when compiling for your specific system, for + improved efficiency; however, the defaults should be okay. + + The general concept of this implementation is to keep + track of all alloca-allocated blocks, and reclaim any + that are found to be deeper in the stack than the current + invocation. This heuristic does not reclaim storage as + soon as it becomes invalid, but it will do so eventually. + + As a special case, alloca(0) reclaims storage without + allocating any. It is a good idea to use alloca(0) in + your main control loop, etc. to force garbage collection. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* If compiling with GCC, this file's not needed. */ +#ifndef alloca + +#ifdef emacs +#ifdef static +/* actually, only want this if static is defined as "" + -- this is for usg, in which emacs must undefine static + in order to make unexec workable + */ +#ifndef STACK_DIRECTION +you +lose +-- must know STACK_DIRECTION at compile-time +#endif /* STACK_DIRECTION undefined */ +#endif /* static */ +#endif /* emacs */ + +/* If your stack is a linked list of frames, you have to + provide an "address metric" ADDRESS_FUNCTION macro. */ + +#ifdef CRAY +long i00afunc (); +#define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg)) +#else +#define ADDRESS_FUNCTION(arg) &(arg) +#endif + +#if __STDC__ +typedef void *pointer; +#else +typedef char *pointer; +#endif + +#define NULL 0 + +/* Different portions of Emacs need to call different versions of + malloc. The Emacs executable needs alloca to call xmalloc, because + ordinary malloc isn't protected from input signals. On the other + hand, the utilities in lib-src need alloca to call malloc; some of + them are very simple, and don't have an xmalloc routine. + + Non-Emacs programs expect this to call use xmalloc. + + Callers below should use malloc. */ + +#ifndef emacs +#define malloc xmalloc +extern pointer xmalloc (); +#endif + +/* Define STACK_DIRECTION if you know the direction of stack + growth for your system; otherwise it will be automatically + deduced at run-time. + + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ + +#ifndef STACK_DIRECTION +#define STACK_DIRECTION 0 /* Direction unknown. */ +#endif + +#if STACK_DIRECTION != 0 + +#define STACK_DIR STACK_DIRECTION /* Known at compile-time. */ + +#else /* STACK_DIRECTION == 0; need run-time code. */ + +static int stack_dir; /* 1 or -1 once known. */ +#define STACK_DIR stack_dir + +static void +find_stack_direction () +{ + static char *addr = NULL; /* Address of first `dummy', once known. */ + auto char dummy; /* To get stack address. */ + + if (addr == NULL) + { /* Initial entry. */ + addr = ADDRESS_FUNCTION (dummy); + + find_stack_direction (); /* Recurse once. */ + } + else + { + /* Second entry. */ + if (ADDRESS_FUNCTION (dummy) > addr) + stack_dir = 1; /* Stack grew upward. */ + else + stack_dir = -1; /* Stack grew downward. */ + } +} + +#endif /* STACK_DIRECTION == 0 */ + +/* An "alloca header" is used to: + (a) chain together all alloca'ed blocks; + (b) keep track of stack depth. + + It is very important that sizeof(header) agree with malloc + alignment chunk size. The following default should work okay. */ + +#ifndef ALIGN_SIZE +#define ALIGN_SIZE sizeof(double) +#endif + +typedef union hdr +{ + char align[ALIGN_SIZE]; /* To force sizeof(header). */ + struct + { + union hdr *next; /* For chaining headers. */ + char *deep; /* For stack depth measure. */ + } h; +} header; + +static header *last_alloca_header = NULL; /* -> last alloca header. */ + +/* Return a pointer to at least SIZE bytes of storage, + which will be automatically reclaimed upon exit from + the procedure that called alloca. Originally, this space + was supposed to be taken from the current stack frame of the + caller, but that method cannot be made to work for some + implementations of C, for example under Gould's UTX/32. */ + +pointer +alloca (size) + unsigned size; +{ + auto char probe; /* Probes stack depth: */ + register char *depth = ADDRESS_FUNCTION (probe); + +#if STACK_DIRECTION == 0 + if (STACK_DIR == 0) /* Unknown growth direction. */ + find_stack_direction (); +#endif + + /* Reclaim garbage, defined as all alloca'd storage that + was allocated from deeper in the stack than currently. */ + + { + register header *hp; /* Traverses linked list. */ + + for (hp = last_alloca_header; hp != NULL;) + if ((STACK_DIR > 0 && hp->h.deep > depth) + || (STACK_DIR < 0 && hp->h.deep < depth)) + { + register header *np = hp->h.next; + + free ((pointer) hp); /* Collect garbage. */ + + hp = np; /* -> next header. */ + } + else + break; /* Rest are not deeper. */ + + last_alloca_header = hp; /* -> last valid storage. */ + } + + if (size == 0) + return NULL; /* No allocation required. */ + + /* Allocate combined header + user data storage. */ + + { + register pointer new = malloc (sizeof (header) + size); + /* Address of header. */ + + ((header *) new)->h.next = last_alloca_header; + ((header *) new)->h.deep = depth; + + last_alloca_header = (header *) new; + + /* User storage begins just after header. */ + + return (pointer) ((char *) new + sizeof (header)); + } +} + +#ifdef CRAY + +#ifdef DEBUG_I00AFUNC +#include +#endif + +#ifndef CRAY_STACK +#define CRAY_STACK +#ifndef CRAY2 +/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */ +struct stack_control_header + { + long shgrow:32; /* Number of times stack has grown. */ + long shaseg:32; /* Size of increments to stack. */ + long shhwm:32; /* High water mark of stack. */ + long shsize:32; /* Current size of stack (all segments). */ + }; + +/* The stack segment linkage control information occurs at + the high-address end of a stack segment. (The stack + grows from low addresses to high addresses.) The initial + part of the stack segment linkage control information is + 0200 (octal) words. This provides for register storage + for the routine which overflows the stack. */ + +struct stack_segment_linkage + { + long ss[0200]; /* 0200 overflow words. */ + long sssize:32; /* Number of words in this segment. */ + long ssbase:32; /* Offset to stack base. */ + long:32; + long sspseg:32; /* Offset to linkage control of previous + segment of stack. */ + long:32; + long sstcpt:32; /* Pointer to task common address block. */ + long sscsnm; /* Private control structure number for + microtasking. */ + long ssusr1; /* Reserved for user. */ + long ssusr2; /* Reserved for user. */ + long sstpid; /* Process ID for pid based multi-tasking. */ + long ssgvup; /* Pointer to multitasking thread giveup. */ + long sscray[7]; /* Reserved for Cray Research. */ + long ssa0; + long ssa1; + long ssa2; + long ssa3; + long ssa4; + long ssa5; + long ssa6; + long ssa7; + long sss0; + long sss1; + long sss2; + long sss3; + long sss4; + long sss5; + long sss6; + long sss7; + }; + +#else /* CRAY2 */ +/* The following structure defines the vector of words + returned by the STKSTAT library routine. */ +struct stk_stat + { + long now; /* Current total stack size. */ + long maxc; /* Amount of contiguous space which would + be required to satisfy the maximum + stack demand to date. */ + long high_water; /* Stack high-water mark. */ + long overflows; /* Number of stack overflow ($STKOFEN) calls. */ + long hits; /* Number of internal buffer hits. */ + long extends; /* Number of block extensions. */ + long stko_mallocs; /* Block allocations by $STKOFEN. */ + long underflows; /* Number of stack underflow calls ($STKRETN). */ + long stko_free; /* Number of deallocations by $STKRETN. */ + long stkm_free; /* Number of deallocations by $STKMRET. */ + long segments; /* Current number of stack segments. */ + long maxs; /* Maximum number of stack segments so far. */ + long pad_size; /* Stack pad size. */ + long current_address; /* Current stack segment address. */ + long current_size; /* Current stack segment size. This + number is actually corrupted by STKSTAT to + include the fifteen word trailer area. */ + long initial_address; /* Address of initial segment. */ + long initial_size; /* Size of initial segment. */ + }; + +/* The following structure describes the data structure which trails + any stack segment. I think that the description in 'asdef' is + out of date. I only describe the parts that I am sure about. */ + +struct stk_trailer + { + long this_address; /* Address of this block. */ + long this_size; /* Size of this block (does not include + this trailer). */ + long unknown2; + long unknown3; + long link; /* Address of trailer block of previous + segment. */ + long unknown5; + long unknown6; + long unknown7; + long unknown8; + long unknown9; + long unknown10; + long unknown11; + long unknown12; + long unknown13; + long unknown14; + }; + +#endif /* CRAY2 */ +#endif /* not CRAY_STACK */ + +#ifdef CRAY2 +/* Determine a "stack measure" for an arbitrary ADDRESS. + I doubt that "lint" will like this much. */ + +static long +i00afunc (long *address) +{ + struct stk_stat status; + struct stk_trailer *trailer; + long *block, size; + long result = 0; + + /* We want to iterate through all of the segments. The first + step is to get the stack status structure. We could do this + more quickly and more directly, perhaps, by referencing the + $LM00 common block, but I know that this works. */ + + STKSTAT (&status); + + /* Set up the iteration. */ + + trailer = (struct stk_trailer *) (status.current_address + + status.current_size + - 15); + + /* There must be at least one stack segment. Therefore it is + a fatal error if "trailer" is null. */ + + if (trailer == 0) + abort (); + + /* Discard segments that do not contain our argument address. */ + + while (trailer != 0) + { + block = (long *) trailer->this_address; + size = trailer->this_size; + if (block == 0 || size == 0) + abort (); + trailer = (struct stk_trailer *) trailer->link; + if ((block <= address) && (address < (block + size))) + break; + } + + /* Set the result to the offset in this segment and add the sizes + of all predecessor segments. */ + + result = address - block; + + if (trailer == 0) + { + return result; + } + + do + { + if (trailer->this_size <= 0) + abort (); + result += trailer->this_size; + trailer = (struct stk_trailer *) trailer->link; + } + while (trailer != 0); + + /* We are done. Note that if you present a bogus address (one + not in any segment), you will get a different number back, formed + from subtracting the address of the first block. This is probably + not what you want. */ + + return (result); +} + +#else /* not CRAY2 */ +/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP. + Determine the number of the cell within the stack, + given the address of the cell. The purpose of this + routine is to linearize, in some sense, stack addresses + for alloca. */ + +static long +i00afunc (long address) +{ + long stkl = 0; + + long size, pseg, this_segment, stack; + long result = 0; + + struct stack_segment_linkage *ssptr; + + /* Register B67 contains the address of the end of the + current stack segment. If you (as a subprogram) store + your registers on the stack and find that you are past + the contents of B67, you have overflowed the segment. + + B67 also points to the stack segment linkage control + area, which is what we are really interested in. */ + + stkl = CRAY_STACKSEG_END (); + ssptr = (struct stack_segment_linkage *) stkl; + + /* If one subtracts 'size' from the end of the segment, + one has the address of the first word of the segment. + + If this is not the first segment, 'pseg' will be + nonzero. */ + + pseg = ssptr->sspseg; + size = ssptr->sssize; + + this_segment = stkl - size; + + /* It is possible that calling this routine itself caused + a stack overflow. Discard stack segments which do not + contain the target address. */ + + while (!(this_segment <= address && address <= stkl)) + { +#ifdef DEBUG_I00AFUNC + fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl); +#endif + if (pseg == 0) + break; + stkl = stkl - pseg; + ssptr = (struct stack_segment_linkage *) stkl; + size = ssptr->sssize; + pseg = ssptr->sspseg; + this_segment = stkl - size; + } + + result = address - this_segment; + + /* If you subtract pseg from the current end of the stack, + you get the address of the previous stack segment's end. + This seems a little convoluted to me, but I'll bet you save + a cycle somewhere. */ + + while (pseg != 0) + { +#ifdef DEBUG_I00AFUNC + fprintf (stderr, "%011o %011o\n", pseg, size); +#endif + stkl = stkl - pseg; + ssptr = (struct stack_segment_linkage *) stkl; + size = ssptr->sssize; + pseg = ssptr->sspseg; + result += size; + } + return (result); +} + +#endif /* not CRAY2 */ +#endif /* CRAY */ + +#endif /* no alloca */ diff --git a/gnu/usr.bin/cpio/copyin.c b/gnu/usr.bin/cpio/copyin.c new file mode 100644 index 0000000000..5196c5d679 --- /dev/null +++ b/gnu/usr.bin/cpio/copyin.c @@ -0,0 +1,1272 @@ +/* copyin.c - extract or list a cpio archive + Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include "filetypes.h" +#include "system.h" +#include "cpiohdr.h" +#include "dstring.h" +#include "extern.h" +#include "defer.h" +#include "rmt.h" +#ifndef FNM_PATHNAME +#include +#endif + +#ifndef HAVE_LCHOWN +#define lchown chown +#endif + +static void read_pattern_file (); +static void skip_padding (); +static void defer_copyin (); +static void create_defered_links (); +static void create_final_defers (); + +/* Return 16-bit integer I with the bytes swapped. */ +#define swab_short(i) ((((i) << 8) & 0xff00) | (((i) >> 8) & 0x00ff)) + +/* Read the header, including the name of the file, from file + descriptor IN_DES into FILE_HDR. */ + +void +read_in_header (file_hdr, in_des) + struct new_cpio_header *file_hdr; + int in_des; +{ + long bytes_skipped = 0; /* Bytes of junk found before magic number. */ + + /* Search for a valid magic number. */ + + if (archive_format == arf_unknown) + { + char tmpbuf[512]; + int check_tar; + int peeked_bytes; + + while (archive_format == arf_unknown) + { + peeked_bytes = peek_in_buf (tmpbuf, in_des, 512); + if (peeked_bytes < 6) + error (1, 0, "premature end of archive"); + + if (!strncmp (tmpbuf, "070701", 6)) + archive_format = arf_newascii; + else if (!strncmp (tmpbuf, "070707", 6)) + archive_format = arf_oldascii; + else if (!strncmp (tmpbuf, "070702", 6)) + { + archive_format = arf_crcascii; + crc_i_flag = TRUE; + } + else if ((*((unsigned short *) tmpbuf) == 070707) || + (*((unsigned short *) tmpbuf) == swab_short ((unsigned short) 070707))) + archive_format = arf_binary; + else if (peeked_bytes >= 512 + && (check_tar = is_tar_header (tmpbuf))) + { + if (check_tar == 2) + archive_format = arf_ustar; + else + archive_format = arf_tar; + } + else + { + copy_in_buf ((char *) tmpbuf, in_des, 1L); + ++bytes_skipped; + } + } + } + + if (archive_format == arf_tar || archive_format == arf_ustar) + { + if (append_flag) + last_header_start = input_bytes - io_block_size + + (in_buff - input_buffer); + if (bytes_skipped > 0) + error (0, 0, "warning: skipped %ld bytes of junk", bytes_skipped); + read_in_tar_header (file_hdr, in_des); + return; + } + + file_hdr->c_tar_linkname = NULL; + + copy_in_buf ((char *) file_hdr, in_des, 6L); + while (1) + { + if (append_flag) + last_header_start = input_bytes - io_block_size + + (in_buff - input_buffer) - 6; + if (archive_format == arf_newascii + && !strncmp ((char *) file_hdr, "070701", 6)) + { + if (bytes_skipped > 0) + error (0, 0, "warning: skipped %ld bytes of junk", bytes_skipped); + read_in_new_ascii (file_hdr, in_des); + break; + } + if (archive_format == arf_crcascii + && !strncmp ((char *) file_hdr, "070702", 6)) + { + if (bytes_skipped > 0) + error (0, 0, "warning: skipped %ld bytes of junk", bytes_skipped); + read_in_new_ascii (file_hdr, in_des); + break; + } + if ( (archive_format == arf_oldascii || archive_format == arf_hpoldascii) + && !strncmp ((char *) file_hdr, "070707", 6)) + { + if (bytes_skipped > 0) + error (0, 0, "warning: skipped %ld bytes of junk", bytes_skipped); + read_in_old_ascii (file_hdr, in_des); + break; + } + if ( (archive_format == arf_binary || archive_format == arf_hpbinary) + && (file_hdr->c_magic == 070707 + || file_hdr->c_magic == swab_short ((unsigned short) 070707))) + { + /* Having to skip 1 byte because of word alignment is normal. */ + if (bytes_skipped > 0) + error (0, 0, "warning: skipped %ld bytes of junk", bytes_skipped); + read_in_binary (file_hdr, in_des); + break; + } + bytes_skipped++; + bcopy ((char *) file_hdr + 1, (char *) file_hdr, 5); + copy_in_buf ((char *) file_hdr + 5, in_des, 1L); + } +} + +/* Fill in FILE_HDR by reading an old-format ASCII format cpio header from + file descriptor IN_DES, except for the magic number, which is + already filled in. */ + +void +read_in_old_ascii (file_hdr, in_des) + struct new_cpio_header *file_hdr; + int in_des; +{ + char ascii_header[78]; + unsigned long dev; + unsigned long rdev; + + copy_in_buf (ascii_header, in_des, 70L); + ascii_header[70] = '\0'; + sscanf (ascii_header, + "%6lo%6lo%6lo%6lo%6lo%6lo%6lo%11lo%6lo%11lo", + &dev, &file_hdr->c_ino, + &file_hdr->c_mode, &file_hdr->c_uid, &file_hdr->c_gid, + &file_hdr->c_nlink, &rdev, &file_hdr->c_mtime, + &file_hdr->c_namesize, &file_hdr->c_filesize); + file_hdr->c_dev_maj = major (dev); + file_hdr->c_dev_min = minor (dev); + file_hdr->c_rdev_maj = major (rdev); + file_hdr->c_rdev_min = minor (rdev); + + /* Read file name from input. */ + if (file_hdr->c_name != NULL) + free (file_hdr->c_name); + file_hdr->c_name = (char *) xmalloc (file_hdr->c_namesize + 1); + copy_in_buf (file_hdr->c_name, in_des, (long) file_hdr->c_namesize); +#ifndef __MSDOS__ + /* HP/UX cpio creates archives that look just like ordinary archives, + but for devices it sets major = 0, minor = 1, and puts the + actual major/minor number in the filesize field. See if this + is an HP/UX cpio archive, and if so fix it. We have to do this + here because process_copy_in() assumes filesize is always 0 + for devices. */ + switch (file_hdr->c_mode & CP_IFMT) + { + case CP_IFCHR: + case CP_IFBLK: +#ifdef CP_IFSOCK + case CP_IFSOCK: +#endif +#ifdef CP_IFIFO + case CP_IFIFO: +#endif + if (file_hdr->c_filesize != 0 + && file_hdr->c_rdev_maj == 0 + && file_hdr->c_rdev_min == 1) + { + file_hdr->c_rdev_maj = major (file_hdr->c_filesize); + file_hdr->c_rdev_min = minor (file_hdr->c_filesize); + file_hdr->c_filesize = 0; + } + break; + default: + break; + } +#endif /* __MSDOS__ */ +} + +/* Fill in FILE_HDR by reading a new-format ASCII format cpio header from + file descriptor IN_DES, except for the magic number, which is + already filled in. */ + +void +read_in_new_ascii (file_hdr, in_des) + struct new_cpio_header *file_hdr; + int in_des; +{ + char ascii_header[112]; + + copy_in_buf (ascii_header, in_des, 104L); + ascii_header[104] = '\0'; + sscanf (ascii_header, + "%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx", + &file_hdr->c_ino, &file_hdr->c_mode, &file_hdr->c_uid, + &file_hdr->c_gid, &file_hdr->c_nlink, &file_hdr->c_mtime, + &file_hdr->c_filesize, &file_hdr->c_dev_maj, &file_hdr->c_dev_min, + &file_hdr->c_rdev_maj, &file_hdr->c_rdev_min, &file_hdr->c_namesize, + &file_hdr->c_chksum); + /* Read file name from input. */ + if (file_hdr->c_name != NULL) + free (file_hdr->c_name); + file_hdr->c_name = (char *) xmalloc (file_hdr->c_namesize); + copy_in_buf (file_hdr->c_name, in_des, (long) file_hdr->c_namesize); + + /* In SVR4 ASCII format, the amount of space allocated for the header + is rounded up to the next long-word, so we might need to drop + 1-3 bytes. */ + skip_padding (in_des, file_hdr->c_namesize + 110); +} + +/* Fill in FILE_HDR by reading a binary format cpio header from + file descriptor IN_DES, except for the first 6 bytes (the magic + number, device, and inode number), which are already filled in. */ + +void +read_in_binary (file_hdr, in_des) + struct new_cpio_header *file_hdr; + int in_des; +{ + struct old_cpio_header short_hdr; + + /* Copy the data into the short header, then later transfer + it into the argument long header. */ + short_hdr.c_dev = ((struct old_cpio_header *) file_hdr)->c_dev; + short_hdr.c_ino = ((struct old_cpio_header *) file_hdr)->c_ino; + copy_in_buf (((char *) &short_hdr) + 6, in_des, 20L); + + /* If the magic number is byte swapped, fix the header. */ + if (file_hdr->c_magic == swab_short ((unsigned short) 070707)) + { + static int warned = 0; + + /* Alert the user that they might have to do byte swapping on + the file contents. */ + if (warned == 0) + { + error (0, 0, "warning: archive header has reverse byte-order"); + warned = 1; + } + swab_array ((char *) &short_hdr, 13); + } + + file_hdr->c_dev_maj = major (short_hdr.c_dev); + file_hdr->c_dev_min = minor (short_hdr.c_dev); + file_hdr->c_ino = short_hdr.c_ino; + file_hdr->c_mode = short_hdr.c_mode; + file_hdr->c_uid = short_hdr.c_uid; + file_hdr->c_gid = short_hdr.c_gid; + file_hdr->c_nlink = short_hdr.c_nlink; + file_hdr->c_rdev_maj = major (short_hdr.c_rdev); + file_hdr->c_rdev_min = minor (short_hdr.c_rdev); + file_hdr->c_mtime = (unsigned long) short_hdr.c_mtimes[0] << 16 + | short_hdr.c_mtimes[1]; + + file_hdr->c_namesize = short_hdr.c_namesize; + file_hdr->c_filesize = (unsigned long) short_hdr.c_filesizes[0] << 16 + | short_hdr.c_filesizes[1]; + + /* Read file name from input. */ + if (file_hdr->c_name != NULL) + free (file_hdr->c_name); + file_hdr->c_name = (char *) xmalloc (file_hdr->c_namesize); + copy_in_buf (file_hdr->c_name, in_des, (long) file_hdr->c_namesize); + + /* In binary mode, the amount of space allocated in the header for + the filename is `c_namesize' rounded up to the next short-word, + so we might need to drop a byte. */ + if (file_hdr->c_namesize % 2) + toss_input (in_des, 1L); + +#ifndef __MSDOS__ + /* HP/UX cpio creates archives that look just like ordinary archives, + but for devices it sets major = 0, minor = 1, and puts the + actual major/minor number in the filesize field. See if this + is an HP/UX cpio archive, and if so fix it. We have to do this + here because process_copy_in() assumes filesize is always 0 + for devices. */ + switch (file_hdr->c_mode & CP_IFMT) + { + case CP_IFCHR: + case CP_IFBLK: +#ifdef CP_IFSOCK + case CP_IFSOCK: +#endif +#ifdef CP_IFIFO + case CP_IFIFO: +#endif + if (file_hdr->c_filesize != 0 + && file_hdr->c_rdev_maj == 0 + && file_hdr->c_rdev_min == 1) + { + file_hdr->c_rdev_maj = major (file_hdr->c_filesize); + file_hdr->c_rdev_min = minor (file_hdr->c_filesize); + file_hdr->c_filesize = 0; + } + break; + default: + break; + } +#endif /* __MSDOS__ */ +} + +/* Exchange the bytes of each element of the array of COUNT shorts + starting at PTR. */ + +void +swab_array (ptr, count) + char *ptr; + int count; +{ + char tmp; + + while (count-- > 0) + { + tmp = *ptr; + *ptr = *(ptr + 1); + ++ptr; + *ptr = tmp; + ++ptr; + } +} + +/* Current time for verbose table. */ +static time_t current_time; + +/* Read the collection from standard input and create files + in the file system. */ + +void +process_copy_in () +{ + char done = FALSE; /* True if trailer reached. */ + int res; /* Result of various function calls. */ + dynamic_string new_name; /* New file name for rename option. */ + FILE *tty_in; /* Interactive file for rename option. */ + FILE *tty_out; /* Interactive file for rename option. */ + char *str_res; /* Result for string function. */ + struct utimbuf times; /* For setting file times. */ + struct stat file_stat; /* Output file stat record. */ + struct new_cpio_header file_hdr; /* Output header information. */ + int out_file_des; /* Output file descriptor. */ + int in_file_des; /* Input file descriptor. */ + char skip_file; /* Flag for use with patterns. */ + int existing_dir; /* True if file is a dir & already exists. */ + int i; /* Loop index variable. */ + char *link_name = NULL; /* Name of hard and symbolic links. */ +#ifdef HPUX_CDF + int cdf_flag; /* True if file is a CDF. */ + int cdf_char; /* Index of `+' char indicating a CDF. */ +#endif + + /* Initialize the copy in. */ + if (pattern_file_name) + read_pattern_file (); + file_hdr.c_name = NULL; + ds_init (&new_name, 128); + /* Initialize this in case it has members we don't know to set. */ + bzero (×, sizeof (struct utimbuf)); + + /* Open interactive file pair for rename operation. */ + if (rename_flag) + { + tty_in = fopen (CONSOLE, "r"); + if (tty_in == NULL) + error (2, errno, CONSOLE); + tty_out = fopen (CONSOLE, "w"); + if (tty_out == NULL) + error (2, errno, CONSOLE); + } + + /* Get date and time if needed for processing the table option. */ + if (table_flag && verbose_flag) + time (¤t_time); + +#ifdef __MSDOS__ + setmode (archive_des, O_BINARY); +#endif + /* Check whether the input file might be a tape. */ + in_file_des = archive_des; + if (_isrmt (in_file_des)) + { + input_is_special = 1; + input_is_seekable = 0; + } + else + { + if (fstat (in_file_des, &file_stat)) + error (1, errno, "standard input is closed"); + input_is_special = +#ifdef S_ISBLK + S_ISBLK (file_stat.st_mode) || +#endif + S_ISCHR (file_stat.st_mode); + input_is_seekable = S_ISREG (file_stat.st_mode); + } + output_is_seekable = TRUE; + + /* While there is more input in the collection, process the input. */ + while (!done) + { + link_name = NULL; + swapping_halfwords = swapping_bytes = FALSE; + + /* Start processing the next file by reading the header. */ + read_in_header (&file_hdr, in_file_des); + +#ifdef DEBUG_CPIO + if (debug_flag) + { + struct new_cpio_header *h; + h = &file_hdr; + fprintf (stderr, + "magic = 0%o, ino = %d, mode = 0%o, uid = %d, gid = %d\n", + h->c_magic, h->c_ino, h->c_mode, h->c_uid, h->c_gid); + fprintf (stderr, + "nlink = %d, mtime = %d, filesize = %d, dev_maj = 0x%x\n", + h->c_nlink, h->c_mtime, h->c_filesize, h->c_dev_maj); + fprintf (stderr, + "dev_min = 0x%x, rdev_maj = 0x%x, rdev_min = 0x%x, namesize = %d\n", + h->c_dev_min, h->c_rdev_maj, h->c_rdev_min, h->c_namesize); + fprintf (stderr, + "chksum = %d, name = \"%s\", tar_linkname = \"%s\"\n", + h->c_chksum, h->c_name, + h->c_tar_linkname ? h->c_tar_linkname : "(null)" ); + + } +#endif + /* Is this the header for the TRAILER file? */ + if (strcmp ("TRAILER!!!", file_hdr.c_name) == 0) + { + done = TRUE; + break; + } + + /* Does the file name match one of the given patterns? */ + if (num_patterns <= 0) + skip_file = FALSE; + else + { + skip_file = copy_matching_files; + for (i = 0; i < num_patterns + && skip_file == copy_matching_files; i++) + { + if (fnmatch (save_patterns[i], file_hdr.c_name, 0) == 0) + skip_file = !copy_matching_files; + } + } + + if (skip_file) + { + toss_input (in_file_des, file_hdr.c_filesize); + skip_padding (in_file_des, file_hdr.c_filesize); + } + else if (table_flag) + { + if (verbose_flag) + { +#ifdef CP_IFLNK + if ((file_hdr.c_mode & CP_IFMT) == CP_IFLNK) + { + if (archive_format != arf_tar && archive_format != arf_ustar) + { + link_name = (char *) xmalloc ((unsigned int) file_hdr.c_filesize + 1); + link_name[file_hdr.c_filesize] = '\0'; + copy_in_buf (link_name, in_file_des, file_hdr.c_filesize); + long_format (&file_hdr, link_name); + free (link_name); + skip_padding (in_file_des, file_hdr.c_filesize); + continue; + } + else + { + long_format (&file_hdr, file_hdr.c_tar_linkname); + continue; + } + } + else +#endif + long_format (&file_hdr, (char *) 0); + } + else + printf ("%s\n", file_hdr.c_name); + + toss_input (in_file_des, file_hdr.c_filesize); + skip_padding (in_file_des, file_hdr.c_filesize); + } + else if (append_flag) + { + toss_input (in_file_des, file_hdr.c_filesize); + skip_padding (in_file_des, file_hdr.c_filesize); + } + else + { + /* Copy the input file into the directory structure. */ + + /* Do we need to rename the file? */ + if (rename_flag) + { + fprintf (tty_out, "rename %s -> ", file_hdr.c_name); + fflush (tty_out); + str_res = ds_fgets (tty_in, &new_name); + if (str_res == NULL || str_res[0] == 0) + { + toss_input (in_file_des, file_hdr.c_filesize); + skip_padding (in_file_des, file_hdr.c_filesize); + continue; + } + else + file_hdr.c_name = xstrdup (new_name.ds_string); + } + + /* See if the file already exists. */ + existing_dir = FALSE; + if (lstat (file_hdr.c_name, &file_stat) == 0) + { + if (S_ISDIR (file_stat.st_mode) + && ((file_hdr.c_mode & CP_IFMT) == CP_IFDIR)) + { + /* If there is already a directory there that + we are trying to create, don't complain about + it. */ + existing_dir = TRUE; + } + else if (!unconditional_flag + && file_hdr.c_mtime <= file_stat.st_mtime) + { + error (0, 0, "%s not created: newer or same age version exists", + file_hdr.c_name); + toss_input (in_file_des, file_hdr.c_filesize); + skip_padding (in_file_des, file_hdr.c_filesize); + continue; /* Go to the next file. */ + } + else if (S_ISDIR (file_stat.st_mode) + ? rmdir (file_hdr.c_name) + : unlink (file_hdr.c_name)) + { + error (0, errno, "cannot remove current %s", + file_hdr.c_name); + toss_input (in_file_des, file_hdr.c_filesize); + skip_padding (in_file_des, file_hdr.c_filesize); + continue; /* Go to the next file. */ + } + } + + /* Do the real copy or link. */ + switch (file_hdr.c_mode & CP_IFMT) + { + case CP_IFREG: +#ifndef __MSDOS__ + /* Can the current file be linked to a previously copied file? */ + if (file_hdr.c_nlink > 1 && (archive_format == arf_newascii + || archive_format == arf_crcascii) ) + { + int link_res; + if (file_hdr.c_filesize == 0) + { + /* The newc and crc formats store multiply linked copies + of the same file in the archive only once. The + actual data is attached to the last link in the + archive, and the other links all have a filesize + of 0. Since this file has multiple links and a + filesize of 0, its data is probably attatched to + another file in the archive. Save the link, and + process it later when we get the actual data. We + can't just create it with length 0 and add the + data later, in case the file is readonly. We still + lose if its parent directory is readonly (and we aren't + running as root), but there's nothing we can do about + that. */ + defer_copyin (&file_hdr); + toss_input (in_file_des, file_hdr.c_filesize); + skip_padding (in_file_des, file_hdr.c_filesize); + break; + } + /* If the file has data (filesize != 0), then presumably + any other links have already been defer_copyin'ed(), + but GNU cpio version 2.0-2.2 didn't do that, so we + still have to check for links here (and also in case + the archive was created and later appeneded to). */ + link_res = link_to_maj_min_ino (file_hdr.c_name, + file_hdr.c_dev_maj, file_hdr.c_dev_maj, + file_hdr.c_ino); + if (link_res == 0) + { + toss_input (in_file_des, file_hdr.c_filesize); + skip_padding (in_file_des, file_hdr.c_filesize); + break; + } + } + else if (file_hdr.c_nlink > 1 && archive_format != arf_tar + && archive_format != arf_ustar) + { + int link_res; + link_res = link_to_maj_min_ino (file_hdr.c_name, + file_hdr.c_dev_maj, file_hdr.c_dev_maj, + file_hdr.c_ino); + if (link_res == 0) + { + toss_input (in_file_des, file_hdr.c_filesize); + skip_padding (in_file_des, file_hdr.c_filesize); + break; + } + } + else if ((archive_format == arf_tar || archive_format == arf_ustar) + && file_hdr.c_tar_linkname && + file_hdr.c_tar_linkname[0] != '\0') + { + int link_res; + link_res = link_to_name (file_hdr.c_name, + file_hdr.c_tar_linkname); + if (link_res < 0) + { + error (0, errno, "cannot link %s to %s", + file_hdr.c_tar_linkname, file_hdr.c_name); + } + break; + } +#endif + + /* If not linked, copy the contents of the file. */ + if (link_name == NULL) + { + out_file_des = open (file_hdr.c_name, + O_CREAT | O_WRONLY | O_BINARY, 0600); + if (out_file_des < 0 && create_dir_flag) + { + create_all_directories (file_hdr.c_name); + out_file_des = open (file_hdr.c_name, + O_CREAT | O_WRONLY | O_BINARY, + 0600); + } + if (out_file_des < 0) + { + error (0, errno, "%s", file_hdr.c_name); + toss_input (in_file_des, file_hdr.c_filesize); + skip_padding (in_file_des, file_hdr.c_filesize); + continue; + } + + crc = 0; + if (swap_halfwords_flag) + { + if ((file_hdr.c_filesize % 4) == 0) + swapping_halfwords = TRUE; + else + error (0, 0, "cannot swap halfwords of %s: odd number of halfwords", + file_hdr.c_name); + } + if (swap_bytes_flag) + { + if ((file_hdr.c_filesize % 2) == 0) + swapping_bytes = TRUE; + else + error (0, 0, "cannot swap bytes of %s: odd number of bytes", + file_hdr.c_name); + } + copy_files (in_file_des, out_file_des, file_hdr.c_filesize); + empty_output_buffer (out_file_des); + if (close (out_file_des) < 0) + error (0, errno, "%s", file_hdr.c_name); + + if (archive_format == arf_crcascii) + { + if (crc != file_hdr.c_chksum) + error (0, 0, "%s: checksum error (0x%x, should be 0x%x)", + file_hdr.c_name, crc, file_hdr.c_chksum); + } + /* File is now copied; set attributes. */ + if (!no_chown_flag) + if ((chown (file_hdr.c_name, + set_owner_flag ? set_owner : file_hdr.c_uid, + set_group_flag ? set_group : file_hdr.c_gid) < 0) + && errno != EPERM) + error (0, errno, "%s", file_hdr.c_name); + /* chown may have turned off some permissions we wanted. */ + if (chmod (file_hdr.c_name, (int) file_hdr.c_mode) < 0) + error (0, errno, "%s", file_hdr.c_name); + if (retain_time_flag) + { + times.actime = times.modtime = file_hdr.c_mtime; + if (utime (file_hdr.c_name, ×) < 0) + error (0, errno, "%s", file_hdr.c_name); + } + skip_padding (in_file_des, file_hdr.c_filesize); + if (file_hdr.c_nlink > 1 && (archive_format == arf_newascii + || archive_format == arf_crcascii) ) + { + /* (see comment above for how the newc and crc formats + store multiple links). Now that we have the data + for this file, create any other links to it which + we defered. */ + create_defered_links (&file_hdr); + } + } + break; + + case CP_IFDIR: + /* Strip any trailing `/'s off the filename; tar puts + them on. We might as well do it here in case anybody + else does too, since they cause strange things to happen. */ + strip_trailing_slashes (file_hdr.c_name); + + /* Ignore the current directory. It must already exist, + and we don't want to change its permission, ownership + or time. */ + if (file_hdr.c_name[0] == '.' && file_hdr.c_name[1] == '\0') + break; + +#ifdef HPUX_CDF + cdf_flag = 0; +#endif + if (!existing_dir) + + { +#ifdef HPUX_CDF + /* If the directory name ends in a + and is SUID, + then it is a CDF. Strip the trailing + from + the name before creating it. */ + cdf_char = strlen (file_hdr.c_name) - 1; + if ( (cdf_char > 0) && + (file_hdr.c_mode & 04000) && + (file_hdr.c_name [cdf_char] == '+') ) + { + file_hdr.c_name [cdf_char] = '\0'; + cdf_flag = 1; + } +#endif + res = mkdir (file_hdr.c_name, file_hdr.c_mode); + } + else + res = 0; + if (res < 0 && create_dir_flag) + { + create_all_directories (file_hdr.c_name); + res = mkdir (file_hdr.c_name, file_hdr.c_mode); + } + if (res < 0) + { + error (0, errno, "%s", file_hdr.c_name); + continue; + } + if (!no_chown_flag) + if ((chown (file_hdr.c_name, + set_owner_flag ? set_owner : file_hdr.c_uid, + set_group_flag ? set_group : file_hdr.c_gid) < 0) + && errno != EPERM) + error (0, errno, "%s", file_hdr.c_name); + /* chown may have turned off some permissions we wanted. */ + if (chmod (file_hdr.c_name, (int) file_hdr.c_mode) < 0) + error (0, errno, "%s", file_hdr.c_name); +#ifdef HPUX_CDF + if (cdf_flag) + /* Once we "hide" the directory with the chmod(), + we have to refer to it using name+ instead of name. */ + file_hdr.c_name [cdf_char] = '+'; +#endif + if (retain_time_flag) + { + times.actime = times.modtime = file_hdr.c_mtime; + if (utime (file_hdr.c_name, ×) < 0) + error (0, errno, "%s", file_hdr.c_name); + } + break; + +#ifndef __MSDOS__ + case CP_IFCHR: + case CP_IFBLK: +#ifdef CP_IFSOCK + case CP_IFSOCK: +#endif +#ifdef CP_IFIFO + case CP_IFIFO: +#endif + if (file_hdr.c_nlink > 1 && archive_format != arf_tar + && archive_format != arf_ustar) + { + int link_res; + link_res = link_to_maj_min_ino (file_hdr.c_name, + file_hdr.c_dev_maj, file_hdr.c_dev_maj, + file_hdr.c_ino); + if (link_res == 0) + break; + } + else if (archive_format == arf_ustar && + file_hdr.c_tar_linkname && + file_hdr.c_tar_linkname [0] != '\0') + { + int link_res; + link_res = link_to_name (file_hdr.c_name, + file_hdr.c_tar_linkname); + if (link_res < 0) + { + error (0, errno, "cannot link %s to %s", + file_hdr.c_tar_linkname, file_hdr.c_name); + /* Something must be wrong, because we couldn't + find the file to link to. But can we assume + that the device maj/min numbers are correct + and fall through to the mknod? It's probably + safer to just break, rather than possibly + creating a bogus device file. */ + } + break; + } + + res = mknod (file_hdr.c_name, file_hdr.c_mode, + makedev (file_hdr.c_rdev_maj, file_hdr.c_rdev_min)); + if (res < 0 && create_dir_flag) + { + create_all_directories (file_hdr.c_name); + res = mknod (file_hdr.c_name, file_hdr.c_mode, + makedev (file_hdr.c_rdev_maj, file_hdr.c_rdev_min)); + } + if (res < 0) + { + error (0, errno, "%s", file_hdr.c_name); + continue; + } + if (!no_chown_flag) + if ((chown (file_hdr.c_name, + set_owner_flag ? set_owner : file_hdr.c_uid, + set_group_flag ? set_group : file_hdr.c_gid) < 0) + && errno != EPERM) + error (0, errno, "%s", file_hdr.c_name); + /* chown may have turned off some permissions we wanted. */ + if (chmod (file_hdr.c_name, file_hdr.c_mode) < 0) + error (0, errno, "%s", file_hdr.c_name); + if (retain_time_flag) + { + times.actime = times.modtime = file_hdr.c_mtime; + if (utime (file_hdr.c_name, ×) < 0) + error (0, errno, "%s", file_hdr.c_name); + } + break; +#endif + +#ifdef CP_IFLNK + case CP_IFLNK: + { + if (archive_format != arf_tar && archive_format != arf_ustar) + { + link_name = (char *) xmalloc ((unsigned int) file_hdr.c_filesize + 1); + link_name[file_hdr.c_filesize] = '\0'; + copy_in_buf (link_name, in_file_des, file_hdr.c_filesize); + skip_padding (in_file_des, file_hdr.c_filesize); + } + else + { + link_name = xstrdup (file_hdr.c_tar_linkname); + } + + res = UMASKED_SYMLINK (link_name, file_hdr.c_name, + file_hdr.c_mode); + if (res < 0 && create_dir_flag) + { + create_all_directories (file_hdr.c_name); + res = UMASKED_SYMLINK (link_name, file_hdr.c_name, + file_hdr.c_mode); + } + if (res < 0) + { + error (0, errno, "%s", file_hdr.c_name); + free (link_name); + link_name = NULL; + continue; + } + if (!no_chown_flag) + if ((lchown (file_hdr.c_name, + set_owner_flag ? set_owner : file_hdr.c_uid, + set_group_flag ? set_group : file_hdr.c_gid) < 0) + && errno != EPERM) + error (0, errno, "%s", file_hdr.c_name); + free (link_name); + link_name = NULL; + } + break; +#endif + + default: + error (0, 0, "%s: unknown file type", file_hdr.c_name); + toss_input (in_file_des, file_hdr.c_filesize); + skip_padding (in_file_des, file_hdr.c_filesize); + } + + if (verbose_flag) + fprintf (stderr, "%s\n", file_hdr.c_name); + if (dot_flag) + fputc ('.', stderr); + } + } + + if (dot_flag) + fputc ('\n', stderr); + + if (append_flag) + return; + + if (archive_format == arf_newascii || archive_format == arf_crcascii) + create_final_defers (); + res = (input_bytes + io_block_size - 1) / io_block_size; + if (res == 1) + fprintf (stderr, "1 block\n"); + else + fprintf (stderr, "%d blocks\n", res); +} + +/* Print the file described by FILE_HDR in long format. + If LINK_NAME is nonzero, it is the name of the file that + this file is a symbolic link to. */ + +void +long_format (file_hdr, link_name) + struct new_cpio_header *file_hdr; + char *link_name; +{ + char mbuf[11]; + char tbuf[40]; + time_t when; + + mode_string (file_hdr->c_mode, mbuf); + mbuf[10] = '\0'; + + /* Get time values ready to print. */ + when = file_hdr->c_mtime; + strcpy (tbuf, ctime (&when)); + if (current_time - when > 6L * 30L * 24L * 60L * 60L + || current_time - when < 0L) + { + /* The file is older than 6 months, or in the future. + Show the year instead of the time of day. */ + strcpy (tbuf + 11, tbuf + 19); + } + tbuf[16] = '\0'; + + printf ("%s %3u ", mbuf, file_hdr->c_nlink); + +#ifndef __MSDOS__ + if (numeric_uid) +#endif + printf ("%-8u %-8u ", (unsigned int) file_hdr->c_uid, + (unsigned int) file_hdr->c_gid); +#ifndef __MSDOS__ + else + printf ("%-8.8s %-8.8s ", getuser (file_hdr->c_uid), + getgroup (file_hdr->c_gid)); + + if ((file_hdr->c_mode & CP_IFMT) == CP_IFCHR + || (file_hdr->c_mode & CP_IFMT) == CP_IFBLK) + printf ("%3u, %3u ", file_hdr->c_rdev_maj, + file_hdr->c_rdev_min); + else +#endif + printf ("%8lu ", file_hdr->c_filesize); + + printf ("%s ", tbuf + 4); + + print_name_with_quoting (file_hdr->c_name); + if (link_name) + { + printf (" -> "); + print_name_with_quoting (link_name); + } + putc ('\n', stdout); +} + +void +print_name_with_quoting (p) + register char *p; +{ + register unsigned char c; + + while (c = *p++) + { + switch (c) + { +#ifndef __MSDOS__ + case '\\': + printf ("\\\\"); + break; +#endif + + case '\n': + printf ("\\n"); + break; + + case '\b': + printf ("\\b"); + break; + + case '\r': + printf ("\\r"); + break; + + case '\t': + printf ("\\t"); + break; + + case '\f': + printf ("\\f"); + break; + + case ' ': + printf ("\\ "); + break; + + case '"': + printf ("\\\""); + break; + + default: + if (c > 040 && +#ifdef __MSDOS__ + c < 0377 && c != 0177 +#else + c < 0177 +#endif + ) + putchar (c); + else + printf ("\\%03o", (unsigned int) c); + } + } +} + +/* Read a pattern file (for the -E option). Put a list of + `num_patterns' elements in `save_patterns'. Any patterns that were + already in `save_patterns' (from the command line) are preserved. */ + +static void +read_pattern_file () +{ + int max_new_patterns; + char **new_save_patterns; + int new_num_patterns; + int i; + dynamic_string pattern_name; + FILE *pattern_fp; + + if (num_patterns < 0) + num_patterns = 0; + max_new_patterns = 1 + num_patterns; + new_save_patterns = (char **) xmalloc (max_new_patterns * sizeof (char *)); + new_num_patterns = num_patterns; + ds_init (&pattern_name, 128); + + pattern_fp = fopen (pattern_file_name, "r"); + if (pattern_fp == NULL) + error (1, errno, "%s", pattern_file_name); + while (ds_fgetstr (pattern_fp, &pattern_name, '\n') != NULL) + { + if (new_num_patterns >= max_new_patterns) + { + max_new_patterns += 1; + new_save_patterns = (char **) + xrealloc ((char *) new_save_patterns, + max_new_patterns * sizeof (char *)); + } + new_save_patterns[new_num_patterns] = xstrdup (pattern_name.ds_string); + ++new_num_patterns; + } + if (ferror (pattern_fp) || fclose (pattern_fp) == EOF) + error (1, errno, "%s", pattern_file_name); + + for (i = 0; i < num_patterns; ++i) + new_save_patterns[i] = save_patterns[i]; + + save_patterns = new_save_patterns; + num_patterns = new_num_patterns; +} + +/* Skip the padding on IN_FILE_DES after a header or file, + up to the next header. + The number of bytes skipped is based on OFFSET -- the current offset + from the last start of a header (or file) -- and the current + header type. */ + +static void +skip_padding (in_file_des, offset) + int in_file_des; + int offset; +{ + int pad; + + if (archive_format == arf_crcascii || archive_format == arf_newascii) + pad = (4 - (offset % 4)) % 4; + else if (archive_format == arf_binary || archive_format == arf_hpbinary) + pad = (2 - (offset % 2)) % 2; + else if (archive_format == arf_tar || archive_format == arf_ustar) + pad = (512 - (offset % 512)) % 512; + else + pad = 0; + + if (pad != 0) + toss_input (in_file_des, pad); +} + + +/* The newc and crc formats store multiply linked copies of the same file + in the archive only once. The actual data is attached to the last link + in the archive, and the other links all have a filesize of 0. When a + file in the archive has multiple links and a filesize of 0, its data is + probably "attatched" to another file in the archive, so we can't create + it right away. We have to "defer" creating it until we have created + the file that has the data "attatched" to it. We keep a list of the + "defered" links on deferments. */ + +struct deferment *deferments = NULL; + +/* Add a file header to the deferments list. For now they all just + go on one list, although we could optimize this if necessary. */ + +static void +defer_copyin (file_hdr) + struct new_cpio_header *file_hdr; +{ + struct deferment *d; + d = create_deferment (file_hdr); + d->next = deferments; + deferments = d; + return; +} + +/* We just created a file that (probably) has some other links to it + which have been defered. Go through all of the links on the deferments + list and create any which are links to this file. */ + +static void +create_defered_links (file_hdr) + struct new_cpio_header *file_hdr; +{ + struct deferment *d; + struct deferment *d_prev; + int ino; + int maj; + int min; + int link_res; + ino = file_hdr->c_ino; + maj = file_hdr->c_dev_maj; + min = file_hdr->c_dev_min; + d = deferments; + d_prev = NULL; + while (d != NULL) + { + if ( (d->header.c_ino == ino) && (d->header.c_dev_maj == maj) + && (d->header.c_dev_min == min) ) + { + struct deferment *d_free; + link_res = link_to_name (d->header.c_name, file_hdr->c_name); + if (link_res < 0) + { + error (0, errno, "cannot link %s to %s", + d->header.c_name, file_hdr->c_name); + } + if (d_prev != NULL) + d_prev->next = d->next; + else + deferments = d->next; + d_free = d; + d = d->next; + free_deferment (d_free); + } + else + { + d_prev = d; + d = d->next; + } + } +} + +/* If we had a multiply linked file that really was empty then we would + have defered all of its links, since we never found any with data + "attached", and they will still be on the deferment list even when + we are done reading the whole archive. Write out all of these + empty links that are still on the deferments list. */ + +static void +create_final_defers () +{ + struct deferment *d; + struct deferment *d_prev; + struct new_cpio_header *h; + int link_res; + int out_file_des; + struct utimbuf times; /* For setting file times. */ + /* Initialize this in case it has members we don't know to set. */ + bzero (×, sizeof (struct utimbuf)); + + for (d = deferments; d != NULL; d = d->next) + { + d = deferments; + link_res = link_to_maj_min_ino (d->header.c_name, + d->header.c_dev_maj, d->header.c_dev_maj, + d->header.c_ino); + if (link_res == 0) + { + continue; + } + out_file_des = open (d->header.c_name, + O_CREAT | O_WRONLY | O_BINARY, 0600); + if (out_file_des < 0 && create_dir_flag) + { + create_all_directories (d->header.c_name); + out_file_des = open (d->header.c_name, + O_CREAT | O_WRONLY | O_BINARY, + 0600); + } + if (out_file_des < 0) + { + error (0, errno, "%s", d->header.c_name); + continue; + } + + if (close (out_file_des) < 0) + error (0, errno, "%s", d->header.c_name); + + /* File is now copied; set attributes. */ + if (!no_chown_flag) + if ((chown (d->header.c_name, + set_owner_flag ? set_owner : d->header.c_uid, + set_group_flag ? set_group : d->header.c_gid) < 0) + && errno != EPERM) + error (0, errno, "%s", d->header.c_name); + /* chown may have turned off some permissions we wanted. */ + if (chmod (d->header.c_name, (int) d->header.c_mode) < 0) + error (0, errno, "%s", d->header.c_name); + if (retain_time_flag) + { + times.actime = times.modtime = d->header.c_mtime; + if (utime (d->header.c_name, ×) < 0) + error (0, errno, "%s", d->header.c_name); + } + } +} diff --git a/gnu/usr.bin/cpio/copyout.c b/gnu/usr.bin/cpio/copyout.c new file mode 100644 index 0000000000..f763f2b7ca --- /dev/null +++ b/gnu/usr.bin/cpio/copyout.c @@ -0,0 +1,801 @@ +/* copyout.c - create a cpio archive + Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include "filetypes.h" +#include "system.h" +#include "cpiohdr.h" +#include "dstring.h" +#include "extern.h" +#include "defer.h" +#include "rmt.h" + +static unsigned long read_for_checksum (); +static void clear_rest_of_block (); +static void pad_output (); +static int last_link (); +static int count_defered_links_to_dev_ino (); +static void add_link_defer (); +static void writeout_other_defers (); +static void writeout_final_defers(); +static void writeout_defered_file (); + +/* Write out header FILE_HDR, including the file name, to file + descriptor OUT_DES. */ + +void +write_out_header (file_hdr, out_des) + struct new_cpio_header *file_hdr; + int out_des; +{ + if (archive_format == arf_newascii || archive_format == arf_crcascii) + { + char ascii_header[112]; + char *magic_string; + + if (archive_format == arf_crcascii) + magic_string = "070702"; + else + magic_string = "070701"; + sprintf (ascii_header, + "%6s%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx", + magic_string, + file_hdr->c_ino, file_hdr->c_mode, file_hdr->c_uid, + file_hdr->c_gid, file_hdr->c_nlink, file_hdr->c_mtime, + file_hdr->c_filesize, file_hdr->c_dev_maj, file_hdr->c_dev_min, + file_hdr->c_rdev_maj, file_hdr->c_rdev_min, file_hdr->c_namesize, + file_hdr->c_chksum); + copy_buf_out (ascii_header, out_des, 110L); + + /* Write file name to output. */ + copy_buf_out (file_hdr->c_name, out_des, (long) file_hdr->c_namesize); + pad_output (out_des, file_hdr->c_namesize + 110); + } + else if (archive_format == arf_oldascii || archive_format == arf_hpoldascii) + { + char ascii_header[78]; +#ifndef __MSDOS__ + dev_t dev; + dev_t rdev; + + if (archive_format == arf_oldascii) + { + dev = makedev (file_hdr->c_dev_maj, file_hdr->c_dev_min); + rdev = makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min); + } + else + { + /* HP/UX cpio creates archives that look just like ordinary archives, + but for devices it sets major = 0, minor = 1, and puts the + actual major/minor number in the filesize field. */ + switch (file_hdr->c_mode & CP_IFMT) + { + case CP_IFCHR: + case CP_IFBLK: +#ifdef CP_IFSOCK + case CP_IFSOCK: +#endif +#ifdef CP_IFIFO + case CP_IFIFO: +#endif + file_hdr->c_filesize = makedev (file_hdr->c_rdev_maj, + file_hdr->c_rdev_min); + rdev = 1; + break; + default: + dev = makedev (file_hdr->c_dev_maj, file_hdr->c_dev_min); + rdev = makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min); + break; + } + } +#else + int dev = 0, rdev = 0; +#endif + + if ((file_hdr->c_ino >> 16) != 0) + error (0, 0, "%s: truncating inode number", file_hdr->c_name); + + sprintf (ascii_header, + "%06lo%06lo%06lo%06lo%06lo%06lo%06lo%06lo%011lo%06lo%011lo", + file_hdr->c_magic & 0xFFFF, dev & 0xFFFF, + file_hdr->c_ino & 0xFFFF, file_hdr->c_mode & 0xFFFF, + file_hdr->c_uid & 0xFFFF, file_hdr->c_gid & 0xFFFF, + file_hdr->c_nlink & 0xFFFF, rdev & 0xFFFF, + file_hdr->c_mtime, file_hdr->c_namesize & 0xFFFF, + file_hdr->c_filesize); + copy_buf_out (ascii_header, out_des, 76L); + + /* Write file name to output. */ + copy_buf_out (file_hdr->c_name, out_des, (long) file_hdr->c_namesize); + } + else if (archive_format == arf_tar || archive_format == arf_ustar) + { + write_out_tar_header (file_hdr, out_des); + } + else + { + struct old_cpio_header short_hdr; + + short_hdr.c_magic = 070707; + short_hdr.c_dev = makedev (file_hdr->c_dev_maj, file_hdr->c_dev_min); + + if ((file_hdr->c_ino >> 16) != 0) + error (0, 0, "%s: truncating inode number", file_hdr->c_name); + + short_hdr.c_ino = file_hdr->c_ino & 0xFFFF; + short_hdr.c_mode = file_hdr->c_mode & 0xFFFF; + short_hdr.c_uid = file_hdr->c_uid & 0xFFFF; + short_hdr.c_gid = file_hdr->c_gid & 0xFFFF; + short_hdr.c_nlink = file_hdr->c_nlink & 0xFFFF; + if (archive_format != arf_hpbinary) + short_hdr.c_rdev = makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min); + else + { + switch (file_hdr->c_mode & CP_IFMT) + { + /* HP/UX cpio creates archives that look just like ordinary + archives, but for devices it sets major = 0, minor = 1, and + puts the actual major/minor number in the filesize field. */ + case CP_IFCHR: + case CP_IFBLK: +#ifdef CP_IFSOCK + case CP_IFSOCK: +#endif +#ifdef CP_IFIFO + case CP_IFIFO: +#endif + file_hdr->c_filesize = makedev (file_hdr->c_rdev_maj, + file_hdr->c_rdev_min); + short_hdr.c_rdev = makedev (0, 1); + break; + default: + short_hdr.c_rdev = makedev (file_hdr->c_rdev_maj, + file_hdr->c_rdev_min); + break; + } + } + short_hdr.c_mtimes[0] = file_hdr->c_mtime >> 16; + short_hdr.c_mtimes[1] = file_hdr->c_mtime & 0xFFFF; + + short_hdr.c_namesize = file_hdr->c_namesize & 0xFFFF; + + short_hdr.c_filesizes[0] = file_hdr->c_filesize >> 16; + short_hdr.c_filesizes[1] = file_hdr->c_filesize & 0xFFFF; + + /* Output the file header. */ + copy_buf_out ((char *) &short_hdr, out_des, 26L); + + /* Write file name to output. */ + copy_buf_out (file_hdr->c_name, out_des, (long) file_hdr->c_namesize); + + pad_output (out_des, file_hdr->c_namesize + 26); + } +} + +/* Read a list of file names from the standard input + and write a cpio collection on the standard output. + The format of the header depends on the compatibility (-c) flag. */ + +void +process_copy_out () +{ + int res; /* Result of functions. */ + dynamic_string input_name; /* Name of file read from stdin. */ + struct utimbuf times; /* For resetting file times after copy. */ + struct stat file_stat; /* Stat record for file. */ + struct new_cpio_header file_hdr; /* Output header information. */ + int in_file_des; /* Source file descriptor. */ + int out_file_des; /* Output file descriptor. */ + char *p; + + /* Initialize the copy out. */ + ds_init (&input_name, 128); + /* Initialize this in case it has members we don't know to set. */ + bzero (×, sizeof (struct utimbuf)); + file_hdr.c_magic = 070707; + +#ifdef __MSDOS__ + setmode (archive_des, O_BINARY); +#endif + /* Check whether the output file might be a tape. */ + out_file_des = archive_des; + if (_isrmt (out_file_des)) + { + output_is_special = 1; + output_is_seekable = 0; + } + else + { + if (fstat (out_file_des, &file_stat)) + error (1, errno, "standard output is closed"); + output_is_special = +#ifdef S_ISBLK + S_ISBLK (file_stat.st_mode) || +#endif + S_ISCHR (file_stat.st_mode); + output_is_seekable = S_ISREG (file_stat.st_mode); + } + + if (append_flag) + { + process_copy_in (); + prepare_append (out_file_des); + } + + /* Copy files with names read from stdin. */ + while (ds_fgetstr (stdin, &input_name, name_end) != NULL) + { + /* Check for blank line. */ + if (input_name.ds_string[0] == 0) + { + error (0, 0, "blank line ignored"); + continue; + } + + /* Process next file. */ + if ((*xstat) (input_name.ds_string, &file_stat) < 0) + error (0, errno, "%s", input_name.ds_string); + else + { + /* Set values in output header. */ + file_hdr.c_dev_maj = major (file_stat.st_dev); + file_hdr.c_dev_min = minor (file_stat.st_dev); + file_hdr.c_ino = file_stat.st_ino; + /* For POSIX systems that don't define the S_IF macros, + we can't assume that S_ISfoo means the standard Unix + S_IFfoo bit(s) are set. So do it manually, with a + different name. Bleah. */ + file_hdr.c_mode = (file_stat.st_mode & 07777); + if (S_ISREG (file_stat.st_mode)) + file_hdr.c_mode |= CP_IFREG; + else if (S_ISDIR (file_stat.st_mode)) + file_hdr.c_mode |= CP_IFDIR; +#ifdef S_ISBLK + else if (S_ISBLK (file_stat.st_mode)) + file_hdr.c_mode |= CP_IFBLK; +#endif +#ifdef S_ISCHR + else if (S_ISCHR (file_stat.st_mode)) + file_hdr.c_mode |= CP_IFCHR; +#endif +#ifdef S_ISFIFO + else if (S_ISFIFO (file_stat.st_mode)) + file_hdr.c_mode |= CP_IFIFO; +#endif +#ifdef S_ISLNK + else if (S_ISLNK (file_stat.st_mode)) + file_hdr.c_mode |= CP_IFLNK; +#endif +#ifdef S_ISSOCK + else if (S_ISSOCK (file_stat.st_mode)) + file_hdr.c_mode |= CP_IFSOCK; +#endif +#ifdef S_ISNWK + else if (S_ISNWK (file_stat.st_mode)) + file_hdr.c_mode |= CP_IFNWK; +#endif + file_hdr.c_uid = file_stat.st_uid; + file_hdr.c_gid = file_stat.st_gid; + file_hdr.c_nlink = file_stat.st_nlink; + file_hdr.c_rdev_maj = major (file_stat.st_rdev); + file_hdr.c_rdev_min = minor (file_stat.st_rdev); + file_hdr.c_mtime = file_stat.st_mtime; + file_hdr.c_filesize = file_stat.st_size; + file_hdr.c_chksum = 0; + file_hdr.c_tar_linkname = NULL; + + /* Strip leading `./' from the filename. */ + p = input_name.ds_string; + while (*p == '.' && *(p + 1) == '/') + { + ++p; + while (*p == '/') + ++p; + } +#ifndef HPUX_CDF + file_hdr.c_name = p; + file_hdr.c_namesize = strlen (p) + 1; +#else + if ( (archive_format != arf_tar) && (archive_format != arf_ustar) ) + { + /* We mark CDF's in cpio files by adding a 2nd `/' after the + "hidden" directory name. We need to do this so we can + properly recreate the directory as hidden (in case the + files of a directory go into the archive before the + directory itself (e.g from "find ... -depth ... | cpio")). */ + file_hdr.c_name = add_cdf_double_slashes (p); + file_hdr.c_namesize = strlen (file_hdr.c_name) + 1; + } + else + { + /* We don't mark CDF's in tar files. We assume the "hidden" + directory will always go into the archive before any of + its files. */ + file_hdr.c_name = p; + file_hdr.c_namesize = strlen (p) + 1; + } +#endif + if ((archive_format == arf_tar || archive_format == arf_ustar) + && is_tar_filename_too_long (file_hdr.c_name)) + { + error (0, 0, "%s: file name too long", file_hdr.c_name); + continue; + } + + /* Copy the named file to the output. */ + switch (file_hdr.c_mode & CP_IFMT) + { + case CP_IFREG: +#ifndef __MSDOS__ + if (archive_format == arf_tar || archive_format == arf_ustar) + { + char *otherfile; + if ((otherfile = find_inode_file (file_hdr.c_ino, + file_hdr.c_dev_maj, + file_hdr.c_dev_min))) + { + file_hdr.c_tar_linkname = otherfile; + write_out_header (&file_hdr, out_file_des); + break; + } + } + if ( (archive_format == arf_newascii || archive_format == arf_crcascii) + && (file_hdr.c_nlink > 1) ) + { + if (last_link (&file_hdr) ) + { + writeout_other_defers (&file_hdr, out_file_des); + } + else + { + add_link_defer (&file_hdr); + break; + } + } +#endif + in_file_des = open (input_name.ds_string, + O_RDONLY | O_BINARY, 0); + if (in_file_des < 0) + { + error (0, errno, "%s", input_name.ds_string); + continue; + } + + if (archive_format == arf_crcascii) + file_hdr.c_chksum = read_for_checksum (in_file_des, + file_hdr.c_filesize, + input_name.ds_string); + + write_out_header (&file_hdr, out_file_des); + copy_files (in_file_des, out_file_des, file_hdr.c_filesize); + +#ifndef __MSDOS__ + if (archive_format == arf_tar || archive_format == arf_ustar) + add_inode (file_hdr.c_ino, file_hdr.c_name, file_hdr.c_dev_maj, + file_hdr.c_dev_min); +#endif + + pad_output (out_file_des, file_hdr.c_filesize); + + if (close (in_file_des) < 0) + error (0, errno, "%s", input_name.ds_string); + if (reset_time_flag) + { + times.actime = file_stat.st_atime; + times.modtime = file_stat.st_mtime; + if (utime (file_hdr.c_name, ×) < 0) + error (0, errno, "%s", file_hdr.c_name); + } + break; + + case CP_IFDIR: + file_hdr.c_filesize = 0; + write_out_header (&file_hdr, out_file_des); + break; + +#ifndef __MSDOS__ + case CP_IFCHR: + case CP_IFBLK: +#ifdef CP_IFSOCK + case CP_IFSOCK: +#endif +#ifdef CP_IFIFO + case CP_IFIFO: +#endif + if (archive_format == arf_tar) + { + error (0, 0, "%s not dumped: not a regular file", + file_hdr.c_name); + continue; + } + else if (archive_format == arf_ustar) + { + char *otherfile; + if ((otherfile = find_inode_file (file_hdr.c_ino, + file_hdr.c_dev_maj, + file_hdr.c_dev_min))) + { + /* This file is linked to another file already in the + archive, so write it out as a hard link. */ + file_hdr.c_mode = (file_stat.st_mode & 07777); + file_hdr.c_mode |= CP_IFREG; + file_hdr.c_tar_linkname = otherfile; + write_out_header (&file_hdr, out_file_des); + break; + } + add_inode (file_hdr.c_ino, file_hdr.c_name, + file_hdr.c_dev_maj, file_hdr.c_dev_min); + } + file_hdr.c_filesize = 0; + write_out_header (&file_hdr, out_file_des); + break; +#endif + +#ifdef CP_IFLNK + case CP_IFLNK: + { + char *link_name = (char *) xmalloc (file_stat.st_size + 1); + + if (readlink (input_name.ds_string, link_name, + file_stat.st_size) < 0) + { + error (0, errno, "%s", input_name.ds_string); + free (link_name); + continue; + } + if (archive_format == arf_tar || archive_format == arf_ustar) + { + if (file_stat.st_size + 1 > 100) + { + error (0, 0, "%s: symbolic link too long", + file_hdr.c_name); + } + else + { + link_name[file_stat.st_size] = '\0'; + file_hdr.c_tar_linkname = link_name; + write_out_header (&file_hdr, out_file_des); + } + } + else + { + write_out_header (&file_hdr, out_file_des); + copy_buf_out (link_name, out_file_des, file_stat.st_size); + pad_output (out_file_des, file_hdr.c_filesize); + } + free (link_name); + } + break; +#endif + + default: + error (0, 0, "%s: unknown file type", input_name.ds_string); + } + + if (verbose_flag) + fprintf (stderr, "%s\n", input_name.ds_string); + if (dot_flag) + fputc ('.', stderr); + } + } + + writeout_final_defers(out_file_des); + /* The collection is complete; append the trailer. */ + file_hdr.c_ino = 0; + file_hdr.c_mode = 0; + file_hdr.c_uid = 0; + file_hdr.c_gid = 0; + file_hdr.c_nlink = 1; /* Must be 1 for crc format. */ + file_hdr.c_dev_maj = 0; + file_hdr.c_dev_min = 0; + file_hdr.c_rdev_maj = 0; + file_hdr.c_rdev_min = 0; + file_hdr.c_mtime = 0; + file_hdr.c_chksum = 0; + + file_hdr.c_filesize = 0; + file_hdr.c_namesize = 11; + file_hdr.c_name = "TRAILER!!!"; + if (archive_format != arf_tar && archive_format != arf_ustar) + write_out_header (&file_hdr, out_file_des); + else + { + copy_buf_out (zeros_512, out_file_des, 512); + copy_buf_out (zeros_512, out_file_des, 512); + } + + /* Fill up the output block. */ + clear_rest_of_block (out_file_des); + empty_output_buffer (out_file_des); + if (dot_flag) + fputc ('\n', stderr); + res = (output_bytes + io_block_size - 1) / io_block_size; + if (res == 1) + fprintf (stderr, "1 block\n"); + else + fprintf (stderr, "%d blocks\n", res); +} + +/* Read FILE_SIZE bytes of FILE_NAME from IN_FILE_DES and + compute and return a checksum for them. */ + +static unsigned long +read_for_checksum (in_file_des, file_size, file_name) + int in_file_des; + int file_size; + char *file_name; +{ + unsigned long crc; + char buf[BUFSIZ]; + int bytes_left; + int bytes_read; + int i; + + crc = 0; + + for (bytes_left = file_size; bytes_left > 0; bytes_left -= bytes_read) + { + bytes_read = read (in_file_des, buf, BUFSIZ); + if (bytes_read < 0) + error (1, errno, "cannot read checksum for %s", file_name); + if (bytes_read == 0) + break; + for (i = 0; i < bytes_read; ++i) + crc += buf[i] & 0xff; + } + if (lseek (in_file_des, 0L, SEEK_SET)) + error (1, errno, "cannot read checksum for %s", file_name); + + return crc; +} + +/* Write out NULs to fill out the rest of the current block on + OUT_FILE_DES. */ + +static void +clear_rest_of_block (out_file_des) + int out_file_des; +{ + while (output_size < io_block_size) + { + if ((io_block_size - output_size) > 512) + copy_buf_out (zeros_512, out_file_des, 512); + else + copy_buf_out (zeros_512, out_file_des, io_block_size - output_size); + } +} + +/* Write NULs on OUT_FILE_DES to move from OFFSET (the current location) + to the end of the header. */ + +static void +pad_output (out_file_des, offset) + int out_file_des; + int offset; +{ + int pad; + + if (archive_format == arf_newascii || archive_format == arf_crcascii) + pad = (4 - (offset % 4)) % 4; + else if (archive_format == arf_tar || archive_format == arf_ustar) + pad = (512 - (offset % 512)) % 512; + else if (archive_format != arf_oldascii && archive_format != arf_hpoldascii) + pad = (2 - (offset % 2)) % 2; + else + pad = 0; + + if (pad != 0) + copy_buf_out (zeros_512, out_file_des, pad); +} + + +/* When creating newc and crc archives if a file has multiple (hard) + links, we don't put any of them into the archive until we have seen + all of them (or until we get to the end of the list of files that + are going into the archive and know that we have seen all of the links + to the file that we will see). We keep these "defered" files on + this list. */ + +struct deferment *deferouts = NULL; + + +/* Is this file_hdr the last (hard) link to a file? I.e., have + we already seen and defered all of the other links? */ + +static int +last_link (file_hdr) + struct new_cpio_header *file_hdr; +{ + int other_files_sofar; + + other_files_sofar = count_defered_links_to_dev_ino (file_hdr); + if (file_hdr->c_nlink == (other_files_sofar + 1) ) + { + return 1; + } + return 0; +} + +/* Count the number of other (hard) links to this file that have + already been defered. */ + +static int +count_defered_links_to_dev_ino (file_hdr) + struct new_cpio_header *file_hdr; +{ + struct deferment *d; + int ino; + int maj; + int min; + int count; + ino = file_hdr->c_ino; + maj = file_hdr->c_dev_maj; + min = file_hdr->c_dev_min; + count = 0; + for (d = deferouts; d != NULL; d = d->next) + { + if ( (d->header.c_ino == ino) && (d->header.c_dev_maj == maj) + && (d->header.c_dev_min == min) ) + ++count; + } + return count; +} + +/* Add the file header for a link that is being defered to the deferouts + list. */ + +static void +add_link_defer (file_hdr) + struct new_cpio_header *file_hdr; +{ + struct deferment *d; + d = create_deferment (file_hdr); + d->next = deferouts; + deferouts = d; +} + +/* We are about to put a file into a newc or crc archive that is + multiply linked. We have already seen and defered all of the + other links to the file but haven't written them into the archive. + Write the other links into the archive, and remove them from the + deferouts list. */ + +static void +writeout_other_defers (file_hdr, out_des) + struct new_cpio_header *file_hdr; + int out_des; +{ + struct deferment *d; + struct deferment *d_prev; + int ino; + int maj; + int min; + int count; + ino = file_hdr->c_ino; + maj = file_hdr->c_dev_maj; + min = file_hdr->c_dev_min; + d_prev = NULL; + d = deferouts; + while (d != NULL) + { + if ( (d->header.c_ino == ino) && (d->header.c_dev_maj == maj) + && (d->header.c_dev_min == min) ) + { + struct deferment *d_free; + d->header.c_filesize = 0; + write_out_header (&d->header, out_des); + if (d_prev != NULL) + d_prev->next = d->next; + else + deferouts = d->next; + d_free = d; + d = d->next; + free_deferment (d_free); + } + else + { + d_prev = d; + d = d->next; + } + } + return; +} +/* When writing newc and crc format archives we defer multiply linked + files until we have seen all of the links to the file. If a file + has links to it that aren't going into the archive, then we will + never see the "last" link to the file, so at the end we just write + all of the leftover defered files into the archive. */ + +static void +writeout_final_defers(out_des) + int out_des; +{ + struct deferment *d; + int other_count; + while (deferouts != NULL) + { + d = deferouts; + other_count = count_defered_links_to_dev_ino (&d->header); + if (other_count == 1) + { + writeout_defered_file (&d->header, out_des); + } + else + { + struct new_cpio_header file_hdr; + file_hdr = d->header; + file_hdr.c_filesize = 0; + write_out_header (&file_hdr, out_des); + } + deferouts = deferouts->next; + } +} + +/* Write a file into the archive. This code is the same as + the code in process_copy_out(), but we need it here too + for writeout_final_defers() to call. */ + +static void +writeout_defered_file (header, out_file_des) + struct new_cpio_header *header; +{ + int in_file_des; + struct new_cpio_header file_hdr; + struct utimbuf times; /* For setting file times. */ + /* Initialize this in case it has members we don't know to set. */ + bzero (×, sizeof (struct utimbuf)); + + file_hdr = *header; + + + in_file_des = open (header->c_name, + O_RDONLY | O_BINARY, 0); + if (in_file_des < 0) + { + error (0, errno, "%s", header->c_name); + return; + } + + if (archive_format == arf_crcascii) + file_hdr.c_chksum = read_for_checksum (in_file_des, + file_hdr.c_filesize, + header->c_name); + + write_out_header (&file_hdr, out_file_des); + copy_files (in_file_des, out_file_des, file_hdr.c_filesize); + +#ifndef __MSDOS__ + if (archive_format == arf_tar || archive_format == arf_ustar) + add_inode (file_hdr.c_ino, file_hdr.c_name, file_hdr.c_dev_maj, + file_hdr.c_dev_min); +#endif + + pad_output (out_file_des, file_hdr.c_filesize); + + if (close (in_file_des) < 0) + error (0, errno, "%s", header->c_name); + if (reset_time_flag) + { + times.actime = file_hdr.c_mtime; + times.modtime = file_hdr.c_mtime; + if (utime (file_hdr.c_name, ×) < 0) + error (0, errno, "%s", file_hdr.c_name); + } + return; +} diff --git a/gnu/usr.bin/cpio/copypass.c b/gnu/usr.bin/cpio/copypass.c new file mode 100644 index 0000000000..afd5753eca --- /dev/null +++ b/gnu/usr.bin/cpio/copypass.c @@ -0,0 +1,449 @@ +/* copypass.c - cpio copy pass sub-function. + Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include "filetypes.h" +#include "system.h" +#include "cpiohdr.h" +#include "dstring.h" +#include "extern.h" + +#ifndef HAVE_LCHOWN +#define lchown chown +#endif + +/* Copy files listed on the standard input into directory `directory_name'. + If `link_flag', link instead of copying. */ + +void +process_copy_pass () +{ + dynamic_string input_name; /* Name of file from stdin. */ + dynamic_string output_name; /* Name of new file. */ + int dirname_len; /* Length of `directory_name'. */ + int res; /* Result of functions. */ + char *slash; /* For moving past slashes in input name. */ + struct utimbuf times; /* For resetting file times after copy. */ + struct stat in_file_stat; /* Stat record for input file. */ + struct stat out_file_stat; /* Stat record for output file. */ + int in_file_des; /* Input file descriptor. */ + int out_file_des; /* Output file descriptor. */ + int existing_dir; /* True if file is a dir & already exists. */ +#ifdef HPUX_CDF + int cdf_flag; + int cdf_char; +#endif + + /* Initialize the copy pass. */ + dirname_len = strlen (directory_name); + ds_init (&input_name, 128); + ds_init (&output_name, dirname_len + 2); + strcpy (output_name.ds_string, directory_name); + output_name.ds_string[dirname_len] = '/'; + output_is_seekable = TRUE; + /* Initialize this in case it has members we don't know to set. */ + bzero (×, sizeof (struct utimbuf)); + + /* Copy files with names read from stdin. */ + while (ds_fgetstr (stdin, &input_name, name_end) != NULL) + { + int link_res = -1; + + /* Check for blank line and ignore it if found. */ + if (input_name.ds_string[0] == '\0') + { + error (0, 0, "blank line ignored"); + continue; + } + + /* Check for current directory and ignore it if found. */ + if (input_name.ds_string[0] == '.' + && (input_name.ds_string[1] == '\0' + || (input_name.ds_string[1] == '/' + && input_name.ds_string[2] == '\0'))) + continue; + + if ((*xstat) (input_name.ds_string, &in_file_stat) < 0) + { + error (0, errno, "%s", input_name.ds_string); + continue; + } + + /* Make the name of the new file. */ + for (slash = input_name.ds_string; *slash == '/'; ++slash) + ; +#ifdef HPUX_CDF + /* For CDF's we add a 2nd `/' after all "hidden" directories. + This kind of a kludge, but it's what we do when creating + archives, and it's easier to do this than to separately + keep track of which directories in a path are "hidden". */ + slash = add_cdf_double_slashes (slash); +#endif + ds_resize (&output_name, dirname_len + strlen (slash) + 2); + strcpy (output_name.ds_string + dirname_len + 1, slash); + + existing_dir = FALSE; + if (lstat (output_name.ds_string, &out_file_stat) == 0) + { + if (S_ISDIR (out_file_stat.st_mode) + && S_ISDIR (in_file_stat.st_mode)) + { + /* If there is already a directory there that + we are trying to create, don't complain about it. */ + existing_dir = TRUE; + } + else if (!unconditional_flag + && in_file_stat.st_mtime <= out_file_stat.st_mtime) + { + error (0, 0, "%s not created: newer or same age version exists", + output_name.ds_string); + continue; /* Go to the next file. */ + } + else if (S_ISDIR (out_file_stat.st_mode) + ? rmdir (output_name.ds_string) + : unlink (output_name.ds_string)) + { + error (0, errno, "cannot remove current %s", + output_name.ds_string); + continue; /* Go to the next file. */ + } + } + + /* Do the real copy or link. */ + if (S_ISREG (in_file_stat.st_mode)) + { +#ifndef __MSDOS__ + /* Can the current file be linked to a another file? + Set link_name to the original file name. */ + if (link_flag) + /* User said to link it if possible. Try and link to + the original copy. If that fails we'll still try + and link to a copy we've already made. */ + link_res = link_to_name (output_name.ds_string, + input_name.ds_string); + if ( (link_res < 0) && (in_file_stat.st_nlink > 1) ) + link_res = link_to_maj_min_ino (output_name.ds_string, + major (in_file_stat.st_dev), + minor (in_file_stat.st_dev), + in_file_stat.st_ino); +#endif + + /* If the file was not linked, copy contents of file. */ + if (link_res < 0) + { + in_file_des = open (input_name.ds_string, + O_RDONLY | O_BINARY, 0); + if (in_file_des < 0) + { + error (0, errno, "%s", input_name.ds_string); + continue; + } + out_file_des = open (output_name.ds_string, + O_CREAT | O_WRONLY | O_BINARY, 0600); + if (out_file_des < 0 && create_dir_flag) + { + create_all_directories (output_name.ds_string); + out_file_des = open (output_name.ds_string, + O_CREAT | O_WRONLY | O_BINARY, 0600); + } + if (out_file_des < 0) + { + error (0, errno, "%s", output_name.ds_string); + close (in_file_des); + continue; + } + + copy_files (in_file_des, out_file_des, in_file_stat.st_size); + empty_output_buffer (out_file_des); + if (close (in_file_des) < 0) + error (0, errno, "%s", input_name.ds_string); + if (close (out_file_des) < 0) + error (0, errno, "%s", output_name.ds_string); + + /* Set the attributes of the new file. */ + if (!no_chown_flag) + if ((chown (output_name.ds_string, + set_owner_flag ? set_owner : in_file_stat.st_uid, + set_group_flag ? set_group : in_file_stat.st_gid) < 0) + && errno != EPERM) + error (0, errno, "%s", output_name.ds_string); + /* chown may have turned off some permissions we wanted. */ + if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0) + error (0, errno, "%s", output_name.ds_string); + if (reset_time_flag) + { + times.actime = in_file_stat.st_atime; + times.modtime = in_file_stat.st_mtime; + if (utime (input_name.ds_string, ×) < 0) + error (0, errno, "%s", input_name.ds_string); + if (utime (output_name.ds_string, ×) < 0) + error (0, errno, "%s", output_name.ds_string); + } + if (retain_time_flag) + { + times.actime = times.modtime = in_file_stat.st_mtime; + if (utime (output_name.ds_string, ×) < 0) + error (0, errno, "%s", output_name.ds_string); + } + } + } + else if (S_ISDIR (in_file_stat.st_mode)) + { +#ifdef HPUX_CDF + cdf_flag = 0; +#endif + if (!existing_dir) + { +#ifdef HPUX_CDF + /* If the directory name ends in a + and is SUID, + then it is a CDF. Strip the trailing + from the name + before creating it. */ + cdf_char = strlen (output_name.ds_string) - 1; + if ( (cdf_char > 0) && + (in_file_stat.st_mode & 04000) && + (output_name.ds_string [cdf_char] == '+') ) + { + output_name.ds_string [cdf_char] = '\0'; + cdf_flag = 1; + } +#endif + res = mkdir (output_name.ds_string, in_file_stat.st_mode); + + } + else + res = 0; + if (res < 0 && create_dir_flag) + { + create_all_directories (output_name.ds_string); + res = mkdir (output_name.ds_string, in_file_stat.st_mode); + } + if (res < 0) + { + error (0, errno, "%s", output_name.ds_string); + continue; + } + if (!no_chown_flag) + if ((chown (output_name.ds_string, + set_owner_flag ? set_owner : in_file_stat.st_uid, + set_group_flag ? set_group : in_file_stat.st_gid) < 0) + && errno != EPERM) + error (0, errno, "%s", output_name.ds_string); + /* chown may have turned off some permissions we wanted. */ + if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0) + error (0, errno, "%s", output_name.ds_string); +#ifdef HPUX_CDF + if (cdf_flag) + /* Once we "hide" the directory with the chmod(), + we have to refer to it using name+ isntead of name. */ + output_name.ds_string [cdf_char] = '+'; +#endif + if (retain_time_flag) + { + times.actime = times.modtime = in_file_stat.st_mtime; + if (utime (output_name.ds_string, ×) < 0) + error (0, errno, "%s", output_name.ds_string); + } + } +#ifndef __MSDOS__ + else if (S_ISCHR (in_file_stat.st_mode) || + S_ISBLK (in_file_stat.st_mode) || +#ifdef S_ISFIFO + S_ISFIFO (in_file_stat.st_mode) || +#endif +#ifdef S_ISSOCK + S_ISSOCK (in_file_stat.st_mode) || +#endif + 0) + { + /* Can the current file be linked to a another file? + Set link_name to the original file name. */ + if (link_flag) + /* User said to link it if possible. */ + link_res = link_to_name (output_name.ds_string, + input_name.ds_string); + if ( (link_res < 0) && (in_file_stat.st_nlink > 1) ) + link_res = link_to_maj_min_ino (output_name.ds_string, + major (in_file_stat.st_dev), + minor (in_file_stat.st_dev), + in_file_stat.st_ino); + + if (link_res < 0) + { + res = mknod (output_name.ds_string, in_file_stat.st_mode, + in_file_stat.st_rdev); + if (res < 0 && create_dir_flag) + { + create_all_directories (output_name.ds_string); + res = mknod (output_name.ds_string, in_file_stat.st_mode, + in_file_stat.st_rdev); + } + if (res < 0) + { + error (0, errno, "%s", output_name.ds_string); + continue; + } + if (!no_chown_flag) + if ((chown (output_name.ds_string, + set_owner_flag ? set_owner : in_file_stat.st_uid, + set_group_flag ? set_group : in_file_stat.st_gid) < 0) + && errno != EPERM) + error (0, errno, "%s", output_name.ds_string); + /* chown may have turned off some permissions we wanted. */ + if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0) + error (0, errno, "%s", output_name.ds_string); + if (retain_time_flag) + { + times.actime = times.modtime = in_file_stat.st_mtime; + if (utime (output_name.ds_string, ×) < 0) + error (0, errno, "%s", output_name.ds_string); + } + } + } +#endif + +#ifdef S_ISLNK + else if (S_ISLNK (in_file_stat.st_mode)) + { + char *link_name; + link_name = (char *) xmalloc ((unsigned int) in_file_stat.st_size + 1); + + if (readlink (input_name.ds_string, link_name, + in_file_stat.st_size) < 0) + { + error (0, errno, "%s", input_name.ds_string); + free (link_name); + continue; + } + link_name[in_file_stat.st_size] = '\0'; + + res = UMASKED_SYMLINK (link_name, output_name.ds_string, + in_file_stat.st_mode); + if (res < 0 && create_dir_flag) + { + create_all_directories (output_name.ds_string); + res = UMASKED_SYMLINK (link_name, output_name.ds_string, + in_file_stat.st_mode); + } + if (res < 0) + { + error (0, errno, "%s", output_name.ds_string); + free (link_name); + continue; + } + + /* Set the attributes of the new link. */ + if (!no_chown_flag) + if ((lchown (output_name.ds_string, + set_owner_flag ? set_owner : in_file_stat.st_uid, + set_group_flag ? set_group : in_file_stat.st_gid) < 0) + && errno != EPERM) + error (0, errno, "%s", output_name.ds_string); + free (link_name); + } +#endif + else + { + error (0, 0, "%s: unknown file type", input_name.ds_string); + } + + if (verbose_flag) + fprintf (stderr, "%s\n", output_name.ds_string); + if (dot_flag) + fputc ('.', stderr); + } + + if (dot_flag) + fputc ('\n', stderr); + res = (output_bytes + io_block_size - 1) / io_block_size; + if (res == 1) + fprintf (stderr, "1 block\n"); + else + fprintf (stderr, "%d blocks\n", res); +} + +/* Try and create a hard link from FILE_NAME to another file + with the given major/minor device number and inode. If no other + file with the same major/minor/inode numbers is known, add this file + to the list of known files and associated major/minor/inode numbers + and return -1. If another file with the same major/minor/inode + numbers is found, try and create another link to it using + link_to_name, and return 0 for success and -1 for failure. */ + +int +link_to_maj_min_ino (file_name, st_dev_maj, st_dev_min, st_ino) + char *file_name; + int st_dev_maj; + int st_dev_min; + int st_ino; +{ + int link_res; + char *link_name; + link_res = -1; +#ifndef __MSDOS__ + /* Is the file a link to a previously copied file? */ + link_name = find_inode_file (st_ino, + st_dev_maj, + st_dev_min); + if (link_name == NULL) + add_inode (st_ino, file_name, + st_dev_maj, + st_dev_min); + else + link_res = link_to_name (file_name, link_name); +#endif + return link_res; +} + +/* Try and create a hard link from LINK_NAME to LINK_TARGET. If + `create_dir_flag' is set, any non-existent (parent) directories + needed by LINK_NAME will be created. If the link is successfully + created and `verbose_flag' is set, print "LINK_TARGET linked to LINK_NAME\n". + If the link can not be created and `link_flag' is set, print + "cannot link LINK_TARGET to LINK_NAME\n". Return 0 if the link + is created, -1 otherwise. */ + +int +link_to_name (link_name, link_target) + char *link_name; + char *link_target; +{ + int res; +#ifdef __MSDOS__ + res = -1; +#else /* not __MSDOS__ */ + res = link (link_target, link_name); + if (res < 0 && create_dir_flag) + { + create_all_directories (link_name); + res = link (link_target, link_name); + } + if (res == 0) + { + if (verbose_flag) + error (0, 0, "%s linked to %s", + link_target, link_name); + } + else if (link_flag) + { + error (0, errno, "cannot link %s to %s", + link_target, link_name); + } +#endif /* not __MSDOS__ */ + return res; +} diff --git a/gnu/usr.bin/cpio/cpio.1 b/gnu/usr.bin/cpio/cpio.1 new file mode 100644 index 0000000000..7ef74c41c1 --- /dev/null +++ b/gnu/usr.bin/cpio/cpio.1 @@ -0,0 +1,310 @@ +.TH CPIO 1L \" -*- nroff -*- +.SH NAME +cpio \- copy files to and from archives +.SH SYNOPSIS +.B cpio +{\-o|\-\-create} [\-0acvABLV] [\-C bytes] [\-H format] [\-M message] +[\-O [[user@]host:]archive] [\-F [[user@]host:]archive] +[\-\-file=[[user@]host:]archive] [\-\-format=format] [\-\-message=message] +[\-\-null] [\-\-reset-access-time] [\-\-verbose] [\-\-dot] [\-\-append] +[\-\-block-size=blocks] [\-\-dereference] [\-\-io-size=bytes] +[\-\-help] [\-\-version] < name-list [> archive] + +.B cpio +{\-i|\-\-extract} [\-bcdfmnrtsuvBSV] [\-C bytes] [\-E file] [\-H format] +[\-M message] [\-R [user][:.][group]] [\-I [[user@]host:]archive] +[\-F [[user@]host:]archive] [\-\-file=[[user@]host:]archive] +[\-\-make-directories] [\-\-nonmatching] [\-\-preserve-modification-time] +[\-\-numeric-uid-gid] [\-\-rename] [\-\-list] [\-\-swap-bytes] [\-\-swap] [\-\-dot] +[\-\-unconditional] [\-\-verbose] [\-\-block-size=blocks] [\-\-swap-halfwords] +[\-\-io-size=bytes] [\-\-pattern-file=file] [\-\-format=format] +[\-\-owner=[user][:.][group]] [\-\-no-preserve-owner] [\-\-message=message] +[\-\-help] [\-\-version] [pattern...] [< archive] + +.B cpio +{\-p|\-\-pass-through} [\-0adlmuvLV] [\-R [user][:.][group]] +[\-\-null] [\-\-reset-access-time] [\-\-make-directories] [\-\-link] +[\-\-preserve-modification-time] [\-\-unconditional] [\-\-verbose] [\-\-dot] +[\-\-dereference] [\-\-owner=[user][:.][group]] [\-\-no-preserve-owner] +[\-\-help] [\-\-version] destination-directory < name-list +.SH DESCRIPTION +This manual page +documents the GNU version of +.BR cpio . +.B cpio +copies files into or out of a cpio or tar archive, which is a file that +contains other files plus information about them, such as their +pathname, owner, timestamps, and access permissions. The archive can +be another file on the disk, a magnetic tape, or a pipe. +.B cpio +has three operating modes. +.PP +In copy-out mode, +.B cpio +copies files into an archive. It reads a list of filenames, one per +line, on the standard input, and writes the archive onto the standard +output. A typical way to generate the list of filenames is with the +.B find +command; you should give +.B find +the \-depth option to minimize problems with permissions on +directories that are unwritable or not searchable. +.PP +In copy-in mode, +.B cpio +copies files out of an archive or lists the archive contents. It +reads the archive from the standard input. Any non-option command +line arguments are shell globbing patterns; only files in the archive +whose names match one or more of those patterns are copied from the +archive. Unlike in the shell, an initial `.' in a filename does +match a wildcard at the start of a pattern, and a `/' in a filename +can match wildcards. If no patterns are given, all files are +extracted. +.PP +In copy-pass mode, +.B cpio +copies files from one directory tree to another, combining the +copy-out and copy-in steps without actually using an archive. +It reads the list of files to copy from the standard input; the +directory into which it will copy them is given as a non-option +argument. +.PP +.B cpio +supports the following archive formats: binary, old ASCII, new +ASCII, crc, HPUX binary, HPUX old ASCII, old tar, and POSIX.1 tar. +The binary format +is obsolete because it encodes information about the files in a way +that is not portable between different machine architectures. +The old ASCII format is portable between different machine architectures, +but should not be used on file systems with more than 65536 i-nodes. +The new ASCII format is portable between different machine architectures +and can be used on any size file system, but is not supported by all +versions of +.BR cpio ; +currently, it is only supported by GNU and Unix System V R4. +The crc format is +like the new ASCII format, but also contains a checksum for each file +which +.B cpio +calculates when creating an archive +and verifies when the file is extracted from the archive. +The HPUX formats are provided for compatibility with HPUX's cpio which +stores device files differently. +.PP +The tar format is provided for compatability with +the +.B tar +program. It can not be used to archive files with names +longer than 100 characters, and can not be used to archive "special" +(block or character devices) files. +The POSIX.1 tar format can not be used to archive files with names longer +than 255 characters (less unless they have a "/" in just the right place). +.PP +By default, +.B cpio +creates binary format archives, for compatibility with +older +.B cpio +programs. +When extracting from archives, +.B cpio +automatically recognizes which kind of archive it is reading and can +read archives created on machines with a different byte-order. +.PP +Some of the options to +.B cpio +apply only to certain operating modes; see the SYNOPSIS section for a +list of which options are allowed in which modes. +.SS OPTIONS +.TP +.I "\-0, \-\-null" +In copy-out and copy-pass modes, read a list of filenames terminated +by a null character instead of a newline, so that files whose names +contain newlines can be archived. GNU +.B find +is one way to produce a list of null-terminated filenames. +.TP +.I "\-a, \-\-reset-access-time" +Reset the access times of files after reading them, so that it does +not look like they have just been read. +.TP +.I "\-A, \-\-append" +Append to an existing archive. Only works in copy-out mode. The +archive must be a disk file specified with the +.I \-O +or +.I "\-F (\-\-file)" +option. +.TP +.I "\-b, \-\-swap" +In copy-in mode, swap both halfwords of words and bytes of halfwords +in the data. Equivalent to +.IR "\-sS" . +Use this option to convert 32-bit integers between big-endian and +little-endian machines. +.TP +.I "\-B" +Set the I/O block size to 5120 bytes. Initially the block size is 512 +bytes. +.TP +.I "\-\-block-size=BLOCK-SIZE" +Set the I/O block size to BLOCK-SIZE * 512 bytes. +.TP +.I "\-c" +Use the old portable (ASCII) archive format. +.TP +.I "\-C IO-SIZE, \-\-io-size=IO-SIZE" +Set the I/O block size to IO-SIZE bytes. +.TP +.I "\-d, \-\-make-directories" +Create leading directories where needed. +.TP +.I "\-E FILE, \-\-pattern-file=FILE" +In copy-in mode, read additional patterns specifying filenames to +extract or list from FILE. The lines of FILE are treated as if they +had been non-option arguments to +.BR cpio . +.TP +.I "\-f, \-\-nonmatching" +Only copy files that do not match any of the given patterns. +.TP +.I "\-F, \-\-file=archive" +Archive filename to use instead of standard input or output. To use a +tape drive on another machine as the archive, use a filename that +starts with `HOSTNAME:'. The hostname can be preceded by a +username and an `@' to access the remote tape drive as that user, if +you have permission to do so (typically an entry in that user's +`~/.rhosts' file). +.TP +.I "\-\-force-local" +With +.IR \-F , +.IR \-I , +or +.IR \-O , +take the archive file name to be a local file even if it contains a +colon, which would ordinarily indicate a remote host name. +.TP +.I "\-H FORMAT, \-\-format=FORMAT" +Use archive format FORMAT. The valid formats are listed below; +the same names are also recognized in all-caps. The default in +copy-in mode is to automatically detect the archive format, and in +copy-out mode is "bin". +.RS +.IP bin +The obsolete binary format. +.IP odc +The old (POSIX.1) portable format. +.IP newc +The new (SVR4) portable format, which supports file systems having +more than 65536 i-nodes. +.IP crc +The new (SVR4) portable format with a checksum added. +.IP tar +The old tar format. +.IP ustar +The POSIX.1 tar format. Also recognizes GNU +.B tar +archives, which are similar but not identical. +.IP hpbin +The obsolete binary format used by HPUX's cpio (which stores device files +differently). +.IP hpodc +The portable format used by HPUX's cpio (which stores device files differently). +.RE +.TP +.I "\-i, \-\-extract" +Run in copy-in mode. +.TP +.I "\-I archive" +Archive filename to use instead of standard input. To use a +tape drive on another machine as the archive, use a filename that +starts with `HOSTNAME:'. The hostname can be preceded by a +username and an `@' to access the remote tape drive as that user, if +you have permission to do so (typically an entry in that user's +`~/.rhosts' file). +.TP +.I \-k +Ignored; for compatibility with other versions of +.BR cpio . +.TP +.I "\-l, \-\-link" +Link files instead of copying them, when possible. +.TP +.I "\-L, \-\-dereference" +Dereference symbolic links (copy the files that they point to instead +of copying the links). +.TP +.I "\-m, \-\-preserve-modification-time" +Retain previous file modification times when creating files. +.TP +.I "\-M MESSAGE, \-\-message=MESSAGE" +Print MESSAGE when the end of a volume of the backup media (such as a +tape or a floppy disk) is reached, to prompt the user to insert a new +volume. If MESSAGE contains the string "%d", it is replaced by the +current volume number (starting at 1). +.TP +.I "\-n, \-\-numeric-uid-gid" +In the verbose table of contents listing, show numeric UID and GID +instead of translating them into names. +.TP +.I " \-\-no-preserve-owner" +In copy-in mode and copy-pass mode, do not change the ownership of the +files; leave them owned by the user extracting them. This is the +default for non-root users, so that users on System V don't +inadvertantly give away files. +.TP +.I "\-o, \-\-create" +Run in copy-out mode. +.TP +.I "\-O archive" +Archive filename to use instead of standard output. To use a +tape drive on another machine as the archive, use a filename that +starts with `HOSTNAME:'. The hostname can be preceded by a +username and an `@' to access the remote tape drive as that user, if +you have permission to do so (typically an entry in that user's +`~/.rhosts' file). +.TP +.I "\-p, \-\-pass-through" +Run in copy-pass mode. +.TP +.I "\-r, \-\-rename" +Interactively rename files. +.TP +.I "\-R [user][:.][group], \-\-owner [user][:.][group]" +In copy-out and copy-pass modes, set the ownership of all files +created to the specified user and/or group. Either the user or the +group, or both, must be present. If the group is omitted but the ":" +or "." separator is given, use the given user's login group. Only the +super-user can change files' ownership. +.TP +.I "\-s, \-\-swap-bytes" +In copy-in mode, swap the bytes of each halfword (pair of bytes) in +the files. +.TP +.I "\-S, \-\-swap-halfwords" +In copy-in mode, swap the halfwords of each word (4 bytes) in the +files. +.TP +.I "\-t, \-\-list" +Print a table of contents of the input. +.TP +.I "\-u, \-\-unconditional" +Replace all files, without asking whether to replace existing newer +files with older files. +.TP +.I "\-v, \-\-verbose" +List the files processed, or with +.IR \-t , +give an `ls \-l' style table of contents listing. In a verbose table +of contents of a ustar archive, user and group names in the archive +that do not exist on the local system are replaced by the names that +correspond locally to the numeric UID and GID stored in the archive. +.TP +.I "\-V \-\-dot" +Print a "." for each file processed. +.TP +.I "\-\-version" +Print the +.B cpio +program version number and exit. diff --git a/gnu/usr.bin/cpio/cpio.h b/gnu/usr.bin/cpio/cpio.h new file mode 100644 index 0000000000..537da72120 --- /dev/null +++ b/gnu/usr.bin/cpio/cpio.h @@ -0,0 +1,69 @@ +/* Extended cpio format from POSIX.1. + Copyright (C) 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _CPIO_H + +#define _CPIO_H 1 + +/* A cpio archive consists of a sequence of files. + Each file has a 76 byte header, + a variable length, NUL terminated filename, + and variable length file data. + A header for a filename "TRAILER!!!" indicates the end of the archive. */ + +/* All the fields in the header are ISO 646 (approximately ASCII) strings + of octal numbers, left padded, not NUL terminated. + + Field Name Length in Bytes Notes + c_magic 6 must be "070707" + c_dev 6 + c_ino 6 + c_mode 6 see below for value + c_uid 6 + c_gid 6 + c_nlink 6 + c_rdev 6 only valid for chr and blk special files + c_mtime 11 + c_namesize 6 count includes terminating NUL in pathname + c_filesize 11 must be 0 for FIFOs and directories */ + +/* Values for c_mode, OR'd together: */ + +#define C_IRUSR 000400 +#define C_IWUSR 000200 +#define C_IXUSR 000100 +#define C_IRGRP 000040 +#define C_IWGRP 000020 +#define C_IXGRP 000010 +#define C_IROTH 000004 +#define C_IWOTH 000002 +#define C_IXOTH 000001 + +#define C_ISUID 004000 +#define C_ISGID 002000 +#define C_ISVTX 001000 + +#define C_ISBLK 060000 +#define C_ISCHR 020000 +#define C_ISDIR 040000 +#define C_ISFIFO 010000 +#define C_ISSOCK 0140000 +#define C_ISLNK 0120000 +#define C_ISCTG 0110000 +#define C_ISREG 0100000 + +#endif /* cpio.h */ diff --git a/gnu/usr.bin/cpio/cpiohdr.h b/gnu/usr.bin/cpio/cpiohdr.h new file mode 100644 index 0000000000..9694af69ba --- /dev/null +++ b/gnu/usr.bin/cpio/cpiohdr.h @@ -0,0 +1,90 @@ +/* Extended cpio header from POSIX.1. + Copyright (C) 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _CPIOHDR_H + +#define _CPIOHDR_H 1 + +#include + +struct old_cpio_header +{ + unsigned short c_magic; + short c_dev; + unsigned short c_ino; + unsigned short c_mode; + unsigned short c_uid; + unsigned short c_gid; + unsigned short c_nlink; + short c_rdev; + unsigned short c_mtimes[2]; + unsigned short c_namesize; + unsigned short c_filesizes[2]; + unsigned long c_mtime; /* Long-aligned copy of `c_mtimes'. */ + unsigned long c_filesize; /* Long-aligned copy of `c_filesizes'. */ + char *c_name; +}; + +/* "New" portable format and CRC format: + + Each file has a 110 byte header, + a variable length, NUL terminated filename, + and variable length file data. + A header for a filename "TRAILER!!!" indicates the end of the archive. */ + +/* All the fields in the header are ISO 646 (approximately ASCII) strings + of hexadecimal numbers, left padded, not NUL terminated. + + Field Name Length in Bytes Notes + c_magic 6 "070701" for "new" portable format + "070702" for CRC format + c_ino 8 + c_mode 8 + c_uid 8 + c_gid 8 + c_nlink 8 + c_mtime 8 + c_filesize 8 must be 0 for FIFOs and directories + c_maj 8 + c_min 8 + c_rmaj 8 only valid for chr and blk special files + c_rmin 8 only valid for chr and blk special files + c_namesize 8 count includes terminating NUL in pathname + c_chksum 8 0 for "new" portable format; for CRC format + the sum of all the bytes in the file */ + +struct new_cpio_header +{ + unsigned short c_magic; + unsigned long c_ino; + unsigned long c_mode; + unsigned long c_uid; + unsigned long c_gid; + unsigned long c_nlink; + unsigned long c_mtime; + unsigned long c_filesize; + long c_dev_maj; + long c_dev_min; + long c_rdev_maj; + long c_rdev_min; + unsigned long c_namesize; + unsigned long c_chksum; + char *c_name; + char *c_tar_linkname; +}; + +#endif /* cpiohdr.h */ diff --git a/gnu/usr.bin/cpio/defer.c b/gnu/usr.bin/cpio/defer.c new file mode 100644 index 0000000000..efe60e0dd1 --- /dev/null +++ b/gnu/usr.bin/cpio/defer.c @@ -0,0 +1,43 @@ +/* defer.c - handle "defered" links in newc and crc archives + Copyright (C) 1993 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include "system.h" +#include "cpiohdr.h" +#include "extern.h" +#include "defer.h" + +struct deferment * +create_deferment (file_hdr) + struct new_cpio_header *file_hdr; +{ + struct deferment *d; + d = (struct deferment *) xmalloc (sizeof (struct deferment) ); + d->header = *file_hdr; + d->header.c_name = (char *) xmalloc (strlen (file_hdr->c_name) + 1); + strcpy (d->header.c_name, file_hdr->c_name); + return d; +} + +void +free_deferment (d) + struct deferment *d; +{ + free (d->header.c_name); + free (d); +} diff --git a/gnu/usr.bin/cpio/defer.h b/gnu/usr.bin/cpio/defer.h new file mode 100644 index 0000000000..89abffec03 --- /dev/null +++ b/gnu/usr.bin/cpio/defer.h @@ -0,0 +1,8 @@ +struct deferment + { + struct deferment *next; + struct new_cpio_header header; + }; + +struct deferment *create_deferment P_((struct new_cpio_header *file_hdr)); +void free_deferment P_((struct deferment *d)); diff --git a/gnu/usr.bin/cpio/dirname.c b/gnu/usr.bin/cpio/dirname.c new file mode 100644 index 0000000000..5a92ce557f --- /dev/null +++ b/gnu/usr.bin/cpio/dirname.c @@ -0,0 +1,66 @@ +/* dirname.c -- return all but the last element in a path + Copyright (C) 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef STDC_HEADERS +#include +#else +char *malloc (); +#endif +#if defined(STDC_HEADERS) || defined(HAVE_STRING_H) +#include +#ifndef rindex +#define rindex strrchr +#endif +#else +#include +#endif + +/* Return the leading directories part of PATH, + allocated with malloc. If out of memory, return 0. + Assumes that trailing slashes have already been + removed. */ + +char * +dirname (path) + char *path; +{ + char *newpath; + char *slash; + int length; /* Length of result, not including NUL. */ + + slash = rindex (path, '/'); + if (slash == 0) + { + /* File is in the current directory. */ + path = "."; + length = 1; + } + else + { + /* Remove any trailing slashes from the result. */ + while (slash > path && *slash == '/') + --slash; + + length = slash - path + 1; + } + newpath = malloc (length + 1); + if (newpath == 0) + return 0; + strncpy (newpath, path, length); + newpath[length] = 0; + return newpath; +} diff --git a/gnu/usr.bin/cpio/dstring.c b/gnu/usr.bin/cpio/dstring.c new file mode 100644 index 0000000000..26d6bbcc1d --- /dev/null +++ b/gnu/usr.bin/cpio/dstring.c @@ -0,0 +1,114 @@ +/* dstring.c - The dynamic string handling routines used by cpio. + Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) +#include +#else +#include +#endif +#include "dstring.h" + +#if __STDC__ +# define P_(s) s +#else +# define P_(s) () +#endif +char *xmalloc P_((unsigned n)); +char *xrealloc P_((char *p, unsigned n)); + +/* Initialiaze dynamic string STRING with space for SIZE characters. */ + +void +ds_init (string, size) + dynamic_string *string; + int size; +{ + string->ds_length = size; + string->ds_string = (char *) xmalloc (size); +} + +/* Expand dynamic string STRING, if necessary, to hold SIZE characters. */ + +void +ds_resize (string, size) + dynamic_string *string; + int size; +{ + if (size > string->ds_length) + { + string->ds_length = size; + string->ds_string = (char *) xrealloc ((char *) string->ds_string, size); + } +} + +/* Dynamic string S gets a string terminated by the EOS character + (which is removed) from file F. S will increase + in size during the function if the string from F is longer than + the current size of S. + Return NULL if end of file is detected. Otherwise, + Return a pointer to the null-terminated string in S. */ + +char * +ds_fgetstr (f, s, eos) + FILE *f; + dynamic_string *s; + char eos; +{ + int insize; /* Amount needed for line. */ + int strsize; /* Amount allocated for S. */ + int next_ch; + + /* Initialize. */ + insize = 0; + strsize = s->ds_length; + + /* Read the input string. */ + next_ch = getc (f); + while (next_ch != eos && next_ch != EOF) + { + if (insize >= strsize - 1) + { + ds_resize (s, strsize * 2 + 2); + strsize = s->ds_length; + } + s->ds_string[insize++] = next_ch; + next_ch = getc (f); + } + s->ds_string[insize++] = '\0'; + + if (insize == 1 && next_ch == EOF) + return NULL; + else + return s->ds_string; +} + +char * +ds_fgets (f, s) + FILE *f; + dynamic_string *s; +{ + return ds_fgetstr (f, s, '\n'); +} + +char * +ds_fgetname (f, s) + FILE *f; + dynamic_string *s; +{ + return ds_fgetstr (f, s, '\0'); +} diff --git a/gnu/usr.bin/cpio/dstring.h b/gnu/usr.bin/cpio/dstring.h new file mode 100644 index 0000000000..369da0bcbe --- /dev/null +++ b/gnu/usr.bin/cpio/dstring.h @@ -0,0 +1,49 @@ +/* dstring.h - Dynamic string handling include file. Requires strings.h. + Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef NULL +#define NULL 0 +#endif + +/* A dynamic string consists of record that records the size of an + allocated string and the pointer to that string. The actual string + is a normal zero byte terminated string that can be used with the + usual string functions. The major difference is that the + dynamic_string routines know how to get more space if it is needed + by allocating new space and copying the current string. */ + +typedef struct +{ + int ds_length; /* Actual amount of storage allocated. */ + char *ds_string; /* String. */ +} dynamic_string; + + +/* Macros that look similar to the original string functions. + WARNING: These macros work only on pointers to dynamic string records. + If used with a real record, an "&" must be used to get the pointer. */ +#define ds_strlen(s) strlen ((s)->ds_string) +#define ds_strcmp(s1, s2) strcmp ((s1)->ds_string, (s2)->ds_string) +#define ds_strncmp(s1, s2, n) strncmp ((s1)->ds_string, (s2)->ds_string, n) +#define ds_index(s, c) index ((s)->ds_string, c) +#define ds_rindex(s, c) rindex ((s)->ds_string, c) + +void ds_init (); +void ds_resize (); +char *ds_fgetname (); +char *ds_fgets (); +char *ds_fgetstr (); diff --git a/gnu/usr.bin/cpio/error.c b/gnu/usr.bin/cpio/error.c new file mode 100644 index 0000000000..e849c5b2fb --- /dev/null +++ b/gnu/usr.bin/cpio/error.c @@ -0,0 +1,106 @@ +/* error.c -- error handler for noninteractive utilities + Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by David MacKenzie. */ + +#include + +#ifdef HAVE_VPRINTF + +#if __STDC__ +#include +#define VA_START(args, lastarg) va_start(args, lastarg) +#else /* !__STDC__ */ +#include +#define VA_START(args, lastarg) va_start(args) +#endif /* !__STDC__ */ + +#else /* !HAVE_VPRINTF */ + +#ifdef HAVE_DOPRNT +#define va_alist args +#define va_dcl int args; +#else /* !HAVE_DOPRNT */ +#define va_alist a1, a2, a3, a4, a5, a6, a7, a8 +#define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8; +#endif /* !HAVE_DOPRNT */ + +#endif /* !HAVE_VPRINTF */ + +#ifdef STDC_HEADERS +#include +#include +#else /* !STDC_HEADERS */ +void exit (); +#endif /* !STDC_HEADERS */ + +extern char *program_name; + +#ifndef HAVE_STRERROR +static char * +private_strerror (errnum) + int errnum; +{ + extern char *sys_errlist[]; + extern int sys_nerr; + + if (errnum > 0 && errnum <= sys_nerr) + return sys_errlist[errnum]; + return "Unknown system error"; +} +#define strerror private_strerror +#endif /* !HAVE_STRERROR */ + +/* Print the program name and error message MESSAGE, which is a printf-style + format string with optional args. + If ERRNUM is nonzero, print its corresponding system error message. + Exit with status STATUS if it is nonzero. */ +/* VARARGS */ +void +#if defined (HAVE_VPRINTF) && __STDC__ +error (int status, int errnum, char *message, ...) +#else /* !HAVE_VPRINTF or !__STDC__ */ +error (status, errnum, message, va_alist) + int status; + int errnum; + char *message; + va_dcl +#endif /* !HAVE_VPRINTF or !__STDC__ */ +{ +#ifdef HAVE_VPRINTF + va_list args; +#endif /* HAVE_VPRINTF */ + + fprintf (stderr, "%s: ", program_name); +#ifdef HAVE_VPRINTF + VA_START (args, message); + vfprintf (stderr, message, args); + va_end (args); +#else /* !HAVE_VPRINTF */ +#ifdef HAVE_DOPRNT + _doprnt (message, &args, stderr); +#else /* !HAVE_DOPRNT */ + fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8); +#endif /* !HAVE_DOPRNT */ +#endif /* !HAVE_VPRINTF */ + if (errnum) + fprintf (stderr, ": %s", strerror (errnum)); + putc ('\n', stderr); + fflush (stderr); + if (status) + exit (status); +} diff --git a/gnu/usr.bin/cpio/extern.h b/gnu/usr.bin/cpio/extern.h new file mode 100644 index 0000000000..50b152cdd9 --- /dev/null +++ b/gnu/usr.bin/cpio/extern.h @@ -0,0 +1,176 @@ +/* extern.h - External declarations for cpio. Requires system.h. + Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +enum archive_format +{ + arf_unknown, arf_binary, arf_oldascii, arf_newascii, arf_crcascii, + arf_tar, arf_ustar, arf_hpoldascii, arf_hpbinary +}; +extern enum archive_format archive_format; +extern int reset_time_flag; +extern int io_block_size; +extern int create_dir_flag; +extern int rename_flag; +extern int table_flag; +extern int unconditional_flag; +extern int verbose_flag; +extern int dot_flag; +extern int link_flag; +extern int retain_time_flag; +extern int crc_i_flag; +extern int append_flag; +extern int swap_bytes_flag; +extern int swap_halfwords_flag; +extern int swapping_bytes; +extern int swapping_halfwords; +extern int set_owner_flag; +extern uid_t set_owner; +extern int set_group_flag; +extern gid_t set_group; +extern int no_chown_flag; +extern int last_header_start; +extern int copy_matching_files; +extern int numeric_uid; +extern char *pattern_file_name; +extern char *new_media_message; +extern char *new_media_message_with_number; +extern char *new_media_message_after_number; +extern int archive_des; +extern char *archive_name; +extern unsigned long crc; +#ifdef DEBUG_CPIO +extern int debug_flag; +#endif + +extern char *input_buffer, *output_buffer; +extern char *in_buff, *out_buff; +extern long input_size, output_size; +extern long input_bytes, output_bytes; +extern char zeros_512[]; +extern char *directory_name; +extern char **save_patterns; +extern int num_patterns; +extern char name_end; +extern char input_is_special; +extern char output_is_special; +extern char input_is_seekable; +extern char output_is_seekable; +extern int f_force_local; +extern char *program_name; +extern int (*xstat) (); +extern void (*copy_function) (); + +#if __STDC__ || defined(__MSDOS__) +# define P_(s) s +#else +# define P_(s) () +#endif + +/* copyin.c */ +void read_in_header P_((struct new_cpio_header *file_hdr, int in_des)); +void read_in_old_ascii P_((struct new_cpio_header *file_hdr, int in_des)); +void read_in_new_ascii P_((struct new_cpio_header *file_hdr, int in_des)); +void read_in_binary P_((struct new_cpio_header *file_hdr, int in_des)); +void swab_array P_((char *arg, int count)); +void process_copy_in P_((void)); +void long_format P_((struct new_cpio_header *file_hdr, char *link_name)); +void print_name_with_quoting P_((char *p)); + +/* copyout.c */ +void write_out_header P_((struct new_cpio_header *file_hdr, int out_des)); +void process_copy_out P_((void)); + +/* copypass.c */ +void process_copy_pass P_((void)); + +/* dirname.c */ +char *dirname P_((char *path)); + +/* error.c */ +void error P_((int status, int errnum, char *message, ...)); + +/* filemode.c */ +void mode_string P_((unsigned int mode, char *str)); + +/* idcache.c */ +#ifndef __MSDOS__ +char *getgroup (); +char *getuser (); +uid_t *getuidbyname (); +gid_t *getgidbyname (); +#endif + +/* main.c */ +void process_args P_((int argc, char *argv[])); +void initialize_buffers P_((void)); + +/* makepath.c */ +int make_path P_((char *argpath, int mode, int parent_mode, + uid_t owner, gid_t group, char *verbose_fmt_string)); + +/* stripslash.c */ +void strip_trailing_slashes P_((char *path)); + +/* tar.c */ +void write_out_tar_header P_((struct new_cpio_header *file_hdr, int out_des)); +int null_block P_((long *block, int size)); +void read_in_tar_header P_((struct new_cpio_header *file_hdr, int in_des)); +int otoa P_((char *s, unsigned long *n)); +int is_tar_header P_((char *buf)); +int is_tar_filename_too_long P_((char *name)); + +/* userspec.c */ +#ifndef __MSDOS__ +char *parse_user_spec P_((char *name, uid_t *uid, gid_t *gid, + char **username, char **groupname)); +#endif + +/* util.c */ +void empty_output_buffer P_((int out_des)); +void swahw_array P_((char *ptr, int count)); +void fill_input_buffer P_((int in_des, int num_bytes)); +void copy_buf_out P_((char *in_buf, int out_des, long num_bytes)); +void copy_in_buf P_((char *in_buf, int in_des, long num_bytes)); +int peek_in_buf P_((char *peek_buf, int in_des, int num_bytes)); +void toss_input P_((int in_des, long num_bytes)); +void copy_files P_((int in_des, int out_des, long num_bytes)); +void create_all_directories P_((char *name)); +void prepare_append P_((int out_file_des)); +char *find_inode_file P_((unsigned long node_num, + unsigned long major_num, unsigned long minor_num)); +void add_inode P_((unsigned long node_num, char *file_name, + unsigned long major_num, unsigned long minor_num)); +int open_archive P_((char *file)); +void tape_offline P_((int tape_des)); +void get_next_reel P_((int tape_des)); +void set_new_media_message P_((char *message)); +#ifdef __MSDOS__ +int chown P_((char *path, int owner, int group)); +#endif +#ifdef __TURBOC__ +int utime P_((char *filename, struct utimbuf *utb)); +#endif +#ifdef HPUX_CDF +char *add_cdf_double_slashes P_((char *filename)); +#endif + +/* xmalloc.c */ +char *xmalloc P_((unsigned n)); +char *xrealloc P_((char *p, unsigned n)); + +/* xstrdup.c */ +char *xstrdup P_((char *string)); diff --git a/gnu/usr.bin/cpio/filemode.c b/gnu/usr.bin/cpio/filemode.c new file mode 100644 index 0000000000..9293af6815 --- /dev/null +++ b/gnu/usr.bin/cpio/filemode.c @@ -0,0 +1,229 @@ +/* filemode.c -- make a string describing file modes + Copyright (C) 1985, 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#ifndef S_IREAD +#define S_IREAD S_IRUSR +#define S_IWRITE S_IWUSR +#define S_IEXEC S_IXUSR +#endif + +#if !defined(S_ISREG) || defined(NO_MODE_T) +/* Doesn't have POSIX.1 stat stuff or doesn't have mode_t. */ +#define mode_t unsigned short +#endif + +#if !defined(S_ISBLK) && defined(S_IFBLK) +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#endif +#if !defined(S_ISCHR) && defined(S_IFCHR) +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISDIR) && defined(S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISFIFO) && defined(S_IFIFO) +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISLNK) && defined(S_IFLNK) +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) && defined(S_IFSOCK) +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */ +#define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) +#define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) +#endif +#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */ +#define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) +#endif + +void mode_string (); +static char ftypelet (); +static void rwx (); +static void setst (); + +/* filemodestring - fill in string STR with an ls-style ASCII + representation of the st_mode field of file stats block STATP. + 10 characters are stored in STR; no terminating null is added. + The characters stored in STR are: + + 0 File type. 'd' for directory, 'c' for character + special, 'b' for block special, 'm' for multiplex, + 'l' for symbolic link, 's' for socket, 'p' for fifo, + '-' for regular, '?' for any other file type + + 1 'r' if the owner may read, '-' otherwise. + + 2 'w' if the owner may write, '-' otherwise. + + 3 'x' if the owner may execute, 's' if the file is + set-user-id, '-' otherwise. + 'S' if the file is set-user-id, but the execute + bit isn't set. + + 4 'r' if group members may read, '-' otherwise. + + 5 'w' if group members may write, '-' otherwise. + + 6 'x' if group members may execute, 's' if the file is + set-group-id, '-' otherwise. + 'S' if it is set-group-id but not executable. + + 7 'r' if any user may read, '-' otherwise. + + 8 'w' if any user may write, '-' otherwise. + + 9 'x' if any user may execute, 't' if the file is "sticky" + (will be retained in swap space after execution), '-' + otherwise. + 'T' if the file is sticky but not executable. */ + +void +filemodestring (statp, str) + struct stat *statp; + char *str; +{ + mode_string (statp->st_mode, str); +} + +/* Like filemodestring, but only the relevant part of the `struct stat' + is given as an argument. */ + +void +mode_string (mode, str) + unsigned short mode; + char *str; +{ + str[0] = ftypelet (mode); + rwx ((mode & 0700) << 0, &str[1]); + rwx ((mode & 0070) << 3, &str[4]); + rwx ((mode & 0007) << 6, &str[7]); + setst (mode, str); +} + +/* Return a character indicating the type of file described by + file mode BITS: + 'd' for directories + 'b' for block special files + 'c' for character special files + 'm' for multiplexor files + 'l' for symbolic links + 's' for sockets + 'p' for fifos + '-' for regular files + '?' for any other file type. */ + +static char +ftypelet (bits) + mode_t bits; +{ +#ifdef S_ISBLK + if (S_ISBLK (bits)) + return 'b'; +#endif + if (S_ISCHR (bits)) + return 'c'; + if (S_ISDIR (bits)) + return 'd'; + if (S_ISREG (bits)) + return '-'; +#ifdef S_ISFIFO + if (S_ISFIFO (bits)) + return 'p'; +#endif +#ifdef S_ISLNK + if (S_ISLNK (bits)) + return 'l'; +#endif +#ifdef S_ISSOCK + if (S_ISSOCK (bits)) + return 's'; +#endif +#ifdef S_ISMPC + if (S_ISMPC (bits)) + return 'm'; +#endif +#ifdef S_ISNWK + if (S_ISNWK (bits)) + return 'n'; +#endif + return '?'; +} + +/* Look at read, write, and execute bits in BITS and set + flags in CHARS accordingly. */ + +static void +rwx (bits, chars) + unsigned short bits; + char *chars; +{ + chars[0] = (bits & S_IREAD) ? 'r' : '-'; + chars[1] = (bits & S_IWRITE) ? 'w' : '-'; + chars[2] = (bits & S_IEXEC) ? 'x' : '-'; +} + +/* Set the 's' and 't' flags in file attributes string CHARS, + according to the file mode BITS. */ + +static void +setst (bits, chars) + unsigned short bits; + char *chars; +{ +#ifdef S_ISUID + if (bits & S_ISUID) + { + if (chars[3] != 'x') + /* Set-uid, but not executable by owner. */ + chars[3] = 'S'; + else + chars[3] = 's'; + } +#endif +#ifdef S_ISGID + if (bits & S_ISGID) + { + if (chars[6] != 'x') + /* Set-gid, but not executable by group. */ + chars[6] = 'S'; + else + chars[6] = 's'; + } +#endif +#ifdef S_ISVTX + if (bits & S_ISVTX) + { + if (chars[9] != 'x') + /* Sticky, but not executable by others. */ + chars[9] = 'T'; + else + chars[9] = 't'; + } +#endif +} diff --git a/gnu/usr.bin/cpio/filetypes.h b/gnu/usr.bin/cpio/filetypes.h new file mode 100644 index 0000000000..46a79a9c3b --- /dev/null +++ b/gnu/usr.bin/cpio/filetypes.h @@ -0,0 +1,84 @@ +/* filetypes.h - deal with POSIX annoyances + Copyright (C) 1991 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Include sys/types.h and sys/stat.h before this file. */ + +#ifndef S_ISREG /* Doesn't have POSIX.1 stat stuff. */ +#define mode_t unsigned short +#endif + +/* Define the POSIX macros for systems that lack them. */ +#if !defined(S_ISBLK) && defined(S_IFBLK) +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#endif +#if !defined(S_ISCHR) && defined(S_IFCHR) +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISDIR) && defined(S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISFIFO) && defined(S_IFIFO) +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISLNK) && defined(S_IFLNK) +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) && defined(S_IFSOCK) +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX network special */ +#define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) +#endif + +/* Define the file type bits used in cpio archives. + They have the same values as the S_IF bits in traditional Unix. */ + +#define CP_IFMT 0170000 /* Mask for all file type bits. */ + +#if defined(S_ISBLK) +#define CP_IFBLK 0060000 +#endif +#if defined(S_ISCHR) +#define CP_IFCHR 0020000 +#endif +#if defined(S_ISDIR) +#define CP_IFDIR 0040000 +#endif +#if defined(S_ISREG) +#define CP_IFREG 0100000 +#endif +#if defined(S_ISFIFO) +#define CP_IFIFO 0010000 +#endif +#if defined(S_ISLNK) +#define CP_IFLNK 0120000 +#endif +#if defined(S_ISSOCK) +#define CP_IFSOCK 0140000 +#endif +#if defined(S_ISNWK) +#define CP_IFNWK 0110000 +#endif + +#ifndef S_ISLNK +#define lstat stat +#endif +int lstat (); +int stat (); diff --git a/gnu/usr.bin/cpio/fnmatch.c b/gnu/usr.bin/cpio/fnmatch.c new file mode 100644 index 0000000000..8a25a905d1 --- /dev/null +++ b/gnu/usr.bin/cpio/fnmatch.c @@ -0,0 +1,200 @@ +/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +#if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS) +extern int errno; +#endif + +/* Match STRING against the filename pattern PATTERN, returning zero if + it matches, nonzero if not. */ +int +fnmatch (pattern, string, flags) + const char *pattern; + const char *string; + int flags; +{ + register const char *p = pattern, *n = string; + register char c; + +/* Note that this evalutes C many times. */ +#define FOLD(c) ((flags & FNM_CASEFOLD) && isupper (c) ? tolower (c) : (c)) + + while ((c = *p++) != '\0') + { + c = FOLD (c); + + switch (c) + { + case '?': + if (*n == '\0') + return FNM_NOMATCH; + else if ((flags & FNM_FILE_NAME) && *n == '/') + return FNM_NOMATCH; + else if ((flags & FNM_PERIOD) && *n == '.' && + (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/'))) + return FNM_NOMATCH; + break; + + case '\\': + if (!(flags & FNM_NOESCAPE)) + { + c = *p++; + c = FOLD (c); + } + if (FOLD (*n) != c) + return FNM_NOMATCH; + break; + + case '*': + if ((flags & FNM_PERIOD) && *n == '.' && + (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/'))) + return FNM_NOMATCH; + + for (c = *p++; c == '?' || c == '*'; c = *p++, ++n) + if (((flags & FNM_FILE_NAME) && *n == '/') || + (c == '?' && *n == '\0')) + return FNM_NOMATCH; + + if (c == '\0') + return 0; + + { + char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c; + c1 = FOLD (c1); + for (--p; *n != '\0'; ++n) + if ((c == '[' || FOLD (*n) == c1) && + fnmatch (p, n, flags & ~FNM_PERIOD) == 0) + return 0; + return FNM_NOMATCH; + } + + case '[': + { + /* Nonzero if the sense of the character class is inverted. */ + register int not; + + if (*n == '\0') + return FNM_NOMATCH; + + if ((flags & FNM_PERIOD) && *n == '.' && + (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/'))) + return FNM_NOMATCH; + + not = (*p == '!' || *p == '^'); + if (not) + ++p; + + c = *p++; + for (;;) + { + register char cstart = c, cend = c; + + if (!(flags & FNM_NOESCAPE) && c == '\\') + cstart = cend = *p++; + + cstart = cend = FOLD (cstart); + + if (c == '\0') + /* [ (unterminated) loses. */ + return FNM_NOMATCH; + + c = *p++; + c = FOLD (c); + + if ((flags & FNM_FILE_NAME) && c == '/') + /* [/] can never match. */ + return FNM_NOMATCH; + + if (c == '-' && *p != ']') + { + cend = *p++; + if (!(flags & FNM_NOESCAPE) && cend == '\\') + cend = *p++; + if (cend == '\0') + return FNM_NOMATCH; + cend = FOLD (cend); + + c = *p++; + } + + if (FOLD (*n) >= cstart && FOLD (*n) <= cend) + goto matched; + + if (c == ']') + break; + } + if (!not) + return FNM_NOMATCH; + break; + + matched:; + /* Skip the rest of the [...] that already matched. */ + while (c != ']') + { + if (c == '\0') + /* [... (unterminated) loses. */ + return FNM_NOMATCH; + + c = *p++; + if (!(flags & FNM_NOESCAPE) && c == '\\') + /* XXX 1003.2d11 is unclear if this is right. */ + ++p; + } + if (not) + return FNM_NOMATCH; + } + break; + + default: + if (c != FOLD (*n)) + return FNM_NOMATCH; + } + + ++n; + } + + if (*n == '\0') + return 0; + + if ((flags & FNM_LEADING_DIR) && *n == '/') + /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */ + return 0; + + return FNM_NOMATCH; +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ diff --git a/gnu/usr.bin/cpio/fnmatch.h b/gnu/usr.bin/cpio/fnmatch.h new file mode 100644 index 0000000000..5c94813f4b --- /dev/null +++ b/gnu/usr.bin/cpio/fnmatch.h @@ -0,0 +1,60 @@ +/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#ifndef _FNMATCH_H + +#define _FNMATCH_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined (__cplusplus) || (defined (__STDC__) && __STDC__) +#undef __P +#define __P(args) args +#else /* Not C++ or ANSI C. */ +#undef __P +#define __P(args) () +/* We can get away without defining `const' here only because in this file + it is used only inside the prototype for `fnmatch', which is elided in + non-ANSI C where `const' is problematical. */ +#endif /* C++ or ANSI C. */ + +/* Bits set in the FLAGS argument to `fnmatch'. */ +#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */ +#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */ +#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */ + +#if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_GNU_SOURCE) +#define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */ +#define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */ +#define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */ +#endif + +/* Value returned by `fnmatch' if STRING does not match PATTERN. */ +#define FNM_NOMATCH 1 + +/* Match STRING against the filename pattern PATTERN, + returning zero if it matches, FNM_NOMATCH if not. */ +extern int fnmatch __P ((const char *__pattern, const char *__string, + int __flags)); + +#ifdef __cplusplus +} +#endif + +#endif /* fnmatch.h */ diff --git a/gnu/usr.bin/cpio/getopt.c b/gnu/usr.bin/cpio/getopt.c new file mode 100644 index 0000000000..2867a90f65 --- /dev/null +++ b/gnu/usr.bin/cpio/getopt.c @@ -0,0 +1,744 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if !__STDC__ && !defined(const) && IN_GCC +#define const +#endif + +/* This tells Alpha OSF/1 not to define a getopt prototype in . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#endif /* GNU C library. */ + +/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a + long-named option. Because this is not POSIX.2 compliant, it is + being phased out. */ +/* #define GETOPT_COMPAT */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = 0; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +char *getenv (); + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +#ifdef IN_GCC +#include "gstddef.h" +#else +#include +#endif +extern size_t strlen (const char *); +#endif + +#endif /* GNU C library. */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + int option_index; + + optarg = 0; + + /* Initialize the internal data when the first call is made. + Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + if (optind == 0) + { + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (getenv ("POSIXLY_CORRECT") != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + } + + if (nextchar == NULL || *nextchar == '\0') + { + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Now skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + optind++; + last_nonopt = optind; + } + + /* Special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Start decoding its characters. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + if (longopts != NULL + && ((argv[optind][0] == '-' + && (argv[optind][1] == '-' || long_only)) +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + )) + { + const struct option *p; + char *s = nextchar; + int exact = 0; + int ambig = 0; + const struct option *pfound = NULL; + int indfound; + + while (*s && *s != '=') + s++; + + /* Test all options for either exact match or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; + p++, option_index++) + if (!strncmp (p->name, nextchar, s - nextchar)) + { + if (s - nextchar == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*s) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = s + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return '?'; + } + } + + /* Look at and handle the next option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { +#if 0 + if (c < 040 || c >= 0177) + fprintf (stderr, "%s: unrecognized option, character code 0%o\n", + argv[0], c); + else + fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); +#endif + } + optopt = c; + return '?'; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = 0; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { +#if 0 + fprintf (stderr, "%s: option `-%c' requires an argument\n", + argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/gnu/usr.bin/diff3/getopt.h b/gnu/usr.bin/cpio/getopt.h similarity index 100% rename from gnu/usr.bin/diff3/getopt.h rename to gnu/usr.bin/cpio/getopt.h diff --git a/gnu/usr.bin/diff3/getopt1.c b/gnu/usr.bin/cpio/getopt1.c similarity index 100% rename from gnu/usr.bin/diff3/getopt1.c rename to gnu/usr.bin/cpio/getopt1.c diff --git a/gnu/usr.bin/cpio/global.c b/gnu/usr.bin/cpio/global.c new file mode 100644 index 0000000000..d4b54415f1 --- /dev/null +++ b/gnu/usr.bin/cpio/global.c @@ -0,0 +1,168 @@ +/* global.c - global variables and initial values for cpio. + Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include "cpiohdr.h" +#include "dstring.h" +#include "system.h" +#include "extern.h" + +/* If TRUE, reset access times after reading files (-a). */ +int reset_time_flag = FALSE; + +/* Block size value, initially 512. -B sets to 5120. */ +int io_block_size = 512; + +/* The header format to recognize and produce. */ +enum archive_format archive_format = arf_unknown; + +/* If TRUE, create directories as needed. (-d with -i or -p) */ +int create_dir_flag = FALSE; + +/* If TRUE, interactively rename files. (-r) */ +int rename_flag = FALSE; + +/* If TRUE, print a table of contents of input. (-t) */ +int table_flag = FALSE; + +/* If TRUE, copy unconditionally (older replaces newer). (-u) */ +int unconditional_flag = FALSE; + +/* If TRUE, list the files processed, or ls -l style output with -t. (-v) */ +int verbose_flag = FALSE; + +/* If TRUE, print a . for each file processed. (-V) */ +int dot_flag = FALSE; + +/* If TRUE, link files whenever possible. Used with -p option. (-l) */ +int link_flag = FALSE; + +/* If TRUE, retain previous file modification time. (-m) */ +int retain_time_flag = FALSE; + +/* Set TRUE if crc_flag is TRUE and we are doing a cpio -i. Used + by copy_files so it knows whether to compute the crc. */ +int crc_i_flag = FALSE; + +/* If TRUE, append to end of archive. (-A) */ +int append_flag = FALSE; + +/* If TRUE, swap bytes of each file during cpio -i. */ +int swap_bytes_flag = FALSE; + +/* If TRUE, swap halfwords of each file during cpio -i. */ +int swap_halfwords_flag = FALSE; + +/* If TRUE, we are swapping halfwords on the current file. */ +int swapping_halfwords = FALSE; + +/* If TRUE, we are swapping bytes on the current file. */ +int swapping_bytes = FALSE; + +/* If TRUE, set ownership of all files to UID `set_owner'. */ +int set_owner_flag = FALSE; +uid_t set_owner; + +/* If TRUE, set group ownership of all files to GID `set_group'. */ +int set_group_flag = FALSE; +gid_t set_group; + +/* If TRUE, do not chown the files. */ +int no_chown_flag = FALSE; + +#ifdef DEBUG_CPIO +/* If TRUE, print debugging information. */ +int debug_flag = FALSE; +#endif + +/* File position of last header read. Only used during -A to determine + where the old TRAILER!!! record started. */ +int last_header_start = 0; + +/* With -i; if TRUE, copy only files that match any of the given patterns; + if FALSE, copy only files that do not match any of the patterns. (-f) */ +int copy_matching_files = TRUE; + +/* With -itv; if TRUE, list numeric uid and gid instead of translating them + into names. */ +int numeric_uid = FALSE; + +/* Name of file containing additional patterns (-E). */ +char *pattern_file_name = NULL; + +/* Message to print when end of medium is reached (-M). */ +char *new_media_message = NULL; + +/* With -M with %d, message to print when end of medium is reached. */ +char *new_media_message_with_number = NULL; +char *new_media_message_after_number = NULL; + +/* File descriptor containing the archive. */ +int archive_des; + +/* Name of file containing the archive, if known; NULL if stdin/out. */ +char *archive_name = NULL; + +/* CRC checksum. */ +unsigned long crc; + +/* Input and output buffers. */ +char *input_buffer, *output_buffer; + +/* Current locations in `input_buffer' and `output_buffer'. */ +char *in_buff, *out_buff; + +/* Current number of bytes stored at `input_buff' and `output_buff'. */ +long input_size, output_size; + +/* Total number of bytes read and written for all files. */ +long input_bytes, output_bytes; + +/* 512 bytes of 0; used for various padding operations. */ +char zeros_512[512]; + +/* Saving of argument values for later reference. */ +char *directory_name = NULL; +char **save_patterns; +int num_patterns; + +/* Character that terminates file names read from stdin. */ +char name_end = '\n'; + +/* TRUE if input (cpio -i) or output (cpio -o) is a device node. */ +char input_is_special = FALSE; +char output_is_special = FALSE; + +/* TRUE if lseek works on the input. */ +char input_is_seekable = FALSE; + +/* TRUE if lseek works on the output. */ +char output_is_seekable = FALSE; + +/* If nonzero, don't consider file names that contain a `:' to be + on remote hosts; all files are local. */ +int f_force_local = 0; + +/* The name this program was run with. */ +char *program_name; + +/* A pointer to either lstat or stat, depending on whether + dereferencing of symlinks is done for input files. */ +int (*xstat) (); + +/* Which copy operation to perform. (-i, -o, -p) */ +void (*copy_function) () = 0; diff --git a/gnu/usr.bin/cpio/idcache.c b/gnu/usr.bin/cpio/idcache.c new file mode 100644 index 0000000000..dd9c366b16 --- /dev/null +++ b/gnu/usr.bin/cpio/idcache.c @@ -0,0 +1,206 @@ +/* idcache.c -- map user and group IDs, cached for speed + Copyright (C) 1985, 1988, 1989, 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include + +#if defined(STDC_HEADERS) || defined(HAVE_STRING_H) +#include +#else +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifndef _POSIX_VERSION +struct passwd *getpwuid (); +struct passwd *getpwnam (); +struct group *getgrgid (); +struct group *getgrnam (); +#endif + +char *xmalloc (); +char *xstrdup (); + +struct userid +{ + union + { + uid_t u; + gid_t g; + } id; + char *name; + struct userid *next; +}; + +static struct userid *user_alist; + +/* The members of this list have names not in the local passwd file. */ +static struct userid *nouser_alist; + +/* Translate UID to a login name or a stringified number, + with cache. */ + +char * +getuser (uid) + uid_t uid; +{ + register struct userid *tail; + struct passwd *pwent; + char usernum_string[20]; + + for (tail = user_alist; tail; tail = tail->next) + if (tail->id.u == uid) + return tail->name; + + pwent = getpwuid (uid); + tail = (struct userid *) xmalloc (sizeof (struct userid)); + tail->id.u = uid; + if (pwent == 0) + { + sprintf (usernum_string, "%u", (unsigned) uid); + tail->name = xstrdup (usernum_string); + } + else + tail->name = xstrdup (pwent->pw_name); + + /* Add to the head of the list, so most recently used is first. */ + tail->next = user_alist; + user_alist = tail; + return tail->name; +} + +/* Translate USER to a UID, with cache. + Return NULL if there is no such user. + (We also cache which user names have no passwd entry, + so we don't keep looking them up.) */ + +uid_t * +getuidbyname (user) + char *user; +{ + register struct userid *tail; + struct passwd *pwent; + + for (tail = user_alist; tail; tail = tail->next) + /* Avoid a function call for the most common case. */ + if (*tail->name == *user && !strcmp (tail->name, user)) + return &tail->id.u; + + for (tail = nouser_alist; tail; tail = tail->next) + /* Avoid a function call for the most common case. */ + if (*tail->name == *user && !strcmp (tail->name, user)) + return 0; + + pwent = getpwnam (user); + + tail = (struct userid *) xmalloc (sizeof (struct userid)); + tail->name = xstrdup (user); + + /* Add to the head of the list, so most recently used is first. */ + if (pwent) + { + tail->id.u = pwent->pw_uid; + tail->next = user_alist; + user_alist = tail; + return &tail->id.u; + } + + tail->next = nouser_alist; + nouser_alist = tail; + return 0; +} + +/* Use the same struct as for userids. */ +static struct userid *group_alist; +static struct userid *nogroup_alist; + +/* Translate GID to a group name or a stringified number, + with cache. */ + +char * +getgroup (gid) + gid_t gid; +{ + register struct userid *tail; + struct group *grent; + char groupnum_string[20]; + + for (tail = group_alist; tail; tail = tail->next) + if (tail->id.g == gid) + return tail->name; + + grent = getgrgid (gid); + tail = (struct userid *) xmalloc (sizeof (struct userid)); + tail->id.g = gid; + if (grent == 0) + { + sprintf (groupnum_string, "%u", (unsigned int) gid); + tail->name = xstrdup (groupnum_string); + } + else + tail->name = xstrdup (grent->gr_name); + + /* Add to the head of the list, so most recently used is first. */ + tail->next = group_alist; + group_alist = tail; + return tail->name; +} + +/* Translate GROUP to a UID, with cache. + Return NULL if there is no such group. + (We also cache which group names have no group entry, + so we don't keep looking them up.) */ + +gid_t * +getgidbyname (group) + char *group; +{ + register struct userid *tail; + struct group *grent; + + for (tail = group_alist; tail; tail = tail->next) + /* Avoid a function call for the most common case. */ + if (*tail->name == *group && !strcmp (tail->name, group)) + return &tail->id.g; + + for (tail = nogroup_alist; tail; tail = tail->next) + /* Avoid a function call for the most common case. */ + if (*tail->name == *group && !strcmp (tail->name, group)) + return 0; + + grent = getgrnam (group); + + tail = (struct userid *) xmalloc (sizeof (struct userid)); + tail->name = xstrdup (group); + + /* Add to the head of the list, so most recently used is first. */ + if (grent) + { + tail->id.g = grent->gr_gid; + tail->next = group_alist; + group_alist = tail; + return &tail->id.g; + } + + tail->next = nogroup_alist; + nogroup_alist = tail; + return 0; +} diff --git a/gnu/usr.bin/cpio/main.c b/gnu/usr.bin/cpio/main.c new file mode 100644 index 0000000000..78b5fc5314 --- /dev/null +++ b/gnu/usr.bin/cpio/main.c @@ -0,0 +1,479 @@ +/* main.c - main program and argument processing for cpio. + Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by Phil Nelson , + David MacKenzie , + and John Oleynick . */ + +#include +#include +#include +#include +#include "filetypes.h" +#include "system.h" +#include "cpiohdr.h" +#include "dstring.h" +#include "extern.h" +#include "rmt.h" + +struct option long_opts[] = +{ + {"null", 0, 0, '0'}, + {"append", 0, 0, 'A'}, + {"block-size", 1, 0, 130}, + {"create", 0, 0, 'o'}, + {"dereference", 0, 0, 'L'}, + {"dot", 0, 0, 'V'}, + {"extract", 0, 0, 'i'}, + {"file", 1, 0, 'F'}, + {"force-local", 0, &f_force_local, 1}, + {"format", 1, 0, 'H'}, + {"help", 0, 0, 132}, + {"io-size", 1, 0, 'C'}, + {"link", 0, &link_flag, TRUE}, + {"list", 0, &table_flag, TRUE}, + {"make-directories", 0, &create_dir_flag, TRUE}, + {"message", 1, 0, 'M'}, + {"no-preserve-owner", 0, 0, 134}, + {"nonmatching", 0, ©_matching_files, FALSE}, + {"numeric-uid-gid", 0, &numeric_uid, TRUE}, + {"owner", 1, 0, 'R'}, + {"pass-through", 0, 0, 'p'}, + {"pattern-file", 1, 0, 'E'}, + {"preserve-modification-time", 0, &retain_time_flag, TRUE}, + {"rename", 0, &rename_flag, TRUE}, + {"swap", 0, 0, 'b'}, + {"swap-bytes", 0, 0, 's'}, + {"swap-halfwords", 0, 0, 'S'}, + {"reset-access-time", 0, &reset_time_flag, TRUE}, + {"unconditional", 0, &unconditional_flag, TRUE}, + {"verbose", 0, &verbose_flag, TRUE}, + {"version", 0, 0, 131}, +#ifdef DEBUG_CPIO + {"debug", 0, &debug_flag, TRUE}, +#endif + {0, 0, 0, 0} +}; + +/* Print usage message and exit with error. */ + +void +usage (fp, status) + FILE *fp; + int status; +{ + fprintf (fp, "\ +Usage: %s {-o|--create} [-0acvABLV] [-C bytes] [-H format] [-M message]\n\ + [-O [[user@]host:]archive] [-F [[user@]host:]archive]\n\ + [--file=[[user@]host:]archive] [--format=format] [--message=message]\n\ + [--null] [--reset-access-time] [--verbose] [--dot] [--append]\n\ + [--block-size=blocks] [--dereference] [--io-size=bytes]\n\ + [--force-local] [--help] [--version] < name-list [> archive]\n", program_name); + fprintf (fp, "\ + %s {-i|--extract} [-bcdfmnrtsuvBSV] [-C bytes] [-E file] [-H format]\n\ + [-M message] [-R [user][:.][group]] [-I [[user@]host:]archive]\n\ + [-F [[user@]host:]archive] [--file=[[user@]host:]archive]\n\ + [--make-directories] [--nonmatching] [--preserve-modification-time]\n\ + [--numeric-uid-gid] [--rename] [--list] [--swap-bytes] [--swap] [--dot]\n\ + [--unconditional] [--verbose] [--block-size=blocks] [--swap-halfwords]\n\ + [--io-size=bytes] [--pattern-file=file] [--format=format]\n\ + [--owner=[user][:.][group]] [--no-preserve-owner] [--message=message]\n\ + [--force-local] [--help] [--version] [pattern...] [< archive]\n", + program_name); + fprintf (fp, "\ + %s {-p|--pass-through} [-0adlmuvLV] [-R [user][:.][group]]\n\ + [--null] [--reset-access-time] [--make-directories] [--link]\n\ + [--preserve-modification-time] [--unconditional] [--verbose] [--dot]\n\ + [--dereference] [--owner=[user][:.][group]] [--no-preserve-owner]\n\ + [--help] [--version] destination-directory < name-list\n", program_name); + exit (status); +} + +/* Process the arguments. Set all options and set up the copy pass + directory or the copy in patterns. */ + +void +process_args (argc, argv) + int argc; + char *argv[]; +{ + extern char *version_string; + void (*copy_in) (); /* Work around for pcc bug. */ + void (*copy_out) (); + int c; + char *input_archive_name = 0; + char *output_archive_name = 0; + + if (argc < 2) + usage (stderr, 2); + + xstat = lstat; + + while ((c = getopt_long (argc, argv, + "0aAbBcC:dfE:F:H:iI:lLmM:noO:prR:sStuvVz", + long_opts, (int *) 0)) != -1) + { + switch (c) + { + case 0: /* A long option that just sets a flag. */ + break; + + case '0': /* Read null-terminated filenames. */ + name_end = '\0'; + break; + + case 'a': /* Reset access times. */ + reset_time_flag = TRUE; + break; + + case 'A': /* Append to the archive. */ + append_flag = TRUE; + break; + + case 'b': /* Swap bytes and halfwords. */ + swap_bytes_flag = TRUE; + swap_halfwords_flag = TRUE; + break; + + case 'B': /* Set block size to 5120. */ + io_block_size = 5120; + break; + + case 130: /* --block-size */ + io_block_size = atoi (optarg); + if (io_block_size < 1) + error (2, 0, "invalid block size"); + io_block_size *= 512; + break; + + case 'c': /* Use the old portable ASCII format. */ + if (archive_format != arf_unknown) + usage (stderr, 2); +#ifdef SVR4_COMPAT + archive_format = arf_newascii; /* -H newc. */ +#else + archive_format = arf_oldascii; /* -H odc. */ +#endif + break; + + case 'C': /* Block size. */ + io_block_size = atoi (optarg); + if (io_block_size < 1) + error (2, 0, "invalid block size"); + break; + + case 'd': /* Create directories where needed. */ + create_dir_flag = TRUE; + break; + + case 'f': /* Only copy files not matching patterns. */ + copy_matching_files = FALSE; + break; + + case 'E': /* Pattern file name. */ + pattern_file_name = optarg; + break; + + case 'F': /* Archive file name. */ + archive_name = optarg; + break; + + case 'H': /* Header format name. */ + if (archive_format != arf_unknown) + usage (stderr, 2); + if (!strcmp (optarg, "crc") || !strcmp (optarg, "CRC")) + archive_format = arf_crcascii; + else if (!strcmp (optarg, "newc") || !strcmp (optarg, "NEWC")) + archive_format = arf_newascii; + else if (!strcmp (optarg, "odc") || !strcmp (optarg, "ODC")) + archive_format = arf_oldascii; + else if (!strcmp (optarg, "bin") || !strcmp (optarg, "BIN")) + archive_format = arf_binary; + else if (!strcmp (optarg, "ustar") || !strcmp (optarg, "USTAR")) + archive_format = arf_ustar; + else if (!strcmp (optarg, "tar") || !strcmp (optarg, "TAR")) + archive_format = arf_tar; + else if (!strcmp (optarg, "hpodc") || !strcmp (optarg, "HPODC")) + archive_format = arf_hpoldascii; + else if (!strcmp (optarg, "hpbin") || !strcmp (optarg, "HPBIN")) + archive_format = arf_hpbinary; + else + error (2, 0, "\ +invalid archive format `%s'; valid formats are:\n\ +crc newc odc bin ustar tar (all-caps also recognized)", optarg); + break; + + case 'i': /* Copy-in mode. */ + if (copy_function != 0) + usage (stderr, 2); + copy_function = process_copy_in; + break; + + case 'I': /* Input archive file name. */ + input_archive_name = optarg; + break; + + case 'k': /* Handle corrupted archives. We always handle + corrupted archives, but recognize this + option for compatability. */ + break; + + case 'l': /* Link files when possible. */ + link_flag = TRUE; + break; + + case 'L': /* Dereference symbolic links. */ + xstat = stat; + break; + + case 'm': /* Retain previous file modify times. */ + retain_time_flag = TRUE; + break; + + case 'M': /* New media message. */ + set_new_media_message (optarg); + break; + + case 'n': /* Long list owner and group as numbers. */ + numeric_uid = TRUE; + break; + + case 134: /* --no-preserve-owner */ + if (set_owner_flag || set_group_flag) + usage (stderr, 2); + no_chown_flag = TRUE; + break; + + case 'o': /* Copy-out mode. */ + if (copy_function != 0) + usage (stderr, 2); + copy_function = process_copy_out; + break; + + case 'O': /* Output archive file name. */ + output_archive_name = optarg; + break; + + case 'p': /* Copy-pass mode. */ + if (copy_function != 0) + usage (stderr, 2); + copy_function = process_copy_pass; + break; + + case 'r': /* Interactively rename. */ + rename_flag = TRUE; + break; + + case 'R': /* Set the owner. */ + if (no_chown_flag) + usage (stderr, 2); +#ifndef __MSDOS__ + { + char *e, *u, *g; + + e = parse_user_spec (optarg, &set_owner, &set_group, &u, &g); + if (e) + error (2, 0, "%s: %s", optarg, e); + if (u) + { + free (u); + set_owner_flag = TRUE; + } + if (g) + { + free (g); + set_group_flag = TRUE; + } + } +#endif + break; + + case 's': /* Swap bytes. */ + swap_bytes_flag = TRUE; + break; + + case 'S': /* Swap halfwords. */ + swap_halfwords_flag = TRUE; + break; + + case 't': /* Only print a list. */ + table_flag = TRUE; + break; + + case 'u': /* Replace all! Unconditionally! */ + unconditional_flag = TRUE; + break; + + case 'v': /* Verbose! */ + verbose_flag = TRUE; + break; + + case 'V': /* Print `.' for each file. */ + dot_flag = TRUE; + break; + + case 131: + printf ("GNU cpio %s", version_string); + exit (0); + break; + + case 132: /* --help */ + usage (stdout, 0); + break; + + default: + usage (stderr, 2); + } + } + + /* Do error checking and look at other args. */ + + if (copy_function == 0) + { + if (table_flag) + copy_function = process_copy_in; + else + usage (stderr, 2); + } + + if ((!table_flag || !verbose_flag) && numeric_uid) + usage (stderr, 2); + + /* Work around for pcc bug. */ + copy_in = process_copy_in; + copy_out = process_copy_out; + + if (copy_function == copy_in) + { + archive_des = 0; + if (link_flag || reset_time_flag || xstat != lstat || append_flag + || output_archive_name + || (archive_name && input_archive_name)) + usage (stderr, 2); + if (archive_format == arf_crcascii) + crc_i_flag = TRUE; + num_patterns = argc - optind; + save_patterns = &argv[optind]; + if (input_archive_name) + archive_name = input_archive_name; + } + else if (copy_function == copy_out) + { + archive_des = 1; + if (argc != optind || create_dir_flag || rename_flag + || table_flag || unconditional_flag || link_flag + || retain_time_flag || no_chown_flag || set_owner_flag + || set_group_flag || swap_bytes_flag || swap_halfwords_flag + || (append_flag && !(archive_name || output_archive_name)) + || input_archive_name || (archive_name && output_archive_name)) + usage (stderr, 2); + if (archive_format == arf_unknown) + archive_format = arf_binary; + if (output_archive_name) + archive_name = output_archive_name; + } + else + { + /* Copy pass. */ + archive_des = -1; + if (argc - 1 != optind || archive_format != arf_unknown + || swap_bytes_flag || swap_halfwords_flag + || table_flag || rename_flag || append_flag) + usage (stderr, 2); + directory_name = argv[optind]; + } + + if (archive_name) + { + if (copy_function != copy_in && copy_function != copy_out) + usage (stderr, 2); + archive_des = open_archive (archive_name); + if (archive_des < 0) + error (1, errno, "%s", archive_name); + } + +#ifndef __MSDOS__ + /* Prevent SysV non-root users from giving away files inadvertantly. + This happens automatically on BSD, where only root can give + away files. */ + if (set_owner_flag == FALSE && set_group_flag == FALSE && geteuid ()) + no_chown_flag = TRUE; +#endif +} + +/* Initialize the input and output buffers to their proper size and + initialize all variables associated with the input and output + buffers. */ + +void +initialize_buffers () +{ + int buf_size; + + /* Make sure buffers can always hold 2 blocks and that they + are big enough to hold 1 tar record (512 bytes) even if it + is not aligned on a block boundary. The extra buffer space + is needed by process_copyin and peek_in_buf to automatically + figure out what kind of archive it is reading. */ + + if (io_block_size >= 512) + buf_size = 2 * io_block_size; + else + buf_size = 1024; + input_buffer = (char *) xmalloc (buf_size); + in_buff = input_buffer; + input_size = 0; + input_bytes = 0; + + /* Leave space for an `int' sentinel for `empty_output_buffer', + in case we ever put back sparseness checking. */ + output_buffer = (char *) xmalloc (buf_size + sizeof (int) * 2); + out_buff = output_buffer; + output_size = 0; + output_bytes = 0; + + /* Clear the block of zeros. */ + bzero (zeros_512, 512); +} + +void +main (argc, argv) + int argc; + char *argv[]; +{ + program_name = argv[0]; + umask (0); + +#ifdef __TURBOC__ + _fmode = O_BINARY; /* Put stdin and stdout in binary mode. */ +#endif +#ifdef __EMX__ /* gcc on OS/2. */ + _response (&argc, &argv); + _wildcard (&argc, &argv); +#endif + + process_args (argc, argv); + + initialize_buffers (); + + (*copy_function) (); + + if (archive_des >= 0 && rmtclose (archive_des) == -1) + error (1, errno, "error closing archive"); + + exit (0); +} diff --git a/gnu/usr.bin/cpio/makepath.c b/gnu/usr.bin/cpio/makepath.c new file mode 100644 index 0000000000..bdf6829750 --- /dev/null +++ b/gnu/usr.bin/cpio/makepath.c @@ -0,0 +1,297 @@ +/* makepath.c -- Ensure that a directory path exists. + Copyright (C) 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by David MacKenzie and + Jim Meyering . */ + +/* This copy of makepath is almost like the fileutils one, but has + changes for HPUX CDF's. Maybe the 2 versions of makepath can + come together again in the future. */ + +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +#ifdef HAVE_ALLOCA_H +#include +#else +#ifdef _AIX + #pragma alloca +#else +char *alloca (); +#endif +#endif +#endif + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#if !defined(S_ISDIR) && defined(S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +#ifdef STDC_HEADERS +#include +#include +#else +extern int errno; +#endif + +#if defined(STDC_HEADERS) || defined(HAVE_STRING_H) +#include +#define index strchr +#else +#include +#endif + +#ifdef __MSDOS__ +typedef int uid_t; +typedef int gid_t; +#endif + +void error (); + +/* Ensure that the directory ARGPATH exists. + Remove any trailing slashes from ARGPATH before calling this function. + + Make any leading directories that don't already exist, with + permissions PARENT_MODE. + If the last element of ARGPATH does not exist, create it as + a new directory with permissions MODE. + If OWNER and GROUP are non-negative, make them the UID and GID of + created directories. + If VERBOSE_FMT_STRING is nonzero, use it as a printf format + string for printing a message after successfully making a directory, + with the name of the directory that was just made as an argument. + + Return 0 if ARGPATH exists as a directory with the proper + ownership and permissions when done, otherwise 1. */ + +int +make_path (argpath, mode, parent_mode, owner, group, verbose_fmt_string) + char *argpath; + int mode; + int parent_mode; + uid_t owner; + gid_t group; + char *verbose_fmt_string; +{ + char *dirpath; /* A copy we can scribble NULs on. */ + struct stat stats; + int retval = 0; + int oldmask = umask (0); + dirpath = alloca (strlen (argpath) + 1); + strcpy (dirpath, argpath); + + if (stat (dirpath, &stats)) + { + char *slash; + int tmp_mode; /* Initial perms for leading dirs. */ + int re_protect; /* Should leading dirs be unwritable? */ + struct ptr_list + { + char *dirname_end; + struct ptr_list *next; + }; + struct ptr_list *p, *leading_dirs = NULL; + + /* If leading directories shouldn't be writable or executable, + or should have set[ug]id or sticky bits set and we are setting + their owners, we need to fix their permissions after making them. */ + if (((parent_mode & 0300) != 0300) + || (owner != (uid_t) -1 && group != (gid_t) -1 + && (parent_mode & 07000) != 0)) + { + tmp_mode = 0700; + re_protect = 1; + } + else + { + tmp_mode = parent_mode; + re_protect = 0; + } + + slash = dirpath; + while (*slash == '/') + slash++; + while ((slash = index (slash, '/'))) + { +#ifdef HPUX_CDF + int iscdf; + iscdf = 0; +#endif + *slash = '\0'; + if (stat (dirpath, &stats)) + { +#ifdef HPUX_CDF + /* If this component of the pathname ends in `+' and is + followed by 2 `/'s, then this is a CDF. We remove the + `+' from the name and create the directory. Later + we will "hide" the directory. */ + if ( (*(slash +1) == '/') && (*(slash -1) == '+') ) + { + iscdf = 1; + *(slash -1) = '\0'; + } +#endif + if (mkdir (dirpath, tmp_mode)) + { + error (0, errno, "cannot make directory `%s'", dirpath); + umask (oldmask); + return 1; + } + else + { + if (verbose_fmt_string != NULL) + error (0, 0, verbose_fmt_string, dirpath); + + if (owner != (uid_t) -1 && group != (gid_t) -1 + && chown (dirpath, owner, group) +#ifdef AFS + && errno != EPERM +#endif + ) + { + error (0, errno, "%s", dirpath); + retval = 1; + } + if (re_protect) + { + struct ptr_list *new = (struct ptr_list *) + alloca (sizeof (struct ptr_list)); + new->dirname_end = slash; + new->next = leading_dirs; + leading_dirs = new; + } +#ifdef HPUX_CDF + if (iscdf) + { + /* If this is a CDF, "hide" the directory by setting + its hidden/setuid bit. Also add the `+' back to + its name (since once it's "hidden" we must refer + to as `name+' instead of `name'). */ + chmod (dirpath, 04700); + *(slash - 1) = '+'; + } +#endif + } + } + else if (!S_ISDIR (stats.st_mode)) + { + error (0, 0, "`%s' exists but is not a directory", dirpath); + umask (oldmask); + return 1; + } + + *slash++ = '/'; + + /* Avoid unnecessary calls to `stat' when given + pathnames containing multiple adjacent slashes. */ + while (*slash == '/') + slash++; + } + + /* We're done making leading directories. + Make the final component of the path. */ + + if (mkdir (dirpath, mode)) + { + error (0, errno, "cannot make directory `%s'", dirpath); + umask (oldmask); + return 1; + } + if (verbose_fmt_string != NULL) + error (0, 0, verbose_fmt_string, dirpath); + + if (owner != (uid_t) -1 && group != (gid_t) -1) + { + if (chown (dirpath, owner, group) +#ifdef AFS + && errno != EPERM +#endif + ) + { + error (0, errno, "%s", dirpath); + retval = 1; + } + } + /* chown may have turned off some permission bits we wanted. */ + if ((mode & 07000) != 0 && chmod (dirpath, mode)) + { + error (0, errno, "%s", dirpath); + retval = 1; + } + + /* If the mode for leading directories didn't include owner "wx" + privileges, we have to reset their protections to the correct + value. */ + for (p = leading_dirs; p != NULL; p = p->next) + { + *(p->dirname_end) = '\0'; +#if 0 + /* cpio always calls make_path with parent mode 0700, so + we don't have to do this. If we ever do have to do this, + we have to stat the directory first to get the setuid + bit so we don't break HP CDF's. */ + if (chmod (dirpath, parent_mode)) + { + error (0, errno, "%s", dirpath); + retval = 1; + } +#endif + + } + } + else + { + /* We get here if the entire path already exists. */ + + if (!S_ISDIR (stats.st_mode)) + { + error (0, 0, "`%s' exists but is not a directory", dirpath); + umask (oldmask); + return 1; + } + + /* chown must precede chmod because on some systems, + chown clears the set[ug]id bits for non-superusers, + resulting in incorrect permissions. + On System V, users can give away files with chown and then not + be able to chmod them. So don't give files away. */ + + if (owner != (uid_t) -1 && group != (gid_t) -1 + && chown (dirpath, owner, group) +#ifdef AFS + && errno != EPERM +#endif + ) + { + error (0, errno, "%s", dirpath); + retval = 1; + } + if (chmod (dirpath, mode)) + { + error (0, errno, "%s", dirpath); + retval = 1; + } + } + + umask (oldmask); + return retval; +} diff --git a/gnu/usr.bin/cpio/rmt.h b/gnu/usr.bin/cpio/rmt.h new file mode 100644 index 0000000000..2155223954 --- /dev/null +++ b/gnu/usr.bin/cpio/rmt.h @@ -0,0 +1,98 @@ +/* Definitions for communicating with a remote tape drive. + Copyright (C) 1988, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_UNISTD_H +#include +#endif + +#if !defined(_POSIX_VERSION) +#ifdef __MSDOS__ +#include +#else /* !__MSDOS__ */ +extern off_t lseek (); +#endif /* __MSDOS__ */ +#endif /* _POSIX_VERSION */ + +#ifdef NO_REMOTE +#define _isrmt(f) 0 +#define rmtopen open +#define rmtaccess access +#define rmtstat stat +#define rmtcreat creat +#define rmtlstat lstat +#define rmtread read +#define rmtwrite write +#define rmtlseek lseek +#define rmtclose close +#define rmtioctl ioctl +#define rmtdup dup +#define rmtfstat fstat +#define rmtfcntl fcntl +#define rmtisatty isatty + +#else /* !NO_REMOTE */ + +#define __REM_BIAS 128 +#define RMTIOCTL + +#ifndef O_CREAT +#define O_CREAT 01000 +#endif + +extern char *__rmt_path; + +#if defined(STDC_HEADERS) || defined(HAVE_STRING_H) +#include +#ifndef index +#define index strchr +#endif +#else +extern char *index (); +#endif + +#define _remdev(path) (!f_force_local && (__rmt_path=index(path, ':'))) +#define _isrmt(fd) ((fd) >= __REM_BIAS) + +#define rmtopen(path,oflag,mode) (_remdev(path) ? __rmt_open(path, oflag, mode, __REM_BIAS) : open(path, oflag, mode)) +#define rmtaccess(path, amode) (_remdev(path) ? 0 : access(path, amode)) +#define rmtstat(path, buf) (_remdev(path) ? (errno = EOPNOTSUPP), -1 : stat(path, buf)) +#define rmtcreat(path, mode) (_remdev(path) ? __rmt_open (path, 1 | O_CREAT, mode, __REM_BIAS) : creat(path, mode)) +#define rmtlstat(path,buf) (_remdev(path) ? (errno = EOPNOTSUPP), -1 : lstat(path,buf)) + +#define rmtread(fd, buf, n) (_isrmt(fd) ? __rmt_read(fd - __REM_BIAS, buf, n) : read(fd, buf, n)) +#define rmtwrite(fd, buf, n) (_isrmt(fd) ? __rmt_write(fd - __REM_BIAS, buf, n) : write(fd, buf, n)) +#define rmtlseek(fd, off, wh) (_isrmt(fd) ? __rmt_lseek(fd - __REM_BIAS, off, wh) : lseek(fd, off, wh)) +#define rmtclose(fd) (_isrmt(fd) ? __rmt_close(fd - __REM_BIAS) : close(fd)) +#ifdef RMTIOCTL +#define rmtioctl(fd,req,arg) (_isrmt(fd) ? __rmt_ioctl(fd - __REM_BIAS, req, arg) : ioctl(fd, req, arg)) +#else +#define rmtioctl(fd,req,arg) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : ioctl(fd, req, arg)) +#endif +#define rmtdup(fd) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : dup(fd)) +#define rmtfstat(fd, buf) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : fstat(fd, buf)) +#define rmtfcntl(fd,cmd,arg) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : fcntl (fd, cmd, arg)) +#define rmtisatty(fd) (_isrmt(fd) ? 0 : isatty(fd)) + +#undef RMTIOCTL + +int __rmt_open (); +int __rmt_close (); +int __rmt_read (); +int __rmt_write (); +long __rmt_lseek (); +int __rmt_ioctl (); +#endif /* !NO_REMOTE */ diff --git a/gnu/usr.bin/cpio/rtapelib.c b/gnu/usr.bin/cpio/rtapelib.c new file mode 100644 index 0000000000..eece76ffcd --- /dev/null +++ b/gnu/usr.bin/cpio/rtapelib.c @@ -0,0 +1,582 @@ +/* Functions for communicating with a remote tape drive. + Copyright (C) 1988, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* The man page rmt(8) for /etc/rmt documents the remote mag tape + protocol which rdump and rrestore use. Unfortunately, the man + page is *WRONG*. The author of the routines I'm including originally + wrote his code just based on the man page, and it didn't work, so he + went to the rdump source to figure out why. The only thing he had to + change was to check for the 'F' return code in addition to the 'E', + and to separate the various arguments with \n instead of a space. I + personally don't think that this is much of a problem, but I wanted to + point it out. -- Arnold Robbins + + Originally written by Jeff Lee, modified some by Arnold Robbins. + Redone as a library that can replace open, read, write, etc., by + Fred Fish, with some additional work by Arnold Robbins. + Modified to make all rmtXXX calls into macros for speed by Jay Fenlason. + Use -DHAVE_NETDB_H for rexec code, courtesy of Dan Kegel, srs!dan. */ + +#include +#include +#include + +#ifdef HAVE_SYS_MTIO_H +#include +#include +#endif + +#ifdef HAVE_NETDB_H +#include +#endif + +#include +#include +#include + +#ifndef errno +extern int errno; +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef STDC_HEADERS +#include +#include +#endif + +/* Maximum size of a fully qualified host name. */ +#define MAXHOSTLEN 257 + +/* Size of buffers for reading and writing commands to rmt. + (An arbitrary limit.) */ +#define CMDBUFSIZE 64 + +#ifndef RETSIGTYPE +#define RETSIGTYPE void +#endif + +/* Maximum number of simultaneous remote tape connections. + (Another arbitrary limit.) */ +#define MAXUNIT 4 + +/* Return the parent's read side of remote tape connection FILDES. */ +#define READ(fildes) (from_rmt[fildes][0]) + +/* Return the parent's write side of remote tape connection FILDES. */ +#define WRITE(fildes) (to_rmt[fildes][1]) + +/* The pipes for receiving data from remote tape drives. */ +static int from_rmt[MAXUNIT][2] = +{-1, -1, -1, -1, -1, -1, -1, -1}; + +/* The pipes for sending data to remote tape drives. */ +static int to_rmt[MAXUNIT][2] = +{-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Temporary variable used by macros in rmt.h. */ +char *__rmt_path; + +/* Close remote tape connection FILDES. */ + +static void +_rmt_shutdown (fildes) + int fildes; +{ + close (READ (fildes)); + close (WRITE (fildes)); + READ (fildes) = -1; + WRITE (fildes) = -1; +} + +/* Attempt to perform the remote tape command specified in BUF + on remote tape connection FILDES. + Return 0 if successful, -1 on error. */ + +static int +command (fildes, buf) + int fildes; + char *buf; +{ + register int buflen; + RETSIGTYPE (*pipe_handler) (); + + /* Save the current pipe handler and try to make the request. */ + + pipe_handler = signal (SIGPIPE, SIG_IGN); + buflen = strlen (buf); + if (write (WRITE (fildes), buf, buflen) == buflen) + { + signal (SIGPIPE, pipe_handler); + return 0; + } + + /* Something went wrong. Close down and go home. */ + + signal (SIGPIPE, pipe_handler); + _rmt_shutdown (fildes); + errno = EIO; + return -1; +} + +/* Read and return the status from remote tape connection FILDES. + If an error occurred, return -1 and set errno. */ + +static int +status (fildes) + int fildes; +{ + int i; + char c, *cp; + char buffer[CMDBUFSIZE]; + + /* Read the reply command line. */ + + for (i = 0, cp = buffer; i < CMDBUFSIZE; i++, cp++) + { + if (read (READ (fildes), cp, 1) != 1) + { + _rmt_shutdown (fildes); + errno = EIO; + return -1; + } + if (*cp == '\n') + { + *cp = '\0'; + break; + } + } + + if (i == CMDBUFSIZE) + { + _rmt_shutdown (fildes); + errno = EIO; + return -1; + } + + /* Check the return status. */ + + for (cp = buffer; *cp; cp++) + if (*cp != ' ') + break; + + if (*cp == 'E' || *cp == 'F') + { + errno = atoi (cp + 1); + /* Skip the error message line. */ + while (read (READ (fildes), &c, 1) == 1) + if (c == '\n') + break; + + if (*cp == 'F') + _rmt_shutdown (fildes); + + return -1; + } + + /* Check for mis-synced pipes. */ + + if (*cp != 'A') + { + _rmt_shutdown (fildes); + errno = EIO; + return -1; + } + + /* Got an `A' (success) response. */ + return atoi (cp + 1); +} + +#ifdef HAVE_NETDB_H +/* Execute /etc/rmt as user USER on remote system HOST using rexec. + Return a file descriptor of a bidirectional socket for stdin and stdout. + If USER is NULL, or an empty string, use the current username. + + By default, this code is not used, since it requires that + the user have a .netrc file in his/her home directory, or that the + application designer be willing to have rexec prompt for login and + password info. This may be unacceptable, and .rhosts files for use + with rsh are much more common on BSD systems. */ + +static int +_rmt_rexec (host, user) + char *host; + char *user; +{ + struct servent *rexecserv; + int save_stdin = dup (fileno (stdin)); + int save_stdout = dup (fileno (stdout)); + int tape_fd; /* Return value. */ + + /* When using cpio -o < filename, stdin is no longer the tty. + But the rexec subroutine reads the login and the passwd on stdin, + to allow remote execution of the command. + So, reopen stdin and stdout on /dev/tty before the rexec and + give them back their original value after. */ + if (freopen ("/dev/tty", "r", stdin) == NULL) + freopen ("/dev/null", "r", stdin); + if (freopen ("/dev/tty", "w", stdout) == NULL) + freopen ("/dev/null", "w", stdout); + + rexecserv = getservbyname ("exec", "tcp"); + if (NULL == rexecserv) + { + fprintf (stderr, "exec/tcp: service not available"); + exit (1); + } + if (user != NULL && *user == '\0') + user = NULL; + tape_fd = rexec (&host, rexecserv->s_port, user, NULL, + "/etc/rmt", (int *) NULL); + fclose (stdin); + fdopen (save_stdin, "r"); + fclose (stdout); + fdopen (save_stdout, "w"); + + return tape_fd; +} + +#endif /* HAVE_NETDB_H */ + +/* Open a magtape device on the system specified in PATH, as the given user. + PATH has the form `[user@]system:/dev/????'. + If COMPAT is defined, it can also have the form `system[.user]:/dev/????'. + + OFLAG is O_RDONLY, O_WRONLY, etc. + MODE is ignored; 0666 is always used. + + If successful, return the remote tape pipe number plus BIAS. + On error, return -1. */ + +int +__rmt_open (path, oflag, mode, bias) + char *path; + int oflag; + int mode; + int bias; +{ + int i, rc; + char buffer[CMDBUFSIZE]; /* Command buffer. */ + char system[MAXHOSTLEN]; /* The remote host name. */ + char device[CMDBUFSIZE]; /* The remote device name. */ + char login[CMDBUFSIZE]; /* The remote user name. */ + char *sys, *dev, *user; /* For copying into the above buffers. */ + + sys = system; + dev = device; + user = login; + + /* Find an unused pair of file descriptors. */ + + for (i = 0; i < MAXUNIT; i++) + if (READ (i) == -1 && WRITE (i) == -1) + break; + + if (i == MAXUNIT) + { + errno = EMFILE; + return -1; + } + + /* Pull apart the system and device, and optional user. + Don't munge the original string. */ + + while (*path != '@' +#ifdef COMPAT + && *path != '.' +#endif + && *path != ':') + { + *sys++ = *path++; + } + *sys = '\0'; + path++; + + if (*(path - 1) == '@') + { + /* Saw user part of user@host. Start over. */ + strcpy (user, system); + sys = system; + while (*path != ':') + { + *sys++ = *path++; + } + *sys = '\0'; + path++; + } +#ifdef COMPAT + else if (*(path - 1) == '.') + { + while (*path != ':') + { + *user++ = *path++; + } + *user = '\0'; + path++; + } +#endif + else + *user = '\0'; + + while (*path) + { + *dev++ = *path++; + } + *dev = '\0'; + +#ifdef HAVE_NETDB_H + /* Execute the remote command using rexec. */ + READ (i) = WRITE (i) = _rmt_rexec (system, login); + if (READ (i) < 0) + return -1; +#else /* !HAVE_NETDB_H */ + /* Set up the pipes for the `rsh' command, and fork. */ + + if (pipe (to_rmt[i]) == -1 || pipe (from_rmt[i]) == -1) + return -1; + + rc = fork (); + if (rc == -1) + return -1; + + if (rc == 0) + { + /* Child. */ + close (0); + dup (to_rmt[i][0]); + close (to_rmt[i][0]); + close (to_rmt[i][1]); + + close (1); + dup (from_rmt[i][1]); + close (from_rmt[i][0]); + close (from_rmt[i][1]); + + setuid (getuid ()); + setgid (getgid ()); + + if (*login) + { + execl ("/usr/ucb/rsh", "rsh", system, "-l", login, + "/etc/rmt", (char *) 0); + execl ("/usr/bin/remsh", "remsh", system, "-l", login, + "/etc/rmt", (char *) 0); + execl ("/usr/bin/rsh", "rsh", system, "-l", login, + "/etc/rmt", (char *) 0); + execl ("/usr/bsd/rsh", "rsh", system, "-l", login, + "/etc/rmt", (char *) 0); + execl ("/usr/bin/nsh", "nsh", system, "-l", login, + "/etc/rmt", (char *) 0); + } + else + { + execl ("/usr/ucb/rsh", "rsh", system, + "/etc/rmt", (char *) 0); + execl ("/usr/bin/remsh", "remsh", system, + "/etc/rmt", (char *) 0); + execl ("/usr/bin/rsh", "rsh", system, + "/etc/rmt", (char *) 0); + execl ("/usr/bsd/rsh", "rsh", system, + "/etc/rmt", (char *) 0); + execl ("/usr/bin/nsh", "nsh", system, + "/etc/rmt", (char *) 0); + } + + /* Bad problems if we get here. */ + + perror ("cannot execute remote shell"); + _exit (1); + } + + /* Parent. */ + close (to_rmt[i][0]); + close (from_rmt[i][1]); +#endif /* !HAVE_NETDB_H */ + + /* Attempt to open the tape device. */ + + sprintf (buffer, "O%s\n%d\n", device, oflag); + if (command (i, buffer) == -1 || status (i) == -1) + return -1; + + return i + bias; +} + +/* Close remote tape connection FILDES and shut down. + Return 0 if successful, -1 on error. */ + +int +__rmt_close (fildes) + int fildes; +{ + int rc; + + if (command (fildes, "C\n") == -1) + return -1; + + rc = status (fildes); + _rmt_shutdown (fildes); + return rc; +} + +/* Read up to NBYTE bytes into BUF from remote tape connection FILDES. + Return the number of bytes read on success, -1 on error. */ + +int +__rmt_read (fildes, buf, nbyte) + int fildes; + char *buf; + unsigned int nbyte; +{ + int rc, i; + char buffer[CMDBUFSIZE]; + + sprintf (buffer, "R%d\n", nbyte); + if (command (fildes, buffer) == -1 || (rc = status (fildes)) == -1) + return -1; + + for (i = 0; i < rc; i += nbyte, buf += nbyte) + { + nbyte = read (READ (fildes), buf, rc - i); + if (nbyte <= 0) + { + _rmt_shutdown (fildes); + errno = EIO; + return -1; + } + } + + return rc; +} + +/* Write NBYTE bytes from BUF to remote tape connection FILDES. + Return the number of bytes written on success, -1 on error. */ + +int +__rmt_write (fildes, buf, nbyte) + int fildes; + char *buf; + unsigned int nbyte; +{ + char buffer[CMDBUFSIZE]; + RETSIGTYPE (*pipe_handler) (); + + sprintf (buffer, "W%d\n", nbyte); + if (command (fildes, buffer) == -1) + return -1; + + pipe_handler = signal (SIGPIPE, SIG_IGN); + if (write (WRITE (fildes), buf, nbyte) == nbyte) + { + signal (SIGPIPE, pipe_handler); + return status (fildes); + } + + /* Write error. */ + signal (SIGPIPE, pipe_handler); + _rmt_shutdown (fildes); + errno = EIO; + return -1; +} + +/* Perform an imitation lseek operation on remote tape connection FILDES. + Return the new file offset if successful, -1 if on error. */ + +long +__rmt_lseek (fildes, offset, whence) + int fildes; + long offset; + int whence; +{ + char buffer[CMDBUFSIZE]; + + sprintf (buffer, "L%ld\n%d\n", offset, whence); + if (command (fildes, buffer) == -1) + return -1; + + return status (fildes); +} + +/* Perform a raw tape operation on remote tape connection FILDES. + Return the results of the ioctl, or -1 on error. */ + +#ifdef MTIOCTOP +int +__rmt_ioctl (fildes, op, arg) + int fildes, op; + char *arg; +{ + char c; + int rc, cnt; + char buffer[CMDBUFSIZE]; + + switch (op) + { + default: + errno = EINVAL; + return -1; + + case MTIOCTOP: + /* MTIOCTOP is the easy one. Nothing is transfered in binary. */ + sprintf (buffer, "I%d\n%d\n", ((struct mtop *) arg)->mt_op, + ((struct mtop *) arg)->mt_count); + if (command (fildes, buffer) == -1) + return -1; + return status (fildes); /* Return the count. */ + + case MTIOCGET: + /* Grab the status and read it directly into the structure. + This assumes that the status buffer is not padded + and that 2 shorts fit in a long without any word + alignment problems; i.e., the whole struct is contiguous. + NOTE - this is probably NOT a good assumption. */ + + if (command (fildes, "S") == -1 || (rc = status (fildes)) == -1) + return -1; + + for (; rc > 0; rc -= cnt, arg += cnt) + { + cnt = read (READ (fildes), arg, rc); + if (cnt <= 0) + { + _rmt_shutdown (fildes); + errno = EIO; + return -1; + } + } + + /* Check for byte position. mt_type is a small integer field + (normally) so we will check its magnitude. If it is larger than + 256, we will assume that the bytes are swapped and go through + and reverse all the bytes. */ + + if (((struct mtget *) arg)->mt_type < 256) + return 0; + + for (cnt = 0; cnt < rc; cnt += 2) + { + c = arg[cnt]; + arg[cnt] = arg[cnt + 1]; + arg[cnt + 1] = c; + } + + return 0; + } +} + +#endif diff --git a/gnu/usr.bin/cpio/stripslash.c b/gnu/usr.bin/cpio/stripslash.c new file mode 100644 index 0000000000..2971d4ced0 --- /dev/null +++ b/gnu/usr.bin/cpio/stripslash.c @@ -0,0 +1,39 @@ +/* stripslash.c -- remove trailing slashes from a string + Copyright (C) 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#if defined(STDC_HEADERS) || defined(HAVE_STRING_H) +#include +#else +#include +#endif + +/* Remove trailing slashes from PATH. + This is useful when using filename completion from a shell that + adds a "/" after directory names (such as tcsh and bash), because + the Unix rename and rmdir system calls return an "Invalid argument" error + when given a path that ends in "/" (except for the root directory). */ + +void +strip_trailing_slashes (path) + char *path; +{ + int last; + + last = strlen (path) - 1; + while (last > 0 && path[last] == '/') + path[last--] = '\0'; +} diff --git a/gnu/usr.bin/cpio/system.h b/gnu/usr.bin/cpio/system.h new file mode 100644 index 0000000000..abe732d237 --- /dev/null +++ b/gnu/usr.bin/cpio/system.h @@ -0,0 +1,139 @@ +/* System dependent declarations. Requires sys/types.h. + Copyright (C) 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#if defined(STDC_HEADERS) || defined(HAVE_STRING_H) +#include +#ifndef index +#define index strchr +#endif +#ifndef rindex +#define rindex strrchr +#endif +#ifndef bcmp +#define bcmp(s1, s2, n) memcmp ((s1), (s2), (n)) +#endif +#ifndef bzero +#define bzero(s, n) memset ((s), 0, (n)) +#endif +#else +#include +#endif + +#include + +#ifdef STDC_HEADERS +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 +#endif + +#ifndef _POSIX_VERSION +off_t lseek (); +#endif + +/* Since major is a function on SVR4, we can't use `ifndef major'. */ +#ifdef MAJOR_IN_MKDEV +#include +#define HAVE_MAJOR +#endif + +#ifdef MAJOR_IN_SYSMACROS +#include +#define HAVE_MAJOR +#endif + +#ifdef major /* Might be defined in sys/types.h. */ +#define HAVE_MAJOR +#endif + +#ifndef HAVE_MAJOR +#define major(dev) (((dev) >> 8) & 0xff) +#define minor(dev) ((dev) & 0xff) +#define makedev(ma, mi) (((ma) << 8) | (mi)) +#endif +#undef HAVE_MAJOR + +#if defined(__MSDOS__) || defined(_POSIX_VERSION) || defined(HAVE_FCNTL_H) +#include +#else +#include +#endif +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#include +#ifndef errno +extern int errno; +#endif +#ifdef __EMX__ /* gcc on OS/2. */ +#define EPERM EACCES +#define ENXIO EIO +#endif + +#ifdef HAVE_UTIME_H +#include +#else +struct utimbuf +{ + time_t actime; + time_t modtime; +}; +#endif + +#ifdef TRUE +#undef TRUE +#endif +#define TRUE 1 +#ifdef FALSE +#undef FALSE +#endif +#define FALSE 0 + +#ifndef __MSDOS__ +#define CONSOLE "/dev/tty" +#else +#define CONSOLE "con" +#endif + +#ifdef __MSDOS__ +typedef int uid_t; +typedef int gid_t; +#endif + +/* On most systems symlink() always creates links with rwxrwxrwx + protection modes, but on some (HP/UX 8.07; I think maybe DEC's OSF + on MIPS too) symlink() uses the value of umask, so links' protection modes + aren't always rwxrwxrwx. There doesn't seem to be any way to change + the modes of a link (no system call like, say, lchmod() ), it seems + the only way to set the modes right is to set umask before calling + symlink(). */ + +#ifndef SYMLINK_USES_UMASK +#define UMASKED_SYMLINK(name1,name2,mode) symlink(name1,name2) +#else +#define UMASKED_SYMLINK(name1,name2,mode) umasked_symlink(name1,name2,mode) +#endif /* SYMLINK_USES_UMASK */ + diff --git a/gnu/usr.bin/cpio/tar.c b/gnu/usr.bin/cpio/tar.c new file mode 100644 index 0000000000..16eeee0764 --- /dev/null +++ b/gnu/usr.bin/cpio/tar.c @@ -0,0 +1,522 @@ +/* tar.c - read in write tar headers for cpio + Copyright (C) 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include "filetypes.h" +#include "system.h" +#include "cpiohdr.h" +#include "dstring.h" +#include "extern.h" +#include "rmt.h" +#include "tarhdr.h" + +static void to_oct (); +static char *stash_tar_linkname (); +static char *stash_tar_filename (); + +/* Compute and return a checksum for TAR_HDR, + counting the checksum bytes as if they were spaces. */ + +unsigned long +tar_checksum (tar_hdr) + struct tar_header *tar_hdr; +{ + unsigned long sum = 0; + char *p = (char *) tar_hdr; + char *q = p + TARRECORDSIZE; + int i; + + while (p < tar_hdr->chksum) + sum += *p++ & 0xff; + for (i = 0; i < 8; ++i) + { + sum += ' '; + ++p; + } + while (p < q) + sum += *p++ & 0xff; + return sum; +} + +/* Write out header FILE_HDR, including the file name, to file + descriptor OUT_DES. */ + +void +write_out_tar_header (file_hdr, out_des) + struct new_cpio_header *file_hdr; + int out_des; +{ + int name_len; + union tar_record tar_rec; + struct tar_header *tar_hdr = (struct tar_header *) &tar_rec; + + bzero ((char *) &tar_rec, TARRECORDSIZE); + + /* process_copy_out must ensure that file_hdr->c_name is short enough, + or we will lose here. */ + + name_len = strlen (file_hdr->c_name); + if (name_len <= TARNAMESIZE) + { + strncpy (tar_hdr->name, file_hdr->c_name, name_len); + } + else + { + /* Fit as much as we can into `name', the rest into `prefix'. */ + char *suffix = file_hdr->c_name + name_len - TARNAMESIZE; + + /* We have to put the boundary at a slash. */ + name_len = TARNAMESIZE; + while (*suffix != '/') + { + --name_len; + ++suffix; + } + strncpy (tar_hdr->name, suffix + 1, name_len); + strncpy (tar_hdr->prefix, file_hdr->c_name, suffix - file_hdr->c_name); + } + + /* SVR4 seems to want the whole mode, not just protection modes. + Nobody else seems to care, so we might as well put it all in. */ + to_oct (file_hdr->c_mode, 8, tar_hdr->mode); + to_oct (file_hdr->c_uid, 8, tar_hdr->uid); + to_oct (file_hdr->c_gid, 8, tar_hdr->gid); + to_oct (file_hdr->c_filesize, 12, tar_hdr->size); + to_oct (file_hdr->c_mtime, 12, tar_hdr->mtime); + + switch (file_hdr->c_mode & CP_IFMT) + { + case CP_IFREG: + if (file_hdr->c_tar_linkname) + { + /* process_copy_out makes sure that c_tar_linkname is shorter + than TARLINKNAMESIZE. */ + strncpy (tar_hdr->linkname, file_hdr->c_tar_linkname, + TARLINKNAMESIZE); + tar_hdr->typeflag = LNKTYPE; + to_oct (0, 12, tar_hdr->size); + } + else + tar_hdr->typeflag = REGTYPE; + break; + case CP_IFDIR: + tar_hdr->typeflag = DIRTYPE; + break; +#ifndef __MSDOS__ + case CP_IFCHR: + tar_hdr->typeflag = CHRTYPE; + break; + case CP_IFBLK: + tar_hdr->typeflag = BLKTYPE; + break; +#ifdef CP_IFIFO + case CP_IFIFO: + tar_hdr->typeflag = FIFOTYPE; + break; +#endif /* CP_IFIFO */ +#ifdef CP_IFLNK + case CP_IFLNK: + tar_hdr->typeflag = SYMTYPE; + /* process_copy_out makes sure that c_tar_linkname is shorter + than TARLINKNAMESIZE. */ + strncpy (tar_hdr->linkname, file_hdr->c_tar_linkname, + TARLINKNAMESIZE); + to_oct (0, 12, tar_hdr->size); + break; +#endif /* CP_IFLNK */ +#endif /* !__MSDOS__ */ + } + + if (archive_format == arf_ustar) + { + char *name; + + strncpy (tar_hdr->magic, TMAGIC, TMAGLEN); + strncpy (tar_hdr->magic + TMAGLEN, TVERSION, TVERSLEN); + +#ifndef __MSDOS__ + name = getuser (file_hdr->c_uid); + if (name) + strcpy (tar_hdr->uname, name); + name = getgroup (file_hdr->c_gid); + if (name) + strcpy (tar_hdr->gname, name); +#endif + + to_oct (file_hdr->c_rdev_maj, 8, tar_hdr->devmajor); + to_oct (file_hdr->c_rdev_min, 8, tar_hdr->devminor); + } + + to_oct (tar_checksum (tar_hdr), 8, tar_hdr->chksum); + + copy_buf_out ((char *) &tar_rec, out_des, TARRECORDSIZE); +} + +/* Return nonzero iff all the bytes in BLOCK are NUL. + SIZE is the number of bytes to check in BLOCK; it must be a + multiple of sizeof (long). */ + +int +null_block (block, size) + long *block; + int size; +{ + register long *p = block; + register int i = size / sizeof (long); + + while (i--) + if (*p++) + return 0; + return 1; +} + +/* Read a tar header, including the file name, from file descriptor IN_DES + into FILE_HDR. */ + +void +read_in_tar_header (file_hdr, in_des) + struct new_cpio_header *file_hdr; + int in_des; +{ + long bytes_skipped = 0; + int warned = FALSE; + union tar_record tar_rec; + struct tar_header *tar_hdr = (struct tar_header *) &tar_rec; +#ifndef __MSDOS__ + uid_t *uidp; + gid_t *gidp; +#endif + + copy_in_buf ((char *) &tar_rec, in_des, TARRECORDSIZE); + + /* Check for a block of 0's. */ + if (null_block ((long *) &tar_rec, TARRECORDSIZE)) + { +#if 0 + /* Found one block of 512 0's. If the next block is also all 0's + then this is the end of the archive. If not, assume the + previous block was all corruption and continue reading + the archive. */ + /* Commented out because GNU tar sometimes creates archives with + only one block of 0's at the end. This happened for the + cpio 2.0 distribution! */ + copy_in_buf ((char *) &tar_rec, in_des, TARRECORDSIZE); + if (null_block ((long *) &tar_rec, TARRECORDSIZE)) +#endif + { + file_hdr->c_name = "TRAILER!!!"; + return; + } +#if 0 + bytes_skipped = TARRECORDSIZE; +#endif + } + + while (1) + { + otoa (tar_hdr->chksum, &file_hdr->c_chksum); + + if (file_hdr->c_chksum != tar_checksum (tar_hdr)) + { + /* If the checksum is bad, skip 1 byte and try again. When + we try again we do not look for an EOF record (all zeros), + because when we start skipping bytes in a corrupted archive + the chances are pretty good that we might stumble across + 2 blocks of 512 zeros (that probably is not really the last + record) and it is better to miss the EOF and give the user + a "premature EOF" error than to give up too soon on a corrupted + archive. */ + if (!warned) + { + error (0, 0, "invalid header: checksum error"); + warned = TRUE; + } + bcopy (((char *) &tar_rec) + 1, (char *) &tar_rec, + TARRECORDSIZE - 1); + copy_in_buf (((char *) &tar_rec) + (TARRECORDSIZE - 1), in_des, 1); + ++bytes_skipped; + continue; + } + + if (archive_format != arf_ustar) + file_hdr->c_name = stash_tar_filename (NULL, tar_hdr->name); + else + file_hdr->c_name = stash_tar_filename (tar_hdr->prefix, tar_hdr->name); + file_hdr->c_nlink = 1; + otoa (tar_hdr->mode, &file_hdr->c_mode); + file_hdr->c_mode = file_hdr->c_mode & 07777; +#ifndef __MSDOS__ + if (archive_format == arf_ustar + && (uidp = getuidbyname (tar_hdr->uname))) + file_hdr->c_uid = *uidp; + else +#endif + otoa (tar_hdr->uid, &file_hdr->c_uid); +#ifndef __MSDOS__ + if (archive_format == arf_ustar + && (gidp = getgidbyname (tar_hdr->gname))) + file_hdr->c_gid = *gidp; + else +#endif + otoa (tar_hdr->gid, &file_hdr->c_gid); + otoa (tar_hdr->size, &file_hdr->c_filesize); + otoa (tar_hdr->mtime, &file_hdr->c_mtime); + otoa (tar_hdr->devmajor, (unsigned long *) &file_hdr->c_rdev_maj); + otoa (tar_hdr->devminor, (unsigned long *) &file_hdr->c_rdev_min); + file_hdr->c_tar_linkname = NULL; + + switch (tar_hdr->typeflag) + { + case REGTYPE: + case CONTTYPE: /* For now, punt. */ + default: + file_hdr->c_mode |= CP_IFREG; + break; + case DIRTYPE: + file_hdr->c_mode |= CP_IFDIR; + break; +#ifndef __MSDOS__ + case CHRTYPE: + file_hdr->c_mode |= CP_IFCHR; + /* If a POSIX tar header has a valid linkname it's always supposed + to set typeflag to be LNKTYPE. System V.4 tar seems to + be broken, and for device files with multiple links it + puts the name of the link into linkname, but leaves typeflag + as CHRTYPE, BLKTYPE, FIFOTYPE, etc. */ + file_hdr->c_tar_linkname = stash_tar_linkname (tar_hdr->linkname); + + /* Does POSIX say that the filesize must be 0 for devices? We + assume so, but HPUX's POSIX tar sets it to be 1 which causes + us problems (when reading an archive we assume we can always + skip to the next file by skipping filesize bytes). For + now at least, it's easier to clear filesize for devices, + rather than check everywhere we skip in copyin.c. */ + file_hdr->c_filesize = 0; + break; + case BLKTYPE: + file_hdr->c_mode |= CP_IFBLK; + file_hdr->c_tar_linkname = stash_tar_linkname (tar_hdr->linkname); + file_hdr->c_filesize = 0; + break; +#ifdef CP_IFIFO + case FIFOTYPE: + file_hdr->c_mode |= CP_IFIFO; + file_hdr->c_tar_linkname = stash_tar_linkname (tar_hdr->linkname); + file_hdr->c_filesize = 0; + break; +#endif + case SYMTYPE: +#ifdef CP_IFLNK + file_hdr->c_mode |= CP_IFLNK; + file_hdr->c_tar_linkname = stash_tar_linkname (tar_hdr->linkname); + file_hdr->c_filesize = 0; + break; + /* Else fall through. */ +#endif + case LNKTYPE: + file_hdr->c_mode |= CP_IFREG; + file_hdr->c_tar_linkname = stash_tar_linkname (tar_hdr->linkname); + file_hdr->c_filesize = 0; + break; +#endif /* !__MSDOS__ */ + case AREGTYPE: + /* Old tar format; if the last char in filename is '/' then it is + a directory, otherwise it's a regular file. */ + if (file_hdr->c_name[strlen (file_hdr->c_name) - 1] == '/') + file_hdr->c_mode |= CP_IFDIR; + else + file_hdr->c_mode |= CP_IFREG; + break; + } + break; + } + if (bytes_skipped > 0) + error (0, 0, "warning: skipped %ld bytes of junk", bytes_skipped); +} + +/* Stash the tar linkname in static storage. */ + +static char * +stash_tar_linkname (linkname) + char *linkname; +{ + static char hold_tar_linkname[TARLINKNAMESIZE + 1]; + + strncpy (hold_tar_linkname, linkname, TARLINKNAMESIZE); + hold_tar_linkname[TARLINKNAMESIZE] = '\0'; + return hold_tar_linkname; +} + +/* Stash the tar filename and optional prefix in static storage. */ + +static char * +stash_tar_filename (prefix, filename) + char *prefix; + char *filename; +{ + static char hold_tar_filename[TARNAMESIZE + TARPREFIXSIZE + 2]; + if (prefix == NULL || *prefix == '\0') + { + strncpy (hold_tar_filename, filename, TARNAMESIZE); + hold_tar_filename[TARNAMESIZE] = '\0'; + } + else + { + strncpy (hold_tar_filename, prefix, TARPREFIXSIZE); + hold_tar_filename[TARPREFIXSIZE] = '\0'; + strcat (hold_tar_filename, "/"); + strncat (hold_tar_filename, filename, TARNAMESIZE); + hold_tar_filename[TARPREFIXSIZE + TARNAMESIZE] = '\0'; + } + return hold_tar_filename; +} + +/* Convert the string of octal digits S into a number and store + it in *N. Return nonzero if the whole string was converted, + zero if there was something after the number. + Skip leading and trailing spaces. */ + +int +otoa (s, n) + char *s; + unsigned long *n; +{ + unsigned long val = 0; + + while (*s == ' ') + ++s; + while (*s >= '0' && *s <= '7') + val = 8 * val + *s++ - '0'; + while (*s == ' ') + ++s; + *n = val; + return *s == '\0'; +} + +/* Convert a number into a string of octal digits. + Convert long VALUE into a DIGITS-digit field at WHERE, + including a trailing space and room for a NUL. DIGITS==3 means + 1 digit, a space, and room for a NUL. + + We assume the trailing NUL is already there and don't fill it in. + This fact is used by start_header and finish_header, so don't change it! + + This is be equivalent to: + sprintf (where, "%*lo ", digits - 2, value); + except that sprintf fills in the trailing NUL and we don't. */ + +static void +to_oct (value, digits, where) + register long value; + register int digits; + register char *where; +{ + --digits; /* Leave the trailing NUL slot alone. */ + where[--digits] = ' '; /* Put in the space, though. */ + + /* Produce the digits -- at least one. */ + do + { + where[--digits] = '0' + (char) (value & 7); /* One octal digit. */ + value >>= 3; + } + while (digits > 0 && value != 0); + + /* Add leading spaces, if necessary. */ + while (digits > 0) + where[--digits] = ' '; +} + +/* Return + 2 if BUF is a valid POSIX tar header (the checksum is correct + and it has the "ustar" magic string), + 1 if BUF is a valid old tar header (the checksum is correct), + 0 otherwise. */ + +int +is_tar_header (buf) + char *buf; +{ + struct tar_header *tar_hdr = (struct tar_header *) buf; + unsigned long chksum; + + otoa (tar_hdr->chksum, &chksum); + + if (chksum != tar_checksum (tar_hdr)) + return 0; + + /* GNU tar 1.10 and previous set the magic field to be "ustar " instead + of "ustar\0". Only look at the first 5 characters of the magic + field so we can recognize old GNU tar ustar archives. */ + if (!strncmp (tar_hdr->magic, TMAGIC, TMAGLEN - 1)) + return 2; + return 1; +} + +/* Return TRUE if the filename is too long to fit in a tar header. + For old tar headers, if the filename's length is less than or equal + to 100 then it will fit, otherwise it will not. For POSIX tar headers, + if the filename's length is less than or equal to 100 then it + will definitely fit, and if it is greater than 256 then it + will definitely not fit. If the length is between 100 and 256, + then the filename will fit only if it is possible to break it + into a 155 character "prefix" and 100 character "name". There + must be a slash between the "prefix" and the "name", although + the slash is not stored or counted in either the "prefix" or + the "name", and there must be at least one character in both + the "prefix" and the "name". If it is not possible to break down + the filename like this then it will not fit. */ + +int +is_tar_filename_too_long (name) + char *name; +{ + int whole_name_len; + int prefix_name_len; + char *p; + + whole_name_len = strlen (name); + if (whole_name_len <= TARNAMESIZE) + return FALSE; + + if (archive_format != arf_ustar) + return TRUE; + + if (whole_name_len > TARNAMESIZE + TARPREFIXSIZE + 1) + return TRUE; + + /* See whether we can split up the name into acceptably-sized + `prefix' and `name' (`p') pieces. Start out by making `name' + as big as possible, then shrink it by looking for the first '/'. */ + p = name + whole_name_len - TARNAMESIZE; + while (*p != '/' && *p != '\0') + ++p; + if (*p == '\0') + /* The last component of the path is longer than TARNAMESIZE. */ + return TRUE; + + prefix_name_len = p - name - 1; + /* Interestingly, a name consisting of a slash followed by + TARNAMESIZE characters can't be stored, because the prefix + would be empty, and thus ignored. */ + if (prefix_name_len > TARPREFIXSIZE || prefix_name_len == 0) + return TRUE; + + return FALSE; +} diff --git a/gnu/usr.bin/cpio/tar.h b/gnu/usr.bin/cpio/tar.h new file mode 100644 index 0000000000..411579c9d1 --- /dev/null +++ b/gnu/usr.bin/cpio/tar.h @@ -0,0 +1,112 @@ +/* Extended tar format from POSIX.1. + Copyright (C) 1992 Free Software Foundation, Inc. + Written by David J. MacKenzie. + +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#ifndef _TAR_H + +#define _TAR_H 1 + + +/* A tar archive consists of 512-byte blocks. + Each file in the archive has a header block followed by 0+ data blocks. + Two blocks of NUL bytes indicate the end of the archive. */ + +/* The fields of header blocks: + All strings are stored as ISO 646 (approximately ASCII) strings. + + Fields are numeric unless otherwise noted below; numbers are ISO 646 + representations of octal numbers, with leading zeros as needed. + + linkname is only valid when typeflag==LNKTYPE. It doesn't use prefix; + files that are links to pathnames >100 chars long can not be stored + in a tar archive. + + If typeflag=={LNKTYPE,SYMTYPE,DIRTYPE} then size must be 0. + + devmajor and devminor are only valid for typeflag=={BLKTYPE,CHRTYPE}. + + chksum contains the sum of all 512 bytes in the header block, + treating each byte as an 8-bit unsigned value and treating the + 8 bytes of chksum as blank characters. + + uname and gname are used in preference to uid and gid, if those + names exist locally. + + Field Name Byte Offset Length in Bytes Field Type + name 0 100 NUL-terminated if NUL fits + mode 100 8 + uid 108 8 + gid 116 8 + size 124 12 + mtime 136 12 + chksum 148 8 + typeflag 156 1 see below + linkname 157 100 NUL-terminated if NUL fits + magic 257 6 must be TMAGIC (NUL term.) + version 263 2 must be TVERSION + uname 265 32 NUL-terminated + gname 297 32 NUL-terminated + devmajor 329 8 + devminor 337 8 + prefix 345 155 NUL-terminated if NUL fits + + If the first character of prefix is '\0', the file name is name; + otherwise, it is prefix/name. Files whose pathnames don't fit in that + length can not be stored in a tar archive. */ + +/* The bits in mode: */ +#define TSUID 04000 +#define TSGID 02000 +#define TSVTX 01000 +#define TUREAD 00400 +#define TUWRITE 00200 +#define TUEXEC 00100 +#define TGREAD 00040 +#define TGWRITE 00020 +#define TGEXEC 00010 +#define TOREAD 00004 +#define TOWRITE 00002 +#define TOEXEC 00001 + +/* The values for typeflag: + Values 'A'-'Z' are reserved for custom implementations. + All other values are reserved for future POSIX.1 revisions. */ + +#define REGTYPE '0' /* Regular file (preferred code). */ +#define AREGTYPE '\0' /* Regular file (alternate code). */ +#define LNKTYPE '1' /* Hard link. */ +#define SYMTYPE '2' /* Symbolic link (hard if not supported). */ +#define CHRTYPE '3' /* Character special. */ +#define BLKTYPE '4' /* Block special. */ +#define DIRTYPE '5' /* Directory. */ +#define FIFOTYPE '6' /* Named pipe. */ +#define CONTTYPE '7' /* Contiguous file */ + /* (regular file if not supported). */ + +/* Contents of magic field and its length. */ +#define TMAGIC "ustar" +#define TMAGLEN 6 + +/* Contents of the version field and its length. */ +#define TVERSION "00" +#define TVERSLEN 2 + + +#endif /* tar.h */ diff --git a/gnu/usr.bin/cpio/tarhdr.h b/gnu/usr.bin/cpio/tarhdr.h new file mode 100644 index 0000000000..54de0d6a35 --- /dev/null +++ b/gnu/usr.bin/cpio/tarhdr.h @@ -0,0 +1,62 @@ +/* Extended tar header from POSIX.1. + Copyright (C) 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _TARHDR_H + +#define _TARHDR_H 1 + +#include + +/* Size of `name' field. */ +#define TARNAMESIZE 100 + +/* Size of `linkname' field. */ +#define TARLINKNAMESIZE 100 + +/* Size of `prefix' field. */ +#define TARPREFIXSIZE 155 + +/* Size of entire tar header. */ +#define TARRECORDSIZE 512 + +struct tar_header +{ + char name[TARNAMESIZE]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char chksum[8]; + char typeflag; + char linkname[TARLINKNAMESIZE]; + char magic[6]; + char version[2]; + char uname[32]; + char gname[32]; + char devmajor[8]; + char devminor[8]; + char prefix[TARPREFIXSIZE]; +}; + +union tar_record +{ + struct tar_header header; + char buffer[TARRECORDSIZE]; +}; + +#endif /* tarhdr.h */ diff --git a/gnu/usr.bin/cpio/tcexparg.c b/gnu/usr.bin/cpio/tcexparg.c new file mode 100644 index 0000000000..c5d88f069b --- /dev/null +++ b/gnu/usr.bin/cpio/tcexparg.c @@ -0,0 +1,240 @@ +/* tcexparg.c - Unix-style command line wildcards for Turbo C 2.0 + + This file is in the public domain. + + Compile your main program with -Dmain=_main and link with this file. + + After that, it is just as if the operating system had expanded the + arguments, except that they are not sorted. The program name and all + arguments that are expanded from wildcards are lowercased. + + Syntax for wildcards: + * Matches zero or more of any character (except a '.' at + the beginning of a name). + ? Matches any single character. + [r3z] Matches 'r', '3', or 'z'. + [a-d] Matches a single character in the range 'a' through 'd'. + [!a-d] Matches any single character except a character in the + range 'a' through 'd'. + + The period between the filename root and its extension need not be + given explicitly. Thus, the pattern `a*e' will match 'abacus.exe' + and 'axyz.e' as well as 'apple'. Comparisons are not case sensitive. + + Authors: + The expargs code is a modification of wildcard expansion code + written for Turbo C 1.0 by + Richard Hargrove + Texas Instruments, Inc. + P.O. Box 869305, m/s 8473 + Plano, Texas 75086 + 214/575-4128 + and posted to USENET in September, 1987. + + The wild_match code was written by Rich Salz, rsalz@bbn.com, + posted to net.sources in November, 1986. + + The code connecting the two is by Mike Slomin, bellcore!lcuxa!mike2, + posted to comp.sys.ibm.pc in November, 1988. + + Major performance enhancements and bug fixes, and source cleanup, + by David MacKenzie, djm@gnu.ai.mit.edu. */ + +#include +#include +#include +#include +#include + +/* Number of new arguments to allocate space for at a time. */ +#define ARGS_INCREMENT 10 + +/* The name this program was run with, for error messages. */ +static char *program_name; + +static char **grow_argv (char **new_argv, int new_argc); +static void fatal_error (const char *message); + +int wild_match (char *string, char *pattern); +char *basename (char *path); + +char **expargs (int *, char **); + +#ifdef main +#undef main +#endif + +int +main (int argc, char **argv, char **envp) +{ + argv = expargs (&argc, argv); + return _main (argc, argv, envp); +} + +char ** +expargs (int *pargc, char **argv) +{ + char path[MAXPATH + 1]; + char **new_argv; + struct ffblk block; + char *path_base; + char *arg_base; + int argind; + int new_argc; + int path_length; + int matched; + + program_name = argv[0]; + if (program_name && *program_name) + strlwr (program_name); + new_argv = grow_argv (NULL, 0); + new_argv[0] = argv[0]; + new_argc = 1; + + for (argind = 1; argind < *pargc; ++argind) + { + matched = 0; + if (strpbrk (argv[argind], "?*[") != NULL) + { + strncpy (path, argv[argind], MAXPATH - 3); + path_base = basename (path); + strcpy (path_base, "*.*"); + arg_base = argv[argind] + (path_base - path); + + if (!findfirst (path, &block, FA_DIREC)) + { + strlwr (path); + do + { + /* Only match "." and ".." explicitly. */ + if (*block.ff_name == '.' && *arg_base != '.') + continue; + path_length = stpcpy (path_base, block.ff_name) - path + 1; + strlwr (path_base); + if (wild_match (path, argv[argind])) + { + matched = 1; + new_argv[new_argc] = (char *) malloc (path_length); + if (new_argv[new_argc] == NULL) + fatal_error ("memory exhausted"); + strcpy (new_argv[new_argc++], path); + new_argv = grow_argv (new_argv, new_argc); + } + } while (!findnext (&block)); + } + } + if (matched == 0) + new_argv[new_argc++] = argv[argind]; + new_argv = grow_argv (new_argv, new_argc); + } + + *pargc = new_argc; + new_argv[new_argc] = NULL; + return &new_argv[0]; +} + +/* Return a pointer to the last element of PATH. */ + +char * +basename (char *path) +{ + char *tail; + + for (tail = path; *path; ++path) + if (*path == ':' || *path == '\\') + tail = path + 1; + return tail; +} + +static char ** +grow_argv (char **new_argv, int new_argc) +{ + if (new_argc % ARGS_INCREMENT == 0) + { + new_argv = (char **) realloc + (new_argv, sizeof (char *) * (new_argc + ARGS_INCREMENT)); + if (new_argv == NULL) + fatal_error ("memory exhausted"); + } + return new_argv; +} + +static void +fatal_error (const char *message) +{ + putc ('\n', stderr); + if (program_name && *program_name) + { + fputs (program_name, stderr); + fputs (": ", stderr); + } + fputs (message, stderr); + putc ('\n', stderr); + exit (1); +} + +/* Shell-style pattern matching for ?, \, [], and * characters. + I'm putting this replacement in the public domain. + + Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. */ + +/* The character that inverts a character class; '!' or '^'. */ +#define INVERT '!' + +static int star (char *string, char *pattern); + +/* Return nonzero if `string' matches Unix-style wildcard pattern + `pattern'; zero if not. */ + +int +wild_match (char *string, char *pattern) +{ + int prev; /* Previous character in character class. */ + int matched; /* If 1, character class has been matched. */ + int reverse; /* If 1, character class is inverted. */ + + for (; *pattern; string++, pattern++) + switch (*pattern) + { + case '\\': + /* Literal match with following character; fall through. */ + pattern++; + default: + if (*string != *pattern) + return 0; + continue; + case '?': + /* Match anything. */ + if (*string == '\0') + return 0; + continue; + case '*': + /* Trailing star matches everything. */ + return *++pattern ? star (string, pattern) : 1; + case '[': + /* Check for inverse character class. */ + reverse = pattern[1] == INVERT; + if (reverse) + pattern++; + for (prev = 256, matched = 0; *++pattern && *pattern != ']'; + prev = *pattern) + if (*pattern == '-' + ? *string <= *++pattern && *string >= prev + : *string == *pattern) + matched = 1; + if (matched == reverse) + return 0; + continue; + } + + return *string == '\0'; +} + +static int +star (char *string, char *pattern) +{ + while (wild_match (string, pattern) == 0) + if (*++string == '\0') + return 0; + return 1; +} diff --git a/gnu/usr.bin/cpio/userspec.c b/gnu/usr.bin/cpio/userspec.c new file mode 100644 index 0000000000..44d7d91744 --- /dev/null +++ b/gnu/usr.bin/cpio/userspec.c @@ -0,0 +1,180 @@ +/* userspec.c -- Parse a user and group string. + Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by David MacKenzie . */ + +#include +#include +#include +#include + +#if defined(STDC_HEADERS) || defined(HAVE_STRING_H) +#include +#ifndef index +#define index strchr +#endif +#else +#include +#endif + +#ifdef STDC_HEADERS +#include +#else +char *malloc (); +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifndef _POSIX_VERSION +struct passwd *getpwnam (); +struct group *getgrnam (); +struct group *getgrgid (); +#endif + +#ifdef _POSIX_SOURCE +#define endpwent() +#define endgrent() +#endif + +#define isdigit(c) ((c) >= '0' && (c) <= '9') + +char *strdup (); +static int isnumber (); + +/* Extract from NAME, which has the form "[user][:.][group]", + a USERNAME, UID U, GROUPNAME, and GID G. + Either user or group, or both, must be present. + If the group is omitted but the ":" or "." separator is given, + use the given user's login group. + + USERNAME and GROUPNAME will be in newly malloc'd memory. + Either one might be NULL instead, indicating that it was not + given and the corresponding numeric ID was left unchanged. + Might write NULs into NAME. + + Return NULL if successful, a static error message string if not. */ + +char * +parse_user_spec (name, uid, gid, username, groupname) + char *name; + uid_t *uid; + gid_t *gid; + char **username, **groupname; +{ + static char *tired = "virtual memory exhausted"; + struct passwd *pwd; + struct group *grp; + char *cp; + int use_login_group = 0; + + *username = *groupname = NULL; + + /* Check whether a group is given. */ + cp = index (name, ':'); + if (cp == NULL) + cp = index (name, '.'); + if (cp != NULL) + { + *cp++ = '\0'; + if (*cp == '\0') + { + if (cp == name + 1) + /* Neither user nor group given, just "." or ":". */ + return "can not omit both user and group"; + else + /* "user.". */ + use_login_group = 1; + } + else + { + /* Explicit group. */ + *groupname = strdup (cp); + if (*groupname == NULL) + return tired; + grp = getgrnam (cp); + if (grp == NULL) + { + if (!isnumber (cp)) + return "invalid group"; + *gid = atoi (cp); + } + else + *gid = grp->gr_gid; + endgrent (); /* Save a file descriptor. */ + } + } + + /* Parse the user name, now that any group has been removed. */ + + if (name[0] == '\0') + /* No user name was given, just a group. */ + return NULL; + + *username = strdup (name); + if (*username == NULL) + return tired; + + pwd = getpwnam (name); + if (pwd == NULL) + { + if (!isnumber (name)) + return "invalid user"; + if (use_login_group) + return "cannot get the login group of a numeric UID"; + *uid = atoi (name); + } + else + { + *uid = pwd->pw_uid; + if (use_login_group) + { + *gid = pwd->pw_gid; + grp = getgrgid (pwd->pw_gid); + if (grp == NULL) + { + *groupname = malloc (15); + if (*groupname == NULL) + return tired; + sprintf (*groupname, "%u", pwd->pw_gid); + } + else + { + *groupname = strdup (grp->gr_name); + if (*groupname == NULL) + return tired; + } + endgrent (); + } + } + endpwent (); + return NULL; +} + +/* Return nonzero if STR represents an unsigned decimal integer, + otherwise return 0. */ + +static int +isnumber (str) + char *str; +{ + for (; *str; str++) + if (!isdigit (*str)) + return 0; + return 1; +} diff --git a/gnu/usr.bin/cpio/util.c b/gnu/usr.bin/cpio/util.c new file mode 100644 index 0000000000..52e3e85f47 --- /dev/null +++ b/gnu/usr.bin/cpio/util.c @@ -0,0 +1,1102 @@ +/* util.c - Several utility routines for cpio. + Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#ifdef HPUX_CDF +#include +#endif +#include "system.h" +#include "cpiohdr.h" +#include "dstring.h" +#include "extern.h" +#include "rmt.h" + +#ifndef __MSDOS__ +#include +#else +#include +#endif + +#ifdef HAVE_SYS_MTIO_H +#ifdef HAVE_SYS_IO_TRIOCTL_H +#include +#endif +#include +#endif + +static void empty_output_buffer_swap (); +static void hash_insert (); + +/* Write `output_size' bytes of `output_buffer' to file + descriptor OUT_DES and reset `output_size' and `out_buff'. */ + +void +empty_output_buffer (out_des) + int out_des; +{ + int bytes_written; + +#ifdef BROKEN_LONG_TAPE_DRIVER + static long output_bytes_before_lseek = 0; +#endif + + if (swapping_halfwords || swapping_bytes) + { + empty_output_buffer_swap (out_des); + return; + } + +#ifdef BROKEN_LONG_TAPE_DRIVER + /* Some tape drivers seem to have a signed internal seek pointer and + they lose if it overflows and becomes negative (e.g. when writing + tapes > 2Gb). Doing an lseek (des, 0, SEEK_SET) seems to reset the + seek pointer and prevent it from overflowing. */ + if (output_is_special + && (output_bytes_before_lseek += output_size) < 0L) + { + lseek(out_des, 0L, SEEK_SET); + output_bytes_before_lseek = 0; + } +#endif + + bytes_written = rmtwrite (out_des, output_buffer, output_size); + if (bytes_written != output_size) + { + int rest_bytes_written; + int rest_output_size; + + if (output_is_special + && (bytes_written >= 0 + || (bytes_written < 0 + && (errno == ENOSPC || errno == EIO || errno == ENXIO)))) + { + get_next_reel (out_des); + if (bytes_written > 0) + rest_output_size = output_size - bytes_written; + else + rest_output_size = output_size; + rest_bytes_written = rmtwrite (out_des, output_buffer, + rest_output_size); + if (rest_bytes_written != rest_output_size) + error (1, errno, "write error"); + } + else + error (1, errno, "write error"); + } + output_bytes += output_size; + out_buff = output_buffer; + output_size = 0; +} + +/* Write `output_size' bytes of `output_buffer' to file + descriptor OUT_DES with byte and/or halfword swapping and reset + `output_size' and `out_buff'. This routine should not be called + with `swapping_bytes' set unless the caller knows that the + file being written has an even number of bytes, and it should not be + called with `swapping_halfwords' set unless the caller knows + that the file being written has a length divisible by 4. If either + of those restrictions are not met, bytes may be lost in the output + file. OUT_DES must refer to a file that we are creating during + a process_copy_in, so we don't have to check for end of media + errors or be careful about only writing in blocks of `output_size' + bytes. */ + +static void +empty_output_buffer_swap (out_des) + int out_des; +{ + /* Since `output_size' might not be divisible by 4 or 2, we might + not be able to be able to swap all the bytes and halfwords in + `output_buffer' (e.g., if `output_size' is odd), so we might not be + able to write them all. We will swap and write as many bytes as + we can, and save the rest in `left_overs' for the next time we are + called. */ + static char left_overs[4]; + static int left_over_bytes = 0; + + int bytes_written; + int complete_halfwords; + int complete_words; + int extra_bytes; + + output_bytes += output_size; + + out_buff = output_buffer; + + if (swapping_halfwords) + { + if (left_over_bytes != 0) + { + while (output_size > 0 && left_over_bytes < 4) + { + left_overs[left_over_bytes++] = *out_buff++; + --output_size; + } + if (left_over_bytes < 4) + { + out_buff = output_buffer; + output_size = 0; + return; + } + swahw_array (left_overs, 1); + if (swapping_bytes) + swab_array (left_overs, 2); + bytes_written = rmtwrite (out_des, left_overs, 4); + if (bytes_written != 4) + error (1, errno, "write error"); + left_over_bytes = 0; + } + complete_words = output_size / 4; + if (complete_words > 0) + { + swahw_array (out_buff, complete_words); + if (swapping_bytes) + swab_array (out_buff, 2 * complete_words); + bytes_written = rmtwrite (out_des, out_buff, 4 * complete_words); + if (bytes_written != (4 * complete_words)) + error (1, errno, "write error"); + } + out_buff += (4 * complete_words); + extra_bytes = output_size % 4; + while (extra_bytes > 0) + { + left_overs[left_over_bytes++] = *out_buff++; + --extra_bytes; + } + + } + else + { + if (left_over_bytes != 0) + { + while (output_size > 0 && left_over_bytes < 2) + { + left_overs[left_over_bytes++] = *out_buff++; + --output_size; + } + if (left_over_bytes < 2) + { + out_buff = output_buffer; + output_size = 0; + return; + } + swab_array (left_overs, 1); + bytes_written = rmtwrite (out_des, left_overs, 2); + if (bytes_written != 2) + error (1, errno, "write error"); + left_over_bytes = 0; + } + complete_halfwords = output_size / 2; + if (complete_halfwords > 0) + { + swab_array (out_buff, complete_halfwords); + bytes_written = rmtwrite (out_des, out_buff, 2 * complete_halfwords); + if (bytes_written != (2 * complete_halfwords)) + error (1, errno, "write error"); + } + out_buff += (2 * complete_halfwords); + extra_bytes = output_size % 2; + while (extra_bytes > 0) + { + left_overs[left_over_bytes++] = *out_buff++; + --extra_bytes; + } + } + + out_buff = output_buffer; + output_size = 0; +} + +/* Exchange the halfwords of each element of the array of COUNT longs + starting at PTR. PTR does not have to be aligned at a word + boundary. */ + +void +swahw_array (ptr, count) + char *ptr; + int count; +{ + char tmp; + + for (; count > 0; --count) + { + tmp = *ptr; + *ptr = *(ptr + 2); + *(ptr + 2) = tmp; + ++ptr; + tmp = *ptr; + *ptr = *(ptr + 2); + *(ptr + 2) = tmp; + ptr += 3; + } +} + +/* Read at most NUM_BYTES or `io_block_size' bytes, whichever is smaller, + into the start of `input_buffer' from file descriptor IN_DES. + Set `input_size' to the number of bytes read and reset `in_buff'. + Exit with an error if end of file is reached. */ + +#ifdef BROKEN_LONG_TAPE_DRIVER +static long input_bytes_before_lseek = 0; +#endif + +void +fill_input_buffer (in_des, num_bytes) + int in_des; + int num_bytes; +{ +#ifdef BROKEN_LONG_TAPE_DRIVER + /* Some tape drivers seem to have a signed internal seek pointer and + they lose if it overflows and becomes negative (e.g. when writing + tapes > 4Gb). Doing an lseek (des, 0, SEEK_SET) seems to reset the + seek pointer and prevent it from overflowing. */ + if (input_is_special + && (input_bytes_before_lseek += num_bytes) < 0L) + { + lseek(in_des, 0L, SEEK_SET); + input_bytes_before_lseek = 0; + } +#endif + in_buff = input_buffer; + num_bytes = (num_bytes < io_block_size) ? num_bytes : io_block_size; + input_size = rmtread (in_des, input_buffer, num_bytes); + if (input_size == 0 && input_is_special) + { + get_next_reel (in_des); + input_size = rmtread (in_des, input_buffer, num_bytes); + } + if (input_size < 0) + error (1, errno, "read error"); + if (input_size == 0) + { + error (0, 0, "premature end of file"); + exit (1); + } + input_bytes += input_size; +} + +/* Copy NUM_BYTES of buffer IN_BUF to `out_buff', which may be partly full. + When `out_buff' fills up, flush it to file descriptor OUT_DES. */ + +void +copy_buf_out (in_buf, out_des, num_bytes) + char *in_buf; + int out_des; + long num_bytes; +{ + register long bytes_left = num_bytes; /* Bytes needing to be copied. */ + register long space_left; /* Room left in output buffer. */ + + while (bytes_left > 0) + { + space_left = io_block_size - output_size; + if (space_left == 0) + empty_output_buffer (out_des); + else + { + if (bytes_left < space_left) + space_left = bytes_left; + bcopy (in_buf, out_buff, (unsigned) space_left); + out_buff += space_left; + output_size += space_left; + in_buf += space_left; + bytes_left -= space_left; + } + } +} + +/* Copy NUM_BYTES of buffer `in_buff' into IN_BUF. + `in_buff' may be partly full. + When `in_buff' is exhausted, refill it from file descriptor IN_DES. */ + +void +copy_in_buf (in_buf, in_des, num_bytes) + char *in_buf; + int in_des; + long num_bytes; +{ + register long bytes_left = num_bytes; /* Bytes needing to be copied. */ + register long space_left; /* Bytes to copy from input buffer. */ + + while (bytes_left > 0) + { + if (input_size == 0) + fill_input_buffer (in_des, io_block_size); + if (bytes_left < input_size) + space_left = bytes_left; + else + space_left = input_size; + bcopy (in_buff, in_buf, (unsigned) space_left); + in_buff += space_left; + in_buf += space_left; + input_size -= space_left; + bytes_left -= space_left; + } +} + +/* Copy the the next NUM_BYTES bytes of `input_buffer' into PEEK_BUF. + If NUM_BYTES bytes are not available, read the next `io_block_size' bytes + into the end of `input_buffer' and update `input_size'. + + Return the number of bytes copied into PEEK_BUF. + If the number of bytes returned is less than NUM_BYTES, + then EOF has been reached. */ + +int +peek_in_buf (peek_buf, in_des, num_bytes) + char *peek_buf; + int in_des; + int num_bytes; +{ + long tmp_input_size; + long got_bytes; + char *append_buf; + +#ifdef BROKEN_LONG_TAPE_DRIVER + /* Some tape drivers seem to have a signed internal seek pointer and + they lose if it overflows and becomes negative (e.g. when writing + tapes > 4Gb). Doing an lseek (des, 0, SEEK_SET) seems to reset the + seek pointer and prevent it from overflowing. */ + if (input_is_special + && (input_bytes_before_lseek += num_bytes) < 0L) + { + lseek(in_des, 0L, SEEK_SET); + input_bytes_before_lseek = 0; + } +#endif + + while (input_size < num_bytes) + { + append_buf = in_buff + input_size; + tmp_input_size = rmtread (in_des, append_buf, io_block_size); + if (tmp_input_size == 0) + { + if (input_is_special) + { + get_next_reel (in_des); + tmp_input_size = rmtread (in_des, append_buf, io_block_size); + } + else + break; + } + if (tmp_input_size < 0) + error (1, errno, "read error"); + input_bytes += tmp_input_size; + input_size += tmp_input_size; + } + if (num_bytes <= input_size) + got_bytes = num_bytes; + else + got_bytes = input_size; + bcopy (in_buff, peek_buf, (unsigned) got_bytes); + return got_bytes; +} + +/* Skip the next NUM_BYTES bytes of file descriptor IN_DES. */ + +void +toss_input (in_des, num_bytes) + int in_des; + long num_bytes; +{ + register long bytes_left = num_bytes; /* Bytes needing to be copied. */ + register long space_left; /* Bytes to copy from input buffer. */ + + while (bytes_left > 0) + { + if (input_size == 0) + fill_input_buffer (in_des, io_block_size); + if (bytes_left < input_size) + space_left = bytes_left; + else + space_left = input_size; + in_buff += space_left; + input_size -= space_left; + bytes_left -= space_left; + } +} + +/* Copy a file using the input and output buffers, which may start out + partly full. After the copy, the files are not closed nor the last + block flushed to output, and the input buffer may still be partly + full. If `crc_i_flag' is set, add each byte to `crc'. + IN_DES is the file descriptor for input; + OUT_DES is the file descriptor for output; + NUM_BYTES is the number of bytes to copy. */ + +void +copy_files (in_des, out_des, num_bytes) + int in_des; + int out_des; + long num_bytes; +{ + long size; + long k; + + while (num_bytes > 0) + { + if (input_size == 0) + fill_input_buffer (in_des, io_block_size); + size = (input_size < num_bytes) ? input_size : num_bytes; + if (crc_i_flag) + { + for (k = 0; k < size; ++k) + crc += in_buff[k] & 0xff; + } + copy_buf_out (in_buff, out_des, size); + num_bytes -= size; + input_size -= size; + in_buff += size; + } +} + +/* Create all directories up to but not including the last part of NAME. + Do not destroy any nondirectories while creating directories. */ + +void +create_all_directories (name) + char *name; +{ + char *dir; + int mode; +#ifdef HPUX_CDF + int cdf; +#endif + + dir = dirname (name); + mode = 0700; +#ifdef HPUX_CDF + cdf = islastparentcdf (name); + if (cdf) + { + dir [strlen (dir) - 1] = '\0'; /* remove final + */ + mode = 04700; + } + +#endif + + if (dir == NULL) + error (2, 0, "virtual memory exhausted"); + + if (dir[0] != '.' || dir[1] != '\0') + make_path (dir, mode, 0700, -1, -1, (char *) NULL); + + free (dir); +} + +/* Prepare to append to an archive. We have been in + process_copy_in, keeping track of the position where + the last header started in `last_header_start'. Now we + have the starting position of the last header (the TRAILER!!! + header, or blank record for tar archives) and we want to start + writing (appending) over the last header. The last header may + be in the middle of a block, so to keep the buffering in sync + we lseek back to the start of the block, read everything up + to but not including the last header, lseek back to the start + of the block, and then do a copy_buf_out of what we read. + Actually, we probably don't have to worry so much about keeping the + buffering perfect since you can only append to archives that + are disk files. */ + +void +prepare_append (out_file_des) + int out_file_des; +{ + int start_of_header; + int start_of_block; + int useful_bytes_in_block; + char *tmp_buf; + + start_of_header = last_header_start; + /* Figure out how many bytes we will rewrite, and where they start. */ + useful_bytes_in_block = start_of_header % io_block_size; + start_of_block = start_of_header - useful_bytes_in_block; + + if (lseek (out_file_des, start_of_block, SEEK_SET) < 0) + error (1, errno, "cannot seek on output"); + if (useful_bytes_in_block > 0) + { + tmp_buf = (char *) xmalloc (useful_bytes_in_block); + read (out_file_des, tmp_buf, useful_bytes_in_block); + if (lseek (out_file_des, start_of_block, SEEK_SET) < 0) + error (1, errno, "cannot seek on output"); + copy_buf_out (tmp_buf, out_file_des, useful_bytes_in_block); + free (tmp_buf); + } + + /* We are done reading the archive, so clear these since they + will now be used for reading in files that we are appending + to the archive. */ + input_size = 0; + input_bytes = 0; + in_buff = input_buffer; +} + +/* Support for remembering inodes with multiple links. Used in the + "copy in" and "copy pass" modes for making links instead of copying + the file. */ + +struct inode_val +{ + unsigned long inode; + unsigned long major_num; + unsigned long minor_num; + char *file_name; +}; + +/* Inode hash table. Allocated by first call to add_inode. */ +static struct inode_val **hash_table = NULL; + +/* Size of current hash table. Initial size is 47. (47 = 2*22 + 3) */ +static int hash_size = 22; + +/* Number of elements in current hash table. */ +static int hash_num; + +/* Find the file name associated with NODE_NUM. If there is no file + associated with NODE_NUM, return NULL. */ + +char * +find_inode_file (node_num, major_num, minor_num) + unsigned long node_num; + unsigned long major_num; + unsigned long minor_num; +{ +#ifndef __MSDOS__ + int start; /* Initial hash location. */ + int temp; /* Rehash search variable. */ + + if (hash_table != NULL) + { + /* Hash function is node number modulo the table size. */ + start = node_num % hash_size; + + /* Initial look into the table. */ + if (hash_table[start] == NULL) + return NULL; + if (hash_table[start]->inode == node_num + && hash_table[start]->major_num == major_num + && hash_table[start]->minor_num == minor_num) + return hash_table[start]->file_name; + + /* The home position is full with a different inode record. + Do a linear search terminated by a NULL pointer. */ + for (temp = (start + 1) % hash_size; + hash_table[temp] != NULL && temp != start; + temp = (temp + 1) % hash_size) + { + if (hash_table[temp]->inode == node_num + && hash_table[start]->major_num == major_num + && hash_table[start]->minor_num == minor_num) + return hash_table[temp]->file_name; + } + } +#endif + return NULL; +} + +/* Associate FILE_NAME with the inode NODE_NUM. (Insert into hash table.) */ + +void +add_inode (node_num, file_name, major_num, minor_num) + unsigned long node_num; + char *file_name; + unsigned long major_num; + unsigned long minor_num; +{ +#ifndef __MSDOS__ + struct inode_val *temp; + + /* Create new inode record. */ + temp = (struct inode_val *) xmalloc (sizeof (struct inode_val)); + temp->inode = node_num; + temp->major_num = major_num; + temp->minor_num = minor_num; + temp->file_name = xstrdup (file_name); + + /* Do we have to increase the size of (or initially allocate) + the hash table? */ + if (hash_num == hash_size || hash_table == NULL) + { + struct inode_val **old_table; /* Pointer to old table. */ + int i; /* Index for re-insert loop. */ + + /* Save old table. */ + old_table = hash_table; + if (old_table == NULL) + hash_num = 0; + + /* Calculate new size of table and allocate it. + Sequence of table sizes is 47, 97, 197, 397, 797, 1597, 3197, 6397 ... + where 3197 and most of the sizes after 6397 are not prime. The other + numbers listed are prime. */ + hash_size = 2 * hash_size + 3; + hash_table = (struct inode_val **) + xmalloc (hash_size * sizeof (struct inode_val *)); + bzero (hash_table, hash_size * sizeof (struct inode_val *)); + + /* Insert the values from the old table into the new table. */ + for (i = 0; i < hash_num; i++) + hash_insert (old_table[i]); + + if (old_table != NULL) + free (old_table); + } + + /* Insert the new record and increment the count of elements in the + hash table. */ + hash_insert (temp); + hash_num++; +#endif /* __MSDOS__ */ +} + +/* Do the hash insert. Used in normal inserts and resizing the hash + table. It is guaranteed that there is room to insert the item. + NEW_VALUE is the pointer to the previously allocated inode, file + name association record. */ + +static void +hash_insert (new_value) + struct inode_val *new_value; +{ + int start; /* Home position for the value. */ + int temp; /* Used for rehashing. */ + + /* Hash function is node number modulo the table size. */ + start = new_value->inode % hash_size; + + /* Do the initial look into the table. */ + if (hash_table[start] == NULL) + { + hash_table[start] = new_value; + return; + } + + /* If we get to here, the home position is full with a different inode + record. Do a linear search for the first NULL pointer and insert + the new item there. */ + temp = (start + 1) % hash_size; + while (hash_table[temp] != NULL) + temp = (temp + 1) % hash_size; + + /* Insert at the NULL. */ + hash_table[temp] = new_value; +} + +/* Open FILE in the mode specified by the command line options + and return an open file descriptor for it, + or -1 if it can't be opened. */ + +int +open_archive (file) + char *file; +{ + int fd; + void (*copy_in) (); /* Workaround for pcc bug. */ + + copy_in = process_copy_in; + + if (copy_function == copy_in) + fd = rmtopen (file, O_RDONLY | O_BINARY, 0666); + else + { + if (!append_flag) + fd = rmtopen (file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); + else + fd = rmtopen (file, O_RDWR | O_BINARY, 0666); + } + + return fd; +} + +/* Attempt to rewind the tape drive on file descriptor TAPE_DES + and take it offline. */ + +void +tape_offline (tape_des) + int tape_des; +{ +#if defined(MTIOCTOP) && defined(MTOFFL) + struct mtop control; + + control.mt_op = MTOFFL; + control.mt_count = 1; + rmtioctl (tape_des, MTIOCTOP, &control); /* Don't care if it fails. */ +#endif +} + +/* The file on file descriptor TAPE_DES is assumed to be magnetic tape + (or floppy disk or other device) and the end of the medium + has been reached. Ask the user for to mount a new "tape" to continue + the processing. If the user specified the device name on the + command line (with the -I, -O, -F or --file options), then we can + automatically re-open the same device to use the next medium. If the + user did not specify the device name, then we have to ask them which + device to use. */ + +void +get_next_reel (tape_des) + int tape_des; +{ + static int reel_number = 1; + FILE *tty_in; /* File for interacting with user. */ + FILE *tty_out; /* File for interacting with user. */ + int old_tape_des; + char *next_archive_name; + dynamic_string new_name; + char *str_res; + + ds_init (&new_name, 128); + + /* Open files for interactive communication. */ + tty_in = fopen (CONSOLE, "r"); + if (tty_in == NULL) + error (2, errno, CONSOLE); + tty_out = fopen (CONSOLE, "w"); + if (tty_out == NULL) + error (2, errno, CONSOLE); + + old_tape_des = tape_des; + tape_offline (tape_des); + rmtclose (tape_des); + + /* Give message and wait for carrage return. User should hit carrage return + only after loading the next tape. */ + ++reel_number; + if (new_media_message) + fprintf (tty_out, "%s", new_media_message); + else if (new_media_message_with_number) + fprintf (tty_out, "%s%d%s", new_media_message_with_number, reel_number, + new_media_message_after_number); + else if (archive_name) + fprintf (tty_out, "Found end of tape. Load next tape and press RETURN. "); + else + fprintf (tty_out, "Found end of tape. To continue, type device/file name when ready.\n"); + + fflush (tty_out); + + if (archive_name) + { + int c; + + do + c = getc (tty_in); + while (c != EOF && c != '\n'); + + tape_des = open_archive (archive_name); + if (tape_des == -1) + error (1, errno, "%s", archive_name); + } + else + { + do + { + if (tape_des < 0) + { + fprintf (tty_out, + "To continue, type device/file name when ready.\n"); + fflush (tty_out); + } + + str_res = ds_fgets (tty_in, &new_name); + if (str_res == NULL || str_res[0] == '\0') + exit (1); + next_archive_name = str_res; + + tape_des = open_archive (next_archive_name); + if (tape_des == -1) + error (0, errno, "%s", next_archive_name); + } + while (tape_des < 0); + } + + /* We have to make sure that `tape_des' has not changed its value even + though we closed it and reopened it, since there are local + copies of it in other routines. This works fine on Unix (even with + rmtread and rmtwrite) since open will always return the lowest + available file descriptor and we haven't closed any files (e.g., + stdin, stdout or stderr) that were opened before we originally opened + the archive. */ + + if (tape_des != old_tape_des) + error (1, 0, "internal error: tape descriptor changed from %d to %d", + old_tape_des, tape_des); + + free (new_name.ds_string); + fclose (tty_in); + fclose (tty_out); +} + +/* If MESSAGE does not contain the string "%d", make `new_media_message' + a copy of MESSAGE. If MESSAGES does contain the string "%d", make + `new_media_message_with_number' a copy of MESSAGE up to, but + not including, the string "%d", and make `new_media_message_after_number' + a copy of MESSAGE after the string "%d". */ + +void +set_new_media_message (message) + char *message; +{ + char *p; + int prev_was_percent; + + p = message; + prev_was_percent = 0; + while (*p != '\0') + { + if (*p == 'd' && prev_was_percent) + break; + prev_was_percent = (*p == '%'); + ++p; + } + if (*p == '\0') + { + new_media_message = xstrdup (message); + } + else + { + int length = p - message - 1; + + new_media_message_with_number = xmalloc (length + 1); + strncpy (new_media_message_with_number, message, length); + new_media_message_with_number[length] = '\0'; + length = strlen (p + 1); + new_media_message_after_number = xmalloc (length + 1); + strcpy (new_media_message_after_number, message); + } +} + +#ifdef SYMLINK_USES_UMASK +/* Most machines always create symlinks with rwxrwxrwx protection, + but some (HP/UX 8.07; maybe DEC's OSF on MIPS, too?) use the + umask when creating symlinks, so if your umask is 022 you end + up with rwxr-xr-x symlinks (although HP/UX seems to completely + ignore the protection). There doesn't seem to be any way to + manipulate the modes once the symlinks are created (e.g. + a hypothetical "lchmod"), so to create them with the right + modes we have to set the umask first. */ + +int +umasked_symlink (name1, name2, mode) + char *name1; + char *name2; + int mode; +{ + int old_umask; + int rc; + mode = ~(mode & 0777) & 0777; + old_umask = umask (mode); + rc = symlink (name1, name2); + umask (old_umask); + return rc; +} +#endif /* SYMLINK_USES_UMASK */ + +#ifdef __MSDOS__ +int +chown (path, owner, group) + char *path; + int owner, group; +{ + return 0; +} +#endif + +#ifdef __TURBOC__ +#include +#include +#include + +int +utime (char *filename, struct utimbuf *utb) +{ + extern int errno; + struct tm *tm; + struct ftime filetime; + time_t when; + int fd; + int status; + + if (utb == 0) + when = time (0); + else + when = utb->modtime; + + fd = _open (filename, O_RDWR); + if (fd == -1) + return -1; + + tm = localtime (&when); + if (tm->tm_year < 80) + filetime.ft_year = 0; + else + filetime.ft_year = tm->tm_year - 80; + filetime.ft_month = tm->tm_mon + 1; + filetime.ft_day = tm->tm_mday; + if (tm->tm_hour < 0) + filetime.ft_hour = 0; + else + filetime.ft_hour = tm->tm_hour; + filetime.ft_min = tm->tm_min; + filetime.ft_tsec = tm->tm_sec / 2; + + status = setftime (fd, &filetime); + _close (fd); + return status; +} +#endif +#ifdef HPUX_CDF +/* When we create a cpio archive we mark CDF's by putting an extra `/' + after their component name so we can distinguish the CDF's when we + extract the archive (in case the "hidden" directory's files appear + in the archive before the directory itself). E.g., in the path + "a/b+/c", if b+ is a CDF, we will write this path as "a/b+//c" in + the archive so when we extract the archive we will know that b+ + is actually a CDF, and not an ordinary directory whose name happens + to end in `+'. We also do the same thing internally in copypass.c. */ + + +/* Take an input pathname and check it for CDF's. Insert an extra + `/' in the pathname after each "hidden" directory. If we add + any `/'s, return a malloced string (which it will reuse for + later calls so our caller doesn't have to worry about freeing + the string) instead of the original input string. */ + +char * +add_cdf_double_slashes (input_name) + char *input_name; +{ + static char *ret_name = NULL; /* re-usuable return buffer (malloc'ed) */ + static int ret_size = -1; /* size of return buffer. */ + char *p; + char *q; + int n; + struct stat dir_stat; + + /* Search for a `/' preceeded by a `+'. */ + + for (p = input_name; *p != '\0'; ++p) + { + if ( (*p == '+') && (*(p + 1) == '/') ) + break; + } + + /* If we didn't find a `/' preceeded by a `+' then there are + no CDF's in this pathname. Return the original pathname. */ + + if (*p == '\0') + return input_name; + + /* There was a `/' preceeded by a `+' in the pathname. If it is a CDF + then we will need to copy the input pathname to our return + buffer so we can insert the extra `/'s. Since we can't tell + yet whether or not it is a CDF we will just always copy the + string to the return buffer. First we have to make sure the + buffer is large enough to hold the string and any number of + extra `/'s we might add. */ + + n = 2 * (strlen (input_name) + 1); + if (n >= ret_size) + { + if (ret_size < 0) + ret_name = (char *) malloc (n); + else + ret_name = (char *)realloc (ret_name, n); + ret_size = n; + } + + /* Clear the `/' after this component, so we can stat the pathname + up to and including this component. */ + ++p; + *p = '\0'; + if ((*xstat) (input_name, &dir_stat) < 0) + { + error (0, errno, "%s", input_name); + return input_name; + } + + /* Now put back the `/' after this component and copy the pathname up to + and including this component and its trailing `/' to the return + buffer. */ + *p++ = '/'; + strncpy (ret_name, input_name, p - input_name); + q = ret_name + (p - input_name); + + /* If it was a CDF, add another `/'. */ + if (S_ISDIR (dir_stat.st_mode) && (dir_stat.st_mode & 04000) ) + *q++ = '/'; + + /* Go through the rest of the input pathname, copying it to the + return buffer, and adding an extra `/' after each CDF. */ + while (*p != '\0') + { + if ( (*p == '+') && (*(p + 1) == '/') ) + { + *q++ = *p++; + + *p = '\0'; + if ((*xstat) (input_name, &dir_stat) < 0) + { + error (0, errno, "%s", input_name); + return input_name; + } + *p = '/'; + + if (S_ISDIR (dir_stat.st_mode) && (dir_stat.st_mode & 04000) ) + *q++ = '/'; + } + *q++ = *p++; + } + *q = '\0'; + + return ret_name; +} + +/* Is the last parent directory (e.g., c in a/b/c/d) a CDF? If the + directory name ends in `+' and is followed by 2 `/'s instead of 1 + then it is. This is only the case for cpio archives, but we don't + have to worry about tar because tar always has the directory before + its files (or else we lose). */ + +islastparentcdf(path) + char *path; +{ + char *newpath; + char *slash; + int slash_count; + int length; /* Length of result, not including NUL. */ + + slash = rindex (path, '/'); + if (slash == 0) + return 0; + else + { + slash_count = 0; + while (slash > path && *slash == '/') + { + ++slash_count; + --slash; + } + + + if ( (*slash == '+') && (slash_count >= 2) ) + return 1; + } + return 0; +} +#endif diff --git a/gnu/usr.bin/cpio/version.c b/gnu/usr.bin/cpio/version.c new file mode 100644 index 0000000000..38a63f2c98 --- /dev/null +++ b/gnu/usr.bin/cpio/version.c @@ -0,0 +1,2 @@ +/* The version number of cpio and mt. */ +char *version_string = "version 2.3\n"; diff --git a/gnu/usr.bin/cpio/xmalloc.c b/gnu/usr.bin/cpio/xmalloc.c new file mode 100644 index 0000000000..f989004beb --- /dev/null +++ b/gnu/usr.bin/cpio/xmalloc.c @@ -0,0 +1,65 @@ +/* xmalloc.c -- malloc with out of memory checking + Copyright (C) 1990, 1991 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef STDC_HEADERS +#include +#else +char *malloc (); +char *realloc (); +void free (); +#endif + +void error (); + +/* Allocate N bytes of memory dynamically, with error checking. */ + +char * +xmalloc (n) + unsigned n; +{ + char *p; + + p = malloc (n); + if (p == 0) + /* Must exit with 2 for `cmp'. */ + error (2, 0, "virtual memory exhausted"); + return p; +} + +/* Change the size of an allocated block of memory P to N bytes, + with error checking. + If P is NULL, run xmalloc. + If N is 0, run free and return NULL. */ + +char * +xrealloc (p, n) + char *p; + unsigned n; +{ + if (p == 0) + return xmalloc (n); + if (n == 0) + { + free (p); + return 0; + } + p = realloc (p, n); + if (p == 0) + /* Must exit with 2 for `cmp'. */ + error (2, 0, "virtual memory exhausted"); + return p; +} diff --git a/gnu/usr.bin/cpio/xstrdup.c b/gnu/usr.bin/cpio/xstrdup.c new file mode 100644 index 0000000000..9588bc78d1 --- /dev/null +++ b/gnu/usr.bin/cpio/xstrdup.c @@ -0,0 +1,32 @@ +/* xstrdup.c -- copy a string with out of memory checking + Copyright (C) 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#if defined(STDC_HEADERS) || defined(HAVE_STRING_H) +#include +#else +#include +#endif +char *xmalloc (); + +/* Return a newly allocated copy of STRING. */ + +char * +xstrdup (string) + char *string; +{ + return strcpy (xmalloc (strlen (string) + 1), string); +} diff --git a/gnu/usr.bin/cvs/cvs/Makefile b/gnu/usr.bin/cvs/cvs/Makefile index 5be0fbe323..be35c2f95f 100644 --- a/gnu/usr.bin/cvs/cvs/Makefile +++ b/gnu/usr.bin/cvs/cvs/Makefile @@ -3,7 +3,11 @@ CFLAGS += -I${.CURDIR}/../lib \ -DDIRENT -DSTDC_HEADERS -DPOSIX -DBROKEN_SIGISMEMBER \ -DFTIME_MISSING -DHAVE_TIMEZONE -DUTIME_NULL_MISSING +.if exists(${.CURDIR}/../lib/obj) LDADD= -L${.CURDIR}/../lib/obj -lcvs +.else +LDADD= -L${.CURDIR}/../lib/ -lcvs +.endif SRCS = add.c admin.c checkin.c checkout.c classify.c commit.c \ create_adm.c diff.c entries.c find_names.c history.c ignore.c \ @@ -11,8 +15,11 @@ import.c lock.c log.c logmsg.c main.c rcs.c modules.c \ no_diff.c parseinfo.c patch.c recurse.c release.c remove.c repos.c rtag.c \ status.c tag.c update.c vers_ts.c version.c -MAN1= cvs.0 -MAN5= cvs.0 +MAN1= cvs.1 +MAN5= cvs.5 + +check: + @echo `pwd` ${.CURDIR} .include .include "../../Makefile.inc" diff --git a/gnu/usr.bin/cvs/cvs/import.c b/gnu/usr.bin/cvs/cvs/import.c index bf66dcbfc8..095a80042f 100644 --- a/gnu/usr.bin/cvs/cvs/import.c +++ b/gnu/usr.bin/cvs/cvs/import.c @@ -252,6 +252,7 @@ import_descend (message, vtag, targc, targv) struct direct *dp; int err = 0; int has_dirs = 0; + FILE *links = (FILE *)0; /* first, load up any per-directory ignore lists */ ign_add_file (CVSDOTIGNORE, 1); @@ -279,8 +280,38 @@ import_descend (message, vtag, targc, targv) { if (islink (dp->d_name)) { +#ifdef DO_LINKS + char lnbuf[PATH_MAX]; + int lln; + + add_log ('L', dp->d_name); + if ((lln = readlink(dp->d_name, lnbuf, PATH_MAX)) == -1) { + error(0, errno, "Can't read contents of symlink %s", + dp->d_name); + return (1); + } + else { + if (!links) { + char lnrep[PATH_MAX]; + + sprintf(lnrep, "%s/SymLinks", repository); + links = fopen(lnrep, "a+"); + if (!links) { + error (0, errno, + "Can't open SymLinks file %s", lnrep); + return (1); + } + } + lnbuf[lln] = '\0'; + fputs(dp->d_name, links); + fputc('\n', links); + fputs(lnbuf, links); + fputc('\n', links); + } +#else add_log ('L', dp->d_name); err++; +#endif } else { @@ -307,6 +338,8 @@ import_descend (message, vtag, targc, targv) (void) closedir (dirp); } } + if (links) + fclose(links); return (err); } diff --git a/gnu/usr.bin/cvs/cvs/update.c b/gnu/usr.bin/cvs/cvs/update.c index e0dc2870cf..c25bc757f5 100644 --- a/gnu/usr.bin/cvs/cvs/update.c +++ b/gnu/usr.bin/cvs/cvs/update.c @@ -424,6 +424,37 @@ update_filesdone_proc (err, repository, update_dir) (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); } +#ifdef DO_LINKS + { + char lnfile[PATH_MAX]; + FILE *links; + + sprintf(lnfile, "%s/SymLinks", repository); + links = fopen(lnfile, "r"); + if (links) { + char from[PATH_MAX], to[PATH_MAX]; + + /* Read all the link pairs from the symlinks file */ + while (fgets(to, PATH_MAX, links)) { + fgets(from, PATH_MAX, links); + + /* Strip off the newlines */ + to[strlen(to) - 1] = '\0'; + from[strlen(from) - 1] = '\0'; + + /* Do it */ + if (symlink(from, to) == -1) { + error (0, errno, "Unable to create symlink `%s'", to); + return 1; + } + else if (!quiet) + error (0, 0, "Creating symlink %s", to); + } + fclose(links); + } + } +#endif + return (err); } diff --git a/gnu/usr.bin/cvs/lib/Makefile b/gnu/usr.bin/cvs/lib/Makefile index cf3f20fd28..3c68ed5530 100644 --- a/gnu/usr.bin/cvs/lib/Makefile +++ b/gnu/usr.bin/cvs/lib/Makefile @@ -5,4 +5,9 @@ CFLAGS += -I${.CURDIR} -I${.CURDIR}/../cvs -DFTIME_MISSING -DHAVE_TIMEZONE SRCS = argmatch.c error.c getopt.c sighandle.c strippath.c stripslash.c yesno.c \ getdate.y fnmatch.c regex.c subr.c myndbm.c hash.c +CLEANFILES+= getdate.c + +install: + @echo -n + .include diff --git a/gnu/usr.bin/cvs/lib/Makefile.in b/gnu/usr.bin/cvs/lib/Makefile.in deleted file mode 100644 index a8309f2fc0..0000000000 --- a/gnu/usr.bin/cvs/lib/Makefile.in +++ /dev/null @@ -1,91 +0,0 @@ -# Makefile for library files used by GNU CVS. -# Do not use this makefile directly, but only from `../Makefile'. -# Copyright (C) 1986, 1988-1992 Free Software Foundation, Inc. - -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -# @(#)Makefile.in 1.12 92/03/31 - -SHELL = /bin/sh - -srcdir = @srcdir@ -@VPATH@ - -SOURCES = argmatch.c \ -error.c getopt.c getopt1.c \ -sighandle.c \ -strippath.c stripslash.c yesno.c \ -getdate.y \ -hostname.c fnmatch.c ftruncate.c mkdir.c rename.c regex.c \ -strdup.c getwd.c alloca.c - -OBJECTS = argmatch.o \ -error.o getopt.o getopt1.o \ -sighandle.o \ -strippath.o stripslash.o yesno.o \ -getdate.o \ -@LIBOBJS@ - -DISTFILES = Makefile.in getopt.h \ -fnmatch.h regex.h system.h wait.h $(SOURCES) - -xxx: - @cd ..; $(MAKE) all SUBDIRS=lib - -all: libcvs.a -.PHONY: all - -install: all -.PHONY: install - -tags: $(DISTFILES) - ctags $(DISTFILES) - -TAGS: $(DISTFILES) - etags $(DISTFILES) - -ls: - @echo $(DISTFILES) -.PHONY: ls - -clean: - rm -f *.a *.o *.tab.c getdate.c -.PHONY: clean - -distclean: clean - rm -f tags TAGS Makefile -.PHONY: distclean - -realclean: distclean -.PHONY: realclean - -dist: - ln $(DISTFILES) ../`cat ../.fname`/lib -.PHONY: dist - -libcvs.a: $(OBJECTS) - $(AR) cr $@ $(OBJECTS) - -$(RANLIB) $@ - -getdate.c: getdate.y - @echo expect 8 shift/reduce conflicts - $(YACC) $(srcdir)/getdate.y - -if test -f y.tab.c ; then mv y.tab.c getdate.c ; fi - -if test -f getdate.tab.c ; then mv getdate.tab.c getdate.c ; fi - -fnmatch.o: fnmatch.h -getopt1.o: getopt.h -regex.o: regex.h -getwd.o: system.h diff --git a/gnu/usr.bin/cvs/mkmodules/Makefile b/gnu/usr.bin/cvs/mkmodules/Makefile index 9f66fdefc9..bec0bd6484 100644 --- a/gnu/usr.bin/cvs/mkmodules/Makefile +++ b/gnu/usr.bin/cvs/mkmodules/Makefile @@ -1,8 +1,12 @@ PROG = mkmodules SRCS = mkmodules.c - CFLAGS += -I${.CURDIR}/../cvs -I${.CURDIR}/../lib -LDADD= -L${.CURDIR}/../lib/obj -lcvs + +.if exists(${.CURDIR}/../lib/obj) +LDADD= -L${.CURDIR}/../lib/obj -lcvs +.else +LDADD= -L${.CURDIR}/../lib/ -lcvs +.endif .include .include "../../Makefile.inc" diff --git a/gnu/usr.bin/cvs/mkmodules/xxx b/gnu/usr.bin/cvs/mkmodules/xxx deleted file mode 100644 index f0dd87dee7..0000000000 --- a/gnu/usr.bin/cvs/mkmodules/xxx +++ /dev/null @@ -1,5320 +0,0 @@ -# 1 "/usr/src/gnu/cvs/mkmodules/mkmodules.c" - - - - - - - - - - - - - -# 1 "/usr/include/sys/syslimits.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 14 "/usr/src/gnu/cvs/mkmodules/mkmodules.c" 2 - -# 1 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 1 - - -# 1 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 1 - - - - - - - - - - - - - - - - - - - -# 1 "/usr/include/sys/types.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -typedef unsigned char u_char; -typedef unsigned short u_short; -typedef unsigned int u_int; -typedef unsigned long u_long; -typedef unsigned short ushort; - -typedef char * caddr_t; -typedef long daddr_t; -typedef short dev_t; -typedef u_long ino_t; -typedef long off_t; -typedef u_short nlink_t; -typedef long swblk_t; -typedef long segsz_t; -typedef u_short uid_t; -typedef u_short gid_t; -typedef short pid_t; -typedef u_short mode_t; -typedef u_long fixpt_t; - - -typedef struct _uquad { u_long val[2]; } u_quad; -typedef struct _quad { long val[2]; } quad; -typedef long * qaddr_t; - - - - - - -# 1 "/usr/include/machine/ansi.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 69 "/usr/include/sys/types.h" 2 - - -# 1 "/usr/include/machine/types.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -typedef struct _physadr { - int r[1]; -} *physadr; - -typedef struct label_t { - int val[6]; -} label_t; - -typedef u_long vm_offset_t; -typedef u_long vm_size_t; - - -# 71 "/usr/include/sys/types.h" 2 - - - - -typedef unsigned long clock_t; - - - - -typedef unsigned int size_t; - - - - -typedef long time_t; - - - - - - - - - - - - - - - - -typedef long fd_mask; - - - - - - -typedef struct fd_set { - fd_mask fds_bits[(((256 )+(( (sizeof(fd_mask) * 8 ) )-1))/( (sizeof(fd_mask) * 8 ) )) ]; -} fd_set; - - - - - - -# 132 "/usr/include/sys/types.h" - - - - -# 20 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2 - -# 1 "/usr/include/sys/stat.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -struct stat -{ - dev_t st_dev; - ino_t st_ino; - mode_t st_mode; - nlink_t st_nlink; - uid_t st_uid; - gid_t st_gid; - dev_t st_rdev; - off_t st_size; - time_t st_atime; - long st_spare1; - time_t st_mtime; - long st_spare2; - time_t st_ctime; - long st_spare3; - long st_blksize; - long st_blocks; - u_long st_flags; - u_long st_gen; -}; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 1 "/usr/include/sys/cdefs.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 76 "/usr/include/sys/cdefs.h" - - - -# 114 "/usr/include/sys/stat.h" 2 - - - -mode_t umask (mode_t) ; -int chmod (const char *, mode_t) ; -int fstat (int, struct stat *) ; -int mkdir (const char *, mode_t) ; -int mkfifo (const char *, mode_t) ; -int stat (const char *, struct stat *) ; - -int fchmod (int, mode_t) ; -int lstat (const char *, struct stat *) ; - - - -# 21 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -off_t lseek (); - - - - - -# 1 "/usr/include/time.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 1 "/usr/include/machine/ansi.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 57 "/usr/include/machine/ansi.h" - -# 39 "/usr/include/time.h" 2 - - - - - - - - - - - - - - - - - - - - - -struct tm { - int tm_sec; - int tm_min; - int tm_hour; - int tm_mday; - int tm_mon; - int tm_year; - int tm_wday; - int tm_yday; - int tm_isdst; - long tm_gmtoff; - char *tm_zone; -}; - -# 1 "/usr/include/sys/cdefs.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 78 "/usr/include/sys/cdefs.h" - -# 74 "/usr/include/time.h" 2 - - - -char *asctime (const struct tm *) ; -clock_t clock (void) ; -char *ctime (const time_t *) ; -double difftime (time_t, time_t) ; -struct tm *gmtime (const time_t *) ; -struct tm *localtime (const time_t *) ; -time_t mktime (struct tm *) ; -size_t strftime (char *, size_t, const char *, const struct tm *) ; -time_t time (time_t *) ; - - -void tzset (void) ; - - - -char *timezone (int, int) ; -void tzsetwall (void) ; - - - - -# 72 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2 - - - -# 86 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" - -# 1 "/usr/include/sys/timeb.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -struct timeb { - time_t time; - unsigned short millitm; - short timezone; - short dstflag; -}; -# 87 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2 - - - - - - - - - - -# 1 "/usr/include/sys/param.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 1 "/usr/include/sys/types.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 135 "/usr/include/sys/types.h" - -# 46 "/usr/include/sys/param.h" 2 - - - - - - - - - - -# 1 "/usr/include/sys/syslimits.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 56 "/usr/include/sys/param.h" 2 - - - - - - - - - - - - - - - - - - - - - - - -# 1 "/usr/include/sys/signal.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 1 "/usr/include/machine/trap.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 42 "/usr/include/sys/signal.h" 2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 1 "/usr/include/sys/cdefs.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 78 "/usr/include/sys/cdefs.h" - -# 90 "/usr/include/sys/signal.h" 2 - - - -typedef void (*sig_t) (int) ; - - -typedef void (*__sighandler_t) (int) ; -typedef unsigned int sigset_t; - - -int sigaddset (sigset_t *, int) ; -int sigdelset (sigset_t *, int) ; -int sigemptyset (sigset_t *) ; -int sigfillset (sigset_t *) ; -int sigismember (const sigset_t *, int) ; - - - - - - - - - - - -struct sigaction { - __sighandler_t sa_handler; - sigset_t sa_mask; - int sa_flags; -}; - - - - - - - - - - - - - - - - - - -struct sigvec { - void (*sv_handler)(); - int sv_mask; - int sv_flags; -}; - - - - - - - -struct sigaltstack { - char *ss_base; - int ss_len; - int ss_onstack; -}; - - - - -struct sigstack { - char *ss_sp; - int ss_onstack; -}; - - - - - - - - -struct sigcontext { - int sc_onstack; - int sc_mask; - int sc_sp; - int sc_fp; - int sc_ap; - int sc_pc; - int sc_ps; -}; - - - - - - - - - - - - - - -# 1 "/usr/include/sys/types.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 135 "/usr/include/sys/types.h" - -# 195 "/usr/include/sys/signal.h" 2 - - - - -__sighandler_t signal (int, __sighandler_t) ; -int raise (int) ; - -int kill (pid_t, int) ; -int sigaction (int, const struct sigaction *, struct sigaction *) ; -int sigpending (sigset_t *) ; -int sigprocmask (int, const sigset_t *, sigset_t *) ; -int sigsuspend (const sigset_t *) ; - - -int killpg (pid_t, int) ; -void psignal (unsigned, const char *) ; -int sigblock (int) ; -int siginterrupt (int, int) ; -int sigpause (int) ; -int sigreturn (struct sigcontext *) ; -int sigsetmask (int) ; -int sigstack (const struct sigstack *, struct sigstack *) ; -int sigvec (int, struct sigvec *, struct sigvec *) ; - - - - - -# 79 "/usr/include/sys/param.h" 2 - - - -# 1 "/usr/include/machine/param.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 82 "/usr/include/sys/param.h" 2 - -# 1 "/usr/include/machine/endian.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 1 "/usr/include/sys/cdefs.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 78 "/usr/include/sys/cdefs.h" - -# 55 "/usr/include/machine/endian.h" 2 - - - - - - - - - -# 77 "/usr/include/machine/endian.h" - - - - - - - - - - - - - - - - - - -# 106 "/usr/include/machine/endian.h" - - - - - - - - - - - - -# 83 "/usr/include/sys/param.h" 2 - -# 1 "/usr/include/machine/limits.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 84 "/usr/include/sys/param.h" 2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 97 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2 - - - - - - - - - - - - - - - - - - - -struct utimbuf -{ - long actime; - long modtime; -}; - -int utime (); - - -# 145 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" - -# 1 "/usr/include/strings.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 1 "/usr/include/string.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 1 "/usr/include/machine/ansi.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 57 "/usr/include/machine/ansi.h" - -# 38 "/usr/include/string.h" 2 - - - - - - - - - - - -# 1 "/usr/include/sys/cdefs.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 78 "/usr/include/sys/cdefs.h" - -# 49 "/usr/include/string.h" 2 - - - -void *memchr (const void *, int, size_t) ; -int memcmp (const void *, const void *, size_t) ; -void *memcpy (void *, const void *, size_t) ; -void *memmove (void *, const void *, size_t) ; -void *memset (void *, int, size_t) ; -char *strcat (char *, const char *) ; -char *strchr (const char *, int) ; -int strcmp (const char *, const char *) ; -int strcoll (const char *, const char *) ; -char *strcpy (char *, const char *) ; -size_t strcspn (const char *, const char *) ; -char *strerror (int) ; -size_t strlen (const char *) ; -char *strncat (char *, const char *, size_t) ; -int strncmp (const char *, const char *, size_t) ; -char *strncpy (char *, const char *, size_t) ; -char *strpbrk (const char *, const char *) ; -char *strrchr (const char *, int) ; -size_t strspn (const char *, const char *) ; -char *strstr (const char *, const char *) ; -char *strtok (char *, const char *) ; -size_t strxfrm (char *, const char *, size_t) ; - - - -int bcmp (const void *, const void *, size_t) ; -void bcopy (const void *, void *, size_t) ; -void bzero (void *, size_t) ; -int ffs (int) ; -char *index (const char *, int) ; -void *memccpy (void *, const void *, int, size_t) ; -char *rindex (const char *, int) ; -int strcasecmp (const char *, const char *) ; -char *strdup (const char *) ; -void strmode (int, char *) ; -int strncasecmp (const char *, const char *, size_t) ; -char *strsep (char **, const char *) ; -void swab (const void *, void *, size_t) ; - - - - -# 36 "/usr/include/strings.h" 2 - -# 146 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2 - - - -# 1 "/usr/include/errno.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -extern int errno; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 149 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2 - - - - -char *getenv (); -char *malloc (); -char *realloc (); -char *calloc (); -extern int errno; - - - - - - -# 173 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" - - - - - - -# 1 "/usr/include/sys/file.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 1 "/usr/include/sys/fcntl.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 1 "/usr/include/sys/types.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 135 "/usr/include/sys/types.h" - -# 46 "/usr/include/sys/fcntl.h" 2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 99 "/usr/include/sys/fcntl.h" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -struct flock { - short l_type; - short l_whence; - off_t l_start; - off_t l_len; - pid_t l_pid; -}; - - - - - - - - - - - - -# 1 "/usr/include/sys/cdefs.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 78 "/usr/include/sys/cdefs.h" - -# 169 "/usr/include/sys/fcntl.h" 2 - - - -int open (const char *, int, ...) ; -int creat (const char *, mode_t) ; -int fcntl (int, int, ...) ; - -int flock (int, int) ; - - - - - -# 36 "/usr/include/sys/file.h" 2 - -# 1 "/usr/include/sys/unistd.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 37 "/usr/include/sys/file.h" 2 - - -# 73 "/usr/include/sys/file.h" - -# 179 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2 - -char *getwd (); - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 1 "/usr/include/sys/dir.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 1 "/usr/include/dirent.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -struct dirent { - u_long d_fileno; - u_short d_reclen; - u_short d_namlen; - - - - - char d_name[255 + 1]; - -}; - - - - - - - - - - - -typedef struct _dirdesc { - int dd_fd; - long dd_loc; - long dd_size; - char *dd_buf; - int dd_len; - long dd_seek; -} DIR; - - - - - - - - - - - -# 1 "/usr/include/sys/cdefs.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 78 "/usr/include/sys/cdefs.h" - -# 88 "/usr/include/dirent.h" 2 - - - -DIR *opendir (const char *) ; -struct dirent *readdir (DIR *) ; -void rewinddir (DIR *) ; -int closedir (DIR *) ; - -long telldir (const DIR *) ; -void seekdir (DIR *, long) ; -int scandir (const char *, struct dirent ***, - int (*)(struct dirent *), int (*)(const void *, const void *)) ; -int alphasort (const void *, const void *) ; -int getdirentries (int, char *, int, long *) ; - - - - - - -# 44 "/usr/include/sys/dir.h" 2 - - - - - - - - - - - - - - - - - - -# 208 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2 - - - - - - - - - - - - - - - - -# 3 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2 - -# 1 "/usr/include/stdio.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 1 "/usr/include/sys/cdefs.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 78 "/usr/include/sys/cdefs.h" - -# 42 "/usr/include/stdio.h" 2 - - -# 1 "/usr/include/machine/ansi.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 57 "/usr/include/machine/ansi.h" - -# 44 "/usr/include/stdio.h" 2 - - - - - - - - - - -typedef long fpos_t; - - - - - - - - - - -struct __sbuf { - unsigned char *_base; - int _size; -}; - - - - - - - - - - - - - - - - - - - - - - - - - -typedef struct __sFILE { - unsigned char *_p; - int _r; - int _w; - short _flags; - short _file; - struct __sbuf _bf; - int _lbfsize; - - - void *_cookie; - int (*_close) (void *) ; - int (*_read) (void *, char *, int) ; - fpos_t (*_seek) (void *, fpos_t, int) ; - int (*_write) (void *, const char *, int) ; - - - struct __sbuf _ub; - unsigned char *_up; - int _ur; - - - unsigned char _ubuf[3]; - unsigned char _nbuf[1]; - - - struct __sbuf _lb; - - - int _blksize; - int _offset; -} FILE; - - -extern FILE __sF[]; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void clearerr (FILE *) ; -int fclose (FILE *) ; -int feof (FILE *) ; -int ferror (FILE *) ; -int fflush (FILE *) ; -int fgetc (FILE *) ; -int fgetpos (FILE *, fpos_t *) ; -char *fgets (char *, size_t, FILE *) ; -FILE *fopen (const char *, const char *) ; -int fprintf (FILE *, const char *, ...) ; -int fputc (int, FILE *) ; -int fputs (const char *, FILE *) ; -int fread (void *, size_t, size_t, FILE *) ; -FILE *freopen (const char *, const char *, FILE *) ; -int fscanf (FILE *, const char *, ...) ; -int fseek (FILE *, long, int) ; -int fsetpos (FILE *, const fpos_t *) ; -long ftell (const FILE *) ; -int fwrite (const void *, size_t, size_t, FILE *) ; -int getc (FILE *) ; -int getchar (void) ; -char *gets (char *) ; - -extern int sys_nerr; -extern char *sys_errlist[]; - -void perror (const char *) ; -int printf (const char *, ...) ; -int putc (int, FILE *) ; -int putchar (int) ; -int puts (const char *) ; -int remove (const char *) ; -int rename (const char *, const char *) ; -void rewind (FILE *) ; -int scanf (const char *, ...) ; -void setbuf (FILE *, char *) ; -int setvbuf (FILE *, char *, int, size_t) ; -int sprintf (char *, const char *, ...) ; -int sscanf (char *, const char *, ...) ; -FILE *tmpfile (void) ; -char *tmpnam (char *) ; -int ungetc (int, FILE *) ; -int vfprintf (FILE *, const char *, char * ) ; -int vprintf (const char *, char * ) ; -int vsprintf (char *, const char *, char * ) ; - - - - - - - - - - -char *ctermid (char *) ; -FILE *fdopen (int, const char *) ; -int fileno (FILE *) ; - - - - - - - - -char *fgetline (FILE *, size_t *) ; -int fpurge (FILE *) ; -int getw (FILE *) ; -int pclose (FILE *) ; -FILE *popen (const char *, const char *) ; -int putw (int, FILE *) ; -void setbuffer (FILE *, char *, int) ; -int setlinebuf (FILE *) ; -char *tempnam (const char *, const char *) ; -int snprintf (char *, size_t, const char *, ...) ; -int vsnprintf (char *, size_t, const char *, char * ) ; -int vscanf (const char *, char * ) ; -int vsscanf (const char *, const char *, char * ) ; - - - - - - - - - - - - - -FILE *funopen (const void *, - int (*)(void *, char *, int), - int (*)(void *, const char *, int), - fpos_t (*)(void *, fpos_t, int), - int (*)(void *)) ; - - - - - - - - - -int __srget (FILE *) ; -int __svfscanf (FILE *, const char *, char * ) ; -int __swbuf (int, FILE *) ; - - - - - - - - -static inline int __sputc(int _c, FILE *_p) { - if (--_p->_w >= 0 || (_p->_w >= _p->_lbfsize && (char)_c != '\n')) - return (*_p->_p++ = _c); - else - return (__swbuf(_c, _p)); -} -# 331 "/usr/include/stdio.h" - - - - - - - - - - - - - - - - - - - - - - - -# 4 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2 - -# 1 "/usr/include/ctype.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -extern char _ctype_[]; - - - - - - - - - - - - - - - - - - -# 5 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2 - -# 1 "/usr/include/pwd.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 1 "/usr/include/sys/types.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 135 "/usr/include/sys/types.h" - -# 39 "/usr/include/pwd.h" 2 - - - - - - - - - - - - - - - - - - - - -struct passwd { - char *pw_name; - char *pw_passwd; - int pw_uid; - int pw_gid; - time_t pw_change; - char *pw_class; - char *pw_gecos; - char *pw_dir; - char *pw_shell; - time_t pw_expire; -}; - -# 1 "/usr/include/sys/cdefs.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 78 "/usr/include/sys/cdefs.h" - -# 72 "/usr/include/pwd.h" 2 - - - -struct passwd *getpwuid (uid_t) ; -struct passwd *getpwnam (const char *) ; - -struct passwd *getpwent (void) ; -int setpassent (int) ; -int setpwent (void) ; -void endpwent (void) ; - - - - -# 6 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2 - -# 1 "/usr/include/signal.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 222 "/usr/include/signal.h" - -# 7 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2 - -# 1 "/usr/src/gnu/cvs/mkmodules/../cvs/hash.h" 1 - - - - - - - - - - - - - - - - - - -enum ntype -{ - UNKNOWN, HEADER, ENTRIES, FILES, LIST, RCSNODE, - RCSVERS, DIRS, UPDATE, LOCK, NDBMNODE -}; -typedef enum ntype Ntype; - -struct node -{ - Ntype type; - struct node *next; - struct node *prev; - struct node *hashnext; - struct node *hashprev; - char *key; - char *data; - void (*delproc) (); -}; -typedef struct node Node; - -struct list -{ - Node *list; - Node *hasharray[151 ]; - struct list *next; -}; -typedef struct list List; - -struct entnode -{ - char *version; - char *timestamp; - char *options; - char *tag; - char *date; -}; -typedef struct entnode Entnode; - - -List *getlist (void); -Node *findnode (List * list, char *key); -Node *getnode (void); -int addnode (List * list, Node * p); -int walklist (List * list, int (*proc) ()); -void dellist (List ** listp); -void delnode (Node * p); -void freenode (Node * p); -void sortlist (List * list, int (*comp) ()); -# 77 "/usr/src/gnu/cvs/mkmodules/../cvs/hash.h" - -# 8 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2 - -# 1 "/usr/src/gnu/cvs/mkmodules/../cvs/rcs.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -struct rcsnode -{ - int refcount; - int flags; - char *path; - char *head; - char *branch; - List *symbols; - List *versions; - List *dates; -}; -typedef struct rcsnode RCSNode; - -struct rcsversnode -{ - char *version; - char *date; - char *next; - List *branches; -}; -typedef struct rcsversnode RCSVers; - - - - - - - - - - - - - - -List *RCS_parsefiles (List * files, char *xrepos); -RCSNode *RCS_parse (char *file, char *repos); -RCSNode *RCS_parsercsfile (char *rcsfile); -char *RCS_check_kflag (char *arg); -char *RCS_getdate (RCSNode * rcs, char *date, int force_tag_match); -char *RCS_gettag (RCSNode * rcs, char *tag, int force_tag_match); -char *RCS_getversion (RCSNode * rcs, char *tag, char *date, - int force_tag_match); -char *RCS_magicrev (RCSNode *rcs, char *rev); -int RCS_isbranch (char *file, char *rev, List *srcfiles); -char *RCS_whatbranch (char *file, char *tag, List *srcfiles); -char *RCS_head (RCSNode * rcs); -int RCS_datecmp (char *date1, char *date2); -time_t RCS_getrevtime (RCSNode * rcs, char *rev, char *date, int fudge); -void RCS_check_tag (char *tag); -void freercsnode (RCSNode ** rnodep); -# 102 "/usr/src/gnu/cvs/mkmodules/../cvs/rcs.h" - -# 9 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2 - -# 1 "/usr/src/gnu/cvs/mkmodules/../lib/regex.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -typedef unsigned reg_syntax_t; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -extern reg_syntax_t obscure_syntax; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -typedef enum -{ - REG_NOERROR = 0, - REG_NOMATCH, - - - - REG_BADPAT, - REG_ECOLLATE, - REG_ECTYPE, - REG_EESCAPE, - REG_ESUBREG, - REG_EBRACK, - REG_EPAREN, - REG_EBRACE, - REG_BADBR, - REG_ERANGE, - REG_ESPACE, - REG_BADRPT, - - - REG_EEND, - REG_ESIZE, - REG_ERPAREN -} reg_errcode_t; - - - - - - - - - - -struct re_pattern_buffer -{ - - - - - unsigned char *buffer; - - - unsigned long allocated; - - - unsigned long used; - - - reg_syntax_t syntax; - - - - - char *fastmap; - - - - - - char *translate; - - - size_t re_nsub; - - - - - - - unsigned can_be_null : 2; - - - - unsigned fastmap_accurate : 1; - - - - unsigned no_sub : 1; - - - - unsigned not_bol : 1; - - - unsigned not_eol : 1; - - - unsigned newline_anchor : 1; - - - - - unsigned caller_allocated_regs : 1; - -}; - -typedef struct re_pattern_buffer regex_t; - - - - - - - - - - - -typedef int regoff_t; - - - - -struct re_registers -{ - unsigned num_regs; - regoff_t *start; - regoff_t *end; -}; - - - - - - - - - - - - -typedef struct -{ - regoff_t rm_so; - regoff_t rm_eo; -} regmatch_t; - - - - - - - - - - -extern reg_syntax_t re_set_syntax (reg_syntax_t syntax); - - - - -extern const char *re_compile_pattern (const char *pattern, int length, - struct re_pattern_buffer *buffer); - - - - - -extern int re_compile_fastmap (struct re_pattern_buffer *buffer); - - - - - - - -extern int re_search (struct re_pattern_buffer *buffer, - const char *string, int length, - int start, int range, - struct re_registers *regs); - - - - -extern int re_search_2 (struct re_pattern_buffer *buffer, - const char *string1, int length1, - const char *string2, int length2, - int start, int range, - struct re_registers *regs, - int stop); - - - - -extern int re_match (const struct re_pattern_buffer *buffer, - const char *string, int length, - int start, struct re_registers *regs); - - - -extern int re_match_2 (const struct re_pattern_buffer *buffer, - const char *string1, int length1, - const char *string2, int length2, - int start, - struct re_registers *regs, - int stop); - - - - - - - - - - - -extern int regcomp (regex_t *preg, const char *pattern, int cflags); -extern int regexec (const regex_t *preg, const char *string, size_t nmatch, - regmatch_t pmatch[], int eflags); -extern size_t regerror (int errcode, const regex_t *preg, char *errbuf, - size_t errbuf_size); -extern void regfree (regex_t *preg); - -# 468 "/usr/src/gnu/cvs/mkmodules/../lib/regex.h" - - - - - - - - - - - - -# 10 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2 - -# 1 "/usr/src/gnu/cvs/mkmodules/../lib/fnmatch.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -extern int fnmatch (const char *pattern, const char *string, int flags); - - - - - -# 11 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2 - -# 1 "/usr/src/gnu/cvs/mkmodules/../lib/getopt.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - -extern char *optarg; - - - - - - - - - - - - - -extern int optind; - - - - -extern int opterr; - - - - - - - - - - - - - - - - - - - - - -struct option -{ - char *name; - int has_arg; - int *flag; - int val; -}; - - -extern const struct option *_getopt_long_options; - - - - - - - -extern int _getopt_long_only; - - - - - -extern int option_index; - - -int gnu_getopt (int argc, char **argv, const char *shortopts); -int gnu_getopt_long (int argc, char **argv, const char *shortopts, - const struct option *longopts, int *longind); -int gnu_getopt_long_only (int argc, char **argv, const char *shortopts, - const struct option *longopts, int *longind); - - - - - -# 12 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2 - -# 1 "/usr/src/gnu/cvs/mkmodules/../lib/wait.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# 13 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2 - -# 1 "/usr/src/gnu/cvs/mkmodules/../cvs/config.h" 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -extern void exit (); - - - -extern char *getwd (); - - - - - - - - - - - - - - - -# 14 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2 - - -# 1 "/usr/src/gnu/cvs/mkmodules/../cvs/myndbm.h" 1 - - - - - - -typedef struct -{ - List *dbm_list; - Node *dbm_next; -} DBM; - -typedef struct -{ - char *dptr; - int dsize; -} datum; - - - - - - - - - - - - - -DBM *mydbm_open (char *file, int flags, int mode); -void mydbm_close (DBM * db); -datum mydbm_fetch (DBM * db, datum key); -datum mydbm_firstkey (DBM * db); -datum mydbm_nextkey (DBM * db); - - - - - - - - - -# 16 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -enum mtype -{ - CHECKOUT, TAG, PATCH -}; - - - - - - -enum classify_type -{ - T_UNKNOWN = 1, - T_CONFLICT, - T_NEEDS_MERGE, - T_MODIFIED, - T_CHECKOUT, - T_ADDED, - T_REMOVED, - T_REMOVE_ENTRY, - T_UPTODATE, - T_TITLE -}; -typedef enum classify_type Ctype; - - - - - - - - -struct vers_ts -{ - char *vn_user; - - - - - - char *vn_rcs; - - char *ts_user; - char *ts_rcs; - char *options; - - char *tag; - char *date; - Entnode *entdata; - RCSNode *srcfile; -}; -typedef struct vers_ts Vers_TS; - - - - - -struct stickydirtag -{ - int aflag; - char *tag; - char *date; - char *options; -}; - - - - - - - - - - - - - - - - -enum direnter_type -{ - R_PROCESS = 1, - R_SKIP_FILES, - R_SKIP_DIRS, - R_SKIP_ALL -}; -typedef enum direnter_type Dtype; - -extern char *program_name, *command_name; -extern char *Rcsbin, *Editor, *CVSroot; -extern char *CurDir; -extern int really_quiet, quiet; -extern int use_editor; -extern int cvswrite; - -extern int trace; -extern int noexec; -extern int logoff; - - - -int Reader_Lock (char *xrepository); -DBM *open_module (void); -FILE *Fopen (char *name, char *mode); -FILE *open_file (char *name, char *mode); -List *Find_Dirs (char *repository, int which); -List *ParseEntries (int aflag); -char *Make_Date (char *rawdate); -char *Name_Repository (char *dir, char *update_dir); -char *Short_Repository (char *repository); -char *getcaller (void); -char *time_stamp (char *file); -char *xmalloc (int bytes); -char *xrealloc (char *ptr, int bytes); -char *xstrdup (char *str); -int No_Difference (char *file, Vers_TS * vers, List * entries); -int Parse_Info (char *infofile, char *repository, int (*callproc) (), int all); -int Reader_Lock (char *xrepository); -int SIG_register (int sig, void (*fn) ()); -int Writer_Lock (List * list); -int gethostname (char *name, int namelen); -int ign_name (char *name); -int isdir (char *file); -int isfile (char *file); -int islink (char *file); -int isreadable (char *file); -int iswritable (char *file); -int link_file (char *from, char *to); -int numdots (char *s); -int run_exec (char *stin, char *stout, char *sterr, int flags); -int unlink_file (char *f); -int update (int argc, char *argv[]); -int xcmp (char *file1, char *file2); -int yesno (void); -time_t get_date (char *date, struct timeb *now); -void Create_Admin (char *dir, char *repository, char *tag, char *date); -void Lock_Cleanup (void); -void ParseTag (char **tagp, char **datep); -void Scratch_Entry (List * list, char *fname); -void WriteTag (char *dir, char *tag, char *date); -void cat_module (int status); -void check_entries (char *dir); -void close_module (DBM * db); -void copy_file (char *from, char *to); -void error (int status, int errnum, char *message,...); -void fperror (FILE * fp, int status, int errnum, char *message,...); -void free_names (int *pargc, char *argv[]); -void freevers_ts (Vers_TS ** versp); -void ign_add (char *ign, int hold); -void ign_add_file (char *file, int hold); -void ign_setup (void); -void line2argv (int *pargc, char *argv[], char *line); -void make_directories (char *name); -void make_directory (char *name); -void rename_file (char *from, char *to); -void run_arg (char *s); -void run_args (char *fmt,...); -void run_print (FILE * fp); -void run_setup (char *fmt,...); -void strip_path (char *path); -void update_delproc (Node * p); -void usage (char **cpp); -void xchmod (char *fname, int writable); -int Checkin (int type, char *file, char *repository, char *rcs, char *rev, - char *tag, char *message, List * entries); -Ctype Classify_File (char *file, char *tag, char *date, char *options, - int force_tag_match, int aflag, char *repository, - List *entries, List *srcfiles, Vers_TS **versp); -List *Find_Names (char *repository, int which, int aflag, - List ** optentries); -void Register (List * list, char *fname, char *vn, char *ts, - char *options, char *tag, char *date); -void Update_Logfile (char *repository, char *xmessage, char *xrevision, - FILE * xlogfp, List * xchanges); -Vers_TS *Version_TS (char *repository, char *options, char *tag, - char *date, char *user, int force_tag_match, - int set_time, List * entries, List * xfiles); -void do_editor (char *dir, char *message, char *repository, - List * changes); -int do_module (DBM * db, char *mname, enum mtype m_type, char *msg, - int (*callback_proc) (), char *where, int shorten, - int local_specified, int run_module_prog, char *extra_arg); -int do_recursion (int (*xfileproc) (), int (*xfilesdoneproc) (), - Dtype (*xdirentproc) (), int (*xdirleaveproc) (), - Dtype xflags, int xwhich, int xaflag, int xreadlock, - int xdosrcs); -int do_update (int argc, char *argv[], char *xoptions, char *xtag, - char *xdate, int xforce, int local, int xbuild, - int xaflag, int xprune, int xpipeout, int which, - char *xjoin_rev1, char *xjoin_rev2, char *preload_update_dir); -void history_write (int type, char *update_dir, char *revs, char *name, - char *repository); -int start_recursion (int (*fileproc) (), int (*filesdoneproc) (), - Dtype (*direntproc) (), int (*dirleaveproc) (), - int argc, char *argv[], int local, int which, - int aflag, int readlock, char *update_preload, - int dosrcs); -void SIG_beginCrSect (); -void SIG_endCrSect (); -# 438 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" - -# 15 "/usr/src/gnu/cvs/mkmodules/mkmodules.c" 2 - - - - - - -PATH_MAX +2 - - -static char rcsid[] = "@(#)mkmodules.c 1.39 92/03/31"; - - - - - - -char *program_name, *command_name; - -char *Rcsbin = "" ; -int noexec = 0; -int trace = 0; - - -static int checkout_file (char *file, char *temp); -static void make_tempfile (char *temp); -static void mkmodules_usage (void); -static void rename_rcsfile (char *temp, char *real); - - - - - - -# 60 "/usr/src/gnu/cvs/mkmodules/mkmodules.c" - - -int -main (argc, argv) - int argc; - char *argv[]; -{ - extern char *getenv (); - char temp[PATH_MAX +2 ]; - char *cp; - - DBM *db; - - - - - - if ((program_name = rindex (argv[0], '/')) == 0 ) - program_name = argv[0]; - else - program_name++; - - if (argc != 2) - mkmodules_usage (); - - if ((cp = getenv ("RCSBIN" )) != 0 ) - Rcsbin = cp; - - - - - - if (Rcsbin[0] != '\0') - { - int len = strlen (Rcsbin); - char *rcsbin; - - if (Rcsbin[len - 1] != '/') - { - rcsbin = Rcsbin; - Rcsbin = xmalloc (len + 2); - (void) strcpy (Rcsbin, rcsbin); - (void) strcat (Rcsbin, "/"); - } - } - - if (chdir (argv[1]) < 0) - error (1, errno, "cannot chdir to %s", argv[1]); - - - - - make_tempfile (temp); - switch (checkout_file ("modules" , temp)) - { - - case 0: - - - if ((db = mydbm_open (temp, 0x0000 , 0666)) != 0 ) - mydbm_close (db); - - - - - rename_rcsfile (temp, "modules" ); - break; - - case -1: - (void) unlink_file (temp); - exit (1); - - - default: - error (0, 0, - "'cvs checkout' is less functional without a %s file", - "modules" ); - break; - } - - (void) unlink_file (temp); - - - - - - make_tempfile (temp); - if (checkout_file ("loginfo" , temp) == 0) - rename_rcsfile (temp, "loginfo" ); - else - error (0, 0, - "no logging of 'cvs commit' messages is done without a %s file", - "loginfo" ); - (void) unlink_file (temp); - - - - - - make_tempfile (temp); - if (checkout_file ("rcsinfo" , temp) == 0) - rename_rcsfile (temp, "rcsinfo" ); - else - error (0, 0, - "a %s file can be used to configure 'cvs commit' templates", - "rcsinfo" ); - (void) unlink_file (temp); - - - - - - make_tempfile (temp); - if (checkout_file ("editinfo" , temp) == 0) - rename_rcsfile (temp, "editinfo" ); - else - error (0, 0, - "a %s file can be used to validate log messages", - "editinfo" ); - (void) unlink_file (temp); - - - - - - make_tempfile (temp); - if (checkout_file ("commitinfo" , temp) == 0) - rename_rcsfile (temp, "commitinfo" ); - else - error (0, 0, - "a %s file can be used to configure 'cvs commit' checking", - "commitinfo" ); - (void) unlink_file (temp); - return (0); -} - - - - -static void -make_tempfile (temp) - char *temp; -{ - static int seed = 0; - int fd; - - if (seed == 0) - seed = getpid (); - while (1) - { - (void) sprintf (temp, "%s%d", ".#" , seed++); - if ((fd = open (temp, 0x0200 | 0x0800 | 0x0002 , 0666)) != -1) - break; - if (errno != 17 ) - error (1, errno, "cannot create temporary file %s", temp); - } - if (close(fd) < 0) - error(1, errno, "cannot close temporary file %s", temp); -} - -static int -checkout_file (file, temp) - char *file; - char *temp; -{ - char rcs[PATH_MAX +2 ]; - int retcode = 0; - - (void) sprintf (rcs, "%s%s", file, ",v" ); - if (!isfile (rcs)) - return (1); - run_setup ("%s%s -q -p", Rcsbin, "co" ); - run_arg (rcs); - if ((retcode = run_exec ( (char *)0 , temp, (char *)0 , 0x0000 )) != 0) - { - error (0, retcode == -1 ? errno : 0, "failed to check out %s file", file); - } - return (retcode); -} - -# 369 "/usr/src/gnu/cvs/mkmodules/mkmodules.c" - - -static void -rename_rcsfile (temp, real) - char *temp; - char *real; -{ - char bak[50]; - - if (chmod (temp, 0444) < 0) - error (0, errno, "warning: cannot chmod %s", temp); - (void) sprintf (bak, "%s%s", ".#" , real); - (void) unlink_file (bak); - (void) rename (real, bak); - (void) rename (temp, real); -} - - - - -void -Lock_Cleanup () -{ -} - -static void -mkmodules_usage () -{ - (void) fprintf ((&__sF[2]) , "Usage: %s modules-directory\n", program_name); - exit (1); -} diff --git a/gnu/usr.bin/dc/Makefile b/gnu/usr.bin/dc/Makefile index d786bc8054..937c7d9f2d 100644 --- a/gnu/usr.bin/dc/Makefile +++ b/gnu/usr.bin/dc/Makefile @@ -1,6 +1,6 @@ PROG= dc -CFLAGS+=-I${.CURDIR} -DHAVE_BCOPY=1 -DHAVE_BZERO=1 SRCS= dc.c decimal.c +CFLAGS+=-I${.CURDIR} -DHAVE_BCOPY=1 -DHAVE_BZERO=1 DPADD= ${LIBM} LDADD= -lm diff --git a/gnu/usr.bin/dc/dc.1 b/gnu/usr.bin/dc/dc.1 new file mode 100644 index 0000000000..17d9356a8f --- /dev/null +++ b/gnu/usr.bin/dc/dc.1 @@ -0,0 +1,278 @@ +.TH DC 1 "03 Aug 1993" "GNU Project" +.SH NAME +dc, An Arbitrary Precision Calculator +.SH SYNOPSIS +.B dc +.SH DESCRIPTION +.PP +DC is a reverse-polish desk calculator which supports unlimited +precision arithmetic. It also allows you to define and call macros. +Normally DC reads from the standard input; if any command arguments +are given to it, they are filenames, and DC reads and executes the +contents of the files before reading from standard input. All output +is to standard output. + +A reverse-polish calculator stores numbers on a stack. Entering a +number pushes it on the stack. Arithmetic operations pop arguments off +the stack and push the results. + +To enter a number in DC, type the digits, with an optional decimal +point. Exponential notation is not supported. To enter a negative +number, begin the number with `_'. `-' cannot be used for this, as it +is a binary operator for subtraction instead. To enter two numbers in +succession, separate them with spaces or newlines. These have no +meaning as commands. +.PD +.SH "Printing Commands" +.PP +.B p +Prints the value on the top of the stack, +without altering the stack. A newline is printed +after the value. +.PP +.B P +Prints the value on the top of the stack, +popping it off, and does not print a newline after. +.PP +.B f +Prints the entire contents of the stack +and the contents of all of the registers, +without altering anything. This is a good command +to use if you are lost or want to figure out +what the effect of some command has been. +.PD +.SH "Arithmetic" +.PP +.B + +Pops two values off the stack, adds them, +and pushes the result. The precision of the result +is determined only by the values of the arguments, +and is enough to be exact. +.PP +.B - +Pops two values, subtracts the first one popped +from the second one popped, and pushes the result. +.PP +.B * +Pops two values, multiplies them, and pushes the result. +The number of fraction digits in the result is controlled +by the current precision flag (see below) and does not +depend on the values being multiplied. +.PP +.B / +Pops two values, divides the second one popped from +the first one popped, and pushes the result. +The number of fraction digits is specified by the precision flag. +.PP +.B % +Pops two values, computes the remainder of the division +that the \fB/\fR command would do, and pushes that. +The division is done with as many fraction digits +as the precision flag specifies, and the remainder +is also computed with that many fraction digits. +.PP +.B ^ +Pops two values and exponentiates, using the first +value popped as the exponent and the second popped as the base. +The fraction part of the exponent is ignored. +The precision flag specifies the number of fraction +digits in the result. +.PP +.B v +Pops one value, computes its square root, and pushes that. +The precision flag specifies the number of fraction digits +in the result. +.PP +Most arithmetic operations are affected by the "precision flag", +which you can set with the +.BR k +command. The default precision +value is zero, which means that all arithmetic except for +addition and subtraction produces integer results. +.PP +The remainder operation +.BR % +requires some explanation: applied to +arguments `a' and `b' it produces `a - (b * (a / b))', +where `a / b' is computed in the current precision. +.PP +.SH "Stack Control" +.PP +.B c +Clears the stack, rendering it empty. +.PP +.B d +Duplicates the value on the top of the stack, +pushing another copy of it. Thus, +`4d*p' computes 4 squared and prints it. +.SH "Registers" +.PP +DC provides 128 memory registers, each named by a single +ASCII character. You can store a number in a register +and retrieve it later. +.PP +.B s\fIr\fR +Pop the value off the top of the stack and store +it into register \fIr\fR. +.PP +.B l\fIr\fR +Copy the value in register \fIr\fR and push it onto the stack. This +does not alter the contents of \fIr\fR. +.PP +Each register also contains its own stack. The current +register value is the top of the register's stack. +.PP +.B S\fIr\fR +Pop the value off the top of the (main) stack and +push it onto the stack of register \fIr\fR. +The previous value of the register becomes inaccessible. +.PP +.B L\fIr\fR +Pop the value off the top of register \fIr\fR's stack +and push it onto the main stack. The previous value +in register \fIr\fR's stack, if any, is now accessible +via the +.BR Ir +command. +.PP +The +.BR f +command prints a list of all registers that have contents +stored in them, together with their contents. Only the +current contents of each register (the top of its stack) +is printed. +.PP +.SH "Parameters" +.PP +DC has three parameters that control its operation: the precision, the +input radix, and the output radix. The precision specifies the number +of fraction digits to keep in the result of most arithmetic operations. +The input radix controls the interpretation of numbers typed in; +allnumbers typed in use this radix. The output radix is used +for printing numbers. +.PP +The input and output radices are separate parameters; you can make them +unequal, which can be useful or confusing. Each radix must be between 2 +and 36 inclusive. The precision must be zero or greater. The precision +is always measured in decimal digits, regardless of the current input or +output radix. +.PP +.B i +Pops the value off the top of the stack +and uses it to set the input radix. +.PP +.B o +.PP +.B k +Similarly set the output radix and the precision. +.PP +.B I +Pushes the current input radix on the stack. +.PP +.B O +.PP +.B K +Similarly push the current output radix and the current precision. +.PP +.SH "Strings" +.PP +DC can operate on strings as well as on numbers. The only things you +can do with strings are print them and execute them as macros (which +means that the contents of the string are processed as DC commands). +Both registers and the stack can hold strings, and DC always knows +whether any given object is a string or a number. Some commands such as +arithmetic operations demand numbers as arguments and print errors if +given strings. Other commands can accept either a number or a string; +for example, the +.BR p +command can accept either and prints the object +according to its type. +.PP +.B [characters] +Makes a string containing +.BR characters +and pushes it +on the stack. For example, +.BR [foo]p +prints the +characters \fBfoo\fR (with no newline). +.PP +.B x +Pops a value off the stack and executes it as a macro. +Normally it should be a string; if it is a number, +it is simply pushed back onto the stack. +For example, +.BR [1p]x +executes the macro +.BR 1p +which pushes \fB1\fR on the stack and prints \fB1\fR +on a separate line. +.PP +Macros are most often stored in registers; +\fB[1p]sa\fR stores a macro to print \fB1\fR into register \fBa\fR, +and \fBlax\fR invokes the macro. +.PP +.B >\fIr\fR +Pops two values off the stack and compares them +assuming they are numbers, executing the contents +of register \fIr\fR as a macro if the original top-of-stack +is greater. Thus, \fB1 2>a\fR will invoke register \fBa\fR's contents +and \fB2 1>a\fR will not. +.PP +.B <\fIr\fB +Similar but invokes the macro if the original top-of-stack +is less. +.PP +.B =\fIr\fR +Similar but invokes the macro if the two numbers popped +are equal. This can also be validly used to compare two +strings for equality. +.PP +.B ? +Reads a line from the terminal and executes it. +This command allows a macro to request input from the user. +.PP +.B q +During the execution of a macro, this comand +does not exit DC. Instead, it exits from that +macro and also from the macro which invoked it (if any). +.PP +.B Q +Pops a value off the stack and uses it as a count +of levels of macro execution to be exited. Thus, +\fB3Q\fR exits three levels. +.SH "Status Inquiry" +.PP +.B Z +Pops a value off the stack, calculates the number of +digits it has (or number of characters, if it is a string) +and pushes that number. +.PP +.B X +Pops a value off the stack, calculates the number of +fraction digits it has, and pushes that number. +For a string, the value pushed is -1. +.PP +.B z +Pushes the current stack depth; the number of +objects on the stack before the execution of the \fBz\fR command. +.PP +.B I +Pushes the current value of the input radix. +.PP +.B O +Pushes the current value of the output radix. +.PP +.B K +Pushes the current value of the precision. +.SH "Notes" +.PP +The \fB:\fR and \fB;\fR commands of the Unix DC program are +not supported, as the documentation does not say what they do. +The \fB!\fR command is not supported, but will be supported +as soon as a library for executing a line as a command exists. +.SH BUGS +.PP +Email bug reports to +.BR bug-gnu-utils@prep.ai.mit.edu . +Be sure to include the word ``dc'' somewhere in the ``Subject:'' field. diff --git a/gnu/usr.bin/diff/COPYING b/gnu/usr.bin/diff/COPYING new file mode 100644 index 0000000000..a43ea2126f --- /dev/null +++ b/gnu/usr.bin/diff/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/gnu/usr.bin/diff3/diff3.c b/gnu/usr.bin/diff/diff3.c similarity index 100% rename from gnu/usr.bin/diff3/diff3.c rename to gnu/usr.bin/diff/diff3.c diff --git a/gnu/usr.bin/diff/fnmatch.h b/gnu/usr.bin/diff/fnmatch.h deleted file mode 100644 index 55cb17cdab..0000000000 --- a/gnu/usr.bin/diff/fnmatch.h +++ /dev/null @@ -1,62 +0,0 @@ -/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public License as -published by the Free Software Foundation; either version 2 of the -License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this library; see the file COPYING.LIB. If -not, write to the Free Software Foundation, Inc., 675 Mass Ave, -Cambridge, MA 02139, USA. */ - -#ifndef _FNMATCH_H - -#define _FNMATCH_H 1 - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined (__cplusplus) || (defined (__STDC__) && __STDC__) -#undef __P -#define __P(args) args -#else /* Not C++ or ANSI C. */ -#undef __P -#define __P(args) () -/* We can get away without defining `const' here only because in this file - it is used only inside the prototype for `fnmatch', which is elided in - non-ANSI C where `const' is problematical. */ -#endif /* C++ or ANSI C. */ - -/* Bits set in the FLAGS argument to `fnmatch'. */ -#ifndef FNM_PATHNAME -#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */ -#endif -#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */ -#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */ - -#if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_GNU_SOURCE) -#define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */ -#define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */ -#define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */ -#endif - -/* Value returned by `fnmatch' if STRING does not match PATTERN. */ -#define FNM_NOMATCH 1 - -/* Match STRING against the filename pattern PATTERN, - returning zero if it matches, FNM_NOMATCH if not. */ -extern int fnmatch __P ((const char *__pattern, const char *__string, - int __flags)); - -#ifdef __cplusplus -} -#endif - -#endif /* fnmatch.h */ diff --git a/gnu/usr.bin/diff/sdiff.c b/gnu/usr.bin/diff/sdiff.c new file mode 100644 index 0000000000..7f1019e530 --- /dev/null +++ b/gnu/usr.bin/diff/sdiff.c @@ -0,0 +1,1067 @@ +/* SDIFF -- interactive merge front end to diff + Copyright (C) 1992 Free Software Foundation, Inc. + +This file is part of GNU DIFF. + +GNU DIFF is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU DIFF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU DIFF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* GNU SDIFF was written by Thomas Lord. */ + +#include +#include +#include "system.h" +#include +#include "getopt.h" + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +/* Size of chunks read from files which must be parsed into lines. */ +#define SDIFF_BUFSIZE 65536 + +/* Default name of the diff program */ +#ifndef DIFF_PROGRAM +#define DIFF_PROGRAM "/usr/bin/diff" +#endif + +/* Users' editor of nonchoice */ +#ifndef DEFAULT_EDITOR +#define DEFAULT_EDITOR "ed" +#endif + +extern char *version_string; +static char const *prog; +static char const *diffbin = DIFF_PROGRAM; +static char const *edbin = DEFAULT_EDITOR; + +static char *tmpname; +static int volatile tmpmade; +static pid_t volatile diffpid; + +struct line_filter; +static void diffarg (); /* (char *); */ +static void execdiff (); /* (int, char const *, char const *, char const *); */ +static int edit (); /* (struct line_filter *left, int lenl, struct + line_filter *right, int lenr, FILE *outfile); */ +static int interact (); /* (struct line_filter *diff, + struct line_filter *left, + struct line_filter *right, FILE *outfile); */ +static void trapsigs (); /* (void); */ +/* this lossage until the gnu libc conquers the universe */ +#define TMPNAMSIZE 1024 +#define PVT_tmpdir "/tmp" +static char *private_tempnam (); /* (const char *, const char *, int, int *); */ +static int diraccess (); + +/* Options: */ + +/* name of output file if -o spec'd */ +static char *out_file; + +/* do not print common lines if true, set by -s option */ +static int suppress_common_flag; + +static struct option longopts[] = +{ + {"ignore-blank-lines", 0, NULL, 'B'}, + {"speed-large-files", 0, NULL, 'H'}, + {"ignore-matching-lines", 1, NULL, 'I'}, + {"ignore-all-space", 0, NULL, 'W'}, /* swap W and w for historical reasons */ + {"text", 0, NULL, 'a'}, + {"ignore-space-change", 0, NULL, 'b'}, + {"minimal", 0, NULL, 'd'}, + {"ignore-case", 0, NULL, 'i'}, + {"left-column", 0, NULL, 'l'}, + {"output", 1, NULL, 'o'}, + {"suppress-common-lines", 0, NULL, 's'}, + {"expand-tabs", 0, NULL, 't'}, + {"width", 1, NULL, 'w'}, + {"version", 0, NULL, 'v'}, + {NULL, 0, NULL, 0} +}; + +/* prints usage message and quits */ +static void +usage () +{ + fprintf (stderr, "Usage: %s [options] from-file to-file\n", prog); + fprintf (stderr, "Options:\n\ + [-abBdHilstv] [-I regexp] [-o outfile] [-w columns]\n\ + [--text] [--minimal] [--speed-large-files] [--expand-tabs]\n\ + [--ignore-case] [--ignore-matching-lines=regexp]\n\ + [--ignore-space-change] [--ignore-blank-lines] [--ignore-all-space]\n\ + [--suppress-common-lines] [--left-column] [--output=outfile]\n\ + [--version] [--width=columns]\n"); + exit (2); +} + +static void +cleanup () +{ + if (0 < diffpid) + kill (diffpid, SIGPIPE); + if (tmpmade) + unlink (tmpname); +} + +static void +exiterr () +{ + cleanup (); + exit (2); +} + +static void +fatal (msg) + char *msg; +{ + fprintf (stderr, "%s: %s\n", prog, msg); + exiterr (); +} + +static void +perror_fatal (msg) + char *msg; +{ + int e = errno; + fprintf (stderr, "%s: ", prog); + errno = e; + perror (msg); + exiterr (); +} + + +/* malloc freely or DIE! */ +char * +xmalloc (size) + size_t size; +{ + char *r = malloc (size); + if (!r) + fatal ("virtual memory exhausted"); + return r; +} + +static FILE * +ck_fopen (fname, type) + char *fname, *type; +{ + FILE *r = fopen (fname, type); + if (!r) + perror_fatal (fname); + return r; +} + + +static FILE * +ck_fdopen (fd, type) + int fd; + char *type; +{ + FILE *r = fdopen (fd, type); + if (!r) + perror_fatal ("fdopen"); + return r; +} + +static void +ck_fclose (f) + FILE *f; +{ + if (fclose (f)) + perror_fatal ("input/output error"); +} + +static size_t +ck_fread (buf, size, f) + char *buf; + size_t size; + FILE *f; +{ + size_t r = fread (buf, sizeof (char), size, f); + if (r == 0 && ferror (f)) + perror_fatal ("input error"); + return r; +} + +static void +ck_fwrite (buf, size, f) + char *buf; + size_t size; + FILE *f; +{ + if (fwrite (buf, sizeof (char), size, f) != size) + perror_fatal ("output error"); +} + +static void +ck_fflush (f) + FILE *f; +{ + if (fflush (f) != 0) + perror_fatal ("output error"); +} + +#if !HAVE_MEMCHR +char * +memchr (s, c, n) + char *s; + int c; + size_t n; +{ + unsigned char *p = (unsigned char *) s, *lim = p + n; + for (; p < lim; p++) + if (*p == c) + return (char *) p; + return 0; +} +#endif + +#ifndef HAVE_WAITPID +/* Emulate waitpid well enough for sdiff, which has at most two children. */ +static pid_t +waitpid (pid, stat_loc, options) + pid_t pid; + int *stat_loc; + int options; +{ + static int ostatus; + static pid_t opid; + int npid, status; + + if (pid == opid) + { + opid = 0; + status = ostatus; + } + else + while ((npid = wait (&status)) != pid) + { + if (npid < 0) + return npid; + opid = npid; + ostatus = status; + } + *stat_loc = status; + return pid; +} +#endif + +static char const * +expand_name (name, isdir, other_name) + char *name; + int isdir; + char const *other_name; +{ + if (strcmp (name, "-") == 0) + fatal ("cannot interactively merge standard input"); + if (!isdir) + return name; + else + { + /* Yield NAME/BASE, where BASE is OTHER_NAME's basename. */ + const char + *p = rindex (other_name, '/'), + *base = p ? p+1 : other_name; + size_t namelen = strlen (name), baselen = strlen (base); + char *r = xmalloc (namelen + baselen + 2); + bcopy (name, r, namelen); + r[namelen] = '/'; + bcopy (base, r + namelen + 1, baselen + 1); + return r; + } +} + + + +struct line_filter { + FILE *infile; + char *bufpos; + char *buffer; + char *buflim; +}; + +static void +lf_init (lf, infile) + struct line_filter *lf; + FILE *infile; +{ + lf->infile = infile; + lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1); + lf->buflim[0] = '\n'; +} + +/* Fill an exhausted line_filter buffer from its INFILE */ +static size_t +lf_refill (lf) + struct line_filter *lf; +{ + size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile); + lf->bufpos = lf->buffer; + lf->buflim = lf->buffer + s; + lf->buflim[0] = '\n'; + return s; +} + +/* Advance LINES on LF's infile, copying lines to OUTFILE */ +static void +lf_copy (lf, lines, outfile) + struct line_filter *lf; + int lines; + FILE *outfile; +{ + char *start = lf->bufpos; + + while (lines) + { + lf->bufpos = memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos); + if (! lf->bufpos) + { + ck_fwrite (start, lf->buflim - start, outfile); + if (! lf_refill (lf)) + return; + start = lf->bufpos; + } + else + { + --lines; + ++lf->bufpos; + } + } + + ck_fwrite (start, lf->bufpos - start, outfile); +} + +/* Advance LINES on LF's infile without doing output */ +static void +lf_skip (lf, lines) + struct line_filter *lf; + int lines; +{ + while (lines) + { + lf->bufpos = memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos); + if (! lf->bufpos) + { + if (! lf_refill (lf)) + break; + } + else + { + --lines; + ++lf->bufpos; + } + } +} + +/* Snarf a line into a buffer. Return EOF if EOF, 0 if error, 1 if OK. */ +static int +lf_snarf (lf, buffer, bufsize) + struct line_filter *lf; + char *buffer; + size_t bufsize; +{ + char *start = lf->bufpos; + + for (;;) + { + char *next = memchr (start, '\n', lf->buflim + 1 - start); + size_t s = next - start; + if (bufsize <= s) + return 0; + bcopy (start, buffer, s); + if (next < lf->buflim) + { + buffer[s] = 0; + lf->bufpos = next + 1; + return 1; + } + if (! lf_refill (lf)) + return s ? 0 : EOF; + buffer += s; + bufsize -= s; + start = next; + } +} + + + +int +main (argc, argv) + int argc; + char *argv[]; +{ + int opt; + int version_requested = 0; + char *editor = getenv ("EDITOR"); + char *differ = getenv ("DIFF"); + + prog = argv[0]; + if (editor) + edbin = editor; + if (differ) + diffbin = differ; + + diffarg ("diff"); + + /* parse command line args */ + while ((opt=getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, (int *)0)) != EOF) + { + switch (opt) + { + case 'a': + diffarg ("-a"); + break; + + case 'b': + diffarg ("-b"); + break; + + case 'B': + diffarg ("-B"); + break; + + case 'd': + diffarg ("-d"); + break; + + case 'H': + diffarg ("-H"); + break; + + case 'i': + diffarg ("-i"); + break; + + case 'I': + diffarg ("-I"); + diffarg (optarg); + break; + + case 'l': + diffarg ("--left-column"); + break; + + case 'o': + out_file = optarg; + break; + + case 's': + suppress_common_flag = 1; + break; + + case 't': + diffarg ("-t"); + break; + + case 'v': + version_requested = 1; + fprintf (stderr, "GNU sdiff version %s\n", version_string); + ck_fflush (stderr); + break; + + case 'w': + diffarg ("-W"); + diffarg (optarg); + break; + + case 'W': + diffarg ("-w"); + break; + + default: + usage (); + } + } + + /* check: did user just want version message? if so exit. */ + if (version_requested && argc - optind == 0) + exit (0); + + if (argc - optind != 2) + usage (); + + if (! out_file) + /* easy case: diff does everything for us */ + execdiff (suppress_common_flag, "-y", argv[optind], argv[optind + 1]); + else + { + FILE *left, *right, *out, *diffout; + int diff_fds[2]; + int interact_ok; + pid_t pid; + struct line_filter lfilt; + struct line_filter rfilt; + struct line_filter diff_filt; + int leftdir = diraccess (argv[optind]); + int rightdir = diraccess (argv[optind + 1]); + + if (leftdir && rightdir) + fatal ("both files to be compared are directories"); + + left = ck_fopen (expand_name (argv[optind], leftdir, argv[optind + 1]), "r"); + ; + right = ck_fopen (expand_name (argv[optind + 1], rightdir, argv[optind]), "r"); + out = ck_fopen (out_file, "w"); + + if (pipe (diff_fds)) + perror_fatal ("pipe"); + + trapsigs (); + + diffpid = pid = vfork (); + + if (pid == 0) + { + signal (SIGINT, SIG_IGN); /* in case user interrupts editor */ + signal (SIGPIPE, SIG_DFL); + + close (diff_fds[0]); + if (diff_fds[1] != fileno (stdout)) + { + dup2 (diff_fds[1], fileno (stdout)); + close (diff_fds[1]); + } + + execdiff (0, "--sdiff-merge-assist", argv[optind], argv[optind + 1]); + } + + if (pid < 0) + perror_fatal ("fork failed"); + + close (diff_fds[1]); + diffout = ck_fdopen (diff_fds[0], "r"); + + lf_init (&diff_filt, diffout); + lf_init (&lfilt, left); + lf_init (&rfilt, right); + + interact_ok = interact (&diff_filt, &lfilt, &rfilt, out); + + ck_fclose (diffout); + ck_fclose (left); + ck_fclose (right); + ck_fclose (out); + + { + int wstatus; + + if (waitpid (pid, &wstatus, 0) < 0) + perror_fatal ("wait failed"); + diffpid = 0; + + if (tmpmade) + { + unlink (tmpname); + tmpmade = 0; + } + + if (! interact_ok) + exit (2); + + if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2)) + fatal ("Subsidiary diff failed"); + + exit (WEXITSTATUS (wstatus)); + } + } + return 0; /* Fool -Wall . . . */ +} + +static char **diffargv; + +static void +diffarg (a) + char *a; +{ + static unsigned diffargs, diffargsmax; + + if (diffargs == diffargsmax) + { + if (! diffargsmax) + { + diffargv = (char **) xmalloc (sizeof (char)); + diffargsmax = 8; + } + diffargsmax *= 2; + diffargv = (char **) realloc (diffargv, diffargsmax * sizeof (char *)); + if (! diffargv) + fatal ("out of memory"); + } + diffargv[diffargs++] = a; +} + +static void +execdiff (differences_only, option, file1, file2) + int differences_only; + char *option, *file1, *file2; +{ + if (differences_only) + diffarg ("--suppress-common-lines"); + diffarg (option); + diffarg ("--"); + diffarg (file1); + diffarg (file2); + diffarg (0); + + execvp (diffbin, diffargv); + write (fileno (stderr), diffbin, strlen (diffbin)); + write (fileno (stderr), ": not found\n", 12); + _exit (2); +} + + + + +/* Signal handling */ + +static int volatile ignore_signals; + +static void +catchsig (s) + int s; +{ + signal (s, catchsig); + if (! ignore_signals) + { + cleanup (); + _exit (2); + } +} + +static void +trapsigs () +{ + static int const sigs[] = { +# ifdef SIGHUP + SIGHUP, +# endif +# ifdef SIGQUIT + SIGQUIT, +# endif +# ifdef SIGTERM + SIGTERM, +# endif +# ifdef SIGXCPU + SIGXCPU, +# endif +# ifdef SIGXFSZ + SIGXFSZ, +# endif + SIGINT, + SIGPIPE + }; + int const *p; + + for (p = sigs; p < sigs + sizeof (sigs) / sizeof (*sigs); p++) + if (signal (*p, SIG_IGN) != SIG_IGN && signal (*p, catchsig) != SIG_IGN) + fatal ("signal error"); +} + + + +static void +give_help () +{ + fprintf (stderr,"l:\tuse the left version\n"); + fprintf (stderr,"r:\tuse the right version\n"); + fprintf (stderr,"e l:\tedit then use the left version\n"); + fprintf (stderr,"e r:\tedit then use the right version\n"); + fprintf (stderr,"e b:\tedit then use the left and right versions concatenated\n"); + fprintf (stderr,"e:\tedit a new version\n"); + fprintf (stderr,"s:\tsilently include common lines\n"); + fprintf (stderr,"v:\tverbosely include common lines\n"); + fprintf (stderr,"q:\tquit\n"); +} + +static int +skip_white () +{ + int c; + while (isspace (c = getchar ()) && c != '\n') + ; + if (ferror (stdin)) + perror_fatal ("input error"); + return c; +} + +static void +flush_line () +{ + int c; + while ((c = getchar ()) != '\n' && c != EOF) + ; + if (ferror (stdin)) + perror_fatal ("input error"); +} + + +/* interpret an edit command */ +static int +edit (left, lenl, right, lenr, outfile) + struct line_filter *left; + int lenl; + struct line_filter *right; + int lenr; + FILE *outfile; +{ + for (;;) + { + int cmd0, cmd1; + int gotcmd = 0; + + while (!gotcmd) + { + if (putchar ('%') != '%') + perror_fatal ("output error"); + ck_fflush (stdout); + + cmd0 = skip_white (); + switch (cmd0) + { + case 'l': case 'r': case 's': case 'v': case 'q': + if (skip_white () != '\n') + { + give_help (); + flush_line (); + continue; + } + gotcmd = 1; + break; + + case 'e': + cmd1 = skip_white (); + switch (cmd1) + { + case 'l': case 'r': case 'b': + if (skip_white () != '\n') + { + give_help (); + flush_line (); + continue; + } + gotcmd = 1; + break; + case '\n': + gotcmd = 1; + break; + default: + give_help (); + flush_line (); + continue; + } + break; + case EOF: + if (feof (stdin)) + { + gotcmd = 1; + cmd0 = 'q'; + break; + } + /* falls through */ + default: + give_help (); + flush_line (); + continue; + } + } + + switch (cmd0) + { + case 'l': + lf_copy (left, lenl, outfile); + lf_skip (right, lenr); + return 1; + case 'r': + lf_copy (right, lenr, outfile); + lf_skip (left, lenl); + return 1; + case 's': + suppress_common_flag = 1; + break; + case 'v': + suppress_common_flag = 0; + break; + case 'q': + return 0; + case 'e': + if (! tmpname && ! (tmpname = private_tempnam (0, "sdiff", 1, 0))) + perror_fatal ("temporary file name"); + + tmpmade = 1; + + { + FILE *tmp = ck_fopen (tmpname, "w+"); + + if (cmd1 == 'l' || cmd1 == 'b') + lf_copy (left, lenl, tmp); + else + lf_skip (left, lenl); + + if (cmd1 == 'r' || cmd1 == 'b') + lf_copy (right, lenr, tmp); + else + lf_skip (right, lenr); + + ck_fflush (tmp); + + { + pid_t pid; + int wstatus; + + ignore_signals = 1; + + pid = vfork (); + if (pid == 0) + { + char const *argv[3]; + int i = 0; + + argv[i++] = edbin; + argv[i++] = tmpname; + argv[i++] = 0; + + execvp (edbin, (char **) argv); + write (fileno (stderr), edbin, strlen (edbin)); + write (fileno (stderr), ": not found\n", 12); + _exit (1); + } + + if (pid < 0) + perror_fatal ("fork failed"); + + while (waitpid (pid, &wstatus, 0) < 0) + if (errno != EINTR) + perror_fatal ("wait failed"); + + ignore_signals = 0; + + if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 1)) + fatal ("Subsidiary editor failed"); + } + + if (fseek (tmp, 0L, SEEK_SET) != 0) + perror_fatal ("fseek"); + { + /* SDIFF_BUFSIZE is too big for a local var + in some compilers, so we allocate it dynamically. */ + char *buf = (char *) xmalloc (SDIFF_BUFSIZE); + size_t size; + + while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0) + ck_fwrite (buf, size, outfile); + ck_fclose (tmp); + + free (buf); + } + return 1; + } + default: + give_help (); + break; + } + } +} + + + +/* Alternately reveal bursts of diff output and handle user editing comands. */ +static int +interact (diff, left, right, outfile) + struct line_filter *diff; + struct line_filter *left; + struct line_filter *right; + FILE *outfile; +{ + for (;;) + { + char diff_help[256]; + int snarfed = lf_snarf (diff, diff_help, sizeof (diff_help)); + + if (snarfed <= 0) + return snarfed; + + switch (diff_help[0]) + { + case ' ': + puts (diff_help + 1); + break; + case 'i': + { + int lenl = atoi (diff_help + 1), lenr, lenmax; + char *p = index (diff_help, ','); + + if (!p) + fatal (diff_help); + lenr = atoi (p + 1); + lenmax = max (lenl, lenr); + + if (suppress_common_flag) + lf_skip (diff, lenmax); + else + lf_copy (diff, lenmax, stdout); + + lf_copy (left, lenl, outfile); + lf_skip (right, lenr); + break; + } + case 'c': + { + int lenl = atoi (diff_help + 1), lenr; + char *p = index (diff_help, ','); + + if (!p) + fatal (diff_help); + lenr = atoi (p + 1); + lf_copy (diff, max (lenl, lenr), stdout); + if (! edit (left, lenl, right, lenr, outfile)) + return 0; + break; + } + default: + fatal (diff_help); + break; + } + } +} + + + +/* temporary lossage: this is torn from gnu libc */ +/* Return nonzero if DIR is an existent directory. */ +static int +diraccess (dir) + const char *dir; +{ + struct stat buf; + return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode); +} + +/* Return nonzero if FILE exists. */ +static int +exists (file) + const char *file; +{ + struct stat buf; + return stat (file, &buf) == 0; +} + +/* These are the characters used in temporary filenames. */ +static const char letters[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + +/* Generate a temporary filename. + If DIR_SEARCH is nonzero, DIR and PFX are used as + described for tempnam. If not, a temporary filename + in P_tmpdir with no special prefix is generated. If LENPTR + is not NULL, *LENPTR is set the to length (including the + terminating '\0') of the resultant filename, which is returned. + This goes through a cyclic pattern of all possible filenames + consisting of five decimal digits of the current pid and three + of the characters in `letters'. Data for tempnam and tmpnam + is kept separate, but when tempnam is using P_tmpdir and no + prefix (i.e, it is identical to tmpnam), the same data is used. + Each potential filename is tested for an already-existing file of + the same name, and no name of an existing file will be returned. + When the cycle reaches its end (12345ZZZ), NULL is returned. */ + + +static char * +private_tempnam (dir, pfx, dir_search, lenptr) + const char *dir; + const char *pfx; + int dir_search; + size_t *lenptr; +{ + static const char tmpdir[] = PVT_tmpdir; + static struct + { + char buf[3]; + char *s; + size_t i; + } infos[2], *info; + static char buf[TMPNAMSIZE]; + static pid_t oldpid = 0; + pid_t pid = getpid (); + register size_t len, plen; + + if (dir_search) + { + register const char *d = getenv ("TMPDIR"); + if (d != NULL && !diraccess (d)) + d = NULL; + if (d == NULL && dir != NULL && diraccess (dir)) + d = dir; + if (d == NULL && diraccess (tmpdir)) + d = tmpdir; + if (d == NULL && diraccess ("/tmp")) + d = "/tmp"; + if (d == NULL) + { + errno = ENOENT; + return NULL; + } + dir = d; + } + else + dir = tmpdir; + + if (pfx != NULL && *pfx != '\0') + { + plen = strlen (pfx); + if (plen > 5) + plen = 5; + } + else + plen = 0; + + if (dir != tmpdir && !strcmp (dir, tmpdir)) + dir = tmpdir; + info = &infos[(plen == 0 && dir == tmpdir) ? 1 : 0]; + + if (pid != oldpid) + { + oldpid = pid; + info->buf[0] = info->buf[1] = info->buf[2] = '0'; + info->s = &info->buf[0]; + info->i = 0; + } + + len = strlen (dir) + 1 + plen + 8; + for (;;) + { + *info->s = letters[info->i]; + sprintf (buf, "%s/%.*s%.5d%.3s", dir, (int) plen, pfx, + pid % 100000, info->buf); + if (!exists (buf)) + break; + ++info->i; + if (info->i > sizeof (letters) - 1) + { + info->i = 0; + if (info->s == &info->buf[2]) + { + errno = EEXIST; + return NULL; + } + ++info->s; + } + } + + if (lenptr != NULL) + *lenptr = len; + return buf; +} diff --git a/gnu/usr.bin/diff3/Makefile b/gnu/usr.bin/diff3/Makefile index 7510536e03..db54aa58f8 100644 --- a/gnu/usr.bin/diff3/Makefile +++ b/gnu/usr.bin/diff3/Makefile @@ -1,9 +1,11 @@ -PROG= diff3 -SRCS= diff3.c getopt.c getopt1.c version.c -CFLAGS+=-DDIRENT=1 -DHAVE_UNISTD_H=1 -DHAVE_DUP2=1 -DHAVE_MEMCHR=1 \ - -DHAVE_STRERROR=1 -DHAVE_WAITPID=1 -DHAVE_FCNTL_H=1 \ - -DHAVE_STRING_H=1 -DHAVE_SYS_WAIT_H=1 -DHAVE_TIME_H=1 \ - -DHAVE_ST_BLKSIZE=1 -DDIFF_PROGRAM=\"/usr/bin/diff\" -NOMAN=noman +PROG= diff3 +SRCS= diff3.c getopt.c getopt1.c version.c +CFLAGS+= -I$(.CURDIR)/../diff\ + -DDIRENT=1 -DHAVE_UNISTD_H=1 -DHAVE_DUP2=1 -DHAVE_MEMCHR=1\ + -DHAVE_STRERROR=1 -DHAVE_WAITPID=1 -DHAVE_FCNTL_H=1\ + -DHAVE_STRING_H=1 -DHAVE_SYS_WAIT_H=1 -DHAVE_TIME_H=1\ + -DHAVE_ST_BLKSIZE=1 -DDIFF_PROGRAM=\"/usr/bin/diff\" +NOMAN= noman .include +.PATH: $(.CURDIR)/../diff diff --git a/gnu/usr.bin/diff3/getopt.c b/gnu/usr.bin/diff3/getopt.c deleted file mode 100644 index a59a013398..0000000000 --- a/gnu/usr.bin/diff3/getopt.c +++ /dev/null @@ -1,731 +0,0 @@ -/* Getopt for GNU. - NOTE: getopt is now part of the C library, so if you don't know what - "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu - before changing it! - - Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 - Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* NOTE!!! AIX requires this to be the first thing in the file. - Do not put ANYTHING before it! */ -#if !defined (__GNUC__) && defined (_AIX) - #pragma alloca -#endif - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef __GNUC__ -#define alloca __builtin_alloca -#else /* not __GNUC__ */ -#if defined (HAVE_ALLOCA_H) || (defined(sparc) && (defined(sun) || (!defined(USG) && !defined(SVR4) && !defined(__svr4__)))) -#include -#else -#ifndef _AIX -char *alloca (); -#endif -#endif /* alloca.h */ -#endif /* not __GNUC__ */ - -#if !__STDC__ && !defined(const) && IN_GCC -#define const -#endif - -/* This tells Alpha OSF/1 not to define a getopt prototype in . */ -#ifndef _NO_PROTO -#define _NO_PROTO -#endif - -#include - -/* Comment out all this code if we are using the GNU C Library, and are not - actually compiling the library itself. This code is part of the GNU C - Library, but also included in many other GNU distributions. Compiling - and linking in this code is a waste when using the GNU C library - (especially if it is a shared library). Rather than having every GNU - program understand `configure --with-gnu-libc' and omit the object files, - it is simpler to just do this in the source for each such file. */ - -#if defined (_LIBC) || !defined (__GNU_LIBRARY__) - - -/* This needs to come after some library #include - to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ -#undef alloca -/* Don't include stdlib.h for non-GNU C libraries because some of them - contain conflicting prototypes for getopt. */ -#include -#else /* Not GNU C library. */ -#define __alloca alloca -#endif /* GNU C library. */ - -/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a - long-named option. Because this is not POSIX.2 compliant, it is - being phased out. */ -/* #define GETOPT_COMPAT */ - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of ARGV so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. - - Setting the environment variable POSIXLY_CORRECT disables permutation. - Then the behavior is completely standard. - - GNU application programs can use a third alternative mode in which - they can distinguish the relative order of options and other arguments. */ - -#include "getopt.h" - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -char *optarg = 0; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns EOF, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -/* XXX 1003.2 says this must be 1 before any call. */ -int optind = 0; - -/* The next char to be scanned in the option-element - in which the last option character we returned was found. - This allows us to pick up the scan where we left off. - - If this is zero, or a null string, it means resume the scan - by advancing to the next ARGV-element. */ - -static char *nextchar; - -/* Callers store zero here to inhibit the error message - for unrecognized options. */ - -int opterr = 1; - -/* Set to an option character which was unrecognized. - This must be initialized on some systems to avoid linking in the - system's own getopt implementation. */ - -int optopt = '?'; - -/* Describe how to deal with options that follow non-option ARGV-elements. - - If the caller did not specify anything, - the default is REQUIRE_ORDER if the environment variable - POSIXLY_CORRECT is defined, PERMUTE otherwise. - - REQUIRE_ORDER means don't recognize them as options; - stop option processing when the first non-option is seen. - This is what Unix does. - This mode of operation is selected by either setting the environment - variable POSIXLY_CORRECT, or using `+' as the first character - of the list of option characters. - - PERMUTE is the default. We permute the contents of ARGV as we scan, - so that eventually all the non-options are at the end. This allows options - to be given in any order, even with programs that were not written to - expect this. - - RETURN_IN_ORDER is an option available to programs that were written - to expect options and other ARGV-elements in any order and that care about - the ordering of the two. We describe each non-option ARGV-element - as if it were the argument of an option with character code 1. - Using `-' as the first character of the list of option characters - selects this mode of operation. - - The special argument `--' forces an end of option-scanning regardless - of the value of `ordering'. In the case of RETURN_IN_ORDER, only - `--' can cause `getopt' to return EOF with `optind' != ARGC. */ - -static enum -{ - REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER -} ordering; - -#ifdef __GNU_LIBRARY__ -/* We want to avoid inclusion of string.h with non-GNU libraries - because there are many ways it can cause trouble. - On some systems, it contains special magic macros that don't work - in GCC. */ -#include -#define my_index strchr -#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n)) -#else - -/* Avoid depending on library functions or files - whose names are inconsistent. */ - -char *getenv (); - -static char * -my_index (str, chr) - const char *str; - int chr; -{ - while (*str) - { - if (*str == chr) - return (char *) str; - str++; - } - return 0; -} - -static void -my_bcopy (from, to, size) - const char *from; - char *to; - int size; -{ - int i; - for (i = 0; i < size; i++) - to[i] = from[i]; -} -#endif /* GNU C library. */ - -/* Handle permutation of arguments. */ - -/* Describe the part of ARGV that contains non-options that have - been skipped. `first_nonopt' is the index in ARGV of the first of them; - `last_nonopt' is the index after the last of them. */ - -static int first_nonopt; -static int last_nonopt; - -/* Exchange two adjacent subsequences of ARGV. - One subsequence is elements [first_nonopt,last_nonopt) - which contains all the non-options that have been skipped so far. - The other is elements [last_nonopt,optind), which contains all - the options processed since those non-options were skipped. - - `first_nonopt' and `last_nonopt' are relocated so that they describe - the new indices of the non-options in ARGV after they are moved. */ - -static void -exchange (argv) - char **argv; -{ - int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); - char **temp = (char **) __alloca (nonopts_size); - - /* Interchange the two blocks of data in ARGV. */ - - my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size); - my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt], - (optind - last_nonopt) * sizeof (char *)); - my_bcopy ((char *) temp, - (char *) &argv[first_nonopt + optind - last_nonopt], - nonopts_size); - - /* Update records for the slots the non-options now occupy. */ - - first_nonopt += (optind - last_nonopt); - last_nonopt = optind; -} - -/* Scan elements of ARGV (whose length is ARGC) for option characters - given in OPTSTRING. - - If an element of ARGV starts with '-', and is not exactly "-" or "--", - then it is an option element. The characters of this element - (aside from the initial '-') are option characters. If `getopt' - is called repeatedly, it returns successively each of the option characters - from each of the option elements. - - If `getopt' finds another option character, it returns that character, - updating `optind' and `nextchar' so that the next call to `getopt' can - resume the scan with the following option character or ARGV-element. - - If there are no more option characters, `getopt' returns `EOF'. - Then `optind' is the index in ARGV of the first ARGV-element - that is not an option. (The ARGV-elements have been permuted - so that those that are not options now come last.) - - OPTSTRING is a string containing the legitimate option characters. - If an option character is seen that is not listed in OPTSTRING, - return '?' after printing an error message. If you set `opterr' to - zero, the error message is suppressed but we still return '?'. - - If a char in OPTSTRING is followed by a colon, that means it wants an arg, - so the following text in the same ARGV-element, or the text of the following - ARGV-element, is returned in `optarg'. Two colons mean an option that - wants an optional arg; if there is text in the current ARGV-element, - it is returned in `optarg', otherwise `optarg' is set to zero. - - If OPTSTRING starts with `-' or `+', it requests different methods of - handling the non-option ARGV-elements. - See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - - Long-named options begin with `--' instead of `-'. - Their names may be abbreviated as long as the abbreviation is unique - or is an exact match for some defined option. If they have an - argument, it follows the option name in the same ARGV-element, separated - from the option name by a `=', or else the in next ARGV-element. - When `getopt' finds a long-named option, it returns 0 if that option's - `flag' field is nonzero, the value of the option's `val' field - if the `flag' field is zero. - - The elements of ARGV aren't really const, because we permute them. - But we pretend they're const in the prototype to be compatible - with other systems. - - LONGOPTS is a vector of `struct option' terminated by an - element containing a name which is zero. - - LONGIND returns the index in LONGOPT of the long-named option found. - It is only valid when a long-named option has been found by the most - recent call. - - If LONG_ONLY is nonzero, '-' as well as '--' can introduce - long-named options. */ - -int -_getopt_internal (argc, argv, optstring, longopts, longind, long_only) - int argc; - char *const *argv; - const char *optstring; - const struct option *longopts; - int *longind; - int long_only; -{ - int option_index; - - optarg = 0; - - /* Initialize the internal data when the first call is made. - Start processing options with ARGV-element 1 (since ARGV-element 0 - is the program name); the sequence of previously skipped - non-option ARGV-elements is empty. */ - - if (optind == 0) - { - first_nonopt = last_nonopt = optind = 1; - - nextchar = NULL; - - /* Determine how to handle the ordering of options and nonoptions. */ - - if (optstring[0] == '-') - { - ordering = RETURN_IN_ORDER; - ++optstring; - } - else if (optstring[0] == '+') - { - ordering = REQUIRE_ORDER; - ++optstring; - } - else if (getenv ("POSIXLY_CORRECT") != NULL) - ordering = REQUIRE_ORDER; - else - ordering = PERMUTE; - } - - if (nextchar == NULL || *nextchar == '\0') - { - if (ordering == PERMUTE) - { - /* If we have just processed some options following some non-options, - exchange them so that the options come first. */ - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange ((char **) argv); - else if (last_nonopt != optind) - first_nonopt = optind; - - /* Now skip any additional non-options - and extend the range of non-options previously skipped. */ - - while (optind < argc - && (argv[optind][0] != '-' || argv[optind][1] == '\0') -#ifdef GETOPT_COMPAT - && (longopts == NULL - || argv[optind][0] != '+' || argv[optind][1] == '\0') -#endif /* GETOPT_COMPAT */ - ) - optind++; - last_nonopt = optind; - } - - /* Special ARGV-element `--' means premature end of options. - Skip it like a null option, - then exchange with previous non-options as if it were an option, - then skip everything else like a non-option. */ - - if (optind != argc && !strcmp (argv[optind], "--")) - { - optind++; - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange ((char **) argv); - else if (first_nonopt == last_nonopt) - first_nonopt = optind; - last_nonopt = argc; - - optind = argc; - } - - /* If we have done all the ARGV-elements, stop the scan - and back over any non-options that we skipped and permuted. */ - - if (optind == argc) - { - /* Set the next-arg-index to point at the non-options - that we previously skipped, so the caller will digest them. */ - if (first_nonopt != last_nonopt) - optind = first_nonopt; - return EOF; - } - - /* If we have come to a non-option and did not permute it, - either stop the scan or describe it to the caller and pass it by. */ - - if ((argv[optind][0] != '-' || argv[optind][1] == '\0') -#ifdef GETOPT_COMPAT - && (longopts == NULL - || argv[optind][0] != '+' || argv[optind][1] == '\0') -#endif /* GETOPT_COMPAT */ - ) - { - if (ordering == REQUIRE_ORDER) - return EOF; - optarg = argv[optind++]; - return 1; - } - - /* We have found another option-ARGV-element. - Start decoding its characters. */ - - nextchar = (argv[optind] + 1 - + (longopts != NULL && argv[optind][1] == '-')); - } - - if (longopts != NULL - && ((argv[optind][0] == '-' - && (argv[optind][1] == '-' || long_only)) -#ifdef GETOPT_COMPAT - || argv[optind][0] == '+' -#endif /* GETOPT_COMPAT */ - )) - { - const struct option *p; - char *s = nextchar; - int exact = 0; - int ambig = 0; - const struct option *pfound = NULL; - int indfound; - - while (*s && *s != '=') - s++; - - /* Test all options for either exact match or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; - p++, option_index++) - if (!strncmp (p->name, nextchar, s - nextchar)) - { - if (s - nextchar == strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else - /* Second nonexact match found. */ - ambig = 1; - } - - if (ambig && !exact) - { - if (opterr) - fprintf (stderr, "%s: option `%s' is ambiguous\n", - argv[0], argv[optind]); - nextchar += strlen (nextchar); - optind++; - return '?'; - } - - if (pfound != NULL) - { - option_index = indfound; - optind++; - if (*s) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - optarg = s + 1; - else - { - if (opterr) - { - if (argv[optind - 1][1] == '-') - /* --option */ - fprintf (stderr, - "%s: option `--%s' doesn't allow an argument\n", - argv[0], pfound->name); - else - /* +option or -option */ - fprintf (stderr, - "%s: option `%c%s' doesn't allow an argument\n", - argv[0], argv[optind - 1][0], pfound->name); - } - nextchar += strlen (nextchar); - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (optind < argc) - optarg = argv[optind++]; - else - { - if (opterr) - fprintf (stderr, "%s: option `%s' requires an argument\n", - argv[0], argv[optind - 1]); - nextchar += strlen (nextchar); - return optstring[0] == ':' ? ':' : '?'; - } - } - nextchar += strlen (nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - /* Can't find it as a long option. If this is not getopt_long_only, - or the option starts with '--' or is not a valid short - option, then it's an error. - Otherwise interpret it as a short option. */ - if (!long_only || argv[optind][1] == '-' -#ifdef GETOPT_COMPAT - || argv[optind][0] == '+' -#endif /* GETOPT_COMPAT */ - || my_index (optstring, *nextchar) == NULL) - { - if (opterr) - { - if (argv[optind][1] == '-') - /* --option */ - fprintf (stderr, "%s: unrecognized option `--%s'\n", - argv[0], nextchar); - else - /* +option or -option */ - fprintf (stderr, "%s: unrecognized option `%c%s'\n", - argv[0], argv[optind][0], nextchar); - } - nextchar = (char *) ""; - optind++; - return '?'; - } - } - - /* Look at and handle the next option-character. */ - - { - char c = *nextchar++; - char *temp = my_index (optstring, c); - - /* Increment `optind' when we start to process its last character. */ - if (*nextchar == '\0') - ++optind; - - if (temp == NULL || c == ':') - { - if (opterr) - { -#if 0 - if (c < 040 || c >= 0177) - fprintf (stderr, "%s: unrecognized option, character code 0%o\n", - argv[0], c); - else - fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); -#else - /* 1003.2 specifies the format of this message. */ - fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); -#endif - } - optopt = c; - return '?'; - } - if (temp[1] == ':') - { - if (temp[2] == ':') - { - /* This is an option that accepts an argument optionally. */ - if (*nextchar != '\0') - { - optarg = nextchar; - optind++; - } - else - optarg = 0; - nextchar = NULL; - } - else - { - /* This is an option that requires an argument. */ - if (*nextchar != '\0') - { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - optind++; - } - else if (optind == argc) - { - if (opterr) - { -#if 0 - fprintf (stderr, "%s: option `-%c' requires an argument\n", - argv[0], c); -#else - /* 1003.2 specifies the format of this message. */ - fprintf (stderr, "%s: option requires an argument -- %c\n", - argv[0], c); -#endif - } - optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - } - else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - optarg = argv[optind++]; - nextchar = NULL; - } - } - return c; - } -} - -int -getopt (argc, argv, optstring) - int argc; - char *const *argv; - const char *optstring; -{ - return _getopt_internal (argc, argv, optstring, - (const struct option *) 0, - (int *) 0, - 0); -} - -#endif /* _LIBC or not __GNU_LIBRARY__. */ - -#ifdef TEST - -/* Compile with -DTEST to make an executable for use in testing - the above definition of `getopt'. */ - -int -main (argc, argv) - int argc; - char **argv; -{ - int c; - int digit_optind = 0; - - while (1) - { - int this_option_optind = optind ? optind : 1; - - c = getopt (argc, argv, "abc:d:0123456789"); - if (c == EOF) - break; - - switch (c) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 && digit_optind != this_option_optind) - printf ("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf ("option %c\n", c); - break; - - case 'a': - printf ("option a\n"); - break; - - case 'b': - printf ("option b\n"); - break; - - case 'c': - printf ("option c with value `%s'\n", optarg); - break; - - case '?': - break; - - default: - printf ("?? getopt returned character code 0%o ??\n", c); - } - } - - if (optind < argc) - { - printf ("non-option ARGV-elements: "); - while (optind < argc) - printf ("%s ", argv[optind++]); - printf ("\n"); - } - - exit (0); -} - -#endif /* TEST */ diff --git a/gnu/usr.bin/diff3/system.h b/gnu/usr.bin/diff3/system.h deleted file mode 100644 index b17d39a08a..0000000000 --- a/gnu/usr.bin/diff3/system.h +++ /dev/null @@ -1,159 +0,0 @@ -/* System dependent declarations. - Copyright (C) 1988, 1989, 1992, 1993 Free Software Foundation, Inc. - -This file is part of GNU DIFF. - -GNU DIFF is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU DIFF is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU DIFF; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include -#include - -#ifndef S_ISDIR -#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) -#endif -#ifndef S_ISREG -#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) -#endif - -#if HAVE_UNISTD_H -#include -#endif - -#if HAVE_TIME_H -#include -#else -#include -#endif - -#if HAVE_FCNTL_H -#include -#else -#include -#endif - -#if !HAVE_DUP2 -#define dup2(f,t) (close (t), fcntl (f,F_DUPFD,t)) -#endif - -#ifndef O_RDONLY -#define O_RDONLY 0 -#endif - -#if HAVE_SYS_WAIT_H -#ifndef _POSIX_VERSION -/* Prevent the NeXT prototype using union wait from causing problems. */ -#define wait system_wait -#endif -#include -#ifndef _POSIX_VERSION -#undef wait -#endif -#endif /* HAVE_SYS_WAIT_H */ - -#ifndef WEXITSTATUS -#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) -#undef WIFEXITED /* Avoid 4.3BSD incompatibility with Posix. */ -#endif -#ifndef WIFEXITED -#define WIFEXITED(stat_val) (((stat_val) & 255) == 0) -#endif - -#if HAVE_ST_BLKSIZE -#define STAT_BLOCKSIZE(s) (s).st_blksize -#else -#define STAT_BLOCKSIZE(s) (S_ISREG ((s).st_mode) ? 8192 : 4096) -#endif - -#if DIRENT || defined (_POSIX_VERSION) -#include -#ifdef direct -#undef direct -#endif -#define direct dirent -#else /* ! (DIRENT || defined (_POSIX_VERSION)) */ -#if SYSNDIR -#include -#else -#if SYSDIR -#include -#else -#include -#endif -#endif -#endif /* ! (DIRENT || defined (_POSIX_VERSION)) */ - -#if HAVE_VFORK_H -#include -#endif - -#if HAVE_STRING_H || STDC_HEADERS -#include -#ifndef index -#define index strchr -#endif -#ifndef rindex -#define rindex strrchr -#endif -#ifndef bcopy -#define bcopy(s,d,n) memcpy (d,s,n) -#endif -#ifndef bcmp -#define bcmp(s1,s2,n) memcmp (s1,s2,n) -#endif -#ifndef bzero -#define bzero(s,n) memset (s,0,n) -#endif -#else -#include -#endif -#if !HAVE_MEMCHR && !STDC_HEADERS -char *memchr (); -#endif - -#if STDC_HEADERS -#include -#include -#else -char *getenv (); -char *malloc (); -char *realloc (); -#if __STDC__ || __GNUC__ -#include "limits.h" -#else -#define INT_MAX 2147483647 -#define CHAR_BIT 8 -#endif -#endif - -#include -#if !STDC_HEADERS -extern int errno; -#endif - -#ifdef TRUE -#undef TRUE -#endif -#ifdef FALSE -#undef FALSE -#endif -#define TRUE 1 -#define FALSE 0 - -#if !__STDC__ -#define volatile -#endif - -#define min(a,b) ((a) <= (b) ? (a) : (b)) -#define max(a,b) ((a) >= (b) ? (a) : (b)) diff --git a/gnu/usr.bin/diff3/version.c b/gnu/usr.bin/diff3/version.c deleted file mode 100644 index cb9d3b9c45..0000000000 --- a/gnu/usr.bin/diff3/version.c +++ /dev/null @@ -1,3 +0,0 @@ -/* Version number of GNU diff. */ - -char *version_string = "2.3"; diff --git a/gnu/usr.bin/gcc1/Makefile b/gnu/usr.bin/gcc1/Makefile deleted file mode 100644 index ff837db0dd..0000000000 --- a/gnu/usr.bin/gcc1/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# @(#)Makefile 6.1 (Berkeley) 1/29/91 -# -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 1 00119 -# -------------------- ----- ---------------------- -# -# 30 Mar 93 Rodney W. Grimes Added special rule for make depend -# - -SUBDIR= cc cpp cc1 gnulib - -# special top level rule due to symbolic links needed for this to work -# -depend: - cd $(.CURDIR)/cc; make config.h; make depend - cd $(.CURDIR)/cc1; make config.h; make depend - cd $(.CURDIR)/cpp; make config.h; make depend - cd $(.CURDIR)/gnulib; make config.h; make depend - -.include diff --git a/gnu/usr.bin/gcc1/Makefile.symlinks b/gnu/usr.bin/gcc1/Makefile.symlinks deleted file mode 100644 index 5f5e32e5dd..0000000000 --- a/gnu/usr.bin/gcc1/Makefile.symlinks +++ /dev/null @@ -1,24 +0,0 @@ -# @(#)Makefile.symlinks 6.1 (Berkeley) 1/29/91 -# -# Generate machine-dependent symlinks for GCC compiles. -# Similar to config.gcc but (1) puts symlinks in the obj directory and -# (2) knows that it's running on BSD. -# - -tm.h config.h md aux-output.c: - case $(MACHINE) in \ - vax) T=tm-vax.h C=xm-vax.h M=vax.md O=out-vax.c;; \ - tahoe) T=tm-tahoe.h C=xm-tahoe.h M=tahoe.md O=out-tahoe.c;; \ - hp300) T=tm-hp9k3bsd.h C=xm-m68k.h M=m68k.md O=out-m68k.c;; \ - i386) T=tm-i386b.h C=xm-i386.h M=i386.md O=out-i386.c;; \ - *) echo $(MACHINE)': unsupported GCC platform'; exit 1;; \ - esac; \ - D=$(.CURDIR)/../cc1/config; \ - ln -s $$D/$$T tm.h; \ - ln -s $$D/$$C config.h; \ - ln -s $$D/$$M md; \ - ln -s $$D/$$O aux-output.c - -$(OBJS): tm.h config.h md aux-output.c - -CLEANFILES+= tm.h config.h md aux-output.c diff --git a/gnu/usr.bin/gcc1/cc/Makefile b/gnu/usr.bin/gcc1/cc/Makefile deleted file mode 100644 index 154f6e3afe..0000000000 --- a/gnu/usr.bin/gcc1/cc/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# @(#)Makefile 6.2 (Berkeley) 3/25/91 - -PROG= cc -SRCS= cc.c version.c obstack.c -CFLAGS+= -I. -I$(.CURDIR)/../cc1 -I$(.CURDIR)/../cc1/config -.PATH: $(.CURDIR)/../cc1 - -.include "../../Makefile.inc" - -afterinstall: - -rm -f $(DESTDIR)$(BINDIR)/gcc - cd $(DESTDIR)$(BINDIR); ln -s cc gcc - -.include -.include "../Makefile.symlinks" diff --git a/gnu/usr.bin/gcc1/cc/cc.1 b/gnu/usr.bin/gcc1/cc/cc.1 deleted file mode 100644 index 351ad6d444..0000000000 --- a/gnu/usr.bin/gcc1/cc/cc.1 +++ /dev/null @@ -1,1455 +0,0 @@ -.\" -.\" This code is derived from software copyrighted by the Free Software -.\" Foundation. -.\" -.\" Modified 1991 by Donn Seeley at UUNET Technologies, Inc. -.\" -.\" @(#)cc.1 6.5 (Berkeley) 5/9/91 -.\" -.TH CC 1 "May 9, 1991" "Version 1.36" -.de BP -.sp -.ti -.2i -\(** -.. -.SH NAME -cc \- GNU project C Compiler -.SH SYNOPSIS -.B cc -[ options ] files -.SH DESCRIPTION -.I Cc -is a version of the GNU C compiler. -It accepts a dialect of ANSI C with extensions; -this dialect is different from the dialect used -in 4.3 BSD and earlier distributions. -The -.B \-traditional -flag causes the compiler to accept -a dialect of extended Classic C, -much like the C of these earlier distributions. -If you are not already familiar with -ANSI C and its new features, -you will want to build your software with -.BR \-traditional . -.SH DIFFERENCES -Most older C compiler flags are supported by -.IR cc . -Three that are not are: -.BR \-go , -to generate symbol tables for the unsupported -.I sdb -debugger; -.BR \-f , -for single precision floating point -in expressions, -which is now the default; and -.BR \-t , -for alternate compiler passes. -.PP -The differences between ANSI C and Classic C dialects -are too numerous to describe here in detail. -The following quick summary is meant to make -users aware of potential subtle problems when -converting Classic C code to ANSI C. -.PP -The most obvious change is the pervasive use of -.IR "function prototypes" . -Under the ANSI C dialect, the number and type of arguments -to C library functions are checked by the compiler -when standard header files are included; -calls that fail to match will yield errors. -A subtle consequence of adding prototype declarations is that -user code that inadvertently redefines a C library function -may break; for example it is no longer possible to write an -.I abort -function that takes different parameters or returns a different value -from the standard -.IR abort , -when including standard header files. -Another issue with prototypes is that functions -that take different parameter types no longer have the same type; -function pointers now differ by parameter types as well as return types. -Variable argument lists are handled differently; -the old -.IR varargs (3) -package is obsolete, -replaced by -.IR stdarg (3), -which unfortunately is not completely compatible. -A subtle change in type promotion can be confusing: small -unsigned types are now widened into signed types -rather than unsigned types. -A similar problem can occur with the -.B sizeof -operator, which now yields an unsigned type rather than a signed type. -One common problem is due to a change in scoping: external declarations -are now scoped to the block they occur in, -so a declaration for (say) -.B errno -inside one block will no longer -declare it in all subsequent blocks. -The syntax for braces in structure initializations is now a bit stricter, -and it is sometimes necessary to add braces to please the compiler. -Two very subtle and sometimes very annoying features -apply to constant strings and to the -.IR longjmp (3) -function. -Constant strings in the ANSI dialect are read-only; -attempts to alter them cause protection violations. -This ANSI feature permits the compiler to coalesce -identical strings in the same source file, -and saves space when multiple copies of a binary are running -at the same time, since the read-only part of a binary -is sharable. -The most common difficulty with read-only strings -lies with the use of the -.B mktemp -function, which in the past often altered a constant string argument. -It is now necessary to copy a constant string -(for example, with -.IR strdup (3)) -before it may be altered. -The -.B longjmp -function may now destroy any register -.I or -stack variable in the function that made -the corresponding call to the -.B setjmp -function; -to protect a local variable, the new ANSI -.B volatile -modifier must be used. -This often leads to confusing situations -upon `return' from -.BR setjmp . -The compiler has extended warning flags for -dealing with read-only strings and -.BR setjmp , -but these are not very effective. -If your code has problems with any of these ANSI features, -you will probably want to use -.BR \-traditional . -.PP -Even with -.BR \-traditional , -there are some differences between -this dialect of Classic C and the dialect supported on older distributions. -.PP -There are at least two differences that are a consequence -of the fact that -.I cc -uses an ANSI C style grammar for both -traditional and ANSI modes. -The old C dialect permitted a typedef -to replace a simple type in the idiom ``unsigned -.IR type ''; -this -.I cc -treats such forms as syntax errors. -The old C dialect also permitted formal parameters -to have the same names as typedef types; -the current dialect does not. -.PP -Some questionable or illegal practices -that were supported in the old C dialect are -not supported by -.BR \-traditional : -non-comment text at the end of a ``#include'' -preprocessor control line is an error, not ignored; -compound assignment operators must not contain white space, -.I e.g. -``*\0='' is not the same as ``*=''; -the last member declaration in a structure or union -must be terminated by a semicolon; -it is not possible to ``switch'' on function pointers; -more than one occurrence of ``#else'' at the same level -in a preprocessor ``#if'' clause is an error, not ignored. -.PP -Some truly ancient C practices are no longer supported. -The idiom of declaring an anonymous structure and -using its members to extract fields from other structures -or even non-structures is illegal. -Integers are not automatically converted to pointers -when they are dereferenced. -The -.B \-traditional -dialect does not retain the so-called ``old-fashioned'' assignment operators -(with the ``='' preceding rather than following the operator) -or initializations (with no ``='' between initializer and initializee). -.SH WARNING -This rest of man page is an extract of the documentation of the -.I GNU C compiler -and is limited to the meaning of the options. -.BR "It is not kept up to date" . -If you want to be certain of the information -below, check it in the manual "Using and Porting GCC". Refer to the Info file -.B gcc.info -or the DVI file -.B gcc.dvi -which are made from the Texinfo source file -.BR gcc.texinfo . -.PP -The -.I GNU C compiler -uses a command syntax much like the Unix C compiler. -The -.I cc -program accepts options and file names as operands. -Multiple single-letter options may -.I not -be grouped: -.B \-dr -is very different from -.BR "\-d \-r" . -.P -When you invoke GNU CC, it normally does preprocessing, compilation, -assembly and linking. -File names which end in -.B .c -are taken as C source to be preprocessed and compiled; -file names ending in -.B .i -are taken as preprocessor output to be compiled; -compiler output files plus any input files with names ending in -.B .s -are assembled; -then the resulting object files, plus any other input files, -are linked together to produce an executable. -.P -Command options allow you to stop this process at an intermediate stage. -For example, the -.B \-c -option says not to run the linker. -Then the output consists of object files output by the assembler. -.P -Other command options are passed on to one stage of processing. -Some options control the preprocessor and others the compiler itself. -Yet other options control the assembler and linker; -these are not documented here, but you rarely need to use any of them. -.SH OPTIONS -Here are the options to control the overall compilation process, -including those that say whether to link, whether to assemble, and so on. -.TP -.BI \-o " file" -Place output in file -.IR file . -This applies regardless to whatever sort of output is being produced, -whether it be an executable file, an object file, -an assembler file or preprocessed C code. -.sp -If -.B \-o -is not specified, the default is to put an executable file in -.BR a.out , -the object file -.IB source .c -in -.IB source .o\fR, -an assembler file in -.IB source .s\fR, -and preprocessed C on standard output. -.TP -.B \-c -Compile or assemble the source files, but do not link. -Produce object files with names made by replacing -.B .c -or -.B .s -with -.B .o -at the end of the input file names. -Do nothing at all for object files specified as input. -.TP -.B \-S -Compile into assembler code but do not assemble. -The assembler output file name is made by replacing -.B .c -with -.B .s -at the end of the input file name. -Do nothing at all for assembler source files or -object files specified as input. -.TP -.B \-E -Run only the C preprocessor. -Preprocess all the C source files specified and output -the results to standard output. -.TP -.B \-v -Compiler driver program prints the commands it executes as it runs -the preprocessor, compiler proper, assembler and linker. -Some of these are directed to print their own version numbers. -.TP -.B \-pipe -Use pipes rather than temporary files for communication between the -various stages of compilation. -This fails to work on some systems where the assembler is unable -to read from a pipe; but the GNU assembler has no trouble. -.TP -.BI \-B prefix -Compiler driver program tries -.I prefix -as a prefix for each program it tries to run. -These programs are -.IR cpp , -.IR cc1 , -.I as -and -.IR ld . -.sp -For each subprogram to be run, the compiler driver first tries the -.B \-B -prefix, if any. -If that name is not found, or if -.B \-B -was not specified, the driver tries a standard prefix, which currently is -.BR /usr/libexec/ . -If this does not result in a file name that is found, the -unmodified program name is searched for using the directories -specified in your -.B PATH -environment variable. -.sp -You can get a similar result from the environment variable -.BR GCC_EXEC_PREFIX ; -if it is defined, its value is used as a prefix in the same way. -If both the -.B \-B -option and the -.B GCC_EXEC_PREFIX -variable are present, the -.B \-B -option is used first and the environment variable value second. -.TP -.BI -b prefix -The argument -.I prefix -is used as a second prefix for the compiler executables and libraries. -This prefix is optional: the compiler tries each file first with it, -then without it. -This prefix follows the prefix specified with -.B \-B -or the default prefixes. -.sp -Thus, -.B \-bvax- \-Bcc/ -in the presence of environment variable -.B GCC_EXEC_PREFIX -with definition -.B /u/foo/ -causes GNU CC to try the following file names for the preprocessor executable: -.sp - \fBcc/vax-cpp -.br - cc/cpp -.br - /u/foo/vax-cpp -.br - /u/foo/cpp -.br - /usr/libexec/vax-cpp -.br - /usr/libexec/cpp\fR -.P -These options control the details of C compilation itself. -.TP -.B \-ansi -Support all ANSI standard C programs. -.sp -This turns off certain features of GNU C that are incompatible with -ANSI C, such as the -.BR asm , -.B inline -and -.B typeof -keywords, and predefined macros such as -.B unix -and -.B vax -that identify the type of system you are using. -It also enables the undesirable and rarely used ANSI trigraph feature. -.sp -The alternate keywords -.BR __asm__ , -.B __inline__ -and -.B __typeof__ -continue to work despite -.BR \-ansi . -You would not want to use them in an ANSI C program, of course, -but it useful to put them in header files that might be included -in compilations done with -.BR \-ansi . -Alternate predefined macros such as -.B __unix__ -and -.B __vax__ -are also available, with or without -.BR \-ansi . -.sp -The -.B \-ansi -option does not cause non-ANSI programs to be rejected gratuitously. -For that, -.B \-pedantic -is required in addition to -.BR \-ansi . -.sp -The macro -.B __STRICT_ANSI__ -is predefined when the -.B \-ansi -option is used. -Some header files may notice this macro and refrain from declaring -certain functions or defining certain macros that the ANSI standard -doesn't call for; this is to avoid interfering with any programs -that might use these names for other things. -.TP -.B \-traditional -Attempt to support some aspects of traditional C compilers. -Specifically: -.BP -All -.B extern -declarations take effect globally even if they are -written inside of a function definition. -This includes implicit declarations of functions. -.BP -The keywords -.BR typeof , -.BR inline , -.BR signed , -.B const -and -.B volatile -are not recognized. -.BP -Comparisons between pointers and integers are always allowed. -.BP -Integer types -.B "unsigned short" -and -.B "unsigned char" -promote to -.BR "unsigned int" . -.BP -Out-of-range floating point literals are not an error. -.BP -All automatic variables not declared -.B register -are preserved by -.IR longjmp (3C). -Ordinarily, GNU C follows ANSI C: automatic variables not declared -.B volatile -may be clobbered. -.BP -In the preprocessor, comments convert to nothing at all, -rather than to a space. -This allows traditional token concatenation. -.BP -In the preprocessor, macro arguments are recognized within string -constants in a macro definition (and their values are stringified, though -without additional quote marks, when they appear in such a context). -The preprocessor always considers a string constant to end at a newline. -.BP -The predefined macro -.B __STDC__ -is not defined when you use -.BR \-traditional , -but -.B __GNUC__ -is (since the GNU extensions which -.B __GNUC__ -indicates are not affected by -.BR \-traditional ). -If you need to write header files that work differently depending on whether -.B \-traditional -is in use, by testing both of these predefined macros you can distinguish -four situations: GNU C, traditional GNU C, other ANSI C compilers, and -other old C compilers. -.TP -.B \-O -Optimize. -Optimizing compilation takes somewhat more time, -and a lot more memory for a large function. -.sp -Without -.BR \-O , -the compiler's goal is to reduce the cost of compilation and -to make debugging produce the expected results. -Statements are independent: if you stop the program with a breakpoint -between statements, you can then assign a new value to any variable or -change the program counter to any other statement in the function and -get exactly the results you would expect from the source code. -.sp -Without -.BR \-O , -only variables declared -.B register -are allocated in registers. -The resulting compiled code is a little worse than produced by PCC without -.BR \-O . -.sp -With -.BR \-O , -the compiler tries to reduce code size and execution time. -.sp -Some of the -.B \-f -options described below turn specific kinds of optimization on or off. -.TP -.B \-g -Produce debugging information in the operating system's -native format (for DBX or SDB). -GDB also can work with this debugging information. -.sp -Unlike most other C compilers, GNU CC allows you to use -.B \-g -with -.BR \-O . -The shortcuts taken by optimized code may occasionally -produce surprising results: some variables you declared may not exist -at all; flow of control may briefly move where you did not expect it; -some statements may not be executed because they compute constant -results or their values were already at hand; some statements may -execute in different places because they were moved out of loops. -Nevertheless it proves possible to debug optimized output. -This makes it reasonable to use the optimizer for programs -that might have bugs. -.TP -.B \-w -Inhibit all warning messages. -.TP -.B \-W -Print extra warning messages for these events: -.BP -An automatic variable is used without first being initialized. -.sp -These warnings are possible only in optimizing compilation, -because they require data flow information that is computed only -when optimizing. -If you don't specify -.BR \-O , -you simply won't get these warnings. -.sp -These warnings occur only for variables that are candidates for -register allocation. -Therefore, they do not occur for a variable that is declared -.BR volatile , -or whose address is taken, or whose size is other than 1, 2, 4 or 8 bytes. -Also, they do not occur for structures, unions or arrays, even when -they are in registers. -.sp -Note that there may be no warning about a variable that is used only -to compute a value that itself is never used, because such -computations may be deleted by data flow analysis before the warnings -are printed. -.sp -These warnings are made optional because GNU CC is not smart -enough to see all the reasons why the code might be correct -despite appearing to have an error. -Here is one example of how this can happen: -.sp - { -.br - \ \ int x; -.br - \ \ switch (y) -.br - \ \ \ \ { -.br - \ \ \ \ case 1: x = 1; -.br - \ \ \ \ \ \ break; -.br - \ \ \ \ case 2: x = 4; -.br - \ \ \ \ \ \ break; -.br - \ \ \ \ case 3: x = 5; -.br - \ \ \ \ } -.br - \ \ foo (x); -.br - } -.sp -If the value of -.I y -is always 1, 2 or 3, then -.I x -is always initialized, but GNU CC doesn't know this. -Here is another common case: -.sp - { -.br - \ \ int save_y; -.br - \ \ if (change_y) save_y = y, y = new_y; -.br - \ \ ... -.br - \ \ if (change_y) y = save_y; -.br - } -.sp -This has no bug because -.I save_y -is used only if it is set. -.sp -Some spurious warnings can be avoided if you declare as -.B volatile -all the functions you use that never return. -.BP -A nonvolatile automatic variable might be changed by a call to -.IR longjmp (3C). -These warnings as well are possible only in optimizing compilation. -.sp -The compiler sees only the calls to -.IR setjmp (3C). -It cannot know where -.IR longjmp (3C) -will be called; in fact, a signal handler could -call it at any point in the code. -As a result, you may get a warning even when there is -in fact no problem because -.IR longjmp (3C) -cannot in fact be called at the place which would cause a problem. -.BP -A function can return either with or without a value. -(Falling off the end of the function body is considered returning without -a value.) -For example, this function would evoke such a warning: -.sp - foo (a) -.br - { -.br - \ \ if (a > 0) -.br - \ \ \ \ return a; -.br - } -.sp -Spurious warnings can occur because GNU CC does not realize that -certain functions (including -.IR abort (3C) -and -.IR longjmp (3C)) -will never return. -.BP -An expression-statement contains no side effects. -.sp -In the future, other useful warnings may also be enabled by this option. -.TP -.B \-Wimplicit -Warn whenever a function is implicitly declared. -.TP -.B \-Wreturn-type -Warn whenever a function is defined with a return-type that defaults to -.BR int . -Also warn about any -.B return -statement with no return-value in a function whose return-type is not -.BR void . -.TP -.B \-Wunused -Warn whenever a local variable is unused aside from its declaration, -and whenever a function is declared static but never defined. -.TP -.B \-Wswitch -Warn whenever a -.B switch -statement has an index of enumeral type and lacks a -.B case -for one or more of the named codes of that enumeration. -(The presence of a -.B default -label prevents this warning.) -.B case -labels outside the enumeration range also provoke -warnings when this option is used. -.TP -.B \-Wcomment -Warn whenever a comment-start sequence -.B /\(** -appears in a comment. -.TP -.B \-Wtrigraphs -Warn if any trigraphs are encountered (assuming they are enabled). -.TP -.B \-Wall -All of the above -.B \-W -options combined. -These are all the options which pertain to usage that we do not recommend and -that we believe is always easy to avoid, even in conjunction with macros. -.sp -The other -.BR \-W ... -options below are not implied by -.B \-Wall -because certain kinds of useful macros are almost impossible to write -without causing those warnings. -.TP -.B \-Wshadow -Warn whenever a local variable shadows another local variable. -.TP -.BI \-Wid-clash- len -Warn whenever two distinct identifiers match in the first -.I len -characters. -This may help you prepare a program that will compile with certain obsolete, -brain-damaged compilers. -.TP -.B \-Wpointer-arith -Warn about anything that depends on the size of a function type or of -.BR void . -GNU C assigns these types a size of 1, for convenience in calculations with -.B void \(** -pointers and pointers to functions. -.TP -.B \-Wcast-qual -Warn whenever a pointer is cast so as to remove a type qualifier from -the target type. -For example, warn if a -.B const char \(** -is cast to an ordinary -.BR "char \(**" . -.TP -.B \-Wwrite-strings -Give string constants the type -.B const char[\fIlength\fB] -so that copying the address of one into a -.RB non- "const char \(**" -pointer will get a warning. -These warnings will help you find at compile time -code that can try to write into a string constant, -but only if you have been very careful about using -.B const -in declarations and prototypes. -Otherwise, it will just be a nuisance; this is why we did not make -.B \-Wall -request these warnings. -.TP -.B \-p -Generate extra code to write profile information suitable -for the analysis program -.IR prof (1). -.TP -.B \-pg -Generate extra code to write profile information suitable for the -analysis program -.IR gprof (1). -.TP -.B \-a -Generate extra code to write profile information for basic blocks, -suitable for the analysis program -.IR tcov (1). -Eventually GNU -.IR gprof (1) -should be extended to process this data. -.TP -.BI \-l library -Search a standard list of directories for a library named -.IR library , -which is actually a file named -.BR lib\fIlibrary\fB.a . -The linker uses this file as if it had been specified precisely by name. -.sp -The directories searched include several standard system directories -plus any that you specify with -.BR \-L . -.sp -Normally the files found this way are library files--archive files -whose members are object files. -The linker handles an archive file by scanning through it for members -which define symbols that have so far been referenced but not defined. -But if the file that is found is an ordinary object file, it is linked -in the usual fashion. -The only difference between using an -.B \-l -option and specifying a file name is that -.B \-l -searches several directories. -.TP -.BI \-L dir -Add directory -.I dir -to the list of directories to be searched for -.BR \-l . -.TP -.B \-nostdlib -Don't use the standard system libraries and startup files when linking. -Only the files you specify (plus -.BR gnulib ) -will be passed to the linker. -.TP -.BI \-m machinespec -Machine-dependent option specifying something about the type of target machine. -These options are defined by the macro -.B TARGET_SWITCHES -in the machine description. -The default for the options is also defined by that macro, -which enables you to change the defaults. -.sp -These are the -.B \-m -options defined in the 68000 machine description: -.sp -.B \-m68020 -.br -.B \-mc68020 -.in +.5i -Generate output for a 68020 (rather than a 68000). -This is the default if you use the unmodified sources. -.in -.5i -.sp -.B \-m68000 -.br -.B \-mc68000 -.in +.5i -Generate output for a 68000 (rather than a 68020). -.in -.5i -.sp -.B \-m68881 -.in +.5i -Generate output containing 68881 instructions for floating point. -This is the default if you use the unmodified sources. -.in -.5i -.sp -.B \-mfpa -.in +.5i -Generate output containing Sun FPA instructions for floating point. -.in -.5i -.sp -.B \-msoft-float -.in +.5i -Generate output containing library calls for floating point. -.in -.5i -.sp -.B \-mshort -.in +.5i -Consider type -.B int -to be 16 bits wide, like -.BR "short int" . -.in -.5i -.sp -.B \-mnobitfield -.in +.5i -Do not use the bit-field instructions. -.B \-m68000 -implies -.BR \-mnobitfield . -.in -.5i -.sp -.B \-mbitfield -.in +.5i -Do use the bit-field instructions. -.B \-m68020 -implies -.BR \-mbitfield . -This is the default if you use the unmodified sources. -.in -.5i -.sp -.B \-mrtd -.in +.5i -Use a different function-calling convention, in which functions -that take a fixed number of arguments return with the -.B rtd -instruction, which pops their arguments while returning. -This saves one instruction in the caller since there is no need to pop -the arguments there. -.sp -This calling convention is incompatible with the one normally -used on Unix, so you cannot use it if you need to call libraries -compiled with the Unix compiler. -.sp -Also, you must provide function prototypes for all functions that -take variable numbers of arguments (including -.BR printf (3S)); -otherwise incorrect code will be generated for calls to those functions. -.sp -In addition, seriously incorrect code will result if you call a -function with too many arguments. -(Normally, extra arguments are harmlessly ignored.) -.sp -The -.B rtd -instruction is supported by the 68010 and 68020 processors, -but not by the 68000. -.in -.5i -.sp -These -.B \-m -options are defined in the Vax machine description: -.sp -.B \-munix -.in +.5i -Do not output certain jump instructions -.RB ( aobleq -and so on) that the Unix assembler for the Vax -cannot handle across long ranges. -.in -.5i -.sp -.B \-mgnu -.in +.5i -Do output those jump instructions, on the assumption that you -will assemble with the GNU assembler. -.in -.5i -.sp -.B \-mg -.in +.5i -Output code for g-format floating point numbers instead of d-format. -.in -.5i -.sp -These -.B \-m -switches are supported on the Sparc: -.sp -.B \-mfpu -.in +.5i -Generate output containing floating point instructions. -This is the default if you use the unmodified sources. -.in -.5i -.sp -.B \-msoft-float -.in +.5i -Generate output containing library calls for floating point. -.in -.5i -.sp -.B \-mno-epilogue -.in +.5i -Generate separate return instructions for -.B return -statements. -This has both advantages and disadvantages; I don't recall what they are. -.in -.5i -.sp -These -.B \-m -options are defined in the Convex machine description: -.sp -.B \-mc1 -.in +.5i -Generate output for a C1. -This is the default when the compiler is configured for a C1. -.in -.5i -.sp -.B \-mc2 -.in +.5i -Generate output for a C2. -This is the default when the compiler is configured for a C2. -.in -.5i -.sp -.B \-margcount -.in +.5i -Generate code which puts an argument count in the word preceding each -argument list. -Some nonportable Convex and Vax programs need this word. -(Debuggers don't; this info is in the symbol table.) -.in -.5i -.sp -.B \-mnoargcount -.in +.5i -Omit the argument count word. -This is the default if you use the unmodified sources. -.in -.5i -.TP -.BI \-f flag -Specify machine-independent flags. -Most flags have both positive and negative forms; the negative form of -.B \-ffoo -would be -.BR \-fno-foo . -In the table below, only one of the forms is listed--the one which -is not the default. -You can figure out the other form by either removing -.B no- -or adding it. -.TP -.B \-fpcc-struct-return -Use the same convention for returning -.B struct -and -.B union -values that is used by the usual C compiler on your system. -This convention is less efficient for small structures, and on many -machines it fails to be reentrant; but it has the advantage of allowing -intercallability between GCC-compiled code and PCC-compiled code. -.TP -.B \-ffloat-store -Do not store floating-point variables in registers. -This prevents undesirable excess precision on machines such as the -68000 where the floating registers (of the 68881) keep more -precision than a -.B double -is supposed to have. -.sp -For most programs, the excess precision does only good, but a few -programs rely on the precise definition of IEEE floating point. -Use -.B \-ffloat-store -for such programs. -.TP -.B \-fno-asm -Do not recognize -.BR asm , -.B inline -or -.B typeof -as a keyword. -These words may then be used as identifiers. -You can use -.BR __asm__ , -.B __inline__ -and -.B __typeof__ -instead. -.TP -.B \-fno-defer-pop -Always pop the arguments to each function call as soon as that -function returns. -Normally the compiler (when optimizing) lets arguments accumulate -on the stack for several function calls and pops them all at once. -.TP -.B \-fstrength-reduce -Perform the optimizations of loop strength reduction and -elimination of iteration variables. -.TP -.B \-fcombine-regs -Allow the combine pass to combine an instruction that copies one -register into another. -This might or might not produce better code when used in addition to -.BR \-O . -I am interested in hearing about the difference this makes. -.TP -.B \-fforce-mem -Force memory operands to be copied into registers before doing -arithmetic on them. -This may produce better code by making all memory references -potential common subexpressions. -When they are not common subexpressions, instruction combination should -eliminate the separate register-load. -I am interested in hearing about the difference this makes. -.TP -.B \-fforce-addr -Force memory address constants to be copied into registers before -doing arithmetic on them. -This may produce better code just as -.B \-fforce-mem -may. -I am interested in hearing about the difference this makes. -.TP -.B \-fomit-frame-pointer -Don't keep the frame pointer in a register for functions that -don't need one. -This avoids the instructions to save, set up and restore frame pointers; -it also makes an extra register available in many functions. -.B "It also makes debugging impossible." -.sp -On some machines, such as the Vax, this flag has no effect, because -the standard calling sequence automatically handles the frame pointer -and nothing is saved by pretending it doesn't exist. -The machine-description macro -.B FRAME_POINTER_REQUIRED -controls whether a target machine supports this flag. -.TP -.B \-finline-functions -Integrate all simple functions into their callers. -The compiler heuristically decides which functions are simple -enough to be worth integrating in this way. -.sp -If all calls to a given function are integrated, and the function is declared -.BR static , -then the function is normally not output as assembler code in its own right. -.TP -.B \-fcaller-saves -Enable values to be allocated in registers that will be clobbered by -function calls, by emitting extra instructions to save and restore the -registers around such calls. -Such allocation is done only when it seems to result in better code than -would otherwise be produced. -.sp -This option is enabled by default on certain machines, usually those -which have no call-preserved registers to use instead. -.TP -.B \-fkeep-inline-functions -Even if all calls to a given function are integrated, and the function is -declared -.BR static , -nevertheless output a separate run-time callable version of the function. -.TP -.B \-fwritable-strings -Store string constants in the writable data segment and don't uniquize them. -This is for compatibility with old programs which assume they can write -into string constants. -Writing into string constants is a very bad idea; -constants should be constant. -.TP -.B \-fcond-mismatch -Allow conditional expressions with mismatched types in the second and -third arguments. -The value of such an expression is void. -.TP -.B \-fno-function-cse -Do not put function addresses in registers; make each instruction that -calls a constant function contain the function's address explicitly. -.sp -This option results in less efficient code, but some strange hacks that -alter the assembler output may be confused by the optimizations performed -when this option is not used. -.TP -.B \-fvolatile -Consider all memory references through pointers to be volatile. -.TP -.B \-fshared-data -Requests that the data and -.RB non- const -variables of this compilation be shared data rather than private data. -The distinction makes sense only on certain operating systems, where -shared data is shared between processes running the same program, while -private data exists in one copy per process. -.TP -.B \-funsigned-char -Let the type -.B char -be the unsigned, like -.BR "unsigned char" . -.sp -Each kind of machine has a default for what -.B char -should be. -It is either like -.B "unsigned char" -by default or like -.B "signed char" -by default. -(Actually, at present, the default is always signed.) -.sp -The type -.B char -is always a distinct type from either -.B "signed char" -or -.BR "unsigned char" , -even though its behavior is always just like one of those two. -.sp -Note that this is equivalent to -.BR \-fno-signed-char , -which is the negative form of -.BR \-fsigned-char . -.TP -.B \-fsigned-char -Let the type -.B char -be signed, like -.BR "signed char" . -.sp -Note that this is equivalent to -.BR \-fno-unsigned-char , -which is the negative form of -.BR \-funsigned-char . -.TP -.B \-fdelayed-branch -If supported for the target machine, attempt to reorder instructions to -exploit instruction slots available after delayed branch instructions. -.TP -.BI \-ffixed- reg -Treat the register named -.I reg -as a fixed register; generated code should never refer to it -(except perhaps as a stack pointer, frame pointer or in some other fixed role). -.sp -.I reg -must be the name of a register. -The register names accepted are machine-specific and are defined in the -.B REGISTER_NAMES -macro in the machine description macro file. -.sp -This flag does not have a negative form, because it specifies a -three-way choice. -.TP -.BI \-fcall-used- reg -Treat the register named -.I reg -as an allocatable register that is clobbered by function calls. -It may be allocated for temporaries or variables that do not live -across a call. -Functions compiled this way will not save and restore the register REG. -.sp -Use of this flag for a register that has a fixed pervasive role -in the machine's execution model, such as the stack pointer or -frame pointer, will produce disastrous results. -.sp -This flag does not have a negative form, because it specifies a -three-way choice. -.TP -.BI \-fcall-saved- reg -Treat the register named -.I reg -as an allocatable register saved by functions. -It may be allocated even for temporaries or variables that live across a call. -Functions compiled this way will save and restore the register -.I reg -if they use it. -.sp -Use of this flag for a register that has a fixed pervasive role -in the machine's execution model, such as the stack pointer or -frame pointer, will produce disastrous results. -.sp -A different sort of disaster will result from the use of this -flag for a register in which function values may be returned. -.sp -This flag does not have a negative form, because it specifies a -three-way choice. -.TP -.BI \-d letters -Says to make debugging dumps at times specified by -.IR letters . -Here are the possible letters: -.sp -.B r -.in +.5i -Dump after RTL generation. -.in -.5i -.B j -.in +.5i -Dump after first jump optimization. -.in -.5i -.B J -.in +.5i -Dump after last jump optimization. -.in -.5i -.B s -.in +.5i -Dump after CSE (including the jump optimization that sometimes follows CSE). -.in -.5i -.B L -.in +.5i -Dump after loop optimization. -.in -.5i -.B f -.in +.5i -Dump after flow analysis. -.in -.5i -.B c -.in +.5i -Dump after instruction combination. -.in -.5i -.B l -.in +.5i -Dump after local register allocation. -.in -.5i -.B g -.in +.5i -Dump after global register allocation. -.in -.5i -.B d -.in +.5i -Dump after delayed branch scheduling. -.in -.5i -.B m -.in +.5i -Print statistics on memory usage, at the end of the run. -.in -.5i -.TP -.B \-pedantic -Issue all the warnings demanded by strict ANSI standard C; reject -all programs that use forbidden extensions. -.sp -Valid ANSI standard C programs should compile properly with or without -this option (though a rare few will require -.BR \-ansi ). -However, without this option, certain GNU extensions and traditional C -features are supported as well. -With this option, they are rejected. -There is no reason to use this option; it exists only to satisfy pedants. -.sp -.B \-pedantic -does not cause warning messages for use of the alternate keywords whose -names begin and end with -.BR __ . -.TP -.B \-static -On Suns running version 4, this prevents linking with the shared -libraries. -.RB ( \-g -has the same effect.) -.P -These options control the C preprocessor, which is run on each C source -file before actual compilation. If you use the `-E' option, nothing -is done except C preprocessing. Some of these options make sense only -together with `-E' because they request preprocessor output that is -not suitable for actual compilation. -.TP -.B \-C -Tell the preprocessor not to discard comments. -Used with the -.B \-E -option. -.TP -.BI \-I dir -Search directory -.I dir -for include files. -.TP -.B \-I- -Any directories specified with -.B \-I -options before the -.B \-I- -option are searched only for the case of -.B #include -\fB"\fIfile\fB"\fR; they are not searched for -.BR "#include <\fIfile\fB>" . -.sp -If additional directories are specified with -.B \-I -options after the -.BR \-I- , -these directories are searched for all -.B #include -directives. -(Ordinarily -.I all -.B \-I -directories are used this way.) -.sp -In addition, the -.B \-I- -option inhibits the use of the current directory as the first -search directory for -.B #include -\fB"\fIfile\fB"\fR. -Therefore, the current directory is searched only if it is requested -explicitly with -.BR \-I. . -Specifying both -.B \-I- -and -.B -I. -allows you to control precisely which directories are searched before -the current one and which are searched after. -.TP -.B \-nostdinc -Do not search the standard system directories for header files. -Only the directories you have specified with -.B \-I -options (and the current directory, if appropriate) are searched. -.sp -Between -.B \-nostdinc -and -.BR \-I- , -you can eliminate all directories from the search path -except those you specify. -.TP -.B \-M -Tell the preprocessor to output a rule suitable for -.BI make (1) -describing the dependencies of each source file. -For each source file, the preprocessor outputs one -.BR make -rule -whose target is the object file name for that source file and whose -dependencies are all the files -.BR #include d -in it. -This rule may be a single line or may be continued with -.B \\\\-newline -if it is long. -.sp -.B \-M -implies -.BR \-E . -.TP -.B \-MM -Like -.B \-M -but the output mentions only the user-header files included with -.B #include -\fB"\fIfile\fB"\fR. -System header files included with -.B "#include <\fIfile\fB>" -are omitted. -.sp -.B \-MM -implies -.BR \-E . -.TP -.BI \-D macro -Define macro -.I macro -with the empty string as its definition. -.TP -.BI \-D macro\fR=\fIdefn -Define macro -.I macro -as -.IR defn . -.TP -.BI \-U macro -Undefine macro -.IR macro . -.TP -.B \-trigraphs -Support ANSI C trigraphs. -You don't want to know about this brain-damage. -The -.B \-ansi -option also has this effect. -.SH FILES -.ta \w'/usr/lib/libgnulib.a 'u -file.c C source file -.br -file.s assembly language file -.br -file.o object file -.br -a.out link edited output -.br -/tmp/cc\(** temporary files -.br -/usr/libexec/cpp preprocessor -.br -/usr/libexec/cc1 compiler -.br -/usr/lib/libgnulib.a library needed by GCC on some machines -.br -/usr/lib/crt0.o start-up routine -.br -/usr/lib/libc.a standard C library, see -.IR intro (3) -.br -/usr/include standard directory for -.B #include -files -.SH "SEE ALSO" -as(1), ld(1), adb(1), dbx(1), gdb(1). -.SH BUGS -Bugs should be reported to -.BR bug-gcc@prep.ai.mit.edu . -Bugs tend actually to be fixed if they can be isolated, so it is in your -interest to report them in such a way that they can be easily reproduced. -.SH COPYING -Copyright (c) 1988 Free Software Foundation, Inc. -.P -Permission is granted to make and distribute verbatim copies of -this manual provided the copyright notice and this permission notice -are preserved on all copies. -.P -Permission is granted to copy and distribute modified versions of this -manual under the conditions for verbatim copying, provided that the -entire resulting derived work is distributed under the terms of a -permission notice identical to this one. -.P -Permission is granted to copy and distribute translations of this -manual into another language, under the above conditions for modified -versions, except that this permission notice may be included in -translations approved by the Free Software Foundation instead of in -the original English. -.SH AUTHORS -See the GNU CC Manual for the contributors to GNU CC. diff --git a/gnu/usr.bin/gcc1/cc/cc.c b/gnu/usr.bin/gcc1/cc/cc.c deleted file mode 100644 index 7c98a89bca..0000000000 --- a/gnu/usr.bin/gcc1/cc/cc.c +++ /dev/null @@ -1,2114 +0,0 @@ -/*- - * This code is derived from software copyrighted by the Free Software - * Foundation. - * - * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. - */ - -#ifndef lint -static char sccsid[] = "@(#)cc.c 6.7 (Berkeley) 5/8/91"; -#endif /* not lint */ - -/*- - Copyright (C) 1987,1989 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - -This paragraph is here to try to keep Sun CC from dying. -The number of chars here seems crucial!!!! */ - -void record_temp_file (); - -/* This program is the user interface to the C compiler and possibly to -other compilers. It is used because compilation is a complicated procedure -which involves running several programs and passing temporary files between -them, forwarding the users switches to those programs selectively, -and deleting the temporary files at the end. - -CC recognizes how to compile each input file by suffixes in the file names. -Once it knows which kind of compilation to perform, the procedure for -compilation is specified by a string called a "spec". - -Specs are strings containing lines, each of which (if not blank) -is made up of a program name, and arguments separated by spaces. -The program name must be exact and start from root, since no path -is searched and it is unreliable to depend on the current working directory. -Redirection of input or output is not supported; the subprograms must -accept filenames saying what files to read and write. - -In addition, the specs can contain %-sequences to substitute variable text -or for conditional text. Here is a table of all defined %-sequences. -Note that spaces are not generated automatically around the results of -expanding these sequences; therefore, you can concatenate them together -or with constant text in a single argument. - - %% substitute one % into the program name or argument. - %i substitute the name of the input file being processed. - %b substitute the basename of the input file being processed. - This is the substring up to (and not including) the last period. - %g substitute the temporary-file-name-base. This is a string chosen - once per compilation. Different temporary file names are made by - concatenation of constant strings on the end, as in `%g.s'. - %g also has the same effect of %d. - %d marks the argument containing or following the %d as a - temporary file name, so that that file will be deleted if CC exits - successfully. Unlike %g, this contributes no text to the argument. - %w marks the argument containing or following the %w as the - "output file" of this compilation. This puts the argument - into the sequence of arguments that %o will substitute later. - %W{...} - like %{...} but mark last argument supplied within - as a file to be deleted on failure. - %o substitutes the names of all the output files, with spaces - automatically placed around them. You should write spaces - around the %o as well or the results are undefined. - %o is for use in the specs for running the linker. - Input files whose names have no recognized suffix are not compiled - at all, but they are included among the output files, so they will - be linked. - %p substitutes the standard macro predefinitions for the - current target machine. Use this when running cpp. - %P like %p, but puts `__' before and after the name of each macro. - This is for ANSI C. - %s current argument is the name of a library or startup file of some sort. - Search for that file in a standard list of directories - and substitute the full pathname found. - %eSTR Print STR as an error message. STR is terminated by a newline. - Use this when inconsistent options are detected. - %a process ASM_SPEC as a spec. - This allows config.h to specify part of the spec for running as. - %l process LINK_SPEC as a spec. - %L process LIB_SPEC as a spec. - %S process STARTFILE_SPEC as a spec. A capital S is actually used here. - %E process ENDFILE_SPEC as a spec. A capital E is actually used here. - %c process SIGNED_CHAR_SPEC as a spec. - %C process CPP_SPEC as a spec. A capital C is actually used here. - %1 process CC1_SPEC as a spec. - %{S} substitutes the -S switch, if that switch was given to CC. - If that switch was not specified, this substitutes nothing. - Here S is a metasyntactic variable. - %{S*} substitutes all the switches specified to CC whose names start - with -S. This is used for -o, -D, -I, etc; switches that take - arguments. CC considers `-o foo' as being one switch whose - name starts with `o'. %{o*} would substitute this text, - including the space; thus, two arguments would be generated. - %{S:X} substitutes X, but only if the -S switch was given to CC. - %{!S:X} substitutes X, but only if the -S switch was NOT given to CC. - %{|S:X} like %{S:X}, but if no S switch, substitute `-'. - %{|!S:X} like %{!S:X}, but if there is an S switch, substitute `-'. - -The conditional text X in a %{S:X} or %{!S:X} construct may contain -other nested % constructs or spaces, or even newlines. -They are processed as usual, as described above. - -The character | is used to indicate that a command should be piped to -the following command, but only if -pipe is specified. - -Note that it is built into CC which switches take arguments and which -do not. You might think it would be useful to generalize this to -allow each compiler's spec to say which switches take arguments. But -this cannot be done in a consistent fashion. CC cannot even decide -which input files have been specified without knowing which switches -take arguments, and it must know which input files to compile in order -to tell which compilers to run. - -CC also knows implicitly that arguments starting in `-l' are to -be treated as compiler output files, and passed to the linker in their proper -position among the other output files. - -*/ - -#include -#include -#include -#include - -#include "config.h" -#include "obstack.h" -#include "gvarargs.h" - -#ifdef USG -#ifndef R_OK -#define R_OK 4 -#define W_OK 2 -#define X_OK 1 -#endif - -#define vfork fork -#endif /* USG */ - -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free -extern int xmalloc (); -extern void free (); - -/* If a stage of compilation returns an exit status >= 1, - compilation of that file ceases. */ - -#define MIN_FATAL_STATUS 1 - -/* This is the obstack which we use to allocate many strings. */ - -struct obstack obstack; - -char *handle_braces (); -char *save_string (); -char *concat (); -int do_spec (); -int do_spec_1 (); -char *find_file (); -static char *find_exec_file (); -void validate_switches (); -void validate_all_switches (); -void fancy_abort (); - -/* config.h can define ASM_SPEC to provide extra args to the assembler - or extra switch-translations. */ -#ifndef ASM_SPEC -#define ASM_SPEC "" -#endif - -/* config.h can define CPP_SPEC to provide extra args to the C preprocessor - or extra switch-translations. */ -#ifndef CPP_SPEC -#define CPP_SPEC "" -#endif - -/* config.h can define CC1_SPEC to provide extra args to cc1 - or extra switch-translations. */ -#ifndef CC1_SPEC -#define CC1_SPEC "" -#endif - -/* config.h can define LINK_SPEC to provide extra args to the linker - or extra switch-translations. */ -#ifndef LINK_SPEC -#define LINK_SPEC "" -#endif - -/* config.h can define LIB_SPEC to override the default libraries. */ -#ifndef LIB_SPEC -#define LIB_SPEC "%{!p:%{!pg:-lc}}%{p:-lc_p}%{pg:-lc_p}" -#endif - -/* config.h can define ENDFILE_SPEC to override the default crtn files. */ -#ifndef ENDFILE_SPEC -#define ENDFILE_SPEC "" -#endif - -/* config.h can define STARTFILE_SPEC to override the default crt0 files. */ -#ifndef STARTFILE_SPEC -#define STARTFILE_SPEC \ - "%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt0.o%s}}" -#endif - -/* This spec is used for telling cpp whether char is signed or not. */ -#define SIGNED_CHAR_SPEC \ - (DEFAULT_SIGNED_CHAR ? "%{funsigned-char:-D__CHAR_UNSIGNED__}" \ - : "%{!fsigned-char:-D__CHAR_UNSIGNED__}") - -/* This defines which switch letters take arguments. */ - -#ifndef SWITCH_TAKES_ARG -#define SWITCH_TAKES_ARG(CHAR) \ - ((CHAR) == 'D' || (CHAR) == 'U' || (CHAR) == 'o' \ - || (CHAR) == 'e' || (CHAR) == 'T' || (CHAR) == 'u' \ - || (CHAR) == 'I' || (CHAR) == 'Y' || (CHAR) == 'm' \ - || (CHAR) == 'L' || (CHAR) == 'i' || (CHAR) == 'A') -#endif - -/* This defines which multi-letter switches take arguments. */ - -#ifndef WORD_SWITCH_TAKES_ARG -#define WORD_SWITCH_TAKES_ARG(STR) (!strcmp (STR, "Tdata")) -#endif - -/* This structure says how to run one compiler, and when to do so. */ - -struct compiler -{ - char *suffix; /* Use this compiler for input files - whose names end in this suffix. */ - char *spec; /* To use this compiler, pass this spec - to do_spec. */ -}; - -/* Here are the specs for compiling files with various known suffixes. - A file that does not end in any of these suffixes will be passed - unchanged to the loader and nothing else will be done to it. */ - -struct compiler compilers[] = -{ - {".c", - "cpp %{nostdinc} %{C} %{v} %{D*} %{U*} %{I*} %{M*} %{i*} %{trigraphs} -undef \ - -D__GNUC__ %{ansi:-trigraphs -$ -D__STRICT_ANSI__} %{!ansi:%p} %P\ - %c %{O:-D__OPTIMIZE__} %{traditional} %{pedantic} %{P}\ - %{Wcomment*} %{Wtrigraphs} %{Wall} %{w} %C\ - %i %{!M*:%{!E:%{!pipe:%g.cpp}}}%{E:%W{o*}}%{M*:%W{o*}} |\n\ - %{!M*:%{!E:cc1 %{!pipe:%g.cpp} %1 \ - %{!Q:-quiet} -dumpbase %i %{Y*} %{d*} %{m*} %{f*} %{a}\ - %{g} %{O} %{W*} %{w} %{pedantic} %{ansi} %{traditional}\ - %{v:-version} %{gg:-symout %g.sym} %{pg:-p} %{p}\ - %{pg:%{fomit-frame-pointer:%e-pg and -fomit-frame-pointer are incompatible}}\ - %{S:%W{o*}%{!o*:-o %b.s}}%{!S:-o %{|!pipe:%g.s}} |\n\ - %{!S:as %{R} %{j} %{J} %{h} %{d2} %a %{gg:-G %g.sym}\ - %{c:%W{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%b.o}\ - %{!pipe:%g.s}\n }}}"}, - {".cc", - "cpp -+ %{nostdinc} %{C} %{v} %{D*} %{U*} %{I*} %{M*} %{i*} \ - -undef -D__GNUC__ -D__GNUG__ %p %P\ - %c %{O:-D__OPTIMIZE__} %{traditional} %{pedantic} %{P}\ - %{Wcomment*} %{Wtrigraphs} %{Wall} %{w} %C\ - %i %{!M*:%{!E:%{!pipe:%g.cpp}}}%{E:%W{o*}}%{M*:%W{o*}} |\n\ - %{!M*:%{!E:cc1plus %{!pipe:%g.cpp} %1\ - %{!Q:-quiet} -dumpbase %i %{Y*} %{d*} %{m*} %{f*} %{a}\ - %{g} %{O} %{W*} %{w} %{pedantic} %{traditional}\ - %{v:-version} %{gg:-symout %g.sym} %{pg:-p} %{p}\ - %{pg:%{fomit-frame-pointer:%e-pg and -fomit-frame-pointer are incompatible}}\ - %{S:%W{o*}%{!o*:-o %b.s}}%{!S:-o %{|!pipe:%g.s}} |\n\ - %{!S:as %{R} %{j} %{J} %{h} %{d2} %a %{gg:-G %g.sym}\ - %{c:%W{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%b.o}\ - %{!pipe:%g.s}\n }}}"}, - {".i", - "cc1 %i %1 %{!Q:-quiet} %{Y*} %{d*} %{m*} %{f*} %{a}\ - %{g} %{O} %{W*} %{w} %{pedantic} %{ansi} %{traditional}\ - %{v:-version} %{gg:-symout %g.sym} %{pg:-p} %{p}\ - %{S:%W{o*}%{!o*:-o %b.s}}%{!S:-o %{|!pipe:%g.s}} |\n\ - %{!S:as %{R} %{j} %{J} %{h} %{d2} %a %{gg:-G %g.sym}\ - %{c:%W{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%b.o} %{!pipe:%g.s}\n }"}, - {".s", - "%{!S:as %{R} %{j} %{J} %{h} %{d2} %a \ - %{c:%W{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%b.o} %i\n }"}, - {".S", - "cpp %{nostdinc} %{C} %{v} %{D*} %{U*} %{I*} %{M*} %{trigraphs} \ - -undef -D__GNUC__ -$ %p %P\ - %c %{O:-D__OPTIMIZE__} %{traditional} %{pedantic} %{P}\ - %{Wcomment*} %{Wtrigraphs} %{Wall} %{w} %C\ - %i %{!M*:%{!E:%{!pipe:%g.s}}}%{E:%W{o*}}%{M*:%W{o*}} |\n\ - %{!M*:%{!E:%{!S:as %{R} %{j} %{J} %{h} %{d2} %a \ - %{c:%W{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%b.o}\ - %{!pipe:%g.s}\n }}}"}, - /* Mark end of table */ - {0, 0} -}; - -/* Here is the spec for running the linker, after compiling all files. */ -char *link_spec = "%{!c:%{!M*:%{!E:%{!S:ld %{o*} %l\ - %{A} %{d} %{e*} %{N} %{n} %{r} %{s} %{S} %{T*} %{t} %{u*} %{X} %{x} %{z}\ - %{y*} %{!A:%{!nostdlib:%S}} \ - %{L*} %o %{!nostdlib:%L -lgnulib %{!A:%E}}\n }}}}"; - -/* Accumulate a command (program name and args), and run it. */ - -/* Vector of pointers to arguments in the current line of specifications. */ - -char **argbuf; - -/* Number of elements allocated in argbuf. */ - -int argbuf_length; - -/* Number of elements in argbuf currently in use (containing args). */ - -int argbuf_index; - -/* Number of commands executed so far. */ - -int execution_count; - -/* Flag indicating whether we should print the command and arguments */ - -unsigned char vflag; - -/* Name with which this program was invoked. */ - -char *programname; - -/* User-specified -B prefix to attach to command names, - or 0 if none specified. */ - -char *user_exec_prefix = 0; - -/* Environment-specified prefix to attach to command names, - or 0 if none specified. */ - -char *env_exec_prefix = 0; - -/* Suffix to attach to directories searched for commands. */ - -char *machine_suffix = 0; - -/* Default prefixes to attach to command names. */ - -#ifndef STANDARD_EXEC_PREFIX -#define STANDARD_EXEC_PREFIX "/usr/libexec/" -#endif /* !defined STANDARD_EXEC_PREFIX */ - -char *standard_exec_prefix = STANDARD_EXEC_PREFIX; -char *standard_exec_prefix_1 = "/usr/libexec/gcc-"; - -#ifndef STANDARD_STARTFILE_PREFIX -#define STANDARD_STARTFILE_PREFIX "/usr/lib/" -#endif /* !defined STANDARD_STARTFILE_PREFIX */ - -char *standard_startfile_prefix = STANDARD_STARTFILE_PREFIX; -char *standard_startfile_prefix_1 = "/usr/local/lib/"; -char *standard_startfile_prefix_2 = "/usr/lib/gcc-"; - -/* Clear out the vector of arguments (after a command is executed). */ - -void -clear_args () -{ - argbuf_index = 0; -} - -/* Add one argument to the vector at the end. - This is done when a space is seen or at the end of the line. - If DELETE_ALWAYS is nonzero, the arg is a filename - and the file should be deleted eventually. - If DELETE_FAILURE is nonzero, the arg is a filename - and the file should be deleted if this compilation fails. */ - -void -store_arg (arg, delete_always, delete_failure) - char *arg; - int delete_always, delete_failure; -{ - if (argbuf_index + 1 == argbuf_length) - { - argbuf = (char **) realloc (argbuf, (argbuf_length *= 2) * sizeof (char *)); - } - - argbuf[argbuf_index++] = arg; - argbuf[argbuf_index] = 0; - - if (delete_always || delete_failure) - record_temp_file (arg, delete_always, delete_failure); -} - -/* Record the names of temporary files we tell compilers to write, - and delete them at the end of the run. */ - -/* This is the common prefix we use to make temp file names. - It is chosen once for each run of this program. - It is substituted into a spec by %g. - Thus, all temp file names contain this prefix. - In practice, all temp file names start with this prefix. - - This prefix comes from the envvar TMPDIR if it is defined; - otherwise, in /tmp. */ - -char *temp_filename; - -/* Length of the prefix. */ - -int temp_filename_length; - -/* Define the list of temporary files to delete. */ - -struct temp_file -{ - char *name; - struct temp_file *next; -}; - -/* Queue of files to delete on success or failure of compilation. */ -struct temp_file *always_delete_queue; -/* Queue of files to delete on failure of compilation. */ -struct temp_file *failure_delete_queue; - -/* Record FILENAME as a file to be deleted automatically. - ALWAYS_DELETE nonzero means delete it if all compilation succeeds; - otherwise delete it in any case. - FAIL_DELETE nonzero means delete it if a compilation step fails; - otherwise delete it in any case. */ - -void -record_temp_file (filename, always_delete, fail_delete) - char *filename; - int always_delete; - int fail_delete; -{ - register char *name; - name = (char *) xmalloc (strlen (filename) + 1); - strcpy (name, filename); - - if (always_delete) - { - register struct temp_file *temp; - for (temp = always_delete_queue; temp; temp = temp->next) - if (! strcmp (name, temp->name)) - goto already1; - temp = (struct temp_file *) xmalloc (sizeof (struct temp_file)); - temp->next = always_delete_queue; - temp->name = name; - always_delete_queue = temp; - already1:; - } - - if (fail_delete) - { - register struct temp_file *temp; - for (temp = failure_delete_queue; temp; temp = temp->next) - if (! strcmp (name, temp->name)) - goto already2; - temp = (struct temp_file *) xmalloc (sizeof (struct temp_file)); - temp->next = failure_delete_queue; - temp->name = name; - failure_delete_queue = temp; - already2:; - } -} - -/* Delete all the temporary files whose names we previously recorded. */ - -void -delete_temp_files () -{ - register struct temp_file *temp; - - for (temp = always_delete_queue; temp; temp = temp->next) - { -#ifdef DEBUG - int i; - printf ("Delete %s? (y or n) ", temp->name); - fflush (stdout); - i = getchar (); - if (i != '\n') - while (getchar () != '\n') ; - if (i == 'y' || i == 'Y') -#endif /* DEBUG */ - { - if (unlink (temp->name) < 0) - if (vflag) - perror_with_name (temp->name); - } - } - - always_delete_queue = 0; -} - -/* Delete all the files to be deleted on error. */ - -void -delete_failure_queue () -{ - register struct temp_file *temp; - - for (temp = failure_delete_queue; temp; temp = temp->next) - { -#ifdef DEBUG - int i; - printf ("Delete %s? (y or n) ", temp->name); - fflush (stdout); - i = getchar (); - if (i != '\n') - while (getchar () != '\n') ; - if (i == 'y' || i == 'Y') -#endif /* DEBUG */ - { - if (unlink (temp->name) < 0) - if (vflag) - perror_with_name (temp->name); - } - } -} - -void -clear_failure_queue () -{ - failure_delete_queue = 0; -} - -/* Compute a string to use as the base of all temporary file names. - It is substituted for %g. */ - -void -choose_temp_base () -{ - extern char *getenv (); - char *base = getenv ("TMPDIR"); - int len; - - if (base == (char *)0) - base = "/tmp/"; - - len = strlen (base); - temp_filename = (char *) xmalloc (len + sizeof("/ccXXXXXX")); - strcpy (temp_filename, base); - if (len > 0 && temp_filename[len-1] != '/') - temp_filename[len++] = '/'; - strcpy (temp_filename + len, "ccXXXXXX"); - - mktemp (temp_filename); - temp_filename_length = strlen (temp_filename); -} - -/* Search for an execute file through our search path. - Return 0 if not found, otherwise return its name, allocated with malloc. */ - -static char * -find_exec_file (prog) - char *prog; -{ - int win = 0; - char *temp; - int size; - - size = strlen (standard_exec_prefix); - if (user_exec_prefix != 0 && strlen (user_exec_prefix) > size) - size = strlen (user_exec_prefix); - if (env_exec_prefix != 0 && strlen (env_exec_prefix) > size) - size = strlen (env_exec_prefix); - if (strlen (standard_exec_prefix_1) > size) - size = strlen (standard_exec_prefix_1); - size += strlen (prog) + 1; - if (machine_suffix) - size += strlen (machine_suffix) + 1; - temp = (char *) xmalloc (size); - - /* Determine the filename to execute. */ - - if (user_exec_prefix) - { - if (machine_suffix) - { - strcpy (temp, user_exec_prefix); - strcat (temp, machine_suffix); - strcat (temp, prog); - win = (access (temp, X_OK) == 0); - } - if (!win) - { - strcpy (temp, user_exec_prefix); - strcat (temp, prog); - win = (access (temp, X_OK) == 0); - } - } - - if (!win && env_exec_prefix) - { - if (machine_suffix) - { - strcpy (temp, env_exec_prefix); - strcat (temp, machine_suffix); - strcat (temp, prog); - win = (access (temp, X_OK) == 0); - } - if (!win) - { - strcpy (temp, env_exec_prefix); - strcat (temp, prog); - win = (access (temp, X_OK) == 0); - } - } - - if (!win) - { - if (machine_suffix) - { - strcpy (temp, standard_exec_prefix); - strcat (temp, machine_suffix); - strcat (temp, prog); - win = (access (temp, X_OK) == 0); - } - if (!win) - { - strcpy (temp, standard_exec_prefix); - strcat (temp, prog); - win = (access (temp, X_OK) == 0); - } - } - - if (!win) - { - if (machine_suffix) - { - strcpy (temp, standard_exec_prefix_1); - strcat (temp, machine_suffix); - strcat (temp, prog); - win = (access (temp, X_OK) == 0); - } - if (!win) - { - strcpy (temp, standard_exec_prefix_1); - strcat (temp, prog); - win = (access (temp, X_OK) == 0); - } - } - - if (win) - return temp; - else - return 0; -} - -/* stdin file number. */ -#define STDIN_FILE_NO 0 - -/* stdout file number. */ -#define STDOUT_FILE_NO 1 - -/* value of `pipe': port index for reading. */ -#define READ_PORT 0 - -/* value of `pipe': port index for writing. */ -#define WRITE_PORT 1 - -/* Pipe waiting from last process, to be used as input for the next one. - Value is STDIN_FILE_NO if no pipe is waiting - (i.e. the next command is the first of a group). */ - -int last_pipe_input; - -/* Fork one piped subcommand. FUNC is the system call to use - (either execv or execvp). ARGV is the arg vector to use. - NOT_LAST is nonzero if this is not the last subcommand - (i.e. its output should be piped to the next one.) */ - -static int -pexecute (func, program, argv, not_last) - char *program; - int (*func)(); - char *argv[]; - int not_last; -{ - int pid; - int pdes[2]; - int input_desc = last_pipe_input; - int output_desc = STDOUT_FILE_NO; - - /* If this isn't the last process, make a pipe for its output, - and record it as waiting to be the input to the next process. */ - - if (not_last) - { - if (pipe (pdes) < 0) - pfatal_with_name ("pipe"); - output_desc = pdes[WRITE_PORT]; - last_pipe_input = pdes[READ_PORT]; - } - else - last_pipe_input = STDIN_FILE_NO; - - pid = vfork (); - - switch (pid) - { - case -1: - pfatal_with_name ("vfork"); - break; - - case 0: /* child */ - /* Move the input and output pipes into place, if nec. */ - if (input_desc != STDIN_FILE_NO) - { - close (STDIN_FILE_NO); - dup (input_desc); - close (input_desc); - } - if (output_desc != STDOUT_FILE_NO) - { - close (STDOUT_FILE_NO); - dup (output_desc); - close (output_desc); - } - - /* Close the parent's descs that aren't wanted here. */ - if (last_pipe_input != STDIN_FILE_NO) - close (last_pipe_input); - - /* Exec the program. */ - (*func) (program, argv); - perror_exec (program); - exit (-1); - /* NOTREACHED */ - - default: - /* In the parent, after forking. - Close the descriptors that we made for this child. */ - if (input_desc != STDIN_FILE_NO) - close (input_desc); - if (output_desc != STDOUT_FILE_NO) - close (output_desc); - - /* Return child's process number. */ - return pid; - } -} - -/* Execute the command specified by the arguments on the current line of spec. - When using pipes, this includes several piped-together commands - with `|' between them. - - Return 0 if successful, -1 if failed. */ - -int -execute () -{ - int i, j; - int n_commands; /* # of command. */ - char *string; - struct command - { - char *prog; /* program name. */ - char **argv; /* vector of args. */ - int pid; /* pid of process for this command. */ - }; - - struct command *commands; /* each command buffer with above info. */ - - /* Count # of piped commands. */ - for (n_commands = 1, i = 0; i < argbuf_index; i++) - if (strcmp (argbuf[i], "|") == 0) - n_commands++; - - /* Get storage for each command. */ - commands - = (struct command *) alloca (n_commands * sizeof (struct command)); - - /* Split argbuf into its separate piped processes, - and record info about each one. - Also search for the programs that are to be run. */ - - commands[0].prog = argbuf[0]; /* first command. */ - commands[0].argv = &argbuf[0]; - string = find_exec_file (commands[0].prog); - if (string) - commands[0].argv[0] = string; - - for (n_commands = 1, i = 0; i < argbuf_index; i++) - if (strcmp (argbuf[i], "|") == 0) - { /* each command. */ - argbuf[i] = 0; /* termination of command args. */ - commands[n_commands].prog = argbuf[i + 1]; - commands[n_commands].argv = &argbuf[i + 1]; - string = find_exec_file (commands[n_commands].prog); - if (string) - commands[n_commands].argv[0] = string; - n_commands++; - } - - argbuf[argbuf_index] = 0; - - /* If -v, print what we are about to do, and maybe query. */ - - if (vflag) - { - /* Print each piped command as a separate line. */ - for (i = 0; i < n_commands ; i++) - { - char **j; - - for (j = commands[i].argv; *j; j++) - fprintf (stderr, " %s", *j); - - /* Print a pipe symbol after all but the last command. */ - if (i + 1 != n_commands) - fprintf (stderr, " |"); - fprintf (stderr, "\n"); - } - fflush (stderr); -#ifdef DEBUG - fprintf (stderr, "\nGo ahead? (y or n) "); - fflush (stderr); - j = getchar (); - if (j != '\n') - while (getchar () != '\n') ; - if (j != 'y' && j != 'Y') - return 0; -#endif /* DEBUG */ - } - - /* Run each piped subprocess. */ - - last_pipe_input = STDIN_FILE_NO; - for (i = 0; i < n_commands; i++) - { - extern int execv(), execvp(); - char *string = commands[i].argv[0]; - - commands[i].pid = pexecute ((string != commands[i].prog ? execv : execvp), - string, commands[i].argv, - i + 1 < n_commands); - - if (string != commands[i].prog) - free (string); - } - - execution_count++; - - /* Wait for all the subprocesses to finish. - We don't care what order they finish in; - we know that N_COMMANDS waits will get them all. */ - - { - int ret_code = 0; - - for (i = 0; i < n_commands; i++) - { - int status; - int pid; - char *prog; - - pid = wait (&status); - if (pid < 0) - abort (); - - if (status != 0) - { - int j; - for (j = 0; j < n_commands; j++) - if (commands[j].pid == pid) - prog = commands[j].prog; - - if ((status & 0x7F) != 0) - fatal ("Program %s got fatal signal %d.", prog, (status & 0x7F)); - if (((status & 0xFF00) >> 8) >= MIN_FATAL_STATUS) - ret_code = -1; - } - } - return ret_code; - } -} - -/* Find all the switches given to us - and make a vector describing them. - The elements of the vector a strings, one per switch given. - If a switch uses the following argument, then the `part1' field - is the switch itself and the `part2' field is the following argument. - The `valid' field is nonzero if any spec has looked at this switch; - if it remains zero at the end of the run, it must be meaningless. */ - -struct switchstr -{ - char *part1; - char *part2; - int valid; -}; - -struct switchstr *switches; - -int n_switches; - -/* Also a vector of input files specified. */ - -char **infiles; - -int n_infiles; - -/* And a vector of corresponding output files is made up later. */ - -char **outfiles; - -/* Create the vector `switches' and its contents. - Store its length in `n_switches'. */ - -void -process_command (argc, argv) - int argc; - char **argv; -{ - extern char *getenv (); - register int i; - n_switches = 0; - n_infiles = 0; - - env_exec_prefix = getenv ("GCC_EXEC_PREFIX"); - - /* Scan argv twice. Here, the first time, just count how many switches - there will be in their vector, and how many input files in theirs. - Here we also parse the switches that cc itself uses (e.g. -v). */ - - for (i = 1; i < argc; i++) - { - if (argv[i][0] == '-' && argv[i][1] != 'l') - { - register char *p = &argv[i][1]; - register int c = *p; - - switch (c) - { - case 'b': - machine_suffix = p + 1; - break; - - case 'B': - user_exec_prefix = p + 1; - break; - - case 'v': /* Print our subcommands and print versions. */ - vflag++; - n_switches++; - break; - - default: - n_switches++; - - if (SWITCH_TAKES_ARG (c) && p[1] == 0) - i++; - else if (WORD_SWITCH_TAKES_ARG (p)) - i++; - } - } - else - n_infiles++; - } - - /* Then create the space for the vectors and scan again. */ - - switches = ((struct switchstr *) - xmalloc ((n_switches + 1) * sizeof (struct switchstr))); - infiles = (char **) xmalloc ((n_infiles + 1) * sizeof (char *)); - n_switches = 0; - n_infiles = 0; - - /* This, time, copy the text of each switch and store a pointer - to the copy in the vector of switches. - Store all the infiles in their vector. */ - - for (i = 1; i < argc; i++) - { - if (argv[i][0] == '-' && argv[i][1] != 'l') - { - register char *p = &argv[i][1]; - register int c = *p; - - if (c == 'B' || c == 'b') - continue; - switches[n_switches].part1 = p; - if ((SWITCH_TAKES_ARG (c) && p[1] == 0) - || WORD_SWITCH_TAKES_ARG (p)) - switches[n_switches].part2 = argv[++i]; - else if (c == 'o') { - /* On some systems, ld cannot handle -o without space. - So split the -o and its argument. */ - switches[n_switches].part2 = (char *) xmalloc (strlen (p)); - strcpy (switches[n_switches].part2, &p[1]); - p[1] = 0; - } else - switches[n_switches].part2 = 0; - switches[n_switches].valid = 0; - n_switches++; - } - else - infiles[n_infiles++] = argv[i]; - } - - switches[n_switches].part1 = 0; - infiles[n_infiles] = 0; -} - -/* Process a spec string, accumulating and running commands. */ - -/* These variables describe the input file name. - input_file_number is the index on outfiles of this file, - so that the output file name can be stored for later use by %o. - input_basename is the start of the part of the input file - sans all directory names, and basename_length is the number - of characters starting there excluding the suffix .c or whatever. */ - -char *input_filename; -int input_file_number; -int input_filename_length; -int basename_length; -char *input_basename; - -/* These are variables used within do_spec and do_spec_1. */ - -/* Nonzero if an arg has been started and not yet terminated - (with space, tab or newline). */ -int arg_going; - -/* Nonzero means %d or %g has been seen; the next arg to be terminated - is a temporary file name. */ -int delete_this_arg; - -/* Nonzero means %w has been seen; the next arg to be terminated - is the output file name of this compilation. */ -int this_is_output_file; - -/* Nonzero means %s has been seen; the next arg to be terminated - is the name of a library file and we should try the standard - search dirs for it. */ -int this_is_library_file; - -/* Process the spec SPEC and run the commands specified therein. - Returns 0 if the spec is successfully processed; -1 if failed. */ - -int -do_spec (spec) - char *spec; -{ - int value; - - clear_args (); - arg_going = 0; - delete_this_arg = 0; - this_is_output_file = 0; - this_is_library_file = 0; - - value = do_spec_1 (spec, 0); - - /* Force out any unfinished command. - If -pipe, this forces out the last command if it ended in `|'. */ - if (value == 0) - { - if (argbuf_index > 0 && !strcmp (argbuf[argbuf_index - 1], "|")) - argbuf_index--; - - if (argbuf_index > 0) - value = execute (); - } - - return value; -} - -/* Process the sub-spec SPEC as a portion of a larger spec. - This is like processing a whole spec except that we do - not initialize at the beginning and we do not supply a - newline by default at the end. - INSWITCH nonzero means don't process %-sequences in SPEC; - in this case, % is treated as an ordinary character. - This is used while substituting switches. - INSWITCH nonzero also causes SPC not to terminate an argument. - - Value is zero unless a line was finished - and the command on that line reported an error. */ - -int -do_spec_1 (spec, inswitch) - char *spec; - int inswitch; -{ - register char *p = spec; - register int c; - char *string; - - while (c = *p++) - /* If substituting a switch, treat all chars like letters. - Otherwise, NL, SPC, TAB and % are special. */ - switch (inswitch ? 'a' : c) - { - case '\n': - /* End of line: finish any pending argument, - then run the pending command if one has been started. */ - if (arg_going) - { - obstack_1grow (&obstack, 0); - string = obstack_finish (&obstack); - if (this_is_library_file) - string = find_file (string); - store_arg (string, delete_this_arg, this_is_output_file); - if (this_is_output_file) - outfiles[input_file_number] = string; - } - arg_going = 0; - - if (argbuf_index > 0 && !strcmp (argbuf[argbuf_index - 1], "|")) - { - int i; - for (i = 0; i < n_switches; i++) - if (!strcmp (switches[i].part1, "pipe")) - break; - - /* A `|' before the newline means use a pipe here, - but only if -pipe was specified. - Otherwise, execute now and don't pass the `|' as an arg. */ - if (i < n_switches) - { - switches[i].valid = 1; - break; - } - else - argbuf_index--; - } - - if (argbuf_index > 0) - { - int value = execute (); - if (value) - return value; - } - /* Reinitialize for a new command, and for a new argument. */ - clear_args (); - arg_going = 0; - delete_this_arg = 0; - this_is_output_file = 0; - this_is_library_file = 0; - break; - - case '|': - /* End any pending argument. */ - if (arg_going) - { - obstack_1grow (&obstack, 0); - string = obstack_finish (&obstack); - if (this_is_library_file) - string = find_file (string); - store_arg (string, delete_this_arg, this_is_output_file); - if (this_is_output_file) - outfiles[input_file_number] = string; - } - - /* Use pipe */ - obstack_1grow (&obstack, c); - arg_going = 1; - break; - - case '\t': - case ' ': - /* Space or tab ends an argument if one is pending. */ - if (arg_going) - { - obstack_1grow (&obstack, 0); - string = obstack_finish (&obstack); - if (this_is_library_file) - string = find_file (string); - store_arg (string, delete_this_arg, this_is_output_file); - if (this_is_output_file) - outfiles[input_file_number] = string; - } - /* Reinitialize for a new argument. */ - arg_going = 0; - delete_this_arg = 0; - this_is_output_file = 0; - this_is_library_file = 0; - break; - - case '%': - switch (c = *p++) - { - case 0: - fatal ("Invalid specification! Bug in cc."); - - case 'b': - obstack_grow (&obstack, input_basename, basename_length); - arg_going = 1; - break; - - case 'd': - delete_this_arg = 2; - break; - - case 'e': - /* {...:%efoo} means report an error with `foo' as error message - and don't execute any more commands for this file. */ - { - char *q = p; - char *buf; - while (*p != 0 && *p != '\n') p++; - buf = (char *) alloca (p - q + 1); - strncpy (buf, q, p - q); - buf[p - q] = 0; - error ("%s", buf); - return -1; - } - break; - - case 'g': - obstack_grow (&obstack, temp_filename, temp_filename_length); - delete_this_arg = 1; - arg_going = 1; - break; - - case 'i': - obstack_grow (&obstack, input_filename, input_filename_length); - arg_going = 1; - break; - - case 'o': - { - register int f; - for (f = 0; f < n_infiles; f++) - store_arg (outfiles[f], 0, 0); - } - break; - - case 's': - this_is_library_file = 1; - break; - - case 'W': - { - int index = argbuf_index; - /* Handle the {...} following the %W. */ - if (*p != '{') - abort (); - p = handle_braces (p + 1); - if (p == 0) - return -1; - /* If any args were output, mark the last one for deletion - on failure. */ - if (argbuf_index != index) - record_temp_file (argbuf[argbuf_index - 1], 0, 1); - break; - } - - case 'w': - this_is_output_file = 1; - break; - - case '{': - p = handle_braces (p); - if (p == 0) - return -1; - break; - - case '%': - obstack_1grow (&obstack, '%'); - break; - -/*** The rest just process a certain constant string as a spec. */ - - case '1': - do_spec_1 (CC1_SPEC, 0); - break; - - case 'a': - do_spec_1 (ASM_SPEC, 0); - break; - - case 'c': - do_spec_1 (SIGNED_CHAR_SPEC, 0); - break; - - case 'C': - do_spec_1 (CPP_SPEC, 0); - break; - - case 'l': - do_spec_1 (LINK_SPEC, 0); - break; - - case 'L': - do_spec_1 (LIB_SPEC, 0); - break; - - case 'p': - do_spec_1 (CPP_PREDEFINES, 0); - break; - - case 'P': - { - char *x = (char *) alloca (strlen (CPP_PREDEFINES) * 2 + 1); - char *buf = x; - char *y = CPP_PREDEFINES; - - /* Copy all of CPP_PREDEFINES into BUF, - but put __ after every -D and at the end of each arg, */ - while (1) - { - if (! strncmp (y, "-D", 2)) - { - *x++ = '-'; - *x++ = 'D'; - *x++ = '_'; - *x++ = '_'; - y += 2; - } - else if (*y == ' ' || *y == 0) - { - *x++ = '_'; - *x++ = '_'; - if (*y == 0) - break; - else - *x++ = *y++; - } - else - *x++ = *y++; - } - *x = 0; - - do_spec_1 (buf, 0); - } - break; - - case 'S': - do_spec_1 (STARTFILE_SPEC, 0); - break; - - case 'E': - do_spec_1 (ENDFILE_SPEC, 0); - break; - - default: - abort (); - } - break; - - default: - /* Ordinary character: put it into the current argument. */ - obstack_1grow (&obstack, c); - arg_going = 1; - } - - return 0; /* End of string */ -} - -/* Return 0 if we call do_spec_1 and that returns -1. */ - -char * -handle_braces (p) - register char *p; -{ - register char *q; - char *filter; - int pipe = 0; - int negate = 0; - - if (*p == '|') - /* A `|' after the open-brace means, - if the test fails, output a single minus sign rather than nothing. - This is used in %{|!pipe:...}. */ - pipe = 1, ++p; - - if (*p == '!') - /* A `!' after the open-brace negates the condition: - succeed if the specified switch is not present. */ - negate = 1, ++p; - - filter = p; - while (*p != ':' && *p != '}') p++; - if (*p != '}') - { - register int count = 1; - q = p + 1; - while (count > 0) - { - if (*q == '{') - count++; - else if (*q == '}') - count--; - else if (*q == 0) - abort (); - q++; - } - } - else - q = p + 1; - - if (p[-1] == '*' && p[0] == '}') - { - /* Substitute all matching switches as separate args. */ - register int i; - --p; - for (i = 0; i < n_switches; i++) - if (!strncmp (switches[i].part1, filter, p - filter)) - give_switch (i); - } - else - { - /* Test for presence of the specified switch. */ - register int i; - int present = 0; - - /* If name specified ends in *, as in {x*:...}, - check for presence of any switch name starting with x. */ - if (p[-1] == '*') - { - for (i = 0; i < n_switches; i++) - { - if (!strncmp (switches[i].part1, filter, p - filter - 1)) - { - switches[i].valid = 1; - present = 1; - } - } - } - /* Otherwise, check for presence of exact name specified. */ - else - { - for (i = 0; i < n_switches; i++) - { - if (!strncmp (switches[i].part1, filter, p - filter) - && switches[i].part1[p - filter] == 0) - { - switches[i].valid = 1; - present = 1; - break; - } - } - } - - /* If it is as desired (present for %{s...}, absent for %{-s...}) - then substitute either the switch or the specified - conditional text. */ - if (present != negate) - { - if (*p == '}') - { - give_switch (i); - } - else - { - if (do_spec_1 (save_string (p + 1, q - p - 2), 0) < 0) - return 0; - } - } - else if (pipe) - { - /* Here if a %{|...} conditional fails: output a minus sign, - which means "standard output" or "standard input". */ - do_spec_1 ("-", 0); - } - } - - return q; -} - -/* Pass a switch to the current accumulating command - in the same form that we received it. - SWITCHNUM identifies the switch; it is an index into - the vector of switches gcc received, which is `switches'. - This cannot fail since it never finishes a command line. */ - -give_switch (switchnum) - int switchnum; -{ - do_spec_1 ("-", 0); - do_spec_1 (switches[switchnum].part1, 1); - do_spec_1 (" ", 0); - if (switches[switchnum].part2 != 0) - { - do_spec_1 (switches[switchnum].part2, 1); - do_spec_1 (" ", 0); - } - switches[switchnum].valid = 1; -} - -/* Search for a file named NAME trying various prefixes including the - user's -B prefix and some standard ones. - Return the absolute pathname found. If nothing is found, return NAME. */ - -char * -find_file (name) - char *name; -{ - int size; - char *temp; - int win = 0; - - /* Compute maximum size of NAME plus any prefix we will try. */ - - size = strlen (standard_exec_prefix); - if (user_exec_prefix != 0 && strlen (user_exec_prefix) > size) - size = strlen (user_exec_prefix); - if (env_exec_prefix != 0 && strlen (env_exec_prefix) > size) - size = strlen (env_exec_prefix); - if (strlen (standard_exec_prefix) > size) - size = strlen (standard_exec_prefix); - if (strlen (standard_exec_prefix_1) > size) - size = strlen (standard_exec_prefix_1); - if (strlen (standard_startfile_prefix) > size) - size = strlen (standard_startfile_prefix); - if (strlen (standard_startfile_prefix_1) > size) - size = strlen (standard_startfile_prefix_1); - if (strlen (standard_startfile_prefix_2) > size) - size = strlen (standard_startfile_prefix_2); - if (machine_suffix) - size += strlen (machine_suffix) + 1; - size += strlen (name) + 1; - - temp = (char *) alloca (size); - - if (user_exec_prefix) - { - if (machine_suffix) - { - strcpy (temp, user_exec_prefix); - strcat (temp, machine_suffix); - strcat (temp, name); - win = (access (temp, R_OK) == 0); - } - if (!win) - { - strcpy (temp, user_exec_prefix); - strcat (temp, name); - win = (access (temp, R_OK) == 0); - } - } - - if (!win && env_exec_prefix) - { - if (machine_suffix) - { - strcpy (temp, env_exec_prefix); - strcat (temp, machine_suffix); - strcat (temp, name); - win = (access (temp, R_OK) == 0); - } - if (!win) - { - strcpy (temp, env_exec_prefix); - strcat (temp, name); - win = (access (temp, R_OK) == 0); - } - } - - if (!win) - { - if (machine_suffix) - { - strcpy (temp, standard_exec_prefix); - strcat (temp, machine_suffix); - strcat (temp, name); - win = (access (temp, R_OK) == 0); - } - if (!win) - { - strcpy (temp, standard_exec_prefix); - strcat (temp, name); - win = (access (temp, R_OK) == 0); - } - } - - if (!win) - { - if (machine_suffix) - { - strcpy (temp, standard_exec_prefix_1); - strcat (temp, machine_suffix); - strcat (temp, name); - win = (access (temp, R_OK) == 0); - } - if (!win) - { - strcpy (temp, standard_exec_prefix_1); - strcat (temp, name); - win = (access (temp, R_OK) == 0); - } - } - - if (!win) - { - if (machine_suffix) - { - strcpy (temp, standard_startfile_prefix); - strcat (temp, machine_suffix); - strcat (temp, name); - win = (access (temp, R_OK) == 0); - } - if (!win) - { - strcpy (temp, standard_startfile_prefix); - strcat (temp, name); - win = (access (temp, R_OK) == 0); - } - } - - if (!win) - { - if (machine_suffix) - { - strcpy (temp, standard_startfile_prefix_1); - strcat (temp, machine_suffix); - strcat (temp, name); - win = (access (temp, R_OK) == 0); - } - if (!win) - { - strcpy (temp, standard_startfile_prefix_1); - strcat (temp, name); - win = (access (temp, R_OK) == 0); - } - } - - if (!win) - { - if (machine_suffix) - { - strcpy (temp, standard_startfile_prefix_2); - strcat (temp, machine_suffix); - strcat (temp, name); - win = (access (temp, R_OK) == 0); - } - if (!win) - { - strcpy (temp, standard_startfile_prefix_2); - strcat (temp, name); - win = (access (temp, R_OK) == 0); - } - } - - if (!win) - { - if (machine_suffix) - { - strcpy (temp, "./"); - strcat (temp, machine_suffix); - strcat (temp, name); - win = (access (temp, R_OK) == 0); - } - if (!win) - { - strcpy (temp, "./"); - strcat (temp, name); - win = (access (temp, R_OK) == 0); - } - } - - if (win) - return save_string (temp, strlen (temp)); - return name; -} - -/* On fatal signals, delete all the temporary files. */ - -void -fatal_error (signum) - int signum; -{ - signal (signum, SIG_DFL); - delete_failure_queue (); - delete_temp_files (); - /* Get the same signal again, this time not handled, - so its normal effect occurs. */ - kill (getpid (), signum); -} - -int -main (argc, argv) - int argc; - char **argv; -{ - register int i; - int value; - int error_count = 0; - int linker_was_run = 0; - char *explicit_link_files; - - programname = argv[0]; - - if (signal (SIGINT, SIG_IGN) != SIG_IGN) - signal (SIGINT, fatal_error); - if (signal (SIGHUP, SIG_IGN) != SIG_IGN) - signal (SIGHUP, fatal_error); - if (signal (SIGTERM, SIG_IGN) != SIG_IGN) - signal (SIGTERM, fatal_error); - if (signal (SIGPIPE, SIG_IGN) != SIG_IGN) - signal (SIGPIPE, fatal_error); - - argbuf_length = 10; - argbuf = (char **) xmalloc (argbuf_length * sizeof (char *)); - - obstack_init (&obstack); - - choose_temp_base (); - - /* Make a table of what switches there are (switches, n_switches). - Make a table of specified input files (infiles, n_infiles). */ - - process_command (argc, argv); - - if (vflag) - { - extern char *version_string; - fprintf (stderr, "gcc version %s\n", version_string); - if (n_infiles == 0) - exit (0); - } - - if (n_infiles == 0) - fatal ("No input files specified."); - - /* Make a place to record the compiler output file names - that correspond to the input files. */ - - outfiles = (char **) xmalloc (n_infiles * sizeof (char *)); - bzero (outfiles, n_infiles * sizeof (char *)); - - /* Record which files were specified explicitly as link input. */ - - explicit_link_files = (char *) xmalloc (n_infiles); - bzero (explicit_link_files, n_infiles); - - for (i = 0; i < n_infiles; i++) - { - register struct compiler *cp; - int this_file_error = 0; - - /* Tell do_spec what to substitute for %i. */ - - input_filename = infiles[i]; - input_filename_length = strlen (input_filename); - input_file_number = i; - - /* Use the same thing in %o, unless cp->spec says otherwise. */ - - outfiles[i] = input_filename; - - /* Figure out which compiler from the file's suffix. */ - - for (cp = compilers; cp->spec; cp++) - { - if (strlen (cp->suffix) < input_filename_length - && !strcmp (cp->suffix, - infiles[i] + input_filename_length - - strlen (cp->suffix))) - { - /* Ok, we found an applicable compiler. Run its spec. */ - /* First say how much of input_filename to substitute for %b */ - register char *p; - - input_basename = input_filename; - for (p = input_filename; *p; p++) - if (*p == '/') - input_basename = p + 1; - basename_length = (input_filename_length - strlen (cp->suffix) - - (input_basename - input_filename)); - value = do_spec (cp->spec); - if (value < 0) - this_file_error = 1; - break; - } - } - - /* If this file's name does not contain a recognized suffix, - record it as explicit linker input. */ - - if (! cp->spec) - explicit_link_files[i] = 1; - - /* Clear the delete-on-failure queue, deleting the files in it - if this compilation failed. */ - - if (this_file_error) - { - delete_failure_queue (); - error_count++; - } - /* If this compilation succeeded, don't delete those files later. */ - clear_failure_queue (); - } - - /* Run ld to link all the compiler output files. */ - - if (error_count == 0) - { - int tmp = execution_count; - value = do_spec (link_spec); - if (value < 0) - error_count = 1; - linker_was_run = (tmp != execution_count); - } - - /* If options said don't run linker, - complain about input files to be given to the linker. */ - - if (! linker_was_run && error_count == 0) - for (i = 0; i < n_infiles; i++) - if (explicit_link_files[i]) - error ("%s: linker input file unused since linking not done", - outfiles[i]); - - /* Set the `valid' bits for switches that match anything in any spec. */ - - validate_all_switches (); - - /* Warn about any switches that no pass was interested in. */ - - for (i = 0; i < n_switches; i++) - if (! switches[i].valid) - error ("unrecognized option `-%s'", switches[i].part1); - - /* Delete some or all of the temporary files we made. */ - - if (error_count) - delete_failure_queue (); - delete_temp_files (); - - exit (error_count); -} - -xmalloc (size) - int size; -{ - register int value = malloc (size); - if (value == 0) - fatal ("Virtual memory full."); - return value; -} - -xrealloc (ptr, size) - int ptr, size; -{ - register int value = realloc (ptr, size); - if (value == 0) - fatal ("Virtual memory full."); - return value; -} - -/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */ - -char * -concat (s1, s2, s3) - char *s1, *s2, *s3; -{ - int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); - char *result = (char *) xmalloc (len1 + len2 + len3 + 1); - - strcpy (result, s1); - strcpy (result + len1, s2); - strcpy (result + len1 + len2, s3); - *(result + len1 + len2 + len3) = 0; - - return result; -} - -char * -save_string (s, len) - char *s; - int len; -{ - register char *result = (char *) xmalloc (len + 1); - - bcopy (s, result, len); - result[len] = 0; - return result; -} - -pfatal_with_name (name) - char *name; -{ - extern int errno, sys_nerr; - extern char *sys_errlist[]; - char *s; - - if (errno < sys_nerr) - s = concat ("%s: ", sys_errlist[errno], ""); - else - s = "cannot open %s"; - fatal (s, name); -} - -perror_with_name (name) - char *name; -{ - extern int errno, sys_nerr; - extern char *sys_errlist[]; - char *s; - - if (errno < sys_nerr) - s = concat ("%s: ", sys_errlist[errno], ""); - else - s = "cannot open %s"; - error (s, name); -} - -perror_exec (name) - char *name; -{ - extern int errno, sys_nerr; - extern char *sys_errlist[]; - char *s; - - if (errno < sys_nerr) - s = concat ("installation problem, cannot exec %s: ", - sys_errlist[errno], ""); - else - s = "installation problem, cannot exec %s"; - error (s, name); -} - -/* More 'friendly' abort that prints the line and file. - config.h can #define abort fancy_abort if you like that sort of thing. */ - -void -fancy_abort () -{ - fatal ("Internal gcc abort."); -} - -#ifdef HAVE_VPRINTF - -/* Output an error message and exit */ - -int -fatal (va_alist) - va_dcl -{ - va_list ap; - char *format; - - va_start(ap); - format = va_arg (ap, char *); - vfprintf (stderr, format, ap); - va_end (ap); - fprintf (stderr, "\n"); - delete_temp_files (); - exit (1); -} - -error (va_alist) - va_dcl -{ - va_list ap; - char *format; - - va_start(ap); - format = va_arg (ap, char *); - fprintf (stderr, "%s: ", programname); - vfprintf (stderr, format, ap); - va_end (ap); - - fprintf (stderr, "\n"); -} - -#else /* not HAVE_VPRINTF */ - -fatal (msg, arg1, arg2) - char *msg, *arg1, *arg2; -{ - error (msg, arg1, arg2); - delete_temp_files (0); - exit (1); -} - -error (msg, arg1, arg2) - char *msg, *arg1, *arg2; -{ - fprintf (stderr, "%s: ", programname); - fprintf (stderr, msg, arg1, arg2); - fprintf (stderr, "\n"); -} - -#endif /* not HAVE_VPRINTF */ - - -void -validate_all_switches () -{ - struct compiler *comp; - register char *p; - register char c; - - for (comp = compilers; comp->spec; comp++) - { - p = comp->spec; - while (c = *p++) - if (c == '%' && *p == '{') - /* We have a switch spec. */ - validate_switches (p + 1); - } - - p = link_spec; - while (c = *p++) - if (c == '%' && *p == '{') - /* We have a switch spec. */ - validate_switches (p + 1); - - /* Now notice switches mentioned in the machine-specific specs. */ - -#ifdef ASM_SPEC - p = ASM_SPEC; - while (c = *p++) - if (c == '%' && *p == '{') - /* We have a switch spec. */ - validate_switches (p + 1); -#endif - -#ifdef CPP_SPEC - p = CPP_SPEC; - while (c = *p++) - if (c == '%' && *p == '{') - /* We have a switch spec. */ - validate_switches (p + 1); -#endif - -#ifdef SIGNED_CHAR_SPEC - p = SIGNED_CHAR_SPEC; - while (c = *p++) - if (c == '%' && *p == '{') - /* We have a switch spec. */ - validate_switches (p + 1); -#endif - -#ifdef CC1_SPEC - p = CC1_SPEC; - while (c = *p++) - if (c == '%' && *p == '{') - /* We have a switch spec. */ - validate_switches (p + 1); -#endif - -#ifdef LINK_SPEC - p = LINK_SPEC; - while (c = *p++) - if (c == '%' && *p == '{') - /* We have a switch spec. */ - validate_switches (p + 1); -#endif - -#ifdef LIB_SPEC - p = LIB_SPEC; - while (c = *p++) - if (c == '%' && *p == '{') - /* We have a switch spec. */ - validate_switches (p + 1); -#endif - -#ifdef STARTFILE_SPEC - p = STARTFILE_SPEC; - while (c = *p++) - if (c == '%' && *p == '{') - /* We have a switch spec. */ - validate_switches (p + 1); -#endif -} - -/* Look at the switch-name that comes after START - and mark as valid all supplied switches that match it. */ - -void -validate_switches (start) - char *start; -{ - register char *p = start; - char *filter; - register int i; - - if (*p == '|') - ++p; - - if (*p == '!') - ++p; - - filter = p; - while (*p != ':' && *p != '}') p++; - - if (p[-1] == '*') - { - /* Mark all matching switches as valid. */ - --p; - for (i = 0; i < n_switches; i++) - if (!strncmp (switches[i].part1, filter, p - filter)) - switches[i].valid = 1; - } - else - { - /* Mark an exact matching switch as valid. */ - for (i = 0; i < n_switches; i++) - { - if (!strncmp (switches[i].part1, filter, p - filter) - && switches[i].part1[p - filter] == 0) - switches[i].valid = 1; - } - } -} diff --git a/gnu/usr.bin/gcc1/cc1/.dbxinit b/gnu/usr.bin/gcc1/cc1/.dbxinit deleted file mode 100644 index 5018f99664..0000000000 --- a/gnu/usr.bin/gcc1/cc1/.dbxinit +++ /dev/null @@ -1,10 +0,0 @@ -alias l list -alias p print -alias c cont -alias s step -alias n next -alias bt where -alias r run -alias f file -alias si stepi -alias q quit diff --git a/gnu/usr.bin/gcc1/cc1/.gdbinit b/gnu/usr.bin/gcc1/cc1/.gdbinit deleted file mode 100644 index ca93679365..0000000000 --- a/gnu/usr.bin/gcc1/cc1/.gdbinit +++ /dev/null @@ -1,58 +0,0 @@ -define pr -set debug_rtx ($) -end - -document pr -Print the full structure of the rtx that is $. -Works only when an inferior is executing. -end - -define pt -set debug_tree ($) -end - -document pt -Print the full structure of the tree that is $. -Works only when an inferior is executing. -end - -define ptc -output (enum tree_code) $.common.code -echo \n -end - -document ptc -Print the tree-code of the tree node that is $. -end - -define pdn -output $.decl.name->identifier.pointer -echo \n -end - -document pdn -Print the name of the decl-node that is $. -end - -define prc -output (enum rtx_code) $.code -echo \ ( -output $.mode -echo )\n -end - -document prc -Print the rtx-code and machine mode of the rtx that is $. -end - -define pi -print $.fld[0].rtx@7 -end - -document pi -Print the fields of an instruction that is $. -end - -# Don't let abort actually run, as it will make -# stdio stop working and therefore the `pr' command below as well. -b abort diff --git a/gnu/usr.bin/gcc1/cc1/Makefile b/gnu/usr.bin/gcc1/cc1/Makefile deleted file mode 100644 index 73c4d2d94e..0000000000 --- a/gnu/usr.bin/gcc1/cc1/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -# @(#)Makefile 6.2 (Berkeley) 2/2/91 - -PROG= cc1 -BINDIR= /usr/libexec -SRCS= $(CINSN) c-convert.c c-decl.c c-parse.y c-typeck.c \ - caller-save.c combine.c cse.c dbxout.c emit-rtl.c \ - explow.c expmed.c expr.c final.c flow.c fold-const.c \ - global-alloc.c integrate.c jump.c local-alloc.c \ - loop.c obstack.c optabs.c print-tree.c recog.c \ - regclass.c reload.c reload1.c rtl.c rtlanal.c \ - sdbout.c stmt.c stor-layout.c stupid.c symout.c \ - toplev.c tree.c varasm.c version.c -CFLAGS+= -I. -I$(.CURDIR) -I$(.CURDIR)/config -YFLAGS= -NOMAN= noman -CLEANFILES+= $(HINSN) $(CINSN) - -# -# Some machine-dependent source files are generated from -# the machine description file. -# -HINSN= insn-codes.h insn-config.h insn-flags.h -CINSN= insn-emit.c insn-extract.c insn-peep.c insn-recog.c insn-output.c -GEN= gencodes.c genconfig.c genflags.c genemit.c genextract.c \ - genpeep.c genrecog.c genoutput.c - -$(CINSN): md rtl.o obstack.o $(GEN) - $(CC) $(CFLAGS) $(.CURDIR)/../../gcc/cc1/gencodes.c \ - rtl.o obstack.o -o gen; ./gen md > insn-codes.h - $(CC) $(CFLAGS) $(.CURDIR)/../../gcc/cc1/genconfig.c \ - rtl.o obstack.o -o gen; ./gen md > insn-config.h - $(CC) $(CFLAGS) $(.CURDIR)/../../gcc/cc1/genflags.c \ - rtl.o obstack.o -o gen; ./gen md > insn-flags.h - $(CC) $(CFLAGS) $(.CURDIR)/../../gcc/cc1/genemit.c \ - rtl.o obstack.o -o gen; ./gen md > insn-emit.c - $(CC) $(CFLAGS) $(.CURDIR)/../../gcc/cc1/genextract.c \ - rtl.o obstack.o -o gen; ./gen md > insn-extract.c - $(CC) $(CFLAGS) $(.CURDIR)/../../gcc/cc1/genpeep.c \ - rtl.o obstack.o -o gen; ./gen md > insn-peep.c - $(CC) $(CFLAGS) $(.CURDIR)/../../gcc/cc1/genrecog.c \ - rtl.o obstack.o -o gen; ./gen md > insn-recog.c - $(CC) $(CFLAGS) $(.CURDIR)/../../gcc/cc1/genoutput.c \ - rtl.o obstack.o -o gen; ./gen md > insn-output.c - rm -f gen - -.include -.include "../Makefile.symlinks" diff --git a/gnu/usr.bin/gcc1/cc1/Makefile.gnu b/gnu/usr.bin/gcc1/cc1/Makefile.gnu deleted file mode 100644 index 1979ad9ca6..0000000000 --- a/gnu/usr.bin/gcc1/cc1/Makefile.gnu +++ /dev/null @@ -1,724 +0,0 @@ -# Makefile for GNU C compiler. -# Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -#This file is part of GNU CC. - -#GNU CC is free software; you can redistribute it and/or modify -#it under the terms of the GNU General Public License as published by -#the Free Software Foundation; either version 1, or (at your option) -#any later version. - -#GNU CC is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. - -#You should have received a copy of the GNU General Public License -#along with GNU CC; see the file COPYING. If not, write to -#the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - -# Variables that exist for you to override. -# See below for how to change them for certain systems. - -CFLAGS = -g $(XCFLAGS) -CC = cc -BISON = bison -BISONFLAGS = -v -# This should be the version of ar to use with output from GCC. -AR = ar -SHELL = /bin/sh -# on sysV, define this as cp. -INSTALL = install -c -# Directory not specified here so that, if a GNU ranlib -# is earlier in the path, it will be used. -# That is likely to be true on systems that have the GNU ar -# installed earlier in the path. -RANLIB = ranlib - -# Compiler to use for compiling gnulib. -# OLDCC should not be the GNU C compiler. -OLDCC = cc - -# CFLAGS for use with OLDCC, for compiling gnulib. -# NOTE: -O does not work on some Unix systems! -# On them, you must take it out. -CCLIBFLAGS=-O - -# This should be the version of ar to use with output from $(OLDCC). -OLDAR = ar - -# CFLAGS for use with OLDCC, for compiling hard-params. -HARD_PARAMS_FLAGS= - -# Directory where sources are, from where we are. -srcdir = . -# Directory in which to put the executable for the command `gcc' -bindir = $(prefix)/usr/local/bin -# Directory in which to put the subprograms used by the compiler. -libdir = $(prefix)/usr/local/lib -# Directory in which to put man pages. -mandir = $(prefix)/usr/local/man/man1 -# Number to put in man-page filename. -manext = 1 - -# Additional system libraries to link with. -CLIB= - -# Change this to a null string if obstacks are installed in the -# system library. -OBSTACK=obstack.o - -# Directory to link to, when using the target `maketest'. -DIR = ../gcc - -# End of variables for you to override. - - -# Variables you should change for certain systems. - -# These are what you would need on HPUX: -# CFLAGS = -Wc,-Ns2000 -Wc,-Ne700 -Wc,-Np300 -# If you are using the GNU assembler and linker on HPUX, -# add -I../hp-include to CFLAGS. -# -g is desirable in CFLAGS, but a compiler bug in HPUX version 5 -# bites whenever tree.def, rtl.def or machmode.def is included -# (ie., on every source file). -# If you have a floating point accelerator, you might want -lsetjmp as well. -# CCLIBFLAGS = -Wc,-Ns2000 -Wc,-Ne700 -O -# HARD_PARAMS_FLAGS = -Wc,-Ns2000 -Wc,-Ne700 -# INSTALL = cp -# For CCLIBFLAGS you might want to specify the switch that -# forces only 68000 instructions to be used. -# If using the GNU assembler and linker, set AR to the GNU ar program, -# wherever that is. -# To get a working alloca, you may need to get alloca.s from Emacs -# and assemble it into alloca.o rather than using alloca.c. - -# On the Sequent, you may need to set CCLIBFLAG to empty. - -# On the 3b1, this line may help you compile gnulib -# if you already have a prior version of gcc installed. -# CCLIBFLAGS = -B/usr/local/lib/gcc- -tp -Wp,-traditional - -# On SysV from SCO, uncomment these lines as well as those for SysV in general. -# You might also want to remove limits.h from the definition of USER_H, -# since the one that comes with the system is good for POSIX. -# RANLIB = : -# CC = rcc -# OLDCC = rcc - -# On a 386 running an ISC system, uncomment the following lines. -# You also need to add -D_POSIX_SOURCE to CFLAGS -# when compiling with GCC. -# INSTALL = cp -# CLIB = -lPW -lcposix - -# If you are making gcc for the first time, and if you are compiling it with -# a non-gcc compiler, and if your system doesn't have a working alloca() in any -# of the standard libraries (as is true for HP/UX or Genix), -# then un-comment the following line when compiling with the system's cc: -# ALLOCA = alloca.o -# But don't do that if compiling using GCC. - -# If your system has a working alloca in /lib/libPW.a, -# un-comment the following line. Note that -lPW doesn't work on HPUX or IRIX. -# CLIB= -lPW - -# On SysV R4, when compiling with PCC, you can get alloca as follows: -# CLIB = -lc /usr/ucblib/libucb.a -# -lc is needed because if libucb.a were searched before libc.a, -# you would get an incompatible stdio package that won't work. - -# On the NCR Tower 32 running SVR3, says ra@intsys.no : -# Do *not* enable optimization in CFLAGS when using the native cc, because: -# a) The optimizer seems to loop when invoked with -O2. -# b) The -O1 level does stack/frame pointer optimizations that make the -# assembler alloca in libPW.a fail, and the C alloca eats *lots* of memory. -# c) gcc will eventually be recompiled with itself, so all this doesn't matter. -# CFLAGS = -g -O0 -# HARD_PARAMS_FLAGS = -O0 -# CCLIBFLAGS = -O2 -# CLIB = -lmalloc -lPW - -# On a pyramid, you need to uncomment the following line: -# CLIB = -lc /usr/.attlib/libPW.a - -# If your system's malloc() routine fails for any reason (as it does on -# certain versions of Genix), try getting the files -# malloc.c and getpagesize.h from GNU Emacs and un-comment the following line: -# MALLOC = malloc.o - -# To build gcc for Apollo 68K machines you must be running at least SR10.2 -# with the 6.7 version of the C compiler. Use 'apollo68' as the argument to -# config.gcc, then go into a bsd4.3 environment and use this: -# CFLAGS = -g -O -Acpu,3000 -Arun,bsd4.3 -Asys,any -Anansi $(XCFLAGS) -# CCLIBFLAGS = -O -Acpu,3000 -Arun,bsd4.3 -Asys,any -Anansi -# HARD_PARAMS_FLAGS= -Anansi -# (Says vasta@apollo.com.) - - -# Dependency on obstack, alloca, malloc or whatever library facilities -# are not installed in the system libraries. -LIBDEPS= $(OBSTACK) $(ALLOCA) $(MALLOC) - -# How to link with both our special library facilities -# and the system's installed libraries. -LIBS = $(OBSTACK) $(ALLOCA) $(MALLOC) $(CLIB) - -# Specify the directories to be searched for header files. -# Both . and srcdir are used, in that order, -# so that tm.h and config.h will be found in the compilation -# subdirectory rather than in the source directory. -INCLUDES = -I. -I$(srcdir) -I$(srcdir)/config -SUBDIR_INCLUDES = -I.. -I../$(srcdir) -I../$(srcdir)/config - -# Always use -I$(srcdir)/config when compiling. -.c.o: - $(CC) -c $(CFLAGS) $(CPPFLAGS) $(INCLUDES) $< - -# Language-specific object files for C. -C_OBJS = c-parse.tab.o c-decl.o c-typeck.o c-convert.o - -# Language-specific object files for C++. -# (These are not yet released.) -CPLUS_OBJS = cplus-parse.o cplus-decl.o cplus-typeck.o \ - cplus-cvt.o cplus-search.o cplus-lex.o \ - cplus-class.o cplus-init.o cplus-method.o - -# Language-independent object files. -OBJS = toplev.o version.o tree.o print-tree.o stor-layout.o fold-const.o \ - rtl.o rtlanal.o expr.o stmt.o expmed.o explow.o optabs.o varasm.o \ - symout.o dbxout.o sdbout.o emit-rtl.o insn-emit.o \ - integrate.o jump.o cse.o loop.o flow.o stupid.o combine.o \ - regclass.o local-alloc.o global-alloc.o reload.o reload1.o caller-save.o \ - insn-peep.o final.o recog.o insn-recog.o insn-extract.o insn-output.o - -# Files to be copied away after each stage in building. -STAGE_GCC=gcc -STAGESTUFF = *.o insn-flags.h insn-config.h insn-codes.h \ - insn-output.c insn-recog.c insn-emit.c insn-extract.c insn-peep.c \ - stamp-flags stamp-config stamp-codes \ - stamp-output stamp-recog stamp-emit stamp-extract stamp-peep \ - genemit genoutput genrecog genextract genflags gencodes genconfig genpeep \ - cc1 cpp cccp # cc1plus - -# Members of gnulib. -LIBFUNCS = _eprintf _builtin_new _builtin_New _builtin_del _bb \ - _umulsi3 _mulsi3 _udivsi3 _divsi3 _umodsi3 _modsi3 \ - _lshrsi3 _lshlsi3 _ashrsi3 _ashlsi3 \ - _divdf3 _muldf3 _negdf2 _adddf3 _subdf3 _cmpdf2 \ - _fixunsdfsi _fixdfsi _floatsidf _truncdfsf2 _extendsfdf2 \ - _addsf3 _negsf2 _subsf3 _cmpsf2 _mulsf3 _divsf3 - -# Library members defined in gnulib2.c. -LIB2FUNCS = _adddi3 _subdi3 _muldi3 _divdi3 _moddi3 _udivdi3 _umoddi3 _negdi2 \ - _anddi3 _iordi3 _xordi3 _lshrdi3 _lshldi3 _ashldi3 _ashrdi3 _one_cmpldi2 \ - _bdiv _cmpdi2 _ucmpdi2 _fixunsdfdi _fixdfdi _floatdidf _varargs - -# Header files that are made available to programs compiled with gcc. -USER_H = stddef.h assert.h va-i860.h va-mips.h va-pyr.h va-sparc.h \ - va-spur.h limits.h proto.h - -# The files that "belong" in CONFIG_H are deliberately omitted -# because having them there would not be useful in actual practice. -# All they would do is cause complete recompilation every time -# one of the machine description files is edited. -# That may or may not be what one wants to do. -# If it is, rm *.o is an easy way to do it. -# CONFIG_H = config.h tm.h -CONFIG_H = -RTL_H = rtl.h rtl.def machmode.def -TREE_H = tree.h real.h tree.def machmode.def -CPLUS_TREE_H = $(TREE_H) cplus-tree.h c-tree.h - -# Note that dependencies on obstack.h are not written -# because that file is not part of GCC. -# Dependencies on gvarargs.h are not written -# because all that file does, when not compiling with GCC, -# is include the system varargs.h. - -all: config.status gnulib gcc cc1 cpp float.h gnulib2 libg # cc1plus - -# Use this instead of `all' if you need to convert the libraries -# before you can use the compiler. -# Don't forget to do `make gnulib2' before installation. -all-libconvert: config.status gnulib gcc cc1 cpp float.h libg # cc1plus - -lang-c: config.status gnulib gcc cc1 cpp gnulib2 libg -# lang-cplus: config.status gnulib gcc cc1plus cpp gnulib2 libg - -config.status: - @echo You must configure gcc. Look at the INSTALL file for details. - @false - -doc: $(srcdir)/cpp.info $(srcdir)/gplus.info $(srcdir)/gcc.info - -compilations: ${OBJS} - -gcc: gcc.o version.o $(LIBDEPS) - $(CC) $(CFLAGS) $(LDFLAGS) -o gccnew gcc.o version.o $(LIBS) -# Go via `gccnew' to avoid `file busy' if $(CC) is `gcc'. - mv gccnew gcc - -cc1: $(C_OBJS) $(OBJS) $(LIBDEPS) - $(CC) $(CFLAGS) $(LDFLAGS) -o cc1 $(C_OBJS) $(OBJS) $(LIBS) - -cc1plus: $(CPLUS_OBJS) $(OBJS) $(LIBDEPS) - $(CC) $(CFLAGS) $(LDFLAGS) -o cc1plus $(CPLUS_OBJS) $(OBJS) $(LIBS) - -# Make sure there is some libg.a to be found, -# in case compiling with a GCC that was built but not installed. -libg: - if [ ! -f /lib/libg.a -a ! -f /usr/lib/libg.a ]; \ - then ar rc libg.a; else true; fi - -#Library of arithmetic subroutines -# Don't compile this with gcc! -# (That would cause most arithmetic functions to call themselves.) -gnulib: gnulib.c $(CONFIG_H) config.status - -rm -f stamp-gnulib2 - rm -f tmpgnulib gnulib; \ - for name in $(LIBFUNCS); \ - do \ - echo $${name}; \ - rm -f $${name}.c; \ - cp $(srcdir)/gnulib.c $${name}.c; \ - $(OLDCC) $(CCLIBFLAGS) $(INCLUDES) -c -DL$${name} $${name}.c; \ - $(OLDAR) qc tmpgnulib $${name}.o; \ - rm -f $${name}.[co]; \ - done - -if [ -f /usr/bin/ranlib -o -f /bin/ranlib ] ;then $(RANLIB) tmpgnulib; else true; fi -# Actually build it in tmpgnulib above, then rename now, -# so that gnulib itself remains nonexistent if compilation is aborted. - mv tmpgnulib gnulib -# On HPUX, if you are working with the GNU assembler and linker, -# the previous two command lines must be replaced with the following line. -# No change is needed here if you are using the HPUX assembler and linker. -# ../hp-bin/hpxt tmpgnulib gnulib - -gnulib2: stamp-gnulib2; -stamp-gnulib2: gnulib2.c gnulib cc1 gcc cpp $(CONFIG_H) - for name in $(LIB2FUNCS); \ - do \ - echo $${name}; \ - ./gcc -B./ -fstrength-reduce -O $(INCLUDES) $(GNULIB2_CFLAGS) -c -DL$${name} $(srcdir)/gnulib2.c -o $${name}.o; \ - $(AR) rc gnulib $${name}.o; \ - rm -f $${name}.o; \ - done - -if [ -f /usr/bin/ranlib -o -f /bin/ranlib ] ; then $(RANLIB) gnulib; else true; fi -# On HPUX, if you are working with the GNU assembler and linker, -# the previous line must be commented out. -# No change is needed here if you are using the HPUX assembler and linker. - touch stamp-gnulib2 - -float.h: -# Originally, we used `make' rather than $(MAKE), to avoid propagating -# a CC=gcc command option. However, since hard-params is now made -# with $(OLDCC) explicitly, this is no longer important. -# However, $(MAKE) fails on some systems where it isn't defined. -# `make' has the disadvantage of sometimes running the system's make, -# instead of GNU make. And the system's make might not support VPATH. -# However, the compilation of hard-params should not need to use VPATH, -# due to the explicit use of `$(srcdir)'. - make hard-params - -./hard-params -f > float.h - -# Compile hard-params with standard cc. It avoids some headaches. -hard-params: hard-params.o - $(OLDCC) $(HARD_PARAMS_FLAGS) $(LDFLAGS) hard-params.o -o $@ -hard-params.o: $(srcdir)/hard-params.c - -cp $(srcdir)/hard-params.c . > /dev/null 2>&1 - $(OLDCC) $(HARD_PARAMS_FLAGS) $(CPPFLAGS) -DNO_SC -c hard-params.c - -# C language specific files. - -c-parse.tab.o : $(srcdir)/c-parse.tab.c $(CONFIG_H) $(TREE_H) c-parse.h c-tree.h input.h - $(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -c $(srcdir)/c-parse.tab.c -$(srcdir)/c-parse.tab.c : $(srcdir)/c-parse.y - $(BISON) $(BISONFLAGS) $(srcdir)/c-parse.y -o $@ - -c-decl.o : c-decl.c $(CONFIG_H) $(TREE_H) c-tree.h c-parse.h flags.h -c-typeck.o : c-typeck.c $(CONFIG_H) $(TREE_H) c-tree.h flags.h -c-convert.o : c-convert.c $(CONFIG_H) $(TREE_H) - -# C++ language specific files. - -cplus-parse.o : $(srcdir)/cplus-parse.c $(CONFIG_H) $(CPLUS_TREE_H) flags.h - $(CC) -c $(CFLAGS) $(INCLUDES) \ - -DPARSE_OUTPUT=\"$(PWD)/cplus-parse.output\" \ - `echo $(srcdir)/cplus-parse.c | sed 's,^\./,,'` - -$(srcdir)/cplus-parse.h $(srcdir)/cplus-parse.c : $(srcdir)/cplus-parse.y - @echo expect 49 shift/reduce conflicts and 4 reduce/reduce conflicts - $(BISON) $(BISONFLAGS) -d -o $(srcdir)/cplus-parse.c $(srcdir)/cplus-parse.y - -cplus-lex.o : cplus-lex.c $(CONFIG_H) $(CPLUS_TREE_H) $(srcdir)/cplus-parse.h -cplus-decl.o : cplus-decl.c $(CONFIG_H) $(CPLUS_TREE_H) flags.h -cplus-typeck.o : cplus-typeck.c $(CONFIG_H) $(CPLUS_TREE_H) flags.h -cplus-class.o : cplus-class.c $(CONFIG_H) $(CPLUS_TREE_H) -cplus-init.o : cplus-init.c $(CONFIG_H) $(CPLUS_TREE_H) -cplus-method.o : cplus-method.c $(CONFIG_H) $(CPLUS_TREE_H) -cplus-cvt.o : cplus-cvt.c $(CONFIG_H) $(CPLUS_TREE_H) -cplus-search.o : cplus-search.c $(CONFIG_H) $(CPLUS_TREE_H) -new-method.o : new-method.c $(CONFIG_H) $(CPLUS_TREE_H) - -# Language-independent files. - -gcc.o: gcc.c $(CONFIG_H) gvarargs.h obstack.h - $(CC) $(CFLAGS) $(INCLUDES) \ - -DSTANDARD_STARTFILE_PREFIX=\"$(libdir)/\" \ - -DSTANDARD_EXEC_PREFIX=\"$(libdir)/gcc-\" -c \ - `echo $(srcdir)/gcc.c | sed 's,^\./,,'` - -version.o: version.c -obstack.o: obstack.c - -tree.o : tree.c $(CONFIG_H) $(TREE_H) flags.h -print-tree.o : print-tree.c $(CONFIG_H) $(TREE_H) -stor-layout.o : stor-layout.c $(CONFIG_H) $(TREE_H) $(RTL_H) -fold-const.o : fold-const.c $(CONFIG_H) $(TREE_H) -toplev.o : toplev.c $(CONFIG_H) $(TREE_H) $(RTL_H) flags.h input.h - -rtl.o : rtl.c $(CONFIG_H) $(RTL_H) - -rtlanal.o : rtlanal.c $(CONFIG_H) $(RTL_H) - -varasm.o : varasm.c $(CONFIG_H) $(TREE_H) $(RTL_H) flags.h expr.h \ - insn-codes.h hard-reg-set.h -stmt.o : stmt.c $(CONFIG_H) $(RTL_H) $(TREE_H) flags.h \ - insn-flags.h insn-config.h insn-codes.h expr.h regs.h hard-reg-set.h recog.h -expr.o : expr.c $(CONFIG_H) $(RTL_H) $(TREE_H) flags.h \ - insn-flags.h insn-codes.h expr.h insn-config.h recog.h typeclass.h -expmed.o : expmed.c $(CONFIG_H) $(RTL_H) $(TREE_H) flags.h \ - insn-flags.h insn-codes.h expr.h insn-config.h recog.h -explow.o : explow.c $(CONFIG_H) $(RTL_H) $(TREE_H) flags.h expr.h insn-codes.h -optabs.o : optabs.c $(CONFIG_H) $(RTL_H) $(TREE_H) flags.h \ - insn-flags.h insn-codes.h expr.h insn-config.h recog.h -symout.o : symout.c $(CONFIG_H) $(TREE_H) $(RTL_H) symseg.h gdbfiles.h -dbxout.o : dbxout.c $(CONFIG_H) $(TREE_H) $(RTL_H) flags.h -sdbout.o : sdbout.c $(CONFIG_H) $(TREE_H) $(RTL_H) - -emit-rtl.o : emit-rtl.c $(CONFIG_H) $(RTL_H) regs.h insn-config.h real.h - -integrate.o : integrate.c $(CONFIG_H) $(RTL_H) $(TREE_H) flags.h expr.h \ - insn-flags.h insn-codes.h - -jump.o : jump.c $(CONFIG_H) $(RTL_H) flags.h regs.h -stupid.o : stupid.c $(CONFIG_H) $(RTL_H) regs.h hard-reg-set.h - -cse.o : cse.c $(CONFIG_H) $(RTL_H) regs.h hard-reg-set.h flags.h real.h -loop.o : loop.c $(CONFIG_H) $(RTL_H) insn-config.h insn-codes.h \ - regs.h hard-reg-set.h recog.h flags.h expr.h -flow.o : flow.c $(CONFIG_H) $(RTL_H) basic-block.h regs.h hard-reg-set.h -combine.o : combine.c $(CONFIG_H) $(RTL_H) flags.h \ - insn-config.h regs.h basic-block.h recog.h -regclass.o : regclass.c $(CONFIG_H) $(RTL_H) hard-reg-set.h flags.h \ - basic-block.h regs.h insn-config.h recog.h -local-alloc.o : local-alloc.c $(CONFIG_H) $(RTL_H) flags.h basic-block.h regs.h \ - hard-reg-set.h insn-config.h recog.h -global-alloc.o : global-alloc.c $(CONFIG_H) $(RTL_H) flags.h \ - basic-block.h regs.h hard-reg-set.h insn-config.h - -reload.o : reload.c $(CONFIG_H) $(RTL_H) flags.h \ - reload.h recog.h hard-reg-set.h insn-config.h regs.h -reload1.o : reload1.c $(CONFIG_H) $(RTL_H) flags.h \ - reload.h regs.h hard-reg-set.h insn-config.h basic-block.h recog.h -caller-save.o : caller-save.c $(CONFIG_H) $(RTL_H) flags.h \ - reload.h regs.h hard-reg-set.h insn-config.h basic-block.h recog.h -final.o : final.c $(CONFIG_H) $(RTL_H) flags.h regs.h recog.h conditions.h \ - gdbfiles.h insn-config.h real.h output.h -recog.o : recog.c $(CONFIG_H) $(RTL_H) \ - regs.h recog.h hard-reg-set.h insn-config.h real.h - -# Normally this target is not used; but it is used if you -# define ALLOCA=alloca.o. In that case, you must get a suitable alloca.c -# from the GNU Emacs distribution. -# Note some machines won't allow $(CC) without -S on this source file. -alloca.o: alloca.c - $(CC) $(CFLAGS) -S `echo $(srcdir)/alloca.c | sed 's,^\./,,'` - as alloca.s -o alloca.o - -# Now the source files that are generated from the machine description. - -.PRECIOUS: insn-config.h insn-flags.h insn-codes.h \ - insn-emit.c insn-recog.c insn-extract.c insn-output.c insn-peep.c - -# The following pair of rules has this effect: -# genconfig is run only if the md has changed since genconfig was last run; -# but the file insn-config.h is touched only when its contents actually change. - -# Each of the other insn-* files is handled by a similar pair of rules. - -insn-config.h: stamp-config ; -stamp-config : md genconfig $(srcdir)/move-if-change - ./genconfig md > tmp-config.h - $(srcdir)/move-if-change tmp-config.h insn-config.h - touch stamp-config - -insn-flags.h: stamp-flags ; -stamp-flags : md genflags $(srcdir)/move-if-change - ./genflags md > tmp-flags.h - $(srcdir)/move-if-change tmp-flags.h insn-flags.h - touch stamp-flags - -insn-codes.h: stamp-codes ; -stamp-codes : md gencodes $(srcdir)/move-if-change - ./gencodes md > tmp-codes.h - $(srcdir)/move-if-change tmp-codes.h insn-codes.h - touch stamp-codes - -insn-emit.o : insn-emit.c $(CONFIG_H) $(RTL_H) expr.h real.h insn-codes.h \ - insn-config.h insn-flags.h - $(CC) $(CFLAGS) $(INCLUDES) -c insn-emit.c - -insn-emit.c: stamp-emit ; -stamp-emit : md genemit $(srcdir)/move-if-change - ./genemit md > tmp-emit.c - $(srcdir)/move-if-change tmp-emit.c insn-emit.c - touch stamp-emit - -insn-recog.o : insn-recog.c $(CONFIG_H) $(RTL_H) insn-config.h real.h recog.h - $(CC) $(CFLAGS) $(INCLUDES) -c insn-recog.c - -insn-recog.c: stamp-recog ; -stamp-recog : md genrecog $(srcdir)/move-if-change - ./genrecog md > tmp-recog.c - $(srcdir)/move-if-change tmp-recog.c insn-recog.c - touch stamp-recog - -insn-extract.o : insn-extract.c $(CONFIG_H) $(RTL_H) - $(CC) $(CFLAGS) $(INCLUDES) -c insn-extract.c - -insn-extract.c: stamp-extract ; -stamp-extract : md genextract $(srcdir)/move-if-change - ./genextract md > tmp-extract.c - $(srcdir)/move-if-change tmp-extract.c insn-extract.c - touch stamp-extract - -insn-peep.o : insn-peep.c $(CONFIG_H) $(RTL_H) regs.h real.h - $(CC) $(CFLAGS) $(INCLUDES) -c insn-peep.c - -insn-peep.c: stamp-peep ; -stamp-peep : md genpeep $(srcdir)/move-if-change - ./genpeep md > tmp-peep.c - $(srcdir)/move-if-change tmp-peep.c insn-peep.c - touch stamp-peep - -insn-output.o : insn-output.c $(CONFIG_H) $(RTL_H) regs.h real.h conditions.h \ - hard-reg-set.h insn-config.h insn-flags.h output.h aux-output.c - $(CC) $(CFLAGS) $(INCLUDES) -c insn-output.c - -insn-output.c: stamp-output ; -stamp-output : md genoutput $(srcdir)/move-if-change - ./genoutput md > tmp-output.c - $(srcdir)/move-if-change tmp-output.c insn-output.c - touch stamp-output - -# Now the programs that generate those files. -# $(CONFIG_H) is omitted from the deps of the gen*.o -# because these programs don't really depend on anything -# about the target machine. They do depend on config.h itself, -# since that describes the host machine. - -genconfig : genconfig.o rtl.o $(LIBDEPS) - $(CC) $(CFLAGS) $(LDFLAGS) -o genconfig genconfig.o rtl.o $(LIBS) - -genconfig.o : genconfig.c $(RTL_H) config.h - -genflags : genflags.o rtl.o $(LIBDEPS) - $(CC) $(CFLAGS) $(LDFLAGS) -o genflags genflags.o rtl.o $(LIBS) - -genflags.o : genflags.c $(RTL_H) config.h - -gencodes : gencodes.o rtl.o $(LIBDEPS) - $(CC) $(CFLAGS) $(LDFLAGS) -o gencodes gencodes.o rtl.o $(LIBS) - -gencodes.o : gencodes.c $(RTL_H) config.h - -genemit : genemit.o rtl.o $(LIBDEPS) - $(CC) $(CFLAGS) $(LDFLAGS) -o genemit genemit.o rtl.o $(LIBS) - -genemit.o : genemit.c $(RTL_H) config.h - -genrecog : genrecog.o rtl.o $(LIBDEPS) - $(CC) $(CFLAGS) $(LDFLAGS) -o genrecog genrecog.o rtl.o $(LIBS) - -genrecog.o : genrecog.c $(RTL_H) config.h - -genextract : genextract.o rtl.o $(LIBDEPS) - $(CC) $(CFLAGS) $(LDFLAGS) -o genextract genextract.o rtl.o $(LIBS) - -genextract.o : genextract.c $(RTL_H) config.h - -genpeep : genpeep.o rtl.o $(LIBDEPS) - $(CC) $(CFLAGS) $(LDFLAGS) -o genpeep genpeep.o rtl.o $(LIBS) - -genpeep.o : genpeep.c $(RTL_H) config.h - -genoutput : genoutput.o rtl.o $(LIBDEPS) - $(CC) $(CFLAGS) $(LDFLAGS) -o genoutput genoutput.o rtl.o $(LIBS) - -genoutput.o : genoutput.c $(RTL_H) config.h - -# Making the preprocessor -cpp: cccp - -rm -f cpp - ln cccp cpp -cccp: cccp.o cexp.o version.o $(LIBDEPS) - $(CC) $(CFLAGS) $(LDFLAGS) -o cccp cccp.o cexp.o version.o $(LIBS) -cexp.o: $(srcdir)/cexp.c $(CONFIG_H) - $(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -c $(srcdir)/cexp.c -$(srcdir)/cexp.c: $(srcdir)/cexp.y - $(BISON) -o $(srcdir)/cexp.c $(srcdir)/cexp.y -cccp.o: cccp.c $(CONFIG_H) - $(CC) $(CFLAGS) $(INCLUDES) \ - -DGCC_INCLUDE_DIR=\"$(libdir)/gcc-include\" \ - -DGPLUSPLUS_INCLUDE_DIR=\"$(libdir)/g++-include\" \ - -c `echo $(srcdir)/cccp.c | sed 's,^\./,,'` - -$(srcdir)/cpp.info: $(srcdir)/cpp.texinfo - makeinfo `echo $(srcdir)/cpp.texinfo | sed 's,^\./,,'` - -$(srcdir)/gplus.info: $(srcdir)/gplus.texinfo - makeinfo `echo $(srcdir)/gplus.texinfo | sed 's,^\./,,'` - -$(srcdir)/gcc.info: $(srcdir)/gcc.texinfo - makeinfo `echo $(srcdir)/gcc.texinfo | sed 's,^\./,,'` - -# gnulib is not deleted because deleting it would be inconvenient -# for most uses of this target. -clean: - -rm -f $(STAGESTUFF) $(STAGE_GCC) -# Delete the temp files made in the course of building gnulib. - -rm -f tmpgnulib - for name in $(LIBFUNCS); do rm -f $${name}.c; done - -rm -f stamp-*.[ch] tmp-* - -rm -f *.s *.s[0-9] *.co *.greg *.lreg *.combine *.flow *.cse *.jump *.rtl *.tree *.loop *.dbr *.jump2 - -rm -f core float.h hard-params - -# Like clean but also delete the links made to configure gcc. -# Also removes gnulib, since that is desirable if you are changing cpus. -cleanconfig: clean - -rm -f tm.h aux-output.c config.h md config.status gnulib stamp-gnulib2 - -# Get rid of every file that's generated from some other file (except INSTALL). -realclean: cleanconfig - -rm -f cpp.aux cpp.cps cpp.fns cpp.info cpp.kys cpp.pgs cpp.tps cpp.vrs -# -rm -f cplus-parse.tab.c cplus-parse.output - -rm -f c-parse.tab.c c-parse.output c-parse.tab.output - -rm -f gnulib cexp.c TAGS - -rm -f cpp.info* cpp.?? cpp.??s cpp.log cpp.toc cpp.*aux - -rm -f gcc.info* gcc.?? gcc.??s gcc.log gcc.toc gcc.*aux - -rm -f gplus.info* gplus.?? gplus.??s gplus.log gplus.toc gplus.*aux - -rm -f *.dvi - -# Copy the files into directories where they will be run. -install: all $(USER_H) float.h gvarargs.h gstdarg.h gcc.1 - -mkdir $(libdir) - -if [ -f cc1 ] ; then $(INSTALL) cc1 $(libdir)/gcc-cc1 ; else true; fi - -if [ -f cc1plus ] ; then $(INSTALL) cc1plus $(libdir)/gcc-cc1plus ; else true; fi - $(INSTALL) gnulib $(libdir)/gcc-gnulib - -if [ -f /usr/bin/ranlib -o -f /bin/ranlib ] ; then (cd $(libdir); $(RANLIB) gcc-gnulib) ; else true; fi - $(INSTALL) cpp $(libdir)/gcc-cpp - $(INSTALL) gcc $(bindir) - -mkdir $(libdir)/gcc-include - -chmod ugo+rx $(libdir)/gcc-include - for file in $(USER_H); do \ - for eachfile in $(srcdir)/$${file} ; do \ - $(INSTALL) $(srcdir)/`basename $${eachfile}` $(libdir)/gcc-include/`basename $${eachfile}`; \ - done ; done - $(INSTALL) float.h $(libdir)/gcc-include/float.h - $(INSTALL) $(srcdir)/gvarargs.h $(libdir)/gcc-include/varargs.h - $(INSTALL) $(srcdir)/gstdarg.h $(libdir)/gcc-include/stdarg.h - -chmod a-x $(libdir)/gcc-include/*.h - $(INSTALL) $(srcdir)/gcc.1 $(mandir)/gcc.$(manext) - -chmod a-x $(mandir)/gcc.$(manext) -# Make sure -lg won't get an error message from the linker: -# create a library libg.a if there isn't one. - -if [ -f /lib/libg.a -o -f /usr/lib/libg.a ]; then \ - : ; \ - else \ - echo Installing a dummy libg.a into /usr/lib; \ - echo "_no_libg(){}" > _no_libg.c; \ - ./gcc -B./ -c _no_libg.c; \ - $(AR) rc libg.a _no_libg.o; \ - $(INSTALL) libg.a /usr/lib/libg.a; \ - if [ -f /usr/bin/ranlib -o -f /bin/ranlib ] ; then (cd /usr/lib; $(RANLIB) libg.a) ; else true; fi; \ - rm -f _no_libg.[co] libg.a; \ - fi - -# do make -f ../gcc/Makefile maketest DIR=../gcc -# in the intended test directory to make it a suitable test directory. -maketest: - ln -s $(DIR)/*.[chy] . - ln -s $(DIR)/config . - ln -s $(DIR)/*.def . - -rm -f =* - ln -s $(DIR)/.gdbinit . - -ln -s $(DIR)/bison.simple . - ln -s $(DIR)/config.gcc . - ln -s $(DIR)/move-if-change . -# The then and else were swapped to avoid a problem on Ultrix. - if [ ! -f Makefile ] ; then ln -s $(DIR)/Makefile . ; else false; fi - -rm tm.h aux-output.c config.h md - make clean -# You must then run config.gcc to set up for compilation. - -bootstrap: all force - $(MAKE) stage1 - $(MAKE) CC="stage1/gcc -Bstage1/" CFLAGS="-O $(CFLAGS)" libdir=$(libdir) - $(MAKE) stage2 - $(MAKE) CC="stage2/gcc -Bstage2/" CFLAGS="-O $(CFLAGS)" libdir=$(libdir) - -bootstrap2: force - $(MAKE) CC="stage1/gcc -Bstage1/" CFLAGS="-O $(CFLAGS)" libdir=$(libdir) - $(MAKE) stage2 - $(MAKE) CC="stage2/gcc -Bstage2/" CFLAGS="-O $(CFLAGS)" libdir=$(libdir) - -bootstrap3: force - $(MAKE) CC="stage2/gcc -Bstage2/" CFLAGS="-O $(CFLAGS)" libdir=$(libdir) - -# Copy the object files from a particular stage into a subdirectory. -stage1: force - -mkdir stage1 - -mv $(STAGESTUFF) $(STAGE_GCC) stage1 - -rm -f stage1/gnulib - -ln gnulib stage1 || cp gnulib stage1 - -if [ -f /usr/bin/ranlib -o -f /bin/ranlib ] ; then $(RANLIB) stage1/gnulib; else true; fi - -stage2: force - -mkdir stage2 - -mv $(STAGESTUFF) $(STAGE_GCC) stage2 - -rm -f stage2/gnulib - -ln gnulib stage2 || cp gnulib stage2 - -if [ -f /usr/bin/ranlib -o -f /bin/ranlib ] ; then $(RANLIB) stage2/gnulib; else true; fi - -stage3: force - -mkdir stage3 - -mv $(STAGESTUFF) $(STAGE_GCC) stage3 - -rm -f stage3/gnulib - -ln gnulib stage3 || cp gnulib stage3 - -if [ -f /usr/bin/ranlib -o -f /bin/ranlib ] ; then $(RANLIB) stage3/gnulib; else true; fi - -stage4: force - -mkdir stage4 - -mv $(STAGESTUFF) $(STAGE_GCC) stage4 - -rm -f stage4/gnulib - -ln gnulib stage4 || cp gnulib stage4 - -if [ -f /usr/bin/ranlib -o -f /bin/ranlib ] ; then $(RANLIB) stage4/gnulib; else true; fi - -TAGS: force - mkdir temp - -mv c-parse.tab.c cplus-parse.c cplus-parse.h cexp.c temp - etags *.y *.h *.c - mv temp/* . - rmdir temp - -includes: force - export LIB; LIB=$(libdir)/gcc-include ./fixincludes - -#In GNU Make, ignore whether `stage*' exists. -.PHONY: stage1 stage2 stage3 stage4 clean realclean TAGS bootstrap - -force: diff --git a/gnu/usr.bin/gcc1/cc1/PROBLEMS b/gnu/usr.bin/gcc1/cc1/PROBLEMS deleted file mode 100644 index 7477de0932..0000000000 --- a/gnu/usr.bin/gcc1/cc1/PROBLEMS +++ /dev/null @@ -1,128 +0,0 @@ -3. When find_reloads is used to count number of spills needed -it does not take into account the fact that a reload may -turn out to be a dummy. - -I'm not sure this really happens any more. Doesn't it find -all the dummies on both passes? - -10. movl a3@,a0 - movl a3@(16),a1 - clrb a0@(a1:l) -is generated and may be worse than - movl a3@,a0 - addl a3@(16),a0 - clrb a0@ -If ordering of operands is improved, many more -such cases will be generated from typical array accesses. - -23. (memory >> 24) and (memory >> 24) == CONST optimizations -ought to be done machine independently. - -38. Hack expand_mult so that if there is no same-modes multiply -it will use a widening multiply and then truncate rather than -calling the library. - -39. Hack expanding of division to notice cases for -long -> short division. - -40. Represent divide insns as (DIV:SI ...) followed by -a separate lowpart extract. Represent remainder insns as DIV:SI -followed by a separate highpart extract. Then cse can work on -the DIV:SI part. Problem is, this may not be desirable on machines -where computing the quotient alone does not necessarily give -a remainder--such as the 68020 for long operands. - -42. In subst in combine.c at line 704 or so, a reg that really -wants an areg gets a dreg. It is i*4, for indexing. Why? - -52. Reloading can look at how reload_contents got set up. -If it was copied from a register, just reload from that register. -Otherwise, perhaps can change the previous insn to move the -data via the reload reg, thus avoiding one memory ref. - -53. Know that certain library routines do not clobber memory. - -63. Potential problem in cc_status.value2, if it ever activates itself -after a two-address subtraction (which currently cannot happen). -It is supposed to compare the current value of the destination -but eliminating it would use the results of the subtraction, equivalent -to comparing the previous value of the destination. - -65. Should loops that neither start nor end with a break -be rearranged to end with the last break? - -69. Define the floating point converting arithmetic instructions -for the 68881. - -74. Combine loop opt with cse opt in one pass. Do cse on each loop, -then loop opt on that loop, and go from innermost loops outward. -Make loop invariants available for cse at end of loop. - -85. pea can force a value to be reloaded into an areg -which can make it worse than separate adding and pushing. -This can only happen for adding something within addql range -and it only loses if the qty becomes dead at that point -so it can be added to with no copying. - -93. If a pseudo doesn't get a hard reg everywhere, -can it get one during a loop? - -95. Can simplify shift of result of a bfextu. See testunsfld.c. -Likewise and of result of a bfextu. See hyph.c. - -96. Can do SImode bitfield insns without reloading, but must -alter the operands in special ways. - -99. final could check loop-entry branches to see if they -screw up deletion of a test instruction. If they do, -can put another test instruction before the branch and -make it conditional and redirect it. - -106. Aliasing may be impossible if data types of refs differ -and data type of containing objects also differ. -(But check this wrt unions.) - -108. Can speed up flow analysis by making a table saying which -register is set and which registers are used by each instruction that -only sets one register and only uses two. This way avoid the tree -walk for such instructions (most instructions). - -109. It is desirable to avoid converting INDEX to SImode if a -narrower mode suffices, as HImode does on the 68000. -How can this be done? - -110. Possible special combination pattern: -If the two operands to a comparison die there and both come from insns -that are identical except for replacing one operand with the other, -throw away those insns. Ok if insns being discarded are known 1 to 1. -An andl #1 after a seq is 1 to 1, but how should compiler know that? - -112. Can convert float to unsigned int by subtracting a constant, -converting to signed int, and changing the sign bit. - -117. Any number of slow zero-extensions in one loop, that have -their clr insns moved out of the loop, can share one register -if their original life spans are disjoint. -But it may be hard to be sure of this since -the life span data that regscan produces may be hard to interpret -validly or may be incorrect after cse. - -118. In cse, when a bfext insn refers to a register, if the field -corresponds to a halfword or a byte and the register is equivalent -to a memory location, it would be possible to detect this and -replace it with a simple memory reference. - -121. Insns that store two values cannot be moved out of loops. -The code in scan_loop doesn't even try to deal with them. - -122. When insn-output.c turns a bit-test into a sign-test, -it should see whether the cc is already set up with that sign. - -123. When a conditional expression is used as a function arg, it would -be faster (and in some cases shorter) to push each alternative rather -than compute in a register and push that. This would require -being able to specify "push this" as a target for expand_expr. - -124. On the 386, bad code results from foo (bar ()) when bar -returns a double, because the pseudo used fails to get preferenced -into an fp reg because of the distinction between regs 8 and 9. diff --git a/gnu/usr.bin/gcc1/cc1/PROJECTS b/gnu/usr.bin/gcc1/cc1/PROJECTS deleted file mode 100644 index 162e3ebe3f..0000000000 --- a/gnu/usr.bin/gcc1/cc1/PROJECTS +++ /dev/null @@ -1,364 +0,0 @@ -0. Improved efficiency. - -* Parse and output array initializers an element at a time, freeing -storage after each, instead of parsing the whole initializer first and -then outputting. This would reduce memory usage for large -initializers. - -1. Better optimization. - -* Constants in unused inline functions - -It would be nice to delay output of string constants so that string -constants mentioned in unused inline functions are never generated. -Perhaps this would also take care of string constants in dead code. - -The difficulty is in finding a clean way for the RTL which refers -to the constant (currently, only by an assembler symbol name) -to point to the constant and cause it to be output. - -* More cse - -The techniques for doing full global cse are described in the red -dragon book, or (a different version) in Frederick Chow's thesis from -Stanford. It is likely to be slow and use a lot of memory, but it -might be worth offering as an additional option. - -It is probably possible to extend cse to a few very frequent cases -without so much expense. - -For example, it is not very hard to handle cse through if-then -statements with no else clauses. Here's how to do it. On reaching a -label, notice that the label's use-count is 1 and that the last -preceding jump jumps conditionally to this label. Now you know it -is a simple if-then statement. Remove from the hash table -all the expressions that were entered since that jump insn -and you can continue with cse. - -It is probably not hard to handle cse from the end of a loop -around to the beginning, and a few loops would be greatly sped -up by this. - -* Support more general tail-recursion among different functions. - -This might be possible under certain circumstances, such as when -the argument lists of the functions have the same lengths. -Perhaps it could be done with a special declaration. - -You would need to verify in the calling function that it does not -use the addresses of any local variables and does not use setjmp. - -* Put short statics vars at low addresses and use short addressing mode? - -Useful on the 68000/68020 and perhaps on the 32000 series, -provided one has a linker that works with the feature. -This is said to make a 15% speedup on the 68000. - -* Keep global variables in registers. - -Here is a scheme for doing this. A global variable, or a local variable -whose address is taken, can be kept in a register for an entire function -if it does not use non-constant memory addresses and (for globals only) -does not call other functions. If the entire function does not meet -this criterion, a loop may. - -The VAR_DECL for such a variable would have to have two RTL expressions: -the true home in memory, and the pseudo-register used temporarily. -It is necessary to emit insns to copy the memory location into the -pseudo-register at the beginning of the function or loop, and perhaps -back out at the end. These insns should have REG_EQUIV notes so that, -if the pseudo-register does not get a hard register, it is spilled into -the memory location which exists in any case. - -The easiest way to set up these insns is to modify the routine -put_var_into_stack so that it does not apply to the entire function -(sparing any loops which contain nothing dangerous) and to call it at -the end of the function regardless of where in the function the -address of a local variable is taken. It would be called -unconditionally at the end of the function for all relevant global -variables. - -For debugger output, the thing to do is to invent a new binding level -around the appropriate loop and define the variable name as a register -variable with that scope. - -* Live-range splitting. - -Currently a variable is allocated a hard register either for the full -extent of its use or not at all. Sometimes it would be good to -allocate a variable a hard register for just part of a function; for -example, through a particular loop where the variable is mostly used, -or outside of a particular loop where the variable is not used. (The -latter is nice because it might let the variable be in a register most -of the time even though the loop needs all the registers.) - -It might not be very hard to do this in global-alloc.c when a variable -fails to get a hard register for its entire life span. - -The first step is to find a loop in which the variable is live, but -which is not the whole life span or nearly so. It's probably best to -use a loop in which the variable is heavily used. - -Then create a new pseudo-register to represent the variable in that loop. -Substitute this for the old pseudo-register there, and insert move insns -to copy between the two at the loop entry and all exits. (When several -such moves are inserted at the same place, some new feature should be -added to say that none of those registers conflict merely because of -overlap between the new moves. And the reload pass should reorder them -so that a store precedes a load, for any given hard register.) - -After doing this for all the reasonable candidates, run global-alloc -over again. With luck, one of the two pseudo-registers will be fit -somewhere. It may even have a much higher priority due to its reduced -life span. - -There will be no room in general for the new pseudo-registers in -basic_block_live_at_start, so there will need to be a second such -matrix exclusively for the new ones. Various other vectors indexed by -register number will have to be made bigger, or there will have to be -secondary extender vectors just for global-alloc. - -A simple new feature could arrange that both pseudo-registers get the -same stack slot if they both fail to get hard registers. - -Other compilers split live ranges when they are not connected, or -try to split off pieces `at the edge'. I think splitting around loops -will provide more speedup. - -Creating a fake binding block and a new like-named variable with -shorter life span and different address might succeed in describing -this technique for the debugger. - -* Detect dead stores into memory? - -A store into memory is dead if it is followed by another store into -the same location; and, in between, there is no reference to anything -that might be that location (including no reference to a variable -address). - -* Loop optimization. - -Strength reduction and iteration variable elimination could be -smarter. They should know how to decide which iteration variables are -not worth making explicit because they can be computed as part of an -address calculation. Based on this information, they should decide -when it is desirable to eliminate one iteration variable and create -another in its place. - -It should be possible to compute what the value of an iteration -variable will be at the end of the loop, and eliminate the variable -within the loop by computing that value at the loop end. - -When a loop has a simple increment that adds 1, -instead of jumping in after the increment, -decrement the loop count and jump to the increment. -This allows aob insns to be used. - -* Using constraints on values. - -Many operations could be simplified based on knowledge of the -minimum and maximum possible values of a register at any particular time. -These limits could come from the data types in the tree, via rtl generation, -or they can be deduced from operations that are performed. For example, -the result of an `and' operation one of whose operands is 7 must be in -the range 0 to 7. Compare instructions also tell something about the -possible values of the operand, in the code beyond the test. - -Value constraints can be used to determine the results of a further -comparison. They can also indicate that certain `and' operations are -redundant. Constraints might permit a decrement and branch -instruction that checks zeroness to be used when the user has -specified to exit if negative. - -* Smarter reload pass. - -The reload pass as currently written can reload values only into registers -that are reserved for reloading. This means that in order to use a -register for reloading it must spill everything out of that register. - -It would be straightforward, though complicated, for reload1.c to keep -track, during its scan, of which hard registers were available at each -point in the function, and use for reloading even registers that were -free only at the point they were needed. This would avoid much spilling -and make better code. - -* Change the type of a variable. - -Sometimes a variable is declared as `int', it is assigned only once -from a value of type `char', and then it is used only by comparison -against constants. On many machines, better code would result if -the variable had type `char'. If the compiler could detect this -case, it could change the declaration of the variable and change -all the places that use it. - -* Order of subexpressions. - -It might be possible to make better code by paying attention -to the order in which to generate code for subexpressions of an expression. - -* More code motion. - -Consider hoisting common code up past conditional branches or -tablejumps. - -* Trace scheduling. - -This technique is said to be able to figure out which way a jump -will usually go, and rearrange the code to make that path the -faster one. - -* Distributive law. - -The C expression *(X + 4 * (Y + C)) compiles better on certain -machines if rewritten as *(X + 4*C + 4*Y) because of known addressing -modes. It may be tricky to determine when, and for which machines, to -use each alternative. - -Some work has been done on this, in combine.c. - -* Can optimize by changing if (x) y; else z; into z; if (x) y; -if z and x do not interfere and z has no effects not undone by y. -This is desirable if z is faster than jumping. - -* For a two-insn loop on the 68020, such as - foo: movb a2@+,a3@+ - jne foo -it is better to insert dbeq d0,foo before the jne. -d0 can be a junk register. The challenge is to fit this into -a portable framework: when can you detect this situation and -still be able to allocate a junk register? - -2. Simpler porting. - -Right now, describing the target machine's instructions is done -cleanly, but describing its addressing mode is done with several -ad-hoc macro definitions. Porting would be much easier if there were -an RTL description for addressing modes like that for instructions. -Tools analogous to genflags and genrecog would generate macros from -this description. - -There would be one pattern in the address-description file for each -kind of addressing, and this pattern would have: - - * the RTL expression for the address - * C code to verify its validity (since that may depend on - the exact data). - * C code to print the address in assembler language. - * C code to convert the address into a valid one, if it is not valid. - (This would replace LEGITIMIZE_ADDRESS). - * Register constraints for all indeterminates that appear - in the RTL expression. - -3. Other languages. - -Front ends for Pascal, Fortran, Algol, Cobol, Modula-2 and Ada are -desirable. - -Pascal, Modula-2 and Ada require the implementation of functions -within functions. Some of the mechanisms for this already exist. - -4. More extensions. - -* Label-addresses as expressions. - -It would be nice to have access to the addresses of labels; to be able to -store them in variables, or initialize vectors of them. - -Alas, `&label0' is the address of the variable named label0, which is -unrelated to the label with that name. Some other syntax is needed. -Perhaps colon as a unary operator? That is ambiguous with `?:' with -the middle operand omitted. Perhaps ^ as a unary operator? Perhaps -`__label__ label0' could mean the value of label0? Its type could be -`void *'. `goto *EXP' could be used to go to a value of type `void -*'--no ambiguity there. - -Jump optimization and flow analysis must know about computed jumps, -but that is not hard. Each basic block headed by a possible target of -computed jumps must be considered a successor of each basic block -ending in a computed jump. Aside from this, I believe no other -optimizer changes are needed. - -Next question: stack levels. In most functions, there is no problem, -but it would be a shame to make a feature that doesn't work together -with other features. Here is an idea: - -For each label that might need stack level restoration, construct a -shadow-label which will restore the stack and jump to the user-label. -Then use the address of the shadow label for label0 when someone asks -for that of label0. Jump optimization will delete all the shadow labels -if the function has no computed gotos. - -* Generated unique labels. Have some way of generating distinct labels -for use in extended asm statements. I don't know what a good syntax would -be. - -5. Generalize the machine model. - -* Some new compiler features may be needed to do a good job on machines -where static data needs to be addressed using base registers. - -* Some machines have two stacks in different areas of memory, one used -for scalars and another for large objects. The compiler does not -now have a way to understand this. - -6. Better documentation of how GCC works and how to port it. - -Here is an outline proposed by Allan Adler. - -I. Overview of this document -II. The machines on which GCC is implemented - A. Prose description of those characteristics of target machines and - their operating systems which are pertinent to the implementation - of GCC. - i. target machine characteristics - ii. comparison of this system of machine characteristics with - other systems of machine specification currently in use - B. Tables of the characteristics of the target machines on which - GCC is implemented. - C. A priori restrictions on the values of characteristics of target - machines, with special reference to those parts of the source code - which entail those restrictions - i. restrictions on individual characteristics - ii. restrictions involving relations between various characteristics - D. The use of GCC as a cross-compiler - i. cross-compilation to existing machines - ii. cross-compilation to non-existent machines - E. Assumptions which are made regarding the target machine - i. assumptions regarding the architecture of the target machine - ii. assumptions regarding the operating system of the target machine - iii. assumptions regarding software resident on the target machine - iv. where in the source code these assumptions are in effect made -III. A systematic approach to writing the files tm.h and xm.h - A. Macros which require special care or skill - B. Examples, with special reference to the underlying reasoning -IV. A systematic approach to writing the machine description file md - A. Minimal viable sets of insn descriptions - B. Examples, with special reference to the underlying reasoning -V. Uses of the file aux-output.c -VI. Specification of what constitutes correct performance of an - implementation of GCC - A. The components of GCC - B. The itinerary of a C program through GCC - C. A system of benchmark programs - D. What your RTL and assembler should look like with these benchmarks - E. Fine tuning for speed and size of compiled code -VII. A systematic procedure for debugging an implementation of GCC - A. Use of GDB - i. the macros in the file .gdbinit for GCC - ii. obstacles to the use of GDB - a. functions implemented as macros can't be called in GDB - B. Debugging without GDB - i. How to turn off the normal operation of GCC and access specific - parts of GCC - C. Debugging tools - D. Debugging the parser - i. how machine macros and insn definitions affect the parser - E. Debugging the recognizer - i. how machine macros and insn definitions affect the recognizer - -ditto for other components - -VIII. Data types used by GCC, with special reference to restrictions not - specified in the formal definition of the data type -IX. References to the literature for the algorithms used in GCC - diff --git a/gnu/usr.bin/gcc1/cc1/README b/gnu/usr.bin/gcc1/cc1/README deleted file mode 100644 index 3ef4c425a4..0000000000 --- a/gnu/usr.bin/gcc1/cc1/README +++ /dev/null @@ -1,15 +0,0 @@ -This directory contains the version 1.38 release of the GNU C compiler. -All bugs reported for previous test releases have been fixed. -This version probably does not have very many bugs, and we no longer -consider it a test version. - -See the file gcc.texinfo for installation and porting information. -The file INSTALL contains a copy of the installation information. - -The GNU C compiler is free software. See the file COPYING for copying -permission. - -The files print-self.c and print-self1.c are not part of GCC. -They are programs that print themselves on standard output. -They were written by Dario Dariol and Giovanni Cozzi, and are -included for your hacking pleasure. diff --git a/gnu/usr.bin/gcc1/cc1/assert.h b/gnu/usr.bin/gcc1/cc1/assert.h deleted file mode 100644 index 0cc8b8d438..0000000000 --- a/gnu/usr.bin/gcc1/cc1/assert.h +++ /dev/null @@ -1,32 +0,0 @@ -/* Allow this file to be included multiple times - with different settings of NDEBUG. */ -#undef assert -#undef __assert - -#ifdef NDEBUG -#define assert(ignore) ((void)0) -#else - -void __eprintf (); /* Defined in gnulib */ - -#ifdef __STDC__ - -#define assert(expression) \ - ((void) ((expression) ? 0 : __assert (#expression, __FILE__, __LINE__))) - -#define __assert(expression, file, lineno) \ - (__eprintf ("Failed assertion `%s' at line %d of `%s'.\n", \ - expression, lineno, file), 0) - -#else /* no __STDC__; i.e. -traditional. */ - -#define assert(expression) \ - ((void) ((expression) ? 0 : __assert (expression, __FILE__, __LINE__))) - -#define __assert(expression, file, lineno) \ - (__eprintf ("Failed assertion `%s' at line %d of `%s'.\n", \ - "expression", lineno, file), 0) - -#endif /* no __STDC__; i.e. -traditional. */ - -#endif diff --git a/gnu/usr.bin/gcc1/cc1/basic-block.h b/gnu/usr.bin/gcc1/cc1/basic-block.h deleted file mode 100644 index 42d4cc94ed..0000000000 --- a/gnu/usr.bin/gcc1/cc1/basic-block.h +++ /dev/null @@ -1,62 +0,0 @@ -/* Define control and data flow tables, and regsets. - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* Define the type for a pointer to a set with a bit for each - (hard or pseudo) register. */ - -typedef long *regset; - -/* Size of a regset for the current function, - in (1) bytes and (2) elements. */ - -extern int regset_bytes; -extern int regset_size; - -/* Number of bits in each actual element of a regset. */ - -#define REGSET_ELT_BITS HOST_BITS_PER_INT - -/* Number of basic blocks in the current function. */ - -extern int n_basic_blocks; - -/* Index by basic block number, get first insn in the block. */ - -extern rtx *basic_block_head; - -/* Index by basic block number, get last insn in the block. */ - -extern rtx *basic_block_end; - -/* Index by basic block number, get address of regset - describing the registers live at the start of that block. */ - -extern regset *basic_block_live_at_start; - -/* Indexed by n, gives number of basic block that (REG n) is used in. - If the value is REG_BLOCK_GLOBAL (-2), - it means (REG n) is used in more than one basic block. - REG_BLOCK_UNKNOWN (-1) means it hasn't been seen yet so we don't know. - This information remains valid for the rest of the compilation - of the current function; it is used to control register allocation. */ - -#define REG_BLOCK_UNKNOWN -1 -#define REG_BLOCK_GLOBAL -2 -extern short *reg_basic_block; diff --git a/gnu/usr.bin/gcc1/cc1/c-convert.c b/gnu/usr.bin/gcc1/cc1/c-convert.c deleted file mode 100644 index 85ac17bc2d..0000000000 --- a/gnu/usr.bin/gcc1/cc1/c-convert.c +++ /dev/null @@ -1,397 +0,0 @@ -/* Language-level data type conversion for GNU C. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This file contains the functions for converting C expressions - to different data types. The only entry point is `convert'. - Every language front end must have a `convert' function - but what kind of conversions it does will depend on the language. */ - -#include "config.h" -#include "tree.h" - -/* Change of width--truncation and extension of integers or reals-- - is represented with NOP_EXPR. Proper functioning of many things - assumes that no other conversions can be NOP_EXPRs. - - Conversion between integer and pointer is represented with CONVERT_EXPR. - Converting integer to real uses FLOAT_EXPR - and real to integer uses FIX_TRUNC_EXPR. - - Here is a list of all the functions that assume that widening and - narrowing is always done with a NOP_EXPR: - In c-convert.c, convert_to_integer. - In c-typeck.c, build_binary_op_nodefault (boolean ops), - and truthvalue_conversion. - In expr.c: expand_expr, for operands of a MULT_EXPR. - In fold-const.c: fold. - In tree.c: get_narrower and get_unwidened. */ - -/* Subroutines of `convert'. */ - -static tree -convert_to_pointer (type, expr) - tree type, expr; -{ - register tree intype = TREE_TYPE (expr); - register enum tree_code form = TREE_CODE (intype); - - if (integer_zerop (expr)) - { - if (type == TREE_TYPE (null_pointer_node)) - return null_pointer_node; - expr = build_int_2 (0, 0); - TREE_TYPE (expr) = type; - return expr; - } - - if (form == POINTER_TYPE) - return build (NOP_EXPR, type, expr); - - - if (form == INTEGER_TYPE || form == ENUMERAL_TYPE) - { - if (type_precision (intype) == POINTER_SIZE) - return build (CONVERT_EXPR, type, expr); - return convert_to_pointer (type, - convert (type_for_size (POINTER_SIZE, 0), - expr)); - } - - error ("cannot convert to a pointer type"); - - return null_pointer_node; -} - -static tree -convert_to_real (type, expr) - tree type, expr; -{ - register enum tree_code form = TREE_CODE (TREE_TYPE (expr)); - extern int flag_float_store; - - if (form == REAL_TYPE) - return build (flag_float_store ? CONVERT_EXPR : NOP_EXPR, - type, expr); - - if (form == INTEGER_TYPE || form == ENUMERAL_TYPE) - return build (FLOAT_EXPR, type, expr); - - if (form == POINTER_TYPE) - error ("pointer value used where a float was expected"); - else - error ("aggregate value used where a float was expected"); - - { - register tree tem = make_node (REAL_CST); - TREE_TYPE (tem) = type; - TREE_REAL_CST (tem) = REAL_VALUE_ATOF ("0.0"); - return tem; - } -} - -/* The result of this is always supposed to be a newly created tree node - not in use in any existing structure. */ - -static tree -convert_to_integer (type, expr) - tree type, expr; -{ - register tree intype = TREE_TYPE (expr); - register enum tree_code form = TREE_CODE (intype); - extern tree build_binary_op_nodefault (); - extern tree build_unary_op (); - - if (form == POINTER_TYPE) - { - if (integer_zerop (expr)) - expr = integer_zero_node; - else - expr = fold (build (CONVERT_EXPR, - type_for_size (POINTER_SIZE, 0), expr)); - intype = TREE_TYPE (expr); - form = TREE_CODE (intype); - if (intype == type) - return expr; - } - - if (form == INTEGER_TYPE || form == ENUMERAL_TYPE) - { - register int outprec = TYPE_PRECISION (type); - register int inprec = TYPE_PRECISION (intype); - register enum tree_code ex_form = TREE_CODE (expr); - - if (outprec >= inprec) - return build (NOP_EXPR, type, expr); - -/* Here detect when we can distribute the truncation down past some arithmetic. - For example, if adding two longs and converting to an int, - we can equally well convert both to ints and then add. - For the operations handled here, such truncation distribution - is always safe. - It is desirable in these cases: - 1) when truncating down to full-word from a larger size - 2) when truncating takes no work. - 3) when at least one operand of the arithmetic has been extended - (as by C's default conversions). In this case we need two conversions - if we do the arithmetic as already requested, so we might as well - truncate both and then combine. Perhaps that way we need only one. - - Note that in general we cannot do the arithmetic in a type - shorter than the desired result of conversion, even if the operands - are both extended from a shorter type, because they might overflow - if combined in that type. The exceptions to this--the times when - two narrow values can be combined in their narrow type even to - make a wider result--are handled by "shorten" in build_binary_op. */ - - switch (ex_form) - { - case RSHIFT_EXPR: - /* We can pass truncation down through right shifting - when the shift count is a negative constant. */ - if (TREE_CODE (TREE_OPERAND (expr, 1)) != INTEGER_CST - || TREE_INT_CST_LOW (TREE_OPERAND (expr, 1)) > 0) - break; - goto trunc1; - - case LSHIFT_EXPR: - /* We can pass truncation down through left shifting - when the shift count is a positive constant. */ - if (TREE_CODE (TREE_OPERAND (expr, 1)) != INTEGER_CST - || TREE_INT_CST_LOW (TREE_OPERAND (expr, 1)) < 0) - break; - /* In this case, shifting is like multiplication. */ - goto trunc1; - - case MAX_EXPR: - case MIN_EXPR: - case MULT_EXPR: - { - tree arg0 = get_unwidened (TREE_OPERAND (expr, 0), type); - tree arg1 = get_unwidened (TREE_OPERAND (expr, 1), type); - - /* Don't distribute unless the output precision is at least as big - as the actual inputs. Otherwise, the comparison of the - truncated values will be wrong. */ - if (outprec >= TYPE_PRECISION (TREE_TYPE (arg0)) - && outprec >= TYPE_PRECISION (TREE_TYPE (arg1)) - /* If signedness of arg0 and arg1 don't match, - we can't necessarily find a type to compare them in. */ - && (TREE_UNSIGNED (TREE_TYPE (arg0)) - == TREE_UNSIGNED (TREE_TYPE (arg1)))) - goto trunc1; - break; - } - - case PLUS_EXPR: - case MINUS_EXPR: - case BIT_AND_EXPR: - case BIT_IOR_EXPR: - case BIT_XOR_EXPR: - case BIT_ANDTC_EXPR: - trunc1: - { - tree arg0 = get_unwidened (TREE_OPERAND (expr, 0), type); - tree arg1 = get_unwidened (TREE_OPERAND (expr, 1), type); - - if (outprec >= BITS_PER_WORD - || TRULY_NOOP_TRUNCATION (outprec, inprec) - || inprec > TYPE_PRECISION (TREE_TYPE (arg0)) - || inprec > TYPE_PRECISION (TREE_TYPE (arg1))) - { - /* Do the arithmetic in type TYPEX, - then convert result to TYPE. */ - register tree typex = type; - - /* Can't do arithmetic in enumeral types - so use an integer type that will hold the values. */ - if (TREE_CODE (typex) == ENUMERAL_TYPE) - typex = type_for_size (TYPE_PRECISION (typex), - TREE_UNSIGNED (typex)); - - /* But now perhaps TYPEX is as wide as INPREC. - In that case, do nothing special here. - (Otherwise would recurse infinitely in convert. */ - if (TYPE_PRECISION (typex) != inprec) - { - /* Don't do unsigned arithmetic where signed was wanted, - or vice versa. - Exception: if the original operands were unsigned - then can safely do the work as unsigned. - And we may need to do it as unsigned - if we truncate to the original size. */ - typex = ((TREE_UNSIGNED (TREE_TYPE (expr)) - || TREE_UNSIGNED (TREE_TYPE (arg0))) - ? unsigned_type (typex) : signed_type (typex)); - return convert (type, - build_binary_op_nodefault (ex_form, - convert (typex, arg0), - convert (typex, arg1), - ex_form)); - } - } - } - break; - - case EQ_EXPR: - case NE_EXPR: - case GT_EXPR: - case GE_EXPR: - case LT_EXPR: - case LE_EXPR: - case TRUTH_AND_EXPR: - case TRUTH_ANDIF_EXPR: - case TRUTH_OR_EXPR: - case TRUTH_ORIF_EXPR: - case TRUTH_NOT_EXPR: - /* If we want result of comparison converted to a byte, - we can just regard it as a byte, since it is 0 or 1. */ - TREE_TYPE (expr) = type; - return expr; - - case NEGATE_EXPR: - case BIT_NOT_EXPR: - case ABS_EXPR: - { - register tree typex = type; - - /* Can't do arithmetic in enumeral types - so use an integer type that will hold the values. */ - if (TREE_CODE (typex) == ENUMERAL_TYPE) - typex = type_for_size (TYPE_PRECISION (typex), - TREE_UNSIGNED (typex)); - - /* But now perhaps TYPEX is as wide as INPREC. - In that case, do nothing special here. - (Otherwise would recurse infinitely in convert. */ - if (TYPE_PRECISION (typex) != inprec) - { - /* Don't do unsigned arithmetic where signed was wanted, - or vice versa. */ - typex = (TREE_UNSIGNED (TREE_TYPE (expr)) - ? unsigned_type (typex) : signed_type (typex)); - return convert (type, - build_unary_op (ex_form, - convert (typex, TREE_OPERAND (expr, 0)), - 1)); - } - } - - case NOP_EXPR: - /* If truncating after truncating, might as well do all at once. - If truncating after extending, we may get rid of wasted work. */ - return convert (type, get_unwidened (TREE_OPERAND (expr, 0), type)); - - case COND_EXPR: - /* Can treat the two alternative values like the operands - of an arithmetic expression. */ - { - tree arg1 = get_unwidened (TREE_OPERAND (expr, 1), type); - tree arg2 = get_unwidened (TREE_OPERAND (expr, 2), type); - - if (outprec >= BITS_PER_WORD - || TRULY_NOOP_TRUNCATION (outprec, inprec) - || inprec > TYPE_PRECISION (TREE_TYPE (arg1)) - || inprec > TYPE_PRECISION (TREE_TYPE (arg2))) - { - /* Do the arithmetic in type TYPEX, - then convert result to TYPE. */ - register tree typex = type; - - /* Can't do arithmetic in enumeral types - so use an integer type that will hold the values. */ - if (TREE_CODE (typex) == ENUMERAL_TYPE) - typex = type_for_size (TYPE_PRECISION (typex), - TREE_UNSIGNED (typex)); - - /* But now perhaps TYPEX is as wide as INPREC. - In that case, do nothing special here. - (Otherwise would recurse infinitely in convert. */ - if (TYPE_PRECISION (typex) != inprec) - { - /* Don't do unsigned arithmetic where signed was wanted, - or vice versa. */ - typex = (TREE_UNSIGNED (TREE_TYPE (expr)) - ? unsigned_type (typex) : signed_type (typex)); - return convert (type, - build (COND_EXPR, typex, - TREE_OPERAND (expr, 0), - convert (typex, arg1), - convert (typex, arg2))); - } - } - } - - } - - return build (NOP_EXPR, type, expr); - } - - if (form == REAL_TYPE) - return build (FIX_TRUNC_EXPR, type, expr); - - error ("aggregate value used where an integer was expected"); - - { - register tree tem = build_int_2 (0, 0); - TREE_TYPE (tem) = type; - return tem; - } -} - -/* Create an expression whose value is that of EXPR, - converted to type TYPE. The TREE_TYPE of the value - is always TYPE. This function implements all reasonable - conversions; callers should filter out those that are - not permitted by the language being compiled. */ - -tree -convert (type, expr) - tree type, expr; -{ - register tree e = expr; - register enum tree_code code = TREE_CODE (type); - - if (type == TREE_TYPE (expr) || TREE_CODE (expr) == ERROR_MARK) - return expr; - if (TREE_CODE (TREE_TYPE (expr)) == ERROR_MARK) - return error_mark_node; - if (TREE_CODE (TREE_TYPE (expr)) == VOID_TYPE) - { - error ("void value not ignored as it ought to be"); - return error_mark_node; - } - if (code == VOID_TYPE) - return build (CONVERT_EXPR, type, e); -#if 0 - /* This is incorrect. A truncation can't be stripped this way. - Extensions will be stripped by the use of get_unwidened. */ - if (TREE_CODE (expr) == NOP_EXPR) - return convert (type, TREE_OPERAND (expr, 0)); -#endif - if (code == INTEGER_TYPE || code == ENUMERAL_TYPE) - return fold (convert_to_integer (type, e)); - if (code == POINTER_TYPE) - return fold (convert_to_pointer (type, e)); - if (code == REAL_TYPE) - return fold (convert_to_real (type, e)); - - error ("conversion to non-scalar type requested"); - return error_mark_node; -} diff --git a/gnu/usr.bin/gcc1/cc1/c-decl.c b/gnu/usr.bin/gcc1/cc1/c-decl.c deleted file mode 100644 index b4bec6e473..0000000000 --- a/gnu/usr.bin/gcc1/cc1/c-decl.c +++ /dev/null @@ -1,4060 +0,0 @@ -/*- - * This code is derived from software copyrighted by the Free Software - * Foundation. - * - * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. - */ - -#ifndef lint -static char sccsid[] = "@(#)c-decl.c 6.3 (Berkeley) 5/8/91"; -#endif /* not lint */ - -/* Process declarations and variables for C compiler. - Copyright (C) 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* Process declarations and symbol lookup for C front end. - Also constructs types; the standard scalar types at initialization, - and structure, union, array and enum types when they are declared. */ - -/* ??? not all decl nodes are given the most useful possible - line numbers. For example, the CONST_DECLs for enum values. */ - -#include "config.h" -#include "tree.h" -#include "flags.h" -#include "c-tree.h" -#include "c-parse.h" - -#include - -/* In grokdeclarator, distinguish syntactic contexts of declarators. */ -enum decl_context -{ NORMAL, /* Ordinary declaration */ - FUNCDEF, /* Function definition */ - PARM, /* Declaration of parm before function body */ - FIELD, /* Declaration inside struct or union */ - TYPENAME}; /* Typename (inside cast or sizeof) */ - -#define NULL 0 -#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) -#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) - -#ifndef CHAR_TYPE_SIZE -#define CHAR_TYPE_SIZE BITS_PER_UNIT -#endif - -#ifndef SHORT_TYPE_SIZE -#define SHORT_TYPE_SIZE (BITS_PER_UNIT * MIN ((UNITS_PER_WORD + 1) / 2, 2)) -#endif - -#ifndef INT_TYPE_SIZE -#define INT_TYPE_SIZE BITS_PER_WORD -#endif - -#ifndef LONG_TYPE_SIZE -#define LONG_TYPE_SIZE BITS_PER_WORD -#endif - -#ifndef LONG_LONG_TYPE_SIZE -#define LONG_LONG_TYPE_SIZE (BITS_PER_WORD * 2) -#endif - -#ifndef FLOAT_TYPE_SIZE -#define FLOAT_TYPE_SIZE BITS_PER_WORD -#endif - -#ifndef DOUBLE_TYPE_SIZE -#define DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2) -#endif - -#ifndef LONG_DOUBLE_TYPE_SIZE -#define LONG_DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2) -#endif - -/* a node which has tree code ERROR_MARK, and whose type is itself. - All erroneous expressions are replaced with this node. All functions - that accept nodes as arguments should avoid generating error messages - if this node is one of the arguments, since it is undesirable to get - multiple error messages from one error in the input. */ - -tree error_mark_node; - -/* INTEGER_TYPE and REAL_TYPE nodes for the standard data types */ - -tree short_integer_type_node; -tree integer_type_node; -tree long_integer_type_node; -tree long_long_integer_type_node; - -tree short_unsigned_type_node; -tree unsigned_type_node; -tree long_unsigned_type_node; -tree long_long_unsigned_type_node; - -tree unsigned_char_type_node; -tree signed_char_type_node; -tree char_type_node; - -tree float_type_node; -tree double_type_node; -tree long_double_type_node; - -/* a VOID_TYPE node. */ - -tree void_type_node; - -/* A node for type `void *'. */ - -tree ptr_type_node; - -/* A node for type `char *'. */ - -tree string_type_node; - -/* Type `char[256]' or something like it. - Used when an array of char is needed and the size is irrelevant. */ - -tree char_array_type_node; - -/* Type `int[256]' or something like it. - Used when an array of int needed and the size is irrelevant. */ - -tree int_array_type_node; - -/* type `int ()' -- used for implicit declaration of functions. */ - -tree default_function_type; - -/* function types `double (double)' and `double (double, double)', etc. */ - -tree double_ftype_double, double_ftype_double_double; -tree int_ftype_int, long_ftype_long; - -/* Function type `void (void *, void *, int)' and similar ones */ - -tree void_ftype_ptr_ptr_int, int_ftype_ptr_ptr_int, void_ftype_ptr_int_int; - -/* Two expressions that are constants with value zero. - The first is of type `int', the second of type `void *'. */ - -tree integer_zero_node; -tree null_pointer_node; - -/* A node for the integer constant 1. */ - -tree integer_one_node; - -/* An identifier whose name is . This is used as the "name" - of the RESULT_DECLs for values of functions. */ - -tree value_identifier; - -/* While defining an enum type, this is 1 plus the last enumerator - constant value. */ - -static tree enum_next_value; - -/* Parsing a function declarator leaves a list of parameter names - or a chain or parameter decls here. */ - -static tree last_function_parms; - -/* Parsing a function declarator leaves here a chain of structure - and enum types declared in the parmlist. */ - -static tree last_function_parm_tags; - -/* After parsing the declarator that starts a function definition, - `start_function' puts here the list of parameter names or chain of decls. - `store_parm_decls' finds it here. */ - -static tree current_function_parms; - -/* Similar, for last_function_parm_tags. */ -static tree current_function_parm_tags; - -/* A list (chain of TREE_LIST nodes) of all LABEL_STMTs in the function - that have names. Here so we can clear out their names' definitions - at the end of the function. */ - -static tree named_labels; - -/* The FUNCTION_DECL for the function currently being compiled, - or 0 if between functions. */ -tree current_function_decl; - -/* Set to 0 at beginning of a function definition, set to 1 if - a return statement that specifies a return value is seen. */ - -int current_function_returns_value; - -/* Set to 0 at beginning of a function definition, set to 1 if - a return statement with no argument is seen. */ - -int current_function_returns_null; - -/* Set to nonzero by `grokdeclarator' for a function - whose return type is defaulted, if warnings for this are desired. */ - -static int warn_about_return_type; - -/* Nonzero when starting a function delcared `extern inline'. */ - -static int current_extern_inline; - -/* For each binding contour we allocate a binding_level structure - * which records the names defined in that contour. - * Contours include: - * 0) the global one - * 1) one for each function definition, - * where internal declarations of the parameters appear. - * 2) one for each compound statement, - * to record its declarations. - * - * The current meaning of a name can be found by searching the levels from - * the current one out to the global one. - */ - -/* Note that the information in the `names' component of the global contour - is duplicated in the IDENTIFIER_GLOBAL_VALUEs of all identifiers. */ - -struct binding_level - { - /* A chain of _DECL nodes for all variables, constants, functions, - and typedef types. These are in the reverse of the order supplied. - */ - tree names; - - /* A list of structure, union and enum definitions, - * for looking up tag names. - * It is a chain of TREE_LIST nodes, each of whose TREE_PURPOSE is a name, - * or NULL_TREE; and whose TREE_VALUE is a RECORD_TYPE, UNION_TYPE, - * or ENUMERAL_TYPE node. - */ - tree tags; - - /* For each level, a list of shadowed outer-level local definitions - to be restored when this level is popped. - Each link is a TREE_LIST whose TREE_PURPOSE is an identifier and - whose TREE_VALUE is its old definition (a kind of ..._DECL node). */ - tree shadowed; - - /* For each level (except not the global one), - a chain of LET_STMT nodes for all the levels - that were entered and exited one level down. */ - tree blocks; - - /* The binding level which this one is contained in (inherits from). */ - struct binding_level *level_chain; - - /* Nonzero for the level that holds the parameters of a function. */ - char parm_flag; - - /* Nonzero if this level "doesn't exist" for tags. */ - char tag_transparent; - - /* Nonzero means make a LET_STMT for this level regardless of all else. */ - char keep; - - /* Nonzero means make a LET_STMT if this level has any subblocks. */ - char keep_if_subblocks; - - /* Number of decls in `names' that have incomplete - structure or union types. */ - int n_incomplete; - }; - -#define NULL_BINDING_LEVEL (struct binding_level *) NULL - -/* The binding level currently in effect. */ - -static struct binding_level *current_binding_level; - -/* A chain of binding_level structures awaiting reuse. */ - -static struct binding_level *free_binding_level; - -/* The outermost binding level, for names of file scope. - This is created when the compiler is started and exists - through the entire run. */ - -static struct binding_level *global_binding_level; - -/* Binding level structures are initialized by copying this one. */ - -static struct binding_level clear_binding_level - = {NULL, NULL, NULL, NULL, NULL, 0, 0, 0}; - -/* Nonzero means unconditionally make a LET_STMT for the next level pushed. */ - -static int keep_next_level_flag; - -/* Nonzero means make a LET_STMT for the next level pushed - if it has subblocks. */ - -static int keep_next_if_subblocks; - -/* Forward declarations. */ - -static tree grokparms (), grokdeclarator (); -tree pushdecl (); -static void builtin_function (); - -static tree lookup_tag (); -static tree lookup_tag_reverse (); -static tree lookup_name_current_level (); -static char *redeclaration_error_message (); -static void layout_array_type (); - -/* C-specific option variables. */ - -/* Nonzero means allow type mismatches in conditional expressions; - just make their values `void'. */ - -int flag_cond_mismatch; - -/* Nonzero means don't recognize the keyword `asm'. */ - -int flag_no_asm; - -/* Nonzero means do some things the same way PCC does. */ - -int flag_traditional; - -/* Nonzero means warn about implicit declarations. */ - -int warn_implicit; - -/* Nonzero means warn about function definitions that default the return type - or that use a null return and have a return-type other than void. */ - -int warn_return_type; - -/* Nonzero means give string constants the type `const char *' - to get extra warnings from them. These warnings will be too numerous - to be useful, except in thoroughly ANSIfied programs. */ - -int warn_write_strings; - -/* Nonzero means warn about pointer casts that can drop a type qualifier - from the pointer target type. */ - -int warn_cast_qual; - -/* Nonzero means warn about sizeof(function) or addition/subtraction - of function pointers. */ - -int warn_pointer_arith; - -/* Nonzero means warn for all old-style non-prototype function decls. */ - -int warn_strict_prototypes; - -/* Nonzero means `$' can be in an identifier. - See cccp.c for reasons why this breaks some obscure ANSI C programs. */ - -#ifndef DOLLARS_IN_IDENTIFIERS -#define DOLLARS_IN_IDENTIFIERS 0 -#endif -int dollars_in_ident = DOLLARS_IN_IDENTIFIERS; - -char *language_string = "GNU C"; - -/* Decode the string P as a language-specific option. - Return 1 if it is recognized (and handle it); - return 0 if not recognized. */ - -int -lang_decode_option (p) - char *p; -{ - if (!strcmp (p, "-ftraditional") || !strcmp (p, "-traditional")) - flag_traditional = 1, dollars_in_ident = 1, flag_writable_strings = 1; - else if (!strcmp (p, "-fsigned-char")) - flag_signed_char = 1; - else if (!strcmp (p, "-funsigned-char")) - flag_signed_char = 0; - else if (!strcmp (p, "-fno-signed-char")) - flag_signed_char = 0; - else if (!strcmp (p, "-fno-unsigned-char")) - flag_signed_char = 1; - else if (!strcmp (p, "-fshort-enums")) - flag_short_enums = 1; - else if (!strcmp (p, "-fno-short-enums")) - flag_short_enums = 0; - else if (!strcmp (p, "-fcond-mismatch")) - flag_cond_mismatch = 1; - else if (!strcmp (p, "-fno-cond-mismatch")) - flag_cond_mismatch = 0; - else if (!strcmp (p, "-fasm")) - flag_no_asm = 0; - else if (!strcmp (p, "-fno-asm")) - flag_no_asm = 1; - else if (!strcmp (p, "-ansi")) - flag_no_asm = 1, dollars_in_ident = 0; - else if (!strcmp (p, "-Wimplicit")) - warn_implicit = 1; - else if (!strcmp (p, "-Wreturn-type")) - warn_return_type = 1; - else if (!strcmp (p, "-Wwrite-strings")) - warn_write_strings = 1; - else if (!strcmp (p, "-Wcast-qual")) - warn_cast_qual = 1; - else if (!strcmp (p, "-Wpointer-arith")) - warn_pointer_arith = 1; - else if (!strcmp (p, "-Wstrict-prototypes")) - warn_strict_prototypes = 1; - else if (!strcmp (p, "-Wcomment")) - ; /* cpp handles this one. */ - else if (!strcmp (p, "-Wcomments")) - ; /* cpp handles this one. */ - else if (!strcmp (p, "-Wtrigraphs")) - ; /* cpp handles this one. */ - else if (!strcmp (p, "-Wall")) - { - extra_warnings = 1; - warn_implicit = 1; - warn_return_type = 1; - warn_unused = 1; - warn_switch = 1; - } - else - return 0; - - return 1; -} - -void -print_lang_identifier (file, node, indent) - FILE *file; - tree node; - int indent; -{ - print_node (file, "global", IDENTIFIER_GLOBAL_VALUE (node), indent + 4); - print_node (file, "local", IDENTIFIER_LOCAL_VALUE (node), indent + 4); - print_node (file, "label", IDENTIFIER_LABEL_VALUE (node), indent + 4); - print_node (file, "implicit", IDENTIFIER_IMPLICIT_DECL (node), indent + 4); - print_node (file, "error locus", IDENTIFIER_ERROR_LOCUS (node), indent + 4); -} - -/* Create a new `struct binding_level'. */ - -static -struct binding_level * -make_binding_level () -{ - /* NOSTRICT */ - return (struct binding_level *) xmalloc (sizeof (struct binding_level)); -} - -/* Nonzero if we are currently in the global binding level. */ - -int -global_bindings_p () -{ - return current_binding_level == global_binding_level; -} - -void -keep_next_level () -{ - keep_next_level_flag = 1; -} - -/* Nonzero if the current level needs to have a LET_STMT made. */ - -int -kept_level_p () -{ - return ((current_binding_level->keep_if_subblocks - && current_binding_level->blocks != 0) - || current_binding_level->keep - || current_binding_level->names != 0); -} - -/* Identify this binding level as a level of parameters. */ - -void -declare_parm_level () -{ - current_binding_level->parm_flag = 1; -} - -/* Nonzero if currently making parm declarations. */ - -in_parm_level_p () -{ - return current_binding_level->parm_flag; -} - -/* Enter a new binding level. - If TAG_TRANSPARENT is nonzero, do so only for the name space of variables, - not for that of tags. */ - -void -pushlevel (tag_transparent) - int tag_transparent; -{ - register struct binding_level *newlevel = NULL_BINDING_LEVEL; - - /* If this is the top level of a function, - just make sure that NAMED_LABELS is 0. - They should have been set to 0 at the end of the previous function. */ - - if (current_binding_level == global_binding_level) - { - if (named_labels) - abort (); - } - - /* Reuse or create a struct for this binding level. */ - - if (free_binding_level) - { - newlevel = free_binding_level; - free_binding_level = free_binding_level->level_chain; - } - else - { - newlevel = make_binding_level (); - } - - /* Add this level to the front of the chain (stack) of levels that - are active. */ - - *newlevel = clear_binding_level; - newlevel->level_chain = current_binding_level; - current_binding_level = newlevel; - newlevel->tag_transparent = tag_transparent; - newlevel->keep = keep_next_level_flag; - keep_next_level_flag = 0; - newlevel->keep_if_subblocks = keep_next_if_subblocks; - keep_next_if_subblocks = 0; -} - -/* Exit a binding level. - Pop the level off, and restore the state of the identifier-decl mappings - that were in effect when this level was entered. - - If KEEP is nonzero, this level had explicit declarations, so - and create a "block" (a LET_STMT node) for the level - to record its declarations and subblocks for symbol table output. - - If FUNCTIONBODY is nonzero, this level is the body of a function, - so create a block as if KEEP were set and also clear out all - label names. - - If REVERSE is nonzero, reverse the order of decls before putting - them into the LET_STMT. */ - -tree -poplevel (keep, reverse, functionbody) - int keep; - int reverse; - int functionbody; -{ - register tree link; - /* The chain of decls was accumulated in reverse order. - Put it into forward order, just for cleanliness. */ - tree decls; - tree tags = current_binding_level->tags; - tree subblocks = current_binding_level->blocks; - tree block = 0; - - keep |= current_binding_level->keep; - - /* This warning is turned off because it causes warnings for - declarations like `extern struct foo *x'. */ -#if 0 - /* Warn about incomplete structure types in this level. */ - for (link = tags; link; link = TREE_CHAIN (link)) - if (TYPE_SIZE (TREE_VALUE (link)) == 0) - { - tree type = TREE_VALUE (link); - char *errmsg; - switch (TREE_CODE (type)) - { - case RECORD_TYPE: - errmsg = "`struct %s' incomplete in scope ending here"; - break; - case UNION_TYPE: - errmsg = "`union %s' incomplete in scope ending here"; - break; - case ENUMERAL_TYPE: - errmsg = "`enum %s' incomplete in scope ending here"; - break; - } - if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) - error (errmsg, IDENTIFIER_POINTER (TYPE_NAME (type))); - else - /* If this type has a typedef-name, the TYPE_NAME is a TYPE_DECL. */ - error (errmsg, IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)))); - } -#endif /* 0 */ - - /* Get the decls in the order they were written. - Usually current_binding_level->names is in reverse order. - But parameter decls were previously put in forward order. */ - - if (reverse) - current_binding_level->names - = decls = nreverse (current_binding_level->names); - else - decls = current_binding_level->names; - - /* If there were any declarations or structure tags in that level, - or if this level is a function body, - create a LET_STMT to record them for the life of this function. */ - - if (keep || functionbody - || (current_binding_level->keep_if_subblocks && subblocks != 0)) - block = build_let (0, 0, keep ? decls : 0, - subblocks, 0, keep ? tags : 0); - - /* In each subblock, record that this is its superior. */ - - for (link = subblocks; link; link = TREE_CHAIN (link)) - STMT_SUPERCONTEXT (link) = block; - - /* Clear out the meanings of the local variables of this level; - also record in each decl which block it belongs to. */ - - for (link = decls; link; link = TREE_CHAIN (link)) - { - if (DECL_NAME (link) != 0) - { - /* If the ident. was used via a local extern decl, - don't forget that fact. */ - if (TREE_USED (link) && TREE_EXTERNAL (link)) - TREE_USED (DECL_NAME (link)) = 1; - IDENTIFIER_LOCAL_VALUE (DECL_NAME (link)) = 0; - } - DECL_CONTEXT (link) = block; - } - - /* Restore all name-meanings of the outer levels - that were shadowed by this level. */ - - for (link = current_binding_level->shadowed; link; link = TREE_CHAIN (link)) - IDENTIFIER_LOCAL_VALUE (TREE_PURPOSE (link)) = TREE_VALUE (link); - - /* If the level being exited is the top level of a function, - check over all the labels. */ - - if (functionbody) - { - /* Clear out the definitions of all label names, - since their scopes end here. */ - - for (link = named_labels; link; link = TREE_CHAIN (link)) - { - if (DECL_SOURCE_LINE (TREE_VALUE (link)) == 0) - { - error ("label `%s' used somewhere above but not defined", - IDENTIFIER_POINTER (DECL_NAME (TREE_VALUE (link)))); - /* Avoid crashing later. */ - define_label (input_filename, 1, DECL_NAME (TREE_VALUE (link))); - } - else if (warn_unused && !TREE_USED (TREE_VALUE (link))) - warning_with_decl (TREE_VALUE (link), - "label `%s' defined but not used"); - IDENTIFIER_LABEL_VALUE (DECL_NAME (TREE_VALUE (link))) = 0; - } - - named_labels = 0; - } - - /* Pop the current level, and free the structure for reuse. */ - - { - register struct binding_level *level = current_binding_level; - current_binding_level = current_binding_level->level_chain; - - level->level_chain = free_binding_level; - free_binding_level = level; - } - - if (functionbody) - { - DECL_INITIAL (current_function_decl) = block; - /* If this is the top level block of a function, - the vars are the function's parameters. - Don't leave them in the LET_STMT because they are - found in the FUNCTION_DECL instead. */ - STMT_VARS (block) = 0; - } - else if (block) - current_binding_level->blocks - = chainon (current_binding_level->blocks, block); - /* If we did not make a block for the level just exited, - any blocks made for inner levels - (since they cannot be recorded as subblocks in that level) - must be carried forward so they will later become subblocks - of something else. */ - else if (subblocks) - current_binding_level->blocks - = chainon (current_binding_level->blocks, subblocks); - - if (block) - TREE_USED (block) = 1; - return block; -} - -/* Push a definition of struct, union or enum tag "name". - "type" should be the type node. - We assume that the tag "name" is not already defined. - - Note that the definition may really be just a forward reference. - In that case, the TYPE_SIZE will be zero. */ - -void -pushtag (name, type) - tree name, type; -{ - register struct binding_level *b = current_binding_level; - while (b->tag_transparent) b = b->level_chain; - - if (name) - { - /* Record the identifier as the type's name if it has none. */ - - if (TYPE_NAME (type) == 0) - TYPE_NAME (type) = name; - - if (b == global_binding_level) - b->tags = perm_tree_cons (name, type, b->tags); - else - b->tags = saveable_tree_cons (name, type, b->tags); - } -} - -/* Handle when a new declaration NEWDECL - has the same name as an old one OLDDECL - in the same binding contour. - Prints an error message if appropriate. - - If safely possible, alter OLDDECL to look like NEWDECL, and return 1. - Otherwise, return 0. */ - -static int -duplicate_decls (newdecl, olddecl) - register tree newdecl, olddecl; -{ - int types_match = comptypes (TREE_TYPE (newdecl), TREE_TYPE (olddecl)); - - if (TREE_CODE (TREE_TYPE (newdecl)) == ERROR_MARK - || TREE_CODE (TREE_TYPE (olddecl)) == ERROR_MARK) - types_match = 0; - - /* If this decl has linkage, and the old one does too, maybe no error. */ - if (TREE_CODE (olddecl) != TREE_CODE (newdecl)) - { - error_with_decl (newdecl, "`%s' redeclared as different kind of symbol"); - error_with_decl (olddecl, "previous declaration of `%s'"); - } - else - { - if (flag_traditional && TREE_CODE (newdecl) == FUNCTION_DECL - && IDENTIFIER_IMPLICIT_DECL (DECL_NAME (newdecl)) == olddecl - && DECL_INITIAL (olddecl) == 0) - /* If -traditional, avoid error for redeclaring fcn - after implicit decl. */ - ; - else if (TREE_CODE (olddecl) == FUNCTION_DECL - && DECL_FUNCTION_CODE (olddecl) != NOT_BUILT_IN) - { - if (!types_match) - error_with_decl (newdecl, "conflicting types for built-in function `%s'"); - else if (extra_warnings) - warning_with_decl (newdecl, "built-in function `%s' redeclared"); - } - else if (!types_match) - { - error_with_decl (newdecl, "conflicting types for `%s'"); - /* Check for function type mismatch - involving an empty arglist vs a nonempty one. */ - if (TREE_CODE (olddecl) == FUNCTION_DECL - && comptypes (TREE_TYPE (TREE_TYPE (olddecl)), - TREE_TYPE (TREE_TYPE (newdecl))) - && ((TYPE_ARG_TYPES (TREE_TYPE (olddecl)) == 0 - && DECL_INITIAL (olddecl) == 0) - || - (TYPE_ARG_TYPES (TREE_TYPE (newdecl)) == 0 - && DECL_INITIAL (newdecl) == 0))) - { - /* Classify the problem further. */ - register tree t = TYPE_ARG_TYPES (TREE_TYPE (olddecl)); - if (t == 0) - t = TYPE_ARG_TYPES (TREE_TYPE (newdecl)); - for (; t; t = TREE_CHAIN (t)) - { - register tree type = TREE_VALUE (t); - - if (TREE_CHAIN (t) == 0 && type != void_type_node) - { - error ("A parameter list with an ellipsis can't match"); - error ("an empty parameter name list declaration."); - break; - } - - if (type == float_type_node - || (TREE_CODE (type) == INTEGER_TYPE - && (TYPE_PRECISION (type) - < TYPE_PRECISION (integer_type_node)))) - { - error ("An argument type that has a default promotion"); - error ("can't match an empty parameter name list declaration."); - break; - } - } - } - error_with_decl (olddecl, "previous declaration of `%s'"); - } - else - { - char *errmsg = redeclaration_error_message (newdecl, olddecl); - if (errmsg) - { - error_with_decl (newdecl, errmsg); - error_with_decl (olddecl, - "here is the previous declaration of `%s'"); - } - else if (TREE_CODE (olddecl) == FUNCTION_DECL - && DECL_INITIAL (olddecl) != 0 - && TYPE_ARG_TYPES (TREE_TYPE (olddecl)) == 0 - && TYPE_ARG_TYPES (TREE_TYPE (newdecl)) != 0) - { - /* Prototype decl follows defn w/o prototype. */ - warning_with_decl (newdecl, "prototype for `%s'"); - warning_with_decl (olddecl, - "follows non-prototype definition here"); - } - - /* These bits are logically part of the type. */ - if (pedantic - && (TREE_READONLY (newdecl) != TREE_READONLY (olddecl) - || TREE_THIS_VOLATILE (newdecl) != TREE_THIS_VOLATILE (olddecl))) - warning_with_decl (newdecl, "type qualifiers for `%s' conflict with previous decl"); - } - } - - if (TREE_CODE (olddecl) == TREE_CODE (newdecl)) - { - int new_is_definition = (TREE_CODE (newdecl) == FUNCTION_DECL - && DECL_INITIAL (newdecl) != 0); - - /* Copy all the DECL_... slots specified in the new decl - except for any that we copy here from the old type. */ - - if (types_match) - { - tree oldtype = TREE_TYPE (olddecl); - /* Merge the data types specified in the two decls. */ - TREE_TYPE (newdecl) - = TREE_TYPE (olddecl) - = commontype (TREE_TYPE (newdecl), TREE_TYPE (olddecl)); - - /* Lay the type out, unless already done. */ - if (oldtype != TREE_TYPE (newdecl)) - { - if (TREE_TYPE (newdecl) != error_mark_node) - layout_type (TREE_TYPE (newdecl)); - if (TREE_CODE (newdecl) != FUNCTION_DECL - && TREE_CODE (newdecl) != TYPE_DECL - && TREE_CODE (newdecl) != CONST_DECL) - layout_decl (newdecl, 0); - } - else - { - /* Since the type is OLDDECL's, make OLDDECL's size go with. */ - DECL_SIZE (newdecl) = DECL_SIZE (olddecl); - DECL_SIZE_UNIT (newdecl) = DECL_SIZE_UNIT (olddecl); - if (DECL_ALIGN (olddecl) > DECL_ALIGN (newdecl)) - DECL_ALIGN (newdecl) = DECL_ALIGN (olddecl); - } - - /* Merge the type qualifiers. */ - if (TREE_READONLY (newdecl)) - TREE_READONLY (olddecl) = 1; - if (TREE_THIS_VOLATILE (newdecl)) - TREE_THIS_VOLATILE (olddecl) = 1; - - /* Merge the initialization information. */ - if (DECL_INITIAL (newdecl) == 0) - DECL_INITIAL (newdecl) = DECL_INITIAL (olddecl); - /* Keep the old rtl since we can safely use it. */ - DECL_RTL (newdecl) = DECL_RTL (olddecl); - } - /* If cannot merge, then use the new type and qualifiers, - and don't preserve the old rtl. */ - else - { - TREE_TYPE (olddecl) = TREE_TYPE (newdecl); - TREE_READONLY (olddecl) = TREE_READONLY (newdecl); - TREE_THIS_VOLATILE (olddecl) = TREE_THIS_VOLATILE (newdecl); - TREE_VOLATILE (olddecl) = TREE_VOLATILE (newdecl); - } - - /* Merge the storage class information. */ - if (TREE_EXTERNAL (newdecl)) - { - TREE_STATIC (newdecl) = TREE_STATIC (olddecl); - TREE_EXTERNAL (newdecl) = TREE_EXTERNAL (olddecl); - - /* For functions, static overrides non-static. */ - if (TREE_CODE (newdecl) == FUNCTION_DECL) - { - TREE_PUBLIC (newdecl) &= TREE_PUBLIC (olddecl); - /* This is since we don't automatically - copy the attributes of NEWDECL into OLDDECL. */ - TREE_PUBLIC (olddecl) = TREE_PUBLIC (newdecl); - /* If this clears `static', clear it in the identifier too. */ - if (! TREE_PUBLIC (olddecl)) - TREE_PUBLIC (DECL_NAME (olddecl)) = 0; - } - else - TREE_PUBLIC (newdecl) = TREE_PUBLIC (olddecl); - } - else - { - TREE_STATIC (olddecl) = TREE_STATIC (newdecl); - TREE_EXTERNAL (olddecl) = 0; - TREE_PUBLIC (olddecl) = TREE_PUBLIC (newdecl); - } - - /* If either decl says `inline', this fn is inline, - unless its definition was passed already. */ - if (TREE_INLINE (newdecl) && DECL_INITIAL (olddecl) == 0) - TREE_INLINE (olddecl) = 1; - - /* If redeclaring a builtin function, and not a definition, - it stays built in. - Also preserve various other info from the definition. */ - if (TREE_CODE (newdecl) == FUNCTION_DECL && !new_is_definition) - { - DECL_SET_FUNCTION_CODE (newdecl, DECL_FUNCTION_CODE (olddecl)); - DECL_RESULT (newdecl) = DECL_RESULT (olddecl); - DECL_INITIAL (newdecl) = DECL_INITIAL (olddecl); - DECL_SAVED_INSNS (newdecl) = DECL_SAVED_INSNS (olddecl); - DECL_RESULT_TYPE (newdecl) = DECL_RESULT_TYPE (olddecl); - DECL_ARGUMENTS (newdecl) = DECL_ARGUMENTS (olddecl); - DECL_FRAME_SIZE (newdecl) = DECL_FRAME_SIZE (olddecl); - } - - /* Don't lose track of having output OLDDECL as GDB symbol. */ - DECL_BLOCK_SYMTAB_ADDRESS (newdecl) - = DECL_BLOCK_SYMTAB_ADDRESS (olddecl); - - bcopy ((char *) newdecl + sizeof (struct tree_common), - (char *) olddecl + sizeof (struct tree_common), - sizeof (struct tree_decl) - sizeof (struct tree_common)); - - return 1; - } - - /* New decl is completely inconsistent with the old one => - tell caller to replace the old one. */ - return 0; -} - -/* Record a decl-node X as belonging to the current lexical scope. - Check for errors (such as an incompatible declaration for the same - name already seen in the same scope). - - Returns either X or an old decl for the same name. - If an old decl is returned, it may have been smashed - to agree with what X says. */ - -tree -pushdecl (x) - tree x; -{ - register tree t; - register tree name = DECL_NAME (x); - register struct binding_level *b = current_binding_level; - - if (name) - { - char *file; - int line; - - t = lookup_name_current_level (name); - if (t != 0 && t == error_mark_node) - /* error_mark_node is 0 for a while during initialization! */ - { - t = 0; - error_with_decl (x, "`%s' used prior to declaration"); - } - - if (t != 0) - { - file = DECL_SOURCE_FILE (t); - line = DECL_SOURCE_LINE (t); - } - - if (t != 0 && duplicate_decls (x, t)) - { - /* If this decl is `static' and an implicit decl was seen previously, - warn. But don't complain if -traditional, - since traditional compilers don't complain. */ - if (!flag_traditional && TREE_PUBLIC (name) - && ! TREE_PUBLIC (x) && ! TREE_EXTERNAL (x) - /* We used to warn also for explicit extern followed by static, - but sometimes you need to do it that way. */ - && IDENTIFIER_IMPLICIT_DECL (name) != 0) - { - warning ("`%s' was declared implicitly `extern' and later `static'", - IDENTIFIER_POINTER (name)); - warning_with_file_and_line (file, line, - "previous declaration of `%s'", - IDENTIFIER_POINTER (name)); - } - return t; - } - - /* If declaring a type as a typedef, and the type has no known - typedef name, install this TYPE_DECL as its typedef name. */ - if (TREE_CODE (x) == TYPE_DECL) - if (TYPE_NAME (TREE_TYPE (x)) == 0) - TYPE_NAME (TREE_TYPE (x)) = x; - - /* Multiple external decls of the same identifier ought to match. */ - - if (TREE_EXTERNAL (x) && IDENTIFIER_GLOBAL_VALUE (name) != 0 - && (TREE_EXTERNAL (IDENTIFIER_GLOBAL_VALUE (name)) - || TREE_PUBLIC (IDENTIFIER_GLOBAL_VALUE (name)))) - { - if (! comptypes (TREE_TYPE (x), - TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (name)))) - { - warning_with_decl (x, - "type mismatch with previous external decl"); - warning_with_decl (IDENTIFIER_GLOBAL_VALUE (name), - "previous external decl of `%s'"); - } - } - - /* In PCC-compatibility mode, extern decls of vars with no current decl - take effect at top level no matter where they are. */ - if (flag_traditional && TREE_EXTERNAL (x) - && lookup_name (name) == 0) - b = global_binding_level; - - /* This name is new in its binding level. - Install the new declaration and return it. */ - if (b == global_binding_level) - { - /* Install a global value. */ - - /* If the first global decl has external linkage, - warn if we later see static one. */ - if (IDENTIFIER_GLOBAL_VALUE (name) == 0 && TREE_PUBLIC (x)) - TREE_PUBLIC (name) = 1; - - IDENTIFIER_GLOBAL_VALUE (name) = x; - - /* Don't forget if the function was used via an implicit decl. */ - if (IDENTIFIER_IMPLICIT_DECL (name) - && TREE_USED (IDENTIFIER_IMPLICIT_DECL (name))) - TREE_USED (x) = 1, TREE_USED (name) = 1; - - /* Don't forget if its address was taken in that way. */ - if (IDENTIFIER_IMPLICIT_DECL (name) - && TREE_ADDRESSABLE (IDENTIFIER_IMPLICIT_DECL (name))) - TREE_ADDRESSABLE (x) = 1; - - /* Warn about mismatches against previous implicit decl. */ - if (IDENTIFIER_IMPLICIT_DECL (name) != 0 - /* If this real decl matches the implicit, don't complain. */ - && ! (TREE_CODE (x) == FUNCTION_DECL - && TREE_TYPE (TREE_TYPE (x)) == integer_type_node)) - warning ("`%s' was previously implicitly declared to return `int'", - IDENTIFIER_POINTER (name)); - - /* If this decl is `static' and an `extern' was seen previously, - that is erroneous. */ - if (TREE_PUBLIC (name) - && ! TREE_PUBLIC (x) && ! TREE_EXTERNAL (x)) - { - if (IDENTIFIER_IMPLICIT_DECL (name)) - warning ("`%s' was declared implicitly `extern' and later `static'", - IDENTIFIER_POINTER (name)); - else - warning ("`%s' was declared `extern' and later `static'", - IDENTIFIER_POINTER (name)); - } - } - else - { - /* Here to install a non-global value. */ - tree oldlocal = IDENTIFIER_LOCAL_VALUE (name); - tree oldglobal = IDENTIFIER_GLOBAL_VALUE (name); - IDENTIFIER_LOCAL_VALUE (name) = x; - - /* If this is an extern function declaration, see if we - have a global definition for the function. */ - if (oldlocal == 0 - && oldglobal != 0 - && TREE_CODE (x) == FUNCTION_DECL - && TREE_CODE (oldglobal) == FUNCTION_DECL) - { - /* We have one. Their types must agree. */ - if (! comptypes (TREE_TYPE (x), - TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (name)))) - warning_with_decl (x, "local declaration of `%s' doesn't match global one"); - /* If the global one is inline, make the local one inline. */ - else if (TREE_INLINE (oldglobal) - || DECL_FUNCTION_CODE (oldglobal) != NOT_BUILT_IN - || (TYPE_ARG_TYPES (TREE_TYPE (oldglobal)) != 0 - && TYPE_ARG_TYPES (TREE_TYPE (x)) == 0)) - IDENTIFIER_LOCAL_VALUE (name) = oldglobal; - } - /* If we have a local external declaration, - and no file-scope declaration has yet been seen, - then if we later have a file-scope decl it must not be static. */ - if (oldlocal == 0 - && oldglobal == 0 - && TREE_EXTERNAL (x) - && TREE_PUBLIC (x)) - { - TREE_PUBLIC (name) = 1; - } - - /* Warn if shadowing an argument at the top level of the body. */ - if (oldlocal != 0 && !TREE_EXTERNAL (x) - && TREE_CODE (oldlocal) == PARM_DECL - && TREE_CODE (x) != PARM_DECL - && current_binding_level->level_chain->parm_flag) - warning ("declaration of `%s' shadows a parameter", - IDENTIFIER_POINTER (name)); - - /* Maybe warn if shadowing something else. */ - else if (warn_shadow && !TREE_EXTERNAL (x) - /* No shadow warnings for vars made for inlining. */ - && !TREE_INLINE (x)) - { - char *warnstring = 0; - - if (oldlocal != 0 && TREE_CODE (oldlocal) == PARM_DECL) - warnstring = "declaration of `%s' shadows a parameter"; - else if (oldlocal != 0) - warnstring = "declaration of `%s' shadows previous local"; - else if (IDENTIFIER_GLOBAL_VALUE (name) != 0) - warnstring = "declaration of `%s' shadows global declaration"; - - if (warnstring) - warning (warnstring, IDENTIFIER_POINTER (name)); - } - - /* If storing a local value, there may already be one (inherited). - If so, record it for restoration when this binding level ends. */ - if (oldlocal != 0) - b->shadowed = tree_cons (name, oldlocal, b->shadowed); - } - - /* Keep count of variables in this level with incomplete type. */ - if (TYPE_SIZE (TREE_TYPE (x)) == 0) - ++b->n_incomplete; - } - - /* Put decls on list in reverse order. - We will reverse them later if necessary. */ - TREE_CHAIN (x) = b->names; - b->names = x; - - return x; -} - -/* Generate an implicit declaration for identifier FUNCTIONID - as a function of type int (). Print a warning if appropriate. */ - -tree -implicitly_declare (functionid) - tree functionid; -{ - register tree decl; - - /* Save the decl permanently so we can warn if definition follows. */ -#if 0 /* A temporary implicit decl causes a crash in pushdecl. - In 1.38, fix pushdecl. */ - if (flag_traditional || !warn_implicit - || current_binding_level == global_binding_level) -#endif - end_temporary_allocation (); - - /* We used to reuse an old implicit decl here, - but this loses with inline functions because it can clobber - the saved decl chains. */ -/* if (IDENTIFIER_IMPLICIT_DECL (functionid) != 0) - decl = IDENTIFIER_IMPLICIT_DECL (functionid); - else */ - decl = build_decl (FUNCTION_DECL, functionid, default_function_type); - - TREE_EXTERNAL (decl) = 1; - TREE_PUBLIC (decl) = 1; - - /* ANSI standard says implicit declarations are in the innermost block. - So we record the decl in the standard fashion. - If flag_traditional is set, pushdecl does it top-level. */ - pushdecl (decl); - rest_of_decl_compilation (decl, 0, 0, 0); - - if (warn_implicit - /* Only one warning per identifier. */ - && IDENTIFIER_IMPLICIT_DECL (functionid) == 0) - warning ("implicit declaration of function `%s'", - IDENTIFIER_POINTER (functionid)); - - IDENTIFIER_IMPLICIT_DECL (functionid) = decl; - -#if 0 - if (flag_traditional || ! warn_implicit - || current_binding_level == global_binding_level) -#endif - resume_temporary_allocation (); - - return decl; -} - -/* Return zero if the declaration NEWDECL is valid - when the declaration OLDDECL (assumed to be for the same name) - has already been seen. - Otherwise return an error message format string with a %s - where the identifier should go. */ - -static char * -redeclaration_error_message (newdecl, olddecl) - tree newdecl, olddecl; -{ - if (TREE_CODE (newdecl) == TYPE_DECL) - { - if (flag_traditional && TREE_TYPE (newdecl) == TREE_TYPE (olddecl)) - return 0; - return "redefinition of `%s'"; - } - else if (TREE_CODE (newdecl) == FUNCTION_DECL) - { - /* Declarations of functions can insist on internal linkage - but they can't be inconsistent with internal linkage, - so there can be no error on that account. - However defining the same name twice is no good. */ - if (DECL_INITIAL (olddecl) != 0 && DECL_INITIAL (newdecl) != 0 - /* However, defining once as extern inline and a second - time in another way is ok. */ - && !(TREE_INLINE (olddecl) && TREE_EXTERNAL (olddecl) - && !(TREE_INLINE (newdecl) && TREE_EXTERNAL (newdecl)))) - return "redefinition of `%s'"; - return 0; - } - else if (current_binding_level == global_binding_level) - { - /* Objects declared at top level: */ - /* If at least one is a reference, it's ok. */ - if (TREE_EXTERNAL (newdecl) || TREE_EXTERNAL (olddecl)) - return 0; - /* Reject two definitions. */ - if (DECL_INITIAL (olddecl) != 0 && DECL_INITIAL (newdecl) != 0) - return "redefinition of `%s'"; - /* Now we have two tentative defs, or one tentative and one real def. */ - /* Insist that the linkage match. */ - if (TREE_PUBLIC (olddecl) != TREE_PUBLIC (newdecl)) - return "conflicting declarations of `%s'"; - return 0; - } - else - { - /* Objects declared with block scope: */ - /* Reject two definitions, and reject a definition - together with an external reference. */ - if (!(TREE_EXTERNAL (newdecl) && TREE_EXTERNAL (olddecl))) - return "redeclaration of `%s'"; - return 0; - } -} - -/* Get the LABEL_DECL corresponding to identifier ID as a label. - Create one if none exists so far for the current function. - This function is called for both label definitions and label references. */ - -tree -lookup_label (id) - tree id; -{ - register tree decl = IDENTIFIER_LABEL_VALUE (id); - - if (decl != 0) - return decl; - - decl = build_decl (LABEL_DECL, id, NULL_TREE); - DECL_MODE (decl) = VOIDmode; - /* Mark that the label's definition has not been seen. */ - DECL_SOURCE_LINE (decl) = 0; - - IDENTIFIER_LABEL_VALUE (id) = decl; - - named_labels - = tree_cons (NULL_TREE, decl, named_labels); - - return decl; -} - -/* Define a label, specifying the location in the source file. - Return the LABEL_DECL node for the label, if the definition is valid. - Otherwise return 0. */ - -tree -define_label (filename, line, name) - char *filename; - int line; - tree name; -{ - tree decl = lookup_label (name); - if (DECL_SOURCE_LINE (decl) != 0) - { - error_with_decl (decl, "duplicate label `%s'"); - return 0; - } - else - { - /* Mark label as having been defined. */ - DECL_SOURCE_FILE (decl) = filename; - DECL_SOURCE_LINE (decl) = line; - return decl; - } -} - -/* Return the list of declarations of the current level. - Note that this list is in reverse order unless/until - you nreverse it; and when you do nreverse it, you must - store the result back using `storedecls' or you will lose. */ - -tree -getdecls () -{ - return current_binding_level->names; -} - -/* Return the list of type-tags (for structs, etc) of the current level. */ - -tree -gettags () -{ - return current_binding_level->tags; -} - -/* Store the list of declarations of the current level. - This is done for the parameter declarations of a function being defined, - after they are modified in the light of any missing parameters. */ - -static void -storedecls (decls) - tree decls; -{ - current_binding_level->names = decls; -} - -/* Similarly, store the list of tags of the current level. */ - -static void -storetags (tags) - tree tags; -{ - current_binding_level->tags = tags; -} - -/* Given NAME, an IDENTIFIER_NODE, - return the structure (or union or enum) definition for that name. - Searches binding levels from BINDING_LEVEL up to the global level. - If THISLEVEL_ONLY is nonzero, searches only the specified context - (but skips any tag-transparent contexts to find one that is - meaningful for tags). - FORM says which kind of type the caller wants; - it is RECORD_TYPE or UNION_TYPE or ENUMERAL_TYPE. - If the wrong kind of type is found, an error is reported. */ - -static tree -lookup_tag (form, name, binding_level, thislevel_only) - enum tree_code form; - struct binding_level *binding_level; - tree name; - int thislevel_only; -{ - register struct binding_level *level; - - for (level = binding_level; level; level = level->level_chain) - { - register tree tail; - for (tail = level->tags; tail; tail = TREE_CHAIN (tail)) - { - if (TREE_PURPOSE (tail) == name) - { - if (TREE_CODE (TREE_VALUE (tail)) != form) - { - /* Definition isn't the kind we were looking for. */ - error ("`%s' defined as wrong kind of tag", - IDENTIFIER_POINTER (name)); - } - return TREE_VALUE (tail); - } - } - if (thislevel_only && ! level->tag_transparent) - return NULL_TREE; - } - return NULL_TREE; -} - -/* Given a type, find the tag that was defined for it and return the tag name. - Otherwise return 0. However, the value can never be 0 - in the cases in which this is used. */ - -static tree -lookup_tag_reverse (type) - tree type; -{ - register struct binding_level *level; - - for (level = current_binding_level; level; level = level->level_chain) - { - register tree tail; - for (tail = level->tags; tail; tail = TREE_CHAIN (tail)) - { - if (TREE_VALUE (tail) == type) - return TREE_PURPOSE (tail); - } - } - return NULL_TREE; -} - -/* Look up NAME in the current binding level and its superiors - in the namespace of variables, functions and typedefs. - Return a ..._DECL node of some kind representing its definition, - or return 0 if it is undefined. */ - -tree -lookup_name (name) - tree name; -{ - register tree val; - if (current_binding_level != global_binding_level - && IDENTIFIER_LOCAL_VALUE (name)) - val = IDENTIFIER_LOCAL_VALUE (name); - else - val = IDENTIFIER_GLOBAL_VALUE (name); - if (val && TREE_TYPE (val) == error_mark_node) - return error_mark_node; - return val; -} - -/* Similar to `lookup_name' but look only at current binding level. */ - -static tree -lookup_name_current_level (name) - tree name; -{ - register tree t; - - if (current_binding_level == global_binding_level) - return IDENTIFIER_GLOBAL_VALUE (name); - - if (IDENTIFIER_LOCAL_VALUE (name) == 0) - return 0; - - for (t = current_binding_level->names; t; t = TREE_CHAIN (t)) - if (DECL_NAME (t) == name) - break; - - return t; -} - -/* Create the predefined scalar types of C, - and some nodes representing standard constants (0, 1, (void *)0). - Initialize the global binding level. - Make definitions for built-in primitive functions. */ - -void -init_decl_processing () -{ - register tree endlink; - - /* Make identifier nodes long enough for the language-specific slots. */ - set_identifier_size (sizeof (struct lang_identifier)); - - current_function_decl = NULL; - named_labels = NULL; - current_binding_level = NULL_BINDING_LEVEL; - free_binding_level = NULL_BINDING_LEVEL; - pushlevel (0); /* make the binding_level structure for global names */ - global_binding_level = current_binding_level; - - value_identifier = get_identifier (""); - - /* Define `int' and `char' first so that dbx will output them first. */ - - integer_type_node = make_signed_type (INT_TYPE_SIZE); - pushdecl (build_decl (TYPE_DECL, ridpointers[(int) RID_INT], - integer_type_node)); - - /* Define `char', which is like either `signed char' or `unsigned char' - but not the same as either. */ - - char_type_node = - (flag_signed_char - ? make_signed_type (CHAR_TYPE_SIZE) - : make_unsigned_type (CHAR_TYPE_SIZE)); - pushdecl (build_decl (TYPE_DECL, get_identifier ("char"), - char_type_node)); - - long_integer_type_node = make_signed_type (LONG_TYPE_SIZE); - pushdecl (build_decl (TYPE_DECL, get_identifier ("long int"), - long_integer_type_node)); - - unsigned_type_node = make_unsigned_type (INT_TYPE_SIZE); - pushdecl (build_decl (TYPE_DECL, get_identifier ("unsigned int"), - unsigned_type_node)); - - long_unsigned_type_node = make_unsigned_type (LONG_TYPE_SIZE); - pushdecl (build_decl (TYPE_DECL, get_identifier ("long unsigned int"), - long_unsigned_type_node)); - - /* `unsigned long' or `unsigned int' is the standard type for sizeof. - Traditionally, use a signed type. */ - if (INT_TYPE_SIZE != LONG_TYPE_SIZE) - sizetype = flag_traditional ? long_integer_type_node : long_unsigned_type_node; - else - sizetype = flag_traditional ? integer_type_node : unsigned_type_node; - - TREE_TYPE (TYPE_SIZE (integer_type_node)) = sizetype; - TREE_TYPE (TYPE_SIZE (char_type_node)) = sizetype; - TREE_TYPE (TYPE_SIZE (unsigned_type_node)) = sizetype; - TREE_TYPE (TYPE_SIZE (long_unsigned_type_node)) = sizetype; - TREE_TYPE (TYPE_SIZE (long_integer_type_node)) = sizetype; - - error_mark_node = make_node (ERROR_MARK); - TREE_TYPE (error_mark_node) = error_mark_node; - - short_integer_type_node = make_signed_type (SHORT_TYPE_SIZE); - pushdecl (build_decl (TYPE_DECL, get_identifier ("short int"), - short_integer_type_node)); - - long_long_integer_type_node = make_signed_type (LONG_LONG_TYPE_SIZE); - pushdecl (build_decl (TYPE_DECL, get_identifier ("long long int"), - long_long_integer_type_node)); - - short_unsigned_type_node = make_unsigned_type (SHORT_TYPE_SIZE); - pushdecl (build_decl (TYPE_DECL, get_identifier ("short unsigned int"), - short_unsigned_type_node)); - - long_long_unsigned_type_node = make_unsigned_type (LONG_LONG_TYPE_SIZE); - pushdecl (build_decl (TYPE_DECL, get_identifier ("long long unsigned int"), - long_long_unsigned_type_node)); - - /* Define both `signed char' and `unsigned char'. */ - signed_char_type_node = make_signed_type (CHAR_TYPE_SIZE); - pushdecl (build_decl (TYPE_DECL, get_identifier ("signed char"), - signed_char_type_node)); - - unsigned_char_type_node = make_unsigned_type (CHAR_TYPE_SIZE); - pushdecl (build_decl (TYPE_DECL, get_identifier ("unsigned char"), - unsigned_char_type_node)); - - float_type_node = make_node (REAL_TYPE); - TYPE_PRECISION (float_type_node) = FLOAT_TYPE_SIZE; - pushdecl (build_decl (TYPE_DECL, ridpointers[(int) RID_FLOAT], - float_type_node)); - layout_type (float_type_node); - - double_type_node = make_node (REAL_TYPE); - TYPE_PRECISION (double_type_node) = DOUBLE_TYPE_SIZE; - pushdecl (build_decl (TYPE_DECL, ridpointers[(int) RID_DOUBLE], - double_type_node)); - layout_type (double_type_node); - - long_double_type_node = make_node (REAL_TYPE); - TYPE_PRECISION (long_double_type_node) = LONG_DOUBLE_TYPE_SIZE; - pushdecl (build_decl (TYPE_DECL, get_identifier ("long double"), - long_double_type_node)); - layout_type (long_double_type_node); - - integer_zero_node = build_int_2 (0, 0); - TREE_TYPE (integer_zero_node) = integer_type_node; - integer_one_node = build_int_2 (1, 0); - TREE_TYPE (integer_one_node) = integer_type_node; - - size_zero_node = build_int_2 (0, 0); - TREE_TYPE (size_zero_node) = sizetype; - size_one_node = build_int_2 (1, 0); - TREE_TYPE (size_one_node) = sizetype; - - void_type_node = make_node (VOID_TYPE); - pushdecl (build_decl (TYPE_DECL, - ridpointers[(int) RID_VOID], void_type_node)); - layout_type (void_type_node); /* Uses integer_zero_node */ - - null_pointer_node = build_int_2 (0, 0); - TREE_TYPE (null_pointer_node) = build_pointer_type (void_type_node); - layout_type (TREE_TYPE (null_pointer_node)); - - string_type_node = build_pointer_type (char_type_node); - - /* make a type for arrays of 256 characters. - 256 is picked randomly because we have a type for integers from 0 to 255. - With luck nothing will ever really depend on the length of this - array type. */ - char_array_type_node - = build_array_type (char_type_node, unsigned_char_type_node); - /* Likewise for arrays of ints. */ - int_array_type_node - = build_array_type (integer_type_node, unsigned_char_type_node); - - default_function_type - = build_function_type (integer_type_node, NULL_TREE); - - ptr_type_node = build_pointer_type (void_type_node); - endlink = tree_cons (NULL_TREE, void_type_node, NULL_TREE); - - double_ftype_double - = build_function_type (double_type_node, - tree_cons (NULL_TREE, double_type_node, endlink)); - - double_ftype_double_double - = build_function_type (double_type_node, - tree_cons (NULL_TREE, double_type_node, - tree_cons (NULL_TREE, - double_type_node, endlink))); - - int_ftype_int - = build_function_type (integer_type_node, - tree_cons (NULL_TREE, integer_type_node, endlink)); - - long_ftype_long - = build_function_type (long_integer_type_node, - tree_cons (NULL_TREE, - long_integer_type_node, endlink)); - - void_ftype_ptr_ptr_int - = build_function_type (void_type_node, - tree_cons (NULL_TREE, ptr_type_node, - tree_cons (NULL_TREE, ptr_type_node, - tree_cons (NULL_TREE, - integer_type_node, - endlink)))); - - int_ftype_ptr_ptr_int - = build_function_type (integer_type_node, - tree_cons (NULL_TREE, ptr_type_node, - tree_cons (NULL_TREE, ptr_type_node, - tree_cons (NULL_TREE, - integer_type_node, - endlink)))); - - void_ftype_ptr_int_int - = build_function_type (void_type_node, - tree_cons (NULL_TREE, ptr_type_node, - tree_cons (NULL_TREE, integer_type_node, - tree_cons (NULL_TREE, - integer_type_node, - endlink)))); - - builtin_function ("__builtin_alloca", - build_function_type (ptr_type_node, - tree_cons (NULL_TREE, - integer_type_node, - endlink)), - BUILT_IN_ALLOCA); - - builtin_function ("__builtin_abs", int_ftype_int, BUILT_IN_ABS); - builtin_function ("__builtin_fabs", double_ftype_double, BUILT_IN_FABS); - builtin_function ("__builtin_labs", long_ftype_long, BUILT_IN_LABS); - builtin_function ("__builtin_ffs", int_ftype_int, BUILT_IN_FFS); - builtin_function ("__builtin_saveregs", default_function_type, - BUILT_IN_SAVEREGS); - builtin_function ("__builtin_classify_type", default_function_type, - BUILT_IN_CLASSIFY_TYPE); - builtin_function ("__builtin_next_arg", - build_function_type (ptr_type_node, endlink), - BUILT_IN_NEXT_ARG); -#if 0 - /* Support for these has not been written in either expand_builtin - or build_function_call. */ - builtin_function ("__builtin_div", default_ftype, BUILT_IN_DIV); - builtin_function ("__builtin_ldiv", default_ftype, BUILT_IN_LDIV); - builtin_function ("__builtin_ffloor", double_ftype_double, BUILT_IN_FFLOOR); - builtin_function ("__builtin_fceil", double_ftype_double, BUILT_IN_FCEIL); - builtin_function ("__builtin_fmod", double_ftype_double_double, BUILT_IN_FMOD); - builtin_function ("__builtin_frem", double_ftype_double_double, BUILT_IN_FREM); - builtin_function ("__builtin_memcpy", void_ftype_ptr_ptr_int, BUILT_IN_MEMCPY); - builtin_function ("__builtin_memcmp", int_ftype_ptr_ptr_int, BUILT_IN_MEMCMP); - builtin_function ("__builtin_memset", void_ftype_ptr_int_int, BUILT_IN_MEMSET); - builtin_function ("__builtin_fsqrt", double_ftype_double, BUILT_IN_FSQRT); - builtin_function ("__builtin_getexp", double_ftype_double, BUILT_IN_GETEXP); - builtin_function ("__builtin_getman", double_ftype_double, BUILT_IN_GETMAN); -#endif - - start_identifier_warnings (); -} - -/* Make a definition for a builtin function named NAME and whose data type - is TYPE. TYPE should be a function type with argument types. - FUNCTION_CODE tells later passes how to compile calls to this function. - See tree.h for its possible values. */ - -static void -builtin_function (name, type, function_code) - char *name; - tree type; - enum built_in_function function_code; -{ - tree decl = build_decl (FUNCTION_DECL, get_identifier (name), type); - TREE_EXTERNAL (decl) = 1; - TREE_PUBLIC (decl) = 1; - make_decl_rtl (decl, 0, 1); - pushdecl (decl); - DECL_SET_FUNCTION_CODE (decl, function_code); -} - -/* Called when a declaration is seen that contains no names to declare. - If its type is a reference to a structure, union or enum inherited - from a containing scope, shadow that tag name for the current scope - with a forward reference. - If its type defines a new named structure or union - or defines an enum, it is valid but we need not do anything here. - Otherwise, it is an error. */ - -void -shadow_tag (declspecs) - tree declspecs; -{ - int found_tag = 0; - int warned = 0; - register tree link; - - for (link = declspecs; link; link = TREE_CHAIN (link)) - { - register tree value = TREE_VALUE (link); - register enum tree_code code = TREE_CODE (value); - int ok = 0; - - if (code == RECORD_TYPE || code == UNION_TYPE || code == ENUMERAL_TYPE) - /* Used to test also that TYPE_SIZE (value) != 0. - That caused warning for `struct foo;' at top level in the file. */ - { - register tree name = lookup_tag_reverse (value); - register tree t = lookup_tag (code, name, current_binding_level, 1); - - if (t == 0) - { - t = make_node (code); - pushtag (name, t); - ok = 1; - } - else if (name != 0 || code == ENUMERAL_TYPE) - ok = 1; - } - - if (ok) - found_tag++; - else - { - if (!warned) - warning ("useless keyword or type name in declaration"); - warned = 1; - } - } - - if (!warned) - { - if (found_tag > 1) - warning ("multiple types in one declaration"); - if (found_tag == 0) - warning ("empty declaration"); - } -} - -/* Decode a "typename", such as "int **", returning a ..._TYPE node. */ - -tree -groktypename (typename) - tree typename; -{ - if (TREE_CODE (typename) != TREE_LIST) - return typename; - return grokdeclarator (TREE_VALUE (typename), - TREE_PURPOSE (typename), - TYPENAME, 0); -} - -/* Decode a declarator in an ordinary declaration or data definition. - This is called as soon as the type information and variable name - have been parsed, before parsing the initializer if any. - Here we create the ..._DECL node, fill in its type, - and put it on the list of decls for the current context. - The ..._DECL node is returned as the value. - - Exception: for arrays where the length is not specified, - the type is left null, to be filled in by `finish_decl'. - - Function definitions do not come here; they go to start_function - instead. However, external and forward declarations of functions - do go through here. Structure field declarations are done by - grokfield and not through here. */ - -/* Set this to zero to debug not using the temporary obstack - to parse initializers. */ -int debug_temp_inits = 1; - -tree -start_decl (declarator, declspecs, initialized) - tree declspecs, declarator; - int initialized; -{ - register tree decl = grokdeclarator (declarator, declspecs, - NORMAL, initialized); - register tree tem; - int init_written = initialized; - - if (initialized) - /* Is it valid for this decl to have an initializer at all? - If not, set INITIALIZED to zero, which will indirectly - tell `finish_decl' to ignore the initializer once it is parsed. */ - switch (TREE_CODE (decl)) - { - case TYPE_DECL: - /* typedef foo = bar means give foo the same type as bar. - We haven't parsed bar yet, so `finish_decl' will fix that up. - Any other case of an initialization in a TYPE_DECL is an error. */ - if (pedantic || list_length (declspecs) > 1) - { - error ("typedef `%s' is initialized", - IDENTIFIER_POINTER (DECL_NAME (decl))); - initialized = 0; - } - break; - - case FUNCTION_DECL: - error ("function `%s' is initialized like a variable", - IDENTIFIER_POINTER (DECL_NAME (decl))); - initialized = 0; - break; - - default: - /* Don't allow initializations for incomplete types - except for arrays which might be completed by the initialization. */ - if (TYPE_SIZE (TREE_TYPE (decl)) != 0) - ; /* A complete type is ok. */ - else if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE) - { - error ("variable `%s' has initializer but incomplete type", - IDENTIFIER_POINTER (DECL_NAME (decl))); - initialized = 0; - } - else if (TYPE_SIZE (TREE_TYPE (TREE_TYPE (decl))) == 0) - { - error ("elements of array `%s' have incomplete type", - IDENTIFIER_POINTER (DECL_NAME (decl))); - initialized = 0; - } - } - - if (initialized) - { -#if 0 /* Seems redundant. */ - if (current_binding_level != global_binding_level - && TREE_EXTERNAL (decl) - && TREE_CODE (decl) != FUNCTION_DECL) - warning ("declaration of `%s' has `extern' and is initialized", - IDENTIFIER_POINTER (DECL_NAME (decl))); -#endif - TREE_EXTERNAL (decl) = 0; - if (current_binding_level == global_binding_level) - TREE_STATIC (decl) = 1; - - /* Tell `pushdecl' this is an initialized decl - even though we don't yet have the initializer expression. - Also tell `finish_decl' it may store the real initializer. */ - DECL_INITIAL (decl) = error_mark_node; - } - - /* Add this decl to the current binding level. - TEM may equal DECL or it may be a previous decl of the same name. */ - tem = pushdecl (decl); - - /* For a local variable, define the RTL now. */ - if (current_binding_level != global_binding_level - /* But not if this is a duplicate decl - and we preserved the rtl from the previous one - (which may or may not happen). */ - && DECL_RTL (tem) == 0) - { - if (TYPE_SIZE (TREE_TYPE (tem)) != 0) - expand_decl (tem, NULL_TREE); - else if (TREE_CODE (TREE_TYPE (tem)) == ARRAY_TYPE - && DECL_INITIAL (tem) != 0) - expand_decl (tem, NULL_TREE); - } - - if (init_written) - { - /* When parsing and digesting the initializer, - use temporary storage. Do this even if we will ignore the value. */ - if (current_binding_level == global_binding_level && debug_temp_inits) - temporary_allocation (); - } - - return tem; -} - -/* Finish processing of a declaration; - install its line number and initial value. - If the length of an array type is not known before, - it must be determined now, from the initial value, or it is an error. */ - -void -finish_decl (decl, init, asmspec_tree) - tree decl, init; - tree asmspec_tree; -{ - register tree type = TREE_TYPE (decl); - int was_incomplete = (DECL_SIZE (decl) == 0); - int temporary = allocation_temporary_p (); - char *asmspec = 0; - - if (asmspec_tree) - asmspec = TREE_STRING_POINTER (asmspec_tree); - - /* If `start_decl' didn't like having an initialization, ignore it now. */ - - if (init != 0 && DECL_INITIAL (decl) == 0) - init = 0; - - if (init) - { - if (TREE_CODE (decl) != TYPE_DECL) - store_init_value (decl, init); - else - { - /* typedef foo = bar; store the type of bar as the type of foo. */ - TREE_TYPE (decl) = TREE_TYPE (init); - DECL_INITIAL (decl) = init = 0; - } - } - - /* For top-level declaration, the initial value was read in - the temporary obstack. MAXINDEX, rtl, etc. to be made below - must go in the permanent obstack; but don't discard the - temporary data yet. */ - - if (current_binding_level == global_binding_level && temporary) - end_temporary_allocation (); - - /* Deduce size of array from initialization, if not already known */ - - if (TREE_CODE (type) == ARRAY_TYPE - && TYPE_DOMAIN (type) == 0 - && TREE_CODE (decl) != TYPE_DECL) - { -#if 0 - int do_default = ! ((!pedantic && TREE_STATIC (decl)) - || TREE_EXTERNAL (decl)); -#endif - int do_default - = (TREE_STATIC (decl) - /* Even if pedantic, an external linkage array - may have incomplete type at first. */ - ? pedantic && !TREE_PUBLIC (decl) - : !TREE_EXTERNAL (decl)); - int failure - = complete_array_type (type, DECL_INITIAL (decl), do_default); - - if (failure == 1) - error_with_decl (decl, "initializer fails to determine size of `%s'"); - - if (failure == 2) - { - if (do_default) - { - if (! TREE_PUBLIC (decl)) - error_with_decl (decl, "array size missing in `%s'"); - } - else if (!pedantic && TREE_STATIC (decl)) - TREE_EXTERNAL (decl) = 1; - } - - if (pedantic && TYPE_DOMAIN (type) != 0 - && tree_int_cst_lt (TYPE_MAX_VALUE (TYPE_DOMAIN (type)), - integer_zero_node)) - error_with_decl (decl, "zero-size array `%s'"); - - layout_decl (decl, 0); - } - - if (TREE_CODE (decl) == VAR_DECL) - { - if (TREE_STATIC (decl) && DECL_SIZE (decl) == 0) - { - /* A static variable with an incomplete type: - that is an error if it is initialized or `static'. - Otherwise, let it through, but if it is not `extern' - then it may cause an error message later. */ - if (! (TREE_PUBLIC (decl) && DECL_INITIAL (decl) == 0)) - error_with_decl (decl, "storage size of `%s' isn't known"); - } - else if (!TREE_EXTERNAL (decl) && DECL_SIZE (decl) == 0) - { - /* An automatic variable with an incomplete type: - that is an error. */ - error_with_decl (decl, "storage size of `%s' isn't known"); - TREE_TYPE (decl) = error_mark_node; - } - - if ((TREE_EXTERNAL (decl) || TREE_STATIC (decl)) - && DECL_SIZE (decl) != 0 && ! TREE_LITERAL (DECL_SIZE (decl))) - error_with_decl (decl, "storage size of `%s' isn't constant"); - } - - /* Output the assembler code and/or RTL code for variables and functions, - unless the type is an undefined structure or union. - If not, it will get done when the type is completed. */ - - if (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == FUNCTION_DECL) - { - if (flag_traditional && allocation_temporary_p ()) - { - end_temporary_allocation (); - rest_of_decl_compilation (decl, asmspec, - current_binding_level == global_binding_level, - 0); - resume_temporary_allocation (); - } - else - rest_of_decl_compilation (decl, asmspec, - current_binding_level == global_binding_level, - 0); - if (current_binding_level != global_binding_level) - { - /* Recompute the RTL of a local array now - if it used to be an incomplete type. */ - if (was_incomplete - && ! TREE_STATIC (decl) && ! TREE_EXTERNAL (decl)) - { - /* If we used it already as memory, it must stay in memory. */ - TREE_ADDRESSABLE (decl) = TREE_USED (decl); - /* If it's still incomplete now, no init will save it. */ - if (DECL_SIZE (decl) == 0) - DECL_INITIAL (decl) = 0; - expand_decl (decl, NULL_TREE); - } - /* Compute and store the initial value. */ - expand_decl_init (decl); - } - } - - if (TREE_CODE (decl) == TYPE_DECL) - rest_of_decl_compilation (decl, 0, - current_binding_level == global_binding_level, - 0); - - /* Resume permanent allocation, if not within a function. */ - if (temporary && current_binding_level == global_binding_level) - { - permanent_allocation (); - /* We need to remember that this array HAD an initialization, - but discard the actual nodes, since they are temporary anyway. */ - if (DECL_INITIAL (decl) != 0) - DECL_INITIAL (decl) = error_mark_node; - } - - /* At the end of a declaration, throw away any variable type sizes - of types defined inside that declaration. There is no use - computing them in the following function definition. */ - if (current_binding_level == global_binding_level) - get_pending_sizes (); -} - -/* Given a parsed parameter declaration, - decode it into a PARM_DECL and push that on the current binding level. */ - -void -push_parm_decl (parm) - tree parm; -{ - register tree decl = grokdeclarator (TREE_VALUE (parm), TREE_PURPOSE (parm), - PARM, 0); - - /* Add this decl to the current binding level. */ - finish_decl (pushdecl (decl), NULL_TREE, NULL_TREE); -} - -/* Make TYPE a complete type based on INITIAL_VALUE. - Return 0 if successful, 1 if INITIAL_VALUE can't be decyphered, - 2 if there was no information (in which case assume 1 if DO_DEFAULT). */ - -int -complete_array_type (type, initial_value, do_default) - tree type; - tree initial_value; - int do_default; -{ - register tree maxindex = NULL_TREE; - int value = 0; - int temporary = (TREE_PERMANENT (type) && allocation_temporary_p ()); - - /* Don't put temporary nodes in permanent type. */ - if (temporary) - end_temporary_allocation (); - - if (initial_value) - { - /* Note MAXINDEX is really the maximum index, - one less than the size. */ - if (TREE_CODE (initial_value) == STRING_CST) - { - int eltsize = TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (TREE_TYPE (initial_value)))); - maxindex = build_int_2 (TREE_STRING_LENGTH (initial_value) / eltsize - 1, 0); - } - else if (TREE_CODE (initial_value) == CONSTRUCTOR) - { - register int nelts - = list_length (CONSTRUCTOR_ELTS (initial_value)); - maxindex = build_int_2 (nelts - 1, 0); - } - else - { - /* Make an error message unless that happened already. */ - if (initial_value != error_mark_node) - value = 1; - - /* Prevent further error messages. */ - maxindex = build_int_2 (1, 0); - } - } - - if (!maxindex) - { - if (do_default) - maxindex = build_int_2 (1, 0); - value = 2; - } - - if (maxindex) - { - TYPE_DOMAIN (type) = build_index_type (maxindex); - if (!TREE_TYPE (maxindex)) - TREE_TYPE (maxindex) = TYPE_DOMAIN (type); - } - - /* Lay out the type now that we can get the real answer. */ - - layout_type (type); - - if (temporary) - resume_temporary_allocation (); - - return value; -} - -/* Given declspecs and a declarator, - determine the name and type of the object declared - and construct a ..._DECL node for it. - (In one case we can return a ..._TYPE node instead. - For invalid input we sometimes return 0.) - - DECLSPECS is a chain of tree_list nodes whose value fields - are the storage classes and type specifiers. - - DECL_CONTEXT says which syntactic context this declaration is in: - NORMAL for most contexts. Make a VAR_DECL or FUNCTION_DECL or TYPE_DECL. - FUNCDEF for a function definition. Like NORMAL but a few different - error messages in each case. Return value may be zero meaning - this definition is too screwy to try to parse. - PARM for a parameter declaration (either within a function prototype - or before a function body). Make a PARM_DECL, or return void_type_node. - TYPENAME if for a typename (in a cast or sizeof). - Don't make a DECL node; just return the ..._TYPE node. - FIELD for a struct or union field; make a FIELD_DECL. - INITIALIZED is 1 if the decl has an initializer. - - In the TYPENAME case, DECLARATOR is really an absolute declarator. - It may also be so in the PARM case, for a prototype where the - argument type is specified but not the name. - - This function is where the complicated C meanings of `static' - and `extern' are intrepreted. */ - -static tree -grokdeclarator (declarator, declspecs, decl_context, initialized) - tree declspecs; - tree declarator; - enum decl_context decl_context; - int initialized; -{ - int specbits = 0; - tree spec; - tree type = NULL_TREE; - int longlong = 0; - int constp; - int volatilep; - int inlinep; - int explicit_int = 0; - int explicit_char = 0; - char *name; - tree typedef_type = 0; - int funcdef_flag = 0; - int resume_temporary = 0; - enum tree_code innermost_code = ERROR_MARK; - - if (decl_context == FUNCDEF) - funcdef_flag = 1, decl_context = NORMAL; - - if (flag_traditional && allocation_temporary_p ()) - { - resume_temporary = 1; - end_temporary_allocation (); - } - - /* Look inside a declarator for the name being declared - and get it as a string, for an error message. */ - { - register tree decl = declarator; - name = 0; - - while (decl) - switch (TREE_CODE (decl)) - { - case ARRAY_REF: - case INDIRECT_REF: - case CALL_EXPR: - innermost_code = TREE_CODE (decl); - decl = TREE_OPERAND (decl, 0); - break; - - case IDENTIFIER_NODE: - name = IDENTIFIER_POINTER (decl); - decl = 0; - break; - - default: - abort (); - } - if (name == 0) - name = "type name"; - } - - /* A function definition's declarator must have the form of - a function declarator. */ - - if (funcdef_flag && innermost_code != CALL_EXPR) - return 0; - - /* Anything declared one level down from the top level - must be one of the parameters of a function - (because the body is at least two levels down). */ - - if (decl_context == NORMAL - && current_binding_level->level_chain == global_binding_level) - decl_context = PARM; - - /* Look through the decl specs and record which ones appear. - Some typespecs are defined as built-in typenames. - Others, the ones that are modifiers of other types, - are represented by bits in SPECBITS: set the bits for - the modifiers that appear. Storage class keywords are also in SPECBITS. - - If there is a typedef name or a type, store the type in TYPE. - This includes builtin typedefs such as `int'. - - Set EXPLICIT_INT if the type is `int' or `char' and did not - come from a user typedef. - - Set LONGLONG if `long' is mentioned twice. */ - - for (spec = declspecs; spec; spec = TREE_CHAIN (spec)) - { - register int i; - register tree id = TREE_VALUE (spec); - - if (id == ridpointers[(int) RID_INT]) - explicit_int = 1; - if (id == ridpointers[(int) RID_CHAR]) - explicit_char = 1; - - if (TREE_CODE (id) == IDENTIFIER_NODE) - for (i = (int) RID_FIRST_MODIFIER; i < (int) RID_MAX; i++) - { - if (ridpointers[i] == id) - { - if (i == (int) RID_LONG && specbits & (1< 1) - warning ("duplicate `const'"); - if (volatilep > 1) - warning ("duplicate `volatile'"); - type = TYPE_MAIN_VARIANT (type); - - /* Warn if two storage classes are given. Default to `auto'. */ - - { - int nclasses = 0; - - if (specbits & 1 << (int) RID_AUTO) nclasses++; - if (specbits & 1 << (int) RID_STATIC) nclasses++; - if (specbits & 1 << (int) RID_EXTERN) nclasses++; - if (specbits & 1 << (int) RID_REGISTER) nclasses++; - if (specbits & 1 << (int) RID_TYPEDEF) nclasses++; - - /* Warn about storage classes that are invalid for certain - kinds of declarations (parameters, typenames, etc.). */ - - if (nclasses > 1) - error ("multiple storage classes in declaration of `%s'", name); - else if (funcdef_flag - && (specbits - & ((1 << (int) RID_REGISTER) - | (1 << (int) RID_AUTO) - | (1 << (int) RID_TYPEDEF)))) - { - if (specbits & 1 << (int) RID_AUTO) - error ("function definition declared `auto'"); - if (specbits & 1 << (int) RID_REGISTER) - error ("function definition declared `auto'"); - if (specbits & 1 << (int) RID_TYPEDEF) - error ("function definition declared `typedef'"); - specbits &= ~ ((1 << (int) RID_TYPEDEF) | (1 << (int) RID_REGISTER) - | (1 << (int) RID_AUTO)); - } - else if (decl_context != NORMAL && nclasses > 0) - { - if (decl_context == PARM && specbits & 1 << (int) RID_REGISTER) - ; - else - { - error ((decl_context == FIELD - ? "storage class specified for structure field `%s'" - : (decl_context == PARM - ? "storage class specified for parameter `%s'" - : "storage class specified for typename")), - name); - specbits &= ~ ((1 << (int) RID_TYPEDEF) | (1 << (int) RID_REGISTER) - | (1 << (int) RID_AUTO) | (1 << (int) RID_STATIC) - | (1 << (int) RID_EXTERN)); - } - } - else if (specbits & 1 << (int) RID_EXTERN && initialized - && ! funcdef_flag) - warning ("`%s' initialized and declared `extern'", name); - else if (current_binding_level == global_binding_level - && specbits & (1 << (int) RID_AUTO)) - error ("top-level declaration of `%s' specifies `auto'", name); - } - - /* Now figure out the structure of the declarator proper. - Descend through it, creating more complex types, until we reach - the declared identifier (or NULL_TREE, in an absolute declarator). */ - - while (declarator && TREE_CODE (declarator) != IDENTIFIER_NODE) - { - if (type == error_mark_node) - { - declarator = TREE_OPERAND (declarator, 0); - continue; - } - - /* Each level of DECLARATOR is either an ARRAY_REF (for ...[..]), - an INDIRECT_REF (for *...), - a CALL_EXPR (for ...(...)), - an identifier (for the name being declared) - or a null pointer (for the place in an absolute declarator - where the name was omitted). - For the last two cases, we have just exited the loop. - - At this point, TYPE is the type of elements of an array, - or for a function to return, or for a pointer to point to. - After this sequence of ifs, TYPE is the type of the - array or function or pointer, and DECLARATOR has had its - outermost layer removed. */ - - if (TREE_CODE (declarator) == ARRAY_REF) - { - register tree itype = NULL_TREE; - register tree size = TREE_OPERAND (declarator, 1); - - declarator = TREE_OPERAND (declarator, 0); - - /* Check for some types that there cannot be arrays of. */ - - if (type == void_type_node) - { - error ("declaration of `%s' as array of voids", name); - type = error_mark_node; - } - - if (TREE_CODE (type) == FUNCTION_TYPE) - { - error ("declaration of `%s' as array of functions", name); - type = error_mark_node; - } - - if (size == error_mark_node) - type = error_mark_node; - - if (type == error_mark_node) - continue; - - /* If size was specified, set ITYPE to a range-type for that size. - Otherwise, ITYPE remains null. finish_decl may figure it out - from an initial value. */ - - if (size) - { - /* might be a cast */ - if (TREE_CODE (size) == NOP_EXPR - && TREE_TYPE (size) == TREE_TYPE (TREE_OPERAND (size, 0))) - size = TREE_OPERAND (size, 0); - - if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE - && TREE_CODE (TREE_TYPE (size)) != ENUMERAL_TYPE) - { - error ("size of array `%s' has non-integer type", name); - size = integer_one_node; - } - if (pedantic && integer_zerop (size)) - warning ("ANSI C forbids zero-size array `%s'", name); - if (TREE_CODE (size) == INTEGER_CST) - { - if (INT_CST_LT (size, integer_zero_node)) - { - error ("size of array `%s' is negative", name); - size = integer_one_node; - } - itype = build_index_type (build_int_2 (TREE_INT_CST_LOW (size) - 1, 0)); - } - else - { - if (pedantic) - warning ("ANSI C forbids variable-size array `%s'", name); - itype = build_binary_op (MINUS_EXPR, size, integer_one_node); - itype = build_index_type (itype); - } - } - - /* Complain about arrays of incomplete types, except in typedefs. */ - - if (TYPE_SIZE (type) == 0 - && !(specbits & (1 << (int) RID_TYPEDEF))) - warning ("array type has incomplete element type"); - - /* Build the array type itself. - Merge any constancy or volatility into the target type. */ - - if (constp || volatilep) - type = c_build_type_variant (type, constp, volatilep); - -#if 0 /* don't clear these; leave them set so that the array type - or the variable is itself const or volatile. */ - constp = 0; - volatilep = 0; -#endif - - type = build_array_type (type, itype); - } - else if (TREE_CODE (declarator) == CALL_EXPR) - { - tree arg_types; - - /* Declaring a function type. - Make sure we have a valid type for the function to return. */ - if (type == error_mark_node) - continue; - - if (pedantic && (constp || volatilep)) - warning ("function declared to return const or volatile result"); - - /* Warn about some types functions can't return. */ - - if (TREE_CODE (type) == FUNCTION_TYPE) - { - error ("`%s' declared as function returning a function", name); - type = integer_type_node; - } - if (TREE_CODE (type) == ARRAY_TYPE) - { - error ("`%s' declared as function returning an array", name); - type = integer_type_node; - } - - /* Traditionally, declaring return type float means double. */ - - if (flag_traditional && type == float_type_node) - type = double_type_node; - - /* Construct the function type and go to the next - inner layer of declarator. */ - - arg_types = grokparms (TREE_OPERAND (declarator, 1), - funcdef_flag - /* Say it's a definition - only for the CALL_EXPR - closest to the identifier. */ - && TREE_CODE (TREE_OPERAND (declarator, 0)) == IDENTIFIER_NODE); -#if 0 /* This seems to be false. We turn off temporary allocation - above in this function if -traditional. - And this code caused inconsistent results with prototypes: - callers would ignore them, and pass arguments wrong. */ - - /* Omit the arg types if -traditional, since the arg types - and the list links might not be permanent. */ - type = build_function_type (type, flag_traditional ? 0 : arg_types); -#endif - type = build_function_type (type, arg_types); - declarator = TREE_OPERAND (declarator, 0); - } - else if (TREE_CODE (declarator) == INDIRECT_REF) - { - /* Merge any constancy or volatility into the target type - for the pointer. */ - - if (constp || volatilep) - type = c_build_type_variant (type, constp, volatilep); - constp = 0; - volatilep = 0; - - type = build_pointer_type (type); - - /* Process a list of type modifier keywords - (such as const or volatile) that were given inside the `*'. */ - - if (TREE_TYPE (declarator)) - { - register tree typemodlist; - int erred = 0; - for (typemodlist = TREE_TYPE (declarator); typemodlist; - typemodlist = TREE_CHAIN (typemodlist)) - { - if (TREE_VALUE (typemodlist) == ridpointers[(int) RID_CONST]) - constp++; - else if (TREE_VALUE (typemodlist) == ridpointers[(int) RID_VOLATILE]) - volatilep++; - else if (!erred) - { - erred = 1; - error ("invalid type modifier within pointer declarator"); - } - } - if (constp > 1) - warning ("duplicate `const'"); - if (volatilep > 1) - warning ("duplicate `volatile'"); - } - - declarator = TREE_OPERAND (declarator, 0); - } - else - abort (); - -/* layout_type (type); */ - - /* @@ Should perhaps replace the following code by changes in - * @@ stor_layout.c. */ - if (TREE_CODE (type) == FUNCTION_DECL) - { - /* A function variable in C should be Pmode rather than EPmode - because it has just the address of a function, no static chain.*/ - TYPE_MODE (type) = Pmode; - } - } - - /* Now TYPE has the actual type. */ - - /* If this is declaring a typedef name, return a TYPE_DECL. */ - - if (specbits & (1 << (int) RID_TYPEDEF)) - { - /* Note that the grammar rejects storage classes - in typenames, fields or parameters */ - if (constp || volatilep) - type = c_build_type_variant (type, constp, volatilep); - if (resume_temporary) - resume_temporary_allocation (); - return build_decl (TYPE_DECL, declarator, type); - } - - /* Detect the case of an array type of unspecified size - which came, as such, direct from a typedef name. - We must copy the type, so that each identifier gets - a distinct type, so that each identifier's size can be - controlled separately by its own initializer. */ - - if (type != 0 && typedef_type != 0 - && TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (typedef_type) - && TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type) == 0) - type = build_array_type (TREE_TYPE (type), 0); - - /* If this is a type name (such as, in a cast or sizeof), - compute the type and return it now. */ - - if (decl_context == TYPENAME) - { - /* Note that the grammar rejects storage classes - in typenames, fields or parameters */ - if (constp || volatilep) - type = c_build_type_variant (type, constp, volatilep); - if (resume_temporary) - resume_temporary_allocation (); - return type; - } - - /* `void' at top level (not within pointer) - is allowed only in typedefs or type names. - We don't complain about parms either, but that is because - a better error message can be made later. */ - - if (type == void_type_node && decl_context != PARM) - { - error ("variable or field `%s' declared void", - IDENTIFIER_POINTER (declarator)); - type = integer_type_node; - } - - /* Now create the decl, which may be a VAR_DECL, a PARM_DECL - or a FUNCTION_DECL, depending on DECL_CONTEXT and TYPE. */ - - { - register tree decl; - - if (decl_context == PARM) - { - /* A parameter declared as an array of T is really a pointer to T. - One declared as a function is really a pointer to a function. */ - - if (TREE_CODE (type) == ARRAY_TYPE) - { - /* Transfer const-ness of array into that of type pointed to. */ - type = build_pointer_type - (c_build_type_variant (TREE_TYPE (type), constp, volatilep)); - volatilep = constp = 0; - } - else if (TREE_CODE (type) == FUNCTION_TYPE) - { - if (pedantic && (constp || volatilep)) - warning ("ANSI C forbids const or volatile function types"); - type = build_pointer_type (c_build_type_variant (type, constp, volatilep)); - volatilep = constp = 0; - } - - if (initialized) - error ("parameter `%s' is initialized", name); - - decl = build_decl (PARM_DECL, declarator, type); - - /* Compute the type actually passed in the parmlist, - for the case where there is no prototype. - (For example, shorts and chars are passed as ints.) - When there is a prototype, this is overridden later. */ - - DECL_ARG_TYPE (decl) = type; - if (type == float_type_node) - DECL_ARG_TYPE (decl) = double_type_node; - else if (TREE_CODE (type) == INTEGER_TYPE - /* ANSI C says short and char are promoted to int - or unsigned int, even if that is not wider. */ - && (TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node) - || type == short_integer_type_node - || type == short_unsigned_type_node)) - { - if (TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node) - && TREE_UNSIGNED (type)) - DECL_ARG_TYPE (decl) = unsigned_type_node; - else - DECL_ARG_TYPE (decl) = integer_type_node; - } - } - else if (decl_context == FIELD) - { - /* Structure field. It may not be a function. */ - - if (TREE_CODE (type) == FUNCTION_TYPE) - { - error ("field `%s' declared as a function", - IDENTIFIER_POINTER (declarator)); - type = build_pointer_type (type); - } - else if (TREE_CODE (type) != ERROR_MARK && TYPE_SIZE (type) == 0) - { - error ("field `%s' has incomplete type", - IDENTIFIER_POINTER (declarator)); - type = error_mark_node; - } - /* Move type qualifiers down to element of an array. */ - if (TREE_CODE (type) == ARRAY_TYPE && (constp || volatilep)) - { - type = c_build_type_variant (type, constp, volatilep); - constp = volatilep = 0; - } - /* Note that the grammar rejects storage classes - in typenames, fields or parameters */ - decl = build_decl (FIELD_DECL, declarator, type); - } - else if (TREE_CODE (type) == FUNCTION_TYPE) - { - if (specbits & ((1 << (int) RID_AUTO) | (1 << (int) RID_REGISTER))) - error ("invalid storage class for function `%s'", - IDENTIFIER_POINTER (declarator)); - /* Function declaration not at top level. - Storage classes other than `extern' are not allowed - and `extern' makes no difference. */ - if (current_binding_level != global_binding_level - && (specbits & ((1 << (int) RID_STATIC) | (1 << (int) RID_INLINE))) - && pedantic) - warning ("invalid storage class for function `%s'", - IDENTIFIER_POINTER (declarator)); - decl = build_decl (FUNCTION_DECL, declarator, type); - - TREE_EXTERNAL (decl) = 1; - /* Record presence of `static'. */ - TREE_PUBLIC (decl) = !(specbits & (1 << (int) RID_STATIC)); - /* Record presence of `inline', if it is reasonable. */ - if (inlinep) - { - tree last = tree_last (TYPE_ARG_TYPES (type)); - - if (! strcmp (IDENTIFIER_POINTER (declarator), "main")) - warning ("cannot inline function `main'"); - else if (last && TREE_VALUE (last) != void_type_node) - warning ("inline declaration ignored for function with `...'"); - else - /* Assume that otherwise the function can be inlined. */ - TREE_INLINE (decl) = 1; - - if (specbits & (1 << (int) RID_EXTERN)) - current_extern_inline = 1; - } - } - else - { - /* It's a variable. */ - - /* Move type qualifiers down to element of an array. */ - if (TREE_CODE (type) == ARRAY_TYPE && (constp || volatilep)) - { - type = c_build_type_variant (type, constp, volatilep); -#if 0 /* but a variable whose type is const should still have TREE_READONLY. */ - constp = volatilep = 0; -#endif - } - - decl = build_decl (VAR_DECL, declarator, type); - - if (inlinep) - warning_with_decl (decl, "variable `%s' declared `inline'"); - - /* An uninitialized decl with `extern' is a reference. */ - TREE_EXTERNAL (decl) - = !initialized && (specbits & (1 << (int) RID_EXTERN)); - /* At top level, either `static' or no s.c. makes a definition - (perhaps tentative), and absence of `static' makes it public. */ - if (current_binding_level == global_binding_level) - { - TREE_PUBLIC (decl) = !(specbits & (1 << (int) RID_STATIC)); - TREE_STATIC (decl) = ! TREE_EXTERNAL (decl); - } - /* Not at top level, only `static' makes a static definition. */ - else - { - TREE_STATIC (decl) = (specbits & (1 << (int) RID_STATIC)) != 0; - TREE_PUBLIC (decl) = TREE_EXTERNAL (decl); - /* `extern' with initialization is invalid if not at top level. */ - if ((specbits & (1 << (int) RID_EXTERN)) && initialized) - error ("`%s' has both `extern' and initializer", name); - } - } - - /* Record `register' declaration for warnings on & - and in case doing stupid register allocation. */ - - if (specbits & (1 << (int) RID_REGISTER)) - TREE_REGDECL (decl) = 1; - - /* Record constancy and volatility. */ - - if (constp) - TREE_READONLY (decl) = 1; - if (volatilep) - { - TREE_VOLATILE (decl) = 1; - TREE_THIS_VOLATILE (decl) = 1; - } - - if (resume_temporary) - resume_temporary_allocation (); - - return decl; - } -} - -/* Make a variant type in the proper way for C, propagating qualifiers - down to the element type of an array. */ - -tree -c_build_type_variant (type, constp, volatilep) - tree type; - int constp, volatilep; -{ - if (TREE_CODE (type) != ARRAY_TYPE) - return build_type_variant (type, constp, volatilep); - - return build_array_type (c_build_type_variant (TREE_TYPE (type), - constp, volatilep), - TYPE_DOMAIN (type)); -} - -/* Decode the parameter-list info for a function type or function definition. - The argument is the value returned by `get_parm_info' (or made in parse.y - if there is an identifier list instead of a parameter decl list). - These two functions are separate because when a function returns - or receives functions then each is called multiple times but the order - of calls is different. The last call to `grokparms' is always the one - that contains the formal parameter names of a function definition. - - Store in `last_function_parms' a chain of the decls of parms. - Also store in `last_function_parm_tags' a chain of the struct and union - tags declared among the parms. - - Return a list of arg types to use in the FUNCTION_TYPE for this function. - - FUNCDEF_FLAG is nonzero for a function definition, 0 for - a mere declaration. A nonempty identifier-list gets an error message - when FUNCDEF_FLAG is zero. */ - -static tree -grokparms (parms_info, funcdef_flag) - tree parms_info; - int funcdef_flag; -{ - tree first_parm = TREE_CHAIN (parms_info); - - last_function_parms = TREE_PURPOSE (parms_info); - last_function_parm_tags = TREE_VALUE (parms_info); - - if (warn_strict_prototypes && first_parm == 0 && !funcdef_flag) - warning ("function declaration isn't a prototype"); - - if (first_parm != 0 - && TREE_CODE (TREE_VALUE (first_parm)) == IDENTIFIER_NODE) - { - if (! funcdef_flag) - warning ("parameter names (without types) in function declaration"); - - last_function_parms = first_parm; - return 0; - } - else - { - tree parm; - tree typelt; - /* We no longer test FUNCDEF_FLAG. - If the arg types are incomplete in a declaration, - they must include undefined tags. - These tags can never be defined in the scope of the declaration, - so the types can never be completed, - and no call can be compiled successfully. */ -#if 0 - /* In a fcn definition, arg types must be complete. */ - if (funcdef_flag) -#endif - for (parm = last_function_parms, typelt = first_parm; - parm; - parm = TREE_CHAIN (parm)) - /* Skip over any enumeration constants declared here. */ - if (TREE_CODE (parm) == PARM_DECL) - { - /* Barf if the parameter itself has an incomplete type. */ - tree type = TREE_VALUE (typelt); - if (TYPE_SIZE (type) == 0) - { - if (funcdef_flag && DECL_NAME (parm) != 0) - error ("parameter `%s' has incomplete type", - IDENTIFIER_POINTER (DECL_NAME (parm))); - else - warning ("parameter has incomplete type"); - if (funcdef_flag) - { - TREE_VALUE (typelt) = error_mark_node; - TREE_TYPE (parm) = error_mark_node; - } - } -#if 0 /* This has been replaced by parm_tags_warning - which uses a more accurate criterion for what to warn about. */ - else - { - /* Now warn if is a pointer to an incomplete type. */ - while (TREE_CODE (type) == POINTER_TYPE - || TREE_CODE (type) == REFERENCE_TYPE) - type = TREE_TYPE (type); - type = TYPE_MAIN_VARIANT (type); - if (TYPE_SIZE (type) == 0) - { - if (DECL_NAME (parm) != 0) - warning ("parameter `%s' points to incomplete type", - IDENTIFIER_POINTER (DECL_NAME (parm))); - else - warning ("parameter points to incomplete type"); - } - } -#endif - typelt = TREE_CHAIN (typelt); - } - - return first_parm; - } -} - - -/* Return a tree_list node with info on a parameter list just parsed. - The TREE_PURPOSE is a chain of decls of those parms. - The TREE_VALUE is a list of structure, union and enum tags defined. - The TREE_CHAIN is a list of argument types to go in the FUNCTION_TYPE. - This tree_list node is later fed to `grokparms'. - - VOID_AT_END nonzero means append `void' to the end of the type-list. - Zero means the parmlist ended with an ellipsis so don't append `void'. */ - -tree -get_parm_info (void_at_end) - int void_at_end; -{ - register tree decl; - register tree types = 0; - int erred = 0; - tree tags = gettags (); - tree parms = nreverse (getdecls ()); - - /* Just `void' (and no ellipsis) is special. There are really no parms. */ - if (void_at_end && parms != 0 - && TREE_CHAIN (parms) == 0 - && TREE_TYPE (parms) == void_type_node - && DECL_NAME (parms) == 0) - { - parms = NULL_TREE; - storedecls (NULL_TREE); - return saveable_tree_cons (NULL_TREE, NULL_TREE, - saveable_tree_cons (NULL_TREE, void_type_node, NULL_TREE)); - } - - storedecls (parms); - - for (decl = parms; decl; decl = TREE_CHAIN (decl)) - /* There may also be declarations for enumerators if an enumeration - type is declared among the parms. Ignore them here. */ - if (TREE_CODE (decl) == PARM_DECL) - { - /* Since there is a prototype, - args are passed in their declared types. */ - tree type = TREE_TYPE (decl); - DECL_ARG_TYPE (decl) = type; -#ifdef PROMOTE_PROTOTYPES - if (TREE_CODE (type) == INTEGER_TYPE - && TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node)) - DECL_ARG_TYPE (decl) = integer_type_node; -#endif - - types = saveable_tree_cons (NULL_TREE, TREE_TYPE (decl), types); - if (TREE_VALUE (types) == void_type_node && ! erred - && DECL_NAME (decl) == 0) - { - error ("`void' in parameter list must be the entire list"); - erred = 1; - } - } - - if (void_at_end) - return saveable_tree_cons (parms, tags, - nreverse (saveable_tree_cons (NULL_TREE, void_type_node, types))); - - return saveable_tree_cons (parms, tags, nreverse (types)); -} - -/* At end of parameter list, warn about any struct, union or enum tags - defined within. Do so because these types cannot ever become complete. */ - -void -parmlist_tags_warning () -{ - tree elt; - static int already; - - for (elt = current_binding_level->tags; elt; elt = TREE_CHAIN (elt)) - { - enum tree_code code = TREE_CODE (TREE_VALUE (elt)); - warning ("`%s %s' declared inside parameter list", - (code == RECORD_TYPE ? "struct" - : code == UNION_TYPE ? "union" - : "enum"), - IDENTIFIER_POINTER (TREE_PURPOSE (elt))); - if (! already) - { - warning ("its scope is only this definition or declaration,"); - warning ("which is probably not what you want."); - already = 1; - } - } -} - -/* Get the struct, enum or union (CODE says which) with tag NAME. - Define the tag as a forward-reference if it is not defined. */ - -tree -xref_tag (code, name) - enum tree_code code; - tree name; -{ - int temporary = allocation_temporary_p (); - - /* If a cross reference is requested, look up the type - already defined for this tag and return it. */ - - register tree ref = lookup_tag (code, name, current_binding_level, 0); - if (ref) return ref; - - if (current_binding_level == global_binding_level && temporary) - end_temporary_allocation (); - - /* If no such tag is yet defined, create a forward-reference node - and record it as the "definition". - When a real declaration of this type is found, - the forward-reference will be altered into a real type. */ - - ref = make_node (code); - if (code == ENUMERAL_TYPE) - { - /* (In ANSI, Enums can be referred to only if already defined.) */ - if (pedantic) - warning ("ANSI C forbids forward references to `enum' types"); - /* Give the type a default layout like unsigned int - to avoid crashing if it does not get defined. */ - TYPE_MODE (ref) = SImode; - TYPE_ALIGN (ref) = TYPE_ALIGN (unsigned_type_node); - TREE_UNSIGNED (ref) = 1; - TYPE_PRECISION (ref) = TYPE_PRECISION (unsigned_type_node); - TYPE_MIN_VALUE (ref) = TYPE_MIN_VALUE (unsigned_type_node); - TYPE_MAX_VALUE (ref) = TYPE_MAX_VALUE (unsigned_type_node); - } - - pushtag (name, ref); - - if (current_binding_level == global_binding_level && temporary) - resume_temporary_allocation (); - - return ref; -} - -/* Make sure that the tag NAME is defined *in the current binding level* - at least as a forward reference. - CODE says which kind of tag NAME ought to be. */ - -tree -start_struct (code, name) - enum tree_code code; - tree name; -{ - /* If there is already a tag defined at this binding level - (as a forward reference), just return it. */ - - register tree ref = 0; - - if (name != 0) - ref = lookup_tag (code, name, current_binding_level, 1); - if (ref && TREE_CODE (ref) == code) - { - if (TYPE_FIELDS (ref)) - error ((code == UNION_TYPE ? "redefinition of `union %s'" - : "redefinition of `struct %s'"), - IDENTIFIER_POINTER (name)); - - return ref; - } - - /* Otherwise create a forward-reference just so the tag is in scope. */ - - ref = make_node (code); - pushtag (name, ref); - return ref; -} - -/* Process the specs, declarator (NULL if omitted) and width (NULL if omitted) - of a structure component, returning a FIELD_DECL node. - WIDTH is non-NULL for bit fields only, and is an INTEGER_CST node. - - This is done during the parsing of the struct declaration. - The FIELD_DECL nodes are chained together and the lot of them - are ultimately passed to `build_struct' to make the RECORD_TYPE node. */ - -tree -grokfield (filename, line, declarator, declspecs, width) - char *filename; - int line; - tree declarator, declspecs, width; -{ - register tree value = grokdeclarator (declarator, declspecs, FIELD, 0); - - finish_decl (value, NULL, NULL); - DECL_INITIAL (value) = width; - - return value; -} - -/* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T. - FIELDLIST is a chain of FIELD_DECL nodes for the fields. */ - -tree -finish_struct (t, fieldlist) - register tree t, fieldlist; -{ - register tree x; - int old_momentary; - int round_up_size = 1; - - /* If this type was previously laid out as a forward reference, - make sure we lay it out again. */ - - TYPE_SIZE (t) = 0; - - if (extra_warnings && in_parm_level_p ()) - warning ((TREE_CODE (t) == UNION_TYPE ? "union defined inside parms" - : "structure defined inside parms")); - - old_momentary = suspend_momentary (); - - if (fieldlist == 0 && pedantic) - warning ((TREE_CODE (t) == UNION_TYPE ? "union has no members" - : "structure has no members")); - - /* Install struct as DECL_CONTEXT of each field decl. - Also process specified field sizes. - Set DECL_SIZE_UNIT to the specified size, or 0 if none specified. - The specified size is found in the DECL_INITIAL. - Store 0 there, except for ": 0" fields (so we can find them - and delete them, below). */ - - for (x = fieldlist; x; x = TREE_CHAIN (x)) - { - DECL_CONTEXT (x) = t; - DECL_SIZE_UNIT (x) = 0; - - /* If any field is const, the structure type is pseudo-const. */ - if (TREE_READONLY (x)) - C_TYPE_FIELDS_READONLY (t) = 1; - else - { - /* A field that is pseudo-const makes the structure likewise. */ - tree t1 = TREE_TYPE (x); - while (TREE_CODE (t1) == ARRAY_TYPE) - t1 = TREE_TYPE (t1); - if ((TREE_CODE (t1) == RECORD_TYPE || TREE_CODE (t1) == UNION_TYPE) - && C_TYPE_FIELDS_READONLY (t1)) - C_TYPE_FIELDS_READONLY (t) = 1; - } - - /* Detect invalid bit-field size. */ - if (DECL_INITIAL (x) && TREE_CODE (DECL_INITIAL (x)) != INTEGER_CST) - { - error_with_decl (x, "bit-field `%s' width not an integer constant"); - DECL_INITIAL (x) = NULL; - } - - /* Detect invalid bit-field type. */ - if (DECL_INITIAL (x) - && TREE_CODE (TREE_TYPE (x)) != INTEGER_TYPE - && TREE_CODE (TREE_TYPE (x)) != ENUMERAL_TYPE) - { - error_with_decl (x, "bit-field `%s' has invalid type"); - DECL_INITIAL (x) = NULL; - } - if (DECL_INITIAL (x) && pedantic - && TREE_TYPE (x) != integer_type_node - && TREE_TYPE (x) != unsigned_type_node) - warning_with_decl (x, "bit-field `%s' type invalid in ANSI C"); - - /* Detect and ignore out of range field width. */ - if (DECL_INITIAL (x)) - { - register int width = TREE_INT_CST_LOW (DECL_INITIAL (x)); - - if (width < 0) - { - DECL_INITIAL (x) = NULL; - warning_with_decl (x, "negative width in bit-field `%s'"); - } - else if (width == 0 && DECL_NAME (x) != 0) - { - error_with_decl (x, "zero width for bit-field `%s'"); - DECL_INITIAL (x) = NULL; - } - else if (width > TYPE_PRECISION (TREE_TYPE (x))) - { - DECL_INITIAL (x) = NULL; - warning_with_decl (x, "width of `%s' exceeds its type"); - } - } - - /* Process valid field width. */ - if (DECL_INITIAL (x)) - { - register int width = TREE_INT_CST_LOW (DECL_INITIAL (x)); - - if (width == 0) - { - /* field size 0 => mark following field as "aligned" */ - if (TREE_CHAIN (x)) - DECL_ALIGN (TREE_CHAIN (x)) - = MAX (DECL_ALIGN (TREE_CHAIN (x)), EMPTY_FIELD_BOUNDARY); - /* field of size 0 at the end => round up the size. */ - else - round_up_size = EMPTY_FIELD_BOUNDARY; - } - else - { - DECL_INITIAL (x) = NULL; - DECL_SIZE_UNIT (x) = width; - TREE_PACKED (x) = 1; - /* Traditionally a bit field is unsigned - even if declared signed. */ - if (flag_traditional - && TREE_CODE (TREE_TYPE (x)) == INTEGER_TYPE) - TREE_TYPE (x) = unsigned_type_node; - } - } - else - /* Non-bit-fields are aligned for their type. */ - DECL_ALIGN (x) = MAX (DECL_ALIGN (x), TYPE_ALIGN (TREE_TYPE (x))); - } - - /* Now DECL_INITIAL is null on all members except for zero-width bit-fields. - And they have already done their work. */ - - /* Delete all zero-width bit-fields from the front of the fieldlist */ - while (fieldlist - && DECL_INITIAL (fieldlist)) - fieldlist = TREE_CHAIN (fieldlist); - /* Delete all such members from the rest of the fieldlist */ - for (x = fieldlist; x;) - { - if (TREE_CHAIN (x) && DECL_INITIAL (TREE_CHAIN (x))) - TREE_CHAIN (x) = TREE_CHAIN (TREE_CHAIN (x)); - else x = TREE_CHAIN (x); - } - - /* Delete all duplicate fields from the fieldlist */ - for (x = fieldlist; x && TREE_CHAIN (x);) - /* Anonymous fields aren't duplicates. */ - if (DECL_NAME (TREE_CHAIN (x)) == 0) - x = TREE_CHAIN (x); - else - { - register tree y = fieldlist; - - while (1) - { - if (DECL_NAME (y) == DECL_NAME (TREE_CHAIN (x))) - break; - if (y == x) - break; - y = TREE_CHAIN (y); - } - if (DECL_NAME (y) == DECL_NAME (TREE_CHAIN (x))) - { - error_with_decl (TREE_CHAIN (x), "duplicate member `%s'"); - TREE_CHAIN (x) = TREE_CHAIN (TREE_CHAIN (x)); - } - else x = TREE_CHAIN (x); - } - - /* Now we have the final fieldlist. Record it, - then lay out the structure or union (including the fields). */ - - TYPE_FIELDS (t) = fieldlist; - - /* If there's a :0 field at the end, round the size to the - EMPTY_FIELD_BOUNDARY. */ - TYPE_ALIGN (t) = round_up_size; - - for (x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x)) - { - TYPE_FIELDS (x) = TYPE_FIELDS (t); - TYPE_ALIGN (x) = TYPE_ALIGN (t); - } - - layout_type (t); - - /* Promote each bit-field's type to int if it is narrower than that. */ - for (x = fieldlist; x; x = TREE_CHAIN (x)) - if (TREE_PACKED (x) - && TREE_CODE (TREE_TYPE (x)) == INTEGER_TYPE - && (TREE_INT_CST_LOW (DECL_SIZE (x)) * DECL_SIZE_UNIT (x) - < TYPE_PRECISION (integer_type_node))) - TREE_TYPE (x) = integer_type_node; - - /* If this structure or union completes the type of any previous - variable declaration, lay it out and output its rtl. */ - - if (current_binding_level->n_incomplete != 0) - { - tree decl; - for (decl = current_binding_level->names; decl; decl = TREE_CHAIN (decl)) - { - if (TREE_TYPE (decl) == t - && TREE_CODE (decl) != TYPE_DECL) - { - int toplevel = global_binding_level == current_binding_level; - layout_decl (decl, 0); - rest_of_decl_compilation (decl, 0, toplevel, 0); - if (! toplevel) - expand_decl (decl, NULL_TREE); - --current_binding_level->n_incomplete; - } - else if (TYPE_SIZE (TREE_TYPE (decl)) == 0 - && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE) - { - tree element = TREE_TYPE (decl); - while (TREE_CODE (element) == ARRAY_TYPE) - element = TREE_TYPE (element); - if (element == t) - layout_array_type (TREE_TYPE (decl)); - } - } - } - - resume_momentary (old_momentary); - - return t; -} - -/* Lay out the type T, and its element type, and so on. */ - -static void -layout_array_type (t) - tree t; -{ - if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE) - layout_array_type (TREE_TYPE (t)); - layout_type (t); -} - -/* Begin compiling the definition of an enumeration type. - NAME is its name (or null if anonymous). - Returns the type object, as yet incomplete. - Also records info about it so that build_enumerator - may be used to declare the individual values as they are read. */ - -tree -start_enum (name) - tree name; -{ - register tree enumtype = 0; - - /* If this is the real definition for a previous forward reference, - fill in the contents in the same object that used to be the - forward reference. */ - - if (name != 0) - enumtype = lookup_tag (ENUMERAL_TYPE, name, current_binding_level, 1); - - if (enumtype == 0 || TREE_CODE (enumtype) != ENUMERAL_TYPE) - { - enumtype = make_node (ENUMERAL_TYPE); - pushtag (name, enumtype); - } - - if (TYPE_VALUES (enumtype) != 0) - { - /* This enum is a named one that has been declared already. */ - error ("redeclaration of `enum %s'", IDENTIFIER_POINTER (name)); - - /* Completely replace its old definition. - The old enumerators remain defined, however. */ - TYPE_VALUES (enumtype) = 0; - } - - /* Initially, set up this enum as like `int' - so that we can create the enumerators' declarations and values. - Later on, the precision of the type may be changed and - it may be laid out again. */ - - TYPE_PRECISION (enumtype) = TYPE_PRECISION (integer_type_node); - TYPE_SIZE (enumtype) = 0; - fixup_unsigned_type (enumtype); - - enum_next_value = integer_zero_node; - - return enumtype; -} - -/* After processing and defining all the values of an enumeration type, - install their decls in the enumeration type and finish it off. - ENUMTYPE is the type object and VALUES a list of name-value pairs. - Returns ENUMTYPE. */ - -tree -finish_enum (enumtype, values) - register tree enumtype, values; -{ - register tree pair; - register long maxvalue = 0; - register long minvalue = 0; - register int i; - - if (in_parm_level_p ()) - warning ("enum defined inside parms"); - - TYPE_VALUES (enumtype) = values; - - /* Calculate the maximum value of any enumerator in this type. */ - - for (pair = values; pair; pair = TREE_CHAIN (pair)) - { - int value = TREE_INT_CST_LOW (TREE_VALUE (pair)); - if (pair == values) - minvalue = maxvalue = value; - else - { - if (value > maxvalue) - maxvalue = value; - if (value < minvalue) - minvalue = value; - } - } - - if (flag_short_enums) - { - /* Determine the precision this type needs, lay it out, and define it. */ - - for (i = maxvalue; i; i >>= 1) - TYPE_PRECISION (enumtype)++; - - if (!TYPE_PRECISION (enumtype)) - TYPE_PRECISION (enumtype) = 1; - - /* Cancel the laying out previously done for the enum type, - so that fixup_unsigned_type will do it over. */ - TYPE_SIZE (enumtype) = 0; - - fixup_unsigned_type (enumtype); - } - - TREE_INT_CST_LOW (TYPE_MAX_VALUE (enumtype)) = maxvalue; - - /* An enum can have some negative values; then it is signed. */ - if (minvalue < 0) - { - TREE_INT_CST_LOW (TYPE_MIN_VALUE (enumtype)) = minvalue; - TREE_INT_CST_HIGH (TYPE_MIN_VALUE (enumtype)) = -1; - TREE_UNSIGNED (enumtype) = 0; - } - return enumtype; -} - -/* Build and install a CONST_DECL for one value of the - current enumeration type (one that was begun with start_enum). - Return a tree-list containing the name and its value. - Assignment of sequential values by default is handled here. */ - -tree -build_enumerator (name, value) - tree name, value; -{ - register tree decl; - - /* Validate and default VALUE. */ - - /* Remove no-op casts from the value. */ - while (value != 0 && TREE_CODE (value) == NOP_EXPR) - value = TREE_OPERAND (value, 0); - - if (value != 0 && TREE_CODE (value) != INTEGER_CST) - { - error ("enumerator value for `%s' not integer constant", - IDENTIFIER_POINTER (name)); - value = 0; - } - - /* Default based on previous value. */ - if (value == 0) - value = enum_next_value; - - /* Might as well enforce the ANSI restriction, since - values outside this range don't work in version 1. */ - if (! int_fits_type_p (value, integer_type_node)) - { - error ("enumerator value outside range of `int'"); - value = integer_zero_node; - } - - /* Set basis for default for next value. */ - enum_next_value = build_binary_op_nodefault (PLUS_EXPR, value, - integer_one_node, PLUS_EXPR); - - /* Now create a declaration for the enum value name. */ - - decl = build_decl (CONST_DECL, name, integer_type_node); - DECL_INITIAL (decl) = value; - TREE_TYPE (value) = integer_type_node; - pushdecl (decl); - - return saveable_tree_cons (name, value, NULL); -} - -/* Create the FUNCTION_DECL for a function definition. - DECLSPECS and DECLARATOR are the parts of the declaration; - they describe the function's name and the type it returns, - but twisted together in a fashion that parallels the syntax of C. - - This function creates a binding context for the function body - as well as setting up the FUNCTION_DECL in current_function_decl. - - Returns 1 on success. If the DECLARATOR is not suitable for a function - (it defines a datum instead), we return 0, which tells - yyparse to report a parse error. */ - -int -start_function (declspecs, declarator) - tree declarator, declspecs; -{ - tree decl1, old_decl; - tree restype; - - current_function_returns_value = 0; /* Assume, until we see it does. */ - current_function_returns_null = 0; - warn_about_return_type = 0; - current_extern_inline = 0; - - decl1 = grokdeclarator (declarator, declspecs, FUNCDEF, 1); - - /* If the declarator is not suitable for a function definition, - cause a syntax error. */ - if (decl1 == 0) - return 0; - - current_function_decl = decl1; - - announce_function (current_function_decl); - - if (TYPE_SIZE (TREE_TYPE (TREE_TYPE (decl1))) == 0) - { - error ("return-type is an incomplete type"); - /* Make it return void instead. */ - TREE_TYPE (decl1) - = build_function_type (void_type_node, - TYPE_ARG_TYPES (TREE_TYPE (decl1))); - } - - if (warn_about_return_type) - warning ("return-type defaults to `int'"); - - /* Save the parm names or decls from this function's declarator - where store_parm_decls will find them. */ - current_function_parms = last_function_parms; - current_function_parm_tags = last_function_parm_tags; - - /* Make the init_value nonzero so pushdecl knows this is not tentative. - error_mark_node is replaced below (in poplevel) with the LET_STMT. */ - DECL_INITIAL (current_function_decl) = error_mark_node; - - /* If this definition isn't a prototype and we had a prototype declaration - before, copy the arg type info from that prototype. */ - old_decl = lookup_name_current_level (DECL_NAME (current_function_decl)); - if (old_decl != 0 - && TREE_TYPE (TREE_TYPE (current_function_decl)) == TREE_TYPE (TREE_TYPE (old_decl)) - && TYPE_ARG_TYPES (TREE_TYPE (current_function_decl)) == 0) - TREE_TYPE (current_function_decl) = TREE_TYPE (old_decl); - - /* This is a definition, not a reference. - So normally clear TREE_EXTERNAL. - However, `extern inline' acts like a declaration - except for defining how to inline. So set TREE_EXTERNAL in that case. */ - TREE_EXTERNAL (current_function_decl) = current_extern_inline; - - /* This function exists in static storage. - (This does not mean `static' in the C sense!) */ - TREE_STATIC (current_function_decl) = 1; - - /* Record the decl so that the function name is defined. - If we already have a decl for this name, and it is a FUNCTION_DECL, - use the old decl. */ - - current_function_decl = pushdecl (current_function_decl); - - pushlevel (0); - declare_parm_level (); - - make_function_rtl (current_function_decl); - - restype = TREE_TYPE (TREE_TYPE (current_function_decl)); - /* Promote the value to int before returning it. */ - if (TREE_CODE (restype) == INTEGER_TYPE - && TYPE_PRECISION (restype) < TYPE_PRECISION (integer_type_node)) - restype = integer_type_node; - DECL_RESULT_TYPE (current_function_decl) = restype; - DECL_RESULT (current_function_decl) - = build_decl (RESULT_DECL, value_identifier, restype); - - /* Allocate further tree nodes temporarily during compilation - of this function only. */ - temporary_allocation (); - - /* If this fcn was already referenced via a block-scope `extern' decl - (or an implicit decl), propagate certain information about the usage. */ - if (TREE_ADDRESSABLE (DECL_NAME (current_function_decl))) - TREE_ADDRESSABLE (current_function_decl) = 1; - - return 1; -} - -/* Store the parameter declarations into the current function declaration. - This is called after parsing the parameter declarations, before - digesting the body of the function. */ - -void -store_parm_decls () -{ - register tree fndecl = current_function_decl; - register tree parm; - - /* This is either a chain of PARM_DECLs (if a prototype was used) - or a list of IDENTIFIER_NODEs (for an old-fashioned C definition). */ - tree specparms = current_function_parms; - - /* This is a list of types declared among parms in a prototype. */ - tree parmtags = current_function_parm_tags; - - /* This is a chain of PARM_DECLs from old-style parm declarations. */ - register tree parmdecls = getdecls (); - - /* This is a chain of any other decls that came in among the parm - declarations. If a parm is declared with enum {foo, bar} x; - then CONST_DECLs for foo and bar are put here. */ - tree nonparms = 0; - - if (specparms != 0 && TREE_CODE (specparms) != TREE_LIST) - { - /* This case is when the function was defined with an ANSI prototype. - The parms already have decls, so we need not do anything here - except record them as in effect - and complain if any redundant old-style parm decls were written. */ - - register tree next; - tree others = 0; - - if (parmdecls != 0) - error_with_decl (fndecl, - "parm types given both in parmlist and separately"); - - specparms = nreverse (specparms); - for (parm = specparms; parm; parm = next) - { - next = TREE_CHAIN (parm); - if (DECL_NAME (parm) == 0) - error_with_decl (parm, "parameter name omitted"); - else if (TREE_TYPE (parm) == void_type_node) - error_with_decl (parm, "parameter `%s' declared void"); - else if (TREE_CODE (parm) == PARM_DECL) - pushdecl (parm); - else - { - /* If we find an enum constant, put it aside for the moment. */ - TREE_CHAIN (parm) = 0; - others = chainon (others, parm); - } - } - - /* Get the decls in their original chain order - and record in the function. */ - DECL_ARGUMENTS (fndecl) = getdecls (); - - /* Now pushdecl the enum constants. */ - for (parm = others; parm; parm = next) - { - next = TREE_CHAIN (parm); - if (DECL_NAME (parm) == 0) - ; - else if (TREE_TYPE (parm) == void_type_node) - ; - else if (TREE_CODE (parm) != PARM_DECL) - pushdecl (parm); - } - - storetags (chainon (parmtags, gettags ())); - } - else - { - /* SPECPARMS is an identifier list--a chain of TREE_LIST nodes - each with a parm name as the TREE_VALUE. - - PARMDECLS is a list of declarations for parameters. - Warning! It can also contain CONST_DECLs which are not parameters - but are names of enumerators of any enum types - declared among the parameters. - - First match each formal parameter name with its declaration. - Associate decls with the names and store the decls - into the TREE_PURPOSE slots. */ - - for (parm = specparms; parm; parm = TREE_CHAIN (parm)) - { - register tree tail, found = NULL; - - if (TREE_VALUE (parm) == 0) - { - error_with_decl (fndecl, "parameter name missing from parameter list"); - TREE_PURPOSE (parm) = 0; - continue; - } - - /* See if any of the parmdecls specifies this parm by name. - Ignore any enumerator decls. */ - for (tail = parmdecls; tail; tail = TREE_CHAIN (tail)) - if (DECL_NAME (tail) == TREE_VALUE (parm) - && TREE_CODE (tail) == PARM_DECL) - { - found = tail; - break; - } - - /* If declaration already marked, we have a duplicate name. - Complain, and don't use this decl twice. */ - if (found && DECL_CONTEXT (found) != 0) - { - error_with_decl (found, "multiple parameters named `%s'"); - found = 0; - } - - /* If the declaration says "void", complain and ignore it. */ - if (found && TREE_TYPE (found) == void_type_node) - { - error_with_decl (found, "parameter `%s' declared void"); - TREE_TYPE (found) = integer_type_node; - DECL_ARG_TYPE (found) = integer_type_node; - layout_decl (found, 0); - } - - /* If no declaration found, default to int. */ - if (!found) - { - found = build_decl (PARM_DECL, TREE_VALUE (parm), - integer_type_node); - DECL_ARG_TYPE (found) = TREE_TYPE (found); - DECL_SOURCE_LINE (found) = DECL_SOURCE_LINE (fndecl); - DECL_SOURCE_FILE (found) = DECL_SOURCE_FILE (fndecl); - if (extra_warnings) - warning_with_decl (found, "type of `%s' defaults to `int'"); - pushdecl (found); - } - - TREE_PURPOSE (parm) = found; - - /* Mark this decl as "already found" -- see test, above. - It is safe to clobber DECL_CONTEXT temporarily - because the final values are not stored until - the `poplevel' in `finish_function'. */ - DECL_CONTEXT (found) = error_mark_node; - } - - /* Complain about declarations not matched with any names. - Put any enumerator constants onto the list NONPARMS. */ - - nonparms = 0; - for (parm = parmdecls; parm; ) - { - tree next = TREE_CHAIN (parm); - TREE_CHAIN (parm) = 0; - - /* Complain about args with incomplete types. */ - if (TYPE_SIZE (TREE_TYPE (parm)) == 0) - { - error_with_decl (parm, "parameter `%s' has incomplete type"); - TREE_TYPE (parm) = error_mark_node; - } - - if (TREE_CODE (parm) != PARM_DECL) - nonparms = chainon (nonparms, parm); - - else if (DECL_CONTEXT (parm) == 0) - { - error_with_decl (parm, - "declaration for parameter `%s' but no such parameter"); - /* Pretend the parameter was not missing. - This gets us to a standard state and minimizes - further error messages. */ - specparms - = chainon (specparms, - tree_cons (parm, NULL_TREE, NULL_TREE)); - } - - parm = next; - } - - /* Chain the declarations together in the order of the list of names. */ - /* Store that chain in the function decl, replacing the list of names. */ - parm = specparms; - DECL_ARGUMENTS (fndecl) = 0; - { - register tree last; - for (last = 0; parm; parm = TREE_CHAIN (parm)) - if (TREE_PURPOSE (parm)) - { - if (last == 0) - DECL_ARGUMENTS (fndecl) = TREE_PURPOSE (parm); - else - TREE_CHAIN (last) = TREE_PURPOSE (parm); - last = TREE_PURPOSE (parm); - TREE_CHAIN (last) = 0; - DECL_CONTEXT (last) = 0; - } - } - - /* If there was a previous prototype, - set the DECL_ARG_TYPE of each argument according to - the type previously specified, and report any mismatches. */ - - if (TYPE_ARG_TYPES (TREE_TYPE (fndecl))) - { - register tree type; - for (parm = DECL_ARGUMENTS (fndecl), - type = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); - parm || (type && TREE_VALUE (type) != void_type_node); - parm = TREE_CHAIN (parm), type = TREE_CHAIN (type)) - { - if (parm == 0 || type == 0 - || TREE_VALUE (type) == void_type_node) - { - error ("number of arguments doesn't match prototype"); - break; - } - /* Type for passing arg must be consistent - with that declared for the arg. */ - if (! comptypes (DECL_ARG_TYPE (parm), TREE_VALUE (type))) - { - error ("argument `%s' doesn't match function prototype", - IDENTIFIER_POINTER (DECL_NAME (parm))); - if (DECL_ARG_TYPE (parm) == integer_type_node - && TREE_VALUE (type) == TREE_TYPE (parm)) - { - error ("a formal parameter type that promotes to `int'"); - error ("can match only `int' in the prototype"); - } - if (DECL_ARG_TYPE (parm) == double_type_node - && TREE_VALUE (type) == TREE_TYPE (parm)) - { - error ("a formal parameter type that promotes to `double'"); - error ("can match only `double' in the prototype"); - } - } - } - } - - /* Now store the final chain of decls for the arguments - as the decl-chain of the current lexical scope. - Put the enumerators in as well, at the front so that - DECL_ARGUMENTS is not modified. */ - - storedecls (chainon (nonparms, DECL_ARGUMENTS (fndecl))); - } - - /* Make sure the binding level for the top of the function body - gets a LET_STMT if there are any in the function. - Otherwise, the dbx output is wrong. */ - - keep_next_if_subblocks = 1; - - /* Initialize the RTL code for the function. */ - - init_function_start (fndecl, input_filename, lineno); - - /* Set up parameters and prepare for return, for the function. */ - - expand_function_start (fndecl, 0); -} - -/* Finish up a function declaration and compile that function - all the way to assembler language output. The free the storage - for the function definition. - - This is called after parsing the body of the function definition. - LINENO is the current line number. */ - -void -finish_function (lineno) - int lineno; -{ - register tree fndecl = current_function_decl; - -/* TREE_READONLY (fndecl) = 1; - This caused &foo to be of type ptr-to-const-function - which then got a warning when stored in a ptr-to-function variable. */ - - poplevel (1, 0, 1); - - /* Must mark the RESULT_DECL as being in this function. */ - - DECL_CONTEXT (DECL_RESULT (fndecl)) = DECL_INITIAL (fndecl); - - /* Obey `register' declarations if `setjmp' is called in this fn. */ - if (flag_traditional && current_function_calls_setjmp) - setjmp_protect (DECL_INITIAL (fndecl)); - - /* Generate rtl for function exit. */ - expand_function_end (input_filename, lineno); - - /* So we can tell if jump_optimize sets it to 1. */ - current_function_returns_null = 0; - - /* Run the optimizers and output the assembler code for this function. */ - rest_of_compilation (fndecl); - - if (TREE_THIS_VOLATILE (fndecl) && current_function_returns_null) - warning ("`volatile' function does return"); - else if (warn_return_type && current_function_returns_null - && TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node) - /* If this function returns non-void and control can drop through, - complain. */ - warning ("control reaches end of non-void function"); - /* With just -W, complain only if function returns both with - and without a value. */ - else if (extra_warnings - && current_function_returns_value && current_function_returns_null) - warning ("this function may return with or without a value"); - - /* Free all the tree nodes making up this function. */ - /* Switch back to allocating nodes permanently - until we start another function. */ - permanent_allocation (); - - if (DECL_SAVED_INSNS (fndecl) == 0) - { - /* Stop pointing to the local nodes about to be freed. */ - /* But DECL_INITIAL must remain nonzero so we know this - was an actual function definition. */ - DECL_INITIAL (fndecl) = error_mark_node; - DECL_ARGUMENTS (fndecl) = 0; - } - - /* Let the error reporting routines know that we're outside a function. */ - current_function_decl = NULL; -} diff --git a/gnu/usr.bin/gcc1/cc1/c-parse.gperf b/gnu/usr.bin/gcc1/cc1/c-parse.gperf deleted file mode 100644 index feef59babb..0000000000 --- a/gnu/usr.bin/gcc1/cc1/c-parse.gperf +++ /dev/null @@ -1,56 +0,0 @@ -%{ -/* Command-line: gperf -p -j1 -i 1 -g -o -t -N is_reserved_word -k1,3,$ c-parse.gperf */ -%} -struct resword { char *name; short token; enum rid rid; }; -%% -__alignof, ALIGNOF, NORID -__alignof__, ALIGNOF, NORID -__asm, ASM, NORID -__asm__, ASM, NORID -__attribute, ATTRIBUTE, NORID -__attribute__, ATTRIBUTE, NORID -__const, TYPE_QUAL, RID_CONST -__const__, TYPE_QUAL, RID_CONST -__inline, SCSPEC, RID_INLINE -__inline__, SCSPEC, RID_INLINE -__signed, TYPESPEC, RID_SIGNED -__signed__, TYPESPEC, RID_SIGNED -__typeof, TYPEOF, NORID -__typeof__, TYPEOF, NORID -__volatile, TYPE_QUAL, RID_VOLATILE -__volatile__, TYPE_QUAL, RID_VOLATILE -asm, ASM, NORID -auto, SCSPEC, RID_AUTO -break, BREAK, NORID -case, CASE, NORID -char, TYPESPEC, RID_CHAR -const, TYPE_QUAL, RID_CONST -continue, CONTINUE, NORID -default, DEFAULT, NORID -do, DO, NORID -double, TYPESPEC, RID_DOUBLE -else, ELSE, NORID -enum, ENUM, NORID -extern, SCSPEC, RID_EXTERN -float, TYPESPEC, RID_FLOAT -for, FOR, NORID -goto, GOTO, NORID -if, IF, NORID -inline, SCSPEC, RID_INLINE -int, TYPESPEC, RID_INT -long, TYPESPEC, RID_LONG -register, SCSPEC, RID_REGISTER -return, RETURN, NORID -short, TYPESPEC, RID_SHORT -signed, TYPESPEC, RID_SIGNED -sizeof, SIZEOF, NORID -static, SCSPEC, RID_STATIC -struct, STRUCT, NORID -switch, SWITCH, NORID -typedef, SCSPEC, RID_TYPEDEF -typeof, TYPEOF, NORID -union, UNION, NORID -unsigned, TYPESPEC, RID_UNSIGNED -void, TYPESPEC, RID_VOID -volatile, TYPE_QUAL, RID_VOLATILE -while, WHILE, NORID diff --git a/gnu/usr.bin/gcc1/cc1/c-parse.h b/gnu/usr.bin/gcc1/cc1/c-parse.h deleted file mode 100644 index 697442e909..0000000000 --- a/gnu/usr.bin/gcc1/cc1/c-parse.h +++ /dev/null @@ -1,49 +0,0 @@ -/* Define constants for communication with parse.y. - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - - -enum rid -{ - RID_UNUSED, - RID_INT, - RID_CHAR, - RID_FLOAT, - RID_DOUBLE, - RID_VOID, - RID_UNUSED1, - - RID_UNSIGNED, - RID_SHORT, - RID_LONG, - RID_AUTO, - RID_STATIC, - RID_EXTERN, - RID_REGISTER, - RID_TYPEDEF, - RID_SIGNED, - RID_CONST, - RID_VOLATILE, - RID_INLINE, - RID_NOALIAS, - - RID_MAX -}; - -#define RID_FIRST_MODIFIER RID_UNSIGNED diff --git a/gnu/usr.bin/gcc1/cc1/c-parse.y b/gnu/usr.bin/gcc1/cc1/c-parse.y deleted file mode 100644 index c0bc6db598..0000000000 --- a/gnu/usr.bin/gcc1/cc1/c-parse.y +++ /dev/null @@ -1,2884 +0,0 @@ -/*- - * - * This code is derived from software copyrighted by the Free Software - * Foundation. - * - * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. - * Modified 1991 by The CSRG at the University of California, Berkeley. - */ - -/* YACC parser for C syntax. - Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* To whomever it may concern: I have heard that such a thing was once -written by AT&T, but I have never seen it. */ - -/* %expect 8 */ - -/* These are the 8 conflicts you should get in parse.output; - the state numbers may vary if minor changes in the grammar are made. - -State 41 contains 1 shift/reduce conflict. (Two ways to recover from error.) -State 92 contains 1 shift/reduce conflict. (Two ways to recover from error.) -State 99 contains 1 shift/reduce conflict. (Two ways to recover from error.) -State 103 contains 1 shift/reduce conflict. (Two ways to recover from error.) -State 119 contains 1 shift/reduce conflict. (See comment at component_decl.) -State 183 contains 1 shift/reduce conflict. (Two ways to recover from error.) -State 193 contains 1 shift/reduce conflict. (Two ways to recover from error.) -State 199 contains 1 shift/reduce conflict. (Two ways to recover from error.) -*/ - -%{ -#ifndef lint -static char sccsid[] = "@(#)c-parse.y 6.3 (Berkeley) 5/8/91"; -#endif /* not lint */ - -#include "config.h" -#include "tree.h" -#include "input.h" -#include "c-parse.h" -#include "c-tree.h" - -#include -#include - -#ifndef errno -extern int errno; -#endif - -void yyerror (); - -/* Cause the `yydebug' variable to be defined. */ -#define YYDEBUG 1 -%} - -%start program - -%union {long itype; tree ttype; enum tree_code code; } - -/* All identifiers that are not reserved words - and are not declared typedefs in the current block */ -%token IDENTIFIER - -/* All identifiers that are declared typedefs in the current block. - In some contexts, they are treated just like IDENTIFIER, - but they can also serve as typespecs in declarations. */ -%token TYPENAME - -/* Reserved words that specify storage class. - yylval contains an IDENTIFIER_NODE which indicates which one. */ -%token SCSPEC - -/* Reserved words that specify type. - yylval contains an IDENTIFIER_NODE which indicates which one. */ -%token TYPESPEC - -/* Reserved words that qualify type: "const" or "volatile". - yylval contains an IDENTIFIER_NODE which indicates which one. */ -%token TYPE_QUAL - -/* Character or numeric constants. - yylval is the node for the constant. */ -%token CONSTANT - -/* String constants in raw form. - yylval is a STRING_CST node. */ -%token STRING - -/* "...", used for functions with variable arglists. */ -%token ELLIPSIS - -/* the reserved words */ -%token SIZEOF ENUM STRUCT UNION IF ELSE WHILE DO FOR SWITCH CASE DEFAULT -%token BREAK CONTINUE RETURN GOTO ASM TYPEOF ALIGNOF -%token ATTRIBUTE - -/* Add precedence rules to solve dangling else s/r conflict */ -%nonassoc IF -%nonassoc ELSE - -/* Define the operator tokens and their precedences. - The value is an integer because, if used, it is the tree code - to use in the expression made from the operator. */ - -%right ASSIGN '=' -%right '?' ':' -%left OROR -%left ANDAND -%left '|' -%left '^' -%left '&' -%left EQCOMPARE -%left ARITHCOMPARE -%left LSHIFT RSHIFT -%left '+' '-' -%left '*' '/' '%' -%right UNARY PLUSPLUS MINUSMINUS -%left HYPERUNARY -%left POINTSAT '.' '(' '[' - -%type unop - -%type identifier IDENTIFIER TYPENAME CONSTANT expr nonnull_exprlist exprlist -%type expr_no_commas cast_expr unary_expr primary string STRING -%type typed_declspecs reserved_declspecs -%type typed_typespecs reserved_typespecquals -%type declmods typespec typespecqual_reserved -%type SCSPEC TYPESPEC TYPE_QUAL nonempty_type_quals maybe_type_qual -%type initdecls notype_initdecls initdcl notype_initdcl -%type init initlist maybeasm -%type asm_operands nonnull_asm_operands asm_operand asm_clobbers -%type maybe_attribute attribute_list attrib - -%type compstmt - -%type declarator -%type notype_declarator after_type_declarator -%type parm_declarator - -%type structsp component_decl_list component_decl_list2 -%type component_decl components component_declarator -%type enumlist enumerator -%type typename absdcl absdcl1 type_quals -%type xexpr parms parm identifiers - -%type parmlist parmlist_1 parmlist_2 -%type parmlist_or_identifiers parmlist_or_identifiers_1 - -%type setspecs - -%{ -/* the declaration found for the last IDENTIFIER token read in. - yylex must look this up to detect typedefs, which get token type TYPENAME, - so it is left around in case the identifier is not a typedef but is - used in a context which makes it a reference to a variable. */ -static tree lastiddecl; - -static tree make_pointer_declarator (); -static tree combine_strings (); -static void reinit_parse_for_function (); - -/* List of types and structure classes of the current declaration. */ -tree current_declspecs; - -/* Stack of saved values of current_declspecs. */ -tree declspec_stack; - -int undeclared_variable_notice; /* 1 if we explained undeclared var errors. */ - -static int yylex (); -%} - -%% -program: /* empty */ - | extdefs - ; - -/* the reason for the strange actions in this rule - is so that notype_initdecls when reached via datadef - can find a valid list of type and sc specs in $0. */ - -extdefs: - {$$ = NULL_TREE; } extdef - | extdefs {$$ = NULL_TREE; } extdef - ; - -extdef: - fndef - | datadef - | ASM '(' string ')' ';' - { if (pedantic) - warning ("ANSI C forbids use of `asm' keyword"); - if (TREE_CHAIN ($3)) $3 = combine_strings ($3); - assemble_asm ($3); } - ; - -datadef: - setspecs notype_initdecls ';' - { if (pedantic) - error ("ANSI C forbids data definition lacking type or storage class"); - else if (!flag_traditional) - warning ("data definition lacks type or storage class"); } - | declmods setspecs notype_initdecls ';' - {} - | typed_declspecs setspecs initdecls ';' - {} - | declmods ';' - { error ("empty declaration"); } - | typed_declspecs ';' - { shadow_tag ($1); } - | error ';' - | error '}' - | ';' - { if (pedantic) - warning ("ANSI C does not allow extra `;' outside of a function"); } - ; - -fndef: - typed_declspecs setspecs declarator - { if (! start_function ($1, $3)) - YYERROR; - reinit_parse_for_function (); } - xdecls - { store_parm_decls (); } - compstmt_or_error - { finish_function (lineno); } - | typed_declspecs setspecs declarator error - { } - | declmods setspecs notype_declarator - { if (! start_function ($1, $3)) - YYERROR; - reinit_parse_for_function (); } - xdecls - { store_parm_decls (); } - compstmt_or_error - { finish_function (lineno); } - | declmods setspecs notype_declarator error - { } - | setspecs notype_declarator - { if (! start_function (0, $2)) - YYERROR; - reinit_parse_for_function (); } - xdecls - { store_parm_decls (); } - compstmt_or_error - { finish_function (lineno); } - | setspecs notype_declarator error - { } - ; - -identifier: - IDENTIFIER - | TYPENAME - ; - -unop: '&' - { $$ = ADDR_EXPR; } - | '-' - { $$ = NEGATE_EXPR; } - | '+' - { $$ = CONVERT_EXPR; } - | PLUSPLUS - { $$ = PREINCREMENT_EXPR; } - | MINUSMINUS - { $$ = PREDECREMENT_EXPR; } - | '~' - { $$ = BIT_NOT_EXPR; } - | '!' - { $$ = TRUTH_NOT_EXPR; } - ; - -expr: nonnull_exprlist - { $$ = build_compound_expr ($1); } - ; - -exprlist: - /* empty */ - { $$ = NULL_TREE; } - | nonnull_exprlist - ; - -nonnull_exprlist: - expr_no_commas - { $$ = build_tree_list (NULL_TREE, $1); } - | nonnull_exprlist ',' expr_no_commas - { chainon ($1, build_tree_list (NULL_TREE, $3)); } - ; - -unary_expr: - primary - | '*' cast_expr %prec UNARY - { $$ = build_indirect_ref ($2, "unary *"); } - | unop cast_expr %prec UNARY - { $$ = build_unary_op ($1, $2, 0); } - | SIZEOF unary_expr %prec UNARY - { if (TREE_CODE ($2) == COMPONENT_REF - && TREE_PACKED (TREE_OPERAND ($2, 1))) - error ("`sizeof' applied to a bit-field"); - /* ANSI says arrays and functions are converted inside comma. - But we can't really convert them in build_compound_expr - because that would break commas in lvalues. - So do the conversion here if operand was a comma. */ - if (TREE_CODE ($2) == COMPOUND_EXPR - && (TREE_CODE (TREE_TYPE ($2)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE ($2)) == FUNCTION_TYPE)) - $2 = default_conversion ($2); - $$ = c_sizeof (TREE_TYPE ($2)); } - | SIZEOF '(' typename ')' %prec HYPERUNARY - { $$ = c_sizeof (groktypename ($3)); } - | ALIGNOF unary_expr %prec UNARY - { if (TREE_CODE ($2) == COMPONENT_REF - && TREE_PACKED (TREE_OPERAND ($2, 1))) - error ("`__alignof' applied to a bit-field"); - if (TREE_CODE ($2) == INDIRECT_REF) - { - tree t = TREE_OPERAND ($2, 0); - tree best = t; - int bestalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t))); - while (TREE_CODE (t) == NOP_EXPR - && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == POINTER_TYPE) - { - int thisalign; - t = TREE_OPERAND (t, 0); - thisalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t))); - if (thisalign > bestalign) - best = t, bestalign = thisalign; - } - $$ = c_alignof (TREE_TYPE (TREE_TYPE (best))); - } - else - { - /* ANSI says arrays and fns are converted inside comma. - But we can't convert them in build_compound_expr - because that would break commas in lvalues. - So do the conversion here if operand was a comma. */ - if (TREE_CODE ($2) == COMPOUND_EXPR - && (TREE_CODE (TREE_TYPE ($2)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE ($2)) == FUNCTION_TYPE)) - $2 = default_conversion ($2); - $$ = c_alignof (TREE_TYPE ($2)); - } - } - | ALIGNOF '(' typename ')' %prec HYPERUNARY - { $$ = c_alignof (groktypename ($3)); } - ; - -cast_expr: - unary_expr - | '(' typename ')' cast_expr %prec UNARY - { tree type = groktypename ($2); - $$ = build_c_cast (type, $4); } - | '(' typename ')' '{' initlist maybecomma '}' %prec UNARY - { tree type = groktypename ($2); - if (pedantic) - warning ("ANSI C forbids constructor expressions"); - $$ = digest_init (type, build_nt (CONSTRUCTOR, NULL_TREE, nreverse ($5)), 0); - if (TREE_CODE (type) == ARRAY_TYPE && TYPE_SIZE (type) == 0) - { - int failure = complete_array_type (type, $$, 1); - if (failure) - abort (); - } - } - ; - -expr_no_commas: - cast_expr - | expr_no_commas '+' expr_no_commas - { $$ = build_binary_op ($2, $1, $3); } - | expr_no_commas '-' expr_no_commas - { $$ = build_binary_op ($2, $1, $3); } - | expr_no_commas '*' expr_no_commas - { $$ = build_binary_op ($2, $1, $3); } - | expr_no_commas '/' expr_no_commas - { $$ = build_binary_op ($2, $1, $3); } - | expr_no_commas '%' expr_no_commas - { $$ = build_binary_op ($2, $1, $3); } - | expr_no_commas LSHIFT expr_no_commas - { $$ = build_binary_op ($2, $1, $3); } - | expr_no_commas RSHIFT expr_no_commas - { $$ = build_binary_op ($2, $1, $3); } - | expr_no_commas ARITHCOMPARE expr_no_commas - { $$ = build_binary_op ($2, $1, $3); } - | expr_no_commas EQCOMPARE expr_no_commas - { $$ = build_binary_op ($2, $1, $3); } - | expr_no_commas '&' expr_no_commas - { $$ = build_binary_op ($2, $1, $3); } - | expr_no_commas '|' expr_no_commas - { $$ = build_binary_op ($2, $1, $3); } - | expr_no_commas '^' expr_no_commas - { $$ = build_binary_op ($2, $1, $3); } - | expr_no_commas ANDAND expr_no_commas - { $$ = build_binary_op (TRUTH_ANDIF_EXPR, $1, $3); } - | expr_no_commas OROR expr_no_commas - { $$ = build_binary_op (TRUTH_ORIF_EXPR, $1, $3); } - | expr_no_commas '?' xexpr ':' expr_no_commas - { $$ = build_conditional_expr ($1, $3, $5); } - | expr_no_commas '=' expr_no_commas - { $$ = build_modify_expr ($1, NOP_EXPR, $3); } - | expr_no_commas ASSIGN expr_no_commas - { $$ = build_modify_expr ($1, $2, $3); } - ; - -primary: - IDENTIFIER - { $$ = lastiddecl; - if (!$$ || $$ == error_mark_node) - { - if (yychar == -1) - yychar = yylex(); - if (yychar == '(') - { - $$ = implicitly_declare ($1); - assemble_external ($$); - TREE_USED ($$) = 1; - } - else if (current_function_decl == 0) - { - error ("`%s' undeclared, outside of functions", - IDENTIFIER_POINTER ($1)); - $$ = error_mark_node; - } - else - { - if (IDENTIFIER_GLOBAL_VALUE ($1) != error_mark_node - || IDENTIFIER_ERROR_LOCUS ($1) != current_function_decl) - { - error ("`%s' undeclared (first use this function)", - IDENTIFIER_POINTER ($1)); - - if (! undeclared_variable_notice) - { - error ("(Each undeclared identifier is reported only once"); - error ("for each function it appears in.)"); - undeclared_variable_notice = 1; - } - } - $$ = error_mark_node; - /* Prevent repeated error messages. */ - IDENTIFIER_GLOBAL_VALUE ($1) = error_mark_node; - IDENTIFIER_ERROR_LOCUS ($1) = current_function_decl; - } - } - else if (! TREE_USED ($$)) - { - if (TREE_EXTERNAL ($$)) - assemble_external ($$); - TREE_USED ($$) = 1; - } - if (TREE_CODE ($$) == CONST_DECL) - $$ = DECL_INITIAL ($$); - } - | CONSTANT - | string - { $$ = combine_strings ($1); } - | '(' expr ')' - { $$ = $2; } - | '(' error ')' - { $$ = error_mark_node; } - | '(' - { if (current_function_decl == 0) - { - error ("braced-group within expression allowed only inside a function"); - YYERROR; - } - keep_next_level (); - $$ = expand_start_stmt_expr (); } - compstmt ')' - { tree rtl_exp; - if (pedantic) - warning ("ANSI C forbids braced-groups within expressions"); - rtl_exp = expand_end_stmt_expr ($2); - $$ = $3; - TREE_USED ($$) = 0; - /* Since the statements have side effects, - consider this volatile. */ - TREE_VOLATILE ($$) = 1; - TREE_TYPE ($$) = TREE_TYPE (rtl_exp); - STMT_BODY ($$) = rtl_exp; } - | primary '(' exprlist ')' %prec '.' - { $$ = build_function_call ($1, $3); } - | primary '[' expr ']' %prec '.' - { $$ = build_array_ref ($1, $3); } - | primary '.' identifier - { $$ = build_component_ref ($1, $3); } - | primary POINTSAT identifier - { $$ = build_component_ref (build_indirect_ref ($1, "->"), $3); } - | primary PLUSPLUS - { $$ = build_unary_op (POSTINCREMENT_EXPR, $1, 0); } - | primary MINUSMINUS - { $$ = build_unary_op (POSTDECREMENT_EXPR, $1, 0); } - ; - -/* Produces a STRING_CST with perhaps more STRING_CSTs chained onto it. */ -string: - STRING - | string STRING - { $$ = chainon ($1, $2); } - ; - -xdecls: - /* empty */ - | decls - ; - -decls: - decl - | errstmt - | decls decl - | decl errstmt - ; - -/* records the type and storage class specs to use for processing - the declarators that follow. - Maintains a stack of outer-level values of current_declspecs, - for the sake of parm declarations nested in function declarators. */ -setspecs: /* empty */ - { $$ = suspend_momentary (); - declspec_stack = tree_cons (0, current_declspecs, - declspec_stack); - current_declspecs = $0; } - ; - -decl: - typed_declspecs setspecs initdecls ';' - { current_declspecs = TREE_VALUE (declspec_stack); - declspec_stack = TREE_CHAIN (declspec_stack); - resume_momentary ($2); } - | declmods setspecs notype_initdecls ';' - { current_declspecs = TREE_VALUE (declspec_stack); - declspec_stack = TREE_CHAIN (declspec_stack); - resume_momentary ($2); } - | typed_declspecs ';' - { shadow_tag ($1); } - | declmods ';' - { warning ("empty declaration"); } - ; - -/* Declspecs which contain at least one type specifier or typedef name. - (Just `const' or `volatile' is not enough.) - A typedef'd name following these is taken as a name to be declared. */ - -typed_declspecs: - typespec reserved_declspecs - { $$ = tree_cons (NULL_TREE, $1, $2); } - | declmods typespec reserved_declspecs - { $$ = chainon ($3, tree_cons (NULL_TREE, $2, $1)); } - ; - -reserved_declspecs: /* empty */ - { $$ = NULL_TREE; } - | reserved_declspecs typespecqual_reserved - { $$ = tree_cons (NULL_TREE, $2, $1); } - | reserved_declspecs SCSPEC - { $$ = tree_cons (NULL_TREE, $2, $1); } - ; - -/* List of just storage classes and type modifiers. - A declaration can start with just this, but then it cannot be used - to redeclare a typedef-name. */ - -declmods: - TYPE_QUAL - { $$ = tree_cons (NULL_TREE, $1, NULL_TREE); } - | SCSPEC - { $$ = tree_cons (NULL_TREE, $1, NULL_TREE); } - | declmods TYPE_QUAL - { $$ = tree_cons (NULL_TREE, $2, $1); } - | declmods SCSPEC - { $$ = tree_cons (NULL_TREE, $2, $1); } - ; - - -/* Used instead of declspecs where storage classes are not allowed - (that is, for typenames and structure components). - Don't accept a typedef-name if anything but a modifier precedes it. */ - -typed_typespecs: - typespec reserved_typespecquals - { $$ = tree_cons (NULL_TREE, $1, $2); } - | nonempty_type_quals typespec reserved_typespecquals - { $$ = chainon ($3, tree_cons (NULL_TREE, $2, $1)); } - ; - -reserved_typespecquals: /* empty */ - { $$ = NULL_TREE; } - | reserved_typespecquals typespecqual_reserved - { $$ = tree_cons (NULL_TREE, $2, $1); } - ; - -/* A typespec (but not a type qualifier). - Once we have seen one of these in a declaration, - if a typedef name appears then it is being redeclared. */ - -typespec: TYPESPEC - | structsp - | TYPENAME - | TYPEOF '(' expr ')' - { $$ = TREE_TYPE ($3); - if (pedantic) - warning ("ANSI C forbids `typeof'"); } - | TYPEOF '(' typename ')' - { $$ = groktypename ($3); - if (pedantic) - warning ("ANSI C forbids `typeof'"); } - ; - -/* A typespec that is a reserved word, or a type qualifier. */ - -typespecqual_reserved: TYPESPEC - | TYPE_QUAL - | structsp - ; - -initdecls: - initdcl - | initdecls ',' initdcl - ; - -notype_initdecls: - notype_initdcl - | notype_initdecls ',' initdcl - ; - -maybeasm: - /* empty */ - { $$ = NULL_TREE; } - | ASM '(' string ')' - { if (TREE_CHAIN ($3)) $3 = combine_strings ($3); - $$ = $3; - if (pedantic) - warning ("ANSI C forbids use of `asm' keyword"); - } - ; - -initdcl: - declarator maybeasm maybe_attribute '=' - { $$ = start_decl ($1, current_declspecs, 1); } - init -/* Note how the declaration of the variable is in effect while its init is parsed! */ - { finish_decl ($5, $6, $2); } - | declarator maybeasm maybe_attribute - { tree d = start_decl ($1, current_declspecs, 0); - finish_decl (d, NULL_TREE, $2); } - ; - -notype_initdcl: - notype_declarator maybeasm maybe_attribute '=' - { $$ = start_decl ($1, current_declspecs, 1); } - init -/* Note how the declaration of the variable is in effect while its init is parsed! */ - { finish_decl ($5, $6, $2); } - | notype_declarator maybeasm maybe_attribute - { tree d = start_decl ($1, current_declspecs, 0); - finish_decl (d, NULL_TREE, $2); } - ; -/* the * rules are dummies to accept the Apollo extended syntax - so that the header files compile. */ -maybe_attribute: - /* empty */ - { $$ = NULL_TREE; } - | ATTRIBUTE '(' '(' attribute_list ')' ')' - { $$ = $4; } - ; - -attribute_list - : attrib - | attribute_list ',' attrib - ; - -attrib - : IDENTIFIER - { warning ("`%s' attribute directive ignored", - IDENTIFIER_POINTER ($1)); - $$ = $1; } - | IDENTIFIER '(' CONSTANT ')' - { /* if not "aligned(1)", then issue warning */ - if (strcmp (IDENTIFIER_POINTER ($1), "aligned") != 0 - || TREE_CODE ($3) != INTEGER_CST - || TREE_INT_CST_LOW ($3) != 1) - warning ("`%s' attribute directive ignored", - IDENTIFIER_POINTER ($1)); - $$ = $1; } - | IDENTIFIER '(' identifiers ')' - { warning ("`%s' attribute directive ignored", - IDENTIFIER_POINTER ($1)); - $$ = $1; } - ; - -init: - expr_no_commas - | '{' '}' - { $$ = build_nt (CONSTRUCTOR, NULL_TREE, NULL_TREE); - if (pedantic) - warning ("ANSI C forbids empty initializer braces"); } - | '{' initlist '}' - { $$ = build_nt (CONSTRUCTOR, NULL_TREE, nreverse ($2)); } - | '{' initlist ',' '}' - { $$ = build_nt (CONSTRUCTOR, NULL_TREE, nreverse ($2)); } - | error - { $$ = NULL_TREE; } - ; - -/* This chain is built in reverse order, - and put in forward order where initlist is used. */ -initlist: - init - { $$ = build_tree_list (NULL_TREE, $1); } - | initlist ',' init - { $$ = tree_cons (NULL_TREE, $3, $1); } - ; - -/* Any kind of declarator (thus, all declarators allowed - after an explicit typespec). */ - -declarator: - after_type_declarator - | notype_declarator - ; - -/* A declarator that is allowed only after an explicit typespec. */ - -after_type_declarator: - '(' after_type_declarator ')' - { $$ = $2; } - | after_type_declarator '(' parmlist_or_identifiers %prec '.' - { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } -/* | after_type_declarator '(' error ')' %prec '.' - { $$ = build_nt (CALL_EXPR, $1, NULL_TREE, NULL_TREE); - poplevel (0, 0, 0); } */ - | after_type_declarator '[' expr ']' %prec '.' - { $$ = build_nt (ARRAY_REF, $1, $3); } - | after_type_declarator '[' ']' %prec '.' - { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } - | '*' type_quals after_type_declarator %prec UNARY - { $$ = make_pointer_declarator ($2, $3); } - | TYPENAME - ; - -/* Kinds of declarator that can appear in a parameter list - in addition to notype_declarator. This is like after_type_declarator - but does not allow a typedef name in parentheses as an identifier - (because it would conflict with a function with that typedef as arg). */ - -parm_declarator: - parm_declarator '(' parmlist_or_identifiers %prec '.' - { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } -/* | parm_declarator '(' error ')' %prec '.' - { $$ = build_nt (CALL_EXPR, $1, NULL_TREE, NULL_TREE); - poplevel (0, 0, 0); } */ - | parm_declarator '[' expr ']' %prec '.' - { $$ = build_nt (ARRAY_REF, $1, $3); } - | parm_declarator '[' ']' %prec '.' - { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } - | '*' type_quals parm_declarator %prec UNARY - { $$ = make_pointer_declarator ($2, $3); } - | TYPENAME - ; - -/* A declarator allowed whether or not there has been - an explicit typespec. These cannot redeclare a typedef-name. */ - -notype_declarator: - notype_declarator '(' parmlist_or_identifiers %prec '.' - { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } -/* | notype_declarator '(' error ')' %prec '.' - { $$ = build_nt (CALL_EXPR, $1, NULL_TREE, NULL_TREE); - poplevel (0, 0, 0); } */ - | '(' notype_declarator ')' - { $$ = $2; } - | '*' type_quals notype_declarator %prec UNARY - { $$ = make_pointer_declarator ($2, $3); } - | notype_declarator '[' expr ']' %prec '.' - { $$ = build_nt (ARRAY_REF, $1, $3); } - | notype_declarator '[' ']' %prec '.' - { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } - | IDENTIFIER - ; - -structsp: - STRUCT identifier '{' - { $$ = start_struct (RECORD_TYPE, $2); - /* Start scope of tag before parsing components. */ - } - component_decl_list '}' - { $$ = finish_struct ($4, $5); - /* Really define the structure. */ - } - | STRUCT '{' component_decl_list '}' - { $$ = finish_struct (start_struct (RECORD_TYPE, NULL_TREE), - $3); } - | STRUCT identifier - { $$ = xref_tag (RECORD_TYPE, $2); } - | UNION identifier '{' - { $$ = start_struct (UNION_TYPE, $2); } - component_decl_list '}' - { $$ = finish_struct ($4, $5); } - | UNION '{' component_decl_list '}' - { $$ = finish_struct (start_struct (UNION_TYPE, NULL_TREE), - $3); } - | UNION identifier - { $$ = xref_tag (UNION_TYPE, $2); } - | ENUM identifier '{' - { $3 = suspend_momentary (); - $$ = start_enum ($2); } - enumlist maybecomma_warn '}' - { $$ = finish_enum ($4, nreverse ($5)); - resume_momentary ($3); } - | ENUM '{' - { $2 = suspend_momentary (); - $$ = start_enum (NULL_TREE); } - enumlist maybecomma_warn '}' - { $$ = finish_enum ($3, nreverse ($4)); - resume_momentary ($2); } - | ENUM identifier - { $$ = xref_tag (ENUMERAL_TYPE, $2); } - ; - -maybecomma: - /* empty */ - | ',' - ; - -maybecomma_warn: - /* empty */ - | ',' - { if (pedantic) warning ("comma at end of enumerator list"); } - ; - -component_decl_list: - component_decl_list2 - { $$ = $1; } - | component_decl_list2 component_decl - { $$ = chainon ($1, $2); - warning ("no semicolon at end of struct or union"); } - ; - -component_decl_list2: /* empty */ - { $$ = NULL_TREE; } - | component_decl_list2 component_decl ';' - { $$ = chainon ($1, $2); } - | component_decl_list2 ';' - { if (pedantic) - warning ("extra semicolon in struct or union specified"); } - ; - -/* There is a shift-reduce conflict here, because `components' may - start with a `typename'. It happens that shifting (the default resolution) - does the right thing, because it treats the `typename' as part of - a `typed_typespecs'. - - It is possible that this same technique would allow the distinction - between `notype_initdecls' and `initdecls' to be eliminated. - But I am being cautious and not trying it. */ - -component_decl: - typed_typespecs setspecs components - { $$ = $3; - current_declspecs = TREE_VALUE (declspec_stack); - declspec_stack = TREE_CHAIN (declspec_stack); - resume_momentary ($2); } - | nonempty_type_quals setspecs components - { $$ = $3; - current_declspecs = TREE_VALUE (declspec_stack); - declspec_stack = TREE_CHAIN (declspec_stack); - resume_momentary ($2); } - | error - { $$ = NULL_TREE; } - ; - -components: - /* empty */ - { if (pedantic) - warning ("ANSI C forbids member declarations with no members"); - $$ = NULL_TREE; } - | component_declarator - | components ',' component_declarator - { $$ = chainon ($1, $3); } - ; - -component_declarator: - declarator maybe_attribute - { $$ = grokfield (input_filename, lineno, $1, current_declspecs, NULL_TREE); } - | declarator ':' expr_no_commas maybe_attribute - { $$ = grokfield (input_filename, lineno, $1, current_declspecs, $3); } - | ':' expr_no_commas - { $$ = grokfield (input_filename, lineno, NULL_TREE, current_declspecs, $2); } - ; - -/* We chain the enumerators in reverse order. - They are put in forward order where enumlist is used. - (The order used to be significant, but no longer is so. - However, we still maintain the order, just to be clean.) */ - -enumlist: - enumerator - | enumlist ',' enumerator - { $$ = chainon ($3, $1); } - ; - - -enumerator: - identifier - { $$ = build_enumerator ($1, NULL_TREE); } - | identifier '=' expr_no_commas - { $$ = build_enumerator ($1, $3); } - ; - -typename: - typed_typespecs absdcl - { $$ = build_tree_list ($1, $2); } - | nonempty_type_quals absdcl - { $$ = build_tree_list ($1, $2); } - ; - -absdcl: /* an absolute declarator */ - /* empty */ - { $$ = NULL_TREE; } - | absdcl1 - ; - -nonempty_type_quals: - TYPE_QUAL - { $$ = tree_cons (NULL_TREE, $1, NULL_TREE); } - | nonempty_type_quals TYPE_QUAL - { $$ = tree_cons (NULL_TREE, $2, $1); } - ; - -type_quals: - /* empty */ - { $$ = NULL_TREE; } - | type_quals TYPE_QUAL - { $$ = tree_cons (NULL_TREE, $2, $1); } - ; - -absdcl1: /* a nonempty absolute declarator */ - '(' absdcl1 ')' - { $$ = $2; } - /* `(typedef)1' is `int'. */ - | '*' type_quals absdcl1 %prec UNARY - { $$ = make_pointer_declarator ($2, $3); } - | '*' type_quals %prec UNARY - { $$ = make_pointer_declarator ($2, NULL_TREE); } - | absdcl1 '(' parmlist %prec '.' - { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } - | absdcl1 '[' expr ']' %prec '.' - { $$ = build_nt (ARRAY_REF, $1, $3); } - | absdcl1 '[' ']' %prec '.' - { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } - | '(' parmlist %prec '.' - { $$ = build_nt (CALL_EXPR, NULL_TREE, $2, NULL_TREE); } - | '[' expr ']' %prec '.' - { $$ = build_nt (ARRAY_REF, NULL_TREE, $2); } - | '[' ']' %prec '.' - { $$ = build_nt (ARRAY_REF, NULL_TREE, NULL_TREE); } - ; - -/* at least one statement, the first of which parses without error. */ -/* stmts is used only after decls, so an invalid first statement - is actually regarded as an invalid decl and part of the decls. */ - -stmts: - stmt - | stmts stmt - | stmts errstmt - ; - -xstmts: - /* empty */ - | stmts - ; - -errstmt: error ';' - ; - -pushlevel: /* empty */ - { pushlevel (0); - clear_last_expr (); - push_momentary (); - expand_start_bindings (0); } - ; - -/* This is the body of a function definition. - It causes syntax errors to ignore to the next openbrace. */ -compstmt_or_error: - compstmt - {} - | error compstmt - ; - -compstmt: '{' '}' - { $$ = 0; } - | '{' pushlevel decls xstmts '}' - { expand_end_bindings (getdecls (), 1, 0); - $$ = poplevel (1, 1, 0); - pop_momentary (); } - | '{' pushlevel error '}' - { expand_end_bindings (getdecls (), kept_level_p (), 0); - $$ = poplevel (kept_level_p (), 0, 0); - pop_momentary (); } - | '{' pushlevel stmts '}' - { expand_end_bindings (getdecls (), kept_level_p (), 0); - $$ = poplevel (kept_level_p (), 0, 0); - pop_momentary (); } - ; - -simple_if: - IF '(' expr ')' - { emit_line_note (input_filename, lineno); - expand_start_cond (truthvalue_conversion ($3), 0); } - stmt - ; - -stmt: - compstmt {} - | expr ';' - { emit_line_note (input_filename, lineno); - /* Do default conversion if safe and possibly important, - in case within ({...}). */ - if ((TREE_CODE (TREE_TYPE ($1)) == ARRAY_TYPE - && lvalue_p ($1)) - || TREE_CODE (TREE_TYPE ($1)) == FUNCTION_TYPE) - $1 = default_conversion ($1); - expand_expr_stmt ($1); - clear_momentary (); } - | simple_if ELSE - { expand_start_else (); } - stmt - { expand_end_else (); } - | simple_if %prec IF - { expand_end_cond (); } - | WHILE - { emit_nop (); - emit_line_note (input_filename, lineno); - expand_start_loop (1); } - '(' expr ')' - { emit_line_note (input_filename, lineno); - expand_exit_loop_if_false (truthvalue_conversion ($4)); } - stmt - { expand_end_loop (); } - | DO - { emit_nop (); - emit_line_note (input_filename, lineno); - expand_start_loop_continue_elsewhere (1); } - stmt WHILE - { expand_loop_continue_here (); } - '(' expr ')' ';' - { emit_line_note (input_filename, lineno); - expand_exit_loop_if_false (truthvalue_conversion ($7)); - expand_end_loop (); - clear_momentary (); } - | FOR - '(' xexpr ';' - { emit_nop (); - emit_line_note (input_filename, lineno); - if ($3) expand_expr_stmt ($3); - expand_start_loop_continue_elsewhere (1); } - xexpr ';' - { emit_line_note (input_filename, lineno); - if ($6) - expand_exit_loop_if_false (truthvalue_conversion ($6)); } - xexpr ')' - /* Don't let the tree nodes for $9 be discarded - by clear_momentary during the parsing of the next stmt. */ - { push_momentary (); - $10 = lineno; } - stmt - { emit_line_note (input_filename, $10); - expand_loop_continue_here (); - if ($9) - expand_expr_stmt ($9); - pop_momentary (); - expand_end_loop (); } - | SWITCH '(' expr ')' - { emit_line_note (input_filename, lineno); - c_expand_start_case ($3); - /* Don't let the tree nodes for $3 be discarded by - clear_momentary during the parsing of the next stmt. */ - push_momentary (); } - stmt - { expand_end_case ($3); - pop_momentary (); } - | CASE expr ':' - { register tree value = fold ($2); - register tree label - = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); - - /* build_c_cast puts on a NOP_EXPR to make a non-lvalue. - Strip such NOP_EXPRs. */ - if (TREE_CODE (value) == NOP_EXPR - && TREE_TYPE (value) == TREE_TYPE (TREE_OPERAND (value, 0))) - value = TREE_OPERAND (value, 0); - - if (TREE_CODE (value) != INTEGER_CST - && value != error_mark_node) - { - error ("case label does not reduce to an integer constant"); - value = error_mark_node; - } - else - /* Promote char or short to int. */ - value = default_conversion (value); - if (value != error_mark_node) - { - int success = pushcase (value, label); - if (success == 1) - error ("case label not within a switch statement"); - else if (success == 2) - error ("duplicate case value"); - else if (success == 3) - warning ("case value out of range"); - } - } - stmt - | DEFAULT ':' - { - register tree label - = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); - int success = pushcase (NULL_TREE, label); - if (success == 1) - error ("default label not within a switch statement"); - else if (success == 2) - error ("multiple default labels in one switch"); - } - stmt - | BREAK ';' - { emit_line_note (input_filename, lineno); - if ( ! expand_exit_something ()) - error ("break statement not within loop or switch"); } - | CONTINUE ';' - { emit_line_note (input_filename, lineno); - if (! expand_continue_loop ()) - error ("continue statement not within a loop"); } - | RETURN ';' - { emit_line_note (input_filename, lineno); - c_expand_return (NULL_TREE); } - | RETURN expr ';' - { emit_line_note (input_filename, lineno); - c_expand_return ($2); } - | ASM maybe_type_qual '(' string ')' ';' - { if (TREE_CHAIN ($4)) $4 = combine_strings ($4); - emit_line_note (input_filename, lineno); - expand_asm ($4); } - /* This is the case with just output operands. */ - | ASM maybe_type_qual '(' string ':' asm_operands ')' ';' - { if (TREE_CHAIN ($4)) $4 = combine_strings ($4); - emit_line_note (input_filename, lineno); - c_expand_asm_operands ($4, $6, NULL_TREE, NULL_TREE, - $2 == ridpointers[(int)RID_VOLATILE], - input_filename, lineno); } - /* This is the case with input operands as well. */ - | ASM maybe_type_qual '(' string ':' asm_operands ':' asm_operands ')' ';' - { if (TREE_CHAIN ($4)) $4 = combine_strings ($4); - emit_line_note (input_filename, lineno); - c_expand_asm_operands ($4, $6, $8, NULL_TREE, - $2 == ridpointers[(int)RID_VOLATILE], - input_filename, lineno); } - /* This is the case with clobbered registers as well. */ - | ASM maybe_type_qual '(' string ':' asm_operands ':' - asm_operands ':' asm_clobbers ')' ';' - { if (TREE_CHAIN ($4)) $4 = combine_strings ($4); - emit_line_note (input_filename, lineno); - c_expand_asm_operands ($4, $6, $8, $10, - $2 == ridpointers[(int)RID_VOLATILE], - input_filename, lineno); } - | GOTO identifier ';' - { tree decl; - emit_line_note (input_filename, lineno); - decl = lookup_label ($2); - TREE_USED (decl) = 1; - expand_goto (decl); } - | identifier ':' - { tree label = define_label (input_filename, lineno, $1); - emit_nop (); - if (label) - expand_label (label); } - stmt - | ';' - ; - -/* Either a type-qualifier or nothing. First thing in an `asm' statement. */ - -maybe_type_qual: - /* empty */ - { if (pedantic) - warning ("ANSI C forbids use of `asm' keyword"); - emit_line_note (input_filename, lineno); } - | TYPE_QUAL - { if (pedantic) - warning ("ANSI C forbids use of `asm' keyword"); - emit_line_note (input_filename, lineno); } - ; - -xexpr: - /* empty */ - { $$ = NULL_TREE; } - | expr - ; - -/* These are the operands other than the first string and colon - in asm ("addextend %2,%1": "=dm" (x), "0" (y), "g" (*x)) */ -asm_operands: /* empty */ - { $$ = NULL_TREE; } - | nonnull_asm_operands - ; - -nonnull_asm_operands: - asm_operand - | nonnull_asm_operands ',' asm_operand - { $$ = chainon ($1, $3); } - ; - -asm_operand: - STRING '(' expr ')' - { $$ = build_tree_list ($1, $3); } - ; - -asm_clobbers: - string - { $$ = tree_cons (NULL_TREE, combine_strings ($1), NULL_TREE); } - | asm_clobbers ',' string - { $$ = tree_cons (NULL_TREE, combine_strings ($3), $1); } - ; - -/* This is what appears inside the parens in a function declarator. - Its value is a list of ..._TYPE nodes. */ -parmlist: - { pushlevel (0); - declare_parm_level (); } - parmlist_1 - { $$ = $2; - parmlist_tags_warning (); - poplevel (0, 0, 0); } - ; - -/* This is referred to where either a parmlist or an identifier list is ok. - Its value is a list of ..._TYPE nodes or a list of identifiers. */ -parmlist_or_identifiers: - { pushlevel (0); - declare_parm_level (); } - parmlist_or_identifiers_1 - { $$ = $2; - parmlist_tags_warning (); - poplevel (0, 0, 0); } - ; - -parmlist_or_identifiers_1: - parmlist_2 ')' - | identifiers ')' - { $$ = tree_cons (NULL_TREE, NULL_TREE, $1); } - | error ')' - { $$ = tree_cons (NULL_TREE, NULL_TREE, NULL_TREE); } - ; - -parmlist_1: - parmlist_2 ')' - | error ')' - { $$ = tree_cons (NULL_TREE, NULL_TREE, NULL_TREE); } - ; - -/* This is what appears inside the parens in a function declarator. - Is value is represented in the format that grokdeclarator expects. */ -parmlist_2: /* empty */ - { $$ = get_parm_info (0); } - | parms - { $$ = get_parm_info (1); } - | parms ',' ELLIPSIS - { $$ = get_parm_info (0); } - ; - -parms: - parm - { push_parm_decl ($1); } - | parms ',' parm - { push_parm_decl ($3); } - ; - -/* A single parameter declaration or parameter type name, - as found in a parmlist. */ -parm: - typed_declspecs parm_declarator - { $$ = build_tree_list ($1, $2) ; } - | typed_declspecs notype_declarator - { $$ = build_tree_list ($1, $2) ; } - | typed_declspecs absdcl - { $$ = build_tree_list ($1, $2); } - | declmods notype_declarator - { $$ = build_tree_list ($1, $2) ; } - | declmods absdcl - { $$ = build_tree_list ($1, $2); } - ; - -/* A nonempty list of identifiers. */ -identifiers: - IDENTIFIER - { $$ = build_tree_list (NULL_TREE, $1); } - | identifiers ',' IDENTIFIER - { $$ = chainon ($1, build_tree_list (NULL_TREE, $3)); } - ; -%% - -/* Return something to represent absolute declarators containing a *. - TARGET is the absolute declarator that the * contains. - TYPE_QUALS is a list of modifiers such as const or volatile - to apply to the pointer type, represented as identifiers. - - We return an INDIRECT_REF whose "contents" are TARGET - and whose type is the modifier list. */ - -static tree -make_pointer_declarator (type_quals, target) - tree type_quals, target; -{ - return build (INDIRECT_REF, type_quals, target); -} - -/* Given a chain of STRING_CST nodes, - concatenate them into one STRING_CST - and give it a suitable array-of-chars data type. */ - -static tree -combine_strings (strings) - tree strings; -{ - register tree value, t; - register int length = 1; - int wide_length = 0; - int wide_flag = 0; - int nchars; - - if (TREE_CHAIN (strings)) - { - /* More than one in the chain, so concatenate. */ - register char *p, *q; - - /* Don't include the \0 at the end of each substring, - except for the last one. - Count wide strings and ordinary strings separately. */ - for (t = strings; t; t = TREE_CHAIN (t)) - { - if (TREE_TYPE (t) == int_array_type_node) - { - wide_length += (TREE_STRING_LENGTH (t) - UNITS_PER_WORD); - wide_flag = 1; - } - else - length += (TREE_STRING_LENGTH (t) - 1); - } - - /* If anything is wide, the non-wides will be converted, - which makes them take more space. */ - if (wide_flag) - length = length * UNITS_PER_WORD + wide_length; - - p = (char *) savealloc (length); - - /* Copy the individual strings into the new combined string. - If the combined string is wide, convert the chars to ints - for any individual strings that are not wide. */ - - q = p; - for (t = strings; t; t = TREE_CHAIN (t)) - { - int len = TREE_STRING_LENGTH (t) - 1; - if ((TREE_TYPE (t) == int_array_type_node) == wide_flag) - { - bcopy (TREE_STRING_POINTER (t), q, len); - q += len; - } - else - { - int i; - for (i = 0; i < len; i++) - ((int *) q)[i] = TREE_STRING_POINTER (t)[i]; - q += len * UNITS_PER_WORD; - } - } - *q = 0; - - value = make_node (STRING_CST); - TREE_STRING_POINTER (value) = p; - TREE_STRING_LENGTH (value) = length; - TREE_LITERAL (value) = 1; - } - else - { - value = strings; - length = TREE_STRING_LENGTH (value); - if (TREE_TYPE (value) == int_array_type_node) - wide_flag = 1; - } - - /* Compute the number of elements, for the array type. */ - nchars = wide_flag ? length / UNITS_PER_WORD : length; - - /* Create the array type for the string constant. - -Wwrite-strings says make the string constant an array of const char - so that copying it to a non-const pointer will get a warning. */ - if (warn_write_strings) - { - tree elements - = build_type_variant (wide_flag ? integer_type_node : char_type_node, - 1, 0); - TREE_TYPE (value) - = build_array_type (elements, - build_index_type (build_int_2 (nchars - 1, 0))); - } - else - TREE_TYPE (value) - = build_array_type (wide_flag ? integer_type_node : char_type_node, - build_index_type (build_int_2 (nchars - 1, 0))); - TREE_LITERAL (value) = 1; - TREE_STATIC (value) = 1; - return value; -} - -FILE *finput; /* input file. - Normally a pipe from the preprocessor. */ - -/* lexical analyzer */ - -static int maxtoken; /* Current nominal length of token buffer. */ -static char *token_buffer; /* Pointer to token buffer. - Actual allocated length is maxtoken + 2. */ -static int max_wide; /* Current nominal length of wide_buffer. */ -static int *wide_buffer; /* Pointer to wide-string buffer. - Actual allocated length is max_wide + 1. */ - -/* Nonzero if end-of-file has been seen on input. */ -static int end_of_file; - -/* Data type that represents the GNU C reserved words. */ -struct resword { char *name; short token; enum rid rid; }; - -#define MIN_WORD_LENGTH 2 /* minimum size for C keyword */ -#define MAX_WORD_LENGTH 13 /* maximum size for C keyword */ -#define MIN_HASH_VALUE 7 /* range of the hash keys values */ -#define MAX_HASH_VALUE 91 /* for the perfect hash generator */ -#define NORID RID_UNUSED - -/* This function performs the minimum-perfect hash mapping from input - string to reswords table index. It only looks at the first and - last characters in the string, thus assuring the O(1) lookup time - (this keeps our constant down to an insignificant amount!). Compiling - the following 2 functions as inline removes all overhead of the - function calls. */ - -#ifdef __GNUC__ -__inline -#endif -static int -hash (str, len) - register char *str; - register int len; -{ -/* This table is used to build the hash table index that recognizes - reserved words in 0(1) steps. It is larger than strictly necessary, - but I'm trading off the space for the time-saving luxury of avoiding - subtraction of an offset. All those ``91's'' (actually just a - short-hand for MAX_HASH_VALUE #defined above) are used to speed up - the search when the string found on the input stream doesn't have a - first or last character that is part of the set of alphabetic - characters that comprise the first or last characters in C - reserved words. */ - - static int hash_table[] = - { - 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 91, 91, 91, 91, 91, 1, 91, 2, 1, 32, - 7, 5, 18, 20, 1, 17, 91, 1, 18, 1, - 28, 1, 23, 91, 12, 20, 1, 41, 7, 15, - 91, 91, 10, 91, 91, 91, 91, 91, - }; - register int hval = len ; - - switch (hval) - { - default: - case 3: - hval += hash_table[str[2]]; - case 2: - case 1: - return hval + hash_table[str[0]] + hash_table[str[len - 1]]; - } -} - -/* This routine attempts to match the string found in the reswords table - with the one from the input stream. If all the relevant details - match then an actual strcmp comparison is performed and the address of - correct struct resword entry is returned. Otherwise, a NULL - pointer is returned. */ - -#ifdef __GNUC__ -__inline -#endif -struct resword * -is_reserved_word (str, len) - register char *str; - register int len; -{ - /* This is the hash table of keywords. - The order of keywords has been chosen for perfect hashing. - Therefore, this table cannot be updated by hand. - Use the program ``gperf,'' available with the latest libg++ - distribution, to generate an updated table. A file called - c-parse.gperf, distributed with GNU C, contains the keyword file. */ - - static struct resword reswords[] = - { - { "", }, { "", }, { "", }, { "", }, { "", }, { "", }, { "", }, - {"asm", ASM, NORID }, - {"auto", SCSPEC, RID_AUTO }, - {"__asm", ASM, NORID }, - {"do", DO, NORID }, - {"__asm__", ASM, NORID }, - {"break", BREAK, NORID }, - {"__typeof__", TYPEOF, NORID }, - { "", }, - {"__alignof__", ALIGNOF, NORID }, - { "", }, - {"__attribute__", ATTRIBUTE, NORID }, - { "", }, - {"__attribute", ATTRIBUTE, NORID }, - { "", }, - {"__volatile__", TYPE_QUAL, RID_VOLATILE }, - {"int", TYPESPEC, RID_INT }, - {"__volatile", TYPE_QUAL, RID_VOLATILE }, - { "", }, - {"float", TYPESPEC, RID_FLOAT }, - {"goto", GOTO, NORID }, - {"short", TYPESPEC, RID_SHORT }, - {"__typeof", TYPEOF, NORID }, - {"__inline__", SCSPEC, RID_INLINE }, - {"__alignof", ALIGNOF, NORID }, - {"__inline", SCSPEC, RID_INLINE }, - {"__signed__", TYPESPEC, RID_SIGNED }, - {"default", DEFAULT, NORID }, - {"else", ELSE, NORID }, - {"void", TYPESPEC, RID_VOID }, - {"__signed", TYPESPEC, RID_SIGNED }, - {"if", IF, NORID }, - {"volatile", TYPE_QUAL, RID_VOLATILE }, - {"struct", STRUCT, NORID }, - {"extern", SCSPEC, RID_EXTERN }, - {"__const", TYPE_QUAL, RID_CONST }, - {"while", WHILE, NORID }, - {"__const__", TYPE_QUAL, RID_CONST }, - {"switch", SWITCH, NORID }, - {"for", FOR, NORID }, - {"inline", SCSPEC, RID_INLINE }, - {"return", RETURN, NORID }, - {"typeof", TYPEOF, NORID }, - {"typedef", SCSPEC, RID_TYPEDEF }, - {"char", TYPESPEC, RID_CHAR }, - {"enum", ENUM, NORID }, - {"register", SCSPEC, RID_REGISTER }, - {"signed", TYPESPEC, RID_SIGNED }, - {"sizeof", SIZEOF, NORID }, - { "", }, { "", }, { "", }, { "", }, - {"double", TYPESPEC, RID_DOUBLE }, - {"static", SCSPEC, RID_STATIC }, - {"case", CASE, NORID }, - { "", }, { "", }, { "", }, { "", }, - {"const", TYPE_QUAL, RID_CONST }, - { "", }, { "", }, { "", }, - {"long", TYPESPEC, RID_LONG }, - { "", }, { "", }, - {"continue", CONTINUE, NORID }, - { "", }, { "", }, - {"unsigned", TYPESPEC, RID_UNSIGNED }, - { "", }, { "", }, { "", }, { "", }, { "", }, { "", }, { "", }, { "", }, { "", }, - { "", }, { "", }, { "", }, { "", }, { "", }, - {"union", UNION, NORID }, - }; - - if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) - { - register int key = hash (str, len); - - if (key <= MAX_HASH_VALUE) - { - register char *s = reswords[key].name; - - if (*s == *str && !strcmp (str + 1, s + 1)) - return &reswords[key]; - } - } - return 0; -} - -/* The elements of `ridpointers' are identifier nodes - for the reserved type names and storage classes. - It is indexed by a RID_... value. */ - -tree ridpointers[(int) RID_MAX]; - -int check_newline (); - -void -init_lex () -{ - /* Start it at 0, because check_newline is called at the very beginning - and will increment it to 1. */ - lineno = 0; - - maxtoken = 40; - token_buffer = (char *) xmalloc (maxtoken + 2); - max_wide = 40; - wide_buffer = (int *) xmalloc (max_wide + 1); - - ridpointers[(int) RID_INT] = get_identifier ("int"); - ridpointers[(int) RID_CHAR] = get_identifier ("char"); - ridpointers[(int) RID_VOID] = get_identifier ("void"); - ridpointers[(int) RID_FLOAT] = get_identifier ("float"); - ridpointers[(int) RID_DOUBLE] = get_identifier ("double"); - ridpointers[(int) RID_SHORT] = get_identifier ("short"); - ridpointers[(int) RID_LONG] = get_identifier ("long"); - ridpointers[(int) RID_UNSIGNED] = get_identifier ("unsigned"); - ridpointers[(int) RID_SIGNED] = get_identifier ("signed"); - ridpointers[(int) RID_INLINE] = get_identifier ("inline"); - ridpointers[(int) RID_CONST] = get_identifier ("const"); - ridpointers[(int) RID_VOLATILE] = get_identifier ("volatile"); - ridpointers[(int) RID_AUTO] = get_identifier ("auto"); - ridpointers[(int) RID_STATIC] = get_identifier ("static"); - ridpointers[(int) RID_EXTERN] = get_identifier ("extern"); - ridpointers[(int) RID_TYPEDEF] = get_identifier ("typedef"); - ridpointers[(int) RID_REGISTER] = get_identifier ("register"); -} - -static void -reinit_parse_for_function () -{ -} - -/* If C is not whitespace, return C. - Otherwise skip whitespace and return first nonwhite char read. */ - -static int -skip_white_space (c) - register int c; -{ -#if 0 - register int inside; -#endif - - for (;;) - { - switch (c) - { - /* Don't recognize comments in cc1: all comments are removed by cpp, - and cpp output can include / and * consecutively as operators. */ -#if 0 - case '/': - c = getc (finput); - if (c != '*') - { - ungetc (c, finput); - return '/'; - } - - c = getc (finput); - - inside = 1; - while (inside) - { - if (c == '*') - { - while (c == '*') - c = getc (finput); - - if (c == '/') - { - inside = 0; - c = getc (finput); - } - } - else if (c == '\n') - { - lineno++; - c = getc (finput); - } - else if (c == EOF) - { - error ("unterminated comment"); - break; - } - else - c = getc (finput); - } - - break; -#endif - - case '\n': - c = check_newline (); - break; - - case ' ': - case '\t': - case '\f': - case '\r': - case '\v': - case '\b': - c = getc (finput); - break; - - case '\\': - c = getc (finput); - if (c == '\n') - lineno++; - else - error ("stray '\\' in program"); - c = getc (finput); - break; - - default: - return (c); - } - } -} - - - -/* Make the token buffer longer, preserving the data in it. - P should point to just beyond the last valid character in the old buffer. - The value we return is a pointer to the new buffer - at a place corresponding to P. */ - -static char * -extend_token_buffer (p) - char *p; -{ - int offset = p - token_buffer; - - maxtoken = maxtoken * 2 + 10; - token_buffer = (char *) xrealloc (token_buffer, maxtoken + 2); - - return token_buffer + offset; -} - -/* At the beginning of a line, increment the line number - and process any #-directive on this line. - If the line is a #-directive, read the entire line and return a newline. - Otherwise, return the line's first non-whitespace character. */ - -int -check_newline () -{ - register int c; - register int token; - - lineno++; - - /* Read first nonwhite char on the line. */ - - c = getc (finput); - while (c == ' ' || c == '\t') - c = getc (finput); - - if (c != '#') - { - /* If not #, return it so caller will use it. */ - return c; - } - - /* Read first nonwhite char after the `#'. */ - - c = getc (finput); - while (c == ' ' || c == '\t') - c = getc (finput); - - /* If a letter follows, then if the word here is `line', skip - it and ignore it; otherwise, ignore the line, with an error - if the word isn't `pragma'. */ - - if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) - { - if (c == 'p') - { - if (getc (finput) == 'r' - && getc (finput) == 'a' - && getc (finput) == 'g' - && getc (finput) == 'm' - && getc (finput) == 'a' - && ((c = getc (finput)) == ' ' || c == '\t' || c == '\n')) - goto skipline; - } - - else if (c == 'l') - { - if (getc (finput) == 'i' - && getc (finput) == 'n' - && getc (finput) == 'e' - && ((c = getc (finput)) == ' ' || c == '\t')) - goto linenum; - } - else if (c == 'i') - { - if (getc (finput) == 'd' - && getc (finput) == 'e' - && getc (finput) == 'n' - && getc (finput) == 't' - && ((c = getc (finput)) == ' ' || c == '\t')) - { - extern FILE *asm_out_file; - - if (pedantic) - error ("ANSI C does not allow #ident"); - - /* Here we have just seen `#ident '. - A string constant should follow. */ - - while (c == ' ' || c == '\t') - c = getc (finput); - - /* If no argument, ignore the line. */ - if (c == '\n') - return c; - - ungetc (c, finput); - token = yylex (); - if (token != STRING - || TREE_CODE (yylval.ttype) != STRING_CST) - { - error ("invalid #ident"); - goto skipline; - } - -#ifdef ASM_OUTPUT_IDENT - ASM_OUTPUT_IDENT (asm_out_file, TREE_STRING_POINTER (yylval.ttype)); -#endif - - /* Skip the rest of this line. */ - goto skipline; - } - } - - error ("undefined or invalid # directive"); - goto skipline; - } - -linenum: - /* Here we have either `#line' or `# '. - In either case, it should be a line number; a digit should follow. */ - - while (c == ' ' || c == '\t') - c = getc (finput); - - /* If the # is the only nonwhite char on the line, - just ignore it. Check the new newline. */ - if (c == '\n') - return c; - - /* Something follows the #; read a token. */ - - ungetc (c, finput); - token = yylex (); - - if (token == CONSTANT - && TREE_CODE (yylval.ttype) == INTEGER_CST) - { - int old_lineno = lineno; - /* subtract one, because it is the following line that - gets the specified number */ - - int l = TREE_INT_CST_LOW (yylval.ttype) - 1; - - /* Is this the last nonwhite stuff on the line? */ - c = getc (finput); - while (c == ' ' || c == '\t') - c = getc (finput); - if (c == '\n') - { - /* No more: store the line number and check following line. */ - lineno = l; - return c; - } - ungetc (c, finput); - - /* More follows: it must be a string constant (filename). */ - - token = yylex (); - if (token != STRING || TREE_CODE (yylval.ttype) != STRING_CST) - { - error ("invalid #line"); - goto skipline; - } - - input_filename - = (char *) permalloc (TREE_STRING_LENGTH (yylval.ttype) + 1); - strcpy (input_filename, TREE_STRING_POINTER (yylval.ttype)); - lineno = l; - - if (main_input_filename == 0) - main_input_filename = input_filename; - - /* Is this the last nonwhite stuff on the line? */ - c = getc (finput); - while (c == ' ' || c == '\t') - c = getc (finput); - if (c == '\n') - return c; - ungetc (c, finput); - - token = yylex (); - - /* `1' after file name means entering new file. - `2' after file name means just left a file. */ - - if (token == CONSTANT - && TREE_CODE (yylval.ttype) == INTEGER_CST) - { - if (TREE_INT_CST_LOW (yylval.ttype) == 1) - { - struct file_stack *p - = (struct file_stack *) xmalloc (sizeof (struct file_stack)); - input_file_stack->line = old_lineno; - p->next = input_file_stack; - p->name = input_filename; - input_file_stack = p; - input_file_stack_tick++; - } - else if (input_file_stack->next) - { - struct file_stack *p = input_file_stack; - input_file_stack = p->next; - free (p); - input_file_stack_tick++; - } - else - error ("#-lines for entering and leaving files don't match"); - } - } - else - error ("invalid #-line"); - - /* skip the rest of this line. */ - skipline: - if (c == '\n') - return c; - while ((c = getc (finput)) != EOF && c != '\n'); - return c; -} - -#define isalnum(char) ((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || (char >= '0' && char <= '9')) -#define isdigit(char) (char >= '0' && char <= '9') -#define ENDFILE -1 /* token that represents end-of-file */ - - -static int -readescape () -{ - register int c = getc (finput); - register int count, code; - int firstdig; - - switch (c) - { - case 'x': - code = 0; - count = 0; - while (1) - { - c = getc (finput); - if (!(c >= 'a' && c <= 'f') - && !(c >= 'A' && c <= 'F') - && !(c >= '0' && c <= '9')) - { - ungetc (c, finput); - break; - } - code *= 16; - if (c >= 'a' && c <= 'f') - code += c - 'a' + 10; - if (c >= 'A' && c <= 'F') - code += c - 'A' + 10; - if (c >= '0' && c <= '9') - code += c - '0'; - if (count == 0) - firstdig = code; - count++; - } - if (count == 0) - error ("\\x used with no following hex digits"); - else if ((count - 1) * 4 >= TYPE_PRECISION (integer_type_node) - || (count > 1 - && ((1 << (TYPE_PRECISION (integer_type_node) - (count - 1) * 4)) - <= firstdig))) - warning ("hex escape out of range"); - return code; - - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': - code = 0; - count = 0; - while ((c <= '7') && (c >= '0') && (count++ < 3)) - { - code = (code * 8) + (c - '0'); - c = getc (finput); - } - ungetc (c, finput); - return code; - - case '\\': case '\'': case '"': - return c; - - case '\n': - lineno++; - return -1; - - case 'n': - return TARGET_NEWLINE; - - case 't': - return TARGET_TAB; - - case 'r': - return TARGET_CR; - - case 'f': - return TARGET_FF; - - case 'b': - return TARGET_BS; - - case 'a': - return TARGET_BELL; - - case 'v': - return TARGET_VT; - - case 'E': - return 033; - - case '?': - /* `\(', etc, are used at beginning of line to avoid confusing Emacs. */ - case '(': - case '{': - case '[': - return c; - } - if (c >= 040 && c <= 0177) - warning ("unknown escape sequence `\\%c'", c); - else - warning ("unknown escape sequence: `\\' followed by char code 0x%x", c); - return c; -} - -void -yyerror (string) - char *string; -{ - char buf[200]; - - strcpy (buf, string); - - /* We can't print string and character constants well - because the token_buffer contains the result of processing escapes. */ - if (end_of_file) - strcat (buf, " at end of input"); - else if (token_buffer[0] == 0) - strcat (buf, " at null character"); - else if (token_buffer[0] == '"') - strcat (buf, " before string constant"); - else if (token_buffer[0] == '\'') - strcat (buf, " before character constant"); - else if (token_buffer[0] < 040 || token_buffer[0] >= 0177) - sprintf (buf + strlen (buf), " before character 0%o", token_buffer[0]); - else - strcat (buf, " before `%s'"); - - error (buf, token_buffer); -} - -static int nextchar = -1; - -static int -yylex () -{ - register int c; - register char *p; - register int value; - int wide_flag = 0; - - if (nextchar >= 0) - c = nextchar, nextchar = -1; - else - c = getc (finput); - - /* Effectively do c = skip_white_space (c) - but do it faster in the usual cases. */ - while (1) - switch (c) - { - case ' ': - case '\t': - case '\f': - case '\r': - case '\v': - case '\b': - c = getc (finput); - break; - - case '\n': - case '/': - case '\\': - c = skip_white_space (c); - default: - goto found_nonwhite; - } - found_nonwhite: - - token_buffer[0] = c; - token_buffer[1] = 0; - -/* yylloc.first_line = lineno; */ - - switch (c) - { - case EOF: - end_of_file = 1; - token_buffer[0] = 0; - value = ENDFILE; - break; - - case '$': - if (dollars_in_ident) - goto letter; - return '$'; - - case 'L': - /* Capital L may start a wide-string or wide-character constant. */ - { - register int c = getc (finput); - if (c == '\'') - { - wide_flag = 1; - goto char_constant; - } - if (c == '"') - { - wide_flag = 1; - goto string_constant; - } - ungetc (c, finput); - } - - case 'A': case 'B': case 'C': case 'D': case 'E': - case 'F': case 'G': case 'H': case 'I': case 'J': - case 'K': case 'M': case 'N': case 'O': - case 'P': case 'Q': case 'R': case 'S': case 'T': - case 'U': case 'V': case 'W': case 'X': case 'Y': - case 'Z': - case 'a': case 'b': case 'c': case 'd': case 'e': - case 'f': case 'g': case 'h': case 'i': case 'j': - case 'k': case 'l': case 'm': case 'n': case 'o': - case 'p': case 'q': case 'r': case 's': case 't': - case 'u': case 'v': case 'w': case 'x': case 'y': - case 'z': - case '_': - letter: - p = token_buffer; - while (isalnum (c) || c == '_' || c == '$') - { - if (p >= token_buffer + maxtoken) - p = extend_token_buffer (p); - if (c == '$' && ! dollars_in_ident) - break; - - *p++ = c; - c = getc (finput); - } - - *p = 0; - nextchar = c; - - value = IDENTIFIER; - yylval.itype = 0; - - /* Try to recognize a keyword. Uses minimum-perfect hash function */ - - { - register struct resword *ptr; - - if (ptr = is_reserved_word (token_buffer, p - token_buffer)) - { - if (ptr->rid) - yylval.ttype = ridpointers[(int) ptr->rid]; - if ((! flag_no_asm - /* -fno-asm means don't recognize the non-ANSI keywords. */ - || ((int) ptr->token != ASM - && (int) ptr->token != TYPEOF - && ptr->rid != RID_INLINE) - /* Recognize __asm and __inline despite -fno-asm. */ - || token_buffer[0] == '_') - /* -ftraditional means don't recognize nontraditional keywords - typeof, const, volatile, signed or inline. */ - && (! flag_traditional - || ((int) ptr->token != TYPE_QUAL - && (int) ptr->token != TYPEOF - && ptr->rid != RID_SIGNED - && ptr->rid != RID_INLINE) - /* Recognize __inline, etc. despite -ftraditional. */ - || token_buffer[0] == '_')) - value = (int) ptr->token; - } - } - - /* If we did not find a keyword, look for an identifier - (or a typename). */ - - if (value == IDENTIFIER) - { - yylval.ttype = get_identifier (token_buffer); - lastiddecl = lookup_name (yylval.ttype); - - if (lastiddecl != 0 && TREE_CODE (lastiddecl) == TYPE_DECL) - value = TYPENAME; - } - - break; - - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case '.': - { - int base = 10; - int count = 0; - int largest_digit = 0; - int numdigits = 0; - /* for multi-precision arithmetic, - we store only 8 live bits in each short, - giving us 64 bits of reliable precision */ - short shorts[8]; - int overflow = 0; - - enum anon1 { NOT_FLOAT, AFTER_POINT, TOO_MANY_POINTS} floatflag - = NOT_FLOAT; - - for (count = 0; count < 8; count++) - shorts[count] = 0; - - p = token_buffer; - *p++ = c; - - if (c == '0') - { - *p++ = (c = getc (finput)); - if ((c == 'x') || (c == 'X')) - { - base = 16; - *p++ = (c = getc (finput)); - } - else - { - base = 8; - numdigits++; - } - } - - /* Read all the digits-and-decimal-points. */ - - while (c == '.' - || (isalnum (c) && (c != 'l') && (c != 'L') - && (c != 'u') && (c != 'U') - && (floatflag == NOT_FLOAT || ((c != 'f') && (c != 'F'))))) - { - if (c == '.') - { - if (base == 16) - error ("floating constant may not be in radix 16"); - if (floatflag == AFTER_POINT) - { - error ("malformed floating constant"); - floatflag = TOO_MANY_POINTS; - } - else - floatflag = AFTER_POINT; - - base = 10; - *p++ = c = getc (finput); - /* Accept '.' as the start of a floating-point number - only when it is followed by a digit. - Otherwise, unread the following non-digit - and use the '.' as a structural token. */ - if (p == token_buffer + 2 && !isdigit (c)) - { - if (c == '.') - { - c = getc (finput); - if (c == '.') - { - *p++ = c; - *p = 0; - return ELLIPSIS; - } - error ("parse error at `..'"); - } - ungetc (c, finput); - token_buffer[1] = 0; - value = '.'; - goto done; - } - } - else - { - /* It is not a decimal point. - It should be a digit (perhaps a hex digit). */ - - if (isdigit (c)) - { - c = c - '0'; - } - else if (base <= 10) - { - if ((c&~040) == 'E') - { - base = 10; - floatflag = AFTER_POINT; - break; /* start of exponent */ - } - error ("nondigits in number and not hexadecimal"); - c = 0; - } - else if (c >= 'a') - { - c = c - 'a' + 10; - } - else - { - c = c - 'A' + 10; - } - if (c >= largest_digit) - largest_digit = c; - numdigits++; - - for (count = 0; count < 8; count++) - { - shorts[count] *= base; - if (count) - { - shorts[count] += (shorts[count-1] >> 8); - shorts[count-1] &= (1<<8)-1; - } - else shorts[0] += c; - } - - if (shorts[7] >= 1<<8 - || shorts[7] < - (1 << 8)) - overflow = TRUE; - - if (p >= token_buffer + maxtoken - 3) - p = extend_token_buffer (p); - *p++ = (c = getc (finput)); - } - } - - if (numdigits == 0) - error ("numeric constant with no digits"); - - if (largest_digit >= base) - error ("numeric constant contains digits beyond the radix"); - - /* Remove terminating char from the token buffer and delimit the string */ - *--p = 0; - - if (floatflag != NOT_FLOAT) - { - tree type = double_type_node; - char f_seen = 0; - char l_seen = 0; - REAL_VALUE_TYPE value; - - /* Read explicit exponent if any, and put it in tokenbuf. */ - - if ((c == 'e') || (c == 'E')) - { - if (p >= token_buffer + maxtoken - 3) - p = extend_token_buffer (p); - *p++ = c; - c = getc (finput); - if ((c == '+') || (c == '-')) - { - *p++ = c; - c = getc (finput); - } - if (! isdigit (c)) - error ("floating constant exponent has no digits"); - while (isdigit (c)) - { - if (p >= token_buffer + maxtoken - 3) - p = extend_token_buffer (p); - *p++ = c; - c = getc (finput); - } - } - - *p = 0; - errno = 0; - value = REAL_VALUE_ATOF (token_buffer); -#ifdef ERANGE - if (errno == ERANGE && !flag_traditional) - { - char *p1 = token_buffer; - /* Check for "0.0" and variants; - Sunos 4 spuriously returns ERANGE for them. */ - while (*p1 == '0') p1++; - if (*p1 == '.') - { - p1++; - while (*p1 == '0') p1++; - } - if (*p1 == 'e' || *p1 == 'E') - { - /* with significand==0, ignore the exponent */ - p1++; - while (*p1 != 0) p1++; - } - /* ERANGE is also reported for underflow, - so test the value to distinguish overflow from that. */ - if (*p1 != 0 && (value > 1.0 || value < -1.0)) - warning ("floating point number exceeds range of `double'"); - } -#endif - - /* Read the suffixes to choose a data type. */ - while (1) - { - if (c == 'f' || c == 'F') - { - float floater; - if (f_seen) - error ("two `f's in floating constant"); - f_seen = 1; - type = float_type_node; - floater = value; - value = floater; - } - else if (c == 'l' || c == 'L') - { - if (l_seen) - error ("two `l's in floating constant"); - l_seen = 1; - type = long_double_type_node; - } - else - { - if (isalnum (c)) - { - error ("garbage at end of number"); - while (isalnum (c)) - { - if (p >= token_buffer + maxtoken - 3) - p = extend_token_buffer (p); - *p++ = c; - c = getc (finput); - } - } - break; - } - if (p >= token_buffer + maxtoken - 3) - p = extend_token_buffer (p); - *p++ = c; - c = getc (finput); - } - - /* Create a node with determined type and value. */ - yylval.ttype = build_real (type, value); - - ungetc (c, finput); - *p = 0; - } - else - { - tree type; - int spec_unsigned = 0; - int spec_long = 0; - int spec_long_long = 0; - - while (1) - { - if (c == 'u' || c == 'U') - { - if (spec_unsigned) - error ("two `u's in integer constant"); - spec_unsigned = 1; - } - else if (c == 'l' || c == 'L') - { - if (spec_long) - { - if (spec_long_long) - error ("three `l's in integer constant"); - else if (pedantic) - warning ("ANSI C forbids long long integer constants"); - spec_long_long = 1; - } - spec_long = 1; - } - else - { - if (isalnum (c)) - { - error ("garbage at end of number"); - while (isalnum (c)) - { - if (p >= token_buffer + maxtoken - 3) - p = extend_token_buffer (p); - *p++ = c; - c = getc (finput); - } - } - break; - } - if (p >= token_buffer + maxtoken - 3) - p = extend_token_buffer (p); - *p++ = c; - c = getc (finput); - } - - ungetc (c, finput); - - if ((overflow || shorts[7] || shorts[6] || shorts[5] || shorts[4]) - && !spec_long_long) - warning ("integer constant out of range"); - - /* If it won't fit in a signed long long, make it unsigned. - We can't distinguish based on the tree node because - any integer constant fits any long long type. */ - if (shorts[7] >= (1<<8)) - spec_unsigned = 1; - - /* This is simplified by the fact that our constant - is always positive. */ - yylval.ttype - = (build_int_2 - ((shorts[3]<<24) + (shorts[2]<<16) + (shorts[1]<<8) + shorts[0], - (spec_long_long - ? (shorts[7]<<24) + (shorts[6]<<16) + (shorts[5]<<8) + shorts[4] - : 0))); - - if (!spec_long && !spec_unsigned - && int_fits_type_p (yylval.ttype, integer_type_node)) - type = integer_type_node; - - else if (!spec_long && (base != 10 || spec_unsigned) - && int_fits_type_p (yylval.ttype, unsigned_type_node)) - type = unsigned_type_node; - - else if (!spec_unsigned && !spec_long_long - && int_fits_type_p (yylval.ttype, long_integer_type_node)) - type = long_integer_type_node; - - else if (! spec_long_long - && int_fits_type_p (yylval.ttype, - long_unsigned_type_node)) - type = long_unsigned_type_node; - - else if (! spec_unsigned - && int_fits_type_p (yylval.ttype, - long_long_integer_type_node)) - type = long_long_integer_type_node; - - else if (int_fits_type_p (yylval.ttype, - long_long_unsigned_type_node)) - type = long_long_unsigned_type_node; - - else - { - type = long_long_integer_type_node; - warning ("integer constant out of range"); - } - - TREE_TYPE (yylval.ttype) = type; - *p = 0; - } - - value = CONSTANT; break; - } - - case '\'': - char_constant: - { - register int result = 0; - register num_chars = 0; - int width = TYPE_PRECISION (char_type_node); - int max_chars; - - if (wide_flag) width = TYPE_PRECISION (integer_type_node); - - max_chars = TYPE_PRECISION (integer_type_node) / width; - - while (1) - { - tryagain: - - c = getc (finput); - - if (c == '\'' || c == EOF) - break; - - if (c == '\\') - { - c = readescape (); - if (c < 0) - goto tryagain; - if (width < HOST_BITS_PER_INT - && (unsigned) c >= (1 << width)) - warning ("escape sequence out of range for character"); - } - else if (c == '\n') - { - if (pedantic) - warning ("ANSI C forbids newline in character constant"); - lineno++; - } - - num_chars++; - if (num_chars > maxtoken - 4) - extend_token_buffer (token_buffer); - - token_buffer[num_chars] = c; - - /* Merge character into result; ignore excess chars. */ - if (num_chars < max_chars + 1) - { - if (width < HOST_BITS_PER_INT) - result = (result << width) | (c & ((1 << width) - 1)); - else - result = c; - } - } - - token_buffer[num_chars + 1] = '\''; - token_buffer[num_chars + 2] = 0; - - if (c != '\'') - error ("malformatted character constant"); - else if (num_chars == 0) - error ("empty character constant"); - else if (num_chars > max_chars) - { - num_chars = max_chars; - error ("character constant too long"); - } - else if (num_chars != 1 && ! flag_traditional) - warning ("multi-character character constant"); - - /* If char type is signed, sign-extend the constant. */ - if (! wide_flag) - { - int num_bits = num_chars * width; - if (TREE_UNSIGNED (char_type_node) - || ((result >> (num_bits - 1)) & 1) == 0) - yylval.ttype - = build_int_2 (result & ((unsigned) ~0 - >> (HOST_BITS_PER_INT - num_bits)), - 0); - else - yylval.ttype - = build_int_2 (result | ~((unsigned) ~0 - >> (HOST_BITS_PER_INT - num_bits)), - -1); - } - else - yylval.ttype = build_int_2 (result, 0); - - TREE_TYPE (yylval.ttype) = integer_type_node; - value = CONSTANT; break; - } - - case '"': - string_constant: - { - int *widep; - - c = getc (finput); - p = token_buffer + 1; - - if (wide_flag) - widep = wide_buffer; - - while (c != '"' && c >= 0) - { - if (c == '\\') - { - c = readescape (); - if (c < 0) - goto skipnewline; - if (!wide_flag && c >= (1 << TYPE_PRECISION (char_type_node))) - warning ("escape sequence out of range for character"); - } - else if (c == '\n') - { - if (pedantic) - warning ("ANSI C forbids newline in string constant"); - lineno++; - } - - /* Store the char in C into the appropriate buffer. */ - - if (wide_flag) - { - if (widep == wide_buffer + max_wide) - { - int n = widep - wide_buffer; - max_wide *= 2; - wide_buffer = (int *) xrealloc (wide_buffer, max_wide + 1); - widep = wide_buffer + n; - } - *widep++ = c; - } - else - { - if (p == token_buffer + maxtoken) - p = extend_token_buffer (p); - *p++ = c; - } - - skipnewline: - c = getc (finput); - } - - /* We have read the entire constant. - Construct a STRING_CST for the result. */ - - if (wide_flag) - { - /* If this is a L"..." wide-string, make a vector - of the ints in wide_buffer. */ - *widep = 0; - /* We have not implemented the case where `int' - on the target and on the execution machine differ in size. */ - if (TYPE_PRECISION (integer_type_node) - != sizeof (int) * BITS_PER_UNIT) - abort (); - yylval.ttype - = build_string ((widep - wide_buffer + 1) * sizeof (int), - wide_buffer); - TREE_TYPE (yylval.ttype) = int_array_type_node; - } - else - { - *p = 0; - yylval.ttype = build_string (p - token_buffer, token_buffer + 1); - TREE_TYPE (yylval.ttype) = char_array_type_node; - } - - *p++ = '"'; - *p = 0; - - value = STRING; break; - } - - case '+': - case '-': - case '&': - case '|': - case '<': - case '>': - case '*': - case '/': - case '%': - case '^': - case '!': - case '=': - { - register int c1; - - combine: - - switch (c) - { - case '+': - yylval.code = PLUS_EXPR; break; - case '-': - yylval.code = MINUS_EXPR; break; - case '&': - yylval.code = BIT_AND_EXPR; break; - case '|': - yylval.code = BIT_IOR_EXPR; break; - case '*': - yylval.code = MULT_EXPR; break; - case '/': - yylval.code = TRUNC_DIV_EXPR; break; - case '%': - yylval.code = TRUNC_MOD_EXPR; break; - case '^': - yylval.code = BIT_XOR_EXPR; break; - case LSHIFT: - yylval.code = LSHIFT_EXPR; break; - case RSHIFT: - yylval.code = RSHIFT_EXPR; break; - case '<': - yylval.code = LT_EXPR; break; - case '>': - yylval.code = GT_EXPR; break; - } - - token_buffer[1] = c1 = getc (finput); - token_buffer[2] = 0; - - if (c1 == '=') - { - switch (c) - { - case '<': - value = ARITHCOMPARE; yylval.code = LE_EXPR; goto done; - case '>': - value = ARITHCOMPARE; yylval.code = GE_EXPR; goto done; - case '!': - value = EQCOMPARE; yylval.code = NE_EXPR; goto done; - case '=': - value = EQCOMPARE; yylval.code = EQ_EXPR; goto done; - } - value = ASSIGN; goto done; - } - else if (c == c1) - switch (c) - { - case '+': - value = PLUSPLUS; goto done; - case '-': - value = MINUSMINUS; goto done; - case '&': - value = ANDAND; goto done; - case '|': - value = OROR; goto done; - case '<': - c = LSHIFT; - goto combine; - case '>': - c = RSHIFT; - goto combine; - } - else if ((c == '-') && (c1 == '>')) - { value = POINTSAT; goto done; } - ungetc (c1, finput); - token_buffer[1] = 0; - - if ((c == '<') || (c == '>')) - value = ARITHCOMPARE; - else value = c; - goto done; - } - - case 0: - /* Don't make yyparse think this is eof. */ - value = 1; - break; - - default: - value = c; - } - -done: -/* yylloc.last_line = lineno; */ - - return value; -} diff --git a/gnu/usr.bin/gcc1/cc1/c-tree.h b/gnu/usr.bin/gcc1/cc1/c-tree.h deleted file mode 100644 index 49def44f0e..0000000000 --- a/gnu/usr.bin/gcc1/cc1/c-tree.h +++ /dev/null @@ -1,154 +0,0 @@ -/* Definitions for C parsing and type checking. - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Language-dependent contents of an identifier. */ - -struct lang_identifier -{ - struct tree_identifier ignore; - tree global_value, local_value, label_value, implicit_decl; - tree error_locus; -}; - -/* Macros for access to language-specific slots in an identifier. */ - -#define IDENTIFIER_GLOBAL_VALUE(NODE) \ - (((struct lang_identifier *)(NODE))->global_value) -#define IDENTIFIER_LOCAL_VALUE(NODE) \ - (((struct lang_identifier *)(NODE))->local_value) -#define IDENTIFIER_LABEL_VALUE(NODE) \ - (((struct lang_identifier *)(NODE))->label_value) -#define IDENTIFIER_IMPLICIT_DECL(NODE) \ - (((struct lang_identifier *)(NODE))->implicit_decl) -#define IDENTIFIER_ERROR_LOCUS(NODE) \ - (((struct lang_identifier *)(NODE))->error_locus) - -/* Nonzero means reject anything that ANSI standard C forbids. */ -extern int pedantic; - -/* In a RECORD_TYPE or UNION_TYPE, nonzero if any component is read-only. */ -#define C_TYPE_FIELDS_READONLY(type) TYPE_SEP_UNIT (type) - -/* in c-typecheck.c */ -extern tree build_component_ref(), build_conditional_expr(), build_compound_expr(); -extern tree build_unary_op(), build_binary_op(), build_function_call(); -extern tree build_binary_op_nodefault (); -extern tree build_indirect_ref(), build_array_ref(), build_c_cast(); -extern tree build_modify_expr(); -extern tree c_sizeof (), c_alignof (); -extern void store_init_value (); -extern tree digest_init (); -extern tree c_expand_start_case (); -extern tree default_conversion (); - -/* Given two integer or real types, return the type for their sum. - Given two compatible ANSI C types, returns the merged type. */ - -extern tree commontype (); - -/* in c-decl.c */ -extern tree build_label (); - -extern int start_function (); -extern void finish_function (); -extern void store_parm_decls (); -extern tree get_parm_info (); - -extern void pushlevel (); -extern tree poplevel (); - -extern tree groktypename(), lookup_name(); - -extern tree lookup_label(), define_label(); - -extern tree implicitly_declare(), getdecls(), gettags (); - -extern tree start_decl(); -extern void finish_decl(); - -extern tree start_struct(), finish_struct(), xref_tag(); -extern tree grokfield(); - -extern tree start_enum(), finish_enum(); -extern tree build_enumerator(); - -extern tree make_index_type (); - -/* Add qualifiers to a type, in the fashion for C. */ -extern tree c_build_type_variant (); - -extern tree double_type_node, long_double_type_node, float_type_node; -extern tree char_type_node, unsigned_char_type_node, signed_char_type_node; - -extern tree short_integer_type_node, short_unsigned_type_node; -extern tree long_integer_type_node, long_unsigned_type_node; -extern tree long_long_integer_type_node, long_long_unsigned_type_node; -extern tree unsigned_type_node; -extern tree string_type_node, char_array_type_node, int_array_type_node; - -extern int current_function_returns_value; -extern int current_function_returns_null; - -extern tree ridpointers[]; - -/* Nonzero means `$' can be in an identifier. */ - -extern int dollars_in_ident; - -/* Nonzero means allow type mismatches in conditional expressions; - just make their values `void'. */ - -extern int flag_cond_mismatch; - -/* Nonzero means don't recognize the keyword `asm'. */ - -extern int flag_no_asm; - -/* Nonzero means warn about implicit declarations. */ - -extern int warn_implicit; - -/* Nonzero means warn about function definitions that default the return type - or that use a null return and have a return-type other than void. */ - -extern int warn_return_type; - -/* Nonzero means give string constants the type `const char *' - to get extra warnings from them. These warnings will be too numerous - to be useful, except in thoroughly ANSIfied programs. */ - -extern int warn_write_strings; - -/* Nonzero means warn about sizeof(function) or addition/subtraction - of function pointers. */ - -extern int warn_pointer_arith; - -/* Nonzero means warn for all old-style non-prototype function decls. */ - -extern int warn_strict_prototypes; - -/* Nonzero means warn about pointer casts that can drop a type qualifier - from the pointer target type. */ - -extern int warn_cast_qual; - -/* Nonzero means do some things the same way PCC does. */ - -extern int flag_traditional; diff --git a/gnu/usr.bin/gcc1/cc1/c-typeck.c b/gnu/usr.bin/gcc1/cc1/c-typeck.c deleted file mode 100644 index 74628dfdf5..0000000000 --- a/gnu/usr.bin/gcc1/cc1/c-typeck.c +++ /dev/null @@ -1,3812 +0,0 @@ -/*- - * This code is derived from software copyrighted by the Free Software - * Foundation. - * - * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. - */ - -#ifndef lint -static char sccsid[] = "@(#)c-typeck.c 6.3 (Berkeley) 5/8/91"; -#endif /* not lint */ - -/* Build expressions with type checking for C compiler. - Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This file is part of the C front end. - It contains routines to build C expressions given their operands, - including computing the types of the result, C-specific error checks, - and some optimization. - - There are also routines to build RETURN_STMT nodes and CASE_STMT nodes, - and to process initializations in declarations (since they work - like a strange sort of assignment). */ - -#include "config.h" -#include -#include "tree.h" -#include "c-tree.h" -#include "flags.h" - - - -int mark_addressable (); -static tree convert_for_assignment (); -static int compparms (); -int comp_target_types (); -static tree shorten_compare (); -static void binary_op_error (); -static tree pointer_int_sum (); -static tree pointer_diff (); -static tree convert_sequence (); -static tree unary_complex_lvalue (); -static tree process_init_constructor (); -tree digest_init (); -tree truthvalue_conversion (); -static tree invert_truthvalue (); -void incomplete_type_error (); -void readonly_warning (); - -/* Return the _TYPE node describing the data type - of the data which NODE represents as a C expression. - Arrays and functions are converted to pointers - just as they are when they appear as C expressions. */ - -tree -datatype (node) - tree node; -{ - register tree type = TREE_TYPE (node); - if (TREE_CODE (type) == ARRAY_TYPE) - return TYPE_POINTER_TO (TREE_TYPE (type)); - if (TREE_CODE (type) == FUNCTION_TYPE) - return build_pointer_type (type); - return type; -} - -/* Do `exp = require_complete_type (exp);' to make sure exp - does not have an incomplete type. (That includes void types.) */ - -tree -require_complete_type (value) - tree value; -{ - tree type = TREE_TYPE (value); - - /* First, detect a valid value with a complete type. */ - if (TYPE_SIZE (type) != 0 - && type != void_type_node) - return value; - - incomplete_type_error (value, type); - return error_mark_node; -} - -/* Print an error message for invalid use of an incomplete type. - VALUE is the expression that was used (or 0 if that isn't known) - and TYPE is the type that was invalid. */ - -void -incomplete_type_error (value, type) - tree value; - tree type; -{ - char *errmsg; - - /* Avoid duplicate error message. */ - if (TREE_CODE (type) == ERROR_MARK) - return; - - if (value != 0 && (TREE_CODE (value) == VAR_DECL - || TREE_CODE (value) == PARM_DECL)) - error ("`%s' has an incomplete type", - IDENTIFIER_POINTER (DECL_NAME (value))); - else - { - retry: - /* We must print an error message. Be clever about what it says. */ - - switch (TREE_CODE (type)) - { - case RECORD_TYPE: - errmsg = "invalid use of undefined type `struct %s'"; - break; - - case UNION_TYPE: - errmsg = "invalid use of undefined type `union %s'"; - break; - - case ENUMERAL_TYPE: - errmsg = "invalid use of undefined type `enum %s'"; - break; - - case VOID_TYPE: - error ("invalid use of void expression"); - return; - - case ARRAY_TYPE: - if (TYPE_DOMAIN (type)) - { - type = TREE_TYPE (type); - goto retry; - } - error ("invalid use of array with unspecified bounds"); - return; - - default: - abort (); - } - - if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) - error (errmsg, IDENTIFIER_POINTER (TYPE_NAME (type))); - else - /* If this type has a typedef-name, the TYPE_NAME is a TYPE_DECL. */ - error ("invalid use of incomplete typedef `%s'", - IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)))); - } -} - -/* Return a variant of TYPE which has all the type qualifiers of LIKE - as well as those of TYPE. */ - -static tree -qualify_type (type, like) - tree type, like; -{ - int constflag = TREE_READONLY (type) || TREE_READONLY (like); - int volflag = TREE_VOLATILE (type) || TREE_VOLATILE (like); - return c_build_type_variant (type, constflag, volflag); -} - -/* Return the common type of two types. - We assume that comptypes has already been done and returned 1; - if that isn't so, this may crash. - - This is the type for the result of most arithmetic operations - if the operands have the given two types. - - We do not deal with enumeral types here because they have already been - converted to integer types. */ - -tree -commontype (t1, t2) - tree t1, t2; -{ - register enum tree_code form1; - register enum tree_code form2; - - /* Save time if the two types are the same. */ - - if (t1 == t2) return t1; - - /* If one type is nonsense, use the other. */ - if (t1 == error_mark_node) - return t2; - if (t2 == error_mark_node) - return t1; - - /* Treat an enum type as the unsigned integer type of the same width. */ - - if (TREE_CODE (t1) == ENUMERAL_TYPE) - t1 = type_for_size (TYPE_PRECISION (t1), 1); - if (TREE_CODE (t2) == ENUMERAL_TYPE) - t2 = type_for_size (TYPE_PRECISION (t2), 1); - - form1 = TREE_CODE (t1); - form2 = TREE_CODE (t2); - - switch (form1) - { - case INTEGER_TYPE: - case REAL_TYPE: - /* If only one is real, use it as the result. */ - - if (form1 == REAL_TYPE && form2 != REAL_TYPE) - return t1; - - if (form2 == REAL_TYPE && form1 != REAL_TYPE) - return t2; - - /* Both real or both integers; use the one with greater precision. */ - - if (TYPE_PRECISION (t1) > TYPE_PRECISION (t2)) - return t1; - else if (TYPE_PRECISION (t2) > TYPE_PRECISION (t1)) - return t2; - - /* Same precision. Prefer longs to ints even when same size. */ - - if (t1 == long_unsigned_type_node - || t2 == long_unsigned_type_node) - return long_unsigned_type_node; - - if (t1 == long_integer_type_node - || t2 == long_integer_type_node) - { - /* But preserve unsignedness from the other type, - since long cannot hold all the values of an unsigned int. */ - if (TREE_UNSIGNED (t1) || TREE_UNSIGNED (t2)) - return long_unsigned_type_node; - return long_integer_type_node; - } - - /* Otherwise prefer the unsigned one. */ - - if (TREE_UNSIGNED (t1)) - return t1; - else return t2; - - case POINTER_TYPE: -#if 0 - /* For two pointers, do this recursively on the target type, - and combine the qualifiers of the two types' targets. */ - { - tree target = commontype (TYPE_MAIN_VARIANT (TREE_TYPE (t1)), - TYPE_MAIN_VARIANT (TREE_TYPE (t2))); - int constp - = TREE_READ_ONLY (TREE_TYPE (t1)) || TREE_READ_ONLY (TREE_TYPE (t2)); - int volatilep - = TREE_VOLATILE (TREE_TYPE (t1)) || TREE_VOLATILE (TREE_TYPE (t2)); - return build_pointer_type (c_build_type_variant (target, constp, volatilep)); - } -#endif - return build_pointer_type (commontype (TREE_TYPE (t1), TREE_TYPE (t2))); - - case ARRAY_TYPE: - { - tree elt = commontype (TREE_TYPE (t1), TREE_TYPE (t2)); - /* Save space: see if the result is identical to one of the args. */ - if (elt == TREE_TYPE (t1) && TYPE_DOMAIN (t1)) - return t1; - if (elt == TREE_TYPE (t2) && TYPE_DOMAIN (t2)) - return t2; - /* Merge the element types, and have a size if either arg has one. */ - return build_array_type (elt, TYPE_DOMAIN (TYPE_DOMAIN (t1) ? t1 : t2)); - } - - case FUNCTION_TYPE: - /* Function types: prefer the one that specified arg types. - If both do, merge the arg types. Also merge the return types. */ - { - tree valtype = commontype (TREE_TYPE (t1), TREE_TYPE (t2)); - tree p1 = TYPE_ARG_TYPES (t1); - tree p2 = TYPE_ARG_TYPES (t2); - int len; - tree newargs, n; - int i; - - /* Save space: see if the result is identical to one of the args. */ - if (valtype == TREE_TYPE (t1) && ! TYPE_ARG_TYPES (t2)) - return t1; - if (valtype == TREE_TYPE (t2) && ! TYPE_ARG_TYPES (t1)) - return t2; - - /* Simple way if one arg fails to specify argument types. */ - if (TYPE_ARG_TYPES (t1) == 0) - return build_function_type (valtype, TYPE_ARG_TYPES (t2)); - if (TYPE_ARG_TYPES (t2) == 0) - return build_function_type (valtype, TYPE_ARG_TYPES (t1)); - - /* If both args specify argument types, we must merge the two - lists, argument by argument. */ - - len = list_length (p1); - newargs = 0; - - for (i = 0; i < len; i++) - newargs = tree_cons (0, 0, newargs); - - n = newargs; - - for (; p1; - p1 = TREE_CHAIN (p1), p2 = TREE_CHAIN (p2), n = TREE_CHAIN (n)) - TREE_VALUE (n) = commontype (TREE_VALUE (p1), TREE_VALUE (p2)); - - return build_function_type (valtype, newargs); - } - - default: - return t1; - } - -} - -/* Return 1 if TYPE1 and TYPE2 are compatible types for assignment - or various other operations. This is what ANSI C speaks of as - "being the same". */ - -int -comptypes (type1, type2) - tree type1, type2; -{ - register tree t1 = type1; - register tree t2 = type2; - - /* Suppress errors caused by previously reported errors */ - - if (t1 == t2 || TREE_CODE (t1) == ERROR_MARK || TREE_CODE (t2) == ERROR_MARK) - return 1; - - /* Treat an enum type as the unsigned integer type of the same width. */ - - if (TREE_CODE (t1) == ENUMERAL_TYPE) - t1 = type_for_size (TYPE_PRECISION (t1), 1); - if (TREE_CODE (t2) == ENUMERAL_TYPE) - t2 = type_for_size (TYPE_PRECISION (t2), 1); - - if (t1 == t2) - return 1; - - /* Different classes of types can't be compatible. */ - - if (TREE_CODE (t1) != TREE_CODE (t2)) return 0; - - /* Qualifiers must match. */ - - if (TREE_READONLY (t1) != TREE_READONLY (t2)) - return 0; - if (TREE_THIS_VOLATILE (t1) != TREE_THIS_VOLATILE (t2)) - return 0; - - switch (TREE_CODE (t1)) - { - case POINTER_TYPE: - return (TREE_TYPE (t1) == TREE_TYPE (t2) - || comptypes (TREE_TYPE (t1), TREE_TYPE (t2))); - - case FUNCTION_TYPE: - return ((TREE_TYPE (t1) == TREE_TYPE (t2) - || comptypes (TREE_TYPE (t1), TREE_TYPE (t2))) - && compparms (TYPE_ARG_TYPES (t1), TYPE_ARG_TYPES (t2))); - - case ARRAY_TYPE: - /* Target types must match incl. qualifiers. */ - if (!(TREE_TYPE (t1) == TREE_TYPE (t2) - || comptypes (TREE_TYPE (t1), TREE_TYPE (t2)))) - return 0; - { - tree d1 = TYPE_DOMAIN (t1); - tree d2 = TYPE_DOMAIN (t2); - - /* Sizes must match unless one is missing or variable. */ - if (d1 == 0 || d2 == 0 || d1 == d2 - || TREE_CODE (TYPE_MIN_VALUE (d1)) != INTEGER_CST - || TREE_CODE (TYPE_MIN_VALUE (d2)) != INTEGER_CST - || TREE_CODE (TYPE_MAX_VALUE (d1)) != INTEGER_CST - || TREE_CODE (TYPE_MAX_VALUE (d2)) != INTEGER_CST) - return 1; - - return ((TREE_INT_CST_LOW (TYPE_MIN_VALUE (d1)) - == TREE_INT_CST_LOW (TYPE_MIN_VALUE (d2))) - && (TREE_INT_CST_HIGH (TYPE_MIN_VALUE (d1)) - == TREE_INT_CST_HIGH (TYPE_MIN_VALUE (d2))) - && (TREE_INT_CST_LOW (TYPE_MAX_VALUE (d1)) - == TREE_INT_CST_LOW (TYPE_MAX_VALUE (d2))) - && (TREE_INT_CST_HIGH (TYPE_MAX_VALUE (d1)) - == TREE_INT_CST_HIGH (TYPE_MAX_VALUE (d2)))); - } - } - return 0; -} - -/* Return 1 if TTL and TTR are pointers to types that are equivalent, - ignoring their qualifiers. */ - -int -comp_target_types (ttl, ttr) - tree ttl, ttr; -{ - return comptypes (TYPE_MAIN_VARIANT (TREE_TYPE (ttl)), - TYPE_MAIN_VARIANT (TREE_TYPE (ttr))); -} - -/* Subroutines of `comptypes'. */ - -/* Return 1 if two parameter type lists PARMS1 and PARMS2 - are equivalent in the sense that functions with those parameter types - can have equivalent types. - If either list is empty, we win. - Otherwise, the two lists must be equivalent, element by element. */ - -static int -compparms (parms1, parms2) - tree parms1, parms2; -{ - register tree t1 = parms1, t2 = parms2; - - /* An unspecified parmlist matches any specified parmlist - whose argument types don't need default promotions. */ - - if (t1 == 0) - return compparms1 (t2); - if (t2 == 0) - return compparms1 (t1); - - while (1) - { - if (t1 == 0 && t2 == 0) - return 1; - /* If one parmlist is shorter than the other, - they fail to match. */ - if (t1 == 0 || t2 == 0) - return 0; - if (! comptypes (TREE_VALUE (t1), TREE_VALUE (t2))) - return 0; - t1 = TREE_CHAIN (t1); - t2 = TREE_CHAIN (t2); - } -} - -/* Return 1 if PARMS specifies a fixed number of parameters - and none of their types is affected by default promotions. */ - -int -compparms1 (parms) - tree parms; -{ - register tree t; - for (t = parms; t; t = TREE_CHAIN (t)) - { - register tree type = TREE_VALUE (t); - - if (TREE_CHAIN (t) == 0 && type != void_type_node) - return 0; - - if (type == float_type_node) - return 0; - - if (TREE_CODE (type) == INTEGER_TYPE - && TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node)) - return 0; - } - return 1; -} - -/* Return an unsigned type the same as TYPE in other respects. */ - -tree -unsigned_type (type) - tree type; -{ - if (type == signed_char_type_node || type == char_type_node) - return unsigned_char_type_node; - if (type == integer_type_node) - return unsigned_type_node; - if (type == short_integer_type_node) - return short_unsigned_type_node; - if (type == long_integer_type_node) - return long_unsigned_type_node; - if (type == long_long_integer_type_node) - return long_long_unsigned_type_node; - return type; -} - -/* Return a signed type the same as TYPE in other respects. */ - -tree -signed_type (type) - tree type; -{ - if (type == unsigned_char_type_node || type == char_type_node) - return signed_char_type_node; - if (type == unsigned_type_node) - return integer_type_node; - if (type == short_unsigned_type_node) - return short_integer_type_node; - if (type == long_unsigned_type_node) - return long_integer_type_node; - if (type == long_long_unsigned_type_node) - return long_long_integer_type_node; - return type; -} - -/* Return a type the same as TYPE except unsigned or - signed according to UNSIGNEDP. */ - -tree -signed_or_unsigned_type (unsignedp, type) - int unsignedp; - tree type; -{ - if (TREE_CODE (type) != INTEGER_TYPE) - return type; - if (TYPE_PRECISION (type) == TYPE_PRECISION (signed_char_type_node)) - return unsignedp ? unsigned_char_type_node : signed_char_type_node; - if (TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node)) - return unsignedp ? unsigned_type_node : integer_type_node; - if (TYPE_PRECISION (type) == TYPE_PRECISION (short_integer_type_node)) - return unsignedp ? short_unsigned_type_node : short_integer_type_node; - if (TYPE_PRECISION (type) == TYPE_PRECISION (long_integer_type_node)) - return unsignedp ? long_unsigned_type_node : long_integer_type_node; - if (TYPE_PRECISION (type) == TYPE_PRECISION (long_long_integer_type_node)) - return (unsignedp ? long_long_unsigned_type_node - : long_long_integer_type_node); - return type; -} - -/* Return an integer type with BITS bits of precision, - that is unsigned if UNSIGNEDP is nonzero, otherwise signed. */ - -tree -type_for_size (bits, unsignedp) - int bits; - int unsignedp; -{ - if (bits <= TYPE_PRECISION (signed_char_type_node)) - return unsignedp ? unsigned_char_type_node : signed_char_type_node; - - if (bits <= TYPE_PRECISION (short_integer_type_node)) - return unsignedp ? short_unsigned_type_node : short_integer_type_node; - - if (bits <= TYPE_PRECISION (integer_type_node)) - return unsignedp ? unsigned_type_node : integer_type_node; - - if (bits <= TYPE_PRECISION (long_integer_type_node)) - return unsignedp ? long_unsigned_type_node : long_integer_type_node; - - if (bits <= TYPE_PRECISION (long_long_integer_type_node)) - return (unsignedp ? long_long_unsigned_type_node - : long_long_integer_type_node); - - return 0; -} - -tree -get_floating_type (mode) - enum machine_mode mode; -{ - if (mode == TYPE_MODE (float_type_node)) - return float_type_node; - if (mode == TYPE_MODE (double_type_node)) - return double_type_node; - if (mode == TYPE_MODE (long_double_type_node)) - return long_double_type_node; - abort (); -} - -tree -c_sizeof (type) - tree type; -{ - enum tree_code code = TREE_CODE (type); - - if (code == FUNCTION_TYPE) - { - if (pedantic || warn_pointer_arith) - warning ("sizeof applied to a function type"); - return build_int (1); - } - if (code == VOID_TYPE) - { - if (pedantic || warn_pointer_arith) - warning ("sizeof applied to a void type"); - return build_int (1); - } - - /* Convert in case a char is more than one unit. */ - return convert_units (size_in_bytes (type), BITS_PER_UNIT, - TYPE_PRECISION (char_type_node)); -} - -tree -c_sizeof_nowarn (type) - tree type; -{ - enum tree_code code = TREE_CODE (type); - - if (code == FUNCTION_TYPE - || code == VOID_TYPE) - return build_int (1); - - /* Convert in case a char is more than one unit. */ - return convert_units (size_in_bytes (type), BITS_PER_UNIT, - TYPE_PRECISION (char_type_node)); -} - -/* Implement the __alignof keyword: Return the minimum required - alignment of TYPE, measured in bytes. */ - -tree -c_alignof (type) - tree type; -{ - enum tree_code code = TREE_CODE (type); - - if (code == FUNCTION_TYPE) - return build_int (FUNCTION_BOUNDARY / BITS_PER_UNIT); - - if (code == VOID_TYPE) - return build_int (1); - - return build_int (TYPE_ALIGN (type) / BITS_PER_UNIT); -} - -/* Return either DECL or its known constant value (if it has one). */ - -static tree -decl_constant_value (decl) - tree decl; -{ - if (! TREE_PUBLIC (decl) - /* Don't change a variable array bound or initial value to a constant - in a place where a variable is invalid. */ - && current_function_decl != 0 - && ! pedantic - && ! TREE_THIS_VOLATILE (decl) - && DECL_INITIAL (decl) != 0 - && TREE_CODE (DECL_INITIAL (decl)) != ERROR_MARK - /* This is invalid if initial value is not constant. - If it has either a function call, a memory reference, - or a variable, then re-evaluating it could give different results. */ - && TREE_LITERAL (DECL_INITIAL (decl)) - /* Check for cases where this is sub-optimal, even though valid. */ - && TREE_CODE (DECL_INITIAL (decl)) != CONSTRUCTOR - && DECL_MODE (decl) != BLKmode) - return DECL_INITIAL (decl); - return decl; -} - -/* Perform default promotions for C data used in expressions. - Arrays and functions are converted to pointers; - enumeral types or short or char, to int. - In addition, manifest constants symbols are replaced by their values. */ - -tree -default_conversion (exp) - tree exp; -{ - register tree dt = TREE_TYPE (exp); - register enum tree_code form = TREE_CODE (dt); - - if (TREE_CODE (exp) == CONST_DECL) - exp = DECL_INITIAL (exp); - /* Replace a nonvolatile const static variable with its value. */ - else if (optimize - && TREE_CODE (exp) == VAR_DECL - && TREE_READONLY (exp)) - exp = decl_constant_value (exp); - - /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. - Strip such NOP_EXPRs, since EXP is being used in non-lvalue context. */ - if (TREE_CODE (exp) == NOP_EXPR - && TREE_TYPE (exp) == TREE_TYPE (TREE_OPERAND (exp, 0))) - exp = TREE_OPERAND (exp, 0); - - if (form == ENUMERAL_TYPE - || (form == INTEGER_TYPE - && (TYPE_PRECISION (dt) - < TYPE_PRECISION (integer_type_node)))) - { - /* Traditionally, unsignedness is preserved in default promotions. */ - if (flag_traditional && TREE_UNSIGNED (dt)) - return convert (unsigned_type_node, exp); - return convert (integer_type_node, exp); - } - if (flag_traditional && dt == float_type_node) - return convert (double_type_node, exp); - if (form == VOID_TYPE) - { - error ("void value not ignored as it ought to be"); - return error_mark_node; - } - if (form == FUNCTION_TYPE) - { - return build_unary_op (ADDR_EXPR, exp, 0); - } - if (form == ARRAY_TYPE) - { - register tree adr; - tree restype = TREE_TYPE (dt); - tree ptrtype; - - if (TREE_CODE (exp) == INDIRECT_REF) - return convert (TYPE_POINTER_TO (restype), - TREE_OPERAND (exp, 0)); - - if (TREE_CODE (exp) == COMPOUND_EXPR) - { - tree op1 = default_conversion (TREE_OPERAND (exp, 1)); - return build (COMPOUND_EXPR, TREE_TYPE (op1), - TREE_OPERAND (exp, 0), op1); - } - - if (!lvalue_p (exp) - && ! (TREE_CODE (exp) == CONSTRUCTOR && TREE_STATIC (exp))) - { - error ("invalid use of non-lvalue array"); - return error_mark_node; - } - - if (TREE_READONLY (exp) || TREE_THIS_VOLATILE (exp)) - restype = c_build_type_variant (restype, TREE_READONLY (exp), - TREE_THIS_VOLATILE (exp)); - - ptrtype = build_pointer_type (restype); - - if (TREE_CODE (exp) == VAR_DECL) - { - /* ??? This is not really quite correct - in that the type of the operand of ADDR_EXPR - is not the target type of the type of the ADDR_EXPR itself. - Question is, can this lossage be avoided? */ - adr = build (ADDR_EXPR, ptrtype, exp); - if (mark_addressable (exp) == 0) - return error_mark_node; - TREE_LITERAL (adr) = staticp (exp); - TREE_VOLATILE (adr) = 0; /* Default would be, same as EXP. */ - return adr; - } - /* This way is better for a COMPONENT_REF since it can - simplify the offset for a component. */ - adr = build_unary_op (ADDR_EXPR, exp, 1); - return convert (ptrtype, adr); - } - return exp; -} - -/* Make an expression to refer to the COMPONENT field of - structure or union value DATUM. COMPONENT is an IDENTIFIER_NODE. */ - -tree -build_component_ref (datum, component) - tree datum, component; -{ - register tree basename = datum; - register tree basetype = TREE_TYPE (basename); - register enum tree_code form = TREE_CODE (basetype); - register tree field = NULL; - register tree ref; - - /* First, see if there is a field or component with name COMPONENT. */ - - if (form == RECORD_TYPE || form == UNION_TYPE) - { - if (TYPE_SIZE (basetype) == 0) - { - incomplete_type_error (0, basetype); - return error_mark_node; - } - - /* Look up component name in the structure type definition. */ - - for (field = TYPE_FIELDS (basetype); field; field = TREE_CHAIN (field)) - { - if (DECL_NAME (field) == component) - break; - } - - if (!field) - { - error (form == RECORD_TYPE - ? "structure has no member named `%s'" - : "union has no member named `%s'", - IDENTIFIER_POINTER (component)); - return error_mark_node; - } - if (TREE_TYPE (field) == error_mark_node) - return error_mark_node; - - ref = build (COMPONENT_REF, TREE_TYPE (field), basename, field); - - if (TREE_READONLY (basename) || TREE_READONLY (field)) - TREE_READONLY (ref) = 1; - if (TREE_THIS_VOLATILE (basename) || TREE_VOLATILE (field)) - TREE_THIS_VOLATILE (ref) = 1; - - return ref; - } - else if (form != ERROR_MARK) - error ("request for member `%s' in something not a structure or union", - IDENTIFIER_POINTER (component)); - - return error_mark_node; -} - -/* Given an expression PTR for a pointer, return an expression - for the value pointed to. - ERRORSTRING is the name of the operator to appear in error messages. */ - -tree -build_indirect_ref (ptr, errorstring) - tree ptr; - char *errorstring; -{ - register tree pointer = default_conversion (ptr); - register tree dt = TREE_TYPE (pointer); - - if (TREE_CODE (dt) == POINTER_TYPE) - if (TREE_CODE (pointer) == ADDR_EXPR - && (TREE_TYPE (TREE_OPERAND (pointer, 0)) - == TREE_TYPE (dt))) - return TREE_OPERAND (pointer, 0); - else - { - tree t = TREE_TYPE (dt); - register tree ref = build (INDIRECT_REF, - TYPE_MAIN_VARIANT (t), pointer); - - if (TREE_CODE (t) == VOID_TYPE - || (TYPE_SIZE (t) == 0 && TREE_CODE (t) != ARRAY_TYPE)) - { - error ("dereferencing pointer to incomplete type"); - return error_mark_node; - } - - TREE_READONLY (ref) = TREE_READONLY (t); - TREE_VOLATILE (ref) = TREE_VOLATILE (t) || TREE_VOLATILE (pointer); - TREE_THIS_VOLATILE (ref) = TREE_VOLATILE (t); - return ref; - } - else if (TREE_CODE (pointer) != ERROR_MARK) - error ("invalid type argument of `%s'", errorstring); - return error_mark_node; -} - -/* This handles expressions of the form "a[i]", which denotes - an array reference. - - This is logically equivalent in C to *(a+i), but we may do it differently. - If A is a variable or a member, we generate a primitive ARRAY_REF. - This avoids forcing the array out of registers, and can work on - arrays that are not lvalues (for example, members of structures returned - by functions). */ - -tree -build_array_ref (array, index) - tree array, index; -{ - if (index == 0) - { - error ("subscript missing in array reference"); - return error_mark_node; - } - - if (TREE_CODE (TREE_TYPE (array)) == ARRAY_TYPE - && TREE_CODE (array) != INDIRECT_REF) - { - tree rval, type; - - index = default_conversion (index); - if (index != error_mark_node - && TREE_CODE (TREE_TYPE (index)) != INTEGER_TYPE) - { - error ("array subscript is not an integer"); - return error_mark_node; - } - - /* An array that is indexed by a non-constant - cannot be stored in a register; we must be able to do - address arithmetic on its address. - Likewise an array of elements of variable size. */ - if (TREE_CODE (index) != INTEGER_CST - || (TYPE_SIZE (TREE_TYPE (TREE_TYPE (array))) != 0 - && TREE_CODE (TYPE_SIZE (TREE_TYPE (TREE_TYPE (array)))) != INTEGER_CST)) - { - if (mark_addressable (array) == 0) - return error_mark_node; - } - - if (pedantic && !lvalue_p (array)) - warning ("ANSI C forbids subscripting non-lvalue array"); - - if (pedantic) - { - tree foo = array; - while (TREE_CODE (foo) == COMPONENT_REF) - foo = TREE_OPERAND (foo, 0); - if (TREE_CODE (foo) == VAR_DECL && TREE_REGDECL (foo)) - warning ("ANSI C forbids subscripting non-lvalue array"); - } - - type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (array))); - rval = build (ARRAY_REF, type, array, index); - /* Array ref is const/volatile if the array elements are. */ - TREE_READONLY (rval) |= TREE_READONLY (TREE_TYPE (TREE_TYPE (array))); - TREE_VOLATILE (rval) |= TREE_VOLATILE (TREE_TYPE (TREE_TYPE (array))); - TREE_THIS_VOLATILE (rval) |= TREE_VOLATILE (TREE_TYPE (TREE_TYPE (array))); - return require_complete_type (fold (rval)); - } - - { - tree ar = default_conversion (array); - tree ind = default_conversion (index); - - if ((TREE_CODE (TREE_TYPE (ar)) == POINTER_TYPE - && TREE_CODE (TREE_TYPE (ind)) != INTEGER_TYPE) - || (TREE_CODE (TREE_TYPE (ind)) == POINTER_TYPE - && TREE_CODE (TREE_TYPE (ar)) != INTEGER_TYPE)) - { - error ("array subscript is not an integer"); - return error_mark_node; - } - - return build_indirect_ref (build_binary_op_nodefault (PLUS_EXPR, ar, ind, PLUS_EXPR), - "array indexing"); - } -} - -/* Build a function call to function FUNCTION with parameters PARAMS. - PARAMS is a list--a chain of TREE_LIST nodes--in which the - TREE_VALUE of each node is a parameter-expression. - FUNCTION's data type may be a function type or a pointer-to-function. */ - -tree -build_function_call (function, params) - tree function, params; -{ - register tree fntype; - register tree value_type; - register tree coerced_params; - tree name = NULL_TREE; - tree actualparameterlist (); - - /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. - Strip such NOP_EXPRs, since FUNCTION is used in non-lvalue context. */ - if (TREE_CODE (function) == NOP_EXPR - && TREE_TYPE (function) == TREE_TYPE (TREE_OPERAND (function, 0))) - function = TREE_OPERAND (function, 0); - - /* Convert anything with function type to a pointer-to-function. */ - if (TREE_CODE (function) == FUNCTION_DECL) - { - name = DECL_NAME (function); - /* Differs from default_conversion by not setting TREE_ADDRESSABLE - (because calling an inline function does not mean the function - needs to be separately compiled). */ - function = build (ADDR_EXPR, build_pointer_type (TREE_TYPE (function)), - function); - } - else - function = default_conversion (function); - - fntype = TREE_TYPE (function); - - if (TREE_CODE (fntype) == ERROR_MARK) - return error_mark_node; - - if (!(TREE_CODE (fntype) == POINTER_TYPE - && TREE_CODE (TREE_TYPE (fntype)) == FUNCTION_TYPE)) - { - error ("called object is not a function"); - return error_mark_node; - } - - /* fntype now gets the type of function pointed to. */ - fntype = TREE_TYPE (fntype); - - /* Convert the parameters to the types declared in the - function prototype, or apply default promotions. */ - - coerced_params = actualparameterlist (TYPE_ARG_TYPES (fntype), params, name); - - /* Recognize certain built-in functions so we can make tree-codes - other than CALL_EXPR. We do this when it enables fold-const.c - to do something useful. */ - - if (TREE_CODE (function) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (function, 0)) == FUNCTION_DECL) - switch (DECL_FUNCTION_CODE (TREE_OPERAND (function, 0))) - { - case BUILT_IN_ABS: - case BUILT_IN_LABS: - case BUILT_IN_FABS: - if (coerced_params == 0) - return integer_zero_node; - return build_unary_op (ABS_EXPR, TREE_VALUE (coerced_params), 0); - } - - value_type = TREE_TYPE (fntype) ? TREE_TYPE (fntype) : void_type_node; - - { - register tree result = - build (CALL_EXPR, value_type, function, coerced_params, NULL_TREE); - - TREE_VOLATILE (result) = 1; - if (value_type == void_type_node) - return result; - return require_complete_type (result); - } -} - -/* Convert the actual parameter expressions in the list VALUES - to the types in the list TYPELIST. - If parmdecls is exhausted, or when an element has NULL as its type, - perform the default conversions. - - NAME is an IDENTIFIER_NODE or 0. It is used only for error messages. - - This is also where warnings about wrong number of args are generated. - - Return a list of expressions for the parameters as converted. - - Both VALUES and the returned value are chains of TREE_LIST nodes - with the elements of the list in the TREE_VALUE slots of those nodes. */ - -tree -actualparameterlist (typelist, values, name) - tree typelist, values, name; -{ - register tree typetail, valtail; - register tree result = NULL; - register ordinal = 1; - - for (valtail = values, typetail = typelist; - valtail; - ++ordinal, valtail = TREE_CHAIN (valtail)) - { - register tree type = typetail ? TREE_VALUE (typetail) : 0; - register tree val = TREE_VALUE (valtail); - register tree parm; - - if (type == void_type_node) - { - if (name) - error ("too many arguments to function `%s'", - IDENTIFIER_POINTER (name)); - else - error ("too many arguments to function"); - break; - } - - /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. - Strip such NOP_EXPRs, since VAL is used in non-lvalue context. */ - if (TREE_CODE (val) == NOP_EXPR - && TREE_TYPE (val) == TREE_TYPE (TREE_OPERAND (val, 0))) - val = TREE_OPERAND (val, 0); - - if (TREE_CODE (TREE_TYPE (val)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE (val)) == FUNCTION_TYPE) - val = default_conversion (val); - - val = require_complete_type (val); - - if (type != 0) - { - /* Formal parm type is specified by a function prototype. */ - tree parmval; - - if (TYPE_SIZE (type) == 0) - { - error ("parameter type of called function is incomplete"); - parmval = val; - } - else - { -#ifdef PROMOTE_PROTOTYPES - /* Rather than truncating and then reextending, - convert directly to int, if that's the type we will want. */ - if (! flag_traditional - && TREE_CODE (type) == INTEGER_TYPE - && (TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node))) - type = integer_type_node; -#endif - parmval = convert_for_assignment (type, val, "argument passing", - ordinal); -#ifdef PROMOTE_PROTOTYPES - if (TREE_CODE (type) == INTEGER_TYPE - && (TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node))) - parmval = default_conversion (parmval); -#endif - } - parm = build_tree_list (0, parmval); - } - else if (TREE_CODE (TREE_TYPE (val)) == REAL_TYPE - && (TYPE_PRECISION (TREE_TYPE (val)) - < TYPE_PRECISION (double_type_node))) - /* Convert `float' to `double'. */ - parm = build_tree_list (NULL_TREE, convert (double_type_node, val)); - else - /* Convert `short' and `char' to full-size `int'. */ - parm = build_tree_list (NULL_TREE, default_conversion (val)); - - result = chainon (result, parm); - if (typetail) - typetail = TREE_CHAIN (typetail); - } - - if (typetail != 0 && TREE_VALUE (typetail) != void_type_node) - { - if (name) - error ("too few arguments to function `%s'", - IDENTIFIER_POINTER (name)); - else - error ("too few arguments to function"); - } - - return result; -} - -/* Build a binary-operation expression, after performing default - conversions on the operands. CODE is the kind of expression to build. */ - -tree -build_binary_op (code, arg1, arg2) - enum tree_code code; - tree arg1, arg2; -{ - return build_binary_op_nodefault (code, default_conversion (arg1), - default_conversion (arg2), code); -} - -/* Build a binary-operation expression without default conversions. - CODE is the kind of expression to build. - This function differs from `build' in several ways: - the data type of the result is computed and recorded in it, - warnings are generated if arg data types are invalid, - special handling for addition and subtraction of pointers is known, - and some optimization is done (operations on narrow ints - are done in the narrower type when that gives the same result). - Constant folding is also done before the result is returned. - - ERROR_CODE is the code that determines what to say in error messages. - It is usually, but not always, the same as CODE. - - Note that the operands will never have enumeral types - because either they have just had the default conversions performed - or they have both just been converted to some other type in which - the arithmetic is to be done. */ - -tree -build_binary_op_nodefault (code, op0, op1, error_code) - enum tree_code code; - tree op0, op1; - enum tree_code error_code; -{ - tree dt0 = datatype (op0), dt1 = datatype (op1); - - /* The expression codes of the data types of the arguments tell us - whether the arguments are integers, floating, pointers, etc. */ - register enum tree_code code0 = TREE_CODE (dt0); - register enum tree_code code1 = TREE_CODE (dt1); - - /* Expression code to give to the expression when it is built. - Normally this is CODE, which is what the caller asked for, - but in some special cases we change it. */ - register enum tree_code resultcode = code; - - /* Data type in which the computation is to be performed. - In the simplest cases this is the common type of the arguments. */ - register tree result_type = NULL; - - /* Nonzero means operands have already been type-converted - in whatever way is necessary. - Zero means they need to be converted to RESULT_TYPE. */ - int converted = 0; - - /* Nonzero means after finally constructing the expression - give it this type. Otherwise, give it type RESULT_TYPE. */ - tree final_type = 0; - - /* Nonzero if this is an operation like MIN or MAX which can - safely be computed in short if both args are promoted shorts. - Also implies COMMON. - -1 indicates a bitwise operation; this makes a difference - in the exact conditions for when it is safe to do the operation - in a narrower mode. */ - int shorten = 0; - - /* Nonzero if this is a comparison operation; - if both args are promoted shorts, compare the original shorts. - Also implies COMMON. */ - int short_compare = 0; - - /* Nonzero if this is a right-shift operation, which can be computed on the - original short and then promoted if the operand is a promoted short. */ - int short_shift = 0; - - /* Nonzero means set RESULT_TYPE to the common type of the args. */ - int common = 0; - - /* If an error was already reported for one of the arguments, - avoid reporting another error. */ - - if (code0 == ERROR_MARK || code1 == ERROR_MARK) - return error_mark_node; - - switch (code) - { - case PLUS_EXPR: - /* Handle the pointer + int case. */ - if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE) - return pointer_int_sum (PLUS_EXPR, op0, op1); - else if (code1 == POINTER_TYPE && code0 == INTEGER_TYPE) - return pointer_int_sum (PLUS_EXPR, op1, op0); - else - common = 1; - break; - - case MINUS_EXPR: - /* Subtraction of two similar pointers. - We must subtract them as integers, then divide by object size. */ - if (code0 == POINTER_TYPE && code1 == POINTER_TYPE - && comp_target_types (dt0, dt1)) - return pointer_diff (op0, op1); - /* Handle pointer minus int. Just like pointer plus int. */ - else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE) - return pointer_int_sum (MINUS_EXPR, op0, op1); - else - common = 1; - break; - - case MULT_EXPR: - common = 1; - break; - - case TRUNC_DIV_EXPR: - case CEIL_DIV_EXPR: - case FLOOR_DIV_EXPR: - case ROUND_DIV_EXPR: - case EXACT_DIV_EXPR: - if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE) - && (code1 == INTEGER_TYPE || code1 == REAL_TYPE)) - { - if (!(code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)) - resultcode = RDIV_EXPR; - else - shorten = 1; - common = 1; - } - break; - - case BIT_AND_EXPR: - case BIT_ANDTC_EXPR: - case BIT_IOR_EXPR: - case BIT_XOR_EXPR: - if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE) - shorten = -1; - /* If one operand is a constant, and the other is a short type - that has been converted to an int, - really do the work in the short type and then convert the - result to int. If we are lucky, the constant will be 0 or 1 - in the short type, making the entire operation go away. */ - if (TREE_CODE (op0) == INTEGER_CST - && TREE_CODE (op1) == NOP_EXPR - && TYPE_PRECISION (dt1) > TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (op1, 0))) - && TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (op1, 0)))) - { - final_type = result_type; - op1 = TREE_OPERAND (op1, 0); - result_type = TREE_TYPE (op1); - } - if (TREE_CODE (op1) == INTEGER_CST - && TREE_CODE (op0) == NOP_EXPR - && TYPE_PRECISION (dt0) > TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (op0, 0))) - && TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (op0, 0)))) - { - final_type = result_type; - op0 = TREE_OPERAND (op0, 0); - result_type = TREE_TYPE (op0); - } - break; - - case TRUNC_MOD_EXPR: - if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE) - shorten = 1; - break; - - case TRUTH_ANDIF_EXPR: - case TRUTH_ORIF_EXPR: - case TRUTH_AND_EXPR: - case TRUTH_OR_EXPR: - if ((code0 == INTEGER_TYPE || code0 == POINTER_TYPE || code0 == REAL_TYPE) - && (code1 == INTEGER_TYPE || code1 == POINTER_TYPE || code1 == REAL_TYPE)) - { - /* Result of these operations is always an int, - but that does not mean the operands should be - converted to ints! */ - result_type = integer_type_node; - op0 = truthvalue_conversion (op0); - op1 = truthvalue_conversion (op1); - converted = 1; - } - break; - - /* Shift operations: result has same type as first operand; - always convert second operand to int. - Also set SHORT_SHIFT if shifting rightward. */ - - case RSHIFT_EXPR: - if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE) - { - result_type = dt0; - if (TREE_CODE (op1) == INTEGER_CST - && TREE_INT_CST_LOW (op1) > 0) - short_shift = 1; - /* Convert the shift-count to an integer, regardless of - size of value being shifted. */ - if (TREE_TYPE (op1) != integer_type_node) - op1 = convert (integer_type_node, op1); - } - break; - - case LSHIFT_EXPR: - if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE) - { - result_type = dt0; - if (TREE_CODE (op1) == INTEGER_CST - && TREE_INT_CST_LOW (op1) < 0) - short_shift = 1; - /* Convert the shift-count to an integer, regardless of - size of value being shifted. */ - if (TREE_TYPE (op1) != integer_type_node) - op1 = convert (integer_type_node, op1); - } - break; - - case RROTATE_EXPR: - case LROTATE_EXPR: - if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE) - { - result_type = dt0; - /* Convert the shift-count to an integer, regardless of - size of value being shifted. */ - if (TREE_TYPE (op1) != integer_type_node) - op1 = convert (integer_type_node, op1); - } - break; - - case EQ_EXPR: - case NE_EXPR: - /* Result of comparison is always int, - but don't convert the args to int! */ - result_type = integer_type_node; - converted = 1; - if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE) - && (code1 == INTEGER_TYPE || code1 == REAL_TYPE)) - short_compare = 1; - else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE) - { - register tree tt0 = TREE_TYPE (dt0); - register tree tt1 = TREE_TYPE (dt1); - /* Anything compares with void *. void * compares with anything. - Otherwise, the targets must be the same. */ - if (comp_target_types (dt0, dt1)) - ; - else if (TYPE_MAIN_VARIANT (tt0) == void_type_node) - { - if (pedantic && TREE_CODE (tt1) == FUNCTION_TYPE) - warning ("ANSI C forbids comparison of `void *' with function pointer"); - } - else if (TYPE_MAIN_VARIANT (tt1) == void_type_node) - { - if (pedantic && TREE_CODE (tt0) == FUNCTION_TYPE) - warning ("ANSI C forbids comparison of `void *' with function pointer"); - } - else - warning ("comparison of distinct pointer types lacks a cast"); - } - else if (code0 == POINTER_TYPE && TREE_CODE (op1) == INTEGER_CST - && integer_zerop (op1)) - op1 = null_pointer_node; - else if (code1 == POINTER_TYPE && TREE_CODE (op0) == INTEGER_CST - && integer_zerop (op0)) - op0 = null_pointer_node; - else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE) - { - if (! flag_traditional) - warning ("comparison between pointer and integer"); - op1 = convert (TREE_TYPE (op0), op1); - } - else if (code0 == INTEGER_TYPE && code1 == POINTER_TYPE) - { - if (! flag_traditional) - warning ("comparison between pointer and integer"); - op0 = convert (TREE_TYPE (op1), op0); - } - else - /* If args are not valid, clear out RESULT_TYPE - to cause an error message later. */ - result_type = 0; - break; - - case MAX_EXPR: - case MIN_EXPR: - if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE) - && (code1 == INTEGER_TYPE || code1 == REAL_TYPE)) - shorten = 1; - else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE) - { - if (! comp_target_types (dt0, dt1)) - warning ("comparison of distinct pointer types lacks a cast"); - else if (pedantic - && TREE_CODE (TREE_TYPE (dt0)) == FUNCTION_TYPE) - warning ("ANSI C forbids ordered comparisons of pointers to functions"); - result_type = commontype (dt0, dt1); - } - break; - - case LE_EXPR: - case GE_EXPR: - case LT_EXPR: - case GT_EXPR: - if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE) - && (code1 == INTEGER_TYPE || code1 == REAL_TYPE)) - short_compare = 1; - else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE) - { - if (! comp_target_types (dt0, dt1)) - warning ("comparison of distinct pointer types lacks a cast"); - else if (pedantic - && TREE_CODE (TREE_TYPE (dt0)) == FUNCTION_TYPE) - warning ("ANSI C forbids ordered comparisons of pointers to functions"); - result_type = integer_type_node; - } - else if (code0 == POINTER_TYPE && TREE_CODE (op1) == INTEGER_CST - && integer_zerop (op1)) - { - result_type = integer_type_node; - op1 = null_pointer_node; - if (! flag_traditional) - warning ("ordered comparison of pointer with integer zero"); - } - else if (code1 == POINTER_TYPE && TREE_CODE (op0) == INTEGER_CST - && integer_zerop (op0)) - { - result_type = integer_type_node; - op0 = null_pointer_node; - if (pedantic) - warning ("ordered comparison of pointer with integer zero"); - } - else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE) - { - result_type = integer_type_node; - if (! flag_traditional) - warning ("comparison between pointer and integer"); - op1 = convert (TREE_TYPE (op0), op1); - } - else if (code0 == INTEGER_TYPE && code1 == POINTER_TYPE) - { - result_type = integer_type_node; - if (! flag_traditional) - warning ("comparison between pointer and integer"); - op0 = convert (TREE_TYPE (op1), op0); - } - converted = 1; - break; - } - - if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE) - && (code1 == INTEGER_TYPE || code1 == REAL_TYPE)) - { - if (shorten || common || short_compare) - result_type = commontype (dt0, dt1); - - /* For certain operations (which identify themselves by shorten != 0) - if both args were extended from the same smaller type, - do the arithmetic in that type and then extend. - - shorten !=0 and !=1 indicates a bitwise operation. - For them, this optimization is safe only if - both args are zero-extended or both are sign-extended. - Otherwise, we might change the result. - Eg, (short)-1 | (unsigned short)-1 is (int)-1 - but calculated in (unsigned short) it would be (unsigned short)-1. */ - - if (shorten) - { - int unsigned0, unsigned1; - tree arg0 = get_narrower (op0, &unsigned0); - tree arg1 = get_narrower (op1, &unsigned1); - /* UNS is 1 if the operation to be done is an unsigned one. */ - int uns = TREE_UNSIGNED (result_type); - tree type; - - final_type = result_type; - - /* Handle the case that OP0 does not *contain* a conversion - but it *requires* conversion to FINAL_TYPE. */ - - if (op0 == arg0 && TREE_TYPE (op0) != final_type) - unsigned0 = TREE_UNSIGNED (TREE_TYPE (op0)); - if (op1 == arg1 && TREE_TYPE (op1) != final_type) - unsigned1 = TREE_UNSIGNED (TREE_TYPE (op1)); - - /* Now UNSIGNED0 is 1 if ARG0 zero-extends to FINAL_TYPE. */ - - /* For bitwise operations, signedness of nominal type - does not matter. Consider only how operands were extended. */ - if (shorten == -1) - uns = unsigned0; - - /* Note that in all three cases below we refrain from optimizing - an unsigned operation on sign-extended args. - That would not be valid. */ - - /* Both args variable: if both extended in same way - from same width, do it in that width. - Do it unsigned if args were zero-extended. */ - if ((TYPE_PRECISION (TREE_TYPE (arg0)) - < TYPE_PRECISION (result_type)) - && (TYPE_PRECISION (TREE_TYPE (arg1)) - == TYPE_PRECISION (TREE_TYPE (arg0))) - && unsigned0 == unsigned1 - && (unsigned0 || !uns)) - result_type - = signed_or_unsigned_type (unsigned0, - commontype (TREE_TYPE (arg0), TREE_TYPE (arg1))); - else if (TREE_CODE (arg0) == INTEGER_CST - && (unsigned1 || !uns) - && (TYPE_PRECISION (TREE_TYPE (arg1)) - < TYPE_PRECISION (result_type)) - && (type = signed_or_unsigned_type (unsigned1, - TREE_TYPE (arg1)), - int_fits_type_p (arg0, type))) - result_type = type; - else if (TREE_CODE (arg1) == INTEGER_CST - && (unsigned0 || !uns) - && (TYPE_PRECISION (TREE_TYPE (arg0)) - < TYPE_PRECISION (result_type)) - && (type = signed_or_unsigned_type (unsigned0, - TREE_TYPE (arg0)), - int_fits_type_p (arg1, type))) - result_type = type; - } - - /* Shifts can be shortened if shifting right. */ - - if (short_shift) - { - int unsigned_arg; - tree arg0 = get_narrower (op0, &unsigned_arg); - - final_type = result_type; - - if (arg0 == op0 && final_type == TREE_TYPE (op0)) - unsigned_arg = TREE_UNSIGNED (TREE_TYPE (op0)); - - if (TYPE_PRECISION (TREE_TYPE (arg0)) < TYPE_PRECISION (result_type) - /* If arg is sign-extended and then unsigned-shifted, - we can simulate this with a signed shift in arg's type - only if the extended result is at least twice as wide - as the arg. Otherwise, the shift could use up all the - ones made by sign-extension and bring in zeros. - We can't optimize that case at all, but in most machines - it never happens because available widths are 2**N. */ - && (!TREE_UNSIGNED (final_type) - || unsigned_arg - || 2 * TYPE_PRECISION (TREE_TYPE (arg0)) <= TYPE_PRECISION (result_type))) - { - /* Do an unsigned shift if the operand was zero-extended. */ - result_type - = signed_or_unsigned_type (unsigned_arg, - TREE_TYPE (arg0)); - /* Convert value-to-be-shifted to that type. */ - if (TREE_TYPE (op0) != result_type) - op0 = convert (result_type, op0); - converted = 1; - } - } - - /* Comparison operations are shortened too but differently. - They identify themselves by setting short_compare = 1. */ - - if (short_compare) - { - /* Don't write &op0, etc., because that would prevent op0 - from being kept in a register. - Instead, make copies of the our local variables and - pass the copies by reference, then copy them back afterward. */ - tree xop0 = op0, xop1 = op1, xresult_type = result_type; - enum tree_code xresultcode = resultcode; - tree val - = shorten_compare (&xop0, &xop1, &xresult_type, &xresultcode); - if (val != 0) - return val; - op0 = xop0, op1 = xop1, result_type = xresult_type; - resultcode = xresultcode; - } - } - - /* At this point, RESULT_TYPE must be nonzero to avoid an error message. - If CONVERTED is zero, both args will be converted to type RESULT_TYPE. - Then the expression will be built. - It will be given type FINAL_TYPE if that is nonzero; - otherwise, it will be given type RESULT_TYPE. */ - - if (!result_type) - { - binary_op_error (error_code); - return error_mark_node; - } - - if (! converted) - { - if (TREE_TYPE (op0) != result_type) - op0 = convert (result_type, op0); - if (TREE_TYPE (op1) != result_type) - op1 = convert (result_type, op1); - } - - { - register tree result = build (resultcode, result_type, op0, op1); - register tree folded; - - folded = fold (result); - if (folded == result) - TREE_LITERAL (folded) = TREE_LITERAL (op0) & TREE_LITERAL (op1); - if (final_type != 0) - return convert (final_type, folded); - return folded; - } -} - -/* Return a tree for the sum or difference (RESULTCODE says which) - of pointer PTROP and integer INTOP. */ - -static tree -pointer_int_sum (resultcode, ptrop, intop) - enum tree_code resultcode; - register tree ptrop, intop; -{ - tree size_exp; - - register tree result; - register tree folded; - - /* The result is a pointer of the same type that is being added. */ - - register tree result_type = datatype (ptrop); - - if (TREE_CODE (TREE_TYPE (result_type)) == VOID_TYPE) - { - if (pedantic || warn_pointer_arith) - warning ("pointer of type `void *' used in arithmetic"); - size_exp = integer_one_node; - } - else if (TREE_CODE (TREE_TYPE (result_type)) == FUNCTION_TYPE) - { - if (pedantic || warn_pointer_arith) - warning ("pointer to a function used in arithmetic"); - size_exp = integer_one_node; - } - else - size_exp = c_sizeof (TREE_TYPE (result_type)); - - /* If what we are about to multiply by the size of the elements - contains a constant term, apply distributive law - and multiply that constant term separately. - This helps produce common subexpressions. */ - - if ((TREE_CODE (intop) == PLUS_EXPR || TREE_CODE (intop) == MINUS_EXPR) - && ! TREE_LITERAL (intop) - && TREE_LITERAL (TREE_OPERAND (intop, 1)) - && TREE_LITERAL (size_exp)) - { - enum tree_code subcode = resultcode; - if (TREE_CODE (intop) == MINUS_EXPR) - subcode = (subcode == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR); - ptrop = build_binary_op (subcode, ptrop, TREE_OPERAND (intop, 1)); - intop = TREE_OPERAND (intop, 0); - } - - /* Convert the integer argument to a type the same size as a pointer - so the multiply won't overflow spuriously. */ - - if (TYPE_PRECISION (TREE_TYPE (intop)) != POINTER_SIZE) - intop = convert (type_for_size (POINTER_SIZE, 0), intop); - - /* Replace the integer argument - with a suitable product by the object size. */ - - intop = build_binary_op (MULT_EXPR, intop, size_exp); - - /* Create the sum or difference. */ - - result = build (resultcode, result_type, ptrop, intop); - - folded = fold (result); - if (folded == result) - TREE_LITERAL (folded) = TREE_LITERAL (ptrop) & TREE_LITERAL (intop); - return folded; -} - -/* Return a tree for the difference of pointers OP0 and OP1. - The resulting tree has type int. */ - -static tree -pointer_diff (op0, op1) - register tree op0, op1; -{ - tree dt0 = datatype (op0); - enum tree_code resultcode; - register tree result, folded; - tree restype = type_for_size (POINTER_SIZE, 0); - - if (pedantic) - { - if (TREE_CODE (TREE_TYPE (dt0)) == VOID_TYPE) - warning ("pointer of type `void *' used in subtraction"); - if (TREE_CODE (TREE_TYPE (dt0)) == FUNCTION_TYPE) - warning ("pointer to a function used in subtraction"); - } - - /* First do the subtraction as integers; - then drop through to build the divide operator. */ - - op0 = build_binary_op (MINUS_EXPR, - convert (restype, op0), convert (restype, op1)); - op1 = c_sizeof_nowarn (TREE_TYPE (dt0)); - - /* Create the sum or difference. */ - - result = build (EXACT_DIV_EXPR, restype, op0, op1); - - folded = fold (result); - if (folded == result) - TREE_LITERAL (folded) = TREE_LITERAL (op0) & TREE_LITERAL (op1); - return folded; -} - -/* Print an error message for invalid operands to arith operation CODE. - NOP_EXPR is used as a special case (see truthvalue_conversion). */ - -static void -binary_op_error (code) - enum tree_code code; -{ - register char *opname; - switch (code) - { - case NOP_EXPR: - error ("invalid truth-value expression"); - return; - - case PLUS_EXPR: - opname = "+"; break; - case MINUS_EXPR: - opname = "-"; break; - case MULT_EXPR: - opname = "*"; break; - case MAX_EXPR: - opname = "max"; break; - case MIN_EXPR: - opname = "min"; break; - case EQ_EXPR: - opname = "=="; break; - case NE_EXPR: - opname = "!="; break; - case LE_EXPR: - opname = "<="; break; - case GE_EXPR: - opname = ">="; break; - case LT_EXPR: - opname = "<"; break; - case GT_EXPR: - opname = ">"; break; - case LSHIFT_EXPR: - opname = "<<"; break; - case RSHIFT_EXPR: - opname = ">>"; break; - case TRUNC_MOD_EXPR: - opname = "%"; break; - case TRUNC_DIV_EXPR: - opname = "/"; break; - case BIT_AND_EXPR: - opname = "&"; break; - case BIT_IOR_EXPR: - opname = "|"; break; - case TRUTH_ANDIF_EXPR: - opname = "&&"; break; - case TRUTH_ORIF_EXPR: - opname = "||"; break; - case BIT_XOR_EXPR: - opname = "^"; break; - } - error ("invalid operands to binary %s", opname); -} - -/* Subroutine of build_binary_op_nodefault, used for comparison operations. - See if the operands have both been converted from subword integer types - and, if so, perhaps change them both back to their original type. - - The arguments of this function are all pointers to local variables - of build_binary_op_nodefault: OP0_PTR is &OP0, OP1_PTR is &OP1, - RESTYPE_PTR is &RESULT_TYPE and RESCODE_PTR is &RESULTCODE. - - If this function returns nonzero, it means that the comparison has - a constant value. What this function returns is an expression for - that value. */ - -static tree -shorten_compare (op0_ptr, op1_ptr, restype_ptr, rescode_ptr) - tree *op0_ptr, *op1_ptr; - tree *restype_ptr; - enum tree_code *rescode_ptr; -{ - register tree type; - tree op0 = *op0_ptr; - tree op1 = *op1_ptr; - int unsignedp0, unsignedp1; - int real1, real2; - tree primop0, primop1; - enum tree_code code = *rescode_ptr; - - /* Throw away any conversions to wider types - already present in the operands. */ - - primop0 = get_narrower (op0, &unsignedp0); - primop1 = get_narrower (op1, &unsignedp1); - - /* Handle the case that OP0 does not *contain* a conversion - but it *requires* conversion to FINAL_TYPE. */ - - if (op0 == primop0 && TREE_TYPE (op0) != *restype_ptr) - unsignedp0 = TREE_UNSIGNED (TREE_TYPE (op0)); - if (op1 == primop1 && TREE_TYPE (op1) != *restype_ptr) - unsignedp1 = TREE_UNSIGNED (TREE_TYPE (op1)); - - /* If one of the operands must be floated, we cannot optimize. */ - real1 = TREE_CODE (TREE_TYPE (primop0)) == REAL_TYPE; - real2 = TREE_CODE (TREE_TYPE (primop1)) == REAL_TYPE; - - /* If first arg is constant, swap the args (changing operation - so value is preserved), for canonicalization. */ - - if (TREE_LITERAL (primop0)) - { - register tree tem = primop0; - register int temi = unsignedp0; - primop0 = primop1; - primop1 = tem; - tem = op0; - op0 = op1; - op1 = tem; - *op0_ptr = op0; - *op1_ptr = op1; - unsignedp0 = unsignedp1; - unsignedp1 = temi; - temi = real1; - real1 = real2; - real2 = temi; - - switch (code) - { - case LT_EXPR: - code = GT_EXPR; - break; - case GT_EXPR: - code = LT_EXPR; - break; - case LE_EXPR: - code = GE_EXPR; - break; - case GE_EXPR: - code = LE_EXPR; - break; - } - *rescode_ptr = code; - } - - /* If comparing an integer against a constant more bits wide, - maybe we can deduce a value of 1 or 0 independent of the data. - Or else truncate the constant now - rather than extend the variable at run time. - - This is only interesting if the constant is the wider arg. - Also, it is not safe if the constant is unsigned and the - variable arg is signed, since in this case the variable - would be sign-extended and then regarded as unsigned. - Our technique fails in this case because the lowest/highest - possible unsigned results don't follow naturally from the - lowest/highest possible values of the variable operand. - For just EQ_EXPR and NE_EXPR there is another technique that - could be used: see if the constant can be faithfully represented - in the other operand's type, by truncating it and reextending it - and see if that preserves the constant's value. */ - - if (!real1 && !real2 - && TREE_CODE (primop1) == INTEGER_CST - && TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr)) - { - int min_gt, max_gt, min_lt, max_lt; - tree maxval, minval; - /* 1 if comparison is nominally unsigned. */ - int unsignedp = TREE_UNSIGNED (*restype_ptr); - tree val; - - type = signed_or_unsigned_type (unsignedp0, TREE_TYPE (primop0)); - - maxval = TYPE_MAX_VALUE (type); - minval = TYPE_MIN_VALUE (type); - - if (unsignedp && !unsignedp0) - *restype_ptr = signed_type (*restype_ptr); - - if (TREE_TYPE (primop1) != *restype_ptr) - primop1 = convert (*restype_ptr, primop1); - if (type != *restype_ptr) - { - minval = convert (*restype_ptr, minval); - maxval = convert (*restype_ptr, maxval); - } - - if (unsignedp && unsignedp0) - { - min_gt = INT_CST_LT_UNSIGNED (primop1, minval); - max_gt = INT_CST_LT_UNSIGNED (primop1, maxval); - min_lt = INT_CST_LT_UNSIGNED (minval, primop1); - max_lt = INT_CST_LT_UNSIGNED (maxval, primop1); - } - else - { - min_gt = INT_CST_LT (primop1, minval); - max_gt = INT_CST_LT (primop1, maxval); - min_lt = INT_CST_LT (minval, primop1); - max_lt = INT_CST_LT (maxval, primop1); - } - - val = 0; - /* This used to be a switch, but Genix compiler can't handle that. */ - if (code == NE_EXPR) - { - if (max_lt || min_gt) - val = integer_one_node; - } - else if (code == EQ_EXPR) - { - if (max_lt || min_gt) - val = integer_zero_node; - } - else if (code == LT_EXPR) - { - if (max_lt) - val = integer_one_node; - if (!min_lt) - val = integer_zero_node; - } - else if (code == GT_EXPR) - { - if (min_gt) - val = integer_one_node; - if (!max_gt) - val = integer_zero_node; - } - else if (code == LE_EXPR) - { - if (!max_gt) - val = integer_one_node; - if (min_gt) - val = integer_zero_node; - } - else if (code == GE_EXPR) - { - if (!min_lt) - val = integer_one_node; - if (max_lt) - val = integer_zero_node; - } - - /* If primop0 was sign-extended and unsigned comparison specd, - we did a signed comparison above using the signed type bounds. - But the comparison we output must be unsigned. - - Also, for inequalities, VAL is no good; but if the signed - comparison had *any* fixed result, it follows that the - unsigned comparison just tests the sign in reverse - (positive values are LE, negative ones GE). - So we can generate an unsigned comparison - against an extreme value of the signed type. */ - - if (unsignedp && !unsignedp0) - { - if (val != 0) - switch (code) - { - case LT_EXPR: - case GE_EXPR: - primop1 = TYPE_MIN_VALUE (type); - val = 0; - break; - - case LE_EXPR: - case GT_EXPR: - primop1 = TYPE_MAX_VALUE (type); - val = 0; - break; - } - type = unsigned_type (type); - } - - if (max_lt && !unsignedp0) - { - /* This is the case of (char)x >?< 0x80, which people used to use - expecting old C compilers to change the 0x80 into -0x80. */ - if (val == integer_zero_node) - warning ("comparison is always 0 due to limited range of data type"); - if (val == integer_one_node) - warning ("comparison is always 1 due to limited range of data type"); - } - - if (val != 0) - { - /* Don't forget to evaluate PRIMOP0 if it has side effects. */ - if (TREE_VOLATILE (primop0)) - return build (COMPOUND_EXPR, TREE_TYPE (val), primop0, val); - return val; - } - - /* Value is not predetermined, but do the comparison - in the type of the operand that is not constant. - TYPE is already properly set. */ - } - else if (real1 && real2 - && TYPE_PRECISION (TREE_TYPE (primop0)) == TYPE_PRECISION (TREE_TYPE (primop1))) - type = TREE_TYPE (primop0); - - /* If args' natural types are both narrower than nominal type - and both extend in the same manner, compare them - in the type of the wider arg. - Otherwise must actually extend both to the nominal - common type lest different ways of extending - alter the result. - (eg, (short)-1 == (unsigned short)-1 should be 0.) */ - - else if (unsignedp0 == unsignedp1 && real1 == real2 - && TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr) - && TYPE_PRECISION (TREE_TYPE (primop1)) < TYPE_PRECISION (*restype_ptr)) - { - type = commontype (TREE_TYPE (primop0), TREE_TYPE (primop1)); - type = signed_or_unsigned_type (unsignedp0 - || TREE_UNSIGNED (*restype_ptr), - type); - /* Make sure shorter operand is extended the right way - to match the longer operand. */ - primop0 = convert (signed_or_unsigned_type (unsignedp0, TREE_TYPE (primop0)), - primop0); - primop1 = convert (signed_or_unsigned_type (unsignedp1, TREE_TYPE (primop1)), - primop1); - } - else - { - /* Here we must do the comparison on the nominal type - using the args exactly as we received them. */ - type = *restype_ptr; - primop0 = op0; - primop1 = op1; - } - - *op0_ptr = convert (type, primop0); - *op1_ptr = convert (type, primop1); - - *restype_ptr = integer_type_node; - - return 0; -} - -/* Construct and perhaps optimize a tree representation - for a unary operation. CODE, a tree_code, specifies the operation - and XARG is the operand. NOCONVERT nonzero suppresses - the default promotions (such as from short to int). */ - -tree -build_unary_op (code, xarg, noconvert) - enum tree_code code; - tree xarg; - int noconvert; -{ - /* No default_conversion here. It causes trouble for ADDR_EXPR. */ - register tree arg = xarg; - register tree argtype = 0; - register enum tree_code typecode = TREE_CODE (TREE_TYPE (arg)); - char *errstring = NULL; - tree val; - - if (typecode == ERROR_MARK) - return error_mark_node; - if (typecode == ENUMERAL_TYPE) - typecode = INTEGER_TYPE; - - switch (code) - { - case CONVERT_EXPR: - /* This is used for unary plus, because a CONVERT_EXPR - is enough to prevent anybody from looking inside for - associativity, but won't generate any code. */ - if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE)) - errstring = "wrong type argument to unary plus"; - else if (!noconvert) - arg = default_conversion (arg); - break; - - case NEGATE_EXPR: - if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE)) - errstring = "wrong type argument to unary minus"; - else if (!noconvert) - arg = default_conversion (arg); - break; - - case BIT_NOT_EXPR: - if (typecode != INTEGER_TYPE) - errstring = "wrong type argument to bit-complement"; - else if (!noconvert) - arg = default_conversion (arg); - break; - - case ABS_EXPR: - if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE)) - errstring = "wrong type argument to abs"; - else if (!noconvert) - arg = default_conversion (arg); - break; - - case TRUTH_NOT_EXPR: - if (typecode != INTEGER_TYPE - && typecode != REAL_TYPE && typecode != POINTER_TYPE - /* This will convert to a pointer. */ - && typecode != ARRAY_TYPE && typecode != FUNCTION_TYPE) - { - errstring = "wrong type argument to unary exclamation mark"; - break; - } - arg = truthvalue_conversion (arg); - val = invert_truthvalue (arg); - if (val) return val; - break; - - case NOP_EXPR: - break; - - case PREINCREMENT_EXPR: - case POSTINCREMENT_EXPR: - case PREDECREMENT_EXPR: - case POSTDECREMENT_EXPR: - /* Handle complex lvalues (when permitted) - by reduction to simpler cases. */ - - val = unary_complex_lvalue (code, arg); - if (val != 0) - return val; - - /* Report invalid types. */ - - if (typecode != POINTER_TYPE - && typecode != INTEGER_TYPE && typecode != REAL_TYPE) - { - if (code == PREINCREMENT_EXPR || code == POSTINCREMENT_EXPR) - errstring ="wrong type argument to increment"; - else - errstring ="wrong type argument to decrement"; - break; - } - - /* Report something read-only. */ - - if (TREE_READONLY (arg)) - readonly_warning (arg, - ((code == PREINCREMENT_EXPR - || code == POSTINCREMENT_EXPR) - ? "increment" : "decrement")); - - { - register tree inc; - tree result_type = TREE_TYPE (arg); - - arg = get_unwidened (arg, 0); - argtype = TREE_TYPE (arg); - - /* Compute the increment. */ - - if (typecode == POINTER_TYPE) - { - if (pedantic - && (TREE_CODE (TREE_TYPE (result_type)) == FUNCTION_TYPE - || TREE_CODE (TREE_TYPE (result_type)) == VOID_TYPE)) - warning ("wrong type argument to %s", - ((code == PREINCREMENT_EXPR - || code == POSTINCREMENT_EXPR) - ? "increment" : "decrement")); - inc = c_sizeof_nowarn (TREE_TYPE (result_type)); - } - else - inc = integer_one_node; - - inc = convert (argtype, inc); - - /* Handle incrementing a cast-expression. */ - - if (!pedantic) - switch (TREE_CODE (arg)) - { - case NOP_EXPR: - case CONVERT_EXPR: - case FLOAT_EXPR: - case FIX_TRUNC_EXPR: - case FIX_FLOOR_EXPR: - case FIX_ROUND_EXPR: - case FIX_CEIL_EXPR: - { - tree incremented, modify, value; - arg = stabilize_reference (arg); - if (code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR) - value = arg; - else - value = save_expr (arg); - incremented = build (((code == PREINCREMENT_EXPR - || code == POSTINCREMENT_EXPR) - ? PLUS_EXPR : MINUS_EXPR), - argtype, value, inc); - TREE_VOLATILE (incremented) = 1; - modify = build_modify_expr (arg, NOP_EXPR, incremented); - return build (COMPOUND_EXPR, TREE_TYPE (arg), modify, value); - } - } - - /* Complain about anything else that is not a true lvalue. */ - if (!lvalue_or_else (arg, ((code == PREINCREMENT_EXPR - || code == POSTINCREMENT_EXPR) - ? "increment" : "decrement"))) - return error_mark_node; - - val = build (code, TREE_TYPE (arg), arg, inc); - TREE_VOLATILE (val) = 1; - return convert (result_type, val); - } - - case ADDR_EXPR: - /* Note that this operation never does default_conversion - regardless of NOCONVERT. */ - - /* Let &* cancel out to simplify resulting code. */ - if (TREE_CODE (arg) == INDIRECT_REF) - { - /* Don't let this be an lvalue. */ - if (lvalue_p (TREE_OPERAND (arg, 0))) - return build (NOP_EXPR, TREE_TYPE (TREE_OPERAND (arg, 0)), - TREE_OPERAND (arg, 0)); - return TREE_OPERAND (arg, 0); - } - - /* For &x[y], return x+y */ - if (TREE_CODE (arg) == ARRAY_REF) - { - if (mark_addressable (TREE_OPERAND (arg, 0)) == 0) - return error_mark_node; - return build_binary_op (PLUS_EXPR, TREE_OPERAND (arg, 0), - TREE_OPERAND (arg, 1)); - } - - /* Handle complex lvalues (when permitted) - by reduction to simpler cases. */ - val = unary_complex_lvalue (code, arg); - if (val != 0) - return val; - - /* Address of a cast is just a cast of the address - of the operand of the cast. */ - switch (TREE_CODE (arg)) - { - case NOP_EXPR: - case CONVERT_EXPR: - case FLOAT_EXPR: - case FIX_TRUNC_EXPR: - case FIX_FLOOR_EXPR: - case FIX_ROUND_EXPR: - case FIX_CEIL_EXPR: - if (pedantic) - warning ("ANSI C forbids the address of a cast expression"); - return convert (build_pointer_type (TREE_TYPE (arg)), - build_unary_op (ADDR_EXPR, TREE_OPERAND (arg, 0), - 0)); - } - - /* Allow the address of a constructor if all the elements - are constant. */ - if (TREE_CODE (arg) == CONSTRUCTOR && TREE_LITERAL (arg)) - ; - /* Anything not already handled and not a true memory reference - is an error. */ - else if (typecode != FUNCTION_TYPE && !lvalue_or_else (arg, "unary `&'")) - return error_mark_node; - - /* Ordinary case; arg is a COMPONENT_REF or a decl. */ - argtype = TREE_TYPE (arg); - if (TREE_READONLY (arg) || TREE_THIS_VOLATILE (arg)) - argtype = c_build_type_variant (argtype, - TREE_READONLY (arg), - TREE_THIS_VOLATILE (arg)); - - argtype = build_pointer_type (argtype); - - if (mark_addressable (arg) == 0) - return error_mark_node; - - { - tree addr; - - if (TREE_CODE (arg) == COMPONENT_REF) - { - tree field = TREE_OPERAND (arg, 1); - - addr = build_unary_op (ADDR_EXPR, TREE_OPERAND (arg, 0), 0); - - if (TREE_PACKED (field)) - { - error ("attempt to take address of bit-field structure member `%s'", - IDENTIFIER_POINTER (DECL_NAME (field))); - return error_mark_node; - } - - addr = convert (argtype, addr); - - if (DECL_OFFSET (field) != 0) - { - tree offset = build_int_2 ((DECL_OFFSET (field) - / BITS_PER_UNIT), - 0); - TREE_TYPE (offset) = argtype; - addr = fold (build (PLUS_EXPR, argtype, addr, offset)); - } - } - else - addr = build (code, argtype, arg); - - /* Address of a static or external variable or - function counts as a constant */ - TREE_LITERAL (addr) = staticp (arg); - return addr; - } - } - - if (!errstring) - { - if (argtype == 0) - argtype = TREE_TYPE (arg); - return fold (build (code, argtype, arg)); - } - - error (errstring); - return error_mark_node; -} - -/* If CONVERSIONS is a conversion expression or a nested sequence of such, - convert ARG with the same conversions in the same order - and return the result. */ - -static tree -convert_sequence (conversions, arg) - tree conversions; - tree arg; -{ - switch (TREE_CODE (conversions)) - { - case NOP_EXPR: - case CONVERT_EXPR: - case FLOAT_EXPR: - case FIX_TRUNC_EXPR: - case FIX_FLOOR_EXPR: - case FIX_ROUND_EXPR: - case FIX_CEIL_EXPR: - return convert (TREE_TYPE (conversions), - convert_sequence (TREE_OPERAND (conversions, 0), - arg)); - - default: - return arg; - } -} - -/* Apply unary lvalue-demanding operator CODE to the expression ARG - for certain kinds of expressions which are not really lvalues - but which we can accept as lvalues. - - If ARG is not a kind of expression we can handle, return zero. */ - -static tree -unary_complex_lvalue (code, arg) - enum tree_code code; - tree arg; -{ - if (pedantic) - return 0; - - /* Handle (a, b) used as an "lvalue". */ - if (TREE_CODE (arg) == COMPOUND_EXPR) - { - tree real_result = build_unary_op (code, TREE_OPERAND (arg, 1), 0); - return build (COMPOUND_EXPR, TREE_TYPE (real_result), - TREE_OPERAND (arg, 0), real_result); - } - - /* Handle (a ? b : c) used as an "lvalue". */ - if (TREE_CODE (arg) == COND_EXPR) - return (build_conditional_expr - (TREE_OPERAND (arg, 0), - build_unary_op (code, TREE_OPERAND (arg, 1), 0), - build_unary_op (code, TREE_OPERAND (arg, 2), 0))); - - return 0; -} - -/* Warn about storing in something that is `const'. */ - -void -readonly_warning (arg, string) - tree arg; - char *string; -{ - char buf[80]; - strcpy (buf, string); - - if (TREE_CODE (arg) == COMPONENT_REF) - { - if (TREE_READONLY (TREE_OPERAND (arg, 0))) - readonly_warning (TREE_OPERAND (arg, 0), string); - else - { - strcat (buf, " of read-only member `%s'"); - warning (buf, IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND (arg, 1)))); - } - } - else if (TREE_CODE (arg) == VAR_DECL) - { - strcat (buf, " of read-only variable `%s'"); - warning (buf, IDENTIFIER_POINTER (DECL_NAME (arg))); - } - else - { - warning ("%s of read-only location", buf); - } -} - -/* Prepare expr to be an argument of a TRUTH_NOT_EXPR, - or validate its data type for an `if' or `while' statement or ?..: exp. - - This preparation consists of taking the ordinary - representation of an expression expr and producing a valid tree - boolean expression describing whether expr is nonzero. We could - simply always do build_binary_op (NE_EXPR, expr, integer_zero_node), - but we optimize comparisons, &&, ||, and ! */ - -tree -truthvalue_conversion (expr) - tree expr; -{ - register enum tree_code form; - - /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. - Strip such NOP_EXPRs, since EXPR is being used in non-lvalue context. */ - if (TREE_CODE (expr) == NOP_EXPR - && TREE_TYPE (expr) == TREE_TYPE (TREE_OPERAND (expr, 0))) - expr = TREE_OPERAND (expr, 0); - - form = TREE_CODE (expr); - - if (form == EQ_EXPR && integer_zerop (TREE_OPERAND (expr, 1))) - return build_unary_op (TRUTH_NOT_EXPR, TREE_OPERAND (expr, 0), 0); - - /* A one-bit unsigned bit-field is already acceptable. */ - if (form == COMPONENT_REF - && 1 == TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (expr, 1))) - && 1 == DECL_SIZE_UNIT (TREE_OPERAND (expr, 1)) - && TREE_UNSIGNED (TREE_OPERAND (expr, 1))) - return expr; - - if (form == TRUTH_ANDIF_EXPR || form == TRUTH_ORIF_EXPR - || form == TRUTH_AND_EXPR || form == TRUTH_OR_EXPR - || form == TRUTH_NOT_EXPR - || form == EQ_EXPR || form == NE_EXPR - || form == LE_EXPR || form == GE_EXPR - || form == LT_EXPR || form == GT_EXPR - || form == ERROR_MARK) - return expr; - - /* Unary minus has no effect on whether its argument is nonzero. */ - if (form == NEGATE_EXPR) - return truthvalue_conversion (TREE_OPERAND (expr, 0)); - - /* Distribute the conversion into the arms of a COND_EXPR. */ - if (form == COND_EXPR) - return build (COND_EXPR, integer_type_node, - TREE_OPERAND (expr, 0), - truthvalue_conversion (TREE_OPERAND (expr, 1)), - truthvalue_conversion (TREE_OPERAND (expr, 2))); - - /* Sign-extension and zero-extension has no effect. */ - if (form == NOP_EXPR - && TREE_CODE (TREE_TYPE (expr)) == INTEGER_TYPE - && (TYPE_PRECISION (TREE_TYPE (expr)) - > TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (expr, 0))))) - return truthvalue_conversion (TREE_OPERAND (expr, 0)); - - return build_binary_op_nodefault (NE_EXPR, default_conversion (expr), - integer_zero_node, NOP_EXPR); -} - -/* Return a simplified tree node for the truth-negation of ARG - (perhaps by altering ARG). - If it can't be simplified, return 0. */ - -static tree -invert_truthvalue (arg) - tree arg; -{ - switch (TREE_CODE (arg)) - { - case NE_EXPR: - TREE_SET_CODE (arg, EQ_EXPR); - return arg; - - case EQ_EXPR: - TREE_SET_CODE (arg, NE_EXPR); - return arg; - - case GE_EXPR: - TREE_SET_CODE (arg, LT_EXPR); - return arg; - - case GT_EXPR: - TREE_SET_CODE (arg, LE_EXPR); - return arg; - - case LE_EXPR: - TREE_SET_CODE (arg, GT_EXPR); - return arg; - - case LT_EXPR: - TREE_SET_CODE (arg, GE_EXPR); - return arg; - - case TRUTH_AND_EXPR: - return build (TRUTH_OR_EXPR, TREE_TYPE (arg), - build_unary_op (TRUTH_NOT_EXPR, - TREE_OPERAND (arg, 0), 0), - build_unary_op (TRUTH_NOT_EXPR, - TREE_OPERAND (arg, 1), 0)); - - case TRUTH_OR_EXPR: - return build (TRUTH_AND_EXPR, TREE_TYPE (arg), - build_unary_op (TRUTH_NOT_EXPR, - TREE_OPERAND (arg, 0), 0), - build_unary_op (TRUTH_NOT_EXPR, - TREE_OPERAND (arg, 1), 0)); - - case TRUTH_ANDIF_EXPR: - return build (TRUTH_ORIF_EXPR, TREE_TYPE (arg), - build_unary_op (TRUTH_NOT_EXPR, - TREE_OPERAND (arg, 0), 0), - build_unary_op (TRUTH_NOT_EXPR, - TREE_OPERAND (arg, 1), 0)); - - case TRUTH_ORIF_EXPR: - return build (TRUTH_ANDIF_EXPR, TREE_TYPE (arg), - build_unary_op (TRUTH_NOT_EXPR, - TREE_OPERAND (arg, 0), 0), - build_unary_op (TRUTH_NOT_EXPR, - TREE_OPERAND (arg, 1), 0)); - - case TRUTH_NOT_EXPR: - return TREE_OPERAND (arg, 0); - - case COND_EXPR: - return build (COND_EXPR, TREE_TYPE (arg), TREE_OPERAND (arg, 0), - build_unary_op (TRUTH_NOT_EXPR, TREE_OPERAND (arg, 1), 0), - build_unary_op (TRUTH_NOT_EXPR, TREE_OPERAND (arg, 2), 0)); - } - return 0; -} - -/* Mark EXP saying that we need to be able to take the - address of it; it should not be allocated in a register. - Value is 1 if successful. */ - -int -mark_addressable (exp) - tree exp; -{ - register tree x = exp; - while (1) - switch (TREE_CODE (x)) - { - case ADDR_EXPR: - case COMPONENT_REF: - case ARRAY_REF: - x = TREE_OPERAND (x, 0); - break; - - case VAR_DECL: - case CONST_DECL: - case PARM_DECL: - case RESULT_DECL: - if (TREE_REGDECL (x) && !TREE_ADDRESSABLE (x)) - { - if (TREE_PUBLIC (x)) - { - error ("address of global register variable `%s' requested", - IDENTIFIER_POINTER (DECL_NAME (x))); - return 0; - } - warning ("address of register variable `%s' requested", - IDENTIFIER_POINTER (DECL_NAME (x))); - } - put_var_into_stack (x); - - /* drops in */ - case FUNCTION_DECL: - TREE_ADDRESSABLE (x) = 1; - TREE_ADDRESSABLE (DECL_NAME (x)) = 1; - - default: - return 1; - } -} - -/* Build and return a conditional expression IFEXP ? OP1 : OP2. */ - -tree -build_conditional_expr (ifexp, op1, op2) - tree ifexp, op1, op2; -{ - register tree type1; - register tree type2; - register enum tree_code code1; - register enum tree_code code2; - register tree result_type = NULL; - - /* If second operand is omitted, it is the same as the first one; - make sure it is calculated only once. */ - if (op1 == 0) - { - if (pedantic) - warning ("ANSI C forbids omitting the middle term of a ?: expression"); - ifexp = op1 = save_expr (ifexp); - } - - ifexp = truthvalue_conversion (default_conversion (ifexp)); - - if (TREE_CODE (ifexp) == ERROR_MARK - || TREE_CODE (TREE_TYPE (op1)) == ERROR_MARK - || TREE_CODE (TREE_TYPE (op2)) == ERROR_MARK) - return error_mark_node; - -#if 0 /* Produces wrong result if within sizeof. */ - /* Don't promote the operands separately if they promote - the same way. Return the unpromoted type and let the combined - value get promoted if necessary. */ - - if (TREE_TYPE (op1) == TREE_TYPE (op2) - && TREE_CODE (TREE_TYPE (op1)) != ARRAY_TYPE - && TREE_CODE (TREE_TYPE (op1)) != ENUMERAL_TYPE - && TREE_CODE (TREE_TYPE (op1)) != FUNCTION_TYPE) - { - if (TREE_LITERAL (ifexp) - && (TREE_CODE (ifexp) == INTEGER_CST - || TREE_CODE (ifexp) == ADDR_EXPR)) - return (integer_zerop (ifexp) ? op2 : op1); - - return build (COND_EXPR, TREE_TYPE (op1), ifexp, op1, op2); - } -#endif - - /* They don't match; promote them both and then try to reconcile them. */ - - if (TREE_CODE (TREE_TYPE (op1)) != VOID_TYPE) - op1 = default_conversion (op1); - if (TREE_CODE (TREE_TYPE (op2)) != VOID_TYPE) - op2 = default_conversion (op2); - - type1 = TREE_TYPE (op1); - code1 = TREE_CODE (type1); - type2 = TREE_TYPE (op2); - code2 = TREE_CODE (type2); - - /* Quickly detect the usual case where op1 and op2 have the same type - after promotion. */ - if (type1 == type2) - result_type = type1; - else if ((code1 == INTEGER_TYPE || code1 == REAL_TYPE) - && (code2 == INTEGER_TYPE || code2 == REAL_TYPE)) - { - result_type = commontype (type1, type2); - } - else if (code1 == VOID_TYPE || code2 == VOID_TYPE) - { - if (pedantic && (code1 != VOID_TYPE || code2 != VOID_TYPE)) - warning ("ANSI C forbids conditional expr with only one void side"); - result_type = void_type_node; - } - else if (code1 == POINTER_TYPE && code2 == POINTER_TYPE) - { - if (comp_target_types (type1, type2)) - result_type = commontype (type1, type2); - else if (integer_zerop (op1) && TREE_TYPE (type1) == void_type_node) - result_type = qualify_type (type2, type1); - else if (integer_zerop (op2) && TREE_TYPE (type2) == void_type_node) - result_type = qualify_type (type1, type2); - else if (TYPE_MAIN_VARIANT (TREE_TYPE (type1)) == void_type_node) - { - if (pedantic && TREE_CODE (type2) == FUNCTION_TYPE) - warning ("ANSI C forbids conditional expr between `void *' and function pointer"); - result_type = qualify_type (type1, type2); - } - else if (TYPE_MAIN_VARIANT (TREE_TYPE (type2)) == void_type_node) - { - if (pedantic && TREE_CODE (type1) == FUNCTION_TYPE) - warning ("ANSI C forbids conditional expr between `void *' and function pointer"); - result_type = qualify_type (type2, type1); - } - else - { - warning ("pointer type mismatch in conditional expression"); - result_type = build_pointer_type (void_type_node); - } - } - else if (code1 == POINTER_TYPE && code2 == INTEGER_TYPE) - { - if (!integer_zerop (op2)) - warning ("pointer/integer type mismatch in conditional expression"); - else - { - op2 = null_pointer_node; -#if 0 /* The spec seems to say this is permitted. */ - if (pedantic && TREE_CODE (type1) == FUNCTION_TYPE) - warning ("ANSI C forbids conditional expr between 0 and function pointer"); -#endif - } - result_type = type1; - } - else if (code2 == POINTER_TYPE && code1 == INTEGER_TYPE) - { - if (!integer_zerop (op1)) - warning ("pointer/integer type mismatch in conditional expression"); - else - { - op1 = null_pointer_node; -#if 0 /* The spec seems to say this is permitted. */ - if (pedantic && TREE_CODE (type2) == FUNCTION_TYPE) - warning ("ANSI C forbids conditional expr between 0 and function pointer"); -#endif - } - result_type = type2; - } - - if (!result_type) - { - if (flag_cond_mismatch) - result_type = void_type_node; - else - { - error ("type mismatch in conditional expression"); - return error_mark_node; - } - } - - if (result_type != TREE_TYPE (op1)) - op1 = convert (result_type, op1); - if (result_type != TREE_TYPE (op2)) - op2 = convert (result_type, op2); - -#if 0 - if (code1 == RECORD_TYPE || code1 == UNION_TYPE) - { - result_type = TREE_TYPE (op1); - if (TREE_LITERAL (ifexp)) - return (integer_zerop (ifexp) ? op2 : op1); - - if (TYPE_MODE (result_type) == BLKmode) - { - register tree tempvar - = build_decl (VAR_DECL, NULL_TREE, result_type); - register tree xop1 = build_modify_expr (tempvar, op1); - register tree xop2 = build_modify_expr (tempvar, op2); - register tree result = build (COND_EXPR, result_type, - ifexp, xop1, xop2); - - layout_decl (tempvar); - /* No way to handle variable-sized objects here. - I fear that the entire handling of BLKmode conditional exprs - needs to be redone. */ - if (! TREE_LITERAL (DECL_SIZE (tempvar))) - abort (); - DECL_RTL (tempvar) - = assign_stack_local (DECL_MODE (tempvar), - (TREE_INT_CST_LOW (DECL_SIZE (tempvar)) - * DECL_SIZE_UNIT (tempvar) - + BITS_PER_UNIT - 1) - / BITS_PER_UNIT); - - TREE_VOLATILE (result) - = TREE_VOLATILE (ifexp) | TREE_VOLATILE (op1) - | TREE_VOLATILE (op2); - return build (COMPOUND_EXPR, result_type, result, tempvar); - } - } -#endif /* 0 */ - - if (TREE_CODE (ifexp) == INTEGER_CST) - return (integer_zerop (ifexp) ? op2 : op1); - - return build (COND_EXPR, result_type, ifexp, op1, op2); -} - -/* Given a list of expressions, return a compound expression - that performs them all and returns the value of the last of them. */ - -tree -build_compound_expr (list) - tree list; -{ - register tree rest; - - if (TREE_CHAIN (list) == 0) - { - /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. - Strip such NOP_EXPRs, since LIST is used in non-lvalue context. */ - if (TREE_CODE (list) == NOP_EXPR - && TREE_TYPE (list) == TREE_TYPE (TREE_OPERAND (list, 0))) - list = TREE_OPERAND (list, 0); - - return TREE_VALUE (list); - } - - rest = build_compound_expr (TREE_CHAIN (list)); - - /* This is patched out so that sizeof (0, array) is distinguishable from - sizeof array. */ -#if 0 - if (! TREE_VOLATILE (TREE_VALUE (list))) - return rest; -#endif - - return build (COMPOUND_EXPR, TREE_TYPE (rest), TREE_VALUE (list), rest); -} - -/* Build an expression representing a cast to type TYPE of expression EXPR. */ - -tree -build_c_cast (type, expr) - register tree type; - tree expr; -{ - register tree value = expr; - - if (type == error_mark_node || expr == error_mark_node) - return error_mark_node; - type = TYPE_MAIN_VARIANT (type); - - /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. - Strip such NOP_EXPRs, since VALUE is being used in non-lvalue context. */ - if (TREE_CODE (value) == NOP_EXPR - && TREE_TYPE (value) == TREE_TYPE (TREE_OPERAND (value, 0))) - value = TREE_OPERAND (value, 0); - - if (TREE_CODE (type) == ARRAY_TYPE) - { - error ("cast specifies array type"); - return error_mark_node; - } - - if (type == TREE_TYPE (value)) - { - if (pedantic) - { - if (TREE_CODE (type) == RECORD_TYPE - || TREE_CODE (type) == UNION_TYPE) - warning ("ANSI C forbids casting nonscalar to the same type"); - } - } - else - { - tree otype; - /* Convert functions and arrays to pointers, - but don't convert any other types. */ - if (TREE_CODE (TREE_TYPE (value)) == FUNCTION_TYPE - || TREE_CODE (TREE_TYPE (value)) == ARRAY_TYPE) - value = default_conversion (value); - otype = TREE_TYPE (value); - - /* Optionally warn about potentially worrysome casts. */ - - if (warn_cast_qual - && TREE_CODE (type) == POINTER_TYPE - && TREE_CODE (otype) == POINTER_TYPE) - { - if (TREE_VOLATILE (TREE_TYPE (otype)) - && ! TREE_VOLATILE (TREE_TYPE (type))) - warning ("cast discards `volatile' from pointer target type"); - if (TREE_READONLY (TREE_TYPE (otype)) - && ! TREE_READONLY (TREE_TYPE (type))) - warning ("cast discards `const' from pointer target type"); - } - - value = convert (type, value); - } - - if (value == expr) - { - /* Always produce some operator for an explicit cast, - so we can tell (for -pedantic) that the cast is no lvalue. */ - tree nvalue = build (NOP_EXPR, type, value); - TREE_LITERAL (nvalue) = TREE_LITERAL (value); - return nvalue; - } - return value; -} - -/* Build an assignment expression of lvalue LHS from value RHS. - MODIFYCODE is the code for a binary operator that we use - to combine the old value of LHS with RHS to get the new value. - Or else MODIFYCODE is NOP_EXPR meaning do a simple assignment. */ - -tree -build_modify_expr (lhs, modifycode, rhs) - tree lhs, rhs; - enum tree_code modifycode; -{ - register tree result; - tree newrhs; - tree lhstype = TREE_TYPE (lhs); - tree olhstype = lhstype; - - /* Types that aren't fully specified cannot be used in assignments. */ - lhs = require_complete_type (lhs); - - /* Avoid duplicate error messages from operands that had errors. */ - if (TREE_CODE (lhs) == ERROR_MARK || TREE_CODE (rhs) == ERROR_MARK) - return error_mark_node; - - /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. - Strip such NOP_EXPRs, since RHS is being used in non-lvalue context. */ - if (TREE_CODE (rhs) == NOP_EXPR - && TREE_TYPE (rhs) == TREE_TYPE (TREE_OPERAND (rhs, 0))) - rhs = TREE_OPERAND (rhs, 0); - - newrhs = rhs; - - /* Handle control structure constructs used as "lvalues". */ - - if (!pedantic) - switch (TREE_CODE (lhs)) - { - /* Handle (a, b) used as an "lvalue". */ - case COMPOUND_EXPR: - return build (COMPOUND_EXPR, lhstype, - TREE_OPERAND (lhs, 0), - build_modify_expr (TREE_OPERAND (lhs, 1), - modifycode, rhs)); - - /* Handle (a ? b : c) used as an "lvalue". */ - case COND_EXPR: - rhs = save_expr (rhs); - { - /* Produce (a ? (b = rhs) : (c = rhs)) - except that the RHS goes through a save-expr - so the code to compute it is only emitted once. */ - tree cond - = build_conditional_expr - (TREE_OPERAND (lhs, 0), - build_modify_expr (TREE_OPERAND (lhs, 1), - modifycode, rhs), - build_modify_expr (TREE_OPERAND (lhs, 2), - modifycode, rhs)); - /* Make sure the code to compute the rhs comes out - before the split. */ - return build (COMPOUND_EXPR, TREE_TYPE (lhs), - /* Cast to void to suppress warning - from warn_if_unused_value. */ - convert (void_type_node, rhs), - cond); - } - } - - /* If a binary op has been requested, combine the old LHS value with the RHS - producing the value we should actually store into the LHS. */ - - if (modifycode != NOP_EXPR) - { - lhs = stabilize_reference (lhs); - newrhs = build_binary_op (modifycode, lhs, rhs); - } - - /* Handle a cast used as an "lvalue". - We have already performed any binary operator using the value as cast. - Now convert the result to the true type of the lhs and store there; - then cast the result back to the specified type to be the value - of the assignment. */ - - if (!pedantic) - switch (TREE_CODE (lhs)) - { - case NOP_EXPR: - case CONVERT_EXPR: - case FLOAT_EXPR: - case FIX_TRUNC_EXPR: - case FIX_FLOOR_EXPR: - case FIX_ROUND_EXPR: - case FIX_CEIL_EXPR: - if (TREE_CODE (TREE_TYPE (newrhs)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE (newrhs)) == FUNCTION_TYPE) - newrhs = default_conversion (newrhs); - { - tree inner_lhs = TREE_OPERAND (lhs, 0); - tree result = build_modify_expr (inner_lhs, NOP_EXPR, - convert (TREE_TYPE (inner_lhs), - newrhs)); - return convert (TREE_TYPE (lhs), result); - } - } - - /* Now we have handled acceptable kinds of LHS that are not truly lvalues. - Reject anything strange now. */ - - if (!lvalue_or_else (lhs, "assignment")) - return error_mark_node; - - /* Warn about storing in something that is `const'. */ - - if (TREE_READONLY (lhs) - || ((TREE_CODE (lhstype) == RECORD_TYPE - || TREE_CODE (lhstype) == UNION_TYPE) - && C_TYPE_FIELDS_READONLY (lhstype))) - readonly_warning (lhs, "assignment"); - - /* If storing into a structure or union member, - it has probably been given type `int'. - Compute the type that would go with - the actual amount of storage the member occupies. */ - - if (TREE_CODE (lhs) == COMPONENT_REF - && (TREE_CODE (lhstype) == INTEGER_TYPE - || TREE_CODE (lhstype) == REAL_TYPE - || TREE_CODE (lhstype) == ENUMERAL_TYPE)) - lhstype = TREE_TYPE (get_unwidened (lhs, 0)); - - /* If storing in a field that is in actuality a short or narrower than one, - we must store in the field in its actual type. */ - - if (lhstype != TREE_TYPE (lhs)) - { - lhs = copy_node (lhs); - TREE_TYPE (lhs) = lhstype; - } - - /* Convert new value to destination type. */ - - newrhs = convert_for_assignment (lhstype, newrhs, "assignment", 0); - if (TREE_CODE (newrhs) == ERROR_MARK) - return error_mark_node; - - result = build (MODIFY_EXPR, lhstype, lhs, newrhs); - TREE_VOLATILE (result) = 1; - - /* If we got the LHS in a different type for storing in, - convert the result back to the nominal type of LHS - so that the value we return always has the same type - as the LHS argument. */ - - if (olhstype == TREE_TYPE (result)) - return result; - return convert_for_assignment (olhstype, result, "assignment", 0); -} - -/* Return 0 if EXP is not a valid lvalue in this language - even though `lvalue_or_else' would accept it. */ - -int -language_lvalue_valid (exp) - tree exp; -{ - return 1; -} - -/* Convert value RHS to type TYPE as preparation for an assignment - to an lvalue of type TYPE. - The real work of conversion is done by `convert'. - The purpose of this function is to generate error messages - for assignments that are not allowed in C. - ERRTYPE is a string to use in error messages: - "assignment", "return", etc. - If NUM is positive, ERRTYPE is a printf format - that contains a %d to print NUM. */ - -static tree -convert_for_assignment (type, rhs, errtype, num) - tree type, rhs; - char *errtype; - int num; -{ - register enum tree_code codel = TREE_CODE (type); - register tree rhstype; - register enum tree_code coder; - - /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. - Strip such NOP_EXPRs, since RHS is used in non-lvalue context. */ - if (TREE_CODE (rhs) == NOP_EXPR - && TREE_TYPE (rhs) == TREE_TYPE (TREE_OPERAND (rhs, 0))) - rhs = TREE_OPERAND (rhs, 0); - - if (TREE_CODE (TREE_TYPE (rhs)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE (rhs)) == FUNCTION_TYPE) - rhs = default_conversion (rhs); - - rhstype = TREE_TYPE (rhs); - coder = TREE_CODE (rhstype); - - if (coder == ERROR_MARK) - return error_mark_node; - - if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (rhstype)) - return rhs; - - if (coder == VOID_TYPE) - { - error ("void value not ignored as it ought to be"); - return error_mark_node; - } - /* Arithmetic types all interconvert, and enum is treated like int. */ - if ((codel == INTEGER_TYPE || codel == REAL_TYPE || codel == ENUMERAL_TYPE) - && - (coder == INTEGER_TYPE || coder == REAL_TYPE || coder == ENUMERAL_TYPE)) - { - return convert (type, rhs); - } - /* Conversions among pointers */ - else if (codel == POINTER_TYPE && coder == POINTER_TYPE) - { - register tree ttl = TREE_TYPE (type); - register tree ttr = TREE_TYPE (rhstype); - /* Any non-function converts to a [const][volatile] void * - and vice versa; otherwise, targets must be the same. - Meanwhile, the lhs target must have all the qualifiers of the rhs. */ - if (TYPE_MAIN_VARIANT (ttl) == void_type_node - || TYPE_MAIN_VARIANT (ttr) == void_type_node - || comp_target_types (type, rhstype)) - { - if (pedantic - && ((TYPE_MAIN_VARIANT (ttl) == void_type_node - && TREE_CODE (ttr) == FUNCTION_TYPE) - || - (TYPE_MAIN_VARIANT (ttr) == void_type_node - && TREE_CODE (ttl) == FUNCTION_TYPE))) - warning_with_arg ("%s between incompatible pointer types", errtype, num); - else - { - if (! TREE_READONLY (ttl) && TREE_READONLY (ttr)) - warning_with_arg ("%s of non-const * pointer from const *", errtype, num); - if (! TREE_VOLATILE (ttl) && TREE_VOLATILE (ttr)) - warning_with_arg ("%s of non-volatile * pointer from volatile *", errtype, num); - } - } - else - warning_with_arg ("%s between incompatible pointer types", errtype, num); - return convert (type, rhs); - } - else if (codel == POINTER_TYPE && coder == INTEGER_TYPE) - { - if (! integer_zerop (rhs)) - { - warning_with_arg ("%s of pointer from integer lacks a cast", errtype, num); - return convert (type, rhs); - } - return null_pointer_node; - } - else if (codel == INTEGER_TYPE && coder == POINTER_TYPE) - { - warning_with_arg ("%s of integer from pointer lacks a cast", errtype, num); - return convert (type, rhs); - } - - error_with_arg ("incompatible types in %s", errtype, num); - return error_mark_node; -} - -/* Return nonzero if VALUE is a valid constant-valued expression - for use in initializing a static variable; one that can be an - element of a "constant" initializer. - - Return 1 if the value is absolute; return 2 if it is relocatable. - We assume that VALUE has been folded as much as possible; - therefore, we do not need to check for such things as - arithmetic-combinations of integers. */ - -static int -initializer_constant_valid_p (value) - tree value; -{ - switch (TREE_CODE (value)) - { - case CONSTRUCTOR: - return TREE_STATIC (value); - - case INTEGER_CST: - case REAL_CST: - case STRING_CST: - return 1; - - case ADDR_EXPR: - return 2; - - case CONVERT_EXPR: - case NOP_EXPR: - /* Allow conversions between types of the same kind. */ - if (TREE_CODE (TREE_TYPE (value)) - == TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0)))) - return initializer_constant_valid_p (TREE_OPERAND (value, 0)); - /* Allow (int) &foo. */ - if (TREE_CODE (TREE_TYPE (value)) == INTEGER_TYPE - && TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == POINTER_TYPE) - return initializer_constant_valid_p (TREE_OPERAND (value, 0)); - return 0; - - case PLUS_EXPR: - { - int valid0 = initializer_constant_valid_p (TREE_OPERAND (value, 0)); - int valid1 = initializer_constant_valid_p (TREE_OPERAND (value, 1)); - if (valid0 == 1 && valid1 == 2) - return 2; - if (valid0 == 2 && valid1 == 1) - return 2; - return 0; - } - - case MINUS_EXPR: - { - int valid0 = initializer_constant_valid_p (TREE_OPERAND (value, 0)); - int valid1 = initializer_constant_valid_p (TREE_OPERAND (value, 1)); - if (valid0 == 2 && valid1 == 1) - return 2; - return 0; - } - } - - return 0; -} - -/* Perform appropriate conversions on the initial value of a variable, - store it in the declaration DECL, - and print any error messages that are appropriate. - If the init is invalid, store an ERROR_MARK. */ - -void -store_init_value (decl, init) - tree decl, init; -{ - register tree value, type; - - /* If variable's type was invalidly declared, just ignore it. */ - - type = TREE_TYPE (decl); - if (TREE_CODE (type) == ERROR_MARK) - return; - - /* Digest the specified initializer into an expression. */ - - value = digest_init (type, init, 0); - - /* Store the expression if valid; else report error. */ - - if (value == error_mark_node) - ; - else if (TREE_STATIC (decl) && ! TREE_LITERAL (value)) - { - error ("initializer for static variable is not constant"); - value = error_mark_node; - } - else if (TREE_STATIC (decl) - && ! initializer_constant_valid_p (value)) - { - error ("initializer for static variable uses complicated arithmetic"); - value = error_mark_node; - } - else - { - if (pedantic && TREE_CODE (value) == CONSTRUCTOR) - { - if (! TREE_LITERAL (value)) - warning ("aggregate initializer is not constant"); - else if (! TREE_STATIC (value)) - warning ("aggregate initializer uses complicated arithmetic"); - } - } - DECL_INITIAL (decl) = value; -} - -/* Digest the parser output INIT as an initializer for type TYPE. - Return a C expression of type TYPE to represent the initial value. - - If TAIL is nonzero, it points to a variable holding a list of elements - of which INIT is the first. We update the list stored there by - removing from the head all the elements that we use. - Normally this is only one; we use more than one element only if - TYPE is an aggregate and INIT is not a constructor. */ - -tree -digest_init (type, init, tail) - tree type, init, *tail; -{ - enum tree_code code = TREE_CODE (type); - tree element = 0; - tree old_tail_contents; - /* Nonzero if INIT is a braced grouping, which comes in as a CONSTRUCTOR - tree node which has no TREE_TYPE. */ - int raw_constructor - = TREE_CODE (init) == CONSTRUCTOR && TREE_TYPE (init) == 0; - - /* By default, assume we use one element from a list. - We correct this later in the sole case where it is not true. */ - - if (tail) - { - old_tail_contents = *tail; - *tail = TREE_CHAIN (*tail); - } - - if (init == error_mark_node) - return init; - - if (init && raw_constructor - && CONSTRUCTOR_ELTS (init) != 0 - && TREE_CHAIN (CONSTRUCTOR_ELTS (init)) == 0) - element = TREE_VALUE (CONSTRUCTOR_ELTS (init)); - - /* Any type can be initialized from an expression of the same type, - optionally with braces. */ - - if (init && (TREE_TYPE (init) == type - || (code == ARRAY_TYPE && TREE_TYPE (init) - && comptypes (TREE_TYPE (init), type)))) - { - if (pedantic && code == ARRAY_TYPE - && TREE_CODE (init) != STRING_CST) - warning ("ANSI C forbids initializing array from array expression"); - if (optimize && TREE_READONLY (init) && TREE_CODE (init) == VAR_DECL) - return decl_constant_value (init); - return init; - } - - if (element && (TREE_TYPE (element) == type - || (code == ARRAY_TYPE && TREE_TYPE (element) - && comptypes (TREE_TYPE (element), type)))) - { - if (pedantic && code == ARRAY_TYPE) - warning ("ANSI C forbids initializing array from array expression"); - if (pedantic && (code == RECORD_TYPE || code == UNION_TYPE)) - warning ("single-expression nonscalar initializer has braces"); - if (optimize && TREE_READONLY (element) && TREE_CODE (element) == VAR_DECL) - return decl_constant_value (element); - return element; - } - - /* Check for initializing a union by its first field. - Such an initializer must use braces. */ - - if (code == UNION_TYPE) - { - tree result; - - if (TYPE_FIELDS (type) == 0) - { - error ("union with no members cannot be initialized"); - return error_mark_node; - } - - if (raw_constructor) - return process_init_constructor (type, init, 0); - else if (tail != 0) - { - *tail = old_tail_contents; - return process_init_constructor (type, 0, tail); - } - } - - /* Initialization of an array of chars from a string constant - optionally enclosed in braces. */ - - if (code == ARRAY_TYPE) - { - tree typ1 = TYPE_MAIN_VARIANT (TREE_TYPE (type)); - if ((typ1 == char_type_node - || typ1 == signed_char_type_node - || typ1 == unsigned_char_type_node - || typ1 == unsigned_type_node - || typ1 == integer_type_node) - && ((init && TREE_CODE (init) == STRING_CST) - || (element && TREE_CODE (element) == STRING_CST))) - { - tree string = element ? element : init; - - if ((TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (string))) - != char_type_node) - && TYPE_PRECISION (typ1) == TYPE_PRECISION (char_type_node)) - { - error ("char-array initialized from wide string"); - return error_mark_node; - } - if ((TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (string))) - == char_type_node) - && TYPE_PRECISION (typ1) == TYPE_PRECISION (integer_type_node)) - { - error ("int-array initialized from non-wide string"); - return error_mark_node; - } - - TREE_TYPE (string) = type; - if (TYPE_DOMAIN (type) != 0 - && TREE_LITERAL (TYPE_SIZE (type))) - { - register int size - = TREE_INT_CST_LOW (TYPE_SIZE (type)) * TYPE_SIZE_UNIT (type); - size = (size + BITS_PER_UNIT - 1) / BITS_PER_UNIT; - /* Subtract 1 because it's ok to ignore the terminating null char - that is counted in the length of the constant. */ - if (size < TREE_STRING_LENGTH (string) - 1) - warning ("initializer-string for array of chars is too long"); - } - return string; - } - } - - /* Handle scalar types, including conversions. */ - - if (code == INTEGER_TYPE || code == REAL_TYPE || code == POINTER_TYPE - || code == ENUMERAL_TYPE) - { - if (raw_constructor) - { - if (element == 0) - { - error ("initializer for scalar variable requires one element"); - return error_mark_node; - } - init = element; - } - - if (TREE_CODE (init) == CONSTRUCTOR) - { - error ("initializer for scalar has extra braces"); - return error_mark_node; - } - - return convert_for_assignment (type, default_conversion (init), - "initialization", 0); - } - - /* Come here only for records and arrays. */ - - if (TYPE_SIZE (type) && ! TREE_LITERAL (TYPE_SIZE (type))) - { - error ("variable-sized object may not be initialized"); - return error_mark_node; - } - - if (code == ARRAY_TYPE || code == RECORD_TYPE) - { - if (raw_constructor) - return process_init_constructor (type, init, 0); - else if (tail != 0) - { - *tail = old_tail_contents; - return process_init_constructor (type, 0, tail); - } - else if (flag_traditional) - /* Traditionally one can say `char x[100] = 0;'. */ - return process_init_constructor (type, - build_nt (CONSTRUCTOR, 0, - tree_cons (0, init, 0)), - 0); - } - - error ("invalid initializer"); - return error_mark_node; -} - -/* Process a constructor for a variable of type TYPE. - The constructor elements may be specified either with INIT or with ELTS, - only one of which should be non-null. - - If INIT is specified, it is a CONSTRUCTOR node which is specifically - and solely for initializing this datum. - - If ELTS is specified, it is the address of a variable containing - a list of expressions. We take as many elements as we need - from the head of the list and update the list. - - In the resulting constructor, TREE_LITERAL is set if all elts are - constant, and TREE_STATIC is set if, in addition, all elts are simple enough - constants that the assembler and linker can compute them. */ - -static tree -process_init_constructor (type, init, elts) - tree type, init, *elts; -{ - register tree tail; - /* List of the elements of the result constructor, - in reverse order. */ - register tree members = NULL; - tree result; - int allconstant = 1; - int allsimple = 1; - int error_flag = 0; - - /* Make TAIL be the list of elements to use for the initialization, - no matter how the data was given to us. */ - - if (elts) - tail = *elts; - else - tail = CONSTRUCTOR_ELTS (init); - - /* Gobble as many elements as needed, and make a constructor or initial value - for each element of this aggregate. Chain them together in result. - If there are too few, use 0 for each scalar ultimate component. */ - - if (TREE_CODE (type) == ARRAY_TYPE) - { - tree domain = TYPE_DOMAIN (type); - register long len; - register int i; - - if (domain) - len = TREE_INT_CST_LOW (TYPE_MAX_VALUE (domain)) - - TREE_INT_CST_LOW (TYPE_MIN_VALUE (domain)) - + 1; - else - len = -1; /* Take as many as there are */ - - for (i = 0; (len < 0 || i < len) && tail != 0; i++) - { - register tree next1; - - if (TREE_VALUE (tail) != 0) - { - tree tail1 = tail; - next1 = digest_init (TYPE_MAIN_VARIANT (TREE_TYPE (type)), - TREE_VALUE (tail), &tail1); - if (tail1 != 0 && TREE_CODE (tail1) != TREE_LIST) - abort (); - if (tail == tail1 && len < 0) - { - error ("non-empty initializer for array of empty elements"); - /* Just ignore what we were supposed to use. */ - tail1 = 0; - } - tail = tail1; - } - else - { - next1 = error_mark_node; - tail = TREE_CHAIN (tail); - } - - if (next1 == error_mark_node) - error_flag = 1; - else if (!TREE_LITERAL (next1)) - allconstant = 0; - else if (! initializer_constant_valid_p (next1)) - allsimple = 0; - members = tree_cons (NULL_TREE, next1, members); - } - } - if (TREE_CODE (type) == RECORD_TYPE) - { - register tree field; - - for (field = TYPE_FIELDS (type); field && tail; - field = TREE_CHAIN (field)) - { - register tree next1; - - if (! DECL_NAME (field)) - { - members = tree_cons (field, integer_zero_node, members); - continue; - } - - if (TREE_VALUE (tail) != 0) - { - tree tail1 = tail; - next1 = digest_init (TREE_TYPE (field), - TREE_VALUE (tail), &tail1); - if (tail1 != 0 && TREE_CODE (tail1) != TREE_LIST) - abort (); - tail = tail1; - } - else - { - next1 = error_mark_node; - tail = TREE_CHAIN (tail); - } - - if (next1 == error_mark_node) - error_flag = 1; - else if (!TREE_LITERAL (next1)) - allconstant = 0; - else if (! initializer_constant_valid_p (next1)) - allsimple = 0; - members = tree_cons (field, next1, members); - } - } - - if (TREE_CODE (type) == UNION_TYPE) - { - register tree field = TYPE_FIELDS (type); - register tree next1; - - /* For a union, get the initializer for 1 fld. */ - - if (TREE_VALUE (tail) != 0) - { - tree tail1 = tail; - next1 = digest_init (TREE_TYPE (field), - TREE_VALUE (tail), &tail1); - if (tail1 != 0 && TREE_CODE (tail1) != TREE_LIST) - abort (); - tail = tail1; - } - else - { - next1 = error_mark_node; - tail = TREE_CHAIN (tail); - } - - if (next1 == error_mark_node) - error_flag = 1; - else if (!TREE_LITERAL (next1)) - allconstant = 0; - else if (! initializer_constant_valid_p (next1)) - allsimple = 0; - members = tree_cons (field, next1, members); - } - - /* If arguments were specified as a list, just remove the ones we used. */ - if (elts) - *elts = tail; - /* If arguments were specified as a constructor, - complain unless we used all the elements of the constructor. */ - else if (tail) - warning ("excess elements in aggregate initializer"); - - if (error_flag) - return error_mark_node; - - result = build (CONSTRUCTOR, type, NULL_TREE, nreverse (members)); - if (allconstant) TREE_LITERAL (result) = 1; - if (allconstant && allsimple) TREE_STATIC (result) = 1; - return result; -} - -/* Expand an ASM statement with operands, handling output operands - that are not variables or INDIRECT_REFS by transforming such - cases into cases that expand_asm_operands can handle. - - Arguments are same as for expand_asm_operands. */ - -void -c_expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line) - tree string, outputs, inputs, clobbers; - int vol; - char *filename; - int line; -{ - int noutputs = list_length (outputs); - register int i; - /* o[I] is the place that output number I should be written. */ - register tree *o = (tree *) alloca (noutputs * sizeof (tree)); - register tree tail; - - /* Record the contents of OUTPUTS before it is modifed. */ - for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++) - o[i] = TREE_VALUE (tail); - -#if 0 /* Don't do this--it screws up operands expected to be in memory. */ - /* Perform default conversions on all inputs. */ - for (i = 0, tail = inputs; tail; tail = TREE_CHAIN (tail), i++) - TREE_VALUE (tail) = default_conversion (TREE_VALUE (tail)); -#endif - - /* Generate the ASM_OPERANDS insn; - store into the TREE_VALUEs of OUTPUTS some trees for - where the values were actually stored. */ - expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line); - - /* Copy all the intermediate outputs into the specified outputs. */ - for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++) - { - if (o[i] != TREE_VALUE (tail)) - expand_expr (build_modify_expr (o[i], NOP_EXPR, TREE_VALUE (tail)), - 0, VOIDmode, 0); - /* Detect modification of read-only values. - (Otherwise done by build_modify_expr.) */ - else - { - tree type = TREE_TYPE (o[i]); - if (TREE_READONLY (o[i]) - || ((TREE_CODE (type) == RECORD_TYPE - || TREE_CODE (type) == UNION_TYPE) - && C_TYPE_FIELDS_READONLY (type))) - readonly_warning (o[i], "modification by `asm'"); - } - } - - /* Those MODIFY_EXPRs could do autoincrements. */ - emit_queue (); -} - -/* Expand a C `return' statement. - RETVAL is the expression for what to return, - or a null pointer for `return;' with no value. */ - -void -c_expand_return (retval) - tree retval; -{ - tree valtype = TREE_TYPE (TREE_TYPE (current_function_decl)); - - if (TREE_THIS_VOLATILE (current_function_decl)) - warning ("function declared `volatile' has a `return' statement"); - - if (!retval) - { - current_function_returns_null = 1; - if (warn_return_type && valtype != 0 && TREE_CODE (valtype) != VOID_TYPE) - warning ("`return' with no value, in function returning non-void"); - expand_null_return (); - } - else if (valtype == 0 || TREE_CODE (valtype) == VOID_TYPE) - { - current_function_returns_null = 1; - if (pedantic || TREE_CODE (TREE_TYPE (retval)) != VOID_TYPE) - warning ("`return' with a value, in function returning void"); - expand_return (retval); - } - else - { - tree t = convert_for_assignment (valtype, retval, "return", 0); - tree res = DECL_RESULT (current_function_decl); - t = build (MODIFY_EXPR, TREE_TYPE (res), - res, convert (TREE_TYPE (res), t)); - expand_return (t); - current_function_returns_value = 1; - } -} - -/* Start a C switch statement, testing expression EXP. - Return EXP if it is valid, an error node otherwise. */ - -tree -c_expand_start_case (exp) - tree exp; -{ - register enum tree_code code = TREE_CODE (TREE_TYPE (exp)); - tree type = TREE_TYPE (exp); - - if (code != INTEGER_TYPE && code != ENUMERAL_TYPE && code != ERROR_MARK) - { - error ("switch quantity not an integer"); - exp = error_mark_node; - } - else - { - tree index; - - exp = default_conversion (exp); - type = TREE_TYPE (exp); - index = get_unwidened (exp, 0); - /* We can't strip a conversion from a signed type to an unsigned, - because if we did, int_fits_type_p would do the wrong thing - when checking case values for being in range, - and it's too hard to do the right thing. */ - if (TREE_UNSIGNED (TREE_TYPE (exp)) - == TREE_UNSIGNED (TREE_TYPE (index))) - exp = index; - } - - expand_start_case (1, exp, type); - - return exp; -} diff --git a/gnu/usr.bin/gcc1/cc1/caller-save.c b/gnu/usr.bin/gcc1/cc1/caller-save.c deleted file mode 100644 index 9a91a56cd5..0000000000 --- a/gnu/usr.bin/gcc1/cc1/caller-save.c +++ /dev/null @@ -1,666 +0,0 @@ -/* Save and restore call-clobbered registers which are live across a call. - Copyright (C) 1989 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include "config.h" -#include "rtl.h" -#include "insn-config.h" -#include "flags.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "reload.h" -#include "recog.h" -#include "basic-block.h" - -/* Set of hard regs currently live (during scan of all insns). */ - -static HARD_REG_SET hard_regs_live; - -/* The block of storage on the stack where regs are saved */ - -static rtx save_block_addr; -static int save_block_size; - -/* A REG rtx for each hard register that has been saved. */ - -static rtx save_reg_rtx[FIRST_PSEUDO_REGISTER]; - -static void set_reg_live (); -static void clear_reg_live (); -static void insert_call_saves (); -static void emit_mult_save (); -static void emit_mult_restore (); -static rtx grow_save_block (); -static enum machine_mode choose_hard_reg_mode (); - -/* Find the places where hard regs are live across calls and save them. */ - -save_call_clobbered_regs () -{ - rtx insn; - int b; - - if (obey_regdecls) - return; - - save_block_size = 0; - save_block_addr = 0; - bzero (save_reg_rtx, sizeof save_reg_rtx); - - for (b = 0; b < n_basic_blocks; b++) - { - regset regs_live = basic_block_live_at_start[b]; - int offset, bit, i; - - /* Compute hard regs live at start of block -- this is the - real hard regs marked live, plus live pseudo regs that - have been renumbered to hard regs. */ - -#ifdef HARD_REG_SET - hard_regs_live = *regs_live; -#else - COPY_HARD_REG_SET (hard_regs_live, regs_live); -#endif - - for (offset = 0, i = 0; offset < regset_size; offset++) - { - if (regs_live[offset] == 0) - i += HOST_BITS_PER_INT; - else - for (bit = 1; bit && i < max_regno; bit <<= 1, i++) - if ((regs_live[offset] & bit) && reg_renumber[i] >= 0) - SET_HARD_REG_BIT (hard_regs_live, reg_renumber[i]); - } - - /* Now scan the insns in the block, keeping track of what hard - regs are live as we go. When we see a call, save the live - call-clobbered hard regs. */ - - for (insn = basic_block_head[b]; TRUE; insn = NEXT_INSN (insn)) - { - RTX_CODE code = GET_CODE (insn); - - if (code == CALL_INSN) - insert_call_saves (insn); - - if (code == INSN || code == CALL_INSN || code == JUMP_INSN) - { - rtx link; - - /* NB: the normal procedure is to first enliven any - registers set by insn, then deaden any registers that - had their last use at insn. This is incorrect now, - since multiple pseudos may have been mapped to the - same hard reg, and the death notes are ambiguous. So - it must be done in the other, safe, order. */ - - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_DEAD) - clear_reg_live (XEXP (link, 0)); - - note_stores (PATTERN (insn), set_reg_live); - } - - if (insn == basic_block_end[b]) - break; - } - } -} - -/* Here from note_stores when an insn stores a value in a register. - Set the proper bit or bits in hard_regs_live. */ - -static void -set_reg_live (reg, setter) - rtx reg, setter; -{ - register int regno; - - /* WORD is which word of a multi-register group is being stored. - For the case where the store is actually into a SUBREG of REG. - Except we don't use it; I believe the entire REG needs to be - live. */ - int word = 0; - - if (GET_CODE (reg) == SUBREG) - { - word = SUBREG_WORD (reg); - reg = SUBREG_REG (reg); - } - - if (GET_CODE (reg) != REG) - return; - - regno = REGNO (reg); - - /* For pseudo reg, see if it has been assigned a hardware reg. */ - if (reg_renumber[regno] >= 0) - regno = reg_renumber[regno] /* + word */; - - /* Handle hardware regs (and pseudos allocated to hard regs). */ - if (regno < FIRST_PSEUDO_REGISTER && ! call_fixed_regs[regno]) - { - register int last = regno + HARD_REGNO_NREGS (regno, GET_MODE (reg)); - while (regno < last) - { - SET_HARD_REG_BIT (hard_regs_live, regno); - regno++; - } - } -} - -/* Here when a REG_DEAD note records the last use of a reg. Clear - the appropriate bit or bits in hard_regs_live. */ - -static void -clear_reg_live (reg) - rtx reg; -{ - register int regno = REGNO (reg); - - /* For pseudo reg, see if it has been assigned a hardware reg. */ - if (reg_renumber[regno] >= 0) - regno = reg_renumber[regno]; - - /* Handle hardware regs (and pseudos allocated to hard regs). */ - if (regno < FIRST_PSEUDO_REGISTER && ! call_fixed_regs[regno]) - { - /* Pseudo regs already assigned hardware regs are treated - almost the same as explicit hardware regs. */ - register int last = regno + HARD_REGNO_NREGS (regno, GET_MODE (reg)); - while (regno < last) - { - CLEAR_HARD_REG_BIT (hard_regs_live, regno); - regno++; - } - } -} - -/* Insert insns to save and restore live call-clobbered regs around - call insn INSN. */ - -static void -insert_call_saves (insn) - rtx insn; -{ - int regno; - int save_block_size_needed; - int save_block_offset[FIRST_PSEUDO_REGISTER]; - - save_block_size_needed = 0; - - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno) - { - save_block_offset[regno] = -1; - if (call_used_regs[regno] && ! call_fixed_regs[regno] - && TEST_HARD_REG_BIT (hard_regs_live, regno)) - { - enum machine_mode mode = choose_hard_reg_mode (regno); - int align = GET_MODE_UNIT_SIZE (mode); - if (align > BIGGEST_ALIGNMENT / BITS_PER_UNIT) - align = BIGGEST_ALIGNMENT / BITS_PER_UNIT; - save_block_size_needed = - ((save_block_size_needed + align - 1) / align) * align; - save_block_offset[regno] = save_block_size_needed; - save_block_size_needed += GET_MODE_SIZE (mode); - if (! save_reg_rtx[regno]) - save_reg_rtx[regno] = gen_rtx (REG, mode, regno); - } - } - - if (save_block_size < save_block_size_needed) - save_block_addr = grow_save_block (save_block_addr, - save_block_size_needed); - emit_mult_save (insn, save_block_addr, save_block_offset); - emit_mult_restore (insn, save_block_addr, save_block_offset); -} - -/* Emit a string of stores to save the hard regs listed in - OFFSET[] at address ADDR. Emit them before INSN. - OFFSET[reg] is -1 if reg should not be saved, or a - suitably-aligned offset from ADDR. - The offsets actually used do not have to be those listed - in OFFSET, but should fit in a block of the same size. */ - -static void -emit_mult_save (insn, addr, offset) - rtx insn, addr; - int offset[]; -{ - int regno; - /* A register to use as a temporary for address calculations. */ - rtx tempreg; - /* A register that could be used as that temp if we save and restore it. */ - rtx can_push_reg; - /* Nonzero means we need to save a register to use it as TEMPREG. */ - int needpush; - /* The amount the stack is decremented to save that register (if we do). */ - int decrement; - /* Record which regs we save, in case we branch to retry. */ - char already_saved[FIRST_PSEUDO_REGISTER]; - - bzero (already_saved, sizeof already_saved); - - /* Hair is needed because sometimes the addresses to save in are - not valid (offsets too big). - So we need a reg, TEMPREG, to compute addresses in. - - We look first for an empty reg to use. - Sometimes no reg is empty. Then we push a reg, use it, and pop it. - - Sometimes the only reg to push and pop this way is one we want to save. - We can't save it while using it as a temporary. - So we save all the other registers, pop it, and go back to `retry'. - At that point, only this reg remains to be saved; - all the others already saved are empty. - So one of them can be the temporary for this one. */ - - /* Sometimes we can't save all the regs conveniently at once, just some. - If that happens, we branch back here to save the rest. */ - retry: - needpush = 0; - tempreg = 0; - can_push_reg = 0; - - /* Set NEEDPUSH if any save-addresses are not valid memory addresses. - If any register is available, record it in TEMPREG. - If any register doesn't need saving here, record it in CAN_PUSH_REG. */ - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno) - { - if (offset[regno] >= 0 && ! already_saved[regno]) - { - rtx reg = save_reg_rtx[regno]; - rtx addr1 = plus_constant (addr, offset[regno]); - if (memory_address_p (GET_MODE (reg), addr1)) - needpush = 1; - } - - /* A call-clobbered reg that is dead, or already saved, - can be used as a temporary for sure, at no extra cost. */ - if (tempreg == 0 && call_used_regs[regno] && ! fixed_regs[regno] - && !(offset[regno] >= 0 && ! already_saved[regno]) - && HARD_REGNO_MODE_OK (regno, Pmode)) - { - tempreg = gen_rtx (REG, Pmode, regno); - /* Don't use it if not valid for addressing. */ - if (! strict_memory_address_p (QImode, tempreg)) - tempreg = 0; - } - - /* A call-saved reg can be a temporary if we push and pop it. */ - if (can_push_reg == 0 && ! call_used_regs[regno] - && HARD_REGNO_MODE_OK (regno, Pmode)) - { - can_push_reg = gen_rtx (REG, Pmode, regno); - /* Don't use it if not valid for addressing. */ - if (! strict_memory_address_p (QImode, can_push_reg)) - can_push_reg = 0; - } - } - - /* Clear NEEDPUSH if we already found an empty reg. */ - if (tempreg != 0) - needpush = 0; - - /* If we need a temp reg and none is free, make one free. */ - if (needpush) - { - /* Choose a reg, preferably not among those it is our job to save. */ - if (can_push_reg != 0) - tempreg = can_push_reg; - else - { - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno) - if (offset[regno] >= 0 && !already_saved[regno] - && HARD_REGNO_MODE_OK (regno, Pmode)) - { - tempreg = gen_rtx (REG, Pmode, regno); - /* Don't use it if not valid for addressing. */ - if (! strict_memory_address_p (QImode, tempreg)) - tempreg = 0; - else - break; - } - } - - /* Push it on the stack. */ -#ifdef STACK_GROWS_DOWNWARD - decrement = UNITS_PER_WORD; -#else - decrement = - UNITS_PER_WORD; -#endif - - emit_insn_before (gen_add2_insn (stack_pointer_rtx, - gen_rtx (CONST_INT, VOIDmode, -decrement)), - insn); - emit_insn_before (gen_move_insn (gen_rtx (MEM, Pmode, stack_pointer_rtx), - tempreg), - insn); - } - - /* Save the regs we are supposed to save, aside from TEMPREG. - Use TEMPREG for address calculations when needed. */ - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno) - if (offset[regno] >= 0 && ! already_saved[regno] - && tempreg != 0 && REGNO (tempreg) != regno) - { - rtx reg = save_reg_rtx[regno]; - rtx addr1 = plus_constant (addr, offset[regno]); - rtx temp; - if (! memory_address_p (GET_MODE (reg), addr1)) - { - if (GET_CODE (addr1) != PLUS) - abort (); - if (GET_CODE (XEXP (addr1, 1)) != CONST_INT - || GET_CODE (XEXP (addr1, 0)) != REG) - abort (); - emit_insn_before (gen_move_insn (tempreg, XEXP (addr1, 0)), insn); - emit_insn_before (gen_add2_insn (tempreg, XEXP (addr1, 1)), insn); - addr1 = tempreg; - } - temp = gen_rtx (MEM, GET_MODE (reg), addr1); - emit_insn_before (gen_move_insn (temp, reg), insn); - already_saved[regno] = 1; - } - - /* If we pushed TEMPREG to make it free, pop it. */ - if (needpush) - { - emit_insn_before (gen_move_insn (tempreg, - gen_rtx (MEM, Pmode, stack_pointer_rtx)), - insn); - emit_insn_before (gen_add2_insn (stack_pointer_rtx, - gen_rtx (CONST_INT, VOIDmode, decrement)), - insn); - } - - /* If TEMPREG itself needs saving, go back and save it. - There are plenty of free regs now, those already saved. */ - if (tempreg != 0 - && offset[REGNO (tempreg)] >= 0 && ! already_saved[REGNO (tempreg)]) - goto retry; -} - -/* Emit a string of loads to restore the hard regs listed in - OFFSET[] from address ADDR; insert the loads after INSN. - OFFSET[reg] is -1 if reg should not be loaded, or a - suitably-aligned offset from ADDR. - The offsets actually used do not need to be those provided in - OFFSET, but should agree with whatever emit_mult_save does. */ - -static void -emit_mult_restore (insn, addr, offset) - rtx insn, addr; - int offset[]; -{ - int regno; - - /* Number of regs now needing to be restored. */ - int restore_count; - /* A register to use as a temporary for address calculations. */ - rtx tempreg; - /* A register available for that purpose but less desirable. */ - rtx maybe_tempreg; - /* A register that could be used as that temp if we push and pop it. */ - rtx can_push_reg; - /* Nonzero means we need to push and pop a register to use it as TEMPREG. */ - int needpush; - /* The amount the stack is decremented to save that register (if we do). */ - int decrement; - /* Record which regs we restore, in case we branch to retry. */ - char already_restored[FIRST_PSEUDO_REGISTER]; - - bzero (already_restored, sizeof already_restored); - - /* Note: INSN can't be the last insn, since if it were, - no regs would live across it. */ - insn = NEXT_INSN (insn); - if (insn == 0) - abort (); - /* Now we can insert before INSN. - That is convenient because we can insert them in the order - that they should ultimately appear. */ - - /* Hair is needed because sometimes the addresses to restore from are - not valid (offsets too big). - So we need a reg, TEMPREG, to compute addresses in. - - We look first for an empty reg to use. - Sometimes no reg is empty. Then we push a reg, use it, and pop it. - - If all the suitable regs need to be restored, - that strategy won't work. So we restore all but one, using that one - as a temporary. Then we jump to `retry' to restore that one, - pushing and popping another (already restored) as a temporary. */ - - retry: - needpush = 0; - tempreg = 0; - can_push_reg = 0; - restore_count = 0; - - /* Set NEEDPUSH if any restore-addresses are not valid memory addresses. - If any register is available, record it in TEMPREG. - Otherwise, one register yet to be restored goes in MAYBE_TEMPREG, - and can be used as TEMPREG for any other regs to be restored. - If any register doesn't need restoring, record it in CAN_PUSH_REG. */ - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno) - { - if (offset[regno] >= 0 && ! already_restored[regno]) - { - rtx reg = save_reg_rtx[regno]; - rtx addr1 = plus_constant (addr, offset[regno]); - - restore_count++; - - if (memory_address_p (GET_MODE (reg), addr1)) - needpush = 1; - - /* Find a call-clobbered reg that needs restoring. - We can use it as a temporary if we defer restoring it. */ - if (maybe_tempreg == 0) - { - maybe_tempreg = gen_rtx (REG, Pmode, regno); - /* Don't use it if not valid for addressing. */ - if (! strict_memory_address_p (QImode, maybe_tempreg)) - maybe_tempreg = 0; - } - } - - /* If any call-clobbered reg is dead, put it in TEMPREG. - It can be used as a temporary at no extra cost. */ - if (tempreg == 0 && call_used_regs[regno] && ! fixed_regs[regno] - && ! offset[regno] >= 0 - && HARD_REGNO_MODE_OK (regno, Pmode)) - { - tempreg = gen_rtx (REG, Pmode, regno); - /* Don't use it if not valid for addressing. */ - if (! strict_memory_address_p (QImode, tempreg)) - tempreg = 0; - } - - /* Any non-call-clobbered reg, put in CAN_PUSH_REG. - It can be used as a temporary if we push and pop it. */ - if (can_push_reg == 0 && ! call_used_regs[regno] - && HARD_REGNO_MODE_OK (regno, Pmode)) - { - can_push_reg = gen_rtx (REG, Pmode, regno); - /* Don't use it if not valid for addressing. */ - if (! strict_memory_address_p (QImode, can_push_reg)) - can_push_reg = 0; - } - /* Any reg we already restored can be a temporary - if we push and pop it. */ - if (can_push_reg == 0 && already_restored[regno] - && HARD_REGNO_MODE_OK (regno, Pmode)) - { - can_push_reg = gen_rtx (REG, Pmode, regno); - /* Don't use it if not valid for addressing. */ - if (! strict_memory_address_p (QImode, can_push_reg)) - can_push_reg = 0; - } - } - - /* If 2 or more regs need to be restored, use one as a temp reg - for the rest (if we need a tempreg). */ - if (tempreg == 0 && maybe_tempreg != 0 && restore_count > 1) - tempreg = maybe_tempreg; - - /* Clear NEEDPUSH if we already found an empty reg. */ - if (tempreg != 0) - needpush = 0; - - /* If we need a temp reg and none is free, make one free. */ - if (needpush) - { - tempreg = can_push_reg; - - /* Push it on the stack. */ -#ifdef STACK_GROWS_DOWNWARD - decrement = UNITS_PER_WORD; -#else - decrement = - UNITS_PER_WORD; -#endif - - emit_insn_before (gen_add2_insn (stack_pointer_rtx, - gen_rtx (CONST_INT, VOIDmode, -decrement)), - insn); - emit_insn_before (gen_move_insn (gen_rtx (MEM, Pmode, stack_pointer_rtx), - tempreg), - insn); - } - - /* Restore the regs we are supposed to restore, aside from TEMPREG. - Use TEMPREG for address calculations when needed. */ - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno) - if (offset[regno] >= 0 && ! already_restored[regno] - && tempreg != 0 && REGNO (tempreg) != regno) - { - rtx reg = save_reg_rtx[regno]; - rtx addr1 = plus_constant (addr, offset[regno]); - rtx temp; - if (! memory_address_p (GET_MODE (reg), addr1)) - { - if (GET_CODE (addr1) != PLUS) - abort (); - if (GET_CODE (XEXP (addr1, 1)) != CONST_INT - || GET_CODE (XEXP (addr1, 0)) != REG) - abort (); - emit_insn_before (gen_move_insn (tempreg, XEXP (addr1, 0)), insn); - emit_insn_before (gen_add2_insn (tempreg, XEXP (addr1, 1)), insn); - addr1 = tempreg; - } - temp = gen_rtx (MEM, GET_MODE (reg), addr1); - emit_insn_before (gen_move_insn (reg, temp), insn); - already_restored[regno] = 1; - } - - /* If we pushed TEMPREG to make it free, pop it. */ - if (needpush) - { - emit_insn_before (gen_move_insn (tempreg, - gen_rtx (MEM, Pmode, stack_pointer_rtx)), - insn); - emit_insn_before (gen_add2_insn (stack_pointer_rtx, - gen_rtx (CONST_INT, VOIDmode, decrement)), - insn); - } - - /* If TEMPREG itself needs restoring, go back and restore it. - We can find a reg already restored to push and use as a temporary. */ - if (tempreg != 0 - && offset[REGNO (tempreg)] >= 0 && ! already_restored[REGNO (tempreg)]) - goto retry; -} - -/* Return the address of a new block of size SIZE on the stack. - The old save block is at ADDR; ADDR is 0 if no block exists yet. */ - -static rtx -grow_save_block (addr, size) - rtx addr; - int size; -{ - rtx newaddr; - - /* Keep the size a multiple of the main allocation unit. */ - size = (((size + (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1) - / (BIGGEST_ALIGNMENT / BITS_PER_UNIT)) - * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); - - /* If no save block exists yet, create one and return it. */ - if (! addr) - { - save_block_size = size; - return XEXP (assign_stack_local (BLKmode, size), 0); - } - - /* Get a new block and coalesce it with the old one. */ - newaddr = XEXP (assign_stack_local (BLKmode, size - save_block_size), 0); - if (GET_CODE (newaddr) == PLUS - && XEXP (newaddr, 0) == frame_pointer_rtx - && GET_CODE (XEXP (newaddr, 1)) == CONST_INT - && GET_CODE (addr) == PLUS - && XEXP (addr, 0) == frame_pointer_rtx - && GET_CODE (XEXP (addr, 1)) == CONST_INT - && ((INTVAL (XEXP (newaddr, 1)) - INTVAL (XEXP (addr, 1)) - == size - save_block_size) - || (INTVAL (XEXP (addr, 1)) - INTVAL (XEXP (newaddr, 1)) - == size - save_block_size))) - { - save_block_size = size; - if (INTVAL (XEXP (newaddr, 1)) < INTVAL (XEXP (addr, 1))) - return newaddr; - else - return addr; - } - - /* They didn't coalesce, find out why */ - abort (); - - save_block_size = size; - return XEXP (assign_stack_local (BLKmode, size), 0); -} - -/* Return a machine mode that is legitimate for hard reg REGNO - and large enough to save the whole register. */ - -static enum machine_mode -choose_hard_reg_mode (regno) - int regno; -{ - enum reg_class class = REGNO_REG_CLASS (regno); - - if (CLASS_MAX_NREGS (class, DImode) == 1 - && HARD_REGNO_MODE_OK (regno, DImode)) - return DImode; - else if (CLASS_MAX_NREGS (class, DFmode) == 1 - && HARD_REGNO_MODE_OK (regno, DFmode)) - return DFmode; - else if (CLASS_MAX_NREGS (class, SImode) == 1 - && HARD_REGNO_MODE_OK (regno, SImode)) - return SImode; - else if (CLASS_MAX_NREGS (class, SFmode) == 1 - && HARD_REGNO_MODE_OK (regno, SFmode)) - return SFmode; - else if (CLASS_MAX_NREGS (class, HImode) == 1 - && HARD_REGNO_MODE_OK (regno, HImode)) - return HImode; - else - abort (); -} diff --git a/gnu/usr.bin/gcc1/cc1/combine.c b/gnu/usr.bin/gcc1/cc1/combine.c deleted file mode 100644 index cc6676ebbe..0000000000 --- a/gnu/usr.bin/gcc1/cc1/combine.c +++ /dev/null @@ -1,2796 +0,0 @@ -/* Optimize by combining instructions for GNU compiler. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This module is essentially the "combiner" phase of the U. of Arizona - Portable Optimizer, but redone to work on our list-structured - representation for RTL instead of their string representation. - - The LOG_LINKS of each insn identify the most recent assignment - to each REG used in the insn. It is a list of previous insns, - each of which contains a SET for a REG that is used in this insn - and not used or set in between. LOG_LINKs never cross basic blocks. - They were set up by the preceding pass (lifetime analysis). - - We try to combine each pair of insns joined by a logical link. - We also try to combine triples of insns A, B and C when - C has a link back to B and B has a link back to A. - - LOG_LINKS does not have links for use of the CC0. They don't - need to, because the insn that sets the CC0 is always immediately - before the insn that tests it. So we always regard a branch - insn as having a logical link to the preceding insn. - - We check (with use_crosses_set_p) to avoid combining in such a way - as to move a computation to a place where its value would be different. - - Combination is done by mathematically substituting the previous - insn(s) values for the regs they set into the expressions in - the later insns that refer to these regs. If the result is a valid insn - for our target machine, according to the machine description, - we install it, delete the earlier insns, and update the data flow - information (LOG_LINKS and REG_NOTES) for what we did. - - To simplify substitution, we combine only when the earlier insn(s) - consist of only a single assignment. To simplify updating afterward, - we never combine when a subroutine call appears in the middle. - - Since we do not represent assignments to CC0 explicitly except when that - is all an insn does, there is no LOG_LINKS entry in an insn that uses - the condition code for the insn that set the condition code. - Fortunately, these two insns must be consecutive. - Therefore, every JUMP_INSN is taken to have an implicit logical link - to the preceding insn. This is not quite right, since non-jumps can - also use the condition code; but in practice such insns would not - combine anyway. */ - -#include - -#include "config.h" -#include "rtl.h" -#include "flags.h" -#include "regs.h" -#include "basic-block.h" -#include "insn-config.h" -#include "recog.h" - -#define max(A,B) ((A) > (B) ? (A) : (B)) -#define min(A,B) ((A) < (B) ? (A) : (B)) - -/* It is not safe to use ordinary gen_lowpart in combine. - Use gen_lowpart_for_combine instead. See comments there. */ -#define gen_lowpart dont_use_gen_lowpart_you_dummy - -/* Number of attempts to combine instructions in this function. */ - -static int combine_attempts; -static int distrib_attempts; - -/* Number of attempts that got as far as substitution in this function. */ - -static int combine_merges; -static int distrib_merges_1, distrib_merges_2; - -/* Number of instructions combined with added SETs in this function. */ - -static int combine_extras; - -/* Number of instructions combined in this function. */ - -static int combine_successes; -static int distrib_successes; - -/* Totals over entire compilation. */ - -static int total_attempts, total_merges, total_extras, total_successes; -static int total_distrib_attempts, total_distrib_merges_1, total_distrib_merges_2, total_distrib_successes; - - -/* Vector mapping INSN_UIDs to cuids. - The cuids are like uids but increase monononically always. - Combine always uses cuids so that it can compare them. - But actually renumbering the uids, which we used to do, - proves to be a bad idea because it makes it hard to compare - the dumps produced by earlier passes with those from later passes. */ - -static int *uid_cuid; - -/* Get the cuid of an insn. */ - -#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)]) - - -/* Record last point of death of (hard or pseudo) register n. */ - -static rtx *reg_last_death; - -/* Record last point of modification of (hard or pseudo) register n. */ - -static rtx *reg_last_set; - -/* Record the cuid of the last insn that invalidated memory - (anything that writes memory, and subroutine calls). */ - -static int mem_last_set; - -/* Record the cuid of the last CALL_INSN - so we can tell whether a potential combination crosses any calls. */ - -static int last_call_cuid; - -/* When `subst' is called, this is the insn that is being modified - (by combining in a previous insn). The PATTERN of this insn - is still the old pattern partially modified and it should not be - looked at, but this may be used to examine the successors of the insn - to judge whether a simplification is valid. */ - -static rtx subst_insn; - -/* Record one modification to rtl structure - to be undone by storing old_contents into *where. - is_int is 1 if the contents are an int. */ - -struct undo -{ - rtx *where; - rtx old_contents; - int is_int; -}; - -struct undo_int -{ - int *where; - int old_contents; - int is_int; -}; - -/* Record a bunch of changes to be undone, up to MAX_UNDO of them. - num_undo says how many are currently recorded. - storage is nonzero if we must undo the allocation of new storage. - The value of storage is what to pass to obfree. */ - -#define MAX_UNDO 10 - -struct undobuf -{ - int num_undo; - char *storage; - struct undo undo[MAX_UNDO]; -}; - -static struct undobuf undobuf; - -/* Number of times the pseudo being substituted for - was found and replaced. */ - -static int n_occurrences; - -static void move_deaths (); -static void move_deaths_2 (); -void remove_death (); -static void record_dead_and_set_regs (); -int regno_dead_p (); -static int use_crosses_set_p (); -static int try_combine (); -static rtx try_distrib (); -static rtx subst (); -static void undo_all (); -static void copy_substitutions (); -static void add_links (); -static void remove_links (); -static void add_incs (); -static int adjacent_insns_p (); -static int check_asm_operands (); -static rtx simplify_and_const_int (); -static rtx gen_lowpart_for_combine (); -static void simplify_set_cc0_and (); - -/* Main entry point for combiner. F is the first insn of the function. - NREGS is the first unused pseudo-reg number. */ - -void -combine_instructions (f, nregs) - rtx f; - int nregs; -{ - register rtx insn; - register int i; - register rtx links, nextlinks; - rtx prev; - - combine_attempts = 0; - combine_merges = 0; - combine_extras = 0; - combine_successes = 0; - distrib_attempts = 0; - distrib_merges_1 = 0; - distrib_merges_2 = 0; - distrib_successes = 0; - - reg_last_death = (rtx *) alloca (nregs * sizeof (rtx)); - reg_last_set = (rtx *) alloca (nregs * sizeof (rtx)); - bzero (reg_last_death, nregs * sizeof (rtx)); - bzero (reg_last_set, nregs * sizeof (rtx)); - - init_recog (); - - /* Compute maximum uid value so uid_cuid can be allocated. */ - - for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) - if (INSN_UID (insn) > i) - i = INSN_UID (insn); - - uid_cuid = (int *) alloca ((i + 1) * sizeof (int)); - - /* Compute the mapping from uids to cuids. - Cuids are numbers assigned to insns, like uids, - except that cuids increase monotonically through the code. */ - - for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) - INSN_CUID (insn) = ++i; - - /* Now scan all the insns in forward order. */ - - last_call_cuid = 0; - mem_last_set = 0; - prev = 0; - - for (insn = f; insn; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == INSN - || GET_CODE (insn) == CALL_INSN - || GET_CODE (insn) == JUMP_INSN) - { - retry: - /* Try this insn with each insn it links back to. */ - - for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) - if (try_combine (insn, XEXP (links, 0), 0)) - goto retry; - - /* Try each sequence of three linked insns ending with this one. */ - - for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) - if (GET_CODE (XEXP (links, 0)) != NOTE) - for (nextlinks = LOG_LINKS (XEXP (links, 0)); nextlinks; - nextlinks = XEXP (nextlinks, 1)) - if (try_combine (insn, XEXP (links, 0), XEXP (nextlinks, 0))) - goto retry; - - /* Try to combine a jump insn that uses CC0 - with a preceding insn that sets CC0, and maybe with its - logical predecessor as well. - This is how we make decrement-and-branch insns. - We need this special code because data flow connections - via CC0 do not get entered in LOG_LINKS. */ - - if (GET_CODE (insn) == JUMP_INSN - && prev != 0 - && GET_CODE (prev) == INSN - && GET_CODE (PATTERN (prev)) == SET - && GET_CODE (SET_DEST (PATTERN (prev))) == CC0) - { - if (try_combine (insn, prev, 0)) - goto retry; - - if (GET_CODE (prev) != NOTE) - for (nextlinks = LOG_LINKS (prev); nextlinks; - nextlinks = XEXP (nextlinks, 1)) - if (try_combine (insn, prev, XEXP (nextlinks, 0))) - goto retry; - } - - /* Try to apply the distributive law to this insn - and two insns that compute the operands of this one. */ - for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) - if (GET_CODE (XEXP (links, 0)) != NOTE) - for (nextlinks = XEXP (links, 1); nextlinks; nextlinks = XEXP (nextlinks, 1)) - if (GET_CODE (XEXP (nextlinks, 0)) != NOTE) - { - rtx try_from = 0; - - if (GET_CODE (PATTERN (XEXP (links, 0))) == SET - && find_reg_note (insn, REG_DEAD, SET_DEST (PATTERN (XEXP (links, 0)))) - && GET_CODE (PATTERN (XEXP (nextlinks, 0))) == SET - && find_reg_note (insn, REG_DEAD, SET_DEST (PATTERN (XEXP (nextlinks, 0))))) - try_from = try_distrib (insn, XEXP (links, 0), XEXP (nextlinks, 0)); - if (try_from != 0) - { - insn = try_from; - goto retry; - } - } -#if 0 -/* Turned off because on 68020 it takes four insns to make - something like (a[b / 32] & (1 << (31 - (b % 32)))) != 0 - that could actually be optimized, and that's an unlikely piece of code. */ - /* If an insn gets or sets a bit field, try combining it - with two different insns whose results it uses. */ - if (GET_CODE (insn) == INSN - && GET_CODE (PATTERN (insn)) == SET - && (GET_CODE (SET_DEST (PATTERN (insn))) == ZERO_EXTRACT - || GET_CODE (SET_DEST (PATTERN (insn))) == SIGN_EXTRACT - || GET_CODE (SET_SRC (PATTERN (insn))) == ZERO_EXTRACT - || GET_CODE (SET_SRC (PATTERN (insn))) == SIGN_EXTRACT)) - { - for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) - if (GET_CODE (XEXP (links, 0)) != NOTE) - for (nextlinks = XEXP (links, 1); nextlinks; - nextlinks = XEXP (nextlinks, 1)) - if (try_combine (insn, XEXP (links, 0), XEXP (nextlinks, 0))) - goto retry; - } -#endif - if (GET_CODE (insn) != NOTE) - record_dead_and_set_regs (insn); - prev = insn; - } - else if (GET_CODE (insn) != NOTE) - prev = 0; - } - total_attempts += combine_attempts; - total_merges += combine_merges; - total_extras += combine_extras; - total_successes += combine_successes; -} - -/* Try to combine the insns I1 and I2 into I3. - Here I1 appears earlier than I2, which is earlier than I3. - I1 can be zero; then we combine just I2 into I3. - - Return 1 if successful; if that happens, I1 and I2 are pseudo-deleted - by turning them into NOTEs, and I3 is modified. - Return 0 if the combination does not work. Then nothing is changed. */ - -static int -try_combine (i3, i2, i1) - register rtx i3, i2, i1; -{ - register rtx newpat; - int added_sets_1 = 0; - int added_sets_2 = 0; - int total_sets; - int i2_is_used; - register rtx link; - int insn_code_number; - rtx i2dest, i2src; - rtx i1dest, i1src; - int maxreg; - rtx temp; - int i; - - combine_attempts++; - - /* Don't combine with something already used up by combination. */ - - if (GET_CODE (i2) == NOTE - || (i1 && GET_CODE (i1) == NOTE)) - return 0; - - /* Don't combine across a CALL_INSN, because that would possibly - change whether the life span of some REGs crosses calls or not, - and it is a pain to update that information. */ - - if (INSN_CUID (i2) < last_call_cuid - || (i1 && INSN_CUID (i1) < last_call_cuid)) - return 0; - - /* Can combine only if previous insn is a SET of a REG, a SUBREG or CC0. - That REG must be either set or dead by the final instruction - (so that we can safely forget about setting it). - Also test use_crosses_set_p to make sure that the value - that is to be substituted for the register - does not use any registers whose values alter in between. - Do not try combining with moves from one register to another - since it is better to let them be tied by register allocation. - (There is a switch to permit such combination; except the insns - that copy a function value into another register are never combined - because moving that too far away from the function call could cause - something else to be stored in that register in the interim.) - - A set of a SUBREG is considered as if it were a set from - SUBREG. Thus, (SET (SUBREG:X (REG:Y...)) (something:X...)) - is handled by substituting (SUBREG:Y (something:X...)) for (REG:Y...). */ - - if (GET_CODE (PATTERN (i2)) != SET) - return 0; - i2dest = SET_DEST (PATTERN (i2)); - i2src = SET_SRC (PATTERN (i2)); - if (GET_CODE (i2dest) == SUBREG) - { - i2dest = SUBREG_REG (i2dest); - i2src = gen_rtx (SUBREG, GET_MODE (i2dest), i2src, 0); - } - /* Don't eliminate a store in the stack pointer. */ - if (i2dest == stack_pointer_rtx) - return 0; - /* Don't install a subreg involving two modes not tieable. - It can worsen register allocation, and can even make invalid reload insns, - since the reg inside may need to be copied from in the outside mode, - and that may be invalid if it is an fp reg copied in integer mode. */ - if (GET_CODE (i2src) == SUBREG - && ! MODES_TIEABLE_P (GET_MODE (i2src), GET_MODE (SUBREG_REG (i2src)))) - return 0; - if (GET_CODE (i2dest) != CC0 - && (GET_CODE (i2dest) != REG - || (GET_CODE (i2src) == REG - /* Do allow the combination of y = x; x = y; (with x dead) - because the result will turn into nothing. */ - && !(GET_CODE (PATTERN (i3)) == SET - && i2src == SET_DEST (PATTERN (i3))) - && (!flag_combine_regs - /* Don't substitute a function value reg for any other. */ - || FUNCTION_VALUE_REGNO_P (REGNO (i2src)))) - || GET_CODE (i2src) == CALL - /* Don't substitute into an incremented register. */ - || find_reg_note (i3, REG_INC, i2dest) - || use_crosses_set_p (i2src, INSN_CUID (i2)))) - return 0; - if (GET_CODE (i2src) == ASM_OPERANDS && MEM_VOLATILE_P (i2src)) - return 0; - /* Don't substitute for a register intended as a clobberable operand. */ - if (GET_CODE (PATTERN (i3)) == PARALLEL) - for (i = 0; i < XVECLEN (PATTERN (i3), 0); i++) - if (GET_CODE (XVECEXP (PATTERN (i3), 0, i)) == CLOBBER - && XEXP (XVECEXP (PATTERN (i3), 0, i), 0) == i2dest) - return 0; - - if (i1 != 0) - { - if (GET_CODE (PATTERN (i1)) != SET) - return 0; - i1dest = SET_DEST (PATTERN (i1)); - i1src = SET_SRC (PATTERN (i1)); - if (GET_CODE (i1dest) == SUBREG) - { - i1dest = SUBREG_REG (i1dest); - i1src = gen_rtx (SUBREG, GET_MODE (i1dest), i1src, 0); - } - if (i1dest == stack_pointer_rtx) - return 0; - if (GET_CODE (i1src) == SUBREG - && ! MODES_TIEABLE_P (GET_MODE (i1src), - GET_MODE (SUBREG_REG (i1src)))) - return 0; - if (GET_CODE (i1dest) != CC0 - && (GET_CODE (i1dest) != REG - || (GET_CODE (i1src) == REG - && (!flag_combine_regs - || FUNCTION_VALUE_REGNO_P (REGNO (i1src)))) - || GET_CODE (i1src) == CALL - || find_reg_note (i3, REG_INC, i1dest) - || find_reg_note (i2, REG_INC, i1dest) - || use_crosses_set_p (i1src, INSN_CUID (i1)))) - return 0; - if (GET_CODE (i1src) == ASM_OPERANDS && MEM_VOLATILE_P (i1src)) - return 0; - /* Don't substitute for a register intended as a clobberable operand. */ - if (GET_CODE (PATTERN (i3)) == PARALLEL) - for (i = 0; i < XVECLEN (PATTERN (i3), 0); i++) - if (GET_CODE (XVECEXP (PATTERN (i3), 0, i)) == CLOBBER - && XEXP (XVECEXP (PATTERN (i3), 0, i), 0) == i1dest) - return 0; - } - - /* If it is better that two different modes keep two different pseudos, - avoid combining them. */ - if (GET_CODE (PATTERN (i3)) == SET) - { - rtx i3dest = SET_DEST (PATTERN (i3)); - while (GET_CODE (i3dest) == SUBREG - || GET_CODE (i3dest) == STRICT_LOW_PART - || GET_CODE (i3dest) == SIGN_EXTRACT - || GET_CODE (i3dest) == ZERO_EXTRACT) - i3dest = SUBREG_REG (i3dest); - - if (SET_SRC (PATTERN (i3)) == i2dest - && GET_CODE (i3dest) == REG - && ! MODES_TIEABLE_P (GET_MODE (i2dest), GET_MODE (i3dest))) - return 0; - } - - /* If I2 contains anything volatile, reject, unless nothing - volatile comes between it and I3. */ - if (volatile_refs_p (PATTERN (i2))) - { - rtx insn; - for (insn = NEXT_INSN (i2); insn != i3; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN - || GET_CODE (insn) == JUMP_INSN) - if (volatile_refs_p (PATTERN (insn))) - return 0; - } - /* Likewise for I1; nothing volatile can come between it and I3, - except optionally I2. */ - if (i1 && volatile_refs_p (PATTERN (i1))) - { - rtx insn; - rtx end = (volatile_refs_p (PATTERN (i2)) ? i2 : i3); - for (insn = NEXT_INSN (i1); insn != end; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN - || GET_CODE (insn) == JUMP_INSN) - if (volatile_refs_p (PATTERN (insn))) - return 0; - } - - /* If I1 or I2 contains an autoincrement or autodecrement, - make sure that register is not used between there and I3, - and not already used in I3 either. - Also insist that I3 not be a jump; if it were one - and the incremented register were spilled, we would lose. */ - for (link = REG_NOTES (i2); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_INC - && (GET_CODE (i3) == JUMP_INSN - || reg_used_between_p (XEXP (link, 0), i2, i3) - || reg_mentioned_p (XEXP (link, 0), PATTERN (i3)))) - return 0; - - if (i1) - for (link = REG_NOTES (i1); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_INC - && (GET_CODE (i3) == JUMP_INSN - || reg_used_between_p (XEXP (link, 0), i1, i3) - || reg_mentioned_p (XEXP (link, 0), PATTERN (i3)))) - return 0; - - /* If I3 has an inc, then give up if I1 or I2 uses the reg that is inc'd, - EXCEPT in one case: I3 has a post-inc in an output operand. */ - if (!(GET_CODE (PATTERN (i3)) == SET - && GET_CODE (SET_SRC (PATTERN (i3))) == REG - && GET_CODE (SET_DEST (PATTERN (i3))) == MEM - && (GET_CODE (XEXP (SET_DEST (PATTERN (i3)), 0)) == POST_INC - || GET_CODE (XEXP (SET_DEST (PATTERN (i3)), 0)) == POST_DEC))) - /* It's not the exception. */ - for (link = REG_NOTES (i3); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_INC - && (reg_mentioned_p (XEXP (link, 0), PATTERN (i2)) - || (i1 != 0 - && reg_mentioned_p (XEXP (link, 0), PATTERN (i1))))) - return 0; - - /* Don't combine an insn I1 or I2 that follows a CC0-setting insn. - An insn that uses CC0 must not be separated from the one that sets it. - It would be more logical to test whether CC0 occurs inside I1 or I2, - but that would be much slower, and this ought to be equivalent. */ - temp = PREV_INSN (i2); - while (temp && GET_CODE (temp) == NOTE) - temp = PREV_INSN (temp); - if (temp && GET_CODE (temp) == INSN && sets_cc0_p (PATTERN (temp))) - return 0; - if (i1) - { - temp = PREV_INSN (i2); - while (temp && GET_CODE (temp) == NOTE) - temp = PREV_INSN (temp); - if (temp && GET_CODE (temp) == INSN && sets_cc0_p (PATTERN (temp))) - return 0; - } - - /* See if the SETs in i1 or i2 need to be kept around in the merged - instruction: whenever the value set there is still needed past i3. */ - added_sets_2 = (GET_CODE (i2dest) != CC0 - && ! dead_or_set_p (i3, i2dest)); - if (i1) - added_sets_1 = ! (dead_or_set_p (i3, i1dest) - || dead_or_set_p (i2, i1dest)); - - combine_merges++; - - undobuf.num_undo = 0; - undobuf.storage = 0; - - /* Substitute in the latest insn for the regs set by the earlier ones. */ - - maxreg = max_reg_num (); - - subst_insn = i3; - n_occurrences = 0; /* `subst' counts here */ - - newpat = subst (PATTERN (i3), i2dest, i2src); - /* Record whether i2's body now appears within i3's body. */ - i2_is_used = n_occurrences; - - if (i1) - { - n_occurrences = 0; - newpat = subst (newpat, i1dest, i1src); - } - - if (GET_CODE (PATTERN (i3)) == SET - && SET_DEST (PATTERN (i3)) == cc0_rtx - && (GET_CODE (SET_SRC (PATTERN (i3))) == AND - || GET_CODE (SET_SRC (PATTERN (i3))) == LSHIFTRT) - && next_insn_tests_no_inequality (i3)) - simplify_set_cc0_and (i3); - - if (max_reg_num () != maxreg) - abort (); - - /* If the actions of the earler insns must be kept - in addition to substituting them into the latest one, - we must make a new PARALLEL for the latest insn - to hold additional the SETs. */ - - if (added_sets_1 || added_sets_2) - { - combine_extras++; - - /* Arrange to free later what we allocate now - if we don't accept this combination. */ - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - - if (GET_CODE (newpat) == PARALLEL) - { - rtvec old = XVEC (newpat, 0); - total_sets = XVECLEN (newpat, 0) + added_sets_1 + added_sets_2; - newpat = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (total_sets)); - bcopy (&old->elem[0], &XVECEXP (newpat, 0, 0), - sizeof (old->elem[0]) * old->num_elem); - } - else - { - rtx old = newpat; - total_sets = 1 + added_sets_1 + added_sets_2; - newpat = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (total_sets)); - XVECEXP (newpat, 0, 0) = old; - } - if (added_sets_1) - { - XVECEXP (newpat, 0, --total_sets) = PATTERN (i1); - } - if (added_sets_2) - { - /* If there is no I1, use I2's body as is. */ - if (i1 == 0 - /* If I2 was stuck into I3, then anything within it has - already had I1 substituted into it when that was done to I3. */ - || i2_is_used) - { - XVECEXP (newpat, 0, --total_sets) = PATTERN (i2); - } - else - XVECEXP (newpat, 0, --total_sets) - = subst (PATTERN (i2), i1dest, i1src); - } - } - - /* Fail if an autoincrement side-effect has been duplicated. */ - if ((i2_is_used > 1 && find_reg_note (i2, REG_INC, 0) != 0) - || (i1 != 0 && n_occurrences > 1 && find_reg_note (i1, REG_INC, 0) != 0)) - { - undo_all (); - return 0; - } - - /* Is the result of combination a valid instruction? */ - insn_code_number = recog (newpat, i3); - - if (insn_code_number >= 0 - /* Is the result a reasonable ASM_OPERANDS? */ - || (check_asm_operands (newpat) && ! added_sets_1 && ! added_sets_2)) - { - /* Yes. Install it. */ - register int regno; - INSN_CODE (i3) = insn_code_number; - PATTERN (i3) = newpat; - /* If anything was substituted more than once, - copy it to avoid invalid shared rtl structure. */ - copy_substitutions (); - /* The data flowing into I2 now flows into I3. - But we cannot always move all of I2's LOG_LINKS into I3, - since they must go to a setting of a REG from the - first use following. If I2 was the first use following a set, - I3 is now a use, but it is not the first use - if some instruction between I2 and I3 is also a use. - Here, for simplicity, we move all the links only if - there are no real insns between I2 and I3. - Otherwise, we move only links that correspond to regs - that used to die in I2. They are always safe to move. */ - add_links (i3, i2, adjacent_insns_p (i2, i3)); - /* Most REGs that previously died in I2 now die in I3. */ - move_deaths (i2src, INSN_CUID (i2), i3); - if (GET_CODE (i2dest) == REG) - { - /* If the reg formerly set in I2 died only once and that was in I3, - zero its use count so it won't make `reload' do any work. */ - regno = REGNO (i2dest); - if (! added_sets_2) - { - reg_n_sets[regno]--; - /* Used to check && regno_dead_p (regno, i3) also here. */ - if (reg_n_sets[regno] == 0 - && ! (basic_block_live_at_start[0][regno / HOST_BITS_PER_INT] - & (1 << (regno % HOST_BITS_PER_INT)))) - reg_n_refs[regno] = 0; - } - /* If a ref to REGNO was substituted into I3 from I2, - then it still dies there if it previously did. - Otherwise either REGNO never did die in I3 so remove_death is safe - or this entire life of REGNO is gone so remove its death. */ - if (!added_sets_2 - && ! reg_mentioned_p (i2dest, PATTERN (i3))) - remove_death (regno, i3); - } - /* Any registers previously autoincremented in I2 - are now incremented in I3. */ - add_incs (i3, REG_NOTES (i2)); - if (i1) - { - /* Likewise, merge the info from I1 and get rid of it. */ - add_links (i3, i1, - adjacent_insns_p (i1, i2) && adjacent_insns_p (i2, i3)); - move_deaths (i1src, INSN_CUID (i1), i3); - if (GET_CODE (i1dest) == REG) - { - regno = REGNO (i1dest); - if (! added_sets_1) - { - reg_n_sets[regno]--; - /* Used to also check && regno_dead_p (regno, i3) here. */ - - if (reg_n_sets[regno] == 0 - && ! (basic_block_live_at_start[0][regno / HOST_BITS_PER_INT] - & (1 << (regno % HOST_BITS_PER_INT)))) - - reg_n_refs[regno] = 0; - } - /* If a ref to REGNO was substituted into I3 from I1, - then it still dies there if it previously did. - Else either REGNO never did die in I3 so remove_death is safe - or this entire life of REGNO is gone so remove its death. */ - if (! added_sets_1 - && ! reg_mentioned_p (i1dest, PATTERN (i3))) - remove_death (regno, i3); - } - add_incs (i3, REG_NOTES (i1)); - LOG_LINKS (i1) = 0; - PUT_CODE (i1, NOTE); - NOTE_LINE_NUMBER (i1) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (i1) = 0; - } - /* Get rid of I2. */ - LOG_LINKS (i2) = 0; - PUT_CODE (i2, NOTE); - NOTE_LINE_NUMBER (i2) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (i2) = 0; - - combine_successes++; - return 1; - } - - /* Failure: change I3 back the way it was. */ - undo_all (); - - return 0; -} - -/* Undo all the modifications recorded in undobuf. */ - -static void -undo_all () -{ - register int i; - if (undobuf.num_undo > MAX_UNDO) - undobuf.num_undo = MAX_UNDO; - for (i = undobuf.num_undo - 1; i >= 0; i--) - *undobuf.undo[i].where = undobuf.undo[i].old_contents; - if (undobuf.storage) - obfree (undobuf.storage); - undobuf.num_undo = 0; - undobuf.storage = 0; -} - -/* If this insn had more than one substitution, - copy all but one, so that no invalid shared substructure is introduced. */ - -static void -copy_substitutions () -{ - register int i; - if (undobuf.num_undo > 1) - { - for (i = undobuf.num_undo - 1; i >= 1; i--) - if (! undobuf.undo[i].is_int) - *undobuf.undo[i].where = copy_rtx (*undobuf.undo[i].where); - } -} - -/* Throughout X, replace FROM with TO, and return the result. - The result is TO if X is FROM; - otherwise the result is X, but its contents may have been modified. - If they were modified, a record was made in undobuf so that - undo_all will (among other things) return X to its original state. - - If the number of changes necessary is too much to record to undo, - the excess changes are not made, so the result is invalid. - The changes already made can still be undone. - undobuf.num_undo is incremented for such changes, so by testing that - the caller can tell whether the result is valid. - - `n_occurrences' is incremented each time FROM is replaced. */ - -static rtx -subst (x, from, to) - register rtx x, from, to; -{ - register char *fmt; - register int len, i; - register enum rtx_code code; - char was_replaced[2]; - -#define SUBST(INTO, NEWVAL) \ - do { if (undobuf.num_undo < MAX_UNDO) \ - { \ - undobuf.undo[undobuf.num_undo].where = &INTO; \ - undobuf.undo[undobuf.num_undo].old_contents = INTO; \ - undobuf.undo[undobuf.num_undo].is_int = 0; \ - INTO = NEWVAL; \ - } \ - undobuf.num_undo++; } while (0) - -#define SUBST_INT(INTO, NEWVAL) \ - do { if (undobuf.num_undo < MAX_UNDO) \ - { \ - struct undo_int *u = (struct undo_int *)&undobuf.undo[undobuf.num_undo];\ - u->where = &INTO; \ - u->old_contents = INTO; \ - u->is_int = 1; \ - INTO = NEWVAL; \ - } \ - undobuf.num_undo++; } while (0) - -/* FAKE_EXTEND_SAFE_P (MODE, FROM) is 1 if (subreg:MODE FROM 0) is a safe - replacement for (zero_extend:MODE FROM) or (sign_extend:MODE FROM). - If it is 0, that cannot be done. We can now do this for any MEM - because (SUBREG (MEM...)) is guaranteed to cause the MEM to be reloaded. - If not for that, MEM's would very rarely be safe. */ - -/* Reject MODEs bigger than a word, because we might not be able - to reference a two-register group starting with an arbitrary register - (and currently gen_lowpart might crash for a SUBREG). */ - -#define FAKE_EXTEND_SAFE_P(MODE, FROM) \ - (GET_MODE_SIZE (MODE) <= UNITS_PER_WORD \ - && (GET_CODE (FROM) == REG || GET_CODE (FROM) == SUBREG \ - || GET_CODE (FROM) == MEM)) - - if (x == from) - return to; - - /* It is possible to have a subexpression appear twice in the insn. - Suppose that FROM is a register that appears within TO. - Then, after that subexpression has been scanned once by `subst', - the second time it is scanned, TO may be found. If we were - to scan TO here, we would find FROM within it and create a - self-referent rtl structure which is completely wrong. */ - if (x == to) - return to; - - code = GET_CODE (x); - - /* A little bit of algebraic simplification here. */ - switch (code) - { - /* This case has no effect except to speed things up. */ - case REG: - case CONST_INT: - case CONST: - case SYMBOL_REF: - case LABEL_REF: - case PC: - case CC0: - return x; - } - - was_replaced[0] = 0; - was_replaced[1] = 0; - - len = GET_RTX_LENGTH (code); - fmt = GET_RTX_FORMAT (code); - - /* Don't replace FROM where it is being stored in rather than used. */ - if (code == SET && SET_DEST (x) == from) - fmt = "ie"; - if (code == SET && GET_CODE (SET_DEST (x)) == SUBREG - && SUBREG_REG (SET_DEST (x)) == from) - fmt = "ie"; - - for (i = 0; i < len; i++) - { - if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - { - register rtx new; - if (XVECEXP (x, i, j) == from) - new = to, n_occurrences++; - else - new = subst (XVECEXP (x, i, j), from, to); - if (new != XVECEXP (x, i, j)) - SUBST (XVECEXP (x, i, j), new); - } - } - else if (fmt[i] == 'e') - { - register rtx new; - - if (XEXP (x, i) == from) - { - new = to; - n_occurrences++; - if (i < 2) - was_replaced[i] = 1; - } - else - new = subst (XEXP (x, i), from, to); - - if (new != XEXP (x, i)) - SUBST (XEXP (x, i), new); - } - } - - /* A little bit of algebraic simplification here. */ - switch (code) - { - case SUBREG: - /* Changing mode twice with SUBREG => just change it once, - or not at all if changing back to starting mode. */ - if (SUBREG_REG (x) == to - && GET_CODE (to) == SUBREG) - { - if (GET_MODE (x) == GET_MODE (SUBREG_REG (to))) - if (SUBREG_WORD (x) == 0 && SUBREG_WORD (to) == 0) - return SUBREG_REG (to); - SUBST (SUBREG_REG (x), SUBREG_REG (to)); - if (SUBREG_WORD (to) != 0) - SUBST_INT (SUBREG_WORD (x), SUBREG_WORD (x) + SUBREG_WORD (to)); - } - if (SUBREG_REG (x) == to - && (GET_CODE (to) == SIGN_EXTEND || GET_CODE (to) == ZERO_EXTEND) - && subreg_lowpart_p (x)) - { - /* (subreg (sign_extend X)) is X, if it has same mode as X. */ - if (GET_MODE (x) == GET_MODE (XEXP (to, 0))) - return XEXP (to, 0); - /* (subreg (sign_extend X)), if it has a mode wider than X, - can be done with (sign_extend X). */ - if (GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (XEXP (to, 0)))) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (GET_CODE (to), GET_MODE (x), XEXP (to, 0)); - } - /* Extend and then truncate smaller than it was to start with: - no need to extend. */ - if (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (GET_MODE (XEXP (to, 0)))) - { - SUBST (XEXP (x, 0), XEXP (to, 0)); - } - } - /* (subreg:A (mem:B X) N) becomes a modified MEM. - If we can't do that safely, then it becomes something nonsensical - so that this combination won't take place. - This avoids producing any (subreg (mem))s except in the special - paradoxical case where gen_lowpart_for_combine makes them. */ - if (SUBREG_REG (x) == to - && GET_CODE (to) == MEM) - { - int endian_offset = 0; - /* Don't combine this if mode A is wider than B. */ - if (GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (to))) - return gen_rtx (CLOBBER, VOIDmode, const0_rtx); - /* Don't change the mode of the MEM - if that would change the meaning of the address. */ - if (mode_dependent_address_p (XEXP (to, 0))) - return gen_rtx (CLOBBER, VOIDmode, const0_rtx); -#ifdef BYTES_BIG_ENDIAN - if (GET_MODE_SIZE (GET_MODE (x)) < UNITS_PER_WORD) - endian_offset += UNITS_PER_WORD - GET_MODE_SIZE (GET_MODE (x)); - if (GET_MODE_SIZE (GET_MODE (to)) < UNITS_PER_WORD) - endian_offset -= UNITS_PER_WORD - GET_MODE_SIZE (GET_MODE (to)); -#endif - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - /* Note if the plus_constant doesn't make a valid address - then this combination won't be accepted. */ - return gen_rtx (MEM, GET_MODE (x), - plus_constant (XEXP (to, 0), - (SUBREG_WORD (x) * UNITS_PER_WORD - + endian_offset))); - } - break; - - case NOT: - /* (not (minus X 1)) can become (neg X). */ - if (was_replaced[0] - && ((GET_CODE (to) == PLUS && INTVAL (XEXP (to, 1)) == -1) - || (GET_CODE (to) == MINUS && XEXP (to, 1) == const1_rtx))) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (NEG, GET_MODE (to), XEXP (to, 0)); - } - /* Don't let substitution introduce double-negatives. */ - if (was_replaced[0] - && GET_CODE (to) == code) - return XEXP (to, 0); - break; - - case NEG: - /* (neg (minus X Y)) can become (minus Y X). */ - if (was_replaced[0] && GET_CODE (to) == MINUS) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (MINUS, GET_MODE (to), - XEXP (to, 1), XEXP (to, 0)); - } - /* Don't let substitution introduce double-negatives. */ - if (was_replaced[0] - && GET_CODE (to) == code) - return XEXP (to, 0); - break; - - case FLOAT_TRUNCATE: - /* (float_truncate:SF (float_extend:DF foo:SF)) = foo:SF. */ - if (was_replaced[0] - && GET_CODE (to) == FLOAT_EXTEND - && GET_MODE (XEXP (to, 0)) == GET_MODE (x)) - return XEXP (to, 0); - break; - -#if 0 - case COMPARE: - /* -x>0 if 0>x. */ - if (GET_CODE (XEXP (x, 0)) == NEG && XEXP (x, 1) == const0_rtx) - { - SUBST (XEXP (x, 1), XEXP (XEXP (x, 0), 0)); - SUBST (XEXP (x, 0), const0_rtx); - } - if (GET_CODE (XEXP (x, 1)) == NEG && XEXP (x, 0) == const0_rtx) - { - SUBST (XEXP (x, 0), XEXP (XEXP (x, 1), 0)); - SUBST (XEXP (x, 1), const0_rtx); - } - break; -#endif - - case PLUS: -#if 0 /* Turned off for caution: turn it on after 1.36. */ - /* Identify constant sums as such. */ - if ((was_replaced[0] || was_replaced[1]) - && CONSTANT_P (XEXP (x, 0)) - && CONSTANT_P (XEXP (x, 1))) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (CONST, GET_MODE (x), x); - } -#endif - /* In (plus (ashift )) - change the shift to a multiply so we can recognize - scaled indexed addresses. */ - if ((was_replaced[0] - || was_replaced[1]) - && GET_CODE (to) == ASHIFT - && GET_CODE (XEXP (to, 1)) == CONST_INT - && INTVAL (XEXP (to, 1)) < HOST_BITS_PER_INT) - { - rtx temp; - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - temp = gen_rtx (MULT, GET_MODE (to), - XEXP (to, 0), - gen_rtx (CONST_INT, VOIDmode, - 1 << INTVAL (XEXP (to, 1)))); - if (was_replaced[0]) - SUBST (XEXP (x, 0), temp); - else - SUBST (XEXP (x, 1), temp); - } - /* (plus X (neg Y)) becomes (minus X Y). */ - if (GET_CODE (XEXP (x, 1)) == NEG) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (MINUS, GET_MODE (x), - XEXP (x, 0), XEXP (XEXP (x, 1), 0)); - } - /* (plus (neg X) Y) becomes (minus Y X). */ - if (GET_CODE (XEXP (x, 0)) == NEG) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (MINUS, GET_MODE (x), - XEXP (x, 1), XEXP (XEXP (x, 0), 0)); - } - /* (plus (plus x c1) c2) => (plus x c1+c2) */ - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && GET_CODE (XEXP (x, 0)) == PLUS - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) - { - int sum = (INTVAL (XEXP (x, 1)) - + INTVAL (XEXP (XEXP (x, 0), 1))); - if (sum == 0) - return XEXP (XEXP (x, 0), 0); - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - SUBST (XEXP (x, 1), gen_rtx (CONST_INT, VOIDmode, sum)); - SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0)); - break; - } - /* If we have something (putative index) being added to a sum, - associate it so that any constant term is outermost. - That's because that's the way indexed addresses are - now supposed to appear. */ - if (((was_replaced[0] && GET_CODE (XEXP (x, 1)) == PLUS) - || (was_replaced[1] && GET_CODE (XEXP (x, 0)) == PLUS)) - || - ((was_replaced[0] || was_replaced[1]) - && GET_CODE (to) == PLUS)) - { - rtx offset = 0, base, index; - if (GET_CODE (to) != PLUS) - { - index = to; - base = was_replaced[0] ? XEXP (x, 1) : XEXP (x, 0); - } - else - { - index = was_replaced[0] ? XEXP (x, 1) : XEXP (x, 0); - base = to; - } - if (CONSTANT_ADDRESS_P (XEXP (base, 0))) - { - offset = XEXP (base, 0); - base = XEXP (base, 1); - } - else if (CONSTANT_ADDRESS_P (XEXP (base, 1))) - { - offset = XEXP (base, 1); - base = XEXP (base, 0); - } - if (offset != 0) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - if (GET_CODE (offset) == CONST_INT) - return plus_constant (gen_rtx (PLUS, GET_MODE (index), - base, index), - INTVAL (offset)); - if (GET_CODE (index) == CONST_INT) - return plus_constant (gen_rtx (PLUS, GET_MODE (offset), - base, offset), - INTVAL (index)); - return gen_rtx (PLUS, GET_MODE (index), - gen_rtx (PLUS, GET_MODE (index), - base, index), - offset); - } - } - break; - - case EQ: - case NE: - /* If comparing a subreg against zero, discard the subreg. */ - if (was_replaced[0] - && GET_CODE (to) == SUBREG - && SUBREG_WORD (to) == 0 - && XEXP (x, 1) == const0_rtx) - SUBST (XEXP (x, 0), SUBREG_REG (to)); - - /* If comparing a ZERO_EXTRACT against zero, - canonicalize to a SIGN_EXTRACT, - since the two are equivalent here. */ - if (was_replaced[0] - && GET_CODE (to) == ZERO_EXTRACT - && XEXP (x, 1) == const0_rtx) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - SUBST (XEXP (x, 0), - gen_rtx (SIGN_EXTRACT, GET_MODE (to), - XEXP (to, 0), XEXP (to, 1), - XEXP (to, 2))); - } -#ifndef BITS_BIG_ENDIAN - /* If we are putting (ASHIFT 1 x) into (EQ (AND ... y) 0), - arrange to return (EQ (SIGN_EXTRACT y 1 x) 0), - which is what jump-on-bit instructions are written with. */ - else if (XEXP (x, 1) == const0_rtx - && GET_CODE (XEXP (x, 0)) == AND - && (XEXP (XEXP (x, 0), 0) == to - || XEXP (XEXP (x, 0), 1) == to) - && GET_CODE (to) == ASHIFT - && XEXP (to, 0) == const1_rtx) - { - register rtx y = XEXP (XEXP (x, 0), - XEXP (XEXP (x, 0), 0) == to); - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - SUBST (XEXP (x, 0), - gen_rtx (SIGN_EXTRACT, GET_MODE (to), - y, - const1_rtx, XEXP (to, 1))); - } -#endif /* not BITS_BIG_ENDIAN */ - /* Negation is a no-op before equality test against zero. */ - if (GET_CODE (XEXP (x, 0)) == NEG && XEXP (x, 1) == const0_rtx) - { - SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0)); - } - if (GET_CODE (XEXP (x, 1)) == NEG && XEXP (x, 0) == const0_rtx) - { - SUBST (XEXP (x, 1), XEXP (XEXP (x, 1), 0)); - } - break; - - case ZERO_EXTEND: - /* Nested zero-extends are equivalent to just one. */ - if (was_replaced[0] - && GET_CODE (to) == ZERO_EXTEND) - SUBST (XEXP (x, 0), XEXP (to, 0)); - /* Zero extending a constant int can be replaced - by a zero-extended constant. */ - if (was_replaced[0] - && HOST_BITS_PER_INT >= GET_MODE_BITSIZE (GET_MODE (from)) - && GET_CODE (to) == CONST_INT) - { - int intval = INTVAL (to) & GET_MODE_MASK (GET_MODE (from)); - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (CONST_INT, VOIDmode, intval); - } - /* Zero-extending the result of an and with a constant can be done - with a wider and. */ - if (was_replaced[0] - && GET_CODE (to) == AND - && GET_CODE (XEXP (to, 1)) == CONST_INT - && FAKE_EXTEND_SAFE_P (GET_MODE (x), XEXP (to, 0)) - /* Avoid getting wrong result if the constant has high bits set - that are irrelevant in the narrow mode where it is being used. */ - && 0 == (INTVAL (XEXP (to, 1)) - & ~ GET_MODE_MASK (GET_MODE (to)))) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (AND, GET_MODE (x), - gen_lowpart_for_combine (GET_MODE (x), XEXP (to, 0)), - XEXP (to, 1)); - } - /* Change (zero_extend:M (subreg:N (zero_extract:M ...) 0)) - to (zero_extract:M ...) if the field extracted fits in mode N. */ - if (GET_CODE (XEXP (x, 0)) == SUBREG - && GET_CODE (XEXP (XEXP (x, 0), 0)) == ZERO_EXTRACT - && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT - && (INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1)) - <= GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))))) - { - return XEXP (XEXP (x, 0), 0); - } - /* Change (zero_extend:M (subreg:N (and:M ... ) 0)) - to (and:M ...) if the significant bits fit in mode N. */ - if (GET_CODE (XEXP (x, 0)) == SUBREG - && SUBREG_REG (XEXP (x, 0)) == to - && SUBREG_WORD (XEXP (x, 0)) == 0 - && GET_CODE (to) == AND - && GET_CODE (XEXP (to, 1)) == CONST_INT - && FAKE_EXTEND_SAFE_P (GET_MODE (x), XEXP (to, 0)) - /* Avoid getting wrong result if the constant has high bits set - that are irrelevant in the narrow mode where it is being used. */ - && 0 == (INTVAL (XEXP (to, 1)) - & ~ GET_MODE_MASK (GET_MODE (to)))) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (AND, GET_MODE (x), - gen_lowpart_for_combine (GET_MODE (x), XEXP (to, 0)), - XEXP (to, 1)); - } - /* In (zero_extend:M (subreg:N (lshiftrt:M REG))), - where REG was assigned from (zero_extend:M (any:N ...)), - remove the outer zero extension. */ - if (GET_CODE (XEXP (x, 0)) == SUBREG - && SUBREG_REG (XEXP (x, 0)) == to - && SUBREG_WORD (XEXP (x, 0)) == 0 - && GET_CODE (to) == LSHIFTRT) - { - rtx tmp = XEXP (to, 0); - - /* See if arg of LSHIFTRT is a register whose value we can find. */ - if (GET_CODE (tmp) == REG) - if (reg_n_sets[REGNO (tmp)] == 1 - && reg_last_set[REGNO (tmp)] != 0 - && SET_DEST (PATTERN (reg_last_set[REGNO (tmp)])) == tmp) - tmp = SET_SRC (PATTERN (reg_last_set[REGNO (tmp)])); - else - break; - - if (GET_CODE (tmp) == ZERO_EXTEND - && GET_MODE (tmp) == GET_MODE (x) - && GET_MODE (XEXP (tmp, 0)) == GET_MODE (XEXP (x, 0))) - return SUBREG_REG (XEXP (x, 0)); - } - break; - - case SIGN_EXTEND: - /* Nested sign-extends are equivalent to just one. */ - if (was_replaced[0] - && GET_CODE (to) == SIGN_EXTEND) - SUBST (XEXP (x, 0), XEXP (to, 0)); - /* Sign extending a constant int can be replaced - by a sign-extended constant. */ - if (was_replaced[0] - && HOST_BITS_PER_INT >= GET_MODE_BITSIZE (GET_MODE (from)) - && GET_CODE (to) == CONST_INT) - { - int intval = INTVAL (to); - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - if (intval > 0 - && (intval & (1 << (GET_MODE_BITSIZE (GET_MODE (from)) - 1)))) - intval |= ~ GET_MODE_MASK (GET_MODE (from)); - return gen_rtx (CONST_INT, VOIDmode, intval); - } - /* Sign-extending the result of an and with a constant can be done - with a wider and, provided the high bit of the constant is 0. */ - if (was_replaced[0] - && GET_CODE (to) == AND - && GET_CODE (XEXP (to, 1)) == CONST_INT - && FAKE_EXTEND_SAFE_P (GET_MODE (x), XEXP (to, 0)) - && ((INTVAL (XEXP (to, 1)) - & (-1 << (GET_MODE_BITSIZE (GET_MODE (to)) - 1))) - == 0)) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (AND, GET_MODE (x), - gen_lowpart_for_combine (GET_MODE (x), XEXP (to, 0)), - XEXP (to, 1)); - } - /* hacks added by tiemann. */ - /* Change (sign_extend:M (subreg:N (and:M ... ) 0)) - to (and:M ...), provided the result fits in mode N, - and the high bit of the constant is 0 in mode N. */ - if (GET_CODE (XEXP (x, 0)) == SUBREG - && SUBREG_REG (XEXP (x, 0)) == to - && SUBREG_WORD (XEXP (x, 0)) == 0 - && GET_CODE (to) == AND - && GET_CODE (XEXP (to, 1)) == CONST_INT - && FAKE_EXTEND_SAFE_P (GET_MODE (x), XEXP (to, 0)) - && ((INTVAL (XEXP (to, 1)) - & (-1 << (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - 1))) - == 0)) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (AND, GET_MODE (x), - gen_lowpart_for_combine (GET_MODE (x), XEXP (to, 0)), - XEXP (to, 1)); - } - /* In (sign_extend:M (subreg:N (ashiftrt:M REG))), - where REG was assigned from (sign_extend:M (any:N ...)), - remove the outer sign extension. */ - if (GET_CODE (XEXP (x, 0)) == SUBREG - && SUBREG_REG (XEXP (x, 0)) == to - && SUBREG_WORD (XEXP (x, 0)) == 0 - && GET_CODE (to) == ASHIFTRT) - { - rtx tmp = XEXP (to, 0); - - /* See if arg of LSHIFTRT is a register whose value we can find. */ - if (GET_CODE (tmp) == REG) - if (reg_n_sets[REGNO (tmp)] == 1 - && reg_last_set[REGNO (tmp)] != 0 - && SET_DEST (PATTERN (reg_last_set[REGNO (tmp)])) == tmp) - tmp = SET_SRC (PATTERN (reg_last_set[REGNO (tmp)])); - else - break; - - if (GET_CODE (tmp) == SIGN_EXTEND - && GET_MODE (tmp) == GET_MODE (x) - && GET_MODE (XEXP (tmp, 0)) == GET_MODE (XEXP (x, 0))) - return SUBREG_REG (XEXP (x, 0)); - } - break; - - case SET: - /* In (set (zero-extract ) (and <(2**n-1) | anything>)) - the `and' can be deleted. This can happen when storing a bit - that came from a set-flag insn followed by masking to one bit. */ - if (GET_CODE (XEXP (x, 0)) == ZERO_EXTRACT - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && was_replaced[1] - && GET_CODE (to) == AND - && GET_CODE (XEXP (to, 1)) == CONST_INT - && 0 == (((1 << INTVAL (XEXP (XEXP (x, 0), 1))) - 1) - & ~ INTVAL (XEXP (to, 1)))) - { - SUBST (XEXP (x, 1), XEXP (to, 0)); - } - /* In (set (zero-extract ) - (subreg (and <(2**n-1) | anything>))) - the `and' can be deleted. */ - if (GET_CODE (XEXP (x, 0)) == ZERO_EXTRACT - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && GET_CODE (XEXP (x, 1)) == SUBREG - && SUBREG_WORD (XEXP (x, 1)) == 0 - && GET_CODE (SUBREG_REG (XEXP (x, 1))) == AND - && GET_CODE (XEXP (SUBREG_REG (XEXP (x, 1)), 1)) == CONST_INT - && 0 == (((1 << INTVAL (XEXP (XEXP (x, 0), 1))) - 1) - & ~ INTVAL (XEXP (SUBREG_REG (XEXP (x, 1)), 1)))) - { - SUBST (SUBREG_REG (XEXP (x, 1)), XEXP (SUBREG_REG (XEXP (x, 1)), 0)); - } - /* (set (zero_extract ...) (and/or/xor (zero_extract ...) const)), - if both zero_extracts have the same location, size and position, - can be changed to avoid the byte extracts. */ - if ((GET_CODE (XEXP (x, 0)) == ZERO_EXTRACT - || GET_CODE (XEXP (x, 0)) == SIGN_EXTRACT) - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && (GET_CODE (XEXP (x, 1)) == AND - || GET_CODE (XEXP (x, 1)) == IOR - || GET_CODE (XEXP (x, 1)) == XOR) - && rtx_equal_p (XEXP (x, 0), XEXP (XEXP (x, 1), 0)) - && GET_CODE (XEXP (XEXP (x, 1), 0)) == GET_CODE (XEXP (x, 0)) - && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT - /* zero_extract can apply to a QImode even if the bits extracted - don't fit inside that byte. In such a case, we may not do this - optimization, since the OR or AND insn really would need - to fit in a byte. */ - && (INTVAL (XEXP (XEXP (x, 0), 1)) + INTVAL (XEXP (XEXP (x, 0), 2)) - < GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (x, 0), 0))))) - { - int shiftcount; - int newmask; -#ifdef BITS_BIG_ENDIAN - shiftcount - = GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (x, 0), 0))) - - INTVAL (XEXP (XEXP (x, 0), 1)) - INTVAL (XEXP (XEXP (x, 0), 2)); -#else - shiftcount - = INTVAL (XEXP (XEXP (x, 0), 2)); -#endif - newmask = ((INTVAL (XEXP (XEXP (x, 1), 1)) << shiftcount) - + (GET_CODE (XEXP (x, 1)) == AND - ? (1 << shiftcount) - 1 - : 0)); - if (GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (x, 0), 0))) - < HOST_BITS_PER_INT) - newmask &= (1 << GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (x, 0), 0)))) - 1; - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return - gen_rtx (SET, VOIDmode, - XEXP (XEXP (x, 0), 0), - gen_rtx (GET_CODE (XEXP (x, 1)), - GET_MODE (XEXP (XEXP (x, 0), 0)), - XEXP (XEXP (XEXP (x, 1), 0), 0), - gen_rtx (CONST_INT, VOIDmode, newmask))); - } - /* Can simplify (set (cc0) (compare (zero/sign_extend FOO) CONST)) - to (set (cc0) (compare FOO CONST)) if CONST fits in FOO's mode - and we are only testing equality. - In fact, this is valid for zero_extend if what follows is an - unsigned comparison, and for sign_extend with a signed comparison. */ - if (SET_DEST (x) == cc0_rtx - && GET_CODE (SET_SRC (x)) == COMPARE - && (GET_CODE (XEXP (SET_SRC (x), 0)) == ZERO_EXTEND - || GET_CODE (XEXP (SET_SRC (x), 0)) == SIGN_EXTEND) - && next_insn_tests_no_inequality (subst_insn) - && GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT - /* This is overly cautious by one bit, but saves worrying about - whether it is zero-extension or sign extension. */ - && ((unsigned) INTVAL (XEXP (SET_SRC (x), 1)) - < (1 << (GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (SET_SRC (x), 0), 0))) - 1)))) - SUBST (XEXP (SET_SRC (x), 0), XEXP (XEXP (SET_SRC (x), 0), 0)); - break; - - case AND: - if (GET_CODE (XEXP (x, 1)) == CONST_INT) - { - rtx tem = simplify_and_const_int (x, to); - if (tem) - return tem; - } - break; - - case IOR: - case XOR: - /* (ior (ior x c1) c2) => (ior x c1|c2); likewise for xor. */ - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && GET_CODE (XEXP (x, 0)) == code - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) - { - int c0 = INTVAL (XEXP (x, 1)); - int c1 = INTVAL (XEXP (XEXP (x, 0), 1)); - int combined = (code == IOR ? c0 | c1 : c0 ^ c1); - - if (combined == 0) - return XEXP (XEXP (x, 0), 0); - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - SUBST (XEXP (x, 1), gen_rtx (CONST_INT, VOIDmode, combined)); - SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0)); - break; - } - - case FLOAT: - /* (float (sign_extend )) = (float ). */ - if (was_replaced[0] - && GET_CODE (to) == SIGN_EXTEND) - SUBST (XEXP (x, 0), XEXP (to, 0)); - break; - - case ZERO_EXTRACT: - /* (ZERO_EXTRACT (TRUNCATE x)...) - can become (ZERO_EXTRACT x ...). */ - if (was_replaced[0] - && GET_CODE (to) == TRUNCATE) - { -#ifdef BITS_BIG_ENDIAN - if (GET_CODE (XEXP (x, 2)) == CONST_INT) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - /* On a big-endian machine, must increment the bit-number - since sign bit is farther away in the pre-truncated value. */ - return gen_rtx (ZERO_EXTRACT, GET_MODE (x), - XEXP (to, 0), - XEXP (x, 1), - gen_rtx (CONST_INT, VOIDmode, - (INTVAL (XEXP (x, 2)) - + GET_MODE_BITSIZE (GET_MODE (XEXP (to, 0))) - - GET_MODE_BITSIZE (GET_MODE (to))))); - } -#else - SUBST (XEXP (x, 0), XEXP (to, 0)); -#endif - } - /* Extracting a single bit from the result of a shift: - see which bit it was before the shift and extract that directly. */ - if (was_replaced[0] - && (GET_CODE (to) == ASHIFTRT || GET_CODE (to) == LSHIFTRT - || GET_CODE (to) == ASHIFT || GET_CODE (to) == LSHIFT) - && GET_CODE (XEXP (to, 1)) == CONST_INT - && XEXP (x, 1) == const1_rtx - && GET_CODE (XEXP (x, 2)) == CONST_INT) - { - int shift = INTVAL (XEXP (to, 1)); - int newpos; - if (GET_CODE (to) == ASHIFT || GET_CODE (to) == LSHIFT) - shift = - shift; -#ifdef BITS_BIG_ENDIAN - shift = - shift; -#endif - newpos = INTVAL (XEXP (x, 2)) + shift; - if (newpos >= 0 && - newpos < GET_MODE_BITSIZE (GET_MODE (to))) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (ZERO_EXTRACT, GET_MODE (x), - XEXP (to, 0), const1_rtx, - gen_rtx (CONST_INT, VOIDmode, newpos)); - } - } - break; - - case LSHIFTRT: - case ASHIFTRT: - case ROTATE: - case ROTATERT: -#ifdef SHIFT_COUNT_TRUNCATED - /* (lshift (sign_extend )) = (lshift ) (most machines). - True for all kinds of shifts and also for zero_extend. */ - if (was_replaced[1] - && (GET_CODE (to) == SIGN_EXTEND - || GET_CODE (to) == ZERO_EXTEND) - && FAKE_EXTEND_SAFE_P (GET_MODE (to), XEXP (to, 0))) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - SUBST (XEXP (x, 1), - /* This is a perverse SUBREG, wider than its base. */ - gen_lowpart_for_combine (GET_MODE (to), XEXP (to, 0))); - } -#endif - /* Two shifts in a row of same kind - in same direction with constant counts - may be combined. */ - if (was_replaced[0] - && GET_CODE (to) == GET_CODE (x) - && GET_CODE (XEXP (x, 1)) == CONST_INT - && GET_CODE (XEXP (to, 1)) == CONST_INT - && INTVAL (XEXP (to, 1)) > 0 - && INTVAL (XEXP (x, 1)) > 0 - && (INTVAL (XEXP (x, 1)) + INTVAL (XEXP (to, 1)) - < GET_MODE_BITSIZE (GET_MODE (x)))) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (GET_CODE (x), GET_MODE (x), - XEXP (to, 0), - gen_rtx (CONST_INT, VOIDmode, - INTVAL (XEXP (x, 1)) - + INTVAL (XEXP (to, 1)))); - } - break; - - case LSHIFT: - case ASHIFT: -#ifdef SHIFT_COUNT_TRUNCATED - /* (lshift (sign_extend )) = (lshift ) (most machines). - True for all kinds of shifts and also for zero_extend. */ - if (was_replaced[1] - && (GET_CODE (to) == SIGN_EXTEND - || GET_CODE (to) == ZERO_EXTEND) - && FAKE_EXTEND_SAFE_P (GET_MODE (to), XEXP (to, 0))) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - SUBST (XEXP (x, 1), - /* This is a perverse SUBREG, wider than its base. */ - gen_lowpart_for_combine (GET_MODE (to), XEXP (to, 0))); - } -#endif - /* (lshift (and (lshiftrt ) ) ) - happens copying between bit fields in similar structures. - It can be replaced by one and instruction. - It does not matter whether the shifts are logical or arithmetic. */ - if (GET_CODE (XEXP (x, 0)) == AND - && GET_CODE (XEXP (x, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) > 0 - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && XEXP (XEXP (x, 0), 0) == to - && (GET_CODE (to) == LSHIFTRT - || GET_CODE (to) == ASHIFTRT) -#if 0 -/* I now believe this restriction is unnecessary. - The outer shift will discard those bits in any case, right? */ - - /* If inner shift is arithmetic, either it shifts left or - the bits it shifts the sign into are zeroed by the and. */ - && (INTVAL (XEXP (x, 1)) < 0 - || ((unsigned) INTVAL (XEXP (XEXP (x, 0), 1)) - < 1 << (GET_MODE_BITSIZE (GET_MODE (x)) - - INTVAL (XEXP (x, 0))))) -#endif - && GET_CODE (XEXP (to, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) == INTVAL (XEXP (to, 1))) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - /* The constant in the new `and' is << - but clear out all bits that don't belong in our mode. */ - return gen_rtx (AND, GET_MODE (x), XEXP (to, 0), - gen_rtx (CONST_INT, VOIDmode, - (GET_MODE_MASK (GET_MODE (x)) - & ((GET_MODE_MASK (GET_MODE (x)) - & INTVAL (XEXP (XEXP (x, 0), 1))) - << INTVAL (XEXP (x, 1)))))); - } - /* Two shifts in a row in same direction with constant counts - may be combined. */ - if (was_replaced[0] - && (GET_CODE (to) == ASHIFT || GET_CODE (to) == LSHIFT) - && GET_CODE (XEXP (x, 1)) == CONST_INT - && GET_CODE (XEXP (to, 1)) == CONST_INT - && INTVAL (XEXP (to, 1)) > 0 - && INTVAL (XEXP (x, 1)) > 0 - && (INTVAL (XEXP (x, 1)) + INTVAL (XEXP (to, 1)) - < GET_MODE_BITSIZE (GET_MODE (x)))) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (GET_CODE (x), GET_MODE (x), - XEXP (to, 0), - gen_rtx (CONST_INT, VOIDmode, - INTVAL (XEXP (x, 1)) - + INTVAL (XEXP (to, 1)))); - } - /* (ashift (ashiftrt ) ) - (or, on some machines, (ashift (ashift <-X>) ) instead) - happens if you divide by 2**N and then multiply by 2**N. - It can be replaced by one `and' instruction. - It does not matter whether the shifts are logical or arithmetic. */ - if (GET_CODE (XEXP (x, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) > 0 - && was_replaced[0] - && (((GET_CODE (to) == LSHIFTRT || GET_CODE (to) == ASHIFTRT) - && GET_CODE (XEXP (to, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) == INTVAL (XEXP (to, 1))) - || - ((GET_CODE (to) == LSHIFT || GET_CODE (to) == ASHIFT) - && GET_CODE (XEXP (to, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) == - INTVAL (XEXP (to, 1))))) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - /* The constant in the new `and' is -1 << - but clear out all bits that don't belong in our mode. */ - return gen_rtx (AND, GET_MODE (x), XEXP (to, 0), - gen_rtx (CONST_INT, VOIDmode, - (GET_MODE_MASK (GET_MODE (x)) - & (GET_MODE_MASK (GET_MODE (x)) - << INTVAL (XEXP (x, 1)))))); - } - - } - - return x; -} - -/* This is the AND case of the function subst. */ - -static rtx -simplify_and_const_int (x, to) - rtx x, to; -{ - register rtx varop = XEXP (x, 0); - register int constop = INTVAL (XEXP (x, 1)); - - /* (and (subreg (and ) 0) ) - results from an andsi followed by an andqi, - which happens frequently when storing bit-fields - on something whose result comes from an andsi. */ - if (GET_CODE (varop) == SUBREG - && XEXP (varop, 0) == to - && subreg_lowpart_p (varop) - && GET_CODE (to) == AND - && GET_CODE (XEXP (to, 1)) == CONST_INT - /* Verify that the result of the outer `and' - is not affected by any bits not defined in the inner `and'. - True if the outer mode is narrower, or if the outer constant - masks to zero all the bits that the inner mode doesn't have. */ - && (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (GET_MODE (to)) - || (constop & ~ GET_MODE_MASK (GET_MODE (to))) == 0)) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (AND, GET_MODE (x), - gen_lowpart_for_combine (GET_MODE (x), XEXP (to, 0)), - gen_rtx (CONST_INT, VOIDmode, - constop - /* Remember that the bits outside that mode - are not being changed, so the effect - is as if they were all 1. */ - & INTVAL (XEXP (to, 1)))); - } - /* (and:SI (zero_extract:SI ...) ) - results from an andsi following a byte-fetch on risc machines. - When the constant includes all bits extracted, eliminate the `and'. */ - if (GET_CODE (varop) == ZERO_EXTRACT - && GET_CODE (XEXP (varop, 1)) == CONST_INT - /* The `and' must not clear any bits that the extract can give. */ - && (~ constop & ((1 << INTVAL (XEXP (varop, 1))) - 1)) == 0) - return varop; - /* (and (zero_extend ) ) - often results from storing in a bit-field something - that was calculated as a short. Replace with a single `and' - in whose constant all bits not in 's mode are zero. */ - if (varop == to - && GET_CODE (to) == ZERO_EXTEND - && FAKE_EXTEND_SAFE_P (GET_MODE (x), XEXP (to, 0))) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (AND, GET_MODE (x), - /* This is a perverse SUBREG, wider than its base. */ - gen_lowpart_for_combine (GET_MODE (x), XEXP (to, 0)), - gen_rtx (CONST_INT, VOIDmode, - constop & GET_MODE_MASK (GET_MODE (XEXP (to, 0))))); - } - /* (and (sign_extend ) ) - can be replaced with (and (subreg ) ) - if is narrower than 's mode, - or with (zero_extend ) if is a mask for that mode. */ - if (varop == to - && GET_CODE (to) == SIGN_EXTEND - && ((unsigned) constop <= GET_MODE_MASK (GET_MODE (XEXP (to, 0)))) - && FAKE_EXTEND_SAFE_P (GET_MODE (x), XEXP (to, 0))) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - if (constop == GET_MODE_MASK (GET_MODE (XEXP (to, 0)))) - return gen_rtx (ZERO_EXTEND, GET_MODE (x), XEXP (to, 0)); - return gen_rtx (AND, GET_MODE (x), - /* This is a perverse SUBREG, wider than its base. */ - gen_lowpart_for_combine (GET_MODE (x), XEXP (to, 0)), - XEXP (x, 1)); - } - /* (and (and ) ) - comes from two and instructions in a row. */ - if (varop == to - && GET_CODE (to) == AND - && GET_CODE (XEXP (to, 1)) == CONST_INT) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (AND, GET_MODE (x), - XEXP (to, 0), - gen_rtx (CONST_INT, VOIDmode, - constop - & INTVAL (XEXP (to, 1)))); - } - /* (and (ashiftrt (ashift FOO N) N) CONST) - may be simplified to (and FOO CONST) if CONST masks off the bits - changed by the two shifts. */ - if (GET_CODE (varop) == ASHIFTRT - && GET_CODE (XEXP (varop, 1)) == CONST_INT - && XEXP (varop, 0) == to - && GET_CODE (to) == ASHIFT - && GET_CODE (XEXP (to, 1)) == CONST_INT - && INTVAL (XEXP (varop, 1)) == INTVAL (XEXP (to, 1)) - && ((unsigned) constop >> INTVAL (XEXP (varop, 1))) == 0) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - /* If CONST is a mask for the low byte, - change this into a zero-extend instruction - from just the low byte of FOO. */ - if (constop == GET_MODE_MASK (QImode)) - { - rtx temp = gen_lowpart_for_combine (QImode, XEXP (to, 0)); - if (GET_CODE (temp) != CLOBBER) - return gen_rtx (ZERO_EXTEND, GET_MODE (x), temp); - } - return gen_rtx (AND, GET_MODE (x), - XEXP (to, 0), XEXP (x, 1)); - } - /* (and (ashiftrt (zero_extend FOO) N) CONST) - may be simplified to (and (ashiftrt (subreg FOO) N) CONST) - if CONST masks off the bits changed by extension. */ - if ((GET_CODE (varop) == ASHIFTRT || GET_CODE (varop) == LSHIFTRT) - && GET_CODE (XEXP (varop, 1)) == CONST_INT - && XEXP (varop, 0) == to - && (GET_CODE (to) == ZERO_EXTEND || GET_CODE (to) == SIGN_EXTEND) - /* Verify the and discards all the extended bits. */ - && (((unsigned) constop << INTVAL (XEXP (varop, 1))) - >> GET_MODE_BITSIZE (GET_MODE (XEXP (to, 0)))) == 0 - && FAKE_EXTEND_SAFE_P (GET_MODE (x), XEXP (to, 0))) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - SUBST (XEXP (varop, 0), - gen_lowpart_for_combine (GET_MODE (x), XEXP (to, 0))); - return x; - } - /* (and x const) may be converted to (zero_extend (subreg x 0)). */ - if (constop == GET_MODE_MASK (QImode) - && GET_CODE (varop) == REG) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (ZERO_EXTEND, GET_MODE (x), - gen_rtx (SUBREG, QImode, varop, 0)); - } - if (constop == GET_MODE_MASK (HImode) - && GET_CODE (varop) == REG) - { - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - return gen_rtx (ZERO_EXTEND, GET_MODE (x), - gen_rtx (SUBREG, HImode, varop, 0)); - } - /* No simplification applies. */ - return 0; -} - -/* Like gen_lowpart but for use by combine. In combine it is not possible - to create any new pseudoregs. However, it is safe to create - invalid memory addresses, because combine will try to recognize - them and all they will do is make the combine attempt fail. - - If for some reason this cannot do its job, an rtx - (clobber (const_int 0)) is returned. - An insn containing that will not be recognized. */ - -#undef gen_lowpart - -static rtx -gen_lowpart_for_combine (mode, x) - enum machine_mode mode; - register rtx x; -{ - if (GET_CODE (x) == SUBREG || GET_CODE (x) == REG) - return gen_lowpart (mode, x); - if (GET_MODE (x) == mode) - return gen_rtx (CLOBBER, VOIDmode, const0_rtx); - if (GET_CODE (x) == MEM) - { - register int offset = 0; - - /* Refuse to work on a volatile memory ref. */ - if (MEM_VOLATILE_P (x)) - return gen_rtx (CLOBBER, VOIDmode, const0_rtx); - - /* If we want to refer to something bigger than the original memref, - generate a perverse subreg instead. That will force a reload - of the original memref X. */ - if (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (mode)) - return gen_rtx (SUBREG, mode, x, 0); - -#ifdef WORDS_BIG_ENDIAN - offset = (max (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD) - - max (GET_MODE_SIZE (mode), UNITS_PER_WORD)); -#endif -#ifdef BYTES_BIG_ENDIAN - /* Adjust the address so that the address-after-the-data - is unchanged. */ - offset -= (min (UNITS_PER_WORD, GET_MODE_SIZE (mode)) - - min (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x)))); -#endif - return gen_rtx (MEM, mode, plus_constant (XEXP (x, 0), - offset)); - } - else - return gen_rtx (CLOBBER, VOIDmode, const0_rtx); -} - -/* After substitution, if the resulting pattern looks like - (set (cc0) (and ...)) or (set (cc0) (lshiftrt ...)), - this function is called to simplify the - pattern into a bit-field operation if possible. */ - -static void -simplify_set_cc0_and (insn) - rtx insn; -{ - register rtx value = XEXP (PATTERN (insn), 1); - register rtx op0 = XEXP (value, 0); - register rtx op1 = XEXP (value, 1); - int offset = 0; - rtx var = 0; - rtx bitnum = 0; - int temp; - int unit; - rtx newpat; - - if (GET_CODE (value) == AND) - { - op0 = XEXP (value, 0); - op1 = XEXP (value, 1); - } - else if (GET_CODE (value) == LSHIFTRT) - { - /* If there is no AND, but there is a shift that discards - all but the sign bit, we can pretend that the shift result - is ANDed with 1. Otherwise we cannot handle just a shift. */ - if (GET_CODE (XEXP (value, 1)) == CONST_INT - && (INTVAL (XEXP (value, 1)) - == GET_MODE_BITSIZE (GET_MODE (value)) - 1)) - { - op0 = value; - op1 = const1_rtx; - } - else - return; - } - else - abort (); - - /* Look for a constant power of 2 or a shifted 1 - on either side of the AND. Set VAR to the other side. - Set BITNUM to the shift count of the 1 (as an rtx). - Or, if bit number is constant, set OFFSET to the bit number. */ - - switch (GET_CODE (op0)) - { - case CONST_INT: - temp = exact_log2 (INTVAL (op0)); - if (temp < 0) - return; - offset = temp; - var = op1; - break; - - case ASHIFT: - case LSHIFT: - if (XEXP (op0, 0) == const1_rtx) - { - bitnum = XEXP (op0, 1); - var = op1; - } - } - if (var == 0) - switch (GET_CODE (op1)) - { - case CONST_INT: - temp = exact_log2 (INTVAL (op1)); - if (temp < 0) - return; - offset = temp; - var = op0; - break; - - case ASHIFT: - case LSHIFT: - if (XEXP (op1, 0) == const1_rtx) - { - bitnum = XEXP (op1, 1); - var = op0; - } - } - - /* If VAR is 0, we didn't find something recognizable. */ - if (var == 0) - return; - - if (!undobuf.storage) - undobuf.storage = (char *) oballoc (0); - - /* If the bit position is currently exactly 0, - extract a right-shift from the variable portion. */ - if (offset == 0 - && (GET_CODE (var) == ASHIFTRT || GET_CODE (var) == LSHIFTRT)) - { - bitnum = XEXP (var, 1); - var = XEXP (var, 0); - } - - if (GET_CODE (var) == SUBREG && SUBREG_WORD (var) == 0) - var = SUBREG_REG (var); - - /* Note that BITNUM and OFFSET are always little-endian thru here - even on a big-endian machine. */ - -#ifdef BITS_BIG_ENDIAN - unit = GET_MODE_BITSIZE (GET_MODE (var)) - 1; - - if (bitnum != 0) - bitnum = gen_rtx (MINUS, SImode, - gen_rtx (CONST_INT, VOIDmode, unit), bitnum); - else - offset = unit - offset; -#endif - - if (bitnum == 0) - bitnum = gen_rtx (CONST_INT, VOIDmode, offset); - - newpat = gen_rtx (SET, VOIDmode, cc0_rtx, - gen_rtx (ZERO_EXTRACT, VOIDmode, var, const1_rtx, bitnum)); - if (recog (newpat, insn) >= 0) - { - if (undobuf.num_undo < MAX_UNDO) - { - undobuf.undo[undobuf.num_undo].where = &XEXP (PATTERN (insn), 1); - undobuf.undo[undobuf.num_undo].old_contents = value; - XEXP (PATTERN (insn), 1) = XEXP (newpat, 1); - } - undobuf.num_undo++; - } -} - -/* Update the records of when each REG was most recently set or killed - for the things done by INSN. This is the last thing done in processing - INSN in the combiner loop. - - We update reg_last_set, reg_last_death, and also the similar information - mem_last_set (which insn most recently modified memory) - and last_call_cuid (which insn was the most recent subroutine call). */ - -static void -record_dead_and_set_regs (insn) - rtx insn; -{ - register rtx link; - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - { - if (REG_NOTE_KIND (link) == REG_DEAD) - reg_last_death[REGNO (XEXP (link, 0))] = insn; - else if (REG_NOTE_KIND (link) == REG_INC) - reg_last_set[REGNO (XEXP (link, 0))] = insn; - } - - if (GET_CODE (insn) == CALL_INSN) - last_call_cuid = mem_last_set = INSN_CUID (insn); - - if (GET_CODE (PATTERN (insn)) == PARALLEL) - { - register int i; - for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) - { - register rtx elt = XVECEXP (PATTERN (insn), 0, i); - register enum rtx_code code = GET_CODE (elt); - if (code == SET || code == CLOBBER) - { - rtx dest = XEXP (elt, 0); - while (GET_CODE (dest) == SUBREG - || GET_CODE (dest) == STRICT_LOW_PART - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == ZERO_EXTRACT) - dest = XEXP (dest, 0); - - if (GET_CODE (dest) == REG) - reg_last_set[REGNO (dest)] = insn; - else if (GET_CODE (dest) == MEM) - mem_last_set = INSN_CUID (insn); - } - } - } - else if (GET_CODE (PATTERN (insn)) == SET - || GET_CODE (PATTERN (insn)) == CLOBBER) - { - register rtx dest = XEXP (PATTERN (insn), 0); - - while (GET_CODE (dest) == SUBREG - || GET_CODE (dest) == STRICT_LOW_PART - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == ZERO_EXTRACT) - dest = XEXP (dest, 0); - - if (GET_CODE (dest) == REG) - reg_last_set[REGNO (dest)] = insn; - else if (GET_CODE (dest) == MEM) - mem_last_set = INSN_CUID (insn); - } -} - -/* Return nonzero if expression X refers to a REG or to memory - that is set in an instruction more recent than FROM_CUID. */ - -static int -use_crosses_set_p (x, from_cuid) - register rtx x; - int from_cuid; -{ - register char *fmt; - register int i; - register enum rtx_code code = GET_CODE (x); - - if (code == REG) - { - register int regno = REGNO (x); -#ifdef PUSH_ROUNDING - /* Don't allow uses of the stack pointer to be moved, - because we don't know whether the move crosses a push insn. */ - if (regno == STACK_POINTER_REGNUM) - return 1; -#endif - return (reg_last_set[regno] - && INSN_CUID (reg_last_set[regno]) > from_cuid); - } - - if (code == MEM && mem_last_set > from_cuid) - return 1; - - fmt = GET_RTX_FORMAT (code); - - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - if (use_crosses_set_p (XVECEXP (x, i, j), from_cuid)) - return 1; - } - else if (fmt[i] == 'e' - && use_crosses_set_p (XEXP (x, i), from_cuid)) - return 1; - } - return 0; -} - -/* Return nonzero if reg REGNO is marked as dying in INSN. */ - -int -regno_dead_p (regno, insn) - int regno; - rtx insn; -{ - register rtx link; - - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if ((REG_NOTE_KIND (link) == REG_DEAD - || REG_NOTE_KIND (link) == REG_INC) - && REGNO (XEXP (link, 0)) == regno) - return 1; - - return 0; -} - -/* Return nonzero if J is the first insn following I, - not counting labels, line numbers, etc. - We assume that J follows I. */ - -static int -adjacent_insns_p (i, j) - rtx i, j; -{ - register rtx insn; - for (insn = NEXT_INSN (i); insn != j; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == INSN - || GET_CODE (insn) == CALL_INSN - || GET_CODE (insn) == JUMP_INSN) - return 0; - return 1; -} - -/* Check that X is an insn-body for an `asm' with operands - and that the operands mentioned in it are legitimate. */ - -static int -check_asm_operands (x) - rtx x; -{ - int noperands = asm_noperands (x); - rtx *operands; - int i; - - if (noperands < 0) - return 0; - if (noperands == 0) - return 1; - - operands = (rtx *) alloca (noperands * sizeof (rtx)); - decode_asm_operands (x, operands, 0, 0, 0); - - for (i = 0; i < noperands; i++) - if (!general_operand (operands[i], VOIDmode)) - return 0; - - return 1; -} - -/* Concatenate the list of logical links of OINSN - into INSN's list of logical links. - Modifies OINSN destructively. - - If ALL_LINKS is nonzero, move all the links that OINSN has. - Otherwise, move only those that point to insns that set regs - that die in the insn OINSN. - Other links are clobbered so that they are no longer effective. */ - -static void -add_links (insn, oinsn, all_links) - rtx insn, oinsn; - int all_links; -{ - register rtx links = LOG_LINKS (oinsn); - if (! all_links) - { - rtx tail; - for (tail = links; tail; tail = XEXP (tail, 1)) - { - rtx target = XEXP (tail, 0); - if (GET_CODE (target) != INSN - || GET_CODE (PATTERN (target)) != SET - || GET_CODE (SET_DEST (PATTERN (target))) != REG - || ! dead_or_set_p (oinsn, SET_DEST (PATTERN (target)))) - /* OINSN is going to become a NOTE - so a link pointing there will have no effect. */ - XEXP (tail, 0) = oinsn; - } - } - if (LOG_LINKS (insn) == 0) - LOG_LINKS (insn) = links; - else - { - register rtx next, prev = LOG_LINKS (insn); - while (next = XEXP (prev, 1)) - prev = next; - XEXP (prev, 1) = links; - } -} - -/* Delete any LOG_LINKS of INSN which point at OINSN. */ - -static void -remove_links (insn, oinsn) - rtx insn, oinsn; -{ - register rtx next = LOG_LINKS (insn), prev = 0; - while (next) - { - if (XEXP (next, 0) == oinsn) - { - if (prev) - XEXP (prev, 1) = XEXP (next, 1); - else - LOG_LINKS (insn) = XEXP (next, 1); - } - else - prev = next; - next = XEXP (next, 1); - } -} - -/* Concatenate the any elements of the list of reg-notes INCS - which are of type REG_INC - into INSN's list of reg-notes. */ - -static void -add_incs (insn, incs) - rtx insn, incs; -{ - register rtx tail; - - for (tail = incs; tail; tail = XEXP (tail, 1)) - if (REG_NOTE_KIND (tail) == REG_INC) - REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_INC, XEXP (tail, 0), REG_NOTES (insn)); -} - -/* Remove register number REGNO from the dead registers list of INSN. */ - -void -remove_death (regno, insn) - int regno; - rtx insn; -{ - register rtx link, next; - while ((link = REG_NOTES (insn)) - && REG_NOTE_KIND (link) == REG_DEAD - && REGNO (XEXP (link, 0)) == regno) - REG_NOTES (insn) = XEXP (link, 1); - - if (link) - while (next = XEXP (link, 1)) - { - if (REG_NOTE_KIND (next) == REG_DEAD - && REGNO (XEXP (next, 0)) == regno) - XEXP (link, 1) = XEXP (next, 1); - else - link = next; - } -} - -/* For each register (hardware or pseudo) used within expression X, - if its death is in an instruction with cuid - between FROM_CUID (inclusive) and TO_INSN (exclusive), - mark it as dead in TO_INSN instead. - - This is done when X is being merged by combination into TO_INSN. */ - -static void -move_deaths (x, from_cuid, to_insn) - rtx x; - int from_cuid; - rtx to_insn; -{ - register char *fmt; - register int len, i; - register enum rtx_code code = GET_CODE (x); - - if (code == REG) - { - register rtx where_dead = reg_last_death[REGNO (x)]; - - if (where_dead && INSN_CUID (where_dead) >= from_cuid - && INSN_CUID (where_dead) < INSN_CUID (to_insn)) - { - remove_death (REGNO (x), reg_last_death[REGNO (x)]); - if (! dead_or_set_p (to_insn, x)) - REG_NOTES (to_insn) - = gen_rtx (EXPR_LIST, REG_DEAD, x, REG_NOTES (to_insn)); - } - return; - } - - len = GET_RTX_LENGTH (code); - fmt = GET_RTX_FORMAT (code); - - for (i = 0; i < len; i++) - { - if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - move_deaths (XVECEXP (x, i, j), from_cuid, to_insn); - } - else if (fmt[i] == 'e') - move_deaths (XEXP (x, i), from_cuid, to_insn); - } -} - -/* Like move_deaths, but deaths are moving both forward - (from FROM_CUID to TO_INSN), and backwards - (from FROM_INSN to TO_INSN). This is what happens - when an insn is removed after applying the distributive law. */ - -static void -move_deaths_2 (x, from_cuid, from_insn, to_insn) - rtx x; - int from_cuid; - rtx from_insn, to_insn; -{ - register char *fmt; - register int len, i; - register enum rtx_code code = GET_CODE (x); - - if (code == REG) - { - register rtx where_dead = reg_last_death[REGNO (x)]; - - if (where_dead && INSN_CUID (where_dead) >= from_cuid - && INSN_CUID (where_dead) < INSN_CUID (to_insn)) - { - remove_death (REGNO (x), reg_last_death[REGNO (x)]); - if (! dead_or_set_p (to_insn, x)) - REG_NOTES (to_insn) - = gen_rtx (EXPR_LIST, REG_DEAD, x, REG_NOTES (to_insn)); - } - /* Can't use where_dead for from_insn because it has - not been computed yet. */ - else if (dead_or_set_p (from_insn, x)) - { - remove_death (REGNO (x), from_insn); - if (! dead_or_set_p (to_insn, x)) - REG_NOTES (to_insn) - = gen_rtx (EXPR_LIST, REG_DEAD, x, REG_NOTES (to_insn)); - } - return; - } - - len = GET_RTX_LENGTH (code); - fmt = GET_RTX_FORMAT (code); - - for (i = 0; i < len; i++) - { - if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - move_deaths_2 (XVECEXP (x, i, j), from_cuid, from_insn, to_insn); - } - else if (fmt[i] == 'e') - move_deaths_2 (XEXP (x, i), from_cuid, from_insn, to_insn); - } -} - -/* The distrib combiner rewrites groups of insns so that optimizations - can be more easily recognized. The front-end does not know how to - group certain kinds of operations for efficient execution, and the - resulting code can be quite poor. For example, on a machine without - bitfield instructions, bitfield references look like - - (and (lshiftrt ... n) m) - - When combining two bitfield operations, such as with ||, this can - yield code like - - (set z - (or (and (lshiftrt x n) 1) - (and (lshiftrt y n) 1))) - - which can be more efficiently executed as - - (set z - (lshiftrt (and (or x y) - (1 << m)) n)) - - From there, the combiner attempts to rewrite the insns, - keeping flow information accurate for later passes, - and reducing the total number of insns executed. - - This function returns the point at which we should try - looking for more simplifications. This will be before - INSN if the call succeeds. We do not need to fear - infinite loops, since this function is guaranteed to - eliminate at least one (non-note) instruction if it returns - successfully. */ - -static rtx -try_distrib (insn, xprev1, xprev2) - rtx insn, xprev1, xprev2; -{ - rtx pat = PATTERN (insn); - rtx prev1, prev2, pat1, pat2, src1, src2; - rtx to_prev, to_insn; - enum rtx_code code; - int insn_code_number, prev_code_number, regno; - rtx new_insn_pat, new_prev_pat; - - distrib_attempts++; - - /* ??? Need to implement a test that PREV2 and PREV1 - are completely independent. Right now their - recognition ability is sufficiently limited that - it should not be necessary, but better safe than sorry. */ - - /* Let PREV1 be the later of the two insns, and PREV2 the earlier. */ - if (INSN_CUID (xprev1) > INSN_CUID (xprev2)) - { - prev1 = xprev1; - prev2 = xprev2; - } - else - { - prev1 = xprev2; - prev2 = xprev1; - } - - pat1 = PATTERN (prev1); - pat2 = PATTERN (prev2); - - /* First, see if INSN, PREV1, and PREV2 have patterns we can expect - to simplify. */ - - if (GET_CODE (pat) != SET - || GET_CODE (pat1) != SET - || GET_CODE (pat2) != SET) - return 0; - - code = GET_CODE (SET_SRC (pat)); - src1 = SET_SRC (pat1); - src2 = SET_SRC (pat2); - - if (GET_CODE (SET_DEST (pat1)) != REG - || GET_CODE (SET_DEST (pat2)) != REG) - return 0; - - switch (code) - { - default: - return 0; - - case IOR: - case AND: - case XOR: - case PLUS: - ; - } - - /* Insns PREV1 and PREV2 must provide the two operands of the arithmetic - that is done in INSN. */ - if (! ((XEXP (SET_SRC (pat), 0) == SET_DEST (pat1) - && XEXP (SET_SRC (pat), 1) == SET_DEST (pat2)) - || - (XEXP (SET_SRC (pat), 0) == SET_DEST (pat2) - && XEXP (SET_SRC (pat), 1) == SET_DEST (pat1)))) - return 0; - - /* They must not be used in any other way in INSN. - In particular, they must not be used in a result memory address. */ - if (reg_mentioned_p (SET_DEST (pat1), SET_DEST (pat)) - || reg_mentioned_p (SET_DEST (pat2), SET_DEST (pat))) - return 0; - - /* Give up if the two operands' modes don't match. */ - if (GET_MODE (src1) != GET_MODE (src2)) - return 0; - - /* PREV1 and PREV2 must compute the same operation. - Actually, there are other cases that could be handled, - but are not implemented. For example: - - (set (reg:SI 94) - (and:SI (reg:SI 73) - (const_int 223))) - - (set (reg:SI 95) - (zero_extend:SI (subreg:QI (reg:SI 91) 0))) - - (set (reg:SI 96) - (ior:SI (reg:SI 94) - (reg:SI 95))) - - In this case, we know that because (reg:SI 94) has - been anded with 223, there is no need to zero_extend - (reg:SI 91), and we could eliminate (reg:SI 95). */ - - if (GET_CODE (src1) != GET_CODE (src2)) - return 0; - - /* The SETs in PREV1 and PREV2 do not need to be kept around. */ - - undobuf.num_undo = 0; - undobuf.storage = 0; - - /* Substitute in the latest insn for the regs set by the earlier ones. */ - subst_insn = insn; - n_occurrences = 0; /* `subst' counts here */ - - switch (GET_CODE (src1)) - { - /* case XOR: Does not distribute through anything! */ - case LSHIFTRT: - case ASHIFTRT: - /* Right-shift can't distribute through addition - since the round-off would happen differently. */ - case AND: - case IOR: - /* Boolean ops don't distribute through addition. */ - if (code == PLUS) - return 0; - goto do_distrib; - - case LSHIFT: - case ASHIFT: - /* Left shifts are multiplication; they distribute through - addition. Also, since they work bitwise, they - distribute through boolean operations. */ -#ifdef NEGATIVE_SHIFT_COUNTS - /* Negative count is really a right-shift. */ - if (NEGATIVE_SHIFT_COUNTS - && code == PLUS - && !(GET_CODE (XEXP (src1, 1)) - == CONST_INT && INTVAL (XEXP (src1, 1)) >= 0)) - return 0; -#endif - goto do_distrib; - - case MULT: - /* Multiplication distributes through addition only. */ - if (code != PLUS) - return 0; - - do_distrib: - if (GET_CODE (XEXP (src1, 1)) != CONST_INT - || GET_CODE (XEXP (src2, 1)) != CONST_INT - || INTVAL (XEXP (src1, 1)) != INTVAL (XEXP (src2, 1))) - return 0; - - /* Give up if we would move a use of a reg across an alteration. - Note this is unnecessarily conservative, since a problem really - happens only if this reg is set *between* PREV2 and PREV1 - But this test is easier. */ - if (use_crosses_set_p (XEXP (src2, 0), INSN_CUID (prev2))) - return 0; - - /* Try changing (+ (* x c) (* y c)) to (* (+ x y) c). */ - to_prev = gen_rtx (code, GET_MODE (src1), - XEXP (src1, 0), XEXP (src2, 0)); - to_insn = gen_rtx (GET_CODE (src1), GET_MODE (src1), SET_DEST (pat1), XEXP (src1, 1)); - break; - - case ZERO_EXTEND: - case SIGN_EXTEND: - /* Extension can't distribute through addition; - the carries could be changed. */ - if (code == PLUS) - return 0; - { - rtx inner1 = XEXP (src1, 0), inner2 = XEXP (src2, 0); - int subreg_needed = 0; - - /* Try changing (& (extend x) (extend y)) to (extend (& x y)). */ - /* But keep extend insns together with their subregs. */ - if (GET_CODE (inner1) == SUBREG) - { - if (SUBREG_WORD (inner1) != 0) - return 0; - else - { - subreg_needed = 1; - inner1 = SUBREG_REG (inner1); - } - } - - if (GET_CODE (inner2) == SUBREG) - { - if (SUBREG_WORD (inner2) != 0) - return 0; - else - { - subreg_needed = 1; - inner2 = SUBREG_REG (inner2); - } - } - - /* Give up if we would move a use of a reg across an alteration. - Note this is unnecessarily conservative, since a problem really - happens only if this reg is set *between* PREV2 and PREV1 - But this test is easier. */ - if (use_crosses_set_p (inner2, INSN_CUID (prev2))) - return 0; - - to_prev = gen_rtx (code, GET_MODE (src1), inner1, inner2); - to_insn = gen_rtx (GET_CODE (src1), GET_MODE (src1), - subreg_needed - ? gen_rtx (SUBREG, GET_MODE (XEXP (src1, 0)), - SET_DEST (pat1), 0) - : SET_DEST (pat1)); - } - break; - - default: - return 0; - } - - /* Are the results of this "substitution" a valid instruction? */ - - new_insn_pat = subst (PATTERN (insn), SET_SRC (PATTERN (insn)), to_insn); - distrib_merges_1++; - - insn_code_number = recog (new_insn_pat, insn); - if (insn_code_number < 0) - { - undo_all (); - return 0; - } - - subst_insn = prev1; - new_prev_pat = subst (pat1, src1, to_prev); - distrib_merges_2++; - - prev_code_number = recog (new_prev_pat, prev1); - if (prev_code_number < 0) - { - undo_all (); - return 0; - } - - /* Everything worked; install the new patterns. */ - INSN_CODE (insn) = insn_code_number; - PATTERN (insn) = new_insn_pat; - - INSN_CODE (prev1) = prev_code_number; - PATTERN (prev1) = new_prev_pat; - - /* Need to change LOG_LINKS around...PREV1 now gets - whatever flowed into PREV2. PREV2 is going to - become a NOTE, so we clear out its LOG_LINKS. */ - remove_links (insn, prev2); - add_links (prev1, prev2, adjacent_insns_p (prev2, prev1)); - - /* Registers which died in PREV2 now die in PREV1. - Also, registers born in PREV2 dying in INSN now die in PREV1. */ - move_deaths_2 (src2, INSN_CUID (prev2), insn, prev1); - - regno = REGNO (SET_DEST (pat2)); - - reg_n_sets[regno]--; - if (reg_n_sets[regno] == 0 - && ! (basic_block_live_at_start[0][regno / HOST_BITS_PER_INT] - & (1 << (regno % HOST_BITS_PER_INT)))) - reg_n_refs[regno] = 0; - remove_death (regno, insn); - - PUT_CODE (prev2, NOTE); - NOTE_LINE_NUMBER (prev2) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (prev2) = 0; - - distrib_successes++; - return prev1; -} - -void -dump_combine_stats (file) - FILE *file; -{ - fprintf - (file, - ";; Combiner statistics: %d attempts, %d substitutions (%d requiring new space),\n;; %d successes.\n\n", - combine_attempts, combine_merges, combine_extras, combine_successes); - fprintf - (file, - ";; Distributer statistics: %d attempts, %d:%d substitutions,\n;; %d successes.\n\n", - distrib_attempts, distrib_merges_1, - distrib_merges_2, distrib_successes); -} - -void -dump_combine_total_stats (file) - FILE *file; -{ - fprintf - (file, - "\n;; Combiner totals: %d attempts, %d substitutions (%d requiring new space),\n;; %d successes.\n", - total_attempts, total_merges, total_extras, total_successes); - fprintf - (file, - "\n;; Distributer totals: %d attempts, %d:%d substitutions,\n;; %d successes.\n", - total_distrib_attempts, total_distrib_merges_1, - total_distrib_merges_2, total_distrib_successes); -} diff --git a/gnu/usr.bin/gcc1/cc1/conditions.h b/gnu/usr.bin/gcc1/cc1/conditions.h deleted file mode 100644 index 030c455b06..0000000000 --- a/gnu/usr.bin/gcc1/cc1/conditions.h +++ /dev/null @@ -1,99 +0,0 @@ -/* Definitions for condition code handling in final.c and output routines. - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* The variable cc_status says how to interpret the condition code. - It is set by output routines for an instruction that sets the cc's - and examined by output routines for jump instructions. - - cc_status contains two components named `value1' and `value2' - that record two equivalent expressions for the values that the - condition codes were set from. (Either or both may be null if - there is no useful expression to record.) These fields are - used for eliminating redundant test and compare instructions - in the cases where the condition codes were already set by the - previous instruction. - - cc_status.flags contains flags which say that the condition codes - were set in a nonstandard manner. The output of jump instructions - uses these flags to compensate and produce the standard result - with the nonstandard condition codes. Standard flags are defined here. - The tm- file can also define other machine-dependent flags. - - cc_status also contains a machine-dependent component `mdep' - whose type, `CC_STATUS_MDEP', may be defined as a macro in the - tm- file. */ - -#ifndef CC_STATUS_MDEP -#define CC_STATUS_MDEP int -#endif - -#ifndef CC_STATUS_MDEP_INIT -#define CC_STATUS_MDEP_INIT 0 -#endif - -typedef struct {int flags; rtx value1, value2; CC_STATUS_MDEP mdep;} CC_STATUS; - -/* While outputting an insn as assembler code, - this is the status BEFORE that insn. */ -extern CC_STATUS cc_prev_status; - -/* While outputting an insn as assembler code, - this is being altered to the status AFTER that insn. */ -extern CC_STATUS cc_status; - -/* These are the machine-independent flags: */ - -/* Set if the sign of the cc value is inverted: - output a following jump-if-less as a jump-if-greater, etc. */ -#define CC_REVERSED 1 - -/* This bit means that the current setting of the N bit is bogus - and conditional jumps should use the Z bit in its place. - This state obtains when an extraction of a signed single-bit field - or an arithmetic shift right of a byte by 7 bits - is turned into a btst, because btst does not set the N bit. */ -#define CC_NOT_POSITIVE 2 - -/* This bit means that the current setting of the N bit is bogus - and conditional jumps should pretend that the N bit is clear. - Used after extraction of an unsigned bit - or logical shift right of a byte by 7 bits is turned into a btst. - The btst does not alter the N bit, but the result of that shift - or extract is never negative. */ -#define CC_NOT_NEGATIVE 4 - -/* This bit means that the current setting of the overflow flag - is bogus and conditional jumps should pretend there is no overflow. */ -#define CC_NO_OVERFLOW 010 - -/* This bit means that what ought to be in the Z bit - should be tested as the complement of the N bit. */ -#define CC_Z_IN_NOT_N 020 - -/* This bit means that what ought to be in the Z bit - should be tested as the N bit. */ -#define CC_Z_IN_N 040 - -/* This is how to initialize the variable cc_status. - final does this at appropriate moments. */ - -#define CC_STATUS_INIT \ - (cc_status.flags = 0, cc_status.value1 = 0, cc_status.value2 = 0, \ - CC_STATUS_MDEP_INIT) diff --git a/gnu/usr.bin/gcc1/cc1/config/i386.md b/gnu/usr.bin/gcc1/cc1/config/i386.md deleted file mode 100644 index fc9c9e1802..0000000000 --- a/gnu/usr.bin/gcc1/cc1/config/i386.md +++ /dev/null @@ -1,1986 +0,0 @@ -;; -;; This code is derived from software copyrighted by the Free Software -;; Foundation. -;; -;; Modified 1991 by Donn Seeley at UUNET Technologies, Inc. -;; -;; @(#)i386.md 6.2 (Berkeley) 5/8/91 -;; - -;; GCC machine description for Intel 80386. -;; Copyright (C) 1988 Free Software Foundation, Inc. -;; Mostly by William Schelter. - -;; This file is part of GNU CC. - -;; GNU CC is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 1, or (at your option) -;; any later version. - -;; GNU CC is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU CC; see the file COPYING. If not, write to -;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - -;;- instruction definitions - -;;- @@The original PO technology requires these to be ordered by speed, -;;- @@ so that assigner will pick the fastest. - -;;- See file "rtl.def" for documentation on define_insn, match_*, et. al. - -;;- When naming insn's (operand 0 of define_insn) be careful about using -;;- names from other targets machine descriptions. - -;;- cpp macro #define NOTICE_UPDATE_CC in file tm.h handles condition code -;;- updates for most instructions. - -;;- Operand classes for the register allocator: -;;- 'a' for eax -;;- 'd' for edx -;;- 'c' for ecx -;;- 'b' for ebx -;;- 'f' for anything in FLOAT_REGS -;;- 'r' any (non-floating-point) register -;;- 'q' regs that allow byte operations (A, B, C and D) -;;- 'A' A and D registers - -;; the special asm out single letter directives following a '%' are: -;; 'z' mov%z1 would be movl, movw, or movb depending on the mode of operands[1] -;; 's' output a '*' -;; 'w' If the operand is a REG, it uses the mode size to determine the -;; printing of the reg - - -;; Put tstsi first among test insns so it matches a CONST_INT operand. - -(define_insn "tstsi" - [(set (cc0) - (match_operand:SI 0 "general_operand" "rm"))] - "" - "* -{ - operands[1] = const0_rtx; - if (REG_P (operands[0])) - return AS2 (test%L0,%0,%0); - return AS2 (cmp%L0,%1,%0); -}") - -(define_insn "tsthi" - [(set (cc0) - (match_operand:HI 0 "general_operand" "rm"))] - "" - "* -{ - operands[1] = const0_rtx; - if (REG_P (operands[0])) - return AS2 (test%W0,%0,%0); - return AS2 (cmp%W0,%1,%0); -}") - -(define_insn "tstqi" - [(set (cc0) - (match_operand:QI 0 "general_operand" "qm"))] - "" - "* -{ - operands[1] = const0_rtx; - if (REG_P (operands[0])) - return AS2 (test%B0,%0,%0); - return AS2 (cmp%B0,%1,%0); -}") - -(define_insn "tstsf" - [(set (cc0) - (match_operand:SF 0 "general_operand" "rm,f")) - (clobber (reg:SI 0))] - "TARGET_80387" - "* -{ - rtx xops[1]; - if (!FP_REG_P (operands[0])) - fp_push_sf (operands[0]); -/* fp_pop_level--; */ - xops[0] = FP_TOP; - cc_status.flags |= CC_IN_80387; - if (FP_REG_P (operands[0]) && ! top_dead_p (insn)) - output_asm_insn (\"ftst\;fnstsw %R0ax\;sahf\", xops); - else - output_asm_insn (\"ftst\;fstp %0(0)\;fnstsw %R0ax\;sahf\", xops); - RETCOM (testsf); -}") - -(define_insn "tstdf" - [(set (cc0) - (match_operand:DF 0 "general_operand" "rm,f")) - (clobber (reg:SI 0)) - ] - "TARGET_80387" - "* -{ - rtx xops[1]; - if (!FP_REG_P (operands[0])) - fp_push_df (operands[0]); -/* fp_pop_level--; */ - xops[0] = FP_TOP; - cc_status.flags |= CC_IN_80387; - if (FP_REG_P (operands[0]) && ! top_dead_p (insn)) - output_asm_insn (\"ftst\;fnstsw %R0ax\;sahf\", xops); - else - output_asm_insn (\"ftst\;fstp %0(0)\;fnstsw %R0ax\;sahf\", xops); - RETCOM (testdf); -}") - -;;- compare instructions - -;; Put cmpsi first among compare insns so it matches two CONST_INT operands. - -(define_insn "cmpsi" - [(set (cc0) - (compare (match_operand:SI 0 "general_operand" "mr,ri") - (match_operand:SI 1 "general_operand" "ri,mr")))] - "" - "* -{ - if (REG_P (operands[1]) - || (!REG_P (operands[0]) && GET_CODE (operands[0]) != MEM)) - { - cc_status.flags |= CC_REVERSED; - return AS2 (cmp%L0,%0,%1); - } - return AS2 (cmp%L0,%1,%0); -}") - -(define_insn "cmphi" - [(set (cc0) - (compare (match_operand:HI 0 "general_operand" "mr,ri") - (match_operand:HI 1 "general_operand" "ri,mr")))] - "" - "* -{ - if (REG_P (operands[1]) - || (!REG_P (operands[0]) && GET_CODE (operands[0]) != MEM)) - { - cc_status.flags |= CC_REVERSED; - return AS2 (cmp%W0,%0,%1); - } - return AS2 (cmp%W0,%1,%0); -}") - -(define_insn "cmpqi" - [(set (cc0) - (compare (match_operand:QI 0 "general_operand" "qn,mq") - (match_operand:QI 1 "general_operand" "qm,nq")))] - "" - "* -{ - if (REG_P (operands[1]) - || (!REG_P (operands[0]) && GET_CODE (operands[0]) != MEM)) - { - cc_status.flags |= CC_REVERSED; - return AS2 (cmp%B0,%0,%1); - } - return AS2 (cmp%B0,%1,%0); -}") - -(define_insn "cmpdf" - [(set (cc0) - (compare (match_operand:DF 0 "general_operand" "m,f*r,m,f,r,!*r") - (match_operand:DF 1 "general_operand" "m,m,f*r,r,f,*r"))) - (clobber (reg:SI 0))] - "TARGET_80387" - "* -{ - if (FP_REG_P (operands[0])) - { - rtx tem = operands[1]; - operands[1] = operands[0]; - operands[0] = tem; - cc_status.flags |= CC_REVERSED; - } - if (! FP_REG_P (operands[1])) - output_movdf (FP_TOP, operands[1]); - output_movdf (FP_TOP, operands[0]); -/* fp_pop_level--; - fp_pop_level--; */ - cc_status.flags |= CC_IN_80387; - return \"fcompp\;fnstsw %R0ax\;sahf\"; -}") - -(define_insn "cmpsf" - [(set (cc0) - (compare (match_operand:SF 0 "general_operand" "m,f*r,m,f,r,!*r") - (match_operand:SF 1 "general_operand" "m,m,f*r,r,f,*r"))) - (clobber (reg:SI 0))] - "TARGET_80387" - "* -{ - if (FP_REG_P (operands[0])) - { - rtx tem = operands[1]; - operands[1] = operands[0]; - operands[0] = tem; - cc_status.flags |= CC_REVERSED; - } - if (! FP_REG_P (operands[1])) - output_movsf (FP_TOP, operands[1]); - output_movsf (FP_TOP, operands[0]); -/* fp_pop_level--; - fp_pop_level--; */ - cc_status.flags |= CC_IN_80387; - return \"fcompp\;fnstsw %R0ax\;sahf\"; -}") - -;; logical compare -(define_insn "" - [(set (cc0) - (and:SI (match_operand:SI 0 "general_operand" "rm,ri") - (match_operand:SI 1 "general_operand" "ri,rm")))] - "" - "* -{ - if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM) - return AS2 (test%L0,%1,%0); - return AS2 (test%L0,%0,%1); -}") - -(define_insn "" - [(set (cc0) - (and:HI (match_operand:HI 0 "general_operand" "rm,ri") - (match_operand:HI 1 "general_operand" "ri,rm")))] - "" - "* -{ - if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM) - return AS2 (test%W0,%1,%0); - return AS2 (test%W0,%0,%1); -}") - -(define_insn "" - [(set (cc0) - (and:QI (match_operand:QI 0 "general_operand" "qm,qi") - (match_operand:QI 1 "general_operand" "qi,qm")))] - "" - "* -{ - if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM) - return AS2 (test%B0,%1,%0); - return AS2 (test%B0,%0,%1); -}") - -;; move instructions. -;; There is one for each machine mode, -;; and each is preceded by a corresponding push-insn pattern -;; (since pushes are not general_operands on the 386). - -(define_insn "" - [(set (match_operand:SI 0 "push_operand" "=<") - (match_operand:SI 1 "general_operand" "g"))] - "" - "push%L0 %1") - -;; General case of fullword move. -(define_insn "movsi" - [(set (match_operand:SI 0 "general_operand" "=g,r") - (match_operand:SI 1 "general_operand" "ri,m"))] - "" - "* -{ - rtx link; - if (operands[1] == const0_rtx && REG_P (operands[0])) - return \"xor%L0 %0,%0\"; - if (operands[1] == const1_rtx - && (link = find_reg_note (insn, REG_WAS_0, 0)) - /* Make sure the insn that stored the 0 is still present. */ - && ! XEXP (link, 0)->volatil - && GET_CODE (XEXP (link, 0)) != NOTE - /* Make sure cross jumping didn't happen here. */ - && no_labels_between_p (XEXP (link, 0), insn)) - /* Fastest way to change a 0 to a 1. */ - return \"inc%L0 %0\"; - return \"mov%L0 %1,%0\"; -}") - -(define_insn "" - [(set (match_operand:HI 0 "push_operand" "=<") - (match_operand:HI 1 "general_operand" "g"))] - "" - "push%W0 %1") - -(define_insn "movhi" - [(set (match_operand:HI 0 "general_operand" "=g,r") - (match_operand:HI 1 "general_operand" "ri,m"))] - "" - "* -{ - rtx link; - if (operands[1] == const0_rtx && REG_P (operands[0])) - return \"xor%W0 %0,%0\"; - if (operands[1] == const1_rtx - && (link = find_reg_note (insn, REG_WAS_0, 0)) - /* Make sure the insn that stored the 0 is still present. */ - && ! XEXP (link, 0)->volatil - && GET_CODE (XEXP (link, 0)) != NOTE - /* Make sure cross jumping didn't happen here. */ - && no_labels_between_p (XEXP (link, 0), insn)) - /* Fastest way to change a 0 to a 1. */ - return \"inc%W0 %0\"; - return \"mov%W0 %1,%0\"; -}") - -;; emit_push_insn when it calls move_by_pieces -;; requires an insn to "push a byte". -;; But actually we use pushw, which has the effect of rounding -;; the amount pushed up to a halfword. -(define_insn "" - [(set (match_operand:QI 0 "push_operand" "=<") - (match_operand:QI 1 "general_operand" "q"))] - "" - "* -{ - operands[1] = gen_rtx (REG, HImode, REGNO (operands[1])); - return \"push%W0 %1\"; -}") - -(define_insn "movqi" - [(set (match_operand:QI 0 "general_operand" "=q,*r,m") - (match_operand:QI 1 "general_operand" "*g,q,qi"))] - "" - "* -{ - rtx link; - if (operands[1] == const0_rtx && REG_P (operands[0])) - return \"xor%B0 %0,%0\"; - if (operands[1] == const1_rtx - && (link = find_reg_note (insn, REG_WAS_0, 0)) - /* Make sure the insn that stored the 0 is still present. */ - && ! XEXP (link, 0)->volatil - && GET_CODE (XEXP (link, 0)) != NOTE - /* Make sure cross jumping didn't happen here. */ - && no_labels_between_p (XEXP (link, 0), insn)) - /* Fastest way to change a 0 to a 1. */ - return \"inc%B0 %0\"; - /* If mov%B0 isn't allowed for one of these regs, use mov%W0. */ - if (NON_QI_REG_P (operands[0]) || NON_QI_REG_P (operands[1])) - return (AS2 (mov%W0,%w1,%w0)); - return (AS2 (mov%B0,%1,%0)); -}") - -; I suspect nothing can ever match this ??? -;(define_insn "" -; [(set (match_operand:SF 0 "general_operand" "rm") -; (match_operand:SF 1 "general_operand" "f")) -; (clobber (reg:SF 8))] -; "" -; "* -;{ -; output_asm_insn ("???", operands); -; fpop_sf (operands[0]); -; RETCOM (movsf_clobber); -;}") - -(define_insn "" - [(set (match_operand:SF 0 "push_operand" "=<,<") - (match_operand:SF 1 "general_operand" "gF,f"))] - "" - "* -{ - if (FP_REG_P (operands[1])) - { - rtx xops[3]; - xops[0] = AT_SP (SFmode); - xops[1] = gen_rtx (CONST_INT, VOIDmode, 4); - xops[2] = stack_pointer_rtx; -/* fp_pop_level--; */ - output_asm_insn (AS2 (sub%L0,%1,%2), xops); - if (top_dead_p (insn)) - output_asm_insn (\"fstp%S0 %0\", xops); - else - output_asm_insn (\"fst%S0 %0\", xops); - RET; - } - return \"push%L0 %1\"; -}") - -(define_insn "movsf" - ;; `rf' is duplicated in the second alternative - ;; to make sure an optional reload is generated - ;; for the memref in operand 0. Otherwise - ;; we could use too many hard regs. - [(set (match_operand:SF 0 "general_operand" "=rf,mrf,!rm") - (match_operand:SF 1 "general_operand" "mrf,rf,F"))] - "" - "* -{ - if (FP_REG_P (operands[1]) - && !FP_REG_P (operands[0]) - && !top_dead_p (insn)) - fp_store_sf (operands[0]); - else - output_movsf (operands[0], operands[1]); - RETCOM (movsf); -}") - -;;should change to handle the memory operands[1] without doing df push.. -(define_insn "" - [(set (match_operand:DF 0 "push_operand" "=<,<") - (match_operand:DF 1 "general_operand" "gF,f"))] - "" - "* -{ - if (FP_REG_P (operands[1])) - { - rtx xops[3]; - xops[0] = AT_SP (DFmode); - xops[1] = gen_rtx (CONST_INT, VOIDmode, 8); - xops[2] = stack_pointer_rtx; -/* fp_pop_level--; */ - output_asm_insn (AS2 (sub%L0,%1,%2), xops); - if (top_dead_p(insn)) - output_asm_insn (\"fstp%Q0 %0\", xops); - else - output_asm_insn (\"fst%Q0 %0\", xops); - RETCOM (pushdf); - } - else - return output_move_double (operands); -}") - -(define_insn "movdf" - [(set (match_operand:DF 0 "general_operand" "=rmf,&fr,!rm") - ;; `rf' is duplicated in the second alternative - ;; to make sure that optional reloads are generated - ;; for the memory reference in operand 1. - (match_operand:DF 1 "general_operand" "fr,mrf,F"))] - "" - "* -{ - if (FP_REG_P (operands[1]) - && ! FP_REG_P (operands[0]) - && ! top_dead_p (insn)) - fp_store_df (operands[0]); - else - output_movdf (operands[0], operands[1]); - RETCOM (movdf); -}") - -(define_insn "" - [(set (match_operand:DI 0 "push_operand" "=<") - (match_operand:DI 1 "general_operand" "roiF"))] - "" - "* -{ - return output_move_double (operands); -}") - -(define_insn "movdi" - [(set (match_operand:DI 0 "general_operand" "=&r,rm") - (match_operand:DI 1 "general_operand" "m,riF"))] - "" - "* -{ - return output_move_double (operands); -}") - -;; These go after the move instructions -;; because the move instructions are better (require no spilling) -;; when they can apply. But these go before the add and subtract insns -;; because it is often shorter to use these when both apply. - -;Lennart Augustsson -;says this pattern just makes slower code: -; pushl %ebp -; addl $-80,(%esp) -;instead of -; leal -80(%ebp),%eax -; pushl %eax -; -;(define_insn "" -; [(set (match_operand:SI 0 "push_operand" "=<") -; (plus:SI (match_operand:SI 1 "general_operand" "%r") -; (match_operand:SI 2 "general_operand" "ri")))] -; "" -; "* -;{ -; rtx xops[4]; -; xops[0] = operands[0]; -; xops[1] = operands[1]; -; xops[2] = operands[2]; -; xops[3] = gen_rtx (MEM, SImode, stack_pointer_rtx); -; output_asm_insn (\"push%z1 %1\", xops); -; output_asm_insn (AS2 (add%z3,%2,%3), xops); -; RET; -;}") - -(define_insn "" - [(set (match_operand:SI 0 "general_operand" "=g") - (plus:SI (match_operand:SI 1 "general_operand" "0") - (const_int 1)))] - "" - "inc%L0 %0") - -(define_insn "" - [(set (match_operand:SI 0 "general_operand" "=g") - (plus:SI (match_operand:SI 1 "general_operand" "0") - (const_int -1)))] - "" - "dec%L0 %0") - -(define_insn "" - [(set (match_operand:SI 0 "general_operand" "=g") - (minus:SI (match_operand:SI 1 "general_operand" "0") - (const_int 1)))] - "" - "dec%L0 %0") - -(define_insn "" - [(set (match_operand:SI 0 "register_operand" "=r") - (match_operand:QI 1 "address_operand" "p"))] - "" - "* -{ - CC_STATUS_INIT; - /* Adding a constant to a register is faster with an add. */ - if (GET_CODE (operands[1]) == PLUS - && GET_CODE (XEXP (operands[1], 1)) == CONST_INT - && rtx_equal_p (operands[0], XEXP (operands[1], 0))) - { - operands[1] = XEXP (operands[1], 1); - return AS2 (add%L0,%1,%0); - } - return \"lea%L0 %a1,%0\"; -}") - -;;- conversion instructions -;;- NONE - -;;- truncation instructions -(define_insn "truncsiqi2" - [(set (match_operand:QI 0 "general_operand" "=q,m") - (truncate:QI - (match_operand:SI 1 "general_operand" "qim,qn")))] - "" - "* -{ - if (CONSTANT_P (operands[1]) && GET_CODE (operands[1]) != CONST_INT) - return \"mov%L0 %1,%k0\"; - return \"mov%B0 %b1,%0\"; -}") - -(define_insn "trunchiqi2" - [(set (match_operand:QI 0 "general_operand" "=q,m") - (truncate:QI - (match_operand:HI 1 "general_operand" "qim,qn")))] - "" - "* -{ - if (CONSTANT_P (operands[1]) && GET_CODE (operands[1]) != CONST_INT) - return \"mov%W0 %1,%w0\"; - return \"mov%B0 %b1,%0\"; -}") - -(define_insn "truncsihi2" - [(set (match_operand:HI 0 "general_operand" "=r,m") - (truncate:HI - (match_operand:SI 1 "general_operand" "rim,rn")))] - "" - "* -{ - if (CONSTANT_P (operands[1]) && GET_CODE (operands[1]) != CONST_INT) - return \"mov%L0 %1,%k0\"; - return \"mov%W0 %w1,%0\"; -}") - -;;- zero extension instructions -;; Note that the one starting from HImode comes before those for QImode -;; so that a constant operand will match HImode, not QImode. - -(define_insn "zero_extendhisi2" - [(set (match_operand:SI 0 "general_operand" "=r") - (zero_extend:SI - (match_operand:HI 1 "general_operand" "rm")))] - "" - "movz%W0%L0 %1,%0") - -(define_insn "zero_extendqihi2" - [(set (match_operand:HI 0 "general_operand" "=r") - (zero_extend:HI - (match_operand:QI 1 "general_operand" "qm")))] - "" - "movz%B0%W0 %1,%0") - -(define_insn "zero_extendqisi2" - [(set (match_operand:SI 0 "general_operand" "=r") - (zero_extend:SI - (match_operand:QI 1 "general_operand" "qm")))] - "" - "movz%B0%L0 %1,%0") - -;;- sign extension instructions -;; Note that the one starting from HImode comes before those for QImode -;; so that a constant operand will match HImode, not QImode. - -/* -(define_insn "extendsidi2" - [(set (match_operand:DI 0 "general_operand" "=a") - (sign_extend:DI - (match_operand:SI 1 "general_operand" "a")))] - "" - "clq") -*/ - -;; Note that the i386 programmers' manual says that the opcodes -;; are named movsx..., but the assembler on Unix does not accept that. -;; We use what the Unix assembler expects. - -(define_insn "extendhisi2" - [(set (match_operand:SI 0 "general_operand" "=r") - (sign_extend:SI - (match_operand:HI 1 "general_operand" "rm")))] - "" - "movs%W0%L0 %1,%0") - -(define_insn "extendqihi2" - [(set (match_operand:HI 0 "general_operand" "=r") - (sign_extend:HI - (match_operand:QI 1 "general_operand" "qm")))] - "" - "movs%B0%W0 %1,%0") - -(define_insn "extendqisi2" - [(set (match_operand:SI 0 "general_operand" "=r") - (sign_extend:SI - (match_operand:QI 1 "general_operand" "qm")))] - "" - "movs%B0%L0 %1,%0" - ) - -;; Conversions between float and double. - -(define_insn "extendsfdf2" - [(set (match_operand:DF 0 "general_operand" "=fm,f,fm,fm") - (float_extend:DF - (match_operand:SF 1 "general_operand" "m,0,f,!*r")))] - "TARGET_80387" - "* -{ - if (FP_REG_P (operands[0])) - { - output_movsf (operands[0], operands[1]); - RET; - } - if (FP_REG_P (operands[1])) - { - if (top_dead_p (insn)) - fp_pop_df (operands[0]); - else - fp_store_df (operands[0]); - RET; - } - output_movsf (FP_TOP, operands[1]); - fp_pop_df (operands[0]); - RETCOM (extendsfdf2); -}") - -;; This cannot output into an f-reg because there is no way to be -;; sure of truncating in that case. -(define_insn "truncdfsf2" - [(set (match_operand:SF 0 "general_operand" "=m,!*r") - (float_truncate:SF - (match_operand:DF 1 "general_operand" "f,f")))] - "TARGET_80387" - "* -{ - if (top_dead_p (insn)) - fp_pop_sf (operands[0]); - else - fp_store_sf (operands[0]); - RETCOM (truncdfsf2); -}") - -;; Conversion between fixed point and floating point. -;; Note that among the fix-to-float insns -;; the ones that start with SImode come first. -;; That is so that an operand that is a CONST_INT -;; (and therefore lacks a specific machine mode). -;; will be recognized as SImode (which is always valid) -;; rather than as QImode or HImode. The 80387 would not know -;; what to do with the smaller sizes anyway. (I think). - -(define_insn "floatsisf2" - [(set (match_operand:SF 0 "general_operand" "=fm,fm") - (float:SF (match_operand:SI 1 "general_operand" "m,!*r")))] - "TARGET_80387" - "* -{ -/* fp_pop_level++; */ - - if (GET_CODE (operands[1]) != MEM) - { - rtx xops[2]; - output_asm_insn (\"push%L0 %1\", operands); - operands[1] = AT_SP (SImode); - output_asm_insn (\"fild%L0 %1\", operands); - xops[0] = stack_pointer_rtx; - xops[1] = gen_rtx (CONST_INT, VOIDmode, 4); - output_asm_insn (AS2 (add%L0,%1,%0), xops); - } - else - output_asm_insn (\"fild%L0 %1\", operands); - - if (! FP_REG_P (operands[0])) - { -/* fp_pop_level--; */ - return \"fstp%S0 %0\"; - } - RET; -}") - -(define_insn "floatsidf2" - [(set (match_operand:DF 0 "general_operand" "=fm,fm") - (float:DF (match_operand:SI 1 "general_operand" "m,!*r")))] - "TARGET_80387" - "* -{ -/* fp_pop_level++; */ - if (GET_CODE (operands[1]) != MEM) - { - rtx xops[2]; - output_asm_insn (\"push%L0 %1\", operands); - operands[1] = AT_SP (SImode); - output_asm_insn (\"fild%L0 %1\", operands); - xops[0] = stack_pointer_rtx; - xops[1] = gen_rtx (CONST_INT, VOIDmode, 4); - output_asm_insn (AS2 (add%L0,%1,%0), xops); - } - else - output_asm_insn (\"fild%L0 %1\", operands); - if (! FP_REG_P (operands[0])) - { -/* fp_pop_level--; */ - return \"fstp%Q0 %0\"; - } - RET; -}") - -;; Convert a float to a float whose value is an integer. -;; This is the first stage of converting it to an integer type. - -;; On the 387 truncating doub to an short integer shor can be performed: - -; fstcw -4(%esp) ;save cw -; movw -4(%esp),%ax -; orw $0x0c00,%ax ;set rounding to chop towards zero -; movw %ax,-2(%esp) ; -; fldcw -2(%esp) ; -; fldl doubl -; fistpl -12(%esp) ;store the round value -; fldcw -4(%esp) ;restore cw -; movl -12(%esp),%eax -; movw %ax,shor ; move the result into shor. - -;; but it is probably better to have a call, rather than waste this -;; space. The last instruction would have been a movl if were -;; going to an int instead of a short. -;; For the moment we will go with the soft float for these. - -/* These are incorrect since they don't set the rounding bits of CW flag. - The proper way to do that is to make the function prologue save the CW - and also construct the alternate CW value needed for these insns. - Then these insns can output two fldcw's, referring to fixed places in - the stack frame. - -;; Convert a float whose value is an integer -;; to an actual integer. Second stage of converting float to integer type. - -(define_insn "fix_truncsfqi2" - [(set (match_operand:QI 0 "general_operand" "=m,?*q") - (fix:QI (fix:SF (match_operand:SF 1 "general_operand" "f,f"))))] - "TARGET_80387" - "* -{ - fp_pop_int (operands[0]); - RET; -}") - -(define_insn "fix_truncsfhi2" - [(set (match_operand:HI 0 "general_operand" "=m,?*r") - (fix:HI (fix:SF (match_operand:SF 1 "general_operand" "f,f"))))] - "TARGET_80387" - "* -{ - fp_pop_int (operands[0]); - RET; -}") - -(define_insn "fix_truncsfsi2" - [(set (match_operand:SI 0 "general_operand" "=m,?*r") - (fix:SI (fix:SF (match_operand:SF 1 "general_operand" "f,f"))))] - "TARGET_80387" - "* -{ - fp_pop_int (operands[0]); - RET; -}") - -(define_insn "fix_truncdfqi2" - [(set (match_operand:QI 0 "general_operand" "=m,?*q") - (fix:QI (fix:DF (match_operand:DF 1 "general_operand" "f,f"))))] - - "TARGET_80387" - "* -{ - fp_pop_int (operands[0]); - RET; -}") - - -(define_insn "fix_truncdfhi2" - [(set (match_operand:HI 0 "general_operand" "=m,?*r") - (fix:HI (fix:DF (match_operand:DF 1 "general_operand" "f,f"))))] - "TARGET_80387" - "* -{ - fp_pop_int (operands[0]); - RET; -}") - - -(define_insn "fix_truncdfsi2" - [(set (match_operand:SI 0 "general_operand" "=m,?*r") - (fix:SI (fix:DF (match_operand:DF 1 "general_operand" "f,f"))))] - "TARGET_80387" - "* -{ - fp_pop_int (operands[0]); - RET; -}") -*/ - - -;;- add instructions -;;moved incl to above leal - -(define_insn "addsi3" - [(set (match_operand:SI 0 "general_operand" "=rm,r") - (plus:SI (match_operand:SI 1 "general_operand" "%0,0") - (match_operand:SI 2 "general_operand" "ri,rm")))] - "" - "add%L0 %2,%0") - -(define_insn "" - [(set (match_operand:HI 0 "general_operand" "=g") - (plus:HI (match_operand:HI 1 "general_operand" "0") - (const_int 1)))] - "" - "inc%W0 %0") - -(define_insn "addhi3" - [(set (match_operand:HI 0 "general_operand" "=rm,r") - (plus:HI (match_operand:HI 1 "general_operand" "%0,0") - (match_operand:HI 2 "general_operand" "ri,rm")))] - "" - "add%W0 %2,%0") - -(define_insn "" - [(set (match_operand:QI 0 "general_operand" "=qm") - (plus:QI (match_operand:QI 1 "general_operand" "0") - (const_int 1)))] - "" - "inc%B0 %0") - -(define_insn "addqi3" - [(set (match_operand:QI 0 "general_operand" "=m,q") - (plus:QI (match_operand:QI 1 "general_operand" "%0,0") - (match_operand:QI 2 "general_operand" "qn,qmn")))] - "" - "add%B0 %2,%0") - -;;had "fmF,m" - -(define_insn "adddf3" - [(set (match_operand:DF 0 "general_operand" "=f,m,f") - (plus:DF (match_operand:DF 1 "general_operand" "%0,0,0") - (match_operand:DF 2 "general_operand" "m,!f,!*r")))] - "TARGET_80387" - "*FP_CALL (\"fadd%z0 %0\", \"fadd%z0 %0\", 2)") - -(define_insn "addsf3" - [(set (match_operand:SF 0 "general_operand" "=f,m,f") - (plus:SF (match_operand:SF 1 "general_operand" "%0,0,0") - (match_operand:SF 2 "general_operand" "m,!f,!*r")))] - "TARGET_80387" - "*FP_CALL (\"fadd%z0 %0\", \"fadd%z0 %0\", 2)") - -;;- subtract instructions - -;;moved decl above leal - -(define_insn "subsi3" - [(set (match_operand:SI 0 "general_operand" "=rm,r") - (minus:SI (match_operand:SI 1 "general_operand" "0,0") - (match_operand:SI 2 "general_operand" "ri,rm")))] - "" - "sub%L0 %2,%0") - -(define_insn "" - [(set (match_operand:HI 0 "general_operand" "=g") - (minus:HI (match_operand:HI 1 "general_operand" "0") - (const_int 1)))] - "" - "dec%W0 %0") - -(define_insn "subhi3" - [(set (match_operand:HI 0 "general_operand" "=rm,r") - (minus:HI (match_operand:HI 1 "general_operand" "0,0") - (match_operand:HI 2 "general_operand" "ri,rm")))] - "" - "sub%W0 %2,%0") - -(define_insn "" - [(set (match_operand:QI 0 "general_operand" "=qm") - (minus:QI (match_operand:QI 1 "general_operand" "0") - (const_int 1)))] - "" - "dec%B0 %0") - -(define_insn "subqi3" - [(set (match_operand:QI 0 "general_operand" "=m,q") - (minus:QI (match_operand:QI 1 "general_operand" "0,0") - (match_operand:QI 2 "general_operand" "qn,qmn")))] - "" - "sub%B0 %2,%0") - -(define_insn "subdf3" - [(set (match_operand:DF 0 "general_operand" "=f,m,f,f") - (minus:DF (match_operand:DF 1 "general_operand" "0,0,0,m") - (match_operand:DF 2 "general_operand" "m,!f,!*r,*0")))] - "TARGET_80387" - "*FP_CALL (\"fsub%z0 %0\", \"fsubr%z0 %0\", 2)") - - -(define_insn "subsf3" - [(set (match_operand:SF 0 "general_operand" "=f,m,f,f") - (minus:SF (match_operand:SF 1 "general_operand" "0,0,0,m") - (match_operand:SF 2 "general_operand" "m,!f,!*r,*0")))] - "TARGET_80387" - "*FP_CALL (\"fsub%z0 %0\", \"fsubr%z0 %0\", 2)") - -;;- multiply instructions - -;(define_insn "mulqi3" -; [(set (match_operand:QI 0 "general_operand" "=a") -; (mult:QI (match_operand:QI 1 "general_operand" "%0") -; (match_operand:QI 2 "general_operand" "qm")))] -; "" -; "mul%B0 %2,%0") - -(define_insn "mulhi3" - [(set (match_operand:HI 0 "general_operand" "=r,r") - (mult:SI (match_operand:HI 1 "general_operand" "%0,rm") - (match_operand:HI 2 "general_operand" "g,i")))] - "" - "* -{ - if (GET_CODE (operands[1]) == REG - && REGNO (operands[1]) == REGNO (operands[0]) - && (GET_CODE (operands[2]) == MEM - || GET_CODE (operands[2]) == REG)) - /* Assembler has weird restrictions. */ - return AS2 (imul%W0,%2,%0); - return AS3 (imul%W0,%2,%1,%0); -}") - -(define_insn "mulsi3" - [(set (match_operand:SI 0 "general_operand" "=r,r") - (mult:SI (match_operand:SI 1 "general_operand" "%0,rm") - (match_operand:SI 2 "general_operand" "g,i")))] - "" - "* -{ - if (GET_CODE (operands[1]) == REG - && REGNO (operands[1]) == REGNO (operands[0]) - && (GET_CODE (operands[2]) == MEM - || GET_CODE (operands[2]) == REG)) - /* Assembler has weird restrictions. */ - return AS2 (imul%L0,%2,%0); - return AS3 (imul%L0,%2,%1,%0); -}") - -;; Turned off due to possible assembler bug. -;(define_insn "umulqi3" -; [(set (match_operand:QI 0 "general_operand" "=a") -; (umult:QI (match_operand:QI 1 "general_operand" "%0") -; (match_operand:QI 2 "general_operand" "qm")))] -; "" -; "mul%B0 %2,%0") - -;(define_insn "umulqihi3" -; [(set (match_operand:HI 0 "general_operand" "=a") -; (umult:HI (match_operand:QI 1 "general_operand" "%0") -; (match_operand:QI 2 "general_operand" "qm")))] -; "" -; "mul%B0 %2,%0") - -(define_insn "umulhi3" - [(set (match_operand:HI 0 "general_operand" "=a") - (umult:SI (match_operand:HI 1 "general_operand" "%0") - (match_operand:HI 2 "general_operand" "rm"))) - (clobber (reg:HI 1))] - "" - "mul%W0 %2,%0") - -(define_insn "umulsi3" - [(set (match_operand:SI 0 "general_operand" "=a") - (umult:SI (match_operand:SI 1 "general_operand" "%0") - (match_operand:SI 2 "general_operand" "rm"))) - (clobber (reg:SI 1))] - "" - "mul%L0 %2,%0") - -(define_insn "muldf3" - [(set (match_operand:DF 0 "general_operand" "=f,m,f") - (mult:DF (match_operand:DF 1 "general_operand" "%0,0,0") - (match_operand:DF 2 "general_operand" "m,!f,!*r")))] - "TARGET_80387" - "*FP_CALL (\"fmul%z0 %0\", \"fmul%z0 %0\", 2) -") - -(define_insn "mulsf3" - [(set (match_operand:SF 0 "general_operand" "=f,m,f") - (mult:SF (match_operand:SF 1 "general_operand" "%0,0,0") - (match_operand:SF 2 "general_operand" "m,!f,!*r")))] - "TARGET_80387" - "*FP_CALL (\"fmul%z0 %0\", \"fmul%z0 %0\", 2) -") - -;;- divide instructions -(define_insn "divdf3" - [(set (match_operand:DF 0 "general_operand" "=f,m,f,f") - (div:DF (match_operand:DF 1 "general_operand" "0,0,0,m") - (match_operand:DF 2 "general_operand" "m,!f,!*r,*0")))] - "TARGET_80387" - "*FP_CALL (\"fdiv%z0 %0\", \"fdivr%z0 %0\", 2) -") - -(define_insn "divsf3" - [(set (match_operand:SF 0 "general_operand" "=f,m,f,f") - (div:SF (match_operand:SF 1 "general_operand" "0,0,0,m") - (match_operand:SF 2 "general_operand" "m,!f,!*r,*0")))] - "TARGET_80387" - "*FP_CALL (\"fdiv%z0 %0\", \"fdivr%z0 %0\", 2) -") - -;; Remainder instructions. - -(define_insn "divmodsi4" - [(set (match_operand:SI 0 "general_operand" "=a") - (div:SI (match_operand:SI 1 "general_operand" "0") - (match_operand:SI 2 "general_operand" "rm"))) - (set (match_operand:SI 3 "general_operand" "=&d") - (mod:SI (match_dup 1) (match_dup 2)))] - "" - "cltd\;idiv%L0 %2") - -(define_insn "udivmodsi4" - [(set (match_operand:SI 0 "general_operand" "=a") - (udiv:SI (match_operand:SI 1 "general_operand" "0") - (match_operand:SI 2 "general_operand" "rm"))) - (set (match_operand:SI 3 "general_operand" "=&d") - (umod:SI (match_dup 1) (match_dup 2)))] - "" - "xor%L0 %3,%3\;div%L0 %2") - -/* -;;this should be a valid double division which we may want to add - -(define_insn "" - [(set (match_operand:SI 0 "general_operand" "=a") - (udiv:DI (match_operand:DI 1 "general_operand" "a") - (match_operand:SI 2 "general_operand" "rm"))) - (set (match_operand:SI 3 "general_operand" "=d") - (umod:SI (match_dup 1) (match_dup 2)))] - "" - "div%L0 %2,%0") -*/ - -;;- and instructions - -;; The `r' in `rm' for operand 3 looks redundant, but it causes -;; optional reloads to be generated if op 3 is a pseudo in a stack slot. - -(define_insn "andsi3" - [(set (match_operand:SI 0 "general_operand" "=rm,r") - (and:SI (match_operand:SI 1 "general_operand" "%0,0") - (match_operand:SI 2 "general_operand" "ri,rm")))] - "" - "and%L0 %2,%0") - -(define_insn "andhi3" - [(set (match_operand:HI 0 "general_operand" "=rm,r") - (and:HI (match_operand:HI 1 "general_operand" "%0,0") - (match_operand:HI 2 "general_operand" "ri,rm")))] - "" - "and%W0 %2,%0") - -(define_insn "andqi3" - [(set (match_operand:QI 0 "general_operand" "=m,q") - (and:QI (match_operand:QI 1 "general_operand" "%0,0") - (match_operand:QI 2 "general_operand" "qn,qmn")))] - "" - "and%B0 %2,%0") - -/* I am nervous about these two.. add them later.. -;I presume this means that we have something in say op0= eax which is small -;and we want to and it with memory so we can do this by just an -;andb m,%al and have success. -(define_insn "" - [(set (match_operand:SI 0 "general_operand" "=r") - (and:SI (zero_extend:SI (match_operand:HI 1 "general_operand" "rm")) - (match_operand:SI 2 "general_operand" "0")))] - "GET_CODE (operands[2]) == CONST_INT - && (unsigned int) INTVAL (operands[2]) < (1 << GET_MODE_BITSIZE (HImode))" - "and%W0 %1,%0") - -(define_insn "" - [(set (match_operand:SI 0 "general_operand" "=q") - (and:SI (zero_extend:SI (match_operand:QI 1 "general_operand" "qm")) - (match_operand:SI 2 "general_operand" "0")))] - "GET_CODE (operands[2]) == CONST_INT - && (unsigned int) INTVAL (operands[2]) < (1 << GET_MODE_BITSIZE (QImode))" - "and%L0 %1,%0") - -*/ - - - -;;- Bit set (inclusive or) instructions - -(define_insn "iorsi3" - [(set (match_operand:SI 0 "general_operand" "=rm,r") - (ior:SI (match_operand:SI 1 "general_operand" "%0,0") - (match_operand:SI 2 "general_operand" "ri,rm")))] - "" - "or%L0 %2,%0") - -(define_insn "iorhi3" - [(set (match_operand:HI 0 "general_operand" "=rm,r") - (ior:HI (match_operand:HI 1 "general_operand" "%0,0") - (match_operand:HI 2 "general_operand" "ri,rm")))] - "" - "or%W0 %2,%0") - -(define_insn "iorqi3" - [(set (match_operand:QI 0 "general_operand" "=m,q") - (ior:QI (match_operand:QI 1 "general_operand" "%0,0") - (match_operand:QI 2 "general_operand" "qn,qmn")))] - "" - "or%B0 %2,%0") - -;;- xor instructions - -(define_insn "xorsi3" - [(set (match_operand:SI 0 "general_operand" "=rm,r") - (xor:SI (match_operand:SI 1 "general_operand" "%0,0") - (match_operand:SI 2 "general_operand" "ri,rm")))] - "" - "xor%L0 %2,%0") - -(define_insn "xorhi3" - [(set (match_operand:HI 0 "general_operand" "=rm,r") - (xor:HI (match_operand:HI 1 "general_operand" "%0,0") - (match_operand:HI 2 "general_operand" "ri,rm")))] - "" - "xor%W0 %2,%0") - -(define_insn "xorqi3" - [(set (match_operand:QI 0 "general_operand" "=qm") - (xor:QI (match_operand:QI 1 "general_operand" "%0") - (match_operand:QI 2 "general_operand" "qn")))] - "" - "xor%B0 %2,%0") - -;;- negation instructions -(define_insn "negsi2" - [(set (match_operand:SI 0 "general_operand" "=rm") - (neg:SI (match_operand:SI 1 "general_operand" "0")))] - "" - "neg%L0 %0") - -(define_insn "neghi2" - [(set (match_operand:HI 0 "general_operand" "=rm") - (neg:HI (match_operand:HI 1 "general_operand" "0")))] - "" - "neg%W0 %0") - -(define_insn "negqi2" - [(set (match_operand:QI 0 "general_operand" "=qm") - (neg:QI (match_operand:QI 1 "general_operand" "0")))] - "" - "neg%B0 %0") - -(define_insn "negsf2" - [(set (match_operand:SF 0 "general_operand" "=f,!m") - (neg:SF (match_operand:SF 1 "general_operand" "0,0")))] - "TARGET_80387" - "*FP_CALL1 (\"fchs\")") - -(define_insn "negdf2" - [(set (match_operand:DF 0 "general_operand" "=f,!m") - (neg:DF (match_operand:DF 1 "general_operand" "0,0")))] - "TARGET_80387" - "*FP_CALL1 (\"fchs\")") - -;; Absolute value instructions - -(define_insn "abssf2" - [(set (match_operand:SF 0 "general_operand" "=f,!m") - (abs:SF (match_operand:SF 1 "general_operand" "0,0")))] - "TARGET_80387" - "*FP_CALL1 (\"fabs\")") - -(define_insn "absdf2" - [(set (match_operand:DF 0 "general_operand" "=f,!m") - (abs:DF (match_operand:DF 1 "general_operand" "0,0")))] - "TARGET_80387" - "*FP_CALL1 (\"fabs\")") - -;;- one complement instructions -(define_insn "one_cmplsi2" - [(set (match_operand:SI 0 "general_operand" "=rm") - (not:SI (match_operand:SI 1 "general_operand" "0")))] - "" - "not%L0 %0") - -(define_insn "one_cmplhi2" - [(set (match_operand:HI 0 "general_operand" "=rm") - (not:HI (match_operand:HI 1 "general_operand" "0")))] - "" - "not%W0 %0") - -(define_insn "one_cmplqi2" - [(set (match_operand:QI 0 "general_operand" "=qm") - (not:QI (match_operand:QI 1 "general_operand" "0")))] - "" - "not%B0 %0") - -;;- arithmetic shift instructions - -(define_insn "ashlsi3" - [(set (match_operand:SI 0 "general_operand" "=rm") - (ashift:SI (match_operand:SI 1 "general_operand" "0") - (match_operand:SI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (sal%L0,%R0cl,%0); - else if (REG_P (operands[1]) && GET_CODE (operands[2]) == CONST_INT - && INTVAL (operands[2]) == 1) - return AS2 (add%L0,%1,%1); - return AS2 (sal%L0,%2,%1); -}") - -(define_insn "ashlhi3" - [(set (match_operand:HI 0 "general_operand" "=rm") - (ashift:HI (match_operand:HI 1 "general_operand" "0") - (match_operand:HI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (sal%W0,%R0cl,%0); - else - return AS2 (sal%W0,%2,%1); -}") - -(define_insn "ashlqi3" - [(set (match_operand:QI 0 "general_operand" "=qm") - (ashift:QI (match_operand:QI 1 "general_operand" "0") - (match_operand:QI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (sal%B0,%R0cl,%0); - else - return AS2 (sal%B0,%2,%1); -}") - -(define_insn "ashrsi3" - [(set (match_operand:SI 0 "general_operand" "=rm") - (ashiftrt:SI (match_operand:SI 1 "general_operand" "0") - (match_operand:SI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (sar%L0,%R0cl,%0); - else - return AS2 (sar%L0,%2,%0); -}") - -(define_insn "ashrhi3" - [(set (match_operand:HI 0 "general_operand" "=rm") - (ashiftrt:HI (match_operand:HI 1 "general_operand" "0") - (match_operand:HI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (sar%W0,%R0cl,%0); - else - return AS2 (sar%W0,%2,%0); -}") - -(define_insn "ashrqi3" - [(set (match_operand:QI 0 "general_operand" "=qm") - (ashiftrt:QI (match_operand:QI 1 "general_operand" "0") - (match_operand:QI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (sar%B0,%R0cl,%0); - return - AS2 (sar%B0,%2,%1); -}") - -;;- logical shift instructions - -(define_insn "lshlsi3" - [(set (match_operand:SI 0 "general_operand" "=rm") - (lshift:SI (match_operand:SI 1 "general_operand" "0") - (match_operand:SI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (shl%L0,%R0cl,%0); - else - return AS2 (shl%L0,%2,%1); -}") - -(define_insn "lshlhi3" - [(set (match_operand:HI 0 "general_operand" "=rm") - (lshift:HI (match_operand:HI 1 "general_operand" "0") - (match_operand:HI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (shl%W0,%R0cl,%0); - else - return AS2 (shl%W0,%2,%1); -}") - -(define_insn "lshlqi3" - [(set (match_operand:QI 0 "general_operand" "=qm") - (lshift:QI (match_operand:QI 1 "general_operand" "0") - (match_operand:QI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (shl%B0,%R0cl,%0); - else - return AS2 (shl%B0,%2,%1); -}") - -(define_insn "lshrsi3" - [(set (match_operand:SI 0 "general_operand" "=rm") - (lshiftrt:SI (match_operand:SI 1 "general_operand" "0") - (match_operand:SI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (shr%L0,%R0cl,%0); - else - return AS2 (shr%L0,%2,%1); -}") - -(define_insn "lshrhi3" - [(set (match_operand:HI 0 "general_operand" "=rm") - (lshiftrt:HI (match_operand:HI 1 "general_operand" "0") - (match_operand:HI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (shr%W0,%%cl,%0); - else - return AS2 (shr%W0,%2,%1); -}") - -(define_insn "lshrqi3" - [(set (match_operand:QI 0 "general_operand" "=qm") - (lshiftrt:QI (match_operand:QI 1 "general_operand" "0") - (match_operand:QI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (shr%B0,%%cl,%0); - else - return AS2 (shr%B0,%2,%1); -}") - -;;- rotate instructions - -(define_insn "rotlsi3" - [(set (match_operand:SI 0 "general_operand" "=rm") - (rotate:SI (match_operand:SI 1 "general_operand" "0") - (match_operand:SI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (rol%L0,%%cl,%0); - else - return AS2 (rol%L0,%2,%1); -}") - -(define_insn "rotlhi3" - [(set (match_operand:HI 0 "general_operand" "=rm") - (rotate:HI (match_operand:HI 1 "general_operand" "0") - (match_operand:HI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (rol%W0,%%cl,%0); - else - return AS2 (rol%W0,%2,%1); -}") - -(define_insn "rotlqi3" - [(set (match_operand:QI 0 "general_operand" "=qm") - (rotate:QI (match_operand:QI 1 "general_operand" "0") - (match_operand:QI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (rol%B0,%%cl,%0); - else - return AS2 (rol%B0,%2,%1); -}") - -(define_insn "rotrsi3" - [(set (match_operand:SI 0 "general_operand" "=rm") - (rotatert:SI (match_operand:SI 1 "general_operand" "0") - (match_operand:SI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (ror%L0,%%cl,%0); - else - return AS2 (ror%L0,%2,%1); -}") - -(define_insn "rotrhi3" - [(set (match_operand:HI 0 "general_operand" "=rm") - (rotatert:HI (match_operand:HI 1 "general_operand" "0") - (match_operand:HI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (ror%W0,%%cl,%0); - else - return AS2 (ror%W0,%2,%1); -}") - -(define_insn "rotrqi3" - [(set (match_operand:QI 0 "general_operand" "=qm") - (rotatert:QI (match_operand:QI 1 "general_operand" "0") - (match_operand:QI 2 "general_operand" "cI")))] - "" - "* -{ - if (REG_P (operands[2])) - return AS2 (ror%B0,%%cl,%0); - else - return AS2 (ror%B0,%2,%1); -}") - -;; Store-flag instructions. - -(define_insn "seq" - [(set (match_operand:QI 0 "general_operand" "=q") - (eq (cc0) (const_int 0)))] - "" - "* - cc_status = cc_prev_status; - return \"sete %0\"; -") - -(define_insn "sne" - [(set (match_operand:QI 0 "general_operand" "=q") - (ne (cc0) (const_int 0)))] - "" - "* - cc_status = cc_prev_status; - return \"setne %0\"; -") - -(define_insn "sgt" - [(set (match_operand:QI 0 "general_operand" "=q") - (gt (cc0) (const_int 0)))] - "" - "* - cc_status = cc_prev_status; - OUTPUT_JUMP (\"setg %0\", \"seta %0\", 0); -") - -(define_insn "sgtu" - [(set (match_operand:QI 0 "general_operand" "=q") - (gtu (cc0) (const_int 0)))] - "" - "* cc_status = cc_prev_status; - return \"seta %0\"; ") - -(define_insn "slt" - [(set (match_operand:QI 0 "general_operand" "=q") - (lt (cc0) (const_int 0)))] - "" - "* cc_status = cc_prev_status; - OUTPUT_JUMP (\"setl %0\", \"setb %0\", \"sets %0\"); ") - -(define_insn "sltu" - [(set (match_operand:QI 0 "general_operand" "=q") - (ltu (cc0) (const_int 0)))] - "" - "* cc_status = cc_prev_status; - return \"setb %0\"; ") - -(define_insn "sge" - [(set (match_operand:QI 0 "general_operand" "=q") - (ge (cc0) (const_int 0)))] - "" - "* cc_status = cc_prev_status; - OUTPUT_JUMP (\"setge %0\", \"setae %0\", \"setns %0\"); ") - -(define_insn "sgeu" - [(set (match_operand:QI 0 "general_operand" "=q") - (geu (cc0) (const_int 0)))] - "" - "* cc_status = cc_prev_status; - return \"setae %0\"; ") - -(define_insn "sle" - [(set (match_operand:QI 0 "general_operand" "=q") - (le (cc0) (const_int 0)))] - "" - "* - cc_status = cc_prev_status; - OUTPUT_JUMP (\"setle %0\", \"setbe %0\", 0); -") - -(define_insn "sleu" - [(set (match_operand:QI 0 "general_operand" "=q") - (leu (cc0) (const_int 0)))] - "" - "* cc_status = cc_prev_status; - return \"setbe %0\"; ") - -;; Basic conditional jump instructions. -;; We ignore the overflow flag for signed branch instructions. - -(define_insn "beq" - [(set (pc) - (if_then_else (eq (cc0) - (const_int 0)) - (label_ref (match_operand 0 "" "")) - (pc)))] - "" - "je %l0") - -(define_insn "bne" - [(set (pc) - (if_then_else (ne (cc0) - (const_int 0)) - (label_ref (match_operand 0 "" "")) - (pc)))] - "" - "jne %l0") - -(define_insn "bgt" - [(set (pc) - (if_then_else (gt (cc0) - (const_int 0)) - (label_ref (match_operand 0 "" "")) - (pc)))] - "" - "*OUTPUT_JUMP (\"jg %l0\", \"ja %l0\", 0)") - -(define_insn "bgtu" - [(set (pc) - (if_then_else (gtu (cc0) - (const_int 0)) - (label_ref (match_operand 0 "" "")) - (pc)))] - "" - "ja %l0") - -;; There is no jump insn to check for `<' on IEEE floats. -;; Page 17-80 in the 80387 manual says jb, but that's wrong; -;; jb checks for `not >='. So swap the operands and do `>'. -(define_expand "blt" - [(set (pc) - (if_then_else (lt (cc0) - (const_int 0)) - (label_ref (match_operand 0 "" "")) - (pc)))] - "" - " -{ - extern rtx sequence_stack; - rtx prev = XEXP (XEXP (sequence_stack, 1), 0); - rtx body = PATTERN (prev); - rtx comp; - if (GET_CODE (body) == SET) - comp = SET_SRC (body); - else - comp = SET_SRC (XVECEXP (body, 0, 0)); - - if (GET_CODE (comp) == COMPARE - ? GET_MODE_CLASS (GET_MODE (XEXP (comp, 0))) == MODE_FLOAT - : GET_MODE_CLASS (GET_MODE (comp)) == MODE_FLOAT) - { - if (GET_CODE (comp) == COMPARE) - { - rtx op0 = XEXP (comp, 0); - rtx op1 = XEXP (comp, 1); - XEXP (comp, 0) = op1; - XEXP (comp, 1) = op0; - } - else - { - rtx new = gen_rtx (COMPARE, VOIDmode, - CONST0_RTX (GET_MODE (comp)), comp); - if (GET_CODE (body) == SET) - SET_SRC (body) = new; - else - SET_SRC (XVECEXP (body, 0, 0)) = new; - } - emit_insn (gen_bgt (operands[0])); - DONE; - } -}") - -(define_insn "" - [(set (pc) - (if_then_else (lt (cc0) - (const_int 0)) - (label_ref (match_operand 0 "" "")) - (pc)))] - "" - "*OUTPUT_JUMP (\"jl %l0\", \"jb %l0\", \"js %l0\")") - -(define_insn "bltu" - [(set (pc) - (if_then_else (ltu (cc0) - (const_int 0)) - (label_ref (match_operand 0 "" "")) - (pc)))] - "" - "jb %l0") - -(define_insn "bge" - [(set (pc) - (if_then_else (ge (cc0) - (const_int 0)) - (label_ref (match_operand 0 "" "")) - (pc)))] - "" - "*OUTPUT_JUMP (\"jge %l0\", \"jae %l0\", \"jns %l0\")") - -(define_insn "bgeu" - [(set (pc) - (if_then_else (geu (cc0) - (const_int 0)) - (label_ref (match_operand 0 "" "")) - (pc)))] - "" - "jae %l0") - -;; See comment on `blt', above. -(define_expand "ble" - [(set (pc) - (if_then_else (le (cc0) - (const_int 0)) - (label_ref (match_operand 0 "" "")) - (pc)))] - "" - " -{ - extern rtx sequence_stack; - rtx prev = XEXP (XEXP (sequence_stack, 1), 0); - rtx body = PATTERN (prev); - rtx comp; - if (GET_CODE (body) == SET) - comp = SET_SRC (body); - else - comp = SET_SRC (XVECEXP (body, 0, 0)); - - if (GET_CODE (comp) == COMPARE - ? GET_MODE_CLASS (GET_MODE (XEXP (comp, 0))) == MODE_FLOAT - : GET_MODE_CLASS (GET_MODE (comp)) == MODE_FLOAT) - { - if (GET_CODE (comp) == COMPARE) - { - rtx op0 = XEXP (comp, 0); - rtx op1 = XEXP (comp, 1); - XEXP (comp, 0) = op1; - XEXP (comp, 1) = op0; - } - else - { - rtx new = gen_rtx (COMPARE, VOIDmode, - CONST0_RTX (GET_MODE (comp)), comp); - if (GET_CODE (body) == SET) - SET_SRC (body) = new; - else - SET_SRC (XVECEXP (body, 0, 0)) = new; - } - emit_insn (gen_bge (operands[0])); - DONE; - } -}") - -(define_insn "" - [(set (pc) - (if_then_else (le (cc0) - (const_int 0)) - (label_ref (match_operand 0 "" "")) - (pc)))] - "" - "*OUTPUT_JUMP (\"jle %l0\", \"jbe %l0\", 0) ") - -(define_insn "bleu" - [(set (pc) - (if_then_else (leu (cc0) - (const_int 0)) - (label_ref (match_operand 0 "" "")) - (pc)))] - "" - "jbe %l0") - -;; Negated conditional jump instructions. - -(define_insn "" - [(set (pc) - (if_then_else (eq (cc0) - (const_int 0)) - (pc) - (label_ref (match_operand 0 "" ""))))] - "" - "jne %l0") - -(define_insn "" - [(set (pc) - (if_then_else (ne (cc0) - (const_int 0)) - (pc) - (label_ref (match_operand 0 "" ""))))] - "" - "je %l0") - -(define_insn "" - [(set (pc) - (if_then_else (gt (cc0) - (const_int 0)) - (pc) - (label_ref (match_operand 0 "" ""))))] - "" - "*OUTPUT_JUMP (\"jle %l0\", \"jbe %l0\", 0) ") - -(define_insn "" - [(set (pc) - (if_then_else (gtu (cc0) - (const_int 0)) - (pc) - (label_ref (match_operand 0 "" ""))))] - "" - "jbe %l0") - -(define_insn "" - [(set (pc) - (if_then_else (lt (cc0) - (const_int 0)) - (pc) - (label_ref (match_operand 0 "" ""))))] - "" - "*OUTPUT_JUMP (\"jge %l0\", \"jae %l0\", \"jns %l0\") -") - -(define_insn "" - [(set (pc) - (if_then_else (ltu (cc0) - (const_int 0)) - (pc) - (label_ref (match_operand 0 "" ""))))] - "" - "jae %l0") - -(define_insn "" - [(set (pc) - (if_then_else (ge (cc0) - (const_int 0)) - (pc) - (label_ref (match_operand 0 "" ""))))] - "" - "*OUTPUT_JUMP (\"jl %l0\", \"jb %l0\", \"js %l0\")") - -(define_insn "" - [(set (pc) - (if_then_else (geu (cc0) - (const_int 0)) - (pc) - (label_ref (match_operand 0 "" ""))))] - "" - "jb %l0") - -(define_insn "" - [(set (pc) - (if_then_else (le (cc0) - (const_int 0)) - (pc) - (label_ref (match_operand 0 "" ""))))] - "" - "*OUTPUT_JUMP (\"jg %l0\", \"ja %l0\", 0)") - -(define_insn "" - [(set (pc) - (if_then_else (leu (cc0) - (const_int 0)) - (pc) - (label_ref (match_operand 0 "" ""))))] - "" - "ja %l0") - -;; Unconditional and other jump instructions -(define_insn "jump" - [(set (pc) - (label_ref (match_operand 0 "" "")))] - "" - "jmp %l0") - -(define_insn "tablejump" - [(set (pc) (match_operand:SI 0 "general_operand" "rm")) - (use (label_ref (match_operand 1 "" "")))] - "" - "* -{ - CC_STATUS_INIT; - - return \"jmp %*%0\"; -}") - -/* -(define_insn "" - [(set (pc) - (if_then_else - (ne (compare (minus:HI (match_operand:HI 0 "general_operand" "c") - (const_int 1)) - (const_int -1)) - (const_int 0)) - (label_ref (match_operand 1 "" "g")) - (pc))) - (set (match_dup 0) - (minus:HI (match_dup 0) - (const_int 1)))] - "" - "loop %l1") - -(define_insn "" - [(set (pc) - (if_then_else - (ne (compare (const_int -1) - (minus:SI (match_operand:SI 0 "general_operand" "c") - (const_int 1))) - (const_int 0)) - (label_ref (match_operand 1 "" "g")) - (pc))) - (set (match_dup 0) - (minus:SI (match_dup 0) - (const_int 1)))] - "" - "loop %l1") -*/ - -;; Call subroutine returning no value. -(define_insn "call" - [(call (match_operand:QI 0 "indirect_operand" "m") - (match_operand:SI 1 "general_operand" "g"))] - ;; Operand 1 not really used on the m68000. - "" - "* -{ - if (GET_CODE (operands[0]) == MEM - && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) - { - operands[0] = XEXP (operands[0], 0); - return \"call %*%0\"; - } - else - return \"call %0\"; -}") - -;; Call subroutine, returning value in operand 0 -;; (which must be a hard register). -(define_insn "call_value" - [(set (match_operand 0 "" "=rf") - (call (match_operand:QI 1 "indirect_operand" "m") - (match_operand:SI 2 "general_operand" "g")))] - ;; Operand 2 not really used on the m68000. - "" - "* -{ - if (GET_CODE (operands[1]) == MEM - && ! CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) - { - operands[1] = XEXP (operands[1], 0); - output_asm_insn (\"call %*%1\", operands); - } - else - output_asm_insn (\"call %1\", operands); - - if (GET_MODE (operands[0]) == DFmode - || GET_MODE (operands[0]) == SFmode) - { -/* fp_pop_level++; */ - /* pop if reg dead */ - if (!FP_REG_P (operands[0])) - abort (); - if (top_dead_p (insn)) - { - POP_ONE_FP; - } - } - RET; -}") - -(define_insn "nop" - [(const_int 0)] - "" - "nop") - -;;- Local variables: -;;- mode:emacs-lisp -;;- comment-start: ";;- " -;;- eval: (set-syntax-table (copy-sequence (syntax-table))) -;;- eval: (modify-syntax-entry ?[ "(]") -;;- eval: (modify-syntax-entry ?] ")[") -;;- eval: (modify-syntax-entry ?{ "(}") -;;- eval: (modify-syntax-entry ?} "){") -;;- End: diff --git a/gnu/usr.bin/gcc1/cc1/config/out-i386.c b/gnu/usr.bin/gcc1/cc1/config/out-i386.c deleted file mode 100644 index 65540ec8fc..0000000000 --- a/gnu/usr.bin/gcc1/cc1/config/out-i386.c +++ /dev/null @@ -1,1427 +0,0 @@ -/*- - * - * This code is derived from software copyrighted by the Free Software - * Foundation. - * - * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. - */ - -#ifndef lint -static char sccsid[] = "@(#)out-i386.c 6.4 (Berkeley) 5/8/91"; -#endif /* not lint */ - -/* Subroutines for insn-output.c for Intel 80386. - Copyright (C) 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifndef FILE -#include -#endif - -#define FP_TOP (gen_rtx(REG, DFmode, FIRST_FLOAT_REG)) - -#define AT_SP(mode) (gen_rtx (MEM, (mode), stack_pointer_rtx)) -#define AT_BP(mode) (gen_rtx (MEM, (mode), frame_pointer_rtx)) - -#define RET return "" - -/* #define RETCOM(X) fprintf (asm_out_file, "%sX fp_pop_level=%d\n", \ - COMMENT_BEGIN, fp_pop_level); RET */ -#define RETCOM(X) return "" - -#define POP_ONE_FP \ - { /* fp_pop_level--; */ \ - fprintf (asm_out_file, "\tfstp %sst(0)\n", RP); } - -extern FILE *asm_out_file; -static char *singlemove_string (); -static void output_movf (); -static void replace_float_constant (); -static int mentions_fp_top (); -static int call_top_dead_p (); -static int fp_top_dead_p1 (); -static rtx via_memory (); -static void output_asm_insn_double_reg_op (); - -/* All output functions must increment or decrement this to indicate - the net number of pops or pushes which they perform. Note that it won't - necessarily balance with the optimize running, since we might have - two different calls with the same pop shared by cross jumping. - However on optimize the reg dead heuristic seems to work. */ - -int fp_pop_level = 0; - -static char *hi_reg_name[] = HI_REGISTER_NAMES; -static char *qi_reg_name[] = QI_REGISTER_NAMES; - -/* for fabs, fch, .. where the argument operand[1] must first be moved to - constraints "=fm" "0" */ - -#define FP_CALL1(op) \ - { if (FP_REG_P (operands[0])) \ - return op; \ - output_movf (FP_TOP, operands[1]); \ - output_asm_insn (op, operands); \ - /* fp_pop_level--; */ \ - return "fstp%z0 %0"; } - -/* handle case of call where op0/op1 is "=mf" and opn is "mrf" - eg. fadd */ -#define FP_CALL(op, rev, n) \ - return fp_call_internal (op, rev, n, operands, insn); - -static char * -fp_call_internal (op, rev, n, operands, insn) - char *op; - char *rev; - int n; - rtx *operands; - rtx insn; -{ - if (!FP_REG_P (operands[0])) - { - /* Here destination is in memory - and source is in the fp stack. */ - output_movf (FP_TOP, operands[0]); - output_asm_insn_double_reg_op (op, rev, insn); - return "fstp%z0 %0"; - } - - if (FP_REG_P (operands[n])) - { - rtx temp = operands[1]; - char *tem1 = op; - operands[1] = operands[n]; - op = rev; - operands[n] = temp; - rev = tem1; - } - - if (REG_P (operands[n])) - { - rtx xops[2]; - via_memory (operands[n]); - operands[n] = AT_SP (GET_MODE (operands[n])); - xops[0] = stack_pointer_rtx; - xops[1] = gen_rtx (CONST_INT, VOIDmode, - GET_MODE_SIZE (GET_MODE (operands[n]))); - output_asm_insn (op, operands + n); - output_asm_insn (AS2 (add%L0,%1,%0), xops); - } - else - output_asm_insn (op, operands + n); - - if (FP_REG_P (operands[0])) - { - /* It turns out not to work to use top_dead_p because - the death notes are not accurate enough. - But this ought to work, because the only thing that can - live across basic blocks is reg 8, and these insns - never involve reg 8 directly. */ - if (fp_top_dead_p1 (insn)) - POP_ONE_FP; - } - - RET; -} - -/* Output assembler code to perform insn OP - with two stack operands, and output on the stack. - - REV is the assembler insn that does the same thing but - effectively interchanges the meanings of the two arguments. - - Somewhat counterintuitively, the "first" operand was pushed last. - - The output replaces either the top-of-stack or both of the arguments, - depending on whether the other argument is wanted after this insn. */ - -static void -output_asm_insn_double_reg_op (op, rev, insn) - char *op; - char *rev; - rtx insn; -{ - fputc ('\t', asm_out_file); - if (top_dead_p (insn)) - { - /* Here we want the "reversed" insn, fsubr or fdivr. - But there is an assembler bug in all 80386 assemblers - which exchanges the meanings of fsubr and fsub, and of fdivr and fdiv! - So use the "unreversed" opcode (which will assemble into - the "reversed" insn). */ - rev = op; - - while (*rev && *rev != '%') - fputc (*rev++, asm_out_file); - /* fp_pop_level--; */ - - fprintf (asm_out_file, AS2 (p,%sst,%sst(1)), RP, RP); - } - else - { - while (*op && *op != '%') - fputc (*op++, asm_out_file); - fprintf (asm_out_file,AS2 ( ,%sst(1),%sst), RP, RP); - } - putc ('\n', asm_out_file); -} - -/* Moves X to memory location 8 below stack pointer - and returns an RTX for that memory location. - X should be a register, in DFmode or SFmode. */ - -static rtx -via_memory (x) - rtx x; -{ - if (!REG_P (x)) - abort (); - if (GET_MODE (x) == DFmode) - { - rtx xops[1]; - xops[0] = gen_rtx (REG, SImode, REGNO (x) + 1); - output_asm_insn ("push%L0 %0", xops); - } - output_asm_insn ("push%L0 %0", &x); -} - -/* Output an insn to copy the SFmode value in fp0 to OPERAND - without clobbering fp0. */ - -void -fp_store_sf (target) - rtx target; -{ - if (REG_P (target)) - { - rtx xoperands[3]; - xoperands[0] = stack_pointer_rtx; - xoperands[1] = AT_SP (Pmode); - xoperands[2] = gen_rtx (CONST_INT, VOIDmode, -4); - output_asm_insn (AS2 (add%L0,%2,%0), xoperands); - output_asm_insn ("fst%S0 %1", xoperands); - output_asm_insn ("pop%L0 %0", &target); - } - else if (GET_CODE (target) == MEM) - output_asm_insn ("fst%S0 %0", &target); -} - -/* Output an insn to pop an SF value from fp0 into TARGET. - This destroys the value of fp0. */ - -void -fp_pop_sf (target) - rtx target; -{ - if (REG_P (target)) - { - rtx xoperands[3]; - xoperands[0] = stack_pointer_rtx; - xoperands[1] = AT_SP (Pmode); - xoperands[2] = gen_rtx (CONST_INT, VOIDmode, -4); - output_asm_insn (AS2 (add%L0,%2,%0), xoperands); - output_asm_insn ("fstp%S0 %1", xoperands); - output_asm_insn ("pop%L0 %0", &target); - /* fp_pop_level--; */ - } - else if (GET_CODE (target) == MEM) - { - /* fp_pop_level--; */ - output_asm_insn ("fstp%S0 %0", &target); - } - else abort (); -} - -/* Copy the top of the fpu stack into TARGET, without popping. */ - -void -fp_store_df (target) - rtx target; -{ - if (REG_P (target)) - { - rtx xoperands[4]; - xoperands[0] = stack_pointer_rtx; - xoperands[1] = gen_rtx (REG, SImode, REGNO (target) + 1); - xoperands[2] = AT_SP (Pmode); - xoperands[3] = gen_rtx (CONST_INT, VOIDmode, -8); - output_asm_insn (AS2 (add%L0,%3,%0), xoperands); - output_asm_insn ("fst%Q0 %2", xoperands); - output_asm_insn ("pop%L0 %0", &target); - output_asm_insn ("pop%L0 %1", xoperands); - } - else if (GET_CODE (target) == MEM) - output_asm_insn ("fst%Q0 %0", &target); -} - -/* Copy the top of the fpu stack into TARGET, with popping. */ - -void -fp_pop_df (target) - rtx target; -{ - if (REG_P (target)) - { - rtx xoperands[4]; - xoperands[0] = stack_pointer_rtx; - xoperands[1] = gen_rtx (REG, SImode, REGNO (target) + 1); - xoperands[2] = AT_SP (Pmode); - xoperands[3] = gen_rtx (CONST_INT, VOIDmode, -8); - output_asm_insn (AS2 (add%L0,%3,%0), xoperands); - /* fp_pop_level--; */ - output_asm_insn ("fstp%Q0 %2", xoperands); - output_asm_insn ("pop%L0 %0", &target); - output_asm_insn ("pop%L0 %1", xoperands); - } - else if (GET_CODE (target) == MEM) - { - /* fp_pop_level--; */ - output_asm_insn ("fstp%z0 %0", &target); - } -} - -#if 0 -/* Pop the fp stack, convert value to integer and store in TARGET. - TARGET may be memory or register, and may have QI, HI or SImode. */ - -void -fp_pop_int (target) - rtx target; -{ - if (REG_P (target) || GET_MODE (target) != SImode) - { - rtx xxops[2]; - xxops[0] = stack_pointer_rtx; - xxops[1] = gen_rtx (CONST_INT, VOIDmode, 4); - output_asm_insn (AS2 (sub%L0,%1,%0), xxops); - xxops[0] = AT_SP (Pmode); - /* fp_pop_level--; */ - output_asm_insn ("fistp%L0 %0", xxops); - output_asm_insn ("pop%L0 %0", &target); - } - else if (GET_CODE (target) == MEM) - { - /* fp_pop_level--; */ - output_asm_insn ("fistp%L0 %0", &target); - } - else abort (); -} -#endif - -/* Push the SFmode value X onto the fpu stack. */ - -void -fp_push_sf (x) - rtx x; -{ - /* fp_pop_level++; */ - if (REG_P (x)) - { - rtx xoperands[2]; - rtx xfops[3]; - output_asm_insn ("push%L0 %0", &x); - xfops[0] = AT_SP (Pmode); - xfops[2] = gen_rtx (CONST_INT, VOIDmode, 4); - xfops[1] = stack_pointer_rtx; - output_asm_insn ("fld%S0 %0\n\tadd%L0 %2,%1", xfops); - } - else - output_asm_insn ("fld%S0 %0", &x); -} - -/* Push the DFmode value X onto the fpu stack. */ - -void -fp_push_df (x) - rtx x; -{ - /* fp_pop_level++; */ - - if (REG_P (x)) - { - rtx xoperands[2]; - rtx xfops[3]; - xoperands[0] = x; - xoperands[1] = gen_rtx (REG, SImode, REGNO (x) + 1); - output_asm_insn ("push%L0 %1", xoperands); - output_asm_insn ("push%L0 %0", xoperands); - xfops[0] = AT_SP (Pmode); - xfops[2] = gen_rtx (CONST_INT, VOIDmode, 8); - xfops[1] = stack_pointer_rtx; - output_asm_insn ("fld%Q0 %0\n\tadd%L0 %2,%1", xfops); - } - else if (GET_CODE (x) == MEM) - output_asm_insn ("fld%Q0 %0", &x); -} - -static char *output_move_const_single (); - -static char * -singlemove_string (operands) - rtx *operands; -{ - rtx x; - if (GET_CODE (operands[0]) == MEM - && GET_CODE (x = XEXP (operands[0], 0)) == PRE_DEC) - { - if (XEXP (x, 0) != stack_pointer_rtx) - abort (); - return "push%L0 %1"; - } - else if (GET_CODE (operands[1]) == CONST_DOUBLE) - { - return output_move_const_single (operands); - } - else if (GET_CODE (operands[0]) == REG || GET_CODE (operands[1]) == REG) - return AS2 (mov%L0,%1,%0); - else if (CONSTANT_P (operands[1])) - return AS2 (mov%L0,%1,%0); - else - { - output_asm_insn ("push%L0 %1", operands); - return "pop%L0 %0"; - } -} - -/* Return a REG that occurs in ADDR with coefficient 1. - ADDR can be effectively incremented by incrementing REG. */ - -static rtx -find_addr_reg (addr) - rtx addr; -{ - while (GET_CODE (addr) == PLUS) - { - if (GET_CODE (XEXP (addr, 0)) == REG) - addr = XEXP (addr, 0); - else if (GET_CODE (XEXP (addr, 1)) == REG) - addr = XEXP (addr, 1); - else if (CONSTANT_P (XEXP (addr, 0))) - addr = XEXP (addr, 1); - else if (CONSTANT_P (XEXP (addr, 1))) - addr = XEXP (addr, 0); - else - abort (); - } - if (GET_CODE (addr) == REG) - return addr; - abort (); -} - -/* Output an insn to add the constant N to the register X. */ - -static void -asm_add (n, x) - int n; - rtx x; -{ - rtx xops[2]; - xops[1] = x; - if (n < 0) - { - xops[0] = gen_rtx (CONST_INT, VOIDmode, -n); - output_asm_insn (AS2 (sub%L0,%0,%1), xops); - } - else if (n > 0) - { - xops[0] = gen_rtx (CONST_INT, VOIDmode, n); - output_asm_insn (AS2 (add%L0,%0,%1), xops); - } -} - -/* Output assembler code to perform a doubleword move insn - with operands OPERANDS. */ - -char * -output_move_double (operands) - rtx *operands; -{ - enum {REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1; - rtx latehalf[2]; - rtx addreg0 = 0, addreg1 = 0; - - /* First classify both operands. */ - - if (REG_P (operands[0])) - optype0 = REGOP; - else if (offsettable_memref_p (operands[0])) - optype0 = OFFSOP; - else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC) - optype0 = POPOP; - else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) - optype0 = PUSHOP; - else if (GET_CODE (operands[0]) == MEM) - optype0 = MEMOP; - else - optype0 = RNDOP; - - if (REG_P (operands[1])) - optype1 = REGOP; - else if (CONSTANT_P (operands[1]) - || GET_CODE (operands[1]) == CONST_DOUBLE) - optype1 = CNSTOP; - else if (offsettable_memref_p (operands[1])) - optype1 = OFFSOP; - else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC) - optype1 = POPOP; - else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) - optype1 = PUSHOP; - else if (GET_CODE (operands[1]) == MEM) - optype1 = MEMOP; - else - optype1 = RNDOP; - - /* Check for the cases that the operand constraints are not - supposed to allow to happen. Abort if we get one, - because generating code for these cases is painful. */ - - if (optype0 == RNDOP || optype1 == RNDOP) - abort (); - - /* If one operand is decrementing and one is incrementing - decrement the former register explicitly - and change that operand into ordinary indexing. */ - - if (optype0 == PUSHOP && optype1 == POPOP) - { - operands[0] = XEXP (XEXP (operands[0], 0), 0); - asm_add (-8, operands[0]); - operands[0] = gen_rtx (MEM, DImode, operands[0]); - optype0 = OFFSOP; - } - if (optype0 == POPOP && optype1 == PUSHOP) - { - operands[1] = XEXP (XEXP (operands[1], 0), 0); - asm_add (-8, operands[1]); - operands[1] = gen_rtx (MEM, DImode, operands[1]); - optype1 = OFFSOP; - } - - /* If an operand is an unoffsettable memory ref, find a register - we can increment temporarily to make it refer to the second word. */ - - if (optype0 == MEMOP) - addreg0 = find_addr_reg (XEXP (operands[0], 0)); - - if (optype1 == MEMOP) - addreg1 = find_addr_reg (XEXP (operands[1], 0)); - - /* Ok, we can do one word at a time. - Normally we do the low-numbered word first, - but if either operand is autodecrementing then we - do the high-numbered word first. - - In either case, set up in LATEHALF the operands to use - for the high-numbered word and in some cases alter the - operands in OPERANDS to be suitable for the low-numbered word. */ - - if (optype0 == REGOP) - latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); - else if (optype0 == OFFSOP) - latehalf[0] = adj_offsettable_operand (operands[0], 4); - else - latehalf[0] = operands[0]; - - if (optype1 == REGOP) - latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); - else if (optype1 == OFFSOP) - latehalf[1] = adj_offsettable_operand (operands[1], 4); - else if (optype1 == CNSTOP) - { - if (CONSTANT_P (operands[1])) - latehalf[1] = const0_rtx; - else if (GET_CODE (operands[1]) == CONST_DOUBLE) - { - latehalf[1] = gen_rtx (CONST_INT, VOIDmode, - CONST_DOUBLE_HIGH (operands[1])); - operands[1] = gen_rtx (CONST_INT, VOIDmode, - CONST_DOUBLE_LOW (operands[1])); - } - } - else - latehalf[1] = operands[1]; - - /* If insn is effectively movd N (sp),-(sp) then we will do the - high word first. We should use the adjusted operand 1 (which is N+4 (sp)) - for the low word as well, to compensate for the first decrement of sp. */ - if (optype0 == PUSHOP - && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM - && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) - operands[1] = latehalf[1]; - - /* If one or both operands autodecrementing, - do the two words, high-numbered first. */ - - /* Likewise, the first move would clobber the source of the second one, - do them in the other order. This happens only for registers; - such overlap can't happen in memory unless the user explicitly - sets it up, and that is an undefined circumstance. */ - - if (optype0 == PUSHOP || optype1 == PUSHOP - || (optype0 == REGOP && optype1 == REGOP - && REGNO (operands[0]) == REGNO (latehalf[1]))) - { - /* Make any unoffsettable addresses point at high-numbered word. */ - if (addreg0) - asm_add (4, addreg0); - if (addreg1) - asm_add (4, addreg1); - - /* Do that word. */ - output_asm_insn (singlemove_string (latehalf), latehalf); - - /* Undo the adds we just did. */ - if (addreg0) - asm_add (-4, addreg0); - if (addreg1) - asm_add (-4, addreg1); - - /* Do low-numbered word. */ - return singlemove_string (operands); - } - - /* Normal case: do the two words, low-numbered first. */ - - output_asm_insn (singlemove_string (operands), operands); - - /* Make any unoffsettable addresses point at high-numbered word. */ - if (addreg0) - asm_add (4, addreg0); - if (addreg1) - asm_add (4, addreg1); - - /* Do that word. */ - output_asm_insn (singlemove_string (latehalf), latehalf); - - /* Undo the adds we just did. */ - if (addreg0) - asm_add (-4, addreg0); - if (addreg1) - asm_add (-4, addreg1); - - return ""; -} - -int -standard_80387_constant_p (x) - rtx x; -{ - union { double d; int i[2];} u; - register double d; - u.i[0] = XINT (x, 0); - u.i[1] = XINT (x, 1); - d = u.d; - - if (d == 0) - return 1; - if (d == 1) - return 2; - /* Note that on the 80387, other constants, such as pi, - are much slower to load as standard constants - than to load from doubles in memory! */ - - return 0; -} - -static char * -output_move_const_double (operands) - rtx *operands; -{ - if (FP_REG_P (operands[0])) - { - int conval = standard_80387_constant_p (operands[1]); - - /* fp_pop_level++; */ - if (conval == 1) - return "fldz"; - if (conval == 2) - return "fld1"; - /* fp_pop_level--; */ - } - - output_move_double (operands); -} - - -static char * -output_move_const_single (operands) - rtx *operands; -{ - if (FP_REG_P (operands[0])) - { - int conval = standard_80387_constant_p (operands[1]); - - /* fp_pop_level++; */ - if (conval == 1) - return "fldz"; - if (conval == 2) - return "fld1"; - /* fp_pop_level--; */ - } - if (GET_CODE (operands[1]) == CONST_DOUBLE) - { - union { int i[2]; double d;} u1; - union { int i; float f;} u2; - u1.i[0] = CONST_DOUBLE_LOW (operands[1]); - u1.i[1] = CONST_DOUBLE_HIGH (operands[1]); - u2.f = u1.d; - operands[1] = gen_rtx (CONST_INT, VOIDmode, u2.i); - } - return singlemove_string (operands); -} - -/* Output an insn to move an SF value from FROM to TO. - The kinds of operands are not restricted - except that they may not both be in memory. */ - -void -output_movsf (to, from) - rtx from, to; -{ - rtx xops[2]; - xops[0] = to; - xops[1] = from; - if (FP_REG_P (from) || FP_REG_P (to)) - { - from = xops[1]; - } - - if (FP_REG_P (from)) - { -#if 0 - { - if (REGNO (from) != REGNO (to)) - { - output_asm_insn ("fld%S0 %1\n\tfstp%S0 %0", xops); - } - } - else -#endif - - if (! FP_REG_P (to)) - fp_pop_sf (to); - } - else if (FP_REG_P (to)) - fp_push_sf (from); - else - output_asm_insn (singlemove_string (xops), xops); -} - -/* Output an insn to move a DF value from FROM to TO. - The kinds of operands are not restricted - except that they may not both be in memory. */ - -void -output_movdf (to, from) - rtx from, to; -{ - rtx xops[2]; - xops[0] = to; - xops[1] = from; - if (FP_REG_P (from) || FP_REG_P (to)) - { - from = xops[1]; - to = xops[0]; - } - if (FP_REG_P (from)) - { -#if 0 - { - if (REGNO (from) != REGNO (to)) - abort (); -/* output_asm_insn ("fld%Q0 %1\n\t fstp%Q0 %0", xops);*/ - } - else - { -#endif - if (! FP_REG_P (to)) - fp_pop_df (to); - } - else if (FP_REG_P (to)) - fp_push_df (from); - else - output_asm_insn (output_move_double (xops), xops); -} - -/* does move of FROM to TO where the mode is the minimum of the -two */ - -static void -output_movf (to, from) - rtx to, from; -{ - if (GET_MODE (from) == SFmode || GET_MODE (to) == SFmode) - output_movsf (to, from); - else - output_movdf (to, from); -} - -/* Return the best assembler insn template - for moving operands[1] into operands[0] as a fullword. */ - -void -function_prologue (file, size) - FILE *file; - int size; -{ - register int regno; - int nregs, limit; - rtx xops[4]; - extern int frame_pointer_needed; - - /* fp_pop_level = 0; */ - xops[0] = stack_pointer_rtx; - xops[1] = frame_pointer_rtx; - xops[2] = gen_rtx (CONST_INT, VOIDmode, size); - if (frame_pointer_needed) - { - output_asm_insn ("push%L0 %1", xops); - output_asm_insn (AS2 (mov%L0,%0,%1), xops); - if (size) - output_asm_insn (AS2 (sub%L0,%2,%0), xops); - } - - /* Note If use enter it is NOT reversed args. - This one is not reversed from intel!! - I think enter is slower. Also sdb doesn't like it. - But if you want it the code is: - { - xops[3] = const0_rtx; - output_asm_insn ("enter %2,%3", xops); - } - */ - nregs = 0; - limit = (frame_pointer_needed ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM); - for (regno = limit - 1; regno >= 0; regno--) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { - fprintf (file, "\tpush%s %se%s\n", L_SIZE, RP, hi_reg_name[regno]); - } -} - -void -function_epilogue (file, size) - FILE *file; - int size; -{ - register int regno; - register int nregs, limit; - int assure_sp_pos; - int return_struct_adjust; - extern int frame_pointer_needed; - extern int current_function_pops_args; - extern int current_function_args_size; - extern int flag_pcc_struct_return; - - limit = (frame_pointer_needed ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM); - nregs = 0; - - return_struct_adjust = - (current_function_returns_struct -#ifdef STRUCT_RETURN_CALLER_POP - && !flag_pcc_struct_return -#endif - ? 4 : 0); - - for (regno = (limit -1); regno >= 0; regno--) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - nregs++; - - /* sp is often unreliable so we must go off the frame pointer, - */ - - if (nregs && frame_pointer_needed) - { - rtx xops[2]; - xops[0] = adj_offsettable_operand (AT_BP (Pmode), - -size -(nregs*(UNITS_PER_WORD))); - xops[1] = stack_pointer_rtx; - output_asm_insn (AS2 (lea%L0,%0,%1), xops); - } - for (regno = 0; regno < limit; regno++) - { - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { - fprintf (file, "\tpop%s ", L_SIZE); - fprintf (file, "%se%s\n", RP, hi_reg_name[regno]); - } - } - - if (frame_pointer_needed) - fprintf (file, "\tleave\n"); - if (current_function_pops_args && current_function_args_size) - fprintf (file, "\tret %s%d\n", IP, - (current_function_args_size + return_struct_adjust)); - else if (return_struct_adjust) - fprintf (file, "\tret %s%d\n", IP, return_struct_adjust); - else - fprintf (file, "\tret\n"); -} - -int -hard_regno_mode_ok (regno, mode) - int regno; - enum machine_mode mode; -{ - return - (regno < 2 ? 1 - /* Used to reject floating modes here */ - : regno < 4 ? 1 - : regno >= 8 ? mode == DFmode || mode == SFmode - : mode != QImode); -} - -/* Print the name of a register based on its machine mode and number. - If CODE is 'w', pretend the mode is HImode. - If CODE is 'b', pretend the mode is QImode. - If CODE is 'k', pretend the mode is SImode. */ - -#define PRINT_REG(X, CODE, FILE) \ - do { fprintf (FILE, "%s", RP); \ - switch ((CODE == 'w' ? 2 \ - : CODE == 'b' ? 1 \ - : CODE == 'k' ? 4 \ - : GET_MODE_SIZE (GET_MODE (X)))) \ - { \ - case 4: \ - case 8: \ - if (!FP_REG_P (X)) fputs ("e", FILE); \ - case 2: \ - fputs (hi_reg_name[REGNO (X)], FILE); \ - break; \ - case 1: \ - fputs (qi_reg_name[REGNO (X)], FILE); \ - break; \ - } \ - } while (0) - -/* Meaning of CODE: - f -- float insn (print a CONST_DOUBLE as a float rather than in hex). - L,W,B,Q,S -- print the opcode suffix for specified size of operand. - R -- print the prefix for register names. - z -- print the opcode suffix for the size of the current operand. - * -- print a star (in certain assembler syntax) - w -- print the operand as if it's a "word" (HImode) even if it isn't. - c -- don't print special prefixes before constant operands. -*/ - -void -print_operand (file, x, code) - FILE *file; - rtx x; - int code; -{ - if (code) - { - switch (code) - { - case '*': - if (USE_STAR) - putc ('*', file); - return; - - case 'L': - PUT_OP_SIZE (code, 'l', file); - return; - - case 'W': - PUT_OP_SIZE (code, 'w', file); - return; - - case 'B': - PUT_OP_SIZE (code, 'b', file); - return; - - case 'Q': - PUT_OP_SIZE (code, 'l', file); - return; - - case 'S': - PUT_OP_SIZE (code, 's', file); - return; - - case 'R': - fprintf (file, "%s", RP); - return; - - case 'z': - /* this is the size of op from size of operand */ - switch (GET_MODE_SIZE (GET_MODE (x))) - { - case 2: - PUT_OP_SIZE ('W', 'w', file); - return; - case 4: - if (GET_MODE (x) == SFmode) - { - PUT_OP_SIZE ('S', 's', file); - return; - } - else - PUT_OP_SIZE ('L', 'l', file); - return; - case 8: - if (!FP_REG_P (x)) PUT_OP_SIZE ('Q', 'l', file); - return; - case 1: - PUT_OP_SIZE ('B', 'b', file); - return; - } - } - } - if (GET_CODE (x) == REG) - { - PRINT_REG (x, code, file); - } - else if (GET_CODE (x) == MEM) - { - PRINT_PTR (x, file); - if (CONSTANT_ADDRESS_P (XEXP (x, 0))) - output_addr_const (file, XEXP (x, 0)); - else - output_address (XEXP (x, 0)); - } - else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == SFmode) - { - union { double d; int i[2]; } u; - union { float f; int i; } u1; - u.i[0] = CONST_DOUBLE_LOW (x); - u.i[1] = CONST_DOUBLE_HIGH (x); - u1.f = u.d; - if (code == 'f') - fprintf (file, "%.22e", u1.f); - else - { - PRINT_IMMED_PREFIX (file); - fprintf (file, "0x%x", u1.i); - } - } - else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == DFmode) - { - union { double d; int i[2]; } u; - u.i[0] = CONST_DOUBLE_LOW (x); - u.i[1] = CONST_DOUBLE_HIGH (x); - fprintf (file, "%.22e", u.d); - } - else - { - if (code != 'c') - { - if (GET_CODE (x) == CONST_INT) - PRINT_IMMED_PREFIX (file); - else if (GET_CODE (x) == CONST || GET_CODE (x) == SYMBOL_REF) - PRINT_OFFSET_PREFIX (file); - } - output_addr_const (file, x); - } -} - -/* Print a memory operand whose address is ADDR. */ - -void -print_operand_address (file, addr) - FILE *file; - register rtx addr; -{ - register rtx reg1, reg2, breg, ireg; - rtx offset; - - switch (GET_CODE (addr)) - { - case REG: - ADDR_BEG (file); - fprintf (file, "%se", RP); - fputs (hi_reg_name[REGNO (addr)], file); - ADDR_END (file); - break; - - case PLUS: - reg1 = 0; - reg2 = 0; - ireg = 0; - breg = 0; - offset = 0; - if (CONSTANT_ADDRESS_P (XEXP (addr, 0))) - { - offset = XEXP (addr, 0); - addr = XEXP (addr, 1); - } - else if (CONSTANT_ADDRESS_P (XEXP (addr, 1))) - { - offset = XEXP (addr, 1); - addr = XEXP (addr, 0); - } - if (GET_CODE (addr) != PLUS) ; - else if (GET_CODE (XEXP (addr, 0)) == MULT) - { - reg1 = XEXP (addr, 0); - addr = XEXP (addr, 1); - } - else if (GET_CODE (XEXP (addr, 1)) == MULT) - { - reg1 = XEXP (addr, 1); - addr = XEXP (addr, 0); - } - else if (GET_CODE (XEXP (addr, 0)) == REG) - { - reg1 = XEXP (addr, 0); - addr = XEXP (addr, 1); - } - else if (GET_CODE (XEXP (addr, 1)) == REG) - { - reg1 = XEXP (addr, 1); - addr = XEXP (addr, 0); - } - if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT) - { - if (reg1 == 0) reg1 = addr; - else reg2 = addr; - addr = 0; - } - if (offset != 0) - { - if (addr != 0) abort (); - addr = offset; - } - if ((reg1 && GET_CODE (reg1) == MULT) - || (reg2 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg2)))) - { - breg = reg2; - ireg = reg1; - } - else if (reg1 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg1))) - { - breg = reg1; - ireg = reg2; - } - - if (ireg != 0 || breg != 0) - { - int scale = 1; - - if (addr != 0) - { - if (GET_CODE (addr) == LABEL_REF) - output_asm_label (addr); - else - output_addr_const (file, addr); - } - - if (ireg != 0 && GET_CODE (ireg) == MULT) - { - scale = INTVAL (XEXP (ireg, 1)); - ireg = XEXP (ireg, 0); - } - /* output breg+ireg*scale */ - PRINT_B_I_S (breg, ireg, scale, file); - break; - } - - case MULT: - { - int scale; - if (GET_CODE (XEXP (addr, 0)) == CONST_INT) - { - scale = INTVAL (XEXP (addr, 0)); - ireg = XEXP (addr, 1); - } - else - { - scale = INTVAL (XEXP (addr, 1)); - ireg = XEXP (addr, 0); - } - output_addr_const (file, const0_rtx); - PRINT_B_I_S ((rtx) 0, ireg, scale, file); - } - break; - - default: - if (GET_CODE (addr) == CONST_INT - && INTVAL (addr) < 0x8000 - && INTVAL (addr) >= -0x8000) - fprintf (file, "%d", INTVAL (addr)); - else - output_addr_const (file, addr); - } -} - -/* Set the cc_status for the results of an insn whose pattern is EXP. - On the 80386, we assume that only test and compare insns, as well - as SI, HI, & DI mode ADD, SUB, NEG, AND, IOR, XOR, ASHIFT, LSHIFT, - ASHIFTRT, and LSHIFTRT instructions set the condition codes usefully. - Also, we assume that jumps and moves don't affect the condition codes. - All else, clobbers the condition codes, by assumption. - - We assume that ALL add, minus, etc. instructions effect the condition - codes. This MUST be consistent with i386.md. */ - -notice_update_cc (exp) - rtx exp; -{ - if (GET_CODE (exp) == SET) - { - /* Jumps do not alter the cc's. */ - if (SET_DEST (exp) == pc_rtx) - return; - /* Moving register or memory into a register: - it doesn't alter the cc's, but it might invalidate - the RTX's which we remember the cc's came from. - (Note that moving a constant 0 or 1 MAY set the cc's). */ - if (REG_P (SET_DEST (exp)) - && (REG_P (SET_SRC (exp)) || GET_CODE (SET_SRC (exp)) == MEM)) - { - if (cc_status.value1 - && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1)) - cc_status.value1 = 0; - if (cc_status.value2 - && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2)) - cc_status.value2 = 0; - return; - } - /* Moving register into memory doesn't alter the cc's. - It may invalidate the RTX's which we remember the cc's came from. */ - if (GET_CODE (SET_DEST (exp)) == MEM && REG_P (SET_SRC (exp))) - { - if (cc_status.value1 && GET_CODE (cc_status.value1) == MEM) - cc_status.value1 = 0; - if (cc_status.value2 && GET_CODE (cc_status.value2) == MEM) - cc_status.value2 = 0; - return; - } - /* Function calls clobber the cc's. */ - else if (GET_CODE (SET_SRC (exp)) == CALL) - { - CC_STATUS_INIT; - return; - } - /* Tests and compares set the cc's in predictable ways. */ - else if (SET_DEST (exp) == cc0_rtx) - { - CC_STATUS_INIT; - cc_status.value1 = SET_SRC (exp); - return; - } - /* Certain instructions effect the condition codes. */ - else if (GET_MODE (SET_SRC (exp)) == SImode - || GET_MODE (SET_SRC (exp)) == HImode - || GET_MODE (SET_SRC (exp)) == QImode) - switch (GET_CODE (SET_SRC (exp))) - { - case ASHIFTRT: case LSHIFTRT: - case ASHIFT: case LSHIFT: - /* Shifts on the 386 don't set the condition codes if the - shift count is zero. */ - if (GET_CODE (XEXP (SET_SRC (exp), 1)) != CONST_INT) - { - CC_STATUS_INIT; - break; - } - /* We assume that the CONST_INT is non-zero (this rtx would - have been deleted if it were zero. */ - - case PLUS: case MINUS: case NEG: - case AND: case IOR: case XOR: - cc_status.flags = CC_NO_OVERFLOW; - cc_status.value1 = SET_SRC (exp); - cc_status.value2 = SET_DEST (exp); - break; - - default: - CC_STATUS_INIT; - } - else - { - CC_STATUS_INIT; - } - } - else if (GET_CODE (exp) == PARALLEL - && GET_CODE (XVECEXP (exp, 0, 0)) == SET) - { - if (SET_DEST (XVECEXP (exp, 0, 0)) == pc_rtx) - return; - if (SET_DEST (XVECEXP (exp, 0, 0)) == cc0_rtx) - { - CC_STATUS_INIT; - cc_status.value1 = SET_SRC (XVECEXP (exp, 0, 0)); - return; - } - CC_STATUS_INIT; - } - else - { - CC_STATUS_INIT; - } -} - -/* Nonzero if the top of the fpu stack dies in this insn. */ - -int -top_dead_p (insn) - rtx insn; -{ - extern int optimize; - if (optimize) - return (find_regno_note (insn, REG_DEAD, FIRST_FLOAT_REG) - || find_regno_note (insn, REG_DEAD, FIRST_FLOAT_REG + 1)); - - if (GET_CODE (insn) == CALL_INSN) - return call_top_dead_p (insn); - - return fp_top_dead_p1 (insn); -} - -/* Following is used after a call_value insn - if obey_regdecls there will not be the REG_DEAD notes - to go by (there won't be any cross jumping to worry about - either), and we depend on seeing if the FP_TOP is used - in the next two insn's. Otherwise we depend on the - REG_DEAD notes. - */ - -static int -call_top_dead_p (insn) - rtx insn; -{ - int i; - for (i = 0; i < 3; i++) - { - insn = NEXT_INSN (insn); - if (insn == 0) - return 1; - if (GET_CODE (insn) == NOTE || GET_CODE (insn) == CODE_LABEL) - continue; - if (GET_CODE (insn) == BARRIER) - abort (); - if (GET_CODE (PATTERN (insn)) == SET - && SET_DEST (PATTERN (insn)) != stack_pointer_rtx) - return (!(mentions_fp_top (SET_SRC (PATTERN (insn))))); - if (GET_CODE (PATTERN (insn)) == CALL) - return 1; - if (GET_CODE (PATTERN (insn)) == USE) - return (! FP_REG_P (XEXP (PATTERN (insn), 0))); - } - return 1; -} - -/* Return 1 if current val of fpu top-of-stack appears unused - in rest of this basic block. */ - -static int -fp_top_dead_p1 (insn) - rtx insn; -{ - extern int optimize; - - int past_label = 0; - - for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn)) - { - switch (GET_CODE (insn)) - { - case CALL_INSN: - /* Function calls clobber this value, so it's dead. */ - return 1; - - case JUMP_INSN: - if (! optimize) - /* Can't use JUMP_LABEL, but there's no cross-jumping either. */ - return 1; - if (JUMP_LABEL (insn) == 0) - return 1; - insn = JUMP_LABEL (insn); - case CODE_LABEL: - /* Go past one label or follow one jump in case of cross-jumping, - which could insert such a label or jump into one basic block. */ - if (! optimize) - return 1; - if (past_label) - return 1; - past_label = 1; - break; - - case INSN: - if (GET_CODE (PATTERN (insn)) == SET) - { - if ((mentions_fp_top (SET_SRC (PATTERN (insn))))) - return 0; - else if (FP_REG_P (SET_DEST (PATTERN (insn)))) - return 1; - } - else if (mentions_fp_top (PATTERN (insn))) - return 0; - break; - } - } - return 1; -} - -/* Return 1 if X involves an FPU register. */ - -static int -mentions_fp_top (x) - rtx x; -{ - register RTX_CODE code; - - code = GET_CODE (x); - switch (code) - { - case LABEL_REF: - case SYMBOL_REF: - case CONST_INT: - case CONST: - case CC0: - case PC: - case CLOBBER: - case MEM: - return 0; - - case REG: - return FP_REGNO_P (REGNO (x)); - } - - /* Recursively scan the operands of this expression. */ - { - register char *fmt = GET_RTX_FORMAT (code); - register int i; - - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - if (mentions_fp_top (XEXP (x, i))) - return 1; - } - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - if (mentions_fp_top (XVECEXP (x, i, j))) - return 1; - } - } - } - return 0; -} - -/* Some asm-dependent functions. */ - -#ifdef MASM -#include "masm386.c" -#endif diff --git a/gnu/usr.bin/gcc1/cc1/config/tm-att386.h b/gnu/usr.bin/gcc1/cc1/config/tm-att386.h deleted file mode 100644 index d796a6fa2a..0000000000 --- a/gnu/usr.bin/gcc1/cc1/config/tm-att386.h +++ /dev/null @@ -1,222 +0,0 @@ -/* Definitions for AT&T assembler syntax for the Intel 80386. - Copyright (C) 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#define TARGET_VERSION fprintf (stderr, " (80386, ATT syntax)"); - -/* Define the syntax of instructions and addresses. */ - -/* Define some concatenation macros to concatenate an opcode - and one, two or three operands. In other assembler syntaxes - they may alter the order of ther operands. */ - -#ifdef __STDC__ -#define AS2(a,b,c) #a " " #b "," #c -#define AS3(a,b,c,d) #a " " #b "," #c "," #d -#define AS1(a,b) #a " " #b -#else -#define AS1(a,b) "a b" -#define AS2(a,b,c) "a b,c" -#define AS3(a,b,c,d) "a b,c,d" -#endif - -/* Output the size-letter for an opcode. - CODE is the letter used in an operand spec (L, B, W, S or Q). - CH is the corresponding lower case letter - (except if CODE is L then CH is `l'). */ -#define PUT_OP_SIZE(CODE,CH,FILE) putc (CH,(FILE)) - -/* Opcode suffix for fullword insn. */ -#define L_SIZE "l" - -/* Prefix for register names in this syntax. */ -#define RP "%" - -/* Prefix for immediate operands in this syntax. */ -#define IP "$" - -/* Prefix for internally generated assembler labels. */ -#define LPREFIX ".L" - -/* Output the prefix for an immediate operand, or for an offset operand. */ -#define PRINT_IMMED_PREFIX(FILE) fputs ("$", (FILE)) -#define PRINT_OFFSET_PREFIX(FILE) fputs ("$", (FILE)) - -/* Indirect call instructions should use `*'. */ -#define USE_STAR 1 - -/* Prefix for a memory-operand X. */ -#define PRINT_PTR(X, FILE) - -/* Delimiters that surround base reg and index reg. */ -#define ADDR_BEG(FILE) putc('(', (FILE)) -#define ADDR_END(FILE) putc(')', (FILE)) - -/* Print an index register (whose rtx is IREG). */ -#define PRINT_IREG(FILE,IREG) \ - do \ - { fputs (",", (FILE)); PRINT_REG ((IREG), 0, (FILE)); } \ - while (0) - -/* Print an index scale factor SCALE. */ -#define PRINT_SCALE(FILE,SCALE) \ - if ((SCALE) != 1) fprintf ((FILE), ",%d", (SCALE)) - -/* Print a base/index combination. - BREG is the base reg rtx, IREG is the index reg rtx, - and SCALE is the index scale factor (an integer). */ - -#define PRINT_B_I_S(BREG,IREG,SCALE,FILE) \ - { ADDR_BEG (FILE); \ - if (BREG) PRINT_REG ((BREG), 0, (FILE)); \ - if ((IREG) != 0) \ - { PRINT_IREG ((FILE), (IREG)); \ - PRINT_SCALE ((FILE), (SCALE)); } \ - ADDR_END (FILE); } - -/* Define the syntax of pseudo-ops, labels and comments. */ - -/* Assembler pseudos to introduce constants of various size. */ - -#define ASM_BYTE "\t.byte " -#define ASM_SHORT "\t.value " -#define ASM_LONG "\t.long " -#define ASM_DOUBLE "\t.double " - -/* String containing the assembler's comment-starter. */ - -#define COMMENT_BEGIN "/" - -/* Output at beginning of assembler file. */ -/* The .file command should always begin the output. */ - -#undef ASM_FILE_START -#define ASM_FILE_START(FILE) \ - do { sdbout_filename ((FILE), main_input_filename); \ - if (optimize) ASM_FILE_START_1 (FILE); \ - } while (0) - -#define ASM_FILE_START_1(FILE) fprintf (FILE, "\t.optim\n") - -/* Output to assembler file text saying following lines - may contain character constants, extra white space, comments, etc. */ - -#define ASM_APP_ON "/APP\n" - -/* Output to assembler file text saying following lines - no longer contain unusual constructs. */ - -#define ASM_APP_OFF "/NO_APP\n" - -/* This is how to output an assembler line - that says to advance the location counter - to a multiple of 2**LOG bytes. */ - -#define ASM_OUTPUT_ALIGN(FILE,LOG) \ - if ((LOG)!=0) fprintf ((FILE), "\t.align %d\n", 1<<(LOG)) - -/* This is how to output an assembler line - that says to advance the location counter by SIZE bytes. */ - -#define ASM_OUTPUT_SKIP(FILE,SIZE) \ - fprintf ((FILE), "\t.set .,.+%u\n", (SIZE)) - -/* Output before read-only data. */ - -#define TEXT_SECTION_ASM_OP ".text" - -/* Output before writable data. */ - -#define DATA_SECTION_ASM_OP ".data" - -/* Output before uninitialized data. */ - -#define BSS_SECTION_ASM_OP ".bss" - -#define EXTRA_SECTIONS in_bss - -#define EXTRA_SECTION_FUNCTIONS \ -void \ -bss_section () \ -{ \ - if (in_section != in_bss) \ - { \ - fprintf (asm_out_file, "%s\n", BSS_SECTION_ASM_OP); \ - in_section = in_bss; \ - } \ -} - -/* Define the syntax of labels and symbol definitions/declarations. */ - -/* This says how to output an assembler line - to define a global common symbol. */ -/* We don't use ROUNDED because the standard compiler doesn't, - and the linker gives error messages if a common symbol - has more than one length value. */ - -#define ASM_OUTPUT_COMMON(FILE, NAME, SIZE, ROUNDED) \ -( fputs (".comm ", (FILE)), \ - assemble_name ((FILE), (NAME)), \ - fprintf ((FILE), ",%u\n", (SIZE))) - -/* This says how to output an assembler line - to define a local common symbol. */ - -/* Note that using bss_section here caused errors - in building shared libraries on system V.3. */ -#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED) \ - (data_section (), \ - ASM_OUTPUT_LABEL ((FILE), (NAME)), \ - fprintf ((FILE), "\t.set .,.+%u\n", (ROUNDED))) - -/* This is how to store into the string BUF - the symbol_ref name of an internal numbered label where - PREFIX is the class of label and NUM is the number within the class. - This is suitable for output with `assemble_name'. */ - -#define ASM_GENERATE_INTERNAL_LABEL(BUF,PREFIX,NUMBER) \ - sprintf ((BUF), ".%s%d", (PREFIX), (NUMBER)) - -/* This is how to output a reference to a user-level label named NAME. */ - -#define ASM_OUTPUT_LABELREF(FILE,NAME) \ - fprintf (FILE, "%s", NAME) - -/* This is how to output an internal numbered label where - PREFIX is the class of label and NUM is the number within the class. */ - -#define ASM_OUTPUT_INTERNAL_LABEL(FILE,PREFIX,NUM) \ - fprintf (FILE, ".%s%d:\n", PREFIX, NUM) - -/* This is how to output a command to make the user-level label named NAME - defined for reference from other files. */ - -#define ASM_GLOBALIZE_LABEL(FILE,NAME) \ - (fputs (".globl ", FILE), assemble_name (FILE, NAME), fputs ("\n", FILE)) - -/* How to output an ASCII string constant. */ - -#define ASM_OUTPUT_ASCII(FILE, p, size) \ -{ int i=0; \ - while (i < size) \ - { if (i%10 == 0) { if (i!=0) fprintf (FILE, "\n"); \ - fprintf (FILE, ASM_BYTE); } \ - else fprintf (FILE, ","); \ - fprintf (FILE, "0x%x",(p[i++] & 0377)) ;} \ - fprintf (FILE, "\n"); } diff --git a/gnu/usr.bin/gcc1/cc1/config/tm-bsd386.h b/gnu/usr.bin/gcc1/cc1/config/tm-bsd386.h deleted file mode 100644 index 2bfc61ddb9..0000000000 --- a/gnu/usr.bin/gcc1/cc1/config/tm-bsd386.h +++ /dev/null @@ -1,202 +0,0 @@ -/* Definitions for BSD assembler syntax for Intel 386 - (actually AT&T syntax for insns and operands, - adapted to BSD conventions for symbol names and debugging.) - Copyright (C) 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Use the Sequent Symmetry assembler syntax. */ - -#define TARGET_VERSION fprintf (stderr, " (80386, BSD syntax)"); - -/* Define some concatenation macros to concatenate an opcode - and one, two or three operands. In other assembler syntaxes - they may alter the order of ther operands. */ - -#ifdef __STDC__ -#define AS2(a,b,c) #a " " #b "," #c -#define AS3(a,b,c,d) #a " " #b "," #c "," #d -#define AS1(a,b) #a " " #b -#else -#define AS1(a,b) "a b" -#define AS2(a,b,c) "a b,c" -#define AS3(a,b,c,d) "a b,c,d" -#endif - -/* Output the size-letter for an opcode. - CODE is the letter used in an operand spec (L, B, W, S or Q). - CH is the corresponding lower case letter - (except if CODE is L then CH is `l'). */ -#define PUT_OP_SIZE(CODE,CH,FILE) putc (CH,(FILE)) - -/* Opcode suffix for fullword insn. */ -#define L_SIZE "l" - -/* Prefix for register names in this syntax. */ -#define RP "%" - -/* Prefix for immediate operands in this syntax. */ -#define IP "$" - -/* Prefix for internally generated assembler labels. */ -#define LPREFIX "L" - -/* Output the prefix for an immediate operand, or for an offset operand. */ -#define PRINT_IMMED_PREFIX(FILE) fputs ("$", (FILE)) -#define PRINT_OFFSET_PREFIX(FILE) fputs ("$", (FILE)) - -/* Indirect call instructions should use `*'. */ -#define USE_STAR 1 - -/* Prefix for a memory-operand X. */ -#define PRINT_PTR(X, FILE) - -/* Delimiters that surround base reg and index reg. */ -#define ADDR_BEG(FILE) putc('(', (FILE)) -#define ADDR_END(FILE) putc(')', (FILE)) - -/* Print an index register (whose rtx is IREG). */ -#define PRINT_IREG(FILE,IREG) \ - do \ - { fputs (",", (FILE)); PRINT_REG ((IREG), 0, (FILE)); } \ - while (0) - -/* Print an index scale factor SCALE. */ -#define PRINT_SCALE(FILE,SCALE) \ - if ((SCALE) != 1) fprintf ((FILE), ",%d", (SCALE)) - -/* Print a base/index combination. - BREG is the base reg rtx, IREG is the index reg rtx, - and SCALE is the index scale factor (an integer). */ - -#define PRINT_B_I_S(BREG,IREG,SCALE,FILE) \ - { ADDR_BEG (FILE); \ - if (BREG) PRINT_REG ((BREG), 0, (FILE)); \ - if ((IREG) != 0) \ - { PRINT_IREG ((FILE), (IREG)); \ - PRINT_SCALE ((FILE), (SCALE)); } \ - ADDR_END (FILE); } - -/* Define the syntax of pseudo-ops, labels and comments. */ - -/* Assembler pseudos to introduce constants of various size. */ - -#define ASM_BYTE "\t.byte " -#define ASM_SHORT "\t.word " -#define ASM_LONG "\t.long " -#define ASM_DOUBLE "\t.double " - -/* String containing the assembler's comment-starter. */ - -#define COMMENT_BEGIN "/" - -/* Output at beginning of assembler file. - ??? I am skeptical of this -- RMS. */ - -#define ASM_FILE_START(FILE) \ - fprintf (FILE, "\t.file\t\"%s\"\n", dump_base_name); - -/* This was suggested, but it shouldn't be right for DBX output. -- RMS - #define ASM_OUTPUT_SOURCE_FILENAME(FILE, NAME) */ - -/* Output to assembler file text saying following lines - may contain character constants, extra white space, comments, etc. */ - -#define ASM_APP_ON "/APP\n" - -/* Output to assembler file text saying following lines - no longer contain unusual constructs. */ - -#define ASM_APP_OFF "/NO_APP\n" - -/* Define the syntax of labels and symbol definitions/declarations. */ - -/* This is how to output an assembler line - that says to advance the location counter by SIZE bytes. */ - -#define ASM_OUTPUT_SKIP(FILE,SIZE) \ - fprintf (FILE, "\t.space %u\n", (SIZE)) - -/* Output before read-only data. */ - -#define TEXT_SECTION_ASM_OP ".text" - -/* Output before writable data. */ - -#define DATA_SECTION_ASM_OP ".data" - -/* Define the syntax of labels and symbol definitions/declarations. */ - -/* This says how to output an assembler line - to define a global common symbol. */ - -#define ASM_OUTPUT_COMMON(FILE, NAME, SIZE, ROUNDED) \ -( fputs (".comm ", (FILE)), \ - assemble_name ((FILE), (NAME)), \ - fprintf ((FILE), ",%u\n", (SIZE))) - -/* This says how to output an assembler line - to define a local common symbol. */ - -#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED) \ -( fputs (".lcomm ", (FILE)), \ - assemble_name ((FILE), (NAME)), \ - fprintf ((FILE), ",%u\n", (SIZE))) - -/* This is how to output an assembler line - that says to advance the location counter - to a multiple of 2**LOG bytes. */ - -#define ASM_OUTPUT_ALIGN(FILE,LOG) \ - if ((LOG)!=0) fprintf ((FILE), "\t.align %d\n", (LOG)) - -/* This is how to store into the string BUF - the symbol_ref name of an internal numbered label where - PREFIX is the class of label and NUM is the number within the class. - This is suitable for output with `assemble_name'. */ - -#define ASM_GENERATE_INTERNAL_LABEL(BUF,PREFIX,NUMBER) \ - sprintf ((BUF), "*%s%d", (PREFIX), (NUMBER)) - -/* This is how to output an internal numbered label where - PREFIX is the class of label and NUM is the number within the class. */ - -#define ASM_OUTPUT_INTERNAL_LABEL(FILE,PREFIX,NUM) \ - fprintf (FILE, "%s%d:\n", PREFIX, NUM) - -/* This is how to output a reference to a user-level label named NAME. */ - -#define ASM_OUTPUT_LABELREF(FILE,NAME) \ - fprintf (FILE, "_%s", NAME) - -/* Sequent has some changes in the format of DBX symbols. */ -#define DBX_NO_XREFS 1 - -/* Don't split DBX symbols into continuations. */ -#define DBX_CONTIN_LENGTH 0 - -/* This is how to output a command to make the user-level label named NAME - defined for reference from other files. */ - -#define ASM_GLOBALIZE_LABEL(FILE,NAME) \ - (fputs (".globl ", FILE), assemble_name (FILE, NAME), fputs ("\n", FILE)) - -/* This is how to output an assembler line defining a `double' constant. */ - -#undef ASM_OUTPUT_DOUBLE -#define ASM_OUTPUT_DOUBLE(FILE,VALUE) \ - fprintf (FILE, "\t.double 0d%.20e\n", (VALUE)) diff --git a/gnu/usr.bin/gcc1/cc1/config/tm-compaq.h b/gnu/usr.bin/gcc1/cc1/config/tm-compaq.h deleted file mode 100644 index 6aa7d5f767..0000000000 --- a/gnu/usr.bin/gcc1/cc1/config/tm-compaq.h +++ /dev/null @@ -1,39 +0,0 @@ -/* Definitions for Compaq as target machine. NOT TESTED! - Copyright (C) 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "tm-i386.h" - -/* Use the ATT assembler syntax. */ - -#include "tm-att386.h" - -/* By default, target has a 80387. */ - -#define TARGET_DEFAULT 1 - -#define ASM_SPEC "" - -/* Names to predefine in the preprocessor for this target machine. */ - -#define CPP_PREDEFINES "-Di386 -Di80386 -Dunix" - - -#include "tm-i386.h" -#include "tm-att386.h" diff --git a/gnu/usr.bin/gcc1/cc1/config/tm-i386.h b/gnu/usr.bin/gcc1/cc1/config/tm-i386.h deleted file mode 100644 index 499667ea26..0000000000 --- a/gnu/usr.bin/gcc1/cc1/config/tm-i386.h +++ /dev/null @@ -1,1083 +0,0 @@ -/* Definitions of target machine for GNU compiler for Intel 80386. - Copyright (C) 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* Note that some other tm- files include this one and then override - many of the definitions that relate to assembler syntax. */ - -/* Names to predefine in the preprocessor for this target machine. */ - -/* the file tm-compaq.h includes this file */ - - -#define I386 1 - -/* Run-time compilation parameters selecting different hardware subsets. */ - -extern int target_flags; - -/* Macros used in the machine description to test the flags. */ - -/* Compile 80387 insns for floating point (not library calls). */ -#define TARGET_80387 (target_flags & 1) -/* Compile using ret insn that pops args. - This will not work unless you use prototypes at least - for all functions that can take varying numbers of args. */ -#define TARGET_RTD (target_flags & 8) -/* Compile passing first two args in regs 0 and 1. - This exists only to test compiler features that will - be needed for RISC chips. It is not usable - and is not intended to be usable on this cpu. */ -#define TARGET_REGPARM (target_flags & 020) - -/* Macro to define tables used to set the flags. - This is a list in braces of pairs in braces, - each pair being { "NAME", VALUE } - where VALUE is the bits to set or minus the bits to clear. - An empty string NAME is used to identify the default VALUE. */ - -#define TARGET_SWITCHES \ - { { "80387", 1}, \ - { "soft-float", -1}, \ - { "rtd", 8}, \ - { "nortd", -8}, \ - { "regparm", 020}, \ - { "noregparm", -020}, \ - { "", TARGET_DEFAULT}} - -/* TARGET_DEFAULT is defined in tm-compaq.h, etc. */ - -/* target machine storage layout */ - -/* Define this if most significant byte of a word is the lowest numbered. */ -/* That is true on the 80386. */ - -/* #define BITS_BIG_ENDIAN */ - -/* Define this if most significant byte of a word is the lowest numbered. */ -/* That is not true on the 80386. */ -/* #define BYTES_BIG_ENDIAN */ - -/* Define this if most significant word of a multiword number is numbered. */ -/* Not true for 80386 */ -/* #define WORDS_BIG_ENDIAN */ - -/* number of bits in an addressible storage unit */ -#define BITS_PER_UNIT 8 - -/* Width in bits of a "word", which is the contents of a machine register. - Note that this is not necessarily the width of data type `int'; - if using 16-bit ints on a 80386, this would still be 32. - But on a machine with 16-bit registers, this would be 16. */ -#define BITS_PER_WORD 32 - -/* Width of a word, in units (bytes). */ -#define UNITS_PER_WORD 4 - -/* Width in bits of a pointer. - See also the macro `Pmode' defined below. */ -#define POINTER_SIZE 32 - -/* Allocation boundary (in *bits*) for storing pointers in memory. */ -#define POINTER_BOUNDARY 32 - -/* Allocation boundary (in *bits*) for storing arguments in argument list. */ -#define PARM_BOUNDARY 32 - -/* Boundary (in *bits*) on which stack pointer should be aligned. */ -#define STACK_BOUNDARY 32 - -/* Allocation boundary (in *bits*) for the code of a function. */ -#define FUNCTION_BOUNDARY 32 - -/* Alignment of field after `int : 0' in a structure. */ - -#define EMPTY_FIELD_BOUNDARY 32 - -/* There is no point aligning anything to a rounder boundary than this. */ -/* Some structures in the ATT libraries are assumed to round up from 16 to 18 - bytes, for example the _io_buf */ -#define BIGGEST_ALIGNMENT 32 - -/* Define this if move instructions will actually fail to work - when given unaligned data. */ -/* #define STRICT_ALIGNMENT */ - -/* Standard register usage. */ - -/* Number of actual hardware registers. - The hardware registers are assigned numbers for the compiler - from 0 to just below FIRST_PSEUDO_REGISTER. - All registers that the compiler knows about must be given numbers, - even those that are not normally considered general registers. - In the 80387 we give the 8 general purpose registers the numbers 0-7, - we assign 6 numbers for floating point registers 8-13, - Note that registers 0-7 can be accessed as a short or int, - while only 0-3 may be used with mov byte instructions. -*/ -#define FIRST_PSEUDO_REGISTER 10 - -/* 1 for registers that have pervasive standard uses - and are not available for the register allocator. - On the 80386, only the stack pointer is such. */ -#define FIXED_REGISTERS \ -/*ax,ad,ac,ab,si,di,bp,sp,fval,fp0*/ \ -{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0} - -/* ;;change-wfs */ - -/* 1 for registers not available across function calls. - These must include the FIXED_REGISTERS and also any - registers that can be used without being saved. - The latter must include the registers where values are returned - and the register where structure-value addresses are passed. - Aside from that, you can include as many other registers as you like. */ - -#define CALL_USED_REGISTERS \ -/*ax,ad,ac,ab,si,di,bp,sp,*/ \ -{ 1, 1, 1, 0, 0, 0, 0, 1, \ - 1, 1} - -/* Return number of consecutive hard regs needed starting at reg REGNO - to hold something of mode MODE. - This is ordinarily the length in words of a value of mode MODE - but can be less for certain modes in special long registers. - - Actually there are no two word move instructions for consecutive - registers. And only registers 0-3 may have mov byte instructions - applied to them. - */ - -#define HARD_REGNO_NREGS(REGNO, MODE) \ - ((REGNO) >= 8 ? 1 \ - : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)) - -/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. - On the 80386, the first 4 cpu registers can hold any mode. - While the floating point registers may hold SFmode or DFmode only. - */ - -#define HARD_REGNO_MODE_OK(REGNO, MODE) \ - hard_regno_mode_ok(REGNO,MODE) - -/* Value is 1 if it is a good idea to tie two pseudo registers - when one has mode MODE1 and one has mode MODE2. - If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2, - for any hard reg, then this must be 0 for correct output. */ - -#define MODES_TIEABLE_P(MODE1, MODE2) ((MODE1) == (MODE2)) - -/* Specify the registers used for certain standard purposes. - The values of these macros are register numbers. */ - -/* on the 386 the pc register is %eip, and is not usable as a general - register. The ordinary mov instructions won't work */ -/* #define PC_REGNUM */ - -/* Register to use for pushing function arguments. */ -#define STACK_POINTER_REGNUM 7 - -/* Base register for access to local variables of the function. */ -#define FRAME_POINTER_REGNUM 6 - -/* First floating point reg */ -#define FIRST_FLOAT_REG 8 -/* Value should be nonzero if functions must have frame pointers. - Zero means the frame pointer need not be set up (and parms - may be accessed via the stack pointer) in functions that seem suitable. - This is computed in `reload', in reload1.c. */ -#define FRAME_POINTER_REQUIRED 0 - -/* Base register for access to arguments of the function. */ -#define ARG_POINTER_REGNUM 6 - -/* Register in which static-chain is passed to a function. */ -#define STATIC_CHAIN_REGNUM 2 - -/* Register in which address to store a structure value - arrives in the function. On the 386, the prologue - copies this from the stack to register %eax. */ -#define STRUCT_VALUE_INCOMING \ - gen_rtx (MEM, Pmode, gen_rtx (PLUS, Pmode, frame_pointer_rtx, \ - gen_rtx (CONST_INT, VOIDmode, 8))) - -/* Place in which caller passes the structure value address. - Actually, all that matters about this value is it its rtx_code: - MEM means push the value on the stack like an argument. */ -#define STRUCT_VALUE \ - gen_rtx (MEM, Pmode, gen_rtx (PRE_DEC, Pmode, stack_pointer_rtx)) - -/* Define the classes of registers for register constraints in the - machine description. Also define ranges of constants. - - One of the classes must always be named ALL_REGS and include all hard regs. - If there is more than one class, another class must be named NO_REGS - and contain no registers. - - The name GENERAL_REGS must be the name of a class (or an alias for - another name such as ALL_REGS). This is the class of registers - that is allowed by "g" or "r" in a register constraint. - Also, registers outside this class are allocated only when - instructions express preferences for them. - - The classes must be numbered in nondecreasing order; that is, - a larger-numbered class must never be contained completely - in a smaller-numbered class. - - For any two classes, it is very desirable that there be another - class that represents their union. */ - - -enum reg_class { - NO_REGS, AREG, DREG, ADREG, CREG, BREG, Q_REGS, SIREG, DIREG, - INDEX_REGS, GENERAL_REGS, FLOAT_REGS, ALL_REGS, LIM_REG_CLASSES }; - -#define N_REG_CLASSES (int) LIM_REG_CLASSES - -/* Give names of register classes as strings for dump file. */ - -#define REG_CLASS_NAMES \ -{ "NO_REGS", "AREG", "DREG", "ADREG", "CREG", "BREG","Q_REGS", \ - "SIREG", "DIREG", \ - "INDEX_REGS", "GENERAL_REGS", "FLOAT_REGS", "ALL_REGS"} -/* Define which registers fit in which classes. - This is an initializer for a vector of HARD_REG_SET - of length N_REG_CLASSES. */ - - - -#define REG_CLASS_CONTENTS {0, 0x1, 0x2, 0x3, 0x4, 0x8, 0xf,\ - 0x10, 0x20, 0x7f, 0xff, 0x300, 0x3ff} - -/* The same information, inverted: - Return the class number of the smallest class containing - reg number REGNO. This could be a conditional expression - or could index an array. */ - -#define REGNO_REG_CLASS(REGNO) \ - ((REGNO) == 0 ? AREG : \ - (REGNO) == 1 ? DREG : \ - (REGNO) == 2 ? CREG : \ - (REGNO) == 3 ? BREG : \ - (REGNO) == 4 ? SIREG : \ - (REGNO) == 5 ? DIREG : \ - (REGNO) == 7 ? GENERAL_REGS : \ - (REGNO) < 8 ? INDEX_REGS : \ - FLOAT_REGS) - -#define NON_QI_REG_P(X) \ - (REG_P (X) && REGNO (X) >= 4 && REGNO (X) < FIRST_PSEUDO_REGISTER) - -#define FP_REG_P(X) (REG_P (X) && FP_REGNO_P (REGNO (X))) -#define FP_REGNO_P(n) ((n) >= FIRST_FLOAT_REG && (n) < FIRST_PSEUDO_REGISTER) - -/* Try to maintain the accuracy of the death notes for regs satisfying the - following. Important for stack like regs, to know when to pop. */ - -#define PRESERVE_DEATH_INFO_REGNO_P(x) FP_REGNO_P(x) - -/* 1 if register REGNO can magically overlap other regs. - Note that nonzero values work only in very special circumstances. - We return 1 for an FP reg because "both" our FP regs - are really the same reg. */ - -#define OVERLAPPING_REGNO_P(REGNO) FP_REGNO_P (REGNO) - -/* The class value for index registers, and the one for base regs. */ - -#define INDEX_REG_CLASS INDEX_REGS -#define BASE_REG_CLASS GENERAL_REGS - -/* Get reg_class from a letter such as appears in the machine description. */ - -#define REG_CLASS_FROM_LETTER(C) \ - ((C) == 'r' ? GENERAL_REGS : \ - (C) == 'q' ? Q_REGS : \ - (C) == 'f' ? FLOAT_REGS : \ - (C) == 'a' ? AREG : (C) == 'b' ? BREG : \ - (C) == 'c' ? CREG : (C) == 'd' ? DREG : \ - (C) == 'A' ? ADREG : \ - (C) == 'S' ? SIREG : \ - (C) == 'D' ? DIREG : NO_REGS) - -/* The letters I, J, K, L and M in a register constraint string - can be used to stand for particular ranges of immediate operands. - This macro defines what the ranges are. - C is the letter, and VALUE is a constant value. - Return 1 if VALUE is in the range specified by C. - - I is for the maximum shifts. - */ - -#define CONST_OK_FOR_LETTER_P(VALUE, C) \ - ((C) == 'I' ? (VALUE) >= 0 && (VALUE) <= 31 :0) - -/* Similar, but for floating constants, and defining letters G and H. - Here VALUE is the CONST_DOUBLE rtx itself. */ - -#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \ - ((C) == 'G' ? ! (TARGET_80387 && standard_80387_constant_p (VALUE)) : 1) - -/* Given an rtx X being reloaded into a reg required to be - in class CLASS, return the class of reg to actually use. - In general this is just CLASS; but on some machines - in some cases it is preferable to use a more restrictive class. - On the 80386 series, we prevent floating constants from being - reloaded into floating registers (since no move-insn can do that) - and we ensure that QImodes aren't reloaded into the esi or edi reg. */ - -#define PREFERRED_RELOAD_CLASS(X,CLASS) \ - (GET_CODE (X) == CONST_DOUBLE \ - ? ((CLASS) == GENERAL_REGS || (CLASS) == ALL_REGS \ - ? GENERAL_REGS : NO_REGS) \ - : GET_MODE (X) == QImode \ - ? ((CLASS) == GENERAL_REGS || (CLASS) == ALL_REGS \ - ? Q_REGS \ - : (CLASS) == INDEX_REGS ? (abort (), INDEX_REGS) \ - : (CLASS)) \ - : (CLASS)) - -/* Return the maximum number of consecutive registers - needed to represent mode MODE in a register of class CLASS. */ -/* On the 80386, this is the size of MODE in words, - except in the FP regs, where a single reg is always enough. */ -#define CLASS_MAX_NREGS(CLASS, MODE) \ - ((CLASS) == FLOAT_REGS ? 1 : \ - ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)) - -/* Stack layout; function entry, exit and calling. */ - -/* Define this if pushing a word on the stack - makes the stack pointer a smaller address. */ -#define STACK_GROWS_DOWNWARD - -/* Define this if the nominal address of the stack frame - is at the high-address end of the local variables; - that is, each additional local variable allocated - goes at a more negative offset in the frame. */ -#define FRAME_GROWS_DOWNWARD - -/* Offset within stack frame to start allocating local variables at. - If FRAME_GROWS_DOWNWARD, this is the offset to the END of the - first local allocated. Otherwise, it is the offset to the BEGINNING - of the first local allocated. */ -#define STARTING_FRAME_OFFSET 0 - -/* If we generate an insn to push BYTES bytes, - this says how many the stack pointer really advances by. - On 386 pushw decrements by exactly 2 no matter what the position was. - On the 386 there is no pushb; we use pushw instead, and this - has the effect of rounding up to 2. */ - -#define PUSH_ROUNDING(BYTES) (((BYTES) + 1) & (-2)) - -/* Offset of first parameter from the argument pointer register value. */ -#define FIRST_PARM_OFFSET(FNDECL) 8 - -/* Value is 1 if returning from a function call automatically - pops the arguments described by the number-of-args field in the call. - FUNTYPE is the data type of the function (as a tree), - or for a library call it is an identifier node for the subroutine name. - - On the 80386, the RTD insn may be used to pop them if the number - of args is fixed, but if the number is variable then the caller - must pop them all. RTD can't be used for library calls now - because the library is compiled with the Unix compiler. - Use of RTD is a selectable option, since it is incompatible with - standard Unix calling sequences. If the option is not selected, - the caller must always pop the args. */ - -#define RETURN_POPS_ARGS(FUNTYPE) \ - (TARGET_RTD && TREE_CODE (FUNTYPE) != IDENTIFIER_NODE \ - && (TYPE_ARG_TYPES (FUNTYPE) == 0 \ - || TREE_VALUE (tree_last (TYPE_ARG_TYPES (FUNTYPE))) == void_type_node)) - -#define FUNCTION_VALUE(VALTYPE, FUNC) \ - gen_rtx (REG, TYPE_MODE (VALTYPE), \ - VALUE_REGNO(TYPE_MODE(VALTYPE))) - -/* Define how to find the value returned by a library function - assuming the value has mode MODE. */ - -#define LIBCALL_VALUE(MODE) \ - gen_rtx (REG, MODE, VALUE_REGNO(MODE)) - -/* 1 if N is a possible register number for function argument passing. - On the 80386, no registers are used in this way. - *NOTE* -mregparm does not work. - It exists only to test register calling conventions. */ - -#define FUNCTION_ARG_REGNO_P(N) 0 -/* Define a data type for recording info about an argument list - during the scan of that argument list. This data type should - hold all necessary information about the function itself - and about the args processed so far, enough to enable macros - such as FUNCTION_ARG to determine where the next arg should go. - - On the 80386, this is a single integer, which is a number of bytes - of arguments scanned so far. */ - -#define CUMULATIVE_ARGS int - -/* Initialize a variable CUM of type CUMULATIVE_ARGS - for a call to a function whose data type is FNTYPE. - For a library call, FNTYPE is 0. - - On the 80386, the offset starts at 0. */ - -#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE) \ - ((CUM) = 0) - -/* Update the data in CUM to advance over an argument - of mode MODE and data type TYPE. - (TYPE is null for libcalls where that information may not be available.) */ - -#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ - ((CUM) += ((MODE) != BLKmode \ - ? (GET_MODE_SIZE (MODE) + 3) & ~3 \ - : (int_size_in_bytes (TYPE) + 3) & ~3)) - -/* Define where to put the arguments to a function. - Value is zero to push the argument on the stack, - or a hard register in which to store the argument. - - MODE is the argument's machine mode. - TYPE is the data type of the argument (as a tree). - This is null for libcalls where that information may - not be available. - CUM is a variable of type CUMULATIVE_ARGS which gives info about - the preceding args and about the function being called. - NAMED is nonzero if this argument is a named parameter - (otherwise it is an extra parameter matching an ellipsis). */ - - -/* On the 80386 all args are pushed, except if -mregparm is specified - then the first two words of arguments are passed in EAX, EDX. - *NOTE* -mregparm does not work. - It exists only to test register calling conventions. */ - -#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ -((TARGET_REGPARM && (CUM) < 8) ? gen_rtx (REG, (MODE), (CUM) / 4) : 0) - -/* For an arg passed partly in registers and partly in memory, - this is the number of registers used. - For args passed entirely in registers or entirely in memory, zero. */ - - -#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \ -((TARGET_REGPARM && (CUM) < 8 \ - && 8 < ((CUM) + ((MODE) == BLKmode \ - ? int_size_in_bytes (TYPE) \ - : GET_MODE_SIZE (MODE)))) \ - ? 2 - (CUM) / 4 : 0) - -/* This macro generates the assembly code for function entry. - FILE is a stdio stream to output the code to. - SIZE is an int: how many units of temporary storage to allocate. - Refer to the array `regs_ever_live' to determine which registers - to save; `regs_ever_live[I]' is nonzero if register number I - is ever used in the function. This macro is responsible for - knowing which registers should not be saved even if used. */ - -#define FUNCTION_PROLOGUE(FILE, SIZE) \ - function_prologue (FILE, SIZE) - -/* Output assembler code to FILE to increment profiler label # LABELNO - for profiling a function entry. */ - -#define FUNCTION_PROFILER(FILE, LABELNO) \ - fprintf (FILE, "\tmovl $%sP%d,%%edx\n\tcall _mcount\n", LPREFIX, (LABELNO)); - -/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function, - the stack pointer does not matter. The value is tested only in - functions that have frame pointers. - No definition is equivalent to always zero. */ -/* Note on the 386 it might be more efficient not to define this since - we have to restore it ourselves from the frame pointer, in order to - use pop */ - -#define EXIT_IGNORE_STACK 1 - -/* This macro generates the assembly code for function exit, - on machines that need it. If FUNCTION_EPILOGUE is not defined - then individual return instructions are generated for each - return statement. Args are same as for FUNCTION_PROLOGUE. - - The function epilogue should not depend on the current stack pointer! - It should use the frame pointer only. This is mandatory because - of alloca; we also take advantage of it to omit stack adjustments - before returning. */ - -#define FUNCTION_EPILOGUE(FILE, SIZE) \ - function_epilogue (FILE, SIZE) - -/* If the memory address ADDR is relative to the frame pointer, - correct it to be relative to the stack pointer instead. - This is for when we don't use a frame pointer. - ADDR should be a variable name. */ - - -#define FIX_FRAME_POINTER_ADDRESS(ADDR,DEPTH) \ -{ int offset = -1; \ - rtx regs = stack_pointer_rtx; \ - if (ADDR == frame_pointer_rtx) \ - offset = 0; \ - else if (GET_CODE (ADDR) == PLUS && XEXP (ADDR, 0) == frame_pointer_rtx \ - && GET_CODE (XEXP (ADDR, 1)) == CONST_INT) \ - offset = INTVAL (XEXP (ADDR, 1)); \ - else if (GET_CODE (ADDR) == PLUS && XEXP (ADDR, 0) == frame_pointer_rtx) \ - { rtx other_reg = XEXP (ADDR, 1); \ - offset = 0; \ - regs = gen_rtx (PLUS, Pmode, stack_pointer_rtx, other_reg); } \ - else if (GET_CODE (ADDR) == PLUS && XEXP (ADDR, 1) == frame_pointer_rtx) \ - { rtx other_reg = XEXP (ADDR, 0); \ - offset = 0; \ - regs = gen_rtx (PLUS, Pmode, stack_pointer_rtx, other_reg); } \ - else if (GET_CODE (ADDR) == PLUS \ - && GET_CODE (XEXP (ADDR, 0)) == PLUS \ - && XEXP (XEXP (ADDR, 0), 0) == frame_pointer_rtx \ - && GET_CODE (XEXP (ADDR, 1)) == CONST_INT) \ - { rtx other_reg = XEXP (XEXP (ADDR, 0), 1); \ - offset = INTVAL (XEXP (ADDR, 1)); \ - regs = gen_rtx (PLUS, Pmode, stack_pointer_rtx, other_reg); } \ - else if (GET_CODE (ADDR) == PLUS \ - && GET_CODE (XEXP (ADDR, 0)) == PLUS \ - && XEXP (XEXP (ADDR, 0), 1) == frame_pointer_rtx \ - && GET_CODE (XEXP (ADDR, 1)) == CONST_INT) \ - { rtx other_reg = XEXP (XEXP (ADDR, 0), 0); \ - offset = INTVAL (XEXP (ADDR, 1)); \ - regs = gen_rtx (PLUS, Pmode, stack_pointer_rtx, other_reg); } \ - if (offset >= 0) \ - { int regno; \ - extern char call_used_regs[]; \ - for (regno = FIRST_FLOAT_REG; regno < FIRST_PSEUDO_REGISTER; regno++)\ - if (regs_ever_live[regno] && ! call_used_regs[regno]) \ - offset += 8; \ - for (regno=0 ; regno = FIRST_PSEUDO_REGISTER) -/* Nonzero if X is a hard reg that can be used as a base reg - of if it is a pseudo reg. */ - /* ?wfs */ -#define REG_OK_FOR_BASE_P(X) (REGNO (X) <= STACK_POINTER_REGNUM || REGNO(X) >= FIRST_PSEUDO_REGISTER) -#define REG_OK_FOR_STRREG_P(X) \ - (REGNO (X) == 4 || REGNO (X) == 5 || REGNO (X) >= FIRST_PSEUDO_REGISTER) - -#else - -/* Nonzero if X is a hard reg that can be used as an index. */ -#define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X)) -/* Nonzero if X is a hard reg that can be used as a base reg. */ -#define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X)) -#define REG_OK_FOR_STRREG_P(X) \ - (REGNO_OK_FOR_DIREG_P (REGNO (X)) || REGNO_OK_FOR_SIREG_P (REGNO (X))) - -#endif - -/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression - that is a valid memory address for an instruction. - The MODE argument is the machine mode for the MEM expression - that wants to use this address. - - The other macros defined here are used only in GO_IF_LEGITIMATE_ADDRESS, - except for CONSTANT_ADDRESS_P which is usually machine-independent. */ - -#define MAX_REGS_PER_ADDRESS 2 - -#define CONSTANT_ADDRESS_P(X) CONSTANT_P (X) - -/* Nonzero if the constant value X is a legitimate general operand. - It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */ - -#define LEGITIMATE_CONSTANT_P(X) 1 - -#define GO_IF_INDEXABLE_BASE(X, ADDR) \ - if (GET_CODE (X) == REG && REG_OK_FOR_BASE_P (X)) goto ADDR - -#define LEGITIMATE_INDEX_REG_P(X) \ - (GET_CODE (X) == REG && REG_OK_FOR_INDEX_P (X)) - -/* Return 1 if X is an index or an index times a scale. */ - -#define LEGITIMATE_INDEX_P(X) \ - (LEGITIMATE_INDEX_REG_P (X) \ - || (GET_CODE (X) == MULT \ - && LEGITIMATE_INDEX_REG_P (XEXP (X, 0)) \ - && GET_CODE (XEXP (X, 1)) == CONST_INT \ - && (INTVAL (XEXP (X, 1)) == 2 \ - || INTVAL (XEXP (X, 1)) == 4 \ - || INTVAL (XEXP (X, 1)) == 8))) - -/* Go to ADDR if X is an index term, a base reg, or a sum of those. */ - -#define GO_IF_INDEXING(X, ADDR) \ -{ if (LEGITIMATE_INDEX_P (X)) goto ADDR; \ - GO_IF_INDEXABLE_BASE (X, ADDR); \ - if (GET_CODE (X) == PLUS && LEGITIMATE_INDEX_P (XEXP (X, 0))) \ - { GO_IF_INDEXABLE_BASE (XEXP (X, 1), ADDR); } \ - if (GET_CODE (X) == PLUS && LEGITIMATE_INDEX_P (XEXP (X, 1))) \ - { GO_IF_INDEXABLE_BASE (XEXP (X, 0), ADDR); } } - -/* We used to allow this, but it isn't ever used. - || ((GET_CODE (X) == POST_DEC || GET_CODE (X) == POST_INC) \ - && REG_P (XEXP (X, 0)) \ - && REG_OK_FOR_STRREG_P (XEXP (X, 0))) \ -*/ - -#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ -{ if (CONSTANT_ADDRESS_P (X)) goto ADDR; \ - GO_IF_INDEXING (X, ADDR); \ - if (GET_CODE (X) == PLUS) \ - { if (CONSTANT_ADDRESS_P (XEXP (X, 1))) \ - GO_IF_INDEXING (XEXP (X, 0), ADDR); \ - if (CONSTANT_ADDRESS_P (XEXP (X, 0))) \ - GO_IF_INDEXING (XEXP (X, 1), ADDR); } } - -/* Try machine-dependent ways of modifying an illegitimate address - to be legitimate. If we find one, return the new, valid address. - This macro is used in only one place: `memory_address' in explow.c. - - OLDX is the address as it was before break_out_memory_refs was called. - In some cases it is useful to look at this to decide what needs to be done. - - MODE and WIN are passed so that this macro can use - GO_IF_LEGITIMATE_ADDRESS. - - It is always safe for this macro to do nothing. It exists to recognize - opportunities to optimize the output. - - For the 80386, we handle X+REG by loading X into a register R and - using R+REG. R will go in a general reg and indexing will be used. - However, if REG is a broken-out memory address or multiplication, - nothing needs to be done because REG can certainly go in a general reg. */ - -#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) \ -{ register int ch = (X) != (OLDX); \ - if (GET_CODE (X) == PLUS) \ - { if (GET_CODE (XEXP (X, 0)) == MULT) \ - ch = 1, XEXP (X, 0) = force_operand (XEXP (X, 0), 0); \ - if (GET_CODE (XEXP (X, 1)) == MULT) \ - ch = 1, XEXP (X, 1) = force_operand (XEXP (X, 1), 0); \ - if (ch && GET_CODE (XEXP (X, 1)) == REG \ - && GET_CODE (XEXP (X, 0)) == REG) \ - return X; \ - if (ch) { GO_IF_LEGITIMATE_ADDRESS (MODE, X, WIN); } \ - if (GET_CODE (XEXP (X, 0)) == REG \ - || (GET_CODE (XEXP (X, 0)) == SIGN_EXTEND \ - && GET_CODE (XEXP (XEXP (X, 0), 0)) == REG \ - && GET_MODE (XEXP (XEXP (X, 0), 0)) == HImode)) \ - { register rtx temp = gen_reg_rtx (Pmode); \ - register rtx val = force_operand (XEXP (X, 1), temp); \ - if (val != temp) emit_move_insn (temp, val, 0); \ - XEXP (X, 1) = temp; \ - return X; } \ - else if (GET_CODE (XEXP (X, 1)) == REG \ - || (GET_CODE (XEXP (X, 1)) == SIGN_EXTEND \ - && GET_CODE (XEXP (XEXP (X, 1), 0)) == REG \ - && GET_MODE (XEXP (XEXP (X, 1), 0)) == HImode)) \ - { register rtx temp = gen_reg_rtx (Pmode); \ - register rtx val = force_operand (XEXP (X, 0), temp); \ - if (val != temp) emit_move_insn (temp, val, 0); \ - XEXP (X, 0) = temp; \ - return X; }}} - -/* Go to LABEL if ADDR (a legitimate address expression) - has an effect that depends on the machine mode it is used for. - On the 80386, only postdecrement and postincrement address depend thus - (the amount of decrement or increment being the length of the operand). */ -#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL) \ - if (GET_CODE (ADDR) == POST_INC || GET_CODE (ADDR) == POST_DEC) goto LABEL - -/* Specify the machine mode that this machine uses - for the index in the tablejump instruction. */ -#define CASE_VECTOR_MODE Pmode - -/* Define this if the tablejump instruction expects the table - to contain offsets from the address of the table. - Do not define this if the table should contain absolute addresses. */ -/* #define CASE_VECTOR_PC_RELATIVE */ - -/* Specify the tree operation to be used to convert reals to integers. - This should be changed to take advantage of fist --wfs ?? - */ -#define IMPLICIT_FIX_EXPR FIX_ROUND_EXPR - -/* This is the kind of divide that is easiest to do in the general case. */ -#define EASY_DIV_EXPR TRUNC_DIV_EXPR - -/* Define this as 1 if `char' should by default be signed; else as 0. */ -#define DEFAULT_SIGNED_CHAR 1 - -/* Max number of bytes we can move from memory to memory - in one reasonably fast instruction. */ -#define MOVE_MAX 4 - -/* Define this if zero-extension is slow (more than one real instruction). */ -/* #define SLOW_ZERO_EXTEND */ - -/* Nonzero if access to memory by bytes is slow and undesirable. */ -#define SLOW_BYTE_ACCESS 0 - -/* Define if shifts truncate the shift count - which implies one can omit a sign-extension or zero-extension - of a shift count. */ -#define SHIFT_COUNT_TRUNCATED - -/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits - is done just by pretending it is already truncated. */ -#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 - -/* We assume that the store-condition-codes instructions store 0 for false - and some other value for true. This is the value stored for true. */ - -#define STORE_FLAG_VALUE 1 - -/* When a prototype says `char' or `short', really pass an `int'. - (The 386 can't easily push less than an int.) */ - -#define PROMOTE_PROTOTYPES - -/* Specify the machine mode that pointers have. - After generation of rtl, the compiler makes no further distinction - between pointers and any other objects of this machine mode. */ -#define Pmode SImode - -/* A function address in a call instruction - is a byte address (for indexing purposes) - so give the MEM rtx a byte's mode. */ -#define FUNCTION_MODE QImode - -/* Define this if addresses of constant functions - shouldn't be put through pseudo regs where they can be cse'd. - Desirable on the 386 because a CALL with a constant address is - not much slower than one with a register address. */ -#define NO_FUNCTION_CSE - -/* Compute the cost of computing a constant rtl expression RTX - whose rtx-code is CODE. The body of this macro is a portion - of a switch statement. If the code is computed here, - return it with a return statement. Otherwise, break from the switch. */ - -#define CONST_COSTS(RTX,CODE) \ - case CONST_INT: \ - if (RTX == const0_rtx) return 0; \ - if ((unsigned) INTVAL (RTX) < 077) return 1; \ - case CONST: \ - case LABEL_REF: \ - case SYMBOL_REF: \ - return 3; \ - case CONST_DOUBLE: \ - return 5; \ - case PLUS: \ - if (GET_CODE (XEXP (RTX, 0)) == REG \ - && GET_CODE (XEXP (RTX, 1)) == CONST_INT) \ - return 2; - -/* Tell final.c how to eliminate redundant test instructions. */ - -/* ??? Find a better place to put this. */ -#if 0 -#define FINAL_PRESCAN_INSN(INSN, OPERANDS, NOPERANDS) \ - fp_hook (INSN, OPERANDS, NOPERANDS) -#endif - -/* Here we define machine-dependent flags and fields in cc_status - (see `conditions.h'). */ - -/* Set if the cc value is actually in the 80387, so a floating point - conditional branch must be output. */ -#define CC_IN_80387 04000 - -/* Store in cc_status the expressions - that the condition codes will describe - after execution of an instruction whose pattern is EXP. - Do not alter them if the instruction would not alter the cc's. */ - -#define NOTICE_UPDATE_CC(EXP, INSN) \ - notice_update_cc((EXP)) - -/* Output a signed jump insn. Use template NORMAL ordinarily, or - FLOAT following a floating point comparison. - Use NO_OV following an arithmetic insn that set the cc's - before a test insn that was deleted. - NO_OV may be zero, meaning final should reinsert the test insn - because the jump cannot be handled properly without it. */ - -#define OUTPUT_JUMP(NORMAL, FLOAT, NO_OV) \ -{ \ - if (cc_status.flags & CC_IN_80387) \ - return FLOAT; \ - if (cc_status.flags & CC_NO_OVERFLOW) \ - return NO_OV; \ - return NORMAL; \ -} - -/* Control the assembler format that we output. */ - -#ifdef ATT -#include -#else -#define FILNMLEN 14 -#endif - -/* How to refer to registers in assembler output. - This sequence is indexed by compiler's hard-register-number (see above). */ - -/* In order to refer to the first 8 regs as 32 bit regs prefix an "e" - For non floating point regs, the following are the HImode names. - */ - - -#define HI_REGISTER_NAMES \ -{"ax","dx","cx","bx","si","di","bp","sp", \ - "st","st(1)"} -/* ,"st(2)","st(3)","st(4)","st(5)" } */ -#define REGISTER_NAMES HI_REGISTER_NAMES - -/* Note we are omitting these since currently I don't know how -to get gcc to use these, since they want the same but different -number as al, and ax. -*/ - -/* note the last four are not really qi_registsers, but - the md will have to never output movb into one of them - only a movw . There is no movb into the hardware reg - esi that I can find */ - -#define QI_REGISTER_NAMES \ -{"al", "dl", "cl", "bl", "si", "di", "bp", "sp",} - -/* - Don't know how to use these, yet. They overlap with ax,dx,cx,bx - and so would clobber al,dl,cl,bl -#define QI_REGISTER_NAMES_TOP \ -{"ah", \ - "dh", \ - "ch", \ - "bh", } -*/ - -/* How to renumber registers for dbxand gdb. */ - -/* {0,2,1,3,6,7,4,5,12,13,14,15,16,17} */ -#define DBX_REGISTER_NUMBER(n) \ -((n)==0?0 :(n)==1?2 :(n)==2?1 :(n)==3?3 :(n)==4?6 :(n)==5?7 :(n)==6?4 :(n)==7?5 :(n)==8?12 :(n)==9?12 :(n)) - -/* This is how to output the definition of a user-level label named NAME, - such as the label on a static function or variable NAME. */ - -#define ASM_OUTPUT_LABEL(FILE,NAME) \ - (assemble_name (FILE, NAME), fputs (":\n", FILE)) - -/* This is how to output an assembler line defining a `double' constant. */ - -#define ASM_OUTPUT_DOUBLE(FILE,VALUE) \ - fprintf (FILE, "%s%.22e\n",ASM_DOUBLE, (VALUE)) - - -/* This is how to output an assembler line defining a `float' constant. */ - -#define ASM_OUTPUT_FLOAT(FILE,VALUE) \ -do { union { float f; long l;} tem; \ - tem.f = (VALUE); \ - fputs(ASM_LONG,FILE); \ - fprintf((FILE), "0x%x\n", tem.l); \ - } while (0) - - -/* Store in OUTPUT a string (made with alloca) containing - an assembler-name for a local static variable named NAME. - LABELNO is an integer which is different for each call. */ - -#define ASM_FORMAT_PRIVATE_NAME(OUTPUT, NAME, LABELNO) \ -( (OUTPUT) = (char *) alloca (strlen ((NAME)) + 10), \ - sprintf ((OUTPUT), "%s.%d", (NAME), (LABELNO))) - - - -/* This is how to output an assembler line defining an `int' constant. */ - -#define ASM_OUTPUT_INT(FILE,VALUE) \ -( fprintf (FILE,ASM_LONG), \ - output_addr_const (FILE,(VALUE)), \ - putc('\n',FILE)) - -/* Likewise for `char' and `short' constants. */ -/* is this supposed to do align too?? */ - -#define ASM_OUTPUT_SHORT(FILE,VALUE) \ -( fprintf (FILE,ASM_SHORT), \ - output_addr_const (FILE,(VALUE)), \ - putc('\n',FILE)) - -/* -#define ASM_OUTPUT_SHORT(FILE,VALUE) \ -( fputs (ASM_BYTE,FILE), \ - output_addr_const (FILE,(VALUE)), \ - fputs ( ",",FILE), \ - output_addr_const (FILE,(VALUE)), \ - fputs (" >> 8\n",FILE)) -*/ - - -#define ASM_OUTPUT_CHAR(FILE,VALUE) \ -( fprintf (FILE, ASM_BYTE), \ - output_addr_const (FILE,(VALUE)), \ - putc('\n',FILE)) - -/* This is how to output an assembler line for a numeric constant byte. */ - -#define ASM_OUTPUT_BYTE(FILE,VALUE) \ - fprintf ((FILE), "%s0x%x\n", ASM_BYTE, (VALUE)) - -/* This is how to output an insn to push a register on the stack. - It need not be very fast code. */ - -#define ASM_OUTPUT_REG_PUSH(FILE,REGNO) \ - fprintf (FILE, "\tpushl e%s\n", reg_names[REGNO]) - -/* This is how to output an insn to pop a register from the stack. - It need not be very fast code. */ - -#define ASM_OUTPUT_REG_POP(FILE,REGNO) \ - fprintf (FILE, "\tpopl e%s\n", reg_names[REGNO]) - -/* This is how to output an element of a case-vector that is absolute. - */ - -#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ - fprintf (FILE, "%s%s%d\n",ASM_LONG,LPREFIX, VALUE) - -/* This is how to output an element of a case-vector that is relative. - We don't use these on the 386 yet, because the ATT assembler can't do - forward reference the differences. - */ - -#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, VALUE, REL) abort(); \ - fprintf (FILE, "\t.word %s%d-%s%d\n",LPREFIX, VALUE,LPREFIX, REL) - -/* Define the parentheses used to group arithmetic operations - in assembler code. */ - -#define ASM_OPEN_PAREN "" -#define ASM_CLOSE_PAREN "" - -/* Define results of standard character escape sequences. */ -#define TARGET_BELL 007 -#define TARGET_BS 010 -#define TARGET_TAB 011 -#define TARGET_NEWLINE 012 -#define TARGET_VT 013 -#define TARGET_FF 014 -#define TARGET_CR 015 - -/* Print operand X (an rtx) in assembler syntax to file FILE. - CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified. - The CODE z takes the size of operand from the following digit, and - outputs b,w,or l respectively. - - On the 80386, we use several such letters: - f -- float insn (print a CONST_DOUBLE as a float rather than in hex). - L,W,B,Q,S -- print the opcode suffix for specified size of operand. - R -- print the prefix for register names. - z -- print the opcode suffix for the size of the current operand. - * -- print a star (in certain assembler syntax) - w -- print the operand as if it's a "word" (HImode) even if it isn't. - w -- print the operand as if it's a byte (QImode) even if it isn't. - c -- don't print special prefixes before constant operands. */ - -#define PRINT_OPERAND_PUNCT_VALID_P(CODE) \ - ((CODE) == '*') - -#define PRINT_OPERAND(FILE, X, CODE) \ - print_operand (FILE, X, CODE) - - -#define PRINT_OPERAND_ADDRESS(FILE, ADDR) \ - print_operand_address (FILE, ADDR) - -/* Routines in gnulib that return floats must return them in an fp reg, - just as other functions do which return such values. - These macros make that happen. */ - -#define SFVALUE float -#define INTIFY(FLOATVAL) FLOATVAL - -/* Nonzero if INSN magically clobbers register REGNO. */ - -#define INSN_CLOBBERS_REGNO_P(INSN, REGNO) \ - (FP_REGNO_P (REGNO) \ - && (GET_CODE (INSN) == JUMP_INSN || GET_CODE (INSN) == BARRIER)) - -/* a letter which is not needed by the normal asm syntax, which - we can use for operand syntax in the extended asm */ - -#define ASM_OPERAND_LETTER '#' - - -/* -Local variables: -version-control: t -End: -*/ diff --git a/gnu/usr.bin/gcc1/cc1/config/tm-i386b.h b/gnu/usr.bin/gcc1/cc1/config/tm-i386b.h deleted file mode 100644 index ab1a183dce..0000000000 --- a/gnu/usr.bin/gcc1/cc1/config/tm-i386b.h +++ /dev/null @@ -1,78 +0,0 @@ -/*- - * This code is derived from software copyrighted by the Free Software - * Foundation. - * - * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. - * [and modified yet again -wfj 1992] - * - * @(#)tm-i386b.h 6.2 (Berkeley) 5/8/91 - */ - -/* Definitions for 386BSD ; derived from tm-seq386.h. - Copyright (C) 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include "tm-i386.h" - -/* Use the BSD assembler syntax. */ - -#include "tm-bsd386.h" - -/* By default, target has a 80387. */ - -#define TARGET_DEFAULT 1 - -/* Specify predefined symbols in preprocessor. */ - -#define CPP_PREDEFINES "-Dunix -Di386 -D__386BSD__" - -/* Don't permit / as a comment start character. */ - -#undef COMMENT_BEGIN -#define COMMENT_BEGIN "#" - -#undef ASM_APP_ON -#define ASM_APP_ON "#APP\n" - -#undef ASM_APP_OFF -#define ASM_APP_OFF "#NO_APP\n" - -#undef ASM_FILE_START -#define ASM_FILE_START(FILE) fprintf (FILE, "#NO_APP\n"); - -/* We want to output DBX debugging information. */ - -#define DBX_DEBUGGING_INFO -#undef DBX_NO_XREFS -#undef DBX_CONTIN_LENGTH - -/* Floating-point return values come in the FP register. */ - -#define VALUE_REGNO(MODE) \ - (((MODE)==SFmode || (MODE)==DFmode) ? FIRST_FLOAT_REG : 0) - -/* 1 if N is a possible register number for a function value. */ - -#define FUNCTION_VALUE_REGNO_P(N) ((N) == 0 || (N)== FIRST_FLOAT_REG) - -/* Output assembler code to FILE to increment profiler label # LABELNO - for profiling a function entry. */ - -#undef FUNCTION_PROFILER -#define FUNCTION_PROFILER(FILE, LABELNO) \ - fprintf (FILE, "\tmovl $LP%d,%%eax\n\tcall mcount\n", (LABELNO)); diff --git a/gnu/usr.bin/gcc1/cc1/config/tm-i386b.h.save b/gnu/usr.bin/gcc1/cc1/config/tm-i386b.h.save deleted file mode 100644 index 1c37f6c0f4..0000000000 --- a/gnu/usr.bin/gcc1/cc1/config/tm-i386b.h.save +++ /dev/null @@ -1,76 +0,0 @@ -/* Definitions for BSD Intel 386; derived from tm-seq386.h. - Copyright (C) 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include "tm-i386.h" - -/* Use the BSD assembler syntax. */ - -#include "tm-bsd386.h" - -/* By default, target has a 80387. */ - -#define TARGET_DEFAULT 1 - -/* Specify predefined symbols in preprocessor. */ - -#define CPP_PREDEFINES "-Dunix -Di386 -Di386b" - -/* Don't permit / as a comment start character. */ - -#undef COMMENT_BEGIN -#define COMMENT_BEGIN "#" - -#undef ASM_APP_ON -#define ASM_APP_ON "#APP\n" - -#undef ASM_APP_OFF -#define ASM_APP_OFF "#NO_APP\n" - -#undef ASM_FILE_START -#define ASM_FILE_START(FILE) fprintf (FILE, "#NO_APP\n"); - -/* We want to output DBX debugging information. */ - -#define DBX_DEBUGGING_INFO -#undef DBX_NO_XREFS -#undef DBX_CONTIN_LENGTH - -/* Don't put floating point numbers in general registers -- there - are no fp-to-general register moves, and we run out of registers. */ - -#undef HARD_REGNO_MODE_OK -#define HARD_REGNO_MODE_OK(REGNO, MODE) \ - (MODE == DFmode || MODE == SFmode ? REGNO >= 8 \ - : MODE == QImode ? REGNO < 4 : REGNO < 8) - -/* Floating-point return values come in the FP register. */ - -#define VALUE_REGNO(MODE) \ - (((MODE)==SFmode || (MODE)==DFmode) ? FIRST_FLOAT_REG : 0) - -/* 1 if N is a possible register number for a function value. */ - -#define FUNCTION_VALUE_REGNO_P(N) ((N) == 0 || (N)== FIRST_FLOAT_REG) - -/* Output assembler code to FILE to increment profiler label # LABELNO - for profiling a function entry. */ - -#undef FUNCTION_PROFILER -#define FUNCTION_PROFILER(FILE, LABELNO) \ - fprintf (FILE, "\tmovl $LP%d,%%eax\n\tcall mcount\n", (LABELNO)); diff --git a/gnu/usr.bin/gcc1/cc1/config/tm-i386gas.h b/gnu/usr.bin/gcc1/cc1/config/tm-i386gas.h deleted file mode 100644 index fdb3c59d9a..0000000000 --- a/gnu/usr.bin/gcc1/cc1/config/tm-i386gas.h +++ /dev/null @@ -1,106 +0,0 @@ -/* Definitions for Intel 386 running system V with gnu tools - Copyright (C) 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "tm-i386.h" -/* Use the bsd assembler syntax. */ -/* we need to do this because gas is really a bsd style assembler, - * and so doesn't work well this these att-isms: - * - * ASM_OUTPUT_SKIP is .set .,.+N, which isn't implemented in gas - * ASM_OUTPUT_LOCAL is done with .set .,.+N, but that can't be - * used to define bss static space - * - * Next is the question of whether to uses underscores. RMS didn't - * like this idea at first, but since it is now obvious that we - * need this separate tm file for use with gas, at least to get - * dbx debugging info, I think we should also switch to underscores. - * We can keep tm-i386v for real att style output, and the few - * people who want both form will have to compile twice. - */ - -#include "tm-bsd386.h" - -/* these come from tm-bsd386.h, but are specific to sequent */ -#undef DBX_NO_XREFS -#undef DBX_CONTIN_LENGTH - -/* By default, target has a 80387. */ - -#define TARGET_DEFAULT 1 - -/* Specify predefined symbols in preprocessor. */ - -#define CPP_PREDEFINES "-Dunix -Di386" - -/* Allow #sccs in preprocessor. */ - -#define SCCS_DIRECTIVE - -/* Output #ident as a .ident. */ - -#define ASM_OUTPUT_IDENT(FILE, NAME) fprintf (FILE, "\t.ident \"%s\"\n", NAME); - -/* We do not want to output SDB debugging information. */ - -#undef SDB_DEBUGGING_INFO - -/* We want to output DBX debugging information. */ - -#define DBX_DEBUGGING_INFO - -/* Implicit library calls should use memcpy, not bcopy, etc. */ - -#define TARGET_MEM_FUNCTIONS - -#if 0 /* People say gas uses the log as the arg to .align. */ -/* When using gas, .align N aligns to an N-byte boundary. */ - -#undef ASM_OUTPUT_ALIGN -#define ASM_OUTPUT_ALIGN(FILE,LOG) \ - if ((LOG)!=0) fprintf ((FILE), "\t.align %d\n", 1<<(LOG)) -#endif - -/* Align labels, etc. at 4-byte boundaries. */ - -#define ASM_OUTPUT_ALIGN_CODE(FILE) \ - fprintf ((FILE), "\t.align 2\n"); /* Use log of 4 as arg. */ - -#if 0 -#define ASM_OUTPUT_ALIGN_CODE(FILE) \ - fprintf ((FILE), "\t.align 4\n"); -#endif - -/* Machines that use the AT&T assembler syntax - also return floating point values in an FP register. */ -/* Define how to find the value returned by a function. - VALTYPE is the data type of the value (as a tree). - If the precise function being called is known, FUNC is its FUNCTION_DECL; - otherwise, FUNC is 0. */ - -#define VALUE_REGNO(MODE) \ - (((MODE)==SFmode || (MODE)==DFmode) ? FIRST_FLOAT_REG : 0) - -/* 1 if N is a possible register number for a function value. */ - -#define FUNCTION_VALUE_REGNO_P(N) ((N) == 0 || (N)== FIRST_FLOAT_REG) - -#undef ASM_FILE_START -#define ASM_FILE_START(FILE) \ - fprintf (FILE, "\t.file\t\"%s\"\n", dump_base_name); diff --git a/gnu/usr.bin/gcc1/cc1/config/tm-i386v.h b/gnu/usr.bin/gcc1/cc1/config/tm-i386v.h deleted file mode 100644 index 573804b317..0000000000 --- a/gnu/usr.bin/gcc1/cc1/config/tm-i386v.h +++ /dev/null @@ -1,86 +0,0 @@ -/* Definitions for Intel 386 running system V. - Copyright (C) 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "tm-i386.h" - -/* Use the ATT assembler syntax. */ - -#include "tm-att386.h" - -/* By default, target has a 80387. */ - -#define TARGET_DEFAULT 1 - -/* Use crt1.o as a startup file and crtn.o as a closing file. */ - -#define STARTFILE_SPEC \ - "%{pg:gcrt1.o%s}%{!pg:%{posix:%{p:mcrtp1.o%s}%{!p:crtp1.o%s}}%{!posix:%{p:mcrt1.o%s}%{!p:crt1.o%s}}}\ - %{p:-L/usr/lib/libp}%{pg:-L/usr/lib/libp}" - -#define LIB_SPEC "%{posix:-lcposix} %{shlib:-lc_s} -lc crtn.o%s" - -/* Specify predefined symbols in preprocessor. */ - -#define CPP_PREDEFINES "-Dunix -Di386" - -#define CPP_SPEC "%{posix:-D_POSIX_SOURCE}" - -/* Allow #sccs in preprocessor. */ - -#define SCCS_DIRECTIVE - -/* Output #ident as a .ident. */ - -#define ASM_OUTPUT_IDENT(FILE, NAME) fprintf (FILE, "\t.ident \"%s\"\n", NAME); - -/* We want to output SDB debugging information. */ - -#define SDB_DEBUGGING_INFO - -/* We don't want to output DBX debugging information. */ - -#undef DBX_DEBUGGING_INFO - -/* Implicit library calls should use memcpy, not bcopy, etc. */ - -#define TARGET_MEM_FUNCTIONS - -/* Writing `int' for a bitfield forces int alignment for the structure. */ - -#define PCC_BITFIELD_TYPE_MATTERS 1 - -/* Don't write a `.optim' pseudo; this assembler doesn't handle them. */ - -#undef ASM_FILE_START_1 -#define ASM_FILE_START_1(FILE) - -/* Machines that use the AT&T assembler syntax - also return floating point values in an FP register. */ -/* Define how to find the value returned by a function. - VALTYPE is the data type of the value (as a tree). - If the precise function being called is known, FUNC is its FUNCTION_DECL; - otherwise, FUNC is 0. */ - -#define VALUE_REGNO(MODE) \ - (((MODE)==SFmode || (MODE)==DFmode) ? FIRST_FLOAT_REG : 0) - -/* 1 if N is a possible register number for a function value. */ - -#define FUNCTION_VALUE_REGNO_P(N) ((N) == 0 || (N)== FIRST_FLOAT_REG) diff --git a/gnu/usr.bin/gcc1/cc1/config/tm-i386v4.h b/gnu/usr.bin/gcc1/cc1/config/tm-i386v4.h deleted file mode 100644 index 5ee3bf87c7..0000000000 --- a/gnu/usr.bin/gcc1/cc1/config/tm-i386v4.h +++ /dev/null @@ -1,52 +0,0 @@ -/* Definitions for Intel 386 running system Vr4. - Copyright (C) 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Written by James Van Artsdalen, Dell Computer Corporation. - james@bigtex.cactus.org */ - -#include "tm-i386v.h" - -#undef STANDARD_STARTFILE_PREFIX -#define STANDARD_STARTFILE_PREFIX "/usr/ccs/lib/" - -/* Use crt1.o as a startup file and crtn.o as a closing file. */ - -#undef STARTFILE_SPEC -#define STARTFILE_SPEC \ - "%{pg:gcrt1.o%s}%{!pg:%{p:mcrt1.o%s}%{!p:crt1.o%s}} %{pg:gcrti.o%s}%{!pg:%{p:mcrti.o%s}%{!p:crti.o%s}} values-Xt.o%s" - -#undef LIB_SPEC -#define LIB_SPEC "%{p:-L/usr/lib/libp}%{pg:-L/usr/lib/libp} -Y P,/usr/ccs/lib:/usr/lib -Qy -lc crtn.o%s" - -/* Brain-damaged v4 include files won't work right if __STDC__ != 0 */ - -#define STDC_VALUE 0 - -/* We do not want to output SDB debugging information. */ - -#undef SDB_DEBUGGING_INFO - -#undef ASM_FILE_START -#define ASM_FILE_START(FILE) \ - do { \ - char *p = (char *) strrchr(main_input_filename, '/'); \ - if (!p++) \ - p = main_input_filename; \ - fprintf ((FILE), "\t.file\t\"%s\"\n", p); \ - } while (0) diff --git a/gnu/usr.bin/gcc1/cc1/config/tm-i386vgas.h b/gnu/usr.bin/gcc1/cc1/config/tm-i386vgas.h deleted file mode 100644 index d8e69ffaac..0000000000 --- a/gnu/usr.bin/gcc1/cc1/config/tm-i386vgas.h +++ /dev/null @@ -1,121 +0,0 @@ -/* Definitions for Intel 386 running system V with gnu tools - Copyright (C) 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "tm-i386.h" -/* Use the bsd assembler syntax. */ -/* we need to do this because gas is really a bsd style assembler, - * and so doesn't work well this these att-isms: - * - * ASM_OUTPUT_SKIP is .set .,.+N, which isn't implemented in gas - * ASM_OUTPUT_LOCAL is done with .set .,.+N, but that can't be - * used to define bss static space - * - * Next is the question of whether to uses underscores. RMS didn't - * like this idea at first, but since it is now obvious that we - * need this separate tm file for use with gas, at least to get - * dbx debugging info. */ - -#include "tm-bsd386.h" - -/* these come from tm-bsd386.h, but are specific to sequent */ -#undef DBX_NO_XREFS -#undef DBX_CONTIN_LENGTH - -/* By default, target has a 80387. */ - -#define TARGET_DEFAULT 1 - -#if 0 /* These aren't right for GNU ld. */ -/* Use crt1.o as a startup file and crtn.o as a closing file. */ - -#define STARTFILE_SPEC \ - "%{pg:gcrt1.o%s}%{!pg:%{p:mcrt1.o%s}%{!p:crt1.o%s}}" - -#define LIB_SPEC "%{p:-L/usr/lib/libp}%{pg:-L/usr/lib/libp} -lc crtn.o%s" -#endif - -/* Specify predefined symbols in preprocessor. */ - -#define CPP_PREDEFINES "-Dunix -Di386" - -/* Allow #sccs in preprocessor. */ - -#define SCCS_DIRECTIVE - -/* Output #ident as a .ident. */ - -#define ASM_OUTPUT_IDENT(FILE, NAME) fprintf (FILE, "\t.ident \"%s\"\n", NAME); - -/* We do not want to output SDB debugging information. */ - -#undef SDB_DEBUGGING_INFO - -/* We want to output DBX debugging information. */ - -#define DBX_DEBUGGING_INFO - -/* Implicit library calls should use memcpy, not bcopy, etc. */ - -#define TARGET_MEM_FUNCTIONS - -/* Writing `int' for a bitfield forces int alignment for the structure. */ - -#define PCC_BITFIELD_TYPE_MATTERS 1 - -#undef ASM_FILE_START -#define ASM_FILE_START(FILE) \ - fprintf (FILE, "\t.file\t\"%s\"\n", dump_base_name); - -/* This is how to output a reference to a user-level label named NAME. */ -#undef ASM_OUTPUT_LABELREF -#define ASM_OUTPUT_LABELREF(FILE,NAME) \ - fprintf (FILE, "_%s", NAME) - -#if 0 /* People say gas uses the log as the arg to .align. */ -/* When using gas, .align N aligns to an N-byte boundary. */ - -#undef ASM_OUTPUT_ALIGN -#define ASM_OUTPUT_ALIGN(FILE,LOG) \ - if ((LOG)!=0) fprintf ((FILE), "\t.align %d\n", 1<<(LOG)) -#endif - -/* Align labels, etc. at 4-byte boundaries. */ - -#define ASM_OUTPUT_ALIGN_CODE(FILE) \ - fprintf ((FILE), "\t.align 2\n"); /* Use log of 4 as arg. */ - -#if 0 -#define ASM_OUTPUT_ALIGN_CODE(FILE) \ - fprintf ((FILE), "\t.align 4\n"); -#endif - -/* Machines that use the AT&T assembler syntax - also return floating point values in an FP register. */ -/* Define how to find the value returned by a function. - VALTYPE is the data type of the value (as a tree). - If the precise function being called is known, FUNC is its FUNCTION_DECL; - otherwise, FUNC is 0. */ - -#define VALUE_REGNO(MODE) \ - (((MODE)==SFmode || (MODE)==DFmode) ? FIRST_FLOAT_REG : 0) - -/* 1 if N is a possible register number for a function value. */ - -#define FUNCTION_VALUE_REGNO_P(N) ((N) == 0 || (N)== FIRST_FLOAT_REG) diff --git a/gnu/usr.bin/gcc1/cc1/config/xm-i386.h b/gnu/usr.bin/gcc1/cc1/config/xm-i386.h deleted file mode 100644 index e883f47b5e..0000000000 --- a/gnu/usr.bin/gcc1/cc1/config/xm-i386.h +++ /dev/null @@ -1,42 +0,0 @@ -/* Configuration for GNU C-compiler for Intel 80386 running Sequent Symmetry - Copyright (C) 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* #defines that need visibility everywhere. */ -#define FALSE 0 -#define TRUE 1 - -/* This describes the machine the compiler is hosted on. */ -#define HOST_BITS_PER_CHAR 8 -#define HOST_BITS_PER_SHORT 16 -#define HOST_BITS_PER_INT 32 -#define HOST_BITS_PER_LONG 32 - -/* Arguments to use with `exit'. */ -#define SUCCESS_EXIT_CODE 0 -#define FATAL_EXIT_CODE 33 - -/* target machine dependencies. - tm.h is a symbolic link to the actual target specific file. */ - -#include "tm.h" - -#ifdef __GNUC__ -#define alloca __builtin_alloca -#endif diff --git a/gnu/usr.bin/gcc1/cc1/config/xm-i386v.h b/gnu/usr.bin/gcc1/cc1/config/xm-i386v.h deleted file mode 100644 index 60d4ce9ae5..0000000000 --- a/gnu/usr.bin/gcc1/cc1/config/xm-i386v.h +++ /dev/null @@ -1,47 +0,0 @@ -/* Configuration for GNU C-compiler for Intel 80386 running System V. - Copyright (C) 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* #defines that need visibility everywhere. */ -#define FALSE 0 -#define TRUE 1 - -/* This describes the machine the compiler is hosted on. */ -#define HOST_BITS_PER_CHAR 8 -#define HOST_BITS_PER_SHORT 16 -#define HOST_BITS_PER_INT 32 -#define HOST_BITS_PER_LONG 32 - -/* Arguments to use with `exit'. */ -#define SUCCESS_EXIT_CODE 0 -#define FATAL_EXIT_CODE 33 - -#define USG - -/* target machine dependencies. - tm.h is a symbolic link to the actual target specific file. */ -#include "tm.h" - -#define bcopy(a,b,c) memcpy (b,a,c) -#define bzero(a,b) memset (a,0,b) -#define bcmp(a,b,c) memcmp (a,b,c) - -#ifdef __GNUC__ -#define alloca(n) __builtin_alloca(n) -#endif diff --git a/gnu/usr.bin/gcc1/cc1/cse.c b/gnu/usr.bin/gcc1/cc1/cse.c deleted file mode 100644 index dc6f19f306..0000000000 --- a/gnu/usr.bin/gcc1/cc1/cse.c +++ /dev/null @@ -1,3920 +0,0 @@ -/* Common subexpression elimination for GNU compiler. - Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "config.h" -#include "rtl.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "flags.h" -#include "real.h" - -#include - -/* The basic idea of common subexpression elimination is to go - through the code, keeping a record of expressions that would - have the same value at the current scan point, and replacing - expressions encountered with the cheapest equivalent expression. - - It is too complicated to keep track of the different possibilities - when control paths merge; so, at each label, we forget all that is - known and start fresh. This can be described as processing each - basic block separately. Note, however, that these are not quite - the same as the basic blocks found by a later pass and used for - data flow analysis and register packing. We do not need to start fresh - after a conditional jump instruction if there is no label there. - - We use two data structures to record the equivalent expressions: - a hash table for most expressions, and several vectors together - with "quantity numbers" to record equivalent (pseudo) registers. - - The use of the special data structure for registers is desirable - because it is faster. It is possible because registers references - contain a fairly small number, the register number, taken from - a contiguously allocated series, and two register references are - identical if they have the same number. General expressions - do not have any such thing, so the only way to retrieve the - information recorded on an expression other than a register - is to keep it in a hash table. - -Registers and "quantity numbers": - - At the start of each basic block, all of the (hardware and pseudo) - registers used in the function are given distinct quantity - numbers to indicate their contents. During scan, when the code - copies one register into another, we copy the quantity number. - When a register is loaded in any other way, we allocate a new - quantity number to describe the value generated by this operation. - `reg_qty' records what quantity a register is currently thought - of as containing. - - We also maintain a bidirectional chain of registers for each - quantity number. `qty_first_reg', `qty_last_reg', - `reg_next_eqv' and `reg_prev_eqv' hold these chains. - - The first register in a chain is the one whose lifespan is least local. - Among equals, it is the one that was seen first. - We replace any equivalent register with that one. - -Constants and quantity numbers - - When a quantity has a known constant value, that value is stored - in the appropriate element of qty_const. This is in addition to - putting the constant in the hash table as is usual for non-regs. - - Regs are preferred to constants as they are to everything else, - but expressions containing constants can be simplified, by fold_rtx. - - When a quantity has a known nearly constant value (such as an address - of a stack slot), that value is stored in the appropriate element - of qty_const. - - Integer constants don't have a machine mode. However, cse - determines the intended machine mode from the destination - of the instruction that moves the constant. The machine mode - is recorded in the hash table along with the actual RTL - constant expression so that different modes are kept separate. - -Other expressions: - - To record known equivalences among expressions in general - we use a hash table called `table'. It has a fixed number of buckets - that contain chains of `struct table_elt' elements for expressions. - These chains connect the elements whose expressions have the same - hash codes. - - Other chains through the same elements connect the elements which - currently have equivalent values. - - Register references in an expression are canonicalized before hashing - the expression. This is done using `reg_qty' and `qty_first_reg'. - The hash code of a register reference is computed using the quantity - number, not the register number. - - When the value of an expression changes, it is necessary to remove from the - hash table not just that expression but all expressions whose values - could be different as a result. - - 1. If the value changing is in memory, except in special cases - ANYTHING referring to memory could be changed. That is because - nobody knows where a pointer does not point. - The function `invalidate_memory' removes what is necessary. - - The special cases are when the address is constant or is - a constant plus a fixed register such as the frame pointer - or a static chain pointer. When such addresses are stored in, - we can tell exactly which other such addresses must be invalidated - due to overlap. `invalidate' does this. - All expressions that refer to non-constant - memory addresses are also invalidated. `invalidate_memory' does this. - - 2. If the value changing is a register, all expressions - containing references to that register, and only those, - must be removed. - - Because searching the entire hash table for expressions that contain - a register is very slow, we try to figure out when it isn't necessary. - Precisely, this is necessary only when expressions have been - entered in the hash table using this register, and then the value has - changed, and then another expression wants to be added to refer to - the register's new value. This sequence of circumstances is rare - within any one basic block. - - The vectors `reg_tick' and `reg_in_table' are used to detect this case. - reg_tick[i] is incremented whenever a value is stored in register i. - reg_in_table[i] holds -1 if no references to register i have been - entered in the table; otherwise, it contains the value reg_tick[i] had - when the references were entered. If we want to enter a reference - and reg_in_table[i] != reg_tick[i], we must scan and remove old references. - Until we want to enter a new entry, the mere fact that the two vectors - don't match makes the entries be ignored if anyone tries to match them. - - Registers themselves are entered in the hash table as well as in - the equivalent-register chains. However, the vectors `reg_tick' - and `reg_in_table' do not apply to expressions which are simple - register references. These expressions are removed from the table - immediately when they become invalid, and this can be done even if - we do not immediately search for all the expressions that refer to - the register. - - A CLOBBER rtx in an instruction invalidates its operand for further - reuse. A CLOBBER or SET rtx whose operand is a MEM:BLK - invalidates everything that resides in memory. - -Related expressions: - - Constant expressions that differ only by an additive integer - are called related. When a constant expression is put in - the table, the related expression with no constant term - is also entered. These are made to point at each other - so that it is possible to find out if there exists any - register equivalent to an expression related to a given expression. */ - -/* One plus largest register number used in this function. */ - -static int max_reg; - -/* Length of vectors indexed by quantity number. - We know in advance we will not need a quantity number this big. */ - -static int max_qty; - -/* Next quantity number to be allocated. - This is 1 + the largest number needed so far. */ - -static int next_qty; - -/* Indexed by quantity number, gives the first (or last) (pseudo) register - in the chain of registers that currently contain this quantity. */ - -static int *qty_first_reg; -static int *qty_last_reg; - -/* Indexed by quantity number, gives the rtx of the constant value of the - quantity, or zero if it does not have a known value. - A sum of the frame pointer (or arg pointer) plus a constant - can also be entered here. */ - -static rtx *qty_const; - -/* Indexed by qty number, gives the insn that stored the constant value - recorded in `qty_const'. */ - -static rtx *qty_const_insn; - -/* Value stored in CC0 by previous insn: - 0 if previous insn didn't store in CC0. - else 0100 + (M&7)<<3 + (N&7) - where M is 1, 0 or -1 if result was >, == or < as signed number - and N is 1, 0 or -1 if result was >, == or < as unsigned number. - 0200 bit may also be set, meaning that only == and != comparisons - have known results. */ - -static int prev_insn_cc0; - -/* For machines where CC0 is one bit, we may see CC0 assigned a - constant value (after fold_rtx). - Record here the value stored in the previous insn (0 if none). */ - -static rtx prev_insn_explicit_cc0; - -/* Previous actual insn. 0 if at first insn of basic block. */ - -static rtx prev_insn; - -/* Insn being scanned. */ - -static rtx this_insn; - -/* Index by (pseudo) register number, gives the quantity number - of the register's current contents. */ - -static int *reg_qty; - -/* Index by (pseudo) register number, gives the number of the next - (pseudo) register in the chain of registers sharing the same value. - Or -1 if this register is at the end of the chain. */ - -static int *reg_next_eqv; - -/* Index by (pseudo) register number, gives the number of the previous - (pseudo) register in the chain of registers sharing the same value. - Or -1 if this register is at the beginning of the chain. */ - -static int *reg_prev_eqv; - -/* Index by (pseudo) register number, gives the latest rtx - to use to insert a ref to that register. */ - -static rtx *reg_rtx; - -/* Index by (pseudo) register number, gives the number of times - that register has been altered in the current basic block. */ - -static int *reg_tick; - -/* Index by (pseudo) register number, gives the reg_tick value at which - rtx's containing this register are valid in the hash table. - If this does not equal the current reg_tick value, such expressions - existing in the hash table are invalid. - If this is -1, no expressions containing this register have been - entered in the table. */ - -static int *reg_in_table; - -/* Two vectors of max_reg ints: - one containing all -1's; in the other, element i contains i. - These are used to initialize various other vectors fast. */ - -static int *all_minus_one; -static int *consec_ints; - -/* Set nonzero in cse_insn to tell cse_basic_block to skip immediately - to the next basic block and treat it as a continuation of this one. */ - -static int cse_skip_to_next_block; - -/* CUID of insn that starts the basic block currently being cse-processed. */ - -static int cse_basic_block_start; - -/* CUID of insn that ends the basic block currently being cse-processed. */ - -static int cse_basic_block_end; - -/* Vector mapping INSN_UIDs to cuids. - The cuids are like uids but increase monononically always. - We use them to see whether a reg is used outside a given basic block. */ - -static short *uid_cuid; - -/* Get the cuid of an insn. */ - -#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)]) - -/* Nonzero if cse has altered conditional jump insns - in such a way that jump optimization should be redone. */ - -static int cse_jumps_altered; - -/* canon_hash stores 1 in do_not_record - if it notices a reference to CC0, CC1 or PC. */ - -static int do_not_record; - -/* canon_hash stores 1 in hash_arg_in_memory - if it notices a reference to memory within the expression being hashed. */ - -static int hash_arg_in_memory; - -/* canon_hash stores 1 in hash_arg_in_struct - if it notices a reference to memory that's part of a structure. */ - -static int hash_arg_in_struct; - -/* The hash table contains buckets which are chains of `struct table_elt's, - each recording one expression's information. - That expression is in the `exp' field. - - Those elements with the same hash code are chained in both directions - through the `next_same_hash' and `prev_same_hash' fields. - - Each set of expressions with equivalent values - are on a two-way chain through the `next_same_value' - and `prev_same_value' fields, and all point with - the `first_same_value' field at the first element in - that chain. The chain is in order of increasing cost. - Each element's cost value is in its `cost' field. - - The `in_memory' field is nonzero for elements that - involve any reference to memory. These elements are removed - whenever a write is done to an unidentified location in memory. - To be safe, we assume that a memory address is unidentified unless - the address is either a symbol constant or a constant plus - the frame pointer or argument pointer. - - The `in_struct' field is nonzero for elements that - involve any reference to memory inside a structure or array. - - The `equivalence_only' field means that this expression came from a - REG_EQUIV or REG_EQUAL note; it is not valid for substitution into an insn. - - The `related_value' field is used to connect related expressions - (that differ by adding an integer). - The related expressions are chained in a circular fashion. - `related_value' is zero for expressions for which this - chain is not useful. - - The `mode' field is usually the same as GET_MODE (`exp'), but - if `exp' is a CONST_INT and has no machine mode then the `mode' - field is the mode it was being used as. Each constant is - recorded separately for each mode it is used with. */ - - -struct table_elt -{ - rtx exp; - struct table_elt *next_same_hash; - struct table_elt *prev_same_hash; - struct table_elt *next_same_value; - struct table_elt *prev_same_value; - struct table_elt *first_same_value; - struct table_elt *related_value; - int cost; - enum machine_mode mode; - char in_memory; - char in_struct; - char equivalence_only; -}; - -#define HASH(x, m) (canon_hash (x, m) % NBUCKETS) -/* We don't want a lot of buckets, because we rarely have very many - things stored in the hash table, and a lot of buckets slows - down a lot of loops that happen frequently. */ -#define NBUCKETS 31 - -static struct table_elt *table[NBUCKETS]; - -/* Chain of `struct table_elt's made so far for this function - but currently removed from the table. */ - -static struct table_elt *free_element_chain; - -/* Number of `struct table_elt' structures made so far for this function. */ - -static int n_elements_made; - -/* Maximum value `n_elements_made' has had so far in this compilation - for functions previously processed. */ - -static int max_elements_made; - -/* Bits describing what kind of values in memory must be invalidated - for a particular instruction. If all three bits are zero, - no memory refs need to be invalidated. Each bit is more powerful - than the preceding ones, and if a bit is set then the preceding - bits are also set. - - Here is how the bits are set. - Writing at a fixed address invalidates only variable addresses, - writing in a structure element at variable address - invalidates all but scalar variables, - and writing in anything else at variable address invalidates everything. */ - -struct write_data -{ - int var : 1; /* Invalidate variable addresses. */ - int nonscalar : 1; /* Invalidate all but scalar variables. */ - int all : 1; /* Invalidate all memory refs. */ -}; - -/* Nonzero if X has the form (PLUS frame-pointer integer). */ - -#define FIXED_BASE_PLUS_P(X) \ - (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \ - && (XEXP (X, 0) == frame_pointer_rtx || XEXP (X, 0) == arg_pointer_rtx)) - -static struct table_elt *lookup (); -static void free_element (); - -static void remove_invalid_refs (); -static int exp_equiv_p (); -int refers_to_p (); -int refers_to_mem_p (); -static void invalidate_from_clobbers (); -static int safe_hash (); -static int canon_hash (); -static rtx equiv_constant (); -static int get_integer_term (); -static rtx get_related_value (); -static void note_mem_written (); -static int cse_rtx_addr_varies_p (); -static int fold_cc0 (); - -/* Return an estimate of the cost of computing rtx X. - The only use of this is to compare the costs of two expressions - to decide whether to replace one with the other. */ - -static int -rtx_cost (x) - rtx x; -{ - register int i, j; - register enum rtx_code code; - register char *fmt; - register int total; - - if (x == 0) - return 0; - - code = GET_CODE (x); - switch (code) - { - case REG: - return 1; - case SUBREG: - return 2; - CONST_COSTS (x, code); - } - - total = 2; - if (code == MEM) - total = 2 * GET_MODE_SIZE (GET_MODE (x)) / UNITS_PER_WORD; - - /* Sum the costs of the sub-rtx's, plus 2 just put in. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - total += rtx_cost (XEXP (x, i)); - else if (fmt[i] == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - total += rtx_cost (XVECEXP (x, i, j)); - - return total; -} - -/* Clear the hash table and initialize each register with its own quantity, - for a new basic block. */ - -static void -new_basic_block () -{ - register int i; - register int vecsize = max_reg * sizeof (rtx); - next_qty = max_reg; - - bzero (reg_rtx, vecsize); - bzero (reg_tick, vecsize); - - bcopy (all_minus_one, reg_in_table, vecsize); - bcopy (all_minus_one, reg_next_eqv, vecsize); - bcopy (all_minus_one, reg_prev_eqv, vecsize); - bcopy (consec_ints, reg_qty, vecsize); - - for (i = 0; i < max_qty; i++) - { - qty_first_reg[i] = i; - qty_last_reg[i] = i; - qty_const[i] = 0; - qty_const_insn[i] = 0; - } - - for (i = 0; i < NBUCKETS; i++) - { - register struct table_elt *this, *next; - for (this = table[i]; this; this = next) - { - next = this->next_same_hash; - free_element (this); - } - } - - bzero (table, sizeof table); - - prev_insn_cc0 = 0; - prev_insn_explicit_cc0 = 0; - prev_insn = 0; -} - -/* Say that register REG contains a quantity not in any register before. */ - -static void -make_new_qty (reg) - register int reg; -{ - register int q; - - q = reg_qty[reg] = next_qty++; - qty_first_reg[q] = reg; - qty_last_reg[q] = reg; -} - -/* Make reg NEW equivalent to reg OLD. - OLD is not changing; NEW is. */ - -static void -make_regs_eqv (new, old) - register int new, old; -{ - register int lastr, firstr; - register int q = reg_qty[old]; - - /* Nothing should become eqv until it has a "non-invalid" qty number. */ - if (q == old) - abort (); - - reg_qty[new] = q; - firstr = qty_first_reg[q]; - lastr = qty_last_reg[q]; - - /* Prefer pseudo regs to hard regs with the same value. - Among pseudos, if NEW will live longer than any other reg of the same qty, - and that is beyond the current basic block, - make it the new canonical replacement for this qty. */ - if (new >= FIRST_PSEUDO_REGISTER - && (firstr < FIRST_PSEUDO_REGISTER - || ((uid_cuid[regno_last_uid[new]] > cse_basic_block_end - || uid_cuid[regno_first_uid[new]] < cse_basic_block_start) - && (uid_cuid[regno_last_uid[new]] - > uid_cuid[regno_last_uid[firstr]])))) - { - reg_prev_eqv[firstr] = new; - reg_next_eqv[new] = firstr; - reg_prev_eqv[new] = -1; - qty_first_reg[q] = new; - } - else - { - /* If NEW is a hard reg, insert at end. - Otherwise, insert before any hard regs that are at the end. */ - while (lastr < FIRST_PSEUDO_REGISTER && new >= FIRST_PSEUDO_REGISTER) - lastr = reg_prev_eqv[lastr]; - reg_next_eqv[new] = reg_next_eqv[lastr]; - if (reg_next_eqv[lastr] >= 0) - reg_prev_eqv[reg_next_eqv[lastr]] = new; - else - qty_last_reg[q] = new; - reg_next_eqv[lastr] = new; - reg_prev_eqv[new] = lastr; - } -} - -/* Discard the records of what is in register REG. */ - -static void -reg_invalidate (reg) - register int reg; -{ - register int n = reg_next_eqv[reg]; - register int p = reg_prev_eqv[reg]; - register int q = reg_qty[reg]; - - reg_tick[reg]++; - - if (q == reg) - { - /* Save time if already invalid */ - /* It shouldn't be linked to anything if it's invalid. */ - if (reg_prev_eqv[q] != -1) - abort (); - if (reg_next_eqv[q] != -1) - abort (); - return; - } - - if (n != -1) - reg_prev_eqv[n] = p; - else - qty_last_reg[q] = p; - if (p != -1) - reg_next_eqv[p] = n; - else - qty_first_reg[q] = n; - - reg_qty[reg] = reg; - qty_first_reg[reg] = reg; - qty_last_reg[reg] = reg; - reg_next_eqv[reg] = -1; - reg_prev_eqv[reg] = -1; -} - -/* Remove any invalid expressions from the hash table - that refer to any of the registers contained in expression X. - - Make sure that newly inserted references to those registers - as subexpressions will be considered valid. - - mention_regs is not called when a register itself - is being stored in the table. */ - -static void -mention_regs (x) - rtx x; -{ - register enum rtx_code code; - register int i, j; - register char *fmt; - - if (x == 0) - return; - - code = GET_CODE (x); - if (code == REG) - { - register int regno = REGNO (x); - reg_rtx[regno] = x; - - if (reg_in_table[regno] >= 0 && reg_in_table[regno] != reg_tick[regno]) - remove_invalid_refs (regno); - - reg_in_table[regno] = reg_tick[regno]; - - return; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - mention_regs (XEXP (x, i)); - else if (fmt[i] == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - mention_regs (XVECEXP (x, i, j)); -} - -/* Update the register quantities for inserting X into the hash table - with a value equivalent to CLASSP. - (If CLASSP is not a REG or a SUBREG, it is irrelevant.) - If MODIFIED is nonzero, X is a destination; it is being modified. - Note that reg_invalidate should be called on a register - before insert_regs is done on that register with MODIFIED != 0. - - Nonzero value means that elements of reg_qty have changed - so X's hash code may be different. */ - -static int -insert_regs (x, classp, modified) - rtx x; - struct table_elt *classp; - int modified; -{ - if (GET_CODE (x) == REG) - { - register int regno = REGNO (x); - reg_rtx[regno] = x; - if (modified || reg_qty[regno] == regno) - { - if (classp && GET_CODE (classp->exp) == REG) - { - make_regs_eqv (regno, REGNO (classp->exp)); - /* Make sure reg_rtx is set up even for regs - not explicitly set (such as function value). */ - reg_rtx[REGNO (classp->exp)] = classp->exp; - } - else - make_new_qty (regno); - return 1; - } - } - /* Copying a subreg into a subreg makes the regs equivalent, - but only if the entire regs' mode is within one word. - Copying one reg of a DImode into one reg of another DImode - does not make them equivalent. */ - else if (GET_CODE (x) == SUBREG - && GET_CODE (SUBREG_REG (x)) == REG - && GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) <= UNITS_PER_WORD - && (modified - || reg_qty[REGNO (SUBREG_REG (x))] == REGNO (SUBREG_REG (x)))) - { - if (classp && GET_CODE (classp->exp) == SUBREG - && GET_CODE (SUBREG_REG (classp->exp)) == REG - && GET_MODE (SUBREG_REG (classp->exp)) == GET_MODE (SUBREG_REG (x))) - { - int oregno = REGNO (SUBREG_REG (classp->exp)); - make_regs_eqv (REGNO (SUBREG_REG (x)), oregno); - /* Make sure reg_rtx is set up even for regs - not explicitly set (such as function value). */ - reg_rtx[oregno] = SUBREG_REG (classp->exp); - } - else - make_new_qty (REGNO (SUBREG_REG (x))); - return 1; - } - else - mention_regs (x); - return 0; -} - -/* Look in or update the hash table. */ - -/* Put the element ELT on the list of free elements. */ - -static void -free_element (elt) - struct table_elt *elt; -{ - elt->next_same_hash = free_element_chain; - free_element_chain = elt; -} - -/* Return an element that is free for use. */ - -static struct table_elt * -get_element () -{ - struct table_elt *elt = free_element_chain; - if (elt) - { - free_element_chain = elt->next_same_hash; - return elt; - } - n_elements_made++; - return (struct table_elt *) oballoc (sizeof (struct table_elt)); -} - -/* Remove table element ELT from use in the table. - HASH is its hash code, made using the HASH macro. - It's an argument because often that is known in advance - and we save much time not recomputing it. */ - -static void -remove (elt, hash) - register struct table_elt *elt; - int hash; -{ - if (elt == 0) - return; - - /* Mark this element as removed. See cse_insn. */ - elt->first_same_value = 0; - - /* Remove the table element from its equivalence class. */ - - { - register struct table_elt *prev = elt->prev_same_value; - register struct table_elt *next = elt->next_same_value; - - if (next) next->prev_same_value = prev; - - if (prev) - prev->next_same_value = next; - else - { - register struct table_elt *newfirst = next; - while (next) - { - next->first_same_value = newfirst; - next = next->next_same_value; - } - } - } - - /* Remove the table element from its hash bucket. */ - - { - register struct table_elt *prev = elt->prev_same_hash; - register struct table_elt *next = elt->next_same_hash; - - if (next) next->prev_same_hash = prev; - - if (prev) - prev->next_same_hash = next; - else - table[hash] = next; - } - - /* Remove the table element from its related-value circular chain. */ - - if (elt->related_value != 0 && elt->related_value != elt) - { - register struct table_elt *p = elt->related_value; - while (p->related_value != elt) - p = p->related_value; - p->related_value = elt->related_value; - if (p->related_value == p) - p->related_value = 0; - } - - free_element (elt); -} - -/* Look up X in the hash table and return its table element, - or 0 if X is not in the table. - - MODE is the machine-mode of X, or if X is an integer constant - with VOIDmode then MODE is the mode with which X will be used. - - Here we are satisfied to find an expression whose tree structure - looks like X. */ - -static struct table_elt * -lookup (x, hash, mode) - rtx x; - int hash; - enum machine_mode mode; -{ - register struct table_elt *p; - - for (p = table[hash]; p; p = p->next_same_hash) - if (mode == p->mode && (x == p->exp || exp_equiv_p (x, p->exp, 1))) - return p; - - return 0; -} - -/* Like `lookup' but don't care whether the table element uses invalid regs. - Also ignore discrepancies in the machine mode of a register. */ - -static struct table_elt * -lookup_for_remove (x, hash, mode) - rtx x; - int hash; - enum machine_mode mode; -{ - register struct table_elt *p; - - if (GET_CODE (x) == REG) - { - int regno = REGNO (x); - /* Don't check the machine mode when comparing registers; - invalidating (REG:SI 0) also invalidates (REG:DF 0). */ - for (p = table[hash]; p; p = p->next_same_hash) - if (GET_CODE (p->exp) == REG - && REGNO (p->exp) == regno) - return p; - } - else - { - for (p = table[hash]; p; p = p->next_same_hash) - if (mode == p->mode && (x == p->exp || exp_equiv_p (x, p->exp, 0))) - return p; - } - - return 0; -} - -/* Look for an expression equivalent to X and with code CODE. - If one is found, return that expression. */ - -static rtx -lookup_as_function (x, code) - rtx x; - enum rtx_code code; -{ - register struct table_elt *p = lookup (x, safe_hash (x, 0) % NBUCKETS, - GET_MODE (x)); - if (p == 0) - return 0; - - for (p = p->first_same_value; p; p = p->next_same_value) - { - if (GET_CODE (p->exp) == code - /* Make sure this is a valid entry in the table. */ - && (exp_equiv_p (XEXP (p->exp, 0), XEXP (p->exp, 0), 1))) - return p->exp; - } - - return 0; -} - -/* Insert X in the hash table, assuming HASH is its hash code - and CLASSP is the current first element of the class it should go in - (or 0 if a new class should be made). - It is inserted at the proper position to keep the class in - the order cheapest first. - - MODE is the machine-mode of X, or if X is an integer constant - with VOIDmode then MODE is the mode with which X will be used. - - For elements of equal cheapness, the most recent one - goes in front, except that the first element in the list - remains first unless a cheaper element is added. - - The in_memory field in the hash table element is set to 0. - The caller must set it nonzero if appropriate. - - You should call insert_regs (X, CLASSP, MODIFY) before calling here, - and if insert_regs returns a nonzero value - you must then recompute its hash code before calling here. - - If necessary, update table showing constant values of quantities. */ - -#define CHEAPER(X,Y) \ - (((X)->cost < (Y)->cost) || \ - ((X)->cost == (Y)->cost \ - && GET_CODE ((X)->exp) == REG && GET_CODE ((Y)->exp) == REG \ - && (uid_cuid[regno_last_uid[REGNO ((X)->exp)]] > cse_basic_block_end \ - || uid_cuid[regno_first_uid[REGNO ((X)->exp)]] < cse_basic_block_start) \ - && (uid_cuid[regno_last_uid[REGNO ((X)->exp)]] \ - > uid_cuid[regno_last_uid[REGNO ((Y)->exp)]]))) - -static struct table_elt * -insert (x, classp, hash, mode) - register rtx x; - register struct table_elt *classp; - int hash; - enum machine_mode mode; -{ - register struct table_elt *elt; - - /* Put an element for X into the right hash bucket. */ - - elt = get_element (); - elt->exp = x; - elt->cost = rtx_cost (x) * 2; - /* Make pseudo regs a little cheaper than hard regs. */ - if (GET_CODE (x) == REG && REGNO (x) >= FIRST_PSEUDO_REGISTER) - elt->cost -= 1; - elt->next_same_value = 0; - elt->prev_same_value = 0; - elt->next_same_hash = table[hash]; - elt->prev_same_hash = 0; - elt->related_value = 0; - elt->in_memory = 0; - elt->equivalence_only = 0; - elt->mode = mode; - if (table[hash]) - table[hash]->prev_same_hash = elt; - table[hash] = elt; - - /* Put it into the proper value-class. */ - if (classp) - { - if (CHEAPER (elt, classp)) - /** Insert at the head of the class */ - { - register struct table_elt *p; - elt->next_same_value = classp; - classp->prev_same_value = elt; - elt->first_same_value = elt; - - for (p = classp; p; p = p->next_same_value) - p->first_same_value = elt; - } - else - { - /* Insert not at head of the class. */ - /* Put it after the last element cheaper than X. */ - register struct table_elt *p, *next; - for (p = classp; (next = p->next_same_value) && CHEAPER (next, elt); - p = next); - /* Put it after P and before NEXT. */ - elt->next_same_value = next; - if (next) - next->prev_same_value = elt; - elt->prev_same_value = p; - p->next_same_value = elt; - elt->first_same_value = classp; - } - } - else - elt->first_same_value = elt; - - if ((CONSTANT_P (x) || GET_CODE (x) == CONST_DOUBLE || FIXED_BASE_PLUS_P (x)) - && GET_CODE (elt->first_same_value->exp) == REG) - { - qty_const[reg_qty[REGNO (elt->first_same_value->exp)]] = x; - qty_const_insn[reg_qty[REGNO (elt->first_same_value->exp)]] = this_insn; - } - - if (GET_CODE (x) == REG) - { - if (elt->next_same_value != 0 - && (CONSTANT_P (elt->next_same_value->exp) - || GET_CODE (elt->next_same_value->exp) == CONST_DOUBLE - || FIXED_BASE_PLUS_P (elt->next_same_value->exp))) - { - qty_const[reg_qty[REGNO (x)]] = elt->next_same_value->exp; - qty_const_insn[reg_qty[REGNO (x)]] = this_insn; - } - if (CONSTANT_P (elt->first_same_value->exp) - || GET_CODE (elt->first_same_value->exp) == CONST_DOUBLE - || FIXED_BASE_PLUS_P (elt->first_same_value->exp)) - { - qty_const[reg_qty[REGNO (x)]] = elt->first_same_value->exp; - qty_const_insn[reg_qty[REGNO (x)]] = this_insn; - } - } - - /* If this is a constant with symbolic value, - and it has a term with an explicit integer value, - link it up with related expressions. */ - if (GET_CODE (x) == CONST) - { - rtx subexp = get_related_value (x); - int subhash; - struct table_elt *subelt, *subelt_prev; - - if (subexp != 0) - { - /* Get the integer-free subexpression in the hash table. */ - subhash = safe_hash (subexp, mode) % NBUCKETS; - subelt = lookup (subexp, subhash, mode); - if (subelt == 0) - subelt = insert (subexp, 0, subhash, mode); - /* Initialize SUBELT's circular chain if it has none. */ - if (subelt->related_value == 0) - subelt->related_value = subelt; - /* Find the element in the circular chain that precedes SUBELT. */ - subelt_prev = subelt; - while (subelt_prev->related_value != subelt) - subelt_prev = subelt_prev->related_value; - /* Put new ELT into SUBELT's circular chain just before SUBELT. - This way the element that follows SUBELT is the oldest one. */ - elt->related_value = subelt_prev->related_value; - subelt_prev->related_value = elt; - } - } - - return elt; -} - -/* Remove from the hash table, or mark as invalid, - all expressions whose values could be altered by storing in X. - X is a register, a subreg, or a memory reference with nonvarying address - (because, when a memory reference with a varying address is stored in, - all memory references are removed by invalidate_memory - so specific invalidation is superfluous). - - A nonvarying address may be just a register or just - a symbol reference, or it may be either of those plus - a numeric offset. */ - -static void -invalidate (x) - rtx x; -{ - register int i; - register struct table_elt *p; - register rtx base; - register int start, end; - - /* If X is a register, dependencies on its contents - are recorded through the qty number mechanism. - Just change the qty number of the register, - mark it as invalid for expressions that refer to it, - and remove it itself. */ - - if (GET_CODE (x) == REG) - { - register int hash = HASH (x, 0); - reg_invalidate (REGNO (x)); - remove (lookup_for_remove (x, hash, GET_MODE (x)), hash); - return; - } - - if (GET_CODE (x) == SUBREG) - { - if (GET_CODE (SUBREG_REG (x)) != REG) - abort (); - invalidate (SUBREG_REG (x)); - return; - } - - /* X is not a register; it must be a memory reference with - a nonvarying address. Remove all hash table elements - that refer to overlapping pieces of memory. */ - - if (GET_CODE (x) != MEM) - abort (); - base = XEXP (x, 0); - start = 0; - - /* Registers with nonvarying addresses usually have constant equivalents; - but the frame pointer register is also possible. */ - if (GET_CODE (base) == REG - && qty_const[reg_qty[REGNO (base)]] != 0) - base = qty_const[reg_qty[REGNO (base)]]; - - if (GET_CODE (base) == CONST) - base = XEXP (base, 0); - if (GET_CODE (base) == PLUS - && GET_CODE (XEXP (base, 1)) == CONST_INT) - { - start = INTVAL (XEXP (base, 1)); - base = XEXP (base, 0); - } - - end = start + GET_MODE_SIZE (GET_MODE (x)); - for (i = 0; i < NBUCKETS; i++) - { - register struct table_elt *next; - for (p = table[i]; p; p = next) - { - next = p->next_same_hash; - if (refers_to_mem_p (p->exp, base, start, end)) - remove (p, i); - } - } -} - -/* Remove all expressions that refer to register REGNO, - since they are already invalid, and we are about to - mark that register valid again and don't want the old - expressions to reappear as valid. */ - -static void -remove_invalid_refs (regno) - int regno; -{ - register int i; - register struct table_elt *p, *next; - register rtx x = reg_rtx[regno]; - - for (i = 0; i < NBUCKETS; i++) - for (p = table[i]; p; p = next) - { - next = p->next_same_hash; - if (GET_CODE (p->exp) != REG && refers_to_p (p->exp, x)) - remove (p, i); - } -} - -/* Remove from the hash table all expressions that reference memory, - or some of them as specified by *WRITES. */ - -static void -invalidate_memory (writes) - struct write_data *writes; -{ - register int i; - register struct table_elt *p, *next; - int all = writes->all; - int nonscalar = writes->nonscalar; - - for (i = 0; i < NBUCKETS; i++) - for (p = table[i]; p; p = next) - { - next = p->next_same_hash; - if (p->in_memory - && (all - || (nonscalar && p->in_struct) - || cse_rtx_addr_varies_p (p->exp))) - remove (p, i); - } -} - -/* Return the value of the integer term in X, if one is apparent; - otherwise return 0. - We do not check extremely carefully for the presence of integer terms - but rather consider only the cases that `insert' notices - for the `related_value' field. */ - -static int -get_integer_term (x) - rtx x; -{ - if (GET_CODE (x) == CONST) - x = XEXP (x, 0); - - if (GET_CODE (x) == MINUS - && GET_CODE (XEXP (x, 1)) == CONST_INT) - return - INTVAL (XEXP (x, 1)); - if (GET_CODE (x) != PLUS) - return 0; - if (GET_CODE (XEXP (x, 0)) == CONST_INT) - return INTVAL (XEXP (x, 0)); - if (GET_CODE (XEXP (x, 1)) == CONST_INT) - return INTVAL (XEXP (x, 1)); - return 0; -} - -static rtx -get_related_value (x) - rtx x; -{ - if (GET_CODE (x) != CONST) - return 0; - x = XEXP (x, 0); - if (GET_CODE (x) == PLUS) - { - if (GET_CODE (XEXP (x, 0)) == CONST_INT) - return XEXP (x, 1); - if (GET_CODE (XEXP (x, 1)) == CONST_INT) - return XEXP (x, 0); - } - else if (GET_CODE (x) == MINUS - && GET_CODE (XEXP (x, 1)) == CONST_INT) - return XEXP (x, 0); - return 0; -} - -/* Given an expression X of type CONST, - and ELT which is its table entry (or 0 if it - is not in the hash table), - return an alternate expression for X as a register plus integer. - If none can be found or it would not be a valid address, return 0. */ - -static rtx -use_related_value (x, elt) - rtx x; - struct table_elt *elt; -{ - register struct table_elt *relt = 0; - register struct table_elt *p; - int offset; - rtx addr; - - /* First, is there anything related known? - If we have a table element, we can tell from that. - Otherwise, must look it up. */ - - if (elt != 0 && elt->related_value != 0) - relt = elt; - else if (elt == 0 && GET_CODE (x) == CONST) - { - rtx subexp = get_related_value (x); - if (subexp != 0) - relt = lookup (subexp, - safe_hash (subexp, GET_MODE (subexp)) % NBUCKETS, - GET_MODE (subexp)); - } - - if (relt == 0) - return 0; - - /* Search all related table entries for one that has an - equivalent register. */ - - p = relt; - while (1) - { - if (p->first_same_value != 0 - && GET_CODE (p->first_same_value->exp) == REG) - break; - p = p->related_value; - - /* We went all the way around, so there is nothing to be found. - Return failure. */ - if (p == relt) - return 0; - /* Perhaps RELT was in the table for some other reason and - it has no related values recorded. */ - if (p == 0) - return 0; - } - - /* Note: OFFSET may be 0 if P->xexp and X are related by commutativity. */ - offset = (get_integer_term (x) - get_integer_term (p->exp)); - addr = plus_constant (p->first_same_value->exp, offset); - if (memory_address_p (QImode, addr)) - return addr; - return 0; -} - -/* Hash an rtx. We are careful to make sure the value is never negative. - Equivalent registers hash identically. - MODE is used in hashing for CONST_INTs only; - otherwise the mode of X is used. - - Store 1 in do_not_record if any subexpression is volatile. - - Store 1 in hash_arg_in_memory if X contains a MEM rtx - which does not have the RTX_UNCHANGING_P bit set. - In this case, also store 1 in hash_arg_in_struct - if there is a MEM rtx which has the MEM_IN_STRUCT_P bit set. - - Note that cse_insn knows that the hash code of a MEM expression - is just (int) MEM plus the hash code of the address. - It also knows it can use HASHREG to get the hash code of (REG n). */ - -#define HASHBITS 16 - -#define HASHREG(RTX) \ - ((((int) REG << 7) + reg_qty[REGNO (RTX)]) % NBUCKETS) - -static int -canon_hash (x, mode) - rtx x; - enum machine_mode mode; -{ - register int i, j; - register int hash = 0; - register enum rtx_code code; - register char *fmt; - - /* repeat is used to turn tail-recursion into iteration. */ - repeat: - if (x == 0) - return hash; - - code = GET_CODE (x); - switch (code) - { - case REG: - { - /* We do not invalidate anything on pushing or popping - because they cannot change anything but the stack pointer; - but that means we must consider the stack pointer volatile - since it can be changed "mysteriously". */ - - register int regno = REGNO (x); - if (regno == STACK_POINTER_REGNUM - || (regno < FIRST_PSEUDO_REGISTER && global_regs[regno])) - { - do_not_record = 1; - return 0; - } - return hash + ((int) REG << 7) + reg_qty[regno]; - } - - case CONST_INT: - hash += ((int) mode + ((int) CONST_INT << 7) - + INTVAL (x) + (INTVAL (x) >> HASHBITS)); - return ((1 << HASHBITS) - 1) & hash; - - case CONST_DOUBLE: - /* This is like the general case, except that it only counts - the first two elements. */ - hash += (int) code + (int) GET_MODE (x); - { - int i; - for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++) - { - int tem = XINT (x, i); - hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS)); - } - } - return hash; - - /* Assume there is only one rtx object for any given label. */ - case LABEL_REF: - /* Use `and' to ensure a positive number. */ - return (hash + ((int) LABEL_REF << 7) - + ((int) XEXP (x, 0) & ((1 << HASHBITS) - 1))); - - case SYMBOL_REF: - return (hash + ((int) SYMBOL_REF << 7) - + ((int) XEXP (x, 0) & ((1 << HASHBITS) - 1))); - - case MEM: - if (MEM_VOLATILE_P (x)) - { - do_not_record = 1; - return 0; - } - if (! RTX_UNCHANGING_P (x)) - { - hash_arg_in_memory = 1; - if (MEM_IN_STRUCT_P (x)) hash_arg_in_struct = 1; - } - /* Now that we have already found this special case, - might as well speed it up as much as possible. */ - hash += (int) MEM; - x = XEXP (x, 0); - goto repeat; - - case PRE_DEC: - case PRE_INC: - case POST_DEC: - case POST_INC: - case PC: - case CC0: - case CALL: - do_not_record = 1; - return 0; - - case ASM_OPERANDS: - if (MEM_VOLATILE_P (x)) - { - do_not_record = 1; - return 0; - } - } - - i = GET_RTX_LENGTH (code) - 1; - hash += (int) code + (int) GET_MODE (x); - fmt = GET_RTX_FORMAT (code); - for (; i >= 0; i--) - { - if (fmt[i] == 'e') - { - /* If we are about to do the last recursive call - needed at this level, change it into iteration. - This function is called enough to be worth it. */ - if (i == 0) - { - x = XEXP (x, 0); - goto repeat; - } - hash += canon_hash (XEXP (x, i), 0); - } - else if (fmt[i] == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - hash += canon_hash (XVECEXP (x, i, j), 0); - else if (fmt[i] == 's') - { - register char *p = XSTR (x, i); - if (p) - while (*p) - { - register int tem = *p++; - hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS)); - } - } - else - { - register int tem = XINT (x, i); - hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS)); - } - } - return hash; -} - -/* Like canon_hash but with no side effects. */ - -static int -safe_hash (x, mode) - rtx x; - enum machine_mode mode; -{ - int save_do_not_record = do_not_record; - int save_hash_arg_in_memory = hash_arg_in_memory; - int save_hash_arg_in_struct = hash_arg_in_struct; - int hash = canon_hash (x, mode); - hash_arg_in_memory = save_hash_arg_in_memory; - hash_arg_in_struct = save_hash_arg_in_struct; - do_not_record = save_do_not_record; - return hash; -} - -/* Return 1 iff X and Y would canonicalize into the same thing, - without actually constructing the canonicalization of either one. - If VALIDATE is nonzero, - we assume X is an expression being processed from the rtl - and Y was found in the hash table. We check register refs - in Y for being marked as valid. */ - -static int -exp_equiv_p (x, y, validate) - rtx x, y; - int validate; -{ - register int i; - register enum rtx_code code; - register char *fmt; - - /* Note: it is incorrect to assume an expression is equivalent to itself - if VALIDATE is nonzero. */ - if (x == y && !validate) - return 1; - if (x == 0 || y == 0) - return x == y; - code = GET_CODE (x); - if (code != GET_CODE (y)) - return 0; - - switch (code) - { - case PC: - case CC0: - return x == y; - - case CONST_INT: - return XINT (x, 0) == XINT (y, 0); - - case LABEL_REF: - case SYMBOL_REF: - return XEXP (x, 0) == XEXP (y, 0); - - case REG: - return (reg_qty[REGNO (x)] == reg_qty[REGNO (y)] - && (!validate - || reg_in_table[REGNO (y)] == reg_tick[REGNO (y)])); - } - - /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */ - - if (GET_MODE (x) != GET_MODE (y)) - return 0; - - /* Compare the elements. If any pair of corresponding elements - fail to match, return 0 for the whole things. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - if (! exp_equiv_p (XEXP (x, i), XEXP (y, i), validate)) - return 0; - } - else if (fmt[i] == 'E') - { - int j; - if (XVECLEN (x, i) != XVECLEN (y, i)) - return 0; - for (j = 0; j < XVECLEN (x, i); j++) - if (! exp_equiv_p (XVECEXP (x, i, j), XVECEXP (y, i, j), validate)) - return 0; - } - else if (fmt[i] == 's') - { - if (strcmp (XSTR (x, i), XSTR (y, i))) - return 0; - } - else - { - if (XINT (x, i) != XINT (y, i)) - return 0; - } - } - return 1; -} - -/* Return 1 iff any subexpression of X matches Y. - Here we do not require that X or Y be valid (for registers referred to) - for being in the hash table. */ - -int -refers_to_p (x, y) - rtx x, y; -{ - register int i; - register enum rtx_code code; - register char *fmt; - - repeat: - if (x == y) - return 1; - if (x == 0 || y == 0) - return 0; - - code = GET_CODE (x); - /* If X as a whole has the same code as Y, they may match. - If so, return 1. */ - if (code == GET_CODE (y)) - { - if (exp_equiv_p (x, y, 0)) - return 1; - } - - /* X does not match, so try its subexpressions. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - { - if (i == 0) - { - x = XEXP (x, 0); - goto repeat; - } - else - if (refers_to_p (XEXP (x, i), y)) - return 1; - } - else if (fmt[i] == 'E') - { - int j; - for (j = 0; j < XVECLEN (x, i); j++) - if (refers_to_p (XVECEXP (x, i, j), y)) - return 1; - } - - return 0; -} - -/* Return 1 iff any subexpression of X refers to memory - at an address of REG plus some offset - such that any of the bytes' offsets fall between START (inclusive) - and END (exclusive). - - The value is undefined if X is a varying address. - This function is not used in such cases. - - When used in the cse pass, `qty_const' is nonzero, and it is used - to treat an address that is a register with a known constant value - as if it were that constant value. - In the loop pass, `qty_const' is zero, so this is not done. */ - -int -refers_to_mem_p (x, reg, start, end) - rtx x, reg; - int start, end; -{ - register int i; - register enum rtx_code code; - register char *fmt; - - repeat: - if (x == 0) - return 0; - - code = GET_CODE (x); - if (code == MEM) - { - register rtx addr = XEXP (x, 0); /* Get the address. */ - int myend; - if (GET_CODE (addr) == REG - /* qty_const is 0 when outside the cse pass; - at such times, this info is not available. */ - && qty_const != 0 - && qty_const[reg_qty[REGNO (addr)]] != 0) - addr = qty_const[reg_qty[REGNO (addr)]]; - if (GET_CODE (addr) == CONST) - addr = XEXP (addr, 0); - - /* If ADDR is BASE, or BASE plus an integer, put - the integer in I. */ - if (addr == reg) - i = 0; - else if (GET_CODE (addr) == PLUS - && XEXP (addr, 0) == reg - && GET_CODE (XEXP (addr, 1)) == CONST_INT) - i = INTVAL (XEXP (addr, 1)); - else - return 0; - - myend = i + GET_MODE_SIZE (GET_MODE (x)); - return myend > start && i < end; - } - - /* X does not match, so try its subexpressions. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - { - if (i == 0) - { - x = XEXP (x, 0); - goto repeat; - } - else - if (refers_to_mem_p (XEXP (x, i), reg, start, end)) - return 1; - } - else if (fmt[i] == 'E') - { - int j; - for (j = 0; j < XVECLEN (x, i); j++) - if (refers_to_mem_p (XVECEXP (x, i, j), reg, start, end)) - return 1; - } - - return 0; -} - -/* Nonzero if X refers to memory at a varying address; - except that a register which has at the moment a known constant value - isn't considered variable. */ - -static int -cse_rtx_addr_varies_p (x) - rtx x; -{ - if (GET_CODE (x) == MEM - && GET_CODE (XEXP (x, 0)) == REG - && qty_const[reg_qty[REGNO (XEXP (x, 0))]] != 0) - return 0; - return rtx_addr_varies_p (x); -} - -/* Canonicalize an expression: - replace each register reference inside it - with the "oldest" equivalent register. */ - -static rtx -canon_reg (x) - rtx x; -{ - register int i; - register enum rtx_code code; - register char *fmt; - - if (x == 0) - return x; - - code = GET_CODE (x); - switch (code) - { - case PC: - case CC0: - case CONST: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case LABEL_REF: - case ADDR_VEC: - case ADDR_DIFF_VEC: - return x; - - case REG: - { - register rtx new; - /* Never replace a hard reg, because hard regs can appear - in more than one machine mode, and we must preserve the mode - of each occurrence. Also, some hard regs appear in - MEMs that are shared and mustn't be altered. */ - if (REGNO (x) < FIRST_PSEUDO_REGISTER) - return x; - new = reg_rtx[qty_first_reg[reg_qty[REGNO (x)]]]; - return new ? new : x; - } - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - register int j; - - if (fmt[i] == 'e') - XEXP (x, i) = canon_reg (XEXP (x, i)); - else if (fmt[i] == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - XVECEXP (x, i, j) = canon_reg (XVECEXP (x, i, j)); - } - - return x; -} - -/* If X is a nontrivial arithmetic operation on an argument - for which a constant value can be determined, return - the result of operating on that value, as a constant. - Otherwise, return X, possibly with one or more operands - modified by recursive calls to this function. - - If X is a register whose contents are known, we do NOT - return those contents. This is because an instruction that - uses a register is usually faster than one that uses a constant. - - COPYFLAG is nonzero for memory addresses and subexpressions thereof. - If COPYFLAG is nonzero, we avoid altering X itself - by creating new structure when necessary. In this case we - can risk creating invalid structure because it will be tested. - If COPYFLAG is zero, be careful not to substitute constants - into expressions that cannot be simplified. */ - -static rtx -fold_rtx (x, copyflag) - rtx x; - int copyflag; -{ - register enum rtx_code code; - register char *fmt; - register int i, val; - rtx new = 0; - int copied = ! copyflag; - int width; - - /* Constant equivalents of first three operands of X; - 0 when no such equivalent is known. */ - rtx const_arg0; - rtx const_arg1; - rtx const_arg2; - - if (x == 0) - return x; - - width = GET_MODE_BITSIZE (GET_MODE (x)); - - code = GET_CODE (x); - switch (code) - { - case CONST: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case LABEL_REF: - case PC: - case CC0: - case REG: - /* No use simplifying an EXPR_LIST - since they are used only for lists of args - in a function call's REG_EQUAL note. */ - case EXPR_LIST: - return x; - - /* We must be careful when folding a memory address - to avoid making it invalid. So fold nondestructively - and use the result only if it's valid. */ - case MEM: - { - rtx newaddr = fold_rtx (XEXP (x, 0), 1); - /* Save time if no change was made. */ - if (XEXP (x, 0) == newaddr) - return x; - - if (! memory_address_p (GET_MODE (x), newaddr) - && memory_address_p (GET_MODE (x), XEXP (x, 0))) - return x; - - /* Don't replace a value with a more expensive one. */ - if (rtx_cost (XEXP (x, 0)) < rtx_cost (newaddr)) - return x; - - if (copyflag) - return gen_rtx (MEM, GET_MODE (x), newaddr); - XEXP (x, 0) = newaddr; - return x; - } - } - - const_arg0 = 0; - const_arg1 = 0; - const_arg2 = 0; - - /* Try folding our operands. - Then see which ones have constant values known. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - { - register rtx tem = fold_rtx (XEXP (x, i), copyflag); - - /* If an operand has changed under folding, and we are not supposed to - alter the original structure, copy X if we haven't yet done so. */ - if (! copied && tem != XEXP (x, i)) - { - int j; - rtx new = rtx_alloc (code); - PUT_MODE (new, GET_MODE (x)); - for (j = 0; j < GET_RTX_LENGTH (code); j++) - XINT (new, j) = XINT (x, j); - x = new; - copied = 1; - } - - /* Install the possibly altered folded operand. */ - XEXP (x, i) = tem; - - /* For the first three operands, see if the operand - is constant or equivalent to a constant. */ - if (i < 3) - { - rtx const_arg = equiv_constant (tem); - - switch (i) - { - case 0: - const_arg0 = const_arg; - break; - case 1: - const_arg1 = const_arg; - break; - case 2: - const_arg2 = const_arg; - break; - } - } - } - else if (fmt[i] == 'E') - /* Don't try to fold inside of a vector of expressions. - Doing nothing is is harmless. */ - ; - - /* If a commutative operation, place a constant integer as the second - operand unless the first operand is also a constant integer. Otherwise, - place any constant second unless the first operand is also a constant. */ - - switch (code) - { - case PLUS: - case MULT: - case UMULT: - case AND: - case IOR: - case XOR: - case NE: - case EQ: - if (const_arg0 && const_arg0 == XEXP (x, 0) - && (! (const_arg1 && const_arg1 == XEXP (x, 1)) - || (GET_CODE (const_arg0) == CONST_INT - && GET_CODE (const_arg1) != CONST_INT))) - { - register rtx tem; - - if (! copied) - copied = 1, x = copy_rtx (x); - tem = XEXP (x, 0); XEXP (x, 0) = XEXP (x, 1); XEXP (x, 1) = tem; - tem = const_arg0; const_arg0 = const_arg1; const_arg1 = tem; - } - break; - } - - /* Now decode the kind of rtx X is - and then return X (if nothing can be done) - or return a folded rtx - or store a value in VAL and drop through - (to return a CONST_INT for the integer VAL). */ - - if (GET_RTX_LENGTH (code) == 1) - { - if (const_arg0 == 0) - return x; - - if (GET_CODE (const_arg0) == CONST_INT) - { - register int arg0 = INTVAL (const_arg0); - - switch (GET_CODE (x)) - { - case NOT: - val = ~ arg0; - break; - - case NEG: - val = - arg0; - break; - - case TRUNCATE: - val = arg0; - break; - - case ZERO_EXTEND: - { - enum machine_mode mode = GET_MODE (XEXP (x, 0)); - if (mode == VOIDmode) - return x; - if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_INT) - val = arg0 & ~((-1) << GET_MODE_BITSIZE (mode)); - else - return x; - break; - } - - case SIGN_EXTEND: - { - enum machine_mode mode = GET_MODE (XEXP (x, 0)); - if (mode == VOIDmode) - return x; - if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_INT) - { - val = arg0 & ~((-1) << GET_MODE_BITSIZE (mode)); - if (val & (1 << (GET_MODE_BITSIZE (mode) - 1))) - val -= 1 << GET_MODE_BITSIZE (mode); - } - else - return x; - break; - } - - default: - return x; - } - } -#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) - else if (GET_CODE (const_arg0) == CONST_DOUBLE - && GET_CODE (x) == NEG - && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) - { - union real_extract u; - register REAL_VALUE_TYPE arg0; - jmp_buf handler; - - if (setjmp (handler)) - { - warning ("floating point trap in constant folding"); - return x; - } - set_float_handler (handler); - bcopy (&CONST_DOUBLE_LOW (const_arg0), &u, sizeof u); - arg0 = u.d; - - u.d = REAL_VALUE_NEGATE (arg0); - x = immed_real_const_1 (u.d, GET_MODE (x)); - set_float_handler (0); - return x; - } -#endif - else - return x; - } - else if (GET_RTX_LENGTH (code) == 2) - { - register int arg0, arg1, arg0s, arg1s; - int arithwidth = width; - - /* If 1st arg is the condition codes, 2nd must be zero - and this must be a comparison. - Decode the info on how the previous insn set the cc0 - and use that to deduce result of comparison. */ - if (XEXP (x, 0) == cc0_rtx - || GET_CODE (XEXP (x, 0)) == COMPARE) - { - if (XEXP (x, 0) == cc0_rtx) - arg0 = prev_insn_cc0; - else - arg0 = fold_cc0 (VOIDmode, XEXP (x, 0)); - - if (arg0 == 0 - || const_arg1 != const0_rtx - /* 0200 bit in arg0 means only zeroness is known, - and sign is not known. */ - || ((arg0 & 0200) != 0 && code != EQ && code != NE)) - return x; - - /* Extract either the signed or the unsigned digit from ARG0. */ - if (code == LEU || code == LTU || code == GEU || code == GTU) - arg0 = arg0 & 7; - else - arg0 = (arg0 >> 3) & 7; - if (arg0 == 7) arg0 = -1; - - switch (code) - { - case LE: - case LEU: - return (arg0 <= 0) ? const1_rtx : const0_rtx; - case LT: - case LTU: - return (arg0 < 0) ? const1_rtx : const0_rtx; - case GE: - case GEU: - return (arg0 >= 0) ? const1_rtx : const0_rtx; - case GT: - case GTU: - return (arg0 > 0) ? const1_rtx : const0_rtx; - case NE: - return (arg0 != 0) ? const1_rtx : const0_rtx; - case EQ: - return (arg0 == 0) ? const1_rtx : const0_rtx; - default: - abort (); - } - } - - if (const_arg0 == 0 || const_arg1 == 0 - || GET_CODE (const_arg0) != CONST_INT - || GET_CODE (const_arg1) != CONST_INT) - { - /* Even if we can't compute a constant result, - there are some cases worth simplifying. */ - /* Note that we cannot rely on constant args to come last, - even for commutative operators, - because that happens only when the constant is explicit. */ - switch (code) - { - case PLUS: - if (const_arg0 == const0_rtx - || const_arg0 == fconst0_rtx - || const_arg0 == dconst0_rtx) - return XEXP (x, 1); - if (const_arg1 == const0_rtx - || const_arg1 == fconst0_rtx - || const_arg1 == dconst0_rtx) - return XEXP (x, 0); - - /* Handle both-operands-constant cases. */ - if (const_arg0 != 0 && const_arg1 != 0 - && GET_CODE (const_arg0) != CONST_DOUBLE - && GET_CODE (const_arg1) != CONST_DOUBLE - && GET_MODE_CLASS (GET_MODE (x)) == MODE_INT) - { - if (GET_CODE (const_arg1) == CONST_INT) - new = plus_constant (const_arg0, INTVAL (const_arg1)); - else - { - new = gen_rtx (PLUS, GET_MODE (x), const0_rtx, const0_rtx); - XEXP (new, 0) = const_arg0; - if (GET_CODE (const_arg0) == CONST) - XEXP (new, 0) = XEXP (const_arg0, 0); - XEXP (new, 1) = const_arg1; - if (GET_CODE (const_arg1) == CONST) - XEXP (new, 1) = XEXP (const_arg1, 0); - new = gen_rtx (CONST, GET_MODE (new), new); - } - } - else if (const_arg1 != 0 - && GET_CODE (const_arg1) == CONST_INT - && GET_CODE (XEXP (x, 0)) == PLUS - && (CONSTANT_P (XEXP (XEXP (x, 0), 0)) - || CONSTANT_P (XEXP (XEXP (x, 0), 1)))) - /* constant + (variable + constant) - can result if an index register is made constant. - We simplify this by adding the constants. - If we did not, it would become an invalid address. */ - new = plus_constant (XEXP (x, 0), - INTVAL (const_arg1)); - break; - - case COMPARE: - if (const_arg1 == const0_rtx) - return XEXP (x, 0); - - if (XEXP (x, 0) == XEXP (x, 1) - || (const_arg0 != 0 && const_arg0 == const_arg1)) - { - /* We can't assume x-x is 0 with IEEE floating point. */ - if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT) - return const0_rtx; - } - break; - - case MINUS: - if (const_arg1 == const0_rtx - || const_arg1 == fconst0_rtx - || const_arg1 == dconst0_rtx) - return XEXP (x, 0); - - if (XEXP (x, 0) == XEXP (x, 1) - || (const_arg0 != 0 && const_arg0 == const_arg1)) - { - /* We can't assume x-x is 0 with IEEE floating point. */ - if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT) - return const0_rtx; - } - - /* Change subtraction from zero into negation. */ - if (const_arg0 == const0_rtx) - return gen_rtx (NEG, GET_MODE (x), XEXP (x, 1)); - - /* Don't let a relocatable value get a negative coeff. */ - if (const_arg0 != 0 && const_arg1 != 0 - && GET_CODE (const_arg1) == CONST_INT) - new = plus_constant (const_arg0, - INTVAL (const_arg1)); - break; - - case MULT: - case UMULT: - if (const_arg1 && GET_CODE (const_arg1) == CONST_INT - && INTVAL (const_arg1) == -1 - /* Don't do this in the case of widening multiplication. */ - && GET_MODE (XEXP (x, 0)) == GET_MODE (x)) - return gen_rtx (NEG, GET_MODE (x), XEXP (x, 0)); - if (const_arg0 && GET_CODE (const_arg0) == CONST_INT - && INTVAL (const_arg0) == -1 - && GET_MODE (XEXP (x, 1)) == GET_MODE (x)) - return gen_rtx (NEG, GET_MODE (x), XEXP (x, 1)); - if (const_arg1 == const0_rtx || const_arg0 == const0_rtx) - new = const0_rtx; - if (const_arg1 == fconst0_rtx || const_arg0 == fconst0_rtx) - new = fconst0_rtx; - if (const_arg1 == dconst0_rtx || const_arg0 == dconst0_rtx) - new = dconst0_rtx; - if (const_arg1 == const1_rtx) - return XEXP (x, 0); - if (const_arg0 == const1_rtx) - return XEXP (x, 1); - break; - - case IOR: - if (const_arg1 == const0_rtx) - return XEXP (x, 0); - if (const_arg0 == const0_rtx) - return XEXP (x, 1); - if (const_arg1 && GET_CODE (const_arg1) == CONST_INT - && (INTVAL (const_arg1) & GET_MODE_MASK (GET_MODE (x))) - == GET_MODE_MASK (GET_MODE (x))) - new = const_arg1; - if (const_arg0 && GET_CODE (const_arg0) == CONST_INT - && (INTVAL (const_arg0) & GET_MODE_MASK (GET_MODE (x))) - == GET_MODE_MASK (GET_MODE (x))) - new = const_arg0; - break; - - case XOR: - if (const_arg1 == const0_rtx) - return XEXP (x, 0); - if (const_arg0 == const0_rtx) - return XEXP (x, 1); - if (const_arg1 && GET_CODE (const_arg1) == CONST_INT - && (INTVAL (const_arg1) & GET_MODE_MASK (GET_MODE (x))) - == GET_MODE_MASK (GET_MODE (x))) - return gen_rtx (NOT, GET_MODE (x), XEXP (x, 0)); - if (const_arg0 && GET_CODE (const_arg0) == CONST_INT - && (INTVAL (const_arg0) & GET_MODE_MASK (GET_MODE (x))) - == GET_MODE_MASK (GET_MODE (x))) - return gen_rtx (NOT, GET_MODE (x), XEXP (x, 1)); - break; - - case AND: - if (const_arg1 == const0_rtx || const_arg0 == const0_rtx) - new = const0_rtx; - if (const_arg1 && GET_CODE (const_arg1) == CONST_INT - && (INTVAL (const_arg1) & GET_MODE_MASK (GET_MODE (x))) - == GET_MODE_MASK (GET_MODE (x))) - return XEXP (x, 0); - if (const_arg0 && GET_CODE (const_arg0) == CONST_INT - && (INTVAL (const_arg0) & GET_MODE_MASK (GET_MODE (x))) - == GET_MODE_MASK (GET_MODE (x))) - return XEXP (x, 1); - break; - - case DIV: - case UDIV: - if (const_arg1 == const1_rtx) - return XEXP (x, 0); - if (const_arg0 == const0_rtx) - new = const0_rtx; - break; - - case UMOD: - case MOD: - if (const_arg0 == const0_rtx || const_arg1 == const1_rtx) - new = const0_rtx; - break; - - case LSHIFT: - case ASHIFT: - case ROTATE: - case ASHIFTRT: - case LSHIFTRT: - case ROTATERT: - if (const_arg1 == const0_rtx) - return XEXP (x, 0); - if (const_arg0 == const0_rtx) - new = const_arg0; - break; - } - - if (new != 0 && LEGITIMATE_CONSTANT_P (new)) - return new; - return x; - } - - if (arithwidth == 0) - { - if (GET_MODE (XEXP (x, 0)) != VOIDmode) - arithwidth = GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))); - if (GET_MODE (XEXP (x, 1)) != VOIDmode) - arithwidth = GET_MODE_BITSIZE (GET_MODE (XEXP (x, 1))); - } - - /* Get the integer argument values in two forms: - zero-extended in ARG0, ARG1 and sign-extended in ARG0S, ARG1S. */ - - arg0 = INTVAL (const_arg0); - arg1 = INTVAL (const_arg1); - - if (arithwidth < HOST_BITS_PER_INT && arithwidth > 0) - { - arg0 &= (1 << arithwidth) - 1; - arg1 &= (1 << arithwidth) - 1; - - arg0s = arg0; - if (arg0s & (1 << (arithwidth - 1))) - arg0s |= ((-1) << arithwidth); - - arg1s = arg1; - if (arg1s & (1 << (arithwidth - 1))) - arg1s |= ((-1) << arithwidth); - } - else - { - arg0s = arg0; - arg1s = arg1; - } - - /* Compute the value of the arithmetic. */ - - switch (code) - { - case PLUS: - val = arg0 + arg1; - break; - - case MINUS: - val = arg0 - arg1; - break; - - case MULT: - val = arg0s * arg1s; - break; - - case DIV: - if (arg1s == 0) - return x; - val = arg0s / arg1s; - break; - - case MOD: - if (arg1s == 0) - return x; - val = arg0s % arg1s; - break; - - case UMULT: - val = (unsigned) arg0 * arg1; - break; - - case UDIV: - if (arg1 == 0) - return x; - val = (unsigned) arg0 / arg1; - break; - - case UMOD: - if (arg1 == 0) - return x; - val = (unsigned) arg0 % arg1; - break; - - case AND: - val = arg0 & arg1; - break; - - case IOR: - val = arg0 | arg1; - break; - - case XOR: - val = arg0 ^ arg1; - break; - - case NE: - val = arg0 != arg1; - break; - - case EQ: - val = arg0 == arg1; - break; - - case LE: - val = arg0s <= arg1s; - break; - - case LT: - val = arg0s < arg1s; - break; - - case GE: - val = arg0s >= arg1s; - break; - - case GT: - val = arg0s > arg1s; - break; - - case LEU: - val = ((unsigned) arg0) <= ((unsigned) arg1); - break; - - case LTU: - val = ((unsigned) arg0) < ((unsigned) arg1); - break; - - case GEU: - val = ((unsigned) arg0) >= ((unsigned) arg1); - break; - - case GTU: - val = ((unsigned) arg0) > ((unsigned) arg1); - break; - - case LSHIFT: - /* If target machine uses negative shift counts - but host machine does not, simulate them. */ - if (arg1 < 0) - val = ((unsigned) arg0) >> -arg1; - else - val = ((unsigned) arg0) << arg1; - break; - - case ASHIFT: - if (arg1 < 0) - val = arg0s >> -arg1; - else - val = arg0s << arg1; - break; - - case ROTATERT: - arg1 = - arg1; - case ROTATE: - { - int size = GET_MODE_SIZE (GET_MODE (x)) * BITS_PER_UNIT; - if (arg1 > 0) - { - arg1 %= size; - val = ((((unsigned) arg0) << arg1) - | (((unsigned) arg0) >> (size - arg1))); - } - else if (arg1 < 0) - { - arg1 = (- arg1) % size; - val = ((((unsigned) arg0) >> arg1) - | (((unsigned) arg0) << (size - arg1))); - } - else - val = arg0; - } - break; - - case LSHIFTRT: - /* If target machine uses negative shift counts - but host machine does not, simulate them. */ - if (arg1 < 0) - val = ((unsigned) arg0) << -arg1; - else - val = ((unsigned) arg0) >> arg1; - break; - - case ASHIFTRT: - if (arg1 < 0) - val = arg0s << -arg1; - else - val = arg0s >> arg1; - break; - - default: - return x; - } - } - else if (code == IF_THEN_ELSE && const_arg0 != 0 - && GET_CODE (const_arg0) == CONST_INT) - return XEXP (x, ((INTVAL (const_arg0) != 0) ? 1 : 2)); - else if (code == IF_THEN_ELSE && XEXP (x, 0) == cc0_rtx - && prev_insn_explicit_cc0 != 0) - return XEXP (x, ((INTVAL (prev_insn_explicit_cc0) != 0) ? 1 : 2)); - else if (code == SIGN_EXTRACT || code == ZERO_EXTRACT) - { - if (const_arg0 != 0 && const_arg1 != 0 && const_arg2 != 0 - && GET_CODE (const_arg0) == CONST_INT - && GET_CODE (const_arg1) == CONST_INT - && GET_CODE (const_arg2) == CONST_INT) - { - /* Extracting a bit-field from a constant */ - val = INTVAL (const_arg0); -#ifdef BITS_BIG_ENDIAN - val >>= (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - - INTVAL (const_arg2) - INTVAL (const_arg1)); -#else - val >>= INTVAL (const_arg2); -#endif - if (HOST_BITS_PER_INT != INTVAL (const_arg1)) - { - /* First zero-extend. */ - val &= (1 << INTVAL (const_arg1)) - 1; - /* If desired, propagate sign bit. */ - if (code == SIGN_EXTRACT - && (val & (1 << (INTVAL (const_arg1) - 1)))) - val |= ~ (1 << INTVAL (const_arg1)); - } - } - else - return x; - } - else - return x; - - /* Clear the bits that don't belong in our mode, - unless they and our sign bit are all one. - So we get either a reasonable negative value or a reasonable - unsigned value for this mode. */ - if (width < HOST_BITS_PER_INT && width > 0) - { - if ((val & ((-1) << (width - 1))) - != ((-1) << (width - 1))) - val &= (1 << width) - 1; - } - - /* Now make the new constant. */ - { - rtx new = gen_rtx (CONST_INT, VOIDmode, val); - return LEGITIMATE_CONSTANT_P (new) ? new : x; - } -} - -/* Return a constant value currently equivalent to X. - Return 0 if we don't know one. */ - -static rtx -equiv_constant (x) - rtx x; -{ - rtx tem1; - - if (CONSTANT_P (x) || GET_CODE (x) == CONST_DOUBLE) - return x; - else if (GET_CODE (x) == REG - && (tem1 = qty_const[reg_qty[REGNO (x)]]) != 0 - /* Make sure it is really a constant */ - && GET_CODE (tem1) != REG && GET_CODE (tem1) != PLUS) - return tem1; - /* If integer truncation is being done with SUBREG, - we can compute the result. */ - else if (GET_CODE (x) == SUBREG && SUBREG_WORD (x) == 0 - && (tem1 = qty_const[reg_qty[REGNO (SUBREG_REG (x))]]) != 0 - /* Make sure it is a known integer. */ - && GET_CODE (tem1) == CONST_INT - && GET_MODE_SIZE (GET_MODE (x)) <= HOST_BITS_PER_INT - /* Make sure this SUBREG is truncation. */ - && GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) - { - int value = INTVAL (tem1); - if (GET_MODE_BITSIZE (GET_MODE (x)) != HOST_BITS_PER_INT) - value &= (1 << GET_MODE_BITSIZE (GET_MODE (x))) - 1; - - if (value == INTVAL (tem1)) - return tem1; - else - return gen_rtx (CONST_INT, VOIDmode, value); - } - return 0; -} - -/* Given an expression X which is used to set CC0, - return an integer recording (in the encoding used for prev_insn_cc0) - how the condition codes would be set by that expression. - Return 0 if the value is not constant - or if there is any doubt what condition codes result from it. - - MODE is the machine mode to use to interpret X if it is a CONST_INT. */ - -static int -fold_cc0 (mode, x) - enum machine_mode mode; - rtx x; -{ - if (GET_CODE (x) == COMPARE) - { - rtx y0 = fold_rtx (XEXP (x, 0), 0); - rtx y1 = fold_rtx (XEXP (x, 1), 0); - int u0, u1, s0, s1; - enum machine_mode m; - rtx tem; - - m = GET_MODE (y0); - if (m == VOIDmode) - m = GET_MODE (y1); - if (m == VOIDmode) - return 0; - - tem = equiv_constant (y0); - if (tem != 0) - y0 = tem; - - if (y0 == 0) - return 0; - - tem = equiv_constant (y1); - if (tem != 0) - y1 = tem; - - if (y1 == 0) - return 0; - - /* Compare floats; report the result only for signed compares - since that's all there are for floats. */ - if (GET_CODE (y0) == CONST_DOUBLE - && GET_CODE (y1) == CONST_DOUBLE - && GET_MODE_CLASS (GET_MODE (y0)) == MODE_FLOAT) - { - union real_extract u0, u1; - int value; - jmp_buf handler; - - if (setjmp (handler)) - { - warning ("floating point trap in constant folding"); - return 0; - } - set_float_handler (handler); - bcopy (&CONST_DOUBLE_LOW (y0), &u0, sizeof u0); - bcopy (&CONST_DOUBLE_LOW (y1), &u1, sizeof u1); - value = 0100 + (REAL_VALUES_LESS (u0.d, u1.d) ? 7 << 3 - : REAL_VALUES_LESS (u1.d, u0.d) ? 1 << 3 : 0); - set_float_handler (0); - return value; - } - - /* Aside from that, demand explicit integers. */ - - if (GET_CODE (y0) != CONST_INT) - return 0; - - if (GET_CODE (y1) != CONST_INT) - return 0; - - s0 = u0 = INTVAL (y0); - s1 = u1 = INTVAL (y1); - - { - int width = GET_MODE_BITSIZE (m); - if (width < HOST_BITS_PER_INT) - { - s0 = u0 &= ~ ((-1) << width); - s1 = u1 &= ~ ((-1) << width); - if (u0 & (1 << (width - 1))) - s0 |= ((-1) << width); - if (u1 & (1 << (width - 1))) - s1 |= ((-1) << width); - } - } - - return 0100 + ((s0 < s1 ? 7 : s0 > s1) << 3) - + (((unsigned) u0 < (unsigned) u1) ? 7 - : ((unsigned) u0 > (unsigned) u1)); - } - { - rtx y0; - int u0, s0; - enum machine_mode m; - - y0 = fold_rtx (x, 0); - - m = GET_MODE (y0); - if (m == VOIDmode) - m = mode; - - if (GET_CODE (y0) == REG) - y0 = qty_const[reg_qty[REGNO (y0)]]; - - /* Register had no constant equivalent? We can't do anything. */ - if (y0 == 0) - return 0; - - /* If we don't know the mode, we can't test the sign. */ - if (m == VOIDmode) - return 0; - - /* Value is frame-pointer plus a constant? Or non-explicit constant? - That isn't zero, but we don't know its sign. */ - if (FIXED_BASE_PLUS_P (y0) - || GET_CODE (y0) == SYMBOL_REF || GET_CODE (y0) == CONST - || GET_CODE (y0) == LABEL_REF) - return 0300 + (1<<3) + 1; - - /* Otherwise, only integers enable us to optimize. */ - if (GET_CODE (y0) != CONST_INT) - return 0; - - s0 = u0 = INTVAL (y0); - { - int width = GET_MODE_BITSIZE (m); - if (width < HOST_BITS_PER_INT) - { - s0 = u0 &= ~ ((-1) << GET_MODE_BITSIZE (m)); - if (u0 & (1 << (GET_MODE_BITSIZE (m) - 1))) - s0 |= ((-1) << GET_MODE_BITSIZE (m)); - } - } - return 0100 + ((s0 < 0 ? 7 : s0 > 0) << 3) + (u0 != 0); - } -} - -/* Attempt to prove that a loop will be executed >= 1 times, - or prove it will be executed 0 times. - If either can be proved, delete some of the code. */ - -static void -predecide_loop_entry (insn) - register rtx insn; -{ - register rtx jump = NEXT_INSN (insn); - register rtx p; - register rtx loop_top_label = NEXT_INSN (jump); - enum anon1 { UNK, DELETE_LOOP, DELETE_JUMP } disposition = UNK; - int count = 0; - - /* Give up if we don't find a jump that enters the loop. */ - if (! simplejump_p (jump)) - return; - - /* Find the label at the top of the loop. */ - while (GET_CODE (loop_top_label) == BARRIER - || GET_CODE (loop_top_label) == NOTE) - { - loop_top_label = NEXT_INSN (loop_top_label); - /* No label? Give up. */ - if (loop_top_label == 0) - return; - } - if (GET_CODE (loop_top_label) != CODE_LABEL) - abort (); - - /* Find the label at which the loop is entered. */ - p = XEXP (SET_SRC (PATTERN (jump)), 0); - if (GET_CODE (p) != CODE_LABEL) - abort (); - - /* Trace the flow of control through the end test, - propagating constants, to see if result is determined. */ - prev_insn_cc0 = 0; - prev_insn_explicit_cc0 = 0; - /* Avoid infinite loop if we find a cycle of jumps. */ - while (count < 10) - { - /* At end of function? Means rtl is inconsistent, - but this can happen when stmt.c gets confused - by a syntax error. */ - if (p == 0) - break; - /* Arriving at end of loop means endtest will drop out. */ - if (GET_CODE (p) == NOTE - && NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END) - { - disposition = DELETE_LOOP; - break; - } - else if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == NOTE) - ; - /* We only know how to handle two kinds of insns: - conditional jumps, and those that set the condition codes. */ - else if (GET_CODE (p) == INSN && GET_CODE (PATTERN (p)) == SET - && SET_DEST (PATTERN (p)) == cc0_rtx) - { - prev_insn_cc0 = fold_cc0 (GET_MODE (SET_SRC (PATTERN (p))), - copy_rtx (SET_SRC (PATTERN (p)))); - if (GET_CODE (SET_SRC (PATTERN (p))) == CONST_INT) - prev_insn_explicit_cc0 = SET_SRC (PATTERN (p)); - } - else if (GET_CODE (p) == JUMP_INSN - && GET_CODE (PATTERN (p)) == SET - && SET_DEST (PATTERN (p)) == pc_rtx) - { - register rtx target - = fold_rtx (SET_SRC (PATTERN (p)), 1); - if (GET_CODE (target) == LABEL_REF) - p = XEXP (target, 0); - else if (target != pc_rtx) - /* If destination of jump is not fixed, give up. */ - break; - count++; - } - /* Any other kind of insn means we don't know - what result the test will have. */ - else - break; - - /* Arriving at top of loop means we can drop straight in. - Check here because we can arrive only via a jump insn - which would have changed P above. */ - if (p == loop_top_label) - { - disposition = DELETE_JUMP; - break; - } - /* We went past one insn; consider the next. */ - p = NEXT_INSN (p); - } - if (disposition == DELETE_JUMP) - { - /* We know the loop test will succeed the first time, - so delete the jump to the test; drop right into loop. - Note that one call to delete_insn gets the BARRIER as well. */ - delete_insn (jump); - } - if (disposition == DELETE_LOOP) - { - /* We know the endtest will fail and drop right out of the loop, - but it isn't safe to delete the loop here. - There could be jumps into it from outside. - So make the entry-jump jump around the loop. - This will cause find_basic_blocks to delete it if appropriate. */ - register rtx label = gen_label_rtx (); - emit_label_after (label, p); - redirect_jump (jump, label); - } -} - -/* CSE processing for one instruction. - First simplify sources and addresses of all assignments - in the instruction, using previously-computed equivalents values. - Then install the new sources and destinations in the table - of available values. */ - -/* Data on one SET contained in the instruction. */ - -struct set -{ - /* The SET rtx itself. */ - rtx rtl; - /* The hash-table element for the SET_SRC of the SET. */ - struct table_elt *src_elt; - /* Hash code for the SET_SRC. */ - int src_hash_code; - /* Hash code for the SET_DEST. */ - int dest_hash_code; - /* The SET_DEST, with SUBREG, etc., stripped. */ - rtx inner_dest; - /* Place where the pointer to the INNER_DEST was found. */ - rtx *inner_dest_loc; - /* Nonzero if the SET_SRC is in memory. */ - char src_in_memory; - /* Nonzero if the SET_SRC is in a structure. */ - char src_in_struct; - /* Nonzero if the SET_SRC contains something - whose value cannot be predicted and understood. */ - char src_volatile; - /* Original machine mode, in case it becomes a CONST_INT. */ - enum machine_mode mode; -}; - -static void -cse_insn (insn) - rtx insn; -{ - register rtx x = PATTERN (insn); - register int i; - register int n_sets = 0; - - /* Records what this insn does to set CC0, - using same encoding used for prev_insn_cc0. */ - int this_insn_cc0 = 0; - /* Likewise, what to store in prev_insn_explicit_cc0. */ - rtx this_insn_explicit_cc0 = 0; - struct write_data writes_memory; - static struct write_data init = {0, 0, 0}; - - rtx src_eqv = 0; - struct table_elt *src_eqv_elt = 0; - int src_eqv_in_memory; - int src_eqv_in_struct; - int src_eqv_hash_code; - - struct set *sets; - - this_insn = insn; - writes_memory = init; - - /* Find all the SETs and CLOBBERs in this instruction. - Record all the SETs in the array `set' and count them. - Also determine whether there is a CLOBBER that invalidates - all memory references, or all references at varying addresses. */ - - if (GET_CODE (x) == SET) - { - rtx tem; - n_sets = 1; - sets = (struct set *) alloca (sizeof (struct set)); - sets[0].rtl = x; - - if (REG_NOTES (insn) != 0) - { - /* Store the equivalent value (re REG_EQUAL or REG_EQUIV) in SRC_EQV. */ - tem = find_reg_note (insn, REG_EQUIV, 0); - if (tem == 0) - tem = find_reg_note (insn, REG_EQUAL, 0); - if (tem) src_eqv = XEXP (tem, 0); - - /* Ignore the REG_EQUAL or REG_EQUIV note if its contents - are the same as the source. */ - if (src_eqv && rtx_equal_p (src_eqv, SET_SRC (x))) - src_eqv = 0; - } - - /* Return now for unconditional jumps. - They never need cse processing, so this does not hurt. - The reason is not efficiency but rather - so that we can test at the end for instructions - that have been simplified to unconditional jumps - and not be misled by unchanged instructions - that were unconditional jumps to begin with. */ - if (SET_DEST (x) == pc_rtx - && GET_CODE (SET_SRC (x)) == LABEL_REF) - return; - - /* Return now for call-insns, (set (reg 0) (call ...)). - The hard function value register is used only once, to copy to - someplace else, so it isn't worth cse'ing (and on 80386 is unsafe)! */ - if (GET_CODE (SET_SRC (x)) == CALL) - { - canon_reg (SET_SRC (x)); - return; - } - } - else if (GET_CODE (x) == PARALLEL) - { - register int lim = XVECLEN (x, 0); - - sets = (struct set *) alloca (lim * sizeof (struct set)); - - /* Find all regs explicitly clobbered in this insn, - and ensure they are not replaced with any other regs - elsewhere in this insn. - When a reg that is clobbered is also used for input, - we should presume that that is for a reason, - and we should not substitute some other register - which is not supposed to be clobbered. */ - for (i = 0; i < lim; i++) - { - register rtx y = XVECEXP (x, 0, i); - if (GET_CODE (y) == CLOBBER && GET_CODE (XEXP (y, 0)) == REG) - invalidate (XEXP (y, 0)); - } - - for (i = 0; i < lim; i++) - { - register rtx y = XVECEXP (x, 0, i); - if (GET_CODE (y) == SET) - sets[n_sets++].rtl = y; - else if (GET_CODE (y) == CLOBBER) - { - /* If we clobber memory, take note of that, - and canon the address. - This does nothing when a register is clobbered - because we have already invalidated the reg. */ - canon_reg (y); - note_mem_written (XEXP (y, 0), &writes_memory); - } - else if (GET_CODE (y) == USE - && ! (GET_CODE (XEXP (y, 0)) == REG - && REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER)) - canon_reg (y); - else if (GET_CODE (y) == CALL) - canon_reg (y); - } - } - else if (GET_CODE (x) == CLOBBER) - note_mem_written (XEXP (x, 0), &writes_memory); - else if (GET_CODE (x) == CALL) - canon_reg (x); - - if (n_sets == 0) - { - invalidate_from_clobbers (&writes_memory, x); - return; - } - - /* Canonicalize sources and addresses of destinations. - set sets[i].src_elt to the class each source belongs to. - Detect assignments from or to volatile things - and set set[i] to zero so they will be ignored - in the rest of this function. - - Nothing in this loop changes the hash table or the register chains. */ - - for (i = 0; i < n_sets; i++) - { - register rtx src, dest; - register struct table_elt *elt; - enum machine_mode mode; - - dest = SET_DEST (sets[i].rtl); - src = SET_SRC (sets[i].rtl); - - /* If SRC is a constant that has no machine mode, - hash it with the destination's machine mode. - This way we can keep different modes separate. */ - - mode = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src); - sets[i].mode = mode; - - /* Replace each registers in SRC with oldest equivalent register, - but if DEST is a register do not replace it if it appears in SRC. */ - - if (GET_CODE (dest) == REG) - { - int tem = reg_qty[REGNO (dest)]; - reg_qty[REGNO (dest)] = REGNO (dest); - src = canon_reg (src); - - if (src_eqv) - src_eqv = canon_reg (src_eqv); - - reg_qty[REGNO (dest)] = tem; - } - else - { - src = canon_reg (src); - - if (src_eqv) - src_eqv = canon_reg (src_eqv); - } - - if (src_eqv) - { - enum machine_mode eqvmode = mode; - if (GET_CODE (dest) == STRICT_LOW_PART) - eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0))); - do_not_record = 0; - hash_arg_in_memory = 0; - hash_arg_in_struct = 0; - src_eqv = fold_rtx (src_eqv, 0); - src_eqv_hash_code = HASH (src_eqv, eqvmode); - - /* Replace the src_eqv with its cheapest equivalent. */ - - if (!do_not_record) - { - elt = lookup (src_eqv, src_eqv_hash_code, eqvmode); - if (elt && elt != elt->first_same_value) - { - elt = elt->first_same_value; - /* Find the cheapest one that is still valid. */ - while ((GET_CODE (elt->exp) != REG - && !exp_equiv_p (elt->exp, elt->exp, 1)) - || elt->equivalence_only) - elt = elt->next_same_value; - src_eqv = copy_rtx (elt->exp); - hash_arg_in_memory = 0; - hash_arg_in_struct = 0; - src_eqv_hash_code = HASH (src_eqv, elt->mode); - } - src_eqv_elt = elt; - } - else - src_eqv = 0; - - src_eqv_in_memory = hash_arg_in_memory; - src_eqv_in_struct = hash_arg_in_struct; - } - - /* Compute SRC's hash code, and also notice if it - should not be recorded at all. In that case, - prevent any further processing of this assignment. */ - do_not_record = 0; - hash_arg_in_memory = 0; - hash_arg_in_struct = 0; - src = fold_rtx (src, 0); - /* If SRC is a subreg of a reg with a known value, - perform the truncation now. */ - if (GET_CODE (src) == SUBREG) - { - rtx temp = equiv_constant (src); - if (temp) - src = temp; - } - /* If we have (NOT Y), see if Y is known to be (NOT Z). - If so, (NOT Y) simplifies to Z. */ - if (GET_CODE (src) == NOT || GET_CODE (src) == NEG) - { - rtx y = lookup_as_function (XEXP (src, 0), GET_CODE (src)); - if (y != 0) - src = copy_rtx (XEXP (y, 0)); - } - - /* If storing a constant value in a register that - previously held the constant value 0, - record this fact with a REG_WAS_0 note on this insn. */ - if (GET_CODE (src) == CONST_INT - && GET_CODE (dest) == REG - && qty_const[reg_qty[REGNO (dest)]] == const0_rtx) - REG_NOTES (insn) = gen_rtx (INSN_LIST, REG_WAS_0, - qty_const_insn[reg_qty[REGNO (dest)]], - REG_NOTES (insn)); - - sets[i].src_hash_code = HASH (src, mode); - - sets[i].src_volatile = do_not_record; - -#if 0 - /* This code caused multiple hash-table entries - to be created for registers. Invalidation - would only get one, leaving others that didn't belong. - I don't know what good this ever did. */ - if (GET_CODE (src) == REG) - { - sets[i].src_in_memory = 0; - sets[i].src_elt = 0; - } - else ...; -#endif - /* If source is a perverse subreg (such as QI treated as an SI), - treat it as volatile. It may do the work of an SI in one context - where the extra bits are not being used, but cannot replace an SI - in general. */ - if (GET_CODE (src) == SUBREG - && (GET_MODE_SIZE (GET_MODE (src)) - > GET_MODE_SIZE (GET_MODE (SUBREG_REG (src))))) - sets[i].src_volatile = 1; - else if (!sets[i].src_volatile) - { - /* Replace the source with its cheapest equivalent. */ - - elt = lookup (src, sets[i].src_hash_code, mode); - if (elt && elt != elt->first_same_value) - { - elt = elt->first_same_value; - /* Find the cheapest one that is still valid. */ - while ((GET_CODE (elt->exp) != REG - && !exp_equiv_p (elt->exp, elt->exp, 1)) - || elt->equivalence_only) - elt = elt->next_same_value; - /* Don't replace with things that are not likely to be valid, - such as arithmetic expressions, unless the destination is - a register. */ - if (general_operand (elt->exp, VOIDmode) - || GET_CODE (dest) == REG) - { - src = copy_rtx (elt->exp); - hash_arg_in_memory = 0; - hash_arg_in_struct = 0; - sets[i].src_hash_code = HASH (src, elt->mode); - } - } - - /* If ELT is a constant, is there a register - linearly related to it? If so, replace it - with the sum of that register plus an offset. */ - - if (GET_CODE (src) == CONST && n_sets == 1 - && SET_DEST (sets[i].rtl) != cc0_rtx) - { - rtx newsrc = use_related_value (src, elt); - if (newsrc == 0 && src_eqv != 0) - newsrc = use_related_value (src_eqv, src_eqv_elt); - if (newsrc) - { - rtx oldsrc = src; - src = newsrc; - hash_arg_in_memory = 0; - hash_arg_in_struct = 0; - sets[i].src_hash_code = HASH (src, GET_MODE (src)); - /* The new expression for the SRC has the same value - as the previous one; so if the previous one is in - the hash table, put the new one in as equivalent. */ - if (elt != 0) - elt = insert (src, elt->first_same_value, sets[i].src_hash_code, - elt->mode); - else - { - /* Maybe the new expression is in the table already. */ - elt = lookup (src, sets[i].src_hash_code, mode); - /* And maybe a register contains the same value. */ - if (elt && elt != elt->first_same_value) - { - elt = elt->first_same_value; - /* Find the cheapest one that is still valid. */ - while ((GET_CODE (elt->exp) != REG - && !exp_equiv_p (elt->exp, elt->exp, 1)) - || elt->equivalence_only) - elt = elt->next_same_value; - src = copy_rtx (elt->exp); - hash_arg_in_memory = 0; - hash_arg_in_struct = 0; - sets[i].src_hash_code = HASH (src, elt->mode); - } - } - - /* This would normally be inhibited by the REG_EQUIV - note we are about to make. */ -#if 0 - /* Deleted because the inhibition was deleted. */ - SET_SRC (sets[i].rtl) = src; -#endif - - /* Record the actual constant value - in a REG_EQUIV or REG_EQUAL note. */ - if (GET_CODE (SET_DEST (sets[i].rtl)) == REG) - { - /* A REG_EQUIV note means the dest never changes. - Don't put one on unless there is already one. */ - rtx note = find_reg_note (insn, REG_EQUIV, 0); - if (note != 0) - XEXP (note, 0) = oldsrc; - else - REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL, - oldsrc, REG_NOTES (insn)); - } - } - } - - sets[i].src_elt = elt; - sets[i].src_in_memory = hash_arg_in_memory; - sets[i].src_in_struct = hash_arg_in_struct; - } - - /* Either canon_reg or the copy_rtx may have changed this. */ - /* Note it is not safe to replace the sources if there - is more than one set. We could get an insn - [(set (reg) (reg)) (set (reg) (reg))], which is probably - not in the machine description. - This case we could handle by breaking into several insns. - Cases of partial substitution cannot win at all. */ - /* Also, if this insn is setting a "constant" register, - we may not replace the value that is given to it. */ - if (n_sets == 1) -#if 0 - /* Now that the REG_EQUIV contains the constant instead of the reg, - it should be ok to modify the insn's actual source. */ - if (REG_NOTES (insn) == 0 - || REG_NOTE_KIND (REG_NOTES (insn)) != REG_EQUIV) -#endif - SET_SRC (sets[0].rtl) = src; - - do_not_record = 0; - sets[i].inner_dest_loc = &SET_DEST (sets[0].rtl); - - /* Look within any SIGN_EXTRACT or ZERO_EXTRACT - to the MEM or REG within it. */ - while (1) - { - if (GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == ZERO_EXTRACT) - { - XEXP (dest, 1) = canon_reg (XEXP (dest, 1)); - XEXP (dest, 2) = canon_reg (XEXP (dest, 2)); - sets[i].inner_dest_loc = &XEXP (dest, 0); - dest = XEXP (dest, 0); - } - else if (GET_CODE (dest) == SUBREG - || GET_CODE (dest) == STRICT_LOW_PART) - { - sets[i].inner_dest_loc = &XEXP (dest, 0); - dest = XEXP (dest, 0); - } - else - break; - } - - sets[i].inner_dest = dest; - - /* If storing into memory, do cse on the memory address. - Also compute the hash code of the destination now, - before the effects of this instruction are recorded, - since the register values used in the address computation - are those before this instruction. */ - if (GET_CODE (dest) == MEM) - { - register rtx addr; - register int hash; - - canon_reg (dest); - dest = fold_rtx (dest, 0); - addr = XEXP (dest, 0); - - /* Pushing or popping does not invalidate anything. */ - if ((GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC - || GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC) - && GET_CODE (XEXP (addr, 0)) == REG - && REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM) - ; - else - /* Otherwise, decide whether we invalidate - everything in memory, or just things at non-fixed places. - Writing a large aggregate must invalidate everything - because we don't know how long it is. */ - note_mem_written (dest, &writes_memory); - - /* Do not try to replace addresses of local and argument slots. - The MEM expressions for args and non-register local variables - are made only once and inserted in many instructions, - as well as being used to control symbol table output. - It is not safe to clobber them. It also doesn't do any good! */ - if ((GET_CODE (addr) == PLUS - && GET_CODE (XEXP (addr, 0)) == REG - && GET_CODE (XEXP (addr, 1)) == CONST_INT - && (hash = REGNO (XEXP (addr, 0)), - hash == FRAME_POINTER_REGNUM || hash == ARG_POINTER_REGNUM)) - || (GET_CODE (addr) == REG - && (hash = REGNO (addr), - hash == FRAME_POINTER_REGNUM || hash == ARG_POINTER_REGNUM))) - sets[i].dest_hash_code = ((int)MEM + canon_hash (addr, GET_MODE (dest))) % NBUCKETS; - else - { - /* Look for a simpler equivalent for the destination address. */ - hash = HASH (addr, Pmode); - if (! do_not_record) - { - elt = lookup (addr, hash, Pmode); - sets[i].dest_hash_code = ((int) MEM + hash) % NBUCKETS; - - if (elt && elt != elt->first_same_value) - { - elt = elt->first_same_value; - /* Find the cheapest one that is still valid. */ - while ((GET_CODE (elt->exp) != REG - && !exp_equiv_p (elt->exp, elt->exp, 1)) - || elt->equivalence_only) - elt = elt->next_same_value; - - addr = copy_rtx (elt->exp); - /* Create a new MEM rtx, in case the old one - is shared somewhere else. */ - dest = gen_rtx (MEM, GET_MODE (dest), addr); - MEM_VOLATILE_P (dest) - = MEM_VOLATILE_P (sets[i].inner_dest); - MEM_IN_STRUCT_P (dest) - = MEM_IN_STRUCT_P (sets[i].inner_dest); - *sets[i].inner_dest_loc = dest; - sets[i].inner_dest = dest; - } - } - } - } - - /* Don't enter a bit-field in the hash table - because the value in it after the store - may not equal what was stored, due to truncation. */ - - if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT - || GET_CODE (SET_DEST (sets[i].rtl)) == SIGN_EXTRACT) - { - rtx width = XEXP (SET_DEST (sets[i].rtl), 1); - rtx value = equiv_constant (SET_SRC (sets[i].rtl)); - - if (value != 0 && GET_CODE (value) == CONST_INT - && GET_CODE (width) == CONST_INT - && INTVAL (width) < HOST_BITS_PER_INT - && ! (INTVAL (value) & (-1) << INTVAL (width))) - /* Exception: if the value is constant, - we can tell whether truncation would change it. */ - ; - else - sets[i].src_volatile = 1, src_eqv = 0; - } - - /* No further processing for this assignment - if destination is volatile or if the source and destination - are the same. */ - - else if (do_not_record - || (GET_CODE (dest) == REG - ? REGNO (dest) == STACK_POINTER_REGNUM - : GET_CODE (dest) != MEM) - || rtx_equal_p (SET_SRC (sets[i].rtl), SET_DEST (sets[i].rtl))) - sets[i].rtl = 0; - - if (sets[i].rtl != 0 && dest != SET_DEST (sets[i].rtl)) - sets[i].dest_hash_code = HASH (SET_DEST (sets[i].rtl), mode); - - if (dest == cc0_rtx - && (GET_CODE (src) == COMPARE - || CONSTANT_P (src) - || GET_CODE (src) == REG)) - this_insn_cc0 = fold_cc0 (sets[i].mode, src); - - if (dest == cc0_rtx && GET_CODE (src) == CONST_INT) - this_insn_explicit_cc0 = src; - } - - /* Now enter all non-volatile source expressions in the hash table - if they are not already present. - Record in src_elt the heads of their equivalence classes. - This way we can insert the corresponding destinations into - the same classes even if the actual sources are no longer in them - (having been invalidated). */ - - if (src_eqv && src_eqv_elt == 0 && sets[0].rtl != 0) - { - register struct table_elt *elt; - rtx dest = SET_DEST (sets[0].rtl); - enum machine_mode eqvmode = GET_MODE (dest); - - if (GET_CODE (dest) == STRICT_LOW_PART) - eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0))); - if (insert_regs (src_eqv, 0, 0)) - src_eqv_hash_code = HASH (src_eqv, eqvmode); - elt = insert (src_eqv, 0, src_eqv_hash_code, eqvmode); - elt->in_memory = src_eqv_in_memory; - elt->in_struct = src_eqv_in_struct; - elt->equivalence_only = 1; - src_eqv_elt = elt->first_same_value; - } - - for (i = 0; i < n_sets; i++) - if (sets[i].rtl && ! sets[i].src_volatile) - { - if (GET_CODE (SET_DEST (sets[i].rtl)) == STRICT_LOW_PART) - { - /* REG_EQUAL in setting a STRICT_LOW_PART - gives an equivalent for the entire destination register, - not just for the subreg being stored in now. - This is a more interesting equivalent, so we arrange later - to treat the entire reg as the destination. */ - sets[i].src_elt = src_eqv_elt; - sets[i].src_hash_code = src_eqv_hash_code; - } - else if (sets[i].src_elt == 0) - { - register rtx src = SET_SRC (sets[i].rtl); - register rtx dest = SET_DEST (sets[i].rtl); - register struct table_elt *elt; - enum machine_mode mode - = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src); - - /* Note that these insert_regs calls cannot remove - any of the src_elt's, because they would have failed to match - if not still valid. */ - if (insert_regs (src, 0, 0)) - sets[i].src_hash_code = HASH (src, mode); - elt = insert (src, src_eqv_elt, sets[i].src_hash_code, mode); - elt->in_memory = sets[i].src_in_memory; - elt->in_struct = sets[i].src_in_struct; - sets[i].src_elt = elt->first_same_value; - } - } - - invalidate_from_clobbers (&writes_memory, x); - - /* Now invalidate everything set by this instruction. - If a SUBREG or other funny destination is being set, - sets[i].rtl is still nonzero, so here we invalidate the reg - a part of which is being set. */ - - for (i = 0; i < n_sets; i++) - if (sets[i].rtl) - { - register rtx dest = sets[i].inner_dest; - - /* Needed for registers to remove the register from its - previous quantity's chain. - Needed for memory if this is a nonvarying address, unless - we have just done an invalidate_memory that covers even those. */ - if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG - || (! writes_memory.all && ! cse_rtx_addr_varies_p (dest))) - invalidate (dest); - } - - /* Make sure registers mentioned in destinations - are safe for use in an expression to be inserted. - This removes from the hash table - any invalid entry that refers to one of these registers. */ - - for (i = 0; i < n_sets; i++) - if (sets[i].rtl && GET_CODE (SET_DEST (sets[i].rtl)) != REG) - mention_regs (SET_DEST (sets[i].rtl)); - - /* We may have just removed some of the src_elt's from the hash table. - So replace each one with the current head of the same class. */ - - for (i = 0; i < n_sets; i++) - if (sets[i].rtl) - { - /* If the source is volatile, its destination goes in - a class of its own. */ - if (sets[i].src_volatile) - sets[i].src_elt = 0; - - if (sets[i].src_elt && sets[i].src_elt->first_same_value == 0) - /* If elt was removed, find current head of same class, - or 0 if nothing remains of that class. */ - { - register struct table_elt *elt = sets[i].src_elt; - - while (elt && elt->first_same_value == 0) - elt = elt->next_same_value; - sets[i].src_elt = elt ? elt->first_same_value : 0; - } - } - - /* Now insert the destinations into their equivalence classes. */ - - for (i = 0; i < n_sets; i++) - if (sets[i].rtl) - { - register rtx dest = SET_DEST (sets[i].rtl); - register struct table_elt *elt; - - if (flag_float_store - && GET_CODE (dest) == MEM - && (GET_MODE (dest) == SFmode || GET_MODE (dest) == DFmode)) - continue; - - /* STRICT_LOW_PART isn't part of the value BEING set, - and neither is the SUBREG inside it. - Note that in this case SETS[I].SRC_ELT is really SRC_EQV_ELT. */ - if (GET_CODE (dest) == STRICT_LOW_PART) - dest = SUBREG_REG (XEXP (dest, 0)); - - if (GET_CODE (dest) == REG) - /* Registers must also be inserted into chains for quantities. */ - if (insert_regs (dest, sets[i].src_elt, 1)) - /* If `insert_regs' changes something, the hash code must be - recalculated. */ - sets[i].dest_hash_code = HASHREG (dest); - - if (GET_CODE (dest) == SUBREG) - /* Registers must also be inserted into chains for quantities. */ - if (insert_regs (dest, sets[i].src_elt, 1)) - /* If `insert_regs' changes something, the hash code must be - recalculated. */ - sets[i].dest_hash_code - = canon_hash (dest, GET_MODE (dest)) % NBUCKETS; - - elt = insert (dest, sets[i].src_elt, sets[i].dest_hash_code, GET_MODE (dest)); - elt->in_memory = GET_CODE (sets[i].inner_dest) == MEM; - if (elt->in_memory) - { - elt->in_struct = (MEM_IN_STRUCT_P (sets[i].inner_dest) - || sets[i].inner_dest != SET_DEST (sets[i].rtl)); - } - } - - /* Special handling for (set REG0 REG1) - where REG0 is the "cheapest", cheaper than REG1. - After cse, REG1 will probably not be used in the sequel, - so (if easily done) change this insn to (set REG1 REG0) and - replace REG1 with REG0 in the previous insn that computed their value. - Then REG1 will become a dead store and won't cloud the situation - for later optimizations. */ - if (n_sets == 1 && sets[0].rtl && GET_CODE (SET_DEST (sets[0].rtl)) == REG - && GET_CODE (SET_SRC (sets[0].rtl)) == REG - && rtx_equal_p (canon_reg (SET_SRC (sets[0].rtl)), SET_DEST (sets[0].rtl))) - { - rtx prev = PREV_INSN (insn); - while (prev && GET_CODE (prev) == NOTE) - prev = PREV_INSN (prev); - - if (prev && GET_CODE (prev) == INSN && GET_CODE (PATTERN (prev)) == SET - && SET_DEST (PATTERN (prev)) == SET_SRC (sets[0].rtl)) - { - rtx dest = SET_DEST (sets[0].rtl); - rtx note = find_reg_note (prev, REG_EQUIV, 0); - - SET_DEST (PATTERN (prev)) = dest; - SET_DEST (sets[0].rtl) = SET_SRC (sets[0].rtl); - SET_SRC (sets[0].rtl) = dest; - /* If REG1 was equivalent to a constant, REG0 is not. */ - if (note) - PUT_MODE (note, REG_EQUAL); - } - } - - /* Did this insn become an unconditional branch or become a no-op? */ - if (GET_CODE (insn) == JUMP_INSN - && GET_CODE (x) == SET - && SET_DEST (x) == pc_rtx) - { - if (SET_SRC (x) == pc_rtx) - { - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - cse_jumps_altered = 1; - /* If previous insn just set CC0 for us, delete it too. */ - if (prev_insn_cc0 != 0 || prev_insn_explicit_cc0 != 0) - { - PUT_CODE (prev_insn, NOTE); - NOTE_LINE_NUMBER (prev_insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (prev_insn) = 0; - } - /* One less use of the label this insn used to jump to. */ - --LABEL_NUSES (JUMP_LABEL (insn)); - } - else if (GET_CODE (SET_SRC (x)) == LABEL_REF) - { - rtx label; - - emit_barrier_after (insn); - cse_jumps_altered = 1; - /* If previous insn just set CC0 for us, delete it too. */ - if (prev_insn_cc0 != 0 || prev_insn_explicit_cc0 != 0) - { - PUT_CODE (prev_insn, NOTE); - NOTE_LINE_NUMBER (prev_insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (prev_insn) = 0; - } - /* If jump target is the following label, and this is only use of it, - skip direct to that label and continue optimizing there. */ - label = insn; - while (label != 0 && GET_CODE (label) != CODE_LABEL) - label = NEXT_INSN (label); - if (label == XEXP (SET_SRC (x), 0) - && LABEL_NUSES (label) == 1) - cse_skip_to_next_block = 1; - } - } - - /* If this insn used to store a value based on CC0 but now value is constant, - and the previous insn just set CC0 for us, delete previous insn. - Here we use the fact that nothing expects CC0 to be valid over an insn, - which is true until the final pass. */ - if (GET_CODE (x) == SET && prev_insn_cc0 - && CONSTANT_P (SET_SRC (x))) - { - PUT_CODE (prev_insn, NOTE); - NOTE_LINE_NUMBER (prev_insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (prev_insn) = 0; - } - - prev_insn_explicit_cc0 = this_insn_explicit_cc0; - prev_insn_cc0 = this_insn_cc0; - prev_insn = insn; -} - -/* Store 1 in *WRITES_PTR for those categories of memory ref - that must be invalidated when the expression WRITTEN is stored in. - If WRITTEN is null, say everything must be invalidated. */ - -static void -note_mem_written (written, writes_ptr) - rtx written; - struct write_data *writes_ptr; -{ - static struct write_data everything = {1, 1, 1}; - - if (written == 0) - *writes_ptr = everything; - else if (GET_CODE (written) == MEM) - { - /* Pushing or popping the stack invalidates nothing. */ - rtx addr = XEXP (written, 0); - if ((GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC - || GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC) - && GET_CODE (XEXP (addr, 0)) == REG - && REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM) - return; - if (GET_MODE (written) == BLKmode) - *writes_ptr = everything; - else if (cse_rtx_addr_varies_p (written)) - { - /* A varying address that is a sum indicates an array element, - and that's just as good as a structure element - in implying that we need not invalidate scalar variables. */ - if (!(MEM_IN_STRUCT_P (written) - || GET_CODE (XEXP (written, 0)) == PLUS)) - writes_ptr->all = 1; - writes_ptr->nonscalar = 1; - } - writes_ptr->var = 1; - } -} - -/* Perform invalidation on the basis of everything about an insn - except for invalidating the actual places that are SET in it. - This includes the places CLOBBERed, and anything that might - alias with something that is SET or CLOBBERed. - - W points to the writes_memory for this insn, a struct write_data - saying which kinds of memory references must be invalidated. - X is the pattern of the insn. */ - -static void -invalidate_from_clobbers (w, x) - struct write_data *w; - rtx x; -{ - /* If W->var is not set, W specifies no action. - If W->all is set, this step gets all memory refs - so they can be ignored in the rest of this function. */ - if (w->var) - invalidate_memory (w); - - if (GET_CODE (x) == CLOBBER) - { - rtx ref = XEXP (x, 0); - if (ref - && (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG - || (GET_CODE (ref) == MEM && ! w->all))) - invalidate (ref); - } - else if (GET_CODE (x) == PARALLEL) - { - register int i; - for (i = XVECLEN (x, 0) - 1; i >= 0; i--) - { - register rtx y = XVECEXP (x, 0, i); - if (GET_CODE (y) == CLOBBER) - { - rtx ref = XEXP (y, 0); - if (ref - &&(GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG - || (GET_CODE (ref) == MEM && !w->all))) - invalidate (ref); - } - } - } -} - -/* Find the end of INSN's basic block, and return the cuid of its last insn - and the total number of SETs in all the insns of the block. */ - -struct cse_basic_block_data { int cuid, nsets; rtx last; }; - -static struct cse_basic_block_data -cse_end_of_basic_block (insn) - rtx insn; -{ - rtx p = insn; - struct cse_basic_block_data val; - int nsets = 0; - int last_uid = 0; - - /* Scan to end of this basic block. */ - while (p && GET_CODE (p) != CODE_LABEL) - { - /* Don't cse out the end of a loop. This makes a difference - only for the unusual loops that always execute at least once; - all other loops have labels there so we will stop in any case. - Cse'ing out the end of the loop is dangerous because it - might cause an invariant expression inside the loop - to be reused after the end of the loop. This would make it - hard to move the expression out of the loop in loop.c, - especially if it is one of several equivalent expressions - and loop.c would like to eliminate it. - The occasional optimizations lost by this will all come back - if loop and cse are made to work alternatingly. */ - if (GET_CODE (p) == NOTE - && NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END) - break; - - /* Don't cse over a call to setjmp; on some machines (eg vax) - the regs restored by the longjmp come from - a later time than the setjmp. */ - if (GET_CODE (p) == NOTE - && NOTE_LINE_NUMBER (p) == NOTE_INSN_SETJMP) - break; - - /* A PARALLEL can have lots of SETs in it, - especially if it is really an ASM_OPERANDS. */ - if (GET_CODE (p) == INSN && GET_CODE (PATTERN (p)) == PARALLEL) - nsets += XVECLEN (PATTERN (p), 0); - else - nsets += 1; - - last_uid = INSN_UID (p); - p = NEXT_INSN (p); - } - val.cuid = uid_cuid[last_uid]; - val.nsets = nsets; - val.last = p; - - return val; -} - -static rtx cse_basic_block (); - -/* Perform cse on the instructions of a function. - F is the first instruction. - NREGS is one plus the highest pseudo-reg number used in the instruction. - - Returns 1 if jump_optimize should be redone due to simplifications - in conditional jump instructions. */ - -int -cse_main (f, nregs) - /* f is the first instruction of a chain of insns for one function */ - rtx f; - /* nregs is the total number of registers used in it */ - int nregs; -{ - register rtx insn = f; - register int i; - - cse_jumps_altered = 0; - - init_recog (); - - max_reg = nregs; - - all_minus_one = (int *) alloca (nregs * sizeof (int)); - consec_ints = (int *) alloca (nregs * sizeof (int)); - for (i = 0; i < nregs; i++) - { - all_minus_one[i] = -1; - consec_ints[i] = i; - } - - reg_next_eqv = (int *) alloca (nregs * sizeof (int)); - reg_prev_eqv = (int *) alloca (nregs * sizeof (int)); - reg_qty = (int *) alloca (nregs * sizeof (int)); - reg_rtx = (rtx *) alloca (nregs * sizeof (rtx)); - reg_in_table = (int *) alloca (nregs * sizeof (int)); - reg_tick = (int *) alloca (nregs * sizeof (int)); - - /* Discard all the free elements of the previous function - since they are allocated in the temporarily obstack. */ - bzero (table, sizeof table); - free_element_chain = 0; - n_elements_made = 0; - - /* Find the largest uid. */ - - for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) - if (INSN_UID (insn) > i) - i = INSN_UID (insn); - - uid_cuid = (short *) alloca ((i + 1) * sizeof (short)); - bzero (uid_cuid, (i + 1) * sizeof (short)); - - /* Compute the mapping from uids to cuids. - CUIDs are numbers assigned to insns, like uids, - except that cuids increase monotonically through the code. - Don't assign cuids to line-number NOTEs, so that the distance in cuids - between two insns is not affected by -g. */ - - for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) != NOTE - || NOTE_LINE_NUMBER (insn) < 0) - INSN_CUID (insn) = ++i; - else - /* Give a line number note the same cuid as preceding insn. */ - INSN_CUID (insn) = i; - } - - /* Loop over basic blocks. - Compute the maximum number of qty's needed for each basic block - (which is 2 for each SET). */ - insn = f; - while (insn) - { - struct cse_basic_block_data val; - - val = cse_end_of_basic_block (insn); - - cse_basic_block_end = val.cuid; - cse_basic_block_start = INSN_CUID (insn); - max_qty = val.nsets * 2; - - /* Make MAX_QTY bigger to give us room to optimize - past the end of this basic block, if that should prove useful. */ - if (max_qty < 500) - max_qty = 500; - - max_qty += max_reg; - - insn = cse_basic_block (insn, val.last); -#ifdef USE_C_ALLOCA - alloca (0); -#endif - } - - /* Tell refers_to_mem_p that qty_const info is not available. */ - qty_const = 0; - - if (max_elements_made < n_elements_made) - max_elements_made = n_elements_made; - - return cse_jumps_altered; -} - -static rtx -cse_basic_block (from, to) - register rtx from, to; -{ - register rtx insn; - int *qv1 = (int *) alloca (max_qty * sizeof (int)); - int *qv2 = (int *) alloca (max_qty * sizeof (int)); - rtx *qv3 = (rtx *) alloca (max_qty * sizeof (rtx)); - - qty_first_reg = qv1; - qty_last_reg = qv2; - qty_const = qv3; - qty_const_insn = (rtx *) alloca (max_qty * sizeof (rtx)); - - new_basic_block (); - - cse_skip_to_next_block = 0; - - for (insn = from; insn != to; insn = NEXT_INSN (insn)) - { - register enum rtx_code code; - - code = GET_CODE (insn); - - if (code == INSN || code == JUMP_INSN || code == CALL_INSN) - cse_insn (insn); - /* Memory, and some registers, are invalidate by subroutine calls. */ - if (code == CALL_INSN) - { - register int i; - static struct write_data everything = {1, 1, 1}; - invalidate_memory (&everything); - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (call_used_regs[i] && reg_rtx[i] - && i != FRAME_POINTER_REGNUM - && i != ARG_POINTER_REGNUM) - invalidate (reg_rtx[i]); - } - /* Loop beginnings are often followed by jumps - (that enter the loop above the endtest). - See if we can prove the loop will be executed at least once; - if so, delete the jump. Also perhaps we can prove loop - will never be executed and delete the entire thing. */ - if (code == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG - && GET_CODE (NEXT_INSN (insn)) == JUMP_INSN) - { - predecide_loop_entry (insn); - /* Whether that jump was deleted or not, - it certainly is the end of the basic block. - Since the jump is unconditional, - it requires no further processing here. */ - break; - } - - /* See if it is ok to keep on going past the label - which used to end our basic block. */ - if (cse_skip_to_next_block - || (to != 0 && NEXT_INSN (insn) == to && LABEL_NUSES (to) == 0)) - { - struct cse_basic_block_data val; - - /* Skip the remaining insns in this block. */ - cse_skip_to_next_block = 0; - insn = to; - if (insn == 0) - break; - - /* Find the end of the following block. */ - val = cse_end_of_basic_block (NEXT_INSN (insn)); - - /* If the tables we allocated have enough space left - to handle all the SETs in the next basic block, - continue through it. Otherwise, return, - and that block will be scanned individually. */ - if (val.nsets * 2 + next_qty > max_qty) - break; - - cse_basic_block_end = val.cuid; - to = val.last; - } - } - - if (next_qty > max_qty) - abort (); - - return to ? NEXT_INSN (to) : 0; -} diff --git a/gnu/usr.bin/gcc1/cc1/dbxout.c b/gnu/usr.bin/gcc1/cc1/dbxout.c deleted file mode 100644 index 0cba1e382c..0000000000 --- a/gnu/usr.bin/gcc1/cc1/dbxout.c +++ /dev/null @@ -1,1201 +0,0 @@ -/* Output dbx-format symbol table information from GNU compiler. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* Output dbx-format symbol table data. - This consists of many symbol table entries, each of them - a .stabs assembler pseudo-op with four operands: - a "name" which is really a description of one symbol and its type, - a "code", which is a symbol defined in stab.h whose name starts with N_, - an unused operand always 0, - and a "value" which is an address or an offset. - The name is enclosed in doublequote characters. - - Each function, variable, typedef, and structure tag - has a symbol table entry to define it. - The beginning and end of each level of name scoping within - a function are also marked by special symbol table entries. - - The "name" consists of the symbol name, a colon, a kind-of-symbol letter, - and a data type number. The data type number may be followed by - "=" and a type definition; normally this will happen the first time - the type number is mentioned. The type definition may refer to - other types by number, and those type numbers may be followed - by "=" and nested definitions. - - This can make the "name" quite long. - When a name is more than 80 characters, we split the .stabs pseudo-op - into two .stabs pseudo-ops, both sharing the same "code" and "value". - The first one is marked as continued with a double-backslash at the - end of its "name". - - The kind-of-symbol letter distinguished function names from global - variables from file-scope variables from parameters from auto - variables in memory from typedef names from register variables. - See `dbxout_symbol'. - - The "code" is mostly redundant with the kind-of-symbol letter - that goes in the "name", but not entirely: for symbols located - in static storage, the "code" says which segment the address is in, - which controls how it is relocated. - - The "value" for a symbol in static storage - is the core address of the symbol (actually, the assembler - label for the symbol). For a symbol located in a stack slot - it is the stack offset; for one in a register, the register number. - For a typedef symbol, it is zero. - - If DEBUG_SYMS_TEXT is defined, all debugging symbols must be - output while in the text section. - - For more on data type definitions, see `dbxout_type'. */ - -#include "config.h" -#include "tree.h" -#include "rtl.h" -#include "flags.h" -#include - -/* Typical USG systems don't have stab.h, and they also have - no use for DBX-format debugging info. */ - -#ifdef DBX_DEBUGGING_INFO - -#ifdef DEBUG_SYMS_TEXT -#define FORCE_TEXT text_section (); -#else -#define FORCE_TEXT -#endif - -#ifdef USG -#include "stab.h" /* If doing DBX on sysV, use our own stab.h. */ -#else -#include /* On BSD, use the system's stab.h. */ -#endif /* not USG */ - -/* Stream for writing to assembler file. */ - -static FILE *asmfile; - -enum typestatus {TYPE_UNSEEN, TYPE_XREF, TYPE_DEFINED}; - -/* Vector recording the status of describing C data types. - When we first notice a data type (a tree node), - we assign it a number using next_type_number. - That is its index in this vector. - The vector element says whether we have yet output - the definition of the type. TYPE_XREF says we have - output it as a cross-reference only. */ - -enum typestatus *typevec; - -/* Number of elements of space allocated in `typevec'. */ - -static int typevec_len; - -/* In dbx output, each type gets a unique number. - This is the number for the next type output. - The number, once assigned, is in the TYPE_SYMTAB_ADDRESS field. */ - -static int next_type_number; - -/* In dbx output, we must assign symbol-blocks id numbers - in the order in which their beginnings are encountered. - We output debugging info that refers to the beginning and - end of the ranges of code in each block - with assembler labels LBBn and LBEn, where n is the block number. - The labels are generated in final, which assigns numbers to the - blocks in the same way. */ - -static int next_block_number; - -/* These variables are for dbxout_symbol to communicate to - dbxout_finish_symbol. - current_sym_code is the symbol-type-code, a symbol N_... define in stab.h. - current_sym_value and current_sym_addr are two ways to address the - value to store in the symtab entry. - current_sym_addr if nonzero represents the value as an rtx. - If that is zero, current_sym_value is used. This is used - when the value is an offset (such as for auto variables, - register variables and parms). */ - -static int current_sym_code; -static int current_sym_value; -static rtx current_sym_addr; - -/* Number of chars of symbol-description generated so far for the - current symbol. Used by CHARS and CONTIN. */ - -static int current_sym_nchars; - -/* Report having output N chars of the current symbol-description. */ - -#define CHARS(N) (current_sym_nchars += (N)) - -/* Break the current symbol-description, generating a continuation, - if it has become long. */ - -#ifndef DBX_CONTIN_LENGTH -#define DBX_CONTIN_LENGTH 80 -#endif - -#if DBX_CONTIN_LENGTH > 0 -#define CONTIN \ - do {if (current_sym_nchars > DBX_CONTIN_LENGTH) dbxout_continue ();} while (0) -#else -#define CONTIN -#endif - -void dbxout_types (); -void dbxout_tags (); -void dbxout_args (); -void dbxout_symbol (); -static void dbxout_type_name (); -static void dbxout_type (); -static void dbxout_finish_symbol (); -static void dbxout_continue (); - -/* At the beginning of compilation, start writing the symbol table. - Initialize `typevec' and output the standard data types of C. */ - -void -dbxout_init (asm_file, input_file_name) - FILE *asm_file; - char *input_file_name; -{ - asmfile = asm_file; - - typevec_len = 100; - typevec = (enum typestatus *) xmalloc (typevec_len * sizeof typevec[0]); - bzero (typevec, typevec_len * sizeof typevec[0]); - - /* Used to put `Ltext:' before the reference, but that loses on sun 4. */ - fprintf (asmfile, - "\t.stabs \"%s\",%d,0,0,Ltext\nLtext:\n", - input_file_name, N_SO); - - next_type_number = 1; - next_block_number = 2; - - /* Make sure that types `int' and `char' have numbers 1 and 2. - Definitions of other integer types will refer to those numbers. */ - - dbxout_symbol (TYPE_NAME (integer_type_node), 0); - dbxout_symbol (TYPE_NAME (char_type_node), 0); - - /* Get all permanent types not yet gotten, and output them. */ - - dbxout_types (get_permanent_types ()); -} - -/* Continue a symbol-description that gets too big. - End one symbol table entry with a double-backslash - and start a new one, eventually producing something like - .stabs "start......\\",code,0,value - .stabs "...rest",code,0,value */ - -static void -dbxout_continue () -{ -#ifdef DBX_CONTIN_CHAR - fprintf (asmfile, "%c", DBX_CONTIN_CHAR); -#else - fprintf (asmfile, "\\\\"); -#endif - dbxout_finish_symbol (); - fprintf (asmfile, ".stabs \""); - current_sym_nchars = 0; -} - -/* Output a reference to a type. If the type has not yet been - described in the dbx output, output its definition now. - For a type already defined, just refer to its definition - using the type number. - - If FULL is nonzero, and the type has been described only with - a forward-reference, output the definition now. - If FULL is zero in this case, just refer to the forward-reference - using the number previously allocated. */ - -static void -dbxout_type (type, full) - tree type; - int full; -{ - register tree tem; - - /* If there was an input error and we don't really have a type, - avoid crashing and write something that is at least valid - by assuming `int'. */ - if (type == error_mark_node) - type = integer_type_node; - else - type = TYPE_MAIN_VARIANT (type); - - if (TYPE_SYMTAB_ADDRESS (type) == 0) - { - /* Type has no dbx number assigned. Assign next available number. */ - TYPE_SYMTAB_ADDRESS (type) = next_type_number++; - - /* Make sure type vector is long enough to record about this type. */ - - if (next_type_number == typevec_len) - { - typevec = (enum typestatus *) xrealloc (typevec, typevec_len * 2 * sizeof typevec[0]); - bzero (typevec + typevec_len, typevec_len * sizeof typevec[0]); - typevec_len *= 2; - } - } - - /* Output the number of this type, to refer to it. */ - fprintf (asmfile, "%d", TYPE_SYMTAB_ADDRESS (type)); - CHARS (3); - - /* If this type's definition has been output or is now being output, - that is all. */ - - switch (typevec[TYPE_SYMTAB_ADDRESS (type)]) - { - case TYPE_UNSEEN: - break; - case TYPE_XREF: - if (! full) - return; - break; - case TYPE_DEFINED: - return; - } - -#ifdef DBX_NO_XREFS - /* For systems where dbx output does not allow the `=xsNAME:' syntax, - leave the type-number completely undefined rather than output - a cross-reference. */ - if (TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE - || TREE_CODE (type) == ENUMERAL_TYPE) - - if ((TYPE_NAME (type) != 0 && !full) - || TYPE_SIZE (type) == 0) - { - typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF; - return; - } -#endif - - /* Output a definition now. */ - - fprintf (asmfile, "="); - CHARS (1); - - /* Mark it as defined, so that if it is self-referent - we will not get into an infinite recursion of definitions. */ - - typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_DEFINED; - - switch (TREE_CODE (type)) - { - case VOID_TYPE: - /* For a void type, just define it as itself; ie, "5=5". - This makes us consider it defined - without saying what it is. The debugger will make it - a void type when the reference is seen, and nothing will - ever override that default. */ - fprintf (asmfile, "%d", TYPE_SYMTAB_ADDRESS (type)); - CHARS (3); - break; - - case INTEGER_TYPE: - if (type == char_type_node && ! TREE_UNSIGNED (type)) - /* Output the type `char' as a subrange of itself! - I don't understand this definition, just copied it - from the output of pcc. */ - fprintf (asmfile, "r2;0;127;"); - else - /* Output other integer types as subranges of `int'. */ - fprintf (asmfile, "r1;%d;%d;", - TREE_INT_CST_LOW (TYPE_MIN_VALUE (type)), - TREE_INT_CST_LOW (TYPE_MAX_VALUE (type))); - CHARS (25); - break; - - case REAL_TYPE: - /* This must be magic. */ - fprintf (asmfile, "r1;%d;0;", - TREE_INT_CST_LOW (size_in_bytes (type))); - CHARS (16); - break; - - case ARRAY_TYPE: - /* Output "a" followed by a range type definition - for the index type of the array - followed by a reference to the target-type. - ar1;0;N;M for an array of type M and size N. */ - fprintf (asmfile, "ar1;0;%d;", - (TYPE_DOMAIN (type) - ? TREE_INT_CST_LOW (TYPE_MAX_VALUE (TYPE_DOMAIN (type))) - : -1)); - CHARS (17); - dbxout_type (TREE_TYPE (type), 0); - break; - - case RECORD_TYPE: - case UNION_TYPE: - /* Output a structure type. */ - if ((TYPE_NAME (type) != 0 && !full) - || TYPE_SIZE (type) == 0) - { - /* If the type is just a cross reference, output one - and mark the type as partially described. - If it later becomes defined, we will output - its real definition. - If the type has a name, don't nest its definition within - another type's definition; instead, output an xref - and let the definition come when the name is defined. */ - fprintf (asmfile, (TREE_CODE (type) == RECORD_TYPE) ? "xs" : "xu"); - CHARS (3); -#if 0 /* This assertion is legitimately false in C++. */ - /* We shouldn't be outputting a reference to a type before its - definition unless the type has a tag name. - A typedef name without a tag name should be impossible. */ - if (TREE_CODE (TYPE_NAME (type)) != IDENTIFIER_NODE) - abort (); -#endif - dbxout_type_name (type); - fprintf (asmfile, ":"); - typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF; - break; - } - tem = size_in_bytes (type); - fprintf (asmfile, (TREE_CODE (type) == RECORD_TYPE) ? "s%d" : "u%d", - TREE_INT_CST_LOW (tem)); - - if (TYPE_BASETYPES (type) && use_gdb_dbx_extensions) - { - putc ('!', asmfile); - putc ((TREE_PUBLIC (TYPE_BASETYPES (type)) ? '2' : '0'), - asmfile); - dbxout_type (TREE_VALUE (TYPE_BASETYPES (type)), 0); - putc (',', asmfile); - CHARS (3); - } - CHARS (11); - - for (tem = TYPE_FIELDS (type); tem; tem = TREE_CHAIN (tem)) - /* Output the name, type, position (in bits), size (in bits) - of each field. */ - /* Omit here the nameless fields that are used to skip bits. */ - if (DECL_NAME (tem) != 0) - { - /* Continue the line if necessary, - but not before the first field. */ - if (tem != TYPE_FIELDS (type)) - CONTIN; - fprintf (asmfile, "%s:", IDENTIFIER_POINTER (DECL_NAME (tem))); - CHARS (2 + IDENTIFIER_LENGTH (DECL_NAME (tem))); -#ifdef TREE_PRIVATE - if (use_gdb_dbx_extensions - && (TREE_PRIVATE (tem) || TREE_PROTECTED (tem) - || TREE_CODE (tem) != FIELD_DECL)) - { - putc ('/', asmfile); - putc ((TREE_PRIVATE (tem) ? '0' - : TREE_PROTECTED (tem) ? '1' : '2'), - asmfile); - CHARS (2); - if (TREE_CODE (tem) == FUNCTION_DECL) - { - putc (':', asmfile); - CHARS (1); - dbxout_type (TREE_TYPE (tem), 0); /* FUNCTION_TYPE */ - dbxout_args (TYPE_ARG_TYPES (TREE_TYPE (tem))); -#ifdef TREE_VIRTUAL - fprintf (asmfile, ":%s;%c", - XSTR (XEXP (DECL_RTL (tem), 0), 0), - TREE_VIRTUAL (tem) ? '*' : '.'); -#endif - CHARS (3 + strlen (XSTR (XEXP (DECL_RTL (tem), 0), 0))); - } - else - dbxout_type (TREE_TYPE (tem), 0); - } - else -#endif - dbxout_type (TREE_TYPE (tem), 0); - - if (TREE_CODE (tem) == VAR_DECL) - { - if (use_gdb_dbx_extensions) - { - fprintf (asmfile, ":%s", - XSTR (XEXP (DECL_RTL (tem), 0), 0)); - CHARS (2 + strlen (XSTR (XEXP (DECL_RTL (tem), 0), 0))); - } - else - { - fprintf (asmfile, ",0,0;"); - CHARS (5); - } - } - else - { - fprintf (asmfile, ",%d,%d;", DECL_OFFSET (tem), - (TREE_INT_CST_LOW (DECL_SIZE (tem)) - * DECL_SIZE_UNIT (tem))); - CHARS (23); - } - } - - putc (';', asmfile); - CHARS (1); - break; - - case ENUMERAL_TYPE: - if ((TYPE_NAME (type) != 0 && !full) - || TYPE_SIZE (type) == 0) - { - fprintf (asmfile, "xe"); - CHARS (3); - dbxout_type_name (type); - typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF; - fprintf (asmfile, ":"); - return; - } - putc ('e', asmfile); - CHARS (1); - for (tem = TYPE_VALUES (type); tem; tem = TREE_CHAIN (tem)) - { - fprintf (asmfile, "%s:%d,", IDENTIFIER_POINTER (TREE_PURPOSE (tem)), - TREE_INT_CST_LOW (TREE_VALUE (tem))); - CHARS (11 + IDENTIFIER_LENGTH (TREE_PURPOSE (tem))); - if (TREE_CHAIN (tem) != 0) - CONTIN; - } - putc (';', asmfile); - CHARS (1); - break; - - case POINTER_TYPE: - putc ('*', asmfile); - CHARS (1); - dbxout_type (TREE_TYPE (type), 0); - break; - - case METHOD_TYPE: - if (use_gdb_dbx_extensions) - { - putc ('@', asmfile); - CHARS (1); - dbxout_type (TYPE_METHOD_BASETYPE (type), 0); - putc (',', asmfile); - CHARS (1); - dbxout_type (TREE_TYPE (type), 0); - } - else - { - /* Treat it as a function type. */ - dbxout_type (TREE_TYPE (type), 0); - } - break; - - case OFFSET_TYPE: - if (use_gdb_dbx_extensions) - { - putc ('@', asmfile); - CHARS (1); - dbxout_type (TYPE_OFFSET_BASETYPE (type), 0); - putc (',', asmfile); - CHARS (1); - dbxout_type (TREE_TYPE (type), 0); - } - else - { - /* Treat it as a function type. */ - dbxout_type (TREE_TYPE (type), 0); - } - break; - - case REFERENCE_TYPE: - putc (use_gdb_dbx_extensions ? '&' : '*', asmfile); - CHARS (1); - dbxout_type (TREE_TYPE (type), 0); - break; - - case FUNCTION_TYPE: - putc ('f', asmfile); - CHARS (1); - dbxout_type (TREE_TYPE (type), 0); - break; - - default: - abort (); - } -} - -/* Output the name of type TYPE, with no punctuation. - Such names can be set up either by typedef declarations - or by struct, enum and union tags. */ - -static void -dbxout_type_name (type) - register tree type; -{ - tree t; - if (TYPE_NAME (type) == 0) - abort (); - if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) - { - t = TYPE_NAME (type); - } - else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL) - { - t = DECL_NAME (TYPE_NAME (type)); - } - else - abort (); - - fprintf (asmfile, "%s", IDENTIFIER_POINTER (t)); - CHARS (IDENTIFIER_LENGTH (t)); -} - -/* Output a .stabs for the symbol defined by DECL, - which must be a ..._DECL node in the normal namespace. - It may be a CONST_DECL, a FUNCTION_DECL, a PARM_DECL or a VAR_DECL. - LOCAL is nonzero if the scope is less than the entire file. */ - -void -dbxout_symbol (decl, local) - tree decl; - int local; -{ - int letter = 0; - tree type = TREE_TYPE (decl); - - /* If global, first output all types and all - struct, enum and union tags that have been created - and not yet output. */ - - if (local == 0) - { - dbxout_tags (gettags ()); - dbxout_types (get_permanent_types ()); - } - - current_sym_code = 0; - current_sym_value = 0; - current_sym_addr = 0; - - /* The output will always start with the symbol name, - so count that always in the length-output-so-far. */ - - current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (decl)); - - switch (TREE_CODE (decl)) - { - case CONST_DECL: - /* Enum values are defined by defining the enum type. */ - break; - - case FUNCTION_DECL: - if (DECL_RTL (decl) == 0) - return; - if (TREE_EXTERNAL (decl)) - break; - if (GET_CODE (DECL_RTL (decl)) != MEM - || GET_CODE (XEXP (DECL_RTL (decl), 0)) != SYMBOL_REF) - break; - FORCE_TEXT; - fprintf (asmfile, ".stabs \"%s:%c", - IDENTIFIER_POINTER (DECL_NAME (decl)), - TREE_PUBLIC (decl) ? 'F' : 'f'); - - current_sym_code = N_FUN; - current_sym_addr = XEXP (DECL_RTL (decl), 0); - - if (TREE_TYPE (TREE_TYPE (decl))) - dbxout_type (TREE_TYPE (TREE_TYPE (decl)), 0); - else - dbxout_type (void_type_node, 0); - dbxout_finish_symbol (); - break; - - case TYPE_DECL: -#if 0 - /* This seems all wrong. Outputting most kinds of types gives no name - at all. A true definition gives no name; a cross-ref for a - structure can give the tag name, but not a type name. - It seems that no typedef name is defined by outputting a type. */ - - /* If this typedef name was defined by outputting the type, - don't duplicate it. */ - if (typevec[TYPE_SYMTAB_ADDRESS (type)] == TYPE_DEFINED - && TYPE_NAME (TREE_TYPE (decl)) == decl) - return; -#endif - /* Don't output the same typedef twice. */ - if (TREE_ASM_WRITTEN (decl)) - return; - - /* Output typedef name. */ - FORCE_TEXT; - fprintf (asmfile, ".stabs \"%s:t", - IDENTIFIER_POINTER (DECL_NAME (decl))); - - current_sym_code = N_LSYM; - - dbxout_type (TREE_TYPE (decl), 1); - dbxout_finish_symbol (); - - /* Prevent duplicate output of a typedef. */ - TREE_ASM_WRITTEN (decl) = 1; - break; - - case PARM_DECL: - /* Parm decls go in their own separate chains - and are output by dbxout_reg_parms and dbxout_parms. */ - abort (); - - case VAR_DECL: - if (DECL_RTL (decl) == 0) - return; - /* Don't mention a variable that is external. - Let the file that defines it describe it. */ - if (TREE_EXTERNAL (decl)) - break; - - /* Don't mention a variable at all - if it was completely optimized into nothingness. */ - if (GET_CODE (DECL_RTL (decl)) == REG - && (REGNO (DECL_RTL (decl)) < 0 - || REGNO (DECL_RTL (decl)) >= FIRST_PSEUDO_REGISTER)) - break; - - /* The kind-of-variable letter depends on where - the variable is and on the scope of its name: - G and N_GSYM for static storage and global scope, - S for static storage and file scope, - V for static storage and local scope, - for those two, use N_LCSYM if data is in bss segment, - N_STSYM if in data segment, N_FUN otherwise. - (We used N_FUN originally, then changed to N_STSYM - to please GDB. However, it seems that confused ld. - Now GDB has been fixed to like N_FUN, says Kingdon.) - no letter at all, and N_LSYM, for auto variable, - r and N_RSYM for register variable. */ - - if (GET_CODE (DECL_RTL (decl)) == MEM - && GET_CODE (XEXP (DECL_RTL (decl), 0)) == SYMBOL_REF) - { - if (TREE_PUBLIC (decl)) - { - letter = 'G'; - current_sym_code = N_GSYM; - } - else - { - current_sym_addr = XEXP (DECL_RTL (decl), 0); - - letter = TREE_PERMANENT (decl) ? 'S' : 'V'; - - if (!DECL_INITIAL (decl)) - current_sym_code = N_LCSYM; - else if (TREE_READONLY (decl) && ! TREE_VOLATILE (decl)) - /* This is not quite right, but it's the closest - of all the codes that Unix defines. */ - current_sym_code = N_FUN; - else - { -/* Ultrix `as' seems to need this. */ -#ifdef DBX_STATIC_STAB_DATA_SECTION - data_section (); -#endif - current_sym_code = N_STSYM; - } - } - } - else if (GET_CODE (DECL_RTL (decl)) == REG) - { - letter = 'r'; - current_sym_code = N_RSYM; - current_sym_value = DBX_REGISTER_NUMBER (REGNO (DECL_RTL (decl))); - } - else if (GET_CODE (DECL_RTL (decl)) == SUBREG) - { - rtx value = DECL_RTL (decl); - int offset = 0; - while (GET_CODE (value) == SUBREG) - { - offset += SUBREG_WORD (value); - value = SUBREG_REG (value); - } - letter = 'r'; - current_sym_code = N_RSYM; - current_sym_value = DBX_REGISTER_NUMBER (REGNO (value) + offset); - } - else if (GET_CODE (DECL_RTL (decl)) == MEM - && (GET_CODE (XEXP (DECL_RTL (decl), 0)) == MEM - || (GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG - && REGNO (XEXP (DECL_RTL (decl), 0)) != FRAME_POINTER_REGNUM))) - /* If the value is indirect by memory or by a register - that isn't the frame pointer - then it means the object is variable-sized and address through - that register or stack slot. DBX has no way to represent this - so all we can do is output the variable as a pointer. - If it's not a parameter, ignore it. - (VAR_DECLs like this can be made by integrate.c.) */ - { - if (GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG) - { - letter = 'r'; - current_sym_code = N_RSYM; - current_sym_value = DBX_REGISTER_NUMBER (REGNO (XEXP (DECL_RTL (decl), 0))); - } - else - { - current_sym_code = N_LSYM; - /* DECL_RTL looks like (MEM (MEM (PLUS (REG...) (CONST_INT...)))). - We want the value of that CONST_INT. */ - current_sym_value = INTVAL (XEXP (XEXP (XEXP (DECL_RTL (decl), 0), 0), 1)); - } - - /* Effectively do build_pointer_type, but don't cache this type, - since it might be temporary whereas the type it points to - might have been saved for inlining. */ - type = make_node (POINTER_TYPE); - TREE_TYPE (type) = TREE_TYPE (decl); - } - else if (GET_CODE (DECL_RTL (decl)) == MEM - && GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG) - { - current_sym_code = N_LSYM; - current_sym_value = 0; - } - else if (GET_CODE (DECL_RTL (decl)) == MEM - && GET_CODE (XEXP (DECL_RTL (decl), 0)) == PLUS - && GET_CODE (XEXP (XEXP (DECL_RTL (decl), 0), 1)) == CONST_INT) - { - current_sym_code = N_LSYM; - /* DECL_RTL looks like (MEM (PLUS (REG...) (CONST_INT...))) - We want the value of that CONST_INT. */ - current_sym_value = INTVAL (XEXP (XEXP (DECL_RTL (decl), 0), 1)); - } - else - /* Address might be a MEM, when DECL is a variable-sized object. - Or it might be const0_rtx, meaning previous passes - want us to ignore this variable. */ - break; - - /* Ok, start a symtab entry and output the variable name. */ - FORCE_TEXT; - fprintf (asmfile, ".stabs \"%s:", - IDENTIFIER_POINTER (DECL_NAME (decl))); - if (letter) putc (letter, asmfile); - dbxout_type (type, 0); - dbxout_finish_symbol (); - break; - } -} - -static void -dbxout_finish_symbol () -{ - fprintf (asmfile, "\",%d,0,0,", current_sym_code); - if (current_sym_addr) - output_addr_const (asmfile, current_sym_addr); - else - fprintf (asmfile, "%d", current_sym_value); - putc ('\n', asmfile); -} - -/* Output definitions of all the decls in a chain. */ - -static void -dbxout_syms (syms) - tree syms; -{ - while (syms) - { - dbxout_symbol (syms, 1); - syms = TREE_CHAIN (syms); - } -} - -/* The following two functions output definitions of function parameters. - Each parameter gets a definition locating it in the parameter list. - Each parameter that is a register variable gets a second definition - locating it in the register. - - Printing or argument lists in gdb uses the definitions that - locate in the parameter list. But reference to the variable in - expressions uses preferentially the definition as a register. */ - -/* Output definitions, referring to storage in the parmlist, - of all the parms in PARMS, which is a chain of PARM_DECL nodes. */ - -static void -dbxout_parms (parms) - tree parms; -{ - for (; parms; parms = TREE_CHAIN (parms)) - { - if (DECL_OFFSET (parms) >= 0) - { - current_sym_code = N_PSYM; - current_sym_value = DECL_OFFSET (parms) / BITS_PER_UNIT; - current_sym_addr = 0; - current_sym_nchars = 2 + strlen (IDENTIFIER_POINTER (DECL_NAME (parms))); - - FORCE_TEXT; - fprintf (asmfile, ".stabs \"%s:p", - IDENTIFIER_POINTER (DECL_NAME (parms))); - - if (GET_CODE (DECL_RTL (parms)) == REG - && REGNO (DECL_RTL (parms)) >= 0 - && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER) - dbxout_type (DECL_ARG_TYPE (parms), 0); - else - { - /* This is the case where the parm is passed as an int or double - and it is converted to a char, short or float and stored back - in the parmlist. In this case, describe the parm - with the variable's declared type, and adjust the address - if the least significant bytes (which we are using) are not - the first ones. */ -#ifdef BYTES_BIG_ENDIAN - if (TREE_TYPE (parms) != DECL_ARG_TYPE (parms)) - current_sym_value += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms))) - - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms)))); -#endif - - if (GET_CODE (DECL_RTL (parms)) == MEM - && GET_CODE (XEXP (DECL_RTL (parms), 0)) == PLUS - && GET_CODE (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == CONST_INT - && INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == current_sym_value) - dbxout_type (TREE_TYPE (parms), 0); - else - { - current_sym_value = DECL_OFFSET (parms) / BITS_PER_UNIT; - dbxout_type (DECL_ARG_TYPE (parms), 0); - } - } - dbxout_finish_symbol (); - } - /* Parm was passed in registers. - If it lives in a hard register, output a "regparm" symbol - for the register it lives in. */ - else if (GET_CODE (DECL_RTL (parms)) == REG - && REGNO (DECL_RTL (parms)) >= 0 - && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER) - { - current_sym_code = N_RSYM; - current_sym_value = DBX_REGISTER_NUMBER (REGNO (DECL_RTL (parms))); - current_sym_addr = 0; - current_sym_nchars = 2 + strlen (IDENTIFIER_POINTER (DECL_NAME (parms))); - - FORCE_TEXT; - fprintf (asmfile, ".stabs \"%s:P", - IDENTIFIER_POINTER (DECL_NAME (parms))); - - dbxout_type (DECL_ARG_TYPE (parms), 0); - dbxout_finish_symbol (); - } - else if (GET_CODE (DECL_RTL (parms)) == MEM - && XEXP (DECL_RTL (parms), 0) != const0_rtx) - { - current_sym_code = N_LSYM; - /* DECL_RTL looks like (MEM (PLUS (REG...) (CONST_INT...))). - We want the value of that CONST_INT. */ - current_sym_value = INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)); - current_sym_addr = 0; - current_sym_nchars = 2 + strlen (IDENTIFIER_POINTER (DECL_NAME (parms))); - - FORCE_TEXT; - fprintf (asmfile, ".stabs \"%s:p", - IDENTIFIER_POINTER (DECL_NAME (parms))); - -#if 0 /* This is actually the case in which a parameter - is passed in registers but lives on the stack in a local slot. - The address we are using is already correct, so don't change it. */ - - /* This is the case where the parm is passed as an int or double - and it is converted to a char, short or float and stored back - in the parmlist. In this case, describe the parm - with the variable's declared type, and adjust the address - if the least significant bytes (which we are using) are not - the first ones. */ -#ifdef BYTES_BIG_ENDIAN - if (TREE_TYPE (parms) != DECL_ARG_TYPE (parms)) - current_sym_value += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms))) - - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms)))); -#endif -#endif /* 0 */ - - dbxout_type (TREE_TYPE (parms), 0); - dbxout_finish_symbol (); - } - } -} - -/* Output definitions, referring to registers, - of all the parms in PARMS which are stored in registers during the function. - PARMS is a chain of PARM_DECL nodes. */ - -static void -dbxout_reg_parms (parms) - tree parms; -{ - while (parms) - { - /* Report parms that live in registers during the function. */ - if (GET_CODE (DECL_RTL (parms)) == REG - && REGNO (DECL_RTL (parms)) >= 0 - && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER - && DECL_OFFSET (parms) >= 0) - { - current_sym_code = N_RSYM; - current_sym_value = DBX_REGISTER_NUMBER (REGNO (DECL_RTL (parms))); - current_sym_addr = 0; - current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms)); - FORCE_TEXT; - fprintf (asmfile, ".stabs \"%s:r", - IDENTIFIER_POINTER (DECL_NAME (parms))); - dbxout_type (TREE_TYPE (parms), 0); - dbxout_finish_symbol (); - } - /* Report parms that live in memory but outside the parmlist. */ - else if (GET_CODE (DECL_RTL (parms)) == MEM - && GET_CODE (XEXP (DECL_RTL (parms), 0)) == PLUS - && GET_CODE (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == CONST_INT) - { - int offset = DECL_OFFSET (parms) / BITS_PER_UNIT; - /* A parm declared char is really passed as an int, - so it occupies the least significant bytes. - On a big-endian machine those are not the low-numbered ones. */ -#ifdef BYTES_BIG_ENDIAN - if (offset != -1 && TREE_TYPE (parms) != DECL_ARG_TYPE (parms)) - offset += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms))) - - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms)))); -#endif - if (INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)) != offset) - { - current_sym_code = N_LSYM; - current_sym_value = INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)); - current_sym_addr = 0; - current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms)); - FORCE_TEXT; - fprintf (asmfile, ".stabs \"%s:", - IDENTIFIER_POINTER (DECL_NAME (parms))); - dbxout_type (TREE_TYPE (parms), 0); - dbxout_finish_symbol (); - } - } - parms = TREE_CHAIN (parms); - } -} - -/* Given a chain of ..._TYPE nodes (as come in a parameter list), - output definitions of those names, in raw form */ - -void -dbxout_args (args) - tree args; -{ - while (args) - { - putc (',', asmfile); - dbxout_type (TREE_VALUE (args), 0); - CHARS (1); - args = TREE_CHAIN (args); - } -} - -/* Given a chain of ..._TYPE nodes, - find those which have typedef names and output those names. - This is to ensure those types get output. */ - -void -dbxout_types (types) - register tree types; -{ - while (types) - { - if (TYPE_NAME (types) - && TREE_CODE (TYPE_NAME (types)) == TYPE_DECL - && ! TREE_ASM_WRITTEN (TYPE_NAME (types))) - dbxout_symbol (TYPE_NAME (types), 1); - types = TREE_CHAIN (types); - } -} - -/* Output the tags (struct, union and enum definitions with names) for a block, - given a list of them (a chain of TREE_LIST nodes) in TAGS. - We must check to include those that have been mentioned already with - only a cross-reference. */ - -void -dbxout_tags (tags) - tree tags; -{ - register tree link; - for (link = tags; link; link = TREE_CHAIN (link)) - { - register tree type = TYPE_MAIN_VARIANT (TREE_VALUE (link)); - if (TREE_PURPOSE (link) != 0 - && ! TREE_ASM_WRITTEN (link) - && TYPE_SIZE (type) != 0) - { - TREE_ASM_WRITTEN (link) = 1; - current_sym_code = N_LSYM; - current_sym_value = 0; - current_sym_addr = 0; - current_sym_nchars = 2 + IDENTIFIER_LENGTH (TREE_PURPOSE (link)); - - FORCE_TEXT; - fprintf (asmfile, ".stabs \"%s:T", - IDENTIFIER_POINTER (TREE_PURPOSE (link))); - dbxout_type (type, 1); - dbxout_finish_symbol (); - } - } -} - -/* Output everything about a symbol block (that is to say, a LET_STMT node - that represents a scope level), - including recursive output of contained blocks. - - STMT is the LET_STMT node. - DEPTH is its depth within containing symbol blocks. - ARGS is usually zero; but for the outermost block of the - body of a function, it is a chain of PARM_DECLs for the function parameters. - We output definitions of all the register parms - as if they were local variables of that block. - - Actually, STMT may be several statements chained together. - We handle them all in sequence. */ - -static void -dbxout_block (stmt, depth, args) - register tree stmt; - int depth; - tree args; -{ - int blocknum; - - while (stmt) - { - switch (TREE_CODE (stmt)) - { - case COMPOUND_STMT: - case LOOP_STMT: - dbxout_block (STMT_BODY (stmt), depth, 0); - break; - - case IF_STMT: - dbxout_block (STMT_THEN (stmt), depth, 0); - dbxout_block (STMT_ELSE (stmt), depth, 0); - break; - - case LET_STMT: - /* Ignore LET_STMTs for blocks never really used to make RTL. */ - if (! TREE_USED (stmt)) - break; - /* In dbx format, the syms of a block come before the N_LBRAC. */ - dbxout_tags (STMT_TYPE_TAGS (stmt)); - dbxout_syms (STMT_VARS (stmt)); - if (args) - dbxout_reg_parms (args); - - /* Now output an N_LBRAC symbol to represent the beginning of - the block. Use the block's tree-walk order to generate - the assembler symbols LBBn and LBEn - that final will define around the code in this block. */ - if (depth > 0) - { - char buf[20]; - blocknum = next_block_number++; - ASM_GENERATE_INTERNAL_LABEL (buf, "LBB", blocknum); - fprintf (asmfile, ".stabn %d,0,0,", N_LBRAC); - assemble_name (asmfile, buf); - fprintf (asmfile, "\n"); - } - - /* Output the subblocks. */ - dbxout_block (STMT_SUBBLOCKS (stmt), depth + 1, 0); - - /* Refer to the marker for the end of the block. */ - if (depth > 0) - { - char buf[20]; - ASM_GENERATE_INTERNAL_LABEL (buf, "LBE", blocknum); - fprintf (asmfile, ".stabn %d,0,0,", N_RBRAC); - assemble_name (asmfile, buf); - fprintf (asmfile, "\n"); - } - } - stmt = TREE_CHAIN (stmt); - } -} - -/* Output dbx data for a function definition. - This includes a definition of the function name itself (a symbol), - definitions of the parameters (locating them in the parameter list) - and then output the block that makes up the function's body - (including all the auto variables of the function). */ - -void -dbxout_function (decl) - tree decl; -{ - dbxout_symbol (decl, 0); - dbxout_parms (DECL_ARGUMENTS (decl)); - dbxout_block (DECL_INITIAL (decl), 0, DECL_ARGUMENTS (decl)); - - /* If we made any temporary types in this fn that weren't - output, output them now. */ - dbxout_types (get_temporary_types ()); -} - -#else /* not DBX_DEBUGGING_INFO */ - -void -dbxout_init (asm_file, input_file_name) - FILE *asm_file; - char *input_file_name; -{} - -void -dbxout_symbol (decl, local) - tree decl; - int local; -{} - -void -dbxout_types (types) - register tree types; -{} - -void -dbxout_tags (tags) - tree tags; -{} - -void -dbxout_function (decl) - tree decl; -{} - -#endif /* DBX_DEBUGGING_INFO */ diff --git a/gnu/usr.bin/gcc1/cc1/emit-rtl.c b/gnu/usr.bin/gcc1/cc1/emit-rtl.c deleted file mode 100644 index 6d1ea3b959..0000000000 --- a/gnu/usr.bin/gcc1/cc1/emit-rtl.c +++ /dev/null @@ -1,1633 +0,0 @@ -/* Emit RTL for the GNU C-Compiler expander. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* Middle-to-low level generation of rtx code and insns. - - This file contains the functions `gen_rtx', `gen_reg_rtx' - and `gen_label_rtx' that are the usual ways of creating rtl - expressions for most purposes. - - It also has the functions for creating insns and linking - them in the doubly-linked chain. - - The patterns of the insns are created by machine-dependent - routines in insn-emit.c, which is generated automatically from - the machine description. These routines use `gen_rtx' to make - the individual rtx's of the pattern; what is machine dependent - is the kind of rtx's they make and what arguments they use. */ - -#include "config.h" -#include -#include "gvarargs.h" -#include "rtl.h" -#include "regs.h" -#include "insn-config.h" -#include "real.h" - -#define max(A,B) ((A) > (B) ? (A) : (B)) -#define min(A,B) ((A) < (B) ? (A) : (B)) - -/* This is reset to FIRST_PSEUDO_REGISTER at the start each function. - After rtl generation, it is 1 plus the largest register number used. */ - -int reg_rtx_no = FIRST_PSEUDO_REGISTER; - -/* This is *not* reset after each function. It gives each CODE_LABEL - in the entire compilation a unique label number. */ - -static int label_num = 1; - -/* Value of `label_num' at start of current function. */ - -static int first_label_num; - -/* Nonzero means do not generate NOTEs for source line numbers. */ - -static int no_line_numbers; - -/* Commonly used rtx's, so that we only need space for one copy. - These are initialized once for the entire compilation. - All of these except perhaps fconst0_rtx and dconst0_rtx - are unique; no other rtx-object will be equal to any of these. */ - -rtx pc_rtx; /* (PC) */ -rtx cc0_rtx; /* (CC0) */ -rtx cc1_rtx; /* (CC1) (not actually used nowadays) */ -rtx const0_rtx; /* (CONST_INT 0) */ -rtx const1_rtx; /* (CONST_INT 1) */ -rtx fconst0_rtx; /* (CONST_DOUBLE:SF 0) */ -rtx dconst0_rtx; /* (CONST_DOUBLE:DF 0) */ - -/* All references to the following fixed hard registers go through - these unique rtl objects. On machines where the frame-pointer and - arg-pointer are the same register, they use the same unique object. - - After register allocation, other rtl objects which used to be pseudo-regs - may be clobbered to refer to the frame-pointer register. - But references that were originally to the frame-pointer can be - distinguished from the others because they contain frame_pointer_rtx. - - In an inline procedure, the stack and frame pointer rtxs may not be - used for anything else. */ -rtx stack_pointer_rtx; /* (REG:Pmode STACK_POINTER_REGNUM) */ -rtx frame_pointer_rtx; /* (REG:Pmode FRAME_POINTER_REGNUM) */ -rtx arg_pointer_rtx; /* (REG:Pmode ARG_POINTER_REGNUM) */ -rtx struct_value_rtx; /* (REG:Pmode STRUCT_VALUE_REGNUM) */ -rtx struct_value_incoming_rtx; /* (REG:Pmode STRUCT_VALUE_INCOMING_REGNUM) */ -rtx static_chain_rtx; /* (REG:Pmode STATIC_CHAIN_REGNUM) */ -rtx static_chain_incoming_rtx; /* (REG:Pmode STATIC_CHAIN_INCOMING_REGNUM) */ - -/* The ends of the doubly-linked chain of rtl for the current function. - Both are reset to null at the start of rtl generation for the function. - - start_sequence saves both of these on `sequence_stack' and then - starts a new, nested sequence of insns. */ - -static rtx first_insn = NULL; -static rtx last_insn = NULL; - -/* Stack of pending (incomplete) sequences saved by `start_sequence'. - This looks like - (INSN_LIST saved-first-insn - (INSN_LIST saved-last-insn ...more saved sequences...)). - The main insn-chain is saved in the last two links of the chain, - unless the chain is empty. */ - -rtx sequence_stack = 0; - -/* INSN_UID for next insn emitted. - Reset to 1 for each function compiled. */ - -static int cur_insn_uid = 1; - -/* Line number and source file of the last line-number NOTE emitted. - This is used to avoid generating duplicates. */ - -static int last_linenum = 0; -static char *last_filename = 0; - -/* A vector indexed by pseudo reg number. The allocated length - of this vector is regno_pointer_flag_length. Since this - vector is needed during the expansion phase when the total - number of registers in the function is not yet known, - it is copied and made bigger when necessary. */ - -char *regno_pointer_flag; -int regno_pointer_flag_length; - -/* Indexed by pseudo register number, gives the rtx for that pseudo. - Allocated in parallel with regno_pointer_flag. */ - -rtx *regno_reg_rtx; - -/* Filename and line number of last line-number note, - whether we actually emitted it or not. */ -extern char *emit_filename; -extern int emit_lineno; - -rtx change_address (); - -/* rtx gen_rtx (code, mode, [element1, ..., elementn]) -** -** This routine generates an RTX of the size specified by -** , which is an RTX code. The RTX structure is initialized -** from the arguments through , which are -** interpreted according to the specific RTX type's format. The -** special machine mode associated with the rtx (if any) is specified -** in . -** -** gen_rtx() can be invoked in a way which resembles the lisp-like -** rtx it will generate. For example, the following rtx structure: -** -** (plus:QI (mem:QI (reg:SI 1)) -** (mem:QI (plusw:SI (reg:SI 2) (reg:SI 3)))) -** -** ...would be generated by the following C code: -** -** gen_rtx (PLUS, QImode, -** gen_rtx (MEM, QImode, -** gen_rtx (REG, SImode, 1)), -** gen_rtx (MEM, QImode, -** gen_rtx (PLUS, SImode, -** gen_rtx (REG, SImode, 2), -** gen_rtx (REG, SImode, 3)))), -*/ - -/*VARARGS2*/ -rtx -gen_rtx (va_alist) - va_dcl -{ - va_list p; - enum rtx_code code; - enum machine_mode mode; - register int i; /* Array indices... */ - register char *fmt; /* Current rtx's format... */ - register rtx rt_val; /* RTX to return to caller... */ - - va_start (p); - code = va_arg (p, enum rtx_code); - mode = va_arg (p, enum machine_mode); - - if (code == CONST_INT) - { - int arg = va_arg (p, int); - if (arg == 0) - return const0_rtx; - if (arg == 1) - return const1_rtx; - rt_val = rtx_alloc (code); - INTVAL (rt_val) = arg; - } - else - { - rt_val = rtx_alloc (code); /* Allocate the storage space. */ - rt_val->mode = mode; /* Store the machine mode... */ - - fmt = GET_RTX_FORMAT (code); /* Find the right format... */ - for (i = 0; i < GET_RTX_LENGTH (code); i++) - { - switch (*fmt++) - { - case '0': /* Unused field. */ - break; - - case 'i': /* An integer? */ - XINT (rt_val, i) = va_arg (p, int); - break; - - case 's': /* A string? */ - XSTR (rt_val, i) = va_arg (p, char *); - break; - - case 'e': /* An expression? */ - case 'u': /* An insn? Same except when printing. */ - XEXP (rt_val, i) = va_arg (p, rtx); - break; - - case 'E': /* An RTX vector? */ - XVEC (rt_val, i) = va_arg (p, rtvec); - break; - - default: - abort(); - } - } - } - va_end (p); - return rt_val; /* Return the new RTX... */ -} - -/* gen_rtvec (n, [rt1, ..., rtn]) -** -** This routine creates an rtvec and stores within it the -** pointers to rtx's which are its arguments. -*/ - -/*VARARGS1*/ -rtvec -gen_rtvec (va_alist) - va_dcl -{ - int n, i; - va_list p; - rtx *vector; - - va_start (p); - n = va_arg (p, int); - - if (n == 0) - return NULL_RTVEC; /* Don't allocate an empty rtvec... */ - - vector = (rtx *) alloca (n * sizeof (rtx)); - for (i = 0; i < n; i++) - vector[i] = va_arg (p, rtx); - va_end (p); - - return gen_rtvec_v (n, vector); -} - -rtvec -gen_rtvec_v (n, argp) - int n; - rtx *argp; -{ - register int i; - register rtvec rt_val; - - if (n == 0) - return NULL_RTVEC; /* Don't allocate an empty rtvec... */ - - rt_val = rtvec_alloc (n); /* Allocate an rtvec... */ - - for (i = 0; i < n; i++) - rt_val->elem[i].rtx = *argp++; - - return rt_val; -} - -/* Generate a REG rtx for a new pseudo register of mode MODE. - This pseudo is assigned the next sequential register number. */ - -rtx -gen_reg_rtx (mode) - enum machine_mode mode; -{ - register rtx val; - - /* Make sure regno_pointer_flag and regno_reg_rtx are large - enough to have an element for this pseudo reg number. */ - - if (reg_rtx_no == regno_pointer_flag_length) - { - rtx *new1; - char *new = - (char *) oballoc (regno_pointer_flag_length * 2); - bzero (new, regno_pointer_flag_length * 2); - bcopy (regno_pointer_flag, new, regno_pointer_flag_length); - regno_pointer_flag = new; - - new1 = (rtx *) oballoc (regno_pointer_flag_length * 2 * sizeof (rtx)); - bzero (new1, regno_pointer_flag_length * 2 * sizeof (rtx)); - bcopy (regno_reg_rtx, new1, regno_pointer_flag_length * sizeof (rtx)); - regno_reg_rtx = new1; - - regno_pointer_flag_length *= 2; - } - - val = gen_rtx (REG, mode, reg_rtx_no); - regno_reg_rtx[reg_rtx_no++] = val; - return val; -} - -/* Identify REG as a probable pointer register. */ - -void -mark_reg_pointer (reg) - rtx reg; -{ - REGNO_POINTER_FLAG (REGNO (reg)) = 1; -} - -/* Return 1 plus largest pseudo reg number used in the current function. */ - -int -max_reg_num () -{ - return reg_rtx_no; -} - -/* Return 1 + the largest label number used so far. */ - -int -max_label_num () -{ - return label_num; -} - -/* Return first label number used in this function (if any were used). */ - -int -get_first_label_num () -{ - return first_label_num; -} - -/* Assuming that X is an rtx (MEM, REG or SUBREG) for a fixed-point number, - return a MEM or SUBREG rtx that refers to the least-significant part of X. - MODE specifies how big a part of X to return; - it must not be larger than a word. - If X is a MEM whose address is a QUEUED, the value may be so also. */ - -rtx -gen_lowpart (mode, x) - enum machine_mode mode; - register rtx x; -{ - /* This case loses if X is a subreg. To catch bugs early, - complain if an invalid MODE is used even in other cases. */ - if (GET_MODE_SIZE (mode) > UNITS_PER_WORD - && GET_MODE_SIZE (mode) != GET_MODE_UNIT_SIZE (GET_MODE (x))) - abort (); - if (GET_MODE (x) == mode) - return x; - if (GET_CODE (x) == CONST_INT) - return gen_rtx (CONST_INT, VOIDmode, INTVAL (x) & GET_MODE_MASK (mode)); - if (GET_CODE (x) == CONST_DOUBLE) -/* In version 1.37, try this: */ -/* if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) abort (); */ - /* Assume it's an int, so ..._LOW means the low-order word. */ - return gen_rtx (CONST_INT, VOIDmode, - CONST_DOUBLE_LOW (x) & GET_MODE_MASK (mode)); - if (GET_CODE (x) == MEM) - { - register int offset = 0; -#ifdef WORDS_BIG_ENDIAN - offset = (max (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD) - - max (GET_MODE_SIZE (mode), UNITS_PER_WORD)); -#endif -#ifdef BYTES_BIG_ENDIAN - /* Adjust the address so that the address-after-the-data - is unchanged. */ - offset -= (min (UNITS_PER_WORD, GET_MODE_SIZE (mode)) - - min (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x)))); -#endif - return change_address (x, mode, plus_constant (XEXP (x, 0), offset)); - } - else if (GET_CODE (x) == SUBREG) - return (GET_MODE (SUBREG_REG (x)) == mode && SUBREG_WORD (x) == 0 - ? SUBREG_REG (x) - : gen_rtx (SUBREG, mode, SUBREG_REG (x), SUBREG_WORD (x))); - else if (GET_CODE (x) == REG) - { -#ifdef WORDS_BIG_ENDIAN - if (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD) - { - return gen_rtx (SUBREG, mode, x, - ((GET_MODE_SIZE (GET_MODE (x)) - - max (GET_MODE_SIZE (mode), UNITS_PER_WORD)) - / UNITS_PER_WORD)); - } -#endif - return gen_rtx (SUBREG, mode, x, 0); - } - else - abort (); -} - -/* Like `gen_lowpart', but refer to the most significant part. */ - -rtx -gen_highpart (mode, x) - enum machine_mode mode; - register rtx x; -{ - if (GET_CODE (x) == MEM) - { - register int offset = 0; -#ifndef WORDS_BIG_ENDIAN - offset = (max (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD) - - max (GET_MODE_SIZE (mode), UNITS_PER_WORD)); -#endif -#ifndef BYTES_BIG_ENDIAN - if (GET_MODE_SIZE (mode) < UNITS_PER_WORD) - offset -= (GET_MODE_SIZE (mode) - - min (UNITS_PER_WORD, - GET_MODE_SIZE (GET_MODE (x)))); -#endif - return change_address (x, mode, plus_constant (XEXP (x, 0), offset)); - } - else if (GET_CODE (x) == REG) - { -#ifndef WORDS_BIG_ENDIAN - if (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD) - { - return gen_rtx (SUBREG, mode, x, - ((GET_MODE_SIZE (GET_MODE (x)) - - max (GET_MODE_SIZE (mode), UNITS_PER_WORD)) - / UNITS_PER_WORD)); - } -#endif - return gen_rtx (SUBREG, mode, x, 0); - } - else - abort (); -} - -/* Return 1 iff X, assumed to be a SUBREG, - refers to the least significant part of its containing reg. - If X is not a SUBREG, always return 1 (it is its own low part!). */ - -int -subreg_lowpart_p (x) - rtx x; -{ - if (GET_CODE (x) != SUBREG) - return 1; -#ifdef WORDS_BIG_ENDIAN - if (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD) - { - register enum machine_mode mode = GET_MODE (SUBREG_REG (x)); - return (SUBREG_WORD (x) - == ((GET_MODE_SIZE (GET_MODE (x)) - - max (GET_MODE_SIZE (mode), UNITS_PER_WORD)) - / UNITS_PER_WORD)); - } -#endif - return SUBREG_WORD (x) == 0; -} - -/* Return a memory reference like MEMREF, but with its mode changed - to MODE and its address changed to ADDR. - (VOIDmode means don't change the mode. - NULL for ADDR means don't change the address.) */ - -rtx -change_address (memref, mode, addr) - rtx memref; - enum machine_mode mode; - rtx addr; -{ - rtx new; - - if (GET_CODE (memref) != MEM) - abort (); - if (mode == VOIDmode) - mode = GET_MODE (memref); - if (addr == 0) - addr = XEXP (memref, 0); - - new = gen_rtx (MEM, mode, memory_address (mode, addr)); - MEM_VOLATILE_P (new) = MEM_VOLATILE_P (memref); - RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (memref); - MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (memref); - return new; -} - -/* Return a newly created CODE_LABEL rtx with a unique label number. */ - -rtx -gen_label_rtx () -{ - register rtx label = gen_rtx (CODE_LABEL, VOIDmode, 0, 0, 0, label_num++); - LABEL_NUSES (label) = 0; - return label; -} - -/* For procedure integration. */ - -/* Return a newly created INLINE_HEADER rtx. Should allocate this - from a permanent obstack when the opportunity arises. */ - -rtx -gen_inline_header_rtx (insn, last_insn, - first_labelno, last_labelno, - max_parm_regnum, max_regnum, args_size, - stack_slots) - rtx insn, last_insn; - int first_labelno, last_labelno, max_parm_regnum, max_regnum, args_size; - rtx stack_slots; -{ - rtx header = gen_rtx (INLINE_HEADER, VOIDmode, - cur_insn_uid++, NULL, - insn, last_insn, - first_labelno, last_labelno, - max_parm_regnum, max_regnum, args_size, stack_slots); - return header; -} - -/* Install new pointers to the first and last insns in the chain. - Used for an inline-procedure after copying the insn chain. */ - -void -set_new_first_and_last_insn (first, last) - rtx first, last; -{ - first_insn = first; - last_insn = last; -} - -/* Go through all the RTL insn bodies and copy any invalid shared structure. - It does not work to do this twice, because the mark bits set here - are not cleared afterwards. */ - -static int unshare_copies = 0; /* Count rtx's that were copied. */ - -static rtx copy_rtx_if_shared (); - -void -unshare_all_rtl (insn) - register rtx insn; -{ - extern rtx stack_slot_list; - - for (; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN - || GET_CODE (insn) == CALL_INSN) - { - PATTERN (insn) = copy_rtx_if_shared (PATTERN (insn)); - REG_NOTES (insn) = copy_rtx_if_shared (REG_NOTES (insn)); - LOG_LINKS (insn) = copy_rtx_if_shared (LOG_LINKS (insn)); - } - - /* Make sure the addresses of stack slots are not shared - with anything in the insn chain. That could happen if - the stack slot is referenced only by its address. */ - copy_rtx_if_shared (stack_slot_list); -} - -/* Mark ORIG as in use, and return a copy of it if it was already in use. - Recursively does the same for subexpressions. */ - -static rtx -copy_rtx_if_shared (orig) - rtx orig; -{ - register rtx x = orig; - register int i; - register enum rtx_code code; - register char *format_ptr; - int copied = 0; - - if (x == 0) - return 0; - - code = GET_CODE (x); - - /* These types may be freely shared. */ - - switch (code) - { - case REG: - case QUEUED: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case CODE_LABEL: - case PC: - case CC0: - return x; - - case INSN: - case JUMP_INSN: - case CALL_INSN: - case NOTE: - case LABEL_REF: - case BARRIER: - /* The chain of insns is not being copied. */ - return x; - - case MEM: - /* A MEM is allowed to be shared if its address is constant - or is a constant plus one of the special registers. */ - if (CONSTANT_ADDRESS_P (XEXP (x, 0))) - return x; - if (GET_CODE (XEXP (x, 0)) == PLUS - && (XEXP (XEXP (x, 0), 0) == frame_pointer_rtx - || XEXP (XEXP (x, 0), 0) == arg_pointer_rtx) - && CONSTANT_ADDRESS_P (XEXP (XEXP (x, 0), 1))) - { - /* This MEM can appear in more than one place, - but its address better not be shared with anything else. */ - if (! x->used) - XEXP (x, 0) = copy_rtx_if_shared (XEXP (x, 0)); - x->used = 1; - return x; - } - if (XEXP (x, 0) == frame_pointer_rtx - || XEXP (x, 0) == arg_pointer_rtx) - return x; - } - - /* This rtx may not be shared. If it has already been seen, - replace it with a copy of itself. */ - - if (x->used) - { - register rtx copy; - - unshare_copies++; - - copy = rtx_alloc (code); - bcopy (x, copy, (sizeof (*copy) - sizeof (copy->fld) - + sizeof (copy->fld[0]) * GET_RTX_LENGTH (code))); - x = copy; - copied = 1; - } - x->used = 1; - - /* Now scan the subexpressions recursively. - We can store any replaced subexpressions directly into X - since we know X is not shared! Any vectors in X - must be copied if X was copied. */ - - format_ptr = GET_RTX_FORMAT (code); - - for (i = 0; i < GET_RTX_LENGTH (code); i++) - { - switch (*format_ptr++) - { - case 'e': - XEXP (x, i) = copy_rtx_if_shared (XEXP (x, i)); - break; - - case 'E': - if (XVEC (x, i) != NULL) - { - register int j; - - if (copied) - XVEC (x, i) = gen_rtvec_v (XVECLEN (x, i), &XVECEXP (x, i, 0)); - for (j = 0; j < XVECLEN (x, i); j++) - XVECEXP (x, i, j) - = copy_rtx_if_shared (XVECEXP (x, i, j)); - } - break; - } - } - return x; -} - -/* Copy X if necessary so that it won't be altered by changes in OTHER. - Return X or the rtx for the pseudo reg the value of X was copied into. - OTHER must be valid as a SET_DEST. */ - -rtx -make_safe_from (x, other) - rtx x, other; -{ - while (1) - switch (GET_CODE (other)) - { - case SUBREG: - other = SUBREG_REG (other); - break; - case STRICT_LOW_PART: - case SIGN_EXTEND: - case ZERO_EXTEND: - other = XEXP (other, 0); - break; - default: - goto done; - } - done: - if ((GET_CODE (other) == MEM - && ! CONSTANT_P (x) - && GET_CODE (x) != CONST_DOUBLE - && GET_CODE (x) != REG - && GET_CODE (x) != SUBREG) - || (GET_CODE (other) == REG - && (REGNO (other) < FIRST_PSEUDO_REGISTER - || reg_mentioned_p (other, x)))) - { - rtx temp = gen_reg_rtx (GET_MODE (x)); - emit_move_insn (temp, x); - return temp; - } - return x; -} - -/* Emission of insns (adding them to the doubly-linked list). */ - -/* Return the first insn of the current sequence or current function. */ - -rtx -get_insns () -{ - return first_insn; -} - -/* Return the last insn emitted in current sequence or current function. */ - -rtx -get_last_insn () -{ - return last_insn; -} - -/* Specify a new insn as the last in the chain. */ - -void -set_last_insn (insn) - rtx insn; -{ - if (NEXT_INSN (insn) != 0) - abort (); - last_insn = insn; -} - -/* Return a number larger than any instruction's uid in this function. */ - -int -get_max_uid () -{ - return cur_insn_uid; -} - -rtx -next_insn (insn) - rtx insn; -{ - if (insn) return NEXT_INSN (insn); - return 0; -} - -rtx -previous_insn (insn) - rtx insn; -{ - if (insn) return PREV_INSN (insn); - return 0; -} - -/* Make and return an INSN rtx, initializing all its slots. - Store PATTERN in the pattern slots. - PAT_FORMALS is an idea that never really went anywhere. */ - -static rtx -make_insn_raw (pattern, pat_formals) - rtx pattern; - rtvec pat_formals; -{ - register rtx insn; - - insn = rtx_alloc(INSN); - INSN_UID(insn) = cur_insn_uid++; - - PATTERN (insn) = pattern; - INSN_CODE (insn) = -1; - LOG_LINKS(insn) = NULL; - REG_NOTES(insn) = NULL; - - return insn; -} - -/* Like `make_insn' but make a JUMP_INSN instead of an insn. */ - -static rtx -make_jump_insn_raw (pattern, pat_formals) - rtx pattern; - rtvec pat_formals; -{ - register rtx insn; - - insn = rtx_alloc(JUMP_INSN); - INSN_UID(insn) = cur_insn_uid++; - - PATTERN (insn) = pattern; - INSN_CODE (insn) = -1; - LOG_LINKS(insn) = NULL; - REG_NOTES(insn) = NULL; - JUMP_LABEL(insn) = NULL; - - return insn; -} - -/* Add INSN to the end of the doubly-linked list. - INSN may be an INSN, JUMP_INSN, CALL_INSN, CODE_LABEL, BARRIER or NOTE. */ - -static void -add_insn (insn) - register rtx insn; -{ - PREV_INSN (insn) = last_insn; - NEXT_INSN (insn) = 0; - - if (NULL != last_insn) - NEXT_INSN (last_insn) = insn; - - if (NULL == first_insn) - first_insn = insn; - - last_insn = insn; -} - -/* Add INSN, an rtx of code INSN, into the doubly-linked list - after insn AFTER. */ - -static void -add_insn_after (insn, after) - rtx insn, after; -{ - NEXT_INSN (insn) = NEXT_INSN (after); - PREV_INSN (insn) = after; - - if (NEXT_INSN (insn)) - PREV_INSN (NEXT_INSN (insn)) = insn; - else if (last_insn == after) - last_insn = insn; - else - { - rtx stack = sequence_stack; - /* Scan all pending sequences too. */ - for (; stack; stack = XEXP (XEXP (stack, 1), 1)) - if (after == XEXP (XEXP (stack, 1), 0)) - XEXP (XEXP (stack, 1), 0) = insn; - } - - NEXT_INSN (after) = insn; -} - -/* Delete all insns made since FROM. - FROM becomes the new last instruction. */ - -void -delete_insns_since (from) - rtx from; -{ - if (from == 0) - first_insn = 0; - else - NEXT_INSN (from) = 0; - last_insn = from; -} - -/* Move a consecutive bunch of insns to a different place in the chain. - The insns to be moved are those between FROM and TO. - They are moved to a new position after the insn AFTER. */ - -void -reorder_insns (from, to, after) - rtx from, to, after; -{ - /* Splice this bunch out of where it is now. */ - if (PREV_INSN (from)) - NEXT_INSN (PREV_INSN (from)) = NEXT_INSN (to); - if (NEXT_INSN (to)) - PREV_INSN (NEXT_INSN (to)) = PREV_INSN (from); - if (last_insn == to) - last_insn = PREV_INSN (from); - if (first_insn == from) - first_insn = NEXT_INSN (to); - - /* Make the new neighbors point to it and it to them. */ - if (NEXT_INSN (after)) - { - PREV_INSN (NEXT_INSN (after)) = to; - NEXT_INSN (to) = NEXT_INSN (after); - } - PREV_INSN (from) = after; - NEXT_INSN (after) = from; - if (after == last_insn) - last_insn = to; -} - -/* Emit an insn of given code and pattern - at a specified place within the doubly-linked list. */ - -/* Make an instruction with body PATTERN - and output it before the instruction BEFORE. */ - -rtx -emit_insn_before (pattern, before) - register rtx pattern, before; -{ - register rtx insn; - - if (GET_CODE (pattern) == SEQUENCE) - { - register int i; - /* For an empty sequence, emit nothing. */ - if (XVEC (pattern, 0)) - for (i = 0; i < XVECLEN (pattern, 0); i++) - add_insn_after (XVECEXP (pattern, 0, i), PREV_INSN (before)); - return PREV_INSN (before); - } - - insn = make_insn_raw (pattern, 0); - - PREV_INSN (insn) = PREV_INSN (before); - NEXT_INSN (insn) = before; - - if (PREV_INSN (insn)) - NEXT_INSN (PREV_INSN (insn)) = insn; - else - first_insn = insn; - PREV_INSN (before) = insn; - - return insn; -} - -/* Make an instruction with body PATTERN and code JUMP_INSN - and output it before the instruction BEFORE. */ - -rtx -emit_jump_insn_before (pattern, before) - register rtx pattern, before; -{ - register rtx insn = make_jump_insn_raw (pattern, 0); - - PREV_INSN (insn) = PREV_INSN (before); - NEXT_INSN (insn) = before; - - if (PREV_INSN (insn)) - NEXT_INSN (PREV_INSN (insn)) = insn; - else - first_insn = insn; - PREV_INSN (before) = insn; - - return insn; -} - -/* Make an instruction with body PATTERN and code CALL_INSN - and output it before the instruction BEFORE. */ - -rtx -emit_call_insn_before (pattern, before) - register rtx pattern, before; -{ - rtx insn = emit_insn_before (pattern, before); - PUT_CODE (insn, CALL_INSN); - return insn; -} - -/* Make an insn of code INSN with body PATTERN - and output it after the insn AFTER. */ - -rtx -emit_insn_after (pattern, after) - register rtx pattern, after; -{ - if (GET_CODE (pattern) == SEQUENCE) - { - register int i; - /* For an empty sequence, emit nothing. */ - if (XVEC (pattern, 0)) - for (i = 0; i < XVECLEN (pattern, 0); i++) - { - add_insn_after (XVECEXP (pattern, 0, i), after); - after = NEXT_INSN (after); - } - return after; - } - else - { - register rtx insn = make_insn_raw (pattern, 0); - add_insn_after (insn, after); - return insn; - } -} - -/* Make an insn of code JUMP_INSN with body PATTERN - and output it after the insn AFTER. */ - -rtx -emit_jump_insn_after (pattern, after) - register rtx pattern, after; -{ - register rtx insn = make_jump_insn_raw (pattern, 0); - - add_insn_after (insn, after); - return insn; -} - -/* Make an insn of code BARRIER - and output it after the insn AFTER. */ - -rtx -emit_barrier_after (after) - register rtx after; -{ - register rtx insn = rtx_alloc (BARRIER); - - INSN_UID (insn) = cur_insn_uid++; - - add_insn_after (insn, after); - return insn; -} - -/* Emit the label LABEL after the insn AFTER. */ - -void -emit_label_after (label, after) - rtx label, after; -{ - /* This can be called twice for the same label - as a result of the confusion that follows a syntax error! - So make it harmless. */ - if (INSN_UID (label) == 0) - { - INSN_UID (label) = cur_insn_uid++; - add_insn_after (label, after); - } -} - -/* Emit a note of subtype SUBTYPE after the insn AFTER. */ - -void -emit_note_after (subtype, after) - int subtype; - rtx after; -{ - register rtx note = rtx_alloc (NOTE); - INSN_UID (note) = cur_insn_uid++; - XSTR (note, 3) = 0; - XINT (note, 4) = subtype; - add_insn_after (note, after); -} - -/* Make an insn of code INSN with pattern PATTERN - and add it to the end of the doubly-linked list. - If PATTERN is a SEQUENCE, take the elements of it - and emit an insn for each element. - - Returns the last insn emitted. */ - -rtx -emit_insn (pattern) - rtx pattern; -{ - rtx insn; - - if (GET_CODE (pattern) == SEQUENCE) - { - register int i; - /* For an empty sequence, emit nothing. */ - if (XVEC (pattern, 0)) - for (i = 0; i < XVECLEN (pattern, 0); i++) - add_insn (insn = XVECEXP (pattern, 0, i)); - } - else - { - insn = make_insn_raw (pattern, NULL); - add_insn (insn); - } - return insn; -} - -/* Emit the insns in a chain starting with INSN. */ - -rtx -emit_insns (insn) - rtx insn; -{ - while (insn) - { - rtx next = NEXT_INSN (insn); - add_insn (insn); - insn = next; - } -} - -/* Make an insn of code JUMP_INSN with pattern PATTERN - and add it to the end of the doubly-linked list. */ - -rtx -emit_jump_insn (pattern) - rtx pattern; -{ - if (GET_CODE (pattern) == SEQUENCE) - return emit_insn (pattern); - else - { - register rtx insn = make_jump_insn_raw (pattern, NULL); - add_insn (insn); - return insn; - } -} - -/* Make an insn of code CALL_INSN with pattern PATTERN - and add it to the end of the doubly-linked list. */ - -rtx -emit_call_insn (pattern) - rtx pattern; -{ - if (GET_CODE (pattern) == SEQUENCE) - return emit_insn (pattern); - else - { - register rtx insn = make_insn_raw (pattern, NULL); - add_insn (insn); - PUT_CODE (insn, CALL_INSN); - return insn; - } -} - -/* Add the label LABEL to the end of the doubly-linked list. */ - -rtx -emit_label (label) - rtx label; -{ - /* This can be called twice for the same label - as a result of the confusion that follows a syntax error! - So make it harmless. */ - if (INSN_UID (label) == 0) - { - INSN_UID (label) = cur_insn_uid++; - add_insn (label); - } - return label; -} - -/* Make an insn of code BARRIER - and add it to the end of the doubly-linked list. */ - -rtx -emit_barrier () -{ - register rtx barrier = rtx_alloc (BARRIER); - INSN_UID (barrier) = cur_insn_uid++; - add_insn (barrier); - return barrier; -} - -/* Make an insn of code NOTE - with data-fields specified by FILE and LINE - and add it to the end of the doubly-linked list, - but only if line-numbers are desired for debugging info. */ - -rtx -emit_line_note (file, line) - char *file; - int line; -{ - emit_filename = file; - emit_lineno = line; - -#if 0 - if (no_line_numbers) - return 0; -#endif - - return emit_note (file, line); -} - -/* Make an insn of code NOTE - with data-fields specified by FILE and LINE - and add it to the end of the doubly-linked list. - If it is a line-number NOTE, omit it if it matches the previous one. */ - -rtx -emit_note (file, line) - char *file; - int line; -{ - register rtx note; - - if (line > 0) - { - if (file && last_filename && !strcmp (file, last_filename) - && line == last_linenum) - return 0; - last_filename = file; - last_linenum = line; - } - - if (no_line_numbers && line > 0) - { - cur_insn_uid++; - return 0; - } - - note = rtx_alloc (NOTE); - INSN_UID (note) = cur_insn_uid++; - XSTR (note, 3) = file; - XINT (note, 4) = line; - add_insn (note); - return note; -} - -/* Emit a NOTE, and don't omit it even if LINE it the previous note. */ - -rtx -emit_line_note_force (file, line) - char *file; - int line; -{ - last_linenum = -1; - return emit_line_note (file, line); -} - -/* Cause next statement to emit a line note even if the line number - has not changed. This is used at the beginning of a function. */ - -void -force_next_line_note () -{ - last_linenum = -1; -} - -/* Return an indication of which type of insn should have X as a body. - The value is CODE_LABEL, INSN, CALL_INSN or JUMP_INSN. */ - -enum rtx_code -classify_insn (x) - rtx x; -{ - if (GET_CODE (x) == CODE_LABEL) - return CODE_LABEL; - if (GET_CODE (x) == CALL) - return CALL_INSN; - if (GET_CODE (x) == RETURN) - return JUMP_INSN; - if (GET_CODE (x) == SET) - { - if (SET_DEST (x) == pc_rtx) - return JUMP_INSN; - else if (GET_CODE (SET_SRC (x)) == CALL) - return CALL_INSN; - else - return INSN; - } - if (GET_CODE (x) == PARALLEL) - { - register int j; - for (j = XVECLEN (x, 0) - 1; j >= 0; j--) - if (GET_CODE (XVECEXP (x, 0, j)) == CALL) - return CALL_INSN; - else if (GET_CODE (XVECEXP (x, 0, j)) == SET - && SET_DEST (XVECEXP (x, 0, j)) == pc_rtx) - return JUMP_INSN; - else if (GET_CODE (XVECEXP (x, 0, j)) == SET - && GET_CODE (SET_SRC (XVECEXP (x, 0, j))) == CALL) - return CALL_INSN; - } - return INSN; -} - -/* Emit the rtl pattern X as an appropriate kind of insn. - If X is a label, it is simply added into the insn chain. */ - -void -emit (x) - rtx x; -{ - enum rtx_code code = classify_insn (x); - - if (code == CODE_LABEL) - emit_label (x); - else if (code == INSN) - emit_insn (x); - else if (code == JUMP_INSN) - { - register rtx insn = emit_jump_insn (x); - if (simplejump_p (insn) || GET_CODE (x) == RETURN) - emit_barrier (); - } - else if (code == CALL_INSN) - emit_call_insn (x); -} - -/* Begin emitting insns to a sequence which can be packaged in an RTL_EXPR. - Return an rtx containing data on any sequence already in progress. */ - -rtx -start_sequence () -{ - sequence_stack - = gen_rtx (INSN_LIST, VOIDmode, - first_insn, gen_rtx (INSN_LIST, VOIDmode, - last_insn, sequence_stack)); - first_insn = 0; - last_insn = 0; - return sequence_stack; -} - -/* Set up the insn chain starting with FIRST - as the current sequence, saving the previously current one. */ - -void -push_to_sequence (first) - rtx first; -{ - rtx last; - for (last = first; last && NEXT_INSN (last); last = NEXT_INSN (last)); - sequence_stack - = gen_rtx (INSN_LIST, VOIDmode, - first_insn, gen_rtx (INSN_LIST, VOIDmode, - last_insn, sequence_stack)); - first_insn = first; - last_insn = last; -} - -/* After emitting to a sequence, restore previous saved state. - The argument SAVED is no longer used. - - To get the contents of the sequence just made, - you must call `gen_sequence' *before* calling here. */ - -void -end_sequence (saved) - rtx saved; -{ - first_insn = XEXP (sequence_stack, 0); - last_insn = XEXP (XEXP (sequence_stack, 1), 0); - sequence_stack = XEXP (XEXP (sequence_stack, 1), 1); -} - -/* Generate a SEQUENCE rtx containing the insns already emitted - to the current sequence. - - This is how the gen_... function from a DEFINE_EXPAND - constructs the SEQUENCE that it returns. */ - -rtx -gen_sequence () -{ - rtx tem; - rtvec newvec; - int i; - int len; - - /* Count the insns in the chain. */ - len = 0; - for (tem = first_insn; tem; tem = NEXT_INSN (tem)) - len++; - - /* For an empty sequence... */ - if (len == 0) - return gen_rtx (SEQUENCE, VOIDmode, NULL); - - /* If only one insn, return its pattern rather than a SEQUENCE. */ - if (len == 1 - && (GET_CODE (first_insn) == INSN - || GET_CODE (first_insn) == JUMP_INSN - || GET_CODE (first_insn) == CALL_INSN)) - return PATTERN (first_insn); - - /* Put them in a vector. */ - newvec = rtvec_alloc (len); - i = 0; - for (tem = first_insn; tem; tem = NEXT_INSN (tem), i++) - newvec->elem[i].rtx = tem; - - /* Make a SEQUENCE from this vector. */ - return gen_rtx (SEQUENCE, VOIDmode, newvec); -} - -/* Set up regno_reg_rtx, reg_rtx_no and regno_pointer_flag - according to the chain of insns starting with FIRST. - - Also set cur_insn_uid to exceed the largest uid in that chain. - - This is used when an inline function's rtl is saved - and passed to rest_of_compilation later. */ - -static void restore_reg_data_1 (); - -void -restore_reg_data (first) - rtx first; -{ - register rtx insn; - int i; - register int max_uid = 0; - - for (insn = first; insn; insn = NEXT_INSN (insn)) - { - if (INSN_UID (insn) >= max_uid) - max_uid = INSN_UID (insn); - - switch (GET_CODE (insn)) - { - case NOTE: - case CODE_LABEL: - case BARRIER: - break; - - case JUMP_INSN: - case CALL_INSN: - case INSN: - restore_reg_data_1 (PATTERN (insn)); - break; - } - } - - /* Don't duplicate the uids already in use. */ - cur_insn_uid = max_uid + 1; - - /* If any regs are missing, make them up. */ - for (i = FIRST_PSEUDO_REGISTER; i < reg_rtx_no; i++) - if (regno_reg_rtx[i] == 0) - regno_reg_rtx[i] = gen_rtx (REG, SImode, i); -} - -static void -restore_reg_data_1 (orig) - rtx orig; -{ - register rtx x = orig; - register int i; - register enum rtx_code code; - register char *format_ptr; - - code = GET_CODE (x); - - switch (code) - { - case QUEUED: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case CODE_LABEL: - case PC: - case CC0: - case LABEL_REF: - return; - - case REG: - if (REGNO (x) >= FIRST_PSEUDO_REGISTER) - { - /* Make sure regno_pointer_flag and regno_reg_rtx are large - enough to have an element for this pseudo reg number. */ - if (REGNO (x) >= reg_rtx_no) - { - reg_rtx_no = REGNO (x); - - if (reg_rtx_no >= regno_pointer_flag_length) - { - int newlen = max (regno_pointer_flag_length * 2, - reg_rtx_no + 30); - rtx *new1; - char *new = (char *) oballoc (newlen); - bzero (new, newlen); - bcopy (regno_pointer_flag, new, regno_pointer_flag_length); - - new1 = (rtx *) oballoc (newlen * sizeof (rtx)); - bzero (new1, newlen * sizeof (rtx)); - bcopy (regno_reg_rtx, new1, regno_pointer_flag_length * sizeof (rtx)); - - regno_pointer_flag = new; - regno_reg_rtx = new1; - regno_pointer_flag_length = newlen; - } - reg_rtx_no ++; - } - regno_reg_rtx[REGNO (x)] = x; - } - return; - - case MEM: - if (GET_CODE (XEXP (x, 0)) == REG) - mark_reg_pointer (XEXP (x, 0)); - restore_reg_data_1 (XEXP (x, 0)); - return; - } - - /* Now scan the subexpressions recursively. */ - - format_ptr = GET_RTX_FORMAT (code); - - for (i = 0; i < GET_RTX_LENGTH (code); i++) - { - switch (*format_ptr++) - { - case 'e': - restore_reg_data_1 (XEXP (x, i)); - break; - - case 'E': - if (XVEC (x, i) != NULL) - { - register int j; - - for (j = 0; j < XVECLEN (x, i); j++) - restore_reg_data_1 (XVECEXP (x, i, j)); - } - break; - } - } -} - -/* Initialize data structures and variables in this file - before generating rtl for each function. - WRITE_SYMBOLS is nonzero if any kind of debugging info - is to be generated. */ - -void -init_emit (write_symbols) - int write_symbols; -{ - first_insn = NULL; - last_insn = NULL; - sequence_stack = NULL; - cur_insn_uid = 1; - reg_rtx_no = FIRST_PSEUDO_REGISTER; - last_linenum = 0; - last_filename = 0; - first_label_num = label_num; - - no_line_numbers = ! write_symbols; - - /* Init the tables that describe all the pseudo regs. */ - - regno_pointer_flag_length = FIRST_PSEUDO_REGISTER + 100; - - regno_pointer_flag - = (char *) oballoc (regno_pointer_flag_length); - bzero (regno_pointer_flag, regno_pointer_flag_length); - - regno_reg_rtx - = (rtx *) oballoc (regno_pointer_flag_length * sizeof (rtx)); - bzero (regno_reg_rtx, regno_pointer_flag_length * sizeof (rtx)); -} - -/* Create some permanent unique rtl objects shared between all functions. */ - -void -init_emit_once () -{ - /* Create the unique rtx's for certain rtx codes and operand values. */ - - pc_rtx = gen_rtx (PC, VOIDmode); - cc0_rtx = gen_rtx (CC0, VOIDmode); - - /* Don't use gen_rtx here since gen_rtx in this case - tries to use these variables. */ - const0_rtx = rtx_alloc (CONST_INT); - INTVAL (const0_rtx) = 0; - const1_rtx = rtx_alloc (CONST_INT); - INTVAL (const1_rtx) = 1; - - fconst0_rtx = rtx_alloc (CONST_DOUBLE); - dconst0_rtx = rtx_alloc (CONST_DOUBLE); - { - union real_extract u; -#ifdef REAL_IS_NOT_DOUBLE - bzero (&u, sizeof u); - u.d = REAL_VALUE_ATOF ("0"); -#else - u.d = 0; -#endif - - bcopy (&u, &CONST_DOUBLE_LOW (fconst0_rtx), sizeof u); - CONST_DOUBLE_MEM (fconst0_rtx) = cc0_rtx; - PUT_MODE (fconst0_rtx, SFmode); - - bcopy (&u, &CONST_DOUBLE_LOW (dconst0_rtx), sizeof u); - CONST_DOUBLE_MEM (dconst0_rtx) = cc0_rtx; - PUT_MODE (dconst0_rtx, DFmode); - } - - stack_pointer_rtx = gen_rtx (REG, Pmode, STACK_POINTER_REGNUM); - frame_pointer_rtx = gen_rtx (REG, Pmode, FRAME_POINTER_REGNUM); -#ifdef STRUCT_VALUE - struct_value_rtx = STRUCT_VALUE; -#else - struct_value_rtx = gen_rtx (REG, Pmode, STRUCT_VALUE_REGNUM); -#endif - -#ifdef STRUCT_VALUE_INCOMING - struct_value_incoming_rtx = STRUCT_VALUE_INCOMING; -#else -#ifdef STRUCT_VALUE_INCOMING_REGNUM - struct_value_incoming_rtx - = gen_rtx (REG, Pmode, STRUCT_VALUE_INCOMING_REGNUM); -#else - struct_value_incoming_rtx = struct_value_rtx; -#endif -#endif - - static_chain_rtx = gen_rtx (REG, Pmode, STATIC_CHAIN_REGNUM); - -#ifdef STATIC_CHAIN_INCOMING_REGNUM - if (STATIC_CHAIN_INCOMING_REGNUM != STATIC_CHAIN_REGNUM) - static_chain_incoming_rtx = gen_rtx (REG, Pmode, STATIC_CHAIN_INCOMING_REGNUM); - else -#endif - static_chain_incoming_rtx = static_chain_rtx; - - if (FRAME_POINTER_REGNUM == ARG_POINTER_REGNUM) - arg_pointer_rtx = frame_pointer_rtx; - else - arg_pointer_rtx = gen_rtx (REG, Pmode, ARG_POINTER_REGNUM); -} diff --git a/gnu/usr.bin/gcc1/cc1/explow.c b/gnu/usr.bin/gcc1/cc1/explow.c deleted file mode 100644 index e6041a8c47..0000000000 --- a/gnu/usr.bin/gcc1/cc1/explow.c +++ /dev/null @@ -1,575 +0,0 @@ -/* Subroutines for manipulating rtx's in semantically interesting ways. - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "config.h" -#include "rtl.h" -#include "tree.h" -#include "flags.h" -#include "expr.h" - -/* Return an rtx for the sum of X and the integer C. */ - -rtx -plus_constant (x, c) - register rtx x; - register int c; -{ - register RTX_CODE code = GET_CODE (x); - register enum machine_mode mode = GET_MODE (x); - int all_constant = 0; - - if (c == 0) - return x; - - if (code == CONST_INT) - return gen_rtx (CONST_INT, VOIDmode, (INTVAL (x) + c)); - - /* If adding to something entirely constant, set a flag - so that we can add a CONST around the result. */ - if (code == CONST) - { - x = XEXP (x, 0); - all_constant = 1; - } - else if (code == SYMBOL_REF || code == LABEL_REF) - all_constant = 1; - - /* The interesting case is adding the integer to a sum. - Look for constant term in the sum and combine - with C. For an integer constant term, we make a combined - integer. For a constant term that is not an explicit integer, - we cannot really combine, but group them together anyway. */ - - if (GET_CODE (x) == PLUS) - { - if (GET_CODE (XEXP (x, 0)) == CONST_INT) - { - c += INTVAL (XEXP (x, 0)); - x = XEXP (x, 1); - } - else if (GET_CODE (XEXP (x, 1)) == CONST_INT) - { - c += INTVAL (XEXP (x, 1)); - x = XEXP (x, 0); - } - else if (CONSTANT_P (XEXP (x, 0))) - { - return gen_rtx (PLUS, mode, - plus_constant (XEXP (x, 0), c), - XEXP (x, 1)); - } - else if (CONSTANT_P (XEXP (x, 1))) - { - return gen_rtx (PLUS, mode, - XEXP (x, 0), - plus_constant (XEXP (x, 1), c)); - } -#ifdef OLD_INDEXING - /* Detect adding a constant to an indexed address - of the form (PLUS (MULT (REG) (CONST)) regs-and-constants). - Keep the (MULT ...) at the top level of addition so that - the result is still suitable for indexing and constants - are combined. */ - else if (GET_CODE (XEXP (x, 0)) == MULT) - { - return gen_rtx (PLUS, mode, XEXP (x, 0), - plus_constant (XEXP (x, 1), c)); - } - else if (GET_CODE (XEXP (x, 1)) == MULT) - { - return gen_rtx (PLUS, mode, plus_constant (XEXP (x, 0), c), - XEXP (x, 1)); - } -#endif - } - if (c != 0) - x = gen_rtx (PLUS, mode, x, gen_rtx (CONST_INT, VOIDmode, c)); - - if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF) - return x; - else if (all_constant) - return gen_rtx (CONST, mode, x); - else - return x; -} - -/* If X is a sum, return a new sum like X but lacking any constant terms. - Add all the removed constant terms into *CONSTPTR. - X itself is not altered. The result != X if and only if - it is not isomorphic to X. */ - -rtx -eliminate_constant_term (x, constptr) - rtx x; - int *constptr; -{ - int c; - register rtx x0, x1; - - if (GET_CODE (x) != PLUS) - return x; - - /* First handle constants appearing at this level explicitly. */ - if (GET_CODE (XEXP (x, 0)) == CONST_INT) - { - *constptr += INTVAL (XEXP (x, 0)); - return eliminate_constant_term (XEXP (x, 1), constptr); - } - - if (GET_CODE (XEXP (x, 1)) == CONST_INT) - { - *constptr += INTVAL (XEXP (x, 1)); - return eliminate_constant_term (XEXP (x, 0), constptr); - } - - c = 0; - x0 = eliminate_constant_term (XEXP (x, 0), &c); - x1 = eliminate_constant_term (XEXP (x, 1), &c); - if (x1 != XEXP (x, 1) || x0 != XEXP (x, 0)) - { - *constptr += c; - return gen_rtx (PLUS, GET_MODE (x), x0, x1); - } - return x; -} - -/* Return an rtx for the size in bytes of the value of EXP. */ - -rtx -expr_size (exp) - tree exp; -{ - return expand_expr (size_in_bytes (TREE_TYPE (exp)), 0, SImode, 0); -} - -/* Not yet really written since C does not need it. */ - -rtx -lookup_static_chain (context) - rtx context; -{ - abort (); -} - -/* Return a copy of X in which all memory references - and all constants that involve symbol refs - have been replaced with new temporary registers. - Also emit code to load the memory locations and constants - into those registers. - - If X contains no such constants or memory references, - X itself (not a copy) is returned. - - X may contain no arithmetic except addition, subtraction and multiplication. - Values returned by expand_expr with 1 for sum_ok fit this constraint. */ - -static rtx -break_out_memory_refs (x) - register rtx x; -{ - if (GET_CODE (x) == MEM || GET_CODE (x) == CONST - || GET_CODE (x) == SYMBOL_REF) - { - register rtx temp = force_reg (Pmode, x); - mark_reg_pointer (temp); - x = temp; - } - else if (GET_CODE (x) == PLUS || GET_CODE (x) == MINUS - || GET_CODE (x) == MULT) - { - register rtx op0 = break_out_memory_refs (XEXP (x, 0)); - register rtx op1 = break_out_memory_refs (XEXP (x, 1)); - if (op0 != XEXP (x, 0) || op1 != XEXP (x, 1)) - x = gen_rtx (GET_CODE (x), Pmode, op0, op1); - } - return x; -} - -/* Given a memory address or facsimile X, construct a new address, - currently equivalent, that is stable: future stores won't change it. - - X must be composed of constants, register and memory references - combined with addition, subtraction and multiplication: - in other words, just what you can get from expand_expr if sum_ok is 1. - - Works by making copies of all regs and memory locations used - by X and combining them the same way X does. - You could also stabilize the reference to this address - by copying the address to a register with copy_to_reg; - but then you wouldn't get indexed addressing in the reference. */ - -rtx -copy_all_regs (x) - register rtx x; -{ - if (GET_CODE (x) == REG) - { - if (REGNO (x) != FRAME_POINTER_REGNUM) - x = copy_to_reg (x); - } - else if (GET_CODE (x) == MEM) - x = copy_to_reg (x); - else if (GET_CODE (x) == PLUS || GET_CODE (x) == MINUS - || GET_CODE (x) == MULT) - { - register rtx op0 = copy_all_regs (XEXP (x, 0)); - register rtx op1 = copy_all_regs (XEXP (x, 1)); - if (op0 != XEXP (x, 0) || op1 != XEXP (x, 1)) - x = gen_rtx (GET_CODE (x), Pmode, op0, op1); - } - return x; -} - -/* Return something equivalent to X but valid as a memory address - for something of mode MODE. When X is not itself valid, this - works by copying X or subexpressions of it into registers. */ - -rtx -memory_address (mode, x) - enum machine_mode mode; - register rtx x; -{ - register rtx oldx; - - /* By passing constant addresses thru registers - we get a chance to cse them. */ - if (! cse_not_expected && CONSTANT_P (x)) - return force_reg (Pmode, x); - - /* Accept a QUEUED that refers to a REG - even though that isn't a valid address. - On attempting to put this in an insn we will call protect_from_queue - which will turn it into a REG, which is valid. */ - if (GET_CODE (x) == QUEUED - && GET_CODE (QUEUED_VAR (x)) == REG) - return x; - - /* We get better cse by rejecting indirect addressing at this stage. - Let the combiner create indirect addresses where appropriate. - For now, generate the code so that the subexpressions useful to share - are visible. But not if cse won't be done! */ - oldx = x; - if (! cse_not_expected && GET_CODE (x) != REG) - x = break_out_memory_refs (x); - - /* At this point, any valid address is accepted. */ - GO_IF_LEGITIMATE_ADDRESS (mode, x, win); - - /* If it was valid before but breaking out memory refs invalidated it, - use it the old way. */ - if (memory_address_p (mode, oldx)) - goto win2; - - /* Perform machine-dependent transformations on X - in certain cases. This is not necessary since the code - below can handle all possible cases, but machine-dependent - transformations can make better code. */ - LEGITIMIZE_ADDRESS (x, oldx, mode, win); - - /* PLUS and MULT can appear in special ways - as the result of attempts to make an address usable for indexing. - Usually they are dealt with by calling force_operand, below. - But a sum containing constant terms is special - if removing them makes the sum a valid address: - then we generate that address in a register - and index off of it. We do this because it often makes - shorter code, and because the addresses thus generated - in registers often become common subexpressions. */ - if (GET_CODE (x) == PLUS) - { - int constant_term = 0; - rtx y = eliminate_constant_term (x, &constant_term); - if (constant_term == 0 - || ! memory_address_p (mode, y)) - return force_operand (x, 0); - - y = plus_constant (copy_to_reg (y), constant_term); - if (! memory_address_p (mode, y)) - return force_operand (x, 0); - return y; - } - if (GET_CODE (x) == MULT || GET_CODE (x) == MINUS) - return force_operand (x, 0); - - /* If we have a register that's an invalid address, - it must be a hard reg of the wrong class. Copy it to a pseudo. */ - if (GET_CODE (x) == REG) - return copy_to_reg (x); - - /* Last resort: copy the value to a register, since - the register is a valid address. */ - return force_reg (Pmode, x); - - win2: - x = oldx; - win: - if (flag_force_addr && optimize && GET_CODE (x) != REG - /* Don't copy an addr via a reg if it is one of our stack slots. - If we did, it would cause invalid REG_EQUIV notes for parms. */ - && ! (GET_CODE (x) == PLUS - && (XEXP (x, 0) == frame_pointer_rtx - || XEXP (x, 0) == arg_pointer_rtx))) - { - if (general_operand (x, Pmode)) - return force_reg (Pmode, x); - else - return force_operand (x, 0); - } - return x; -} - -/* Like `memory_address' but pretend `flag_force_addr' is 0. */ - -rtx -memory_address_noforce (mode, x) - enum machine_mode mode; - rtx x; -{ - int ambient_force_addr = flag_force_addr; - rtx val; - - flag_force_addr = 0; - val = memory_address (mode, x); - flag_force_addr = ambient_force_addr; - return val; -} - -/* Return a modified copy of X with its memory address copied - into a temporary register to protect it from side effects. - If X is not a MEM, it is returned unchanged (and not copied). - Perhaps even if it is a MEM, if there is no need to change it. */ - -rtx -stabilize (x) - rtx x; -{ - register rtx addr; - if (GET_CODE (x) != MEM) - return x; - addr = XEXP (x, 0); - if (rtx_unstable_p (addr)) - { - rtx temp = copy_all_regs (addr); - rtx mem; - if (GET_CODE (temp) != REG) - temp = copy_to_reg (temp); - mem = gen_rtx (MEM, GET_MODE (x), temp); - /* Mark returned memref with in_struct - if it's in an array or structure. */ - if (GET_CODE (addr) == PLUS || MEM_IN_STRUCT_P (x)) - MEM_IN_STRUCT_P (mem) = 1; - return mem; - } - return x; -} - -/* Copy the value or contents of X to a new temp reg and return that reg. */ - -rtx -copy_to_reg (x) - rtx x; -{ - register rtx temp = gen_reg_rtx (GET_MODE (x)); - - /* If not an operand, must be an address with PLUS and MULT so - do the computation. */ - if (! general_operand (x, VOIDmode)) - x = force_operand (x, temp); - - if (x != temp) - emit_move_insn (temp, x); - - return temp; -} - -/* Like copy_to_reg but always give the new register mode Pmode - in case X is a constant. */ - -rtx -copy_addr_to_reg (x) - rtx x; -{ - return copy_to_mode_reg (Pmode, x); -} - -/* Like copy_to_reg but always give the new register mode MODE - in case X is a constant. */ - -rtx -copy_to_mode_reg (mode, x) - enum machine_mode mode; - rtx x; -{ - register rtx temp = gen_reg_rtx (mode); - - /* If not an operand, must be an address with PLUS and MULT so - do the computation. */ - if (! general_operand (x, VOIDmode)) - x = force_operand (x, temp); - - if (GET_MODE (x) != mode && GET_MODE (x) != VOIDmode) - abort (); - if (x != temp) - emit_move_insn (temp, x); - return temp; -} - -/* Load X into a register if it is not already one. - Use mode MODE for the register. - X should be valid for mode MODE, but it may be a constant which - is valid for all integer modes; that's why caller must specify MODE. - - The caller must not alter the value in the register we return, - since we mark it as a "constant" register. */ - -rtx -force_reg (mode, x) - enum machine_mode mode; - rtx x; -{ - register rtx temp, insn; - - if (GET_CODE (x) == REG) - return x; - temp = gen_reg_rtx (mode); - insn = emit_move_insn (temp, x); - /* Let optimizers know that TEMP's value never changes - and that X can be substituted for it. */ - if (CONSTANT_P (x)) - REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUIV, x, REG_NOTES (insn)); - return temp; -} - -/* If X is a memory ref, copy its contents to a new temp reg and return - that reg. Otherwise, return X. */ - -rtx -force_not_mem (x) - rtx x; -{ - register rtx temp; - if (GET_CODE (x) != MEM) - return x; - temp = gen_reg_rtx (GET_MODE (x)); - emit_move_insn (temp, x); - return temp; -} - -/* Copy X to TARGET (if it's nonzero and a reg) - or to a new temp reg and return that reg. */ - -rtx -copy_to_suggested_reg (x, target) - rtx x, target; -{ - register rtx temp; - if (target && GET_CODE (target) == REG) - temp = target; - else - temp = gen_reg_rtx (GET_MODE (x)); - emit_move_insn (temp, x); - return temp; -} - -/* Adjust the stack pointer by ADJUST (an rtx for a number of bytes). - This pops when ADJUST is positive. ADJUST need not be constant. */ - -void -adjust_stack (adjust) - rtx adjust; -{ - adjust = protect_from_queue (adjust, 0); - -#ifdef STACK_GROWS_DOWNWARD - emit_insn (gen_add2_insn (stack_pointer_rtx, adjust)); -#else - emit_insn (gen_sub2_insn (stack_pointer_rtx, adjust)); -#endif -} - -/* Adjust the stack pointer by minus ADJUST (an rtx for a number of bytes). - This pushes when ADJUST is positive. ADJUST need not be constant. */ - -void -anti_adjust_stack (adjust) - rtx adjust; -{ - adjust = protect_from_queue (adjust, 0); - -#ifdef STACK_GROWS_DOWNWARD - emit_insn (gen_sub2_insn (stack_pointer_rtx, adjust)); -#else - emit_insn (gen_add2_insn (stack_pointer_rtx, adjust)); -#endif -} - -/* Round the size of a block to be pushed up to the boundary required - by this machine. SIZE is the desired size, which need not be constant. */ - -rtx -round_push (size) - rtx size; -{ -#ifdef STACK_BOUNDARY - int align = STACK_BOUNDARY / BITS_PER_UNIT; - if (align == 1) - return size; - if (GET_CODE (size) == CONST_INT) - { - int new = (INTVAL (size) + align - 1) / align * align; - if (INTVAL (size) != new) - size = gen_rtx (CONST_INT, VOIDmode, new); - } - else - { - size = expand_divmod (0, CEIL_DIV_EXPR, Pmode, size, - gen_rtx (CONST_INT, VOIDmode, align), - 0, 1); - size = expand_mult (Pmode, size, - gen_rtx (CONST_INT, VOIDmode, align), - 0, 1); - } -#endif /* STACK_BOUNDARY */ - return size; -} - -/* Return an rtx representing the register or memory location - in which a scalar value of data type VALTYPE - was returned by a function call to function FUNC. - FUNC is a FUNCTION_DECL node if the precise function is known, - otherwise 0. */ - -rtx -hard_function_value (valtype, func) - tree valtype; - tree func; -{ - return FUNCTION_VALUE (valtype, func); -} - -/* Return an rtx representing the register or memory location - in which a scalar value of mode MODE was returned by a library call. */ - -rtx -hard_libcall_value (mode) - enum machine_mode mode; -{ - return LIBCALL_VALUE (mode); -} diff --git a/gnu/usr.bin/gcc1/cc1/expmed.c b/gnu/usr.bin/gcc1/cc1/expmed.c deleted file mode 100644 index f0e3abb6d9..0000000000 --- a/gnu/usr.bin/gcc1/cc1/expmed.c +++ /dev/null @@ -1,1863 +0,0 @@ -/* Medium-level subroutines: convert bit-field store and extract - and shifts, multiplies and divides to rtl instructions. - Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "config.h" -#include "rtl.h" -#include "tree.h" -#include "flags.h" -#include "insn-flags.h" -#include "insn-codes.h" -#include "insn-config.h" -#include "expr.h" -#include "recog.h" - -static rtx extract_split_bit_field (); -static rtx extract_fixed_bit_field (); -static void store_split_bit_field (); -static void store_fixed_bit_field (); - -/* Return an rtx representing minus the value of X. - MODE is the intended mode of the result, - useful if X is a CONST_INT. */ - -rtx -negate_rtx (mode, x) - enum machine_mode mode; - rtx x; -{ - if (GET_CODE (x) == CONST_INT) - { - int val = - INTVAL (x); - if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_INT) - { - /* Sign extend the value from the bits that are significant. */ - if (val & (1 << (GET_MODE_BITSIZE (mode) - 1))) - val |= (-1) << GET_MODE_BITSIZE (mode); - else - val &= (1 << GET_MODE_BITSIZE (mode)) - 1; - } - return gen_rtx (CONST_INT, VOIDmode, val); - } - else - return expand_unop (GET_MODE (x), neg_optab, x, 0, 0); -} - -/* Generate code to store value from rtx VALUE - into a bit-field within structure STR_RTX - containing BITSIZE bits starting at bit BITNUM. - FIELDMODE is the machine-mode of the FIELD_DECL node for this field. - ALIGN is the alignment that STR_RTX is known to have, measured in bytes. - TOTAL_SIZE is the size of the structure in bytes, or -1 if unknown. */ - -rtx -store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size) - rtx str_rtx; - register int bitsize; - int bitnum; - enum machine_mode fieldmode; - rtx value; - int align; - int total_size; -{ - int unit = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD; - register int offset = bitnum / unit; - register int bitpos = bitnum % unit; - register rtx op0 = str_rtx; - rtx value1; - - /* At this point, BITPOS counts within UNIT for a memref. - For a register or a subreg, it actually counts within the width - of the mode of OP0. However, BITNUM never exceeds that width, - so the % operation above never really does anything. - - We will adjust BITPOS later to count properly within UNIT - in the case of a register. */ - - /* Discount the part of the structure before the desired byte. - We need to know how many bytes are safe to reference after it. */ - if (total_size >= 0) - total_size -= (bitpos / BIGGEST_ALIGNMENT - * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); - - while (GET_CODE (op0) == SUBREG) - { -#ifdef BYTES_BIG_ENDIAN - /* Keep BITPOS counting within the size of op0. */ - bitpos += (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0))) - - GET_MODE_BITSIZE (GET_MODE (op0))); -#endif - offset += SUBREG_WORD (op0); - op0 = SUBREG_REG (op0); - } - - value = protect_from_queue (value, 0); - - if (flag_force_mem) - value = force_not_mem (value); - - if (GET_MODE_SIZE (fieldmode) >= UNITS_PER_WORD - && GET_MODE_BITSIZE (fieldmode) == bitsize - && bitpos % BITS_PER_WORD == 0 - && GET_CODE (op0) == REG) - { - /* Storing in a full-word or multi-word field in a register - can be done with just SUBREG. */ - if (GET_MODE (op0) != fieldmode) - op0 = gen_rtx (SUBREG, fieldmode, op0, offset); - emit_move_insn (op0, value); - return value; - } - -#ifdef BYTES_BIG_ENDIAN - /* If OP0 is a register, BITPOS must count within UNIT, which should be SI. - But as we have it, it counts within whatever size OP0 now has. - These are not the same, so convert if big-endian. */ - if (GET_CODE (op0) != MEM && unit > GET_MODE_BITSIZE (GET_MODE (op0))) - { - bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0)); - /* Change the mode now so we don't adjust BITPOS again. */ - if (GET_CODE (op0) == SUBREG) - PUT_MODE (op0, SImode); - else - op0 = gen_rtx (SUBREG, SImode, op0, 0); - } -#endif - - /* Storing an lsb-aligned field in a register - can be done with a movestrict instruction. */ - - if (GET_CODE (op0) != MEM -#ifdef BYTES_BIG_ENDIAN - && bitpos + bitsize == unit -#else - && bitpos == 0 -#endif - && (GET_MODE (op0) == fieldmode - || (movstrict_optab->handlers[(int) fieldmode].insn_code - != CODE_FOR_nothing))) - { - /* Get appropriate low part of the value being stored. */ - if (GET_CODE (value) == CONST_INT || GET_CODE (value) == REG) - value = gen_lowpart (fieldmode, value); - else if (!(GET_CODE (value) == SYMBOL_REF - || GET_CODE (value) == LABEL_REF - || GET_CODE (value) == CONST)) - value = convert_to_mode (fieldmode, value, 0); - - if (GET_MODE (op0) == fieldmode) - emit_move_insn (op0, value); - else - { - if (GET_CODE (op0) == SUBREG) - PUT_MODE (op0, fieldmode); - else - op0 = gen_rtx (SUBREG, fieldmode, op0, offset); - emit_insn (GEN_FCN (movstrict_optab->handlers[(int) fieldmode].insn_code) - (op0, value)); - } - - return value; - } - - /* Handle fields bigger than a word. */ - - if (bitsize > BITS_PER_WORD) - { - int low_size = BITS_PER_WORD; - int low_pos = bitpos + offset * unit; - int high_size = bitsize - low_size; - int high_pos; -#ifdef BYTES_BIG_ENDIAN - high_pos = low_pos; - low_pos += high_size; -#else - high_pos = low_pos + low_size; -#endif - - value = force_reg (GET_MODE (value), value); - store_bit_field (op0, low_size, low_pos, SImode, - gen_lowpart (SImode, value), align, total_size); - store_bit_field (op0, high_size, high_pos, SImode, - gen_highpart (SImode, value), align, total_size); - return value; - } - - /* From here on we can assume that the field to be stored in is an integer, - since it is shorter than a word. */ - - /* OFFSET is the number of words or bytes (UNIT says which) - from STR_RTX to the first word or byte containing part of the field. */ - - if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) - { - /* If not in memory, merge in the offset now. */ - if (offset != 0 - || GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (SImode)) - { - if (GET_CODE (op0) == SUBREG) - SUBREG_WORD (op0) += offset; - else - op0 = gen_rtx (SUBREG, SImode, op0, offset); - } - offset = 0; - } - else - { - op0 = protect_from_queue (op0, 1); - } - - /* Now OFFSET is nonzero only if OP0 is memory - and is therefore always measured in bytes. */ - -#ifdef HAVE_insv - if (HAVE_insv - && !(bitsize == 1 && GET_CODE (value) == CONST_INT)) - { - int xbitpos = bitpos; - rtx xop0 = op0; - rtx last = get_last_insn (); - rtx pat; - - /* If this machine's insv can only insert into a register, - copy OP0 into a register and save it back later. */ - if (GET_CODE (op0) == MEM - && ! (*insn_operand_predicate[(int) CODE_FOR_insv][0]) (op0, VOIDmode)) - { - rtx tempreg; - enum machine_mode trymode, bestmode = VOIDmode, insn_mode; - /* Don't use a mode bigger than the one of the value to be stored. - That mode must be okay, since a bit field can be that big. */ - int maxsize - = GET_MODE_SIZE (insn_operand_mode[(int) CODE_FOR_insv][3]); - /* This used to use the mode desired for operand 0, - but that is normally QImode on most machines, - and QImode won't work for fields that cross byte - boundaries. */ - - /* Also don't use a mode bigger than the structure. */ - if (total_size >= 0 && maxsize > total_size) - maxsize = total_size; - - /* Find biggest machine mode we can safely use - to fetch from this structure. - But don't use a bigger mode than the insn wants. */ - for (trymode = QImode; - trymode && GET_MODE_SIZE (trymode) <= maxsize; - trymode = GET_MODE_WIDER_MODE (trymode)) - if (GET_MODE_SIZE (trymode) <= align - || align == BIGGEST_ALIGNMENT / BITS_PER_UNIT) - bestmode = trymode; - if (! bestmode) - abort (); - /* Adjust address to point to the containing unit of that mode. */ - unit = GET_MODE_BITSIZE (bestmode); - /* Compute offset as multiple of this unit, counting in bytes. */ - offset = (bitnum / unit) * GET_MODE_SIZE (bestmode); - bitpos = bitnum % unit; - op0 = change_address (op0, bestmode, - plus_constant (XEXP (op0, 0), offset)); - - /* Fetch that unit, store the bitfield in it, then store the unit. */ - tempreg = copy_to_reg (op0); - /* To actually store in TEMPREG, - look at it in the mode this insn calls for. - (Probably SImode.) */ - insn_mode = SImode; -#ifdef BYTES_BIG_ENDIAN - if (GET_MODE_BITSIZE (insn_mode) > unit) - bitpos += GET_MODE_BITSIZE (insn_mode) - unit; -#endif - store_bit_field (gen_rtx (SUBREG, insn_mode, tempreg, 0), - bitsize, bitpos, fieldmode, value, - align, total_size); - emit_move_insn (op0, tempreg); - return value; - } - - /* Add OFFSET into OP0's address. */ - if (GET_CODE (xop0) == MEM) - xop0 = change_address (xop0, QImode, - plus_constant (XEXP (xop0, 0), offset)); - - /* If xop0 is a register, we need it in SImode - to make it acceptable to the format of insv. */ - if (GET_CODE (xop0) == SUBREG) - PUT_MODE (xop0, SImode); - if (GET_CODE (xop0) == REG && GET_MODE (xop0) != SImode) - { -#ifdef BYTES_BIG_ENDIAN - xbitpos += (GET_MODE_BITSIZE (SImode) - - GET_MODE_BITSIZE (GET_MODE (xop0))); -#endif - xop0 = gen_rtx (SUBREG, SImode, xop0, 0); - } - - /* Convert VALUE to SImode (which insv insn wants) in VALUE1. */ - value1 = value; - if (GET_MODE (value) != SImode) - { - if (GET_MODE_BITSIZE (GET_MODE (value)) >= bitsize) - { - /* Optimization: Don't bother really extending VALUE - if it has all the bits we will actually use. */ - - /* Avoid making subreg of a subreg, or of a mem. */ - if (GET_CODE (value1) != REG) - value1 = copy_to_reg (value1); - value1 = gen_rtx (SUBREG, SImode, value1, 0); - } - else if (!CONSTANT_P (value)) - /* Parse phase is supposed to make VALUE's data type - match that of the component reference, which is a type - at least as wide as the field; so VALUE should have - a mode that corresponds to that type. */ - abort (); - } - - /* If this machine's insv insists on a register, - get VALUE1 into a register. */ - if (! (*insn_operand_predicate[(int) CODE_FOR_insv][3]) (value1, SImode)) - value1 = force_reg (SImode, value1); - - /* On big-endian machines, we count bits from the most significant. - If the bit field insn does not, we must invert. */ - -#if defined (BITS_BIG_ENDIAN) != defined (BYTES_BIG_ENDIAN) - xbitpos = unit - 1 - xbitpos; -#endif - - pat = gen_insv (xop0, - gen_rtx (CONST_INT, VOIDmode, bitsize), - gen_rtx (CONST_INT, VOIDmode, xbitpos), - value1); - if (pat) - emit_insn (pat); - else - { - delete_insns_since (last); - store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align); - } - } - else -#endif - /* Insv is not available; store using shifts and boolean ops. */ - store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align); - return value; -} - -/* Use shifts and boolean operations to store VALUE - into a bit field of width BITSIZE - in a memory location specified by OP0 except offset by OFFSET bytes. - (OFFSET must be 0 if OP0 is a register.) - The field starts at position BITPOS within the byte. - (If OP0 is a register, it may be SImode or a narrower mode, - but BITPOS still counts within a full word, - which is significant on bigendian machines.) - STRUCT_ALIGN is the alignment the structure is known to have (in bytes). - - Note that protect_from_queue has already been done on OP0 and VALUE. */ - -static void -store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align) - register rtx op0; - register int offset, bitsize, bitpos; - register rtx value; - int struct_align; -{ - register enum machine_mode mode; - int total_bits = BITS_PER_WORD; - rtx subtarget; - int all_zero = 0; - int all_one = 0; - - /* Add OFFSET to OP0's address (if it is in memory) - and if a single byte contains the whole bit field - change OP0 to a byte. */ - - /* There is a case not handled here: - a structure with a known alignment of just a halfword - and a field split across two aligned halfwords within the structure. - Or likewise a structure with a known alignment of just a byte - and a field split across two bytes. - Such cases are not supposed to be able to occur. */ - - if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) - { - if (offset != 0) - abort (); - /* Special treatment for a bit field split across two registers. */ - if (bitsize + bitpos > BITS_PER_WORD) - { - store_split_bit_field (op0, bitsize, bitpos, value, BITS_PER_WORD); - return; - } - } - else if (bitsize + bitpos <= BITS_PER_UNIT - && (! SLOW_BYTE_ACCESS - || (struct_align == 1 - && BIGGEST_ALIGNMENT > 1))) - { - /* It fits in one byte, and either bytes are fast - or the alignment won't let us use anything bigger. */ - total_bits = BITS_PER_UNIT; - op0 = change_address (op0, QImode, - plus_constant (XEXP (op0, 0), offset)); - } - else if ((bitsize + bitpos + (offset % GET_MODE_SIZE (HImode)) * BITS_PER_UNIT - <= GET_MODE_BITSIZE (HImode)) - /* If halfwords are fast, use them whenever valid. */ - && (! SLOW_BYTE_ACCESS - /* Use halfwords if larger is invalid due to alignment. */ - || (struct_align == GET_MODE_SIZE (HImode) - && BIGGEST_ALIGNMENT > GET_MODE_SIZE (HImode)))) - { - /* It fits in an aligned halfword within the structure, - and either halfwords are fast - or the alignment won't let us use anything bigger. */ - total_bits = GET_MODE_BITSIZE (HImode); - - /* Get ref to halfword containing the field. */ - bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT; - offset -= (offset % (total_bits / BITS_PER_UNIT)); - op0 = change_address (op0, HImode, - plus_constant (XEXP (op0, 0), offset)); - } - else - { - /* Get ref to an aligned word containing the field. */ - /* Adjust BITPOS to be position within a word, - and OFFSET to be the offset of that word. - Then alter OP0 to refer to that word. */ - bitpos += (offset % (BITS_PER_WORD / BITS_PER_UNIT)) * BITS_PER_UNIT; - offset -= (offset % (BITS_PER_WORD / BITS_PER_UNIT)); - op0 = change_address (op0, SImode, - plus_constant (XEXP (op0, 0), offset)); - - /* Special treatment for a bit field split across two aligned words. */ - if (bitsize + bitpos > BITS_PER_WORD) - { - store_split_bit_field (op0, bitsize, bitpos, value, struct_align); - return; - } - } - - mode = GET_MODE (op0); - - /* Now MODE is either QImode, HImode or SImode for a MEM as OP0, - or is SImode for a REG as OP0. TOTAL_BITS corresponds. - The bit field is contained entirely within OP0. - BITPOS is the starting bit number within OP0. - (OP0's mode may actually be narrower than MODE.) */ - -#ifdef BYTES_BIG_ENDIAN - /* BITPOS is the distance between our msb - and that of the containing datum. - Convert it to the distance from the lsb. */ - - bitpos = total_bits - bitsize - bitpos; -#endif - /* Now BITPOS is always the distance between our lsb - and that of OP0. */ - - /* Shift VALUE left by BITPOS bits. If VALUE is not constant, - we must first convert its mode to MODE. */ - - if (GET_CODE (value) == CONST_INT) - { - register int v = INTVAL (value); - - if (bitsize < HOST_BITS_PER_INT) - v &= (1 << bitsize) - 1; - - if (v == 0) - all_zero = 1; - else if (bitsize < HOST_BITS_PER_INT && v == (1 << bitsize) - 1) - all_one = 1; - - value = gen_rtx (CONST_INT, VOIDmode, v << bitpos); - } - else - { - int must_and = (GET_MODE_BITSIZE (GET_MODE (value)) != bitsize); - - if (GET_MODE (value) != mode) - { - if ((GET_CODE (value) == REG || GET_CODE (value) == SUBREG) - && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (value))) - value = gen_lowpart (mode, value); - else - value = convert_to_mode (mode, value, 1); - } - - if (must_and && bitsize < HOST_BITS_PER_INT) - value = expand_bit_and (mode, value, - gen_rtx (CONST_INT, VOIDmode, - (1 << bitsize) - 1), - 0); - if (bitpos > 0) - value = expand_shift (LSHIFT_EXPR, mode, value, - build_int_2 (bitpos, 0), 0, 1); - } - - /* Now clear the chosen bits in OP0, - except that if VALUE is -1 we need not bother. */ - - subtarget = op0; - - if (! all_one) - subtarget = expand_bit_and (mode, op0, - gen_rtx (CONST_INT, VOIDmode, - (~ (((unsigned) ~0 - >> (HOST_BITS_PER_INT - bitsize)) - << bitpos)) - & ((GET_MODE_BITSIZE (mode) - == HOST_BITS_PER_INT) - ? -1 - : ((1 << GET_MODE_BITSIZE (mode)) - 1))), - subtarget); - - /* Now logical-or VALUE into OP0, unless it is zero. */ - - if (! all_zero) - subtarget = expand_binop (mode, ior_optab, subtarget, value, - op0, 1, OPTAB_LIB_WIDEN); - if (op0 != subtarget) - emit_move_insn (op0, subtarget); -} - -/* Store a bit field that is split across two words. - - OP0 is the REG, SUBREG or MEM rtx for the first of the two words. - BITSIZE is the field width; BITPOS the position of its first bit - (within the word). - VALUE is the value to store. */ - -static void -store_split_bit_field (op0, bitsize, bitpos, value, align) - rtx op0; - int bitsize, bitpos; - rtx value; - int align; -{ - /* BITSIZE_1 is size of the part in the first word. */ - int bitsize_1 = BITS_PER_WORD - bitpos; - /* BITSIZE_2 is size of the rest (in the following word). */ - int bitsize_2 = bitsize - bitsize_1; - rtx part1, part2; - - /* Alignment of VALUE, after conversion. */ - int valalign = GET_MODE_SIZE (SImode); - - if (GET_MODE (value) != VOIDmode) - value = convert_to_mode (SImode, value, 1); - if (CONSTANT_P (value) && GET_CODE (value) != CONST_INT) - value = copy_to_reg (value); - - /* Split the value into two parts: - PART1 gets that which goes in the first word; PART2 the other. */ -#ifdef BYTES_BIG_ENDIAN - /* PART1 gets the more significant part. */ - if (GET_CODE (value) == CONST_INT) - { - part1 = gen_rtx (CONST_INT, VOIDmode, - (unsigned) (INTVAL (value)) >> bitsize_2); - part2 = gen_rtx (CONST_INT, VOIDmode, - (unsigned) (INTVAL (value)) & ((1 << bitsize_2) - 1)); - } - else - { - part1 = extract_fixed_bit_field (SImode, value, 0, bitsize_1, - BITS_PER_WORD - bitsize, 0, 1, valalign); - part2 = extract_fixed_bit_field (SImode, value, 0, bitsize_2, - BITS_PER_WORD - bitsize_2, 0, 1, valalign); - } -#else - /* PART1 gets the less significant part. */ - if (GET_CODE (value) == CONST_INT) - { - part1 = gen_rtx (CONST_INT, VOIDmode, - (unsigned) (INTVAL (value)) & ((1 << bitsize_1) - 1)); - part2 = gen_rtx (CONST_INT, VOIDmode, - (unsigned) (INTVAL (value)) >> bitsize_1); - } - else - { - part1 = extract_fixed_bit_field (SImode, value, 0, bitsize_1, 0, - 0, 1, valalign); - part2 = extract_fixed_bit_field (SImode, value, 0, bitsize_2, - bitsize_1, 0, 1, valalign); - } -#endif - - /* Store PART1 into the first word. */ - store_fixed_bit_field (op0, 0, bitsize_1, bitpos, part1, align); - - /* Offset op0 to get to the following word. */ - if (GET_CODE (op0) == MEM) - op0 = change_address (op0, SImode, - plus_constant (XEXP (op0, 0), UNITS_PER_WORD)); - else if (GET_CODE (op0) == REG) - op0 = gen_rtx (SUBREG, SImode, op0, 1); - else if (GET_CODE (op0) == SUBREG) - op0 = gen_rtx (SUBREG, SImode, SUBREG_REG (op0), SUBREG_WORD (op0) + 1); - - /* Store PART2 into the second word. */ - store_fixed_bit_field (op0, 0, bitsize_2, 0, part2, align); -} - -/* Generate code to extract a byte-field from STR_RTX - containing BITSIZE bits, starting at BITNUM, - and put it in TARGET if possible (if TARGET is nonzero). - Regardless of TARGET, we return the rtx for where the value is placed. - It may be a QUEUED. - - STR_RTX is the structure containing the byte (a REG or MEM). - UNSIGNEDP is nonzero if this is an unsigned bit field. - MODE is the natural mode of the field value once extracted. - TMODE is the mode the caller would like the value to have; - but the value may be returned with type MODE instead. - - ALIGN is the alignment that STR_RTX is known to have, measured in bytes. - TOTAL_SIZE is the total size in bytes of the structure, if known. - Otherwise it is -1. - - If a TARGET is specified and we can store in it at no extra cost, - we do so, and return TARGET. - Otherwise, we return a REG of mode TMODE or MODE, with TMODE preferred - if they are equally easy. */ - -rtx -extract_bit_field (str_rtx, bitsize, bitnum, unsignedp, - target, mode, tmode, align, total_size) - rtx str_rtx; - register int bitsize; - int bitnum; - int unsignedp; - rtx target; - enum machine_mode mode, tmode; - int align; - int total_size; -{ - int unit = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD; - register int offset = bitnum / unit; - register int bitpos = bitnum % unit; - register rtx op0 = str_rtx; - rtx spec_target = target; - rtx bitsize_rtx, bitpos_rtx; - rtx spec_target_subreg = 0; - - /* Discount the part of the structure before the desired byte. - We need to know how many bytes are safe to reference after it. */ - if (total_size >= 0) - total_size -= (bitpos / BIGGEST_ALIGNMENT - * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); - - if (tmode == VOIDmode) - tmode = mode; - - while (GET_CODE (op0) == SUBREG) - { -#ifdef BYTES_BIG_ENDIAN - /* Keep BITPOS counting within the size of op0. */ - bitpos += (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0))) - - GET_MODE_BITSIZE (GET_MODE (op0))); -#endif - offset += SUBREG_WORD (op0); - op0 = SUBREG_REG (op0); - } - -#ifdef BYTES_BIG_ENDIAN - /* If OP0 is a register, BITPOS must count within a word. - But as we have it, it counts within whatever size OP0 now has. - On a bigendian machine, these are not the same, so convert. */ - if (GET_CODE (op0) != MEM && unit > GET_MODE_BITSIZE (GET_MODE (op0))) - { - bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0)); - /* Change the mode now so we don't adjust BITPOS again. */ - if (GET_CODE (op0) == SUBREG) - PUT_MODE (op0, SImode); - else - op0 = gen_rtx (SUBREG, SImode, op0, 0); - } -#endif - - /* Extracting a full-word or multi-word value - from a structure in a register. - This can be done with just SUBREG. - So too extracting a subword value in - the least significant part of the register. */ - - if (GET_CODE (op0) == REG - && ((bitsize >= BITS_PER_WORD && bitsize == GET_MODE_BITSIZE (mode) - && bitpos % BITS_PER_WORD == 0) - || ((bitsize == GET_MODE_BITSIZE (mode) - || bitsize == GET_MODE_BITSIZE (QImode) - || bitsize == GET_MODE_BITSIZE (HImode)) -#ifdef BYTES_BIG_ENDIAN - && bitpos + bitsize == BITS_PER_WORD -#else - && bitpos == 0 -#endif - ))) - { - enum machine_mode mode1 = mode; - - if (bitsize == GET_MODE_BITSIZE (QImode)) - mode1 = QImode; - if (bitsize == GET_MODE_BITSIZE (HImode)) - mode1 = HImode; - - if (mode1 != GET_MODE (op0)) - { - if (GET_CODE (op0) == SUBREG) - PUT_MODE (op0, mode1); - else - op0 = gen_rtx (SUBREG, mode1, op0, offset); - } - - if (mode1 != mode) - return convert_to_mode (tmode, op0, unsignedp); - return op0; - } - - /* Handle fields bigger than a word. */ - - if (bitsize > BITS_PER_WORD) - { - int low_size = BITS_PER_WORD; - int low_pos = bitpos + offset * unit; - rtx target_low_part, low_part; - int high_size = bitsize - low_size; - int high_pos; - rtx target_high_part, high_part; -#ifdef BYTES_BIG_ENDIAN - high_pos = low_pos; - low_pos += high_size; -#else - high_pos = low_pos + low_size; -#endif - - if (target == 0 || GET_CODE (target) != REG) - target = gen_reg_rtx (mode); - - /* Extract the low part of the bitfield, and make sure - to store it in the low part of TARGET. */ - target_low_part = gen_lowpart (SImode, target); - low_part = extract_bit_field (op0, low_size, low_pos, 1, - target_low_part, SImode, SImode, - align, total_size); - if (low_part != target_low_part) - emit_move_insn (target_low_part, low_part); - - /* Likewise for the high part. */ - target_high_part = gen_highpart (SImode, target); - high_part = extract_bit_field (op0, high_size, high_pos, unsignedp, - target_high_part, SImode, SImode, - align, total_size); - if (high_part != target_high_part) - emit_move_insn (target_high_part, high_part); - - return target; - } - - /* From here on we know the desired field is smaller than a word - so we can assume it is an integer. So we can safely extract it as one - size of integer, if necessary, and then truncate or extend - to the size that is wanted. */ - - /* OFFSET is the number of words or bytes (UNIT says which) - from STR_RTX to the first word or byte containing part of the field. */ - - if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) - { - /* If not in memory, merge in the offset now. */ - if (offset != 0 - || GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (SImode)) - { - if (GET_CODE (op0) == SUBREG) - SUBREG_WORD (op0) += offset; - else - op0 = gen_rtx (SUBREG, SImode, op0, offset); - } - offset = 0; - } - else - { - op0 = protect_from_queue (str_rtx, 1); - } - - /* Now OFFSET is nonzero only for memory operands. */ - - if (unsignedp) - { -#ifdef HAVE_extzv - if (HAVE_extzv) - { - int xbitpos = bitpos, xoffset = offset; - rtx last = get_last_insn(); - rtx xop0 = op0; - rtx xtarget = target; - rtx xspec_target = spec_target; - rtx xspec_target_subreg = spec_target_subreg; - rtx pat; - - if (GET_CODE (xop0) == MEM) - { - /* Is the memory operand acceptable? */ - if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][1]) - (xop0, GET_MODE (xop0)))) - { - /* No, load into a reg and extract from there. */ - enum machine_mode bestmode = VOIDmode, trymode; - /* Don't use a mode bigger than the one of the value - to be fetched. That mode must be okay, - since a bit field can be that big. */ - int maxsize - = GET_MODE_SIZE (insn_operand_mode[(int) CODE_FOR_extzv][0]); - /* This used to use the mode desired for operand 1, - but that is normally QImode on most machines, - and QImode won't work for fields that cross byte - boundaries. */ - - /* Also don't use a mode bigger than the structure. */ - if (total_size >= 0 && maxsize > total_size) - maxsize = total_size; - - /* Find biggest machine mode we can safely use - to fetch from this structure. - But don't use a bigger mode than the insn wants. */ - for (trymode = QImode; - trymode && GET_MODE_SIZE (trymode) <= maxsize; - trymode = GET_MODE_WIDER_MODE (trymode)) - if (GET_MODE_SIZE (trymode) <= align - || align == BIGGEST_ALIGNMENT / BITS_PER_UNIT) - bestmode = trymode; - if (! bestmode) - abort (); - unit = GET_MODE_BITSIZE (bestmode); - - /* Compute offset as multiple of this unit, - counting in bytes. */ - xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode); - xbitpos = bitnum % unit; - xop0 = change_address (xop0, bestmode, - plus_constant (XEXP (xop0, 0), - xoffset)); - /* Fetch it to a register in that size. */ - xop0 = force_reg (bestmode, xop0); - - /* Now ref the register in the mode extzv wants. */ - /* We used to use the mode from operand 1 in the md, - but that is often QImode because that's needed for MEM. - Here we need SImode instead. */ - if (bestmode != SImode) - xop0 = gen_rtx (SUBREG, SImode, xop0, 0); -#ifdef BYTES_BIG_ENDIAN - if (GET_MODE_BITSIZE (GET_MODE (xop0)) > unit) - xbitpos += GET_MODE_BITSIZE (GET_MODE (xop0)) - unit; -#endif - } - else - /* Get ref to first byte containing part of the field. */ - xop0 = change_address (xop0, QImode, - plus_constant (XEXP (xop0, 0), xoffset)); - } - - /* If op0 is a register, we need it in SImode - to make it acceptable to the format of extzv. */ - if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != SImode) - abort (); - if (GET_CODE (xop0) == REG && GET_MODE (xop0) != SImode) - { -#ifdef BYTES_BIG_ENDIAN - xbitpos += (GET_MODE_BITSIZE (SImode) - - GET_MODE_BITSIZE (GET_MODE (xop0))); -#endif - xop0 = gen_rtx (SUBREG, SImode, xop0, 0); - } - - if (xtarget == 0 - || (flag_force_mem && GET_CODE (xtarget) == MEM)) - xtarget = xspec_target = gen_reg_rtx (tmode); - - if (GET_MODE (xtarget) != SImode) - { - if (GET_CODE (xtarget) == REG) - xspec_target_subreg = xtarget = gen_lowpart (SImode, xtarget); - else - xtarget = gen_reg_rtx (SImode); - } - - /* If this machine's extzv insists on a register target, - make sure we have one. */ - if (! (*insn_operand_predicate[(int) CODE_FOR_extzv][0]) (xtarget, SImode)) - xtarget = gen_reg_rtx (SImode); - - /* On big-endian machines, we count bits from the most significant. - If the bit field insn does not, we must invert. */ -#if defined (BITS_BIG_ENDIAN) != defined (BYTES_BIG_ENDIAN) - xbitpos = unit - 1 - xbitpos; -#endif - - bitsize_rtx = gen_rtx (CONST_INT, VOIDmode, bitsize); - bitpos_rtx = gen_rtx (CONST_INT, VOIDmode, xbitpos); - - pat = gen_extzv (protect_from_queue (xtarget, 1), - xop0, bitsize_rtx, bitpos_rtx); - if (pat) - { - emit_insn (pat); - target = xtarget; - spec_target = xspec_target; - spec_target_subreg = xspec_target_subreg; - } - else - { - delete_insns_since (last); - target = extract_fixed_bit_field (tmode, op0, offset, bitsize, - bitpos, target, 1, align); - } - } - else -#endif - target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos, - target, 1, align); - } - else - { -#ifdef HAVE_extv - if (HAVE_extv) - { - int xbitpos = bitpos, xoffset = offset; - rtx last = get_last_insn(); - rtx xop0 = op0, xtarget = target; - rtx xspec_target = spec_target; - rtx xspec_target_subreg = spec_target_subreg; - rtx pat; - - if (GET_CODE (xop0) == MEM) - { - /* Is the memory operand acceptable? */ - if (! ((*insn_operand_predicate[(int) CODE_FOR_extv][1]) - (xop0, GET_MODE (xop0)))) - { - /* No, load into a reg and extract from there. */ - enum machine_mode bestmode = VOIDmode, trymode; - /* Don't use a mode bigger than the one of the value - to be fetched. That mode must be okay, - since a bit field can be that big. */ - int maxsize - = GET_MODE_SIZE (insn_operand_mode[(int) CODE_FOR_extv][0]); - /* This used to use the mode desired for operand 1, - but that is normally QImode on most machines, - and QImode won't work for fields that cross byte - boundaries. */ - - /* Also don't use a mode bigger than the structure. */ - if (total_size >= 0 && maxsize > total_size) - maxsize = total_size; - - /* Find biggest machine mode we can safely use - to fetch from this structure. - But don't use a bigger mode than the insn wants. */ - for (trymode = QImode; - trymode && GET_MODE_SIZE (trymode) <= maxsize; - trymode = GET_MODE_WIDER_MODE (trymode)) - if (GET_MODE_SIZE (trymode) <= align - || align == BIGGEST_ALIGNMENT / BITS_PER_UNIT) - bestmode = trymode; - if (! bestmode) - abort (); - unit = GET_MODE_BITSIZE (bestmode); - - /* Compute offset as multiple of this unit, - counting in bytes. */ - xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode); - xbitpos = bitnum % unit; - xop0 = change_address (xop0, bestmode, - plus_constant (XEXP (xop0, 0), - xoffset)); - /* Fetch it to a register in that size. */ - xop0 = force_reg (bestmode, xop0); - - /* Now ref the register in the mode extv wants. */ - /* We used to use the mode from operand 1 in the md, - but that is often QImode because that's needed for MEM. - Here we need SImode instead. */ - if (bestmode != SImode) - xop0 = gen_rtx (SUBREG, SImode, xop0, 0); -#ifdef BYTES_BIG_ENDIAN - if (GET_MODE_BITSIZE (GET_MODE (xop0)) > unit) - xbitpos += GET_MODE_BITSIZE (GET_MODE (xop0)) - unit; -#endif - } - else - /* Get ref to first byte containing part of the field. */ - xop0 = change_address (xop0, QImode, - plus_constant (XEXP (xop0, 0), xoffset)); - } - - /* If op0 is a register, we need it in SImode - to make it acceptable to the format of extv. */ - if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != SImode) - abort (); - if (GET_CODE (xop0) == REG && GET_MODE (xop0) != SImode) - { -#ifdef BYTES_BIG_ENDIAN - xbitpos += (GET_MODE_BITSIZE (SImode) - - GET_MODE_BITSIZE (GET_MODE (xop0))); -#endif - xop0 = gen_rtx (SUBREG, SImode, xop0, 0); - } - - if (xtarget == 0 - || (flag_force_mem && GET_CODE (xtarget) == MEM)) - xtarget = xspec_target = gen_reg_rtx (tmode); - - if (GET_MODE (xtarget) != SImode) - { - if (GET_CODE (xtarget) == REG) - xspec_target_subreg = xtarget = gen_lowpart (SImode, xtarget); - else - xtarget = gen_reg_rtx (SImode); - } - - /* If this machine's extv insists on a register target, - make sure we have one. */ - if (! (*insn_operand_predicate[(int) CODE_FOR_extv][0]) (xtarget, SImode)) - xtarget = gen_reg_rtx (SImode); - - /* On big-endian machines, we count bits from the most significant. - If the bit field insn does not, we must invert. */ -#if defined (BITS_BIG_ENDIAN) != defined (BYTES_BIG_ENDIAN) - xbitpos = unit - 1 - xbitpos; -#endif - - bitsize_rtx = gen_rtx (CONST_INT, VOIDmode, bitsize); - bitpos_rtx = gen_rtx (CONST_INT, VOIDmode, xbitpos); - - pat = gen_extv (protect_from_queue (xtarget, 1), - xop0, bitsize_rtx, bitpos_rtx); - if (pat) - { - emit_insn (pat); - target = xtarget; - spec_target = xspec_target; - spec_target_subreg = xspec_target_subreg; - } - else - { - delete_insns_since (last); - target = extract_fixed_bit_field (tmode, op0, offset, bitsize, - bitpos, target, 0, align); - } - } - else -#endif - target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos, - target, 0, align); - } - if (target == spec_target) - return target; - if (target == spec_target_subreg) - return spec_target; - if (GET_MODE (target) != tmode && GET_MODE (target) != mode) - return convert_to_mode (tmode, target, unsignedp); - return target; -} - -/* Extract a bit field using shifts and boolean operations - Returns an rtx to represent the value. - OP0 addresses a register (word) or memory (byte). - BITPOS says which bit within the word or byte the bit field starts in. - OFFSET says how many bytes farther the bit field starts; - it is 0 if OP0 is a register. - BITSIZE says how many bits long the bit field is. - (If OP0 is a register, it may be narrower than SImode, - but BITPOS still counts within a full word, - which is significant on bigendian machines.) - - UNSIGNEDP is nonzero for an unsigned bit field (don't sign-extend value). - If TARGET is nonzero, attempts to store the value there - and return TARGET, but this is not guaranteed. - If TARGET is not used, create a pseudo-reg of mode TMODE for the value. - - ALIGN is the alignment that STR_RTX is known to have, measured in bytes. */ - -static rtx -extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos, - target, unsignedp, align) - enum machine_mode tmode; - register rtx op0, target; - register int offset, bitsize, bitpos; - int unsignedp; - int align; -{ - int total_bits = BITS_PER_WORD; - enum machine_mode mode; - - if (GET_CODE (op0) == SUBREG || GET_CODE (op0) == REG) - { - /* Special treatment for a bit field split across two registers. */ - if (bitsize + bitpos > BITS_PER_WORD) - return extract_split_bit_field (op0, bitsize, bitpos, - unsignedp, align); - } - else if (bitsize + bitpos <= BITS_PER_UNIT - && (! SLOW_BYTE_ACCESS - || (align == 1 - && BIGGEST_ALIGNMENT > 1))) - { - /* It fits in one byte, and either bytes are fast - or the alignment won't let us use anything bigger. */ - total_bits = BITS_PER_UNIT; - op0 = change_address (op0, QImode, - plus_constant (XEXP (op0, 0), offset)); - } - else if ((bitsize + bitpos + (offset % GET_MODE_SIZE (HImode)) * BITS_PER_UNIT - <= GET_MODE_BITSIZE (HImode)) - /* If halfwords are fast, use them whenever valid. */ - && (! SLOW_BYTE_ACCESS - /* Use halfwords if larger is invalid due to alignment. */ - || (align == GET_MODE_SIZE (HImode) - && BIGGEST_ALIGNMENT > GET_MODE_SIZE (HImode)))) - { - /* It fits in an aligned halfword, and either halfwords are fast - or the alignment won't let us use anything bigger. */ - total_bits = GET_MODE_BITSIZE (HImode); - - /* Get ref to halfword containing the field. */ - bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT; - offset -= (offset % (total_bits / BITS_PER_UNIT)); - op0 = change_address (op0, HImode, - plus_constant (XEXP (op0, 0), offset)); - } - else - { - /* Get ref to word containing the field. */ - /* Adjust BITPOS to be position within a word, - and OFFSET to be the offset of that word. */ - bitpos += (offset % (BITS_PER_WORD / BITS_PER_UNIT)) * BITS_PER_UNIT; - offset -= (offset % (BITS_PER_WORD / BITS_PER_UNIT)); - op0 = change_address (op0, SImode, - plus_constant (XEXP (op0, 0), offset)); - - /* Special treatment for a bit field split across two words. */ - if (bitsize + bitpos > BITS_PER_WORD) - return extract_split_bit_field (op0, bitsize, bitpos, - unsignedp, align); - } - - mode = GET_MODE (op0); - -#ifdef BYTES_BIG_ENDIAN - /* BITPOS is the distance between our msb and that of OP0. - Convert it to the distance from the lsb. */ - - bitpos = total_bits - bitsize - bitpos; -#endif - /* Now BITPOS is always the distance between the field's lsb and that of OP0. - We have reduced the big-endian case to the little-endian case. */ - - if (unsignedp) - { - if (bitpos) - { - /* If the field does not already start at the lsb, - shift it so it does. */ - tree amount = build_int_2 (bitpos, 0); - /* Maybe propagate the target for the shift. */ - /* But not if we will return it--could confuse integrate.c. */ - rtx subtarget = (target != 0 && GET_CODE (target) == REG - && !REG_FUNCTION_VALUE_P (target) - ? target : 0); - if (tmode != mode) subtarget = 0; - op0 = expand_shift (RSHIFT_EXPR, mode, op0, amount, subtarget, 1); - } - /* Convert the value to the desired mode. */ - if (mode != tmode) - op0 = convert_to_mode (tmode, op0, 1); - - /* Unless the msb of the field used to be the msb when we shifted, - mask out the upper bits. */ - - if ((GET_MODE_BITSIZE (mode) != bitpos + bitsize -#if 0 -#ifdef SLOW_ZERO_EXTEND - /* Always generate an `and' if - we just zero-extended op0 and SLOW_ZERO_EXTEND, since it - will combine fruitfully with the zero-extend. */ - || tmode != mode -#endif -#endif - ) - && bitsize < HOST_BITS_PER_INT) - return expand_bit_and (GET_MODE (op0), op0, - gen_rtx (CONST_INT, VOIDmode, (1 << bitsize) - 1), - target); - return op0; - } - - /* To extract a signed bit-field, first shift its msb to the msb of the word, - then arithmetic-shift its lsb to the lsb of the word. */ - op0 = force_reg (mode, op0); - if (mode != tmode) - target = 0; - if (GET_MODE_BITSIZE (QImode) < GET_MODE_BITSIZE (mode) - && GET_MODE_BITSIZE (QImode) >= bitsize + bitpos) - mode = QImode, op0 = convert_to_mode (QImode, op0, 0); - if (GET_MODE_BITSIZE (HImode) < GET_MODE_BITSIZE (mode) - && GET_MODE_BITSIZE (HImode) >= bitsize + bitpos) - mode = HImode, op0 = convert_to_mode (HImode, op0, 0); - if (GET_MODE_BITSIZE (mode) != (bitsize + bitpos)) - { - tree amount = build_int_2 (GET_MODE_BITSIZE (mode) - (bitsize + bitpos), 0); - /* Maybe propagate the target for the shift. */ - /* But not if we will return the result--could confuse integrate.c. */ - rtx subtarget = (target != 0 && GET_CODE (target) == REG - && ! REG_FUNCTION_VALUE_P (target) - ? target : 0); - op0 = expand_shift (LSHIFT_EXPR, mode, op0, amount, subtarget, 1); - } - - return expand_shift (RSHIFT_EXPR, mode, op0, - build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0), - target, 0); -} - -/* Extract a bit field that is split across two words - and return an RTX for the result. - - OP0 is the REG, SUBREG or MEM rtx for the first of the two words. - BITSIZE is the field width; BITPOS, position of its first bit, in the word. - UNSIGNEDP is 1 if should zero-extend the contents; else sign-extend. */ - -static rtx -extract_split_bit_field (op0, bitsize, bitpos, unsignedp, align) - rtx op0; - int bitsize, bitpos, unsignedp, align; -{ - /* BITSIZE_1 is size of the part in the first word. */ - int bitsize_1 = BITS_PER_WORD - bitpos; - /* BITSIZE_2 is size of the rest (in the following word). */ - int bitsize_2 = bitsize - bitsize_1; - rtx part1, part2, result; - - /* Get the part of the bit field from the first word. */ - part1 = extract_fixed_bit_field (SImode, op0, 0, bitsize_1, bitpos, - 0, 1, align); - - /* Offset op0 by 1 word to get to the following one. */ - if (GET_CODE (op0) == MEM) - op0 = change_address (op0, SImode, - plus_constant (XEXP (op0, 0), UNITS_PER_WORD)); - else if (GET_CODE (op0) == REG) - op0 = gen_rtx (SUBREG, SImode, op0, 1); - else - op0 = gen_rtx (SUBREG, SImode, SUBREG_REG (op0), SUBREG_WORD (op0) + 1); - - /* Get the part of the bit field from the second word. */ - part2 = extract_fixed_bit_field (SImode, op0, 0, bitsize_2, 0, 0, 1, align); - - /* Shift the more significant part up to fit above the other part. */ -#ifdef BYTES_BIG_ENDIAN - part1 = expand_shift (LSHIFT_EXPR, SImode, part1, - build_int_2 (bitsize_2, 0), 0, 1); -#else - part2 = expand_shift (LSHIFT_EXPR, SImode, part2, - build_int_2 (bitsize_1, 0), 0, 1); -#endif - - /* Combine the two parts with bitwise or. This works - because we extracted both parts as unsigned bit fields. */ - result = expand_binop (SImode, ior_optab, part1, part2, 0, 1, - OPTAB_LIB_WIDEN); - - /* Unsigned bit field: we are done. */ - if (unsignedp) - return result; - /* Signed bit field: sign-extend with two arithmetic shifts. */ - result = expand_shift (LSHIFT_EXPR, SImode, result, - build_int_2 (BITS_PER_WORD - bitsize, 0), 0, 0); - return expand_shift (RSHIFT_EXPR, SImode, result, - build_int_2 (BITS_PER_WORD - bitsize, 0), 0, 0); -} - -/* Add INC into TARGET. */ - -void -expand_inc (target, inc) - rtx target, inc; -{ - rtx value = expand_binop (GET_MODE (target), add_optab, - target, inc, - target, 0, OPTAB_LIB_WIDEN); - if (value != target) - emit_move_insn (target, value); -} - -/* Subtract INC from TARGET. */ - -void -expand_dec (target, dec) - rtx target, dec; -{ - rtx value = expand_binop (GET_MODE (target), sub_optab, - target, dec, - target, 0, OPTAB_LIB_WIDEN); - if (value != target) - emit_move_insn (target, value); -} - -/* Output a shift instruction for expression code CODE, - with SHIFTED being the rtx for the value to shift, - and AMOUNT the tree for the amount to shift by. - Store the result in the rtx TARGET, if that is convenient. - If UNSIGNEDP is nonzero, do a logical shift; otherwise, arithmetic. - Return the rtx for where the value is. */ - -/* Pastel, for shifts, converts shift count to SImode here - independent of the mode being shifted. - Should that be done in an earlier pass? - It turns out not to matter for C. */ - -rtx -expand_shift (code, mode, shifted, amount, target, unsignedp) - enum tree_code code; - register enum machine_mode mode; - rtx shifted; - tree amount; - register rtx target; - int unsignedp; -{ - register rtx op1, temp = 0; - register int left = (code == LSHIFT_EXPR || code == LROTATE_EXPR); - int try; - int rotate = code == LROTATE_EXPR || code == RROTATE_EXPR; - rtx last; - - /* Previously detected shift-counts computed by NEGATE_EXPR - and shifted in the other direction; but that does not work - on all machines. */ - - op1 = expand_expr (amount, 0, VOIDmode, 0); - - last = get_last_insn (); - - for (try = 0; temp == 0 && try < 3; try++) - { - enum optab_methods methods; - delete_insns_since (last); - - if (try == 0) - methods = OPTAB_DIRECT; - else if (try == 1) - methods = OPTAB_WIDEN; - else - methods = OPTAB_LIB_WIDEN; - - if (rotate) - { - /* Widening does not work for rotation. */ - if (methods != OPTAB_DIRECT) - methods = OPTAB_LIB; - - temp = expand_binop (mode, - left ? rotl_optab : rotr_optab, - shifted, op1, target, -1, methods); - } - else if (unsignedp) - { - temp = expand_binop (mode, - left ? lshl_optab : lshr_optab, - shifted, op1, target, unsignedp, methods); - if (temp == 0 && left) - temp = expand_binop (mode, ashl_optab, - shifted, op1, target, unsignedp, methods); - if (temp != 0) - return temp; - } - /* Do arithmetic shifts. - Also, if we are going to widen the operand, we can just as well - use an arithmetic right-shift instead of a logical one. */ - if (! rotate && (! unsignedp || (! left && methods == OPTAB_WIDEN))) - { - enum optab_methods methods1 = methods; - - /* If trying to widen a log shift to an arithmetic shift, - don't accept an arithmetic shift of the same size. */ - if (unsignedp) - methods1 = OPTAB_MUST_WIDEN; - - /* Arithmetic shift */ - - temp = expand_binop (mode, - left ? ashl_optab : ashr_optab, - shifted, op1, target, unsignedp, methods1); - if (temp != 0) - return temp; - } - - if (unsignedp) - { - /* No logical shift insn in either direction => - try a bit-field extract instruction if we have one. */ -#ifdef HAVE_extzv -#ifndef BITS_BIG_ENDIAN - if (HAVE_extzv && !left - && ((methods == OPTAB_DIRECT && mode == SImode) - || (methods == OPTAB_WIDEN - && GET_MODE_SIZE (mode) < GET_MODE_SIZE (SImode)))) - { - rtx shifted1 = convert_to_mode (SImode, shifted, 1); - rtx target1 = target; - - /* If -fforce-mem, don't let the operand be in memory. */ - if (flag_force_mem && GET_CODE (shifted1) == MEM) - shifted1 = force_not_mem (shifted1); - - /* If this machine's extzv insists on a register for - operand 1, arrange for that. */ - if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][1]) - (shifted1, SImode))) - shifted1 = force_reg (SImode, shifted1); - - /* If we don't have or cannot use a suggested target, - make a place for the result, in the proper mode. */ - if (methods == OPTAB_WIDEN || target1 == 0 - || ! ((*insn_operand_predicate[(int) CODE_FOR_extzv][0]) - (target1, SImode))) - target1 = gen_reg_rtx (SImode); - - op1 = convert_to_mode (SImode, op1, 0); - - /* If this machine's extzv insists on a register for - operand 3, arrange for that. */ - if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][3]) - (op1, SImode))) - op1 = force_reg (SImode, op1); - - op1 = protect_from_queue (op1, 1); - - /* TEMP gets the width of the bit field to extract: - wordsize minus # bits to shift by. */ - if (GET_CODE (op1) == CONST_INT) - temp = gen_rtx (CONST_INT, VOIDmode, - (GET_MODE_BITSIZE (mode) - INTVAL (op1))); - else - temp = expand_binop (SImode, sub_optab, - gen_rtx (CONST_INT, VOIDmode, - GET_MODE_BITSIZE (mode)), - op1, gen_reg_rtx (SImode), - 0, OPTAB_LIB_WIDEN); - /* Now extract with width TEMP, omitting OP1 least sig bits. */ - emit_insn (gen_extzv (protect_from_queue (target1, 1), - protect_from_queue (shifted1, 0), - temp, op1)); - return convert_to_mode (mode, target1, 1); - } - /* Can also do logical shift with signed bit-field extract - followed by inserting the bit-field at a different position. - That strategy is not yet implemented. */ -#endif /* not BITS_BIG_ENDIAN */ -#endif /* HAVE_extzv */ - /* We have failed to generate the logical shift and will abort. */ - } - } - if (temp == 0) - abort (); - return temp; -} - -/* Output an instruction or two to bitwise-and OP0 with OP1 - in mode MODE, with output to TARGET if convenient and TARGET is not zero. - Returns where the result is. */ -/* This function used to do more; now it could be eliminated. */ - -rtx -expand_bit_and (mode, op0, op1, target) - enum machine_mode mode; - rtx op0, op1, target; -{ - register rtx temp; - - /* First try to open-code it directly. */ - temp = expand_binop (mode, and_optab, op0, op1, target, 1, OPTAB_LIB_WIDEN); - if (temp == 0) - abort (); - return temp; -} - -/* Perform a multiplication and return an rtx for the result. - MODE is mode of value; OP0 and OP1 are what to multiply (rtx's); - TARGET is a suggestion for where to store the result (an rtx). - - We check specially for a constant integer as OP1. - If you want this check for OP0 as well, then before calling - you should swap the two operands if OP0 would be constant. */ - -rtx -expand_mult (mode, op0, op1, target, unsignedp) - enum machine_mode mode; - register rtx op0, op1, target; - int unsignedp; -{ - /* Don't use the function value register as a target - since we have to read it as well as write it, - and function-inlining gets confused by this. */ - if (target && REG_P (target) && REG_FUNCTION_VALUE_P (target)) - target = 0; - - if (GET_CODE (op1) == CONST_INT) - { - register int foo; - int bar; - int negate = INTVAL (op1) < 0; - int absval = INTVAL (op1) * (negate ? -1 : 1); - - /* Is multiplier a power of 2, or minus that? */ - foo = exact_log2 (absval); - if (foo >= 0) - { - rtx tem; - if (foo == 0) - tem = op0; - else - tem = expand_shift (LSHIFT_EXPR, mode, op0, - build_int_2 (foo, 0), - target, 0); - return (negate - ? expand_unop (mode, neg_optab, tem, target, 0) - : tem); - } - /* Is multiplier a sum of two powers of 2, or minus that? */ - bar = floor_log2 (absval); - foo = exact_log2 (absval - (1 << bar)); - if (bar >= 0 && foo >= 0) - { - rtx tem = - force_operand (gen_rtx (PLUS, mode, - expand_shift (LSHIFT_EXPR, mode, op0, - build_int_2 (bar - foo, 0), - 0, 0), - op0), - ((foo == 0 && ! negate) ? target : 0)); - - if (foo != 0) - tem = expand_shift (LSHIFT_EXPR, mode, tem, - build_int_2 (foo, 0), - negate ? 0 : target, 0); - - return negate ? expand_unop (mode, neg_optab, tem, target, 0) : tem; - } - } - /* This used to use umul_optab if unsigned, - but I think that for non-widening multiply there is no difference - between signed and unsigned. */ - op0 = expand_binop (mode, smul_optab, - op0, op1, target, unsignedp, OPTAB_LIB_WIDEN); - if (op0 == 0) - abort (); - return op0; -} - -/* Emit the code to divide OP0 by OP1, putting the result in TARGET - if that is convenient, and returning where the result is. - You may request either the quotient or the remainder as the result; - specify REM_FLAG nonzero to get the remainder. - - CODE is the expression code for which kind of division this is; - it controls how rounding is done. MODE is the machine mode to use. - UNSIGNEDP nonzero means do unsigned division. */ - -/* ??? For CEIL_MOD_EXPR, can compute incorrect remainder with ANDI - and then correct it by or'ing in missing high bits - if result of ANDI is nonzero. - For ROUND_MOD_EXPR, can use ANDI and then sign-extend the result. - This could optimize to a bfexts instruction. - But C doesn't use these operations, so their optimizations are - left for later. */ - -rtx -expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp) - int rem_flag; - enum tree_code code; - enum machine_mode mode; - register rtx op0, op1, target; - int unsignedp; -{ - register rtx temp; - int log = -1; - int can_clobber_op0; - int mod_insn_no_good = 0; - rtx adjusted_op0 = op0; - - /* Don't use the function value register as a target - since we have to read it as well as write it, - and function-inlining gets confused by this. */ - if (target && REG_P (target) && REG_FUNCTION_VALUE_P (target)) - target = 0; - - /* Don't clobber an operand while doing a multi-step calculation. */ - if (target) - if ((rem_flag && (reg_mentioned_p (target, op0) - || (GET_CODE (op0) == MEM && GET_CODE (target) == MEM))) - || reg_mentioned_p (target, op1) - || (GET_CODE (op1) == MEM && GET_CODE (target) == MEM)) - target = 0; - - if (target == 0) - target = gen_reg_rtx (mode); - - can_clobber_op0 = (GET_CODE (op0) == REG && op0 == target); - - if (GET_CODE (op1) == CONST_INT) - log = exact_log2 (INTVAL (op1)); - - /* If log is >= 0, we are dividing by 2**log, and will do it by shifting, - which is really floor-division. Otherwise we will really do a divide, - and we assume that is trunc-division. - - We must correct the dividend by adding or subtracting something - based on the divisor, in order to do the kind of rounding specified - by CODE. The correction depends on what kind of rounding is actually - available, and that depends on whether we will shift or divide. */ - - switch (code) - { - case TRUNC_MOD_EXPR: - case TRUNC_DIV_EXPR: - if (log >= 0 && ! unsignedp) - { - rtx label = gen_label_rtx (); - if (! can_clobber_op0) - adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target); - emit_cmp_insn (adjusted_op0, const0_rtx, 0, 0, 0); - emit_jump_insn (gen_bge (label)); - expand_inc (adjusted_op0, plus_constant (op1, -1)); - emit_label (label); - mod_insn_no_good = 1; - } - break; - - case FLOOR_DIV_EXPR: - case FLOOR_MOD_EXPR: - if (log < 0 && ! unsignedp) - { - rtx label = gen_label_rtx (); - if (! can_clobber_op0) - adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target); - emit_cmp_insn (adjusted_op0, const0_rtx, 0, 0, 0); - emit_jump_insn (gen_bge (label)); - expand_dec (adjusted_op0, op1); - expand_inc (adjusted_op0, const1_rtx); - emit_label (label); - mod_insn_no_good = 1; - } - break; - - case CEIL_DIV_EXPR: - case CEIL_MOD_EXPR: - if (! can_clobber_op0) - adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target); - if (log < 0) - { - rtx label = 0; - if (! unsignedp) - { - label = gen_label_rtx (); - emit_cmp_insn (adjusted_op0, const0_rtx, 0, 0, 0); - emit_jump_insn (gen_ble (label)); - } - expand_inc (adjusted_op0, op1); - expand_dec (adjusted_op0, const1_rtx); - if (! unsignedp) - emit_label (label); - } - else - { - adjusted_op0 = expand_binop (GET_MODE (target), add_optab, - adjusted_op0, plus_constant (op1, -1), - 0, 0, OPTAB_LIB_WIDEN); - } - mod_insn_no_good = 1; - break; - - case ROUND_DIV_EXPR: - case ROUND_MOD_EXPR: - if (! can_clobber_op0) - adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target); - if (log < 0) - { - op1 = expand_shift (RSHIFT_EXPR, mode, op1, integer_one_node, 0, 0); - if (! unsignedp) - { - rtx label = gen_label_rtx (); - emit_cmp_insn (adjusted_op0, const0_rtx, 0, 0, 0); - emit_jump_insn (gen_bge (label)); - expand_unop (mode, neg_optab, op1, op1, 0); - emit_label (label); - } - expand_inc (adjusted_op0, op1); - } - else - { - op1 = gen_rtx (CONST_INT, VOIDmode, INTVAL (op1) / 2); - expand_inc (adjusted_op0, op1); - } - mod_insn_no_good = 1; - break; - } - - if (rem_flag && !mod_insn_no_good) - { - /* Try to produce the remainder directly */ - if (log >= 0) - { - return expand_bit_and (mode, adjusted_op0, - gen_rtx (CONST_INT, VOIDmode, - INTVAL (op1) - 1), - target); - } - else - { - /* See if we can do remainder without a library call. */ - temp = sign_expand_binop (mode, umod_optab, smod_optab, - adjusted_op0, op1, target, - unsignedp, OPTAB_WIDEN); - if (temp != 0) - return temp; - /* No luck there. - Can we do remainder and divide at once without a library call? */ - temp = gen_reg_rtx (mode); - if (expand_twoval_binop (unsignedp ? udivmod_optab : sdivmod_optab, - adjusted_op0, op1, - 0, temp, unsignedp)) - return temp; - temp = 0; - } - } - - /* Produce the quotient. */ - if (log >= 0) - temp = expand_shift (RSHIFT_EXPR, mode, adjusted_op0, - build_int_2 (exact_log2 (INTVAL (op1)), 0), - target, unsignedp); - else if (rem_flag && !mod_insn_no_good) - /* If producing quotient in order to subtract for remainder, - and a remainder subroutine would be ok, - don't use a divide subroutine. */ - temp = sign_expand_binop (mode, udiv_optab, sdiv_optab, - adjusted_op0, op1, target, - unsignedp, OPTAB_WIDEN); - else - { - /* Try a quotient insn, but not a library call. */ - temp = sign_expand_binop (mode, udiv_optab, sdiv_optab, - adjusted_op0, op1, target, - unsignedp, OPTAB_WIDEN); - if (temp == 0) - { - /* No luck there. Try a quotient-and-remainder insn, - keeping the quotient alone. */ - temp = gen_reg_rtx (mode); - if (! expand_twoval_binop (unsignedp ? udivmod_optab : sdivmod_optab, - adjusted_op0, op1, - temp, 0, unsignedp)) - temp = 0; - } - - /* If still no luck, use a library call. */ - if (temp == 0) - temp = sign_expand_binop (mode, udiv_optab, sdiv_optab, - adjusted_op0, op1, target, - unsignedp, OPTAB_LIB_WIDEN); - } - - /* If we really want the remainder, get it by subtraction. */ - if (rem_flag) - { - if (temp == 0) - { - /* No divide instruction either. Use library for remainder. */ - temp = sign_expand_binop (mode, umod_optab, smod_optab, - op0, op1, target, - unsignedp, OPTAB_LIB_WIDEN); - } - else - { - /* We divided. Now finish doing X - Y * (X / Y). */ - temp = expand_mult (mode, temp, op1, temp, unsignedp); - if (! temp) abort (); - temp = expand_binop (mode, sub_optab, op0, - temp, target, unsignedp, OPTAB_LIB_WIDEN); - } - } - - if (temp == 0) - abort (); - return temp; -} - -/* Return a tree node with data type TYPE, describing the value of X. - Usually this is an RTL_EXPR, if there is no obvious better choice. */ - -static tree -make_tree (type, x) - tree type; - rtx x; -{ - tree t; - switch (GET_CODE (x)) - { - case CONST_INT: - t = build_int_2 (INTVAL (x), 0); - TREE_TYPE (t) = type; - return fold (t); - - default: - t = make_node (RTL_EXPR); - TREE_TYPE (t) = type; - RTL_EXPR_RTL (t) = x; - /* There are no insns to be output - when this rtl_expr is used. */ - RTL_EXPR_SEQUENCE (t) = 0; - return t; - } -} - -/* Return an rtx representing the value of X * MULT + ADD. - MODE is the machine mode for the computation. - UNSIGNEDP is non-zero to do unsigned multiplication. - This may emit insns. */ - -rtx -expand_mult_add (x, mult, add, mode, unsignedp) - rtx x, mult, add; - enum machine_mode mode; - int unsignedp; -{ - tree type = type_for_size (GET_MODE_BITSIZE (mode), unsignedp); - tree prod = fold (build (MULT_EXPR, type, make_tree (type, x), - make_tree (type, mult))); - tree sum = fold (build (PLUS_EXPR, type, prod, make_tree (type, add))); - return expand_expr (sum, 0, VOIDmode, 0); -} diff --git a/gnu/usr.bin/gcc1/cc1/expr.c b/gnu/usr.bin/gcc1/cc1/expr.c deleted file mode 100644 index a0a361c96c..0000000000 --- a/gnu/usr.bin/gcc1/cc1/expr.c +++ /dev/null @@ -1,5600 +0,0 @@ -/* Convert tree expression to rtl instructions, for GNU compiler. - Copyright (C) 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "config.h" -#include "rtl.h" -#include "tree.h" -#include "flags.h" -#include "insn-flags.h" -#include "insn-codes.h" -#include "expr.h" -#include "insn-config.h" -#include "recog.h" -#include "gvarargs.h" -#include "typeclass.h" -#include "recog.h" - -/* Decide whether a function's arguments should be processed - from first to last or from last to first. */ - -#ifdef STACK_GROWS_DOWNWARD -#ifdef PUSH_ROUNDING -#define PUSH_ARGS_REVERSED /* If it's last to first */ -#endif -#endif - -/* Like STACK_BOUNDARY but in units of bytes, not bits. */ -#define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT) - -/* If this is nonzero, we do not bother generating VOLATILE - around volatile memory references, and we are willing to - output indirect addresses. If cse is to follow, we reject - indirect addresses so a useful potential cse is generated; - if it is used only once, instruction combination will produce - the same indirect address eventually. */ -int cse_not_expected; - -/* Nonzero to generate code for all the subroutines within an - expression before generating the upper levels of the expression. - Nowadays this is never zero. */ -int do_preexpand_calls = 1; - -/* Number of units that we should eventually pop off the stack. - These are the arguments to function calls that have already returned. */ -int pending_stack_adjust; - -/* Nonzero means stack pops must not be deferred, and deferred stack - pops must not be output. It is nonzero inside a function call, - inside a conditional expression, inside a statement expression, - and in other cases as well. */ -int inhibit_defer_pop; - -/* A list of all cleanups which belong to the arguments of - function calls being expanded by expand_call. */ -static tree cleanups_of_this_call; - -/* Nonzero means __builtin_saveregs has already been done in this function. - The value is the pseudoreg containing the value __builtin_saveregs - returned. */ -static rtx saveregs_value; - -/* Nonzero means current function may call alloca - as a subroutine. (__builtin_alloca does not count.) */ -int may_call_alloca; - -rtx store_expr (); -static void store_constructor (); -static rtx store_field (); -static rtx expand_call (); -static void emit_call_1 (); -static rtx prepare_call_address (); -static rtx expand_builtin (); -static rtx compare (); -static rtx compare_constants (); -static rtx compare1 (); -static rtx do_store_flag (); -static void preexpand_calls (); -static rtx expand_increment (); -static void init_queue (); - -void do_pending_stack_adjust (); - -/* MOVE_RATIO is the number of move instructions that is better than - a block move. */ - -#ifndef MOVE_RATIO -#if defined (HAVE_movstrqi) || defined (HAVE_movstrhi) || defined (HAVE_movstrsi) -#define MOVE_RATIO 2 -#else -/* A value of around 6 would minimize code size; infinity would minimize - execution time. */ -#define MOVE_RATIO 15 -#endif -#endif - -/* Table indexed by tree code giving 1 if the code is for a - comparison operation, or anything that is most easily - computed with a conditional branch. - - We include tree.def to give it the proper length. - The contents thus created are irrelevant. - The real contents are initialized in init_comparisons. */ - -#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) 0, - -static char comparison_code[] = { -#include "tree.def" -}; -#undef DEFTREECODE - -/* This is run once per compilation. */ - -void -init_comparisons () -{ - comparison_code[(int) EQ_EXPR] = 1; - comparison_code[(int) NE_EXPR] = 1; - comparison_code[(int) LT_EXPR] = 1; - comparison_code[(int) GT_EXPR] = 1; - comparison_code[(int) LE_EXPR] = 1; - comparison_code[(int) GE_EXPR] = 1; -} - -/* This is run at the start of compiling a function. */ - -void -init_expr () -{ - init_queue (); - may_call_alloca = 0; - saveregs_value = 0; -} - -/* Manage the queue of increment instructions to be output - for POSTINCREMENT_EXPR expressions, etc. */ - -static rtx pending_chain; - -/* Queue up to increment (or change) VAR later. BODY says how: - BODY should be the same thing you would pass to emit_insn - to increment right away. It will go to emit_insn later on. - - The value is a QUEUED expression to be used in place of VAR - where you want to guarantee the pre-incrementation value of VAR. */ - -static rtx -enqueue_insn (var, body) - rtx var, body; -{ - pending_chain = gen_rtx (QUEUED, GET_MODE (var), - var, 0, 0, body, pending_chain); - return pending_chain; -} - -/* Use protect_from_queue to convert a QUEUED expression - into something that you can put immediately into an instruction. - If the queued incrementation has not happened yet, - protect_from_queue returns the variable itself. - If the incrementation has happened, protect_from_queue returns a temp - that contains a copy of the old value of the variable. - - Any time an rtx which might possibly be a QUEUED is to be put - into an instruction, it must be passed through protect_from_queue first. - QUEUED expressions are not meaningful in instructions. - - Do not pass a value through protect_from_queue and then hold - on to it for a while before putting it in an instruction! - If the queue is flushed in between, incorrect code will result. */ - -rtx -protect_from_queue (x, modify) - register rtx x; - int modify; -{ - register RTX_CODE code = GET_CODE (x); - if (code != QUEUED) - { - /* A special hack for read access to (MEM (QUEUED ...)) - to facilitate use of autoincrement. - Make a copy of the contents of the memory location - rather than a copy of the address. */ - if (code == MEM && GET_CODE (XEXP (x, 0)) == QUEUED && !modify) - { - register rtx y = XEXP (x, 0); - XEXP (x, 0) = QUEUED_VAR (y); - if (QUEUED_INSN (y)) - { - register rtx temp = gen_reg_rtx (GET_MODE (x)); - emit_insn_before (gen_move_insn (temp, x), - QUEUED_INSN (y)); - return temp; - } - return x; - } - /* Otherwise, recursively protect the subexpressions of all - the kinds of rtx's that can contain a QUEUED. */ - if (code == MEM) - XEXP (x, 0) = protect_from_queue (XEXP (x, 0), 0); - else if (code == PLUS || code == MULT) - { - XEXP (x, 0) = protect_from_queue (XEXP (x, 0), 0); - XEXP (x, 1) = protect_from_queue (XEXP (x, 1), 0); - } - return x; - } - /* If the increment has not happened, use the variable itself. */ - if (QUEUED_INSN (x) == 0) - return QUEUED_VAR (x); - /* If the increment has happened and a pre-increment copy exists, - use that copy. */ - if (QUEUED_COPY (x) != 0) - return QUEUED_COPY (x); - /* The increment has happened but we haven't set up a pre-increment copy. - Set one up now, and use it. */ - QUEUED_COPY (x) = gen_reg_rtx (GET_MODE (QUEUED_VAR (x))); - emit_insn_before (gen_move_insn (QUEUED_COPY (x), QUEUED_VAR (x)), - QUEUED_INSN (x)); - return QUEUED_COPY (x); -} - -/* Return nonzero if X contains a QUEUED expression: - if it contains anything that will be altered by a queued increment. - We handle only combinations of MEM, PLUS, MINUS and MULT operators - since memory addresses generally contain only those. */ - -static int -queued_subexp_p (x) - rtx x; -{ - register enum rtx_code code = GET_CODE (x); - switch (code) - { - case QUEUED: - return 1; - case MEM: - return queued_subexp_p (XEXP (x, 0)); - case MULT: - case PLUS: - case MINUS: - return queued_subexp_p (XEXP (x, 0)) - || queued_subexp_p (XEXP (x, 1)); - } - return 0; -} - -/* Perform all the pending incrementations. */ - -void -emit_queue () -{ - register rtx p; - while (p = pending_chain) - { - QUEUED_INSN (p) = emit_insn (QUEUED_BODY (p)); - pending_chain = QUEUED_NEXT (p); - } -} - -static void -init_queue () -{ - if (pending_chain) - abort (); -} - -/* Copy data from FROM to TO, where the machine modes are not the same. - Both modes may be integer, or both may be floating. - UNSIGNEDP should be nonzero if FROM is an unsigned type. - This causes zero-extension instead of sign-extension. */ - -void -convert_move (to, from, unsignedp) - register rtx to, from; - int unsignedp; -{ - enum machine_mode to_mode = GET_MODE (to); - enum machine_mode from_mode = GET_MODE (from); - int to_real = GET_MODE_CLASS (to_mode) == MODE_FLOAT; - int from_real = GET_MODE_CLASS (from_mode) == MODE_FLOAT; - int extending = (int) to_mode > (int) from_mode; - - to = protect_from_queue (to, 1); - from = protect_from_queue (from, 0); - - if (to_real != from_real) - abort (); - - if (to_mode == from_mode - || (from_mode == VOIDmode && CONSTANT_P (from))) - { - emit_move_insn (to, from); - return; - } - - if (to_real) - { -#ifdef HAVE_extendsfdf2 - if (HAVE_extendsfdf2 && extending) - { - emit_unop_insn (CODE_FOR_extendsfdf2, to, from, UNKNOWN); - return; - } -#endif -#ifdef HAVE_truncdfsf2 - if (HAVE_truncdfsf2 && ! extending) - { - emit_unop_insn (CODE_FOR_truncdfsf2, to, from, UNKNOWN); - return; - } -#endif - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, (extending - ? "__extendsfdf2" - : "__truncdfsf2")), 0, - GET_MODE (to), 1, - from, (extending ? SFmode : DFmode)); - emit_move_insn (to, hard_libcall_value (GET_MODE (to))); - return; - } - - /* Now both modes are integers. */ - - if (to_mode == DImode) - { - if (unsignedp) - { -#ifdef HAVE_zero_extendsidi2 - if (HAVE_zero_extendsidi2 && from_mode == SImode) - emit_unop_insn (CODE_FOR_zero_extendsidi2, to, from, ZERO_EXTEND); - else -#endif -#ifdef HAVE_zero_extendhidi2 - if (HAVE_zero_extendhidi2 && from_mode == HImode) - emit_unop_insn (CODE_FOR_zero_extendhidi2, to, from, ZERO_EXTEND); - else -#endif -#ifdef HAVE_zero_extendqidi2 - if (HAVE_zero_extendqidi2 && from_mode == QImode) - emit_unop_insn (CODE_FOR_zero_extendqidi2, to, from, ZERO_EXTEND); - else -#endif -#ifdef HAVE_zero_extendsidi2 - if (HAVE_zero_extendsidi2) - { - convert_move (gen_lowpart (SImode, to), from, unsignedp); - emit_unop_insn (CODE_FOR_zero_extendsidi2, to, - gen_lowpart (SImode, to), ZERO_EXTEND); - } - else -#endif - { - emit_insn (gen_rtx (CLOBBER, VOIDmode, to)); - convert_move (gen_lowpart (SImode, to), from, unsignedp); - emit_clr_insn (gen_highpart (SImode, to)); - } - } -#ifdef HAVE_extendsidi2 - else if (HAVE_extendsidi2 && from_mode == SImode) - emit_unop_insn (CODE_FOR_extendsidi2, to, from, SIGN_EXTEND); -#endif -#ifdef HAVE_extendhidi2 - else if (HAVE_extendhidi2 && from_mode == HImode) - emit_unop_insn (CODE_FOR_extendhidi2, to, from, SIGN_EXTEND); -#endif -#ifdef HAVE_extendqidi2 - else if (HAVE_extendqidi2 && from_mode == QImode) - emit_unop_insn (CODE_FOR_extendqidi2, to, from, SIGN_EXTEND); -#endif -#ifdef HAVE_extendsidi2 - else if (HAVE_extendsidi2) - { - convert_move (gen_lowpart (SImode, to), from, unsignedp); - emit_unop_insn (CODE_FOR_extendsidi2, to, - gen_lowpart (SImode, to), SIGN_EXTEND); - } -#endif -#ifdef HAVE_slt - else if (HAVE_slt && insn_operand_mode[(int) CODE_FOR_slt][0] == SImode) - { - rtx temp, target; - emit_insn (gen_rtx (CLOBBER, VOIDmode, to)); - convert_move (gen_lowpart (SImode, to), from, unsignedp); - emit_cmp_insn (gen_lowpart (SImode, to), const0_rtx, 0, 0, 0); - target = gen_highpart (SImode, to); - if (!(*insn_operand_predicate[(int) CODE_FOR_slt][0]) (target, SImode)) - temp = gen_reg_rtx (SImode); - else - temp = target; - emit_insn (gen_slt (temp)); - if (temp != target) - emit_move_insn (target, temp); - } -#endif - else - { - register rtx label = gen_label_rtx (); - rtx temp, target; - - emit_insn (gen_rtx (CLOBBER, VOIDmode, to)); - emit_clr_insn (gen_highpart (SImode, to)); - convert_move (gen_lowpart (SImode, to), from, unsignedp); - emit_cmp_insn (gen_lowpart (SImode, to), - gen_rtx (CONST_INT, VOIDmode, 0), - 0, 0, 0); - NO_DEFER_POP; - emit_jump_insn (gen_bge (label)); - target = gen_highpart (SImode, to); - temp = expand_unop (SImode, one_cmpl_optab, - target, gen_highpart (SImode, to), - 1); - if (temp != target) - emit_move_insn (target, temp); - emit_label (label); - OK_DEFER_POP; - } - return; - } - - if (from_mode == DImode) - { - convert_move (to, gen_lowpart (SImode, from), 0); - return; - } - - /* Now follow all the conversions between integers - no more than a word long. */ - - /* For truncation, usually we can just refer to FROM in a narrower mode. */ - if (GET_MODE_BITSIZE (to_mode) < GET_MODE_BITSIZE (from_mode) - && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (to_mode), - GET_MODE_BITSIZE (from_mode)) - && ((GET_CODE (from) == MEM - && ! MEM_VOLATILE_P (from) - && ! mode_dependent_address_p (XEXP (from, 0))) - || GET_CODE (from) == REG - || GET_CODE (from) == SUBREG)) - { - emit_move_insn (to, gen_lowpart (to_mode, from)); - return; - } - - if (to_mode == SImode && from_mode == HImode) - { - if (unsignedp) - { -#ifdef HAVE_zero_extendhisi2 - if (HAVE_zero_extendhisi2) - emit_unop_insn (CODE_FOR_zero_extendhisi2, to, from, ZERO_EXTEND); - else -#endif - abort (); - } - else - { -#ifdef HAVE_extendhisi2 - if (HAVE_extendhisi2) - emit_unop_insn (CODE_FOR_extendhisi2, to, from, SIGN_EXTEND); - else -#endif - abort (); - } - return; - } - - if (to_mode == SImode && from_mode == QImode) - { - if (unsignedp) - { -#ifdef HAVE_zero_extendqisi2 - if (HAVE_zero_extendqisi2) - { - emit_unop_insn (CODE_FOR_zero_extendqisi2, to, from, ZERO_EXTEND); - return; - } -#endif -#if defined (HAVE_zero_extendqihi2) && defined (HAVE_extendhisi2) - if (HAVE_zero_extendqihi2 && HAVE_extendhisi2) - { - register rtx temp = gen_reg_rtx (HImode); - emit_unop_insn (CODE_FOR_zero_extendqihi2, temp, from, ZERO_EXTEND); - emit_unop_insn (CODE_FOR_extendhisi2, to, temp, SIGN_EXTEND); - return; - } -#endif - } - else - { -#ifdef HAVE_extendqisi2 - if (HAVE_extendqisi2) - { - emit_unop_insn (CODE_FOR_extendqisi2, to, from, SIGN_EXTEND); - return; - } -#endif -#if defined (HAVE_extendqihi2) && defined (HAVE_extendhisi2) - if (HAVE_extendqihi2 && HAVE_extendhisi2) - { - register rtx temp = gen_reg_rtx (HImode); - emit_unop_insn (CODE_FOR_extendqihi2, temp, from, SIGN_EXTEND); - emit_unop_insn (CODE_FOR_extendhisi2, to, temp, SIGN_EXTEND); - return; - } -#endif - } - abort (); - } - - if (to_mode == HImode && from_mode == QImode) - { - if (unsignedp) - { -#ifdef HAVE_zero_extendqihi2 - if (HAVE_zero_extendqihi2) - { - emit_unop_insn (CODE_FOR_zero_extendqihi2, to, from, ZERO_EXTEND); - return; - } -#endif - } - else - { -#ifdef HAVE_extendqihi2 - if (HAVE_extendqihi2) - { - emit_unop_insn (CODE_FOR_extendqihi2, to, from, SIGN_EXTEND); - return; - } -#endif - } - abort (); - } - -#if 0 /* This seems to be redundant with code 100 lines up. */ - - /* Now we are truncating an integer to a smaller one. - If the result is a temporary, we might as well just copy it, - since only the low-order part of the result needs to be valid - and it is valid with no change. */ - - if (GET_CODE (to) == REG) - { - if (GET_CODE (from) == REG) - { - emit_move_insn (to, gen_lowpart (GET_MODE (to), from)); - return; - } - else if (GET_CODE (from) == SUBREG) - { - from = copy_rtx (from); - /* This is safe since FROM is not more than one word. */ - PUT_MODE (from, GET_MODE (to)); - emit_move_insn (to, from); - return; - } -#ifndef BYTES_BIG_ENDIAN - else if (GET_CODE (from) == MEM) - { - register rtx addr = XEXP (from, 0); - if (memory_address_p (GET_MODE (to), addr)) - { - emit_move_insn (to, gen_rtx (MEM, GET_MODE (to), addr)); - return; - } - } -#endif /* not BYTES_BIG_ENDIAN */ - } -#endif /* 0 */ - - if (from_mode == SImode && to_mode == HImode) - { -#ifdef HAVE_truncsihi2 - if (HAVE_truncsihi2) - { - emit_unop_insn (CODE_FOR_truncsihi2, to, from, UNKNOWN); - return; - } -#endif - abort (); - } - - if (from_mode == SImode && to_mode == QImode) - { -#ifdef HAVE_truncsiqi2 - if (HAVE_truncsiqi2) - { - emit_unop_insn (CODE_FOR_truncsiqi2, to, from, UNKNOWN); - return; - } -#endif - abort (); - } - - if (from_mode == HImode && to_mode == QImode) - { -#ifdef HAVE_trunchiqi2 - if (HAVE_trunchiqi2) - { - emit_unop_insn (CODE_FOR_trunchiqi2, to, from, UNKNOWN); - return; - } -#endif - abort (); - } - - /* Mode combination is not recognized. */ - abort (); -} - -/* Return an rtx for a value that would result - from converting X to mode MODE. - Both X and MODE may be floating, or both integer. - UNSIGNEDP is nonzero if X is an unsigned value. - This can be done by referring to a part of X in place - or by copying to a new temporary with conversion. */ - -rtx -convert_to_mode (mode, x, unsignedp) - enum machine_mode mode; - rtx x; - int unsignedp; -{ - register rtx temp; - if (mode == GET_MODE (x)) - return x; - if (integer_mode_p (mode) - && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (GET_MODE (x)) - && ! (GET_CODE (x) == MEM && MEM_VOLATILE_P (x))) - return gen_lowpart (mode, x); - temp = gen_reg_rtx (mode); - convert_move (temp, x, unsignedp); - return temp; -} - -int -integer_mode_p (mode) - enum machine_mode mode; -{ - return (int) mode > (int) VOIDmode && (int) mode <= (int) TImode; -} - -/* Generate several move instructions to copy LEN bytes - from block FROM to block TO. (These are MEM rtx's with BLKmode). - The caller must pass FROM and TO - through protect_from_queue before calling. - ALIGN (in bytes) is maximum alignment we can assume. */ - -struct move_by_pieces -{ - rtx to; - rtx to_addr; - int autinc_to; - int explicit_inc_to; - rtx from; - rtx from_addr; - int autinc_from; - int explicit_inc_from; - int len; - int offset; - int reverse; -}; - -static void move_by_pieces_1 (); -static int move_by_pieces_ninsns (); - -static void -move_by_pieces (to, from, len, align) - rtx to, from; - int len, align; -{ - struct move_by_pieces data; - rtx to_addr = XEXP (to, 0), from_addr = XEXP (from, 0); - - data.offset = 0; - data.to_addr = to_addr; - data.from_addr = from_addr; - data.to = to; - data.from = from; - data.autinc_to - = (GET_CODE (to_addr) == PRE_INC || GET_CODE (to_addr) == PRE_DEC - || GET_CODE (to_addr) == POST_INC || GET_CODE (to_addr) == POST_DEC); - data.autinc_from - = (GET_CODE (from_addr) == PRE_INC || GET_CODE (from_addr) == PRE_DEC - || GET_CODE (from_addr) == POST_INC - || GET_CODE (from_addr) == POST_DEC); - - data.explicit_inc_from = 0; - data.explicit_inc_to = 0; - data.reverse - = (GET_CODE (to_addr) == PRE_DEC || GET_CODE (to_addr) == POST_DEC); - if (data.reverse) data.offset = len; - data.len = len; - - /* If copying requires more than two move insns, - copy addresses to registers (to make displacements shorter) - and use post-increment if available. */ - if (!(data.autinc_from && data.autinc_to) - && move_by_pieces_ninsns (len, align) > 2) - { -#ifdef HAVE_PRE_DECREMENT - if (data.reverse && ! data.autinc_from) - { - data.from_addr = copy_addr_to_reg (plus_constant (from_addr, len)); - data.autinc_from = 1; - data.explicit_inc_from = -1; - } -#endif -#ifdef HAVE_POST_INCREMENT - if (! data.autinc_from) - { - data.from_addr = copy_addr_to_reg (from_addr); - data.autinc_from = 1; - data.explicit_inc_from = 1; - } -#endif - if (!data.autinc_from && CONSTANT_P (from_addr)) - data.from_addr = copy_addr_to_reg (from_addr); -#ifdef HAVE_PRE_DECREMENT - if (data.reverse && ! data.autinc_to) - { - data.to_addr = copy_addr_to_reg (plus_constant (to_addr, len)); - data.autinc_to = 1; - data.explicit_inc_to = -1; - } -#endif -#ifdef HAVE_POST_INCREMENT - if (! data.reverse && ! data.autinc_to) - { - data.to_addr = copy_addr_to_reg (to_addr); - data.autinc_to = 1; - data.explicit_inc_to = 1; - } -#endif - if (!data.autinc_to && CONSTANT_P (to_addr)) - data.to_addr = copy_addr_to_reg (to_addr); - } - -#ifdef STRICT_ALIGNMENT - if (align > MOVE_MAX || align >= BIGGEST_ALIGNMENT / BITS_PER_UNIT) - align = MOVE_MAX; -#else - align = MOVE_MAX; -#endif - -#ifdef HAVE_movti - if (HAVE_movti && align >= GET_MODE_SIZE (TImode)) - move_by_pieces_1 (gen_movti, TImode, &data); -#endif -#ifdef HAVE_movdi - if (HAVE_movdi && align >= GET_MODE_SIZE (DImode)) - move_by_pieces_1 (gen_movdi, DImode, &data); -#endif -#ifdef HAVE_movsi - if (align >= GET_MODE_SIZE (SImode)) - move_by_pieces_1 (gen_movsi, SImode, &data); -#endif -#ifdef HAVE_movhi - if (HAVE_movhi && align >= GET_MODE_SIZE (HImode)) - move_by_pieces_1 (gen_movhi, HImode, &data); -#endif -#ifdef HAVE_movqi - move_by_pieces_1 (gen_movqi, QImode, &data); -#else - movqi instruction required in machine description -#endif -} - -/* Return number of insns required to move L bytes by pieces. - ALIGN (in bytes) is maximum alignment we can assume. */ - -static int -move_by_pieces_ninsns (l, align) - unsigned int l; - int align; -{ - register int n_insns = 0; - -#ifdef STRICT_ALIGNMENT - if (align > MOVE_MAX || align >= BIGGEST_ALIGNMENT / BITS_PER_UNIT) - align = MOVE_MAX; -#else - align = MOVE_MAX; -#endif - -#ifdef HAVE_movti - if (HAVE_movti && align >= GET_MODE_SIZE (TImode)) - n_insns += l / GET_MODE_SIZE (TImode), l %= GET_MODE_SIZE (TImode); -#endif -#ifdef HAVE_movdi - if (HAVE_movdi && align >= GET_MODE_SIZE (DImode)) - n_insns += l / GET_MODE_SIZE (DImode), l %= GET_MODE_SIZE (DImode); -#endif -#ifdef HAVE_movsi - if (HAVE_movsi && align >= GET_MODE_SIZE (SImode)) - n_insns += l / GET_MODE_SIZE (SImode), l %= GET_MODE_SIZE (SImode); -#endif -#ifdef HAVE_movhi - if (HAVE_movhi && align >= GET_MODE_SIZE (HImode)) - n_insns += l / GET_MODE_SIZE (HImode), l %= GET_MODE_SIZE (HImode); -#endif - n_insns += l; - - return n_insns; -} - -/* Subroutine of move_by_pieces. Move as many bytes as appropriate - with move instructions for mode MODE. GENFUN is the gen_... function - to make a move insn for that mode. DATA has all the other info. */ - -static void -move_by_pieces_1 (genfun, mode, data) - rtx (*genfun) (); - enum machine_mode mode; - struct move_by_pieces *data; -{ - register int size = GET_MODE_SIZE (mode); - register rtx to1, from1; - - while (data->len >= size) - { - if (data->reverse) data->offset -= size; - - to1 = (data->autinc_to - ? gen_rtx (MEM, mode, data->to_addr) - : change_address (data->to, mode, - plus_constant (data->to_addr, data->offset))); - from1 = - (data->autinc_from - ? gen_rtx (MEM, mode, data->from_addr) - : change_address (data->from, mode, - plus_constant (data->from_addr, data->offset))); - -#ifdef HAVE_PRE_DECREMENT - if (data->explicit_inc_to < 0) - emit_insn (gen_sub2_insn (data->to_addr, - gen_rtx (CONST_INT, VOIDmode, size))); - if (data->explicit_inc_from < 0) - emit_insn (gen_sub2_insn (data->from_addr, - gen_rtx (CONST_INT, VOIDmode, size))); -#endif - - emit_insn ((*genfun) (to1, from1)); -#ifdef HAVE_POST_INCREMENT - if (data->explicit_inc_to > 0) - emit_insn (gen_add2_insn (data->to_addr, - gen_rtx (CONST_INT, VOIDmode, size))); - if (data->explicit_inc_from > 0) - emit_insn (gen_add2_insn (data->from_addr, - gen_rtx (CONST_INT, VOIDmode, size))); -#endif - - if (! data->reverse) data->offset += size; - - data->len -= size; - } -} - -/* Emit code to move a block Y to a block X. - This may be done with string-move instructions, - with multiple scalar move instructions, or with a library call. - - Both X and Y must be MEM rtx's (perhaps inside VOLATILE) - with mode BLKmode. - SIZE is an rtx that says how long they are. - ALIGN is the maximum alignment we can assume they have, - measured in bytes. */ - -static void -emit_block_move (x, y, size, align) - rtx x, y; - rtx size; - int align; -{ - if (GET_MODE (x) != BLKmode) - abort (); - - if (GET_MODE (y) != BLKmode) - abort (); - - x = protect_from_queue (x, 1); - y = protect_from_queue (y, 0); - - if (GET_CODE (x) != MEM) - abort (); - if (GET_CODE (y) != MEM) - abort (); - if (size == 0) - abort (); - - if (GET_CODE (size) == CONST_INT - && (move_by_pieces_ninsns ((unsigned) INTVAL (size), align) - < MOVE_RATIO)) - move_by_pieces (x, y, INTVAL (size), align); - else - { - /* Try the most limited insn first, because there's no point - including more than one in the machine description unless - the more limited one has some advantage. */ -#ifdef HAVE_movstrqi - if (HAVE_movstrqi - && GET_CODE (size) == CONST_INT - && ((unsigned) INTVAL (size) - < (1 << (GET_MODE_BITSIZE (QImode) - 1)))) - { - emit_insn (gen_movstrqi (x, y, size, - gen_rtx (CONST_INT, VOIDmode, align))); - return; - } -#endif -#ifdef HAVE_movstrhi - if (HAVE_movstrhi - && GET_CODE (size) == CONST_INT - && ((unsigned) INTVAL (size) - < (1 << (GET_MODE_BITSIZE (HImode) - 1)))) - { - emit_insn (gen_movstrhi (x, y, size, - gen_rtx (CONST_INT, VOIDmode, align))); - return; - } -#endif -#ifdef HAVE_movstrsi - if (HAVE_movstrsi) - { - emit_insn (gen_movstrsi (x, y, size, - gen_rtx (CONST_INT, VOIDmode, align))); - return; - } -#endif - -#ifdef TARGET_MEM_FUNCTIONS - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0, - VOIDmode, 3, XEXP (x, 0), Pmode, - XEXP (y, 0), Pmode, - size, Pmode); -#else - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "bcopy"), 0, - VOIDmode, 3, XEXP (y, 0), Pmode, - XEXP (x, 0), Pmode, - size, Pmode); -#endif - } -} - -/* Copy all or part of a BLKmode value X into registers starting at REGNO. - The number of registers to be filled is NREGS. */ - -static void -move_block_to_reg (regno, x, nregs) - int regno; - rtx x; - int nregs; -{ - int i; - if (GET_CODE (x) == CONST_DOUBLE && x != dconst0_rtx) - x = force_const_double_mem (x); - for (i = 0; i < nregs; i++) - { - if (GET_CODE (x) == REG) - emit_move_insn (gen_rtx (REG, SImode, regno + i), - gen_rtx (SUBREG, SImode, x, i)); - else if (x == dconst0_rtx || x == const0_rtx) - emit_move_insn (gen_rtx (REG, SImode, regno + i), - const0_rtx); - else - emit_move_insn (gen_rtx (REG, SImode, regno + i), - gen_rtx (MEM, SImode, - memory_address (SImode, - plus_constant (XEXP (x, 0), - i * GET_MODE_SIZE (SImode))))); - } -} - -/* Copy all or part of a BLKmode value X out of registers starting at REGNO. - The number of registers to be filled is NREGS. */ - -void -move_block_from_reg (regno, x, nregs) - int regno; - rtx x; - int nregs; -{ - int i; - for (i = 0; i < nregs; i++) - { - if (GET_CODE (x) == REG) - emit_move_insn (gen_rtx (SUBREG, SImode, x, i), - gen_rtx (REG, SImode, regno + i)); - else - emit_move_insn (gen_rtx (MEM, SImode, - memory_address (SImode, - plus_constant (XEXP (x, 0), - i * GET_MODE_SIZE (SImode)))), - gen_rtx (REG, SImode, regno + i)); - } -} - -/* Mark NREGS consecutive regs, starting at REGNO, as being live now. */ - -static void -use_regs (regno, nregs) - int regno; - int nregs; -{ - int i; - for (i = 0; i < nregs; i++) - emit_insn (gen_rtx (USE, VOIDmode, gen_rtx (REG, SImode, regno + i))); -} - -/* Write zeros through the storage of OBJECT. - If OBJECT has BLKmode, SIZE is its length in bytes. */ - -void -clear_storage (object, size) - rtx object; - int size; -{ - if (GET_MODE (object) == BLKmode) - { -#ifdef TARGET_MEM_FUNCTIONS - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memset"), 0, - VOIDmode, 3, - XEXP (object, 0), Pmode, const0_rtx, Pmode, - gen_rtx (CONST_INT, VOIDmode, size), Pmode); -#else - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "bzero"), 0, - VOIDmode, 2, - XEXP (object, 0), Pmode, - gen_rtx (CONST_INT, VOIDmode, size), Pmode); -#endif - } - else - emit_move_insn (object, const0_rtx); -} - -/* Generate code to copy Y into X. - Both Y and X must have the same mode, except that - Y can be a constant with VOIDmode. - This mode cannot be BLKmode; use emit_block_move for that. - - Return the last instruction emitted. */ - -rtx -emit_move_insn (x, y) - rtx x, y; -{ - enum machine_mode mode = GET_MODE (x); - x = protect_from_queue (x, 1); - y = protect_from_queue (y, 0); - - if (mode == BLKmode) - abort (); - if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) - { - int icode = (int) mov_optab->handlers[(int) mode].insn_code; - if (! (*insn_operand_predicate[icode][1]) (y, mode) - && (CONSTANT_P (y) || GET_CODE (y) == CONST_DOUBLE)) - { - y = force_const_mem (mode, y); - if (! memory_address_p (mode, XEXP (y, 0))) - y = gen_rtx (MEM, mode, memory_address (mode, XEXP (y, 0))); - } - return emit_insn (GEN_FCN (icode) (x, y)); - } -#if 0 - /* It turns out you get much better optimization (in cse and flow) - if you define movdi and movdf instruction patterns - even if they must turn into multiple assembler instructions. */ - else if (GET_MODE_SIZE (mode) >= GET_MODE_SIZE (SImode)) - { - register int count = GET_MODE_SIZE (mode) / GET_MODE_SIZE (SImode); - register int i; - if (GET_CODE (y) == CONST_DOUBLE && y != dconst0_rtx) - y = force_const_double_mem (y); - for (i = 0; i < count; i++) - { - rtx x1, y1; - if (GET_CODE (x) == REG) - x1 = gen_rtx (SUBREG, SImode, x, i); - else - x1 = gen_rtx (MEM, SImode, - memory_address (SImode, - plus_constant (XEXP (x, 0), - i * GET_MODE_SIZE (SImode)))); - if (GET_CODE (y) == REG) - y1 = gen_rtx (SUBREG, SImode, y, i); - else if (y == dconst0_rtx) - y1 = const0_rtx; - else - y1 = gen_rtx (MEM, SImode, - memory_address (SImode, - plus_constant (XEXP (y, 0), - i * GET_MODE_SIZE (SImode)))); - emit_insn (gen_movsi (protect_from_queue (x1, 1), protect_from_queue (y1, 0))); - } - } -#endif - else - abort (); -} - -/* Pushing data onto the stack. */ - -/* Push a block of length SIZE (perhaps variable) - and return an rtx to address the beginning of the block. - Note that it is not possible for the value returned to be a QUEUED. - The value may be stack_pointer_rtx. - - EXTRA is the number of bytes of padding to push in addition to the block. - The padding is pushed "after" the specified size. - - The value we return does take account of STACK_POINTER_OFFSET. */ - -rtx -push_block (size, extra) - rtx size; - int extra; -{ - register rtx temp; - if (CONSTANT_P (size)) - anti_adjust_stack (plus_constant (size, extra)); - else if (GET_CODE (size) == REG && extra == 0) - anti_adjust_stack (size); - else - { - rtx temp = copy_to_mode_reg (Pmode, size); - if (extra != 0) - temp = expand_binop (Pmode, add_optab, - temp, gen_rtx (CONST_INT, VOIDmode, extra), - temp, 0, OPTAB_LIB_WIDEN); - anti_adjust_stack (temp); - } - -#ifdef STACK_GROWS_DOWNWARD - temp = stack_pointer_rtx; - if (extra != 0) - temp = plus_constant (temp, extra); -#else - temp = gen_rtx (PLUS, Pmode, - stack_pointer_rtx, - negate_rtx (Pmode, size)); - if (GET_CODE (size) != CONST_INT) - temp = force_operand (temp, 0); - if (extra != 0) - temp = plus_constant (temp, -extra); -#endif - -#ifdef STACK_POINTER_OFFSET - temp = plus_constant (temp, STACK_POINTER_OFFSET); -#endif /* STACK_POINTER_OFFSET */ - - return memory_address (QImode, temp); -} - -static rtx -gen_push_operand () -{ - return gen_rtx ( -#ifdef STACK_GROWS_DOWNWARD - PRE_DEC, -#else - PRE_INC, -#endif - Pmode, - stack_pointer_rtx); -} - -/* Generate code to push X onto the stack, assuming it has mode MODE. - MODE is redundant except when X is a CONST_INT (since they don't - carry mode info). - SIZE is an rtx for the size of data to be copied (in bytes), - needed only if X is BLKmode. - - ALIGN (in bytes) is maximum alignment we can assume. - - If PARTIAL is nonzero, then copy that many of the first words - of X into registers starting with REG, and push the rest of X. - The amount of space pushed is decreased by PARTIAL words, - rounded *down* to a multiple of PARM_BOUNDARY. - REG must be a hard register in this case. - - EXTRA is the amount in bytes of extra space to leave next to this arg. - Within the function, we set EXTRA to zero once the padding is done, - to avoid padding twice. - - On a machine that lacks real push insns, ARGS_ADDR is the address of - the bottom of the argument block for this call. We use indexing off there - to store the arg. On machines with push insns, ARGS_ADDR is 0. - - ARGS_SO_FAR is the size of args previously pushed for this call. */ - -static void -emit_push_insn (x, mode, size, align, partial, reg, extra, args_addr, args_so_far) - register rtx x; - enum machine_mode mode; - rtx size; - int align; - int partial; - rtx reg; - int extra; - rtx args_addr; - rtx args_so_far; -{ - rtx xinner; - enum direction stack_direction -#ifdef STACK_GROWS_DOWNWARD - = downward; -#else - = upward; -#endif - - /* Decide where to pad the argument: `downward' for below, - `upward' for above, or `none' for don't pad it. - Default is below for small data on big-endian machines; else above. */ - enum direction where_pad = FUNCTION_ARG_PADDING (mode, size); - - xinner = x = protect_from_queue (x, 0); - - if (extra) - { - if (args_addr == 0) - { - /* Push padding now if padding above and stack grows down, - or if padding below and stack grows up. */ - if (where_pad != none && where_pad != stack_direction) - { - anti_adjust_stack (gen_rtx (CONST_INT, VOIDmode, extra)); - extra = 0; - } - } - else - { - /* If space already allocated, just adjust the address we use. */ - if (where_pad == downward) - { - args_so_far = plus_constant (args_so_far, extra); - } - /* If padding comes after a space already allocated, - there is nothing to do. */ - extra = 0; - } - } - - if (mode == BLKmode) - { - /* Copy a block into the stack, entirely or partially. */ - - register rtx temp; - int used = partial * UNITS_PER_WORD; - int offset = used % (PARM_BOUNDARY / BITS_PER_UNIT); - int skip; - - if (size == 0) - abort (); - - used -= offset; - - /* USED is now the # of bytes we need not copy to the stack - because registers will take care of them. */ - - if (partial != 0) - xinner = change_address (xinner, BLKmode, - plus_constant (XEXP (xinner, 0), used)); - -/* If the partial register-part of the arg counts in its stack size, - skip the part of stack space corresponding to the registers. - Otherwise, start copying to the beginning of the stack space, - by setting SKIP to 0. */ -#ifndef FIRST_PARM_CALLER_OFFSET - skip = 0; -#else - skip = used; -#endif - -#ifdef PUSH_ROUNDING - /* Do it with several push insns if that doesn't take lots of insns - and if there is no difficulty with push insns that skip bytes - on the stack for alignment purposes. */ - if (args_addr == 0 - && GET_CODE (size) == CONST_INT - && args_addr == 0 - && skip == 0 - && (move_by_pieces_ninsns ((unsigned) INTVAL (size) - used, align) - < MOVE_RATIO) - && PUSH_ROUNDING (INTVAL (size)) == INTVAL (size)) - move_by_pieces (gen_rtx (MEM, BLKmode, gen_push_operand ()), xinner, - INTVAL (size) - used, align); - else -#endif /* PUSH_ROUNDING */ - { - /* Otherwise make space on the stack and copy the data - to the address of that space. */ - - /* Deduct words put into registers from the size we must copy. */ - if (partial != 0) - { - if (GET_CODE (size) == CONST_INT) - size = gen_rtx (CONST_INT, VOIDmode, INTVAL (size) - used); - else - size = expand_binop (GET_MODE (size), sub_optab, size, - gen_rtx (CONST_INT, VOIDmode, used), - 0, 0, OPTAB_LIB_WIDEN); - } - - /* Get the address of the stack space. */ - if (! args_addr) - { - temp = push_block (size, extra); - extra = 0; - } - else if (GET_CODE (args_so_far) == CONST_INT) - temp = memory_address (BLKmode, - plus_constant (args_addr, - skip + INTVAL (args_so_far))); - else - temp = memory_address (BLKmode, - plus_constant (gen_rtx (PLUS, Pmode, - args_addr, args_so_far), - skip)); - - /* TEMP is the address of the block. Copy the data there. */ - if (GET_CODE (size) == CONST_INT - && (move_by_pieces_ninsns ((unsigned) INTVAL (size), align) - < MOVE_RATIO)) - { - move_by_pieces (gen_rtx (MEM, BLKmode, temp), xinner, - INTVAL (size), align); - goto ret; - } - /* Try the most limited insn first, because there's no point - including more than one in the machine description unless - the more limited one has some advantage. */ -#ifdef HAVE_movstrqi - if (HAVE_movstrqi - && GET_CODE (size) == CONST_INT - && ((unsigned) INTVAL (size) - < (1 << (GET_MODE_BITSIZE (QImode) - 1)))) - { - emit_insn (gen_movstrqi (gen_rtx (MEM, BLKmode, temp), - xinner, size, - gen_rtx (CONST_INT, VOIDmode, align))); - goto ret; - } -#endif -#ifdef HAVE_movstrhi - if (HAVE_movstrhi - && GET_CODE (size) == CONST_INT - && ((unsigned) INTVAL (size) - < (1 << (GET_MODE_BITSIZE (HImode) - 1)))) - { - emit_insn (gen_movstrhi (gen_rtx (MEM, BLKmode, temp), - xinner, size, - gen_rtx (CONST_INT, VOIDmode, align))); - goto ret; - } -#endif -#ifdef HAVE_movstrsi - if (HAVE_movstrsi) - { - emit_insn (gen_movstrsi (gen_rtx (MEM, BLKmode, temp), - xinner, size, - gen_rtx (CONST_INT, VOIDmode, align))); - goto ret; - } -#endif - - if (reg_mentioned_p (stack_pointer_rtx, temp)) - { - /* Now that emit_library_call does force_operand - before pushing anything, preadjustment does not work. */ - temp = copy_to_reg (temp); -#if 0 - /* Correct TEMP so it holds what will be a description of - the address to copy to, valid after one arg is pushed. */ - int xsize = GET_MODE_SIZE (Pmode); -#ifdef PUSH_ROUNDING - xsize = PUSH_ROUNDING (xsize); -#endif - xsize = ((xsize + PARM_BOUNDARY / BITS_PER_UNIT - 1) - / (PARM_BOUNDARY / BITS_PER_UNIT) - * (PARM_BOUNDARY / BITS_PER_UNIT)); -#ifdef TARGET_MEM_FUNCTIONS - /* If we are calling bcopy, we push one arg before TEMP. - If calling memcpy, we push two. */ - xsize *= 2; -#endif -#ifdef STACK_GROWS_DOWNWARD - temp = plus_constant (temp, xsize); -#else - temp = plus_constant (temp, -xsize); -#endif /* not STACK_GROWS_DOWNWARD */ -#endif /* 0 */ - } - - /* Make inhibit_defer_pop nonzero around the library call - to force it to pop the bcopy-arguments right away. */ - NO_DEFER_POP; -#ifdef TARGET_MEM_FUNCTIONS - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0, - VOIDmode, 3, temp, Pmode, XEXP (xinner, 0), Pmode, - size, Pmode); -#else - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "bcopy"), 0, - VOIDmode, 3, XEXP (xinner, 0), Pmode, temp, Pmode, - size, Pmode); -#endif - OK_DEFER_POP; - } - } - else if (partial > 0) - { - /* Scalar partly in registers. */ - - int size = GET_MODE_SIZE (mode) / UNITS_PER_WORD; - int i; - int not_stack; - /* # words of start of argument - that we must make space for but need not store. */ - int offset = partial % (PARM_BOUNDARY / BITS_PER_WORD); - int args_offset = INTVAL (args_so_far); - int skip; - - /* If we make space by pushing it, we might as well push - the real data. Otherwise, we can leave OFFSET nonzero - and leave the space uninitialized. */ - if (args_addr == 0) - offset = 0; - - /* Now NOT_STACK gets the number of words that we don't need to - allocate on the stack. */ - not_stack = partial - offset; - -/* If the partial register-part of the arg counts in its stack size, - skip the part of stack space corresponding to the registers. - Otherwise, start copying to the beginning of the stack space, - by setting SKIP to 0. */ -#ifndef FIRST_PARM_CALLER_OFFSET - skip = 0; -#else - skip = not_stack; -#endif - - if (GET_CODE (x) == CONST_DOUBLE && x != dconst0_rtx) - x = force_const_double_mem (x); - - /* Loop over all the words allocated on the stack for this arg. */ - /* We can do it by words, because any scalar bigger than a word - has a size a multiple of a word. */ -#ifndef PUSH_ARGS_REVERSED - for (i = not_stack; i < size; i++) -#else - for (i = size - 1; i >= not_stack; i--) -#endif - if (i >= not_stack + offset) - { - rtx wd; - rtx addr; - /* Get the next word of the value in WD. */ - if (GET_CODE (x) == MEM) - { - rtx addr = memory_address (SImode, - plus_constant (XEXP (x, 0), - i * UNITS_PER_WORD)); - /* Copy to a reg, since machine may lack - memory-to-memory move insns. */ - wd = copy_to_reg (gen_rtx (MEM, SImode, addr)); - } - else if (GET_CODE (x) == REG) - wd = gen_rtx (SUBREG, SImode, x, i); - else if (x == dconst0_rtx || x == const0_rtx) - wd = const0_rtx; - else - abort (); - - emit_push_insn (wd, - SImode, 0, align, 0, 0, 0, args_addr, - gen_rtx (CONST_INT, VOIDmode, - args_offset + (i - not_stack + skip) * UNITS_PER_WORD)); - } - } - else - { - rtx addr; -#ifdef PUSH_ROUNDING - if (args_addr == 0) - addr = gen_push_operand (); - else -#endif - if (GET_CODE (args_so_far) == CONST_INT) - addr - = memory_address (mode, - plus_constant (args_addr, INTVAL (args_so_far))); - else - addr = memory_address (mode, gen_rtx (PLUS, Pmode, args_addr, - args_so_far)); - - emit_move_insn (gen_rtx (MEM, mode, addr), x); - } - - ret: - /* If part should go in registers, copy that part - into the appropriate registers. Do this now, at the end, - since mem-to-mem copies above may do function calls. */ - if (partial > 0) - move_block_to_reg (REGNO (reg), x, partial); - - if (extra) - anti_adjust_stack (gen_rtx (CONST_INT, VOIDmode, extra)); -} - -/* Output a library call to function FUN (a SYMBOL_REF rtx) - (emitting the queue unless NO_QUEUE is nonzero), - for a value of mode OUTMODE, - with NARGS different arguments, passed as alternating rtx values - and machine_modes to convert them to. - The rtx values should have been passed through protect_from_queue already. */ - -void -emit_library_call (va_alist) - va_dcl -{ - register va_list p; - register int args_size = 0; - register int argnum; - enum machine_mode outmode; - int nargs; - rtx fun; - rtx orgfun; - int inc; - int count; - rtx *regvec; - rtx argblock = 0; - CUMULATIVE_ARGS args_so_far; - struct arg { rtx value; enum machine_mode mode; rtx reg; int partial; }; - struct arg *argvec; - int old_inhibit_defer_pop = inhibit_defer_pop; - int stack_padding = 0; - int no_queue = 0; - rtx use_insns; - - va_start (p); - orgfun = fun = va_arg (p, rtx); - no_queue = va_arg (p, int); - outmode = va_arg (p, enum machine_mode); - nargs = va_arg (p, int); - - regvec = (rtx *) alloca (nargs * sizeof (rtx)); - - /* Copy all the libcall-arguments out of the varargs data - and into a vector ARGVEC. */ - argvec = (struct arg *) alloca (nargs * sizeof (struct arg)); - - INIT_CUMULATIVE_ARGS (args_so_far, (tree)0); - for (count = 0; count < nargs; count++) - { - rtx val = va_arg (p, rtx); - enum machine_mode mode = va_arg (p, enum machine_mode); - int arg_size; - - argvec[count].value = val; - - /* Convert the arg value to the mode the library wants. - Also make sure it is a reasonable operand - for a move or push insn. */ - /* ??? It is wrong to do it here; must do it earlier - where we know the signedness of the arg. */ - if (GET_MODE (val) != mode && GET_MODE (val) != VOIDmode) - { - val = gen_reg_rtx (mode); - convert_move (val, argvec[count].value, 0); - } - else if (GET_CODE (val) != REG && GET_CODE (val) != MEM - - && ! ((CONSTANT_P (val) || GET_CODE (val) == CONST_DOUBLE) - && LEGITIMATE_CONSTANT_P (val))) - val = force_operand (val, 0); - - argvec[count].value = val; - argvec[count].mode = mode; - - regvec[count] = FUNCTION_ARG (args_so_far, mode, (tree)0, 1); - -#ifdef FUNCTION_ARG_PARTIAL_NREGS - argvec[count].partial - = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, (tree)0, 1); -#else - argvec[count].partial = 0; -#endif - - FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree)0, 1); - } - va_end (p); - - /* If we have no actual push instructions, make space for all the args - right now. */ -#ifndef PUSH_ROUNDING - for (count = 0; count < nargs; count++) - { - register enum machine_mode mode = argvec[count].mode; - register rtx reg = regvec[count]; - register int partial = argvec[count].partial; - - if (reg == 0 || partial != 0) - args_size += GET_MODE_SIZE (mode); - if (partial != 0) - args_size -= partial * GET_MODE_SIZE (SImode); - } - - if (args_size != 0) - { -#ifdef STACK_ARGS_ADJUST - struct args_size size; - size.constant = args_size; - size.var = 0; - STACK_ARGS_ADJUST (size); - args_size = size.constant; -#endif - argblock - = push_block (round_push (gen_rtx (CONST_INT, VOIDmode, args_size)), 0); - } -#endif /* no PUSH_ROUNDING */ - -#ifdef PUSH_ARGS_REVERSED - inc = -1; - argnum = nargs - 1; -#else - inc = 1; - argnum = 0; -#endif - args_size = stack_padding; - - for (count = 0; count < nargs; count++, argnum += inc) - { - register enum machine_mode mode = argvec[argnum].mode; - register rtx val = argvec[argnum].value; - rtx reg = regvec[argnum]; - int partial = argvec[argnum].partial; - int arg_size; - - if (reg != 0 && partial == 0) - emit_move_insn (reg, val); - else - emit_push_insn (val, mode, 0, 0, partial, reg, 0, argblock, - gen_rtx (CONST_INT, VOIDmode, args_size)); - - /* Compute size of stack space used by this argument. */ - if (reg == 0 || partial != 0) - arg_size = GET_MODE_SIZE (mode); - else - arg_size = 0; - if (partial != 0) - arg_size - -= ((partial * UNITS_PER_WORD) - / (PARM_BOUNDARY / BITS_PER_UNIT) - * (PARM_BOUNDARY / BITS_PER_UNIT)); - - args_size += arg_size; - - NO_DEFER_POP; - } - - /* For version 1.37, try deleting this entirely. */ - if (! no_queue) - emit_queue (); - - fun = prepare_call_address (fun, 0); - - /* Any regs containing parms remain in use through the call. */ - start_sequence (); - for (count = 0; count < nargs; count++) - if (regvec[count] != 0) - emit_insn (gen_rtx (USE, VOIDmode, regvec[count])); - - use_insns = gen_sequence (); - end_sequence (); - -#ifdef STACK_BOUNDARY - args_size = (args_size + STACK_BYTES - 1) / STACK_BYTES * STACK_BYTES; -#endif - - /* Don't allow popping to be deferred, since then - cse'ing of library calls could delete a call and leave the pop. */ - NO_DEFER_POP; - emit_call_1 (fun, get_identifier (XSTR (orgfun, 0)), args_size, - FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1), - outmode != VOIDmode ? hard_libcall_value (outmode) : 0, - old_inhibit_defer_pop + 1, use_insns); - OK_DEFER_POP; -} - -/* Expand an assignment that stores the value of FROM into TO. - If WANT_VALUE is nonzero, return an rtx for the value of TO. - (This may contain a QUEUED rtx.) - Otherwise, the returned value is not meaningful. - - SUGGEST_REG is no longer actually used. - It used to mean, copy the value through a register - and return that register, if that is possible. - But now we do this if WANT_VALUE. - - If the value stored is a constant, we return the constant. */ - -rtx -expand_assignment (to, from, want_value, suggest_reg) - tree to, from; - int want_value; - int suggest_reg; -{ - register rtx to_rtx = 0; - - /* Don't crash if the lhs of the assignment was erroneous. */ - - if (TREE_CODE (to) == ERROR_MARK) - return expand_expr (from, 0, VOIDmode, 0); - - /* Assignment of a structure component needs special treatment - if the structure component's rtx is not simply a MEM. - Assignment of an array element at a constant index - has the same problem. */ - - if (TREE_CODE (to) == COMPONENT_REF - || (TREE_CODE (to) == ARRAY_REF - && TREE_CODE (TREE_OPERAND (to, 1)) == INTEGER_CST - && TREE_CODE (TYPE_SIZE (TREE_TYPE (to))) == INTEGER_CST)) - { - register enum machine_mode mode1; - int bitsize; - int volstruct = 0; - tree tem = to; - int bitpos = 0; - int unsignedp; - - if (TREE_CODE (to) == COMPONENT_REF) - { - tree field = TREE_OPERAND (to, 1); - bitsize = TREE_INT_CST_LOW (DECL_SIZE (field)) * DECL_SIZE_UNIT (field); - mode1 = DECL_MODE (TREE_OPERAND (to, 1)); - unsignedp = TREE_UNSIGNED (field); - } - else - { - mode1 = TYPE_MODE (TREE_TYPE (to)); - bitsize = GET_MODE_BITSIZE (mode1); - unsignedp = TREE_UNSIGNED (TREE_TYPE (to)); - } - - /* Compute cumulative bit-offset for nested component-refs - and array-refs, and find the ultimate containing object. */ - - while (1) - { - if (TREE_CODE (tem) == COMPONENT_REF) - { - bitpos += DECL_OFFSET (TREE_OPERAND (tem, 1)); - if (TREE_THIS_VOLATILE (tem)) - volstruct = 1; - } - else if (TREE_CODE (tem) == ARRAY_REF - && TREE_CODE (TREE_OPERAND (tem, 1)) == INTEGER_CST - && TREE_CODE (TYPE_SIZE (TREE_TYPE (tem))) == INTEGER_CST) - { - bitpos += (TREE_INT_CST_LOW (TREE_OPERAND (tem, 1)) - * TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (tem))) - * TYPE_SIZE_UNIT (TREE_TYPE (tem))); - if (TREE_THIS_VOLATILE (tem)) - volstruct = 1; - } - else - break; - tem = TREE_OPERAND (tem, 0); - } - /* TEM is now the containing data object. */ - - /* If we are going to use store_bit_field and extract_bit_field, - make sure to_rtx will be safe for multiple use. */ - if (mode1 == BImode && want_value) - tem = stabilize_reference (tem); - - to_rtx = expand_expr (tem, 0, VOIDmode, 0); - if (volstruct) - { - if (GET_CODE (to_rtx) == MEM) - MEM_VOLATILE_P (to_rtx) = 1; - else - abort (); - } - - return store_field (to_rtx, bitsize, bitpos, mode1, from, - (want_value - /* Spurious cast makes HPUX compiler happy. */ - ? (enum machine_mode) TYPE_MODE (TREE_TYPE (to)) - : VOIDmode), - unsignedp, - /* Required alignment of containing datum. */ - TYPE_ALIGN (TREE_TYPE (tem)) / BITS_PER_UNIT, - int_size_in_bytes (TREE_TYPE (tem))); - } - - /* Ordinary treatment. Expand TO to get a REG or MEM rtx. - Don't re-expand if it was expanded already (in COMPONENT_REF case). */ - - if (to_rtx == 0) - to_rtx = expand_expr (to, 0, VOIDmode, 0); - - /* Compute FROM and store the value in the rtx we got. */ - - return store_expr (from, to_rtx, want_value); -} - -/* Generate code for computing expression EXP, - and storing the value into TARGET. - Returns TARGET or an equivalent value. - TARGET may contain a QUEUED rtx. - - If SUGGEST_REG is nonzero, copy the value through a register - and return that register, if that is possible. - - If the value stored is a constant, we return the constant. */ - -rtx -store_expr (exp, target, suggest_reg) - register tree exp; - register rtx target; - int suggest_reg; -{ - register rtx temp; - int dont_return_target = 0; - - /* Copying a non-constant CONSTRUCTOR needs special treatment. */ - - if (TREE_CODE (exp) == CONSTRUCTOR && ! TREE_LITERAL (exp)) - { - store_constructor (exp, target); - return target; - } - - if (suggest_reg && GET_CODE (target) == MEM && GET_MODE (target) != BLKmode) - /* If target is in memory and caller wants value in a register instead, - arrange that. Pass TARGET as target for expand_expr so that, - if EXP is another assignment, SUGGEST_REG will be nonzero for it. - We know expand_expr will not use the target in that case. */ - { - temp = expand_expr (exp, cse_not_expected ? 0 : target, - GET_MODE (target), 0); - if (GET_MODE (temp) != BLKmode && GET_MODE (temp) != VOIDmode) - temp = copy_to_reg (temp); - dont_return_target = 1; - } - else if (queued_subexp_p (target)) - /* If target contains a postincrement, it is not safe - to use as the returned value. It would access the wrong - place by the time the queued increment gets output. - So copy the value through a temporary and use that temp - as the result. */ - { - temp = expand_expr (exp, 0, GET_MODE (target), 0); - if (GET_MODE (temp) != BLKmode && GET_MODE (temp) != VOIDmode) - temp = copy_to_reg (temp); - dont_return_target = 1; - } - else - { - temp = expand_expr (exp, target, GET_MODE (target), 0); - /* DO return TARGET if it's a specified hardware register. - expand_return relies on this. */ - if (!(target && GET_CODE (target) == REG - && REGNO (target) < FIRST_PSEUDO_REGISTER) - && (CONSTANT_P (temp) || GET_CODE (temp) == CONST_DOUBLE)) - dont_return_target = 1; - } - - /* If value was not generated in the target, store it there. - Convert the value to TARGET's type first if nec. */ - - if (temp != target && TREE_CODE (exp) != ERROR_MARK) - { - target = protect_from_queue (target, 1); - if (GET_MODE (temp) != GET_MODE (target) - && GET_MODE (temp) != VOIDmode) - { - int unsignedp = TREE_UNSIGNED (TREE_TYPE (exp)); - if (dont_return_target) - { - /* In this case, we will return TEMP, - so make sure it has the proper mode. - But don't forget to store the value into TARGET. */ - temp = convert_to_mode (GET_MODE (target), temp, unsignedp); - emit_move_insn (target, temp); - } - else - convert_move (target, temp, unsignedp); - } - - else if (GET_MODE (temp) == BLKmode) - emit_block_move (target, temp, expr_size (exp), - TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); - else - emit_move_insn (target, temp); - } - if (dont_return_target) - return temp; - return target; -} - -/* Store the value of constructor EXP into the rtx TARGET. - TARGET is either a REG or a MEM. */ - -static void -store_constructor (exp, target) - tree exp; - rtx target; -{ - /* Don't try copying piece by piece into a hard register - since that is vulnerable to being clobbered by EXP. - Instead, construct in a pseudo register and then copy it all. */ - if (GET_CODE (target) == REG && REGNO (target) < FIRST_PSEUDO_REGISTER) - { - rtx temp = gen_reg_rtx (GET_MODE (target)); - store_constructor (exp, temp); - emit_move_insn (target, temp); - return; - } - - if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE) - { - register tree elt; - - /* If the constructor has fewer fields than the structure, - clear the whole structure first. */ - - if (list_length (CONSTRUCTOR_ELTS (exp)) - != list_length (TYPE_FIELDS (TREE_TYPE (exp)))) - clear_storage (target, int_size_in_bytes (TREE_TYPE (exp))); - else - /* Inform later passes that the old value is dead. */ - emit_insn (gen_rtx (CLOBBER, VOIDmode, target)); - - /* Store each element of the constructor into - the corresponding field of TARGET. */ - - for (elt = CONSTRUCTOR_ELTS (exp); elt; elt = TREE_CHAIN (elt)) - { - register tree field = TREE_PURPOSE (elt); - register enum machine_mode mode; - int bitsize; - int bitpos; - int unsignedp; - - bitsize = TREE_INT_CST_LOW (DECL_SIZE (field)) * DECL_SIZE_UNIT (field); - mode = DECL_MODE (field); - unsignedp = TREE_UNSIGNED (field); - - bitpos = DECL_OFFSET (field); - - store_field (target, bitsize, bitpos, mode, TREE_VALUE (elt), - /* The alignment of TARGET is - at least what its type requires. */ - VOIDmode, 0, - TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT, - int_size_in_bytes (TREE_TYPE (exp))); - } - } - else if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE) - { - register tree elt; - register int i; - tree domain = TYPE_DOMAIN (TREE_TYPE (exp)); - int minelt = TREE_INT_CST_LOW (TYPE_MIN_VALUE (domain)); - int maxelt = TREE_INT_CST_LOW (TYPE_MAX_VALUE (domain)); - tree elttype = TREE_TYPE (TREE_TYPE (exp)); - - /* If the constructor has fewer fields than the structure, - clear the whole structure first. */ - - if (list_length (CONSTRUCTOR_ELTS (exp)) < maxelt - minelt + 1) - clear_storage (target, maxelt - minelt + 1); - else - /* Inform later passes that the old value is dead. */ - emit_insn (gen_rtx (CLOBBER, VOIDmode, target)); - - /* Store each element of the constructor into - the corresponding element of TARGET, determined - by counting the elements. */ - for (elt = CONSTRUCTOR_ELTS (exp), i = 0; - elt; - elt = TREE_CHAIN (elt), i++) - { - register enum machine_mode mode; - int bitsize; - int bitpos; - int unsignedp; - - mode = TYPE_MODE (elttype); - bitsize = GET_MODE_BITSIZE (mode); - unsignedp = TREE_UNSIGNED (elttype); - - bitpos = (i * TREE_INT_CST_LOW (TYPE_SIZE (elttype)) - * TYPE_SIZE_UNIT (elttype)); - - store_field (target, bitsize, bitpos, mode, TREE_VALUE (elt), - /* The alignment of TARGET is - at least what its type requires. */ - VOIDmode, 0, - TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT, - int_size_in_bytes (TREE_TYPE (exp))); - } - } -} - -/* Store the value of EXP (an expression tree) - into a subfield of TARGET which has mode MODE and occupies - BITSIZE bits, starting BITPOS bits from the start of TARGET. - - If VALUE_MODE is VOIDmode, return nothing in particular. - UNSIGNEDP is not used in this case. - - Otherwise, return an rtx for the value stored. This rtx - has mode VALUE_MODE if that is convenient to do. - In this case, UNSIGNEDP must be nonzero if the value is an unsigned type. - - ALIGN is the alignment that TARGET is known to have, measured in bytes. - TOTAL_SIZE is its size in bytes, or -1 if variable. */ - -static rtx -store_field (target, bitsize, bitpos, mode, exp, value_mode, unsignedp, align, - total_size) - rtx target; - int bitsize, bitpos; - enum machine_mode mode; - tree exp; - enum machine_mode value_mode; - int unsignedp; - int align; - int total_size; -{ - /* If the structure is in a register or if the component - is a bit field, we cannot use addressing to access it. - Use bit-field techniques or SUBREG to store in it. */ - - if (mode == BImode || GET_CODE (target) == REG - || GET_CODE (target) == SUBREG) - { - store_bit_field (target, bitsize, bitpos, - mode, - expand_expr (exp, 0, VOIDmode, 0), - align, total_size); - if (value_mode != VOIDmode) - return extract_bit_field (target, bitsize, bitpos, unsignedp, - 0, value_mode, 0, align, total_size); - return const0_rtx; - } - else - { - rtx addr = XEXP (target, 0); - rtx to_rtx; - - /* If a value is wanted, it must be the lhs; - so make the address stable for multiple use. */ - - if (value_mode != VOIDmode && GET_CODE (addr) != REG - && ! CONSTANT_ADDRESS_P (addr)) - addr = copy_to_reg (addr); - - /* Now build a reference to just the desired component. */ - - to_rtx = change_address (target, mode, - plus_constant (addr, - (bitpos / BITS_PER_UNIT))); - MEM_IN_STRUCT_P (to_rtx) = 1; - - return store_expr (exp, to_rtx, value_mode != VOIDmode); - } -} - -/* Given an rtx VALUE that may contain additions and multiplications, - return an equivalent value that just refers to a register or memory. - This is done by generating instructions to perform the arithmetic - and returning a pseudo-register containing the value. */ - -rtx -force_operand (value, target) - rtx value, target; -{ - register optab binoptab = 0; - register rtx op2; - /* Use subtarget as the target for operand 0 of a binary operation. */ - register rtx subtarget = (target != 0 && GET_CODE (target) == REG ? target : 0); - - if (GET_CODE (value) == PLUS) - binoptab = add_optab; - else if (GET_CODE (value) == MINUS) - binoptab = sub_optab; - else if (GET_CODE (value) == MULT) - { - op2 = XEXP (value, 1); - if (!CONSTANT_P (op2) - && !(GET_CODE (op2) == REG && op2 != subtarget)) - subtarget = 0; - return expand_mult (GET_MODE (value), - force_operand (XEXP (value, 0), subtarget), - force_operand (op2, 0), - target, 0); - } - - if (binoptab) - { - op2 = XEXP (value, 1); - if (!CONSTANT_P (op2) - && !(GET_CODE (op2) == REG && op2 != subtarget)) - subtarget = 0; - if (binoptab == sub_optab - && GET_CODE (op2) == CONST_INT && INTVAL (op2) < 0) - { - binoptab = add_optab; - op2 = gen_rtx (CONST_INT, VOIDmode, - INTVAL (op2)); - } - return expand_binop (GET_MODE (value), binoptab, - force_operand (XEXP (value, 0), subtarget), - force_operand (op2, 0), - target, 0, OPTAB_LIB_WIDEN); - /* We give UNSIGNEP = 0 to expand_binop - because the only operations we are expanding here are signed ones. */ - } - return value; -} - -/* expand_expr: generate code for computing expression EXP. - An rtx for the computed value is returned. The value is never null. - In the case of a void EXP, const0_rtx is returned. - - The value may be stored in TARGET if TARGET is nonzero. - TARGET is just a suggestion; callers must assume that - the rtx returned may not be the same as TARGET. - - If TARGET is CONST0_RTX, it means that the value will be ignored. - - If TMODE is not VOIDmode, it suggests generating the - result in mode TMODE. But this is done only when convenient. - Otherwise, TMODE is ignored and the value generated in its natural mode. - TMODE is just a suggestion; callers must assume that - the rtx returned may not have mode TMODE. - - If MODIFIER is EXPAND_SUM then when EXP is an addition - we can return an rtx of the form (MULT (REG ...) (CONST_INT ...)) - or a nest of (PLUS ...) and (MINUS ...) where the terms are - products as above, or REG or MEM, or constant. - Ordinarily in such cases we would output mul or add instructions - and then return a pseudo reg containing the sum. - - If MODIFIER is EXPAND_CONST_ADDRESS then it is ok to return - a MEM rtx whose address is a constant that isn't a legitimate address. */ - -/* Subroutine of expand_expr: - save the non-copied parts (LIST) of an expr (LHS), and return a list - which can restore these values to their previous values, - should something modify their storage. */ -static tree -save_noncopied_parts (lhs, list) - tree lhs; - tree list; -{ - tree tail; - tree parts = 0; - - for (tail = list; tail; tail = TREE_CHAIN (tail)) - if (TREE_CODE (TREE_VALUE (tail)) == TREE_LIST) - parts = chainon (parts, save_noncopied_parts (TREE_VALUE (tail))); - else - { - tree part = TREE_VALUE (tail); - tree part_type = TREE_TYPE (part); - parts = tree_cons (save_expr (build_component_ref (lhs, part, parts, 0)), - build_nt (RTL_EXPR, 0, (tree) assign_stack_local (TYPE_MODE (part_type), int_size_in_bytes (part_type))), - parts); - store_expr (TREE_PURPOSE (parts), RTL_EXPR_RTL (TREE_VALUE (parts)), 0); - } - return parts; -} - -/* Subroutine of expand_expr: - return the target to use when recursively expanding - the first operand of an arithmetic operation. */ - -static rtx -validate_subtarget (subtarget, otherop) - rtx subtarget; - tree otherop; -{ - if (TREE_LITERAL (otherop)) - return subtarget; - if (TREE_CODE (otherop) == VAR_DECL - && DECL_RTL (otherop) != subtarget) - return subtarget; - return 0; -} - -rtx -expand_expr (exp, target, tmode, modifier) - register tree exp; - rtx target; - enum machine_mode tmode; - enum expand_modifier modifier; -{ - register rtx op0, op1, temp; - tree type = TREE_TYPE (exp); - register enum machine_mode mode = TYPE_MODE (type); - register enum tree_code code = TREE_CODE (exp); - optab this_optab; - int negate_1; - /* Use subtarget as the target for operand 0 of a binary operation. */ - rtx subtarget = (target != 0 && GET_CODE (target) == REG ? target : 0); - rtx original_target = target; - int ignore = target == const0_rtx; - - /* Don't use hard regs as subtargets, because the combiner - can only handle pseudo regs. */ - if (subtarget && REGNO (subtarget) < FIRST_PSEUDO_REGISTER) - subtarget = 0; - /* Avoid subtargets inside loops, - since they hide some invariant expressions. */ - if (optimize && inside_loop ()) - subtarget = 0; - - if (ignore) target = 0, original_target = 0; - - /* If will do cse, generate all results into registers - since 1) that allows cse to find more things - and 2) otherwise cse could produce an insn the machine - cannot support. */ - - if (! cse_not_expected && mode != BLKmode) - target = subtarget; - - /* No sense saving up arithmetic to be done - if it's all in the wrong mode to form part of an address. - And force_operand won't know whether to sign-extend or zero-extend. */ - - if (mode != Pmode && modifier == EXPAND_SUM) - modifier = EXPAND_NORMAL; - - /* Ensure we reference a volatile object even if value is ignored. */ - if (ignore && TREE_THIS_VOLATILE (exp) - && mode != VOIDmode && mode != BLKmode) - { - target = gen_reg_rtx (mode); - temp = expand_expr (exp, target, VOIDmode, modifier); - if (temp != target) - emit_move_insn (target, temp); - return target; - } - - switch (code) - { - case PARM_DECL: - if (DECL_RTL (exp) == 0) - { - error_with_decl (exp, "prior parameter's size depends on `%s'"); - return const0_rtx; - } - - case FUNCTION_DECL: - case VAR_DECL: - case RESULT_DECL: - if (DECL_RTL (exp) == 0) - abort (); - /* This is the case of an array whose size is to be determined - from its initializer, while the initializer is still being parsed. - See expand_decl. */ - if (GET_CODE (DECL_RTL (exp)) == MEM - && GET_CODE (XEXP (DECL_RTL (exp), 0)) == REG) - return change_address (DECL_RTL (exp), GET_MODE (DECL_RTL (exp)), - XEXP (DECL_RTL (exp), 0)); - if (GET_CODE (DECL_RTL (exp)) == MEM - && modifier != EXPAND_CONST_ADDRESS) - { - /* DECL_RTL probably contains a constant address. - On RISC machines where a constant address isn't valid, - make some insns to get that address into a register. */ - if (!memory_address_p (DECL_MODE (exp), XEXP (DECL_RTL (exp), 0)) - || (flag_force_addr - && CONSTANT_ADDRESS_P (XEXP (DECL_RTL (exp), 0)))) - return change_address (DECL_RTL (exp), VOIDmode, - copy_rtx (XEXP (DECL_RTL (exp), 0))); - } - return DECL_RTL (exp); - - case INTEGER_CST: - if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_INT) - return gen_rtx (CONST_INT, VOIDmode, TREE_INT_CST_LOW (exp)); - /* Generate immediate CONST_DOUBLE - which will be turned into memory by reload if necessary. */ - return immed_double_const (TREE_INT_CST_LOW (exp), - TREE_INT_CST_HIGH (exp), - mode); - - case CONST_DECL: - return expand_expr (DECL_INITIAL (exp), target, VOIDmode, 0); - - case REAL_CST: - /* If optimized, generate immediate CONST_DOUBLE - which will be turned into memory by reload if necessary. */ - if (!cse_not_expected) - return immed_real_const (exp); - case COMPLEX_CST: - case STRING_CST: - if (! TREE_CST_RTL (exp)) - output_constant_def (exp); - - /* TREE_CST_RTL probably contains a constant address. - On RISC machines where a constant address isn't valid, - make some insns to get that address into a register. */ - if (GET_CODE (TREE_CST_RTL (exp)) == MEM - && modifier != EXPAND_CONST_ADDRESS - && !memory_address_p (mode, XEXP (TREE_CST_RTL (exp), 0))) - return change_address (TREE_CST_RTL (exp), VOIDmode, - copy_rtx (XEXP (TREE_CST_RTL (exp), 0))); - return TREE_CST_RTL (exp); - - case SAVE_EXPR: - if (SAVE_EXPR_RTL (exp) == 0) - { - rtx reg = gen_reg_rtx (mode); - SAVE_EXPR_RTL (exp) = reg; - store_expr (TREE_OPERAND (exp, 0), reg, 0); - if (!optimize) - save_expr_regs = gen_rtx (EXPR_LIST, VOIDmode, reg, - save_expr_regs); - } - /* Don't let the same rtl node appear in two places. */ - return SAVE_EXPR_RTL (exp); - - case LET_STMT: - TREE_USED (exp) = 1; - temp = expand_expr (STMT_BODY (exp), target, tmode, modifier); - return temp; - - case RTL_EXPR: - if (RTL_EXPR_SEQUENCE (exp) == const0_rtx) - abort (); - emit_insns (RTL_EXPR_SEQUENCE (exp)); - RTL_EXPR_SEQUENCE (exp) = const0_rtx; - return RTL_EXPR_RTL (exp); - - case CONSTRUCTOR: - /* All elts simple constants => refer to a constant in memory. */ - if (TREE_STATIC (exp)) - /* For aggregate types with non-BLKmode modes, - this should ideally construct a CONST_INT. */ - { - rtx constructor = output_constant_def (exp); - if (! memory_address_p (GET_MODE (constructor), - XEXP (constructor, 0))) - constructor = change_address (constructor, VOIDmode, - XEXP (constructor, 0)); - return constructor; - } - - if (ignore) - { - tree elt; - for (elt = CONSTRUCTOR_ELTS (exp); elt; elt = TREE_CHAIN (elt)) - expand_expr (TREE_VALUE (elt), const0_rtx, VOIDmode, 0); - return const0_rtx; - } - else - { - if (target == 0) - target - = assign_stack_local (TYPE_MODE (TREE_TYPE (exp)), - int_size_in_bytes (TREE_TYPE (exp))); - store_expr (exp, target, 0); - return target; - } - - case INDIRECT_REF: - { - tree exp1 = TREE_OPERAND (exp, 0); - tree exp2; - - /* A SAVE_EXPR as the address in an INDIRECT_EXPR is generated - for *PTR += ANYTHING where PTR is put inside the SAVE_EXPR. - This code has the same general effect as simply doing - expand_expr on the save expr, except that the expression PTR - is computed for use as a memory address. This means different - code, suitable for indexing, may be generated. */ - if (TREE_CODE (exp1) == SAVE_EXPR - && SAVE_EXPR_RTL (exp1) == 0 - && TREE_CODE (exp2 = TREE_OPERAND (exp1, 0)) != ERROR_MARK - && TYPE_MODE (TREE_TYPE (exp1)) == Pmode - && TYPE_MODE (TREE_TYPE (exp2)) == Pmode) - { - temp = expand_expr (TREE_OPERAND (exp1, 0), 0, VOIDmode, EXPAND_SUM); - op0 = memory_address (mode, temp); - op0 = copy_all_regs (op0); - SAVE_EXPR_RTL (exp1) = op0; - } - else - { - op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, EXPAND_SUM); - op0 = memory_address (mode, op0); - } - } - temp = gen_rtx (MEM, mode, op0); - /* If address was computed by addition, - mark this as an element of an aggregate. */ - if (TREE_CODE (TREE_OPERAND (exp, 0)) == PLUS_EXPR - || (TREE_CODE (TREE_OPERAND (exp, 0)) == SAVE_EXPR - && TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) == PLUS_EXPR)) - MEM_IN_STRUCT_P (temp) = 1; - MEM_VOLATILE_P (temp) = TREE_THIS_VOLATILE (exp) || flag_volatile; - RTX_UNCHANGING_P (temp) = TREE_READONLY (exp); - return temp; - - case ARRAY_REF: - if (TREE_CODE (TREE_OPERAND (exp, 1)) != INTEGER_CST - || TREE_CODE (TYPE_SIZE (TREE_TYPE (exp))) != INTEGER_CST) - { - /* Nonconstant array index or nonconstant element size. - Generate the tree for *(&array+index) and expand that, - except do it in a language-independent way - and don't complain about non-lvalue arrays. - `mark_addressable' should already have been called - for any array for which this case will be reached. */ - - /* Don't forget the const or volatile flag from the array element. */ - tree variant_type = build_type_variant (type, - TREE_READONLY (exp), - TREE_THIS_VOLATILE (exp)); - tree array_adr = build (ADDR_EXPR, TYPE_POINTER_TO (variant_type), - TREE_OPERAND (exp, 0)); - tree index = TREE_OPERAND (exp, 1); - tree elt; - - /* Convert the integer argument to a type the same size as a pointer - so the multiply won't overflow spuriously. */ - if (TYPE_PRECISION (TREE_TYPE (index)) != POINTER_SIZE) - index = convert (type_for_size (POINTER_SIZE, 0), index); - - /* The array address isn't volatile even if the array is. */ - TREE_VOLATILE (array_adr) = 0; - - elt = build (INDIRECT_REF, type, - fold (build (PLUS_EXPR, TYPE_POINTER_TO (variant_type), - array_adr, - fold (build (MULT_EXPR, - TYPE_POINTER_TO (variant_type), - index, size_in_bytes (type)))))); - - return expand_expr (elt, target, tmode, modifier); - } - - /* Fold an expression like: "foo"[2]. - This is not done in fold so it won't happen inside &. */ - { - int i; - tree arg0 = TREE_OPERAND (exp, 0); - tree arg1 = TREE_OPERAND (exp, 1); - - if (TREE_CODE (arg0) == STRING_CST - && TREE_CODE (arg1) == INTEGER_CST - && !TREE_INT_CST_HIGH (arg1) - && (i = TREE_INT_CST_LOW (arg1)) < TREE_STRING_LENGTH (arg0)) - { - if (TREE_TYPE (TREE_TYPE (arg0)) == integer_type_node) - { - exp = build_int_2 (((int *)TREE_STRING_POINTER (arg0))[i], 0); - TREE_TYPE (exp) = integer_type_node; - return expand_expr (exp, target, tmode, modifier); - } - if (TREE_TYPE (TREE_TYPE (arg0)) == char_type_node) - { - exp = build_int_2 (TREE_STRING_POINTER (arg0)[i], 0); - TREE_TYPE (exp) = integer_type_node; - return expand_expr (convert (TREE_TYPE (TREE_TYPE (arg0)), exp), target, tmode, modifier); - } - } - } - - /* If this is a constant index into a constant array, - just get the value from the array. */ - if (TREE_READONLY (TREE_OPERAND (exp, 0)) - && ! TREE_VOLATILE (TREE_OPERAND (exp, 0)) - && TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 0))) == ARRAY_TYPE - && TREE_LITERAL (TREE_OPERAND (exp, 1)) - && TREE_CODE (TREE_OPERAND (exp, 0)) == VAR_DECL - && DECL_INITIAL (TREE_OPERAND (exp, 0)) - && TREE_CODE (DECL_INITIAL (TREE_OPERAND (exp, 0))) != ERROR_MARK) - { - tree index = fold (TREE_OPERAND (exp, 1)); - if (TREE_CODE (index) == INTEGER_CST) - { - int i = TREE_INT_CST_LOW (index); - tree init = CONSTRUCTOR_ELTS (DECL_INITIAL (TREE_OPERAND (exp, 0))); - - while (init && i--) - init = TREE_CHAIN (init); - if (init) - return expand_expr (fold (TREE_VALUE (init)), target, tmode, modifier); - } - } - /* Treat array-ref with constant index as a component-ref. */ - - case COMPONENT_REF: - { - register enum machine_mode mode1; - int volstruct = 0; - int bitsize; - tree tem = exp; - int bitpos = 0; - int unsignedp; - - if (TREE_CODE (exp) == COMPONENT_REF) - { - tree field = TREE_OPERAND (exp, 1); - bitsize = TREE_INT_CST_LOW (DECL_SIZE (field)) * DECL_SIZE_UNIT (field); - mode1 = DECL_MODE (TREE_OPERAND (exp, 1)); - unsignedp = TREE_UNSIGNED (field); - } - else - { - mode1 = TYPE_MODE (TREE_TYPE (exp)); - bitsize = GET_MODE_BITSIZE (mode1); - unsignedp = TREE_UNSIGNED (TREE_TYPE (exp)); - } - - /* Compute cumulative bit-offset for nested component-refs - and array-refs, and find the ultimate containing object. */ - - while (1) - { - if (TREE_CODE (tem) == COMPONENT_REF) - { - bitpos += DECL_OFFSET (TREE_OPERAND (tem, 1)); - if (TREE_THIS_VOLATILE (tem)) - volstruct = 1; - } - else if (TREE_CODE (tem) == ARRAY_REF - && TREE_CODE (TREE_OPERAND (tem, 1)) == INTEGER_CST - && TREE_CODE (TYPE_SIZE (TREE_TYPE (tem))) == INTEGER_CST) - { - bitpos += (TREE_INT_CST_LOW (TREE_OPERAND (tem, 1)) - * TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (tem))) - * TYPE_SIZE_UNIT (TREE_TYPE (tem))); - if (TREE_THIS_VOLATILE (tem)) - volstruct = 1; - } - else - break; - tem = TREE_OPERAND (tem, 0); - } - - op0 = expand_expr (tem, 0, VOIDmode, - (modifier == EXPAND_CONST_ADDRESS - ? modifier : EXPAND_NORMAL)); - - if (mode1 == BImode || GET_CODE (op0) == REG - || GET_CODE (op0) == SUBREG) - return extract_bit_field (op0, bitsize, bitpos, unsignedp, - target, mode, tmode, - TYPE_ALIGN (TREE_TYPE (tem)) / BITS_PER_UNIT, - int_size_in_bytes (TREE_TYPE (tem))); - /* Get a reference to just this component. */ - if (modifier == EXPAND_CONST_ADDRESS) - op0 = gen_rtx (MEM, mode1, plus_constant (XEXP (op0, 0), - (bitpos / BITS_PER_UNIT))); - else - op0 = change_address (op0, mode1, - plus_constant (XEXP (op0, 0), - (bitpos / BITS_PER_UNIT))); - MEM_IN_STRUCT_P (op0) = 1; - MEM_VOLATILE_P (op0) |= volstruct; - /* If OP0 is in the shared structure-value stack slot, - and it is not BLKmode, copy it into a register. - The shared slot may be clobbered at any time by another call. - BLKmode is safe because our caller will either copy the value away - or take another component and come back here. */ - if (mode != BLKmode - && TREE_CODE (TREE_OPERAND (exp, 0)) == CALL_EXPR - && TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))) == BLKmode) - op0 = copy_to_reg (op0); - if (mode == mode1 || mode1 == BLKmode || mode1 == tmode) - return op0; - if (target == 0) - target = gen_reg_rtx (tmode != VOIDmode ? tmode : mode); - convert_move (target, op0, unsignedp); - return target; - } - - /* Intended for a reference to a buffer of a file-object in Pascal. - But it's not certain that a special tree code will really be - necessary for these. INDIRECT_REF might work for them. */ - case BUFFER_REF: - abort (); - - case WITH_CLEANUP_EXPR: - RTL_EXPR_RTL (TREE_OPERAND (exp, 1)) - = expand_expr (TREE_OPERAND (exp, 0), target, tmode, modifier); - cleanups_of_this_call = tree_cons (0, TREE_OPERAND (exp, 2), cleanups_of_this_call); - return RTL_EXPR_RTL (TREE_OPERAND (exp, 1)); - - case CALL_EXPR: - /* Check for a built-in function. */ - if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) == FUNCTION_DECL - && (DECL_FUNCTION_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) - != NOT_BUILT_IN)) - return expand_builtin (exp, target, subtarget, tmode, ignore); - /* If this call was expanded already by preexpand_calls, - just return the result we got. */ - if (CALL_EXPR_RTL (exp) != 0) - return CALL_EXPR_RTL (exp); - return expand_call (exp, target, ignore); - - case NOP_EXPR: - case CONVERT_EXPR: - case REFERENCE_EXPR: - if (TREE_CODE (type) == VOID_TYPE || ignore) - { - expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, modifier); - return const0_rtx; - } - if (mode == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) - return expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, modifier); - op0 = expand_expr (TREE_OPERAND (exp, 0), 0, mode, 0); - if (GET_MODE (op0) == mode || GET_MODE (op0) == VOIDmode) - return op0; - if (flag_force_mem && GET_CODE (op0) == MEM) - op0 = copy_to_reg (op0); - if (GET_MODE (op0) == VOIDmode) - /* Avoid problem in convert_move due to unknown mode of OP0. */ - op0 = copy_to_mode_reg (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))), - op0); - if (target == 0) - target = gen_reg_rtx (mode); - convert_move (target, op0, TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0)))); - return target; - - case PLUS_EXPR: - preexpand_calls (exp); - if (TREE_CODE (TREE_OPERAND (exp, 0)) == INTEGER_CST - && modifier == EXPAND_SUM) - { - op1 = expand_expr (TREE_OPERAND (exp, 1), subtarget, VOIDmode, EXPAND_SUM); - op1 = plus_constant (op1, TREE_INT_CST_LOW (TREE_OPERAND (exp, 0))); - return op1; - } - negate_1 = 1; - plus_minus: - if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST - && modifier == EXPAND_SUM) - { - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, EXPAND_SUM); - op0 = plus_constant (op0, - negate_1 * TREE_INT_CST_LOW (TREE_OPERAND (exp, 1))); - return op0; - } - this_optab = add_optab; - if (modifier != EXPAND_SUM) goto binop; - subtarget = validate_subtarget (subtarget, TREE_OPERAND (exp, 1)); - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, EXPAND_SUM); - op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, EXPAND_SUM); - /* Put a sum last, to simplify what follows. */ -#ifdef OLD_INDEXING - if (GET_CODE (op1) == MULT) - { - temp = op0; - op0 = op1; - op1 = temp; - } -#endif -#ifndef OLD_INDEXING - /* Make sure any term that's a sum with a constant comes last. */ - if (GET_CODE (op0) == PLUS - && CONSTANT_P (XEXP (op0, 1))) - { - temp = op0; - op0 = op1; - op1 = temp; - } - /* If adding to a sum including a constant, - associate it to put the constant outside. */ - if (GET_CODE (op1) == PLUS - && CONSTANT_P (XEXP (op1, 1))) - { - rtx tem; - int constant_term = 0; - - op0 = gen_rtx (PLUS, mode, XEXP (op1, 0), op0); - /* Let's also eliminate constants from op0 if possible. */ - tem = eliminate_constant_term (op0, &constant_term); - if (GET_CODE (XEXP (op1, 1)) == CONST_INT) - { - if (constant_term != 0) - return plus_constant (tem, INTVAL (XEXP (op1, 1)) + constant_term); - else - return plus_constant (op0, INTVAL (XEXP (op1, 1))); - } - else - return gen_rtx (PLUS, mode, op0, XEXP (op1, 1)); - } -#endif - return gen_rtx (PLUS, mode, op0, op1); - - case MINUS_EXPR: - preexpand_calls (exp); - if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST - && GET_MODE_BITSIZE (TYPE_MODE (type)) <= HOST_BITS_PER_INT) - { - int negated; - if (modifier == EXPAND_SUM) - { - negate_1 = -1; - goto plus_minus; - } - subtarget = validate_subtarget (subtarget, TREE_OPERAND (exp, 1)); - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - negated = - TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)); - if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_INT) - negated &= (1 << GET_MODE_BITSIZE (mode)) - 1; - op1 = gen_rtx (CONST_INT, VOIDmode, negated); - this_optab = add_optab; - goto binop2; - } - this_optab = sub_optab; - goto binop; - - case MULT_EXPR: - preexpand_calls (exp); - /* If first operand is constant, swap them. - Thus the following special case checks need only - check the second operand. */ - if (TREE_CODE (TREE_OPERAND (exp, 0)) == INTEGER_CST) - { - register tree t1 = TREE_OPERAND (exp, 0); - TREE_OPERAND (exp, 0) = TREE_OPERAND (exp, 1); - TREE_OPERAND (exp, 1) = t1; - } - - /* Attempt to return something suitable for generating an - indexed address, for machines that support that. */ - - if (modifier == EXPAND_SUM - && TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST) - { - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, EXPAND_SUM); - - /* Apply distributive law if OP0 is x+c. */ - if (GET_CODE (op0) == PLUS - && GET_CODE (XEXP (op0, 1)) == CONST_INT) - return gen_rtx (PLUS, mode, - gen_rtx (MULT, mode, XEXP (op0, 0), - gen_rtx (CONST_INT, VOIDmode, - TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)))), - gen_rtx (CONST_INT, VOIDmode, - (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)) - * INTVAL (XEXP (op0, 1))))); - - if (GET_CODE (op0) != REG) - op0 = force_operand (op0, 0); - if (GET_CODE (op0) != REG) - op0 = copy_to_mode_reg (mode, op0); - - return gen_rtx (MULT, mode, op0, - gen_rtx (CONST_INT, VOIDmode, - TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)))); - } - subtarget = validate_subtarget (subtarget, TREE_OPERAND (exp, 1)); - /* Check for multiplying things that have been extended - from a narrower type. If this machine supports multiplying - in that narrower type with a result in the desired type, - do it that way, and avoid the explicit type-conversion. */ - if (TREE_CODE (TREE_OPERAND (exp, 0)) == NOP_EXPR - && TREE_CODE (TREE_TYPE (exp)) == INTEGER_TYPE - && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))) - < TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (exp, 0)))) - && ((TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST - && int_fits_type_p (TREE_OPERAND (exp, 1), - TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))) - /* Don't use a widening multiply if a shift will do. */ - && exact_log2 (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1))) < 0) - || - (TREE_CODE (TREE_OPERAND (exp, 1)) == NOP_EXPR - && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 1), 0))) - == - TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))) - /* If both operands are extended, they must either both - be zero-extended or both be sign-extended. */ - && (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 1), 0))) - == - TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))))))) - { - enum machine_mode innermode - = TYPE_MODE (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))); - this_optab = (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))) - ? umul_widen_optab : smul_widen_optab); - if (mode == GET_MODE_WIDER_MODE (innermode) - && this_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) - { - op0 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 0), 0), - 0, VOIDmode, 0); - if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST) - op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0); - else - op1 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 1), 0), - 0, VOIDmode, 0); - goto binop2; - } - } - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0); - return expand_mult (mode, op0, op1, target, TREE_UNSIGNED (type)); - - case TRUNC_DIV_EXPR: - case FLOOR_DIV_EXPR: - case CEIL_DIV_EXPR: - case ROUND_DIV_EXPR: - case EXACT_DIV_EXPR: - preexpand_calls (exp); - subtarget = validate_subtarget (subtarget, TREE_OPERAND (exp, 1)); - /* Possible optimization: compute the dividend with EXPAND_SUM - then if the divisor is constant can optimize the case - where some terms of the dividend have coeffs divisible by it. */ - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0); - return expand_divmod (0, code, mode, op0, op1, target, - TREE_UNSIGNED (type)); - - case RDIV_EXPR: - preexpand_calls (exp); - this_optab = flodiv_optab; - goto binop; - - case TRUNC_MOD_EXPR: - case FLOOR_MOD_EXPR: - case CEIL_MOD_EXPR: - case ROUND_MOD_EXPR: - preexpand_calls (exp); - subtarget = validate_subtarget (subtarget, TREE_OPERAND (exp, 1)); - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0); - return expand_divmod (1, code, mode, op0, op1, target, - TREE_UNSIGNED (type)); -#if 0 -#ifdef HAVE_divmoddisi4 - if (GET_MODE (op0) != DImode) - { - temp = gen_reg_rtx (DImode); - convert_move (temp, op0, 0); - op0 = temp; - if (GET_MODE (op1) != SImode && GET_CODE (op1) != CONST_INT) - { - temp = gen_reg_rtx (SImode); - convert_move (temp, op1, 0); - op1 = temp; - } - temp = gen_reg_rtx (SImode); - if (target == 0) - target = gen_reg_rtx (SImode); - emit_insn (gen_divmoddisi4 (temp, protect_from_queue (op0, 0), - protect_from_queue (op1, 0), - protect_from_queue (target, 1))); - return target; - } -#endif -#endif - - case FIX_ROUND_EXPR: - case FIX_FLOOR_EXPR: - case FIX_CEIL_EXPR: - abort (); /* Not used for C. */ - - case FIX_TRUNC_EXPR: - op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0); - if (target == 0) - target = gen_reg_rtx (mode); - { - int unsignedp = TREE_UNSIGNED (TREE_TYPE (exp)); - if (mode == HImode || mode == QImode) - { - register rtx temp = gen_reg_rtx (SImode); - expand_fix (temp, op0, 0); - convert_move (target, temp, 0); - } - else - expand_fix (target, op0, unsignedp); - } - return target; - - case FLOAT_EXPR: - op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0); - if (target == 0) - target = gen_reg_rtx (mode); - if (GET_MODE (op0) == VOIDmode) - /* Avoid problem in convert_move due to unknown mode of OP0. */ - op0 = copy_to_mode_reg (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))), - op0); - { - int unsignedp = TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))); - if (GET_MODE (op0) == HImode - || GET_MODE (op0) == QImode) - { - register rtx temp = gen_reg_rtx (SImode); - convert_move (temp, op0, unsignedp); - expand_float (target, temp, 0); - } - else - expand_float (target, op0, unsignedp); - } - return target; - - case NEGATE_EXPR: - op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, 0); - temp = expand_unop (mode, neg_optab, op0, target, 0); - if (temp == 0) - abort (); - return temp; - - case ABS_EXPR: - /* First try to do it with a special abs instruction. - If that does not win, use conditional jump and negate. */ - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - temp = expand_unop (mode, abs_optab, op0, target, 0); - if (temp != 0) - return temp; - temp = gen_label_rtx (); - if (target == 0 || GET_CODE (target) != REG) - target = gen_reg_rtx (mode); - emit_move_insn (target, op0); - emit_cmp_insn (target, - expand_expr (convert (TREE_TYPE (exp), integer_zero_node), - 0, VOIDmode, 0), - 0, 0, 0); - NO_DEFER_POP; - emit_jump_insn (gen_bge (temp)); - op0 = expand_unop (mode, neg_optab, target, target, 0); - if (op0 != target) - emit_move_insn (target, op0); - emit_label (temp); - OK_DEFER_POP; - return target; - - case MAX_EXPR: - case MIN_EXPR: - mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 1))); - op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0); - if (target == 0 || GET_CODE (target) != REG || target == op1) - target = gen_reg_rtx (mode); - op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, 0); - if (target != op0) - emit_move_insn (target, op0); - op0 = gen_label_rtx (); - if (code == MAX_EXPR) - temp = (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 1))) - ? compare1 (target, op1, GEU, LEU, 1, mode) - : compare1 (target, op1, GE, LE, 0, mode)); - else - temp = (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 1))) - ? compare1 (target, op1, LEU, GEU, 1, mode) - : compare1 (target, op1, LE, GE, 0, mode)); - if (temp == const0_rtx) - emit_move_insn (target, op1); - else if (temp != const1_rtx) - { - if (bcc_gen_fctn[(int) GET_CODE (temp)] != 0) - emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (temp)]) (op0)); - else - abort (); - emit_move_insn (target, op1); - } - emit_label (op0); - return target; - -/* ??? Can optimize when the operand of this is a bitwise operation, - by using a different bitwise operation. */ - case BIT_NOT_EXPR: - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - temp = expand_unop (mode, one_cmpl_optab, op0, target, 1); - if (temp == 0) - abort (); - return temp; - - case FFS_EXPR: - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - temp = expand_unop (mode, ffs_optab, op0, target, 1); - if (temp == 0) - abort (); - return temp; - -/* ??? Can optimize bitwise operations with one arg constant. - Pastel optimizes (a bitwise1 n) bitwise2 (a bitwise3 b) - and (a bitwise1 b) bitwise2 b (etc) - but that is probably not worth while. */ - -/* BIT_AND_EXPR is for bitwise anding. - TRUTH_AND_EXPR is for anding two boolean values - when we want in all cases to compute both of them. - In general it is fastest to do TRUTH_AND_EXPR by - computing both operands as actual zero-or-1 values - and then bitwise anding. In cases where there cannot - be any side effects, better code would be made by - treating TRUTH_AND_EXPR like TRUTH_ANDIF_EXPR; - but the question is how to recognize those cases. */ - - case TRUTH_AND_EXPR: - case BIT_AND_EXPR: - preexpand_calls (exp); - subtarget = validate_subtarget (subtarget, TREE_OPERAND (exp, 1)); - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0); - return expand_bit_and (mode, op0, op1, target); - -/* See comment above about TRUTH_AND_EXPR; it applies here too. */ - case TRUTH_OR_EXPR: - case BIT_IOR_EXPR: - preexpand_calls (exp); - this_optab = ior_optab; - goto binop; - - case BIT_XOR_EXPR: - preexpand_calls (exp); - this_optab = xor_optab; - goto binop; - - case LSHIFT_EXPR: - case RSHIFT_EXPR: - case LROTATE_EXPR: - case RROTATE_EXPR: - preexpand_calls (exp); - subtarget = validate_subtarget (subtarget, TREE_OPERAND (exp, 1)); - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - return expand_shift (code, mode, op0, TREE_OPERAND (exp, 1), target, - TREE_UNSIGNED (type)); - -/* ??? cv's were used to effect here to combine additive constants - and to determine the answer when only additive constants differ. - Also, the addition of one can be handled by changing the condition. */ - case LT_EXPR: - case LE_EXPR: - case GT_EXPR: - case GE_EXPR: - case EQ_EXPR: - case NE_EXPR: - preexpand_calls (exp); - temp = do_store_flag (exp, target, mode); - if (temp != 0) - return temp; - /* For foo != 0, load foo, and if it is nonzero load 1 instead. */ - if (code == NE_EXPR && integer_zerop (TREE_OPERAND (exp, 1)) - && subtarget - && (GET_MODE (subtarget) - == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))) - { - temp = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - if (temp != subtarget) - temp = copy_to_reg (temp); - op1 = gen_label_rtx (); - emit_cmp_insn (temp, const0_rtx, 0, TREE_UNSIGNED (type), 0); - emit_jump_insn (gen_beq (op1)); - emit_move_insn (temp, const1_rtx); - emit_label (op1); - return temp; - } - /* If no set-flag instruction, must generate a conditional - store into a temporary variable. Drop through - and handle this like && and ||. */ - - case TRUTH_ANDIF_EXPR: - case TRUTH_ORIF_EXPR: - temp = gen_reg_rtx (mode); - emit_clr_insn (temp); - op1 = gen_label_rtx (); - jumpifnot (exp, op1); - emit_0_to_1_insn (temp); - emit_label (op1); - return temp; - - case TRUTH_NOT_EXPR: - op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, 0); - /* The parser is careful to generate TRUTH_NOT_EXPR - only with operands that are always zero or one. */ - temp = expand_binop (mode, xor_optab, op0, - gen_rtx (CONST_INT, mode, 1), - target, 1, OPTAB_LIB_WIDEN); - if (temp == 0) - abort (); - return temp; - - case COMPOUND_EXPR: - expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, 0); - emit_queue (); - return expand_expr (TREE_OPERAND (exp, 1), target, VOIDmode, 0); - - case COND_EXPR: - /* Note that COND_EXPRs whose type is a structure or union - are required to be constructed to contain assignments of - a temporary variable, so that we can evaluate them here - for side effect only. If type is void, we must do likewise. */ - op0 = gen_label_rtx (); - op1 = gen_label_rtx (); - - if (mode == VOIDmode || ignore) - temp = 0; - else if (target) - temp = target; - else if (mode == BLKmode) - { - if (TYPE_SIZE (type) == 0 || ! TREE_LITERAL (TYPE_SIZE (type))) - abort (); - temp = assign_stack_local (BLKmode, - (TREE_INT_CST_LOW (TYPE_SIZE (type)) - * TYPE_SIZE_UNIT (type) - + BITS_PER_UNIT - 1) - / BITS_PER_UNIT); - } - else - temp = gen_reg_rtx (mode); - - jumpifnot (TREE_OPERAND (exp, 0), op0); - NO_DEFER_POP; - if (temp != 0) - store_expr (TREE_OPERAND (exp, 1), temp, 0); - else - expand_expr (TREE_OPERAND (exp, 1), ignore ? const0_rtx : 0, - VOIDmode, 0); - emit_queue (); - emit_jump_insn (gen_jump (op1)); - emit_barrier (); - emit_label (op0); - if (temp != 0) - store_expr (TREE_OPERAND (exp, 2), temp, 0); - else - expand_expr (TREE_OPERAND (exp, 2), ignore ? const0_rtx : 0, - VOIDmode, 0); - emit_queue (); - emit_label (op1); - OK_DEFER_POP; - return temp; - - case MODIFY_EXPR: - { - /* If lhs is complex, expand calls in rhs before computing it. - That's so we don't compute a pointer and save it over a call. - If lhs is simple, compute it first so we can give it as a - target if the rhs is just a call. This avoids an extra temp and copy - and that prevents a partial-subsumption which makes bad code. - Actually we could treat component_ref's of vars like vars. */ - - tree lhs = TREE_OPERAND (exp, 0); - tree rhs = TREE_OPERAND (exp, 1); - tree noncopied_parts; - - if (TREE_CODE (lhs) != VAR_DECL - && TREE_CODE (lhs) != RESULT_DECL - && TREE_CODE (lhs) != PARM_DECL) - preexpand_calls (exp); - - noncopied_parts = save_noncopied_parts (lhs, TYPE_NONCOPIED_PARTS (TREE_TYPE (lhs))); - temp = expand_assignment (lhs, rhs, ! ignore, original_target != 0); - while (noncopied_parts != 0) - { - store_expr (TREE_VALUE (noncopied_parts), - SAVE_EXPR_RTL (TREE_PURPOSE (noncopied_parts)), 0); - noncopied_parts = TREE_CHAIN (noncopied_parts); - } - return temp; - } - - case PREINCREMENT_EXPR: - case PREDECREMENT_EXPR: - return expand_increment (exp, 0); - - case POSTINCREMENT_EXPR: - case POSTDECREMENT_EXPR: - return expand_increment (exp, 1); - - case ADDR_EXPR: - op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, - EXPAND_CONST_ADDRESS); - if (GET_CODE (op0) != MEM) - abort (); - if (modifier == EXPAND_SUM) - return XEXP (op0, 0); - op0 = force_operand (XEXP (op0, 0), target); - if (flag_force_addr && GET_CODE (op0) != REG) - return force_reg (Pmode, op0); - return op0; - - case ENTRY_VALUE_EXPR: - abort (); - - case ERROR_MARK: - return const0_rtx; - - default: - abort (); - } - - /* Here to do an ordinary binary operator, generating an instruction - from the optab already placed in `this_optab'. */ - binop: - /* Detect things like x = y | (a == b) - and do them as (x = y), (a == b ? x |= 1 : 0), x. */ - /* First, get the comparison or conditional into the second arg. */ - if (comparison_code[(int) TREE_CODE (TREE_OPERAND (exp, 0))] - || (TREE_CODE (TREE_OPERAND (exp, 0)) == COND_EXPR - && (integer_zerop (TREE_OPERAND (TREE_OPERAND (exp, 0), 1)) - || integer_zerop (TREE_OPERAND (TREE_OPERAND (exp, 0), 2))))) - { - if (this_optab == ior_optab || this_optab == add_optab - || this_optab == xor_optab) - { - tree exch = TREE_OPERAND (exp, 1); - TREE_OPERAND (exp, 1) = TREE_OPERAND (exp, 0); - TREE_OPERAND (exp, 0) = exch; - } - } - /* Optimize X + (Y ? Z : 0) by computing X and maybe adding Z. */ - if (comparison_code[(int) TREE_CODE (TREE_OPERAND (exp, 1))] - || (TREE_CODE (TREE_OPERAND (exp, 1)) == COND_EXPR - && (integer_zerop (TREE_OPERAND (TREE_OPERAND (exp, 1), 1)) - || integer_zerop (TREE_OPERAND (TREE_OPERAND (exp, 1), 2))))) - { - if (this_optab == ior_optab || this_optab == add_optab - || this_optab == xor_optab || this_optab == sub_optab - || this_optab == lshl_optab || this_optab == ashl_optab - || this_optab == lshr_optab || this_optab == ashr_optab - || this_optab == rotl_optab || this_optab == rotr_optab) - { - tree thenexp; - rtx thenv = 0; - - /* TARGET gets a reg in which we can perform the computation. - Use the specified target if it's a pseudo reg and safe. */ - target = validate_subtarget (subtarget, TREE_OPERAND (exp, 1)); - if (target == 0) target = gen_reg_rtx (mode); - - /* Compute X into the target. */ - store_expr (TREE_OPERAND (exp, 0), target, 0); - op0 = gen_label_rtx (); - - /* If other operand is a comparison COMP, treat it as COMP ? 1 : 0 */ - if (TREE_CODE (TREE_OPERAND (exp, 1)) != COND_EXPR) - { - do_jump (TREE_OPERAND (exp, 1), op0, 0); - thenv = const1_rtx; - } - else if (integer_zerop (TREE_OPERAND (TREE_OPERAND (exp, 1), 2))) - { - do_jump (TREE_OPERAND (TREE_OPERAND (exp, 1), 0), op0, 0); - thenexp = TREE_OPERAND (TREE_OPERAND (exp, 1), 1); - } - else - { - do_jump (TREE_OPERAND (TREE_OPERAND (exp, 1), 0), 0, op0); - thenexp = TREE_OPERAND (TREE_OPERAND (exp, 1), 2); - } - - if (thenv == 0) - thenv = expand_expr (thenexp, 0, VOIDmode, 0); - - /* THENV is now Z, the value to operate on, as an rtx. - We have already tested that Y isn't zero, so do the operation. */ - - if (this_optab == rotl_optab || this_optab == rotr_optab) - temp = expand_binop (mode, this_optab, target, thenv, target, - -1, OPTAB_LIB); - else if (this_optab == lshl_optab || this_optab == lshr_optab) - temp = expand_binop (mode, this_optab, target, thenv, target, - 1, OPTAB_LIB_WIDEN); - else - temp = expand_binop (mode, this_optab, target, thenv, target, - 0, OPTAB_LIB_WIDEN); - if (target != temp) - emit_move_insn (target, temp); - - emit_queue (); - do_pending_stack_adjust (); - emit_label (op0); - return target; - } - } - subtarget = validate_subtarget (subtarget, TREE_OPERAND (exp, 1)); - op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0); - op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0); - binop2: - temp = expand_binop (mode, this_optab, op0, op1, target, - TREE_UNSIGNED (TREE_TYPE (exp)), OPTAB_LIB_WIDEN); - if (temp == 0) - abort (); - return temp; -} - -/* Expand an expression EXP that calls a built-in function, - with result going to TARGET if that's convenient - (and in mode MODE if that's convenient). - SUBTARGET may be used as the target for computing one of EXP's operands. - IGNORE is nonzero if the value is to be ignored. */ - -static rtx -expand_builtin (exp, target, subtarget, mode, ignore) - tree exp; - rtx target; - rtx subtarget; - enum machine_mode mode; - int ignore; -{ - tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); - tree arglist = TREE_OPERAND (exp, 1); - rtx op0; - - switch (DECL_FUNCTION_CODE (fndecl)) - { - case BUILT_IN_ABS: - case BUILT_IN_LABS: - case BUILT_IN_FABS: - /* build_function_call changes these into ABS_EXPR. */ - abort (); - - case BUILT_IN_SAVEREGS: - if (saveregs_value != 0) - return saveregs_value; - { - /* When this function is called, it means that registers must be - saved on entry to this function. So we migrate the - call to the first insn of this function. */ - rtx last = get_last_insn (); - /* Now really call the function. `expand_call' does not call - expand_builtin, so there is no danger of infinite recursion here. */ - rtx temp = expand_call (exp, target, ignore); - reorder_insns (NEXT_INSN (last), get_last_insn (), get_insns ()); - saveregs_value = temp; - return temp; - } - - case BUILT_IN_NEXT_ARG: - { - tree fntype = TREE_TYPE (current_function_decl); - if (!(TYPE_ARG_TYPES (fntype) != 0 - && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) - != void_type_node))) - { - error ("`stdarg.h' facilities used, but function has fixed args"); - return const0_rtx; - } - } - - return expand_binop (Pmode, add_optab, - arg_pointer_rtx, current_function_arg_offset_rtx, - 0, 0, OPTAB_LIB_WIDEN); - - case BUILT_IN_CLASSIFY_TYPE: - if (arglist != 0) - { - tree type = TREE_TYPE (TREE_VALUE (arglist)); - enum tree_code code = TREE_CODE (type); - if (code == VOID_TYPE) - return gen_rtx (CONST_INT, VOIDmode, void_type_class); - if (code == INTEGER_TYPE) - return gen_rtx (CONST_INT, VOIDmode, integer_type_class); - if (code == CHAR_TYPE) - return gen_rtx (CONST_INT, VOIDmode, char_type_class); - if (code == ENUMERAL_TYPE) - return gen_rtx (CONST_INT, VOIDmode, enumeral_type_class); - if (code == BOOLEAN_TYPE) - return gen_rtx (CONST_INT, VOIDmode, boolean_type_class); - if (code == POINTER_TYPE) - return gen_rtx (CONST_INT, VOIDmode, pointer_type_class); - if (code == REFERENCE_TYPE) - return gen_rtx (CONST_INT, VOIDmode, reference_type_class); - if (code == OFFSET_TYPE) - return gen_rtx (CONST_INT, VOIDmode, offset_type_class); - if (code == REAL_TYPE) - return gen_rtx (CONST_INT, VOIDmode, real_type_class); - if (code == COMPLEX_TYPE) - return gen_rtx (CONST_INT, VOIDmode, complex_type_class); - if (code == FUNCTION_TYPE) - return gen_rtx (CONST_INT, VOIDmode, function_type_class); - if (code == METHOD_TYPE) - return gen_rtx (CONST_INT, VOIDmode, method_type_class); - if (code == RECORD_TYPE) - return gen_rtx (CONST_INT, VOIDmode, record_type_class); - if (code == UNION_TYPE) - return gen_rtx (CONST_INT, VOIDmode, union_type_class); - if (code == ARRAY_TYPE) - return gen_rtx (CONST_INT, VOIDmode, array_type_class); - if (code == STRING_TYPE) - return gen_rtx (CONST_INT, VOIDmode, string_type_class); - if (code == SET_TYPE) - return gen_rtx (CONST_INT, VOIDmode, set_type_class); - if (code == FILE_TYPE) - return gen_rtx (CONST_INT, VOIDmode, file_type_class); - if (code == LANG_TYPE) - return gen_rtx (CONST_INT, VOIDmode, lang_type_class); - } - return gen_rtx (CONST_INT, VOIDmode, no_type_class); - - case BUILT_IN_ALLOCA: - if (arglist == 0 - /* Arg could be non-integer if user redeclared this fcn wrong. */ - || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE) - return const0_rtx; - frame_pointer_needed = 1; - current_function_calls_alloca = 1; - /* Compute the argument. */ - op0 = expand_expr (TREE_VALUE (arglist), 0, VOIDmode, 0); - if (! CONSTANT_P (op0)) - { - op0 = force_reg (GET_MODE (op0), op0); - if (GET_MODE (op0) != Pmode) - op0 = convert_to_mode (Pmode, op0, 1); - } - /* Push that much space (rounding it up). */ - do_pending_stack_adjust (); - -#ifdef STACK_POINTER_OFFSET - /* If we will have to round the result down (which is up - if stack grows down), make sure we have extra space so the - user still gets at least as much space as he asked for. */ - if ((STACK_POINTER_OFFSET + STACK_BYTES - 1) / STACK_BYTES - != STACK_POINTER_OFFSET / STACK_BYTES) - op0 = plus_constant (op0, STACK_BYTES); -#endif - -#ifdef STACK_GROWS_DOWNWARD - anti_adjust_stack (round_push (op0)); -#endif - /* Return a copy of current stack ptr, in TARGET if possible. */ - if (target) - emit_move_insn (target, stack_pointer_rtx); - else - target = copy_to_reg (stack_pointer_rtx); -#ifdef STACK_POINTER_OFFSET - /* If the contents of the stack pointer reg are offset from the - actual top-of-stack address, add the offset here. */ - if (GET_CODE (target) == REG) - emit_insn (gen_add2_insn (target, - gen_rtx (CONST_INT, VOIDmode, - (STACK_POINTER_OFFSET + STACK_BYTES - 1) / STACK_BYTES * STACK_BYTES))); - else - { - rtx temp = - expand_binop (GET_MODE (target), add_optab, target, - gen_rtx (CONST_INT, VOIDmode, - (STACK_POINTER_OFFSET + STACK_BYTES - 1) / STACK_BYTES * STACK_BYTES), - target, - 1, OPTAB_DIRECT); - if (temp == 0) abort (); - if (temp != target) - emit_move_insn (target, temp); - } -#endif -#ifndef STACK_GROWS_DOWNWARD - anti_adjust_stack (round_push (op0)); -#endif - /* Some systems require a particular insn to refer to the stack - to make the pages exist. */ -#ifdef HAVE_probe - if (HAVE_probe) - emit_insn (gen_probe ()); -#endif - return target; - - case BUILT_IN_FFS: - if (arglist == 0 - /* Arg could be non-integer if user redeclared this fcn wrong. */ - || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE) - return const0_rtx; - - /* Compute the argument. */ - op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0); - /* Compute ffs, into TARGET if possible. - Set TARGET to wherever the result comes back. */ - target = expand_unop (GET_MODE (op0), ffs_optab, op0, target, 1); - if (target == 0) - abort (); - return target; - - default: - abort (); - } -} - -/* Expand code for a post- or pre- increment or decrement - and return the RTX for the result. - POST is 1 for postinc/decrements and 0 for preinc/decrements. */ - -static rtx -expand_increment (exp, post) - register tree exp; - int post; -{ - register rtx op0, op1; - register rtx temp; - register tree incremented = TREE_OPERAND (exp, 0); - optab this_optab = add_optab; - int icode; - enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp)); - int op0_is_copy = 0; - - /* Stabilize any component ref that might need to be - evaluated more than once below. */ - if (TREE_CODE (incremented) == COMPONENT_REF - && (TREE_CODE (TREE_OPERAND (incremented, 0)) != INDIRECT_REF - || DECL_MODE (TREE_OPERAND (incremented, 1)) == BImode)) - incremented = stabilize_reference (incremented); - - /* Compute the operands as RTX. - Note whether OP0 is the actual lvalue or a copy of it: - I believe it is a copy iff it is a register and insns were - generated in computing it. */ - temp = get_last_insn (); - op0 = expand_expr (incremented, 0, VOIDmode, 0); - if (temp != get_last_insn ()) - op0_is_copy = (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG); - op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0); - - /* Decide whether incrementing or decrementing. */ - if (TREE_CODE (exp) == POSTDECREMENT_EXPR - || TREE_CODE (exp) == PREDECREMENT_EXPR) - this_optab = sub_optab; - - /* If OP0 is not the actual lvalue, but rather a copy in a register, - then we cannot just increment OP0. We must - therefore contrive to increment the original value. - Then we can return OP0 since it is a copy of the old value. */ - if (op0_is_copy) - { - /* This is the easiest way to increment the value wherever it is. - Problems with multiple evaluation of INCREMENTED - are prevented because either (1) it is a component_ref, - in which case it was stabilized above, or (2) it is an array_ref - with constant index in an array in a register, which is - safe to reevaluate. */ - tree newexp = build ((this_optab == add_optab - ? PLUS_EXPR : MINUS_EXPR), - TREE_TYPE (exp), - incremented, - TREE_OPERAND (exp, 1)); - temp = expand_assignment (incremented, newexp, ! post, 0); - return post ? op0 : temp; - } - - /* Convert decrement by a constant into a negative increment. */ - if (this_optab == sub_optab - && GET_CODE (op1) == CONST_INT) - { - op1 = gen_rtx (CONST_INT, VOIDmode, - INTVAL (op1)); - this_optab = add_optab; - } - - if (post) - { - /* We have a true reference to the value in OP0. - If there is an insn to add or subtract in this mode, queue it. */ - - /* I'm not sure this is still necessary. */ - op0 = stabilize (op0); - - icode = (int) this_optab->handlers[(int) mode].insn_code; - if (icode != (int) CODE_FOR_nothing - /* Make sure that OP0 is valid for operands 0 and 1 - of the insn we want to queue. */ - && (*insn_operand_predicate[icode][0]) (op0, mode) - && (*insn_operand_predicate[icode][1]) (op0, mode)) - { - if (! (*insn_operand_predicate[icode][2]) (op1, mode)) - op1 = force_reg (mode, op1); - - return enqueue_insn (op0, GEN_FCN (icode) (op0, op0, op1)); - } - } - - /* Preincrement, or we can't increment with one simple insn. */ - if (post) - /* Save a copy of the value before inc or dec, to return it later. */ - temp = copy_to_reg (op0); - else - /* Arrange to return the incremented value. */ - /* Copy the rtx because expand_binop will protect from the queue, - and the results of that would be invalid for us to return - if our caller does emit_queue before using our result. */ - temp = copy_rtx (op0); - - /* Increment however we can. */ - op1 = expand_binop (mode, this_optab, op0, op1, op0, - TREE_UNSIGNED (TREE_TYPE (exp)), OPTAB_LIB_WIDEN); - /* Make sure the value is stored into OP0. */ - if (op1 != op0) - emit_move_insn (op0, op1); - - return temp; -} - -/* Expand all function calls contained within EXP, innermost ones first. - But don't look within expressions that have sequence points. - For each CALL_EXPR, record the rtx for its value - in the CALL_EXPR_RTL field. - - Calls that return large structures for which a structure return - stack slot is needed are not preexpanded. Preexpanding them loses - because if more than one were preexpanded they would try to use the - same stack slot. */ - -static void -preexpand_calls (exp) - tree exp; -{ - register int nops, i; - - if (! do_preexpand_calls) - return; - - /* Only expressions and references can contain calls. */ - - if (tree_code_type[(int) TREE_CODE (exp)][0] != 'e' - && tree_code_type[(int) TREE_CODE (exp)][0] != 'r') - return; - - switch (TREE_CODE (exp)) - { - case CALL_EXPR: - /* Do nothing to built-in functions. */ - if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) == FUNCTION_DECL - && (DECL_FUNCTION_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) - != NOT_BUILT_IN)) - return; - /* Precompute calls that don't return values in memory. */ - if (CALL_EXPR_RTL (exp) == 0 - && TYPE_MODE (TREE_TYPE (exp)) != BLKmode - && ! RETURN_IN_MEMORY (TREE_TYPE (exp))) - CALL_EXPR_RTL (exp) = expand_call (exp, 0, 0); - return; - - case COMPOUND_EXPR: - case COND_EXPR: - case TRUTH_ANDIF_EXPR: - case TRUTH_ORIF_EXPR: - /* If we find one of these, then we can be sure - the adjust will be done for it (since it makes jumps). - Do it now, so that if this is inside an argument - of a function, we don't get the stack adjustment - after some other args have already been pushed. */ - do_pending_stack_adjust (); - return; - - case RTL_EXPR: - return; - - case SAVE_EXPR: - if (SAVE_EXPR_RTL (exp) != 0) - return; - } - - nops = tree_code_length[(int) TREE_CODE (exp)]; - for (i = 0; i < nops; i++) - if (TREE_OPERAND (exp, i) != 0) - { - register int type = *tree_code_type[(int) TREE_CODE (TREE_OPERAND (exp, i))]; - if (type == 'e' || type == 'r') - preexpand_calls (TREE_OPERAND (exp, i)); - } -} - -/* Force FUNEXP into a form suitable for the address of a CALL, - and return that as an rtx. Also load the static chain register - from either FUNEXP or CONTEXT. */ - -static rtx -prepare_call_address (funexp, context) - rtx funexp; - rtx context; -{ - funexp = protect_from_queue (funexp, 0); - if (context != 0) - context = protect_from_queue (context, 0); - - /* Function variable in language with nested functions. */ - if (GET_MODE (funexp) == EPmode) - { - emit_move_insn (static_chain_rtx, gen_highpart (Pmode, funexp)); - funexp = memory_address (FUNCTION_MODE, gen_lowpart (Pmode, funexp)); - emit_insn (gen_rtx (USE, VOIDmode, static_chain_rtx)); - } - else - { - if (context != 0) - /* Unless function variable in C, or top level function constant */ - emit_move_insn (static_chain_rtx, lookup_static_chain (context)); - - /* Make a valid memory address and copy constants thru pseudo-regs, - but not for a constant address if -fno-function-cse. */ - if (GET_CODE (funexp) != SYMBOL_REF) - funexp = memory_address (FUNCTION_MODE, funexp); - else - { -#ifndef NO_FUNCTION_CSE - if (optimize && ! flag_no_function_cse) - funexp = force_reg (Pmode, funexp); -#endif - } - - if (context != 0) - emit_insn (gen_rtx (USE, VOIDmode, static_chain_rtx)); - } - return funexp; -} - -/* Generate instructions to call function FUNEXP, - and optionally pop the results. - The CALL_INSN is the first insn generated. - - FUNTYPE is the data type of the function, or, for a library call, - the identifier for the name of the call. This is given to the - macro RETURN_POPS_ARGS to determine whether this function pops its own args. - - STACK_SIZE is the number of bytes of arguments on the stack, - rounded up to STACK_BOUNDARY; zero if the size is variable. - This is both to put into the call insn and - to generate explicit popping code if necessary. - - NEXT_ARG_REG is the rtx that results from executing - FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1) - just after all the args have had their registers assigned. - This could be whatever you like, but normally it is the first - arg-register beyond those used for args in this call, - or 0 if all the arg-registers are used in this call. - It is passed on to `gen_call' so you can put this info in the call insn. - - VALREG is a hard register in which a value is returned, - or 0 if the call does not return a value. - - OLD_INHIBIT_DEFER_POP is the value that `inhibit_defer_pop' had before - the args to this call were processed. - We restore `inhibit_defer_pop' to that value. - - USE_INSNS is a SEQUENCE of USE insns to be emitted immediately before - the actual CALL insn. */ - -static void -emit_call_1 (funexp, funtype, stack_size, next_arg_reg, valreg, old_inhibit_defer_pop, use_insns) - rtx funexp; - tree funtype; - int stack_size; - rtx next_arg_reg; - rtx valreg; - int old_inhibit_defer_pop; - rtx use_insns; -{ - rtx stack_size_rtx = gen_rtx (CONST_INT, VOIDmode, stack_size); - rtx call_insn; - - if (valreg) - emit_call_insn (gen_call_value (valreg, - gen_rtx (MEM, FUNCTION_MODE, funexp), - stack_size_rtx, next_arg_reg)); - else - emit_call_insn (gen_call (gen_rtx (MEM, FUNCTION_MODE, funexp), - stack_size_rtx, next_arg_reg)); - - /* Find the CALL insn we just emitted and write the USE insns before it. */ - for (call_insn = get_last_insn(); - call_insn && GET_CODE (call_insn) != CALL_INSN; - call_insn = PREV_INSN (call_insn)) - ; - - if (! call_insn) - abort (); - - /* Put the USE insns before the CALL. */ - emit_insn_before (use_insns, call_insn); - - inhibit_defer_pop = old_inhibit_defer_pop; - - /* If returning from the subroutine does not automatically pop the args, - we need an instruction to pop them sooner or later. - Perhaps do it now; perhaps just record how much space to pop later. */ - - if (! RETURN_POPS_ARGS (funtype) - && stack_size != 0) - { - if (flag_defer_pop && inhibit_defer_pop == 0) - pending_stack_adjust += stack_size; - else - adjust_stack (stack_size_rtx); - } -} - -/* At the start of a function, record that we have no previously-pushed - arguments waiting to be popped. */ - -void -init_pending_stack_adjust () -{ - pending_stack_adjust = 0; -} - -/* When exiting from function, if safe, clear out any pending stack adjust - so the adjustment won't get done. */ - -void -clear_pending_stack_adjust () -{ -#ifdef EXIT_IGNORE_STACK - if (!flag_omit_frame_pointer && EXIT_IGNORE_STACK - && ! TREE_INLINE (current_function_decl) - && ! flag_inline_functions) - pending_stack_adjust = 0; -#endif -} - -/* Pop any previously-pushed arguments that have not been popped yet. */ - -void -do_pending_stack_adjust () -{ - if (inhibit_defer_pop == 0) - { - if (pending_stack_adjust != 0) - adjust_stack (gen_rtx (CONST_INT, VOIDmode, pending_stack_adjust)); - pending_stack_adjust = 0; - } -} - -/* Data structure and subroutines used within expand_call. */ - -struct arg_data -{ - /* Tree node for this argument. */ - tree tree_value; - /* Precomputed RTL value, or 0 if it isn't precomputed. */ - rtx value; - /* Register to pass this argument in, or 0 if passed on stack. */ - rtx reg; - /* Number of registers to use. 0 means put the whole arg in registers. - Also 0 if not passed in registers. */ - int partial; - /* Offset of this argument from beginning of stack-args. */ - struct args_size offset; - /* Size of this argument on the stack, rounded up for any padding it gets, - parts of the argument passed in registers do not count. - If the FIRST_PARM_CALLER_OFFSET is negative, then register parms - are counted here as well. */ - struct args_size size; - /* Nonzero if this arg has already been stored. */ - int stored; - /* const0_rtx means should preallocate stack space for this arg. - Other non0 value is the stack slot, preallocated. - Used only for BLKmode. */ - rtx stack; -}; - -static void store_one_arg (); -static rtx target_for_arg (); - -/* Generate all the code for a function call - and return an rtx for its value. - Store the value in TARGET (specified as an rtx) if convenient. - If the value is stored in TARGET then TARGET is returned. - If IGNORE is nonzero, then we ignore the value of the function call. */ - -static rtx -expand_call (exp, target, ignore) - tree exp; - rtx target; - int ignore; -{ - /* List of actual parameters. */ - tree actparms = TREE_OPERAND (exp, 1); - /* RTX for the function to be called. */ - rtx funexp; - /* Data type of the function. */ - tree funtype; - /* Declaration of the function being called, - or 0 if the function is computed (not known by name). */ - tree fndecl = 0; - - /* Register in which non-BLKmode value will be returned, - or 0 if no value or if value is BLKmode. */ - rtx valreg; - /* Address where we should return a BLKmode value; - 0 if value not BLKmode. */ - rtx structure_value_addr = 0; - /* Nonzero if that address is being passed by treating it as - an extra, implicit first parameter. Otherwise, - it is passed by being copied directly into struct_value_rtx. */ - int structure_value_addr_parm = 0; - /* Save get_structure_value_addr data to prevent multiple use. */ - rtx saved_structure_value_addr; - int saved_structure_value_size; - /* Nonzero if called function returns an aggregate in memory PCC style, - by returning the address of where to find it. */ - int pcc_struct_value = 0; - /* Insn that was used to copy the result to the specified target, - or 0 if no such insn. */ - rtx result_copy_insn = 0; - - /* Number of actual parameters in this call, including struct value addr. */ - int num_actuals; - /* Number of named args. Args after this are anonymous ones - and they must all go on the stack. */ - int n_named_args; - /* Count arg position in order args appear. */ - int argpos; - - /* Vector of information about each argument. - Arguments are numbered in the order they will be pushed, - not the order they are written. */ - struct arg_data *args; - - /* Total size in bytes of all the stack-parms scanned so far. */ - struct args_size args_size; - /* Remember initial value of args_size.constant. */ - int starting_args_size; - /* Nonzero means count reg-parms' size in ARGS_SIZE. */ - int stack_count_regparms = 0; - /* Data on reg parms scanned so far. */ - CUMULATIVE_ARGS args_so_far; - /* Nonzero if a reg parm has been scanned. */ - int reg_parm_seen; - /* Nonzero if we must avoid push-insns in the args for this call. */ - int must_preallocate; - /* 1 if scanning parms front to back, -1 if scanning back to front. */ - int inc; - /* Address of space preallocated for stack parms - (on machines that lack push insns), or 0 if space not preallocated. */ - rtx argblock = 0; - /* Amount to align the stack by before or after we push any args. */ - int stack_align = 0; - - /* Nonzero if it is plausible that this is a call to alloca. */ - int may_be_alloca; - /* Nonzero if this is a call to setjmp or a related function. */ - int is_setjmp; - /* Nonzero if this is a call to an inline function. */ - int is_integrable = 0; - /* Nonzero if this is a call to __builtin_new. */ - int is_builtin_new; - /* Nonzero if this is a call to a `const' function. */ - int is_const = 0; - - /* Nonzero if there are BLKmode args whose data types require them - to be passed in memory, not (even partially) in registers. */ - int BLKmode_parms_forced = 0; - /* The offset of the first BLKmode parameter which - *must* be passed in memory. */ - int BLKmode_parms_first_offset = 0; - /* Total size of BLKmode parms which could usefully be preallocated. */ - int BLKmode_parms_sizes = 0; - - /* Amount stack was adjusted to protect BLKmode parameters - which are below the nominal "stack address" value. */ - rtx protected_stack = 0; - - /* The last insn before the things that are intrinsically part of the call. - The beginning reg-note goes on the insn after this one. */ - rtx insn_before; - - rtx old_stack_level = 0; - int old_pending_adj; - int old_inhibit_defer_pop = inhibit_defer_pop; - tree old_cleanups = cleanups_of_this_call; - rtx use_insns; - - register tree p; - register int i; - - /* See if we can find a DECL-node for the actual function. - As a result, decide whether this is a call to an integrable function. */ - - p = TREE_OPERAND (exp, 0); - if (TREE_CODE (p) == ADDR_EXPR) - { - fndecl = TREE_OPERAND (p, 0); - if (TREE_CODE (fndecl) != FUNCTION_DECL) - fndecl = 0; - else - { - extern tree current_function_decl; - - if (fndecl != current_function_decl - && DECL_SAVED_INSNS (fndecl)) - is_integrable = 1; - else - { - /* In case this function later becomes inlineable, - record that there was already a non-inline call to it. */ - mark_addressable (fndecl); - } - - if (TREE_READONLY (fndecl) && ! TREE_THIS_VOLATILE (fndecl)) - is_const = 1; - } - } - - /* When calling a const function, we must pop the stack args right away, - so that the pop is deleted or moved with the call. */ - if (is_const) - NO_DEFER_POP; - - /* Set up a place to return a structure. */ - - /* Cater to broken compilers. */ - if (aggregate_value_p (exp)) - { - /* This call returns a big structure. */ -#ifdef PCC_STATIC_STRUCT_RETURN - if (flag_pcc_struct_return) - { - pcc_struct_value = 1; - is_integrable = 0; /* Easier than making that case work right. */ - } - else -#endif - { - if (target && GET_CODE (target) == MEM) - { - structure_value_addr = XEXP (target, 0); - if (reg_mentioned_p (stack_pointer_rtx, structure_value_addr)) - structure_value_addr = copy_to_reg (structure_value_addr); - } - else - { - push_structure_value (&saved_structure_value_addr, - &saved_structure_value_size); - /* Make room on the stack to hold the value. */ - structure_value_addr - = get_structure_value_addr (expr_size (exp)); - target = 0; - } - } - } - - /* If called function is inline, try to integrate it. */ - - if (is_integrable) - { - extern rtx expand_inline_function (); - rtx temp; - - temp = expand_inline_function (fndecl, actparms, target, - ignore, TREE_TYPE (exp), - structure_value_addr); - - /* If inlining succeeded, return. */ - if ((int) temp != -1) - return temp; - - /* If inlining failed, mark FNDECL as needing to be compiled - separately after all. */ - TREE_ADDRESSABLE (fndecl) = 1; - TREE_ADDRESSABLE (DECL_NAME (fndecl)) = 1; - } - -#if 0 - /* Unless it's a call to a specific function that isn't alloca, - if it has one argument, we must assume it might be alloca. */ - - may_be_alloca = - (!(fndecl != 0 - && strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), - "alloca")) - && actparms != 0 - && TREE_CHAIN (actparms) == 0); -#else - /* We assume that alloca will always be called by name. It - makes no sense to pass it as a pointer-to-function to - anything that does not understand its behavior. */ - may_be_alloca = - (fndecl && (! strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "alloca") - || ! strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), - "__builtin_alloca"))); -#endif - - /* See if this is a call to a function that can return more than once. */ - - is_setjmp - = (fndecl != 0 - && (!strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "setjmp") - || !strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "_setjmp"))); - - is_builtin_new - = (fndecl != 0 - && (!strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "__builtin_new"))); - - if (may_be_alloca) - { - frame_pointer_needed = 1; - may_call_alloca = 1; - current_function_calls_alloca = 1; - } - - /* Don't let pending stack adjusts add up to too much. - Also, do all pending adjustments now - if there is any chance this might be a call to alloca - or if it is const. */ - - if (pending_stack_adjust >= 32 || is_const - || (pending_stack_adjust > 0 && may_be_alloca)) - do_pending_stack_adjust (); - - /* Operand 0 is a pointer-to-function; get the type of the function. */ - funtype = TREE_TYPE (TREE_OPERAND (exp, 0)); - if (TREE_CODE (funtype) != POINTER_TYPE) - abort (); - funtype = TREE_TYPE (funtype); - - /* If structure_value_addr is set, it means pass the address - as if it were an extra parameter. We typically avoid doing - so here, which would imply that the caller has to pop it off - the stack; but some compilers do expect caller pop. */ - if (structure_value_addr -#ifdef STRUCT_RETURN_CALLER_POP - && flag_pcc_struct_return -#else - && struct_value_rtx == 0 -#endif - ) - { - rtx tem; - - INIT_CUMULATIVE_ARGS (args_so_far, funtype); - tem = FUNCTION_ARG (args_so_far, Pmode, - build_pointer_type (TREE_TYPE (funtype)), 1); - if (tem == 0) - { - actparms = tree_cons (error_mark_node, - build (SAVE_EXPR, - type_for_size (GET_MODE_BITSIZE (Pmode), 0), - 0, - force_reg (Pmode, structure_value_addr)), - actparms); - structure_value_addr_parm = 1; - } -#ifdef STRUCT_RETURN_CALLER_POP - /* Moved in 1.39 from before the preceding open-brace. - Should be safe without the conditional because, - if STRUCT_RETURN_CALLER_POP is not defined, - this can still happen only if struct_value_rtx is 0, - and in that case, we would crash anyway if this weren't done. */ - structure_value_addr_parm = 1; -#endif - } - - /* Count the arguments and set NUM_ACTUALS. */ - for (p = actparms, i = 0; p; p = TREE_CHAIN (p)) i++; - num_actuals = i; - - /* Compute number of named args. - Don't include the last named arg if anonymous args follow. - (If no anonymous args follow, the result of list_length - is actually one too large.) */ - if (TYPE_ARG_TYPES (funtype) != 0) - n_named_args = list_length (TYPE_ARG_TYPES (funtype)) - 1; - else - /* If we know nothing, treat all args as named. */ - n_named_args = num_actuals; - - /* Make a vector to hold all the information about each arg. */ - args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data)); - bzero (args, num_actuals * sizeof (struct arg_data)); - - args_size.constant = 0; - args_size.var = 0; -#ifdef FIRST_PARM_CALLER_OFFSET - args_size.constant = FIRST_PARM_CALLER_OFFSET (funtype); - stack_count_regparms = 1; -#endif - starting_args_size = args_size.constant; - - /* In this loop, we consider args in the order they are written. - We fill up ARGS from the front of from the back if necessary - so that in any case the first arg to be pushed ends up at the front. */ - -#ifdef PUSH_ARGS_REVERSED - i = num_actuals - 1, inc = -1; - /* In this case, must reverse order of args - so that we compute and push the last arg first. */ -#else - i = 0, inc = 1; -#endif - - INIT_CUMULATIVE_ARGS (args_so_far, funtype); - - /* I counts args in order (to be) pushed; ARGPOS counts in order written. */ - for (p = actparms, argpos = 0; p; p = TREE_CHAIN (p), i += inc, argpos++) - { - tree type = TREE_TYPE (TREE_VALUE (p)); - args[i].tree_value = TREE_VALUE (p); - args[i].offset = args_size; - - if (type == error_mark_node - || TYPE_SIZE (type) == 0) - continue; - - /* Decide where to pass this arg. */ - /* args[i].reg is nonzero if all or part is passed in registers. - args[i].partial is nonzero if part but not all is passed in registers, - and the exact value says how many words are passed in registers. */ - - if (TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST - && args_size.var == 0 - /* error_mark_node here is a flag for the fake argument - for a structure value address. */ - && TREE_PURPOSE (p) != error_mark_node) - { - args[i].reg = FUNCTION_ARG (args_so_far, TYPE_MODE (type), type, - argpos < n_named_args); - /* If this argument needs more than the usual parm alignment, do - extrinsic padding to reach that alignment. */ - -#ifdef MAX_PARM_BOUNDARY - /* If MAX_PARM_BOUNDARY is not defined, it means that the usual - alignment requirements are relaxed for parms, and that no parm - needs more than PARM_BOUNDARY, regardless of data type. */ - - if (PARM_BOUNDARY < TYPE_ALIGN (type)) - { - int boundary = PARM_BOUNDARY; - - /* Determine the boundary to pad up to. */ - if (TYPE_ALIGN (type) > boundary) - boundary = TYPE_ALIGN (type); - if (boundary > MAX_PARM_BOUNDARY) - boundary = MAX_PARM_BOUNDARY; - - /* If the previous args don't reach such a boundary, - advance to the next one. */ - boundary /= BITS_PER_UNIT; - args[i].offset.constant += boundary - 1; - args[i].offset.constant &= ~(boundary - 1); - args_size.constant += boundary - 1; - args_size.constant &= ~(boundary - 1); - - if (args_size.var != 0) - abort (); /* This case not implemented yet */ - } -#endif /* MAX_PARM_BOUNDARY */ - -#ifdef FUNCTION_ARG_PARTIAL_NREGS - args[i].partial - = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, - TYPE_MODE (type), type, - i < n_named_args); -#endif - } - - /* Compute the stack-size of this argument. */ - - if (args[i].reg != 0 && args[i].partial == 0 - && ! stack_count_regparms) - /* On most machines, don't count stack space for a register arg. */ - ; - else if (TYPE_MODE (type) != BLKmode) - { - register int size; - - /* If we are counting "up to zero" and find a stack parm - before we reach zero, skip up to zero. - Negative offsets correspond to registers. */ - if (stack_count_regparms && args_size.constant < 0 - /* This used to check args[i].partial != 0, - but on the Sparc now that seems to be 0. */ - && args[i].reg == 0) - { - args_size.constant = 0; - args[i].offset.constant = 0; - } - size = GET_MODE_SIZE (TYPE_MODE (type)); - /* Compute how much space the push instruction will push. - On many machines, pushing a byte will advance the stack - pointer by a halfword. */ -#ifdef PUSH_ROUNDING - size = PUSH_ROUNDING (size); -#endif - /* Compute how much space the argument should get: - maybe pad to a multiple of the alignment for arguments. */ - if (none == FUNCTION_ARG_PADDING (TYPE_MODE (type), const0_rtx)) - args[i].size.constant = size; - else - args[i].size.constant - = (((size + PARM_BOUNDARY / BITS_PER_UNIT - 1) - / (PARM_BOUNDARY / BITS_PER_UNIT)) - * (PARM_BOUNDARY / BITS_PER_UNIT)); - } - else - { - register tree size = size_in_bytes (type); - - /* If we are counting "up to zero" and find a stack parm - before we reach zero, skip up to zero. - Negative offsets correspond to registers. */ - if (stack_count_regparms && args_size.constant < 0 - /* This used to check args[i].partial != 0, - but on the Sparc now that seems to be 0. */ - && args[i].reg == 0) - { - args_size.constant = 0; - args[i].offset.constant = 0; - } - - /* A nonscalar. Round its size up to a multiple - of PARM_BOUNDARY bits, unless it is not supposed to be padded. */ - if (none - != FUNCTION_ARG_PADDING (TYPE_MODE (type), - expand_expr (size, 0, VOIDmode, 0))) - size = convert_units (convert_units (size, BITS_PER_UNIT, - PARM_BOUNDARY), - PARM_BOUNDARY, BITS_PER_UNIT); - ADD_PARM_SIZE (args[i].size, size); - - /* Certain data types may not be passed in registers - (eg C++ classes with constructors). - Also, BLKmode parameters initialized from CALL_EXPRs - are treated specially, if it is a win to do so. */ - if (TREE_CODE (TREE_VALUE (p)) == CALL_EXPR - || TREE_ADDRESSABLE (type)) - { - if (TREE_ADDRESSABLE (type)) - BLKmode_parms_forced = 1; - /* This is a marker for such a parameter. */ - args[i].stack = const0_rtx; - BLKmode_parms_sizes += TREE_INT_CST_LOW (size); - - /* If this parm's location is "below" the nominal stack pointer, - note to decrement the stack pointer while it is computed. */ -#ifdef FIRST_PARM_CALLER_OFFSET - if (BLKmode_parms_first_offset == 0) - BLKmode_parms_first_offset - /* If parameter's offset is variable, assume the worst. */ - = (args[i].offset.var - ? FIRST_PARM_CALLER_OFFSET (funtype) - : args[i].offset.constant); -#endif - } - } - - /* If a part of the arg was put into registers, - don't include that part in the amount pushed. */ - if (! stack_count_regparms) - args[i].size.constant - -= ((args[i].partial * UNITS_PER_WORD) - / (PARM_BOUNDARY / BITS_PER_UNIT) - * (PARM_BOUNDARY / BITS_PER_UNIT)); - - /* Update ARGS_SIZE, the total stack space for args so far. */ - - args_size.constant += args[i].size.constant; - if (args[i].size.var) - { - ADD_PARM_SIZE (args_size, args[i].size.var); - } - - /* Increment ARGS_SO_FAR, which has info about which arg-registers - have been used, etc. */ - - FUNCTION_ARG_ADVANCE (args_so_far, TYPE_MODE (type), type, - i < n_named_args); - } - - /* If we would have to push a partially-in-regs parm - before other stack parms, preallocate stack space instead. */ - must_preallocate = 0; - { - int partial_seen = 0; - for (i = 0; i < num_actuals; i++) - { - if (args[i].partial > 0) - partial_seen = 1; - else if (partial_seen && args[i].reg == 0) - must_preallocate = 1; - } - } - - /* Precompute all register parameters. It isn't safe to compute anything - once we have started filling any specific hard regs. - If this function call is cse'able, precompute all the parameters. */ - - reg_parm_seen = 0; - for (i = 0; i < num_actuals; i++) - if (args[i].reg != 0 || is_const) - { - int j; - int struct_value_lossage = 0; - - /* First, see if this is a precomputed struct-returning function call - and other subsequent parms are also such. */ - if ((TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode - || RETURN_IN_MEMORY (TREE_TYPE (args[i].tree_value))) - && TREE_CODE (args[i].tree_value) == CALL_EXPR) - for (j = i + 1; j < num_actuals; j++) - if ((TYPE_MODE (TREE_TYPE (args[j].tree_value)) == BLKmode - || RETURN_IN_MEMORY (TREE_TYPE (args[j].tree_value))) - && TREE_CODE (args[j].tree_value) == CALL_EXPR - && args[j].reg != 0 || is_const) - { - /* We have two precomputed structure-values call expressions - in our parm list. Both of them would normally use - the structure-value block. To avoid the conflict, - compute this parm with a different temporary block. */ - int size = int_size_in_bytes (TREE_TYPE (args[i].tree_value)); - rtx structval = assign_stack_local (BLKmode, size); - args[i].value = expand_expr (args[i].tree_value, structval, - VOIDmode, 0); - struct_value_lossage = 1; - break; - } - if (!struct_value_lossage) - args[i].value = expand_expr (args[i].tree_value, 0, VOIDmode, 0); - - if (args[i].reg != 0) - reg_parm_seen = 1; - - if (GET_CODE (args[i].value) != MEM - && ! CONSTANT_P (args[i].value) - && GET_CODE (args[i].value) != CONST_DOUBLE) - args[i].value - = force_reg (TYPE_MODE (TREE_TYPE (args[i].tree_value)), - args[i].value); - /* ANSI doesn't require a sequence point here, - but PCC has one, so this will avoid some problems. */ - emit_queue (); - } - - /* Get the function to call, in the form of RTL, if it is a constant. */ - if (fndecl && is_const) - { - /* Get a SYMBOL_REF rtx for the function address. */ - funexp = XEXP (DECL_RTL (fndecl), 0); - -#ifndef NO_FUNCTION_CSE - /* Pass the address through a pseudoreg, if desired, - before the "beginning" of the library call. - So this insn isn't "part of" the library call, in case that - is deleted, or cse'd. */ - if (! flag_no_function_cse) - funexp = copy_to_mode_reg (Pmode, funexp); -#endif - } - - /* Now we are about to start emitting insns that can be deleted - if the libcall is deleted. */ - insn_before = get_last_insn (); - - /* Maybe do additional rounding on the size of the arguments. */ -#ifdef STACK_ARGS_ADJUST - STACK_ARGS_ADJUST (args_size); -#endif - - /* If we have no actual push instructions, or shouldn't use them, - or we need a variable amount of space, make space for all args right now. - Round the needed size up to multiple of STACK_BOUNDARY. */ - - if (args_size.var != 0) - { - old_stack_level = copy_to_mode_reg (Pmode, stack_pointer_rtx); - old_pending_adj = pending_stack_adjust; - argblock = push_block (round_push (ARGS_SIZE_RTX (args_size)), 0); - } - else if (args_size.constant > 0) - { - int needed = args_size.constant; - -#ifdef STACK_BOUNDARY - needed = (needed + STACK_BYTES - 1) / STACK_BYTES * STACK_BYTES; - stack_align = needed - args_size.constant; -#endif - args_size.constant = needed; - - if ( -#ifndef PUSH_ROUNDING - 1 /* Always preallocate if no push insns. */ -#else - must_preallocate || BLKmode_parms_forced - || BLKmode_parms_sizes > (args_size.constant >> 1) -#endif - ) - { - if (inhibit_defer_pop == 0) - { - /* Try to reuse some or all of the pending_stack_adjust - to get this space. Maybe we can avoid any pushing. */ - if (needed > pending_stack_adjust) - { - needed -= pending_stack_adjust; - pending_stack_adjust = 0; - } - else - { - pending_stack_adjust -= needed; - needed = 0; - } - } - argblock = push_block (gen_rtx (CONST_INT, VOIDmode, needed), 0); - } - } -#ifndef PUSH_ROUNDING - else if (BLKmode_parms_forced) - { - /* If we have reg-parms that need to be temporarily on the stack, - set up an arg block address even though there is no space - to be allocated for it. */ - argblock = push_block (const0_rtx, 0); - } -#endif - -#if 0 - /* If stack needs padding below the args, increase all arg offsets - so the args are stored above the padding. */ - if (stack_padding) - for (i = 0; i < num_actuals; i++) - args[i].offset.constant += stack_padding; -#endif - - /* Don't try to defer pops if preallocating, not even from the first arg, - since ARGBLOCK probably refers to the SP. */ - if (argblock) - NO_DEFER_POP; - -#ifdef STACK_GROWS_DOWNWARD - /* If any BLKmode parms need to be preallocated in space - below the nominal stack-pointer address, we need to adjust the - stack pointer so that this location is temporarily above it. - This ensures that computation won't clobber that space. */ - if (BLKmode_parms_first_offset < 0 && argblock != 0) - { - int needed = -BLKmode_parms_first_offset; - argblock = copy_to_reg (argblock); - -#ifdef STACK_BOUNDARY - needed = (needed + STACK_BYTES - 1) / STACK_BYTES * STACK_BYTES; -#endif - protected_stack = gen_rtx (CONST_INT, VOIDmode, needed); - anti_adjust_stack (protected_stack); - } -#endif /* STACK_GROWS_DOWNWARD */ - -#ifdef PUSH_ARGS_REVERSED -#ifdef STACK_BOUNDARY - /* If we push args individually in reverse order, perform stack alignment - before the first push (the last arg). */ - if (argblock == 0 && stack_align > 0) - anti_adjust_stack (gen_rtx (CONST_INT, VOIDmode, stack_align)); -#endif -#endif - - /* Get the function to call, in the form of RTL. */ - if (fndecl) - /* Get a SYMBOL_REF rtx for the function address. */ - funexp = XEXP (DECL_RTL (fndecl), 0); - else - /* Generate an rtx (probably a pseudo-register) for the address. */ - { - funexp = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0); - emit_queue (); - } - - /* Now compute and store all non-register parms. - These come before register parms, since they can require block-moves, - which could clobber the registers used for register parms. - Parms which have partial registers are not stored here, - but we do preallocate space here if they want that. */ - - for (i = 0; i < num_actuals; i++) - { - /* Preallocate the stack space for a parm if appropriate - so it can be computed directly in the stack space. */ - if (args[i].stack != 0 && argblock != 0) - args[i].stack = target_for_arg (TREE_TYPE (args[i].tree_value), - ARGS_SIZE_RTX (args[i].size), - argblock, args[i].offset); - else - args[i].stack = 0; - - if (args[i].reg == 0 - && TYPE_SIZE (TREE_TYPE (args[i].tree_value)) != 0) - store_one_arg (&args[i], argblock, may_be_alloca); - } - - /* Now store any partially-in-registers parm. - This is the last place a block-move can happen. */ - if (reg_parm_seen) - for (i = 0; i < num_actuals; i++) - if (args[i].partial != 0) - store_one_arg (&args[i], argblock, may_be_alloca); - - if (protected_stack != 0) - adjust_stack (protected_stack); - -#ifndef PUSH_ARGS_REVERSED -#ifdef STACK_BOUNDARY - /* If we pushed args in forward order, perform stack alignment - after pushing the last arg. */ - if (argblock == 0 && stack_align > 0) - anti_adjust_stack (gen_rtx (CONST_INT, VOIDmode, stack_align)); -#endif -#endif - - /* If the function will be returning a structure, and if the address in - which to return the value isn't being passed as a parameter, pass it - now. This may result in a register move or in a push; if it's a push, - we count on the called routine to pop it. */ - if (structure_value_addr && ! structure_value_addr_parm) - emit_move_insn (struct_value_rtx, - force_reg (Pmode, force_operand (structure_value_addr, 0))); - - /* Now set up any wholly-register parms. They were computed already. */ - if (reg_parm_seen) - for (i = 0; i < num_actuals; i++) - if (args[i].reg != 0 && args[i].partial == 0) - store_one_arg (&args[i], argblock, may_be_alloca); - - /* Perform postincrements before actually calling the function. */ - emit_queue (); - - /* All arguments and registers used for the call must be set up by now! */ - - /* ??? Other languages need a nontrivial second argument (static chain). */ - funexp = prepare_call_address (funexp, 0); - - /* Mark all register-parms as living through the call. */ - start_sequence (); - for (i = 0; i < num_actuals; i++) - if (args[i].reg != 0) - { - if (args[i].partial > 0) - use_regs (REGNO (args[i].reg), args[i].partial); - else if (GET_MODE (args[i].reg) == BLKmode) - use_regs (REGNO (args[i].reg), - ((int_size_in_bytes (TREE_TYPE (args[i].tree_value)) - + UNITS_PER_WORD - 1) - / UNITS_PER_WORD)); - else - emit_insn (gen_rtx (USE, VOIDmode, args[i].reg)); - } - - if (structure_value_addr && ! structure_value_addr_parm - && GET_CODE (struct_value_rtx) == REG) - emit_insn (gen_rtx (USE, VOIDmode, struct_value_rtx)); - - use_insns = gen_sequence (); - end_sequence (); - - /* Figure out the register where the value, if any, will come back. */ - valreg = 0; - if (TYPE_MODE (TREE_TYPE (exp)) != VOIDmode - && ! structure_value_addr) - { - if (pcc_struct_value) - valreg = hard_libcall_value (Pmode); - else - valreg = hard_function_value (TREE_TYPE (exp), fndecl); - } - - /* Generate the actual call instruction. */ - /* This also has the effect of turning off any pop-inhibition - done in expand_call. */ - if (args_size.constant < 0) - args_size.constant = 0; - emit_call_1 (funexp, funtype, args_size.constant, - FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1), - valreg, old_inhibit_defer_pop, use_insns); - -/* ??? Nothing has been done here to record control flow - when contained functions can do nonlocal gotos. */ - - /* For calls to `setjmp', etc., inform flow.c it should complain - if nonvolatile values are live. */ - - if (is_setjmp) - { - emit_note (IDENTIFIER_POINTER (DECL_NAME (fndecl)), NOTE_INSN_SETJMP); - current_function_calls_setjmp = 1; - } - - /* Notice functions that cannot return. - If optimizing, insns emitted below will be dead. - If not optimizing, they will exist, which is useful - if the user uses the `return' command in the debugger. */ - - if (fndecl && TREE_THIS_VOLATILE (fndecl)) - emit_barrier (); - - /* If this call is to be cse'd, then make sure it balances the stack. */ - if (is_const) - do_pending_stack_adjust (); - - /* For calls to __builtin_new, note that it can never return 0. - This is because a new handler will be called, and 0 it not - among the numbers it is supposed to return. */ -#if 0 - if (is_builtin_new) - emit_note (IDENTIFIER_POINTER (DECL_NAME (fndecl)), NOTE_INSN_BUILTIN_NEW); -#endif - - /* If value type not void, return an rtx for the value. */ - - /* If there are cleanups to be called, don't use a hard reg as target. */ - if (cleanups_of_this_call != old_cleanups - && target && REG_P (target) - && REGNO (target) < FIRST_PSEUDO_REGISTER) - target = 0; - - result_copy_insn = 0; - - if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode - || ignore) - { - target = const0_rtx; - } - else if (structure_value_addr) - { - if (target == 0 || GET_CODE (target) != MEM) - target = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), - memory_address (BLKmode, structure_value_addr)); - } - else if (pcc_struct_value) - { - valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)), - fndecl); - if (target == 0) - target = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), - copy_to_reg (valreg)); - else if (TYPE_MODE (TREE_TYPE (exp)) != BLKmode) - result_copy_insn - = emit_move_insn (target, gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), - copy_to_reg (valreg))); - else - emit_block_move (target, gen_rtx (MEM, BLKmode, copy_to_reg (valreg)), - expr_size (exp), - TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); - } - else if (target && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp))) - { - if (!rtx_equal_p (target, valreg)) - result_copy_insn = emit_move_insn (target, valreg); - else - /* This tells expand_inline_function to copy valreg to its target. */ - emit_insn (gen_rtx (USE, VOIDmode, valreg)); - } - else - { - target = copy_to_reg (valreg); - result_copy_insn = get_last_insn (); - } - - /* Perform all cleanups needed for the arguments of this call - (i.e. destructors in C++). */ - while (cleanups_of_this_call != old_cleanups) - { - expand_expr (TREE_VALUE (cleanups_of_this_call), 0, VOIDmode, 0); - cleanups_of_this_call = TREE_CHAIN (cleanups_of_this_call); - } - - /* If we pushed this, pop it. */ - if (saved_structure_value_addr != 0) - pop_structure_value (saved_structure_value_addr, - saved_structure_value_size); - - /* If size of args is variable, restore saved stack-pointer value. */ - - if (old_stack_level) - { - emit_move_insn (stack_pointer_rtx, old_stack_level); - pending_stack_adjust = old_pending_adj; - } - - /* If call is cse'able, make appropriate pair of reg-notes around it. */ - if (is_const) - { - rtx insn_first = NEXT_INSN (insn_before); - rtx insn_last = get_last_insn (); - rtx note = 0; - - /* Don't put the notes on if we don't have insns that can hold them. */ - if ((GET_CODE (insn_first) == INSN - || GET_CODE (insn_first) == CALL_INSN - || GET_CODE (insn_first) == JUMP_INSN) - && (GET_CODE (insn_last) == INSN - || GET_CODE (insn_last) == CALL_INSN - || GET_CODE (insn_last) == JUMP_INSN) - && insn_last == result_copy_insn) - { - /* Construct an "equal form" for the value - which mentions all the arguments in order - as well as the function name. */ - for (i = 0; i < num_actuals; i++) - if (args[i].reg != 0 || is_const) - note = gen_rtx (EXPR_LIST, VOIDmode, args[i].value, note); - note = gen_rtx (EXPR_LIST, VOIDmode, - XEXP (DECL_RTL (fndecl), 0), note); - - REG_NOTES (insn_last) - = gen_rtx (EXPR_LIST, REG_EQUAL, note, - gen_rtx (INSN_LIST, REG_RETVAL, insn_first, - REG_NOTES (insn_last))); - REG_NOTES (insn_first) - = gen_rtx (INSN_LIST, REG_LIBCALL, insn_last, - REG_NOTES (insn_first)); - } - } - - return target; -} - -/* Return an rtx which represents a suitable home on the stack - given TYPE, the type of the argument looking for a home. - This is called only for BLKmode arguments. - - SIZE is the size needed for this target. - ARGS_ADDR is the address of the bottom of the argument block for this call. - OFFSET describes this parameter's offset into ARGS_ADDR. It is meaningless - if this machine uses push insns. */ - -static rtx -target_for_arg (type, size, args_addr, offset) - tree type; - rtx size; - rtx args_addr; - struct args_size offset; -{ - rtx target; - rtx offset_rtx = ARGS_SIZE_RTX (offset); - - /* We do not call memory_address if possible, - because we want to address as close to the stack - as possible. For non-variable sized arguments, - this will be stack-pointer relative addressing. */ - if (GET_CODE (offset_rtx) == CONST_INT) - target = plus_constant (args_addr, INTVAL (offset_rtx)); - else - { - /* I have no idea how to guarantee that this - will work in the presence of register parameters. */ - target = gen_rtx (PLUS, Pmode, args_addr, offset_rtx); - target = memory_address (QImode, target); - } - - return gen_rtx (MEM, BLKmode, target); -} - -/* Store a single argument for a function call - into the register or memory area where it must be passed. - *ARG describes the argument value and where to pass it. - ARGBLOCK is the address of the stack-block for all the arguments, - or 0 on a machine where arguemnts are pushed individually. - MAY_BE_ALLOCA nonzero says this could be a call to `alloca' - so must be careful about how the stack is used. */ - -static void -store_one_arg (arg, argblock, may_be_alloca) - struct arg_data *arg; - rtx argblock; - int may_be_alloca; -{ - register tree pval = arg->tree_value; - int used = 0; - - if (TREE_CODE (pval) == ERROR_MARK) - return; - - if (arg->reg != 0 && arg->partial == 0) - { - /* Being passed entirely in a register. */ - if (arg->value != 0) - { - if (GET_MODE (arg->value) == BLKmode) - move_block_to_reg (REGNO (arg->reg), arg->value, - ((int_size_in_bytes (TREE_TYPE (pval)) - + UNITS_PER_WORD - 1) - / UNITS_PER_WORD)); - else - emit_move_insn (arg->reg, arg->value); - } - else - store_expr (pval, arg->reg, 0); - - /* Don't allow anything left on stack from computation - of argument to alloca. */ - if (may_be_alloca) - do_pending_stack_adjust (); - } - else if (TYPE_MODE (TREE_TYPE (pval)) != BLKmode) - { - register int size; - rtx tem; - - /* Argument is a scalar, not entirely passed in registers. - (If part is passed in registers, arg->partial says how much - and emit_push_insn will take care of putting it there.) - - Push it, and if its size is less than the - amount of space allocated to it, - also bump stack pointer by the additional space. - Note that in C the default argument promotions - will prevent such mismatches. */ - - used = size = GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (pval))); - /* Compute how much space the push instruction will push. - On many machines, pushing a byte will advance the stack - pointer by a halfword. */ -#ifdef PUSH_ROUNDING - size = PUSH_ROUNDING (size); -#endif - /* Compute how much space the argument should get: - round up to a multiple of the alignment for arguments. */ - if (none != FUNCTION_ARG_PADDING (TYPE_MODE (TREE_TYPE (pval)), const0_rtx)) - used = (((size + PARM_BOUNDARY / BITS_PER_UNIT - 1) - / (PARM_BOUNDARY / BITS_PER_UNIT)) - * (PARM_BOUNDARY / BITS_PER_UNIT)); - - tem = arg->value; - if (tem == 0) - { - tem = expand_expr (pval, 0, VOIDmode, 0); - /* ANSI doesn't require a sequence point here, - but PCC has one, so this will avoid some problems. */ - emit_queue (); - } - - /* Don't allow anything left on stack from computation - of argument to alloca. */ - if (may_be_alloca) - do_pending_stack_adjust (); - - emit_push_insn (tem, TYPE_MODE (TREE_TYPE (pval)), 0, 0, - arg->partial, arg->reg, used - size, - argblock, ARGS_SIZE_RTX (arg->offset)); - } - else if (arg->stack != 0) - { - /* BLKmode parm, not entirely passed in registers, - and with space already allocated. */ - - tree sizetree = size_in_bytes (TREE_TYPE (pval)); - /* Round the size up to multiple of PARM_BOUNDARY bits. */ - tree s1 = convert_units (sizetree, BITS_PER_UNIT, PARM_BOUNDARY); - tree s2 = convert_units (s1, PARM_BOUNDARY, BITS_PER_UNIT); - - /* Find out if the parm needs padding, and whether above or below. */ - enum direction where_pad - = FUNCTION_ARG_PADDING (TYPE_MODE (TREE_TYPE (pval)), - expand_expr (sizetree, 0, VOIDmode, 0)); - - /* If it is padded below, adjust the stack address - upward over the padding. */ - - if (where_pad == downward) - { - rtx offset_rtx; - rtx address = XEXP (arg->stack, 0); - struct args_size stack_offset; - - stack_offset.constant = 0; - stack_offset.var = 0; - - /* Compute amount of padding. */ - ADD_PARM_SIZE (stack_offset, s2); - SUB_PARM_SIZE (stack_offset, sizetree); - offset_rtx = ARGS_SIZE_RTX (stack_offset); - - /* Adjust the address to store at. */ - if (GET_CODE (offset_rtx) == CONST_INT) - address = plus_constant (address, INTVAL (offset_rtx)); - else - { - address = gen_rtx (PLUS, Pmode, address, offset_rtx); - address = memory_address (QImode, address); - } - arg->stack = change_address (arg->stack, VOIDmode, address); - } - - /* ARG->stack probably refers to the stack-pointer. If so, - stabilize it, in case stack-pointer changes during evaluation. */ - if (reg_mentioned_p (stack_pointer_rtx, arg->stack)) - arg->stack = change_address (arg->stack, VOIDmode, - copy_to_reg (XEXP (arg->stack, 0))); - /* BLKmode argument that should go in a prespecified stack location. */ - if (arg->value == 0) - /* Not yet computed => compute it there. */ - /* ??? This should be changed to tell expand_expr - that it can store directly in the target. */ - arg->value = store_expr (arg->tree_value, arg->stack, 0); - else if (arg->value != arg->stack) - /* It was computed somewhere, but not where we wanted. - For example, the value may have come from an official - local variable or parameter. In that case, expand_expr - does not fill our suggested target. */ - emit_block_move (arg->stack, arg->value, ARGS_SIZE_RTX (arg->size), - TYPE_ALIGN (TREE_TYPE (pval)) / BITS_PER_UNIT); - - /* Now, if this value wanted to be partly in registers, - move the value from the stack to the registers - that are supposed to hold the values. */ - if (arg->partial > 0) - move_block_to_reg (REGNO (arg->reg), arg->stack, arg->partial); - } - else - { - /* BLKmode, at least partly to be pushed. */ - - register rtx tem - = arg->value ? arg->value : expand_expr (pval, 0, VOIDmode, 0); - register int excess; - rtx size_rtx; - - /* Pushing a nonscalar. - If part is passed in registers, arg->partial says how much - and emit_push_insn will take care of putting it there. */ - - /* Round its size up to a multiple - of the allocation unit for arguments. */ - - if (arg->size.var != 0) - { - excess = 0; - size_rtx = ARGS_SIZE_RTX (arg->size); - } - else - { - register tree size = size_in_bytes (TREE_TYPE (pval)); - /* PUSH_ROUNDING has no effect on us, because - emit_push_insn for BLKmode is careful to avoid it. */ - excess = (arg->size.constant - TREE_INT_CST_LOW (size) - + arg->partial * UNITS_PER_WORD); - size_rtx = expand_expr (size, 0, VOIDmode, 0); - } - - if (arg->stack) - abort (); - - emit_push_insn (tem, TYPE_MODE (TREE_TYPE (pval)), size_rtx, - TYPE_ALIGN (TREE_TYPE (pval)) / BITS_PER_UNIT, - arg->partial, arg->reg, excess, argblock, - ARGS_SIZE_RTX (arg->offset)); - } - - /* Once we have pushed something, pops can't safely - be deferred during the rest of the arguments. */ - NO_DEFER_POP; -} - -/* Expand conditional expressions. */ - -/* Generate code to evaluate EXP and jump to LABEL if the value is zero. - LABEL is an rtx of code CODE_LABEL, in this function and all the - functions here. */ - -void -jumpifnot (exp, label) - tree exp; - rtx label; -{ - do_jump (exp, label, 0); -} - -/* Generate code to evaluate EXP and jump to LABEL if the value is nonzero. */ - -void -jumpif (exp, label) - tree exp; - rtx label; -{ - do_jump (exp, 0, label); -} - -/* Generate code to evaluate EXP and jump to IF_FALSE_LABEL if - the result is zero, or IF_TRUE_LABEL if the result is one. - Either of IF_FALSE_LABEL and IF_TRUE_LABEL may be zero, - meaning fall through in that case. - - This function is responsible for optimizing cases such as - &&, || and comparison operators in EXP. */ - -void -do_jump (exp, if_false_label, if_true_label) - tree exp; - rtx if_false_label, if_true_label; -{ - register enum tree_code code = TREE_CODE (exp); - /* Some cases need to create a label to jump to - in order to properly fall through. - These cases set DROP_THROUGH_LABEL nonzero. */ - rtx drop_through_label = 0; - rtx temp; - rtx comparison = 0; - - emit_queue (); - - switch (code) - { - case ERROR_MARK: - break; - - case INTEGER_CST: - temp = integer_zerop (exp) ? if_false_label : if_true_label; - if (temp) - emit_jump (temp); - break; - - case ADDR_EXPR: - /* The address of something can never be zero. */ - if (if_true_label) - emit_jump (if_true_label); - break; - - case NOP_EXPR: - do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label); - break; - - case TRUTH_NOT_EXPR: - do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label); - break; - - case TRUTH_ANDIF_EXPR: - if (if_false_label == 0) - if_false_label = drop_through_label = gen_label_rtx (); - do_jump (TREE_OPERAND (exp, 0), if_false_label, 0); - do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label); - break; - - case TRUTH_ORIF_EXPR: - if (if_true_label == 0) - if_true_label = drop_through_label = gen_label_rtx (); - do_jump (TREE_OPERAND (exp, 0), 0, if_true_label); - do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label); - break; - - case COMPOUND_EXPR: - expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, 0); - emit_queue (); - do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label); - break; - - case COND_EXPR: - { - register rtx label1 = gen_label_rtx (); - drop_through_label = gen_label_rtx (); - do_jump (TREE_OPERAND (exp, 0), label1, 0); - /* Now the THEN-expression. */ - do_jump (TREE_OPERAND (exp, 1), - if_false_label ? if_false_label : drop_through_label, - if_true_label ? if_true_label : drop_through_label); - emit_label (label1); - /* Now the ELSE-expression. */ - do_jump (TREE_OPERAND (exp, 2), - if_false_label ? if_false_label : drop_through_label, - if_true_label ? if_true_label : drop_through_label); - } - break; - - case EQ_EXPR: - comparison = compare (exp, EQ, EQ, EQ, EQ); - break; - - case NE_EXPR: - comparison = compare (exp, NE, NE, NE, NE); - break; - - case LT_EXPR: - comparison = compare (exp, LT, LTU, GT, GTU); - break; - - case LE_EXPR: - comparison = compare (exp, LE, LEU, GE, GEU); - break; - - case GT_EXPR: - comparison = compare (exp, GT, GTU, LT, LTU); - break; - - case GE_EXPR: - comparison = compare (exp, GE, GEU, LE, LEU); - break; - - default: - temp = expand_expr (exp, 0, VOIDmode, 0); - /* Copy to register to avoid generating bad insns by cse - from (set (mem ...) (arithop)) (set (cc0) (mem ...)). */ - if (!cse_not_expected && GET_CODE (temp) == MEM) - temp = copy_to_reg (temp); - do_pending_stack_adjust (); - { - rtx zero = CONST0_RTX (GET_MODE (temp)); - - if (GET_CODE (temp) == CONST_INT) - comparison = compare_constants (NE, 0, - INTVAL (temp), 0, BITS_PER_WORD); - else if (GET_MODE (temp) != VOIDmode) - comparison = compare1 (temp, zero, NE, NE, 0, GET_MODE (temp)); - else - abort (); - } - } - - /* Do any postincrements in the expression that was tested. */ - emit_queue (); - - /* If COMPARISON is nonzero here, it is an rtx that can be substituted - straight into a conditional jump instruction as the jump condition. - Otherwise, all the work has been done already. */ - - if (comparison == const1_rtx) - { - if (if_true_label) - emit_jump (if_true_label); - } - else if (comparison == const0_rtx) - { - if (if_false_label) - emit_jump (if_false_label); - } - else if (comparison) - { - if (if_true_label) - { - if (bcc_gen_fctn[(int) GET_CODE (comparison)] != 0) - emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (comparison)]) (if_true_label)); - else - abort (); - - if (if_false_label) - emit_jump (if_false_label); - } - else if (if_false_label) - { - rtx pat; - - if (bcc_gen_fctn[(int) GET_CODE (comparison)] == 0) - abort (); - - pat = (*bcc_gen_fctn[(int) GET_CODE (comparison)]) (if_false_label); - /* Now invert the sense of the jump by exchanging the two arms - of each IF_THEN_ELSE. Note that inverting the condition - would be incorrect for IEEE floating point with nans! */ - if (GET_CODE (pat) == SEQUENCE) - { - int i; - /* We can invert a sequence if the only jump is at the end. */ - for (i = 0; i < (int) (XVECLEN (pat, 0) - 1); i++) - if (GET_CODE (XVECEXP (pat, 0, i)) == JUMP_INSN) - abort (); - invert_exp (PATTERN (XVECEXP (pat, 0, XVECLEN (pat, 0) - 1)), - 0, 0); - } - else - invert_exp (pat, 0, 0); - - emit_jump_insn (pat); - } - } - - if (drop_through_label) - emit_label (drop_through_label); -} - -/* Compare two integer constant rtx's, OP0 and OP1. - The comparison operation is OPERATION. - Return an rtx representing the value 1 or 0. - WIDTH is the width in bits that is significant. */ - -static rtx -compare_constants (operation, unsignedp, op0, op1, width) - enum rtx_code operation; - int unsignedp; - int op0, op1; - int width; -{ - int val; - - /* Sign-extend or zero-extend the operands to a full word - from an initial width of WIDTH bits. */ - if (width < HOST_BITS_PER_INT) - { - op0 &= (1 << width) - 1; - op1 &= (1 << width) - 1; - - if (! unsignedp) - { - if (op0 & (1 << (width - 1))) - op0 |= ((-1) << width); - if (op1 & (1 << (width - 1))) - op1 |= ((-1) << width); - } - } - - switch (operation) - { - case EQ: - val = op0 == op1; - break; - - case NE: - val = op0 != op1; - break; - - case GT: - case GTU: - val = op0 > op1; - break; - - case LT: - case LTU: - val = op0 < op1; - break; - - case GE: - case GEU: - val = op0 >= op1; - break; - - case LE: - case LEU: - val = op0 <= op1; - } - - return val ? const1_rtx : const0_rtx; -} - -/* Generate code for a comparison expression EXP - (including code to compute the values to be compared) - and set (CC0) according to the result. - SIGNED_FORWARD should be the rtx operation for this comparison for - signed data; UNSIGNED_FORWARD, likewise for use if data is unsigned. - SIGNED_REVERSE and UNSIGNED_REVERSE are used if it is desirable - to interchange the operands for the compare instruction. - - We force a stack adjustment unless there are currently - things pushed on the stack that aren't yet used. */ - -static rtx -compare (exp, signed_forward, unsigned_forward, - signed_reverse, unsigned_reverse) - register tree exp; - enum rtx_code signed_forward, unsigned_forward; - enum rtx_code signed_reverse, unsigned_reverse; -{ - - register rtx op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0); - register rtx op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0); - register enum machine_mode mode = GET_MODE (op0); - int unsignedp; - - /* If one operand is 0, make it the second one. */ - - if (op0 == const0_rtx - || (GET_MODE_CLASS (mode) == MODE_FLOAT && op0 == CONST0_RTX (mode))) - { - rtx tem = op0; - op0 = op1; - op1 = tem; - signed_forward = signed_reverse; - unsigned_forward = unsigned_reverse; - } - - if (flag_force_mem) - { - op0 = force_not_mem (op0); - op1 = force_not_mem (op1); - } - - do_pending_stack_adjust (); - - unsignedp = (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))) - || TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 1)))); - - if (GET_CODE (op0) == CONST_INT && GET_CODE (op1) == CONST_INT) - return compare_constants (signed_forward, unsignedp, - INTVAL (op0), INTVAL (op1), - GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))); - - emit_cmp_insn (op0, op1, - (mode == BLKmode) ? expr_size (TREE_OPERAND (exp, 0)) : 0, - unsignedp, - TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); - - return gen_rtx ((unsignedp ? unsigned_forward : signed_forward), - VOIDmode, cc0_rtx, const0_rtx); -} - -/* Like compare but expects the values to compare as two rtx's. - The decision as to signed or unsigned comparison must be made by the caller. - BLKmode is not allowed. */ - -static rtx -compare1 (op0, op1, forward_op, reverse_op, unsignedp, mode) - register rtx op0, op1; - enum rtx_code forward_op, reverse_op; - int unsignedp; - enum machine_mode mode; -{ - /* If one operand is 0, make it the second one. */ - - if (op0 == const0_rtx - || (GET_MODE_CLASS (mode) == MODE_FLOAT && op0 == CONST0_RTX (mode))) - { - rtx tem = op0; - op0 = op1; - op1 = tem; - forward_op = reverse_op; - } - - if (flag_force_mem) - { - op0 = force_not_mem (op0); - op1 = force_not_mem (op1); - } - - do_pending_stack_adjust (); - - if (GET_CODE (op0) == CONST_INT && GET_CODE (op1) == CONST_INT) - return compare_constants (forward_op, unsignedp, - INTVAL (op0), INTVAL (op1), - GET_MODE_BITSIZE (mode)); - - emit_cmp_insn (op0, op1, 0, unsignedp, 0); - - return gen_rtx (forward_op, VOIDmode, cc0_rtx, const0_rtx); -} - -/* Generate code to calculate EXP using a store-flag instruction - and return an rtx for the result. - If TARGET is nonzero, store the result there if convenient. - - Return zero if there is no suitable set-flag instruction - available on this machine. */ - -static rtx -do_store_flag (exp, target, mode) - tree exp; - rtx target; - enum machine_mode mode; -{ - register enum tree_code code = TREE_CODE (exp); - register rtx comparison = 0; - enum machine_mode compare_mode; - rtx prev_insn = get_last_insn (); - enum insn_code icode; - - switch (code) - { -#ifdef HAVE_seq - case EQ_EXPR: - if (HAVE_seq) - { - comparison = compare (exp, EQ, EQ, EQ, EQ); - icode = CODE_FOR_seq; - compare_mode = insn_operand_mode[(int) CODE_FOR_seq][0]; - } - break; -#endif - -#ifdef HAVE_sne - case NE_EXPR: - if (HAVE_sne) - { - comparison = compare (exp, NE, NE, NE, NE); - icode = CODE_FOR_sne; - compare_mode = insn_operand_mode[(int) CODE_FOR_sne][0]; - } - break; -#endif - -#if defined (HAVE_slt) && defined (HAVE_sltu) && defined (HAVE_sgt) && defined (HAVE_sgtu) - case LT_EXPR: - if (HAVE_slt && HAVE_sltu && HAVE_sgt && HAVE_sgtu) - { - comparison = compare (exp, LT, LTU, GT, GTU); - icode = CODE_FOR_slt; - compare_mode = insn_operand_mode[(int) CODE_FOR_slt][0]; - } - break; - - case GT_EXPR: - if (HAVE_slt && HAVE_sltu && HAVE_sgt && HAVE_sgtu) - { - comparison = compare (exp, GT, GTU, LT, LTU); - icode = CODE_FOR_slt; - compare_mode = insn_operand_mode[(int) CODE_FOR_slt][0]; - } - break; -#endif - -#if defined (HAVE_sle) && defined (HAVE_sleu) && defined (HAVE_sge) && defined (HAVE_sgeu) - case LE_EXPR: - if (HAVE_sle && HAVE_sleu && HAVE_sge && HAVE_sgeu) - { - comparison = compare (exp, LE, LEU, GE, GEU); - icode = CODE_FOR_sle; - compare_mode = insn_operand_mode[(int) CODE_FOR_sle][0]; - } - break; - - case GE_EXPR: - if (HAVE_sle && HAVE_sleu && HAVE_sge && HAVE_sgeu) - { - comparison = compare (exp, GE, GEU, LE, LEU); - icode = CODE_FOR_sle; - compare_mode = insn_operand_mode[(int) CODE_FOR_sle][0]; - } - break; -#endif - } - if (comparison == 0) - return 0; - - if (target == 0 || GET_MODE (target) != mode - /* Don't use specified target unless the insn can handle it. */ - || ! (*insn_operand_predicate[(int) icode][0]) (target, mode) - /* When modes don't match, don't use specified target, - because it might be the same as an operand, - and then the CLOBBER output below would screw up. */ - || (mode != compare_mode && GET_CODE (comparison) != CONST_INT)) - target = gen_reg_rtx (mode); - - /* Store the comparison in its proper mode. */ - if (GET_CODE (comparison) == CONST_INT) - emit_move_insn (target, comparison); - else if (GET_MODE (target) != compare_mode) - { - /* We want a different mode: store result in its natural mode. - Combine the mode conversion with the truncation we must do anyway. */ - /* Put a CLOBBER before the compare, so we don't come between - the compare and the insn that uses the result. */ - emit_insn_after (gen_rtx (CLOBBER, VOIDmode, target), prev_insn); - emit_insn ((*setcc_gen_fctn[(int) GET_CODE (comparison)]) - (gen_rtx (SUBREG, compare_mode, target, 0))); - /* If the desired mode is wider than what we got, - use an AND to convert it, but not if we will do one anyway. */ -#if STORE_FLAG_VALUE == 1 - if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (compare_mode)) - expand_bit_and (mode, target, const1_rtx, target); -#endif - } - else - emit_insn ((*setcc_gen_fctn[(int) GET_CODE (comparison)]) (target)); - -#if STORE_FLAG_VALUE != 1 -#if STORE_FLAG_VALUE & 1 - expand_bit_and (mode, target, const1_rtx, target); -#else - expand_shift (RSHIFT_EXPR, mode, target, - build_int_2 (GET_MODE_BITSIZE (mode) - 1, 0), - target, TRUE); -#endif -#endif - return target; -} - -/* Generate a tablejump instruction (used for switch statements). */ - -#ifdef HAVE_tablejump - -/* INDEX is the value being switched on, with the lowest value - in the table already subtracted. - RANGE is the length of the jump table. - TABLE_LABEL is a CODE_LABEL rtx for the table itself. - - DEFAULT_LABEL is a CODE_LABEL rtx to jump to if the - index value is out of range. */ - -void -do_tablejump (index, range, table_label, default_label) - rtx index, range, table_label, default_label; -{ - register rtx temp; - - emit_cmp_insn (range, index, 0, 0, 0); - emit_jump_insn (gen_bltu (default_label)); - /* If flag_force_addr were to affect this address - it could interfere with the tricky assumptions made - about addresses that contain label-refs, - which may be valid only very near the tablejump itself. */ - index = memory_address_noforce - (CASE_VECTOR_MODE, - gen_rtx (PLUS, Pmode, - gen_rtx (MULT, Pmode, index, - gen_rtx (CONST_INT, VOIDmode, - GET_MODE_SIZE (CASE_VECTOR_MODE))), - gen_rtx (LABEL_REF, VOIDmode, table_label))); - temp = gen_reg_rtx (CASE_VECTOR_MODE); - convert_move (temp, gen_rtx (MEM, CASE_VECTOR_MODE, index), 0); - - emit_jump_insn (gen_tablejump (temp, table_label)); -} - -#endif /* HAVE_tablejump */ diff --git a/gnu/usr.bin/gcc1/cc1/expr.h b/gnu/usr.bin/gcc1/cc1/expr.h deleted file mode 100644 index 3cc495c3fa..0000000000 --- a/gnu/usr.bin/gcc1/cc1/expr.h +++ /dev/null @@ -1,386 +0,0 @@ -/* Definitions for code generation pass of GNU compiler. - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* Macros to access the slots of a QUEUED rtx. - Here rather than in rtl.h because only the expansion pass - should ever encounter a QUEUED. */ - -/* The variable for which an increment is queued. */ -#define QUEUED_VAR(P) XEXP (P, 0) -/* If the increment has been emitted, this is the insn - that does the increment. It is zero before the increment is emitted. */ -#define QUEUED_INSN(P) XEXP (P, 1) -/* If a pre-increment copy has been generated, this is the copy - (it is a temporary reg). Zero if no copy made yet. */ -#define QUEUED_COPY(P) XEXP (P, 2) -/* This is the body to use for the insn to do the increment. - It is used to emit the increment. */ -#define QUEUED_BODY(P) XEXP (P, 3) -/* Next QUEUED in the queue. */ -#define QUEUED_NEXT(P) XEXP (P, 4) - -/* This is the 4th arg to `expand_expr'. - EXPAND_SUM means it is ok to return a PLUS rtx or MULT rtx. - EXPND_CONST_ADDRESS means it is ok to return a MEM whose address - is a constant that is not a legitimate address. */ -enum expand_modifier {EXPAND_NORMAL, EXPAND_SUM, EXPAND_CONST_ADDRESS}; - -/* If this is nonzero, we do not bother generating VOLATILE - around volatile memory references, and we are willing to - output indirect addresses. If cse is to follow, we reject - indirect addresses so a useful potential cse is generated; - if it is used only once, instruction combination will produce - the same indirect address eventually. */ -extern int cse_not_expected; - -/* List (chain of EXPR_LISTs) of pseudo-regs of SAVE_EXPRs. - So we can mark them all live at the end of the function, if stupid. */ -extern rtx save_expr_regs; - -extern int current_function_calls_alloca; - -/* This is the offset from the arg pointer to the place where the first - anonymous arg can be found, if there is one. */ -extern rtx current_function_arg_offset_rtx; - -/* Nonzero means stack pops must not be deferred, and deferred stack - pops must not be output. It is nonzero inside a function call, - inside a conditional expression, inside a statement expression, - and in other cases as well. */ -extern int inhibit_defer_pop; - -#define NO_DEFER_POP (inhibit_defer_pop += 1) -#define OK_DEFER_POP (inhibit_defer_pop -= 1) - -#ifdef TREE_CODE /* Don't lose if tree.h not included. */ -/* Structure to record the size of a sequence of arguments - as the sum of a tree-expression and a constant. */ - -struct args_size -{ - int constant; - tree var; -}; -#endif - -/* Add the value of the tree INC to the `struct args_size' TO. */ - -#define ADD_PARM_SIZE(TO, INC) \ -{ tree inc = (INC); \ - if (TREE_CODE (inc) == INTEGER_CST) \ - (TO).constant += TREE_INT_CST_LOW (inc); \ - else if ((TO).var == 0) \ - (TO).var = inc; \ - else \ - (TO).var = genop (PLUS_EXPR, (TO).var, inc); } - -#define SUB_PARM_SIZE(TO, DEC) \ -{ tree dec = (DEC); \ - if (TREE_CODE (dec) == INTEGER_CST) \ - (TO).constant -= TREE_INT_CST_LOW (dec); \ - else if ((TO).var == 0) \ - (TO).var = genop (MINUS_EXPR, integer_zero_node, dec); \ - else \ - (TO).var = genop (MINUS_EXPR, (TO).var, dec); } - -/* Convert the implicit sum in a `struct args_size' into an rtx. */ -#define ARGS_SIZE_RTX(SIZE) \ -((SIZE).var == 0 ? gen_rtx (CONST_INT, VOIDmode, (SIZE).constant) \ - : plus_constant (expand_expr ((SIZE).var, 0, VOIDmode, 0), \ - (SIZE).constant)) - -/* Supply a default definition for FUNCTION_ARG_PADDING: - usually pad upward, but pad short args downward on big-endian machines. */ - -enum direction {none, upward, downward}; /* Value has this type. */ - -#ifndef FUNCTION_ARG_PADDING -#ifdef BYTES_BIG_ENDIAN -#define FUNCTION_ARG_PADDING(mode, size) \ - (((mode) == BLKmode \ - ? (GET_CODE (size) == CONST_INT \ - && INTVAL (size) < PARM_BOUNDARY / BITS_PER_UNIT) \ - : GET_MODE_BITSIZE (mode) < PARM_BOUNDARY) \ - ? downward : upward) -#else -#define FUNCTION_ARG_PADDING(mode, size) upward -#endif -#endif - -/* Nonzero if type TYPE should be returned in memory - (even though its mode is not BLKmode). - Most machines can use the following default definition. */ - -#ifndef RETURN_IN_MEMORY -#define RETURN_IN_MEMORY(type) 0 -#endif - -/* Optabs are tables saying how to generate insn bodies - for various machine modes and numbers of operands. - Each optab applies to one operation. - For example, add_optab applies to addition. - - The insn_code slot is the enum insn_code that says how to - generate an insn for this operation on a particular machine mode. - It is CODE_FOR_nothing if there is no such insn on the target machine. - - The `lib_call' slot is the name of the library function that - can be used to perform the operation. - - A few optabs, such as move_optab and cmp_optab, are used - by special code. */ - -/* Everything that uses expr.h needs to define enum insn_code - but we don't list it in the Makefile dependencies just for that. */ -#include "insn-codes.h" - -typedef struct optab -{ - enum rtx_code code; - struct { - enum insn_code insn_code; - char *lib_call; - } handlers [NUM_MACHINE_MODES]; -} * optab; - -/* Given an enum insn_code, access the function to construct - the body of that kind of insn. */ -#define GEN_FCN(CODE) (*insn_gen_function[(int) (CODE)]) -extern rtx (*insn_gen_function[]) (); - -extern optab add_optab; -extern optab sub_optab; -extern optab smul_optab; /* Signed multiply */ -extern optab umul_optab; /* Unsigned multiply */ -extern optab smul_widen_optab; /* Signed multiply with result - one machine mode wider than args */ -extern optab umul_widen_optab; -extern optab sdiv_optab; /* Signed divide */ -extern optab sdivmod_optab; /* Signed divide-and-remainder in one */ -extern optab udiv_optab; -extern optab udivmod_optab; -extern optab smod_optab; /* Signed remainder */ -extern optab umod_optab; -extern optab flodiv_optab; /* Optab for floating divide. */ -extern optab ftrunc_optab; /* Convert float to integer in float fmt */ -extern optab and_optab; /* Logical and */ -extern optab andcb_optab; /* Logical and with complement of 2nd arg */ -extern optab ior_optab; /* Logical or */ -extern optab xor_optab; /* Logical xor */ -extern optab ashl_optab; /* Arithmetic shift left */ -extern optab ashr_optab; /* Arithmetic shift right */ -extern optab lshl_optab; /* Logical shift left */ -extern optab lshr_optab; /* Logical shift right */ -extern optab rotl_optab; /* Rotate left */ -extern optab rotr_optab; /* Rotate right */ - -extern optab mov_optab; /* Move instruction. */ -extern optab movstrict_optab; /* Move, preserving high part of register. */ - -extern optab cmp_optab; /* Compare insn; two operands. */ -extern optab tst_optab; /* tst insn; compare one operand against 0 */ - -/* Unary operations */ -extern optab neg_optab; /* Negation */ -extern optab abs_optab; /* Abs value */ -extern optab one_cmpl_optab; /* Bitwise not */ -extern optab ffs_optab; /* Find first bit set */ - -/* Passed to expand_binop and expand_unop to say which options to try to use - if the requested operation can't be open-coded on the requisite mode. - Either OPTAB_LIB or OPTAB_LIB_WIDEN says try using a library call. - Either OPTAB_WIDEN or OPTAB_LIB_WIDEN says try using a wider mode. - OPTAB_MUST_WIDEN says try widening and don't try anything else. */ - -enum optab_methods -{ - OPTAB_DIRECT, - OPTAB_LIB, - OPTAB_WIDEN, - OPTAB_LIB_WIDEN, - OPTAB_MUST_WIDEN -}; - -typedef rtx (*rtxfun) (); - -/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...) - gives the gen_function to make a branch to test that condition. */ - -extern rtxfun bcc_gen_fctn[NUM_RTX_CODE]; - -/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...) - gives the gen_function to make a store-condition insn - to test that condition. */ - -extern rtxfun setcc_gen_fctn[NUM_RTX_CODE]; - -/* Expand a binary operation given optab and rtx operands. */ -rtx expand_binop (); - -/* Expand a binary operation with both signed and unsigned forms. */ -rtx sign_expand_binop (); - -/* Expand a unary arithmetic operation given optab rtx operand. */ -rtx expand_unop (); - -/* Arguments MODE, RTX: return an rtx for the negation of that value. - May emit insns. */ -rtx negate_rtx (); - -/* Initialize the tables that control conversion between fixed and - floating values. */ -void init_fixtab (); -void init_floattab (); - -/* Generate code for a FIX_EXPR. */ -void expand_fix (); - -/* Generate code for a FLOAT_EXPR. */ -void expand_float (); - -/* Create but don't emit one rtl instruction to add one rtx into another. - Modes must match. - Likewise for subtraction and for just copying. - These do not call protect_from_queue; caller must do so. */ -rtx gen_add2_insn (); -rtx gen_sub2_insn (); -rtx gen_move_insn (); - -/* Emit one rtl instruction to store zero in specified rtx. */ -void emit_clr_insn (); - -/* Emit one rtl insn to store 1 in specified rtx assuming it contains 0. */ -void emit_0_to_1_insn (); - -/* Emit one rtl insn to compare two rtx's. */ -void emit_cmp_insn (); - -/* Emit some rtl insns to move data between rtx's, converting machine modes. - Both modes must be floating or both fixed. */ -void convert_move (); - -/* Convert an rtx to specified machine mode and return the result. */ -rtx convert_to_mode (); - -/* Emit code to push some arguments and call a library routine, - storing the value in a specified place. Calling sequence is - complicated. */ -void emit_library_call (); - -/* Given an rtx that may include add and multiply operations, - generate them as insns and return a pseudo-reg containing the value. - Useful after calling expand_expr with 1 as sum_ok. */ -rtx force_operand (); - -/* Return an rtx for the size in bytes of the value of an expr. */ -rtx expr_size (); - -/* Return an rtx for the sum of an rtx and an integer. */ -rtx plus_constant (); - -rtx lookup_static_chain (); - -/* Return an rtx like arg but sans any constant terms. - Returns the original rtx if it has no constant terms. - The constant terms are added and stored via a second arg. */ -rtx eliminate_constant_term (); - -/* Convert arg to a valid memory address for specified machine mode, - by emitting insns to perform arithmetic if nec. */ -rtx memory_address (); - -/* Like `memory_address' but pretent `flag_force_addr' is 0. */ -rtx memory_address_noforce (); - -/* Return a memory reference like MEMREF, but with its mode changed - to MODE and its address changed to ADDR. - (VOIDmode means don't change the mode. - NULL for ADDR means don't change the address.) */ -rtx change_address (); - -/* Return 1 if two rtx's are equivalent in structure and elements. */ -int rtx_equal_p (); - -/* Given rtx, return new rtx whose address won't be affected by - any side effects. It has been copied to a new temporary reg. */ -rtx stabilize (); - -/* Given an rtx, copy all regs it refers to into new temps - and return a modified copy that refers to the new temps. */ -rtx copy_all_regs (); - -/* Copy given rtx to a new temp reg and return that. */ -rtx copy_to_reg (); - -/* Like copy_to_reg but always make the reg Pmode. */ -rtx copy_addr_to_reg (); - -/* Like copy_to_reg but always make the reg the specified mode MODE. */ -rtx copy_to_mode_reg (); - -/* Copy given rtx to given temp reg and return that. */ -rtx copy_to_suggested_reg (); - -/* Copy a value to a register if it isn't already a register. - Args are mode (in case value is a constant) and the value. */ -rtx force_reg (); - -/* Return given rtx, copied into a new temp reg if it was in memory. */ -rtx force_not_mem (); - -/* Remove some bytes from the stack. An rtx says how many. */ -void adjust_stack (); - -/* Add some bytes to the stack. An rtx says how many. */ -void anti_adjust_stack (); - -/* Emit code to copy function value to a new temp reg and return that reg. */ -rtx function_value (); - -/* Return an rtx that refers to the value returned by a function - in its original home. This becomes invalid if any more code is emitted. */ -rtx hard_function_value (); - -/* Return an rtx that refers to the value returned by a library call - in its original home. This becomes invalid if any more code is emitted. */ -rtx hard_libcall_value (); - -/* Emit code to copy function value to a specified place. */ -void copy_function_value (); - -/* Given an rtx, return an rtx for a value rounded up to a multiple - of STACK_BOUNDARY / BITS_PER_UNIT. */ -rtx round_push (); - -rtx store_bit_field (); -rtx extract_bit_field (); -rtx expand_shift (); -rtx expand_bit_and (); -rtx expand_mult (); -rtx expand_divmod (); -rtx expand_mult_add (); -rtx get_structure_value_addr (); -rtx expand_stmt_expr (); - -void jumpifnot (); -void jumpif (); -void do_jump (); - -rtx assemble_static_space (); diff --git a/gnu/usr.bin/gcc1/cc1/final.c b/gnu/usr.bin/gcc1/cc1/final.c deleted file mode 100644 index 910348e500..0000000000 --- a/gnu/usr.bin/gcc1/cc1/final.c +++ /dev/null @@ -1,1652 +0,0 @@ -/* Convert RTL to assembler code and output it, for GNU compiler. - Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This is the final pass of the compiler. - It looks at the rtl code for a function and outputs assembler code. - - Call `final_start_function' to output the assembler code for function entry, - `final' to output assembler code for some RTL code, - `final_end_function' to output assembler code for function exit. - If a function is compiled in several pieces, each piece is - output separately with `final'. - - Some optimizations are also done at this level. - Move instructions that were made unnecessary by good register allocation - are detected and omitted from the output. (Though most of these - are removed by the last jump pass.) - - Instructions to set the condition codes are omitted when it can be - seen that the condition codes already had the desired values. - - In some cases it is sufficient if the inherited condition codes - have related values, but this may require the following insn - (the one that tests the condition codes) to be modified. - - The code for the function prologue and epilogue are generated - directly as assembler code by the macros FUNCTION_PROLOGUE and - FUNCTION_EPILOGUE. Those instructions never exist as rtl. */ - -#include -#include "config.h" -#include "rtl.h" -#include "regs.h" -#include "insn-config.h" -#include "recog.h" -#include "conditions.h" -#include "gdbfiles.h" -#include "flags.h" -#include "real.h" -#include "output.h" - -/* Get N_SLINE and N_SOL from stab.h if we can expect the file to exist. */ -#ifdef DBX_DEBUGGING_INFO -#ifdef USG -#include "stab.h" /* If doing DBX on sysV, use our own stab.h. */ -#else -#include /* On BSD, use the system's stab.h. */ -#endif /* not USG */ -#endif /* DBX_DEBUGGING_INFO */ - -/* .stabd code for line number. */ -#ifndef N_SLINE -#define N_SLINE 0x44 -#endif - -/* .stabs code for included file name. */ -#ifndef N_SOL -#define N_SOL 0x84 -#endif - -#define min(A,B) ((A) < (B) ? (A) : (B)) - -rtx peephole (); -void output_asm_insn (); -rtx alter_subreg (); -static int alter_cond (); -void output_asm_label (); -static void output_operand (); -void output_address (); -void output_addr_const (); -static void output_source_line (); -rtx final_scan_insn (); - -/* the sdb debugger needs the line given as an offset from the beginning - of the current function -wfs*/ - -extern int sdb_begin_function_line; - -/* Line number of last NOTE. */ -static int last_linenum; - -/* Number of basic blocks seen so far; - used if profile_block_flag is set. */ -static int count_basic_blocks; - -/* Nonzero while outputting an `asm' with operands. - This means that inconsistencies are the user's fault, so don't abort. - The precise value is the insn being output, to pass to error_for_asm. */ -static rtx this_is_asm_operands; - -/* Number of operands of this insn, for an `asm' with operands. */ -static int insn_noperands; - -/* File in which assembler code is being written. */ - -extern FILE *asm_out_file; - -/* Compare optimization flag. */ - -static rtx last_ignored_compare = 0; - -/* Flag indicating this insn is the start of a new basic block. */ - -static int new_block = 1; - -/* All the symbol-blocks (levels of scoping) in the compilation - are assigned sequence numbers in order of appearance of the - beginnings of the symbol-blocks. Both final and dbxout do this, - and assume that they will both give the same number to each block. - Final uses these sequence numbers to generate assembler label names - LBBnnn and LBEnnn for the beginning and end of the symbol-block. - Dbxout uses the sequence nunbers to generate references to the same labels - from the dbx debugging information. - - Sdb records this level at the beginning - of each function, so that when it recurses down the declarations, it may - find the current level, since it outputs the block beginning and endings - at the point in the asm file, where the blocks would begin and end. */ - -int next_block_index; - -/* Chain of all `struct gdbfile's. */ - -struct gdbfile *gdbfiles; - -/* `struct gdbfile' for the last file we wrote a line number for. */ - -static struct gdbfile *current_gdbfile; - -/* Filenum to assign to the next distinct source file encountered. */ - -static int next_gdb_filenum; - -/* This variable contains machine-dependent flags (defined in tm-...h) - set and examined by output routines - that describe how to interpret the condition codes properly. */ - -CC_STATUS cc_status; - -/* During output of an insn, this contains a copy of cc_status - from before the insn. */ - -CC_STATUS cc_prev_status; - -/* Last source file name mentioned in a NOTE insn. */ - -static char *lastfile; - -/* Indexed by hardware reg number, is 1 if that register is ever - used in the current function. - - In life_analysis, or in stupid_life_analysis, this is set - up to record the hard regs used explicitly. Reload adds - in the hard regs used for holding pseudo regs. Final uses - it to generate the code in the function prologue and epilogue - to save and restore registers as needed. */ - -char regs_ever_live[FIRST_PSEUDO_REGISTER]; - -/* Nonzero means current function must be given a frame pointer. - Set in stmt.c if anything is allocated on the stack there. - Set in reload1.c if anything is allocated on the stack there. */ - -int frame_pointer_needed; - -/* Assign unique numbers to labels generated for profiling. */ - -int profile_label_no; - -/* Length so far allocated in PENDING_BLOCKS. */ - -static int max_block_depth; - -/* Stack of sequence numbers of symbol-blocks of which we have seen the - beginning but not yet the end. Sequence numbers are assigned at - the beginning; this stack allows us to find the sequence number - of a block that is ending. */ - -static int *pending_blocks; - -/* Number of elements currently in use in PENDING_BLOCKS. */ - -static int block_depth; - -/* Nonzero if have enabled APP processing of our assembler output. */ - -static int app_on; - -/* If we are outputting an insn sequence, this contains the sequence rtx. - Zero otherwise. */ - -rtx final_sequence; - -/* Initialize data in final at the beginning of a compilation. */ - -void -init_final (filename) - char *filename; -{ - next_block_index = 2; - lastfile = filename; - app_on = 0; - max_block_depth = 20; - pending_blocks = (int *) xmalloc (20 * sizeof *pending_blocks); - gdbfiles = 0; - next_gdb_filenum = 0; - final_sequence = 0; -} - -/* Called at end of source file, - to output the block-profiling table for this entire compilation. */ - -void -end_final (filename) - char *filename; -{ - int i; - - if (profile_block_flag) - { - char name[12]; - - data_section (); - - /* Output the main header, of 6 words: - 0: 1 if this file's initialized, else 0. - 1: address of file name. - 2: address of table of counts. - 4: number of counts in the table. - 5: always 0, for compatibility with Sun. - 6: extra word added by GNU: address of address table - which contains addresses of basic blocks, - in parallel with the table of counts. */ - ASM_OUTPUT_ALIGN (asm_out_file, - exact_log2 (min (UNITS_PER_WORD, - BIGGEST_ALIGNMENT / BITS_PER_UNIT))); - - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 0); - assemble_integer_zero (); - - ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 1); - ASM_OUTPUT_INT (asm_out_file, gen_rtx (SYMBOL_REF, Pmode, name)); - ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 2); - ASM_OUTPUT_INT (asm_out_file, gen_rtx (SYMBOL_REF, Pmode, name)); - ASM_OUTPUT_INT (asm_out_file, gen_rtx (CONST_INT, VOIDmode, - count_basic_blocks)); - assemble_integer_zero (); - ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 3); - ASM_OUTPUT_INT (asm_out_file, gen_rtx (SYMBOL_REF, Pmode, name)); - - /* Output the file name. */ - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 1); - { - int len = strlen (filename); - char *data_file = (char *) alloca (len + 3); - strcpy (data_file, filename); - if (len > 2 && ! strcmp (".c", data_file + len - 2)) - data_file[len - 2] = 0; - else if (len > 2 && ! strcmp (".i", data_file + len - 2)) - data_file[len - 2] = 0; - else if (len > 3 && ! strcmp (".co", data_file + len - 3)) - data_file[len - 3] = 0; - strcat (data_file, ".d"); - assemble_string (data_file, strlen (data_file) + 1); - } - - /* Realign data section. */ - ASM_OUTPUT_ALIGN (asm_out_file, - exact_log2 (min (UNITS_PER_WORD, - BIGGEST_ALIGNMENT / BITS_PER_UNIT))); - - /* Make space for the table of counts. */ - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 2); - ASM_OUTPUT_SKIP (asm_out_file, UNITS_PER_WORD * count_basic_blocks); - - /* Output the table of addresses. */ - text_section (); - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 3); - for (i = 0; i < count_basic_blocks; i++) - { - char name[12]; - ASM_GENERATE_INTERNAL_LABEL (name, "LPB", i); - ASM_OUTPUT_INT (asm_out_file, gen_rtx (SYMBOL_REF, Pmode, name)); - } - - /* End with the address of the table of addresses, - so we can find it easily, as the last word in the file's text. */ - ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 3); - ASM_OUTPUT_INT (asm_out_file, gen_rtx (SYMBOL_REF, Pmode, name)); - } -} - -/* Enable APP processing of subsequent output. - Used before the output from an `asm' statement. */ - -void -app_enable () -{ - if (! app_on) - { - fprintf (asm_out_file, ASM_APP_ON); - app_on = 1; - } -} - -/* Enable APP processing of subsequent output. - Called from varasm.c before most kinds of output. */ - -void -app_disable () -{ - if (app_on) - { - fprintf (asm_out_file, ASM_APP_OFF); - app_on = 0; - } -} - -/* Return the number of slots filled in the current - delayed branch sequence. */ - -#ifdef HAVE_DELAYED_BRANCH -int -dbr_sequence_length () -{ - int i; - int slots = 0; - /* It's zero if we are not scheduling or not in a sequence. - (We never count the first insn.) */ - if (flag_delayed_branch && final_sequence != 0) - { - for (i = 1; i < XVECLEN (final_sequence, 0); i++) - slots += DBR_INSN_SLOTS (XVECEXP (final_sequence, 0, i)); - } - return slots; -} -#endif - -/* Output assembler code for the start of a function, - and initialize some of the variables in this file - for the new function. The label for the function and associated - assembler pseudo-ops have already been output in `assemble_function'. - - FIRST is the first insn of the rtl for the function being compiled. - FILE is the file to write assembler code to. - WRITE_SYMBOLS says which kind of debugging info to write (or none). - OPTIMIZE is nonzero if we should eliminate redundant - test and compare insns. */ - -void -final_start_function (first, file, write_symbols, optimize) - rtx first; - FILE *file; - enum debugger write_symbols; - int optimize; -{ - block_depth = 0; - - this_is_asm_operands = 0; - - /* Record beginning of the symbol-block that's the entire function. */ - - if (write_symbols == GDB_DEBUG) - { - pending_blocks[block_depth++] = next_block_index; - fprintf (file, "\t.gdbbeg %d\n", next_block_index++); - } - - /* Initial line number is supposed to be output - before the function's prologue and label - so that the function's address will not appear to be - in the last statement of the preceding function. */ - if (NOTE_LINE_NUMBER (first) != NOTE_INSN_DELETED) - { - if (write_symbols == SDB_DEBUG) - /* For sdb, let's not, but say we did. - We need to set last_linenum for sdbout_function_begin, - but we can't have an actual line number before the .bf symbol. - (sdb_begin_function_line is not set, - and other compilers don't do it.) */ - last_linenum = NOTE_LINE_NUMBER (first); - else - output_source_line (file, first, write_symbols); - } - - /* The Sun386i and perhaps other machines don't work right - if the profiling code comes after the prologue. */ -#ifdef PROFILE_BEFORE_PROLOGUE - if (profile_flag) - profile_function (file); -#endif /* PROFILE_BEFORE_PROLOGUE */ - -#ifdef FUNCTION_PROLOGUE - /* First output the function prologue: code to set up the stack frame. */ - FUNCTION_PROLOGUE (file, get_frame_size ()); -#endif - -#ifdef SDB_DEBUGGING_INFO - next_block_index = 1; -#endif - -#ifdef FUNCTION_BLOCK_PROFILER - if (profile_block_flag) - { - FUNCTION_BLOCK_PROFILER (file, profile_label_no); - } -#endif /* FUNCTION_BLOCK_PROFILER */ - -#ifndef PROFILE_BEFORE_PROLOGUE - if (profile_flag) - profile_function (file); -#endif /* not PROFILE_BEFORE_PROLOGUE */ - - profile_label_no++; -} - -profile_function (file) - FILE *file; -{ - int align = min (BIGGEST_ALIGNMENT, BITS_PER_WORD); - extern int current_function_returns_struct; - extern int current_function_needs_context; - int sval = current_function_returns_struct; - int cxt = current_function_needs_context; - - data_section (); - ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT)); - ASM_OUTPUT_INTERNAL_LABEL (file, "LP", profile_label_no); - assemble_integer_zero (); - - text_section (); - -#ifdef STRUCT_VALUE_INCOMING_REGNUM - if (sval) - ASM_OUTPUT_REG_PUSH (file, STRUCT_VALUE_INCOMING_REGNUM); -#else -#ifdef STRUCT_VALUE_REGNUM - if (sval) - ASM_OUTPUT_REG_PUSH (file, STRUCT_VALUE_REGNUM); -#endif -#endif - -#if 0 -#ifdef STATIC_CHAIN_INCOMING_REGNUM - if (cxt) - ASM_OUTPUT_REG_PUSH (file, STATIC_CHAIN_INCOMING_REGNUM); -#else -#ifdef STATIC_CHAIN_REGNUM - if (cxt) - ASM_OUTPUT_REG_PUSH (file, STATIC_CHAIN_REGNUM); -#endif -#endif -#endif /* 0 */ - - FUNCTION_PROFILER (file, profile_label_no); - -#if 0 -#ifdef STATIC_CHAIN_INCOMING_REGNUM - if (cxt) - ASM_OUTPUT_REG_POP (file, STATIC_CHAIN_INCOMING_REGNUM); -#else -#ifdef STATIC_CHAIN_REGNUM - if (cxt) - ASM_OUTPUT_REG_POP (file, STATIC_CHAIN_REGNUM); -#endif -#endif -#endif /* 0 */ - -#ifdef STRUCT_VALUE_INCOMING_REGNUM - if (sval) - ASM_OUTPUT_REG_POP (file, STRUCT_VALUE_INCOMING_REGNUM); -#else -#ifdef STRUCT_VALUE_REGNUM - if (sval) - ASM_OUTPUT_REG_POP (file, STRUCT_VALUE_REGNUM); -#endif -#endif -} - -/* Output assembler code for the end of a function. - For clarity, args are same as those of `final_start_function' - even though not all of them are needed. */ - -void -final_end_function (first, file, write_symbols, optimize) - rtx first; - FILE *file; - enum debugger write_symbols; - int optimize; -{ - if (app_on) - { - fprintf (file, ASM_APP_OFF); - app_on = 0; - } - - if (write_symbols == GDB_DEBUG) - fprintf (file, "\t.gdbend %d\n", pending_blocks[0]); - -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG) - sdbout_end_function (last_linenum); -#endif - -#ifdef FUNCTION_EPILOGUE - /* Finally, output the function epilogue: - code to restore the stack frame and return to the caller. */ - FUNCTION_EPILOGUE (file, get_frame_size ()); -#endif - -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG) - sdbout_end_epilogue (); -#endif - - /* If FUNCTION_EPILOGUE is not defined, then the function body - itself contains return instructions wherever needed. */ -} - -/* Output assembler code for some insns: all or part of a function. - For description of args, see `final_start_function', above. - - PRESCAN is 1 if we are not really outputting, - just scanning as if we were outputting. - Prescanning deletes and rearranges insns just like ordinary output. - PRESCAN is -2 if we are outputting after having prescanned. - In this case, don't try to delete or rearrange insns - because that has already been done. - Prescanning is done only on certain machines. */ - -void -final (first, file, write_symbols, optimize, prescan) - rtx first; - FILE *file; - enum debugger write_symbols; - int optimize; - int prescan; -{ - register rtx insn; - - last_ignored_compare = 0; - new_block = 1; - - init_recog (); - - CC_STATUS_INIT; - - for (insn = NEXT_INSN (first); insn;) - insn = final_scan_insn (insn, file, write_symbols, optimize, - prescan, 0); -} - -/* The final scan for one insn, INSN. - Args are same as in `final', except that INSN - is the insn being scanned. - Value returned is the next insn to be scanned. - - NOPEEPHOLES is the flag to disallow peephole processing (currently - used for within delayed branch sequence output). */ - -rtx -final_scan_insn (insn, file, write_symbols, optimize, prescan, nopeepholes) - rtx insn; - FILE *file; - enum debugger write_symbols; - int optimize; - int prescan; - int nopeepholes; -{ - register int i; - switch (GET_CODE (insn)) - { - case NOTE: - if (prescan > 0) - break; - if (write_symbols == NO_DEBUG) - break; - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG) - { -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG) - sdbout_begin_function (last_linenum); -#endif - break; - } - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG - || NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END) - break; - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED) - break; /* An insn that was "deleted" */ - if (app_on) - { - fprintf (file, ASM_APP_OFF); - app_on = 0; - } - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG) - { - /* Beginning of a symbol-block. Assign it a sequence number - and push the number onto the stack PENDING_BLOCKS. */ - - if (block_depth == max_block_depth) - { - /* PENDING_BLOCKS is full; make it longer. */ - max_block_depth *= 2; - pending_blocks - = (int *) xrealloc (pending_blocks, - max_block_depth * sizeof (int)); - } - pending_blocks[block_depth++] = next_block_index; - - /* Output debugging info about the symbol-block beginning. */ - -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG) - sdbout_begin_block (file, last_linenum, next_block_index); -#endif -#ifdef DBX_DEBUGGING_INFO - if (write_symbols == DBX_DEBUG) - ASM_OUTPUT_INTERNAL_LABEL (file, "LBB", next_block_index); -#endif - if (write_symbols == GDB_DEBUG) - fprintf (file, "\t.gdbbeg %d\n", next_block_index); - - next_block_index++; - } - else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END) - { - /* End of a symbol-block. Pop its sequence number off - PENDING_BLOCKS and output debugging info based on that. */ - - --block_depth; - -#ifdef DBX_DEBUGGING_INFO - if (write_symbols == DBX_DEBUG && block_depth >= 0) - ASM_OUTPUT_INTERNAL_LABEL (file, "LBE", - pending_blocks[block_depth]); -#endif - -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG && block_depth >= 0) - sdbout_end_block (file, last_linenum); -#endif - - if (write_symbols == GDB_DEBUG) - fprintf (file, "\t.gdbend %d\n", pending_blocks[block_depth]); - } - else if (NOTE_LINE_NUMBER (insn) > 0) - /* This note is a line-number. */ - output_source_line (file, insn, write_symbols); - break; - - case BARRIER: -#ifdef ASM_OUTPUT_ALIGN_CODE - ASM_OUTPUT_ALIGN_CODE (file); -#endif - break; - - case CODE_LABEL: - CC_STATUS_INIT; - if (prescan > 0) - break; - new_block = 1; - if (app_on) - { - fprintf (file, ASM_APP_OFF); - app_on = 0; - } -#ifdef ASM_OUTPUT_CASE_LABEL - if (NEXT_INSN (insn) != 0 - && GET_CODE (NEXT_INSN (insn)) == JUMP_INSN) - { - rtx nextbody = PATTERN (NEXT_INSN (insn)); - - /* If this label is followed by a jump-table, - output the two of them together in a special way. */ - - if (GET_CODE (nextbody) == ADDR_VEC - || GET_CODE (nextbody) == ADDR_DIFF_VEC) - { - ASM_OUTPUT_CASE_LABEL (file, "L", CODE_LABEL_NUMBER (insn), - NEXT_INSN (insn)); - break; - } - } -#endif - - ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (insn)); - break; - - default: - { - register rtx body = PATTERN (insn); - int insn_code_number; - char *template; - - /* An INSN, JUMP_INSN or CALL_INSN. - First check for special kinds that recog doesn't recognize. */ - - if (GET_CODE (body) == USE /* These are just declarations */ - || GET_CODE (body) == CLOBBER) - break; - - if (profile_block_flag && new_block) - { - rtx real_body = body; - if (GET_CODE (insn) == NOTE) - real_body = PATTERN (next_real_insn (insn)); - - /* Don't add instructions in front of jump tables. */ - if (GET_CODE (real_body) != ADDR_VEC - && GET_CODE (real_body) != ADDR_DIFF_VEC) - { - new_block = 0; - /* Enable the table of basic-block use counts - to point at the code it applies to. */ - ASM_OUTPUT_INTERNAL_LABEL (file, "LPB", count_basic_blocks); - /* Before first insn of this basic block, increment the - count of times it was entered. */ -#ifdef BLOCK_PROFILER - BLOCK_PROFILER (file, count_basic_blocks); -#endif - count_basic_blocks++; - } - } - - if (GET_CODE (body) == ASM_INPUT) - { - /* There's no telling what that did to the condition codes. */ - CC_STATUS_INIT; - if (prescan > 0) - break; - if (! app_on) - { - fprintf (file, ASM_APP_ON); - app_on = 1; - } - fprintf (asm_out_file, "\t%s\n", XSTR (body, 0)); - break; - } - - /* Detect `asm' construct with operands. */ - if (asm_noperands (body) >= 0) - { - int noperands = asm_noperands (body); - rtx *ops; - char *string; - - /* There's no telling what that did to the condition codes. */ - CC_STATUS_INIT; - if (prescan > 0) - break; - - /* alloca won't do here, since only return from `final' - would free it. */ - if (noperands > 0) - ops = (rtx *) xmalloc (noperands * sizeof (rtx)); - - if (! app_on) - { - fprintf (file, ASM_APP_ON); - app_on = 1; - } - - /* Get out the operand values. */ - string = decode_asm_operands (body, ops, 0, 0, 0); - /* Inhibit aborts on what would otherwise be compiler bugs. */ - insn_noperands = noperands; - this_is_asm_operands = insn; - /* Output the insn using them. */ - output_asm_insn (string, ops); - this_is_asm_operands = 0; - if (noperands > 0) - free (ops); - break; - } - - if (prescan <= 0 && app_on) - { - fprintf (file, ASM_APP_OFF); - app_on = 0; - } - - /* Detect insns that are really jump-tables - and output them as such. */ - - if (GET_CODE (body) == ADDR_VEC) - { - register int vlen, idx; - - if (prescan > 0) - break; - - vlen = XVECLEN (body, 0); - for (idx = 0; idx < vlen; idx++) - ASM_OUTPUT_ADDR_VEC_ELT (file, - CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 0, idx), 0))); -#ifdef ASM_OUTPUT_CASE_END - ASM_OUTPUT_CASE_END (file, - CODE_LABEL_NUMBER (PREV_INSN (insn)), - insn); -#endif - break; - } - if (GET_CODE (body) == ADDR_DIFF_VEC) - { - register int vlen, idx; - - if (prescan > 0) - break; - - vlen = XVECLEN (body, 1); - for (idx = 0; idx < vlen; idx++) - ASM_OUTPUT_ADDR_DIFF_ELT (file, - CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 1, idx), 0)), - CODE_LABEL_NUMBER (XEXP (XEXP (body, 0), 0))); -#ifdef ASM_OUTPUT_CASE_END - ASM_OUTPUT_CASE_END (file, - CODE_LABEL_NUMBER (PREV_INSN (insn)), - insn); -#endif - break; - } - - if (recog_memoized (insn) == -1 - && GET_CODE (body) == SEQUENCE) /* A delayed-branch sequence */ - { - register int i; - if (prescan > 0) - break; - final_sequence = body; - for (i = 0; i < XVECLEN (body, 0); i++) - final_scan_insn (XVECEXP (body, 0, i), file, write_symbols, - optimize, prescan, 1); - final_sequence = 0; -#ifdef DBR_OUTPUT_SEQEND - DBR_OUTPUT_SEQEND (file); -#endif - break; - } - - /* We have a real machine instruction as rtl. */ - - body = PATTERN (insn); - - /* Check for redundant test and compare instructions - (when the condition codes are already set up as desired). - This is done only when optimizing; if not optimizing, - it should be possible for the user to alter a variable - with the debugger in between statements - and the next statement should reexamine the variable - to compute the condition codes. */ - - if (optimize - && GET_CODE (body) == SET - && GET_CODE (SET_DEST (body)) == CC0 - && insn != last_ignored_compare) - { - if (GET_CODE (SET_SRC (body)) == SUBREG) - SET_SRC (body) = alter_subreg (SET_SRC (body)); - if ((cc_status.value1 != 0 - && rtx_equal_p (SET_SRC (body), cc_status.value1)) - || (cc_status.value2 != 0 - && rtx_equal_p (SET_SRC (body), cc_status.value2))) - { - /* Don't delete insn if has an addressing side-effect */ - if (! find_reg_note (insn, REG_INC, 0) - /* or if anything in it is volatile. */ - && ! volatile_refs_p (PATTERN (insn))) - { - /* We don't really delete the insn; just ignore it. */ - last_ignored_compare = insn; - break; - } - } - } - - /* Following a conditional branch, we have a new basic block. */ - if (GET_CODE (insn) == JUMP_INSN && GET_CODE (body) == SET - && GET_CODE (SET_SRC (body)) != LABEL_REF) - new_block = 1; - - /* If this is a conditional branch, maybe modify it - if the cc's are in a nonstandard state - so that it accomplishes the same thing that it would - do straightforwardly if the cc's were set up normally. */ - - if (cc_status.flags != 0 - && GET_CODE (insn) == JUMP_INSN - && GET_CODE (body) == SET - && SET_DEST (body) == pc_rtx - && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE - /* This is done during prescan; it is not done again - in final scan when prescan has been done. */ - && prescan >= 0) - { - /* This function may alter the contents of its argument - and clear some of the cc_status.flags bits. - It may also return 1 meaning condition now always true - or -1 meaning condition now always false - or 2 meaning condition nontrivial but altered. */ - register int result = alter_cond (XEXP (SET_SRC (body), 0)); - /* If condition now has fixed value, replace the IF_THEN_ELSE - with its then-operand or its else-operand. */ - if (result == 1) - SET_SRC (body) = XEXP (SET_SRC (body), 1); - if (result == -1) - SET_SRC (body) = XEXP (SET_SRC (body), 2); - /* The jump is now either unconditional or a no-op. - If it has become a no-op, don't try to output it. - (It would not be recognized.) */ - if (SET_SRC (body) == pc_rtx) - { - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - break; - } - /* Rerecognize the instruction if it has changed. */ - if (result != 0) - INSN_CODE (insn) = -1; - } - -#ifdef STORE_FLAG_VALUE - /* Make same adjustments to instructions that examine the - condition codes without jumping (if this machine has them). */ - - if (cc_status.flags != 0 - && GET_CODE (body) == SET) - switch (GET_CODE (SET_SRC (body))) - { - case GTU: - case GT: - case LTU: - case LT: - case GEU: - case GE: - case LEU: - case LE: - case EQ: - case NE: - { - register int result; - if (GET_CODE (XEXP (SET_SRC (body), 0)) != CC0) - break; - result = alter_cond (SET_SRC (body)); - if (result == 1) - SET_SRC (body) = gen_rtx (CONST_INT, VOIDmode, - STORE_FLAG_VALUE); - if (result == -1) - SET_SRC (body) = const0_rtx; - if (result != 0) - INSN_CODE (insn) = -1; - } - } -#endif /* STORE_FLAG_VALUE */ - - /* Do machine-specific peephole optimizations if desired. */ - - if (optimize && !flag_no_peephole && !nopeepholes) - { - rtx next = peephole (insn); - /* When peepholing, if there were notes within the peephole, - emit them before the peephole. */ - if (next != 0 && next != NEXT_INSN (insn)) - { - rtx note = NEXT_INSN (insn); - rtx prev = PREV_INSN (insn); - while (note != next) - { - final_scan_insn (note, file, write_symbols, optimize, - prescan, nopeepholes); - note = NEXT_INSN (note); - } - /* In case this is prescan, put the notes - in proper position for later rescan. */ - note = NEXT_INSN (insn); - PREV_INSN (note) = prev; - NEXT_INSN (prev) = note; - NEXT_INSN (PREV_INSN (next)) = insn; - PREV_INSN (insn) = PREV_INSN (next); - NEXT_INSN (insn) = next; - PREV_INSN (next) = insn; - } - - /* PEEPHOLE might have changed this. */ - body = PATTERN (insn); - } - - /* Try to recognize the instruction. - If successful, verify that the operands satisfy the - constraints for the instruction. Crash if they don't, - since `reload' should have changed them so that they do. */ - - insn_code_number = recog_memoized (insn); - insn_extract (insn); - for (i = 0; i < insn_n_operands[insn_code_number]; i++) - { - if (GET_CODE (recog_operand[i]) == SUBREG) - recog_operand[i] = alter_subreg (recog_operand[i]); - } - -#ifdef REGISTER_CONSTRAINTS - if (! constrain_operands (insn_code_number)) - abort (); -#endif - - /* Some target machines need to prescan each insn before - it is output. */ - -#ifdef FINAL_PRESCAN_INSN - FINAL_PRESCAN_INSN (insn, recog_operand, - insn_n_operands[insn_code_number]); -#endif - - cc_prev_status = cc_status; - - /* Update `cc_status' for this instruction. - The instruction's output routine may change it further. - If the output routine for a jump insn needs to depend - on the cc status, it should look at cc_prev_status. */ - - NOTICE_UPDATE_CC (body, insn); - - /* If the proper template needs to be chosen by some C code, - run that code and get the real template. */ - - template = insn_template[insn_code_number]; - if (template == 0) - { - template = (*insn_outfun[insn_code_number]) (recog_operand, insn); - - /* If the C code returns 0, it means that it is a jump insn - which follows a deleted test insn, and that test insn - needs to be reinserted. */ - if (template == 0) - { - if (PREV_INSN (insn) != last_ignored_compare) - abort (); - new_block = 0; - return PREV_INSN (insn); - } - } - - if (prescan > 0) - break; - - /* Output assembler code from the template. */ - - output_asm_insn (template, recog_operand); - - /* Mark this insn as having been output. */ - INSN_DELETED_P (insn) = 1; - } - } - return NEXT_INSN (insn); -} - -/* Set up FILENAME as the current file for GDB line-number output. */ - -void -set_current_gdbfile (filename) - char *filename; -{ - register struct gdbfile *f; - for (f = gdbfiles; f; f = f->next) - if (!strcmp (f->name, filename)) - break; - - if (!f) - { - f = (struct gdbfile *) permalloc (sizeof (struct gdbfile)); - f->next = gdbfiles; - gdbfiles = f; - f->name = filename; - f->filenum = next_gdb_filenum++; - f->nlines = 0; - } - current_gdbfile = f; - lastfile = filename; -} - -/* Output debugging info to the assembler file FILE - based on the NOTE-insn INSN, assumed to be a line number. */ - -static void -output_source_line (file, insn, write_symbols) - FILE *file; - rtx insn; - enum debugger write_symbols; -{ - register char *filename = NOTE_SOURCE_FILE (insn); - - last_linenum = NOTE_LINE_NUMBER (insn); - - if (write_symbols == GDB_DEBUG) - { - /* Output GDB-format line number info. */ - - /* If this is not the same source file as last time, - find or assign a GDB-file-number to this file. */ - if (filename && (lastfile == 0 || strcmp (filename, lastfile) - || current_gdbfile == 0)) - set_current_gdbfile (filename); - - ++current_gdbfile->nlines; - fprintf (file, "\t.gdbline %d,%d\n", - current_gdbfile->filenum, NOTE_LINE_NUMBER (insn)); - } - - if (write_symbols == SDB_DEBUG || write_symbols == DBX_DEBUG) - { -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG -#if 0 /* People like having line numbers even in wrong file! */ - /* COFF can't handle multiple source files--lose, lose. */ - && !strcmp (filename, main_input_filename) -#endif - /* COFF relative line numbers must be positive. */ - && last_linenum > sdb_begin_function_line) - { -#ifdef ASM_OUTPUT_SOURCE_LINE - ASM_OUTPUT_SOURCE_LINE (file, last_linenum); -#else - fprintf (file, "\t.ln\t%d\n", - (sdb_begin_function_line - ? last_linenum - sdb_begin_function_line : 1)); -#endif - } -#endif - -#ifdef DBX_DEBUGGING_INFO - if (write_symbols == DBX_DEBUG) - { - /* Write DBX line number data. */ - - if (filename && (lastfile == 0 || strcmp (filename, lastfile))) - { -#ifdef ASM_OUTPUT_SOURCE_FILENAME - ASM_OUTPUT_SOURCE_FILENAME (file, filename); -#else - fprintf (file, "\t.stabs \"%s\",%d,0,0,Ltext\n", - filename, N_SOL); -#endif - lastfile = filename; - } - } - -#ifdef ASM_OUTPUT_SOURCE_LINE - ASM_OUTPUT_SOURCE_LINE (file, NOTE_LINE_NUMBER (insn)); -#else - fprintf (file, "\t.stabd %d,0,%d\n", - N_SLINE, NOTE_LINE_NUMBER (insn)); -#endif -#endif /* DBX_DEBUGGING_INFO */ - } -} - -/* If X is a SUBREG, replace it with a REG or a MEM, - based on the thing it is a subreg of. */ - -rtx -alter_subreg (x) - register rtx x; -{ - register rtx y = SUBREG_REG (x); - if (GET_CODE (y) == SUBREG) - y = alter_subreg (y); - - if (GET_CODE (y) == REG) - { - /* If the containing reg really gets a hard reg, so do we. */ - PUT_CODE (x, REG); - REGNO (x) = REGNO (y) + SUBREG_WORD (x); - } - else if (GET_CODE (y) == MEM) - { - register int offset = SUBREG_WORD (x) * UNITS_PER_WORD; -#ifdef BYTES_BIG_ENDIAN - offset -= (min (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x))) - - min (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (y)))); -#endif - PUT_CODE (x, MEM); - MEM_VOLATILE_P (x) = MEM_VOLATILE_P (y); - XEXP (x, 0) = plus_constant (XEXP (y, 0), offset); - } - else if (GET_CODE (y) == CONST_DOUBLE) - return y; - - return x; -} - -/* Do alter_subreg on all the SUBREGs contained in X. */ - -static rtx -walk_alter_subreg (x) - rtx x; -{ - switch (GET_CODE (x)) - { - case PLUS: - case MULT: - XEXP (x, 0) = walk_alter_subreg (XEXP (x, 0)); - XEXP (x, 1) = walk_alter_subreg (XEXP (x, 1)); - break; - - case MEM: - XEXP (x, 0) = walk_alter_subreg (XEXP (x, 0)); - break; - - case SUBREG: - return alter_subreg (x); - } - - return x; -} - -/* Given BODY, the body of a jump instruction, alter the jump condition - as required by the bits that are set in cc_status.flags. - Not all of the bits there can be handled at this level in all cases. - - The value is normally 0. - 1 means that the condition has become always true. - -1 means that the condition has become always false. - 2 means that COND has been altered. */ - -static int -alter_cond (cond) - register rtx cond; -{ - int value = 0; - - if (cc_status.flags & CC_REVERSED) - { - value = 2; - switch (GET_CODE (cond)) - { - case LE: - PUT_CODE (cond, GE); - break; - case GE: - PUT_CODE (cond, LE); - break; - case LT: - PUT_CODE (cond, GT); - break; - case GT: - PUT_CODE (cond, LT); - break; - case LEU: - PUT_CODE (cond, GEU); - break; - case GEU: - PUT_CODE (cond, LEU); - break; - case LTU: - PUT_CODE (cond, GTU); - break; - case GTU: - PUT_CODE (cond, LTU); - break; - } - } - - if (cc_status.flags & CC_NOT_POSITIVE) - switch (GET_CODE (cond)) - { - case LE: - case LEU: - case GEU: - /* Jump becomes unconditional. */ - return 1; - - case GT: - case GTU: - case LTU: - /* Jump becomes no-op. */ - return -1; - - case GE: - PUT_CODE (cond, EQ); - value = 2; - break; - - case LT: - PUT_CODE (cond, NE); - value = 2; - break; - } - - if (cc_status.flags & CC_NOT_NEGATIVE) - switch (GET_CODE (cond)) - { - case GE: - case GEU: - /* Jump becomes unconditional. */ - return 1; - - case LT: - case LTU: - /* Jump becomes no-op. */ - return -1; - - case LE: - case LEU: - PUT_CODE (cond, EQ); - value = 2; - break; - - case GT: - case GTU: - PUT_CODE (cond, NE); - value = 2; - break; - } - - if (cc_status.flags & CC_NO_OVERFLOW) - switch (GET_CODE (cond)) - { - case GEU: - /* Jump becomes unconditional. */ - return 1; - - case LEU: - PUT_CODE (cond, EQ); - value = 2; - break; - - case GTU: - PUT_CODE (cond, NE); - value = 2; - break; - - case LTU: - /* Jump becomes no-op. */ - return -1; - } - - if (cc_status.flags & (CC_Z_IN_NOT_N | CC_Z_IN_N)) - switch (GET_CODE (cond)) - { - case LE: - case LEU: - case GE: - case GEU: - case LT: - case LTU: - case GT: - case GTU: - abort (); - - case NE: - PUT_CODE (cond, cc_status.flags & CC_Z_IN_N ? GE : LT); - value = 2; - break; - - case EQ: - PUT_CODE (cond, cc_status.flags & CC_Z_IN_N ? LT : GE); - value = 2; - break; - } - - return value; -} - -/* Report inconsistency between the assembler template and the operands. - In an `asm', it's the user's fault; otherwise, the compiler's fault. */ - -static void -output_operand_lossage (str) - char *str; -{ - if (this_is_asm_operands) - error_for_asm (this_is_asm_operands, "invalid `asm': %s", str); - else - abort (); -} - -/* Output of assembler code from a template, and its subroutines. */ - -/* Output text from TEMPLATE to the assembler output file, - obeying %-directions to substitute operands taken from - the vector OPERANDS. - - %N (for N a digit) means print operand N in usual manner. - %lN means require operand N to be a CODE_LABEL or LABEL_REF - and print the label name with no punctuation. - %cN means require operand N to be a constant - and print the constant expression with no punctuation. - %aN means expect operand N to be a memory address - (not a memory reference!) and print a reference - to that address. - %nN means expect operand N to be a constant - and print a constant expression for minus the value - of the operand, with no other punctuation. */ - -void -output_asm_insn (template, operands) - char *template; - rtx *operands; -{ - register char *p; - register int c; - - /* An insn may return a null string template - in a case where no assembler code is needed. */ - if (*template == 0) - return; - - p = template; - putc ('\t', asm_out_file); - -#ifdef ASM_OUTPUT_OPCODE - ASM_OUTPUT_OPCODE (asm_out_file, p); -#endif - - while (c = *p++) - { -#ifdef ASM_OUTPUT_OPCODE - if (c == '\n') - { - putc (c, asm_out_file); - while ((c = *p) == '\t') - { - putc (c, asm_out_file); - p++; - } - ASM_OUTPUT_OPCODE (asm_out_file, p); - } - else -#endif - if (c != '%') - putc (c, asm_out_file); - else - { - /* %% outputs a single %. */ - if (*p == '%') - { - p++; - putc (c, asm_out_file); - } - /* % followed by a letter and some digits - outputs an operand in a special way depending on the letter. - Letters `acln' are implemented here. - Other letters are passed to `output_operand' so that - the PRINT_OPERAND macro can define them. */ - else if ((*p >= 'a' && *p <= 'z') - || (*p >= 'A' && *p <= 'Z')) - { - int letter = *p++; - c = atoi (p); - - if (! (*p >= '0' && *p <= '9')) - output_operand_lossage ("operand number missing after %-letter"); - else if (this_is_asm_operands && c >= (unsigned) insn_noperands) - output_operand_lossage ("operand number out of range"); - else if (letter == 'l') - output_asm_label (operands[c]); - else if (letter == 'a') - output_address (operands[c]); - else if (letter == 'c') - { - if (CONSTANT_ADDRESS_P (operands[c])) - output_addr_const (asm_out_file, operands[c]); - else - output_operand (operands[c], 'c'); - } - else if (letter == 'n') - { - if (GET_CODE (operands[c]) == CONST_INT) - fprintf (asm_out_file, "%d", - INTVAL (operands[c])); - else - { - putc ('-', asm_out_file); - output_addr_const (asm_out_file, operands[c]); - } - } - else - output_operand (operands[c], letter); - - while ((c = *p) >= '0' && c <= '9') p++; - } - /* % followed by a digit outputs an operand the default way. */ - else if (*p >= '0' && *p <= '9') - { - c = atoi (p); - if (this_is_asm_operands && c >= (unsigned) insn_noperands) - output_operand_lossage ("operand number out of range"); - else - output_operand (operands[c], 0); - while ((c = *p) >= '0' && c <= '9') p++; - } - /* % followed by punctuation: output something for that - punctuation character alone, with no operand. - The PRINT_OPERAND macro decides what is actually done. */ -#ifdef PRINT_OPERAND_PUNCT_VALID_P - else if (PRINT_OPERAND_PUNCT_VALID_P (*p)) - output_operand (0, *p++); -#endif - else - output_operand_lossage ("invalid %%-code"); - } - } - - putc ('\n', asm_out_file); -} - -/* Output a LABEL_REF, or a bare CODE_LABEL, as an assembler symbol. */ - -void -output_asm_label (x) - rtx x; -{ - char buf[256]; - - if (GET_CODE (x) == LABEL_REF) - ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (XEXP (x, 0))); - else if (GET_CODE (x) == CODE_LABEL) - ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x)); - else - output_operand_lossage ("`%l' operand isn't a label"); - - assemble_name (asm_out_file, buf); -} - -/* Print operand X using machine-dependent assembler syntax. - The macro PRINT_OPERAND is defined just to control this function. - CODE is a non-digit that preceded the operand-number in the % spec, - such as 'z' if the spec was `%z3'. CODE is 0 if there was no char - between the % and the digits. - When CODE is a non-letter, X is 0. - - The meanings of the letters are machine-dependent and controlled - by PRINT_OPERAND. */ - -static void -output_operand (x, code) - rtx x; - int code; -{ - if (x && GET_CODE (x) == SUBREG) - x = alter_subreg (x); - PRINT_OPERAND (asm_out_file, x, code); -} - -/* Print a memory reference operand for address X - using machine-dependent assembler syntax. - The macro PRINT_OPERAND_ADDRESS exists just to control this function. */ - -void -output_address (x) - rtx x; -{ - walk_alter_subreg (x); - PRINT_OPERAND_ADDRESS (asm_out_file, x); -} - -/* Print an integer constant expression in assembler syntax. - Addition and subtraction are the only arithmetic - that may appear in these expressions. */ - -void -output_addr_const (file, x) - FILE *file; - rtx x; -{ - char buf[256]; - - restart: - switch (GET_CODE (x)) - { - case SYMBOL_REF: - assemble_name (file, XSTR (x, 0)); - break; - - case LABEL_REF: - ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (XEXP (x, 0))); - assemble_name (asm_out_file, buf); - break; - - case CODE_LABEL: - ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x)); - assemble_name (asm_out_file, buf); - break; - - case CONST_INT: - fprintf (file, "%d", INTVAL (x)); - break; - - case CONST: - x = XEXP (x, 0); - goto restart; - - case CONST_DOUBLE: - if (GET_MODE (x) == DImode) - { - /* We can use %d if the number is <32 bits and positive. */ - if (CONST_DOUBLE_HIGH (x) || CONST_DOUBLE_LOW (x) < 0) - fprintf (file, "0x%x%08x", - CONST_DOUBLE_HIGH (x), CONST_DOUBLE_LOW (x)); - else - fprintf (file, "%d", CONST_DOUBLE_LOW (x)); - } - else - /* We can't handle floating point constants; - PRINT_OPERAND must handle them. */ - output_operand_lossage ("floating constant misused"); - break; - - case PLUS: - /* Some assemblers need integer constants to appear last (eg masm). */ - if (GET_CODE (XEXP (x, 0)) == CONST_INT) - { - output_addr_const (file, XEXP (x, 1)); - if (INTVAL (XEXP (x, 0)) >= 0) - fprintf (file, "+"); - output_addr_const (file, XEXP (x, 0)); - } - else - { - output_addr_const (file, XEXP (x, 0)); - if (INTVAL (XEXP (x, 1)) >= 0) - fprintf (file, "+"); - output_addr_const (file, XEXP (x, 1)); - } - break; - - case MINUS: - output_addr_const (file, XEXP (x, 0)); - fprintf (file, "-"); - output_addr_const (file, XEXP (x, 1)); - break; - - default: - output_operand_lossage ("invalid expression as operand"); - } -} diff --git a/gnu/usr.bin/gcc1/cc1/flags.h b/gnu/usr.bin/gcc1/cc1/flags.h deleted file mode 100644 index f9c218f2a2..0000000000 --- a/gnu/usr.bin/gcc1/cc1/flags.h +++ /dev/null @@ -1,191 +0,0 @@ -/* Compilation switch flag definitions for GNU CC. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Name of the input .c file being compiled. */ -extern char *main_input_filename; - -/* 1 => write gdb debugging output (using symout.c). - 2 => write dbx debugging output (using dbxout.c). - 3 => write sdb debugging output (using sdbout.c). */ -enum debugger { NO_DEBUG = 0, GDB_DEBUG = 1, DBX_DEBUG = 2, SDB_DEBUG = 3, - EXTENDED_DBX_DEBUG = 4 }; - -extern enum debugger write_symbols; - -/* Nonzero means use GDB-only extensions of DBX format. */ -extern int use_gdb_dbx_extensions; - -/* Nonzero means do optimizations. -opt. */ - -extern int optimize; - -/* Nonzero means do stupid register allocation. -noreg. - This and `optimize' are controlled by different switches in cc1, - but normally cc controls them both with the -O switch. */ - -extern int obey_regdecls; - -/* Don't print functions as they are compiled and don't print - times taken by the various passes. -quiet. */ - -extern int quiet_flag; - -/* Don't print warning messages. -w. */ - -extern int inhibit_warnings; - -/* Do print extra warnings (such as for uninitialized variables). -W. */ - -extern int extra_warnings; - -/* Nonzero to warn about unused local variables. */ - -extern int warn_unused; - -/* Nonzero means warn about all declarations which shadow others. */ - -extern int warn_shadow; - -/* Warn if a switch on an enum fails to have a case for every enum value. */ - -extern int warn_switch; - -/* Nonzero means warn about any identifiers that match in the first N - characters. The value N is in `id_clash_len'. */ - -extern int warn_id_clash; -extern int id_clash_len; - -/* Nonzero if generating code to do profiling. */ - -extern int profile_flag; - -/* Nonzero if generating code to do profiling on the basis of basic blocks. */ - -extern int profile_block_flag; - -/* Nonzero for -pedantic switch: warn about anything - that standard C forbids. */ - -extern int pedantic; - -/* Now the symbols that are set with `-f' switches. */ - -/* Nonzero means `char' should be signed. */ - -extern int flag_signed_char; - -/* Nonzero means give an enum type only as many bytes as it needs. */ - -extern int flag_short_enums; - -/* Nonzero for -fcaller-saves: allocate values in regs that need to - be saved across function calls, if that produces overall better code. - Optional now, so people can test it. */ - -extern int flag_caller_saves; - -/* Nonzero for -fpcc-struct-return: return values the same way PCC does. */ - -extern int flag_pcc_struct_return; - -/* Nonzero for -fforce-mem: load memory value into a register - before arithmetic on it. This makes better cse but slower compilation. */ - -extern int flag_force_mem; - -/* Nonzero for -fforce-addr: load memory address into a register before - reference to memory. This makes better cse but slower compilation. */ - -extern int flag_force_addr; - -/* Nonzero for -fdefer-pop: don't pop args after each function call; - instead save them up to pop many calls' args with one insns. */ - -extern int flag_defer_pop; - -/* Nonzero for -ffloat-store: don't allocate floats and doubles - in extended-precision registers. */ - -extern int flag_float_store; - -/* Nonzero for -fcombine-regs: - allow instruction combiner to combine an insn - that just copies one reg to another. */ - -extern int flag_combine_regs; - -/* Nonzero enables strength-reduction in loop.c. */ - -extern int flag_strength_reduce; - -/* Nonzero for -fwritable-strings: - store string constants in data segment and don't uniquize them. */ - -extern int flag_writable_strings; - -/* Nonzero means don't put addresses of constant functions in registers. - Used for compiling the Unix kernel, where strange substitutions are - done on the assembly output. */ - -extern int flag_no_function_cse; - -/* Nonzero for -fomit-frame-pointer: - don't make a frame pointer in simple functions that don't require one. */ - -extern int flag_omit_frame_pointer; - -/* This isn't a flag, but everyone who needs flag_omit_frame_pointer - also needs this. - Nonzero means current function must be given a frame pointer. - Set in stmt.c if anything is allocated on the stack there. - Set in reload1.c if anything is allocated on the stack there. */ - -extern int frame_pointer_needed; - -/* Nonzero to inhibit use of define_optimization peephole opts. */ - -extern int flag_no_peephole; - -/* Nonzero means all references through pointers are volatile. */ - -extern int flag_volatile; - -/* Nonzero means make functions that look like good inline candidates - go inline. */ - -extern int flag_inline_functions; - -/* Nonzero for -fkeep-inline-functions: even if we make a function - go inline everywhere, keep its defintion around for debugging - purposes. */ - -extern int flag_keep_inline_functions; - -/* Nonzero if we are only using compiler to check syntax errors. */ - -extern int flag_syntax_only; - -/* Nonzero means make the text shared if supported. */ - -extern int flag_shared_data; - -/* Nonzero means put things in delayed-branch slots if supported. */ - -extern int flag_delayed_branch; diff --git a/gnu/usr.bin/gcc1/cc1/flow.c b/gnu/usr.bin/gcc1/cc1/flow.c deleted file mode 100644 index 956c960962..0000000000 --- a/gnu/usr.bin/gcc1/cc1/flow.c +++ /dev/null @@ -1,2094 +0,0 @@ -/* Data flow analysis for GNU compiler. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This file contains the data flow analysis pass of the compiler. - It computes data flow information - which tells combine_instructions which insns to consider combining - and controls register allocation. - - Additional data flow information that is too bulky to record - is generated during the analysis, and is used at that time to - create autoincrement and autodecrement addressing. - - The first step is dividing the function into basic blocks. - find_basic_blocks does this. Then life_analysis determines - where each register is live and where it is dead. - - ** find_basic_blocks ** - - find_basic_blocks divides the current function's rtl - into basic blocks. It records the beginnings and ends of the - basic blocks in the vectors basic_block_head and basic_block_end, - and the number of blocks in n_basic_blocks. - - find_basic_blocks also finds any unreachable loops - and deletes them. - - ** life_analysis ** - - life_analysis is called immediately after find_basic_blocks. - It uses the basic block information to determine where each - hard or pseudo register is live. - - ** live-register info ** - - The information about where each register is live is in two parts: - the REG_NOTES of insns, and the vector basic_block_live_at_start. - - basic_block_live_at_start has an element for each basic block, - and the element is a bit-vector with a bit for each hard or pseudo - register. The bit is 1 if the register is live at the beginning - of the basic block. - - To each insn's REG_NOTES is added an element for each register - that is live before the insn or set by the insn, but is dead - after the insn. - - To determine which registers are live after any insn, one can - start from the beginning of the basic block and scan insns, noting - which registers are set by each insn and which die there. - - ** Other actions of life_analysis ** - - life_analysis sets up the LOG_LINKS fields of insns because the - information needed to do so is readily available. - - life_analysis deletes insns whose only effect is to store a value - that is never used. - - life_analysis notices cases where a reference to a register as - a memory address can be combined with a preceding or following - incrementation or decrementation of the register. The separate - instruction to increment or decrement is deleted and the address - is changed to a POST_INC or similar rtx. - - Each time an incrementing or decrementing address is created, - a REG_INC element is added to the insn's REG_NOTES list. - - life_analysis fills in certain vectors containing information about - register usage: reg_n_refs, reg_n_deaths, reg_n_sets, - reg_live_length, reg_n_calls_crosses and reg_basic_block. */ - -#include -#include "config.h" -#include "rtl.h" -#include "basic-block.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "flags.h" - -#include "obstack.h" -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free - -extern int xmalloc (); -extern void free (); - -/* Get the basic block number of an insn. - This info should not be expected to remain available - after the end of life_analysis. */ - -#define BLOCK_NUM(INSN) uid_block_number[INSN_UID (INSN)] - -/* This is where the BLOCK_NUM values are really stored. - This is set up by find_basic_blocks and used there and in life_analysis, - and then freed. */ - -static short *uid_block_number; - -/* INSN_VOLATILE (insn) is 1 if the insn refers to anything volatile. */ - -#define INSN_VOLATILE(INSN) uid_volatile[INSN_UID (INSN)] -static char *uid_volatile; - -/* Number of basic blocks in the current function. */ - -int n_basic_blocks; - -/* Maximum register number used in this function, plus one. */ - -int max_regno; - -/* Indexed by n, gives number of basic block that (REG n) is used in. - If the value is REG_BLOCK_GLOBAL (-2), - it means (REG n) is used in more than one basic block. - REG_BLOCK_UNKNOWN (-1) means it hasn't been seen yet so we don't know. - This information remains valid for the rest of the compilation - of the current function; it is used to control register allocation. */ - -short *reg_basic_block; - -/* Indexed by n, gives number of times (REG n) is used or set, each - weighted by its loop-depth. - This information remains valid for the rest of the compilation - of the current function; it is used to control register allocation. */ - -short *reg_n_refs; - -/* Indexed by n, gives number of times (REG n) is set. - This information remains valid for the rest of the compilation - of the current function; it is used to control register allocation. */ - -short *reg_n_sets; - -/* Indexed by N, gives number of places register N dies. - This information remains valid for the rest of the compilation - of the current function; it is used to control register allocation. */ - -short *reg_n_deaths; - -/* Indexed by N, gives 1 if that reg is live across any CALL_INSNs. - This information remains valid for the rest of the compilation - of the current function; it is used to control register allocation. */ - -int *reg_n_calls_crossed; - -/* Indexed by N, gives the uid of the first insn that mentions reg N, - provided that reg is local to one basic block. - The value here is undefined otherwise. */ - -rtx *reg_first_use; - -/* Total number of instructions at which (REG n) is live. - The larger this is, the less priority (REG n) gets for - allocation in a real register. - This information remains valid for the rest of the compilation - of the current function; it is used to control register allocation. - - local-alloc.c may alter this number to change the priority. - - Negative values are special. - -1 is used to mark a pseudo reg which has a constant or memory equivalent - and is used infrequently enough that it should not get a hard register. - -2 is used to mark a pseudo reg for a parameter, when a frame pointer - is not required. global-alloc.c makes an allocno for this but does - not try to assign a hard register to it. */ - -int *reg_live_length; - -/* Element N is the next insn that uses (hard or pseudo) register number N - within the current basic block; or zero, if there is no such insn. - This is valid only during the final backward scan in propagate_block. */ - -static rtx *reg_next_use; - -/* Size of a regset for the current function, - in (1) bytes and (2) elements. */ - -int regset_bytes; -int regset_size; - -/* Element N is first insn in basic block N. - This info lasts until we finish compiling the function. */ - -rtx *basic_block_head; - -/* Element N is last insn in basic block N. - This info lasts until we finish compiling the function. */ - -rtx *basic_block_end; - -/* Element N is a regset describing the registers live - at the start of basic block N. - This info lasts until we finish compiling the function. */ - -regset *basic_block_live_at_start; - -/* Regset of regs live when calls to `setjmp'-like functions happen. */ - -regset regs_live_at_setjmp; - -/* Element N is nonzero if control can drop into basic block N - from the preceding basic block. Freed after life_analysis. */ - -static char *basic_block_drops_in; - -/* Element N is depth within loops of basic block number N. - Freed after life_analysis. */ - -static short *basic_block_loop_depth; - -/* Element N nonzero if basic block N can actually be reached. - Vector exists only during find_basic_blocks. */ - -static char *block_live_static; - -/* Depth within loops of basic block being scanned for lifetime analysis, - plus one. This is the weight attached to references to registers. */ - -static int loop_depth; - -/* Define AUTO_INC_DEC if machine has any kind of incrementing - or decrementing addressing. */ - -#ifdef HAVE_PRE_DECREMENT -#define AUTO_INC_DEC -#endif - -#ifdef HAVE_PRE_INCREMENT -#define AUTO_INC_DEC -#endif - -#ifdef HAVE_POST_DECREMENT -#define AUTO_INC_DEC -#endif - -#ifdef HAVE_POST_INCREMENT -#define AUTO_INC_DEC -#endif - -/* Forward declarations */ -static void find_basic_blocks (); -static void life_analysis (); -static void mark_label_ref (); -void allocate_for_life_analysis (); /* Used also in stupid_life_analysis */ -static void init_regset_vector (); -static void propagate_block (); -static void mark_set_regs (); -static void mark_used_regs (); -static int insn_dead_p (); -static int libcall_dead_p (); -static int try_pre_increment (); -static int try_pre_increment_1 (); -static rtx find_use_as_address (); -void dump_flow_info (); - -/* Find basic blocks of the current function and perform data flow analysis. - F is the first insn of the function and NREGS the number of register numbers - in use. */ - -void -flow_analysis (f, nregs, file) - rtx f; - int nregs; - FILE *file; -{ - register rtx insn; - register int i; - register int max_uid = 0; - - /* Count the basic blocks. Also find maximum insn uid value used. */ - - { - register RTX_CODE prev_code = JUMP_INSN; - register RTX_CODE code; - - for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) - { - code = GET_CODE (insn); - if (INSN_UID (insn) > max_uid) - max_uid = INSN_UID (insn); - if (code == CODE_LABEL - || (prev_code != INSN && prev_code != CALL_INSN - && prev_code != CODE_LABEL - && (code == INSN || code == CALL_INSN || code == JUMP_INSN))) - i++; - if (code != NOTE) - prev_code = code; - } - } - - /* Allocate some tables that last till end of compiling this function - and some needed only in find_basic_blocks and life_analysis. */ - - n_basic_blocks = i; - basic_block_head = (rtx *) oballoc (n_basic_blocks * sizeof (rtx)); - basic_block_end = (rtx *) oballoc (n_basic_blocks * sizeof (rtx)); - basic_block_drops_in = (char *) alloca (n_basic_blocks); - basic_block_loop_depth = (short *) alloca (n_basic_blocks * sizeof (short)); - uid_block_number = (short *) alloca ((max_uid + 1) * sizeof (short)); - uid_volatile = (char *) alloca (max_uid + 1); - bzero (uid_volatile, max_uid + 1); - - find_basic_blocks (f); - life_analysis (f, nregs); - if (file) - dump_flow_info (file); - - basic_block_drops_in = 0; - uid_block_number = 0; - basic_block_loop_depth = 0; -} - -/* Find all basic blocks of the function whose first insn is F. - Store the correct data in the tables that describe the basic blocks, - set up the chains of references for each CODE_LABEL, and - delete any entire basic blocks that cannot be reached. */ - -static void -find_basic_blocks (f) - rtx f; -{ - register rtx insn; - register int i; - - /* Initialize the ref chain of each label to 0. */ - /* Record where all the blocks start and end and their depth in loops. */ - /* For each insn, record the block it is in. */ - - { - register RTX_CODE prev_code = JUMP_INSN; - register RTX_CODE code; - int depth = 1; - - for (insn = f, i = -1; insn; insn = NEXT_INSN (insn)) - { - code = GET_CODE (insn); - if (code == NOTE) - { - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG) - depth++; - else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END) - depth--; - } - else if (code == CODE_LABEL - || (prev_code != INSN && prev_code != CALL_INSN - && prev_code != CODE_LABEL - && (code == INSN || code == CALL_INSN || code == JUMP_INSN))) - { - basic_block_head[++i] = insn; - basic_block_end[i] = insn; - basic_block_loop_depth[i] = depth; - if (code == CODE_LABEL) - LABEL_REFS (insn) = insn; - } - else if (code == INSN || code == CALL_INSN || code == JUMP_INSN) - basic_block_end[i] = insn; - BLOCK_NUM (insn) = i; - if (code != NOTE) - prev_code = code; - } - if (i + 1 != n_basic_blocks) - abort (); - } - - /* Record which basic blocks control can drop in to. */ - - { - register int i; - for (i = 0; i < n_basic_blocks; i++) - { - register rtx insn = PREV_INSN (basic_block_head[i]); - /* TEMP1 is used to avoid a bug in Sequent's compiler. */ - register int temp1; - while (insn && GET_CODE (insn) == NOTE) - insn = PREV_INSN (insn); - temp1 = insn && GET_CODE (insn) != BARRIER; - basic_block_drops_in[i] = temp1; - } - } - - /* Now find which basic blocks can actually be reached - and put all jump insns' LABEL_REFS onto the ref-chains - of their target labels. */ - - if (n_basic_blocks > 0) - { - register char *block_live = (char *) alloca (n_basic_blocks); - register char *block_marked = (char *) alloca (n_basic_blocks); - int something_marked = 1; - - /* Initialize with just block 0 reachable and no blocks marked. */ - - bzero (block_live, n_basic_blocks); - bzero (block_marked, n_basic_blocks); - block_live[0] = 1; - block_live_static = block_live; - - /* Pass over all blocks, marking each block that is reachable - and has not yet been marked. - Keep doing this until, in one pass, no blocks have been marked. - Then blocks_live and blocks_marked are identical and correct. - In addition, all jumps actually reachable have been marked. */ - - while (something_marked) - { - something_marked = 0; - for (i = 0; i < n_basic_blocks; i++) - if (block_live[i] && !block_marked[i]) - { - block_marked[i] = 1; - something_marked = 1; - if (i + 1 < n_basic_blocks && basic_block_drops_in[i + 1]) - block_live[i + 1] = 1; - insn = basic_block_end[i]; - if (GET_CODE (insn) == JUMP_INSN) - mark_label_ref (PATTERN (insn), insn, 0); - } - } - - /* Now delete the code for any basic blocks that can't be reached. - They can occur because jump_optimize does not recognize - unreachable loops as unreachable. */ - - for (i = 0; i < n_basic_blocks; i++) - if (!block_live[i]) - { - insn = basic_block_head[i]; - while (1) - { - if (GET_CODE (insn) == BARRIER) - abort (); - if (GET_CODE (insn) != NOTE) - { - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - } - if (insn == basic_block_end[i]) - { - /* BARRIERs are between basic blocks, not part of one. - Delete a BARRIER if the preceding jump is deleted. - We cannot alter a BARRIER into a NOTE - because it is too short; but we can really delete - it because it is not part of a basic block. */ - if (NEXT_INSN (insn) != 0 - && GET_CODE (NEXT_INSN (insn)) == BARRIER) - delete_insn (NEXT_INSN (insn)); - break; - } - insn = NEXT_INSN (insn); - } - /* Each time we delete some basic blocks, - see if there is a jump around them that is - being turned into a no-op. If so, delete it. */ - - if (block_live[i - 1]) - { - register int j; - for (j = i; j < n_basic_blocks; j++) - if (block_live[j]) - { - rtx label; - insn = basic_block_end[i - 1]; - if (GET_CODE (insn) == JUMP_INSN - /* An unconditional jump is the only possibility - we must check for, since a conditional one - would make these blocks live. */ - && simplejump_p (insn) - && (label = XEXP (SET_SRC (PATTERN (insn)), 0), 1) - && INSN_UID (label) != 0 - && BLOCK_NUM (label) == j) - { - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - if (GET_CODE (NEXT_INSN (insn)) != BARRIER) - abort (); - delete_insn (NEXT_INSN (insn)); - } - break; - } - } - } - } -} - -/* Check expression X for label references; - if one is found, add INSN to the label's chain of references. - - CHECKDUP means check for and avoid creating duplicate references - from the same insn. Such duplicates do no serious harm but - can slow life analysis. CHECKDUP is set only when duplicates - are likely. */ - -static void -mark_label_ref (x, insn, checkdup) - rtx x, insn; - int checkdup; -{ - register RTX_CODE code = GET_CODE (x); - register int i; - register char *fmt; - - if (code == LABEL_REF) - { - register rtx label = XEXP (x, 0); - register rtx y; - if (GET_CODE (label) != CODE_LABEL) - abort (); - /* If the label was never emitted, this insn is junk, - but avoid a crash trying to refer to BLOCK_NUM (label). - This can happen as a result of a syntax error - and a diagnostic has already been printed. */ - if (INSN_UID (label) == 0) - return; - CONTAINING_INSN (x) = insn; - /* if CHECKDUP is set, check for duplicate ref from same insn - and don't insert. */ - if (checkdup) - for (y = LABEL_REFS (label); y != label; y = LABEL_NEXTREF (y)) - if (CONTAINING_INSN (y) == insn) - return; - LABEL_NEXTREF (x) = LABEL_REFS (label); - LABEL_REFS (label) = x; - block_live_static[BLOCK_NUM (label)] = 1; - return; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - mark_label_ref (XEXP (x, i), insn, 0); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - mark_label_ref (XVECEXP (x, i, j), insn, 1); - } - } -} - -/* Determine the which registers are live at the start of each - basic block of the function whose first insn is F. - NREGS is the number of registers used in F. - We allocate the vector basic_block_live_at_start - and the regsets that it points to, and fill them with the data. - regset_size and regset_bytes are also set here. */ - -static void -life_analysis (f, nregs) - rtx f; - int nregs; -{ - register regset tem; - int first_pass; - int changed; - /* For each basic block, a bitmask of regs - live on exit from the block. */ - regset *basic_block_live_at_end; - /* For each basic block, a bitmask of regs - live on entry to a successor-block of this block. - If this does not match basic_block_live_at_end, - that must be updated, and the block must be rescanned. */ - regset *basic_block_new_live_at_end; - /* For each basic block, a bitmask of regs - whose liveness at the end of the basic block - can make a difference in which regs are live on entry to the block. - These are the regs that are set within the basic block, - possibly excluding those that are used after they are set. */ - regset *basic_block_significant; - register int i; - rtx insn; - - struct obstack flow_obstack; - - obstack_init (&flow_obstack); - - max_regno = nregs; - - bzero (regs_ever_live, sizeof regs_ever_live); - - /* Allocate and zero out many data structures - that will record the data from lifetime analysis. */ - - allocate_for_life_analysis (); - - reg_next_use = (rtx *) alloca (nregs * sizeof (rtx)); - bzero (reg_next_use, nregs * sizeof (rtx)); - - /* Set up several regset-vectors used internally within this function. - Their meanings are documented above, with their declarations. */ - - basic_block_live_at_end = (regset *) alloca (n_basic_blocks * sizeof (regset)); - /* Don't use alloca since that leads to a crash rather than an error message - if there isn't enough space. - Don't use oballoc since we may need to allocate other things during - this function on the temporary obstack. */ - tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes); - bzero (tem, n_basic_blocks * regset_bytes); - init_regset_vector (basic_block_live_at_end, tem, n_basic_blocks, regset_bytes); - - basic_block_new_live_at_end = (regset *) alloca (n_basic_blocks * sizeof (regset)); - tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes); - bzero (tem, n_basic_blocks * regset_bytes); - init_regset_vector (basic_block_new_live_at_end, tem, n_basic_blocks, regset_bytes); - - basic_block_significant = (regset *) alloca (n_basic_blocks * sizeof (regset)); - tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes); - bzero (tem, n_basic_blocks * regset_bytes); - init_regset_vector (basic_block_significant, tem, n_basic_blocks, regset_bytes); - - /* Record which insns refer to any volatile memory - or for any reason can't be deleted just because they are dead stores. - Also, delete any insns that copy a register to itself. */ - - for (insn = f; insn; insn = NEXT_INSN (insn)) - { - enum rtx_code code1 = GET_CODE (insn); - if (code1 == CALL_INSN) - INSN_VOLATILE (insn) = 1; - else if (code1 == INSN || code1 == JUMP_INSN) - { - if (GET_CODE (PATTERN (insn)) == SET - && GET_CODE (SET_DEST (PATTERN (insn))) == REG - && GET_CODE (SET_SRC (PATTERN (insn))) == REG - && REGNO (SET_DEST (PATTERN (insn))) == - REGNO (SET_SRC (PATTERN (insn)))) - { - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - } - else if (GET_CODE (PATTERN (insn)) != USE) - INSN_VOLATILE (insn) = volatile_refs_p (PATTERN (insn)); - /* A SET that makes space on the stack cannot be dead. - (Such SETs occur only for allocating variable-size data, - so they will always have a PLUS or MINUS according to the - direction of stack growth.) - Even if this function never uses this stack pointer value, - signal handlers do! */ - else if (code1 == INSN && GET_CODE (PATTERN (insn)) == SET - && SET_DEST (PATTERN (insn)) == stack_pointer_rtx -#ifdef STACK_GROWS_DOWNWARD - && GET_CODE (SET_SRC (PATTERN (insn))) == MINUS -#else - && GET_CODE (SET_SRC (PATTERN (insn))) == PLUS -#endif - && XEXP (SET_SRC (PATTERN (insn)), 0) == stack_pointer_rtx) - INSN_VOLATILE (insn) = 1; - } - } - - if (n_basic_blocks > 0) -#ifdef EXIT_IGNORE_STACK - if (! (EXIT_IGNORE_STACK) || ! frame_pointer_needed) -#endif - { - /* If exiting needs the right stack value, - consider the stack pointer live at the end of the function. */ - basic_block_live_at_end[n_basic_blocks - 1] - [STACK_POINTER_REGNUM / REGSET_ELT_BITS] - |= 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS); - basic_block_new_live_at_end[n_basic_blocks - 1] - [STACK_POINTER_REGNUM / REGSET_ELT_BITS] - |= 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS); - } - - /* Propagate life info through the basic blocks - around the graph of basic blocks. - - This is a relaxation process: each time a new register - is live at the end of the basic block, we must scan the block - to determine which registers are, as a consequence, live at the beginning - of that block. These registers must then be marked live at the ends - of all the blocks that can transfer control to that block. - The process continues until it reaches a fixed point. */ - - first_pass = 1; - changed = 1; - while (changed) - { - changed = 0; - for (i = n_basic_blocks - 1; i >= 0; i--) - { - int consider = first_pass; - int must_rescan = first_pass; - register int j; - - /* Set CONSIDER if this block needs thinking about at all - (that is, if the regs live now at the end of it - are not the same as were live at the end of it when - we last thought about it). - Set must_rescan if it needs to be thought about - instruction by instruction (that is, if any additional - reg that is live at the end now but was not live there before - is one of the significant regs of this basic block). */ - - for (j = 0; j < regset_size; j++) - { - register int x = basic_block_new_live_at_end[i][j] - & ~basic_block_live_at_end[i][j]; - if (x) - consider = 1; - if (x & basic_block_significant[i][j]) - { - must_rescan = 1; - consider = 1; - break; - } - } - - if (! consider) - continue; - - /* The live_at_start of this block may be changing, - so another pass will be required after this one. */ - changed = 1; - - if (! must_rescan) - { - /* No complete rescan needed; - just record those variables newly known live at end - as live at start as well. */ - for (j = 0; j < regset_size; j++) - { - register int x = basic_block_new_live_at_end[i][j] - & ~basic_block_live_at_end[i][j]; - basic_block_live_at_start[i][j] |= x; - basic_block_live_at_end[i][j] |= x; - } - } - else - { - /* Update the basic_block_live_at_start - by propagation backwards through the block. */ - bcopy (basic_block_new_live_at_end[i], - basic_block_live_at_end[i], regset_bytes); - bcopy (basic_block_live_at_end[i], - basic_block_live_at_start[i], regset_bytes); - propagate_block (basic_block_live_at_start[i], - basic_block_head[i], basic_block_end[i], 0, - first_pass ? basic_block_significant[i] : 0, - i); - } - - { - register rtx jump, head; - /* Update the basic_block_new_live_at_end's of the block - that falls through into this one (if any). */ - head = basic_block_head[i]; - jump = PREV_INSN (head); - if (basic_block_drops_in[i]) - { - register int from_block = BLOCK_NUM (jump); - register int j; - for (j = 0; j < regset_size; j++) - basic_block_new_live_at_end[from_block][j] - |= basic_block_live_at_start[i][j]; - } - /* Update the basic_block_new_live_at_end's of - all the blocks that jump to this one. */ - if (GET_CODE (head) == CODE_LABEL) - for (jump = LABEL_REFS (head); - jump != head; - jump = LABEL_NEXTREF (jump)) - { - register int from_block = BLOCK_NUM (CONTAINING_INSN (jump)); - register int j; - for (j = 0; j < regset_size; j++) - basic_block_new_live_at_end[from_block][j] - |= basic_block_live_at_start[i][j]; - } - } -#ifdef USE_C_ALLOCA - alloca (0); -#endif - } - first_pass = 0; - } - -#if 0 /* This seems unnecessary; life at start of function shouldn't - mean that the reg is live in more than one basic block. */ - - /* Process the regs live at the beginning of the function. - Mark them as not local to any one basic block. */ - - if (n_basic_blocks > 0) - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (basic_block_live_at_start[0][i / REGSET_ELT_BITS] - & (1 << (i % REGSET_ELT_BITS))) - reg_basic_block[i] = REG_BLOCK_GLOBAL; -#endif - - /* Now the life information is accurate. - Make one more pass over each basic block - to delete dead stores, create autoincrement addressing - and record how many times each register is used, is set, or dies. - - To save time, we operate directly in basic_block_live_at_end[i], - thus destroying it (in fact, converting it into a copy of - basic_block_live_at_start[i]). This is ok now because - basic_block_live_at_end[i] is no longer used past this point. */ - - for (i = 0; i < n_basic_blocks; i++) - { - propagate_block (basic_block_live_at_end[i], - basic_block_head[i], basic_block_end[i], 1, 0, i); -#ifdef USE_C_ALLOCA - alloca (0); -#endif - } - -#if 0 - /* Something live during a setjmp should not be put in a register - on certain machines which restore regs from stack frames - rather than from the jmpbuf. - But we don't need to do this for the user's variables, since - ANSI says only volatile variables need this. */ -#ifdef LONGJMP_RESTORE_FROM_STACK - for (i = FIRST_PSEUDO_REGISTER; i < nregs; i++) - if (regs_live_at_setjmp[i / REGSET_ELT_BITS] & (1 << (i % REGSET_ELT_BITS)) - && regno_reg_rtx[i] != 0 && ! REG_USERVAR_P (regno_reg_rtx[i])) - { - reg_live_length[i] = -1; - reg_basic_block[i] = -1; - } -#endif -#endif - - /* We have a problem with any pseudoreg that - lives across the setjmp. ANSI says that if a - user variable does not change in value - between the setjmp and the longjmp, then the longjmp preserves it. - This includes longjmp from a place where the pseudo appears dead. - (In principle, the value still exists if it is in scope.) - If the pseudo goes in a hard reg, some other value may occupy - that hard reg where this pseudo is dead, thus clobbering the pseudo. - Conclusion: such a pseudo must not go in a hard reg. */ - for (i = FIRST_PSEUDO_REGISTER; i < nregs; i++) - if (regs_live_at_setjmp[i / REGSET_ELT_BITS] & (1 << (i % REGSET_ELT_BITS)) - && regno_reg_rtx[i] != 0) - { - reg_live_length[i] = -1; - reg_basic_block[i] = -1; - } - - obstack_free (&flow_obstack, 0); -} - -/* Subroutines of life analysis. */ - -/* Allocate the permanent data structures that represent the results - of life analysis. Not static since used also for stupid life analysis. */ - -void -allocate_for_life_analysis () -{ - register int i; - register regset tem; - - regset_size = ((max_regno + REGSET_ELT_BITS - 1) / REGSET_ELT_BITS); - regset_bytes = regset_size * sizeof (*(regset)0); - - reg_n_refs = (short *) oballoc (max_regno * sizeof (short)); - bzero (reg_n_refs, max_regno * sizeof (short)); - - reg_n_sets = (short *) oballoc (max_regno * sizeof (short)); - bzero (reg_n_sets, max_regno * sizeof (short)); - - reg_n_deaths = (short *) oballoc (max_regno * sizeof (short)); - bzero (reg_n_deaths, max_regno * sizeof (short)); - - reg_first_use = (rtx *) oballoc (max_regno * sizeof (rtx)); - bzero (reg_first_use, max_regno * sizeof (rtx)); - - reg_live_length = (int *) oballoc (max_regno * sizeof (int)); - bzero (reg_live_length, max_regno * sizeof (int)); - - reg_n_calls_crossed = (int *) oballoc (max_regno * sizeof (int)); - bzero (reg_n_calls_crossed, max_regno * sizeof (int)); - - reg_basic_block = (short *) oballoc (max_regno * sizeof (short)); - for (i = 0; i < max_regno; i++) - reg_basic_block[i] = REG_BLOCK_UNKNOWN; - - basic_block_live_at_start = (regset *) oballoc (n_basic_blocks * sizeof (regset)); - tem = (regset) oballoc (n_basic_blocks * regset_bytes); - bzero (tem, n_basic_blocks * regset_bytes); - init_regset_vector (basic_block_live_at_start, tem, n_basic_blocks, regset_bytes); - - regs_live_at_setjmp = (regset) oballoc (regset_bytes); - bzero (regs_live_at_setjmp, regset_bytes); -} - -/* Make each element of VECTOR point at a regset, - taking the space for all those regsets from SPACE. - SPACE is of type regset, but it is really as long as NELTS regsets. - BYTES_PER_ELT is the number of bytes in one regset. */ - -static void -init_regset_vector (vector, space, nelts, bytes_per_elt) - regset *vector; - regset space; - int nelts; - int bytes_per_elt; -{ - register int i; - register regset p = space; - - for (i = 0; i < nelts; i++) - { - vector[i] = p; - p += bytes_per_elt / sizeof (*p); - } -} - -/* Compute the registers live at the beginning of a basic block - from those live at the end. - - When called, OLD contains those live at the end. - On return, it contains those live at the beginning. - FIRST and LAST are the first and last insns of the basic block. - - FINAL is nonzero if we are doing the final pass which is not - for computing the life info (since that has already been done) - but for acting on it. On this pass, we delete dead stores, - set up the logical links and dead-variables lists of instructions, - and merge instructions for autoincrement and autodecrement addresses. - - SIGNIFICANT is nonzero only the first time for each basic block. - If it is nonzero, it points to a regset in which we store - a 1 for each register that is set within the block. - - BNUM is the number of the basic block. */ - -static void -propagate_block (old, first, last, final, significant, bnum) - register regset old; - rtx first; - rtx last; - int final; - regset significant; - int bnum; -{ - register rtx insn; - rtx prev; - regset live; - regset dead; - - /* The following variables are used only if FINAL is nonzero. */ - /* This vector gets one element for each reg that has been live - at any point in the basic block that has been scanned so far. - SOMETIMES_MAX says how many elements are in use so far. - In each element, OFFSET is the byte-number within a regset - for the register described by the element, and BIT is a mask - for that register's bit within the byte. */ - register struct foo { short offset; short bit; } *regs_sometimes_live; - int sometimes_max = 0; - /* This regset has 1 for each reg that we have seen live so far. - It and REGS_SOMETIMES_LIVE are updated together. */ - regset maxlive; - - loop_depth = basic_block_loop_depth[bnum]; - - dead = (regset) alloca (regset_bytes); - live = (regset) alloca (regset_bytes); - - if (final) - { - register int i, offset, bit; - - maxlive = (regset) alloca (regset_bytes); - bcopy (old, maxlive, regset_bytes); - regs_sometimes_live - = (struct foo *) alloca (max_regno * sizeof (struct foo)); - - /* Process the regs live at the end of the block. - Enter them in MAXLIVE and REGS_SOMETIMES_LIVE. - Also mark them as not local to any one basic block. */ - - for (offset = 0, i = 0; offset < regset_size; offset++) - for (bit = 1; bit; bit <<= 1, i++) - { - if (i == max_regno) - break; - if (old[offset] & bit) - { - reg_basic_block[i] = REG_BLOCK_GLOBAL; - regs_sometimes_live[sometimes_max].offset = offset; - regs_sometimes_live[sometimes_max].bit = i % REGSET_ELT_BITS; - sometimes_max++; - } - } - } - - /* Include any notes at the end of the block in the scan. - This is in case the block ends with a call to setjmp. */ - - while (NEXT_INSN (last) != 0 && GET_CODE (NEXT_INSN (last)) == NOTE) - last = NEXT_INSN (last); - - /* Scan the block an insn at a time from end to beginning. */ - - for (insn = last; ; insn = prev) - { - prev = PREV_INSN (insn); - - /* If this is a call to `setjmp' et al, - warn if any non-volatile datum is live. */ - - if (final && GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP) - { - int i; - for (i = 0; i < regset_size; i++) - regs_live_at_setjmp[i] |= old[i]; - } - - /* Update the life-status of regs for this insn. - First DEAD gets which regs are set in this insn - then LIVE gets which regs are used in this insn. - Then the regs live before the insn - are those live after, with DEAD regs turned off, - and then LIVE regs turned on. */ - - if (GET_CODE (insn) == INSN - || GET_CODE (insn) == JUMP_INSN - || GET_CODE (insn) == CALL_INSN) - { - register int i; - rtx note = find_reg_note (insn, REG_RETVAL, 0); - - /* If an instruction consists of just dead store(s) on final pass, - "delete" it by turning it into a NOTE of type NOTE_INSN_DELETED. - We could really delete it with delete_insn, but that - can cause trouble for first or last insn in a basic block. */ - if (final && insn_dead_p (PATTERN (insn), old, 1) - /* Don't delete something that refers to volatile storage! */ - && ! INSN_VOLATILE (insn)) - { - rtx oldpat = PATTERN (insn); - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - /* If this insn is copying the return value from a library call, - delete the entire library call. */ - if (note && libcall_dead_p (oldpat, old)) - { - rtx first = XEXP (note, 0); - rtx prev = insn; - while (INSN_DELETED_P (first)) - first = NEXT_INSN (first); - while (prev != first) - { - prev = PREV_INSN (prev); - PUT_CODE (prev, NOTE); - NOTE_LINE_NUMBER (prev) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (prev) = 0; - } - } - goto flushed; - } - - for (i = 0; i < regset_size; i++) - { - dead[i] = 0; /* Faster than bzero here */ - live[i] = 0; /* since regset_size is usually small */ - } - - /* See if this is an increment or decrement that can be - merged into a following memory address. */ -#ifdef AUTO_INC_DEC - { - register rtx x = PATTERN (insn); - /* Does this instruction increment or decrement a register? */ - if (final && GET_CODE (x) == SET - && GET_CODE (SET_DEST (x)) == REG - && (GET_CODE (SET_SRC (x)) == PLUS - || GET_CODE (SET_SRC (x)) == MINUS) - && XEXP (SET_SRC (x), 0) == SET_DEST (x) - && GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT - /* Ok, look for a following memory ref we can combine with. - If one is found, change the memory ref to a PRE_INC - or PRE_DEC, cancel this insn, and return 1. - Return 0 if nothing has been done. */ - && try_pre_increment_1 (insn)) - goto flushed; - } -#endif /* AUTO_INC_DEC */ - - /* If this is not the final pass, and this insn is copying the - value of a library call and it's dead, don't scan the - insns that perform the library call, so that the call's - arguments are not marked live. */ - if (note && insn_dead_p (PATTERN (insn), old, 1) - && libcall_dead_p (PATTERN (insn), old)) - { - /* Mark the dest reg as `significant'. */ - mark_set_regs (old, dead, PATTERN (insn), 0, significant); - - insn = XEXP (note, 0); - prev = PREV_INSN (insn); - } - else if (GET_CODE (PATTERN (insn)) == SET - && SET_DEST (PATTERN (insn)) == stack_pointer_rtx - && GET_CODE (SET_SRC (PATTERN (insn))) == PLUS - && XEXP (SET_SRC (PATTERN (insn)), 0) == stack_pointer_rtx - && GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 1)) == CONST_INT) - /* We have an insn to pop a constant amount off the stack. - (Such insns use PLUS regardless of the direction of the stack, - and any insn to adjust the stack by a constant is always a pop.) - These insns, if not dead stores, have no effect on life. */ - ; - else - { - /* LIVE gets the regs used in INSN; DEAD gets those set by it. */ - mark_set_regs (old, dead, PATTERN (insn), final ? insn : 0, - significant); - mark_used_regs (old, live, PATTERN (insn), final, insn); - - /* Update OLD for the registers used or set. */ - for (i = 0; i < regset_size; i++) - { - old[i] &= ~dead[i]; - old[i] |= live[i]; - } - - if (GET_CODE (insn) == CALL_INSN) - { - register int i; - - /* Each call clobbers all call-clobbered regs. - Note that the function-value reg is one of these, and - mark_set_regs has already had a chance to handle it. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (call_used_regs[i]) - dead[i / REGSET_ELT_BITS] |= - (1 << (i % REGSET_ELT_BITS)); - - /* The stack ptr is used (honorarily) by a CALL insn. */ - live[STACK_POINTER_REGNUM / REGSET_ELT_BITS] - |= (1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS)); - } - - /* Update OLD for the registers used or set. */ - for (i = 0; i < regset_size; i++) - { - old[i] &= ~dead[i]; - old[i] |= live[i]; - } - - if (GET_CODE (insn) == CALL_INSN && final) - { - /* Any regs live at the time of a call instruction - must not go in a register clobbered by calls. - Find all regs now live and record this for them. */ - - register struct foo *p = regs_sometimes_live; - - for (i = 0; i < sometimes_max; i++, p++) - if (old[p->offset] & (1 << p->bit)) - reg_n_calls_crossed[p->offset * REGSET_ELT_BITS + p->bit]+= 1; - } - } - - /* On final pass, add any additional sometimes-live regs - into MAXLIVE and REGS_SOMETIMES_LIVE. - Also update counts of how many insns each reg is live at. */ - - if (final) - { - for (i = 0; i < regset_size; i++) - { - register int diff = live[i] & ~maxlive[i]; - - if (diff) - { - register int regno; - maxlive[i] |= diff; - for (regno = 0; diff && regno < REGSET_ELT_BITS; regno++) - if (diff & (1 << regno)) - { - regs_sometimes_live[sometimes_max].offset = i; - regs_sometimes_live[sometimes_max].bit = regno; - diff &= ~ (1 << regno); - sometimes_max++; - } - } - } - - { - register struct foo *p = regs_sometimes_live; - for (i = 0; i < sometimes_max; i++, p++) - { - if (old[p->offset] & (1 << p->bit)) - reg_live_length[p->offset * REGSET_ELT_BITS + p->bit]++; - } - } - } - } - flushed: ; - if (insn == first) - break; - } -} - -/* Return 1 if X (the body of an insn, or part of it) is just dead stores - (SET expressions whose destinations are registers dead after the insn). - NEEDED is the regset that says which regs are alive after the insn. */ - -static int -insn_dead_p (x, needed, strict_low_ok) - rtx x; - regset needed; - int strict_low_ok; -{ - register RTX_CODE code = GET_CODE (x); -#if 0 - /* Make sure insns to set the stack pointer are never deleted. */ - needed[STACK_POINTER_REGNUM / REGSET_ELT_BITS] - |= 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS); -#endif - - /* If setting something that's a reg or part of one, - see if that register's altered value will be live. */ - - if (code == SET) - { - register rtx r = SET_DEST (x); - /* A SET that is a subroutine call cannot be dead. */ - if (GET_CODE (SET_SRC (x)) == CALL) - return 0; - while (GET_CODE (r) == SUBREG - || (strict_low_ok && GET_CODE (r) == STRICT_LOW_PART) - || GET_CODE (r) == ZERO_EXTRACT - || GET_CODE (r) == SIGN_EXTRACT) - r = SUBREG_REG (r); - if (GET_CODE (r) == REG) - { - register int regno = REGNO (r); - register int offset = regno / REGSET_ELT_BITS; - register int bit = 1 << (regno % REGSET_ELT_BITS); - return (! (regno < FIRST_PSEUDO_REGISTER && global_regs[regno]) - && (needed[offset] & bit) == 0); - } - } - /* If performing several activities, - insn is dead if each activity is individually dead. - Also, CLOBBERs and USEs can be ignored; a CLOBBER or USE - that's inside a PARALLEL doesn't make the insn worth keeping. */ - else if (code == PARALLEL) - { - register int i = XVECLEN (x, 0); - for (i--; i >= 0; i--) - { - rtx elt = XVECEXP (x, 0, i); - if (!insn_dead_p (elt, needed, strict_low_ok) - && GET_CODE (elt) != CLOBBER - && GET_CODE (elt) != USE) - return 0; - } - return 1; - } - /* We do not check CLOBBER or USE here. - An insn consisting of just a CLOBBER or just a USE - should not be deleted. */ - return 0; -} - -/* If X is the last insn in a libcall, and assuming X is dead, - return 1 if the entire library call is dead. - This is true if the source of X is a dead register - (as well as the destination, which we tested already). - If this insn doesn't just copy a register, then we don't - have an ordinary libcall. In that case, cse could not have - managed to substitute the source for the dest later on, - so we can assume the libcall is dead. */ - -static int -libcall_dead_p (x, needed) - rtx x; - regset needed; -{ - register RTX_CODE code = GET_CODE (x); - - if (code == SET) - { - register rtx r = SET_SRC (x); - if (GET_CODE (r) == REG) - { - register int regno = REGNO (r); - register int offset = regno / REGSET_ELT_BITS; - register int bit = 1 << (regno % REGSET_ELT_BITS); - return (needed[offset] & bit) == 0; - } - } - return 1; -} - -/* Return 1 if register REGNO was used before it was set. - In other words, if it is live at function entry. */ - -int -regno_uninitialized (regno) - int regno; -{ - if (n_basic_blocks == 0) - return 0; - - return (basic_block_live_at_start[0][regno / REGSET_ELT_BITS] - & (1 << (regno % REGSET_ELT_BITS))); -} - -/* 1 if register REGNO was alive at a place where `setjmp' was called - and was set more than once. Such regs may be clobbered by `longjmp'. */ - -int -regno_clobbered_at_setjmp (regno) - int regno; -{ - return (reg_n_sets[regno] > 1 - && (regs_live_at_setjmp[regno / REGSET_ELT_BITS] - & (1 << (regno % REGSET_ELT_BITS)))); -} - -/* Process the registers that are set within X. - Their bits are set to 1 in the regset DEAD, - because they are dead prior to this insn. - - If INSN is nonzero, it is the insn being processed - and the fact that it is nonzero implies this is the FINAL pass - in propagate_block. In this case, various info about register - usage is stored, LOG_LINKS fields of insns are set up. */ - -static void mark_set_1 (); - -static void -mark_set_regs (needed, dead, x, insn, significant) - regset needed; - regset dead; - rtx x; - rtx insn; - regset significant; -{ - register RTX_CODE code = GET_CODE (x); - - if (code == SET || code == CLOBBER) - mark_set_1 (needed, dead, x, insn, significant); - else if (code == PARALLEL) - { - register int i; - for (i = XVECLEN (x, 0) - 1; i >= 0; i--) - { - code = GET_CODE (XVECEXP (x, 0, i)); - if (code == SET || code == CLOBBER) - mark_set_1 (needed, dead, XVECEXP (x, 0, i), insn, significant); - } - } -} - -/* Process a single SET rtx, X. */ - -static void -mark_set_1 (needed, dead, x, insn, significant) - regset needed; - regset dead; - rtx x; - rtx insn; - regset significant; -{ - register int regno; - register rtx reg = SET_DEST (x); - int subreg_p = 0; - - if (reg == 0) - return; - /* Modifying just one hardware register of a multi-reg value - or just a byte field of a register - does not mean the value from before this insn is now dead. - But it does mean liveness of that register at the end of the block - is significant. */ - while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == ZERO_EXTRACT - || GET_CODE (reg) == SIGN_EXTRACT - || GET_CODE (reg) == STRICT_LOW_PART) - { - if (GET_CODE (reg) == ZERO_EXTRACT - || GET_CODE (reg) == SIGN_EXTRACT - || (GET_CODE (reg) == SUBREG - && REG_SIZE (SUBREG_REG (reg)) > REG_SIZE (reg))) - subreg_p = 1; - - reg = XEXP (reg, 0); - } - - if (GET_CODE (reg) == REG - && (regno = REGNO (reg), regno != FRAME_POINTER_REGNUM) - && regno != ARG_POINTER_REGNUM - && ! (regno < FIRST_PSEUDO_REGISTER && global_regs[regno])) - /* && regno != STACK_POINTER_REGNUM) -- let's try without this. */ - { - register int offset = regno / REGSET_ELT_BITS; - register int bit = 1 << (regno % REGSET_ELT_BITS); - int is_needed = 0; - - /* Mark it as a significant register for this basic block. */ - if (significant) - significant[offset] |= bit; - /* That's all we do, if we are setting only part of the register. */ - if (subreg_p) - return; - - /* If entire register being set, mark it as as dead before this insn. */ - dead[offset] |= bit; - /* A hard reg in a wide mode may really be multiple registers. - If so, mark all of them just like the first. */ - if (regno < FIRST_PSEUDO_REGISTER) - { - int n; - - /* Nothing below is needed for the stack pointer; get out asap. - Eg, log links aren't needed, since combine won't use them. */ - if (regno == STACK_POINTER_REGNUM) - return; - - n = HARD_REGNO_NREGS (regno, GET_MODE (reg)); - while (--n > 0) - { - dead[(regno + n) / REGSET_ELT_BITS] - |= 1 << ((regno + n) % REGSET_ELT_BITS); - if (significant) - significant[(regno + n) / REGSET_ELT_BITS] - |= 1 << ((regno + n) % REGSET_ELT_BITS); - is_needed |= (needed[(regno + n) / REGSET_ELT_BITS] - & 1 << ((regno + n) % REGSET_ELT_BITS)); - } - } - /* Additional data to record if this is the final pass. */ - if (insn) - { - register rtx y = reg_next_use[regno]; - register int blocknum = BLOCK_NUM (insn); - - /* If this is a hard reg, record this function uses the reg. - `combine.c' will get confused if LOG_LINKs are made - for hard regs. */ - - if (regno < FIRST_PSEUDO_REGISTER) - { - register int i; - i = HARD_REGNO_NREGS (regno, GET_MODE (reg)); - if (i == 0) - i = 1; - do - regs_ever_live[regno + --i] = 1; - while (i > 0); - - if (! ((needed[offset] & bit) || is_needed)) - { - /* Note that dead stores have already been deleted if poss. - If we get here, we have found a dead store that cannot - be eliminated (because the insn does something useful). - Indicate this by marking the reg set as dying here. */ - REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_DEAD, - reg, REG_NOTES (insn)); - reg_n_deaths[REGNO (reg)]++; - } - return; - } - - /* Keep track of which basic blocks each reg appears in. */ - - if (reg_basic_block[regno] == REG_BLOCK_UNKNOWN) - reg_basic_block[regno] = blocknum; - else if (reg_basic_block[regno] != blocknum) - reg_basic_block[regno] = REG_BLOCK_GLOBAL; - - /* Record first insn to use this reg. */ - reg_first_use[regno] = insn; - - /* Count (weighted) references, stores, etc. */ - reg_n_refs[regno] += loop_depth; - reg_n_sets[regno]++; - /* The next use is no longer "next", since a store intervenes. */ - reg_next_use[regno] = 0; - /* The insns where a reg is live are normally counted elsewhere, - but we want the count to include the insn where the reg is set, - and the normal counting mechanism would not count it. */ - reg_live_length[regno]++; - if ((needed[offset] & bit) || is_needed) - { - /* Make a logical link from the next following insn - that uses this register, back to this insn. - The following insns have already been processed. */ - if (y && (BLOCK_NUM (y) == blocknum)) - LOG_LINKS (y) - = gen_rtx (INSN_LIST, VOIDmode, insn, LOG_LINKS (y)); - } - else - { - /* Note that dead stores have already been deleted when possible - If we get here, we have found a dead store that cannot - be eliminated (because the same insn does something useful). - Indicate this by marking the reg being set as dying here. */ - REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_DEAD, - reg, REG_NOTES (insn)); - reg_n_deaths[REGNO (reg)]++; - } - } - } -} - -/* Scan expression X and store a 1-bit in LIVE for each reg it uses. - This is done assuming the registers needed from X - are those that have 1-bits in NEEDED. - - On the final pass, FINAL is 1. This means try for autoincrement - and count the uses and deaths of each pseudo-reg. - - INSN is the containing instruction. */ - -static void -mark_used_regs (needed, live, x, final, insn) - regset needed; - regset live; - rtx x; - rtx insn; - int final; -{ - register RTX_CODE code; - register int regno; - - retry: - code = GET_CODE (x); - switch (code) - { - case LABEL_REF: - case SYMBOL_REF: - case CONST_INT: - case CONST: - case CONST_DOUBLE: - case CC0: - case PC: - case CLOBBER: - case ADDR_VEC: - case ADDR_DIFF_VEC: - case ASM_INPUT: - return; - -#if defined (HAVE_POST_INCREMENT) || defined (HAVE_POST_DECREMENT) - case MEM: - /* Here we detect use of an index register which might - be good for postincrement or postdecrement. */ - if (final) - { - rtx addr = XEXP (x, 0); - register int size = GET_MODE_SIZE (GET_MODE (x)); - - if (GET_CODE (addr) == REG) - { - register rtx y; - regno = REGNO (addr); - /* Is the next use an increment that might make auto-increment? */ - y = reg_next_use[regno]; - if (y && GET_CODE (PATTERN (y)) == SET - && BLOCK_NUM (y) == BLOCK_NUM (insn) - /* Can't add side effects to jumps; if reg is spilled and - reloaded, there's no way to store back the altered value. */ - && GET_CODE (insn) != JUMP_INSN - && (y = SET_SRC (PATTERN (y)), - (0 -#ifdef HAVE_POST_INCREMENT - || GET_CODE (y) == PLUS -#endif -#ifdef HAVE_POST_DECREMENT - || GET_CODE (y) == MINUS -#endif - ) - && XEXP (y, 0) == addr - && GET_CODE (XEXP (y, 1)) == CONST_INT - && INTVAL (XEXP (y, 1)) == size) - && dead_or_set_p (reg_next_use[regno], addr)) - { - rtx use = find_use_as_address (PATTERN (insn), addr, 0); - - /* Make sure this register appears only once in this insn. */ - if (use != 0 && use != (rtx) 1) - { - /* We have found a suitable auto-increment: - do POST_INC around the register here, - and patch out the increment instruction that follows. */ - XEXP (x, 0) - = gen_rtx (GET_CODE (y) == PLUS ? POST_INC : POST_DEC, - Pmode, addr); - /* Record that this insn has an implicit side effect. */ - REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_INC, addr, REG_NOTES (insn)); - - /* Modify the old increment-insn to simply copy - the already-incremented value of our register. */ - y = reg_next_use[regno]; - SET_SRC (PATTERN (y)) = addr; - - /* If that makes it a no-op (copying the register - into itself) then change it to a simpler no-op - so it won't appear to be a "use" and a "set" - of this register. */ - if (SET_DEST (PATTERN (y)) == addr) - PATTERN (y) = gen_rtx (USE, VOIDmode, const0_rtx); - - /* Count an extra reference to the reg for the increment. - When a reg is incremented. - spilling it is worse, so we want to make that - less likely. */ - reg_n_refs[regno] += loop_depth; - /* Count the increment as a setting of the register, - even though it isn't a SET in rtl. */ - reg_n_sets[regno]++; - } - } - } - } - break; -#endif /* HAVE_POST_INCREMENT or HAVE_POST_DECREMENT */ - - case REG: - /* See a register other than being set - => mark it as needed. */ - - regno = REGNO (x); - if (regno != FRAME_POINTER_REGNUM) - /* && regno != ARG_POINTER_REGNUM) -- and without this. */ - /* && regno != STACK_POINTER_REGNUM) -- let's try without this. */ - { - register int offset = regno / REGSET_ELT_BITS; - register int bit = 1 << (regno % REGSET_ELT_BITS); - int is_needed = 0; - - live[offset] |= bit; - /* A hard reg in a wide mode may really be multiple registers. - If so, mark all of them just like the first. */ - if (regno < FIRST_PSEUDO_REGISTER) - { - int n; - - /* For stack ptr or arg pointer, - nothing below can be necessary, so waste no more time. */ - if (regno == STACK_POINTER_REGNUM - || regno == ARG_POINTER_REGNUM) - return; - /* No death notes for global register variables; - their values are live after this function exits. */ - if (global_regs[regno]) - return; - - n = HARD_REGNO_NREGS (regno, GET_MODE (x)); - while (--n > 0) - { - live[(regno + n) / REGSET_ELT_BITS] - |= 1 << ((regno + n) % REGSET_ELT_BITS); - is_needed |= (needed[(regno + n) / REGSET_ELT_BITS] - & 1 << ((regno + n) % REGSET_ELT_BITS)); - } - } - if (final) - { - if (regno < FIRST_PSEUDO_REGISTER) - { - /* If a hard reg is being used, - record that this function does use it. */ - - register int i; - i = HARD_REGNO_NREGS (regno, GET_MODE (x)); - if (i == 0) - i = 1; - do - regs_ever_live[regno + --i] = 1; - while (i > 0); - } - else - { - /* Keep track of which basic block each reg appears in. */ - - register int blocknum = BLOCK_NUM (insn); - - if (reg_basic_block[regno] == REG_BLOCK_UNKNOWN) - reg_basic_block[regno] = blocknum; - else if (reg_basic_block[regno] != blocknum) - reg_basic_block[regno] = REG_BLOCK_GLOBAL; - - /* Record the earliest insn that uses this reg, - provided the reg is used only in one basic block. - Do this by recording each insn, and the one that - sticks is the last one scanned (the earliest insn). */ - - reg_first_use[regno] = insn; - - /* Record where each reg is used, so when the reg - is set we know the next insn that uses it. */ - - reg_next_use[regno] = insn; - - /* Count (weighted) number of uses of each reg. */ - - reg_n_refs[regno] += loop_depth; - } - - /* Record and count the insns in which a reg dies. - If it is used in this insn and was dead below the insn - then it dies in this insn. */ - - if (!(needed[offset] & bit) && !is_needed - && ! find_regno_note (insn, REG_DEAD, regno)) - { - REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_DEAD, x, REG_NOTES (insn)); - reg_n_deaths[regno]++; - } - } - } - return; - - case SET: - { - register rtx testreg = SET_DEST (x); - int mark_dest = 0; - - /* Storing in STRICT_LOW_PART is like storing in a reg - in that this SET might be dead, so ignore it in TESTREG. - but in some other ways it is like using the reg. */ - /* Storing in a SUBREG or a bit field is like storing the entire - register in that if the register's value is not used - then this SET is not needed. */ - while (GET_CODE (testreg) == STRICT_LOW_PART - || GET_CODE (testreg) == ZERO_EXTRACT - || GET_CODE (testreg) == SIGN_EXTRACT - || GET_CODE (testreg) == SUBREG) - { - /* Modifying a single register in an alternate mode - does not use any of the old value. But these other - ways of storing in a register do use the old value. */ - if (GET_CODE (testreg) == SUBREG - && !(REG_SIZE (SUBREG_REG (testreg)) > REG_SIZE (testreg))) - ; - else - mark_dest = 1; - - testreg = XEXP (testreg, 0); - } - - /* If this is a store into a register, - recursively scan the only value being stored, - and only if the register's value is live after this insn. - If the value being computed here would never be used - then the values it uses don't need to be computed either. */ - - if (GET_CODE (testreg) == REG - && (regno = REGNO (testreg), regno != FRAME_POINTER_REGNUM) - && regno != ARG_POINTER_REGNUM - && ! (regno < FIRST_PSEUDO_REGISTER && global_regs[regno])) -#if 0 /* This was added in 1.25, but screws up death notes for hard regs. - It probably isn't really needed anyway. */ - && (regno >= FIRST_PSEUDO_REGISTER - || INSN_VOLATILE (insn))) -#endif - { - register int offset = regno / REGSET_ELT_BITS; - register int bit = 1 << (regno % REGSET_ELT_BITS); - if ((needed[offset] & bit) - /* If insn refers to volatile, we mustn't delete it, - so its inputs are all needed. */ - || INSN_VOLATILE (insn)) - { - mark_used_regs (needed, live, SET_SRC (x), final, insn); - if (mark_dest) - mark_used_regs (needed, live, SET_DEST (x), final, insn); - } - return; - } - } - break; - } - - /* Recursively scan the operands of this expression. */ - - { - register char *fmt = GET_RTX_FORMAT (code); - register int i; - - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - /* Tail recursive case: save a function call level. */ - if (i == 0) - { - x = XEXP (x, 0); - goto retry; - } - mark_used_regs (needed, live, XEXP (x, i), final, insn); - } - else if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - mark_used_regs (needed, live, XVECEXP (x, i, j), final, insn); - } - } - } -} - -#ifdef AUTO_INC_DEC - -static int -try_pre_increment_1 (insn) - rtx insn; -{ - /* Find the next use of this reg. If in same basic block, - make it do pre-increment or pre-decrement if appropriate. */ - rtx x = PATTERN (insn); - int amount = ((GET_CODE (SET_SRC (x)) == PLUS ? 1 : -1) - * INTVAL (XEXP (SET_SRC (x), 1))); - int regno = REGNO (SET_DEST (x)); - rtx y = reg_next_use[regno]; - if (y != 0 - && BLOCK_NUM (y) == BLOCK_NUM (insn) - && try_pre_increment (y, SET_DEST (PATTERN (insn)), - amount)) - { - /* We have found a suitable auto-increment - and already changed insn Y to do it. - So flush this increment-instruction. */ - PUT_CODE (insn, NOTE); - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - /* Count a reference to this reg for the increment - insn we are deleting. When a reg is incremented. - spilling it is worse, so we want to make that - less likely. */ - reg_n_refs[regno] += loop_depth; - reg_n_sets[regno]++; - return 1; - } - return 0; -} - -/* Try to change INSN so that it does pre-increment or pre-decrement - addressing on register REG in order to add AMOUNT to REG. - AMOUNT is negative for pre-decrement. - Returns 1 if the change could be made. - This checks all about the validity of the result of modifying INSN. */ - -static int -try_pre_increment (insn, reg, amount) - rtx insn, reg; - int amount; -{ - register rtx use; - - /* Nonzero if we can try to make a pre-increment or pre-decrement. - For example, addl $4,r1; movl (r1),... can become movl +(r1),... */ - int pre_ok = 0; - /* Nonzero if we can try to make a post-increment or post-decrement. - For example, addl $4,r1; movl -4(r1),... can become movl (r1)+,... - It is possible for both PRE_OK and POST_OK to be nonzero if the machine - supports both pre-inc and post-inc, or both pre-dec and post-dec. */ - int post_ok = 0; - - /* Nonzero if the opportunity actually requires post-inc or post-dec. */ - int do_post = 0; - - /* From the sign of increment, see which possibilities are conceivable - on this target machine. */ -#ifdef HAVE_PRE_INCREMENT - if (amount > 0) - pre_ok = 1; -#endif -#ifdef HAVE_POST_INCREMENT - if (amount > 0) - post_ok = 1; -#endif - -#ifdef HAVE_PRE_DECREMENT - if (amount < 0) - pre_ok = 1; -#endif -#ifdef HAVE_POST_DECREMENT - if (amount < 0) - post_ok = 1; -#endif - - if (! (pre_ok || post_ok)) - return 0; - - /* It is not safe to add a side effect to a jump insn - because if the incremented register is spilled and must be reloaded - there would be no way to store the incremented value back in memory. */ - - if (GET_CODE (insn) == JUMP_INSN) - return 0; - - use = 0; - if (pre_ok) - use = find_use_as_address (PATTERN (insn), reg, 0); - if (post_ok && (use == 0 || use == (rtx) 1)) - { - use = find_use_as_address (PATTERN (insn), reg, -amount); - do_post = 1; - } - - if (use == 0 || use == (rtx) 1) - return 0; - - if (GET_MODE_SIZE (GET_MODE (use)) != (amount > 0 ? amount : - amount)) - return 0; - - XEXP (use, 0) = gen_rtx (amount > 0 - ? (do_post ? POST_INC : PRE_INC) - : (do_post ? POST_DEC : PRE_DEC), - Pmode, reg); - - /* Record that this insn now has an implicit side effect on X. */ - REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_INC, reg, REG_NOTES (insn)); - return 1; -} - -#endif /* AUTO_INC_DEC */ - -/* Find the place in the rtx X where REG is used as a memory address. - Return the MEM rtx that so uses it. - If PLUSCONST is nonzero, search instead for a memory address equivalent to - (plus REG (const_int PLUSCONST)). - - If such an address does not appear, return 0. - If REG appears more than once, or is used other than in such an address, - return (rtx)1. */ - -static rtx -find_use_as_address (x, reg, plusconst) - register rtx x; - rtx reg; - int plusconst; -{ - enum rtx_code code = GET_CODE (x); - char *fmt = GET_RTX_FORMAT (code); - register int i; - register rtx value = 0; - register rtx tem; - - if (code == MEM && XEXP (x, 0) == reg && plusconst == 0) - return x; - - if (code == MEM && GET_CODE (XEXP (x, 0)) == PLUS - && XEXP (XEXP (x, 0), 0) == reg - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && INTVAL (XEXP (XEXP (x, 0), 1)) == plusconst) - return x; - - if (code == SIGN_EXTRACT || code == ZERO_EXTRACT) - { - /* If REG occurs inside a MEM used in a bit-field reference, - that is unacceptable. */ - if (find_use_as_address (XEXP (x, 0), reg, 0) != 0) - return (rtx) 1; - } - - if (x == reg) - return (rtx) 1; - - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - tem = find_use_as_address (XEXP (x, i), reg, plusconst); - if (value == 0) - value = tem; - else if (tem != 0) - return (rtx) 1; - } - if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - { - tem = find_use_as_address (XVECEXP (x, i, j), reg, plusconst); - if (value == 0) - value = tem; - else if (tem != 0) - return (rtx) 1; - } - } - } - - return value; -} - -/* Write information about registers and basic blocks into FILE. - This is part of making a debugging dump. */ - -void -dump_flow_info (file) - FILE *file; -{ - register int i; - static char *reg_class_names[] = REG_CLASS_NAMES; - - fprintf (file, "%d registers.\n", max_regno); - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (reg_n_refs[i]) - { - enum reg_class class; - fprintf (file, "\nRegister %d used %d times across %d insns", - i, reg_n_refs[i], reg_live_length[i]); - if (reg_basic_block[i] >= 0) - fprintf (file, " in block %d", reg_basic_block[i]); - if (reg_n_deaths[i] != 1) - fprintf (file, "; dies in %d places", reg_n_deaths[i]); - if (reg_n_calls_crossed[i] == 1) - fprintf (file, "; crosses 1 call", reg_n_calls_crossed[i]); - else if (reg_n_calls_crossed[i]) - fprintf (file, "; crosses %d calls", reg_n_calls_crossed[i]); - if (PSEUDO_REGNO_BYTES (i) != UNITS_PER_WORD) - fprintf (file, "; %d bytes", PSEUDO_REGNO_BYTES (i)); - class = reg_preferred_class (i); - if (class != GENERAL_REGS) - { - if (reg_preferred_or_nothing (i)) - fprintf (file, "; %s or none", reg_class_names[(int) class]); - else - fprintf (file, "; pref %s", reg_class_names[(int) class]); - } - if (REGNO_POINTER_FLAG (i)) - fprintf (file, "; pointer"); - fprintf (file, ".\n"); - } - fprintf (file, "\n%d basic blocks.\n", n_basic_blocks); - for (i = 0; i < n_basic_blocks; i++) - { - register rtx head, jump; - register int regno; - fprintf (file, "\nBasic block %d: first insn %d, last %d.\n", - i, - INSN_UID (basic_block_head[i]), - INSN_UID (basic_block_end[i])); - /* The control flow graph's storage is freed - now when flow_analysis returns. - Don't try to print it if it is gone. */ - if (basic_block_drops_in) - { - fprintf (file, "Reached from blocks: "); - head = basic_block_head[i]; - if (GET_CODE (head) == CODE_LABEL) - for (jump = LABEL_REFS (head); - jump != head; - jump = LABEL_NEXTREF (jump)) - { - register int from_block = BLOCK_NUM (CONTAINING_INSN (jump)); - fprintf (file, " %d", from_block); - } - if (basic_block_drops_in[i]) - fprintf (file, " previous"); - } - fprintf (file, "\nRegisters live at start:"); - for (regno = 0; regno < max_regno; regno++) - { - register int offset = regno / REGSET_ELT_BITS; - register int bit = 1 << (regno % REGSET_ELT_BITS); - if (basic_block_live_at_start[i][offset] & bit) - fprintf (file, " %d", regno); - } - fprintf (file, "\n"); - } - fprintf (file, "\n"); -} diff --git a/gnu/usr.bin/gcc1/cc1/fold-const.c b/gnu/usr.bin/gcc1/cc1/fold-const.c deleted file mode 100644 index be908f7434..0000000000 --- a/gnu/usr.bin/gcc1/cc1/fold-const.c +++ /dev/null @@ -1,1838 +0,0 @@ -/* Fold a constant sub-tree into a single node for C-compiler - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/*@@ Fix lossage on folding division of big integers. */ - -/*@@ This file should be rewritten to use an arbitary precision - @@ representation for "struct tree_int_cst" and "struct tree_real_cst". - @@ Perhaps the routines could also be used for bc/dc, and made a lib. - @@ The routines that translate from the ap rep should - @@ warn if precision et. al. is lost. - @@ This would also make life easier when this technology is used - @@ for cross-compilers. */ - - -/* There are only two entry points in this file: - fold and combine. - - fold takes a tree as argument and returns a simplified tree. - - combine takes a tree code for an arithmetic operation - and two operands that are trees for constant values - and returns the result of the specified operation on those values, - also as a tree. */ - -#include -#include -#include "config.h" -#include "tree.h" - -static void lshift_double (); -static void rshift_double (); -static void lrotate_double (); -static void rrotate_double (); - -/* To do constant folding on INTEGER_CST nodes requires 64-bit arithmetic. - We do that by representing the 64-bit integer as 8 shorts, - with only 8 bits stored in each short, as a positive number. */ - -/* Unpack a 64-bit integer into 8 shorts. - LOW and HI are the integer, as two `int' pieces. - SHORTS points to the array of shorts. */ - -static void -encode (shorts, low, hi) - short *shorts; - int low, hi; -{ - shorts[0] = low & 0xff; - shorts[1] = (low >> 8) & 0xff; - shorts[2] = (low >> 16) & 0xff; - shorts[3] = (low >> 24) & 0xff; - shorts[4] = hi & 0xff; - shorts[5] = (hi >> 8) & 0xff; - shorts[6] = (hi >> 16) & 0xff; - shorts[7] = (hi >> 24) & 0xff; -} - -/* Pack an array of 8 shorts into a 64-bit integer. - SHORTS points to the array of shorts. - The integer is stored into *LOW and *HI as two `int' pieces. */ - -static void -decode (shorts, low, hi) - short *shorts; - int *low, *hi; -{ - *low = (shorts[3] << 24) | (shorts[2] << 16) | (shorts[1] << 8) | shorts[0]; - *hi = (shorts[7] << 24) | (shorts[6] << 16) | (shorts[5] << 8) | shorts[4]; -} - -/* Make the integer constant T valid for its type - by setting to 0 or 1 all the bits in the constant - that don't belong in the type. */ - -static void -force_fit_type (t) - tree t; -{ - register int prec = TYPE_PRECISION (TREE_TYPE (t)); - - if (TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE) - prec = BITS_PER_WORD; - - /* First clear all bits that are beyond the type's precision. */ - - if (prec == 2 * HOST_BITS_PER_INT) - ; - else if (prec > HOST_BITS_PER_INT) - { - TREE_INT_CST_HIGH (t) - &= ~((-1) << (prec - HOST_BITS_PER_INT)); - } - else - { - TREE_INT_CST_HIGH (t) = 0; - if (prec < HOST_BITS_PER_INT) - TREE_INT_CST_LOW (t) - &= ~((-1) << prec); - } - - /* If it's a signed type and value's sign bit is set, extend the sign. */ - - if (! TREE_UNSIGNED (TREE_TYPE (t)) - && prec != 2 * HOST_BITS_PER_INT - && (prec > HOST_BITS_PER_INT - ? TREE_INT_CST_HIGH (t) & (1 << (prec - HOST_BITS_PER_INT - 1)) - : TREE_INT_CST_LOW (t) & (1 << (prec - 1)))) - { - /* Value is negative: - set to 1 all the bits that are outside this type's precision. */ - if (prec > HOST_BITS_PER_INT) - { - TREE_INT_CST_HIGH (t) - |= ((-1) << (prec - HOST_BITS_PER_INT)); - } - else - { - TREE_INT_CST_HIGH (t) = -1; - if (prec < HOST_BITS_PER_INT) - TREE_INT_CST_LOW (t) - |= ((-1) << prec); - } - } -} - -/* Add two 64-bit integers with 64-bit result. - Each argument is given as two `int' pieces. - One argument is L1 and H1; the other, L2 and H2. - The value is stored as two `int' pieces in *LV and *HV. - We use the 8-shorts representation internally. */ - -static void -add_double (l1, h1, l2, h2, lv, hv) - int l1, h1, l2, h2; - int *lv, *hv; -{ - short arg1[8]; - short arg2[8]; - register int carry = 0; - register int i; - - encode (arg1, l1, h1); - encode (arg2, l2, h2); - - for (i = 0; i < 8; i++) - { - carry += arg1[i] + arg2[i]; - arg1[i] = carry & 0xff; - carry >>= 8; - } - - decode (arg1, lv, hv); -} - -/* Negate a 64-bit integers with 64-bit result. - The argument is given as two `int' pieces in L1 and H1. - The value is stored as two `int' pieces in *LV and *HV. - We use the 8-shorts representation internally. */ - -static void -neg_double (l1, h1, lv, hv) - int l1, h1; - int *lv, *hv; -{ - if (l1 == 0) - { - *lv = 0; - *hv = - h1; - } - else - { - *lv = - l1; - *hv = ~ h1; - } -} - -/* Multiply two 64-bit integers with 64-bit result. - Each argument is given as two `int' pieces. - One argument is L1 and H1; the other, L2 and H2. - The value is stored as two `int' pieces in *LV and *HV. - We use the 8-shorts representation internally. */ - -static void -mul_double (l1, h1, l2, h2, lv, hv) - int l1, h1, l2, h2; - int *lv, *hv; -{ - short arg1[8]; - short arg2[8]; - short prod[16]; - register int carry = 0; - register int i, j, k; - - /* These two cases are used extensively, arising from pointer - combinations. */ - if (h2 == 0) - { - if (l2 == 2) - { - unsigned temp = l1 + l1; - *hv = h1 * 2 + (temp < l1); - *lv = temp; - return; - } - if (l2 == 4) - { - unsigned temp = l1 + l1; - h1 = h1 * 4 + (temp < l1) << 1; - l1 = temp; - temp += temp; - h1 += (temp < l1); - *lv = temp; - *hv = h1; - return; - } - if (l2 == 8) - { - unsigned temp = l1 + l1; - h1 = h1 * 8 + (temp < l1) << 2; - l1 = temp; - temp += temp; - h1 += (temp < l1) << 1; - l1 = temp; - temp += temp; - h1 += (temp < l1); - *lv = temp; - *hv = h1; - return; - } - } - - encode (arg1, l1, h1); - encode (arg2, l2, h2); - - bzero (prod, sizeof prod); - - for (i = 0; i < 8; i++) - for (j = 0; j < 8; j++) - { - k = i + j; - carry = arg1[i] * arg2[j]; - while (carry) - { - carry += prod[k]; - prod[k] = carry & 0xff; - carry >>= 8; - k++; - } - } - - decode (prod, lv, hv); /* @@decode ignores prod[8] -> prod[15] */ -} - -/* Shift the 64-bit integer in L1, H1 left by COUNT places - keeping only PREC bits of result. - Shift right if COUNT is negative. - ARITH nonzero specifies arithmetic shifting; otherwise use logical shift. - Store the value as two `int' pieces in *LV and *HV. */ - -static void -lshift_double (l1, h1, count, prec, lv, hv, arith) - int l1, h1, count, prec; - int *lv, *hv; - int arith; -{ - short arg1[8]; - register int i; - register int carry; - - if (count < 0) - { - rshift_double (l1, h1, - count, prec, lv, hv, arith); - return; - } - - encode (arg1, l1, h1); - - if (count > prec) - count = prec; - - while (count > 0) - { - carry = 0; - for (i = 0; i < 8; i++) - { - carry += arg1[i] << 1; - arg1[i] = carry & 0xff; - carry >>= 8; - } - count--; - } - - decode (arg1, lv, hv); -} - -/* Shift the 64-bit integer in L1, H1 right by COUNT places - keeping only PREC bits of result. COUNT must be positive. - ARITH nonzero specifies arithmetic shifting; otherwise use logical shift. - Store the value as two `int' pieces in *LV and *HV. */ - -static void -rshift_double (l1, h1, count, prec, lv, hv, arith) - int l1, h1, count, prec; - int *lv, *hv; - int arith; -{ - short arg1[8]; - register int i; - register int carry; - - encode (arg1, l1, h1); - - if (count > prec) - count = prec; - - while (count > 0) - { - carry = arith && arg1[7] >> 7; - for (i = 7; i >= 0; i--) - { - carry <<= 8; - carry += arg1[i]; - arg1[i] = (carry >> 1) & 0xff; - } - count--; - } - - decode (arg1, lv, hv); -} - -/* Rotate the 64-bit integer in L1, H1 left by COUNT places - keeping only PREC bits of result. - Rotate right if COUNT is negative. - Store the value as two `int' pieces in *LV and *HV. */ - -static void -lrotate_double (l1, h1, count, prec, lv, hv) - int l1, h1, count, prec; - int *lv, *hv; -{ - short arg1[8]; - register int i; - register int carry; - - if (count < 0) - { - rrotate_double (l1, h1, - count, prec, lv, hv); - return; - } - - encode (arg1, l1, h1); - - if (count > prec) - count = prec; - - carry = arg1[7] >> 7; - while (count > 0) - { - for (i = 0; i < 8; i++) - { - carry += arg1[i] << 1; - arg1[i] = carry & 0xff; - carry >>= 8; - } - count--; - } - - decode (arg1, lv, hv); -} - -/* Rotate the 64-bit integer in L1, H1 left by COUNT places - keeping only PREC bits of result. COUNT must be positive. - Store the value as two `int' pieces in *LV and *HV. */ - -static void -rrotate_double (l1, h1, count, prec, lv, hv) - int l1, h1, count, prec; - int *lv, *hv; -{ - short arg1[8]; - register int i; - register int carry; - - encode (arg1, l1, h1); - - if (count > prec) - count = prec; - - carry = arg1[0] & 1; - while (count > 0) - { - for (i = 7; i >= 0; i--) - { - carry <<= 8; - carry += arg1[i]; - arg1[i] = (carry >> 1) & 0xff; - } - count--; - } - - decode (arg1, lv, hv); -} - -/* Divide 64 bit integer LNUM, HNUM by 64 bit integer LDEN, HDEN - for a quotient (stored in *LQUO, *HQUO) and remainder (in *LREM, *HREM). - CODE is a tree code for a kind of division, one of - TRUNC_DIV_EXPR, FLOOR_DIV_EXPR, CEIL_DIV_EXPR, ROUND_DIV_EXPR - or EXACT_DIV_EXPR - It controls how the quotient is rounded to a integer. - UNS nonzero says do unsigned division. */ - -static void -div_and_round_double (code, uns, - lnum_orig, hnum_orig, lden_orig, hden_orig, - lquo, hquo, lrem, hrem) - enum tree_code code; - int uns; - int lnum_orig, hnum_orig; /* num == numerator == dividend */ - int lden_orig, hden_orig; /* den == denominator == divisor */ - int *lquo, *hquo, *lrem, *hrem; -{ - int quo_neg = 0; - short num[9], den[8], quo[8]; /* extra element for scaling. */ - register int i, j, work; - register int carry = 0; - int lnum = lnum_orig, hnum = hnum_orig; - int lden = lden_orig, hden = hden_orig; - - if ((hden == 0) && (lden == 0)) - abort (); - - /* calculate quotient sign and convert operands to unsigned. */ - if (!uns) - { - if (hden < 0) - { - quo_neg = ~ quo_neg; - neg_double (lden, hden, &lden, &hden); - } - if (hnum < 0) - { - quo_neg = ~ quo_neg; - neg_double (lnum, hnum, &lnum, &hnum); - } - } - - if (hnum == 0 && hden == 0) - { /* single precision */ - *hquo = *hrem = 0; - *lquo = (unsigned) lnum / lden; /* rounds toward zero since positive args */ - goto finish_up; - } - - if (hnum == 0) - { /* trivial case: dividend < divisor */ - /* hden != 0 already checked. */ - *hquo = *lquo = 0; - *hrem = hnum; - *lrem = lnum; - goto finish_up; - } - - bzero (quo, sizeof quo); - - bzero (num, sizeof num); /* to zero 9th element */ - bzero (den, sizeof den); - - encode (num, lnum, hnum); - encode (den, lden, hden); - - if (hden == 0) - { /* simpler algorithm */ - /* hnum != 0 already checked. */ - for (i = 7; i >= 0; i--) - { - work = num[i] + (carry << 8); - quo[i] = work / lden; - carry = work % lden; - } - } - else { /* full double precision, - with thanks to Don Knuth's - "Semi-Numericial Algorithms". */ -#define BASE 256 - int quo_est, scale, num_hi_sig, den_hi_sig, quo_hi_sig; - - /* Find the highest non-zero divisor digit. */ - for (i = 7; ; i--) - if (den[i] != 0) { - den_hi_sig = i; - break; - } - for (i = 7; ; i--) - if (num[i] != 0) { - num_hi_sig = i; - break; - } - quo_hi_sig = num_hi_sig - den_hi_sig + 1; - - /* Insure that the first digit of the divisor is at least BASE/2. - This is required by the quotient digit estimation algorithm. */ - - scale = BASE / (den[den_hi_sig] + 1); - if (scale > 1) { /* scale divisor and dividend */ - carry = 0; - for (i = 0; i <= 8; i++) { - work = (num[i] * scale) + carry; - num[i] = work & 0xff; - carry = work >> 8; - if (num[i] != 0) num_hi_sig = i; - } - carry = 0; - for (i = 0; i <= 7; i++) { - work = (den[i] * scale) + carry; - den[i] = work & 0xff; - carry = work >> 8; - if (den[i] != 0) den_hi_sig = i; - } - } - - /* Main loop */ - for (i = quo_hi_sig; i > 0; i--) { - /* quess the next quotient digit, quo_est, by dividing the first - two remaining dividend digits by the high order quotient digit. - quo_est is never low and is at most 2 high. */ - - int num_hi; /* index of highest remaining dividend digit */ - - num_hi = i + den_hi_sig; - - work = (num[num_hi] * BASE) + (num_hi ? 0 : num[num_hi - 1]); - if (num[num_hi] != den[den_hi_sig]) { - quo_est = work / den[den_hi_sig]; - } - else { - quo_est = BASE - 1; - } - - /* refine quo_est so it's usually correct, and at most one high. */ - while ((den[den_hi_sig - 1] * quo_est) - > (((work - (quo_est * den[den_hi_sig])) * BASE) - + ((num_hi - 1) ? 0 : num[num_hi - 2]))) { - quo_est--; - } - - /* try quo_est as the quotient digit, by multiplying the - divisor by quo_est and subtracting from the remaining dividend. */ - - carry = 0; - - for (j = 0; j <= den_hi_sig; j++) { - int digit; - - work = num[i + j] - (quo_est * den[j]) + carry; - digit = work & 0xff; - carry = work >> 8; - if (digit < 0) { - digit += BASE; - carry--; - } - num[i + j] = digit; - } - - /* if quo_est was high by one, then num[i] went negative and - we need to correct things. */ - - if (num[num_hi] < 0) { - quo_est--; - carry = 0; /* add divisor back in */ - for (j = 0; j <= den_hi_sig; j++) { - work = num[i + j] + den[j] + carry; - if (work > BASE) { - work -= BASE; - carry = 1; - } - else { - carry = 0; - } - num[i + j] = work; - } - num [num_hi] += carry; - } - - /* store the quotient digit. */ - quo[i - 1] = quo_est; - } - } - - decode (quo, lquo, hquo); - - finish_up: - /* if result is negative, make it so. */ - if (quo_neg) - neg_double (*lquo, *hquo, lquo, hquo); - - /* compute trial remainder: rem = num - (quo * den) */ - mul_double (*lquo, *hquo, lden_orig, hden_orig, lrem, hrem); - neg_double (*lrem, *hrem, lrem, hrem); - add_double (lnum_orig, hnum_orig, *lrem, *hrem, lrem, hrem); - - switch (code) - { - case TRUNC_DIV_EXPR: - case TRUNC_MOD_EXPR: /* round toward zero */ - case EXACT_DIV_EXPR: /* for this one, it shouldn't matter */ - return; - - case FLOOR_DIV_EXPR: - case FLOOR_MOD_EXPR: /* round toward negative infinity */ - if (quo_neg && (*lrem != 0 || *hrem != 0)) /* ratio < 0 && rem != 0 */ - { - /* quo = quo - 1; */ - add_double (*lquo, *hquo, -1, -1, lquo, hquo); - } - else return; - break; - - case CEIL_DIV_EXPR: - case CEIL_MOD_EXPR: /* round toward positive infinity */ - if (!quo_neg && (*lrem != 0 || *hrem != 0)) /* ratio > 0 && rem != 0 */ - { - add_double (*lquo, *hquo, 1, 0, lquo, hquo); - } - else return; - break; - - case ROUND_DIV_EXPR: - case ROUND_MOD_EXPR: /* round to closest integer */ - { - int labs_rem = *lrem, habs_rem = *hrem; - int labs_den = lden, habs_den = hden, ltwice, htwice; - - /* get absolute values */ - if (*hrem < 0) neg_double (*lrem, *hrem, &labs_rem, &habs_rem); - if (hden < 0) neg_double (lden, hden, &labs_den, &habs_den); - - /* if (2 * abs (lrem) >= abs (lden)) */ - mul_double (2, 0, labs_rem, habs_rem, <wice, &htwice); - if (((unsigned) habs_den < (unsigned) htwice) - || (((unsigned) habs_den == (unsigned) htwice) - && ((unsigned) labs_den < (unsigned) ltwice))) - { - if (*hquo < 0) - /* quo = quo - 1; */ - add_double (*lquo, *hquo, -1, -1, lquo, hquo); - else - /* quo = quo + 1; */ - add_double (*lquo, *hquo, 1, 0, lquo, hquo); - } - else return; - } - break; - - default: - abort (); - } - - /* compute true remainder: rem = num - (quo * den) */ - mul_double (*lquo, *hquo, lden_orig, hden_orig, lrem, hrem); - neg_double (*lrem, *hrem, lrem, hrem); - add_double (lnum_orig, hnum_orig, *lrem, *hrem, lrem, hrem); -} - -/* Split a tree IN into a constant and a variable part - that could be combined with CODE to make IN. - CODE must be a commutative arithmetic operation. - Store the constant part into *CONP and the variable in &VARP. - Return 1 if this was done; zero means the tree IN did not decompose - this way. - - If CODE is PLUS_EXPR we also split trees that use MINUS_EXPR. - Therefore, we must tell the caller whether the variable part - was subtracted. We do this by storing 1 or -1 into *VARSIGNP. - The value stored is the coefficient for the variable term. - The constant term we return should always be added; - we negate it if necessary. */ - -static int -split_tree (in, code, varp, conp, varsignp) - tree in; - enum tree_code code; - tree *varp, *conp; - int *varsignp; -{ - register tree outtype = TREE_TYPE (in); - *varp = 0; - *conp = 0; - - /* Strip any conversions that don't change the machine mode. */ - while ((TREE_CODE (in) == NOP_EXPR - || TREE_CODE (in) == CONVERT_EXPR) - && (TYPE_MODE (TREE_TYPE (in)) - == TYPE_MODE (TREE_TYPE (TREE_OPERAND (in, 0))))) - in = TREE_OPERAND (in, 0); - - if (TREE_CODE (in) == code - || (TREE_CODE (TREE_TYPE (in)) != REAL_TYPE - /* We can associate addition and subtraction together - (even though the C standard doesn't say so) - for integers because the value is not affected. - For reals, the value might be affected, so we can't. */ - && - ((code == PLUS_EXPR && TREE_CODE (in) == MINUS_EXPR) - || (code == MINUS_EXPR && TREE_CODE (in) == PLUS_EXPR)))) - { - enum tree_code code = TREE_CODE (TREE_OPERAND (in, 0)); - if (code == INTEGER_CST) - { - *conp = TREE_OPERAND (in, 0); - *varp = TREE_OPERAND (in, 1); - if (TREE_TYPE (*varp) != outtype) - *varp = convert (outtype, *varp); - *varsignp = (TREE_CODE (in) == MINUS_EXPR) ? -1 : 1; - return 1; - } - if (TREE_LITERAL (TREE_OPERAND (in, 1))) - { - *conp = TREE_OPERAND (in, 1); - *varp = TREE_OPERAND (in, 0); - *varsignp = 1; - if (TREE_TYPE (*varp) != outtype) - *varp = convert (outtype, *varp); - if (TREE_CODE (in) == MINUS_EXPR) - { - /* If operation is subtraction and constant is second, - must negate it to get an additive constant. - And this cannot be done unless it is a manifest constant. - It could also be the address of a static variable. - We cannot negate that, so give up. */ - if (TREE_CODE (*conp) == INTEGER_CST) - *conp = fold (build (NEGATE_EXPR, TREE_TYPE (*conp), *conp)); - else - return 0; - } - return 1; - } - if (TREE_LITERAL (TREE_OPERAND (in, 0))) - { - *conp = TREE_OPERAND (in, 0); - *varp = TREE_OPERAND (in, 1); - if (TREE_TYPE (*varp) != outtype) - *varp = convert (outtype, *varp); - *varsignp = (TREE_CODE (in) == MINUS_EXPR) ? -1 : 1; - return 1; - } - } - return 0; -} - -/* Combine two constants NUM and ARG2 under operation CODE - to produce a new constant. - We assume ARG1 and ARG2 have the same data type, - or at least are the same kind of constant and the same machine mode. */ - -/* Handle floating overflow for `combine'. */ -static jmp_buf combine_error; - -tree -combine (code, arg1, arg2) - enum tree_code code; - register tree arg1, arg2; -{ - if (TREE_CODE (arg1) == INTEGER_CST) - { - register int int1l = TREE_INT_CST_LOW (arg1); - register int int1h = TREE_INT_CST_HIGH (arg1); - int int2l = TREE_INT_CST_LOW (arg2); - int int2h = TREE_INT_CST_HIGH (arg2); - int low, hi; - int garbagel, garbageh; - register tree t; - int uns = TREE_UNSIGNED (TREE_TYPE (arg1)); - - switch (code) - { - case BIT_IOR_EXPR: - t = build_int_2 (int1l | int2l, int1h | int2h); - break; - - case BIT_XOR_EXPR: - t = build_int_2 (int1l ^ int2l, int1h ^ int2h); - break; - - case BIT_AND_EXPR: - t = build_int_2 (int1l & int2l, int1h & int2h); - break; - - case BIT_ANDTC_EXPR: - t = build_int_2 (int1l & ~int2l, int1h & ~int2h); - break; - - case RSHIFT_EXPR: - int2l = - int2l; - case LSHIFT_EXPR: - lshift_double (int1l, int1h, int2l, - TYPE_PRECISION (TREE_TYPE (arg1)), - &low, &hi, - !uns); - t = build_int_2 (low, hi); - break; - - case RROTATE_EXPR: - int2l = - int2l; - case LROTATE_EXPR: - lrotate_double (int1l, int1h, int2l, - TYPE_PRECISION (TREE_TYPE (arg1)), - &low, &hi); - t = build_int_2 (low, hi); - break; - - case PLUS_EXPR: - if (int1h == 0) - { - int2l += int1l; - if ((unsigned) int2l < int1l) - int2h += 1; - t = build_int_2 (int2l, int2h); - break; - } - if (int2h == 0) - { - int1l += int2l; - if ((unsigned) int1l < int2l) - int1h += 1; - t = build_int_2 (int1l, int1h); - break; - } - add_double (int1l, int1h, int2l, int2h, &low, &hi); - t = build_int_2 (low, hi); - break; - - case MINUS_EXPR: - if (int1h == 0 && int1l == 0) - { - t = build_int_2 (- int2l, - int2h - (int2l != 0)); - break; - } - if (int2h == 0 && int2l == 0) - { - t = build_int_2 (int1l, int1h); - break; - } - neg_double (int2l, int2h, &int2l, &int2h); - add_double (int1l, int1h, int2l, int2h, &low, &hi); - t = build_int_2 (low, hi); - break; - - case MULT_EXPR: - /* Optimize simple cases. */ - if (int1h == 0) - { - unsigned temp; - - switch (int1l) - { - case 0: - t = build_int_2 (0, 0); - goto got_it; - case 1: - t = build_int_2 (int2l, int2h); - goto got_it; - case 2: - temp = int2l + int2l; - int2h = int2h * 2 + (temp < int2l); - t = build_int_2 (temp, int2h); - goto got_it; - case 3: - temp = int2l + int2l + int2l; - int2h = int2h * 3 + (temp < int2l); - t = build_int_2 (temp, int2h); - goto got_it; - case 4: - temp = int2l + int2l; - int2h = int2h * 4 + (temp < int2l) << 1; - int2l = temp; - temp += temp; - int2h += (temp < int2l); - t = build_int_2 (temp, int2h); - goto got_it; - case 8: - temp = int2l + int2l; - int2h = int2h * 8 + (temp < int2l) << 2; - int2l = temp; - temp += temp; - int2h += (temp < int2l) << 1; - int2l = temp; - temp += temp; - int2h += (temp < int2l); - t = build_int_2 (temp, int2h); - goto got_it; - default: - break; - } - } - - if (int2h == 0) - { - if (int2l == 0) - { - t = build_int_2 (0, 0); - break; - } - if (int2l == 1) - { - t = build_int_2 (int1l, int1h); - break; - } - } - - mul_double (int1l, int1h, int2l, int2h, &low, &hi); - t = build_int_2 (low, hi); - break; - - case TRUNC_DIV_EXPR: case ROUND_DIV_EXPR: - case FLOOR_DIV_EXPR: case CEIL_DIV_EXPR: - case EXACT_DIV_EXPR: - if (int2h == 0 && int2l == 1) - { - t = build_int_2 (int1l, int1h); - break; - } - if (int1l == int2l && int1h == int2h) - { - if ((int1l | int1h) == 0) - abort (); - t = build_int_2 (1, 0); - break; - } - div_and_round_double (code, uns, int1l, int1h, int2l, int2h, - &low, &hi, &garbagel, &garbageh); - t = build_int_2 (low, hi); - break; - - case TRUNC_MOD_EXPR: case ROUND_MOD_EXPR: - case FLOOR_MOD_EXPR: case CEIL_MOD_EXPR: - div_and_round_double (code, uns, int1l, int1h, int2l, int2h, - &garbagel, &garbageh, &low, &hi); - t = build_int_2 (low, hi); - break; - - case MIN_EXPR: - case MAX_EXPR: - if (uns) - { - low = (((unsigned) int1h < (unsigned) int2h) - || (((unsigned) int1h == (unsigned) int2h) - && ((unsigned) int1l < (unsigned) int2l))); - } - else - { - low = ((int1h < int2h) - || ((int1h == int2h) - && ((unsigned) int1l < (unsigned) int2l))); - } - if (low == (code == MIN_EXPR)) - t = build_int_2 (int1l, int1h); - else - t = build_int_2 (int2l, int2h); - break; - - default: - abort (); - } - got_it: - TREE_TYPE (t) = TREE_TYPE (arg1); - force_fit_type (t); - return t; - } -#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) - if (TREE_CODE (arg1) == REAL_CST) - { - register REAL_VALUE_TYPE d1 = TREE_REAL_CST (arg1); - register REAL_VALUE_TYPE d2 = TREE_REAL_CST (arg2); - register REAL_VALUE_TYPE value; - - if (setjmp (combine_error)) - { - warning ("floating overflow in constant folding"); - return build (code, TREE_TYPE (arg1), arg1, arg2); - } - set_float_handler (combine_error); - -#ifdef REAL_ARITHMETIC - REAL_ARITHMETIC (value, code, d1, d2); -#else - switch (code) - { - case PLUS_EXPR: - value = d1 + d2; - break; - - case MINUS_EXPR: - value = d1 - d2; - break; - - case MULT_EXPR: - value = d1 * d2; - break; - - case RDIV_EXPR: - if (d2 == 0) - abort (); - - value = d1 / d2; - break; - - case MIN_EXPR: - value = d1 < d2 ? d1 : d2; - break; - - case MAX_EXPR: - value = d1 > d2 ? d1 : d2; - break; - - default: - abort (); - } -#endif /* no REAL_ARITHMETIC */ - set_float_handler (0); - return build_real (TREE_TYPE (arg1), value); - } -#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */ - if (TREE_CODE (arg1) == COMPLEX_CST) - { - register tree r1 = TREE_REALPART (arg1); - register tree i1 = TREE_IMAGPART (arg1); - register tree r2 = TREE_REALPART (arg2); - register tree i2 = TREE_IMAGPART (arg2); - register tree t; - - switch (code) - { - case PLUS_EXPR: - t = build_complex (combine (PLUS_EXPR, r1, r2), - combine (PLUS_EXPR, i1, i2)); - break; - - case MINUS_EXPR: - t = build_complex (combine (MINUS_EXPR, r1, r2), - combine (MINUS_EXPR, i1, i2)); - break; - - case MULT_EXPR: - t = build_complex (combine (MINUS_EXPR, - combine (MULT_EXPR, r1, r2), - combine (MULT_EXPR, i1, i2)), - combine (PLUS_EXPR, - combine (MULT_EXPR, r1, i2), - combine (MULT_EXPR, i1, r2))); - break; - - case RDIV_EXPR: - { - register tree magsquared - = combine (PLUS_EXPR, - combine (MULT_EXPR, r2, r2), - combine (MULT_EXPR, i2, i2)); - t = build_complex (combine (RDIV_EXPR, - combine (PLUS_EXPR, - combine (MULT_EXPR, r1, r2), - combine (MULT_EXPR, i1, i2)), - magsquared), - combine (RDIV_EXPR, - combine (MINUS_EXPR, - combine (MULT_EXPR, i1, r2), - combine (MULT_EXPR, r1, i2)), - magsquared)); - } - break; - - default: - abort (); - } - TREE_TYPE (t) = TREE_TYPE (arg1); - return t; - } - return 0; -} - -/* Given T, a tree representing type conversion of a constant, - return a constant tree representing the result of conversion. */ - -static tree -fold_convert (t) - register tree t; -{ - register tree arg1 = TREE_OPERAND (t, 0); - register tree type = TREE_TYPE (t); - - if (TREE_CODE (type) == POINTER_TYPE - || TREE_CODE (type) == INTEGER_TYPE - || TREE_CODE (type) == ENUMERAL_TYPE) - { - if (TREE_CODE (arg1) == INTEGER_CST) - { - /* Given an integer constant, make new constant with new type, - appropriately sign-extended or truncated. */ - t = build_int_2 (TREE_INT_CST_LOW (arg1), - TREE_INT_CST_HIGH (arg1)); - TREE_TYPE (t) = type; - force_fit_type (t); - } -#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) - else if (TREE_CODE (arg1) == REAL_CST) - { - if (REAL_VALUES_LESS (real_value_from_int_cst (TYPE_MAX_VALUE (type)), - TREE_REAL_CST (arg1)) - || REAL_VALUES_LESS (TREE_REAL_CST (arg1), - real_value_from_int_cst (TYPE_MIN_VALUE (type)))) - { - warning ("real constant out of range for integer conversion"); - return t; - } -#ifndef REAL_ARITHMETIC - { - REAL_VALUE_TYPE d; - int low, high; - int half_word = 1 << (HOST_BITS_PER_INT / 2); - - d = TREE_REAL_CST (arg1); - if (d < 0) - d = -d; - - high = (int) (d / half_word / half_word); - d -= (REAL_VALUE_TYPE) high * half_word * half_word; - low = (unsigned) d; - if (TREE_REAL_CST (arg1) < 0) - neg_double (low, high, &low, &high); - t = build_int_2 (low, high); - } -#else - { - int low, high; - REAL_VALUE_TO_INT (low, high, TREE_REAL_CST (arg1)); - t = build_int_2 (low, high); - } -#endif - TREE_TYPE (t) = type; - force_fit_type (t); - } -#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */ - TREE_TYPE (t) = type; - } - else if (TREE_CODE (type) == REAL_TYPE) - { -#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) - if (TREE_CODE (arg1) == INTEGER_CST) - return build_real_from_int_cst (type, arg1); -#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */ - if (TREE_CODE (arg1) == REAL_CST) - return build_real (type, TREE_REAL_CST (arg1)); - } - TREE_LITERAL (t) = 1; - return t; -} - -/* Return nonzero if two constants (that are not manifest constants) - are necessarily equal. It detects only the easiest, common case of - equality. */ - -static int -operand_equal_p (arg0, arg1) - tree arg0, arg1; -{ - while ((TREE_CODE (arg0) == NOP_EXPR - || TREE_CODE (arg0) == CONVERT_EXPR) - && TYPE_MODE (TREE_TYPE (arg0)) == TYPE_MODE (TREE_TYPE (TREE_OPERAND (arg0, 0)))) - arg0 = TREE_OPERAND (arg0, 0); - while ((TREE_CODE (arg1) == NOP_EXPR - || TREE_CODE (arg1) == CONVERT_EXPR) - && TYPE_MODE (TREE_TYPE (arg1)) == TYPE_MODE (TREE_TYPE (TREE_OPERAND (arg1, 0)))) - arg1 = TREE_OPERAND (arg1, 0); - - if (TREE_CODE (arg0) == TREE_CODE (arg1) - && TREE_CODE (arg0) == ADDR_EXPR - && TREE_OPERAND (arg0, 0) == TREE_OPERAND (arg1, 0)) - return 1; - return 0; -} - -#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) - -/* Return 1 if ARG is a real constant with value zero. - This function is not defined in the case where it is impossible - to tell whether a real constant is zero (for cross-compilation). */ - -static int -real_zerop (arg) - tree arg; -{ -#ifdef REAL_IS_NOT_DOUBLE - tree t1 = build_real_from_int_cst (TREE_TYPE (arg), integer_zero_node); - return REAL_VALUES_EQUAL (TREE_REAL_CST (arg), TREE_REAL_CST (t1)); -#else - return TREE_REAL_CST (arg) == 0; -#endif -} -#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */ - -/* Perform constant folding and related simplification of EXPR. - The related simplifications include x*1 => x, x*0 => 0, etc., - and application of the associative law. - NOP_EXPR conversions may be removed freely (as long as we - are careful not to change the C type of the overall expression) - We cannot simplify through a CONVERT_EXPR, FIX_EXPR or FLOAT_EXPR, - but we can constant-fold them if they have constant operands. */ - -tree -fold (expr) - tree expr; -{ - register tree t = expr; - tree type = TREE_TYPE (expr); - register tree arg0, arg1; - register enum tree_code code = TREE_CODE (t); - register int kind; - - /* WINS will be nonzero when the switch is done - if all operands are constant. - - LOSES will be nonzero when the switch is done - if any operand is volatile. - This inhibits optimizations such as (foo () * 0) => 0. - But identity-element optimizations such as - (foo () * 1) => (foo ()) can be done even if LOSES is set. */ - - int wins = 1; - int loses = 0; - - /* Return right away if already constant. */ - if (TREE_LITERAL (t)) - { - if (code == CONST_DECL) - return DECL_INITIAL (t); - return t; - } - - kind = *tree_code_type[(int) code]; - if (kind == 'e' || kind == 'r') - { - register int len = tree_code_length[(int) code]; - register int i; - for (i = 0; i < len; i++) - { - if (TREE_OPERAND (t, i) == 0) - continue; /* Valid for CALL_EXPR, at least. */ - if (TREE_CODE (TREE_OPERAND (t, i)) != INTEGER_CST -#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) - && TREE_CODE (TREE_OPERAND (t, i)) != REAL_CST -#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */ - ) - /* Note that TREE_LITERAL isn't enough: - static var addresses are constant but we can't - do arithmetic on them. */ - wins = 0; - if (TREE_VOLATILE (TREE_OPERAND (t, i))) - loses = 1; - } - arg0 = TREE_OPERAND (t, 0); - if (len > 1) - arg1 = TREE_OPERAND (t, 1); - } - - /* Now WINS and LOSES are set as described above, - ARG0 is the first operand of EXPR, - and ARG1 is the second operand (if it has more than one operand). */ - - switch (code) - { - case INTEGER_CST: - case REAL_CST: - case STRING_CST: - case COMPLEX_CST: - case CONSTRUCTOR: - return t; - - case CONST_DECL: - return fold (DECL_INITIAL (t)); - - case NOP_EXPR: - case FLOAT_EXPR: - case CONVERT_EXPR: - case FIX_TRUNC_EXPR: - /* Other kinds of FIX are not handled properly by fold_convert. */ - if (!wins) - { - TREE_LITERAL (t) = TREE_LITERAL (arg0); - return t; - } - return fold_convert (t); - -#if 0 /* This loses on &"foo"[0]. */ - case ARRAY_REF: - { - int i; - - /* Fold an expression like: "foo"[2] */ - if (TREE_CODE (arg0) == STRING_CST - && TREE_CODE (arg1) == INTEGER_CST - && !TREE_INT_CST_HIGH (arg1) - && (i = TREE_INT_CST_LOW (arg1)) < TREE_STRING_LENGTH (arg0)) - { - t = build_int_2 (TREE_STRING_POINTER (arg0)[i], 0); - TREE_TYPE (t) = TREE_TYPE (TREE_TYPE (arg0)); - force_fit_type (t); - } - } - return t; -#endif /* 0 */ - - case RANGE_EXPR: - TREE_LITERAL (t) = wins; - return t; - - case NEGATE_EXPR: - if (wins) - { - if (TREE_CODE (arg0) == INTEGER_CST) - { - if (TREE_INT_CST_LOW (arg0) == 0) - t = build_int_2 (0, - TREE_INT_CST_HIGH (arg0)); - else - t = build_int_2 (- TREE_INT_CST_LOW (arg0), - ~ TREE_INT_CST_HIGH (arg0)); - TREE_TYPE (t) = type; - force_fit_type (t); - } - else if (TREE_CODE (arg0) == REAL_CST) - t = build_real (type, REAL_VALUE_NEGATE (TREE_REAL_CST (arg0))); - TREE_TYPE (t) = type; - } - return t; - - case ABS_EXPR: - if (wins) - { - if (TREE_CODE (arg0) == INTEGER_CST) - { - if (! TREE_UNSIGNED (type) - && TREE_INT_CST_HIGH (arg0) < 0) - { - if (TREE_INT_CST_LOW (arg0) == 0) - t = build_int_2 (0, - TREE_INT_CST_HIGH (arg0)); - else - t = build_int_2 (- TREE_INT_CST_LOW (arg0), - ~ TREE_INT_CST_HIGH (arg0)); - } - } - else if (TREE_CODE (arg0) == REAL_CST) - { - if ( -#if defined (REAL_IS_NOT_DOUBLE) - REAL_VALUES_LESS (TREE_REAL_CST (arg0), - REAL_VALUE_ATOF ("0.0")) -#else - REAL_VALUES_LESS (TREE_REAL_CST (arg0), 0) -#endif - ) - t = build_real (type, - REAL_VALUE_NEGATE (TREE_REAL_CST (arg0))); - } - TREE_TYPE (t) = type; - } - return t; - - case BIT_NOT_EXPR: - if (wins) - { - if (TREE_CODE (arg0) == INTEGER_CST) - t = build_int_2 (~ TREE_INT_CST_LOW (arg0), - ~ TREE_INT_CST_HIGH (arg0)); - TREE_TYPE (t) = type; - force_fit_type (t); - } - return t; - - case PLUS_EXPR: - if (integer_zerop (arg0)) - return convert (type, arg1); - if (integer_zerop (arg1)) - return convert (type, arg0); - associate: - /* In most languages, can't associate operations on floats - through parentheses. Rather than remember where the parentheses - were, we don't associate floats at all. It shouldn't matter much. */ - if (TREE_CODE (type) == REAL_TYPE) - goto binary; - /* The varsign == -1 cases happen only for addition and subtraction. - It says that the arg that was split was really CON minus VAR. - The rest of the code applies to all associative operations. */ - if (!wins) - { - tree var, con, tem; - int varsign; - - if (split_tree (arg0, code, &var, &con, &varsign)) - { - if (varsign == -1) - { - /* EXPR is (CON-VAR) +- ARG1. */ - /* If it is + and VAR==ARG1, return just CONST. */ - if (code == PLUS_EXPR && operand_equal_p (var, arg1)) - return convert (TREE_TYPE (t), con); - - /* Otherwise return (CON +- ARG1) - VAR. */ - TREE_SET_CODE (t, MINUS_EXPR); - TREE_OPERAND (t, 1) = var; - TREE_OPERAND (t, 0) - = fold (build (code, TREE_TYPE (t), con, arg1)); - } - else - { - /* EXPR is (VAR+CON) +- ARG1. */ - /* If it is - and VAR==ARG1, return just CONST. */ - if (code == MINUS_EXPR && operand_equal_p (var, arg1)) - return convert (TREE_TYPE (t), con); - - /* Otherwise return VAR +- (ARG1 +- CON). */ - TREE_OPERAND (t, 1) = tem - = fold (build (code, TREE_TYPE (t), arg1, con)); - TREE_OPERAND (t, 0) = var; - if (integer_zerop (tem) - && (code == PLUS_EXPR || code == MINUS_EXPR)) - return var; - /* If we have x +/- (c - d) [c an explicit integer] - change it to x -/+ (d - c) since if d is relocatable - then the latter can be a single immediate insn - and the former cannot. */ - if (TREE_CODE (tem) == MINUS_EXPR - && TREE_CODE (TREE_OPERAND (tem, 0)) == INTEGER_CST) - { - tree tem1 = TREE_OPERAND (tem, 1); - TREE_OPERAND (tem, 1) = TREE_OPERAND (tem, 0); - TREE_OPERAND (tem, 0) = tem1; - TREE_SET_CODE (t, - (code == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR)); - } - } - return t; - } - - if (split_tree (arg1, code, &var, &con, &varsign)) - { - /* EXPR is ARG0 +- (CON +- VAR). */ - if (varsign == -1) - TREE_SET_CODE (t, - (code == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR)); - if (TREE_CODE (t) == MINUS_EXPR && operand_equal_p (var, arg0)) - { - /* If VAR and ARG0 cancel, return just CON or -CON. */ - if (code == PLUS_EXPR) - return convert (TREE_TYPE (t), con); - return fold (build (NEGATE_EXPR, TREE_TYPE (t), - convert (TREE_TYPE (t), con))); - } - TREE_OPERAND (t, 0) - = fold (build (code, TREE_TYPE (t), arg0, con)); - TREE_OPERAND (t, 1) = var; - if (integer_zerop (TREE_OPERAND (t, 0)) - && TREE_CODE (t) == PLUS_EXPR) - return convert (TREE_TYPE (t), var); - return t; - } - } - binary: -#if defined (REAL_IS_NOT_DOUBLE) && ! defined (REAL_ARITHMETIC) - if (TREE_CODE (arg1) == REAL_CST) - return t; -#endif /* REAL_IS_NOT_DOUBLE, and no REAL_ARITHMETIC */ - { - register tree t1 = NULL_TREE; - if (wins) - t1 = combine (code, arg0, arg1); - if (t1 != NULL_TREE) return t1; - return t; - } - - case MINUS_EXPR: - if (! wins && integer_zerop (arg0)) - return build (NEGATE_EXPR, type, arg1); - if (integer_zerop (arg1)) - return convert (type, arg0); - /* Fold &x - &x. This can happen from &x.foo - &x. */ - if (operand_equal_p (arg0, arg1)) - return convert (TREE_TYPE (t), integer_zero_node); - goto associate; - - case MULT_EXPR: - if (!loses && integer_zerop (arg0)) - return convert (type, arg0); - if (!loses && integer_zerop (arg1)) - return convert (type, arg1); - if (integer_onep (arg0)) - return convert (type, arg1); - if (integer_onep (arg1)) - return convert (type, arg0); - goto associate; - - case BIT_IOR_EXPR: - if (!loses && integer_all_onesp (arg0)) - return convert (type, arg0); - if (!loses && integer_all_onesp (arg1)) - return convert (type, arg1); - case BIT_XOR_EXPR: - if (integer_zerop (arg0)) - return convert (type, arg1); - if (integer_zerop (arg1)) - return convert (type, arg0); - goto associate; - - case BIT_AND_EXPR: - if (integer_all_onesp (arg0)) - return convert (type, arg1); - if (integer_all_onesp (arg1)) - return convert (type, arg0); - if (!loses && integer_zerop (arg0)) - return convert (type, arg0); - if (!loses && integer_zerop (arg1)) - return convert (type, arg1); - /* Simplify ((int)c & 0x377) into (int)c, if c is unsigned char. */ - if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == NOP_EXPR - && TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (arg1, 0)))) - { - int prec = TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg1, 0))); - if (prec < BITS_PER_WORD && prec < HOST_BITS_PER_INT - && (~TREE_INT_CST_LOW (arg0) & ((1 << prec) - 1)) == 0) - return build (NOP_EXPR, type, TREE_OPERAND (arg1, 0)); - } - if (TREE_CODE (arg1) == INTEGER_CST && TREE_CODE (arg0) == NOP_EXPR - && TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (arg0, 0)))) - { - int prec = TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg0, 0))); - if (prec < BITS_PER_WORD && prec < HOST_BITS_PER_INT - && (~TREE_INT_CST_LOW (arg1) & ((1 << prec) - 1)) == 0) - return build (NOP_EXPR, type, TREE_OPERAND (arg0, 0)); - } - goto associate; - - case BIT_ANDTC_EXPR: - if (integer_all_onesp (arg0)) - return convert (type, arg1); - if (integer_zerop (arg1)) - return convert (type, arg0); - if (!loses && integer_zerop (arg0)) - return convert (type, arg0); - if (!loses && integer_all_onesp (arg1)) - return combine (code, arg1, arg1); - goto binary; - - case TRUNC_DIV_EXPR: - case ROUND_DIV_EXPR: - case FLOOR_DIV_EXPR: - case CEIL_DIV_EXPR: - case EXACT_DIV_EXPR: - case RDIV_EXPR: - if (integer_onep (arg1)) - return convert (type, arg0); - if (integer_zerop (arg1)) - return t; -#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) - if (TREE_CODE (arg1) == REAL_CST - && real_zerop (arg1)) - return t; -#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */ - - goto binary; - - case CEIL_MOD_EXPR: - case FLOOR_MOD_EXPR: - case ROUND_MOD_EXPR: - case TRUNC_MOD_EXPR: - if (!loses && integer_onep (arg1)) - return combine (code, arg1, arg1); - if (integer_zerop (arg1)) - return t; - goto binary; - - case LSHIFT_EXPR: - case RSHIFT_EXPR: - case LROTATE_EXPR: - case RROTATE_EXPR: - if (integer_zerop (arg1)) - return convert (type, arg0); - goto binary; - - case MIN_EXPR: case MAX_EXPR: - goto associate; - - case TRUTH_NOT_EXPR: - /* Note that the operand of this must be an int - and its values must be 0 or 1. - ("true" is a fixed value perhaps depending on the language, - but we don't handle values other than 1 correctly yet.) */ - if (TREE_CODE (arg0) == INTEGER_CST) - { - t = build_int_2 ((TREE_INT_CST_LOW (arg0) == 0 - && TREE_INT_CST_HIGH (arg0) == 0), - 0); - TREE_TYPE (t) = integer_type_node; - } - return t; - - case TRUTH_ANDIF_EXPR: - /* Note that the operands of this must be ints - and their values must be 0 or 1. - ("true" is a fixed value perhaps depending on the language.) */ - /* If first arg is constant zero, return it. */ - if (TREE_CODE (arg0) == INTEGER_CST && integer_zerop (arg0)) - return arg0; - case TRUTH_AND_EXPR: - /* If either arg is constant true, drop it. */ - if (TREE_CODE (arg0) == INTEGER_CST && ! integer_zerop (arg0)) - return arg1; - if (TREE_CODE (arg1) == INTEGER_CST && ! integer_zerop (arg1)) - return arg0; - /* Both known to be zero => return zero. */ - if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) - return arg0; - return t; - - case TRUTH_ORIF_EXPR: - /* Note that the operands of this must be ints - and their values must be 0 or true. - ("true" is a fixed value perhaps depending on the language.) */ - /* If first arg is constant true, return it. */ - if (TREE_CODE (arg0) == INTEGER_CST && ! integer_zerop (arg0)) - return arg0; - case TRUTH_OR_EXPR: - /* If either arg is constant zero, drop it. */ - if (TREE_CODE (arg0) == INTEGER_CST && integer_zerop (arg0)) - return arg1; - if (TREE_CODE (arg1) == INTEGER_CST && integer_zerop (arg1)) - return arg0; - /* Both known to be true => return true. */ - if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) - return arg0; - return t; - - case EQ_EXPR: - case NE_EXPR: - case LT_EXPR: - case GT_EXPR: - case LE_EXPR: - case GE_EXPR: - /* If one arg is a constant integer, put it last. */ - if (TREE_CODE (arg0) == INTEGER_CST - && TREE_CODE (arg1) != INTEGER_CST) - { - TREE_OPERAND (t, 0) = arg1; - TREE_OPERAND (t, 1) = arg0; - arg0 = TREE_OPERAND (t, 0); - arg1 = TREE_OPERAND (t, 1); - switch (code) - { - case GT_EXPR: - code = LT_EXPR; - break; - case GE_EXPR: - code = LE_EXPR; - break; - case LT_EXPR: - code = GT_EXPR; - break; - case LE_EXPR: - code = GE_EXPR; - break; - } - TREE_SET_CODE (t, code); - } - - /* Convert foo++ == CONST into ++foo == CONST + INCR. - First, see if one arg is constant; find the constant arg - and the other one. */ - { - tree constop = 0, varop; - tree *constoploc; - - if (TREE_LITERAL (arg1)) - constoploc = &TREE_OPERAND (t, 1), constop = arg1, varop = arg0; - if (TREE_LITERAL (arg0)) - constoploc = &TREE_OPERAND (t, 0), constop = arg0, varop = arg1; - - if (constop && TREE_CODE (varop) == POSTINCREMENT_EXPR) - { - tree newconst - = fold (build (PLUS_EXPR, TREE_TYPE (constop), - constop, TREE_OPERAND (varop, 1))); - /* This optimization is invalid for ordered comparisons - if CONST+INCR overflows or if foo+incr might overflow. - For pointer types we assume overflow doesn't happen. */ - if (TREE_CODE (TREE_TYPE (varop)) == POINTER_TYPE - || code == EQ_EXPR || code == NE_EXPR) - { - TREE_SET_CODE (varop, PREINCREMENT_EXPR); - *constoploc = newconst; - return t; - } - } - else if (constop && TREE_CODE (varop) == POSTDECREMENT_EXPR) - { - tree newconst - = fold (build (MINUS_EXPR, TREE_TYPE (constop), - constop, TREE_OPERAND (varop, 1))); - if (TREE_CODE (TREE_TYPE (varop)) == POINTER_TYPE - || code == EQ_EXPR || code == NE_EXPR) - { - TREE_SET_CODE (varop, PREDECREMENT_EXPR); - *constoploc = newconst; - return t; - } - } - } - - /* Change X >= CST to X > (CST - 1) if CST is positive. */ - if (TREE_CODE (arg1) == INTEGER_CST - && TREE_CODE (arg0) != INTEGER_CST - && ! tree_int_cst_lt (arg1, integer_one_node)) - { - switch (TREE_CODE (t)) - { - case GE_EXPR: - code = GT_EXPR; - TREE_SET_CODE (t, code); - arg1 = combine (MINUS_EXPR, arg1, integer_one_node); - TREE_OPERAND (t, 1) = arg1; - break; - - case LT_EXPR: - code = LE_EXPR; - TREE_SET_CODE (t, code); - arg1 = combine (MINUS_EXPR, arg1, integer_one_node); - TREE_OPERAND (t, 1) = arg1; - } - } - - /* An unsigned comparison against 0 can be simplified. */ - if (integer_zerop (arg1) - && (TREE_CODE (TREE_TYPE (arg1)) == INTEGER_TYPE - || TREE_CODE (TREE_TYPE (arg1)) == POINTER_TYPE) - && TREE_UNSIGNED (TREE_TYPE (arg1))) - { - switch (TREE_CODE (t)) - { - case GT_EXPR: - TREE_SET_CODE (t, NE_EXPR); - break; - case LE_EXPR: - TREE_SET_CODE (t, EQ_EXPR); - break; - case GE_EXPR: - return build (COMPOUND_EXPR, integer_type_node, - arg0, integer_one_node); - case LT_EXPR: - return build (COMPOUND_EXPR, integer_type_node, - arg0, integer_zero_node); - } - } - - /* To compute GT, swap the arguments and do LT. - To compute GE, do LT and invert the result. - To compute LE, swap the arguments, do LT and invert the result. - To compute NE, do EQ and invert the result. */ - if (code == LE_EXPR || code == GT_EXPR) - { - register tree temp = arg0; - arg0 = arg1; - arg1 = temp; - } - - /* Compute a result for LT or EQ if args permit; - otherwise return T. */ - if (TREE_CODE (arg0) == INTEGER_CST - && TREE_CODE (arg1) == INTEGER_CST) - { - if (code == EQ_EXPR || code == NE_EXPR) - t = build_int_2 - (TREE_INT_CST_LOW (arg0) == TREE_INT_CST_LOW (arg1) - && TREE_INT_CST_HIGH (arg0) == TREE_INT_CST_HIGH (arg1), - 0); - else - t = build_int_2 ((TREE_UNSIGNED (TREE_TYPE (arg0)) - ? INT_CST_LT_UNSIGNED (arg0, arg1) - : INT_CST_LT (arg0, arg1)), - 0); - } - else if (TREE_CODE (arg1) == INTEGER_CST - && TREE_LITERAL (arg0) - && TREE_CODE (arg0) == ADDR_EXPR - && (code == EQ_EXPR || code == NE_EXPR)) - { - t = build_int_2 (0, 0); - } - else if (TREE_CODE (arg0) == REAL_CST - && TREE_CODE (arg1) == REAL_CST) - { - if (code == EQ_EXPR || code == NE_EXPR) - t = build_int_2 (REAL_VALUES_EQUAL (TREE_REAL_CST (arg0), - TREE_REAL_CST (arg1)), - 0); - else - t = build_int_2 (REAL_VALUES_LESS (TREE_REAL_CST (arg0), - TREE_REAL_CST (arg1)), - 0); - } - else - return t; - - /* If what we want is other than LT or EQ, invert the result. */ - if (code == GE_EXPR || code == LE_EXPR || code == NE_EXPR) - TREE_INT_CST_LOW (t) ^= 1; - TREE_TYPE (t) = type; - return t; - - case COND_EXPR: - if (TREE_LITERAL (arg0)) - return TREE_OPERAND (expr, (integer_zerop (arg0) ? 2 : 1)); - return t; - - default: - return t; - } /* switch (code) */ -} diff --git a/gnu/usr.bin/gcc1/cc1/gdbfiles.h b/gnu/usr.bin/gcc1/cc1/gdbfiles.h deleted file mode 100644 index f58d301607..0000000000 --- a/gnu/usr.bin/gcc1/cc1/gdbfiles.h +++ /dev/null @@ -1,15 +0,0 @@ - -/* Alist matching source file names to GDB filenumbers. - Used in output_source_line. */ - -struct gdbfile -{ - struct gdbfile *next; - char *name; /* name of source file */ - int filenum; /* Assigned number */ - int nlines; /* # lines generated for this source file */ -}; - -/* Chain of all `struct gdbfile's. */ - -extern struct gdbfile *gdbfiles; diff --git a/gnu/usr.bin/gcc1/cc1/gencodes.c b/gnu/usr.bin/gcc1/cc1/gencodes.c deleted file mode 100644 index 183aeadb06..0000000000 --- a/gnu/usr.bin/gcc1/cc1/gencodes.c +++ /dev/null @@ -1,154 +0,0 @@ -/* Generate from machine description: - - - some macros CODE_FOR_... giving the insn_code_number value - for each of the defined standard insn names. - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include -#include "config.h" -#include "rtl.h" -#include "obstack.h" - -struct obstack obstack; -struct obstack *rtl_obstack = &obstack; - -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free -extern int xmalloc (); -extern void free (); - -void fatal (); -void fancy_abort (); - -int insn_code_number; - -void -gen_insn (insn) - rtx insn; -{ - /* Don't mention instructions whose names are the null string. - They are in the machine description just to be recognized. */ - if (strlen (XSTR (insn, 0)) != 0) - printf (" CODE_FOR_%s = %d,\n", XSTR (insn, 0), - insn_code_number); -} - -int -xmalloc (size) -{ - register int val = malloc (size); - - if (val == 0) - fatal ("virtual memory exhausted"); - return val; -} - -int -xrealloc (ptr, size) - char *ptr; - int size; -{ - int result = realloc (ptr, size); - if (!result) - fatal ("virtual memory exhausted"); - return result; -} - -void -fatal (s, a1, a2) - char *s; -{ - fprintf (stderr, "gencodes: "); - fprintf (stderr, s, a1, a2); - fprintf (stderr, "\n"); - exit (FATAL_EXIT_CODE); -} - -/* More 'friendly' abort that prints the line and file. - config.h can #define abort fancy_abort if you like that sort of thing. */ - -void -fancy_abort () -{ - fatal ("Internal gcc abort."); -} - -int -main (argc, argv) - int argc; - char **argv; -{ - rtx desc; - FILE *infile; - extern rtx read_rtx (); - register int c; - - obstack_init (rtl_obstack); - - if (argc <= 1) - fatal ("No input file name."); - - infile = fopen (argv[1], "r"); - if (infile == 0) - { - perror (argv[1]); - exit (FATAL_EXIT_CODE); - } - - init_rtl (); - - printf ("/* Generated automatically by the program `gencodes'\n\ -from the machine description file `md'. */\n\n"); - - printf ("#ifndef MAX_INSN_CODE\n\n"); - - /* Read the machine description. */ - - insn_code_number = 0; - printf ("enum insn_code {\n"); - - while (1) - { - c = read_skip_spaces (infile); - if (c == EOF) - break; - ungetc (c, infile); - - desc = read_rtx (infile); - if (GET_CODE (desc) == DEFINE_INSN || GET_CODE (desc) == DEFINE_EXPAND) - { - gen_insn (desc); - insn_code_number++; - } - if (GET_CODE (desc) == DEFINE_PEEPHOLE) - { - insn_code_number++; - } - } - - printf (" CODE_FOR_nothing };\n"); - - printf ("\n#define MAX_INSN_CODE ((int) CODE_FOR_nothing)\n"); - - printf ("#endif /* MAX_INSN_CODE */\n"); - - fflush (stdout); - exit (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE); -} diff --git a/gnu/usr.bin/gcc1/cc1/genconfig.c b/gnu/usr.bin/gcc1/cc1/genconfig.c deleted file mode 100644 index cb152acfc0..0000000000 --- a/gnu/usr.bin/gcc1/cc1/genconfig.c +++ /dev/null @@ -1,267 +0,0 @@ -/* Generate from machine description: - - - some #define configuration flags. - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include -#include "config.h" -#include "rtl.h" -#include "obstack.h" - -struct obstack obstack; -struct obstack *rtl_obstack = &obstack; - -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free -extern int xmalloc (); -extern void free (); - -/* flags to determine output of machine description dependent #define's. */ -int max_recog_operands_flag; -int max_dup_operands_flag; -int max_clobbers_per_insn_flag; -int register_constraint_flag; - -int clobbers_seen_this_insn; -int dup_operands_seen_this_insn; - -void fatal (); -void fancy_abort (); - -void -walk_insn_part (part) - rtx part; -{ - register int i, j; - register RTX_CODE code; - register char *format_ptr; - - if (part == 0) - return; - - code = GET_CODE (part); - switch (code) - { - case CLOBBER: - clobbers_seen_this_insn++; - break; - - case MATCH_OPERAND: - if (XINT (part, 0) > max_recog_operands_flag) - max_recog_operands_flag = XINT (part, 0); - if (XSTR (part, 2) && *XSTR (part, 2)) - register_constraint_flag = 1; - return; - - case MATCH_OPERATOR: - if (XINT (part, 0) > max_recog_operands_flag) - max_recog_operands_flag = XINT (part, 0); - /* Now scan the rtl'x in the vector inside the match_operator. */ - break; - - case LABEL_REF: - if (GET_CODE (XEXP (part, 0)) == MATCH_OPERAND) - break; - return; - - case MATCH_DUP: - ++dup_operands_seen_this_insn; - if (XINT (part, 0) > max_recog_operands_flag) - max_recog_operands_flag = XINT (part, 0); - - case REG: case CONST_INT: case SYMBOL_REF: - case PC: case CC0: - return; - } - - format_ptr = GET_RTX_FORMAT (GET_CODE (part)); - - for (i = 0; i < GET_RTX_LENGTH (GET_CODE (part)); i++) - switch (*format_ptr++) - { - case 'e': - case 'u': - walk_insn_part (XEXP (part, i)); - break; - case 'E': - if (XVEC (part, i) != NULL) - for (j = 0; j < XVECLEN (part, i); j++) - walk_insn_part (XVECEXP (part, i, j)); - break; - } -} - -void -gen_insn (insn) - rtx insn; -{ - int i; - - /* Walk the insn pattern to gather the #define's status. */ - clobbers_seen_this_insn = 0; - dup_operands_seen_this_insn = 0; - if (XVEC (insn, 1) != 0) - for (i = 0; i < XVECLEN (insn, 1); i++) - walk_insn_part (XVECEXP (insn, 1, i)); - - if (clobbers_seen_this_insn > max_clobbers_per_insn_flag) - max_clobbers_per_insn_flag = clobbers_seen_this_insn; - if (dup_operands_seen_this_insn > max_dup_operands_flag) - max_dup_operands_flag = dup_operands_seen_this_insn; -} - -/* Similar but scan a define_expand. */ - -void -gen_expand (insn) - rtx insn; -{ - int i; - - /* Walk the insn pattern to gather the #define's status. */ - - /* Note that we don't bother recording the number of MATCH_DUPs - that occur in a gen_expand, because only reload cares about that. */ - if (XVEC (insn, 1) != 0) - for (i = 0; i < XVECLEN (insn, 1); i++) - { - /* Compute the maximum SETs and CLOBBERS - in any one of the sub-insns; - don't sum across all of them. */ - clobbers_seen_this_insn = 0; - - walk_insn_part (XVECEXP (insn, 1, i)); - - if (clobbers_seen_this_insn > max_clobbers_per_insn_flag) - max_clobbers_per_insn_flag = clobbers_seen_this_insn; - } -} - -void -gen_peephole (peep) - rtx peep; -{ - int i; - - /* Look through the patterns that are matched - to compute the maximum operand number. */ - for (i = 0; i < XVECLEN (peep, 0); i++) - walk_insn_part (XVECEXP (peep, 0, i)); -} - -int -xmalloc (size) -{ - register int val = malloc (size); - - if (val == 0) - fatal ("virtual memory exhausted"); - - return val; -} - -int -xrealloc (ptr, size) - char *ptr; - int size; -{ - int result = realloc (ptr, size); - if (!result) - fatal ("virtual memory exhausted"); - return result; -} - -void -fatal (s, a1, a2) - char *s; -{ - fprintf (stderr, "genconfig: "); - fprintf (stderr, s, a1, a2); - fprintf (stderr, "\n"); - exit (FATAL_EXIT_CODE); -} - -/* More 'friendly' abort that prints the line and file. - config.h can #define abort fancy_abort if you like that sort of thing. */ - -void -fancy_abort () -{ - fatal ("Internal gcc abort."); -} - -int -main (argc, argv) - int argc; - char **argv; -{ - rtx desc; - FILE *infile; - extern rtx read_rtx (); - register int c; - - obstack_init (rtl_obstack); - - if (argc <= 1) - fatal ("No input file name."); - - infile = fopen (argv[1], "r"); - if (infile == 0) - { - perror (argv[1]); - exit (FATAL_EXIT_CODE); - } - - init_rtl (); - - printf ("/* Generated automatically by the program `genconfig'\n\ -from the machine description file `md'. */\n\n"); - - /* Read the machine description. */ - - while (1) - { - c = read_skip_spaces (infile); - if (c == EOF) - break; - ungetc (c, infile); - - desc = read_rtx (infile); - if (GET_CODE (desc) == DEFINE_INSN) - gen_insn (desc); - if (GET_CODE (desc) == DEFINE_EXPAND) - gen_expand (desc); - if (GET_CODE (desc) == DEFINE_PEEPHOLE) - gen_peephole (desc); - } - - /* 3 more than needed for this md file, for the sake of asm constructs. */ - printf ("\n#define MAX_RECOG_OPERANDS %d\n", max_recog_operands_flag + 4); - - if (max_dup_operands_flag == 0) - max_dup_operands_flag = 1; - printf ("\n#define MAX_DUP_OPERANDS %d\n", max_dup_operands_flag); - - if (register_constraint_flag) - printf ("#define REGISTER_CONSTRAINTS\n"); - - fflush (stdout); - exit (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE); -} diff --git a/gnu/usr.bin/gcc1/cc1/genemit.c b/gnu/usr.bin/gcc1/cc1/genemit.c deleted file mode 100644 index 9e8df575e3..0000000000 --- a/gnu/usr.bin/gcc1/cc1/genemit.c +++ /dev/null @@ -1,480 +0,0 @@ -/* Generate code from machine description to emit insns as rtl. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include -#include "config.h" -#include "rtl.h" -#include "obstack.h" - -struct obstack obstack; -struct obstack *rtl_obstack = &obstack; - -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free -extern int xmalloc (); -extern void free (); - -void fatal (); -void fancy_abort (); - -int max_opno; -int max_dup_opno; -int register_constraints; -int insn_code_number; - -#define max(a, b) ((a) > (b) ? (a) : (b)) - -void -max_operand_1 (x) - rtx x; -{ - register RTX_CODE code; - register int i; - register int len; - register char *fmt; - - if (x == 0) - return; - - code = GET_CODE (x); - - if (code == MATCH_OPERAND && XSTR (x, 2) != 0) - register_constraints = 1; - if (code == MATCH_OPERAND || code == MATCH_OPERATOR) - max_opno = max (max_opno, XINT (x, 0)); - if (code == MATCH_DUP) - max_dup_opno = max (max_dup_opno, XINT (x, 0)); - - fmt = GET_RTX_FORMAT (code); - len = GET_RTX_LENGTH (code); - for (i = 0; i < len; i++) - { - if (fmt[i] == 'e' || fmt[i] == 'u') - max_operand_1 (XEXP (x, i)); - else if (fmt[i] == 'E') - { - int j; - for (j = 0; j < XVECLEN (x, i); j++) - max_operand_1 (XVECEXP (x, i, j)); - } - } -} - -int -max_operand_vec (insn, arg) - rtx insn; - int arg; -{ - register int len = XVECLEN (insn, arg); - register int i; - - max_opno = -1; - max_dup_opno = -1; - - for (i = 0; i < len; i++) - max_operand_1 (XVECEXP (insn, arg, i)); - - return max_opno + 1; -} - -void -print_code (code) - RTX_CODE code; -{ - register char *p1; - for (p1 = GET_RTX_NAME (code); *p1; p1++) - { - if (*p1 >= 'a' && *p1 <= 'z') - putchar (*p1 + 'A' - 'a'); - else - putchar (*p1); - } -} - -/* Print a C expression to construct an RTX just like X, - substituting any operand references appearing within. */ - -void -gen_exp (x) - rtx x; -{ - register RTX_CODE code; - register int i; - register int len; - register char *fmt; - - if (x == 0) - { - printf ("0"); - return; - } - - code = GET_CODE (x); - - switch (code) - { - case MATCH_OPERAND: - case MATCH_DUP: - printf ("operand%d", XINT (x, 0)); - return; - - case MATCH_OPERATOR: - printf ("gen_rtx (GET_CODE (operand%d)", XINT (x, 0)); - printf (", %smode", GET_MODE_NAME (GET_MODE (x))); - for (i = 0; i < XVECLEN (x, 2); i++) - { - printf (",\n\t\t"); - gen_exp (XVECEXP (x, 2, i)); - } - printf (")"); - return; - - case ADDRESS: - fatal ("ADDRESS expression code used in named instruction pattern"); - - case PC: - printf ("pc_rtx"); - return; - - case CC0: - printf ("cc0_rtx"); - return; - - case CONST_INT: - if (INTVAL (x) == 0) - { - printf ("const0_rtx"); - return; - } - if (INTVAL (x) == 1) - { - printf ("const1_rtx"); - return; - } - } - - printf ("gen_rtx ("); - print_code (code); - printf (", %smode", GET_MODE_NAME (GET_MODE (x))); - - fmt = GET_RTX_FORMAT (code); - len = GET_RTX_LENGTH (code); - for (i = 0; i < len; i++) - { - if (fmt[i] == '0') - break; - printf (", "); - if (fmt[i] == 'e' || fmt[i] == 'u') - gen_exp (XEXP (x, i)); - else if (fmt[i] == 'i') - printf ("%d", XINT (x, i)); - else if (fmt[i] == 's') - printf ("\"%s\"", XSTR (x, i)); - else if (fmt[i] == 'E') - { - int j; - printf ("gen_rtvec (%d", XVECLEN (x, i)); - for (j = 0; j < XVECLEN (x, i); j++) - { - printf (",\n\t\t"); - gen_exp (XVECEXP (x, i, j)); - } - printf (")"); - } - else - abort (); - } - printf (")"); -} - -/* Generate the `gen_...' function for a DEFINE_INSN. */ - -void -gen_insn (insn) - rtx insn; -{ - int operands; - register int i; - - /* Don't mention instructions whose names are the null string. - They are in the machine description just to be recognized. */ - if (strlen (XSTR (insn, 0)) == 0) - return; - - /* Find out how many operands this function has, - and also whether any of them have register constraints. */ - register_constraints = 0; - operands = max_operand_vec (insn, 1); - if (max_dup_opno >= operands) - fatal ("match_dup operand number has no match_operand"); - - /* Output the function name and argument declarations. */ - printf ("rtx\ngen_%s (", XSTR (insn, 0)); - for (i = 0; i < operands; i++) - printf (i ? ", operand%d" : "operand%d", i); - printf (")\n"); - for (i = 0; i < operands; i++) - printf (" rtx operand%d;\n", i); - printf ("{\n"); - - /* Output code to construct and return the rtl for the instruction body */ - - if (XVECLEN (insn, 1) == 1) - { - printf (" return "); - gen_exp (XVECEXP (insn, 1, 0)); - printf (";\n}\n\n"); - } - else - { - printf (" return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (%d", XVECLEN (insn, 1)); - for (i = 0; i < XVECLEN (insn, 1); i++) - { - printf (",\n\t\t"); - gen_exp (XVECEXP (insn, 1, i)); - } - printf ("));\n}\n\n"); - } -} - -/* Generate the `gen_...' function for a DEFINE_EXPAND. */ - -void -gen_expand (expand) - rtx expand; -{ - int operands; - register int i; - - if (strlen (XSTR (expand, 0)) == 0) - fatal ("define_expand lacks a name"); - if (XVEC (expand, 1) == 0) - fatal ("define_expand for %s lacks a pattern", XSTR (expand, 0)); - - /* Find out how many operands this function has, - and also whether any of them have register constraints. */ - register_constraints = 0; - - operands = max_operand_vec (expand, 1); - - /* Output the function name and argument declarations. */ - printf ("rtx\ngen_%s (", XSTR (expand, 0)); - for (i = 0; i < operands; i++) - printf (i ? ", operand%d" : "operand%d", i); - printf (")\n"); - for (i = 0; i < operands; i++) - printf (" rtx operand%d;\n", i); - printf ("{\n"); - - /* For each operand referred to only with MATCH_DUPs, - make a local variable. */ - for (i = operands; i <= max_dup_opno; i++) - printf (" rtx operand%d;\n", i); - printf (" rtx operands[%d];\n", max (operands, max_dup_opno + 1)); - printf (" rtx _val;\n"); - printf (" start_sequence ();\n"); - - /* The fourth operand of DEFINE_EXPAND is some code to be executed - before the actual construction. - This code expects to refer to `operands' - just as the output-code in a DEFINE_INSN does, - but here `operands' is an automatic array. - So copy the operand values there before executing it. */ - if (XSTR (expand, 3)) - { - /* Output code to copy the arguments into `operands'. */ - for (i = 0; i < operands; i++) - printf (" operands[%d] = operand%d;\n", i, i); - - /* Output the special code to be executed before the sequence - is generated. */ - printf ("%s\n", XSTR (expand, 3)); - - /* Output code to copy the arguments back out of `operands' - (unless we aren't going to use them at all). */ - if (XVEC (expand, 1) != 0) - { - for (i = 0; i < operands; i++) - printf (" operand%d = operands[%d];\n", i, i); - for (; i <= max_dup_opno; i++) - printf (" operand%d = operands[%d];\n", i, i); - } - } - - /* Output code to construct the rtl for the instruction bodies. - Use emit_insn to add them to the sequence being accumulated. - But don't do this if the user's code has set `no_more' nonzero. */ - - for (i = 0; i < XVECLEN (expand, 1); i++) - { - rtx next = XVECEXP (expand, 1, i); - if ((GET_CODE (next) == SET && GET_CODE (SET_DEST (next)) == PC) - || (GET_CODE (next) == PARALLEL - && GET_CODE (XVECEXP (next, 0, 0)) == SET - && GET_CODE (SET_DEST (XVECEXP (next, 0, 0))) == PC) - || GET_CODE (next) == RETURN) - printf (" emit_jump_insn ("); - else if ((GET_CODE (next) == SET && GET_CODE (SET_SRC (next)) == CALL) - || GET_CODE (next) == CALL - || (GET_CODE (next) == PARALLEL - && GET_CODE (XVECEXP (next, 0, 0)) == SET - && GET_CODE (SET_SRC (XVECEXP (next, 0, 0))) == CALL) - || (GET_CODE (next) == PARALLEL - && GET_CODE (XVECEXP (next, 0, 0)) == CALL)) - printf (" emit_call_insn ("); - else if (GET_CODE (next) == CODE_LABEL) - printf (" emit_label ("); - else if (GET_CODE (next) == MATCH_OPERAND - || GET_CODE (next) == MATCH_OPERATOR - || GET_CODE (next) == MATCH_DUP - || GET_CODE (next) == PARALLEL) - printf (" emit ("); - else - printf (" emit_insn ("); - gen_exp (next); - printf (");\n"); - if (GET_CODE (next) == SET && GET_CODE (SET_DEST (next)) == PC - && GET_CODE (SET_SRC (next)) == LABEL_REF) - printf (" emit_barrier ();"); - } - - /* Call `gen_sequence' to make a SEQUENCE out of all the - insns emitted within this gen_... function. */ - - printf (" _done:\n"); - printf (" _val = gen_sequence ();\n"); - printf (" end_sequence ();\n"); - printf (" return _val;\n}\n\n"); -} - -int -xmalloc (size) -{ - register int val = malloc (size); - - if (val == 0) - fatal ("virtual memory exhausted"); - - return val; -} - -int -xrealloc (ptr, size) - char *ptr; - int size; -{ - int result = realloc (ptr, size); - if (!result) - fatal ("virtual memory exhausted"); - return result; -} - -void -fatal (s, a1, a2) - char *s; -{ - fprintf (stderr, "genemit: "); - fprintf (stderr, s, a1, a2); - fprintf (stderr, "\n"); - exit (FATAL_EXIT_CODE); -} - -/* More 'friendly' abort that prints the line and file. - config.h can #define abort fancy_abort if you like that sort of thing. */ - -void -fancy_abort () -{ - fatal ("Internal gcc abort."); -} - -int -main (argc, argv) - int argc; - char **argv; -{ - rtx desc; - FILE *infile; - extern rtx read_rtx (); - register int c; - - obstack_init (rtl_obstack); - - if (argc <= 1) - fatal ("No input file name."); - - infile = fopen (argv[1], "r"); - if (infile == 0) - { - perror (argv[1]); - exit (FATAL_EXIT_CODE); - } - - init_rtl (); - - /* Assign sequential codes to all entries in the machine description - in parallel with the tables in insn-output.c. */ - - insn_code_number = 0; - - printf ("/* Generated automatically by the program `genemit'\n\ -from the machine description file `md'. */\n\n"); - - printf ("#include \"config.h\"\n"); - printf ("#include \"rtl.h\"\n"); - printf ("#include \"expr.h\"\n"); - printf ("#include \"real.h\"\n"); - printf ("#include \"insn-config.h\"\n\n"); - printf ("#include \"insn-flags.h\"\n\n"); - printf ("extern char *insn_operand_constraint[][MAX_RECOG_OPERANDS];\n\n"); - printf ("extern rtx recog_operand[];\n"); - printf ("#define operands emit_operand\n\n"); - printf ("#define FAIL do { end_sequence (); return 0;} while (0)\n\n"); - printf ("#define DONE goto _done\n\n"); - - /* Read the machine description. */ - - while (1) - { - c = read_skip_spaces (infile); - if (c == EOF) - break; - ungetc (c, infile); - - desc = read_rtx (infile); - if (GET_CODE (desc) == DEFINE_INSN) - { - gen_insn (desc); - ++insn_code_number; - } - if (GET_CODE (desc) == DEFINE_EXPAND) - { - gen_expand (desc); - ++insn_code_number; - } - if (GET_CODE (desc) == DEFINE_PEEPHOLE) - { - ++insn_code_number; - } - } - - fflush (stdout); - exit (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE); -} diff --git a/gnu/usr.bin/gcc1/cc1/genextract.c b/gnu/usr.bin/gcc1/cc1/genextract.c deleted file mode 100644 index b98632f235..0000000000 --- a/gnu/usr.bin/gcc1/cc1/genextract.c +++ /dev/null @@ -1,348 +0,0 @@ -/* Generate code from machine description to extract operands from insn as rtl. - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include -#include "config.h" -#include "rtl.h" -#include "obstack.h" - -struct obstack obstack; -struct obstack *rtl_obstack = &obstack; - -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free -extern int xmalloc (); -extern void free (); - -/* Number instruction patterns handled, starting at 0 for first one. */ - -int insn_code_number; - -/* Number the occurrences of MATCH_DUP in each instruction, - starting at 0 for the first occurrence. */ - -int dup_count; - -/* While tree-walking an instruction pattern, we keep a chain - of these `struct link's to record how to get down to the - current position. In each one, POS is the operand number, - and if the operand is a vector VEC is the element number. - VEC is -1 if the operand is not a vector. */ - -struct link -{ - struct link *next; - int pos; - int vecelt; -}; - -void walk_rtx (); -void print_path (); -void fatal (); -void fancy_abort (); - -void -gen_insn (insn) - rtx insn; -{ - register int i; - - dup_count = 0; - - /* Output the function name and argument declaration. */ - /* It would be cleaner to make `void' the return type - but 4.2 vax compiler doesn't accept that in the array - that these functions are supposed to go in. */ - printf ("VOID\nextract_%d (insn)\n rtx insn;\n", insn_code_number); - printf ("{\n"); - - /* Walk the insn's pattern, remembering at all times the path - down to the walking point. */ - - if (XVECLEN (insn, 1) == 1) - walk_rtx (XVECEXP (insn, 1, 0), 0); - else - for (i = XVECLEN (insn, 1) - 1; i >= 0; i--) - { - struct link link; - link.next = 0; - link.pos = 0; - link.vecelt = i; - walk_rtx (XVECEXP (insn, 1, i), &link); - } - printf ("}\n\n"); -} - -/* Like gen_insn but handles `define_peephole'. */ - -void -gen_peephole (peep) - rtx peep; -{ - /* Output the function name and argument declaration. */ - printf ("VOID\nextract_%d (insn)\n rtx insn;\n", insn_code_number); - printf ("{\n"); - /* The vector in the insn says how many operands it has. - And all it contains are operands. In fact, the vector was - created just for the sake of this function. */ - printf ("\ - bcopy (&XVECEXP (insn, 0, 0), recog_operand,\ - sizeof (rtx) * XVECLEN (insn, 0));\n"); - printf ("}\n\n"); -} - -void -walk_rtx (x, path) - rtx x; - struct link *path; -{ - register RTX_CODE code; - register int i; - register int len; - register char *fmt; - struct link link; - - if (x == 0) - return; - - code = GET_CODE (x); - - switch (code) - { - case PC: - case CC0: - case CONST_INT: - case SYMBOL_REF: - return; - - case MATCH_OPERAND: - printf (" recog_operand[%d] = *(recog_operand_loc[%d]\n = &", - XINT (x, 0), XINT (x, 0)); - print_path (path); - printf (");\n"); - break; - - case MATCH_DUP: - printf (" recog_dup_loc[%d] = &", dup_count); - print_path (path); - printf (";\n"); - printf (" recog_dup_num[%d] = %d;\n", dup_count, XINT (x, 0)); - dup_count++; - break; - - case MATCH_OPERATOR: - printf (" recog_operand[%d] = *(recog_operand_loc[%d]\n = &", - XINT (x, 0), XINT (x, 0)); - print_path (path); - printf (");\n"); - link.next = path; - link.vecelt = -1; - for (i = XVECLEN (x, 2) - 1; i >= 0; i--) - { - link.pos = i; - walk_rtx (XVECEXP (x, 2, i), &link); - } - return; - - case ADDRESS: - walk_rtx (XEXP (x, 0), path); - return; - } - - link.next = path; - link.vecelt = -1; - fmt = GET_RTX_FORMAT (code); - len = GET_RTX_LENGTH (code); - for (i = 0; i < len; i++) - { - link.pos = i; - if (fmt[i] == 'e' || fmt[i] == 'u') - { - walk_rtx (XEXP (x, i), &link); - } - else if (fmt[i] == 'E') - { - int j; - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - { - link.vecelt = j; - walk_rtx (XVECEXP (x, i, j), &link); - } - } - } -} - -/* Given a PATH, representing a path down the instruction's - pattern from the root to a certain point, output code to - evaluate to the rtx at that point. */ - -void -print_path (path) - struct link *path; -{ - if (path == 0) - printf ("insn"); - else if (path->vecelt >= 0) - { - printf ("XVECEXP ("); - print_path (path->next); - printf (", %d, %d)", path->pos, path->vecelt); - } - else - { - printf ("XEXP ("); - print_path (path->next); - printf (", %d)", path->pos); - } -} - -int -xmalloc (size) -{ - register int val = malloc (size); - - if (val == 0) - fatal ("virtual memory exhausted"); - return val; -} - -int -xrealloc (ptr, size) - char *ptr; - int size; -{ - int result = realloc (ptr, size); - if (!result) - fatal ("virtual memory exhausted"); - return result; -} - -void -fatal (s, a1, a2) - char *s; -{ - fprintf (stderr, "genextract: "); - fprintf (stderr, s, a1, a2); - fprintf (stderr, "\n"); - exit (FATAL_EXIT_CODE); -} - -/* More 'friendly' abort that prints the line and file. - config.h can #define abort fancy_abort if you like that sort of thing. */ - -void -fancy_abort () -{ - fatal ("Internal gcc abort."); -} - -int -main (argc, argv) - int argc; - char **argv; -{ - rtx desc; - FILE *infile; - extern rtx read_rtx (); - register int c, i; - - obstack_init (rtl_obstack); - - if (argc <= 1) - fatal ("No input file name."); - - infile = fopen (argv[1], "r"); - if (infile == 0) - { - perror (argv[1]); - exit (FATAL_EXIT_CODE); - } - - init_rtl (); - - /* Assign sequential codes to all entries in the machine description - in parallel with the tables in insn-output.c. */ - - insn_code_number = 0; - - printf ("/* Generated automatically by the program `genextract'\n\ -from the machine description file `md'. */\n\n"); - - printf ("#include \"config.h\"\n"); - printf ("#include \"rtl.h\"\n\n"); - - printf ("extern rtx recog_operand[];\n"); - printf ("extern rtx *recog_operand_loc[];\n"); - printf ("extern rtx *recog_dup_loc[];\n"); - printf ("extern char recog_dup_num[];\n\n"); - - /* The extractor functions really should return `void'; - but old C compilers don't seem to be able to handle the array - definition if `void' is used. So use `int' in non-ANSI C compilers. */ - - printf ("#ifdef __STDC__\n#define VOID void\n#else\n#define VOID int\n#endif\n\n"); - - /* Read the machine description. */ - - while (1) - { - c = read_skip_spaces (infile); - if (c == EOF) - break; - ungetc (c, infile); - - desc = read_rtx (infile); - if (GET_CODE (desc) == DEFINE_INSN) - { - gen_insn (desc); - ++insn_code_number; - } - if (GET_CODE (desc) == DEFINE_PEEPHOLE) - { - gen_peephole (desc); - ++insn_code_number; - } - if (GET_CODE (desc) == DEFINE_EXPAND) - { - printf ("VOID extract_%d () {}\n\n", insn_code_number); - ++insn_code_number; - } - } - - printf ("VOID (*insn_extract_fn[]) () =\n{ "); - for (i = 0; i < insn_code_number; i++) - { - if (i % 4 != 0) - printf (", "); - else if (i != 0) - printf (",\n "); - printf ("extract_%d", i); - } - printf ("\n};\n\n"); - - printf ("void fatal_insn_not_found ();\n\n"); - printf ("void\ninsn_extract (insn)\n"); - printf (" rtx insn;\n"); - printf ("{\n if (INSN_CODE (insn) == -1) fatal_insn_not_found (insn);\n"); - printf (" (*insn_extract_fn[INSN_CODE (insn)]) (PATTERN (insn));\n}\n"); - - fflush (stdout); - exit (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE); -} diff --git a/gnu/usr.bin/gcc1/cc1/genflags.c b/gnu/usr.bin/gcc1/cc1/genflags.c deleted file mode 100644 index eb4ebb5003..0000000000 --- a/gnu/usr.bin/gcc1/cc1/genflags.c +++ /dev/null @@ -1,138 +0,0 @@ -/* Generate from machine description: - - - some flags HAVE_... saying which simple standard instructions are - available for this machine. - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include -#include "config.h" -#include "rtl.h" -#include "obstack.h" - -struct obstack obstack; -struct obstack *rtl_obstack = &obstack; - -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free -extern int xmalloc (); -extern void free (); - -void fatal (); -void fancy_abort (); - -void -gen_insn (insn) - rtx insn; -{ - /* Don't mention instructions whose names are the null string. - They are in the machine description just to be recognized. */ - if (strlen (XSTR (insn, 0)) == 0) - return; - - printf ("#define HAVE_%s (%s)\n", XSTR (insn, 0), - strlen (XSTR (insn, 2)) ? XSTR (insn, 2) : "1"); - printf ("extern rtx gen_%s ();\n", XSTR (insn, 0)); -} - -int -xmalloc (size) -{ - register int val = malloc (size); - - if (val == 0) - fatal ("virtual memory exhausted"); - - return val; -} - -int -xrealloc (ptr, size) - char *ptr; - int size; -{ - int result = realloc (ptr, size); - if (!result) - fatal ("virtual memory exhausted"); - return result; -} - -void -fatal (s, a1, a2) - char *s; -{ - fprintf (stderr, "genflags: "); - fprintf (stderr, s, a1, a2); - fprintf (stderr, "\n"); - exit (FATAL_EXIT_CODE); -} - -/* More 'friendly' abort that prints the line and file. - config.h can #define abort fancy_abort if you like that sort of thing. */ - -void -fancy_abort () -{ - fatal ("Internal gcc abort."); -} - -int -main (argc, argv) - int argc; - char **argv; -{ - rtx desc; - FILE *infile; - extern rtx read_rtx (); - register int c; - - obstack_init (rtl_obstack); - - if (argc <= 1) - fatal ("No input file name."); - - infile = fopen (argv[1], "r"); - if (infile == 0) - { - perror (argv[1]); - exit (FATAL_EXIT_CODE); - } - - init_rtl (); - - printf ("/* Generated automatically by the program `genflags'\n\ -from the machine description file `md'. */\n\n"); - - /* Read the machine description. */ - - while (1) - { - c = read_skip_spaces (infile); - if (c == EOF) - break; - ungetc (c, infile); - - desc = read_rtx (infile); - if (GET_CODE (desc) == DEFINE_INSN || GET_CODE (desc) == DEFINE_EXPAND) - gen_insn (desc); - } - - fflush (stdout); - exit (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE); -} diff --git a/gnu/usr.bin/gcc1/cc1/genoutput.c b/gnu/usr.bin/gcc1/cc1/genoutput.c deleted file mode 100644 index da17e9e52f..0000000000 --- a/gnu/usr.bin/gcc1/cc1/genoutput.c +++ /dev/null @@ -1,786 +0,0 @@ -/* Generate code from to output assembler insns as recognized from rtl. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This program reads the machine description for the compiler target machine - and produces a file containing three things: - - 1, An array of strings `insn_template' which is indexed by insn code number - and contains the template for output of that insn, - - 2. An array of ints `insn_n_operands' which is indexed by insn code number - and contains the number of distinct operands in the pattern for that insn, - - 3. An array of ints `insn_n_dups' which is indexed by insn code number - and contains the number of match_dup's that appear in the insn's pattern. - This says how many elements of `recog_dup_loc' are significant - after an insn has been recognized. - - 4. An array of arrays of operand constraint strings, - `insn_operand_constraint', - indexed first by insn code number and second by operand number, - containing the constraint for that operand. - - This array is generated only if register constraints appear in - match_operand rtx's. - - 5. An array of arrays of chars which indicate which operands of - which insn patterns appear within ADDRESS rtx's. This array is - called `insn_operand_address_p' and is generated only if there - are *no* register constraints in the match_operand rtx's. - - 6. An array of arrays of machine modes, `insn_operand_mode', - indexed first by insn code number and second by operand number, - containing the machine mode that that operand is supposed to have. - Also `insn_operand_strict_low', which is nonzero for operands - contained in a STRICT_LOW_PART. - - 7. An array of arrays of int-valued functions, `insn_operand_predicate', - indexed first by insn code number and second by operand number, - containing the match_operand predicate for this operand. - - 8. An array of functions `insn_gen_function' which, indexed - by insn code number, gives the function to generate a body - for that patter, given operands as arguments. - - 9. A function `output_insn_hairy' which is called with two arguments - (an insn code number and a vector of operand value rtx's) - and returns a template to use for output of that insn. - This is used only in the cases where the template is not constant. - These cases are specified by a * at the beginning of the template string - in the machine description. They are identified for the sake of - other parts of the compiler by a zero element in `insn_template'. - - 10. An array of structures, `insn_machine_info', that gives machine-specific - information about the insn. - - 11. An array of ints, `insn_n_alternatives', that gives the number - of alternatives in the constraints of each pattern. - -The code number of an insn is simply its position in the machine description; -code numbers are assigned sequentially to entries in the description, -starting with code number 0. - -Thus, the following entry in the machine description - - (define_insn "clrdf" - [(set (match_operand:DF 0 "general_operand" "") - (const_int 0))] - "" - "clrd %0") - -assuming it is the 25th entry present, would cause -insn_template[24] to be "clrd %0", and insn_n_operands[24] to be 1. -It would not make an case in output_insn_hairy because the template -given in the entry is a constant (it does not start with `*'). */ - -#include -#include "config.h" -#include "rtl.h" -#include "obstack.h" - -/* No instruction can have more operands than this. - Sorry for this arbitrary limit, but what machine will - have an instruction with this many operands? */ - -#define MAX_MAX_OPERANDS 40 - -struct obstack obstack; -struct obstack *rtl_obstack = &obstack; - -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free -extern int xmalloc (); -extern void free (); - -void fatal (); -void fancy_abort (); -void error (); -void mybcopy (); -void mybzero (); - -/* insns in the machine description are assigned sequential code numbers - that are used by insn-recog.c (produced by genrecog) to communicate - to insn-output.c (produced by this program). */ - -int next_code_number; - -/* Record in this chain all information that we will output, - associated with the code number of the insn. */ - -struct data -{ - int code_number; - char *name; - char *template; /* string such as "movl %1,%0" */ - int n_operands; /* Number of operands this insn recognizes */ - int n_dups; /* Number times match_dup appears in pattern */ - int n_alternatives; /* Number of alternatives in each constraint */ - struct data *next; - char *constraints[MAX_MAX_OPERANDS]; - /* Number of alternatives in constraints of operand N. */ - int op_n_alternatives[MAX_MAX_OPERANDS]; - char *predicates[MAX_MAX_OPERANDS]; - char address_p[MAX_MAX_OPERANDS]; - enum machine_mode modes[MAX_MAX_OPERANDS]; - char strict_low[MAX_MAX_OPERANDS]; - char outfun; /* Nonzero means this has an output function */ - char *machine_info; /* machine-specific info string. */ -}; - -/* This variable points to the first link in the chain. */ - -struct data *insn_data; - -/* Pointer to the last link in the chain, so new elements - can be added at the end. */ - -struct data *end_of_insn_data; - -/* Nonzero if any match_operand has a constraint string; - implies that REGISTER_CONSTRAINTS will be defined - for this machine description. */ - -int have_constraints; - -void -output_prologue () -{ - - printf ("/* Generated automatically by the program `genoutput'\n\ -from the machine description file `md'. */\n\n"); - - printf ("#include \"config.h\"\n"); - printf ("#include \"rtl.h\"\n"); - printf ("#include \"regs.h\"\n"); - printf ("#include \"hard-reg-set.h\"\n"); - printf ("#include \"real.h\"\n"); - printf ("#include \"conditions.h\"\n"); - printf ("#include \"insn-flags.h\"\n"); - printf ("#include \"insn-config.h\"\n\n"); - - printf ("#ifndef __STDC__\n"); - printf ("#define const\n"); - printf ("#endif\n\n"); - - printf ("#include \"output.h\"\n"); - printf ("#include \"aux-output.c\"\n\n"); - - /* Make sure there is at least a dummy definition of INSN_MACHINE_INFO. */ - printf ("#ifndef INSN_MACHINE_INFO\n"); - printf ("#define INSN_MACHINE_INFO struct dummy1 {int i;}\n"); - printf ("#endif\n\n"); -} - -void -output_epilogue () -{ - register struct data *d; - - printf ("\nchar * const insn_template[] =\n {\n"); - for (d = insn_data; d; d = d->next) - { - if (d->template) - printf (" \"%s\",\n", d->template); - else - printf (" 0,\n"); - } - printf (" };\n"); - - printf ("\nchar *(*const insn_outfun[])() =\n {\n"); - for (d = insn_data; d; d = d->next) - { - if (d->outfun) - printf (" output_%d,\n", d->code_number); - else - printf (" 0,\n"); - } - printf (" };\n"); - - printf ("\nrtx (*const insn_gen_function[]) () =\n {\n"); - for (d = insn_data; d; d = d->next) - { - if (d->name) - printf (" gen_%s,\n", d->name); - else - printf (" 0,\n"); - } - printf (" };\n"); - - printf ("\nconst int insn_n_operands[] =\n {\n"); - for (d = insn_data; d; d = d->next) - { - printf (" %d,\n", d->n_operands); - } - printf (" };\n"); - - printf ("\nconst int insn_n_dups[] =\n {\n"); - for (d = insn_data; d; d = d->next) - { - printf (" %d,\n", d->n_dups); - } - printf (" };\n"); - - if (have_constraints) - { - printf ("\nchar *const insn_operand_constraint[][MAX_RECOG_OPERANDS] =\n {\n"); - for (d = insn_data; d; d = d->next) - { - register int i, n = 0, start; - printf (" {"); - /* Make sure all the operands have the same number of - alternatives in their constraints. - Let N be that number. */ - for (start = 0; start < d->n_operands; start++) - if (d->op_n_alternatives[start] > 0) - { - if (n == 0) - n = d->op_n_alternatives[start]; - else if (n != d->op_n_alternatives[start]) - error ("wrong number of alternatives in operand %d of insn number %d", - start, d->code_number); - } - /* Record the insn's overall number of alternatives. */ - d->n_alternatives = n; - - for (i = 0; i < d->n_operands; i++) - { - if (d->constraints[i] == 0) - printf (" \"\","); - else - printf (" \"%s\",", d->constraints[i]); - } - if (d->n_operands == 0) - printf (" 0"); - printf (" },\n"); - } - printf (" };\n"); - } - else - { - printf ("\nconst char insn_operand_address_p[][MAX_RECOG_OPERANDS] =\n {\n"); - for (d = insn_data; d; d = d->next) - { - register int i; - printf (" {"); - for (i = 0; i < d->n_operands; i++) - printf (" %d,", d->address_p[i]); - if (d->n_operands == 0) - printf (" 0"); - printf (" },\n"); - } - printf (" };\n"); - } - - printf ("\nconst enum machine_mode insn_operand_mode[][MAX_RECOG_OPERANDS] =\n {\n"); - for (d = insn_data; d; d = d->next) - { - register int i; - printf (" {"); - for (i = 0; i < d->n_operands; i++) - printf (" %smode,", GET_MODE_NAME (d->modes[i])); - if (d->n_operands == 0) - printf (" VOIDmode"); - printf (" },\n"); - } - printf (" };\n"); - - printf ("\nconst char insn_operand_strict_low[][MAX_RECOG_OPERANDS] =\n {\n"); - for (d = insn_data; d; d = d->next) - { - register int i; - printf (" {"); - for (i = 0; i < d->n_operands; i++) - printf (" %d,", d->strict_low[i]); - if (d->n_operands == 0) - printf (" 0"); - printf (" },\n"); - } - printf (" };\n"); - - printf ("\nint (*const insn_operand_predicate[][MAX_RECOG_OPERANDS])() =\n {\n"); - for (d = insn_data; d; d = d->next) - { - register int i; - printf (" {"); - for (i = 0; i < d->n_operands; i++) - printf (" %s,", ((d->predicates[i] && d->predicates[i][0]) - ? d->predicates[i] : "0")); - if (d->n_operands == 0) - printf (" 0"); - printf (" },\n"); - } - printf (" };\n"); - - printf ("\n#ifndef DEFAULT_MACHINE_INFO\n#define DEFAULT_MACHINE_INFO 0\n"); - printf ("#endif\n\nconst INSN_MACHINE_INFO insn_machine_info[] =\n {\n"); - for (d = insn_data; d; d = d->next) - { - if (d->machine_info) - printf (" {%s},\n", d->machine_info); - else - printf(" { DEFAULT_MACHINE_INFO },\n"); - } - printf(" };\n"); - - printf ("\nconst int insn_n_alternatives[] =\n {\n"); - for (d = insn_data; d; d = d->next) - { - if (d->n_alternatives) - printf (" %d,\n", d->n_alternatives); - else - printf(" 0,\n"); - } - printf(" };\n"); -} - -/* scan_operands (X) stores in max_opno the largest operand - number present in X, if that is larger than the previous - value of max_opno. It stores all the constraints in `constraints' - and all the machine modes in `modes'. - - THIS_ADDRESS_P is nonzero if the containing rtx was an ADDRESS. - THIS_STRICT_LOW is nonzero if the containing rtx was a STRICT_LOW_PART. */ - -int max_opno; -int num_dups; -char *constraints[MAX_MAX_OPERANDS]; -int op_n_alternatives[MAX_MAX_OPERANDS]; -char *predicates[MAX_MAX_OPERANDS]; -char address_p[MAX_MAX_OPERANDS]; -enum machine_mode modes[MAX_MAX_OPERANDS]; -char strict_low[MAX_MAX_OPERANDS]; - -void -scan_operands (part, this_address_p, this_strict_low) - rtx part; - int this_address_p; - int this_strict_low; -{ - register int i, j; - register RTX_CODE code; - register char *format_ptr; - - if (part == 0) - return; - - code = GET_CODE (part); - - if (code == MATCH_OPERAND) - { - int opno = XINT (part, 0); - if (opno > max_opno) - max_opno = opno; - if (max_opno >= MAX_MAX_OPERANDS) - error ("Too many operands (%d) in one instruction pattern.\n", - max_opno + 1); - modes[opno] = GET_MODE (part); - strict_low[opno] = this_strict_low; - predicates[opno] = XSTR (part, 1); - constraints[opno] = XSTR (part, 2); - if (XSTR (part, 2) != 0 && *XSTR (part, 2) != 0) - { - op_n_alternatives[opno] = n_occurrences (',', XSTR (part, 2)) + 1; - have_constraints = 1; - } - address_p[opno] = this_address_p; - return; - } - - if (code == MATCH_OPERATOR) - { - int opno = XINT (part, 0); - if (opno > max_opno) - max_opno = opno; - if (max_opno >= MAX_MAX_OPERANDS) - error ("Too many operands (%d) in one instruction pattern.\n", - max_opno + 1); - modes[opno] = GET_MODE (part); - strict_low[opno] = 0; - predicates[opno] = XSTR (part, 1); - constraints[opno] = 0; - address_p[opno] = 0; - for (i = 0; i < XVECLEN (part, 2); i++) - scan_operands (XVECEXP (part, 2, i), 0, 0); - return; - } - - if (code == MATCH_DUP) - { - ++num_dups; - return; - } - - if (code == ADDRESS) - { - scan_operands (XEXP (part, 0), 1, 0); - return; - } - - if (code == STRICT_LOW_PART) - { - scan_operands (XEXP (part, 0), 0, 1); - return; - } - - format_ptr = GET_RTX_FORMAT (GET_CODE (part)); - - for (i = 0; i < GET_RTX_LENGTH (GET_CODE (part)); i++) - switch (*format_ptr++) - { - case 'e': - scan_operands (XEXP (part, i), 0, 0); - break; - case 'E': - if (XVEC (part, i) != NULL) - for (j = 0; j < XVECLEN (part, i); j++) - scan_operands (XVECEXP (part, i, j), 0, 0); - break; - } -} - -/* Look at a define_insn just read. Assign its code number. - Record on insn_data the template and the number of arguments. - If the insn has a hairy output action, output a function for now. */ - -void -gen_insn (insn) - rtx insn; -{ - register struct data *d = (struct data *) xmalloc (sizeof (struct data)); - register int i; - - d->code_number = next_code_number++; - if (XSTR (insn, 0)[0]) - d->name = XSTR (insn, 0); - else - d->name = 0; - - /* Build up the list in the same order as the insns are seen - in the machine description. */ - d->next = 0; - if (end_of_insn_data) - end_of_insn_data->next = d; - else - insn_data = d; - - end_of_insn_data = d; - - max_opno = -1; - num_dups = 0; - - mybzero (constraints, sizeof constraints); - mybzero (op_n_alternatives, sizeof op_n_alternatives); - mybzero (predicates, sizeof predicates); - mybzero (address_p, sizeof address_p); - mybzero (modes, sizeof modes); - mybzero (strict_low, sizeof strict_low); - for (i = 0; i < XVECLEN (insn, 1); i++) - scan_operands (XVECEXP (insn, 1, i), 0, 0); - d->n_operands = max_opno + 1; - d->n_dups = num_dups; - mybcopy (constraints, d->constraints, sizeof constraints); - mybcopy (op_n_alternatives, d->op_n_alternatives, sizeof op_n_alternatives); - mybcopy (predicates, d->predicates, sizeof predicates); - mybcopy (address_p, d->address_p, sizeof address_p); - mybcopy (modes, d->modes, sizeof modes); - mybcopy (strict_low, d->strict_low, sizeof strict_low); - d->machine_info = XSTR (insn, 4); - - /* We need to consider only the instructions whose assembler code template - starts with a *. These are the ones where the template is really - C code to run to decide on a template to use. - So for all others just return now. */ - - if (XSTR (insn, 3)[0] != '*') - { - d->template = XSTR (insn, 3); - d->outfun = 0; - return; - } - - d->template = 0; - d->outfun = 1; - - printf ("\nstatic char *\n"); - printf ("output_%d (operands, insn)\n", d->code_number); - printf (" rtx *operands;\n"); - printf (" rtx insn;\n"); - printf ("{\n"); - /* The following is done in a funny way to get around problems in - VAX-11 "C" on VMS. It is the equivalent of: - printf ("%s\n", &(XSTR (insn, 3)[1])); */ - { - register char *cp = &(XSTR (insn, 3)[1]); - while (*cp) putchar (*cp++); - putchar ('\n'); - } - printf ("}\n"); -} - -/* Look at a define_peephole just read. Assign its code number. - Record on insn_data the template and the number of arguments. - If the insn has a hairy output action, output it now. */ - -void -gen_peephole (peep) - rtx peep; -{ - register struct data *d = (struct data *) xmalloc (sizeof (struct data)); - register int i; - - d->code_number = next_code_number++; - d->name = 0; - - /* Build up the list in the same order as the insns are seen - in the machine description. */ - d->next = 0; - if (end_of_insn_data) - end_of_insn_data->next = d; - else - insn_data = d; - - end_of_insn_data = d; - - max_opno = -1; - mybzero (constraints, sizeof constraints); - mybzero (op_n_alternatives, sizeof op_n_alternatives); - - /* Get the number of operands by scanning all the - patterns of the peephole optimizer. - But ignore all the rest of the information thus obtained. */ - for (i = 0; i < XVECLEN (peep, 0); i++) - scan_operands (XVECEXP (peep, 0, i), 0, 0); - - d->n_operands = max_opno + 1; - d->n_dups = 0; - mybcopy (constraints, d->constraints, sizeof constraints); - mybcopy (op_n_alternatives, d->op_n_alternatives, sizeof op_n_alternatives); - mybzero (d->predicates, sizeof predicates); - mybzero (d->address_p, sizeof address_p); - mybzero (d->modes, sizeof modes); - mybzero (d->strict_low, sizeof strict_low); - d->machine_info = XSTR (peep, 3); - - /* We need to consider only the instructions whose assembler code template - starts with a *. These are the ones where the template is really - C code to run to decide on a template to use. - So for all others just return now. */ - - if (XSTR (peep, 2)[0] != '*') - { - d->template = XSTR (peep, 2); - d->outfun = 0; - return; - } - - d->template = 0; - d->outfun = 1; - - printf ("\nstatic char *\n"); - printf ("output_%d (operands, insn)\n", d->code_number); - printf (" rtx *operands;\n"); - printf (" rtx insn;\n"); - printf ("{\n"); - printf ("%s\n", &(XSTR (peep, 2)[1])); - printf ("}\n"); -} - -/* Process a define_expand just read. Assign its code number, - only for the purposes of `insn_gen_function'. */ - -void -gen_expand (insn) - rtx insn; -{ - register struct data *d = (struct data *) xmalloc (sizeof (struct data)); - register int i; - - d->code_number = next_code_number++; - if (XSTR (insn, 0)[0]) - d->name = XSTR (insn, 0); - else - d->name = 0; - - /* Build up the list in the same order as the insns are seen - in the machine description. */ - d->next = 0; - if (end_of_insn_data) - end_of_insn_data->next = d; - else - insn_data = d; - - end_of_insn_data = d; - - max_opno = -1; - num_dups = 0; - - /* Scan the operands to get the specified predicates and modes, - since expand_binop needs to know them. */ - - mybzero (predicates, sizeof predicates); - mybzero (modes, sizeof modes); - if (XVEC (insn, 1)) - for (i = 0; i < XVECLEN (insn, 1); i++) - scan_operands (XVECEXP (insn, 1, i), 0, 0); - d->n_operands = max_opno + 1; - mybcopy (predicates, d->predicates, sizeof predicates); - mybcopy (modes, d->modes, sizeof modes); - - mybzero (d->constraints, sizeof constraints); - mybzero (d->op_n_alternatives, sizeof op_n_alternatives); - mybzero (d->address_p, sizeof address_p); - mybzero (d->strict_low, sizeof strict_low); - - d->n_dups = 0; - d->template = 0; - d->outfun = 0; - d->machine_info = 0; -} - -int -xmalloc (size) -{ - register int val = malloc (size); - - if (val == 0) - fatal ("virtual memory exhausted"); - return val; -} - -int -xrealloc (ptr, size) - char *ptr; - int size; -{ - int result = realloc (ptr, size); - if (!result) - fatal ("virtual memory exhausted"); - return result; -} - -void -mybzero (b, length) - register char *b; - register int length; -{ - while (length-- > 0) - *b++ = 0; -} - -void -mybcopy (b1, b2, length) - register char *b1; - register char *b2; - register int length; -{ - while (length-- > 0) - *b2++ = *b1++; -} - -void -fatal (s, a1, a2) - char *s; -{ - fprintf (stderr, "genoutput: "); - fprintf (stderr, s, a1, a2); - fprintf (stderr, "\n"); - exit (FATAL_EXIT_CODE); -} - -/* More 'friendly' abort that prints the line and file. - config.h can #define abort fancy_abort if you like that sort of thing. */ - -void -fancy_abort () -{ - fatal ("Internal gcc abort."); -} - -void -error (s, a1, a2) - char *s; -{ - fprintf (stderr, "genoutput: "); - fprintf (stderr, s, a1, a2); - fprintf (stderr, "\n"); -} - -int -main (argc, argv) - int argc; - char **argv; -{ - rtx desc; - FILE *infile; - extern rtx read_rtx (); - register int c; - - obstack_init (rtl_obstack); - - if (argc <= 1) - fatal ("No input file name."); - - infile = fopen (argv[1], "r"); - if (infile == 0) - { - perror (argv[1]); - exit (FATAL_EXIT_CODE); - } - - init_rtl (); - - output_prologue (); - next_code_number = 0; - have_constraints = 0; - - /* Read the machine description. */ - - while (1) - { - c = read_skip_spaces (infile); - if (c == EOF) - break; - ungetc (c, infile); - - desc = read_rtx (infile); - if (GET_CODE (desc) == DEFINE_INSN) - gen_insn (desc); - if (GET_CODE (desc) == DEFINE_PEEPHOLE) - gen_peephole (desc); - if (GET_CODE (desc) == DEFINE_EXPAND) - gen_expand (desc); - } - - output_epilogue (); - - fflush (stdout); - exit (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE); -} - -int -n_occurrences (c, s) - char c; - char *s; -{ - int n = 0; - while (*s) - n += (*s++ == c); - return n; -} diff --git a/gnu/usr.bin/gcc1/cc1/genpeep.c b/gnu/usr.bin/gcc1/cc1/genpeep.c deleted file mode 100644 index 7553485e54..0000000000 --- a/gnu/usr.bin/gcc1/cc1/genpeep.c +++ /dev/null @@ -1,437 +0,0 @@ -/* Generate code from machine description to perform peephole optimizations. - Copyright (C) 1987, 1989 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include -#include "config.h" -#include "rtl.h" -#include "obstack.h" - -struct obstack obstack; -struct obstack *rtl_obstack = &obstack; - -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free -extern int xmalloc (); -extern void free (); - -/* While tree-walking an instruction pattern, we keep a chain - of these `struct link's to record how to get down to the - current position. In each one, POS is the operand number, - and if the operand is a vector VEC is the element number. - VEC is -1 if the operand is not a vector. */ - -struct link -{ - struct link *next; - int pos; - int vecelt; -}; - -void match_rtx (); -void gen_exp (); -void fatal (); -void fancy_abort (); - -int max_opno; - -/* Number of operands used in current peephole definition. */ - -int n_operands; - -/* Peephole optimizations get insn codes just like insn patterns. - Count them so we know the code of the define_peephole we are handling. */ - -int insn_code_number = 0; - -void print_path (); -void print_code (); - -void -gen_peephole (peep) - rtx peep; -{ - int ninsns = XVECLEN (peep, 0); - int i; - - n_operands = 0; - - printf (" insn = ins1;\n"); -#if 0 - printf (" want_jump = 0;\n"); -#endif - - for (i = 0; i < ninsns; i++) - { - if (i > 0) - { - printf (" do { insn = NEXT_INSN (insn);\n"); - printf (" if (insn == 0) goto L%d; }\n", - insn_code_number); - printf (" while (GET_CODE (insn) == NOTE);\n"); - - printf (" if (GET_CODE (insn) == CODE_LABEL\n\ - || GET_CODE (insn) == BARRIER)\n goto L%d;\n", - insn_code_number); - } - -#if 0 - printf (" if (GET_CODE (insn) == JUMP_INSN)\n"); - printf (" want_jump = JUMP_LABEL (insn);\n"); -#endif - - printf (" pat = PATTERN (insn);\n"); - - /* Walk the insn's pattern, remembering at all times the path - down to the walking point. */ - - match_rtx (XVECEXP (peep, 0, i), 0, insn_code_number); - } - - /* We get this far if the pattern matches. - Now test the extra condition. */ - - if (XSTR (peep, 1) && XSTR (peep, 1)[0]) - printf (" if (! (%s)) goto L%d;\n", - XSTR (peep, 1), insn_code_number); - - /* If that matches, construct new pattern and put it in the first insn. - This new pattern will never be matched. - It exists only so that insn-extract can get the operands back. - So use a simple regular form: a PARALLEL containing a vector - of all the operands. */ - - printf (" PATTERN (ins1) = gen_rtx (PARALLEL, VOIDmode, gen_rtvec_v (%d, operands));\n", n_operands); - -#if 0 - printf (" if (want_jump && GET_CODE (ins1) != JUMP_INSN)\n"); - printf (" {\n"); - printf (" rtx insn2 = emit_jump_insn_before (PATTERN (ins1), ins1);\n"); - printf (" delete_insn (ins1);\n"); - printf (" ins1 = ins2;\n"); - printf (" }\n"); -#endif - - /* Record this define_peephole's insn code in the insn, - as if it had been recognized to match this. */ - printf (" INSN_CODE (ins1) = %d;\n", - insn_code_number); - - /* Delete the remaining insns. */ - if (ninsns > 1) - printf (" delete_for_peephole (NEXT_INSN (ins1), insn);\n"); - - /* See reload1.c for insertion of NOTE which guarantees that this - cannot be zero. */ - printf (" return NEXT_INSN (insn);\n"); - - printf (" L%d:\n\n", insn_code_number); -} - -void -match_rtx (x, path, fail_label) - rtx x; - struct link *path; - int fail_label; -{ - register RTX_CODE code; - register int i; - register int len; - register char *fmt; - struct link link; - - if (x == 0) - return; - - - code = GET_CODE (x); - - switch (code) - { - case MATCH_OPERAND: - if (XINT (x, 0) > max_opno) - max_opno = XINT (x, 0); - if (XINT (x, 0) >= n_operands) - n_operands = 1 + XINT (x, 0); - - printf (" x = "); - print_path (path); - printf (";\n"); - - printf (" operands[%d] = x;\n", XINT (x, 0)); - if (XSTR (x, 1) && XSTR (x, 1)[0]) - printf (" if (! %s (x, %smode)) goto L%d;\n", - XSTR (x, 1), GET_MODE_NAME (GET_MODE (x)), fail_label); - return; - - case MATCH_DUP: - printf (" x = "); - print_path (path); - printf (";\n"); - - printf (" if (!rtx_equal_p (operands[%d], x)) goto L%d;\n", - XINT (x, 0), fail_label); - return; - - case MATCH_OPERATOR: - if (XINT (x, 0) > max_opno) - max_opno = XINT (x, 0); - if (XINT (x, 0) >= n_operands) - n_operands = 1 + XINT (x, 0); - - printf (" x = (rtx)"); - print_path (path); - printf (";\n"); - - printf (" operands[%d] = x;\n", XINT (x, 0)); - if (XSTR (x, 1) && XSTR (x, 1)[0]) - printf (" if (! %s (x, %smode)) goto L%d;\n", - XSTR (x, 1), GET_MODE_NAME (GET_MODE (x)), fail_label); - link.next = path; - link.vecelt = -1; - for (i = 0; i < XVECLEN (x, 2); i++) - { - link.pos = i; - match_rtx (XVECEXP (x, 2, i), &link, fail_label); - } - return; - - case ADDRESS: - match_rtx (XEXP (x, 0), path, fail_label); - return; - } - - printf (" x = "); - print_path (path); - printf (";\n"); - - printf (" if (GET_CODE (x) != "); - print_code (code); - printf (") goto L%d;\n", fail_label); - - if (GET_MODE (x) != VOIDmode) - { - printf (" if (GET_MODE (x) != %smode) goto L%d;\n", - GET_MODE_NAME (GET_MODE (x)), fail_label); - } - - link.next = path; - link.vecelt = -1; - fmt = GET_RTX_FORMAT (code); - len = GET_RTX_LENGTH (code); - for (i = 0; i < len; i++) - { - link.pos = i; - if (fmt[i] == 'e' || fmt[i] == 'u') - match_rtx (XEXP (x, i), &link, fail_label); - else if (fmt[i] == 'E') - { - int j; - printf (" if (XVECLEN (x, %d) != %d) goto L%d;\n", - i, XVECLEN (x, i), fail_label); - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - { - link.vecelt = j; - match_rtx (XVECEXP (x, i, j), &link, fail_label); - } - } - else if (fmt[i] == 'i') - { - /* Make sure that at run time `x' is the RTX we want to test. */ - if (i != 0) - { - printf (" x = "); - print_path (path); - printf (";\n"); - } - - printf (" if (XINT (x, %d) != %d) goto L%d;\n", - i, XINT (x, i), fail_label); - } - else if (fmt[i] == 's') - { - /* Make sure that at run time `x' is the RTX we want to test. */ - if (i != 0) - { - printf (" x = "); - print_path (path); - printf (";\n"); - } - - printf (" if (strcmp (XSTR (x, %d), \"%s\")) goto L%d;\n", - i, XSTR (x, i), fail_label); - } - } -} - -/* Given a PATH, representing a path down the instruction's - pattern from the root to a certain point, output code to - evaluate to the rtx at that point. */ - -void -print_path (path) - struct link *path; -{ - if (path == 0) - printf ("pat"); - else if (path->vecelt >= 0) - { - printf ("XVECEXP ("); - print_path (path->next); - printf (", %d, %d)", path->pos, path->vecelt); - } - else - { - printf ("XEXP ("); - print_path (path->next); - printf (", %d)", path->pos); - } -} - -void -print_code (code) - RTX_CODE code; -{ - register char *p1; - for (p1 = GET_RTX_NAME (code); *p1; p1++) - { - if (*p1 >= 'a' && *p1 <= 'z') - putchar (*p1 + 'A' - 'a'); - else - putchar (*p1); - } -} - -int -xmalloc (size) -{ - register int val = malloc (size); - - if (val == 0) - fatal ("virtual memory exhausted"); - return val; -} - -int -xrealloc (ptr, size) - char *ptr; - int size; -{ - int result = realloc (ptr, size); - if (!result) - fatal ("virtual memory exhausted"); - return result; -} - -void -fatal (s, a1, a2) - char *s; -{ - fprintf (stderr, "genpeep: "); - fprintf (stderr, s, a1, a2); - fprintf (stderr, "\n"); - exit (FATAL_EXIT_CODE); -} - -/* More 'friendly' abort that prints the line and file. - config.h can #define abort fancy_abort if you like that sort of thing. */ - -void -fancy_abort () -{ - fatal ("Internal gcc abort."); -} - -int -main (argc, argv) - int argc; - char **argv; -{ - rtx desc; - FILE *infile; - extern rtx read_rtx (); - register int c; - - max_opno = -1; - - obstack_init (rtl_obstack); - - if (argc <= 1) - fatal ("No input file name."); - - infile = fopen (argv[1], "r"); - if (infile == 0) - { - perror (argv[1]); - exit (FATAL_EXIT_CODE); - } - - init_rtl (); - - printf ("/* Generated automatically by the program `genpeep'\n\ -from the machine description file `md'. */\n\n"); - - printf ("#include \"config.h\"\n"); - printf ("#include \"rtl.h\"\n"); - printf ("#include \"regs.h\"\n"); - printf ("#include \"real.h\"\n\n"); - - printf ("extern rtx peep_operand[];\n\n"); - printf ("#define operands peep_operand\n\n"); - - printf ("rtx\npeephole (ins1)\n rtx ins1;\n{\n"); - printf (" rtx insn, x, pat;\n"); - printf (" int i;\n\n"); - - /* Early out: no peepholes for insns followed by barriers. */ - printf (" if (NEXT_INSN (ins1)\n"); - printf (" && GET_CODE (NEXT_INSN (ins1)) == BARRIER)\n"); - printf (" return 0;\n\n"); - - /* Read the machine description. */ - - while (1) - { - c = read_skip_spaces (infile); - if (c == EOF) - break; - ungetc (c, infile); - - desc = read_rtx (infile); - if (GET_CODE (desc) == DEFINE_PEEPHOLE) - { - gen_peephole (desc); - insn_code_number++; - } - if (GET_CODE (desc) == DEFINE_INSN || GET_CODE (desc) == DEFINE_EXPAND) - { - insn_code_number++; - } - } - - printf (" return 0;\n}\n\n"); - - if (max_opno == -1) - max_opno = 1; - - printf ("rtx peep_operand[%d];\n", max_opno + 1); - - fflush (stdout); - exit (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE); -} diff --git a/gnu/usr.bin/gcc1/cc1/genrecog.c b/gnu/usr.bin/gcc1/cc1/genrecog.c deleted file mode 100644 index be759ba77b..0000000000 --- a/gnu/usr.bin/gcc1/cc1/genrecog.c +++ /dev/null @@ -1,1095 +0,0 @@ -/* Generate code from machine description to emit insns as rtl. - Copyright (C) 1987,1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This program is used to produce insn-recog.c, which contains - a function called `recog' plus its subroutines. - These functions contain a decision tree - that recognizes whether an rtx, the argument given to recog, - is a valid instruction. - - recog returns -1 if the rtx is not valid. - If the rtx is valid, recog returns a nonnegative number - which is the insn code number for the pattern that matched. - This is the same as the order in the machine description of the - entry that matched. This number can be used as an index into - insn_templates and insn_n_operands (found in insn-output.c) - or as an argument to output_insn_hairy (also in insn-output.c). */ - -#include -#include "config.h" -#include "rtl.h" -#include "obstack.h" - -struct obstack obstack; -struct obstack *rtl_obstack = &obstack; - -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free -extern int xmalloc (); -extern void free (); - -/* Data structure for decision tree for recognizing - legitimate instructions. */ - -struct decision -{ - int number; - char *position; - RTX_CODE code; - char *exact; - enum machine_mode mode; - char *tests; - int insn_code_number; - struct decision *next; - struct decision *success; - int opno; - int dupno; - int dupcount; - int test_elt_zero_int; - int elt_zero_int; - int test_elt_one_int; - int elt_one_int; - int ignmode; - struct decision *afterward; - int label_needed; - char *c_test; - char *reg_class; - char enforce_mode; - int veclen; - int subroutine_number; -}; - -#define SUBROUTINE_THRESHOLD 50 - -int next_subroutine_number; - -/* -recognize (top) -{ - staten: - x = XVECEXP (top, 0, 3); - if (test_code (GET_CODE (x)) - && test_mode (MODE (x)) - && whatever_else) - goto statep; - else if (next one...) - goto statem: - goto stater; - - statep: - actions...; - return 1; - - statem: - x = stack[depth--]; - more tests...; - - stateq: - stack[++depth] = x; - x = XEXP (stack[depth], 0); - more tests...; - - stater: - x = XEXP (stack[depth], 1); -} - -*/ - -int next_number; - -int next_insn_code; - -/* Number of MATCH_DUP's seen so far in this instruction. */ -int dupcount; - -struct decision *add_to_sequence (); -struct decision *try_merge_2 (); -void write_subroutine (); -void print_code (); -void clear_codes (); -void clear_modes (); -void change_state (); -void write_tree (); -char *copystr (); -char *concat (); -void fatal (); -void fancy_abort (); -void mybzero (); - -struct decision *first; - -/* Construct and return a sequence of decisions - that will recognize INSN. */ - -struct decision * -make_insn_sequence (insn) - rtx insn; -{ - rtx x; - char *c_test = XSTR (insn, 2); - struct decision *last; - - dupcount = 0; - - if (XVECLEN (insn, 1) == 1) - x = XVECEXP (insn, 1, 0); - else - { - x = rtx_alloc (PARALLEL); - XVEC (x, 0) = XVEC (insn, 1); - PUT_MODE (x, VOIDmode); - } - - last = add_to_sequence (x, 0, ""); - - if (c_test[0]) - last->c_test = c_test; - last->insn_code_number = next_insn_code++; - - return first; -} - -struct decision * -add_to_sequence (pattern, last, position) - rtx pattern; - struct decision *last; - char *position; -{ - register RTX_CODE code; - register struct decision *new - = (struct decision *) xmalloc (sizeof (struct decision)); - struct decision *this; - char *newpos; - register char *fmt; - register int i; - int depth; - int len; - - new->number = next_number++; - new->position = copystr (position); - new->exact = 0; - new->next = 0; - new->success = 0; - new->insn_code_number = -1; - new->tests = 0; - new->opno = -1; - new->dupno = -1; - new->dupcount = -1; - new->test_elt_zero_int = 0; - new->test_elt_one_int = 0; - new->elt_zero_int = 0; - new->elt_one_int = 0; - new->enforce_mode = 0; - new->ignmode = 0; - new->afterward = 0; - new->label_needed = 0; - new->c_test = 0; - new->reg_class = 0; - new->veclen = 0; - new->subroutine_number = 0; - - this = new; - - if (last == 0) - first = new; - else - last->success = new; - - depth = strlen (position); - newpos = (char *) alloca (depth + 2); - strcpy (newpos, position); - newpos[depth + 1] = 0; - - restart: - - if (pattern == 0) - { - new->exact = "0"; - new->code = UNKNOWN; - new->mode = VOIDmode; - return new; - } - - switch (GET_MODE (pattern)) - { - case 0: - new->mode = VOIDmode; - break; - - default: - new->mode = GET_MODE (pattern); - break; - } - - new->code = code = GET_CODE (pattern); - - switch (code) - { - case MATCH_OPERAND: - new->opno = XINT (pattern, 0); - new->code = UNKNOWN; - new->tests = XSTR (pattern, 1); - if (*new->tests == 0) - new->tests = 0; - new->reg_class = XSTR (pattern, 2); - if (*new->reg_class == 0) - new->reg_class = 0; - return new; - - case MATCH_OPERATOR: - new->opno = XINT (pattern, 0); - new->code = UNKNOWN; - new->tests = XSTR (pattern, 1); - if (*new->tests == 0) - new->tests = 0; - for (i = 0; i < XVECLEN (pattern, 2); i++) - { - newpos[depth] = i + '0'; - new = add_to_sequence (XVECEXP (pattern, 2, i), new, newpos); - } - this->success->enforce_mode = 0; - return new; - - case MATCH_DUP: - new->dupno = XINT (pattern, 0); - new->dupcount = dupcount++; - new->code = UNKNOWN; - return new; - - case ADDRESS: - pattern = XEXP (pattern, 0); - goto restart; - - case PC: - new->exact = "pc_rtx"; - return new; - - case CC0: - new->exact = "cc0_rtx"; - return new; - - case CONST_INT: - if (INTVAL (pattern) == 0) - { - new->exact = "const0_rtx"; - return new; - } - if (INTVAL (pattern) == 1) - { - new->exact = "const1_rtx"; - return new; - } - break; - - case SET: - newpos[depth] = '0'; - new = add_to_sequence (SET_DEST (pattern), new, newpos); - this->success->enforce_mode = 1; - newpos[depth] = '1'; - new = add_to_sequence (SET_SRC (pattern), new, newpos); - return new; - - case STRICT_LOW_PART: - newpos[depth] = '0'; - new = add_to_sequence (XEXP (pattern, 0), new, newpos); - this->success->enforce_mode = 1; - return new; - - case SUBREG: - this->test_elt_one_int = 1; - this->elt_one_int = XINT (pattern, 1); - newpos[depth] = '0'; - new = add_to_sequence (XEXP (pattern, 0), new, newpos); - this->success->enforce_mode = 1; - return new; - - case ZERO_EXTRACT: - case SIGN_EXTRACT: - newpos[depth] = '0'; - new = add_to_sequence (XEXP (pattern, 0), new, newpos); - this->success->enforce_mode = 1; - newpos[depth] = '1'; - new = add_to_sequence (XEXP (pattern, 1), new, newpos); - newpos[depth] = '2'; - new = add_to_sequence (XEXP (pattern, 2), new, newpos); - return new; - } - - fmt = GET_RTX_FORMAT (code); - len = GET_RTX_LENGTH (code); - for (i = 0; i < len; i++) - { - newpos[depth] = '0' + i; - if (fmt[i] == 'e' || fmt[i] == 'u') - new = add_to_sequence (XEXP (pattern, i), new, newpos); - else if (fmt[i] == 'i' && i == 0) - { - this->test_elt_zero_int = 1; - this->elt_zero_int = XINT (pattern, i); - } - else if (fmt[i] == 'i' && i == 1) - { - this->test_elt_one_int = 1; - this->elt_one_int = XINT (pattern, i); - } - else if (fmt[i] == 'E') - { - register int j; - /* We do not handle a vector appearing as other than - the first item, just because nothing uses them - and by handling only the special case - we can use one element in newpos for either - the item number of a subexpression - or the element number in a vector. */ - if (i != 0) - abort (); - this->veclen = XVECLEN (pattern, i); - for (j = 0; j < XVECLEN (pattern, i); j++) - { - newpos[depth] = 'a' + j; - new = add_to_sequence (XVECEXP (pattern, i, j), - new, newpos); - } - } - else if (fmt[i] != '0') - abort (); - } - return new; -} - -/* Merge two decision trees OLD and ADD, - modifying OLD destructively, - and return the merged tree. */ - -struct decision * -merge_trees (old, add) - register struct decision *old, *add; -{ - while (add) - { - register struct decision *next = add->next; - add->next = 0; - if (!try_merge_1 (old, add)) - old = try_merge_2 (old, add); - add = next; - } - return old; -} - -/* Merge ADD into the next-chain starting with OLD - only if it overlaps a condition already tested in OLD. - Returns 1 if successful (OLD is modified), - 0 if nothing has been done. */ - -int -try_merge_1 (old, add) - register struct decision *old, *add; -{ - while (old) - { - if ((old->position == add->position - || (old->position && add->position - && !strcmp (old->position, add->position))) - && (old->tests == add->tests - || (old->tests && add->tests && !strcmp (old->tests, add->tests))) - && (old->c_test == add->c_test - || (old->c_test && add->c_test && !strcmp (old->c_test, add->c_test))) - && old->test_elt_zero_int == add->test_elt_zero_int - && old->elt_zero_int == add->elt_zero_int - && old->test_elt_one_int == add->test_elt_one_int - && old->elt_one_int == add->elt_one_int - && old->veclen == add->veclen - && old->dupno == add->dupno - && old->opno == add->opno - && (old->tests == 0 - || (add->enforce_mode ? no_same_mode (old) : old->next == 0)) - && old->code == add->code - && old->mode == add->mode) - { - old->success = merge_trees (old->success, add->success); - if (old->insn_code_number >= 0 && add->insn_code_number >= 0) - fatal ("Two actions at one point in tree."); - if (old->insn_code_number == -1) - old->insn_code_number = add->insn_code_number; - return 1; - } - old = old->next; - } - return 0; -} - -/* Merge ADD into the next-chain that starts with OLD, - preferably after something that tests the same place - that ADD does. - The next-chain of ADD itself is ignored, and it is set - up for entering ADD into the new chain. - Returns the new chain. */ - -struct decision * -try_merge_2 (old, add) - struct decision *old, *add; -{ - register struct decision *p; - struct decision *last = 0; - struct decision *last_same_place = 0; - - /* Put this in after the others that test the same place, - if there are any. If not, find the last chain element - and insert there. - - One modification: if this one is NOT a MATCH_OPERAND, - put it before any MATCH_OPERANDS that test the same place. - - Another: if enforce_mode (i.e. this is first operand of a SET), - put this after the last thing that tests the same place for - the same mode. */ - - int operand = 0 != add->tests; - - for (p = old; p; p = p->next) - { - if (p->position == add->position - || (p->position && add->position - && !strcmp (p->position, add->position))) - { - last_same_place = p; - /* If enforce_mode, segregate the modes in numerical order. */ - if (p->enforce_mode && (int) add->mode < (int) p->mode) - break; -#if 0 - /* Keep explicit decompositions before those that test predicates. - If enforce_mode, do this separately within each mode. */ - if (! p->enforce_mode || p->mode == add->mode) - if (!operand && p->tests) - break; -#endif - } - /* If this is past the end of the decisions at the same place as ADD, - stop looking now; add ADD before here. */ - else if (last_same_place) - break; - last = p; - } - - /* Insert before P, which means after LAST. */ - - if (last) - { - add->next = last->next; - last->next = add; - return old; - } - - add->next = old; - return add; -} - -int -no_same_mode (node) - struct decision *node; -{ - register struct decision *p; - register enum machine_mode mode = node->mode; - - for (p = node->next; p; p = p->next) - if (p->mode == mode) - return 0; - - return 1; -} - -/* Count the number of subnodes of node NODE, assumed to be the start - of a next-chain. If the number is high enough, make NODE start - a separate subroutine in the C code that is generated. */ - -int -break_out_subroutines (node) - struct decision *node; -{ - int size = 0; - struct decision *sub; - for (sub = node; sub; sub = sub->next) - size += 1 + break_out_subroutines (sub->success); - if (size > SUBROUTINE_THRESHOLD) - { - node->subroutine_number = ++next_subroutine_number; - write_subroutine (node); - size = 1; - } - return size; -} - -void -write_subroutine (tree) - struct decision *tree; -{ - printf ("int\nrecog_%d (x0, insn)\n register rtx x0;\n rtx insn;\n{\n", - tree->subroutine_number); - printf (" register rtx x1, x2, x3, x4, x5;\n rtx x6, x7, x8, x9, x10, x11;\n"); - printf (" int tem;\n"); - write_tree (tree, "", 0, "", 1); - printf (" ret0: return -1;\n}\n\n"); -} - -/* Write out C code to perform the decisions in the tree. */ - -void -write_tree (tree, prevpos, afterward, afterpos, initial) - struct decision *tree; - char *prevpos; - int afterward; - char *afterpos; - int initial; -{ - register struct decision *p, *p1; - char *pos; - register int depth; - int ignmode; - enum anon1 { NO_SWITCH, CODE_SWITCH, MODE_SWITCH } in_switch = NO_SWITCH; - char modemap[NUM_MACHINE_MODES]; - char codemap[NUM_RTX_CODE]; - - pos = prevpos; - - if (tree->subroutine_number > 0 && ! initial) - { - printf (" L%d:\n", tree->number); - - if (afterward) - { - printf (" tem = recog_%d (x0, insn);\n", - tree->subroutine_number); - printf (" if (tem >= 0) return tem;\n"); - change_state (pos, afterpos); - printf (" goto L%d;\n", afterward); - } - else - printf (" return recog_%d (x0, insn);\n", - tree->subroutine_number); - return; - } - - tree->label_needed = 1; - for (p = tree; p; p = p->next) - { - /* Find the next alternative to p - that might be true when p is true. - Test that one next if p's successors fail. - Note that when the `tests' field is nonzero - it is up to the specified test-function to compare machine modes - and some (such as general_operand) don't always do so. - But when inside a switch-on-modes we ignore this and - consider all modes mutually exclusive. */ - for (p1 = p->next; p1; p1 = p1->next) - if (((p->code == UNKNOWN || p1->code == UNKNOWN || p->code == p1->code) - && (p->mode == VOIDmode || p1->mode == VOIDmode - || p->mode == p1->mode - || (in_switch != MODE_SWITCH && (p->tests || p1->tests)))) - || strcmp (p1->position, p->position)) - break; - p->afterward = p1; - if (p1) p1->label_needed = 1; - - if (in_switch == MODE_SWITCH - && (p->mode == VOIDmode || (! p->enforce_mode && p->tests != 0))) - { - in_switch = NO_SWITCH; - printf (" }\n"); - } - if (in_switch == CODE_SWITCH && p->code == UNKNOWN) - { - in_switch = NO_SWITCH; - printf (" }\n"); - } - - if (p->label_needed) - printf (" L%d:\n", p->number); - - if (p->success == 0 && p->insn_code_number < 0) - abort (); - - change_state (pos, p->position); - pos = p->position; - depth = strlen (pos); - - ignmode = p->ignmode || pos[depth - 1] == '*' || p->tests; - - if (in_switch == NO_SWITCH) - { - /* If p and its alternatives all want the same mode, - reject all others at once, first, then ignore the mode. */ - if (!ignmode && p->mode != VOIDmode && p->next && same_modes (p, p->mode)) - { - printf (" if (GET_MODE (x%d) != %smode)\n", - depth, GET_MODE_NAME (p->mode)); - if (afterward) - { - printf (" {\n "); - change_state (pos, afterpos); - printf (" goto L%d;\n }\n", afterward); - } - else - printf (" goto ret0;\n"); - clear_modes (p); - ignmode = 1; - } - - /* If p and its alternatives all want the same code, - reject all others at once, first, then ignore the code. */ - if (p->code != UNKNOWN && p->next && same_codes (p, p->code)) - { - printf (" if (GET_CODE (x%d) != ", depth); - print_code (p->code); - printf (")\n"); - if (afterward) - { - printf (" {"); - change_state (pos, afterpos); - printf (" goto L%d; }\n", afterward); - } - else - printf (" goto ret0;\n"); - clear_codes (p); - } - } - - /* If p and its alternatives all have different modes - and there are at least 4 of them, make a switch. */ - if (in_switch == NO_SWITCH && pos[depth-1] != '*') - { - register int i; - int lose = 0; - - mybzero (modemap, sizeof modemap); - for (p1 = p, i = 0; - (p1 && p1->mode != VOIDmode - && (p1->tests == 0 || p1->enforce_mode)); - p1 = p1->next, i++) - { - if (! p->enforce_mode && modemap[(int) p1->mode]) - { - lose = 1; - break; - } - modemap[(int) p1->mode] = 1; - } - if (!lose && i >= 4) - { - in_switch = MODE_SWITCH; - printf (" switch (GET_MODE (x%d))\n {\n", depth); - } - } - - if (in_switch == NO_SWITCH) - { - register int i; - mybzero (codemap, sizeof codemap); - for (p1 = p, i = 0; p1 && p1->code != UNKNOWN; p1 = p1->next, i++) - { - if (codemap[(int) p1->code]) - break; - codemap[(int) p1->code] = 1; - } - if ((p1 == 0 || p1->code == UNKNOWN) && i >= 4) - { - in_switch = CODE_SWITCH; - printf (" switch (GET_CODE (x%d))\n {\n", depth); - } - } - - if (in_switch == MODE_SWITCH) - { - if (modemap[(int) p->mode]) - { - printf (" case %smode:\n", GET_MODE_NAME (p->mode)); - modemap[(int) p->mode] = 0; - } - } - if (in_switch == CODE_SWITCH) - { - if (codemap[(int) p->code]) - { - printf (" case "); - print_code (p->code); - printf (":\n"); - codemap[(int) p->code] = 0; - } - } - - printf (" if ("); - if (p->exact || (p->code != UNKNOWN && in_switch != CODE_SWITCH)) - { - if (p->exact) - printf ("x%d == %s", depth, p->exact); - else - { - printf ("GET_CODE (x%d) == ", depth); - print_code (p->code); - } - printf (" && "); - } - if (p->mode != VOIDmode && !ignmode && in_switch != MODE_SWITCH) - printf ("GET_MODE (x%d) == %smode && ", - depth, GET_MODE_NAME (p->mode)); - if (p->test_elt_zero_int) - printf ("XINT (x%d, 0) == %d && ", depth, p->elt_zero_int); - if (p->veclen) - printf ("XVECLEN (x%d, 0) == %d && ", depth, p->veclen); - if (p->test_elt_one_int) - printf ("XINT (x%d, 1) == %d && ", depth, p->elt_one_int); - if (p->dupno >= 0) - printf ("rtx_equal_p (x%d, recog_operand[%d]) && ", depth, p->dupno); - if (p->tests) - printf ("%s (x%d, %smode)", p->tests, depth, - GET_MODE_NAME (p->mode)); - else - printf ("1"); - - if (p->opno >= 0) - printf (")\n { recog_operand[%d] = x%d; ", - p->opno, depth); - else - printf (")\n "); - - if (p->c_test) - printf ("if (%s) ", p->c_test); - - if (p->insn_code_number >= 0) - printf ("return %d;", p->insn_code_number); - else - printf ("goto L%d;", p->success->number); - - if (p->opno >= 0) - printf (" }\n"); - else - printf ("\n"); - - /* Now, if inside a switch, branch to next switch member - that might also need to be tested if this one fails. */ - - if (in_switch == CODE_SWITCH) - { - /* Find the next alternative to p - that might be applicable if p was applicable. */ - for (p1 = p->next; p1; p1 = p1->next) - if (p1->code == UNKNOWN || p->code == p1->code) - break; - if (p1 == 0 || p1->code == UNKNOWN) - printf (" break;\n"); - else if (p1 != p->next) - { - printf (" goto L%d;\n", p1->number); - p1->label_needed = 1; - } - } - - if (in_switch == MODE_SWITCH) - { - /* Find the next alternative to p - that might be applicable if p was applicable. */ - for (p1 = p->next; p1; p1 = p1->next) - if (p1->mode == VOIDmode || p->mode == p1->mode) - break; - if (p1 == 0 || p1->mode == VOIDmode) - printf (" break;\n"); - else if (p1 != p->next) - { - printf (" goto L%d;\n", p1->number); - p1->label_needed = 1; - } - } - } - - if (in_switch != NO_SWITCH) - printf (" }\n"); - - if (afterward) - { - change_state (pos, afterpos); - printf (" goto L%d;\n", afterward); - } - else - printf (" goto ret0;\n"); - - for (p = tree; p; p = p->next) - if (p->success) - { - { - pos = p->position; - write_tree (p->success, pos, - p->afterward ? p->afterward->number : afterward, - p->afterward ? pos : afterpos, - 0); - } - } -} - -void -print_code (code) - RTX_CODE code; -{ - register char *p1; - for (p1 = GET_RTX_NAME (code); *p1; p1++) - { - if (*p1 >= 'a' && *p1 <= 'z') - putchar (*p1 + 'A' - 'a'); - else - putchar (*p1); - } -} - -int -same_codes (p, code) - register struct decision *p; - register RTX_CODE code; -{ - for (; p; p = p->next) - if (p->code != code) - return 0; - - return 1; -} - -void -clear_codes (p) - register struct decision *p; -{ - for (; p; p = p->next) - p->code = UNKNOWN; -} - -int -same_modes (p, mode) - register struct decision *p; - register enum machine_mode mode; -{ - for (; p; p = p->next) - if (p->mode != mode || p->tests) - return 0; - - return 1; -} - -void -clear_modes (p) - register struct decision *p; -{ - for (; p; p = p->next) - p->ignmode = 1; -} - -void -change_state (oldpos, newpos) - char *oldpos; - char *newpos; -{ - int odepth = strlen (oldpos); - int depth = odepth; - int ndepth = strlen (newpos); - - /* Pop up as many levels as necessary. */ - - while (strncmp (oldpos, newpos, depth)) - --depth; - - /* Go down to desired level. */ - - while (depth < ndepth) - { - if (newpos[depth] == '*') - printf (" x%d = recog_addr_dummy;\n XEXP (x%d, 0) = x%d;\n", - depth + 1, depth + 1, depth); - else if (newpos[depth] >= 'a' && newpos[depth] <= 'z') - printf (" x%d = XVECEXP (x%d, 0, %d);\n", - depth + 1, depth, newpos[depth] - 'a'); - else - printf (" x%d = XEXP (x%d, %c);\n", - depth + 1, depth, newpos[depth]); - ++depth; - } -} - -char * -copystr (s1) - char *s1; -{ - register char *tem; - - if (s1 == 0) - return 0; - - tem = (char *) xmalloc (strlen (s1) + 1); - strcpy (tem, s1); - - return tem; -} - -void -mybzero (b, length) - register char *b; - register int length; -{ - while (length-- > 0) - *b++ = 0; -} - -char * -concat (s1, s2) - char *s1, *s2; -{ - register char *tem; - - if (s1 == 0) - return s2; - if (s2 == 0) - return s1; - - tem = (char *) xmalloc (strlen (s1) + strlen (s2) + 2); - strcpy (tem, s1); - strcat (tem, " "); - strcat (tem, s2); - - return tem; -} - -int -xrealloc (ptr, size) - char *ptr; - int size; -{ - int result = realloc (ptr, size); - if (!result) - fatal ("virtual memory exhausted"); - return result; -} - -int -xmalloc (size) -{ - register int val = malloc (size); - - if (val == 0) - fatal ("virtual memory exhausted"); - return val; -} - -void -fatal (s, a1, a2) - char *s; -{ - fprintf (stderr, "genrecog: "); - fprintf (stderr, s, a1, a2); - fprintf (stderr, "\n"); - fprintf (stderr, "after %d instruction definitions\n", - next_insn_code); - exit (FATAL_EXIT_CODE); -} - -/* More 'friendly' abort that prints the line and file. - config.h can #define abort fancy_abort if you like that sort of thing. */ - -void -fancy_abort () -{ - fatal ("Internal gcc abort."); -} - -int -main (argc, argv) - int argc; - char **argv; -{ - rtx desc; - struct decision *tree = 0; - FILE *infile; - extern rtx read_rtx (); - register int c; - - obstack_init (rtl_obstack); - - if (argc <= 1) - fatal ("No input file name."); - - infile = fopen (argv[1], "r"); - if (infile == 0) - { - perror (argv[1]); - exit (FATAL_EXIT_CODE); - } - - init_rtl (); - next_insn_code = 0; - - printf ("/* Generated automatically by the program `genrecog'\n\ -from the machine description file `md'. */\n\n"); - - /* Read the machine description. */ - - while (1) - { - c = read_skip_spaces (infile); - if (c == EOF) - break; - ungetc (c, infile); - - desc = read_rtx (infile); - if (GET_CODE (desc) == DEFINE_INSN) - tree = merge_trees (tree, make_insn_sequence (desc)); - if (GET_CODE (desc) == DEFINE_PEEPHOLE - || GET_CODE (desc) == DEFINE_EXPAND) - next_insn_code++; - } - - printf ("#include \"config.h\"\n"); - printf ("#include \"rtl.h\"\n"); - printf ("#include \"insn-config.h\"\n"); - printf ("#include \"recog.h\"\n"); - printf ("#include \"real.h\"\n"); - printf ("\n\ -/* `recog' contains a decision tree\n\ - that recognizes whether the rtx X0 is a valid instruction.\n\ -\n\ - recog returns -1 if the rtx is not valid.\n\ - If the rtx is valid, recog returns a nonnegative number\n\ - which is the insn code number for the pattern that matched.\n"); - printf (" This is the same as the order in the machine description of\n\ - the entry that matched. This number can be used as an index into\n\ - insn_templates and insn_n_operands (found in insn-output.c)\n\ - or as an argument to output_insn_hairy (also in insn-output.c). */\n\n"); - - printf ("rtx recog_operand[MAX_RECOG_OPERANDS];\n\n"); - printf ("rtx *recog_operand_loc[MAX_RECOG_OPERANDS];\n\n"); - printf ("rtx *recog_dup_loc[MAX_DUP_OPERANDS];\n\n"); - printf ("char recog_dup_num[MAX_DUP_OPERANDS];\n\n"); - printf ("extern rtx recog_addr_dummy;\n\n"); - printf ("#define operands recog_operand\n\n"); - - break_out_subroutines (tree); - - printf ("int\nrecog (x0, insn)\n register rtx x0;\n rtx insn;\n{\n"); - printf (" register rtx x1, x2, x3, x4, x5;\n rtx x6, x7, x8, x9, x10, x11;\n"); - printf (" int tem;\n"); - - write_tree (tree, "", 0, "", 1); - printf (" ret0: return -1;\n}\n"); - - fflush (stdout); - exit (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE); -} diff --git a/gnu/usr.bin/gcc1/cc1/global-alloc.c b/gnu/usr.bin/gcc1/cc1/global-alloc.c deleted file mode 100644 index 2a1883b580..0000000000 --- a/gnu/usr.bin/gcc1/cc1/global-alloc.c +++ /dev/null @@ -1,1090 +0,0 @@ -/* Allocate registers for pseudo-registers that span basic blocks. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include -#include "config.h" -#include "rtl.h" -#include "flags.h" -#include "basic-block.h" -#include "hard-reg-set.h" -#include "regs.h" -#include "insn-config.h" - -/* This pass of the compiler performs global register allocation. - It assigns hard register numbers to all the pseudo registers - that were not handled in local_alloc. Assignments are recorded - in the vector reg_renumber, not by changing the rtl code. - (Such changes are made by final). The entry point is - the function global_alloc. - - After allocation is complete, the reload pass is run as a subroutine - of this pass, so that when a pseudo reg loses its hard reg due to - spilling it is possible to make a second attempt to find a hard - reg for it. The reload pass is independent in other respects - and it is run even when stupid register allocation is in use. - - 1. count the pseudo-registers still needing allocation - and assign allocation-numbers (allocnos) to them. - Set up tables reg_allocno and allocno_reg to map - reg numbers to allocnos and vice versa. - max_allocno gets the number of allocnos in use. - - 2. Allocate a max_allocno by max_allocno conflict bit matrix and clear it. - Allocate a max_allocno by FIRST_PSEUDO_REGISTER conflict matrix - for conflicts between allocnos and explicit hard register use - (which includes use of pseudo-registers allocated by local_alloc). - - 3. for each basic block - walk forward through the block, recording which - unallocated registers and which hardware registers are live. - Build the conflict matrix between the unallocated registers - and another of unallocated registers versus hardware registers. - Also record the preferred hardware registers - for each unallocated one. - - 4. Sort a table of the allocnos into order of - desirability of the variables. - - 5. Allocate the variables in that order; each if possible into - a preferred register, else into another register. */ - -/* Number of pseudo-registers still requiring allocation - (not allocated by local_allocate). */ - -static int max_allocno; - -/* Indexed by (pseudo) reg number, gives the allocno, or -1 - for pseudo registers already allocated by local_allocate. */ - -static int *reg_allocno; - -/* Indexed by allocno, gives the reg number. */ - -static int *allocno_reg; - -/* A vector of the integers from 0 to max_allocno-1, - sorted in the order of first-to-be-allocated first. */ - -static int *allocno_order; - -/* Indexed by an allocno, gives the number of consecutive - hard registers needed by that pseudo reg. */ - -static int *allocno_size; - -/* max_allocno by max_allocno array of bits, - recording whether two allocno's conflict (can't go in the same - hardware register). - - `conflicts' is not symmetric; a conflict between allocno's i and j - is recorded either in element i,j or in element j,i. */ - -static int *conflicts; - -/* Number of ints require to hold max_allocno bits. - This is the length of a row in `conflicts'. */ - -static int allocno_row_words; - -/* Two macros to test or store 1 in an element of `conflicts'. */ - -#define CONFLICTP(I, J) \ - (conflicts[(I) * allocno_row_words + (J) / INT_BITS] \ - & (1 << ((J) % INT_BITS))) - -#define SET_CONFLICT(I, J) \ - (conflicts[(I) * allocno_row_words + (J) / INT_BITS] \ - |= (1 << ((J) % INT_BITS))) - -/* Set of hard regs currently live (during scan of all insns). */ - -static HARD_REG_SET hard_regs_live; - -/* Indexed by N, set of hard regs conflicting with allocno N. */ - -static HARD_REG_SET *hard_reg_conflicts; - -/* Indexed by N, set of hard regs preferred by allocno N. - This is used to make allocnos go into regs that are copied to or from them, - when possible, to reduce register shuffling. */ - -static HARD_REG_SET *hard_reg_preferences; - -/* Set of registers that some allocno has a preference for. */ - -static HARD_REG_SET regs_someone_prefers; - -/* Set of registers that global-alloc isn't supposed to use. */ - -static HARD_REG_SET no_global_alloc_regs; - -/* Test a bit in TABLE, a vector of HARD_REG_SETs, - for vector element I, and hard register number J. */ - -#define REGBITP(TABLE, I, J) TEST_HARD_REG_BIT (TABLE[I], J) - -/* Set to 1 a bit in a vector of HARD_REG_SETs. Works like REGBITP. */ - -#define SET_REGBIT(TABLE, I, J) SET_HARD_REG_BIT (TABLE[I], J) - -/* Bit mask for allocnos live at current point in the scan. */ - -static int *allocnos_live; - -#define INT_BITS HOST_BITS_PER_INT - -/* Test, set or clear bit number I in allocnos_live, - a bit vector indexed by allocno. */ - -#define ALLOCNO_LIVE_P(I) \ - (allocnos_live[(I) / INT_BITS] & (1 << ((I) % INT_BITS))) - -#define SET_ALLOCNO_LIVE(I) \ - (allocnos_live[(I) / INT_BITS] |= (1 << ((I) % INT_BITS))) - -#define CLEAR_ALLOCNO_LIVE(I) \ - (allocnos_live[(I) / INT_BITS] &= ~(1 << ((I) % INT_BITS))) - -/* Record all regs that are set in any one insn. - Communication from mark_reg_{store,clobber} and global_conflicts. */ - -static rtx *regs_set; -static int n_regs_set; - -static int allocno_compare (); -static void mark_reg_store (); -static void mark_reg_clobber (); -static void mark_reg_live_nc (); -static void mark_reg_death (); -static void dump_conflicts (); -static void find_reg (); -static void global_conflicts (); -static void record_conflicts (); -static void set_preference (); - -/* Perform allocation of pseudo-registers not allocated by local_alloc. - FILE is a file to output debugging information on, - or zero if such output is not desired. */ - -void -global_alloc (file) - FILE *file; -{ - register int i; - - max_allocno = 0; - - CLEAR_HARD_REG_SET (regs_someone_prefers); - - /* A machine may have certain hard registers that - are safe to use only within a basic block. */ - - CLEAR_HARD_REG_SET (no_global_alloc_regs); -#ifdef OVERLAPPING_REGNO_P - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (OVERLAPPING_REGNO_P (i)) - SET_HARD_REG_BIT (no_global_alloc_regs, i); -#endif - - /* Establish mappings from register number to allocation number - and vice versa. In the process, count the allocnos. */ - - reg_allocno = (int *) alloca (max_regno * sizeof (int)); - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - reg_allocno[i] = -1; - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - /* Note that reg_live_length[i] < 0 indicates a "constant" reg - that we are supposed to refrain from putting in a hard reg. - -2 means do make an allocno but don't allocate it. */ - if (reg_n_refs[i] != 0 && reg_renumber[i] < 0 && reg_live_length[i] != -1) - { - reg_allocno[i] = max_allocno++; - if (reg_live_length[i] == 0) - abort (); - } - else - reg_allocno[i] = -1; - - allocno_reg = (int *) alloca (max_allocno * sizeof (int)); - allocno_size = (int *) alloca (max_allocno * sizeof (int)); - bzero (allocno_size, max_allocno * sizeof (int)); - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (reg_allocno[i] >= 0) - { - allocno_reg[reg_allocno[i]] = i; - allocno_size[reg_allocno[i]] = PSEUDO_REGNO_SIZE (i); - } - - /* Allocate the space for the conflict tables. */ - - hard_reg_conflicts = (HARD_REG_SET *) - alloca (max_allocno * sizeof (HARD_REG_SET)); - bzero (hard_reg_conflicts, max_allocno * sizeof (HARD_REG_SET)); - - hard_reg_preferences = (HARD_REG_SET *) - alloca (max_allocno * sizeof (HARD_REG_SET)); - bzero (hard_reg_preferences, max_allocno * sizeof (HARD_REG_SET)); - - allocno_row_words = (max_allocno + INT_BITS - 1) / INT_BITS; - - conflicts = (int *) - alloca (max_allocno * allocno_row_words * sizeof (int)); - bzero (conflicts, max_allocno * allocno_row_words * sizeof (int)); - - allocnos_live = (int *) alloca (allocno_row_words * sizeof (int)); - - /* If there is work to be done (at least one reg to allocate), - perform global conflict analysis and allocate the regs. */ - - if (max_allocno > 0) - { - /* Scan all the insns and compute the conflicts among allocnos - and between allocnos and hard regs. */ - - global_conflicts (); - - /* Determine the order to allocate the remaining pseudo registers. */ - - allocno_order = (int *) alloca (max_allocno * sizeof (int)); - for (i = 0; i < max_allocno; i++) - allocno_order[i] = i; - - /* Default the size to 1, since allocno_compare uses it to divide by. */ - - for (i = 0; i < max_allocno; i++) - if (allocno_size[i] == 0) - allocno_size[i] = 1; - - qsort (allocno_order, max_allocno, sizeof (int), allocno_compare); - - if (file) - dump_conflicts (file); - - /* Try allocating them, one by one, in that order, - except for parameters marked with reg_live_length[regno] == -2. */ - - for (i = 0; i < max_allocno; i++) - if (reg_live_length[allocno_reg[allocno_order[i]]] >= 0) - { - /* If we have more than one register class, - first try allocating in the class that is cheapest - for this pseudo-reg. If that fails, try any reg. */ - if (N_REG_CLASSES > 1) - { - find_reg (allocno_order[i], 0, 0, 0, - hard_reg_preferences[allocno_order[i]]); - if (reg_renumber[allocno_reg[allocno_order[i]]] >= 0) - continue; - } - if (!reg_preferred_or_nothing (allocno_reg[allocno_order[i]])) - find_reg (allocno_order[i], 0, 1, 0, - hard_reg_preferences[allocno_order[i]]); - } - } - - /* Do the reloads now while the allocno data still exist, so that we can - try to assign new hard regs to any pseudo regs that are spilled. */ - - if (n_basic_blocks > 0) - reload (basic_block_head[0], 1, file); -} - -/* Sort predicate for ordering the allocnos. - Returns -1 (1) if *v1 should be allocated before (after) *v2. */ - -static int -allocno_compare (v1, v2) - int *v1, *v2; -{ - register int r1 = allocno_reg[*v1]; - register int r2 = allocno_reg[*v2]; - /* Note that the quotient will never be bigger than - the value of floor_log2 times the maximum number of - times a register can occur in one insn (surely less than 100). - Multiplying this by 10000 can't overflow. */ - register int pri1 - = (((double) (floor_log2 (reg_n_refs[r1]) * reg_n_refs[r1]) - / (reg_live_length[r1] * allocno_size[*v1])) - * 10000); - register int pri2 - = (((double) (floor_log2 (reg_n_refs[r2]) * reg_n_refs[r2]) - / (reg_live_length[r2] * allocno_size[*v2])) - * 10000); - if (pri2 - pri1) - return pri2 - pri1; - - /* If regs are equally good, sort by allocno, - so that the results of qsort leave nothing to chance. */ - return *v1 - *v2; -} - -/* Scan the rtl code and record all conflicts in the conflict matrices. */ - -static void -global_conflicts () -{ - register int b, i; - register rtx insn; - short *block_start_allocnos; - - /* Make a vector that mark_reg_{store,clobber} will store in. */ - regs_set = (rtx *) alloca (max_parallel * sizeof (rtx) * 2); - - block_start_allocnos = (short *) alloca (max_allocno * sizeof (short)); - - for (b = 0; b < n_basic_blocks; b++) - { - bzero (allocnos_live, allocno_row_words * sizeof (int)); - - /* Initialize table of registers currently live - to the state at the beginning of this basic block. - This also marks the conflicts among them. - - For pseudo-regs, there is only one bit for each one - no matter how many hard regs it occupies. - This is ok; we know the size from PSEUDO_REGNO_SIZE. - For explicit hard regs, we cannot know the size that way - since one hard reg can be used with various sizes. - Therefore, we must require that all the hard regs - implicitly live as part of a multi-word hard reg - are explicitly marked in basic_block_live_at_start. */ - - { - register int offset, bit; - register regset old = basic_block_live_at_start[b]; - int ax = 0; - -#ifdef HARD_REG_SET - hard_regs_live = old[0]; -#else - COPY_HARD_REG_SET (hard_regs_live, old); -#endif - for (offset = 0, i = 0; offset < regset_size; offset++) - if (old[offset] == 0) - i += HOST_BITS_PER_INT; - else - for (bit = 1; bit; bit <<= 1, i++) - { - if (i >= max_regno) - break; - if (old[offset] & bit) - { - register int a = reg_allocno[i]; - if (a >= 0) - { - SET_ALLOCNO_LIVE (a); - block_start_allocnos[ax++] = a; - } - else if ((a = reg_renumber[i]) >= 0) - mark_reg_live_nc (a, PSEUDO_REGNO_MODE (i)); - } - } - - /* Record that each allocno now live conflicts with each other - allocno now live, and with each hard reg now live. */ - - record_conflicts (block_start_allocnos, ax); - } - - insn = basic_block_head[b]; - - /* Scan the code of this basic block, noting which allocnos - and hard regs are born or die. When one is born, - record a conflict with all others currently live. */ - - while (1) - { - register RTX_CODE code = GET_CODE (insn); - register rtx link; - - /* Make regs_set an empty set. */ - - n_regs_set = 0; - - if (code == INSN || code == CALL_INSN || code == JUMP_INSN) - { - /* Mark any registers clobbered by INSN as live, - so they conflict with the inputs. */ - - note_stores (PATTERN (insn), mark_reg_clobber); - - /* Mark any registers dead after INSN as dead now. */ - - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_DEAD) - mark_reg_death (XEXP (link, 0)); - - /* Mark any registers set in INSN as live, - and mark them as conflicting with all other live regs. - Clobbers are processed again, so they conflict with - the registers that are set. */ - - note_stores (PATTERN (insn), mark_reg_store); - - /* Mark any registers both set and dead after INSN as dead. - This is not redundant! - A register may be set and killed in the same insn. - It is necessary to mark them as live, above, to get - the right conflicts within the insn. */ - - while (n_regs_set > 0) - if (find_regno_note (insn, REG_DEAD, REGNO (regs_set[--n_regs_set]))) - mark_reg_death (regs_set[n_regs_set]); - - /* Likewise for regs set by incrementation. */ - - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_INC - && find_regno_note (insn, REG_DEAD, REGNO (XEXP (link, 0)))) - mark_reg_death (XEXP (link, 0)); - } - - if (insn == basic_block_end[b]) - break; - insn = NEXT_INSN (insn); - } - } -} - -/* Assign a hard register to ALLOCNO; look for one that is the beginning - of a long enough stretch of hard regs none of which conflicts with ALLOCNO. - The registers marked in PREFREGS are tried first. - - If ALL_REGS_P is zero, consider only the preferred class of ALLOCNO's reg. - Otherwise ignore that preferred class. - - If ACCEPT_CALL_CLOBBERED is nonzero, accept a call-clobbered hard reg that - will have to be saved and restored at calls. - - If we find one, record it in reg_renumber. - If not, do nothing. */ - -static void -find_reg (allocno, losers, all_regs_p, accept_call_clobbered, prefregs) - int allocno; - register short *losers; - int all_regs_p; - int accept_call_clobbered; - HARD_REG_SET prefregs; -{ - register int i, prefreg, pass; -#ifdef HARD_REG_SET - register /* Declare it register if it's a scalar. */ -#endif - HARD_REG_SET used; - - enum reg_class class - = all_regs_p ? GENERAL_REGS : reg_preferred_class (allocno_reg[allocno]); - enum machine_mode mode = PSEUDO_REGNO_MODE (allocno_reg[allocno]); - - if (accept_call_clobbered) - COPY_HARD_REG_SET (used, call_fixed_reg_set); - else if (reg_n_calls_crossed[allocno_reg[allocno]] == 0) - COPY_HARD_REG_SET (used, fixed_reg_set); - else - COPY_HARD_REG_SET (used, call_used_reg_set); - - /* Some registers should not be allocated in global-alloc. */ - IOR_HARD_REG_SET (used, no_global_alloc_regs); - - IOR_COMPL_HARD_REG_SET (used, reg_class_contents[(int) class]); - IOR_HARD_REG_SET (used, hard_reg_conflicts[allocno]); - if (frame_pointer_needed) - SET_HARD_REG_BIT (used, FRAME_POINTER_REGNUM); - - AND_COMPL_HARD_REG_SET (prefregs, used); - - /* Try to find a register from the preferred set first. */ - - i = -1; - for (prefreg = 0; prefreg < FIRST_PSEUDO_REGISTER; prefreg++) - if (TEST_HARD_REG_BIT (prefregs, prefreg) - && (losers == 0 || losers[prefreg] < 0) - && HARD_REGNO_MODE_OK (prefreg, mode)) - { - register int j; - register int lim = prefreg + HARD_REGNO_NREGS (prefreg, mode); - for (j = prefreg + 1; - (j < lim - && ! TEST_HARD_REG_BIT (used, j) - && (losers == 0 || losers[j] < 0)); - j++); - if (j == lim) - { - i = prefreg; - break; - } - } - -#if 0 - /* Otherwise try each hard reg to see if it fits. Do this in two passes. - In the first pass, skip registers that are prefered by some pseudo to - give it a better chance of getting one of those registers. Only if - we can't get a register when excluding those do we take one of them. */ - - /* This is turned off because it makes worse allocation on the 68020. */ - for (pass = 0; pass <= 1 && i < 0; pass++) -#endif - pass = 1; - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { -#ifdef REG_ALLOC_ORDER - int regno = reg_alloc_order[i]; -#else - int regno = i; -#endif - if (! TEST_HARD_REG_BIT (used, regno) - && (losers == 0 || losers[regno] < 0) - && (pass == 1 || ! TEST_HARD_REG_BIT (regs_someone_prefers, regno)) - && HARD_REGNO_MODE_OK (regno, mode)) - { - register int j; - register int lim = regno + HARD_REGNO_NREGS (regno, mode); - for (j = regno + 1; - (j < lim - && ! TEST_HARD_REG_BIT (used, j) - && (losers == 0 || losers[j] < 0)); - j++); - if (j == lim) - { - i = regno; - break; - } -#ifndef REG_ALLOC_ORDER - i = j; /* Skip starting points we know will lose */ -#endif - } - } - - /* Did we find a register? */ - - if (i < FIRST_PSEUDO_REGISTER) - { - register int lim, j; - HARD_REG_SET this_reg; - - /* Yes. Record it as the hard register of this pseudo-reg. */ - reg_renumber[allocno_reg[allocno]] = i; - /* For each other pseudo-reg conflicting with this one, - mark it as conflicting with the hard regs this one occupies. */ - CLEAR_HARD_REG_SET (this_reg); - lim = i + HARD_REGNO_NREGS (i, mode); - for (j = i; j < lim; j++) - SET_HARD_REG_BIT (this_reg, j); - lim = allocno; - for (j = 0; j < max_allocno; j++) - if (CONFLICTP (lim, j) || CONFLICTP (j, lim)) - { - IOR_HARD_REG_SET (hard_reg_conflicts[j], this_reg); - } - } - else if (flag_caller_saves) - { - /* Did not find a register. If it would be profitable to - allocate a call-clobbered register and save and restore it - around calls, do that. */ - if (! accept_call_clobbered - && reg_n_calls_crossed[allocno_reg[allocno]] != 0 - && CALLER_SAVE_PROFITABLE (reg_n_refs[allocno_reg[allocno]], - reg_n_calls_crossed[allocno_reg[allocno]])) - { - find_reg (allocno, losers, all_regs_p, 1, prefregs); - if (reg_renumber[allocno_reg[allocno]] >= 0) - caller_save_needed = 1; - } - } -} - -/* Called from `reload' to look for a hard reg to put pseudo reg REGNO in. - Perhaps it had previously seemed not worth a hard reg, - or perhaps its old hard reg has been commandeered for reloads. - FORBIDDEN_REGS is a vector that indicates certain hard regs - that may not be used, even if they do not appear to be allocated. - A nonnegative element means the corresponding hard reg is forbidden. - If FORBIDDEN_REGS is zero, no regs are forbidden. */ - -void -retry_global_alloc (regno, forbidden_regs) - int regno; - short *forbidden_regs; -{ - int allocno = reg_allocno[regno]; - if (allocno >= 0) - { - /* If we have more than one register class, - first try allocating in the class that is cheapest - for this pseudo-reg. If that fails, try any reg. */ - if (N_REG_CLASSES > 1) - find_reg (allocno, forbidden_regs, 0, 0, - hard_reg_preferences[allocno]); - if (reg_renumber[regno] < 0 - && !reg_preferred_or_nothing (regno)) - find_reg (allocno, forbidden_regs, 1, 0, - hard_reg_preferences[allocno]); - } -} - -/* Called from reload pass to see if current function's pseudo regs - require a frame pointer to be allocated and set up. - - Return 1 if so, 0 otherwise. - We may alter the hard-reg allocation of the pseudo regs - in order to make the frame pointer unnecessary. - However, if the value is 1, nothing has been altered. - - Args grant access to some tables used in reload1.c. - See there for info on them. */ - -int -check_frame_pointer_required (reg_equiv_constant, reg_equiv_mem, reg_equiv_address) - rtx *reg_equiv_constant, *reg_equiv_mem, *reg_equiv_address; -{ - register int i; - HARD_REG_SET *old_hard_reg_conflicts; - short *old_reg_renumber; - char old_regs_ever_live[FIRST_PSEUDO_REGISTER]; - - /* If any pseudo reg has no hard reg and no equivalent, - we must have a frame pointer. */ - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (reg_renumber[i] < 0 && reg_n_refs[i] > 0 - && reg_equiv_mem[i] == 0 && reg_equiv_constant[i] == 0 - && reg_equiv_address[i] == 0) - return 1; - - /* If we might not need a frame pointer, - try finding a hard reg for any pseudo that has a memory equivalent. - That is because the memory equivalent probably refers to a frame - pointer. */ - - old_reg_renumber = (short *) alloca (max_regno * sizeof (short)); - old_hard_reg_conflicts = (HARD_REG_SET *) - alloca (max_allocno * sizeof (HARD_REG_SET)); - - bcopy (reg_renumber, old_reg_renumber, max_regno * sizeof (short)); - bcopy (hard_reg_conflicts, old_hard_reg_conflicts, - max_allocno * sizeof (HARD_REG_SET)); - bcopy (regs_ever_live, old_regs_ever_live, sizeof regs_ever_live); - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (reg_renumber[i] < 0 - && ((reg_equiv_mem[i] - && reg_mentioned_p (frame_pointer_rtx, reg_equiv_mem[i])) - || (reg_equiv_address[i] - && reg_mentioned_p (frame_pointer_rtx, reg_equiv_address[i])))) - { - retry_global_alloc (i, 0); - /* If we can't find a hard reg for ALL of them, - or if a previously unneeded hard reg is used that requires saving, - we fail: set all those pseudos back as they were. */ - if (reg_renumber[i] < 0 - || (! old_regs_ever_live[reg_renumber[i]] - && ! call_used_regs[reg_renumber[i]])) - { - bcopy (old_reg_renumber, reg_renumber, - max_regno * sizeof (short)); - bcopy (old_hard_reg_conflicts, hard_reg_conflicts, - max_allocno * sizeof (HARD_REG_SET)); - bcopy (old_regs_ever_live, regs_ever_live, sizeof regs_ever_live); - return 1; - } - mark_home_live (i); - } - - return 0; -} - -/* Record a conflict between register REGNO - and everything currently live. - REGNO must not be a pseudo reg that was allocated - by local_alloc; such numbers must be translated through - reg_renumber before calling here. */ - -static void -record_one_conflict (regno) - int regno; -{ - register int j; - - if (regno < FIRST_PSEUDO_REGISTER) - /* When a hard register becomes live, - record conflicts with live pseudo regs. */ - for (j = 0; j < max_allocno; j++) - { - if (ALLOCNO_LIVE_P (j)) - SET_HARD_REG_BIT (hard_reg_conflicts[j], regno); - } - else - /* When a pseudo-register becomes live, - record conflicts first with hard regs, - then with other pseudo regs. */ - { - register int ialloc = reg_allocno[regno]; - register int ialloc_prod = ialloc * allocno_row_words; - IOR_HARD_REG_SET (hard_reg_conflicts[ialloc], hard_regs_live); - for (j = allocno_row_words - 1; j >= 0; j--) - conflicts[ialloc_prod + j] |= allocnos_live[j]; - } -} - -/* Record all allocnos currently live as conflicting - with each other and with all hard regs currently live. - ALLOCNO_VEC is a vector of LEN allocnos, all allocnos that - are currently live. Their bits are also flagged in allocnos_live. */ - -static void -record_conflicts (allocno_vec, len) - register short *allocno_vec; - register int len; -{ - register int allocno; - register int j; - register int ialloc_prod; - - while (--len >= 0) - { - allocno = allocno_vec[len]; - ialloc_prod = allocno * allocno_row_words; - IOR_HARD_REG_SET (hard_reg_conflicts[allocno], hard_regs_live); - for (j = allocno_row_words - 1; j >= 0; j--) - conflicts[ialloc_prod + j] |= allocnos_live[j]; - } -} - -/* Handle the case where REG is set by the insn being scanned, - during the forward scan to accumulate conflicts. - Store a 1 in regs_live or allocnos_live for this register, record how many - consecutive hardware registers it actually needs, - and record a conflict with all other registers already live. - - Note that even if REG does not remain alive after this insn, - we must mark it here as live, to ensure a conflict between - REG and any other regs set in this insn that really do live. - This is because those other regs could be considered after this. - - REG might actually be something other than a register; - if so, we do nothing. - - CLOBBERs are processed here by calling mark_reg_clobber. */ - -static void -mark_reg_store (orig_reg, setter) - rtx orig_reg, setter; -{ - register int regno; - register rtx reg = orig_reg; - - /* WORD is which word of a multi-register group is being stored. - For the case where the store is actually into a SUBREG of REG. - Except we don't use it; I believe the entire REG needs to be - made live. */ - int word = 0; - - if (GET_CODE (reg) == SUBREG) - { - word = SUBREG_WORD (reg); - reg = SUBREG_REG (reg); - } - - if (GET_CODE (reg) != REG) - return; - - if (GET_CODE (setter) != SET) - { - /* A clobber of a register should be processed here too. */ - mark_reg_clobber (orig_reg, setter); - return; - } - - regs_set[n_regs_set++] = reg; - - set_preference (reg, SET_SRC (setter)); - - regno = REGNO (reg); - - if (reg_renumber[regno] >= 0) - regno = reg_renumber[regno] /* + word */; - - /* Either this is one of the max_allocno pseudo regs not allocated, - or it is or has a hardware reg. First handle the pseudo-regs. */ - if (regno >= FIRST_PSEUDO_REGISTER) - { - if (reg_allocno[regno] >= 0) - { - SET_ALLOCNO_LIVE (reg_allocno[regno]); - record_one_conflict (regno); - } - } - /* Handle hardware regs (and pseudos allocated to hard regs). */ - else if (! fixed_regs[regno]) - { - register int last = regno + HARD_REGNO_NREGS (regno, GET_MODE (reg)); - while (regno < last) - { - record_one_conflict (regno); - SET_HARD_REG_BIT (hard_regs_live, regno); - regno++; - } - } -} - -/* Like mark_reg_set except notice just CLOBBERs; ignore SETs. */ - -static void -mark_reg_clobber (reg, setter) - rtx reg, setter; -{ - register int regno; - - /* WORD is which word of a multi-register group is being stored. - For the case where the store is actually into a SUBREG of REG. - Except we don't use it; I believe the entire REG needs to be - made live. */ - int word = 0; - - if (GET_CODE (setter) != CLOBBER) - return; - - if (GET_CODE (reg) == SUBREG) - { - word = SUBREG_WORD (reg); - reg = SUBREG_REG (reg); - } - - if (GET_CODE (reg) != REG) - return; - - regs_set[n_regs_set++] = reg; - - regno = REGNO (reg); - - if (reg_renumber[regno] >= 0) - regno = reg_renumber[regno] /* + word */; - - /* Either this is one of the max_allocno pseudo regs not allocated, - or it is or has a hardware reg. First handle the pseudo-regs. */ - if (regno >= FIRST_PSEUDO_REGISTER) - { - if (reg_allocno[regno] >= 0) - { - SET_ALLOCNO_LIVE (reg_allocno[regno]); - record_one_conflict (regno); - } - } - /* Handle hardware regs (and pseudos allocated to hard regs). */ - else if (! fixed_regs[regno]) - { - register int last = regno + HARD_REGNO_NREGS (regno, GET_MODE (reg)); - while (regno < last) - { - record_one_conflict (regno); - SET_HARD_REG_BIT (hard_regs_live, regno); - regno++; - } - } -} - -/* Mark REG as being dead (following the insn being scanned now). - Store a 0 in regs_live or allocnos_live for this register. */ - -static void -mark_reg_death (reg) - rtx reg; -{ - register int regno = REGNO (reg); - - /* For pseudo reg, see if it has been assigned a hardware reg. */ - if (reg_renumber[regno] >= 0) - regno = reg_renumber[regno]; - - /* Either this is one of the max_allocno pseudo regs not allocated, - or it is a hardware reg. First handle the pseudo-regs. */ - if (regno >= FIRST_PSEUDO_REGISTER) - { - if (reg_allocno[regno] >= 0) - CLEAR_ALLOCNO_LIVE (reg_allocno[regno]); - } - /* Handle hardware regs (and pseudos allocated to hard regs). */ - else if (! fixed_regs[regno]) - { - /* Pseudo regs already assigned hardware regs are treated - almost the same as explicit hardware regs. */ - register int last = regno + HARD_REGNO_NREGS (regno, GET_MODE (reg)); - while (regno < last) - { - CLEAR_HARD_REG_BIT (hard_regs_live, regno); - regno++; - } - } -} - -/* Mark hard reg REGNO as currently live, assuming machine mode MODE - for the value stored in it. MODE determines how many consecutive - registers are actually in use. Do not record conflicts; - it is assumed that the caller will do that. */ - -static void -mark_reg_live_nc (regno, mode) - register int regno; - enum machine_mode mode; -{ - register int last = regno + HARD_REGNO_NREGS (regno, mode); - while (regno < last) - { - SET_HARD_REG_BIT (hard_regs_live, regno); - regno++; - } -} - -/* Try to set a preference for an allocno to a hard register. - We are passed DEST and SRC which are the operands of a SET. It is known - that SRC is a register. If SRC or the first operand of SRC is a register, - try to set a preference. If one of the two is a hard register and the other - is a pseudo-register, mark the preference. - - Note that we are not as agressive as local-alloc in trying to tie a - pseudo-register to a hard register. */ - -static void -set_preference (dest, src) - rtx dest, src; -{ - int src_regno, dest_regno; - /* Amount to add to the hard regno for SRC, or subtract from that for DEST, - to compensate for subregs in SRC or DEST. */ - int offset = 0; - - if (GET_RTX_FORMAT (GET_CODE (src))[0] == 'e') - src = XEXP (src, 0); - - /* Get the reg number for both SRC and DEST. - If neither is a reg, give up. */ - - if (GET_CODE (src) == REG) - src_regno = REGNO (src); - else if (GET_CODE (src) == SUBREG && GET_CODE (SUBREG_REG (src)) == REG) - { - src_regno = REGNO (SUBREG_REG (src)); - offset += SUBREG_WORD (src); - } - else - return; - - if (GET_CODE (dest) == REG) - dest_regno = REGNO (dest); - else if (GET_CODE (dest) == SUBREG && GET_CODE (SUBREG_REG (dest)) == REG) - { - dest_regno = REGNO (SUBREG_REG (dest)); - offset -= SUBREG_WORD (dest); - } - else - return; - - /* Convert either or both to hard reg numbers. */ - - if (reg_renumber[src_regno] >= 0) - src_regno = reg_renumber[src_regno]; - - if (reg_renumber[dest_regno] >= 0) - dest_regno = reg_renumber[dest_regno]; - - /* Now if one is a hard reg and the other is a global pseudo - then give the other a preference. */ - - if (dest_regno < FIRST_PSEUDO_REGISTER && src_regno >= FIRST_PSEUDO_REGISTER - && reg_allocno[src_regno] >= 0) - { - dest_regno -= offset; - if (dest_regno >= 0 && dest_regno < FIRST_PSEUDO_REGISTER) - { - SET_REGBIT (hard_reg_preferences, - reg_allocno[src_regno], dest_regno); - SET_HARD_REG_BIT (regs_someone_prefers, dest_regno); - } - } - - if (src_regno < FIRST_PSEUDO_REGISTER && dest_regno >= FIRST_PSEUDO_REGISTER - && reg_allocno[dest_regno] >= 0) - { - src_regno += offset; - if (src_regno >= 0 && src_regno < FIRST_PSEUDO_REGISTER) - { - SET_REGBIT (hard_reg_preferences, - reg_allocno[dest_regno], src_regno); - SET_HARD_REG_BIT (regs_someone_prefers, src_regno); - } - } -} - -/* Print debugging trace information if -greg switch is given, - showing the information on which the allocation decisions are based. */ - -static void -dump_conflicts (file) - FILE *file; -{ - register int i; - fprintf (file, ";; %d regs to allocate:", max_allocno); - for (i = 0; i < max_allocno; i++) - { - fprintf (file, " %d", allocno_reg[allocno_order[i]]); - if (allocno_size[allocno_order[i]] != 1) - fprintf (file, " (%d)", allocno_size[allocno_order[i]]); - } - fprintf (file, "\n"); - - for (i = 0; i < max_allocno; i++) - { - register int j; - fprintf (file, ";; %d conflicts:", allocno_reg[i]); - for (j = 0; j < max_allocno; j++) - if (CONFLICTP (i, j) || CONFLICTP (j, i)) - fprintf (file, " %d", allocno_reg[j]); - for (j = 0; j < FIRST_PSEUDO_REGISTER; j++) - if (TEST_HARD_REG_BIT (hard_reg_conflicts[i], j)) - fprintf (file, " %d", j); - fprintf (file, "\n"); - } - fprintf (file, "\n"); -} - -void -dump_global_regs (file) - FILE *file; -{ - register int i; - - fprintf (file, ";; Register dispositions:"); - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - { - if (reg_renumber[i] >= 0) - fprintf (file, " %d in %d ", i, reg_renumber[i]); - } - - fprintf (file, "\n\n;; Hard regs used: "); - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (regs_ever_live[i]) - fprintf (file, " %d", i); - fprintf (file, "\n\n"); -} diff --git a/gnu/usr.bin/gcc1/cc1/gstdarg.h b/gnu/usr.bin/gcc1/cc1/gstdarg.h deleted file mode 100644 index 3fdba71417..0000000000 --- a/gnu/usr.bin/gcc1/cc1/gstdarg.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef _STDARG_H -#define _STDARG_H - -/* The macro _VA_LIST_ is the same thing used by this file in Ultrix. */ -#ifndef _VA_LIST_ -#define _VA_LIST_ -typedef char *va_list; -#endif - -/* Amount of space required in an argument list for an arg of type TYPE. - TYPE may alternatively be an expression whose type is used. */ - -#define __va_rounded_size(TYPE) \ - (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int)) - -#ifndef __sparc__ -#define va_start(AP, LASTARG) \ - (AP = ((char *) __builtin_next_arg ())) -#else -#define va_start(AP, LASTARG) \ - (__builtin_saveregs (), \ - AP = ((char *) __builtin_next_arg ())) -#endif - -void va_end (va_list); /* Defined in gnulib */ -#define va_end(AP) - -#ifdef __mips__ -#define va_arg(AP, mode) ((mode *)(AP = \ - (char *) (sizeof(mode) > 4 ? ((int)AP + 2*8 - 1) & -8 \ - : ((int)AP + 2*4 - 1) & -4)))[-1] -#else /* not __mips__ */ -#define va_arg(AP, TYPE) \ - (*((TYPE *) (AP += __va_rounded_size (TYPE), \ - AP - (sizeof (TYPE) < 4 ? sizeof (TYPE) \ - : __va_rounded_size (TYPE))))) -#endif /* not __mips__ */ - -#endif /* _STDARG_H */ diff --git a/gnu/usr.bin/gcc1/cc1/gvarargs.h b/gnu/usr.bin/gcc1/cc1/gvarargs.h deleted file mode 100644 index dc9c4efc8a..0000000000 --- a/gnu/usr.bin/gcc1/cc1/gvarargs.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef __GNUC__ -/* Use the system's macros with the system's compiler. */ -#include -#else -/* Record that varargs.h is defined; this turns off stdarg.h. */ - -#ifndef _VARARGS_H -#define _VARARGS_H - -#ifdef __sparc__ -#include "va-sparc.h" -#else -#ifdef __spur__ -#include "va-spur.h" -#else -#ifdef __mips__ -#include "va-mips.h" -#else -#ifdef __i860__ -#include "va-i860.h" -#else -#ifdef __pyr__ -#include "va-pyr.h" -#else - -#ifdef __NeXT__ - -/* On Next, erase any vestiges of stdarg.h. */ - -#undef va_alist -#undef va_dcl -#undef va_list -#undef va_start -#undef va_end -#undef __va_rounded_size -#undef va_arg -#endif /* __NeXT__ */ - -/* These macros implement traditional (non-ANSI) varargs - for GNU C. */ - -#define va_alist __builtin_va_alist -#define va_dcl int __builtin_va_alist; -#define va_list char * - -#ifdef __sparc__ -#define va_start(AP) \ - (__builtin_saveregs (), \ - AP = ((void *) &__builtin_va_alist)) -#else -#define va_start(AP) AP=(char *) &__builtin_va_alist -#endif -#define va_end(AP) - -#define __va_rounded_size(TYPE) \ - (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int)) - -#define va_arg(AP, TYPE) \ - (*((TYPE *) (AP += __va_rounded_size (TYPE), \ - AP - __va_rounded_size (TYPE)))) - -#endif /* not pyr */ -#endif /* not i860 */ -#endif /* not mips */ -#endif /* not spur */ -#endif /* not sparc */ -#endif /* not _VARARGS_H */ -#endif /* __GNUC__ */ diff --git a/gnu/usr.bin/gcc1/cc1/hard-reg-set.h b/gnu/usr.bin/gcc1/cc1/hard-reg-set.h deleted file mode 100644 index ad011146f8..0000000000 --- a/gnu/usr.bin/gcc1/cc1/hard-reg-set.h +++ /dev/null @@ -1,229 +0,0 @@ -/* Sets (bit vectors) of hard registers, and operations on them. - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* Define the type of a set of hard registers. */ - -/* If HARD_REG_SET is a macro, its definition is a scalar type - that has enough bits for all the target machine's hard registers. - Otherwise, it is a typedef for a suitable array of longs, - and HARD_REG_SET_LONGS is how many. */ - -#if FIRST_PSEUDO_REGISTER <= HOST_BITS_PER_CHAR -#define HARD_REG_SET char -#else -#if FIRST_PSEUDO_REGISTER <= HOST_BITS_PER_SHORT -#define HARD_REG_SET short -#else -#if FIRST_PSEUDO_REGISTER <= HOST_BITS_PER_INT -#define HARD_REG_SET int -#else -#if FIRST_PSEUDO_REGISTER <= HOST_BITS_PER_LONG -#define HARD_REG_SET long -#else -#define HARD_REG_SET_LONGS \ - ((FIRST_PSEUDO_REGISTER + HOST_BITS_PER_LONG - 1) / HOST_BITS_PER_LONG) -typedef long HARD_REG_SET[HARD_REG_SET_LONGS]; -#endif -#endif -#endif -#endif - -/* Define macros SET_HARD_REG_BIT, CLEAR_HARD_REG_BIT and TEST_HARD_REG_BIT - to set, clear or test one bit in a hard reg set of type HARD_REG_SET. - All three take two arguments: the set and the register number. - - In the case where sets are arrays of longs, the first argument - is actually a pointer to a long. - - Define two macros for initializing a set: - CLEAR_HARD_REG_SET and SET_HARD_REG_SET. - These take just one argument. - - Also define macros for copying hard reg sets: - COPY_HARD_REG_SET and COMPL_HARD_REG_SET. - These take two arguments TO and FROM; they read from FROM - and store into TO. COMPL_HARD_REG_SET complements each bit. - - Also define macros for combining hard reg sets: - IOR_HARD_REG_SET and AND_HARD_REG_SET. - These take two arguments TO and FROM; they read from FROM - and combine bitwise into TO. Define also two variants - IOR_COMPL_HARD_REG_SET and AND_COMPL_HARD_REG_SET - which use the complement of the set FROM. - - Also define GO_IF_HARD_REG_SUBSET (X, Y, TO): - if X is a subset of Y, go to TO. -*/ - -#ifdef HARD_REG_SET - -#define SET_HARD_REG_BIT(SET, BIT) \ - ((SET) |= 1 << (BIT)) -#define CLEAR_HARD_REG_BIT(SET, BIT) \ - ((SET) &= ~(1 << (BIT))) -#define TEST_HARD_REG_BIT(SET, BIT) \ - ((SET) & (1 << (BIT))) - -#define CLEAR_HARD_REG_SET(TO) ((TO) = 0) -#define SET_HARD_REG_SET(TO) ((TO) = -1) - -#define COPY_HARD_REG_SET(TO, FROM) ((TO) = (FROM)) -#define COMPL_HARD_REG_SET(TO, FROM) ((TO) = ~(FROM)) - -#define IOR_HARD_REG_SET(TO, FROM) ((TO) |= (FROM)) -#define IOR_COMPL_HARD_REG_SET(TO, FROM) ((TO) |= ~ (FROM)) -#define AND_HARD_REG_SET(TO, FROM) ((TO) &= (FROM)) -#define AND_COMPL_HARD_REG_SET(TO, FROM) ((TO) &= ~ (FROM)) - -#define GO_IF_HARD_REG_SUBSET(X,Y,TO) if (0 == ((X) & ~(Y))) goto TO -#else - -#define SET_HARD_REG_BIT(SET, BIT) \ - ((SET)[(BIT) / HOST_BITS_PER_LONG] |= 1 << ((BIT) % HOST_BITS_PER_LONG)) -#define CLEAR_HARD_REG_BIT(SET, BIT) \ - ((SET)[(BIT) / HOST_BITS_PER_LONG] &= ~(1 << ((BIT) % HOST_BITS_PER_LONG))) -#define TEST_HARD_REG_BIT(SET, BIT) \ - ((SET)[(BIT) / HOST_BITS_PER_LONG] & (1 << ((BIT) % HOST_BITS_PER_LONG))) - -#define CLEAR_HARD_REG_SET(TO) \ -do { register long *scan_tp_ = (TO); \ - register int i; \ - for (i = 0; i < HARD_REG_SET_LONGS; i++) \ - *scan_tp_++ = 0; } while (0) - -#define SET_HARD_REG_SET(TO) \ -do { register long *scan_tp_ = (TO); \ - register int i; \ - for (i = 0; i < HARD_REG_SET_LONGS; i++) \ - *scan_tp_++ = -1; } while (0) - -#define COPY_HARD_REG_SET(TO, FROM) \ -do { register long *scan_tp_ = (TO), *scan_fp_ = (FROM); \ - register int i; \ - for (i = 0; i < HARD_REG_SET_LONGS; i++) \ - *scan_tp_++ = *scan_fp_++; } while (0) - -#define COMPL_HARD_REG_SET(TO, FROM) \ -do { register long *scan_tp_ = (TO), *scan_fp_ = (FROM); \ - register int i; \ - for (i = 0; i < HARD_REG_SET_LONGS; i++) \ - *scan_tp_++ = ~ *scan_fp_++; } while (0) - -#define AND_HARD_REG_SET(TO, FROM) \ -do { register long *scan_tp_ = (TO), *scan_fp_ = (FROM); \ - register int i; \ - for (i = 0; i < HARD_REG_SET_LONGS; i++) \ - *scan_tp_++ &= *scan_fp_++; } while (0) - -#define AND_COMPL_HARD_REG_SET(TO, FROM) \ -do { register long *scan_tp_ = (TO), *scan_fp_ = (FROM); \ - register int i; \ - for (i = 0; i < HARD_REG_SET_LONGS; i++) \ - *scan_tp_++ &= ~ *scan_fp_++; } while (0) - -#define IOR_HARD_REG_SET(TO, FROM) \ -do { register long *scan_tp_ = (TO), *scan_fp_ = (FROM); \ - register int i; \ - for (i = 0; i < HARD_REG_SET_LONGS; i++) \ - *scan_tp_++ |= *scan_fp_++; } while (0) - -#define IOR_COMPL_HARD_REG_SET(TO, FROM) \ -do { register long *scan_tp_ = (TO), *scan_fp_ = (FROM); \ - register int i; \ - for (i = 0; i < HARD_REG_SET_LONGS; i++) \ - *scan_tp_++ |= ~ *scan_fp_++; } while (0) - -#define GO_IF_HARD_REG_SUBSET(X,Y,TO) \ -do { register long *scan_xp_ = (X), *scan_yp_ = (Y); \ - register int i; \ - for (i = 0; i < HARD_REG_SET_LONGS; i++) \ - if (0 != (*scan_xp_++ & ~*scan_yp_++)) break; \ - if (i == HARD_REG_SET_LONGS) goto TO; } while (0) - -#endif - -/* Define some standard sets of registers. */ - -/* Indexed by hard register number, contains 1 for registers - that are fixed use (stack pointer, pc, frame pointer, etc.). - These are the registers that cannot be used to allocate - a pseudo reg whose life does not cross calls. */ - -extern char fixed_regs[FIRST_PSEUDO_REGISTER]; - -/* The same info as a HARD_REG_SET. */ - -extern HARD_REG_SET fixed_reg_set; - -/* Indexed by hard register number, contains 1 for registers - that are fixed use or are clobbered by function calls. - These are the registers that cannot be used to allocate - a pseudo reg whose life crosses calls. */ - -extern char call_used_regs[FIRST_PSEUDO_REGISTER]; - -/* The same info as a HARD_REG_SET. */ - -extern HARD_REG_SET call_used_reg_set; - -/* Indexed by hard register number, contains 1 for registers that are - fixed use -- i.e. in fixed_regs -- or a function value return register - or STRUCT_VALUE_REGNUM or STATIC_CHAIN_REGNUM. These are the - registers that cannot hold quantities across calls even if we are - willing to save and restore them. */ - -extern char call_fixed_regs[FIRST_PSEUDO_REGISTER]; - -/* The same info as a HARD_REG_SET. */ - -extern HARD_REG_SET call_fixed_reg_set; - -/* Indexed by hard register number, contains 1 for registers - that are being used for global register decls. - These must be exempt from ordinary flow analysis - and are also considered fixed. */ - -extern char global_regs[FIRST_PSEUDO_REGISTER]; - -/* Table of register numbers in the order in which to try to use them. */ - -extern int reg_alloc_order[FIRST_PSEUDO_REGISTER]; - -/* For each reg class, a HARD_REG_SET saying which registers are in it. */ - -extern HARD_REG_SET reg_class_contents[]; - -/* For each reg class, number of regs it contains. */ - -extern int reg_class_size[N_REG_CLASSES]; - -/* For each reg class, table listing all the containing classes. */ - -extern enum reg_class reg_class_superclasses[N_REG_CLASSES][N_REG_CLASSES]; - -/* For each reg class, table listing all the classes contained in it. */ - -extern enum reg_class reg_class_subclasses[N_REG_CLASSES][N_REG_CLASSES]; - -/* For each pair of reg classes, - a largest reg class contained in their union. */ - -extern enum reg_class reg_class_subunion[N_REG_CLASSES][N_REG_CLASSES]; - diff --git a/gnu/usr.bin/gcc1/cc1/input.h b/gnu/usr.bin/gcc1/cc1/input.h deleted file mode 100644 index 270f267750..0000000000 --- a/gnu/usr.bin/gcc1/cc1/input.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Source file current line is coming from. */ -extern char *input_filename; - -/* Top-level source file. */ -extern char *main_input_filename; - -/* Line number in current source file. */ -extern int lineno; - -struct file_stack - { - char *name; - struct file_stack *next; - int line; - }; - -/* Stack of currently pending input files. - The line member is not accurate for the innermost file on the stack. */ -extern struct file_stack *input_file_stack; - -/* Incremented on each change to input_file_stack. */ -extern int input_file_stack_tick; diff --git a/gnu/usr.bin/gcc1/cc1/integrate.c b/gnu/usr.bin/gcc1/cc1/integrate.c deleted file mode 100644 index 00d93c595c..0000000000 --- a/gnu/usr.bin/gcc1/cc1/integrate.c +++ /dev/null @@ -1,2023 +0,0 @@ -/* Procedure integration for GNU CC. - Copyright (C) 1988 Free Software Foundation, Inc. - Contributed by Michael Tiemann (tiemann@mcc.com) - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include - -#include "config.h" -#include "rtl.h" -#include "tree.h" -#include "flags.h" -#include "insn-flags.h" -#include "expr.h" - -#include "obstack.h" -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free -extern int xmalloc (); -extern void free (); - -extern struct obstack permanent_obstack, maybepermanent_obstack; -extern struct obstack *rtl_obstack, *saveable_obstack, *current_obstack; - -extern rtx stack_slot_list; - -#define MIN(x,y) ((x < y) ? x : y) - -extern tree pushdecl (); -extern tree poplevel (); - -/* Default max number of insns a function can have and still be inline. - This is overridden on RISC machines. */ -#ifndef INTEGRATE_THRESHOLD -#define INTEGRATE_THRESHOLD(DECL) \ - (8 * (8 + list_length (DECL_ARGUMENTS (DECL)))) -#endif - -/* This is the target of the inline function being expanded, - or NULL if there is none. */ -static rtx inline_target; - -/* We must take special care not to disrupt life too severely - when performing procedure integration. One thing that that - involves is not creating illegitimate address which reload - cannot fix. Since we don't know what the frame pointer is - not capable of (in a machine independent way), we create - a pseudo-frame pointer which will have to do for now. */ -static rtx inline_fp_rtx; - -/* Convert old frame-pointer offsets to new. Parameters which only - produce values (no addresses, and are never assigned), map directly - to the pseudo-reg of the incoming value. Parameters that are - assigned to but do not have their address taken are given a fresh - pseudo-register. Parameters that have their address take are - given a fresh stack-slot. */ -static rtx *parm_map; - -/* ?? Should this be done here?? It is not right now. - Keep track of whether a given pseudo-register is the sum - of the frame pointer and a const_int (or zero). */ -static char *fp_addr_p; - -/* For the local variables of the procdure being integrated that live - on the frame, FRAME_POINTER_DELTA says how much to change their - offsets by, so that they now live in the correct place on the - frame of the function being compiled. */ -static int fp_delta; - -/* When an insn is being copied by copy_rtx_and_substitute, - this is nonzero if we have copied an ASM_OPERANDS. - In that case, it is the original input-operand vector. - Likewise in copy_for_inline. */ -static rtvec orig_asm_operands_vector; - -/* When an insn is being copied by copy_rtx_and_substitute, - this is nonzero if we have copied an ASM_OPERANDS. - In that case, it is the copied input-operand vector. - Likewise in copy_for_inline. */ -static rtvec copy_asm_operands_vector; - -/* Likewise, this is the copied constraints vector. */ -static rtvec copy_asm_constraints_vector; - -/* Return a copy of an rtx (as needed), substituting pseudo-register, - labels, and frame-pointer offsets as necessary. */ -static rtx copy_rtx_and_substitute (); -/* Variant, used for memory addresses that are not memory_address_p. */ -static rtx copy_address (); - -/* Return the rtx corresponding to a given index in the stack arguments. */ -static rtx access_parm_map (); - -static void copy_parm_decls (); -static void copy_decl_tree (); - -static rtx try_fold_cc0 (); - -/* We do some simple constant folding optimization. This optimization - really exists primarily to save time inlining a function. It - also helps users who ask for inline functions without -O. */ -static rtx fold_out_const_cc0 (); - -/* Zero if the current function (whose FUNCTION_DECL is FNDECL) - is safe and reasonable to integrate into other functions. - Nonzero means value is a warning message with a single %s - for the function's name. */ - -char * -function_cannot_inline_p (fndecl) - register tree fndecl; -{ - register rtx insn; - tree last = tree_last (TYPE_ARG_TYPES (TREE_TYPE (fndecl))); - int max_insns = INTEGRATE_THRESHOLD (fndecl); - register int ninsns = 0; - register tree parms; - - /* No inlines with varargs. `grokdeclarator' gives a warning - message about that if `inline' is specified. This code - it put in to catch the volunteers. */ - if (last && TREE_VALUE (last) != void_type_node) - return "varargs function cannot be inline"; - - if (current_function_calls_alloca) - return "function using alloca cannot be inline"; - - /* If its not even close, don't even look. */ - if (!TREE_INLINE (fndecl) && get_max_uid () > 3 * max_insns) - return "function too large to be inline"; - - /* We can't inline functions that return structures - the old-fashioned PCC way, copying into a static block. */ -#ifdef PCC_STATIC_STRUCT_RETURN - if (flag_pcc_struct_return - && (TYPE_MODE (TREE_TYPE (TREE_TYPE (fndecl))) == BLKmode - || RETURN_IN_MEMORY (TREE_TYPE (TREE_TYPE (fndecl))))) - return "inline functions not supported for this return value type"; -#endif - - /* Don't inline functions which have BLKmode arguments. - Don't inline functions that take the address of - a parameter and do not specify a function prototype. */ - for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms)) - { - if (TYPE_MODE (TREE_TYPE (parms)) == BLKmode) - return "function with large aggregate parameter cannot be inline"; - if (last == NULL_TREE && TREE_ADDRESSABLE (parms)) - return "no prototype, and parameter address used; cannot be inline"; - /* If an aggregate is thought of as "in memory" - then its components are referred to by narrower memory refs. - If the actual parameter is a reg, these refs can't be translated, - esp. since copy_rtx_and_substitute doesn't know whether it is - reading or writing. */ - if ((TREE_CODE (TREE_TYPE (parms)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (parms)) == UNION_TYPE) - && GET_CODE (DECL_RTL (parms)) == MEM) - return "address of an aggregate parameter is used; cannot be inline"; - } - - if (!TREE_INLINE (fndecl) && get_max_uid () > max_insns) - { - for (ninsns = 0, insn = get_first_nonparm_insn (); insn && ninsns < max_insns; - insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == INSN - || GET_CODE (insn) == JUMP_INSN - || GET_CODE (insn) == CALL_INSN) - ninsns++; - } - - if (ninsns >= max_insns) - return "function too large to be inline"; - } - - return 0; -} - -/* Variables used within save_for_inline. */ - -/* Mapping from old pesudo-register to new pseudo-registers. - The first element of this map is reg_map[FIRST_PSEUDO_REGISTER]. - It is allocated in `save_for_inline' and `expand_inline_function', - and deallocated on exit from each of those routines. */ -static rtx *reg_map; - -/* Mapping from old code-labels to new code-labels. - The first element of this map is label_map[min_labelno]. - It is allocated in `save_for_inline' and `expand_inline_function', - and deallocated on exit from each of those routines. */ -static rtx *label_map; - -/* Mapping from old insn uid's to copied insns. - It is allocated in `save_for_inline' and `expand_inline_function', - and deallocated on exit from each of those routines. */ -static rtx *insn_map; - -/* Map pseudo reg number into the PARM_DECL for the parm living in the reg. - Zero for a reg that isn't a parm's home. - Only reg numbers less than max_parm_reg are mapped here. */ -static tree *parmdecl_map; - -/* Keep track of first pseudo-register beyond those that are parms. */ -static int max_parm_reg; - -/* Offset from arg ptr to the first parm of this inline function. */ -static int first_parm_offset; - -/* On machines that perform a function return with a single - instruction, such as the VAX, these return insns must be - mapped into branch statements. */ -extern rtx return_label; - -/* Copy an rtx for save_for_inline. */ -static rtx copy_for_inline (); - -/* Make the insns and PARM_DECLs of the current function permanent - and record other information in DECL_SAVED_INSNS to allow inlining - of this function in subsequent calls. */ - -void -save_for_inline (fndecl) - tree fndecl; -{ - extern rtx *regno_reg_rtx; /* in emit-rtl.c. */ - extern current_function_args_size; - - rtx first_insn, last_insn, insn; - rtx head, copy; - tree parms; - int max_labelno, min_labelno, i, len; - int max_reg; - int max_uid; - - /* Make and emit a return-label if we have not already done so. */ - - if (return_label == 0) - { - return_label = gen_label_rtx (); - emit_label (return_label); - } - - /* Get some bounds on the labels and registers used. */ - - max_labelno = max_label_num (); - min_labelno = get_first_label_num (); - max_parm_reg = max_parm_reg_num (); - max_reg = max_reg_num (); - - /* Set up PARMDECL_MAP which maps pseudo-reg number to its PARM_DECL. - - Set TREE_VOLATILE to 0 if the parm is in a register, otherwise 1. - Later we set TREE_READONLY to 0 if the parm is modified inside the fn. */ - - parmdecl_map = (tree *) alloca (max_parm_reg * sizeof (tree)); - bzero (parmdecl_map, max_parm_reg * sizeof (tree)); - - for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms)) - { - rtx p = DECL_RTL (parms); - - if (GET_CODE (p) == REG) - { - parmdecl_map[REGNO (p)] = parms; - TREE_VOLATILE (parms) = 0; - } - else - TREE_VOLATILE (parms) = 1; - TREE_READONLY (parms) = 1; - } - - /* The list of DECL_SAVES_INSNS, starts off with a header which - contains the following information: - - the first insn of the function (not including the insns that copy - parameters into registers). - the first label used by that function, - the last label used by that function, - and the total number of registers used. */ - - head = gen_inline_header_rtx (NULL, NULL, min_labelno, max_labelno, - max_parm_reg, max_reg, - current_function_args_size, stack_slot_list); - max_uid = INSN_UID (head); - - /* We have now allocated all that needs to be allocated permanently - on the rtx obstack. Set our high-water mark, so that we - can free the rest of this when the time comes. */ - - preserve_data (); - - /* Copy the chain insns of this function. - Install the copied chain as the insns of this function, - for continued compilation; - the original chain is recorded as the DECL_SAVED_INSNS - for inlining future calls. */ - - /* If there are insns that copy parms from the stack into pseudo registers, - those insns are not copied. `expand_inline_function' must - emit the correct code to handle such things. */ - - insn = get_insns (); - if (GET_CODE (insn) != NOTE) - abort (); - first_insn = rtx_alloc (NOTE); - NOTE_SOURCE_FILE (first_insn) = NOTE_SOURCE_FILE (insn); - NOTE_LINE_NUMBER (first_insn) = NOTE_LINE_NUMBER (insn); - INSN_UID (first_insn) = INSN_UID (insn); - PREV_INSN (first_insn) = NULL; - NEXT_INSN (first_insn) = NULL; - last_insn = first_insn; - - /* Each pseudo-reg in the old insn chain must have a unique rtx in the copy. - Make these new rtx's now, and install them in regno_reg_rtx, so they - will be the official pseudo-reg rtx's for the rest of compilation. */ - - reg_map = (rtx *) alloca ((max_reg + 1) * sizeof (rtx)); - - len = sizeof (struct rtx_def) + (GET_RTX_LENGTH (REG) - 1) * sizeof (rtunion); - for (i = max_reg - 1; i >= FIRST_PSEUDO_REGISTER; i--) - reg_map[i] = (rtx)obstack_copy (&maybepermanent_obstack, regno_reg_rtx[i], len); - bcopy (reg_map + FIRST_PSEUDO_REGISTER, - regno_reg_rtx + FIRST_PSEUDO_REGISTER, - (max_reg - FIRST_PSEUDO_REGISTER) * sizeof (rtx)); - - /* Likewise each label rtx must have a unique rtx as its copy. */ - - label_map = (rtx *)alloca ((max_labelno - min_labelno) * sizeof (rtx)); - label_map -= min_labelno; - - for (i = min_labelno; i < max_labelno; i++) - label_map[i] = gen_label_rtx (); - - /* Record the mapping of old insns to copied insns. */ - - insn_map = (rtx *) alloca (max_uid * sizeof (rtx)); - bzero (insn_map, max_uid * sizeof (rtx)); - - /* Now copy the chain of insns. */ - - for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn)) - { - orig_asm_operands_vector = 0; - copy_asm_operands_vector = 0; - - switch (GET_CODE (insn)) - { - case NOTE: - /* No need to keep these. */ - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED) - continue; - - copy = rtx_alloc (NOTE); - NOTE_SOURCE_FILE (copy) = NOTE_SOURCE_FILE (insn); - NOTE_LINE_NUMBER (copy) = NOTE_LINE_NUMBER (insn); - break; - - case INSN: - case CALL_INSN: - case JUMP_INSN: - copy = rtx_alloc (GET_CODE (insn)); - PATTERN (copy) = copy_for_inline (PATTERN (insn)); - INSN_CODE (copy) = -1; - LOG_LINKS (copy) = NULL; - RTX_INTEGRATED_P (copy) = RTX_INTEGRATED_P (insn); - break; - - case CODE_LABEL: - copy = label_map[CODE_LABEL_NUMBER (insn)]; - break; - - case BARRIER: - copy = rtx_alloc (BARRIER); - break; - - default: - abort (); - } - INSN_UID (copy) = INSN_UID (insn); - insn_map[INSN_UID (insn)] = copy; - NEXT_INSN (last_insn) = copy; - PREV_INSN (copy) = last_insn; - last_insn = copy; - } - - /* Now copy the reg notes of the insns. - Do this now because there can be forward references. */ - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN - || GET_CODE (insn) == CALL_INSN) - { - rtx copy = insn_map[INSN_UID (insn)]; - REG_NOTES (copy) = copy_for_inline (REG_NOTES (insn)); - } - - NEXT_INSN (last_insn) = NULL; - - NEXT_INSN (head) = get_first_nonparm_insn (); - FIRST_PARM_INSN (head) = get_insns (); - DECL_SAVED_INSNS (fndecl) = head; - DECL_FRAME_SIZE (fndecl) = get_frame_size (); - TREE_INLINE (fndecl) = 1; - - parmdecl_map = 0; - label_map = 0; - reg_map = 0; - return_label = 0; - - set_new_first_and_last_insn (first_insn, last_insn); -} - -/* Copy the rtx ORIG recursively, replacing pseudo-regs and labels - according to `reg_map' and `label_map'. - All other kinds of rtx are copied except those that can never be - changed during compilation. */ - -static rtx -copy_for_inline (orig) - rtx orig; -{ - register rtx x = orig; - register int i; - register enum rtx_code code; - register char *format_ptr; - - if (x == 0) - return x; - - code = GET_CODE (x); - - /* These types may be freely shared. */ - - switch (code) - { - case QUEUED: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case PC: - case CC0: - return x; - - case ASM_OPERANDS: - /* If a single asm insn contains multiple output operands - then it contains multiple ASM_OPERANDS rtx's that share operand 3. - We must make sure that the copied insn continues to share it. */ - if (orig_asm_operands_vector == XVEC (orig, 3)) - { - x = rtx_alloc (ASM_OPERANDS); - XSTR (x, 0) = XSTR (orig, 0); - XSTR (x, 1) = XSTR (orig, 1); - XINT (x, 2) = XINT (orig, 2); - XVEC (x, 3) = copy_asm_operands_vector; - XVEC (x, 4) = copy_asm_constraints_vector; - XSTR (x, 5) = XSTR (orig, 5); - XINT (x, 6) = XINT (orig, 6); - return x; - } - break; - - case MEM: - /* A MEM is allowed to be shared if its address is constant - or is a constant plus one of the special registers. */ - if (CONSTANT_ADDRESS_P (XEXP (x, 0))) - return x; -#if 0 /* This is turned off because it is possible for - unshare_all_rtl to copy the address, into memory that won't be saved. - Although the MEM can safely be shared, and won't be copied there, - the address itself cannot be shared, and may need to be copied. */ - if (GET_CODE (XEXP (x, 0)) == PLUS - && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG - && (REGNO (XEXP (XEXP (x, 0), 0)) == FRAME_POINTER_REGNUM - || REGNO (XEXP (XEXP (x, 0), 0)) == ARG_POINTER_REGNUM) - && CONSTANT_ADDRESS_P (XEXP (XEXP (x, 0), 1))) -#if 0 - /* This statement was accidentally deleted in the remote past. - Reinsert it for 1.37. Don't take the risk now. */ - return x; -#endif - if (GET_CODE (XEXP (x, 0)) == REG - && (REGNO (XEXP (x, 0)) == FRAME_POINTER_REGNUM - || REGNO (XEXP (x, 0)) == ARG_POINTER_REGNUM) - && CONSTANT_ADDRESS_P (XEXP (x, 1))) - return x; -#endif /* 0 */ - break; - - case LABEL_REF: - { - /* Must point to the new insn. */ - return gen_rtx (LABEL_REF, GET_MODE (orig), - label_map[CODE_LABEL_NUMBER (XEXP (orig, 0))]); - } - - case REG: - if (REGNO (x) >= FIRST_PSEUDO_REGISTER) - return reg_map [REGNO (x)]; - else - return x; - - /* If a parm that gets modified lives in a pseudo-reg, - set its TREE_VOLATILE to prevent certain optimizations. */ - case SET: - { - rtx dest = SET_DEST (x); - - if (GET_CODE (dest) == REG - && REGNO (dest) < max_parm_reg - && REGNO (dest) >= FIRST_PSEUDO_REGISTER - && parmdecl_map[REGNO (dest)] != 0) - TREE_READONLY (parmdecl_map[REGNO (dest)]) = 0; - } - break; - } - - /* Replace this rtx with a copy of itself. */ - - x = rtx_alloc (code); - bcopy (orig, x, (sizeof (*x) - sizeof (x->fld) - + sizeof (x->fld[0]) * GET_RTX_LENGTH (code))); - - /* Now scan the subexpressions recursively. - We can store any replaced subexpressions directly into X - since we know X is not shared! Any vectors in X - must be copied if X was copied. */ - - format_ptr = GET_RTX_FORMAT (code); - - for (i = 0; i < GET_RTX_LENGTH (code); i++) - { - switch (*format_ptr++) - { - case 'e': - XEXP (x, i) = copy_for_inline (XEXP (x, i)); - break; - - case 'u': - /* Change any references to old-insns to point to the - corresponding copied insns. */ - XEXP (x, i) = insn_map[INSN_UID (XEXP (x, i))]; - break; - - case 'E': - if (XVEC (x, i) != NULL && XVECLEN (x, i) != 0) - { - register int j; - - XVEC (x, i) = gen_rtvec_v (XVECLEN (x, i), &XVECEXP (x, i, 0)); - for (j = 0; j < XVECLEN (x, i); j++) - XVECEXP (x, i, j) - = copy_for_inline (XVECEXP (x, i, j)); - } - break; - } - } - - if (code == ASM_OPERANDS && orig_asm_operands_vector == 0) - { - orig_asm_operands_vector = XVEC (orig, 3); - copy_asm_operands_vector = XVEC (x, 3); - copy_asm_constraints_vector = XVEC (x, 4); - } - - return x; -} - -/* Integrate the procedure defined by FNDECL. Note that this function - may wind up calling itself. Since the static variables are not - reentrant, we do not assign them until after the possibility - or recursion is eliminated. - - If IGNORE is nonzero, do not produce a value. - Otherwise store the value in TARGET if it is nonzero and that is convenient. - - Value is: - (rtx)-1 if we could not substitute the function - 0 if we substituted it and it does not produce a value - else an rtx for where the value is stored. */ - -rtx -expand_inline_function (fndecl, parms, target, ignore, type, structure_value_addr) - tree fndecl, parms; - rtx target; - int ignore; - tree type; - rtx structure_value_addr; -{ - tree formal, actual; - rtx header = DECL_SAVED_INSNS (fndecl); - rtx insns = FIRST_FUNCTION_INSN (header); - rtx parm_insns = FIRST_PARM_INSN (header); - rtx insn; - int max_regno = MAX_REGNUM (header) + 1; - register int i; - int min_labelno = FIRST_LABELNO (header); - int max_labelno = LAST_LABELNO (header); - int nargs; - rtx *arg_vec; - rtx local_return_label = 0; - rtx follows_call = 0; - rtx this_struct_value_rtx = 0; - /* List of tree_list nodes with parm as purpose and its index as value. */ - tree must_load_parms = 0; - - if (max_regno < FIRST_PSEUDO_REGISTER) - abort (); - - nargs = list_length (DECL_ARGUMENTS (fndecl)); - - /* We expect PARMS to have the right length; don't crash if not. */ - if (list_length (parms) != nargs) - return (rtx)-1; - /* Also check that the parms type match. Since the appropriate - conversions or default promotions have already been applied, - the machine modes should match exactly. */ - for (formal = DECL_ARGUMENTS (fndecl), - actual = parms; - formal; - formal = TREE_CHAIN (formal), - actual = TREE_CHAIN (actual)) - { - tree arg = TREE_VALUE (actual); - enum machine_mode mode = TYPE_MODE (DECL_ARG_TYPE (formal)); - if (mode != TYPE_MODE (TREE_TYPE (arg))) - return (rtx)-1; - /* If they are block mode, the types should match exactly. */ - if (mode == BLKmode && TREE_TYPE (arg) != TREE_TYPE (formal)) - return (rtx)-1; - } - - /* Make a binding contour to keep inline cleanups called at - outer function-scope level from looking like they are shadowing - parameter declarations. */ - pushlevel (0); - - /* Make a fresh binding contour that we can easily remove. */ - pushlevel (0); - expand_start_bindings (0); - if (GET_CODE (parm_insns) == NOTE - && NOTE_LINE_NUMBER (parm_insns) < 0) - emit_note (NOTE_SOURCE_FILE (parm_insns), NOTE_LINE_NUMBER (parm_insns)); - - /* Get all the actual args as RTL, and store them in ARG_VEC. */ - - arg_vec = (rtx *)alloca (nargs * sizeof (rtx)); - - for (formal = DECL_ARGUMENTS (fndecl), - actual = parms, - i = 0; - formal; - formal = TREE_CHAIN (formal), - actual = TREE_CHAIN (actual), - i++) - { - /* Actual parameter, already converted to DECL_ARG_TYPE (formal). */ - tree arg = TREE_VALUE (actual); - /* Mode of the value supplied. */ - enum machine_mode tmode = TYPE_MODE (DECL_ARG_TYPE (formal)); - /* Mode of the variable used within the function. */ - enum machine_mode imode = TYPE_MODE (TREE_TYPE (formal)); - rtx copy; - - emit_note (DECL_SOURCE_FILE (formal), DECL_SOURCE_LINE (formal)); - - /* Make a place to hold the argument value, still in mode TMODE, - and put it in COPY. */ - if (TREE_ADDRESSABLE (formal)) - { - int size = int_size_in_bytes (DECL_ARG_TYPE (formal)); - copy = assign_stack_local (tmode, size); - if (!memory_address_p (DECL_MODE (formal), XEXP (copy, 0))) - copy = change_address (copy, VOIDmode, copy_rtx (XEXP (copy, 0))); - store_expr (arg, copy, 0); - } - else if (! TREE_READONLY (formal) - || TREE_VOLATILE (formal)) - { - /* If parm is modified or if it hasn't a pseudo reg, - we may not simply substitute the actual value; - copy it through a register. */ - copy = gen_reg_rtx (tmode); - store_expr (arg, copy, 0); - } - else - { - copy = expand_expr (arg, 0, tmode, 0); - - /* We do not use CONSTANT_ADDRESS_P here because - the set of cases where that might make a difference - are a subset of the cases that arise even when - it is a CONSTANT_ADDRESS_P (i.e., fp_delta - gets into the act. */ - if (GET_CODE (copy) != REG && ! CONSTANT_P (copy)) - copy = copy_to_reg (copy); - } - /* If passed mode != nominal mode, COPY is now the passed mode. - Convert it to the nominal mode (i.e. truncate it). */ - if (tmode != imode) - copy = convert_to_mode (imode, copy, 0); - arg_vec[i] = copy; - } - - copy_parm_decls (DECL_ARGUMENTS (fndecl), arg_vec); - - /* Perform postincrements before actually calling the function. */ - emit_queue (); - - /* clean up stack so that variables might have smaller offsets. */ - do_pending_stack_adjust (); - - /* Pass the function the address in which to return a structure value. */ - if (structure_value_addr) - { - if (GET_CODE (structure_value_addr) == REG - && (struct_value_rtx == 0 || GET_CODE (struct_value_rtx) == MEM)) - this_struct_value_rtx = structure_value_addr; - else - this_struct_value_rtx = copy_to_mode_reg (Pmode, structure_value_addr); - } - - /* Now prepare for copying the insns. - Set up reg_map, parm_map and label_map saying how to translate - the pseudo-registers, stack-parm references and labels when copying. */ - - reg_map = (rtx *) alloca (max_regno * sizeof (rtx)); - bzero (reg_map, max_regno * sizeof (rtx)); - - parm_map = (rtx *)alloca ((FUNCTION_ARGS_SIZE (header) + UNITS_PER_WORD - 1) - / UNITS_PER_WORD * sizeof (rtx)); - bzero (parm_map, ((FUNCTION_ARGS_SIZE (header) + UNITS_PER_WORD - 1) - / UNITS_PER_WORD * sizeof (rtx))); - - /* Note that expand_expr (called above) can clobber first_parm_offset. */ - first_parm_offset = FIRST_PARM_OFFSET (fndecl); - parm_map -= first_parm_offset / UNITS_PER_WORD; - - if (DECL_ARGUMENTS (fndecl)) - { - tree decl = DECL_ARGUMENTS (fndecl); - - for (formal = decl, i = 0; formal; formal = TREE_CHAIN (formal), i++) - { - /* Create an entry in PARM_MAP that says what pseudo register - is associated with an address we might compute. */ - if (DECL_OFFSET (formal) >= 0) - { - /* This parameter has a home in the stack. */ - parm_map[DECL_OFFSET (formal) / BITS_PER_WORD] = arg_vec[i]; - } - else - { - /* Parameter that was passed in a register; - does it have a home on the stack (as a local)? */ - rtx frtx = DECL_RTL (formal); - rtx offset = 0; - if (GET_CODE (frtx) == MEM) - { - frtx = XEXP (frtx, 0); - if (GET_CODE (frtx) == PLUS) - { - if (XEXP (frtx, 0) == frame_pointer_rtx - && GET_CODE (XEXP (frtx, 1)) == CONST_INT) - offset = XEXP (frtx, 1); - else if (XEXP (frtx, 1) == frame_pointer_rtx - && GET_CODE (XEXP (frtx, 0)) == CONST_INT) - offset = XEXP (frtx, 0); -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - /* If there is a separate arg pointer - and REG_PARM_STACK_SPACE is defined, - parms passed in regs can be copied - to slots reached via the arg pointer. */ - if (XEXP (frtx, 0) == arg_pointer_rtx - && GET_CODE (XEXP (frtx, 1)) == CONST_INT) - offset = XEXP (frtx, 1); - else if (XEXP (frtx, 1) == arg_pointer_rtx - && GET_CODE (XEXP (frtx, 0)) == CONST_INT) - offset = XEXP (frtx, 0); -#endif - } - if (offset && INTVAL (offset) >= first_parm_offset) - parm_map[INTVAL (offset) / UNITS_PER_WORD] = arg_vec[i]; - else if (offset) - must_load_parms - = tree_cons (formal, build_int_2 (i, 0), - must_load_parms); - else if (TREE_TYPE (formal) != error_mark_node) - abort (); - } - else if (GET_CODE (frtx) != REG) - abort (); - } - /* Create an entry in REG_MAP that says what rtx is associated - with a pseudo register from the function being inlined. */ - if (GET_CODE (DECL_RTL (formal)) == REG) - reg_map[REGNO (DECL_RTL (formal))] = arg_vec[i]; - } - } - -#if 0 /* This was turned off when it was written, - because expand_call was changed not to need it. */ - /* Handle the case where our caller offers a register target - but the called function wants to return the value in memory. */ - if (this_struct_value_rtx == 0 - && aggregate_value_p (DECL_RESULT (fndecl))) - { - enum machine_mode mode1 = GET_MODE (DECL_RTL (DECL_RESULT (fndecl))); - this_struct_value_rtx - = assign_stack_local (mode1, GET_MODE_SIZE (mode1)); - target = 0; - } -#endif - - /* Make certain that we can accept struct_value_{incoming_rtx,rtx}, - and map it. */ - if (this_struct_value_rtx == 0) - ; - else if (GET_CODE (struct_value_incoming_rtx) == REG) - reg_map[REGNO (XEXP (DECL_RTL (DECL_RESULT (fndecl)), 0))] - = this_struct_value_rtx; - else if (GET_CODE (struct_value_incoming_rtx) == MEM - && XEXP (XEXP (struct_value_incoming_rtx, 0), 0) == frame_pointer_rtx - && GET_CODE (XEXP (XEXP (struct_value_incoming_rtx, 0), 1)) == CONST_INT) - reg_map[REGNO (XEXP (DECL_RTL (DECL_RESULT (fndecl)), 0))] - = this_struct_value_rtx; -#if 0 - parm_map[INTVAL (XEXP (XEXP (struct_value_incoming_rtx, 0), 1)) / UNITS_PER_WORD] - = this_struct_value_rtx; -#endif - else - abort (); - - label_map = (rtx *)alloca ((max_labelno - min_labelno) * sizeof (rtx)); - label_map -= min_labelno; - - for (i = min_labelno; i < max_labelno; i++) - label_map[i] = gen_label_rtx (); - - /* As we copy insns, record the correspondence, so that inter-insn - references can be copied into isomorphic structure. */ - - insn_map = (rtx *) alloca (INSN_UID (header) * sizeof (rtx)); - bzero (insn_map, INSN_UID (header) * sizeof (rtx)); - - /* Set up a target to translate the inline function's value-register. */ - - if (this_struct_value_rtx != 0 || TYPE_MODE (type) == VOIDmode) - inline_target = 0; - else - { - /* Machine mode function was declared to return. */ - enum machine_mode departing_mode = TYPE_MODE (type); - /* (Possibly wider) machine mode it actually computes - (for the sake of callers that fail to declare it right). */ - enum machine_mode arriving_mode - = TYPE_MODE (DECL_RESULT_TYPE (fndecl)); - - /* Don't use MEMs as direct targets because on some machines - substituting a MEM for a REG makes invalid insns. - Let the combiner substitute the MEM if that is valid. */ - if (target && GET_CODE (target) == REG - && GET_MODE (target) == departing_mode) - inline_target = target; - else - inline_target = target = gen_reg_rtx (departing_mode); - - /* If function's value was promoted before return, - avoid machine mode mismatch when we substitute INLINE_TARGET. - But TARGET is what we will return to the caller. */ - if (arriving_mode != departing_mode) - inline_target = gen_rtx (SUBREG, arriving_mode, target, 0); - } - - /* Make space in current function's stack frame - for the stack frame of the inline function. - Adjust all frame-pointer references by the difference - between the offset to this space - and the offset to the equivalent space in the inline - function's frame. - This difference equals the size of preexisting locals. */ - - fp_delta = get_frame_size (); -#ifdef FRAME_GROWS_DOWNWARD - fp_delta = - fp_delta; -#endif - - inline_fp_rtx - = copy_to_mode_reg (Pmode, - plus_constant (frame_pointer_rtx, fp_delta)); - - /* Now allocate the space for that to point at. */ - - assign_stack_local (VOIDmode, DECL_FRAME_SIZE (fndecl)); - - /* Load any parms represented as locals with the supplied values. - We couldn't do this above where the other parms' values are handled - because we need fp_delta to do it right. */ - while (must_load_parms) - { - rtx dest = DECL_RTL (TREE_PURPOSE (must_load_parms)); - int parm_num = TREE_INT_CST_LOW (TREE_VALUE (must_load_parms)); - emit_insn (gen_move_insn (copy_rtx_and_substitute (dest), - arg_vec[parm_num])); - must_load_parms = TREE_CHAIN (must_load_parms); - } - - /* Now copy the insns one by one. */ - - for (insn = insns; insn; insn = NEXT_INSN (insn)) - { - rtx copy, pattern, next = 0; - - orig_asm_operands_vector = 0; - copy_asm_operands_vector = 0; - - switch (GET_CODE (insn)) - { - case INSN: - pattern = PATTERN (insn); - - /* Special handling for the insn immediately after a CALL_INSN - that returned a value: - If it does copy the value, we must avoid the usual translation - of the return-register into INLINE_TARGET. - If it just USEs the value, the inline function expects it to - stay in the return-register and be returned, - so copy it into INLINE_TARGET. */ - - if (follows_call - /* Allow a stack-adjust, handled normally, to come in between - the call and the value-copying insn. */ - && ! (GET_CODE (pattern) == SET - && SET_DEST (pattern) == stack_pointer_rtx)) - { - if (GET_CODE (pattern) == SET - && rtx_equal_p (SET_SRC (pattern), follows_call)) - /* This insn copies the value: take special care to copy - that value to this insn's destination. */ - { - copy = emit_insn (gen_rtx (SET, VOIDmode, - copy_rtx_and_substitute (SET_DEST (pattern)), - follows_call)); - RTX_INTEGRATED_P (copy) = 1; - follows_call = 0; - break; - } - else if (GET_CODE (pattern) == USE - && rtx_equal_p (XEXP (pattern, 0), follows_call)) - /* This insn does nothing but says the value is expected - to flow through to the inline function's return-value. - Make that happen, then ignore this insn. */ - { - copy = emit_insn (gen_rtx (SET, VOIDmode, inline_target, - follows_call)); - RTX_INTEGRATED_P (copy) = 1; - follows_call = 0; - break; - } - /* If it does neither, this value must be ignored. */ - follows_call = 0; - } - - /* The (USE (REG n)) at return from the function should be ignored - since we are changing (REG n) into inline_target. */ - copy = 0; - if (GET_CODE (pattern) == USE - && GET_CODE (XEXP (pattern, 0)) == REG - && REG_FUNCTION_VALUE_P (XEXP (pattern, 0))) - break; - /* Ignore setting a function value that we don't want to use. */ - if (inline_target == 0 - && GET_CODE (pattern) == SET - && GET_CODE (SET_DEST (pattern)) == REG - && REG_FUNCTION_VALUE_P (SET_DEST (pattern))) - break; - - /* Try to do some quick constant folding here. - This will save save execution time of the compiler, - as well time and space of the program if done here. */ - if (GET_CODE (pattern) == SET - && SET_DEST (pattern) == cc0_rtx) - next = try_fold_cc0 (insn); - - if (next != 0) - { - insn = next; - } - else - { - rtx note = find_reg_note (insn, REG_EQUIV, 0); - - copy = emit_insn (copy_rtx_and_substitute (pattern)); - RTX_INTEGRATED_P (copy) = 1; - - /* If we are copying an insn that loads a constant, - record the constantness. */ - if (note) - REG_NOTES (copy) - = gen_rtx (EXPR_LIST, REG_EQUIV, XEXP (note, 0), - REG_NOTES (copy)); - } - break; - - case JUMP_INSN: - follows_call = 0; - if (GET_CODE (PATTERN (insn)) == RETURN) - { - if (local_return_label == 0) - local_return_label = gen_label_rtx (); - emit_jump (local_return_label); - break; - } - copy = emit_jump_insn (copy_rtx_and_substitute (PATTERN (insn))); - RTX_INTEGRATED_P (copy) = 1; - break; - - case CALL_INSN: -#if 0 - /* This should no longer be necessary now that references - to this function's return value are flagged to distinguish - them from other references to the same hard register. */ - { - rtx newbod; - /* If the call's body is (set (reg...) (call...)), - the register is a function return register, but DON'T - translate it into INLINE_TARGET because it describes the - called function, not the caller's return value. */ - if (GET_CODE (PATTERN (insn)) == SET) - newbod = gen_rtx (SET, VOIDmode, SET_DEST (PATTERN (insn)), - copy_rtx_and_substitute (SET_SRC (PATTERN (insn)))); - else if (GET_CODE (PATTERN (insn)) == PARALLEL - && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) - { - register int j; - rtx newelem; - newbod = gen_rtx (PARALLEL, VOIDmode, - rtvec_alloc (XVECLEN (PATTERN (insn), 0))); - newelem = gen_rtx (SET, VOIDmode, - SET_DEST (XVECEXP (PATTERN (insn), 0, 0)), - copy_rtx_and_substitute (SET_SRC (XVECEXP (PATTERN (insn), 0, 0)))); - XVECEXP (newbod, 0, 0) = newelem; - for (j = 1; j < XVECLEN (newbod, 0); j++) - XVECEXP (newbod, 0, j) - = copy_rtx_and_substitute (XVECEXP (PATTERN (insn), 0, j)); - } - else - newbod = copy_rtx_and_substitute (PATTERN (insn)); - copy = emit_call_insn (newbod); - } -#else /* 1 */ - copy = emit_call_insn (copy_rtx_and_substitute (PATTERN (insn))); -#endif /* 1 */ - RTX_INTEGRATED_P (copy) = 1; - /* Special handling needed for the following INSN depending on - whether it copies the value from the fcn return reg. */ - if (GET_CODE (PATTERN (insn)) == SET) - follows_call = SET_DEST (PATTERN (insn)); - else if (GET_CODE (PATTERN (insn)) == PARALLEL - && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) - follows_call = SET_DEST (XVECEXP (PATTERN (insn), 0, 0)); - break; - - case CODE_LABEL: - copy = emit_label (label_map[CODE_LABEL_NUMBER (insn)]); - follows_call = 0; - break; - - case BARRIER: - copy = emit_barrier (); - break; - - case NOTE: - if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END - && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_BEG) - copy = emit_note (NOTE_SOURCE_FILE (insn), NOTE_LINE_NUMBER (insn)); - else - copy = 0; - break; - - default: - abort (); - break; - } - - insn_map[INSN_UID (insn)] = copy; - } - - if (local_return_label) - emit_label (local_return_label); - - /* Make copies of the decls of the symbols in the inline function, so that - the copies of the variables get declared in the current function. */ - copy_decl_tree (DECL_INITIAL (fndecl), 0); - - /* End the scope containing the copied formal parameter variables. */ - - expand_end_bindings (getdecls (), 1, 1); - poplevel (1, 1, 0); - poplevel (0, 0, 0); - - emit_line_note (input_filename, lineno); - reg_map = NULL; - label_map = NULL; - - if (ignore || TYPE_MODE (type) == VOIDmode) - return 0; - - if (structure_value_addr) - { - if (target) - return target; - return gen_rtx (MEM, TYPE_MODE (type), - memory_address (BLKmode, structure_value_addr)); - } - - return target; -} - -/* Given a chain of PARM_DECLs, ARGS, and a vector of RTL homes VEC, - copy each decl into a VAR_DECL, push all of those decls - and give each one the corresponding home. */ - -static void -copy_parm_decls (args, vec) - tree args; - rtx *vec; -{ - register tree tail; - register int i; - - for (tail = args, i = 0; tail; tail = TREE_CHAIN (tail), i++) - { - register tree decl = pushdecl (build_decl (VAR_DECL, DECL_NAME (tail), - TREE_TYPE (tail))); - /* These args would always appear unused, if not for this. */ - TREE_USED (decl) = 1; - /* Prevent warning for shadowing with these. */ - TREE_INLINE (decl) = 1; - DECL_RTL (decl) = vec[i]; - } -} - -/* Given a LET_STMT node, push decls and levels - so as to construct in the current function a tree of contexts - isomorphic to the one that is given. */ - -static void -copy_decl_tree (let, level) - tree let; - int level; -{ - tree t, node; - - pushlevel (0); - - for (t = STMT_VARS (let); t; t = TREE_CHAIN (t)) - { - tree d = build_decl (TREE_CODE (t), DECL_NAME (t), TREE_TYPE (t)); - DECL_SOURCE_LINE (d) = DECL_SOURCE_LINE (t); - DECL_SOURCE_FILE (d) = DECL_SOURCE_FILE (t); - if (DECL_RTL (t) != 0) - { - if (GET_CODE (DECL_RTL (t)) == MEM - && CONSTANT_ADDRESS_P (XEXP (DECL_RTL (t), 0))) - /* copy_rtx_and_substitute would call memory_address - which would copy the address into a register. - Then debugging-output wouldn't know how to handle it. */ - DECL_RTL (d) = DECL_RTL (t); - else - DECL_RTL (d) = copy_rtx_and_substitute (DECL_RTL (t)); - } - TREE_EXTERNAL (d) = TREE_EXTERNAL (t); - TREE_STATIC (d) = TREE_STATIC (t); - TREE_PUBLIC (d) = TREE_PUBLIC (t); - TREE_LITERAL (d) = TREE_LITERAL (t); - TREE_ADDRESSABLE (d) = TREE_ADDRESSABLE (t); - TREE_READONLY (d) = TREE_READONLY (t); - TREE_VOLATILE (d) = TREE_VOLATILE (t); - /* These args would always appear unused, if not for this. */ - TREE_USED (d) = 1; - /* Prevent warning for shadowing with these. */ - TREE_INLINE (d) = 1; - pushdecl (d); - } - - for (t = STMT_SUBBLOCKS (let); t; t = TREE_CHAIN (t)) - copy_decl_tree (t, level + 1); - - node = poplevel (level > 0, 0, 0); - if (node) - TREE_USED (node) = TREE_USED (let); -} - -/* Create a new copy of an rtx. - Recursively copies the operands of the rtx, - except for those few rtx codes that are sharable. */ - -static rtx -copy_rtx_and_substitute (orig) - register rtx orig; -{ - register rtx copy, temp; - register int i, j; - register RTX_CODE code; - register enum machine_mode mode; - register char *format_ptr; - int regno; - - if (orig == 0) - return 0; - - code = GET_CODE (orig); - mode = GET_MODE (orig); - - switch (code) - { - case REG: - /* If a frame-pointer register shows up, then we - must `fix' the reference. If the stack pointer - register shows up, it must be part of stack-adjustments - (*not* because we eliminated the frame pointer!). - Small hard registers are returned as-is. Pseudo-registers - go through their `reg_map'. */ - regno = REGNO (orig); - if (regno < FIRST_PSEUDO_REGISTER) - { - /* Some hard registers are also mapped, - but others are not translated. */ - if (reg_map[regno] != 0) - return reg_map[regno]; - if (REG_FUNCTION_VALUE_P (orig)) - { - /* This is a reference to the function return value. If - the function doesn't have a return value, error. - If it does, it may not be the same mode as `inline_target' - because SUBREG is not required for hard regs. - If not, adjust mode of inline_target to fit the context. */ - if (inline_target == 0) - abort (); - if (mode == GET_MODE (inline_target)) - return inline_target; - return gen_rtx (SUBREG, mode, inline_target, 0); - } - if (regno == FRAME_POINTER_REGNUM) - return plus_constant (orig, fp_delta); - return orig; - } - if (reg_map[regno] == NULL) - reg_map[regno] = gen_reg_rtx (mode); - return reg_map[regno]; - - case SUBREG: - copy = copy_rtx_and_substitute (SUBREG_REG (orig)); - /* SUBREG is ordinary, but don't make nested SUBREGs. */ - if (GET_CODE (copy) == SUBREG) - return gen_rtx (SUBREG, GET_MODE (orig), SUBREG_REG (copy), - SUBREG_WORD (orig) + SUBREG_WORD (copy)); - return gen_rtx (SUBREG, GET_MODE (orig), copy, - SUBREG_WORD (orig)); - - case CODE_LABEL: - return label_map[CODE_LABEL_NUMBER (orig)]; - - case LABEL_REF: - copy = rtx_alloc (LABEL_REF); - PUT_MODE (copy, mode); - XEXP (copy, 0) = label_map[CODE_LABEL_NUMBER (XEXP (orig, 0))]; - return copy; - - case PC: - case CC0: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - return orig; - - case ASM_OPERANDS: - /* If a single asm insn contains multiple output operands - then it contains multiple ASM_OPERANDS rtx's that share operand 3. - We must make sure that the copied insn continues to share it. */ - if (orig_asm_operands_vector == XVEC (orig, 3)) - { - copy = rtx_alloc (ASM_OPERANDS); - XSTR (copy, 0) = XSTR (orig, 0); - XSTR (copy, 1) = XSTR (orig, 1); - XINT (copy, 2) = XINT (orig, 2); - XVEC (copy, 3) = copy_asm_operands_vector; - XVEC (copy, 4) = copy_asm_constraints_vector; - XSTR (copy, 5) = XSTR (orig, 5); - XINT (copy, 6) = XINT (orig, 6); - return copy; - } - break; - - case CALL: - /* This is given special treatment because the first - operand of a CALL is a (MEM ...) which may get - forced into a register for cse. This is undesirable - if function-address cse isn't wanted or if we won't do cse. */ -#ifndef NO_FUNCTION_CSE - if (! (optimize && ! flag_no_function_cse)) -#endif - return gen_rtx (CALL, GET_MODE (orig), - gen_rtx (MEM, GET_MODE (XEXP (orig, 0)), - copy_rtx_and_substitute (XEXP (XEXP (orig, 0), 0))), - copy_rtx_and_substitute (XEXP (orig, 1))); - break; - - case PLUS: - /* Note: the PLUS case is not nearly as careful as the MEM - case in terms of preserving addresses. The reason for this - is that it is expected that if a PLUS_EXPR turns out not - to be a legitimate address, reload can fix that up, without - doing major damage. However, a MEM rtx must preside - over a legitimate address. The MEM case has lots of hair - to deal with what happens when it sits on a PLUS... */ - /* Take care of the easy case quickly. */ - if (XEXP (orig, 0) == frame_pointer_rtx - || XEXP (orig, 1) == frame_pointer_rtx - || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM - && (XEXP (orig, 0) == arg_pointer_rtx - || XEXP (orig, 1) == arg_pointer_rtx))) - { - rtx reg; - if (XEXP (orig, 0) == frame_pointer_rtx - || XEXP (orig, 0) == arg_pointer_rtx) - reg = XEXP (orig, 0), copy = XEXP (orig, 1); - else - reg = XEXP (orig, 1), copy = XEXP (orig, 0); - - if (GET_CODE (copy) == CONST_INT) - { - int c = INTVAL (copy); - - if (reg == arg_pointer_rtx && c >= first_parm_offset) - { - copy = access_parm_map (c, VOIDmode); - if (GET_CODE (copy) != MEM) - /* Should not happen, because a parm we need to address - should not be living in a register. - (expand_inline_function copied it to a stack slot.) */ - abort (); - return XEXP (copy, 0); - } - return gen_rtx (PLUS, mode, - frame_pointer_rtx, - gen_rtx (CONST_INT, SImode, - c + fp_delta)); - } - copy = copy_rtx_and_substitute (copy); - temp = force_reg (mode, gen_rtx (PLUS, mode, frame_pointer_rtx, copy)); - return plus_constant (temp, fp_delta); - } - else if (reg_mentioned_p (frame_pointer_rtx, orig) - || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM - && reg_mentioned_p (arg_pointer_rtx, orig))) - { - /* If we have a complex sum which has a frame pointer - in it, and it was a legitimate address, then - keep it that way. */ - if (memory_address_p (mode, orig)) - { - if (GET_CODE (XEXP (orig, 0)) == CONST_INT) - { - copy = copy_rtx_and_substitute (XEXP (orig, 1)); - temp = plus_constant (copy, INTVAL (XEXP (orig, 0))); - } - else if (GET_CODE (XEXP (orig, 1)) == CONST_INT) - { - copy = copy_rtx_and_substitute (XEXP (orig, 0)); - temp = plus_constant (copy, INTVAL (XEXP (orig, 1))); - } - else - { - temp = gen_rtx (PLUS, GET_MODE (orig), - copy_rtx_and_substitute (XEXP (orig, 0)), - copy_rtx_and_substitute (XEXP (orig, 1))); - } - temp = memory_address (mode, temp); - } - else - temp = gen_rtx (PLUS, GET_MODE (orig), - copy_rtx_and_substitute (XEXP (orig, 0)), - copy_rtx_and_substitute (XEXP (orig, 1))); - } - else - temp = gen_rtx (PLUS, GET_MODE (orig), - copy_rtx_and_substitute (XEXP (orig, 0)), - copy_rtx_and_substitute (XEXP (orig, 1))); - - return temp; - - case MEM: - /* Take care of easiest case here. */ - copy = XEXP (orig, 0); - if (copy == frame_pointer_rtx || copy == arg_pointer_rtx) - return gen_rtx (MEM, mode, - plus_constant (frame_pointer_rtx, fp_delta)); - - /* Allow a pushing-address even if that is not valid as an - ordinary memory address. It indicates we are inlining a special - push-insn. These must be copied; otherwise unshare_all_rtl - might clobber them to point at temporary rtl of this function. */ -#ifdef STACK_GROWS_DOWNWARD - if (GET_CODE (copy) == PRE_DEC && XEXP (copy, 0) == stack_pointer_rtx) - return gen_rtx (MEM, mode, copy_rtx_and_substitute (copy)); -#else - if (GET_CODE (copy) == PRE_INC && XEXP (copy, 0) == stack_pointer_rtx) - return gen_rtx (MEM, mode, copy_rtx_and_substitute (copy)); -#endif - - /* If this is some other sort of address that isn't generally valid, - break out all the registers referred to. */ - if (! memory_address_p (mode, copy)) - return gen_rtx (MEM, mode, copy_address (copy)); - - if (GET_CODE (copy) == PLUS) - { - if (XEXP (copy, 0) == frame_pointer_rtx - || XEXP (copy, 1) == frame_pointer_rtx - || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM - && (XEXP (copy, 0) == arg_pointer_rtx - || XEXP (copy, 1) == arg_pointer_rtx))) - { - rtx reg; - if (XEXP (copy, 0) == frame_pointer_rtx - || XEXP (copy, 0) == arg_pointer_rtx) - reg = XEXP (copy, 0), copy = XEXP (copy, 1); - else - reg = XEXP (copy, 1), copy = XEXP (copy, 0); - - if (GET_CODE (copy) == CONST_INT) - { - int c = INTVAL (copy); - - if (reg == arg_pointer_rtx && c >= first_parm_offset) - return access_parm_map (c, mode); - - temp = gen_rtx (PLUS, Pmode, - frame_pointer_rtx, - gen_rtx (CONST_INT, SImode, - c + fp_delta)); - if (! memory_address_p (Pmode, temp)) - return gen_rtx (MEM, mode, plus_constant (inline_fp_rtx, c)); - } - copy = copy_rtx_and_substitute (copy); - temp = gen_rtx (PLUS, Pmode, frame_pointer_rtx, copy); - temp = plus_constant (temp, fp_delta); - temp = memory_address (Pmode, temp); - } - else if (reg_mentioned_p (frame_pointer_rtx, copy) - || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM - && reg_mentioned_p (arg_pointer_rtx, copy))) - { - if (GET_CODE (XEXP (copy, 0)) == CONST_INT) - { - temp = copy_rtx_and_substitute (XEXP (copy, 1)); - temp = plus_constant (temp, INTVAL (XEXP (copy, 0))); - } - else if (GET_CODE (XEXP (copy, 1)) == CONST_INT) - { - temp = copy_rtx_and_substitute (XEXP (copy, 0)); - temp = plus_constant (temp, INTVAL (XEXP (copy, 1))); - } - else - { - temp = gen_rtx (PLUS, GET_MODE (copy), - copy_rtx_and_substitute (XEXP (copy, 0)), - copy_rtx_and_substitute (XEXP (copy, 1))); - } - } - else - { - if (GET_CODE (XEXP (copy, 1)) == CONST_INT) - temp = plus_constant (copy_rtx_and_substitute (XEXP (copy, 0)), - INTVAL (XEXP (copy, 1))); - else if (GET_CODE (XEXP (copy, 0)) == CONST_INT) - temp = plus_constant (copy_rtx_and_substitute (XEXP (copy, 1)), - INTVAL (XEXP (copy, 0))); - else - { - rtx left = copy_rtx_and_substitute (XEXP (copy, 0)); - rtx right = copy_rtx_and_substitute (XEXP (copy, 1)); - - temp = gen_rtx (PLUS, GET_MODE (copy), left, right); - } - } - } - else - temp = copy_rtx_and_substitute (copy); - - return change_address (orig, mode, temp); - - case RETURN: - abort (); - } - - copy = rtx_alloc (code); - PUT_MODE (copy, mode); - copy->in_struct = orig->in_struct; - copy->volatil = orig->volatil; - copy->unchanging = orig->unchanging; - - format_ptr = GET_RTX_FORMAT (GET_CODE (copy)); - - for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++) - { - switch (*format_ptr++) - { - case '0': - break; - - case 'e': - XEXP (copy, i) = copy_rtx_and_substitute (XEXP (orig, i)); - break; - - case 'u': - /* Change any references to old-insns to point to the - corresponding copied insns. */ - XEXP (copy, i) = insn_map[INSN_UID (XEXP (orig, i))]; - break; - - case 'E': - XVEC (copy, i) = XVEC (orig, i); - if (XVEC (orig, i) != NULL && XVECLEN (orig, i) != 0) - { - XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i)); - for (j = 0; j < XVECLEN (copy, i); j++) - XVECEXP (copy, i, j) = copy_rtx_and_substitute (XVECEXP (orig, i, j)); - } - break; - - case 'i': - XINT (copy, i) = XINT (orig, i); - break; - - case 's': - XSTR (copy, i) = XSTR (orig, i); - break; - - default: - abort (); - } - } - - if (code == ASM_OPERANDS && orig_asm_operands_vector == 0) - { - orig_asm_operands_vector = XVEC (orig, 3); - copy_asm_operands_vector = XVEC (copy, 3); - copy_asm_constraints_vector = XVEC (copy, 4); - } - - return copy; -} - -/* Get the value corresponding to an address relative to the arg pointer - at index RELADDRESS. MODE is the machine mode of the reference. - MODE is used only when the value is a REG. - Pass VOIDmode for MODE when the mode is not known; - in such cases, you should make sure the value is a MEM. */ - -static rtx -access_parm_map (reladdress, mode) - int reladdress; - enum machine_mode mode; -{ - /* Index in parm_map. */ - int index = reladdress / UNITS_PER_WORD; - /* Offset of the data being referenced - from the beginning of the value for that parm. */ - int offset = reladdress % UNITS_PER_WORD; - rtx copy; - - /* If we are referring to the middle of a multiword parm, - find the beginning of that parm. - OFFSET gets the offset of the reference from - the beginning of the parm. */ - - while (parm_map[index] == 0) - { - index--; - if (index < first_parm_offset / UNITS_PER_WORD) - /* If this abort happens, it means we need - to handle "decrementing" INDEX back far - enough to start looking among the reg parms - instead of the stack parms. What a mess! */ - abort (); - offset += UNITS_PER_WORD; - } - - copy = parm_map[index]; - -#ifdef BYTES_BIG_ENDIAN - /* Subtract from OFFSET the offset of where - the actual parm value would start. */ - if (GET_MODE_SIZE (GET_MODE (copy)) < UNITS_PER_WORD) - offset - -= (UNITS_PER_WORD - - GET_MODE_SIZE (GET_MODE (copy))); -#endif - - /* For memory ref, adjust it by the desired offset. */ - if (GET_CODE (copy) == MEM) - { - if (offset != 0) - return change_address (copy, mode, - plus_constant (XEXP (copy, 0), - offset)); - return copy; - } - - if (GET_CODE (copy) != REG && GET_CODE (copy) != SUBREG - && ! CONSTANT_P (copy)) - abort (); - if (mode == VOIDmode) - abort (); - - /* A REG cannot be offset by bytes, so use a subreg - (which is possible only in certain cases). */ - if (GET_MODE (copy) != mode - && GET_MODE (copy) != VOIDmode) - { - int word; - /* Crash if the portion of the arg wanted - is not the least significant. - Functions with refs to other parts of a - parameter should not be inline-- - see function_cannot_inline_p. */ -#ifdef BYTES_BIG_ENDIAN - if ((offset + GET_MODE_SIZE (mode)) % UNITS_PER_WORD - != GET_MODE_SIZE (GET_MODE (copy)) % UNITS_PER_WORD) - abort (); -#else - if ((offset % UNITS_PER_WORD) != 0) - abort (); -#endif - word = offset % UNITS_PER_WORD; - if (GET_CODE (copy) == SUBREG) - word = SUBREG_WORD (copy), copy = SUBREG_REG (copy); - if (CONSTANT_P (copy)) - copy = force_reg (GET_MODE (copy), copy); - return gen_rtx (SUBREG, mode, copy, word); - } - - return copy; -} - -/* Like copy_rtx_and_substitute but produces different output, suitable - for an ideosyncractic address that isn't memory_address_p. - The output resembles the input except that REGs and MEMs are replaced - with new psuedo registers. All the "real work" is done in separate - insns which set up the values of these new registers. */ - -static rtx -copy_address (orig) - register rtx orig; -{ - register rtx copy; - register int i, j; - register RTX_CODE code; - register enum machine_mode mode; - register char *format_ptr; - - if (orig == 0) - return 0; - - code = GET_CODE (orig); - mode = GET_MODE (orig); - - switch (code) - { - case REG: - if (REGNO (orig) != FRAME_POINTER_REGNUM) - return copy_rtx_and_substitute (orig); - return plus_constant (frame_pointer_rtx, fp_delta); - - case PLUS: - if (GET_CODE (XEXP (orig, 0)) == REG - && REGNO (XEXP (orig, 0)) == FRAME_POINTER_REGNUM) - return plus_constant (orig, fp_delta); - break; - - case MEM: - return copy_to_reg (copy_rtx_and_substitute (orig)); - - case CODE_LABEL: - case LABEL_REF: - return copy_rtx_and_substitute (orig); - - case PC: - case CC0: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - return orig; - } - - copy = rtx_alloc (code); - PUT_MODE (copy, mode); - copy->in_struct = orig->in_struct; - copy->volatil = orig->volatil; - copy->unchanging = orig->unchanging; - - format_ptr = GET_RTX_FORMAT (GET_CODE (copy)); - - for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++) - { - switch (*format_ptr++) - { - case '0': - break; - - case 'e': - XEXP (copy, i) = copy_rtx_and_substitute (XEXP (orig, i)); - break; - - case 'u': - /* Change any references to old-insns to point to the - corresponding copied insns. */ - XEXP (copy, i) = insn_map[INSN_UID (XEXP (orig, i))]; - break; - - case 'E': - XVEC (copy, i) = XVEC (orig, i); - if (XVEC (orig, i) != NULL && XVECLEN (orig, i) != 0) - { - XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i)); - for (j = 0; j < XVECLEN (copy, i); j++) - XVECEXP (copy, i, j) = copy_rtx_and_substitute (XVECEXP (orig, i, j)); - } - break; - - case 'i': - XINT (copy, i) = XINT (orig, i); - break; - - case 's': - XSTR (copy, i) = XSTR (orig, i); - break; - - default: - abort (); - } - } - return copy; -} - -/* Attempt to simplify INSN while copying it from an inline fn, - assuming it is a SET that sets CC0. - - If we simplify it, we emit the appropriate insns and return - the last insn that we have handled (since we may handle the insn - that follows INSN as well as INSN itself). - - Otherwise we do nothing and return zero. */ - -static rtx -try_fold_cc0 (insn) - rtx insn; -{ - rtx cnst = copy_rtx_and_substitute (SET_SRC (PATTERN (insn))); - rtx pat, copy; - - if (CONSTANT_P (cnst) - /* @@ Cautious: Don't know how many of these tests we need. */ - && NEXT_INSN (insn) - && GET_CODE (pat = PATTERN (NEXT_INSN (insn))) == SET - && SET_DEST (pat) == pc_rtx - && GET_CODE (pat = SET_SRC (pat)) == IF_THEN_ELSE - && GET_RTX_LENGTH (GET_CODE (XEXP (pat, 0))) == 2) - { - rtx cnst2; - rtx cond = XEXP (pat, 0); - - if ((XEXP (cond, 0) == cc0_rtx - && CONSTANT_P (XEXP (cond, 1)) - && (cnst2 = XEXP (cond, 1))) - || (XEXP (cond, 1) == cc0_rtx - && CONSTANT_P (XEXP (cond, 0)) - && (cnst2 = XEXP (cond, 0)))) - { - copy = fold_out_const_cc0 (cond, XEXP (pat, 1), XEXP (pat, 2), - cnst, cnst2); - if (copy) - { - if (GET_CODE (copy) == LABEL_REF) - { - /* We will branch unconditionally to - the label specified by COPY. - Eliminate dead code by running down the - list of insn until we see a CODE_LABEL. - If the CODE_LABEL is the one specified - by COPY, we win, and can delete all code - up to (but not necessarily including) - that label. Otherwise only win a little: - emit the branch insn, and continue expanding. */ - rtx tmp = NEXT_INSN (insn); - while (tmp && GET_CODE (tmp) != CODE_LABEL) - tmp = NEXT_INSN (tmp); - if (! tmp) - abort (); - if (label_map[CODE_LABEL_NUMBER (tmp)] == XEXP (copy, 0)) - { - /* Big win. */ - return PREV_INSN (tmp); - } - else - { - /* Small win. Emit the unconditional branch, - followed by a BARRIER, so that jump optimization - will know what to do. */ - emit_jump (copy); - return NEXT_INSN (insn); - } - } - else if (copy == pc_rtx) - { - /* Do not take the branch, just fall through. - Jump optimize should handle the elimination of - dead code if appropriate. */ - return NEXT_INSN (insn); - } - else - abort (); - } - } - } - return 0; -} - -/* If (COND_RTX CNST1 CNST2) yield a result we can treat - as being constant, return THEN_RTX if the result is always - non-zero, and return ELSE_RTX otherwise. */ -static rtx -fold_out_const_cc0 (cond_rtx, then_rtx, else_rtx, cnst1, cnst2) - rtx cond_rtx, then_rtx, else_rtx; - rtx cnst1, cnst2; -{ - int value1, value2; - int int1 = GET_CODE (cnst1) == CONST_INT; - int int2 = GET_CODE (cnst2) == CONST_INT; - if (int1) - value1 = INTVAL (cnst1); - else - value1 = 1; - if (int2) - value2 = INTVAL (cnst2); - else - value2 = 1; - - switch (GET_CODE (cond_rtx)) - { - case NE: - if (int1 && int2) - if (value1 != value2) - return copy_rtx_and_substitute (then_rtx); - else - return copy_rtx_and_substitute (else_rtx); - if (value1 == 0 || value2 == 0) - return copy_rtx_and_substitute (then_rtx); - if (int1 == 0 && int2 == 0) - if (rtx_equal_p (cnst1, cnst2)) - return copy_rtx_and_substitute (else_rtx); - break; - case EQ: - if (int1 && int2) - if (value1 == value2) - return copy_rtx_and_substitute (then_rtx); - else - return copy_rtx_and_substitute (else_rtx); - if (value1 == 0 || value2 == 0) - return copy_rtx_and_substitute (else_rtx); - if (int1 == 0 && int2 == 0) - if (rtx_equal_p (cnst1, cnst2)) - return copy_rtx_and_substitute (then_rtx); - break; - case GE: - if (int1 && int2) - if (value1 >= value2) - return copy_rtx_and_substitute (then_rtx); - else - return copy_rtx_and_substitute (else_rtx); - if (value1 == 0) - return copy_rtx_and_substitute (else_rtx); - if (value2 == 0) - return copy_rtx_and_substitute (then_rtx); - break; - case GT: - if (int1 && int2) - if (value1 > value2) - return copy_rtx_and_substitute (then_rtx); - else - return copy_rtx_and_substitute (else_rtx); - if (value1 == 0) - return copy_rtx_and_substitute (else_rtx); - if (value2 == 0) - return copy_rtx_and_substitute (then_rtx); - break; - case LE: - if (int1 && int2) - if (value1 <= value2) - return copy_rtx_and_substitute (then_rtx); - else - return copy_rtx_and_substitute (else_rtx); - if (value1 == 0) - return copy_rtx_and_substitute (then_rtx); - if (value2 == 0) - return copy_rtx_and_substitute (else_rtx); - break; - case LT: - if (int1 && int2) - if (value1 < value2) - return copy_rtx_and_substitute (then_rtx); - else - return copy_rtx_and_substitute (else_rtx); - if (value1 == 0) - return copy_rtx_and_substitute (then_rtx); - if (value2 == 0) - return copy_rtx_and_substitute (else_rtx); - break; - case GEU: - if (int1 && int2) - if ((unsigned)value1 >= (unsigned)value2) - return copy_rtx_and_substitute (then_rtx); - else - return copy_rtx_and_substitute (else_rtx); - if (value1 == 0) - return copy_rtx_and_substitute (else_rtx); - if (value2 == 0) - return copy_rtx_and_substitute (then_rtx); - break; - case GTU: - if (int1 && int2) - if ((unsigned)value1 > (unsigned)value2) - return copy_rtx_and_substitute (then_rtx); - else - return copy_rtx_and_substitute (else_rtx); - if (value1 == 0) - return copy_rtx_and_substitute (else_rtx); - if (value2 == 0) - return copy_rtx_and_substitute (then_rtx); - break; - case LEU: - if (int1 && int2) - if ((unsigned)value1 <= (unsigned)value2) - return copy_rtx_and_substitute (then_rtx); - else - return copy_rtx_and_substitute (else_rtx); - if (value1 == 0) - return copy_rtx_and_substitute (then_rtx); - if (value2 == 0) - return copy_rtx_and_substitute (else_rtx); - break; - case LTU: - if (int1 && int2) - if ((unsigned)value1 < (unsigned)value2) - return copy_rtx_and_substitute (then_rtx); - else - return copy_rtx_and_substitute (else_rtx); - if (value1 == 0) - return copy_rtx_and_substitute (then_rtx); - if (value2 == 0) - return copy_rtx_and_substitute (else_rtx); - break; - } - /* Could not hack it. */ - return 0; -} - -/* Output the assembly language code for the function FNDECL - from its DECL_SAVED_INSNS. Used for inline functions that are output - at end of compilation instead of where they came in the source. */ - -void -output_inline_function (fndecl) - tree fndecl; -{ - rtx head = DECL_SAVED_INSNS (fndecl); - rtx last; - extern rtx stack_slot_list; - - temporary_allocation (); - - current_function_decl = fndecl; - - /* This call is only used to initialize global variables. */ - init_function_start (fndecl, "lossage", 1); - - /* Set stack frame size. */ - assign_stack_local (BLKmode, DECL_FRAME_SIZE (fndecl)); - - restore_reg_data (FIRST_PARM_INSN (head)); - - stack_slot_list = XEXP (head, 9); - - expand_function_end (DECL_SOURCE_FILE (fndecl), DECL_SOURCE_LINE (fndecl)); - - for (last = head; NEXT_INSN (last); last = NEXT_INSN (last)) - ; - - set_new_first_and_last_insn (FIRST_PARM_INSN (head), last); - - /* Compile this function all the way down to assembly code. */ - rest_of_compilation (fndecl); - - current_function_decl = 0; - - permanent_allocation (); -} diff --git a/gnu/usr.bin/gcc1/cc1/jump.c b/gnu/usr.bin/gcc1/cc1/jump.c deleted file mode 100644 index 52f9db07ce..0000000000 --- a/gnu/usr.bin/gcc1/cc1/jump.c +++ /dev/null @@ -1,1618 +0,0 @@ -/* Optimize jump instructions, for GNU compiler. - Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This is the jump-optimization pass of the compiler. - It is run two or three times: once before cse, sometimes once after cse, - and once after reload (before final). - - jump_optimize deletes unreachable code and labels that are not used. - It also deletes jumps that jump to the following insn, - and simplifies jumps around unconditional jumps and jumps - to unconditional jumps. - - Each CODE_LABEL has a count of the times it is used - stored in the LABEL_NUSES internal field, and each JUMP_INSN - has one label that it refers to stored in the - JUMP_LABEL internal field. With this we can detect labels that - become unused because of the deletion of all the jumps that - formerly used them. The JUMP_LABEL info is sometimes looked - at by later passes. - - Optionally, cross-jumping can be done. Currently it is done - only the last time (when after reload and before final). - In fact, the code for cross-jumping now assumes that register - allocation has been done, since it uses `rtx_renumbered_equal_p'. - - Jump optimization is done after cse when cse's constant-propagation - causes jumps to become unconditional or to be deleted. - - Unreachable loops are not detected here, because the labels - have references and the insns appear reachable from the labels. - find_basic_blocks in flow.c finds and deletes such loops. - - The subroutines delete_insn, redirect_jump, invert_jump, next_real_insn - and prev_real_insn are used from other passes as well. */ - -#include "config.h" -#include "rtl.h" -#include "flags.h" -#include "regs.h" - -/* ??? Eventually must record somehow the labels used by jumps - from nested functions. */ -/* Pre-record the next or previous real insn for each label? - No, this pass is very fast anyway. */ -/* Condense consecutive labels? - This would make life analysis faster, maybe. */ -/* Optimize jump y; x: ... y: jumpif... x? - Don't know if it is worth bothering with. */ -/* Optimize two cases of conditional jump to conditional jump? - This can never delete any instruction or make anything dead, - or even change what is live at any point. - So perhaps let combiner do it. */ - -/* Vector indexed by uid. - For each CODE_LABEL, index by its uid to get first unconditional jump - that jumps to the label. - For each JUMP_INSN, index by its uid to get the next unconditional jump - that jumps to the same label. - Element 0 is the start of a chain of all return insns. - (It is safe to use element 0 because insn uid 0 is not used. */ - -rtx *jump_chain; - -rtx delete_insn (); -void redirect_jump (); -void invert_jump (); -rtx next_real_insn (); -rtx prev_real_insn (); -rtx next_label (); - -static void mark_jump_label (); -static void delete_jump (); -static void squeeze_block_notes (); -void invert_exp (); -static void redirect_exp (); -static rtx follow_jumps (); -static int tension_vector_labels (); -static void find_cross_jump (); -static void do_cross_jump (); -static enum rtx_code reverse_condition (); -static int jump_back_p (); -int condjump_p (); - -/* Delete no-op jumps and optimize jumps to jumps - and jumps around jumps. - Delete unused labels and unreachable code. - If CROSS_JUMP is nonzero, detect matching code - before a jump and its destination and unify them. - If NOOP_MOVES is nonzero, also delete no-op move insns. - - If `optimize' is zero, don't change any code, - just determine whether control drops off the end of the function. - This case occurs when we have -W and not -O. - It works because `delete_insn' checks the value of `optimize' - and refrains from actually deleting when that is 0. */ - -void -jump_optimize (f, cross_jump, noop_moves) - rtx f; -{ - register rtx insn; - int changed; - int first = 1; - int max_uid = 0; - rtx last_insn; - - /* Initialize LABEL_NUSES and JUMP_LABEL fields. */ - - for (insn = f; insn; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == CODE_LABEL) - LABEL_NUSES (insn) = 0; - if (GET_CODE (insn) == JUMP_INSN) - JUMP_LABEL (insn) = 0; - if (INSN_UID (insn) > max_uid) - max_uid = INSN_UID (insn); - } - - max_uid++; - - jump_chain = (rtx *) alloca (max_uid * sizeof (rtx)); - bzero (jump_chain, max_uid * sizeof (rtx)); - - /* Delete insns following barriers, up to next label. */ - - for (insn = f; insn;) - { - if (GET_CODE (insn) == BARRIER) - { - insn = NEXT_INSN (insn); - while (insn != 0 && GET_CODE (insn) != CODE_LABEL) - { - if (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END) - insn = NEXT_INSN (insn); - else - insn = delete_insn (insn); - } - /* INSN is now the code_label. */ - } - else - insn = NEXT_INSN (insn); - } - - /* Mark the label each jump jumps to. - Combine consecutive labels, and count uses of labels. - - For each label, make a chain (using `jump_chain') - of all the *unconditional* jumps that jump to it; - also make a chain of all returns. */ - - for (insn = f; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == JUMP_INSN && ! INSN_DELETED_P (insn)) - { - mark_jump_label (PATTERN (insn), insn, cross_jump); - if (JUMP_LABEL (insn) != 0 && simplejump_p (insn)) - { - jump_chain[INSN_UID (insn)] - = jump_chain[INSN_UID (JUMP_LABEL (insn))]; - jump_chain[INSN_UID (JUMP_LABEL (insn))] = insn; - } - if (GET_CODE (PATTERN (insn)) == RETURN) - { - jump_chain[INSN_UID (insn)] = jump_chain[0]; - jump_chain[0] = insn; - } - } - - /* Delete all labels already not referenced. - Also find the last insn. */ - - last_insn = 0; - for (insn = f; insn; ) - { - if (GET_CODE (insn) == CODE_LABEL && LABEL_NUSES (insn) == 0) - insn = delete_insn (insn); - else - { - last_insn = insn; - insn = NEXT_INSN (insn); - } - } - - if (!optimize) - { - /* See if there is still a NOTE_INSN_FUNCTION_END in this function. - If so record that this function can drop off the end. */ - - insn = last_insn; - { - int n_labels = 1; - while (insn - /* One label can follow the end-note: the return label. */ - && ((GET_CODE (insn) == CODE_LABEL && n_labels-- > 0) - /* Ordinary insns can follow it if returning a structure. */ - || GET_CODE (insn) == INSN - /* If machine uses explicit RETURN insns, no epilogue, - then one of them follows the note. */ - || (GET_CODE (insn) == JUMP_INSN - && GET_CODE (PATTERN (insn)) == RETURN) - /* Other kinds of notes can follow also. */ - || (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END))) - insn = PREV_INSN (insn); - } - - if (insn && GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_END - && ! INSN_DELETED_P (insn)) - { - extern int current_function_returns_null; - current_function_returns_null = 1; - } - /* Zero the "deleted" flag of all the "deleted" insns. */ - for (insn = f; insn; insn = NEXT_INSN (insn)) - INSN_DELETED_P (insn) = 0; - return; - } - - if (noop_moves) - for (insn = f; insn; ) - { - register rtx next = NEXT_INSN (insn); - - if (GET_CODE (insn) == INSN) - { - register rtx body = PATTERN (insn); - -#if 0 /* Keep these insns, since they are used for conditional branch - scheduling peepholes on the sparc. */ -#endif - /* Delete insns that existed just to advise flow-analysis. */ - - if (GET_CODE (body) == USE - || GET_CODE (body) == CLOBBER) - delete_insn (insn); - else - - /* Detect and delete no-op move instructions - resulting from not allocating a parameter in a register. */ - - if (GET_CODE (body) == SET - && (SET_DEST (body) == SET_SRC (body) - || (GET_CODE (SET_DEST (body)) == MEM - && GET_CODE (SET_SRC (body)) == MEM - && rtx_equal_p (SET_SRC (body), SET_DEST (body)))) - && ! (GET_CODE (SET_DEST (body)) == MEM - && MEM_VOLATILE_P (SET_DEST (body))) - && ! (GET_CODE (SET_SRC (body)) == MEM - && MEM_VOLATILE_P (SET_SRC (body)))) - delete_insn (insn); - - /* Detect and ignore no-op move instructions - resulting from smart or fortuitous register allocation. */ - - else if (GET_CODE (body) == SET) - { - int sreg = true_regnum (SET_SRC (body)); - int dreg = true_regnum (SET_DEST (body)); - - if (sreg == dreg && sreg >= 0) - delete_insn (insn); - else if (sreg >= 0 && dreg >= 0) - { - rtx tem = find_equiv_reg (0, insn, 0, - sreg, 0, dreg, - GET_MODE (SET_SRC (body))); - -#ifdef PRESERVE_DEATH_INFO_REGNO_P - /* Deleting insn could lose a death-note for SREG or DREG - so don't do it if final needs accurate death-notes. */ - if (! PRESERVE_DEATH_INFO_REGNO_P (sreg) - && ! PRESERVE_DEATH_INFO_REGNO_P (dreg)) -#endif - if (tem != 0 - && GET_MODE (tem) == GET_MODE (SET_DEST (body))) - delete_insn (insn); - } - } - } - insn = next; - } - - /* Now iterate optimizing jumps until nothing changes over one pass. */ - changed = 1; - while (changed) - { - register rtx next; - changed = 0; - - for (insn = f; insn; insn = next) - { -#if 0 - /* If NOT the first iteration, if this is the last jump pass - (just before final), do the special peephole optimizations. - Avoiding the first iteration gives ordinary jump opts - a chance to work before peephole opts. */ - - if (noop_moves && !first && !flag_no_peephole) - if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN) - peephole (insn); -#endif - - /* That could have deleted some insns after INSN, so check now - what the following insn is. */ - - next = NEXT_INSN (insn); - - /* Tension the labels in dispatch tables. */ - - if (GET_CODE (insn) == JUMP_INSN) - { - if (GET_CODE (PATTERN (insn)) == ADDR_VEC) - changed |= tension_vector_labels (PATTERN (insn), 0, noop_moves); - if (GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC) - changed |= tension_vector_labels (PATTERN (insn), 1, noop_moves); - } - - /* Don't allow dropping through into a dispatch table. - That means the dispatch insn itself was deleted, - so delete the table too. */ - - if (GET_CODE (insn) == JUMP_INSN) - { - /* Note: the corresponding job for ADDR_VEC is done - in delete_insn. */ - - /* A vector of offsets is unused if its label - is used only once (i.e., from the vector). */ - if (GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC - && LABEL_NUSES (XEXP (XEXP (PATTERN (insn), 0), 0)) == 1) - { - /* So delete both label and vector. */ - delete_insn (PREV_INSN (insn)); - delete_insn (insn); - changed = 1; - } - } - - if (GET_CODE (insn) == JUMP_INSN && JUMP_LABEL (insn)) - { - register rtx reallabelprev = prev_real_insn (JUMP_LABEL (insn)); - rtx temp; - - /* Detect jump to following insn. */ - if (reallabelprev == insn && condjump_p (insn)) - { - delete_jump (insn); - changed = 1; - } - /* Detect worthless conditional jump. */ - else if ((temp = next_real_insn (insn)) - && GET_CODE (temp) == JUMP_INSN - && condjump_p (insn) - && simplejump_p (temp) - && JUMP_LABEL (insn) == JUMP_LABEL (temp)) - { - delete_jump (insn); - changed = 1; - next = NEXT_INSN (insn); - } - /* A jump to a return becomes a return. */ - else if (simplejump_p (insn) - && (temp = next_real_insn (JUMP_LABEL (insn))) != 0 - && GET_CODE (PATTERN (temp)) == RETURN) - { - PATTERN (insn) = PATTERN (temp); - /* Re-recognize this insn. */ - INSN_CODE (insn) = -1; - } - /* Detect jumping over an unconditional jump. */ - else if (reallabelprev != 0 - && GET_CODE (reallabelprev) == JUMP_INSN - && prev_real_insn (reallabelprev) == insn - && no_labels_between_p (insn, reallabelprev) - && simplejump_p (reallabelprev) - /* Ignore this if INSN is a hairy kind of jump, - since they may not be invertible. - This is conservative; could instead construct - the inverted insn and try recognizing it. */ - && condjump_p (insn)) - { - /* Delete the original unconditional jump (and barrier). */ - /* But don't let its destination go with it. */ - ++LABEL_NUSES (JUMP_LABEL (reallabelprev)); - delete_insn (reallabelprev); - /* Now change the condition, and make it go to the - place the deleted jump went to. - This may cause the label after the deletion to go away. - But now that the unconditional jump and its barrier - are gone, that is ok. */ - invert_jump (insn, JUMP_LABEL (reallabelprev)); - --LABEL_NUSES (JUMP_LABEL (reallabelprev)); - next = insn; - changed = 1; - } - else - { - /* Detect a jump to a jump. */ - { - register rtx nlabel - = follow_jumps (JUMP_LABEL (insn), noop_moves); - if (nlabel != JUMP_LABEL (insn)) - { - redirect_jump (insn, nlabel); - changed = 1; - next = insn; - } - } - - /* Look for if (foo) bar; else break; */ - /* The insns look like this: - insn = condjump label1; - ...range1 (some insns)... - jump label2; - label1: - ...range2 (some insns)... - jump somewhere unconditionally - label2: */ - { - rtx label1 = next_label (insn); - rtx range1end = label1 ? prev_real_insn (label1) : 0; - /* Don't do this optimization on the first round, so that - jump-around-a-jump gets simplified before we ask here - whether a jump is unconditional. */ - if (! first - /* Make sure INSN is something we can invert. */ - && condjump_p (insn) - && JUMP_LABEL (insn) == label1 - && LABEL_NUSES (label1) == 1 - && GET_CODE (range1end) == JUMP_INSN - && simplejump_p (range1end)) - { - rtx label2 = next_label (label1); - rtx range2end = label2 ? prev_real_insn (label2) : 0; - if (range1end != range2end - && JUMP_LABEL (range1end) == label2 - && GET_CODE (range2end) == JUMP_INSN - && GET_CODE (NEXT_INSN (range2end)) == BARRIER) - { - rtx range1beg = next_real_insn (insn); - rtx range2beg = next_real_insn (label1); - rtx range1after, range2after; - rtx range1before, range2before; - - /* Don't move NOTEs for blocks; shift them - outside the ranges, where they'll stay put. */ - squeeze_block_notes (range1beg, range1end); - squeeze_block_notes (range2beg, range2end); - - /* Get current surrounds of the 2 ranges. */ - range1before = PREV_INSN (range1beg); - range2before = PREV_INSN (range2beg); - range1after = NEXT_INSN (range1end); - range2after = NEXT_INSN (range2end); - - /* Splice range2 where range1 was. */ - NEXT_INSN (range1before) = range2beg; - PREV_INSN (range2beg) = range1before; - NEXT_INSN (range2end) = range1after; - PREV_INSN (range1after) = range2end; - /* Splice range1 where range2 was. */ - NEXT_INSN (range2before) = range1beg; - PREV_INSN (range1beg) = range2before; - NEXT_INSN (range1end) = range2after; - PREV_INSN (range2after) = range1end; - /* Invert the jump condition, so we - still execute the same insns in each case. */ - invert_jump (insn, label1); - changed = 1; - continue; - } - } - } - - /* Now that the jump has been tensioned, - try cross jumping: check for identical code - before the jump and before its target label. */ - - /* First, cross jumping of conditional jumps: */ - - if (cross_jump && condjump_p (insn)) - { - rtx newjpos, newlpos; - rtx x = prev_real_insn (JUMP_LABEL (insn)); - - /* A conditional jump may be crossjumped - only if the place it jumps to follows - an opposing jump that comes back here. */ - - if (x != 0 && ! jump_back_p (x, insn)) - /* We have no opposing jump; - cannot cross jump this insn. */ - x = 0; - - newjpos = 0; - /* TARGET is nonzero if it is ok to cross jump - to code before TARGET. If so, see if matches. */ - if (x != 0) - find_cross_jump (insn, x, 2, - &newjpos, &newlpos); - - if (newjpos != 0) - { - do_cross_jump (insn, newjpos, newlpos); - /* Make the old conditional jump - into an unconditional one. */ - SET_SRC (PATTERN (insn)) - = gen_rtx (LABEL_REF, VOIDmode, JUMP_LABEL (insn)); - emit_barrier_after (insn); - changed = 1; - next = insn; - } - } - - /* Cross jumping of unconditional jumps: - a few differences. */ - - if (cross_jump && simplejump_p (insn)) - { - rtx newjpos, newlpos; - rtx target; - - newjpos = 0; - - /* TARGET is nonzero if it is ok to cross jump - to code before TARGET. If so, see if matches. */ - find_cross_jump (insn, JUMP_LABEL (insn), 1, - &newjpos, &newlpos); - - /* If cannot cross jump to code before the label, - see if we can cross jump to another jump to - the same label. */ - /* Try each other jump to this label. */ - if (INSN_UID (JUMP_LABEL (insn)) < max_uid) - for (target = jump_chain[INSN_UID (JUMP_LABEL (insn))]; - target != 0 && newjpos == 0; - target = jump_chain[INSN_UID (target)]) - if (target != insn - && JUMP_LABEL (target) == JUMP_LABEL (insn) - /* Ignore TARGET if it's deleted. */ - && ! INSN_DELETED_P (target)) - find_cross_jump (insn, target, 2, - &newjpos, &newlpos); - - if (newjpos != 0) - { - do_cross_jump (insn, newjpos, newlpos); - changed = 1; - next = insn; - } - } - } - } - else if (GET_CODE (insn) == JUMP_INSN - && GET_CODE (PATTERN (insn)) == RETURN) - { - /* Return insns all "jump to the same place" - so we can cross-jump between any two of them. */ - if (cross_jump) - { - rtx newjpos, newlpos, target; - - newjpos = 0; - - /* If cannot cross jump to code before the label, - see if we can cross jump to another jump to - the same label. */ - /* Try each other jump to this label. */ - for (target = jump_chain[0]; - target != 0 && newjpos == 0; - target = jump_chain[INSN_UID (target)]) - if (target != insn - && ! INSN_DELETED_P (target) - && GET_CODE (PATTERN (target)) == RETURN) - find_cross_jump (insn, target, 2, - &newjpos, &newlpos); - - if (newjpos != 0) - { - do_cross_jump (insn, newjpos, newlpos); - changed = 1; - next = insn; - } - } - } - - } - - first = 0; - } - - /* See if there is still a NOTE_INSN_FUNCTION_END in this function. - If so, delete it, and record that this function can drop off the end. */ - - insn = last_insn; - { - int n_labels = 1; - while (insn - /* One label can follow the end-note: the return label. */ - && ((GET_CODE (insn) == CODE_LABEL && n_labels-- > 0) - /* Ordinary insns can follow it if returning a structure. */ - || GET_CODE (insn) == INSN - /* If machine uses explicit RETURN insns, no epilogue, - then one of them follows the note. */ - || (GET_CODE (insn) == JUMP_INSN - && GET_CODE (PATTERN (insn)) == RETURN) - /* Other kinds of notes can follow also. */ - || (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END))) - insn = PREV_INSN (insn); - } - if (insn && GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_END) - { - extern int current_function_returns_null; - current_function_returns_null = 1; - delete_insn (insn); - } -} - -/* Compare the instructions before insn E1 with those before E2. - Assume E1 is a jump that jumps to label E2 - (that is not always true but it might as well be). - Find the longest possible equivalent sequences - and store the first insns of those sequences into *F1 and *F2. - Store zero there if no equivalent preceding instructions are found. - - We give up if we find a label in stream 1. - Actually we could transfer that label into stream 2. */ - -static void -find_cross_jump (e1, e2, minimum, f1, f2) - rtx e1, e2; - int minimum; - rtx *f1, *f2; -{ - register rtx i1 = e1, i2 = e2; - register rtx p1, p2; - - rtx last1 = 0, last2 = 0; - rtx afterlast1 = 0, afterlast2 = 0; - - *f1 = 0; - *f2 = 0; - - while (1) - { - i1 = PREV_INSN (i1); - while (i1 && GET_CODE (i1) == NOTE) - i1 = PREV_INSN (i1); - - i2 = PREV_INSN (i2); - while (i2 && (GET_CODE (i2) == NOTE || GET_CODE (i2) == CODE_LABEL)) - i2 = PREV_INSN (i2); - - if (i1 == 0) - break; - - /* Don't allow the range of insns preceding E1 or E2 - to include the other (E2 or E1). */ - if (i2 == e1 || i1 == e2) - break; - - /* If we will get to this code by jumping, those jumps will be - tensioned to go directly to the new label (before I2), - so this cross-jumping won't cost extra. So reduce the minimum. */ - if (GET_CODE (i1) == CODE_LABEL) - { - --minimum; - break; - } - - if (i2 == 0 || GET_CODE (i1) != GET_CODE (i2)) - break; - - p1 = PATTERN (i1); - p2 = PATTERN (i2); - - if (GET_CODE (p1) != GET_CODE (p2) - || !rtx_renumbered_equal_p (p1, p2)) - { - /* Insns fail to match; cross jumping is limited to the following - insns. */ - - /* Don't allow the insn after a compare to be shared by cross-jumping - unless the compare is also shared. - Here, if either of these non-matching insns is a compare, - exclude the following insn from possible cross-jumping. */ - if (sets_cc0_p (p1) || sets_cc0_p (p2)) - last1 = afterlast1, last2 = afterlast2, ++minimum; - - /* If cross-jumping here will feed a jump-around-jump optimization, - this jump won't cost extra, so reduce the minimum. */ - if (GET_CODE (i1) == JUMP_INSN - && JUMP_LABEL (i1) - && prev_real_insn (JUMP_LABEL (i1)) == e1) - --minimum; - break; - } - - if (GET_CODE (p1) != USE && GET_CODE (p1) != CLOBBER) - { - /* Ok, this insn is potentially includable in a cross-jump here. */ - afterlast1 = last1, afterlast2 = last2; - last1 = i1, last2 = i2, --minimum; - } - } - - if (minimum <= 0 && last1 != 0) - *f1 = last1, *f2 = last2; -} - -static void -do_cross_jump (insn, newjpos, newlpos) - rtx insn, newjpos, newlpos; -{ - register rtx label; - /* Find an existing label at this point - or make a new one if there is none. */ - label = PREV_INSN (newlpos); - while (label && GET_CODE (label) == NOTE) - label = PREV_INSN (label); - - if (label == 0 || GET_CODE (label) != CODE_LABEL) - { - label = gen_label_rtx (); - emit_label_after (label, PREV_INSN (newlpos)); - LABEL_NUSES (label) = 0; - } - /* Make the same jump insn jump to the new point. */ - if (GET_CODE (PATTERN (insn)) == RETURN) - { - extern rtx gen_jump (); - PATTERN (insn) = gen_jump (label); - INSN_CODE (insn) = -1; - JUMP_LABEL (insn) = label; - LABEL_NUSES (label)++; - } - else - redirect_jump (insn, label); - /* Delete the matching insns before the jump. */ - newjpos = PREV_INSN (newjpos); - while (NEXT_INSN (newjpos) != insn) - /* Don't delete line numbers. */ - if (GET_CODE (NEXT_INSN (newjpos)) != NOTE) - delete_insn (NEXT_INSN (newjpos)); - else - newjpos = NEXT_INSN (newjpos); -} - -/* Move all block-beg and block-end notes between START and END - out before START. Assume neither START nor END is such a note. */ - -static void -squeeze_block_notes (start, end) - rtx start, end; -{ - rtx insn; - rtx next; - - for (insn = start; insn != end; insn = next) - { - next = NEXT_INSN (insn); - if (GET_CODE (insn) == NOTE - && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END - || NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG)) - { - rtx prev = PREV_INSN (insn); - PREV_INSN (insn) = PREV_INSN (start); - NEXT_INSN (insn) = start; - NEXT_INSN (PREV_INSN (insn)) = insn; - PREV_INSN (NEXT_INSN (insn)) = insn; - NEXT_INSN (prev) = next; - PREV_INSN (next) = prev; - } - } -} - -/* Return 1 if INSN is a jump that jumps to right after TARGET - only on the condition that TARGET itself would drop through. - Assumes that TARGET is a conditional jump. */ - -static int -jump_back_p (insn, target) - rtx insn, target; -{ - rtx cinsn, ctarget, prev; - enum rtx_code codei, codet; - - if (simplejump_p (insn) || ! condjump_p (insn) - || simplejump_p (target)) - return 0; - if (target != prev_real_insn (JUMP_LABEL (insn))) - return 0; - - /* Verify that the condition code was based on a fixed-point computation. - Using reverse_condition is invalid for IEEE floating point with nans. */ - prev = prev_real_insn (insn); - if (! (prev != 0 - && GET_CODE (prev) == INSN - && GET_CODE (PATTERN (prev)) == SET - && SET_DEST (PATTERN (prev)) == cc0_rtx - && (GET_MODE_CLASS (GET_MODE (SET_SRC (PATTERN (prev)))) == MODE_INT - || (GET_CODE (SET_SRC (PATTERN (prev))) == COMPARE - && (GET_MODE_CLASS (GET_MODE (XEXP (SET_SRC (PATTERN (prev)), 0))) - == MODE_INT))))) - return 0; - - cinsn = XEXP (SET_SRC (PATTERN (insn)), 0); - ctarget = XEXP (SET_SRC (PATTERN (target)), 0); - - codei = GET_CODE (cinsn); - codet = GET_CODE (ctarget); - if (XEXP (SET_SRC (PATTERN (insn)), 1) == pc_rtx) - - codei = reverse_condition (codei); - if (XEXP (SET_SRC (PATTERN (target)), 2) == pc_rtx) - codet = reverse_condition (codet); - return (codei == codet - && rtx_renumbered_equal_p (XEXP (cinsn, 0), XEXP (ctarget, 0)) - && rtx_renumbered_equal_p (XEXP (cinsn, 1), XEXP (ctarget, 1))); -} - -/* Given an rtx-code for a comparison, return the code - for the negated comparison. - WATCH OUT! reverse_condition is not safe to use on a jump - that might be acting on the results of an IEEE floating point comparison, - because of the special treatment of non-signaling nans in comparisons. */ - -static enum rtx_code -reverse_condition (code) - enum rtx_code code; -{ - switch (code) - { - case EQ: - return NE; - - case NE: - return EQ; - - case GT: - return LE; - - case GE: - return LT; - - case LT: - return GE; - - case LE: - return GT; - - case GTU: - return LEU; - - case GEU: - return LTU; - - case LTU: - return GEU; - - case LEU: - return GTU; - - default: - abort (); - return UNKNOWN; - } -} - -/* Return 1 if INSN is an unconditional jump and nothing else. */ - -int -simplejump_p (insn) - rtx insn; -{ - register rtx x = PATTERN (insn); - if (GET_CODE (x) != SET) - return 0; - if (GET_CODE (SET_DEST (x)) != PC) - return 0; - if (GET_CODE (SET_SRC (x)) != LABEL_REF) - return 0; - return 1; -} - -/* Return nonzero if INSN is a (possibly) conditional jump - and nothing more. */ - -int -condjump_p (insn) - rtx insn; -{ - register rtx x = PATTERN (insn); - if (GET_CODE (x) != SET) - return 0; - if (GET_CODE (SET_DEST (x)) != PC) - return 0; - if (GET_CODE (SET_SRC (x)) == LABEL_REF) - return 1; - if (GET_CODE (SET_SRC (x)) != IF_THEN_ELSE) - return 0; - if (XEXP (SET_SRC (x), 2) == pc_rtx - && GET_CODE (XEXP (SET_SRC (x), 1)) == LABEL_REF) - return 1; - if (XEXP (SET_SRC (x), 1) == pc_rtx - && GET_CODE (XEXP (SET_SRC (x), 2)) == LABEL_REF) - return 1; - return 0; -} - -/* Return 1 if X is an RTX that does nothing but set the condition codes - and CLOBBER or USE registers. - Return -1 if X does explicitly set the condition codes, - but also does other things. */ - -int -sets_cc0_p (x) - rtx x; -{ - if (GET_CODE (x) == SET && SET_DEST (x) == cc0_rtx) - return 1; - if (GET_CODE (x) == PARALLEL) - { - int i; - int sets_cc0 = 0; - int other_things = 0; - for (i = XVECLEN (x, 0) - 1; i >= 0; i--) - { - if (GET_CODE (XVECEXP (x, 0, i)) == SET - && SET_DEST (XVECEXP (x, 0, i)) == cc0_rtx) - sets_cc0 = 1; - else if (GET_CODE (XVECEXP (x, 0, i)) == SET) - other_things = 1; - } - return ! sets_cc0 ? 0 : other_things ? -1 : 1; - } - return 0; -} - -/* Return 1 if in between BEG and END there is no CODE_LABEL insn. */ - -int -no_labels_between_p (beg, end) - rtx beg, end; -{ - register rtx p; - for (p = beg; p != end; p = NEXT_INSN (p)) - if (GET_CODE (p) == CODE_LABEL) - return 0; - return 1; -} - -/* Return the last INSN, CALL_INSN or JUMP_INSN before LABEL; - or 0, if there is none. */ - -rtx -prev_real_insn (label) - rtx label; -{ - register rtx insn = PREV_INSN (label); - register RTX_CODE code; - - while (1) - { - if (insn == 0) - return 0; - code = GET_CODE (insn); - if (code == INSN || code == CALL_INSN || code == JUMP_INSN) - break; - insn = PREV_INSN (insn); - } - - return insn; -} - -/* Return the next INSN, CALL_INSN or JUMP_INSN after LABEL; - or 0, if there is none. */ - -rtx -next_real_insn (label) - rtx label; -{ - register rtx insn = NEXT_INSN (label); - register RTX_CODE code; - - while (1) - { - if (insn == 0) - return insn; - code = GET_CODE (insn); - if (code == INSN || code == CALL_INSN || code == JUMP_INSN) - break; - insn = NEXT_INSN (insn); - } - - return insn; -} - -/* Return the next CODE_LABEL after the insn INSN, or 0 if there is none. */ - -rtx -next_label (insn) - rtx insn; -{ - do insn = NEXT_INSN (insn); - while (insn != 0 && GET_CODE (insn) != CODE_LABEL); - return insn; -} - -/* Follow any unconditional jump at LABEL; - return the ultimate label reached by any such chain of jumps. - If LABEL is not followed by a jump, return LABEL. - If IGNORE_LOOPS is 0, we do not chain across a NOTE_INSN_LOOP_BEG. */ - -static rtx -follow_jumps (label, ignore_loops) - rtx label; - int ignore_loops; -{ - register rtx insn; - register rtx next; - register rtx value = label; - register int depth; - - for (depth = 0; - (depth < 10 - && (insn = next_real_insn (value)) != 0 - && GET_CODE (insn) == JUMP_INSN - && JUMP_LABEL (insn) != 0 - && (next = NEXT_INSN (insn)) - && GET_CODE (next) == BARRIER); - depth++) - { - /* Don't chain through the insn that jumps into a loop - from outside the loop, - since that would create multiple loop entry jumps - and prevent loop optimization. */ - rtx tem; - if (!ignore_loops) - for (tem = value; tem != insn; tem = NEXT_INSN (tem)) - if (GET_CODE (tem) == NOTE - && NOTE_LINE_NUMBER (tem) == NOTE_INSN_LOOP_BEG) - return value; - - /* If we have found a cycle, make the insn jump to itself. */ - if (JUMP_LABEL (insn) == label) - break; - value = JUMP_LABEL (insn); - } - return value; -} - -/* Assuming that field IDX of X is a vector of label_refs, - replace each of them by the ultimate label reached by it. - Return nonzero if a change is made. - If IGNORE_LOOPS is 0, we do not chain across a NOTE_INSN_LOOP_BEG. */ - -static int -tension_vector_labels (x, idx, ignore_loops) - register rtx x; - register int idx; - int ignore_loops; -{ - int changed = 0; - register int i; - for (i = XVECLEN (x, idx) - 1; i >= 0; i--) - { - register rtx olabel = XEXP (XVECEXP (x, idx, i), 0); - register rtx nlabel = follow_jumps (olabel, ignore_loops); - if (nlabel != olabel) - { - XEXP (XVECEXP (x, idx, i), 0) = nlabel; - ++LABEL_NUSES (nlabel); - if (--LABEL_NUSES (olabel) == 0) - delete_insn (olabel); - changed = 1; - } - } - return changed; -} - -/* Find all CODE_LABELs referred to in X, - and increment their use counts. - Also store one of them in JUMP_LABEL (INSN) if INSN is nonzero. - Also, when there are consecutive labels, - canonicalize on the last of them. - - Note that two labels separated by a loop-beginning note - must be kept distinct if we have not yet done loop-optimization, - because the gap between them is where loop-optimize - will want to move invariant code to. CROSS_JUMP tells us - that loop-optimization is done with. */ - -static void -mark_jump_label (x, insn, cross_jump) - register rtx x; - rtx insn; - int cross_jump; -{ - register RTX_CODE code = GET_CODE (x); - register int i; - register char *fmt; - - if (code == LABEL_REF) - { - register rtx label = XEXP (x, 0); - register rtx next; - if (GET_CODE (label) != CODE_LABEL) - return; - /* If there are other labels following this one, - replace it with the last of the consecutive labels. */ - for (next = NEXT_INSN (label); next; next = NEXT_INSN (next)) - { - if (GET_CODE (next) == CODE_LABEL) - label = next; - else if (GET_CODE (next) != NOTE - || NOTE_LINE_NUMBER (next) == NOTE_INSN_LOOP_BEG - || NOTE_LINE_NUMBER (next) == NOTE_INSN_FUNCTION_END) - break; - } - XEXP (x, 0) = label; - ++LABEL_NUSES (label); - if (insn) - JUMP_LABEL (insn) = label; - return; - } - - /* Do walk the labels in a vector, - but don't set its JUMP_LABEL. */ - if (code == ADDR_VEC || code == ADDR_DIFF_VEC) - insn = 0; - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code); i >= 0; i--) - { - if (fmt[i] == 'e') - mark_jump_label (XEXP (x, i), insn, cross_jump); - else if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - mark_jump_label (XVECEXP (x, i, j), insn, cross_jump); - } - } -} - -/* If all INSN does is set the pc, delete it, - and delete the insn that set the condition codes for it - if that's what the previous thing was. */ - -static void -delete_jump (insn) - rtx insn; -{ - register rtx x = PATTERN (insn); - register rtx prev; - - if (GET_CODE (x) == SET - && GET_CODE (SET_DEST (x)) == PC) - { - prev = PREV_INSN (insn); - delete_insn (insn); - /* We assume that at this stage - CC's are always set explicitly - and always immediately before the jump that - will use them. So if the previous insn - exists to set the CC's, delete it - (unless it performs auto-increments, etc.). */ - while (prev && GET_CODE (prev) == NOTE) - prev = PREV_INSN (prev); - if (prev && GET_CODE (prev) == INSN - && sets_cc0_p (PATTERN (prev)) > 0 - && !find_reg_note (prev, REG_INC, 0)) - delete_insn (prev); - } -} - -/* Delete insn INSN from the chain of insns and update label ref counts. - May delete some following insns as a consequence; may even delete - a label elsewhere and insns that follow it. - - Returns the first insn after INSN that was not deleted. */ - -rtx -delete_insn (insn) - register rtx insn; -{ - register rtx next = NEXT_INSN (insn); - register rtx prev = PREV_INSN (insn); - - while (next && INSN_DELETED_P (next)) - next = NEXT_INSN (next); - - /* This insn is already deleted => return first following nondeleted. */ - if (INSN_DELETED_P (insn)) - return next; - - /* Mark this insn as deleted. */ - - INSN_DELETED_P (insn) = 1; - - /* If instruction is followed by a barrier, - delete the barrier too. */ - - if (next != 0 && GET_CODE (next) == BARRIER) - { - INSN_DELETED_P (next) = 1; - next = NEXT_INSN (next); - } - - /* Patch out INSN (and the barrier if any) */ - - if (optimize) - { - if (prev) - NEXT_INSN (prev) = next; - - if (next) - PREV_INSN (next)= prev; - - if (prev && NEXT_INSN (prev) == 0) - set_last_insn (prev); - } - - /* If deleting a jump, decrement the count of the label, - and delete the label if it is now unused. */ - - if (GET_CODE (insn) == JUMP_INSN && JUMP_LABEL (insn)) - if (--LABEL_NUSES (JUMP_LABEL (insn)) == 0) - { - /* This can delete NEXT or PREV, - either directly if NEXT is JUMP_LABEL (INSN), - or indirectly through more levels of jumps. */ - delete_insn (JUMP_LABEL (insn)); - /* I feel a little doubtful about this loop, - but I see no clean and sure alternative way - to find the first insn after INSN that is not now deleted. - I hope this works. */ - while (next && INSN_DELETED_P (next)) - next = NEXT_INSN (next); - return next; - } - - while (prev && (INSN_DELETED_P (prev) || GET_CODE (prev) == NOTE)) - prev = PREV_INSN (prev); - - /* If INSN was a label and a dispatch table follows it, - delete the dispatch table. The tablejump must have gone already. - It isn't useful to fall through into a table. */ - - if (GET_CODE (insn) == CODE_LABEL - && NEXT_INSN (insn) != 0 - && GET_CODE (NEXT_INSN (insn)) == JUMP_INSN - && GET_CODE (PATTERN (NEXT_INSN (insn))) == ADDR_VEC) - next = delete_insn (NEXT_INSN (insn)); - - /* If INSN was a label, delete insns following it if now unreachable. */ - - if (GET_CODE (insn) == CODE_LABEL && prev - && GET_CODE (prev) == BARRIER) - { - register RTX_CODE code; - while (next != 0 - && ((code = GET_CODE (next)) == INSN - || code == JUMP_INSN || code == CALL_INSN - || code == NOTE)) - { - if (code == NOTE - && NOTE_LINE_NUMBER (next) != NOTE_INSN_FUNCTION_END) - next = NEXT_INSN (next); - else - /* Note: if this deletes a jump, it can cause more - deletion of unreachable code, after a different label. - As long as the value from this recursive call is correct, - this invocation functions correctly. */ - next = delete_insn (next); - } - } - - return next; -} - -/* Advance from INSN till reaching something not deleted - then return that. May return INSN itself. */ - -rtx -next_nondeleted_insn (insn) - rtx insn; -{ - while (INSN_DELETED_P (insn)) - insn = NEXT_INSN (insn); - return insn; -} - -/* Delete a range of insns from FROM to TO, inclusive. - This is for the sake of peephole optimization, so assume - that whatever these insns do will still be done by a new - peephole insn that will replace them. */ - -void -delete_for_peephole (from, to) - register rtx from, to; -{ - register rtx insn = from; - - while (1) - { - register rtx next = NEXT_INSN (insn); - register rtx prev = PREV_INSN (insn); - - if (GET_CODE (insn) != NOTE) - { - INSN_DELETED_P (insn) = 1; - - /* Patch this insn out of the chain. */ - /* We don't do this all at once, because we - must preserve all NOTEs. */ - if (prev) - NEXT_INSN (prev) = next; - - if (next) - PREV_INSN (next) = prev; - } - - if (insn == to) - break; - insn = next; - } - - /* Note that if TO is an unconditional jump - we *do not* delete the BARRIER that follows, - since the peephole that replaces this sequence - is also an unconditional jump in that case. */ -} - -/* Invert the condition of the jump JUMP, and make it jump - to label NLABEL instead of where it jumps now. */ - -void -invert_jump (jump, nlabel) - rtx jump, nlabel; -{ - register rtx olabel = JUMP_LABEL (jump); - invert_exp (PATTERN (jump), olabel, nlabel); - JUMP_LABEL (jump) = nlabel; - ++LABEL_NUSES (nlabel); - INSN_CODE (jump) = -1; - - if (--LABEL_NUSES (olabel) == 0) - delete_insn (olabel); -} - -/* Invert the jump condition of rtx X, - and replace OLABEL with NLABEL throughout. - This is used in do_jump as well as in this file. */ - -void -invert_exp (x, olabel, nlabel) - rtx x; - rtx olabel, nlabel; -{ - register RTX_CODE code; - register int i; - register char *fmt; - - if (x == 0) - return; - - code = GET_CODE (x); - if (code == IF_THEN_ELSE) - { - /* Inverting the jump condition of an IF_THEN_ELSE - means exchanging the THEN-part with the ELSE-part. */ - register rtx tem = XEXP (x, 1); - XEXP (x, 1) = XEXP (x, 2); - XEXP (x, 2) = tem; - } - - if (code == LABEL_REF) - { - if (XEXP (x, 0) == olabel) - XEXP (x, 0) = nlabel; - return; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - invert_exp (XEXP (x, i), olabel, nlabel); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - invert_exp (XVECEXP (x, i, j), olabel, nlabel); - } - } -} - -/* Make jump JUMP jump to label NLABEL instead of where it jumps now. - If the old jump target label is unused as a result, - it and the code following it may be deleted. */ - -void -redirect_jump (jump, nlabel) - rtx jump, nlabel; -{ - register rtx olabel = JUMP_LABEL (jump); - - if (nlabel == olabel) - return; - - redirect_exp (PATTERN (jump), olabel, nlabel); - JUMP_LABEL (jump) = nlabel; - ++LABEL_NUSES (nlabel); - INSN_CODE (jump) = -1; - - if (--LABEL_NUSES (olabel) == 0) - delete_insn (olabel); -} - -/* Throughout the rtx X, - alter (LABEL_REF OLABEL) to (LABEL_REF NLABEL). */ - -static void -redirect_exp (x, olabel, nlabel) - rtx x; - rtx olabel, nlabel; -{ - register RTX_CODE code = GET_CODE (x); - register int i; - register char *fmt; - - if (code == LABEL_REF) - { - if (XEXP (x, 0) == olabel) - XEXP (x, 0) = nlabel; - return; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - redirect_exp (XEXP (x, i), olabel, nlabel); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - redirect_exp (XVECEXP (x, i, j), olabel, nlabel); - } - } -} - -/* Like rtx_equal_p except that it considers two REGs as equal - if they renumber to the same value. */ - -int -rtx_renumbered_equal_p (x, y) - rtx x, y; -{ - register int i; - register RTX_CODE code = GET_CODE (x); - register char *fmt; - - if (x == y) - return 1; - if ((code == REG || (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG)) - && (GET_CODE (y) == REG || (GET_CODE (y) == SUBREG - && GET_CODE (SUBREG_REG (y)) == REG))) - { - register int j; - - if (GET_MODE (x) != GET_MODE (y)) - return 0; - - if (code == SUBREG) - { - i = REGNO (SUBREG_REG (x)); - if (reg_renumber[i] >= 0) - i = reg_renumber[i]; - i += SUBREG_WORD (x); - } - else - { - i = REGNO (x); - if (reg_renumber[i] >= 0) - i = reg_renumber[i]; - } - if (GET_CODE (y) == SUBREG) - { - j = REGNO (SUBREG_REG (y)); - if (reg_renumber[j] >= 0) - j = reg_renumber[j]; - j += SUBREG_WORD (y); - } - else - { - j = REGNO (y); - if (reg_renumber[j] >= 0) - j = reg_renumber[j]; - } - return i == j; - } - /* Now we have disposed of all the cases - in which different rtx codes can match. */ - if (code != GET_CODE (y)) - return 0; - switch (code) - { - case PC: - case CC0: - case ADDR_VEC: - case ADDR_DIFF_VEC: - return 0; - - case CONST_INT: - return XINT (x, 0) == XINT (y, 0); - - case LABEL_REF: - /* Two label-refs are equivalent if they point at labels - in the same position in the instruction stream. */ - return (next_real_insn (XEXP (x, 0)) - == next_real_insn (XEXP (y, 0))); - - case SYMBOL_REF: - return XSTR (x, 0) == XSTR (y, 0); - } - - /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */ - - if (GET_MODE (x) != GET_MODE (y)) - return 0; - - /* Compare the elements. If any pair of corresponding elements - fail to match, return 0 for the whole things. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - register int j; - switch (fmt[i]) - { - case 'i': - if (XINT (x, i) != XINT (y, i)) - return 0; - break; - - case 's': - if (strcmp (XSTR (x, i), XSTR (y, i))) - return 0; - break; - - case 'e': - if (! rtx_renumbered_equal_p (XEXP (x, i), XEXP (y, i))) - return 0; - break; - - case '0': - break; - - case 'E': - if (XVECLEN (x, i) != XVECLEN (y, i)) - return 0; - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - if (!rtx_renumbered_equal_p (XVECEXP (x, i, j), XVECEXP (y, i, j))) - return 0; - break; - - default: - abort (); - } - } - return 1; -} - -/* If X is a hard register or equivalent to one or a subregister of one, - return the hard register number. Otherwise, return -1. - Any rtx is valid for X. */ - -int -true_regnum (x) - rtx x; -{ - if (GET_CODE (x) == REG) - { - if (REGNO (x) >= FIRST_PSEUDO_REGISTER) - return reg_renumber[REGNO (x)]; - return REGNO (x); - } - if (GET_CODE (x) == SUBREG) - { - int base = true_regnum (SUBREG_REG (x)); - if (base >= 0 && base < FIRST_PSEUDO_REGISTER) - return SUBREG_WORD (x) + base; - } - return -1; -} diff --git a/gnu/usr.bin/gcc1/cc1/limits.h b/gnu/usr.bin/gcc1/cc1/limits.h deleted file mode 100644 index 433b534070..0000000000 --- a/gnu/usr.bin/gcc1/cc1/limits.h +++ /dev/null @@ -1,43 +0,0 @@ -/* Number of bits in a `char'. */ -#define CHAR_BIT 8 - -/* No multibyte characters supported yet. */ -#define MB_LEN_MAX 1 - -/* Minimum and maximum values a `signed char' can hold. */ -#define SCHAR_MIN (-128) -#define SCHAR_MAX 127 - -/* Maximum value an `unsigned char' can hold. (Minimum is 0). */ -#define UCHAR_MAX 255U - -/* Minimum and maximum values a `char' can hold. */ -#ifdef __CHAR_UNSIGNED__ -#define CHAR_MIN 0 -#define CHAR_MAX 255U -#else -#define CHAR_MIN (-128) -#define CHAR_MAX 127 -#endif - -/* Minimum and maximum values a `signed short int' can hold. */ -#define SHRT_MIN (-32768) -#define SHRT_MAX 32767 - -/* Maximum value an `unsigned short int' can hold. (Minimum is 0). */ -#define USHRT_MAX 65535U - -/* Minimum and maximum values a `signed int' can hold. */ -#define INT_MIN (-INT_MAX-1) -#define INT_MAX 2147483647 - -/* Maximum value an `unsigned int' can hold. (Minimum is 0). */ -#define UINT_MAX 4294967295U - -/* Minimum and maximum values a `signed long int' can hold. - (Same as `int'). */ -#define LONG_MIN (-LONG_MAX-1) -#define LONG_MAX 2147483647 - -/* Maximum value an `unsigned long int' can hold. (Minimum is 0). */ -#define ULONG_MAX 4294967295U diff --git a/gnu/usr.bin/gcc1/cc1/local-alloc.c b/gnu/usr.bin/gcc1/cc1/local-alloc.c deleted file mode 100644 index beb3799e79..0000000000 --- a/gnu/usr.bin/gcc1/cc1/local-alloc.c +++ /dev/null @@ -1,1227 +0,0 @@ -/* Allocate registers within a basic block, for GNU compiler. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* Allocation of hard register numbers to pseudo registers is done in - two passes. In this pass we consider only regs that are born and - die once within one basic block. We do this one basic block at a - time. Then the next pass allocates the registers that remain. - Two passes are used because this pass uses methods that work only - on linear code, but that do a better job than the general methods - used in global_alloc, and more quickly too. - - The assignments made are recorded in the vector reg_renumber - whose space is allocated here. The rtl code itself is not altered. - - We assign each instruction in the basic block a number - which is its order from the beginning of the block. - Then we can represent the lifetime of a pseudo register with - a pair of numbers, and check for conflicts easily. - We can record the availability of hard registers with a - HARD_REG_SET for each instruction. The HARD_REG_SET - contains 0 or 1 for each hard reg. - - To avoid register shuffling, we tie registers together when one - dies by being copied into another, or dies in an instruction that - does arithmetic to produce another. The tied registers are - allocated as one. Registers with different reg class preferences - can never be tied unless the class preferred by one is a subclass - of the one preferred by the other. - - Tying is represented with "quantity numbers". - A non-tied register is given a new quantity number. - Tied registers have the same quantity number. - - We have provision to exempt registers, even when they are contained - within the block, that can be tied to others that are not contained in it. - This is so that global_alloc could process them both and tie them then. - But this is currently disabled since tying in global_alloc is not - yet implemented. */ - -#include -#include "config.h" -#include "rtl.h" -#include "flags.h" -#include "basic-block.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "insn-config.h" -#include "recog.h" - -/* What about hardware registers used and set within same insn? - Will that ever happen for a non-fixed register? - Our lifetime-tracking for hardware registers would lose. - [This caution is an old comment that may be obsolete; - I think there is no longer a problem, but I'm not sure.] */ - -/* Next quantity number available for allocation. */ - -static int next_qty; - -/* In all the following vectors indexed by quantity number, - only elements at indices >= FIRST_PSEUDO_REGISTER are actually used. */ - -/* Element Q is the hard reg number chosen for quantity Q, - or -1 if none was found. */ - -static short *qty_phys_reg; - -/* Element Q is the hard reg number suggested for quantity Q, - or -1 if no specific suggestion. */ - -static short *qty_phys_sugg; - -/* Element Q is the number of refs to quantity Q. */ - -static short *qty_n_refs; - -/* Element Q is a reg class contained in (smaller than) the - preferred classes of all the pseudo regs that are tied in quantity Q. - This is the preferred class for allocating that quantity. */ - -static enum reg_class *qty_min_class; - -/* Insn number (counting from head of basic block) - where quantity Q was born. -1 if birth has not been recorded. */ - -static int *qty_birth; - -/* Insn number (counting from head of basic block) - where quantity Q died. Due to the way tying is done, - and the fact that we consider in this pass only regs that die but once, - a quantity can die only once. Each quantity's life span - is a set of consecutive insns. -1 if death has not been recorded. */ - -static int *qty_death; - -/* Number of words needed to hold the data in quantity Q. - This depends on its machine mode. It is used for these purposes: - 1. If it is 0, the qty is not really in use and is not allocated. - 2. It is used in computing the relative importances of qtys, - which determines the order in which we look for regs for them. - 3. It is used in rules that prevent tying several registers of - different sizes in a way that is geometrically impossible - (see combine_regs). */ - -static int *qty_size; - -/* This holds the mode of the registers that are tied to qty Q, - or VOIDmode if registers with differing modes are tied together. */ - -static enum machine_mode *qty_mode; - -/* Number of times a reg tied to qty Q lives across a CALL_INSN. */ - -static int *qty_n_calls_crossed; - -/* Nonzero means don't allocate qty Q if we can't get its preferred class. */ - -static char *qty_preferred_or_nothing; - -/* reg_qty[N] (where N is a pseudo reg number) - is the qty number of that reg (which is >= FIRST_PSEUDO_REGISTER), - or -1 if (REG N) is not local to the current basic block, - or -2 if not known yet. - - If N is < FIRST_PSEUDO_REGISTER, reg_qty[N] is -1. */ - -static int *reg_qty; - -/* The offset (in words) of register N within its quantity. - This can be nonzero if register N is SImode, and has been tied - to a subreg of a DImode register. */ - -static int *reg_offset; - -/* Vector of substitutions of register numbers, - used to map pseudo regs into hardware regs. - This is set up as a result of register allocation. - Element N is the hard reg assigned to pseudo reg N, - or is -1 if no hard reg was assigned. - If N is a hard reg number, element N is N. */ - -short *reg_renumber; - -/* Set of hard registers live at the current point in the scan - of the instructions in a basic block. */ - -static HARD_REG_SET regs_live; - -/* Indexed by insn-number-within-basic-block, - a set or hard registers live *after* that insn. */ - -static HARD_REG_SET *regs_live_at; - -/* Nonzero if a CALL_INSN has been scanned - but we have not yet seen a reference to the value returned. */ - -static int call_seen; - -/* Communicate local vars `insn_number' and `insn' - from `block_alloc' to `reg_is_set' and `wipe_dead_reg'. */ -static int this_insn_number; -static rtx this_insn; - -static void block_alloc (); -static int combine_regs (); -static void wipe_dead_reg (); -static int find_free_reg (); -static void reg_is_born (); -static void reg_is_set (); -static void mark_life (); -static void post_mark_life (); -static int qty_compare (); -static int qty_compare_1 (); -static int reg_meets_class_p (); -static int reg_class_subset_p (); -static int reg_classes_overlap_p (); -static void update_qty_class (); - -/* Allocate a new quantity (new within current basic block) - for register number REGNO which is born in insn number INSN_NUMBER - within the block. MODE and SIZE are info on reg REGNO. */ - -static void -alloc_qty (regno, mode, size, insn_number) - int regno; - enum machine_mode mode; - int size, insn_number; -{ - register int qty = next_qty++; - reg_qty[regno] = qty; - reg_offset[regno] = 0; - qty_size[qty] = size; - qty_mode[qty] = mode; - qty_birth[qty] = insn_number; - qty_n_calls_crossed[qty] = reg_n_calls_crossed[regno]; - qty_min_class[qty] = reg_preferred_class (regno); - qty_preferred_or_nothing[qty] = reg_preferred_or_nothing (regno); - qty_n_refs[qty] = reg_n_refs[regno]; -} - -/* Main entry point of this file. */ - -void -local_alloc () -{ - register int b, i; - - /* Allocate vectors of temporary data. - See the declarations of these variables, above, - for what they mean. */ - - qty_phys_reg = (short *) alloca (max_regno * sizeof (short)); - qty_phys_sugg = (short *) alloca (max_regno * sizeof (short)); - qty_birth = (int *) alloca (max_regno * sizeof (int)); - qty_death = (int *) alloca (max_regno * sizeof (int)); - qty_size = (int *) alloca (max_regno * sizeof (int)); - qty_mode = (enum machine_mode *) alloca (max_regno * sizeof (enum machine_mode)); - qty_n_calls_crossed = (int *) alloca (max_regno * sizeof (int)); - qty_min_class = (enum reg_class *) alloca (max_regno * sizeof (enum reg_class)); - qty_preferred_or_nothing = (char *) alloca (max_regno); - qty_n_refs = (short *) alloca (max_regno * sizeof (short)); - - reg_qty = (int *) alloca (max_regno * sizeof (int)); - reg_offset = (int *) alloca (max_regno * sizeof (int)); - - reg_renumber = (short *) oballoc (max_regno * sizeof (short)); - for (i = 0; i < max_regno; i++) - reg_renumber[i] = -1; - - /* This controls only how many elts of the `qty_...' vectors - need to be zero for the first basic block. */ - next_qty = max_regno; - - /* Allocate each block's local registers, block by block. */ - - for (b = 0; b < n_basic_blocks; b++) - { - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - reg_qty[i] = -1; - } - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - { - qty_phys_sugg[i] = -1; - qty_birth[i] = -1; - qty_death[i] = -1; - /* Set reg_qty to -2 for pseudos in this block, -1 for others. */ - if (reg_basic_block[i] == b && reg_n_deaths[i] == 1) - reg_qty[i] = -2; - else - reg_qty[i] = -1; - } - - bzero (reg_offset, max_regno * sizeof (int)); - - /* NEXT_QTY indicates which elements of the `qty_...' - vectors might need to be initialized. Initialize those, - with explicit loop if there are few, else with bzero. */ - - if (next_qty < FIRST_PSEUDO_REGISTER + 6) - { - for (i = FIRST_PSEUDO_REGISTER; i < next_qty; i++) - { - qty_size[i] = 0; - qty_mode[i] = VOIDmode; - qty_min_class[i] = NO_REGS; - qty_preferred_or_nothing[i] = 0; - qty_n_calls_crossed[i] = 0; - qty_n_refs[i] = 0; - } - } - else - { - int clear_length = next_qty - FIRST_PSEUDO_REGISTER; - -#define CLEAR(vector) \ - bzero ((vector) + FIRST_PSEUDO_REGISTER, \ - (sizeof (*(vector))) * clear_length) - - CLEAR (qty_size); - CLEAR (qty_mode); - CLEAR (qty_min_class); - CLEAR (qty_preferred_or_nothing); - CLEAR (qty_n_calls_crossed); - CLEAR (qty_n_refs); - } - - next_qty = FIRST_PSEUDO_REGISTER; - - block_alloc (b); -#ifdef USE_C_ALLOCA - alloca (0); -#endif - } -} - -/* Allocate hard regs to the pseudo regs used only within block number B. - Only the pseudos that die but once can be handled. */ - -static void -block_alloc (b) - int b; -{ - register int i, q; - register rtx insn; - int insn_number = 0; - int insn_count = 0; - short *qty_order; - int *insn_map; - - call_seen = 0; - - /* Count the instructions in the basic block. */ - - insn = basic_block_end[b]; - while (1) - { - if (GET_CODE (insn) != NOTE) - insn_count++; - if (insn == basic_block_head[b]) - break; - insn = PREV_INSN (insn); - } - - /* +1 to leave room for a post_mark_life at the last insn. */ - regs_live_at = (HARD_REG_SET *) alloca ((insn_count + 1) - * sizeof (HARD_REG_SET)); - bzero (regs_live_at, (insn_count + 1) * sizeof (HARD_REG_SET)); - - /* This will be a map from uids to insn-numbers within the block. */ - - insn_map = (int *) alloca (get_max_uid () * sizeof (int)); - - /* Initialize table of hardware registers currently live. */ - -#ifdef HARD_REG_SET - regs_live = *basic_block_live_at_start[b]; -#else - COPY_HARD_REG_SET (regs_live, basic_block_live_at_start[b]); -#endif - - /* This loop scans the instructions of the basic block - and assigns quantities to registers. - It computes which registers to tie. */ - - insn = basic_block_head[b]; - insn_number = 0; - while (1) - { - register rtx body = PATTERN (insn); - - if (GET_CODE (insn) != NOTE) - insn_number++; - insn_map[INSN_UID (insn)] = insn_number; - - if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN - || GET_CODE (insn) == CALL_INSN) - { - register rtx link; - register int win = 0; - register rtx r0, r1; - int combined_regno = -1; - int insn_code_number = recog_memoized (insn); - int commutative = 0; - - this_insn_number = insn_number; - this_insn = insn; - - /* Set COMMUTATIVE if operands 1 and 2 are commutative. */ - if (insn_code_number >= 0 - && insn_n_operands[insn_code_number] > 2 - && insn_operand_constraint[insn_code_number][1][0] == '%') - commutative = 1; - - /* Is this insn suitable for tying two registers? - If so, try doing that. - Suitable insns are (set reg0 reg1) and - (set reg0 (arithop reg1 ...)). - For a commutative operation, try (set reg0 (arithop ... reg1)). - Subregs in place of regs are also ok. - An insn with parallel sets is ok if the first set is suitable. - - If tying is done, WIN is set nonzero. */ - - if (GET_CODE (body) == SET - && (r0 = SET_DEST (body), - GET_CODE (r0) == REG || GET_CODE (r0) == SUBREG) - && (r1 = SET_SRC (body), - GET_CODE (r1) == REG || GET_CODE (r1) == SUBREG)) - win = combine_regs (r1, r0, b, insn_number, insn); - else if (GET_CODE (body) == SET) - { - r0 = SET_DEST (body); - if (GET_CODE (r0) == REG || GET_CODE (r0) == SUBREG) - { - if (GET_RTX_FORMAT (GET_CODE (SET_SRC (body)))[0] == 'e' - && (r1 = XEXP (SET_SRC (body), 0), - GET_CODE (r1) == REG || GET_CODE (r1) == SUBREG)) - win = combine_regs (r1, r0, b, insn_number, insn); - if (win == 0 && commutative - && GET_RTX_FORMAT (GET_CODE (SET_SRC (body)))[1] == 'e' - && (r1 = XEXP (SET_SRC (body), 1), - GET_CODE (r1) == REG || GET_CODE (r1) == SUBREG)) - win = combine_regs (r1, r0, b, insn_number, insn); - } - } - else if (GET_CODE (body) == PARALLEL) - { - rtx set1 = XVECEXP (body, 0, 0); - if (GET_CODE (set1) == SET - && (r0 = SET_DEST (set1), - GET_CODE (r0) == REG || GET_CODE (r0) == SUBREG) - && GET_RTX_FORMAT (GET_CODE (SET_SRC (set1)))[0] == 'e' - && (r1 = XEXP (SET_SRC (set1), 0), - GET_CODE (r1) == REG || GET_CODE (r1) == SUBREG)) - win = combine_regs (r1, r0, b, insn_number, insn); - if (win == 0 && commutative && GET_CODE (set1) == SET - && (r0 = SET_DEST (set1), - GET_CODE (r0) == REG || GET_CODE (r0) == SUBREG) - && GET_RTX_FORMAT (GET_CODE (SET_SRC (set1)))[1] == 'e' - && (r1 = XEXP (SET_SRC (set1), 1), - GET_CODE (r1) == REG || GET_CODE (r1) == SUBREG)) - win = combine_regs (r1, r0, b, insn_number, insn); - } - - /* If registers were just tied, set COMBINED_REGNO - to the number of the register used in this insn - that was tied to the register set in this insn. - This register's qty should not be "killed". */ - - if (win) - { - while (GET_CODE (r1) == SUBREG) - r1 = SUBREG_REG (r1); - combined_regno = REGNO (r1); - } - - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - { - /* Mark the death of everything that dies in this instruction, - except for anything that was just combined. */ - if (XEXP (link, 0) - && REG_NOTE_KIND (link) == REG_DEAD - && combined_regno != REGNO (XEXP (link, 0))) - { -#if 0 /* The mechanism in reg_is_set that checks whether the qty dies here - ought to handle this case properly. */ - if (combined_regno >= 0 && - reg_qty[combined_regno] == reg_qty[REGNO (XEXP (link, 0))]) - /* Here for the death of the quotient in a divmod insn: - something that was born and dead in this insn - but combined with something else that also dies here. - Mark the qty as dying one instruction later. */ - wipe_dead_reg (XEXP (link, 0), insn_number, - insn_number + 1); - else -#endif - wipe_dead_reg (XEXP (link, 0), insn_number, insn_number); - } - /* Also, if this insn introduces a "constant" register, - that could just be replaced by the value it is given here - (which can legitimately be an immediate operand), - tell global-alloc not to allocate it - unless it is used at least twice more. */ - - else if (REG_NOTE_KIND (link) == REG_EQUIV - && GET_CODE (SET_DEST (body)) == REG - && general_operand (XEXP (link, 0), VOIDmode) - /* Don't inhibit allocation of a "constant" register - that we have already tied to something else! */ - && combined_regno < 0 - /* Don't mess with things live during setjmp. */ - && reg_live_length[REGNO (SET_DEST (body))] >= 0) - { - i = REGNO (SET_DEST (body)); - if (reg_n_sets[i] > 1) - { - /* Register is set in another place => not really constant. - cse or flow can cause this to happen. - Ok, forget we ever thought it was constant. */ - GET_MODE (link) = VOIDmode; - } - else if (reg_n_refs[i] <= 2) - { - /* For a parameter copy, do let global-alloc - allocate it; otherwise we would be forced to - have a frame pointer. */ - if (! frame_pointer_needed - && GET_CODE (SET_SRC (PATTERN (insn))) == MEM) - reg_live_length[i] = -2; - else - reg_live_length[i] = -1; - - /* If value is not constant, we have a parameter - or a static chain pointer. Tell local-alloc - as well not to allocate it. */ - if (! CONSTANT_P (SET_SRC (PATTERN (insn)))) - { - reg_basic_block[i] = REG_BLOCK_GLOBAL; - reg_qty[i] = -1; - } - } - else - /* In any case, lower its priority for global-alloc. */ - reg_live_length[i] *= 2; - } - } - - /* Allocate qty numbers for all registers local to this block - that are born (set) in this instruction. - A pseudo that already has a qty is not changed. */ - - note_stores (PATTERN (insn), reg_is_set); - } - if (GET_CODE (insn) == CALL_INSN) - call_seen = 1; - if (insn == basic_block_end[b]) - break; - /* We don't need this for the block's first instruction - since no regs we care about are live before that instruction. - Also we do not allocate space in regs_live_at for that instruction. */ - IOR_HARD_REG_SET (regs_live_at[insn_number], regs_live); - insn = NEXT_INSN (insn); - } - - /* Now every register that is local to this basic block - should have been given a quantity, or else -1 meaning ignore it. - Every quantity should have a known birth (verify this now). - - If a qty's death has not been established, it indicates a dead store. - That is ok if the insn is not entirely dead. - So set the qty'd death to just after its birth. */ - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (reg_basic_block[i] == b && reg_qty[i] >= 0) - { - /* In the case of a register that is used uninitialized, - the code above will miss the actual first use. - So count that first use as the birth. */ - if (qty_birth[reg_qty[i]] > insn_map[INSN_UID (reg_first_use[i])]) - qty_birth[reg_qty[i]] = insn_map[INSN_UID (reg_first_use[i])]; - } - - for (i = FIRST_PSEUDO_REGISTER; i < next_qty; i++) - { - if (qty_birth[i] == -1) - abort (); - if (qty_death[i] == -1) - qty_death[i] = qty_birth[i] + 1; - } - - /* Now order the qtys so we assign them registers - in order of decreasing length of life. */ - qty_order = (short *) alloca (next_qty * sizeof (short)); - for (i = FIRST_PSEUDO_REGISTER; i < next_qty; i++) - qty_order[i] = i; - -#define EXCHANGE(I1, I2) \ - { i = qty_order[I1]; qty_order[I1] = qty_order[I2]; qty_order[I2] = i; } - - if (next_qty == 2 + FIRST_PSEUDO_REGISTER) - { - if (qty_compare (FIRST_PSEUDO_REGISTER, FIRST_PSEUDO_REGISTER + 1) > 0) - EXCHANGE (FIRST_PSEUDO_REGISTER, FIRST_PSEUDO_REGISTER + 1); - } - else if (next_qty == 3 + FIRST_PSEUDO_REGISTER) - { - if (qty_compare (FIRST_PSEUDO_REGISTER, FIRST_PSEUDO_REGISTER + 1) > 0) - EXCHANGE (FIRST_PSEUDO_REGISTER, FIRST_PSEUDO_REGISTER + 1); - if (qty_compare (FIRST_PSEUDO_REGISTER + 1, FIRST_PSEUDO_REGISTER + 2) > 0) - EXCHANGE (FIRST_PSEUDO_REGISTER + 2, FIRST_PSEUDO_REGISTER + 1); - if (qty_compare (FIRST_PSEUDO_REGISTER, FIRST_PSEUDO_REGISTER + 1) > 0) - EXCHANGE (FIRST_PSEUDO_REGISTER, FIRST_PSEUDO_REGISTER + 1); - } - else if (next_qty > 3 + FIRST_PSEUDO_REGISTER) - qsort (qty_order + FIRST_PSEUDO_REGISTER, - next_qty - FIRST_PSEUDO_REGISTER, sizeof (short), qty_compare_1); - - /* Now for each qty that is not a hardware register, - look for a hardware register to put it in. - First try the register class that is cheapest for this qty, - if there is more than one class. */ - - for (i = FIRST_PSEUDO_REGISTER; i < next_qty; i++) - { - q = qty_order[i]; - if (qty_size[q] >= 0) - { - if (N_REG_CLASSES > 1) - { - qty_phys_reg[q] = find_free_reg (qty_min_class[q], - qty_mode[q], q, 0, - qty_birth[q], qty_death[q]); - if (qty_phys_reg[q] >= 0) - continue; - } - - if (!qty_preferred_or_nothing[q]) - qty_phys_reg[q] = find_free_reg (GENERAL_REGS, - qty_mode[q], q, 0, - qty_birth[q], qty_death[q]); - } - } - - /* Now propagate the register assignments - to the pseudo regs belonging to the qtys. */ - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (reg_qty[i] >= 0 && qty_phys_reg[reg_qty[i]] >= 0) - { - reg_renumber[i] = qty_phys_reg[reg_qty[i]] + reg_offset[i]; - } -} - -/* Compare two quantities' priority for getting real registers. - We give quantities with hard-reg suggestions priority over all others. - We give longer-lived quantities higher priority - so that the shorter-lived ones will tend to be in the same places - which gives in general the maximum room for the regs to - be allocated by global-alloc. - Regs with more references are also preferred. */ - -static int -qty_compare (q1, q2) - int q1, q2; -{ - register int tem = (qty_phys_sugg[q2] >= 0) - (qty_phys_sugg[q1] >= 0); - if (tem != 0) return tem; - return -((qty_n_refs[q1] + qty_death[q1] - qty_birth[q1]) * qty_size[q2] - - (qty_n_refs[q2] + qty_death[q2] - qty_birth[q2]) * qty_size[q1]); -} - -static int -qty_compare_1 (q1, q2) - short *q1, *q2; -{ - register int tem = (qty_phys_sugg[*q2] >= 0) - (qty_phys_sugg[*q1] >= 0); - if (tem != 0) return tem; - tem = -((qty_n_refs[*q1] + qty_death[*q1] - qty_birth[*q1]) * qty_size[*q2] - - (qty_n_refs[*q2] + qty_death[*q2] - qty_birth[*q2]) * qty_size[*q1]); - if (tem != 0) return tem; - /* If qtys are equally good, sort by qty number, - so that the results of qsort leave nothing to chance. */ - return *q1 - *q2; -} - -/* Attempt to combine the two registers (rtx's) USEDREG and SETREG. - Returns 1 if have done so, or 0 if cannot. - - Combining registers means marking them as having the same quantity - and adjusting the offsets within the quantity if either of - them is a SUBREG). - - We don't actually combine a hard reg with a pseudo; instead - we just record the hard reg as the suggestion for the pseudo's quantity. - If we really combined them, we could lose if the pseudo lives - across an insn that clobbers the hard reg (eg, movstr). - - There are elaborate checks for the validity of combining. */ - - -static int -combine_regs (usedreg, setreg, b, insn_number, insn) - rtx usedreg, setreg; - int b; - int insn_number; - rtx insn; -{ - register int ureg, sreg; - register int offset = 0; - int usize, ssize; - register int sqty; - - /* Determine the numbers and sizes of registers being used. */ - - while (GET_CODE (usedreg) == SUBREG) - { - offset += SUBREG_WORD (usedreg); - usedreg = SUBREG_REG (usedreg); - } - if (GET_CODE (usedreg) != REG) - return 0; - ureg = REGNO (usedreg); - usize = REG_SIZE (usedreg); - - while (GET_CODE (setreg) == SUBREG) - { - offset -= SUBREG_WORD (setreg); - setreg = SUBREG_REG (setreg); - } - if (GET_CODE (setreg) != REG) - return 0; - sreg = REGNO (setreg); - ssize = REG_SIZE (setreg); - - /* Do not combine registers unless one fits within the other. */ - if (offset > 0 && usize + offset > ssize) - return 0; - if (offset < 0 && usize + offset < ssize) - return 0; - /* Do not combine with a smaller already-assigned object - if that smaller object is already combined with something bigger - or if that smaller object is a hard reg. - In the latter case, we would implicitly be using consecutive - hard regs, and there is no code to keep track of that. - (This is overcautious; we could check that ssize actually - requires more hard regs at this spot.) */ - if (ssize > usize && reg_qty[ureg] >= FIRST_PSEUDO_REGISTER - && usize < qty_size[reg_qty[ureg]]) - return 0; - - /* Don't do anything with the non-allocatable registers. - Also, don't suggest a call-clobberable register - for something that must live across calls. - Also, don't suggest a hardware register for anything larger than it. */ - if (ureg < FIRST_PSEUDO_REGISTER) - { - if (fixed_regs[ureg]) - return 0; - if (reg_n_calls_crossed[sreg] != 0 && call_used_regs[ureg]) - return 0; - if (usize < ssize) - return 0; - } - - if (sreg < FIRST_PSEUDO_REGISTER) - { - if (fixed_regs[sreg]) - return 0; - if (reg_n_calls_crossed[ureg] != 0 && call_used_regs[sreg]) - return 0; - if (ssize < usize) - return 0; - } - - /* Don't tie something to itself. In most cases it would make no - difference, but it would screw up if the reg being tied to itself - also dies in this insn. */ - - if (ureg == sreg) - return 0; - - /* Don't try to connect two different hardware registers. */ - - if (ureg < FIRST_PSEUDO_REGISTER && sreg < FIRST_PSEUDO_REGISTER) - return 0; - - /* Don't connect two different machine modes if they have different - implications as to which registers may be used. */ - - if (!MODES_TIEABLE_P (GET_MODE (usedreg), GET_MODE (setreg))) - return 0; - - /* Now, if one of UREG and SREG is a hard reg and the other is - a pseudo, record the hard reg as the qty_phys_sugg for the pseudo - instead of tying them. */ - /* Return "failure" so that the lifespan of UREG is terminated here; - that way the two lifespans will be disjoint and nothing will prevent - the pseudo reg from being given this hard reg. */ - - if (ureg < FIRST_PSEUDO_REGISTER) - { - if (reg_qty[sreg] == -2) - reg_is_born (setreg, insn_number); - if (reg_qty[ureg] == -2) - reg_is_born (usedreg, insn_number); - if (reg_qty[sreg] >= 0) - qty_phys_sugg[reg_qty[sreg]] = ureg; - return 0; - } - if (sreg < FIRST_PSEUDO_REGISTER) - { - if (reg_qty[sreg] == -2) - reg_is_born (setreg, insn_number); - if (reg_qty[ureg] == -2) - reg_is_born (usedreg, insn_number); - /* If UREG already has a suggested hard reg, don't override it, - since the most likely case is on a risc machine - when a pseudo gets a subroutine result and is then returned by - this function. In this case, the outgoing register window - is probably a better place to use. */ - if (reg_qty[ureg] >= 0 - && (qty_phys_sugg[reg_qty[ureg]] < 0 - /* If the old suggestion is no good, override it. */ - || (qty_n_calls_crossed[reg_qty[ureg]] != 0 - && call_used_regs[qty_phys_sugg[reg_qty[ureg]]]))) - qty_phys_sugg[reg_qty[ureg]] = sreg; - return 0; - } - - /* Do nothing if SREG is a pseudo that already has a quantity - or if it isn't local to this basic block or dies more than once. */ - - if (reg_qty[sreg] != -2) - return 0; - - /* Do nothing if UREG isn't local to this block or dies more than once. - We do this because global_alloc has no idea of tying, - so there is no use noting those local pseudos that could - profitably be delayed till global_alloc and get tied to global ones. */ - - if (reg_qty[ureg] == -1) - return 0; - - /* We don't already know about SREG, so tie it to UREG - if this is the last use of UREG, provided the classes they want - are compatible. */ - - if (find_regno_note (insn, REG_DEAD, ureg) - && (reg_qty[ureg] >= FIRST_PSEUDO_REGISTER - ? reg_meets_class_p (sreg, qty_min_class[reg_qty[ureg]]) - : reg_meets_class_p (sreg, reg_preferred_class (ureg)))) - { - /* If combining these two registers would leave no satisfactory - register available, don't do it. */ - if (ureg >= FIRST_PSEUDO_REGISTER && sreg >= FIRST_PSEUDO_REGISTER - && (qty_preferred_or_nothing[reg_qty[ureg]] - || reg_preferred_or_nothing (sreg)) - && ! (reg_classes_overlap_p - (reg_preferred_class (ureg), reg_preferred_class (sreg), - reg_n_calls_crossed[ureg] || reg_n_calls_crossed[sreg]))) - return 0; - if (reg_qty[ureg] == -2) - reg_is_born (usedreg, insn_number); - sqty = reg_qty[sreg] = reg_qty[ureg]; - if (sqty < FIRST_PSEUDO_REGISTER) abort (); - /* If SREG's reg class is smaller, set qty_min_class[SQTY]. */ - update_qty_class (sqty, sreg); - reg_offset[sreg] = reg_offset[ureg] + offset; - if (sqty >= 0) - { - qty_n_calls_crossed[sqty] += reg_n_calls_crossed[sreg]; - qty_n_refs[sqty] += reg_n_refs[sreg]; - if (! reg_preferred_or_nothing (sreg)) - qty_preferred_or_nothing[sqty] = 0; - if (usize < ssize) - { - register int i; - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (reg_qty[i] == sqty) - reg_offset[i] -= offset; - qty_size[sqty] = ssize; - qty_mode[sqty] = GET_MODE (setreg); - } - } - } - else - return 0; - - return 1; -} - -/* Return 1 if the preferred class of REG allows it to be tied - to a quantity or register whose class is CLASS. - True if REG's reg class either contains or is contained in CLASS. */ - -static int -reg_meets_class_p (reg, class) - int reg; - enum reg_class class; -{ - register enum reg_class rclass = reg_preferred_class (reg); - return (reg_class_subset_p (rclass, class) - || reg_class_subset_p (class, rclass)); -} - -/* Return nonzero if R2's preferred class is the same as or contains - R1's preferred class. R1 and R2 are pseudo-register numbers. */ - -static int -reg_class_subset_p (c1, c2) - register enum reg_class c1; - register enum reg_class c2; -{ - if (c1 == c2) return 1; - - if (c2 == ALL_REGS) - win: - return 1; - GO_IF_HARD_REG_SUBSET (reg_class_contents[(int)c1], - reg_class_contents[(int)c2], - win); - return 0; -} - -/* Return 1 if the two specified classes have registers in common. - If CALL_SAVED, then consider only call-saved registers. */ - -static int -reg_classes_overlap_p (c1, c2, call_saved) - register enum reg_class c1; - register enum reg_class c2; - int call_saved; -{ - HARD_REG_SET c; - int i; - - COPY_HARD_REG_SET (c, reg_class_contents[(int) c1]); - AND_HARD_REG_SET (c, reg_class_contents[(int) c2]); - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (TEST_HARD_REG_BIT (c, i) - && (! call_saved || ! call_used_regs[i])) - return 1; - - return 0; -} - -/* Update the class of QTY assuming that REG is being tied to it. */ - -static void -update_qty_class (qty, reg) - int qty; - int reg; -{ - enum reg_class rclass = reg_preferred_class (reg); - if (reg_class_subset_p (rclass, qty_min_class[qty])) - qty_min_class[qty] = rclass; -} - -/* Handle something which alters the value of an rtx REG. - REG is whatever is set or clobbered. (CLOBBER_FLAG says which.) - If it is not really a register, we do nothing. - The file-global variables `this_insn' and `this_insn_number' - carry info from `block_alloc'. */ - -static void -reg_is_set (reg, setter) - rtx reg; - rtx setter; -{ - register int regno; - int clobber_flag = GET_CODE (setter) == CLOBBER; - - if (reg == 0 || GET_CODE (reg) != REG) - return; - - regno = REGNO (reg); - - if (regno < FIRST_PSEUDO_REGISTER) - { - /* A hard reg is set or clobbered. - Mark it as live at the moment immediately following this insn - so that no pseudo can live here at that time. - For a CLOBBER, mark it as live before this insn, - to make sure it is free during the entire insn. */ - - register int lim = regno + HARD_REGNO_NREGS (regno, GET_MODE (reg)); - register int i; - for (i = regno; i < lim; i++) - { - SET_HARD_REG_BIT (regs_live_at[this_insn_number], i); - if (clobber_flag) - SET_HARD_REG_BIT (regs_live_at[this_insn_number - 1], i); - } - - /* If the hard reg is given a useful value - and it does not die in this insn, - mark it as live indefinitely afterward. */ - if (! clobber_flag - && ! find_regno_note (this_insn, REG_DEAD, regno)) - reg_is_born (reg, this_insn_number); - } - else if (! clobber_flag) - { - /* A pseudo-reg is set (not just clobbered). */ - - reg_is_born (reg, this_insn_number); - - /* If a pseudo register dies in the same insn that sets it, - say it dies in the following insn instead, - because it will have to be live right after this insn. */ - if (qty_death[reg_qty[regno]] == this_insn_number) - { - /* Calls to post_mark_life and mark_life deleted here. - They only know how to handle hard regs. */ - qty_death[reg_qty[regno]]++; - } - } - else if (reg_qty[regno] >= 0 && qty_death[reg_qty[regno]] == this_insn_number - && qty_birth[reg_qty[regno]] == this_insn_number) - { - /* A psuedo-reg is clobbered by this insn and was born and dies here. - This is a temporary required for this insn and so will - conflict with any other live registers at this point. We must - assume that this register is used before all the inputs of the - insn are dead. So this register must not conflict with any of them. - Mark it as born at the previous insn. */ - qty_birth[reg_qty[regno]]--; - /* It should also conflict with this insn's outputs. */ - qty_death[reg_qty[regno]]++; - } -} - -/* Handle beginning of the life of register REG. - INSN_NUMBER is the insn at which this is happening. */ - -static void -reg_is_born (reg, insn_number) - rtx reg; - int insn_number; -{ - register int regno = REGNO (reg); - - if (regno < FIRST_PSEUDO_REGISTER) - mark_life (regno, GET_MODE (reg), 1); - else if (reg_qty[regno] == -2) - alloc_qty (regno, GET_MODE (reg), PSEUDO_REGNO_SIZE (regno), insn_number); -} - -/* Record the death in insn DEATH_INSN_NUMBER for the register REG. */ - -static void -wipe_dead_reg (reg, this_insn_number, death_insn_number) - register rtx reg; - int this_insn_number; - int death_insn_number; -{ - register int regno = REGNO (reg); - - if (regno < FIRST_PSEUDO_REGISTER) - { - mark_life (regno, GET_MODE (reg), 0); - if (this_insn_number != death_insn_number) - abort (); -#if 0 /* Should never get here */ - post_mark_life (regno, GET_MODE (reg), 1, - this_insn_number, death_insn_number); -#endif - } - else - { - /* If a pseudo reg is referred to but was never set, - we will find here that its qty is -2. - Since these regs do not conflict with anything, - mark them as born and dead in the same place. */ - if (reg_qty[regno] == -2) - { - alloc_qty (regno, GET_MODE (reg), REG_SIZE (reg), this_insn_number); - REG_NOTES (this_insn) = gen_rtx (EXPR_LIST, REG_UNSET, reg, - REG_NOTES (this_insn)); - } - - if (reg_qty[regno] >= 0) - qty_death[reg_qty[regno]] = death_insn_number; - } -} - -/* Find a block of SIZE words of hard regs in reg_class CLASS - that can hold something of machine-mode MODE - (but actually we test only the first of the block for holding MODE) - and still free between insn BORN_INSN and insn DEAD_INSN, - and return the number of the first of them. - Return -1 if such a block cannot be found. - If QTY crosses calls, insist on a register preserved by calls, - unless ACCEPT_CALL_CLOBBERED is nonzero. */ - -static int -find_free_reg (class, mode, qty, accept_call_clobbered, born_insn, dead_insn) - enum reg_class class; - enum machine_mode mode; - int accept_call_clobbered; - int qty; - int born_insn, dead_insn; -{ - register int i, ins; -#ifdef HARD_REG_SET - register /* Declare it register if it's a scalar. */ -#endif - HARD_REG_SET used; - - if (accept_call_clobbered) - COPY_HARD_REG_SET (used, call_fixed_reg_set); - else if (qty_n_calls_crossed[qty] == 0) - COPY_HARD_REG_SET (used, fixed_reg_set); - else - COPY_HARD_REG_SET (used, call_used_reg_set); - - for (ins = born_insn; ins < dead_insn; ins++) - IOR_HARD_REG_SET (used, regs_live_at[ins]); - - IOR_COMPL_HARD_REG_SET (used, reg_class_contents[(int) class]); - /* Don't use the frame pointer reg in local-alloc even if - we may omit the frame pointer, because if we do that and then we - need a frame pointer, reload won't know how to move the pseudo - to another hard reg. It can move only regs made by global-alloc. */ - SET_HARD_REG_BIT (used, FRAME_POINTER_REGNUM); - - /* If quantity QTY has a suggested physical register, - try that one first. */ - - if (qty_phys_sugg[qty] >= 0) - { - i = qty_phys_sugg[qty]; - if (! TEST_HARD_REG_BIT (used, i) - && HARD_REGNO_MODE_OK (i, mode)) - { - register int j; - register int size1 = HARD_REGNO_NREGS (i, mode); - for (j = 1; j < size1 && ! TEST_HARD_REG_BIT (used, i + j); j++); - if (j == size1) - { - post_mark_life (i, mode, 1, born_insn, dead_insn); - return i; - } - } - } - - /* If that doesn't find one, test each hard reg. */ - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { -#ifdef REG_ALLOC_ORDER - int regno = reg_alloc_order[i]; -#else - int regno = i; -#endif - if (! TEST_HARD_REG_BIT (used, regno) - && HARD_REGNO_MODE_OK (regno, mode)) - { - register int j; - register int size1 = HARD_REGNO_NREGS (regno, mode); - for (j = 1; j < size1 && ! TEST_HARD_REG_BIT (used, regno + j); j++); - if (j == size1) - { - post_mark_life (regno, mode, 1, born_insn, dead_insn); - return regno; - } -#ifndef REG_ALLOC_ORDER - i += j; /* Skip starting points we know will lose */ -#endif - } - } - - /* If it would be profitable to allocate a call-clobbered register - and save and restore it around calls, do that. */ - - if (! accept_call_clobbered - && flag_caller_saves - && qty_n_calls_crossed[qty] != 0 - && CALLER_SAVE_PROFITABLE (qty_n_refs[qty], qty_n_calls_crossed[qty])) - { - i = find_free_reg (class, mode, qty, 1, born_insn, dead_insn); - if (i >= 0) - caller_save_needed = 1; - return i; - } - return -1; -} - -static void -mark_life (regno, mode, life) - register int regno; - enum machine_mode mode; - int life; -{ - register int j = HARD_REGNO_NREGS (regno, mode); - if (life) - while (--j >= 0) - SET_HARD_REG_BIT (regs_live, regno + j); - else - while (--j >= 0) - CLEAR_HARD_REG_BIT (regs_live, regno + j); -} - -static void -post_mark_life (regno, mode, life, birth, death) - register int regno, life, birth; - enum machine_mode mode; - int death; -{ - register int j = HARD_REGNO_NREGS (regno, mode); -#ifdef HARD_REG_SET - register /* Declare it register if it's a scalar. */ -#endif - HARD_REG_SET this_reg; - - CLEAR_HARD_REG_SET (this_reg); - while (--j >= 0) - SET_HARD_REG_BIT (this_reg, regno + j); - - /* If a reg is born and dies in one insn, - consider it live after that insn. */ - - if (birth == death) - death++; - - if (life) - while (birth < death) - { - IOR_HARD_REG_SET (regs_live_at[birth], this_reg); - birth++; - } - else - while (birth < death) - { - AND_COMPL_HARD_REG_SET (regs_live_at[birth], this_reg); - birth++; - } -} - -void -dump_local_alloc (file) - FILE *file; -{ - register int i; - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (reg_renumber[i] != -1) - fprintf (file, ";; Register %d in %d.\n", i, reg_renumber[i]); -} diff --git a/gnu/usr.bin/gcc1/cc1/loop.c b/gnu/usr.bin/gcc1/cc1/loop.c deleted file mode 100644 index 0eacf67d2b..0000000000 --- a/gnu/usr.bin/gcc1/cc1/loop.c +++ /dev/null @@ -1,5353 +0,0 @@ -/* Move constant computations out of loops. - Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This is the loop optimization pass of the compiler. - It finds invariant computations within loops and moves them - to the beginning of the loop. Then it identifies basic and - general induction variables. Strength reduction is applied to the general - induction variables, and induction variable elimination is applied to - the basic induction variables. - - It also finds cases where - a register is set within the loop by zero-extending a narrower value - and changes these to zero the entire register once before the loop - and merely copy the low part within the loop. - - Most of the complexity is in heuristics to decide when it is worth - while to do these things. */ - -/* ??? verify_loop would run faster if we made one table - of the minimum and maximum luids from which each label is reached. - Also, it would be faster if loop_store_addrs were a hash table. */ - -#include "config.h" -#include "rtl.h" -#include "expr.h" -#include "insn-config.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "recog.h" -#include "flags.h" -#include - -/* Vector mapping INSN_UIDs to luids. - The luids are like uids but increase monononically always. - We use them to see whether a jump comes from outside a given loop. */ - -static int *uid_luid; - -/* Get the luid of an insn. */ - -#define INSN_LUID(INSN) (uid_luid[INSN_UID (INSN)]) - -/* 1 + largest uid of any insn. */ - -static int max_uid; - -/* 1 + luid of last insn. */ - -static int max_luid; - -/* Nonzero if somewhere in the current loop - there is either a subroutine call, - or a store into a memory address that is not fixed, - or a store in a BLKmode memory operand, - or too many different fixed addresses stored in - to record them all in `loop_store_addrs'. - - In any of these cases, no memory location can be regarded - as invariant. */ - -static int unknown_address_altered; - -/* Nonzero if somewhere in the current loop there is a store - into a memory address that is not fixed but is known to be - part of an aggregate. - - In this case, no memory reference in an aggregate may be - considered invariant. */ - -static int unknown_aggregate_altered; - -/* Nonzero if somewhere in the current loop there is a store - into a memory address other than a fixed address not in an aggregate. - - In this case, no memory reference in an aggregate at a varying address - may be considered invariant. */ - -static int fixed_aggregate_altered; - -/* Nonzero if there is a subroutine call in the current loop. - (unknown_address_altered is also nonzero in this case.) */ - -static int loop_has_call; - -/* Added loop_continue which is the NOTE_INSN_LOOP_CONT of the - current loop. A continue statement will generate a branch to - NEXT_INSN (loop_continue). */ - -static rtx loop_continue; - -/* Indexed by register number, contains the number of times the reg - is set during the loop being scanned. - During code motion, -1 indicates a reg that has been made a candidate. - After code motion, regs moved have 0 (which is accurate now) - while the failed candidates have the original number of times set. - - Therefore, at all times, 0 indicates an invariant register; - -1 a conditionally invariant one. */ - -static short *n_times_set; - -/* Original value of n_times_set; same except that this value - is not set to -1 for a reg whose sets have been made candidates - and not set to 0 for a reg that is moved. */ - -static short *n_times_used; - -/* Index by register number, 1 indicates that the register - cannot be moved or strength reduced. */ - -static char *may_not_optimize; - -/* Nonzero means reg N has already been moved out of one loop. - This reduces the desire to move it out of another. */ - -static char *moved_once; - -/* Array of fixed memory addresses that are stored in this loop. - If there are too many to fit here, - we just turn on unknown_address_altered. */ - -#define NUM_STORES 10 -static rtx loop_store_addrs[NUM_STORES]; -static int loop_store_widths[NUM_STORES]; - -/* Index of first available slot in above array. */ -static int loop_store_addrs_idx; - -/* Count of movable (i.e. invariant) instructions discovered in the loop. */ -static int num_movables; - -/* Count of memory write instructions discovered in the loop. */ -static int num_mem_sets; - -/* Number of loops contained within the current one, including itself. */ -static int loops_enclosed; - -/* Bound on pseudo register number before loop optimization. - A pseudo has valid regscan info if its number is < old_max_reg. */ -static int old_max_reg; - -/* During the analysis of a loop, a chain of `struct movable's - is made to record all the movable insns found. - Then the entire chain can be scanned to decide which to move. */ - -struct movable -{ - rtx insn; /* A movable insn */ - rtx set_src; /* The expression this reg is set from. - Either SET_SRC (body) or a REG_EQUAL. */ - int consec; /* Number of consecutive following insns - that must be moved with this one. */ - int regno; /* The register it sets */ - short lifetime; /* lifetime of that register; - may be adjusted when matching movables - that load the same value are found. */ - short savings; /* Number of insns we can move for this reg, - including other movables that force this - or match this one. */ - unsigned int cond : 1; /* 1 if only conditionally movable */ - unsigned int force : 1; /* 1 means MUST move this insn */ - unsigned int global : 1; /* 1 means reg is live outside this loop */ - /* If PARTIAL is 1, GLOBAL means something different: - that the reg is live outside the range from where it is set - to the following label. */ - unsigned int done : 1; /* 1 inhibits further processing of this */ - /* 1 in PARTIAL means this reg is used for zero-extending. - In particular, moving it does not make it invariant. */ - unsigned int partial : 1; - enum machine_mode savemode; /* Nonzero means it is a mode for a low part - that we should avoid changing when clearing - the rest of the reg. */ - struct movable *match; /* First entry for same value */ - struct movable *forces; /* An insn that must be moved if this is */ - struct movable *next; -}; - -static FILE *loop_dump_stream; - -/* Forward declarations. */ - -struct induction; -struct iv_class; - -static rtx loop_find_reg_equal (); -static int reg_in_basic_block_p (); -static rtx verify_loop (); -static int invariant_p (); -static int consec_sets_invariant_p (); -static int can_jump_into_range_p (); -static int labels_in_range_p (); -static void count_loop_regs_set (); -static void note_addr_stored (); -static int loop_reg_used_before_p (); -static void constant_high_bytes (); -static void scan_loop (); -static rtx replace_regs (); -static void replace_call_address (); -static rtx skip_consec_insns (); -static void ignore_some_movables (); -static void force_movables (); -static void combine_movables (); -static int rtx_equal_for_loop_p (); -static void move_movables (); -static void strength_reduce (); -static void find_mem_givs (); -static void record_giv (); -static void delete_insn_forces (); -static int basic_induction_var (); -static int general_induction_var (); -static int consec_sets_giv (); -static int check_dbra_loop (); -static void emit_iv_init_code (); -static int product_cheap_p (); -static void emit_iv_inc (); -static void check_eliminate_biv (); -static int can_eliminate_biv_p (); -static void eliminate_biv (); -static rtx final_biv_value (); -static int last_use_this_basic_block (); - -/* Entry point of this file. Perform loop optimization - on the current function. F is the first insn of the function - and DUMPFILE is a stream for output of a trace of actions taken - (or 0 if none should be output). */ - -void -loop_optimize (f, dumpfile) - /* f is the first instruction of a chain of insns for one function */ - rtx f; - FILE *dumpfile; -{ - register rtx insn; - register int i; - rtx end; - rtx last_insn; - - loop_dump_stream = dumpfile; - - init_recog (); - - old_max_reg = max_reg_num (); - - moved_once = (char *) alloca (old_max_reg); - bzero (moved_once, old_max_reg); - - /* First find the last real insn, and count the number of insns, - and assign insns their luids. */ - - for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) - if (INSN_UID (insn) > i) - i = INSN_UID (insn); - - max_uid = i + 1; - uid_luid = (int *) alloca ((i + 1) * sizeof (int)); - bzero (uid_luid, (i + 1) * sizeof (int)); - - /* Compute the mapping from uids to luids. - LUIDs are numbers assigned to insns, like uids, - except that luids increase monotonically through the code. - Don't assign luids to line-number NOTEs, so that the distance in luids - between two insns is not affected by -g. */ - - for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) - { - last_insn = insn; - if (GET_CODE (insn) != NOTE - || NOTE_LINE_NUMBER (insn) < 0) - INSN_LUID (insn) = ++i; - else - /* Give a line number note the same luid as preceding insn. */ - INSN_LUID (insn) = i; - } - - max_luid = i; - - /* Don't leave gaps in uid_luid for insns that have been - deleted. It is possible that the first or last insn - using some register has been deleted by cross-jumping. - Make sure that uid_luid for that former insn's uid - points to the general area where that insn used to be. */ - for (i = 0; i < max_uid; i++) - { - uid_luid[0] = uid_luid[i]; - if (uid_luid[0] != 0) - break; - } - for (i = 0; i < max_uid; i++) - if (uid_luid[i] == 0) - uid_luid[i] = uid_luid[i - 1]; - - /* Find and process each loop. - We scan from the end, and process each loop when its start is seen, - so we process innermost loops first. */ - - for (insn = last_insn; insn; insn = PREV_INSN (insn)) - if (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG) - { - /* Make sure it really is a loop -- no jumps in from outside. */ - end = verify_loop (f, insn); - if (end != 0) - /* If so, optimize this loop. */ - scan_loop (insn, end, max_reg_num ()); - else if (loop_dump_stream) - fprintf (loop_dump_stream, - "\nLoop at %d ignored due to multiple entry points.\n", - INSN_UID (insn)); - } -} - -/* Optimize one loop whose start is LOOP_START and end is END. - LOOP_START is the NOTE_INSN_LOOP_BEG and END is the matching - NOTE_INSN_LOOP_END. */ - -/* ??? can also move memory writes out of loop if destination - address is invariant? */ - -static void -scan_loop (loop_start, end, nregs) - rtx loop_start, end; - int nregs; -{ - register int i; - register rtx p = NEXT_INSN (loop_start); - /* 1 if we are scanning insns that could be executed zero times. */ - int maybe_never = 0; - /* 1 if we are scanning insns that might never be executed - due to a subroutine call which might exit before they are reached. */ - int call_passed = 0; - /* For a rotated loop that is entered near the bottom, - this is the label at the top. Otherwise it is zero. */ - rtx loop_top = 0; - /* Jump insn that enters the loop, or 0 if control drops in. */ - rtx loop_entry_jump = 0; - /* Place in the loop where control enters. */ - rtx scan_start; - /* Number of insns in the loop. */ - int insn_count; - int tem; - rtx temp; - /* Chain describing insns movable in current loop. */ - struct movable *movables = 0; - /* Last element in `movables' -- so we can add elements at the end. */ - struct movable *last_movable = 0; - /* Ratio of extra register life span we can justify - for saving an instruction. More if loop doesn't call subroutines - since in that case saving an insn makes more difference - and more registers are available. */ - int threshold = loop_has_call ? 15 : 30; - /* Nonzero if the insn that jumps into the real loop - is not the very first thing after the loop-beginning note. */ - int something_before_entry_jump = 0; - - n_times_set = (short *) alloca (nregs * sizeof (short)); - n_times_used = (short *) alloca (nregs * sizeof (short)); - may_not_optimize = (char *) alloca (nregs); - - /* Determine whether this loop starts with a jump down - to a test at the end. */ - while (p != end - && GET_CODE (p) != CODE_LABEL && GET_CODE (p) != JUMP_INSN) - { - if (GET_CODE (p) == CALL_INSN || GET_CODE (p) == INSN) - something_before_entry_jump = 1; - p = NEXT_INSN (p); - } - - /* "Loop" contains neither jumps nor labels; - it must have been a dummy. Think no more about it. */ - if (p == end) - return; - - scan_start = p; - - /* If loop has a jump before the first label, - the true entry is the target of that jump. - Start scan from there. - But record in LOOP_TOP the place where the end-test jumps - back to so we can scan that after the end of the loop. */ - if (GET_CODE (p) == JUMP_INSN) - { - loop_entry_jump = p; - loop_top = NEXT_INSN (p); - /* Loop entry will never be a conditional jump. - If we see one, this must not be a real loop. - Also, a return-insn isn't a jump to enter the loop. */ - if (GET_CODE (loop_top) != BARRIER - || GET_CODE (PATTERN (p)) != SET) - return; - /* Get the label at which the loop is entered. */ - p = XEXP (SET_SRC (PATTERN (p)), 0); - /* Check to see whether the jump actually - jumps out of the loop (meaning it's no loop). - This case can happen for things like - do {..} while (0). */ - if (p == 0 - || INSN_LUID (p) < INSN_LUID (loop_start) - || INSN_LUID (p) >= INSN_LUID (end)) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, "\nLoop from %d to %d is phony.\n\n", - INSN_UID (loop_start), INSN_UID (end)); - return; - } - - /* Find the first label after the entry-jump. */ - while (GET_CODE (loop_top) != CODE_LABEL) - { - loop_top = NEXT_INSN (loop_top); - if (loop_top == 0) - abort (); - } - - /* Maybe rearrange the loop to drop straight in - with a new test to jump around it entirely. - (The latter is considered outside the loop.) - If this is done, we no longer enter with a jump. */ - if (! something_before_entry_jump - && loop_skip_over (loop_start, end, loop_entry_jump)) - { - scan_start = loop_top; - loop_top = 0; - } - else - /* We really do enter with a jump; - scan the loop from the place where the jump jumps to. */ - scan_start = p; - } - - /* Count number of times each reg is set during this loop. - Set may_not_optimize[I] if it is not safe to move out - the setting of register I. */ - - bzero (n_times_set, nregs * sizeof (short)); - bzero (may_not_optimize, nregs); - count_loop_regs_set (loop_top ? loop_top : loop_start, end, - may_not_optimize, &insn_count, nregs); - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - may_not_optimize[i] = 1, n_times_set[i] = 1; - bcopy (n_times_set, n_times_used, nregs * sizeof (short)); - - if (loop_dump_stream) - { - fprintf (loop_dump_stream, "\nLoop from %d to %d: %d real insns.\n", - INSN_UID (loop_start), INSN_UID (end), insn_count); - if (loop_continue) - fprintf (loop_dump_stream, "Continue at insn %d.\n", - INSN_UID (loop_continue)); - } - - /* Scan through the loop finding insns that are safe to move. - In each such insn, store QImode as the mode, to mark it. - Then set n_times_set to -1 for the reg being set, so that - this reg will be considered invariant for subsequent insns. - We consider whether subsequent insns use the reg - in deciding whether it is worth actually moving. - - MAYBE_NEVER is nonzero if we have passed a conditional jump insn - and therefore it is possible that the insns we are scanning - would never be executed. At such times, we must make sure - that it is safe to execute the insn once instead of zero times. - When MAYBE_NEVER is 0, all insns will be executed at least once - so that is not a problem. */ - - p = scan_start; - while (1) - { - p = NEXT_INSN (p); - /* At end of a straight-in loop, we are done. - At end of a loop entered at the bottom, scan the top. */ - if (p == scan_start) - break; - if (p == end) - { - if (loop_top != 0) - p = NEXT_INSN (loop_top); - else - break; - if (p == scan_start) - break; - } - if (GET_CODE (p) == INSN - && GET_CODE (PATTERN (p)) == SET - && GET_CODE (SET_DEST (PATTERN (p))) == REG - && ! may_not_optimize[REGNO (SET_DEST (PATTERN (p)))]) - { - int tem1 = 0; - /* Don't try to optimize a register that was made - by loop-optimization for an inner loop. - We don't know its life-span, so we can't compute the benefit. */ - if (REGNO (SET_DEST (PATTERN (p))) >= old_max_reg) - ; - /* IN order to move a register, we need to have one of three cases: - (1) it is used only in the same basic block as the set - (2) it is not a user variable. - (3) the set is guaranteed to be executed once the loop starts, - and the reg is not used until after that. */ - else if (! ((! maybe_never - && ! loop_reg_used_before_p (p, loop_start, scan_start, end)) - || ! REG_USERVAR_P (SET_DEST (PATTERN (p))) - || reg_in_basic_block_p (p, SET_DEST (PATTERN (p))))) - ; - else if (((tem = invariant_p (SET_SRC (PATTERN (p)))) - || ((temp = loop_find_reg_equal (p)) - && (tem = invariant_p (XEXP (temp, 0))))) - && (n_times_set[REGNO (SET_DEST (PATTERN (p)))] == 1 - || (tem1 - = consec_sets_invariant_p (SET_DEST (PATTERN (p)), - n_times_set[REGNO (SET_DEST (PATTERN (p)))], - p))) - /* If the insn can cause a trap (such as divide by zero), - can't move it unless it's guaranteed to be executed - once loop is entered. Even a function call might - prevent the trap insn from being reached - (since it might exit!) */ - && ! ((maybe_never || call_passed) - && (may_trap_p (SET_SRC (PATTERN (p))) - || ((temp = loop_find_reg_equal (p)) - && may_trap_p (XEXP (temp, 0)))))) - { - register struct movable *m; - register int regno = REGNO (SET_DEST (PATTERN (p))); - int count; - m = (struct movable *) alloca (sizeof (struct movable)); - m->next = 0; - m->insn = p; - temp = loop_find_reg_equal (p); - if (temp) - m->set_src = XEXP (temp, 0); - else - m->set_src = SET_SRC (PATTERN (p)); - m->force = 0; - m->consec = n_times_set[REGNO (SET_DEST (PATTERN (p)))] - 1; - m->done = 0; - m->forces = 0; - m->partial = 0; - m->savemode = VOIDmode; - m->regno = regno; - /* Set M->cond if either invariant_p or consec_sets_invariant_p - returned 2 (only conditionally invariant). */ - m->cond = ((tem | tem1) > 1); - m->global = (uid_luid[regno_last_uid[regno]] > INSN_LUID (end) - || uid_luid[regno_first_uid[regno]] < INSN_LUID (loop_start)); - m->match = 0; - m->lifetime = (uid_luid[regno_last_uid[regno]] - - uid_luid[regno_first_uid[regno]]); - m->savings = n_times_used[regno]; - n_times_set[regno] = -1; - /* Add M to the end of the chain MOVABLES. */ - if (movables == 0) - movables = m; - else - last_movable->next = m; - last_movable = m; - if (m->consec > 0) - { - /* Skip this insn, not checking REG_LIBCALL notes. */ - p = NEXT_INSN (p); - /* Skip the consecutive insns, if there are any. */ - p = skip_consec_insns (p, m->consec); - /* Back up, so the main loop will advance to the right place. */ - p = PREV_INSN (p); - } - } - /* If this register is always set within a STRICT_LOW_PART - or set to zero, then its high bytes are constant. - So clear them outside the loop and within the loop - just load the low bytes. - We must check that the machine has an instruction to do so. - Also, if the value loaded into the register - depends on the same register, this cannot be done. */ - else if (SET_SRC (PATTERN (p)) == const0_rtx - && GET_CODE (NEXT_INSN (p)) == INSN - && GET_CODE (PATTERN (NEXT_INSN (p))) == SET - && (GET_CODE (SET_DEST (PATTERN (NEXT_INSN (p)))) - == STRICT_LOW_PART) - && (GET_CODE (XEXP (SET_DEST (PATTERN (NEXT_INSN (p))), 0)) - == SUBREG) - && (SUBREG_REG (XEXP (SET_DEST (PATTERN (NEXT_INSN (p))), 0)) - == SET_DEST (PATTERN (p))) - && !reg_mentioned_p (SET_DEST (PATTERN (p)), - SET_SRC (PATTERN (NEXT_INSN (p))))) - { - register int regno = REGNO (SET_DEST (PATTERN (p))); - if (n_times_set[regno] == 2) - { - register struct movable *m; - int count; - m = (struct movable *) alloca (sizeof (struct movable)); - m->next = 0; - m->insn = p; - m->force = 0; - m->consec = 0; - m->done = 0; - m->forces = 0; - m->partial = 1; - /* If the insn may not be executed on some cycles, - we can't clear the whole reg; clear just high part. - Not even if the reg is used only within this loop. - Consider this: - while (1) - while (s != t) { - if (foo ()) x = *s; - use (x); - } - Clearing x before the inner loop could clobber a value - being saved from the last time around the outer loop. - However, if the reg is not used outside this loop - and all uses of the register are in the same - basic block as the store, there is no problem. */ - m->global = (uid_luid[regno_last_uid[regno]] > INSN_LUID (end) - || uid_luid[regno_first_uid[regno]] < INSN_LUID (p) - || (labels_in_range_p - (p, uid_luid[regno_first_uid[regno]]))); - if (maybe_never && m->global) - m->savemode = GET_MODE (SET_SRC (PATTERN (NEXT_INSN (p)))); - else - m->savemode = VOIDmode; - m->regno = regno; - m->cond = 0; - m->match = 0; - m->lifetime = (uid_luid[regno_last_uid[regno]] - - uid_luid[regno_first_uid[regno]]); - m->savings = 1; - n_times_set[regno] = -1; - /* Add M to the end of the chain MOVABLES. */ - if (movables == 0) - movables = m; - else - last_movable->next = m; - last_movable = m; - } - } - } - /* Past a call insn, we get to insns which might not be executed - because the call might exit. This matters for insns that trap. */ - else if (GET_CODE (p) == CALL_INSN) - call_passed = 1; - /* Past a label or a jump, we get to insns for which we - can't count on whether or how many times they will be - executed during each iteration. Therefore, we can - only move out sets of trivial variables - (those not used after the loop). */ - else if ((GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN) - /* If we enter the loop in the middle, and scan around - to the beginning, don't set maybe_never for that. */ - && ! (NEXT_INSN (p) == end && GET_CODE (p) == JUMP_INSN - && simplejump_p (p))) - maybe_never = 1; - } - - /* If one movable subsumes another, ignore that other. */ - - ignore_some_movables (movables); - - /* For each movable insn, see if the reg that it loads - leads when it dies right into another conditionally movable insn. - If so, record that the second insn "forces" the first one, - since the second can be moved only if the first is. */ - - force_movables (movables); - - /* See if there are multiple movable insns that load the same value. - If there are, make all but the first point at the first one - through the `match' field, and add the priorities of them - all together as the priority of the first. */ - - combine_movables (movables, nregs); - - /* Now consider each movable insn to decide whether it is worth moving. - Store 0 in n_times_set for each reg that is moved. */ - - move_movables (movables, threshold, - insn_count, loop_start, end, nregs); - - /* Now candidates that still have -1 are those not moved. - Change n_times_set to indicate that those are not actually invariant. */ - for (i = 0; i < nregs; i++) - if (n_times_set[i] < 0) - n_times_set[i] = n_times_used[i]; - - if (flag_strength_reduce) - strength_reduce (scan_start, end, loop_top, - insn_count, loop_start, end, nregs); -} - -/* Return 1 if all uses of REG - are between INSN and the end of the basic block. */ - -static int -reg_in_basic_block_p (insn, reg) - rtx insn, reg; -{ - int regno = REGNO (reg); - rtx p; - - if (regno_first_uid[regno] != INSN_UID (insn)) - return 0; - - /* Search this basic block for the already recorded last use of the reg. */ - for (p = insn; p; p = NEXT_INSN (p)) - { - switch (GET_CODE (p)) - { - case NOTE: - break; - - case INSN: - case CALL_INSN: - /* Ordinary insn: if this is the last use, we win. */ - if (regno_last_uid[regno] == INSN_UID (p)) - return 1; - break; - - case JUMP_INSN: - /* Jump insn: if this is the last use, we win. */ - if (regno_last_uid[regno] == INSN_UID (p)) - return 1; - /* Otherwise, it's the end of the basic block, so we lose. */ - return 0; - - case CODE_LABEL: - case BARRIER: - /* It's the end of the basic block, so we lose. */ - return 0; - } - } - - /* The "last use" doesn't follow the "first use"?? */ - abort (); -} - -/* Skip COUNT insns from INSN, counting library calls as 1 insn. */ - -static rtx -skip_consec_insns (insn, count) - rtx insn; - int count; -{ - for (; count > 0; count--) - { - if (GET_CODE (insn) == NOTE) - insn = NEXT_INSN (insn); - else if (GET_CODE (insn) == BARRIER || GET_CODE (insn) == CODE_LABEL) - abort (); - else - { - rtx i1, temp; - - /* If first insn of gnulib call sequence, skip to end. */ - /* Do this at start of loop, since INSN is guaranteed to - be an insn here. */ - if (temp = find_reg_note (insn, REG_LIBCALL, 0)) - insn = XEXP (temp, 0); - - do insn = NEXT_INSN (insn); - while (GET_CODE (insn) == NOTE); - } - } - - return insn; -} - -/* Find a REG_EQUAL note in INSN but only if it is safe to use for our - purposes. Those put in by CSE are not safe since they may fail to - use the registers that appear in the actual insn source. */ - -static rtx -loop_find_reg_equal (insn) - rtx insn; -{ - return (find_reg_note (insn, REG_RETVAL, 0) - ? find_reg_note (insn, REG_EQUAL, 0) - : 0); -} - -/* Ignore any movable whose insn falls within a libcall - which is part of another movable. - We make use of the fact that the movable for the libcall value - was made later and so appears later on the chain. */ - -static void -ignore_some_movables (movables) - struct movable *movables; -{ - register struct movable *m, *m1; - - for (m = movables; m; m = m->next) - { - /* Is this a movable for the value of a libcall? */ - rtx note = find_reg_note (m->insn, REG_RETVAL, 0); - if (note) - { - /* Find the beginning of that libcall. */ - rtx first_insn = XEXP (note, 0); - /* Check for earlier movables inside that range, - and mark them invalid. */ - for (m1 = movables; m1 != m; m1 = m1->next) - if (INSN_LUID (m1->insn) >= INSN_LUID (first_insn) - && INSN_LUID (m1->insn) < INSN_LUID (m->insn)) - m1->done = 1; - } - } -} - -/* For each movable insn, see if the reg that it loads - leads when it dies right into another conditionally movable insn. - If so, record that the second insn "forces" the first one, - since the second can be moved only if the first is. */ - -static void -force_movables (movables) - struct movable *movables; -{ - register struct movable *m, *m1; - for (m1 = movables; m1; m1 = m1->next) - /* Omit this if moving just the (SET (REG) 0) of a zero-extend. */ - if (!m1->partial && !m1->done) - { - int regno = m1->regno; - for (m = m1->next; m; m = m->next) - /* ??? Could this be a bug? What if CSE caused the - register of M1 to be used after this insn? - Since CSE does not update regno_last_uid, - this insn M->insn might not be where it dies. - But very likely this doesn't matter; what matters is - that M's reg is computed from M1's reg. */ - if (INSN_UID (m->insn) == regno_last_uid[regno] - && !m->done) - break; - if (m != 0 && m->set_src == SET_DEST (PATTERN (m1->insn))) - m = 0; - - /* Increase the priority of the moving the first insn - since it permits the second to be moved as well. */ - if (m != 0) - { - m->forces = m1; - m1->lifetime += m->lifetime; - m1->savings += m1->savings; - } - } -} - -/* Find invariant expressions that are equal and can be combined into - one register. */ - -static void -combine_movables (movables, nregs) - struct movable *movables; - int nregs; -{ - register struct movable *m; - char *matched_regs = (char *) alloca (nregs); - enum machine_mode mode; - - /* Regs that are set more than once are not allowed to match - or be matched. I'm no longer sure why not. */ - /* Perhaps testing m->consec_sets would be more appropriate here? */ - - for (m = movables; m; m = m->next) - if (m->match == 0 && n_times_used[m->regno] == 1 && !m->partial) - { - register struct movable *m1; - int regno = m->regno; - - bzero (matched_regs, nregs); - matched_regs[regno] = 1; - - for (m1 = m->next; m1; m1 = m1->next) - if (m1->match == 0 && n_times_used[m1->regno] == 1 - /* A reg used outside the loop mustn't be eliminated. */ - && !m1->global - /* A reg used for zero-extending mustn't be eliminated. */ - && !m1->partial - && (matched_regs[m1->regno] - || - ( - /* Can't combine regs with different modes - even if loaded from the same constant. */ - (GET_MODE (SET_DEST (PATTERN (m->insn))) - == GET_MODE (SET_DEST (PATTERN (m1->insn)))) - /* See if the source of M1 says it matches M. */ - && ((GET_CODE (m1->set_src) == REG - && matched_regs[REGNO (m1->set_src)]) - || rtx_equal_for_loop_p (m->set_src, m1->set_src, - movables) - || (REG_NOTES (m->insn) && REG_NOTES (m1->insn) - && REG_NOTE_KIND (REG_NOTES (m->insn)) == REG_EQUIV - && REG_NOTE_KIND (REG_NOTES (m1->insn)) == REG_EQUIV - && rtx_equal_p (XEXP (REG_NOTES (m->insn), 0), - XEXP (REG_NOTES (m1->insn), 0))))))) - { - m->lifetime += m1->lifetime; - m->savings += m1->savings; - m1->match = m; - matched_regs[m1->regno] = 1; - } - } - - /* Now combine the regs used for zero-extension. - This can be done for those not marked `global' - provided their lives don't overlap. */ - - for (mode = VOIDmode; (int) mode < (int) MAX_MACHINE_MODE; - mode = (enum machine_mode) ((int) mode + 1)) - if (GET_MODE_CLASS (mode) == MODE_INT) - { - register struct movable *m0 = 0; - - /* Combine all the registers for extension from mode MODE. - Don't combine any that are used outside this loop. */ - for (m = movables; m; m = m->next) - if (m->partial && ! m->global - && mode == GET_MODE (SET_SRC (PATTERN (NEXT_INSN (m->insn))))) - { - register struct movable *m1; - int first = uid_luid[regno_first_uid[m->regno]]; - int last = uid_luid[regno_last_uid[m->regno]]; - - if (m0 == 0) - { - /* First one: don't check for overlap, just record it. */ - m0 = m; - continue; - } - - /* Make sure they extend to the same mode. - (Almost always true.) */ - if (GET_MODE (SET_DEST (PATTERN (m->insn))) - != GET_MODE (SET_DEST (PATTERN (m0->insn)))) - continue; - - /* We already have one: check for overlap with those - already combined together. */ - for (m1 = movables; m1 != m; m1 = m1->next) - if (m1 == m0 || (m1->partial && m1->match == m0)) - if (! (uid_luid[regno_first_uid[m1->regno]] > last - || uid_luid[regno_last_uid[m1->regno]] < first)) - goto overlap; - - /* No overlap: we can combine this with the others. */ - m0->lifetime += m->lifetime; - m0->savings += m->savings; - m->match = m0; - - overlap: ; - } - } -} - -/* Return 1 if regs X and Y will become the same if moved. */ - -static int -regs_match_p (x, y, movables) - rtx x, y; - struct movable *movables; -{ - int xn = REGNO (x); - int yn = REGNO (y); - struct movable *mx, *my; - - for (mx = movables; mx; mx = mx->next) - if (mx->regno == xn) - break; - - for (my = movables; my; my = my->next) - if (my->regno == yn) - break; - - return (mx && my - && ((mx->match == my->match && mx->match != 0) - || mx->match == my - || mx == my->match)); -} - -/* Return 1 if X and Y are identical-looking rtx's. - This is the Lisp function EQUAL for rtx arguments. */ - -static int -rtx_equal_for_loop_p (x, y, movables) - rtx x, y; - struct movable *movables; -{ - register int i; - register int j; - register enum rtx_code code; - register char *fmt; - - if (x == y) - return 1; - if (x == 0 || y == 0) - return 0; - - code = GET_CODE (x); - /* Rtx's of different codes cannot be equal. */ - if (code != GET_CODE (y)) - return 0; - - /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. - (REG:SI x) and (REG:HI x) are NOT equivalent. */ - - if (GET_MODE (x) != GET_MODE (y)) - return 0; - - /* These three types of rtx's can be compared nonrecursively. */ - /* Until the end of reload, - don't consider the a reference to the return register of the current - function the same as the return from a called function. This eases - the job of function integration. Once the distinction no longer - matters, the insn will be deleted. */ - if (code == REG) - return ((REGNO (x) == REGNO (y) - && REG_FUNCTION_VALUE_P (x) == REG_FUNCTION_VALUE_P (y)) - || regs_match_p (x, y, movables)); - - if (code == LABEL_REF) - return XEXP (x, 0) == XEXP (y, 0); - if (code == SYMBOL_REF) - return XSTR (x, 0) == XSTR (y, 0); - - /* Compare the elements. If any pair of corresponding elements - fail to match, return 0 for the whole things. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - switch (fmt[i]) - { - case 'i': - if (XINT (x, i) != XINT (y, i)) - return 0; - break; - - case 'E': - /* Two vectors must have the same length. */ - if (XVECLEN (x, i) != XVECLEN (y, i)) - return 0; - - /* And the corresponding elements must match. */ - for (j = 0; j < XVECLEN (x, i); j++) - if (rtx_equal_for_loop_p (XVECEXP (x, i, j), XVECEXP (y, i, j), movables) == 0) - return 0; - break; - - case 'e': - if (rtx_equal_for_loop_p (XEXP (x, i), XEXP (y, i), movables) == 0) - return 0; - break; - - case 's': - if (strcmp (XSTR (x, i), XSTR (y, i))) - return 0; - break; - - case 'u': - /* These are just backpointers, so they don't matter. */ - break; - - case '0': - break; - - /* It is believed that rtx's at this level will never - contain anything but integers and other rtx's, - except for within LABEL_REFs and SYMBOL_REFs. */ - default: - abort (); - } - } - return 1; -} - -/* Scan MOVABLES, and move the insns that deserve to be moved. - If two matching movables are combined, replace one reg with the - other throughout. */ - -static void -move_movables (movables, threshold, insn_count, loop_start, end, nregs) - struct movable *movables; - int threshold; - int insn_count; - rtx loop_start; - rtx end; - int nregs; -{ - rtx new_start = 0; - register struct movable *m; - register rtx p; - /* Map of pseudo-register replacements to handle combining - when we move several insns that load the same value - into different pseudo-registers. */ - rtx *reg_map = (rtx *) alloca (nregs * sizeof (rtx)); - char *already_moved = (char *) alloca (nregs); - - bzero (already_moved, nregs); - bzero (reg_map, nregs * sizeof (rtx)); - - num_movables = 0; - - for (m = movables; m; m = m->next) - { - /* Describe this movable insn. */ - - if (loop_dump_stream) - { - fprintf (loop_dump_stream, "Insn %d: regno %d (life %d), ", - INSN_UID (m->insn), m->regno, m->lifetime); - if (m->consec > 0) - fprintf (loop_dump_stream, "consec %d, ", m->consec); - if (m->cond) - fprintf (loop_dump_stream, "cond "); - if (m->force) - fprintf (loop_dump_stream, "force "); - if (m->global) - fprintf (loop_dump_stream, "global "); - if (m->done) - fprintf (loop_dump_stream, "done "); - if (m->match) - fprintf (loop_dump_stream, "matches %d ", - INSN_UID (m->match->insn)); - if (m->forces) - fprintf (loop_dump_stream, "forces %d ", - INSN_UID (m->forces->insn)); - } - - /* Count movables. Value used in heuristics in strength_reduce. */ - num_movables++; - - /* Ignore the insn if it's already done (it matched something else). - Otherwise, see if it is now safe to move. */ - - if (!m->done - && (! m->cond - || (1 == invariant_p (m->set_src) - && (m->consec == 0 - || 1 == consec_sets_invariant_p (SET_DEST (PATTERN (m->insn)), - m->consec + 1, - m->insn)))) - && (! m->forces || m->forces->done)) - { - register int regno; - register rtx p; - int savings = m->savings; - - /* We have an insn that is safe to move. - Compute its desirability. */ - - p = m->insn; - regno = m->regno; - - if (loop_dump_stream) - fprintf (loop_dump_stream, "savings %d ", savings); - - if (moved_once[regno]) - { - insn_count *= 2; - - if (loop_dump_stream) - fprintf (loop_dump_stream, "halved since already moved "); - } - - /* An insn MUST be moved if we already moved something else - which is safe only if this one is moved too: that is, - if already_moved[REGNO] is nonzero. */ - - /* An insn is desirable to move if the new lifetime of the - register is no more than THRESHOLD times the old lifetime. - If it's not desirable, it means the loop is so big - that moving won't speed things up much, - and it is liable to make register usage worse. */ - - /* It is also desirable to move if it can be moved at no - extra cost because something else was already moved. */ - - if (already_moved[regno] - || (threshold * savings * m->lifetime) >= insn_count - || (m->forces && m->forces->done - && n_times_used[m->forces->regno] == 1)) - { - int count; - register struct movable *m1; - rtx first; - - /* Now move the insns that set the reg. */ - - for (count = m->consec; count >= 0; count--) - { - rtx i1, temp; - - /* If first insn of gnulib call sequence, skip to end. */ - /* Do this at start of loop, since p is guaranteed to - be an insn here. */ - if (temp = find_reg_note (p, REG_LIBCALL, 0)) - p = XEXP (temp, 0); - - /* If last insn of gnulib call sequence, move all - insns except the last before the loop. The last insn is - handled in the normal manner. */ - if (temp = find_reg_note (p, REG_RETVAL - , 0)) - { - rtx fn_address = 0; - rtx fn_reg = 0; - first = 0; - for (temp = XEXP (temp, 0); temp != p; - temp = NEXT_INSN (temp)) - { - rtx body = PATTERN (temp); - rtx n; - /* Extract the function address from the insn - that loads it into a register. - If this insn was cse'd, we get incorrect code. - So delete it and stick the fn address right - into the call insn. Since the moved insns - won't be cse'd, that does no harm. */ - if (GET_CODE (NEXT_INSN (temp)) == CALL_INSN - && GET_CODE (body) == SET - && GET_CODE (SET_DEST (body)) == REG - && (n = find_reg_note (temp, REG_EQUIV, 0))) - { - fn_reg = SET_SRC (body); - if (GET_CODE (fn_reg) != REG) - fn_reg = SET_DEST (body); - fn_address = XEXP (n, 0); - continue; - } - /* We have the call insn. - Substitute the fn address for the reg - that we believe this insn will use. */ - if (GET_CODE (temp) == CALL_INSN - && fn_address != 0) - replace_call_address (body, fn_reg, fn_address); - if (GET_CODE (temp) == CALL_INSN) - i1 = emit_call_insn_before (body, loop_start); - else - i1 = emit_insn_before (body, loop_start); - if (first == 0) - first = i1; - REG_NOTES (i1) = REG_NOTES (temp); - delete_insn (temp); - } - } - if (m->savemode != VOIDmode) - { - /* P sets REG to zero; but we should clear only the bits - that are not covered by the mode m->savemode. */ - rtx reg = SET_DEST (PATTERN (p)); - i1 = emit_insn_before - (gen_rtx (SET, VOIDmode, reg, - gen_rtx (AND, GET_MODE (reg), - reg, - gen_rtx (CONST_INT, VOIDmode, - (1 << GET_MODE_BITSIZE (m->savemode)) - 1))), - loop_start); - } - else if (GET_CODE (PATTERN (p)) == CALL_INSN) - i1 = emit_call_insn_before (PATTERN (p), loop_start); - else - i1 = emit_insn_before (PATTERN (p), loop_start); - - if (new_start == 0) - new_start = i1; - - if (loop_dump_stream) - fprintf (loop_dump_stream, " moved to %d", INSN_UID (i1)); - - /* Mark the moved, invariant reg as being equivalent to - its constant value. */ - REG_NOTES (i1) = REG_NOTES (p); - if (REG_NOTES (i1) == 0 - && ! m->partial /* But not if it's a zero-extend clr. */ - && ! m->global /* and not if used outside the loop - (since it might get set outside). */ - && CONSTANT_P (SET_SRC (PATTERN (p)))) - REG_NOTES (i1) - = gen_rtx (EXPR_LIST, REG_EQUIV, - SET_SRC (PATTERN (p)), REG_NOTES (i1)); - - /* If library call, now fix the REG_NOTES that contain - insn pointers, namely REG_LIBCALL on FIRST - and REG_RETVAL on I1. */ - if (temp = find_reg_note (i1, REG_RETVAL, 0)) - { - XEXP (temp, 0) = first; - temp = find_reg_note (first, REG_LIBCALL, 0); - XEXP (temp, 0) = i1; - } - - delete_insn (p); - do p = NEXT_INSN (p); - while (p != 0 && GET_CODE (p) == NOTE); - } - - /* The more regs we move, the less we like moving them. */ - threshold -= 3; - - /* Any other movable that loads the same register - MUST be moved. */ - already_moved[regno] = 1; - - /* This reg has been moved out of one loop. */ - moved_once[regno] = 1; - - /* The reg set here is now invariant. */ - if (! m->partial) - n_times_set[regno] = 0; - - m->done = 1; - - /* Change the length-of-life info for the register - to say it lives at least the full length of this loop. - This will help guide optimizations in outer loops. */ - - if (uid_luid[regno_first_uid[regno]] > INSN_LUID (loop_start)) - /* This is the old insn before all the moved insns. - We can't use the moved insn because it is out of range - in uid_luid. Only the old insns have luids. */ - regno_first_uid[regno] = INSN_UID (loop_start); - if (uid_luid[regno_last_uid[regno]] < INSN_LUID (end)) - regno_last_uid[regno] = INSN_UID (end); - - /* Combine with this moved insn any other matching movables. */ - - for (m1 = m->next; m1; m1 = m1->next) - if (m1->match == m) - { - rtx temp; - - /* Schedule the reg loaded by M1 - for replacement so that shares the reg of M. */ - reg_map[m1->regno] = SET_DEST (PATTERN (m->insn)); - /* Get rid of the matching insn - and prevent further processing of it. */ - m1->done = 1; - - /* if library call, delete all insn except last, which - is deleted below */ - if (temp = find_reg_note (m1->insn, REG_RETVAL, 0)) - { - for (temp = XEXP (temp, 0); temp != m1->insn; - temp = NEXT_INSN (temp)) - delete_insn (temp); - } - delete_insn (m1->insn); - - /* Any other movable that loads the same register - MUST be moved. */ - already_moved[m1->regno] = 1; - - /* The reg merged here is now invariant, - if the reg it matches is invariant. */ - if (! m->partial) - n_times_set[m1->regno] = 0; - } - } - else if (loop_dump_stream) - fprintf (loop_dump_stream, "not desirable"); - } - else if (loop_dump_stream && !m->match) - fprintf (loop_dump_stream, "not safe"); - - if (loop_dump_stream) - fprintf (loop_dump_stream, "\n"); - } - - if (new_start == 0) - new_start = loop_start; - - /* Go through all the instructions in the loop, making - all the register substitutions scheduled in REG_MAP. */ - for (p = new_start; p != end; p = NEXT_INSN (p)) - if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN - || GET_CODE (p) == CALL_INSN) - { - rtx tail; - - replace_regs (PATTERN (p), reg_map, nregs); - /* Subsitute registers in the equivalent expression also. */ - for (tail = REG_NOTES (p); tail; tail = XEXP (tail, 1)) - if (REG_NOTE_KIND (tail) == REG_EQUAL - || REG_NOTE_KIND (tail) == REG_EQUIV) - replace_regs (XEXP (tail, 0), reg_map, nregs); - } -} - -/* Optionally change a loop which enters just before the endtest - to one which falls straight in - after skipping around the entire loop if the endtest would drop out. - Returns 1 if the change was made, 0 if the loop was not really suitable. */ - -int -loop_skip_over (start, end, loop_entry_jump) - rtx start; - rtx end; - rtx loop_entry_jump; -{ - rtx entry_insn; - rtx endtest; - rtx endtestjump; - register rtx p = JUMP_LABEL (loop_entry_jump); - - while (GET_CODE (p) != INSN && GET_CODE (p) != JUMP_INSN - && GET_CODE (p) != CALL_INSN) - p = NEXT_INSN (p); - entry_insn = p; - - /* Skip any ordinary arithmetic insns to find the compare. */ - for (; p != 0; p = NEXT_INSN (p)) - if (GET_CODE (p) != NOTE) - if (GET_CODE (p) != INSN || sets_cc0_p (PATTERN (p))) - break; - if (p == 0 || GET_CODE (p) != INSN) - return 0; - endtest = p; - endtestjump = next_real_insn (p); - - /* Check that (1) we have reached a compare insn and (2) - the insn (presumably a jump) following that compare - is the last in the loop and jumps back to the loop beginning. */ - - if (sets_cc0_p (PATTERN (endtest)) > 0 - && endtestjump == prev_real_insn (end) - && prev_real_insn (JUMP_LABEL (endtestjump)) == loop_entry_jump) - { - rtx newlab; - /* This is the jump that we insert. */ - rtx new_jump; - - /* Duplicate the ordinary arith insns before the compare. */ - for (p = entry_insn; p != endtest; p = NEXT_INSN (p)) - if (GET_CODE (p) == INSN) - { - rtx new = emit_insn_before (copy_rtx (PATTERN (p)), start); - if (REG_NOTES (p)) - REG_NOTES (new) = copy_rtx (REG_NOTES (p)); - } - - /* Ok, duplicate that test before start of loop. */ - emit_insn_before (copy_rtx (PATTERN (endtest)), start); - /* Make a new entry-jump (before the original one) - whose condition is opposite to the loop-around endtest - and which jumps around the loop (to just after the ending NOTE). */ - newlab = gen_label_rtx (); - emit_label_after (newlab, end); - emit_jump_insn_before (copy_rtx (PATTERN (endtestjump)), start); - new_jump = PREV_INSN (start); - JUMP_LABEL (new_jump) = JUMP_LABEL (endtestjump); - LABEL_NUSES (JUMP_LABEL (endtestjump))++; - invert_jump (new_jump, newlab); - /* Delete the original entry-jump. */ - delete_insn (loop_entry_jump); - - return 1; - } - - return 0; -} - -/* Throughout the rtx X, replace many registers according to REG_MAP. - Return the replacement for X (which may be X with altered contents). - REG_MAP[R] is the replacement for register R, or 0 for don't replace. - NREGS is the length of REG_MAP; regs >= NREGS are not mapped. */ - -static rtx -replace_regs (x, reg_map, nregs) - rtx x; - rtx *reg_map; - int nregs; -{ - register enum rtx_code code; - register int i; - register char *fmt; - - if (x == 0) - return x; - - code = GET_CODE (x); - switch (code) - { - case PC: - case CC0: - case CONST_INT: - case CONST_DOUBLE: - case CONST: - case SYMBOL_REF: - case LABEL_REF: - return x; - - case REG: - /* Verify that the register has an entry before trying to access it. */ - if (REGNO (x) < nregs && reg_map[REGNO (x)] != 0) - return reg_map[REGNO (x)]; - return x; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - XEXP (x, i) = replace_regs (XEXP (x, i), reg_map, nregs); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - XVECEXP (x, i, j) = replace_regs (XVECEXP (x, i, j), reg_map, nregs); - } - } - return x; -} - -/* Scan X and replace the address of any MEM in it with ADDR. - REG is the address that MEM should have before the replacement. */ - -static void -replace_call_address (x, reg, addr) - rtx x, reg, addr; -{ - register enum rtx_code code; - register int i; - register char *fmt; - - if (x == 0) - return; - code = GET_CODE (x); - switch (code) - { - case PC: - case CC0: - case CONST_INT: - case CONST_DOUBLE: - case CONST: - case SYMBOL_REF: - case LABEL_REF: - case REG: - return; - - case SET: - /* Short cut for very common case. */ - replace_call_address (XEXP (x, 1), reg, addr); - return; - - case CALL: - /* Short cut for very common case. */ - replace_call_address (XEXP (x, 0), reg, addr); - return; - - case MEM: - /* If this MEM uses a reg other than the one we expected, - something is wrong. */ - if (XEXP (x, 0) != reg) - abort (); - XEXP (x, 0) = addr; - return; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - replace_call_address (XEXP (x, i), reg, addr); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - replace_call_address (XVECEXP (x, i, j), reg, addr); - } - } -} - -/* Return the number of memory refs to addresses that vary - in the rtx X. */ - -static int -count_nonfixed_reads (x) - rtx x; -{ - register enum rtx_code code; - register int i; - register char *fmt; - int value; - - if (x == 0) - return 0; - - code = GET_CODE (x); - switch (code) - { - case PC: - case CC0: - case CONST_INT: - case CONST_DOUBLE: - case CONST: - case SYMBOL_REF: - case LABEL_REF: - case REG: - return 0; - - case MEM: - return rtx_varies_p (XEXP (x, 0)) + count_nonfixed_reads (XEXP (x, 0)); - } - - value = 0; - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - value += count_nonfixed_reads (XEXP (x, i)); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - value += count_nonfixed_reads (XVECEXP (x, i, j)); - } - } - return value; -} - - -#if 0 -/* P is an instruction that sets a register to the result of a ZERO_EXTEND. - Replace it with an instruction to load just the low bytes - if the machine supports such an instruction, - and insert above LOOP_START an instruction to clear the register. */ - -static void -constant_high_bytes (p, loop_start) - rtx p, loop_start; -{ - register rtx new; - register int insn_code_number; - - /* Try to change (SET (REG ...) (ZERO_EXTEND (..:B ...))) - to (SET (STRICT_LOW_PART (SUBREG:B (REG...))) ...). */ - - new = gen_rtx (SET, VOIDmode, - gen_rtx (STRICT_LOW_PART, VOIDmode, - gen_rtx (SUBREG, GET_MODE (XEXP (SET_SRC (PATTERN (p)), 0)), - SET_DEST (PATTERN (p)), - 0)), - XEXP (SET_SRC (PATTERN (p)), 0)); - insn_code_number = recog (new, p); - - if (insn_code_number) - { - register int i; - - /* Clear destination register before the loop. */ - emit_insn_before (gen_rtx (SET, VOIDmode, - SET_DEST (PATTERN (p)), - const0_rtx), - loop_start); - - /* Inside the loop, just load the low part. */ - PATTERN (p) = new; - } -} -#endif - -/* Verify that the ostensible loop starting at START - really is a loop: nothing jumps into it from outside. - Return the marker for the end of the loop, or zero if not a real loop. - - Also set the variables `unknown_*_altered' and `loop_has_call', - and fill in the array `loop_store_addrs'. */ - -static rtx -verify_loop (f, start) - rtx f, start; -{ - register int level = 1; - register rtx insn, end; - - /* First find the LOOP_END that matches. - Also check each insn for storing in memory and record where. */ - - unknown_address_altered = 0; - unknown_aggregate_altered = 0; - fixed_aggregate_altered = 0; - loop_has_call = 0; - loop_store_addrs_idx = 0; - - num_mem_sets = 0; - loops_enclosed = 1; - loop_continue = 0; - - for (insn = NEXT_INSN (start); level > 0; insn = NEXT_INSN (insn)) - { - if (insn == 0) - /* Parse errors can cause a loop-beg with no loop-end. */ - return 0; - if (GET_CODE (insn) == NOTE) - { - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG) - { - ++level; - /* Count number of loops contained in this one. */ - loops_enclosed++; - } - else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END) - { - --level; - if (level == 0) - { - end = insn; - break; - } - } - else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_CONT) - { - if (level == 1) - loop_continue = insn; - } - - /* Don't optimize loops containing setjmps. - On some machines, longjmp does not restore the reg - values as of the time of the setjmp. */ - else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP) - return 0; - } - else if (GET_CODE (insn) == CALL_INSN) - { - unknown_address_altered = 1; - loop_has_call = 1; - } -/* ??? - else if (! unknown_address_altered) */ - else - { - if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN) - note_stores (PATTERN (insn), note_addr_stored); - } - } - - /* Now scan all jumps in the function and see if any of them can - reach a label within the range of the loop. */ - - for (insn = f; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == JUMP_INSN - /* Don't get fooled by jumps inserted by loop-optimize. - They don't have valid LUIDs, and they never jump into loops. */ - && INSN_UID (insn) < max_uid - && (INSN_LUID (insn) < INSN_LUID (start) - || INSN_LUID (insn) > INSN_LUID (end)) - /* We have a jump that is outside the loop. - Does it jump into the loop? */ - && can_jump_into_range_p (PATTERN (insn), - INSN_LUID (start), INSN_LUID (end))) - return 0; - -#if 0 - /* Now scan all labels between them and check for any jumps from outside. - This uses the ref-chains set up by find_basic_blocks. - This code is not used because it's more convenient for other reasons - to do the loop optimization before find_basic_blocks. */ - - for (insn = start; insn != end; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == CODE_LABEL) - { - register rtx y; - for (y = LABEL_REFS (insn); y != insn; y = LABEL_NEXTREF (y)) - if (INSN_LUID (CONTAINING_INSN (y)) < INSN_LUID (start) - || INSN_LUID (CONTAINING_INSN (y)) > INSN_LUID (end)) - return 0; - } -#endif - - return end; -} - -/* Return 1 if somewhere in X is a LABEL_REF to a label - located between BEG and END. */ - -static int -can_jump_into_range_p (x, beg, end) - rtx x; - int beg, end; -{ - register enum rtx_code code = GET_CODE (x); - register int i; - register char *fmt; - - if (code == LABEL_REF) - { - register int luid = INSN_LUID (XEXP (x, 0)); - return luid > beg && luid < end; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - if (can_jump_into_range_p (XEXP (x, i), beg, end)) - return 1; - } - else if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - if (can_jump_into_range_p (XVECEXP (x, i, j), beg, end)) - return 1; - } - } - - return 0; -} - -/* Return nonzero if there is a label in the range from - insn INSN to the insn whose luid is END. */ - -static int -labels_in_range_p (insn, end) - rtx insn; - int end; -{ - while (insn && INSN_LUID (insn) <= end) - { - if (GET_CODE (insn) == CODE_LABEL) - return 0; - insn = NEXT_INSN (insn); - } - - return 0; -} - -/* Record that a memory reference X is being set. */ - -static void -note_addr_stored (x) - rtx x; -{ - if (x == 0 || GET_CODE (x) != MEM) - return; - - /* Count number of memory writes. - This affects heuristics in strength_reduce. */ - num_mem_sets++; - if (unknown_address_altered) - return; - - if (GET_MODE (x) == BLKmode) - unknown_address_altered = 1; - else if (rtx_addr_varies_p (x)) - { - if (GET_CODE (XEXP (x, 0)) == PLUS) - unknown_aggregate_altered = 1; - else - unknown_address_altered = 1; - } - else - { - register int i; - register rtx addr = XEXP (x, 0); - - if (MEM_IN_STRUCT_P (x)) - fixed_aggregate_altered = 1; - for (i = 0; i < loop_store_addrs_idx; i++) - if (rtx_equal_p (loop_store_addrs[i], addr)) - { - if (loop_store_widths[i] < GET_MODE_SIZE (GET_MODE (x))) - loop_store_widths[i] = GET_MODE_SIZE (GET_MODE (x)); - break; - } - if (i == NUM_STORES) - unknown_address_altered = 1; - else if (i == loop_store_addrs_idx) - { - loop_store_widths[i] = GET_MODE_SIZE (GET_MODE (x)); - loop_store_addrs[loop_store_addrs_idx++] = addr; - } - } -} - -/* Return nonzero if the rtx X is invariant over the current loop. - - The value is 2 if we refer to something only conditionally invariant. - - If `unknown_address_altered' is nonzero, no memory ref is invariant. - Otherwise if `unknown_aggregate_altered' is nonzero, - a memory ref is invariant if it is not part of an aggregate - and its address is fixed and not in `loop_store_addrs'. - Otherwise if `fixed_aggregate_altered' is nonzero, - a memory ref is invariant - if its address is fixed and not in `loop_store_addrs'. - Otherwise, a memory ref is invariant if its address is fixed and not in - `loop_store_addrs' or if it is not an aggregate. */ - -static int -invariant_p (x) - register rtx x; -{ - register int i; - register enum rtx_code code; - register char *fmt; - int conditional = 0; - - if (x == 0) - return 1; - code = GET_CODE (x); - switch (code) - { - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case LABEL_REF: - case CONST: - return 1; - - case PC: - case CC0: - return 0; - - case REG: - /* We used to check RTX_UNCHANGING_P (x) here, but that is invalid - since the reg might be set by initialization within the loop. */ - if (x == frame_pointer_rtx || x == arg_pointer_rtx) - return 1; - if (n_times_set[REGNO (x)] == -1) - return 2; - return n_times_set[REGNO (x)] == 0; - - case MEM: - /* Constants in the constant pool are invariant. - ?? Really we should detect any constant address in the - text section. */ - if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF - && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0))) - return 1; - /* A store in a varying-address scalar (or a subroutine call) - could clobber anything in memory. */ - if (unknown_address_altered) - return 0; - /* Don't mess with volatile memory references. */ - if (MEM_VOLATILE_P (x)) - return 0; -#if 0 - /* If it's declared read-only, it is invariant - if its address is invariant. */ - if (RTX_UNCHANGING_P (x)) - return invariant_p (XEXP (x, 0)); -#endif - /* A store in a varying-address aggregate component - could clobber anything except a scalar with a fixed address. */ - if (unknown_aggregate_altered - && ((MEM_IN_STRUCT_P (x) || GET_CODE (XEXP (x, 0)) == PLUS) - || rtx_addr_varies_p (x))) - return 0; - /* A store in a fixed-address aggregate component - could clobber anything whose address is not fixed, - even an aggregate component. */ - if (fixed_aggregate_altered - && rtx_addr_varies_p (x)) - return 0; - /* Any store could clobber a varying-address scalar. */ - if (loop_store_addrs_idx - && !(MEM_IN_STRUCT_P (x) || GET_CODE (XEXP (x, 0)) == PLUS) - && rtx_addr_varies_p (x)) - return 0; - /* A store in a fixed address clobbers overlapping references. */ - for (i = loop_store_addrs_idx - 1; i >= 0; i--) - if (addr_overlap_p (x, loop_store_addrs[i], loop_store_widths[i])) - return 0; - /* It's not invalidated by a store in memory - but we must still verify the address is invariant. */ - break; - - case ASM_OPERANDS: - /* Don't mess with insns declared volatile. */ - if (MEM_VOLATILE_P (x)) - return 0; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - int tem = invariant_p (XEXP (x, i)); - if (tem == 0) - return 0; - if (tem == 2) - conditional = 1; - } - else if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - { - int tem = invariant_p (XVECEXP (x, i, j)); - if (tem == 0) - return 0; - if (tem == 2) - conditional = 1; - } - - } - } - - return 1 + conditional; -} - -/* Return 1 if OTHER (a mem ref) overlaps the area of memory - which is SIZE bytes starting at BASE. */ - -int -addr_overlap_p (other, base, size) - rtx other; - rtx base; - int size; -{ - int start = 0, end; - - if (GET_CODE (base) == CONST) - base = XEXP (base, 0); - if (GET_CODE (base) == PLUS - && GET_CODE (XEXP (base, 1)) == CONST_INT) - { - start = INTVAL (XEXP (base, 1)); - base = XEXP (base, 0); - } - - end = start + size; - return refers_to_mem_p (other, base, start, end); -} - -/* Return nonzero if all the insns in the loop that set REG - are INSN and the immediately following insns, - and if each of those insns sets REG in an invariant way - (not counting uses of REG in them). - - The value is 2 if some of these insns are only conditionally invariant. - - We assume that INSN itself is the first set of REG - and that its source is invariant. */ - -static int -consec_sets_invariant_p (reg, n_sets, insn) - int n_sets; - rtx reg, insn; -{ - register rtx p = insn; - register int regno = REGNO (reg); - rtx temp; - /* Number of sets we have to insist on finding after INSN. */ - int count = n_sets - 1; - int old = n_times_set[regno]; - int value = 0; - int this; - - /* If N_SETS hit the limit, we can't rely on its value. */ - if (n_sets == 127) - return 0; - - n_times_set[regno] = 0; - - while (count > 0) - { - register enum rtx_code code; - p = NEXT_INSN (p); - code = GET_CODE (p); - - /* If library call, skip to end of of it. */ - if (code == INSN && (temp = find_reg_note (p, REG_LIBCALL, 0))) - p = XEXP (temp, 0); - - this = 0; - if (code == INSN && GET_CODE (PATTERN (p)) == SET - && GET_CODE (SET_DEST (PATTERN (p))) == REG - && REGNO (SET_DEST (PATTERN (p))) == regno) - { - this = invariant_p (SET_SRC (PATTERN (p))); - if (this != 0) - value |= this; - else if (temp = loop_find_reg_equal (p)) - { - this = invariant_p (XEXP (temp, 0)); - if (this != 0) - value |= this; - } - } - if (this != 0) - count--; - else if (code != NOTE) - { - n_times_set[regno] = old; - return 0; - } - } - - n_times_set[regno] = old; - /* If invariant_p ever returned 2, we return 2. */ - return 1 + (value & 2); -} - -#if 0 -/* I don't think this condition is sufficient to allow INSN - to be moved, so we no longer test it. */ - -/* Return 1 if all insns in the basic block of INSN and following INSN - that set REG are invariant according to TABLE. */ - -static int -all_sets_invariant_p (reg, insn, table) - rtx reg, insn; - short *table; -{ - register rtx p = insn; - register int regno = REGNO (reg); - - while (1) - { - register enum rtx_code code; - p = NEXT_INSN (p); - code = GET_CODE (p); - if (code == CODE_LABEL || code == JUMP_INSN) - return 1; - if (code == INSN && GET_CODE (PATTERN (p)) == SET - && GET_CODE (SET_DEST (PATTERN (p))) == REG - && REGNO (SET_DEST (PATTERN (p))) == regno) - { - if (!invariant_p (SET_SRC (PATTERN (p)), table)) - return 0; - } - } -} -#endif /* 0 */ - -/* Increment N_TIMES_SET at the index of each register - that is modified by an insn between FROM and TO. - If the value of an element of N_TIMES_SET becomes 127 or more, - stop incrementing it, to avoid overflow. - - Store in *COUNT_PTR the number of actual instruction - in the loop. We use this to decide what is worth moving out. */ - -/* last_set[n] is nonzero iff reg n has been set in the current basic block. - In that case, it is the insn that last set reg n. */ - -static void -count_loop_regs_set (from, to, may_not_move, count_ptr, nregs) - register rtx from, to; - char *may_not_move; - int *count_ptr; - int nregs; -{ - register rtx *last_set = (rtx *) alloca (nregs * sizeof (rtx)); - register rtx insn; - register int count = 0; - register rtx dest; - - bzero (last_set, nregs * sizeof (rtx)); - for (insn = from; insn != to; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == CALL_INSN) - { - /* If a register is used as a subroutine address, - don't allow this register's setting to be moved out of the loop. - This condition is not at all logically correct - but it averts a very common lossage pattern - and creates lossage much less often. */ - if (GET_CODE (PATTERN (insn)) == CALL - && GET_CODE (XEXP (PATTERN (insn), 0)) == MEM - && GET_CODE (XEXP (XEXP (PATTERN (insn), 0), 0)) == REG) - { - register int regno - = REGNO (XEXP (XEXP (PATTERN (insn), 0), 0)); - may_not_move[regno] = 1; - } - else if (GET_CODE (PATTERN (insn)) == SET - && GET_CODE (SET_SRC (PATTERN (insn))) == CALL - && GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 0)) == MEM - && GET_CODE (XEXP (XEXP (SET_SRC (PATTERN (insn)), 0), 0)) == REG) - { - register int regno - = REGNO (XEXP (XEXP (SET_SRC (PATTERN (insn)), 0), 0)); - may_not_move[regno] = 1; - /* The call insn itself sets a reg, which cannot be moved. */ - may_not_move[REGNO (SET_DEST (PATTERN (insn)))] = 1; - if (n_times_set[REGNO (SET_DEST (PATTERN (insn)))] < 127) - n_times_set[REGNO (SET_DEST (PATTERN (insn)))]++; - } - } - if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN) - { - ++count; - if (GET_CODE (PATTERN (insn)) == CLOBBER - && GET_CODE (XEXP (PATTERN (insn), 0)) == REG) - /* Don't move a reg that has an explicit clobber. - We might do so sometimes, but it's not worth the pain. */ - may_not_move[REGNO (XEXP (PATTERN (insn), 0))] = 1; - else if (GET_CODE (PATTERN (insn)) == SET) - { - dest = SET_DEST (PATTERN (insn)); - while (GET_CODE (dest) == SUBREG - || GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == STRICT_LOW_PART) - dest = XEXP (dest, 0); - if (GET_CODE (dest) == REG) - { - register int regno = REGNO (dest); - /* If this is the first setting of this reg - in current basic block, and it was set before, - it must be set in two basic blocks, so it cannot - be moved out of the loop. */ - if (n_times_set[regno] > 0 && last_set[regno] == 0) - may_not_move[regno] = 1; - /* If this is not first setting in current basic block, - see if reg was used in between previous one and this. - If so, neither one can be moved. */ - if (last_set[regno] != 0 - && reg_used_between_p (dest, last_set[regno], insn)) - may_not_move[regno] = 1; - if (n_times_set[regno] < 127) - ++n_times_set[regno]; - last_set[regno] = insn; - } - } - else if (GET_CODE (PATTERN (insn)) == PARALLEL) - { - register int i; - for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) - { - register rtx x = XVECEXP (PATTERN (insn), 0, i); - if (GET_CODE (x) == CLOBBER && GET_CODE (XEXP (x, 0)) == REG) - /* Don't move a reg that has an explicit clobber. - It's not worth the pain to try to do it correctly. */ - may_not_move[REGNO (XEXP (x, 0))] = 1; - if (GET_CODE (x) == SET) - { - dest = SET_DEST (x); - while (GET_CODE (dest) == SUBREG - || GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == STRICT_LOW_PART) - dest = XEXP (dest, 0); - if (GET_CODE (dest) == REG) - { - register int regno = REGNO (dest); - if (n_times_set[regno] < 127) - ++n_times_set[regno]; - may_not_move[regno] = 1; - last_set[regno] = insn; - } - } - } - } - } - if (GET_CODE (insn) == CODE_LABEL || GET_CODE (insn) == JUMP_INSN) - bzero (last_set, nregs * sizeof (rtx)); - } - *count_ptr = count; -} - -/* Given a loop that is bounded by LOOP_START and LOOP_END - and that is entered at SCAN_START, - return 1 if the register set by insn INSN is used by - any insn that precedes INSN in cyclic order starting - from the loop entry point. */ - -static int -loop_reg_used_before_p (insn, loop_start, scan_start, loop_end) - rtx insn, loop_start, scan_start, loop_end; -{ - rtx reg = SET_DEST (PATTERN (insn)); - if (INSN_LUID (scan_start) > INSN_LUID (insn)) - return (reg_used_between_p (reg, scan_start, loop_end) - || reg_used_between_p (reg, loop_start, insn)); - else - return reg_used_between_p (reg, scan_start, insn); -} - -/* A "basic induction variable" or biv is a pseudo reg that is set - (within this loop) only by incrementing or decrementing it. */ -/* A "general induction variable" or giv is a pseudo reg whose - value is a linear function of a biv. */ - -/* Bivs are recognized by `basic_induction_var'; - Givs by `general_induct_var'. */ - -/* An enum for the two different types of givs, those that are used - as memory addresses and those that are calculated into registers. */ -enum g_types { DEST_ADDR, DEST_REG }; - -/* A `struct induction' is created for every instruction that sets - an induction variable (either a biv or a giv). */ - -struct induction -{ - rtx insn; /* The insn that sets a biv or giv */ - rtx new_reg; /* New register, containing strength reduced - version of this giv. */ - int src_regno; /* Biv from which this giv is computed. - (If this is a biv, then this is the biv.) */ - enum g_types giv_type; /* Indicate whether DEST_ADDR or DEST_REG giv */ - int dest_regno; /* Destination register for insn: this is the - register which was the biv or giv. - For a biv, this equals src_reg. - For a DEST_ADDR type giv, this is 0. */ - rtx *location; /* Place in the insn where this giv occurs. - If GIV_TYPE is DEST_REG, this is 0. */ - enum machine_mode mode; /* The mode of this biv or giv */ - rtx mult_val; /* Multiplicative factor for src_reg. */ - rtx add_val; /* Additive constant for that product. */ - int benefit; /* Gain from eliminating this insn. */ - int consec; /* The number of consecutive insn that set this - register; they are all eliminated if this - one is. */ - char replaceable; /* 1 if we can substitute the strength-reduced - variable for the original variable. - 0 means they must be kept separate and the - new one must be copied into the old pseudo - reg each time the old one is set. */ - char ignore; /* 1 prohibits further processing of this giv */ - int lifetime; /* Length of life of this giv */ - int times_used; /* # times this giv is used. */ - struct induction *family; /* Links together all induction variables that - have the same src register. */ - struct induction *forces; /* Points to an induction variable insn which - is used only once, to compute this giv, - and hence can be deleted if this insn is - strength reduced. */ - struct induction *forces2; /* Likewise. */ - struct induction *same; /* Links together all induction variables that - have the same tuple (src, mult, add). */ -}; - -/* A `struct iv_class' is created for each biv. */ - -struct iv_class { - int regno; /* Pseudo reg which is the biv. */ - int biv_count; /* Number of insns setting this reg. */ - struct induction *biv; /* List of all insns that set this reg. */ - int giv_count; /* Number of DEST_REG givs computed from this - biv. The resulting count is only used in - check_dbra_loop. */ - struct induction *giv; /* List of all insns that compute a giv - from this reg. */ - int total_benefit; /* Sum of BENEFITs of all those givs */ - rtx initial_value; /* Value of reg at loop start */ - struct iv_class *next; /* Links all class structures together */ - rtx init_insn; /* insn which intializes biv, 0 if none seen. */ - char eliminable; /* 1 if plausible candidate for elimination. */ - char nonneg; /* 1 if we added a REG_NONNEG note for this. */ -}; - -/* Definitions used by the basic induction variable discovery code. */ -enum iv_mode { UNKNOWN_INDUCT, BASIC_INDUCT, NOT_BASIC_INDUCT, - GENERAL_INDUCT }; - -/* Relative gain of eliminating various kinds of operations. */ -#define NO_BENEFIT 0 -#define ADD_BENEFIT 1 -#define SHIFT_BENEFIT 2 -#define MULT_BENEFIT 4 -#define LIBCALL_BENEFIT 15 -/* Benefit penalty, if a giv is not replaceable, i.e. must emit an insn to - copy the value of the strength reduced giv to its original register. */ -#define COPY_PENALTY 2 - -/* Indexed by register number, indicates whether or not register is an - induction variable, and if so what type. */ - -static enum iv_mode *induct_var; - -/* Indexed by register number, contains pointer to `struct induction' - if register is a general induction variable. */ - -static struct induction **induct_struct; - -/* Indexed by register number, contains pointer to `struct iv_class' - if register is a basic induction variable. */ - -static struct iv_class **class_struct; - -/*********************************/ - -/* ??? Unfinished optimizations, wilson@ji.Berkeley.EDU */ - -/* strength reduce addresses found in sources (set () (mem ())*/ - -/* There is one more optimization you might be interested in doing: to - allocate pseudo registers for frequently-accessed memory locations. - If the same memory location is referenced each time around, it might - be possible to copy it into a register before and out after. - This is especially useful when the memory location is a variable which - is in a stack slot because somewhere its address is taken. If the - loop doesn't contain a function call and the variable isn't volatile, - it is safe to keep the value in a register for the duration of the - loop. One tricky thing is that the copying of the value back from the - register has to be done on all exits from the loop. You need to check that - all the exits from the loop go to the same place. */ - -/* WARNING: the interaction of biv elimination, and recognizing 'constant' - bivs may cause problems */ - -/* add heuristic so that DEST_ADDR strength reduction does not cause - performance problems */ - -/* don't eliminate things that can be combined with an addressing mode? - find all giv that have same biv and mult_val (now must also have - same add_val), then for each giv, check to see if its only use - dies in a following memory address, generate a new memory address - and check to see if valid, if valid then store modified mem addr, - else if not valid addr mark giv as not done so that it will get its - own iv */ - -/* consec_sets_giv does not calculate replaceable and forces correctly, - forces should be a more general linked list instead of two entries */ - -/* try to optimize branches when it is known that a biv is always positive */ - -/* when replace biv in compare insn, should replace with closest giv so that - an optimized branch can still be recognized by combiner, i.e. VAXen acb */ - -/* should merge final_value calculation in check_dbra_loop with the - new final_biv_value function */ - -/* many of the checks involving uid_luid could be simplified if regscan - was rerun in loop_optimize() whenever a register was added or moved, - also some of the optimizations could be a little less conservative */ - -/* Perform strength reduction and induction variable elimination. */ - -/* Pseudo registers created during this function will be beyond the last - valid index in several tables including n_times_set and regno_last_uid. - This does not cause a problem here, because the added registers cannot be - givs outside of their loop, and hence will never be reconsidered. - But scan_loop must check regnos to make sure they are in bounds. */ - -static void -strength_reduce (scan_start, end, loop_top, insn_count, - loop_start, loop_end, nregs) - rtx scan_start; - rtx end; - rtx loop_top; - int insn_count; - rtx loop_start; - rtx loop_end; - int nregs; -{ - rtx p; - rtx inc_val; - rtx mult_val; - int dest_regno; - int biv_found; - /* This is 1 if current insn could be executed zero times in the loop. */ - int maybe_never = 0; - /* List of all possible basic induction variables. */ - struct iv_class *iv_list = 0; - /* Temporary list pointers for traversing iv_list. */ - struct iv_class *bl, *backbl; - /* Ratio of extra register life span we can justify - for saving an instruction. More if loop doesn't call subroutines - since in that case saving an insn makes more difference - and more registers are available. */ - /* ??? could set this to last value of threshold in move_movables */ - int threshold = loop_has_call ? 17 : 34; - /* Map of pseudo-register replacements. */ - rtx *reg_map; - int call_seen; - - induct_var = (enum iv_mode *) alloca (nregs * sizeof (induct_var[0])); - bzero ((char *)induct_var, nregs * sizeof (induct_var[0])); - induct_struct = (struct induction **) - alloca (nregs * sizeof (struct induction *)); - bzero ((char *)induct_struct, nregs * sizeof (struct induction *)); - class_struct = (struct iv_class **) - alloca (nregs * sizeof (struct iv_class *)); - bzero ((char *)class_struct, nregs * sizeof (struct iv_class *)); - - /* Scan through loop to find all possible bivs. */ - - for (p = NEXT_INSN (loop_start); p != end; p = NEXT_INSN (p)) - { - if (GET_CODE (p) == INSN - && GET_CODE (PATTERN (p)) == SET - && GET_CODE (SET_DEST (PATTERN (p))) == REG) - { - dest_regno = REGNO (SET_DEST (PATTERN (p))); - if (induct_var[dest_regno] != NOT_BASIC_INDUCT - && dest_regno >= FIRST_PSEUDO_REGISTER) - { - if (basic_induction_var (SET_SRC (PATTERN (p)), dest_regno, - &inc_val, &mult_val)) - { - /* It is a possible basic induction variable. - Create and initialize an induction structure for it. */ - - struct induction *v = - (struct induction *) alloca (sizeof (struct induction)); - - v->insn = p; - v->src_regno = dest_regno; - v->dest_regno = dest_regno; - v->mult_val = mult_val; - v->add_val = inc_val; - v->mode = GET_MODE (SET_DEST (PATTERN (p))); - - /* Add this to the reg's iv_class, creating a class - if this is the first incrementation of the reg. */ - - bl = class_struct[dest_regno]; - if (bl) - { - v->family = bl->biv; - bl->biv = v; - bl->biv_count++; - } - else - { - /* Create and initialize new iv_class. */ - - bl = (struct iv_class *) alloca (sizeof (struct iv_class)); - - bl->regno = dest_regno; - bl->biv = v; - v->family = 0; - bl->giv = 0; - bl->biv_count = 1; - bl->giv_count = 0; - - /* Set initial value to the reg itself. */ - bl->initial_value = SET_DEST (PATTERN (p)); - /* We haven't seen the intializing insn yet */ - bl->init_insn = 0; - bl->eliminable = 0; - bl->nonneg = 0; - - /* Add this insn to iv_list. */ - bl->next = iv_list; - iv_list = bl; - - /* Put it in the array of iv_lists. */ - class_struct[dest_regno] = bl; - } - - induct_var[dest_regno] = BASIC_INDUCT; - - if (loop_dump_stream) - { - fprintf (loop_dump_stream, - "Insn %d: possible biv, reg %d,", - INSN_UID (p), dest_regno); - if (GET_CODE (inc_val) == CONST_INT) - fprintf (loop_dump_stream, " const = %d\n", - INTVAL (inc_val)); - else - { - fprintf (loop_dump_stream, " const = "); - print_rtl (loop_dump_stream, inc_val); - fprintf (loop_dump_stream, "\n"); - } - } - } - else - induct_var[dest_regno] = NOT_BASIC_INDUCT; - } - } - } - - /* Scan iv_list to remove all regs that proved not to be bivs. - Make a sanity check against n_times_set. */ - - biv_found = 0; - - for (backbl = bl = iv_list; bl; backbl = bl, bl = bl->next) - { - if (induct_var[bl->regno] != BASIC_INDUCT) - { - /* Not a basic induction variable, remove this iv_class. */ - - if (backbl == bl) - iv_list = bl->next; - else - backbl->next = bl->next; - - if (loop_dump_stream) - fprintf (loop_dump_stream, "Reg %d: biv discarded, not induct\n", - bl->regno); - } - else if (n_times_set[bl->regno] != bl->biv_count) - { - /* This happens if register modified by subreg, etc. */ - /* Make sure it is not recognized as a basic induction var: */ - /* remove this iv_class from iv_list. */ - - induct_var[bl->regno] = NOT_BASIC_INDUCT; - - if (backbl == bl) - iv_list = bl->next; - else - backbl->next = bl->next; - - if (loop_dump_stream) - fprintf (loop_dump_stream, "Reg %d: biv discarded, count error\n", - bl->regno); - } - else - { - /* This is a valid basic induction variable. */ - - biv_found++; - - if (loop_dump_stream) - fprintf (loop_dump_stream, "Reg %d: biv verified\n", bl->regno); - } - } - - /* Exit if there are no bivs. */ - if (!iv_list) - return; - - /* Find initial value for each biv. */ - /* Search backwards from loop_start, halting at first label - or when all bivs have been seen. */ - - call_seen = 0; - p = loop_start; - while (biv_found) - { - p = PREV_INSN (p); - if (p == 0) - break; - - if (GET_CODE (p) == CALL_INSN) - call_seen = 1; - - if (GET_CODE (p) == INSN - && GET_CODE (PATTERN (p)) == SET) - { - rtx dest = SET_DEST (PATTERN (p)); - - while (GET_CODE (dest) == SUBREG - || GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == STRICT_LOW_PART) - dest = XEXP (dest, 0); - - if (GET_CODE (dest) == REG) - { - int dest_regno = REGNO (dest); - if (induct_var[dest_regno] == BASIC_INDUCT - && class_struct[dest_regno]->init_insn == 0) - { - /* This is the first modification found for this reg. */ - - rtx src = SET_SRC (PATTERN (p)); - - /* Record the intializing INSN */ - - class_struct[dest_regno]->init_insn = p; - - if (loop_dump_stream) - fprintf (loop_dump_stream, "Biv %d initialized at insn %d: ", - dest_regno, INSN_UID (p)); - - /* Save value if it is a constant or register. */ - if (CONSTANT_P (src) - || (GET_CODE (src) == REG - /* Don't try to use a value in a hard reg - across a call which clobbers it. */ - && ! (REGNO (src) < FIRST_PSEUDO_REGISTER - && call_used_regs[REGNO (src)] - && call_seen) - && ! reg_set_between_p (src, p, loop_start))) - { - class_struct[dest_regno]->initial_value = src; - - if (loop_dump_stream) - fprintf (loop_dump_stream, "initial value "); - if (loop_dump_stream) - { - if (GET_CODE (src) == CONST_INT) - fprintf (loop_dump_stream, "%d\n", INTVAL (src)); - else - { - print_rtl (loop_dump_stream, src); - fprintf (loop_dump_stream, "\n"); - } - } - } - else - { - /* Biv initial value is not simple move, - so let it keep intial value of "itself". */ - - if (loop_dump_stream) - fprintf (loop_dump_stream, "complex initial value\n"); - } - - biv_found--; - } - } - } - else if (GET_CODE (p) == CODE_LABEL) - break; - } - - /* Search the loop for general induction variables. */ - - /* A register is a giv if: it is only set once, it is a function of a - biv and a constant (or invariant), and it is not a biv. */ - - p = scan_start; - while (1) - { - p = NEXT_INSN (p); - /* At end of a straight-in loop, we are done. - At end of a loop entered at the bottom, scan the top. */ - if (p == scan_start) - break; - if (p == end) - { - if (loop_top != 0) - p = NEXT_INSN (loop_top); - else - break; - if (p == scan_start) - break; - } - - /* Look for a general induction variable in a register. */ - if (GET_CODE (p) == INSN - && GET_CODE (PATTERN (p)) == SET - && GET_CODE (SET_DEST (PATTERN (p))) == REG - && ! may_not_optimize[REGNO (SET_DEST (PATTERN (p)))]) - { - int src_regno; - rtx add_val; - rtx mult_val; - int benefit; - rtx regnote = 0; - struct induction *forces = 0; - struct induction *forces2 = 0; - - dest_regno = REGNO (SET_DEST (PATTERN (p))); - if (dest_regno < FIRST_PSEUDO_REGISTER) - continue; - - if (/* Normal giv. */ - ((benefit = general_induction_var (SET_SRC (PATTERN (p)), - &src_regno, &add_val, - &mult_val, - &forces, &forces2)) - /* Giv set with call to a library routine. */ - || ((regnote = loop_find_reg_equal (p)) - && - (benefit = general_induction_var (XEXP (regnote, 0), - &src_regno, - &add_val, &mult_val, - &forces, &forces2)))) - /* Don't try to handle any regs made by loop optimization. - We have nothing on them in regno_first_uid, etc. */ - && dest_regno < old_max_reg - /* Don't recognize a BASIC_INDUCT_VAR here. */ - && dest_regno != src_regno - /* This must be the only place where the register is set. */ - && (n_times_set[dest_regno] == 1 - || (benefit = consec_sets_giv (benefit, p, - src_regno, dest_regno, - &add_val, &mult_val)))) - { - int count; - struct induction *v = - (struct induction *) alloca (sizeof (struct induction)); - rtx temp; - - record_giv (v, p, src_regno, dest_regno, mult_val, add_val, benefit, - forces, forces2, DEST_REG, maybe_never, 0, loop_end); - - /* Skip the consecutive insns, if there are any. */ - for (count = v->consec - 1; count >= 0; count--) - { - /* If first insn of libcall sequence, skip to end. */ - /* Do this at start of loop, since INSN is guaranteed to - be an insn here. */ - if (temp = find_reg_note (p, REG_LIBCALL, 0)) - { - /* Eliminating a libcall does more good than - eliminating a single insn to do the same job. */ - benefit += LIBCALL_BENEFIT; - p = XEXP (temp, 0); - } - - do p = NEXT_INSN (p); - while (GET_CODE (p) == NOTE); - } - } - } - -#ifndef DONT_REDUCE_ADDR - /* Look for givs which are memory addresses. */ - /* This resulted in worse code on a VAX 8600. I wonder if it - still does. */ - if (GET_CODE (p) == INSN) - find_mem_givs (PATTERN (p), p, maybe_never, loop_end); -#endif - - /* Past a label or a jump, we get to insns for which we can't count - on whether or how many times they will be executed during each - iteration. Givs found afterwards cannot be marked replaceable. */ - if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN) - maybe_never = 1; - } - - /* Try to prove that the loop counter variable (if any) is always - nonnegative; if so, record that fact with a REG_NONNEG note - so that "decrement and branch until zero" insn can be used. */ - check_dbra_loop (loop_end, iv_list, insn_count, loop_start); - - /* Create reg_map to hold substitutions for replaceable giv regs. */ - reg_map = (rtx *) alloca (nregs * sizeof (rtx)); - bzero ((char *)reg_map, nregs * sizeof (rtx)); - - /* Examine each iv class for feasibility of strength reduction/induction - variable elimination. */ - - for (bl = iv_list; bl; bl = bl->next) - { - struct induction *v; - int benefit; - int replaceable; - int all_reduced; - rtx final_value = 0; - - /* Test whether it will be possible to eliminate this biv - provided all givs are reduced. This is possible if either - the reg is not used outside the loop, or we can compute - what its final value will be. - - Don't try if we put a REG_NONNEG note on the endtest for this biv. - ??? That should be only on machines that have dbra insns. */ - - /* Compare against bl->init_insn rather than loop_start. - We aren't concerned with any uses of the biv between - init_insn and loop_start since these won't be affected - by the value of the biv elsewhere in the function, so - long as init_insn doesn't use the biv itself. - March 14, 1989 -- self@bayes.arc.nasa.gov */ - - if ((uid_luid[regno_last_uid[bl->regno]] < INSN_LUID (loop_end) - && bl->init_insn - && INSN_UID (bl->init_insn) < max_uid - && uid_luid[regno_first_uid[bl->regno]] >= INSN_LUID (bl->init_insn) - && ! reg_mentioned_p (SET_DEST (PATTERN (bl->biv->insn)), - SET_SRC (PATTERN (bl->init_insn))) - && ! bl->nonneg) - || (final_value = final_biv_value (bl, loop_end))) - check_eliminate_biv (bl, loop_start, end); - else - { - if (loop_dump_stream) - { - fprintf (loop_dump_stream, - "Cannot eliminate biv %d.\n", - bl->regno); - fprintf (loop_dump_stream, - "First use: insn %d, last use: insn %d.\n", - regno_first_uid[bl->regno], - regno_last_uid[bl->regno]); - } - } - - /* This will be true at the end, if all givs which depend on this - biv have been strength reduced. - We can't (currently) eliminate the biv unless this is so. */ - all_reduced = 1; - - /* Check each giv in this class. */ - - for (v = bl->giv; v; v = v->family) - { - struct induction *tv; - - if (v->ignore) - continue; - - benefit = v->benefit; - replaceable = v->replaceable; - - /* Reduce benefit if not replaceable, since we will insert - a move-insn to replace the insn that calculates this giv. */ - if (!replaceable && ! bl->eliminable) - benefit -= COPY_PENALTY; - - /* Decrease the benefit to count the add-insns that we will - insert to increment the reduced reg for the giv. */ - benefit -= ADD_BENEFIT * bl->biv_count; - - /* Find all equivalent givs (that bear same relation to the biv). - Link them via the `same' field and add their benefits together. - They can be replaced with a single register. */ - - for (tv = v->family; tv; tv = tv->family) - { - if (tv->ignore == 0 - && tv->src_regno == v->src_regno - && rtx_equal_p (tv->mult_val, v->mult_val) - && rtx_equal_p (tv->add_val, v->add_val)) - { - benefit += tv->benefit; - if (! tv->replaceable) - benefit -= COPY_PENALTY; - v->lifetime += tv->lifetime; - v->times_used += tv->times_used; - tv->ignore = 1; - - /* Link them together via `same' field. */ - tv->same = v->same; - v->same = tv; - - if (loop_dump_stream) - fprintf (loop_dump_stream, - "giv of insn %d combined with that of %d.\n", - INSN_UID (v->insn), INSN_UID (tv->insn)); - } - } - - /* Decide whether to strength-reduce this giv - or to leave the code unchanged - (recompute it from the biv each time it is used). - This decision can be made independently for each giv. */ - - /* ??? Perhaps attempt to guess whether autoincrement will handle - some of the new add insns; if so, can increase BENEFIT - (undo the subtraction of ADD_BENEFIT that was done above). */ - - /* If an insn is not to be strength reduced, then set its ignore - flag, and clear all_reduced. */ - - /* Is it right to consider times_used? */ - - /* ??? What about the insns that are 'forced' by this one? - Although this insn is not worthwhile to reduce, it may be - worthwhile to reduce the simpler givs used to compute this - complex giv. */ - - /* ??? Hey! If a giv has its forces field set, then that means - it is not computed directly from the biv, it is instead computed - from a simpler giv. If we define UNFORCE_INSNS, then the simpler - giv will be considered for strength reduction, and this giv should - not cause all_reduced to be cleared because it DOESN'T use the - biv!!! If the simpler giv can not be reduced, then that simpler - biv will still cause all_reduced to be cleared. */ - - if (benefit <= 0) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, "giv of insn %d, no benefit\n", - INSN_UID (v->insn)); - v->ignore = 1; - all_reduced = 0; - } - - if (v->lifetime * threshold * benefit < insn_count) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "giv of insn %d not worth while, %d vs %d.\n", - INSN_UID (v->insn), - v->lifetime * threshold * benefit, insn_count); - v->ignore = 1; - all_reduced = 0; - } - - /* Now check that we can increment the reduced giv - without needing a multiply insn. If not, reject it. */ - - if (! v->ignore) - { - int success = 1; - - for (tv = bl->biv; tv; tv = tv->family) - if (tv->mult_val == const1_rtx) - success &= product_cheap_p (tv->add_val, v->mult_val); - - if (! success) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "giv of insn %d: would need a multiply.\n", - INSN_UID (v->insn)); - v->ignore = 1; - all_reduced = 0; - } - } - } - - /* Reduce each giv that we decided to reduce. */ - - for (v = bl->giv; v; v = v->family) - { - struct induction *tv; - if (! v->ignore) - { - rtx new_reg; - - /* Note Iris compiler dies if ?: is used inside gen_reg_rtx. */ - if (v->giv_type == DEST_ADDR) - new_reg = gen_reg_rtx (Pmode); - else - new_reg = gen_reg_rtx (GET_MODE (SET_DEST (PATTERN (v->insn)))); - - /* For each place where the biv is incremented, - add an insn to increment the new, reduced reg for the giv. - Insert it before the insn that sets the biv, - so that the biv increment remains last before the endtest, - so that dbra will still be recognized. */ - - for (tv = bl->biv; tv; tv = tv->family) - { - struct induction *iv; - rtx before_insn = tv->insn; - - /* If this increment is between the setting of the giv and - its use, don't increment until after the use. */ - for (iv = v; iv; iv = iv->same) - { - if (INSN_LUID (tv->insn) <= INSN_LUID (iv->insn) - && ((iv->forces - && (INSN_LUID (tv->insn) - >= INSN_LUID (iv->forces->insn)) - || (iv->forces2 - && (INSN_LUID (tv->insn) - >= INSN_LUID (iv->forces2->insn)))))) - { - before_insn = NEXT_INSN (iv->insn); - break; - } - } - - if (tv->mult_val == const1_rtx) - emit_iv_inc (tv->add_val, v->mult_val, - new_reg, before_insn); - else /* tv->mult_val == const0_rtx */ - /* A multiply is acceptable here - since this is presumed to be seldom executed. */ - emit_iv_init_code (tv->add_val, v->mult_val, - v->add_val, new_reg, before_insn); - } - - /* Add code at loop start to initialize giv's reduced reg. */ - - emit_iv_init_code (bl->initial_value, v->mult_val, - v->add_val, new_reg, loop_start); - /* If the initial value uses a register, - then we may have just extended its range of appearance. - Update this conservatively for the sake of outer loops. */ - if (GET_CODE (bl->initial_value) == REG - && (uid_luid[regno_last_uid[REGNO (bl->initial_value)]] - < INSN_LUID (loop_start))) - uid_luid[regno_last_uid[REGNO (bl->initial_value)]] - = INSN_LUID (loop_start); - - /* For each giv register that can be reduced now: - delete old insn that modifies the giv, - if replaceable, substitute reduced reg - wherever the old giv occurs; - else add new move insn "giv_reg = reduced_reg". */ - - for (tv = v; tv; tv = tv->same) - { - /* Record the identity of the reduced reg. */ - tv->new_reg = new_reg; - - if (tv->giv_type == DEST_ADDR) - { - /* Store reduced reg as the address in the memref - where we found this giv. */ - * tv->location = new_reg; - } - else if (tv->replaceable) - { - reg_map[tv->dest_regno] = new_reg; - /* If giv lives after end of loop, - emit insn to copy reduced reg into old reg, - at the end of the loop. - ?? insufficient; used before loop could - mean live after loop, due to surrounding loop. */ - /* Currently a giv used outside - the loop will not be marked replaceable, - so these deficiencies don't really hurt. */ - if (uid_luid[regno_last_uid[tv->dest_regno]] - > uid_luid[INSN_UID (loop_end)]) - { - /* ?? This won't work. We need to do this at - ALL exits. */ - emit_insn_after (gen_rtx (SET, VOIDmode, - SET_DEST (PATTERN (tv->insn)), - new_reg), - loop_end); - abort (); - } - } - else - { - /* Not replaceable; emit an insn to set the - original giv reg from the reduced giv. */ - - int count; - rtx after_insn = tv->insn; - - for (count = tv->consec; count > 0; count--) - after_insn = next_real_insn (after_insn); - - /* Put new insn after, not before, in case - after_insn is the end of a libcall. */ - emit_insn_after (gen_rtx (SET, VOIDmode, - SET_DEST (PATTERN (tv->insn)), - new_reg), - after_insn); - } - - /* Delete the insn that used to set the old giv reg, - unless we modified an address in it. - In any case, delete the other insns used for this one. */ - delete_insn_forces (tv, tv->giv_type != DEST_ADDR); - - if (loop_dump_stream) - fprintf (loop_dump_stream, "giv at %d reduced to reg %d\n", - INSN_UID (tv->insn), REGNO (new_reg)); - } - /* One set of equivalent givs has been strength-reduced. */ - } -#if 0 - else if (v->new_reg == 0) - { - /* This giv wasn't reduced and is not worth reducing. */ - - for (tv = v; tv; tv = tv->same) - if (loop_dump_stream) - fprintf (loop_dump_stream, "giv at %d not reduced\n", - INSN_UID (tv->insn)); - - all_reduced = 0; - } -#endif - } - - /* All the givs in this family have been reduced if they merit it. */ - - /* Try to eliminate the biv, if it is a candidate. - This won't work if ! all_reduced, - since the givs we planned to use might not have been reduced. */ - - if (all_reduced == 1 && bl->eliminable) - { - /* Get the REG rtx for the biv. */ - rtx reg = SET_DEST (PATTERN (bl->biv->insn)); - - for (p = loop_start; p != end; p = NEXT_INSN (p)) - { - enum rtx_code code = GET_CODE (p); - if ((code == INSN || code == JUMP_INSN || code == CALL_INSN) - && reg_mentioned_p (reg, PATTERN (p)) - && SET_DEST (PATTERN (p)) == cc0_rtx) - /* Found a compare instruction using this biv; - rewrite it to use a related giv. */ - { - struct induction *v1; - /* If this is an insn which uses the biv ONLY in the - calculation of a giv which is in the family of this - biv, it's ok becuase it will go away when the giv is - reduced. */ - for (v1 = bl->giv; v1; v1 = v1->family) - if (v1->insn == p) - { - if (v1->giv_type == DEST_REG - || (v1->giv_type == DEST_ADDR - /* I thought the test was backwards, - but then I found the real problem - was in the subroutine. */ - && ! other_reg_use_p (reg, *(v1->location), - PATTERN (p)))) - break; - } - if (!v1) - eliminate_biv (p, bl, loop_start); - } - } - - /* Biv is no longer really needed inside the loop, - so delete all insns that set the biv. */ - - for (v = bl->biv; v; v = v->family) - delete_insn (v->insn); - - /* ?? If we created a new test to bypass the loop entirely, - or otherwise drop straight in, based on this test, then - we might want to rewrite it also. This way some later - pass has more hope of removing the intialization of this - biv entirely. */ - - /* If final_value != 0, then biv may be used after loop end - and we must emit an insn to set it just in case. */ - if (final_value != 0) - emit_insn_after (gen_rtx (SET, VOIDmode, reg, final_value), - loop_end); - - if (loop_dump_stream) - fprintf (loop_dump_stream, "Reg %d: biv eliminated\n", - bl->regno); - } - } - - /* Go through all the instructions in the loop, making all the - register substitutions scheduled in REG_MAP. */ - - for (p = loop_start; p != end; p = NEXT_INSN (p)) - if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN - || GET_CODE (p) == CALL_INSN) - replace_regs (PATTERN (p), reg_map, nregs); - - if (loop_dump_stream) - fprintf (loop_dump_stream, "\n"); -} - -/* Nonzero if register REG appears somewhere within IN, other than in - subexpressions EQ to EXPR. This is a modification of reg_mentioned_p. */ - -int -other_reg_use_p (reg, expr, in) - register rtx reg, expr, in; -{ - register char *fmt; - register int i; - register enum rtx_code code; - - if (in == 0 || in == expr) - return 0; - - if (reg == in) - return 1; - - code = GET_CODE (in); - - switch (code) - { - /* Compare registers by number. */ - case REG: - return GET_CODE (reg) == REG && REGNO (in) == REGNO (reg); - - /* These codes have no constituent expressions - and are unique. */ - case CC0: - case PC: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case CODE_LABEL: - return 0; - } - - fmt = GET_RTX_FORMAT (code); - - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (in, i) - 1; j >= 0; j--) - if (other_reg_use_p (reg, expr, XVECEXP (in, i, j))) - return 1; - } - else if (fmt[i] == 'e' - && other_reg_use_p (reg, expr, XEXP (in, i))) - return 1; - } - return 0; -} - -/* Scan X for memory refs and check each memory address - as a possible giv. INSN is the insn whose pattern X comes from. - MAYBE_NEVER is 1 if the loop might execute INSN zero times. */ - -static void -find_mem_givs (x, insn, maybe_never, loop_end) - rtx x; - rtx insn; - int maybe_never; - rtx loop_end; -{ - register int i, j; - register enum rtx_code code; - register char *fmt; - - if (x == 0) - return; - - code = GET_CODE (x); - switch (code) - { - case REG: - case CONST_INT: - case CONST: - case CONST_DOUBLE: - case SYMBOL_REF: - case LABEL_REF: - case PC: - case CC0: - case ADDR_VEC: - case ADDR_DIFF_VEC: - case USE: - case CLOBBER: - return; - - case MEM: - { - int src_regno; - rtx add_val; - rtx mult_val; - int benefit; - struct induction *forces = 0; - struct induction *forces2 = 0; - - benefit = general_induction_var (XEXP (x, 0), - &src_regno, &add_val, &mult_val, - &forces, &forces2); - if (benefit > 0) - { - /* Found one; record it. */ - struct induction *v = - (struct induction *) oballoc (sizeof (struct induction)); - - record_giv (v, insn, src_regno, 0, mult_val, add_val, benefit, - forces, forces2, DEST_ADDR, maybe_never, &XEXP (x, 0), - loop_end); - } - return; - } - } - - /* Recursively scan the subexpressions for other mem refs. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - find_mem_givs (XEXP (x, i), insn, maybe_never, loop_end); - else if (fmt[i] == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - find_mem_givs (XVECEXP (x, i, j), insn, maybe_never, loop_end); -} - -/* Fill in the data about one giv. - V is the `struct induction' in which we record the giv. (It is - allocated by the caller, with alloca.) - INSN is the insn that sets it. - BENEFIT estimates the savings from deleting this insn. - TYPE is DEST_REG or DEST_ADDR; it says whether the giv is computed - into a register or is used as a memory address. - - SRC_REGNO is the biv reg number which the giv is computed from. - DEST_REGNO is the giv's reg number (if the giv is stored in a reg). - MULT_VAL and ADD_VAL are the coefficients used to compute the giv. - FORCES and FORCES2, if nonzero, are other `struct induction's for - other givs which are used to compute this giv indirectly. - LOCATION points to the place where this giv's value appears in INSN. */ - -static void -record_giv (v, insn, src_regno, dest_regno, mult_val, add_val, benefit, - forces, forces2, type, maybe_never, location, loop_end) - struct induction *v; - rtx insn; - int src_regno, dest_regno; - rtx mult_val, add_val; - int benefit; - struct induction *forces, *forces2; - enum g_types type; - int maybe_never; - rtx *location; - rtx loop_end; -{ - struct induction *b; - struct iv_class *bl; - - v->insn = insn; - v->src_regno = src_regno; - v->giv_type = type; - v->dest_regno = dest_regno; - v->mult_val = mult_val; - v->add_val = add_val; - v->benefit = benefit; - v->location = location; - - if (type == DEST_ADDR) - { - v->mode = GET_MODE (*location); - v->consec = 0; - v->lifetime = 1; - v->times_used = 1; - } - else /* type == DEST_REG */ - { - v->mode = GET_MODE (SET_DEST (PATTERN (insn))); - v->consec = n_times_set[dest_regno] - 1; - v->lifetime = (uid_luid[regno_last_uid[dest_regno]] - - uid_luid[regno_first_uid[dest_regno]]); - v->times_used = n_times_used[dest_regno]; - } - - v->same = 0; - v->forces = 0; - v->forces2 = 0; - v->ignore = 0; - v->new_reg = 0; - - /* Mark giv as forced if it is only used to compute another giv. */ - - /* This check is not sufficient as INSN may have been moved giving - it a new uid, so make another check by calculating lifetimes. - This is overconservative but seems to be correct. */ - - if (forces) - { - v->benefit += forces->benefit; - if ((regno_last_uid[forces->dest_regno] == INSN_UID (insn) - || - ((uid_luid[regno_last_uid[forces->dest_regno]] - - uid_luid[regno_first_uid[forces->dest_regno]]) - == (INSN_LUID (insn) - INSN_LUID (forces->insn)))) - && !reg_used_between_p (SET_DEST (PATTERN (forces->insn)), - forces->insn, insn)) - { - v->forces = forces; - forces->ignore = 1; - } - } - - if (forces2) - { - v->benefit += forces2->benefit; - if ((regno_last_uid[forces2->dest_regno] == INSN_UID (insn) - || - ((uid_luid[regno_last_uid[forces2->dest_regno]] - - uid_luid[regno_first_uid[forces2->dest_regno]]) - == (INSN_LUID (insn) - INSN_LUID (forces2->insn)))) - && !reg_used_between_p (SET_DEST (PATTERN (forces2->insn)), - forces2->insn, insn)) - { - if (v->forces) - v->forces2 = forces2; - else - v->forces = forces2; - forces2->ignore = 1; - } - } - - if (type == DEST_REG) - { - induct_var[dest_regno] = GENERAL_INDUCT; - induct_struct[dest_regno] = v; - } - - /* Add the giv to the class of givs computed from one biv. */ - - bl = class_struct[src_regno]; - if (bl) - { - v->family = bl->giv; - bl->giv = v; - /* Don't count DEST_ADDR. This is supposed to count the number of - insns that calculate givs. */ - if (type == DEST_REG) - bl->giv_count++; - bl->total_benefit += benefit; - } - else - /* Fatal error, biv missing for this giv? */ - abort (); - - if (type == DEST_ADDR) - v->replaceable = 1; - else - { - /* The giv can be replaced outright by the reduced register if - - the insn that sets the giv is always executed on any iteration - on which the giv is used at all - (there are two ways to deduce this: - either the insn is executed on every iteration, - or all uses follow that insn in the same basic block), - - the giv is not used before the insn that sets it, - i.e. no definition outside loop reaches into loop - - no assignments to the biv occur during the giv's lifetime. */ - - /* Is this right? Don't we need to make sure the giv is not used - outside the loop. Someday we will know where all the loop exits - are so we can do better, but until then.... - March 18, 1989 -- self@bayes.arc.nasa.gov */ - - if (regno_first_uid[dest_regno] == INSN_UID (insn) - /* Previous line always fails if INSN was moved by loop opt. */ - && uid_luid[regno_last_uid[dest_regno]] < INSN_LUID (loop_end) - && (!maybe_never || last_use_this_basic_block (dest_regno, insn))) - { - v->replaceable = 1; - for (b = bl->biv; b; b = b->family) - { - if ((uid_luid[INSN_UID (b->insn)] >= uid_luid[regno_first_uid[dest_regno]]) - && - (uid_luid[INSN_UID (b->insn)] - <= uid_luid[regno_last_uid[dest_regno]])) - { - v->replaceable = 0; - break; - } - } - } - else - v->replaceable = 0; - } - - if (loop_dump_stream) - { - if (type == DEST_REG) - fprintf (loop_dump_stream, "Insn %d: giv reg %d", - INSN_UID (insn), dest_regno); - else - fprintf (loop_dump_stream, "Insn %d: dest address", - INSN_UID (insn)); - - fprintf (loop_dump_stream, " src reg %d benefit %d", - src_regno, v->benefit); - fprintf (loop_dump_stream, " used %d lifetime %d", - v->times_used, v->lifetime); - - if (v->replaceable) - fprintf (loop_dump_stream, " replaceable"); - - if (GET_CODE (mult_val) == CONST_INT) - fprintf (loop_dump_stream, " mult %d", - INTVAL (mult_val)); - else - { - fprintf (loop_dump_stream, " mult "); - print_rtl (loop_dump_stream, mult_val); - } - - if (GET_CODE (add_val) == CONST_INT) - fprintf (loop_dump_stream, " add %d", - INTVAL (add_val)); - else - { - fprintf (loop_dump_stream, " add "); - print_rtl (loop_dump_stream, add_val); - } - } - - if (loop_dump_stream && v->forces) - fprintf (loop_dump_stream, " forces insn %d", INSN_UID (v->forces->insn)); - if (loop_dump_stream && v->forces2) - fprintf (loop_dump_stream, " forces insn %d", INSN_UID (v->forces2->insn)); - if (loop_dump_stream && v->consec) - fprintf (loop_dump_stream, " consec %d", v->consec); - if (loop_dump_stream) - fprintf (loop_dump_stream, "\n"); -} - -/* Delete the insns forced by the insn described by V. - If THIS_TOO is nonzero, delete that insn itself as well. */ - -static void -delete_insn_forces (v, this_too) - struct induction *v; - int this_too; -{ - rtx x, p; - int count; - rtx insn; - - if (this_too) - { - insn = v->insn; - for (count = v->consec; count >= 0; count--) - { - /* If first insn of libcall sequence, skip to end. */ - /* Do this at start of loop, since p is guaranteed to - be an insn here. */ - if (x = find_reg_note (insn, REG_LIBCALL, 0)) - insn = XEXP (x, 0); - - if (x = find_reg_note (insn, REG_RETVAL, 0)) - { - /* This is a library call; delete all insns backward until get to - first insn in this group. */ - rtx first = XEXP (x, 0); - for (p = insn; p != first; p = PREV_INSN (p)) - delete_insn (p); - /* Delete first insn also. */ - delete_insn (p); - } - else - delete_insn (insn); - - do insn = NEXT_INSN (insn); - while (GET_CODE (insn) == NOTE); - } - } - - if (v->forces) - delete_insn_forces (v->forces, 1); - if (v->forces2) - delete_insn_forces (v->forces2, 1); -} - -/* Check whether an insn is an increment legitimate for a basic induction var. - X is the source of the insn. - DEST_REG is the putative biv, also the destination of the insn. - We accept patterns of these forms: - REG = REG + INVARIANT - REG = INVARIANT + REG - REG = REG - CONSTANT - - If X is suitable, we return 1, - and store the factor multiplying REF in X into *MULT_VAL - and the additive term into *INC_VAL. - Otherwise we return 0. */ - -static int -basic_induction_var (x, dest_regno, inc_val, mult_val) - register rtx x; - int dest_regno; - rtx *inc_val; - rtx *mult_val; -{ - register enum rtx_code code; - rtx arg; - - if (x == 0) - return 0; - code = GET_CODE (x); - switch (code) - { - case PLUS: - if (GET_CODE (XEXP (x, 0)) == REG - && REGNO (XEXP (x, 0)) == dest_regno) - arg = XEXP (x, 1); - else if (GET_CODE (XEXP (x, 1)) == REG - && REGNO (XEXP (x, 1)) == dest_regno) - arg = XEXP (x, 0); - else - return 0; - - if (invariant_p (arg) == 1) - *inc_val = arg; - else - return 0; - - *mult_val = const1_rtx; - return 1; - - case MINUS: - if (GET_CODE (XEXP (x, 0)) == REG - && REGNO (XEXP (x, 0)) == dest_regno - && GET_CODE (XEXP (x, 1)) == CONST_INT) - *inc_val = gen_rtx (CONST_INT, VOIDmode, - - INTVAL (XEXP (x, 1))); - else - return 0; - *mult_val = const1_rtx; - return 1; - - /* Can accept constant setting of biv only when inside inner most loop. - Otherwise, a biv of an inner loop may be incorrectly recognized - as a biv of the outer loop, - causing code to be moved INTO the inner loop. */ - case REG: - if (!invariant_p (x)) - return 0; - case CONST_INT: - case SYMBOL_REF: - case CONST: - if (loops_enclosed == 1) - { - *inc_val = x; - *mult_val = const0_rtx; - return 1; - } - else - return 0; - - default: - return 0; - } -} - -/* A general induction variable (giv) is any quantity that is a linear function - of a basic induction variable, i.e. giv = biv * mult_val + add_val. - The coefficients can be any loop invariant quantity. - A giv need not be computed directly from the biv; - it can be computed by way of other givs. */ - -/* Determine whether X computes a giv. - If it does, return a nonzero value - which is the benefit from eliminating the computation of X; - set *SRC_REGNO to the register number of the biv that it is computed from; - set *ADD_VAL and *MULT_VAL to the coefficients, - such that the value of X is biv * mult + add; - set forces (and forces2) to identify any other givs that are used - solely to compute this one. */ - -/* This routine recognizes four types of patterns that generate givs: - - giv = biv op invariant v = 0, g = 0 - - giv1 = giv2 op invariant v = 0, g = giv2 - where giv1 and giv2 are functions of the same biv - - giv1 = biv op giv2 v = giv2, g = 0 - where giv2 is a function of biv - - giv1 = giv2 op giv3 v = giv3, g = giv2 - where giv2 and giv3 are functions of the save biv */ - -static int -general_induction_var (x, src_regno, add_val, mult_val, forces, forces2) - rtx x; - int *src_regno; - rtx *add_val; - rtx *mult_val; - struct induction **forces; - struct induction **forces2; -{ - register enum rtx_code code; - rtx arg; - struct induction *g = 0; - struct induction *v = 0; - int subexp = 0; - int tem; - - if (x == 0) - return 0; - - code = GET_CODE (x); - switch (code) - { - case NEG: - /* This can generate givs also, but it is not handled. */ - return 0; - - case MULT: - case UMULT: - /* Reject widening multiply in version 1. - That is safer than trying to handle it. */ - { - enum machine_mode m0 = GET_MODE (XEXP (x, 0)); - enum machine_mode m1 = GET_MODE (XEXP (x, 1)); - if (m0 != VOIDmode && m0 != GET_MODE (x)) - return 0; - if (m1 != VOIDmode && m1 != GET_MODE (x)) - return 0; - } - case PLUS: - case MINUS: - /* Result is linear in both operands. */ - /* Determine which operand is the biv, and put the other in ARG. */ - if (GET_CODE (XEXP (x, 0)) == REG - && induct_var[REGNO (XEXP (x, 0))] == BASIC_INDUCT) - { - *src_regno = REGNO (XEXP (x, 0)); - arg = XEXP (x, 1); - - } - else if (GET_CODE (XEXP (x, 1)) == REG - && induct_var[REGNO (XEXP (x, 1))] == BASIC_INDUCT) - { - *src_regno = REGNO (XEXP (x, 1)); - arg = XEXP (x, 0); - - } - /* Check for an rtl subexpression that is a giv. Memory address - givs often look like (plus (reg) (mult (biv) (const))). */ - /* Do this before checking for a giv operand, as this function will - fail if this special operand is not recognized. */ -#ifndef DONT_REDUCE_ADDR - else if (tem = general_induction_var (XEXP (x, 1), src_regno, - add_val, mult_val, - forces, forces2) - && code != MINUS) - { - /* Set subexp true so that this can be handled a little - differently from the normal case of g set. */ - /* Note that SRC_REGNO is already set. */ - subexp = TRUE; - g = (struct induction *) alloca (sizeof (struct induction)); - g->mult_val = *mult_val; - g->add_val = *add_val; - /* Fake out the test below. */ - g->replaceable = 1; - /* Count this multiply as a shift, since that's what it - really will do. */ - if (tem == MULT_BENEFIT) - g->benefit = SHIFT_BENEFIT; - else - g->benefit = tem; - arg = XEXP (x, 0); - } - else if (tem = general_induction_var (XEXP (x, 0), src_regno, - add_val, mult_val, - forces, forces2)) - { - /* Set subexp true so that this can be handled a little - differently from the normal case of g set. */ - /* Note that SRC_REGNO is already set. */ - subexp = TRUE; - g = (struct induction *) alloca (sizeof (struct induction)); - g->mult_val = *mult_val; - g->add_val = *add_val; - /* Fake out the test below. */ - g->replaceable = 1; - /* Count this multiply as a shift, since that's what it - really will do. */ - if (tem == MULT_BENEFIT) - g->benefit = SHIFT_BENEFIT; - else - g->benefit = tem; - arg = XEXP (x, 1); - } -#endif - /* Also allow general induction variables. - Could have a mult followed by an add (i.e. an address calculation), - thereby generating two related general induction variables - of which only the second is actually used. */ - /* Do this after checking both args for basic induction variables. */ - else if (GET_CODE (XEXP (x, 0)) == REG - && induct_var[REGNO (XEXP (x, 0))] == GENERAL_INDUCT) - { - g = induct_struct[REGNO (XEXP (x, 0))]; - *src_regno = g->src_regno; - arg = XEXP (x, 1); - } - else if (GET_CODE (XEXP (x, 1)) == REG - && induct_var[REGNO (XEXP (x, 1))] == GENERAL_INDUCT - && code != MINUS) - { - g = induct_struct[REGNO (XEXP (x, 1))]; - *src_regno = g->src_regno; - arg = XEXP (x, 0); - } - else - return 0; - - /* Overall form of expression looks good. */ - break; - - /* Could handle these also. */ - case DIV: - case UDIV: - /* For a 68020 could handle these? */ - case LSHIFT: - case ASHIFT: - case ASHIFTRT: - case LSHIFTRT: - /* These operations are linear only in first operand. - Check for a biv or giv there; if found, put other operand in ARG. */ - if (GET_CODE (XEXP (x, 0)) == REG - && induct_var[REGNO (XEXP (x, 0))] == BASIC_INDUCT) - { - *src_regno = REGNO (XEXP (x, 0)); - arg = XEXP (x, 1); - } - /* Also allow general induction variable. */ - else if (GET_CODE (XEXP (x, 0)) == REG - && induct_var[REGNO (XEXP (x, 0))] == GENERAL_INDUCT) - { - g = induct_struct[REGNO (XEXP (x, 0))]; - *src_regno = g->src_regno; - arg = XEXP (x, 1); - } - else - return 0; - - /* Overall form of expression looks good. */ - break; - - default: - return 0; - } - - /* ARG is the operand that is NOT a biv or giv. - Test it for superficial validity. */ - - /* This is just a special case of invariant values, - it is not really needed, but it's a shortcut. */ - if (GET_CODE (arg) == CONST_INT) - ; - - /* Depends on previous general induction variable, which has - the same basic induction variable */ - /* This code detects mults that have been generated as shift and add. */ - else if (GET_CODE (arg) == REG - && induct_var[REGNO (arg)] == GENERAL_INDUCT - && induct_struct[REGNO (arg)]->src_regno == *src_regno) - { - v = induct_struct[REGNO (arg)]; - /* Dependence indicated by forces, sort of kludgey. */ - } - - /* Invariant expression, could be a constant-valued register. */ - else if (invariant_p (arg) == 1) - ; - - /* Failure */ - else - return 0; - - /* Until we can do the correct thing, suppress use of nonreplaceable givs - as sources for other givs. */ - if ((g && ! g->replaceable) - || (v && ! v->replaceable)) - return 0; - - /* Now we know looks like a giv; extract the coefficients. - We can still fail if the coefficients are not what we can handle. */ - - /* Only succeed if result mult_val and add_val are only one level of rtl, - for example, (NEG:SI (REG:SI 34)) is not accepted. - This mainly causes problems with the MINUS code. */ - - switch (code) - { - case PLUS: - if (v && g) - { - if (GET_CODE (g->mult_val) == CONST_INT) - { - if (g->mult_val == const0_rtx) - *mult_val = v->mult_val; - else if (GET_CODE (v->mult_val) == CONST_INT) - *mult_val = gen_rtx (CONST_INT, VOIDmode, - INTVAL (g->mult_val) - + INTVAL (v->mult_val)); - else - return 0; - } - else if (v->mult_val == const0_rtx) - *mult_val = g->mult_val; - else - return 0; - - if (GET_CODE (g->add_val) == CONST_INT) - { - if (g->add_val == const0_rtx) - *add_val = v->add_val; - else if (GET_CODE (v->add_val) == CONST_INT) - *add_val = gen_rtx (CONST_INT, VOIDmode, - INTVAL (g->add_val) - + INTVAL (v->add_val)); - else - return 0; - } - else if (v->add_val == const0_rtx) - *add_val = g->add_val; - else - return 0; - - if (subexp) - { - /* g deleted when return, can't return pointer to it */ - if (*forces2 == 0) - *forces2 = v; - return ADD_BENEFIT + g->benefit; - } - else - { - *forces = g; - *forces2 = v; - return ADD_BENEFIT; - } - } - else if (v) - { - if (GET_CODE (v->mult_val) == CONST_INT) - *mult_val = gen_rtx (CONST_INT, VOIDmode, - INTVAL (v->mult_val) + 1); - else - return 0; - *add_val = v->add_val; - *forces = v; - return ADD_BENEFIT; - } - else if (g) - { - *mult_val = g->mult_val; - if (GET_CODE (g->add_val) == CONST_INT - && nonmemory_operand (arg, GET_MODE (arg))) - *add_val = plus_constant (arg, INTVAL (g->add_val)); - else if (GET_CODE (arg) == CONST_INT - && nonmemory_operand (g->add_val, GET_MODE (g->add_val))) - *add_val = plus_constant (g->add_val, INTVAL (arg)); - else - /* Could succeed if arg == 0, but that will never occur. */ - return 0; - - if (subexp) - /* g deleted when return, can't return pointer to it */ - return ADD_BENEFIT + g->benefit; - else - { - *forces = g; - return ADD_BENEFIT; - } - } - else - { - *mult_val = const1_rtx; - *add_val = arg; - return ADD_BENEFIT; - } - - /* Takes a lot of code and will rarely succeed. */ - case MINUS: - if (v && g) - { - /* G is the first argument of MINUS. */ - - if (GET_CODE (g->mult_val) == CONST_INT) - { - if (g->mult_val == const0_rtx) -#if 0 /* Should not have to fail here */ - *mult_val = gen_rtx (NEG, SImode, v->mult_val); -#endif - return 0; - else if (GET_CODE (v->mult_val) == CONST_INT) - *mult_val = gen_rtx (CONST_INT, VOIDmode, - INTVAL (g->mult_val) - - INTVAL (v->mult_val)); - else - return 0; - } - else if (v->mult_val == const0_rtx) - *mult_val = g->mult_val; - else - return 0; - - if (GET_CODE (g->add_val) == CONST_INT) - { - if (g->add_val == const0_rtx) -#if 0 /* should not have to fail here */ - *add_val = v->add_val; -#endif - return 0; - else if (GET_CODE (v->add_val) == CONST_INT) - *add_val = gen_rtx (CONST_INT, VOIDmode, - INTVAL (g->add_val) - - INTVAL (v->add_val)); - else - return 0; - } - else if (v->add_val == const0_rtx) - *add_val = g->add_val; - else - return 0; - - if (subexp) - { - /* G deleted when return, can't return pointer to it */ - if (*forces2 == 0) - *forces2 = v; - return ADD_BENEFIT + g->benefit; - } - else - { - *forces = g; - *forces2 = v; - return ADD_BENEFIT; - } - } - else if (v) - { - if (GET_CODE (v->mult_val) != CONST_INT) - return 0; - if (arg == XEXP (x, 0)) /* giv1 = giv2 - biv */ - { - *mult_val = gen_rtx (CONST_INT, VOIDmode, - INTVAL (v->mult_val) - 1); - *add_val = v->add_val; - } - else /* giv1 = biv - giv2 */ - { - *mult_val = gen_rtx (CONST_INT, VOIDmode, - 1 - INTVAL (v->mult_val)); - if (GET_CODE (v->add_val) == CONST_INT) - *add_val = gen_rtx (CONST_INT, VOIDmode, - - INTVAL (v->add_val)); - else - return 0; - } - *forces = v; - return ADD_BENEFIT; - } - else if (g) - { - if (arg == XEXP (x, 1)) - *mult_val = g->mult_val; - else - { - if (GET_CODE (g->mult_val) == CONST_INT) - *mult_val = gen_rtx (CONST_INT, VOIDmode, - - INTVAL (g->mult_val)); - else - return 0; - } - if (GET_CODE (g->add_val) == CONST_INT) - { - if (g->add_val == const0_rtx) - { - if (arg == XEXP (x, 1)) /* giv1 = giv2 - arg */ - { - /* Fail unless arg is a constant. */ - if (GET_CODE (arg) == CONST_INT) - *add_val = gen_rtx (CONST_INT, VOIDmode, - -INTVAL (arg)); - else - return 0; - } - else /* giv1 = arg - giv2 */ - *add_val = arg; - } - else if (GET_CODE (arg) == CONST_INT) - { - if (arg == XEXP (x, 1)) /* giv1 = giv2 - arg */ - *add_val = gen_rtx (CONST_INT, VOIDmode, - INTVAL (g->add_val) - - INTVAL (arg)); - else /* giv1 = arg - giv2 */ - *add_val = gen_rtx (CONST_INT, VOIDmode, - INTVAL (arg), - - INTVAL (g->add_val)); - } - else - return 0; - } - else - /* Could succeed if arg == 0, but that will never occur. */ - return 0; - - if (subexp) - /* G deleted when return, can't return pointer to it. */ - return ADD_BENEFIT + g->benefit; - else - { - *forces = g; - return ADD_BENEFIT; - } - } - else if (GET_CODE (arg) == CONST_INT) - { - if (arg == XEXP (x, 1)) - { - *add_val = gen_rtx (CONST_INT, VOIDmode, - INTVAL (arg)); - *mult_val = const1_rtx; - } - else - { - *add_val = arg; - *mult_val = gen_rtx (CONST_INT, VOIDmode, -1); - } - return ADD_BENEFIT; - } - else - return 0; - - /* UMULT can be handled like MULT since C ignores overflows. */ - case MULT: - case UMULT: - if (v && g) - { - /* Quadratic term, just fail. */ - return 0; - } - else if (v) - { - /* Quadratic term, just fail. */ - return 0; - } - else if (g) - { - /* Takes a lot of code and will rarely succeed. */ - /* dest = m * arg * b + a * arg */ - if (GET_CODE (g->mult_val) == CONST_INT) - { - if (g->mult_val == const0_rtx) - *mult_val = const0_rtx; - else if (g->mult_val == const1_rtx) - *mult_val = arg; - else if (GET_CODE (arg) == CONST_INT) - *mult_val = gen_rtx (CONST_INT, VOIDmode, - INTVAL (g->mult_val) * INTVAL (arg)); - else - return 0; - } - else - /* Could suceed if arg == 1 or 0, but this will never occur. */ - return 0; - - if (GET_CODE (g->add_val) == CONST_INT) - { - if (g->add_val == const0_rtx) - *add_val = const0_rtx; - else if (g->add_val == const1_rtx) - *add_val = arg; - else if (GET_CODE (arg) == CONST_INT) - *add_val = gen_rtx (CONST_INT, VOIDmode, - INTVAL (g->add_val) * INTVAL (arg)); - else - return 0; - } - else - /* Could suceed if arg == 1 or 0, but this will never occur. */ - return 0; - - if (subexp) - /* G deleted when return, can't return pointer to it. */ - return MULT_BENEFIT + g->benefit; - else - { - *forces = g; - return MULT_BENEFIT; - } - } - else - { - *mult_val = arg; - *add_val = const0_rtx; - return MULT_BENEFIT; - } - - /* These are not worth the trouble. */ - case DIV: - case UDIV: - return 0; - - /* Handle these, but only for left shift. */ - case LSHIFT: - case ASHIFT: - if (v && g) - { - /* Quadratic term, just fail. */ - return 0; - } - else if (v) - { - /* Quadratic term, just fail. */ - return 0; - } - else if (g) - { - /* Takes a lot of code and will rarely succeed. */ - /* dest = ((m * b) << arg) + (a << arg) */ - if (GET_CODE (g->mult_val) == CONST_INT) - { - if (g->mult_val == const0_rtx) - *mult_val = const0_rtx; - else if (GET_CODE (arg) == CONST_INT && INTVAL (arg) >= 0) - *mult_val = gen_rtx (CONST_INT, VOIDmode, - INTVAL (g->mult_val) - * (1 << INTVAL (arg))); - else - return 0; - } - else - /* Could suceed if arg == 0, but this will never occur. */ - return 0; - - if (GET_CODE (g->add_val) == CONST_INT) - { - if (g->add_val == const0_rtx) - *add_val = const0_rtx; - else if (GET_CODE (arg) == CONST_INT) - *add_val = gen_rtx (CONST_INT, VOIDmode, - INTVAL (g->add_val) - * (1 << INTVAL (arg))); - else - return 0; - } - else - /* Could suceed if arg == 0, but this will never occur. */ - return 0; - - if (subexp) - /* G deleted when return, can't return pointer to it. */ - return SHIFT_BENEFIT + g->benefit; - else - { - *forces = g; - return SHIFT_BENEFIT; - } - } - - if (GET_CODE (arg) == CONST_INT && INTVAL (arg) >= 0) - *mult_val = gen_rtx (CONST_INT, VOIDmode, 1 << INTVAL (arg)); - else - return 0; - *add_val = const0_rtx; - return SHIFT_BENEFIT; - - /* These are not worth the trouble. */ - case ASHIFTRT: - case LSHIFTRT: - return 0; - - /* should never reach here */ - default: - abort (); - return 0; - } -} - -/* Help detect a giv that is calculated by several consecutive insns; - for example, - giv = biv * M - giv = giv + A - The caller has already identified the first insn P as having a giv as dest; - we check that all other insns that set the same register follow - immediately after P, that they alter nothing else, - and that the result of the last is still a giv. - - The value is 0 if the reg set in P is not really a giv. - Otherwise, the value is the amount gained by eliminating - all the consecutive insns that compute the value. - - FIRST_BENEFIT is the amount gained by eliminating the first insn, P. - SRC_REGNO is the regno of the biv; DEST_REGNO is that of the giv. - - The coefficients of the ultimate giv value are stored in - *MULT_VAL and *ADD_VAL. */ - -static int -consec_sets_giv (first_benefit, p, src_regno, dest_regno, - add_val, mult_val) - int first_benefit; - rtx p; - int src_regno; - int dest_regno; - rtx *add_val; - rtx *mult_val; -{ - int count; - int benefit = first_benefit; - enum rtx_code code; - struct induction *forces, *forces2; - rtx temp; - int tem; - - /* Initialize info used by general_induction_var. */ - struct induction *v = - (struct induction *) oballoc (sizeof (struct induction)); - v->src_regno = src_regno; - v->mult_val = *mult_val; - v->add_val = *add_val; - - induct_var[dest_regno] = GENERAL_INDUCT; - induct_struct[dest_regno] = v; - - count = n_times_set[dest_regno] - 1; - - while (count > 0) - { - p = NEXT_INSN (p); - code = GET_CODE (p); - - /* If libcall, skip to end of call sequence. */ - if (code == INSN && (temp = find_reg_note (p, REG_LIBCALL, 0))) - p = XEXP (temp, 0); - - if (code == INSN && GET_CODE (PATTERN (p)) == SET - && GET_CODE (SET_DEST (PATTERN (p))) == REG - && REGNO (SET_DEST (PATTERN (p))) == dest_regno - && ((tem = general_induction_var (SET_SRC (PATTERN (p)), &src_regno, - add_val, mult_val, - &forces, &forces2)) - /* Giv created by call to library routine. */ - || ((temp = loop_find_reg_equal (p)) - && (tem = general_induction_var (XEXP (temp, 0), &src_regno, - add_val, mult_val, - &forces, &forces2)))) - && src_regno == v->src_regno) - { - count--; - benefit += tem; - v->mult_val = *mult_val; - v->add_val = *add_val; - } - else if (code != NOTE) - { - induct_var[dest_regno] = UNKNOWN_INDUCT; - return 0; - } - } - - return benefit; -} - -/* Generate a SEQUENCE to multiply OP0 and OP1 with result in TARGET. - Use expand_mult to "optimally" do the multiply. - This also works for machines that do not have multiply insns. - If one of the operands is a constant, it must be the second. */ - -static rtx -gen_iv_mult (mode, op0, op1, target) - enum machine_mode mode; - register rtx op0, op1, target; -{ - extern rtx gen_sequence (); - extern rtx start_sequence (); - rtx saved, result, temp; - - saved = start_sequence (); - - /* ??? It is very unmodular to use expand_mult here! - This should be redesigned. */ - - /* UNSIGNEDP arg can be zero since operands/target always same width. */ - temp = expand_mult (mode, op0, op1, target, 0); - - /* Move to target register, if expand_mult did not put it there. */ - if (target != 0 && temp != target) - emit_move_insn (target, temp); - - result = gen_sequence (); - end_sequence (saved); - - return result; -} - -/* Emit code to initialize an induction variable created by strength - reduction. - More precisely, emit code before INSERT_BEFORE - to set REG = B * M + A. */ - -static void -emit_iv_init_code (b, m, a, reg, insert_before) - rtx b; /* initial value of basic induction variable */ - rtx m; /* multiplicative constant */ - rtx a; /* additive constant */ - rtx reg; /* destination register */ - rtx insert_before; -{ - rtx seq; - rtx result; - - /* Prevent unexpected sharing of these rtx. */ - a = copy_rtx (a); - b = copy_rtx (b); - - start_sequence (); - result = expand_mult_add (b, m, a, GET_MODE (reg), 0); - if (reg != result) - emit_move_insn (reg, result); - seq = gen_sequence (); - end_sequence (); - - emit_insn_before (seq, insert_before); -} - -/* Emit code to increment the induction variable inside the loop. - Try to emit optimal code for the expression - REG = REG + BIV_ADD * GIV_MULT. */ - -static void -emit_iv_inc (biv_add, giv_mult, reg, insn) - rtx biv_add; /* increment value for biv */ - rtx giv_mult; /* multiply value of giv */ - rtx reg; /* create insn to set this reg */ - rtx insn; /* where to insert the new insn */ -{ - emit_iv_init_code (biv_add, giv_mult, reg, reg, insn); -} - -/* Test whethen BIV_ADD * GIV_MULT can be computed without - an actual multiply insn. Value is 1 if so. */ - -static int -product_cheap_p (biv_add, giv_mult) - rtx biv_add; - rtx giv_mult; -{ - /* Indicates which of MULT/ADD are constants. */ - int status = 0; - int const_val; - rtx tmp; - - if (GET_CODE (biv_add) == CONST_INT) - status |= 0x1; - if (GET_CODE (giv_mult) == CONST_INT) - status |= 0x2; - - switch (status) - { - case 0: - /* Neither is constant: would need a multiply insn, so fail. */ - return 0; - - case 1: - /* BIV_ADD value is constant */ - /* Equivalent to state 2, just switch values of BIV_ADD and GIV_MULT - and fall through. */ - tmp = biv_add; - biv_add = giv_mult; - giv_mult = tmp; - - case 2: - /* GIV_MULT value is constant. - If it is 1, 0 or -1 then we win. */ - const_val = INTVAL (giv_mult); - if (const_val < -1 || const_val > 1) - { - tmp = gen_iv_mult (GET_MODE (biv_add), biv_add, giv_mult, 0); - /* Don't emit a multiply insn, just fail instead. */ - if ((GET_CODE (tmp) == SET && GET_CODE (SET_SRC (tmp)) == MULT) - /* Also fail if library call (which generates more - then two insn) is needed. */ - || (GET_CODE (tmp) == SEQUENCE && XVECLEN (tmp, 0) > 2)) - return 0; - } - return 1; - - case 3: - /* Both BIV_ADD and GIV_MULT are constant; - can compute the product at compile time. */ - return 1; - - default: - abort (); - } -} - - -/* Check to see if loop can be terminated by a "decrement and branch until - zero" instruction. If so, add a REG_NONNEG note to the branch insn if so. - Also try reversing an increment loop to a decrement loop - to see if the optimization can be performed. - Value is nonzero if optimization was performed. */ - -static int -check_dbra_loop (loop_end, iv_list, insn_count, loop_start) - rtx loop_end; - struct iv_class *iv_list; - int insn_count; - rtx loop_start; -{ - struct iv_class *bl; - rtx reg; - rtx jump_label; - rtx final_value; - rtx start_value; - enum rtx_code branch_code; - rtx new_add_val; - rtx tested_before_loop = 0; - rtx p; - - /* See if the loop is contained in `if (X >= 0)' for some reg X. - If so, then we know X is initially nonnegative even though - we don't know its initial value. - Record X in TESTED_BEFORE_LOOP. */ - - for (p = loop_start; p != 0; p = PREV_INSN (p)) - if (GET_CODE (p) != NOTE) - break; - - /* See if a conditional branch preceeds the loop. - There may be no other insns or labels between it and - the beginning of the loop. */ - if (p != 0 && GET_CODE (p) == JUMP_INSN && condjump_p (p) - && SET_SRC (PATTERN (p)) != pc_rtx - && ((GET_CODE (XEXP (SET_SRC (PATTERN (p)), 0)) == LT - && XEXP (SET_SRC (PATTERN (p)), 2) == pc_rtx) - || - (GET_CODE (XEXP (SET_SRC (PATTERN (p)), 0)) == GE - && XEXP (SET_SRC (PATTERN (p)), 1) == pc_rtx)) - && next_real_insn (JUMP_LABEL (p)) == next_real_insn (loop_end)) - { - /* Before the branch should be a test or compare. - See if we are comparing something against zero. */ - p = PREV_INSN (p); - if (GET_CODE (p) == INSN && GET_CODE (PATTERN (p)) == SET - && SET_DEST (PATTERN (p)) == cc0_rtx) - { - if (GET_CODE (SET_SRC (PATTERN (p))) == REG) - tested_before_loop = SET_SRC (PATTERN (p)); - else if (GET_CODE (SET_SRC (PATTERN (p))) == COMPARE - && GET_CODE (XEXP (SET_SRC (PATTERN (p)), 0)) == REG - && XEXP (SET_SRC (PATTERN (p)), 1) == const0_rtx) - tested_before_loop = XEXP (SET_SRC (PATTERN (p)), 0); - else if (GET_CODE (SET_SRC (PATTERN (p))) == COMPARE - && GET_CODE (XEXP (SET_SRC (PATTERN (p)), 1)) == REG - && XEXP (SET_SRC (PATTERN (p)), 0) == const0_rtx) - tested_before_loop = XEXP (SET_SRC (PATTERN (p)), 1); - } - } - - /* If last insn is a conditional branch, and the insn before tests a register - value, then try to optimize it. */ - - if (GET_CODE (PREV_INSN (loop_end)) == JUMP_INSN - && GET_CODE (PATTERN (PREV_INSN (loop_end))) == SET - && GET_CODE (SET_SRC (PATTERN (PREV_INSN (loop_end)))) == IF_THEN_ELSE - && GET_CODE (PREV_INSN (PREV_INSN (loop_end))) == INSN - && GET_CODE (PATTERN (PREV_INSN (PREV_INSN (loop_end)))) == SET - && (GET_CODE (SET_DEST (PATTERN (PREV_INSN (PREV_INSN (loop_end))))) == - CC0)) - { - /* Check all of the bivs to see if the compare uses one of them. */ - - for (bl = iv_list; bl; bl = bl->next) - { - if (reg_mentioned_p (SET_DEST (PATTERN (bl->biv->insn)), - PREV_INSN (PREV_INSN (loop_end)))) - break; - } - - /* If biv set more than once, then give up. - We can't guarantee that it will be zero on the last iteration. - Also give up if the biv is used between its update and the test - insn. */ - if (bl && bl->biv_count == 1 - && ! reg_used_between_p (regno_reg_rtx[bl->regno], bl->biv->insn, - PREV_INSN (PREV_INSN (loop_end)))) - { - /* Look for the case where the basic induction variable is always - nonnegative, and equals zero on the last iteration. - In this case, add a reg_note REG_NONNEG, which allows the - m68k DBRA instruction to be used. */ - - /* the decrement case */ - - if (GET_CODE (bl->biv->add_val) == CONST_INT - && INTVAL (bl->biv->add_val) < 0) - { - /* Initial value must be greater than 0, - init_val % -dec_value == 0 to ensure that it equals zero on - the last iteration */ - - if (GET_CODE (bl->initial_value) == CONST_INT - && INTVAL (bl->initial_value) > 0 - && (INTVAL (bl->initial_value) % - (-INTVAL (bl->biv->add_val))) == 0) - { - /* register always nonnegative, add REG_NOTE to branch */ - REG_NOTES (PREV_INSN (loop_end)) - = gen_rtx (EXPR_LIST, REG_NONNEG, 0, - REG_NOTES (PREV_INSN (loop_end))); - bl->nonneg = 1; - - return 1; - } - - /* If the decrement is 1 and the value was tested as >= 0 before - the loop, then we can safely optimize. */ - if (SET_DEST (PATTERN (bl->biv->insn)) == tested_before_loop - && INTVAL (bl->biv->add_val) == -1) - { - REG_NOTES (PREV_INSN (loop_end)) - = gen_rtx (EXPR_LIST, REG_NONNEG, 0, - REG_NOTES (PREV_INSN (loop_end))); - bl->nonneg = 1; - - return 1; - } - } - else if (num_mem_sets <= 1) - { - /* Try to change inc to dec, so can apply above optimization. */ - /* Can do this if: - all registers modified are induction variables or invariant, - all memory references have non-overlapping addresses - (obviously true if only one write) - allow 2 insns for the compare/jump at the end of the loop. */ - int num_nonfixed_reads = 0; - rtx p; - - for (p = loop_start; p != loop_end; p = NEXT_INSN (p)) - if (GET_CODE (p) == INSN || GET_CODE (p) == CALL_INSN - || GET_CODE (p) == JUMP_INSN) - num_nonfixed_reads += count_nonfixed_reads (PATTERN (p)); - - /* This code only acts for innermost loops. Also it simplifies - the memory address check by only reversing loops with - zero or one memory access. - Two memory accesses could involve parts of the same array, - and that can't be reversed. */ - - if (num_nonfixed_reads <= 1 - && !loop_has_call - && (bl->giv_count + bl->biv_count + num_mem_sets - + num_movables + 2 == insn_count)) - { - rtx src_two_before_end; - int constant; - int win; - - /* Loop can be reversed. */ - if (loop_dump_stream) - fprintf (loop_dump_stream, "Can reverse loop\n"); - - /* Now check other conditions: - initial_value must be zero, - final_value % add_val == 0, so that when reversed, the - biv will be zero on the last iteration. */ - - /* Calculating the final value non trivial. - If branch is (LT (CC0) (CONST 0), - then value in compare is final value. - If branch is (LE (CC0) (CONST 0), - then value in compare is final_value - add_val */ - - branch_code - = GET_CODE (XEXP (SET_SRC (PATTERN (PREV_INSN (loop_end))), 0)); - src_two_before_end - = SET_SRC (PATTERN (PREV_INSN (PREV_INSN (loop_end)))); - - win = 1; - if (GET_CODE (src_two_before_end) == REG) - constant = 0; - else if (GET_CODE (src_two_before_end) == COMPARE - && GET_CODE (XEXP (src_two_before_end, 1)) == CONST_INT) - constant = INTVAL (XEXP (src_two_before_end, 1)); - else - win = 0; - - if (win && bl->initial_value == const0_rtx - && (branch_code == LT || branch_code == LE) - && XEXP (XEXP (SET_SRC (PATTERN (PREV_INSN (loop_end))), 0), 1) == const0_rtx - && (constant % INTVAL (bl->biv->add_val)) == 0) - { - /* Register will always be nonnegative, with value - 0 on last iteration if loop reversed */ - - /* Save some info needed to produce the new insns. */ - reg = SET_DEST (PATTERN (bl->biv->insn)); - jump_label = XEXP (SET_SRC (PATTERN (PREV_INSN (loop_end))), 1); - new_add_val = gen_rtx (CONST_INT, VOIDmode, - - INTVAL (bl->biv->add_val)); - - - if (branch_code == LT) - { - final_value - = gen_rtx (CONST_INT, VOIDmode, constant); - start_value - = gen_rtx (CONST_INT, VOIDmode, - (constant - INTVAL (bl->biv->add_val))); - } - else /* branch_code == LE */ - { - start_value - = gen_rtx (CONST_INT, VOIDmode, constant); - final_value - = gen_rtx (CONST_INT, VOIDmode, - (constant + INTVAL (bl->biv->add_val))); - } - - /* Initialize biv to start_value before loop start. - The old initializing insn will be deleted as a - dead store by flow.c. */ - emit_insn_before (gen_rtx (SET, VOIDmode, reg, - start_value), - loop_start); - - /* Add insn to decrement register, and delete insn - that incremented the register. */ - emit_insn_before (gen_rtx (SET, VOIDmode, reg, - gen_rtx (PLUS, GET_MODE (reg), reg, - new_add_val)), - bl->biv->insn); - /* Update biv info to reflect its new status. */ - bl->biv->insn = PREV_INSN (bl->biv->insn); - delete_insn (NEXT_INSN (bl->biv->insn)); - - /* Inc LABEL_NUSES so that delete_insn will - not delete the label. */ - LABEL_NUSES (XEXP (jump_label, 0)) ++; - - if (regno_last_uid[bl->regno] != INSN_UID (PREV_INSN (loop_end))) - emit_insn_after (gen_rtx (SET, VOIDmode, reg, - final_value), - loop_end); - - /* Delete compare/branch at end of loop. */ - delete_insn (PREV_INSN (loop_end)); - delete_insn (PREV_INSN (loop_end)); - - /* Add new compare/branch insn at end of loop. */ - emit_insn_before (gen_rtx (SET, VOIDmode, cc0_rtx, reg), - loop_end); - emit_jump_insn_before (gen_rtx (SET, VOIDmode, pc_rtx, - gen_rtx (IF_THEN_ELSE, VOIDmode, - gen_rtx (GE, VOIDmode, cc0_rtx, - const0_rtx), - jump_label, - pc_rtx)), - loop_end); - - JUMP_LABEL (PREV_INSN (loop_end)) = XEXP (jump_label, 0); - /* Increment of LABEL_NUSES done above. */ - - /* Register is now always nonnegative, - so add REG_NONNEG note to the branch. */ - REG_NOTES (PREV_INSN (loop_end)) - = gen_rtx (EXPR_LIST, REG_NONNEG, 0, - REG_NOTES (PREV_INSN (loop_end))); - bl->nonneg = 1; - - /* Update rest of biv info. */ - bl->initial_value = start_value; - bl->biv->add_val = new_add_val; - - if (loop_dump_stream) - fprintf (loop_dump_stream, "Reversed loop and added reg_nonneg\n"); - - return 1; - } - } - } - } - } - return 0; -} - -/* Verify whether the biv BL appears to be eliminable, - based on the insns in the loop that refer to it. - LOOP_START is the first insn of the loop, and END is the end insn. */ - -static void -check_eliminate_biv (bl, loop_start, end) - struct iv_class *bl; - rtx loop_start; - rtx end; -{ - /* Get the REG rtx for the biv. */ - rtx reg = SET_DEST (PATTERN (bl->biv->insn)); - rtx p; - struct induction *v; - - bl->eliminable = 0; - - for (p = loop_start; p != end; p = NEXT_INSN (p)) - { - enum rtx_code code = GET_CODE (p); - if ((code == INSN || code == JUMP_INSN || code == CALL_INSN) - && reg_mentioned_p (reg, PATTERN (p))) - { - /* This insn uses the biv. If we can't understand it, - then we can't eliminate the biv. */ - if (GET_CODE (PATTERN (p)) != SET) - { - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Cannot eliminate biv %d: cannot understand insn %d.\n", - bl->regno, INSN_UID (p)); - break; - } - - /* The insns that increment the biv are no problem. */ - if (SET_DEST (PATTERN (p)) == reg) - continue; - - /* If this is an insn which uses the biv ONLY in the - calculation of a giv which is in the family of this - biv, it's ok becuase it will go away when the giv is - reduced. March 14, 1989 -- self@bayes.arc.nasa.gov */ - for (v = bl->giv; v; v = v->family) - if (v->insn == p) - { - if (v->giv_type == DEST_REG - || (v->giv_type == DEST_ADDR - && ! other_reg_use_p (reg, *(v->location), - PATTERN (p)))) - break; - } - if (v) - continue; - - /* If can rewrite this insn not to use the biv, it's ok. */ - if (can_eliminate_biv_p (p, bl)) - continue; - - /* Biv is used in a way we cannot eliminate. */ - if (loop_dump_stream) - fprintf (loop_dump_stream, - "Cannot eliminate biv %d: biv used in insn %d.\n", - bl->regno, INSN_UID (p)); - break; - } - } - - if (p == end) - { - bl->eliminable = 1; - if (loop_dump_stream) - fprintf (loop_dump_stream, "Can eliminate biv %d.\n", - bl->regno); - } -} - -/* Return 1 if INSN, a compare insn which tests the biv described by BL, - can be rewritten to use instead some reduced giv related to that biv. - Does not change the rtl. - - We make the assumption that all the givs depending on this biv - will be reduced, since only in that case will an attempt to eliminate - the biv actually be made. - - The following function is very parallel to this one. */ - -static int -can_eliminate_biv_p (insn, bl) - rtx insn; - struct iv_class *bl; -{ - rtx src; - enum rtx_code code; - struct induction *v, *tv; - rtx arg; - int arg_operand; - /* Mode of this biv. */ - enum machine_mode mode = bl->biv->mode; - - if (SET_DEST (PATTERN (insn)) != cc0_rtx) - return 0; - - src = SET_SRC (PATTERN (insn)); - code = GET_CODE (src); - - switch (code) - { - /* a test insn */ - case REG: - /* Can replace with any giv that has (MULT_VAL != 0) and (ADD_VAL == 0) - Require a constant integer for MULT_VAL, so we know it's nonzero. */ - - for (v = bl->giv; v; v = v->family) - if (GET_CODE (v->mult_val) == CONST_INT && v->mult_val != const0_rtx - && v->add_val == const0_rtx - && ! v->ignore - && v->mode == mode) - return 1; - - /* Look for a giv with (MULT_VAL != 0) and (ADD_VAL != 0) - where ADD_VAL is a constant or a register; - can replace test insn with a compare insn (cmp REDUCED_GIV ADD_VAL). - Require a constant integer for MULT_VAL, so we know it's nonzero. */ - - for (v = bl->giv; v; v = v->family) - if (GET_CODE (v->mult_val) == CONST_INT && v->mult_val != const0_rtx - && (GET_CODE (v->add_val) == REG || GET_CODE (v->add_val) == CONST_INT) - && ! v->ignore - && v->mode == mode) - return 1; - - if (loop_dump_stream) - fprintf (loop_dump_stream, "Cannot eliminate biv %d in test insn %d: no appropriate giv.\n", - bl->regno, INSN_UID (insn)); - - return 0; - - /* a compare insn */ - case COMPARE: - /* Figure out which operand is the biv. */ - if ((GET_CODE (XEXP (src, 0)) == REG) - && (REGNO (XEXP (src, 0)) == bl->regno)) - { - arg = XEXP (src, 1); - arg_operand = 1; - } - else if ((GET_CODE (XEXP (src, 1)) == REG) - && (REGNO (XEXP (src, 1)) == bl->regno)) - { - arg = XEXP (src, 0); - arg_operand = 0; - } - else - return 0; - - if (GET_CODE (arg) == CONST_INT) - { - /* Can replace with any giv that has constant coefficients. */ - - for (v = bl->giv; v; v = v->family) - if (GET_CODE (v->mult_val) == CONST_INT - && GET_CODE (v->add_val) == CONST_INT - && ! v->ignore - && v->mode == mode) - return 1; - - /* Look for giv with constant mult_val and nonconst add_val, - since we can insert add insn before loop - to calculate new compare value. */ - - for (v = bl->giv; v; v = v->family) - if (GET_CODE (v->mult_val) == CONST_INT - && ! v->ignore - && v->mode == mode) - return 1; - } - else if (GET_CODE (arg) == REG || GET_CODE (arg) == MEM) - { - /* Comparing against invariant register or memref can be handled. */ - - if (invariant_p (arg)) - { - /* Look for giv with constant mult_val and nonconst add_val. - Insert add-insn before loop to compute new compare value. */ - - for (v = bl->giv; v; v = v->family) - if ((GET_CODE (v->mult_val) == CONST_INT) - && ! v->ignore - && v->mode == mode) - return 1; - } - - /* Otherwise, only comparing against a biv can be handled. */ - if (GET_CODE (arg) != REG - || induct_var[REGNO (arg)] != BASIC_INDUCT) - return 0; - - /* Look for a giv for each biv that have identical - values for mult_val and add_val. */ - for (v = bl->giv; v; v = v->family) - if (v->mode == mode - && ! v->ignore) - { - for (tv = class_struct[REGNO (arg)]->giv; tv; tv = tv->family) - if ((tv->new_reg != 0) - && rtx_equal_p (tv->mult_val, v->mult_val) - && rtx_equal_p (tv->mult_val, v->mult_val) - && ! tv->ignore - && tv->mode == mode) - return 1; - } - } - return 0; - - default: - return 0; - } -} - -/* Rewrite a compare insn INSN which uses the biv described by BL - so that it doesn't use that biv any more. - Instead it will use some reduced giv related to that biv. - - The preceding function is very parallel to this one. */ - -static void -eliminate_biv (insn, bl, loop_start) - rtx insn; - struct iv_class *bl; - rtx loop_start; -{ - rtx src = SET_SRC (PATTERN (insn)); - enum rtx_code code = GET_CODE (src); - struct induction *v, *tv; - rtx arg, temp; - int arg_operand; - /* Mode of this biv. */ - enum machine_mode mode = bl->biv->mode; - - switch (code) - { - /* a test insn */ - case REG: - /* Can replace with any giv that was reduced and - that has (MULT_VAL != 0) and (ADD_VAL == 0). - Require a constant integer for MULT_VAL, so we know it's nonzero. */ - - for (v = bl->giv; v; v = v->family) - if (GET_CODE (v->mult_val) == CONST_INT && v->mult_val != const0_rtx - && v->add_val == const0_rtx - && v->new_reg != 0 - && v->mode == mode) - break; - if (v) - { - /* We can test the sign of that giv's reduced reg. */ - SET_SRC (PATTERN (insn)) = v->new_reg; - /* If the giv has the opposite direction of change, - then reverse the comparison. */ - if (INTVAL (v->mult_val) < 0) - SET_SRC (PATTERN (insn)) - = gen_rtx (COMPARE, GET_MODE (v->new_reg), - const0_rtx, v->new_reg); - return; - } - - /* Look for a giv with (MULT_VAL != 0) and (ADD_VAL != 0) - where ADD_VAL is a constant or a register; - replace test insn with a compare insn (cmp REDUCED_GIV ADD_VAL). - Require a constant integer for MULT_VAL, so we know it's nonzero. */ - - for (v = bl->giv; v; v = v->family) - if (GET_CODE (v->mult_val) == CONST_INT && v->mult_val != const0_rtx - && (GET_CODE (v->add_val) == REG || GET_CODE (v->add_val) == CONST_INT) - && v->new_reg != 0 - && v->mode == mode) - break; - if (v) - { - /* Replace biv with the giv's reduced register. */ - SET_SRC (PATTERN (insn)) = gen_rtx (COMPARE, GET_MODE (v->new_reg), - v->new_reg, - copy_rtx (v->add_val)); - - /* If the giv has the opposite direction of change, - then reverse the comparison. */ - if (INTVAL (v->mult_val) < 0) - { - XEXP (SET_SRC (PATTERN (insn)), 0) - = XEXP (SET_SRC (PATTERN (insn)), 1); - XEXP (SET_SRC (PATTERN (insn)), 1) = v->new_reg; - } -#if 0 - /* add_val must be invariant, so don't bother storing in a register */ - /* calculate the appropriate constant to compare against */ - emit_insn_before (gen_rtx (SET, VOIDmode, compare_value, - copy_rtx (v->add_val)), - loop_start); -#endif - return; - } - abort (); - break; - - /* a compare insn */ - case COMPARE: - /* Figure out which operand is the biv. */ - if (GET_CODE (XEXP (src, 0)) == REG - && REGNO (XEXP (src, 0)) == bl->regno) - { - arg = XEXP (src, 1); - arg_operand = 1; - } - else if (GET_CODE (XEXP (src, 1)) == REG - && REGNO (XEXP (src, 1)) == bl->regno) - { - arg = XEXP (src, 0); - arg_operand = 0; - } - else - abort (); - - if (GET_CODE (arg) == CONST_INT) - { - /* Can replace with any giv that has constant mult_val and add_val. - Make sure it was strength reduced by checking new_reg != 0. */ - - for (v = bl->giv; v; v = v->family) - if (GET_CODE (v->mult_val) == CONST_INT - && GET_CODE (v->add_val) == CONST_INT - && v->new_reg - && v->mode == mode) - break; - if (v) - { - rtx newval; - /* Replace biv with the giv's reduced reg. */ - XEXP (src, 1-arg_operand) = v->new_reg; - /* Calculate the appropriate constant to compare against. */ - newval = gen_rtx (CONST_INT, VOIDmode, - (INTVAL (arg) * INTVAL (v->mult_val) - + INTVAL (v->add_val))); - XEXP (src, arg_operand) = newval; - /* If that constant is no good in a compare, - put it in a register. */ - if (recog (PATTERN (insn), insn) < 0) - { - temp = gen_reg_rtx (mode); - emit_iv_init_code (arg, v->mult_val, v->add_val, - temp, loop_start); - XEXP (src, arg_operand) = temp; - } - - /* If the giv has the opposite direction of change, - then reverse the comparison. */ - if (INTVAL (v->mult_val) < 0) - { - temp = XEXP (src, 0); - XEXP (src, 0) = XEXP (src, 1); - XEXP (src, 1) = temp; - } - return; - } - - /* Look for giv with constant mult_val and nonconst add_val. - Insert add insn before loop to calculate new compare value. */ - - for (v = bl->giv; v; v = v->family) - if (GET_CODE (v->mult_val) == CONST_INT - && v->new_reg - && v->mode == mode) - break; - if (v) - { - rtx compare_value = gen_reg_rtx (mode); - - /* Replace biv with giv's reduced register. */ - XEXP (src, 1-arg_operand) = v->new_reg; - - /* At start of loop, compute value to compare against. */ - emit_iv_init_code (arg, v->mult_val, v->add_val, - compare_value, loop_start); - /* Use it in this insn. */ - XEXP (src, arg_operand) = compare_value; - - /* If the giv has the opposite direction of change, - then reverse the comparison. */ - if (INTVAL (v->mult_val) < 0) - { - temp = XEXP (src, 0); - XEXP (src, 0) = XEXP (src, 1); - XEXP (src, 1) = temp; - } - return; - } - abort (); - } - else if (GET_CODE (arg) == REG || GET_CODE (arg) == MEM) - { - if (invariant_p (arg)) - { - /* Look for giv with constant mult_val and nonconst add_val. - Insert add-insn before loop to compute new compare value. */ - - for (v = bl->giv; v; v = v->family) - if (GET_CODE (v->mult_val) == CONST_INT - && v->new_reg - && v->mode == mode) - break; - if (v) - { - rtx compare_value = gen_reg_rtx (mode); - - /* Replace biv with giv's reduced register. */ - XEXP (src, 1-arg_operand) = v->new_reg; - - /* At start of loop, compute value to compare against. */ - emit_iv_init_code (arg, v->mult_val, v->add_val, - compare_value, loop_start); - XEXP (src, arg_operand) = compare_value; - - /* If the giv has the opposite direction of change, - then reverse the comparison. */ - if (INTVAL (v->mult_val) < 0) - { - temp = XEXP (src, 0); - XEXP (src, 0) = XEXP (src, 1); - XEXP (src, 1) = temp; - } - return; - } - } - - /* Otherwise the reg compared with had better be a biv. */ - if (GET_CODE (arg) != REG - || induct_var[REGNO (arg)] != BASIC_INDUCT) - abort (); - - /* Look for a pair of givs, one for each biv, - with identical coefficients. */ - for (v = bl->giv; v; v = v->family) - { - if (!v->new_reg && v->mode == mode) - continue; - for (tv = class_struct[REGNO (arg)]->giv; tv; tv = tv->family) - if ((tv->new_reg != 0) - && rtx_equal_p (tv->mult_val, v->mult_val) - && rtx_equal_p (tv->add_val, v->add_val) - && tv->mode == mode) - break; - if (tv) - break; - } - if (v) - { - /* Replace biv with its giv's reduced reg. */ - XEXP (src, 1-arg_operand) = v->new_reg; - /* Replace other operand with the other giv's reduced reg. */ - XEXP (src, arg_operand) = tv->new_reg; - - /* If the giv has the opposite direction of change, - then reverse the comparison. */ - if (INTVAL (v->mult_val) < 0) - { - temp = XEXP (src, 0); - XEXP (src, 0) = XEXP (src, 1); - XEXP (src, 1) = temp; - } - return; - } - } - abort (); - - default: - abort (); - } -} - -/* Try to calculate the final value of the biv, - the value it will have at the end of the loop. - If we can do it, return that value. */ - -/* ??? One case that should be simple to handle - is when the biv is incremented by an invariant - exactly once each time around the loop, - and when the number of iterations can be determined - (as the value of some invariant). - Then the final value would be BIV + (INCREMENT * NUM_ITERATIONS). - - Once that case is handled, it would become desirable to detect empty - loops and delete them, since this optimization could make empty loops. */ - -static rtx -final_biv_value (bl, loop_end) - struct iv_class *bl; - rtx loop_end; -{ - /* wimpy, but guaranteed to work */ - return 0; -} - -/* Return nonzero if the last use of reg REGNO - is in an insn following INSN in the same basic block. */ - -static int -last_use_this_basic_block (regno, insn) - int regno; - rtx insn; -{ - rtx n; - for (n = insn; - n && GET_CODE (n) != CODE_LABEL && GET_CODE (n) != JUMP_INSN; - n = NEXT_INSN (n)) - { - if (regno_last_uid[regno] == INSN_UID (n)) - return 1; - } - return 0; -} diff --git a/gnu/usr.bin/gcc1/cc1/machmode.def b/gnu/usr.bin/gcc1/cc1/machmode.def deleted file mode 100644 index d84ea49db8..0000000000 --- a/gnu/usr.bin/gcc1/cc1/machmode.def +++ /dev/null @@ -1,120 +0,0 @@ -/* This file contains the definitions and documentation for the - machine modes used in the the GNU compiler. - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This file defines all the MACHINE MODES used by GNU CC. - - A machine mode specifies a size and format of data - at the machine level. - - Each RTL expression has a machine mode. - - At the syntax tree level, each ..._TYPE and each ..._DECL node - has a machine mode which describes data of that type or the - data of the variable declared. */ - -/* The first argument is the internal name of the machine mode - used in the C source. - By convention these are in UPPER_CASE, except for the word "mode". - - The second argument is the name of the machine mode in the - external ASCII format used for reading and printing RTL and trees. - By convention these names in UPPER_CASE. - - Third argument states the kind of representation: - MODE_INT - integer - MODE_FLOAT - floating - MODE_COMPLEX_INT - pair of integers - MODE_COMPLEX_FLOAT - pair of floats - MODE_FUNCTION - Algol or Pascal function-variable incl. static chain - MODE_RANDOM - anything else - - Fourth argument is the relative size of the object. - It is zero when the size is meaningless or not determined. - On most machines, this is also the actual size in bytes. - However, the general rule is that the size of SImode in bytes - is UNITS_PER_WORD and the other sizes are proportional to that. - (If UNITS_PER_WORD is less than 4, some modes would be less than - one byte. Their sizes are rounded up to 1.) - - Fifth arg is the relative size of subunits of the object. - It is same as the fourth argument except for complexes and EPmode, - since they are really made of two equal size subunits. - - Sixth arg is next wider natural mode of the same class, - for widening multiply and narrowing divide. 0 if there is none. */ - -/* The compiler assumes that a mode may be widened to another - HIGHER NUMBERED mode if both those modes AND ALL MODES IN BETWEEN - are in the same mode class. Thus, you must should assign all - MODE_INT and MODE_FLOAT modes in separate contiguous blocks. */ - -/* VOIDmode is used when no mode needs to be specified, - as for example on CONST_INT RTL expressions. */ -DEF_MACHMODE (VOIDmode, "VOID", MODE_RANDOM, 0, 0, 0) - -DEF_MACHMODE (QImode, "QI", MODE_INT, 1, 1, HImode) /* int types */ -DEF_MACHMODE (HImode, "HI", MODE_INT, 2, 2, SImode) -/* Pointers on some machines use this type to distinguish them from ints. - Useful if a pointer is 4 bytes but has some bits that are not significant, - so it is really not quite as wide as an integer. */ -DEF_MACHMODE (PSImode, "PSI", MODE_INT, 4, 4, 0) -DEF_MACHMODE (SImode, "SI", MODE_INT, 4, 4, DImode) -DEF_MACHMODE (PDImode, "PDI", MODE_INT, 8, 8, 0) -DEF_MACHMODE (DImode, "DI", MODE_INT, 8, 8, TImode) -DEF_MACHMODE (TImode, "TI", MODE_INT, 16, 16, 0) -DEF_MACHMODE (QFmode, "QF", MODE_FLOAT, 1, 1, 0) -DEF_MACHMODE (HFmode, "HF", MODE_FLOAT, 2, 2, 0) /* floating types */ -DEF_MACHMODE (SFmode, "SF", MODE_FLOAT, 4, 4, 0) -DEF_MACHMODE (DFmode, "DF", MODE_FLOAT, 8, 8, 0) -DEF_MACHMODE (XFmode, "XF", MODE_FLOAT, 12, 12, 0) /* IEEE extended float */ -DEF_MACHMODE (TFmode, "TF", MODE_FLOAT, 16, 16, 0) -DEF_MACHMODE (CQImode, "CQI", MODE_COMPLEX_INT, 2, 1, 0) -DEF_MACHMODE (CHImode, "CHI", MODE_COMPLEX_INT, 4, 2, 0) /* complex ints */ -DEF_MACHMODE (CSImode, "CSI", MODE_COMPLEX_INT, 8, 4, 0) -DEF_MACHMODE (CDImode, "CDI", MODE_COMPLEX_INT, 16, 8, 0) -DEF_MACHMODE (CTImode, "CTI", MODE_COMPLEX_INT, 32, 16, 0) -DEF_MACHMODE (CQFmode, "CQF", MODE_COMPLEX_FLOAT, 2, 1, 0) -DEF_MACHMODE (CHFmode, "CHF", MODE_COMPLEX_FLOAT, 4, 2, 0) /* complex floats */ -DEF_MACHMODE (CSFmode, "CSF", MODE_COMPLEX_FLOAT, 8, 4, 0) -DEF_MACHMODE (CDFmode, "CDF", MODE_COMPLEX_FLOAT, 16, 8, 0) -DEF_MACHMODE (CXFmode, "CXF", MODE_COMPLEX_FLOAT, 24, 12, 0) -DEF_MACHMODE (CTFmode, "CTF", MODE_COMPLEX_FLOAT, 32, 16, 0) - -/* BImode is used only in FIELD_DECL nodes for bit fields - whose size and alignment are not such as to fit any other mode. */ -DEF_MACHMODE (BImode, "BI", MODE_INT, 0, 0, 0) /* signed bit field */ - -/* BLKmode is used for structures, arrays, etc. - that fit no more specific mode. */ -DEF_MACHMODE (BLKmode, "BLK", MODE_RANDOM, 0, 0, 0) - -/* Function-variable that includes a static chain. */ -DEF_MACHMODE (EPmode, "EP", MODE_RANDOM, 8, 4, 0) - -/* The symbol Pmode stands for one of the above machine modes (usually SImode). - The tm file specifies which one. It is not a distinct mode. */ - -/* -Local variables: -mode:c -version-control: t -End: -*/ diff --git a/gnu/usr.bin/gcc1/cc1/math-68881.h b/gnu/usr.bin/gcc1/cc1/math-68881.h deleted file mode 100644 index 0e853f5898..0000000000 --- a/gnu/usr.bin/gcc1/cc1/math-68881.h +++ /dev/null @@ -1,475 +0,0 @@ -/******************************************************************\ -* * -* last modified: 18 May 1989. * -* * -* Copyright (C) 1989 by Matthew Self. * -* You may freely distribute verbatim copies of this software * -* provided that this copyright notice is retained in all copies. * -* You may distribute modifications to this software under the * -* conditions above if you also clearly note such modifications * -* with their author and date. * -* * -* Note: errno is not set to EDOM when domain errors occur for * -* most of these functions. Rather, it is assumed that the * -* 68881's OPERR exception will be enabled and handled * -* appropriately by the operating system. Similarly, overflow * -* and underflow do not set errno to ERANGE. * -* * -* Send bugs to Matthew Self (self@bayes.arc.nasa.gov). * -* * -\******************************************************************/ - -/* Modified by Richard Stallman, November 1990, to initialize HUGE_VAL - specially on a Sun. - December 1989, add parens around `&' in pow. */ - -#include - -#ifndef HUGE_VAL -#ifdef __sun__ -/* The Sun assembler fails to handle the hex constant in the usual defn. */ -#define HUGE_VAL \ -({ \ - static union { int i[2]; double d; } u = { {0x7ff00000, 0} }; \ - u.d; \ -}) -#else -#define HUGE_VAL \ -({ \ - double huge_val; \ - \ - __asm ("fmove%.d #0x7ff0000000000000,%0" /* Infinity */ \ - : "=f" (huge_val) \ - : /* no inputs */); \ - huge_val; \ -}) -#endif -#endif - -__inline static const double sin (double x) -{ - double value; - - __asm ("fsin%.x %1,%0" - : "=f" (value) - : "f" (x)); - return value; -} - -__inline static const double cos (double x) -{ - double value; - - __asm ("fcos%.x %1,%0" - : "=f" (value) - : "f" (x)); - return value; -} - -__inline static const double tan (double x) -{ - double value; - - __asm ("ftan%.x %1,%0" - : "=f" (value) - : "f" (x)); - return value; -} - -__inline static const double asin (double x) -{ - double value; - - __asm ("fasin%.x %1,%0" - : "=f" (value) - : "f" (x)); - return value; -} - -__inline static const double acos (double x) -{ - double value; - - __asm ("facos%.x %1,%0" - : "=f" (value) - : "f" (x)); - return value; -} - -__inline static const double atan (double x) -{ - double value; - - __asm ("fatan%.x %1,%0" - : "=f" (value) - : "f" (x)); - return value; -} - -__inline static const double atan2 (double y, double x) -{ - double pi, pi_over_2; - - __asm ("fmovecr%.x %#0,%0" /* extended precision pi */ - : "=f" (pi) - : /* no inputs */ ); - __asm ("fscale%.b %#-1,%0" /* no loss of accuracy */ - : "=f" (pi_over_2) - : "0" (pi)); - if (x > 0) - { - if (y > 0) - { - if (x > y) - return atan (y / x); - else - return pi_over_2 - atan (x / y); - } - else - { - if (x > -y) - return atan (y / x); - else - return - pi_over_2 - atan (x / y); - } - } - else - { - if (y > 0) - { - if (-x > y) - return pi + atan (y / x); - else - return pi_over_2 - atan (x / y); - } - else - { - if (-x > -y) - return - pi + atan (y / x); - else if (y < 0) - return - pi_over_2 - atan (x / y); - else - { - double value; - - errno = EDOM; - __asm ("fmove%.d %#0rnan,%0" /* quiet NaN */ - : "=f" (value) - : /* no inputs */); - return value; - } - } - } -} - -__inline static const double sinh (double x) -{ - double value; - - __asm ("fsinh%.x %1,%0" - : "=f" (value) - : "f" (x)); - return value; -} - -__inline static const double cosh (double x) -{ - double value; - - __asm ("fcosh%.x %1,%0" - : "=f" (value) - : "f" (x)); - return value; -} - -__inline static const double tanh (double x) -{ - double value; - - __asm ("ftanh%.x %1,%0" - : "=f" (value) - : "f" (x)); - return value; -} - -__inline static const double atanh (double x) -{ - double value; - - __asm ("fatanh%.x %1,%0" - : "=f" (value) - : "f" (x)); - return value; -} - -__inline static const double exp (double x) -{ - double value; - - __asm ("fetox%.x %1,%0" - : "=f" (value) - : "f" (x)); - return value; -} - -__inline static const double expm1 (double x) -{ - double value; - - __asm ("fetoxm1%.x %1,%0" - : "=f" (value) - : "f" (x)); - return value; -} - -__inline static const double log (double x) -{ - double value; - - __asm ("flogn%.x %1,%0" - : "=f" (value) - : "f" (x)); - return value; -} - -__inline static const double log1p (double x) -{ - double value; - - __asm ("flognp1%.x %1,%0" - : "=f" (value) - : "f" (x)); - return value; -} - -__inline static const double log10 (double x) -{ - double value; - - __asm ("flog10%.x %1,%0" - : "=f" (value) - : "f" (x)); - return value; -} - -__inline static const double sqrt (double x) -{ - double value; - - __asm ("fsqrt%.x %1,%0" - : "=f" (value) - : "f" (x)); - return value; -} - -__inline static const double pow (const double x, const double y) -{ - if (x > 0) - return exp (y * log (x)); - else if (x == 0) - { - if (y > 0) - return 0.0; - else - { - double value; - - errno = EDOM; - __asm ("fmove%.d %#0rnan,%0" /* quiet NaN */ - : "=f" (value) - : /* no inputs */); - return value; - } - } - else - { - double temp; - - __asm ("fintrz%.x %1,%0" - : "=f" (temp) /* integer-valued float */ - : "f" (y)); - if (y == temp) - { - int i = (int) y; - - if ((i & 1) == 0) /* even */ - return exp (y * log (x)); - else - return - exp (y * log (x)); - } - else - { - double value; - - errno = EDOM; - __asm ("fmove%.d %#0rnan,%0" /* quiet NaN */ - : "=f" (value) - : /* no inputs */); - return value; - } - } -} - -__inline static const double fabs (double x) -{ - double value; - - __asm ("fabs%.x %1,%0" - : "=f" (value) - : "f" (x)); - return value; -} - -__inline static const double ceil (double x) -{ - int rounding_mode, round_up; - double value; - - __asm volatile ("fmove%.l fpcr,%0" - : "=dm" (rounding_mode) - : /* no inputs */ ); - round_up = rounding_mode | 0x30; - __asm volatile ("fmove%.l %0,fpcr" - : /* no outputs */ - : "dmi" (round_up)); - __asm volatile ("fint%.x %1,%0" - : "=f" (value) - : "f" (x)); - __asm volatile ("fmove%.l %0,fpcr" - : /* no outputs */ - : "dmi" (rounding_mode)); - return value; -} - -__inline static const double floor (double x) -{ - int rounding_mode, round_down; - double value; - - __asm volatile ("fmove%.l fpcr,%0" - : "=dm" (rounding_mode) - : /* no inputs */ ); - round_down = (rounding_mode & ~0x10) - | 0x20; - __asm volatile ("fmove%.l %0,fpcr" - : /* no outputs */ - : "dmi" (round_down)); - __asm volatile ("fint%.x %1,%0" - : "=f" (value) - : "f" (x)); - __asm volatile ("fmove%.l %0,fpcr" - : /* no outputs */ - : "dmi" (rounding_mode)); - return value; -} - -__inline static const double rint (double x) -{ - int rounding_mode, round_nearest; - double value; - - __asm volatile ("fmove%.l fpcr,%0" - : "=dm" (rounding_mode) - : /* no inputs */ ); - round_nearest = rounding_mode & ~0x30; - __asm volatile ("fmove%.l %0,fpcr" - : /* no outputs */ - : "dmi" (round_nearest)); - __asm volatile ("fint%.x %1,%0" - : "=f" (value) - : "f" (x)); - __asm volatile ("fmove%.l %0,fpcr" - : /* no outputs */ - : "dmi" (rounding_mode)); - return value; -} - -__inline static const double fmod (double x, double y) -{ - double value; - - __asm ("fmod%.x %2,%0" - : "=f" (value) - : "0" (x), - "f" (y)); - return value; -} - -__inline static const double drem (double x, double y) -{ - double value; - - __asm ("frem%.x %2,%0" - : "=f" (value) - : "0" (x), - "f" (y)); - return value; -} - -__inline static const double scalb (double x, int n) -{ - double value; - - __asm ("fscale%.l %2,%0" - : "=f" (value) - : "0" (x), - "dmi" (n)); - return value; -} - -__inline static double logb (double x) -{ - double exponent; - - __asm ("fgetexp%.x %1,%0" - : "=f" (exponent) - : "f" (x)); - return exponent; -} - -__inline static const double ldexp (double x, int n) -{ - double value; - - __asm ("fscale%.l %2,%0" - : "=f" (value) - : "0" (x), - "dmi" (n)); - return value; -} - -__inline static double frexp (double x, int *exp) -{ - double float_exponent; - int int_exponent; - double mantissa; - - __asm ("fgetexp%.x %1,%0" - : "=f" (float_exponent) /* integer-valued float */ - : "f" (x)); - int_exponent = (int) float_exponent; - __asm ("fgetman%.x %1,%0" - : "=f" (mantissa) /* 1.0 <= mantissa < 2.0 */ - : "f" (x)); - if (mantissa != 0) - { - __asm ("fscale%.b %#-1,%0" - : "=f" (mantissa) /* mantissa /= 2.0 */ - : "0" (mantissa)); - int_exponent += 1; - } - *exp = int_exponent; - return mantissa; -} - -__inline static double modf (double x, double *ip) -{ - double temp; - - __asm ("fintrz%.x %1,%0" - : "=f" (temp) /* integer-valued float */ - : "f" (x)); - *ip = temp; - return x - temp; -} - diff --git a/gnu/usr.bin/gcc1/cc1/move-if-change b/gnu/usr.bin/gcc1/cc1/move-if-change deleted file mode 100644 index 4bbb2f1e8f..0000000000 --- a/gnu/usr.bin/gcc1/cc1/move-if-change +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh -if -test -r $2 -then -if -cmp $1 $2 > /dev/null -then -echo $2 is unchanged -rm $1 -else -mv $1 $2 -fi -else -mv $1 $2 -fi diff --git a/gnu/usr.bin/gcc1/cc1/obstack.c b/gnu/usr.bin/gcc1/cc1/obstack.c deleted file mode 100644 index 13dc0fc31a..0000000000 --- a/gnu/usr.bin/gcc1/cc1/obstack.c +++ /dev/null @@ -1,326 +0,0 @@ -/* obstack.c - subroutines used implicitly by object stack macros - Copyright (C) 1988 Free Software Foundation, Inc. - -This program is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 1, or (at your option) any -later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - -In other words, you are welcome to use, share and improve this program. -You are forbidden to forbid anyone else to use, share and improve -what you give them. Help stamp out software-hoarding! */ - - -#include "obstack.h" - -#ifdef __STDC__ -#define POINTER void * -#else -#define POINTER char * -#endif - -/* Determine default alignment. */ -struct fooalign {char x; double d;}; -#define DEFAULT_ALIGNMENT ((char *)&((struct fooalign *) 0)->d - (char *)0) -/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT. - But in fact it might be less smart and round addresses to as much as - DEFAULT_ROUNDING. So we prepare for it to do that. */ -union fooround {long x; double d;}; -#define DEFAULT_ROUNDING (sizeof (union fooround)) - -/* When we copy a long block of data, this is the unit to do it with. - On some machines, copying successive ints does not work; - in such a case, redefine COPYING_UNIT to `long' (if that works) - or `char' as a last resort. */ -#ifndef COPYING_UNIT -#define COPYING_UNIT int -#endif - -/* The non-GNU-C macros copy the obstack into this global variable - to avoid multiple evaluation. */ - -struct obstack *_obstack; - -/* Initialize an obstack H for use. Specify chunk size SIZE (0 means default). - Objects start on multiples of ALIGNMENT (0 means use default). - CHUNKFUN is the function to use to allocate chunks, - and FREEFUN the function to free them. */ - -void -_obstack_begin (h, size, alignment, chunkfun, freefun) - struct obstack *h; - int size; - int alignment; - POINTER (*chunkfun) (); - void (*freefun) (); -{ - register struct _obstack_chunk* chunk; /* points to new chunk */ - - if (alignment == 0) - alignment = DEFAULT_ALIGNMENT; - if (size == 0) - /* Default size is what GNU malloc can fit in a 4096-byte block. - Pick a number small enough that when rounded up to DEFAULT_ROUNDING - it is still smaller than 4096 - 4. */ - { - int extra = 4; - if (extra < DEFAULT_ROUNDING) - extra = DEFAULT_ROUNDING; - size = 4096 - extra; - } - - h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun; - h->freefun = freefun; - h->chunk_size = size; - h->alignment_mask = alignment - 1; - - chunk = h->chunk = (*h->chunkfun) (h->chunk_size); - h->next_free = h->object_base = chunk->contents; - h->chunk_limit = chunk->limit - = (char *) chunk + h->chunk_size; - chunk->prev = 0; -} - -/* Allocate a new current chunk for the obstack *H - on the assumption that LENGTH bytes need to be added - to the current object, or a new object of length LENGTH allocated. - Copies any partial object from the end of the old chunk - to the beginning of the new one. */ - -void -_obstack_newchunk (h, length) - struct obstack *h; - int length; -{ - register struct _obstack_chunk* old_chunk = h->chunk; - register struct _obstack_chunk* new_chunk; - register long new_size; - register int obj_size = h->next_free - h->object_base; - register int i; - int already; - - /* Compute size for new chunk. */ - new_size = (obj_size + length) + (obj_size >> 3) + 100; - if (new_size < h->chunk_size) - new_size = h->chunk_size; - - /* Allocate and initialize the new chunk. */ - new_chunk = h->chunk = (*h->chunkfun) (new_size); - new_chunk->prev = old_chunk; - new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size; - - /* Move the existing object to the new chunk. - Word at a time is fast and is safe if the object - is sufficiently aligned. */ - if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT) - { - for (i = obj_size / sizeof (COPYING_UNIT) - 1; - i >= 0; i--) - ((COPYING_UNIT *)new_chunk->contents)[i] - = ((COPYING_UNIT *)h->object_base)[i]; - /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT, - but that can cross a page boundary on a machine - which does not do strict alignment for COPYING_UNITS. */ - already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT); - } - else - already = 0; - /* Copy remaining bytes one by one. */ - for (i = already; i < obj_size; i++) - new_chunk->contents[i] = h->object_base[i]; - - h->object_base = new_chunk->contents; - h->next_free = h->object_base + obj_size; -} - -/* Return nonzero if object OBJ has been allocated from obstack H. - This is here for debugging. - If you use it in a program, you are probably losing. */ - -int -_obstack_allocated_p (h, obj) - struct obstack *h; - POINTER obj; -{ - register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */ - register struct _obstack_chunk* plp; /* point to previous chunk if any */ - - lp = (h)->chunk; - while (lp != 0 && ((POINTER)lp > obj || (POINTER)(lp)->limit < obj)) - { - plp = lp -> prev; - lp = plp; - } - return lp != 0; -} - -/* Free objects in obstack H, including OBJ and everything allocate - more recently than OBJ. If OBJ is zero, free everything in H. */ - -void -#ifdef __STDC__ -#undef obstack_free -obstack_free (struct obstack *h, POINTER obj) -#else -_obstack_free (h, obj) - struct obstack *h; - POINTER obj; -#endif -{ - register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */ - register struct _obstack_chunk* plp; /* point to previous chunk if any */ - - lp = (h)->chunk; - while (lp != 0 && ((POINTER)lp > obj || (POINTER)(lp)->limit < obj)) - { - plp = lp -> prev; - (*h->freefun) (lp); - lp = plp; - } - if (lp) - { - (h)->object_base = (h)->next_free = (char *)(obj); - (h)->chunk_limit = lp->limit; - (h)->chunk = lp; - } - else if (obj != 0) - /* obj is not in any of the chunks! */ - abort (); -} - -/* Let same .o link with output of gcc and other compilers. */ - -#ifdef __STDC__ -void -_obstack_free (h, obj) - struct obstack *h; - POINTER obj; -{ - obstack_free (h, obj); -} -#endif - -#if 0 -/* These are now turned off because the applications do not use it - and it uses bcopy via obstack_grow, which causes trouble on sysV. */ - -/* Now define the functional versions of the obstack macros. - Define them to simply use the corresponding macros to do the job. */ - -#ifdef __STDC__ -/* These function definitions do not work with non-ANSI preprocessors; - they won't pass through the macro names in parentheses. */ - -/* The function names appear in parentheses in order to prevent - the macro-definitions of the names from being expanded there. */ - -POINTER (obstack_base) (obstack) - struct obstack *obstack; -{ - return obstack_base (obstack); -} - -POINTER (obstack_next_free) (obstack) - struct obstack *obstack; -{ - return obstack_next_free (obstack); -} - -int (obstack_object_size) (obstack) - struct obstack *obstack; -{ - return obstack_object_size (obstack); -} - -int (obstack_room) (obstack) - struct obstack *obstack; -{ - return obstack_room (obstack); -} - -void (obstack_grow) (obstack, pointer, length) - struct obstack *obstack; - POINTER pointer; - int length; -{ - obstack_grow (obstack, pointer, length); -} - -void (obstack_grow0) (obstack, pointer, length) - struct obstack *obstack; - POINTER pointer; - int length; -{ - obstack_grow0 (obstack, pointer, length); -} - -void (obstack_1grow) (obstack, character) - struct obstack *obstack; - int character; -{ - obstack_1grow (obstack, character); -} - -void (obstack_blank) (obstack, length) - struct obstack *obstack; - int length; -{ - obstack_blank (obstack, length); -} - -void (obstack_1grow_fast) (obstack, character) - struct obstack *obstack; - int character; -{ - obstack_1grow_fast (obstack, character); -} - -void (obstack_blank_fast) (obstack, length) - struct obstack *obstack; - int length; -{ - obstack_blank_fast (obstack, length); -} - -POINTER (obstack_finish) (obstack) - struct obstack *obstack; -{ - return obstack_finish (obstack); -} - -POINTER (obstack_alloc) (obstack, length) - struct obstack *obstack; - int length; -{ - return obstack_alloc (obstack, length); -} - -POINTER (obstack_copy) (obstack, pointer, length) - struct obstack *obstack; - POINTER pointer; - int length; -{ - return obstack_copy (obstack, pointer, length); -} - -POINTER (obstack_copy0) (obstack, pointer, length) - struct obstack *obstack; - POINTER pointer; - int length; -{ - return obstack_copy0 (obstack, pointer, length); -} - -#endif /* __STDC__ */ - -#endif /* 0 */ diff --git a/gnu/usr.bin/gcc1/cc1/obstack.h b/gnu/usr.bin/gcc1/cc1/obstack.h deleted file mode 100644 index b1bcad77c2..0000000000 --- a/gnu/usr.bin/gcc1/cc1/obstack.h +++ /dev/null @@ -1,410 +0,0 @@ -/* obstack.h - object stack macros - Copyright (C) 1988 Free Software Foundation, Inc. - -This program is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 1, or (at your option) any -later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - -In other words, you are welcome to use, share and improve this program. -You are forbidden to forbid anyone else to use, share and improve -what you give them. Help stamp out software-hoarding! */ - - -/* Summary: - -All the apparent functions defined here are macros. The idea -is that you would use these pre-tested macros to solve a -very specific set of problems, and they would run fast. -Caution: no side-effects in arguments please!! They may be -evaluated MANY times!! - -These macros operate a stack of objects. Each object starts life -small, and may grow to maturity. (Consider building a word syllable -by syllable.) An object can move while it is growing. Once it has -been "finished" it never changes address again. So the "top of the -stack" is typically an immature growing object, while the rest of the -stack is of mature, fixed size and fixed address objects. - -These routines grab large chunks of memory, using a function you -supply, called `obstack_chunk_alloc'. On occasion, they free chunks, -by calling `obstack_chunk_free'. You must define them and declare -them before using any obstack macros. - -Each independent stack is represented by a `struct obstack'. -Each of the obstack macros expects a pointer to such a structure -as the first argument. - -One motivation for this package is the problem of growing char strings -in symbol tables. Unless you are "fascist pig with a read-only mind" -[Gosper's immortal quote from HAKMEM item 154, out of context] you -would not like to put any arbitrary upper limit on the length of your -symbols. - -In practice this often means you will build many short symbols and a -few long symbols. At the time you are reading a symbol you don't know -how long it is. One traditional method is to read a symbol into a -buffer, realloc()ating the buffer every time you try to read a symbol -that is longer than the buffer. This is beaut, but you still will -want to copy the symbol from the buffer to a more permanent -symbol-table entry say about half the time. - -With obstacks, you can work differently. Use one obstack for all symbol -names. As you read a symbol, grow the name in the obstack gradually. -When the name is complete, finalize it. Then, if the symbol exists already, -free the newly read name. - -The way we do this is to take a large chunk, allocating memory from -low addresses. When you want to build a symbol in the chunk you just -add chars above the current "high water mark" in the chunk. When you -have finished adding chars, because you got to the end of the symbol, -you know how long the chars are, and you can create a new object. -Mostly the chars will not burst over the highest address of the chunk, -because you would typically expect a chunk to be (say) 100 times as -long as an average object. - -In case that isn't clear, when we have enough chars to make up -the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed) -so we just point to it where it lies. No moving of chars is -needed and this is the second win: potentially long strings need -never be explicitly shuffled. Once an object is formed, it does not -change its address during its lifetime. - -When the chars burst over a chunk boundary, we allocate a larger -chunk, and then copy the partly formed object from the end of the old -chunk to the beginning of the new larger chunk. We then carry on -accreting characters to the end of the object as we normally would. - -A special macro is provided to add a single char at a time to a -growing object. This allows the use of register variables, which -break the ordinary 'growth' macro. - -Summary: - We allocate large chunks. - We carve out one object at a time from the current chunk. - Once carved, an object never moves. - We are free to append data of any size to the currently - growing object. - Exactly one object is growing in an obstack at any one time. - You can run one obstack per control block. - You may have as many control blocks as you dare. - Because of the way we do it, you can `unwind' a obstack - back to a previous state. (You may remove objects much - as you would with a stack.) -*/ - - -/* Don't do the contents of this file more than once. */ - -#ifndef __OBSTACKS__ -#define __OBSTACKS__ - -/* We use subtraction of (char *)0 instead of casting to int - because on word-addressable machines a simple cast to int - may ignore the byte-within-word field of the pointer. */ - -#ifndef __PTR_TO_INT -#define __PTR_TO_INT(P) ((P) - (char *)0) -#endif - -#ifndef __INT_TO_PTR -#define __INT_TO_PTR(P) ((P) + (char *)0) -#endif - -struct _obstack_chunk /* Lives at front of each chunk. */ -{ - char *limit; /* 1 past end of this chunk */ - struct _obstack_chunk *prev; /* address of prior chunk or NULL */ - char contents[4]; /* objects begin here */ -}; - -struct obstack /* control current object in current chunk */ -{ - long chunk_size; /* preferred size to allocate chunks in */ - struct _obstack_chunk* chunk; /* address of current struct obstack_chunk */ - char *object_base; /* address of object we are building */ - char *next_free; /* where to add next char to current object */ - char *chunk_limit; /* address of char after current chunk */ - int temp; /* Temporary for some macros. */ - int alignment_mask; /* Mask of alignment for each object. */ - struct _obstack_chunk *(*chunkfun) (); /* User's fcn to allocate a chunk. */ - void (*freefun) (); /* User's function to free a chunk. */ -}; - -#ifdef __STDC__ - -/* Do the function-declarations after the structs - but before defining the macros. */ - -void obstack_init (struct obstack *obstack); - -void * obstack_alloc (struct obstack *obstack, int size); - -void * obstack_copy (struct obstack *obstack, void *address, int size); -void * obstack_copy0 (struct obstack *obstack, void *address, int size); - -void obstack_free (struct obstack *obstack, void *block); - -void obstack_blank (struct obstack *obstack, int size); - -void obstack_grow (struct obstack *obstack, void *data, int size); -void obstack_grow0 (struct obstack *obstack, void *data, int size); - -void obstack_1grow (struct obstack *obstack, int data_char); -void obstack_ptr_grow (struct obstack *obstack, void *data); -void obstack_int_grow (struct obstack *obstack, int data); - -void * obstack_finish (struct obstack *obstack); - -int obstack_object_size (struct obstack *obstack); - -int obstack_room (struct obstack *obstack); -void obstack_1grow_fast (struct obstack *obstack, int data_char); -void obstack_ptr_grow_fast (struct obstack *obstack, void *data); -void obstack_int_grow_fast (struct obstack *obstack, int data); -void obstack_blank_fast (struct obstack *obstack, int size); - -void * obstack_base (struct obstack *obstack); -void * obstack_next_free (struct obstack *obstack); -int obstack_alignment_mask (struct obstack *obstack); -int obstack_chunk_size (struct obstack *obstack); - -#endif /* __STDC__ */ - -/* Non-ANSI C cannot really support alternative functions for these macros, - so we do not declare them. */ - -/* Pointer to beginning of object being allocated or to be allocated next. - Note that this might not be the final address of the object - because a new chunk might be needed to hold the final size. */ - -#define obstack_base(h) ((h)->object_base) - -/* Size for allocating ordinary chunks. */ - -#define obstack_chunk_size(h) ((h)->chunk_size) - -/* Pointer to next byte not yet allocated in current chunk. */ - -#define obstack_next_free(h) ((h)->next_free) - -/* Mask specifying low bits that should be clear in address of an object. */ - -#define obstack_alignment_mask(h) ((h)->alignment_mask) - -#define obstack_init(h) \ - _obstack_begin ((h), 0, 0, obstack_chunk_alloc, obstack_chunk_free) - -#define obstack_begin(h, size) \ - _obstack_begin ((h), (size), 0, obstack_chunk_alloc, obstack_chunk_free) - -#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = achar) - -#define obstack_blank_fast(h,n) ((h)->next_free += (n)) - -#if defined (__GNUC__) && defined (__STDC__) - -/* For GNU C, if not -traditional, - we can define these macros to compute all args only once - without using a global variable. - Also, we can avoid using the `temp' slot, to make faster code. */ - -#define obstack_object_size(OBSTACK) \ - ({ struct obstack *__o = (OBSTACK); \ - (unsigned) (__o->next_free - __o->object_base); }) - -#define obstack_room(OBSTACK) \ - ({ struct obstack *__o = (OBSTACK); \ - (unsigned) (__o->chunk_limit - __o->next_free); }) - -#define obstack_grow(OBSTACK,where,length) \ -({ struct obstack *__o = (OBSTACK); \ - int __len = (length); \ - ((__o->next_free + __len > __o->chunk_limit) \ - ? _obstack_newchunk (__o, __len) : 0); \ - bcopy (where, __o->next_free, __len); \ - __o->next_free += __len; \ - (void) 0; }) - -#define obstack_grow0(OBSTACK,where,length) \ -({ struct obstack *__o = (OBSTACK); \ - int __len = (length); \ - ((__o->next_free + __len + 1 > __o->chunk_limit) \ - ? _obstack_newchunk (__o, __len + 1) : 0), \ - bcopy (where, __o->next_free, __len), \ - __o->next_free += __len, \ - *(__o->next_free)++ = 0; \ - (void) 0; }) - -#define obstack_1grow(OBSTACK,datum) \ -({ struct obstack *__o = (OBSTACK); \ - ((__o->next_free + 1 > __o->chunk_limit) \ - ? _obstack_newchunk (__o, 1) : 0), \ - *(__o->next_free)++ = (datum); \ - (void) 0; }) - -/* These assume that the obstack alignment is good enough for pointers or ints, - and that the data added so far to the current object - shares that much alignment. */ - -#define obstack_ptr_grow(OBSTACK,datum) \ -({ struct obstack *__o = (OBSTACK); \ - ((__o->next_free + sizeof (void *) > __o->chunk_limit) \ - ? _obstack_newchunk (__o, sizeof (void *)) : 0), \ - *((void **)__o->next_free)++ = ((void *)datum); \ - (void) 0; }) - -#define obstack_int_grow(OBSTACK,datum) \ -({ struct obstack *__o = (OBSTACK); \ - ((__o->next_free + sizeof (int) > __o->chunk_limit) \ - ? _obstack_newchunk (__o, sizeof (int)) : 0), \ - *((int *)__o->next_free)++ = ((int)datum); \ - (void) 0; }) - -#define obstack_ptr_grow_fast(h,aptr) (*((void **)(h)->next_free)++ = (void *)aptr) -#define obstack_int_grow_fast(h,aint) (*((int *)(h)->next_free)++ = (int)aint) - -#define obstack_blank(OBSTACK,length) \ -({ struct obstack *__o = (OBSTACK); \ - int __len = (length); \ - ((__o->next_free + __len > __o->chunk_limit) \ - ? _obstack_newchunk (__o, __len) : 0); \ - __o->next_free += __len; \ - (void) 0; }) - -#define obstack_alloc(OBSTACK,length) \ -({ struct obstack *__h = (OBSTACK); \ - obstack_blank (__h, (length)); \ - obstack_finish (__h); }) - -#define obstack_copy(OBSTACK,where,length) \ -({ struct obstack *__h = (OBSTACK); \ - obstack_grow (__h, (where), (length)); \ - obstack_finish (__h); }) - -#define obstack_copy0(OBSTACK,where,length) \ -({ struct obstack *__h = (OBSTACK); \ - obstack_grow0 (__h, (where), (length)); \ - obstack_finish (__h); }) - -#define obstack_finish(OBSTACK) \ -({ struct obstack *__o = (OBSTACK); \ - void *value = (void *) __o->object_base; \ - __o->next_free \ - = __INT_TO_PTR ((__PTR_TO_INT (__o->next_free)+__o->alignment_mask)\ - & ~ (__o->alignment_mask)); \ - ((__o->next_free - (char *)__o->chunk \ - > __o->chunk_limit - (char *)__o->chunk) \ - ? (__o->next_free = __o->chunk_limit) : 0); \ - __o->object_base = __o->next_free; \ - value; }) - -#define obstack_free(OBSTACK, OBJ) \ -({ struct obstack *__o = (OBSTACK); \ - void *__obj = (OBJ); \ - if (__obj >= (void *)__o->chunk && __obj < (void *)__o->chunk_limit) \ - __o->next_free = __o->object_base = __obj; \ - else (obstack_free) (__o, __obj); }) - -#else /* not __GNUC__ or not __STDC__ */ - -/* The non-GNU macros copy the obstack-pointer into this global variable - to avoid multiple evaluation. */ - -extern struct obstack *_obstack; - -#define obstack_object_size(h) \ - (unsigned) (_obstack = (h), (h)->next_free - (h)->object_base) - -#define obstack_room(h) \ - (unsigned) (_obstack = (h), (h)->chunk_limit - (h)->next_free) - -#define obstack_grow(h,where,length) \ -( (h)->temp = (length), \ - (((h)->next_free + (h)->temp > (h)->chunk_limit) \ - ? _obstack_newchunk ((h), (h)->temp) : 0), \ - bcopy (where, (h)->next_free, (h)->temp), \ - (h)->next_free += (h)->temp) - -#define obstack_grow0(h,where,length) \ -( (h)->temp = (length), \ - (((h)->next_free + (h)->temp + 1 > (h)->chunk_limit) \ - ? _obstack_newchunk ((h), (h)->temp + 1) : 0), \ - bcopy (where, (h)->next_free, (h)->temp), \ - (h)->next_free += (h)->temp, \ - *((h)->next_free)++ = 0) - -#define obstack_1grow(h,datum) \ -( (((h)->next_free + 1 > (h)->chunk_limit) \ - ? _obstack_newchunk ((h), 1) : 0), \ - *((h)->next_free)++ = (datum)) - -#define obstack_ptr_grow(h,datum) \ -( (((h)->next_free + sizeof (char *) > (h)->chunk_limit) \ - ? _obstack_newchunk ((h), sizeof (char *)) : 0), \ - *((char **)(h)->next_free)++ = ((char *)datum)) - -#define obstack_int_grow(h,datum) \ -( (((h)->next_free + sizeof (int) > (h)->chunk_limit) \ - ? _obstack_newchunk ((h), sizeof (int)) : 0), \ - *((int *)(h)->next_free)++ = ((int)datum)) - -#define obstack_ptr_grow_fast(h,aptr) (*((char **)(h)->next_free)++ = (char *)aptr) -#define obstack_int_grow_fast(h,aint) (*((int *)(h)->next_free)++ = (int)aint) - -#define obstack_blank(h,length) \ -( (h)->temp = (length), \ - (((h)->next_free + (h)->temp > (h)->chunk_limit) \ - ? _obstack_newchunk ((h), (h)->temp) : 0), \ - (h)->next_free += (h)->temp) - -#define obstack_alloc(h,length) \ - (obstack_blank ((h), (length)), obstack_finish ((h))) - -#define obstack_copy(h,where,length) \ - (obstack_grow ((h), (where), (length)), obstack_finish ((h))) - -#define obstack_copy0(h,where,length) \ - (obstack_grow0 ((h), (where), (length)), obstack_finish ((h))) - -#define obstack_finish(h) \ -( (h)->temp = __PTR_TO_INT ((h)->object_base), \ - (h)->next_free \ - = __INT_TO_PTR ((__PTR_TO_INT ((h)->next_free)+(h)->alignment_mask) \ - & ~ ((h)->alignment_mask)), \ - (((h)->next_free - (char *)(h)->chunk \ - > (h)->chunk_limit - (char *)(h)->chunk) \ - ? ((h)->next_free = (h)->chunk_limit) : 0), \ - (h)->object_base = (h)->next_free, \ - __INT_TO_PTR ((h)->temp)) - -#ifdef __STDC__ -#define obstack_free(h,obj) \ -( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \ - (((h)->temp >= 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\ - ? (int) ((h)->next_free = (h)->object_base \ - = (h)->temp + (char *) (h)->chunk) \ - : ((obstack_free) ((h), (h)->temp + (char *) (h)->chunk), 0))) -#else -#define obstack_free(h,obj) \ -( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \ - (((h)->temp >= 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\ - ? (int) ((h)->next_free = (h)->object_base \ - = (h)->temp + (char *) (h)->chunk) \ - : (int) _obstack_free ((h), (h)->temp + (char *) (h)->chunk))) -#endif - -#endif /* not __GNUC__ or not __STDC__ */ - -#endif /* not __OBSTACKS__ */ - diff --git a/gnu/usr.bin/gcc1/cc1/optabs.c b/gnu/usr.bin/gcc1/cc1/optabs.c deleted file mode 100644 index aaf54757ba..0000000000 --- a/gnu/usr.bin/gcc1/cc1/optabs.c +++ /dev/null @@ -1,2228 +0,0 @@ -/* Expand the basic unary and binary arithmetic operations, for GNU compiler. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "config.h" -#include "rtl.h" -#include "tree.h" -#include "flags.h" -#include "insn-flags.h" -#include "insn-codes.h" -#include "expr.h" -#include "insn-config.h" -#include "recog.h" - -/* In ANSI C we could write MODE + 1, but traditional C compilers - seem to reject it. */ -#define INC_MODE(MODE) (enum machine_mode) ((int)(MODE) + 1) - -/* Each optab contains info on how this target machine - can perform a particular operation - for all sizes and kinds of operands. - - The operation to be performed is often specified - by passing one of these optabs as an argument. - - See expr.h for documentation of these optabs. */ - -optab add_optab; -optab sub_optab; -optab smul_optab; -optab umul_optab; -optab smul_widen_optab; -optab umul_widen_optab; -optab sdiv_optab; -optab sdivmod_optab; -optab udiv_optab; -optab udivmod_optab; -optab smod_optab; -optab umod_optab; -optab flodiv_optab; -optab ftrunc_optab; -optab and_optab; -optab andcb_optab; -optab ior_optab; -optab xor_optab; -optab ashl_optab; -optab lshr_optab; -optab lshl_optab; -optab ashr_optab; -optab rotl_optab; -optab rotr_optab; - -optab mov_optab; -optab movstrict_optab; - -optab neg_optab; -optab abs_optab; -optab one_cmpl_optab; -optab ffs_optab; - -optab cmp_optab; -optab ucmp_optab; /* Used only for libcalls for unsigned comparisons. */ -optab tst_optab; - -/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...) - gives the gen_function to make a branch to test that condition. */ - -rtxfun bcc_gen_fctn[NUM_RTX_CODE]; - -/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...) - gives the gen_function to make a store-condition insn - to test that condition. */ - -rtxfun setcc_gen_fctn[NUM_RTX_CODE]; - -/* Generate code to perform an operation specified by BINOPTAB - on operands OP0 and OP1, with result having machine-mode MODE. - - UNSIGNEDP is for the case where we have to widen the operands - to perform the operation. It says to use zero-extension. - - If TARGET is nonzero, the value - is generated there, if it is convenient to do so. - In all cases an rtx is returned for the locus of the value; - this may or may not be TARGET. */ - -rtx -expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods) - enum machine_mode mode; - optab binoptab; - rtx op0, op1; - rtx target; - int unsignedp; - enum optab_methods methods; -{ - enum mode_class class; - enum machine_mode wider_mode; - register rtx temp; - rtx last; - - class = GET_MODE_CLASS (mode); - - op0 = protect_from_queue (op0, 0); - op1 = protect_from_queue (op1, 0); - if (target) - target = protect_from_queue (target, 1); - -#if 0 - /* We may get better code by generating the result in a register - when the target is not one of the operands. */ - if (target && ! rtx_equal_p (target, op1) && ! rtx_equal_p (target, op0)) - target_is_not_an_operand = 1; -#endif - - if (flag_force_mem) - { - op0 = force_not_mem (op0); - op1 = force_not_mem (op1); - } - - /* Record where to delete back to if we backtrack. */ - last = get_last_insn (); - - /* If operation is commutative, - try to make the first operand a register. - Even better, try to make it the same as the target. - Also try to make the last operand a constant. */ - if (binoptab == add_optab - || binoptab == and_optab - || binoptab == ior_optab - || binoptab == xor_optab - || binoptab == smul_optab - || binoptab == umul_optab - || binoptab == smul_widen_optab - || binoptab == umul_widen_optab) - { - if (((target == 0 || GET_CODE (target) == REG) - ? ((GET_CODE (op1) == REG - && GET_CODE (op0) != REG) - || target == op1) - : rtx_equal_p (op1, target)) - || - GET_CODE (op0) == CONST_INT) - { - temp = op1; - op1 = op0; - op0 = temp; - } - } - - /* If we can do it with a three-operand insn, do so. */ - - if (methods != OPTAB_MUST_WIDEN - && binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing) - { - int icode = (int) binoptab->handlers[(int) mode].insn_code; - enum machine_mode mode0 = insn_operand_mode[icode][1]; - enum machine_mode mode1 = insn_operand_mode[icode][2]; - rtx pat; - rtx xop0 = op0, xop1 = op1; - - if (target) - temp = target; - else - temp = gen_reg_rtx (mode); - - /* In case the insn wants input operands in modes different from - the result, convert the operands. */ - - if (GET_MODE (op0) != VOIDmode - && GET_MODE (op0) != mode0) - xop0 = convert_to_mode (mode0, xop0, unsignedp); - - if (GET_MODE (xop1) != VOIDmode - && GET_MODE (xop1) != mode1) - xop1 = convert_to_mode (mode1, xop1, unsignedp); - - /* Now, if insn requires register operands, put operands into regs. */ - - if (! (*insn_operand_predicate[icode][1]) (xop0, mode0)) - xop0 = force_reg (mode0, xop0); - - if (! (*insn_operand_predicate[icode][2]) (xop1, mode1)) - xop1 = force_reg (mode1, xop1); - - if (! (*insn_operand_predicate[icode][0]) (temp, mode)) - temp = gen_reg_rtx (mode); - - pat = GEN_FCN (icode) (temp, xop0, xop1); - if (pat) - { - emit_insn (pat); - return temp; - } - else - delete_insns_since (last); - } - - /* It can't be open-coded in this mode. - Use a library call if one is available and caller says that's ok. */ - - if (binoptab->handlers[(int) mode].lib_call - && (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN)) - { - rtx insn_before, insn_first, insn_last; - rtx funexp = gen_rtx (SYMBOL_REF, Pmode, - binoptab->handlers[(int) mode].lib_call); - - /* Pass the address through a pseudoreg, if desired, - before the "beginning" of the library call. - So this insn isn't "part of" the library call, in case that - is deleted, or cse'd. */ -#ifndef NO_FUNCTION_CSE - if (! flag_no_function_cse) - funexp = copy_to_mode_reg (Pmode, funexp); -#endif - - insn_before = get_last_insn (); - - /* Cannot pass FUNEXP since emit_library_call insists - on getting a SYMBOL_REF. But cse will make this SYMBOL_REF - be replaced with the copy we made just above. */ - /* Pass 1 for NO_QUEUE so we don't lose any increments - if the libcall is cse'd or moved. */ - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, - binoptab->handlers[(int) mode].lib_call), - 1, mode, 2, op0, mode, op1, mode); - target = hard_libcall_value (mode); - temp = copy_to_reg (target); - - if (insn_before == 0) - insn_first = get_insns (); - else - insn_first = NEXT_INSN (insn_before); - insn_last = get_last_insn (); - - REG_NOTES (insn_last) - = gen_rtx (EXPR_LIST, REG_EQUAL, - gen_rtx (binoptab->code, mode, op0, op1), - gen_rtx (INSN_LIST, REG_RETVAL, insn_first, - REG_NOTES (insn_last))); - REG_NOTES (insn_first) - = gen_rtx (INSN_LIST, REG_LIBCALL, insn_last, - REG_NOTES (insn_first)); - return temp; - } - - delete_insns_since (last); - - /* It can't be done in this mode. Can we do it in a wider mode? */ - - if (! (methods == OPTAB_WIDEN || methods == OPTAB_LIB_WIDEN - || methods == OPTAB_MUST_WIDEN)) - return 0; /* Caller says, don't even try. */ - - /* Compute the value of METHODS to pass to recursive calls. - Don't allow widening to be tried recursively. */ - - methods = (methods == OPTAB_LIB_WIDEN ? OPTAB_LIB : OPTAB_DIRECT); - - /* Widening is now independent of specific machine modes. - It is assumed that widening may be performed to any - higher numbered mode in the same mode class. */ - - if (class == MODE_INT || class == MODE_FLOAT) - { - for (wider_mode = INC_MODE (mode); - ((int) wider_mode < (int) MAX_MACHINE_MODE - && GET_MODE_CLASS (wider_mode) == class); - wider_mode = INC_MODE (wider_mode)) - { - if ((binoptab->handlers[(int) wider_mode].insn_code - != CODE_FOR_nothing) - || (methods == OPTAB_LIB - && binoptab->handlers[(int) wider_mode].lib_call)) - { - rtx xop0 = op0, xop1 = op1; - int no_extend = 0; - - /* For certain operations, we need not actually extend - the narrow operands, as long as we will truncate - the results to the same narrowness. */ - - if (binoptab == ior_optab || binoptab == and_optab - || binoptab == xor_optab || binoptab == andcb_optab - || binoptab == add_optab || binoptab == sub_optab - || binoptab == smul_optab || binoptab == umul_optab - || binoptab == ashl_optab || binoptab == lshl_optab) - no_extend = 1; - - if (GET_MODE (xop0) != VOIDmode) - { - if (no_extend) - { - temp = force_reg (GET_MODE (xop0), xop0); - xop0 = gen_rtx (SUBREG, wider_mode, temp, 0); - } - else - { - temp = gen_reg_rtx (wider_mode); - convert_move (temp, xop0, unsignedp); - xop0 = temp; - } - } - if (GET_MODE (xop1) != VOIDmode) - { - if (no_extend) - { - temp = force_reg (GET_MODE (xop1), xop1); - xop1 = gen_rtx (SUBREG, wider_mode, temp, 0); - } - else - { - temp = gen_reg_rtx (wider_mode); - convert_move (temp, xop1, unsignedp); - xop1 = temp; - } - } - - temp = expand_binop (wider_mode, binoptab, xop0, xop1, 0, - unsignedp, methods); - if (temp) - { - if (class == MODE_FLOAT) - { - if (target == 0) - target = gen_reg_rtx (mode); - convert_move (target, temp, 0); - return target; - } - else - return gen_lowpart (mode, temp); - } - else - delete_insns_since (last); - } - } - } - - return 0; -} - -/* Expand a binary operator which has both signed and unsigned forms. - UOPTAB is the optab for unsigned operations, and SOPTAB is for - signed operations. - - If we widen unsigned operands, we may use a signed wider operation instead - of an unsigned wider operation, since the result would be the same. */ - -rtx -sign_expand_binop (mode, uoptab, soptab, op0, op1, target, unsignedp, methods) - enum machine_mode mode; - optab uoptab, soptab; - rtx op0, op1, target; - int unsignedp; - enum optab_methods methods; -{ - register rtx temp; - optab direct_optab = unsignedp ? uoptab : soptab; - struct optab wide_soptab; - - /* Do it without widening, if possible. */ - temp = expand_binop (mode, direct_optab, op0, op1, target, - unsignedp, OPTAB_DIRECT); - if (temp || methods == OPTAB_DIRECT) - return temp; - - /* Try widening to a signed int. Make a fake signed optab that - hides any signed insn for direct use. */ - wide_soptab = *soptab; - wide_soptab.handlers[(int) mode].insn_code = CODE_FOR_nothing; - wide_soptab.handlers[(int) mode].lib_call = 0; - - temp = expand_binop (mode, &wide_soptab, op0, op1, target, - unsignedp, OPTAB_WIDEN); - - /* For unsigned operands, try widening to an unsigned int. */ - if (temp == 0 && unsignedp) - temp = expand_binop (mode, uoptab, op0, op1, target, - unsignedp, OPTAB_WIDEN); - if (temp || methods == OPTAB_WIDEN) - return temp; - - /* Use the right width lib call if that exists. */ - temp = expand_binop (mode, direct_optab, op0, op1, target, unsignedp, OPTAB_LIB); - if (temp || methods == OPTAB_LIB) - return temp; - - /* Must widen and use a lib call, use either signed or unsigned. */ - temp = expand_binop (mode, &wide_soptab, op0, op1, target, - unsignedp, methods); - if (temp != 0) - return temp; - if (unsignedp) - return expand_binop (mode, uoptab, op0, op1, target, - unsignedp, methods); - return 0; -} - -/* Generate code to perform an operation specified by BINOPTAB - on operands OP0 and OP1, with two results to TARG1 and TARG2. - We assume that the order of the operands for the instruction - is TARG0, OP0, OP1, TARG1, which would fit a pattern like - [(set TARG0 (operate OP0 OP1)) (set TARG1 (operate ...))]. - - Either TARG0 or TARG1 may be zero, but what that means is that - that result is not actually wanted. We will generate it into - a dummy pseudo-reg and discard it. They may not both be zero. - - Returns 1 if this operation can be performed; 0 if not. */ - -int -expand_twoval_binop (binoptab, op0, op1, targ0, targ1, unsignedp) - optab binoptab; - rtx op0, op1; - rtx targ0, targ1; - int unsignedp; -{ - enum machine_mode mode = GET_MODE (targ0 ? targ0 : targ1); - enum mode_class class; - enum machine_mode wider_mode; - - class = GET_MODE_CLASS (mode); - - op0 = protect_from_queue (op0, 0); - op1 = protect_from_queue (op1, 0); - - if (flag_force_mem) - { - op0 = force_not_mem (op0); - op1 = force_not_mem (op1); - } - - if (targ0) - targ0 = protect_from_queue (targ0, 1); - else - targ0 = gen_reg_rtx (mode); - if (targ1) - targ1 = protect_from_queue (targ1, 1); - else - targ1 = gen_reg_rtx (mode); - - if (binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing) - { - emit_insn (GEN_FCN (binoptab->handlers[(int) mode].insn_code) - (targ0, op0, op1, targ1)); - return 1; - } - - /* It can't be done in this mode. Can we do it in a wider mode? */ - - if (class == MODE_INT || class == MODE_FLOAT) - { - for (wider_mode = INC_MODE (mode); - ((int) wider_mode < (int) MAX_MACHINE_MODE - && GET_MODE_CLASS (wider_mode) == class); - wider_mode = INC_MODE (wider_mode)) - { - if (binoptab->handlers[(int) wider_mode].insn_code - != CODE_FOR_nothing) - { - expand_twoval_binop_convert (binoptab, wider_mode, op0, op1, - targ0, targ1, unsignedp); - return 1; - } - } - } - return 0; -} - -int -expand_twoval_binop_convert (binoptab, mode, op0, op1, targ0, targ1, unsignedp) - register optab binoptab; - register rtx op0, op1, targ0, targ1; - int unsignedp; -{ - register rtx t0 = gen_reg_rtx (SImode); - register rtx t1 = gen_reg_rtx (SImode); - register rtx temp; - - temp = gen_reg_rtx (SImode); - convert_move (temp, op0, unsignedp); - op0 = temp; - temp = gen_reg_rtx (SImode); - convert_move (temp, op1, unsignedp); - op1 = temp; - - expand_twoval_binop (binoptab, op0, op1, t0, t1, unsignedp); - convert_move (targ0, t0, unsignedp); - convert_move (targ1, t1, unsignedp); - return 1; -} - -/* Generate code to perform an operation specified by UNOPTAB - on operand OP0, with result having machine-mode MODE. - - UNSIGNEDP is for the case where we have to widen the operands - to perform the operation. It says to use zero-extension. - - If TARGET is nonzero, the value - is generated there, if it is convenient to do so. - In all cases an rtx is returned for the locus of the value; - this may or may not be TARGET. */ - -rtx -expand_unop (mode, unoptab, op0, target, unsignedp) - enum machine_mode mode; - optab unoptab; - rtx op0; - rtx target; - int unsignedp; -{ - enum mode_class class; - enum machine_mode wider_mode; - register rtx temp; - - class = GET_MODE_CLASS (mode); - - op0 = protect_from_queue (op0, 0); - - if (flag_force_mem) - { - op0 = force_not_mem (op0); - } - - if (target) - target = protect_from_queue (target, 1); - - if (unoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing) - { - int icode = (int) unoptab->handlers[(int) mode].insn_code; - enum machine_mode mode0 = insn_operand_mode[icode][1]; - - if (target) - temp = target; - else - temp = gen_reg_rtx (mode); - - if (GET_MODE (op0) != VOIDmode - && GET_MODE (op0) != mode0) - op0 = convert_to_mode (mode0, op0, unsignedp); - - /* Now, if insn requires register operands, put operands into regs. */ - - if (! (*insn_operand_predicate[icode][1]) (op0, mode0)) - op0 = force_reg (mode0, op0); - - if (! (*insn_operand_predicate[icode][0]) (temp, mode)) - temp = gen_reg_rtx (mode); - - emit_insn (GEN_FCN (icode) (temp, op0)); - return temp; - } - else if (unoptab->handlers[(int) mode].lib_call) - { - rtx insn_before, insn_last; - rtx funexp = gen_rtx (SYMBOL_REF, Pmode, - unoptab->handlers[(int) mode].lib_call); - - /* Pass the address through a pseudoreg, if desired, - before the "beginning" of the library call (for deletion). */ -#ifndef NO_FUNCTION_CSE - if (! flag_no_function_cse) - funexp = copy_to_mode_reg (Pmode, funexp); -#endif - - insn_before = get_last_insn (); - - /* Cannot pass FUNEXP since emit_library_call insists - on getting a SYMBOL_REF. But cse will make this SYMBOL_REF - be replaced with the copy we made just above. */ - /* Pass 1 for NO_QUEUE so we don't lose any increments - if the libcall is cse'd or moved. */ - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, - unoptab->handlers[(int) mode].lib_call), - 1, mode, 1, op0, mode); - target = hard_libcall_value (mode); - temp = copy_to_reg (target); - insn_last = get_last_insn (); - REG_NOTES (insn_last) - = gen_rtx (EXPR_LIST, REG_EQUAL, - gen_rtx (unoptab->code, mode, op0), - gen_rtx (INSN_LIST, REG_RETVAL, - NEXT_INSN (insn_before), - REG_NOTES (insn_last))); - REG_NOTES (NEXT_INSN (insn_before)) - = gen_rtx (INSN_LIST, REG_LIBCALL, insn_last, - REG_NOTES (NEXT_INSN (insn_before))); - return temp; - } - - /* It can't be done in this mode. Can we do it in a wider mode? */ - - if (class == MODE_INT || class == MODE_FLOAT) - { - for (wider_mode = INC_MODE (mode); - ((int) wider_mode < (int) MAX_MACHINE_MODE - && GET_MODE_CLASS (wider_mode) == class); - wider_mode = INC_MODE (wider_mode)) - { - if ((unoptab->handlers[(int) wider_mode].insn_code - != CODE_FOR_nothing) - || unoptab->handlers[(int) wider_mode].lib_call) - { - if (GET_MODE (op0) != VOIDmode) - { - temp = gen_reg_rtx (wider_mode); - convert_move (temp, op0, unsignedp); - op0 = temp; - } - - target = expand_unop (wider_mode, unoptab, op0, 0, unsignedp); - if (class == MODE_FLOAT) - { - if (target == 0) - target = gen_reg_rtx (mode); - convert_move (target, temp, 0); - return target; - } - else - return gen_lowpart (mode, target); - } - } - } - - return 0; -} - -/* Generate an instruction whose insn-code is INSN_CODE, - with two operands: an output TARGET and an input OP0. - TARGET *must* be nonzero, and the output is always stored there. - CODE is an rtx code such that (CODE OP0) is an rtx that describes - the value that is stored into TARGET. */ - -void -emit_unop_insn (icode, target, op0, code) - int icode; - rtx target; - rtx op0; - enum rtx_code code; -{ - register rtx temp; - enum machine_mode mode0 = insn_operand_mode[icode][1]; - rtx insn; - rtx prev_insn; - - temp = target = protect_from_queue (target, 1); - - op0 = protect_from_queue (op0, 0); - - if (flag_force_mem) - op0 = force_not_mem (op0); - - /* Now, if insn requires register operands, put operands into regs. */ - - if (! (*insn_operand_predicate[icode][1]) (op0, mode0)) - op0 = force_reg (mode0, op0); - - if (! (*insn_operand_predicate[icode][0]) (temp, GET_MODE (temp)) - || (flag_force_mem && GET_CODE (temp) == MEM)) - temp = gen_reg_rtx (GET_MODE (temp)); - - prev_insn = get_last_insn (); - insn = emit_insn (GEN_FCN (icode) (temp, op0)); - - /* If we just made a multi-insn sequence, - record in the last insn an equivalent expression for its value - and a pointer to the first insn. This makes cse possible. */ - if (code != UNKNOWN && PREV_INSN (insn) != prev_insn) - REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_EQUAL, - gen_rtx (code, GET_MODE (temp), op0), - REG_NOTES (insn)); - - if (temp != target) - emit_move_insn (target, temp); -} - -/* Generate code to store zero in X. */ - -void -emit_clr_insn (x) - rtx x; -{ - emit_move_insn (x, const0_rtx); -} - -/* Generate code to store 1 in X - assuming it contains zero beforehand. */ - -void -emit_0_to_1_insn (x) - rtx x; -{ - emit_move_insn (x, const1_rtx); -} - -/* Generate code to compare X with Y - so that the condition codes are set. - - UNSIGNEDP nonzero says that X and Y are unsigned; - this matters if they need to be widened. - - If they have mode BLKmode, then SIZE specifies the size of both X and Y, - and ALIGN specifies the known shared alignment of X and Y. */ - -void -emit_cmp_insn (x, y, size, unsignedp, align) - rtx x, y; - rtx size; - int unsignedp; - int align; -{ - enum machine_mode mode = GET_MODE (x); - enum mode_class class; - enum machine_mode wider_mode; - - if (mode == VOIDmode) mode = GET_MODE (y); - /* They could both be VOIDmode if both args are immediate constants, - but we should fold that at an earlier stage. - With no special code here, this will call abort, - reminding the programmer to implement such folding. */ - - class = GET_MODE_CLASS (mode); - - if (mode != BLKmode && flag_force_mem) - { - x = force_not_mem (x); - y = force_not_mem (y); - } - - /* Handle all BLKmode compares. */ - - if (mode == BLKmode) - { - emit_queue (); - x = protect_from_queue (x, 0); - y = protect_from_queue (y, 0); - - if (size == 0) - abort (); -#ifdef HAVE_cmpstrqi - if (HAVE_cmpstrqi - && GET_CODE (size) == CONST_INT - && INTVAL (size) < (1 << GET_MODE_BITSIZE (QImode))) - emit_insn (gen_cmpstrqi (x, y, size, - gen_rtx (CONST_INT, VOIDmode, align))); - else -#endif -#ifdef HAVE_cmpstrhi - if (HAVE_cmpstrhi - && GET_CODE (size) == CONST_INT - && INTVAL (size) < (1 << GET_MODE_BITSIZE (HImode))) - emit_insn (gen_cmpstrhi (x, y, size, - gen_rtx (CONST_INT, VOIDmode, align))); - else -#endif -#ifdef HAVE_cmpstrsi - if (HAVE_cmpstrsi) - emit_insn (gen_cmpstrsi (x, y, convert_to_mode (SImode, size, 1), - gen_rtx (CONST_INT, VOIDmode, align))); - else -#endif - { -#ifdef TARGET_MEM_FUNCTIONS - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcmp"), 0, - SImode, 3, - XEXP (x, 0), Pmode, XEXP (y, 0), Pmode, - size, Pmode); -#else - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "bcmp"), 0, - SImode, 3, - XEXP (x, 0), Pmode, XEXP (y, 0), Pmode, - size, Pmode); -#endif - emit_cmp_insn (hard_libcall_value (SImode), const0_rtx, 0, 0, 0); - } - return; - } - - /* Handle some compares against zero. */ - - if (y == CONST0_RTX (mode) - && tst_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) - { - int icode = (int) tst_optab->handlers[(int) mode].insn_code; - - emit_queue (); - x = protect_from_queue (x, 0); - y = protect_from_queue (y, 0); - - /* Now, if insn requires register operands, put operands into regs. */ - if (! (*insn_operand_predicate[icode][0]) - (x, insn_operand_mode[icode][0])) - x = force_reg (insn_operand_mode[icode][0], x); - - emit_insn (GEN_FCN (icode) (x)); - return; - } - - /* Handle compares for which there is a directly suitable insn. */ - - if (cmp_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) - { - int icode = (int) cmp_optab->handlers[(int) mode].insn_code; - - emit_queue (); - x = protect_from_queue (x, 0); - y = protect_from_queue (y, 0); - - /* Now, if insn requires register operands, put operands into regs. */ - if (! (*insn_operand_predicate[icode][0]) - (x, insn_operand_mode[icode][0])) - x = force_reg (insn_operand_mode[icode][0], x); - - if (! (*insn_operand_predicate[icode][1]) - (y, insn_operand_mode[icode][1])) - y = force_reg (insn_operand_mode[icode][1], y); - - emit_insn (GEN_FCN (icode) (x, y)); - return; - } - - /* Try widening if we can find a direct insn that way. */ - - if (class == MODE_INT || class == MODE_FLOAT) - { - for (wider_mode = INC_MODE (mode); - ((int) wider_mode < (int) MAX_MACHINE_MODE - && GET_MODE_CLASS (wider_mode) == class); - wider_mode = INC_MODE (wider_mode)) - { - if (cmp_optab->handlers[(int) wider_mode].insn_code - != CODE_FOR_nothing) - { - x = convert_to_mode (wider_mode, x, unsignedp); - y = convert_to_mode (wider_mode, y, unsignedp); - emit_cmp_insn (x, y, 0, unsignedp, align); - return; - } - } - } - - /* Handle a lib call just for the mode we are using. */ - - if (cmp_optab->handlers[(int) mode].lib_call) - { - char *string = cmp_optab->handlers[(int) mode].lib_call; - /* If we want unsigned, and this mode has a distinct unsigned - comparison routine, use that. */ - if (unsignedp && ucmp_optab->handlers[(int) mode].lib_call) - string = ucmp_optab->handlers[(int) mode].lib_call; - - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, string), 0, - SImode, 2, x, mode, y, mode); - - /* Integer comparison returns a result that must be compared against 1, - so that even if we do an unsigned compare afterward, - there is still a value that can represent the result "less than". */ - if (GET_MODE_CLASS (mode) == MODE_INT) - emit_cmp_insn (hard_libcall_value (SImode), const1_rtx, 0, unsignedp, 0); - else - emit_cmp_insn (hard_libcall_value (SImode), const0_rtx, 0, 0, 0); - return; - } - - /* Try widening and then using a libcall. */ - - if (class == MODE_FLOAT) - { - for (wider_mode = INC_MODE (mode); - ((int) wider_mode < (int) MAX_MACHINE_MODE - && GET_MODE_CLASS (wider_mode) == class); - wider_mode = INC_MODE (wider_mode)) - { - if ((cmp_optab->handlers[(int) wider_mode].insn_code - != CODE_FOR_nothing) - || (cmp_optab->handlers[(int) wider_mode].lib_call != 0)) - { - x = convert_to_mode (wider_mode, x, unsignedp); - y = convert_to_mode (wider_mode, y, unsignedp); - emit_cmp_insn (x, y, 0, unsignedp, align); - } - } - return; - } - - abort (); -} - -/* These three functions generate an insn body and return it - rather than emitting the insn. - - They do not protect from queued increments, - because they may be used 1) in protect_from_queue itself - and 2) in other passes where there is no queue. */ - -/* Generate and return an insn body to add Y to X. */ - -rtx -gen_add2_insn (x, y) - rtx x, y; -{ - return (GEN_FCN (add_optab->handlers[(int) GET_MODE (x)].insn_code) - (x, x, y)); -} - -int -have_add2_insn (mode) - enum machine_mode mode; -{ - return add_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing; -} - -/* Generate and return an insn body to subtract Y from X. */ - -rtx -gen_sub2_insn (x, y) - rtx x, y; -{ - return (GEN_FCN (sub_optab->handlers[(int) GET_MODE (x)].insn_code) - (x, x, y)); -} - -int -have_sub2_insn (mode) - enum machine_mode mode; -{ - return add_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing; -} - -/* Generate the body of an instruction to copy Y into X. */ - -rtx -gen_move_insn (x, y) - rtx x, y; -{ - register enum machine_mode mode = GET_MODE (x); - if (mode == VOIDmode) - mode = GET_MODE (y); - return (GEN_FCN (mov_optab->handlers[(int) mode].insn_code) (x, y)); -} - -#if 0 -/* Tables of patterns for extending one integer mode to another. */ -enum insn_code zero_extend_optab[MAX_MACHINE_MODE][MAX_MACHINE_MODE]; -enum insn_code sign_extend_optab[MAX_MACHINE_MODE][MAX_MACHINE_MODE]; - -/* Generate the body of an insn to extend Y (with mode MFROM) - into X (with mode MTO). Do zero-extension if UNSIGNEDP is nonzero. */ - -rtx -gen_extend_insn (x, y, mto, mfrom, unsignedp) - rtx x, y; - enum machine_mode mto, mfrom; - int unsignedp; -{ - return (GEN_FCN ((unsignedp ? zero_extend_optab : sign_extend_optab) - [(int)mto][(int)mfrom]) - (x, y)); -} - -static void -init_extends () -{ - bzero (sign_extend_optab, sizeof sign_extend_optab); - bzero (zero_extend_optab, sizeof zero_extend_optab); - sign_extend_optab[(int) SImode][(int) HImode] = CODE_FOR_extendhisi2; - sign_extend_optab[(int) SImode][(int) QImode] = CODE_FOR_extendqisi2; - sign_extend_optab[(int) HImode][(int) QImode] = CODE_FOR_extendqihi2; - zero_extend_optab[(int) SImode][(int) HImode] = CODE_FOR_zero_extendhisi2; - zero_extend_optab[(int) SImode][(int) QImode] = CODE_FOR_zero_extendqisi2; - zero_extend_optab[(int) HImode][(int) QImode] = CODE_FOR_zero_extendqihi2; -} -#endif - -/* can_fix_p and can_float_p say whether the target machine - can directly convert a given fixed point type to - a given floating point type, or vice versa. - The returned value is the CODE_FOR_... value to use, - or CODE_FOR_nothing if these modes cannot be directly converted. */ - -static enum insn_code fixtab[2][2][2]; -static enum insn_code fixtrunctab[2][2][2]; -static enum insn_code floattab[2][2]; - -/* *TRUNCP_PTR is set to 1 if it is necessary to output - an explicit FTRUNC insn before the fix insn; otherwise 0. */ - -static enum insn_code -can_fix_p (fixmode, fltmode, unsignedp, truncp_ptr) - enum machine_mode fltmode, fixmode; - int unsignedp; - int *truncp_ptr; -{ - *truncp_ptr = 0; - if (fixtrunctab[fltmode != SFmode][fixmode == DImode][unsignedp] - != CODE_FOR_nothing) - return fixtrunctab[fltmode != SFmode][fixmode == DImode][unsignedp]; - if (ftrunc_optab->handlers[(int) fltmode].insn_code != CODE_FOR_nothing) - { - *truncp_ptr = 1; - return fixtab[fltmode != SFmode][fixmode == DImode][unsignedp]; - } - return CODE_FOR_nothing; -} - -static enum insn_code -can_float_p (fltmode, fixmode) - enum machine_mode fixmode, fltmode; -{ - return floattab[fltmode != SFmode][fixmode == DImode]; -} - -void -init_fixtab () -{ - enum insn_code *p; - for (p = fixtab[0][0]; - p < fixtab[0][0] + sizeof fixtab / sizeof (fixtab[0][0][0]); - p++) - *p = CODE_FOR_nothing; - for (p = fixtrunctab[0][0]; - p < fixtrunctab[0][0] + sizeof fixtrunctab / sizeof (fixtrunctab[0][0][0]); - p++) - *p = CODE_FOR_nothing; - -#ifdef HAVE_fixsfsi2 - if (HAVE_fixsfsi2) - fixtab[0][0][0] = CODE_FOR_fixsfsi2; -#endif -#ifdef HAVE_fixsfdi2 - if (HAVE_fixsfdi2) - fixtab[0][1][0] = CODE_FOR_fixsfdi2; -#endif -#ifdef HAVE_fixdfsi2 - if (HAVE_fixdfsi2) - fixtab[1][0][0] = CODE_FOR_fixdfsi2; -#endif -#ifdef HAVE_fixdfdi2 - if (HAVE_fixdfdi2) - fixtab[1][1][0] = CODE_FOR_fixdfdi2; -#endif - -#ifdef HAVE_fixunssfsi2 - if (HAVE_fixunssfsi2) - fixtab[0][0][1] = CODE_FOR_fixunssfsi2; -#endif -#ifdef HAVE_fixunssfdi2 - if (HAVE_fixunssfdi2) - fixtab[0][1][1] = CODE_FOR_fixunssfdi2; -#endif -#ifdef HAVE_fixunsdfsi2 - if (HAVE_fixunsdfsi2) - fixtab[1][0][1] = CODE_FOR_fixunsdfsi2; -#endif -#ifdef HAVE_fixunsdfdi2 - if (HAVE_fixunsdfdi2) - fixtab[1][1][1] = CODE_FOR_fixunsdfdi2; -#endif - -#ifdef HAVE_fix_truncsfsi2 - if (HAVE_fix_truncsfsi2) - fixtrunctab[0][0][0] = CODE_FOR_fix_truncsfsi2; -#endif -#ifdef HAVE_fix_truncsfdi2 - if (HAVE_fix_truncsfdi2) - fixtrunctab[0][1][0] = CODE_FOR_fix_truncsfdi2; -#endif -#ifdef HAVE_fix_truncdfsi2 - if (HAVE_fix_truncdfsi2) - fixtrunctab[1][0][0] = CODE_FOR_fix_truncdfsi2; -#endif -#ifdef HAVE_fix_truncdfdi2 - if (HAVE_fix_truncdfdi2) - fixtrunctab[1][1][0] = CODE_FOR_fix_truncdfdi2; -#endif - -#ifdef HAVE_fixuns_truncsfsi2 - if (HAVE_fixuns_truncsfsi2) - fixtrunctab[0][0][1] = CODE_FOR_fixuns_truncsfsi2; -#endif -#ifdef HAVE_fixuns_truncsfdi2 - if (HAVE_fixuns_truncsfdi2) - fixtrunctab[0][1][1] = CODE_FOR_fixuns_truncsfdi2; -#endif -#ifdef HAVE_fixuns_truncdfsi2 - if (HAVE_fixuns_truncdfsi2) - fixtrunctab[1][0][1] = CODE_FOR_fixuns_truncdfsi2; -#endif -#ifdef HAVE_fixuns_truncdfdi2 - if (HAVE_fixuns_truncdfdi2) - fixtrunctab[1][1][1] = CODE_FOR_fixuns_truncdfdi2; -#endif - -#ifdef FIXUNS_TRUNC_LIKE_FIX_TRUNC - /* This flag says the same insns that convert to a signed fixnum - also convert validly to an unsigned one. */ - { - int i; - int j; - for (i = 0; i < 2; i++) - for (j = 0; j < 2; j++) - fixtrunctab[i][j][1] = fixtrunctab[i][j][0]; - } -#endif -} - -void -init_floattab () -{ - enum insn_code *p; - for (p = floattab[0]; - p < floattab[0] + sizeof floattab / sizeof (floattab[0][0]); - p++) - *p = CODE_FOR_nothing; - -#ifdef HAVE_floatsisf2 - if (HAVE_floatsisf2) - floattab[0][0] = CODE_FOR_floatsisf2; -#endif -#ifdef HAVE_floatdisf2 - if (HAVE_floatdisf2) - floattab[0][1] = CODE_FOR_floatdisf2; -#endif -#ifdef HAVE_floatsidf2 - if (HAVE_floatsidf2) - floattab[1][0] = CODE_FOR_floatsidf2; -#endif -#ifdef HAVE_floatdidf2 - if (HAVE_floatdidf2) - floattab[1][1] = CODE_FOR_floatdidf2; -#endif -} - -/* Generate code to convert FROM to floating point - and store in TO. FROM must be fixed point. - UNSIGNEDP nonzero means regard FROM as unsigned. - Normally this is done by correcting the final value - if it is negative. */ - -void -expand_float (real_to, from, unsignedp) - rtx real_to, from; - int unsignedp; -{ - enum insn_code icode; - register rtx to; - - /* Constants should get converted in `fold'. - We lose here since we don't know the mode. */ - if (GET_MODE (from) == VOIDmode) - abort (); - - to = real_to = protect_from_queue (real_to, 1); - from = protect_from_queue (from, 0); - - if (flag_force_mem) - { - from = force_not_mem (from); - } - - /* If we are about to do some arithmetic to correct for an - unsigned operand, do it in a pseudo-register. */ - - if (unsignedp - && ! (GET_CODE (to) == REG && REGNO (to) >= FIRST_PSEUDO_REGISTER)) - to = gen_reg_rtx (GET_MODE (to)); - - /* Now do the basic conversion. Do it in the specified modes if possible; - otherwise convert either input, output or both with wider mode; - otherwise use a library call. */ - - if ((icode = can_float_p (GET_MODE (to), GET_MODE (from))) - != CODE_FOR_nothing) - { - emit_unop_insn (icode, to, from, FLOAT); - } - else if (GET_MODE (to) == SFmode - && ((icode = can_float_p (DFmode, GET_MODE (from))) - != CODE_FOR_nothing)) - { - to = gen_reg_rtx (DFmode); - emit_unop_insn (icode, to, from, FLOAT); - } - /* If we can't float a SI, maybe we can float a DI. - If so, convert to DI and then float. */ - else if (GET_MODE (from) != DImode - && (can_float_p (GET_MODE (to), DImode) != CODE_FOR_nothing - || can_float_p (DFmode, DImode) != CODE_FOR_nothing)) - { - register rtx tem = gen_reg_rtx (DImode); - convert_move (tem, from, unsignedp); - from = tem; - /* If we extend FROM then we don't need to correct - the final value for unsignedness. */ - unsignedp = 0; - - if ((icode = can_float_p (GET_MODE (to), GET_MODE (from))) - != CODE_FOR_nothing) - { - emit_unop_insn (icode, to, from, FLOAT); - } - else if ((icode = can_float_p (DFmode, DImode)) - != CODE_FOR_nothing) - { - to = gen_reg_rtx (DFmode); - emit_unop_insn (icode, to, from, FLOAT); - } - } - /* No hardware instruction available; call a library - to convert from SImode or DImode into DFmode. */ - else - { - if (GET_MODE_SIZE (GET_MODE (from)) < GET_MODE_SIZE (SImode)) - { - from = convert_to_mode (SImode, from, unsignedp); - unsignedp = 0; - } - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, - (GET_MODE (from) == SImode ? "__floatsidf" - : "__floatdidf")), - 0, DFmode, 1, from, GET_MODE (from)); - to = copy_to_reg (hard_libcall_value (DFmode)); - } - - /* If FROM was unsigned but we treated it as signed, - then in the case where it is negative (and therefore TO is negative), - correct its value by 2**bitwidth. */ - - if (unsignedp) - { - rtx label = gen_label_rtx (); - rtx temp; - REAL_VALUE_TYPE offset; - - do_pending_stack_adjust (); - emit_cmp_insn (to, GET_MODE (to) == DFmode ? dconst0_rtx : fconst0_rtx, - 0, 0, 0); - emit_jump_insn (gen_bge (label)); - offset = REAL_VALUE_LDEXP (1.0, GET_MODE_BITSIZE (GET_MODE (from))); - temp = expand_binop (GET_MODE (to), add_optab, to, - immed_real_const_1 (offset, GET_MODE (to)), - to, 0, OPTAB_LIB_WIDEN); - if (temp != to) - emit_move_insn (to, temp); - do_pending_stack_adjust (); - emit_label (label); - } - - /* Copy result to requested destination - if we have been computing in a temp location. */ - - if (to != real_to) - { - if (GET_MODE (real_to) == GET_MODE (to)) - emit_move_insn (real_to, to); - else - convert_move (real_to, to, 0); - } -} - -/* expand_fix: generate code to convert FROM to fixed point - and store in TO. FROM must be floating point. */ - -static rtx -ftruncify (x) - rtx x; -{ - rtx temp = gen_reg_rtx (GET_MODE (x)); - return expand_unop (GET_MODE (x), ftrunc_optab, x, temp, 0); -} - -void -expand_fix (to, from, unsignedp) - register rtx to, from; - int unsignedp; -{ - enum insn_code icode; - register rtx target; - int must_trunc = 0; - - while (1) - { - icode = can_fix_p (GET_MODE (to), GET_MODE (from), unsignedp, &must_trunc); - if (icode != CODE_FOR_nothing) - { - if (must_trunc) - from = ftruncify (from); - - emit_unop_insn (icode, to, from, FIX); - return; - } - -#if 0 /* Turned off. It fails because the positive numbers - that become temporarily negative are rounded up instead of down. */ - - /* If no insns for unsigned conversion, - we can go via a signed number. - But make sure we won't overflow in the compiler. */ - if (unsignedp && GET_MODE_BITSIZE (GET_MODE (to)) <= HOST_BITS_PER_INT - /* Make sure we won't lose significant bits doing this. */ - && GET_MODE_BITSIZE (GET_MODE (from)) > GET_MODE_BITSIZE (GET_MODE (to))) - { - icode = can_fix_p (GET_MODE (to), GET_MODE (from), - 0, &must_trunc); - - if (icode != CODE_FOR_nothing) - { - REAL_VALUE_TYPE offset; - rtx temp, temp1; - int bitsize = GET_MODE_BITSIZE (GET_MODE (to)); - - if (must_trunc) - from = ftruncify (from); - - /* Subtract 2**(N-1), convert to signed number, - then add 2**(N-1). */ - offset = REAL_VALUE_LDEXP (1.0, bitsize - 1); - temp = expand_binop (GET_MODE (from), sub_optab, from, - immed_real_const_1 (offset, GET_MODE (from)), - 0, 0, OPTAB_LIB_WIDEN); - - temp1 = gen_reg_rtx (GET_MODE (to)); - emit_unop_insn (icode, temp1, temp, FIX); - temp = expand_binop (GET_MODE (to), add_optab, temp1, - gen_rtx (CONST_INT, VOIDmode, - 1 << (bitsize - 1)), - to, 1, OPTAB_LIB_WIDEN); - if (temp != to) - emit_move_insn (to, temp); - return; - } - } -#endif - icode = can_fix_p (DImode, GET_MODE (from), unsignedp, &must_trunc); - - if (GET_MODE (to) != DImode && icode != CODE_FOR_nothing) - { - register rtx temp = gen_reg_rtx (DImode); - - if (must_trunc) - from = ftruncify (from); - emit_unop_insn (icode, temp, from, FIX); - convert_move (to, temp, unsignedp); - return; - } - - /* If FROM is not DFmode, convert to DFmode and try again from there. */ - if (GET_MODE (from) == DFmode) - break; - - from = convert_to_mode (DFmode, from, 0); - } - - /* We can't do it with an insn, so use a library call. - The mode of FROM is known to be DFmode. */ - - to = protect_from_queue (to, 1); - from = protect_from_queue (from, 0); - - if (flag_force_mem) - from = force_not_mem (from); - - if (GET_MODE (to) != DImode) - { - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, - unsignedp ? "__fixunsdfsi" - : "__fixdfsi"), - 0, SImode, 1, from, DFmode); - target = hard_libcall_value (SImode); - } - else - { - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, - unsignedp ? "__fixunsdfdi" - : "__fixdfdi"), - 0, DImode, 1, from, DFmode); - target = hard_libcall_value (DImode); - } - - if (GET_MODE (to) == GET_MODE (target)) - emit_move_insn (to, target); - else - convert_move (to, target, 0); -} - -static optab -init_optab (code) - enum rtx_code code; -{ - int i; - optab op = (optab) malloc (sizeof (struct optab)); - op->code = code; - for (i = 0; i < NUM_MACHINE_MODES; i++) - { - op->handlers[i].insn_code = CODE_FOR_nothing; - op->handlers[i].lib_call = 0; - } - return op; -} - -/* Call this once to initialize the contents of the optabs - appropriately for the current target machine. */ - -void -init_optabs () -{ - init_fixtab (); - init_floattab (); - init_comparisons (); -/* init_extends (); */ - - add_optab = init_optab (PLUS); - sub_optab = init_optab (MINUS); - smul_optab = init_optab (MULT); - umul_optab = init_optab (UMULT); - smul_widen_optab = init_optab (MULT); - umul_widen_optab = init_optab (UMULT); - sdiv_optab = init_optab (DIV); - sdivmod_optab = init_optab (UNKNOWN); - udiv_optab = init_optab (UDIV); - udivmod_optab = init_optab (UNKNOWN); - smod_optab = init_optab (MOD); - umod_optab = init_optab (UMOD); - flodiv_optab = init_optab (DIV); - ftrunc_optab = init_optab (UNKNOWN); - and_optab = init_optab (AND); - andcb_optab = init_optab (UNKNOWN); - ior_optab = init_optab (IOR); - xor_optab = init_optab (XOR); - ashl_optab = init_optab (ASHIFT); - ashr_optab = init_optab (ASHIFTRT); - lshl_optab = init_optab (LSHIFT); - lshr_optab = init_optab (LSHIFTRT); - rotl_optab = init_optab (ROTATE); - rotr_optab = init_optab (ROTATERT); - mov_optab = init_optab (UNKNOWN); - movstrict_optab = init_optab (UNKNOWN); - cmp_optab = init_optab (UNKNOWN); - ucmp_optab = init_optab (UNKNOWN); - tst_optab = init_optab (UNKNOWN); - neg_optab = init_optab (NEG); - abs_optab = init_optab (ABS); - one_cmpl_optab = init_optab (NOT); - ffs_optab = init_optab (FFS); - -#ifdef HAVE_addqi3 - if (HAVE_addqi3) - add_optab->handlers[(int) QImode].insn_code = CODE_FOR_addqi3; -#endif -#ifdef HAVE_addhi3 - if (HAVE_addhi3) - add_optab->handlers[(int) HImode].insn_code = CODE_FOR_addhi3; -#endif -#ifdef HAVE_addsi3 - if (HAVE_addsi3) - add_optab->handlers[(int) SImode].insn_code = CODE_FOR_addsi3; -#endif -#ifdef HAVE_adddi3 - if (HAVE_adddi3) - add_optab->handlers[(int) DImode].insn_code = CODE_FOR_adddi3; -#endif -#ifdef HAVE_addsf3 - if (HAVE_addsf3) - add_optab->handlers[(int) SFmode].insn_code = CODE_FOR_addsf3; -#endif -#ifdef HAVE_adddf3 - if (HAVE_adddf3) - add_optab->handlers[(int) DFmode].insn_code = CODE_FOR_adddf3; -#endif - add_optab->handlers[(int) DImode].lib_call = "__adddi3"; - add_optab->handlers[(int) SFmode].lib_call = "__addsf3"; - add_optab->handlers[(int) DFmode].lib_call = "__adddf3"; - -#ifdef HAVE_subqi3 - if (HAVE_subqi3) - sub_optab->handlers[(int) QImode].insn_code = CODE_FOR_subqi3; -#endif -#ifdef HAVE_subhi3 - if (HAVE_subhi3) - sub_optab->handlers[(int) HImode].insn_code = CODE_FOR_subhi3; -#endif -#ifdef HAVE_subsi3 - if (HAVE_subsi3) - sub_optab->handlers[(int) SImode].insn_code = CODE_FOR_subsi3; -#endif -#ifdef HAVE_subdi3 - if (HAVE_subdi3) - sub_optab->handlers[(int) DImode].insn_code = CODE_FOR_subdi3; -#endif -#ifdef HAVE_subsf3 - if (HAVE_subsf3) - sub_optab->handlers[(int) SFmode].insn_code = CODE_FOR_subsf3; -#endif -#ifdef HAVE_subdf3 - if (HAVE_subdf3) - sub_optab->handlers[(int) DFmode].insn_code = CODE_FOR_subdf3; -#endif - sub_optab->handlers[(int) DImode].lib_call = "__subdi3"; - sub_optab->handlers[(int) SFmode].lib_call = "__subsf3"; - sub_optab->handlers[(int) DFmode].lib_call = "__subdf3"; - -#ifdef HAVE_mulqi3 - if (HAVE_mulqi3) - smul_optab->handlers[(int) QImode].insn_code = CODE_FOR_mulqi3; -#endif -#ifdef HAVE_mulhi3 - if (HAVE_mulhi3) - smul_optab->handlers[(int) HImode].insn_code = CODE_FOR_mulhi3; -#endif -#ifdef HAVE_mulsi3 - if (HAVE_mulsi3) - smul_optab->handlers[(int) SImode].insn_code = CODE_FOR_mulsi3; -#endif -#ifdef HAVE_muldi3 - if (HAVE_muldi3) - smul_optab->handlers[(int) DImode].insn_code = CODE_FOR_muldi3; -#endif -#ifdef HAVE_mulsf3 - if (HAVE_mulsf3) - smul_optab->handlers[(int) SFmode].insn_code = CODE_FOR_mulsf3; -#endif -#ifdef HAVE_muldf3 - if (HAVE_muldf3) - smul_optab->handlers[(int) DFmode].insn_code = CODE_FOR_muldf3; -#endif - -#ifdef MULSI3_LIBCALL - smul_optab->handlers[(int) SImode].lib_call = MULSI3_LIBCALL; -#else - smul_optab->handlers[(int) SImode].lib_call = "__mulsi3"; -#endif - smul_optab->handlers[(int) DImode].lib_call = "__muldi3"; - smul_optab->handlers[(int) SFmode].lib_call = "__mulsf3"; - smul_optab->handlers[(int) DFmode].lib_call = "__muldf3"; - -#ifdef HAVE_mulqihi3 - if (HAVE_mulqihi3) - smul_widen_optab->handlers[(int) HImode].insn_code = CODE_FOR_mulqihi3; -#endif -#ifdef HAVE_mulhisi3 - if (HAVE_mulhisi3) - smul_widen_optab->handlers[(int) SImode].insn_code = CODE_FOR_mulhisi3; -#endif -#ifdef HAVE_mulsidi3 - if (HAVE_mulsidi3) - smul_widen_optab->handlers[(int) DImode].insn_code = CODE_FOR_mulsidi3; -#endif - -#ifdef HAVE_umulqi3 - if (HAVE_umulqi3) - umul_optab->handlers[(int) QImode].insn_code = CODE_FOR_umulqi3; -#endif -#ifdef HAVE_umulhi3 - if (HAVE_umulhi3) - umul_optab->handlers[(int) HImode].insn_code = CODE_FOR_umulhi3; -#endif -#ifdef HAVE_umulsi3 - if (HAVE_umulsi3) - umul_optab->handlers[(int) SImode].insn_code = CODE_FOR_umulsi3; -#endif -#ifdef HAVE_umuldi3 - if (HAVE_umuldi3) - umul_optab->handlers[(int) DImode].insn_code = CODE_FOR_umuldi3; -#endif -#ifdef HAVE_umulsf3 - if (HAVE_umulsf3) - umul_optab->handlers[(int) SFmode].insn_code = CODE_FOR_umulsf3; -#endif -#ifdef HAVE_umuldf3 - if (HAVE_umuldf3) - umul_optab->handlers[(int) DFmode].insn_code = CODE_FOR_umuldf3; -#endif - -#ifdef UMULSI3_LIBCALL - umul_optab->handlers[(int) SImode].lib_call = UMULSI3_LIBCALL; -#else - umul_optab->handlers[(int) SImode].lib_call = "__umulsi3"; -#endif - umul_optab->handlers[(int) DImode].lib_call = "__umuldi3"; - umul_optab->handlers[(int) SFmode].lib_call = "__umulsf3"; - umul_optab->handlers[(int) DFmode].lib_call = "__umuldf3"; - -#ifdef HAVE_umulqihi3 - if (HAVE_umulqihi3) - umul_widen_optab->handlers[(int) HImode].insn_code = CODE_FOR_umulqihi3; -#endif -#ifdef HAVE_umulhisi3 - if (HAVE_umulhisi3) - umul_widen_optab->handlers[(int) SImode].insn_code = CODE_FOR_umulhisi3; -#endif -#ifdef HAVE_umulsidi3 - if (HAVE_umulsidi3) - umul_widen_optab->handlers[(int) DImode].insn_code = CODE_FOR_umulsidi3; -#endif - -#ifdef HAVE_divqi3 - if (HAVE_divqi3) - sdiv_optab->handlers[(int) QImode].insn_code = CODE_FOR_divqi3; -#endif -#ifdef HAVE_divhi3 - if (HAVE_divhi3) - sdiv_optab->handlers[(int) HImode].insn_code = CODE_FOR_divhi3; -#endif -#ifdef HAVE_divsi3 - if (HAVE_divsi3) - sdiv_optab->handlers[(int) SImode].insn_code = CODE_FOR_divsi3; -#endif -#ifdef HAVE_divdi3 - if (HAVE_divdi3) - sdiv_optab->handlers[(int) DImode].insn_code = CODE_FOR_divdi3; -#endif - -#ifdef DIVSI3_LIBCALL - sdiv_optab->handlers[(int) SImode].lib_call = DIVSI3_LIBCALL; -#else - sdiv_optab->handlers[(int) SImode].lib_call = "__divsi3"; -#endif - sdiv_optab->handlers[(int) DImode].lib_call = "__divdi3"; - -#ifdef HAVE_udivqi3 - if (HAVE_udivqi3) - udiv_optab->handlers[(int) QImode].insn_code = CODE_FOR_udivqi3; -#endif -#ifdef HAVE_udivhi3 - if (HAVE_udivhi3) - udiv_optab->handlers[(int) HImode].insn_code = CODE_FOR_udivhi3; -#endif -#ifdef HAVE_udivsi3 - if (HAVE_udivsi3) - udiv_optab->handlers[(int) SImode].insn_code = CODE_FOR_udivsi3; -#endif -#ifdef HAVE_udivdi3 - if (HAVE_udivdi3) - udiv_optab->handlers[(int) DImode].insn_code = CODE_FOR_udivdi3; -#endif - -#ifdef UDIVSI3_LIBCALL - udiv_optab->handlers[(int) SImode].lib_call = UDIVSI3_LIBCALL; -#else - udiv_optab->handlers[(int) SImode].lib_call = "__udivsi3"; -#endif - udiv_optab->handlers[(int) DImode].lib_call = "__udivdi3"; - -#ifdef HAVE_divmodqi4 - if (HAVE_divmodqi4) - sdivmod_optab->handlers[(int) QImode].insn_code = CODE_FOR_divmodqi4; -#endif -#ifdef HAVE_divmodhi4 - if (HAVE_divmodhi4) - sdivmod_optab->handlers[(int) HImode].insn_code = CODE_FOR_divmodhi4; -#endif -#ifdef HAVE_divmodsi4 - if (HAVE_divmodsi4) - sdivmod_optab->handlers[(int) SImode].insn_code = CODE_FOR_divmodsi4; -#endif -#ifdef HAVE_divmoddi4 - if (HAVE_divmoddi4) - sdivmod_optab->handlers[(int) DImode].insn_code = CODE_FOR_divmoddi4; -#endif - -#ifdef HAVE_udivmodqi4 - if (HAVE_udivmodqi4) - udivmod_optab->handlers[(int) QImode].insn_code = CODE_FOR_udivmodqi4; -#endif -#ifdef HAVE_udivmodhi4 - if (HAVE_udivmodhi4) - udivmod_optab->handlers[(int) HImode].insn_code = CODE_FOR_udivmodhi4; -#endif -#ifdef HAVE_udivmodsi4 - if (HAVE_udivmodsi4) - udivmod_optab->handlers[(int) SImode].insn_code = CODE_FOR_udivmodsi4; -#endif -#ifdef HAVE_udivmoddi4 - if (HAVE_udivmoddi4) - udivmod_optab->handlers[(int) DImode].insn_code = CODE_FOR_udivmoddi4; -#endif - -#ifdef HAVE_modqi3 - if (HAVE_modqi3) - smod_optab->handlers[(int) QImode].insn_code = CODE_FOR_modqi3; -#endif -#ifdef HAVE_modhi3 - if (HAVE_modhi3) - smod_optab->handlers[(int) HImode].insn_code = CODE_FOR_modhi3; -#endif -#ifdef HAVE_modsi3 - if (HAVE_modsi3) - smod_optab->handlers[(int) SImode].insn_code = CODE_FOR_modsi3; -#endif -#ifdef HAVE_moddi3 - if (HAVE_moddi3) - smod_optab->handlers[(int) DImode].insn_code = CODE_FOR_moddi3; -#endif - -#ifdef MODSI3_LIBCALL - smod_optab->handlers[(int) SImode].lib_call = MODSI3_LIBCALL; -#else - smod_optab->handlers[(int) SImode].lib_call = "__modsi3"; -#endif - smod_optab->handlers[(int) DImode].lib_call = "__moddi3"; - -#ifdef HAVE_umodqi3 - if (HAVE_umodqi3) - umod_optab->handlers[(int) QImode].insn_code = CODE_FOR_umodqi3; -#endif -#ifdef HAVE_umodhi3 - if (HAVE_umodhi3) - umod_optab->handlers[(int) HImode].insn_code = CODE_FOR_umodhi3; -#endif -#ifdef HAVE_umodsi3 - if (HAVE_umodsi3) - umod_optab->handlers[(int) SImode].insn_code = CODE_FOR_umodsi3; -#endif -#ifdef HAVE_umoddi3 - if (HAVE_umoddi3) - umod_optab->handlers[(int) DImode].insn_code = CODE_FOR_umoddi3; -#endif - -#ifdef UMODSI3_LIBCALL - umod_optab->handlers[(int) SImode].lib_call = UMODSI3_LIBCALL; -#else - umod_optab->handlers[(int) SImode].lib_call = "__umodsi3"; -#endif - umod_optab->handlers[(int) DImode].lib_call = "__umoddi3"; - -#ifdef HAVE_divsf3 - if (HAVE_divsf3) - flodiv_optab->handlers[(int) SFmode].insn_code = CODE_FOR_divsf3; -#endif -#ifdef HAVE_divdf3 - if (HAVE_divdf3) - flodiv_optab->handlers[(int) DFmode].insn_code = CODE_FOR_divdf3; -#endif - flodiv_optab->handlers[(int) SFmode].lib_call = "__divsf3"; - flodiv_optab->handlers[(int) DFmode].lib_call = "__divdf3"; - -#ifdef HAVE_ftruncsf2 - if (HAVE_ftruncsf2) - ftrunc_optab->handlers[(int) SFmode].insn_code = CODE_FOR_ftruncsf2; -#endif -#ifdef HAVE_ftruncdf2 - if (HAVE_ftruncdf2) - ftrunc_optab->handlers[(int) DFmode].insn_code = CODE_FOR_ftruncdf2; -#endif - -#ifdef HAVE_andqi3 - if (HAVE_andqi3) - and_optab->handlers[(int) QImode].insn_code = CODE_FOR_andqi3; -#endif -#ifdef HAVE_andhi3 - if (HAVE_andhi3) - and_optab->handlers[(int) HImode].insn_code = CODE_FOR_andhi3; -#endif -#ifdef HAVE_andsi3 - if (HAVE_andsi3) - and_optab->handlers[(int) SImode].insn_code = CODE_FOR_andsi3; -#endif -#ifdef HAVE_anddi3 - if (HAVE_anddi3) - and_optab->handlers[(int) DImode].insn_code = CODE_FOR_anddi3; -#endif - and_optab->handlers[(int) DImode].lib_call = "__anddi3"; - -#ifdef HAVE_andcbqi3 - if (HAVE_andcbqi3) - andcb_optab->handlers[(int) QImode].insn_code = CODE_FOR_andcbqi3; -#endif -#ifdef HAVE_andcbhi3 - if (HAVE_andcbhi3) - andcb_optab->handlers[(int) HImode].insn_code = CODE_FOR_andcbhi3; -#endif -#ifdef HAVE_andcbsi3 - if (HAVE_andcbsi3) - andcb_optab->handlers[(int) SImode].insn_code = CODE_FOR_andcbsi3; -#endif -#ifdef HAVE_andcbdi3 - if (HAVE_andcbdi3) - andcb_optab->handlers[(int) DImode].insn_code = CODE_FOR_andcbdi3; -#endif - andcb_optab->handlers[(int) DImode].lib_call = "__andcbdi3"; - -#ifdef HAVE_iorqi3 - if (HAVE_iorqi3) - ior_optab->handlers[(int) QImode].insn_code = CODE_FOR_iorqi3; -#endif -#ifdef HAVE_iorhi3 - if (HAVE_iorhi3) - ior_optab->handlers[(int) HImode].insn_code = CODE_FOR_iorhi3; -#endif -#ifdef HAVE_iorsi3 - if (HAVE_iorsi3) - ior_optab->handlers[(int) SImode].insn_code = CODE_FOR_iorsi3; -#endif -#ifdef HAVE_iordi3 - if (HAVE_iordi3) - ior_optab->handlers[(int) DImode].insn_code = CODE_FOR_iordi3; -#endif - ior_optab->handlers[(int) DImode].lib_call = "__iordi3"; - -#ifdef HAVE_xorqi3 - if (HAVE_xorqi3) - xor_optab->handlers[(int) QImode].insn_code = CODE_FOR_xorqi3; -#endif -#ifdef HAVE_xorhi3 - if (HAVE_xorhi3) - xor_optab->handlers[(int) HImode].insn_code = CODE_FOR_xorhi3; -#endif -#ifdef HAVE_xorsi3 - if (HAVE_xorsi3) - xor_optab->handlers[(int) SImode].insn_code = CODE_FOR_xorsi3; -#endif -#ifdef HAVE_xordi3 - if (HAVE_xordi3) - xor_optab->handlers[(int) DImode].insn_code = CODE_FOR_xordi3; -#endif - xor_optab->handlers[(int) DImode].lib_call = "__xordi3"; - -#ifdef HAVE_ashlqi3 - if (HAVE_ashlqi3) - ashl_optab->handlers[(int) QImode].insn_code = CODE_FOR_ashlqi3; -#endif -#ifdef HAVE_ashlhi3 - if (HAVE_ashlhi3) - ashl_optab->handlers[(int) HImode].insn_code = CODE_FOR_ashlhi3; -#endif -#ifdef HAVE_ashlsi3 - if (HAVE_ashlsi3) - ashl_optab->handlers[(int) SImode].insn_code = CODE_FOR_ashlsi3; -#endif -#ifdef HAVE_ashldi3 - if (HAVE_ashldi3) - ashl_optab->handlers[(int) DImode].insn_code = CODE_FOR_ashldi3; -#endif - ashl_optab->handlers[(int) SImode].lib_call = "__ashlsi3"; - ashl_optab->handlers[(int) DImode].lib_call = "__ashldi3"; - -#ifdef HAVE_ashrqi3 - if (HAVE_ashrqi3) - ashr_optab->handlers[(int) QImode].insn_code = CODE_FOR_ashrqi3; -#endif -#ifdef HAVE_ashrhi3 - if (HAVE_ashrhi3) - ashr_optab->handlers[(int) HImode].insn_code = CODE_FOR_ashrhi3; -#endif -#ifdef HAVE_ashrsi3 - if (HAVE_ashrsi3) - ashr_optab->handlers[(int) SImode].insn_code = CODE_FOR_ashrsi3; -#endif -#ifdef HAVE_ashrdi3 - if (HAVE_ashrdi3) - ashr_optab->handlers[(int) DImode].insn_code = CODE_FOR_ashrdi3; -#endif - ashr_optab->handlers[(int) SImode].lib_call = "__ashrsi3"; - ashr_optab->handlers[(int) DImode].lib_call = "__ashrdi3"; - -#ifdef HAVE_lshlqi3 - if (HAVE_lshlqi3) - lshl_optab->handlers[(int) QImode].insn_code = CODE_FOR_lshlqi3; -#endif -#ifdef HAVE_lshlhi3 - if (HAVE_lshlhi3) - lshl_optab->handlers[(int) HImode].insn_code = CODE_FOR_lshlhi3; -#endif -#ifdef HAVE_lshlsi3 - if (HAVE_lshlsi3) - lshl_optab->handlers[(int) SImode].insn_code = CODE_FOR_lshlsi3; -#endif -#ifdef HAVE_lshldi3 - if (HAVE_lshldi3) - lshl_optab->handlers[(int) DImode].insn_code = CODE_FOR_lshldi3; -#endif - lshl_optab->handlers[(int) SImode].lib_call = "__lshlsi3"; - lshl_optab->handlers[(int) DImode].lib_call = "__lshldi3"; - -#ifdef HAVE_lshrqi3 - if (HAVE_lshrqi3) - lshr_optab->handlers[(int) QImode].insn_code = CODE_FOR_lshrqi3; -#endif -#ifdef HAVE_lshrhi3 - if (HAVE_lshrhi3) - lshr_optab->handlers[(int) HImode].insn_code = CODE_FOR_lshrhi3; -#endif -#ifdef HAVE_lshrsi3 - if (HAVE_lshrsi3) - lshr_optab->handlers[(int) SImode].insn_code = CODE_FOR_lshrsi3; -#endif -#ifdef HAVE_lshrdi3 - if (HAVE_lshrdi3) - lshr_optab->handlers[(int) DImode].insn_code = CODE_FOR_lshrdi3; -#endif - lshr_optab->handlers[(int) SImode].lib_call = "__lshrsi3"; - lshr_optab->handlers[(int) DImode].lib_call = "__lshrdi3"; - -#ifdef HAVE_rotlqi3 - if (HAVE_rotlqi3) - rotl_optab->handlers[(int) QImode].insn_code = CODE_FOR_rotlqi3; -#endif -#ifdef HAVE_rotlhi3 - if (HAVE_rotlhi3) - rotl_optab->handlers[(int) HImode].insn_code = CODE_FOR_rotlhi3; -#endif -#ifdef HAVE_rotlsi3 - if (HAVE_rotlsi3) - rotl_optab->handlers[(int) SImode].insn_code = CODE_FOR_rotlsi3; -#endif -#ifdef HAVE_rotldi3 - if (HAVE_rotldi3) - rotl_optab->handlers[(int) DImode].insn_code = CODE_FOR_rotldi3; -#endif - rotl_optab->handlers[(int) SImode].lib_call = "__rotlsi3"; - rotl_optab->handlers[(int) DImode].lib_call = "__rotldi3"; - -#ifdef HAVE_rotrqi3 - if (HAVE_rotrqi3) - rotr_optab->handlers[(int) QImode].insn_code = CODE_FOR_rotrqi3; -#endif -#ifdef HAVE_rotrhi3 - if (HAVE_rotrhi3) - rotr_optab->handlers[(int) HImode].insn_code = CODE_FOR_rotrhi3; -#endif -#ifdef HAVE_rotrsi3 - if (HAVE_rotrsi3) - rotr_optab->handlers[(int) SImode].insn_code = CODE_FOR_rotrsi3; -#endif -#ifdef HAVE_rotrdi3 - if (HAVE_rotrdi3) - rotr_optab->handlers[(int) DImode].insn_code = CODE_FOR_rotrdi3; -#endif - rotr_optab->handlers[(int) SImode].lib_call = "__rotrsi3"; - rotr_optab->handlers[(int) DImode].lib_call = "__rotrdi3"; - -#ifdef HAVE_negqi2 - if (HAVE_negqi2) - neg_optab->handlers[(int) QImode].insn_code = CODE_FOR_negqi2; -#endif -#ifdef HAVE_neghi2 - if (HAVE_neghi2) - neg_optab->handlers[(int) HImode].insn_code = CODE_FOR_neghi2; -#endif -#ifdef HAVE_negsi2 - if (HAVE_negsi2) - neg_optab->handlers[(int) SImode].insn_code = CODE_FOR_negsi2; -#endif -#ifdef HAVE_negdi2 - if (HAVE_negdi2) - neg_optab->handlers[(int) DImode].insn_code = CODE_FOR_negdi2; -#endif -#ifdef HAVE_negsf2 - if (HAVE_negsf2) - neg_optab->handlers[(int) SFmode].insn_code = CODE_FOR_negsf2; -#endif -#ifdef HAVE_negdf2 - if (HAVE_negdf2) - neg_optab->handlers[(int) DFmode].insn_code = CODE_FOR_negdf2; -#endif - neg_optab->handlers[(int) SImode].lib_call = "__negsi2"; - neg_optab->handlers[(int) DImode].lib_call = "__negdi2"; - neg_optab->handlers[(int) SFmode].lib_call = "__negsf2"; - neg_optab->handlers[(int) DFmode].lib_call = "__negdf2"; - -#ifdef HAVE_absqi2 - if (HAVE_absqi2) - abs_optab->handlers[(int) QImode].insn_code = CODE_FOR_absqi2; -#endif -#ifdef HAVE_abshi2 - if (HAVE_abshi2) - abs_optab->handlers[(int) HImode].insn_code = CODE_FOR_abshi2; -#endif -#ifdef HAVE_abssi2 - if (HAVE_abssi2) - abs_optab->handlers[(int) SImode].insn_code = CODE_FOR_abssi2; -#endif -#ifdef HAVE_absdi2 - if (HAVE_absdi2) - abs_optab->handlers[(int) DImode].insn_code = CODE_FOR_absdi2; -#endif -#ifdef HAVE_abssf2 - if (HAVE_abssf2) - abs_optab->handlers[(int) SFmode].insn_code = CODE_FOR_abssf2; -#endif -#ifdef HAVE_absdf2 - if (HAVE_absdf2) - abs_optab->handlers[(int) DFmode].insn_code = CODE_FOR_absdf2; -#endif - /* No library calls here! If there is no abs instruction, - expand_expr will generate a conditional negation. */ - -#ifdef HAVE_one_cmplqi2 - if (HAVE_one_cmplqi2) - one_cmpl_optab->handlers[(int) QImode].insn_code = CODE_FOR_one_cmplqi2; -#endif -#ifdef HAVE_one_cmplhi2 - if (HAVE_one_cmplhi2) - one_cmpl_optab->handlers[(int) HImode].insn_code = CODE_FOR_one_cmplhi2; -#endif -#ifdef HAVE_one_cmplsi2 - if (HAVE_one_cmplsi2) - one_cmpl_optab->handlers[(int) SImode].insn_code = CODE_FOR_one_cmplsi2; -#endif -#ifdef HAVE_one_cmpldi2 - if (HAVE_one_cmpldi2) - one_cmpl_optab->handlers[(int) DImode].insn_code = CODE_FOR_one_cmpldi2; -#endif - one_cmpl_optab->handlers[(int) SImode].lib_call = "__one_cmplsi2"; - one_cmpl_optab->handlers[(int) DImode].lib_call = "__one_cmpldi2"; - -#ifdef HAVE_ffsqi2 - if (HAVE_ffsqi2) - ffs_optab->handlers[(int) QImode].insn_code = CODE_FOR_ffsqi2; -#endif -#ifdef HAVE_ffshi2 - if (HAVE_ffshi2) - ffs_optab->handlers[(int) HImode].insn_code = CODE_FOR_ffshi2; -#endif -#ifdef HAVE_ffssi2 - if (HAVE_ffssi2) - ffs_optab->handlers[(int) SImode].insn_code = CODE_FOR_ffssi2; -#endif -#ifdef HAVE_ffsdi2 - if (HAVE_ffsdi2) - ffs_optab->handlers[(int) DImode].insn_code = CODE_FOR_ffsdi2; -#endif - ffs_optab->handlers[(int) SImode].lib_call = "ffs"; - -#ifdef HAVE_movqi - if (HAVE_movqi) - mov_optab->handlers[(int) QImode].insn_code = CODE_FOR_movqi; -#endif -#ifdef HAVE_movhi - if (HAVE_movhi) - mov_optab->handlers[(int) HImode].insn_code = CODE_FOR_movhi; -#endif -#ifdef HAVE_movsi - if (HAVE_movsi) - mov_optab->handlers[(int) SImode].insn_code = CODE_FOR_movsi; -#endif -#ifdef HAVE_movdi - if (HAVE_movdi) - mov_optab->handlers[(int) DImode].insn_code = CODE_FOR_movdi; -#endif -#ifdef HAVE_movti - if (HAVE_movti) - mov_optab->handlers[(int) TImode].insn_code = CODE_FOR_movti; -#endif -#ifdef HAVE_movsf - if (HAVE_movsf) - mov_optab->handlers[(int) SFmode].insn_code = CODE_FOR_movsf; -#endif -#ifdef HAVE_movdf - if (HAVE_movdf) - mov_optab->handlers[(int) DFmode].insn_code = CODE_FOR_movdf; -#endif -#ifdef HAVE_movtf - if (HAVE_movtf) - mov_optab->handlers[(int) TFmode].insn_code = CODE_FOR_movtf; -#endif - -#ifdef HAVE_movstrictqi - if (HAVE_movstrictqi) - movstrict_optab->handlers[(int) QImode].insn_code = CODE_FOR_movstrictqi; -#endif -#ifdef HAVE_movstricthi - if (HAVE_movstricthi) - movstrict_optab->handlers[(int) HImode].insn_code = CODE_FOR_movstricthi; -#endif -#ifdef HAVE_movstrictsi - if (HAVE_movstrictsi) - movstrict_optab->handlers[(int) SImode].insn_code = CODE_FOR_movstrictsi; -#endif -#ifdef HAVE_movstrictdi - if (HAVE_movstrictdi) - movstrict_optab->handlers[(int) DImode].insn_code = CODE_FOR_movstrictdi; -#endif - -#ifdef HAVE_cmpqi - if (HAVE_cmpqi) - cmp_optab->handlers[(int) QImode].insn_code = CODE_FOR_cmpqi; -#endif -#ifdef HAVE_cmphi - if (HAVE_cmphi) - cmp_optab->handlers[(int) HImode].insn_code = CODE_FOR_cmphi; -#endif -#ifdef HAVE_cmpsi - if (HAVE_cmpsi) - cmp_optab->handlers[(int) SImode].insn_code = CODE_FOR_cmpsi; -#endif -#ifdef HAVE_cmpdi - if (HAVE_cmpdi) - cmp_optab->handlers[(int) DImode].insn_code = CODE_FOR_cmpdi; -#endif -#ifdef HAVE_cmpsf - if (HAVE_cmpsf) - cmp_optab->handlers[(int) SFmode].insn_code = CODE_FOR_cmpsf; -#endif -#ifdef HAVE_cmpdf - if (HAVE_cmpdf) - cmp_optab->handlers[(int) DFmode].insn_code = CODE_FOR_cmpdf; -#endif -#ifdef HAVE_tstqi - if (HAVE_tstqi) - tst_optab->handlers[(int) QImode].insn_code = CODE_FOR_tstqi; -#endif -#ifdef HAVE_tsthi - if (HAVE_tsthi) - tst_optab->handlers[(int) HImode].insn_code = CODE_FOR_tsthi; -#endif -#ifdef HAVE_tstsi - if (HAVE_tstsi) - tst_optab->handlers[(int) SImode].insn_code = CODE_FOR_tstsi; -#endif -#ifdef HAVE_tstdi - if (HAVE_tstdi) - tst_optab->handlers[(int) DImode].insn_code = CODE_FOR_tstdi; -#endif -#ifdef HAVE_tstsf - if (HAVE_tstsf) - tst_optab->handlers[(int) SFmode].insn_code = CODE_FOR_tstsf; -#endif -#ifdef HAVE_tstdf - if (HAVE_tstdf) - tst_optab->handlers[(int) DFmode].insn_code = CODE_FOR_tstdf; -#endif - /* Comparison libcalls for integers MUST come in pairs, signed/unsigned. */ - cmp_optab->handlers[(int) DImode].lib_call = "__cmpdi2"; - ucmp_optab->handlers[(int) DImode].lib_call = "__ucmpdi2"; - cmp_optab->handlers[(int) SFmode].lib_call = "__cmpsf2"; - cmp_optab->handlers[(int) DFmode].lib_call = "__cmpdf2"; - -#if HAVE_beq - if (HAVE_beq) - bcc_gen_fctn[(int) EQ] = gen_beq; -#endif -#if HAVE_bne - if (HAVE_bne) - bcc_gen_fctn[(int) NE] = gen_bne; -#endif -#if HAVE_bgt - if (HAVE_bgt) - bcc_gen_fctn[(int) GT] = gen_bgt; -#endif -#if HAVE_bge - if (HAVE_bge) - bcc_gen_fctn[(int) GE] = gen_bge; -#endif -#if HAVE_bgtu - if (HAVE_bgtu) - bcc_gen_fctn[(int) GTU] = gen_bgtu; -#endif -#if HAVE_bgeu - if (HAVE_bgeu) - bcc_gen_fctn[(int) GEU] = gen_bgeu; -#endif -#if HAVE_blt - if (HAVE_blt) - bcc_gen_fctn[(int) LT] = gen_blt; -#endif -#if HAVE_ble - if (HAVE_ble) - bcc_gen_fctn[(int) LE] = gen_ble; -#endif -#if HAVE_bltu - if (HAVE_bltu) - bcc_gen_fctn[(int) LTU] = gen_bltu; -#endif -#if HAVE_bleu - if (HAVE_bleu) - bcc_gen_fctn[(int) LEU] = gen_bleu; -#endif - -#if HAVE_seq - if (HAVE_seq) - setcc_gen_fctn[(int) EQ] = gen_seq; -#endif -#if HAVE_sne - if (HAVE_sne) - setcc_gen_fctn[(int) NE] = gen_sne; -#endif -#if HAVE_sgt - if (HAVE_sgt) - setcc_gen_fctn[(int) GT] = gen_sgt; -#endif -#if HAVE_sge - if (HAVE_sge) - setcc_gen_fctn[(int) GE] = gen_sge; -#endif -#if HAVE_sgtu - if (HAVE_sgtu) - setcc_gen_fctn[(int) GTU] = gen_sgtu; -#endif -#if HAVE_sgeu - if (HAVE_sgeu) - setcc_gen_fctn[(int) GEU] = gen_sgeu; -#endif -#if HAVE_slt - if (HAVE_slt) - setcc_gen_fctn[(int) LT] = gen_slt; -#endif -#if HAVE_sle - if (HAVE_sle) - setcc_gen_fctn[(int) LE] = gen_sle; -#endif -#if HAVE_sltu - if (HAVE_sltu) - setcc_gen_fctn[(int) LTU] = gen_sltu; -#endif -#if HAVE_sleu - if (HAVE_sleu) - setcc_gen_fctn[(int) LEU] = gen_sleu; -#endif -} diff --git a/gnu/usr.bin/gcc1/cc1/output.h b/gnu/usr.bin/gcc1/cc1/output.h deleted file mode 100644 index ce0be465ed..0000000000 --- a/gnu/usr.bin/gcc1/cc1/output.h +++ /dev/null @@ -1,95 +0,0 @@ -/* Declarations for insn-output.c. These functions are defined in recog.c. - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Operand-predicate functions. */ -int general_operand (); -int push_operand (); -int memory_operand (); -int indirect_operand (); -int immediate_operand (); -int register_operand (); -int address_operand (); -int nonmemory_operand (); -int nonimmediate_operand (); - -int offsettable_address_p (); -rtx adj_offsettable_operand (); - -/* Output a string of assembler code. - Defined in final.c. */ -void output_asm_insn (); - -/* When outputting assembler code, indicates which alternative - of the constraints was actually satisfied. */ -extern int which_alternative; - -/* When outputting delayed branch sequences, this rtx holds the - sequence being output. It is null when no delayed branch - sequence is being output, so it can be used as a test in the - insn output code. - - This variable is defined in final.c. */ -extern rtx final_sequence; - -/* Nonzero if function being compiled pops its args on return. - May affect compilation of return insn or of function epilogue. */ - -extern int current_function_pops_args; - -/* Nonzero if function being compiled needs to be given an address - where the value should be stored. */ - -extern int current_function_returns_struct; - -/* Nonzero if function being compiled needs to - return the address of where it has put a structure value. */ - -extern int current_function_returns_pcc_struct; - -/* Nonzero if function being compiled needs to be passed a static chain. */ - -extern int current_function_needs_context; - -/* Nonzero if function being compiled can call setjmp. */ - -extern int current_function_calls_setjmp; - -/* Nonzero if function being compiled can call alloca, - either as a subroutine or builtin. */ - -extern int current_function_calls_alloca; - -/* Nonzero if the current function returns a pointer type */ - -extern int current_function_returns_pointer; - -/* If function's args have a fixed size, this is that size, in bytes. - Otherwise, it is -1. - May affect compilation of return insn or of function epilogue. */ - -extern int current_function_args_size; - -/* # bytes the prologue should push and pretend that the caller pushed them. - The prologue must do this, but only if parms can be passed in registers. */ - -extern int current_function_pretend_args_size; - -/* Name of function now being compiled. */ - -extern char *current_function_name; diff --git a/gnu/usr.bin/gcc1/cc1/print-tree.c b/gnu/usr.bin/gcc1/cc1/print-tree.c deleted file mode 100644 index 75d06720b6..0000000000 --- a/gnu/usr.bin/gcc1/cc1/print-tree.c +++ /dev/null @@ -1,510 +0,0 @@ -/* Prints out tree in human readable form - GNU C-compiler - Copyright (C) 1990 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "config.h" -#include "tree.h" -#include - - -/* Names of tree components. - Used for printing out the tree and error messages. */ -#define DEFTREECODE(SYM, NAME, TYPE, LEN) NAME, - -char *tree_code_name[] = { -#include "tree.def" -}; -#undef DEFTREECODE - -/* Indexed by enum tree_code, contains a character which is - '<' for a comparison expression, '1', for a unary arithmetic - expression, '2' for a binary arithmetic expression, `e' for - other types of expressions, `r' for a reference, `c' for a - constant, `d' for a decl, `t' for a type, `s' for a statement, - and `x' for anything else (TREE_LIST, IDENTIFIER, etc). */ - -#define TREE_CODE_CLASS(CODE) (*tree_code_type[(int) (CODE)]) - -extern char *mode_name[]; - -void print_node (); - -/* Define the hash table of nodes already seen. - Such nodes are not repeated; brief cross-references are used. */ - -#define HASH_SIZE 37 - -struct bucket -{ - tree node; - struct bucket *next; -}; - -static struct bucket **table; - -/* Print the node NODE on standard error, for debugging. - Most nodes referred to by this one are printed recursively - down to a depth of six. */ - -void -debug_tree (node) - tree node; -{ - char *object = (char *) oballoc (0); - table = (struct bucket **) oballoc (HASH_SIZE * sizeof (struct bucket *)); - bzero (table, HASH_SIZE * sizeof (struct bucket *)); - print_node (stderr, "", node, 0); - table = 0; - obfree (object); - fprintf (stderr, "\n"); -} - -/* Print a node in brief fashion, with just the code, address and name. */ - -void -print_node_brief (file, prefix, node, indent) - FILE *file; - char *prefix; - tree node; - int indent; -{ - char class; - - if (node == 0) - return; - - class = TREE_CODE_CLASS (TREE_CODE (node)); - - /* Always print the slot this node is in, and its code, address and - name if any. */ - if (indent > 0) - fprintf (file, " "); - fprintf (file, "%s <%s %x", prefix, - tree_code_name[(int) TREE_CODE (node)], (int) node); - - if (class == 'd') - { - if (DECL_NAME (node)) - fprintf (file, " %s", IDENTIFIER_POINTER (DECL_NAME (node))); - } - else if (class == 't') - { - if (TYPE_NAME (node)) - { - if (TREE_CODE (TYPE_NAME (node)) == IDENTIFIER_NODE) - fprintf (file, " %s", IDENTIFIER_POINTER (TYPE_NAME (node))); - else if (TREE_CODE (TYPE_NAME (node)) == TYPE_DECL - && DECL_NAME (TYPE_NAME (node))) - fprintf (file, " %s", - IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (node)))); - } - } - if (TREE_CODE (node) == IDENTIFIER_NODE) - fprintf (file, " %s", IDENTIFIER_POINTER (node)); - /* We might as well always print the value of an integer. */ - if (TREE_CODE (node) == INTEGER_CST) - { - if (TREE_INT_CST_HIGH (node) == 0) - fprintf (file, " %1u", TREE_INT_CST_LOW (node)); - else if (TREE_INT_CST_HIGH (node) == -1 - && TREE_INT_CST_LOW (node) != 0) - fprintf (file, " -%1u", -TREE_INT_CST_LOW (node)); - else - fprintf (file, " 0x%x%08x", - TREE_INT_CST_HIGH (node), - TREE_INT_CST_LOW (node)); - } - if (TREE_CODE (node) == REAL_CST) - { -#ifndef REAL_IS_NOT_DOUBLE - fprintf (file, " %e", TREE_REAL_CST (node)); -#else - { - int i; - char *p = (char *) &TREE_REAL_CST (node); - fprintf (file, " 0x"); - for (i = 0; i < sizeof TREE_REAL_CST (node); i++) - fprintf (file, "%02x", *p++); - fprintf (file, ""); - } -#endif /* REAL_IS_NOT_DOUBLE */ - } - - fprintf (file, ">"); -} - -void -indent_to (file, column) - FILE *file; - int column; -{ - int i; - - /* Since this is the long way, indent to desired column. */ - if (column > 0) - fprintf (file, "\n"); - for (i = 0; i < column; i++) - fprintf (file, " "); -} - -/* Print the node NODE in full on file FILE, preceded by PREFIX, - starting in column INDENT. */ - -void -print_node (file, prefix, node, indent) - FILE *file; - char *prefix; - tree node; - int indent; -{ - int hash; - struct bucket *b; - enum machine_mode mode; - char class; - int len; - int first_rtl; - int i; - - if (node == 0) - return; - - class = TREE_CODE_CLASS (TREE_CODE (node)); - - /* Don't get too deep in nesting. If the user wants to see deeper, - it is easy to use the address of a lowest-level node - as an argument in another call to debug_tree. */ - - if (indent > 24) - { - print_node_brief (file, prefix, node, indent); - return; - } - - if (indent > 8 && (class == 't' || class == 'd')) - { - print_node_brief (file, prefix, node, indent); - return; - } - - hash = ((int) node & ~(1 << (HOST_BITS_PER_INT - 1))) % HASH_SIZE; - - /* If node is in the table, just mention its address. */ - for (b = table[hash]; b; b = b->next) - if (b->node == node) - { - print_node_brief (file, prefix, node, indent); - return; - } - - /* Add this node to the table. */ - b = (struct bucket *) oballoc (sizeof (struct bucket)); - b->node = node; - b->next = table[hash]; - table[hash] = b; - - /* Indent to the specified column, since this is the long form. */ - indent_to (file, indent); - - /* Print the slot this node is in, and its code, and address. */ - fprintf (file, "%s <%s %x", prefix, - tree_code_name[(int) TREE_CODE (node)], (int) node); - - /* Print the name, if any. */ - if (class == 'd') - { - if (DECL_NAME (node)) - fprintf (file, " %s", IDENTIFIER_POINTER (DECL_NAME (node))); - } - else if (class == 't') - { - if (TYPE_NAME (node)) - { - if (TREE_CODE (TYPE_NAME (node)) == IDENTIFIER_NODE) - fprintf (file, " %s", IDENTIFIER_POINTER (TYPE_NAME (node))); - else if (TREE_CODE (TYPE_NAME (node)) == TYPE_DECL - && DECL_NAME (TYPE_NAME (node))) - fprintf (file, " %s", - IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (node)))); - } - } - if (TREE_CODE (node) == IDENTIFIER_NODE) - fprintf (file, " %s", IDENTIFIER_POINTER (node)); - - if (TREE_CODE (node) == INTEGER_CST) - { - if (indent <= 4) - print_node_brief (file, "type", TREE_TYPE (node), indent + 4); - } - else - { - print_node (file, "type", TREE_TYPE (node), indent + 4); - if (TREE_TYPE (node)) - indent_to (file, indent + 3); - } - - if (TREE_EXTERNAL (node)) - fprintf (file, " external"); - if (TREE_PUBLIC (node)) - fprintf (file, " public"); - if (TREE_STATIC (node)) - fprintf (file, " static"); - if (TREE_VOLATILE (node)) - fprintf (file, " volatile"); - if (TREE_PACKED (node)) - fprintf (file, " packed"); - if (TREE_READONLY (node)) - fprintf (file, " readonly"); - if (TREE_LITERAL (node)) - fprintf (file, " literal"); - if (TREE_NONLOCAL (node)) - fprintf (file, " nonlocal"); - if (TREE_PERMANENT (node)) - fprintf (file, " permanent"); - if (TREE_ADDRESSABLE (node)) - fprintf (file, " addressable"); - if (TREE_REGDECL (node)) - fprintf (file, " regdecl"); - if (TREE_THIS_VOLATILE (node)) - fprintf (file, " this_vol"); - if (TREE_UNSIGNED (node)) - fprintf (file, " unsigned"); - if (TREE_ASM_WRITTEN (node)) - fprintf (file, " asm_written"); - if (TREE_INLINE (node)) - fprintf (file, " inline"); - if (TREE_USED (node)) - fprintf (file, " used"); - if (TREE_LANG_FLAG_1 (node)) - fprintf (file, " lang_flag_1"); - if (TREE_LANG_FLAG_2 (node)) - fprintf (file, " lang_flag_2"); - if (TREE_LANG_FLAG_3 (node)) - fprintf (file, " lang_flag_3"); - if (TREE_LANG_FLAG_4 (node)) - fprintf (file, " lang_flag_4"); - - - switch (TREE_CODE_CLASS (TREE_CODE (node))) - { - case 'd': - mode = DECL_MODE (node); - fprintf (file, " %s", mode_name[(int) mode]); - - fprintf (file, " file %s line %d", - DECL_SOURCE_FILE (node), DECL_SOURCE_LINE (node)); - - print_node (file, "size", DECL_SIZE (node), indent + 4); - indent_to (file, indent + 3); - fprintf (file, " align %d", DECL_ALIGN (node)); - fprintf (file, " size_unit %d", DECL_SIZE_UNIT (node)); - fprintf (file, " offset %d", DECL_OFFSET (node)); - print_node_brief (file, "context", DECL_CONTEXT (node), indent + 4); - print_node_brief (file, "voffset", DECL_VOFFSET (node), indent + 4); - - print_node (file, "arguments", DECL_ARGUMENTS (node), indent + 4); - print_node (file, "result", DECL_RESULT (node), indent + 4); - print_node_brief (file, "initial", DECL_INITIAL (node), indent + 4); - - /* Print the decl chain only if decl is at second level. */ - if (indent == 4) - print_node (file, "chain", TREE_CHAIN (node), indent + 4); - else - print_node_brief (file, "chain", TREE_CHAIN (node), indent + 4); - break; - - case 't': - mode = TYPE_MODE (node); - fprintf (file, " %s", mode_name[(int) mode]); - - print_node (file, "size", TYPE_SIZE (node), indent + 4); - indent_to (file, indent + 3); - - fprintf (file, " align %d", TYPE_ALIGN (node)); - fprintf (file, " size_unit %d", TYPE_SIZE_UNIT (node)); - fprintf (file, " sep_unit %d", TYPE_SEP_UNIT (node)); - fprintf (file, " symtab %d", TYPE_SYMTAB_ADDRESS (node)); - - print_node (file, "sep", TYPE_SEP (node), indent + 4); - - if (TREE_CODE (node) == ARRAY_TYPE || TREE_CODE (node) == SET_TYPE) - print_node (file, "domain", TYPE_DOMAIN (node), indent + 4); - else if (TREE_CODE (node) == INTEGER_TYPE) - { - fprintf (file, " precision %d", TYPE_PRECISION (node)); - print_node (file, "min", TYPE_MIN_VALUE (node), indent + 4); - print_node (file, "max", TYPE_MAX_VALUE (node), indent + 4); - } - else if (TREE_CODE (node) == ENUMERAL_TYPE) - { - fprintf (file, " precision %d", TYPE_PRECISION (node)); - print_node (file, "min", TYPE_MIN_VALUE (node), indent + 4); - print_node (file, "max", TYPE_MAX_VALUE (node), indent + 4); - print_node (file, "values", TYPE_VALUES (node)); - } - else if (TREE_CODE (node) == REAL_TYPE) - fprintf (file, " precision %d", TYPE_PRECISION (node)); - else if (TREE_CODE (node) == RECORD_TYPE || TREE_CODE (node) == UNION_TYPE) - print_node (file, "fields", TYPE_FIELDS (node), indent + 4); - else if (TREE_CODE (node) == FUNCTION_TYPE || TREE_CODE (node) == METHOD_TYPE) - print_node (file, "arg-types", TYPE_ARG_TYPES (node), indent + 4); - - if (TYPE_POINTER_TO (node) || TREE_CHAIN (node)) - indent_to (file, indent + 3); - print_node_brief (file, "pointer_to_this", TYPE_POINTER_TO (node), indent + 4); - print_node_brief (file, "reference_to_this", TYPE_REFERENCE_TO (node), indent + 4); - print_node_brief (file, "chain", TREE_CHAIN (node), indent + 4); - break; - - case 'e': - case '<': - case '1': - case '2': - case 'r': - first_rtl = len = tree_code_length[(int) TREE_CODE (node)]; - /* These kinds of nodes contain rtx's, not trees, - after a certain point. Print the rtx's as rtx's. */ - switch (TREE_CODE (node)) - { - case SAVE_EXPR: - first_rtl = 1; - break; - case CALL_EXPR: - first_rtl = 2; - break; - case METHOD_CALL_EXPR: - first_rtl = 3; - break; - case WITH_CLEANUP_EXPR: - /* Should be defined to be 2. */ - first_rtl = 1; - break; - case RTL_EXPR: - first_rtl = 0; - } - for (i = 0; i < len; i++) - { - if (i >= first_rtl) - { - if (TREE_OPERAND (node, i)) - print_rtl (file, TREE_OPERAND (node, i)); - else - fprintf (file, "(nil)"); - fprintf (file, "\n"); - } - else - { - char temp[10]; - - sprintf (temp, "arg %d", i); - print_node (file, temp, TREE_OPERAND (node, i), indent + 4); - } - } - break; - - case 's': - fprintf (file, " file %s line %d", - STMT_SOURCE_FILE (node), STMT_SOURCE_LINE (node)); - - switch (TREE_CODE (node)) - { - case IF_STMT: - print_node (file, "cond", STMT_COND (node), indent + 4); - print_node (file, "then", STMT_THEN (node), indent + 4); - print_node (file, "else", STMT_ELSE (node), indent + 4); - break; - - case LET_STMT: - case WITH_STMT: - print_node (file, "vars", STMT_VARS (node), indent + 4); - print_node (file, "tags", STMT_TYPE_TAGS (node), indent + 4); - print_node (file, "supercontext", STMT_SUPERCONTEXT (node), indent + 4); - print_node (file, "body", STMT_BODY (node), indent + 4); - print_node (file, "subblocks", STMT_SUBBLOCKS (node), indent + 4); - break; - - case CASE_STMT: - print_node (file, "index", STMT_CASE_INDEX (node), indent + 4); - print_node (file, "list", STMT_CASE_LIST (node), indent + 4); - break; - - default: - print_node (file, "body", STMT_BODY (node), indent + 4); - break; - } - print_node (file, "chain", TREE_CHAIN (node), indent + 4); - break; - - case 'c': - case 'x': - switch (TREE_CODE (node)) - { - case INTEGER_CST: - if (TREE_INT_CST_HIGH (node) == 0) - fprintf (file, " %1u", TREE_INT_CST_LOW (node)); - else if (TREE_INT_CST_HIGH (node) == -1 - && TREE_INT_CST_LOW (node) != 0) - fprintf (file, " -%1u", -TREE_INT_CST_LOW (node)); - else - fprintf (file, " 0x%x%08x", - TREE_INT_CST_HIGH (node), - TREE_INT_CST_LOW (node)); - break; - - case REAL_CST: -#ifndef REAL_IS_NOT_DOUBLE - fprintf (file, " %e", TREE_REAL_CST (node)); -#else - { - char *p = (char *) &TREE_REAL_CST (node); - fprintf (file, " 0x"); - for (i = 0; i < sizeof TREE_REAL_CST (node); i++) - fprintf (file, "%02x", *p++); - fprintf (file, ""); - } -#endif /* REAL_IS_NOT_DOUBLE */ - break; - - case COMPLEX_CST: - print_node (file, "real", TREE_REALPART (node), indent + 4); - print_node (file, "imag", TREE_IMAGPART (node), indent + 4); - break; - - case STRING_CST: - fprintf (file, " \"%s\"", TREE_STRING_POINTER (node)); - break; - - case IDENTIFIER_NODE: - print_lang_identifier (file, node, indent); - break; - - case TREE_LIST: - print_node (file, "purpose", TREE_PURPOSE (node), indent + 4); - print_node (file, "value", TREE_VALUE (node), indent + 4); - print_node (file, "chain", TREE_CHAIN (node), indent + 4); - break; - - case OP_IDENTIFIER: - print_node (file, "op1", TREE_PURPOSE (node), indent + 4); - print_node (file, "op2", TREE_VALUE (node), indent + 4); - } - - print_node (file, "chain", TREE_CHAIN (node), indent + 4); - break; - } -} diff --git a/gnu/usr.bin/gcc1/cc1/proto.h b/gnu/usr.bin/gcc1/cc1/proto.h deleted file mode 100644 index cc48915d84..0000000000 --- a/gnu/usr.bin/gcc1/cc1/proto.h +++ /dev/null @@ -1,4 +0,0 @@ -/* This header file is to avoid trouble with semi-ANSI header files - on the Convex in system version 8.0. */ - -#define _PROTO(list) () diff --git a/gnu/usr.bin/gcc1/cc1/real.h b/gnu/usr.bin/gcc1/cc1/real.h deleted file mode 100644 index cbf79dce65..0000000000 --- a/gnu/usr.bin/gcc1/cc1/real.h +++ /dev/null @@ -1,94 +0,0 @@ -/* Front-end tree definitions for GNU compiler. - Copyright (C) 1989 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifndef REAL_H_INCLUDED -#define REAL_H_INCLUDED - -/* If we are not cross-compiling, use a `double' to represent the - floating-point value. Otherwise, use some other type - (probably a struct containing an array of longs). */ -#ifndef REAL_VALUE_TYPE -#define REAL_VALUE_TYPE double -#else -#define REAL_IS_NOT_DOUBLE -#endif - -/* Compare two floating-point values for equality. */ -#ifndef REAL_VALUES_EQUAL -#define REAL_VALUES_EQUAL(x,y) ((x) == (y)) -#endif - -/* Compare two floating-point values for less than. */ -#ifndef REAL_VALUES_LESS -#define REAL_VALUES_LESS(x,y) ((x) < (y)) -#endif - -/* Scale X by Y powers of 2. */ -#ifndef REAL_VALUE_LDEXP -#define REAL_VALUE_LDEXP(x,y) ldexp (x, y) -extern double ldexp (); -#endif - -/* Convert the string X to a floating-point value. */ -#ifndef REAL_VALUE_ATOF -#define REAL_VALUE_ATOF(x) atof (x) -extern double atof (); -#endif - -/* Negate the floating-point value X. */ -#ifndef REAL_VALUE_NEGATE -#define REAL_VALUE_NEGATE(x) (- (x)) -#endif - -/* Truncate the floating-point value X to single-precision. */ -#ifndef REAL_VALUE_TRUNCATE -#define REAL_VALUE_TRUNCATE(x) ((float) (x)) -#endif - -/* Union type used for extracting real values from CONST_DOUBLEs - or putting them in. */ - -union real_extract -{ - REAL_VALUE_TYPE d; - int i[sizeof (REAL_VALUE_TYPE) / sizeof (int)]; -}; - -/* For a CONST_DOUBLE: - The usual two ints that hold the value. - For a DImode, that is all there are; - and CONST_DOUBLE_LOW is the low-order word and ..._HIGH the high-order. - For a float, the number of ints varies, - and CONST_DOUBLE_LOW is the one that should come first *in memory*. - So use &CONST_DOUBLE_LOW(r) as the address of an array of ints. */ -#define CONST_DOUBLE_LOW(r) XINT (r, 2) -#define CONST_DOUBLE_HIGH(r) XINT (r, 3) - -/* Link for chain of all CONST_DOUBLEs in use in current function. */ -#define CONST_DOUBLE_CHAIN(r) XEXP (r, 1) -/* The MEM which represents this CONST_DOUBLE's value in memory, - or const0_rtx if no MEM has been made for it yet, - or cc0_rtx if it is not on the chain. */ -#define CONST_DOUBLE_MEM(r) XEXP (r, 0) - -/* Function to return a real value (not a tree node) - from a given integer constant. */ -REAL_VALUE_TYPE real_value_from_int_cst (); - -#endif /* Not REAL_H_INCLUDED */ diff --git a/gnu/usr.bin/gcc1/cc1/recog.c b/gnu/usr.bin/gcc1/cc1/recog.c deleted file mode 100644 index e7bd25c0e8..0000000000 --- a/gnu/usr.bin/gcc1/cc1/recog.c +++ /dev/null @@ -1,1106 +0,0 @@ -/* Subroutines used by or related to instruction recognition. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "config.h" -#include "rtl.h" -#include -#include "insn-config.h" -#include "recog.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "real.h" - - -static int inequality_comparisons_p (); -int strict_memory_address_p (); -int memory_address_p (); - -/* Nonzero means allow operands to be volatile. - This is 1 if you use recog_memoized, 0 if you don't. - init_recog and recog_memoized are responsible for setting it. - This way of handling it is not really clean and will be change later. */ - -int volatile_ok; - -rtx recog_addr_dummy; - -/* On return from `constrain_operands', indicate which alternative - was satisfied. */ - -int which_alternative; - -/* Nonzero after end of reload pass. - Set to 1 or 0 by toplev.c. - Controls the significance of (SUBREG (MEM)). */ - -int reload_completed; - -/* Initialize data used by the function `recog'. - This must be called once in the compilation of a function - before any insn recognition may be done in the function. */ - -void -init_recog () -{ - volatile_ok = 0; - recog_addr_dummy = gen_rtx (MEM, VOIDmode, 0); -} - -/* Try recognizing the instruction INSN, - and return the code number that results. - Remeber the code so that repeated calls do not - need to spend the time for actual rerecognition. - - This function is the normal interface to instruction recognition. - The automatically-generated function `recog' is normally called - through this one. (The only exception is in combine.c.) */ - -int -recog_memoized (insn) - rtx insn; -{ - volatile_ok = 1; - if (INSN_CODE (insn) < 0) - INSN_CODE (insn) = recog (PATTERN (insn), insn); - return INSN_CODE (insn); -} - -/* Return 1 if the insn following INSN does not contain - any ordered tests applied to the condition codes. - EQ and NE tests do not count. */ - -int -next_insn_tests_no_inequality (insn) - rtx insn; -{ - register rtx next = NEXT_INSN (insn); - - return ((GET_CODE (next) == JUMP_INSN - || GET_CODE (next) == INSN - || GET_CODE (next) == CALL_INSN) - && ! inequality_comparisons_p (PATTERN (next))); -} - -/* Return 1 if the CC value set up by INSN is not used. */ - -int -next_insns_test_no_inequality (insn) - rtx insn; -{ - register rtx next = NEXT_INSN (insn); - - for (; next != 0; next = NEXT_INSN (next)) - { - if (GET_CODE (next) == CODE_LABEL - || GET_CODE (next) == BARRIER) - return 1; - if (GET_CODE (next) == NOTE) - continue; - if (inequality_comparisons_p (PATTERN (next))) - return 0; - if (GET_CODE (PATTERN (next)) == SET - && SET_DEST (PATTERN (next)) == cc0_rtx) - return 1; - if (! reg_mentioned_p (cc0_rtx, PATTERN (next))) - return 1; - } - return 1; -} - -static int -inequality_comparisons_p (x) - rtx x; -{ - register char *fmt; - register int len, i; - register enum rtx_code code = GET_CODE (x); - - switch (code) - { - case REG: - case PC: - case CC0: - case CONST_INT: - case CONST_DOUBLE: - case CONST: - case LABEL_REF: - case SYMBOL_REF: - return 0; - - case LT: - case LTU: - case GT: - case GTU: - case LE: - case LEU: - case GE: - case GEU: - return (XEXP (x, 0) == cc0_rtx || XEXP (x, 1) == cc0_rtx); - } - - len = GET_RTX_LENGTH (code); - fmt = GET_RTX_FORMAT (code); - - for (i = 0; i < len; i++) - { - if (fmt[i] == 'e') - { - if (inequality_comparisons_p (XEXP (x, i))) - return 1; - } - else if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - if (inequality_comparisons_p (XVECEXP (x, i, j))) - return 1; - } - } - - return 0; -} - -/* Return 1 if OP is a valid general operand for machine mode MODE. - This is either a register reference, a memory reference, - or a constant. In the case of a memory reference, the address - is checked for general validity for the target machine. - - Register and memory references must have mode MODE in order to be valid, - but some constants have no machine mode and are valid for any mode. - - If MODE is VOIDmode, OP is checked for validity for whatever mode - it has. - - The main use of this function is as a predicate in match_operand - expressions in the machine description. */ - -int -general_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - register enum rtx_code code = GET_CODE (op); - int mode_altering_drug = 0; - - if (mode == VOIDmode) - mode = GET_MODE (op); - - if (CONSTANT_P (op)) - return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode) - && LEGITIMATE_CONSTANT_P (op)); - - /* Except for certain constants with VOIDmode, already checked for, - OP's mode must match MODE if MODE specifies a mode. */ - - if (GET_MODE (op) != mode) - return 0; - - while (code == SUBREG) - { - op = SUBREG_REG (op); - code = GET_CODE (op); -#if 0 - /* No longer needed, since (SUBREG (MEM...)) - will load the MEM into a reload reg in the MEM's own mode. */ - mode_altering_drug = 1; -#endif - } - if (code == REG) - return 1; - if (code == CONST_DOUBLE) - return LEGITIMATE_CONSTANT_P (op); - if (code == MEM) - { - register rtx y = XEXP (op, 0); - if (! volatile_ok && MEM_VOLATILE_P (op)) - return 0; - /* Use the mem's mode, since it will be reloaded thus. */ - mode = GET_MODE (op); - GO_IF_LEGITIMATE_ADDRESS (mode, y, win); - } - return 0; - - win: - if (mode_altering_drug) - return ! mode_dependent_address_p (XEXP (op, 0)); - return 1; -} - -/* Return 1 if OP is a valid memory address for a memory reference - of mode MODE. - - The main use of this function is as a predicate in match_operand - expressions in the machine description. */ - -int -address_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return (memory_address_p (mode, op) - && (GET_CODE (op) != MEM || !MEM_VOLATILE_P (op) || volatile_ok)); -} - -/* Return 1 if OP is a register reference of mode MODE. - If MODE is VOIDmode, accept a register in any mode. - - The main use of this function is as a predicate in match_operand - expressions in the machine description. */ - -int -register_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - if (GET_MODE (op) != mode && mode != VOIDmode) - return 0; - - if (GET_CODE (op) == SUBREG) - { - /* Before reload, we can allow (SUBREG (MEM...)) as a register operand - because it is guaranteed to be reloaded into one. - Just make sure the MEM is valid in itself. - (Ideally, (SUBREG (MEM)...) should not exist after reload, - but currently it does result from (SUBREG (REG)...) where the - reg went on the stack.) */ - if (! reload_completed) - return general_operand (op, mode); - } - - while (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - - return GET_CODE (op) == REG; -} - -/* Return 1 if OP is a valid immediate operand for mode MODE. - - The main use of this function is as a predicate in match_operand - expressions in the machine description. */ - -int -immediate_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return ((CONSTANT_P (op) - || (GET_CODE (op) == CONST_DOUBLE - && (GET_MODE (op) == mode || mode == VOIDmode))) - && LEGITIMATE_CONSTANT_P (op)); -} - -/* Return 1 if OP is a general operand that is not an immediate operand. */ - -int -nonimmediate_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return (general_operand (op, mode) - && ! CONSTANT_P (op) && GET_CODE (op) != CONST_DOUBLE); -} - -/* Return 1 if OP is a register reference or immediate value of mode MODE. */ - -int -nonmemory_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - if (CONSTANT_P (op) || GET_CODE (op) == CONST_DOUBLE) - return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode) - && LEGITIMATE_CONSTANT_P (op)); - - if (GET_MODE (op) != mode && mode != VOIDmode) - return 0; - - if (GET_CODE (op) == SUBREG) - { - /* Before reload, we can allow (SUBREG (MEM...)) as a register operand - because it is guaranteed to be reloaded into one. - Just make sure the MEM is valid in itself. - (Ideally, (SUBREG (MEM)...) should not exist after reload, - but currently it does result from (SUBREG (REG)...) where the - reg went on the stack.) */ - if (! reload_completed) - return general_operand (op, mode); - } - - while (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - - return GET_CODE (op) == REG; -} - -/* Return 1 if OP is a valid operand that stands for pushing a - value of mode MODE onto the stack. - - The main use of this function is as a predicate in match_operand - expressions in the machine description. */ - -int -push_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) != MEM) - return 0; - - if (GET_MODE (op) != mode) - return 0; - - op = XEXP (op, 0); - -#ifdef STACK_GROWS_DOWNWARD - if (GET_CODE (op) != PRE_DEC) - return 0; -#else - if (GET_CODE (op) != PRE_INC) - return 0; -#endif - return XEXP (op, 0) == stack_pointer_rtx; -} - -/* Return 1 if ADDR is a valid memory address for mode MODE. */ - -int -memory_address_p (mode, addr) - enum machine_mode mode; - register rtx addr; -{ - GO_IF_LEGITIMATE_ADDRESS (mode, addr, win); - return 0; - - win: - return 1; -} - -/* Return 1 if OP is a valid memory reference with mode MODE, - including a valid address. - - The main use of this function is as a predicate in match_operand - expressions in the machine description. */ - -int -memory_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - rtx inner; - int mode_altering_drug = 0; - - if (! reload_completed) - /* Note that no SUBREG is a memory operand before end of reload pass, - because (SUBREG (MEM...)) forces reloading into a register. */ - return GET_CODE (op) == MEM && general_operand (op, mode); - - if (mode != VOIDmode && GET_MODE (op) != mode) - return 0; - - inner = op; - while (GET_CODE (inner) == SUBREG) - inner = SUBREG_REG (inner); - - return (GET_CODE (inner) == MEM && general_operand (op, mode)); -} - -/* Return 1 if OP is a valid indirect memory reference with mode MODE; - that is, a memory reference whose address is a general_operand. */ - -int -indirect_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return (GET_MODE (op) == mode && memory_operand (op, mode) - && general_operand (XEXP (op, 0), Pmode)); -} - -/* If BODY is an insn body that uses ASM_OPERANDS, - return the number of operands (both input and output) in the insn. - Otherwise return -1. */ - -int -asm_noperands (body) - rtx body; -{ - if (GET_CODE (body) == ASM_OPERANDS) - /* No output operands: return number of input operands. */ - return XVECLEN (body, 3); - if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS) - /* Single output operand: BODY is (set OUTPUT (asm_operands ...)). */ - return XVECLEN (SET_SRC (body), 3) + 1; - else if (GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == SET - && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) == ASM_OPERANDS) - { - /* Multiple output operands, or 1 output plus some clobbers: - body is [(set OUTPUT (asm_operands ...))... (clobber (reg ...))...]. */ - int i; - int n_sets; - - /* Count backwards through CLOBBERs to determine number of SETs. */ - for (i = XVECLEN (body, 0); i > 0; i--) - { - if (GET_CODE (XVECEXP (body, 0, i - 1)) == SET) - break; - if (GET_CODE (XVECEXP (body, 0, i - 1)) != CLOBBER) - return -1; - } - - /* N_SETS is now number of output operands. */ - n_sets = i; - - /* Verify that all the SETs we have - came from a single original asm_operands insn - (so that invalid combinations are blocked). */ - for (i = 0; i < n_sets; i++) - { - rtx elt = XVECEXP (body, 0, i); - if (GET_CODE (elt) != SET) - return -1; - if (GET_CODE (SET_SRC (elt)) != ASM_OPERANDS) - return -1; - /* If these ASM_OPERANDS rtx's came from different original insns - then they aren't allowed together. */ - if (XVEC (SET_SRC (elt), 3) - != XVEC (SET_SRC (XVECEXP (body, 0, 0)), 3)) - return -1; - } - return XVECLEN (SET_SRC (XVECEXP (body, 0, 0)), 3) + n_sets; - } - else if (GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS) - { - /* 0 outputs, but some clobbers: - body is [(asm_operands ...) (clobber (reg ...))...]. */ - int i; - int n_sets; - - /* Make sure all the other parallel things really are clobbers. */ - for (i = XVECLEN (body, 0) - 1; i > 0; i--) - if (GET_CODE (XVECEXP (body, 0, i)) != CLOBBER) - return -1; - - return XVECLEN (XVECEXP (body, 0, 0), 3); - } - else - return -1; -} - -/* Assuming BODY is an insn body that uses ASM_OPERANDS, - copy its operands (both input and output) into the vector OPERANDS, - the locations of the operands within the insn into the vector OPERAND_LOCS, - and the constraints for the operands into CONSTRAINTS. - Write the modes of the operands into MODES. - Return the assembler-template. - - If MODES, OPERAND_LOCS, CONSTRAINTS or OPERANDS is 0, - we don't store that info. */ - -char * -decode_asm_operands (body, operands, operand_locs, constraints, modes) - rtx body; - rtx *operands; - rtx **operand_locs; - char **constraints; - enum machine_mode *modes; -{ - register int i; - int noperands; - char *template = 0; - - if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS) - { - rtx asmop = SET_SRC (body); - /* Single output operand: BODY is (set OUTPUT (asm_operands ....)). */ - - noperands = XVECLEN (asmop, 3) + 1; - - /* The input operands are found in the 1st element vector. */ - /* Constraints for inputs are in the 2nd element vector. */ - for (i = 1; i < noperands; i++) - { - if (operand_locs) - operand_locs[i] = &XVECEXP (asmop, 3, i - 1); - if (operands) - operands[i] = XVECEXP (asmop, 3, i - 1); - if (constraints) - constraints[i] = XSTR (XVECEXP (asmop, 4, i - 1), 0); - if (modes) - modes[i] = GET_MODE (XVECEXP (asmop, 4, i - 1)); - } - - /* The output is in the SET. - Its constraint is in the ASM_OPERANDS itself. */ - if (operands) - operands[0] = SET_DEST (body); - if (operand_locs) - operand_locs[0] = &SET_DEST (body); - if (constraints) - constraints[0] = XSTR (asmop, 1); - if (modes) - modes[0] = GET_MODE (SET_DEST (body)); - template = XSTR (asmop, 0); - } - else if (GET_CODE (body) == ASM_OPERANDS) - { - rtx asmop = body; - /* No output operands: BODY is (asm_operands ....). */ - - noperands = XVECLEN (asmop, 3); - - /* The input operands are found in the 1st element vector. */ - /* Constraints for inputs are in the 2nd element vector. */ - for (i = 0; i < noperands; i++) - { - if (operand_locs) - operand_locs[i] = &XVECEXP (asmop, 3, i); - if (operands) - operands[i] = XVECEXP (asmop, 3, i); - if (constraints) - constraints[i] = XSTR (XVECEXP (asmop, 4, i), 0); - if (modes) - modes[i] = GET_MODE (XVECEXP (asmop, 4, i)); - } - template = XSTR (asmop, 0); - } - else if (GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == SET) - { - rtx asmop = SET_SRC (XVECEXP (body, 0, 0)); - int nparallel = XVECLEN (body, 0); /* Includes CLOBBERs. */ - int nin = XVECLEN (asmop, 3); - int nout = 0; /* Does not include CLOBBERs. */ - - /* At least one output, plus some CLOBBERs. */ - - /* The outputs are in the SETs. - Their constraints are in the ASM_OPERANDS itself. */ - for (i = 0; i < nparallel; i++) - { - if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER) - break; /* Past last SET */ - - if (operands) - operands[i] = SET_DEST (XVECEXP (body, 0, i)); - if (operand_locs) - operand_locs[i] = &SET_DEST (XVECEXP (body, 0, i)); - if (constraints) - constraints[i] = XSTR (SET_SRC (XVECEXP (body, 0, i)), 1); - if (modes) - modes[i] = GET_MODE (SET_DEST (XVECEXP (body, 0, i))); - nout++; - } - - /* The input operands are found in the 1st element vector. */ - /* Constraints for inputs are in the 2nd element vector. */ - for (i = 0; i < nin; i++) - { - if (operand_locs) - operand_locs[i + nout] = &XVECEXP (asmop, 3, i); - if (operands) - operands[i + nout] = XVECEXP (asmop, 3, i); - if (constraints) - constraints[i + nout] = XSTR (XVECEXP (asmop, 4, i), 0); - if (modes) - modes[i + nout] = GET_MODE (XVECEXP (asmop, 4, i)); - } - - template = XSTR (asmop, 0); - } - else if (GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS) - { - /* No outputs, but some CLOBBERs. */ - - rtx asmop = XVECEXP (body, 0, 0); - int nin = XVECLEN (asmop, 3); - - /* The input operands are found in the 1st element vector. */ - /* Constraints for inputs are in the 2nd element vector. */ - for (i = 0; i < nin; i++) - { - if (operand_locs) - operand_locs[i] = &XVECEXP (asmop, 3, i); - if (operands) - operands[i] = XVECEXP (asmop, 3, i); - if (constraints) - constraints[i] = XSTR (XVECEXP (asmop, 4, i), 0); - if (modes) - modes[i] = GET_MODE (XVECEXP (asmop, 4, i)); - } - - template = XSTR (asmop, 0); - } - - return template; -} - -extern rtx plus_constant (); -extern rtx copy_rtx (); - -/* Given an rtx *P, if it is a sum containing an integer constant term, - return the location (type rtx *) of the pointer to that constant term. - Otherwise, return a null pointer. */ - -static rtx * -find_constant_term_loc (p) - rtx *p; -{ - register rtx *tem; - register enum rtx_code code = GET_CODE (*p); - - /* If *P IS such a constant term, P is its location. */ - - if (code == CONST_INT || code == SYMBOL_REF || code == LABEL_REF - || code == CONST) - return p; - - /* Otherwise, if not a sum, it has no constant term. */ - - if (GET_CODE (*p) != PLUS) - return 0; - - /* If one of the summands is constant, return its location. */ - - if (XEXP (*p, 0) && CONSTANT_P (XEXP (*p, 0)) - && XEXP (*p, 1) && CONSTANT_P (XEXP (*p, 1))) - return p; - - /* Otherwise, check each summand for containing a constant term. */ - - if (XEXP (*p, 0) != 0) - { - tem = find_constant_term_loc (&XEXP (*p, 0)); - if (tem != 0) - return tem; - } - - if (XEXP (*p, 1) != 0) - { - tem = find_constant_term_loc (&XEXP (*p, 1)); - if (tem != 0) - return tem; - } - - return 0; -} - -/* Return 1 if OP is a memory reference - whose address contains no side effects - and remains valid after the addition - of a positive integer less than the - size of the object being referenced. - - We assume that the original address is valid and do not check it. - - This uses strict_memory_address_p as a subroutine, so - don't use it before reload. */ - -int -offsettable_memref_p (op) - rtx op; -{ - return ((GET_CODE (op) == MEM) - && offsettable_address_p (1, GET_MODE (op), XEXP (op, 0))); -} - -/* Return 1 if Y is a memory address which contains no side effects - and would remain valid for mode MODE - after the addition of a positive integer less than the - size of that mode. - - We assume that the original address is valid and do not check it. - - If STRICTP is nonzero, we require a strictly valid address, - for the sake of use in reload.c. */ - -int -offsettable_address_p (strictp, mode, y) - int strictp; - enum machine_mode mode; - register rtx y; -{ - register enum rtx_code ycode = GET_CODE (y); - register rtx z; - rtx y1 = y; - rtx *y2; - int (*addressp) () = (strictp ? strict_memory_address_p : memory_address_p); - - if (CONSTANT_ADDRESS_P (y)) - return 1; - -#ifdef OFFSETTABLE_ADDRESS_P - return OFFSETTABLE_ADDRESS_P (mode, y); -#else - /* If the expression contains a constant term, - see if it remains valid when max possible offset is added. */ - - if ((ycode == PLUS) && (y2 = find_constant_term_loc (&y1))) - { - int old = INTVAL (y1 = *y2); - int good; - INTVAL (y1) += GET_MODE_SIZE (mode) - 1; - good = (*addressp) (mode, y); - /* In any case, restore old contents of memory. */ - INTVAL (y1) = old; - return good; - } - - if (ycode == PRE_DEC || ycode == PRE_INC - || ycode == POST_DEC || ycode == POST_INC) - return 0; - - /* The offset added here is chosen as the maximum offset that - any instruction could need to add when operating on something - of the specified mode. We assume that if Y and Y+c are - valid addresses then so is Y+d for all 0': - if (GET_CODE (op) == MEM - && (GET_CODE (XEXP (op, 0)) == PRE_INC - || GET_CODE (XEXP (op, 0)) == POST_INC)) - win = 1; - break; - - case 'F': - if (GET_CODE (op) == CONST_DOUBLE) - win = 1; - break; - - case 'G': - case 'H': - if (GET_CODE (op) == CONST_DOUBLE - && CONST_DOUBLE_OK_FOR_LETTER_P (op, c)) - win = 1; - break; - - case 's': - if (GET_CODE (op) == CONST_INT) - break; - case 'i': - if (CONSTANT_P (op)) - win = 1; - break; - - case 'n': - if (GET_CODE (op) == CONST_INT) - win = 1; - break; - - case 'I': - case 'J': - case 'K': - case 'L': - case 'M': - if (GET_CODE (op) == CONST_INT - && CONST_OK_FOR_LETTER_P (INTVAL (op), c)) - win = 1; - break; - - case 'o': - if (offsettable_memref_p (op)) - win = 1; - break; - - default: - if (GET_CODE (op) == REG - && reg_fits_class_p (op, REG_CLASS_FROM_LETTER (c), - 0, GET_MODE (op))) - win = 1; - } - - constraints[opno] = p; - /* If this operand did not win somehow, - this alternative loses. */ - if (! win) - lose = 1; - } - /* This alternative won; the operands are ok. - Change whichever operands this alternative says to change. */ - if (! lose) - { - while (--funny_match_index >= 0) - { - recog_operand[funny_match[funny_match_index].other] - = recog_operand[funny_match[funny_match_index].this]; - } - return 1; - } - - which_alternative++; - } - return 0; -} - -/* Return 1 iff OPERAND (assumed to be a REG rtx) - is a hard reg in class CLASS when its regno is offsetted by OFFSET - and changed to mode MODE. - If REG occupies multiple hard regs, all of them must be in CLASS. */ - -int -reg_fits_class_p (operand, class, offset, mode) - rtx operand; - register enum reg_class class; - int offset; - enum machine_mode mode; -{ - register int regno = REGNO (operand); - if (regno < FIRST_PSEUDO_REGISTER - && TEST_HARD_REG_BIT (reg_class_contents[(int) class], - regno + offset)) - { - register int sr; - regno += offset; - for (sr = HARD_REGNO_NREGS (regno, mode) - 1; - sr > 0; sr--) - if (! TEST_HARD_REG_BIT (reg_class_contents[(int) class], - regno + sr)) - break; - return sr == 0; - } - return 0; -} - -#endif /* REGISTER_CONSTRAINTS */ diff --git a/gnu/usr.bin/gcc1/cc1/recog.h b/gnu/usr.bin/gcc1/cc1/recog.h deleted file mode 100644 index 9b6c724e7e..0000000000 --- a/gnu/usr.bin/gcc1/cc1/recog.h +++ /dev/null @@ -1,78 +0,0 @@ -/* Declarations for interface to insn recognizer and insn-output.c. - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Recognize an insn and return its insn-code, - which is the sequence number of the DEFINE_INSN that it matches. - If the insn does not match, return -1. */ - -extern int recog_memoized (); - -/* Extract the operands from an insn that has been recognized. */ - -extern void insn_extract (); - -/* The following vectors hold the results from insn_extract. */ - -/* Indexed by N, gives value of operand N. */ -extern rtx recog_operand[]; - -/* Indexed by N, gives location where operand N was found. */ -extern rtx *recog_operand_loc[]; - -/* Indexed by N, gives location where the Nth duplicate-appearance of - an operand was found. This is something that matched MATCH_DUP. */ -extern rtx *recog_dup_loc[]; - -/* Indexed by N, gives the operand number that was duplicated in the - Nth duplicate-appearance of an operand. */ -extern char recog_dup_num[]; - -/* Tables defined in insn-output.c that give information about - each insn-code value. */ - -/* These are vectors indexed by insn-code. Details in genoutput.c. */ - -extern char *insn_template[]; - -extern char *(*insn_outfun[]) (); - -extern int insn_n_operands[]; - -extern int insn_n_dups[]; - -#ifdef INSN_MACHINE_INFO -extern INSN_MACHINE_INFO insn_machine_info[]; -#endif - -/* Indexed by insn code number, gives # of constraint alternatives. */ - -extern int insn_n_alternatives[]; - -/* These are two-dimensional arrays indexed first by the insn-code - and second by the operand number. Details in genoutput.c. */ - -extern char *insn_operand_constraint[][MAX_RECOG_OPERANDS]; - -extern char insn_operand_address_p[][MAX_RECOG_OPERANDS]; - -extern enum machine_mode insn_operand_mode[][MAX_RECOG_OPERANDS]; - -extern char insn_operand_strict_low[][MAX_RECOG_OPERANDS]; - -extern int (*insn_operand_predicate[][MAX_RECOG_OPERANDS]) (); diff --git a/gnu/usr.bin/gcc1/cc1/regclass.c b/gnu/usr.bin/gcc1/cc1/regclass.c deleted file mode 100644 index 4c423febbd..0000000000 --- a/gnu/usr.bin/gcc1/cc1/regclass.c +++ /dev/null @@ -1,905 +0,0 @@ -/* Compute register class preferences for pseudo-registers. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This file contains two passes of the compiler: reg_scan and reg_class. - It also defines some tables of information about the hardware registers - and a function init_reg_sets to initialize the tables. */ - -#include "config.h" -#include "rtl.h" -#include "hard-reg-set.h" -#include "flags.h" -#include "basic-block.h" -#include "regs.h" -#include "insn-config.h" -#include "recog.h" - -#define max(A,B) ((A) > (B) ? (A) : (B)) -#define min(A,B) ((A) < (B) ? (A) : (B)) - -/* Register tables used by many passes. */ - -/* Indexed by hard register number, contains 1 for registers - that are fixed use (stack pointer, pc, frame pointer, etc.). - These are the registers that cannot be used to allocate - a pseudo reg whose life does not cross calls. */ - -char fixed_regs[FIRST_PSEUDO_REGISTER]; - -/* Same info as a HARD_REG_SET. */ - -HARD_REG_SET fixed_reg_set; - -/* Data for initializing the above. */ - -static char initial_fixed_regs[] = FIXED_REGISTERS; - -/* Indexed by hard register number, contains 1 for registers - that are fixed use or are clobbered by function calls. - These are the registers that cannot be used to allocate - a pseudo reg whose life crosses calls. */ - -char call_used_regs[FIRST_PSEUDO_REGISTER]; - -/* Same info as a HARD_REG_SET. */ - -HARD_REG_SET call_used_reg_set; - -/* Data for initializing the above. */ - -static char initial_call_used_regs[] = CALL_USED_REGISTERS; - -/* Indexed by hard register number, contains 1 for registers that are - fixed use -- i.e. in fixed_regs -- or a function value return register - or STRUCT_VALUE_REGNUM or STATIC_CHAIN_REGNUM. These are the - registers that cannot hold quantities across calls even if we are - willing to save and restore them. */ - -char call_fixed_regs[FIRST_PSEUDO_REGISTER]; - -/* The same info as a HARD_REG_SET. */ - -HARD_REG_SET call_fixed_reg_set; - -/* Indexed by hard register number, contains 1 for registers - that are being used for global register decls. - These must be exempt from ordinary flow analysis - and are also considered fixed. */ - -char global_regs[FIRST_PSEUDO_REGISTER]; - -/* Table of register numbers in the order in which to try to use them. */ -#ifdef REG_ALLOC_ORDER -int reg_alloc_order[FIRST_PSEUDO_REGISTER] = REG_ALLOC_ORDER; -#endif - -/* For each reg class, a HARD_REG_SET saying which registers are in it. */ - -HARD_REG_SET reg_class_contents[] = REG_CLASS_CONTENTS; - -/* For each reg class, number of regs it contains. */ - -int reg_class_size[N_REG_CLASSES]; - -/* For each reg class, table listing all the containing classes. */ - -enum reg_class reg_class_superclasses[N_REG_CLASSES][N_REG_CLASSES]; - -/* For each reg class, table listing all the classes contained in it. */ - -enum reg_class reg_class_subclasses[N_REG_CLASSES][N_REG_CLASSES]; - -/* For each pair of reg classes, - a largest reg class contained in their union. */ - -enum reg_class reg_class_subunion[N_REG_CLASSES][N_REG_CLASSES]; - -/* Array containing all of the register names */ - -char *reg_names[] = REGISTER_NAMES; - - -/* Function called only once to initialize the above data on reg usage. - Once this is done, various switches may override. */ - -void -init_reg_sets () -{ - register int i, j; - - bcopy (initial_fixed_regs, fixed_regs, sizeof fixed_regs); - bcopy (initial_call_used_regs, call_used_regs, sizeof call_used_regs); - bzero (global_regs, sizeof global_regs); - - /* Compute number of hard regs in each class. */ - - bzero (reg_class_size, sizeof reg_class_size); - for (i = 0; i < N_REG_CLASSES; i++) - for (j = 0; j < FIRST_PSEUDO_REGISTER; j++) - if (TEST_HARD_REG_BIT (reg_class_contents[i], j)) - reg_class_size[i]++; - - /* Initialize the table of subunions. - reg_class_subunion[I][J] gets the largest-numbered reg-class - that is contained in the union of classes I and J. */ - - for (i = 0; i < N_REG_CLASSES; i++) - { - for (j = 0; j < N_REG_CLASSES; j++) - { -#ifdef HARD_REG_SET - register /* Declare it register if it's a scalar. */ -#endif - HARD_REG_SET c; - register int k; - - COPY_HARD_REG_SET (c, reg_class_contents[i]); - IOR_HARD_REG_SET (c, reg_class_contents[j]); - for (k = 0; k < N_REG_CLASSES; k++) - { - GO_IF_HARD_REG_SUBSET (reg_class_contents[k], c, - subclass1); - continue; - - subclass1: - reg_class_subunion[i][j] = (enum reg_class) k; - } - } - } - - /* Initialize the tables of subclasses and superclasses of each reg class. - First clear the whole table, then add the elements as they are found. */ - - for (i = 0; i < N_REG_CLASSES; i++) - { - for (j = 0; j < N_REG_CLASSES; j++) - { - reg_class_superclasses[i][j] = LIM_REG_CLASSES; - reg_class_subclasses[i][j] = LIM_REG_CLASSES; - } - } - - for (i = 0; i < N_REG_CLASSES; i++) - { - if (i == (int) NO_REGS) - continue; - - for (j = i + 1; j < N_REG_CLASSES; j++) - { - enum reg_class *p; - - GO_IF_HARD_REG_SUBSET (reg_class_contents[i], reg_class_contents[j], - subclass); - continue; - subclass: - /* Reg class I is a subclass of J. - Add J to the table of superclasses of I. */ - p = ®_class_superclasses[i][0]; - while (*p != LIM_REG_CLASSES) p++; - *p = (enum reg_class) j; - /* Add I to the table of superclasses of J. */ - p = ®_class_subclasses[j][0]; - while (*p != LIM_REG_CLASSES) p++; - *p = (enum reg_class) i; - } - } - -} - -/* After switches have been processed, which perhaps alter - `fixed_regs' and `call_used_regs', convert them to HARD_REG_SETs. */ - -void -init_reg_sets_1 () -{ - register int i; - - /* This macro allows the fixed or call-used registers - to depend on target flags. */ - -#ifdef CONDITIONAL_REGISTER_USAGE - CONDITIONAL_REGISTER_USAGE; -#endif - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (global_regs[i]) - { - if (call_used_regs[i] && ! fixed_regs[i]) - warning ("call-clobbered register used for global register variable"); - fixed_regs[i] = 1; - /* Prevent saving/restoring of this reg. */ - call_used_regs[i] = 1; - } - - /* Initialize "constant" tables. */ - - CLEAR_HARD_REG_SET (fixed_reg_set); - CLEAR_HARD_REG_SET (call_used_reg_set); - CLEAR_HARD_REG_SET (call_fixed_reg_set); - - bcopy (fixed_regs, call_fixed_regs, sizeof call_fixed_regs); -#ifdef STRUCT_VALUE_REGNUM - call_fixed_regs[STRUCT_VALUE_REGNUM] = 1; -#endif -#ifdef STATIC_CHAIN_REGNUM - call_fixed_regs[STATIC_CHAIN_REGNUM] = 1; -#endif - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - if (FUNCTION_VALUE_REGNO_P (i)) - call_fixed_regs[i] = 1; - if (fixed_regs[i]) - SET_HARD_REG_BIT (fixed_reg_set, i); - if (call_used_regs[i]) - SET_HARD_REG_BIT (call_used_reg_set, i); - if (call_fixed_regs[i]) - SET_HARD_REG_BIT (call_fixed_reg_set, i); - } -} - -/* Specify the usage characteristics of the register named NAME. - It should be a fixed register if FIXED and a - call-used register if CALL_USED. */ - -void -fix_register (name, fixed, call_used) - char *name; - int fixed, call_used; -{ - int i; - - /* Decode the name and update the primary form of - the register info. */ - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (!strcmp (reg_names[i], name)) - { - fixed_regs[i] = fixed; - call_used_regs[i] = call_used; - break; - } - - if (i == FIRST_PSEUDO_REGISTER) - { - warning ("unknown register name: %s", name); - return; - } -} - -/* Now the data and code for the `regclass' pass, which happens - just before local-alloc. */ - -/* savings[R].savings[CL] is twice the amount saved by putting register R - in class CL. This data is used within `regclass' and freed - when it is finished. */ - -struct savings -{ - short savings[N_REG_CLASSES]; - short memcost; - short nrefs; -}; - -static struct savings *savings; - -/* (enum reg_class) prefclass[R] is the preferred class for pseudo number R. - This is available after `regclass' is run. */ - -static char *prefclass; - -/* preferred_or_nothing[R] is nonzero if we should put pseudo number R - in memory if we can't get its perferred class. - This is available after `regclass' is run. */ - -static char *preferred_or_nothing; - -void reg_class_record (); -void record_address_regs (); - - -/* Return the reg_class in which pseudo reg number REGNO is best allocated. - This function is sometimes called before the info has been computed. - When that happens, just return GENERAL_REGS, which is innocuous. */ - -enum reg_class -reg_preferred_class (regno) - int regno; -{ - if (prefclass == 0) - return GENERAL_REGS; - return (enum reg_class) prefclass[regno]; -} - -int -reg_preferred_or_nothing (regno) -{ - if (prefclass == 0) - return 0; - return preferred_or_nothing[regno]; -} - -/* This prevents dump_flow_info from losing if called - before regclass is run. */ - -int -regclass_init () -{ - prefclass = 0; -} - -/* This is a pass of the compiler that scans all instructions - and calculates the preferred class for each pseudo-register. - This information can be accessed later by calling `reg_preferred_class'. - This pass comes just before local register allocation. */ - -void -regclass (f, nregs) - rtx f; - int nregs; -{ -#ifdef REGISTER_CONSTRAINTS - register rtx insn; - register int i; - - init_recog (); - - /* Zero out our accumulation of the cost of each class for each reg. */ - - savings = (struct savings *) alloca (nregs * sizeof (struct savings)); - bzero (savings, nregs * sizeof (struct savings)); - - /* Scan the instructions and record each time it would - save code to put a certain register in a certain class. */ - - for (insn = f; insn; insn = NEXT_INSN (insn)) - if ((GET_CODE (insn) == INSN - && GET_CODE (PATTERN (insn)) != USE - && GET_CODE (PATTERN (insn)) != CLOBBER - && GET_CODE (PATTERN (insn)) != ASM_INPUT) - || (GET_CODE (insn) == JUMP_INSN - && GET_CODE (PATTERN (insn)) != ADDR_VEC - && GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC) - || GET_CODE (insn) == CALL_INSN) - { - if (GET_CODE (insn) == INSN && asm_noperands (PATTERN (insn)) >= 0) - { - int noperands = asm_noperands (PATTERN (insn)); - /* We don't use alloca because alloca would not free - any of the space until this function returns. */ - rtx *operands = (rtx *) oballoc (noperands * sizeof (rtx)); - char **constraints - = (char **) oballoc (noperands * sizeof (char *)); - - decode_asm_operands (PATTERN (insn), operands, 0, constraints, 0); - - for (i = noperands - 1; i >= 0; i--) - reg_class_record (operands[i], i, constraints); - - obfree (operands); - } - else - { - int insn_code_number = recog_memoized (insn); - - insn_extract (insn); - - for (i = insn_n_operands[insn_code_number] - 1; i >= 0; i--) - reg_class_record (recog_operand[i], i, - insn_operand_constraint[insn_code_number]); - - /* Improve handling of two-address insns such as - (set X (ashift CONST Y)) where CONST must be made to match X. - Change it into two insns: (set X CONST) (set X (ashift X Y)). - If we left this for reloading, it would probably get three insns - because X and Y might go in the same place. - This prevents X and Y from receiving the same hard reg. */ - - if (optimize - && insn_n_operands[insn_code_number] >= 3 - && insn_operand_constraint[insn_code_number][1][0] == '0' - && insn_operand_constraint[insn_code_number][1][1] == 0 - && CONSTANT_P (recog_operand[1]) - && ! rtx_equal_p (recog_operand[0], recog_operand[1]) - && ! rtx_equal_p (recog_operand[0], recog_operand[2]) - && GET_CODE (recog_operand[0]) == REG) - { - rtx previnsn = prev_real_insn (insn); - rtx newinsn - = emit_insn_before (gen_move_insn (recog_operand[0], - recog_operand[1]), - insn); - - /* If this insn was the start of a basic block, - include the new insn in that block. */ - if (previnsn == 0 || GET_CODE (previnsn) == JUMP_INSN) - { - int b; - for (b = 0; b < n_basic_blocks; b++) - if (insn == basic_block_head[b]) - basic_block_head[b] = newinsn; - } - - /* This makes one more setting of new insns's destination. */ - reg_n_sets[REGNO (recog_operand[0])]++; - - *recog_operand_loc[1] = recog_operand[0]; - for (i = insn_n_dups[insn_code_number] - 1; i >= 0; i--) - if (recog_dup_num[i] == 1) - *recog_dup_loc[i] = recog_operand[0]; - - - } - } - } - - /* Now for each register look at how desirable each class is - and find which class is preferred. Store that in `prefclass[REGNO]'. */ - - prefclass = (char *) oballoc (nregs); - - preferred_or_nothing = (char *) oballoc (nregs); - - for (i = FIRST_PSEUDO_REGISTER; i < nregs; i++) - { - register int best_savings = 0; - enum reg_class best = ALL_REGS; - - /* This is an enum reg_class, but we call it an int - to save lots of casts. */ - register int class; - register struct savings *p = &savings[i]; - - for (class = (int) ALL_REGS - 1; class > 0; class--) - { - if (p->savings[class] > best_savings) - { - best_savings = p->savings[class]; - best = (enum reg_class) class; - } - else if (p->savings[class] == best_savings) - { - best = reg_class_subunion[(int)best][class]; - } - } - -#if 0 - /* Note that best_savings is twice number of places something - is saved. */ - if ((best_savings - p->savings[(int) GENERAL_REGS]) * 5 < reg_n_refs[i]) - prefclass[i] = (int) GENERAL_REGS; - else - prefclass[i] = (int) best; -#else - /* We cast to (int) because (char) hits bugs in some compilers. */ - prefclass[i] = (int) best; -#endif - - /* reg_n_refs + p->memcost measures the cost of putting in memory. - If a GENERAL_REG is no better, don't even try for one. - Since savings and memcost are 2 * number of refs, - this effectively counts each memory operand not needing reloading - as costing 1/2 of a reload insn. */ - if (reg_n_refs != 0) - preferred_or_nothing[i] - = ((best_savings - p->savings[(int) GENERAL_REGS]) - >= p->nrefs + p->memcost); - } -#endif /* REGISTER_CONSTRAINTS */ -} - -#ifdef REGISTER_CONSTRAINTS - -/* Scan an operand OP for register class preferences. - OPNO is the operand number, and CONSTRAINTS is the constraint - vector for the insn. - - Record the preferred register classes from the constraint for OP - if OP is a register. If OP is a memory reference, record suitable - preferences for registers used in the address. */ - -void -reg_class_record (op, opno, constraints) - rtx op; - int opno; - char **constraints; -{ - char *constraint = constraints[opno]; - register char *p; - register enum reg_class class = NO_REGS; - char *next = 0; - int memok = 0; - int double_cost = 0; - - while (1) - { - if (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - else break; - } - - /* Memory reference: scan the address. */ - - if (GET_CODE (op) == MEM) - record_address_regs (XEXP (op, 0), 2, 0); - - if (GET_CODE (op) != REG) - { - /* If the constraint says the operand is supposed to BE an address, - scan it as one. */ - - if (constraint != 0 && constraint[0] == 'p') - record_address_regs (op, 2, 0); - return; - } - - /* Operand is a register: examine the constraint for specified classes. */ - - for (p = constraint; *p || next; p++) - { - if (*p == 0) - { - p = next; - next = 0; - } - switch (*p) - { - case '=': - case '?': - case '#': - case '&': - case '!': - case '%': - case 'F': - case 'G': - case 'H': - case 'i': - case 'n': - case 's': - case 'p': - case ',': - break; - - case '+': - /* An input-output operand is twice as costly if it loses. */ - double_cost = 1; - break; - - case 'm': - case 'o': - memok = 1; - break; - - /* * means ignore following letter - when choosing register preferences. */ - case '*': - p++; - break; - - case 'g': - case 'r': - class - = reg_class_subunion[(int) class][(int) GENERAL_REGS]; - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - /* If constraint says "match another operand", - use that operand's constraint to choose preferences. */ - next = constraints[*p - '0']; - break; - - default: - class - = reg_class_subunion[(int) class][(int) REG_CLASS_FROM_LETTER (*p)]; - } - } - - { - register int i; - register struct savings *pp; - register enum reg_class class1; - int cost = 2 * (1 + double_cost); - pp = &savings[REGNO (op)]; - - /* Increment the savings for this reg - for each class contained in the one the constraint asks for. */ - - if (class != NO_REGS && class != ALL_REGS) - { - pp->savings[(int) class] += cost; - for (i = 0; ; i++) - { - class1 = reg_class_subclasses[(int)class][i]; - if (class1 == LIM_REG_CLASSES) - break; - pp->savings[(int) class1] += cost; - } - } - - if (! memok) - pp->memcost += 1 + 2 * double_cost; - pp->nrefs++; - } -} - -/* Record the pseudo registers we must reload into hard registers - in a subexpression of a memory address, X. - BCOST is the cost if X is a register and it fails to be in BASE_REG_CLASS. - ICOST is the cost if it fails to be in INDEX_REG_CLASS. */ - -void -record_address_regs (x, bcost, icost) - rtx x; - int bcost, icost; -{ - register enum rtx_code code = GET_CODE (x); - - switch (code) - { - case CONST_INT: - case CONST: - case CC0: - case PC: - case SYMBOL_REF: - case LABEL_REF: - return; - - case PLUS: - /* When we have an address that is a sum, - we must determine whether registers are "base" or "index" regs. - If there is a sum of two registers, we must choose one to be - the "base". Luckily, we can use the REGNO_POINTER_FLAG - to make a good choice most of the time. */ - { - rtx arg0 = XEXP (x, 0); - rtx arg1 = XEXP (x, 1); - register enum rtx_code code0 = GET_CODE (arg0); - register enum rtx_code code1 = GET_CODE (arg1); - int icost0 = 0; - int icost1 = 0; - int suppress1 = 0; - int suppress0 = 0; - - /* Look inside subregs. */ - while (code0 == SUBREG) - arg0 = SUBREG_REG (arg0), code0 = GET_CODE (arg0); - while (code1 == SUBREG) - arg1 = SUBREG_REG (arg1), code1 = GET_CODE (arg1); - - if (code0 == MULT || code1 == MEM) - icost0 = 2; - else if (code1 == MULT || code0 == MEM) - icost1 = 2; - else if (code0 == CONST_INT) - suppress0 = 1; - else if (code1 == CONST_INT) - suppress1 = 1; - else if (code0 == REG && code1 == REG) - { - if (REGNO_POINTER_FLAG (REGNO (arg0))) - icost1 = 2; - else if (REGNO_POINTER_FLAG (REGNO (arg1))) - icost0 = 2; - else - icost0 = icost1 = 1; - } - else if (code0 == REG) - { - if (code1 == PLUS - && ! REGNO_POINTER_FLAG (REGNO (arg0))) - icost0 = 2; - else - REGNO_POINTER_FLAG (REGNO (arg0)) = 1; - } - else if (code1 == REG) - { - if (code0 == PLUS - && ! REGNO_POINTER_FLAG (REGNO (arg1))) - icost1 = 2; - else - REGNO_POINTER_FLAG (REGNO (arg1)) = 1; - } - - /* ICOST0 determines whether we are treating operand 0 - as a base register or as an index register. - SUPPRESS0 nonzero means it isn't a register at all. - ICOST1 and SUPPRESS1 are likewise for operand 1. */ - - if (! suppress0) - record_address_regs (arg0, 2 - icost0, icost0); - if (! suppress1) - record_address_regs (arg1, 2 - icost1, icost1); - } - break; - - case POST_INC: - case PRE_INC: - case POST_DEC: - case PRE_DEC: - /* Double the importance of a pseudo register that is incremented - or decremented, since it would take two extra insns - if it ends up in the wrong place. */ - record_address_regs (XEXP (x, 0), 2 * bcost, 2 * icost); - break; - - case REG: - { - register struct savings *pp; - register enum reg_class class, class1; - pp = &savings[REGNO (x)]; - pp->nrefs++; - - /* We have an address (or part of one) that is just one register. */ - - /* Record BCOST worth of savings for classes contained - in BASE_REG_CLASS. */ - - class = BASE_REG_CLASS; - if (class != NO_REGS && class != ALL_REGS) - { - register int i; - pp->savings[(int) class] += bcost; - for (i = 0; ; i++) - { - class1 = reg_class_subclasses[(int)class][i]; - if (class1 == LIM_REG_CLASSES) - break; - pp->savings[(int) class1] += bcost; - } - } - - /* Record ICOST worth of savings for classes contained - in INDEX_REG_CLASS. */ - - class = INDEX_REG_CLASS; - if (icost != 0 && class != NO_REGS && class != ALL_REGS) - { - register int i; - pp->savings[(int) class] += icost; - for (i = 0; ; i++) - { - class1 = reg_class_subclasses[(int)class][i]; - if (class1 == LIM_REG_CLASSES) - break; - pp->savings[(int) class1] += icost; - } - } - } - break; - - default: - { - register char *fmt = GET_RTX_FORMAT (code); - register int i; - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - record_address_regs (XEXP (x, i), bcost, icost); - } - } -} -#endif /* REGISTER_CONSTRAINTS */ - -/* This is the `regscan' pass of the compiler, run just before cse - and again just before loop. - - It finds the first and last use of each pseudo-register - and records them in the vectors regno_first_uid, regno_last_uid. - REPEAT is nonzero the second time this is called. */ - -/* Indexed by pseudo register number, gives uid of first insn using the reg - (as of the time reg_scan is called). */ - -short *regno_first_uid; - -/* Indexed by pseudo register number, gives uid of last insn using the reg - (as of the time reg_scan is called). */ - -short *regno_last_uid; - -/* Maximum number of parallel sets and clobbers in any insn in this fn. - Always at least 3, since the combiner could put that many togetherm - and we want this to remain correct for all the remaining passes. */ - -int max_parallel; - -void reg_scan_mark_refs (); - -void -reg_scan (f, nregs, repeat) - rtx f; - int nregs; - int repeat; -{ - register rtx insn; - - if (!repeat) - regno_first_uid = (short *) oballoc (nregs * sizeof (short)); - bzero (regno_first_uid, nregs * sizeof (short)); - - if (!repeat) - regno_last_uid = (short *) oballoc (nregs * sizeof (short)); - bzero (regno_last_uid, nregs * sizeof (short)); - - max_parallel = 3; - - for (insn = f; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == INSN - || GET_CODE (insn) == CALL_INSN - || GET_CODE (insn) == JUMP_INSN) - { - if (GET_CODE (PATTERN (insn)) == PARALLEL - && XVECLEN (PATTERN (insn), 0) > max_parallel) - max_parallel = XVECLEN (PATTERN (insn), 0); - reg_scan_mark_refs (PATTERN (insn), INSN_UID (insn)); - } -} - -void -reg_scan_mark_refs (x, uid) - rtx x; - int uid; -{ - register enum rtx_code code = GET_CODE (x); - - switch (code) - { - case CONST_INT: - case CONST: - case CONST_DOUBLE: - case CC0: - case PC: - case SYMBOL_REF: - case LABEL_REF: - case ADDR_VEC: - case ADDR_DIFF_VEC: - return; - - case REG: - { - register int regno = REGNO (x); - - regno_last_uid[regno] = uid; - if (regno_first_uid[regno] == 0) - regno_first_uid[regno] = uid; - } - break; - - default: - { - register char *fmt = GET_RTX_FORMAT (code); - register int i; - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - reg_scan_mark_refs (XEXP (x, i), uid); - else if (fmt[i] == 'E' && XVEC (x, i) != 0) - { - register int j; - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - reg_scan_mark_refs (XVECEXP (x, i, j), uid); - } - } - } - } -} diff --git a/gnu/usr.bin/gcc1/cc1/regs.h b/gnu/usr.bin/gcc1/cc1/regs.h deleted file mode 100644 index 90a999f206..0000000000 --- a/gnu/usr.bin/gcc1/cc1/regs.h +++ /dev/null @@ -1,146 +0,0 @@ -/* Define per-register tables for data flow info and register allocation. - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - - -#define REG_BYTES(R) mode_size[(int) GET_MODE (R)] - -/* Get the number of consecutive hard regs required to hold the REG rtx R. - When something may be an explicit hard reg, REG_SIZE is the only - valid way to get this value. You cannot get it from the regno. */ - -#define REG_SIZE(R) \ - ((mode_size[(int) GET_MODE (R)] + UNITS_PER_WORD - 1) / UNITS_PER_WORD) - -/* Maximum register number used in this function, plus one. */ - -extern int max_regno; - -/* Indexed by n, gives number of times (REG n) is used or set. - References within loops may be counted more times. */ - -extern short *reg_n_refs; - -/* Indexed by n, gives number of times (REG n) is set. */ - -extern short *reg_n_sets; - -/* Indexed by N, gives number of insns in which register N dies. - Note that if register N is live around loops, it can die - in transitions between basic blocks, and that is not counted here. - So this is only a reliable indicator of how many regions of life there are - for registers that are contained in one basic block. */ - -extern short *reg_n_deaths; - -/* Indexed by N, gives the first insn that mentions reg N, - provided that reg is local to one basic block. - The value here is undefined otherwise. */ - -extern rtx *reg_first_use; - -/* Get the number of consecutive words required to hold pseudo-reg N. */ - -#define PSEUDO_REGNO_SIZE(N) \ - ((GET_MODE_SIZE (PSEUDO_REGNO_MODE (N)) + UNITS_PER_WORD - 1) \ - / UNITS_PER_WORD) - -/* Get the number of bytes required to hold pseudo-reg N. */ - -#define PSEUDO_REGNO_BYTES(N) \ - GET_MODE_SIZE (PSEUDO_REGNO_MODE (N)) - -/* Get the machine mode of pseudo-reg N. */ - -#define PSEUDO_REGNO_MODE(N) GET_MODE (regno_reg_rtx[N]) - -/* Indexed by N, gives number of CALL_INSNS across which (REG n) is live. */ - -extern int *reg_n_calls_crossed; - -/* Total number of instructions at which (REG n) is live. - The larger this is, the less priority (REG n) gets for - allocation in a hard register (in global-alloc). - This is set in flow.c and remains valid for the rest of the compilation - of the function; it is used to control register allocation. - - local-alloc.c may alter this number to change the priority. - - Negative values are special. - -1 is used to mark a pseudo reg which has a constant or memory equivalent - and is used infrequently enough that it should not get a hard register. - -2 is used to mark a pseudo reg for a parameter, when a frame pointer - is not required. global-alloc.c makes an allocno for this but does - not try to assign a hard register to it. */ - -extern int *reg_live_length; - -/* Vector of substitutions of register numbers, - used to map pseudo regs into hardware regs. */ - -extern short *reg_renumber; - -/* Vector indexed by hardware reg - saying whether that reg is ever used. */ - -extern char regs_ever_live[FIRST_PSEUDO_REGISTER]; - -/* Vector indexed by hardware reg giving its name. */ - -extern char *reg_names[FIRST_PSEUDO_REGISTER]; - -/* Vector indexed by regno; gives uid of first insn using that reg. - This is computed by reg_scan for use by cse and loop. - It is sometimes adjusted for subsequent changes during loop, - but not adjusted by cse even if cse invalidates it. */ - -extern short *regno_first_uid; - -/* Vector indexed by regno; gives uid of last insn using that reg. - This is computed by reg_scan for use by cse and loop. - It is sometimes adjusted for subsequent changes during loop, - but not adjusted by cse even if cse invalidates it. - This is harmless since cse won't scan through a loop end. */ - -extern short *regno_last_uid; - -/* Vector indexed by regno; contains 1 for a register is considered a pointer. - Reloading, etc. will use a pointer register rather than a non-pointer - as the base register in an address, when there is a choice of two regs. */ - -extern char *regno_pointer_flag; -#define REGNO_POINTER_FLAG(REGNO) regno_pointer_flag[REGNO] - -/* Vector mapping pseudo regno into the REG rtx for that register. - This is computed by reg_scan. */ - -extern rtx *regno_reg_rtx; - -/* Flag set by local-alloc or global-alloc if they decide to allocate - something in a call-clobbered register. */ - -extern int caller_save_needed; - -/* Predicate to decide whether to give a hard reg to a pseudo which - is referenced REFS times and would need to be saved and restored - around a call CALLS times. */ - -#ifndef CALLER_SAVE_PROFITABLE -#define CALLER_SAVE_PROFITABLE(REFS, CALLS) (4 * (CALLS) < (REFS)) -#endif diff --git a/gnu/usr.bin/gcc1/cc1/reload.c b/gnu/usr.bin/gcc1/cc1/reload.c deleted file mode 100644 index 40f0ca21a7..0000000000 --- a/gnu/usr.bin/gcc1/cc1/reload.c +++ /dev/null @@ -1,3291 +0,0 @@ -/* Search an insn for pseudo regs that must be in hard regs and are not. - Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This file contains subroutines used only from the file reload1.c. - It knows how to scan one insn for operands and values - that need to be copied into registers to make valid code. - It also finds other operands and values which are valid - but for which equivalent values in registers exist and - ought to be used instead. - - Before processing the first insn of the function, call `init_reload'. - - To scan an insn, call `find_reloads'. This does two things: - 1. sets up tables describing which values must be reloaded - for this insn, and what kind of hard regs they must be reloaded into; - 2. optionally record the locations where those values appear in - the data, so they can be replaced properly later. - This is done only if the second arg to `find_reloads' is nonzero. - - The third arg to `find_reloads' specifies the value of `indirect_ok'. - - Then you must choose the hard regs to reload those pseudo regs into, - and generate appropriate load insns before this insn and perhaps - also store insns after this insn. Set up the array `reload_reg_rtx' - to contain the REG rtx's for the registers you used. In some - cases `find_reloads' will return a nonzero value in `reload_reg_rtx' - for certain reloads. Then that tells you which register to use, - so you do not need to allocate one. But you still do need to add extra - instructions to copy the value into and out of that register. - - Finally you must call `subst_reloads' to substitute the reload reg rtx's - into the locations already recorded. - -NOTE SIDE EFFECTS: - - find_reloads can alter the operands of the instruction it is called on. - - 1. Two operands of any sort may be interchanged, if they are in a - commutative instruction. - This happens only if find_reloads thinks the instruction will compile - better that way. - - 2. Pseudo-registers that are equivalent to constants are replaced - with those constants if they are not in hard registers. - -1 happens every time find_reloads is called. -2 happens only when REPLACE is 1, which is only when -actually doing the reloads, not when just counting them. - - -Using a reload register for several reloads in one insn: - -When an insn has reloads, it is considered as having three parts: -the input reloads, the insn itself after reloading, and the output reloads. -Reloads of values used in memory addresses are often needed for only one part. - -When this is so, reload_when_needed records which part needs the reload. -Two reloads for different parts of the insn can share the same reload -register. - -When a reload is used for addresses in multiple parts, or when it is -an ordinary operand, it is classified as RELOAD_OTHER, and cannot share -a register with any other reload. */ - -#define REG_OK_STRICT - -#include "config.h" -#include "rtl.h" -#include "insn-config.h" -#include "recog.h" -#include "reload.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "flags.h" -#include "real.h" - -#define min(x,y) ((x) < (y) ? (x) : (y)) - -/* The variables set up by `find_reloads' are: - - n_reloads number of distinct reloads needed; max reload # + 1 - tables indexed by reload number - reload_in rtx for value to reload from - reload_out rtx for where to store reload-reg afterward if nec - (often the same as reload_in) - reload_reg_class enum reg_class, saying what regs to reload into - reload_inmode enum machine_mode; mode this operand should have - when reloaded, on input. - reload_outmode enum machine_mode; mode this operand should have - when reloaded, on output. - reload_strict_low char; 1 if this reload is inside a STRICT_LOW_PART. - reload_optional char, nonzero for an optional reload. - Optional reloads are ignored unless the - value is already sitting in a register. - reload_inc int, positive amount to increment or decrement by if - reload_in is a PRE_DEC, PRE_INC, POST_DEC, POST_INC. - Ignored otherwise (don't assume it is zero). - reload_in_reg rtx. A reg for which reload_in is the equivalent. - If reload_in is a symbol_ref which came from - reg_equiv_constant, then this is the pseudo - which has that symbol_ref as equivalent. - reload_reg_rtx rtx. This is the register to reload into. - If it is zero when `find_reloads' returns, - you must find a suitable register in the class - specified by reload_reg_class, and store here - an rtx for that register with mode from - reload_inmode or reload_outmode. - reload_nocombine char, nonzero if this reload shouldn't be - combined with another reload. - reload_needed_for rtx, operand this reload is needed for address of. - 0 means it isn't needed for addressing. - reload_needed_for_multiple - int, 1 if this reload needed for more than one thing. - reload_when_needed enum, classifies reload as needed either for - addressing an input reload, addressing an output, - for addressing a non-reloaded mem ref, - or for unspecified purposes (i.e., more than one - of the above). */ - -int n_reloads; - -rtx reload_in[MAX_RELOADS]; -rtx reload_out[MAX_RELOADS]; -enum reg_class reload_reg_class[MAX_RELOADS]; -enum machine_mode reload_inmode[MAX_RELOADS]; -enum machine_mode reload_outmode[MAX_RELOADS]; -char reload_strict_low[MAX_RELOADS]; -rtx reload_reg_rtx[MAX_RELOADS]; -char reload_optional[MAX_RELOADS]; -int reload_inc[MAX_RELOADS]; -rtx reload_in_reg[MAX_RELOADS]; -char reload_nocombine[MAX_RELOADS]; -int reload_needed_for_multiple[MAX_RELOADS]; -rtx reload_needed_for[MAX_RELOADS]; -enum reload_when_needed reload_when_needed[MAX_RELOADS]; - -/* All the "earlyclobber" operands of the current insn - are recorded here. */ -int n_earlyclobbers; -rtx reload_earlyclobbers[MAX_RECOG_OPERANDS]; - -/* Replacing reloads. - - If `replace_reloads' is nonzero, then as each reload is recorded - an entry is made for it in the table `replacements'. - Then later `subst_reloads' can look through that table and - perform all the replacements needed. */ - -/* Nonzero means record the places to replace. */ -static int replace_reloads; - -/* Each replacement is recorded with a structure like this. */ -struct replacement -{ - rtx *where; /* Location to store in */ - int what; /* which reload this is for */ - enum machine_mode mode; /* mode it must have */ -}; - -static struct replacement replacements[MAX_RECOG_OPERANDS * ((MAX_REGS_PER_ADDRESS * 2) + 1)]; - -/* Number of replacements currently recorded. */ -static int n_replacements; - -/* MEM-rtx's created for pseudo-regs in stack slots not directly addressable; - (see reg_equiv_address). */ -static rtx memlocs[MAX_RECOG_OPERANDS * ((MAX_REGS_PER_ADDRESS * 2) + 1)]; -static int n_memlocs; - -/* The instruction we are doing reloads for; - so we can test whether a register dies in it. */ -static rtx this_insn; - -/* Nonzero means (MEM (REG n)) is valid even if (REG n) is spilled. */ -static int indirect_ok; - -/* If hard_regs_live_known is nonzero, - we can tell which hard regs are currently live, - at least enough to succeed in choosing dummy reloads. */ -static int hard_regs_live_known; - -/* Indexed by hard reg number, - element is nonegative if hard reg has been spilled. - This vector is passed to `find_reloads' as an argument - and is not changed here. */ -static short *static_reload_reg_p; - -/* Set to 1 in subst_reg_equivs if it changes anything. */ -static int subst_reg_equivs_changed; - -/* On return from push_reload, holds the reload-number for the OUT - operand, which can be different for that from the input operand. */ -static int output_reloadnum; - -static int alternative_allows_memconst (); -static rtx find_dummy_reload (); -static rtx find_reloads_toplev (); -static int find_reloads_address (); -static int find_reloads_address_1 (); -static int hard_reg_set_here_p (); -/* static rtx forget_volatility (); */ -static rtx subst_reg_equivs (); -static rtx subst_indexed_address (); -rtx find_equiv_reg (); -static int find_inc_amount (); - -/* Record one (sometimes two) reload that needs to be performed. - IN is an rtx saying where the data are to be found before this instruction. - OUT says where they must be stored after the instruction. - (IN is zero for data not read, and OUT is zero for data not written.) - INLOC and OUTLOC point to the places in the instructions where - IN and OUT were found. - CLASS is a register class required for the reloaded data. - INMODE is the machine mode that the instruction requires - for the reg that replaces IN and OUTMODE is likewise for OUT. - - If IN is zero, then OUT's location and mode should be passed as - INLOC and INMODE. - - STRICT_LOW is the 1 if there is a containing STRICT_LOW_PART rtx. - - OPTIONAL nonzero means this reload does not need to be performed: - it can be discarded if that is more convenient. - - The return value is the reload-number for this reload. - - If both IN and OUT are nonzero, in some rare cases we might - want to make two separate reloads. (Actually we never do this now.) - Therefore, the reload-number for OUT is stored in - output_reloadnum when we return; the return value applies to IN. - Usually (presently always), when IN and OUT are nonzero, - the two reload-numbers are equal, but the caller should be careful to - distinguish them. */ - -static int -push_reload (in, out, inloc, outloc, class, - inmode, outmode, strict_low, optional, needed_for) - register rtx in, out; - rtx *inloc, *outloc; - enum reg_class class; - enum machine_mode inmode, outmode; - int strict_low; - int optional; - rtx needed_for; -{ - register int i; - int dont_share = 0; - - /* Compare two RTX's. */ -#define MATCHES(x, y) (x == y || (x != 0 && GET_CODE (x) != REG && rtx_equal_p (x, y))) - - /* INMODE and/or OUTMODE could be VOIDmode if no mode - has been specified for the operand. In that case, - use the operand's mode as the mode to reload. */ - if (inmode == VOIDmode && in != 0) - inmode = GET_MODE (in); - if (outmode == VOIDmode && out != 0) - outmode = GET_MODE (out); - - /* If IN is a pseudo register everywhere-equivalent to a constant, and - it is not in a hard register, reload straight from the constant, - since we want to get rid of such pseudo registers. - Often this is done earlier, but not always in find_reloads_address. */ - if (in != 0 && GET_CODE (in) == REG) - { - register int regno = REGNO (in); - - if (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0 - && reg_equiv_constant[regno] != 0) - in = reg_equiv_constant[regno]; - } - - /* Likewise for OUT. Of course, OUT will never be equivalent to - an actual constant, but it might be equivalent to a memory location - (in the case of a parameter). */ - if (out != 0 && GET_CODE (out) == REG) - { - register int regno = REGNO (out); - - if (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0 - && reg_equiv_constant[regno] != 0) - out = reg_equiv_constant[regno]; - } - - /* If we have a read-write operand with an address side-effect, - change either IN or OUT so the side-effect happens only once. */ - if (in != 0 && out != 0 && GET_CODE (in) == MEM && rtx_equal_p (in, out)) - { - if (GET_CODE (XEXP (in, 0)) == POST_INC - || GET_CODE (XEXP (in, 0)) == POST_DEC) - in = gen_rtx (MEM, GET_MODE (in), XEXP (XEXP (in, 0), 0)); - if (GET_CODE (XEXP (in, 0)) == PRE_INC - || GET_CODE (XEXP (in, 0)) == PRE_DEC) - out = gen_rtx (MEM, GET_MODE (out), XEXP (XEXP (out, 0), 0)); - } - - /* If we are reloading a (SUBREG (MEM ...) ...) or (SUBREG constant ...), - really reload just the inside expression in its own mode. - Note that the case of (SUBREG (CONST_INT...)...) is handled elsewhere; - we can't handle it here because CONST_INT does not indicate a mode. */ - - if (in != 0 && GET_CODE (in) == SUBREG && GET_CODE (SUBREG_REG (in)) != REG) - { - inloc = &SUBREG_REG (in); - in = *inloc; - if (GET_CODE (SUBREG_REG (in)) == MEM) - /* This is supposed to happen only for paradoxical subregs made by - combine.c. (SUBREG (MEM)) isn't supposed to occur other ways. */ - if (GET_MODE_SIZE (GET_MODE (in)) > GET_MODE_SIZE (inmode)) - abort (); - inmode = GET_MODE (in); - } - - /* If IN appears in OUT, we can't share any input-only reload for IN. */ - if (in != 0 && out != 0 && reg_overlap_mentioned_p (in, out)) - dont_share = 1; - - /* Narrow down the class of register wanted if that is - desirable on this machine for efficiency. */ - if (in != 0) - class = PREFERRED_RELOAD_CLASS (in, class); - - if (class == NO_REGS) - abort (); - - /* We can use an existing reload if the class is right - and at least one of IN and OUT is a match - and the other is at worst neutral. - (A zero compared against anything is neutral.) */ - for (i = 0; i < n_reloads; i++) - if (reload_reg_class[i] == class - && reload_strict_low[i] == strict_low - && ((in != 0 && MATCHES (reload_in[i], in) && ! dont_share - && (out == 0 || reload_out[i] == 0 || MATCHES (reload_out[i], out))) - || - (out != 0 && MATCHES (reload_out[i], out) - && (in == 0 || reload_in[i] == 0 || MATCHES (reload_in[i], in))))) - break; - - /* Reloading a plain reg for input can match a reload to postincrement - that reg, since the postincrement's value is the right value. - Likewise, it can match a preincrement reload, since we regard - the preincrementation as happening before any ref in this insn - to that register. */ - if (i == n_reloads) - for (i = 0; i < n_reloads; i++) - if (reload_reg_class[i] == class - && reload_strict_low[i] == strict_low - && out == 0 && reload_out[i] == 0 - && ((GET_CODE (in) == REG - && (GET_CODE (reload_in[i]) == POST_INC - || GET_CODE (reload_in[i]) == POST_DEC - || GET_CODE (reload_in[i]) == PRE_INC - || GET_CODE (reload_in[i]) == PRE_DEC) - && MATCHES (XEXP (reload_in[i], 0), in)) - || - (GET_CODE (reload_in[i]) == REG - && (GET_CODE (in) == POST_INC - || GET_CODE (in) == POST_DEC - || GET_CODE (in) == PRE_INC - || GET_CODE (in) == PRE_DEC) - && MATCHES (XEXP (in, 0), reload_in[i])))) - { - /* Make sure reload_in ultimately has the increment, - not the plain register. */ - if (GET_CODE (in) == REG) - in = reload_in[i]; - break; - } - - if (i == n_reloads) - { - /* We found no existing reload suitable for re-use. - So add an additional reload. */ - - reload_in[i] = in; - reload_out[i] = out; - reload_reg_class[i] = class; - reload_inmode[i] = inmode; - reload_outmode[i] = outmode; - reload_reg_rtx[i] = 0; - reload_optional[i] = optional; - reload_inc[i] = 0; - reload_strict_low[i] = strict_low; - reload_nocombine[i] = 0; - reload_in_reg[i] = *inloc; - reload_needed_for[i] = needed_for; - reload_needed_for_multiple[i] = 0; - - n_reloads++; - } - else - { - /* We are reusing an existing reload, - but we may have additional information for it. - For example, we may now have both IN and OUT - while the old one may have just one of them. */ - - if (inmode != VOIDmode) - reload_inmode[i] = inmode; - if (outmode != VOIDmode) - reload_outmode[i] = outmode; - if (in != 0) - reload_in[i] = in; - if (out != 0) - reload_out[i] = out; - reload_optional[i] &= optional; - if (reload_needed_for[i] != needed_for) - reload_needed_for_multiple[i] = 1; - } - - /* If the ostensible rtx being reload differs from the rtx found - in the location to substitute, this reload is not safe to combine - because we cannot reliably tell whether it appears in the insn. */ - - if (in != 0 && in != *inloc) - reload_nocombine[i] = 1; - -#if 0 - /* This was replaced by changes in find_reloads_address_1 and the new - function inc_for_reload, which go with a new meaning of reload_inc. */ - - /* If this is an IN/OUT reload in an insn that sets the CC, - it must be for an autoincrement. It doesn't work to store - the incremented value after the insn because that would clobber the CC. - So we must do the increment of the value reloaded from, - increment it, store it back, then decrement again. */ - if (out != 0 && sets_cc0_p (PATTERN (this_insn))) - { - out = 0; - reload_out[i] = 0; - reload_inc[i] = find_inc_amount (PATTERN (this_insn), in); - /* If we did not find a nonzero amount-to-increment-by, - that contradicts the belief that IN is being incremented - in an address in this insn. */ - if (reload_inc[i] == 0) - abort (); - } -#endif - - /* If we will replace IN and OUT with the reload-reg, - record where they are located so that substitution need - not do a tree walk. */ - - if (replace_reloads) - { - if (inloc != 0) - { - register struct replacement *r = &replacements[n_replacements++]; - r->what = i; - r->where = inloc; - r->mode = inmode; - } - if (outloc != 0 && outloc != inloc) - { - register struct replacement *r = &replacements[n_replacements++]; - r->what = i; - r->where = outloc; - r->mode = outmode; - } - } - - /* If this reload is just being introduced and it has both - an incoming quantity and an outgoing quantity that are - supposed to be made to match, see if either one of the two - can serve as the place to reload into. - - If one of them is acceptable, set reload_reg_rtx[i] - to that one. */ - - if (in != 0 && out != 0 && in != out && reload_reg_rtx[i] == 0) - { - reload_reg_rtx[i] = find_dummy_reload (in, out, inloc, outloc, - reload_reg_class[i], i); - - /* If the outgoing register already contains the same value - as the incoming one, we can dispense with loading it. - The easiest way to tell the caller that is to give a phony - value for the incoming operand (same as outgoing one). */ - if (reload_reg_rtx[i] == out - && (GET_CODE (in) == REG || CONSTANT_P (in)) - && 0 != find_equiv_reg (in, this_insn, 0, REGNO (out), - static_reload_reg_p, i, inmode)) - reload_in[i] = out; - } - - if (out) - output_reloadnum = i; - - return i; -} - -/* Record an additional place we must replace a value - for which we have already recorded a reload. - RELOADNUM is the value returned by push_reload - when the reload was recorded. - This is used in insn patterns that use match_dup. */ - -static void -push_replacement (loc, reloadnum, mode) - rtx *loc; - int reloadnum; - enum machine_mode mode; -{ - if (replace_reloads) - { - register struct replacement *r = &replacements[n_replacements++]; - r->what = reloadnum; - r->where = loc; - r->mode = mode; - } -} - -/* If there is only one output reload, try to combine it - with a (logically unrelated) input reload - to reduce the number of reload registers needed. - - This is safe if the input reload does not appear in - the value being output-reloaded, because this implies - it is not needed any more once the original insn completes. */ - -static void -combine_reloads () -{ - int i; - int output_reload = -1; - - /* Find the output reload; return unless there is exactly one - and that one is mandatory. */ - - for (i = 0; i < n_reloads; i++) - if (reload_out[i] != 0) - { - if (output_reload >= 0) - return; - output_reload = i; - } - - if (output_reload < 0 || reload_optional[output_reload]) - return; - - /* An input-output reload isn't combinable. */ - - if (reload_in[output_reload] != 0) - return; - - /* Check each input reload; can we combine it? */ - - for (i = 0; i < n_reloads; i++) - if (reload_in[i] && ! reload_optional[i] && ! reload_nocombine[i] - /* Life span of this reload must not extend past main insn. */ - && reload_when_needed[i] != RELOAD_FOR_OUTPUT_RELOAD_ADDRESS - && reload_inmode[i] == reload_outmode[output_reload] - && reload_inc[i] == 0 - && reload_reg_rtx[i] == 0 - && reload_strict_low[i] == 0 - && reload_reg_class[i] == reload_reg_class[output_reload] - && ! reg_overlap_mentioned_p (reload_in[i], reload_out[output_reload])) - { - int j; - - /* We have found a reload to combine with! */ - reload_out[i] = reload_out[output_reload]; - reload_outmode[i] = reload_outmode[output_reload]; - /* Mark the old output reload as inoperative. */ - reload_out[output_reload] = 0; - /* The combined reload is needed for the entire insn. */ - reload_needed_for_multiple[i] = 1; - reload_when_needed[i] = RELOAD_OTHER; - - /* Transfer all replacements from the old reload to the combined. */ - for (j = 0; j < n_replacements; j++) - if (replacements[j].what == output_reload) - replacements[j].what = i; - - return; - } -} - -/* Try to find a reload register for an in-out reload (expressions IN and OUT). - See if one of IN and OUT is a register that may be used; - this is desirable since a spill-register won't be needed. - If so, return the register rtx that proves acceptable. - - INLOC and OUTLOC are locations where IN and OUT appear in the insn. - CLASS is the register class required for the reload. - - If FOR_REAL is >= 0, it is the number of the reload, - and in some cases when it can be discovered that OUT doesn't need - to be computed, clear out reload_out[FOR_REAL]. - - If FOR_REAL is -1, this should not be done, because this call - is just to see if a register can be found, not to find and install it. */ - -static rtx -find_dummy_reload (in, out, inloc, outloc, class, for_real) - rtx in, out; - rtx *inloc, *outloc; - enum reg_class class; - int for_real; -{ - rtx value = 0; - rtx orig_in = in; - - while (GET_CODE (out) == SUBREG) - out = SUBREG_REG (out); - while (GET_CODE (in) == SUBREG) - in = SUBREG_REG (in); - - /* If operands exceed a word, we can't use either of them - unless they have the same size. */ - if (GET_MODE_SIZE (GET_MODE (out)) != GET_MODE_SIZE (GET_MODE (in)) - && (GET_MODE_SIZE (GET_MODE (out)) > UNITS_PER_WORD - || GET_MODE_SIZE (GET_MODE (in)) > UNITS_PER_WORD)) - return 0; - - /* See if OUT will do. */ - if (GET_CODE (out) == REG) - { - register int regno = REGNO (out); - - /* When we consider whether the insn uses OUT, - ignore references within IN. They don't prevent us - from copying IN into OUT, because those refs would - move into the insn that reloads IN. - - However, we only ignore IN in its role as this operand. - If the insn uses IN elsewhere and it contains OUT, - that counts. We can't be sure it's the "same" operand - so it might not go through this reload. */ - *inloc = const0_rtx; - - if (reg_renumber[regno] >= 0) - regno = reg_renumber[regno]; - if (regno < FIRST_PSEUDO_REGISTER - /* A fixed reg that can overlap other regs better not be used - for reloading in any way. */ -#ifdef OVERLAPPING_REGNO_P - && ! (fixed_regs[regno] && OVERLAPPING_REGNO_P (regno)) -#endif - && ! refers_to_regno_p (regno, regno + HARD_REGNO_NREGS (regno, GET_MODE (out)), - PATTERN (this_insn), outloc) - && TEST_HARD_REG_BIT (reg_class_contents[(int) class], regno)) - value = out; - - *inloc = orig_in; - } - - /* Consider using IN if OUT was not acceptable - or if OUT dies in this insn (like the quotient in a divmod insn). - We can't use IN unless it is free after this insn, - which means we must know accurately which hard regs are live. - Also, the result can't go in IN if IN is used within OUT. */ - if (hard_regs_live_known - && GET_CODE (in) == REG - && ! find_reg_note (this_insn, REG_UNSET, in) - && (value == 0 - || find_regno_note (this_insn, REG_DEAD, REGNO (value)))) - { - register int regno = REGNO (in); - if (find_regno_note (this_insn, REG_DEAD, regno)) - { - if (reg_renumber[regno] >= 0) - regno = reg_renumber[regno]; - if (regno < FIRST_PSEUDO_REGISTER - && ! refers_to_regno_p (regno, - regno + HARD_REGNO_NREGS (regno, GET_MODE (in)), - out, 0) - && ! hard_reg_set_here_p (regno, PATTERN (this_insn)) - && TEST_HARD_REG_BIT (reg_class_contents[(int) class], regno)) - { - /* If we were going to use OUT as the reload reg - and changed our mind, it means OUT is a dummy that - dies here. So don't bother copying value to it. */ - if (for_real >= 0 && value == out) - reload_out[for_real] = 0; - value = in; - } - } - } - - return value; -} - -/* This page contains subroutines used mainly for determining - whether the IN or an OUT of a reload can serve as the - reload register. */ - -/* Return 1 if hard reg number REGNO is stored in by expression X, - either explicitly or in the guise of a pseudo-reg allocated to REGNO. - X should be the body of an instruction. */ - -static int -hard_reg_set_here_p (regno, x) - register int regno; - rtx x; -{ - if (GET_CODE (x) == SET || GET_CODE (x) == CLOBBER) - { - register rtx op0 = SET_DEST (x); - while (GET_CODE (op0) == SUBREG) - op0 = SUBREG_REG (op0); - if (GET_CODE (op0) == REG) - { - register int r = REGNO (op0); - /* See if this reg includes the specified one. */ - if (r <= regno && r + HARD_REGNO_NREGS (r, GET_MODE (op0)) > regno) - return 1; - } - } - else if (GET_CODE (x) == PARALLEL) - { - register int i = XVECLEN (x, 0) - 1; - for (; i >= 0; i--) - if (hard_reg_set_here_p (regno, XVECEXP (x, 0, i))) - return 1; - } - - return 0; -} - -/* Return 1 if ADDR is a valid memory address for mode MODE, - and check that each pseudo reg has the proper kind of - hard reg. */ - -int -strict_memory_address_p (mode, addr) - enum machine_mode mode; - register rtx addr; -{ - GO_IF_LEGITIMATE_ADDRESS (mode, addr, win); - return 0; - - win: - return 1; -} - - -/* Like rtx_equal_p except that it allows a REG and a SUBREG to match - if they are the same hard reg, and has special hacks for - autoincrement and autodecrement. - This is specifically intended for find_reloads to use - in determining whether two operands match. - X is the operand whose number is the lower of the two. - - The value is 2 if Y contains a pre-increment that matches - a non-incrementing address in X. */ - -/* ??? To be completely correct, we should arrange to pass - for X the output operand and for Y the input operand. - For now, we assume that the output operand has the lower number - because that is natural in (SET output (... input ...)). */ - -int -operands_match_p (x, y) - register rtx x, y; -{ - register int i; - register RTX_CODE code = GET_CODE (x); - register char *fmt; - int success_2; - - if (x == y) - return 1; - if ((code == REG || (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG)) - && (GET_CODE (y) == REG || (GET_CODE (y) == SUBREG - && GET_CODE (SUBREG_REG (y)) == REG))) - { - register int j; - - if (code == SUBREG) - { - i = REGNO (SUBREG_REG (x)); - if (i >= FIRST_PSEUDO_REGISTER) - goto slow; - i += SUBREG_WORD (x); - } - else - i = REGNO (x); - - if (GET_CODE (y) == SUBREG) - { - j = REGNO (SUBREG_REG (y)); - if (j >= FIRST_PSEUDO_REGISTER) - goto slow; - j += SUBREG_WORD (y); - } - else - j = REGNO (y); - - return i == j; - } - /* If two operands must match, because they are really a single - operand of an assembler insn, then two postincrements are invalid - because the assembler insn would increment only once. - On the other hand, an postincrement matches ordinary indexing - if the postincrement is the output operand. */ - if (code == POST_DEC || code == POST_INC) - return operands_match_p (XEXP (x, 0), y); - /* Two preincrements are invalid - because the assembler insn would increment only once. - On the other hand, an preincrement matches ordinary indexing - if the preincrement is the input operand. - In this case, return 2, since some callers need to do special - things when this happens. */ - if (GET_CODE (y) == PRE_DEC || GET_CODE (y) == PRE_INC) - return operands_match_p (x, XEXP (y, 0)) ? 2 : 0; - - slow: - - /* Now we have disposed of all the cases - in which different rtx codes can match. */ - if (code != GET_CODE (y)) - return 0; - if (code == LABEL_REF) - return XEXP (x, 0) == XEXP (y, 0); - if (code == SYMBOL_REF) - return XSTR (x, 0) == XSTR (y, 0); - - /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */ - - if (GET_MODE (x) != GET_MODE (y)) - return 0; - - /* Compare the elements. If any pair of corresponding elements - fail to match, return 0 for the whole things. */ - - success_2 = 0; - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - int val; - switch (fmt[i]) - { - case 'i': - if (XINT (x, i) != XINT (y, i)) - return 0; - break; - - case 'e': - val = operands_match_p (XEXP (x, i), XEXP (y, i)); - if (val == 0) - return 0; - /* If any subexpression returns 2, - we should return 2 if we are successful. */ - if (val == 2) - success_2 = 1; - break; - - case '0': - break; - - /* It is believed that rtx's at this level will never - contain anything but integers and other rtx's, - except for within LABEL_REFs and SYMBOL_REFs. */ - default: - abort (); - } - } - return 1 + success_2; -} - -/* Return the number of times character C occurs in string S. */ - -static int -n_occurrences (c, s) - char c; - char *s; -{ - int n = 0; - while (*s) - n += (*s++ == c); - return n; -} - -struct decomposition -{ - int reg_flag; - int safe; - rtx base; - int start; - int end; -}; - -/* Describe the range of registers or memory referenced by X. - If X is a register, set REG_FLAG and put the first register - number into START and the last plus one into END. - If X is a memory reference, put a base address into BASE - and a range of integer offsets into START and END. - If X is pushing on the stack, we can assume it causes no trouble, - so we set the SAFE field. */ - -static struct decomposition -decompose (x) - rtx x; -{ - struct decomposition val; - int all_const = 0; - - val.reg_flag = 0; - val.safe = 0; - if (GET_CODE (x) == MEM) - { - rtx base, offset = 0; - rtx addr = XEXP (x, 0); - - if (GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC - || GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC) - { - val.base = XEXP (addr, 0); - val.start = - GET_MODE_SIZE (GET_MODE (x)); - val.end = GET_MODE_SIZE (GET_MODE (x)); - val.safe = REGNO (val.base) == STACK_POINTER_REGNUM; - return val; - } - - if (GET_CODE (addr) == CONST) - { - addr = XEXP (addr, 0); - all_const = 1; - } - if (GET_CODE (addr) == PLUS) - { - if (CONSTANT_P (XEXP (addr, 0))) - { - base = XEXP (addr, 1); - offset = XEXP (addr, 0); - } - else if (CONSTANT_P (XEXP (addr, 1))) - { - base = XEXP (addr, 0); - offset = XEXP (addr, 1); - } - } - - if (offset == 0) - { - base = addr; - offset = const0_rtx; - } - if (GET_CODE (offset) == CONST) - offset = XEXP (offset, 0); - if (GET_CODE (offset) == PLUS) - { - if (GET_CODE (XEXP (offset, 0)) == CONST_INT) - { - base = gen_rtx (PLUS, GET_MODE (base), base, XEXP (offset, 1)); - offset = XEXP (offset, 0); - } - else if (GET_CODE (XEXP (offset, 1)) == CONST_INT) - { - base = gen_rtx (PLUS, GET_MODE (base), base, XEXP (offset, 0)); - offset = XEXP (offset, 1); - } - else - { - base = gen_rtx (PLUS, GET_MODE (base), base, offset); - offset = const0_rtx; - } - } - else if (GET_CODE (offset) != CONST_INT) - { - base = gen_rtx (PLUS, GET_MODE (base), base, offset); - offset = const0_rtx; - } - - if (all_const && GET_CODE (base) == PLUS) - base = gen_rtx (CONST, GET_MODE (base), base); - - if (GET_CODE (offset) != CONST_INT) - abort (); - - val.start = INTVAL (offset); - val.end = val.start + GET_MODE_SIZE (GET_MODE (x)); - val.base = base; - return val; - } - else if (GET_CODE (x) == REG) - { - val.reg_flag = 1; - val.start = true_regnum (x); - if (val.start < 0) - { - /* A pseudo with no hard reg. */ - val.start = REGNO (x); - val.end = val.start + 1; - } - else - /* A hard reg. */ - val.end = val.start + HARD_REGNO_NREGS (val.start, GET_MODE (x)); - } - else if (GET_CODE (x) == SUBREG) - { - if (GET_CODE (SUBREG_REG (x)) != REG) - /* This could be more precise, but it's good enough. */ - return decompose (SUBREG_REG (x)); - val.reg_flag = 1; - val.start = true_regnum (x); - if (val.start < 0) - return decompose (SUBREG_REG (x)); - else - /* A hard reg. */ - val.end = val.start + HARD_REGNO_NREGS (val.start, GET_MODE (x)); - } - else - abort (); - return val; -} - -/* Return 1 if altering Y will not modify the value of X. - Y is also described by YDATA, which should be decompose (Y). */ - -static int -immune_p (x, y, ydata) - rtx x, y; - struct decomposition ydata; -{ - struct decomposition xdata; - - if (ydata.reg_flag) - return !refers_to_regno_p (ydata.start, ydata.end, x, 0); - if (ydata.safe) - return 1; - - if (GET_CODE (y) != MEM) - abort (); - /* If Y is memory and X is not, Y can't affect X. */ - if (GET_CODE (x) != MEM) - return 1; - - xdata = decompose (x); - - if (! rtx_equal_p (xdata.base, ydata.base)) - { - /* If bases are distinct symbolic constants, there is no overlap. */ - if (CONSTANT_P (xdata.base) && CONSTANT_P (ydata.base)) - return 1; - /* Constants and stack slots never overlap. */ - if (CONSTANT_P (xdata.base) - && (ydata.base == frame_pointer_rtx - || ydata.base == stack_pointer_rtx)) - return 1; - if (CONSTANT_P (ydata.base) - && (xdata.base == frame_pointer_rtx - || xdata.base == stack_pointer_rtx)) - return 1; - /* If either base is variable, we don't know anything. */ - return 0; - } - - - return (xdata.start >= ydata.end || ydata.start >= xdata.end); -} - -/* Main entry point of this file: search the body of INSN - for values that need reloading and record them with push_reload. - REPLACE nonzero means record also where the values occur - so that subst_reloads can be used. - IND_OK says that a memory reference is a valid memory address. - - LIVE_KNOWN says we have valid information about which hard - regs are live at each point in the program; this is true when - we are called from global_alloc but false when stupid register - allocation has been done. - - RELOAD_REG_P if nonzero is a vector indexed by hard reg number - which is nonnegative if the reg has been commandeered for reloading into. - It is copied into STATIC_RELOAD_REG_P and referenced from there - by various subroutines. */ - -void -find_reloads (insn, replace, ind_ok, live_known, reload_reg_p) - rtx insn; - int replace, ind_ok; - int live_known; - short *reload_reg_p; -{ -#ifdef REGISTER_CONSTRAINTS - - enum reload_modified { RELOAD_NOTHING, RELOAD_READ, RELOAD_READ_WRITE, RELOAD_WRITE }; - - register int insn_code_number; - register int i; - int noperands; - /* These are the constraints for the insn. We don't change them. */ - char *constraints1[MAX_RECOG_OPERANDS]; - /* These start out as the constraints for the insn - and they are chewed up as we consider alternatives. */ - char *constraints[MAX_RECOG_OPERANDS]; - /* Nonzero for a MEM operand whose entire address needs a reload. */ - int address_reloaded[MAX_RECOG_OPERANDS]; - int n_alternatives; - int this_alternative[MAX_RECOG_OPERANDS]; - char this_alternative_win[MAX_RECOG_OPERANDS]; - char this_alternative_offmemok[MAX_RECOG_OPERANDS]; - char this_alternative_earlyclobber[MAX_RECOG_OPERANDS]; - int this_alternative_matches[MAX_RECOG_OPERANDS]; - int swapped; - int goal_alternative[MAX_RECOG_OPERANDS]; - int this_alternative_number; - int goal_alternative_number; - int operand_reloadnum[MAX_RECOG_OPERANDS]; - int goal_alternative_matches[MAX_RECOG_OPERANDS]; - int goal_alternative_matched[MAX_RECOG_OPERANDS]; - char goal_alternative_win[MAX_RECOG_OPERANDS]; - char goal_alternative_offmemok[MAX_RECOG_OPERANDS]; - char goal_alternative_earlyclobber[MAX_RECOG_OPERANDS]; - int goal_alternative_swapped; - enum reload_modified modified[MAX_RECOG_OPERANDS]; - int best; - int commutative; - char operands_match[MAX_RECOG_OPERANDS][MAX_RECOG_OPERANDS]; - rtx substed_operand[MAX_RECOG_OPERANDS]; - rtx body = PATTERN (insn); - int goal_earlyclobber, this_earlyclobber; - enum machine_mode operand_mode[MAX_RECOG_OPERANDS]; - - this_insn = insn; - n_reloads = 0; - n_replacements = 0; - n_memlocs = 0; - n_earlyclobbers = 0; - replace_reloads = replace; - indirect_ok = ind_ok; - hard_regs_live_known = live_known; - static_reload_reg_p = reload_reg_p; - - /* Find what kind of insn this is. NOPERANDS gets number of operands. - Make OPERANDS point to a vector of operand values. - Make OPERAND_LOCS point to a vector of pointers to - where the operands were found. - Fill CONSTRAINTS and CONSTRAINTS1 with pointers to the - constraint-strings for this insn. - Return if the insn needs no reload processing. */ - - switch (GET_CODE (body)) - { - case USE: - case CLOBBER: - case ASM_INPUT: - case ADDR_VEC: - case ADDR_DIFF_VEC: - return; - - case SET: - /* Dispose quickly of (set (reg..) (reg..)) if both have hard regs. */ - if (GET_CODE (SET_DEST (body)) == REG - && REGNO (SET_DEST (body)) < FIRST_PSEUDO_REGISTER - && GET_CODE (SET_SRC (body)) == REG - && REGNO (SET_SRC (body)) < FIRST_PSEUDO_REGISTER) - return; - case PARALLEL: - case ASM_OPERANDS: - noperands = asm_noperands (body); - if (noperands >= 0) - { - /* This insn is an `asm' with operands. */ - - insn_code_number = -1; - - /* expand_asm_operands makes sure there aren't too many operands. */ - if (noperands > MAX_RECOG_OPERANDS) - abort (); - - /* Now get the operand values and constraints out of the insn. */ - - decode_asm_operands (body, recog_operand, recog_operand_loc, - constraints, operand_mode); - if (noperands > 0) - { - bcopy (constraints, constraints1, noperands * sizeof (char *)); - n_alternatives = n_occurrences (',', constraints[0]) + 1; - for (i = 1; i < noperands; i++) - if (n_alternatives != n_occurrences (',', constraints[0]) + 1) - { - error_for_asm (insn, "operand constraints differ in number of alternatives"); - /* Avoid further trouble with this insn. */ - PATTERN (insn) = gen_rtx (USE, VOIDmode, const0_rtx); - n_reloads = 0; - return; - } - } - break; - } - - default: - /* Ordinary insn: recognize it, allocate space for operands and - constraints, and get them out via insn_extract. */ - - insn_code_number = recog_memoized (insn); - noperands = insn_n_operands[insn_code_number]; - n_alternatives = insn_n_alternatives[insn_code_number]; - /* Just return "no reloads" if insn has no operands with constraints. */ - if (n_alternatives == 0) - return; - insn_extract (insn); - for (i = 0; i < noperands; i++) - { - constraints[i] = constraints1[i] - = insn_operand_constraint[insn_code_number][i]; - operand_mode[i] = insn_operand_mode[insn_code_number][i]; - } - } - - if (noperands == 0) - return; - - commutative = -1; - - /* If we will need to know, later, whether some pair of operands - are the same, we must compare them now and save the result. - Reloading the base and index registers will clobber them - and afterward they will fail to match. */ - - for (i = 0; i < noperands; i++) - { - register char *p; - register int c; - - substed_operand[i] = recog_operand[i]; - p = constraints[i]; - - /* Scan this operand's constraint to see if it should match another. */ - - while (c = *p++) - if (c == '%') - commutative = i; - else if (c >= '0' && c <= '9') - { - c -= '0'; - operands_match[c][i] - = operands_match_p (recog_operand[c], recog_operand[i]); - /* If C can be commuted with C+1, and C might need to match I, - then C+1 might also need to match I. */ - if (commutative >= 0) - { - if (c == commutative || c == commutative + 1) - { - int other = c + (c == commutative ? 1 : -1); - operands_match[other][i] - = operands_match_p (recog_operand[other], recog_operand[i]); - } - if (i == commutative || i == commutative + 1) - { - int other = i + (i == commutative ? 1 : -1); - operands_match[c][other] - = operands_match_p (recog_operand[c], recog_operand[other]); - } - /* Note that C is supposed to be less than I. - No need to consider altering both C and I - because in that case we would alter one into the other. */ - } - } - } - - /* Examine each operand that is a memory reference or memory address - and reload parts of the addresses into index registers. - While we are at it, initialize the array `modified'. - Also here any references to pseudo regs that didn't get hard regs - but are equivalent to constants get replaced in the insn itself - with those constants. Nobody will ever see them again. */ - - for (i = 0; i < noperands; i++) - { - register RTX_CODE code = GET_CODE (recog_operand[i]); - modified[i] = RELOAD_READ; - address_reloaded[i] = 0; - if (constraints[i][0] == 'p') - { - find_reloads_address (VOIDmode, 0, - recog_operand[i], recog_operand_loc[i], - recog_operand[i]); - substed_operand[i] = recog_operand[i] = *recog_operand_loc[i]; - } - else if (code == MEM) - { - if (find_reloads_address (GET_MODE (recog_operand[i]), - recog_operand_loc[i], - XEXP (recog_operand[i], 0), - &XEXP (recog_operand[i], 0), - recog_operand[i])) - address_reloaded[i] = 1; - substed_operand[i] = recog_operand[i] = *recog_operand_loc[i]; - } - else if (code == SUBREG) - substed_operand[i] = recog_operand[i] = *recog_operand_loc[i] - = find_reloads_toplev (recog_operand[i]); - else if (code == REG) - { - /* This is equivalent to calling find_reloads_toplev. - The code is duplicated for speed. */ - register int regno = REGNO (recog_operand[i]); - if (reg_equiv_constant[regno] != 0) - substed_operand[i] = recog_operand[i] - = reg_equiv_constant[regno]; -#if 0 /* This might screw code in reload1.c to delete prior output-reload - that feeds this insn. */ - if (reg_equiv_mem[regno] != 0) - substed_operand[i] = recog_operand[i] - = reg_equiv_mem[regno]; -#endif - if (reg_equiv_address[regno] != 0) - { - *recog_operand_loc[i] = recog_operand[i] - = gen_rtx (MEM, GET_MODE (recog_operand[i]), - reg_equiv_address[regno]); - find_reloads_address (GET_MODE (recog_operand[i]), - recog_operand_loc[i], - XEXP (recog_operand[i], 0), - &XEXP (recog_operand[i], 0), - recog_operand[i]); - substed_operand[i] = recog_operand[i] = *recog_operand_loc[i]; - } - } - } - - /* Now see what we need for pseudo-regs that didn't get hard regs - or got the wrong kind of hard reg. For this, we must consider - all the operands together against the register constraints. */ - - best = MAX_RECOG_OPERANDS + 100; - - swapped = 0; - try_swapped: - - /* The constraints are made of several alternatives. - Each operand's constraint looks like foo,bar,... with commas - separating the alternatives. The first alternatives for all - operands go together, the second alternatives go together, etc. - - First loop over alternatives. */ - - for (this_alternative_number = 0; - this_alternative_number < n_alternatives; - this_alternative_number++) - { - /* Loop over operands for one constraint alternative. */ - /* LOSERS counts those that don't fit this alternative - and would require loading. */ - int losers = 0; - /* BAD is set to 1 if it some operand can't fit this alternative - even after reloading. */ - int bad = 0; - /* REJECT is a count of how undesirable this alternative says it is - if any reloading is required. If the alternative matches exactly - then REJECT is ignored, but otherwise it gets this much - counted against it in addition to the reloading needed. */ - int reject = 0; - - this_earlyclobber = 0; - - for (i = 0; i < noperands; i++) - { - register char *p = constraints[i]; - register int win = 0; - /* 0 => this operand can be reloaded somehow for this alternative */ - int badop = 1; - /* 0 => this operand can be reloaded if the alternative allows regs. */ - int winreg = 0; - int c; - register rtx operand = recog_operand[i]; - int offset = 0; - /* Nonzero means this is a MEM that must be reloaded into a reg - regardless of what the constraint says. */ - int force_reload = 0; - int offmemok = 0; - int earlyclobber = 0; - - /* If the operand is a SUBREG, extract - the REG or MEM (or maybe even a constant) within. - (Constants can occur as a result of reg_equiv_constant.) */ - - while (GET_CODE (operand) == SUBREG) - { - offset += SUBREG_WORD (operand); - operand = SUBREG_REG (operand); - if (GET_CODE (operand) != REG) - force_reload = 1; - } - - this_alternative[i] = (int) NO_REGS; - this_alternative_win[i] = 0; - this_alternative_offmemok[i] = 0; - this_alternative_earlyclobber[i] = 0; - this_alternative_matches[i] = -1; - - /* An empty constraint or empty alternative - allows anything which matched the pattern. */ - if (*p == 0 || *p == ',') - win = 1, badop = 0; - - /* Scan this alternative's specs for this operand; - set WIN if the operand fits any letter in this alternative. - Otherwise, clear BADOP if this operand could - fit some letter after reloads, - or set WINREG if this operand could fit after reloads - provided the constraint allows some registers. */ - - while (*p && (c = *p++) != ',') - switch (c) - { - case '=': - modified[i] = RELOAD_WRITE; - break; - - case '+': - modified[i] = RELOAD_READ_WRITE; - break; - - case '*': - break; - - case '%': - commutative = i; - break; - - case '?': - reject++; - break; - - case '!': - reject = 100; - break; - - case '#': - /* Ignore rest of this alternative as far as - reloading is concerned. */ - while (*p && *p != ',') p++; - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - c -= '0'; - this_alternative_matches[i] = c; - /* We are supposed to match a previous operand. - If we do, we win if that one did. - If we do not, count both of the operands as losers. - (This is too conservative, since most of the time - only a single reload insn will be needed to make - the two operands win. As a result, this alternative - may be rejected when it is actually desirable.) */ - if ((swapped && (c != commutative || i != commutative + 1)) - /* If we are matching as if two operands were swapped, - also pretend that operands_match had been computed - with swapped. - But if I is the second of those and C is the first, - don't exchange them, because operands_match is valid - only on one side of its diagonal. */ - ? (operands_match - [(c == commutative || c == commutative + 1) - ? 2*commutative + 1 - c : c] - [(i == commutative || i == commutative + 1) - ? 2*commutative + 1 - i : i]) - : operands_match[c][i]) - win = this_alternative_win[c]; - else - { - /* Operands don't match. */ - rtx value; - /* Retroactively mark the operand we had to match - as a loser, if it wasn't already. */ - if (this_alternative_win[c]) - losers++; - this_alternative_win[c] = 0; - if (this_alternative[c] == (int) NO_REGS) - bad = 1; - /* But count the pair only once in the total badness of - this alternative, if the pair can be a dummy reload. */ - value - = find_dummy_reload (recog_operand[i], recog_operand[c], - recog_operand_loc[i], recog_operand_loc[c], - this_alternative[c], -1); - - if (value != 0) - losers--; - } - /* This can be fixed with reloads if the operand - we are supposed to match can be fixed with reloads. */ - badop = 0; - this_alternative[i] = this_alternative[c]; - break; - - case 'p': - /* All necessary reloads for an address_operand - were handled in find_reloads_address. */ - this_alternative[i] = (int) ALL_REGS; - win = 1; - break; - - case 'm': - if (force_reload) - break; - if (GET_CODE (operand) == MEM - || (GET_CODE (operand) == REG - && REGNO (operand) >= FIRST_PSEUDO_REGISTER - && reg_renumber[REGNO (operand)] < 0)) - win = 1; - if (GET_CODE (operand) == CONST_DOUBLE - || CONSTANT_P (operand)) - badop = 0; - break; - - case '<': - if (GET_CODE (operand) == MEM - && ! address_reloaded[i] - && (GET_CODE (XEXP (operand, 0)) == PRE_DEC - || GET_CODE (XEXP (operand, 0)) == POST_DEC)) - win = 1; - break; - - case '>': - if (GET_CODE (operand) == MEM - && ! address_reloaded[i] - && (GET_CODE (XEXP (operand, 0)) == PRE_INC - || GET_CODE (XEXP (operand, 0)) == POST_INC)) - win = 1; - break; - - /* Memory operand whose address is offsettable. */ - case 'o': - if (force_reload) - break; - if ((GET_CODE (operand) == MEM - && offsettable_memref_p (operand)) - /* Certain mem addresses will become offsettable - after they themselves are reloaded. This is important; - we don't want our own handling of unoffsettables - to override the handling of reg_equiv_address. */ - || (GET_CODE (operand) == MEM - && GET_CODE (XEXP (operand, 0)) == REG - && (! ind_ok - || reg_equiv_address[REGNO (XEXP (operand, 0))] != 0)) - || (GET_CODE (operand) == REG - && REGNO (operand) >= FIRST_PSEUDO_REGISTER - && reg_renumber[REGNO (operand)] < 0)) - win = 1; - if (GET_CODE (operand) == CONST_DOUBLE - || CONSTANT_P (operand) - || GET_CODE (operand) == MEM) - badop = 0; - offmemok = 1; - break; - - case '&': - /* Output operand that is stored before the need for the - input operands (and their index registers) is over. */ - if (GET_CODE (operand) == REG - || GET_CODE (operand) == MEM) - earlyclobber = 1, this_earlyclobber = 1; - break; - - case 'F': - if (GET_CODE (operand) == CONST_DOUBLE) - win = 1; - break; - - case 'G': - case 'H': - if (GET_CODE (operand) == CONST_DOUBLE - && CONST_DOUBLE_OK_FOR_LETTER_P (operand, c)) - win = 1; - break; - - case 's': - if (GET_CODE (operand) == CONST_INT) - break; - case 'i': - if (CONSTANT_P (operand)) - win = 1; - break; - - case 'n': - if (GET_CODE (operand) == CONST_INT) - win = 1; - break; - - case 'I': - case 'J': - case 'K': - case 'L': - case 'M': - if (GET_CODE (operand) == CONST_INT - && CONST_OK_FOR_LETTER_P (INTVAL (operand), c)) - win = 1; - break; - - case 'g': - if (! force_reload - && (GENERAL_REGS == ALL_REGS - || GET_CODE (operand) != REG - || (REGNO (operand) >= FIRST_PSEUDO_REGISTER - && reg_renumber[REGNO (operand)] < 0))) - win = 1; - /* Drop through into 'r' case */ - - case 'r': - this_alternative[i] - = (int) reg_class_subunion[this_alternative[i]][(int) GENERAL_REGS]; - goto reg; - - default: - this_alternative[i] - = (int) reg_class_subunion[this_alternative[i]][(int) REG_CLASS_FROM_LETTER (c)]; - - reg: - if (GET_MODE (operand) == BLKmode) - break; - winreg = 1; - if (GET_CODE (operand) == REG - && reg_fits_class_p (operand, this_alternative[i], - offset, GET_MODE (recog_operand[i]))) - win = 1; - break; - } - - constraints[i] = p; - - /* If this operand could be handled with a reg, - and some reg is allowed, then this operand can be handled. */ - if (winreg && this_alternative[i] != (int) NO_REGS) - badop = 0; - - /* Record which operands fit this alternative. */ - this_alternative_earlyclobber[i] = earlyclobber; - if (win && ! force_reload) - this_alternative_win[i] = 1; - else - { - this_alternative_offmemok[i] = offmemok; - losers++; - if (badop) - bad = 1; - /* Alternative loses if it has no regs for a reg operand. */ - if (GET_CODE (operand) == REG - && this_alternative[i] == (int) NO_REGS - && this_alternative_matches[i] < 0) - bad = 1; - } - } - - /* Now see if any output operands that are marked "earlyclobber" - in this alternative conflict with any input operands - or any memory addresses. */ - - for (i = 0; i < noperands; i++) - if (this_alternative_earlyclobber[i] - && this_alternative_win[i]) - { - struct decomposition early_data; - int j; - - early_data = decompose (recog_operand[i]); - - for (j = 0; j < noperands; j++) - /* Is this an input operand or a memory ref? */ - if ((GET_CODE (recog_operand[j]) == MEM - || modified[j] != RELOAD_WRITE) - && j != i - /* Don't count an input operand that is constrained to match - the early clobber operand. */ - && ! (this_alternative_matches[j] == i - && rtx_equal_p (recog_operand[i], recog_operand[j])) - /* Is it altered by storing the earlyclobber operand? */ - && !immune_p (recog_operand[j], recog_operand[i], early_data)) - { - /* If the output is in a single-reg class, - it's costly to reload it, so reload the input instead. */ - if (reg_class_size[this_alternative[i]] == 1 - && (GET_CODE (recog_operand[j]) == REG - || GET_CODE (recog_operand[j]) == SUBREG)) - { - losers++; - this_alternative_win[j] = 0; - } - else - break; - } - /* If an earlyclobber operand conflicts with something, - it must be reloaded, so request this and count the cost. */ - if (j != noperands) - { - losers++; - this_alternative_win[i] = 0; - for (j = 0; j < noperands; j++) - if (this_alternative_matches[j] == i - && this_alternative_win[j]) - { - this_alternative_win[j] = 0; - losers++; - } - } - } - - /* If one alternative accepts all the operands, no reload required, - choose that alternative; don't consider the remaining ones. */ - if (losers == 0) - { - /* Unswap these so that they are never swapped at `finish'. */ - if (commutative >= 0) - { - recog_operand[commutative] = substed_operand[commutative]; - recog_operand[commutative + 1] - = substed_operand[commutative + 1]; - } - for (i = 0; i < noperands; i++) - { - goal_alternative_win[i] = 1; - goal_alternative[i] = this_alternative[i]; - goal_alternative_offmemok[i] = this_alternative_offmemok[i]; - goal_alternative_matches[i] = this_alternative_matches[i]; - goal_alternative_earlyclobber[i] - = this_alternative_earlyclobber[i]; - } - goal_alternative_number = this_alternative_number; - goal_alternative_swapped = swapped; - goal_earlyclobber = this_earlyclobber; - goto finish; - } - - /* REJECT, set by the ! and ? constraint characters, - discourages the use of this alternative for a reload goal. */ - if (reject > 0) - losers += reject; - - /* If this alternative can be made to work by reloading, - and it needs less reloading than the others checked so far, - record it as the chosen goal for reloading. */ - if (! bad && best > losers) - { - for (i = 0; i < noperands; i++) - { - goal_alternative[i] = this_alternative[i]; - goal_alternative_win[i] = this_alternative_win[i]; - goal_alternative_offmemok[i] = this_alternative_offmemok[i]; - goal_alternative_matches[i] = this_alternative_matches[i]; - goal_alternative_earlyclobber[i] - = this_alternative_earlyclobber[i]; - } - goal_alternative_swapped = swapped; - best = losers; - goal_alternative_number = this_alternative_number; - goal_earlyclobber = this_earlyclobber; - } - } - - /* If insn is commutative (it's safe to exchange a certain pair of operands) - then we need to try each alternative twice, - the second time matching those two operands - as if we had exchanged them. - To do this, really exchange them in operands. - - If we have just tried the alternatives the second time, - return operands to normal and drop through. */ - - if (commutative >= 0) - { - swapped = !swapped; - if (swapped) - { - recog_operand[commutative] = substed_operand[commutative + 1]; - recog_operand[commutative + 1] = substed_operand[commutative]; - - bcopy (constraints1, constraints, noperands * sizeof (char *)); - goto try_swapped; - } - else - { - recog_operand[commutative] = substed_operand[commutative]; - recog_operand[commutative + 1] = substed_operand[commutative + 1]; - } - } - - /* The operands don't meet the constraints. - goal_alternative describes the alternative - that we could reach by reloading the fewest operands. - Reload so as to fit it. */ - - if (best == MAX_RECOG_OPERANDS + 100) - { - /* No alternative works with reloads?? */ - if (insn_code_number >= 0) - abort (); - error_for_asm (insn, "inconsistent operand constraints in an `asm'"); - /* Avoid further trouble with this insn. */ - PATTERN (insn) = gen_rtx (USE, VOIDmode, const0_rtx); - n_reloads = 0; - return; - } - - /* Jump to `finish' from above if all operands are valid already. - In that case, goal_alternative_win is all 1. */ - finish: - - /* Right now, for any pair of operands I and J that are required to match, - with I < J, - goal_alternative_matches[J] is I. - Set up goal_alternative_matched as the inverse function: - goal_alternative_matched[I] = J. */ - - for (i = 0; i < noperands; i++) - goal_alternative_matched[i] = -1; - - for (i = 0; i < noperands; i++) - if (! goal_alternative_win[i] - && goal_alternative_matches[i] >= 0) - goal_alternative_matched[goal_alternative_matches[i]] = i; - - /* If the best alternative is with operands 1 and 2 swapped, - consider them swapped before reporting the reloads. */ - - if (goal_alternative_swapped) - { - register rtx tem; - - tem = substed_operand[commutative]; - substed_operand[commutative] = substed_operand[commutative + 1]; - substed_operand[commutative + 1] = tem; - tem = recog_operand[commutative]; - recog_operand[commutative] = recog_operand[commutative + 1]; - recog_operand[commutative + 1] = tem; - } - - /* Perform whatever substitutions on the operands we are supposed - to make due to commutativity or replacement of registers - with equivalent constants or memory slots. */ - - for (i = 0; i < noperands; i++) - { - *recog_operand_loc[i] = substed_operand[i]; - /* While we are looping on operands, initialize this. */ - operand_reloadnum[i] = -1; - } - - /* Any constants that aren't allowed and can't be reloaded - into memory locations are here changed into memory references. */ - for (i = 0; i < noperands; i++) - if (! goal_alternative_win[i] - && (GET_CODE (recog_operand[i]) == CONST_DOUBLE - || CONSTANT_P (recog_operand[i])) - && (PREFERRED_RELOAD_CLASS (recog_operand[i], - (enum reg_class) goal_alternative[i]) - == NO_REGS)) - { - enum machine_mode mode = operand_mode[i]; - *recog_operand_loc[i] = recog_operand[i] - = (GET_CODE (recog_operand[i]) == CONST_DOUBLE - ? force_const_double_mem (recog_operand[i]) - : force_const_mem (mode != VOIDmode ? mode : SImode, - recog_operand[i])); - find_reloads_toplev (recog_operand[i]); - if (alternative_allows_memconst (constraints1[i], goal_alternative_number)) - goal_alternative_win[i] = 1; - } - - /* Now record reloads for all the operands that need them. */ - for (i = 0; i < noperands; i++) - if (! goal_alternative_win[i]) - { - /* Operands that match previous ones have already been handled. */ - if (goal_alternative_matches[i] >= 0) - ; - /* Handle an operand with a nonoffsettable address - appearing where an offsettable address will do - by reloading the address into a base register. */ - else if (goal_alternative_matched[i] == -1 - && goal_alternative_offmemok[i] - && GET_CODE (recog_operand[i]) == MEM) - { - operand_reloadnum[i] - = push_reload (XEXP (recog_operand[i], 0), 0, - &XEXP (recog_operand[i], 0), 0, - BASE_REG_CLASS, GET_MODE (XEXP (recog_operand[i], 0)), - 0, 0, 0, 0); - reload_inc[operand_reloadnum[i]] - = GET_MODE_SIZE (GET_MODE (recog_operand[i])); - } - else if (goal_alternative_matched[i] == -1) - operand_reloadnum[i] = - push_reload (modified[i] != RELOAD_WRITE ? recog_operand[i] : 0, - modified[i] != RELOAD_READ ? recog_operand[i] : 0, - recog_operand_loc[i], 0, - (enum reg_class) goal_alternative[i], - (modified[i] == RELOAD_WRITE ? VOIDmode : operand_mode[i]), - (modified[i] == RELOAD_READ ? VOIDmode : operand_mode[i]), - (insn_code_number < 0 ? 0 - : insn_operand_strict_low[insn_code_number][i]), - 0, 0); - /* In a matching pair of operands, one must be input only - and the other must be output only. - Pass the input operand as IN and the other as OUT. */ - else if (modified[i] == RELOAD_READ - && modified[goal_alternative_matched[i]] == RELOAD_WRITE) - { - operand_reloadnum[i] - = push_reload (recog_operand[i], - recog_operand[goal_alternative_matched[i]], - recog_operand_loc[i], - recog_operand_loc[goal_alternative_matched[i]], - (enum reg_class) goal_alternative[i], - operand_mode[i], - operand_mode[goal_alternative_matched[i]], - VOIDmode, 0, 0); - operand_reloadnum[goal_alternative_matched[i]] = output_reloadnum; - } - else if (modified[i] == RELOAD_WRITE - && modified[goal_alternative_matched[i]] == RELOAD_READ) - { - operand_reloadnum[goal_alternative_matched[i]] - = push_reload (recog_operand[goal_alternative_matched[i]], - recog_operand[i], - recog_operand_loc[goal_alternative_matched[i]], - recog_operand_loc[i], - (enum reg_class) goal_alternative[i], - operand_mode[goal_alternative_matched[i]], - operand_mode[i], - VOIDmode, 0, 0); - operand_reloadnum[i] = output_reloadnum; - } - else if (insn_code_number >= 0) - abort (); - else - { - error_for_asm (insn, "inconsistent operand constraints in an `asm'"); - /* Avoid further trouble with this insn. */ - PATTERN (insn) = gen_rtx (USE, VOIDmode, const0_rtx); - n_reloads = 0; - return; - } - } - else if (goal_alternative_matched[i] < 0 - && goal_alternative_matches[i] < 0 - && optimize) - { - rtx operand = recog_operand[i]; - /* For each non-matching operand that's a pseudo-register - that didn't get a hard register, make an optional reload. - This may get done even if the insn needs no reloads otherwise. */ - /* (It would be safe to make an optional reload for a matching pair - of operands, but we don't bother yet.) */ - while (GET_CODE (operand) == SUBREG) - operand = XEXP (operand, 0); - if (GET_CODE (operand) == REG - && REGNO (operand) >= FIRST_PSEUDO_REGISTER - && reg_renumber[REGNO (operand)] < 0 - && (enum reg_class) goal_alternative[i] != NO_REGS - /* Don't make optional output reloads for jump insns - (such as aobjeq on the vax). */ - && (modified[i] == RELOAD_READ - || GET_CODE (insn) != JUMP_INSN)) - operand_reloadnum[i] - = push_reload (modified[i] != RELOAD_WRITE ? recog_operand[i] : 0, - modified[i] != RELOAD_READ ? recog_operand[i] : 0, - recog_operand_loc[i], 0, - (enum reg_class) goal_alternative[i], - (modified[i] == RELOAD_WRITE ? VOIDmode : operand_mode[i]), - (modified[i] == RELOAD_READ ? VOIDmode : operand_mode[i]), - (insn_code_number < 0 ? 0 - : insn_operand_strict_low[insn_code_number][i]), - 1, 0); - /* Make an optional reload for an explicit mem ref. */ - else if (GET_CODE (operand) == MEM - && (enum reg_class) goal_alternative[i] != NO_REGS - /* Don't make optional output reloads for jump insns - (such as aobjeq on the vax). */ - && (modified[i] == RELOAD_READ - || GET_CODE (insn) != JUMP_INSN)) - operand_reloadnum[i] - = push_reload (modified[i] != RELOAD_WRITE ? recog_operand[i] : 0, - modified[i] != RELOAD_READ ? recog_operand[i] : 0, - recog_operand_loc[i], 0, - (enum reg_class) goal_alternative[i], - (modified[i] == RELOAD_WRITE ? VOIDmode : operand_mode[i]), - (modified[i] == RELOAD_READ ? VOIDmode : operand_mode[i]), - (insn_code_number < 0 ? 0 - : insn_operand_strict_low[insn_code_number][i]), - 1, 0); - } - - /* Record the values of the earlyclobber operands for the caller. */ - if (goal_earlyclobber) - for (i = 0; i < noperands; i++) - if (goal_alternative_earlyclobber[i]) - reload_earlyclobbers[n_earlyclobbers++] = recog_operand[i]; - - /* If this insn pattern contains any MATCH_DUP's, make sure that - they will be substituted if the operands they match are substituted. - Also do now any substitutions we already did on the operands. */ - if (insn_code_number >= 0) - for (i = insn_n_dups[insn_code_number] - 1; i >= 0; i--) - { - int opno = recog_dup_num[i]; - *recog_dup_loc[i] = *recog_operand_loc[opno]; - if (operand_reloadnum[opno] >= 0) - push_replacement (recog_dup_loc[i], operand_reloadnum[opno], - insn_operand_mode[insn_code_number][opno]); - } - -#if 0 - /* This loses because reloading of prior insns can invalidate the equivalence - (or at least find_equiv_reg isn't smart enough to find it any more), - causing this insn to need more reload regs than it needed before. - It may be too late to make the reload regs available. - Now this optimization is done safely in choose_reload_targets. */ - - /* For each reload of a reg into some other class of reg, - search for an existing equivalent reg (same value now) in the right class. - We can use it as long as we don't need to change its contents. */ - for (i = 0; i < n_reloads; i++) - if (reload_reg_rtx[i] == 0 - && reload_in[i] != 0 - && GET_CODE (reload_in[i]) == REG - && reload_out[i] == 0) - { - reload_reg_rtx[i] - = find_equiv_reg (reload_in[i], insn, reload_reg_class[i], -1, - static_reload_reg_p, 0, reload_inmode[i]); - /* Prevent generation of insn to load the value - because the one we found already has the value. */ - if (reload_reg_rtx[i]) - reload_in[i] = reload_reg_rtx[i]; - } -#endif - -#else /* no REGISTER_CONSTRAINTS */ - int noperands; - int insn_code_number; - int goal_earlyclobber = 0; /* Always 0, to make combine_reloads happen. */ - register int i; - rtx body = PATTERN (insn); - - n_reloads = 0; - n_replacements = 0; - n_earlyclobbers = 0; - replace_reloads = replace; - indirect_ok = ind_ok; - this_insn = insn; - - /* Find what kind of insn this is. NOPERANDS gets number of operands. - Store the operand values in RECOG_OPERAND and the locations - of the words in the insn that point to them in RECOG_OPERAND_LOC. - Return if the insn needs no reload processing. */ - - switch (GET_CODE (body)) - { - case USE: - case CLOBBER: - case ASM_INPUT: - case ADDR_VEC: - case ADDR_DIFF_VEC: - return; - - case PARALLEL: - case SET: - noperands = asm_noperands (body); - if (noperands >= 0) - { - /* This insn is an `asm' with operands. - First, find out how many operands, and allocate space. */ - - insn_code_number = -1; - /* ??? This is a bug! ??? - Give up and delete this insn if it has too many operands. */ - if (noperands > MAX_RECOG_OPERANDS) - abort (); - - /* Now get the operand values out of the insn. */ - - decode_asm_operands (body, recog_operand, recog_operand_loc, 0, 0); - break; - } - - default: - /* Ordinary insn: recognize it, allocate space for operands and - constraints, and get them out via insn_extract. */ - - insn_code_number = recog_memoized (insn); - noperands = insn_n_operands[insn_code_number]; - insn_extract (insn); - } - - if (noperands == 0) - return; - - for (i = 0; i < noperands; i++) - { - register RTX_CODE code = GET_CODE (recog_operand[i]); - - if (insn_code_number >= 0) - if (insn_operand_address_p[insn_code_number][i]) - find_reloads_address (VOIDmode, 0, - recog_operand[i], recog_operand_loc[i], - recog_operand[i]); - if (code == MEM) - find_reloads_address (GET_MODE (recog_operand[i]), - recog_operand_loc[i], - XEXP (recog_operand[i], 0), - &XEXP (recog_operand[i], 0), - recog_operand[i]); - if (code == SUBREG) - recog_operand[i] = *recog_operand_loc[i] - = find_reloads_toplev (recog_operand[i]); - if (code == REG) - { - register int regno = REGNO (recog_operand[i]); - if (reg_equiv_constant[regno] != 0) - recog_operand[i] = *recog_operand_loc[i] - = reg_equiv_constant[regno]; -#if 0 /* This might screw code in reload1.c to delete prior output-reload - that feeds this insn. */ - if (reg_equiv_mem[regno] != 0) - recog_operand[i] = *recog_operand_loc[i] - = reg_equiv_mem[regno]; -#endif - } - } -#endif /* no REGISTER_CONSTRAINTS */ - - /* Determine which part of the insn each reload is needed for, - based on which operand the reload is needed for. - Reloads of entire operands are classified as RELOAD_OTHER. - So are reloads for which a unique purpose is not known. */ - - for (i = 0; i < n_reloads; i++) - { - reload_when_needed[i] = RELOAD_OTHER; - - if (reload_needed_for[i] != 0 && ! reload_needed_for_multiple[i]) - { - int j; - int output_address = 0; - int input_address = 0; - int operand_address = 0; - - /* This reload is needed only for the address of something. - Determine whether it is needed for addressing an operand - being reloaded for input, whether it is needed for an - operand being reloaded for output, and whether it is needed - for addressing an operand that won't really be reloaded. */ - - for (j = 0; j < n_reloads; j++) - if (reload_needed_for[i] == reload_in[j] - || reload_needed_for[i] == reload_out[j]) - { - if (reload_optional[j]) - operand_address = 1; - else - { - if (reload_needed_for[i] == reload_in[j]) - input_address = 1; - if (reload_needed_for[i] == reload_out[j]) - output_address = 1; - } - } - - /* If it is needed for only one of those, record which one. */ - - if (input_address && ! output_address && ! operand_address) - reload_when_needed[i] = RELOAD_FOR_INPUT_RELOAD_ADDRESS; - if (output_address && ! input_address && ! operand_address) - reload_when_needed[i] = RELOAD_FOR_OUTPUT_RELOAD_ADDRESS; - if (operand_address && ! input_address && ! output_address) - reload_when_needed[i] = RELOAD_FOR_OPERAND_ADDRESS; - } - } - - /* Perhaps an output reload can be combined with another - to reduce needs by one. */ - if (!goal_earlyclobber) - combine_reloads (); -} - -/* Return 1 if alternative number ALTNUM in constraint-string CONSTRAINT - accepts a memory operand with constant address. */ - -static int -alternative_allows_memconst (constraint, altnum) - char *constraint; - int altnum; -{ - register int c; - /* Skip alternatives before the one requested. */ - while (altnum > 0) - { - while (*constraint++ != ','); - altnum--; - } - /* Scan the requested alternative for 'm' or 'o'. - If one of them is present, this alternative accepts memory constants. */ - while ((c = *constraint++) && c != ',' && c != '#') - if (c == 'm' || c == 'o') - return 1; - return 0; -} - -/* Scan X for memory references and scan the addresses for reloading. - Also checks for references to "constant" regs that we want to eliminate - and replaces them with the values they stand for. - We may alter X descructively if it contains a reference to such. - If X is just a constant reg, we return the equivalent value - instead of X. */ - -static rtx -find_reloads_toplev (x) - rtx x; -{ - register RTX_CODE code = GET_CODE (x); - - register char *fmt = GET_RTX_FORMAT (code); - register int i; - - if (code == REG) - { - /* This code is duplicated for speed in find_reloads. */ - register int regno = REGNO (x); - if (reg_equiv_constant[regno] != 0) - x = reg_equiv_constant[regno]; -#if 0 -/* This creates (subreg (mem...)) which would cause an unnecessary - reload of the mem. */ - else if (reg_equiv_mem[regno] != 0) - x = reg_equiv_mem[regno]; -#endif - else if (reg_equiv_address[regno] != 0) - { - x = gen_rtx (MEM, GET_MODE (x), - reg_equiv_address[regno]); - find_reloads_address (GET_MODE (x), 0, - XEXP (x, 0), - &XEXP (x, 0), x); - } - return x; - } - if (code == MEM) - { - rtx tem = x; - find_reloads_address (GET_MODE (x), &tem, XEXP (x, 0), &XEXP (x, 0), x); - return tem; - } - - if (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG) - { - /* Check for SUBREG containing a REG that's equivalent to a constant. */ - register int regno = REGNO (SUBREG_REG (x)); - if (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0 - && reg_equiv_constant[regno] != 0) - { - /* If the constant has a known value, truncate it right now. */ - if (GET_CODE (reg_equiv_constant[regno]) == CONST_INT) - { - int size = GET_MODE_BITSIZE (GET_MODE (x)); - if (size < BITS_PER_WORD) - return gen_rtx (CONST_INT, VOIDmode, - INTVAL (reg_equiv_constant[regno]) - & ((1 << size) - 1)); - return reg_equiv_constant[regno]; - } - /* If the constant is symbolic, allow it to be substituted normally. - push_reload will strip the subreg later. */ - } - /* If the subreg contains a reg that will be converted to a mem, - convert the subreg to a narrower memref now. - Otherwise, we would get (subreg (mem ...) ...), - which would force reload of the mem. */ - else if (regno >= FIRST_PSEUDO_REGISTER && reg_equiv_address[regno] != 0) - { - int offset = SUBREG_WORD (x) * UNITS_PER_WORD; - rtx addr; -#ifdef BYTES_BIG_ENDIAN - int size; - size = GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))); - offset += min (size, UNITS_PER_WORD); - size = GET_MODE_SIZE (GET_MODE (x)); - offset -= min (size, UNITS_PER_WORD); -#endif - addr = plus_constant (reg_equiv_address[regno], offset); - x = gen_rtx (MEM, GET_MODE (x), addr); - find_reloads_address (GET_MODE (x), 0, - XEXP (x, 0), - &XEXP (x, 0), x); - } - - } - - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - XEXP (x, i) = find_reloads_toplev (XEXP (x, i)); - } - return x; -} - -static rtx -make_memloc (ad, regno) - rtx ad; - int regno; -{ - register int i; - rtx tem = reg_equiv_address[regno]; - for (i = 0; i < n_memlocs; i++) - if (rtx_equal_p (tem, XEXP (memlocs[i], 0))) - return memlocs[i]; - tem = gen_rtx (MEM, GET_MODE (ad), tem); - memlocs[n_memlocs++] = tem; - return tem; -} - -/* Record all reloads needed for handling memory address AD - which appears in *LOC in a memory reference to mode MODE - which itself is found in location *MEMREFLOC. - Note that we take shortcuts assuming that no multi-reg machine mode - occurs as part of an address. - - OPERAND is the operand of the insn within which this address appears. - - Value is nonzero if this address is reloaded or replaced as a whole. - This is interesting to the caller if the address is an autoincrement. */ - -static int -find_reloads_address (mode, memrefloc, ad, loc, operand) - enum machine_mode mode; - rtx *memrefloc; - rtx ad; - rtx *loc; - rtx operand; -{ - register int regno; - rtx tem; - - if (GET_CODE (ad) == REG) - { - regno = REGNO (ad); - - if (reg_equiv_constant[regno] != 0) - { - if (strict_memory_address_p (mode, reg_equiv_constant[regno])) - { - *loc = ad = reg_equiv_constant[regno]; - return 1; - } - } -#if 0 /* This might screw code in reload1.c to delete prior output-reload - that feeds this insn. */ - if (reg_equiv_mem[regno] != 0) - { - if (strict_memory_address_p (mode, reg_equiv_mem[regno])) - { - *loc = ad = reg_equiv_mem[regno]; - return 1; - } - } -#endif - if (reg_equiv_address[regno] != 0) - { - rtx tem = make_memloc (ad, regno); - push_reload (XEXP (tem, 0), 0, &XEXP (tem, 0), 0, - BASE_REG_CLASS, - GET_MODE (XEXP (tem, 0)), 0, VOIDmode, 0, - operand); - push_reload (tem, 0, loc, 0, BASE_REG_CLASS, - GET_MODE (ad), 0, VOIDmode, 0, - operand); - return 1; - } - if (! (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0 - ? indirect_ok - : REGNO_OK_FOR_BASE_P (regno))) - { - push_reload (ad, 0, loc, 0, BASE_REG_CLASS, - GET_MODE (ad), 0, VOIDmode, 0, operand); - return 1; - } - return 0; - } - - if (strict_memory_address_p (mode, ad)) - { - /* The address appears valid, so reloads are not needed. - But the address may contain an eliminable register. - This can happen because a machine with indirect addressing - may consider a pseudo register by itself a valid address even when - it has failed to get a hard reg. - So do a tree-walk to find and eliminate all such regs. */ - - /* But first quickly dispose of a common case. */ - if (GET_CODE (ad) == PLUS - && GET_CODE (XEXP (ad, 1)) == CONST_INT - && GET_CODE (XEXP (ad, 0)) == REG - && reg_equiv_constant[REGNO (XEXP (ad, 0))] == 0) - return 0; - - subst_reg_equivs_changed = 0; - *loc = subst_reg_equivs (ad); - - if (! subst_reg_equivs_changed) - return 0; - - /* Check result for validity after substitution. */ - if (strict_memory_address_p (mode, ad)) - return 0; - } - - /* If we have address of a stack slot but it's not valid - (displacement is too large), compute the sum in a register. */ - if (GET_CODE (ad) == PLUS - && (XEXP (ad, 0) == frame_pointer_rtx -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - || XEXP (ad, 0) == arg_pointer_rtx -#endif - ) - && GET_CODE (XEXP (ad, 1)) == CONST_INT) - { - /* Unshare the MEM rtx so we can safely alter it. */ - if (memrefloc) - { - rtx oldref = *memrefloc; - *memrefloc = copy_rtx (*memrefloc); - loc = &XEXP (*memrefloc, 0); - if (operand == oldref) - operand = *memrefloc; - } - if (double_reg_address_ok) - { - /* Unshare the sum as well. */ - *loc = ad = copy_rtx (ad); - /* Reload the displacement into an index reg. - We assume the frame pointer or arg pointer is a base reg. */ - push_reload (XEXP (ad, 1), 0, &XEXP (ad, 1), 0, INDEX_REG_CLASS, - GET_MODE (ad), VOIDmode, 0, 0, operand); - } - else - { - /* If the sum of two regs is not necessarily valid, - reload the sum into a base reg. - That will at least work. */ - push_reload (ad, 0, loc, 0, BASE_REG_CLASS, - GET_MODE (ad), VOIDmode, 0, 0, operand); - } - return 1; - } - - /* See if address becomes valid when an eliminable register - in a sum is replaced. */ - - tem = ad; - if (GET_CODE (ad) == PLUS) - tem = subst_indexed_address (ad); - if (tem != ad && strict_memory_address_p (mode, tem)) - { - /* Ok, we win that way. Replace any additional eliminable - registers. */ - - subst_reg_equivs_changed = 0; - tem = subst_reg_equivs (tem); - - /* Make sure that didn't make the address invalid again. */ - - if (! subst_reg_equivs_changed || strict_memory_address_p (mode, tem)) - { - *loc = tem; - return 0; - } - } - - /* If constants aren't valid addresses, reload the constant address - into a register. */ - if (CONSTANT_ADDRESS_P (ad) && ! strict_memory_address_p (mode, ad)) - { - push_reload (ad, 0, loc, 0, - BASE_REG_CLASS, - Pmode, 0, VOIDmode, 0, operand); - return 1; - } - - return find_reloads_address_1 (ad, 0, loc, operand); -} - -/* Find all pseudo regs appearing in AD - that are eliminable in favor of equivalent values - and do not have hard regs; replace them by their equivalents. */ - -static rtx -subst_reg_equivs (ad) - rtx ad; -{ - register RTX_CODE code = GET_CODE (ad); - register int i; - register char *fmt; - - switch (code) - { - case CONST_INT: - case CONST: - case CONST_DOUBLE: - case SYMBOL_REF: - case LABEL_REF: - case PC: - case CC0: - return ad; - - case REG: - { - register int regno = REGNO (ad); - - if (reg_equiv_constant[regno] != 0) - { - subst_reg_equivs_changed = 1; - return reg_equiv_constant[regno]; - } - } - return ad; - - case PLUS: - /* Quickly dispose of a common case. */ - if (XEXP (ad, 0) == frame_pointer_rtx - && GET_CODE (XEXP (ad, 1)) == CONST_INT) - return ad; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - XEXP (ad, i) = subst_reg_equivs (XEXP (ad, i)); - return ad; -} - -/* If ADDR is a sum containing a pseudo register that should be - replaced with a constant (from reg_equiv_constant), - return the result of doing so, and also apply the associative - law so that the result is more likely to be a valid address. - (But it is not guaranteed to be one.) - - In all other cases, return ADDR. */ - -static rtx -subst_indexed_address (addr) - rtx addr; -{ - rtx const_part = 0; - rtx var_part = 0; - int regno; - - if (GET_CODE (addr) == PLUS) - { - if (CONSTANT_P (XEXP (addr, 0))) - const_part = XEXP (addr, 0), - var_part = XEXP (addr, 1); - else if (CONSTANT_P (XEXP (addr, 1))) - const_part = XEXP (addr, 1), - var_part = XEXP (addr, 0); - else - var_part = addr; - - if (const_part && GET_CODE (const_part) == CONST) - const_part = XEXP (const_part, 0); - - if (GET_CODE (var_part) == REG - && (regno = REGNO (var_part)) >= FIRST_PSEUDO_REGISTER - && reg_renumber[regno] < 0 - && reg_equiv_constant[regno] != 0) - return (const_part - ? gen_rtx (CONST, VOIDmode, - gen_rtx (PLUS, Pmode, const_part, - reg_equiv_constant[regno])) - : reg_equiv_constant[regno]); - - if (GET_CODE (var_part) != PLUS) - return addr; - - if (GET_CODE (XEXP (var_part, 0)) == REG - && (regno = REGNO (XEXP (var_part, 0))) >= FIRST_PSEUDO_REGISTER - && reg_renumber[regno] < 0 - && reg_equiv_constant[regno] != 0) - return gen_rtx (PLUS, Pmode, XEXP (var_part, 1), - (const_part - ? gen_rtx (CONST, VOIDmode, - gen_rtx (PLUS, Pmode, const_part, - reg_equiv_constant[regno])) - : reg_equiv_constant[regno])); - - if (GET_CODE (XEXP (var_part, 1)) == REG - && (regno = REGNO (XEXP (var_part, 1))) >= FIRST_PSEUDO_REGISTER - && reg_renumber[regno] < 0 - && reg_equiv_constant[regno] != 0) - return gen_rtx (PLUS, Pmode, XEXP (var_part, 0), - (const_part - ? gen_rtx (CONST, VOIDmode, - gen_rtx (PLUS, Pmode, const_part, - reg_equiv_constant[regno])) - : reg_equiv_constant[regno])); - } - return addr; -} - -/* Record the pseudo registers we must reload into hard registers - in a subexpression of a would-be memory address, X. - (This function is not called if the address we find is strictly valid.) - CONTEXT = 1 means we are considering regs as index regs, - = 0 means we are considering them as base regs. - - OPERAND is the operand of the insn within which this address appears. - - We return nonzero if X, as a whole, is reloaded or replaced. */ - -/* Note that we take shortcuts assuming that no multi-reg machine mode - occurs as part of an address. - Also, this is not fully machine-customizable; it works for machines - such as vaxes and 68000's and 32000's, but other possible machines - could have addressing modes that this does not handle right. */ - -static int -find_reloads_address_1 (x, context, loc, operand) - rtx x; - int context; - rtx *loc; - rtx operand; -{ - register RTX_CODE code = GET_CODE (x); - - if (code == PLUS) - { - register rtx op0 = XEXP (x, 0); - register rtx op1 = XEXP (x, 1); - register RTX_CODE code0 = GET_CODE (op0); - register RTX_CODE code1 = GET_CODE (op1); - if (code0 == MULT || code0 == SIGN_EXTEND || code1 == MEM) - { - find_reloads_address_1 (op0, 1, &XEXP (x, 0), operand); - find_reloads_address_1 (op1, 0, &XEXP (x, 1), operand); - } - else if (code1 == MULT || code1 == SIGN_EXTEND || code0 == MEM) - { - find_reloads_address_1 (op0, 0, &XEXP (x, 0), operand); - find_reloads_address_1 (op1, 1, &XEXP (x, 1), operand); - } - else if (code0 == CONST_INT || code0 == CONST - || code0 == SYMBOL_REF || code0 == LABEL_REF) - { - find_reloads_address_1 (op1, 0, &XEXP (x, 1), operand); - } - else if (code1 == CONST_INT || code1 == CONST - || code1 == SYMBOL_REF || code1 == LABEL_REF) - { - find_reloads_address_1 (op0, 0, &XEXP (x, 0), operand); - } - else if (code0 == REG && code1 == REG) - { - if (REG_OK_FOR_INDEX_P (op0) - && REG_OK_FOR_BASE_P (op1)) - return 0; - else if (REG_OK_FOR_INDEX_P (op1) - && REG_OK_FOR_BASE_P (op0)) - return 0; - else if (REG_OK_FOR_BASE_P (op1)) - find_reloads_address_1 (op0, 1, &XEXP (x, 0), operand); - else if (REG_OK_FOR_BASE_P (op0)) - find_reloads_address_1 (op1, 1, &XEXP (x, 1), operand); - else if (REG_OK_FOR_INDEX_P (op1)) - find_reloads_address_1 (op0, 0, &XEXP (x, 0), operand); - else if (REG_OK_FOR_INDEX_P (op0)) - find_reloads_address_1 (op1, 0, &XEXP (x, 1), operand); - else - { - find_reloads_address_1 (op0, 1, &XEXP (x, 0), operand); - find_reloads_address_1 (op1, 0, &XEXP (x, 1), operand); - } - } - else if (code0 == REG) - { - find_reloads_address_1 (op0, 1, &XEXP (x, 0), operand); - find_reloads_address_1 (op1, 0, &XEXP (x, 1), operand); - } - else if (code1 == REG) - { - find_reloads_address_1 (op1, 1, &XEXP (x, 1), operand); - find_reloads_address_1 (op0, 0, &XEXP (x, 0), operand); - } - } - else if (code == POST_INC || code == POST_DEC - || code == PRE_INC || code == PRE_DEC) - { - rtx incremented = XEXP (x, 0); - - if (GET_CODE (incremented) == REG) - { - register int regno = REGNO (incremented); - int value = 0; - - /* A register that is incremented cannot be constant! */ - if (regno >= FIRST_PSEUDO_REGISTER - && reg_equiv_constant[regno] != 0) - abort (); - - /* Handle a register that is equivalent to a memory location - which cannot be addressed directly. */ - if (reg_equiv_address[regno] != 0) - { - rtx tem = make_memloc (incremented, regno); - /* First reload the memory location's address. */ - push_reload (XEXP (tem, 0), 0, &XEXP (tem, 0), 0, - BASE_REG_CLASS, - GET_MODE (XEXP (tem, 0)), 0, VOIDmode, 0, - operand); - /* Put this inside a new increment-expression. */ - x = gen_rtx (GET_CODE (x), GET_MODE (x), tem); - /* Proceed to reload that, as if it contained a register. */ - } - - /* If we have a hard register that is ok as an index, - don't make a reload. If an autoincrement of a nice register - isn't "valid", it must be that no autoincrement is "valid". - If that is true and something made an autoincrement anyway, - this must be a special context where one is allowed. - (For example, a "push" instruction.) - We can't improve this address, so leave it alone. */ - - /* Otherwise, reload the autoincrement into a suitable hard reg - and record how much to increment by. */ - - if (reg_renumber[regno] >= 0) - regno = reg_renumber[regno]; - if ((regno >= FIRST_PSEUDO_REGISTER - || !(context ? REGNO_OK_FOR_INDEX_P (regno) - : REGNO_OK_FOR_BASE_P (regno)))) - { - register rtx link; - - int reloadnum - = push_reload (x, 0, loc, 0, - context ? INDEX_REG_CLASS : BASE_REG_CLASS, - GET_MODE (x), GET_MODE (x), VOIDmode, 0, operand); - reload_inc[reloadnum] - = find_inc_amount (PATTERN (this_insn), incremented); - - value = 1; - - /* Update the REG_INC notes. */ - - for (link = REG_NOTES (this_insn); - link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_INC - && REGNO (XEXP (link, 0)) == REGNO (incremented)) - push_replacement (&XEXP (link, 0), reloadnum, VOIDmode); - } - return value; - } - } - else if (code == REG) - { - register int regno = REGNO (x); - - if (reg_equiv_constant[regno] != 0) - { - push_reload (reg_equiv_constant[regno], 0, loc, 0, - context ? INDEX_REG_CLASS : BASE_REG_CLASS, - GET_MODE (x), 0, VOIDmode, 0, operand); - return 1; - } - -#if 0 /* This might screw code in reload1.c to delete prior output-reload - that feeds this insn. */ - if (reg_equiv_mem[regno] != 0) - { - push_reload (reg_equiv_mem[regno], 0, loc, 0, - context ? INDEX_REG_CLASS : BASE_REG_CLASS, - GET_MODE (x), 0, VOIDmode, 0, operand); - return 1; - } -#endif - if (reg_equiv_address[regno] != 0) - { - x = make_memloc (x, regno); - push_reload (XEXP (x, 0), 0, &XEXP (x, 0), 0, - BASE_REG_CLASS, - GET_MODE (XEXP (x, 0)), 0, VOIDmode, 0, operand); - } - - if (reg_renumber[regno] >= 0) - regno = reg_renumber[regno]; - if ((regno >= FIRST_PSEUDO_REGISTER - || !(context ? REGNO_OK_FOR_INDEX_P (regno) - : REGNO_OK_FOR_BASE_P (regno)))) - { - push_reload (x, 0, loc, 0, - context ? INDEX_REG_CLASS : BASE_REG_CLASS, - GET_MODE (x), 0, VOIDmode, 0, operand); - return 1; - } - } - else - { - register char *fmt = GET_RTX_FORMAT (code); - register int i; - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - find_reloads_address_1 (XEXP (x, i), context, &XEXP (x, i), operand); - } - } - - return 0; -} - -/* Substitute into X the registers into which we have reloaded - the things that need reloading. The array `replacements' - says contains the locations of all pointers that must be changed - and says what to replace them with. - - Return the rtx that X translates into; usually X, but modified. */ - -void -subst_reloads () -{ - register int i; - - for (i = 0; i < n_replacements; i++) - { - register struct replacement *r = &replacements[i]; - register rtx reloadreg = reload_reg_rtx[r->what]; - if (reloadreg) - { - /* Encapsulate RELOADREG so its machine mode matches what - used to be there. */ - if (GET_MODE (reloadreg) != r->mode && r->mode != VOIDmode) - reloadreg = gen_rtx (SUBREG, r->mode, reloadreg, 0); - *r->where = reloadreg; - } - /* If reload got no reg and isn't optional, something's wrong. */ - else if (! reload_optional[r->what]) - abort (); - } -} - -#if 0 - -/* [[This function is currently obsolete, now that volatility - is represented by a special bit `volatil' so VOLATILE is never used; - and UNCHANGING has never been brought into use.]] - - Alter X by eliminating all VOLATILE and UNCHANGING expressions. - Each of them is replaced by its operand. - Thus, (PLUS (VOLATILE (MEM (REG 5))) (CONST_INT 4)) - becomes (PLUS (MEM (REG 5)) (CONST_INT 4)). - - If X is itself a VOLATILE expression, - we return the expression that should replace it - but we do not modify X. */ - -static rtx -forget_volatility (x) - register rtx x; -{ - enum rtx_code code = GET_CODE (x); - register char *fmt; - register int i; - register rtx value = 0; - - switch (code) - { - case LABEL_REF: - case SYMBOL_REF: - case CONST_INT: - case CONST_DOUBLE: - case CONST: - case REG: - case CC0: - case PC: - return x; - - case VOLATILE: - case UNCHANGING: - return XEXP (x, 0); - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - XEXP (x, i) = forget_volatility (XEXP (x, i)); - if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - XVECEXP (x, i, j) = forget_volatility (XVECEXP (x, i, j)); - } - } - - return x; -} - -#endif - -/* Check the insns before INSN to see if there is a suitable register - containing the same value as GOAL. - If OTHER is -1, look for a register in class CLASS. - Otherwise, just see if register number OTHER shares GOAL's value. - - Return an rtx for the register found, or zero if none is found. - - If RELOAD_REG_P is (short *)1, - we reject any hard reg that appears in reload_reg_rtx - because such a hard reg is also needed coming into this insn. - - If RELOAD_REG_P is any other nonzero value, - it is a vector indexed by hard reg number - and we reject any hard reg whose element in the vector is nonnegative - as well as any that appears in reload_reg_rtx. - - If GOAL is zero, then GOALREG is a register number; we look - for an equivalent for that register. - - MODE is the machine mode of the value we want an equivalence for. - If GOAL is nonzero and not VOIDmode, then it must have mode MODE. - - This function is used by jump.c as well as in the reload pass. - - If GOAL is a PLUS, we assume it adds the stack pointer to a constant. */ - -rtx -find_equiv_reg (goal, insn, class, other, reload_reg_p, goalreg, mode) - register rtx goal; - rtx insn; - enum reg_class class; - register int other; - short *reload_reg_p; - int goalreg; - enum machine_mode mode; -{ - register rtx p = insn; - rtx valtry, value, where; - register rtx pat; - register int regno = -1; - int valueno; - int goal_mem = 0; - int goal_const = 0; - int goal_mem_addr_varies = 0; - int nregs; - int valuenregs; - - if (goal == 0) - regno = goalreg; - else if (GET_CODE (goal) == REG) - regno = REGNO (goal); - else if (GET_CODE (goal) == MEM) - { - enum rtx_code code = GET_CODE (XEXP (goal, 0)); - if (MEM_VOLATILE_P (goal)) - return 0; - if (flag_float_store - && (GET_MODE (goal) == DFmode || GET_MODE (goal) == SFmode)) - return 0; - /* An address with side effects must be reexecuted. */ - switch (code) - { - case POST_INC: - case PRE_INC: - case POST_DEC: - case PRE_DEC: - return 0; - } - goal_mem = 1; - } - else if (CONSTANT_P (goal)) - goal_const = 1; - else - return 0; - - /* On some machines, certain regs must always be rejected - because they don't behave the way ordinary registers do. */ - -#ifdef OVERLAPPING_REGNO_P - if (regno >= 0 && regno < FIRST_PSEUDO_REGISTER - && OVERLAPPING_REGNO_P (regno)) - return 0; -#endif - - /* Scan insns back from INSN, looking for one that copies - a value into or out of GOAL. - Stop and give up if we reach a label. */ - - while (1) - { - p = PREV_INSN (p); - if (p == 0 || GET_CODE (p) == CODE_LABEL) - return 0; - if (GET_CODE (p) == INSN - /* If we don't want spill regs (true for all calls in this file) */ - && (! (reload_reg_p != 0 && reload_reg_p != (short *)1) - /* then ignore insns introduced by reload; they aren't useful - and can cause results in reload_as_needed to be different - from what they were when calculating the need for spills. - If we notice an input-reload insn here, we will reject it below, - but it might hide a usable equivalent. That makes bad code. - It may even abort: perhaps no reg was spilled for this insn - because it was assumed we would find that equivalent. */ - || INSN_UID (p) < reload_first_uid)) - { - pat = PATTERN (p); - /* First check for something that sets some reg equal to GOAL. */ - if (GET_CODE (pat) == SET - && ((regno >= 0 - && GET_CODE (SET_SRC (pat)) == REG - && REGNO (SET_SRC (pat)) == regno - && GET_CODE (valtry = SET_DEST (pat)) == REG) - || - (regno >= 0 - && GET_CODE (SET_DEST (pat)) == REG - && REGNO (SET_DEST (pat)) == regno - && GET_CODE (valtry = SET_SRC (pat)) == REG) - || - (goal_const && rtx_equal_p (SET_SRC (pat), goal) - && GET_CODE (valtry = SET_DEST (pat)) == REG) - || (goal_mem - && GET_CODE (valtry = SET_DEST (pat)) == REG - && rtx_renumbered_equal_p (goal, SET_SRC (pat))) - || (goal_mem - && GET_CODE (valtry = SET_SRC (pat)) == REG - && rtx_renumbered_equal_p (goal, SET_DEST (pat))))) - if (valueno = REGNO (valtry), - other >= 0 - ? valueno == other - : ((unsigned) valueno < FIRST_PSEUDO_REGISTER - && TEST_HARD_REG_BIT (reg_class_contents[(int) class], - valueno))) - { - value = valtry; - where = p; - break; - } - } - } - - /* We found a previous insn copying GOAL into a suitable other reg VALUE - (or copying VALUE into GOAL, if GOAL is also a register). - Now verify that VALUE is really valid. */ - - /* VALUENO is the register number of VALUE; a hard register. */ - - /* Don't find the sp as an equiv, since pushes that we don't notice - would invalidate it. */ - if (valueno == STACK_POINTER_REGNUM) - return 0; - - /* Reject VALUE if the copy-insn moved the wrong sort of datum. */ - if (GET_MODE (value) != mode) - return 0; - - /* Reject VALUE if it was loaded from GOAL - and is also a register that appears in the address of GOAL. */ - - if (goal_mem && value == SET_DEST (PATTERN (where)) - && refers_to_regno_p (valueno, - valueno + HARD_REGNO_NREGS (valueno, mode), - goal, 0)) - return 0; - - /* Reject registers that overlap GOAL. */ - - if (!goal_mem && !goal_const - && regno + HARD_REGNO_NREGS (regno, mode) > valueno - && regno < valueno + HARD_REGNO_NREGS (valueno, mode)) - return 0; - - /* Reject VALUE if it is one of the regs reserved for reloads. - Reload1 knows how to reuse them anyway, and it would get - confused if we allocated one without its knowledge. - (Now that insns introduced by reload are ignored above, - this case shouldn't happen, but I'm not positive.) */ - - if (reload_reg_p != 0 && reload_reg_p != (short *)1 - && reload_reg_p[valueno] >= 0) - return 0; - - /* On some machines, certain regs must always be rejected - because they don't behave the way ordinary registers do. */ - -#ifdef OVERLAPPING_REGNO_P - if (OVERLAPPING_REGNO_P (valueno)) - return 0; -#endif - - nregs = HARD_REGNO_NREGS (regno, mode); - valuenregs = HARD_REGNO_NREGS (valueno, mode); - - /* Reject VALUE if it is a register being used for an input reload - even if it is not one of those reserved. */ - - if (reload_reg_p != 0) - { - int i; - for (i = 0; i < n_reloads; i++) - if (reload_reg_rtx[i] != 0 && reload_in[i]) - { - int regno1 = REGNO (reload_reg_rtx[i]); - int nregs1 = HARD_REGNO_NREGS (regno1, - GET_MODE (reload_reg_rtx[i])); - if (regno1 < valueno + valuenregs - && regno1 + nregs1 > valueno) - return 0; - } - } - - if (goal_mem) - goal_mem_addr_varies = rtx_addr_varies_p (goal); - - /* Now verify that the values of GOAL and VALUE remain unaltered - until INSN is reached. */ - - p = insn; - while (1) - { - p = PREV_INSN (p); - if (p == where) - return value; - - /* Don't trust the conversion past a function call - if either of the two is in a call-clobbered register, or memory. */ - if (GET_CODE (p) == CALL_INSN - && ((regno >= 0 && regno < FIRST_PSEUDO_REGISTER - && call_used_regs[regno]) - || - (valueno >= 0 && valueno < FIRST_PSEUDO_REGISTER - && call_used_regs[valueno]) - || - goal_mem)) - return 0; - - if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN - || GET_CODE (p) == CALL_INSN) - { - /* If this insn P stores in either GOAL or VALUE, return 0. - If GOAL is a memory ref and this insn writes memory, return 0. - If GOAL is a memory ref and its address is not constant, - and this insn P changes a register, return 0. - That is in lieue of checking whether GOAL uses this register. */ - - pat = PATTERN (p); - if (GET_CODE (pat) == SET || GET_CODE (pat) == CLOBBER) - { - register rtx dest = SET_DEST (pat); - while (GET_CODE (dest) == SUBREG - || GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == STRICT_LOW_PART) - dest = XEXP (dest, 0); - if (GET_CODE (dest) == REG) - { - register int xregno = REGNO (dest); - int xnregs; - if (REGNO (dest) < FIRST_PSEUDO_REGISTER) - xnregs = HARD_REGNO_NREGS (xregno, GET_MODE (dest)); - else - xnregs = 1; - if (xregno < regno + nregs && xregno + xnregs > regno) - return 0; - if (xregno < valueno + valuenregs - && xregno + xnregs > valueno) - return 0; - if (goal_mem_addr_varies) - return 0; - } - else if (goal_mem && GET_CODE (dest) == MEM - && ! push_operand (dest, GET_MODE (dest))) - return 0; - } - else if (GET_CODE (pat) == PARALLEL) - { - register int i; - for (i = XVECLEN (pat, 0) - 1; i >= 0; i--) - { - register rtx v1 = XVECEXP (pat, 0, i); - if (GET_CODE (v1) == SET || GET_CODE (v1) == CLOBBER) - { - register rtx dest = SET_DEST (v1); - while (GET_CODE (dest) == SUBREG - || GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == STRICT_LOW_PART) - dest = XEXP (dest, 0); - if (GET_CODE (dest) == REG) - { - register int xregno = REGNO (dest); - int xnregs; - if (REGNO (dest) < FIRST_PSEUDO_REGISTER) - xnregs = HARD_REGNO_NREGS (xregno, GET_MODE (dest)); - else - xnregs = 1; - if (xregno < regno + nregs - && xregno + xnregs > regno) - return 0; - if (xregno < valueno + valuenregs - && xregno + xnregs > valueno) - return 0; - if (goal_mem_addr_varies) - return 0; - } - else if (goal_mem && GET_CODE (dest) == MEM - && ! push_operand (dest, GET_MODE (dest))) - return 0; - } - } - } - /* If this insn auto-increments or auto-decrements - either regno or valueno, return 0 now. - If GOAL is a memory ref and its address is not constant, - and this insn P increments a register, return 0. - That is in lieue of checking whether GOAL uses this register. */ - { - register rtx link; - - for (link = REG_NOTES (p); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_INC) - { - register int incno = REGNO (XEXP (link, 0)); - if (incno < regno + nregs && incno >= regno) - return 0; - if (incno < valueno + valuenregs && incno >= valueno) - return 0; - if (goal_mem_addr_varies) - return 0; - } - } - } - } -} - -/* Find a place where INCED appears in an increment or decrement operator - within X, and return the amount INCED is incremented or decremented by. - The value is always positive. */ - -static int -find_inc_amount (x, inced) - rtx x, inced; -{ - register enum rtx_code code = GET_CODE (x); - register char *fmt; - register int i; - - if (code == MEM) - { - register rtx addr = XEXP (x, 0); - if ((GET_CODE (addr) == PRE_DEC - || GET_CODE (addr) == POST_DEC - || GET_CODE (addr) == PRE_INC - || GET_CODE (addr) == POST_INC) - && XEXP (addr, 0) == inced) - return GET_MODE_SIZE (GET_MODE (x)); - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - register int tem = find_inc_amount (XEXP (x, i), inced); - if (tem != 0) - return tem; - } - if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - { - register int tem = find_inc_amount (XVECEXP (x, i, j), inced); - if (tem != 0) - return tem; - } - } - } - - return 0; -} diff --git a/gnu/usr.bin/gcc1/cc1/reload.h b/gnu/usr.bin/gcc1/cc1/reload.h deleted file mode 100644 index c0dcd16c8a..0000000000 --- a/gnu/usr.bin/gcc1/cc1/reload.h +++ /dev/null @@ -1,69 +0,0 @@ -/* Communication between reload.c and reload1.c. - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* See reload.c and reload1.c for comments on these variables. */ - -/* Maximum number of reloads we can need. */ -#define MAX_RELOADS (2 * MAX_RECOG_OPERANDS * (MAX_REGS_PER_ADDRESS + 1)) - -extern rtx reload_in[MAX_RELOADS]; -extern rtx reload_out[MAX_RELOADS]; -extern rtx reload_in_reg[MAX_RELOADS]; -extern enum reg_class reload_reg_class[MAX_RELOADS]; -extern enum machine_mode reload_inmode[MAX_RELOADS]; -extern enum machine_mode reload_outmode[MAX_RELOADS]; -extern char reload_strict_low[MAX_RELOADS]; -extern char reload_optional[MAX_RELOADS]; -extern int reload_inc[MAX_RELOADS]; -extern int reload_needed_for_multiple[MAX_RELOADS]; -extern rtx reload_needed_for[MAX_RELOADS]; -extern int n_reloads; - -extern rtx reload_reg_rtx[MAX_RELOADS]; - -enum reload_when_needed -{ - RELOAD_FOR_INPUT_RELOAD_ADDRESS, - RELOAD_FOR_OUTPUT_RELOAD_ADDRESS, - RELOAD_FOR_OPERAND_ADDRESS, - RELOAD_OTHER -}; - -extern enum reload_when_needed reload_when_needed[MAX_RELOADS]; - -extern rtx *reg_equiv_constant; -extern rtx *reg_equiv_address; -extern rtx *reg_equiv_mem; - -/* All the "earlyclobber" operands of the current insn - are recorded here. */ -extern int n_earlyclobbers; -extern rtx reload_earlyclobbers[MAX_RECOG_OPERANDS]; - -/* First uid used by insns created by reload in this function. - Used in find_equiv_reg. */ -extern int reload_first_uid; - -/* Nonzero if an address (plus (reg frame_pointer) (reg ...)) is valid. */ -extern char double_reg_address_ok; - -void init_reload (); -void find_reloads (); -void subst_reloads (); diff --git a/gnu/usr.bin/gcc1/cc1/reload1.c b/gnu/usr.bin/gcc1/cc1/reload1.c deleted file mode 100644 index 67968f68fb..0000000000 --- a/gnu/usr.bin/gcc1/cc1/reload1.c +++ /dev/null @@ -1,3466 +0,0 @@ -/* Reload pseudo regs into hard regs for insns that require hard regs. - Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "config.h" -#include "rtl.h" -#include "insn-config.h" -#include "flags.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "reload.h" -#include "recog.h" -#include "basic-block.h" -#include - -#define min(A,B) ((A) < (B) ? (A) : (B)) -#define max(A,B) ((A) > (B) ? (A) : (B)) - -/* This file contains the reload pass of the compiler, which is - run after register allocation has been done. It checks that - each insn is valid (operands required to be in registers really - are in registers of the proper class) and fixes up invalid ones - by copying values temporarily into registers for the insns - that need them. - - The results of register allocation are described by the vector - reg_renumber; the insns still contain pseudo regs, but reg_renumber - can be used to find which hard reg, if any, a pseudo reg is in. - - The technique we always use is to free up a few hard regs that are - called ``reload regs'', and for each place where a pseudo reg - must be in a hard reg, copy it temporarily into one of the reload regs. - - All the pseudos that were formerly allocated to the hard regs that - are now in use as reload regs must be ``spilled''. This means - that they go to other hard regs, or to stack slots if no other - available hard regs can be found. Spilling can invalidate more - insns, requiring additional need for reloads, so we must keep checking - until the process stabilizes. - - For machines with different classes of registers, we must keep track - of the register class needed for each reload, and make sure that - we allocate enough reload registers of each class. - - The file reload.c contains the code that checks one insn for - validity and reports the reloads that it needs. This file - is in charge of scanning the entire rtl code, accumulating the - reload needs, spilling, assigning reload registers to use for - fixing up each insn, and generating the new insns to copy values - into the reload registers. */ - -/* During reload_as_needed, element N contains a REG rtx for the hard reg - into which pseudo reg N has been reloaded (perhaps for a previous insn). */ -static rtx *reg_last_reload_reg; - -/* Elt N nonzero if reg_last_reload_reg[N] has been set in this insn - for an output reload that stores into reg N. */ -static char *reg_has_output_reload; - -/* Elt N nonzero if hard reg N is a reload-register for an output reload - in the current insn. */ -static char reg_is_output_reload[FIRST_PSEUDO_REGISTER]; - -/* Element N is the constant value to which pseudo reg N is equivalent, - or zero if pseudo reg N is not equivalent to a constant. - find_reloads looks at this in order to replace pseudo reg N - with the constant it stands for. */ -rtx *reg_equiv_constant; - -/* Element N is the address of stack slot to which pseudo reg N is equivalent. - This is used when the address is not valid as a memory address - (because its displacement is too big for the machine.) */ -rtx *reg_equiv_address; - -/* Element N is the memory slot to which pseudo reg N is equivalent, - or zero if pseudo reg N is not equivalent to a memory slot. */ -rtx *reg_equiv_mem; - -/* Widest width in which each pseudo reg is referred to (via subreg). */ -static int *reg_max_ref_width; - -/* Element N is the insn that initialized reg N from its equivalent - constant or memory slot. */ -static rtx *reg_equiv_init; - -/* During reload_as_needed, element N contains the last pseudo regno - reloaded into the Nth reload register. This vector is in parallel - with spill_regs. */ -static int reg_reloaded_contents[FIRST_PSEUDO_REGISTER]; - -/* During reload_as_needed, element N contains the insn for which - the Nth reload register was last used. This vector is in parallel - with spill_regs, and its contents are significant only when - reg_reloaded_contents is significant. */ -static rtx reg_reloaded_insn[FIRST_PSEUDO_REGISTER]; - -/* Number of spill-regs so far; number of valid elements of spill_regs. */ -static int n_spills; - -/* In parallel with spill_regs, contains REG rtx's for those regs. - Holds the last rtx used for any given reg, or 0 if it has never - been used for spilling yet. This rtx is reused, provided it has - the proper mode. */ -static rtx spill_reg_rtx[FIRST_PSEUDO_REGISTER]; - -/* In parallel with spill_regs, contains nonzero for a spill reg - that was stored after the last time it was used. - The precise value is the insn generated to do the store. */ -static rtx spill_reg_store[FIRST_PSEUDO_REGISTER]; - -/* This table is the inverse mapping of spill_regs: - indexed by hard reg number, - it contains the position of that reg in spill_regs, - or -1 for something that is not in spill_regs. */ -static short spill_reg_order[FIRST_PSEUDO_REGISTER]; - -/* This table contains 1 for a register that may not be used - for retrying global allocation, or -1 for a register that may be used. - The registers that may not be used include all spill registers - and the frame pointer (if we are using one). */ -static short forbidden_regs[FIRST_PSEUDO_REGISTER]; - -/* Describes order of use of registers for reloading - of spilled pseudo-registers. `spills' is the number of - elements that are actually valid; new ones are added at the end. */ -static char spill_regs[FIRST_PSEUDO_REGISTER]; - -/* Describes order of preference for putting regs into spill_regs. - Contains the numbers of all the hard regs, in order most preferred first. - This order is different for each function. - It is set up by order_regs_for_reload. - Empty elements at the end contain -1. */ -static short potential_reload_regs[FIRST_PSEUDO_REGISTER]; - -/* 1 for a hard register that appears explicitly in the rtl - (for example, function value registers, special registers - used by insns, structure value pointer registers). */ -static char regs_explicitly_used[FIRST_PSEUDO_REGISTER]; - -/* For each register, 1 if it was counted against the need for - groups. 0 means it can count against max_nongroup instead. */ -static char counted_for_groups[FIRST_PSEUDO_REGISTER]; - -/* For each register, 1 if it was counted against the need for - non-groups. 0 means it can become part of a new group. - During choose_reload_regs, 1 here means don't use this reg - as part of a group, even if it seems to be otherwise ok. */ -static char counted_for_nongroups[FIRST_PSEUDO_REGISTER]; - -/* Nonzero if spilling (REG n) does not require reloading it into - a register in order to do (MEM (REG n)). */ - -static char spill_indirect_ok; - -/* Nonzero if an address (plus (reg frame_pointer) (reg ...)) is valid. */ - -char double_reg_address_ok; - -/* Record the stack slot for each spilled hard register. */ - -static rtx spill_stack_slot[FIRST_PSEUDO_REGISTER]; - -/* Width allocated so far for that stack slot. */ - -static int spill_stack_slot_width[FIRST_PSEUDO_REGISTER]; - -/* Indexed by basic block number, nonzero if there is any need - for a spill register in that basic block. - The pointer is 0 if we did stupid allocation and don't know - the structure of basic blocks. */ - -char *basic_block_needs; - -/* First uid used by insns created by reload in this function. - Used in find_equiv_reg. */ -int reload_first_uid; - -/* Flag set by local-alloc or global-alloc if anything is live in - a call-clobbered reg across calls. */ - -int caller_save_needed; - -/* Set to 1 by alter_frame_pointer_addresses if it changes anything. */ - -static int frame_pointer_address_altered; - -void mark_home_live (); -static rtx scan_paradoxical_subregs (); -static void reload_as_needed (); -static int modes_equiv_for_class_p (); -static rtx alter_frame_pointer_addresses (); -static void alter_reg (); -static int new_spill_reg(); -static int spill_hard_reg (); -static void choose_reload_regs (); -static void emit_reload_insns (); -static void delete_output_reload (); -static void forget_old_reloads_1 (); -static void order_regs_for_reload (); -static void eliminate_frame_pointer (); -static rtx inc_for_reload (); -static int constraint_accepts_reg_p (); -static int count_occurrences (); -static rtx gen_input_reload (); - -extern void remove_death (); -extern rtx adj_offsettable_operand (); - -/* Main entry point for the reload pass, and only entry point - in this file. - - FIRST is the first insn of the function being compiled. - - GLOBAL nonzero means we were called from global_alloc - and should attempt to reallocate any pseudoregs that we - displace from hard regs we will use for reloads. - If GLOBAL is zero, we do not have enough information to do that, - so any pseudo reg that is spilled must go to the stack. - - DUMPFILE is the global-reg debugging dump file stream, or 0. - If it is nonzero, messages are written to it to describe - which registers are seized as reload regs, which pseudo regs - are spilled from them, and where the pseudo regs are reallocated to. */ - -void -reload (first, global, dumpfile) - rtx first; - int global; - FILE *dumpfile; -{ - register int class; - register int i; - register rtx insn; - - int something_changed; - int something_needs_reloads; - int new_basic_block_needs; - - /* The basic block number currently being processed for INSN. */ - int this_block; - - /* Often (MEM (REG n)) is still valid even if (REG n) is put on the stack. - Set spill_indirect_ok if so. */ - register rtx tem - = gen_rtx (MEM, Pmode, - gen_rtx (PLUS, Pmode, - gen_rtx (REG, Pmode, FRAME_POINTER_REGNUM), - gen_rtx (CONST_INT, VOIDmode, 4))); - - spill_indirect_ok = memory_address_p (QImode, tem); - - tem = gen_rtx (PLUS, Pmode, - gen_rtx (REG, Pmode, FRAME_POINTER_REGNUM), - gen_rtx (REG, Pmode, FRAME_POINTER_REGNUM)); - /* This way, we make sure that reg+reg is an offsettable address. */ - tem = plus_constant (tem, 4); - - double_reg_address_ok = memory_address_p (QImode, tem); - - /* Enable find_equiv_reg to distinguish insns made by reload. */ - reload_first_uid = get_max_uid (); - - basic_block_needs = 0; - - /* Remember which hard regs appear explicitly - before we merge into `regs_ever_live' the ones in which - pseudo regs have been allocated. */ - bcopy (regs_ever_live, regs_explicitly_used, sizeof regs_ever_live); - - /* We don't have a stack slot for any spill reg yet. */ - bzero (spill_stack_slot, sizeof spill_stack_slot); - bzero (spill_stack_slot_width, sizeof spill_stack_slot_width); - - /* Compute which hard registers are now in use - as homes for pseudo registers. - This is done here rather than (eg) in global_alloc - because this point is reached even if not optimizing. */ - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - mark_home_live (i); - - /* Make sure that the last insn in the chain - is not something that needs reloading. */ - emit_note (0, NOTE_INSN_DELETED); - - /* Find all the pseudo registers that didn't get hard regs - but do have known equivalent constants or memory slots. - These include parameters (known equivalent to parameter slots) - and cse'd or loop-moved constant memory addresses. - - Record constant equivalents in reg_equiv_constant - so they will be substituted by find_reloads. - Record memory equivalents in reg_mem_equiv so they can - be substituted eventually by altering the REG-rtx's. */ - - reg_equiv_constant = (rtx *) alloca (max_regno * sizeof (rtx)); - bzero (reg_equiv_constant, max_regno * sizeof (rtx)); - reg_equiv_mem = (rtx *) alloca (max_regno * sizeof (rtx)); - bzero (reg_equiv_mem, max_regno * sizeof (rtx)); - reg_equiv_init = (rtx *) alloca (max_regno * sizeof (rtx)); - bzero (reg_equiv_init, max_regno * sizeof (rtx)); - reg_equiv_address = (rtx *) alloca (max_regno * sizeof (rtx)); - bzero (reg_equiv_address, max_regno * sizeof (rtx)); - reg_max_ref_width = (int *) alloca (max_regno * sizeof (int)); - bzero (reg_max_ref_width, max_regno * sizeof (int)); - - /* Look for REG_EQUIV notes; record what each pseudo is equivalent to. - Also find all paradoxical subregs - and find largest such for each pseudo. */ - - for (insn = first; insn; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == INSN - && GET_CODE (PATTERN (insn)) == SET - && GET_CODE (SET_DEST (PATTERN (insn))) == REG) - { - rtx note = find_reg_note (insn, REG_EQUIV, 0); - if (note) - { - rtx x = XEXP (note, 0); - i = REGNO (SET_DEST (PATTERN (insn))); - if (i >= FIRST_PSEUDO_REGISTER) - { - if (GET_CODE (x) == MEM) - { - if (memory_address_p (GET_MODE (x), XEXP (x, 0))) - reg_equiv_mem[i] = x; - else - reg_equiv_address[i] = XEXP (x, 0); - } - else if (immediate_operand (x, VOIDmode)) - reg_equiv_constant[i] = x; - else - continue; - reg_equiv_init[i] = insn; - } - } - } - if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN - || GET_CODE (insn) == JUMP_INSN) - scan_paradoxical_subregs (PATTERN (insn)); - } - - /* Does this function require a frame pointer? */ - - frame_pointer_needed - |= (! global || FRAME_POINTER_REQUIRED); - - if (! frame_pointer_needed) - frame_pointer_needed - = check_frame_pointer_required (reg_equiv_constant, - reg_equiv_mem, reg_equiv_address); - - /* Alter each pseudo-reg rtx to contain its hard reg number. - Delete initializations of pseudos that don't have hard regs - and do have equivalents. - Assign stack slots to the pseudos that lack hard regs or equivalents. */ - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - alter_reg (i, -1); - -#ifndef REGISTER_CONSTRAINTS - /* If all the pseudo regs have hard regs, - except for those that are never referenced, - we know that no reloads are needed. */ - /* But that is not true if there are register constraints, since - in that case some pseudos might be in the wrong kind of hard reg. */ - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (reg_renumber[i] == -1 && reg_n_refs[i] != 0) - break; - - if (i == max_regno && frame_pointer_needed && ! caller_save_needed) - return; -#endif - - /* Compute the order of preference for hard registers to spill. - Store them by decreasing preference in potential_reload_regs. */ - - order_regs_for_reload (); - - /* So far, no hard regs have been spilled. */ - n_spills = 0; - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - spill_reg_order[i] = -1; - forbidden_regs[i] = -1; - } - - if (caller_save_needed) - frame_pointer_needed = 1; - - if (frame_pointer_needed) - { - forbidden_regs[FRAME_POINTER_REGNUM] = 1; - spill_hard_reg (FRAME_POINTER_REGNUM, global, dumpfile); - } - - if (global) - { - basic_block_needs = (char *)alloca (n_basic_blocks); - bzero (basic_block_needs, n_basic_blocks); - } - - /* This loop scans the entire function each go-round - and repeats until one repetition spills no additional hard regs. */ - - /* This flag is set when a psuedo reg is spilled, - to require another pass. Note that getting an additional reload - reg does not necessarily imply any pseudo reg was spilled; - sometimes we find a reload reg that no pseudo reg was allocated in. */ - something_changed = 1; - /* This flag is set if there are any insns that require reloading. */ - something_needs_reloads = 0; - while (something_changed) - { - /* For each class, number of reload regs needed in that class. - This is the maximum over all insns of the needs in that class - of the individual insn. */ - int max_needs[N_REG_CLASSES]; - /* For each class, size of group of consecutive regs - that is needed for the reloads of this class. */ - int group_size[N_REG_CLASSES]; - /* For each class, max number of consecutive groups needed. - (Each group contains max_needs_size[CLASS] consecutive registers.) */ - int max_groups[N_REG_CLASSES]; - /* For each class, max number needed of regs that don't belong - to any of the groups. */ - int max_nongroups[N_REG_CLASSES]; - /* For each class, the machine mode which requires consecutive - groups of regs of that class. - If two different modes ever require groups of one class, - they must be the same size and equally restrictive for that class, - otherwise we can't handle the complexity. */ - enum machine_mode group_mode[N_REG_CLASSES]; - - something_changed = 0; - bzero (max_needs, sizeof max_needs); - bzero (max_groups, sizeof max_groups); - bzero (max_nongroups, sizeof max_nongroups); - bzero (group_size, sizeof group_size); - for (i = 0; i < N_REG_CLASSES; i++) - group_mode[i] = VOIDmode; - - /* Keep track of which basic blocks are needing the reloads. */ - this_block = 0; - - /* Remember whether any element of basic_block_needs - changes from 0 to 1 in this pass. */ - new_basic_block_needs = 0; - - /* Compute the most additional registers needed by any instruction. - Collect information separately for each class of regs. */ - - for (insn = first; insn; insn = NEXT_INSN (insn)) - { - rtx after_call = 0; - - if (global && this_block + 1 < n_basic_blocks - && insn == basic_block_head[this_block+1]) - ++this_block; - - if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN - || GET_CODE (insn) == CALL_INSN) - { - /* Nonzero means don't use a reload reg that overlaps - the place where a function value can be returned. */ - rtx avoid_return_reg = 0; - /* Initially, count RELOAD_OTHER reloads. - Later, merge in the other kinds. */ - int insn_needs[N_REG_CLASSES]; - int insn_groups[N_REG_CLASSES]; - int insn_total_groups = 0; - - /* Count RELOAD_FOR_INPUT_RELOAD_ADDRESS reloads. */ - int insn_needs_for_inputs[N_REG_CLASSES]; - int insn_groups_for_inputs[N_REG_CLASSES]; - int insn_total_groups_for_inputs = 0; - - /* Count RELOAD_FOR_OUTPUT_RELOAD_ADDRESS reloads. */ - int insn_needs_for_outputs[N_REG_CLASSES]; - int insn_groups_for_outputs[N_REG_CLASSES]; - int insn_total_groups_for_outputs = 0; - - /* Count RELOAD_FOR_OPERAND_ADDRESS reloads. */ - int insn_needs_for_operands[N_REG_CLASSES]; - int insn_groups_for_operands[N_REG_CLASSES]; - int insn_total_groups_for_operands = 0; - - for (i = 0; i < N_REG_CLASSES; i++) - { - insn_needs[i] = 0, insn_groups[i] = 0; - insn_needs_for_inputs[i] = 0, insn_groups_for_inputs[i] = 0; - insn_needs_for_outputs[i] = 0, insn_groups_for_outputs[i] = 0; - insn_needs_for_operands[i] = 0, insn_groups_for_operands[i] = 0; - } - -#if 0 /* This wouldn't work nowadays, since optimize_bit_field - looks for non-strict memory addresses. */ - /* Optimization: a bit-field instruction whose field - happens to be a byte or halfword in memory - can be changed to a move instruction. */ - - if (GET_CODE (PATTERN (insn)) == SET) - { - rtx dest = SET_DEST (PATTERN (insn)); - rtx src = SET_SRC (PATTERN (insn)); - - if (GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SIGN_EXTRACT) - optimize_bit_field (PATTERN (insn), insn, reg_equiv_mem); - if (GET_CODE (src) == ZERO_EXTRACT - || GET_CODE (src) == SIGN_EXTRACT) - optimize_bit_field (PATTERN (insn), insn, reg_equiv_mem); - } -#endif - - /* Set avoid_return_reg if this is an insn - that might use the value of a function call. */ - if (GET_CODE (insn) == CALL_INSN) - { - if (GET_CODE (PATTERN (insn)) == SET) - after_call = SET_DEST (PATTERN (insn)); - else if (GET_CODE (PATTERN (insn)) == PARALLEL - && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) - after_call = SET_DEST (XVECEXP (PATTERN (insn), 0, 0)); - else - after_call = 0; - } - else if (after_call != 0 - && !(GET_CODE (PATTERN (insn)) == SET - && SET_DEST (PATTERN (insn)) == stack_pointer_rtx)) - { - if (reg_mentioned_p (after_call, PATTERN (insn))) - avoid_return_reg = after_call; - after_call = 0; - } - - /* Analyze the instruction. */ - - find_reloads (insn, 0, spill_indirect_ok, global, spill_reg_order); - - if (n_reloads == 0) - continue; - - something_needs_reloads = 1; - - /* Count each reload once in every class - containing the reload's own class. */ - - for (i = 0; i < n_reloads; i++) - { - register enum reg_class *p; - int size; - enum machine_mode mode; - int *this_groups; - int *this_needs; - int *this_total_groups; - - /* Don't use dummy reloads in regs - being spilled in this block. */ - if (reload_reg_rtx[i] != 0 - && (!global || basic_block_needs[this_block]) - && spill_reg_order[REGNO (reload_reg_rtx[i])] >= 0) - reload_reg_rtx[i] = 0; - - /* Don't count the dummy reloads, for which one of the - regs mentioned in the insn can be used for reloading. - Don't count optional reloads. - Don't count reloads that got combined with others. */ - if (reload_reg_rtx[i] != 0 - || reload_optional[i] != 0 - || (reload_out[i] == 0 && reload_in[i] == 0)) - continue; - - /* Decide which time-of-use to count this reload for. */ - switch (reload_when_needed[i]) - { - case RELOAD_OTHER: - this_needs = insn_needs; - this_groups = insn_groups; - this_total_groups = &insn_total_groups; - break; - - case RELOAD_FOR_INPUT_RELOAD_ADDRESS: - this_needs = insn_needs_for_inputs; - this_groups = insn_groups_for_inputs; - this_total_groups = &insn_total_groups_for_inputs; - break; - - case RELOAD_FOR_OUTPUT_RELOAD_ADDRESS: - this_needs = insn_needs_for_outputs; - this_groups = insn_groups_for_outputs; - this_total_groups = &insn_total_groups_for_outputs; - break; - - case RELOAD_FOR_OPERAND_ADDRESS: - this_needs = insn_needs_for_operands; - this_groups = insn_groups_for_operands; - this_total_groups = &insn_total_groups_for_operands; - break; - } - - mode = reload_inmode[i]; - if (GET_MODE_SIZE (reload_outmode[i]) > GET_MODE_SIZE (mode)) - mode = reload_outmode[i]; - size = CLASS_MAX_NREGS (reload_reg_class[i], mode); - if (size > 1) - { - /* Count number of groups needed separately from - number of individual regs needed. */ - this_groups[(int) reload_reg_class[i]]++; - p = reg_class_superclasses[(int) reload_reg_class[i]]; - while (*p != LIM_REG_CLASSES) - this_groups[(int) *p++]++; - (*this_total_groups)++; - - /* If a group of consecutive regs are needed, - record which machine mode needs them. - Crash if two dissimilar machine modes both need - groups of consecutive regs of the same class. */ - - if (group_mode[(int) reload_reg_class[i]] != VOIDmode - && - (! modes_equiv_for_class_p (group_mode[(int) reload_reg_class[i]], mode, reload_reg_class[i]) - || - group_size[(int) reload_reg_class[i]] != size)) - abort (); - - /* Record size and mode of a group of this class. */ - group_size[(int) reload_reg_class[i]] = size; - group_mode[(int) reload_reg_class[i]] = mode; - } - else if (size == 1) - { - this_needs[(int) reload_reg_class[i]] += 1; - p = reg_class_superclasses[(int) reload_reg_class[i]]; - while (*p != LIM_REG_CLASSES) - this_needs[(int) *p++] += 1; - } - else - abort (); - - if (global) - { - if (! basic_block_needs[this_block]) - new_basic_block_needs = 1; - basic_block_needs[this_block] = 1; - } - } - - /* All reloads have been counted for this insn; - now merge the various times of use. - This sets insn_needs, etc., to the maximum total number - of registers needed at any point in this insn. */ - - for (i = 0; i < N_REG_CLASSES; i++) - { - int this_max; - this_max = insn_needs_for_inputs[i]; - if (insn_needs_for_outputs[i] > this_max) - this_max = insn_needs_for_outputs[i]; - if (insn_needs_for_operands[i] > this_max) - this_max = insn_needs_for_operands[i]; - insn_needs[i] += this_max; - this_max = insn_groups_for_inputs[i]; - if (insn_groups_for_outputs[i] > this_max) - this_max = insn_groups_for_outputs[i]; - if (insn_groups_for_operands[i] > this_max) - this_max = insn_groups_for_operands[i]; - insn_groups[i] += this_max; - } - insn_total_groups += max (insn_total_groups_for_inputs, - max (insn_total_groups_for_outputs, - insn_total_groups_for_operands)); - - /* Remember for later shortcuts which insns had any reloads. */ - - PUT_MODE (insn, n_reloads ? QImode : VOIDmode); - - /* If this insn stores the value of a function call, - and that value is in a register that has been spilled, - and if the insn needs a reload in a class - that might use that register as the reload register, - then add add an extra need in that class. - This makes sure we have a register available that does - not overlap the return value. */ - if (avoid_return_reg) - { - int regno = REGNO (avoid_return_reg); - int nregs - = HARD_REGNO_NREGS (regno, GET_MODE (avoid_return_reg)); - int r; - int inc_groups = 0; - for (r = regno; r < regno + nregs; r++) - if (spill_reg_order[r] >= 0) - for (i = 0; i < N_REG_CLASSES; i++) - if (TEST_HARD_REG_BIT (reg_class_contents[i], r)) - { - if (insn_needs[i] > 0) - insn_needs[i]++; - if (insn_groups[i] > 0 - && nregs > 1) - inc_groups = 1; - } - if (inc_groups) - insn_groups[i]++; - } - - /* For each class, collect maximum need of any insn. */ - - for (i = 0; i < N_REG_CLASSES; i++) - { - if (max_needs[i] < insn_needs[i]) - max_needs[i] = insn_needs[i]; - if (max_groups[i] < insn_groups[i]) - max_groups[i] = insn_groups[i]; - if (insn_total_groups > 0) - if (max_nongroups[i] < insn_needs[i]) - max_nongroups[i] = insn_needs[i]; - } - } - /* Note that there is a continue statement above. */ - } - - /* Now deduct from the needs for the registers already - available (already spilled). */ - - bzero (counted_for_groups, sizeof counted_for_groups); - bzero (counted_for_nongroups, sizeof counted_for_nongroups); - - /* Find all consecutive groups of spilled registers - and mark each group off against the need for such groups. */ - - for (i = 0; i < N_REG_CLASSES; i++) - if (group_size[i] > 1) - { - char regmask[FIRST_PSEUDO_REGISTER]; - int j; - - bzero (regmask, sizeof regmask); - /* Make a mask of all the regs that are spill regs in class I. */ - for (j = 0; j < n_spills; j++) - if (TEST_HARD_REG_BIT (reg_class_contents[i], spill_regs[j]) - && !counted_for_groups[spill_regs[j]]) - regmask[spill_regs[j]] = 1; - /* Find each consecutive group of them. */ - for (j = 0; j < FIRST_PSEUDO_REGISTER && max_groups[i] > 0; j++) - if (regmask[j] && j + group_size[i] <= FIRST_PSEUDO_REGISTER - /* Next line in case group-mode for this class - demands an even-odd pair. */ - && HARD_REGNO_MODE_OK (j, group_mode[i])) - { - int k; - for (k = 1; k < group_size[i]; k++) - if (! regmask[j + k]) - break; - if (k == group_size[i]) - { - /* We found a group. Mark it off against this class's - need for groups, and against each superclass too. */ - register enum reg_class *p; - max_groups[i]--; - p = reg_class_superclasses[i]; - while (*p != LIM_REG_CLASSES) - max_groups[(int) *p++]--; - /* Don't count these registers again. */ - counted_for_groups[j] = 1; - for (k = 1; k < group_size[i]; k++) - counted_for_groups[j + k] = 1; - } - j += k; - } - } - - /* Now count all remaining spill regs against the individual need. - Those that weren't counted_for_groups in groups can also count against - the not-in-group need. */ - - for (i = 0; i < n_spills; i++) - { - register enum reg_class *p; - class = (int) REGNO_REG_CLASS (spill_regs[i]); - - max_needs[class]--; - p = reg_class_superclasses[class]; - while (*p != LIM_REG_CLASSES) - max_needs[(int) *p++]--; - - if (! counted_for_groups[spill_regs[i]]) - { - if (max_nongroups[class] > 0) - counted_for_nongroups[spill_regs[i]] = 1; - max_nongroups[class]--; - p = reg_class_superclasses[class]; - while (*p != LIM_REG_CLASSES) - { - if (max_nongroups[(int) *p] > 0) - counted_for_nongroups[spill_regs[i]] = 1; - max_nongroups[(int) *p++]--; - } - } - } - - /* If all needs are met, we win. */ - - for (i = 0; i < N_REG_CLASSES; i++) - if (max_needs[i] > 0 || max_groups[i] > 0 || max_nongroups[i] > 0) - break; - if (i == N_REG_CLASSES && !new_basic_block_needs) - break; - - /* Not all needs are met; must spill more hard regs. */ - - /* If any element of basic_block_needs changed from 0 to 1, - re-spill all the regs already spilled. This may spill - additional pseudos that didn't spill before. */ - - if (new_basic_block_needs) - for (i = 0; i < n_spills; i++) - something_changed - |= spill_hard_reg (spill_regs[i], global, dumpfile); - - /* Now find more reload regs to satisfy the remaining need - Do it by ascending class number, since otherwise a reg - might be spilled for a big class and might fail to count - for a smaller class even though it belongs to that class. - - Count spilled regs in `spills', and add entries to - `spill_regs' and `spill_reg_order'. */ - - for (class = 0; class < N_REG_CLASSES; class++) - { - /* First get the groups of registers. - If we got single registers first, we might fragment - possible groups. */ - while (max_groups[class] > 0) - { - /* Groups of size 2 (the only groups used on most machines) - are treated specially. */ - if (group_size[class] == 2) - { - /* First, look for a register that will complete a group. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - int j = potential_reload_regs[i]; - int other; - if (j >= 0 && !fixed_regs[j] && j != FRAME_POINTER_REGNUM - && !regs_explicitly_used[j] - && - ((j > 0 && (other = j - 1, spill_reg_order[other] >= 0) - && TEST_HARD_REG_BIT (reg_class_contents[class], j) - && TEST_HARD_REG_BIT (reg_class_contents[class], other) - && HARD_REGNO_MODE_OK (other, group_mode[class]) - && ! counted_for_nongroups[other] - /* We don't want one part of another group. - We could get "two groups" that overlap! */ - && ! counted_for_groups[other]) - - || - (j < FIRST_PSEUDO_REGISTER - 1 - && (other = j + 1, spill_reg_order[other] >= 0) - && TEST_HARD_REG_BIT (reg_class_contents[class], j) - && TEST_HARD_REG_BIT (reg_class_contents[class], other) - && HARD_REGNO_MODE_OK (j, group_mode[class]) - && ! counted_for_nongroups[other] - && ! counted_for_groups[other]))) - { - register enum reg_class *p; - - /* We have found one that will complete a group, - so count off one group as provided. */ - max_groups[class]--; - p = reg_class_superclasses[class]; - while (*p != LIM_REG_CLASSES) - max_groups[(int) *p++]--; - - /* Indicate both these regs are part of a group. */ - counted_for_groups[j] = 1; - counted_for_groups[other] = 1; - - break; - } - } - /* We can't complete a group, so start one. */ - if (i == FIRST_PSEUDO_REGISTER) - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - int j = potential_reload_regs[i]; - if (j >= 0 && j + 1 < FIRST_PSEUDO_REGISTER - && !fixed_regs[j] && j != FRAME_POINTER_REGNUM - && spill_reg_order[j] < 0 && spill_reg_order[j + 1] < 0 - && TEST_HARD_REG_BIT (reg_class_contents[class], j) - && TEST_HARD_REG_BIT (reg_class_contents[class], j + 1) - && HARD_REGNO_MODE_OK (j, group_mode[class]) - && ! counted_for_nongroups[j + 1]) - break; - } - - /* I should be the index in potential_reload_regs - of the new reload reg we have found. */ - - something_changed - |= new_spill_reg (i, class, max_needs, 0, - global, dumpfile); - } - else - { - /* For groups of more than 2 registers, - look for a sufficient sequence of unspilled registers, - and spill them all at once. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - int j = potential_reload_regs[i]; - int k; - if (j >= 0 && j + 1 < FIRST_PSEUDO_REGISTER - && HARD_REGNO_MODE_OK (j, group_mode[class])) - { - /* Check each reg in the sequence. */ - for (k = 0; k < group_size[class]; k++) - if (! (spill_reg_order[j + k] < 0 - && !fixed_regs[j + k] - && j + k != FRAME_POINTER_REGNUM - && TEST_HARD_REG_BIT (reg_class_contents[class], j + k))) - break; - /* We got a full sequence, so spill them all. */ - if (k == group_size[class]) - { - register enum reg_class *p; - for (k = 0; k < group_size[class]; k++) - { - int idx; - counted_for_groups[j + k] = 1; - for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++) - if (potential_reload_regs[idx] == j + k) - break; - something_changed - |= new_spill_reg (idx, class, max_needs, 0, - global, dumpfile); - } - - /* We have found one that will complete a group, - so count off one group as provided. */ - max_groups[class]--; - p = reg_class_superclasses[class]; - while (*p != LIM_REG_CLASSES) - max_groups[(int) *p++]--; - - break; - } - } - } - } - } - - /* Now similarly satisfy all need for single registers. */ - - while (max_needs[class] > 0 || max_nongroups[class] > 0) - { - /* Consider the potential reload regs that aren't - yet in use as reload regs, in order of preference. - Find the most preferred one that's in this class. */ - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (potential_reload_regs[i] >= 0 - && TEST_HARD_REG_BIT (reg_class_contents[class], - potential_reload_regs[i])) - break; - - /* I should be the index in potential_reload_regs - of the new reload reg we have found. */ - - something_changed - |= new_spill_reg (i, class, max_needs, max_nongroups, - global, dumpfile); - } - } - } - - /* Insert code to save and restore call-clobbered hard regs - around calls. */ - - if (caller_save_needed) - save_call_clobbered_regs (); - - /* Now we know for certain whether we have a frame pointer. - If not, correct all references to go through the stack pointer. - This must be done before reloading, since reloading could generate - insns where sp+const cannot validly replace the frame pointer. - *This will lose if an insn might need more spill regs after - frame pointer elimination than it needed before.* */ - - if (! frame_pointer_needed) - eliminate_frame_pointer (first); - - /* Use the reload registers where necessary - by generating move instructions to move the must-be-register - values into or out of the reload registers. */ - - if (something_needs_reloads) - reload_as_needed (first, global); - - /* Now eliminate all pseudo regs by modifying them into - their equivalent memory references. - The REG-rtx's for the pseudos are modified in place, - so all insns that used to refer to them now refer to memory. - - For a reg that has a reg_equiv_address, all those insns - were changed by reloading so that no insns refer to it any longer; - but the DECL_RTL of a variable decl may refer to it, - and if so this causes the debugging info to mention the variable. */ - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - { - rtx addr = 0; - if (reg_equiv_mem[i]) - addr = XEXP (reg_equiv_mem[i], 0); - if (reg_equiv_address[i]) - addr = reg_equiv_address[i]; - if (addr) - { - if (! frame_pointer_needed) - FIX_FRAME_POINTER_ADDRESS (addr, 0); - if (reg_renumber[i] < 0) - { - rtx reg = regno_reg_rtx[i]; - XEXP (reg, 0) = addr; - REG_USERVAR_P (reg) = 0; - PUT_CODE (reg, MEM); - } - else if (reg_equiv_mem[i]) - XEXP (reg_equiv_mem[i], 0) = addr; - } - } -} - -/* 1 if two machine modes MODE0 and MODE1 are equivalent - as far as HARD_REGNO_MODE_OK is concerned - for registers in class CLASS. */ - -static int -modes_equiv_for_class_p (mode0, mode1, class) - enum machine_mode mode0, mode1; - enum reg_class class; -{ - register int regno; - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - { - /* If any reg in CLASS allows one mode but not the other, fail. - Or if the two modes have different sizes in that reg, fail. */ - if (TEST_HARD_REG_BIT (reg_class_contents[(int) class], regno) - && (HARD_REGNO_MODE_OK (regno, mode0) - != HARD_REGNO_MODE_OK (regno, mode1)) - && (HARD_REGNO_NREGS (regno, mode0) - != HARD_REGNO_NREGS (regno, mode1))) - return 0; - } - return 1; -} - -/* Add a new register to the tables of available spill-registers - (as well as spilling all pseudos allocated to the register). - I is the index of this register in potential_reload_regs. - CLASS is the regclass whose need is being satisfied. - MAX_NEEDS and MAX_NONGROUPS are the vectors of needs, - so that this register can count off against them. - MAX_NONGROUPS is 0 if this register is part of a group. - GLOBAL and DUMPFILE are the same as the args that `reload' got. */ - -static int -new_spill_reg (i, class, max_needs, max_nongroups, global, dumpfile) - int i; - int class; - int *max_needs; - int *max_nongroups; - int global; - FILE *dumpfile; -{ - register enum reg_class *p; - int val; - int regno = potential_reload_regs[i]; - - if (i >= FIRST_PSEUDO_REGISTER) - abort (); /* Caller failed to find any register. */ - - /* Make reg REGNO an additional reload reg. */ - - potential_reload_regs[i] = -1; - spill_regs[n_spills] = regno; - spill_reg_order[regno] = n_spills; - forbidden_regs[regno] = 1; - if (dumpfile) - fprintf (dumpfile, "Spilling reg %d.\n", spill_regs[n_spills]); - - /* Clear off the needs we just satisfied. */ - - max_needs[class]--; - p = reg_class_superclasses[class]; - while (*p != LIM_REG_CLASSES) - max_needs[(int) *p++]--; - - if (max_nongroups && max_nongroups[class] > 0) - { - counted_for_nongroups[regno] = 1; - max_nongroups[class]--; - p = reg_class_superclasses[class]; - while (*p != LIM_REG_CLASSES) - max_nongroups[(int) *p++]--; - } - - /* Spill every pseudo reg that was allocated to this reg - or to something that overlaps this reg. */ - - val = spill_hard_reg (spill_regs[n_spills], global, dumpfile); - - regs_ever_live[spill_regs[n_spills]] = 1; - n_spills++; - - return val; -} - -/* Scan all insns, computing the stack depth, and convert all - frame-pointer-relative references to stack-pointer-relative references. */ - -static void -eliminate_frame_pointer (first) - rtx first; -{ - int depth = 0; - int max_uid = get_max_uid (); - int *label_depth = (int *) alloca ((max_uid + 1) * sizeof (int)); - int i; - rtx insn; - - for (i = 0; i <= max_uid; i++) - label_depth[i] = -1; - - /* In this loop, for each forward branch we record the stack - depth of the label it jumps to. We take advantage of the fact - that the stack depth at a label reached by a backward branch - is always, in GCC output, equal to the stack depth of the preceding - unconditional jump, because it was either a loop statement or - statement label. */ - - for (insn = first; insn; insn = NEXT_INSN (insn)) - { - rtx pattern = PATTERN (insn); - switch (GET_CODE (insn)) - { - case INSN: - frame_pointer_address_altered = 0; - alter_frame_pointer_addresses (pattern, depth); - /* Rerecognize insn if changed. */ - if (frame_pointer_address_altered) - INSN_CODE (insn) = -1; - - /* Notice pushes and pops; update DEPTH. */ - if (GET_CODE (pattern) == SET) - { -#ifdef PUSH_ROUNDING - if (push_operand (SET_DEST (pattern), - GET_MODE (SET_DEST (pattern)))) - depth += PUSH_ROUNDING (GET_MODE_SIZE (GET_MODE (SET_DEST (pattern)))); -#endif - if (GET_CODE (SET_DEST (pattern)) == REG - && REGNO (SET_DEST (pattern)) == STACK_POINTER_REGNUM) - { - int delta; - if (GET_CODE (SET_SRC (pattern)) == PLUS - && GET_CODE (XEXP (SET_SRC (pattern), 0)) == REG - && REGNO (XEXP (SET_SRC (pattern), 0)) == STACK_POINTER_REGNUM) - delta = INTVAL (XEXP (SET_SRC (pattern), 1)); - else if (GET_CODE (SET_SRC (pattern)) == MINUS - && GET_CODE (XEXP (SET_SRC (pattern), 0)) == REG - && REGNO (XEXP (SET_SRC (pattern), 0)) == STACK_POINTER_REGNUM) - delta = -INTVAL (XEXP (SET_SRC (pattern), 1)); - else abort (); -#ifdef STACK_GROWS_DOWNWARD - depth -= delta; -#else - depth += delta; -#endif - } - } - break; - - case JUMP_INSN: - frame_pointer_address_altered = 0; - alter_frame_pointer_addresses (pattern, depth); - /* Rerecognize insn if changed. */ - if (frame_pointer_address_altered) - INSN_CODE (insn) = -1; - - if (GET_CODE (pattern) == ADDR_VEC) - for (i = 0; i < XVECLEN (pattern, 0); i++) - label_depth[INSN_UID (XEXP (XVECEXP (pattern, 0, i), 0))] = depth; - else if (GET_CODE (pattern) == ADDR_DIFF_VEC) - { - label_depth[INSN_UID (XEXP (XEXP (pattern, 0), 0))] = depth; - for (i = 0; i < XVECLEN (pattern, 1); i++) - label_depth[INSN_UID (XEXP (XVECEXP (pattern, 1, i), 0))] = depth; - } - else if (JUMP_LABEL (insn)) - label_depth[INSN_UID (JUMP_LABEL (insn))] = depth; - else - break; - - case CODE_LABEL: - if (label_depth [INSN_UID (insn)] >= 0) - depth = label_depth [INSN_UID (insn)]; - break; - - case CALL_INSN: - frame_pointer_address_altered = 0; - alter_frame_pointer_addresses (pattern, depth); - /* Rerecognize insn if changed. */ - if (frame_pointer_address_altered) - INSN_CODE (insn) = -1; - break; - } - } -} - -/* Walk the rtx X, converting all frame-pointer refs to stack-pointer refs - on the assumption that the current temporary stack depth is DEPTH. - (The size of saved registers must be added to DEPTH - to get the actual offset between the logical frame-pointer and the - stack pointer. FIX_FRAME_POINTER_ADDRESS takes care of that.) */ - -static rtx -alter_frame_pointer_addresses (x, depth) - register rtx x; - int depth; -{ - register int i; - register char *fmt; - register enum rtx_code code = GET_CODE (x); - - switch (code) - { - case CONST_INT: - case CONST: - case SYMBOL_REF: - case LABEL_REF: - case CONST_DOUBLE: - case CC0: - case PC: - return x; - - case REG: - /* Frame ptr can occur outside a PLUS if a stack slot - can occur with offset 0. */ - if (x == frame_pointer_rtx) - { - rtx oldx = x; - FIX_FRAME_POINTER_ADDRESS (x, depth); - if (x != oldx) - frame_pointer_address_altered = 1; - } - return x; - - case MEM: - { - rtx addr = XEXP (x, 0); - rtx mem; - rtx old_addr = addr; - FIX_FRAME_POINTER_ADDRESS (addr, depth); - if (addr != old_addr) - frame_pointer_address_altered = 1; - /* These MEMs are normally shared. Make a changed copy; - don't alter the shared MEM, since it needs to be altered - differently each time it occurs (since DEPTH varies). */ - mem = gen_rtx (MEM, GET_MODE (x), addr); - MEM_VOLATILE_P (mem) = MEM_VOLATILE_P (x); - return mem; - } - - case PLUS: - { - rtx oldx = x; - /* Handle addresses being loaded or pushed, etc., - rather than referenced. */ - FIX_FRAME_POINTER_ADDRESS (x, depth); - if (x != oldx) - frame_pointer_address_altered = 1; - code = GET_CODE (x); - break; - } - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - XEXP (x, i) = alter_frame_pointer_addresses (XEXP (x, i), depth); - else if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >=0; j--) - XVECEXP (x, i, j) - = alter_frame_pointer_addresses (XVECEXP (x, i, j), depth); - } - } - return x; -} - -/* Modify the home of pseudo-reg I. - The new home is present in reg_renumber[I]. - - FROM_REG may be the hard reg that the pseudo-reg is being spilled from; - or it may be -1, meaning there is none or it is not relevant. - This is used so that all pseudos spilled from a given hard reg - can share one stack slot. */ - -static void -alter_reg (i, from_reg) - register int i; - int from_reg; -{ - /* When outputting an inline function, this can happen - for a reg that isn't actually used. */ - if (regno_reg_rtx[i] == 0) - return; - - /* If the reg got changed to a MEM at rtl-generation time, - ignore it. */ - if (GET_CODE (regno_reg_rtx[i]) != REG) - return; - - /* Modify the reg-rtx to contain the new hard reg - number or else to contain its pseudo reg number. */ - REGNO (regno_reg_rtx[i]) - = reg_renumber[i] >= 0 ? reg_renumber[i] : i; - - if (reg_renumber[i] < 0 && reg_equiv_init[i]) - { - /* Delete the insn that loads the pseudo register. */ - PUT_CODE (reg_equiv_init[i], NOTE); - NOTE_LINE_NUMBER (reg_equiv_init[i]) - = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (reg_equiv_init[i]) = 0; - } - - /* If we have a pseudo that is needed but has no hard reg or equivalent, - allocate a stack slot for it. */ - - if (reg_renumber[i] < 0 - && reg_n_refs[i] > 0 - && reg_equiv_constant[i] == 0 - && reg_equiv_mem[i] == 0 - && reg_equiv_address[i] == 0) - { - register rtx x, addr; - int inherent_size = PSEUDO_REGNO_BYTES (i); - int total_size = max (inherent_size, reg_max_ref_width[i]); - - /* Each pseudo reg has an inherent size which comes from its own mode, - and a total size which provides room for paradoxical subregs - which refer to the pseudo reg in wider modes. - - We can use a slot already allocated if it provides both - enough inherent space and enough total space. - Otherwise, we allocate a new slot, making sure that it has no less - inherent space, and no less total space, then the previous slot. */ - if (from_reg == -1) - { - /* No known place to spill from => no slot to reuse. */ - x = assign_stack_local (GET_MODE (regno_reg_rtx[i]), total_size); -#ifdef BYTES_BIG_ENDIAN - /* Cancel the big-endian correction done in assign_stack_local. - Get the address of the beginning of the slot. - This is so we can do a big-endian correction unconditionally - below. */ - x = gen_rtx (MEM, GET_MODE (regno_reg_rtx[i]), - plus_constant (XEXP (x, 0), - inherent_size - total_size)); -#endif - } - /* Reuse a stack slot if possible. */ - else if (spill_stack_slot[from_reg] != 0 - && spill_stack_slot_width[from_reg] >= total_size - && (GET_MODE_SIZE (GET_MODE (spill_stack_slot[from_reg])) - >= inherent_size)) - x = spill_stack_slot[from_reg]; - /* Allocate a new or bigger slot. */ - else - { - /* Compute maximum size needed, both for inherent size - and for total size. */ - enum machine_mode mode = GET_MODE (regno_reg_rtx[i]); - if (spill_stack_slot[from_reg]) - { - if (GET_MODE_SIZE (GET_MODE (spill_stack_slot[from_reg])) - > inherent_size) - mode = GET_MODE (spill_stack_slot[from_reg]); - if (spill_stack_slot_width[from_reg] > total_size) - total_size = spill_stack_slot_width[from_reg]; - } - /* Make a slot with that size. */ - x = assign_stack_local (mode, total_size); -#ifdef BYTES_BIG_ENDIAN - /* Cancel the big-endian correction done in assign_stack_local. - Get the address of the beginning of the slot. - This is so we can do a big-endian correction unconditionally - below. */ - x = gen_rtx (MEM, mode, - plus_constant (XEXP (x, 0), - GET_MODE_SIZE (mode) - total_size)); -#endif - spill_stack_slot[from_reg] = x; - spill_stack_slot_width[from_reg] = total_size; - } - -#ifdef BYTES_BIG_ENDIAN - /* On a big endian machine, the "address" of the slot - is the address of the low part that fits its inherent mode. */ - if (inherent_size < total_size) - x = gen_rtx (MEM, GET_MODE (regno_reg_rtx[i]), - plus_constant (XEXP (x, 0), - total_size - inherent_size)); -#endif /* BYTES_BIG_ENDIAN */ - - addr = XEXP (x, 0); - - /* If the stack slot is directly addressable, substitute - the MEM we just got directly for the old REG. - Otherwise, record the address; we will generate hairy code - to compute the address in a register each time it is needed. */ - if (memory_address_p (GET_MODE (regno_reg_rtx[i]), addr)) - reg_equiv_mem[i] = x; - else - reg_equiv_address[i] = XEXP (x, 0); - } -} - -/* Mark the slots in regs_ever_live for the hard regs - used by pseudo-reg number REGNO. */ - -void -mark_home_live (regno) - int regno; -{ - register int i, lim; - i = reg_renumber[regno]; - if (i < 0) - return; - lim = i + HARD_REGNO_NREGS (i, PSEUDO_REGNO_MODE (regno)); - while (i < lim) - regs_ever_live[i++] = 1; -} - -/* Kick all pseudos out of hard register REGNO. - If GLOBAL is nonzero, try to find someplace else to put them. - If DUMPFILE is nonzero, log actions taken on that file. - - Return nonzero if any pseudos needed to be kicked out - or if this hard reg may appear explicitly in some instructions. */ - -static int -spill_hard_reg (regno, global, dumpfile) - register int regno; - int global; - FILE *dumpfile; -{ - int something_changed = 0; - register int i; - - /* Spill every pseudo reg that was allocated to this reg - or to something that overlaps this reg. */ - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (reg_renumber[i] >= 0 - && reg_renumber[i] <= regno - && (reg_renumber[i] - + HARD_REGNO_NREGS (reg_renumber[i], - PSEUDO_REGNO_MODE (i)) - > regno)) - { - /* If this register belongs solely to a basic block - which needed no spilling, leave it be. */ - if (regno != FRAME_POINTER_REGNUM - && basic_block_needs - && reg_basic_block[i] >= 0 - && basic_block_needs[reg_basic_block[i]] == 0) - continue; - - /* Mark it as no longer having a hard register home. */ - reg_renumber[i] = -1; - /* We will need to scan everything again. */ - something_changed = 1; - if (global) - { - retry_global_alloc (i, forbidden_regs); - /* Update regs_ever_live for new home (if any). */ - mark_home_live (i); - /* If something gets spilled to the stack, - we must have a frame pointer, so spill the frame pointer. */ - if (reg_renumber[i] == -1 && ! frame_pointer_needed) - { - frame_pointer_needed = 1; - forbidden_regs[FRAME_POINTER_REGNUM] = 1; - spill_hard_reg (FRAME_POINTER_REGNUM, global, dumpfile); - } - } - alter_reg (i, regno); - if (dumpfile) - { - if (reg_renumber[i] == -1) - fprintf (dumpfile, " Register %d now on stack.\n\n", i); - else - fprintf (dumpfile, " Register %d now in %d.\n\n", - i, reg_renumber[i]); - } - } - - return something_changed || regs_explicitly_used[regno]; -} - -/* Find all paradoxical subregs within X and update reg_max_ref_width. */ - -static rtx -scan_paradoxical_subregs (x) - register rtx x; -{ - register int i; - register char *fmt; - register enum rtx_code code = GET_CODE (x); - - switch (code) - { - case CONST_INT: - case CONST: - case SYMBOL_REF: - case LABEL_REF: - case CONST_DOUBLE: - case CC0: - case PC: - case REG: - case USE: - case CLOBBER: - return; - - case SUBREG: - if (GET_CODE (SUBREG_REG (x)) == REG - && GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) - reg_max_ref_width[REGNO (SUBREG_REG (x))] - = GET_MODE_SIZE (GET_MODE (x)); - return; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - scan_paradoxical_subregs (XEXP (x, i)); - else if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >=0; j--) - scan_paradoxical_subregs (XVECEXP (x, i, j)); - } - } -} - -struct hard_reg_n_uses { int regno; int uses; }; - -static int -hard_reg_use_compare (p1, p2) - struct hard_reg_n_uses *p1, *p2; -{ - int tem = p1->uses - p2->uses; - if (tem != 0) return tem; - /* If regs are equally good, sort by regno, - so that the results of qsort leave nothing to chance. */ - return p1->regno - p2->regno; -} - -/* Choose the order to consider regs for use as reload registers - based on how much trouble would be caused by spilling one. - Store them in order of decreasing preference in potential_reload_regs. */ - -static void -order_regs_for_reload () -{ - register int i; - register int o = 0; - int large = 0; - - struct hard_reg_n_uses hard_reg_n_uses[FIRST_PSEUDO_REGISTER]; - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - potential_reload_regs[i] = -1; - - /* Count number of uses of each hard reg by pseudo regs allocated to it - and then order them by decreasing use. */ - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - hard_reg_n_uses[i].uses = 0; - hard_reg_n_uses[i].regno = i; - } - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - { - int regno = reg_renumber[i]; - if (regno >= 0) - { - int lim = regno + HARD_REGNO_NREGS (regno, PSEUDO_REGNO_MODE (i)); - while (regno < lim) - hard_reg_n_uses[regno++].uses += reg_n_refs[i]; - } - large += reg_n_refs[i]; - } - - /* Now fixed registers (which cannot safely be used for reloading) - get a very high use count so they will be considered least desirable. - Registers used explicitly in the rtl code are almost as bad. */ - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - if (fixed_regs[i]) - hard_reg_n_uses[i].uses += large + 2; - else if (regs_explicitly_used[i]) - hard_reg_n_uses[i].uses += large + 1; - } - hard_reg_n_uses[FRAME_POINTER_REGNUM].uses += large + 2; - - qsort (hard_reg_n_uses, FIRST_PSEUDO_REGISTER, - sizeof hard_reg_n_uses[0], hard_reg_use_compare); - - /* Prefer registers not so far used, for use in temporary loading. - Among them, prefer registers not preserved by calls. */ - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { -#ifdef REG_ALLOC_ORDER - int regno = reg_alloc_order[i]; -#else - int regno = i; -#endif - if (regs_ever_live[regno] == 0 && call_used_regs[regno] - && ! fixed_regs[regno]) - potential_reload_regs[o++] = regno; - } - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { -#ifdef REG_ALLOC_ORDER - int regno = reg_alloc_order[i]; -#else - int regno = i; -#endif - if (regs_ever_live[regno] == 0 && ! call_used_regs[regno] - && regno != FRAME_POINTER_REGNUM) - potential_reload_regs[o++] = regno; - } - - /* Now add the regs that are already used, - preferring those used less often. */ - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (regs_ever_live[hard_reg_n_uses[i].regno] != 0) - potential_reload_regs[o++] = hard_reg_n_uses[i].regno; - -#if 0 - /* For regs that are used, don't prefer those not preserved by calls - because those are likely to contain high priority things - that are live for short periods of time. */ - - for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--) - if (regs_ever_live[i] != 0 && ! call_used_regs[i]) - potential_reload_regs[o++] = i; -#endif -} - -/* Reload pseudo-registers into hard regs around each insn as needed. - Additional register load insns are output before the insn that needs it - and perhaps store insns after insns that modify the reloaded pseudo reg. - - reg_last_reload_reg and reg_reloaded_contents keep track of - which pseudo-registers are already available in reload registers. - We update these for the reloads that we perform, - as the insns are scanned. */ - -static void -reload_as_needed (first, live_known) - rtx first; - int live_known; -{ - register rtx insn; - register int i; - int this_block = 0; - rtx x; - rtx after_call = 0; - - bzero (spill_reg_rtx, sizeof spill_reg_rtx); - reg_last_reload_reg = (rtx *) alloca (max_regno * sizeof (rtx)); - bzero (reg_last_reload_reg, max_regno * sizeof (rtx)); - reg_has_output_reload = (char *) alloca (max_regno); - for (i = 0; i < n_spills; i++) - { - reg_reloaded_contents[i] = -1; - reg_reloaded_insn[i] = 0; - } - - for (insn = first; insn;) - { - register rtx next = NEXT_INSN (insn); - - /* Notice when we move to a new basic block. */ - if (basic_block_needs && this_block + 1 < n_basic_blocks - && insn == basic_block_head[this_block+1]) - ++this_block; - - if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN - || GET_CODE (insn) == CALL_INSN) - { - rtx avoid_return_reg = 0; - - /* If insn has no reloads, we want these to be zero, down below. */ - bzero (reg_has_output_reload, max_regno); - bzero (reg_is_output_reload, FIRST_PSEUDO_REGISTER); - - /* Set avoid_return_reg if this is an insn - that might use the value of a function call. */ - if (GET_CODE (insn) == CALL_INSN) - { - if (GET_CODE (PATTERN (insn)) == SET) - after_call = SET_DEST (PATTERN (insn)); - else if (GET_CODE (PATTERN (insn)) == PARALLEL - && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) - after_call = SET_DEST (XVECEXP (PATTERN (insn), 0, 0)); - else - after_call = 0; - } - else if (after_call != 0 - && !(GET_CODE (PATTERN (insn)) == SET - && SET_DEST (PATTERN (insn)) == stack_pointer_rtx)) - { - if (reg_mentioned_p (after_call, PATTERN (insn))) - avoid_return_reg = after_call; - after_call = 0; - } - - if (GET_MODE (insn) == VOIDmode) - n_reloads = 0; - /* First find the pseudo regs that must be reloaded for this insn. - This info is returned in the tables reload_... (see reload.h). - Also modify the body of INSN by substituting RELOAD - rtx's for those pseudo regs. */ - else - find_reloads (insn, 1, spill_indirect_ok, live_known, spill_reg_order); - - if (n_reloads > 0) - { - /* If this block has not had spilling done, - deactivate any optional reloads lest they - try to use a spill-reg which isn't available here. - If we have any non-optionals that need a spill reg, abort. */ - if (basic_block_needs != 0 - && basic_block_needs[this_block] == 0) - { - for (i = 0; i < n_reloads; i++) - { - if (reload_optional[i]) - reload_in[i] = reload_out[i] = 0; - else if (reload_reg_rtx[i] == 0) - abort (); - } - } - - /* Now compute which reload regs to reload them into. Perhaps - reusing reload regs from previous insns, or else output - load insns to reload them. Maybe output store insns too. - Record the choices of reload reg in reload_reg_rtx. */ - choose_reload_regs (insn, avoid_return_reg); - - /* Generate the insns to reload operands into or out of - their reload regs. */ - emit_reload_insns (insn); - - /* Substitute the chosen reload regs from reload_reg_rtx - into the insn's body (or perhaps into the bodies of other - load and store insn that we just made for reloading - and that we moved the structure into). */ - subst_reloads (); - } - /* Any previously reloaded spilled pseudo reg, stored in this insn, - is no longer validly lying around to save a future reload. - Note that this does not detect pseudos that were reloaded - for this insn in order to be stored in - (obeying register constraints). That is correct; such reload - registers ARE still valid. */ - note_stores (PATTERN (insn), forget_old_reloads_1); - - /* Likewise for regs altered by auto-increment in this insn. - But note that the reg-notes are not changed by reloading: - they still contain the pseudo-regs, not the spill regs. */ - for (x = REG_NOTES (insn); x; x = XEXP (x, 1)) - if (REG_NOTE_KIND (x) == REG_INC) - { - /* See if this pseudo reg was reloaded in this insn. - If so, its last-reload info is still valid - because it is based on this insn's reload. */ - for (i = 0; i < n_reloads; i++) - if (reload_out[i] == XEXP (x, 0)) - break; - - if (i != n_reloads) - forget_old_reloads_1 (XEXP (x, 0)); - } - } - /* A reload reg's contents are unknown after a label. */ - if (GET_CODE (insn) == CODE_LABEL) - for (i = 0; i < n_spills; i++) - { - reg_reloaded_contents[i] = -1; - reg_reloaded_insn[i] = 0; - } - - /* Don't assume a reload reg is still good after a call insn - if it is a call-used reg. */ - if (GET_CODE (insn) == CODE_LABEL || GET_CODE (insn) == CALL_INSN) - for (i = 0; i < n_spills; i++) - if (call_used_regs[spill_regs[i]]) - { - reg_reloaded_contents[i] = -1; - reg_reloaded_insn[i] = 0; - } - - /* In case registers overlap, allow certain insns to invalidate - particular hard registers. */ - -#ifdef INSN_CLOBBERS_REGNO_P - for (i = 0 ; i < n_spills ; i++) - if (INSN_CLOBBERS_REGNO_P (insn, spill_regs[i])) - { - reg_reloaded_contents[i] = -1; - reg_reloaded_insn[i] = 0; - } -#endif - - insn = next; - -#ifdef USE_C_ALLOCA - alloca (0); -#endif - } -} - -/* Discard all record of any value reloaded from X, - or reloaded in X from someplace else; - unless X is an output reload reg of the current insn. - - X may be a hard reg (the reload reg) - or it may be a pseudo reg that was reloaded from. - - This function is not called for instructions generated by reload. */ - -static void -forget_old_reloads_1 (x) - rtx x; -{ - register int regno; - int nr; - - if (GET_CODE (x) != REG) - return; - - regno = REGNO (x); - - if (regno >= FIRST_PSEUDO_REGISTER) - nr = 1; - else - { - int i; - nr = HARD_REGNO_NREGS (regno, GET_MODE (x)); - /* Storing into a spilled-reg invalidates its contents. - This can happen if a block-local pseudo is allocated to that reg - and it wasn't spilled because this block's total need is 0. - Then some insn might have an optional reload and use this reg. */ - for (i = 0; i < nr; i++) - if (spill_reg_order[regno + i] >= 0 - /* But don't do this if the reg actually serves as an output - reload reg in the current instruction. */ - && reg_is_output_reload[regno + i] == 0) - { - reg_reloaded_contents[spill_reg_order[regno + i]] = -1; - reg_reloaded_insn[spill_reg_order[regno + i]] = 0; - } - } - - /* Since value of X has changed, - forget any value previously copied from it. */ - - while (nr-- > 0) - /* But don't forget a copy if this is the output reload - that establishes the copy's validity. */ - if (reg_has_output_reload[regno + nr] == 0) - reg_last_reload_reg[regno + nr] = 0; -} - -/* Comparison function for qsort to decide which of two reloads - should be handled first. *P1 and *P2 are the reload numbers. */ - -static int -reload_reg_class_lower (p1, p2) - short *p1, *p2; -{ - register int r1 = *p1, r2 = *p2; - register int t; - register enum machine_mode mode1, mode2; - - /* Consider required reloads before optional ones. */ - t = reload_optional[r1] - reload_optional[r2]; - if (t != 0) - return t; - /* Consider all multi-reg groups first. - This is safe because `reload' fills all group-need before - filling all non-group need. */ - mode1 = (reload_inmode[r1] == VOIDmode ? reload_outmode[r1] : reload_inmode[r1]); - mode2 = (reload_inmode[r2] == VOIDmode ? reload_outmode[r2] : reload_inmode[r2]); - t = (CLASS_MAX_NREGS (reload_reg_class[r2], mode2) - - CLASS_MAX_NREGS (reload_reg_class[r1], mode1)); - if (t != 0) - return t; - /* Consider reloads in order of increasing reg-class number. */ - t = (int) reload_reg_class[r1] - (int) reload_reg_class[r2]; - if (t != 0) return t; - /* If reloads are equally urgent, sort by reload number, - so that the results of qsort leave nothing to chance. */ - return r1 - r2; -} - -/* The following tables are indexed by register number, - not by spill_regs index. */ - -/* 1 if reg is in use as a reload reg for a RELOAD_OTHER reload. */ -static char reload_reg_in_use[FIRST_PSEUDO_REGISTER]; -/* 1 if reg is in use for a RELOAD_FOR_INPUT_RELOAD_ADDRESS reload. */ -static char reload_reg_in_use_for_inputs[FIRST_PSEUDO_REGISTER]; -/* 1 if reg is in use for a RELOAD_FOR_OUTPUT_RELOAD_ADDRESS reload. */ -static char reload_reg_in_use_for_outputs[FIRST_PSEUDO_REGISTER]; -/* 1 if reg is in use for a RELOAD_FOR_OPERAND_ADDRESS reload. */ -static char reload_reg_in_use_for_operands[FIRST_PSEUDO_REGISTER]; - -/* 1 if reg is in use as a reload reg for any sort of reload. */ -static char reload_reg_in_use_at_all[FIRST_PSEUDO_REGISTER]; - -/* Mark reg REGNO as in use for a reload of the sort spec'd by WHEN_NEEDED. */ - -static void -mark_reload_reg_in_use (regno, when_needed) - int regno; - enum reload_when_needed when_needed; -{ - switch (when_needed) - { - case RELOAD_OTHER: - reload_reg_in_use[regno] = 1; - break; - - case RELOAD_FOR_INPUT_RELOAD_ADDRESS: - reload_reg_in_use_for_inputs[regno] = 1; - break; - - case RELOAD_FOR_OUTPUT_RELOAD_ADDRESS: - reload_reg_in_use_for_outputs[regno] = 1; - break; - - case RELOAD_FOR_OPERAND_ADDRESS: - reload_reg_in_use_for_operands[regno] = 1; - break; - } - reload_reg_in_use_at_all[regno] = 1; -} - -/* 1 if reg REGNO is free as a reload reg for a reload of the sort - specified by WHEN_NEEDED. */ - -static int -reload_reg_free_p (regno, when_needed) - int regno; - enum reload_when_needed when_needed; -{ - /* In use for a RELOAD_OTHER means it's not available for anything. */ - if (reload_reg_in_use[regno]) - return 0; - switch (when_needed) - { - case RELOAD_OTHER: - /* In use for anything means not available for a RELOAD_OTHER. */ - return ! reload_reg_in_use_at_all[regno]; - - /* The other three kinds of use can share a register. */ - case RELOAD_FOR_INPUT_RELOAD_ADDRESS: - return ! reload_reg_in_use_for_inputs[regno]; - case RELOAD_FOR_OUTPUT_RELOAD_ADDRESS: - return ! reload_reg_in_use_for_outputs[regno]; - case RELOAD_FOR_OPERAND_ADDRESS: - return ! reload_reg_in_use_for_operands[regno]; - } -} - -/* Return 1 if the value in reload reg REGNO, as used by a reload - needed for the part of the insn specified by WHEN_NEEDED, - is not in use for a reload in any prior part of the insn. */ - -static int -reload_reg_free_before_p (regno, when_needed) - int regno; - enum reload_when_needed when_needed; -{ - switch (when_needed) - { - case RELOAD_OTHER: - /* Since a RELOAD_OTHER reload claims the reg for the entire insn, - its use starts from the beginning, so nothing can use it earlier. */ - return 1; - - /* If this use is for part of the insn, - check the reg is not in use for any prior part. */ - case RELOAD_FOR_OUTPUT_RELOAD_ADDRESS: - if (reload_reg_in_use_for_operands[regno]) - return 0; - case RELOAD_FOR_OPERAND_ADDRESS: - if (reload_reg_in_use_for_inputs[regno]) - return 0; - case RELOAD_FOR_INPUT_RELOAD_ADDRESS: - return 1; - } -} - -/* Return 1 if the value in reload reg REGNO, as used by a reload - needed for the part of the insn specified by WHEN_NEEDED, - is still available in REGNO at the end of the insn. */ - -static int -reload_reg_reaches_end_p (regno, when_needed) - int regno; - enum reload_when_needed when_needed; -{ - switch (when_needed) - { - case RELOAD_OTHER: - /* Since a RELOAD_OTHER reload claims the reg for the entire insn, - its value must reach the end. */ - return 1; - - /* If this use is for part of the insn, - its value reaches if no subsequent part uses the same register. */ - case RELOAD_FOR_INPUT_RELOAD_ADDRESS: - if (reload_reg_in_use_for_operands[regno]) - return 0; - case RELOAD_FOR_OPERAND_ADDRESS: - if (reload_reg_in_use_for_outputs[regno]) - return 0; - case RELOAD_FOR_OUTPUT_RELOAD_ADDRESS: - return 1; - } -} - -/* Vector of reload-numbers showing the order in which the reloads should - be processed. */ -short reload_order[MAX_RELOADS]; - -/* Indexed by reload number, 1 if incoming value - inherited from previous insns. */ -char reload_inherited[MAX_RELOADS]; - -/* For an inherited reload, this is the insn the reload was inherited from, - if we know it. Otherwise, this is 0. */ -rtx reload_inheritance_insn[MAX_RELOADS]; - -/* If non-zero, this is a place to get the value of the reload, - rather than using reload_in. */ -rtx reload_override_in[MAX_RELOADS]; - -/* For each reload, the index in spill_regs of the spill register used, - or -1 if we did not need one of the spill registers for this reload. */ -int reload_spill_index[MAX_RELOADS]; - -/* Assign hard reg targets for the pseudo-registers we must reload - into hard regs for this insn. - Also output the instructions to copy them in and out of the hard regs. - - For machines with register classes, we are responsible for - finding a reload reg in the proper class. */ - -static void -choose_reload_regs (insn, avoid_return_reg) - rtx insn; - rtx avoid_return_reg; -{ - register int j; - int have_groups = 0; - /* Non-zero means we must reuse spill regs for multiple reloads in this insn - or we will not have enough spill regs. */ - int must_reuse = 0; - rtx original_reload_reg_rtx[MAX_RELOADS]; - - /* Don't bother with avoiding the return reg - if we have no mandatory reload that could use it. */ - if (avoid_return_reg) - { - int do_avoid = 0; - int regno = REGNO (avoid_return_reg); - int nregs - = HARD_REGNO_NREGS (regno, GET_MODE (avoid_return_reg)); - int r; - - for (r = regno; r < regno + nregs; r++) - if (spill_reg_order[r] >= 0) - for (j = 0; j < n_reloads; j++) - if (!reload_optional[j] && reload_reg_rtx[j] == 0 - && - TEST_HARD_REG_BIT (reg_class_contents[(int) reload_reg_class[j]], r)) - do_avoid = 1; - if (!do_avoid) - avoid_return_reg = 0; - } - - /* See if we have more mandatory reloads than spill regs. - If so, then we cannot risk optimizations that could prevent - reloads from sharing one spill register. */ - - { - int tem = (avoid_return_reg != 0); - for (j = 0; j < n_reloads; j++) - if (! reload_optional[j] && reload_reg_rtx[j] == 0) - tem++; - if (tem > n_spills) - must_reuse = 1; - } - - bcopy (reload_reg_rtx, original_reload_reg_rtx, sizeof (reload_reg_rtx)); - - /* If we fail to get enough regs without must_reuse, - set must_reuse and jump back here. */ - retry: - bzero (reload_inherited, MAX_RELOADS); - bzero (reload_inheritance_insn, MAX_RELOADS * sizeof (rtx)); - bzero (reload_override_in, MAX_RELOADS * sizeof (rtx)); - bzero (reload_reg_in_use, FIRST_PSEUDO_REGISTER); - bzero (reload_reg_in_use_at_all, FIRST_PSEUDO_REGISTER); - bzero (reload_reg_in_use_for_inputs, FIRST_PSEUDO_REGISTER); - bzero (reload_reg_in_use_for_outputs, FIRST_PSEUDO_REGISTER); - bzero (reload_reg_in_use_for_operands, FIRST_PSEUDO_REGISTER); - - /* Don't use the subroutine call return reg for a reload - if we are supposed to avoid it. */ - if (avoid_return_reg) - { - int regno = REGNO (avoid_return_reg); - int nregs - = HARD_REGNO_NREGS (regno, GET_MODE (avoid_return_reg)); - int r; - - for (r = regno; r < regno + nregs; r++) - if (spill_reg_order[r] >= 0) - reload_reg_in_use[r] = 1; - } - - /* In order to be certain of getting the registers we need, - we must sort the reloads into order of increasing register class. - Then our grabbing of reload registers will parallel the process - that provided the reload registers. */ - - /* Also note whether any of the reloads wants a consecutive group of regs. - When that happens, we must when processing the non-group reloads - avoid (when possible) using a reload reg that would break up a group. */ - - /* This used to look for an existing reloaded home for all - of the reloads, and only then perform any new reloads. - But that could lose if the reloads were done out of reg-class order - because a later reload with a looser constraint might have an old - home in a register needed by an earlier reload with a tighter constraint. - It would be possible with even hairier code to detect such cases - and handle them, but it doesn't seem worth while yet. */ - - for (j = 0; j < n_reloads; j++) - { - enum machine_mode mode; - reload_order[j] = j; - reload_spill_index[j] = -1; - mode = (reload_inmode[j] == VOIDmode - || GET_MODE_SIZE (reload_outmode[j]) > GET_MODE_SIZE (reload_inmode[j]) - ? reload_outmode[j] : reload_inmode[j]); - - if (CLASS_MAX_NREGS (reload_reg_class[j], mode) > 1) - have_groups = 1; - /* If we have already decided to use a certain register, - don't use it in another way. */ - if (reload_reg_rtx[j]) - mark_reload_reg_in_use (REGNO (reload_reg_rtx[j]), reload_when_needed[j]); - } - - if (n_reloads > 1) - qsort (reload_order, n_reloads, sizeof (short), reload_reg_class_lower); - - for (j = 0; j < n_reloads; j++) - { - register int r = reload_order[j]; - register int i; - register rtx new; - enum machine_mode reload_mode = reload_inmode[r]; - int h1_ok, h2_ok, h3_ok; - - /* Ignore reloads that got marked inoperative. */ - if (reload_out[r] == 0 && reload_in[r] == 0) - continue; - - if (GET_MODE_SIZE (reload_outmode[r]) > GET_MODE_SIZE (reload_mode)) - reload_mode = reload_outmode[r]; - if (reload_strict_low[r]) - reload_mode = GET_MODE (SUBREG_REG (reload_out[r])); - - /* No need to find a reload-register if find_reloads chose one. */ - - if (reload_reg_rtx[r] != 0) - continue; - - /* First see if this pseudo is already available as reloaded - for a previous insn. - This feature is disabled for multi-register groups - because we haven't yet any way to tell whether the entire - value is properly preserved. - It is also disabled when there are other reloads for mult-register - groups, lest the inherited reload reg break up a needed group. */ - - { - register int regno = -1; - - if (reload_in[r] == 0) - ; - else if (GET_CODE (reload_in[r]) == REG) - regno = REGNO (reload_in[r]); - else if (GET_CODE (reload_in_reg[r]) == REG) - regno = REGNO (reload_in_reg[r]); -#if 0 - /* This won't work, since REGNO can be a pseudo reg number. - Also, it takes much more hair to keep track of all the things - that can invalidate an inherited reload of part of a pseudoreg. */ - else if (GET_CODE (reload_in[r]) == SUBREG - && GET_CODE (SUBREG_REG (reload_in[r])) == REG) - regno = REGNO (SUBREG_REG (reload_in[r])) + SUBREG_WORD (reload_in[r]); -#endif - - if (regno >= 0 - && reg_last_reload_reg[regno] != 0 - && ! have_groups - /* See comment at next use of must_reuse. */ - && ! must_reuse) - { - i = spill_reg_order[REGNO (reg_last_reload_reg[regno])]; - - if (reg_reloaded_contents[i] == regno - && HARD_REGNO_MODE_OK (spill_regs[i], reload_mode) - && TEST_HARD_REG_BIT (reg_class_contents[(int) reload_reg_class[r]], - spill_regs[i]) - && reload_reg_free_p (spill_regs[i], reload_when_needed[r]) - && reload_reg_free_before_p (spill_regs[i], - reload_when_needed[r])) - { - /* Mark the register as in use for this part of the insn. */ - mark_reload_reg_in_use (spill_regs[i], reload_when_needed[r]); - reload_reg_rtx[r] = reg_last_reload_reg[regno]; - reload_inherited[r] = 1; - reload_inheritance_insn[r] = reg_reloaded_insn[i]; - reload_spill_index[r] = i; - } - } - } - - /* Here's another way to see if the value is already lying around. */ - if (reload_in[r] != 0 - && reload_reg_rtx[r] == 0 - && reload_out[r] == 0 - && (CONSTANT_P (reload_in[r]) - || GET_CODE (reload_in[r]) == PLUS - || GET_CODE (reload_in[r]) == REG - || GET_CODE (reload_in[r]) == MEM) - && ! have_groups - /* This optimization can prevent this reload from reusing - a spill reg used for another reload. That could take away - a spill reg that another reload will need. If we cannot - be sure there will still be enough spill regs, - don't do this optimization. */ - && ! must_reuse) - { - register rtx equiv - = find_equiv_reg (reload_in[r], insn, reload_reg_class[r], - -1, 0, 0, reload_mode); - int regno; - - if (equiv != 0) - regno = REGNO (equiv); - - /* If we found a spill reg, reject it unless it is free - and of the desired class. */ - if (equiv != 0 && GET_CODE (equiv) == REG - && spill_reg_order[regno] >= 0 - && reload_reg_free_before_p (regno, reload_when_needed[r])) - { - if (! TEST_HARD_REG_BIT (reg_class_contents[(int) reload_reg_class[r]], - regno)) - equiv = 0; - } - - if (equiv != 0 && reload_reg_in_use_at_all[regno]) - equiv = 0; - - if (equiv != 0 && ! HARD_REGNO_MODE_OK (regno, reload_mode)) - equiv = 0; - - /* We found a register that contains the value we need. - If this register is the same as an `earlyclobber' operand - of the current insn, just mark it as a place to reload from - since we can't use it as the reload register itself. */ - - if (equiv != 0) - for (i = 0; i < n_earlyclobbers; i++) - if (reg_overlap_mentioned_p (equiv, reload_earlyclobbers[i])) - { - reload_override_in[r] = equiv; - equiv = 0; - break; - } - - /* If we found an equivalent reg, say no code need be generated - to load it, and use it as our reload reg. */ - if (equiv != 0 - && REGNO (equiv) != FRAME_POINTER_REGNUM) - { - reload_reg_rtx[r] = equiv; - reload_inherited[r] = 1; - /* If it is a spill reg, - mark the spill reg as in use for this insn. */ - i = spill_reg_order[REGNO (equiv)]; - if (i >= 0) - { - int nr = HARD_REGNO_NREGS (spill_regs[i], reload_mode); - while (nr > 0) - mark_reload_reg_in_use (REGNO (equiv) + --nr, - reload_when_needed[r]); - } - } - } - - /* If it isn't lying around, and isn't optional, - find a place to reload it into. */ - if (reload_reg_rtx[r] != 0 || reload_optional[r] != 0) - continue; - - /* Value not lying around; find a register to reload it into. - Here I is not a regno, it is an index into spill_regs. */ - i = n_spills; - -#if 0 - /* The following is no longer needed now that all multi-register - (group) reloads are processed before all solitary register reloads - (due to changes in `reg_class_lower_p' and `reload'. */ - /* The following also fails to test HARD_REGNO_MODE_OK appropriately, - which was hard to fix because we don't know the mode that the - group might have that would want this register. */ - - /* If we want just one reg, and other reloads want groups, - first try to find a reg that can't be part of a group. */ - if (have_groups - && CLASS_MAX_NREGS (reload_reg_class[r], reload_mode) == 1) - for (i = 0; i < n_spills; i++) - { - int regno = spill_regs[i]; - int class = (int) reload_reg_class[r]; - if (reload_reg_in_use_at_all[regno] == 0 - && TEST_HARD_REG_BIT (reg_class_contents[class], - regno) - && !(regno + 1 < FIRST_PSEUDO_REGISTER - && spill_reg_order[regno + 1] >= 0 - && reload_reg_in_use_at_all[regno + 1] == 0 - && TEST_HARD_REG_BIT (reg_class_contents[class], - regno + 1)) - && !(regno > 0 - && spill_reg_order[regno - 1] >= 0 - && reload_reg_in_use_at_all[regno - 1] == 0 - && TEST_HARD_REG_BIT (reg_class_contents[class], - regno - 1))) - break; - } - - /* If that didn't work, try to find a register that has only one - neighbor that could make a group with it. That way, if the - available registers are three consecutive ones, we avoid taking - the middle one (which would leave us with no possible groups). */ - - if (have_groups - && CLASS_MAX_NREGS (reload_reg_class[r], reload_mode) == 1 - && i == n_spills) - for (i = 0; i < n_spills; i++) - { - int regno = spill_regs[i]; - int class = (int) reload_reg_class[r]; - if (reload_reg_in_use_at_all[regno] == 0 - && TEST_HARD_REG_BIT (reg_class_contents[class], - regno) - && (!(regno + 1 < FIRST_PSEUDO_REGISTER - && spill_reg_order[regno + 1] >= 0 - && reload_reg_in_use_at_all[regno + 1] == 0 - && TEST_HARD_REG_BIT (reg_class_contents[class], - regno + 1)) - || !(regno > 0 - && spill_reg_order[regno - 1] >= 0 - && reload_reg_in_use_at_all[regno - 1] == 0 - && TEST_HARD_REG_BIT (reg_class_contents[class], - regno - 1)))) - break; - } -#endif - - /* Now, if we want a single register and haven't yet found one, - take any reg in the right class and not in use. - If we want a consecutive group, here is where we look for it. */ - if (i == n_spills) - { - int pass; - /* If we put this reload ahead, thinking it is a group, - then insist on finding a group. Otherwise we can grab a - reg that some other reload needs. - (That can happen when we have a 68000 DATA_OR_FP_REG - which is a group of data regs or one fp reg.) - ??? Really it would be nicer to have smarter handling - for that kind of reg class, where a problem like this is normal. - Perhaps those classes should be avoided for reloading - by use of more alternatives. */ - int force_group - = (CLASS_MAX_NREGS (reload_reg_class[r], reload_mode) > 1); - /* We need not be so restrictive if there are no more reloads - for this insn. */ - if (j + 1 == n_reloads) - force_group = 0; - - for (pass = 0; pass < 2; pass++) - { - for (i = 0; i < n_spills; i++) - { - int class = (int) reload_reg_class[r]; - if (reload_reg_free_p (spill_regs[i], reload_when_needed[r]) - && TEST_HARD_REG_BIT (reg_class_contents[class], - spill_regs[i]) - /* Look first for regs to share, then for unshared. */ - && (pass || reload_reg_in_use_at_all[spill_regs[i]])) - { - int nr = HARD_REGNO_NREGS (spill_regs[i], reload_mode); - /* Avoid the problem where spilling a GENERAL_OR_FP_REG - (on 68000) got us two FP regs. If NR is 1, - we would reject both of them. */ - if (force_group) - nr = CLASS_MAX_NREGS (reload_reg_class[r], reload_mode); - /* If we need only one reg, we have already won. */ - if (nr == 1) - { - /* But reject a single reg if we demand a group. */ - if (force_group) - continue; - break; - } - /* Otherwise check that as many consecutive regs as we need - are available here. - Also, don't use for a group registers that are - needed for nongroups. */ - if (HARD_REGNO_MODE_OK (spill_regs[i], reload_mode) - && ! counted_for_nongroups[spill_regs[i]]) - while (nr > 1) - { - int regno = spill_regs[i] + nr - 1; - if (!(TEST_HARD_REG_BIT (reg_class_contents[class], - regno) - && spill_reg_order[regno] >= 0 - && reload_reg_free_p (regno, reload_when_needed[r]) - && ! counted_for_nongroups[regno])) - break; - nr--; - } - if (nr == 1) - break; - } - } - /* If find something on pass 1, omit pass 2. */ - if (i < n_spills) - break; - } - } - - /* We should have found a spill register by now. */ - if (i == n_spills) - { - if (must_reuse) - abort (); - bcopy (original_reload_reg_rtx, reload_reg_rtx, sizeof (reload_reg_rtx)); - must_reuse = 1; - goto retry; - } - - /* Mark as in use for this insn the reload regs we use for this. */ - { - int nr = HARD_REGNO_NREGS (spill_regs[i], reload_mode); - while (nr > 0) - { - mark_reload_reg_in_use (spill_regs[i] + --nr, - reload_when_needed[r]); - reg_reloaded_contents[spill_reg_order[spill_regs[i] + nr]] = -1; - reg_reloaded_insn[spill_reg_order[spill_regs[i] + nr]] = 0; - } - } - - new = spill_reg_rtx[i]; - - if (new == 0 || GET_MODE (new) != reload_mode) - spill_reg_rtx[i] = new = gen_rtx (REG, reload_mode, spill_regs[i]); - - reload_reg_rtx[r] = new; - reload_spill_index[r] = i; - - /* Detect when the reload reg can't hold the reload mode. - This used to be one `if', but Sequent compiler can't handle that. */ - if (HARD_REGNO_MODE_OK (REGNO (reload_reg_rtx[r]), reload_mode)) - if (! (reload_in[r] != 0 - && ! HARD_REGNO_MODE_OK (REGNO (reload_reg_rtx[r]), - GET_MODE (reload_in[r])))) - if (! (reload_out[r] != 0 - && ! HARD_REGNO_MODE_OK (REGNO (reload_reg_rtx[r]), - GET_MODE (reload_out[r])))) - /* The reg is OK. */ - continue; - - /* The reg is not OK. */ - { - if (asm_noperands (PATTERN (insn)) < 0) - /* It's the compiler's fault. */ - abort (); - /* It's the user's fault; the operand's mode and constraint - don't match. Disable this reload so we don't crash in final. */ - error_for_asm (insn, - "`asm' operand constraint incompatible with operand size"); - reload_in[r] = 0; - reload_out[r] = 0; - reload_reg_rtx[r] = 0; - reload_optional[r] = 1; - } - } - - /* If we thought we could inherit a reload, because it seemed that - nothing else wanted the same reload register earlier in the insn, - verify that assumption, now that all reloads have been assigned. */ - - for (j = 0; j < n_reloads; j++) - { - register int r = reload_order[j]; - - if (reload_inherited[r] && reload_reg_rtx[r] != 0 - && ! reload_reg_free_before_p (REGNO (reload_reg_rtx[r]), - reload_when_needed[r])) - reload_inherited[r] = 0; - - /* If we found a better place to reload from, - validate it in the same fashion, if it is a reload reg. */ - if (reload_override_in[r] - && GET_CODE (reload_override_in[r]) == REG - && spill_reg_order[REGNO (reload_override_in[r])] >= 0 - && ! reload_reg_free_before_p (REGNO (reload_override_in[r]), - reload_when_needed[r])) - reload_override_in[r] = 0; - } - - /* Now that reload_override_in is known valid, - actually override reload_in. */ - for (j = 0; j < n_reloads; j++) - if (reload_override_in[j]) - reload_in[j] = reload_override_in[j]; - - /* For all the spill regs newly reloaded in this instruction, - record what they were reloaded from, so subsequent instructions - can inherit the reloads. */ - - for (j = 0; j < n_reloads; j++) - { - register int r = reload_order[j]; - register int i = reload_spill_index[r]; - - /* I is nonneg if this reload used one of the spill regs. - If reload_reg_rtx[r] is 0, this is an optional reload - that we opted to ignore. */ - if (i >= 0 && reload_reg_rtx[r] != 0) - { - /* Maybe the spill reg contains a copy of reload_out. */ - if (reload_out[r] != 0 && GET_CODE (reload_out[r]) == REG) - { - register int nregno = REGNO (reload_out[r]); - reg_last_reload_reg[nregno] = reload_reg_rtx[r]; - reg_reloaded_contents[i] = nregno; - reg_reloaded_insn[i] = insn; - reg_has_output_reload[nregno] = 1; - reg_is_output_reload[spill_regs[i]] = 1; - if (reload_when_needed[r] != RELOAD_OTHER) - abort (); - } - /* Maybe the spill reg contains a copy of reload_in. */ - else if (reload_out[r] == 0 - && (GET_CODE (reload_in[r]) == REG - || GET_CODE (reload_in_reg[r]) == REG)) - { - register int nregno; - if (GET_CODE (reload_in[r]) == REG) - nregno = REGNO (reload_in[r]); - else - nregno = REGNO (reload_in_reg[r]); - - /* If there are two separate reloads (one in and one out) - for the same (hard or pseudo) reg, - leave reg_last_reload_reg set - based on the output reload. - Otherwise, set it from this input reload. */ - if (!reg_has_output_reload[nregno]) - { - reg_last_reload_reg[nregno] = reload_reg_rtx[r]; - reg_reloaded_contents[i] = nregno; - reg_reloaded_insn[i] = insn; - - /* But don't do so if another input reload - will clobber this one's value. */ - if (! reload_reg_reaches_end_p (spill_regs[i], - reload_when_needed[r])) - { - reg_reloaded_contents[i] = -1; - reg_reloaded_insn[i] = 0; - } - } - } - /* Otherwise, the spill reg doesn't contain a copy of any reg. - Clear out its records, lest it be taken for a copy - of reload_in when that is no longer true. */ - else - { - reg_reloaded_contents[i] = -1; - reg_reloaded_insn[i] = 0; - } - } - - /* The following if-statement was #if 0'd in 1.34 (or before...). - It's reenabled in 1.35 because supposedly nothing else - deals with this problem. */ - - /* If a register gets output-reloaded from a non-spill register, - that invalidates any previous reloaded copy of it. - But forget_old_reloads_1 won't get to see it, because - it thinks only about the original insn. So invalidate it here. */ - if (i < 0 && reload_out[r] != 0 && GET_CODE (reload_out[r]) == REG) - { - register int nregno = REGNO (reload_out[r]); - reg_last_reload_reg[nregno] = 0; - reg_has_output_reload[nregno] = 1; - } - } -} - -/* Output insns to reload values in and out of the chosen reload regs. */ - -static void -emit_reload_insns (insn) - rtx insn; -{ - register int j; - rtx first_output_reload_insn = NEXT_INSN (insn); - rtx first_other_reload_insn = insn; - rtx first_operand_address_reload_insn = insn; - int special; - - /* Now output the instructions to copy the data into and out of the - reload registers. Do these in the order that the reloads were reported, - since reloads of base and index registers precede reloads of operands - and the operands may need the base and index registers reloaded. */ - - for (j = 0; j < n_reloads; j++) - { - register rtx old; - rtx store_insn; - - old = reload_in[j]; - if (old != 0 && ! reload_inherited[j] - && reload_reg_rtx[j] != old - && reload_reg_rtx[j] != 0) - { - register rtx reloadreg = reload_reg_rtx[j]; - rtx oldequiv = 0; - enum machine_mode mode; - rtx where; - rtx this_reload_insn = 0; - -#if 0 - /* No longer done because these paradoxical subregs now occur - only for regs and for spilled stack slots, and in either case - we can safely reload in the nominal machine mode. */ - - /* Strip off of OLD any size-increasing SUBREGs such as - (SUBREG:SI foo:QI 0). */ - - while (GET_CODE (old) == SUBREG && SUBREG_WORD (old) == 0 - && (GET_MODE_SIZE (GET_MODE (old)) - > GET_MODE_SIZE (GET_MODE (SUBREG_REG (old))))) - old = SUBREG_REG (old); -#endif - - /* Determine the mode to reload in. - This is very tricky because we have three to choose from. - There is the mode the insn operand wants (reload_inmode[J]). - There is the mode of the reload register RELOADREG. - There is the intrinsic mode of the operand, which we could find - by stripping some SUBREGs. - It turns out that RELOADREG's mode is irrelevant: - we can change that arbitrarily. - - Consider (SUBREG:SI foo:QI) as an operand that must be SImode; - then the reload reg may not support QImode moves, so use SImode. - If foo is in memory due to spilling a pseudo reg, this is safe, - because the QImode value is in the least significant part of a - slot big enough for a SImode. If foo is some other sort of - memory reference, then it is impossible to reload this case, - so previous passes had better make sure this never happens. - - Then consider a one-word union which has SImode and one of its - members is a float, being fetched as (SUBREG:SF union:SI). - We must fetch that as SFmode because we could be loading into - a float-only register. In this case OLD's mode is correct. - - Consider an immediate integer: it has VOIDmode. Here we need - to get a mode from something else. - - In some cases, there is a fourth mode, the operand's - containing mode. If the insn specifies a containing mode for - this operand, it overrides all others. - - I am not sure whether the algorithm here is always right, - but it does the right things in those cases. */ - - mode = GET_MODE (old); - if (mode == VOIDmode) - mode = reload_inmode[j]; - if (reload_strict_low[j]) - mode = GET_MODE (SUBREG_REG (reload_in[j])); - - /* If reloading from memory, see if there is a register - that already holds the same value. If so, reload from there. - We can pass 0 as the reload_reg_p argument because - any other reload has either already been emitted, - in which case find_equiv_reg will see the reload-insn, - or has yet to be emitted, in which case it doesn't matter - because we will use this equiv reg right away. */ - - if (GET_CODE (old) == MEM - || (GET_CODE (old) == REG - && REGNO (old) >= FIRST_PSEUDO_REGISTER - && reg_renumber[REGNO (old)] < 0)) - oldequiv = find_equiv_reg (old, insn, GENERAL_REGS, - -1, 0, 0, mode); - - /* If OLDEQUIV is a spill register, don't use it for this - if any other reload needs it at an earlier stage of this insn - or at this stage. */ - if (oldequiv && GET_CODE (oldequiv) == REG - && spill_reg_order[REGNO (oldequiv)] >= 0 - && (! reload_reg_free_p (REGNO (oldequiv), reload_when_needed[j]) - || ! reload_reg_free_before_p (REGNO (oldequiv), - reload_when_needed[j]))) - oldequiv = 0; - - /* If OLDEQUIV is not a spill register, - don't use it if any other reload wants it. */ - if (oldequiv && GET_CODE (oldequiv) == REG - && spill_reg_order[REGNO (oldequiv)] < 0) - { - int k; - for (k = 0; k < n_reloads; k++) - if (reload_reg_rtx[k] != 0 && k != j - && reg_overlap_mentioned_p (reload_reg_rtx[k], oldequiv)) - { - oldequiv = 0; - break; - } - } - - if (oldequiv == 0) - oldequiv = old; - - /* Encapsulate both RELOADREG and OLDEQUIV into that mode, - then load RELOADREG from OLDEQUIV. */ - - if (GET_MODE (reloadreg) != mode) - reloadreg = gen_rtx (SUBREG, mode, reloadreg, 0); - while (GET_CODE (oldequiv) == SUBREG && GET_MODE (oldequiv) != mode) - oldequiv = SUBREG_REG (oldequiv); - if (GET_MODE (oldequiv) != VOIDmode - && mode != GET_MODE (oldequiv)) - oldequiv = gen_rtx (SUBREG, mode, oldequiv, 0); - - /* Decide where to put reload insn for this reload. */ - switch (reload_when_needed[j]) - { - case RELOAD_OTHER: - where = first_operand_address_reload_insn; - break; - case RELOAD_FOR_INPUT_RELOAD_ADDRESS: - where = first_other_reload_insn; - break; - case RELOAD_FOR_OUTPUT_RELOAD_ADDRESS: - where = first_output_reload_insn; - break; - case RELOAD_FOR_OPERAND_ADDRESS: - where = insn; - } - - special = 0; - - /* Auto-increment addresses must be reloaded in a special way. */ - if (GET_CODE (oldequiv) == POST_INC - || GET_CODE (oldequiv) == POST_DEC - || GET_CODE (oldequiv) == PRE_INC - || GET_CODE (oldequiv) == PRE_DEC) - { - /* Prevent normal processing of this reload. */ - special = 1; - /* Output a special code sequence for this case. */ - this_reload_insn - = inc_for_reload (reloadreg, oldequiv, reload_inc[j], where); - } - - /* If we are reloading a pseudo-register that was set by the previous - insn, see if we can get rid of that pseudo-register entirely - by redirecting the previous insn into our reload register. */ - - else if (optimize && GET_CODE (old) == REG - && REGNO (old) >= FIRST_PSEUDO_REGISTER - && dead_or_set_p (insn, old) - /* This is unsafe if some other reload - uses the same reg first. */ - && (reload_when_needed[j] == RELOAD_OTHER - || reload_when_needed[j] == RELOAD_FOR_INPUT_RELOAD_ADDRESS)) - { - rtx temp = PREV_INSN (insn); - while (temp && GET_CODE (temp) == NOTE) - temp = PREV_INSN (temp); - if (temp - && GET_CODE (temp) == INSN - && GET_CODE (PATTERN (temp)) == SET - && SET_DEST (PATTERN (temp)) == old - /* Make sure we can access insn_operand_constraint. */ - && asm_noperands (PATTERN (temp)) < 0 - /* This is unsafe if prev insn rejects our reload reg. */ - && constraint_accepts_reg_p (insn_operand_constraint[recog_memoized (temp)][0], - reloadreg) - /* This is unsafe if operand occurs more than once in current - insn. Perhaps some occurrences aren't reloaded. */ - && count_occurrences (PATTERN (insn), old) == 1 - /* Don't risk splitting a matching pair of operands. */ - && ! reg_mentioned_p (old, SET_SRC (PATTERN (temp)))) - { - /* Store into the reload register instead of the pseudo. */ - SET_DEST (PATTERN (temp)) = reloadreg; - /* If these are the only uses of the pseudo reg, - pretend for GDB it lives in the reload reg we used. */ - if (reg_n_deaths[REGNO (old)] == 1 - && reg_n_sets[REGNO (old)] == 1) - { - reg_renumber[REGNO (old)] = REGNO (reload_reg_rtx[j]); - alter_reg (REGNO (old), -1); - } - special = 1; - } - } - - /* We can't do that, so output an insn to load RELOADREG. - Keep them in the following order: - all reloads for input reload addresses, - all reloads for ordinary input operands, - all reloads for addresses of non-reloaded operands, - the insn being reloaded, - all reloads for addresses of output reloads, - the output reloads. */ - if (! special) - this_reload_insn = gen_input_reload (reloadreg, oldequiv, where); - - /* Update where to put other reload insns. */ - if (this_reload_insn) - switch (reload_when_needed[j]) - { - case RELOAD_OTHER: - if (first_other_reload_insn == first_operand_address_reload_insn) - first_other_reload_insn = this_reload_insn; - break; - case RELOAD_FOR_OPERAND_ADDRESS: - if (first_operand_address_reload_insn == insn) - first_operand_address_reload_insn = this_reload_insn; - if (first_other_reload_insn == insn) - first_other_reload_insn = this_reload_insn; - } - - /* reload_inc[j] was formerly processed here. */ - } - - /* Add a note saying the input reload reg - dies in this insn, if anyone cares. */ -#ifdef PRESERVE_DEATH_INFO_REGNO_P - if (old != 0 - && reload_reg_rtx[j] != old - && reload_reg_rtx[j] != 0 - && reload_out[j] == 0 - && PRESERVE_DEATH_INFO_REGNO_P (REGNO (reload_reg_rtx[j]))) - { - register rtx reloadreg = reload_reg_rtx[j]; - - /* The code below is incorrect except for RELOAD_OTHER. */ - if (reload_when_needed[j] != RELOAD_OTHER) - abort (); - - /* Add a death note to this insn, for an input reload. */ - - if (! dead_or_set_p (insn, reloadreg)) - REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_DEAD, - reloadreg, REG_NOTES (insn)); - } -#endif - - /* ??? The following code is inadequate. - It handles regs inherited via reg_last_reloaded_contents - but not those inherited via find_equiv_reg. - Note that we can't expect spill_reg_store to contain anything - useful in the case of find_equiv_reg. */ - -#ifdef PRESERVE_DEATH_INFO_REGNO_P - /* For some registers it is important to keep the REG_DEATH - notes accurate for the final pass. - If we are inheriting an old output-reload out of such a reg, - the reg no longer dies there, so remove the death note. */ - - if (reload_reg_rtx[j] != 0 - && PRESERVE_DEATH_INFO_REGNO_P (REGNO (reload_reg_rtx[j])) - && reload_inherited[j] && reload_spill_index[j] >= 0 - && GET_CODE (reload_in[j]) == REG - && spill_reg_store[reload_spill_index[j]] != 0 - && regno_dead_p (REGNO (reload_reg_rtx[j]), - spill_reg_store[reload_spill_index[j]])) - { - remove_death (REGNO (reload_reg_rtx[j]), - spill_reg_store[reload_spill_index[j]]); - } - - /* Likewise for input reloads that were inherited. */ - - if (reload_reg_rtx[j] != 0 - && PRESERVE_DEATH_INFO_REGNO_P (REGNO (reload_reg_rtx[j])) - && reload_inherited[j] && reload_spill_index[j] >= 0 - && GET_CODE (reload_in[j]) == REG - && spill_reg_store[reload_spill_index[j]] == 0 - && reload_inheritance_insn[j] != 0 - && regno_dead_p (REGNO (reload_reg_rtx[j]), - reload_inheritance_insn[j])) - { - remove_death (REGNO (reload_reg_rtx[j]), - reload_inheritance_insn[j]); - } -#endif - - /* If we are reloading a register that was recently stored in with an - output-reload, see if we can prove there was - actually no need to store the old value in it. */ - - if (optimize && reload_inherited[j] && reload_spill_index[j] >= 0 - /* This is unsafe if some other reload uses the same reg first. */ - && (reload_when_needed[j] == RELOAD_OTHER - || reload_when_needed[j] == RELOAD_FOR_INPUT_RELOAD_ADDRESS) - && GET_CODE (reload_in[j]) == REG - && REGNO (reload_in[j]) >= FIRST_PSEUDO_REGISTER - && spill_reg_store[reload_spill_index[j]] != 0 - && dead_or_set_p (insn, reload_in[j]) - /* This is unsafe if operand occurs more than once in current - insn. Perhaps some occurrences weren't reloaded. */ - && count_occurrences (PATTERN (insn), reload_in[j]) == 1) - delete_output_reload (insn, j, reload_spill_index[j]); - - /* Input-reloading is done. Now do output-reloading, - storing the value from the reload-register after the main insn - if reload_out[j] is nonzero. */ - old = reload_out[j]; - if (old != 0 - && reload_reg_rtx[j] != old - && reload_reg_rtx[j] != 0 - /* An output operand that dies right away - does need a reload reg, but need not - be copied from it. */ - && ! (GET_CODE (old) == REG - && find_reg_note (insn, REG_DEAD, old))) - { - register rtx reloadreg = reload_reg_rtx[j]; - enum machine_mode mode; - -#if 0 - /* Strip off of OLD any size-increasing SUBREGs such as - (SUBREG:SI foo:QI 0). */ - - while (GET_CODE (old) == SUBREG && SUBREG_WORD (old) == 0 - && (GET_MODE_SIZE (GET_MODE (old)) - > GET_MODE_SIZE (GET_MODE (SUBREG_REG (old))))) - old = SUBREG_REG (old); -#endif - - /* Determine the mode to reload in. - See comments above (for input reloading). */ - - mode = GET_MODE (old); - if (mode == VOIDmode) - abort (); /* Should never happen for an output. */ -#if 0 - mode = reload_inmode[j]; -#endif - if (reload_strict_low[j]) - mode = GET_MODE (SUBREG_REG (reload_out[j])); - - /* Encapsulate both RELOADREG and OLD into that mode, - then load RELOADREG from OLD. */ - if (GET_MODE (reloadreg) != mode) - reloadreg = gen_rtx (SUBREG, mode, reloadreg, 0); - /* If OLD is a subreg, then strip it, since the subreg will - be altered by this very reload (if it's a strict_low_part). */ - while (GET_CODE (old) == SUBREG && GET_MODE (old) != mode) - old = SUBREG_REG (old); - if (GET_MODE (old) != VOIDmode - && mode != GET_MODE (old)) - old = gen_rtx (SUBREG, mode, old, 0); - /* Output the reload insn. */ - store_insn = emit_insn_before (gen_move_insn (old, reloadreg), - first_output_reload_insn); - first_output_reload_insn = store_insn; - /* If this output reload doesn't come from a spill reg, - clear any memory of reloaded copies of the pseudo reg. - If this output reload comes from a spill reg, - reg_has_output_reload will make this do nothing. */ - note_stores (PATTERN (store_insn), forget_old_reloads_1); - -#ifdef PRESERVE_DEATH_INFO_REGNO_P - /* If final will look at death notes for this reg, - put one on the output-reload insn. */ - if (PRESERVE_DEATH_INFO_REGNO_P (REGNO (reloadreg))) - REG_NOTES (store_insn) - = gen_rtx (EXPR_LIST, REG_DEAD, - reloadreg, REG_NOTES (store_insn)); - - /* Move all death-notes from the insn being reloaded - to the output reload, if they are for things used - as inputs in this output reload. */ - if (GET_CODE (old) != REG) - { - /* The note we will examine next. */ - rtx reg_notes = REG_NOTES (insn); - /* The place that pointed to this note. */ - rtx *prev_reg_note = ®_NOTES (insn); - - while (reg_notes) - { - rtx next_reg_notes = XEXP (reg_notes, 1); - if (REG_NOTE_KIND (reg_notes) == REG_DEAD - && reg_mentioned_p (XEXP (reg_notes, 0), old)) - { - *prev_reg_note = next_reg_notes; - XEXP (reg_notes, 1) = REG_NOTES (store_insn); - REG_NOTES (store_insn) = reg_notes; - } - else - prev_reg_note = &XEXP (reg_notes, 1); - - reg_notes = next_reg_notes; - } - } -#endif - } - else store_insn = 0; - - if (reload_spill_index[j] >= 0) - spill_reg_store[reload_spill_index[j]] = store_insn; - } - - /* Move death notes from INSN to output-operand-address reload insns. */ -#ifdef PRESERVE_DEATH_INFO_REGNO_P - { - rtx insn1; - /* Loop over those insns, last ones first. */ - for (insn1 = PREV_INSN (first_output_reload_insn); insn1 != insn; - insn1 = PREV_INSN (insn1)) - if (GET_CODE (insn1) == INSN && GET_CODE (PATTERN (insn1)) == SET) - { - rtx source = SET_SRC (PATTERN (insn1)); - - /* The note we will examine next. */ - rtx reg_notes = REG_NOTES (insn); - /* The place that pointed to this note. */ - rtx *prev_reg_note = ®_NOTES (insn); - - /* If the note is for something used in the source of this - output address reload insn, move the note. */ - while (reg_notes) - { - rtx next_reg_notes = XEXP (reg_notes, 1); - if (REG_NOTE_KIND (reg_notes) == REG_DEAD - && reg_mentioned_p (XEXP (reg_notes, 0), source)) - { - *prev_reg_note = next_reg_notes; - XEXP (reg_notes, 1) = REG_NOTES (insn1); - REG_NOTES (insn1) = reg_notes; - } - else - prev_reg_note = &XEXP (reg_notes, 1); - - reg_notes = next_reg_notes; - } - } - } -#endif -} - -/* Emit code before BEFORE_INSN to perform an input reload of IN to RELOADREG. - Handle case of reloading a PLUS expression (currently only happens for - stack slots with out-of-range offset). - - Returns last insn emitted. */ - -static rtx -gen_input_reload (reloadreg, in, before_insn) - rtx reloadreg; - rtx in; - rtx before_insn; -{ -#if 0 /* Install this in version 1.37. Avoid risk for now. */ - if (GET_CODE (in) == PLUS) - { - /* Don't use gen_move_insn to make what is actually an add insn. */ - emit_insn_before (gen_move_insn (reloadreg, XEXP (in, 0)), before_insn); - emit_insn_before (gen_add2_insn (reloadreg, XEXP (in, 1)), before_insn); - } - else -#endif - emit_insn_before (gen_move_insn (reloadreg, in), before_insn); - - return PREV_INSN (before_insn); -} - -/* Delete a previously made output-reload - whose result we now believe is not needed. - First we double-check. - - INSN is the insn now being processed. - J is the reload-number for this insn, - and SPILL_INDEX is the index in spill_regs of the reload-reg - being used for the reload. */ - -static void -delete_output_reload (insn, j, spill_index) - rtx insn; - int j; - int spill_index; -{ - register rtx i1; - - /* Get the raw pseudo-register referred to. */ - - rtx reg = reload_in[j]; - while (GET_CODE (reg) == SUBREG) - reg = SUBREG_REG (reg); - - /* If the pseudo-reg we are reloading is no longer referenced - anywhere between the store into it and here, - and no jumps or labels intervene, then the value can get - here through the reload reg alone. - Otherwise, give up--return. */ - for (i1 = NEXT_INSN (spill_reg_store[spill_index]); - i1 != insn; i1 = NEXT_INSN (i1)) - { - if (GET_CODE (i1) == CODE_LABEL || GET_CODE (i1) == JUMP_INSN) - return; - if ((GET_CODE (i1) == INSN || GET_CODE (i1) == CALL_INSN) - && reg_mentioned_p (reg, PATTERN (i1))) - return; - } - - /* If this insn will store in the pseudo again, - the previous store can be removed. */ - if (reload_out[j] == reload_in[j]) - delete_insn (spill_reg_store[spill_index]); - - /* See if the pseudo reg has been completely replaced - with reload regs. If so, delete the store insn - and forget we had a stack slot for the pseudo. */ - else if (reg_n_deaths[REGNO (reg)] == 1 - && reg_basic_block[REGNO (reg)] >= 0 - && find_regno_note (insn, REG_DEAD, REGNO (reg))) - { - rtx i2; - - /* We know that it was used only between here - and the beginning of the current basic block. - (We also know that the last use before INSN was - the output reload we are thinking of deleting, but never mind that.) - Search that range; see if any ref remains. */ - for (i2 = PREV_INSN (insn); i2; i2 = PREV_INSN (i2)) - { - /* Uses which just store in the pseudo don't count, - since if they are the only uses, they are dead. */ - if (GET_CODE (i2) == INSN - && GET_CODE (PATTERN (i2)) == SET - && SET_DEST (PATTERN (i2)) == reg) - continue; - if (GET_CODE (i2) == CODE_LABEL - || GET_CODE (i2) == JUMP_INSN) - break; - if ((GET_CODE (i2) == INSN || GET_CODE (i2) == CALL_INSN) - && reg_mentioned_p (reg, PATTERN (i2))) - /* Some other ref remains; - we can't do anything. */ - return; - } - - /* Delete the now-dead stores into this pseudo. */ - for (i2 = PREV_INSN (insn); i2; i2 = PREV_INSN (i2)) - { - /* Uses which just store in the pseudo don't count, - since if they are the only uses, they are dead. */ - if (GET_CODE (i2) == INSN - && GET_CODE (PATTERN (i2)) == SET - && SET_DEST (PATTERN (i2)) == reg) - delete_insn (i2); - if (GET_CODE (i2) == CODE_LABEL - || GET_CODE (i2) == JUMP_INSN) - break; - } - - /* For the debugging info, - say the pseudo lives in this reload reg. */ - reg_renumber[REGNO (reg)] = REGNO (reload_reg_rtx[j]); - alter_reg (REGNO (reg), -1); - } -} - - -/* Output reload-insns to reload VALUE into RELOADREG. - VALUE is a autoincrement or autodecrement RTX whose operand - is a register or memory location; - so reloading involves incrementing that location. - - INC_AMOUNT is the number to increment or decrement by (always positive). - This cannot be deduced from VALUE. - - INSN is the insn before which the new insns should be emitted. - - The return value is the first of the insns emitted. */ - -static rtx -inc_for_reload (reloadreg, value, inc_amount, insn) - rtx reloadreg; - rtx value; - int inc_amount; - rtx insn; -{ - /* REG or MEM to be copied and incremented. */ - rtx incloc = XEXP (value, 0); - /* Nonzero if increment after copying. */ - int post = (GET_CODE (value) == POST_DEC || GET_CODE (value) == POST_INC); - - /* No hard register is equivalent to this register after - inc/dec operation. If REG_LAST_RELOAD_REG were non-zero, - we could inc/dec that register as well (maybe even using it for - the source), but I'm not sure it's worth worrying about. */ - if (GET_CODE (incloc) == REG) - reg_last_reload_reg[REGNO (incloc)] = 0; - - if (GET_CODE (value) == PRE_DEC || GET_CODE (value) == POST_DEC) - inc_amount = - inc_amount; - - /* First handle preincrement, which is simpler. */ - if (! post) - { - /* If incrementing a register, assume we can - output an insn to increment it directly. */ - if (GET_CODE (incloc) == REG && - (REGNO (incloc) < FIRST_PSEUDO_REGISTER - || reg_renumber[REGNO (incloc)] >= 0)) - { - rtx first_new - = emit_insn_before (gen_add2_insn (incloc, - gen_rtx (CONST_INT, VOIDmode, - inc_amount)), - insn); - emit_insn_before (gen_move_insn (reloadreg, incloc), insn); - return first_new; - } - else - /* Else we must not assume we can increment the location directly - (even though on many target machines we can); - copy it to the reload register, increment there, then save back. */ - { - rtx first_new - = emit_insn_before (gen_move_insn (reloadreg, incloc), insn); - emit_insn_before (gen_add2_insn (reloadreg, - gen_rtx (CONST_INT, VOIDmode, - inc_amount)), - insn); - emit_insn_before (gen_move_insn (incloc, reloadreg), insn); - return first_new; - } - } - /* Postincrement. - Because this might be a jump insn or a compare, and because RELOADREG - may not be available after the insn in an input reload, - we must do the incrementation before the insn being reloaded for. */ - else - { - /* Copy the value, then increment it. */ - rtx first_new - = emit_insn_before (gen_move_insn (reloadreg, incloc), insn); - - /* If incrementing a register, assume we can - output an insn to increment it directly. */ - if (GET_CODE (incloc) == REG && - (REGNO (incloc) < FIRST_PSEUDO_REGISTER - || reg_renumber[REGNO (incloc)] >= 0)) - { - emit_insn_before (gen_add2_insn (incloc, - gen_rtx (CONST_INT, VOIDmode, - inc_amount)), - insn); - } - else - /* Else we must not assume we can increment INCLOC - (even though on many target machines we can); - increment the copy in the reload register, - save that back, then decrement the reload register - so it has the original value. */ - { - emit_insn_before (gen_add2_insn (reloadreg, - gen_rtx (CONST_INT, VOIDmode, - inc_amount)), - insn); - emit_insn_before (gen_move_insn (incloc, reloadreg), insn); - emit_insn_before (gen_sub2_insn (reloadreg, - gen_rtx (CONST_INT, VOIDmode, - inc_amount)), - insn); - } - return first_new; - } -} - -/* Return 1 if we are certain that the constraint-string STRING allows - the hard register REG. Return 0 if we can't be sure of this. */ - -static int -constraint_accepts_reg_p (string, reg) - char *string; - rtx reg; -{ - int value = 0; - int regno = true_regnum (reg); - - /* We win if this register is a general register - and each alternative accepts all general registers. */ - if (! TEST_HARD_REG_BIT (reg_class_contents[(int) GENERAL_REGS], regno)) - return 0; - - /* Initialize for first alternative. */ - value = 0; - /* Check that each alternative contains `g' or `r'. */ - while (1) - switch (*string++) - { - case 0: - /* If an alternative lacks `g' or `r', we lose. */ - return value; - case ',': - /* If an alternative lacks `g' or `r', we lose. */ - if (value == 0) - return 0; - /* Initialize for next alternative. */ - value = 0; - break; - case 'g': - case 'r': - value = 1; - } -} - -/* Return the number of places FIND appears within X. */ - -static int -count_occurrences (x, find) - register rtx x, find; -{ - register int i, j; - register enum rtx_code code; - register char *format_ptr; - int count; - - if (x == find) - return 1; - if (x == 0) - return 0; - - code = GET_CODE (x); - - switch (code) - { - case REG: - case QUEUED: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case CODE_LABEL: - case PC: - case CC0: - return 0; - } - - format_ptr = GET_RTX_FORMAT (code); - count = 0; - - for (i = 0; i < GET_RTX_LENGTH (code); i++) - { - switch (*format_ptr++) - { - case 'e': - count += count_occurrences (XEXP (x, i), find); - break; - - case 'E': - if (XVEC (x, i) != NULL) - { - for (j = 0; j < XVECLEN (x, i); j++) - count += count_occurrences (XVECEXP (x, i, j), find); - } - break; - } - } - return count; -} diff --git a/gnu/usr.bin/gcc1/cc1/rtl.c b/gnu/usr.bin/gcc1/cc1/rtl.c deleted file mode 100644 index e2cea9a9e0..0000000000 --- a/gnu/usr.bin/gcc1/cc1/rtl.c +++ /dev/null @@ -1,811 +0,0 @@ -/* Allocate, read and print RTL for C-Compiler - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "config.h" -#include -#include -#include "rtl.h" - -#include "obstack.h" -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free -extern int xmalloc (); -extern void free (); - -/* Obstack used for allocating RTL objects. - Between functions, this is the permanent_obstack. - While parsing and expanding a function, this is maybepermanent_obstack - so we can save it if it is an inline function. - During optimization and output, this is temporary_obstack. */ - -extern struct obstack *rtl_obstack; - -#define MIN(x,y) ((x < y) ? x : y) - -extern long ftell(); - -/* Indexed by rtx code, gives number of operands for an rtx with that code. - Does NOT include rtx header data (code and links). - This array is initialized in init_rtl. */ - -int rtx_length[NUM_RTX_CODE + 1]; - -/* Indexed by rtx code, gives the name of that kind of rtx, as a C string. */ - -#define DEF_RTL_EXPR(ENUM, NAME, FORMAT) NAME , - -char *rtx_name[] = { -#include "rtl.def" /* rtl expressions are documented here */ -}; - -#undef DEF_RTL_EXPR - -/* Indexed by machine mode, gives the name of that machine mode. - This name does not include the letters "mode". */ - -#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) NAME, - -char *mode_name[] = { -#include "machmode.def" -}; - -#undef DEF_MACHMODE - -/* Indexed by machine mode, gives the length of the mode, in bytes. - GET_MODE_CLASS uses this. */ - -#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) CLASS, - -enum mode_class mode_class[] = { -#include "machmode.def" -}; - -#undef DEF_MACHMODE - -/* Indexed by machine mode, gives the length of the mode, in bytes. - GET_MODE_SIZE uses this. */ - -#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) \ - (SIZE*UNITS_PER_WORD+3)/4, - -int mode_size[] = { -#include "machmode.def" -}; - -#undef DEF_MACHMODE - -/* Indexed by machine mode, gives the length of the mode's subunit. - GET_MODE_UNIT_SIZE uses this. */ - -#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) \ - (UNIT*UNITS_PER_WORD+3)/4, - -int mode_unit_size[] = { -#include "machmode.def" /* machine modes are documented here */ -}; - -#undef DEF_MACHMODE - -/* Indexed by machine mode, gives next wider natural mode - (QI -> HI -> SI -> DI, etc.) Widening multiply instructions - use this. */ - -#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) \ - (enum machine_mode) WIDER, - -enum machine_mode mode_wider_mode[] = { -#include "machmode.def" /* machine modes are documented here */ -}; - -#undef DEF_MACHMODE - -/* Indexed by rtx code, gives a sequence of operand-types for - rtx's of that code. The sequence is a C string in which - each charcter describes one operand. */ - -char *rtx_format[] = { - /* "*" undefined. - can cause a warning message - "0" field is unused (or used in a phase-dependent manner) - prints nothing - "i" an integer - prints the integer - "s" a pointer to a string - prints the string - "S" like "s", but optional: - the containing rtx may end before this operand - "e" a pointer to an rtl expression - prints the expression - "E" a pointer to a vector that points to a number of rtl expressions - prints a list of the rtl expressions - "u" a pointer to another insn - prints the uid of the insn. */ - -#define DEF_RTL_EXPR(ENUM, NAME, FORMAT) FORMAT , -#include "rtl.def" /* rtl expressions are defined here */ -#undef DEF_RTL_EXPR -}; - -/* Names for kinds of NOTEs and REG_NOTEs. */ - -char *note_insn_name[] = { "NOTE_INSN_FUNCTION_BEG", "NOTE_INSN_DELETED", - "NOTE_INSN_BLOCK_BEG", "NOTE_INSN_BLOCK_END", - "NOTE_INSN_LOOP_BEG", "NOTE_INSN_LOOP_END", - "NOTE_INSN_FUNCTION_END", "NOTE_INSN_SETJMP", - "NOTE_INSN_LOOP_CONT" }; - -char *reg_note_name[] = { "", "REG_DEAD", "REG_INC", "REG_EQUIV", "REG_WAS_0", - "REG_EQUAL", "REG_RETVAL", "REG_LIBCALL", - "REG_NONNEG", "REG_ASM_LINE", "REG_ASM_FILE" }; - -/* Allocate an rtx vector of N elements. - Store the length, and initialize all elements to zero. */ - -rtvec -rtvec_alloc (n) - int n; -{ - rtvec rt; - int i; - - rt = (rtvec) obstack_alloc (rtl_obstack, - sizeof (struct rtvec_def) - + (( n - 1) * sizeof (rtunion))); - - /* clear out the vector */ - PUT_NUM_ELEM(rt, n); - for (i=0; i < n; i++) - rt->elem[i].rtvec = NULL; /* @@ not portable due to rtunion */ - - return rt; -} - -/* Allocate an rtx of code CODE. The CODE is stored in the rtx; - all the rest is initialized to zero. */ - -rtx -rtx_alloc (code) - RTX_CODE code; -{ - rtx rt; - register int nelts = GET_RTX_LENGTH (code); - register int length = sizeof (struct rtx_def) - + (nelts - 1) * sizeof (rtunion); - - rt = (rtx) obstack_alloc (rtl_obstack, length); - - * (int *) rt = 0; - PUT_CODE (rt, code); - - return rt; -} - -/* Create a new copy of an rtx. - Recursively copies the operands of the rtx, - except for those few rtx codes that are sharable. */ - -rtx -copy_rtx (orig) - register rtx orig; -{ - register rtx copy; - register int i, j; - register RTX_CODE code; - register char *format_ptr; - - code = GET_CODE (orig); - - switch (code) - { - case REG: - case QUEUED: - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case CODE_LABEL: - case PC: - case CC0: - return orig; - } - - copy = rtx_alloc (code); - PUT_MODE (copy, GET_MODE (orig)); - copy->in_struct = orig->in_struct; - copy->volatil = orig->volatil; - copy->unchanging = orig->unchanging; - copy->integrated = orig->integrated; - - format_ptr = GET_RTX_FORMAT (GET_CODE (copy)); - - for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++) - { - switch (*format_ptr++) - { - case 'e': - XEXP (copy, i) = XEXP (orig, i); - if (XEXP (orig, i) != NULL) - XEXP (copy, i) = copy_rtx (XEXP (orig, i)); - break; - - case 'E': - XVEC (copy, i) = XVEC (orig, i); - if (XVEC (orig, i) != NULL) - { - XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i)); - for (j = 0; j < XVECLEN (copy, i); j++) - XVECEXP (copy, i, j) = copy_rtx (XVECEXP (orig, i, j)); - } - break; - - default: - XINT (copy, i) = XINT (orig, i); - break; - } - } - return copy; -} - -/* Printing rtl for debugging dumps. */ - -static FILE *outfile; - -char spaces[] = " "; - -static int sawclose = 0; - -/* Print IN_RTX onto OUTFILE. This is the recursive part of printing. */ - -static void -print_rtx (in_rtx) - register rtx in_rtx; -{ - static int indent; - register int i, j; - register char *format_ptr; - - if (sawclose) - { - fprintf (outfile, "\n%s", - (spaces + (sizeof spaces - indent * 2))); - sawclose = 0; - } - - if (in_rtx == 0) - { - fprintf (outfile, "(nil)"); - sawclose = 1; - return; - } - - /* print name of expression code */ - fprintf (outfile, "(%s", GET_RTX_NAME (GET_CODE (in_rtx))); - - if (in_rtx->in_struct) - fprintf (outfile, "/s"); - - if (in_rtx->volatil) - fprintf (outfile, "/v"); - - if (in_rtx->unchanging) - fprintf (outfile, "/u"); - - if (in_rtx->integrated) - fprintf (outfile, "/i"); - - if (GET_MODE (in_rtx) != VOIDmode) - { - /* Print REG_NOTE names for EXPR_LIST and INSN_LIST. */ - if (GET_CODE (in_rtx) == EXPR_LIST || GET_CODE (in_rtx) == INSN_LIST) - fprintf (outfile, ":%s", GET_REG_NOTE_NAME (GET_MODE (in_rtx))); - else - fprintf (outfile, ":%s", GET_MODE_NAME (GET_MODE (in_rtx))); - } - - format_ptr = GET_RTX_FORMAT (GET_CODE (in_rtx)); - - for (i = 0; i < GET_RTX_LENGTH (GET_CODE (in_rtx)); i++) - switch (*format_ptr++) - { - case 'S': - case 's': - if (XSTR (in_rtx, i) == 0) - fprintf (outfile, " \"\""); - else - fprintf (outfile, " (\"%s\")", XSTR (in_rtx, i)); - sawclose = 1; - break; - - /* 0 indicates a field for internal use that should not be printed. */ - case '0': - break; - - case 'e': - indent += 2; - if (!sawclose) - fprintf (outfile, " "); - print_rtx (XEXP (in_rtx, i)); - indent -= 2; - break; - - case 'E': - indent += 2; - if (sawclose) - { - fprintf (outfile, "\n%s", - (spaces + (sizeof spaces - indent * 2))); - sawclose = 0; - } - fprintf (outfile, "[ "); - if (NULL != XVEC (in_rtx, i)) - { - indent += 2; - if (XVECLEN (in_rtx, i)) - sawclose = 1; - - for (j = 0; j < XVECLEN (in_rtx, i); j++) - print_rtx (XVECEXP (in_rtx, i, j)); - - indent -= 2; - } - if (sawclose) - fprintf (outfile, "\n%s", - (spaces + (sizeof spaces - indent * 2))); - - fprintf (outfile, "] "); - sawclose = 1; - indent -= 2; - break; - - case 'i': - fprintf (outfile, " %d", XINT (in_rtx, i)); - sawclose = 0; - break; - - /* Print NOTE_INSN names rather than integer codes. */ - - case 'n': - if (XINT (in_rtx, i) <= 0) - fprintf (outfile, " %s", GET_NOTE_INSN_NAME (XINT (in_rtx, i))); - else - fprintf (outfile, " %d", XINT (in_rtx, i)); - sawclose = 0; - break; - - case 'u': - if (XEXP (in_rtx, i) != NULL) - fprintf(outfile, " %d", INSN_UID (XEXP (in_rtx, i))); - else - fprintf(outfile, " 0"); - sawclose = 0; - break; - - default: - fprintf (stderr, - "switch format wrong in rtl.print_rtx(). format was: %c.\n", - format_ptr[-1]); - abort (); - } - - fprintf (outfile, ")"); - sawclose = 1; -} - -/* Call this function from the debugger to see what X looks like. */ - -void -debug_rtx (x) - rtx x; -{ - outfile = stderr; - print_rtx (x); - fprintf (stderr, "\n"); -} - -/* External entry point for printing a chain of insns - starting with RTX_FIRST onto file OUTF. - A blank line separates insns. - - If RTX_FIRST is not an insn, then it alone is printed, with no newline. */ - -void -print_rtl (outf, rtx_first) - FILE *outf; - rtx rtx_first; -{ - register rtx tmp_rtx; - - outfile = outf; - sawclose = 0; - - switch (GET_CODE (rtx_first)) - { - case INSN: - case JUMP_INSN: - case CALL_INSN: - case NOTE: - case CODE_LABEL: - case BARRIER: - for (tmp_rtx = rtx_first; NULL != tmp_rtx; tmp_rtx = NEXT_INSN (tmp_rtx)) - { - print_rtx (tmp_rtx); - fprintf (outfile, "\n"); - } - break; - - default: - print_rtx (rtx_first); - } -} - -/* Subroutines of read_rtx. */ - -/* Dump code after printing a message. Used when read_rtx finds - invalid data. */ - -static void -dump_and_abort (expected_c, actual_c, infile) - int expected_c, actual_c; - FILE *infile; -{ - int c, i; - - if (expected_c >= 0) - fprintf (stderr, - "Expected character %c. Found character %c.", - expected_c, actual_c); - fprintf (stderr, " At file position: %ld\n", ftell (infile)); - fprintf (stderr, "Following characters are:\n\t"); - for (i = 0; i < 200; i++) - { - c = getc (infile); - if (EOF == c) break; - putc (c, stderr); - } - fprintf (stderr, "Aborting.\n"); - abort (); -} - -/* Read chars from INFILE until a non-whitespace char - and return that. Comments, both Lisp style and C style, - are treated as whitespace. - Tools such as genflags use this function. */ - -int -read_skip_spaces (infile) - FILE *infile; -{ - register int c; - while (c = getc (infile)) - { - if (c == ' ' || c == '\n' || c == '\t' || c == '\f') - ; - else if (c == ';') - { - while ((c = getc (infile)) && c != '\n') ; - } - else if (c == '/') - { - register int prevc; - c = getc (infile); - if (c != '*') - dump_and_abort ('*', c, infile); - - prevc = 0; - while (c = getc (infile)) - { - if (prevc == '*' && c == '/') - break; - prevc = c; - } - } - else break; - } - return c; -} - -/* Read an rtx code name into the buffer STR[]. - It is terminated by any of the punctuation chars of rtx printed syntax. */ - -static void -read_name (str, infile) - char *str; - FILE *infile; -{ - register char *p; - register int c; - - c = read_skip_spaces(infile); - - p = str; - while (1) - { - if (c == ' ' || c == '\n' || c == '\t' || c == '\f') - break; - if (c == ':' || c == ')' || c == ']' || c == '"' || c == '/' - || c == '(' || c == '[') - { - ungetc (c, infile); - break; - } - *p++ = c; - c = getc (infile); - } - if (p == str) - { - fprintf (stderr, "missing name or number"); - dump_and_abort (-1, -1, infile); - } - - *p = 0; -} - -/* Read an rtx in printed representation from INFILE - and return an actual rtx in core constructed accordingly. - read_rtx is not used in the compiler proper, but rather in - the utilities gen*.c that construct C code from machine descriptions. */ - -rtx -read_rtx (infile) - FILE *infile; -{ - register int i, j, list_counter; - RTX_CODE tmp_code; - register char *format_ptr; - /* tmp_char is a buffer used for reading decimal integers - and names of rtx types and machine modes. - Therefore, 256 must be enough. */ - char tmp_char[256]; - rtx return_rtx; - register int c; - int tmp_int; - - /* Linked list structure for making RTXs: */ - struct rtx_list - { - struct rtx_list *next; - rtx value; /* Value of this node... */ - }; - - c = read_skip_spaces (infile); /* Should be open paren. */ - if (c != '(') - dump_and_abort ('(', c, infile); - - read_name (tmp_char, infile); - - tmp_code = UNKNOWN; - - for (i=0; i < NUM_RTX_CODE; i++) /* @@ might speed this search up */ - { - if (!(strcmp (tmp_char, GET_RTX_NAME (i)))) - { - tmp_code = (RTX_CODE) i; /* get value for name */ - break; - } - } - if (tmp_code == UNKNOWN) - { - fprintf (stderr, - "Unknown rtx read in rtl.read_rtx(). Code name was %s .", - tmp_char); - } - /* (NIL) stands for an expression that isn't there. */ - if (tmp_code == NIL) - { - /* Discard the closeparen. */ - while ((c = getc (infile)) && c != ')'); - return 0; - } - - return_rtx = rtx_alloc (tmp_code); /* if we end up with an insn expression - then we free this space below. */ - format_ptr = GET_RTX_FORMAT (GET_CODE (return_rtx)); - - /* If what follows is `: mode ', read it and - store the mode in the rtx. */ - - i = read_skip_spaces (infile); - if (i == ':') - { - register int k; - read_name (tmp_char, infile); - for (k = 0; k < NUM_MACHINE_MODES; k++) - if (!strcmp (GET_MODE_NAME (k), tmp_char)) - break; - - PUT_MODE (return_rtx, (enum machine_mode) k ); - } - else - ungetc (i, infile); - - for (i = 0; i < GET_RTX_LENGTH (GET_CODE (return_rtx)); i++) - switch (*format_ptr++) - { - /* 0 means a field for internal use only. - Don't expect it to be present in the input. */ - case '0': - break; - - case 'e': - case 'u': - XEXP (return_rtx, i) = read_rtx (infile); - break; - - case 'E': - { - register struct rtx_list *next_rtx, *rtx_list_link; - struct rtx_list *list_rtx; - - c = read_skip_spaces (infile); - if (c != '[') - dump_and_abort ('[', c, infile); - - /* add expressions to a list, while keeping a count */ - next_rtx = NULL; - list_counter = 0; - while ((c = read_skip_spaces (infile)) && c != ']') - { - ungetc (c, infile); - list_counter++; - rtx_list_link = (struct rtx_list *) - alloca (sizeof (struct rtx_list)); - rtx_list_link->value = read_rtx (infile); - if (next_rtx == 0) - list_rtx = rtx_list_link; - else - next_rtx->next = rtx_list_link; - next_rtx = rtx_list_link; - rtx_list_link->next = 0; - } - /* get vector length and allocate it */ - XVEC (return_rtx, i) = (list_counter - ? rtvec_alloc (list_counter) - : (struct rtvec_def *) NULL); - if (list_counter > 0) - { - next_rtx = list_rtx; - for (j = 0; j < list_counter; j++, - next_rtx = next_rtx->next) - XVECEXP (return_rtx, i, j) = next_rtx->value; - } - /* close bracket gotten */ - } - break; - - case 'S': - /* 'S' is an optional string: if a closeparen follows, - just store NULL for this element. */ - c = read_skip_spaces (infile); - ungetc (c, infile); - if (c == ')') - { - XSTR (return_rtx, i) = 0; - break; - } - - case 's': - { - int saw_paren = 0; - register char *stringbuf; - int stringbufsize; - - c = read_skip_spaces (infile); - if (c == '(') - { - saw_paren = 1; - c = read_skip_spaces (infile); - } - if (c != '"') - dump_and_abort ('"', c, infile); - j = 0; - stringbufsize = 10; - stringbuf = (char *) xmalloc (stringbufsize + 1); - - while (1) - { - if (j >= stringbufsize - 4) - { - stringbufsize *= 2; - stringbuf = (char *) xrealloc (stringbuf, stringbufsize + 1); - } - stringbuf[j] = getc (infile); /* Read the string */ - if (stringbuf[j] == '\\') - { - stringbuf[j] = getc (infile); /* Read the string */ - /* \; makes stuff for a C string constant containing - newline and tab. */ - if (stringbuf[j] == ';') - { - strcpy (&stringbuf[j], "\\n\\t"); - j += 3; - } - } - else if (stringbuf[j] == '"') - break; - j++; - } - - stringbuf[j] = 0; /* NUL terminate the string */ - stringbuf = (char *) xrealloc (stringbuf, j + 1); - - if (saw_paren) - { - c = read_skip_spaces (infile); - if (c != ')') - dump_and_abort (')', c, infile); - } - XSTR (return_rtx, i) = stringbuf; - } - break; - - case 'i': - case 'n': - read_name (tmp_char, infile); - tmp_int = atoi (tmp_char); - XINT (return_rtx, i) = tmp_int; - break; - - default: - fprintf (stderr, - "switch format wrong in rtl.read_rtx(). format was: %c.\n", - format_ptr[-1]); - fprintf (stderr, "\tfile position: %ld\n", ftell (infile)); - abort (); - } - - c = read_skip_spaces (infile); - if (c != ')') - dump_and_abort (')', c, infile); - - return return_rtx; -} - -/* This is called once per compilation, before any rtx's are constructed. - It initializes the vector `rtx_length'. */ - -void -init_rtl () -{ - int i; - - for (i = 0; i < NUM_RTX_CODE; i++) - rtx_length[i] = strlen (rtx_format[i]); - - /* Make CONST_DOUBLE bigger, if real values are bigger than - it normally expects to have room for. - Note that REAL_VALUE_TYPE is not defined by default, - since tree.h is not included. But the default dfn as `double' - would do no harm. */ -#ifdef REAL_VALUE_TYPE - i = sizeof (REAL_VALUE_TYPE) / sizeof (rtunion) + 2; - if (rtx_length[(int) CONST_DOUBLE] < i) - { - char *s = (char *) malloc (i + 1); - rtx_length[(int) CONST_DOUBLE] = i; - rtx_format[(int) CONST_DOUBLE] = s; - *s++ = 'e'; - *s++ = '0'; - /* Set the GET_RTX_FORMAT of CONST_DOUBLE to a string - of as many `i's as we now have elements. */ - for (i = 0; i < rtx_length[(int) CONST_DOUBLE]; i++) - *s++ = 'i'; - *s++ = 0; - } -#endif -} diff --git a/gnu/usr.bin/gcc1/cc1/rtl.def b/gnu/usr.bin/gcc1/cc1/rtl.def deleted file mode 100644 index 3dbf9a610c..0000000000 --- a/gnu/usr.bin/gcc1/cc1/rtl.def +++ /dev/null @@ -1,542 +0,0 @@ -/* This file contains the definitions and documentation for the - Register Transfer Expressions (rtx's) that make up the - Register Transfer Language (rtl) used in the Back End of the GNU compiler. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* Expression definitions and descriptions for all targets are in this file. - Some will not be used for some targets. - - The fields in the cpp macro call "DEF_RTL_EXPR()" - are used to create declarations in the C source of the compiler. - - The fields are: - - 1. The internal name of the rtx used in the C source. - It is a tag in the enumeration "enum rtx_code" defined in "rtl.h". - By convention these are in UPPER_CASE. - - 2. The name of the rtx in the external ASCII format read by - read_rtx(), and printed by print_rtx(). - These names are stored in rtx_name[]. - By convention these are the internal (field 1) names in lower_case. - - 3. The print format, and type of each rtx->fld[] (field) in this rtx. - These formats are stored in rtx_format[]. - The meaning of the formats is documented in front of this array in rtl.c - - */ - -/* --------------------------------------------------------------------- - Expressions (and "meta" expressions) used for structuring the - rtl representation of a program. - --------------------------------------------------------------------- */ - -/* an expression code name unknown to the reader */ -DEF_RTL_EXPR(UNKNOWN, "UnKnown", "*") - -/* (NIL) is used by rtl reader and printer to represent a null pointer. */ - -DEF_RTL_EXPR(NIL, "nil", "*") - -/* --------------------------------------------------------------------- - Expressions used in constructing lists. - --------------------------------------------------------------------- */ - -/* a linked list of expressions */ -DEF_RTL_EXPR(EXPR_LIST, "expr_list", "ee") - -/* a linked list of instructions. - The insns are represented in print by their uids. */ -DEF_RTL_EXPR(INSN_LIST, "insn_list", "ue") - -/* ---------------------------------------------------------------------- - Expression types for machine descripions. - These do not appear in actual rtl code in the compiler. - ---------------------------------------------------------------------- */ - -/* Appears only in machine descriptions. - Means use the function named by the second arg (the string) - as a predicate; if matched, store the structure that was matched - in the operand table at index specified by the first arg (the integer). - If the second arg is the null string, the structure is just stored. - - A third string argument indicates to the register allocator restrictions - on where the operand can be allocated. - - If the target needs no restriction on any instruction this field should - be the null string. - - The string is prepended by: - '=' to indicate the operand is only written to. - '+' to indicate the operand is both read and written to. - - Each character in the string represents an allocatable class for an operand. - 'g' indicates the operand can be any valid class. - 'i' indicates the operand can be immeadiate (in the instruction) data. - 'r' indicates the operand can be in a register. - 'm' indicates the operand can be in memory. - 'o' a subset of the 'm' class. Those memory addressing modes that - can be offset at compile time (have a constant added to them). - - Other characters indicate target dependent operand classes and - are described in each target's machine description. - - For instructions with more than one operand, sets of classes can be - separated by a comma to indicate the appropriate multi-operand constraints. - There must be a 1 to 1 correspondence between these sets of classes in - all operands for an instruction. - */ -DEF_RTL_EXPR(MATCH_OPERAND, "match_operand", "iss") - -/* Appears only in machine descriptions. - Means match only something equal to what is stored in the operand table - at the index specified by the argument. */ -DEF_RTL_EXPR(MATCH_DUP, "match_dup", "i") - -/* Apply a predicate, AND match recursively the operands of the rtx. - Operand 0 is the operand-number, as in match_operand. - Operand 1 is a predicate to apply (as a string, a function name). - Operand 2 is a vector of expressions, each of which must match - one subexpression of the rtx this construct is matching. */ -DEF_RTL_EXPR(MATCH_OPERATOR, "match_operator", "isE") - -/* Appears only in machine descriptions. - Defines the pattern for one kind of instruction. - Operand: - 0: names this instruction. - If the name is the null string, the instruction is in the - machine description just to be recognized, and will never be emitted by - the tree to rtl expander. - 1: is the pattern. - 2: is a string which is a C expression - giving an additional condition for recognizing this pattern. - A null string means no extra condition. - 3: is the action to execute if this pattern is matched. - If this assembler code template starts with a * then it is a fragment of - C code to run to decide on a template to use. Otherwise, it is the - template to use. - 4: optionally, a string of machine-dependant information about the insn. - */ -DEF_RTL_EXPR(DEFINE_INSN, "define_insn", "sEssS") - -/* Definition of a peephole optimization. - 1st operand: vector of insn patterns to match - 2nd operand: C expression that must be true - 3rd operand: template or C code to produce assembler output. - 4: optionally, a string of machine-dependant information about the insn. */ -DEF_RTL_EXPR(DEFINE_PEEPHOLE, "define_peephole", "EssS") - -/* Definition of a combiner pattern. - Operands not defined yet. */ -DEF_RTL_EXPR(DEFINE_COMBINE, "define_combine", "Ess") - -/* Define how to generate multiple insns for a standard insn name. - 1st operand: the insn name. - 2nd operand: vector of insn-patterns. - Use match_operand to substitute an element of `recog_operand'. - 3rd operand: C expression that must be true for this to be available. - This may not test any operands. - 4th operand: Extra C code to execute before generating the insns. - This might, for example, create some RTX's and store them in - elements of `recog_operand' for use by the vector of insn-patterns. - (`operands' is an alias here for `recog_operand'). */ -DEF_RTL_EXPR(DEFINE_EXPAND, "define_expand", "sEss") - -/* SEQUENCE appears in the result of a `gen_...' function - for a DEFINE_EXPAND that wants to make several insns. - Its elements are the bodies of the insns that should be made. - `emit_insn' takes the SEQUENCE apart and makes separate insns. */ -DEF_RTL_EXPR(SEQUENCE, "sequence", "E") - -/* Refers to the address of its argument. - This appears only in machine descriptions, indicating that - any expression that would be acceptable as the operand of MEM - should be matched. */ -DEF_RTL_EXPR(ADDRESS, "address", "e") - -/* ---------------------------------------------------------------------- - Expressions types used for things in the instruction chain. - - All formats must start with "iuu" to handle the chain. - Each insn expression holds an rtl instruction and its semantics - during back-end processing. - See macros's in "rtl.h" for the meaning of each rtx->fld[]. - - ---------------------------------------------------------------------- */ - -/* An instruction that cannot jump. */ -DEF_RTL_EXPR(INSN, "insn", "iuueiee") - -/* An instruction that can possibly jump. - Fields ( rtx->fld[] ) have exact same meaning as INSN's - except field 3 is also used in jump.c to point to the label jumped to. - Field 7 is used in jump.c as the JUMP_LABEL. */ -DEF_RTL_EXPR(JUMP_INSN, "jump_insn", "iuueiee0") - -/* An instruction that can possibly call a subroutine - but which will not change which instruction comes next - in the current function. - Fields ( rtx->fld[] ) have exact same meaning as INSN's. */ -DEF_RTL_EXPR(CALL_INSN, "call_insn", "iuueiee") - -/* A marker that indicates that control will not flow through. */ -DEF_RTL_EXPR(BARRIER, "barrier", "iuu") - -/* Holds a label that is followed by instructions. - Operand: - 3: is a number that is unique in the entire compilation. - 4: is used in jump.c for the use-count of the label. - and in flow.c to point to the chain of label_ref's to this label. */ -DEF_RTL_EXPR(CODE_LABEL, "code_label", "iuui0") - -/* Say where in the code a source line starts, for symbol table's sake. - Contains a filename and a line number. Line numbers <= 0 are special: - 0 is used in a dummy placed at the front of every function - just so there will never be a need to delete the first insn; - -1 indicates a dummy; insns to be deleted by flow analysis and combining - are really changed to NOTEs with a number of -1. - -2 means beginning of a name binding contour; output N_LBRAC. - -3 means end of a contour; output N_RBRAC. */ -DEF_RTL_EXPR(NOTE, "note", "iuusn") - -/* INLINE_HEADER is use by inline function machinery. The information - it contains helps to build the mapping function between the rtx's of - the function to be inlined and the current function being expanded. */ - -DEF_RTL_EXPR(INLINE_HEADER, "inline_header", "iuuiiiiiiii") - -/* ---------------------------------------------------------------------- - Top level constituents of INSN, JUMP_INSN and CALL_INSN. - ---------------------------------------------------------------------- */ - -/* Several operations to be done in parallel. */ -DEF_RTL_EXPR(PARALLEL, "parallel", "E") - -/* A string that is passed through to the assembler as input. - One can obviously pass comments through by using the - assembler comment syntax. - These occur in an insn all by themselves as the PATTERN. - They also appear inside an ASM_OPERANDS - as a convenient way to hold a string. */ -DEF_RTL_EXPR(ASM_INPUT, "asm_input", "s") - -/* An assembler instruction with operands. - 1st operand is the instruction template. - 2nd operand is the constraint for the output. - 3rd operand is the number of the output this expression refers to. - When an insn stores more than one value, a separate ASM_OPERANDS - is made for each output; this integer distinguishes them. - 4th is a vector of values of input operands. - 5th is a vector of modes and constraints for the input operands. - Each element is an ASM_INPUT containing a constraint string - and whose mode indicates the mode of the input operand. - 6th is the name of the containing source file. - 7th is the source line number. */ -DEF_RTL_EXPR(ASM_OPERANDS, "asm_operands", "ssiEEsi") - -/* Vector of addresses, stored as full words. */ -/* Each element is a LABEL_REF to a CODE_LABEL whose address we want. */ -DEF_RTL_EXPR(ADDR_VEC, "addr_vec", "E") - -/* Vector of address differences X0 - BASE, X1 - BASE, ... - First operand is BASE; the vector contains the X's. - The machine mode of this rtx says how much space to leave - for each difference. */ -DEF_RTL_EXPR(ADDR_DIFF_VEC, "addr_diff_vec", "eE") - -/* ---------------------------------------------------------------------- - At the top level of an instruction (perhaps under PARALLEL). - ---------------------------------------------------------------------- */ - -/* Assignment. - Operand 1 is the location (REG, MEM, PC, CC0 or whatever) assigned to. - Operand 2 is the value stored there. - ALL assignment must use SET. - Instructions that do multiple assignments must use multiple SET, - under PARALLEL. */ -DEF_RTL_EXPR(SET, "set", "ee") - -/* Indicate something is used in a way that we don't want to explain. - For example, subroutine calls will use the register - in which the static chain is passed. */ -DEF_RTL_EXPR(USE, "use", "e") - -/* Indicate something is clobbered in a way that we don't want to explain. - For example, subroutine calls will clobber some physical registers - (the ones that are by convention not saved). */ -DEF_RTL_EXPR(CLOBBER, "clobber", "e") - -/* Call a subroutine. - Operand 1 is the address to call. - Operand 2 is the number of arguments. */ - -DEF_RTL_EXPR(CALL, "call", "ee") - -/* Return from a subroutine. */ - -DEF_RTL_EXPR(RETURN, "return", "") - -/* ---------------------------------------------------------------------- - Primitive values for use in expressions. - ---------------------------------------------------------------------- */ - -/* numeric integer constant */ -DEF_RTL_EXPR(CONST_INT, "const_int", "i") - -/* numeric double constant. - Operand 0 is the MEM that stores this constant in memory, - or various other things (see comments at immed_double_const in varasm.c). - Operand 1 is a chain of all CONST_DOUBLEs in use in the current function. - Remaining operands hold the actual value. - The number of operands may be more than 2 if cross-compiling; - see init_rtl. */ -DEF_RTL_EXPR(CONST_DOUBLE, "const_double", "e0ii") - -/* This is used to encapsulate an expression whose value is constant - (such as the sum of a SYMBOL_REF and a CONST_INT) so that it will be - recognized as a constant operand rather than by arithmetic instructions. */ - -DEF_RTL_EXPR(CONST, "const", "e") - -/* program counter. Ordinary jumps are represented - by a SET whose first operand is (PC). */ -DEF_RTL_EXPR(PC, "pc", "") - -/* A register. The "operand" is the register number, accessed - with the REGNO macro. If this number is less than FIRST_PSEUDO_REGISTER - than a hardware register is being referred to. */ -DEF_RTL_EXPR(REG, "reg", "i") - -/* One word of a multi-word value. - The first operand is the complete value; the second says which word. - The WORDS_BIG_ENDIAN flag controls whether word number 0 - (as numbered in a SUBREG) is the most or least significant word. - - This is also used to refer to a value in a different machine mode. - For example, it can be used to refer to a SImode value as if it were - Qimode, or vice versa. Then the word number is always 0. */ -DEF_RTL_EXPR(SUBREG, "subreg", "ei") - -/* This one-argument rtx is used for move instructions - that are guaranteed to alter only the low part of a destination. - Thus, (SET (SUBREG:HI (REG...)) (MEM:HI ...)) - has an unspecified effect on the high part of REG, - but (SET (STRICT_LOW_PART (SUBREG:HI (REG...))) (MEM:HI ...)) - is guaranteed to alter only the bits of REG that are in HImode. - - The actual instruction used is probably the same in both cases, - but the register constraints may be tighter when STRICT_LOW_PART - is in use. */ - -DEF_RTL_EXPR(STRICT_LOW_PART, "strict_low_part", "e") - -/* A memory location; operand is the address. - Can be nested inside a VOLATILE. */ -DEF_RTL_EXPR(MEM, "mem", "e") - -/* Reference to an assembler label in the code for this function. - The operand is a CODE_LABEL found in the insn chain. - The unprinted fields 1 and 2 are used in flow.c for the - LABEL_NEXTREF and CONTAINING_INSN. */ -DEF_RTL_EXPR(LABEL_REF, "label_ref", "u00") - -/* Reference to a named label: the string that is the first operand, - with `_' added implicitly in front. - Exception: if the first character explicitly given is `*', - to give it to the assembler, remove the `*' and do not add `_'. */ -DEF_RTL_EXPR(SYMBOL_REF, "symbol_ref", "s") - -/* The condition code register is represented, in our imagination, - as a register holding a value that can be compared to zero. - In fact, the machine has already compared them and recorded the - results; but instructions that look at the condition code - pretend to be looking at the entire value and comparing it. */ -DEF_RTL_EXPR(CC0, "cc0", "") - -/* ===================================================================== - A QUEUED expression really points to a member of the queue of instructions - to be output later for postincrement/postdecrement. - QUEUED expressions never become part of instructions. - When a QUEUED expression would be put into an instruction, - instead either the incremented variable or a copy of its previous - value is used. - - Operands are: - 0. the variable to be incremented (a REG rtx). - 1. the incrementing instruction, or 0 if it hasn't been output yet. - 2. A REG rtx for a copy of the old value of the variable, or 0 if none yet. - 3. the body to use for the incrementing instruction - 4. the next QUEUED expression in the queue. - ====================================================================== */ - -DEF_RTL_EXPR(QUEUED, "queued", "eeeee") - -/* ---------------------------------------------------------------------- - Expressions for operators in an rtl pattern - ---------------------------------------------------------------------- */ - -/* if_then_else. This is used in representing ordinary - conditional jump instructions. - Operand: - 0: condition - 1: then expr - 2: else expr */ -DEF_RTL_EXPR(IF_THEN_ELSE, "if_then_else", "eee") - -/* Comparison, produces a condition code result. */ -DEF_RTL_EXPR(COMPARE, "compare", "ee") - -/* plus */ -DEF_RTL_EXPR(PLUS, "plus", "ee") - -/* Operand 0 minus operand 1. */ -DEF_RTL_EXPR(MINUS, "minus", "ee") - -/* Minus operand 0. */ -DEF_RTL_EXPR(NEG, "neg", "e") - -DEF_RTL_EXPR(MULT, "mult", "ee") - -/* Operand 0 divided by operand 1. */ -DEF_RTL_EXPR(DIV, "div", "ee") -/* Remainder of operand 0 divided by operand 1. */ -DEF_RTL_EXPR(MOD, "mod", "ee") - -/* Unsigned multiply and divide. */ - -DEF_RTL_EXPR(UMULT, "umult", "ee") -DEF_RTL_EXPR(UDIV, "udiv", "ee") -DEF_RTL_EXPR(UMOD, "umod", "ee") - -/* Bitwise operations. */ -DEF_RTL_EXPR(AND, "and", "ee") - -DEF_RTL_EXPR(IOR, "ior", "ee") - -DEF_RTL_EXPR(XOR, "xor", "ee") - -DEF_RTL_EXPR(NOT, "not", "e") - -/* Operand: - 0: value to be shifted. - 1: number of bits. - ASHIFT and LSHIFT are distinguished because on some machines - these allow a negative operand and shift right in that case. */ -DEF_RTL_EXPR(LSHIFT, "lshift", "ee") -DEF_RTL_EXPR(ASHIFT, "ashift", "ee") -DEF_RTL_EXPR(ROTATE, "rotate", "ee") - -/* Right shift operations, for machines where these are not the same - as left shifting with a negative argument. */ - -DEF_RTL_EXPR(ASHIFTRT, "ashiftrt", "ee") -DEF_RTL_EXPR(LSHIFTRT, "lshiftrt", "ee") -DEF_RTL_EXPR(ROTATERT, "rotatert", "ee") - -/* These unary operations are used to represent incrementation - and decrementation as they occur in memory addresses. - The amount of increment or decrement are not represented - because they can be understood from the machine-mode of the - containing MEM. These operations exist in only two cases: - 1. pushes onto the stack. - 2. created automatically by the life_analysis pass in flow.c. */ -DEF_RTL_EXPR(PRE_DEC, "pre_dec", "e") -DEF_RTL_EXPR(PRE_INC, "pre_inc", "e") -DEF_RTL_EXPR(POST_DEC, "post_dec", "e") -DEF_RTL_EXPR(POST_INC, "post_inc", "e") - -/* Comparison operations. The ordered comparisons exist in two - flavors, signed and unsigned. */ -DEF_RTL_EXPR(NE, "ne", "ee") -DEF_RTL_EXPR(EQ, "eq", "ee") -DEF_RTL_EXPR(GE, "ge", "ee") -DEF_RTL_EXPR(GT, "gt", "ee") -DEF_RTL_EXPR(LE, "le", "ee") -DEF_RTL_EXPR(LT, "lt", "ee") -DEF_RTL_EXPR(GEU, "geu", "ee") -DEF_RTL_EXPR(GTU, "gtu", "ee") -DEF_RTL_EXPR(LEU, "leu", "ee") -DEF_RTL_EXPR(LTU, "ltu", "ee") - -/* Represents the result of sign-extending the sole operand. - The machine modes of the operand and of the SIGN_EXTEND expression - determine how much sign-extension is going on. */ -DEF_RTL_EXPR(SIGN_EXTEND, "sign_extend", "e") - -/* Similar for zero-extension (such as unsigned short to int). */ -DEF_RTL_EXPR(ZERO_EXTEND, "zero_extend", "e") - -/* Similar but here the operand has a wider mode. */ -DEF_RTL_EXPR(TRUNCATE, "truncate", "e") - -/* Similar for extending floating-point values (such as SFmode to DFmode). */ -DEF_RTL_EXPR(FLOAT_EXTEND, "float_extend", "e") -DEF_RTL_EXPR(FLOAT_TRUNCATE, "float_truncate", "e") - -/* Conversion of fixed point operand to floating point value. */ -DEF_RTL_EXPR(FLOAT, "float", "e") - -/* With fixed-point machine mode: - Conversion of floating point operand to fixed point value. - Value is defined only when the operand's value is an integer. - With floating-point machine mode (and operand with same mode): - Operand is rounded toward zero to produce an integer value - represented in floating point. */ -DEF_RTL_EXPR(FIX, "fix", "e") - -/* Conversion of unsigned fixed point operand to floating point value. */ -DEF_RTL_EXPR(UNSIGNED_FLOAT, "unsigned_float", "e") - -/* With fixed-point machine mode: - Conversion of floating point operand to *unsigned* fixed point value. - Value is defined only when the operand's value is an integer. */ -DEF_RTL_EXPR(UNSIGNED_FIX, "unsigned_fix", "e") - -/* Absolute value */ -DEF_RTL_EXPR(ABS, "abs", "e") - -/* Square root */ -DEF_RTL_EXPR(SQRT, "sqrt", "e") - -/* Find first bit that is set. - Value is 1 + number of trailing zeros in the arg., - or 0 if arg is 0. */ -DEF_RTL_EXPR(FFS, "ffs", "e") - -/* Reference to a signed bit-field of specified size and position. - Operand 0 is the memory unit (usually SImode or QImode) which - contains the field's first bit. Operand 1 is the width, in bits. - Operand 2 is the number of bits in the memory unit before the - first bit of this field. - If BITS_BIG_ENDIAN is defined, the first bit is the msb and - operand 2 counts from the msb of the memory unit. - Otherwise, the first bit is the lsb and operand 2 counts from - the lsb of the memory unit. */ -DEF_RTL_EXPR(SIGN_EXTRACT, "sign_extract", "eee") - -/* Similar for unsigned bit-field. */ -DEF_RTL_EXPR(ZERO_EXTRACT, "zero_extract", "eee") - -/* -Local variables: -mode:c -version-control: t -End: -*/ diff --git a/gnu/usr.bin/gcc1/cc1/rtl.h b/gnu/usr.bin/gcc1/cc1/rtl.h deleted file mode 100644 index b9d4647b73..0000000000 --- a/gnu/usr.bin/gcc1/cc1/rtl.h +++ /dev/null @@ -1,544 +0,0 @@ -/* Register Transfer Language (RTL) definitions for GNU C-Compiler - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#undef FFS /* Some systems predefine this symbol; don't let it interfere. */ - -/* Register Transfer Language EXPRESSIONS CODES */ - -#define RTX_CODE enum rtx_code -enum rtx_code { - -#define DEF_RTL_EXPR(ENUM, NAME, FORMAT) ENUM , -#include "rtl.def" /* rtl expressions are documented here */ -#undef DEF_RTL_EXPR - - LAST_AND_UNUSED_RTX_CODE}; /* A convienent way to get a value for - NUM_RTX_CODE. - Assumes default enum value assignement. */ - -#define NUM_RTX_CODE ((int)LAST_AND_UNUSED_RTX_CODE) - /* The cast here, saves many elsewhere. */ - -extern int rtx_length[]; -#define GET_RTX_LENGTH(CODE) (rtx_length[(int)(CODE)]) - -extern char *rtx_name[]; -#define GET_RTX_NAME(CODE) (rtx_name[(int)(CODE)]) - -extern char *rtx_format[]; -#define GET_RTX_FORMAT(CODE) (rtx_format[(int)(CODE)]) - - -/* Get the definition of `enum machine_mode' */ - -#ifndef HAVE_MACHINE_MODES - -#define DEF_MACHMODE(SYM, NAME, TYPE, SIZE, UNIT, WIDER) SYM, - -enum machine_mode { -#include "machmode.def" -MAX_MACHINE_MODE }; - -#undef DEF_MACHMODE - -#define HAVE_MACHINE_MODES - -#endif /* not HAVE_MACHINE_MODES */ - -#ifndef NUM_MACHINE_MODES -#define NUM_MACHINE_MODES (int) MAX_MACHINE_MODE -#endif - -/* Get the name of mode MODE as a string. */ - -extern char *mode_name[]; -#define GET_MODE_NAME(MODE) (mode_name[(int)(MODE)]) - -enum mode_class { MODE_RANDOM, MODE_INT, MODE_FLOAT, - MODE_COMPLEX_INT, MODE_COMPLEX_FLOAT, MODE_FUNCTION }; - -/* Get the general kind of object that mode MODE represents - (integer, floating, complex, etc.) */ - -extern enum mode_class mode_class[]; -#define GET_MODE_CLASS(MODE) (mode_class[(int)(MODE)]) - -/* Get the size in bytes of an object of mode MODE. */ - -extern int mode_size[]; -#define GET_MODE_SIZE(MODE) (mode_size[(int)(MODE)]) - -/* Get the size in bytes of the basic parts of an object of mode MODE. */ - -extern int mode_unit_size[]; -#define GET_MODE_UNIT_SIZE(MODE) (mode_unit_size[(int)(MODE)]) - -/* Get the size in bits of an object of mode MODE. */ - -#define GET_MODE_BITSIZE(MODE) (BITS_PER_UNIT * mode_size[(int)(MODE)]) - -/* Get a bitmask containing 1 for all bits in a word - that fit within mode MODE. */ - -#define GET_MODE_MASK(MODE) \ - ((GET_MODE_BITSIZE (MODE) >= HOST_BITS_PER_INT) \ - ? -1 : ((1 << GET_MODE_BITSIZE (MODE)) - 1)) - -/* Get the next wider natural mode (eg, QI -> HI -> SI -> DI -> TI). */ - -extern enum machine_mode mode_wider_mode[]; -#define GET_MODE_WIDER_MODE(MODE) (mode_wider_mode[(int)(MODE)]) - -/* Common union for an element of an rtx. */ - -typedef union rtunion_def -{ - int rtint; - char *rtstr; - struct rtx_def *rtx; - struct rtvec_def *rtvec; - enum machine_mode rttype; -} rtunion; - -/* RTL expression ("rtx"). */ - -typedef struct rtx_def -{ -#ifdef SHORT_ENUM_BUG - unsigned short code; -#else - /* The kind of expression this is. */ - enum rtx_code code : 16; -#endif - /* The kind of value the expression has. */ - enum machine_mode mode : 8; - /* 1 in an INSN if it can alter flow of control - within this function. Not yet used! */ - unsigned int jump : 1; - /* 1 in an INSN if it can call another function. Not yet used! */ - unsigned int call : 1; - /* 1 in a MEM or REG if value of this expression will never change - during the current function, even though it is not - manifestly constant. - 1 in a SYMBOL_REF if it addresses something in the per-function - constants pool. */ - unsigned int unchanging : 1; - /* 1 in a MEM expression if contents of memory are volatile. */ - /* 1 in an INSN, CALL_INSN, JUMP_INSN, CODE_LABEL or BARRIER - if it is deleted. */ - /* 1 in a REG expression if corresponds to a variable declared by the user. - 0 for an internally generated temporary. */ - unsigned int volatil : 1; - /* 1 in a MEM referring to a field of a structure (not a union!). - 0 if the MEM was a variable or the result of a * operator in C; - 1 if it was the result of a . or -> operator (on a struct) in C. */ - unsigned int in_struct : 1; - /* 1 if this rtx is used. This is used for copying shared structure. - See `unshare_all_rtl'. - This bit is used to detect that event. */ - unsigned int used : 1; - /* Nonzero if this rtx came from procedure integration. - In a REG, nonzero means this reg refers to the return value - of the current function. */ - unsigned integrated : 1; - /* The first element of the operands of this rtx. - The number of operands and their types are controlled - by the `code' field, according to rtl.def. */ - rtunion fld[1]; -} *rtx; - -#define NULL_RTX (rtx) 0 - -/* Define macros to access the `code' field of the rtx. */ - -#ifdef SHORT_ENUM_BUG -#define GET_CODE(RTX) ((enum rtx_code) ((RTX)->code)) -#define PUT_CODE(RTX, CODE) ((RTX)->code = ((short) (CODE))) -#else -#define GET_CODE(RTX) ((RTX)->code) -#define PUT_CODE(RTX, CODE) ((RTX)->code = (CODE)) -#endif - -#define GET_MODE(RTX) ((RTX)->mode) -#define PUT_MODE(RTX, MODE) ((RTX)->mode = (MODE)) - -#define RTX_INTEGRATED_P(RTX) ((RTX)->integrated) -#define RTX_UNCHANGING_P(RTX) ((RTX)->unchanging) - -/* RTL vector. These appear inside RTX's when there is a need - for a variable number of things. The principle use is inside - PARALLEL expressions. */ - -typedef struct rtvec_def{ - unsigned num_elem; /* number of elements */ - rtunion elem[1]; -} *rtvec; - -#define NULL_RTVEC (rtvec) 0 - -#define GET_NUM_ELEM(RTVEC) ((RTVEC)->num_elem) -#define PUT_NUM_ELEM(RTVEC, NUM) ((RTVEC)->num_elem = (unsigned) NUM) - -/* 1 if X is a REG. */ - -#define REG_P(X) (GET_CODE (X) == REG) - -/* 1 if X is a constant value that is an integer. */ - -#define CONSTANT_P(X) \ - (GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF \ - || GET_CODE (X) == CONST_INT \ - || GET_CODE (X) == CONST) - -/* General accessor macros for accessing the fields of an rtx. */ - -#define XEXP(RTX, N) ((RTX)->fld[N].rtx) -#define XINT(RTX, N) ((RTX)->fld[N].rtint) -#define XSTR(RTX, N) ((RTX)->fld[N].rtstr) -#define XVEC(RTX, N) ((RTX)->fld[N].rtvec) -#define XVECLEN(RTX, N) ((RTX)->fld[N].rtvec->num_elem) -#define XVECEXP(RTX,N,M)((RTX)->fld[N].rtvec->elem[M].rtx) - -/* ACCESS MACROS for particular fields of insns. */ - -/* Holds a unique number for each insn. - These are not necessarily sequentially increasing. */ -#define INSN_UID(INSN) ((INSN)->fld[0].rtint) - -/* Chain insns together in sequence. */ -#define PREV_INSN(INSN) ((INSN)->fld[1].rtx) -#define NEXT_INSN(INSN) ((INSN)->fld[2].rtx) - -/* The body of an insn. */ -#define PATTERN(INSN) ((INSN)->fld[3].rtx) - -/* Code number of instruction, from when it was recognized. - -1 means this instruction has not been recognized yet. */ -#define INSN_CODE(INSN) ((INSN)->fld[4].rtint) - -/* Set up in flow.c; empty before then. - Holds a chain of INSN_LIST rtx's whose first operands point at - previous insns with direct data-flow connections to this one. - That means that those insns set variables whose next use is in this insn. - They are always in the same basic block as this insn. */ -#define LOG_LINKS(INSN) ((INSN)->fld[5].rtx) - -/* 1 if insn has been deleted. */ -#define INSN_DELETED_P(INSN) ((INSN)->volatil) - -/* Holds a list of notes on what this insn does to various REGs. - It is a chain of EXPR_LIST rtx's, where the second operand - is the chain pointer and the first operand is the REG being described. - The mode field of the EXPR_LIST contains not a real machine mode - but a value that says what this note says about the REG: - REG_DEAD means that the REG dies in this insn. - REG_INC means that the REG is autoincremented or autodecremented. - Note that one insn can have both REG_DEAD and REG_INC for the same register - if the register is preincremented or predecremented in the insn - and not needed afterward. This can probably happen. - REG_EQUIV describes the insn as a whole; it says that the - insn sets a register to a constant value or to be equivalent to - a memory address. If the - register is spilled to the stack then the constant value - should be substituted for it. The contents of the REG_EQUIV - is the constant value or memory address, which may be different - from the source of the SET although it has the same value. - REG_EQUAL is like REG_EQUIV except that the destination - is only momentarily equal to the specified rtx. Therefore, it - cannot be used for substitution; but it can be used for cse. - REG_RETVAL means that this insn copies the return-value of - a library call out of the hard reg for return values. This note - is actually an INSN_LIST and it points to the first insn involved - in setting up arguments for the call. flow.c uses this to delete - the entire library call when its result is dead. - REG_LIBCALL is the inverse of REG_RETVAL: it goes on the first insn - of the library call and points at the one that has the REG_RETVAL. - REG_WAS_0 says that the register set in this insn held 0 before the insn. - The contents of the note is the insn that stored the 0. - If that insn is deleted or patched to a NOTE, the REG_WAS_0 is inoperative. - The REG_WAS_0 note is actually an INSN_LIST, not an EXPR_LIST. - REG_NONNEG means that the register is always nonnegative during - the containing loop. This is used in branches so that decrement and - branch instructions terminating on zero can be matched. - REG_UNSET identifies a pseudo-reg used in this insn and never set. */ - -#define REG_NOTES(INSN) ((INSN)->fld[6].rtx) - -/* Don't forget to change reg_note_name in rtl.c. */ -enum reg_note { REG_DEAD = 1, REG_INC = 2, REG_EQUIV = 3, REG_WAS_0 = 4, - REG_EQUAL = 5, REG_RETVAL = 6, REG_LIBCALL = 7, - REG_NONNEG = 8, REG_UNSET = 9 }; - -/* Extract the reg-note kind from an EXPR_LIST. */ -#define REG_NOTE_KIND(LINK) ((enum reg_note) GET_MODE (LINK)) - -/* Names for REG_NOTE's in EXPR_LIST insn's. */ - -extern char *reg_note_name[]; -#define GET_REG_NOTE_NAME(MODE) (reg_note_name[(int)(MODE)]) - -/* The label-number of a code-label. The assembler label - is made from `L' and the label-number printed in decimal. - Label numbers are unique in a compilation. */ -#define CODE_LABEL_NUMBER(INSN) ((INSN)->fld[3].rtint) - -#define LINE_NUMBER NOTE - -/* In a NOTE that is a line number, this is a string for the file name - that the line is in. */ - -#define NOTE_SOURCE_FILE(INSN) ((INSN)->fld[3].rtstr) - -/* In a NOTE that is a line number, this is the line number. - Other kinds of NOTEs are identified by negative numbers here. */ -#define NOTE_LINE_NUMBER(INSN) ((INSN)->fld[4].rtint) - -/* Codes that appear in the NOTE_LINE_NUMBER field - for kinds of notes that are not line numbers. */ - -/* This note indicates the end of the real body of the function, - after moving the parms into their homes, etc. */ -#define NOTE_INSN_FUNCTION_BEG 0 - -/* This note is used to get rid of an insn - when it isn't safe to patch the insn out of the chain. */ -#define NOTE_INSN_DELETED -1 -#define NOTE_INSN_BLOCK_BEG -2 -#define NOTE_INSN_BLOCK_END -3 -#define NOTE_INSN_LOOP_BEG -4 -#define NOTE_INSN_LOOP_END -5 -/* This kind of note is generated at the end of the function body, - just before the return insn or return label. - In an optimizing compilation it is deleted by the first jump optimization, - after enabling that optimizer to determine whether control can fall - off the end of the function body without a return statement. */ -#define NOTE_INSN_FUNCTION_END -6 -/* This kind of note is generated just after each call to `setjmp', et al. */ -#define NOTE_INSN_SETJMP -7 -/* Generated at the place in a loop that `continue' jumps to. */ -#define NOTE_INSN_LOOP_CONT -8 -/* Don't forget to change note_insn_name in rtl.c. */ - -#define NOTE_DECL_NAME(INSN) ((INSN)->fld[3].rtstr) -#define NOTE_DECL_CODE(INSN) ((INSN)->fld[4].rtint) -#define NOTE_DECL_RTL(INSN) ((INSN)->fld[5].rtx) -#define NOTE_DECL_IDENTIFIER(INSN) ((INSN)->fld[6].rtint) -#define NOTE_DECL_TYPE(INSN) ((INSN)->fld[7].rtint) - -/* Names for NOTE insn's other than line numbers. */ - -extern char *note_insn_name[]; -#define GET_NOTE_INSN_NAME(NOTE_CODE) (note_insn_name[-(NOTE_CODE)]) - -/* In jump.c, each label contains a count of the number - of LABEL_REFs that point at it, so unused labels can be deleted. */ -#define LABEL_NUSES(LABEL) ((LABEL)->fld[4].rtint) - -/* In jump.c, each JUMP_INSN can point to a label that it can jump to, - so that if the JUMP_INSN is deleted, the label's LABEL_NUSES can - be decremented and possibly the label can be deleted. */ -#define JUMP_LABEL(INSN) ((INSN)->fld[7].rtx) - -/* Once basic blocks are found in flow.c, - each CODE_LABEL starts a chain that goes through - all the LABEL_REFs that jump to that label. - The chain eventually winds up at the CODE_LABEL; it is circular. */ -#define LABEL_REFS(LABEL) ((LABEL)->fld[4].rtx) - -/* This is the field in the LABEL_REF through which the circular chain - of references to a particular label is linked. - This chain is set up in flow.c. */ - -#define LABEL_NEXTREF(REF) ((REF)->fld[1].rtx) - -/* Once basic blocks are found in flow.c, - Each LABEL_REF points to its containing instruction with this field. */ - -#define CONTAINING_INSN(RTX) ((RTX)->fld[2].rtx) - -/* For a REG rtx, REGNO extracts the register number. */ - -#define REGNO(RTX) ((RTX)->fld[0].rtint) - -/* For a REG rtx, REG_FUNCTION_VALUE_P is nonzero if the reg - is the current function's return value. */ - -#define REG_FUNCTION_VALUE_P(RTX) ((RTX)->integrated) - -/* 1 in a REG rtx if it corresponds to a variable declared by the user. */ -#define REG_USERVAR_P(RTX) ((RTX)->volatil) - -/* For a CONST_INT rtx, INTVAL extracts the integer. */ - -#define INTVAL(RTX) ((RTX)->fld[0].rtint) - -/* For a SUBREG rtx, SUBREG_REG extracts the value we want a subreg of. - SUBREG_WORD extracts the word-number. */ - -#define SUBREG_REG(RTX) ((RTX)->fld[0].rtx) -#define SUBREG_WORD(RTX) ((RTX)->fld[1].rtint) - -/* Access various components of an ASM_OPERANDS rtx. */ - -#define ASM_OPERANDS_TEMPLATE(RTX) XSTR ((RTX), 0) -#define ASM_OPERANDS_OUTPUT_CONSTRAINT(RTX) XSTR ((RTX), 1) -#define ASM_OPERANDS_OUTPUT_IDX(RTX) XINT ((RTX), 2) -#define ASM_OPERANDS_INPUT_VEC(RTX) XVEC ((RTX), 3) -#define ASM_OPERANDS_INPUT_CONSTRAINT_VEC(RTX) XVEC ((RTX), 4) -#define ASM_OPERANDS_INPUT(RTX, N) XVECEXP ((RTX), 3, (N)) -#define ASM_OPERANDS_INPUT_CONSTRAINT(RTX, N) XSTR (XVECEXP ((RTX), 4, (N)), 0) -#define ASM_OPERANDS_INPUT_MODE(RTX, N) GET_MODE (XVECEXP ((RTX), 4, (N))) -#define ASM_OPERANDS_SOURCE_FILE(RTX) XSTR ((RTX), 5) -#define ASM_OPERANDS_SOURCE_LINE(RTX) XINT ((RTX), 6) - -/* For a MEM rtx, 1 if it's a volatile reference. - Also in an ASM_OPERANDS rtx. */ -#define MEM_VOLATILE_P(RTX) ((RTX)->volatil) - -/* For a MEM rtx, 1 if it refers to a structure or union component. */ -#define MEM_IN_STRUCT_P(RTX) ((RTX)->in_struct) - -/* For a SET rtx, SET_DEST is the place that is set - and SET_SRC is the value it is set to. */ -#define SET_DEST(RTX) ((RTX)->fld[0].rtx) -#define SET_SRC(RTX) ((RTX)->fld[1].rtx) - -/* 1 in a SYMBOL_REF if it addresses this function's constants pool. */ -#define CONSTANT_POOL_ADDRESS_P(RTX) ((RTX)->unchanging) -/* 1 in a SYMBOL_REF if it is the name of an external symbol. */ -#define EXTERNAL_SYMBOL_P(RTX) ((RTX)->volatil) - -/* For an INLINE_HEADER rtx, FIRST_FUNCTION_INSN is the first insn - of the function that is not involved in copying parameters to - pseudo-registers. FIRST_PARM_INSN is the very first insn of - the function, including the parameter copying. - We keep this around in case we must splice - this function into the assembly code at the end of the file. - FIRST_LABELNO is the first label number used by the function (inclusive). - LAST_LABELNO is the last label used by the function (exclusive). - MAX_REGNUM is the largest pseudo-register used by that function. - - We want this to lay down like an INSN. The PREV_INSN field - is always NULL. The NEXT_INSN field always points to the - first function insn of the function being squirreled away. */ - -#define FIRST_FUNCTION_INSN(RTX) ((RTX)->fld[2].rtx) -#define FIRST_PARM_INSN(RTX) ((RTX)->fld[3].rtx) -#define FIRST_LABELNO(RTX) ((RTX)->fld[4].rtint) -#define LAST_LABELNO(RTX) ((RTX)->fld[5].rtint) -#define MAX_PARMREG(RTX) ((RTX)->fld[6].rtint) -#define MAX_REGNUM(RTX) ((RTX)->fld[7].rtint) -#define FUNCTION_ARGS_SIZE(RTX) ((RTX)->fld[8].rtint) - -/* Generally useful functions. */ - -extern rtx rtx_alloc (); -extern rtvec rtvec_alloc (); -extern rtx find_reg_note (); -extern rtx gen_rtx (); -extern rtx copy_rtx (); -extern rtvec gen_rtvec (); -extern rtvec gen_rtvec_v (); -extern rtx gen_reg_rtx (); -extern rtx gen_label_rtx (); -extern rtx gen_inline_header_rtx (); -extern rtx gen_lowpart (); -extern rtx gen_highpart (); -extern int subreg_lowpart_p (); -extern rtx make_safe_from (); -extern rtx memory_address (); -extern rtx get_insns (); -extern rtx get_last_insn (); -extern rtx start_sequence (); -extern rtx gen_sequence (); -extern rtx expand_expr (); -extern rtx output_constant_def (); -extern rtx immed_real_const (); -extern rtx immed_real_const_1 (); -extern rtx immed_double_const (); -extern rtx force_const_double_mem (); -extern rtx force_const_mem (); -extern rtx get_parm_real_loc (); -extern rtx assign_stack_local (); -extern rtx protect_from_queue (); -extern void emit_queue (); -extern rtx emit_move_insn (); -extern rtx emit_insn (); -extern rtx emit_jump_insn (); -extern rtx emit_call_insn (); -extern rtx emit_call_insn_before (); -extern rtx emit_insn_before (); -extern rtx emit_insn_after (); -extern rtx emit_label (); -extern rtx emit_barrier (); -extern rtx emit_barrier_after (); -extern rtx emit_note (); -extern rtx emit_line_note (); -extern rtx emit_line_note_force (); -extern rtx prev_real_insn (); -extern rtx next_real_insn (); -extern rtx next_nondeleted_insn (); -extern rtx plus_constant (); -extern rtx find_equiv_reg (); -extern rtx delete_insn (); -extern rtx adj_offsettable_operand (); - -/* Maximum number of parallel sets and clobbers in any insn in this fn. - Always at least 3, since the combiner could put that many togetherm - and we want this to remain correct for all the remaining passes. */ - -extern int max_parallel; - -extern int asm_noperands (); -extern char *decode_asm_operands (); - -#ifdef BITS_PER_WORD -/* Conditional is to detect when config.h has been included. */ -extern enum reg_class reg_preferred_class (); -#endif - -extern rtx get_first_nonparm_insn (); - -/* Standard pieces of rtx, to be substituted directly into things. */ -extern rtx pc_rtx; -extern rtx cc0_rtx; -extern rtx const0_rtx; -extern rtx const1_rtx; -extern rtx fconst0_rtx; -extern rtx dconst0_rtx; - -/* Returns a constant 0 rtx in mode MODE. */ - -#define CONST0_RTX(MODE) \ - ((MODE == SFmode) ? fconst0_rtx \ - : ((MODE == DFmode) ? dconst0_rtx \ - : ((GET_MODE_CLASS (MODE) == MODE_INT) ? const0_rtx \ - : (abort (), NULL_RTX)))) - -/* All references to certain hard regs, except those created - by allocating pseudo regs into them (when that's possible), - go through these unique rtx objects. */ -extern rtx stack_pointer_rtx; -extern rtx frame_pointer_rtx; -extern rtx arg_pointer_rtx; -extern rtx struct_value_rtx; -extern rtx struct_value_incoming_rtx; -extern rtx static_chain_rtx; -extern rtx static_chain_incoming_rtx; diff --git a/gnu/usr.bin/gcc1/cc1/rtlanal.c b/gnu/usr.bin/gcc1/cc1/rtlanal.c deleted file mode 100644 index 17ee75e5e5..0000000000 --- a/gnu/usr.bin/gcc1/cc1/rtlanal.c +++ /dev/null @@ -1,681 +0,0 @@ -/* Analyze RTL for C-Compiler - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "config.h" -#include "rtl.h" - -extern void note_stores (); -static int reg_set_p (); - -/* Return 1 if the value of X is unstable - (would be different at a different point in the program). - The frame pointer, arg pointer, etc. are considered stable - (within one function) and so is anything marked `unchanging'. */ - -int -rtx_unstable_p (x) - rtx x; -{ - register RTX_CODE code = GET_CODE (x); - register int i; - register char *fmt; - - if (code == MEM) - return ! RTX_UNCHANGING_P (x); - - if (code == QUEUED) - return 1; - - if (code == CONST || code == CONST_INT) - return 0; - - if (code == REG) - return ! (REGNO (x) == FRAME_POINTER_REGNUM - || REGNO (x) == ARG_POINTER_REGNUM - || RTX_UNCHANGING_P (x)); - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - if (rtx_unstable_p (XEXP (x, i))) - return 1; - return 0; -} - -/* Return 1 if X has a value that can vary even between two - executions of the program. 0 means X can be compared reliably - against certain constants or near-constants. - The frame pointer and the arg pointer are considered constant. */ - -int -rtx_varies_p (x) - rtx x; -{ - register RTX_CODE code = GET_CODE (x); - register int i; - register char *fmt; - - if (code == MEM) - return 1; - - if (code == QUEUED) - return 1; - - if (code == CONST || code == CONST_INT) - return 0; - - if (code == REG) - return ! (REGNO (x) == FRAME_POINTER_REGNUM - || REGNO (x) == ARG_POINTER_REGNUM); - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - if (rtx_varies_p (XEXP (x, i))) - return 1; - return 0; -} - -/* Return 1 if X refers to a memory location whose address - cannot be compared reliably with constant addresses, - or if X refers to a BLKmode memory object. */ - -int -rtx_addr_varies_p (x) - rtx x; -{ - register enum rtx_code code; - register int i; - register char *fmt; - - if (x == 0) - return 0; - - code = GET_CODE (x); - if (code == MEM) - return GET_MODE (x) == BLKmode || rtx_varies_p (XEXP (x, 0)); - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - if (rtx_addr_varies_p (XEXP (x, i))) - return 1; - return 0; -} - -/* Nonzero if register REG appears somewhere within IN. - Also works if REG is not a register; in this case it checks - for a subexpression of IN that is Lisp "equal" to REG. */ - -int -reg_mentioned_p (reg, in) - register rtx reg, in; -{ - register char *fmt; - register int i; - register enum rtx_code code; - - if (in == 0) - return 0; - - if (reg == in) - return 1; - - code = GET_CODE (in); - - switch (code) - { - /* Compare registers by number. */ - case REG: - return GET_CODE (reg) == REG && REGNO (in) == REGNO (reg); - - /* These codes have no constituent expressions - and are unique. */ - case CC0: - case PC: - return 0; - - case CONST_INT: - return GET_CODE (reg) == CONST_INT && INTVAL (in) == INTVAL (reg); - } - - if (GET_CODE (reg) == code && rtx_equal_p (reg, in)) - return 1; - - fmt = GET_RTX_FORMAT (code); - - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (in, i) - 1; j >= 0; j--) - if (reg_mentioned_p (reg, XVECEXP (in, i, j))) - return 1; - } - else if (fmt[i] == 'e' - && reg_mentioned_p (reg, XEXP (in, i))) - return 1; - } - return 0; -} - -/* Nonzero if register REG is used in an insn between - FROM_INSN and TO_INSN (exclusive of those two). */ - -int -reg_used_between_p (reg, from_insn, to_insn) - rtx reg, from_insn, to_insn; -{ - register rtx insn; - register RTX_CODE code; - for (insn = NEXT_INSN (from_insn); insn != to_insn; insn = NEXT_INSN (insn)) - if (((code = GET_CODE (insn)) == INSN - || code == JUMP_INSN || code == CALL_INSN) - && reg_mentioned_p (reg, PATTERN (insn))) - return 1; - return 0; -} - -/* Nonzero if register REG is set or clobbered in an insn between - FROM_INSN and TO_INSN (exclusive of those two). - Does not notice increments, only SET and CLOBBER. */ - -int -reg_set_between_p (reg, from_insn, to_insn) - rtx reg, from_insn, to_insn; -{ - register rtx insn; - register RTX_CODE code; - for (insn = NEXT_INSN (from_insn); insn != to_insn; insn = NEXT_INSN (insn)) - if (((code = GET_CODE (insn)) == INSN - || code == JUMP_INSN || code == CALL_INSN) - && reg_set_p (reg, PATTERN (insn))) - return 1; - return 0; -} - -/* Internals of reg_set_between_p. */ - -static rtx reg_set_reg; -static int reg_set_flag; - -static void -reg_set_p_1 (x) - rtx x; -{ - if (reg_overlap_mentioned_p (reg_set_reg, x)) - reg_set_flag = 1; -} - -static int -reg_set_p (reg, insn) - rtx reg, insn; -{ - reg_set_reg = reg; - reg_set_flag = 0; - note_stores (insn, reg_set_p_1); - return reg_set_flag; -} - -/* Return nonzero if hard register in range [REGNO, ENDREGNO) - appears either explicitly or implicitly in X - other than being stored into. - - References contained within the substructure at LOC do not count. - LOC may be zero, meaning don't ignore anything. */ - -int -refers_to_regno_p (regno, endregno, x, loc) - int regno, endregno; - rtx x; - rtx *loc; -{ - register int i; - register RTX_CODE code; - register char *fmt; - - repeat: - code = GET_CODE (x); - if (code == REG) - { - i = REGNO (x); - return (endregno > i && regno < i + HARD_REGNO_NREGS (i, GET_MODE (x))); - } - - if (code == SET) - { - /* Note setting a SUBREG counts as referring to the REG it is in! */ - if (GET_CODE (SET_DEST (x)) != REG - && refers_to_regno_p (regno, endregno, SET_DEST (x), loc)) - return 1; - if (loc == &SET_SRC (x)) - return 0; - x = SET_SRC (x); - goto repeat; - } - - if (code == CLOBBER) - { - if (GET_CODE (SET_DEST (x)) != REG - && refers_to_regno_p (regno, endregno, SET_DEST (x), loc)) - return 1; - return 0; - } - - /* X does not match, so try its subexpressions. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e' && loc != &XEXP (x, i)) - { - if (i == 0) - { - x = XEXP (x, 0); - goto repeat; - } - else - if (refers_to_regno_p (regno, endregno, XEXP (x, i), loc)) - return 1; - } - else if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >=0; j--) - if (loc != &XVECEXP (x, i, j) - && refers_to_regno_p (regno, endregno, XVECEXP (x, i, j), loc)) - return 1; - } - } - return 0; -} - -/* Nonzero if X contains any reg that overlaps hard register REG. */ - -int -reg_overlap_mentioned_p (reg, x) - rtx reg, x; -{ - int regno = REGNO (reg); - int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (reg)); - return refers_to_regno_p (regno, endregno, x, 0); -} - -/* This is 1 until after reload pass. */ -int rtx_equal_function_value_matters; - -/* Return 1 if X and Y are identical-looking rtx's. - This is the Lisp function EQUAL for rtx arguments. */ - -int -rtx_equal_p (x, y) - rtx x, y; -{ - register int i; - register int j; - register enum rtx_code code; - register char *fmt; - - if (x == y) - return 1; - if (x == 0 || y == 0) - return 0; - - code = GET_CODE (x); - /* Rtx's of different codes cannot be equal. */ - if (code != GET_CODE (y)) - return 0; - - /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. - (REG:SI x) and (REG:HI x) are NOT equivalent. */ - - if (GET_MODE (x) != GET_MODE (y)) - return 0; - - /* These three types of rtx's can be compared nonrecursively. */ - /* Until the end of reload, - don't consider the a reference to the return register of the current - function the same as the return from a called function. This eases - the job of function integration. Once the distinction no longer - matters, the insn will be deleted. */ - if (code == REG) - return (REGNO (x) == REGNO (y) - && (! rtx_equal_function_value_matters - || REG_FUNCTION_VALUE_P (x) == REG_FUNCTION_VALUE_P (y))); - if (code == LABEL_REF) - return XEXP (x, 0) == XEXP (y, 0); - if (code == SYMBOL_REF) - return XSTR (x, 0) == XSTR (y, 0); - - /* Compare the elements. If any pair of corresponding elements - fail to match, return 0 for the whole things. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - switch (fmt[i]) - { - case 'i': - if (XINT (x, i) != XINT (y, i)) - return 0; - break; - - case 'E': - /* Two vectors must have the same length. */ - if (XVECLEN (x, i) != XVECLEN (y, i)) - return 0; - - /* And the corresponding elements must match. */ - for (j = 0; j < XVECLEN (x, i); j++) - if (rtx_equal_p (XVECEXP (x, i, j), XVECEXP (y, i, j)) == 0) - return 0; - break; - - case 'e': - if (rtx_equal_p (XEXP (x, i), XEXP (y, i)) == 0) - return 0; - break; - - case 's': - if (strcmp (XSTR (x, i), XSTR (y, i))) - return 0; - break; - - case 'u': - /* These are just backpointers, so they don't matter. */ - break; - - case '0': - break; - - /* It is believed that rtx's at this level will never - contain anything but integers and other rtx's, - except for within LABEL_REFs and SYMBOL_REFs. */ - default: - abort (); - } - } - return 1; -} - -/* Call FUN on each register or MEM that is stored into or clobbered by X. - (X would be the pattern of an insn). - FUN receives two arguments: - the REG, MEM, CC0 or PC being stored in or clobbered, - the SET or CLOBBER rtx that does the store. */ - -void -note_stores (x, fun) - register rtx x; - void (*fun) (); -{ - if ((GET_CODE (x) == SET || GET_CODE (x) == CLOBBER)) - { - register rtx dest = SET_DEST (x); - while (GET_CODE (dest) == SUBREG - || GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == STRICT_LOW_PART) - dest = XEXP (dest, 0); - (*fun) (dest, x); - } - else if (GET_CODE (x) == PARALLEL) - { - register int i; - for (i = XVECLEN (x, 0) - 1; i >= 0; i--) - { - register rtx y = XVECEXP (x, 0, i); - if (GET_CODE (y) == SET || GET_CODE (y) == CLOBBER) - { - register rtx dest = SET_DEST (y); - while (GET_CODE (dest) == SUBREG - || GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == STRICT_LOW_PART) - dest = XEXP (dest, 0); - (*fun) (dest, XVECEXP (x, 0, i)); - } - } - } -} - -/* Return nonzero if register REG's old contents don't survive after INSN. - This can be because REG dies in INSN or because INSN entirely sets REG. - - "Entirely set" means set directly and not through a SUBREG, - ZERO_EXTRACT or SIGN_EXTRACT, so no trace of the old contents remains. - - REG may be a hard or pseudo reg. Renumbering is not taken into account, - but for this use that makes no difference, since regs don't overlap - during their lifetimes. Therefore, this function may be used - at any time after deaths have been computed (in flow.c). */ - -int -dead_or_set_p (insn, reg) - rtx insn; - rtx reg; -{ - register rtx link; - register int regno = REGNO (reg); - - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if ((REG_NOTE_KIND (link) == REG_DEAD - || REG_NOTE_KIND (link) == REG_INC) - && REGNO (XEXP (link, 0)) == regno) - return 1; - - if (GET_CODE (PATTERN (insn)) == SET) - return (GET_CODE (SET_DEST (PATTERN (insn))) == REG - && REGNO (SET_DEST (PATTERN (insn))) == regno); - else if (GET_CODE (PATTERN (insn)) == PARALLEL) - { - register int i; - for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) - { - if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET - && GET_CODE (SET_DEST (XVECEXP (PATTERN (insn), 0, i))) == REG - && REGNO (SET_DEST (XVECEXP (PATTERN (insn), 0, i))) == regno) - return 1; - } - } - return 0; -} - -/* Return the reg-note of kind KIND in insn INSN, if there is one. - If DATUM is nonzero, look for one whose datum is DATUM. */ - -rtx -find_reg_note (insn, kind, datum) - rtx insn; - enum reg_note kind; - rtx datum; -{ - register rtx link; - - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == kind - && (datum == 0 || datum == XEXP (link, 0))) - return link; - return 0; -} - -/* Return the reg-note of kind KIND in insn INSN which applies to register - number REGNO, if any. Return 0 if there is no such reg-note. */ - -rtx -find_regno_note (insn, kind, regno) - rtx insn; - enum reg_note kind; - int regno; -{ - register rtx link; - - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == kind - && REGNO (XEXP (link, 0)) == regno) - return link; - return 0; -} - -/* Nonzero if FROM precedes TO with no intervening labels. */ - -int -no_labels_between (from, to) - register rtx from, to; -{ - register rtx p = to; - - while (1) - { - p = PREV_INSN (p); - if (p == 0) - return 0; - if (p == from) - return 1; - if (GET_CODE (p) == CODE_LABEL) - return 0; - } -} - -/* Nonzero if X contains any volatile memory references - or volatile ASM_OPERANDS expressions. */ - -int -volatile_refs_p (x) - rtx x; -{ - register RTX_CODE code; - - code = GET_CODE (x); - switch (code) - { - case LABEL_REF: - case SYMBOL_REF: - case CONST_INT: - case CONST: - case CONST_DOUBLE: - case CC0: - case PC: - case REG: - case CLOBBER: - case ASM_INPUT: - case ADDR_VEC: - case ADDR_DIFF_VEC: - return 0; - - case CALL: - return 1; - - case MEM: - case ASM_OPERANDS: - if (MEM_VOLATILE_P (x)) - return 1; - } - - /* Recursively scan the operands of this expression. */ - - { - register char *fmt = GET_RTX_FORMAT (code); - register int i; - - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - if (volatile_refs_p (XEXP (x, i))) - return 1; - } - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - if (volatile_refs_p (XVECEXP (x, i, j))) - return 1; - } - } - } - return 0; -} - -/* Return nonzero if evaluating rtx X might cause a trap. */ - -int -may_trap_p (x) - rtx x; -{ - int i; - enum rtx_code code; - char *fmt; - - if (x == 0) - return 0; - code = GET_CODE (x); - switch (code) - { - /* Handle these cases fast. */ - case CONST_INT: - case CONST_DOUBLE: - case SYMBOL_REF: - case LABEL_REF: - case CONST: - case PC: - case CC0: - case REG: - return 0; - - /* Memory ref can trap unless it's a static var or a stack slot. */ - case MEM: - return rtx_varies_p (XEXP (x, 0)); - - /* Division by a non-constant might trap. */ - case DIV: - case MOD: - case UDIV: - case UMOD: - if (! CONSTANT_P (XEXP (x, 1)) - && GET_CODE (XEXP (x, 1)) != CONST_DOUBLE) - return 1; - if (XEXP (x, 1) == const0_rtx) - return 1; - default: - /* Any floating arithmetic may trap. */ - if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) - return 1; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - if (may_trap_p (XEXP (x, i))) - return 1; - } - else if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - if (may_trap_p (XVECEXP (x, i, j))) - return 1; - } - } - return 0; -} diff --git a/gnu/usr.bin/gcc1/cc1/sdbout.c b/gnu/usr.bin/gcc1/cc1/sdbout.c deleted file mode 100644 index e6295cd9d1..0000000000 --- a/gnu/usr.bin/gcc1/cc1/sdbout.c +++ /dev/null @@ -1,1098 +0,0 @@ -/* Output sdb-format symbol table information from GNU compiler. - Copyright (C) 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#define MAYBE - -#include "config.h" - -#ifdef SDB_DEBUGGING_INFO - -#include "tree.h" -#include "rtl.h" -#include - -#if defined(USG) && !defined(MIPS_DEBUGGING_INFO) -#include -/* #include used to be this instead of syms.h. */ - -#else -/* For cross compilation, use the portable defintions from the COFF - documentation. */ - -#define C_EFCN -1 -#define C_NULL 0 -#define C_AUTO 1 -#define C_EXT 2 -#define C_STAT 3 -#define C_REG 4 -#define C_EXTDEF 5 -#define C_LABEL 6 -#define C_ULABEL 7 -#define C_MOS 8 -#define C_ARG 9 -#define C_STRTAG 10 -#define C_MOU 11 -#define C_UNTAG 12 -#define C_TPDEF 13 -#define C_USTATIC 14 -#define C_ENTAG 15 -#define C_MOE 16 -#define C_REGPARM 17 -#define C_FIELD 18 -#define C_BLOCK 100 -#define C_FCN 101 -#define C_EOS 102 -#define C_FILE 103 - -#define C_LINE 104 -#define C_ALIAS 105 -#define C_HIDDEN 106 - -#define T_NULL 0 -#define T_ARG 1 -#define T_CHAR 2 -#define T_SHORT 3 -#define T_INT 4 -#define T_LONG 5 -#define T_FLOAT 6 -#define T_DOUBLE 7 -#define T_STRUCT 8 -#define T_UNION 9 -#define T_ENUM 10 -#define T_MOE 11 -#define T_UCHAR 12 -#define T_USHORT 13 -#define T_UINT 14 -#define T_ULONG 15 - -#define DT_NON 0 -#define DT_PTR 1 -#define DT_FCN 2 -#define DT_ARY 3 - -#define N_BTMASK 017 -#define N_TMASK 060 -#define N_TMASK1 0300 -#define N_TMASK2 0360 -#define N_BTSHFT 4 -#define N_TSHIFT 2 -#endif - -/* Line number of beginning of current function, minus one. - Negative means not in a function or not using sdb. */ - -int sdb_begin_function_line = -1; - -/* Counter to generate unique "names" for nameless struct members. */ - -static int unnamed_struct_number = 0; - -extern FILE *asm_out_file; - -extern tree current_function_decl; - -void sdbout_init (); -void sdbout_symbol (); -void sdbout_tags(); -void sdbout_types(); - -static void sdbout_syms (); -static void sdbout_one_type (); -static int plain_type_1 (); - -/* Random macros describing parts of SDB data. */ - -/* Put something here if lines get too long */ -#define CONTIN - -/* Maximum number of dimensions the assembler will allow. */ -#ifndef SDB_MAX_DIM -#define SDB_MAX_DIM 4 -#endif - -#ifndef PUT_SDB_SCL -#define PUT_SDB_SCL(a) fprintf(asm_out_file, "\t.scl\t%d;", (a)) -#endif - -#ifndef PUT_SDB_INT_VAL -#define PUT_SDB_INT_VAL(a) fprintf (asm_out_file, "\t.val\t%d;", (a)) -#endif - -#ifndef PUT_SDB_VAL -#define PUT_SDB_VAL(a) \ -( fputs ("\t.val\t", asm_out_file), \ - output_addr_const (asm_out_file, (a)), \ - fputc (';', asm_out_file)) -#endif - -#ifndef PUT_SDB_DEF -#define PUT_SDB_DEF(a) \ -do { fprintf (asm_out_file, "\t.def\t"); \ - ASM_OUTPUT_LABELREF (asm_out_file, a); \ - fprintf (asm_out_file, ";"); } while (0) -#endif - -#ifndef PUT_SDB_PLAIN_DEF -#define PUT_SDB_PLAIN_DEF(a) fprintf(asm_out_file,"\t.def\t.%s;",a) -#endif - -#ifndef PUT_SDB_ENDEF -#define PUT_SDB_ENDEF fputs("\t.endef\n", asm_out_file) -#endif - -#ifndef PUT_SDB_TYPE -#define PUT_SDB_TYPE(a) fprintf(asm_out_file, "\t.type\t0%o;", a) -#endif - -#ifndef PUT_SDB_SIZE -#define PUT_SDB_SIZE(a) fprintf(asm_out_file, "\t.size\t%d;", a) -#endif - -#ifndef PUT_SDB_START_DIM -#define PUT_SDB_START_DIM fprintf(asm_out_file, "\t.dim\t") -#endif - -#ifndef PUT_SDB_NEXT_DIM -#define PUT_SDB_NEXT_DIM(a) fprintf(asm_out_file, "%d,", a) -#endif - -#ifndef PUT_SDB_LAST_DIM -#define PUT_SDB_LAST_DIM(a) fprintf(asm_out_file, "%d;", a) -#endif - -#ifndef PUT_SDB_TAG -#define PUT_SDB_TAG(a) \ -do { fprintf (asm_out_file, "\t.tag\t"); \ - ASM_OUTPUT_LABELREF (asm_out_file, a); \ - fprintf (asm_out_file, ";"); } while (0) -#endif - -#ifndef PUT_SDB_BLOCK_START -#define PUT_SDB_BLOCK_START(LINE) \ - fprintf (asm_out_file, \ - "\t.def\t.bb;\t.val\t.;\t.scl\t100;\t.line\t%d;\t.endef\n", \ - (LINE)) -#endif - -#ifndef PUT_SDB_BLOCK_END -#define PUT_SDB_BLOCK_END(LINE) \ - fprintf (asm_out_file, \ - "\t.def\t.eb;.val\t.;\t.scl\t100;\t.line\t%d;\t.endef\n", \ - (LINE)) -#endif - -#ifndef PUT_SDB_FUNCTION_START -#define PUT_SDB_FUNCTION_START(LINE) \ - fprintf (asm_out_file, \ - "\t.def\t.bf;\t.val\t.;\t.scl\t101;\t.line\t%d;\t.endef\n", \ - (LINE)) -#endif - -#ifndef PUT_SDB_FUNCTION_END -#define PUT_SDB_FUNCTION_END(LINE) \ - fprintf (asm_out_file, \ - "\t.def\t.ef;\t.val\t.;\t.scl\t101;\t.line\t%d;\t.endef\n", \ - (LINE)) -#endif - -#ifndef PUT_SDB_EPILOGUE_END -#define PUT_SDB_EPILOGUE_END(NAME) \ - fprintf (asm_out_file, \ - "\t.def\t%s;\t.val\t.;\t.scl\t-1;\t.endef\n", \ - (NAME)) -#endif - -#ifndef SDB_GENERATE_FAKE -#define SDB_GENERATE_FAKE(BUFFER, NUMBER) \ - sprintf ((BUFFER), ".%dfake", (NUMBER)); -#endif - -/* Return the sdb tag identifier string for TYPE - if TYPE has already been defined; otherwise return a null pointer. */ - -#define KNOWN_TYPE_TAG(type) (char *)(TYPE_SYMTAB_ADDRESS (type)) - -/* Set the sdb tag identifier string for TYPE to NAME. */ - -#define SET_KNOWN_TYPE_TAG(TYPE, NAME) \ - (TYPE_SYMTAB_ADDRESS (TYPE) = (int)(NAME)) - -/* Return the name (a string) of the struct, union or enum tag - described by the TREE_LIST node LINK. This is 0 for an anonymous one. */ - -#define TAG_NAME(link) \ - (((link) && TREE_PURPOSE ((link)) \ - && IDENTIFIER_POINTER (TREE_PURPOSE ((link)))) \ - ? IDENTIFIER_POINTER (TREE_PURPOSE ((link))) : (char *) 0) - -/* Ensure we don't output a negative line number. */ -#define MAKE_LINE_SAFE(line) \ - if (line <= sdb_begin_function_line) line = sdb_begin_function_line + 1 - -/* Tell the assembler the source file name. - On systems that use SDB, this is done whether or not -g, - so it is called by ASM_FILE_START. - - ASM_FILE is the assembler code output file, - INPUT_NAME is the name of the main input file. */ - -void -sdbout_filename (asm_file, input_name) - FILE *asm_file; - char *input_name; -{ - int len = strlen (input_name); - char *na = input_name + len; - - /* NA gets INPUT_NAME sans directory names. */ - while (na > input_name) - { - if (na[-1] == '/') - break; - na--; - } - -#ifdef ASM_OUTPUT_SOURCE_FILENAME - ASM_OUTPUT_SOURCE_FILENAME (asm_file, na); -#else - fprintf (asm_file, "\t.file\t\"%s\"\n", na); -#endif -} - -/* Set up for SDB output at the start of compilation. */ - -void -sdbout_init () -{ - /* Output all the initial permanent types. */ - sdbout_types (nreverse (get_permanent_types ())); -} - -#if 0 - -/* return the tag identifier for type - */ - -{ -char * -tag_of_ru_type (type,link) - tree type,link; -{ - if (TYPE_SYMTAB_ADDRESS (type)) - return (char *)TYPE_SYMTAB_ADDRESS (type); - if (link && - TREE_PURPOSE (link) - && IDENTIFIER_POINTER (TREE_PURPOSE (link))) - TYPE_SYMTAB_ADDRESS (type) = - (int)IDENTIFIER_POINTER (TREE_PURPOSE (link)); - else - return (char *) TYPE_SYMTAB_ADDRESS (type); -} -#endif - -/* Return a unique string to name an anonymous type. */ - -static char * -gen_fake_label () -{ - char label[10]; - char *labelstr; - SDB_GENERATE_FAKE (label, unnamed_struct_number); - unnamed_struct_number++; - labelstr = (char *) permalloc (strlen (label) + 1); - strcpy (labelstr, label); - return labelstr; -} - -/* Return the number which describes TYPE for SDB. - For pointers, etc., this function is recursive. - Each record, union or enumeral type must already have had a - tag number output. */ - -/* The number is given by d6d5d4d3d2d1bbbb - where bbbb is 4 bit basic type, and di indicate one of notype,ptr,fn,array. - Thus, char *foo () has bbbb=T_CHAR - d1=D_FCN - d2=D_PTR - N_BTMASK= 017 1111 basic type field. - N_TSHIFT= 2 derived type shift - N_BTSHFT= 4 Basic type shift */ - -/* Produce the number that describes a pointer, function or array type. - PREV is the number describing the target, value or element type. - DT_type describes how to transform that type. */ -#define PUSH_DERIVED_LEVEL(DT_type,PREV) \ - ((((PREV)&~N_BTMASK)< 0) - { - int i; - PUT_SDB_START_DIM; - for (i = sdb_n_dims - 1; i > 0; i--) - PUT_SDB_NEXT_DIM (sdb_dims[i]); - PUT_SDB_LAST_DIM (sdb_dims[0]); - sdb_n_dims = 0; - - sdb_type_size = int_size_in_bytes (type); - /* Don't kill sdb if type is not laid out or has variable size. */ - if (sdb_type_size < 0) - sdb_type_size = 0; - } - /* If we have computed the size of an array containing this type, - print it now. */ - if (sdb_type_size >= 0) - { - PUT_SDB_SIZE (sdb_type_size); - sdb_type_size = -1; - } - return val; -} - -static void -sdbout_record_type_name (type) - tree type; -{ - char *name = 0; - - if (KNOWN_TYPE_TAG (type)) - return; - - if (TYPE_NAME (type) != 0) - { - tree t = 0; - /* Find the IDENTIFIER_NODE for the type name. */ - if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) - { - t = TYPE_NAME (type); - } - else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL) - { - t = DECL_NAME (TYPE_NAME (type)); - } - - /* Now get the name as a string, or invent one. */ - if (t != 0) - name = IDENTIFIER_POINTER (t); - } - - if (name == 0) - name = gen_fake_label (); - - SET_KNOWN_TYPE_TAG (type, name); -} - -static int -plain_type_1 (type) - tree type; -{ - if (type == 0) - type = void_type_node; - if (type == error_mark_node) - type = integer_type_node; - type = TYPE_MAIN_VARIANT (type); - - switch (TREE_CODE (type)) - { - case VOID_TYPE: - return T_INT; - case INTEGER_TYPE: - switch (int_size_in_bytes (type)) - { - case 4: - return (TREE_UNSIGNED (type) ? T_UINT : T_INT); - case 1: - return (TREE_UNSIGNED (type) ? T_UCHAR : T_CHAR); - case 2: - return (TREE_UNSIGNED (type) ? T_USHORT : T_SHORT); - default: - return 0; - } - case REAL_TYPE: - switch (int_size_in_bytes (type)) - { - case 4: - return T_FLOAT; - default: - return T_DOUBLE; - } - - case ARRAY_TYPE: - { - int m; - m = plain_type_1 (TREE_TYPE (type)); - if (sdb_n_dims < SDB_MAX_DIM) - sdb_dims[sdb_n_dims++] - = (TYPE_DOMAIN (type) - ? TREE_INT_CST_LOW (TYPE_MAX_VALUE (TYPE_DOMAIN (type))) + 1 - : 0); - return PUSH_DERIVED_LEVEL (DT_ARY, m); - } - - case RECORD_TYPE: - case UNION_TYPE: - case ENUMERAL_TYPE: - { - char *tag; - sdbout_record_type_name (type); - if (TREE_ASM_WRITTEN (type) -#ifdef MAYBE - && KNOWN_TYPE_TAG (type) -#endif - ) - { - /* Output the referenced structure tag name - only if the .def has already been output. - At least on 386, the Unix assembler - cannot handle forward references to tags. */ - tag = KNOWN_TYPE_TAG (type); - PUT_SDB_TAG (tag); - } - sdb_type_size = int_size_in_bytes (type); - if (sdb_type_size < 0) - sdb_type_size = 0; - return ((TREE_CODE (type) == RECORD_TYPE) ? T_STRUCT - : (TREE_CODE (type) == UNION_TYPE) ? T_UNION - : T_ENUM); - } - case POINTER_TYPE: - case REFERENCE_TYPE: - { - int m = plain_type_1 (TREE_TYPE (type)); - return PUSH_DERIVED_LEVEL (DT_PTR, m); - } - case FUNCTION_TYPE: - case METHOD_TYPE: - { - int m = plain_type_1 (TREE_TYPE (type)); - return PUSH_DERIVED_LEVEL (DT_FCN, m); - } - default: - return 0; - } -} - -/* Output the symbols defined in block number DO_BLOCK. - Set NEXT_BLOCK_NUMBER to 0 before calling. - - This function works by walking the tree structure, - counting blocks, until it finds the desired block. */ - -static int do_block = 0; - -static int next_block_number; - -static void -sdbout_block (stmt) - register tree stmt; -{ - while (stmt) - { - switch (TREE_CODE (stmt)) - { - case COMPOUND_STMT: - case LOOP_STMT: - sdbout_block (STMT_BODY (stmt)); - break; - - case IF_STMT: - sdbout_block (STMT_THEN (stmt)); - sdbout_block (STMT_ELSE (stmt)); - break; - - case LET_STMT: - /* Ignore LET_STMTs for blocks never really used to make RTL. */ - if (! TREE_USED (stmt)) - break; - /* When we reach the specified block, output its symbols. */ - if (next_block_number == do_block) - { - sdbout_tags (STMT_TYPE_TAGS (stmt)); - sdbout_syms (STMT_VARS (stmt)); - } - - /* If we are past the specified block, stop the scan. */ - if (next_block_number > do_block) - return; - - next_block_number++; - - /* Scan the blocks within this block. */ - sdbout_block (STMT_SUBBLOCKS (stmt)); - } - stmt = TREE_CHAIN (stmt); - } -} - -/* Call sdbout_symbol on each decl in the chain SYMS. */ - -static void -sdbout_syms (syms) - tree syms; -{ - while (syms) - { - sdbout_symbol (syms, 1); - syms = TREE_CHAIN (syms); - } -} - -/* Output SDB information for a symbol described by DECL. - LOCAL is nonzero if the symbol is not file-scope. */ - -void -sdbout_symbol (decl, local) - tree decl; - int local; -{ - int letter = 0; - tree type = TREE_TYPE (decl); - rtx value; - - /* If global, first output all types and all - struct, enum and union tags that have been created - and not yet output. */ - - if (local == 0) - { - sdbout_tags (gettags ()); - sdbout_types (nreverse (get_permanent_types ())); - } - -#ifdef MAYBE - sdbout_one_type (type); -#endif - - switch (TREE_CODE (decl)) - { - case CONST_DECL: - /* Enum values are defined by defining the enum type. */ - return; - - case FUNCTION_DECL: - if (TREE_EXTERNAL (decl)) - return; - if (GET_CODE (DECL_RTL (decl)) != MEM - || GET_CODE (XEXP (DECL_RTL (decl), 0)) != SYMBOL_REF) - return; - PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_NAME (decl))); - PUT_SDB_VAL (XEXP (DECL_RTL (decl), 0)); - PUT_SDB_SCL (TREE_PUBLIC (decl) ? C_EXT : C_STAT); - break; - - case TYPE_DECL: - /* Output typedef name. */ - PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_NAME (decl))); - PUT_SDB_SCL (C_TPDEF); - break; - - case PARM_DECL: - /* Parm decls go in their own separate chains - and are output by sdbout_reg_parms and sdbout_parms. */ - abort (); - - case VAR_DECL: - /* Don't mention a variable that is external. - Let the file that defines it describe it. */ - if (TREE_EXTERNAL (decl)) - return; - - value = DECL_RTL (decl); - - /* Don't mention a variable at all - if it was completely optimized into nothingness. */ - if (GET_CODE (value) == REG - && (REGNO (value) < 0 - || REGNO (value) >= FIRST_PSEUDO_REGISTER)) - return; - - /* Ok, start a symtab entry and output the variable name. */ - PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_NAME (decl))); - - if (GET_CODE (value) == MEM - && GET_CODE (XEXP (value, 0)) == SYMBOL_REF) - { - if (TREE_PUBLIC (decl)) - { - PUT_SDB_VAL (XEXP (value, 0)); - PUT_SDB_SCL (C_EXT); - } - else - { - PUT_SDB_VAL (XEXP (value, 0)); - PUT_SDB_SCL (C_STAT); - } - } - else if (GET_CODE (value) == REG) - { - PUT_SDB_INT_VAL (DBX_REGISTER_NUMBER (REGNO (value))); - PUT_SDB_SCL (C_REG); - } - else if (GET_CODE (value) == SUBREG) - { - int offset = 0; - while (GET_CODE (value) == SUBREG) - { - offset += SUBREG_WORD (value); - value = SUBREG_REG (value); - } - PUT_SDB_INT_VAL (DBX_REGISTER_NUMBER (REGNO (value) + offset)); - PUT_SDB_SCL (C_REG); - } - else if (GET_CODE (value) == MEM - && (GET_CODE (XEXP (value, 0)) == MEM - || (GET_CODE (XEXP (value, 0)) == REG - && REGNO (XEXP (value, 0)) != FRAME_POINTER_REGNUM))) - /* If the value is indirect by memory or by a register - that isn't the frame pointer - then it means the object is variable-sized and address through - that register or stack slot. DBX has no way to represent this - so all we can do is output the variable as a pointer. */ - { - if (GET_CODE (XEXP (value, 0)) == REG) - { - PUT_SDB_INT_VAL (DBX_REGISTER_NUMBER (REGNO (XEXP (value, 0)))); - PUT_SDB_SCL (C_REG); - } - else - { - /* DECL_RTL looks like (MEM (MEM (PLUS (REG...) - (CONST_INT...)))). - We want the value of that CONST_INT. */ - /* Encore compiler hates a newline in a macro arg, it seems. */ - PUT_SDB_INT_VAL (INTVAL (XEXP (XEXP (XEXP (value, 0), 0), 1))); - PUT_SDB_SCL (C_AUTO); - } - - type = build_pointer_type (TREE_TYPE (decl)); - } - else if (GET_CODE (value) == MEM - && GET_CODE (XEXP (value, 0)) == PLUS - && GET_CODE (XEXP (XEXP (value, 0), 0)) == REG - && GET_CODE (XEXP (XEXP (value, 0), 1)) == CONST_INT) - { - /* DECL_RTL looks like (MEM (PLUS (REG...) (CONST_INT...))). - We want the value of that CONST_INT. */ - PUT_SDB_INT_VAL (INTVAL (XEXP (XEXP (value, 0), 1))); - PUT_SDB_SCL (C_AUTO); - } - else - { - /* It is something we don't know how to represent for SDB. */ - } - break; - } - PUT_SDB_TYPE (plain_type (type)); - PUT_SDB_ENDEF; -} - -/* Given a list of TREE_LIST nodes that point at types, - output those types for SDB. - We must check to include those that have been mentioned already with - only a cross-reference. */ - -void -sdbout_tags (tags) - tree tags; -{ - register tree link; - - for (link = tags; link; link = TREE_CHAIN (link)) - { - register tree type = TREE_VALUE (link); - - if (TREE_PURPOSE (link) != 0 - && TYPE_SIZE (type) != 0) - sdbout_one_type (type); - } -} - -/* Given a chain of ..._TYPE nodes, all of which have names, - output definitions of those names, as typedefs. */ - -void -sdbout_types (types) - register tree types; -{ - register tree link; - - for (link = types; link; link = TREE_CHAIN (link)) - sdbout_one_type (link); -} - -static void -sdbout_type (type) - tree type; -{ - register tree tem; - if (type == error_mark_node) - type = integer_type_node; - PUT_SDB_TYPE (plain_type (type)); -} - -/* Output types of the fields of type TYPE, if they are structs. - Formerly did not chase through pointer types, since that could be circular. - They must come before TYPE, since forward refs are not allowed. - Now james@bigtex.cactus.org says to try them. */ - -static void -sdbout_field_types (type) - tree type; -{ - tree tail; - for (tail = TYPE_FIELDS (type); tail; tail = TREE_CHAIN (tail)) - { -#ifdef MAYBE - if (TREE_CODE (TREE_TYPE (tail)) == POINTER_TYPE) - sdbout_one_type (TREE_TYPE (TREE_TYPE (tail))); - else -#endif - sdbout_one_type (TREE_TYPE (tail)); - } -} - -/* Use this to put out the top level defined record and union types - for later reference. If this is a struct with a name, then put that - name out. Other unnamed structs will have .xxfake labels generated so - that they may be referred to later. - The label will be stored in the KNOWN_TYPE_TAG slot of a type. - It may NOT be called recursively. */ - -static void -sdbout_one_type (type) - tree type; -{ - text_section (); - - switch (TREE_CODE (type)) - { - case RECORD_TYPE: - case UNION_TYPE: - case ENUMERAL_TYPE: - type = TYPE_MAIN_VARIANT (type); - /* Don't output a type twice. */ - if (TREE_ASM_WRITTEN (type)) - return; - - /* Output nothing if type is not yet defined. */ - if (TYPE_SIZE (type) == 0) - return; - - TREE_ASM_WRITTEN (type) = 1; -#ifndef MAYBE - /* Before really doing anything, output types we want to refer to. */ - if (TREE_CODE (type) != ENUMERAL_TYPE) - sdbout_field_types (type); -#endif - - sdbout_record_type_name (type); - - /* Output a structure type. */ - { - int size = int_size_in_bytes (type); - int member_scl; - tree tem; - - PUT_SDB_DEF (KNOWN_TYPE_TAG (type)); - - switch (TREE_CODE (type)) - { - case UNION_TYPE: - PUT_SDB_SCL (C_UNTAG); - PUT_SDB_TYPE (T_UNION); - member_scl = C_MOU; - break; - - case RECORD_TYPE: - PUT_SDB_SCL (C_STRTAG); - PUT_SDB_TYPE (T_STRUCT); - member_scl = C_MOS; - break; - - case ENUMERAL_TYPE: - PUT_SDB_SCL (C_ENTAG); - PUT_SDB_TYPE (T_ENUM); - member_scl = C_MOE; - break; - } - - PUT_SDB_SIZE (size); - PUT_SDB_ENDEF; - - /* output the individual fields */ - - if (TREE_CODE (type) == ENUMERAL_TYPE) - for (tem = TYPE_FIELDS (type); tem; tem = TREE_CHAIN (tem)) - { - PUT_SDB_DEF (IDENTIFIER_POINTER (TREE_PURPOSE (tem))); - PUT_SDB_INT_VAL (TREE_INT_CST_LOW (TREE_VALUE (tem))); - PUT_SDB_SCL (C_MOE); - PUT_SDB_TYPE (T_MOE); - PUT_SDB_ENDEF; - } - - else /* record or union type */ - for (tem = TYPE_FIELDS (type); tem; tem = TREE_CHAIN (tem)) - /* Output the name, type, position (in bits), size (in bits) - of each field. */ - /* Omit here the nameless fields that are used to skip bits. */ - if (DECL_NAME (tem) != 0) - { - CONTIN; - PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_NAME (tem))); - if (TREE_PACKED (tem)) - { - PUT_SDB_INT_VAL (DECL_OFFSET (tem)); - PUT_SDB_SCL (C_FIELD); - sdbout_type (TREE_TYPE (tem)); - PUT_SDB_SIZE (TREE_INT_CST_LOW (DECL_SIZE (tem)) - * DECL_SIZE_UNIT (tem)); - } - else - { - PUT_SDB_INT_VAL (DECL_OFFSET (tem) / BITS_PER_UNIT); - PUT_SDB_SCL (member_scl); - sdbout_type (TREE_TYPE (tem)); - } - PUT_SDB_ENDEF; - } - /* output end of a structure,union, or enumeral definition */ - - PUT_SDB_PLAIN_DEF ("eos"); - PUT_SDB_INT_VAL (size); - PUT_SDB_SCL (C_EOS); - PUT_SDB_TAG (KNOWN_TYPE_TAG (type)); - PUT_SDB_SIZE (size); - PUT_SDB_ENDEF; - break; - } - } -} - -/* Output definitions of all parameters, referring when possible to the - place where the parameters were passed rather than the copies used - within the function. This is done as part of starting the function. - PARMS is a chain of PARM_DECL nodes. */ - -static void -sdbout_parms (parms1) - tree parms1; -{ - tree type; - tree parms; - - for (parms = parms1; parms; parms = TREE_CHAIN (parms)) - { - int current_sym_value = DECL_OFFSET (parms) / BITS_PER_UNIT; - - if (DECL_NAME (parms)) - PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_NAME (parms))); - else - PUT_SDB_DEF (gen_fake_label ()); - - if (GET_CODE (DECL_RTL (parms)) == REG - && REGNO (DECL_RTL (parms)) >= 0 - && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER) - type = DECL_ARG_TYPE (parms); - else - { - /* This is the case where the parm is passed as an int or double - and it is converted to a char, short or float and stored back - in the parmlist. In this case, describe the parm - with the variable's declared type, and adjust the address - if the least significant bytes (which we are using) are not - the first ones. */ -#ifdef BYTES_BIG_ENDIAN - if (TREE_TYPE (parms) != DECL_ARG_TYPE (parms)) - current_sym_value += - (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms))) - - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms)))); -#endif - if (GET_CODE (DECL_RTL (parms)) == MEM - && GET_CODE (XEXP (DECL_RTL (parms), 0)) == PLUS - && GET_CODE (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == CONST_INT - && (INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)) - == current_sym_value)) - type = TREE_TYPE (parms); - else - { - current_sym_value = DECL_OFFSET (parms) / BITS_PER_UNIT; - type = DECL_ARG_TYPE (parms); - } - } - - PUT_SDB_INT_VAL (current_sym_value); - PUT_SDB_SCL (C_ARG); - PUT_SDB_TYPE (plain_type (type)); - PUT_SDB_ENDEF; - } -} - -/* Output definitions, referring to registers, - of all the parms in PARMS which are stored in registers during the function. - PARMS is a chain of PARM_DECL nodes. - This is done as part of starting the function. */ - -static void -sdbout_reg_parms (parms) - tree parms; -{ - while (parms) - { - if (GET_CODE (DECL_RTL (parms)) == REG - && REGNO (DECL_RTL (parms)) >= 0 - && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER) - { - PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_NAME (parms))); - PUT_SDB_INT_VAL (DBX_REGISTER_NUMBER (REGNO (DECL_RTL (parms)))); - PUT_SDB_SCL (C_REG); - PUT_SDB_TYPE (plain_type (TREE_TYPE (parms), 0)); - PUT_SDB_ENDEF; - } - else if (GET_CODE (DECL_RTL (parms)) == MEM - && GET_CODE (XEXP (DECL_RTL (parms), 0)) == PLUS - && GET_CODE (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == CONST_INT) - { - int offset = DECL_OFFSET (parms) / BITS_PER_UNIT; - /* A parm declared char is really passed as an int, - so it occupies the least significant bytes. - On a big-endian machine those are not the low-numbered ones. */ -#ifdef BYTES_BIG_ENDIAN - if (TREE_TYPE (parms) != DECL_ARG_TYPE (parms)) - offset += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms))) - - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms)))); -#endif - if (INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)) != offset) - { - PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_NAME (parms))); - PUT_SDB_INT_VAL (INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1))); - PUT_SDB_SCL (C_AUTO); - PUT_SDB_TYPE (plain_type (TREE_TYPE (parms))); - PUT_SDB_ENDEF; - } - } - parms = TREE_CHAIN (parms); - } -} - -/* Describe the beginning of an internal block within a function. - Also output descriptions of variables defined in this block. - - N is the number of the block, by order of beginning, counting from 1, - and not counting the outermost (function top-level) block. - The blocks match the LET_STMTS in DECL_INITIAL (current_function_decl), - if the count starts at 0 for the outermost one. */ - -void -sdbout_begin_block (file, line, n) - FILE *file; - int line; - int n; -{ - tree decl = current_function_decl; - MAKE_LINE_SAFE (line); - PUT_SDB_BLOCK_START (line - sdb_begin_function_line); - if (n == 1) - { - /* Include the outermost LET_STMT's variables in block 1. */ - next_block_number = 0; - do_block = 0; - sdbout_block (DECL_INITIAL (decl)); - } - next_block_number = 0; - do_block = n; - sdbout_block (DECL_INITIAL (decl)); -} - -/* Describe the end line-number of an internal block within a function. */ - -void -sdbout_end_block (file, line) - FILE *file; - int line; -{ - MAKE_LINE_SAFE (line); - PUT_SDB_BLOCK_END (line - sdb_begin_function_line); -} - -/* Output sdb info for the current function name. - Called from assemble_function. */ - -void -sdbout_mark_begin_function () -{ - sdbout_symbol (current_function_decl, 0); -} - -/* Called at beginning of function body (after prologue). - Record the function's starting line number, so we can output - relative line numbers for the other lines. - Describe beginning of outermost block. - Also describe the parameter list. */ - -void -sdbout_begin_function (line) - int line; -{ - sdb_begin_function_line = line - 1; - PUT_SDB_FUNCTION_START (line); - sdbout_parms (DECL_ARGUMENTS (current_function_decl)); - sdbout_reg_parms (DECL_ARGUMENTS (current_function_decl)); -} - -/* Called at end of function (before epilogue). - Describe end of outermost block. */ - -void -sdbout_end_function (line) - int line; -{ - MAKE_LINE_SAFE (line); - PUT_SDB_FUNCTION_END (line - sdb_begin_function_line); - - /* Indicate we are between functions, for line-number output. */ - sdb_begin_function_line = -1; -} - -/* Output sdb info for the absolute end of a function. - Called after the epilogue is output. */ - -void -sdbout_end_epilogue () -{ - char *name = IDENTIFIER_POINTER (DECL_NAME (current_function_decl)); - PUT_SDB_EPILOGUE_END (name); -} - -#endif /* SDB_DEBUGGING_INFO */ diff --git a/gnu/usr.bin/gcc1/cc1/stab.h b/gnu/usr.bin/gcc1/cc1/stab.h deleted file mode 100644 index 77f2d411ce..0000000000 --- a/gnu/usr.bin/gcc1/cc1/stab.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __GNU_STAB__ - -/* Indicate the GNU stab.h is in use. */ - -#define __GNU_STAB__ - -#define __define_stab(NAME, CODE, STRING) NAME=CODE, - -enum __stab_debug_code -{ -#include "stab.def" -}; - -#undef __define_stab - -#endif /* __GNU_STAB_ */ diff --git a/gnu/usr.bin/gcc1/cc1/stmt.c b/gnu/usr.bin/gcc1/cc1/stmt.c deleted file mode 100644 index 23d7485c67..0000000000 --- a/gnu/usr.bin/gcc1/cc1/stmt.c +++ /dev/null @@ -1,5057 +0,0 @@ -/* Expands front end tree to back end RTL for GNU C-Compiler - Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This file handles the generation of rtl code from tree structure - above the level of expressions, using subroutines in exp*.c and emit-rtl.c. - It also creates the rtl expressions for parameters and auto variables - and has full responsibility for allocating stack slots. - - The functions whose names start with `expand_' are called by the - parser to generate RTL instructions for various kinds of constructs. - - Some control and binding constructs require calling several such - functions at different times. For example, a simple if-then - is expanded by calling `expand_start_cond' (with the condition-expression - as argument) before parsing the then-clause and calling `expand_end_cond' - after parsing the then-clause. - - `expand_function_start' is called at the beginning of a function, - before the function body is parsed, and `expand_function_end' is - called after parsing the body. - - Call `assign_stack_local' to allocate a stack slot for a local variable. - This is usually done during the RTL generation for the function body, - but it can also be done in the reload pass when a pseudo-register does - not get a hard register. - - Call `put_var_into_stack' when you learn, belatedly, that a variable - previously given a pseudo-register must in fact go in the stack. - This function changes the DECL_RTL to be a stack slot instead of a reg - then scans all the RTL instructions so far generated to correct them. */ - -#include "config.h" - -#include - -#include "rtl.h" -#include "tree.h" -#include "flags.h" -#include "insn-flags.h" -#include "insn-config.h" -#include "insn-codes.h" -#include "expr.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "recog.h" - -#define MAX(x,y) (((x) > (y)) ? (x) : (y)) -#define MIN(x,y) (((x) < (y)) ? (x) : (y)) - -/* Nonzero if function being compiled pops its args on return. - May affect compilation of return insn or of function epilogue. */ - -int current_function_pops_args; - -/* Nonzero if function being compiled needs to be given an address - where the value should be stored. */ - -int current_function_returns_struct; - -/* Nonzero if function being compiled needs to - return the address of where it has put a structure value. */ - -int current_function_returns_pcc_struct; - -/* Nonzero if function being compiled needs to be passed a static chain. */ - -int current_function_needs_context; - -/* Nonzero if function being compiled can call setjmp. */ - -int current_function_calls_setjmp; - -/* Nonzero if function being compiled can call alloca, - either as a subroutine or builtin. */ - -int current_function_calls_alloca; - -/* Nonzero if the current function returns a pointer type */ - -int current_function_returns_pointer; - -/* If function's args have a fixed size, this is that size, in bytes. - Otherwise, it is -1. - May affect compilation of return insn or of function epilogue. */ - -int current_function_args_size; - -/* # bytes the prologue should push and pretend that the caller pushed them. - The prologue must do this, but only if parms can be passed in registers. */ - -int current_function_pretend_args_size; - -/* This is the offset from the arg pointer to the place where the first - anonymous arg can be found, if there is one. */ -rtx current_function_arg_offset_rtx; - -/* Name of function now being compiled. */ - -char *current_function_name; - -/* Label that will go on parm cleanup code, if any. - Jumping to this label runs cleanup code for parameters, if - such code must be run. Following this code is the logical return label. */ - -rtx cleanup_label; - -/* Label that will go on function epilogue. - Jumping to this label serves as a "return" instruction - on machines which require execution of the epilogue on all returns. */ - -rtx return_label; - -/* List (chain of EXPR_LISTs) of pseudo-regs of SAVE_EXPRs. - So we can mark them all live at the end of the function, if nonopt. */ -rtx save_expr_regs; - -/* List (chain of EXPR_LISTs) of all stack slots in this function. - Made for the sake of unshare_all_rtl. */ -rtx stack_slot_list; - -/* Filename and line number of last line-number note, - whether we actually emitted it or not. */ -char *emit_filename; -int emit_lineno; - -/* Insn after which register parms and SAVE_EXPRs are born, if nonopt. */ -static rtx parm_birth_insn; - -/* The FUNCTION_DECL node for the function being compiled. */ - -static tree this_function; - -/* Number of binding contours started so far in this function. */ - -static int block_start_count; - -/* Offset to end of allocated area of stack frame. - If stack grows down, this is the address of the last stack slot allocated. - If stack grows up, this is the address for the next slot. */ -static int frame_offset; - -/* Nonzero if a stack slot has been generated whose address is not - actually valid. It means that the generated rtl must all be scanned - to detect and correct the invalid addresses where they occur. */ -static int invalid_stack_slot; - -/* Label to jump back to for tail recursion, or 0 if we have - not yet needed one for this function. */ -static rtx tail_recursion_label; - -/* Place after which to insert the tail_recursion_label if we need one. */ -static rtx tail_recursion_reentry; - -/* Each time we expand an expression-statement, - record the expr's type and its RTL value here. */ - -static tree last_expr_type; -static rtx last_expr_value; - -/* Chain of all RTL_EXPRs that have insns in them. */ -static tree rtl_expr_chain; - -/* Last insn of those whose job was to put parms into their nominal homes. */ -static rtx last_parm_insn; - -/* Functions and data structures for expanding case statements. */ - -/* Case label structure, used to hold info on labels within case - statements. We handle "range" labels; for a single-value label - as in C, the high and low limits are the same. */ - -struct case_node -{ - struct case_node *left; - struct case_node *right; - struct case_node *parent; - tree low; - tree high; - tree test_label; - tree code_label; -}; - -typedef struct case_node case_node; -typedef struct case_node *case_node_ptr; - -static void balance_case_nodes (); -static void emit_case_nodes (); -static void group_case_nodes (); -static void emit_jump_if_reachable (); - -/* Stack of control and binding constructs we are currently inside. - - These constructs begin when you call `expand_start_WHATEVER' - and end when you call `expand_end_WHATEVER'. This stack records - info about how the construct began that tells the end-function - what to do. It also may provide information about the construct - to alter the behavior of other constructs within the body. - For example, they may affect the behavior of C `break' and `continue'. - - Each construct gets one `struct nesting' object. - All of these objects are chained through the `all' field. - `nesting_stack' points to the first object (innermost construct). - The position of an entry on `nesting_stack' is in its `depth' field. - - Each type of construct has its own individual stack. - For example, loops have `loop_stack'. Each object points to the - next object of the same type through the `next' field. - - Some constructs are visible to `break' exit-statements and others - are not. Which constructs are visible depends on the language. - Therefore, the data structure allows each construct to be visible - or not, according to the args given when the construct is started. - The construct is visible if the `exit_label' field is non-null. - In that case, the value should be a CODE_LABEL rtx. */ - -struct nesting -{ - struct nesting *all; - struct nesting *next; - int depth; - rtx exit_label; - union - { - /* For conds (if-then and if-then-else statements). */ - struct - { - /* Label on the else-part, if any, else 0. */ - rtx else_label; - /* Label at the end of the whole construct. */ - rtx after_label; - } cond; - /* For loops. */ - struct - { - /* Label at the top of the loop; place to loop back to. */ - rtx start_label; - /* Label at the end of the whole construct. */ - rtx end_label; - /* Label for `continue' statement to jump to; - this is in front of the stepper of the loop. */ - rtx continue_label; - } loop; - /* For variable binding contours. */ - struct - { - /* Sequence number of this binding contour within the function, - in order of entry. */ - int block_start_count; - /* Nonzero => value to restore stack to on exit. */ - rtx stack_level; - /* The NOTE that starts this contour. - Used by expand_goto to check whether the destination - is within each contour or not. */ - rtx first_insn; - /* Innermost containing binding contour that has a stack level. */ - struct nesting *innermost_stack_block; - /* List of cleanups to be run on exit from this contour. - This is a list of expressions to be evaluated. - The TREE_PURPOSE of each link is the ..._DECL node - which the cleanup pertains to. */ - tree cleanups; - /* List of cleanup-lists of blocks containing this block, - as they were at the locus where this block appears. - There is an element for each containing block, - ordered innermost containing block first. - The element's TREE_VALUE is the cleanup-list of that block, - which may be null. */ - tree outer_cleanups; - /* Chain of labels defined inside this binding contour. - For contours that have stack levels or cleanups. */ - struct label_chain *label_chain; - } block; - /* For switch (C) or case (Pascal) statements, - and also for dummies (see `expand_start_case_dummy'). */ - struct - { - /* The insn after which the case dispatch should finally - be emitted. Zero for a dummy. */ - rtx start; - /* A list of case labels, kept in ascending order by value - as the list is built. - During expand_end_case, this list may be rearranged into a - nearly balanced binary tree. */ - struct case_node *case_list; - /* Label to jump to if no case matches. */ - tree default_label; - /* The expression to be dispatched on. */ - tree index_expr; - /* Type that INDEX_EXPR should be converted to. */ - tree nominal_type; - /* Number of range exprs in case statement. */ - short num_ranges; - } case_stmt; - } data; -}; - -/* Chain of all pending binding contours. */ -struct nesting *block_stack; - -/* Chain of all pending binding contours that restore stack levels - or have cleanups. */ -struct nesting *stack_block_stack; - -/* Chain of all pending conditional statements. */ -struct nesting *cond_stack; - -/* Chain of all pending loops. */ -struct nesting *loop_stack; - -/* Chain of all pending case or switch statements. */ -struct nesting *case_stack; - -/* Separate chain including all of the above, - chained through the `all' field. */ -struct nesting *nesting_stack; - -/* Number of entries on nesting_stack now. */ -int nesting_depth; - -/* Pop one of the sub-stacks, such as `loop_stack' or `cond_stack'; - and pop off `nesting_stack' down to the same level. */ - -#define POPSTACK(STACK) \ -do { int initial_depth = nesting_stack->depth; \ - do { struct nesting *this = STACK; \ - STACK = this->next; \ - nesting_stack = this->all; \ - nesting_depth = this->depth; \ - free (this); } \ - while (nesting_depth > initial_depth); } while (0) - -static int warn_if_unused_value (); -static void expand_goto_internal (); -static int expand_fixup (); -static void fixup_gotos (); -static void expand_cleanups (); -static void fixup_cleanups (); -static void expand_null_return_1 (); -static int tail_recursion_args (); -static void fixup_stack_slots (); -static rtx fixup_stack_1 (); -static rtx fixup_memory_subreg (); -static rtx walk_fixup_memory_subreg (); -static void fixup_var_refs (); -static void fixup_var_refs_insns (); -static rtx fixup_var_refs_1 (); -static rtx parm_stack_loc (); -static void optimize_bit_field (); -static void do_jump_if_equal (); - -/* Emit a no-op instruction. */ - -rtx -emit_nop () -{ - rtx last_insn = get_last_insn (); - if (!optimize - && (GET_CODE (last_insn) == CODE_LABEL - || prev_real_insn (last_insn) == 0)) - emit_insn (gen_nop ()); -} - -/* Return the rtx-label that corresponds to a LABEL_DECL, - creating it if necessary. */ - -static rtx -label_rtx (label) - tree label; -{ - if (TREE_CODE (label) != LABEL_DECL) - abort (); - - if (DECL_RTL (label)) - return DECL_RTL (label); - - return DECL_RTL (label) = gen_label_rtx (); -} - -/* Add an unconditional jump to LABEL as the next sequential instruction. */ - -void -emit_jump (label) - rtx label; -{ - do_pending_stack_adjust (); - emit_jump_insn (gen_jump (label)); - emit_barrier (); -} - -/* Handle goto statements and the labels that they can go to. */ - -/* In some cases it is impossible to generate code for a forward goto - until the label definition is seen. This happens when it may be necessary - for the goto to reset the stack pointer: we don't yet know how to do that. - So expand_goto puts an entry on this fixup list. - Each time a binding contour that resets the stack is exited, - we check each fixup. - If the target label has now been defined, we can insert the proper code. */ - -struct goto_fixup -{ - /* Points to following fixup. */ - struct goto_fixup *next; - /* Points to the insn before the jump insn. - If more code must be inserted, it goes after this insn. */ - rtx before_jump; - /* The LABEL_DECL that this jump is jumping to, or 0 - for break, continue or return. */ - tree target; - /* The CODE_LABEL rtx that this is jumping to. */ - rtx target_rtl; - /* Number of binding contours started in current function - before the label reference. */ - int block_start_count; - /* The outermost stack level that should be restored for this jump. - Each time a binding contour that resets the stack is exited, - if the target label is *not* yet defined, this slot is updated. */ - rtx stack_level; - /* List of lists of cleanup expressions to be run by this goto. - There is one element for each block that this goto is within. - The TREE_VALUE contains the cleanup list of that block as of the - time this goto was seen. - The TREE_ADDRESSABLE flag is 1 for a block that has been exited. */ - tree cleanup_list_list; -}; - -static struct goto_fixup *goto_fixup_chain; - -/* Within any binding contour that must restore a stack level, - all labels are recorded with a chain of these structures. */ - -struct label_chain -{ - /* Points to following fixup. */ - struct label_chain *next; - tree label; -}; - -/* Specify the location in the RTL code of a label BODY, - which is a LABEL_DECL tree node. - - This is used for the kind of label that the user can jump to with a - goto statement, and for alternatives of a switch or case statement. - RTL labels generated for loops and conditionals don't go through here; - they are generated directly at the RTL level, by other functions below. - - Note that this has nothing to do with defining label *names*. - Languages vary in how they do that and what that even means. */ - -void -expand_label (body) - tree body; -{ - struct label_chain *p; - - do_pending_stack_adjust (); - emit_label (label_rtx (body)); - - if (stack_block_stack != 0) - { - p = (struct label_chain *) oballoc (sizeof (struct label_chain)); - p->next = stack_block_stack->data.block.label_chain; - stack_block_stack->data.block.label_chain = p; - p->label = body; - } -} - -/* Generate RTL code for a `goto' statement with target label BODY. - BODY should be a LABEL_DECL tree node that was or will later be - defined with `expand_label'. */ - -void -expand_goto (body) - tree body; -{ - expand_goto_internal (body, label_rtx (body), 0); -} - -/* Generate RTL code for a `goto' statement with target label BODY. - LABEL should be a LABEL_REF. - LAST_INSN, if non-0, is the rtx we should consider as the last - insn emitted (for the purposes of cleaning up a return). */ - -static void -expand_goto_internal (body, label, last_insn) - tree body; - rtx label; - rtx last_insn; -{ - struct nesting *block; - rtx stack_level = 0; - - if (GET_CODE (label) != CODE_LABEL) - abort (); - - /* If label has already been defined, we can tell now - whether and how we must alter the stack level. */ - - if (PREV_INSN (label) != 0) - { - /* Find the innermost pending block that contains the label. - (Check containment by comparing insn-uids.) - Then restore the outermost stack level within that block, - and do cleanups of all blocks contained in it. */ - for (block = block_stack; block; block = block->next) - { - if (INSN_UID (block->data.block.first_insn) < INSN_UID (label)) - break; - if (block->data.block.stack_level != 0) - stack_level = block->data.block.stack_level; - /* Execute the cleanups for blocks we are exiting. */ - if (block->data.block.cleanups != 0) - expand_cleanups (block->data.block.cleanups, 0); - } - - if (stack_level) - emit_move_insn (stack_pointer_rtx, stack_level); - - if (body != 0 && TREE_PACKED (body)) - error ("jump to `%s' invalidly jumps into binding contour", - IDENTIFIER_POINTER (DECL_NAME (body))); - } - /* Label not yet defined: may need to put this goto - on the fixup list. */ - else if (! expand_fixup (body, label, last_insn)) - { - /* No fixup needed. Record that the label is the target - of at least one goto that has no fixup. */ - if (body != 0) - TREE_ADDRESSABLE (body) = 1; - } - - emit_jump (label); -} - -/* Generate if necessary a fixup for a goto - whose target label in tree structure (if any) is TREE_LABEL - and whose target in rtl is RTL_LABEL. - - If LAST_INSN is nonzero, we pretend that the jump appears - after insn LAST_INSN instead of at the current point in the insn stream. - - The fixup will be used later to insert insns at this point - to restore the stack level as appropriate for the target label. - - Value is nonzero if a fixup is made. */ - -static int -expand_fixup (tree_label, rtl_label, last_insn) - tree tree_label; - rtx rtl_label; - rtx last_insn; -{ - struct nesting *block, *end_block; - - /* See if we can recognize which block the label will be output in. - This is possible in some very common cases. - If we succeed, set END_BLOCK to that block. - Otherwise, set it to 0. */ - - if (cond_stack - && (rtl_label == cond_stack->data.cond.else_label - || rtl_label == cond_stack->data.cond.after_label)) - end_block = cond_stack; - /* If we are in a loop, recognize certain labels which - are likely targets. This reduces the number of fixups - we need to create. */ - else if (loop_stack - && (rtl_label == loop_stack->data.loop.start_label - || rtl_label == loop_stack->data.loop.end_label - || rtl_label == loop_stack->data.loop.continue_label)) - end_block = loop_stack; - else - end_block = 0; - - /* Now set END_BLOCK to the binding level to which we will return. */ - - if (end_block) - { - struct nesting *next_block = end_block->all; - block = block_stack; - - /* First see if the END_BLOCK is inside the innermost binding level. - If so, then no cleanups or stack levels are relevant. */ - while (next_block && next_block != block) - next_block = next_block->all; - - if (next_block) - return 0; - - /* Otherwise, set END_BLOCK to the innermost binding level - which is outside the relevant control-structure nesting. */ - next_block = block_stack->next; - for (block = block_stack; block != end_block; block = block->all) - if (block == next_block) - next_block = next_block->next; - end_block = next_block; - } - - /* Does any containing block have a stack level or cleanups? - If not, no fixup is needed, and that is the normal case - (the only case, for standard C). */ - for (block = block_stack; block != end_block; block = block->next) - if (block->data.block.stack_level != 0 - || block->data.block.cleanups != 0) - break; - - if (block != end_block) - { - /* Ok, a fixup is needed. Add a fixup to the list of such. */ - struct goto_fixup *fixup - = (struct goto_fixup *) oballoc (sizeof (struct goto_fixup)); - /* In case an old stack level is restored, make sure that comes - after any pending stack adjust. */ - do_pending_stack_adjust (); - fixup->before_jump = last_insn ? last_insn : get_last_insn (); - fixup->target = tree_label; - fixup->target_rtl = rtl_label; - fixup->block_start_count = block_start_count; - fixup->stack_level = 0; - fixup->cleanup_list_list - = (block->data.block.outer_cleanups || block->data.block.cleanups - ? tree_cons (0, block->data.block.cleanups, - block->data.block.outer_cleanups) - : 0); - fixup->next = goto_fixup_chain; - goto_fixup_chain = fixup; - } - - return block != 0; -} - -/* When exiting a binding contour, process all pending gotos requiring fixups. - THISBLOCK is the structure that describes the block being exited. - STACK_LEVEL is the rtx for the stack level to restore exiting this contour. - CLEANUP_LIST is a list of expressions to evaluate on exiting this contour. - FIRST_INSN is the insn that began this contour. - - Gotos that jump out of this contour must restore the - stack level and do the cleanups before actually jumping. - - DONT_JUMP_IN nonzero means report error there is a jump into this - contour from before the beginning of the contour. - This is also done if STACK_LEVEL is nonzero. */ - -static void -fixup_gotos (thisblock, stack_level, cleanup_list, first_insn, dont_jump_in) - struct nesting *thisblock; - rtx stack_level; - tree cleanup_list; - rtx first_insn; - int dont_jump_in; -{ - register struct goto_fixup *f, *prev; - - /* F is the fixup we are considering; PREV is the previous one. */ - - for (prev = 0, f = goto_fixup_chain; f; prev = f, f = f->next) - { - /* Test for a fixup that is inactive because it is already handled. */ - if (f->before_jump == 0) - { - /* Delete inactive fixup from the chain, if that is easy to do. */ - if (prev != 0) - prev->next = f->next; - } - /* Has this fixup's target label been defined? - If so, we can finalize it. */ - else if (PREV_INSN (f->target_rtl) != 0) - { - /* If this fixup jumped into this contour from before the beginning - of this contour, report an error. */ - /* ??? Bug: this does not detect jumping in through intermediate - blocks that have stack levels or cleanups. - It detects only a problem with the innermost block - around the label. */ - if (f->target != 0 - && (dont_jump_in || stack_level || cleanup_list) - && INSN_UID (first_insn) > INSN_UID (f->before_jump) - && ! TREE_ADDRESSABLE (f->target)) - { - error_with_decl (f->target, - "label `%s' used before containing binding contour"); - /* Prevent multiple errors for one label. */ - TREE_ADDRESSABLE (f->target) = 1; - } - - /* Execute cleanups for blocks this jump exits. */ - if (f->cleanup_list_list) - { - tree lists; - for (lists = f->cleanup_list_list; lists; lists = TREE_CHAIN (lists)) - /* Marked elements correspond to blocks that have been closed. - Do their cleanups. */ - if (TREE_ADDRESSABLE (lists) - && TREE_VALUE (lists) != 0) - fixup_cleanups (TREE_VALUE (lists), &f->before_jump); - } - - /* Restore stack level for the biggest contour that this - jump jumps out of. */ - if (f->stack_level) - emit_insn_after (gen_move_insn (stack_pointer_rtx, f->stack_level), - f->before_jump); - f->before_jump = 0; - } - /* Label has still not appeared. If we are exiting a block with - a stack level to restore, that started before the fixup, - mark this stack level as needing restoration - when the fixup is later finalized. - Also mark the cleanup_list_list element for F - that corresponds to this block, so that ultimately - this block's cleanups will be executed by the code above. */ - /* Note: if THISBLOCK == 0 and we have a label that hasn't appeared, - it means the label is undefined. That's erroneous, but possible. */ - else if (thisblock != 0 - && (thisblock->data.block.block_start_count - < f->block_start_count)) - { - tree lists = f->cleanup_list_list; - for (; lists; lists = TREE_CHAIN (lists)) - /* If the following elt. corresponds to our containing block - then the elt. must be for this block. */ - if (TREE_CHAIN (lists) == thisblock->data.block.outer_cleanups) - TREE_ADDRESSABLE (lists) = 1; - - if (stack_level) - f->stack_level = stack_level; - } - } -} - -/* Generate RTL for an asm statement (explicit assembler code). - BODY is a STRING_CST node containing the assembler code text. */ - -void -expand_asm (body) - tree body; -{ - emit_insn (gen_rtx (ASM_INPUT, VOIDmode, - TREE_STRING_POINTER (body))); - last_expr_type = 0; -} - -/* Generate RTL for an asm statement with arguments. - STRING is the instruction template. - OUTPUTS is a list of output arguments (lvalues); INPUTS a list of inputs. - Each output or input has an expression in the TREE_VALUE and - a constraint-string in the TREE_PURPOSE. - CLOBBERS is a list of STRING_CST nodes each naming a hard register - that is clobbered by this insn. - - Not all kinds of lvalue that may appear in OUTPUTS can be stored directly. - Some elements of OUTPUTS may be replaced with trees representing temporary - values. The caller should copy those temporary values to the originally - specified lvalues. - - VOL nonzero means the insn is volatile; don't optimize it. */ - -void -expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line) - tree string, outputs, inputs, clobbers; - int vol; - char *filename; - int line; -{ - rtvec argvec, constraints; - rtx body; - int ninputs = list_length (inputs); - int noutputs = list_length (outputs); - int nclobbers = list_length (clobbers); - tree tail; - register int i; - /* Vector of RTX's of evaluated output operands. */ - rtx *output_rtx = (rtx *) alloca (noutputs * sizeof (rtx)); - /* The insn we have emitted. */ - rtx insn; - - last_expr_type = 0; - - for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++) - { - tree val = TREE_VALUE (tail); - tree val1; - int j; - int found_equal; - - /* If there's an erroneous arg, emit no insn. */ - if (TREE_TYPE (val) == error_mark_node) - return; - - /* Make sure constraint has `=' and does not have `+'. */ - - found_equal = 0; - for (j = 0; j < TREE_STRING_LENGTH (TREE_PURPOSE (tail)); j++) - { - if (TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '+') - { - error ("output operand constraint contains `+'"); - return; - } - if (TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '=') - found_equal = 1; - } - if (! found_equal) - { - error ("output operand constraint lacks `='"); - return; - } - - /* If an output operand is not a variable or indirect ref, - or a part of one, - create a SAVE_EXPR which is a pseudo-reg - to act as an intermediate temporary. - Make the asm insn write into that, then copy it to - the real output operand. */ - - val1 = val; - while (TREE_CODE (val1) == COMPONENT_REF - || TREE_CODE (val1) == ARRAY_REF) - val1 = TREE_OPERAND (val1, 0); - - if (TREE_CODE (val1) != VAR_DECL - && TREE_CODE (val1) != PARM_DECL - && TREE_CODE (val1) != INDIRECT_REF) - { - rtx reg = gen_reg_rtx (TYPE_MODE (TREE_TYPE (val))); - /* `build' isn't safe; it really expects args to be trees. */ - tree t = build_nt (SAVE_EXPR, val, reg); - - if (GET_MODE (reg) == BLKmode) - abort (); - - save_expr_regs = gen_rtx (EXPR_LIST, VOIDmode, reg, save_expr_regs); - TREE_VALUE (tail) = t; - TREE_TYPE (t) = TREE_TYPE (val); - } - output_rtx[i] = expand_expr (TREE_VALUE (tail), 0, VOIDmode, 0); - } - - if (ninputs + noutputs > MAX_RECOG_OPERANDS) - { - error ("more than %d operands in `asm'", MAX_RECOG_OPERANDS); - return; - } - - /* Make vectors for the expression-rtx and constraint strings. */ - - argvec = rtvec_alloc (ninputs); - constraints = rtvec_alloc (ninputs); - - body = gen_rtx (ASM_OPERANDS, VOIDmode, - TREE_STRING_POINTER (string), "", 0, argvec, constraints, - filename, line); - MEM_VOLATILE_P (body) = vol; - - /* Eval the inputs and put them into ARGVEC. - Put their constraints into ASM_INPUTs and store in CONSTRAINTS. */ - - i = 0; - for (tail = inputs; tail; tail = TREE_CHAIN (tail)) - { - int j; - - /* If there's an erroneous arg, emit no insn, - because the ASM_INPUT would get VOIDmode - and that could cause a crash in reload. */ - if (TREE_TYPE (TREE_VALUE (tail)) == error_mark_node) - return; - if (TREE_PURPOSE (tail) == NULL_TREE) - { - error ("hard register `%s' listed as input operand to `asm'", - TREE_STRING_POINTER (TREE_VALUE (tail)) ); - return; - } - - /* Make sure constraint has neither `=' nor `+'. */ - - for (j = 0; j < TREE_STRING_LENGTH (TREE_PURPOSE (tail)); j++) - if (TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '=' - || TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '+') - { - error ("input operand constraint contains `%c'", - TREE_STRING_POINTER (TREE_PURPOSE (tail))[j]); - return; - } - - XVECEXP (body, 3, i) /* argvec */ - = expand_expr (TREE_VALUE (tail), 0, VOIDmode, 0); - XVECEXP (body, 4, i) /* constraints */ - = gen_rtx (ASM_INPUT, TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))), - TREE_STRING_POINTER (TREE_PURPOSE (tail))); - i++; - } - - /* Protect all the operands from the queue, - now that they have all been evaluated. */ - - for (i = 0; i < ninputs; i++) - XVECEXP (body, 3, i) = protect_from_queue (XVECEXP (body, 3, i), 0); - - for (i = 0; i < noutputs; i++) - output_rtx[i] = protect_from_queue (output_rtx[i], 1); - - /* Now, for each output, construct an rtx - (set OUTPUT (asm_operands INSN OUTPUTNUMBER OUTPUTCONSTRAINT - ARGVEC CONSTRAINTS)) - If there is more than one, put them inside a PARALLEL. */ - - if (noutputs == 1 && nclobbers == 0) - { - XSTR (body, 1) = TREE_STRING_POINTER (TREE_PURPOSE (outputs)); - insn = emit_insn (gen_rtx (SET, VOIDmode, output_rtx[0], body)); - } - else if (noutputs == 0 && nclobbers == 0) - { - /* No output operands: put in a raw ASM_OPERANDS rtx. */ - insn = emit_insn (body); - } - else - { - rtx obody = body; - int num = noutputs; - if (num == 0) num = 1; - body = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (num + nclobbers)); - - /* For each output operand, store a SET. */ - - for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++) - { - XVECEXP (body, 0, i) - = gen_rtx (SET, VOIDmode, - output_rtx[i], - gen_rtx (ASM_OPERANDS, VOIDmode, - TREE_STRING_POINTER (string), - TREE_STRING_POINTER (TREE_PURPOSE (tail)), - i, argvec, constraints, - filename, line)); - MEM_VOLATILE_P (SET_SRC (XVECEXP (body, 0, i))) = vol; - } - - /* If there are no outputs (but there are some clobbers) - store the bare ASM_OPERANDS into the PARALLEL. */ - - if (i == 0) - XVECEXP (body, 0, i++) = obody; - - /* Store (clobber REG) for each clobbered register specified. */ - - for (tail = clobbers; tail; tail = TREE_CHAIN (tail), i++) - { - int j; - char *regname = TREE_STRING_POINTER (TREE_VALUE (tail)); - extern char *reg_names[]; - - for (j = 0; j < FIRST_PSEUDO_REGISTER; j++) - if (!strcmp (regname, reg_names[j])) - break; - - if (j == FIRST_PSEUDO_REGISTER) - { - error ("unknown register name `%s' in `asm'", regname); - return; - } - - /* Use QImode since that's guaranteed to clobber just one reg. */ - XVECEXP (body, 0, i) - = gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, QImode, j)); - } - - insn = emit_insn (body); - } - - last_expr_type = 0; -} - -/* Nonzero if within a ({...}) grouping, in which case we must - always compute a value for each expr-stmt in case it is the last one. */ - -int expr_stmts_for_value; - -/* Generate RTL to evaluate the expression EXP - and remember it in case this is the VALUE in a ({... VALUE; }) constr. */ - -void -expand_expr_stmt (exp) - tree exp; -{ - /* If -W, warn about statements with no side effects, - except inside a ({...}) where they may be useful. */ - if (expr_stmts_for_value == 0 && exp != error_mark_node) - { - if (! TREE_VOLATILE (exp) && (extra_warnings || warn_unused)) - warning_with_file_and_line (emit_filename, emit_lineno, - "statement with no effect"); - else if (warn_unused) - warn_if_unused_value (exp); - } - last_expr_type = TREE_TYPE (exp); - if (! flag_syntax_only) - last_expr_value = expand_expr (exp, expr_stmts_for_value ? 0 : const0_rtx, - VOIDmode, 0); - emit_queue (); -} - -/* Warn if EXP contains any computations whose results are not used. - Return 1 if a warning is printed; 0 otherwise. */ - -static int -warn_if_unused_value (exp) - tree exp; -{ - switch (TREE_CODE (exp)) - { - case PREINCREMENT_EXPR: - case POSTINCREMENT_EXPR: - case PREDECREMENT_EXPR: - case POSTDECREMENT_EXPR: - case MODIFY_EXPR: - case INIT_EXPR: - case NEW_EXPR: - case DELETE_EXPR: - case PUSH_EXPR: - case POP_EXPR: - case CALL_EXPR: - case METHOD_CALL_EXPR: - case RTL_EXPR: - case WRAPPER_EXPR: - case ANTI_WRAPPER_EXPR: - case WITH_CLEANUP_EXPR: - /* We don't warn about COND_EXPR because it may be a useful - construct if either arm contains a side effect. */ - case COND_EXPR: - return 0; - - case TRUTH_ORIF_EXPR: - case TRUTH_ANDIF_EXPR: - /* In && or ||, warn if 2nd operand has no side effect. */ - return warn_if_unused_value (TREE_OPERAND (exp, 1)); - - case COMPOUND_EXPR: - if (warn_if_unused_value (TREE_OPERAND (exp, 0))) - return 1; - return warn_if_unused_value (TREE_OPERAND (exp, 1)); - - case NOP_EXPR: - case CONVERT_EXPR: - /* Don't warn about values cast to void. */ - if (TREE_TYPE (exp) == void_type_node) - return 0; - /* Assignment to a cast results in a cast of a modify. - Don't complain about that. */ - if (TREE_CODE (TREE_OPERAND (exp, 0)) == MODIFY_EXPR) - return 0; - - default: - warning_with_file_and_line (emit_filename, emit_lineno, - "value computed is not used"); - return 1; - } -} - -/* Clear out the memory of the last expression evaluated. */ - -void -clear_last_expr () -{ - last_expr_type = 0; -} - -/* Begin a statement which will return a value. - Return the RTL_EXPR for this statement expr. - The caller must save that value and pass it to expand_end_stmt_expr. */ - -tree -expand_start_stmt_expr () -{ - rtx save = start_sequence (); - /* Make the RTL_EXPR node temporary, not momentary, - so that rtl_expr_chain doesn't become garbage. */ - int momentary = suspend_momentary (); - tree t = make_node (RTL_EXPR); - resume_momentary (momentary); - RTL_EXPR_RTL (t) = save; - NO_DEFER_POP; - expr_stmts_for_value++; - return t; -} - -/* Restore the previous state at the end of a statement that returns a value. - Returns a tree node representing the statement's value and the - insns to compute the value. - - The nodes of that expression have been freed by now, so we cannot use them. - But we don't want to do that anyway; the expression has already been - evaluated and now we just want to use the value. So generate a RTL_EXPR - with the proper type and RTL value. - - If the last substatement was not an expression, - return something with type `void'. */ - -tree -expand_end_stmt_expr (t) - tree t; -{ - rtx saved = RTL_EXPR_RTL (t); - - OK_DEFER_POP; - - if (last_expr_type == 0) - { - last_expr_type = void_type_node; - last_expr_value = const0_rtx; - } - TREE_TYPE (t) = last_expr_type; - RTL_EXPR_RTL (t) = last_expr_value; - RTL_EXPR_SEQUENCE (t) = get_insns (); - - rtl_expr_chain = tree_cons (NULL_TREE, t, rtl_expr_chain); - - end_sequence (saved); - - /* Don't consider deleting this expr or containing exprs at tree level. */ - TREE_VOLATILE (t) = 1; - /* Propagate volatility of the actual RTL expr. */ - TREE_THIS_VOLATILE (t) = volatile_refs_p (last_expr_value); - - last_expr_type = 0; - expr_stmts_for_value--; - - return t; -} - -/* Generate RTL for the start of an if-then. COND is the expression - whose truth should be tested. - - If EXITFLAG is nonzero, this conditional is visible to - `exit_something'. */ - -void -expand_start_cond (cond, exitflag) - tree cond; - int exitflag; -{ - struct nesting *thiscond - = (struct nesting *) xmalloc (sizeof (struct nesting)); - - /* Make an entry on cond_stack for the cond we are entering. */ - - thiscond->next = cond_stack; - thiscond->all = nesting_stack; - thiscond->depth = ++nesting_depth; - thiscond->data.cond.after_label = 0; - thiscond->data.cond.else_label = gen_label_rtx (); - thiscond->exit_label = exitflag ? thiscond->data.cond.else_label : 0; - cond_stack = thiscond; - nesting_stack = thiscond; - - do_jump (cond, thiscond->data.cond.else_label, NULL); -} - -/* Generate RTL for the end of an if-then with no else-clause. - Pop the record for it off of cond_stack. */ - -void -expand_end_cond () -{ - struct nesting *thiscond = cond_stack; - - do_pending_stack_adjust (); - emit_label (thiscond->data.cond.else_label); - - POPSTACK (cond_stack); - last_expr_type = 0; -} - -/* Generate RTL between the then-clause and the else-clause - of an if-then-else. */ - -void -expand_start_else () -{ - cond_stack->data.cond.after_label = gen_label_rtx (); - if (cond_stack->exit_label != 0) - cond_stack->exit_label = cond_stack->data.cond.after_label; - emit_jump (cond_stack->data.cond.after_label); - if (cond_stack->data.cond.else_label) - emit_label (cond_stack->data.cond.else_label); -} - -/* Generate RTL for the end of an if-then-else. - Pop the record for it off of cond_stack. */ - -void -expand_end_else () -{ - struct nesting *thiscond = cond_stack; - - do_pending_stack_adjust (); - /* Note: a syntax error can cause this to be called - without first calling `expand_start_else'. */ - if (thiscond->data.cond.after_label) - emit_label (thiscond->data.cond.after_label); - - POPSTACK (cond_stack); - last_expr_type = 0; -} - -/* Generate RTL for the start of a loop. EXIT_FLAG is nonzero if this - loop should be exited by `exit_something'. This is a loop for which - `expand_continue' will jump to the top of the loop. - - Make an entry on loop_stack to record the labels associated with - this loop. */ - -void -expand_start_loop (exit_flag) - int exit_flag; -{ - register struct nesting *thisloop - = (struct nesting *) xmalloc (sizeof (struct nesting)); - - /* Make an entry on loop_stack for the loop we are entering. */ - - thisloop->next = loop_stack; - thisloop->all = nesting_stack; - thisloop->depth = ++nesting_depth; - thisloop->data.loop.start_label = gen_label_rtx (); - thisloop->data.loop.end_label = gen_label_rtx (); - thisloop->data.loop.continue_label = thisloop->data.loop.start_label; - thisloop->exit_label = exit_flag ? thisloop->data.loop.end_label : 0; - loop_stack = thisloop; - nesting_stack = thisloop; - - do_pending_stack_adjust (); - emit_queue (); - emit_note (0, NOTE_INSN_LOOP_BEG); - emit_label (thisloop->data.loop.start_label); -} - -/* Like expand_start_loop but for a loop where the continuation point - (for expand_continue_loop) will be specified explicitly. */ - -void -expand_start_loop_continue_elsewhere (exit_flag) - int exit_flag; -{ - expand_start_loop (exit_flag); - loop_stack->data.loop.continue_label = gen_label_rtx (); -} - -/* Specify the continuation point for a loop started with - expand_start_loop_continue_elsewhere. - Use this at the point in the code to which a continue statement - should jump. */ - -void -expand_loop_continue_here () -{ - do_pending_stack_adjust (); - emit_note (0, NOTE_INSN_LOOP_CONT); - emit_label (loop_stack->data.loop.continue_label); -} - -/* Finish a loop. Generate a jump back to the top and the loop-exit label. - Pop the block off of loop_stack. */ - -void -expand_end_loop () -{ - register rtx insn = get_last_insn (); - register rtx start_label = loop_stack->data.loop.start_label; - - do_pending_stack_adjust (); - - /* If optimizing, perhaps reorder the loop. If the loop - starts with a conditional exit, roll that to the end - where it will optimize together with the jump back. */ - if (optimize - && - ! (GET_CODE (insn) == JUMP_INSN - && GET_CODE (PATTERN (insn)) == SET - && SET_DEST (PATTERN (insn)) == pc_rtx - && GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE)) - { - /* Scan insns from the top of the loop looking for a qualified - conditional exit. */ - for (insn = loop_stack->data.loop.start_label; insn; insn= NEXT_INSN (insn)) - if (GET_CODE (insn) == JUMP_INSN && GET_CODE (PATTERN (insn)) == SET - && SET_DEST (PATTERN (insn)) == pc_rtx - && GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE - && - ((GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 1)) == LABEL_REF - && (XEXP (XEXP (SET_SRC (PATTERN (insn)), 1), 0) - == loop_stack->data.loop.end_label)) - || - (GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 2)) == LABEL_REF - && (XEXP (XEXP (SET_SRC (PATTERN (insn)), 2), 0) - == loop_stack->data.loop.end_label)))) - break; - if (insn != 0) - { - /* We found one. Move everything from there up - to the end of the loop, and add a jump into the loop - to jump to there. */ - register rtx newstart_label = gen_label_rtx (); - - emit_label_after (newstart_label, PREV_INSN (start_label)); - reorder_insns (start_label, insn, get_last_insn ()); - emit_jump_insn_after (gen_jump (start_label), PREV_INSN (newstart_label)); - emit_barrier_after (PREV_INSN (newstart_label)); - start_label = newstart_label; - } - } - - emit_jump (start_label); - emit_note (0, NOTE_INSN_LOOP_END); - emit_label (loop_stack->data.loop.end_label); - - POPSTACK (loop_stack); - - last_expr_type = 0; -} - -/* Generate a jump to the current loop's continue-point. - This is usually the top of the loop, but may be specified - explicitly elsewhere. If not currently inside a loop, - return 0 and do nothing; caller will print an error message. */ - -int -expand_continue_loop () -{ - last_expr_type = 0; - if (loop_stack == 0) - return 0; - expand_goto_internal (0, loop_stack->data.loop.continue_label, 0); - return 1; -} - -/* Generate a jump to exit the current loop. If not currently inside a loop, - return 0 and do nothing; caller will print an error message. */ - -int -expand_exit_loop () -{ - last_expr_type = 0; - if (loop_stack == 0) - return 0; - expand_goto_internal (0, loop_stack->data.loop.end_label, 0); - return 1; -} - -/* Generate a conditional jump to exit the current loop if COND - evaluates to zero. If not currently inside a loop, - return 0 and do nothing; caller will print an error message. */ - -int -expand_exit_loop_if_false (cond) - tree cond; -{ - last_expr_type = 0; - if (loop_stack == 0) - return 0; - do_jump (cond, loop_stack->data.loop.end_label, NULL); - return 1; -} - -/* Return non-zero if currently inside a loop. */ - -int -inside_loop () -{ - return loop_stack != 0; -} - -/* Generate a jump to exit the current loop, conditional, binding contour - or case statement. Not all such constructs are visible to this function, - only those started with EXIT_FLAG nonzero. Individual languages use - the EXIT_FLAG parameter to control which kinds of constructs you can - exit this way. - - If not currently inside anything that can be exited, - return 0 and do nothing; caller will print an error message. */ - -int -expand_exit_something () -{ - struct nesting *n; - last_expr_type = 0; - for (n = nesting_stack; n; n = n->all) - if (n->exit_label != 0) - { - expand_goto_internal (0, n->exit_label, 0); - return 1; - } - - return 0; -} - -/* Generate RTL to return from the current function, with no value. - (That is, we do not do anything about returning any value.) */ - -void -expand_null_return () -{ - struct nesting *block = block_stack; - rtx last_insn = 0; - - /* Does any pending block have cleanups? */ - - while (block && block->data.block.cleanups == 0) - block = block->next; - - /* If yes, use a goto to return, since that runs cleanups. */ - - expand_null_return_1 (last_insn, block != 0); -} - -/* Output a return with no value. If LAST_INSN is nonzero, - pretend that the return takes place after LAST_INSN. - If USE_GOTO is nonzero then don't use a return instruction; - go to the return label instead. This causes any cleanups - of pending blocks to be executed normally. */ - -static void -expand_null_return_1 (last_insn, use_goto) - rtx last_insn; - int use_goto; -{ - rtx end_label = cleanup_label ? cleanup_label : return_label; - - clear_pending_stack_adjust (); - do_pending_stack_adjust (); - last_expr_type = 0; - - /* PCC-struct return always uses an epilogue. */ - if (current_function_returns_pcc_struct || use_goto) - { - if (end_label == 0) - end_label = return_label = gen_label_rtx (); - expand_goto_internal (0, end_label, last_insn); - return; - } - - /* Otherwise output a simple return-insn if one is available, - unless it won't do the job. */ -#ifdef HAVE_return - if (HAVE_return && cleanup_label == 0) - { - emit_jump_insn (gen_return ()); - emit_barrier (); - return; - } -#endif - - /* Otherwise jump to the epilogue. */ - expand_goto_internal (0, end_label, last_insn); -} - -/* Generate RTL to evaluate the expression RETVAL and return it - from the current function. */ - -void -expand_return (retval) - tree retval; -{ - /* If there are any cleanups to be performed, then they will - be inserted following LAST_INSN. It is desirable - that the last_insn, for such purposes, should be the - last insn before computing the return value. Otherwise, cleanups - which call functions can clobber the return value. */ - /* ??? rms: I think that is erroneous, because in C++ it would - run destructors on variables that might be used in the subsequent - computation of the return value. */ - rtx last_insn = 0; - register rtx val = 0; - register rtx op0; - tree retval_rhs; - int cleanups; - struct nesting *block; - - /* Are any cleanups needed? E.g. C++ destructors to be run? */ - cleanups = 0; - for (block = block_stack; block; block = block->next) - if (block->data.block.cleanups != 0) - { - cleanups = 1; - break; - } - - if (TREE_CODE (retval) == RESULT_DECL) - retval_rhs = retval; - else if ((TREE_CODE (retval) == MODIFY_EXPR || TREE_CODE (retval) == INIT_EXPR) - && TREE_CODE (TREE_OPERAND (retval, 0)) == RESULT_DECL) - retval_rhs = TREE_OPERAND (retval, 1); - else if (TREE_TYPE (retval) == void_type_node) - /* Recognize tail-recursive call to void function. */ - retval_rhs = retval; - else - retval_rhs = NULL_TREE; - - /* Only use `last_insn' if there are cleanups which must be run. */ - if (cleanups || cleanup_label != 0) - last_insn = get_last_insn (); - - /* For tail-recursive call to current function, - just jump back to the beginning. - It's unsafe if any auto variable in this function - has its address taken; for simplicity, - require stack frame to be empty. */ - if (optimize && retval_rhs != 0 - && frame_offset == STARTING_FRAME_OFFSET - && TREE_CODE (retval_rhs) == CALL_EXPR - && TREE_CODE (TREE_OPERAND (retval_rhs, 0)) == ADDR_EXPR - && TREE_OPERAND (TREE_OPERAND (retval_rhs, 0), 0) == this_function - /* Finish checking validity, and if valid emit code - to set the argument variables for the new call. */ - && tail_recursion_args (TREE_OPERAND (retval_rhs, 1), - DECL_ARGUMENTS (this_function))) - { - if (tail_recursion_label == 0) - { - tail_recursion_label = gen_label_rtx (); - emit_label_after (tail_recursion_label, - tail_recursion_reentry); - } - expand_goto_internal (0, tail_recursion_label, last_insn); - emit_barrier (); - return; - } -#ifdef HAVE_return - /* This optimization is safe if there are local cleanups - because expand_null_return takes care of them. - ??? I think it should also be safe when there is a cleanup label, - because expand_null_return takes care of them, too. - Any reason why not? */ - if (HAVE_return && cleanup_label == 0 - && ! current_function_returns_pcc_struct) - { - /* If this is return x == y; then generate - if (x == y) return 1; else return 0; - if we can do it with explicit return insns. */ - if (retval_rhs) - switch (TREE_CODE (retval_rhs)) - { - case EQ_EXPR: - case NE_EXPR: - case GT_EXPR: - case GE_EXPR: - case LT_EXPR: - case LE_EXPR: - case TRUTH_ANDIF_EXPR: - case TRUTH_ORIF_EXPR: - case TRUTH_AND_EXPR: - case TRUTH_OR_EXPR: - case TRUTH_NOT_EXPR: - op0 = gen_label_rtx (); - val = DECL_RTL (DECL_RESULT (this_function)); - jumpifnot (retval_rhs, op0); - emit_move_insn (val, const1_rtx); - emit_insn (gen_rtx (USE, VOIDmode, val)); - expand_null_return (); - emit_label (op0); - emit_move_insn (val, const0_rtx); - emit_insn (gen_rtx (USE, VOIDmode, val)); - expand_null_return (); - return; - } - } -#endif /* HAVE_return */ - - if (cleanups - && retval_rhs != 0 - && TREE_TYPE (retval_rhs) != void_type_node - && GET_CODE (DECL_RTL (DECL_RESULT (this_function))) == REG) - { - rtx last_insn; - /* Calculate the return value into a pseudo reg. */ - val = expand_expr (retval_rhs, 0, VOIDmode, 0); - emit_queue (); - /* Put the cleanups here. */ - last_insn = get_last_insn (); - /* Copy the value into hard return reg. */ - emit_move_insn (DECL_RTL (DECL_RESULT (this_function)), val); - val = DECL_RTL (DECL_RESULT (this_function)); - - if (GET_CODE (val) == REG) - emit_insn (gen_rtx (USE, VOIDmode, val)); - expand_null_return_1 (last_insn, cleanups); - } - else - { - /* No cleanups or no hard reg used; - calculate value into hard return reg - and let cleanups come after. */ - val = expand_expr (retval, 0, VOIDmode, 0); - emit_queue (); - - val = DECL_RTL (DECL_RESULT (this_function)); - if (val && GET_CODE (val) == REG) - emit_insn (gen_rtx (USE, VOIDmode, val)); - expand_null_return (); - } -} - -/* Return 1 if the end of the generated RTX is not a barrier. - This means code already compiled can drop through. */ - -int -drop_through_at_end_p () -{ - rtx insn = get_last_insn (); - while (insn && GET_CODE (insn) == NOTE) - insn = PREV_INSN (insn); - return insn && GET_CODE (insn) != BARRIER; -} - -/* Emit code to alter this function's formal parms for a tail-recursive call. - ACTUALS is a list of actual parameter expressions (chain of TREE_LISTs). - FORMALS is the chain of decls of formals. - Return 1 if this can be done; - otherwise return 0 and do not emit any code. */ - -static int -tail_recursion_args (actuals, formals) - tree actuals, formals; -{ - register tree a = actuals, f = formals; - register int i; - register rtx *argvec; - - /* Check that number and types of actuals are compatible - with the formals. This is not always true in valid C code. - Also check that no formal needs to be addressable - and that all formals are scalars. */ - - /* Also count the args. */ - - for (a = actuals, f = formals, i = 0; a && f; a = TREE_CHAIN (a), f = TREE_CHAIN (f), i++) - { - if (TREE_TYPE (TREE_VALUE (a)) != TREE_TYPE (f)) - return 0; - if (GET_CODE (DECL_RTL (f)) != REG || DECL_MODE (f) == BLKmode) - return 0; - } - if (a != 0 || f != 0) - return 0; - - /* Compute all the actuals. */ - - argvec = (rtx *) alloca (i * sizeof (rtx)); - - for (a = actuals, i = 0; a; a = TREE_CHAIN (a), i++) - argvec[i] = expand_expr (TREE_VALUE (a), 0, VOIDmode, 0); - - /* Find which actual values refer to current values of previous formals. - Copy each of them now, before any formal is changed. */ - - for (a = actuals, i = 0; a; a = TREE_CHAIN (a), i++) - { - int copy = 0; - register int j; - for (f = formals, j = 0; j < i; f = TREE_CHAIN (f), j++) - if (reg_mentioned_p (DECL_RTL (f), argvec[i])) - { copy = 1; break; } - if (copy) - argvec[i] = copy_to_reg (argvec[i]); - } - - /* Store the values of the actuals into the formals. */ - - for (f = formals, a = actuals, i = 0; f; - f = TREE_CHAIN (f), a = TREE_CHAIN (a), i++) - { - if (DECL_MODE (f) == GET_MODE (argvec[i])) - emit_move_insn (DECL_RTL (f), argvec[i]); - else - convert_move (DECL_RTL (f), argvec[i], - TREE_UNSIGNED (TREE_TYPE (TREE_VALUE (a)))); - } - - return 1; -} - -/* Generate the RTL code for entering a binding contour. - The variables are declared one by one, by calls to `expand_decl'. - - EXIT_FLAG is nonzero if this construct should be visible to - `exit_something'. */ - -void -expand_start_bindings (exit_flag) - int exit_flag; -{ - struct nesting *thisblock - = (struct nesting *) xmalloc (sizeof (struct nesting)); - - rtx note = emit_note (0, NOTE_INSN_BLOCK_BEG); - - /* Make an entry on block_stack for the block we are entering. */ - - thisblock->next = block_stack; - thisblock->all = nesting_stack; - thisblock->depth = ++nesting_depth; - thisblock->data.block.stack_level = 0; - thisblock->data.block.cleanups = 0; - /* We build this even if the cleanups lists are empty - because we rely on having an element in the chain - for each block that is pending. */ - thisblock->data.block.outer_cleanups - = (block_stack - ? tree_cons (NULL_TREE, block_stack->data.block.cleanups, - block_stack->data.block.outer_cleanups) - : 0); - thisblock->data.block.label_chain = 0; - thisblock->data.block.innermost_stack_block = stack_block_stack; - thisblock->data.block.first_insn = note; - thisblock->data.block.block_start_count = ++block_start_count; - thisblock->exit_label = exit_flag ? gen_label_rtx () : 0; - - block_stack = thisblock; - nesting_stack = thisblock; -} - -/* Output a USE for any register use in RTL. - This is used with -noreg to mark the extent of lifespan - of any registers used in a user-visible variable's DECL_RTL. */ - -void -use_variable (rtl) - rtx rtl; -{ - if (GET_CODE (rtl) == REG) - /* This is a register variable. */ - emit_insn (gen_rtx (USE, VOIDmode, rtl)); - else if (GET_CODE (rtl) == MEM - && GET_CODE (XEXP (rtl, 0)) == REG - && XEXP (rtl, 0) != frame_pointer_rtx - && XEXP (rtl, 0) != arg_pointer_rtx) - /* This is a variable-sized structure. */ - emit_insn (gen_rtx (USE, VOIDmode, XEXP (rtl, 0))); -} - -/* Like use_variable except that it outputs the USEs after INSN - instead of at the end of the insn-chain. */ - -static void -use_variable_after (rtl, insn) - rtx rtl, insn; -{ - if (GET_CODE (rtl) == REG) - /* This is a register variable. */ - emit_insn_after (gen_rtx (USE, VOIDmode, rtl), insn); - else if (GET_CODE (rtl) == MEM - && GET_CODE (XEXP (rtl, 0)) == REG - && XEXP (rtl, 0) != frame_pointer_rtx - && XEXP (rtl, 0) != arg_pointer_rtx) - /* This is a variable-sized structure. */ - emit_insn_after (gen_rtx (USE, VOIDmode, XEXP (rtl, 0)), insn); -} - -/* Generate RTL code to terminate a binding contour. - VARS is the chain of VAR_DECL nodes - for the variables bound in this contour. - MARK_ENDS is nonzero if we should put a note at the beginning - and end of this binding contour. - - DONT_JUMP_IN is nonzero if it is not valid to jump into this contour. - (That is true automatically if the contour has a saved stack level.) */ - -void -expand_end_bindings (vars, mark_ends, dont_jump_in) - tree vars; - int mark_ends; - int dont_jump_in; -{ - register struct nesting *thisblock = block_stack; - register tree decl; - - if (warn_unused) - for (decl = vars; decl; decl = TREE_CHAIN (decl)) - if (! TREE_USED (decl) && TREE_CODE (decl) == VAR_DECL) - warning_with_decl (decl, "unused variable `%s'"); - - /* Mark the beginning and end of the scope if requested. */ - - if (mark_ends) - emit_note (0, NOTE_INSN_BLOCK_END); - else - /* Get rid of the beginning-mark if we don't make an end-mark. */ - NOTE_LINE_NUMBER (thisblock->data.block.first_insn) = NOTE_INSN_DELETED; - - if (thisblock->exit_label) - { - do_pending_stack_adjust (); - emit_label (thisblock->exit_label); - } - - if (dont_jump_in - || thisblock->data.block.stack_level != 0 - || thisblock->data.block.cleanups != 0) - { - struct label_chain *chain; - - /* Any labels in this block are no longer valid to go to. - Mark them to cause an error message. */ - for (chain = thisblock->data.block.label_chain; chain; chain = chain->next) - { - TREE_PACKED (chain->label) = 1; - /* If any goto without a fixup came to this label, - that must be an error, because gotos without fixups - come from outside all saved stack-levels and all cleanups. */ - if (TREE_ADDRESSABLE (chain->label)) - error_with_decl (chain->label, - "label `%s' used before containing binding contour"); - } - } - - /* Restore stack level in effect before the block - (only if variable-size objects allocated). */ - - if (thisblock->data.block.stack_level != 0 - || thisblock->data.block.cleanups != 0) - { - /* Perform any cleanups associated with the block. */ - - expand_cleanups (thisblock->data.block.cleanups, 0); - - /* Restore the stack level. */ - - if (thisblock->data.block.stack_level != 0) - { - do_pending_stack_adjust (); - emit_move_insn (stack_pointer_rtx, - thisblock->data.block.stack_level); - } - - /* Any gotos out of this block must also do these things. - Also report any gotos with fixups that came to labels in this level. */ - fixup_gotos (thisblock, - thisblock->data.block.stack_level, - thisblock->data.block.cleanups, - thisblock->data.block.first_insn, - dont_jump_in); - } - - /* If doing stupid register allocation, make sure lives of all - register variables declared here extend thru end of scope. */ - - if (obey_regdecls) - for (decl = vars; decl; decl = TREE_CHAIN (decl)) - { - rtx rtl = DECL_RTL (decl); - if (TREE_CODE (decl) == VAR_DECL && rtl != 0) - use_variable (rtl); - } - - /* Restore block_stack level for containing block. */ - - stack_block_stack = thisblock->data.block.innermost_stack_block; - POPSTACK (block_stack); -} - -/* Generate RTL for the automatic variable declaration DECL. - (Other kinds of declarations are simply ignored if seen here.) - CLEANUP is an expression to be executed at exit from this binding contour; - for example, in C++, it might call the destructor for this variable. - - If CLEANUP contains any SAVE_EXPRs, then you must preevaluate them - either before or after calling `expand_decl' but before compiling - any subsequent expressions. This is because CLEANUP may be expanded - more than once, on different branches of execution. - For the same reason, CLEANUP may not contain a CALL_EXPR - except as its topmost node--else `preexpand_calls' would get confused. - - If CLEANUP is nonzero and DECL is zero, we record a cleanup - that is not associated with any particular variable. - - There is no special support here for C++ constructors. - They should be handled by the proper code in DECL_INITIAL. */ - -void -expand_decl (decl, cleanup) - register tree decl; - tree cleanup; -{ - struct nesting *thisblock = block_stack; - tree type; - - /* Record the cleanup if there is one. */ - - if (cleanup != 0) - { - thisblock->data.block.cleanups - = temp_tree_cons (decl, cleanup, thisblock->data.block.cleanups); - /* If this block has a cleanup, it belongs in stack_block_stack. */ - stack_block_stack = thisblock; - } - - if (decl == NULL_TREE) - { - /* This was a cleanup with no variable. */ - if (cleanup == 0) - abort (); - return; - } - - type = TREE_TYPE (decl); - - /* Aside from that, only automatic variables need any expansion done. - Static and external variables, and external functions, - will be handled by `assemble_variable' (called from finish_decl). - TYPE_DECL and CONST_DECL require nothing. - PARM_DECLs are handled in `assign_parms'. */ - - if (TREE_CODE (decl) != VAR_DECL) - return; - if (TREE_STATIC (decl) || TREE_EXTERNAL (decl)) - return; - - /* Create the RTL representation for the variable. */ - - if (type == error_mark_node) - DECL_RTL (decl) = gen_rtx (MEM, BLKmode, const0_rtx); - else if (DECL_SIZE (decl) == 0) - /* Variable with incomplete type. */ - { - if (DECL_INITIAL (decl) == 0) - /* Error message was already done; now avoid a crash. */ - DECL_RTL (decl) = assign_stack_local (DECL_MODE (decl), 0); - else - /* An initializer is going to decide the size of this array. - Until we know the size, represent its address with a reg. */ - DECL_RTL (decl) = gen_rtx (MEM, BLKmode, gen_reg_rtx (Pmode)); - } - else if (DECL_MODE (decl) != BLKmode - /* If -ffloat-store, don't put explicit float vars - into regs. */ - && !(flag_float_store - && TREE_CODE (type) == REAL_TYPE) - && ! TREE_VOLATILE (decl) - && ! TREE_ADDRESSABLE (decl) - && (TREE_REGDECL (decl) || ! obey_regdecls)) - { - /* Automatic variable that can go in a register. */ - DECL_RTL (decl) = gen_reg_rtx (DECL_MODE (decl)); - if (TREE_CODE (type) == POINTER_TYPE) - mark_reg_pointer (DECL_RTL (decl)); - REG_USERVAR_P (DECL_RTL (decl)) = 1; - } - else if (TREE_LITERAL (DECL_SIZE (decl))) - { - rtx oldaddr = 0; - rtx addr; - - /* If we previously made RTL for this decl, it must be an array - whose size was determined by the initializer. - The old address was a register; set that register now - to the proper address. */ - if (DECL_RTL (decl) != 0) - { - if (GET_CODE (DECL_RTL (decl)) != MEM - || GET_CODE (XEXP (DECL_RTL (decl), 0)) != REG) - abort (); - oldaddr = XEXP (DECL_RTL (decl), 0); - } - - /* Variable of fixed size that goes on the stack. */ - DECL_RTL (decl) - = assign_stack_local (DECL_MODE (decl), - (TREE_INT_CST_LOW (DECL_SIZE (decl)) - * DECL_SIZE_UNIT (decl) - + BITS_PER_UNIT - 1) - / BITS_PER_UNIT); - if (oldaddr) - { - addr = force_operand (XEXP (DECL_RTL (decl), 0), oldaddr); - emit_move_insn (oldaddr, addr); - } - - /* If this is a memory ref that contains aggregate components, - mark it as such for cse and loop optimize. */ - MEM_IN_STRUCT_P (DECL_RTL (decl)) - = (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE); -#if 0 - /* If this is in memory because of -ffloat-store, - set the volatile bit, to prevent optimizations from - undoing the effects. */ - if (flag_float_store && TREE_CODE (type) == REAL_TYPE) - MEM_VOLATILE_P (DECL_RTL (decl)) = 1; -#endif - } - else - /* Dynamic-size object: must push space on the stack. */ - { - rtx address, size; - - frame_pointer_needed = 1; - - /* Record the stack pointer on entry to block, if have - not already done so. */ - if (thisblock->data.block.stack_level == 0) - { - do_pending_stack_adjust (); - thisblock->data.block.stack_level - = copy_to_reg (stack_pointer_rtx); - stack_block_stack = thisblock; - } - - /* Compute the variable's size, in bytes. */ - size = expand_expr (convert_units (DECL_SIZE (decl), - DECL_SIZE_UNIT (decl), - BITS_PER_UNIT), - 0, VOIDmode, 0); - - /* Round it up to this machine's required stack boundary. */ -#ifdef STACK_BOUNDARY - /* Avoid extra code if we can prove it's a multiple already. */ - if (DECL_SIZE_UNIT (decl) % STACK_BOUNDARY) - { -#ifdef STACK_POINTER_OFFSET - /* Avoid extra code if we can prove that adding STACK_POINTER_OFFSET - will not give this address invalid alignment. */ - if (DECL_ALIGN (decl) > ((STACK_POINTER_OFFSET * BITS_PER_UNIT) % STACK_BOUNDARY)) - size = plus_constant (size, - STACK_POINTER_OFFSET % (STACK_BOUNDARY / BITS_PER_UNIT)); -#endif - size = round_push (size); - } -#endif /* STACK_BOUNDARY */ - - /* Make space on the stack, and get an rtx for the address of it. */ -#ifdef STACK_GROWS_DOWNWARD - anti_adjust_stack (size); -#endif - address = copy_to_reg (stack_pointer_rtx); -#ifdef STACK_POINTER_OFFSET - { - /* If the contents of the stack pointer reg are offset from the - actual top-of-stack address, add the offset here. */ - rtx sp_offset = gen_rtx (CONST_INT, VOIDmode, STACK_POINTER_OFFSET); -#ifdef STACK_BOUNDARY -#ifdef STACK_GROWS_DOWNWARD - int direction = 1; -#else /* not STACK_GROWS_DOWNWARD */ - int direction = 0; -#endif /* not STACK_GROWS_DOWNWARD */ - if (DECL_ALIGN (decl) > ((STACK_POINTER_OFFSET * BITS_PER_UNIT) % STACK_BOUNDARY)) - sp_offset = plus_constant (sp_offset, - (STACK_POINTER_OFFSET - % (STACK_BOUNDARY / BITS_PER_UNIT) - * direction)); -#endif /* STACK_BOUNDARY */ - emit_insn (gen_add2_insn (address, sp_offset)); - } -#endif /* STACK_POINTER_OFFSET */ -#ifndef STACK_GROWS_DOWNWARD - anti_adjust_stack (size); -#endif - - /* Some systems require a particular insn to refer to the stack - to make the pages exist. */ -#ifdef HAVE_probe - if (HAVE_probe) - emit_insn (gen_probe ()); -#endif - - /* Reference the variable indirect through that rtx. */ - DECL_RTL (decl) = gen_rtx (MEM, DECL_MODE (decl), address); - } - - if (TREE_VOLATILE (decl)) - MEM_VOLATILE_P (DECL_RTL (decl)) = 1; - if (TREE_READONLY (decl)) - RTX_UNCHANGING_P (DECL_RTL (decl)) = 1; - - /* If doing stupid register allocation, make sure life of any - register variable starts here, at the start of its scope. */ - - if (obey_regdecls) - use_variable (DECL_RTL (decl)); -} - -/* Emit code to perform the initialization of a declaration DECL. */ - -void -expand_decl_init (decl) - tree decl; -{ - if (TREE_STATIC (decl)) - return; - - /* Compute and store the initial value now. */ - - if (DECL_INITIAL (decl) == error_mark_node) - { - enum tree_code code = TREE_CODE (TREE_TYPE (decl)); - if (code == INTEGER_TYPE || code == REAL_TYPE || code == ENUMERAL_TYPE - || code == POINTER_TYPE) - expand_assignment (decl, convert (TREE_TYPE (decl), integer_zero_node), - 0, 0); - emit_queue (); - } - else if (DECL_INITIAL (decl) && TREE_CODE (DECL_INITIAL (decl)) != TREE_LIST) - { - emit_line_note (DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl)); - expand_assignment (decl, DECL_INITIAL (decl), 0, 0); - emit_queue (); - } -} - -/* DECL is an anonymous union. CLEANUP is a cleanup for DECL. - DECL_ELTS is the list of elements that belong to DECL's type. - In each, the TREE_VALUE is a VAR_DECL, and the TREE_PURPOSE a cleanup. */ - -void -expand_anon_union_decl (decl, cleanup, decl_elts) - tree decl, cleanup, decl_elts; -{ - struct nesting *thisblock = block_stack; - rtx x; - - expand_decl (decl, cleanup); - x = DECL_RTL (decl); - - while (decl_elts) - { - tree decl_elt = TREE_VALUE (decl_elts); - tree cleanup_elt = TREE_PURPOSE (decl_elts); - - DECL_RTL (decl_elt) - = (GET_MODE (x) != BLKmode -/* -#error broken -/* ??? This is incorrect if X is a MEM. - (SUBREG (MEM)) is not allowed at rtl generation time. */ - ? gen_rtx (SUBREG, TYPE_MODE (TREE_TYPE (decl_elt)), x, 0) - : x); - - /* Record the cleanup if there is one. */ - - if (cleanup != 0) - thisblock->data.block.cleanups - = temp_tree_cons (decl_elt, cleanup_elt, - thisblock->data.block.cleanups); - - decl_elts = TREE_CHAIN (decl_elts); - } -} - -/* Expand a list of cleanups LIST. - Elements may be expressions or may be nested lists. - - If DONT_DO is nonnull, then any list-element - whose TREE_PURPOSE matches DONT_DO is omitted. - This is sometimes used to avoid a cleanup associated with - a value that is being returned out of the scope. */ - -static void -expand_cleanups (list, dont_do) - tree list; - tree dont_do; -{ - tree tail; - for (tail = list; tail; tail = TREE_CHAIN (tail)) - if (dont_do == 0 || TREE_PURPOSE (tail) != dont_do) - { - if (TREE_CODE (TREE_VALUE (tail)) == TREE_LIST) - expand_cleanups (TREE_VALUE (tail), dont_do); - else - expand_expr (TREE_VALUE (tail), const0_rtx, VOIDmode, 0); - } -} - -/* Expand a list of cleanups for a goto fixup. - The expansion is put into the insn chain after the insn *BEFORE_JUMP - and *BEFORE_JUMP is set to the insn that now comes before the jump. */ - -static void -fixup_cleanups (list, before_jump) - tree list; - rtx *before_jump; -{ - rtx beyond_jump = get_last_insn (); - rtx new_before_jump; - - expand_cleanups (list, 0); - new_before_jump = get_last_insn (); - - reorder_insns (NEXT_INSN (beyond_jump), new_before_jump, *before_jump); - *before_jump = new_before_jump; -} - -/* Move all cleanups from the current block_stack - to the containing block_stack, where they are assumed to - have been created. If anything can cause a temporary to - be created, but not expanded for more than one level of - block_stacks, then this code will have to change. */ - -void -move_cleanups_up () -{ - struct nesting *block = block_stack; - struct nesting *outer = block->next; - - outer->data.block.cleanups - = chainon (block->data.block.cleanups, - outer->data.block.cleanups); - block->data.block.cleanups = 0; -} - -int -this_contour_has_cleanups_p () -{ - return block_stack && block_stack->data.block.cleanups != 0; -} - -/* Enter a case (Pascal) or switch (C) statement. - Push a block onto case_stack and nesting_stack - to accumulate the case-labels that are seen - and to record the labels generated for the statement. - - EXIT_FLAG is nonzero if `exit_something' should exit this case stmt. - Otherwise, this construct is transparent for `exit_something'. - - EXPR is the index-expression to be dispatched on. - TYPE is its nominal type. We could simply convert EXPR to this type, - but instead we take short cuts. */ - -void -expand_start_case (exit_flag, expr, type) - int exit_flag; - tree expr; - tree type; -{ - register struct nesting *thiscase - = (struct nesting *) xmalloc (sizeof (struct nesting)); - - /* Make an entry on case_stack for the case we are entering. */ - - thiscase->next = case_stack; - thiscase->all = nesting_stack; - thiscase->depth = ++nesting_depth; - thiscase->exit_label = exit_flag ? gen_label_rtx () : 0; - thiscase->data.case_stmt.case_list = 0; - thiscase->data.case_stmt.index_expr = expr; - thiscase->data.case_stmt.nominal_type = type; - thiscase->data.case_stmt.default_label = 0; - thiscase->data.case_stmt.num_ranges = 0; - case_stack = thiscase; - nesting_stack = thiscase; - - do_pending_stack_adjust (); - - /* Make sure case_stmt.start points to something that won't - need any transformation before expand_end_case. */ - emit_note (0, NOTE_INSN_DELETED); - - thiscase->data.case_stmt.start = get_last_insn (); -} - -/* Start a "dummy case statement" within which case labels are invalid - and are not connected to any larger real case statement. - This can be used if you don't want to let a case statement jump - into the middle of certain kinds of constructs. */ - -void -expand_start_case_dummy () -{ - register struct nesting *thiscase - = (struct nesting *) xmalloc (sizeof (struct nesting)); - - /* Make an entry on case_stack for the dummy. */ - - thiscase->next = case_stack; - thiscase->all = nesting_stack; - thiscase->depth = ++nesting_depth; - thiscase->exit_label = 0; - thiscase->data.case_stmt.case_list = 0; - thiscase->data.case_stmt.start = 0; - thiscase->data.case_stmt.nominal_type = 0; - thiscase->data.case_stmt.default_label = 0; - thiscase->data.case_stmt.num_ranges = 0; - case_stack = thiscase; - nesting_stack = thiscase; -} - -/* End a dummy case statement. */ - -void -expand_end_case_dummy () -{ - POPSTACK (case_stack); -} - -/* Accumulate one case or default label inside a case or switch statement. - VALUE is the value of the case (a null pointer, for a default label). - - If not currently inside a case or switch statement, return 1 and do - nothing. The caller will print a language-specific error message. - If VALUE is a duplicate or overlaps, return 2 and do nothing. - If VALUE is out of range, return 3 and do nothing. - Return 0 on success. - - Extended to handle range statements, should they ever - be adopted. */ - -int -pushcase (value, label) - register tree value; - register tree label; -{ - register struct case_node **l; - register struct case_node *n; - tree index_type; - tree nominal_type; - - /* Fail if not inside a real case statement. */ - if (! (case_stack && case_stack->data.case_stmt.start)) - return 1; - - index_type = TREE_TYPE (case_stack->data.case_stmt.index_expr); - nominal_type = case_stack->data.case_stmt.nominal_type; - - /* If the index is erroneous, avoid more problems: pretend to succeed. */ - if (index_type == error_mark_node) - return 0; - - /* Convert VALUE to the type in which the comparisons are nominally done. */ - if (value != 0) - value = convert (nominal_type, value); - - /* Fail if this value is out of range for the actual type of the index - (which may be narrower than NOMINAL_TYPE). */ - if (value != 0 && ! int_fits_type_p (value, index_type)) - return 3; - - /* Fail if this is a duplicate or overlaps another entry. */ - if (value == 0) - { - if (case_stack->data.case_stmt.default_label != 0) - return 2; - case_stack->data.case_stmt.default_label = label; - } - else - { - /* Find the elt in the chain before which to insert the new value, - to keep the chain sorted in increasing order. - But report an error if this element is a duplicate. */ - for (l = &case_stack->data.case_stmt.case_list; - /* Keep going past elements distinctly less than VALUE. */ - *l != 0 && tree_int_cst_lt ((*l)->high, value); - l = &(*l)->right) - ; - if (*l) - { - /* Element we will insert before must be distinctly greater; - overlap means error. */ - if (! tree_int_cst_lt (value, (*l)->low)) - return 2; - } - - /* Add this label to the chain, and succeed. - Copy VALUE so it is on temporary rather than momentary - obstack and will thus survive till the end of the case statement. */ - n = (struct case_node *) oballoc (sizeof (struct case_node)); - n->left = 0; - n->right = *l; - n->high = n->low = copy_node (value); - n->code_label = label; - n->test_label = 0; - *l = n; - } - - expand_label (label); - return 0; -} - -/* Like pushcase but this case applies to all values - between VALUE1 and VALUE2 (inclusive). - The return value is the same as that of pushcase - but there is one additional error code: - 4 means the specified range was empty. - - Note that this does not currently work, since expand_end_case - has yet to be extended to handle RANGE_EXPRs. */ - -int -pushcase_range (value1, value2, label) - register tree value1, value2; - register tree label; -{ - register struct case_node **l; - register struct case_node *n; - tree index_type; - tree nominal_type; - - /* Fail if not inside a real case statement. */ - if (! (case_stack && case_stack->data.case_stmt.start)) - return 1; - - index_type = TREE_TYPE (case_stack->data.case_stmt.index_expr); - nominal_type = case_stack->data.case_stmt.nominal_type; - - /* If the index is erroneous, avoid more problems: pretend to succeed. */ - if (index_type == error_mark_node) - return 0; - - /* Convert VALUEs to type in which the comparisons are nominally done. */ - if (value1 != 0) - value1 = convert (nominal_type, value1); - if (value2 != 0) - value2 = convert (nominal_type, value2); - - /* Fail if these values are out of range. */ - if (value1 != 0 && ! int_fits_type_p (value1, index_type)) - return 3; - - if (value2 != 0 && ! int_fits_type_p (value2, index_type)) - return 3; - - /* Fail if the range is empty. */ - if (tree_int_cst_lt (value2, value1)) - return 4; - - /* If the bounds are equal, turn this into the one-value case. */ - if (tree_int_cst_equal (value1, value2)) - return pushcase (value1, label); - - /* Find the elt in the chain before which to insert the new value, - to keep the chain sorted in increasing order. - But report an error if this element is a duplicate. */ - for (l = &case_stack->data.case_stmt.case_list; - /* Keep going past elements distinctly less than this range. */ - *l != 0 && tree_int_cst_lt ((*l)->high, value1); - l = &(*l)->right) - ; - if (*l) - { - /* Element we will insert before must be distinctly greater; - overlap means error. */ - if (! tree_int_cst_lt (value2, (*l)->low)) - return 2; - } - - /* Add this label to the chain, and succeed. - Copy VALUE1, VALUE2 so they are on temporary rather than momentary - obstack and will thus survive till the end of the case statement. */ - - n = (struct case_node *) oballoc (sizeof (struct case_node)); - n->left = 0; - n->right = *l; - n->low = copy_node (value1); - n->high = copy_node (value2); - n->code_label = label; - n->test_label = 0; - *l = n; - - expand_label (label); - - case_stack->data.case_stmt.num_ranges++; - - return 0; -} - -/* Check that all enumeration literals are covered by the case - expressions of a switch. Also, warn if there are any extra - switch cases that are *not* elements of the enumerated type. */ - -static void -check_for_full_enumeration_handling (type) - tree type; -{ - register struct case_node *n; - register tree chain; - - /* The time complexity of this loop is currently O(N * M), with - N being the number of enumerals in the enumerated type, and - M being the number of case expressions in the switch. */ - - for (chain = TYPE_VALUES (type); - chain; - chain = TREE_CHAIN (chain)) - { - /* Find a match between enumeral and case expression, if possible. - Quit looking when we've gone too far (since case expressions - are kept sorted in ascending order). Warn about enumerals not - handled in the switch statement case expression list. */ - - for (n = case_stack->data.case_stmt.case_list; - n && tree_int_cst_lt (n->high, TREE_VALUE (chain)); - n = n->right) - ; - - if (!(n && tree_int_cst_equal (n->low, TREE_VALUE (chain)))) - warning ("enumerated value `%s' not handled in switch", - IDENTIFIER_POINTER (TREE_PURPOSE (chain))); - } - - /* Now we go the other way around; we warn if there are case - expressions that don't correspond to enumerals. This can - occur since C and C++ don't enforce type-checking of - assignments to enumeration variables. */ - - for (n = case_stack->data.case_stmt.case_list; n; n = n->right) - { - for (chain = TYPE_VALUES (type); - chain && !tree_int_cst_equal (n->low, TREE_VALUE (chain)); - chain = TREE_CHAIN (chain)) - ; - - if (!chain) - warning ("case value `%d' not in enumerated type `%s'", - TREE_INT_CST_LOW (n->low), - IDENTIFIER_POINTER (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE - ? TYPE_NAME (type) - : DECL_NAME (TYPE_NAME (type)))); - } -} - -/* Terminate a case (Pascal) or switch (C) statement - in which CASE_INDEX is the expression to be tested. - Generate the code to test it and jump to the right place. */ - -void -expand_end_case (orig_index) - tree orig_index; -{ - tree minval, maxval, range; - rtx default_label = 0; - register struct case_node *n; - int count; - rtx index; - rtx table_label = gen_label_rtx (); - int ncases; - rtx *labelvec; - register int i; - rtx before_case; - register struct nesting *thiscase = case_stack; - tree index_expr = thiscase->data.case_stmt.index_expr; - int unsignedp = TREE_UNSIGNED (TREE_TYPE (index_expr)); - - do_pending_stack_adjust (); - - /* An ERROR_MARK occurs for various reasons including invalid data type. */ - if (TREE_TYPE (index_expr) != error_mark_node) - { - /* If switch expression was an enumerated type, check that all - enumeration literals are covered by the cases. - No sense trying this if there's a default case, however. */ - - if (!thiscase->data.case_stmt.default_label - && TREE_CODE (TREE_TYPE (orig_index)) == ENUMERAL_TYPE - && TREE_CODE (index_expr) != INTEGER_CST - && warn_switch) - check_for_full_enumeration_handling (TREE_TYPE (orig_index)); - - /* If we don't have a default-label, create one here, - after the body of the switch. */ - if (thiscase->data.case_stmt.default_label == 0) - { - thiscase->data.case_stmt.default_label - = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); - expand_label (thiscase->data.case_stmt.default_label); - } - default_label = label_rtx (thiscase->data.case_stmt.default_label); - - before_case = get_last_insn (); - - /* Simplify the case-list before we count it. */ - group_case_nodes (thiscase->data.case_stmt.case_list); - - /* Get upper and lower bounds of case values. - Also convert all the case values to the index expr's data type. */ - - count = 0; - for (n = thiscase->data.case_stmt.case_list; n; n = n->right) - { - /* Check low and high label values are integers. */ - if (TREE_CODE (n->low) != INTEGER_CST) - abort (); - if (TREE_CODE (n->high) != INTEGER_CST) - abort (); - - n->low = convert (TREE_TYPE (index_expr), n->low); - n->high = convert (TREE_TYPE (index_expr), n->high); - - /* Count the elements and track the largest and smallest - of them (treating them as signed even if they are not). */ - if (count++ == 0) - { - minval = n->low; - maxval = n->high; - } - else - { - if (INT_CST_LT (n->low, minval)) - minval = n->low; - if (INT_CST_LT (maxval, n->high)) - maxval = n->high; - } - /* A range counts double, since it requires two compares. */ - if (! tree_int_cst_equal (n->low, n->high)) - count++; - } - - /* Compute span of values. */ - if (count != 0) - range = combine (MINUS_EXPR, maxval, minval); - - if (count == 0 || TREE_CODE (TREE_TYPE (index_expr)) == ERROR_MARK) - { - expand_expr (index_expr, const0_rtx, VOIDmode, 0); - emit_queue (); - emit_jump (default_label); - } - /* If range of values is much bigger than number of values, - make a sequence of conditional branches instead of a dispatch. - If the switch-index is a constant, do it this way - because we can optimize it. */ - else if (TREE_INT_CST_HIGH (range) != 0 -#ifdef HAVE_casesi - || count < 4 -#else - /* If machine does not have a case insn that compares the - bounds, this means extra overhead for dispatch tables - which raises the threshold for using them. */ - || count < 5 -#endif - || (unsigned) (TREE_INT_CST_LOW (range)) > 10 * count - || TREE_CODE (index_expr) == INTEGER_CST) - { - index = expand_expr (index_expr, 0, VOIDmode, 0); - - /* If the index is a short or char that we do not have - an insn to handle comparisons directly, convert it to - a full integer now, rather than letting each comparison - generate the conversion. */ - - if ((GET_MODE (index) == QImode || GET_MODE (index) == HImode) - && (cmp_optab->handlers[(int) GET_MODE(index)].insn_code - == CODE_FOR_nothing)) - index = convert_to_mode (SImode, index, unsignedp); - - emit_queue (); - do_pending_stack_adjust (); - - index = protect_from_queue (index, 0); - if (GET_CODE (index) == MEM) - index = copy_to_reg (index); - if (GET_CODE (index) == CONST_INT - || TREE_CODE (index_expr) == INTEGER_CST) - { - /* Make a tree node with the proper constant value - if we don't already have one. */ - if (TREE_CODE (index_expr) != INTEGER_CST) - { - index_expr - = build_int_2 (INTVAL (index), - !unsignedp && INTVAL (index) >= 0 ? 0 : -1); - index_expr = convert (TREE_TYPE (index_expr), index_expr); - } - - /* For constant index expressions we need only - issue a unconditional branch to the appropriate - target code. The job of removing any unreachable - code is left to the optimisation phase if the - "-O" option is specified. */ - for (n = thiscase->data.case_stmt.case_list; - n; - n = n->right) - { - if (! tree_int_cst_lt (index_expr, n->low) - && ! tree_int_cst_lt (n->high, index_expr)) - break; - } - if (n) - emit_jump (label_rtx (n->code_label)); - else - emit_jump (default_label); - } - else - { - /* If the index expression is not constant we generate - a binary decision tree to select the appropriate - target code. This is done as follows: - - The list of cases is rearranged into a binary tree, - nearly optimal assuming equal probability for each case. - - The tree is transformed into RTL, eliminating - redundant test conditions at the same time. - - If program flow could reach the end of the - decision tree an unconditional jump to the - default code is emitted. */ - balance_case_nodes (&thiscase->data.case_stmt.case_list, 0); - emit_case_nodes (index, thiscase->data.case_stmt.case_list, - default_label, unsignedp); - emit_jump_if_reachable (default_label); - } - } - else - { -#ifdef HAVE_casesi - /* Convert the index to SImode. */ - if (TYPE_MODE (TREE_TYPE (index_expr)) == DImode) - { - index_expr = build (MINUS_EXPR, TREE_TYPE (index_expr), - index_expr, minval); - minval = integer_zero_node; - } - if (TYPE_MODE (TREE_TYPE (index_expr)) != SImode) - index_expr = convert (type_for_size (GET_MODE_BITSIZE (SImode), 0), - index_expr); - index = expand_expr (index_expr, 0, VOIDmode, 0); - emit_queue (); - index = protect_from_queue (index, 0); - do_pending_stack_adjust (); - - emit_jump_insn (gen_casesi (index, expand_expr (minval, 0, VOIDmode, 0), - expand_expr (range, 0, VOIDmode, 0), - table_label, default_label)); -#else -#ifdef HAVE_tablejump - index_expr = convert (type_for_size (GET_MODE_BITSIZE (SImode), 0), - build (MINUS_EXPR, TREE_TYPE (index_expr), - index_expr, minval)); - index = expand_expr (index_expr, 0, VOIDmode, 0); - emit_queue (); - index = protect_from_queue (index, 0); - do_pending_stack_adjust (); - - do_tablejump (index, - gen_rtx (CONST_INT, VOIDmode, TREE_INT_CST_LOW (range)), - table_label, default_label); -#else - lossage; -#endif /* not HAVE_tablejump */ -#endif /* not HAVE_casesi */ - - /* Get table of labels to jump to, in order of case index. */ - - ncases = TREE_INT_CST_LOW (range) + 1; - labelvec = (rtx *) alloca (ncases * sizeof (rtx)); - bzero (labelvec, ncases * sizeof (rtx)); - - for (n = thiscase->data.case_stmt.case_list; n; n = n->right) - { - register int i - = TREE_INT_CST_LOW (n->low) - TREE_INT_CST_LOW (minval); - - while (i + TREE_INT_CST_LOW (minval) - <= TREE_INT_CST_LOW (n->high)) - labelvec[i++] - = gen_rtx (LABEL_REF, Pmode, label_rtx (n->code_label)); - } - - /* Fill in the gaps with the default. */ - for (i = 0; i < ncases; i++) - if (labelvec[i] == 0) - labelvec[i] = gen_rtx (LABEL_REF, Pmode, default_label); - - /* Output the table */ - emit_label (table_label); - -#ifdef CASE_VECTOR_PC_RELATIVE - emit_jump_insn (gen_rtx (ADDR_DIFF_VEC, CASE_VECTOR_MODE, - gen_rtx (LABEL_REF, Pmode, table_label), - gen_rtvec_v (ncases, labelvec))); -#else - emit_jump_insn (gen_rtx (ADDR_VEC, CASE_VECTOR_MODE, - gen_rtvec_v (ncases, labelvec))); -#endif - /* If the case insn drops through the table, - after the table we must jump to the default-label. - Otherwise record no drop-through after the table. */ -#ifdef CASE_DROPS_THROUGH - emit_jump (default_label); -#else - emit_barrier (); -#endif - } - - reorder_insns (NEXT_INSN (before_case), get_last_insn (), - thiscase->data.case_stmt.start); - } - if (thiscase->exit_label) - emit_label (thiscase->exit_label); - - POPSTACK (case_stack); -} - -/* Generate code to jump to LABEL if OP1 and OP2 are equal. */ - -static void -do_jump_if_equal (op1, op2, label, unsignedp) - rtx op1, op2, label; - int unsignedp; -{ - if (GET_CODE (op1) == CONST_INT - && GET_CODE (op2) == CONST_INT) - { - if (INTVAL (op1) == INTVAL (op2)) - emit_jump (label); - } - else - { - emit_cmp_insn (op1, op2, 0, unsignedp, 0); - emit_jump_insn (gen_beq (label)); - } -} - -/* Scan an ordered list of case nodes - combining those with consecutive values or ranges. - - Eg. three separate entries 1: 2: 3: become one entry 1..3: */ - -static void -group_case_nodes (head) - case_node_ptr head; -{ - case_node_ptr node = head; - - while (node) - { - rtx lb = next_real_insn (label_rtx (node->code_label)); - case_node_ptr np = node; - - /* Try to group the successors of NODE with NODE. */ - while (((np = np->right) != 0) - /* Do they jump to the same place? */ - && next_real_insn (label_rtx (np->code_label)) == lb - /* Are their ranges consecutive? */ - && tree_int_cst_equal (np->low, - combine (PLUS_EXPR, node->high, - build_int_2 (1, 0))) - /* An overflow is not consecutive. */ - && tree_int_cst_lt (node->high, - combine (PLUS_EXPR, node->high, - build_int_2 (1, 0)))) - { - node->high = np->high; - } - /* NP is the first node after NODE which can't be grouped with it. - Delete the nodes in between, and move on to that node. */ - node->right = np; - node = np; - } -} - -/* Take an ordered list of case nodes - and transform them into a near optimal binary tree, - on the assumtion that any target code selection value is as - likely as any other. - - The transformation is performed by splitting the ordered - list into two equal sections plus a pivot. The parts are - then attached to the pivot as left and right branches. Each - branch is is then transformed recursively. */ - -static void -balance_case_nodes (head, parent) - case_node_ptr *head; - case_node_ptr parent; -{ - register case_node_ptr np; - - np = *head; - if (np) - { - int i = 0; - int ranges = 0; - register case_node_ptr *npp; - case_node_ptr left; - - /* Count the number of entries on branch. - Also count the ranges. */ - while (np) - { - if (!tree_int_cst_equal (np->low, np->high)) - ranges++; - i++; - np = np->right; - } - if (i > 2) - { - /* Split this list if it is long enough for that to help. */ - npp = head; - left = *npp; - /* If there are just three nodes, split at the middle one. */ - if (i == 3) - npp = &(*npp)->right; - else - { - /* Find the place in the list that bisects the list's total cost, - where ranges count as 2. - Here I gets half the total cost. */ - i = (i + ranges + 1) / 2; - while (1) - { - /* Skip nodes while their cost does not reach that amount. */ - if (!tree_int_cst_equal ((*npp)->low, (*npp)->high)) - i--; - i--; - if (i <= 0) - break; - npp = &(*npp)->right; - } - } - *head = np = *npp; - *npp = 0; - np->parent = parent; - np->left = left; - - /* Optimize each of the two split parts. */ - balance_case_nodes (&np->left, np); - balance_case_nodes (&np->right, np); - } - else - { - /* Else leave this branch as one level, - but fill in `parent' fields. */ - np = *head; - np->parent = parent; - for (; np->right; np = np->right) - np->right->parent = np; - } - } -} - -/* Search the parent sections of the case node tree - to see if a test for the lower bound of NODE would be redundant. - - The instructions to synthesis the case decision tree are - output in the same order as nodes are processed so it is - known that if a parent node checks the range of the current - node minus one that the current node is bounded at its lower - span. Thus the test would be redundant. */ - -static int -node_has_low_bound (node) - case_node_ptr node; -{ - tree low_minus_one; - case_node_ptr pnode; - - if (node->left) - { - low_minus_one = combine (MINUS_EXPR, node->low, build_int_2 (1, 0)); - /* Avoid the screw case of overflow where low_minus_one is > low. */ - if (tree_int_cst_lt (low_minus_one, node->low)) - for (pnode = node->parent; pnode; pnode = pnode->parent) - { - if (tree_int_cst_equal (low_minus_one, pnode->high)) - return 1; - /* If a parent node has a left branch we know that none - of its parents can have a high bound of our target - minus one so we abort the search. */ - if (node->left) - break; - } - } - return 0; -} - -/* Search the parent sections of the case node tree - to see if a test for the upper bound of NODE would be redundant. - - The instructions to synthesis the case decision tree are - output in the same order as nodes are processed so it is - known that if a parent node checks the range of the current - node plus one that the current node is bounded at its upper - span. Thus the test would be redundant. */ - -static int -node_has_high_bound (node) - case_node_ptr node; -{ - tree high_plus_one; - case_node_ptr pnode; - - if (node->right == 0) - { - high_plus_one = combine (PLUS_EXPR, node->high, build_int_2 (1, 0)); - /* Avoid the screw case of overflow where high_plus_one is > high. */ - if (tree_int_cst_lt (node->high, high_plus_one)) - for (pnode = node->parent; pnode; pnode = pnode->parent) - { - if (tree_int_cst_equal (high_plus_one, pnode->low)) - return 1; - /* If a parent node has a right branch we know that none - of its parents can have a low bound of our target - plus one so we abort the search. */ - if (node->right) - break; - } - } - return 0; -} - -/* Search the parent sections of the - case node tree to see if both tests for the upper and lower - bounds of NODE would be redundant. */ - -static int -node_is_bounded (node) - case_node_ptr node; -{ - if (node->left || node->right) - return 0; - return node_has_low_bound (node) && node_has_high_bound (node); -} - -/* Emit an unconditional jump to LABEL unless it would be dead code. */ - -static void -emit_jump_if_reachable (label) - rtx label; -{ - rtx last_insn; - - if (GET_CODE (get_last_insn ()) != BARRIER) - emit_jump (label); -} - -/* Emit step-by-step code to select a case for the value of INDEX. - The thus generated decision tree follows the form of the - case-node binary tree NODE, whose nodes represent test conditions. - UNSIGNEDP is nonzero if we should do unsigned comparisons. - - Care is taken to prune redundant tests from the decision tree - by detecting any boundary conditions already checked by - emitted rtx. (See node_has_high_bound, node_has_low_bound - and node_is_bounded, above.) - - Where the test conditions can be shown to be redundant we emit - an unconditional jump to the target code. As a further - optimization, the subordinates of a tree node are examined to - check for bounded nodes. In this case conditional and/or - unconditional jumps as a result of the boundary check for the - current node are arranged to target the subordinates associated - code for out of bound conditions on the current node node. */ - -static void -emit_case_nodes (index, node, default_label, unsignedp) - rtx index; - case_node_ptr node; - rtx default_label; - int unsignedp; -{ - /* If INDEX has an unsigned type, we must make unsigned branches. */ - typedef rtx rtx_function (); - rtx_function *gen_bgt_pat = unsignedp ? gen_bgtu : gen_bgt; - rtx_function *gen_bge_pat = unsignedp ? gen_bgeu : gen_bge; - rtx_function *gen_blt_pat = unsignedp ? gen_bltu : gen_blt; - rtx_function *gen_ble_pat = unsignedp ? gen_bleu : gen_ble; - - if (node->test_label) - { - /* If this test node requires a label it follows that - it must be preceeded by an unconditional branch. - If control can pass to this point we can assume that - a "br default" is in order. */ - emit_jump_if_reachable (default_label); - expand_label (node->test_label); - } - if (tree_int_cst_equal (node->low, node->high)) - { - /* Node is single valued. */ - do_jump_if_equal (index, expand_expr (node->low, 0, VOIDmode, 0), - label_rtx (node->code_label), unsignedp); - if (node->right) - { - if (node->left) - { - /* This node has children on either side. */ - emit_cmp_insn (index, expand_expr (node->high, 0, VOIDmode, 0), 0, unsignedp, 0); - - if (node_is_bounded (node->right)) - { - emit_jump_insn ((*gen_bgt_pat) (label_rtx (node->right->code_label))); - if (node_is_bounded (node->left)) - emit_jump (label_rtx (node->left->code_label)); - else - emit_case_nodes (index, node->left, - default_label, unsignedp); - } - else - { - if (node_is_bounded (node->left)) - emit_jump_insn ((*gen_blt_pat) (label_rtx (node->left->code_label))); - else - { - node->right->test_label = - build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); - emit_jump_insn ((*gen_bgt_pat) (label_rtx (node->right->test_label))); - emit_case_nodes (index, node->left, - default_label, unsignedp); - } - emit_case_nodes (index, node->right, - default_label, unsignedp); - } - } - else - { - /* Here we have a right child but no left - so we issue conditional branch to default - and process the right child. */ - - /* Omit the conditional branch to default - if we it avoid only one right child; - it costs too much space to save so little time. */ - if (node->right->right && !node_has_low_bound (node)) - { - emit_cmp_insn (index, expand_expr (node->high, 0, VOIDmode, 0), 0, unsignedp, 0); - emit_jump_insn ((*gen_blt_pat) (default_label)); - } - if (node_is_bounded (node->right)) - emit_jump (label_rtx (node->right->code_label)); - else - emit_case_nodes (index, node->right, default_label, unsignedp); - } - } - else if (node->left) - { - if (node_is_bounded (node->left)) - emit_jump (label_rtx (node->left->code_label)); - else - emit_case_nodes (index, node->left, default_label, unsignedp); - } - } - else - { - /* Node is a range. */ - if (node->right) - { - if (node->left) - { - emit_cmp_insn (index, expand_expr (node->high, 0, VOIDmode, 0), 0, unsignedp, 0); - if (node_is_bounded (node->right)) - { - /* Right hand node is fully bounded so we can - eliminate any testing and branch directly - to the target code. */ - emit_jump_insn ((*gen_bgt_pat) (label_rtx (node->right->code_label))); - } - else - { - /* Right hand node requires testing so create - a label to put on the cmp code. */ - node->right->test_label = - build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); - emit_jump_insn ((*gen_bgt_pat) (label_rtx (node->right->test_label))); - } - emit_cmp_insn (index, expand_expr (node->low, 0, VOIDmode, 0), 0, unsignedp, 0); - emit_jump_insn ((*gen_bge_pat) (label_rtx (node->code_label))); - if (node_is_bounded (node->left)) - { - /* Left hand node is fully bounded so we can - eliminate any testing and branch directly - to the target code. */ - emit_jump (label_rtx (node->left->code_label)); - } - else - emit_case_nodes (index, node->left, default_label, unsignedp); - /* If right node has been given a test label above - we must process it now. */ - if (node->right->test_label) - emit_case_nodes (index, node->right, default_label, unsignedp); - } - else - { - if (!node_has_low_bound (node)) - { - emit_cmp_insn (index, expand_expr (node->low, 0, VOIDmode, 0), 0, unsignedp, 0); - emit_jump_insn ((*gen_blt_pat) (default_label)); - } - emit_cmp_insn (index, expand_expr (node->high, 0, VOIDmode, 0), 0, unsignedp, 0); - emit_jump_insn ((*gen_ble_pat) (label_rtx (node->code_label))); - if (node_is_bounded (node->right)) - { - /* Right hand node is fully bounded so we can - eliminate any testing and branch directly - to the target code. */ - emit_jump (label_rtx (node->right->code_label)); - } - else - emit_case_nodes (index, node->right, default_label, unsignedp); - } - } - else if (node->left) - { - if (!node_has_high_bound (node)) - { - emit_cmp_insn (index, expand_expr (node->high, 0, VOIDmode, 0), 0, unsignedp, 0); - emit_jump_insn ((*gen_bgt_pat) (default_label)); - } - emit_cmp_insn (index, expand_expr (node->low, 0, VOIDmode, 0), 0, unsignedp, 0); - emit_jump_insn ((*gen_bge_pat) (label_rtx (node->code_label))); - if (node_is_bounded (node->left)) - { - /* Left hand node is fully bounded so we can - eliminate any testing and branch directly - to the target code. */ - emit_jump (label_rtx (node->left->code_label)); - } - else - emit_case_nodes (index, node->left, default_label, unsignedp); - } - else - { - /* Node has no children so we check low and - high bounds to remove redundant tests. In practice - only one of the limits may be bounded or the parent - node will have emmited a jump to our target code. */ - if (!node_has_high_bound (node)) - { - emit_cmp_insn (index, expand_expr (node->high, 0, VOIDmode, 0), 0, unsignedp, 0); - emit_jump_insn ((*gen_bgt_pat) (default_label)); - } - if (!node_has_low_bound (node)) - { - emit_cmp_insn (index, expand_expr (node->low, 0, VOIDmode, 0), 0, unsignedp, 0); - emit_jump_insn ((*gen_bge_pat) (label_rtx (node->code_label))); - } - /* We allow the default case to drop through since - it will picked up by calls to `jump_if_reachable' - either on the next test label or at the end of - the decision tree emission. */ - } - } -} - -/* Allocate fixed slots in the stack frame of the current function. */ - -/* Return size needed for stack frame based on slots so far allocated. */ - -int -get_frame_size () -{ -#ifdef FRAME_GROWS_DOWNWARD - return -frame_offset + STARTING_FRAME_OFFSET; -#else - return frame_offset - STARTING_FRAME_OFFSET; -#endif -} - -/* Allocate a stack slot of SIZE bytes and return a MEM rtx for it - with machine mode MODE. */ - -rtx -assign_stack_local (mode, size) - enum machine_mode mode; - int size; -{ - register rtx x, addr; - int bigend_correction = 0; - - frame_pointer_needed = 1; - - /* Make each stack slot a multiple of the main allocation unit. */ - size = (((size + (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1) - / (BIGGEST_ALIGNMENT / BITS_PER_UNIT)) - * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); - - /* On a big-endian machine, if we are allocating more space than we will use, - use the least significant bytes of those that are allocated. */ -#ifdef BYTES_BIG_ENDIAN - if (mode != BLKmode) - bigend_correction = size - GET_MODE_SIZE (mode); -#endif - -#ifdef FRAME_GROWS_DOWNWARD - frame_offset -= size; -#endif - addr = gen_rtx (PLUS, Pmode, frame_pointer_rtx, - gen_rtx (CONST_INT, VOIDmode, - (frame_offset + bigend_correction))); -#ifndef FRAME_GROWS_DOWNWARD - frame_offset += size; -#endif - - if (! memory_address_p (mode, addr)) - invalid_stack_slot = 1; - - x = gen_rtx (MEM, mode, addr); - - stack_slot_list = gen_rtx (EXPR_LIST, VOIDmode, x, stack_slot_list); - - return x; -} - -/* Retroactively move an auto variable from a register to a stack slot. - This is done when an address-reference to the variable is seen. */ - -void -put_var_into_stack (decl) - tree decl; -{ - register rtx reg = DECL_RTL (decl); - register rtx new; - - /* No need to do anything if decl has no rtx yet - since in that case caller is setting TREE_ADDRESSABLE - and a stack slot will be assigned when the rtl is made. */ - if (reg == 0) - return; - if (GET_CODE (reg) != REG) - return; - - new = parm_stack_loc (reg); - if (new == 0) - new = assign_stack_local (GET_MODE (reg), GET_MODE_SIZE (GET_MODE (reg))); - - XEXP (reg, 0) = XEXP (new, 0); - /* `volatil' bit means one thing for MEMs, another entirely for REGs. */ - REG_USERVAR_P (reg) = 0; - PUT_CODE (reg, MEM); - - /* If this is a memory ref that contains aggregate components, - mark it as such for cse and loop optimize. */ - MEM_IN_STRUCT_P (reg) - = (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE); - - fixup_var_refs (reg); -} - -static void -fixup_var_refs (var) - rtx var; -{ - extern rtx sequence_stack; - rtx stack = sequence_stack; - tree pending; - - stack = sequence_stack; - - /* Must scan all insns for stack-refs that exceed the limit. */ - fixup_var_refs_insns (var, get_insns (), stack == 0); - - /* Scan all pending sequences too. */ - for (; stack; stack = XEXP (XEXP (stack, 1), 1)) - { - push_to_sequence (XEXP (stack, 0)); - fixup_var_refs_insns (var, XEXP (stack, 0), - XEXP (XEXP (stack, 1), 1) == 0); - /* Update remembered end of sequence - in case we added an insn at the end. */ - XEXP (XEXP (stack, 1), 0) = get_last_insn (); - end_sequence (); - } - - /* Scan all waiting RTL_EXPRs too. */ - for (pending = rtl_expr_chain; pending; pending = TREE_CHAIN (pending)) - { - rtx seq = RTL_EXPR_SEQUENCE (TREE_VALUE (pending)); - if (seq != const0_rtx && seq != 0) - { - push_to_sequence (seq); - fixup_var_refs_insns (var, seq, 0); - end_sequence (); - } - } -} - -/* Scan the insn-chain starting with INSN for refs to VAR - and fix them up. TOPLEVEL is nonzero if this chain is the - main chain of insns for the current function. */ - -static void -fixup_var_refs_insns (var, insn, toplevel) - rtx var; - rtx insn; - int toplevel; -{ - while (insn) - { - rtx next = NEXT_INSN (insn); - rtx note; - if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN - || GET_CODE (insn) == JUMP_INSN) - { - /* The insn to load VAR from a home in the arglist - is now a no-op. When we see it, just delete it. */ - if (toplevel - && GET_CODE (PATTERN (insn)) == SET - && SET_DEST (PATTERN (insn)) == var - && rtx_equal_p (SET_SRC (PATTERN (insn)), var)) - { - next = delete_insn (insn); - if (insn == last_parm_insn) - last_parm_insn = PREV_INSN (next); - } - else - fixup_var_refs_1 (var, PATTERN (insn), insn); - /* Also fix up any invalid exprs in the REG_NOTES of this insn. - But don't touch other insns referred to by reg-notes; - we will get them elsewhere. */ - for (note = REG_NOTES (insn); note; note = XEXP (note, 1)) - if (GET_CODE (note) != INSN_LIST) - XEXP (note, 0) = walk_fixup_memory_subreg (XEXP (note, 0), insn); - } - insn = next; - } -} - -static rtx -fixup_var_refs_1 (var, x, insn) - register rtx var; - register rtx x; - rtx insn; -{ - register int i; - RTX_CODE code = GET_CODE (x); - register char *fmt; - register rtx tem; - - switch (code) - { - case MEM: - if (var == x) - { - x = fixup_stack_1 (x, insn); - tem = gen_reg_rtx (GET_MODE (x)); - /* Put new insn before a CALL, before any USEs before it. */ - if (GET_CODE (insn) == CALL_INSN) - while (PREV_INSN (insn) != 0 && GET_CODE (PREV_INSN (insn)) == INSN - && GET_CODE (PATTERN (PREV_INSN (insn))) == USE) - insn = PREV_INSN (insn); - emit_insn_before (gen_move_insn (tem, x), insn); - return tem; - } - break; - - case REG: - case CC0: - case PC: - case CONST_INT: - case CONST: - case SYMBOL_REF: - case LABEL_REF: - case CONST_DOUBLE: - return x; - - case SIGN_EXTRACT: - case ZERO_EXTRACT: - /* Note that in some cases those types of expressions are altered - by optimize_bit_field, and do not survive to get here. */ - case SUBREG: - tem = x; - while (GET_CODE (tem) == SUBREG || GET_CODE (tem) == SIGN_EXTRACT - || GET_CODE (tem) == ZERO_EXTRACT) - tem = XEXP (tem, 0); - if (tem == var) - { - x = fixup_stack_1 (x, insn); - tem = gen_reg_rtx (GET_MODE (x)); - if (GET_CODE (x) == SUBREG) - x = fixup_memory_subreg (x, insn); - emit_insn_before (gen_move_insn (tem, x), insn); - return tem; - } - break; - - case SET: - /* First do special simplification of bit-field references. */ - if (GET_CODE (SET_DEST (x)) == SIGN_EXTRACT - || GET_CODE (SET_DEST (x)) == ZERO_EXTRACT) - optimize_bit_field (x, insn, 0); - if (GET_CODE (SET_SRC (x)) == SIGN_EXTRACT - || GET_CODE (SET_SRC (x)) == ZERO_EXTRACT) - optimize_bit_field (x, insn, 0); - - { - rtx dest = SET_DEST (x); - rtx src = SET_SRC (x); - rtx outerdest = dest; - rtx outersrc = src; - - while (GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == ZERO_EXTRACT) - dest = XEXP (dest, 0); - while (GET_CODE (src) == SUBREG - || GET_CODE (src) == SIGN_EXTRACT - || GET_CODE (src) == ZERO_EXTRACT) - src = XEXP (src, 0); - - /* If VAR does not appear at the top level of the SET - just scan the lower levels of the tree. */ - - if (src != var && dest != var) - break; - - /* Clean up (SUBREG:SI (MEM:mode ...) 0) - that may appear inside a SIGN_EXTRACT or ZERO_EXTRACT. - This was legitimate when the MEM was a REG. */ - - if ((GET_CODE (outerdest) == SIGN_EXTRACT - || GET_CODE (outerdest) == ZERO_EXTRACT) - && GET_CODE (XEXP (outerdest, 0)) == SUBREG - && SUBREG_REG (XEXP (outerdest, 0)) == var) - XEXP (outerdest, 0) = fixup_memory_subreg (XEXP (outerdest, 0), insn); - - if ((GET_CODE (outersrc) == SIGN_EXTRACT - || GET_CODE (outersrc) == ZERO_EXTRACT) - && GET_CODE (XEXP (outersrc, 0)) == SUBREG - && SUBREG_REG (XEXP (outersrc, 0)) == var) - XEXP (outersrc, 0) = fixup_memory_subreg (XEXP (outersrc, 0), insn); - - /* Make sure that the machine's SIGN_EXTRACT and ZERO_EXTRACT insns - accept a memory operand. */ -#ifdef HAVE_extzv - if (GET_CODE (outersrc) == ZERO_EXTRACT - && ! ((*insn_operand_predicate[(int) CODE_FOR_extzv][0]) - (XEXP (outersrc, 0), VOIDmode))) - XEXP (outersrc, 0) = src - = fixup_var_refs_1 (var, XEXP (outersrc, 0), insn); -#endif -#ifdef HAVE_extv - if (GET_CODE (outersrc) == SIGN_EXTRACT - && ! ((*insn_operand_predicate[(int) CODE_FOR_extv][0]) - (XEXP (outersrc, 0), VOIDmode))) - XEXP (outersrc, 0) = src - = fixup_var_refs_1 (var, XEXP (outersrc, 0), insn); -#endif -#ifdef HAVE_insv - if (GET_CODE (outerdest) == ZERO_EXTRACT - && ! ((*insn_operand_predicate[(int) CODE_FOR_insv][0]) - (XEXP (outerdest, 0), VOIDmode))) - { - rtx tem = gen_reg_rtx (GET_MODE (XEXP (outerdest, 0))); - - emit_insn_before (gen_move_insn (tem, XEXP (outerdest, 0)), insn); - emit_insn_after (gen_move_insn (XEXP (outerdest, 0), tem), insn); - dest = XEXP (outerdest, 0) = tem; - } -#endif - - /* Make sure a MEM inside a SIGN_EXTRACT has QImode - since that's what bit-field insns want. */ - - if ((GET_CODE (outerdest) == SIGN_EXTRACT - || GET_CODE (outerdest) == ZERO_EXTRACT) - && GET_CODE (XEXP (outerdest, 0)) == MEM - && GET_MODE (XEXP (outerdest, 0)) != QImode) - { - XEXP (outerdest, 0) = copy_rtx (XEXP (outerdest, 0)); - PUT_MODE (XEXP (outerdest, 0), QImode); - /* Adjust the address so the bit field starts within the byte - addressed. This helps certain optimization patterns. */ - if (GET_CODE (XEXP (outerdest, 2)) == CONST_INT - && offsettable_memref_p (XEXP (outerdest, 0))) - { - int count = INTVAL (XEXP (outerdest, 2)); - XEXP (outerdest, 0) - = adj_offsettable_operand (XEXP (outerdest, 0), - count / GET_MODE_BITSIZE (QImode)); - XEXP (outerdest, 2) - = gen_rtx (CONST_INT, VOIDmode, - count % GET_MODE_BITSIZE (QImode)); - } - } - - if ((GET_CODE (outersrc) == SIGN_EXTRACT - || GET_CODE (outersrc) == ZERO_EXTRACT) - && GET_CODE (XEXP (outersrc, 0)) == MEM - && GET_MODE (XEXP (outersrc, 0)) != QImode) - { - XEXP (outersrc, 0) = copy_rtx (XEXP (outersrc, 0)); - PUT_MODE (XEXP (outersrc, 0), QImode); - /* Adjust the address so the bit field starts within the byte - addressed. This helps certain optimization patterns. */ - if (GET_CODE (XEXP (outersrc, 2)) == CONST_INT - && offsettable_memref_p (XEXP (outersrc, 0))) - { - int count = INTVAL (XEXP (outersrc, 2)); - XEXP (outersrc, 0) - = adj_offsettable_operand (XEXP (outersrc, 0), - count / GET_MODE_BITSIZE (QImode)); - XEXP (outersrc, 2) - = gen_rtx (CONST_INT, VOIDmode, - count % GET_MODE_BITSIZE (QImode)); - } - } - - /* STRICT_LOW_PART is a no-op on memory references - and it can cause combinations to be unrecognizable, - so eliminate it. */ - - if (dest == var && GET_CODE (SET_DEST (x)) == STRICT_LOW_PART) - SET_DEST (x) = XEXP (SET_DEST (x), 0); - - /* An insn to copy VAR into or out of a register - must be left alone, to avoid an infinite loop here. - But do fix up the address of VAR's stack slot if nec, - and fix up SUBREGs containing VAR - (since they are now memory subregs). */ - - if (GET_CODE (SET_SRC (x)) == REG || GET_CODE (SET_DEST (x)) == REG - || (GET_CODE (SET_SRC (x)) == SUBREG - && GET_CODE (SUBREG_REG (SET_SRC (x))) == REG) - || (GET_CODE (SET_DEST (x)) == SUBREG - && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG)) - { - if (src == var && GET_CODE (SET_SRC (x)) == SUBREG) - SET_SRC (x) = fixup_memory_subreg (SET_SRC (x), insn); - if (dest == var && GET_CODE (SET_DEST (x)) == SUBREG) - SET_DEST (x) = fixup_memory_subreg (SET_DEST (x), insn); - return fixup_stack_1 (x, insn); - } - - /* Otherwise, storing into VAR must be handled specially - by storing into a temporary and copying that into VAR - with a new insn after this one. */ - - if (dest == var) - { - rtx temp; - rtx fixeddest; - tem = SET_DEST (x); - /* STRICT_LOW_PART can be discarded, around a MEM. */ - if (GET_CODE (tem) == STRICT_LOW_PART) - tem = XEXP (tem, 0); - /* Convert (SUBREG (MEM)) to a MEM in a changed mode. */ - if (GET_CODE (tem) == SUBREG) - tem = fixup_memory_subreg (tem, insn); - fixeddest = fixup_stack_1 (tem, insn); - temp = gen_reg_rtx (GET_MODE (tem)); - emit_insn_after (gen_move_insn (fixeddest, temp), insn); - SET_DEST (x) = temp; - } - } - } - - /* Nothing special about this RTX; fix its operands. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - XEXP (x, i) = fixup_var_refs_1 (var, XEXP (x, i), insn); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - XVECEXP (x, i, j) - = fixup_var_refs_1 (var, XVECEXP (x, i, j), insn); - } - } - return x; -} - -/* Given X, an rtx of the form (SUBREG:m1 (MEM:m2 addr)), - return an rtx (MEM:m1 newaddr) which is equivalent. - If any insns must be emitted to compute NEWADDR, put them before INSN. */ - -static rtx -fixup_memory_subreg (x, insn) - rtx x; - rtx insn; -{ - int offset = SUBREG_WORD (x) * UNITS_PER_WORD; - rtx addr = XEXP (SUBREG_REG (x), 0); - enum machine_mode mode = GET_MODE (x); - rtx saved, result; - -#ifdef BYTES_BIG_ENDIAN - offset += (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) - - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))); -#endif - addr = plus_constant (addr, offset); - if (memory_address_p (mode, addr)) - return change_address (SUBREG_REG (x), mode, addr); - saved = start_sequence (); - result = change_address (SUBREG_REG (x), mode, addr); - emit_insn_before (gen_sequence (), insn); - end_sequence (saved); - return result; -} - -/* Do fixup_memory_subreg on all (SUBREG (MEM ...) ...) contained in X. - Replace subexpressions of X in place. - If X itself is a (SUBREG (MEM ...) ...), return the replacement expression. - Otherwise return X, with its contents possibly altered. - - If any insns must be emitted to compute NEWADDR, put them before INSN. */ - -static rtx -walk_fixup_memory_subreg (x, insn) - register rtx x; - rtx insn; -{ - register enum rtx_code code; - register char *fmt; - register int i; - - if (x == 0) - return 0; - - code = GET_CODE (x); - - if (code == SUBREG && GET_CODE (SUBREG_REG (x)) == MEM) - return fixup_memory_subreg (x, insn); - - /* Nothing special about this RTX; fix its operands. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - XEXP (x, i) = walk_fixup_memory_subreg (XEXP (x, i), insn); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - XVECEXP (x, i, j) - = walk_fixup_memory_subreg (XVECEXP (x, i, j), insn); - } - } - return x; -} - -#if 0 -/* Fix up any references to stack slots that are invalid memory addresses - because they exceed the maximum range of a displacement. */ - -void -fixup_stack_slots () -{ - register rtx insn; - - /* Did we generate a stack slot that is out of range - or otherwise has an invalid address? */ - if (invalid_stack_slot) - { - /* Yes. Must scan all insns for stack-refs that exceed the limit. */ - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN - || GET_CODE (insn) == JUMP_INSN) - fixup_stack_1 (PATTERN (insn), insn); - } -} -#endif - -/* For each memory ref within X, if it refers to a stack slot - with an out of range displacement, put the address in a temp register - (emitting new insns before INSN to load these registers) - and alter the memory ref to use that register. - Replace each such MEM rtx with a copy, to avoid clobberage. */ - -static rtx -fixup_stack_1 (x, insn) - rtx x; - rtx insn; -{ - register int i; - register RTX_CODE code = GET_CODE (x); - register char *fmt; - - if (code == MEM) - { - register rtx ad = XEXP (x, 0); - /* If we have address of a stack slot but it's not valid - (displacement is too large), compute the sum in a register. */ - if (GET_CODE (ad) == PLUS - && XEXP (ad, 0) == frame_pointer_rtx - && GET_CODE (XEXP (ad, 1)) == CONST_INT) - { - rtx temp; - if (memory_address_p (GET_MODE (x), ad)) - return x; - temp = gen_reg_rtx (GET_MODE (ad)); - emit_insn_before (gen_move_insn (temp, ad), insn); - return change_address (x, VOIDmode, temp); - } - return x; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - XEXP (x, i) = fixup_stack_1 (XEXP (x, i), insn); - if (fmt[i] == 'E') - { - register int j; - for (j = 0; j < XVECLEN (x, i); j++) - XVECEXP (x, i, j) = fixup_stack_1 (XVECEXP (x, i, j), insn); - } - } - return x; -} - -/* Optimization: a bit-field instruction whose field - happens to be a byte or halfword in memory - can be changed to a move instruction. - - We call here when INSN is an insn to examine or store into a bit-field. - BODY is the SET-rtx to be altered. - - EQUIV_MEM is the table `reg_equiv_mem' if that is available; else 0. - (Currently this is called only from stmt.c, and EQUIV_MEM is always 0.) */ - -static void -optimize_bit_field (body, insn, equiv_mem) - rtx body; - rtx insn; - rtx *equiv_mem; -{ - register rtx bitfield; - int destflag; - - if (GET_CODE (SET_DEST (body)) == SIGN_EXTRACT - || GET_CODE (SET_DEST (body)) == ZERO_EXTRACT) - bitfield = SET_DEST (body), destflag = 1; - else - bitfield = SET_SRC (body), destflag = 0; - - /* First check that the field being stored has constant size and position - and is in fact a byte or halfword suitably aligned. */ - - if (GET_CODE (XEXP (bitfield, 1)) == CONST_INT - && GET_CODE (XEXP (bitfield, 2)) == CONST_INT - && (INTVAL (XEXP (bitfield, 1)) == GET_MODE_BITSIZE (QImode) - || INTVAL (XEXP (bitfield, 1)) == GET_MODE_BITSIZE (HImode)) - && INTVAL (XEXP (bitfield, 2)) % INTVAL (XEXP (bitfield, 1)) == 0) - { - register rtx memref = 0; - - /* Now check that the containing word is memory, not a register, - and that it is safe to change the machine mode and to - add something to the address. */ - - if (GET_CODE (XEXP (bitfield, 0)) == MEM) - memref = XEXP (bitfield, 0); - else if (GET_CODE (XEXP (bitfield, 0)) == REG - && equiv_mem != 0) - memref = equiv_mem[REGNO (XEXP (bitfield, 0))]; - else if (GET_CODE (XEXP (bitfield, 0)) == SUBREG - && GET_CODE (SUBREG_REG (XEXP (bitfield, 0))) == MEM) - memref = SUBREG_REG (XEXP (bitfield, 0)); - else if (GET_CODE (XEXP (bitfield, 0)) == SUBREG - && equiv_mem != 0 - && GET_CODE (SUBREG_REG (XEXP (bitfield, 0))) == REG) - memref = equiv_mem[REGNO (SUBREG_REG (XEXP (bitfield, 0)))]; - - if (memref - && ! mode_dependent_address_p (XEXP (memref, 0)) - && offsettable_address_p (0, GET_MODE (bitfield), XEXP (memref, 0))) - { - /* Now adjust the address, first for any subreg'ing - that we are now getting rid of, - and then for which byte of the word is wanted. */ - - register int offset - = INTVAL (XEXP (bitfield, 2)) / GET_MODE_BITSIZE (QImode); - if (GET_CODE (XEXP (bitfield, 0)) == SUBREG) - { - offset += SUBREG_WORD (XEXP (bitfield, 0)) * UNITS_PER_WORD; -#ifdef BYTES_BIG_ENDIAN - offset -= (MIN (UNITS_PER_WORD, - GET_MODE_SIZE (GET_MODE (XEXP (bitfield, 0)))) - - MIN (UNITS_PER_WORD, - GET_MODE_SIZE (GET_MODE (memref)))); -#endif - } - - memref = gen_rtx (MEM, - (INTVAL (XEXP (bitfield, 1)) == GET_MODE_BITSIZE (QImode) - ? QImode : HImode), - XEXP (memref, 0)); - - /* Store this memory reference where - we found the bit field reference. */ - - if (destflag) - { - SET_DEST (body) - = adj_offsettable_operand (memref, offset); - if (! CONSTANT_ADDRESS_P (SET_SRC (body))) - { - rtx src = SET_SRC (body); - while (GET_CODE (src) == SUBREG - && SUBREG_WORD (src) == 0) - src = SUBREG_REG (src); - if (GET_MODE (src) != GET_MODE (memref)) - src = gen_lowpart (GET_MODE (memref), SET_SRC (body)); - SET_SRC (body) = src; - } - else if (GET_MODE (SET_SRC (body)) != VOIDmode - && GET_MODE (SET_SRC (body)) != GET_MODE (memref)) - /* This shouldn't happen because anything that didn't have - one of these modes should have got converted explicitly - and then referenced through a subreg. - This is so because the original bit-field was - handled by agg_mode and so its tree structure had - the same mode that memref now has. */ - abort (); - } - else - { - rtx dest = SET_DEST (body); - - while (GET_CODE (dest) == SUBREG - && SUBREG_WORD (dest) == 0) - dest = SUBREG_REG (dest); - SET_DEST (body) = dest; - - memref = adj_offsettable_operand (memref, offset); - if (GET_MODE (dest) == GET_MODE (memref)) - SET_SRC (body) = memref; - else - { - /* Convert the mem ref to the destination mode. */ - rtx last = get_last_insn (); - rtx newreg = gen_reg_rtx (GET_MODE (dest)); - convert_move (newreg, memref, - GET_CODE (SET_SRC (body)) == ZERO_EXTRACT); - /* Put the conversion before the insn being fixed. */ - reorder_insns (NEXT_INSN (last), get_last_insn (), - PREV_INSN (insn)); - SET_SRC (body) = newreg; - } - } - - /* Cause the insn to be re-recognized. */ - - INSN_CODE (insn) = -1; - } - } -} - -/* 1 + last pseudo register number used for loading a copy - of a parameter of this function. */ - -static int max_parm_reg; - -/* Vector indexed by REGNO, containing location on stack in which - to put the parm which is nominally in pseudo register REGNO, - if we discover that that parm must go in the stack. */ -static rtx *parm_reg_stack_loc; - -int -max_parm_reg_num () -{ - return max_parm_reg; -} - -/* Return the first insn following those generated by `assign_parms'. */ - -rtx -get_first_nonparm_insn () -{ - if (last_parm_insn) - return NEXT_INSN (last_parm_insn); - return get_insns (); -} - -/* Get the stack home of a REG rtx that is one of this function's parameters. - This is called rather than assign a new stack slot as a local. - Return 0 if there is no existing stack home suitable for such use. */ - -static rtx -parm_stack_loc (reg) - rtx reg; -{ - if (REGNO (reg) < max_parm_reg) - return parm_reg_stack_loc[REGNO (reg)]; - return 0; -} - -/* Return 1 if EXP returns an aggregate value, for which an address - must be passed to the function or returned by the function. */ - -int -aggregate_value_p (exp) - tree exp; -{ - if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode) - return 1; - if (RETURN_IN_MEMORY (TREE_TYPE (exp))) - return 1; - if (flag_pcc_struct_return - && (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE)) - return 1; - return 0; -} - -/* Convert a mem ref into one with a valid memory address. - Pass through anything else unchanged. */ - -rtx -validize_mem (ref) - rtx ref; -{ - if (GET_CODE (ref) != MEM) - return ref; - if (memory_address_p (GET_MODE (ref), XEXP (ref, 0))) - return ref; - /* Don't alter REF itself, since that is probably a stack slot. */ - return gen_rtx (MEM, GET_MODE (ref), - memory_address (GET_MODE (ref), XEXP (ref, 0))); -} - -/* Assign RTL expressions to the function's parameters. - This may involve copying them into registers and using - those registers as the RTL for them. */ - -static void -assign_parms (fndecl) - tree fndecl; -{ - register tree parm; - register rtx entry_parm; - register rtx stack_parm; - register CUMULATIVE_ARGS args_so_far; - enum machine_mode passed_mode, nominal_mode; - /* Total space needed so far for args on the stack, - given as a constant and a tree-expression. */ - struct args_size stack_args_size; - int first_parm_offset = FIRST_PARM_OFFSET (fndecl); - int first_parm_caller_offset -#ifdef FIRST_PARM_CALLER_OFFSET - = FIRST_PARM_CALLER_OFFSET (fndecl); -#else - = first_parm_offset; -#endif - tree fntype = TREE_TYPE (fndecl); - /* This is used for the arg pointer when referring to stack args. */ - rtx internal_arg_pointer; - - int nparmregs - = list_length (DECL_ARGUMENTS (fndecl)) + FIRST_PSEUDO_REGISTER; - - /* Nonzero if function takes extra anonymous args. - This means the last named arg must be on the stack - right before the anonymous ones. - Also nonzero if the first arg is named `__builtin_va_alist', - which is used on some machines for old-fashioned non-ANSI varargs.h; - this too should be stuck onto the stack as if it had arrived there. */ - int vararg - = ((DECL_ARGUMENTS (fndecl) != 0 - && DECL_NAME (DECL_ARGUMENTS (fndecl)) - && (! strcmp (IDENTIFIER_POINTER (DECL_NAME (DECL_ARGUMENTS (fndecl))), - "__builtin_va_alist"))) - || - (TYPE_ARG_TYPES (fntype) != 0 - && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) - != void_type_node))); - int arg_pointer_copied = 0; - -#if ARG_POINTER_REGNUM == FRAME_POINTER_REGNUM - internal_arg_pointer = arg_pointer_rtx; -#else - /* If the arg pointer reg is not a fixed reg, - make a copy of it, and address parms via the copy. */ - if (fixed_regs[ARG_POINTER_REGNUM]) - internal_arg_pointer = arg_pointer_rtx; - else - { - internal_arg_pointer = copy_to_reg (arg_pointer_rtx); - arg_pointer_copied = 1; - } -#endif - - stack_args_size.constant = 0; - stack_args_size.var = 0; - - /* If struct value address comes on the stack, count it in size of args. */ - if (aggregate_value_p (DECL_RESULT (fndecl)) - && GET_CODE (struct_value_incoming_rtx) == MEM) - { -#ifdef FIRST_PARM_CALLER_OFFSET - /* Make the right thing happen on the sparc - in a function with a struct value and struct arg. */ - if (first_parm_caller_offset < 0) - first_parm_offset += GET_MODE_SIZE (Pmode); - else -#endif - stack_args_size.constant += GET_MODE_SIZE (Pmode); - } - - parm_reg_stack_loc = (rtx *) oballoc (nparmregs * sizeof (rtx)); - bzero (parm_reg_stack_loc, nparmregs * sizeof (rtx)); - - INIT_CUMULATIVE_ARGS (args_so_far, fntype); - - for (parm = DECL_ARGUMENTS (fndecl); parm; parm = TREE_CHAIN (parm)) - { - int aggregate - = (TREE_CODE (TREE_TYPE (parm)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE (parm)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (parm)) == UNION_TYPE); - struct args_size stack_offset; - rtx stack_offset_rtx; - enum direction where_pad; - - DECL_OFFSET (parm) = -1; - - if (TREE_TYPE (parm) == error_mark_node - /* This can happen after weird syntax errors - or if an enum type is defined among the parms. */ - || TREE_CODE (parm) != PARM_DECL - || DECL_ARG_TYPE (parm) == NULL) - { - DECL_RTL (parm) = gen_rtx (MEM, BLKmode, const0_rtx); - TREE_USED (parm) = 1; - continue; - } - - /* Find mode of arg as it is passed, and mode of arg - as it should be during execution of this function. */ - passed_mode = TYPE_MODE (DECL_ARG_TYPE (parm)); - nominal_mode = TYPE_MODE (TREE_TYPE (parm)); - - /* Get this parm's offset as an rtx. */ - stack_offset = stack_args_size; - stack_offset.constant += first_parm_offset; - - /* If this argument needs more than the usual parm alignment, do - extrinsic padding to reach that alignment. */ - -#ifdef MAX_PARM_BOUNDARY - /* If MAX_PARM_BOUNDARY is not defined, it means that the usual - alignment requirements are relaxed for parms, and that no parm - needs more alignment than PARM_BOUNDARY, regardless of data type. */ - - if (PARM_BOUNDARY < TYPE_ALIGN (DECL_ARG_TYPE (parm))) - { - int boundary = PARM_BOUNDARY; - - /* Determine the boundary to pad up to. */ - if (TYPE_ALIGN (DECL_ARG_TYPE (parm)) > boundary) - boundary = TYPE_ALIGN (DECL_ARG_TYPE (parm)); - if (boundary > MAX_PARM_BOUNDARY) - boundary = MAX_PARM_BOUNDARY; - - /* If the previous args don't reach such a boundary, - advance to the next one. */ - boundary /= BITS_PER_UNIT; - stack_offset.constant += boundary - 1; - stack_offset.constant &= ~(boundary - 1); - stack_args_size.constant += boundary - 1; - stack_args_size.constant &= ~(boundary - 1); - - if (stack_offset.var != 0) - abort (); /* This case not implemented yet */ - } -#endif /* MAX_PARM_BOUNDARY */ - - /* Find out if the parm needs intrinsic padding (up to PARM_BOUNDARY), - and whether above or below. */ - - where_pad - = FUNCTION_ARG_PADDING (passed_mode, - expand_expr (size_in_bytes (DECL_ARG_TYPE (parm)), - 0, VOIDmode, 0)); - - /* If arg should be padded below, adjust the stack address upward. - This padding is considered part of the space occupied by the - argument. It pads only up to PARM_BOUNDARY, and it does not - depend on the previous arguments, since they are assumed to - occupy a multiple of PARM_BOUNDARY. */ - - if (where_pad == downward) - { - if (passed_mode != BLKmode) - { - if (GET_MODE_BITSIZE (passed_mode) % PARM_BOUNDARY) - stack_offset.constant - += (((GET_MODE_BITSIZE (passed_mode) + PARM_BOUNDARY - 1) - / PARM_BOUNDARY * PARM_BOUNDARY / BITS_PER_UNIT) - - GET_MODE_SIZE (passed_mode)); - } - else - { - tree sizetree = size_in_bytes (DECL_ARG_TYPE (parm)); - /* Round the size up to multiple of PARM_BOUNDARY bits. */ - tree s1 = convert_units (sizetree, BITS_PER_UNIT, PARM_BOUNDARY); - tree s2 = convert_units (s1, PARM_BOUNDARY, BITS_PER_UNIT); - /* Add it in. */ - ADD_PARM_SIZE (stack_offset, s2); - SUB_PARM_SIZE (stack_offset, sizetree); - } - } - - /* Let machine desc say which reg (if any) the parm arrives in. - 0 means it arrives on the stack. */ - entry_parm = 0; - /* Variable-size args, and args following such, are never in regs. */ - if (TREE_CODE (TYPE_SIZE (TREE_TYPE (parm))) == INTEGER_CST - || stack_offset.var != 0) - { - /* Set LAST_NAMED if this is last named arg before some - anonymous args. We treat it as if it were anonymous too. */ - int last_named = (TREE_CHAIN (parm) == 0 && vararg); -#ifdef FUNCTION_INCOMING_ARG - entry_parm - = FUNCTION_INCOMING_ARG (args_so_far, passed_mode, - DECL_ARG_TYPE (parm), ! last_named); -#else - entry_parm - = FUNCTION_ARG (args_so_far, passed_mode, DECL_ARG_TYPE (parm), - ! last_named); -#endif - } - -#ifdef REG_PARM_STACK_SPACE - /* If we arrive at a stack parm while still counting space for reg parms, - skip up to the offset for the first stack parm. */ - if (entry_parm == 0 - && stack_args_size.constant + first_parm_caller_offset < 0) - { - int adjustment - = -(stack_args_size.constant + first_parm_caller_offset); - stack_args_size.constant += adjustment; - stack_offset.constant += adjustment; - } -#endif - - stack_offset_rtx = ARGS_SIZE_RTX (stack_offset); - - /* Determine parm's home in the stack, - in case it arrives in the stack or we should pretend it did. */ - /* Note that this is not necessarily a valid address. - We make it valid later when it is used. - It is necessary for the DECL_RTL to be an explicit stack slot, - but not necessary for it to be valid. */ - stack_parm - = gen_rtx (MEM, passed_mode, - gen_rtx (PLUS, Pmode, - internal_arg_pointer, - stack_offset_rtx)); - - /* If this is a memory ref that contains aggregate components, - mark it as such for cse and loop optimize. */ - MEM_IN_STRUCT_P (stack_parm) = aggregate; - - /* If this parm was passed part in regs and part in memory, - pretend it arrived entirely in memory - by pushing the register-part onto the stack. - - In the special case of a DImode or DFmode that is split, - we could put it together in a pseudoreg directly, - but for now that's not worth bothering with. */ - - if (entry_parm) - { - int nregs = 0; - int i; -#ifdef FUNCTION_ARG_PARTIAL_NREGS - nregs = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, passed_mode, - DECL_ARG_TYPE (parm), 1); -#endif - -#if 0 /* Replaced by new calling convention - which actually passes these args on the stack. */ - /* If this is the last named arg and anonymous args follow, - likewise pretend this arg arrived on the stack - so varargs can find the anonymous args following it. */ - if (TREE_CHAIN (parm) == 0 && vararg) - { - if (GET_MODE (entry_parm) == BLKmode) - nregs = GET_MODE_SIZE (GET_MODE (entry_parm)) / UNITS_PER_WORD; - else - nregs = (int_size_in_bytes (DECL_ARG_TYPE (parm)) - / UNITS_PER_WORD); - } -#endif /* 0 */ - - if (nregs > 0) - { - rtx valid_stack_parm = validize_mem (stack_parm); - current_function_pretend_args_size - = (((nregs * UNITS_PER_WORD) + (PARM_BOUNDARY / BITS_PER_UNIT) - 1) - / (PARM_BOUNDARY / BITS_PER_UNIT) - * (PARM_BOUNDARY / BITS_PER_UNIT)); - - i = nregs; - while (--i >= 0) - emit_move_insn (gen_rtx (MEM, SImode, - plus_constant (XEXP (valid_stack_parm, 0), - i * GET_MODE_SIZE (SImode))), - gen_rtx (REG, SImode, REGNO (entry_parm) + i)); - entry_parm = stack_parm; - } - } - - /* If we didn't decide this parm came in a register, - by default it came on the stack. */ - if (entry_parm == 0) - entry_parm = stack_parm; - - /* For a stack parm, record in DECL_OFFSET the arglist offset - of the parm at the time it is passed (before conversion). */ - if (entry_parm == stack_parm) - DECL_OFFSET (parm) = stack_offset.constant * BITS_PER_UNIT; - - /* If there is actually space on the stack for this parm, - count it in stack_args_size; otherwise set stack_parm to 0 - to indicate there is no preallocated stack slot for the parm. */ - - if (entry_parm == stack_parm -#ifdef REG_PARM_STACK_SPACE - /* On some machines, even if a parm value arrives in a register - there is still an (uninitialized) stack slot allocated for it. */ - || 1 -#endif - ) - { - tree sizetree = size_in_bytes (DECL_ARG_TYPE (parm)); -#ifdef PUSH_ROUNDING - /* If this arg will be pushed with a push instruction, - note how that will add to its size. */ - if (DECL_MODE (parm) != BLKmode) - { - int old_bytes = int_size_in_bytes (DECL_ARG_TYPE (parm)); - sizetree = build_int_2 (PUSH_ROUNDING (old_bytes), 0); - } -#endif - if (where_pad != none) - { - /* Round the size up to multiple of PARM_BOUNDARY bits. */ - tree s1 = convert_units (sizetree, BITS_PER_UNIT, PARM_BOUNDARY); - sizetree = convert_units (s1, PARM_BOUNDARY, BITS_PER_UNIT); - } - /* Add it in. */ - ADD_PARM_SIZE (stack_args_size, sizetree); - } - else - /* No stack slot was pushed for this parm. */ - stack_parm = 0; - - /* Now adjust STACK_PARM to the mode and precise location - where this parameter should live during execution, - if we discover that it must live in the stack during execution. - To make debuggers happier on big-endian machines, we store - the value in the last bytes of the space available. */ - - if (nominal_mode != BLKmode && nominal_mode != passed_mode - && stack_parm != 0) - { -#ifdef BYTES_BIG_ENDIAN - if (GET_MODE_SIZE (nominal_mode) < UNITS_PER_WORD) - { - stack_offset.constant - += GET_MODE_SIZE (passed_mode) - - GET_MODE_SIZE (nominal_mode); - stack_offset_rtx = ARGS_SIZE_RTX (stack_offset); - } -#endif - - stack_parm - = gen_rtx (MEM, nominal_mode, - gen_rtx (PLUS, Pmode, - arg_pointer_rtx, - stack_offset_rtx)); - - /* If this is a memory ref that contains aggregate components, - mark it as such for cse and loop optimize. */ - MEM_IN_STRUCT_P (stack_parm) = aggregate; - } - - /* ENTRY_PARM is an RTX for the parameter as it arrives, - in the mode in which it arrives. - STACK_PARM is an RTX for a stack slot where the parameter can live - during the function (in case we want to put it there). - STACK_PARM is 0 if no stack slot was pushed for it. - - Now output code if necessary to convert ENTRY_PARM to - the type in which this function declares it, - and store that result in an appropriate place, - which may be a pseudo reg, may be STACK_PARM, - or may be a local stack slot if STACK_PARM is 0. - - Set DECL_RTL to that place. */ - - if (nominal_mode == BLKmode) - { - /* If a BLKmode arrives in registers, copy it to a stack slot. */ - if (GET_CODE (entry_parm) == REG) - { -#if 0 /* This was probably wrong, but save it just in case. */ - rtx unpadded_stack_parm; - - /* Determine parm's home in the stack. */ - - if (stack_parm == 0) - unpadded_stack_parm - = assign_stack_local (GET_MODE (entry_parm), - int_size_in_bytes (TREE_TYPE (parm))); - else - unpadded_stack_parm - = gen_rtx (MEM, passed_mode, - memory_address (passed_mode, - gen_rtx (PLUS, Pmode, - internal_arg_pointer, - ARGS_SIZE_RTX (unpadded_stack_offset)))); - - /* Here we use unpadded_stack_parm because we assume - that downward padding is used on big-endian machines - where we would want to make the real data in the reg - (which is in the low bits) end up at the padded address. */ -#endif - if (stack_parm == 0) - stack_parm - = assign_stack_local (GET_MODE (entry_parm), - int_size_in_bytes (TREE_TYPE (parm))); - - move_block_from_reg (REGNO (entry_parm), - validize_mem (stack_parm), - ((int_size_in_bytes (TREE_TYPE (parm)) - + UNITS_PER_WORD - 1) - / UNITS_PER_WORD)); - } - DECL_RTL (parm) = stack_parm; - } - else if (! ((obey_regdecls && ! TREE_REGDECL (parm) - && ! TREE_INLINE (fndecl)) - /* layout_decl may set this. */ - || TREE_ADDRESSABLE (parm) - || TREE_VOLATILE (parm) - /* If -ffloat-store specified, don't put explicit - float variables into registers. */ - || (flag_float_store - && TREE_CODE (TREE_TYPE (parm)) == REAL_TYPE))) - { - /* Store the parm in a pseudoregister during the function. */ - register rtx parmreg = gen_reg_rtx (nominal_mode); - - REG_USERVAR_P (parmreg) = 1; - DECL_RTL (parm) = parmreg; - - /* Copy the value into the register. */ - if (GET_MODE (parmreg) != GET_MODE (entry_parm)) - convert_move (parmreg, validize_mem (entry_parm), 0); - else - emit_move_insn (parmreg, validize_mem (entry_parm)); - - /* In any case, record the parm's desired stack location - in case we later discover it must live in the stack. */ - if (REGNO (parmreg) >= nparmregs) - { - rtx *new; - nparmregs = REGNO (parmreg) + 5; - new = (rtx *) oballoc (nparmregs * sizeof (rtx)); - bcopy (parm_reg_stack_loc, new, nparmregs * sizeof (rtx)); - parm_reg_stack_loc = new; - } - parm_reg_stack_loc[REGNO (parmreg)] = stack_parm; - - /* Mark the register as eliminable if we did no conversion - and it was copied from memory at a fixed offset, - and the arg pointer was not copied to a pseudo-reg. - If the arg pointer is a pseudo reg, such memory-equivalences - as we make here would screw up life analysis for it. */ - if (nominal_mode == passed_mode - && GET_CODE (entry_parm) == MEM - && stack_offset.var == 0 - && ! arg_pointer_copied) - REG_NOTES (get_last_insn ()) - = gen_rtx (EXPR_LIST, REG_EQUIV, - entry_parm, REG_NOTES (get_last_insn ())); - - /* For pointer data type, suggest pointer register. */ - if (TREE_CODE (TREE_TYPE (parm)) == POINTER_TYPE) - mark_reg_pointer (parmreg); - } - else - { - /* Value must be stored in the stack slot STACK_PARM - during function execution. */ - - if (passed_mode != nominal_mode) - /* Conversion is required. */ - entry_parm = convert_to_mode (nominal_mode, - validize_mem (entry_parm), 0); - - if (entry_parm != stack_parm) - { - if (stack_parm == 0) - stack_parm = assign_stack_local (GET_MODE (entry_parm), - GET_MODE_SIZE (GET_MODE (entry_parm))); - emit_move_insn (validize_mem (stack_parm), - validize_mem (entry_parm)); - } - - DECL_RTL (parm) = stack_parm; - frame_pointer_needed = 1; - } - - if (TREE_VOLATILE (parm)) - MEM_VOLATILE_P (DECL_RTL (parm)) = 1; - if (TREE_READONLY (parm)) - RTX_UNCHANGING_P (DECL_RTL (parm)) = 1; - - /* Update info on where next arg arrives in registers. */ - - FUNCTION_ARG_ADVANCE (args_so_far, passed_mode, DECL_ARG_TYPE (parm), 1); - } - - max_parm_reg = max_reg_num (); - last_parm_insn = get_last_insn (); - - current_function_args_size = stack_args_size.constant; - - stack_args_size.constant += first_parm_offset; - current_function_arg_offset_rtx = ARGS_SIZE_RTX (stack_args_size); -} - -/* Allocation of space for returned structure values. - During the rtl generation pass, `get_structure_value_addr' - is called from time to time to request the address of a block in our - stack frame in which called functions will store the structures - they are returning. The same space is used for all of these blocks. - - We allocate these blocks like stack locals. We keep reusing - the same block until a bigger one is needed. */ - -/* Length in bytes of largest structure value returned by - any function called so far in this function. */ -static int max_structure_value_size; - -/* An rtx for the addr we are currently using for structure values. - This is typically (PLUS (REG:SI stackptr) (CONST_INT...)). */ -static rtx structure_value; - -rtx -get_structure_value_addr (sizex) - rtx sizex; -{ - register int size; - if (GET_CODE (sizex) != CONST_INT) - abort (); - size = INTVAL (sizex); - - /* Round up to a multiple of the main allocation unit. */ - size = (((size + (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1) - / (BIGGEST_ALIGNMENT / BITS_PER_UNIT)) - * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); - - /* If this size is bigger than space we know to use, - get a bigger piece of space. */ - if (size > max_structure_value_size) - { - max_structure_value_size = size; - structure_value = assign_stack_local (BLKmode, size); - if (GET_CODE (structure_value) == MEM) - structure_value = XEXP (structure_value, 0); - } - - return structure_value; -} - -/* Push and pop the current structure value block. */ - -void -push_structure_value (rtx_ptr, size_ptr) - rtx *rtx_ptr; - int *size_ptr; -{ - *rtx_ptr = structure_value; - *size_ptr = max_structure_value_size; - max_structure_value_size = 0; - structure_value = 0; -} - -void -pop_structure_value (rtx_value, size) - rtx rtx_value; - int size; -{ - structure_value = rtx_value; - max_structure_value_size = size; -} - - -/* Walk the tree of LET_STMTs describing the binding levels within a function - and warn about uninitialized variables. - This is done after calling flow_analysis and before global_alloc - clobbers the pseudo-regs to hard regs. */ - -void -uninitialized_vars_warning (block) - tree block; -{ - register tree decl, sub; - for (decl = STMT_VARS (block); decl; decl = TREE_CHAIN (decl)) - { - if (TREE_CODE (decl) == VAR_DECL - /* These warnings are unreliable for and aggregates - because assigning the fields one by one can fail to convince - flow.c that the entire aggregate was initialized. - Unions are troublesome because members may be shorter. */ - && TREE_CODE (TREE_TYPE (decl)) != RECORD_TYPE - && TREE_CODE (TREE_TYPE (decl)) != UNION_TYPE - && TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE - && DECL_RTL (decl) != 0 - && GET_CODE (DECL_RTL (decl)) == REG - && regno_uninitialized (REGNO (DECL_RTL (decl)))) - warning_with_decl (decl, - "`%s' may be used uninitialized in this function"); - if (TREE_CODE (decl) == VAR_DECL - && DECL_RTL (decl) != 0 - && GET_CODE (DECL_RTL (decl)) == REG - && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl)))) - warning_with_decl (decl, - "variable `%s' may be clobbered by `longjmp'"); - } - for (sub = STMT_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub)) - uninitialized_vars_warning (sub); -} - -/* If this function call setjmp, put all vars into the stack - unless they were declared `register'. */ - -void -setjmp_protect (block) - tree block; -{ - register tree decl, sub; - for (decl = STMT_VARS (block); decl; decl = TREE_CHAIN (decl)) - if ((TREE_CODE (decl) == VAR_DECL - || TREE_CODE (decl) == PARM_DECL) - && DECL_RTL (decl) != 0 - && GET_CODE (DECL_RTL (decl)) == REG - && ! TREE_REGDECL (decl)) - put_var_into_stack (decl); - for (sub = STMT_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub)) - setjmp_protect (sub); -} - -/* Generate RTL for the start of the function SUBR (a FUNCTION_DECL tree node) - and initialize static variables for generating RTL for the statements - of the function. */ - -void -init_function_start (subr, filename, line) - tree subr; - char *filename; - int line; -{ - this_function = subr; - cse_not_expected = ! optimize; - - /* We have not yet found a reason why a frame pointer cannot - be omitted for this function in particular, but maybe we know - a priori that it is required. - `flag_omit_frame_pointer' has its main effect here. */ - frame_pointer_needed = FRAME_POINTER_REQUIRED || ! flag_omit_frame_pointer; - - /* Caller save not needed yet. */ - caller_save_needed = 0; - - /* No gotos have been expanded yet. */ - goto_fixup_chain = 0; - - /* No stack slots have been made yet. */ - stack_slot_list = 0; - - /* No invalid stack slots have been made yet. */ - invalid_stack_slot = 0; - - /* No parm regs have been allocated. - (This is important for output_inline_function.) */ - max_parm_reg = FIRST_PSEUDO_REGISTER; - - /* Initialize the RTL mechanism. */ - init_emit (write_symbols); - - /* Initialize the queue of pending postincrement and postdecrements, - and some other info in expr.c. */ - init_expr (); - - init_const_rtx_hash_table (); - - /* Decide whether function should try to pop its args on return. */ - - current_function_pops_args = RETURN_POPS_ARGS (TREE_TYPE (subr)); - - current_function_name = DECL_PRINT_NAME (subr); - - /* Nonzero if this is a nested function that uses a static chain. */ - - current_function_needs_context - = (DECL_CONTEXT (current_function_decl) != 0 - && TREE_CODE (DECL_CONTEXT (current_function_decl)) == LET_STMT); - - /* Set if a call to setjmp is seen. */ - - current_function_calls_setjmp = 0; - current_function_calls_alloca = 0; - - current_function_returns_pcc_struct = 0; - current_function_returns_struct = 0; - - /* No space assigned yet for structure values. */ - max_structure_value_size = -1; - structure_value = 0; - - /* We are not currently within any block, conditional, loop or case. */ - block_stack = 0; - loop_stack = 0; - case_stack = 0; - cond_stack = 0; - nesting_stack = 0; - nesting_depth = 0; - - block_start_count = 0; - - /* We have not yet needed to make a label to jump to for tail-recursion. */ - tail_recursion_label = 0; - - /* No stack slots allocated yet. */ - frame_offset = STARTING_FRAME_OFFSET; - - /* No SAVE_EXPRs in this function yet. */ - save_expr_regs = 0; - - /* No RTL_EXPRs in this function yet. */ - rtl_expr_chain = 0; - - /* Within function body, compute a type's size as soon it is laid out. */ - immediate_size_expand++; - - init_pending_stack_adjust (); - inhibit_defer_pop = 0; - current_function_pretend_args_size = 0; - - /* Prevent ever trying to delete the first instruction of a function. - Also tell final how to output a linenum before the function prologue. */ - emit_line_note (filename, line); - /* Make sure first insn is a note even if we don't want linenums. - This makes sure the first insn will never be deleted. - Also, final expects a note to appear there. */ - emit_note (0, NOTE_INSN_DELETED); - /* Indicate the beginning of the function body, - as opposed to parm setup. */ - emit_note (0, NOTE_INSN_FUNCTION_BEG); - - /* Set flags used by final.c. */ - if (aggregate_value_p (DECL_RESULT (subr))) - { -#ifdef PCC_STATIC_STRUCT_RETURN - if (flag_pcc_struct_return) - current_function_returns_pcc_struct = 1; - else -#endif - current_function_returns_struct = 1; - } -} - -/* Start the RTL for a new function, and set variables used for - emitting RTL. - SUBR is the FUNCTION_DECL node. - PARMS_HAVE_CLEANUPS is nonzero if there are cleanups associated with - the function's parameters, which must be run at any return statement. */ - -void -expand_function_start (subr, parms_have_cleanups) - tree subr; - int parms_have_cleanups; -{ - register int i; - tree tem; - - /* Make sure volatile mem refs aren't considered - valid operands of arithmetic insns. */ - init_recog (); - - /* If the parameters of this function need cleaning up, get a label - for the beginning of the code which executes those cleanups. This must - be done before doing anything with return_label. */ - if (parms_have_cleanups) - cleanup_label = gen_label_rtx (); - else - cleanup_label = 0; - - /* Make the label for return statements to jump to, if this machine - does not have a one-instruction return and uses an epilogue, - or if it returns a structure, or if it has parm cleanups. */ -#ifdef HAVE_return - if (cleanup_label == 0 && HAVE_return - && ! current_function_returns_pcc_struct - && ! (current_function_returns_struct && ! optimize)) - return_label = 0; - else - return_label = gen_label_rtx (); -#else - return_label = gen_label_rtx (); -#endif - - /* Initialize rtx used to return the value. */ - /* Do this before assign_parms so that we copy the struct value address - before any library calls that assign parms might generate. */ - - /* Decide whether to return the value in memory or in a register. */ - if (aggregate_value_p (DECL_RESULT (subr))) - { - /* Returning something that won't go in a register. */ - register rtx value_address; - -#ifdef PCC_STATIC_STRUCT_RETURN - if (flag_pcc_struct_return) - { - int size = int_size_in_bytes (TREE_TYPE (DECL_RESULT (subr))); - value_address = assemble_static_space (size); - current_function_returns_pcc_struct = 1; - } - else -#endif - { - /* Expect to be passed the address of a place to store the value. */ - value_address = gen_reg_rtx (Pmode); - emit_move_insn (value_address, struct_value_incoming_rtx); - current_function_returns_struct = 1; - } - DECL_RTL (DECL_RESULT (subr)) - = gen_rtx (MEM, DECL_MODE (DECL_RESULT (subr)), - value_address); - } - else if (DECL_MODE (DECL_RESULT (subr)) == VOIDmode) - /* If return mode is void, this decl rtl should not be used. */ - DECL_RTL (DECL_RESULT (subr)) = 0; - else if (parms_have_cleanups) - /* If function will end with cleanup code for parms, - compute the return values into a pseudo reg, - which we will copy into the true return register - after the cleanups are done. */ - DECL_RTL (DECL_RESULT (subr)) - = gen_reg_rtx (DECL_MODE (DECL_RESULT (subr))); - else - /* Scalar, returned in a register. */ - { -#ifdef FUNCTION_OUTGOING_VALUE - DECL_RTL (DECL_RESULT (subr)) - = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (subr)), subr); -#else - DECL_RTL (DECL_RESULT (subr)) - = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (subr)), subr); -#endif - - current_function_returns_pointer - = (TREE_CODE (DECL_RESULT_TYPE (subr)) == POINTER_TYPE); - - /* Mark this reg as the function's return value. */ - if (GET_CODE (DECL_RTL (DECL_RESULT (subr))) == REG) - REG_FUNCTION_VALUE_P (DECL_RTL (DECL_RESULT (subr))) = 1; - } - - /* Initialize rtx for parameters and local variables. - In some cases this requires emitting insns. */ - - assign_parms (subr); - - /* If doing stupid allocation, mark parms as born here. */ - - if (GET_CODE (get_last_insn ()) != NOTE) - emit_note (0, NOTE_INSN_DELETED); - parm_birth_insn = get_last_insn (); - - if (obey_regdecls) - { - for (i = FIRST_PSEUDO_REGISTER; i < max_parm_reg; i++) - use_variable (regno_reg_rtx[i]); - } - - /* After the parm initializations is where the tail-recursion label - should go, if we end up needing one. */ - tail_recursion_reentry = get_last_insn (); - - /* Evaluate now the sizes of any types declared among the arguments. */ - for (tem = get_pending_sizes (); tem; tem = TREE_CHAIN (tem)) - expand_expr (TREE_VALUE (tem), 0, VOIDmode, 0); - - /* Make sure there is a line number after the function entry setup code. - There normally is one anyway, from the following statement, - but there could fail to be one if there is no newline here. */ - force_next_line_note (); -} - -/* Generate RTL for the end of the current function. - FILENAME and LINE are the current position in the source file. */ - -/* ??? Nobody seems to emit the cleanup_label and the cleanups themselves. */ - -void -expand_function_end (filename, line) - char *filename; - int line; -{ - register int i; - rtx decl; - extern rtx sequence_stack; - -#if 0 /* I think unused parms are legitimate enough. */ - /* Warn about unused parms. */ - if (warn_unused) - for (decl = DECL_ARGUMENTS (current_function_decl); - decl; decl = TREE_CHAIN (decl)) - if (! TREE_USED (decl) && TREE_CODE (decl) == VAR_DECL) - warning_with_decl (decl, "unused parameter `%s'"); -#endif - - /* End any sequences that failed to be closed due to syntax errors. */ - while (sequence_stack) - end_sequence (0); - - /* Outside function body, can't compute type's actual size - until next function's body starts. */ - immediate_size_expand--; - - /* If doing stupid register allocation, - mark register parms as dying here. */ - - if (obey_regdecls) - { - rtx tem; - for (i = FIRST_PSEUDO_REGISTER; i < max_parm_reg; i++) - use_variable (regno_reg_rtx[i]); - - /* Likewise for the regs of all the SAVE_EXPRs in the function. */ - - for (tem = save_expr_regs; tem; tem = XEXP (tem, 1)) - { - /* ??? Tiemann thinks this does not work. */ - use_variable (XEXP (tem, 0)); - use_variable_after (XEXP (tem, 0), parm_birth_insn); - } - } - - clear_pending_stack_adjust (); - do_pending_stack_adjust (); - - /* Mark the end of the function body. - If control reaches this insn, the function can drop through - without returning a value. */ - emit_note (0, NOTE_INSN_FUNCTION_END); - - /* Output a linenumber for the end of the function. - SDB depends on this. */ - emit_line_note_force (filename, line); - - /* Output the label for the actual return from the function, - if one is expected. This happens either because a function epilogue - is used instead of a return instruction, or because a return was done - with a goto in order to run local cleanups, or because of pcc-style - structure returning. */ - - if (return_label) - emit_label (return_label); - - /* If we had calls to alloca, and this machine needs - an accurate stack pointer to exit the function, - insert some code to save and restore the stack pointer. */ -#ifdef EXIT_IGNORE_STACK - if (! EXIT_IGNORE_STACK) -#endif - if (current_function_calls_alloca) - { - rtx tem = gen_reg_rtx (Pmode); - emit_insn_after (gen_rtx (SET, VOIDmode, tem, stack_pointer_rtx), - parm_birth_insn); - emit_insn (gen_rtx (SET, VOIDmode, stack_pointer_rtx, tem)); - } - - /* If scalar return value was computed in a pseudo-reg, - copy that to the hard return register. */ - if (DECL_RTL (DECL_RESULT (current_function_decl)) != 0 - && GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) == REG - && (REGNO (DECL_RTL (DECL_RESULT (current_function_decl))) - >= FIRST_PSEUDO_REGISTER)) - { - rtx real_decl_result; - -#ifdef FUNCTION_OUTGOING_VALUE - real_decl_result - = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (current_function_decl)), - current_function_decl); -#else - real_decl_result - = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (current_function_decl)), - current_function_decl); -#endif - REG_FUNCTION_VALUE_P (real_decl_result) = 1; - emit_move_insn (real_decl_result, - DECL_RTL (DECL_RESULT (current_function_decl))); - emit_insn (gen_rtx (USE, VOIDmode, real_decl_result)); - } - - /* If returning a structure, arrange to return the address of the value - in a place where debuggers expect to find it. */ - /* If returning a structure PCC style, - the caller also depends on this value. - And current_function_returns_pcc_struct is not necessarily set. */ - if (current_function_returns_struct - || current_function_returns_pcc_struct) - { - rtx value_address = XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0); - tree type = TREE_TYPE (DECL_RESULT (current_function_decl)); -#ifdef FUNCTION_OUTGOING_VALUE - rtx outgoing - = FUNCTION_OUTGOING_VALUE (build_pointer_type (type), - current_function_decl); -#else - rtx outgoing - = hard_function_value (build_pointer_type (type), - current_function_decl); -#endif - - REG_FUNCTION_VALUE_P (outgoing) = 1; - emit_move_insn (outgoing, value_address); - use_variable (outgoing); - } - - /* Output a return insn if we are using one. - Otherwise, let the rtl chain end here, to drop through - into the epilogue. */ - -#ifdef HAVE_return - if (HAVE_return) - emit_jump_insn (gen_return ()); -#endif - - /* Fix up any gotos that jumped out to the outermost - binding level of the function. - Must follow emitting RETURN_LABEL. */ - - /* If you have any cleanups to do at this point, - and they need to create temporary variables, - then you will lose. */ - fixup_gotos (0, 0, 0, get_insns (), 0); -} diff --git a/gnu/usr.bin/gcc1/cc1/stor-layout.c b/gnu/usr.bin/gcc1/cc1/stor-layout.c deleted file mode 100644 index 12f624780e..0000000000 --- a/gnu/usr.bin/gcc1/cc1/stor-layout.c +++ /dev/null @@ -1,1075 +0,0 @@ -/* C-compiler utilities for types and variables storage layout - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "config.h" -#include - -#include "tree.h" -#include "rtl.h" /* For GET_MODE_SIZE */ - -#define MAX(x,y) ((x) > (y) ? (x) : (y)) -#define MIN(x,y) ((x) < (y) ? (x) : (y)) -#define CEIL(x,y) (((x) + (y) - 1) / (y)) - -/* Data type for the expressions representing sizes of data types. - It is the first integer type laid out. - In C, this is int. */ - -tree sizetype; - -/* An integer constant with value 0 whose type is sizetype. */ - -tree size_zero_node; - -/* An integer constant with value 1 whose type is sizetype. */ - -tree size_one_node; - -#define GET_MODE_ALIGNMENT(MODE) \ - MIN (BIGGEST_ALIGNMENT, \ - MAX (1, (GET_MODE_UNIT_SIZE (MODE) * BITS_PER_UNIT))) - -/* Chain of all permanent types we have allocated since last - call to get_permanent_types. */ - -tree permanent_type_chain; - -/* Chain of all temporary types we have allocated in this function. */ - -tree temporary_type_chain; - -/* When the chains is not null, these point at the last - types on the two chains. These help us tell whether a type - is already on a chain. */ -tree permanent_type_end; -tree temporary_type_end; - -/* Put the newly-made type T - on either permanent_type_chain or temporary_type_chain. - Types that are const or volatile variants of other types - are not put on any chain, since in the gdb symbol segment - we do not make those distinctions. - - If T is already on the chain, we do nothing. */ - -void -chain_type (t) - tree t; -{ - if (TYPE_MAIN_VARIANT (t) != t) - return; - if (TREE_CHAIN (t) != 0) - return; - if (TREE_PERMANENT (t)) - { - /* If T is on the chain at the end, don't chain it to itself! */ - if (t == permanent_type_end) - return; - /* Add T to the end of the chain. */ - if (permanent_type_chain == 0) - permanent_type_chain = t; - else - TREE_CHAIN (permanent_type_end) = t; - permanent_type_end = t; - } - else - { - if (t == temporary_type_end) - return; - if (temporary_type_chain == 0) - temporary_type_chain = t; - else - TREE_CHAIN (temporary_type_end) = t; - temporary_type_end = t; - } -} - -/* Get a chain of all permanent types made since this function - was last called. */ - -tree -get_permanent_types () -{ - register tree tem = permanent_type_chain; - permanent_type_chain = 0; - permanent_type_end = 0; - return tem; -} - -/* Get a chain of all temporary types made since this function - was last called. */ - -tree -get_temporary_types () -{ - register tree tem = temporary_type_chain; - temporary_type_chain = 0; - temporary_type_end = 0; - return tem; -} - -/* SAVE_EXPRs for sizes of types and decls, waiting to be expanded. */ - -static tree pending_sizes; - -/* Nonzero means cannot safely call expand_expr now, - so put variable sizes onto `pending_sizes' instead. */ - -int immediate_size_expand; - -tree -get_pending_sizes () -{ - tree chain = pending_sizes; - pending_sizes = 0; - return chain; -} - -/* Given a size SIZE that isn't constant, return a SAVE_EXPR - to serve as the actual size-expression for a type or decl. */ - -static tree -variable_size (size) - tree size; -{ - size = save_expr (size); - - if (global_bindings_p ()) - { - error ("variable-size type declared outside of any function"); - return build_int (1); - } - - if (immediate_size_expand) - expand_expr (size, 0, VOIDmode, 0); - else - pending_sizes = tree_cons (0, size, pending_sizes); - - return size; -} - -/* Return the machine mode to use for an aggregate of SIZE bits. - - Note!!! We only use a non-BLKmode mode if the size matches exactly. - There used to be the idea of using DImode for anything whose - size was less than DImode but more than SImode. This does not work - because DImode moves cannot be used to store such objects in memory. */ - -#ifndef MAX_FIXED_MODE_SIZE -#define MAX_FIXED_MODE_SIZE GET_MODE_BITSIZE (DImode) -#endif - -static -enum machine_mode -agg_mode (size) - unsigned int size; -{ - register int units = size / BITS_PER_UNIT; - register enum machine_mode t, val; - - if (size % BITS_PER_UNIT != 0) - return BLKmode; - - if (size > MAX_FIXED_MODE_SIZE) - return BLKmode; - - /* Get the last mode which has this size. */ - val = BLKmode; - for (t = QImode; GET_MODE_CLASS (t) == MODE_INT; - t = (enum machine_mode) ((int) t + 1)) - if (GET_MODE_SIZE (t) == units) - val = t; - - return val; -} - -/* Return an INTEGER_CST with value V and type from `sizetype'. */ - -tree -build_int (v) - unsigned int v; -{ - register tree t; - /* Type-size nodes already made for small sizes. */ - static tree size_table[33]; - - if (v < 33 && size_table[v] != 0) - return size_table[v]; - if (v < 33) - { - int temp = allocation_temporary_p (); - /* Make this a permanent node. */ - if (temp) - end_temporary_allocation (); - t = build_int_2 (v, 0); - TREE_TYPE (t) = sizetype; - size_table[v] = t; - if (temp) - resume_temporary_allocation (); - } - else - { - t = build_int_2 (v, 0); - TREE_TYPE (t) = sizetype; - } - return t; -} - -/* Combine operands OP1 and OP2 with arithmetic operation OPC. - OPC is a tree code. Data type is taken from `sizetype', - If the operands are constant, so is the result. */ - -tree -genop (opc, op1, op2) - enum tree_code opc; - tree op1, op2; -{ - /* Handle the special case of two integer constants faster. */ - if (TREE_CODE (op1) == INTEGER_CST && TREE_CODE (op2) == INTEGER_CST) - { - /* And some specific cases even faster than that. */ - if (opc == PLUS_EXPR - && TREE_INT_CST_LOW (op1) == 0 - && TREE_INT_CST_HIGH (op1) == 0) - return op2; - if (opc == MINUS_EXPR - && TREE_INT_CST_LOW (op2) == 0 - && TREE_INT_CST_HIGH (op2) == 0) - return op1; - if (opc == MULT_EXPR - && TREE_INT_CST_LOW (op1) == 1 - && TREE_INT_CST_HIGH (op1) == 0) - return op2; - if (opc == CEIL_DIV_EXPR - && TREE_INT_CST_LOW (op1) == TREE_INT_CST_LOW (op2) - && TREE_INT_CST_HIGH (op1) == TREE_INT_CST_HIGH (op2)) - return size_one_node; - /* Handle general case of two integer constants. */ - return combine (opc, op1, op2); - } - - if (op1 == error_mark_node || op2 == error_mark_node) - return error_mark_node; - - return fold (build (opc, sizetype, op1, op2)); -} - -/* Convert a size which is SIZE when expressed in unit INUNITS - into the units OUTUNITS. Rounds up if conversion is not exact. - If SIZE is constant, so is the result. */ - -tree -convert_units (size, inunits, outunits) - tree size; - register int inunits, outunits; -{ - register tree t; - - if (inunits == outunits) - return size; - /* Check for inunits divisible by outunits. - In that case, just multiply by their ratio. */ - if (0 == (inunits % outunits)) - return genop (MULT_EXPR, size, build_int (inunits / outunits)); - /* The inverse case. */ - if (0 == (outunits % inunits)) - { - /* Discard anything in SIZE to round it up to a multiple - of a number N that divides our current divisor. */ - if (TREE_CODE (size) == MULT_EXPR - && TREE_CODE (TREE_OPERAND (size, 1)) == INTEGER_CST - && 0 == (outunits / inunits) % TREE_INT_CST_LOW (TREE_OPERAND (size, 1)) - && TREE_CODE (TREE_OPERAND (size, 0)) == CEIL_DIV_EXPR - && tree_int_cst_equal (TREE_OPERAND (size, 1), - TREE_OPERAND (TREE_OPERAND (size, 0), 1))) - size = TREE_OPERAND (TREE_OPERAND (size, 0), 0); - return genop (CEIL_DIV_EXPR, size, build_int (outunits / inunits)); - } - /* The general case. */ - t = genop (MULT_EXPR, size, - build_int (inunits)); /* convert to bits */ - return genop (CEIL_DIV_EXPR, t, - build_int (outunits)); /* then to outunits */ -} - -/* Set the size, mode and alignment of a ..._DECL node. - TYPE_DECL does need this for C++. It is up to language-specific - code to intialize the DECL_OFFSET of TYPE_DECL nodes. - Note that LABEL_DECL and CONST_DECL nodes do not need this, - and FUNCTION_DECL nodes have them set up in a special (and simple) way. - Don't call layout_decl for them. - - KNOWN_ALIGN is the amount of alignment we can assume this - decl has with no special effort. It is relevant only for FIELD_DECLs - and depends on the previous fields. - All that matters about KNOWN_ALIGN is which powers of 2 divide it. - If KNOWN_ALIGN is 0, it means, "as much alignment as you like": - the record will be aligned to suit. */ - -void -layout_decl (decl, known_align) - tree decl; - unsigned known_align; -{ - register tree type = TREE_TYPE (decl); - register enum tree_code code = TREE_CODE (decl); - int spec_size = DECL_SIZE_UNIT (decl); - int bitsize; - - if (code == CONST_DECL) - return; - - if (code != VAR_DECL && code != PARM_DECL && code != RESULT_DECL - && code != FIELD_DECL && code != TYPE_DECL) - abort (); - - if (type == error_mark_node) - { - type = void_type_node; - spec_size = 0; - } - if (TYPE_SIZE_UNIT (type) == 0) - abort (); - - /* Usually the size and mode come from the data type without change. */ - - DECL_MODE (decl) = TYPE_MODE (type); - DECL_SIZE (decl) = TYPE_SIZE (type); - DECL_SIZE_UNIT (decl) = TYPE_SIZE_UNIT (type); - TREE_UNSIGNED (decl) = TREE_UNSIGNED (type); - - if (code == FIELD_DECL && TREE_PACKED (decl)) - { - /* This is a bit-field. We don't know how to handle - them except for integers and enums, and front end should - never generate them otherwise. */ - - if (! (TREE_CODE (type) == INTEGER_TYPE - || TREE_CODE (type) == ENUMERAL_TYPE)) - abort (); - - if (spec_size == 0) - abort (); - - /* Mode is "integer bit field". */ - DECL_MODE (decl) = BImode; - /* Size is specified number of bits. */ - DECL_SIZE (decl) = size_one_node; - DECL_SIZE_UNIT (decl) = spec_size; - } - /* Force alignment required for the data type. - But if the decl itself wants greater alignment, don't override that. */ - else if (TYPE_ALIGN (type) > DECL_ALIGN (decl)) - DECL_ALIGN (decl) = TYPE_ALIGN (type); - - if (DECL_SIZE (decl)) - bitsize = TREE_INT_CST_LOW (DECL_SIZE (decl)) * DECL_SIZE_UNIT (decl); - - /* See if we can use a scalar mode such as QImode or SImode - in place of BLKmode or a packed byte mode. */ - /* Conditions are: a fixed size that is correct for another mode - and occupying a complete byte or bytes on proper boundary. */ - if ((DECL_MODE (decl) == BLKmode - || DECL_MODE (decl) == BImode) - /* Don't do this if DECL's type requires it to be BLKmode. */ - && TYPE_MODE (type) != BLKmode - && TYPE_SIZE (type) != 0 - && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST) - { - register enum machine_mode xmode = agg_mode (bitsize); - - if (xmode != BLKmode - && known_align % GET_MODE_ALIGNMENT (xmode) == 0) - { - DECL_ALIGN (decl) = MAX (GET_MODE_ALIGNMENT (xmode), - DECL_ALIGN (decl)); - DECL_MODE (decl) = xmode; - DECL_SIZE (decl) = build_int (GET_MODE_SIZE (xmode)); - DECL_SIZE_UNIT (decl) = BITS_PER_UNIT; - bitsize = GET_MODE_BITSIZE (xmode); - } - } - - /* Don't let more than one word of an aggregate occupy one register, - since then the SUBREG used to access the high part would malfunction. - Check that the expected # of registers is big enough that they - seem to hold this variable with just a word per register. */ - if (DECL_SIZE (decl) != 0 - && (TREE_CODE (type) == RECORD_TYPE - || TREE_CODE (type) == UNION_TYPE - || TREE_CODE (type) == ARRAY_TYPE)) - { - /* This test is not exactly right, since we really want the minimum - number of regs in any class that can hold this mode. - But it does distinguish the machines we need to distinguish, - for now. */ - if (CLASS_MAX_NREGS (ALL_REGS, TYPE_MODE (type)) * BITS_PER_WORD - < bitsize) - TREE_ADDRESSABLE (decl) = 1; - } - - /* Evaluate nonconstant size only once, either now or as soon as safe. */ - if (DECL_SIZE (decl) != 0 && ! TREE_LITERAL (DECL_SIZE (decl))) - DECL_SIZE (decl) = variable_size (DECL_SIZE (decl)); -} - -/* Lay out a RECORD_TYPE type (a C struct). - This means laying out the fields, determining their offsets, - and computing the overall size and required alignment of the record. - Note that if you set the TYPE_ALIGN before calling this - then the struct is aligned to at least that boundary. - - If the type has basetypes, you must call layout_basetypes - before calling this function. */ - -static void -layout_record (rec) - tree rec; -{ - register tree field; -#ifdef STRUCTURE_SIZE_BOUNDARY - int record_align = MAX (STRUCTURE_SIZE_BOUNDARY, TYPE_ALIGN (rec)); -#else - int record_align = MAX (BITS_PER_UNIT, TYPE_ALIGN (rec)); -#endif - /* These must be laid out *after* the record is. */ - tree pending_statics = NULL_TREE; - /* Record size so far is CONST_SIZE + VAR_SIZE * SIZE_UNIT bits, - where CONST_SIZE is an integer - and VAR_SIZE is a tree expression. - If VAR_SIZE is null, the size is just CONST_SIZE. - Naturally we try to avoid using VAR_SIZE. */ - register int const_size = 0; - register tree var_size = 0; - register int size_unit = BITS_PER_UNIT; - -#if 0 - /* If there are basetypes, the caller should already have - laid them out. Leave space at the beginning for them. */ - if (TYPE_SIZE (rec) != 0) - { - if (TREE_CODE (TYPE_SIZE (rec)) == INTEGER_CST) - const_size = TREE_INT_CST_LOW (TYPE_SIZE (rec)); - else - var_size = TYPE_SIZE (rec); - size_unit = TYPE_SIZE_UNIT (rec); - } -#endif /* 0 */ - - for (field = TYPE_FIELDS (rec); field; field = TREE_CHAIN (field)) - { - register int desired_align; - - /* If FIELD is a VAR_DECL, then treat it like a separate variable, - not really like a structure field. - If it is a FUNCTION_DECL, it's a method. - In both cases, all we do is lay out the decl, - and we do it *after* the record is laid out. */ - - if (TREE_CODE (field) == VAR_DECL) - { - pending_statics = tree_cons (NULL, field, pending_statics); - continue; - } - /* Enumerators and enum types which are local to this class need not - be laid out. */ - if (TREE_CODE (field) == CONST_DECL || TREE_CODE (field) == TYPE_DECL) - continue; - - /* Lay out the field so we know what alignment it needs. - For KNOWN_ALIGN, pass the number of bits from start of record - or some divisor of it. */ - - layout_decl (field, var_size ? size_unit : const_size); - desired_align = DECL_ALIGN (field); - - /* Record must have at least as much alignment as any field. - Otherwise, the alignment of the field within the record - is meaningless. */ - - record_align = MAX (record_align, desired_align); -#ifdef PCC_BITFIELD_TYPE_MATTERS - /* In PCC on Vax, Sony, etc., a bit field of declare type `int' - forces the entire structure to have `int' alignment. */ - if (PCC_BITFIELD_TYPE_MATTERS && DECL_NAME (field) != 0) - record_align = MAX (record_align, TYPE_ALIGN (TREE_TYPE (field))); -#endif - - /* Does this field automatically have alignment it needs - by virtue of the fields that precede it and the record's - own alignment? */ - - if (const_size % desired_align != 0 - || (size_unit % desired_align != 0 - && var_size)) - { - /* No, we need to skip space before this field. - Bump the cumulative size to multiple of field alignment. */ - - if (var_size == 0 - || size_unit % desired_align == 0) - const_size - = CEIL (const_size, desired_align) * desired_align; - else - { - var_size - = genop (PLUS_EXPR, var_size, - build_int (CEIL (const_size, size_unit))); - const_size = 0; - var_size = convert_units (var_size, size_unit, desired_align); - size_unit = desired_align; - } - } - -#ifdef PCC_BITFIELD_TYPE_MATTERS - if (PCC_BITFIELD_TYPE_MATTERS && TREE_CODE (field) == FIELD_DECL - && TREE_TYPE (field) != error_mark_node) - { - int type_align = TYPE_ALIGN (TREE_TYPE (field)); - int type_size = (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (field))) - * TYPE_SIZE_UNIT (TREE_TYPE (field))); - register tree dsize = DECL_SIZE (field); - int field_size = TREE_INT_CST_LOW (dsize) * DECL_SIZE_UNIT (field); - - /* A bit field may not span the unit of alignment of its type. - Advance to next boundary if necessary. - If the type's alignment is less than its size, - then the bitfield may span more than one alignment unit, - but only up to the number that the type itself occupies. - Thus, a bitfield declared int, if int is 4 bytes but aligned - to the halfword, must fit within a 4 byte group that is - halfword aligned. */ - if (const_size / type_align + (type_size / type_align - 1) - < (const_size + field_size - 1) / type_align) - const_size = CEIL (const_size, type_align) * type_align; - } -#endif - - /* Size so far becomes the offset of this field. */ - - DECL_OFFSET (field) = const_size; - DECL_VOFFSET (field) = var_size; - DECL_VOFFSET_UNIT (field) = size_unit; - - /* If this field is an anonymous union, - give each union-member the same offset as the union has. */ - - if (DECL_NAME (field) == 0 - && TREE_CODE (TREE_TYPE (field)) == UNION_TYPE) - { - tree uelt = TYPE_FIELDS (TREE_TYPE (field)); - for (; uelt; uelt = TREE_CHAIN (uelt)) - { - DECL_FIELD_CONTEXT (uelt) = DECL_FIELD_CONTEXT (field); - DECL_OFFSET (uelt) = DECL_OFFSET (field); - DECL_VOFFSET (uelt) = DECL_VOFFSET (field); - DECL_VOFFSET_UNIT (uelt) = DECL_VOFFSET_UNIT (field); - } - } - - /* Now add size of this field to the size of the record. */ - - { - register tree dsize = DECL_SIZE (field); - - if (TREE_LITERAL (dsize)) - const_size += TREE_INT_CST_LOW (dsize) * DECL_SIZE_UNIT (field); - else if (var_size == 0) - { - var_size = dsize; - size_unit = DECL_SIZE_UNIT (field); - } - else - { - register int tunits = MIN (size_unit, DECL_SIZE_UNIT (field)); - var_size - = genop (PLUS_EXPR, - convert_units (var_size, size_unit, tunits), - convert_units (dsize, DECL_SIZE_UNIT (field), tunits)); - size_unit = tunits; - } - } - } - - /* Work out the total size and alignment of the record - as one expression and store in the record type. - Round it up to a multiple of the record's alignment. */ - - if (var_size == 0) - TYPE_SIZE (rec) - = build_int (CEIL (CEIL (const_size, record_align) * record_align, - size_unit)); - else - { - if (const_size) - var_size - = genop (PLUS_EXPR, var_size, - build_int (CEIL (const_size, size_unit))); - TYPE_SIZE (rec) - = convert_units (var_size, - size_unit, - record_align); - size_unit = record_align; - } - - TYPE_SIZE (rec) = convert_units (TYPE_SIZE (rec), size_unit, - BITS_PER_UNIT); - TYPE_SIZE_UNIT (rec) = BITS_PER_UNIT; - TYPE_ALIGN (rec) = MIN (BIGGEST_ALIGNMENT, record_align); - - /* Lay out any static members. This is done now - because their type may use the record's type. */ - - for (field = pending_statics; field; field = TREE_CHAIN (field)) - layout_decl (TREE_VALUE (field), 0); -} - -/* Lay out a UNION_TYPE type. - Lay out all the fields, set their offsets to zero, - and compute the size and alignment of the union (maximum of any field). - Note that if you set the TYPE_ALIGN before calling this - then the union align is aligned to at least that boundary. */ - -static void -layout_union (rec) - tree rec; -{ - register tree field; -#ifdef STRUCTURE_SIZE_BOUNDARY - int union_align = STRUCTURE_SIZE_BOUNDARY; -#else - int union_align = BITS_PER_UNIT; -#endif - - /* The size of the union, based on the fields scanned so far, - is max (CONST_SIZE, VAR_SIZE). - VAR_SIZE may be null; then CONST_SIZE by itself is the size. */ - register int const_size = 0; - register tree var_size = 0; - - for (field = TYPE_FIELDS (rec); field; field = TREE_CHAIN (field)) - { -#if 0 /* Should be in a language-specific file, since message is lang spec. */ - if (TREE_STATIC (field)) - { - error_with_decl (field, "field `%s' declared static in union"); - TREE_STATIC (field) = 0; - } -#endif - - /* Ignore enumerators and enum types local to the union. */ - if (TREE_CODE (field) == CONST_DECL || TREE_CODE (field) == TYPE_DECL) - continue; - - layout_decl (field, 0); - DECL_OFFSET (field) = 0; - DECL_VOFFSET (field) = 0; - DECL_VOFFSET_UNIT (field) = BITS_PER_UNIT; - - /* Union must be at least as aligned as any field requires. */ - - union_align = MAX (union_align, DECL_ALIGN (field)); - -#ifdef PCC_BITFIELD_TYPE_MATTERS - /* On the m88000, a bit field of declare type `int' - forces the entire union to have `int' alignment. */ - if (PCC_BITFIELD_TYPE_MATTERS) - union_align = MAX (union_align, TYPE_ALIGN (TREE_TYPE (field))); -#endif - - /* Set union_size to max (decl_size, union_size). - There are more and less general ways to do this. - Use only CONST_SIZE unless forced to use VAR_SIZE. */ - - if (TREE_LITERAL (DECL_SIZE (field))) - const_size = MAX (const_size, - TREE_INT_CST_LOW (DECL_SIZE (field)) - * DECL_SIZE_UNIT (field)); - else if (var_size == 0) - var_size = convert_units (DECL_SIZE (field), - DECL_SIZE_UNIT (field), - BITS_PER_UNIT); - else - var_size = genop (MAX_EXPR, - convert_units (DECL_SIZE (field), - DECL_SIZE_UNIT (field), - BITS_PER_UNIT), - var_size); - } - - /* Determine the ultimate size of the union (in bytes). */ - if (NULL == var_size) - TYPE_SIZE (rec) = build_int (CEIL (const_size, BITS_PER_UNIT)); - else if (const_size == 0) - TYPE_SIZE (rec) = var_size; - else - TYPE_SIZE (rec) = genop (MAX_EXPR, var_size, - build_int (CEIL (const_size, BITS_PER_UNIT))); - - /* Determine the desired alignment. */ - union_align = MIN (BIGGEST_ALIGNMENT, union_align); - TYPE_ALIGN (rec) = MAX (TYPE_ALIGN (rec), union_align); - - /* Round the size up to be a multiple of the required alignment */ - TYPE_SIZE (rec) - = convert_units (TYPE_SIZE (rec), BITS_PER_UNIT, TYPE_ALIGN (rec)); - TYPE_SIZE_UNIT (rec) = TYPE_ALIGN (rec); -} - -/* Calculate the mode, size, and alignment for TYPE. - For an array type, calculate the element separation as well. - Record TYPE on the chain of permanent or temporary types - so that dbxout will find out about it. - - TYPE_SIZE of a type is nonzero if the type has been laid out already. - layout_type does nothing on such a type. - - If the type is incomplete, its TYPE_SIZE remains zero. */ - -void -layout_type (type) - tree type; -{ - int old; - int temporary = 0; - - if (type == 0) - abort (); - - /* Do nothing if type has been laid out before. */ - if (TYPE_SIZE (type)) - return; - - /* Make sure all nodes we allocate are not momentary; - they must last past the current statement. */ - old = suspend_momentary (); - if (TREE_PERMANENT (type) && allocation_temporary_p ()) - { - temporary = 1; - end_temporary_allocation (); - } - - chain_type (type); - - switch (TREE_CODE (type)) - { - case LANG_TYPE: - /* This kind of type is the responsibility - of the languge-specific code. */ - abort (); - - case VOID_TYPE: - TYPE_SIZE (type) = size_zero_node; - TYPE_SIZE_UNIT (type) = BITS_PER_UNIT; - TYPE_ALIGN (type) = 1; - TYPE_MODE (type) = VOIDmode; - break; - - case INTEGER_TYPE: - case ENUMERAL_TYPE: - if (TREE_INT_CST_HIGH (TYPE_MIN_VALUE (type)) >= 0) - TREE_UNSIGNED (type) = 1; - - /* What follows is like agg_mode except that it ignores - MAX_FIXED_MODE_SIZE. That applies only to structures. */ - { - enum machine_mode mode, t; - - /* Get the last mode which has this size. */ - mode = BLKmode; - for (t = QImode; GET_MODE_CLASS (t) == MODE_INT; - t = (enum machine_mode) ((int) t + 1)) - if (GET_MODE_BITSIZE (t) == TYPE_PRECISION (type)) - mode = t; - - TYPE_MODE (type) = mode; - } - TYPE_SIZE (type) = build_int (GET_MODE_SIZE (TYPE_MODE (type))); - TYPE_SIZE_UNIT (type) = BITS_PER_UNIT; - TYPE_ALIGN (type) = GET_MODE_ALIGNMENT (TYPE_MODE (type)); - break; - - case REAL_TYPE: - { - register int prec = TYPE_PRECISION (type); - if (prec <= GET_MODE_BITSIZE (SFmode)) - TYPE_MODE (type) = SFmode; - else if (prec <= GET_MODE_BITSIZE (DFmode)) - TYPE_MODE (type) = DFmode; - else if (prec <= GET_MODE_BITSIZE (TFmode)) - TYPE_MODE (type) = TFmode; - else - abort (); - } - TYPE_SIZE (type) = build_int (GET_MODE_SIZE (TYPE_MODE (type))); - TYPE_SIZE_UNIT (type) = BITS_PER_UNIT; - TYPE_ALIGN (type) = GET_MODE_ALIGNMENT (TYPE_MODE (type)); - break; - - case POINTER_TYPE: - case REFERENCE_TYPE: - TYPE_MODE (type) = Pmode; - TYPE_SIZE (type) = build_int (POINTER_SIZE / BITS_PER_UNIT); - TYPE_SIZE_UNIT (type) = BITS_PER_UNIT; - TYPE_ALIGN (type) = POINTER_BOUNDARY; - TREE_UNSIGNED (type) = 1; - TYPE_PRECISION (type) = POINTER_SIZE; - break; - - case ARRAY_TYPE: - { - register tree index = TYPE_DOMAIN (type); - register tree length; - register tree element = TREE_TYPE (type); - -/* layout_type (element); */ - build_pointer_type (element); - - if (index == 0) - length = 0; - else - length = genop (PLUS_EXPR, size_one_node, - genop (MINUS_EXPR, TYPE_MAX_VALUE (index), - TYPE_MIN_VALUE (index))); - - if (TREE_PACKED (type)) - abort (); /* ??? Not written yet since not needed for C. */ - - TYPE_SIZE_UNIT (type) = TYPE_SIZE_UNIT (element); - if (length && TYPE_SIZE (element)) - TYPE_SIZE (type) = genop (MULT_EXPR, TYPE_SIZE (element), length); - TYPE_SEP (type) = TYPE_SIZE (element); - TYPE_SEP_UNIT (type) = TYPE_SIZE_UNIT (element); - TYPE_ALIGN (type) = MAX (TYPE_ALIGN (element), BITS_PER_UNIT); - TYPE_MODE (type) = BLKmode; - if (TYPE_SIZE (type) != 0 - && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST - /* BLKmode elements force BLKmode aggregate; - else extract/store fields may lose. */ - && TYPE_MODE (TREE_TYPE (type)) != BLKmode -#ifdef STRICT_ALIGNMENT - && (TYPE_ALIGN (type) >= BIGGEST_ALIGNMENT - || TYPE_ALIGN (type) >= (TREE_INT_CST_LOW (TYPE_SIZE (type)) - * TYPE_SIZE_UNIT (type))) -#endif - ) - { - TYPE_MODE (type) - = agg_mode (TREE_INT_CST_LOW (TYPE_SIZE (type)) - * TYPE_SIZE_UNIT (type)); - } - break; - } - - case RECORD_TYPE: - layout_record (type); - TYPE_MODE (type) = BLKmode; - if (TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST - /* If structure's known alignment is less than - what the scalar mode would need, and it matters, - then stick with BLKmode. */ -#ifdef STRICT_ALIGNMENT - && (TYPE_ALIGN (type) >= BIGGEST_ALIGNMENT - || TYPE_ALIGN (type) >= (TREE_INT_CST_LOW (TYPE_SIZE (type)) - * TYPE_SIZE_UNIT (type))) -#endif - ) - { - tree field; - /* A record which has any BLKmode members must itself be BLKmode; - it can't go in a register. */ - for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) - { - if (TYPE_MODE (TREE_TYPE (field)) == BLKmode) - goto record_lose; - - /* Must be BLKmode if any field crosses a word boundary, - since extract_bit_field can't handle that in registers. */ - if (DECL_OFFSET (field) / BITS_PER_WORD - != ((TREE_INT_CST_LOW (DECL_SIZE (field)) * DECL_SIZE_UNIT (field) - + DECL_OFFSET (field) - 1) - / BITS_PER_WORD)) - goto record_lose; - } - - TYPE_MODE (type) - = agg_mode (TREE_INT_CST_LOW (TYPE_SIZE (type)) - * TYPE_SIZE_UNIT (type)); - record_lose: ; - } - break; - - case UNION_TYPE: - layout_union (type); - TYPE_MODE (type) = BLKmode; - if (TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST - /* If structure's known alignment is less than - what the scalar mode would need, and it matters, - then stick with BLKmode. */ -#ifdef STRICT_ALIGNMENT - && (TYPE_ALIGN (type) >= BIGGEST_ALIGNMENT - || TYPE_ALIGN (type) >= (TREE_INT_CST_LOW (TYPE_SIZE (type)) - * TYPE_SIZE_UNIT (type))) -#endif - ) - { - tree field; - /* A union which has any BLKmode members must itself be BLKmode; - it can't go in a register. */ - for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) - if (TYPE_MODE (TREE_TYPE (field)) == BLKmode) - goto union_lose; - - TYPE_MODE (type) - = agg_mode (TREE_INT_CST_LOW (TYPE_SIZE (type)) - * TYPE_SIZE_UNIT (type)); - union_lose: ; - } - break; - - case FUNCTION_TYPE: - case METHOD_TYPE: - TYPE_MODE (type) = EPmode; - TYPE_SIZE (type) = build_int (2 * POINTER_SIZE / BITS_PER_UNIT); - TYPE_SIZE_UNIT (type) = BITS_PER_UNIT; - TYPE_ALIGN (type) = POINTER_BOUNDARY; - break; - - default: - abort (); - } /* end switch */ - - /* Evaluate nonconstant size only once, either now or as soon as safe. */ - if (TYPE_SIZE (type) != 0 && ! TREE_LITERAL (TYPE_SIZE (type))) - TYPE_SIZE (type) = variable_size (TYPE_SIZE (type)); - - /* Also layout any other variants of the type. */ - if (TYPE_NEXT_VARIANT (type) - || type != TYPE_MAIN_VARIANT (type)) - { - tree variant; - /* Record layout info of this variant. */ - tree size = TYPE_SIZE (type); - int size_unit = TYPE_SIZE_UNIT (type); - int align = TYPE_ALIGN (type); - enum machine_mode mode = TYPE_MODE (type); - - /* Copy it into all variants. */ - for (variant = TYPE_MAIN_VARIANT (type); - variant; - variant = TYPE_NEXT_VARIANT (variant)) - { - TYPE_SIZE (variant) = size; - TYPE_SIZE_UNIT (variant) = size_unit; - TYPE_ALIGN (variant) = align; - TYPE_MODE (variant) = mode; - } - } - - if (temporary) - resume_temporary_allocation (); - resume_momentary (old); -} - -/* Create and return a type for signed integers of PRECISION bits. */ - -tree -make_signed_type (precision) - int precision; -{ - register tree type = make_node (INTEGER_TYPE); - - TYPE_PRECISION (type) = precision; - - /* Create the extreme values based on the number of bits. */ - - TYPE_MIN_VALUE (type) - = build_int_2 ((precision-HOST_BITS_PER_INT > 0 ? 0 : (-1)<<(precision-1)), - (-1)<<(precision-HOST_BITS_PER_INT-1 > 0 - ? precision-HOST_BITS_PER_INT-1 - : 0)); - TYPE_MAX_VALUE (type) - = build_int_2 ((precision-HOST_BITS_PER_INT > 0 ? -1 : (1<<(precision-1))-1), - (precision-HOST_BITS_PER_INT-1 > 0 - ? (1<<(precision-HOST_BITS_PER_INT-1))-1 - : 0)); - - /* Give this type's extreme values this type as their type. */ - - TREE_TYPE (TYPE_MIN_VALUE (type)) = type; - TREE_TYPE (TYPE_MAX_VALUE (type)) = type; - - /* The first type made with this or `make_unsigned_type' - is the type for size values. */ - - if (sizetype == 0) - sizetype = type; - - /* Lay out the type: set its alignment, size, etc. */ - - layout_type (type); - - return type; -} - -/* Create and return a type for unsigned integers of PRECISION bits. */ - -tree -make_unsigned_type (precision) - int precision; -{ - register tree type = make_node (INTEGER_TYPE); - - TYPE_PRECISION (type) = precision; - - /* The first type made with this or `make_unsigned_type' - is the type for size values. */ - - if (sizetype == 0) - sizetype = type; - - fixup_unsigned_type (type); - return type; -} - -/* Set the extreme values of TYPE based on its precision in bits, - the lay it out. This is used both in `make_unsigned_type' - and for enumeral types. */ - -void -fixup_unsigned_type (type) - tree type; -{ - register int precision = TYPE_PRECISION (type); - - TYPE_MIN_VALUE (type) = build_int_2 (0, 0); - TYPE_MAX_VALUE (type) - = build_int_2 (precision-HOST_BITS_PER_INT >= 0 ? -1 : (1< 0 - ? ((unsigned) ~0 - >> (HOST_BITS_PER_INT - (precision - HOST_BITS_PER_INT))) - : 0); - TREE_TYPE (TYPE_MIN_VALUE (type)) = type; - TREE_TYPE (TYPE_MAX_VALUE (type)) = type; - - /* Lay out the type: set its alignment, size, etc. */ - - layout_type (type); -} diff --git a/gnu/usr.bin/gcc1/cc1/stupid.c b/gnu/usr.bin/gcc1/cc1/stupid.c deleted file mode 100644 index dfce5e00d1..0000000000 --- a/gnu/usr.bin/gcc1/cc1/stupid.c +++ /dev/null @@ -1,528 +0,0 @@ -/* Dummy data flow analysis for GNU compiler in nonoptimizing mode. - Copyright (C) 1987 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This file performs stupid register allocation, which is used - when cc1 gets the -noreg switch (which is when cc does not get -O). - - Stupid register allocation goes in place of the the flow_analysis, - local_alloc and global_alloc passes. combine_instructions cannot - be done with stupid allocation because the data flow info that it needs - is not computed here. - - In stupid allocation, the only user-defined variables that can - go in registers are those declared "register". They are assumed - to have a life span equal to their scope. Other user variables - are given stack slots in the rtl-generation pass and are not - represented as pseudo regs. A compiler-generated temporary - is assumed to live from its first mention to its last mention. - - Since each pseudo-reg's life span is just an interval, it can be - represented as a pair of numbers, each of which identifies an insn by - its position in the function (number of insns before it). The first - thing done for stupid allocation is to compute such a number for each - insn. It is called the suid. Then the life-interval of each - pseudo reg is computed. Then the pseudo regs are ordered by priority - and assigned hard regs in priority order. */ - -#include -#include "config.h" -#include "rtl.h" -#include "hard-reg-set.h" -#include "regs.h" - -/* Vector mapping INSN_UIDs to suids. - The suids are like uids but increase monononically always. - We use them to see whether a subroutine call came - between a variable's birth and its death. */ - -static int *uid_suid; - -/* Get the suid of an insn. */ - -#define INSN_SUID(INSN) (uid_suid[INSN_UID (INSN)]) - -/* Record the suid of the last CALL_INSN - so we can tell whether a pseudo reg crosses any calls. */ - -static int last_call_suid; - -/* Record the suid of the last JUMP_INSN - so we can tell whether a pseudo reg crosses any jumps. */ - -static int last_jump_suid; - -/* Record the suid of the last CODE_LABEL - so we can tell whether a pseudo reg crosses any labels. */ - -static int last_label_suid; - -/* Element N is suid of insn where life span of pseudo reg N ends. - Element is 0 if register N has not been seen yet on backward scan. */ - -static int *reg_where_dead; - -/* Element N is suid of insn where life span of pseudo reg N begins. */ - -static int *reg_where_born; - -/* Element N is 1 if pseudo reg N lives across labels or jumps. */ - -static char *reg_crosses_blocks; - -/* Numbers of pseudo-regs to be allocated, highest priority first. */ - -static int *reg_order; - -/* Indexed by reg number (hard or pseudo), nonzero if register is live - at the current point in the instruction stream. */ - -static char *regs_live; - -/* Indexed by insn's suid, the set of hard regs live after that insn. */ - -static HARD_REG_SET *after_insn_hard_regs; - -/* Record that hard reg REGNO is live after insn INSN. */ - -#define MARK_LIVE_AFTER(INSN,REGNO) \ - SET_HARD_REG_BIT (after_insn_hard_regs[INSN_SUID (INSN)], (REGNO)) - -static void stupid_mark_refs (); -static int stupid_reg_compare (); -static int stupid_find_reg (); - -/* Stupid life analysis is for the case where only variables declared - `register' go in registers. For this case, we mark all - pseudo-registers that belong to register variables as - dying in the last instruction of the function, and all other - pseudo registers as dying in the last place they are referenced. - Hard registers are marked as dying in the last reference before - the end or before each store into them. */ - -void -stupid_life_analysis (f, nregs, file) - rtx f; - int nregs; - FILE *file; -{ - register int i; - register rtx last, insn; - int max_uid; - - bzero (regs_ever_live, sizeof regs_ever_live); - - regs_live = (char *) alloca (nregs); - - /* First find the last real insn, and count the number of insns, - and assign insns their suids. */ - - for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) - if (INSN_UID (insn) > i) - i = INSN_UID (insn); - - max_uid = i + 1; - uid_suid = (int *) alloca ((i + 1) * sizeof (int)); - - /* Compute the mapping from uids to suids. - Suids are numbers assigned to insns, like uids, - except that suids increase monotonically through the code. */ - - last = 0; /* In case of empty function body */ - for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN - || GET_CODE (insn) == JUMP_INSN) - last = insn; - INSN_SUID (insn) = ++i; - } - - last_call_suid = i + 1; - last_jump_suid = i + 1; - last_label_suid = i + 1; - - max_regno = nregs; - - /* Allocate tables to record info about regs. */ - - reg_where_dead = (int *) alloca (nregs * sizeof (int)); - bzero (reg_where_dead, nregs * sizeof (int)); - - reg_where_born = (int *) alloca (nregs * sizeof (int)); - bzero (reg_where_born, nregs * sizeof (int)); - - reg_crosses_blocks = (char *) alloca (nregs); - bzero (reg_crosses_blocks, nregs); - - reg_order = (int *) alloca (nregs * sizeof (int)); - bzero (reg_order, nregs * sizeof (int)); - - reg_renumber = (short *) oballoc (nregs * sizeof (short)); - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - reg_renumber[i] = i; - - after_insn_hard_regs = (HARD_REG_SET *) alloca (max_uid * sizeof (HARD_REG_SET)); - bzero (after_insn_hard_regs, max_uid * sizeof (HARD_REG_SET)); - - /* Allocate and zero out many data structures - that will record the data from lifetime analysis. */ - - allocate_for_life_analysis (); - - for (i = 0; i < max_regno; i++) - { - reg_n_deaths[i] = 1; - } - - bzero (regs_live, nregs); - - /* Find where each pseudo register is born and dies, - by scanning all insns from the end to the start - and noting all mentions of the registers. - - Also find where each hard register is live - and record that info in after_insn_hard_regs. - regs_live[I] is 1 if hard reg I is live - at the current point in the scan. */ - - for (insn = last; insn; insn = PREV_INSN (insn)) - { - register HARD_REG_SET *p = after_insn_hard_regs + INSN_SUID (insn); - - /* Copy the info in regs_live - into the element of after_insn_hard_regs - for the current position in the rtl code. */ - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (regs_live[i]) - SET_HARD_REG_BIT (*p, i); - - /* Mark all call-clobbered regs as live after each call insn - so that a pseudo whose life span includes this insn - will not go in one of them. - Then mark those regs as all dead for the continuing scan - of the insns before the call. */ - - if (GET_CODE (insn) == CALL_INSN) - { - last_call_suid = INSN_SUID (insn); - IOR_HARD_REG_SET (after_insn_hard_regs[last_call_suid], - call_used_reg_set); - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (call_used_regs[i]) - regs_live[i] = 0; - } - - if (GET_CODE (insn) == JUMP_INSN) - last_jump_suid = INSN_SUID (insn); - - if (GET_CODE (insn) == CODE_LABEL) - last_label_suid = INSN_SUID (insn); - - /* Update which hard regs are currently live - and also the birth and death suids of pseudo regs - based on the pattern of this insn. */ - - if (GET_CODE (insn) == INSN - || GET_CODE (insn) == CALL_INSN - || GET_CODE (insn) == JUMP_INSN) - { - stupid_mark_refs (PATTERN (insn), insn); - } - } - - /* Now decide the order in which to allocate the pseudo registers. */ - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - reg_order[i] = i; - - qsort (®_order[FIRST_PSEUDO_REGISTER], - max_regno - FIRST_PSEUDO_REGISTER, sizeof (int), - stupid_reg_compare); - - /* Now, in that order, try to find hard registers for those pseudo regs. */ - - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - { - register int r = reg_order[i]; - enum reg_class class; - - /* Some regnos disappear from the rtl. Ignore them to avoid crash. */ - if (regno_reg_rtx[r] == 0) - continue; - - /* Now find the best hard-register class for this pseudo register */ - if (N_REG_CLASSES > 1) - { - class = reg_preferred_class (r); - - reg_renumber[r] = stupid_find_reg (reg_n_calls_crossed[r], class, - PSEUDO_REGNO_MODE (r), - reg_where_born[r], - reg_where_dead[r], - reg_crosses_blocks[r]); - } - else - reg_renumber[r] = -1; - - /* If no reg available in that class, - try any reg. */ - if (reg_renumber[r] == -1) - reg_renumber[r] = stupid_find_reg (reg_n_calls_crossed[r], - GENERAL_REGS, - PSEUDO_REGNO_MODE (r), - reg_where_born[r], - reg_where_dead[r], - reg_crosses_blocks[r]); - } - - if (file) - dump_flow_info (file); -} - -/* Comparison function for qsort. - Returns -1 (1) if register *R1P is higher priority than *R2P. */ - -static int -stupid_reg_compare (r1p, r2p) - int *r1p, *r2p; -{ - register int r1 = *r1p, r2 = *r2p; - register int len1 = reg_where_dead[r1] - reg_where_born[r1]; - register int len2 = reg_where_dead[r2] - reg_where_born[r2]; - int tem; - - tem = len2 - len1; - if (tem != 0) return tem; - - tem = reg_n_refs[r1] - reg_n_refs[r2]; - if (tem != 0) return tem; - - /* If regs are equally good, sort by regno, - so that the results of qsort leave nothing to chance. */ - return r1 - r2; -} - -/* Find a block of SIZE words of hard registers in reg_class CLASS - that can hold a value of machine-mode MODE - (but actually we test only the first of the block for holding MODE) - currently free from after insn whose suid is BIRTH - through the insn whose suid is DEATH, - and return the number of the first of them. - Return -1 if such a block cannot be found. - - If CALL_PRESERVED is nonzero, insist on registers preserved - over subroutine calls, and return -1 if cannot find such. - If CROSSES_BLOCKS is nonzero, reject registers for which - PRESERVE_DEATH_INFO_REGNO_P is true. */ - -static int -stupid_find_reg (call_preserved, class, mode, - born_insn, dead_insn, crosses_blocks) - int call_preserved; - enum reg_class class; - enum machine_mode mode; - int born_insn, dead_insn; - int crosses_blocks; -{ - register int i, ins; -#ifdef HARD_REG_SET - register /* Declare them register if they are scalars. */ -#endif - HARD_REG_SET used, this_reg; - - COPY_HARD_REG_SET (used, - call_preserved ? call_used_reg_set : fixed_reg_set); - - for (ins = born_insn; ins < dead_insn; ins++) - IOR_HARD_REG_SET (used, after_insn_hard_regs[ins]); - - IOR_COMPL_HARD_REG_SET (used, reg_class_contents[(int) class]); - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - { -#ifdef REG_ALLOC_ORDER - int regno = reg_alloc_order[i]; -#else - int regno = i; -#endif - - /* If we need reasonable death info on this hard reg, - don't use it for anything whose life spans a label or a jump. */ -#ifdef PRESERVE_DEATH_INFO_REGNO_P - if (PRESERVE_DEATH_INFO_REGNO_P (regno) - && crosses_blocks) - continue; -#endif - /* If a register has screwy overlap problems, - don't use it at all if not optimizing. - Actually this is only for the 387 stack register, - and it's because subsequent code won't work. */ -#ifdef OVERLAPPING_REGNO_P - if (OVERLAPPING_REGNO_P (regno)) - continue; -#endif - - if (! TEST_HARD_REG_BIT (used, regno) - && HARD_REGNO_MODE_OK (regno, mode)) - { - register int j; - register int size1 = HARD_REGNO_NREGS (regno, mode); - for (j = 1; j < size1 && ! TEST_HARD_REG_BIT (used, regno + j); j++); - if (j == size1) - { - CLEAR_HARD_REG_SET (this_reg); - while (--j >= 0) - SET_HARD_REG_BIT (this_reg, regno + j); - for (ins = born_insn; ins < dead_insn; ins++) - { - IOR_HARD_REG_SET (after_insn_hard_regs[ins], this_reg); - } - return regno; - } -#ifndef REG_ALLOC_ORDER - i += j; /* Skip starting points we know will lose */ -#endif - } - } - return -1; -} - -/* Walk X, noting all assignments and references to registers - and recording what they imply about life spans. - INSN is the current insn, supplied so we can find its suid. */ - -static void -stupid_mark_refs (x, insn) - rtx x, insn; -{ - register RTX_CODE code = GET_CODE (x); - register char *fmt; - register int regno, i; - - if (code == SET || code == CLOBBER) - { - if (SET_DEST (x) != 0 && GET_CODE (SET_DEST (x)) == REG) - { - /* Register is being assigned. */ - regno = REGNO (SET_DEST (x)); - - /* For hard regs, update the where-live info. */ - if (regno < FIRST_PSEUDO_REGISTER) - { - register int j - = HARD_REGNO_NREGS (regno, GET_MODE (SET_DEST (x))); - while (--j >= 0) - { - regs_ever_live[regno+j] = 1; - regs_live[regno+j] = 0; - /* The following line is for unused outputs; - they do get stored even though never used again. */ - MARK_LIVE_AFTER (insn, regno); - /* When a hard reg is clobbered, mark it in use - just before this insn, so it is live all through. */ - if (code == CLOBBER) - SET_HARD_REG_BIT (after_insn_hard_regs[INSN_SUID (insn)], - regno); - } - } - /* For pseudo regs, record where born, where dead, number of - times used, and whether live across a call. */ - else - { - /* Update the life-interval bounds of this pseudo reg. */ - - /* When a pseudo-reg is CLOBBERed, it is born just before - the clobbering insn. When setting, just after. */ - int where_born = INSN_SUID (insn) - (code == CLOBBER); - - reg_where_born[regno] = where_born; - /* The reg must live at least one insn even - in it is never again used--because it has to go - in SOME hard reg. */ - if (reg_where_dead[regno] < where_born + 1) - reg_where_dead[regno] = where_born + 1; - - /* Count the refs of this reg. */ - reg_n_refs[regno]++; - - if (last_call_suid < reg_where_dead[regno]) - reg_n_calls_crossed[regno] += 1; - if (last_jump_suid < reg_where_dead[regno] - || last_label_suid < reg_where_dead[regno]) - reg_crosses_blocks[regno] = 1; - } - } - /* Record references from the value being set, - or from addresses in the place being set if that's not a reg. - If setting a SUBREG, we treat the entire reg as *used*. */ - if (code == SET) - { - stupid_mark_refs (SET_SRC (x), insn); - if (GET_CODE (SET_DEST (x)) != REG) - stupid_mark_refs (SET_DEST (x), insn); - } - return; - } - - /* Register value being used, not set. */ - - if (code == REG) - { - regno = REGNO (x); - if (regno < FIRST_PSEUDO_REGISTER) - { - /* Hard reg: mark it live for continuing scan of previous insns. */ - register int j = HARD_REGNO_NREGS (regno, GET_MODE (x)); - while (--j >= 0) - { - regs_ever_live[regno+j] = 1; - regs_live[regno+j] = 1; - } - } - else - { - /* Pseudo reg: record first use, last use and number of uses. */ - - reg_where_born[regno] = INSN_SUID (insn); - reg_n_refs[regno]++; - if (regs_live[regno] == 0) - { - regs_live[regno] = 1; - reg_where_dead[regno] = INSN_SUID (insn); - } - } - return; - } - - /* Recursive scan of all other rtx's. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - stupid_mark_refs (XEXP (x, i), insn); - if (fmt[i] == 'E') - { - register int j; - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - stupid_mark_refs (XVECEXP (x, i, j), insn); - } - } -} diff --git a/gnu/usr.bin/gcc1/cc1/symout.c b/gnu/usr.bin/gcc1/cc1/symout.c deleted file mode 100644 index 7f42a30c7b..0000000000 --- a/gnu/usr.bin/gcc1/cc1/symout.c +++ /dev/null @@ -1,1267 +0,0 @@ -/* Output GDB-format symbol table information from GNU compiler. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#include "config.h" -#include "tree.h" -#include "symseg.h" -#include "rtl.h" -#include "gdbfiles.h" -#include -#undef NULL -/* <...> used here so one can prevent use of ./stddef.h - by changing the -I options used. */ -#include - -/* Get N_SO from stab.h if we can expect the file to exist. */ -#ifdef DBX_DEBUGGING_INFO -#ifdef USG -#include "stab.h" /* If doing DBX on sysV, use our own stab.h. */ -#else -#include /* On BSD, use the system's stab.h. */ -#endif /* not USG */ -#endif - -/* .stabs code for source file name. */ -#ifndef N_SO -#define N_SO 0x64 -#endif - -/* Unix maximum on file name length. Needed for getwd. */ -#define MAXNAMLEN 1024 - -/* Get the number to output for a reference to type TYPE. */ -#define TYPE_OUTPUT_ADDRESS(TYPE) \ - TYPE_SYMTAB_ADDRESS (TYPE_MAIN_VARIANT (TYPE)) - -/* Stream for writing symbol table file. */ -static FILE *symfile; - -/* Name of symbol table file. */ -static char *symfile_name; - -/* Stream for writing to assembler file. */ -static FILE *asmfile; - -/* Address for allocating space in symbol table file. - Changes in this variable are paired globally with writes to symfile, - but often we allocate many structures, advancing next_address, - before writing any of them. */ -static int next_address; - -/* Chain recording all the types that have been output, - giving the address-in-the-symseg of each one. */ - -struct typevec_elt -{ - int address; - struct typevec_elt *next; -}; - -static struct typevec_elt *typevec; - -/* Number of types recorded so far in the chain. */ - -static int total_types; - -/* Lists of types to which forward references have been made. - Separate lists for temporary and permanent types. */ - -static tree temporary_fwd_refs; -static tree permanent_fwd_refs; - -/* `blockvec' is a chain recording all the symbol-blocks that have been output, - giving the address-in-the-symseg of each one. */ - -struct blockvec_elt -{ - int address; - struct blockvec_elt *next; -}; - -static struct blockvec_elt *blockvec; - -/* Number of blocks recorded so far in the chain. */ - -static int total_blocks; - -static void symout_range_bounds (); -static void symout_array_domain (); -static void symout_record_fields (); -static void symout_enum_values (); -static void symout_record_field_names (); -static void symout_enum_value_names (); -static int subrange_p (); -static void symout_strings_skip (); -static void symout_strings_print (); - -/* At the beginning of compilation, start writing the symbol table. - Initialize the type and block chain. - Also open and initialize the symseg file. */ - -void -symout_init (filename, asm_file, sourcename) - char *filename; - FILE *asm_file; - char *sourcename; -{ - struct symbol_root buffer; - -#ifdef VMS - fatal ("Cannot write GDB debugging format on VMS"); -#endif - - asmfile = asm_file; - fprintf (asmfile, ".text 0\n.gdbbeg 0\n.gdbbeg 1\n"); - fprintf (asmfile, - "Ltext:\t.stabs \"%s\",%d,0,0,Ltext\n", - sourcename, N_SO); - fprintf (asmfile, ".data 0\nLdata:\n"); - ASM_OUTPUT_LOCAL (asmfile, "Lbss", 0, 0); - fprintf (asmfile, ".gdbsym Ldata,%d\n", - (char *) &buffer.databeg - (char *) &buffer); - fprintf (asmfile, ".gdbsym Lbss,%d\n", - (char *) &buffer.bssbeg - (char *) &buffer); - - symfile = fopen (filename, "w"); - if (symfile == 0) - pfatal_with_name (filename); - symfile_name = (char *) malloc (strlen (filename) + 1); - strcpy (symfile_name, filename); - - typevec = 0; - blockvec = 0; - total_types = 0; - total_blocks = 0; - - permanent_fwd_refs = 0; - temporary_fwd_refs = 0; - - bzero (&buffer, sizeof buffer); - fwrite (&buffer, sizeof buffer, 1, symfile); - - next_address = sizeof buffer; -} - -/* Functions for outputting strings into the symbol table. - The string to be output is effectively the concatenation of - the two strings P1 and P2. Their lengths are given as S1 and S2. - If P1 or P2 is zero, that string is not used. - - A null character is output to terminate the string, - and it is followed by more nulls as padding to a word boundary. */ - -static void -symout_strings (p1, s1, p2, s2) - char *p1; - int s1; - char *p2; - int s2; -{ - symout_strings_print (p1, s1, p2, s2); - symout_strings_skip (p1, s1, p2, s2); -} - -/* Like symout_strings but only output; do not update next_address. */ - -static void -symout_strings_print (p1, s1, p2, s2) - char *p1; - int s1; - char *p2; - int s2; -{ - register int total; - - if (p1 && s1 == 0) - s1 = strlen (p1); - if (p2 && s2 == 0) - s2 = strlen (p2); - - if (p1) - fwrite (p1, s1, 1, symfile); - if (p2) - fwrite (p2, s2, 1, symfile); - putc (0, symfile); - - total = s1 + s2 + 1; - while (total % sizeof (int)) - { - putc (0, symfile); - total++; - } -} - -/* Like symout_strings but just update next_address; do not output. */ - -static void -symout_strings_skip (p1, s1, p2, s2) - char *p1; - int s1; - char *p2; - int s2; -{ - register int total; - - if (p1 && s1 == 0) - s1 = strlen (p1); - if (p2 && s2 == 0) - s2 = strlen (p2); - - total = s1 + s2 + 1; - while (total % sizeof (int)) - total++; - - next_address += total; -} - -/* Call here to output a chain of types. - After each function, this is done first for the chain of permanent types - made during the function, and then for the chain of temporary types. - This must be done before outputting the symbols and blocks of the function. - - At the end of compilation, this is done for all the permanent types - made since the last function. - - Each permanent type is done once, at the beginning of the next function, - or at the end of the compilation if no functions follow. - Once a type has been processed here, its TYPE_SYMTAB_ADDRESS remains - set up. */ - -void -symout_types (types) - tree types; -{ - struct typerec - { - int number; - int address; - int nfields; - int fields_address; - int name_address; - char *name; - char *name_prefix; - }; - - register int n_types, i; - register struct typerec *records; - register tree next; - struct type buffer; - int this_run_address = next_address; - - /* Count the number of types to be handled here. */ - - for (next = types, n_types = 0; - next; - next = TREE_CHAIN (next), n_types++); - - records = (struct typerec *) alloca (n_types * sizeof (struct typerec)); - - /* Compute the amount of space each type needs, updating next_address - and storing the address of the data for each type. */ - - for (next = types, i = 0; - next; - next = TREE_CHAIN (next), i++) - { - register struct typevec_elt *velt - = (struct typevec_elt *) xmalloc (sizeof (struct typevec_elt)); - velt->next = typevec; - typevec = velt; - - total_types++; - - if (TYPE_NAME (next)) - { - records[i].name_address = next_address; - - if (TREE_CODE (TYPE_NAME (next)) == IDENTIFIER_NODE) - { - records[i].name = IDENTIFIER_POINTER (TYPE_NAME (next)); - switch (TREE_CODE (next)) - { - case RECORD_TYPE: - records[i].name_prefix = "struct "; - break; - - case UNION_TYPE: - records[i].name_prefix = "union "; - break; - - case ENUMERAL_TYPE: - records[i].name_prefix = "enum "; - break; - } - } - else - { - records[i].name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (next))); - records[i].name_prefix = 0; - } - symout_strings_skip (records[i].name_prefix, 0, - records[i].name, 0); - - } - else - { - records[i].name = 0; - records[i].name_address = 0; - records[i].name_prefix = 0; - } - - /* If this type was forward-referenced from a previous call - to symout_types, store this type's address into the reference. */ - if (TYPE_POINTER_TO (next) != 0 - && TYPE_SYMTAB_ADDRESS (TYPE_POINTER_TO (next)) != 0 - && TYPE_SYMTAB_ADDRESS (TYPE_POINTER_TO (next)) < this_run_address) - { - int pos = ftell (symfile); - int myaddr = next_address; - fflush (symfile); - fseek (symfile, - (TYPE_SYMTAB_ADDRESS (TYPE_POINTER_TO (next)) - + offsetof (struct type, target_type)), - 0); - fwrite (&myaddr, sizeof (int), 1, symfile); - fflush (symfile); - fseek (symfile, pos, 0); - } - - records[i].address = next_address; - TYPE_SYMTAB_ADDRESS (next) = next_address; - velt->address = next_address; - next_address += sizeof (struct type); - records[i].nfields = 0; - records[i].fields_address = 0; - switch (TREE_CODE (next)) - { - case ARRAY_TYPE: - records[i].nfields - = (TYPE_DOMAIN(next) - ? ! integer_zerop (TYPE_MIN_VALUE (TYPE_DOMAIN (next))) - : 0 ); - break; - - case INTEGER_TYPE: - if (subrange_p (next)) - buffer.nfields = 2; - break; - - case RECORD_TYPE: - case UNION_TYPE: - case ENUMERAL_TYPE: - records[i].nfields = list_length (TYPE_FIELDS (next)); - } - if (records[i].nfields) - records[i].fields_address = next_address; - next_address += records[i].nfields * sizeof (struct field); - } - - /* Now write the data whose space we have assigned. - First fill the data into BUFFER, then write BUFFER. */ - - for (next = types, i = 0; - next; - next = TREE_CHAIN (next), i++) - { - if (records[i].name) - symout_strings_print (records[i].name_prefix, 0, - records[i].name, 0); - - if (TREE_TYPE (next) != 0 && TYPE_OUTPUT_ADDRESS (TREE_TYPE (next)) == 0) - { - /* We are making a forward-reference to our target type. - Make a list of all of these. */ - if (TREE_PERMANENT (next)) - permanent_fwd_refs - = perm_tree_cons (TREE_TYPE (next), 0, permanent_fwd_refs); - else - temporary_fwd_refs - = tree_cons (TREE_TYPE (next), 0, temporary_fwd_refs); - } - - if (TYPE_SIZE (next) == 0) - buffer.length = 0; - else - buffer.length - = (TREE_INT_CST_LOW (TYPE_SIZE (next)) - * TYPE_SIZE_UNIT (next) / BITS_PER_UNIT); - - buffer.name = (char *) records[i].name_address; - buffer.target_type = (struct type *) (TREE_TYPE (next) ? TYPE_OUTPUT_ADDRESS (TREE_TYPE (next)) : 0); - - buffer.pointer_type = 0; - buffer.function_type = 0; - buffer.flags - = ((TREE_CODE (next) == INTEGER_TYPE || TREE_CODE (next) == ENUMERAL_TYPE) - && TREE_UNSIGNED (next)) - ? TYPE_FLAG_UNSIGNED : 0; - buffer.nfields = records[i].nfields; - buffer.fields = (struct field *) records[i].fields_address; - - switch (TREE_CODE (next)) - { - case INTEGER_TYPE: - buffer.code = TYPE_CODE_INT; - if (buffer.nfields) - buffer.code = TYPE_CODE_RANGE; - break; - - case REAL_TYPE: - buffer.code = TYPE_CODE_FLT; - break; - - case VOID_TYPE: - buffer.code = TYPE_CODE_VOID; - break; - - case POINTER_TYPE: - buffer.code = TYPE_CODE_PTR; - break; - - case ARRAY_TYPE: - if (buffer.nfields == 0) - buffer.code = TYPE_CODE_ARRAY; - else - buffer.code = TYPE_CODE_PASCAL_ARRAY; - break; - - case RECORD_TYPE: - buffer.code = TYPE_CODE_STRUCT; - break; - - case UNION_TYPE: - buffer.code = TYPE_CODE_UNION; - break; - - case FUNCTION_TYPE: - buffer.code = TYPE_CODE_FUNC; - break; - - case ENUMERAL_TYPE: - buffer.code = TYPE_CODE_ENUM; - break; - - default: - abort (); - } - - fwrite (&buffer, sizeof buffer, 1, symfile); - - /* Now write the `struct field's that certain kinds of type have. - This allocates space for the names of those fields, - incrementing next_address for the names. */ - - switch (TREE_CODE (next)) - { - case ARRAY_TYPE: - if (buffer.nfields) - symout_array_domain (next); - break; - - case RECORD_TYPE: - case UNION_TYPE: - symout_record_fields (next); - break; - - case ENUMERAL_TYPE: - symout_enum_values (next); - break; - - case INTEGER_TYPE: - if (buffer.nfields) - symout_range_bounds (next); - } - } - - /* Now output the strings referred to by the fields of certain types. - (next_address was already updated for these strings.) */ - - for (next = types, i = 0; - next; - next = TREE_CHAIN (next), i++) - { - switch (TREE_CODE (next)) - { - case RECORD_TYPE: - case UNION_TYPE: - symout_record_field_names (next); - break; - - case ENUMERAL_TYPE: - symout_enum_value_names (next); - break; - } - } -} - -/* Given a list of types TYPES, return a chain of just those - that haven't been written in the symbol table. */ - -static tree -filter_undefined_types (types) - tree types; -{ - tree new = 0; - tree next; - - for (next = types; next; next = TREE_CHAIN (next)) - if (TYPE_SYMTAB_ADDRESS (TREE_PURPOSE (next)) == 0) - { - TREE_CHAIN (TREE_PURPOSE (next)) = new; - new = TREE_PURPOSE (next); - } - - return new; -} - -/* Return nonzero if TYPE's range of possible values - is not the full range allowed by the number of bits it has. - TYPE is assumed to be an INTEGER_TYPE or ENUMERAL_TYPE. */ - -static int -subrange_p (type) - tree type; -{ - int uns = TREE_UNSIGNED (type); - - if (TYPE_PRECISION (type) >= HOST_BITS_PER_INT) - { - if (uns) - return integer_zerop (TYPE_MIN_VALUE (type)) - && TREE_INT_CST_LOW (TYPE_MAX_VALUE (type)) == 0 - && (TREE_INT_CST_HIGH (TYPE_MAX_VALUE (type)) - == (1 << (TYPE_PRECISION (type) - HOST_BITS_PER_INT)) - 1); - return TREE_INT_CST_LOW (TYPE_MIN_VALUE (type)) == 0 - && TREE_INT_CST_LOW (TYPE_MAX_VALUE (type)) == 0 - && (TREE_INT_CST_HIGH (TYPE_MIN_VALUE (type)) - == (-1) << (TYPE_PRECISION (type) - 1 - HOST_BITS_PER_INT)) - && (TREE_INT_CST_HIGH (TYPE_MAX_VALUE (type)) - == (1 << (TYPE_PRECISION (type) - 1 - HOST_BITS_PER_INT)) - 1); - } - - if (uns) - { - int mask; - - if (TYPE_PRECISION (type) == HOST_BITS_PER_INT) - /* Shifting by 32 loses on some machines. */ - mask = -1; - else - mask = (1 << TYPE_PRECISION (type)) - 1; - - return (integer_zerop (TYPE_MIN_VALUE (type)) - && (TREE_INT_CST_LOW (TYPE_MAX_VALUE (type)) == mask)); - } - else - return ((TREE_INT_CST_LOW (TYPE_MIN_VALUE (type)) - == (-1) << (TYPE_PRECISION (type) - 1)) - && (TREE_INT_CST_LOW (TYPE_MAX_VALUE (type)) - == (1 << (TYPE_PRECISION (type) - 1)) - 1)); -} - -/* Functions to output the "fields" of various kinds of types. - These assume that next_address has already been incremented to - cover these fields, and the fields of all the other types being - output in this batch; so next_address can be used to allocate - space to store field names, etc. */ - -static void -symout_array_domain (type) - tree type; -{ - struct field buffer; - - buffer.bitpos = 0; - buffer.bitsize = 0; - buffer.type = (struct type *) TYPE_OUTPUT_ADDRESS (TYPE_DOMAIN (type)); - buffer.name = 0; - fwrite (&buffer, sizeof (struct field), 1, symfile); -} - -static void -symout_range_bounds (type) - tree type; -{ - struct field buffer; - - buffer.bitpos = TREE_INT_CST_LOW (TYPE_MIN_VALUE (type)); - buffer.bitsize = 0; - buffer.type = (struct type *) TYPE_OUTPUT_ADDRESS (type); - buffer.name = 0; - fwrite (&buffer, sizeof (struct field), 1, symfile); - - buffer.bitpos = TREE_INT_CST_LOW (TYPE_MAX_VALUE (type)); - buffer.bitsize = 0; - buffer.type = (struct type *) TYPE_OUTPUT_ADDRESS (type); - buffer.name = 0; - fwrite (&buffer, sizeof (struct field), 1, symfile); -} - -static void -symout_record_fields (type) - tree type; -{ - struct field buffer; - register tree field; - - for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) - { - buffer.bitpos = DECL_OFFSET (field); - buffer.bitsize - = (TREE_PACKED (field) - ? TREE_INT_CST_LOW (DECL_SIZE (field)) * DECL_SIZE_UNIT (field) - : 0); - buffer.type = (struct type *) TYPE_OUTPUT_ADDRESS (TREE_TYPE (field)); - if (DECL_NAME (field)) - { - buffer.name = (char *) next_address; - symout_strings_skip (0, IDENTIFIER_LENGTH (DECL_NAME (field)), 0, 0); - } - else - buffer.name = 0; - fwrite (&buffer, sizeof (struct field), 1, symfile); - } -} - -static void -symout_enum_values (type) - tree type; -{ - struct field buffer; - register tree link, value; - - for (link = TYPE_VALUES (type); link; link = TREE_CHAIN (link)) - { - value = TREE_VALUE (link); - buffer.bitpos = TREE_INT_CST_LOW (value); - buffer.bitsize = 0; - buffer.type = (struct type *) TYPE_OUTPUT_ADDRESS (type); - buffer.name = (char *) next_address; - symout_strings_skip (0, IDENTIFIER_LENGTH (TREE_PURPOSE (link)), 0, 0); - fwrite (&buffer, sizeof buffer, 1, symfile); - } -} - -/* Output field names or value names for the fields of a type. - This is called, for the types that need it, after the fields - have been output for all the types in the batch. - We do not update next_address here, because it has already been - updated for all the names in all the fields in all the types. */ - -static void -symout_record_field_names (type) - tree type; -{ - register tree field; - - for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) - if (DECL_NAME (field)) - symout_strings_print (IDENTIFIER_POINTER (DECL_NAME (field)), - IDENTIFIER_LENGTH (DECL_NAME (field)), - 0, 0); -} - -static void -symout_enum_value_names (type) - tree type; -{ - register tree value; - - for (value = TYPE_VALUES (type); value; value = TREE_CHAIN (value)) - symout_strings_print (IDENTIFIER_POINTER (TREE_PURPOSE (value)), - IDENTIFIER_LENGTH (TREE_PURPOSE (value)), - 0, 0); -} - -/* Output the symbols of a block, given the list of decl nodes. - Store the file addresses at which the symbols are output - into ADDR_BUFFER, a vector which has just the right length. - - If FILTER is 1, do only the private symbols in DECLS. - If FILTER is 2, do only the public ones (but no externals). - If FILTER is 0, do all (except external functions). */ - -static void -symout_block_symbols (decls, addr_buffer, filter) - tree decls; - int *addr_buffer; - int filter; -{ - register tree decl; - struct symbol buffer; - register int i; - - for (decl = decls, i = 0; decl; decl = TREE_CHAIN (decl)) - { - register int name_address = next_address; - - if (filter == (TREE_PUBLIC (decl) ? 1 : 2)) - continue; - - /* Do not mention external functions. - Let their own files mention them. - In the top blocks, don't mention external anything. */ - - if (TREE_EXTERNAL (decl) - && (filter || TREE_CODE (TREE_TYPE (decl)) == FUNCTION_TYPE)) - continue; - - if (TREE_TYPE (decl) == error_mark_node) - continue; - - symout_strings (IDENTIFIER_POINTER (DECL_NAME (decl)), - IDENTIFIER_LENGTH (DECL_NAME (decl)), - 0, 0); - addr_buffer[i] = next_address; - buffer.name = (char *) name_address; - buffer.namespace = VAR_NAMESPACE; - buffer.type = (struct type *) TYPE_OUTPUT_ADDRESS (TREE_TYPE (decl)); - switch (TREE_CODE (decl)) - { - case PARM_DECL: - buffer.class = LOC_ARG; - buffer.value.value = DECL_OFFSET (decl) / BITS_PER_UNIT; - break; - - case VAR_DECL: - case RESULT_DECL: - if (TREE_STATIC (decl) || TREE_EXTERNAL (decl)) - { - if (! TREE_PUBLIC (decl) || DECL_INITIAL (decl)) - { - char *str = XSTR (XEXP (DECL_RTL (decl), 0), 0); - fprintf (asmfile, "\t.gdbsym "); - ASM_OUTPUT_LABELREF (asmfile, str); - fprintf (asmfile, ",%d\n", - next_address + (char *)&buffer.value - (char *)&buffer); - buffer.class = LOC_STATIC; - } - else - /* Uninitialized public symbols are output as .comm; - Tell GDB to get address from loader global symbol. - Also come here for symbols declared extern. */ - buffer.class = LOC_EXTERNAL; - } - else - { - if (GET_CODE (DECL_RTL (decl)) == REG) - { - buffer.class = LOC_REGISTER; - buffer.value.value = REGNO (DECL_RTL (decl)); - /* Detect vars that were optimized entirely away. */ - if (buffer.value.value == -1) - buffer.class = LOC_CONST; - } - else if (GET_CODE (DECL_RTL (decl)) == MEM - && (GET_CODE (XEXP (DECL_RTL (decl), 0)) == MEM - || (GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG - && REGNO (XEXP (DECL_RTL (decl), 0)) != FRAME_POINTER_REGNUM))) - /* If the value is indirect by memory or by a register - that isn't the frame pointer - then it means the object is variable-sized and address through - that register or stack slot. - If we have a pointer-type (which we should, for an array), - output the variable as a pointer. - Otherwise ignore it, since it is hard to create the ptr - type now and output it, and -gg is being retired. */ - { - tree ptype = TYPE_POINTER_TO (TREE_TYPE (TREE_TYPE (decl))); - if (ptype == 0 - || TYPE_OUTPUT_ADDRESS (ptype) == 0) - continue; - - buffer.type = (struct type *) TYPE_OUTPUT_ADDRESS (ptype); - - - if (GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG) - { - buffer.class = LOC_REGISTER; - buffer.value.value = REGNO (DECL_RTL (decl)); - /* Detect vars that were optimized entirely away. */ - if (buffer.value.value == -1) - buffer.class = LOC_CONST; - } - else - { - register rtx addr = XEXP (DECL_RTL (decl), 0); - if (GET_CODE (addr) != PLUS && GET_CODE (addr) != MINUS) - abort (); - if (GET_CODE (XEXP (addr, 1)) != CONST_INT) - abort (); - buffer.class = LOC_LOCAL; - buffer.value.value = INTVAL (XEXP (addr, 1)); - if (GET_CODE (addr) == MINUS) - buffer.value.value = - buffer.value.value; - } - } - /* Locals in memory are expected to be addressed as - (PLUS (REG ...) (CONST_INT ...)). - Bomb out if that is not so. */ - else if (GET_CODE (DECL_RTL (decl)) == MEM) - { - register rtx addr = XEXP (DECL_RTL (decl), 0); - if (GET_CODE (addr) != PLUS && GET_CODE (addr) != MINUS) - abort (); - if (GET_CODE (XEXP (addr, 1)) != CONST_INT) - abort (); - buffer.class = LOC_LOCAL; - buffer.value.value = INTVAL (XEXP (addr, 1)); - if (GET_CODE (addr) == MINUS) - buffer.value.value = - buffer.value.value; - } - else - abort (); - } - break; - - case TYPE_DECL: - buffer.class = LOC_TYPEDEF; - buffer.value.value = 0; - break; - - case CONST_DECL: - buffer.class = LOC_CONST; - buffer.value.value = TREE_INT_CST_LOW (DECL_INITIAL (decl)); - break; - - case FUNCTION_DECL: - if (DECL_INITIAL (decl)) - { - buffer.class = LOC_BLOCK; - buffer.value.value = DECL_BLOCK_SYMTAB_ADDRESS (decl); - } - else - buffer.class = LOC_EXTERNAL; - } - - fwrite (&buffer, sizeof buffer, 1, symfile); - next_address += sizeof buffer; - i++; - } -} - -/* Output the tags (struct, union and enum definitions) for a block, - given a list of them (a chain of TREE_LIST nodes) in TAGS. - Store their addresses in the file into ADDR_BUFFER. */ - -static void -symout_block_tags (tags, addr_buffer) - tree tags; - int *addr_buffer; -{ - register tree tag; - struct symbol buffer; - register int i; - - for (tag = tags, i = 0; tag; tag = TREE_CHAIN (tag), i++) - { - buffer.name = (char *) next_address; - - symout_strings (IDENTIFIER_POINTER (TREE_PURPOSE (tag)), - IDENTIFIER_LENGTH (TREE_PURPOSE (tag)), - 0, 0); - addr_buffer[i] = next_address; - buffer.namespace = STRUCT_NAMESPACE; - buffer.type = (struct type *) TYPE_OUTPUT_ADDRESS (TREE_VALUE (tag)); - buffer.class = LOC_TYPEDEF; - buffer.value.value = 0; - - fwrite (&buffer, sizeof buffer, 1, symfile); - next_address += sizeof buffer; - } -} - -/* Output all the data structure for a "block" - (any binding contour). - DECLS is the chain of declarations of variables in this block. - TAGS is the list of struct, union and enum tag definitions of this block. - SUPERBLOCK_ADDRESS is the symtab file address of the containing block's - data structure. */ - -int -symout_block (decls, tags, args, superblock_address) - tree decls; - tree tags; - tree args; - int superblock_address; -{ - register tree decl; - register int i; - register int *addr_buffer; - struct block buffer; - int n_decls, n_tags, n_args, total; - register struct blockvec_elt *velt; - int block_address; - - for (decl = decls, i = 0; decl; decl = TREE_CHAIN (decl)) - if (! TREE_EXTERNAL (decl) - || TREE_CODE (TREE_TYPE (decl)) != FUNCTION_TYPE) - i++; - - n_decls = i; - - for (decl = args, i = 0; decl; decl = TREE_CHAIN (decl), i++); - n_args = i; - - for (decl = tags, i = 0; decl; decl = TREE_CHAIN (decl), i++); - n_tags = i; - - total = n_decls + n_args + n_tags; - - addr_buffer = (int *) alloca (total * sizeof (int)); - - symout_block_symbols (args, addr_buffer, 0); - symout_block_symbols (decls, addr_buffer + n_args, 0); - symout_block_tags (tags, addr_buffer + n_decls + n_args); - - velt = (struct blockvec_elt *) xmalloc (sizeof (struct blockvec_elt)); - velt->next = blockvec; - velt->address = next_address; - blockvec = velt; - - buffer.startaddr = 0; - buffer.endaddr = 0; - buffer.superblock = (struct block *) superblock_address; - buffer.function = 0; - buffer.nsyms = total; - - block_address = next_address; - fwrite (&buffer, sizeof buffer - sizeof buffer.sym, 1, symfile); - next_address += sizeof buffer - sizeof buffer.sym; - - fwrite (addr_buffer, sizeof (int), total, symfile); - next_address += total * sizeof (int); - - fprintf (asmfile, "\t.gdbblock %d,%d\n", total_blocks + 2, block_address); - total_blocks++; - - return block_address; -} - -/* Walk STMT, the body of a function, and output symtab data on - all the blocks that compose it and all symbols inside them. - ARGS is a chain of decls for argument variables of the function. - SUPERBLOCK_ADDRESS is the address of symbol data for the - innermost block containing STMT; it is used for recursive calls, - and is always 0 for the outermost call (since the containing - block for a function is output later than the function). */ - -int -symout_function (stmt, args, superblock_address) - register tree stmt; - tree args; - int superblock_address; -{ - int address = superblock_address; - - while (stmt) - { - switch (TREE_CODE (stmt)) - { - case COMPOUND_STMT: - case LOOP_STMT: - symout_function (STMT_BODY (stmt), 0, address); - break; - - case IF_STMT: - symout_function (STMT_THEN (stmt), 0, address); - symout_function (STMT_ELSE (stmt), 0, address); - break; - - case LET_STMT: - /* Ignore LET_STMTs for blocks never really used to make RTL. */ - if (! TREE_USED (stmt)) - break; - address = - symout_block (STMT_VARS (stmt), STMT_TYPE_TAGS (stmt), args, - superblock_address); - - symout_function (STMT_SUBBLOCKS (stmt), 0, address); - } - stmt = TREE_CHAIN (stmt); - } - return address; -} - -symout_function_end () -{ - /* Output dummy entries for any undefined structure references. */ - symout_types (filter_undefined_types (temporary_fwd_refs)); - temporary_fwd_refs = 0; -} - -/* Output all the data structure for a top two blocks in a compilation. - The top block is for public (global) symbols; - the next one is for private (this file only) symbols. - - DECLS is the chain of declarations of variables in this block. - TAGS is the list of struct, union and enum tag definitions. */ - -void -symout_top_blocks (decls, tags) - tree decls; - tree tags; -{ - register tree decl; - register int i; - register int *addr_buffer; - struct block buffer; - int n_decls, n_tags; - register struct blockvec_elt *velt; - int top_block_addr; - - /* First do the public-symbols block. */ - - for (decl = decls, i = 0; decl; decl = TREE_CHAIN (decl)) - if (TREE_PUBLIC (decl) && ! TREE_EXTERNAL (decl)) - i++; - n_decls = i; - - addr_buffer = (int *) alloca (n_decls * sizeof (int)); - - symout_block_symbols (decls, addr_buffer, 2); - - fprintf (asmfile, ".text 0\n\t.gdbend 0\n"); - fprintf (asmfile, "\t.gdbblock 0,%d\n", next_address); - - total_blocks++; - velt = (struct blockvec_elt *) xmalloc (sizeof (struct blockvec_elt)); - velt->next = blockvec; - velt->address = next_address; - blockvec = velt; - - top_block_addr = next_address; - - buffer.startaddr = 0; - buffer.endaddr = 0; - buffer.superblock = 0; - buffer.function = 0; - buffer.nsyms = n_decls;; - - fwrite (&buffer, sizeof buffer - sizeof buffer.sym, 1, symfile); - next_address += sizeof buffer - sizeof buffer.sym; - - fwrite (addr_buffer, sizeof (int), n_decls, symfile); - next_address += n_decls * sizeof (int); - - /* Next do the private-symbols block. */ - - for (decl = decls, i = 0; decl; decl = TREE_CHAIN (decl)) - if (! TREE_PUBLIC (decl) && ! TREE_EXTERNAL (decl)) - i++; - n_decls = i; - - for (decl = tags, i = 0; decl; decl = TREE_CHAIN (decl), i++); - n_tags = i; - - addr_buffer = (int *) alloca ((n_decls + n_tags) * sizeof (int)); - - symout_block_symbols (decls, addr_buffer, 1); - symout_block_tags (tags, addr_buffer + n_decls); - - fprintf (asmfile, "\t.gdbend 1\n"); - fprintf (asmfile, "\t.gdbblock 1,%d\n", next_address); - - total_blocks++; - velt = (struct blockvec_elt *) xmalloc (sizeof (struct blockvec_elt)); - velt->next = blockvec; - velt->address = next_address; - blockvec = velt; - - buffer.startaddr = 0; - buffer.endaddr = 0; - buffer.superblock = (struct block *) top_block_addr; - buffer.function = 0; - buffer.nsyms = n_decls + n_tags;; - - fwrite (&buffer, sizeof buffer - sizeof buffer.sym, 1, symfile); - next_address += sizeof buffer - sizeof buffer.sym; - - fwrite (addr_buffer, sizeof (int), n_decls + n_tags, symfile); - next_address += (n_decls + n_tags) * sizeof (int); -} - -/* Output the source-line-number information. */ - -/* Output a `struct source' for the source file described by F. - Return the address-in-the-symseg of the `struct source'. */ - -static int -symout_source_file (f) - struct gdbfile *f; -{ - /* Make the `struct source' big enough for as many lines as - this file has. */ - int size = sizeof (struct source) + (f->nlines - 1) * sizeof (struct line); - struct source *buffer - = (struct source *) alloca (size); - int addr; - - /* Use zero for the line data, since assembler will store the real data. */ - bzero (buffer, size); - - /* Output the file's name as a string. The assembler doesn't know this. */ - buffer->name = (char *) next_address; - symout_strings (f->name, 0, 0, 0); - buffer->nlines = f->nlines; - - /* Write the structure. */ - addr = next_address; - fwrite (buffer, 1, size, symfile); - next_address += size; - - /* Tell assembler where to write the real line-number data. */ - fprintf (asmfile, "\t.gdblinetab %d,%d\n", - f->filenum, addr + sizeof (int)); - - return addr; -} - -/* Output the `struct sourcevector' which describes all the - source files and points a `struct source' for each one. */ - -static int -symout_sources () -{ - register struct gdbfile *f; - int nfiles = 0; - struct sourcevector *s; - int i; - int size; - int addr; - - /* Count number of files to determine size of the sourcevector. */ - for (f = gdbfiles; f; f = f->next) - ++nfiles; - - /* Allocate buffer for the sourcevector and record its length. */ - size = sizeof (int) + nfiles * sizeof (struct source *); - s = (struct sourcevector *) alloca (size); - s->length = nfiles; - - /* Output a `struct source' for each file; put address into sourcevector. */ - for (f = gdbfiles, i = 0; f; f = f->next, i++) - s->source[i] = (struct source *) symout_source_file (f); - - /* Output the sourcevector. */ - addr = next_address; - fwrite (s, 1, size, symfile); - next_address += size; - return addr; -} - -/* Call here at the end of compilation, after outputting all the - blocks and symbols, to output the blockvector and typevector - and close the symbol table file. FILETIME is source file's - creation time. */ - -void -symout_finish (filename, filetime) - char *filename; - int filetime; -{ - int *blockvector = (int *) alloca ((total_blocks + 1) * sizeof (int)); - int *typevector; - int now = time (0); - register int i; - struct symbol_root buffer; - char dir[MAXNAMLEN]; - - /* Output dummy entries for any undefined structure references. */ - symout_types (filter_undefined_types (permanent_fwd_refs)); - - typevector = (int *) alloca ((total_types + 1) * sizeof (int)); - - buffer.language = language_c; - buffer.blockvector = (struct blockvector *) next_address; - - /* The two blocks at the beginning of the chain - are the file's private symbols block and public symbols block. - They belong at the front of the blockvector, in that order. */ - blockvector[2] = blockvec->address; - blockvec = blockvec->next; - blockvector[1] = blockvec->address; - blockvec = blockvec->next; - - /* The rest of the blocks are in the chain in reverse order. */ - for (i = total_blocks; i > 2; i--) - { - blockvector[i] = blockvec->address; - blockvec = blockvec->next; - } - blockvector[0] = total_blocks; - - fwrite (blockvector, sizeof (int), total_blocks + 1, symfile); - next_address += sizeof (int) * (total_blocks + 1); - - buffer.typevector = (struct typevector *) next_address; - - for (i = total_types; i > 0; i--) - { - typevector[i] = typevec->address; - typevec = typevec->next; - } - typevector[0] = total_types; - - fwrite (typevector, sizeof (int), total_types + 1, symfile); - next_address += sizeof (int) * (total_types + 1); - - buffer.sourcevector = (struct sourcevector *) symout_sources (); - - buffer.format = 1; - buffer.textrel = 0; /* These four will be set up by linker. */ - buffer.datarel = 0; /* Make them 0 now, which is right for */ - buffer.bssrel = 0; /* looking at the .o file in gdb. */ - buffer.ldsymoff = 0; - - buffer.version = (char *) next_address; - symout_strings (ctime (&filetime), 0, 0, 0); - - buffer.compilation = (char *) next_address; - symout_strings (ctime (&now), 0, 0, 0); - - buffer.filename = (char *) next_address; - symout_strings (filename, 0, 0, 0); - - buffer.filedir = (char *) next_address; -#ifdef USG - strcpy (dir, getcwd (dir, MAXNAMLEN)); -#else -#ifndef VMS - getwd (dir); -#else - abort (); -#endif -#endif - symout_strings (dir, 0, 0, 0); - - fflush (symfile); - - if (ferror (symfile) != 0) - fatal_io_error (symfile_name); - - buffer.length = next_address; - - if (lseek (fileno (symfile), 0, 0) < 0) - pfatal_with_name (symfile_name); - if (write (fileno (symfile), &buffer, sizeof buffer) < 0) - pfatal_with_name (symfile_name); - close (fileno (symfile)); -} diff --git a/gnu/usr.bin/gcc1/cc1/symseg.h b/gnu/usr.bin/gcc1/cc1/symseg.h deleted file mode 100644 index 6d50eb4d49..0000000000 --- a/gnu/usr.bin/gcc1/cc1/symseg.h +++ /dev/null @@ -1,350 +0,0 @@ -/* GDB symbol table format definitions. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Format of GDB symbol table data. - There is one symbol segment for each source file or - independant compilation. These segments are simply concatenated - to form the GDB symbol table. A zero word where the beginning - of a segment is expected indicates there are no more segments. - -Format of a symbol segment: - - The symbol segment begins with a word containing 1 - if it is in the format described here. Other formats may - be designed, with other code numbers. - - The segment contains many objects which point at each other. - The pointers are offsets in bytes from the beginning of the segment. - Thus, each segment can be loaded into core and its pointers relocated - to make valid in-core pointers. - - All the data objects in the segment can be found indirectly from - one of them, the root object, of type `struct symbol_root'. - It appears at the beginning of the segment. - - The total size of the segment, in bytes, appears as the `length' - field of this object. This size includes the size of the - root object. - - All the object data types are defined here to contain pointer types - appropriate for in-core use on a relocated symbol segment. - Casts to and from type int are required for working with - unrelocated symbol segments such as are found in the file. - - The ldsymaddr word is filled in by the loader to contain - the offset (in bytes) within the ld symbol table - of the first nonglobal symbol from this compilation. - This makes it possible to match those symbols - (which contain line number information) reliably with - the segment they go with. - - Core addresses within the program that appear in the symbol segment - are not relocated by the loader. They are inserted by the assembler - and apply to addresses as output by the assembler, so GDB must - relocate them when it loads the symbol segment. It gets the information - on how to relocate from the textrel, datarel, bssrel, databeg and bssbeg - words of the root object. - - The words textrel, datarel and bssrel - are filled in by ld with the amounts to relocate within-the-file - text, data and bss addresses by; databeg and bssbeg can be - used to tell which kind of relocation an address needs. */ - -enum language {language_c}; - -struct symbol_root -{ - int format; /* Data format version */ - int length; /* # bytes in this symbol segment */ - int ldsymoff; /* Offset in ld symtab of this file's syms */ - int textrel; /* Relocation for text addresses */ - int datarel; /* Relocation for data addresses */ - int bssrel; /* Relocation for bss addresses */ - char *filename; /* Name of main source file compiled */ - char *filedir; /* Name of directory it was reached from */ - struct blockvector *blockvector; /* Vector of all symbol-naming blocks */ - struct typevector *typevector; /* Vector of all data types */ - enum language language; /* Code identifying the language used */ - char *version; /* Version info. Not fully specified */ - char *compilation; /* Compilation info. Not fully specified */ - int databeg; /* Address within the file of data start */ - int bssbeg; /* Address within the file of bss start */ - struct sourcevector *sourcevector; /* Vector of line-number info */ -}; - -/* All data types of symbols in the compiled program - are represented by `struct type' objects. - All of these objects are pointed to by the typevector. - The type vector may have empty slots that contain zero. */ - -struct typevector -{ - int length; /* Number of types described */ - struct type *type[1]; -}; - -/* Different kinds of data types are distinguished by the `code' field. */ - -enum type_code -{ - TYPE_CODE_UNDEF, /* Not used; catches errors */ - TYPE_CODE_PTR, /* Pointer type */ - TYPE_CODE_ARRAY, /* Array type, lower bound zero */ - TYPE_CODE_STRUCT, /* C struct or Pascal record */ - TYPE_CODE_UNION, /* C union or Pascal variant part */ - TYPE_CODE_ENUM, /* Enumeration type */ - TYPE_CODE_FUNC, /* Function type */ - TYPE_CODE_INT, /* Integer type */ - TYPE_CODE_FLT, /* Floating type */ - TYPE_CODE_VOID, /* Void type (values zero length) */ - TYPE_CODE_SET, /* Pascal sets */ - TYPE_CODE_RANGE, /* Range (integers within spec'd bounds) */ - TYPE_CODE_PASCAL_ARRAY, /* Array with explicit type of index */ -}; - -/* This appears in a type's flags word for an unsigned integer type. */ -#define TYPE_FLAG_UNSIGNED 1 - -/* Other flag bits are used with GDB. */ - -struct type -{ - /* Code for kind of type */ - enum type_code code; - /* Name of this type, or zero if none. - This is used for printing only. - Type names specified as input are defined by symbols. */ - char *name; - /* Length in bytes of storage for a value of this type */ - int length; - /* For a pointer type, describes the type of object pointed to. - For an array type, describes the type of the elements. - For a function type, describes the type of the value. - Unused otherwise. */ - struct type *target_type; - /* Type that is a pointer to this type. - Zero if no such pointer-to type is known yet. - The debugger may add the address of such a type - if it has to construct one later. */ - struct type *pointer_type; - /* Type that is a function returning this type. - Zero if no such function type is known here. - The debugger may add the address of such a type - if it has to construct one later. */ - struct type *function_type; - /* Flags about this type. */ - short flags; - /* Number of fields described for this type */ - short nfields; - /* For structure and union types, a description of each field. - For set and pascal array types, there is one "field", - whose type is the domain type of the set or array. - For range types, there are two "fields", - the minimum and maximum values (both inclusive). - For enum types, each possible value is described by one "field". - For range types, there are two "fields", that record constant values - (inclusive) for the minimum and maximum. - - Using a pointer to a separate array of fields - allows all types to have the same size, which is useful - because we can allocate the space for a type before - we know what to put in it. */ - struct field - { - /* Position of this field, counting in bits from start of - containing structure. For a function type, this is the - position in the argument list of this argument. - For a range bound or enum value, this is the value itself. */ - int bitpos; - /* Size of this field, in bits, or zero if not packed. - For an unpacked field, the field's type's length - says how many bytes the field occupies. */ - int bitsize; - /* In a struct or enum type, type of this field. - In a function type, type of this argument. - In an array type, the domain-type of the array. */ - struct type *type; - /* Name of field, value or argument. - Zero for range bounds and array domains. */ - char *name; - } *fields; -}; - -/* All of the name-scope contours of the program - are represented by `struct block' objects. - All of these objects are pointed to by the blockvector. - - Each block represents one name scope. - Each lexical context has its own block. - - The first two blocks in the blockvector are special. - The first one contains all the symbols defined in this compilation - whose scope is the entire program linked together. - The second one contains all the symbols whose scope is the - entire compilation excluding other separate compilations. - In C, these correspond to global symbols and static symbols. - - Each block records a range of core addresses for the code that - is in the scope of the block. The first two special blocks - give, for the range of code, the entire range of code produced - by the compilation that the symbol segment belongs to. - - The blocks appear in the blockvector - in order of increasing starting-address, - and, within that, in order of decreasing ending-address. - - This implies that within the body of one function - the blocks appear in the order of a depth-first tree walk. */ - -struct blockvector -{ - /* Number of blocks in the list. */ - int nblocks; - /* The blocks themselves. */ - struct block *block[1]; -}; - -struct block -{ - /* Addresses in the executable code that are in this block. - Note: in an unrelocated symbol segment in a file, - these are always zero. They can be filled in from the - N_LBRAC and N_RBRAC symbols in the loader symbol table. */ - int startaddr, endaddr; - /* The symbol that names this block, - if the block is the body of a function; - otherwise, zero. - Note: In an unrelocated symbol segment in an object file, - this field may be zero even when the block has a name. - That is because the block is output before the name - (since the name resides in a higher block). - Since the symbol does point to the block (as its value), - it is possible to find the block and set its name properly. */ - struct symbol *function; - /* The `struct block' for the containing block, or 0 if none. */ - /* Note that in an unrelocated symbol segment in an object file - this pointer may be zero when the correct value should be - the second special block (for symbols whose scope is one compilation). - This is because the compiler ouptuts the special blocks at the - very end, after the other blocks. */ - struct block *superblock; - /* Number of local symbols. */ - int nsyms; - /* The symbols. */ - struct symbol *sym[1]; -}; - -/* Represent one symbol name; a variable, constant, function or typedef. */ - -/* Different name spaces for symbols. Looking up a symbol specifies - a namespace and ignores symbol definitions in other name spaces. - - VAR_NAMESPACE is the usual namespace. - In C, this contains variables, function names, typedef names - and enum type values. - - STRUCT_NAMESPACE is used in C to hold struct, union and enum type names. - Thus, if `struct foo' is used in a C program, - it produces a symbol named `foo' in the STRUCT_NAMESPACE. - - LABEL_NAMESPACE may be used for names of labels (for gotos); - currently it is not used and labels are not recorded at all. */ - -/* For a non-global symbol allocated statically, - the correct core address cannot be determined by the compiler. - The compiler puts an index number into the symbol's value field. - This index number can be matched with the "desc" field of - an entry in the loader symbol table. */ - -enum namespace -{ - UNDEF_NAMESPACE, VAR_NAMESPACE, STRUCT_NAMESPACE, LABEL_NAMESPACE, -}; - -/* An address-class says where to find the value of the symbol in core. */ - -enum address_class -{ - LOC_UNDEF, /* Not used; catches errors */ - LOC_CONST, /* Value is constant int */ - LOC_STATIC, /* Value is at fixed address */ - LOC_REGISTER, /* Value is in register */ - LOC_ARG, /* Value is at spec'd position in arglist */ - LOC_LOCAL, /* Value is at spec'd pos in stack frame */ - LOC_TYPEDEF, /* Value not used; definition in SYMBOL_TYPE - Symbols in the namespace STRUCT_NAMESPACE - all have this class. */ - LOC_LABEL, /* Value is address in the code */ - LOC_BLOCK, /* Value is address of a `struct block'. - Function names have this class. */ - LOC_EXTERNAL, /* Value is at address not in this compilation. - This is used for .comm symbols - and for extern symbols within functions. - Inside GDB, this is changed to LOC_STATIC once the - real address is obtained from a loader symbol. */ - LOC_CONST_BYTES /* Value is a constant byte-sequence. */ -}; - -struct symbol -{ - /* Symbol name */ - char *name; - /* Name space code. */ - enum namespace namespace; - /* Address class */ - enum address_class class; - /* Data type of value */ - struct type *type; - /* constant value, or address if static, or register number, - or offset in arguments, or offset in stack frame. */ - union - { - long value; - struct block *block; /* for LOC_BLOCK */ - char *bytes; /* for LOC_CONST_BYTES */ - } - value; -}; - -/* Source-file information. - This describes the relation between source files and line numbers - and addresses in the program text. */ - -struct sourcevector -{ - int length; /* Number of source files described */ - struct source *source[1]; /* Descriptions of the files */ -}; - -/* Line number and address of one line. */ - -struct line -{ - int linenum; - int address; -}; - -/* All the information on one source file. */ - -struct source -{ - char *name; /* Name of file */ - int nlines; /* Number of lines that follow */ - struct line lines[1]; /* Information on each line */ -}; diff --git a/gnu/usr.bin/gcc1/cc1/toplev.c b/gnu/usr.bin/gcc1/cc1/toplev.c deleted file mode 100644 index c75dc214a9..0000000000 --- a/gnu/usr.bin/gcc1/cc1/toplev.c +++ /dev/null @@ -1,2123 +0,0 @@ -/*- - * This code is derived from software copyrighted by the Free Software - * Foundation. - * - * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. - */ - -#ifndef lint -static char sccsid[] = "@(#)toplev.c 6.4 (Berkeley) 5/8/91"; -#endif /* not lint */ - -/* Top level of GNU C compiler - Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This is the top level of cc1. - It parses command args, opens files, invokes the various passes - in the proper order, and counts the time used by each. - Error messages and low-level interface to malloc also handled here. */ - -#include "config.h" -#include -#include -#include -#include - -#include - -#ifdef USG -#undef FLOAT -#include -/* This is for hpux. It is a real screw. They should change hpux. */ -#undef FLOAT -#include -#include /* Correct for hpux at least. Is it good on other USG? */ -#undef FFS /* Some systems define this in param.h. */ -#else -#ifndef VMS -#include -#include -#endif -#endif - -#include "input.h" -#include "tree.h" -#include "c-tree.h" -#include "rtl.h" -#include "flags.h" - -extern int yydebug; - -extern FILE *finput; - -extern int reload_completed; -extern int rtx_equal_function_value_matters; - -extern void init_lex (); -extern void init_decl_processing (); -extern void init_tree (); -extern void init_rtl (); -extern void init_optabs (); -extern void init_reg_sets (); -extern void dump_flow_info (); -extern void dump_local_alloc (); - -void rest_of_decl_compilation (); -void error (); -void error_with_file_and_line (); -void fancy_abort (); -void set_target_switch (); -void print_target_switch_defaults (); - -/* Bit flags that specify the machine subtype we are compiling for. - Bits are tested using macros TARGET_... defined in the tm-...h file - and set by `-m...' switches. */ - -int target_flags; - -/* Name of current original source file (what was input to cpp). - This comes from each #-command in the actual input. */ - -char *input_filename; - -/* Name of top-level original source file (what was input to cpp). - This comes from the #-command at the beginning of the actual input. - If there isn't any there, then this is the cc1 input file name. */ - -char *main_input_filename; - -/* Current line number in real source file. */ - -int lineno; - -/* Stack of currently pending input files. */ - -struct file_stack *input_file_stack; - -/* Incremented on each change to input_file_stack. */ -int input_file_stack_tick; - -/* FUNCTION_DECL for function now being parsed or compiled. */ - -extern tree current_function_decl; - -/* Name to use as base of names for dump output files. */ - -char *dump_base_name; - -/* Flags saying which kinds of debugging dump have been requested. */ - -int rtl_dump = 0; -int rtl_dump_and_exit = 0; -int jump_opt_dump = 0; -int cse_dump = 0; -int loop_dump = 0; -int flow_dump = 0; -int combine_dump = 0; -int local_reg_dump = 0; -int global_reg_dump = 0; -int jump2_opt_dump = 0; -int dbr_sched_dump = 0; - -/* 1 => write gdb debugging output (using symout.c). -g - 2 => write dbx debugging output (using dbxout.c). -G - 3 => write sdb debugging output (using sdbout.c). -g. */ - -enum debugger write_symbols = NO_DEBUG; - -/* Nonzero means can use our own extensions to DBX format. - Relevant only with write_symbols == DBX_DEBUG. */ - -int use_gdb_dbx_extensions; - -/* Nonzero means do optimizations. -opt. */ - -int optimize = 0; - -/* Nonzero means `char' should be signed. */ - -int flag_signed_char; - -/* Nonzero means give an enum type only as many bytes as it needs. */ - -int flag_short_enums; - -/* Nonzero for -fcaller-saves: allocate values in regs that need to - be saved across function calls, if that produces overall better code. - Optional now, so people can test it. */ - -#ifdef DEFAULT_CALLER_SAVES -int flag_caller_saves = 1; -#else -int flag_caller_saves = 0; -#endif - -/* Nonzero for -fpcc-struct-return: return values the same way PCC does. */ - -int flag_pcc_struct_return = 0; - -/* Nonzero for -fforce-mem: load memory value into a register - before arithmetic on it. This makes better cse but slower compilation. */ - -int flag_force_mem = 0; - -/* Nonzero for -fforce-addr: load memory address into a register before - reference to memory. This makes better cse but slower compilation. */ - -int flag_force_addr = 0; - -/* Nonzero for -fdefer-pop: don't pop args after each function call; - instead save them up to pop many calls' args with one insns. */ - -int flag_defer_pop = 1; - -/* Nonzero for -ffloat-store: don't allocate floats and doubles - in extended-precision registers. */ - -int flag_float_store = 0; - -/* Nonzero for -fcombine-regs: - allow instruction combiner to combine an insn - that just copies one reg to another. */ - -int flag_combine_regs = 0; - -/* Nonzero enables strength-reduction in loop.c. */ - -int flag_strength_reduce = 0; - -/* Nonzero for -fwritable-strings: - store string constants in data segment and don't uniquize them. */ - -int flag_writable_strings = 0; - -/* Nonzero means don't put addresses of constant functions in registers. - Used for compiling the Unix kernel, where strange substitutions are - done on the assembly output. */ - -int flag_no_function_cse = 0; - -/* Nonzero for -fomit-frame-pointer: - don't make a frame pointer in simple functions that don't require one. */ - -int flag_omit_frame_pointer = 0; - -/* Nonzero to inhibit use of define_optimization peephole opts. */ - -int flag_no_peephole = 0; - -/* Nonzero means all references through pointers are volatile. */ - -int flag_volatile; - -/* Nonzero means just do syntax checking; don't output anything. */ - -int flag_syntax_only = 0; - -/* Nonzero means do stupid register allocation. -noreg. - This and `optimize' are controlled by different switches in cc1, - but normally cc controls them both with the -O switch. */ - -int obey_regdecls = 0; - -/* Don't print functions as they are compiled and don't print - times taken by the various passes. -quiet. */ - -int quiet_flag = 0; - -/* Don't print warning messages. -w. */ - -int inhibit_warnings = 0; - -/* Do print extra warnings (such as for uninitialized variables). -W. */ - -int extra_warnings = 0; - -/* Nonzero to warn about unused local variables. */ - -int warn_unused; - -/* Nonzero means warn about all declarations which shadow others. */ - -int warn_shadow; - -/* Warn if a switch on an enum fails to have a case for every enum value. */ - -int warn_switch; - -/* Nonzero means warn about any identifiers that match in the first N - characters. The value N is in `id_clash_len'. */ - -int warn_id_clash; -int id_clash_len; - -/* Number of error messages and warning messages so far. */ - -int errorcount = 0; -int warningcount = 0; -int sorrycount = 0; - -/* Name of program invoked, sans directories. */ - -char *progname; - -/* Nonzero if generating code to do profiling. */ - -int profile_flag = 0; - -/* Nonzero if generating code to do profiling on a line-by-line basis. */ - -int profile_block_flag; - -/* Nonzero for -pedantic switch: warn about anything - that standard spec forbids. */ - -int pedantic = 0; - -/* Nonzero for -finline-functions: ok to inline functions that look like - good inline candidates. */ - -int flag_inline_functions; - -/* Nonzero for -fkeep-inline-functions: even if we make a function - go inline everywhere, keep its defintion around for debugging - purposes. */ - -int flag_keep_inline_functions; - -/* Nonzero means make the text shared if supported. */ - -int flag_shared_data; - -/* Nonzero means schedule into delayed branch slots if supported. */ - -int flag_delayed_branch; - -/* Copy of arguments to main. */ -int save_argc; -char **save_argv; - -/* Name for output file of assembly code, specified with -o. */ - -char *asm_file_name; - -/* Name for output file of GDB symbol segment, specified with -symout. */ - -char *sym_file_name; - -/* Table of language-independent -f options. - STRING is the option name. VARIABLE is the address of the variable. - ON_VALUE is the value to store in VARIABLE - if `-fSTRING' is seen as an option. - (If `-fno-STRING' is seen as an option, the opposite value is stored.) */ - -struct { char *string; int *variable; int on_value;} f_options[] = -{ - {"float-store", &flag_float_store, 1}, - {"volatile", &flag_volatile, 1}, - {"defer-pop", &flag_defer_pop, 1}, - {"omit-frame-pointer", &flag_omit_frame_pointer, 1}, - {"strength-reduce", &flag_strength_reduce, 1}, - {"writable-strings", &flag_writable_strings, 1}, - {"peephole", &flag_no_peephole, 0}, - {"force-mem", &flag_force_mem, 1}, - {"force-addr", &flag_force_addr, 1}, - {"combine-regs", &flag_combine_regs, 1}, - {"function-cse", &flag_no_function_cse, 0}, - {"inline-functions", &flag_inline_functions, 1}, - {"keep-inline-functions", &flag_keep_inline_functions, 1}, - {"syntax-only", &flag_syntax_only, 1}, - {"shared-data", &flag_shared_data, 1}, - {"caller-saves", &flag_caller_saves, 1}, - {"pcc-struct-return", &flag_pcc_struct_return, 1}, - {"delayed-branch", &flag_delayed_branch, 1} -}; - -/* Output files for assembler code (real compiler output) - and debugging dumps. */ - -FILE *asm_out_file; -FILE *rtl_dump_file; -FILE *jump_opt_dump_file; -FILE *cse_dump_file; -FILE *loop_dump_file; -FILE *flow_dump_file; -FILE *combine_dump_file; -FILE *local_reg_dump_file; -FILE *global_reg_dump_file; -FILE *jump2_opt_dump_file; -FILE *dbr_sched_dump_file; - -/* Time accumulators, to count the total time spent in various passes. */ - -int parse_time; -int varconst_time; -int integration_time; -int jump_time; -int cse_time; -int loop_time; -int flow_time; -int combine_time; -int local_alloc_time; -int global_alloc_time; -int dbr_sched_time; -int final_time; -int symout_time; -int dump_time; - -/* Return time used so far, in microseconds. */ - -int -gettime () -{ -#ifdef USG - struct tms tms; -#else -#ifndef VMS - struct rusage rusage; -#else /* VMS */ - struct - { - int proc_user_time; - int proc_system_time; - int child_user_time; - int child_system_time; - } vms_times; -#endif -#endif - - if (quiet_flag) - return 0; - -#ifdef USG - times (&tms); - return (tms.tms_utime + tms.tms_stime) * (1000000 / HZ); -#else -#ifndef VMS - getrusage (0, &rusage); - return (rusage.ru_utime.tv_sec * 1000000 + rusage.ru_utime.tv_usec - + rusage.ru_stime.tv_sec * 1000000 + rusage.ru_stime.tv_usec); -#else /* VMS */ - times (&vms_times); - return (vms_times.proc_user_time + vms_times.proc_system_time) * 10000; -#endif -#endif -} - -#define TIMEVAR(VAR, BODY) \ -do { int otime = gettime (); BODY; VAR += gettime () - otime; } while (0) - -void -print_time (str, total) - char *str; - int total; -{ - fprintf (stderr, - "time in %s: %d.%06d\n", - str, total / 1000000, total % 1000000); -} - -/* Count an error or warning. Return 1 if the message should be printed. */ - -int -count_error (warningp) - int warningp; -{ - if (warningp && inhibit_warnings) - return 0; - - if (warningp) - warningcount++; - else - errorcount++; - - return 1; -} - -/* Print a fatal error message. NAME is the text. - Also include a system error message based on `errno'. */ - -void -pfatal_with_name (name) - char *name; -{ - fprintf (stderr, "%s: ", progname); - perror (name); - exit (35); -} - -void -fatal_io_error (name) - char *name; -{ - fprintf (stderr, "%s: %s: I/O error\n", progname, name); - exit (35); -} - -void -fatal (s, v) - char *s; - int v; -{ - error (s, v); - exit (34); -} - -/* Called from insn-extract to give a better error message when we - don't have an insn to match what we are looking for, rather - than just calling abort(). */ - -void -fatal_insn_not_found (insn) - rtx insn; -{ - error ("The following insn was not recognizable:", 0); - debug_rtx (insn); - abort (); -} - -static int need_error_newline; - -/* Function of last error message; - more generally, function such that if next error message is in it - then we don't have to mention the function name. */ -static tree last_error_function = NULL; - -/* Used to detect when input_file_stack has changed since last described. */ -static int last_error_tick; - -/* Called when the start of a function definition is parsed, - this function prints on stderr the name of the function. */ - -void -announce_function (decl) - tree decl; -{ - if (! quiet_flag) - { - fprintf (stderr, " %s", DECL_PRINT_NAME (decl)); - fflush (stderr); - need_error_newline = 1; - last_error_function = current_function_decl; - } -} - -/* Prints out, if necessary, the name of the current function - which caused an error. Called from all error and warning functions. */ - -void -report_error_function (file) - char *file; -{ - struct file_stack *p; - - if (need_error_newline) - { - fprintf (stderr, "\n"); - need_error_newline = 0; - } - - if (last_error_function != current_function_decl) - { - if (file) - fprintf (stderr, "%s: ", file); - - if (current_function_decl == NULL) - fprintf (stderr, "At top level:\n"); - else if (TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE) - fprintf (stderr, "In method %s:\n", - DECL_PRINT_NAME (current_function_decl)); - else - fprintf (stderr, "In function %s:\n", - DECL_PRINT_NAME (current_function_decl)); - - last_error_function = current_function_decl; - } - if (input_file_stack && input_file_stack->next != 0 - && input_file_stack_tick != last_error_tick) - { - fprintf (stderr, "In file included"); - for (p = input_file_stack->next; p; p = p->next) - { - fprintf (stderr, " from %s:%d", p->name, p->line); - if (p->next) - fprintf (stderr, ","); - } - fprintf (stderr, ":\n"); - last_error_tick = input_file_stack_tick; - } -} - -/* Report an error at the current line number. - S and V are a string and an arg for `printf'. */ - -void -error (s, v, v2) - char *s; - int v; /* @@also used as pointer */ - int v2; /* @@also used as pointer */ -{ - error_with_file_and_line (input_filename, lineno, s, v, v2); -} - -/* Report an error at line LINE of file FILE. - S and V are a string and an arg for `printf'. */ - -void -error_with_file_and_line (file, line, s, v, v2) - char *file; - int line; - char *s; - int v; - int v2; -{ - count_error (0); - - report_error_function (file); - - if (file) - fprintf (stderr, "%s:%d: ", file, line); - else - fprintf (stderr, "%s: ", progname); - fprintf (stderr, s, v, v2); - fprintf (stderr, "\n"); -} - -/* Report an error at the declaration DECL. - S and V are a string and an arg which uses %s to substitute the declaration name. */ - -void -error_with_decl (decl, s, v) - tree decl; - char *s; - int v; -{ - count_error (0); - - report_error_function (DECL_SOURCE_FILE (decl)); - - fprintf (stderr, "%s:%d: ", - DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl)); - - if (DECL_PRINT_NAME (decl)) - fprintf (stderr, s, DECL_PRINT_NAME (decl), v); - else if (DECL_NAME (decl)) - fprintf (stderr, s, IDENTIFIER_POINTER (DECL_NAME (decl)), v); - else - fprintf (stderr, s, "((anonymous))", v); - fprintf (stderr, "\n"); -} - -/* Report an error at argument #NUM. - S is a string that uses %s to substitute the error type. - E is the string for the error type. */ -void -error_with_arg (s, e, num) - char *s, *e; - int num; -{ - char *w; - static char argument[] = "argument #%d: "; - - if (num <= 0) - { - error (s, e); - return; - } - - w = alloca (strlen (s) + sizeof (argument)); - (void) strcat (strcpy (w, argument), s); - error (w, num, e); -} - -/* Report an error at the line number of the insn INSN. - S and V are a string and an arg for `printf'. - This is used only when INSN is an `asm' with operands, - and each ASM_OPERANDS records its own source file and line. */ - -void -error_for_asm (insn, s, v, v2) - rtx insn; - char *s; - int v; /* @@also used as pointer */ - int v2; /* @@also used as pointer */ -{ - rtx temp; - char *filename; - int line; - rtx body = PATTERN (insn); - rtx asmop; - - /* Find the (or one of the) ASM_OPERANDS in the insn. */ - if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS) - asmop = SET_SRC (body); - else if (GET_CODE (body) == ASM_OPERANDS) - asmop = body; - else if (GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == SET) - asmop = SET_SRC (XVECEXP (body, 0, 0)); - else if (GET_CODE (body) == PARALLEL - && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS) - asmop = XVECEXP (body, 0, 0); - - filename = ASM_OPERANDS_SOURCE_FILE (asmop); - line = ASM_OPERANDS_SOURCE_LINE (asmop); - - error_with_file_and_line (filename, line, s, v, v2); -} - -/* Report a warning at line LINE. - S and V are a string and an arg for `printf'. */ - -void -warning_with_file_and_line (file, line, s, v, v2) - char *file; - int line; - char *s; - int v; - int v2; -{ - if (count_error (1) == 0) - return; - - report_error_function (file); - - if (file) - fprintf (stderr, "%s:%d: ", file, line); - else - fprintf (stderr, "%s: ", progname); - - fprintf (stderr, "warning: "); - fprintf (stderr, s, v, v2); - fprintf (stderr, "\n"); -} - -/* Report a warning at the current line number. - S and V are a string and an arg for `printf'. */ - -void -warning (s, v, v2) - char *s; - int v; /* @@also used as pointer */ - int v2; -{ - warning_with_file_and_line (input_filename, lineno, s, v, v2); -} - -/* Report a warning at the declaration DECL. - S is string which uses %s to substitute the declaration name. - V is a second parameter that S can refer to. */ - -void -warning_with_decl (decl, s, v) - tree decl; - char *s; - int v; -{ - if (count_error (1) == 0) - return; - - report_error_function (DECL_SOURCE_FILE (decl)); - - fprintf (stderr, "%s:%d: ", - DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl)); - - fprintf (stderr, "warning: "); - if (DECL_PRINT_NAME (decl)) - fprintf (stderr, s, DECL_PRINT_NAME (decl), v); - else if (DECL_NAME (decl)) - fprintf (stderr, s, IDENTIFIER_POINTER (DECL_NAME (decl)), v); - else - fprintf (stderr, s, "((anonymous))", v); - fprintf (stderr, "\n"); -} - -/* Report a warning at argument #NUM. - S is a string that uses %s to substitute the error type. - E is the string for the error type. */ -void -warning_with_arg (s, e, num) - char *s, *e; - int num; -{ - char *w; - static char argument[] = "argument #%d: "; - - if (num <= 0) - { - warning (s, e); - return; - } - - w = alloca (strlen (s) + sizeof (argument)); - (void) strcat (strcpy (w, argument), s); - warning (w, num, e); -} - -/* Apologize for not implementing some feature. - S, V, and V2 are a string and args for `printf'. */ - -void -sorry (s, v, v2) - char *s; - int v, v2; -{ - sorrycount++; - if (input_filename) - fprintf (stderr, "%s:%d: ", input_filename, lineno); - else - fprintf (stderr, "%s: ", progname); - - fprintf (stderr, "sorry, not implemented: "); - fprintf (stderr, s, v, v2); - fprintf (stderr, "\n"); -} - -/* Apologize for not implementing some feature, then quit. - S, V, and V2 are a string and args for `printf'. */ - -void -really_sorry (s, v, v2) - char *s; - int v, v2; -{ - if (input_filename) - fprintf (stderr, "%s:%d: ", input_filename, lineno); - else - fprintf (stderr, "c++: "); - - fprintf (stderr, "sorry, not implemented: "); - fprintf (stderr, s, v, v2); - fatal (" (fatal)\n"); -} - -/* More 'friendly' abort that prints the line and file. - config.h can #define abort fancy_abort if you like that sort of thing. */ - -void -fancy_abort () -{ - fatal ("Internal gcc abort."); -} - -/* When `malloc.c' is compiled with `rcheck' defined, - it calls this function to report clobberage. */ - -void -botch (s) -{ - abort (); -} - -/* Same as `malloc' but report error if no memory available. */ - -int -xmalloc (size) - unsigned size; -{ - register int value = (int) malloc (size); - if (value == 0) - fatal ("Virtual memory exhausted."); - return value; -} - -/* Same as `realloc' but report error if no memory available. */ - -int -xrealloc (ptr, size) - char *ptr; - int size; -{ - int result = realloc (ptr, size); - if (!result) - fatal ("Virtual memory exhausted."); - return result; -} - -/* Return the logarithm of X, base 2, considering X unsigned, - if X is a power of 2. Otherwise, returns -1. */ - -int -exact_log2 (x) - register unsigned int x; -{ - register int log = 0; - for (log = 0; log < HOST_BITS_PER_INT; log++) - if (x == (1 << log)) - return log; - return -1; -} - -/* Given X, an unsigned number, return the largest int Y such that 2**Y <= X. - If X is 0, return -1. */ - -int -floor_log2 (x) - register unsigned int x; -{ - register int log = 0; - for (log = 0; log < HOST_BITS_PER_INT; log++) - if ((x & ((-1) << log)) == 0) - return log - 1; - return HOST_BITS_PER_INT - 1; -} - -int float_handled; -jmp_buf float_handler; - -/* Specify where to longjmp to when a floating arithmetic error happens. - If HANDLER is 0, it means don't handle the errors any more. */ - -void -set_float_handler (handler) - jmp_buf handler; -{ - float_handled = (handler != 0); - if (handler) - bcopy (handler, float_handler, sizeof (float_handler)); -} - -/* Signals actually come here. */ - -static void -float_signal () -{ - if (float_handled == 0) - abort (); - float_handled = 0; - longjmp (float_handler, 1); -} - -/* Handler for SIGPIPE. */ - -static void -pipe_closed () -{ - fatal ("output pipe has been closed"); -} - -/* Compile an entire file of output from cpp, named NAME. - Write a file of assembly output and various debugging dumps. */ - -static void -compile_file (name) - char *name; -{ - tree globals; - int start_time; - int dump_base_name_length; - - int name_specified = name != 0; - - if (dump_base_name == 0) - dump_base_name = name ? name : "gccdump"; - dump_base_name_length = strlen (dump_base_name); - - parse_time = 0; - varconst_time = 0; - integration_time = 0; - jump_time = 0; - cse_time = 0; - loop_time = 0; - flow_time = 0; - combine_time = 0; - local_alloc_time = 0; - global_alloc_time = 0; - dbr_sched_time = 0; - final_time = 0; - symout_time = 0; - dump_time = 0; - - /* Open input file. */ - - if (name == 0 || !strcmp (name, "-")) - { - finput = stdin; - name = "stdin"; - } - else - finput = fopen (name, "r"); - if (finput == 0) - pfatal_with_name (name); - - /* Initialize data in various passes. */ - - init_tree (); - init_lex (); - init_rtl (); - init_emit_once (); - init_decl_processing (); - init_optabs (); - - /* If rtl dump desired, open the output file. */ - if (rtl_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".rtl"); - rtl_dump_file = fopen (dumpname, "w"); - if (rtl_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If jump_opt dump desired, open the output file. */ - if (jump_opt_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".jump"); - jump_opt_dump_file = fopen (dumpname, "w"); - if (jump_opt_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If cse dump desired, open the output file. */ - if (cse_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".cse"); - cse_dump_file = fopen (dumpname, "w"); - if (cse_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If loop dump desired, open the output file. */ - if (loop_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".loop"); - loop_dump_file = fopen (dumpname, "w"); - if (loop_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If flow dump desired, open the output file. */ - if (flow_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".flow"); - flow_dump_file = fopen (dumpname, "w"); - if (flow_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If combine dump desired, open the output file. */ - if (combine_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 10); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".combine"); - combine_dump_file = fopen (dumpname, "w"); - if (combine_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If local_reg dump desired, open the output file. */ - if (local_reg_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".lreg"); - local_reg_dump_file = fopen (dumpname, "w"); - if (local_reg_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If global_reg dump desired, open the output file. */ - if (global_reg_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".greg"); - global_reg_dump_file = fopen (dumpname, "w"); - if (global_reg_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If jump2_opt dump desired, open the output file. */ - if (jump2_opt_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 7); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".jump2"); - jump2_opt_dump_file = fopen (dumpname, "w"); - if (jump2_opt_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* If dbr_sched dump desired, open the output file. */ - if (dbr_sched_dump) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 7); - strcpy (dumpname, dump_base_name); - strcat (dumpname, ".dbr"); - dbr_sched_dump_file = fopen (dumpname, "w"); - if (dbr_sched_dump_file == 0) - pfatal_with_name (dumpname); - } - - /* Open assembler code output file. */ - - if (! name_specified && asm_file_name == 0) - asm_out_file = stdout; - else - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - int len = strlen (dump_base_name); - strcpy (dumpname, dump_base_name); - if (len > 2 && ! strcmp (".c", dumpname + len - 2)) - dumpname[len - 2] = 0; - else if (len > 2 && ! strcmp (".i", dumpname + len - 2)) - dumpname[len - 2] = 0; - else if (len > 3 && ! strcmp (".co", dumpname + len - 3)) - dumpname[len - 3] = 0; - strcat (dumpname, ".s"); - if (asm_file_name == 0) - { - asm_file_name = (char *) malloc (strlen (dumpname) + 1); - strcpy (asm_file_name, dumpname); - } - if (!strcmp (asm_file_name, "-")) - asm_out_file = stdout; - else - asm_out_file = fopen (asm_file_name, "w"); - if (asm_out_file == 0) - pfatal_with_name (asm_file_name); - } - - input_filename = name; - - /* the beginning of the file is a new line; check for # */ - /* With luck, we discover the real source file's name from that - and put it in input_filename. */ - ungetc (check_newline (), finput); - - /* If the input doesn't start with a #line, use the input name - as the official input file name. */ - if (main_input_filename == 0) - main_input_filename = name; - - /* Put an entry on the input file stack for the main input file. */ - input_file_stack - = (struct file_stack *) xmalloc (sizeof (struct file_stack)); - input_file_stack->next = 0; - input_file_stack->name = input_filename; - - ASM_FILE_START (asm_out_file); - - /* Output something to inform GDB that this compilation was by GCC. */ -#ifndef ASM_IDENTIFY_GCC - fprintf (asm_out_file, "gcc_compiled.:\n"); -#else - ASM_IDENTIFY_GCC (asm_out_file); -#endif - - /* If GDB symbol table desired, open the GDB symbol output file. */ - if (write_symbols == GDB_DEBUG) - { - register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); - int len = strlen (dump_base_name); - strcpy (dumpname, dump_base_name); - if (len > 2 && ! strcmp (".c", dumpname + len - 2)) - dumpname[len - 2] = 0; - else if (len > 2 && ! strcmp (".i", dumpname + len - 2)) - dumpname[len - 2] = 0; - else if (len > 3 && ! strcmp (".co", dumpname + len - 3)) - dumpname[len - 3] = 0; - strcat (dumpname, ".sym"); - if (sym_file_name == 0) - sym_file_name = dumpname; - symout_init (sym_file_name, asm_out_file, main_input_filename); - } - - /* If dbx symbol table desired, initialize writing it - and output the predefined types. */ -#ifdef DBX_DEBUGGING_INFO - if (write_symbols == DBX_DEBUG) - dbxout_init (asm_out_file, main_input_filename); -#endif -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG) - sdbout_init (asm_out_file, main_input_filename); -#endif - - /* Initialize yet another pass. */ - - init_final (main_input_filename); - - start_time = gettime (); - - /* Call the parser, which parses the entire file - (calling rest_of_compilation for each function). */ - - yyparse (); - - /* Compilation is now finished except for writing - what's left of the symbol table output. */ - - parse_time += gettime () - start_time; - - parse_time -= integration_time; - parse_time -= varconst_time; - - globals = getdecls (); - - /* Really define vars that have had only a tentative definition. - Really output inline functions that must actually be callable - and have not been output so far. */ - - { - tree decl; - for (decl = globals; decl; decl = TREE_CHAIN (decl)) - { - if (TREE_CODE (decl) == VAR_DECL && TREE_STATIC (decl) - && ! TREE_ASM_WRITTEN (decl)) - { - /* Don't write out static consts, unless we needed - to take their address for some reason. */ - if (! TREE_READONLY (decl) - || TREE_PUBLIC (decl) - || TREE_ADDRESSABLE (decl)) - rest_of_decl_compilation (decl, 0, 1, 1); - /* Otherwise maybe mention them just for the debugger. */ -#ifdef DBX_DEBUGGING_INFO - else if (DECL_INITIAL (decl) && write_symbols == DBX_DEBUG) - TIMEVAR (varconst_time, dbxout_symbol (decl, 0)); -#endif -#ifdef SDB_DEBUGGING_INFO - else if (DECL_INITIAL (decl) && write_symbols == SDB_DEBUG) - TIMEVAR (varconst_time, sdbout_symbol (decl, 0)); -#endif - } - if (TREE_CODE (decl) == FUNCTION_DECL - && ! TREE_ASM_WRITTEN (decl) - && DECL_INITIAL (decl) != 0 - && TREE_ADDRESSABLE (decl) - && ! TREE_EXTERNAL (decl)) - output_inline_function (decl); - - /* Warn about any function declared static but not defined. */ - if (warn_unused - && TREE_CODE (decl) == FUNCTION_DECL - && DECL_INITIAL (decl) == 0 - && TREE_EXTERNAL (decl) - && ! TREE_PUBLIC (decl)) - warning_with_decl (decl, "`%s' declared but never defined"); - /* Warn about statics fns or vars defined but not used, - but not about inline functions - since unused inline statics is normal practice. */ - if (warn_unused - && (TREE_CODE (decl) == FUNCTION_DECL - || TREE_CODE (decl) == VAR_DECL) - && ! TREE_EXTERNAL (decl) - && ! TREE_PUBLIC (decl) - && ! TREE_USED (decl) - && ! TREE_INLINE (decl) - /* The TREE_USED bit for file-scope decls - is kept in the identifier, to handle multiple - external decls in different scopes. */ - && ! TREE_USED (DECL_NAME (decl))) - warning_with_decl (decl, "`%s' defined but not used"); - } - } - - /* Do dbx symbols */ -#ifdef DBX_DEBUGGING_INFO - if (write_symbols == DBX_DEBUG) - TIMEVAR (symout_time, - { - dbxout_tags (gettags ()); - dbxout_types (get_permanent_types ()); - }); -#endif - -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG) - TIMEVAR (symout_time, - { - sdbout_tags (gettags ()); - sdbout_types (get_permanent_types ()); - }); -#endif - - /* Do gdb symbols */ - if (write_symbols == GDB_DEBUG) - TIMEVAR (symout_time, - { - struct stat statbuf; - fstat (fileno (finput), &statbuf); - symout_types (get_permanent_types ()); - symout_top_blocks (globals, gettags ()); - symout_finish (name, statbuf.st_ctime); - }); - - /* Output some stuff at end of file if nec. */ - - end_final (main_input_filename); - -#ifdef ASM_FILE_END - ASM_FILE_END (asm_out_file); -#endif - - /* Close the dump files. */ - - if (rtl_dump) - fclose (rtl_dump_file); - - if (jump_opt_dump) - fclose (jump_opt_dump_file); - - if (cse_dump) - fclose (cse_dump_file); - - if (loop_dump) - fclose (loop_dump_file); - - if (flow_dump) - fclose (flow_dump_file); - - if (combine_dump) - { - dump_combine_total_stats (combine_dump_file); - fclose (combine_dump_file); - } - - if (local_reg_dump) - fclose (local_reg_dump_file); - - if (global_reg_dump) - fclose (global_reg_dump_file); - - if (jump2_opt_dump) - fclose (jump2_opt_dump_file); - - if (dbr_sched_dump) - fclose (dbr_sched_dump_file); - - /* Close non-debugging input and output files. Take special care to note - whether fclose returns an error, since the pages might still be on the - buffer chain while the file is open. */ - - fclose (finput); - if (ferror (asm_out_file) != 0 || fclose (asm_out_file) != 0) - fatal_io_error (asm_file_name); - - /* Print the times. */ - - if (! quiet_flag) - { - fprintf (stderr,"\n"); - print_time ("parse", parse_time); - print_time ("integration", integration_time); - print_time ("jump", jump_time); - print_time ("cse", cse_time); - print_time ("loop", loop_time); - print_time ("flow", flow_time); - print_time ("combine", combine_time); - print_time ("local-alloc", local_alloc_time); - print_time ("global-alloc", global_alloc_time); - print_time ("dbranch", dbr_sched_time); - print_time ("final", final_time); - print_time ("varconst", varconst_time); - print_time ("symout", symout_time); - print_time ("dump", dump_time); - } -} - -/* This is called from finish_decl (within yyparse) - for each declaration of a function or variable. - This does nothing for automatic variables. - Otherwise, it sets up the RTL and outputs any assembler code - (label definition, storage allocation and initialization). - - DECL is the declaration. If ASMSPEC is nonzero, it specifies - the assembler symbol name to be used. TOP_LEVEL is nonzero - if this declaration is not within a function. */ - -void -rest_of_decl_compilation (decl, asmspec, top_level, at_end) - tree decl; - char *asmspec; - int top_level; - int at_end; -{ - /* Declarations of variables, and of functions defined elsewhere. */ - - if (TREE_STATIC (decl) || TREE_EXTERNAL (decl)) - TIMEVAR (varconst_time, - { - make_decl_rtl (decl, asmspec, top_level); - /* Don't output anything - when a tentative file-scope definition is seen. - But at end of compilation, do output code for them. */ - if (! (! at_end && top_level - && (DECL_INITIAL (decl) == 0 - || DECL_INITIAL (decl) == error_mark_node))) - assemble_variable (decl, top_level, write_symbols, at_end); - }); - else if (TREE_REGDECL (decl) && asmspec != 0) - { - if (decode_reg_name (asmspec) >= 0) - { - DECL_RTL (decl) = 0; - make_decl_rtl (decl, asmspec, top_level); - } - else - error ("invalid register name `%s' for register variable", asmspec); - } -#ifdef DBX_DEBUGGING_INFO - else if (write_symbols == DBX_DEBUG && TREE_CODE (decl) == TYPE_DECL) - TIMEVAR (varconst_time, dbxout_symbol (decl, 0)); -#endif -#ifdef SDB_DEBUGGING_INFO - else if (write_symbols == SDB_DEBUG && top_level - && TREE_CODE (decl) == TYPE_DECL) - TIMEVAR (varconst_time, sdbout_symbol (decl, 0)); -#endif - - if (top_level) - { - if (write_symbols == GDB_DEBUG) - { - TIMEVAR (symout_time, - { - /* The initizations make types when they contain - string constants. The types are on the temporary - obstack, so output them now before they go away. */ - symout_types (get_temporary_types ()); - }); - } - else - /* Clean out the temporary type list, since the types will go away. */ - get_temporary_types (); - } -} - -/* This is called from finish_function (within yyparse) - after each top-level definition is parsed. - It is supposed to compile that function or variable - and output the assembler code for it. - After we return, the tree storage is freed. */ - -void -rest_of_compilation (decl) - tree decl; -{ - register rtx insns; - int start_time = gettime (); - int tem; - - /* If we are reconsidering an inline function - at the end of compilation, skip the stuff for making it inline. */ - - if (DECL_SAVED_INSNS (decl) == 0) - { - - /* If requested, consider whether to make this function inline. */ - if (flag_inline_functions || TREE_INLINE (decl)) - { - TIMEVAR (integration_time, - { - int specd = TREE_INLINE (decl); - char *lose = function_cannot_inline_p (decl); - if (lose != 0 && specd) - warning_with_decl (decl, lose); - if (lose == 0) - save_for_inline (decl); - else - TREE_INLINE (decl) = 0; - }); - } - - insns = get_insns (); - - /* Dump the rtl code if we are dumping rtl. */ - - if (rtl_dump) - TIMEVAR (dump_time, - { - fprintf (rtl_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - if (DECL_SAVED_INSNS (decl)) - fprintf (rtl_dump_file, ";; (integrable)\n\n"); - print_rtl (rtl_dump_file, insns); - fflush (rtl_dump_file); - }); - - /* If function is inline, and we don't yet know whether to - compile it by itself, defer decision till end of compilation. - finish_compilation will call rest_of_compilation again - for those functions that need to be output. */ - - if (((! TREE_PUBLIC (decl) && ! TREE_ADDRESSABLE (decl) - && ! flag_keep_inline_functions) - || TREE_EXTERNAL (decl)) - && TREE_INLINE (decl)) - goto exit_rest_of_compilation; - } - - if (rtl_dump_and_exit || flag_syntax_only) - { - get_temporary_types (); - goto exit_rest_of_compilation; - } - - TREE_ASM_WRITTEN (decl) = 1; - - insns = get_insns (); - - /* Copy any shared structure that should not be shared. */ - - unshare_all_rtl (insns); - - /* See if we have allocated stack slots that are not directly addressable. - If so, scan all the insns and create explicit address computation - for all references to such slots. */ -/* fixup_stack_slots (); */ - - /* Do jump optimization the first time, if -opt. - Also do it if -W, but in that case it doesn't change the rtl code, - it only computes whether control can drop off the end of the function. */ - - if (optimize || extra_warnings || warn_return_type - /* If function is `volatile', we should warn if it tries to return. */ - || TREE_THIS_VOLATILE (decl)) - TIMEVAR (jump_time, jump_optimize (insns, 0, 0)); - - /* Dump rtl code after jump, if we are doing that. */ - - if (jump_opt_dump) - TIMEVAR (dump_time, - { - fprintf (jump_opt_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - print_rtl (jump_opt_dump_file, insns); - fflush (jump_opt_dump_file); - }); - - /* Perform common subexpression elimination. - Nonzero value from `cse_main' means that jumps were simplified - and some code may now be unreachable, so do - jump optimization again. */ - - if (optimize) - { - TIMEVAR (cse_time, reg_scan (insns, max_reg_num (), 0)); - - TIMEVAR (cse_time, tem = cse_main (insns, max_reg_num ())); - - if (tem) - TIMEVAR (jump_time, jump_optimize (insns, 0, 0)); - } - - /* Dump rtl code after cse, if we are doing that. */ - - if (cse_dump) - TIMEVAR (dump_time, - { - fprintf (cse_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - print_rtl (cse_dump_file, insns); - fflush (cse_dump_file); - }); - - if (loop_dump) - TIMEVAR (dump_time, - { - fprintf (loop_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - }); - - /* Move constant computations out of loops. */ - - if (optimize) - { - TIMEVAR (loop_time, - { - reg_scan (insns, max_reg_num (), 1); - loop_optimize (insns, loop_dump ? loop_dump_file : 0); - }); - } - - /* Dump rtl code after loop opt, if we are doing that. */ - - if (loop_dump) - TIMEVAR (dump_time, - { - print_rtl (loop_dump_file, insns); - fflush (loop_dump_file); - }); - - /* Now we choose between stupid (pcc-like) register allocation - (if we got the -noreg switch and not -opt) - and smart register allocation. */ - - if (optimize) /* Stupid allocation probably won't work */ - obey_regdecls = 0; /* if optimizations being done. */ - - regclass_init (); - - /* Print function header into flow dump now - because doing the flow analysis makes some of the dump. */ - - if (flow_dump) - TIMEVAR (dump_time, - { - fprintf (flow_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - }); - - if (obey_regdecls) - { - TIMEVAR (flow_time, - { - regclass (insns, max_reg_num ()); - stupid_life_analysis (insns, max_reg_num (), - flow_dump_file); - }); - } - else - { - /* Do control and data flow analysis, - and write some of the results to dump file. */ - - TIMEVAR (flow_time, flow_analysis (insns, max_reg_num (), - flow_dump_file)); - if (extra_warnings) - uninitialized_vars_warning (DECL_INITIAL (decl)); - } - - /* Dump rtl after flow analysis. */ - - if (flow_dump) - TIMEVAR (dump_time, - { - print_rtl (flow_dump_file, insns); - fflush (flow_dump_file); - }); - - /* If -opt, try combining insns through substitution. */ - - if (optimize) - TIMEVAR (combine_time, combine_instructions (insns, max_reg_num ())); - - /* Dump rtl code after insn combination. */ - - if (combine_dump) - TIMEVAR (dump_time, - { - fprintf (combine_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - dump_combine_stats (combine_dump_file); - print_rtl (combine_dump_file, insns); - fflush (combine_dump_file); - }); - - /* Unless we did stupid register allocation, - allocate pseudo-regs that are used only within 1 basic block. */ - - if (!obey_regdecls) - TIMEVAR (local_alloc_time, - { - regclass (insns, max_reg_num ()); - local_alloc (); - }); - - /* Dump rtl code after allocating regs within basic blocks. */ - - if (local_reg_dump) - TIMEVAR (dump_time, - { - fprintf (local_reg_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - dump_flow_info (local_reg_dump_file); - dump_local_alloc (local_reg_dump_file); - print_rtl (local_reg_dump_file, insns); - fflush (local_reg_dump_file); - }); - - if (global_reg_dump) - TIMEVAR (dump_time, - fprintf (global_reg_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl)))); - - /* Unless we did stupid register allocation, - allocate remaining pseudo-regs, then do the reload pass - fixing up any insns that are invalid. */ - - TIMEVAR (global_alloc_time, - { - if (!obey_regdecls) - global_alloc (global_reg_dump ? global_reg_dump_file : 0); - else - reload (insns, 0, - global_reg_dump ? global_reg_dump_file : 0); - }); - - if (global_reg_dump) - TIMEVAR (dump_time, - { - dump_global_regs (global_reg_dump_file); - print_rtl (global_reg_dump_file, insns); - fflush (global_reg_dump_file); - }); - - rtx_equal_function_value_matters = 1; - reload_completed = 1; - - /* One more attempt to remove jumps to .+1 - left by dead-store-elimination. - Also do cross-jumping this time - and delete no-op move insns. */ - - if (optimize) - { - TIMEVAR (jump_time, jump_optimize (insns, 1, 1)); - } - - /* Dump rtl code after jump, if we are doing that. */ - - if (jump2_opt_dump) - TIMEVAR (dump_time, - { - fprintf (jump2_opt_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - print_rtl (jump2_opt_dump_file, insns); - fflush (jump2_opt_dump_file); - }); - - /* If a scheduling pass for delayed branches is to be done, - call the scheduling code. */ - -#ifdef HAVE_DELAYED_BRANCH - if (optimize && flag_delayed_branch) - { - TIMEVAR (dbr_sched_time, dbr_schedule (insns, dbr_sched_dump_file)); - if (dbr_sched_dump) - { - TIMEVAR (dump_time, - { - fprintf (dbr_sched_dump_file, "\n;; Function %s\n\n", - IDENTIFIER_POINTER (DECL_NAME (decl))); - print_rtl (dbr_sched_dump_file, insns); - fflush (dbr_sched_dump_file); - }); - } - } -#endif - - /* Now turn the rtl into assembler code. */ - - TIMEVAR (final_time, - { - assemble_function (decl); - final_start_function (insns, asm_out_file, - write_symbols, optimize); - final (insns, asm_out_file, - write_symbols, optimize, 0); - final_end_function (insns, asm_out_file, - write_symbols, optimize); - fflush (asm_out_file); - }); - - /* Write GDB symbols if requested */ - - if (write_symbols == GDB_DEBUG) - { - TIMEVAR (symout_time, - { - symout_types (get_permanent_types ()); - symout_types (get_temporary_types ()); - - DECL_BLOCK_SYMTAB_ADDRESS (decl) - = symout_function (DECL_INITIAL (decl), - DECL_ARGUMENTS (decl), 0); - symout_function_end (); - }); - } - else - get_temporary_types (); - - /* Write DBX symbols if requested */ - -#ifdef DBX_DEBUGGING_INFO - if (write_symbols == DBX_DEBUG) - TIMEVAR (symout_time, dbxout_function (decl)); -#endif - - exit_rest_of_compilation: - - rtx_equal_function_value_matters = 0; - reload_completed = 0; - - /* Clear out the real_constant_chain before some of the rtx's - it runs through become garbage. */ - - clear_const_double_mem (); - - /* The parsing time is all the time spent in yyparse - *except* what is spent in this function. */ - - parse_time -= gettime () - start_time; -} - -/* Entry point of cc1. Decode command args, then call compile_file. - Exit code is 35 if can't open files, 34 if fatal error, - 33 if had nonfatal errors, else success. */ - -int -main (argc, argv, envp) - int argc; - char **argv; - char **envp; -{ - register int i; - char *filename = 0; - int print_mem_flag = 0; - char *p; - - /* save in case md file wants to emit args as a comment. */ - save_argc = argc; - save_argv = argv; - - p = argv[0] + strlen (argv[0]); - while (p != argv[0] && p[-1] != '/') --p; - progname = p; - -#ifdef RLIMIT_STACK - /* Get rid of any avoidable limit on stack size. */ - { - struct rlimit rlim; - - /* Set the stack limit huge so that alloca does not fail. */ - getrlimit (RLIMIT_STACK, &rlim); - rlim.rlim_cur = rlim.rlim_max; - setrlimit (RLIMIT_STACK, &rlim); - } -#endif /* RLIMIT_STACK */ - - signal (SIGFPE, float_signal); - - signal (SIGPIPE, pipe_closed); - - /* Initialize whether `char' is signed. */ - flag_signed_char = DEFAULT_SIGNED_CHAR; -#ifdef DEFAULT_SHORT_ENUMS - /* Initialize how much space enums occupy, by default. */ - flag_short_enums = DEFAULT_SHORT_ENUMS; -#endif - - /* This is zeroed by -O. */ - obey_regdecls = 1; - - /* Initialize register usage now so switches may override. */ - init_reg_sets (); - - target_flags = 0; - set_target_switch (""); - - for (i = 1; i < argc; i++) - if (argv[i][0] == '-' && argv[i][1] != 0) - { - register char *str = argv[i] + 1; - if (str[0] == 'Y') - str++; - - if (str[0] == 'm') - set_target_switch (&str[1]); - else if (!strcmp (str, "dumpbase")) - { - dump_base_name = argv[++i]; - } - else if (str[0] == 'd') - { - register char *p = &str[1]; - while (*p) - switch (*p++) - { - case 'c': - combine_dump = 1; - break; - case 'd': - dbr_sched_dump = 1; - break; - case 'f': - flow_dump = 1; - break; - case 'g': - global_reg_dump = 1; - break; - case 'j': - jump_opt_dump = 1; - break; - case 'J': - jump2_opt_dump = 1; - break; - case 'l': - local_reg_dump = 1; - break; - case 'L': - loop_dump = 1; - break; - case 'm': - print_mem_flag = 1; - break; - case 'r': - rtl_dump = 1; - break; - case 's': - cse_dump = 1; - break; - case 'y': - yydebug = 1; - break; - } - } - else if (str[0] == 'f') - { - int j; - register char *p = &str[1]; - int found = 0; - - /* Some kind of -f option. - P's value is the option sans `-f'. - Search for it in the table of options. */ - - for (j = 0; - !found && j < sizeof (f_options) / sizeof (f_options[0]); - j++) - { - if (!strcmp (p, f_options[j].string)) - { - *f_options[j].variable = f_options[j].on_value; - /* A goto here would be cleaner, - but breaks the vax pcc. */ - found = 1; - } - if (p[0] == 'n' && p[1] == 'o' && p[2] == '-' - && ! strcmp (p+3, f_options[j].string)) - { - *f_options[j].variable = ! f_options[j].on_value; - found = 1; - } - } - - if (found) - ; - else if (!strncmp (p, "fixed-", 6)) - fix_register (&p[6], 1, 1); - else if (!strncmp (p, "call-used-", 10)) - fix_register (&p[10], 0, 1); - else if (!strncmp (p, "call-saved-", 11)) - fix_register (&p[11], 0, 0); - else if (! lang_decode_option (argv[i])) - error ("Invalid option `%s'", argv[i]); - } - else if (!strcmp (str, "noreg")) - ; - else if (!strcmp (str, "opt")) - optimize = 1, obey_regdecls = 0; - else if (!strcmp (str, "O")) - optimize = 1, obey_regdecls = 0; - else if (!strcmp (str, "pedantic")) - pedantic = 1; - else if (lang_decode_option (argv[i])) - ; - else if (!strcmp (str, "quiet")) - quiet_flag = 1; - else if (!strcmp (str, "version")) - { - extern char *version_string, *language_string; - fprintf (stderr, "%s version %s", language_string, version_string); -#ifdef TARGET_VERSION - TARGET_VERSION; -#endif -#ifdef __GNUC__ -#ifndef __VERSION__ -#define __VERSION__ "[unknown]" -#endif - fprintf (stderr, " compiled by GNU C version %s.\n", __VERSION__); -#else - fprintf (stderr, " compiled by CC.\n"); -#endif - print_target_switch_defaults (); - } - else if (!strcmp (str, "w")) - inhibit_warnings = 1; - else if (!strcmp (str, "W")) - extra_warnings = 1; - else if (!strcmp (str, "Wunused")) - warn_unused = 1; - else if (!strcmp (str, "Wshadow")) - warn_shadow = 1; - else if (!strcmp (str, "Wswitch")) - warn_switch = 1; - else if (!strncmp (str, "Wid-clash-", 10)) - { - char *endp = str + 10; - - while (*endp) - { - if (*endp >= '0' && *endp <= '9') - endp++; - else - error ("Invalid option `%s'", argv[i]); - } - warn_id_clash = 1; - id_clash_len = atoi (str + 10); - } - else if (!strcmp (str, "p")) - profile_flag = 1; - else if (!strcmp (str, "a")) - { -#if !defined (BLOCK_PROFILER) || !defined (FUNCTION_BLOCK_PROFILER) - warning ("`-a' option (basic block profile) not supported"); -#else - profile_block_flag = 1; -#endif - } - else if (!strcmp (str, "gg")) - write_symbols = GDB_DEBUG; -#ifdef DBX_DEBUGGING_INFO - else if (!strcmp (str, "g0")) - write_symbols = DBX_DEBUG; - else if (!strcmp (str, "G0")) - write_symbols = DBX_DEBUG; - else if (!strcmp (str, "g")) - { - write_symbols = DBX_DEBUG; - use_gdb_dbx_extensions = 1; - } - else if (!strcmp (str, "G")) - { - write_symbols = DBX_DEBUG; - use_gdb_dbx_extensions = 1; - } -#endif -#ifdef SDB_DEBUGGING_INFO - else if (!strcmp (str, "g")) - write_symbols = SDB_DEBUG; - else if (!strcmp (str, "G")) - write_symbols = SDB_DEBUG; - else if (!strcmp (str, "g0")) - write_symbols = SDB_DEBUG; - else if (!strcmp (str, "G0")) - write_symbols = SDB_DEBUG; -#endif - else if (!strcmp (str, "symout")) - { - if (write_symbols == NO_DEBUG) - write_symbols = GDB_DEBUG; - sym_file_name = argv[++i]; - } - else if (!strcmp (str, "o")) - { - asm_file_name = argv[++i]; - } - else - error ("Invalid option `%s'", argv[i]); - } - else - filename = argv[i]; - -#ifdef OVERRIDE_OPTIONS - /* Some machines may reject certain combinations of options. */ - OVERRIDE_OPTIONS; -#endif - - /* Now that register usage is specified, convert it to HARD_REG_SETs. */ - init_reg_sets_1 (); - - compile_file (filename); - -#ifndef USG -#ifndef VMS - if (print_mem_flag) - { - extern char **environ; - char *lim = (char *) sbrk (0); - - fprintf (stderr, "Data size %d.\n", - (int) lim - (int) &environ); - fflush (stderr); - - system ("ps v"); - } -#endif /* not VMS */ -#endif /* not USG */ - - if (errorcount) - exit (FATAL_EXIT_CODE); - if (sorrycount) - exit (FATAL_EXIT_CODE); - exit (SUCCESS_EXIT_CODE); - return 34; -} - -/* Decode -m switches. */ - -/* Here is a table, controlled by the tm-...h file, listing each -m switch - and which bits in `target_switches' it should set or clear. - If VALUE is positive, it is bits to set. - If VALUE is negative, -VALUE is bits to clear. - (The sign bit is not used so there is no confusion.) */ - -struct {char *name; int value;} target_switches [] - = TARGET_SWITCHES; - -/* Decode the switch -mNAME. */ - -void -set_target_switch (name) - char *name; -{ - register int j; - for (j = 0; j < sizeof target_switches / sizeof target_switches[0]; j++) - if (!strcmp (target_switches[j].name, name)) - { - if (target_switches[j].value < 0) - target_flags &= ~-target_switches[j].value; - else - target_flags |= target_switches[j].value; - return; - } - error ("Invalid option `%s'", name); -} - -/* Print default target switches for -version. */ - -void -print_target_switch_defaults () -{ - register int j; - register int mask = TARGET_DEFAULT; - fprintf (stderr, "default target switches:"); - for (j = 0; j < sizeof target_switches / sizeof target_switches[0]; j++) - if (target_switches[j].name[0] != '\0' - && target_switches[j].value > 0 - && (target_switches[j].value & mask) == target_switches[j].value) - - fprintf (stderr, " -m%s", target_switches[j].name); - - fprintf (stderr, "\n"); -} diff --git a/gnu/usr.bin/gcc1/cc1/tree.c b/gnu/usr.bin/gcc1/cc1/tree.c deleted file mode 100644 index b61c24accb..0000000000 --- a/gnu/usr.bin/gcc1/cc1/tree.c +++ /dev/null @@ -1,2211 +0,0 @@ -/* Language-indepednent node constructors for parse phase of GNU compiler. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This file contains the low level primitives for operating on tree nodes, - including allocation, list operations, interning of identifiers, - construction of data type nodes and statement nodes, - and construction of type conversion nodes. It also contains - tables index by tree code that describe how to take apart - nodes of that code. - - It is intended to be language-independent, but occasionally - calls language-dependent routines defined (for C) in typecheck.c. - - The low-level allocation routines oballoc and permalloc - are used also for allocating many other kinds of objects - by all passes of the compiler. */ - -#include "config.h" -#include -#include "tree.h" -#include "obstack.h" -#include "gvarargs.h" -#include "flags.h" - -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free - -extern int xmalloc (); -extern void free (); - -/* Tree nodes of permanent duration are allocated in this obstack. - They are the identifier nodes, and everything outside of - the bodies and parameters of function definitions. */ - -struct obstack permanent_obstack; - -/* The initial RTL, and all ..._TYPE nodes, in a function - are allocated in this obstack. Usually they are freed at the - end of the function, but if the function is inline they are saved. */ - -struct obstack maybepermanent_obstack; - -/* The contents of the current function definition are allocated - in this obstack, and all are freed at the end of the function. */ - -struct obstack temporary_obstack; - -/* The tree nodes of an expression are allocated - in this obstack, and all are freed at the end of the expression. */ - -struct obstack momentary_obstack; - -/* This points at either permanent_obstack or maybepermanent_obstack. */ - -struct obstack *saveable_obstack; - -/* This is same as saveable_obstack during parse and expansion phase; - it points to temporary_obstack during optimization. - This is the obstack to be used for creating rtl objects. */ - -struct obstack *rtl_obstack; - -/* This points at either permanent_obstack or temporary_obstack. */ - -struct obstack *current_obstack; - -/* This points at either permanent_obstack or temporary_obstack - or momentary_obstack. */ - -struct obstack *expression_obstack; - -/* Addresses of first objects in some obstacks. - This is for freeing their entire contents. */ -char *maybepermanent_firstobj; -char *temporary_firstobj; -char *momentary_firstobj; - -/* Nonzero means all ..._TYPE nodes should be allocated permanently. */ - -int all_types_permanent; - -/* Stack of places to restore the momentary obstack back to. */ - -struct momentary_level -{ - /* Pointer back to previous such level. */ - struct momentary_level *prev; - /* First object allocated within this level. */ - char *base; - /* Value of expression_obstack saved at entry to this level. */ - struct obstack *obstack; -}; - -struct momentary_level *momentary_stack; - -/* Table indexed by tree code giving a string containing a character - classifying the tree code. Possibilities are - t, d, s, c, r and e. See tree.def for details. */ - -#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) TYPE, - -char *tree_code_type[] = { -#include "tree.def" -}; -#undef DEFTREECODE - -/* Table indexed by tree code giving number of expression - operands beyond the fixed part of the node structure. - Not used for types or decls. */ - -#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) LENGTH, - -int tree_code_length[] = { -#include "tree.def" -}; -#undef DEFTREECODE - -/* Counter for assigning unique ids to all tree nodes. */ - -int tree_node_counter = 0; - -/* Hash table for uniquizing IDENTIFIER_NODEs by name. */ - -#define MAX_HASH_TABLE 1009 -static tree hash_table[MAX_HASH_TABLE]; /* id hash buckets */ - -/* 0 while creating built-in identifiers. */ -static int do_identifier_warnings; - -/* Init data for node creation, at the beginning of compilation. */ - -void -init_tree () -{ - obstack_init (&permanent_obstack); - - obstack_init (&temporary_obstack); - temporary_firstobj = (char *) obstack_alloc (&temporary_obstack, 0); - obstack_init (&momentary_obstack); - momentary_firstobj = (char *) obstack_alloc (&momentary_obstack, 0); - obstack_init (&maybepermanent_obstack); - maybepermanent_firstobj - = (char *) obstack_alloc (&maybepermanent_obstack, 0); - - current_obstack = &permanent_obstack; - expression_obstack = &permanent_obstack; - rtl_obstack = saveable_obstack = &permanent_obstack; - tree_node_counter = 1; - bzero (hash_table, sizeof hash_table); -} - -/* Start allocating on the temporary (per function) obstack. - This is done in start_function before parsing the function body, - and before each initialization at top level, and to go back - to temporary allocation after doing end_temporary_allocation. */ - -void -temporary_allocation () -{ - current_obstack = &temporary_obstack; - expression_obstack = &temporary_obstack; - rtl_obstack = saveable_obstack = &maybepermanent_obstack; - momentary_stack = 0; -} - -/* Start allocating on the permanent obstack but don't - free the temporary data. After calling this, call - `permanent_allocation' to fully resume permanent allocation status. */ - -void -end_temporary_allocation () -{ - current_obstack = &permanent_obstack; - expression_obstack = &permanent_obstack; - rtl_obstack = saveable_obstack = &permanent_obstack; -} - -/* Resume allocating on the temporary obstack, undoing - effects of `end_temporary_allocation'. */ - -void -resume_temporary_allocation () -{ - current_obstack = &temporary_obstack; - expression_obstack = &temporary_obstack; - rtl_obstack = saveable_obstack = &maybepermanent_obstack; -} - -/* Nonzero if temporary allocation is currently in effect. - Zero if currently doing permanent allocation. */ - -int -allocation_temporary_p () -{ - return current_obstack == &temporary_obstack; -} - -/* Go back to allocating on the permanent obstack - and free everything in the temporary obstack. - This is done in finish_function after fully compiling a function. */ - -void -permanent_allocation () -{ - /* Free up previous temporary obstack data */ - obstack_free (&temporary_obstack, temporary_firstobj); - obstack_free (&momentary_obstack, momentary_firstobj); - obstack_free (&maybepermanent_obstack, maybepermanent_firstobj); - - current_obstack = &permanent_obstack; - expression_obstack = &permanent_obstack; - rtl_obstack = saveable_obstack = &permanent_obstack; -} - -/* Save permanently everything on the maybepermanent_obstack. */ - -void -preserve_data () -{ - maybepermanent_firstobj - = (char *) obstack_alloc (&maybepermanent_obstack, 0); -} - -/* Allocate SIZE bytes in the current obstack - and return a pointer to them. - In practice the current obstack is always the temporary one. */ - -char * -oballoc (size) - int size; -{ - return (char *) obstack_alloc (current_obstack, size); -} - -/* Free the object PTR in the current obstack - as well as everything allocated since PTR. - In practice the current obstack is always the temporary one. */ - -void -obfree (ptr) - char *ptr; -{ - obstack_free (current_obstack, ptr); -} - -/* Allocate SIZE bytes in the permanent obstack - and return a pointer to them. */ - -char * -permalloc (size) - long size; -{ - return (char *) obstack_alloc (&permanent_obstack, size); -} - -/* Allocate SIZE bytes in the saveable obstack - and return a pointer to them. */ - -char * -savealloc (size) - int size; -{ - return (char *) obstack_alloc (saveable_obstack, size); -} - -/* Start a level of momentary allocation. - In C, each compound statement has its own level - and that level is freed at the end of each statement. - All expression nodes are allocated in the momentary allocation level. */ - -void -push_momentary () -{ - struct momentary_level *tem - = (struct momentary_level *) obstack_alloc (&momentary_obstack, - sizeof (struct momentary_level)); - tem->prev = momentary_stack; - tem->base = (char *) obstack_base (&momentary_obstack); - tem->obstack = expression_obstack; - momentary_stack = tem; - expression_obstack = &momentary_obstack; -} - -/* Free all the storage in the current momentary-allocation level. - In C, this happens at the end of each statement. */ - -void -clear_momentary () -{ - obstack_free (&momentary_obstack, momentary_stack->base); -} - -/* Discard a level of momentary allocation. - In C, this happens at the end of each compound statement. - Restore the status of expression node allocation - that was in effect before this level was created. */ - -void -pop_momentary () -{ - struct momentary_level *tem = momentary_stack; - momentary_stack = tem->prev; - obstack_free (&momentary_obstack, tem); - expression_obstack = tem->obstack; -} - -/* Call when starting to parse a declaration: - make expressions in the declaration last the length of the function. - Returns an argument that should be passed to resume_momentary later. */ - -int -suspend_momentary () -{ - register int tem = expression_obstack == &momentary_obstack; - expression_obstack = saveable_obstack; - return tem; -} - -/* Call when finished parsing a declaration: - restore the treatment of node-allocation that was - in effect before the suspension. - YES should be the value previously returned by suspend_momentary. */ - -void -resume_momentary (yes) - int yes; -{ - if (yes) - expression_obstack = &momentary_obstack; -} - -/* Return a newly allocated node of code CODE. - Initialize the node's unique id and its TREE_PERMANENT flag. - For decl and type nodes, some other fields are initialized. - The rest of the node is initialized to zero. - - Achoo! I got a code in the node. */ - -tree -make_node (code) - enum tree_code code; -{ - register tree t; - register int type = *tree_code_type[(int) code]; - register int length; - register struct obstack *obstack = current_obstack; - register int i; - - switch (type) - { - case 'd': /* A decl node */ - length = sizeof (struct tree_decl); - /* All decls in an inline function need to be saved. */ - if (obstack != &permanent_obstack) - obstack = saveable_obstack; - /* PARM_DECLs always go on saveable_obstack, not permanent, - even though we may make them before the function turns - on temporary allocation. */ - else if (code == PARM_DECL) - obstack = &maybepermanent_obstack; - break; - - case 't': /* a type node */ - length = sizeof (struct tree_type); - /* All data types are put where we can preserve them if nec. */ - if (obstack != &permanent_obstack) - obstack = all_types_permanent ? &permanent_obstack : saveable_obstack; - break; - - case 's': /* a stmt node */ - length = sizeof (struct tree_common) - + 2 * sizeof (int) - + tree_code_length[(int) code] * sizeof (char *); - /* All stmts are put where we can preserve them if nec. */ - if (obstack != &permanent_obstack) - obstack = saveable_obstack; - break; - - case 'r': /* a reference */ - case 'e': /* an expression */ - obstack = expression_obstack; - length = sizeof (struct tree_exp) - + (tree_code_length[(int) code] - 1) * sizeof (char *); - break; - - case 'c': /* a constant */ - obstack = expression_obstack; - /* We can't use tree_code_length for this, since the number of words - is machine-dependent due to varying alignment of `double'. */ - if (code == REAL_CST) - { - length = sizeof (struct tree_real_cst); - break; - } - - case 'x': /* something random, like an identifier. */ - length = sizeof (struct tree_common) - + tree_code_length[(int) code] * sizeof (char *); - /* Identifier nodes are always permanent since they are - unique in a compiler run. */ - if (code == IDENTIFIER_NODE) obstack = &permanent_obstack; - } - - t = (tree) obstack_alloc (obstack, length); - - TREE_UID (t) = tree_node_counter++; - TREE_TYPE (t) = 0; - TREE_CHAIN (t) = 0; - for (i = (length / sizeof (int)) - 1; - i >= sizeof (struct tree_common) / sizeof (int) - 1; - i--) - ((int *) t)[i] = 0; - - TREE_SET_CODE (t, code); - if (obstack == &permanent_obstack) - TREE_PERMANENT (t) = 1; - - if (type == 'd') - { - extern int lineno; - - DECL_ALIGN (t) = 1; - DECL_SIZE_UNIT (t) = 1; - DECL_VOFFSET_UNIT (t) = 1; - DECL_SOURCE_LINE (t) = lineno; - DECL_SOURCE_FILE (t) = input_filename; - } - - if (type == 't') - { - TYPE_ALIGN (t) = 1; - TYPE_SIZE_UNIT (t) = 1; - TYPE_MAIN_VARIANT (t) = t; - } - - if (type == 'c') - { - TREE_LITERAL (t) = 1; - } - - return t; -} - -/* Return a new node with the same contents as NODE - except that its TREE_CHAIN is zero and it has a fresh uid. */ - -tree -copy_node (node) - tree node; -{ - register tree t; - register enum tree_code code = TREE_CODE (node); - register int length; - register int i; - - switch (*tree_code_type[(int) code]) - { - case 'd': /* A decl node */ - length = sizeof (struct tree_decl); - break; - - case 't': /* a type node */ - length = sizeof (struct tree_type); - break; - - case 's': - length = sizeof (struct tree_common) - + 2 * sizeof (int) - + tree_code_length[(int) code] * sizeof (char *); - break; - - case 'r': /* a reference */ - case 'e': /* a expression */ - length = sizeof (struct tree_exp) - + (tree_code_length[(int) code] - 1) * sizeof (char *); - break; - - case 'c': /* a constant */ - /* We can't use tree_code_length for this, since the number of words - is machine-dependent due to varying alignment of `double'. */ - if (code == REAL_CST) - { - length = sizeof (struct tree_real_cst); - break; - } - - case 'x': /* something random, like an identifier. */ - length = sizeof (struct tree_common) - + tree_code_length[(int) code] * sizeof (char *); - } - - t = (tree) obstack_alloc (current_obstack, length); - - for (i = ((length + sizeof (int) - 1) / sizeof (int)) - 1; - i >= 0; - i--) - ((int *) t)[i] = ((int *) node)[i]; - - TREE_UID (t) = tree_node_counter++; - TREE_CHAIN (t) = 0; - - TREE_PERMANENT (t) = (current_obstack == &permanent_obstack); - - return t; -} - -/* Return a copy of a chain of nodes, chained through the TREE_CHAIN field. - For example, this can copy a list made of TREE_LIST nodes. */ - -tree -copy_list (list) - tree list; -{ - tree head; - register tree prev, next; - - if (list == 0) - return 0; - - head = prev = copy_node (list); - next = TREE_CHAIN (list); - while (next) - { - TREE_CHAIN (prev) = copy_node (next); - prev = TREE_CHAIN (prev); - next = TREE_CHAIN (next); - } - return head; -} - -#define HASHBITS 30 - -/* Return an IDENTIFIER_NODE whose name is TEXT (a null-terminated string). - If an identifier with that name has previously been referred to, - the same node is returned this time. */ - -tree -get_identifier (text) - register char *text; -{ - register int hi; - register int i; - register tree idp; - register int len, hash_len; - - /* Compute length of text in len. */ - for (len = 0; text[len]; len++); - - /* Decide how much of that length to hash on */ - hash_len = len; - if (warn_id_clash && len > id_clash_len) - hash_len = id_clash_len; - - /* Compute hash code */ - hi = hash_len; - for (i = 0; i < hash_len; i++) - hi = ((hi * 613) + (unsigned)(text[i])); - - hi &= (1 << HASHBITS) - 1; - hi %= MAX_HASH_TABLE; - - /* Search table for identifier */ - for (idp = hash_table[hi]; idp; idp = TREE_CHAIN (idp)) - if (IDENTIFIER_LENGTH (idp) == len - && !strcmp (IDENTIFIER_POINTER (idp), text)) - return idp; /* <-- return if found */ - - /* Not found; optionally warn about a similar identifier */ - if (warn_id_clash && do_identifier_warnings && len >= id_clash_len) - for (idp = hash_table[hi]; idp; idp = TREE_CHAIN (idp)) - if (!strncmp (IDENTIFIER_POINTER (idp), text, id_clash_len)) - { - warning ("`%s' and `%s' identical in first n characters", - IDENTIFIER_POINTER (idp), text); - break; - } - - /* Not found, create one, add to chain */ - idp = make_node (IDENTIFIER_NODE); - IDENTIFIER_LENGTH (idp) = len; - - IDENTIFIER_POINTER (idp) = obstack_copy0 (&permanent_obstack, text, len); - - TREE_CHAIN (idp) = hash_table[hi]; - hash_table[hi] = idp; - return idp; /* <-- return if created */ -} - -/* Enable warnings on similar identifiers (if requested). - Done after the built-in identifiers are created. */ - -void -start_identifier_warnings () -{ - do_identifier_warnings = 1; -} - -/* Record the size of an identifier node for the language in use. - This is called by the language-specific files. */ - -void -set_identifier_size (size) - int size; -{ - tree_code_length[(int) IDENTIFIER_NODE] = size; -} - -/* Return a newly constructed INTEGER_CST node whose constant value - is specified by the two ints LOW and HI. - The TREE_TYPE is set to `int'. */ - -tree -build_int_2 (low, hi) - int low, hi; -{ - register tree t = make_node (INTEGER_CST); - TREE_INT_CST_LOW (t) = low; - TREE_INT_CST_HIGH (t) = hi; - TREE_TYPE (t) = integer_type_node; - return t; -} - -/* Return a new REAL_CST node whose type is TYPE and value is D. */ - -tree -build_real (type, d) - tree type; - REAL_VALUE_TYPE d; -{ - tree v; - - /* Check for valid float value for this type on this target machine; - if not, can print error message and store a valid value in D. */ -#ifdef CHECK_FLOAT_VALUE - CHECK_FLOAT_VALUE (TYPE_MODE (type), d); -#endif - - v = make_node (REAL_CST); - TREE_TYPE (v) = type; - TREE_REAL_CST (v) = d; - return v; -} - -/* Return a new REAL_CST node whose type is TYPE - and whose value is the integer value of the INTEGER_CST node I. */ - -#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) - -REAL_VALUE_TYPE -real_value_from_int_cst (i) - tree i; -{ - REAL_VALUE_TYPE d; -#ifdef REAL_ARITHMETIC - REAL_VALUE_FROM_INT (d, TREE_INT_CST_LOW (i), TREE_INT_CST_HIGH (i)); -#else /* not REAL_ARITHMETIC */ - if (TREE_INT_CST_HIGH (i) < 0) - { - d = (double) (~ TREE_INT_CST_HIGH (i)); - d *= ((double) (1 << (HOST_BITS_PER_INT / 2)) - * (double) (1 << (HOST_BITS_PER_INT / 2))); - d += (double) (unsigned) (~ TREE_INT_CST_LOW (i)); - d = (- d - 1.0); - } - else - { - d = (double) TREE_INT_CST_HIGH (i); - d *= ((double) (1 << (HOST_BITS_PER_INT / 2)) - * (double) (1 << (HOST_BITS_PER_INT / 2))); - d += (double) (unsigned) TREE_INT_CST_LOW (i); - } -#endif /* not REAL_ARITHMETIC */ - return d; -} - -/* This function can't be implemented if we can't do arithmetic - on the float representation. */ - -tree -build_real_from_int_cst (type, i) - tree type; - tree i; -{ - tree v; - REAL_VALUE_TYPE d; - - v = make_node (REAL_CST); - TREE_TYPE (v) = type; - - d = real_value_from_int_cst (i); - /* Check for valid float value for this type on this target machine; - if not, can print error message and store a valid value in D. */ -#ifdef CHECK_FLOAT_VALUE - CHECK_FLOAT_VALUE (TYPE_MODE (type), d); -#endif - - TREE_REAL_CST (v) = d; - return v; -} - -#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */ - -/* Return a newly constructed STRING_CST node whose value is - the LEN characters at STR. - The TREE_TYPE is not initialized. */ - -tree -build_string (len, str) - int len; - char *str; -{ - register tree s = make_node (STRING_CST); - TREE_STRING_LENGTH (s) = len; - TREE_STRING_POINTER (s) = obstack_copy0 (saveable_obstack, str, len); - return s; -} - -/* Return a newly constructed COMPLEX_CST node whose value is - specified by the real and imaginary parts REAL and IMAG. - Both REAL and IMAG should be constant nodes. - The TREE_TYPE is not initialized. */ - -tree -build_complex (real, imag) - tree real, imag; -{ - register tree t = make_node (COMPLEX_CST); - TREE_REALPART (t) = real; - TREE_IMAGPART (t) = imag; - return t; -} - -/* Return 1 if EXPR is the integer constant zero. */ - -int -integer_zerop (expr) - tree expr; -{ - return (TREE_CODE (expr) == INTEGER_CST - && TREE_INT_CST_LOW (expr) == 0 - && TREE_INT_CST_HIGH (expr) == 0); -} - -/* Return 1 if EXPR is the integer constant one. */ - -int -integer_onep (expr) - tree expr; -{ - return (TREE_CODE (expr) == INTEGER_CST - && TREE_INT_CST_LOW (expr) == 1 - && TREE_INT_CST_HIGH (expr) == 0); -} - -/* Return 1 if EXPR is an integer containing all 1's - in as much precision as it contains. */ - -int -integer_all_onesp (expr) - tree expr; -{ - register int prec; - register int uns; - - if (TREE_CODE (expr) != INTEGER_CST) - return 0; - - uns = TREE_UNSIGNED (TREE_TYPE (expr)); - if (!uns) - return TREE_INT_CST_LOW (expr) == -1 && TREE_INT_CST_HIGH (expr) == -1; - - prec = TYPE_PRECISION (TREE_TYPE (expr)); - if (prec >= HOST_BITS_PER_INT) - return TREE_INT_CST_LOW (expr) == -1 - && TREE_INT_CST_HIGH (expr) == (1 << (prec - HOST_BITS_PER_INT)) - 1; - else - return TREE_INT_CST_LOW (expr) == (1 << prec) - 1; -} - -/* Return the length of a chain of nodes chained through TREE_CHAIN. - We expect a null pointer to mark the end of the chain. - This is the Lisp primitive `length'. */ - -int -list_length (t) - tree t; -{ - register tree tail; - register int len = 0; - - for (tail = t; tail; tail = TREE_CHAIN (tail)) - len++; - - return len; -} - -/* Concatenate two chains of nodes (chained through TREE_CHAIN) - by modifying the last node in chain 1 to point to chain 2. - This is the Lisp primitive `nconc'. */ - -tree -chainon (op1, op2) - tree op1, op2; -{ - tree t; - - if (op1) - { - for (t = op1; TREE_CHAIN (t); t = TREE_CHAIN (t)) - if (t == op2) abort (); /* Circularity being created */ - TREE_CHAIN (t) = op2; - return op1; - } - else return op2; -} - -/* Return a newly created TREE_LIST node whose - purpose and value fields are PARM and VALUE. */ - -tree -build_tree_list (parm, value) - tree parm, value; -{ - register tree t = make_node (TREE_LIST); - TREE_PURPOSE (t) = parm; - TREE_VALUE (t) = value; - return t; -} - -/* Return a newly created TREE_LIST node whose - purpose and value fields are PARM and VALUE - and whose TREE_CHAIN is CHAIN. */ - -tree -tree_cons (purpose, value, chain) - tree purpose, value, chain; -{ - register tree node = make_node (TREE_LIST); - TREE_CHAIN (node) = chain; - TREE_PURPOSE (node) = purpose; - TREE_VALUE (node) = value; - return node; -} - -/* Same as `tree_cons' but make a permanent object. */ - -tree -perm_tree_cons (purpose, value, chain) - tree purpose, value, chain; -{ - register tree node; - register struct obstack *ambient_obstack = current_obstack; - current_obstack = &permanent_obstack; - - node = make_node (TREE_LIST); - TREE_CHAIN (node) = chain; - TREE_PURPOSE (node) = purpose; - TREE_VALUE (node) = value; - - current_obstack = ambient_obstack; - return node; -} - -/* Same as `tree_cons', but make this node temporary, regardless. */ - -tree -temp_tree_cons (purpose, value, chain) - tree purpose, value, chain; -{ - register tree node; - register struct obstack *ambient_obstack = current_obstack; - current_obstack = &temporary_obstack; - - node = make_node (TREE_LIST); - TREE_CHAIN (node) = chain; - TREE_PURPOSE (node) = purpose; - TREE_VALUE (node) = value; - - current_obstack = ambient_obstack; - return node; -} - -/* Same as `tree_cons', but save this node if the function's RTL is saved. */ - -tree -saveable_tree_cons (purpose, value, chain) - tree purpose, value, chain; -{ - register tree node; - register struct obstack *ambient_obstack = current_obstack; - current_obstack = saveable_obstack; - - node = make_node (TREE_LIST); - TREE_CHAIN (node) = chain; - TREE_PURPOSE (node) = purpose; - TREE_VALUE (node) = value; - - current_obstack = ambient_obstack; - return node; -} - -/* Return the last node in a chain of nodes (chained through TREE_CHAIN). */ - -tree -tree_last (chain) - register tree chain; -{ - register tree next; - if (chain) - while (next = TREE_CHAIN (chain)) - chain = next; - return chain; -} - -/* Reverse the order of elements in the chain T, - and return the new head of the chain (old last element). */ - -tree -nreverse (t) - tree t; -{ - register tree prev = 0, decl, next; - for (decl = t; decl; decl = next) - { - next = TREE_CHAIN (decl); - TREE_CHAIN (decl) = prev; - prev = decl; - } - return prev; -} - -/* Return the size nominally occupied by an object of type TYPE - when it resides in memory. The value is measured in units of bytes, - and its data type is that normally used for type sizes - (which is the first type created by make_signed_type or - make_unsigned_type). */ - -tree -size_in_bytes (type) - tree type; -{ - if (type == error_mark_node) - return integer_zero_node; - type = TYPE_MAIN_VARIANT (type); - if (TYPE_SIZE (type) == 0) - { - incomplete_type_error (0, type); - return integer_zero_node; - } - return convert_units (TYPE_SIZE (type), TYPE_SIZE_UNIT (type), - BITS_PER_UNIT); -} - -/* Return the size of TYPE (in bytes) as an integer, - or return -1 if the size can vary. */ - -int -int_size_in_bytes (type) - tree type; -{ - int size; - if (type == error_mark_node) - return 0; - type = TYPE_MAIN_VARIANT (type); - if (TYPE_SIZE (type) == 0) - return -1; - if (TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) - return -1; - size = TREE_INT_CST_LOW (TYPE_SIZE (type)) * TYPE_SIZE_UNIT (type); - return (size + BITS_PER_UNIT - 1) / BITS_PER_UNIT; -} - -/* Return, as an INTEGER_CST node, the number of elements for - TYPE (which is an ARRAY_TYPE). */ - -tree -array_type_nelts (type) - tree type; -{ - tree index_type = TYPE_DOMAIN (type); - return (tree_int_cst_equal (TYPE_MIN_VALUE (index_type), integer_zero_node) - ? TYPE_MAX_VALUE (index_type) - : fold (build (MINUS_EXPR, integer_type_node, - TYPE_MAX_VALUE (index_type), - TYPE_MIN_VALUE (index_type)))); -} - -/* Return nonzero if arg is static -- a reference to an object in - static storage. This is not the same as the C meaning of `static'. */ - -int -staticp (arg) - tree arg; -{ - register enum tree_code code = TREE_CODE (arg); - - if ((code == VAR_DECL || code == FUNCTION_DECL || code == CONSTRUCTOR) - && (TREE_STATIC (arg) || TREE_EXTERNAL (arg))) - return 1; - - if (code == STRING_CST) - return 1; - - if (code == COMPONENT_REF) - return (DECL_VOFFSET (TREE_OPERAND (arg, 1)) == 0 - && staticp (TREE_OPERAND (arg, 0))); - - if (code == INDIRECT_REF) - return TREE_LITERAL (TREE_OPERAND (arg, 0)); - - if (code == ARRAY_REF) - { - if (TREE_CODE (TYPE_SIZE (TREE_TYPE (arg))) == INTEGER_CST - && TREE_CODE (TREE_OPERAND (arg, 1)) == INTEGER_CST) - return staticp (TREE_OPERAND (arg, 0)); - } - - return 0; -} - -/* Return nonzero if REF is an lvalue valid for this language. - Lvalues can be assigned, unless they have TREE_READONLY. - Lvalues can have their address taken, unless they have TREE_REGDECL. */ - -int -lvalue_p (ref) - tree ref; -{ - register enum tree_code code = TREE_CODE (ref); - - if (language_lvalue_valid (ref)) - switch (code) - { - case COMPONENT_REF: - return lvalue_p (TREE_OPERAND (ref, 0)); - - case STRING_CST: - return 1; - - case INDIRECT_REF: - case ARRAY_REF: - case VAR_DECL: - case PARM_DECL: - case RESULT_DECL: - case ERROR_MARK: - if (TREE_CODE (TREE_TYPE (ref)) != FUNCTION_TYPE - && TREE_CODE (TREE_TYPE (ref)) != METHOD_TYPE) - return 1; - break; - - case NEW_EXPR: - return 1; - - case CALL_EXPR: - if (TREE_CODE (TREE_TYPE (ref)) == REFERENCE_TYPE) - return 1; - } - return 0; -} - -/* Return nonzero if REF is an lvalue valid for this language; - otherwise, print an error message and return zero. */ - -int -lvalue_or_else (ref, string) - tree ref; - char *string; -{ - int win = lvalue_p (ref); - if (! win) - error ("invalid lvalue in %s", string); - return win; -} - -/* This should be applied to any node which may be used in more than one place, - but must be evaluated only once. Normally, the code generator would - reevaluate the node each time; this forces it to compute it once and save - the result. This is done by encapsulating the node in a SAVE_EXPR. */ - -tree -save_expr (expr) - tree expr; -{ - register tree t = fold (expr); - - /* If the tree evaluates to a constant, then we don't want to hide that - fact (i.e. this allows further folding, and direct checks for constants). - Since it is no problem to reevaluate literals, we just return the - literal node. */ - - if (TREE_LITERAL (t) || TREE_READONLY (t) || TREE_CODE (t) == SAVE_EXPR) - return t; - - return build (SAVE_EXPR, TREE_TYPE (expr), t, NULL); -} - -/* Stabilize a reference so that we can use it any number of times - without causing its operands to be evaluated more than once. - Returns the stabilized reference. - - Also allows conversion expressions whose operands are references. - Any other kind of expression is returned unchanged. */ - -tree -stabilize_reference (ref) - tree ref; -{ - register tree result; - register enum tree_code code = TREE_CODE (ref); - - switch (code) - { - case VAR_DECL: - case PARM_DECL: - case RESULT_DECL: - result = ref; - break; - - case NOP_EXPR: - case CONVERT_EXPR: - case FLOAT_EXPR: - case FIX_TRUNC_EXPR: - case FIX_FLOOR_EXPR: - case FIX_ROUND_EXPR: - case FIX_CEIL_EXPR: - result = build_nt (code, stabilize_reference (TREE_OPERAND (ref, 0))); - break; - - case INDIRECT_REF: - result = build_nt (INDIRECT_REF, save_expr (TREE_OPERAND (ref, 0))); - break; - - case COMPONENT_REF: - result = build_nt (COMPONENT_REF, - stabilize_reference (TREE_OPERAND (ref, 0)), - TREE_OPERAND (ref, 1)); - break; - - case ARRAY_REF: - result = build_nt (ARRAY_REF, stabilize_reference (TREE_OPERAND (ref, 0)), - save_expr (TREE_OPERAND (ref, 1))); - break; - - /* If arg isn't a kind of lvalue we recognize, make no change. - Caller should recognize the error for an invalid lvalue. */ - default: - return ref; - - case ERROR_MARK: - return error_mark_node; - } - - TREE_TYPE (result) = TREE_TYPE (ref); - TREE_READONLY (result) = TREE_READONLY (ref); - TREE_VOLATILE (result) = TREE_VOLATILE (ref); - TREE_THIS_VOLATILE (result) = TREE_THIS_VOLATILE (ref); - - return result; -} - -/* Low-level constructors for expressions. */ - -/* Build an expression of code CODE, data type TYPE, - and operands as specified by the arguments ARG1 and following arguments. - Expressions and reference nodes can be created this way. - Constants, decls, types and misc nodes cannot be. */ - -tree -build (va_alist) - va_dcl -{ - register va_list p; - enum tree_code code; - register tree t; - register int length; - register int i; - - va_start (p); - - code = va_arg (p, enum tree_code); - t = make_node (code); - length = tree_code_length[(int) code]; - TREE_TYPE (t) = va_arg (p, tree); - - if (length == 2) - { - /* This is equivalent to the loop below, but faster. */ - register tree arg0 = va_arg (p, tree); - register tree arg1 = va_arg (p, tree); - TREE_OPERAND (t, 0) = arg0; - TREE_OPERAND (t, 1) = arg1; - TREE_VOLATILE (t) - = (arg0 && TREE_VOLATILE (arg0)) || (arg1 && TREE_VOLATILE (arg1)); - } - else - { - for (i = 0; i < length; i++) - { - register tree operand = va_arg (p, tree); - TREE_OPERAND (t, i) = operand; - if (operand && TREE_VOLATILE (operand)) - TREE_VOLATILE (t) = 1; - } - } - va_end (p); - return t; -} - -/* Similar except don't specify the TREE_TYPE - and leave the TREE_VOLATILE as 0. - It is permissible for arguments to be null, - or even garbage if their values do not matter. */ - -tree -build_nt (va_alist) - va_dcl -{ - register va_list p; - register enum tree_code code; - register tree t; - register int length; - register int i; - - va_start (p); - - code = va_arg (p, enum tree_code); - t = make_node (code); - length = tree_code_length[(int) code]; - - for (i = 0; i < length; i++) - TREE_OPERAND (t, i) = va_arg (p, tree); - - va_end (p); - return t; -} - -tree -build_op_identifier (op1, op2) - tree op1, op2; -{ - register tree t = make_node (OP_IDENTIFIER); - TREE_PURPOSE (t) = op1; - TREE_VALUE (t) = op2; - return t; -} - -/* Create a DECL_... node of code CODE, name NAME and data type TYPE. - We do NOT enter this node in any sort of symbol table. - - layout_decl is used to set up the decl's storage layout. - Other slots are initialized to 0 or null pointers. */ - -tree -build_decl (code, name, type) - enum tree_code code; - tree name, type; -{ - register tree t; - - t = make_node (code); - -/* if (type == error_mark_node) - type = integer_type_node; */ -/* That is not done, deliberately, so that having error_mark_node - as the type can suppress useless errors in the use of this variable. */ - - DECL_NAME (t) = name; - if (name) - { - DECL_PRINT_NAME (t) = IDENTIFIER_POINTER (name); - DECL_ASSEMBLER_NAME (t) = IDENTIFIER_POINTER (name); - } - TREE_TYPE (t) = type; - DECL_ARGUMENTS (t) = NULL_TREE; - DECL_INITIAL (t) = NULL_TREE; - - if (code == VAR_DECL || code == PARM_DECL || code == RESULT_DECL) - layout_decl (t, 0); - else if (code == FUNCTION_DECL) - DECL_MODE (t) = FUNCTION_MODE; - - return t; -} - -#if 0 -/* Low-level constructors for statements. - These constructors all expect source file name and line number - as arguments, as well as enough arguments to fill in the data - in the statement node. */ - -tree -build_goto (filename, line, label) - char *filename; - int line; - tree label; -{ - register tree t = make_node (GOTO_STMT); - STMT_SOURCE_FILE (t) = filename; - STMT_SOURCE_LINE (t) = line; - STMT_BODY (t) = label; - return t; -} - -tree -build_return (filename, line, arg) - char *filename; - int line; - tree arg; -{ - register tree t = make_node (RETURN_STMT); - - STMT_SOURCE_FILE (t) = filename; - STMT_SOURCE_LINE (t) = line; - STMT_BODY (t) = arg; - return t; -} - -tree -build_expr_stmt (filename, line, expr) - char *filename; - int line; - tree expr; -{ - register tree t = make_node (EXPR_STMT); - - STMT_SOURCE_FILE (t) = filename; - STMT_SOURCE_LINE (t) = line; - STMT_BODY (t) = expr; - return t; -} - -tree -build_if (filename, line, cond, thenclause, elseclause) - char *filename; - int line; - tree cond, thenclause, elseclause; -{ - register tree t = make_node (IF_STMT); - - STMT_SOURCE_FILE (t) = filename; - STMT_SOURCE_LINE (t) = line; - STMT_COND (t) = cond; - STMT_THEN (t) = thenclause; - STMT_ELSE (t) = elseclause; - return t; -} - -tree -build_exit (filename, line, cond) - char *filename; - int line; - tree cond; -{ - register tree t = make_node (EXIT_STMT); - STMT_SOURCE_FILE (t) = filename; - STMT_SOURCE_LINE (t) = line; - STMT_BODY (t) = cond; - return t; -} - -tree -build_asm_stmt (filename, line, asmcode) - char *filename; - int line; - tree asmcode; -{ - register tree t = make_node (ASM_STMT); - STMT_SOURCE_FILE (t) = filename; - STMT_SOURCE_LINE (t) = line; - STMT_BODY (t) = asmcode; - return t; -} - -tree -build_case (filename, line, object, cases) - char *filename; - int line; - tree object, cases; -{ - register tree t = make_node (CASE_STMT); - STMT_SOURCE_FILE (t) = filename; - STMT_SOURCE_LINE (t) = line; - STMT_CASE_INDEX (t) = object; - STMT_CASE_LIST (t) = cases; - return t; -} - -tree -build_loop (filename, line, body) - char *filename; - int line; - tree body; -{ - register tree t = make_node (LOOP_STMT); - STMT_SOURCE_FILE (t) = filename; - STMT_SOURCE_LINE (t) = line; - STMT_BODY (t) = body; - return t; -} - -tree -build_compound (filename, line, body) - char *filename; - int line; - tree body; -{ - register tree t = make_node (COMPOUND_STMT); - STMT_SOURCE_FILE (t) = filename; - STMT_SOURCE_LINE (t) = line; - STMT_BODY (t) = body; - return t; -} - -#endif /* 0 */ - -/* LET_STMT nodes are used to represent the structure of binding contours - and declarations, once those contours have been exited and their contents - compiled. This information is used for outputting debugging info. */ - -tree -build_let (filename, line, vars, subblocks, supercontext, tags) - char *filename; - int line; - tree vars, subblocks, supercontext, tags; -{ - register tree t = make_node (LET_STMT); - STMT_SOURCE_FILE (t) = filename; - STMT_SOURCE_LINE (t) = line; - STMT_VARS (t) = vars; - STMT_SUBBLOCKS (t) = subblocks; - STMT_SUPERCONTEXT (t) = supercontext; - STMT_BIND_SIZE (t) = 0; - STMT_TYPE_TAGS (t) = tags; - return t; -} - -/* Return a type like TYPE except that its TREE_READONLY is CONSTP - and its TREE_VOLATILE is VOLATILEP. - - Such variant types already made are recorded so that duplicates - are not made. - - A variant types should never be used as the type of an expression. - Always copy the variant information into the TREE_READONLY - and TREE_VOLATILE of the expression, and then give the expression - as its type the "main variant", the variant whose TREE_READONLY - and TREE_VOLATILE are zero. Use TYPE_MAIN_VARIANT to find the - main variant. */ - -tree -build_type_variant (type, constp, volatilep) - tree type; - int constp, volatilep; -{ - register tree t, m = TYPE_MAIN_VARIANT (type); - register struct obstack *ambient_obstack = current_obstack; - - /* Treat any nonzero argument as 1. */ - constp = !!constp; - volatilep = !!volatilep; - - /* First search the chain variants for one that is what we want. */ - - for (t = m; t; t = TYPE_NEXT_VARIANT (t)) - if (constp == TREE_READONLY (t) - && volatilep == TREE_VOLATILE (t)) - return t; - - /* We need a new one. */ - current_obstack - = TREE_PERMANENT (type) ? &permanent_obstack : saveable_obstack; - - t = copy_node (type); - TREE_READONLY (t) = constp; - TREE_VOLATILE (t) = volatilep; - TYPE_POINTER_TO (t) = 0; - TYPE_REFERENCE_TO (t) = 0; - - /* Add this type to the chain of variants of TYPE. */ - TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (m); - TYPE_NEXT_VARIANT (m) = t; - - current_obstack = ambient_obstack; - return t; -} - -/* Hashing of types so that we don't make duplicates. - The entry point is `type_hash_canon'. */ - -/* Each hash table slot is a bucket containing a chain - of these structures. */ - -struct type_hash -{ - struct type_hash *next; /* Next structure in the bucket. */ - int hashcode; /* Hash code of this type. */ - tree type; /* The type recorded here. */ -}; - -/* Now here is the hash table. When recording a type, it is added - to the slot whose index is the hash code mod the table size. - Note that the hash table is used for several kinds of types - (function types, array types and array index range types, for now). - While all these live in the same table, they are completely independent, - and the hash code is computed differently for each of these. */ - -#define TYPE_HASH_SIZE 59 -struct type_hash *type_hash_table[TYPE_HASH_SIZE]; - -/* Here is how primitive or already-canonicalized types' hash - codes are made. */ -#define TYPE_HASH(TYPE) TREE_UID (TYPE) - -/* Compute a hash code for a list of types (chain of TREE_LIST nodes - with types in the TREE_VALUE slots), by adding the hash codes - of the individual types. */ - -int -type_hash_list (list) - tree list; -{ - register int hashcode; - register tree tail; - for (hashcode = 0, tail = list; tail; tail = TREE_CHAIN (tail)) - hashcode += TYPE_HASH (TREE_VALUE (tail)); - return hashcode; -} - -/* Look in the type hash table for a type isomorphic to TYPE. - If one is found, return it. Otherwise return 0. */ - -tree -type_hash_lookup (hashcode, type) - int hashcode; - tree type; -{ - register struct type_hash *h; - for (h = type_hash_table[hashcode % TYPE_HASH_SIZE]; h; h = h->next) - if (h->hashcode == hashcode - && TREE_CODE (h->type) == TREE_CODE (type) - && TREE_TYPE (h->type) == TREE_TYPE (type) - && (TYPE_MAX_VALUE (h->type) == TYPE_MAX_VALUE (type) - || tree_int_cst_equal (TYPE_MAX_VALUE (h->type), - TYPE_MAX_VALUE (type))) - && (TYPE_MIN_VALUE (h->type) == TYPE_MIN_VALUE (type) - || tree_int_cst_equal (TYPE_MIN_VALUE (h->type), - TYPE_MIN_VALUE (type))) - && (TYPE_DOMAIN (h->type) == TYPE_DOMAIN (type) - || (TREE_CODE (TYPE_DOMAIN (h->type)) == TREE_LIST - && TREE_CODE (TYPE_DOMAIN (type)) == TREE_LIST - && type_list_equal (TYPE_DOMAIN (h->type), TYPE_DOMAIN (type))))) - return h->type; - return 0; -} - -/* Add an entry to the type-hash-table - for a type TYPE whose hash code is HASHCODE. */ - -void -type_hash_add (hashcode, type) - int hashcode; - tree type; -{ - register struct type_hash *h; - - h = (struct type_hash *) oballoc (sizeof (struct type_hash)); - h->hashcode = hashcode; - h->type = type; - h->next = type_hash_table[hashcode % TYPE_HASH_SIZE]; - type_hash_table[hashcode % TYPE_HASH_SIZE] = h; -} - -/* Given TYPE, and HASHCODE its hash code, return the canonical - object for an identical type if one already exists. - Otherwise, return TYPE, and record it as the canonical object - if it is a permanent object. - - To use this function, first create a type of the sort you want. - Then compute its hash code from the fields of the type that - make it different from other similar types. - Then call this function and use the value. - This function frees the type you pass in if it is a duplicate. */ - -/* Set to 1 to debug without canonicalization. Never set by program. */ -int debug_no_type_hash = 0; - -tree -type_hash_canon (hashcode, type) - int hashcode; - tree type; -{ - tree t1; - - if (debug_no_type_hash) - return type; - - t1 = type_hash_lookup (hashcode, type); - if (t1 != 0) - { - struct obstack *o - = TREE_PERMANENT (type) ? &permanent_obstack : saveable_obstack; - obstack_free (o, type); - return t1; - } - - /* If this is a new type, record it for later reuse. */ - if (current_obstack == &permanent_obstack) - type_hash_add (hashcode, type); - - return type; -} - -/* Given two lists of types - (chains of TREE_LIST nodes with types in the TREE_VALUE slots) - return 1 if the lists contain the same types in the same order. - Also, the TREE_PURPOSEs must match. */ - -int -type_list_equal (l1, l2) - tree l1, l2; -{ - register tree t1, t2; - for (t1 = l1, t2 = l2; t1 && t2; t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2)) - { - if (TREE_VALUE (t1) != TREE_VALUE (t2)) - return 0; - if (TREE_PURPOSE (t1) != TREE_PURPOSE (t2) - && !simple_cst_equal (TREE_PURPOSE (t1), TREE_PURPOSE (t2))) - return 0; - } - - return t1 == t2; -} - -/* Nonzero if integer constants T1 and T2 - represent the same constant value. */ - -int -tree_int_cst_equal (t1, t2) - tree t1, t2; -{ - if (t1 == t2) - return 1; - if (t1 == 0 || t2 == 0) - return 0; - if (TREE_CODE (t1) == INTEGER_CST - && TREE_CODE (t2) == INTEGER_CST - && TREE_INT_CST_LOW (t1) == TREE_INT_CST_LOW (t2) - && TREE_INT_CST_HIGH (t1) == TREE_INT_CST_HIGH (t2)) - return 1; - return 0; -} - -/* Nonzero if integer constants T1 and T2 represent values that satisfy <. - The precise way of comparison depends on their data type. */ - -int -tree_int_cst_lt (t1, t2) - tree t1, t2; -{ - if (t1 == t2) - return 0; - - if (!TREE_UNSIGNED (TREE_TYPE (t1))) - return INT_CST_LT (t1, t2); - return INT_CST_LT_UNSIGNED (t1, t2); -} - -/* Compare two constructor-element-type constants. */ - -int -simple_cst_equal (t1, t2) - tree t1, t2; -{ - register enum tree_code code1, code2; - - if (t1 == t2) - return 1; - if (t1 == 0 || t2 == 0) - return 0; - - code1 = TREE_CODE (t1); - code2 = TREE_CODE (t2); - - if (code1 == NOP_EXPR || code1 == CONVERT_EXPR) - if (code2 == NOP_EXPR || code2 == CONVERT_EXPR) - return simple_cst_equal (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0)); - else - return simple_cst_equal (TREE_OPERAND (t1, 0), t2); - else if (code2 == NOP_EXPR || code2 == CONVERT_EXPR) - return simple_cst_equal (t1, TREE_OPERAND (t2, 0)); - - if (code1 != code2) - return 0; - - switch (code1) - { - case INTEGER_CST: - return TREE_INT_CST_LOW (t1) == TREE_INT_CST_LOW (t2) - && TREE_INT_CST_HIGH (t1) == TREE_INT_CST_HIGH (t2); - - case REAL_CST: - return REAL_VALUES_EQUAL (TREE_REAL_CST (t1), TREE_REAL_CST (t2)); - - case STRING_CST: - return TREE_STRING_LENGTH (t1) == TREE_STRING_LENGTH (t2) - && !strcmp (TREE_STRING_POINTER (t1), TREE_STRING_POINTER (t2)); - - case CONSTRUCTOR: - abort (); - - case VAR_DECL: - case PARM_DECL: - case CONST_DECL: - return 0; - - case PLUS_EXPR: - case MINUS_EXPR: - case MULT_EXPR: - case TRUNC_DIV_EXPR: - case TRUNC_MOD_EXPR: - case LSHIFT_EXPR: - case RSHIFT_EXPR: - return (simple_cst_equal (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0)) - && simple_cst_equal (TREE_OPERAND (t1, 1), TREE_OPERAND (t2, 1))); - - case NEGATE_EXPR: - case ADDR_EXPR: - case REFERENCE_EXPR: - case INDIRECT_REF: - return simple_cst_equal (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0)); - - default: - abort (); - } -} - -/* Constructors for pointer, array and function types. - (RECORD_TYPE, UNION_TYPE and ENUMERAL_TYPE nodes are - constructed by language-dependent code, not here.) */ - -/* Construct, lay out and return the type of pointers to TO_TYPE. - If such a type has already been constructed, reuse it. */ - -tree -build_pointer_type (to_type) - tree to_type; -{ - register tree t = TYPE_POINTER_TO (to_type); - register struct obstack *ambient_obstack = current_obstack; - register struct obstack *ambient_saveable_obstack = saveable_obstack; - - /* First, if we already have a type for pointers to TO_TYPE, use it. */ - - if (t) - return t; - - /* We need a new one. If TO_TYPE is permanent, make this permanent too. */ - if (TREE_PERMANENT (to_type)) - { - current_obstack = &permanent_obstack; - saveable_obstack = &permanent_obstack; - } - - t = make_node (POINTER_TYPE); - TREE_TYPE (t) = to_type; - - /* Record this type as the pointer to TO_TYPE. */ - TYPE_POINTER_TO (to_type) = t; - - /* Lay out the type. This function has many callers that are concerned - with expression-construction, and this simplifies them all. - Also, it guarantees the TYPE_SIZE is permanent if the type is. */ - layout_type (t); - - current_obstack = ambient_obstack; - saveable_obstack = ambient_saveable_obstack; - return t; -} - -/* Create a type of integers to be the TYPE_DOMAIN of an ARRAY_TYPE. - MAXVAL should be the maximum value in the domain - (one less than the length of the array). */ - -tree -build_index_type (maxval) - tree maxval; -{ - register tree itype = make_node (INTEGER_TYPE); - TYPE_PRECISION (itype) = BITS_PER_WORD; - TYPE_MIN_VALUE (itype) = build_int_2 (0, 0); - TREE_TYPE (TYPE_MIN_VALUE (itype)) = sizetype; - TYPE_MAX_VALUE (itype) = convert (sizetype, maxval); - TYPE_MODE (itype) = SImode; - TYPE_SIZE (itype) = TYPE_SIZE (sizetype); - TYPE_SIZE_UNIT (itype) = TYPE_SIZE_UNIT (sizetype); - TYPE_ALIGN (itype) = TYPE_ALIGN (sizetype); - if (TREE_CODE (maxval) == INTEGER_CST) - { - int maxint = TREE_INT_CST_LOW (maxval); - return type_hash_canon (maxint > 0 ? maxint : - maxint, itype); - } - else - return itype; -} - -/* Construct, lay out and return the type of arrays of elements with ELT_TYPE - and number of elements specified by the range of values of INDEX_TYPE. - If such a type has already been constructed, reuse it. */ - -tree -build_array_type (elt_type, index_type) - tree elt_type, index_type; -{ - register tree t = make_node (ARRAY_TYPE); - int hashcode; - - if (TREE_CODE (elt_type) == FUNCTION_TYPE) - { - error ("arrays of functions are not meaningful"); - elt_type = integer_type_node; - } - - TREE_TYPE (t) = elt_type; - TYPE_DOMAIN (t) = index_type; - - /* Make sure TYPE_POINTER_TO (elt_type) is filled in. */ - build_pointer_type (elt_type); -#if 0 - /* Also that of the main variant, which is the type the element will have. */ - build_pointer_type (TYPE_MAIN_VARIANT (elt_type)); -#endif - - if (index_type == 0) - return t; - - hashcode = TYPE_HASH (elt_type) + TYPE_HASH (index_type); - t = type_hash_canon (hashcode, t); - - if (TYPE_SIZE (t) == 0) - layout_type (t); - return t; -} - -/* Construct, lay out and return - the type of functions returning type VALUE_TYPE - given arguments of types ARG_TYPES. - ARG_TYPES is a chain of TREE_LIST nodes whose TREE_VALUEs - are data type nodes for the arguments of the function. - If such a type has already been constructed, reuse it. */ - -tree -build_function_type (value_type, arg_types) - tree value_type, arg_types; -{ - register tree t; - int hashcode; - - if (TREE_CODE (value_type) == FUNCTION_TYPE - || TREE_CODE (value_type) == ARRAY_TYPE) - { - error ("function return type cannot be function or array"); - value_type = integer_type_node; - } - - /* Make a node of the sort we want. */ - t = make_node (FUNCTION_TYPE); - TREE_TYPE (t) = value_type; - TYPE_ARG_TYPES (t) = arg_types; - - /* If we already have such a type, use the old one and free this one. */ - hashcode = TYPE_HASH (value_type) + type_hash_list (arg_types); - t = type_hash_canon (hashcode, t); - - if (TYPE_SIZE (t) == 0) - layout_type (t); - return t; -} - -/* Build the node for the type of references-to-TO_TYPE. */ - -tree -build_reference_type (to_type) - tree to_type; -{ - register tree t = TYPE_REFERENCE_TO (to_type); - register struct obstack *ambient_obstack = current_obstack; - register struct obstack *ambient_saveable_obstack = saveable_obstack; - - /* First, if we already have a type for pointers to TO_TYPE, use it. */ - - if (t) - return t; - - /* We need a new one. If TO_TYPE is permanent, make this permanent too. */ - if (TREE_PERMANENT (to_type)) - { - current_obstack = &permanent_obstack; - saveable_obstack = &permanent_obstack; - } - - t = make_node (REFERENCE_TYPE); - TREE_TYPE (t) = to_type; - - /* Record this type as the pointer to TO_TYPE. */ - TYPE_REFERENCE_TO (to_type) = t; - - layout_type (t); - - current_obstack = ambient_obstack; - saveable_obstack = ambient_saveable_obstack; - return t; -} - -/* Construct, lay out and return the type of methods belonging to class - BASETYPE and whose arguments and values are described by TYPE. - If that type exists already, reuse it. - TYPE must be a FUNCTION_TYPE node. */ - -tree -build_method_type (basetype, type) - tree basetype, type; -{ - register tree t; - int hashcode; - - /* Make a node of the sort we want. */ - t = make_node (METHOD_TYPE); - - if (TREE_CODE (type) != FUNCTION_TYPE) - abort (); - - TYPE_METHOD_BASETYPE (t) = basetype; - TREE_TYPE (t) = TREE_TYPE (type); - - /* The actual arglist for this function includes a "hidden" argument - which is "this". Put it into the list of argument types. */ - - TYPE_ARG_TYPES (t) - = tree_cons (NULL, build_pointer_type (basetype), TYPE_ARG_TYPES (type)); - - /* If we already have such a type, use the old one and free this one. */ - hashcode = TYPE_HASH (basetype) + TYPE_HASH (type); - t = type_hash_canon (hashcode, t); - - if (TYPE_SIZE (t) == 0) - layout_type (t); - - return t; -} - -/* Construct, lay out and return the type of methods belonging to class - BASETYPE and whose arguments and values are described by TYPE. - If that type exists already, reuse it. - TYPE must be a FUNCTION_TYPE node. */ - -tree -build_offset_type (basetype, type) - tree basetype, type; -{ - register tree t; - int hashcode; - - /* Make a node of the sort we want. */ - t = make_node (OFFSET_TYPE); - - TYPE_OFFSET_BASETYPE (t) = basetype; - TREE_TYPE (t) = type; - - /* If we already have such a type, use the old one and free this one. */ - hashcode = TYPE_HASH (basetype) + TYPE_HASH (type); - t = type_hash_canon (hashcode, t); - - if (TYPE_SIZE (t) == 0) - layout_type (t); - - return t; -} - -/* Return OP, stripped of any conversions to wider types as much as is safe. - Converting the value back to OP's type makes a value equivalent to OP. - - If FOR_TYPE is nonzero, we return a value which, if converted to - type FOR_TYPE, would be equivalent to converting OP to type FOR_TYPE. - - If FOR_TYPE is nonzero, unaligned bit-field references may be changed to the - narrowest type that can hold the value, even if they don't exactly fit. - Otherwise, bit-field references are changed to a narrower type - only if they can be fetched directly from memory in that type. - - OP must have integer, real or enumeral type. Pointers are not allowed! - - There are some cases where the obvious value we could return - would regenerate to OP if converted to OP's type, - but would not extend like OP to wider types. - If FOR_TYPE indicates such extension is contemplated, we eschew such values. - For example, if OP is (unsigned short)(signed char)-1, - we avoid returning (signed char)-1 if FOR_TYPE is int, - even though extending that to an unsigned short would regenerate OP, - since the result of extending (signed char)-1 to (int) - is different from (int) OP. */ - -tree -get_unwidened (op, for_type) - register tree op; - tree for_type; -{ - /* Set UNS initially if converting OP to FOR_TYPE is a zero-extension. */ - /* TYPE_PRECISION is safe in place of type_precision since - pointer types are not allowed. */ - register tree type = TREE_TYPE (op); - register int final_prec = TYPE_PRECISION (for_type != 0 ? for_type : type); - register int uns - = (for_type != 0 && for_type != type - && final_prec > TYPE_PRECISION (type) - && TREE_UNSIGNED (type)); - register tree win = op; - - while (TREE_CODE (op) == NOP_EXPR) - { - register int bitschange - = TYPE_PRECISION (TREE_TYPE (op)) - - TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (op, 0))); - - /* Truncations are many-one so cannot be removed. - Unless we are later going to truncate down even farther. */ - if (bitschange < 0 - && final_prec > TYPE_PRECISION (TREE_TYPE (op))) - break; - - /* See what's inside this conversion. If we decide to strip it, - we will set WIN. */ - op = TREE_OPERAND (op, 0); - - /* If we have not stripped any zero-extensions (uns is 0), - we can strip any kind of extension. - If we have previously stripped a zero-extension, - only zero-extensions can safely be stripped. - Any extension can be stripped if the bits it would produce - are all going to be discarded later by truncating to FOR_TYPE. */ - - if (bitschange > 0) - { - if (! uns || final_prec <= TYPE_PRECISION (TREE_TYPE (op))) - win = op; - /* TREE_UNSIGNED says whether this is a zero-extension. - Let's avoid computing it if it does not affect WIN - and if UNS will not be needed again. */ - if ((uns || TREE_CODE (op) == NOP_EXPR) - && TREE_UNSIGNED (TREE_TYPE (op))) - { - uns = 1; - win = op; - } - } - } - - if (TREE_CODE (op) == COMPONENT_REF - /* Since type_for_size always gives an integer type. */ - && TREE_CODE (type) != REAL_TYPE) - { - int innerprec = (TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (op, 1))) - * DECL_SIZE_UNIT (TREE_OPERAND (op, 1))); - type = type_for_size (innerprec, TREE_UNSIGNED (TREE_OPERAND (op, 1))); - - /* We can get this structure field in the narrowest type it fits in. - If FOR_TYPE is 0, do this only for a field that matches the - narrower type exactly and is aligned for it (i.e. mode isn't BI). - The resulting extension to its nominal type (a fullword type) - must fit the same conditions as for other extensions. */ - - if (innerprec < TYPE_PRECISION (TREE_TYPE (op)) - && (for_type || DECL_MODE (TREE_OPERAND (op, 1)) != BImode) - && (! uns || final_prec <= innerprec - || TREE_UNSIGNED (TREE_OPERAND (op, 1))) - && type != 0) - { - win = build (COMPONENT_REF, type, TREE_OPERAND (op, 0), - TREE_OPERAND (op, 1)); - TREE_VOLATILE (win) = TREE_VOLATILE (op); - TREE_THIS_VOLATILE (win) = TREE_THIS_VOLATILE (op); - } - } - return win; -} - -/* Return OP or a simpler expression for a narrower value - which can be sign-extended or zero-extended to give back OP. - Store in *UNSIGNEDP_PTR either 1 if the value should be zero-extended - or 0 if the value should be sign-extended. */ - -tree -get_narrower (op, unsignedp_ptr) - register tree op; - int *unsignedp_ptr; -{ - register int uns = 0; - int first = 1; - register tree win = op; - - while (TREE_CODE (op) == NOP_EXPR) - { - register int bitschange - = TYPE_PRECISION (TREE_TYPE (op)) - - TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (op, 0))); - - /* Truncations are many-one so cannot be removed. */ - if (bitschange < 0) - break; - - /* See what's inside this conversion. If we decide to strip it, - we will set WIN. */ - op = TREE_OPERAND (op, 0); - - if (bitschange > 0) - { - /* An extension: the outermost one can be stripped, - but remember whether it is zero or sign extension. */ - if (first) - uns = TREE_UNSIGNED (TREE_TYPE (op)); - /* Otherwise, if a sign extension has been stripped, - only sign extensions can now be stripped; - if a zero extension has been stripped, only zero-extensions. */ - else if (uns != TREE_UNSIGNED (TREE_TYPE (op))) - break; - first = 0; - } - /* A change in nominal type can always be stripped. */ - - win = op; - } - - if (TREE_CODE (op) == COMPONENT_REF - /* Since type_for_size always gives an integer type. */ - && TREE_CODE (TREE_TYPE (op)) != REAL_TYPE) - { - int innerprec = (TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (op, 1))) - * DECL_SIZE_UNIT (TREE_OPERAND (op, 1))); - tree type = type_for_size (innerprec, TREE_UNSIGNED (op)); - - /* We can get this structure field in a narrower type that fits it, - but the resulting extension to its nominal type (a fullword type) - must satisfy the same conditions as for other extensions. - - Do this only for fields that are aligned (not BImode), - because when bit-field insns will be used there is no - advantage in doing this. */ - - if (innerprec < TYPE_PRECISION (TREE_TYPE (op)) - && DECL_MODE (TREE_OPERAND (op, 1)) != BImode - && (first || uns == TREE_UNSIGNED (TREE_OPERAND (op, 1))) - && type != 0) - { - if (first) - uns = TREE_UNSIGNED (TREE_OPERAND (op, 1)); - win = build (COMPONENT_REF, type, TREE_OPERAND (op, 0), - TREE_OPERAND (op, 1)); - TREE_VOLATILE (win) = TREE_VOLATILE (op); - TREE_THIS_VOLATILE (win) = TREE_THIS_VOLATILE (op); - } - } - *unsignedp_ptr = uns; - return win; -} - -/* Return the precision of a type, for arithmetic purposes. - Supports all types on which arithmetic is possible - (including pointer types). - It's not clear yet what will be right for complex types. */ - -int -type_precision (type) - register tree type; -{ - return ((TREE_CODE (type) == INTEGER_TYPE - || TREE_CODE (type) == ENUMERAL_TYPE - || TREE_CODE (type) == REAL_TYPE) - ? TYPE_PRECISION (type) : POINTER_SIZE); -} - -/* Nonzero if integer constant C has a value that is permissible - for type TYPE (an INTEGER_TYPE). */ - -int -int_fits_type_p (c, type) - tree c, type; -{ - if (TREE_UNSIGNED (type)) - return (!INT_CST_LT_UNSIGNED (TYPE_MAX_VALUE (type), c) - && !INT_CST_LT_UNSIGNED (c, TYPE_MIN_VALUE (type))); - else - return (!INT_CST_LT (TYPE_MAX_VALUE (type), c) - && !INT_CST_LT (c, TYPE_MIN_VALUE (type))); -} diff --git a/gnu/usr.bin/gcc1/cc1/tree.def b/gnu/usr.bin/gcc1/cc1/tree.def deleted file mode 100644 index c20c4614dd..0000000000 --- a/gnu/usr.bin/gcc1/cc1/tree.def +++ /dev/null @@ -1,619 +0,0 @@ -/* This file contains the definitions and documentation for the - tree codes used in the GNU C compiler. - Copyright (C) 1987, 1988 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* The third argument can be: - "x" for an exceptional code (fits no category). - "s" for a statement code. - "t" for a type object code. - - "c" for codes for constants. - "d" for codes for declarations (also serving as variable refs). - "r" for codes for references to storage. - "e" for codes for other kinds of expressions. */ - -/* For `r', `e' and `x' nodes, - the 4th element is the number of argument slots to allocate. - This determines the size of the tree node object. */ - -/* Any erroneous construct is parsed into a node of this type. - This type of node is accepted without complaint in all contexts - by later parsing activities, to avoid multiple error messages - for one error. - No fields in these nodes are used except the TREE_CODE. */ -DEFTREECODE (ERROR_MARK, "error_mark", "x", 0) - -/* Used to represent a name (such as, in the DECL_NAME of a decl node). - Internally it looks like a STRING_CST node. - There is only one IDENTIFIER_NODE ever made for any particular name. - Use `get_identifier' to get it (or create it, the first time). */ -DEFTREECODE (IDENTIFIER_NODE, "identifier_node", "x", 7) - -/* Used to hold information to identify an operator (or combination - of two operators) considered as a `noun' rather than a `verb'. - The first operand is encoded in the TREE_TYPE field. */ -DEFTREECODE (OP_IDENTIFIER, "op_identifier", "x", 2) - -/* Has the TREE_VALUE and TREE_PURPOSE fields. */ -/* These nodes are made into lists by chaining through the - TREE_CHAIN field. The elements of the list live in the - TREE_VALUE fields, while TREE_PURPOSE fields are occasionally - used as well to get the effect of Lisp association lists. */ -DEFTREECODE (TREE_LIST, "tree_list", "x", 2) - -/* Each data type is represented by a tree node whose code is one of - the following: */ -/* Each node that represents a data type has a component TYPE_SIZE - containing a tree that is an expression for the size in some units. - The TYPE_SIZE_UNIT component is the number of bits in a unit. - The TYPE_MODE contains the machine mode for values of this type. - The TYPE_POINTER_TO field contains a type for a pointer to this type, - or zero if no such has been created yet. - The TYPE_NEXT_VARIANT field is used to chain together types - that are variants made by type modifiers such as "const" and "volatile". - The TYPE_MAIN_VARIANT field, in any member of such a chain, - points to the start of the chain. - The TYPE_NONCOPIED_PARTS field is a list specifying which parts - of an object of this type should *not* be copied by assignment. - The TREE_PURPOSE of each element is the offset of the part - and the TREE_VALUE is the size in bits of the part. - The TYPE_NAME field contains info on the name used in the program - for this type (for GDB symbol table output). It is either a - TYPE_DECL node, for types that are typedefs, or an IDENTIFIER_NODE - in the case of structs, unions or enums that are known with a tag, - or zero for types that have no special name. */ -/* The TREE_CHAIN of a ..._TYPE node is normally used to put - every type onto permanent_type_chain or temporary_type_chain (see tree.c). - One exception is for ENUMERAL_TYPE, RECORD_TYPE and UNION_TYPE - nodes used as forward-references to names; see below. */ - -DEFTREECODE (VOID_TYPE, "void_type", "t", 0) /* The void type in C */ - -/* Integer types in all languages, including char in C. */ -/* Has components TYPE_MIN_VALUE, TYPE_MAX_VALUE (expressions, inclusive) - and TYPE_PRECISION (number of bits used by this type). - In the case of a subrange type in Pascal, the TREE_TYPE - of this will point at the supertype (another INTEGER_TYPE). - Otherwise, the TREE_TYPE is zero. */ -DEFTREECODE (INTEGER_TYPE, "integer_type", "t", 0) - -/* C's float and double. Different floating types are distinguished - by machine mode and by the TYPE_SIZE and the TYPE_PRECISION. */ -DEFTREECODE (REAL_TYPE, "real_type", "t", 0) - -/* Complex number types. The TREE_TYPE field is the data type - of the real and imaginary parts. */ -DEFTREECODE (COMPLEX_TYPE, "complex_type", "t", 0) - -/* C enums. The type node looks just like an INTEGER_TYPE node. - The symbols for the values of the enum type are defined by - CONST_DECL nodes, but the type does not point to them; - however, the TREE_VALUES is a list in which each elements' TREE_PURPOSE - is a name and the TREE_VALUE is the value (an INTEGER_CST node). */ -/* A forward reference `enum foo' when no enum named foo is defined yet - has zero (a null pointer) in its TYPE_SIZE. The tag name is in - the TYPE_NAME field. If the type is later defined, the normal - fields are filled in. - RECORD_TYPE and UNION_TYPE forward refs are treated similarly. */ -DEFTREECODE (ENUMERAL_TYPE, "enumeral_type", "t", 0) - -/* Pascal's boolean type (true or false are the only values); - no special fields needed. */ -DEFTREECODE (BOOLEAN_TYPE, "boolean_type", "t", 0) - -/* CHAR in Pascal; not used in C. - No special fields needed. */ -DEFTREECODE (CHAR_TYPE, "char_type", "t", 0) - -/* All pointer-to-x types have code POINTER_TYPE. - The TREE_TYPE points to the node for the type pointed to. */ -DEFTREECODE (POINTER_TYPE, "pointer_type", "t", 0) - -/* An offset is a pointer relative to an object. - The TREE_TYPE field is the type of the object at the offset. - The TYPE_OFFSET_BASETYPE points to the node for the type of object - that the offset is relative to. */ -DEFTREECODE (OFFSET_TYPE, "offset_type", "t", 0) - -/* A reference is like a pointer except that it is coerced - automatically to the value it points to. Used in C++. */ -DEFTREECODE (REFERENCE_TYPE, "reference_type", "t", 0) - -/* METHOD_TYPE is the type of a function which takes an extra first - argument for "self", which is not present in the declared argument list. - The TREE_TYPE is the return type of the method. The TYPE_METHOD_BASETYPE - is the type of "self". TYPE_ARG_TYPES is the real argument list, which - includes the hidden argument for "self". */ -DEFTREECODE (METHOD_TYPE, "method_type", "t", 0) - -/* Used for Pascal; details not determined right now. */ -DEFTREECODE (FILE_TYPE, "file_type", "t", 0) - -/* Types of arrays. Special fields: - TREE_TYPE Type of an array element. - TYPE_DOMAIN Type to index by. - Its range of values specifies the array length. - TYPE_SEP Expression for units from one elt to the next. - TYPE_SEP_UNIT Number of bits in a unit for previous. - The field TYPE_POINTER_TO (TREE_TYPE (array_type)) is always nonzero - and holds the type to coerce a value of that array type to in C. */ -/* Array types in C or Pascal */ -DEFTREECODE (ARRAY_TYPE, "array_type", "t", 0) - -/* Types of sets for Pascal. Special fields are the same as - in an array type. The target type is always a boolean type. */ -DEFTREECODE (SET_TYPE, "set_type", "t", 0) - -/* Not known whether Pascal really needs this - or what it should contain. */ -DEFTREECODE (STRING_TYPE, "string_type", "t", 0) - -/* Struct in C, or record in Pascal. */ -/* Special fields: - TYPE_FIELDS chain of FIELD_DECLs for the fields of the struct. - A few may need to be added for Pascal. */ -/* See the comment above, before ENUMERAL_TYPE, for how - forward references to struct tags are handled in C. */ -DEFTREECODE (RECORD_TYPE, "record_type", "t", 0) - -/* Union in C. Like a struct, except that the offsets of the fields - will all be zero. */ -/* See the comment above, before ENUMERAL_TYPE, for how - forward references to union tags are handled in C. */ -DEFTREECODE (UNION_TYPE, "union_type", "t", 0) /* C union type */ - -/* Type of functions. Special fields: - TREE_TYPE type of value returned. - TYPE_ARG_TYPES list of types of arguments expected. - this list is made of TREE_LIST nodes. - Types of "Procedures" in languages where they are different from functions - have code FUNCTION_TYPE also, but then TREE_TYPE is zero or void type. */ -DEFTREECODE (FUNCTION_TYPE, "function_type", "t", 0) - -/* This is a language-specific kind of type. - Its meaning is defined by the language front end. - layout_type does not know how to lay this out, - so the front-end must do so manually. */ -DEFTREECODE (LANG_TYPE, "lang_type", "t", 0) - -/* All statement types have fields STMT_SOURCE_FILE and STMT_SOURCE_LINE. */ -/* Consecutive statements within a compound statement are chained together - through the TREE_CHAIN field. */ - -/* A label definition, encapsulated as a statement. - STMT_BODY is the LABEL_DECL node for the label that appears here. */ -DEFTREECODE (LABEL_STMT, "label_stmt", "s", 1) - -/* GOTO, in any language. STMT_BODY is a LABEL_DECL node. */ -DEFTREECODE (GOTO_STMT, "goto_stmt", "s", 1) - -/* RETURN, in any language. - Evaluates the expression STMT_BODY, then returns from the current function. - Presumably STMT_BODY is an assignment that stores into the - RESULT_DECL that hold the value to be returned. - STMT_BODY may be zero. */ -DEFTREECODE (RETURN_STMT, "return_stmt", "s", 1) - -/* Statement that evaluates an expression. STMT_BODY is the expression. */ -DEFTREECODE (EXPR_STMT, "expr_stmt", "s", 1) - -/* Pascal WITH statement. - Contains a chain of variables (..._DECL nodes) in the STMT_VARS - and a chain of statements (the STMT_BODY). - STMT_SUPERCONTEXT points to the containing declaration scope. */ -DEFTREECODE (WITH_STMT, "with_stmt", "s", 5) - -/* Declare variables whose scope is less than a function. - This is used for C brace-pairs that contain declarations. - Contains a chain of variables (..._DECL nodes) in the STMT_VARS - and a chain of statements (the STMT_BODY). - STMT_SUPERCONTEXT points to the containing declaration scope. - STMT_BIND_SIZE is an expression for the size of local storage here. - STMT_TYPE_TAGS is a list (chain of TREE_LIST nodes) - pairing struct, union and enum tag names with the types they mean, - for tags defined in this context. - - A LET_STMT can be used as an expression. Its STMT_BODY is expanded - in its stead. Its TREE_USED is set if it is expanded. - - A LET_STMT whose TREE_USED is not set is ignored when symbols - are output. If the LET_STMT is passed to expand_expr but it - should not be ignored, set its TREE_USED by hand. */ -DEFTREECODE (LET_STMT, "let_stmt", "s", 6) - -/* if-then-else statements in C and other languages. - STMT_COND is the condition (an expression). - STMT_THEN is the then-branch (a statement or chain of statements). - STMT_ELSE is the else-branch (a statement or chain of statements). */ -DEFTREECODE (IF_STMT, "if_stmt", "s", 3) - -/* if-else-exit; used in building parts of iterations. - STMT_BODY is the condition (an expression). - Exit if the iteration if the condition is FALSE. */ -DEFTREECODE (EXIT_STMT, "exit_stmt", "s", 1) - -/* STMT_CASE_INDEX is an expression for the value to dispatch on. - STMT_CASE_LIST is a list (a chain of TREE_LIST nodes) - of the branches of the dispatch. - Each such TREE_LIST node has the case it is for (a constant expression) - as the TREE_PURPOSE - and the label to go to (a LABEL_DECL) as the TREE_VALUE. - - Normally, the labels reside inside a COMPOUND_STMT - which contains ths CASE_STMT as its first statement. */ -DEFTREECODE (CASE_STMT, "case_stmt", "s", 3) - -/* STMT_BODY contains a chain of statements to be executed repeatedly. */ -DEFTREECODE (LOOP_STMT, "loop_stmt", "s", 1) - -/* Contains as its STMT_BODY a chain of substatements. */ -DEFTREECODE (COMPOUND_STMT, "compound_stmt", "s", 1) - -/* Contains as its STMT_BODY a string of assembly code. */ -DEFTREECODE (ASM_STMT, "asm_stmt", "s", 1) - -/* Expressions */ - -/* First, the constants. */ - -/* Contents are in TREE_INT_CST_LOW and TREE_INT_CST_HIGH fields, - 32 bits each, giving us a 64 bit constant capability. - Note: constants of type char in Pascal are INTEGER_CST, - and so are pointer constants such as nil in Pascal or NULL in C. - `(int *) 1' in C also results in an INTEGER_CST. */ -DEFTREECODE (INTEGER_CST, "integer_cst", "c", 2) - -/* Contents are in TREE_REAL_CST field. Also there is TREE_CST_RTL. */ -DEFTREECODE (REAL_CST, "real_cst", "c", 3) - -/* Contents are in TREE_REALPART and TREE_IMAGPART fields, - whose contents are other constant nodes. - Also there is TREE_CST_RTL. */ -DEFTREECODE (COMPLEX_CST, "complex_cst", "c", 3) - -/* Contents are TREE_STRING_LENGTH and TREE_STRING_POINTER fields. - Also there is TREE_CST_RTL. */ -DEFTREECODE (STRING_CST, "string_cst", "c", 3) - -/* Declarations. All references to names are represented as ..._DECL nodes. - The decls in one binding context are chained through the TREE_CHAIN field. - Each DECL has a DECL_NAME field which contains an IDENTIFIER_NODE. - (Some decls, most often labels, may have zero as the DECL_NAME). - DECL_CONTEXT points to the node representing the context in which - this declaration has its scope. For FIELD_DECLs, this is the - RECORD_TYPE or UNION_TYPE node that the field belongs to; - for other kinds of decl nodes, this is a LET_STMT node or the ROOT_NODE. - The TREE_TYPE field holds the data type of the object, when relevant. - LABEL_DECLs have no data type. For TYPE_DECL, the TREE_TYPE field - contents are the type whose name is being declared. - The DECL_ALIGN, DECL_SIZE, DECL_SIZE_UNIT - and DECL_MODE fields exist in decl nodes just as in type nodes. - They are unused in LABEL_DECL, TYPE_DECL and CONST_DECL nodes. - - DECL_OFFSET holds an integer number of bits offset for the location. - DECL_VOFFSET holds an expression for a variable offset; it is - to be multiplied by DECL_VOFFSET_UNIT (an integer). - These fields are relevant only in FIELD_DECLs and PARM_DECLs. - - DECL_INITIAL holds the value to initialize a variable to, - or the value of a constant. For a function, it holds the body - (a node of type LET_STMT representing the function's binding contour - and whose body contains the function's statements.) - - PARM_DECLs use a special field: - DECL_ARG_TYPE is the type in which the argument is actually - passed, which may be different from its type within the function. - - FUNCTION_DECLs use four special fields: - DECL_ARGUMENTS holds a chain of PARM_DECL nodes for the arguments. - DECL_RESULT holds a RESULT_DECL node for the value of a function, - or it is 0 for a function that returns no value. - (C functions returning void have zero here.) - DECL_RESULT_TYPE holds the type in which the result is actually - returned. This is usually the same as the type of DECL_RESULT, - but (1) it may be a wider integer type and - (2) it remains valid, for the sake of inlining, even after the - function's compilation is done. - DECL_FUNCTION_CODE is a code number that is nonzero for - built-in functions. Its value is an enum built_in_function - that says which built-in function it is. - DECL_BLOCK_SYMTAB_ADDRESS records (after the symtab data for the function's - body has been output) the address in the symtab file of the - `struct block' for the function's top-level binding context. - This must be stored in the symtab structure for the function name. - - DECL_SOURCE_FILE holds a filename string and DECL_SOURCE_LINE - holds a line number. */ - -DEFTREECODE (FUNCTION_DECL, "function_decl", "d", 0) -DEFTREECODE (LABEL_DECL, "label_decl", "d", 0) -DEFTREECODE (CONST_DECL, "const_decl", "d", 0) -DEFTREECODE (TYPE_DECL, "type_decl", "d", 0) -DEFTREECODE (VAR_DECL, "var_decl", "d", 0) -DEFTREECODE (PARM_DECL, "parm_decl", "d", 0) -DEFTREECODE (RESULT_DECL, "result_decl", "d", 0) -DEFTREECODE (FIELD_DECL, "field_decl", "d", 0) - -/* References to storage. */ - -/* Value is structure or union component. - Operand 0 is the structure or union (an expression); - operand 1 is the field (a node of type FIELD_DECL). */ -DEFTREECODE (COMPONENT_REF, "component_ref", "r", 2) - -/* C unary `*' or Pascal `^'. One operand, an expression for a pointer. */ -DEFTREECODE (INDIRECT_REF, "indirect_ref", "r", 1) - -/* Reference to the contents of an offset - (a value whose type is an OFFSET_TYPE). - Operand 0 is the object within which the offset is taken. - Operand 1 is the offset. */ -DEFTREECODE (OFFSET_REF, "offset_ref", "r", 2) - -/* Pascal `^` on a file. One operand, an expression for the file. */ -DEFTREECODE (BUFFER_REF, "buffer_ref", "r", 1) - -/* Array indexing in languages other than C. - Operand 0 is the array; operand 1 is a list of indices - stored as a chain of TREE_LIST nodes. */ -DEFTREECODE (ARRAY_REF, "array_ref", "r", 2) - -/* Constructor: return an aggregate value made from specified components. - In C, this is used only for structure and array initializers. - The first "operand" is really a pointer to the RTL, - for constant constructors only. - The second operand is a list of component values - made out of a chain of TREE_LIST nodes. */ -DEFTREECODE (CONSTRUCTOR, "constructor", "e", 2) - -/* The expression types are mostly straightforward, - with the fourth argument of DEFTREECODE saying - how many operands there are. - Unless otherwise specified, the operands are expressions. */ - -/* Contains two expressions to compute, one followed by the other. - the first value is ignored. The second one's value is used. */ -DEFTREECODE (COMPOUND_EXPR, "compound_expr", "e", 2) - -/* Assignment expression. Operand 0 is the what to set; 1, the new value. */ -DEFTREECODE (MODIFY_EXPR, "modify_expr", "e", 2) - -/* Initialization expression. Operand 0 is the variable to initialize; - Operand 1 is the initializer. */ -DEFTREECODE (INIT_EXPR, "init_expr", "e", 2) - -/* Use these for overloading `new' and `delete'. - ??? Please describe the meaning of the operands. */ -DEFTREECODE (NEW_EXPR, "new_expr", "e", 2) -DEFTREECODE (DELETE_EXPR, "delete_expr", "e", 2) - -/* Use these for providing abstract push and pop operations for wrappers. - Operand 0 is the abstract stack. Operand 1 is the value to push or pop. - ??? What kind of value is an "abstract stack"? */ -DEFTREECODE (PUSH_EXPR, "push_expr", "e", 2) -DEFTREECODE (POP_EXPR, "pop_expr", "e", 2) - -/* Conditional expression ( ... ? ... : ... in C). - Operand 0 is the condition. - Operand 1 is the then-value. - Operand 2 is the else-value. */ -DEFTREECODE (COND_EXPR, "cond_expr", "e", 3) - -/* Function call. Operand 0 is the function. - Operand 1 is the argument list, a list of expressions - made out of a chain of TREE_LIST nodes. - There is no operand 2. That slot is used for the - CALL_EXPR_RTL macro (see preexpand_calls). */ -DEFTREECODE (CALL_EXPR, "call_expr", "e", 3) - -/* Call a method. Operand 0 is the method, whose type is a METHOD_TYPE. - Operand 1 is the expression for "self". - Operand 2 is the list of explicit arguments. */ -DEFTREECODE (METHOD_CALL_EXPR, "method_call_expr", "e", 4) - -/* Specify a value to compute along with its corresponding cleanup. - Operand 0 argument is an expression whose value needs a cleanup. - Operand 1 is an RTL_EXPR which will eventually represent that value. - Operand 2 is the cleanup expression for the object. - The RTL_EXPR is used in this expression, which is how the expression - manages to act on the proper value. - The cleanup is executed when the value is no longer needed, - which is not at precisely the same time that this value is computed. */ -DEFTREECODE (WITH_CLEANUP_EXPR, "with_cleanup_expr", "e", 3) - -/* Simple arithmetic. Operands must have the same machine mode - and the value shares that mode. */ -DEFTREECODE (PLUS_EXPR, "plus_expr", "e", 2) -DEFTREECODE (MINUS_EXPR, "minus_expr", "e", 2) -DEFTREECODE (MULT_EXPR, "mult_expr", "e", 2) - -/* Division for integer result that rounds the quotient toward zero. */ -/* Operands must have the same machine mode. - In principle they may be real, but that is not currently supported. - The result is always fixed point, and it has the same type as the - operands if they are fixed point. */ -DEFTREECODE (TRUNC_DIV_EXPR, "trunc_div_expr", "e", 2) - -/* Division for integer result that rounds the quotient toward infinity. */ -DEFTREECODE (CEIL_DIV_EXPR, "ceil_div_expr", "e", 2) - -/* Division for integer result that rounds toward minus infinity. */ -DEFTREECODE (FLOOR_DIV_EXPR, "floor_div_expr", "e", 2) - -/* Division for integer result that rounds toward nearest integer. */ -DEFTREECODE (ROUND_DIV_EXPR, "round_div_expr", "e", 2) - -/* Four kinds of remainder that go with the four kinds of division. */ -DEFTREECODE (TRUNC_MOD_EXPR, "trunc_mod_expr", "e", 2) -DEFTREECODE (CEIL_MOD_EXPR, "ceil_mod_expr", "e", 2) -DEFTREECODE (FLOOR_MOD_EXPR, "floor_mod_expr", "e", 2) -DEFTREECODE (ROUND_MOD_EXPR, "round_mod_expr", "e", 2) - -/* Division for real result. The two operands must have the same type. - In principle they could be integers, but currently only real - operands are supported. The result must have the same type - as the operands. */ -DEFTREECODE (RDIV_EXPR, "rdiv_expr", "e", 2) - -/* Division which is not supposed to need rounding. - Used for pointer subtraction in C. */ -DEFTREECODE (EXACT_DIV_EXPR, "exact_div_expr", "e", 2) - -/* Conversion of real to fixed point: four ways to round, - like the four ways to divide. - CONVERT_EXPR can also be used to convert a real to an integer, - and that is what is used in languages that do not have ways of - specifying which of these is wanted. Maybe these are not needed. */ -DEFTREECODE (FIX_TRUNC_EXPR, "fix_trunc_expr", "e", 1) -DEFTREECODE (FIX_CEIL_EXPR, "fix_ceil_expr", "e", 1) -DEFTREECODE (FIX_FLOOR_EXPR, "fix_floor_expr", "e", 1) -DEFTREECODE (FIX_ROUND_EXPR, "fix_round_expr", "e", 1) - -/* Conversion of an integer to a real. */ -DEFTREECODE (FLOAT_EXPR, "float_expr", "e", 1) - -/* Exponentiation. Operands may have any types; - constraints on value type are not known yet. */ -DEFTREECODE (EXPON_EXPR, "expon_expr", "e", 2) - -/* Unary negation. Value has same type as operand. */ -DEFTREECODE (NEGATE_EXPR, "negate_expr", "e", 1) - -DEFTREECODE (MIN_EXPR, "min_expr", "e", 2) -DEFTREECODE (MAX_EXPR, "max_expr", "e", 2) -DEFTREECODE (ABS_EXPR, "abs_expr", "e", 1) -DEFTREECODE (FFS_EXPR, "ffs_expr", "e", 1) - -/* Shift operations for shift and rotate. - Shift is supposed to mean logical shift if done on an - unsigned type, arithmetic shift on a signed type. - The second operand is the number of bits to - shift by, and must always have mode SImode. - The result has the same mode as the first operand. */ -DEFTREECODE (LSHIFT_EXPR, "alshift_expr", "e", 2) -DEFTREECODE (RSHIFT_EXPR, "arshift_expr", "e", 2) -DEFTREECODE (LROTATE_EXPR, "lrotate_expr", "e", 2) -DEFTREECODE (RROTATE_EXPR, "rrotate_expr", "e", 2) - -/* Bitwise operations. Operands have same mode as result. */ -DEFTREECODE (BIT_IOR_EXPR, "bit_ior_expr", "e", 2) -DEFTREECODE (BIT_XOR_EXPR, "bit_xor_expr", "e", 2) -DEFTREECODE (BIT_AND_EXPR, "bit_and_expr", "e", 2) -DEFTREECODE (BIT_ANDTC_EXPR, "bit_andtc_expr", "e", 2) -DEFTREECODE (BIT_NOT_EXPR, "bit_not_expr", "e", 1) - -/* Combination of boolean values or of integers considered only - as zero or nonzero. ANDIF and ORIF allow the second operand - not to be computed if the value of the expression is determined - from the first operand. AND and OR always compute the second - operand whether its value is needed or not (for side effects). */ -DEFTREECODE (TRUTH_ANDIF_EXPR, "truth_andif_expr", "e", 2) -DEFTREECODE (TRUTH_ORIF_EXPR, "truth_orif_expr", "e", 2) -DEFTREECODE (TRUTH_AND_EXPR, "truth_and_expr", "e", 2) -DEFTREECODE (TRUTH_OR_EXPR, "truth_or_expr", "e", 2) -DEFTREECODE (TRUTH_NOT_EXPR, "truth_not_expr", "e", 1) - -/* Relational operators. - `EQ_EXPR' and `NE_EXPR' are allowed for any types. - The others are allowed only for integer (or pointer or enumeral) - or real types. - In all cases the operands will have the same type, - and the value is always the type used by the language for booleans. */ -DEFTREECODE (LT_EXPR, "lt_expr", "e", 2) -DEFTREECODE (LE_EXPR, "le_expr", "e", 2) -DEFTREECODE (GT_EXPR, "gt_expr", "e", 2) -DEFTREECODE (GE_EXPR, "ge_expr", "e", 2) -DEFTREECODE (EQ_EXPR, "eq_expr", "e", 2) -DEFTREECODE (NE_EXPR, "ne_expr", "e", 2) - -/* Operations for Pascal sets. Not used now. */ -DEFTREECODE (IN_EXPR, "in_expr", "e", 2) -DEFTREECODE (SET_LE_EXPR, "set_le_expr", "e", 2) -DEFTREECODE (CARD_EXPR, "card_expr", "e", 1) -DEFTREECODE (RANGE_EXPR, "range_expr", "e", 2) - -/* Represents a conversion of type of a value. - All conversions, including implicit ones, must be - represented by CONVERT_EXPR nodes. */ -DEFTREECODE (CONVERT_EXPR, "convert_expr", "e", 1) - -/* Represents a conversion expected to require no code to be generated. */ -DEFTREECODE (NOP_EXPR, "nop_expr", "e", 1) - -/* Represents something we computed once and will use multiple times. - First operand is that expression. Second is the RTL, - nonzero only after the expression has been computed. - TREE_UNSIGNED in a SAVE_EXPR is nonzero if that SAVE_EXPR - has been seen already in assign_vars_1. */ -DEFTREECODE (SAVE_EXPR, "save_expr", "e", 2) - -/* Represents something whose RTL has already been expanded - as a sequence which should be emitted when this expression is expanded. - The first operand is the RTL to emit. It is the first of a chain of insns. - The second is the RTL expression for the result. */ -DEFTREECODE (RTL_EXPR, "rtl_expr", "e", 2) - -/* & in C. Value is the address at which the operand's value resides. - Operand may have any mode. Result mode is Pmode. */ -DEFTREECODE (ADDR_EXPR, "addr_expr", "e", 1) - -/* Postfix & in C++. Value is a reference to the object which is the operand. - Operand type is preserved, but we know that it is no longer - an lvalue. */ -DEFTREECODE (REFERENCE_EXPR, "reference_expr", "e", 1) - -/* A wrapper in C++. Operand 0 is the type that the wrapper - belongs to (if non-virtual). Operand 1 is the function - being wrapped. An anti-wrapper means do not wrap the function - (if it would be wrapped by default). */ -DEFTREECODE (WRAPPER_EXPR, "wrapper_expr", "e", 2) -DEFTREECODE (ANTI_WRAPPER_EXPR, "anti_wrapper_expr", "e", 2) - -/* Operand is a function constant; result is a function variable value - of typeEPmode. Used only for languages that need static chains. */ -DEFTREECODE (ENTRY_VALUE_EXPR, "entry_value_expr", "e", 1) - -/* Given two real or integer operands of the same type, - returns a complex value of the corresponding complex type. */ -DEFTREECODE (COMPLEX_EXPR, "complex_expr", "e", 2) - -/* Complex conjugate of operand. Used only on complex types. - The value has the same type as the operand. */ -DEFTREECODE (CONJ_EXPR, "conj_expr", "e", 1) - -/* Used only on an operand of complex type, these return - a value of the corresponding component type. */ -DEFTREECODE (REALPART_EXPR, "realpart_expr", "e", 1) -DEFTREECODE (IMAGPART_EXPR, "imagpart_expr", "e", 1) - -/* Nodes for ++ and -- in C. - The second arg is how much to increment or decrement by. - For a pointer, it would be the size of the object pointed to. */ -DEFTREECODE (PREDECREMENT_EXPR, "predecrement_expr", "e", 2) -DEFTREECODE (PREINCREMENT_EXPR, "preincrement_expr", "e", 2) -DEFTREECODE (POSTDECREMENT_EXPR, "postdecrement_expr", "e", 2) -DEFTREECODE (POSTINCREMENT_EXPR, "postincrement_expr", "e", 2) - -/* -Local variables: -mode:c -version-control: t -End: -*/ diff --git a/gnu/usr.bin/gcc1/cc1/tree.h b/gnu/usr.bin/gcc1/cc1/tree.h deleted file mode 100644 index 6435039799..0000000000 --- a/gnu/usr.bin/gcc1/cc1/tree.h +++ /dev/null @@ -1,930 +0,0 @@ -/* Front-end tree definitions for GNU compiler. - Copyright (C) 1989 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* codes of tree nodes */ - -#define DEFTREECODE(SYM, STRING, TYPE, NARGS) SYM, - -enum tree_code { -#include "tree.def" - - LAST_AND_UNUSED_TREE_CODE /* A convienent way to get a value for - NUM_TREE_CODE. */ -}; - -#undef DEFTREECODE - -/* Number of tree codes. */ -#define NUM_TREE_CODES ((int)LAST_AND_UNUSED_TREE_CODE) - -/* Indexed by enum tree_code, contains a character which is - `e' for an expression, `r' for a reference, `c' for a constant, - `d' for a decl, `t' for a type, `s' for a statement, - and `x' for anything else (TREE_LIST, IDENTIFIER, etc). */ - -extern char *tree_code_type[]; - -/* Number of argument-words in each kind of tree-node. */ - -extern int tree_code_length[]; - -/* Get the definition of `enum machine_mode' */ - -#ifndef HAVE_MACHINE_MODES -#define DEF_MACHMODE(SYM, NAME, TYPE, SIZE, UNIT, WIDER) SYM, - -enum machine_mode { -#include "machmode.def" -MAX_MACHINE_MODE }; - -#undef DEF_MACHMODE - -#define HAVE_MACHINE_MODES - -#endif /* not HAVE_MACHINE_MODES */ - -#ifndef NUM_MACHINE_MODES -#define NUM_MACHINE_MODES (int) MAX_MACHINE_MODE -#endif - -/* Codes that identify the various built in functions - so that expand_call can identify them quickly. */ - -enum built_in_function -{ - NOT_BUILT_IN, - BUILT_IN_ALLOCA, - BUILT_IN_ABS, - BUILT_IN_FABS, - BUILT_IN_LABS, - BUILT_IN_FFS, - BUILT_IN_DIV, - BUILT_IN_LDIV, - BUILT_IN_FFLOOR, - BUILT_IN_FCEIL, - BUILT_IN_FMOD, - BUILT_IN_FREM, - BUILT_IN_MEMCPY, - BUILT_IN_MEMCMP, - BUILT_IN_MEMSET, - BUILT_IN_FSQRT, - BUILT_IN_GETEXP, - BUILT_IN_GETMAN, - BUILT_IN_SAVEREGS, - BUILT_IN_CLASSIFY_TYPE, - BUILT_IN_NEXT_ARG, - - /* C++ extensions */ - BUILT_IN_NEW, - BUILT_IN_VEC_NEW, - BUILT_IN_DELETE, - BUILT_IN_VEC_DELETE -}; - -/* The definition of tree nodes fills the next several pages. */ - -/* A tree node can represent a data type, a variable, an expression - or a statement. Each node has a TREE_CODE which says what kind of - thing it represents. Some common codes are: - INTEGER_TYPE -- represents a type of integers. - ARRAY_TYPE -- represents a type of pointer. - VAR_DECL -- represents a declared variable. - INTEGER_CST -- represents a constant integer value. - PLUS_EXPR -- represents a sum (an expression). - - As for the contents of a tree node: there are some fields - that all nodes share. Each TREE_CODE has various special-purpose - fields as well. The fields of a node are never accessed directly, - always through accessor macros. */ - -/* This type is used everywhere to refer to a tree node. */ - -typedef union tree_node *tree; - -#define NULL_TREE (tree) NULL - -/* Every kind of tree node starts with this structure, - so all nodes have these fields. - - See the accessor macros, defined below, for documentation of the fields. */ - -struct tree_common -{ - int uid; - union tree_node *chain; - union tree_node *type; - unsigned char code : 8; - - unsigned external_attr : 1; - unsigned public_attr : 1; - unsigned static_attr : 1; - unsigned volatile_attr : 1; - unsigned packed_attr : 1; - unsigned readonly_attr : 1; - unsigned literal_attr : 1; - unsigned nonlocal_attr : 1; - unsigned permanent_attr : 1; - unsigned addressable_attr : 1; - unsigned regdecl_attr : 1; - unsigned this_vol_attr : 1; - unsigned unsigned_attr : 1; - unsigned asm_written_attr: 1; - unsigned inline_attr : 1; - unsigned used_attr : 1; - unsigned lang_flag_1 : 1; - unsigned lang_flag_2 : 1; - unsigned lang_flag_3 : 1; - unsigned lang_flag_4 : 1; - /* There is room for four more attributes. */ -}; - -/* Define accessors for the fields that all tree nodes have - (though some fields are not used for all kinds of nodes). */ - -/* The unique id of a tree node distinguishes it from all other nodes - in the same compiler run. */ -#define TREE_UID(NODE) ((NODE)->common.uid) - -/* The tree-code says what kind of node it is. - Codes are defined in tree.def. */ -#define TREE_CODE(NODE) ((enum tree_code) (NODE)->common.code) -#define TREE_SET_CODE(NODE, VALUE) ((NODE)->common.code = (int) (VALUE)) - -/* In all nodes that are expressions, this is the data type of the expression. - In POINTER_TYPE nodes, this is the type that the pointer points to. - In ARRAY_TYPE nodes, this is the type of the elements. */ -#define TREE_TYPE(NODE) ((NODE)->common.type) - -/* Nodes are chained together for many purposes. - Types are chained together to record them for being output to the debugger - (see the function `chain_type'). - Decls in the same scope are chained together to record the contents - of the scope. - Statement nodes for successive statements used to be chained together. - Often lists of things are represented by TREE_LIST nodes that - are chained together. */ - -#define TREE_CHAIN(NODE) ((NODE)->common.chain) - -/* Define many boolean fields that all tree nodes have. */ - -/* In a VAR_DECL or FUNCTION_DECL, - nonzero means external reference: - do not allocate storage, and refer to a definition elsewhere. */ -#define TREE_EXTERNAL(NODE) ((NODE)->common.external_attr) - -/* In a VAR_DECL, nonzero means allocate static storage. - In a FUNCTION_DECL, currently nonzero if function has been defined. */ -#define TREE_STATIC(NODE) ((NODE)->common.static_attr) - -/* In a VAR_DECL or FUNCTION_DECL, - nonzero means name is to be accessible from outside this module. */ -#define TREE_PUBLIC(NODE) ((NODE)->common.public_attr) - -/* In VAR_DECL nodes, nonzero means address of this is needed. - So it cannot be in a register. - In a FUNCTION_DECL, nonzero means its address is needed. - So it must be compiled even if it is an inline function. - In CONSTRUCTOR nodes, it means the elements are all constants suitable - for output as assembly-language initializers. - In LABEL_DECL nodes, it means a goto for this label has been seen - from a place outside all binding contours that restore stack levels, - or that an error message about jumping into such a binding contour - has been printed for this label. - In ..._TYPE nodes, it means that objects of this type must - be fully addressable. This means that pieces of this - object cannot go into register parameters, for example. */ -#define TREE_ADDRESSABLE(NODE) ((NODE)->common.addressable_attr) - -/* In VAR_DECL nodes, nonzero means declared `register'. */ -#define TREE_REGDECL(NODE) ((NODE)->common.regdecl_attr) - -/* In any expression, nonzero means it has side effects or reevaluation - of the whole expression could produce a different value. - This is set if any subexpression is a function call, a side effect - or a reference to a volatile variable. - In a ..._DECL, this is set only if the declaration said `volatile'. - In a ..._TYPE, nonzero means the type is volatile-qualified. */ -#define TREE_VOLATILE(NODE) ((NODE)->common.volatile_attr) - -/* Nonzero means this expression is volatile in the C sense: - its address should be of type `volatile WHATEVER *'. - If this bit is set, so is `volatile_attr'. */ -#define TREE_THIS_VOLATILE(NODE) ((NODE)->common.this_vol_attr) - -/* In a VAR_DECL, PARM_DECL or FIELD_DECL, or any kind of ..._REF node, - nonzero means it may not be the lhs of an assignment. - In a ..._TYPE node, means this type is const-qualified. */ -#define TREE_READONLY(NODE) ((NODE)->common.readonly_attr) - -/* Nonzero in a FIELD_DECL means it is a bit-field; it may occupy - less than a storage unit, and its address may not be taken, etc. - This controls layout of the containing record. - In a LABEL_DECL, nonzero means label was defined inside a binding - contour that restored a stack level and which is now exited. */ -#define TREE_PACKED(NODE) ((NODE)->common.packed_attr) - -/* Value of expression is constant. - Always appears in all ..._CST nodes. - May also appear in an arithmetic expression, an ADDR_EXPR or a CONSTRUCTOR - if the value is constant. */ -#define TREE_LITERAL(NODE) ((NODE)->common.literal_attr) - -/* Nonzero in a ..._DECL means this variable is ref'd from a nested function. - Cannot happen in C because it does not allow nested functions, as of now. - For VAR_DECL nodes, PARM_DECL nodes, and - maybe FUNCTION_DECL or LABEL_DECL nodes. - - Also set in some languages for variables, etc., outside the normal - lexical scope, such as class instance variables. */ -#define TREE_NONLOCAL(NODE) ((NODE)->common.nonlocal_attr) - -/* Nonzero means permanent node; - node will continue to exist for the entire compiler run. - Otherwise it will be recycled at the end of the function. */ -#define TREE_PERMANENT(NODE) ((NODE)->common.permanent_attr) - -/* In INTEGER_TYPE or ENUMERAL_TYPE nodes, means an unsigned type. - In FIELD_DECL nodes, means an unsigned bit field. */ -#define TREE_UNSIGNED(NODE) ((NODE)->common.unsigned_attr) - -/* Nonzero in a VAR_DECL means assembler code has been written. - Nonzero in a FUNCTION_DECL means that the function has been compiled. - This is interesting in an inline function, since it might not need - to be compiled separately. */ -#define TREE_ASM_WRITTEN(NODE) ((NODE)->common.asm_written_attr) - -/* Nonzero in a FUNCTION_DECL means this function can be substituted - where it is called. */ -#define TREE_INLINE(NODE) ((NODE)->common.inline_attr) - -/* Nonzero in a _DECL if the name is used in its scope. */ -#define TREE_USED(NODE) ((NODE)->common.used_attr) - -#define TREE_LANG_FLAG_1(NODE) ((NODE)->common.lang_flag_1) -#define TREE_LANG_FLAG_2(NODE) ((NODE)->common.lang_flag_2) -#define TREE_LANG_FLAG_3(NODE) ((NODE)->common.lang_flag_3) -#define TREE_LANG_FLAG_4(NODE) ((NODE)->common.lang_flag_4) - -/* Define additional fields and accessors for nodes representing constants. */ - -/* In an INTEGER_CST node. These two together make a 64 bit integer. - If the data type is signed, the value is sign-extended to 64 bits - even though not all of them may really be in use. - In an unsigned constant shorter than 64 bits, the extra bits are 0. */ -#define TREE_INT_CST_LOW(NODE) ((NODE)->int_cst.int_cst_low) -#define TREE_INT_CST_HIGH(NODE) ((NODE)->int_cst.int_cst_high) - -#define INT_CST_LT(A, B) \ -(TREE_INT_CST_HIGH (A) < TREE_INT_CST_HIGH (B) \ - || (TREE_INT_CST_HIGH (A) == TREE_INT_CST_HIGH (B) \ - && ((unsigned) TREE_INT_CST_LOW (A) < (unsigned) TREE_INT_CST_LOW (B)))) - -#define INT_CST_LT_UNSIGNED(A, B) \ -((unsigned) TREE_INT_CST_HIGH (A) < (unsigned) TREE_INT_CST_HIGH (B) \ - || ((unsigned) TREE_INT_CST_HIGH (A) == (unsigned) TREE_INT_CST_HIGH (B) \ - && ((unsigned) TREE_INT_CST_LOW (A) < (unsigned) TREE_INT_CST_LOW (B)))) - -struct tree_int_cst -{ - char common[sizeof (struct tree_common)]; - long int_cst_low; - long int_cst_high; -}; - -/* In REAL_CST, STRING_CST, COMPLEX_CST nodes, and CONSTRUCTOR nodes, - and generally in all kinds of constants that could - be given labels (rather than being immediate). */ - -#define TREE_CST_RTL(NODE) ((NODE)->real_cst.rtl) - -/* In a REAL_CST node. */ -/* We can represent a real value as either a `double' or a string. - Strings don't allow for any optimization, but they do allow - for cross-compilation. */ - -#define TREE_REAL_CST(NODE) ((NODE)->real_cst.real_cst) - -#include "real.h" - -struct tree_real_cst -{ - char common[sizeof (struct tree_common)]; - struct rtx_def *rtl; /* acts as link to register transfer language - (rtl) info */ - REAL_VALUE_TYPE real_cst; -}; - -/* In a STRING_CST */ -#define TREE_STRING_LENGTH(NODE) ((NODE)->string.length) -#define TREE_STRING_POINTER(NODE) ((NODE)->string.pointer) - -struct tree_string -{ - char common[sizeof (struct tree_common)]; - struct rtx_def *rtl; /* acts as link to register transfer language - (rtl) info */ - int length; - char *pointer; -}; - -/* In a COMPLEX_CST node. */ -#define TREE_REALPART(NODE) ((NODE)->complex.real) -#define TREE_IMAGPART(NODE) ((NODE)->complex.imag) - -struct tree_complex -{ - char common[sizeof (struct tree_common)]; - struct rtx_def *rtl; /* acts as link to register transfer language - (rtl) info */ - union tree_node *real; - union tree_node *imag; -}; - -/* Define fields and accessors for some special-purpose tree nodes. */ - -#define IDENTIFIER_LENGTH(NODE) ((NODE)->identifier.length) -#define IDENTIFIER_POINTER(NODE) ((NODE)->identifier.pointer) - -struct tree_identifier -{ - char common[sizeof (struct tree_common)]; - int length; - char *pointer; -}; - -/* In a TREE_LIST node. */ -#define TREE_PURPOSE(NODE) ((NODE)->list.purpose) -#define TREE_VALUE(NODE) ((NODE)->list.value) - -struct tree_list -{ - char common[sizeof (struct tree_common)]; - union tree_node *purpose; - union tree_node *value; -}; - -/* Define fields and accessors for some nodes that represent expressions. */ - -/* In a SAVE_EXPR node. */ -#define SAVE_EXPR_RTL(NODE) (*(struct rtx_def **) &(NODE)->exp.operands[1]) - -/* In a RTL_EXPR node. */ -#define RTL_EXPR_SEQUENCE(NODE) (*(struct rtx_def **) &(NODE)->exp.operands[0]) -#define RTL_EXPR_RTL(NODE) (*(struct rtx_def **) &(NODE)->exp.operands[1]) - -/* In a CALL_EXPR node. */ -#define CALL_EXPR_RTL(NODE) (*(struct rtx_def **) &(NODE)->exp.operands[2]) - -/* In a CONSTRUCTOR node. */ -#define CONSTRUCTOR_ELTS(NODE) TREE_OPERAND (NODE, 1) - -/* In expression and reference nodes. */ -#define TREE_OPERAND(NODE, I) ((NODE)->exp.operands[I]) -#define TREE_COMPLEXITY(NODE, I) ((NODE)->exp.complexity) - -struct tree_exp -{ - char common[sizeof (struct tree_common)]; - int complexity; - union tree_node *operands[1]; -}; - -/* Define fields and accessors for nodes representing data types. */ - -/* See tree.def for documentation of the use of these fields. - Look at the documentation of the various ..._TYPE tree codes. */ - -#define TYPE_SIZE(NODE) ((NODE)->type.size) -#define TYPE_SIZE_UNIT(NODE) ((NODE)->type.size_unit) -#define TYPE_MODE(NODE) ((NODE)->type.mode) -#define TYPE_ALIGN(NODE) ((NODE)->type.align) -#define TYPE_VALUES(NODE) ((NODE)->type.values) -#define TYPE_DOMAIN(NODE) ((NODE)->type.values) -#define TYPE_FIELDS(NODE) ((NODE)->type.values) -#define TYPE_ARG_TYPES(NODE) ((NODE)->type.values) -#define TYPE_METHOD_BASETYPE(NODE) ((NODE)->type.max) -#define TYPE_OFFSET_BASETYPE(NODE) ((NODE)->type.max) -#define TYPE_SEP(NODE) ((NODE)->type.sep) -#define TYPE_SEP_UNIT(NODE) ((NODE)->type.sep_unit) -#define TYPE_POINTER_TO(NODE) ((NODE)->type.pointer_to) -#define TYPE_REFERENCE_TO(NODE) ((NODE)->type.reference_to) -#define TYPE_MIN_VALUE(NODE) ((NODE)->type.sep) -#define TYPE_MAX_VALUE(NODE) ((NODE)->type.max) -#define TYPE_PRECISION(NODE) ((NODE)->type.sep_unit) -#define TYPE_PARSE_INFO(NODE) ((NODE)->type.parse_info) -#define TYPE_SYMTAB_ADDRESS(NODE) ((NODE)->type.symtab_address) -#define TYPE_NAME(NODE) ((NODE)->type.name) -#define TYPE_NEXT_VARIANT(NODE) ((NODE)->type.next_variant) -#define TYPE_MAIN_VARIANT(NODE) ((NODE)->type.main_variant) -#define TYPE_BASETYPES(NODE) ((NODE)->type.basetypes) -#define TYPE_NONCOPIED_PARTS(NODE) ((NODE)->type.noncopied_parts) -#define TYPE_LANG_SPECIFIC(NODE) ((NODE)->type.lang_specific) - -struct tree_type -{ - char common[sizeof (struct tree_common)]; - union tree_node *values; - union tree_node *sep; - union tree_node *size; - - enum machine_mode mode : 8; - unsigned char size_unit; - unsigned char align; - unsigned char sep_unit; - - union tree_node *pointer_to; - union tree_node *reference_to; - int parse_info; - int symtab_address; - union tree_node *name; - union tree_node *max; - union tree_node *next_variant; - union tree_node *main_variant; - union tree_node *basetypes; - union tree_node *noncopied_parts; - /* Points to a structure whose details depend on the language in use. */ - struct lang_type *lang_specific; -}; - -/* Define fields and accessors for nodes representing declared names. */ - -#define DECL_VOFFSET(NODE) ((NODE)->decl.voffset) /* In FIELD_DECLs and maybe PARM_DECLs. */ -#define DECL_RESULT_TYPE(NODE) ((NODE)->decl.voffset) /* In FUNCTION_DECLs. */ -#define DECL_VOFFSET_UNIT(NODE) ((NODE)->decl.voffset_unit) -#define DECL_OFFSET(NODE) ((NODE)->decl.offset) -#define DECL_FUNCTION_CODE(NODE) ((enum built_in_function) (NODE)->decl.offset) -#define DECL_SET_FUNCTION_CODE(NODE,VAL) ((NODE)->decl.offset = (int) (VAL)) -#define DECL_NAME(NODE) ((NODE)->decl.name) -#define DECL_PRINT_NAME(NODE) ((NODE)->decl.print_name) -#define DECL_ASSEMBLER_NAME(NODE) ((NODE)->decl.assembler_name) -#define DECL_CONTEXT(NODE) ((NODE)->decl.context) -#define DECL_FIELD_CONTEXT(NODE) ((NODE)->decl.context) -#define DECL_ARGUMENTS(NODE) ((NODE)->decl.arguments) /* In FUNCTION_DECL. */ -#define DECL_ARG_TYPE(NODE) ((NODE)->decl.arguments) /* In PARM_DECL. */ -#define DECL_RESULT(NODE) ((NODE)->decl.result) -#define DECL_INITIAL(NODE) ((NODE)->decl.initial) -#define DECL_SOURCE_FILE(NODE) ((NODE)->decl.filename) -#define DECL_SOURCE_LINE(NODE) ((NODE)->decl.linenum) -#define DECL_SIZE(NODE) ((NODE)->decl.size) -#define DECL_SIZE_UNIT(NODE) ((NODE)->decl.size_unit) -#define DECL_ALIGN(NODE) ((NODE)->decl.align) -#define DECL_MODE(NODE) ((NODE)->decl.mode) -#define DECL_RTL(NODE) ((NODE)->decl.rtl) -#define DECL_BLOCK_SYMTAB_ADDRESS(NODE) ((NODE)->decl.block_symtab_address) -#define DECL_SYMTAB_INDEX(NODE) ((NODE)->decl.block_symtab_address) -#define DECL_SAVED_INSNS(NODE) ((NODE)->decl.saved_insns) -#define DECL_FRAME_SIZE(NODE) ((NODE)->decl.frame_size) -#define DECL_LANG_SPECIFIC(NODE) ((NODE)->decl.lang_specific) - -struct tree_decl -{ - char common[sizeof (struct tree_common)]; - char *filename; - int linenum; - union tree_node *size; - enum machine_mode mode : 8; - unsigned char size_unit; - unsigned char align; - unsigned char voffset_unit; - union tree_node *name; - union tree_node *context; - int offset; - union tree_node *voffset; - union tree_node *arguments; - union tree_node *result; - union tree_node *initial; - char *print_name; - char *assembler_name; - struct rtx_def *rtl; /* acts as link to register transfer language - (rtl) info */ - int frame_size; /* For FUNCTION_DECLs: size of stack frame */ - struct rtx_def *saved_insns; /* For FUNCTION_DECLs: points to insn that - constitutes its definition on the - permanent obstack. */ - int block_symtab_address; - /* Points to a structure whose details depend on the language in use. */ - struct lang_decl *lang_specific; -}; - -/* Define fields and accessors for nodes representing statements. - These are now obsolete for C, except for LET_STMT, which is used - to record the structure of binding contours (and the names declared - in each contour) for the sake of outputting debugging info. - Perhaps they will be used once again for other languages. */ - -/* For LABEL_STMT, GOTO_STMT, RETURN_STMT, LOOP_STMT, - COMPOUND_STMT, ASM_STMT. */ -#define STMT_SOURCE_LINE(NODE) ((NODE)->stmt.linenum) -#define STMT_SOURCE_FILE(NODE) ((NODE)->stmt.filename) -#define STMT_BODY(NODE) ((NODE)->stmt.body) - -struct tree_stmt -{ - char common[sizeof (struct tree_common)]; - char *filename; - int linenum; - union tree_node *body; -}; - -/* For IF_STMT. */ - -/* #define STMT_SOURCE_LINE(NODE) */ -/* #define STMT_SOURCE_FILE(NODE) */ -#define STMT_COND(NODE) ((NODE)->if_stmt.cond) -#define STMT_THEN(NODE) ((NODE)->if_stmt.thenpart) -#define STMT_ELSE(NODE) ((NODE)->if_stmt.elsepart) - -struct tree_if_stmt -{ - char common[sizeof (struct tree_common)]; - char *filename; - int linenum; - union tree_node *cond, *thenpart, *elsepart; -}; - -/* For LET_STMT and WITH_STMT. */ - -/* #define STMT_SOURCE_LINE(NODE) */ -/* #define STMT_SOURCE_FILE(NODE) */ -/* #define STMT_BODY(NODE) */ -#define STMT_VARS(NODE) ((NODE)->bind_stmt.vars) -#define STMT_SUPERCONTEXT(NODE) ((NODE)->bind_stmt.supercontext) -#define STMT_BIND_SIZE(NODE) ((NODE)->bind_stmt.bind_size) -#define STMT_TYPE_TAGS(NODE) ((NODE)->bind_stmt.type_tags) -#define STMT_SUBBLOCKS(NODE) ((NODE)->bind_stmt.subblocks) - -struct tree_bind_stmt -{ - char common[sizeof (struct tree_common)]; - char *filename; - int linenum; - union tree_node *body, *vars, *supercontext, *bind_size, *type_tags; - union tree_node *subblocks; -}; - -/* For CASE_STMT. */ - -#define STMT_CASE_INDEX(NODE) ((NODE)->case_stmt.index) -#define STMT_CASE_LIST(NODE) ((NODE)->case_stmt.case_list) - -struct tree_case_stmt -{ - char common[sizeof (struct tree_common)]; - char *filename; - int linenum; - union tree_node *index, *case_list; -}; - -/* Define the overall contents of a tree node. - It may be any of the structures declared above - for various types of node. */ - -union tree_node -{ - struct tree_common common; - struct tree_int_cst int_cst; - struct tree_real_cst real_cst; - struct tree_string string; - struct tree_complex complex; - struct tree_identifier identifier; - struct tree_decl decl; - struct tree_type type; - struct tree_list list; - struct tree_exp exp; - struct tree_stmt stmt; - struct tree_if_stmt if_stmt; - struct tree_bind_stmt bind_stmt; - struct tree_case_stmt case_stmt; -}; - -extern char *oballoc (); -extern char *permalloc (); - -/* Lowest level primitive for allocating a node. - The TREE_CODE is the only argument. Contents are initialized - to zero except for a few of the common fields. */ - -extern tree make_node (); - -/* Make a copy of a node, with all the same contents except - for TREE_UID and TREE_PERMANENT. (The copy is permanent - iff nodes being made now are permanent.) */ - -extern tree copy_node (); - -/* Make a copy of a chain of TREE_LIST nodes. */ - -extern tree copy_list (); - -/* Return the (unique) IDENTIFIER_NODE node for a given name. - The name is supplied as a char *. */ - -extern tree get_identifier (); - -/* Construct various types of nodes. */ - -extern tree build_int_2 (); -extern tree build_real (); -extern tree build_real_from_string (); -extern tree build_real_from_int_cst (); -extern tree build_complex (); -extern tree build_string (); -extern tree build (); -extern tree build_nt (); -extern tree build_tree_list (); -extern tree build_op_identifier (); -extern tree build_decl (); -extern tree build_let (); - -/* Construct various nodes representing data types. */ - -extern tree make_signed_type (); -extern tree make_unsigned_type (); -extern void fixup_unsigned_type (); -extern tree build_pointer_type (); -extern tree build_reference_type (); -extern tree build_index_type (); -extern tree build_array_type (); -extern tree build_function_type (); -extern tree build_method_type (); -extern tree build_offset_type (); -extern tree array_type_nelts (); - -/* Construct expressions, performing type checking. */ - -extern tree build_binary_op (); -extern tree build_indirect_ref (); -extern tree build_unary_op (); - -/* Given a type node TYPE, and CONSTP and VOLATILEP, return a type - for the same kind of data as TYPE describes. - Variants point to the "main variant" (which has neither CONST nor VOLATILE) - via TYPE_MAIN_VARIANT, and it points to a chain of other variants - so that duplicate variants are never made. - Only main variants should ever appear as types of expressions. */ - -extern tree build_type_variant (); - -/* Given a ..._TYPE node, calculate the TYPE_SIZE, TYPE_SIZE_UNIT, - TYPE_ALIGN and TYPE_MODE fields. - If called more than once on one node, does nothing except - for the first time. */ - -extern void layout_type (); - -/* Given a hashcode and a ..._TYPE node (for which the hashcode was made), - return a canonicalized ..._TYPE node, so that duplicates are not made. - How the hash code is computed is up to the caller, as long as any two - callers that could hash identical-looking type nodes agree. */ - -extern tree type_hash_canon (); - -/* Given a VAR_DECL, PARM_DECL, RESULT_DECL or FIELD_DECL node, - calculates the DECL_SIZE, DECL_SIZE_UNIT, DECL_ALIGN and DECL_MODE - fields. Call this only once for any given decl node. - - Second argument is the boundary that this field can be assumed to - be starting at (in bits). Zero means it can be assumed aligned - on any boundary that may be needed. */ - -extern void layout_decl (); - -/* Fold constants as much as possible in an expression. - Returns the simplified expression. - Acts only on the top level of the expression; - if the argument itself cannot be simplified, its - subexpressions are not changed. */ - -extern tree fold (); - -/* combine (tree_code, exp1, exp2) where EXP1 and EXP2 are constants - returns a constant expression for the result of performing - the operation specified by TREE_CODE on EXP1 and EXP2. */ - -extern tree combine (); - -extern tree convert (); -extern tree convert_units (); -extern tree size_in_bytes (); -extern tree genop (); -extern tree build_int (); -extern tree get_pending_sizes (); - -/* Type for sizes of data-type. */ - -extern tree sizetype; - -/* Concatenate two lists (chains of TREE_LIST nodes) X and Y - by making the last node in X point to Y. - Returns X, except if X is 0 returns Y. */ - -extern tree chainon (); - -/* Make a new TREE_LIST node from specified PURPOSE, VALUE and CHAIN. */ - -extern tree tree_cons (), perm_tree_cons (), temp_tree_cons (); -extern tree saveable_tree_cons (); - -/* Return the last tree node in a chain. */ - -extern tree tree_last (); - -/* Reverse the order of elements in a chain, and return the new head. */ - -extern tree nreverse (); - -/* Returns the length of a chain of nodes - (number of chain pointers to follow before reaching a null pointer). */ - -extern int list_length (); - -/* integer_zerop (tree x) is nonzero if X is an integer constant of value 0 */ - -extern int integer_zerop (); - -/* integer_onep (tree x) is nonzero if X is an integer constant of value 1 */ - -extern int integer_onep (); - -/* integer_all_onesp (tree x) is nonzero if X is an integer constant - all of whose significant bits are 1. */ - -extern int integer_all_onesp (); - -/* type_unsigned_p (tree x) is nonzero if the type X is an unsigned type - (all of its possible values are >= 0). - If X is a pointer type, the value is 1. - If X is a real type, the value is 0. */ - -extern int type_unsigned_p (); - -/* staticp (tree x) is nonzero if X is a reference to data allocated - at a fixed address in memory. */ - -extern int staticp (); - -/* Gets an error if argument X is not an lvalue. - Also returns 1 if X is an lvalue, 0 if not. */ - -extern int lvalue_or_else (); - -/* save_expr (EXP) returns an expression equivalent to EXP - but it can be used multiple times within context CTX - and only evaluate EXP once. */ - -extern tree save_expr (); - -/* stabilize_reference (EXP) returns an reference equivalent to EXP - but it can be used multiple times - and only evaluate the subexpressions once. */ - -extern tree stabilize_reference (); - -/* Return EXP, stripped of any conversions to wider types - in such a way that the result of converting to type FOR_TYPE - is the same as if EXP were converted to FOR_TYPE. - If FOR_TYPE is 0, it signifies EXP's type. */ - -extern tree get_unwidened (); - -/* Return OP or a simpler expression for a narrower value - which can be sign-extended or zero-extended to give back OP. - Store in *UNSIGNEDP_PTR either 1 if the value should be zero-extended - or 0 if the value should be sign-extended. */ - -extern tree get_narrower (); - -/* Given PRECISION and UNSIGNEDP, return a suitable type-tree - for an integer type with at least that precision. - The definition of this resides in language-specific code - as the repertoire of available types may vary. */ - -extern tree type_for_size (); - -/* Given an integer type T, return a type like T but unsigned. - If T is unsigned, the value is T. - The definition of this resides in language-specific code - as the repertoire of available types may vary. */ - -extern tree unsigned_type (); - -/* Given an integer type T, return a type like T but signed. - If T is signed, the value is T. - The definition of this resides in language-specific code - as the repertoire of available types may vary. */ - -extern tree signed_type (); - -/* Return the floating type node for a given floating machine mode. */ - -extern tree get_floating_type (); - -/* Given the FUNCTION_DECL for the current function, - return zero if it is ok for this function to be inline. - Otherwise return a warning message with a single %s - for the function's name. */ - -extern char *function_cannot_inline_p (); - -/* Declare commonly used variables for tree structure. */ - -/* An integer constant with value 0 */ -extern tree integer_zero_node; - -/* An integer constant with value 1 */ -extern tree integer_one_node; - -/* An integer constant with value 0 whose type is sizetype. */ -extern tree size_zero_node; - -/* An integer constant with value 1 whose type is sizetype. */ -extern tree size_one_node; - -/* A constant of type pointer-to-int and value 0 */ -extern tree null_pointer_node; - -/* A node of type ERROR_MARK. */ -extern tree error_mark_node; - -/* The type node for the void type. */ -extern tree void_type_node; - -/* The type node for the ordinary (signed) integer type. */ -extern tree integer_type_node; - -/* The type node for the unsigned integer type. */ -extern tree unsigned_type_node; - -/* The type node for the ordinary character type. */ -extern tree char_type_node; - -/* Points to the name of the input file from which the current input - being parsed originally came (before it went into cpp). */ -extern char *input_filename; - -/* Current source line number in that file. */ -extern int lineno; - -/* Nonzero for -pedantic switch: warn about anything - that standard C forbids. */ -extern int pedantic; - -/* Nonzero means can safely call expand_expr now; - otherwise layout_type puts variable sizes onto `pending_sizes' instead. */ - -extern int immediate_size_expand; - -/* Points to the FUNCTION_DECL of the function whose body we are reading. */ - -extern tree current_function_decl; - -/* Nonzero if function being compiled can call setjmp. */ - -extern int current_function_calls_setjmp; - -/* Nonzero means all ..._TYPE nodes should be allocated permanently. */ - -extern int all_types_permanent; - -/* In stmt.c */ - -extern tree expand_start_stmt_expr (); -extern tree expand_end_stmt_expr (); -extern void expand_expr_stmt (), clear_last_expr (); -extern void expand_label (), expand_goto (), expand_asm (); -extern void expand_start_cond (), expand_end_cond (); -extern void expand_start_else (), expand_end_else (); -extern void expand_start_loop (), expand_start_loop_continue_elsewhere (); -extern void expand_loop_continue_here (); -extern void expand_end_loop (); -extern int expand_continue_loop (); -extern int expand_exit_loop (), expand_exit_loop_if_false (); -extern int expand_exit_something (); - -extern void expand_start_delayed_expr (); -extern tree expand_end_delayed_expr (); -extern void expand_emit_delayed_expr (); - -extern void expand_null_return (), expand_return (); -extern void expand_start_bindings (), expand_end_bindings (); -extern void expand_start_case (), expand_end_case (); -extern int pushcase (), pushcase_range (); -extern void expand_start_function (), expand_end_function (); diff --git a/gnu/usr.bin/gcc1/cc1/typeclass.h b/gnu/usr.bin/gcc1/cc1/typeclass.h deleted file mode 100644 index b166042536..0000000000 --- a/gnu/usr.bin/gcc1/cc1/typeclass.h +++ /dev/null @@ -1,14 +0,0 @@ -/* Values returned by __builtin_classify_type. */ - -enum type_class -{ - no_type_class = -1, - void_type_class, integer_type_class, char_type_class, - enumeral_type_class, boolean_type_class, - pointer_type_class, reference_type_class, offset_type_class, - real_type_class, complex_type_class, - function_type_class, method_type_class, - record_type_class, union_type_class, - array_type_class, string_type_class, set_type_class, file_type_class, - lang_type_class -}; diff --git a/gnu/usr.bin/gcc1/cc1/varasm.c b/gnu/usr.bin/gcc1/cc1/varasm.c deleted file mode 100644 index 8adf464e0c..0000000000 --- a/gnu/usr.bin/gcc1/cc1/varasm.c +++ /dev/null @@ -1,2030 +0,0 @@ -/* Output variables, constants and external declarations, for GNU compiler. - Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* This file handles generation of all the assembler code - *except* the instructions of a function. - This includes declarations of variables and their initial values. - - We also output the assembler code for constants stored in memory - and are responsible for combining constants with the same value. */ - -#include -#include -/* #include */ -#include "config.h" -#include "rtl.h" -#include "tree.h" -#include "flags.h" -#include "expr.h" -#include "hard-reg-set.h" - -#include "obstack.h" - -#define MIN(a, b) ((a) < (b) ? (a) : (b)) - -/* File in which assembler code is being written. */ - -extern FILE *asm_out_file; - -extern struct obstack *current_obstack; -extern struct obstack *saveable_obstack; -extern struct obstack permanent_obstack; -#define obstack_chunk_alloc xmalloc -extern int xmalloc (); - -/* Number for making the label on the next - constant that is stored in memory. */ - -int const_labelno; - -/* Number for making the label on the next - static variable internal to a function. */ - -int var_labelno; - -/* Nonzero if at least one function definition has been seen. */ -static int function_defined; - -extern FILE *asm_out_file; - -static char *compare_constant_1 (); -static void record_constant_1 (); -void assemble_name (); -void output_addressed_constants (); -void output_constant (); -void output_constructor (); - -#ifdef EXTRA_SECTIONS -static enum in_section {no_section, in_text, in_data, EXTRA_SECTIONS} in_section - = no_section; -#else -static enum in_section {no_section, in_text, in_data} in_section - = no_section; -#endif - -/* Define functions like text_section for any extra sections. */ -#ifdef EXTRA_SECTION_FUNCTIONS -EXTRA_SECTION_FUNCTIONS -#endif - -/* Tell assembler to switch to text section. */ - -void -text_section () -{ - if (in_section != in_text) - { - fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP); - in_section = in_text; - } -} - -/* Tell assembler to switch to data section. */ - -void -data_section () -{ - if (in_section != in_data) - { - if (flag_shared_data) - { -#ifdef SHARED_SECTION_ASM_OP - fprintf (asm_out_file, "%s\n", SHARED_SECTION_ASM_OP); -#else - fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP); -#endif - } - else - fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP); - - in_section = in_data; - } -} - -/* Determine if we're in the text section. */ - -int -in_text_section () -{ - return in_section == in_text; -} - -/* Create the rtl to represent a function, for a function definition. - DECL is a FUNCTION_DECL node which describes which function. - The rtl is stored into DECL. */ - -void -make_function_rtl (decl) - tree decl; -{ - if (DECL_RTL (decl) == 0) - DECL_RTL (decl) - = gen_rtx (MEM, DECL_MODE (decl), - gen_rtx (SYMBOL_REF, Pmode, DECL_ASSEMBLER_NAME (decl))); - - /* Record at least one function has been defined. */ - function_defined = 1; -} - -/* Decode an `asm' spec for a declaration as a register name. - Return the register number, or -1 if nothing specified, - or -2 if the name is not a register. */ - -int -decode_reg_name (asmspec) - char *asmspec; -{ - if (asmspec != 0) - { - int i; - extern char *reg_names[]; - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (!strcmp (asmspec, reg_names[i])) - break; - - if (i < FIRST_PSEUDO_REGISTER) - return i; - else - return -2; - } - - return -1; -} - -/* Create the DECL_RTL for a declaration for a static or external variable - or static or external function. - ASMSPEC, if not 0, is the string which the user specified - as the assembler symbol name. - TOP_LEVEL is nonzero if this is a file-scope variable. - - This is never called for PARM_DECL nodes. */ - -void -make_decl_rtl (decl, asmspec, top_level) - tree decl; - char *asmspec; - int top_level; -{ - register char *name = DECL_ASSEMBLER_NAME (decl); - int reg_number = decode_reg_name (asmspec); - - if (reg_number == -2) - { - name = (char *) obstack_alloc (saveable_obstack, - strlen (asmspec) + 2); - name[0] = '*'; - strcpy (&name[1], asmspec); - } - - /* For a duplicate declaration, we can be called twice on the - same DECL node. Don't alter the RTL already made - unless the old mode is wrong (which can happen when - the previous rtl was made when the type was incomplete). */ - if (DECL_RTL (decl) == 0 - || GET_MODE (DECL_RTL (decl)) != DECL_MODE (decl)) - { - DECL_RTL (decl) = 0; - - /* First detect errors in declaring global registers. */ - if (TREE_REGDECL (decl) && reg_number == -1) - error_with_decl (decl, - "register name not specified for `%s'"); - else if (TREE_REGDECL (decl) && reg_number == -2) - error_with_decl (decl, - "invalid register name for `%s'"); - else if (reg_number >= 0 && ! TREE_REGDECL (decl)) - error_with_decl (decl, - "register name given for non-register variable `%s'"); - else if (TREE_REGDECL (decl) && TREE_CODE (decl) == FUNCTION_DECL) - error ("function declared `register'"); - else if (TREE_REGDECL (decl) && TYPE_MODE (TREE_TYPE (decl)) == BLKmode) - error_with_decl (decl, "data type of `%s' isn't suitable for a register"); - /* Now handle properly declared static register variables. */ - else if (TREE_REGDECL (decl)) - { - int nregs; - if (pedantic) - warning ("ANSI C forbids global register variables"); - if (DECL_INITIAL (decl) != 0) - { - DECL_INITIAL (decl) = 0; - error ("global register variable has initial value"); - } - if (fixed_regs[reg_number] == 0 - && function_defined && top_level) - error ("global register variable follows a function definition"); - DECL_RTL (decl) = gen_rtx (REG, DECL_MODE (decl), reg_number); - if (top_level) - { - /* Make this register fixed, so not usable for anything else. */ - nregs = HARD_REGNO_NREGS (reg_number, DECL_MODE (decl)); - while (nregs > 0) - global_regs[reg_number + --nregs] = 1; - init_reg_sets_1 (); - } - } - - /* Now handle ordinary static variables and functions (in memory). - Also handle vars declared register invalidly. */ - if (DECL_RTL (decl) == 0) - { - /* Can't use just the variable's own name for a variable - whose scope is less than the whole file. - Concatenate a distinguishing number. */ - if (!top_level && !TREE_EXTERNAL (decl) && asmspec == 0) - { - char *label; - - ASM_FORMAT_PRIVATE_NAME (label, name, var_labelno); - name = obstack_copy0 (saveable_obstack, label, strlen (label)); - var_labelno++; - } - - DECL_RTL (decl) = gen_rtx (MEM, DECL_MODE (decl), - gen_rtx (SYMBOL_REF, Pmode, name)); - if (TREE_EXTERNAL (decl)) - EXTERNAL_SYMBOL_P (XEXP (DECL_RTL (decl), 0)) = 1; - if (TREE_VOLATILE (decl)) - MEM_VOLATILE_P (DECL_RTL (decl)) = 1; - if (TREE_READONLY (decl)) - RTX_UNCHANGING_P (DECL_RTL (decl)) = 1; - MEM_IN_STRUCT_P (DECL_RTL (decl)) - = (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE - || TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE); - } - } -} - -/* Output a string of literal assembler code - for an `asm' keyword used between functions. */ - -void -assemble_asm (string) - tree string; -{ - app_enable (); - - fprintf (asm_out_file, "\t%s\n", TREE_STRING_POINTER (string)); -} - -/* Output assembler code associated with defining the name of a function - as described by DECL. */ - -void -assemble_function (decl) - tree decl; -{ - rtx x, n; - char *fnname; - int align; - - /* Get the function's name, as described by its RTL. - This may be different from the DECL_NAME name used in the source file. */ - - x = DECL_RTL (decl); - if (GET_CODE (x) != MEM) - abort (); - n = XEXP (x, 0); - if (GET_CODE (n) != SYMBOL_REF) - abort (); - fnname = XSTR (n, 0); - - /* The following code does not need preprocessing in the assembler. */ - - app_disable (); - - text_section (); - -#ifdef SDB_DEBUGGING_INFO - /* Make sure types are defined for debugger before fcn name is defined. */ - if (write_symbols == SDB_DEBUG) - sdbout_tags (gettags ()); -#endif - - /* Tell assembler to move to target machine's alignment for functions. */ - - align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT); - if (align > 0) - ASM_OUTPUT_ALIGN (asm_out_file, align); - -#ifdef SDB_DEBUGGING_INFO - /* Output SDB definition of the function. */ - if (write_symbols == SDB_DEBUG) - sdbout_mark_begin_function (); -#endif - - /* Make function name accessible from other files, if appropriate. */ - - if (TREE_PUBLIC (decl)) - ASM_GLOBALIZE_LABEL (asm_out_file, fnname); - - /* Do any machine/system dependent processing of the function name */ -#ifdef ASM_DECLARE_FUNCTION_NAME - ASM_DECLARE_FUNCTION_NAME (asm_out_file, fnname, current_function_decl); -#else - /* Standard thing is just output label for the function. */ - ASM_OUTPUT_LABEL (asm_out_file, fnname); -#endif /* ASM_DECLARE_FUNCTION_NAME */ -} - -/* Assemble " .int 0\n" or whatever this assembler wants. */ - -void -assemble_integer_zero () -{ - ASM_OUTPUT_INT (asm_out_file, const0_rtx); -} - -/* Assemble a string constant with the specified C string as contents. */ - -void -assemble_string (p, size) - unsigned char *p; - int size; -{ - register int i; - int excess = 0; - int pos = 0; - int maximum = 2000; - - /* If the string is very long, split it up. */ - - while (pos < size) - { - int thissize = size - pos; - if (thissize > maximum) - thissize = maximum; - -#ifdef ASM_OUTPUT_ASCII - ASM_OUTPUT_ASCII (asm_out_file, p, thissize); -#else - fprintf (asm_out_file, "\t.ascii \""); - - for (i = 0; i < thissize; i++) - { - register int c = p[i]; - if (c == '\"' || c == '\\') - putc ('\\', asm_out_file); - if (c >= ' ' && c < 0177) - putc (c, asm_out_file); - else - { - fprintf (asm_out_file, "\\%o", c); - /* After an octal-escape, if a digit follows, - terminate one string constant and start another. - The Vax assembler fails to stop reading the escape - after three digits, so this is the only way we - can get it to parse the data properly. */ - if (i < thissize - 1 - && p[i + 1] >= '0' && p[i + 1] <= '9') - fprintf (asm_out_file, "\"\n\t.ascii \""); - } - } - fprintf (asm_out_file, "\"\n"); -#endif /* no ASM_OUTPUT_ASCII */ - - pos += thissize; - p += thissize; - } -} - -/* Assemble everything that is needed for a variable or function declaration. - Not used for automatic variables, and not used for function definitions. - Should not be called for variables of incomplete structure type. - - TOP_LEVEL is nonzero if this variable has file scope. - WRITE_SYMBOLS is DBX_DEBUG if writing dbx symbol output. - The dbx data for a file-scope variable is written here. - AT_END is nonzero if this is the special handling, at end of compilation, - to define things that have had only tentative definitions. */ - -void -assemble_variable (decl, top_level, write_symbols, at_end) - tree decl; - int top_level; - enum debugger write_symbols; - int at_end; -{ - register char *name; - register int i; - - /* Do nothing for global register variables. */ - - if (GET_CODE (DECL_RTL (decl)) == REG) - return; - - /* Normally no need to say anything for external references, - since assembler considers all undefined symbols external. */ - - if (TREE_EXTERNAL (decl)) - return; - - /* Output no assembler code for a function declaration. - Only definitions of functions output anything. */ - - if (TREE_CODE (decl) == FUNCTION_DECL) - return; - - /* If type was incomplete when the variable was declared, - see if it is complete now. */ - - if (DECL_SIZE (decl) == 0) - layout_decl (decl, 0); - - /* Still incomplete => don't allocate it; treat the tentative defn - (which is what it must have been) as an `extern' reference. */ - - if (DECL_SIZE (decl) == 0) - { - error_with_file_and_line (DECL_SOURCE_FILE (decl), - DECL_SOURCE_LINE (decl), - "storage size of static var `%s' isn't known", - IDENTIFIER_POINTER (DECL_NAME (decl))); - return; - } - - /* The first declaration of a variable that comes through this function - decides whether it is global (in C, has external linkage) - or local (in C, has internal linkage). So do nothing more - if this function has already run. */ - - if (TREE_ASM_WRITTEN (decl)) - return; - - TREE_ASM_WRITTEN (decl) = 1; - -#ifdef DBX_DEBUGGING_INFO - /* File-scope global variables are output here. */ - if (write_symbols == DBX_DEBUG && top_level) - dbxout_symbol (decl, 0); -#endif -#ifdef SDB_DEBUGGING_INFO - if (write_symbols == SDB_DEBUG && top_level) - sdbout_symbol (decl, 0); -#endif - if (write_symbols == GDB_DEBUG) - /* Make sure the file is known to GDB even if it has no functions. */ - set_current_gdbfile (DECL_SOURCE_FILE (decl)); - - /* If storage size is erroneously variable, just continue. - Error message was already made. */ - - if (! TREE_LITERAL (DECL_SIZE (decl))) - return; - - app_disable (); - - name = XSTR (XEXP (DECL_RTL (decl), 0), 0); - - /* Handle uninitialized definitions. */ - - if (DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node) - { - int size = (TREE_INT_CST_LOW (DECL_SIZE (decl)) - * DECL_SIZE_UNIT (decl) - / BITS_PER_UNIT); - int rounded = size; - /* Don't allocate zero bytes of common, - since that means "undefined external" in the linker. */ - if (size == 0) rounded = 1; - /* Round size up to multiple of BIGGEST_ALIGNMENT bits - so that each uninitialized object starts on such a boundary. */ - rounded = ((rounded + (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1) - / (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); - if (flag_shared_data) - data_section (); - if (TREE_PUBLIC (decl)) - ASM_OUTPUT_COMMON (asm_out_file, name, size, rounded); - else - ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded); - return; - } - - /* Handle initialized definitions. */ - - /* First make the assembler name(s) global if appropriate. */ - if (TREE_PUBLIC (decl) && DECL_NAME (decl)) - ASM_GLOBALIZE_LABEL (asm_out_file, name); -#if 0 - for (d = equivalents; d; d = TREE_CHAIN (d)) - { - tree e = TREE_VALUE (d); - if (TREE_PUBLIC (e) && DECL_NAME (e)) - ASM_GLOBALIZE_LABEL (asm_out_file, - XSTR (XEXP (DECL_RTL (e), 0), 0)); - } -#endif - - /* Output any data that we will need to use the address of. */ - if (DECL_INITIAL (decl)) - output_addressed_constants (DECL_INITIAL (decl)); - - /* Switch to the proper section for this data. */ -#ifdef SELECT_SECTION - SELECT_SECTION (decl); -#else - if (TREE_READONLY (decl) && ! TREE_VOLATILE (decl)) - text_section (); - else - data_section (); -#endif - - /* Output the alignment of this data. */ - for (i = 0; DECL_ALIGN (decl) >= BITS_PER_UNIT << (i + 1); i++); - if (i > 0) - ASM_OUTPUT_ALIGN (asm_out_file, i); - - /* Output the name(s) of this data. */ - ASM_OUTPUT_LABEL (asm_out_file, name); -#if 0 - for (d = equivalents; d; d = TREE_CHAIN (d)) - { - tree e = TREE_VALUE (d); - ASM_OUTPUT_LABEL (asm_out_file, XSTR (XEXP (DECL_RTL (e), 0), 0)); - } -#endif - - if (DECL_INITIAL (decl)) - /* Output the actual data. */ - output_constant (DECL_INITIAL (decl), int_size_in_bytes (TREE_TYPE (decl))); - else - /* Leave space for it. */ - ASM_OUTPUT_SKIP (asm_out_file, int_size_in_bytes (TREE_TYPE (decl))); -} - -/* Output something to declare an external symbol to the assembler. - (Most assemblers don't need this, so we normally output nothing.) */ - -void -assemble_external (decl) - tree decl; -{ - rtx rtl = DECL_RTL (decl); - - if (GET_CODE (rtl) == MEM && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF) - { -#ifdef ASM_OUTPUT_EXTERNAL - /* Some systems do require some output. */ - ASM_OUTPUT_EXTERNAL (asm_out_file, decl, XSTR (XEXP (rtl, 0), 0)); -#endif - } -} - -/* Output to FILE a reference to the assembler name of a C-level name NAME. - If NAME starts with a *, the rest of NAME is output verbatim. - Otherwise NAME is transformed in an implementation-defined way - (usually by the addition of an underscore). - Many macros in the tm file are defined to call this function. */ - -void -assemble_name (file, name) - FILE *file; - char *name; -{ - if (name[0] == '*') - fputs (&name[1], file); - else - ASM_OUTPUT_LABELREF (file, name); -} - -/* Allocate SIZE bytes writable static space with a gensym name - and return an RTX to refer to its address. */ - -rtx -assemble_static_space (size) - int size; -{ - char name[12]; - char *namestring; - rtx x; - /* Round size up to multiple of BIGGEST_ALIGNMENT bits - so that each uninitialized object starts on such a boundary. */ - int rounded = ((size + (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1) - / (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); - - if (flag_shared_data) - data_section (); - ASM_GENERATE_INTERNAL_LABEL (name, "LF", const_labelno); - ++const_labelno; - - namestring = (char *) obstack_alloc (saveable_obstack, - strlen (name) + 2); - strcpy (namestring, name); - - x = gen_rtx (SYMBOL_REF, Pmode, namestring); - ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded); - return x; -} - -/* Here we combine duplicate floating constants to make - CONST_DOUBLE rtx's, and force those out to memory when necessary. */ - -/* Chain of all CONST_DOUBLE rtx's constructed for the current function. - They are chained through the CONST_DOUBLE_CHAIN. - A CONST_DOUBLE rtx has CONST_DOUBLE_MEM != cc0_rtx iff it is on this chain. - In that case, CONST_DOUBLE_MEM is either a MEM, - or const0_rtx if no MEM has been made for this CONST_DOUBLE yet. */ - -static rtx real_constant_chain; - -/* Return a CONST_DOUBLE for a value specified as a pair of ints. - For an integer, I0 is the low-order word and I1 is the high-order word. - For a real number, I0 is the word with the low address - and I1 is the word with the high address. */ - -rtx -immed_double_const (i0, i1, mode) - int i0, i1; - enum machine_mode mode; -{ - register rtx r; - - if (mode == DImode && i0 == 0 && i1 == 0) - return const0_rtx; - - /* Search the chain for an existing CONST_DOUBLE with the right value. - If one is found, return it. */ - - for (r = real_constant_chain; r; r = CONST_DOUBLE_CHAIN (r)) - if (CONST_DOUBLE_LOW (r) == i0 && CONST_DOUBLE_HIGH (r) == i1 - && GET_MODE (r) == mode) - return r; - - /* No; make a new one and add it to the chain. */ - - r = gen_rtx (CONST_DOUBLE, mode, 0, i0, i1); - - CONST_DOUBLE_CHAIN (r) = real_constant_chain; - real_constant_chain = r; - - /* Store const0_rtx in mem-slot since this CONST_DOUBLE is on the chain. - Actual use of mem-slot is only through force_const_double_mem. */ - - CONST_DOUBLE_MEM (r) = const0_rtx; - - return r; -} - -/* Return a CONST_DOUBLE for a specified `double' value - and machine mode. */ - -rtx -immed_real_const_1 (d, mode) - REAL_VALUE_TYPE d; - enum machine_mode mode; -{ - union real_extract u; - register rtx r; - - /* Get the desired `double' value as a sequence of ints - since that is how they are stored in a CONST_DOUBLE. */ - - u.d = d; - - /* Detect zero. */ - - if (! bcmp (&CONST_DOUBLE_LOW (dconst0_rtx), &u, sizeof u)) - return (mode == DFmode ? dconst0_rtx : fconst0_rtx); - - if (sizeof u == 2 * sizeof (int)) - return immed_double_const (u.i[0], u.i[1], mode); - - /* The rest of this function handles the case where - a float value requires more than 2 ints of space. - It will be deleted as dead code on machines that don't need it. */ - - /* Search the chain for an existing CONST_DOUBLE with the right value. - If one is found, return it. */ - - for (r = real_constant_chain; r; r = CONST_DOUBLE_CHAIN (r)) - if (! bcmp (&CONST_DOUBLE_LOW (r), &u, sizeof u) - && GET_MODE (r) == mode) - return r; - - /* No; make a new one and add it to the chain. */ - - r = rtx_alloc (CONST_DOUBLE); - PUT_MODE (r, mode); - bcopy (&u, &CONST_DOUBLE_LOW (r), sizeof u); - - CONST_DOUBLE_CHAIN (r) = real_constant_chain; - real_constant_chain = r; - - /* Store const0_rtx in slot 2 since this CONST_DOUBLE is on the chain. - Actual use of slot 2 is only through force_const_double_mem. */ - - CONST_DOUBLE_MEM (r) = const0_rtx; - - return r; -} - -/* Return a CONST_DOUBLE rtx for a value specified by EXP, - which must be a REAL_CST tree node. */ - -rtx -immed_real_const (exp) - tree exp; -{ - return immed_real_const_1 (TREE_REAL_CST (exp), TYPE_MODE (TREE_TYPE (exp))); -} - -/* Given a CONST_DOUBLE, cause a constant in memory to be created - (unless we already have one for the same value) - and return a MEM rtx to refer to it. - Put the CONST_DOUBLE on real_constant_chain if it isn't already there. */ - -rtx -force_const_double_mem (r) - rtx r; -{ - if (CONST_DOUBLE_MEM (r) == cc0_rtx) - { - CONST_DOUBLE_CHAIN (r) = real_constant_chain; - real_constant_chain = r; - CONST_DOUBLE_MEM (r) = const0_rtx; - } - - if (CONST_DOUBLE_MEM (r) == const0_rtx) - { - CONST_DOUBLE_MEM (r) = force_const_mem (GET_MODE (r), r); - } - /* CONST_DOUBLE_MEM (r) is now a MEM with a constant address. - If that is legitimate, return it. - Othewise it will need reloading, so return a copy of it. */ - if (memory_address_p (GET_MODE (r), XEXP (CONST_DOUBLE_MEM (r), 0))) - return CONST_DOUBLE_MEM (r); - return gen_rtx (MEM, GET_MODE (r), XEXP (CONST_DOUBLE_MEM (r), 0)); -} - -/* At the end of a function, forget the memory-constants - previously made for CONST_DOUBLEs. Mark them as not on real_constant_chain. - Also clear out real_constant_chain and clear out all the chain-pointers. */ - -void -clear_const_double_mem () -{ - register rtx r, next; - - for (r = real_constant_chain; r; r = next) - { - next = CONST_DOUBLE_CHAIN (r); - CONST_DOUBLE_CHAIN (r) = 0; - CONST_DOUBLE_MEM (r) = cc0_rtx; - } - real_constant_chain = 0; -} - -/* Given an expression EXP with a constant value, - reduce it to the sum of an assembler symbol and an integer. - Store them both in the structure *VALUE. - Abort if EXP does not reduce. */ - -struct addr_const -{ - rtx base; - int offset; -}; - -static void -decode_addr_const (exp, value) - tree exp; - struct addr_const *value; -{ - register tree target = TREE_OPERAND (exp, 0); - register int offset = 0; - register rtx x; - - while (1) - { - if (TREE_CODE (target) == COMPONENT_REF) - { - offset += DECL_OFFSET (TREE_OPERAND (target, 1)) / BITS_PER_UNIT; - target = TREE_OPERAND (target, 0); - } - else if (TREE_CODE (target) == ARRAY_REF) - { - if (TREE_CODE (TREE_OPERAND (target, 1)) != INTEGER_CST - || TREE_CODE (TYPE_SIZE (TREE_TYPE (target))) != INTEGER_CST) - abort (); - offset += ((TYPE_SIZE_UNIT (TREE_TYPE (target)) - * TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (target))) - * TREE_INT_CST_LOW (TREE_OPERAND (target, 1))) - / BITS_PER_UNIT); - target = TREE_OPERAND (target, 0); - } - else break; - } - - if (TREE_CODE (target) == VAR_DECL - || TREE_CODE (target) == FUNCTION_DECL) - x = DECL_RTL (target); - else if (TREE_LITERAL (target)) - x = TREE_CST_RTL (target); - else - abort (); - - if (GET_CODE (x) != MEM) - abort (); - x = XEXP (x, 0); - - value->base = x; - value->offset = offset; -} - -/* Uniquize all constants that appear in memory. - Each constant in memory thus far output is recorded - in `const_hash_table' with a `struct constant_descriptor' - that contains a polish representation of the value of - the constant. - - We cannot store the trees in the hash table - because the trees may be temporary. */ - -struct constant_descriptor -{ - struct constant_descriptor *next; - char *label; - char contents[1]; -}; - -#define HASHBITS 30 -#define MAX_HASH_TABLE 1007 -static struct constant_descriptor *const_hash_table[MAX_HASH_TABLE]; - -/* Compute a hash code for a constant expression. */ - -int -const_hash (exp) - tree exp; -{ - register char *p; - register int len, hi, i; - register enum tree_code code = TREE_CODE (exp); - - if (code == INTEGER_CST) - { - p = (char *) &TREE_INT_CST_LOW (exp); - len = 2 * sizeof TREE_INT_CST_LOW (exp); - } - else if (code == REAL_CST) - { - p = (char *) &TREE_REAL_CST (exp); - len = sizeof TREE_REAL_CST (exp); - } - else if (code == STRING_CST) - p = TREE_STRING_POINTER (exp), len = TREE_STRING_LENGTH (exp); - else if (code == COMPLEX_CST) - return const_hash (TREE_REALPART (exp)) * 5 - + const_hash (TREE_IMAGPART (exp)); - else if (code == CONSTRUCTOR) - { - register tree link; - - /* For record type, include the type in the hashing. - We do not do so for array types - because (1) the sizes of the elements are sufficient - and (2) distinct array types can have the same constructor. */ - if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE) - hi = ((int) TREE_TYPE (exp) & ((1 << HASHBITS) - 1)) % MAX_HASH_TABLE; - else - hi = 5; - - for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) - hi = (hi * 603 + const_hash (TREE_VALUE (link))) % MAX_HASH_TABLE; - - return hi; - } - else if (code == ADDR_EXPR) - { - struct addr_const value; - decode_addr_const (exp, &value); - p = (char *) &value; - len = sizeof value; - } - else if (code == PLUS_EXPR || code == MINUS_EXPR) - return const_hash (TREE_OPERAND (exp, 0)) * 9 - + const_hash (TREE_OPERAND (exp, 1)); - else if (code == NOP_EXPR || code == CONVERT_EXPR) - return const_hash (TREE_OPERAND (exp, 0)) * 7 + 2; - - /* Compute hashing function */ - hi = len; - for (i = 0; i < len; i++) - hi = ((hi * 613) + (unsigned)(p[i])); - - hi &= (1 << HASHBITS) - 1; - hi %= MAX_HASH_TABLE; - return hi; -} - -/* Compare a constant expression EXP with a constant-descriptor DESC. - Return 1 if DESC describes a constant with the same value as EXP. */ - -static int -compare_constant (exp, desc) - tree exp; - struct constant_descriptor *desc; -{ - return 0 != compare_constant_1 (exp, desc->contents); -} - -/* Compare constant expression EXP with a substring P of a constant descriptor. - If they match, return a pointer to the end of the substring matched. - If they do not match, return 0. - - Since descriptors are written in polish prefix notation, - this function can be used recursively to test one operand of EXP - against a subdescriptor, and if it succeeds it returns the - address of the subdescriptor for the next operand. */ - -static char * -compare_constant_1 (exp, p) - tree exp; - char *p; -{ - register char *strp; - register int len; - register enum tree_code code = TREE_CODE (exp); - - if (code != (enum tree_code) *p++) - return 0; - - if (code == INTEGER_CST) - { - /* Integer constants are the same only if the same width of type. */ - if (*p++ != TYPE_PRECISION (TREE_TYPE (exp))) - return 0; - strp = (char *) &TREE_INT_CST_LOW (exp); - len = 2 * sizeof TREE_INT_CST_LOW (exp); - } - else if (code == REAL_CST) - { - /* Real constants are the same only if the same width of type. */ - if (*p++ != TYPE_PRECISION (TREE_TYPE (exp))) - return 0; - strp = (char *) &TREE_REAL_CST (exp); - len = sizeof TREE_REAL_CST (exp); - } - else if (code == STRING_CST) - { - if (flag_writable_strings) - return 0; - strp = TREE_STRING_POINTER (exp); - len = TREE_STRING_LENGTH (exp); - if (bcmp (&TREE_STRING_LENGTH (exp), p, - sizeof TREE_STRING_LENGTH (exp))) - return 0; - p += sizeof TREE_STRING_LENGTH (exp); - } - else if (code == COMPLEX_CST) - { - p = compare_constant_1 (TREE_REALPART (exp), p); - if (p == 0) return 0; - p = compare_constant_1 (TREE_IMAGPART (exp), p); - return p; - } - else if (code == CONSTRUCTOR) - { - register tree link; - int length = list_length (CONSTRUCTOR_ELTS (exp)); - tree type; - - if (bcmp (&length, p, sizeof length)) - return 0; - p += sizeof length; - - /* For record constructors, insist that the types match. - For arrays, just verify both constructors are for arrays. */ - if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE) - type = TREE_TYPE (exp); - else - type = 0; - if (bcmp (&type, p, sizeof type)) - return 0; - p += sizeof type; - - for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) - if ((p = compare_constant_1 (TREE_VALUE (link), p)) == 0) - return 0; - return p; - } - else if (code == ADDR_EXPR) - { - struct addr_const value; - decode_addr_const (exp, &value); - strp = (char *) &value; - len = sizeof value; - /* Compare SYMBOL_REF address and offset. */ - while (--len >= 0) - if (*p++ != *strp++) - return 0; - /* Compare symbol name. */ - strp = XSTR (value.base, 0); - len = strlen (strp) + 1; - } - else if (code == PLUS_EXPR || code == MINUS_EXPR) - { - if (*p++ != (char) code) - return 0; - p = compare_constant_1 (TREE_OPERAND (exp, 0), p); - if (p == 0) return 0; - p = compare_constant_1 (TREE_OPERAND (exp, 1), p); - return p; - } - else if (code == NOP_EXPR || code == CONVERT_EXPR) - { - if (*p++ != (char) code) - return 0; - p = compare_constant_1 (TREE_OPERAND (exp, 0), p); - return p; - } - - /* Compare constant contents. */ - while (--len >= 0) - if (*p++ != *strp++) - return 0; - - return p; -} - -/* Construct a constant descriptor for the expression EXP. - It is up to the caller to enter the descriptor in the hash table. */ - -static struct constant_descriptor * -record_constant (exp) - tree exp; -{ - struct constant_descriptor *ptr = 0; - int buf; - - obstack_grow (&permanent_obstack, &ptr, sizeof ptr); - obstack_grow (&permanent_obstack, &buf, sizeof buf); - record_constant_1 (exp); - return (struct constant_descriptor *) obstack_finish (&permanent_obstack); -} - -/* Add a description of constant expression EXP - to the object growing in `permanent_obstack'. - No need to return its address; the caller will get that - from the obstack when the object is complete. */ - -static void -record_constant_1 (exp) - tree exp; -{ - register char *strp; - register int len; - register enum tree_code code = TREE_CODE (exp); - - obstack_1grow (&permanent_obstack, (unsigned int) code); - - if (code == INTEGER_CST) - { - obstack_1grow (&permanent_obstack, TYPE_PRECISION (TREE_TYPE (exp))); - strp = (char *) &TREE_INT_CST_LOW (exp); - len = 2 * sizeof TREE_INT_CST_LOW (exp); - } - else if (code == REAL_CST) - { - obstack_1grow (&permanent_obstack, TYPE_PRECISION (TREE_TYPE (exp))); - strp = (char *) &TREE_REAL_CST (exp); - len = sizeof TREE_REAL_CST (exp); - } - else if (code == STRING_CST) - { - if (flag_writable_strings) - return; - strp = TREE_STRING_POINTER (exp); - len = TREE_STRING_LENGTH (exp); - obstack_grow (&permanent_obstack, (char *) &TREE_STRING_LENGTH (exp), - sizeof TREE_STRING_LENGTH (exp)); - } - else if (code == COMPLEX_CST) - { - record_constant_1 (TREE_REALPART (exp)); - record_constant_1 (TREE_IMAGPART (exp)); - return; - } - else if (code == CONSTRUCTOR) - { - register tree link; - int length = list_length (CONSTRUCTOR_ELTS (exp)); - tree type; - - obstack_grow (&permanent_obstack, (char *) &length, sizeof length); - - /* For record constructors, insist that the types match. - For arrays, just verify both constructors are for arrays. */ - if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE) - type = TREE_TYPE (exp); - else - type = 0; - obstack_grow (&permanent_obstack, (char *) &type, sizeof type); - - for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) - record_constant_1 (TREE_VALUE (link)); - return; - } - else if (code == ADDR_EXPR) - { - struct addr_const value; - decode_addr_const (exp, &value); - /* Record the SYMBOL_REF address and the offset. */ - obstack_grow (&permanent_obstack, (char *) &value, sizeof value); - /* Record the symbol name. */ - obstack_grow (&permanent_obstack, XSTR (value.base, 0), - strlen (XSTR (value.base, 0)) + 1); - return; - } - else if (code == PLUS_EXPR || code == MINUS_EXPR) - { - obstack_1grow (&permanent_obstack, (int) code); - record_constant_1 (TREE_OPERAND (exp, 0)); - record_constant_1 (TREE_OPERAND (exp, 1)); - return; - } - else if (code == NOP_EXPR || code == CONVERT_EXPR) - { - obstack_1grow (&permanent_obstack, (int) code); - record_constant_1 (TREE_OPERAND (exp, 0)); - return; - } - - /* Record constant contents. */ - obstack_grow (&permanent_obstack, strp, len); -} - -/* Return the constant-label-string for constant value EXP. - If no constant equal to EXP has yet been output, - define a new label and output assembler code for it. - The const_hash_table records which constants already have label strings. */ - -static char * -get_or_assign_label (exp) - tree exp; -{ - register int hash, i, align; - register struct constant_descriptor *desc; - char label[256]; - - /* Make sure any other constants whose addresses appear in EXP - are assigned label numbers. */ - - output_addressed_constants (exp); - - /* Compute hash code of EXP. Search the descriptors for that hash code - to see if any of them describes EXP. If yes, the descriptor records - the label number already assigned. */ - - hash = const_hash (exp) % MAX_HASH_TABLE; - - for (desc = const_hash_table[hash]; desc; desc = desc->next) - if (compare_constant (exp, desc)) - return desc->label; - - /* No constant equal to EXP is known to have been output. - Make a constant descriptor to enter EXP in the hash table. - Assign the label number and record it in the descriptor for - future calls to this function to find. */ - - desc = record_constant (exp); - desc->next = const_hash_table[hash]; - const_hash_table[hash] = desc; - - /* Now output assembler code to define that label - and follow it with the data of EXP. */ - - /* First switch to text section, except for writable strings. */ -#ifdef SELECT_SECTION - SELECT_SECTION (exp); -#else - if ((TREE_CODE (exp) == STRING_CST) && flag_writable_strings) - data_section (); - else - text_section (); -#endif - - /* Align the location counter as required by EXP's data type. */ -#ifdef CONSTANT_ALIGNMENT - align = CONSTANT_ALIGNMENT (TREE_CODE (exp), TYPE_ALIGN (TREE_TYPE (exp))); -#else - align = TYPE_ALIGN (TREE_TYPE (exp)); -#endif - - for (i = 0; align >= BITS_PER_UNIT << (i + 1); i++); - if (i > 0) - ASM_OUTPUT_ALIGN (asm_out_file, i); - - /* Output the label itself. */ - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LC", const_labelno); - - /* Output the value of EXP. */ - output_constant (exp, - (TREE_CODE (exp) == STRING_CST - ? TREE_STRING_LENGTH (exp) - : int_size_in_bytes (TREE_TYPE (exp)))); - - /* Create a string containing the label name, in LABEL. */ - ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno); - - ++const_labelno; - - desc->label - = (char *) obstack_copy0 (&permanent_obstack, label, strlen (label)); - - return desc->label; -} - -/* Return an rtx representing a reference to constant data in memory - for the constant expression EXP. - If assembler code for such a constant has already been output, - return an rtx to refer to it. - Otherwise, output such a constant in memory and generate - an rtx for it. The TREE_CST_RTL of EXP is set up to point to that rtx. */ - -rtx -output_constant_def (exp) - tree exp; -{ - register rtx def; - int temp_p = allocation_temporary_p (); - - if (TREE_CODE (exp) == INTEGER_CST) - abort (); /* No TREE_CST_RTL slot in these. */ - - if (TREE_CST_RTL (exp)) - return TREE_CST_RTL (exp); - - if (TREE_PERMANENT (exp)) - end_temporary_allocation (); - - def = gen_rtx (SYMBOL_REF, Pmode, get_or_assign_label (exp)); - - TREE_CST_RTL (exp) - = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), def); - RTX_UNCHANGING_P (TREE_CST_RTL (exp)) = 1; - - if (temp_p && TREE_PERMANENT (exp)) - resume_temporary_allocation (); - - return TREE_CST_RTL (exp); -} - -/* Similar hash facility for making memory-constants - from constant rtl-expressions. It is used on RISC machines - where immediate integer arguments and constant addresses are restricted - so that such constants must be stored in memory. - - This pool of constants is reinitialized for each function - so each function gets its own constants-pool that comes right before it. */ - -#define MAX_RTX_HASH_TABLE 61 -static struct constant_descriptor *const_rtx_hash_table[MAX_RTX_HASH_TABLE]; - -void -init_const_rtx_hash_table () -{ - bzero (const_rtx_hash_table, sizeof const_rtx_hash_table); -} - -struct rtx_const -{ - enum kind { RTX_DOUBLE, RTX_INT } kind : 16; - enum machine_mode mode : 16; - union { - union real_extract du; - struct addr_const addr; - } un; -}; - -/* Express an rtx for a constant integer (perhaps symbolic) - as the sum of a symbol or label plus an explicit integer. - They are stored into VALUE. */ - -static void -decode_rtx_const (mode, x, value) - enum machine_mode mode; - rtx x; - struct rtx_const *value; -{ - /* Clear the whole structure, including any gaps. */ - - { - int *p = (int *) value; - int *end = (int *) (value + 1); - while (p < end) - *p++ = 0; - } - - value->kind = RTX_INT; /* Most usual kind. */ - value->mode = mode; - - switch (GET_CODE (x)) - { - case CONST_DOUBLE: - value->kind = RTX_DOUBLE; - value->mode = GET_MODE (x); - bcopy (&CONST_DOUBLE_LOW (x), &value->un.du, sizeof value->un.du); - break; - - case CONST_INT: - value->un.addr.offset = INTVAL (x); - break; - - case SYMBOL_REF: - value->un.addr.base = x; - break; - - case LABEL_REF: - value->un.addr.base = x; - break; - - case CONST: - x = XEXP (x, 0); - if (GET_CODE (x) == PLUS) - { - value->un.addr.base = XEXP (XEXP (x, 0), 0); - if (GET_CODE (XEXP (x, 1)) != CONST_INT) - abort (); - value->un.addr.offset = INTVAL (XEXP (x, 1)); - } - else if (GET_CODE (x) == MINUS) - { - value->un.addr.base = XEXP (x, 0); - if (GET_CODE (XEXP (x, 1)) != CONST_INT) - abort (); - value->un.addr.offset = - INTVAL (XEXP (x, 1)); - } - else - abort (); - break; - - default: - abort (); - } - - if (value->kind == RTX_INT && value->un.addr.base != 0) - switch (GET_CODE (value->un.addr.base)) - { - case SYMBOL_REF: - case LABEL_REF: - /* Use the string's address, not the SYMBOL_REF's address, - for the sake of addresses of library routines. - For a LABEL_REF, compare labels. */ - value->un.addr.base = XEXP (value->un.addr.base, 0); - } -} - -/* Compute a hash code for a constant RTL expression. */ - -int -const_hash_rtx (mode, x) - enum machine_mode mode; - rtx x; -{ - register int hi, i; - - struct rtx_const value; - decode_rtx_const (mode, x, &value); - - /* Compute hashing function */ - hi = 0; - for (i = 0; i < sizeof value / sizeof (int); i++) - hi += ((int *) &value)[i]; - - hi &= (1 << HASHBITS) - 1; - hi %= MAX_RTX_HASH_TABLE; - return hi; -} - -/* Compare a constant rtl object X with a constant-descriptor DESC. - Return 1 if DESC describes a constant with the same value as X. */ - -static int -compare_constant_rtx (mode, x, desc) - enum machine_mode mode; - rtx x; - struct constant_descriptor *desc; -{ - register int *p = (int *) desc->contents; - register int *strp; - register int len; - struct rtx_const value; - - decode_rtx_const (mode, x, &value); - strp = (int *) &value; - len = sizeof value / sizeof (int); - - /* Compare constant contents. */ - while (--len >= 0) - if (*p++ != *strp++) - return 0; - - return 1; -} - -/* Construct a constant descriptor for the rtl-expression X. - It is up to the caller to enter the descriptor in the hash table. */ - -static struct constant_descriptor * -record_constant_rtx (mode, x) - enum machine_mode mode; - rtx x; -{ - struct constant_descriptor *ptr = 0; - int buf; - struct rtx_const value; - - decode_rtx_const (mode, x, &value); - - obstack_grow (saveable_obstack, &ptr, sizeof ptr); - obstack_grow (saveable_obstack, &buf, sizeof buf); - - /* Record constant contents. */ - obstack_grow (saveable_obstack, &value, sizeof value); - - return (struct constant_descriptor *) obstack_finish (saveable_obstack); -} - -/* Given a constant rtx X, make (or find) a memory constant for its value - and return a MEM rtx to refer to it in memory. */ - -rtx -force_const_mem (mode, x) - enum machine_mode mode; - rtx x; -{ - register int hash; - register struct constant_descriptor *desc; - char label[256]; - char *found = 0; - rtx def; - - if (GET_CODE (x) == CONST_DOUBLE - && GET_CODE (CONST_DOUBLE_MEM (x)) == MEM) - return CONST_DOUBLE_MEM (x); - - /* Compute hash code of X. Search the descriptors for that hash code - to see if any of them describes X. If yes, the descriptor records - the label number already assigned. */ - - hash = const_hash_rtx (mode, x); - - for (desc = const_rtx_hash_table[hash]; desc; desc = desc->next) - if (compare_constant_rtx (mode, x, desc)) - { - found = desc->label; - break; - } - - if (found == 0) - { - int align; - - /* No constant equal to X is known to have been output. - Make a constant descriptor to enter X in the hash table. - Assign the label number and record it in the descriptor for - future calls to this function to find. */ - - desc = record_constant_rtx (mode, x); - desc->next = const_rtx_hash_table[hash]; - const_rtx_hash_table[hash] = desc; - - /* Now output assembler code to define that label - and follow it with the data of EXP. */ - - /* First switch to text section. */ -#ifdef SELECT_RTX_SECTION - SELECT_RTX_SECTION (mode, x); -#else - text_section (); -#endif - - /* Align the location counter as required by EXP's data type. */ - align = (mode == VOIDmode) ? UNITS_PER_WORD : GET_MODE_SIZE (mode); - if (align > BIGGEST_ALIGNMENT / BITS_PER_UNIT) - align = BIGGEST_ALIGNMENT / BITS_PER_UNIT; - - if (align > 1) - ASM_OUTPUT_ALIGN (asm_out_file, exact_log2 (align)); - - /* Output the label itself. */ - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LC", const_labelno); - - /* Output the value of EXP. */ - if (GET_CODE (x) == CONST_DOUBLE) - { - union real_extract u; - - bcopy (&CONST_DOUBLE_LOW (x), &u, sizeof u); - switch (mode) - { - /* Perhaps change the following to use - CONST_DOUBLE_LOW and CONST_DOUBLE_HIGH, rather than u.i. */ - case DImode: -#ifdef ASM_OUTPUT_DOUBLE_INT - ASM_OUTPUT_DOUBLE_INT (asm_out_file, x); -#else /* no ASM_OUTPUT_DOUBLE_INT */ -#ifndef WORDS_BIG_ENDIAN - /* Output two ints. */ - ASM_OUTPUT_INT (asm_out_file, - gen_rtx (CONST_INT, VOIDmode, u.i[0])); - ASM_OUTPUT_INT (asm_out_file, - gen_rtx (CONST_INT, VOIDmode, u.i[1])); -#else - /* Output two ints. */ - ASM_OUTPUT_INT (asm_out_file, - gen_rtx (CONST_INT, VOIDmode, u.i[1])); - ASM_OUTPUT_INT (asm_out_file, - gen_rtx (CONST_INT, VOIDmode, u.i[0])); -#endif /* WORDS_BIG_ENDIAN */ -#endif /* no ASM_OUTPUT_DOUBLE_INT */ - break; - - case DFmode: - ASM_OUTPUT_DOUBLE (asm_out_file, u.d); - break; - - case SFmode: - ASM_OUTPUT_FLOAT (asm_out_file, u.d); - } - } - else - switch (mode) - { - case SImode: - ASM_OUTPUT_INT (asm_out_file, x); - break; - - case HImode: - ASM_OUTPUT_SHORT (asm_out_file, x); - break; - - case QImode: - ASM_OUTPUT_CHAR (asm_out_file, x); - break; - } - - /* Create a string containing the label name, in LABEL. */ - ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno); - - ++const_labelno; - - desc->label = found - = (char *) obstack_copy0 (&permanent_obstack, label, strlen (label)); - } - - /* We have a symbol name; construct the SYMBOL_REF and the MEM. */ - - def = gen_rtx (MEM, mode, gen_rtx (SYMBOL_REF, Pmode, desc->label)); - - RTX_UNCHANGING_P (def) = 1; - /* Mark the symbol_ref as belonging to this constants pool. */ - CONSTANT_POOL_ADDRESS_P (XEXP (def, 0)) = 1; - - if (GET_CODE (x) == CONST_DOUBLE) - { - if (CONST_DOUBLE_MEM (x) == cc0_rtx) - { - CONST_DOUBLE_CHAIN (x) = real_constant_chain; - real_constant_chain = x; - } - CONST_DOUBLE_MEM (x) = def; - } - - return def; -} - -/* Find all the constants whose addresses are referenced inside of EXP, - and make sure assembler code with a label has been output for each one. */ - -void -output_addressed_constants (exp) - tree exp; -{ - switch (TREE_CODE (exp)) - { - case ADDR_EXPR: - { - register tree constant = TREE_OPERAND (exp, 0); - - while (TREE_CODE (constant) == COMPONENT_REF) - { - constant = TREE_OPERAND (constant, 0); - } - - if (TREE_LITERAL (constant)) - /* No need to do anything here - for addresses of variables or functions. */ - output_constant_def (constant); - } - break; - - case PLUS_EXPR: - case MINUS_EXPR: - output_addressed_constants (TREE_OPERAND (exp, 0)); - output_addressed_constants (TREE_OPERAND (exp, 1)); - break; - - case NOP_EXPR: - case CONVERT_EXPR: - output_addressed_constants (TREE_OPERAND (exp, 0)); - break; - - case CONSTRUCTOR: - { - register tree link; - for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) - output_addressed_constants (TREE_VALUE (link)); - } - break; - - case ERROR_MARK: - break; - - default: - if (! TREE_LITERAL (exp)) - abort (); - } -} - -/* Output assembler code for constant EXP to FILE, with no label. - This includes the pseudo-op such as ".int" or ".byte", and a newline. - Assumes output_addressed_constants has been done on EXP already. - - Generate exactly SIZE bytes of assembler data, padding at the end - with zeros if necessary. SIZE must always be specified. - - SIZE is important for structure constructors, - since trailing members may have been omitted from the constructor. - It is also important for initialization of arrays from string constants - since the full length of the string constant might not be wanted. - It is also needed for initialization of unions, where the initializer's - type is just one member, and that may not be as long as the union. - - There a case in which we would fail to output exactly SIZE bytes: - for a structure constructor that wants to produce more than SIZE bytes. - But such constructors will never be generated for any possible input. */ - -void -output_constant (exp, size) - register tree exp; - register int size; -{ - register enum tree_code code = TREE_CODE (TREE_TYPE (exp)); - rtx x; - - if (size == 0) - return; - - /* Eliminate the NOP_EXPR that makes a cast not be an lvalue. - That way we get the constant (we hope) inside it. */ - if (TREE_CODE (exp) == NOP_EXPR - && TREE_TYPE (exp) == TREE_TYPE (TREE_OPERAND (exp, 0))) - exp = TREE_OPERAND (exp, 0); - - switch (code) - { - case INTEGER_TYPE: - case ENUMERAL_TYPE: - case POINTER_TYPE: - case REFERENCE_TYPE: - /* ??? What about (int)((float)(int)&foo + 4) */ - while (TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR) - exp = TREE_OPERAND (exp, 0); - -#ifndef ASM_OUTPUT_DOUBLE_INT - if (TYPE_MODE (TREE_TYPE (exp)) == DImode) - { - if (TREE_CODE (exp) == INTEGER_CST) - { -#ifndef WORDS_BIG_ENDIAN - ASM_OUTPUT_INT (asm_out_file, - gen_rtx (CONST_INT, VOIDmode, - TREE_INT_CST_LOW (exp))); - ASM_OUTPUT_INT (asm_out_file, - gen_rtx (CONST_INT, VOIDmode, - TREE_INT_CST_HIGH (exp))); -#else - ASM_OUTPUT_INT (asm_out_file, - gen_rtx (CONST_INT, VOIDmode, - TREE_INT_CST_HIGH (exp))); - ASM_OUTPUT_INT (asm_out_file, - gen_rtx (CONST_INT, VOIDmode, - TREE_INT_CST_LOW (exp))); -#endif - size -= 8; - break; - } - else - error ("8-byte integer constant expression too complicated"); - - break; - } -#endif /* no ASM_OUTPUT_DOUBLE_INT */ - - x = expand_expr (exp, 0, VOIDmode, EXPAND_SUM); - - if (size == 1) - { - ASM_OUTPUT_CHAR (asm_out_file, x); - size -= 1; - } - else if (size == 2) - { - ASM_OUTPUT_SHORT (asm_out_file, x); - size -= 2; - } - else if (size == 4) - { - ASM_OUTPUT_INT (asm_out_file, x); - size -= 4; - } -#ifdef ASM_OUTPUT_DOUBLE_INT - else if (size == 8) - { - ASM_OUTPUT_DOUBLE_INT (asm_out_file, x); - size -= 8; - } -#endif /* ASM_OUTPUT_DOUBLE_INT */ - else - abort (); - - break; - - case REAL_TYPE: - if (TREE_CODE (exp) != REAL_CST) - error ("initializer for floating value is not a floating constant"); - else - { - REAL_VALUE_TYPE d; - jmp_buf output_constant_handler; - - d = TREE_REAL_CST (exp); - if (setjmp (output_constant_handler)) - { - error ("floating point trap outputting a constant"); -#ifdef REAL_IS_NOT_DOUBLE - bzero (&d, sizeof d); - d = REAL_VALUE_ATOF ("0"); -#else - d = 0; -#endif - } - set_float_handler (output_constant_handler); - - if (size < 4) - break; - else if (size < 8) - { - ASM_OUTPUT_FLOAT (asm_out_file, d); - size -= 4; - } - else - { - ASM_OUTPUT_DOUBLE (asm_out_file, d); - size -= 8; - } - set_float_handler (0); - } - break; - - case COMPLEX_TYPE: - output_constant (TREE_REALPART (exp), size / 2); - output_constant (TREE_IMAGPART (exp), size / 2); - size -= (size / 2) * 2; - break; - - case ARRAY_TYPE: - if (TREE_CODE (exp) == CONSTRUCTOR) - { - output_constructor (exp, size); - return; - } - else if (TREE_CODE (exp) == STRING_CST) - { - int excess = 0; - - if (size > TREE_STRING_LENGTH (exp)) - { - excess = size - TREE_STRING_LENGTH (exp); - size = TREE_STRING_LENGTH (exp); - } - - assemble_string (TREE_STRING_POINTER (exp), size); - size = excess; - } - else - abort (); - break; - - case RECORD_TYPE: - case UNION_TYPE: - if (TREE_CODE (exp) == CONSTRUCTOR) - output_constructor (exp, size); - else - abort (); - return; - } - - if (size > 0) - ASM_OUTPUT_SKIP (asm_out_file, size); -} - -/* Subroutine of output_constant, used for CONSTRUCTORs - (aggregate constants). - Generate at least SIZE bytes, padding if necessary. */ - -void -output_constructor (exp, size) - tree exp; - int size; -{ - register tree link, field = 0; - /* Number of bytes output or skipped so far. - In other words, current position within the constructor. */ - int total_bytes = 0; - /* Non-zero means BYTE contains part of a byte, to be output. */ - int byte_buffer_in_use = 0; - register int byte; - - if (HOST_BITS_PER_INT < BITS_PER_UNIT) - abort (); - - if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE - || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE) - field = TYPE_FIELDS (TREE_TYPE (exp)); - - /* As LINK goes through the elements of the constant, - FIELD goes through the structure fields, if the constant is a structure. - But the constant could also be an array. Then FIELD is zero. */ - for (link = CONSTRUCTOR_ELTS (exp); - link; - link = TREE_CHAIN (link), - field = field ? TREE_CHAIN (field) : 0) - { - tree val = TREE_VALUE (link); - - /* Eliminate the NOP_EXPR that makes a cast not be an lvalue. */ - if (TREE_CODE (val) == NOP_EXPR - && TREE_TYPE (val) == TREE_TYPE (TREE_OPERAND (val, 0))) - val = TREE_OPERAND (val, 0); - - if (field == 0 - || (DECL_MODE (field) != BImode)) - { - register int fieldsize; - - /* An element that is not a bit-field. - Output any buffered-up bit-fields preceding it. */ - if (byte_buffer_in_use) - { - ASM_OUTPUT_BYTE (asm_out_file, byte); - total_bytes++; - byte_buffer_in_use = 0; - } - - /* Advance to offset of this element. - Note no alignment needed in an array, since that is guaranteed - if each element has the proper size. */ - if (field != 0 && DECL_OFFSET (field) / BITS_PER_UNIT != total_bytes) - { - ASM_OUTPUT_SKIP (asm_out_file, - (DECL_OFFSET (field) / BITS_PER_UNIT - - total_bytes)); - total_bytes = DECL_OFFSET (field) / BITS_PER_UNIT; - } - - /* Determine size this element should occupy. */ - if (field) - { - if (! TREE_LITERAL (DECL_SIZE (field))) - abort (); - fieldsize = TREE_INT_CST_LOW (DECL_SIZE (field)) - * DECL_SIZE_UNIT (field); - fieldsize = (fieldsize + BITS_PER_UNIT - 1) / BITS_PER_UNIT; - } - else - fieldsize = int_size_in_bytes (TREE_TYPE (TREE_TYPE (exp))); - - /* Output the element's initial value. */ - output_constant (val, fieldsize); - - /* Count its size. */ - total_bytes += fieldsize; - } - else if (TREE_CODE (val) != INTEGER_CST) - error ("invalid initial value for member `%s'", - IDENTIFIER_POINTER (DECL_NAME (field))); - else - { - /* Element that is a bit-field. */ - - int next_offset = DECL_OFFSET (field); - int end_offset - = (next_offset - + (TREE_INT_CST_LOW (DECL_SIZE (field)) - * DECL_SIZE_UNIT (field))); - - /* If this field does not start in this (or, next) byte, - skip some bytes. */ - if (next_offset / BITS_PER_UNIT != total_bytes) - { - /* Output remnant of any bit field in previous bytes. */ - if (byte_buffer_in_use) - { - ASM_OUTPUT_BYTE (asm_out_file, byte); - total_bytes++; - byte_buffer_in_use = 0; - } - - /* If still not at proper byte, advance to there. */ - if (next_offset / BITS_PER_UNIT != total_bytes) - { - ASM_OUTPUT_SKIP (asm_out_file, - next_offset / BITS_PER_UNIT - total_bytes); - total_bytes = next_offset / BITS_PER_UNIT; - } - } - - if (! byte_buffer_in_use) - byte = 0; - - /* We must split the element into pieces that fall within - separate bytes, and combine each byte with previous or - following bit-fields. */ - - /* next_offset is the offset n fbits from the begining of - the structure to the next bit of this element to be processed. - end_offset is the offset of the first bit past the end of - this element. */ - while (next_offset < end_offset) - { - int this_time; - int next_byte = next_offset / BITS_PER_UNIT; - int next_bit = next_offset % BITS_PER_UNIT; - - /* Advance from byte to byte - within this element when necessary. */ - while (next_byte != total_bytes) - { - ASM_OUTPUT_BYTE (asm_out_file, byte); - total_bytes++; - byte = 0; - } - - /* Number of bits we can process at once - (all part of the same byte). */ - this_time = MIN (end_offset - next_offset, - BITS_PER_UNIT - next_bit); -#ifdef BYTES_BIG_ENDIAN - /* On big-endian machine, take the most significant bits - first (of the bits that are significant) - and put them into bytes from the most significant end. */ - byte |= (((TREE_INT_CST_LOW (val) - >> (end_offset - next_offset - this_time)) - & ((1 << this_time) - 1)) - << (BITS_PER_UNIT - this_time - next_bit)); -#else - /* On little-endian machines, - take first the least significant bits of the value - and pack them starting at the least significant - bits of the bytes. */ - byte |= ((TREE_INT_CST_LOW (val) - >> (next_offset - DECL_OFFSET (field))) - & ((1 << this_time) - 1)) << next_bit; -#endif - next_offset += this_time; - byte_buffer_in_use = 1; - } - } - } - if (byte_buffer_in_use) - { - ASM_OUTPUT_BYTE (asm_out_file, byte); - total_bytes++; - } - if (total_bytes < size) - ASM_OUTPUT_SKIP (asm_out_file, size - total_bytes); -} diff --git a/gnu/usr.bin/gcc1/cc1/version.c b/gnu/usr.bin/gcc1/cc1/version.c deleted file mode 100644 index a1d88c1445..0000000000 --- a/gnu/usr.bin/gcc1/cc1/version.c +++ /dev/null @@ -1 +0,0 @@ -char *version_string = "1.39"; diff --git a/gnu/usr.bin/gcc1/cpp/Makefile b/gnu/usr.bin/gcc1/cpp/Makefile deleted file mode 100644 index 4ae42d1998..0000000000 --- a/gnu/usr.bin/gcc1/cpp/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# @(#)Makefile 6.4 (Berkeley) 2/21/91 - -PROG= cpp -BINDIR= /usr/libexec -SRCS= cpp.c cexp.y version.c obstack.c -CFLAGS+= -I. -I$(.CURDIR)/../cc1 -I$(.CURDIR)/../cc1/config \ - -DGCC_INCLUDE_DIR=\"/usr/include/gcc\" \ - -DGPLUSPLUS_INCLUDE_DIR=\"/usr/include/g++\" -YFLAGS= -NOMAN= noman -.PATH: $(.CURDIR)/../cc1 - -.include - -afterinstall: - install -c -o $(BINOWN) -g $(BINGRP) -m $(BINMODE) \ - $(.CURDIR)/usr.bin.cpp.sh $(DESTDIR)/usr/bin/cpp - -.include "../Makefile.symlinks" diff --git a/gnu/usr.bin/gcc1/cpp/cexp.y b/gnu/usr.bin/gcc1/cpp/cexp.y deleted file mode 100644 index 88f2039b55..0000000000 --- a/gnu/usr.bin/gcc1/cpp/cexp.y +++ /dev/null @@ -1,656 +0,0 @@ -/* Parse C expressions for CCCP. - Copyright (C) 1987 Free Software Foundation. - -This program is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 1, or (at your option) any -later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - In other words, you are welcome to use, share and improve this program. - You are forbidden to forbid anyone else to use, share and improve - what you give them. Help stamp out software-hoarding! - - Adapted from expread.y of GDB by Paul Rubin, July 1986. - -/* Parse a C expression from text in a string */ - -%{ -#include "config.h" -#include -/* #define YYDEBUG 1 */ - - int yylex (); - void yyerror (); - int expression_value; - - static jmp_buf parse_return_error; - - /* some external tables of character types */ - extern unsigned char is_idstart[], is_idchar[]; - -#ifndef CHAR_TYPE_SIZE -#define CHAR_TYPE_SIZE BITS_PER_UNIT -#endif -%} - -%union { - struct constant {long value; int unsignedp;} integer; - int voidval; - char *sval; -} - -%type exp exp1 start -%token INT CHAR -%token NAME -%token ERROR - -%right '?' ':' -%left ',' -%left OR -%left AND -%left '|' -%left '^' -%left '&' -%left EQUAL NOTEQUAL -%left '<' '>' LEQ GEQ -%left LSH RSH -%left '+' '-' -%left '*' '/' '%' -%right UNARY - -/* %expect 40 */ - -%% - -start : exp1 - { expression_value = $1.value; } - ; - -/* Expressions, including the comma operator. */ -exp1 : exp - | exp1 ',' exp - { $$ = $3; } - ; - -/* Expressions, not including the comma operator. */ -exp : '-' exp %prec UNARY - { $$.value = - $2.value; - $$.unsignedp = $2.unsignedp; } - | '!' exp %prec UNARY - { $$.value = ! $2.value; - $$.unsignedp = 0; } - | '+' exp %prec UNARY - { $$ = $2; } - | '~' exp %prec UNARY - { $$.value = ~ $2.value; - $$.unsignedp = $2.unsignedp; } - | '(' exp1 ')' - { $$ = $2; } - ; - -/* Binary operators in order of decreasing precedence. */ -exp : exp '*' exp - { $$.unsignedp = $1.unsignedp || $3.unsignedp; - if ($$.unsignedp) - $$.value = (unsigned) $1.value * $3.value; - else - $$.value = $1.value * $3.value; } - | exp '/' exp - { if ($3.value == 0) - { - error ("division by zero in #if"); - $3.value = 1; - } - $$.unsignedp = $1.unsignedp || $3.unsignedp; - if ($$.unsignedp) - $$.value = (unsigned) $1.value / $3.value; - else - $$.value = $1.value / $3.value; } - | exp '%' exp - { if ($3.value == 0) - { - error ("division by zero in #if"); - $3.value = 1; - } - $$.unsignedp = $1.unsignedp || $3.unsignedp; - if ($$.unsignedp) - $$.value = (unsigned) $1.value % $3.value; - else - $$.value = $1.value % $3.value; } - | exp '+' exp - { $$.value = $1.value + $3.value; - $$.unsignedp = $1.unsignedp || $3.unsignedp; } - | exp '-' exp - { $$.value = $1.value - $3.value; - $$.unsignedp = $1.unsignedp || $3.unsignedp; } - | exp LSH exp - { $$.unsignedp = $1.unsignedp; - if ($$.unsignedp) - $$.value = (unsigned) $1.value << $3.value; - else - $$.value = $1.value << $3.value; } - | exp RSH exp - { $$.unsignedp = $1.unsignedp; - if ($$.unsignedp) - $$.value = (unsigned) $1.value >> $3.value; - else - $$.value = $1.value >> $3.value; } - | exp EQUAL exp - { $$.value = ($1.value == $3.value); - $$.unsignedp = 0; } - | exp NOTEQUAL exp - { $$.value = ($1.value != $3.value); - $$.unsignedp = 0; } - | exp LEQ exp - { $$.unsignedp = 0; - if ($1.unsignedp || $3.unsignedp) - $$.value = (unsigned) $1.value <= $3.value; - else - $$.value = $1.value <= $3.value; } - | exp GEQ exp - { $$.unsignedp = 0; - if ($1.unsignedp || $3.unsignedp) - $$.value = (unsigned) $1.value >= $3.value; - else - $$.value = $1.value >= $3.value; } - | exp '<' exp - { $$.unsignedp = 0; - if ($1.unsignedp || $3.unsignedp) - $$.value = (unsigned) $1.value < $3.value; - else - $$.value = $1.value < $3.value; } - | exp '>' exp - { $$.unsignedp = 0; - if ($1.unsignedp || $3.unsignedp) - $$.value = (unsigned) $1.value > $3.value; - else - $$.value = $1.value > $3.value; } - | exp '&' exp - { $$.value = $1.value & $3.value; - $$.unsignedp = $1.unsignedp || $3.unsignedp; } - | exp '^' exp - { $$.value = $1.value ^ $3.value; - $$.unsignedp = $1.unsignedp || $3.unsignedp; } - | exp '|' exp - { $$.value = $1.value | $3.value; - $$.unsignedp = $1.unsignedp || $3.unsignedp; } - | exp AND exp - { $$.value = ($1.value && $3.value); - $$.unsignedp = 0; } - | exp OR exp - { $$.value = ($1.value || $3.value); - $$.unsignedp = 0; } - | exp '?' exp ':' exp - { $$.value = $1.value ? $3.value : $5.value; - $$.unsignedp = $3.unsignedp || $5.unsignedp; } - | INT - { $$ = yylval.integer; } - | CHAR - { $$ = yylval.integer; } - | NAME - { $$.value = 0; - $$.unsignedp = 0; } - ; -%% - -/* During parsing of a C expression, the pointer to the next character - is in this variable. */ - -static char *lexptr; - -/* Take care of parsing a number (anything that starts with a digit). - Set yylval and return the token type; update lexptr. - LEN is the number of characters in it. */ - -/* maybe needs to actually deal with floating point numbers */ - -int -parse_number (olen) - int olen; -{ - register char *p = lexptr; - register long n = 0; - register int c; - register int base = 10; - register int len = olen; - - for (c = 0; c < len; c++) - if (p[c] == '.') { - /* It's a float since it contains a point. */ - yyerror ("floating point numbers not allowed in #if expressions"); - return ERROR; - } - - yylval.integer.unsignedp = 0; - - if (len >= 3 && (!strncmp (p, "0x", 2) || !strncmp (p, "0X", 2))) { - p += 2; - base = 16; - len -= 2; - } - else if (*p == '0') - base = 8; - - while (len > 0) { - c = *p++; - len--; - if (c >= 'A' && c <= 'Z') c += 'a' - 'A'; - - if (c >= '0' && c <= '9') { - n *= base; - n += c - '0'; - } else if (base == 16 && c >= 'a' && c <= 'f') { - n *= base; - n += c - 'a' + 10; - } else { - /* `l' means long, and `u' means unsigned. */ - while (1) { - if (c == 'l' || c == 'L') - ; - else if (c == 'u' || c == 'U') - yylval.integer.unsignedp = 1; - else - break; - - if (len == 0) - break; - c = *p++; - len--; - } - /* Don't look for any more digits after the suffixes. */ - break; - } - } - - if (len != 0) { - yyerror ("Invalid number in #if expression"); - return ERROR; - } - - /* If too big to be signed, consider it unsigned. */ - if (n < 0) - yylval.integer.unsignedp = 1; - - lexptr = p; - yylval.integer.value = n; - return INT; -} - -struct token { - char *operator; - int token; -}; - -#ifndef NULL -#define NULL 0 -#endif - -static struct token tokentab2[] = { - {"&&", AND}, - {"||", OR}, - {"<<", LSH}, - {">>", RSH}, - {"==", EQUAL}, - {"!=", NOTEQUAL}, - {"<=", LEQ}, - {">=", GEQ}, - {NULL, ERROR} -}; - -/* Read one token, getting characters through lexptr. */ - -int -yylex () -{ - register int c; - register int namelen; - register char *tokstart; - register struct token *toktab; - - retry: - - tokstart = lexptr; - c = *tokstart; - /* See if it is a special token of length 2. */ - for (toktab = tokentab2; toktab->operator != NULL; toktab++) - if (c == *toktab->operator && tokstart[1] == toktab->operator[1]) { - lexptr += 2; - return toktab->token; - } - - switch (c) { - case 0: - return 0; - - case ' ': - case '\t': - case '\r': - case '\n': - lexptr++; - goto retry; - - case '\'': - lexptr++; - c = *lexptr++; - if (c == '\\') - c = parse_escape (&lexptr); - - /* Sign-extend the constant if chars are signed on target machine. */ - { - if (lookup ("__CHAR_UNSIGNED__", sizeof ("__CHAR_UNSIGNED__")-1, -1) - || ((c >> (CHAR_TYPE_SIZE - 1)) & 1) == 0) - yylval.integer.value = c & ((1 << CHAR_TYPE_SIZE) - 1); - else - yylval.integer.value = c | ~((1 << CHAR_TYPE_SIZE) - 1); - } - - yylval.integer.unsignedp = 0; - c = *lexptr++; - if (c != '\'') { - yyerror ("Invalid character constant in #if"); - return ERROR; - } - - return CHAR; - - /* some of these chars are invalid in constant expressions; - maybe do something about them later */ - case '/': - case '+': - case '-': - case '*': - case '%': - case '|': - case '&': - case '^': - case '~': - case '!': - case '@': - case '<': - case '>': - case '(': - case ')': - case '[': - case ']': - case '.': - case '?': - case ':': - case '=': - case '{': - case '}': - case ',': - lexptr++; - return c; - - case '"': - yyerror ("double quoted strings not allowed in #if expressions"); - return ERROR; - } - if (c >= '0' && c <= '9') { - /* It's a number */ - for (namelen = 0; - c = tokstart[namelen], is_idchar[c] || c == '.'; - namelen++) - ; - return parse_number (namelen); - } - - if (!is_idstart[c]) { - yyerror ("Invalid token in expression"); - return ERROR; - } - - /* It is a name. See how long it is. */ - - for (namelen = 0; is_idchar[tokstart[namelen]]; namelen++) - ; - - lexptr += namelen; - return NAME; -} - - -/* Parse a C escape sequence. STRING_PTR points to a variable - containing a pointer to the string to parse. That pointer - is updated past the characters we use. The value of the - escape sequence is returned. - - A negative value means the sequence \ newline was seen, - which is supposed to be equivalent to nothing at all. - - If \ is followed by a null character, we return a negative - value and leave the string pointer pointing at the null character. - - If \ is followed by 000, we return 0 and leave the string pointer - after the zeros. A value of 0 does not mean end of string. */ - -int -parse_escape (string_ptr) - char **string_ptr; -{ - register int c = *(*string_ptr)++; - switch (c) - { - case 'a': - return TARGET_BELL; - case 'b': - return TARGET_BS; - case 'e': - return 033; - case 'f': - return TARGET_FF; - case 'n': - return TARGET_NEWLINE; - case 'r': - return TARGET_CR; - case 't': - return TARGET_TAB; - case 'v': - return TARGET_VT; - case '\n': - return -2; - case 0: - (*string_ptr)--; - return 0; - case '^': - c = *(*string_ptr)++; - if (c == '\\') - c = parse_escape (string_ptr); - if (c == '?') - return 0177; - return (c & 0200) | (c & 037); - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - { - register int i = c - '0'; - register int count = 0; - while (++count < 3) - { - c = *(*string_ptr)++; - if (c >= '0' && c <= '7') - i = (i << 3) + c - '0'; - else - { - (*string_ptr)--; - break; - } - } - if ((i & ~((1 << CHAR_TYPE_SIZE) - 1)) != 0) - { - i &= (1 << CHAR_TYPE_SIZE) - 1; - warning ("octal character constant does not fit in a byte"); - } - return i; - } - case 'x': - { - register int i = 0; - register int count = 0; - for (;;) - { - c = *(*string_ptr)++; - if (c >= '0' && c <= '9') - i = (i << 4) + c - '0'; - else if (c >= 'a' && c <= 'f') - i = (i << 4) + c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - i = (i << 4) + c - 'A' + 10; - else - { - (*string_ptr)--; - break; - } - } - if ((i & ~((1 << BITS_PER_UNIT) - 1)) != 0) - { - i &= (1 << BITS_PER_UNIT) - 1; - warning ("hex character constant does not fit in a byte"); - } - return i; - } - default: - return c; - } -} - -void -yyerror (s) - char *s; -{ - error (s); - longjmp (parse_return_error, 1); -} - -/* This page contains the entry point to this file. */ - -/* Parse STRING as an expression, and complain if this fails - to use up all of the contents of STRING. */ -/* We do not support C comments. They should be removed before - this function is called. */ - -int -parse_c_expression (string) - char *string; -{ - lexptr = string; - - if (lexptr == 0 || *lexptr == 0) { - error ("empty #if expression"); - return 0; /* don't include the #if group */ - } - - /* if there is some sort of scanning error, just return 0 and assume - the parsing routine has printed an error message somewhere. - there is surely a better thing to do than this. */ - if (setjmp (parse_return_error)) - return 0; - - if (yyparse ()) - return 0; /* actually this is never reached - the way things stand. */ - if (*lexptr) - error ("Junk after end of expression."); - - return expression_value; /* set by yyparse () */ -} - -#ifdef TEST_EXP_READER -/* main program, for testing purposes. */ -main () -{ - int n, c; - char buf[1024]; - extern int yydebug; -/* - yydebug = 1; -*/ - initialize_random_junk (); - - for (;;) { - printf ("enter expression: "); - n = 0; - while ((buf[n] = getchar ()) != '\n' && buf[n] != EOF) - n++; - if (buf[n] == EOF) - break; - buf[n] = '\0'; - printf ("parser returned %d\n", parse_c_expression (buf)); - } -} - -/* table to tell if char can be part of a C identifier. */ -unsigned char is_idchar[256]; -/* table to tell if char can be first char of a c identifier. */ -unsigned char is_idstart[256]; -/* table to tell if c is horizontal space. isspace () thinks that - newline is space; this is not a good idea for this program. */ -char is_hor_space[256]; - -/* - * initialize random junk in the hash table and maybe other places - */ -initialize_random_junk () -{ - register int i; - - /* - * Set up is_idchar and is_idstart tables. These should be - * faster than saying (is_alpha (c) || c == '_'), etc. - * Must do set up these things before calling any routines tthat - * refer to them. - */ - for (i = 'a'; i <= 'z'; i++) { - ++is_idchar[i - 'a' + 'A']; - ++is_idchar[i]; - ++is_idstart[i - 'a' + 'A']; - ++is_idstart[i]; - } - for (i = '0'; i <= '9'; i++) - ++is_idchar[i]; - ++is_idchar['_']; - ++is_idstart['_']; -#if DOLLARS_IN_IDENTIFIERS - ++is_idchar['$']; - ++is_idstart['$']; -#endif - - /* horizontal space table */ - ++is_hor_space[' ']; - ++is_hor_space['\t']; -} - -error (msg) -{ - printf ("error: %s\n", msg); -} - -warning (msg) -{ - printf ("warning: %s\n", msg); -} - -struct hashnode * -lookup (name, len, hash) - char *name; - int len; - int hash; -{ - return (DEFAULT_SIGNED_CHAR) ? 0 : ((struct hashnode *) -1); -} -#endif diff --git a/gnu/usr.bin/gcc1/cpp/cpp.c b/gnu/usr.bin/gcc1/cpp/cpp.c deleted file mode 100644 index 4ddc60c72a..0000000000 --- a/gnu/usr.bin/gcc1/cpp/cpp.c +++ /dev/null @@ -1,5753 +0,0 @@ -/*- - * This code is derived from software copyrighted by the Free Software - * Foundation. - * - * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. - */ - -#ifndef lint -static char sccsid[] = "@(#)cpp.c 6.3 (Berkeley) 5/8/91"; -#endif /* not lint */ - -/* C Compatible Compiler Preprocessor (CCCP) -Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. - Written by Paul Rubin, June 1986 - Adapted to ANSI C, Richard Stallman, Jan 1987 - -This program is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 1, or (at your option) any -later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - In other words, you are welcome to use, share and improve this program. - You are forbidden to forbid anyone else to use, share and improve - what you give them. Help stamp out software-hoarding! */ - -typedef unsigned char U_CHAR; - -#ifdef EMACS -#define NO_SHORTNAMES -#include "../src/config.h" -#ifdef open -#undef open -#undef read -#undef write -#endif /* open */ -#endif /* EMACS */ - -#ifndef EMACS -#include "config.h" -#endif /* not EMACS */ - -#ifndef STDC_VALUE -#define STDC_VALUE 1 -#endif - -/* In case config.h defines these. */ -#undef bcopy -#undef bzero -#undef bcmp - -#include -#include -#include -#include -#include - -#ifndef VMS -#include -#ifndef USG -#include /* for __DATE__ and __TIME__ */ -#include -#else -#define index strchr -#define rindex strrchr -#include -#include -#endif /* USG */ -#endif /* not VMS */ - -/* VMS-specific definitions */ -#ifdef VMS -#include -#include /* This defines "errno" properly */ -#include /* This defines sys_errlist/sys_nerr properly */ -#define O_RDONLY 0 /* Open arg for Read/Only */ -#define O_WRONLY 1 /* Open arg for Write/Only */ -#define read(fd,buf,size) VAX11_C_read(fd,buf,size) -#define write(fd,buf,size) VAX11_C_write(fd,buf,size) -#ifdef __GNUC__ -#define BSTRING /* VMS/GCC supplies the bstring routines */ -#endif /* __GNUC__ */ -#endif /* VMS */ - -#ifndef O_RDONLY -#define O_RDONLY 0 -#endif - -#define max(a,b) ((a) > (b) ? (a) : (b)) - -#ifndef S_ISREG -#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) -#endif - -/* External declarations. */ - -void bcopy (), bzero (); -int bcmp (); -extern char *getenv (); -extern char *version_string; - -/* Forward declarations. */ - -struct directive; -struct file_buf; -struct arglist; -struct argdata; - -int do_define (), do_line (), do_include (), do_undef (), do_error (), - do_pragma (), do_if (), do_xifdef (), do_else (), - do_elif (), do_endif (), do_sccs (), do_once (); - -struct hashnode *install (); -struct hashnode *lookup (); - -char *xmalloc (), *xrealloc (), *xcalloc (), *savestring (); -void fatal (), fancy_abort (), pfatal_with_name (), perror_with_name (); - -void macroexpand (); -void dump_all_macros (); -void conditional_skip (); -void skip_if_group (); -void output_line_command (); -/* Last arg to output_line_command. */ -enum file_change_code {same_file, enter_file, leave_file}; - -int grow_outbuf (); -int handle_directive (); -void memory_full (); - -U_CHAR *macarg1 (); -char *macarg (); - -U_CHAR *skip_to_end_of_comment (); -U_CHAR *skip_quoted_string (); - -#ifndef FATAL_EXIT_CODE -#define FATAL_EXIT_CODE 33 /* gnu cc command understands this */ -#endif - -#ifndef SUCCESS_EXIT_CODE -#define SUCCESS_EXIT_CODE 0 /* 0 means success on Unix. */ -#endif - -/* Name under which this program was invoked. */ - -char *progname; - -/* Nonzero means handle C++ comment syntax and use - extra default include directories for C++. */ - -int cplusplus; - -/* Current maximum length of directory names in the search path - for include files. (Altered as we get more of them.) */ - -int max_include_len; - -/* Nonzero means copy comments into the output file. */ - -int put_out_comments = 0; - -/* Nonzero means don't process the ANSI trigraph sequences. */ - -int no_trigraphs = 0; - -/* Nonzero means print the names of included files rather than - the preprocessed output. 1 means just the #include "...", - 2 means #include <...> as well. */ - -int print_deps = 0; - -/* Nonzero means don't output line number information. */ - -int no_line_commands; - -/* Nonzero means inhibit output of the preprocessed text - and instead output the definitions of all user-defined macros - in a form suitable for use as input to cccp. */ - -int dump_macros; - -/* Nonzero means give all the error messages the ANSI standard requires. */ - -int pedantic; - -/* Nonzero means don't print warning messages. -w. */ - -int inhibit_warnings = 0; - -/* Nonzero means warn if slash-star appears in a comment. */ - -int warn_comments; - -/* Nonzero means warn if there are any trigraphs. */ - -int warn_trigraphs; - -/* Nonzero means try to imitate old fashioned non-ANSI preprocessor. */ - -int traditional; - -/* Nonzero causes output not to be done, - but directives such as #define that have side effects - are still obeyed. */ - -int no_output; - -/* I/O buffer structure. - The `fname' field is nonzero for source files and #include files - and for the dummy text used for -D and -U. - It is zero for rescanning results of macro expansion - and for expanding macro arguments. */ -#define INPUT_STACK_MAX 200 -struct file_buf { - char *fname; - int lineno; - int length; - U_CHAR *buf; - U_CHAR *bufp; - /* Macro that this level is the expansion of. - Included so that we can reenable the macro - at the end of this level. */ - struct hashnode *macro; - /* Value of if_stack at start of this file. - Used to prohibit unmatched #endif (etc) in an include file. */ - struct if_stack *if_stack; - /* Object to be freed at end of input at this level. */ - U_CHAR *free_ptr; -} instack[INPUT_STACK_MAX]; - -/* Current nesting level of input sources. - `instack[indepth]' is the level currently being read. */ -int indepth = -1; -#define CHECK_DEPTH(code) \ - if (indepth >= (INPUT_STACK_MAX - 1)) \ - { \ - error_with_line (line_for_error (instack[indepth].lineno), \ - "macro or #include recursion too deep"); \ - code; \ - } - -/* Current depth in #include directives that use <...>. */ -int system_include_depth = 0; - -typedef struct file_buf FILE_BUF; - -/* The output buffer. Its LENGTH field is the amount of room allocated - for the buffer, not the number of chars actually present. To get - that, subtract outbuf.buf from outbuf.bufp. */ - -#define OUTBUF_SIZE 10 /* initial size of output buffer */ -FILE_BUF outbuf; - -/* Grow output buffer OBUF points at - so it can hold at least NEEDED more chars. */ - -#define check_expand(OBUF, NEEDED) \ - (((OBUF)->length - ((OBUF)->bufp - (OBUF)->buf) <= (NEEDED)) \ - ? grow_outbuf ((OBUF), (NEEDED)) : 0) - -struct file_name_list - { - struct file_name_list *next; - char *fname; - }; - -/* #include "file" looks in source file dir, then stack. */ -/* #include just looks in the stack. */ -/* -I directories are added to the end, then the defaults are added. */ -struct file_name_list include_defaults[] = - { -#ifndef VMS - { &include_defaults[1], GCC_INCLUDE_DIR }, - { &include_defaults[2], "/usr/include" }, - { 0, "/usr/local/include" } -#else - { &include_defaults[1], "GNU_CC_INCLUDE:" }, /* GNU includes */ - { &include_defaults[2], "SYS$SYSROOT:[SYSLIB.]" }, /* VAX-11 "C" includes */ - { 0, "" }, /* This makes normal VMS filespecs work OK */ -#endif /* VMS */ - }; - -/* These are used instead of the above, for C++. */ -struct file_name_list cplusplus_include_defaults[] = - { -#ifndef VMS - /* Pick up GNU C++ specific include files. */ - { &cplusplus_include_defaults[1], GPLUSPLUS_INCLUDE_DIR }, - /* Use GNU CC specific header files. */ - { &cplusplus_include_defaults[2], GCC_INCLUDE_DIR }, - { 0, "/usr/include" } -#else - { &cplusplus_include_defaults[1], "GNU_GXX_INCLUDE:" }, - { &cplusplus_include_defaults[2], "GNU_CC_INCLUDE:" }, - /* VAX-11 C includes */ - { &cplusplus_include_defaults[3], "SYS$SYSROOT:[SYSLIB.]" }, - { 0, "" }, /* This makes normal VMS filespecs work OK */ -#endif /* VMS */ - }; - -struct file_name_list *include = 0; /* First dir to search */ - /* First dir to search for */ -struct file_name_list *first_bracket_include = 0; -struct file_name_list *last_include = 0; /* Last in chain */ - -/* List of included files that contained #once. */ -struct file_name_list *dont_repeat_files = 0; - -/* List of other included files. */ -struct file_name_list *all_include_files = 0; - -/* Structure allocated for every #define. For a simple replacement - such as - #define foo bar , - nargs = -1, the `pattern' list is null, and the expansion is just - the replacement text. Nargs = 0 means a functionlike macro with no args, - e.g., - #define getchar() getc (stdin) . - When there are args, the expansion is the replacement text with the - args squashed out, and the reflist is a list describing how to - build the output from the input: e.g., "3 chars, then the 1st arg, - then 9 chars, then the 3rd arg, then 0 chars, then the 2nd arg". - The chars here come from the expansion. Whatever is left of the - expansion after the last arg-occurrence is copied after that arg. - Note that the reflist can be arbitrarily long--- - its length depends on the number of times the arguments appear in - the replacement text, not how many args there are. Example: - #define f(x) x+x+x+x+x+x+x would have replacement text "++++++" and - pattern list - { (0, 1), (1, 1), (1, 1), ..., (1, 1), NULL } - where (x, y) means (nchars, argno). */ - -typedef struct definition DEFINITION; -struct definition { - int nargs; - int length; /* length of expansion string */ - U_CHAR *expansion; - struct reflist { - struct reflist *next; - char stringify; /* nonzero if this arg was preceded by a - # operator. */ - char raw_before; /* Nonzero if a ## operator before arg. */ - char raw_after; /* Nonzero if a ## operator after arg. */ - int nchars; /* Number of literal chars to copy before - this arg occurrence. */ - int argno; /* Number of arg to substitute (origin-0) */ - } *pattern; - /* Names of macro args, concatenated in reverse order - with comma-space between them. - The only use of this is that we warn on redefinition - if this differs between the old and new definitions. */ - U_CHAR *argnames; -}; - -/* different kinds of things that can appear in the value field - of a hash node. Actually, this may be useless now. */ -union hashval { - int ival; - char *cpval; - DEFINITION *defn; -}; - - -/* The structure of a node in the hash table. The hash table - has entries for all tokens defined by #define commands (type T_MACRO), - plus some special tokens like __LINE__ (these each have their own - type, and the appropriate code is run when that type of node is seen. - It does not contain control words like "#define", which are recognized - by a separate piece of code. */ - -/* different flavors of hash nodes --- also used in keyword table */ -enum node_type { - T_DEFINE = 1, /* the `#define' keyword */ - T_INCLUDE, /* the `#include' keyword */ - T_IFDEF, /* the `#ifdef' keyword */ - T_IFNDEF, /* the `#ifndef' keyword */ - T_IF, /* the `#if' keyword */ - T_ELSE, /* `#else' */ - T_PRAGMA, /* `#pragma' */ - T_ELIF, /* `#else' */ - T_UNDEF, /* `#undef' */ - T_LINE, /* `#line' */ - T_ERROR, /* `#error' */ - T_ENDIF, /* `#endif' */ - T_SCCS, /* `#sccs', used on system V. */ - T_IDENT, /* `#ident', used on system V. */ - T_SPECLINE, /* special symbol `__LINE__' */ - T_DATE, /* `__DATE__' */ - T_FILE, /* `__FILE__' */ - T_BASE_FILE, /* `__BASE_FILE__' */ - T_INCLUDE_LEVEL, /* `__INCLUDE_LEVEL__' */ - T_VERSION, /* `__VERSION__' */ - T_TIME, /* `__TIME__' */ - T_CONST, /* Constant value, used by `__STDC__' */ - T_MACRO, /* macro defined by `#define' */ - T_DISABLED, /* macro temporarily turned off for rescan */ - T_SPEC_DEFINED, /* special `defined' macro for use in #if statements */ - T_UNUSED /* Used for something not defined. */ - }; - -struct hashnode { - struct hashnode *next; /* double links for easy deletion */ - struct hashnode *prev; - struct hashnode **bucket_hdr; /* also, a back pointer to this node's hash - chain is kept, in case the node is the head - of the chain and gets deleted. */ - enum node_type type; /* type of special token */ - int length; /* length of token, for quick comparison */ - U_CHAR *name; /* the actual name */ - union hashval value; /* pointer to expansion, or whatever */ -}; - -typedef struct hashnode HASHNODE; - -/* Some definitions for the hash table. The hash function MUST be - computed as shown in hashf () below. That is because the rescan - loop computes the hash value `on the fly' for most tokens, - in order to avoid the overhead of a lot of procedure calls to - the hashf () function. Hashf () only exists for the sake of - politeness, for use when speed isn't so important. */ - -#define HASHSIZE 1403 -HASHNODE *hashtab[HASHSIZE]; -#define HASHSTEP(old, c) ((old << 2) + c) -#define MAKE_POS(v) (v & ~0x80000000) /* make number positive */ - -/* Symbols to predefine. */ - -#ifdef CPP_PREDEFINES -char *predefs = CPP_PREDEFINES; -#else -char *predefs = ""; -#endif - -/* `struct directive' defines one #-directive, including how to handle it. */ - -struct directive { - int length; /* Length of name */ - int (*func)(); /* Function to handle directive */ - char *name; /* Name of directive */ - enum node_type type; /* Code which describes which directive. */ - char angle_brackets; /* Nonzero => <...> is special. */ - char traditional_comments; /* Nonzero: keep comments if -traditional. */ - char pass_thru; /* Copy preprocessed directive to output file. */ -}; - -/* Here is the actual list of #-directives, most-often-used first. */ - -struct directive directive_table[] = { - { 6, do_define, "define", T_DEFINE, 0, 1}, - { 2, do_if, "if", T_IF}, - { 5, do_xifdef, "ifdef", T_IFDEF}, - { 6, do_xifdef, "ifndef", T_IFNDEF}, - { 5, do_endif, "endif", T_ENDIF}, - { 4, do_else, "else", T_ELSE}, - { 4, do_elif, "elif", T_ELIF}, - { 4, do_line, "line", T_LINE}, - { 7, do_include, "include", T_INCLUDE, 1}, - { 5, do_undef, "undef", T_UNDEF}, - { 5, do_error, "error", T_ERROR}, -#ifdef SCCS_DIRECTIVE - { 4, do_sccs, "sccs", T_SCCS}, -#endif - { 6, do_pragma, "pragma", T_PRAGMA, 0, 0, 1}, - { -1, 0, "", T_UNUSED}, -}; - -/* table to tell if char can be part of a C identifier. */ -U_CHAR is_idchar[256]; -/* table to tell if char can be first char of a c identifier. */ -U_CHAR is_idstart[256]; -/* table to tell if c is horizontal space. */ -U_CHAR is_hor_space[256]; -/* table to tell if c is horizontal or vertical space. */ -U_CHAR is_space[256]; - -#define SKIP_WHITE_SPACE(p) do { while (is_hor_space[*p]) p++; } while (0) -#define SKIP_ALL_WHITE_SPACE(p) do { while (is_space[*p]) p++; } while (0) - -int errors = 0; /* Error counter for exit code */ - -/* Zero means dollar signs are punctuation. - -$ stores 0; -traditional, stores 1. Default is 1 for VMS, 0 otherwise. - This must be 0 for correct processing of this ANSI C program: - #define foo(a) #a - #define lose(b) foo(b) - #define test$ - lose(test) */ -#ifndef DOLLARS_IN_IDENTIFIERS -#define DOLLARS_IN_IDENTIFIERS 0 -#endif -int dollars_in_ident = DOLLARS_IN_IDENTIFIERS; - -FILE_BUF expand_to_temp_buffer (); - -DEFINITION *collect_expansion (); - -/* Stack of conditionals currently in progress - (including both successful and failing conditionals). */ - -struct if_stack { - struct if_stack *next; /* for chaining to the next stack frame */ - char *fname; /* copied from input when frame is made */ - int lineno; /* similarly */ - int if_succeeded; /* true if a leg of this if-group - has been passed through rescan */ - enum node_type type; /* type of last directive seen in this group */ -}; -typedef struct if_stack IF_STACK_FRAME; -IF_STACK_FRAME *if_stack = NULL; - -/* Buffer of -M output. */ - -char *deps_buffer; - -/* Number of bytes allocated in above. */ -int deps_allocated_size; - -/* Number of bytes used. */ -int deps_size; - -/* Number of bytes since the last newline. */ -int deps_column; - -/* Nonzero means -I- has been seen, - so don't look for #include "foo" the source-file directory. */ -int ignore_srcdir; - -/* Handler for SIGPIPE. */ - -static void -pipe_closed () -{ - fatal ("output pipe has been closed"); -} - -int -main (argc, argv) - int argc; - char **argv; -{ - int st_mode; - long st_size; - char *in_fname, *out_fname; - int f, i; - FILE_BUF *fp; - char **pend_files = (char **) xmalloc (argc * sizeof (char *)); - char **pend_defs = (char **) xmalloc (argc * sizeof (char *)); - char **pend_undefs = (char **) xmalloc (argc * sizeof (char *)); - int inhibit_predefs = 0; - int no_standard_includes = 0; - - /* Non-0 means don't output the preprocessed program. */ - int inhibit_output = 0; - - /* Stream on which to print the dependency information. */ - FILE *deps_stream = 0; - /* Target-name to write with the dependency information. */ - char *deps_target = 0; - -#ifdef RLIMIT_STACK - /* Get rid of any avoidable limit on stack size. */ - { - struct rlimit rlim; - - /* Set the stack limit huge so that alloca (particularly stringtab - * in dbxread.c) does not fail. */ - getrlimit (RLIMIT_STACK, &rlim); - rlim.rlim_cur = rlim.rlim_max; - setrlimit (RLIMIT_STACK, &rlim); - } -#endif /* RLIMIT_STACK defined */ - - progname = argv[0]; -#ifdef VMS - { - /* Remove directories from PROGNAME. */ - char *s; - extern char *rindex (); - - progname = savestring (argv[0]); - - if (!(s = rindex (progname, ']'))) - s = rindex (progname, ':'); - if (s) - strcpy (progname, s+1); - if (s = rindex (progname, '.')) - *s = '\0'; - } -#endif - - in_fname = NULL; - out_fname = NULL; - - /* Initialize is_idchar to allow $. */ - dollars_in_ident = 1; - initialize_char_syntax (); - dollars_in_ident = DOLLARS_IN_IDENTIFIERS; - - no_line_commands = 0; - no_trigraphs = 1; - dump_macros = 0; - no_output = 0; - cplusplus = 0; -#ifdef CPLUSPLUS - cplusplus = 1; -#endif - - signal (SIGPIPE, pipe_closed); - -#ifndef VMS - max_include_len - = max (max (sizeof (GCC_INCLUDE_DIR), - sizeof (GPLUSPLUS_INCLUDE_DIR)), - sizeof ("/usr/include/CC")); -#else /* VMS */ - max_include_len - = sizeof("SYS$SYSROOT:[SYSLIB.]"); -#endif /* VMS */ - - bzero (pend_files, argc * sizeof (char *)); - bzero (pend_defs, argc * sizeof (char *)); - bzero (pend_undefs, argc * sizeof (char *)); - - /* Process switches and find input file name. */ - - for (i = 1; i < argc; i++) { - if (argv[i][0] != '-') { - if (out_fname != NULL) - fatal ("Usage: %s [switches] input output", argv[0]); - else if (in_fname != NULL) - out_fname = argv[i]; - else - in_fname = argv[i]; - } else { - switch (argv[i][1]) { - - case 'i': - if (argv[i][2] != 0) - pend_files[i] = argv[i] + 2; - else if (i + 1 == argc) - fatal ("Filename missing after -i option"); - else - pend_files[i] = argv[i+1], i++; - break; - - case 'o': - if (out_fname != NULL) - fatal ("Output filename specified twice"); - if (i + 1 == argc) - fatal ("Filename missing after -o option"); - out_fname = argv[++i]; - if (!strcmp (out_fname, "-")) - out_fname = ""; - break; - - case 'p': - pedantic = 1; - break; - - case 't': - if (!strcmp (argv[i], "-traditional")) { - traditional = 1; - dollars_in_ident = 1; - } else if (!strcmp (argv[i], "-trigraphs")) { - no_trigraphs = 0; - } - break; - - case '+': - cplusplus = 1; - break; - - case 'w': - inhibit_warnings = 1; - break; - - case 'W': - if (!strcmp (argv[i], "-Wtrigraphs")) { - warn_trigraphs = 1; - } - if (!strcmp (argv[i], "-Wcomments")) - warn_comments = 1; - if (!strcmp (argv[i], "-Wcomment")) - warn_comments = 1; - if (!strcmp (argv[i], "-Wall")) { - warn_trigraphs = 1; - warn_comments = 1; - } - break; - - case 'M': - if (!strcmp (argv[i], "-M")) - print_deps = 2; - else if (!strcmp (argv[i], "-MM")) - print_deps = 1; - inhibit_output = 1; - break; - - case 'd': - dump_macros = 1; - no_output = 1; - break; - - case 'v': - fprintf (stderr, "GNU CPP version %s\n", version_string); - break; - - case 'D': - { - char *p, *p1; - - if (argv[i][2] != 0) - p = argv[i] + 2; - else if (i + 1 == argc) - fatal ("Macro name missing after -D option"); - else - p = argv[++i]; - - if ((p1 = (char *) index (p, '=')) != NULL) - *p1 = ' '; - pend_defs[i] = p; - } - break; - - case 'U': /* JF #undef something */ - if (argv[i][2] != 0) - pend_undefs[i] = argv[i] + 2; - else if (i + 1 == argc) - fatal ("Macro name missing after -U option"); - else - pend_undefs[i] = argv[i+1], i++; - break; - - case 'C': - put_out_comments = 1; - break; - - case 'E': /* -E comes from cc -E; ignore it. */ - break; - - case 'P': - no_line_commands = 1; - break; - - case '$': /* Don't include $ in identifiers. */ - dollars_in_ident = 0; - break; - - case 'I': /* Add directory to path for includes. */ - { - struct file_name_list *dirtmp; - - if (! ignore_srcdir && !strcmp (argv[i] + 2, "-")) - ignore_srcdir = 1; - else { - dirtmp = (struct file_name_list *) - xmalloc (sizeof (struct file_name_list)); - dirtmp->next = 0; /* New one goes on the end */ - if (include == 0) - include = dirtmp; - else - last_include->next = dirtmp; - last_include = dirtmp; /* Tail follows the last one */ - if (argv[i][2] != 0) - dirtmp->fname = argv[i] + 2; - else if (i + 1 == argc) - fatal ("Directory name missing after -I option"); - else - dirtmp->fname = argv[++i]; - if (strlen (dirtmp->fname) > max_include_len) - max_include_len = strlen (dirtmp->fname); - if (dirtmp->fname[0] == '.' && dirtmp->fname[1] == '\0') - dirtmp->fname = 0; /* Current directory */ - if (ignore_srcdir && first_bracket_include == 0) - first_bracket_include = dirtmp; - } - } - break; - - case 'n': - /* -nostdinc causes no default include directories. - You must specify all include-file directories with -I. */ - no_standard_includes = 1; - break; - - case 'u': - /* Sun compiler passes undocumented switch "-undef". - Let's assume it means to inhibit the predefined symbols. */ - inhibit_predefs = 1; - break; - - case '\0': /* JF handle '-' as file name meaning stdin or stdout */ - if (in_fname == NULL) { - in_fname = ""; - break; - } else if (out_fname == NULL) { - out_fname = ""; - break; - } /* else fall through into error */ - - default: - fatal ("Invalid option `%s'", argv[i]); - } - } - } - - /* Now that dollars_in_ident is known, initialize is_idchar. */ - initialize_char_syntax (); - - /* Install __LINE__, etc. Must follow initialize_char_syntax - and option processing. */ - initialize_builtins (); - - /* Do standard #defines that identify processor type. */ - - if (!inhibit_predefs) { - char *p = (char *) alloca (strlen (predefs) + 1); - strcpy (p, predefs); - while (*p) { - char *q; - if (p[0] != '-' || p[1] != 'D') - abort (); - q = &p[2]; - while (*p && *p != ' ') p++; - if (*p != 0) - *p++= 0; - make_definition (q); - } - } - - /* Do defines specified with -D. */ - for (i = 1; i < argc; i++) - if (pend_defs[i]) - make_definition (pend_defs[i]); - - /* Do undefines specified with -U. */ - for (i = 1; i < argc; i++) - if (pend_undefs[i]) - make_undef (pend_undefs[i]); - - /* Unless -fnostdinc, - tack on the standard include file dirs to the specified list */ - if (!no_standard_includes) { - if (include == 0) - include = (cplusplus ? cplusplus_include_defaults : include_defaults); - else - last_include->next - = (cplusplus ? cplusplus_include_defaults : include_defaults); - /* Make sure the list for #include <...> also has the standard dirs. */ - if (ignore_srcdir && first_bracket_include == 0) - first_bracket_include - = (cplusplus ? cplusplus_include_defaults : include_defaults); - } - - /* Initialize output buffer */ - - outbuf.buf = (U_CHAR *) xmalloc (OUTBUF_SIZE); - outbuf.bufp = outbuf.buf; - outbuf.length = OUTBUF_SIZE; - - /* Scan the -i files before the main input. - Much like #including them, but with no_output set - so that only their macro definitions matter. */ - - no_output++; - for (i = 1; i < argc; i++) - if (pend_files[i]) { - int fd = open (pend_files[i], O_RDONLY, 0666); - if (fd < 0) { - perror_with_name (pend_files[i]); - return FATAL_EXIT_CODE; - } - finclude (fd, pend_files[i], &outbuf); - } - no_output--; - - /* Create an input stack level for the main input file - and copy the entire contents of the file into it. */ - - fp = &instack[++indepth]; - - /* JF check for stdin */ - if (in_fname == NULL || *in_fname == 0) { - in_fname = ""; - f = 0; - } else if ((f = open (in_fname, O_RDONLY, 0666)) < 0) - goto perror; - - /* Either of two environment variables can specify output of deps. - Its value is either "OUTPUT_FILE" or "OUTPUT_FILE DEPS_TARGET", - where OUTPUT_FILE is the file to write deps info to - and DEPS_TARGET is the target to mention in the deps. */ - - if (print_deps == 0 - && (getenv ("SUNPRO_DEPENDENCIES") != 0 - || getenv ("DEPENDENCIES_OUTPUT") != 0)) - { - char *spec = getenv ("DEPENDENCIES_OUTPUT"); - char *s; - char *output_file; - - if (spec == 0) - { - spec = getenv ("SUNPRO_DEPENDENCIES"); - print_deps = 2; - } - else - print_deps = 1; - - s = spec; - /* Find the space before the DEPS_TARGET, if there is one. */ - /* Don't use `index'; that causes trouble on USG. */ - while (*s != 0 && *s != ' ') s++; - if (*s != 0) - { - deps_target = s + 1; - output_file = (char *) xmalloc (s - spec + 1); - bcopy (spec, output_file, s - spec); - output_file[s - spec] = 0; - } - else - { - deps_target = 0; - output_file = spec; - } - - deps_stream = fopen (output_file, "a"); - if (deps_stream == 0) - pfatal_with_name (output_file); - } - /* If the -M option was used, output the deps to standard output. */ - else if (print_deps) - deps_stream = stdout; - - /* For -M, print the expected object file name - as the target of this Make-rule. */ - if (print_deps) { - deps_allocated_size = 200; - deps_buffer = (char *) xmalloc (deps_allocated_size); - deps_buffer[0] = 0; - deps_size = 0; - deps_column = 0; - - if (deps_target) { - deps_output (deps_target, 0); - deps_output (":", 0); - } else if (*in_fname == 0) - deps_output ("-: ", 0); - else { - int len; - char *p = in_fname; - char *p1 = p; - /* Discard all directory prefixes from P. */ - while (*p1) { - if (*p1 == '/') - p = p1 + 1; - p1++; - } - /* Output P, but remove known suffixes. */ - len = strlen (p); - if (p[len - 2] == '.' - && (p[len - 1] == 'c' || p[len - 1] == 'C' || p[len - 1] == 'S')) - deps_output (p, len - 2); - else if (p[len - 3] == '.' - && p[len - 2] == 'c' - && p[len - 1] == 'c') - deps_output (p, len - 3); - else - deps_output (p, 0); - /* Supply our own suffix. */ - deps_output (".o : ", 0); - deps_output (in_fname, 0); - deps_output (" ", 0); - } - } - - file_size_and_mode (f, &st_mode, &st_size); - fp->fname = in_fname; - fp->lineno = 1; - /* JF all this is mine about reading pipes and ttys */ - if (!S_ISREG (st_mode)) { - /* Read input from a file that is not a normal disk file. - We cannot preallocate a buffer with the correct size, - so we must read in the file a piece at the time and make it bigger. */ - int size; - int bsize; - int cnt; - U_CHAR *bufp; - - bsize = 2000; - size = 0; - fp->buf = (U_CHAR *) xmalloc (bsize + 2); - bufp = fp->buf; - for (;;) { - cnt = read (f, bufp, bsize - size); - if (cnt < 0) goto perror; /* error! */ - if (cnt == 0) break; /* End of file */ - size += cnt; - bufp += cnt; - if (bsize == size) { /* Buffer is full! */ - bsize *= 2; - fp->buf = (U_CHAR *) xrealloc (fp->buf, bsize + 2); - bufp = fp->buf + size; /* May have moved */ - } - } - fp->length = size; - } else { - /* Read a file whose size we can determine in advance. - For the sake of VMS, st_size is just an upper bound. */ - long i; - fp->length = 0; - fp->buf = (U_CHAR *) xmalloc (st_size + 2); - - while (st_size > 0) { - i = read (f, fp->buf + fp->length, st_size); - if (i <= 0) { - if (i == 0) break; - goto perror; - } - fp->length += i; - st_size -= i; - } - } - fp->bufp = fp->buf; - fp->if_stack = if_stack; - - /* Unless inhibited, convert trigraphs in the input. */ - - if (!no_trigraphs) - trigraph_pcp (fp); - - /* Make sure data ends with a newline. And put a null after it. */ - - if (fp->length > 0 && fp->buf[fp->length-1] != '\n') - fp->buf[fp->length++] = '\n'; - fp->buf[fp->length] = '\0'; - - /* Now that we know the input file is valid, open the output. */ - - if (!out_fname || !strcmp (out_fname, "")) - out_fname = "stdout"; - else if (! freopen (out_fname, "w", stdout)) - pfatal_with_name (out_fname); - - output_line_command (fp, &outbuf, 0, same_file); - - /* Scan the input, processing macros and directives. */ - - rescan (&outbuf, 0); - - /* Now we have processed the entire input - Write whichever kind of output has been requested. */ - - - if (dump_macros) - dump_all_macros (); - else if (! inhibit_output && deps_stream != stdout) { - if (write (fileno (stdout), outbuf.buf, outbuf.bufp - outbuf.buf) < 0) - fatal ("I/O error on output"); - } - - if (print_deps) { - fputs (deps_buffer, deps_stream); - putc ('\n', deps_stream); - if (deps_stream != stdout) { - fclose (deps_stream); - if (ferror (deps_stream)) - fatal ("I/O error on output"); - } - } - - if (ferror (stdout)) - fatal ("I/O error on output"); - - if (errors) - exit (FATAL_EXIT_CODE); - exit (SUCCESS_EXIT_CODE); - - perror: - pfatal_with_name (in_fname); -} - -/* Pre-C-Preprocessor to translate ANSI trigraph idiocy in BUF - before main CCCP processing. Name `pcp' is also in honor of the - drugs the trigraph designers must have been on. - - Using an extra pass through the buffer takes a little extra time, - but is infinitely less hairy than trying to handle ??/" inside - strings, etc. everywhere, and also makes sure that trigraphs are - only translated in the top level of processing. */ - -trigraph_pcp (buf) - FILE_BUF *buf; -{ - register U_CHAR c, *fptr, *bptr, *sptr; - int len; - - fptr = bptr = sptr = buf->buf; - while ((sptr = (U_CHAR *) index (sptr, '?')) != NULL) { - if (*++sptr != '?') - continue; - switch (*++sptr) { - case '=': - c = '#'; - break; - case '(': - c = '['; - break; - case '/': - c = '\\'; - break; - case ')': - c = ']'; - break; - case '\'': - c = '^'; - break; - case '<': - c = '{'; - break; - case '!': - c = '|'; - break; - case '>': - c = '}'; - break; - case '-': - c = '~'; - break; - case '?': - sptr--; - continue; - default: - continue; - } - len = sptr - fptr - 2; - if (bptr != fptr && len > 0) - bcopy (fptr, bptr, len); /* BSD doc says bcopy () works right - for overlapping strings. In ANSI - C, this will be memmove (). */ - bptr += len; - *bptr++ = c; - fptr = ++sptr; - } - len = buf->length - (fptr - buf->buf); - if (bptr != fptr && len > 0) - bcopy (fptr, bptr, len); - buf->length -= fptr - bptr; - buf->buf[buf->length] = '\0'; - if (warn_trigraphs && fptr != bptr) - warning ("%d trigraph(s) encountered", (fptr - bptr) / 2); -} - -/* Move all backslash-newline pairs out of embarrassing places. - Exchange all such pairs following BP - with any potentially-embarrasing characters that follow them. - Potentially-embarrassing characters are / and * - (because a backslash-newline inside a comment delimiter - would cause it not to be recognized). */ - -newline_fix (bp) - U_CHAR *bp; -{ - register U_CHAR *p = bp; - register int count = 0; - - /* First count the backslash-newline pairs here. */ - - while (*p++ == '\\' && *p++ == '\n') - count++; - - p = bp + count * 2; - - /* Exit if what follows the backslash-newlines is not embarrassing. */ - - if (count == 0 || (*p != '/' && *p != '*')) - return; - - /* Copy all potentially embarrassing characters - that follow the backslash-newline pairs - down to where the pairs originally started. */ - - while (*p == '*' || *p == '/') - *bp++ = *p++; - - /* Now write the same number of pairs after the embarrassing chars. */ - while (count-- > 0) { - *bp++ = '\\'; - *bp++ = '\n'; - } -} - -/* Like newline_fix but for use within a directive-name. - Move any backslash-newlines up past any following symbol constituents. */ - -name_newline_fix (bp) - U_CHAR *bp; -{ - register U_CHAR *p = bp; - register int count = 0; - - /* First count the backslash-newline pairs here. */ - - while (*p++ == '\\' && *p++ == '\n') - count++; - - p = bp + count * 2; - - /* What follows the backslash-newlines is not embarrassing. */ - - if (count == 0 || !is_idchar[*p]) - return; - - /* Copy all potentially embarrassing characters - that follow the backslash-newline pairs - down to where the pairs originally started. */ - - while (is_idchar[*p]) - *bp++ = *p++; - - /* Now write the same number of pairs after the embarrassing chars. */ - while (count-- > 0) { - *bp++ = '\\'; - *bp++ = '\n'; - } -} - -/* - * The main loop of the program. - * - * Read characters from the input stack, transferring them to the - * output buffer OP. - * - * Macros are expanded and push levels on the input stack. - * At the end of such a level it is popped off and we keep reading. - * At the end of any other kind of level, we return. - * #-directives are handled, except within macros. - * - * If OUTPUT_MARKS is nonzero, keep Newline markers found in the input - * and insert them when appropriate. This is set while scanning macro - * arguments before substitution. It is zero when scanning for final output. - * There are three types of Newline markers: - * * Newline - follows a macro name that was not expanded - * because it appeared inside an expansion of the same macro. - * This marker prevents future expansion of that identifier. - * When the input is rescanned into the final output, these are deleted. - * These are also deleted by ## concatenation. - * * Newline Space (or Newline and any other whitespace character) - * stands for a place that tokens must be separated or whitespace - * is otherwise desirable, but where the ANSI standard specifies there - * is no whitespace. This marker turns into a Space (or whichever other - * whitespace char appears in the marker) in the final output, - * but it turns into nothing in an argument that is stringified with #. - * Such stringified arguments are the only place where the ANSI standard - * specifies with precision that whitespace may not appear. - * - * During this function, IP->bufp is kept cached in IBP for speed of access. - * Likewise, OP->bufp is kept in OBP. Before calling a subroutine - * IBP, IP and OBP must be copied back to memory. IP and IBP are - * copied back with the RECACHE macro. OBP must be copied back from OP->bufp - * explicitly, and before RECACHE, since RECACHE uses OBP. - */ - -rescan (op, output_marks) - FILE_BUF *op; - int output_marks; -{ - /* Character being scanned in main loop. */ - register U_CHAR c; - - /* Length of pending accumulated identifier. */ - register int ident_length = 0; - - /* Hash code of pending accumulated identifier. */ - register int hash = 0; - - /* Current input level (&instack[indepth]). */ - FILE_BUF *ip; - - /* Pointer for scanning input. */ - register U_CHAR *ibp; - - /* Pointer to end of input. End of scan is controlled by LIMIT. */ - register U_CHAR *limit; - - /* Pointer for storing output. */ - register U_CHAR *obp; - - /* REDO_CHAR is nonzero if we are processing an identifier - after backing up over the terminating character. - Sometimes we process an identifier without backing up over - the terminating character, if the terminating character - is not special. Backing up is done so that the terminating character - will be dispatched on again once the identifier is dealt with. */ - int redo_char = 0; - - /* 1 if within an identifier inside of which a concatenation - marker (Newline -) has been seen. */ - int concatenated = 0; - - /* While scanning a comment or a string constant, - this records the line it started on, for error messages. */ - int start_line; - - /* Record position of last `real' newline. */ - U_CHAR *beg_of_line; - -/* Pop the innermost input stack level, assuming it is a macro expansion. */ - -#define POPMACRO \ -do { ip->macro->type = T_MACRO; \ - if (ip->free_ptr) free (ip->free_ptr); \ - --indepth; } while (0) - -/* Reload `rescan's local variables that describe the current - level of the input stack. */ - -#define RECACHE \ -do { ip = &instack[indepth]; \ - ibp = ip->bufp; \ - limit = ip->buf + ip->length; \ - op->bufp = obp; \ - check_expand (op, limit - ibp); \ - beg_of_line = 0; \ - obp = op->bufp; } while (0) - - if (no_output && instack[indepth].fname != 0) - skip_if_group (&instack[indepth], 1); - - obp = op->bufp; - RECACHE; - beg_of_line = ibp; - - /* Our caller must always put a null after the end of - the input at each input stack level. */ - if (*limit != 0) - abort (); - - while (1) { - c = *ibp++; - *obp++ = c; - - switch (c) { - case '\\': - if (ibp >= limit) - break; - if (*ibp == '\n') { - /* Always merge lines ending with backslash-newline, - even in middle of identifier. */ - ++ibp; - ++ip->lineno; - --obp; /* remove backslash from obuf */ - break; - } - /* Otherwise, backslash suppresses specialness of following char, - so copy it here to prevent the switch from seeing it. - But first get any pending identifier processed. */ - if (ident_length > 0) - goto specialchar; - *obp++ = *ibp++; - break; - - case '#': - /* If this is expanding a macro definition, don't recognize - preprocessor directives. */ - if (ip->macro != 0) - goto randomchar; - if (ident_length) - goto specialchar; - - /* # keyword: a # must be first nonblank char on the line */ - if (beg_of_line == 0) - goto randomchar; - { - U_CHAR *bp; - - /* Scan from start of line, skipping whitespace, comments - and backslash-newlines, and see if we reach this #. - If not, this # is not special. */ - bp = beg_of_line; - while (1) { - if (is_hor_space[*bp]) - bp++; - else if (*bp == '\\' && bp[1] == '\n') - bp += 2; - else if (*bp == '/' && (newline_fix (bp + 1), bp[1]) == '*') { - bp += 2; - while (!(*bp == '*' && (newline_fix (bp + 1), bp[1]) == '/')) - bp++; - bp += 1; - } - else if (cplusplus && *bp == '/' && (newline_fix (bp + 1), bp[1]) == '/') { - bp += 2; - while (*bp++ != '\n') ; - } - else break; - } - if (bp + 1 != ibp) - goto randomchar; - } - - /* This # can start a directive. */ - - --obp; /* Don't copy the '#' */ - - ip->bufp = ibp; - op->bufp = obp; - if (! handle_directive (ip, op)) { -#ifdef USE_C_ALLOCA - alloca (0); -#endif - /* Not a known directive: treat it as ordinary text. - IP, OP, IBP, etc. have not been changed. */ - if (no_output && instack[indepth].fname) { - /* If not generating expanded output, - what we do with ordinary text is skip it. - Discard everything until next # directive. */ - skip_if_group (&instack[indepth], 1); - RECACHE; - beg_of_line = ibp; - break; - } - ++obp; /* Copy the '#' after all */ - goto randomchar; - } -#ifdef USE_C_ALLOCA - alloca (0); -#endif - /* A # directive has been successfully processed. */ - /* If not generating expanded output, ignore everything until - next # directive. */ - if (no_output && instack[indepth].fname) - skip_if_group (&instack[indepth], 1); - obp = op->bufp; - RECACHE; - beg_of_line = ibp; - break; - - case '\"': /* skip quoted string */ - case '\'': - /* A single quoted string is treated like a double -- some - programs (e.g., troff) are perverse this way */ - - if (ident_length) - goto specialchar; - - start_line = ip->lineno; - - /* Skip ahead to a matching quote. */ - - while (1) { - if (ibp >= limit) { - if (traditional) { - if (ip->macro != 0) { - /* try harder: this string crosses a macro expansion boundary */ - POPMACRO; - RECACHE; - continue; - } - } else - error_with_line (line_for_error (start_line), - "unterminated string or character constant"); - break; - } - *obp++ = *ibp; - switch (*ibp++) { - case '\n': - ++ip->lineno; - ++op->lineno; - /* Traditionally, end of line ends a string constant with no error. - So exit the loop and record the new line. */ - if (traditional) { - beg_of_line = ibp; - goto while2end; - } - if (pedantic || c == '\'') { - error_with_line (line_for_error (start_line), - "unterminated string or character constant"); - goto while2end; - } - break; - - case '\\': - if (ibp >= limit) - break; - if (*ibp == '\n') { - /* Backslash newline is replaced by nothing at all, - but keep the line counts correct. */ - --obp; - ++ibp; - ++ip->lineno; - } else { - /* ANSI stupidly requires that in \\ the second \ - is *not* prevented from combining with a newline. */ - while (*ibp == '\\' && ibp[1] == '\n') { - ibp += 2; - ++ip->lineno; - } - *obp++ = *ibp++; - } - break; - - case '\"': - case '\'': - if (ibp[-1] == c) - goto while2end; - break; - } - } - while2end: - break; - - case '/': - if (*ibp == '\\' && ibp[1] == '\n') - newline_fix (ibp); - /* Don't look for comments inside a macro definition. */ - if (ip->macro != 0) - goto randomchar; - /* A comment constitutes white space, so it can terminate an identifier. - Process the identifier, if any. */ - if (ident_length) - goto specialchar; - if (cplusplus && *ibp == '/') { - /* C++ style comment... */ - start_line = ip->lineno; - - --ibp; /* Back over the slash */ - --obp; - - /* Comments are equivalent to spaces. */ - if (! put_out_comments) - *obp++ = ' '; - else { - /* must fake up a comment here */ - *obp++ = '/'; - *obp++ = '/'; - } - { - U_CHAR *before_bp = ibp+2; - - while (ibp < limit) { - if (*ibp == '\\' && ibp[1] == '\n') { - ip->lineno++; - ibp += 2; - } else if (*ibp++ == '\n') { - ibp--; - if (put_out_comments) { - bcopy (before_bp, obp, ibp - before_bp); - obp += ibp - before_bp; - } - break; - } - } - break; - } - } - if (*ibp != '*') - goto randomchar; - - /* We have a comment. Skip it, optionally copying it to output. */ - - start_line = ip->lineno; - - ++ibp; /* Skip the star. */ - - /* Comments are equivalent to spaces. - Note that we already output the slash; we might not want it. - For -traditional, a comment is equivalent to nothing. */ - if (! put_out_comments) { - if (traditional) - obp--; - else - obp[-1] = ' '; - } - else - *obp++ = '*'; - - { - U_CHAR *before_bp = ibp; - - while (ibp < limit) { - switch (*ibp++) { - case '/': - if (warn_comments && ibp < limit && *ibp == '*') - warning("`/*' within comment"); - break; - case '*': - if (*ibp == '\\' && ibp[1] == '\n') - newline_fix (ibp); - if (ibp >= limit || *ibp == '/') - goto comment_end; - break; - case '\n': - ++ip->lineno; - /* Copy the newline into the output buffer, in order to - avoid the pain of a #line every time a multiline comment - is seen. */ - if (!put_out_comments) - *obp++ = '\n'; - ++op->lineno; - } - } - comment_end: - - if (ibp >= limit) - error_with_line (line_for_error (start_line), - "unterminated comment"); - else { - ibp++; - if (put_out_comments) { - bcopy (before_bp, obp, ibp - before_bp); - obp += ibp - before_bp; - } - } - } - break; - - case '$': - if (!dollars_in_ident) - goto randomchar; - goto letter; - - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - /* If digit is not part of identifier, it starts a number, - which means that following letters are not an identifier. - "0x5" does not refer to an identifier "x5". - So copy all alphanumerics that follow without accumulating - as an identifier. Periods also, for sake of "3.e7". */ - - if (ident_length == 0) { - while (ibp < limit) { - while (ibp < limit && ibp[0] == '\\' && ibp[1] == '\n') { - ++ip->lineno; - ibp += 2; - } - c = *ibp++; - if (!isalnum (c) && c != '.' && c != '_') { - --ibp; - break; - } - *obp++ = c; - /* A sign can be part of a preprocessing number - if it follows an e. */ - if (c == 'e' || c == 'E') { - while (ibp < limit && ibp[0] == '\\' && ibp[1] == '\n') { - ++ip->lineno; - ibp += 2; - } - if (ibp < limit && (*ibp == '+' || *ibp == '-')) { - *obp++ = *ibp++; - /* But traditional C does not let the token go past the sign. */ - if (traditional) - break; - } - } - } - break; - } - /* fall through */ - - case '_': - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': - case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': - case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': - case 's': case 't': case 'u': case 'v': case 'w': case 'x': - case 'y': case 'z': - case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': - case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': - case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': - case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': - case 'Y': case 'Z': - letter: - ident_length++; - /* Compute step of hash function, to avoid a proc call on every token */ - hash = HASHSTEP (hash, c); - break; - - case '\n': - /* If reprocessing a macro expansion, newline is a special marker. */ - if (ip->macro != 0) { - /* Newline White is a "funny space" to separate tokens that are - supposed to be separate but without space between. - Here White means any horizontal whitespace character. - Newline - marks a recursive macro use that is not - supposed to be expandable. */ - - if (*ibp == '-') { - /* Newline - inhibits expansion of preceding token. - If expanding a macro arg, we keep the newline -. - In final output, it is deleted. */ - if (! concatenated) { - ident_length = 0; - hash = 0; - } - ibp++; - if (!output_marks) { - obp--; - } else { - /* If expanding a macro arg, keep the newline -. */ - *obp++ = '-'; - } - } else if (is_space[*ibp]) { - /* Newline Space does not prevent expansion of preceding token - so expand the preceding token and then come back. */ - if (ident_length > 0) - goto specialchar; - - /* If generating final output, newline space makes a space. */ - if (!output_marks) { - obp[-1] = *ibp++; - /* And Newline Newline makes a newline, so count it. */ - if (obp[-1] == '\n') - op->lineno++; - } else { - /* If expanding a macro arg, keep the newline space. - If the arg gets stringified, newline space makes nothing. */ - *obp++ = *ibp++; - } - } else abort (); /* Newline followed by something random? */ - break; - } - - /* If there is a pending identifier, handle it and come back here. */ - if (ident_length > 0) - goto specialchar; - - beg_of_line = ibp; - - /* Update the line counts and output a #line if necessary. */ - ++ip->lineno; - ++op->lineno; - if (ip->lineno != op->lineno) { - op->bufp = obp; - output_line_command (ip, op, 1, same_file); - check_expand (op, ip->length - (ip->bufp - ip->buf)); - obp = op->bufp; - } - break; - - /* Come here either after (1) a null character that is part of the input - or (2) at the end of the input, because there is a null there. */ - case 0: - if (ibp <= limit) - /* Our input really contains a null character. */ - goto randomchar; - - /* At end of a macro-expansion level, pop it and read next level. */ - if (ip->macro != 0) { - obp--; - ibp--; - /* If traditional, and we have an identifier that ends here, - process it now, so we get the right error for recursion. */ - if (traditional && ident_length - && ! is_idchar[*instack[indepth - 1].bufp]) { - redo_char = 1; - goto randomchar; - } - POPMACRO; - RECACHE; - break; - } - - /* If we don't have a pending identifier, - return at end of input. */ - if (ident_length == 0) { - obp--; - ibp--; - op->bufp = obp; - ip->bufp = ibp; - goto ending; - } - - /* If we do have a pending identifier, just consider this null - a special character and arrange to dispatch on it again. - The second time, IDENT_LENGTH will be zero so we will return. */ - - /* Fall through */ - -specialchar: - - /* Handle the case of a character such as /, ', " or null - seen following an identifier. Back over it so that - after the identifier is processed the special char - will be dispatched on again. */ - - ibp--; - obp--; - redo_char = 1; - - default: - -randomchar: - - if (ident_length > 0) { - register HASHNODE *hp; - - /* We have just seen an identifier end. If it's a macro, expand it. - - IDENT_LENGTH is the length of the identifier - and HASH is its hash code. - - The identifier has already been copied to the output, - so if it is a macro we must remove it. - - If REDO_CHAR is 0, the char that terminated the identifier - has been skipped in the output and the input. - OBP-IDENT_LENGTH-1 points to the identifier. - If the identifier is a macro, we must back over the terminator. - - If REDO_CHAR is 1, the terminating char has already been - backed over. OBP-IDENT_LENGTH points to the identifier. */ - - for (hp = hashtab[MAKE_POS (hash) % HASHSIZE]; hp != NULL; - hp = hp->next) { - - if (hp->length == ident_length) { - U_CHAR *obufp_before_macroname; - int op_lineno_before_macroname; - register int i = ident_length; - register U_CHAR *p = hp->name; - register U_CHAR *q = obp - i; - int disabled; - - if (! redo_char) - q--; - - do { /* All this to avoid a strncmp () */ - if (*p++ != *q++) - goto hashcollision; - } while (--i); - - /* We found a use of a macro name. - see if the context shows it is a macro call. */ - - /* Back up over terminating character if not already done. */ - if (! redo_char) { - ibp--; - obp--; - } - - obufp_before_macroname = obp - ident_length; - op_lineno_before_macroname = op->lineno; - - /* Record whether the macro is disabled. */ - disabled = hp->type == T_DISABLED; - - /* This looks like a macro ref, but if the macro was disabled, - just copy its name and put in a marker if requested. */ - - if (disabled) { -#if 0 - /* This error check caught useful cases such as - #define foo(x,y) bar(x(y,0), y) - foo(foo, baz) */ - if (traditional) - error ("recursive use of macro `%s'", hp->name); -#endif - - if (output_marks) { - check_expand (op, limit - ibp + 2); - *obp++ = '\n'; - *obp++ = '-'; - } - break; - } - - /* If macro wants an arglist, verify that a '(' follows. - first skip all whitespace, copying it to the output - after the macro name. Then, if there is no '(', - decide this is not a macro call and leave things that way. */ - if ((hp->type == T_MACRO || hp->type == T_DISABLED) - && hp->value.defn->nargs >= 0) - { - while (1) { - /* Scan forward over whitespace, copying it to the output. */ - if (ibp == limit && ip->macro != 0) { - POPMACRO; - RECACHE; - } - /* A comment: copy it unchanged or discard it. */ - else if (*ibp == '/' && ibp+1 != limit && ibp[1] == '*') { - if (put_out_comments) { - *obp++ = '/'; - *obp++ = '*'; - } else if (! traditional) { - *obp++ = ' '; - } - ibp += 2; - while (ibp + 1 != limit - && !(ibp[0] == '*' && ibp[1] == '/')) { - /* We need not worry about newline-marks, - since they are never found in comments. */ - if (*ibp == '\n') { - /* Newline in a file. Count it. */ - ++ip->lineno; - ++op->lineno; - } - if (put_out_comments) - *obp++ = *ibp++; - else - ibp++; - } - ibp += 2; - if (put_out_comments) { - *obp++ = '*'; - *obp++ = '/'; - } - } - else if (is_space[*ibp]) { - *obp++ = *ibp++; - if (ibp[-1] == '\n') { - if (ip->macro == 0) { - /* Newline in a file. Count it. */ - ++ip->lineno; - ++op->lineno; - } else if (!output_marks) { - /* A newline mark, and we don't want marks - in the output. If it is newline-hyphen, - discard it entirely. Otherwise, it is - newline-whitechar, so keep the whitechar. */ - obp--; - if (*ibp == '-') - ibp++; - else { - if (*ibp == '\n') - ++op->lineno; - *obp++ = *ibp++; - } - } else { - /* A newline mark; copy both chars to the output. */ - *obp++ = *ibp++; - } - } - } - else break; - } - if (*ibp != '(') - break; - } - - /* This is now known to be a macro call. - Discard the macro name from the output, - along with any following whitespace just copied. */ - obp = obufp_before_macroname; - op->lineno = op_lineno_before_macroname; - - /* Expand the macro, reading arguments as needed, - and push the expansion on the input stack. */ - ip->bufp = ibp; - op->bufp = obp; - macroexpand (hp, op); - - /* Reexamine input stack, since macroexpand has pushed - a new level on it. */ - obp = op->bufp; - RECACHE; - break; - } -hashcollision: - ; - } /* End hash-table-search loop */ - ident_length = hash = 0; /* Stop collecting identifier */ - redo_char = 0; - concatenated = 0; - } /* End if (ident_length > 0) */ - } /* End switch */ - } /* End per-char loop */ - - /* Come here to return -- but first give an error message - if there was an unterminated successful conditional. */ - ending: - if (if_stack != ip->if_stack) { - char *str; - switch (if_stack->type) { - case T_IF: - str = "if"; - break; - case T_IFDEF: - str = "ifdef"; - break; - case T_IFNDEF: - str = "ifndef"; - break; - case T_ELSE: - str = "else"; - break; - case T_ELIF: - str = "elif"; - break; - } - error_with_line (line_for_error (if_stack->lineno), - "unterminated #%s conditional", str); - } - if_stack = ip->if_stack; -} - -/* - * Rescan a string into a temporary buffer and return the result - * as a FILE_BUF. Note this function returns a struct, not a pointer. - * - * OUTPUT_MARKS nonzero means keep Newline markers found in the input - * and insert such markers when appropriate. See `rescan' for details. - * OUTPUT_MARKS is 1 for macroexpanding a macro argument separately - * before substitution; it is 0 for other uses. - */ -FILE_BUF -expand_to_temp_buffer (buf, limit, output_marks) - U_CHAR *buf, *limit; - int output_marks; -{ - register FILE_BUF *ip; - FILE_BUF obuf; - int length = limit - buf; - U_CHAR *buf1; - int odepth = indepth; - - if (length < 0) - abort (); - - /* Set up the input on the input stack. */ - - buf1 = (U_CHAR *) alloca (length + 1); - { - register U_CHAR *p1 = buf; - register U_CHAR *p2 = buf1; - - while (p1 != limit) - *p2++ = *p1++; - } - buf1[length] = 0; - - /* Set up to receive the output. */ - - obuf.length = length * 2 + 100; /* Usually enough. Why be stingy? */ - obuf.bufp = obuf.buf = (U_CHAR *) xmalloc (obuf.length); - obuf.fname = 0; - obuf.macro = 0; - obuf.free_ptr = 0; - - CHECK_DEPTH ({return obuf;}); - - ++indepth; - - ip = &instack[indepth]; - ip->fname = 0; - ip->macro = 0; - ip->free_ptr = 0; - ip->length = length; - ip->buf = ip->bufp = buf1; - ip->if_stack = if_stack; - - ip->lineno = obuf.lineno = 1; - - /* Scan the input, create the output. */ - - rescan (&obuf, output_marks); - - /* Pop input stack to original state. */ - --indepth; - - if (indepth != odepth) - abort (); - - /* Record the output. */ - obuf.length = obuf.bufp - obuf.buf; - - return obuf; -} - -/* - * Process a # directive. Expects IP->bufp to point to the '#', as in - * `#define foo bar'. Passes to the command handler - * (do_define, do_include, etc.): the addresses of the 1st and - * last chars of the command (starting immediately after the # - * keyword), plus op and the keyword table pointer. If the command - * contains comments it is copied into a temporary buffer sans comments - * and the temporary buffer is passed to the command handler instead. - * Likewise for backslash-newlines. - * - * Returns nonzero if this was a known # directive. - * Otherwise, returns zero, without advancing the input pointer. - */ - -int -handle_directive (ip, op) - FILE_BUF *ip, *op; -{ - register U_CHAR *bp, *cp; - register struct directive *kt; - register int ident_length; - U_CHAR *resume_p; - - /* Nonzero means we must copy the entire command - to get rid of comments or backslash-newlines. */ - int copy_command = 0; - - U_CHAR *ident, *after_ident; - - bp = ip->bufp; - /* Skip whitespace and \-newline. */ - while (1) { - if (is_hor_space[*bp]) - bp++; - else if (*bp == '/' && (newline_fix (bp + 1), bp[1]) == '*') { - ip->bufp = bp; - skip_to_end_of_comment (ip, &ip->lineno); - bp = ip->bufp; - } else if (*bp == '\\' && bp[1] == '\n') { - bp += 2; ip->lineno++; - } else break; - } - - /* Now find end of directive name. - If we encounter a backslash-newline, exchange it with any following - symbol-constituents so that we end up with a contiguous name. */ - - cp = bp; - while (1) { - if (is_idchar[*cp]) - cp++; - else { - if (*cp == '\\' && cp[1] == '\n') - name_newline_fix (cp); - if (is_idchar[*cp]) - cp++; - else break; - } - } - ident_length = cp - bp; - ident = bp; - after_ident = cp; - - /* A line of just `#' becomes blank. */ - - if (ident_length == 0 && *after_ident == '\n') { - ip->bufp = after_ident; - return 1; - } - - /* - * Decode the keyword and call the appropriate expansion - * routine, after moving the input pointer up to the next line. - */ - for (kt = directive_table; kt->length > 0; kt++) { - if (kt->length == ident_length && !strncmp (kt->name, ident, ident_length)) { - register U_CHAR *buf; - register U_CHAR *limit = ip->buf + ip->length; - int unterminated = 0; - - /* Nonzero means do not delete comments within the directive. - #define needs this when -traditional. */ - int keep_comments = traditional && kt->traditional_comments; - - /* Find the end of this command (first newline not backslashed - and not in a string or comment). - Set COPY_COMMAND if the command must be copied - (it contains a backslash-newline or a comment). */ - - buf = bp = after_ident; - while (bp < limit) { - register U_CHAR c = *bp++; - switch (c) { - case '\\': - if (bp < limit) { - if (*bp == '\n') { - ip->lineno++; - copy_command = 1; - } - bp++; - } - break; - - case '\'': - case '\"': - bp = skip_quoted_string (bp - 1, limit, ip->lineno, &ip->lineno, ©_command, &unterminated); - /* Don't bother calling the directive if we already got an error - message due to unterminated string. Skip everything and pretend - we called the directive. */ - if (unterminated) { - if (traditional) { - /* Traditional preprocessing permits unterminated strings. */ - ip->bufp = bp; - goto endloop1; - } - ip->bufp = bp; - return 1; - } - break; - - /* <...> is special for #include. */ - case '<': - if (!kt->angle_brackets) - break; - while (*bp && *bp != '>') bp++; - break; - - case '/': - if (*bp == '\\' && bp[1] == '\n') - newline_fix (bp); - if (*bp == '*' - || (cplusplus && *bp == '/')) { - U_CHAR *obp = bp - 1; - ip->bufp = bp + 1; - skip_to_end_of_comment (ip, &ip->lineno); - bp = ip->bufp; - /* No need to copy the command because of a comment at the end; - just don't include the comment in the directive. */ - if (bp == limit || *bp == '\n') { - bp = obp; - goto endloop1; - } - /* Don't remove the comments if -traditional. */ - if (! keep_comments) - copy_command++; - } - break; - - case '\n': - --bp; /* Point to the newline */ - ip->bufp = bp; - goto endloop1; - } - } - ip->bufp = bp; - - endloop1: - resume_p = ip->bufp; - /* BP is the end of the directive. - RESUME_P is the next interesting data after the directive. - A comment may come between. */ - - if (copy_command) { - register U_CHAR *xp = buf; - /* Need to copy entire command into temp buffer before dispatching */ - - cp = (U_CHAR *) alloca (bp - buf + 5); /* room for cmd plus - some slop */ - buf = cp; - - /* Copy to the new buffer, deleting comments - and backslash-newlines (and whitespace surrounding the latter). */ - - while (xp < bp) { - register U_CHAR c = *xp++; - *cp++ = c; - - switch (c) { - case '\n': - break; - - /* <...> is special for #include. */ - case '<': - if (!kt->angle_brackets) - break; - while (xp < bp && c != '>') { - c = *xp++; - if (c == '\\' && xp < bp && *xp == '\n') - xp++, ip->lineno++; - else - *cp++ = c; - } - break; - - case '\\': - if (*xp == '\n') { - xp++; - cp--; - if (cp != buf && is_space[cp[-1]]) { - while (cp != buf && is_space[cp[-1]]) cp--; - cp++; - SKIP_WHITE_SPACE (xp); - } else if (is_space[*xp]) { - *cp++ = *xp++; - SKIP_WHITE_SPACE (xp); - } - } - break; - - case '\'': - case '\"': - { - register U_CHAR *bp1 - = skip_quoted_string (xp - 1, limit, ip->lineno, 0, 0, 0); - while (xp != bp1) - *cp++ = *xp++; - } - break; - - case '/': - if (*xp == '*' - || (cplusplus && *xp == '/')) { - ip->bufp = xp + 1; - skip_to_end_of_comment (ip, 0); - if (keep_comments) - while (xp != ip->bufp) - *cp++ = *xp++; - /* Delete or replace the slash. */ - else if (traditional) - cp--; - else - cp[-1] = ' '; - xp = ip->bufp; - } - } - } - - /* Null-terminate the copy. */ - - *cp = 0; - } - else - cp = bp; - - ip->bufp = resume_p; - - /* Some directives should be written out for cc1 to process, - just as if they were not defined. */ - - if (kt->pass_thru) { - int len; - - /* Output directive name. */ - check_expand (op, kt->length+1); - *op->bufp++ = '#'; - bcopy (kt->name, op->bufp, kt->length); - op->bufp += kt->length; - - /* Output arguments. */ - len = (cp - buf); - check_expand (op, len); - bcopy (buf, op->bufp, len); - op->bufp += len; - } - - /* Call the appropriate command handler. buf now points to - either the appropriate place in the input buffer, or to - the temp buffer if it was necessary to make one. cp - points to the first char after the contents of the (possibly - copied) command, in either case. */ - (*kt->func) (buf, cp, op, kt); - check_expand (op, ip->length - (ip->bufp - ip->buf)); - - return 1; - } - } - - return 0; -} - -static char *monthnames[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", - }; - -/* - * expand things like __FILE__. Place the expansion into the output - * buffer *without* rescanning. - */ -special_symbol (hp, op) - HASHNODE *hp; - FILE_BUF *op; -{ - char *buf; - time_t t; - int i, len; - int true_indepth; - FILE_BUF *ip = NULL; - static struct tm *timebuf = NULL; - struct tm *localtime (); - - int paren = 0; /* For special `defined' keyword */ - - for (i = indepth; i >= 0; i--) - if (instack[i].fname != NULL) { - ip = &instack[i]; - break; - } - if (ip == NULL) { - error ("cccp error: not in any file?!"); - return; /* the show must go on */ - } - - switch (hp->type) { - case T_FILE: - case T_BASE_FILE: - { - char *string; - if (hp->type == T_FILE) - string = ip->fname; - else - string = instack[0].fname; - - if (string) - { - buf = (char *) alloca (3 + strlen (string)); - sprintf (buf, "\"%s\"", string); - } - else - buf = "\"\""; - - break; - } - - case T_INCLUDE_LEVEL: - true_indepth = 0; - for (i = indepth; i >= 0; i--) - if (instack[i].fname != NULL) - true_indepth++; - - buf = (char *) alloca (8); /* Eigth bytes ought to be more than enough */ - sprintf (buf, "%d", true_indepth - 1); - break; - - case T_VERSION: - buf = (char *) alloca (3 + strlen (version_string)); - sprintf (buf, "\"%s\"", version_string); - break; - - case T_CONST: - buf = (char *) alloca (4 * sizeof (int)); - sprintf (buf, "%d", hp->value.ival); - break; - - case T_SPECLINE: - buf = (char *) alloca (10); - sprintf (buf, "%d", ip->lineno); - break; - - case T_DATE: - case T_TIME: - if (timebuf == NULL) { - t = time (0); - timebuf = localtime (&t); - } - buf = (char *) alloca (20); - if (hp->type == T_DATE) - sprintf (buf, "\"%s %2d %4d\"", monthnames[timebuf->tm_mon], - timebuf->tm_mday, timebuf->tm_year + 1900); - else - sprintf (buf, "\"%02d:%02d:%02d\"", timebuf->tm_hour, timebuf->tm_min, - timebuf->tm_sec); - break; - - case T_SPEC_DEFINED: - buf = " 0 "; /* Assume symbol is not defined */ - ip = &instack[indepth]; - SKIP_WHITE_SPACE (ip->bufp); - if (*ip->bufp == '(') { - paren++; - ip->bufp++; /* Skip over the paren */ - SKIP_WHITE_SPACE (ip->bufp); - } - - if (!is_idstart[*ip->bufp]) - goto oops; - if (lookup (ip->bufp, -1, -1)) - buf = " 1 "; - while (is_idchar[*ip->bufp]) - ++ip->bufp; - SKIP_WHITE_SPACE (ip->bufp); - if (paren) { - if (*ip->bufp != ')') - goto oops; - ++ip->bufp; - } - break; - -oops: - - error ("`defined' must be followed by ident or (ident)"); - break; - - default: - error ("cccp error: invalid special hash type"); /* time for gdb */ - abort (); - } - len = strlen (buf); - check_expand (op, len); - bcopy (buf, op->bufp, len); - op->bufp += len; - - return; -} - - -/* Routines to handle #directives */ - -/* - * Process include file by reading it in and calling rescan. - * Expects to see "fname" or on the input. - */ - -do_include (buf, limit, op, keyword) - U_CHAR *buf, *limit; - FILE_BUF *op; - struct directive *keyword; -{ - char *fname; /* Dynamically allocated fname buffer */ - U_CHAR *fbeg, *fend; /* Beginning and end of fname */ - - struct file_name_list *stackp = include; /* Chain of dirs to search */ - struct file_name_list dsp[1]; /* First in chain, if #include "..." */ - int flen; - - int f; /* file number */ - - int retried = 0; /* Have already tried macro - expanding the include line*/ - FILE_BUF trybuf; /* It got expanded into here */ - int system_header_p = 0; /* 0 for "...", 1 for <...> */ - - f= -1; /* JF we iz paranoid! */ - -get_filename: - - fbeg = buf; - SKIP_WHITE_SPACE (fbeg); - /* Discard trailing whitespace so we can easily see - if we have parsed all the significant chars we were given. */ - while (limit != fbeg && is_hor_space[limit[-1]]) limit--; - - switch (*fbeg++) { - case '\"': - fend = fbeg; - while (fend != limit && *fend != '\"') - fend++; - if (*fend == '\"' && fend + 1 == limit) { - FILE_BUF *fp; - - /* We have "filename". Figure out directory this source - file is coming from and put it on the front of the list. */ - - /* If -I- was specified, don't search current dir, only spec'd ones. */ - if (ignore_srcdir) break; - - for (fp = &instack[indepth]; fp >= instack; fp--) - { - int n; - char *ep,*nam; - extern char *rindex (); - - if ((nam = fp->fname) != NULL) { - /* Found a named file. Figure out dir of the file, - and put it in front of the search list. */ - dsp[0].next = stackp; - stackp = dsp; -#ifndef VMS - ep = rindex (nam, '/'); -#else /* VMS */ - ep = rindex (nam, ']'); - if (ep == NULL) ep = rindex (nam, '>'); - if (ep == NULL) ep = rindex (nam, ':'); - if (ep != NULL) ep++; -#endif /* VMS */ - if (ep != NULL) { - n = ep - nam; - dsp[0].fname = (char *) alloca (n + 1); - strncpy (dsp[0].fname, nam, n); - dsp[0].fname[n] = '\0'; - if (n > max_include_len) max_include_len = n; - } else { - dsp[0].fname = 0; /* Current directory */ - } - break; - } - } - break; - } - goto fail; - - case '<': - fend = fbeg; - while (fend != limit && *fend != '>') fend++; - if (*fend == '>' && fend + 1 == limit) { - system_header_p = 1; - /* If -I-, start with the first -I dir after the -I-. */ - if (first_bracket_include) - stackp = first_bracket_include; - break; - } - goto fail; - - default: - fail: - if (retried) { - error ("#include expects \"fname\" or "); - return; - } else { - trybuf = expand_to_temp_buffer (buf, limit, 0); - buf = (U_CHAR *) alloca (trybuf.bufp - trybuf.buf + 1); - bcopy (trybuf.buf, buf, trybuf.bufp - trybuf.buf); - limit = buf + (trybuf.bufp - trybuf.buf); - free (trybuf.buf); - retried++; - goto get_filename; - } - } - - flen = fend - fbeg; - fname = (char *) alloca (max_include_len + flen + 2); - /* + 2 above for slash and terminating null. */ - - /* If specified file name is absolute, just open it. */ - - if (*fbeg == '/') { - strncpy (fname, fbeg, flen); - fname[flen] = 0; - f = open (fname, O_RDONLY, 0666); - } else { - /* Search directory path, trying to open the file. - Copy each filename tried into FNAME. */ - - for (; stackp; stackp = stackp->next) { - if (stackp->fname) { - strcpy (fname, stackp->fname); - strcat (fname, "/"); - fname[strlen (fname) + flen] = 0; - } else { - fname[0] = 0; - } - strncat (fname, fbeg, flen); -#ifdef VMS - /* Change this 1/2 Unix 1/2 VMS file specification into a - full VMS file specification */ - if (stackp->fname && (stackp->fname[0] != 0)) { - /* Fix up the filename */ - hack_vms_include_specification (fname); - } else { - /* This is a normal VMS filespec, so use it unchanged. */ - strncpy (fname, fbeg, flen); - fname[flen] = 0; - } -#endif /* VMS */ - if ((f = open (fname, O_RDONLY, 0666)) >= 0) - break; - } - } - - if (f < 0) { - strncpy (fname, fbeg, flen); - fname[flen] = 0; - error_from_errno (fname); - - /* For -M, add this file to the dependencies. */ - if (print_deps > (system_header_p || (system_include_depth > 0))) { - if (system_header_p) - warning ("nonexistent file <%.*s> omitted from dependency output", - fend - fbeg, fbeg); - else - { - deps_output (fbeg, fend - fbeg); - deps_output (" ", 0); - } - } - } else { - - /* Check to see if this include file is a once-only include file. - If so, give up. */ - - struct file_name_list* ptr; - - for (ptr = dont_repeat_files; ptr; ptr = ptr->next) { - if (!strcmp (ptr->fname, fname)) { - close (f); - return; /* This file was once'd. */ - } - } - - for (ptr = all_include_files; ptr; ptr = ptr->next) { - if (!strcmp (ptr->fname, fname)) - break; /* This file was included before. */ - } - - if (ptr == 0) { - /* This is the first time for this file. */ - /* Add it to list of files included. */ - - ptr = (struct file_name_list *) xmalloc (sizeof (struct file_name_list)); - ptr->next = all_include_files; - all_include_files = ptr; - ptr->fname = savestring (fname); - - /* For -M, add this file to the dependencies. */ - if (print_deps > (system_header_p || (system_include_depth > 0))) { - deps_output (fname, strlen (fname)); - deps_output (" ", 0); - } - } - - if (system_header_p) - system_include_depth++; - - /* Actually process the file. */ - finclude (f, fname, op); - - if (system_header_p) - system_include_depth--; - - close (f); - } -} - -/* Process the contents of include file FNAME, already open on descriptor F, - with output to OP. */ - -finclude (f, fname, op) - int f; - char *fname; - FILE_BUF *op; -{ - int st_mode; - long st_size; - long i; - FILE_BUF *fp; /* For input stack frame */ - int success = 0; - - CHECK_DEPTH (return;); - - if (file_size_and_mode (f, &st_mode, &st_size) < 0) - goto nope; /* Impossible? */ - - fp = &instack[indepth + 1]; - bzero (fp, sizeof (FILE_BUF)); - fp->fname = fname; - fp->length = 0; - fp->lineno = 1; - fp->if_stack = if_stack; - - if (S_ISREG (st_mode)) { - fp->buf = (U_CHAR *) alloca (st_size + 2); - fp->bufp = fp->buf; - - /* Read the file contents, knowing that st_size is an upper bound - on the number of bytes we can read. */ - while (st_size > 0) { - i = read (f, fp->buf + fp->length, st_size); - if (i <= 0) { - if (i == 0) break; - goto nope; - } - fp->length += i; - st_size -= i; - } - } - else { - /* Cannot count its file size before reading. - First read the entire file into heap and - copy them into buffer on stack. */ - - U_CHAR *bufp; - U_CHAR *basep; - int bsize = 2000; - - st_size = 0; - basep = (U_CHAR *) xmalloc (bsize + 2); - bufp = basep; - - for (;;) { - i = read (f, bufp, bsize - st_size); - if (i < 0) - goto nope; /* error! */ - if (i == 0) - break; /* End of file */ - st_size += i; - bufp += i; - if (bsize == st_size) { /* Buffer is full! */ - bsize *= 2; - basep = (U_CHAR *) xrealloc (basep, bsize + 2); - bufp = basep + st_size; /* May have moved */ - } - } - fp->buf = (U_CHAR *) alloca (st_size + 2); - fp->bufp = fp->buf; - bcopy (basep, fp->buf, st_size); - fp->length = st_size; - free (basep); - } - - if (!no_trigraphs) - trigraph_pcp (fp); - - if (fp->length > 0 && fp->buf[fp->length-1] != '\n') - fp->buf[fp->length++] = '\n'; - fp->buf[fp->length] = '\0'; - - success = 1; - indepth++; - - output_line_command (fp, op, 0, enter_file); - rescan (op, 0); - indepth--; - output_line_command (&instack[indepth], op, 0, leave_file); - -nope: - - if (!success) - perror_with_name (fname); - - close (f); -} - -/* The arglist structure is built by do_define to tell - collect_definition where the argument names begin. That - is, for a define like "#define f(x,y,z) foo+x-bar*y", the arglist - would contain pointers to the strings x, y, and z. - Collect_definition would then build a DEFINITION node, - with reflist nodes pointing to the places x, y, and z had - appeared. So the arglist is just convenience data passed - between these two routines. It is not kept around after - the current #define has been processed and entered into the - hash table. */ - -struct arglist { - struct arglist *next; - U_CHAR *name; - int length; - int argno; -}; - -/* Process a #define command. -BUF points to the contents of the #define command, as a continguous string. -LIMIT points to the first character past the end of the definition. -KEYWORD is the keyword-table entry for #define. */ - -do_define (buf, limit, op, keyword) - U_CHAR *buf, *limit; - FILE_BUF *op; - struct directive *keyword; -{ - U_CHAR *bp; /* temp ptr into input buffer */ - U_CHAR *symname; /* remember where symbol name starts */ - int sym_length; /* and how long it is */ - - DEFINITION *defn; - int arglengths = 0; /* Accumulate lengths of arg names - plus number of args. */ - int hashcode; - - bp = buf; - - while (is_hor_space[*bp]) - bp++; - - symname = bp; /* remember where it starts */ - while (is_idchar[*bp] && bp < limit) { - bp++; - } - sym_length = bp - symname; - if (sym_length == 0) - error ("invalid macro name"); - else if (!is_idstart[*symname]) { - U_CHAR *msg; /* what pain... */ - msg = (U_CHAR *) alloca (sym_length + 1); - bcopy (symname, msg, sym_length); - msg[sym_length] = 0; - error ("invalid macro name `%s'", msg); - } else { - if (! strncmp (symname, "defined", 7) && sym_length == 7) - error ("defining `defined' as a macro"); - } - - /* lossage will occur if identifiers or control keywords are broken - across lines using backslash. This is not the right place to take - care of that. */ - - if (*bp == '(') { - struct arglist *arg_ptrs = NULL; - int argno = 0; - - bp++; /* skip '(' */ - SKIP_WHITE_SPACE (bp); - - /* Loop over macro argument names. */ - while (*bp != ')') { - struct arglist *temp; - - temp = (struct arglist *) alloca (sizeof (struct arglist)); - temp->name = bp; - temp->next = arg_ptrs; - temp->argno = argno++; - arg_ptrs = temp; - - if (!is_idstart[*bp]) - warning ("parameter name starts with a digit in #define"); - - /* Find the end of the arg name. */ - while (is_idchar[*bp]) { - bp++; - } - temp->length = bp - temp->name; - arglengths += temp->length + 2; - SKIP_WHITE_SPACE (bp); - if (temp->length == 0 || (*bp != ',' && *bp != ')')) { - error ("badly punctuated parameter list in #define"); - goto nope; - } - if (*bp == ',') { - bp++; - SKIP_WHITE_SPACE (bp); - } - if (bp >= limit) { - error ("unterminated parameter list in #define"); - goto nope; - } - } - - ++bp; /* skip paren */ - /* Skip exactly one space or tab if any. */ - if (bp < limit && (*bp == ' ' || *bp == '\t')) ++bp; - /* now everything from bp before limit is the definition. */ - defn = collect_expansion (bp, limit, argno, arg_ptrs); - - /* Now set defn->argnames to the result of concatenating - the argument names in reverse order - with comma-space between them. */ - defn->argnames = (U_CHAR *) xmalloc (arglengths + 1); - { - struct arglist *temp; - int i = 0; - for (temp = arg_ptrs; temp; temp = temp->next) { - bcopy (temp->name, &defn->argnames[i], temp->length); - i += temp->length; - if (temp->next != 0) { - defn->argnames[i++] = ','; - defn->argnames[i++] = ' '; - } - } - defn->argnames[i] = 0; - } - } else { - /* simple expansion or empty definition; gobble it */ - if (is_hor_space[*bp]) - ++bp; /* skip exactly one blank/tab char */ - /* now everything from bp before limit is the definition. */ - defn = collect_expansion (bp, limit, -1, 0); - defn->argnames = (U_CHAR *) ""; - } - - hashcode = hashf (symname, sym_length, HASHSIZE); - - { - HASHNODE *hp; - if ((hp = lookup (symname, sym_length, hashcode)) != NULL) { - if (hp->type != T_MACRO - || compare_defs (defn, hp->value.defn)) { - U_CHAR *msg; /* what pain... */ - msg = (U_CHAR *) alloca (sym_length + 20); - bcopy (symname, msg, sym_length); - strcpy ((char *) (msg + sym_length), " redefined"); - warning (msg); - } - /* Replace the old definition. */ - hp->type = T_MACRO; - hp->value.defn = defn; - } else - install (symname, sym_length, T_MACRO, defn, hashcode); - } - - return 0; - -nope: - - return 1; -} - -/* - * return zero if two DEFINITIONs are isomorphic - */ -int -compare_defs (d1, d2) - DEFINITION *d1, *d2; -{ - register struct reflist *a1, *a2; - register U_CHAR *p1 = d1->expansion; - register U_CHAR *p2 = d2->expansion; - int first = 1; - - if (d1->nargs != d2->nargs) - return 1; - if (strcmp ((char *)d1->argnames, (char *)d2->argnames)) - return 1; - for (a1 = d1->pattern, a2 = d2->pattern; a1 && a2; - a1 = a1->next, a2 = a2->next) { - if (!((a1->nchars == a2->nchars && ! strncmp (p1, p2, a1->nchars)) - || ! comp_def_part (first, p1, a1->nchars, p2, a2->nchars, 0)) - || a1->argno != a2->argno - || a1->stringify != a2->stringify - || a1->raw_before != a2->raw_before - || a1->raw_after != a2->raw_after) - return 1; - first = 0; - p1 += a1->nchars; - p2 += a2->nchars; - } - if (a1 != a2) - return 1; - if (comp_def_part (first, p1, d1->length - (p1 - d1->expansion), - p2, d2->length - (p2 - d2->expansion), 1)) - return 1; - return 0; -} - -/* Return 1 if two parts of two macro definitions are effectively different. - One of the parts starts at BEG1 and has LEN1 chars; - the other has LEN2 chars at BEG2. - Any sequence of whitespace matches any other sequence of whitespace. - FIRST means these parts are the first of a macro definition; - so ignore leading whitespace entirely. - LAST means these parts are the last of a macro definition; - so ignore trailing whitespace entirely. */ - -comp_def_part (first, beg1, len1, beg2, len2, last) - int first; - U_CHAR *beg1, *beg2; - int len1, len2; - int last; -{ - register U_CHAR *end1 = beg1 + len1; - register U_CHAR *end2 = beg2 + len2; - if (first) { - while (beg1 != end1 && is_space[*beg1]) beg1++; - while (beg2 != end2 && is_space[*beg2]) beg2++; - } - if (last) { - while (beg1 != end1 && is_space[end1[-1]]) end1--; - while (beg2 != end2 && is_space[end2[-1]]) end2--; - } - while (beg1 != end1 && beg2 != end2) { - if (is_space[*beg1] && is_space[*beg2]) { - while (beg1 != end1 && is_space[*beg1]) beg1++; - while (beg2 != end2 && is_space[*beg2]) beg2++; - } else if (*beg1 == *beg2) { - beg1++; beg2++; - } else break; - } - return (beg1 != end1) || (beg2 != end2); -} - -/* Read a replacement list for a macro with parameters. - Build the DEFINITION structure. - Reads characters of text starting at BUF until LIMIT. - ARGLIST specifies the formal parameters to look for - in the text of the definition; NARGS is the number of args - in that list, or -1 for a macro name that wants no argument list. - MACRONAME is the macro name itself (so we can avoid recursive expansion) - and NAMELEN is its length in characters. - -Note that comments and backslash-newlines have already been deleted -from the argument. */ - -/* Leading and trailing Space, Tab, etc. are converted to markers - Newline Space, Newline Tab, etc. - Newline Space makes a space in the final output - but is discarded if stringified. (Newline Tab is similar but - makes a Tab instead.) - - If there is no trailing whitespace, a Newline Space is added at the end - to prevent concatenation that would be contrary to the standard. */ - -DEFINITION * -collect_expansion (buf, end, nargs, arglist) - U_CHAR *buf, *end; - int nargs; - struct arglist *arglist; -{ - DEFINITION *defn; - register U_CHAR *p, *limit, *lastp, *exp_p; - struct reflist *endpat = NULL; - /* Pointer to first nonspace after last ## seen. */ - U_CHAR *concat = 0; - /* Pointer to first nonspace after last single-# seen. */ - U_CHAR *stringify = 0; - int maxsize; - int expected_delimiter = '\0'; - - /* Scan thru the replacement list, ignoring comments and quoted - strings, picking up on the macro calls. It does a linear search - thru the arg list on every potential symbol. Profiling might say - that something smarter should happen. */ - - if (end < buf) - abort (); - - /* Find the beginning of the trailing whitespace. */ - /* Find end of leading whitespace. */ - limit = end; - p = buf; - while (p < limit && is_space[limit[-1]]) limit--; - while (p < limit && is_space[*p]) p++; - - /* Allocate space for the text in the macro definition. - Leading and trailing whitespace chars need 2 bytes each. - Each other input char may or may not need 1 byte, - so this is an upper bound. - The extra 2 are for invented trailing newline-marker and final null. */ - maxsize = (sizeof (DEFINITION) - + 2 * (end - limit) + 2 * (p - buf) - + (limit - p) + 3); - defn = (DEFINITION *) xcalloc (1, maxsize); - - defn->nargs = nargs; - exp_p = defn->expansion = (U_CHAR *) defn + sizeof (DEFINITION); - lastp = exp_p; - - p = buf; - - /* Convert leading whitespace to Newline-markers. */ - while (p < limit && is_space[*p]) { - *exp_p++ = '\n'; - *exp_p++ = *p++; - } - - if (p + 1 < limit && p[0] == '#' && p[1] == '#') { - error ("## operator at start of macro definition"); - p += 2; - } - - /* Process the main body of the definition. */ - while (p < limit) { - int skipped_arg = 0; - register U_CHAR c = *p++; - - *exp_p++ = c; - - if (!traditional) { - switch (c) { - case '\'': - case '\"': - for (; p < limit && *p != c; p++) { - *exp_p++ = *p; - if (*p == '\\') { - *exp_p++ = *++p; - } - } - *exp_p++ = *p++; - break; - - /* Special hack: if a \# is written in the #define - include a # in the definition. This is useless for C code - but useful for preprocessing other things. */ - - case '\\': - if (p < limit && *p == '#') { - /* Pass through this # */ - exp_p--; - *exp_p++ = *p++; - } else if (p < limit) { - /* Otherwise backslash goes through but makes next char ordinary. */ - *exp_p++ = *p++; - } - break; - - case '#': - if (p < limit && *p == '#') { - /* ##: concatenate preceding and following tokens. */ - /* Take out the first #, discard preceding whitespace. */ - exp_p--; - while (exp_p > lastp && is_hor_space[exp_p[-1]]) - --exp_p; - /* Skip the second #. */ - p++; - /* Discard following whitespace. */ - SKIP_WHITE_SPACE (p); - concat = p; - if (limit <= p) - error ("## operator at end of macro definition"); - } else { - /* Single #: stringify following argument ref. - Don't leave the # in the expansion. */ - exp_p--; - SKIP_WHITE_SPACE (p); - if (p == limit || ! is_idstart[*p] || nargs <= 0) - error ("# operator should be followed by a macro argument name"); - else - stringify = p; - } - break; - } - } else { - /* In -traditional mode, recognize arguments inside strings and - and character constants, and ignore special properties of #. - Arguments inside strings are considered "stringified", but no - extra quote marks are supplied. */ - switch (c) { - case '\'': - case '\"': - if (expected_delimiter != '\0') { - if (c == expected_delimiter) - expected_delimiter = '\0'; - } else - expected_delimiter = c; - break; - - case '\\': - /* Backslash quotes delimiters and itself, but not macro args. */ - if (expected_delimiter != 0 && p < limit - && (*p == expected_delimiter || *p == '\\')) { - *exp_p++ = *p++; - continue; - } - break; - - case '/': - if (expected_delimiter != '\0') /* No comments inside strings. */ - break; - if (*p == '*') { - /* If we find a comment that wasn't removed by handle_directive, - this must be -traditional. So replace the comment with - nothing at all. */ - exp_p--; - p += 1; - while (p < limit && !(p[-2] == '*' && p[-1] == '/')) - p++; -#if 0 - /* Mark this as a concatenation-point, as if it had been ##. */ - concat = p; -#endif - } - break; - } - } - - if (is_idchar[c] && nargs > 0) { - U_CHAR *id_beg = p - 1; - int id_len; - - --exp_p; - while (p != limit && is_idchar[*p]) p++; - id_len = p - id_beg; - - if (is_idstart[c]) { - register struct arglist *arg; - - for (arg = arglist; arg != NULL; arg = arg->next) { - struct reflist *tpat; - - if (arg->name[0] == c - && arg->length == id_len - && strncmp (arg->name, id_beg, id_len) == 0) { - /* make a pat node for this arg and append it to the end of - the pat list */ - tpat = (struct reflist *) xmalloc (sizeof (struct reflist)); - tpat->next = NULL; - tpat->raw_before = concat == id_beg; - tpat->raw_after = 0; - tpat->stringify = (traditional ? expected_delimiter != '\0' - : stringify == id_beg); - - if (endpat == NULL) - defn->pattern = tpat; - else - endpat->next = tpat; - endpat = tpat; - - tpat->argno = arg->argno; - tpat->nchars = exp_p - lastp; - { - register U_CHAR *p1 = p; - SKIP_WHITE_SPACE (p1); - if (p1 + 2 <= limit && p1[0] == '#' && p1[1] == '#') - tpat->raw_after = 1; - } - lastp = exp_p; /* place to start copying from next time */ - skipped_arg = 1; - break; - } - } - } - - /* If this was not a macro arg, copy it into the expansion. */ - if (! skipped_arg) { - register U_CHAR *lim1 = p; - p = id_beg; - while (p != lim1) - *exp_p++ = *p++; - if (stringify == id_beg) - error ("# operator should be followed by a macro argument name"); - } - } - } - - if (limit < end) { - /* Convert trailing whitespace to Newline-markers. */ - while (limit < end && is_space[*limit]) { - *exp_p++ = '\n'; - *exp_p++ = *limit++; - } - } else if (!traditional) { - /* There is no trailing whitespace, so invent some. */ - *exp_p++ = '\n'; - *exp_p++ = ' '; - } - - *exp_p = '\0'; - - defn->length = exp_p - defn->expansion; - - /* Crash now if we overrun the allocated size. */ - if (defn->length + 1 > maxsize) - abort (); - -#if 0 -/* This isn't worth the time it takes. */ - /* give back excess storage */ - defn->expansion = (U_CHAR *) xrealloc (defn->expansion, defn->length + 1); -#endif - - return defn; -} - -/* - * interpret #line command. Remembers previously seen fnames - * in its very own hash table. - */ -#define FNAME_HASHSIZE 37 - -do_line (buf, limit, op, keyword) - U_CHAR *buf, *limit; - FILE_BUF *op; - struct directive *keyword; -{ - register U_CHAR *bp; - FILE_BUF *ip = &instack[indepth]; - FILE_BUF tem; - int new_lineno; - enum file_change_code file_change = same_file; - - /* Expand any macros. */ - tem = expand_to_temp_buffer (buf, limit, 0); - - /* Point to macroexpanded line, which is null-terminated now. */ - bp = tem.buf; - SKIP_WHITE_SPACE (bp); - - if (!isdigit (*bp)) { - error ("invalid format #line command"); - return; - } - - /* The Newline at the end of this line remains to be processed. - To put the next line at the specified line number, - we must store a line number now that is one less. */ - new_lineno = atoi (bp) - 1; - - /* skip over the line number. */ - while (isdigit (*bp)) - bp++; - -#if 0 /* #line 10"foo.c" is supposed to be allowed. */ - if (*bp && !is_space[*bp]) { - error ("invalid format #line command"); - return; - } -#endif - - SKIP_WHITE_SPACE (bp); - - if (*bp == '\"') { - static HASHNODE *fname_table[FNAME_HASHSIZE]; - HASHNODE *hp, **hash_bucket; - U_CHAR *fname; - int fname_length; - - fname = ++bp; - - while (*bp && *bp != '\"') - bp++; - if (*bp != '\"') { - error ("invalid format #line command"); - return; - } - - fname_length = bp - fname; - - bp++; - SKIP_WHITE_SPACE (bp); - if (*bp) { - if (*bp == '1') - file_change = enter_file; - else if (*bp == '2') - file_change = leave_file; - else { - error ("invalid format #line command"); - return; - } - - bp++; - SKIP_WHITE_SPACE (bp); - if (*bp) { - error ("invalid format #line command"); - return; - } - } - - hash_bucket = - &fname_table[hashf (fname, fname_length, FNAME_HASHSIZE)]; - for (hp = *hash_bucket; hp != NULL; hp = hp->next) - if (hp->length == fname_length && - strncmp (hp->value.cpval, fname, fname_length) == 0) { - ip->fname = hp->value.cpval; - break; - } - if (hp == 0) { - /* Didn't find it; cons up a new one. */ - hp = (HASHNODE *) xcalloc (1, sizeof (HASHNODE) + fname_length + 1); - hp->next = *hash_bucket; - *hash_bucket = hp; - - hp->length = fname_length; - ip->fname = hp->value.cpval = ((char *) hp) + sizeof (HASHNODE); - bcopy (fname, hp->value.cpval, fname_length); - } - } else if (*bp) { - error ("invalid format #line command"); - return; - } - - ip->lineno = new_lineno; - output_line_command (ip, op, 0, file_change); - check_expand (op, ip->length - (ip->bufp - ip->buf)); -} - -/* - * remove all definitions of symbol from symbol table. - * according to un*x /lib/cpp, it is not an error to undef - * something that has no definitions, so it isn't one here either. - */ -do_undef (buf, limit, op, keyword) - U_CHAR *buf, *limit; - FILE_BUF *op; - struct directive *keyword; -{ - HASHNODE *hp; - - SKIP_WHITE_SPACE (buf); - - if (! strncmp (buf, "defined", 7) && ! is_idchar[buf[7]]) - warning ("undefining `defined'"); - - while ((hp = lookup (buf, -1, -1)) != NULL) { - if (hp->type != T_MACRO) - warning ("undefining `%s'", hp->name); - delete_macro (hp); - } -} - -/* - * Report a fatal error detected by the program we are processing. - * Use the text of the line in the error message, then terminate. - * (We use error() because it prints the filename & line#.) - */ -do_error (buf, limit, op, keyword) - U_CHAR *buf, *limit; - FILE_BUF *op; - struct directive *keyword; -{ - int length = limit - buf; - char *copy = (char *) xmalloc (length + 1); - bcopy (buf, copy, length); - copy[length] = 0; - SKIP_WHITE_SPACE (copy); - error ("#error %s", copy); - exit (FATAL_EXIT_CODE); -} - -/* Remember the name of the current file being read from so that we can - avoid ever including it again. */ - -do_once () -{ - int i; - FILE_BUF *ip = NULL; - - for (i = indepth; i >= 0; i--) - if (instack[i].fname != NULL) { - ip = &instack[i]; - break; - } - - if (ip != NULL) - { - struct file_name_list *new; - - new = (struct file_name_list *) xmalloc (sizeof (struct file_name_list)); - new->next = dont_repeat_files; - dont_repeat_files = new; - new->fname = savestring (ip->fname); - } -} - -/* #pragma and its argument line have already been copied to the output file. - Here just check for recognized pragmas. */ - -do_pragma (buf, limit) - U_CHAR *buf, *limit; -{ - while (*buf == ' ' || *buf == '\t') - buf++; - if (!strncmp (buf, "once", 4)) - do_once (); -} - -#if 0 -/* This was a fun hack, but #pragma seems to start to be useful. - By failing to recognize it, we pass it through unchanged to cc1. */ - -/* - * the behavior of the #pragma directive is implementation defined. - * this implementation defines it as follows. - */ -do_pragma () -{ - close (0); - if (open ("/dev/tty", O_RDONLY, 0666) != 0) - goto nope; - close (1); - if (open ("/dev/tty", O_WRONLY, 0666) != 1) - goto nope; - execl ("/usr/games/hack", "#pragma", 0); - execl ("/usr/games/rogue", "#pragma", 0); - execl ("/usr/new/emacs", "-f", "hanoi", "9", "-kill", 0); - execl ("/usr/local/emacs", "-f", "hanoi", "9", "-kill", 0); -nope: - fatal ("You are in a maze of twisty compiler features, all different"); -} -#endif - -/* Just ignore #sccs, on systems where we define it at all. */ -do_sccs () -{ - if (pedantic) - error ("ANSI C does not allow #sccs"); -} - -/* - * handle #if command by - * 1) inserting special `defined' keyword into the hash table - * that gets turned into 0 or 1 by special_symbol (thus, - * if the luser has a symbol called `defined' already, it won't - * work inside the #if command) - * 2) rescan the input into a temporary output buffer - * 3) pass the output buffer to the yacc parser and collect a value - * 4) clean up the mess left from steps 1 and 2. - * 5) call conditional_skip to skip til the next #endif (etc.), - * or not, depending on the value from step 3. - */ - -do_if (buf, limit, op, keyword) - U_CHAR *buf, *limit; - FILE_BUF *op; - struct directive *keyword; -{ - int value; - FILE_BUF *ip = &instack[indepth]; - - value = eval_if_expression (buf, limit - buf); - conditional_skip (ip, value == 0, T_IF); -} - -/* - * handle a #elif directive by not changing if_stack either. - * see the comment above do_else. - */ - -do_elif (buf, limit, op, keyword) - U_CHAR *buf, *limit; - FILE_BUF *op; - struct directive *keyword; -{ - int value; - FILE_BUF *ip = &instack[indepth]; - - if (if_stack == instack[indepth].if_stack) { - error ("#elif not within a conditional"); - return; - } else { - if (if_stack->type != T_IF && if_stack->type != T_ELIF) { - error ("#elif after #else"); - fprintf (stderr, " (matches line %d", if_stack->lineno); - if (if_stack->fname != NULL && ip->fname != NULL && - strcmp (if_stack->fname, ip->fname) != 0) - fprintf (stderr, ", file %s", if_stack->fname); - fprintf (stderr, ")\n"); - } - if_stack->type = T_ELIF; - } - - if (if_stack->if_succeeded) - skip_if_group (ip, 0); - else { - value = eval_if_expression (buf, limit - buf); - if (value == 0) - skip_if_group (ip, 0); - else { - ++if_stack->if_succeeded; /* continue processing input */ - output_line_command (ip, op, 1, same_file); - } - } -} - -/* - * evaluate a #if expression in BUF, of length LENGTH, - * then parse the result as a C expression and return the value as an int. - */ -int -eval_if_expression (buf, length) - U_CHAR *buf; - int length; -{ - FILE_BUF temp_obuf; - HASHNODE *save_defined; - int value; - - save_defined = install ("defined", -1, T_SPEC_DEFINED, 0, -1); - temp_obuf = expand_to_temp_buffer (buf, buf + length, 0); - delete_macro (save_defined); /* clean up special symbol */ - - value = parse_c_expression (temp_obuf.buf); - - free (temp_obuf.buf); - - return value; -} - -/* - * routine to handle ifdef/ifndef. Try to look up the symbol, - * then do or don't skip to the #endif/#else/#elif depending - * on what directive is actually being processed. - */ -do_xifdef (buf, limit, op, keyword) - U_CHAR *buf, *limit; - FILE_BUF *op; - struct directive *keyword; -{ - int skip; - FILE_BUF *ip = &instack[indepth]; - U_CHAR *end; - - /* Discard leading and trailing whitespace. */ - SKIP_WHITE_SPACE (buf); - while (limit != buf && is_hor_space[limit[-1]]) limit--; - - /* Find the end of the identifier at the beginning. */ - for (end = buf; is_idchar[*end]; end++); - - if (end == buf) { - skip = (keyword->type == T_IFDEF); - if (! traditional) - warning (end == limit ? "#%s with no argument" - : "#%s argument starts with punctuation", - keyword->name); - } else { - if (pedantic && buf[0] >= '0' && buf[0] <= '9') - warning ("#%s argument starts with a digit", keyword->name); - else if (end != limit && !traditional) - warning ("garbage at end of #%s argument", keyword->name); - - skip = (lookup (buf, end-buf, -1) == NULL) ^ (keyword->type == T_IFNDEF); - } - - conditional_skip (ip, skip, T_IF); -} - -/* - * push TYPE on stack; then, if SKIP is nonzero, skip ahead. - */ -void -conditional_skip (ip, skip, type) - FILE_BUF *ip; - int skip; - enum node_type type; -{ - IF_STACK_FRAME *temp; - - temp = (IF_STACK_FRAME *) xcalloc (1, sizeof (IF_STACK_FRAME)); - temp->fname = ip->fname; - temp->lineno = ip->lineno; - temp->next = if_stack; - if_stack = temp; - - if_stack->type = type; - - if (skip != 0) { - skip_if_group (ip, 0); - return; - } else { - ++if_stack->if_succeeded; - output_line_command (ip, &outbuf, 1, same_file); - } -} - -/* - * skip to #endif, #else, or #elif. adjust line numbers, etc. - * leaves input ptr at the sharp sign found. - * If ANY is nonzero, return at next directive of any sort. - */ -void -skip_if_group (ip, any) - FILE_BUF *ip; - int any; -{ - register U_CHAR *bp = ip->bufp, *cp; - register U_CHAR *endb = ip->buf + ip->length; - struct directive *kt; - IF_STACK_FRAME *save_if_stack = if_stack; /* don't pop past here */ - U_CHAR *beg_of_line = bp; - - while (bp < endb) { - switch (*bp++) { - case '/': /* possible comment */ - if (*bp == '\\' && bp[1] == '\n') - newline_fix (bp); - if (*bp == '*' - || (cplusplus && *bp == '/')) { - ip->bufp = ++bp; - bp = skip_to_end_of_comment (ip, &ip->lineno); - } - break; - case '\"': - case '\'': - bp = skip_quoted_string (bp - 1, endb, ip->lineno, &ip->lineno, 0, 0); - break; - case '\\': - /* Char after backslash loses its special meaning. */ - if (bp < endb) { - if (*bp == '\n') - ++ip->lineno; /* But do update the line-count. */ - bp++; - } - break; - case '\n': - ++ip->lineno; - beg_of_line = bp; - break; - case '#': - ip->bufp = bp - 1; - - /* # keyword: a # must be first nonblank char on the line */ - if (beg_of_line == 0) - break; - /* Scan from start of line, skipping whitespace, comments - and backslash-newlines, and see if we reach this #. - If not, this # is not special. */ - bp = beg_of_line; - while (1) { - if (is_hor_space[*bp]) - bp++; - else if (*bp == '\\' && bp[1] == '\n') - bp += 2; - else if (*bp == '/' && bp[1] == '*') { - bp += 2; - while (!(*bp == '*' && bp[1] == '/')) - bp++; - bp += 2; - } - else if (cplusplus && *bp == '/' && bp[1] == '/') { - bp += 2; - while (*bp++ != '\n') ; - } - else break; - } - if (bp != ip->bufp) { - bp = ip->bufp + 1; /* Reset bp to after the #. */ - break; - } - - bp = ip->bufp + 1; /* point at '#' */ - - /* Skip whitespace and \-newline. */ - while (1) { - if (is_hor_space[*bp]) - bp++; - else if (*bp == '\\' && bp[1] == '\n') - bp += 2; - else break; - } - - cp = bp; - - /* Now find end of directive name. - If we encounter a backslash-newline, exchange it with any following - symbol-constituents so that we end up with a contiguous name. */ - - while (1) { - if (is_idchar[*bp]) - bp++; - else { - if (*bp == '\\' && bp[1] == '\n') - name_newline_fix (bp); - if (is_idchar[*bp]) - bp++; - else break; - } - } - - for (kt = directive_table; kt->length >= 0; kt++) { - IF_STACK_FRAME *temp; - if (strncmp (cp, kt->name, kt->length) == 0 - && !is_idchar[cp[kt->length]]) { - - /* If we are asked to return on next directive, - do so now. */ - if (any) - return; - - switch (kt->type) { - case T_IF: - case T_IFDEF: - case T_IFNDEF: - temp = (IF_STACK_FRAME *) xcalloc (1, sizeof (IF_STACK_FRAME)); - temp->next = if_stack; - if_stack = temp; - temp->lineno = ip->lineno; - temp->fname = ip->fname; - temp->type = kt->type; - break; - case T_ELSE: - case T_ENDIF: - if (pedantic && if_stack != save_if_stack) - validate_else (bp); - case T_ELIF: - if (if_stack == instack[indepth].if_stack) { - error ("#%s not within a conditional", kt->name); - break; - } - else if (if_stack == save_if_stack) - return; /* found what we came for */ - - if (kt->type != T_ENDIF) { - if (if_stack->type == T_ELSE) - error ("#else or #elif after #else"); - if_stack->type = kt->type; - break; - } - - temp = if_stack; - if_stack = if_stack->next; - free (temp); - break; - } - break; - } - } - } - } - ip->bufp = bp; - /* after this returns, rescan will exit because ip->bufp - now points to the end of the buffer. - rescan is responsible for the error message also. */ -} - -/* - * handle a #else directive. Do this by just continuing processing - * without changing if_stack ; this is so that the error message - * for missing #endif's etc. will point to the original #if. It - * is possible that something different would be better. - */ -do_else (buf, limit, op, keyword) - U_CHAR *buf, *limit; - FILE_BUF *op; - struct directive *keyword; -{ - FILE_BUF *ip = &instack[indepth]; - - if (pedantic) { - SKIP_WHITE_SPACE (buf); - if (buf != limit) - warning ("text following #else violates ANSI standard"); - } - - if (if_stack == instack[indepth].if_stack) { - error ("#else not within a conditional"); - return; - } else { - if (if_stack->type != T_IF && if_stack->type != T_ELIF) { - error ("#else after #else"); - fprintf (stderr, " (matches line %d", if_stack->lineno); - if (strcmp (if_stack->fname, ip->fname) != 0) - fprintf (stderr, ", file %s", if_stack->fname); - fprintf (stderr, ")\n"); - } - if_stack->type = T_ELSE; - } - - if (if_stack->if_succeeded) - skip_if_group (ip, 0); - else { - ++if_stack->if_succeeded; /* continue processing input */ - output_line_command (ip, op, 1, same_file); - } -} - -/* - * unstack after #endif command - */ -do_endif (buf, limit, op, keyword) - U_CHAR *buf, *limit; - FILE_BUF *op; - struct directive *keyword; -{ - if (pedantic) { - SKIP_WHITE_SPACE (buf); - if (buf != limit) - warning ("text following #endif violates ANSI standard"); - } - - if (if_stack == instack[indepth].if_stack) - error ("unbalanced #endif"); - else { - IF_STACK_FRAME *temp = if_stack; - if_stack = if_stack->next; - free (temp); - output_line_command (&instack[indepth], op, 1, same_file); - } -} - -/* When an #else or #endif is found while skipping failed conditional, - if -pedantic was specified, this is called to warn about text after - the command name. P points to the first char after the command name. */ - -validate_else (p) - register U_CHAR *p; -{ - /* Advance P over whitespace and comments. */ - while (1) { - if (*p == '\\' && p[1] == '\n') - p += 2; - if (is_hor_space[*p]) - p++; - else if (*p == '/') { - if (p[1] == '\\' && p[2] == '\n') - newline_fix (p + 1); - if (p[1] == '*') { - p += 2; - /* Don't bother warning about unterminated comments - since that will happen later. Just be sure to exit. */ - while (*p) { - if (p[1] == '\\' && p[2] == '\n') - newline_fix (p + 1); - if (*p == '*' && p[1] == '/') { - p += 2; - break; - } - p++; - } - } - else if (cplusplus && p[1] == '/') { - p += 2; - while (*p && *p++ != '\n') ; - } - } else break; - } - if (*p && *p != '\n') - warning ("text following #else or #endif violates ANSI standard"); -} - -/* - * Skip a comment, assuming the input ptr immediately follows the - * initial slash-star. Bump line counter as necessary. - * (The canonical line counter is &ip->lineno). - * Don't use this routine (or the next one) if bumping the line - * counter is not sufficient to deal with newlines in the string. - */ -U_CHAR * -skip_to_end_of_comment (ip, line_counter) - register FILE_BUF *ip; - int *line_counter; /* place to remember newlines, or NULL */ -{ - register U_CHAR *limit = ip->buf + ip->length; - register U_CHAR *bp = ip->bufp; - FILE_BUF *op = &outbuf; /* JF */ - int output = put_out_comments && !line_counter; - - /* JF this line_counter stuff is a crock to make sure the - comment is only put out once, no matter how many times - the comment is skipped. It almost works */ - if (output) { - *op->bufp++ = '/'; - *op->bufp++ = '*'; - } - if (cplusplus && bp[-1] == '/') { - if (output) { - while (bp < limit) - if ((*op->bufp++ = *bp++) == '\n') { - bp--; - break; - } - op->bufp[-1] = '*'; - *op->bufp++ = '/'; - *op->bufp++ = '\n'; - } else { - while (bp < limit) { - if (*bp++ == '\n') { - bp--; - break; - } - } - } - ip->bufp = bp; - return bp; - } - while (bp < limit) { - if (output) - *op->bufp++ = *bp; - switch (*bp++) { - case '\n': - if (line_counter != NULL) - ++*line_counter; - if (output) - ++op->lineno; - break; - case '*': - if (*bp == '\\' && bp[1] == '\n') - newline_fix (bp); - if (*bp == '/') { - if (output) - *op->bufp++ = '/'; - ip->bufp = ++bp; - return bp; - } - break; - } - } - ip->bufp = bp; - return bp; -} - -/* - * Skip over a quoted string. BP points to the opening quote. - * Returns a pointer after the closing quote. Don't go past LIMIT. - * START_LINE is the line number of the starting point (but it need - * not be valid if the starting point is inside a macro expansion). - * - * The input stack state is not changed. - * - * If COUNT_NEWLINES is nonzero, it points to an int to increment - * for each newline passed. - * - * If BACKSLASH_NEWLINES_P is nonzero, store 1 thru it - * if we pass a backslash-newline. - * - * If EOFP is nonzero, set *EOFP to 1 if the string is unterminated. - */ -U_CHAR * -skip_quoted_string (bp, limit, start_line, count_newlines, backslash_newlines_p, eofp) - register U_CHAR *bp; - register U_CHAR *limit; - int start_line; - int *count_newlines; - int *backslash_newlines_p; - int *eofp; -{ - register U_CHAR c, match; - - match = *bp++; - while (1) { - if (bp >= limit) { - error_with_line (line_for_error (start_line), - "unterminated string or character constant"); - if (eofp) - *eofp = 1; - break; - } - c = *bp++; - if (c == '\\') { - while (*bp == '\\' && bp[1] == '\n') { - if (backslash_newlines_p) - *backslash_newlines_p = 1; - if (count_newlines) - ++*count_newlines; - bp += 2; - } - if (*bp == '\n' && count_newlines) { - if (backslash_newlines_p) - *backslash_newlines_p = 1; - ++*count_newlines; - } - bp++; - } else if (c == '\n') { - if (traditional) { - /* Unterminated strings and character constants are 'legal'. */ - bp--; /* Don't consume the newline. */ - if (eofp) - *eofp = 1; - break; - } - if (match == '\'') { - error_with_line (line_for_error (start_line), - "unterminated character constant"); - bp--; - if (eofp) - *eofp = 1; - break; - } - if (traditional) { /* Unterminated strings are 'legal'. */ - if (eofp) - *eofp = 1; - break; - } - /* If not traditional, then allow newlines inside strings. */ - if (count_newlines) - ++*count_newlines; - } else if (c == match) - break; - } - return bp; -} - -/* - * write out a #line command, for instance, after an #include file. - * If CONDITIONAL is nonzero, we can omit the #line if it would - * appear to be a no-op, and we can output a few newlines instead - * if we want to increase the line number by a small amount. - * FILE_CHANGE says whether we are entering a file, leaving, or neither. - */ - -void -output_line_command (ip, op, conditional, file_change) - FILE_BUF *ip, *op; - int conditional; - enum file_change_code file_change; -{ - int len; - char line_cmd_buf[500]; - - if (no_line_commands - || ip->fname == NULL - || no_output) { - op->lineno = ip->lineno; - return; - } - - if (conditional) { - if (ip->lineno == op->lineno) - return; - - /* If the inherited line number is a little too small, - output some newlines instead of a #line command. */ - if (ip->lineno > op->lineno && ip->lineno < op->lineno + 8) { - check_expand (op, 10); - while (ip->lineno > op->lineno) { - *op->bufp++ = '\n'; - op->lineno++; - } - return; - } - } - -#ifdef OUTPUT_LINE_COMMANDS - sprintf (line_cmd_buf, "#line %d \"%s\"", ip->lineno, ip->fname); -#else - sprintf (line_cmd_buf, "# %d \"%s\"", ip->lineno, ip->fname); -#endif - if (file_change != same_file) - strcat (line_cmd_buf, file_change == enter_file ? " 1" : " 2"); - len = strlen (line_cmd_buf); - line_cmd_buf[len++] = '\n'; - check_expand (op, len + 1); - if (op->bufp > op->buf && op->bufp[-1] != '\n') - *op->bufp++ = '\n'; - bcopy (line_cmd_buf, op->bufp, len); - op->bufp += len; - op->lineno = ip->lineno; -} - -/* This structure represents one parsed argument in a macro call. - `raw' points to the argument text as written (`raw_length' is its length). - `expanded' points to the argument's macro-expansion - (its length is `expand_length'). - `stringified_length' is the length the argument would have - if stringified. - `free1' and `free2', if nonzero, point to blocks to be freed - when the macro argument data is no longer needed. */ - -struct argdata { - U_CHAR *raw, *expanded; - int raw_length, expand_length; - int stringified_length; - U_CHAR *free1, *free2; - char newlines; - char comments; -}; - -/* Expand a macro call. - HP points to the symbol that is the macro being called. - Put the result of expansion onto the input stack - so that subsequent input by our caller will use it. - - If macro wants arguments, caller has already verified that - an argument list follows; arguments come from the input stack. */ - -void -macroexpand (hp, op) - HASHNODE *hp; - FILE_BUF *op; -{ - int nargs; - DEFINITION *defn = hp->value.defn; - register U_CHAR *xbuf; - int xbuf_len; - int start_line = instack[indepth].lineno; - - CHECK_DEPTH (return;); - - /* it might not actually be a macro. */ - if (hp->type != T_MACRO) { - special_symbol (hp, op); - return; - } - - nargs = defn->nargs; - - if (nargs >= 0) { - register int i; - struct argdata *args; - char *parse_error = 0; - - args = (struct argdata *) alloca ((nargs + 1) * sizeof (struct argdata)); - - for (i = 0; i < nargs; i++) { - args[i].raw = args[i].expanded = (U_CHAR *) ""; - args[i].raw_length = args[i].expand_length - = args[i].stringified_length = 0; - args[i].free1 = args[i].free2 = 0; - } - - /* Parse all the macro args that are supplied. I counts them. - The first NARGS args are stored in ARGS. - The rest are discarded. */ - i = 0; - do { - /* Discard the open-parenthesis or comma before the next arg. */ - ++instack[indepth].bufp; - parse_error - = macarg ((i < nargs || (nargs == 0 && i == 0)) ? &args[i] : 0); - if (parse_error) - { - error_with_line (line_for_error (start_line), parse_error); - break; - } - i++; - } while (*instack[indepth].bufp != ')'); - - /* If we got one arg but it was just whitespace, call that 0 args. */ - if (i == 1) { - register U_CHAR *bp = args[0].raw; - register U_CHAR *lim = bp + args[0].raw_length; - while (bp != lim && is_space[*bp]) bp++; - if (bp == lim) - i = 0; - } - - if (nargs == 0 && i > 0) - error ("arguments given to macro `%s'", hp->name); - else if (i < nargs) { - /* traditional C allows foo() if foo wants one argument. */ - if (nargs == 1 && i == 0 && traditional) - ; - else if (i == 0) - error ("no args to macro `%s'", hp->name); - else if (i == 1) - error ("only 1 arg to macro `%s'", hp->name); - else - error ("only %d args to macro `%s'", i, hp->name); - } else if (i > nargs) - error ("too many (%d) args to macro `%s'", i, hp->name); - - /* Swallow the closeparen. */ - ++instack[indepth].bufp; - - /* If macro wants zero args, we parsed the arglist for checking only. - Read directly from the macro definition. */ - if (nargs == 0) { - xbuf = defn->expansion; - xbuf_len = defn->length; - } else { - register U_CHAR *exp = defn->expansion; - register int offset; /* offset in expansion, - copied a piece at a time */ - register int totlen; /* total amount of exp buffer filled so far */ - - register struct reflist *ap; - - /* Macro really takes args. Compute the expansion of this call. */ - - /* Compute length in characters of the macro's expansion. */ - xbuf_len = defn->length; - for (ap = defn->pattern; ap != NULL; ap = ap->next) { - if (ap->stringify) - xbuf_len += args[ap->argno].stringified_length; - else if (ap->raw_before || ap->raw_after || traditional) - xbuf_len += args[ap->argno].raw_length; - else - xbuf_len += args[ap->argno].expand_length; - } - - xbuf = (U_CHAR *) xmalloc (xbuf_len + 1); - - /* Generate in XBUF the complete expansion - with arguments substituted in. - TOTLEN is the total size generated so far. - OFFSET is the index in the definition - of where we are copying from. */ - offset = totlen = 0; - for (ap = defn->pattern; ap != NULL; ap = ap->next) { - register struct argdata *arg = &args[ap->argno]; - - for (i = 0; i < ap->nchars; i++) - xbuf[totlen++] = exp[offset++]; - - if (ap->stringify != 0) { - int arglen = arg->raw_length; - int escaped = 0; - int in_string = 0; - int c; - i = 0; - while (i < arglen - && (c = arg->raw[i], is_space[c])) - i++; - while (i < arglen - && (c = arg->raw[arglen - 1], is_space[c])) - arglen--; - if (!traditional) - xbuf[totlen++] = '\"'; /* insert beginning quote */ - for (; i < arglen; i++) { - c = arg->raw[i]; - - /* Special markers Newline Space - generate nothing for a stringified argument. */ - if (c == '\n' && arg->raw[i+1] != '\n') { - i++; - continue; - } - - /* Internal sequences of whitespace are replaced by one space. */ - if (c == '\n' ? arg->raw[i+1] == '\n' : is_space[c]) { - while (1) { - /* Note that Newline Space does occur within whitespace - sequences; consider it part of the sequence. */ - if (c == '\n' && is_space[arg->raw[i+1]]) - i += 2; - else if (c != '\n' && is_space[c]) - i++; - else break; - c = arg->raw[i]; - } - i--; - c = ' '; - } - - if (escaped) - escaped = 0; - else { - if (c == '\\') - escaped = 1; - if (in_string) { - if (c == in_string) - in_string = 0; - } else if (c == '\"' || c == '\'') - in_string = c; - } - - /* Escape these chars */ - if (c == '\"' || (in_string && c == '\\')) - xbuf[totlen++] = '\\'; - if (isprint (c)) - xbuf[totlen++] = c; - else { - sprintf ((char *) &xbuf[totlen], "\\%03o", (unsigned int) c); - totlen += 4; - } - } - if (!traditional) - xbuf[totlen++] = '\"'; /* insert ending quote */ - } else if (ap->raw_before || ap->raw_after || traditional) { - U_CHAR *p1 = arg->raw; - U_CHAR *l1 = p1 + arg->raw_length; - - if (ap->raw_before) { - while (p1 != l1 && is_space[*p1]) p1++; - while (p1 != l1 && is_idchar[*p1]) - xbuf[totlen++] = *p1++; - /* Delete any no-reexpansion marker that follows - an identifier at the beginning of the argument - if the argument is concatenated with what precedes it. */ - if (p1[0] == '\n' && p1[1] == '-') - p1 += 2; - } - if (ap->raw_after) { - /* Arg is concatenated after: delete trailing whitespace, - whitespace markers, and no-reexpansion markers. */ - while (p1 != l1) { - if (is_space[l1[-1]]) l1--; - else if (l1[-1] == '-') { - U_CHAR *p2 = l1 - 1; - /* If a `-' is preceded by an odd number of newlines then it - and the last newline are a no-reexpansion marker. */ - while (p2 != p1 && p2[-1] == '\n') p2--; - if ((l1 - 1 - p2) & 1) { - l1 -= 2; - } - else break; - } - else break; - } - } - bcopy (p1, xbuf + totlen, l1 - p1); - totlen += l1 - p1; - } else { - bcopy (arg->expanded, xbuf + totlen, arg->expand_length); - totlen += arg->expand_length; - } - - if (totlen > xbuf_len) - abort (); - } - - /* if there is anything left of the definition - after handling the arg list, copy that in too. */ - - for (i = offset; i < defn->length; i++) - xbuf[totlen++] = exp[i]; - - xbuf[totlen] = 0; - xbuf_len = totlen; - - for (i = 0; i < nargs; i++) { - if (args[i].free1 != 0) - free (args[i].free1); - if (args[i].free2 != 0) - free (args[i].free2); - } - } - } else { - xbuf = defn->expansion; - xbuf_len = defn->length; - } - - /* Now put the expansion on the input stack - so our caller will commence reading from it. */ - { - register FILE_BUF *ip2; - - ip2 = &instack[++indepth]; - - ip2->fname = 0; - ip2->lineno = 0; - ip2->buf = xbuf; - ip2->length = xbuf_len; - ip2->bufp = xbuf; - ip2->free_ptr = (nargs > 0) ? xbuf : 0; - ip2->macro = hp; - ip2->if_stack = if_stack; - - /* Recursive macro use sometimes works traditionally. - #define foo(x,y) bar(x(y,0), y) - foo(foo, baz) */ - - if (!traditional) - hp->type = T_DISABLED; - } -} - -/* - * Parse a macro argument and store the info on it into *ARGPTR. - * Return nonzero to indicate a syntax error. - */ - -char * -macarg (argptr) - register struct argdata *argptr; -{ - FILE_BUF *ip = &instack[indepth]; - int paren = 0; - int newlines = 0; - int comments = 0; - - /* Try to parse as much of the argument as exists at this - input stack level. */ - U_CHAR *bp = macarg1 (ip->bufp, ip->buf + ip->length, - &paren, &newlines, &comments); - - /* If we find the end of the argument at this level, - set up *ARGPTR to point at it in the input stack. */ - if (!(ip->fname != 0 && (newlines != 0 || comments != 0)) - && bp != ip->buf + ip->length) { - if (argptr != 0) { - argptr->raw = ip->bufp; - argptr->raw_length = bp - ip->bufp; - } - ip->bufp = bp; - } else { - /* This input stack level ends before the macro argument does. - We must pop levels and keep parsing. - Therefore, we must allocate a temporary buffer and copy - the macro argument into it. */ - int bufsize = bp - ip->bufp; - int extra = newlines; - U_CHAR *buffer = (U_CHAR *) xmalloc (bufsize + extra + 1); - int final_start = 0; - - bcopy (ip->bufp, buffer, bufsize); - ip->bufp = bp; - ip->lineno += newlines; - - while (bp == ip->buf + ip->length) { - if (instack[indepth].macro == 0) { - free (buffer); - return "unterminated macro call"; - } - ip->macro->type = T_MACRO; - if (ip->free_ptr) - free (ip->free_ptr); - ip = &instack[--indepth]; - newlines = 0; - comments = 0; - bp = macarg1 (ip->bufp, ip->buf + ip->length, &paren, - &newlines, &comments); - final_start = bufsize; - bufsize += bp - ip->bufp; - extra += newlines; - buffer = (U_CHAR *) xrealloc (buffer, bufsize + extra + 1); - bcopy (ip->bufp, buffer + bufsize - (bp - ip->bufp), bp - ip->bufp); - ip->bufp = bp; - ip->lineno += newlines; - } - - /* Now, if arg is actually wanted, record its raw form, - discarding comments and duplicating newlines in whatever - part of it did not come from a macro expansion. - EXTRA space has been preallocated for duplicating the newlines. - FINAL_START is the index of the start of that part. */ - if (argptr != 0) { - argptr->raw = buffer; - argptr->raw_length = bufsize; - argptr->free1 = buffer; - argptr->newlines = newlines; - argptr->comments = comments; - if ((newlines || comments) && ip->fname != 0) - argptr->raw_length - = final_start + - discard_comments (argptr->raw + final_start, - argptr->raw_length - final_start, - newlines); - argptr->raw[argptr->raw_length] = 0; - if (argptr->raw_length > bufsize + extra) - abort (); - } - } - - /* If we are not discarding this argument, - macroexpand it and compute its length as stringified. - All this info goes into *ARGPTR. */ - - if (argptr != 0) { - FILE_BUF obuf; - register U_CHAR *buf, *lim; - register int totlen; - - obuf = expand_to_temp_buffer (argptr->raw, - argptr->raw + argptr->raw_length, - 1); - - argptr->expanded = obuf.buf; - argptr->expand_length = obuf.length; - argptr->free2 = obuf.buf; - - buf = argptr->raw; - lim = buf + argptr->raw_length; - - /* If ANSI, discard leading and trailing space. */ - if (!traditional) { - while (buf != lim && is_space[*buf]) - buf++; - while (buf != lim && is_space[lim[-1]]) - lim--; - } - totlen = traditional ? 0 : 2; /* Count opening and closing quote. */ - while (buf != lim) { - register U_CHAR c = *buf++; - totlen++; - /* If ANSI, replace internal sequences of whitespace with one space. */ - if (is_space[c] && !traditional) - SKIP_ALL_WHITE_SPACE (buf); - else if (c == '\"' || c == '\\') /* escape these chars */ - totlen++; - else if (!isprint (c)) - totlen += 3; - } - argptr->stringified_length = totlen; - } - return 0; -} - -/* Scan text from START (inclusive) up to LIMIT (exclusive), - counting parens in *DEPTHPTR, - and return if reach LIMIT - or before a `)' that would make *DEPTHPTR negative - or before a comma when *DEPTHPTR is zero. - Single and double quotes are matched and termination - is inhibited within them. Comments also inhibit it. - Value returned is pointer to stopping place. - - Increment *NEWLINES each time a newline is passed. - Set *COMMENTS to 1 if a comment is seen. */ - -U_CHAR * -macarg1 (start, limit, depthptr, newlines, comments) - U_CHAR *start; - register U_CHAR *limit; - int *depthptr, *newlines, *comments; -{ - register U_CHAR *bp = start; - - while (bp < limit) { - switch (*bp) { - case '(': - (*depthptr)++; - break; - case ')': - if (--(*depthptr) < 0) - return bp; - break; - case '\\': - /* Traditionally, backslash makes following char not special. */ - if (!traditional) - break; - if (bp + 1 < limit) - { - bp++; - /* But count source lines anyway. */ - if (*bp == '\n') - ++*newlines; - } - break; - case '\n': - ++*newlines; - break; - case '/': - if (bp[1] == '\\' && bp[2] == '\n') - newline_fix (bp + 1); - if (cplusplus && bp[1] == '/') { - *comments = 1; - bp += 2; - while (bp < limit && *bp++ != '\n') ; - ++*newlines; - break; - } - if (bp[1] != '*' || bp + 1 >= limit) - break; - *comments = 1; - bp += 2; - while (bp + 1 < limit) { - if (bp[0] == '*' - && bp[1] == '\\' && bp[2] == '\n') - newline_fix (bp + 1); - if (bp[0] == '*' && bp[1] == '/') - break; - if (*bp == '\n') ++*newlines; - bp++; - } - bp += 1; - break; - case '\'': - case '\"': - { - int quotec; - for (quotec = *bp++; bp + 1 < limit && *bp != quotec; bp++) { - if (*bp == '\\') { - bp++; - if (*bp == '\n') - ++*newlines; - while (*bp == '\\' && bp[1] == '\n') { - bp += 2; - } - } else if (*bp == '\n') { - ++*newlines; - if (quotec == '\'') - break; - } - } - } - break; - case ',': - if ((*depthptr) == 0) - return bp; - break; - } - bp++; - } - - return bp; -} - -/* Discard comments and duplicate newlines - in the string of length LENGTH at START, - except inside of string constants. - The string is copied into itself with its beginning staying fixed. - - NEWLINES is the number of newlines that must be duplicated. - We assume that that much extra space is available past the end - of the string. */ - -int -discard_comments (start, length, newlines) - U_CHAR *start; - int length; - int newlines; -{ - register U_CHAR *ibp; - register U_CHAR *obp; - register U_CHAR *limit; - register int c; - - /* If we have newlines to duplicate, copy everything - that many characters up. Then, in the second part, - we will have room to insert the newlines - while copying down. - NEWLINES may actually be too large, because it counts - newlines in string constants, and we don't duplicate those. - But that does no harm. */ - if (newlines > 0) { - ibp = start + length; - obp = ibp + newlines; - limit = start; - while (limit != ibp) - *--obp = *--ibp; - } - - ibp = start + newlines; - limit = start + length + newlines; - obp = start; - - while (ibp < limit) { - *obp++ = c = *ibp++; - switch (c) { - case '\n': - /* Duplicate the newline. */ - *obp++ = '\n'; - break; - - case '\\': - if (*ibp == '\n') { - obp--; - ibp++; - } - break; - - case '/': - if (*ibp == '\\' && ibp[1] == '\n') - newline_fix (ibp); - /* Delete any comment. */ - if (cplusplus && ibp[0] == '/') { - obp--; - ibp++; - while (ibp < limit && *ibp++ != '\n') ; - break; - } - if (ibp[0] != '*' || ibp + 1 >= limit) - break; - obp--; - ibp++; - while (ibp + 1 < limit) { - if (ibp[0] == '*' - && ibp[1] == '\\' && ibp[2] == '\n') - newline_fix (ibp + 1); - if (ibp[0] == '*' && ibp[1] == '/') - break; - ibp++; - } - ibp += 2; - break; - - case '\'': - case '\"': - /* Notice and skip strings, so that we don't - think that comments start inside them, - and so we don't duplicate newlines in them. */ - { - int quotec = c; - while (ibp < limit) { - *obp++ = c = *ibp++; - if (c == quotec) - break; - if (c == '\n' && quotec == '\'') - break; - if (c == '\\' && ibp < limit) { - while (*ibp == '\\' && ibp[1] == '\n') - ibp += 2; - *obp++ = *ibp++; - } - } - } - break; - } - } - - return obp - start; -} - -/* - * error - print error message and increment count of errors. - */ -error (msg, arg1, arg2, arg3) - char *msg; -{ - int i; - FILE_BUF *ip = NULL; - - for (i = indepth; i >= 0; i--) - if (instack[i].fname != NULL) { - ip = &instack[i]; - break; - } - - if (ip != NULL) - fprintf (stderr, "%s:%d: ", ip->fname, ip->lineno); - fprintf (stderr, msg, arg1, arg2, arg3); - fprintf (stderr, "\n"); - errors++; - return 0; -} - -/* Error including a message from `errno'. */ - -error_from_errno (name) - char *name; -{ - int i; - FILE_BUF *ip = NULL; - extern int errno, sys_nerr; - extern char *sys_errlist[]; - - for (i = indepth; i >= 0; i--) - if (instack[i].fname != NULL) { - ip = &instack[i]; - break; - } - - if (ip != NULL) - fprintf (stderr, "%s:%d: ", ip->fname, ip->lineno); - - if (errno < sys_nerr) - fprintf (stderr, "%s: %s\n", name, sys_errlist[errno]); - else - fprintf (stderr, "%s: undocumented I/O error\n", name); - - errors++; - return 0; -} - -/* Print error message but don't count it. */ - -warning (msg, arg1, arg2, arg3) - char *msg; -{ - int i; - FILE_BUF *ip = NULL; - - if (inhibit_warnings) - return 0; - - for (i = indepth; i >= 0; i--) - if (instack[i].fname != NULL) { - ip = &instack[i]; - break; - } - - if (ip != NULL) - fprintf (stderr, "%s:%d: ", ip->fname, ip->lineno); - fprintf (stderr, "warning: "); - fprintf (stderr, msg, arg1, arg2, arg3); - fprintf (stderr, "\n"); - return 0; -} - -error_with_line (line, msg, arg1, arg2, arg3) - int line; - char *msg; -{ - int i; - FILE_BUF *ip = NULL; - - for (i = indepth; i >= 0; i--) - if (instack[i].fname != NULL) { - ip = &instack[i]; - break; - } - - if (ip != NULL) - fprintf (stderr, "%s:%d: ", ip->fname, line); - fprintf (stderr, msg, arg1, arg2, arg3); - fprintf (stderr, "\n"); - errors++; - return 0; -} - -/* Return the line at which an error occurred. - The error is not necessarily associated with the current spot - in the input stack, so LINE says where. LINE will have been - copied from ip->lineno for the current input level. - If the current level is for a file, we return LINE. - But if the current level is not for a file, LINE is meaningless. - In that case, we return the lineno of the innermost file. */ -int -line_for_error (line) - int line; -{ - int i; - int line1 = line; - - for (i = indepth; i >= 0; ) { - if (instack[i].fname != 0) - return line1; - i--; - if (i < 0) - return 0; - line1 = instack[i].lineno; - } -} - -/* - * If OBUF doesn't have NEEDED bytes after OPTR, make it bigger. - * - * As things stand, nothing is ever placed in the output buffer to be - * removed again except when it's KNOWN to be part of an identifier, - * so flushing and moving down everything left, instead of expanding, - * should work ok. - */ - -int -grow_outbuf (obuf, needed) - register FILE_BUF *obuf; - register int needed; -{ - register U_CHAR *p; - int minsize; - - if (obuf->length - (obuf->bufp - obuf->buf) > needed) - return; - - /* Make it at least twice as big as it is now. */ - obuf->length *= 2; - /* Make it have at least 150% of the free space we will need. */ - minsize = (3 * needed) / 2 + (obuf->bufp - obuf->buf); - if (minsize > obuf->length) - obuf->length = minsize; - - if ((p = (U_CHAR *) xrealloc (obuf->buf, obuf->length)) == NULL) - memory_full (); - - obuf->bufp = p + (obuf->bufp - obuf->buf); - obuf->buf = p; -} - -/* Symbol table for macro names and special symbols */ - -/* - * install a name in the main hash table, even if it is already there. - * name stops with first non alphanumeric, except leading '#'. - * caller must check against redefinition if that is desired. - * delete_macro () removes things installed by install () in fifo order. - * this is important because of the `defined' special symbol used - * in #if, and also if pushdef/popdef directives are ever implemented. - * - * If LEN is >= 0, it is the length of the name. - * Otherwise, compute the length by scanning the entire name. - * - * If HASH is >= 0, it is the precomputed hash code. - * Otherwise, compute the hash code. - */ -HASHNODE * -install (name, len, type, value, hash) - U_CHAR *name; - int len; - enum node_type type; - int value; - int hash; - /* watch out here if sizeof (U_CHAR *) != sizeof (int) */ -{ - register HASHNODE *hp; - register int i, bucket; - register U_CHAR *p, *q; - - if (len < 0) { - p = name; - while (is_idchar[*p]) - p++; - len = p - name; - } - - if (hash < 0) - hash = hashf (name, len, HASHSIZE); - - i = sizeof (HASHNODE) + len + 1; - hp = (HASHNODE *) xmalloc (i); - bucket = hash; - hp->bucket_hdr = &hashtab[bucket]; - hp->next = hashtab[bucket]; - hashtab[bucket] = hp; - hp->prev = NULL; - if (hp->next != NULL) - hp->next->prev = hp; - hp->type = type; - hp->length = len; - hp->value.ival = value; - hp->name = ((U_CHAR *) hp) + sizeof (HASHNODE); - p = hp->name; - q = name; - for (i = 0; i < len; i++) - *p++ = *q++; - hp->name[len] = 0; - return hp; -} - -/* - * find the most recent hash node for name name (ending with first - * non-identifier char) installed by install - * - * If LEN is >= 0, it is the length of the name. - * Otherwise, compute the length by scanning the entire name. - * - * If HASH is >= 0, it is the precomputed hash code. - * Otherwise, compute the hash code. - */ -HASHNODE * -lookup (name, len, hash) - U_CHAR *name; - int len; - int hash; -{ - register U_CHAR *bp; - register HASHNODE *bucket; - - if (len < 0) { - for (bp = name; is_idchar[*bp]; bp++) ; - len = bp - name; - } - - if (hash < 0) - hash = hashf (name, len, HASHSIZE); - - bucket = hashtab[hash]; - while (bucket) { - if (bucket->length == len && strncmp (bucket->name, name, len) == 0) - return bucket; - bucket = bucket->next; - } - return NULL; -} - -/* - * Delete a hash node. Some weirdness to free junk from macros. - * More such weirdness will have to be added if you define more hash - * types that need it. - */ - -/* Note that the DEFINITION of a macro is removed from the hash table - but its storage is not freed. This would be a storage leak - except that it is not reasonable to keep undefining and redefining - large numbers of macros many times. - In any case, this is necessary, because a macro can be #undef'd - in the middle of reading the arguments to a call to it. - If #undef freed the DEFINITION, that would crash. */ - -delete_macro (hp) - HASHNODE *hp; -{ - - if (hp->prev != NULL) - hp->prev->next = hp->next; - if (hp->next != NULL) - hp->next->prev = hp->prev; - - /* make sure that the bucket chain header that - the deleted guy was on points to the right thing afterwards. */ - if (hp == *hp->bucket_hdr) - *hp->bucket_hdr = hp->next; - -#if 0 - if (hp->type == T_MACRO) { - DEFINITION *d = hp->value.defn; - struct reflist *ap, *nextap; - - for (ap = d->pattern; ap != NULL; ap = nextap) { - nextap = ap->next; - free (ap); - } - free (d); - } -#endif - free (hp); -} - -/* - * return hash function on name. must be compatible with the one - * computed a step at a time, elsewhere - */ -int -hashf (name, len, hashsize) - register U_CHAR *name; - register int len; - int hashsize; -{ - register int r = 0; - - while (len--) - r = HASHSTEP (r, *name++); - - return MAKE_POS (r) % hashsize; -} - -/* Dump all macro definitions as #defines to stdout. */ - -void -dump_all_macros () -{ - int bucket; - - for (bucket = 0; bucket < HASHSIZE; bucket++) { - register HASHNODE *hp; - - for (hp = hashtab[bucket]; hp; hp= hp->next) { - if (hp->type == T_MACRO) { - register DEFINITION *defn = hp->value.defn; - struct reflist *ap; - int offset; - int concat; - - - /* Print the definition of the macro HP. */ - - printf ("#define %s", hp->name); - if (defn->nargs >= 0) { - int i; - - printf ("("); - for (i = 0; i < defn->nargs; i++) { - dump_arg_n (defn, i); - if (i + 1 < defn->nargs) - printf (", "); - } - printf (")"); - } - - printf (" "); - - offset = 0; - concat = 0; - for (ap = defn->pattern; ap != NULL; ap = ap->next) { - dump_defn_1 (defn->expansion, offset, ap->nchars); - if (ap->nchars != 0) - concat = 0; - offset += ap->nchars; - if (ap->stringify) - printf (" #"); - if (ap->raw_before && !concat) - printf (" ## "); - concat = 0; - dump_arg_n (defn, ap->argno); - if (ap->raw_after) { - printf (" ## "); - concat = 1; - } - } - dump_defn_1 (defn->expansion, offset, defn->length - offset); - printf ("\n"); - } - } - } -} - -/* Output to stdout a substring of a macro definition. - BASE is the beginning of the definition. - Output characters START thru LENGTH. - Discard newlines outside of strings, thus - converting funny-space markers to ordinary spaces. */ - -dump_defn_1 (base, start, length) - U_CHAR *base; - int start; - int length; -{ - U_CHAR *p = base + start; - U_CHAR *limit = base + start + length; - - while (p < limit) { - if (*p != '\n') - putchar (*p); - else if (*p == '\"' || *p =='\'') { - U_CHAR *p1 = skip_quoted_string (p, limit, 0, 0, 0, 0); - fwrite (p, p1 - p, 1, stdout); - p = p1 - 1; - } - p++; - } -} - -/* Print the name of argument number ARGNUM of macro definition DEFN. - Recall that DEFN->argnames contains all the arg names - concatenated in reverse order with comma-space in between. */ - -dump_arg_n (defn, argnum) - DEFINITION *defn; - int argnum; -{ - register U_CHAR *p = defn->argnames; - while (argnum + 1 < defn->nargs) { - p = (U_CHAR *) index (p, ' ') + 1; - argnum++; - } - - while (*p && *p != ',') { - putchar (*p); - p++; - } -} - -/* Initialize syntactic classifications of characters. */ - -initialize_char_syntax () -{ - register int i; - - /* - * Set up is_idchar and is_idstart tables. These should be - * faster than saying (is_alpha (c) || c == '_'), etc. - * Must do set up these things before calling any routines tthat - * refer to them. - */ - for (i = 'a'; i <= 'z'; i++) { - is_idchar[i - 'a' + 'A'] = 1; - is_idchar[i] = 1; - is_idstart[i - 'a' + 'A'] = 1; - is_idstart[i] = 1; - } - for (i = '0'; i <= '9'; i++) - is_idchar[i] = 1; - is_idchar['_'] = 1; - is_idstart['_'] = 1; - is_idchar['$'] = dollars_in_ident; - is_idstart['$'] = dollars_in_ident; - - /* horizontal space table */ - is_hor_space[' '] = 1; - is_hor_space['\t'] = 1; - is_hor_space['\v'] = 1; - is_hor_space['\f'] = 1; - is_hor_space['\r'] = 1; - - is_space[' '] = 1; - is_space['\t'] = 1; - is_space['\v'] = 1; - is_space['\f'] = 1; - is_space['\n'] = 1; - is_space['\r'] = 1; -} - -/* Initialize the built-in macros. */ - -initialize_builtins () -{ - install ("__LINE__", -1, T_SPECLINE, 0, -1); - install ("__DATE__", -1, T_DATE, 0, -1); - install ("__FILE__", -1, T_FILE, 0, -1); - install ("__BASE_FILE__", -1, T_BASE_FILE, 0, -1); - install ("__INCLUDE_LEVEL__", -1, T_INCLUDE_LEVEL, 0, -1); - install ("__VERSION__", -1, T_VERSION, 0, -1); - install ("__TIME__", -1, T_TIME, 0, -1); - if (!traditional) - install ("__STDC__", -1, T_CONST, STDC_VALUE, -1); -/* install ("__GNU__", -1, T_CONST, 1, -1); */ -/* This is supplied using a -D by the compiler driver - so that it is present only when truly compiling with GNU C. */ -} - -/* - * process a given definition string, for initialization - * If STR is just an identifier, define it with value 1. - * If STR has anything after the identifier, then it should - * be identifier-space-definition. - */ -make_definition (str) - U_CHAR *str; -{ - FILE_BUF *ip; - struct directive *kt; - U_CHAR *buf, *p; - - buf = str; - p = str; - while (is_idchar[*p]) p++; - if (p == str) { - error ("malformed option `-D %s'", str); - return; - } - if (*p == 0) { - buf = (U_CHAR *) alloca (p - buf + 4); - strcpy ((char *)buf, str); - strcat ((char *)buf, " 1"); - } else if (*p != ' ') { - error ("malformed option `-D %s'", str); - return; - } else { - U_CHAR *q; - /* Copy the entire option so we can modify it. */ - buf = (U_CHAR *) alloca (2 * strlen (str) + 1); - strncpy (buf, str, p - str); - /* Change the = to a space. */ - buf[p - str] = ' '; - /* Scan for any backslash-newline and remove it. */ - p++; - q = &buf[p - str]; - while (*p) { - if (*p == '\\' && p[1] == '\n') - p += 2; - /* Change newline chars into newline-markers. */ - else if (*p == '\n') - { - *q++ = '\n'; - *q++ = '\n'; - p++; - } - else - *q++ = *p++; - } - *q = 0; - } - - ip = &instack[++indepth]; - ip->fname = "*Initialization*"; - - ip->buf = ip->bufp = buf; - ip->length = strlen (buf); - ip->lineno = 1; - ip->macro = 0; - ip->free_ptr = 0; - ip->if_stack = if_stack; - - for (kt = directive_table; kt->type != T_DEFINE; kt++) - ; - - /* pass NULL as output ptr to do_define since we KNOW it never - does any output.... */ - do_define (buf, buf + strlen (buf) , NULL, kt); - --indepth; -} - -/* JF, this does the work for the -U option */ -make_undef (str) - U_CHAR *str; -{ - FILE_BUF *ip; - struct directive *kt; - - ip = &instack[++indepth]; - ip->fname = "*undef*"; - - ip->buf = ip->bufp = str; - ip->length = strlen (str); - ip->lineno = 1; - ip->macro = 0; - ip->free_ptr = 0; - ip->if_stack = if_stack; - - for (kt = directive_table; kt->type != T_UNDEF; kt++) - ; - - do_undef (str,str + strlen (str) - 1, NULL, kt); - --indepth; -} - -/* Add output to `deps_buffer' for the -M switch. - STRING points to the text to be output. - SIZE is the number of bytes, or 0 meaning output until a null. - If SIZE is nonzero, we break the line first, if it is long enough. */ - -deps_output (string, size) - char *string; - int size; -{ -#ifndef MAX_OUTPUT_COLUMNS -#define MAX_OUTPUT_COLUMNS 75 -#endif - if (size != 0 && deps_column != 0 - && size + deps_column > MAX_OUTPUT_COLUMNS) { - deps_output ("\\\n ", 0); - deps_column = 0; - } - - if (size == 0) - size = strlen (string); - - if (deps_size + size + 1 > deps_allocated_size) { - deps_allocated_size = deps_size + size + 50; - deps_allocated_size *= 2; - deps_buffer = (char *) xrealloc (deps_buffer, deps_allocated_size); - } - bcopy (string, &deps_buffer[deps_size], size); - deps_size += size; - deps_column += size; - deps_buffer[deps_size] = 0; -} - -#ifndef BSD -#ifndef BSTRING - -void -bzero (b, length) - register char *b; - register int length; -{ -#ifdef VMS - short zero = 0; - long max_str = 65535; - - while (length > max_str) { - (void) LIB$MOVC5 (&zero, &zero, &zero, &max_str, b); - length -= max_str; - b += max_str; - } - (void) LIB$MOVC5 (&zero, &zero, &zero, &length, b); -#else - while (length-- > 0) - *b++ = 0; -#endif /* not VMS */ -} - -void -bcopy (b1, b2, length) - register char *b1; - register char *b2; - register int length; -{ -#ifdef VMS - long max_str = 65535; - - while (length > max_str) { - (void) LIB$MOVC3 (&max_str, b1, b2); - length -= max_str; - b1 += max_str; - b2 += max_str; - } - (void) LIB$MOVC3 (&length, b1, b2); -#else - while (length-- > 0) - *b2++ = *b1++; -#endif /* not VMS */ -} - -int -bcmp (b1, b2, length) /* This could be a macro! */ - register char *b1; - register char *b2; - register int length; -{ -#ifdef VMS - struct dsc$descriptor_s src1 = {length, DSC$K_DTYPE_T, DSC$K_CLASS_S, b1}; - struct dsc$descriptor_s src2 = {length, DSC$K_DTYPE_T, DSC$K_CLASS_S, b2}; - - return STR$COMPARE (&src1, &src2); -#else - while (length-- > 0) - if (*b1++ != *b2++) - return 1; - - return 0; -#endif /* not VMS */ -} -#endif /* not BSTRING */ -#endif /* not BSD */ - - -void -fatal (str, arg) - char *str, *arg; -{ - fprintf (stderr, "%s: ", progname); - fprintf (stderr, str, arg); - fprintf (stderr, "\n"); - exit (FATAL_EXIT_CODE); -} - -/* More 'friendly' abort that prints the line and file. - config.h can #define abort fancy_abort if you like that sort of thing. */ - -void -fancy_abort () -{ - fatal ("Internal gcc abort."); -} - -void -perror_with_name (name) - char *name; -{ - extern int errno, sys_nerr; - extern char *sys_errlist[]; - - fprintf (stderr, "%s: ", progname); - if (errno < sys_nerr) - fprintf (stderr, "%s: %s\n", name, sys_errlist[errno]); - else - fprintf (stderr, "%s: undocumented I/O error\n", name); - errors++; -} - -void -pfatal_with_name (name) - char *name; -{ - perror_with_name (name); -#ifdef VMS - exit (vaxc$errno); -#else - exit (FATAL_EXIT_CODE); -#endif -} - - -void -memory_full () -{ - fatal ("Memory exhausted."); -} - - -char * -xmalloc (size) - int size; -{ - extern char *malloc (); - register char *ptr = malloc (size); - if (ptr != 0) return (ptr); - memory_full (); - /*NOTREACHED*/ -} - -char * -xrealloc (old, size) - char *old; - int size; -{ - extern char *realloc (); - register char *ptr = realloc (old, size); - if (ptr != 0) return (ptr); - memory_full (); - /*NOTREACHED*/ -} - -char * -xcalloc (number, size) - int number, size; -{ - extern char *malloc (); - register int total = number * size; - register char *ptr = malloc (total); - if (ptr != 0) { - if (total > 100) - bzero (ptr, total); - else { - /* It's not too long, so loop, zeroing by longs. - It must be safe because malloc values are always well aligned. */ - register long *zp = (long *) ptr; - register long *zl = (long *) (ptr + total - 4); - register int i = total - 4; - while (zp < zl) - *zp++ = 0; - if (i < 0) - i = 0; - while (i < total) - ptr[i++] = 0; - } - return ptr; - } - memory_full (); - /*NOTREACHED*/ -} - -char * -savestring (input) - char *input; -{ - int size = strlen (input); - char *output = xmalloc (size + 1); - strcpy (output, input); - return output; -} - -/* Get the file-mode and data size of the file open on FD - and store them in *MODE_POINTER and *SIZE_POINTER. */ - -int -file_size_and_mode (fd, mode_pointer, size_pointer) - int fd; - int *mode_pointer; - long int *size_pointer; -{ - struct stat sbuf; - - if (fstat (fd, &sbuf) < 0) return (-1); - if (mode_pointer) *mode_pointer = sbuf.st_mode; - if (size_pointer) *size_pointer = sbuf.st_size; - return 0; -} - -#ifdef VMS - -/* Under VMS we need to fix up the "include" specification - filename so that everything following the 1st slash is - changed into its correct VMS file specification. */ - -hack_vms_include_specification (fname) - char *fname; -{ - register char *cp, *cp1, *cp2; - char Local[512]; - extern char *index (), *rindex (); - - /* Ignore leading "./"s */ - while (fname[0] == '.' && fname[1] == '/') - strcpy (fname, fname+2); - /* Look for the boundary between the VMS and UNIX filespecs */ - cp = rindex (fname, ']'); /* Look for end of dirspec. */ - if (cp == 0) cp == rindex (fname, '>'); /* ... Ditto */ - if (cp == 0) cp == rindex (fname, ':'); /* Look for end of devspec. */ - if (cp) { - cp++; - } else { - cp = index (fname, '/'); /* Look for the "/" */ - } - /* See if we found that 1st slash */ - if (cp == 0) return; /* Nothing to do!!! */ - if (*cp != '/') return; /* Nothing to do!!! */ - /* Point to the UNIX filename part (which needs to be fixed!) */ - cp1 = cp+1; - /* If the directory spec is not rooted, we can just copy - the UNIX filename part and we are done */ - if (((cp - fname) > 2) - && ((cp[-1] == ']') || (cp[-1] == '>')) - && (cp[-2] != '.')) { - strcpy (cp, cp1); - return; - } - /* If there are no other slashes then the filename will be - in the "root" directory. Otherwise, we need to add - directory specifications. */ - if (index (cp1, '/') == 0) { - /* Just add "[000000]" as the directory string */ - strcpy (Local, "[000000]"); - cp2 = Local + strlen (Local); - } else { - /* Open the directory specification */ - cp2 = Local; - *cp2++ = '['; - /* As long as there are still subdirectories to add, do them. */ - while (index (cp1, '/') != 0) { - /* If this token is "." we can ignore it */ - if ((cp1[0] == '.') && (cp1[1] == '/')) { - cp1 += 2; - continue; - } - /* Add a subdirectory spec. */ - if (cp2 != Local+1) *cp2++ = '.'; - /* If this is ".." then the spec becomes "-" */ - if ((cp1[0] == '.') && (cp1[1] == '.') && (cp[2] == '/')) { - /* Add "-" and skip the ".." */ - *cp2++ = '-'; - cp1 += 3; - continue; - } - /* Copy the subdirectory */ - while (*cp1 != '/') *cp2++= *cp1++; - cp1++; /* Skip the "/" */ - } - /* Close the directory specification */ - *cp2++ = ']'; - } - /* Now add the filename */ - while (*cp1) *cp2++ = *cp1++; - *cp2 = 0; - /* Now append it to the original VMS spec. */ - strcpy (cp, Local); - return; -} -#endif /* VMS */ - -#ifdef VMS - -/* These are the read/write replacement routines for - VAX-11 "C". They make read/write behave enough - like their UNIX counterparts that CCCP will work */ - -int -read (fd, buf, size) - int fd; - char *buf; - int size; -{ -#undef read /* Get back the REAL read routine */ - register int i; - register int total = 0; - - /* Read until the buffer is exhausted */ - while (size > 0) { - /* Limit each read to 32KB */ - i = (size > (32*1024)) ? (32*1024) : size; - i = read (fd, buf, i); - if (i <= 0) { - if (i == 0) return (total); - return(i); - } - /* Account for this read */ - total += i; - buf += i; - size -= i; - } - return (total); -} - -int -write (fd, buf, size) - int fd; - char *buf; - int size; -{ -#undef write /* Get back the REAL write routine */ - int i; - int j; - - /* Limit individual writes to 32Kb */ - i = size; - while (i > 0) { - j = (i > (32*1024)) ? (32*1024) : i; - if (write (fd, buf, j) < 0) return (-1); - /* Account for the data written */ - buf += j; - i -= j; - } - return (size); -} - -#endif /* VMS */ diff --git a/gnu/usr.bin/gcc1/cpp/usr.bin.cpp.sh b/gnu/usr.bin/gcc1/cpp/usr.bin.cpp.sh deleted file mode 100644 index b95b36fde8..0000000000 --- a/gnu/usr.bin/gcc1/cpp/usr.bin.cpp.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 1990 The Regents of the University of California. -# All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# the Systems Programming Group of the University of Utah Computer -# Science Department. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)usr.bin.cpp.sh 6.5 (Berkeley) 4/1/91 -# -# Transitional front end to CCCP to make it behave like (Reiser) CCP: -# specifies -traditional -# doesn't search gcc-include -# -PATH=/usr/bin:/bin -CPP=/usr/libexec/cpp -ALST="-traditional -D__GNUC__ -$ " -NSI=no -OPTS="" -INCS="-nostdinc" -FOUNDFILES=no - -for A -do - case $A in - -nostdinc) - NSI=yes - ;; - -traditional) - ;; - -I*) - INCS="$INCS $A" - ;; - -U__GNUC__) - ALST=`echo $ALST | sed -e 's/-D__GNUC__//'` - ;; - -*) - OPTS="$OPTS '$A'" - ;; - *) - FOUNDFILES=yes - if [ $NSI = "no" ] - then - INCS="$INCS -I/usr/include" - NSI=skip - fi - eval $CPP $ALST $INCS $LIBS $CSU $OPTS $A || exit $? - ;; - esac -done - -if [ $FOUNDFILES = "no" ] -then - # read standard input - if [ $NSI = "no" ] - then - INCS="$INCS -I/usr/include" - fi - eval exec $CPP $ALST $INCS $LIBS $CSU $OPTS -fi - -exit 0 diff --git a/gnu/usr.bin/gcc1/gnulib/DIST/gnulib.c b/gnu/usr.bin/gcc1/gnulib/DIST/gnulib.c deleted file mode 100644 index f46d23267f..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/DIST/gnulib.c +++ /dev/null @@ -1,452 +0,0 @@ -/* Subroutines needed by GCC output code on some machines. */ -/* Compile this file with the Unix C compiler! */ - -#include "config.h" - -/* Define the C data type to use for an SImode value. */ - -#ifndef SItype -#define SItype long int -#endif - -/* Define the type to be used for returning an SF mode value - and the method for turning a float into that type. - These definitions work for machines where an SF value is - returned in the same register as an int. */ - -#ifndef SFVALUE -#define SFVALUE int -#endif - -#ifndef INTIFY -#define INTIFY(FLOATVAL) (intify.f = (FLOATVAL), intify.i) -#endif - -union flt_or_int { int i; float f; }; - - -#ifdef L_eprintf -#include -/* This is used by the `assert' macro. */ -void -__eprintf (string, expression, line, filename) - char *string; - char *expression; - int line; - char *filename; -{ - fprintf (stderr, string, expression, line, filename); - fflush (stderr); - abort (); -} -#endif - -#ifdef L_umulsi3 -SItype -__umulsi3 (a, b) - unsigned SItype a, b; -{ - return a * b; -} -#endif - -#ifdef L_mulsi3 -SItype -__mulsi3 (a, b) - SItype a, b; -{ - return a * b; -} -#endif - -#ifdef L_udivsi3 -SItype -__udivsi3 (a, b) - unsigned SItype a, b; -{ - return a / b; -} -#endif - -#ifdef L_divsi3 -SItype -__divsi3 (a, b) - SItype a, b; -{ - return a / b; -} -#endif - -#ifdef L_umodsi3 -SItype -__umodsi3 (a, b) - unsigned SItype a, b; -{ - return a % b; -} -#endif - -#ifdef L_modsi3 -SItype -__modsi3 (a, b) - SItype a, b; -{ - return a % b; -} -#endif - -#ifdef L_lshrsi3 -SItype -__lshrsi3 (a, b) - unsigned SItype a, b; -{ - return a >> b; -} -#endif - -#ifdef L_lshlsi3 -SItype -__lshlsi3 (a, b) - unsigned SItype a, b; -{ - return a << b; -} -#endif - -#ifdef L_ashrsi3 -SItype -__ashrsi3 (a, b) - SItype a, b; -{ - return a >> b; -} -#endif - -#ifdef L_ashlsi3 -SItype -__ashlsi3 (a, b) - SItype a, b; -{ - return a << b; -} -#endif - -#ifdef L_divdf3 -double -__divdf3 (a, b) - double a, b; -{ - return a / b; -} -#endif - -#ifdef L_muldf3 -double -__muldf3 (a, b) - double a, b; -{ - return a * b; -} -#endif - -#ifdef L_negdf2 -double -__negdf2 (a) - double a; -{ - return -a; -} -#endif - -#ifdef L_adddf3 -double -__adddf3 (a, b) - double a, b; -{ - return a + b; -} -#endif - -#ifdef L_subdf3 -double -__subdf3 (a, b) - double a, b; -{ - return a - b; -} -#endif - -#ifdef L_cmpdf2 -SItype -__cmpdf2 (a, b) - double a, b; -{ - if (a > b) - return 1; - else if (a < b) - return -1; - return 0; -} -#endif - -#ifdef L_fixunsdfsi -#define HIGH_BIT_INT_COEFF (1 << (BITS_PER_WORD - 1)) -#define HIGH_BIT_COEFF (2 * (double) (1 << (BITS_PER_WORD - 2))) - -SItype -__fixunsdfsi (a) - double a; -{ - if (a < HIGH_BIT_COEFF) - return (SItype)a; - /* Convert large positive numbers to smaller ones, - then increase again after you have a fixed point number. */ - else - return ((SItype) (a - HIGH_BIT_COEFF)) + HIGH_BIT_INT_COEFF; -} -#endif - -#ifdef L_fixdfsi -SItype -__fixdfsi (a) - double a; -{ - return (SItype) a; -} -#endif - -#ifdef L_floatsidf -double -__floatsidf (a) - SItype a; -{ - return (double) a; -} -#endif - -#ifdef L_addsf3 -SFVALUE -__addsf3 (a, b) - union flt_or_int a, b; -{ - union flt_or_int intify; - return INTIFY (a.f + b.f); -} -#endif - -#ifdef L_negsf2 -SFVALUE -__negsf2 (a) - union flt_or_int a; -{ - union flt_or_int intify; - return INTIFY (-a.f); -} -#endif - -#ifdef L_subsf3 -SFVALUE -__subsf3 (a, b) - union flt_or_int a, b; -{ - union flt_or_int intify; - return INTIFY (a.f - b.f); -} -#endif - -#ifdef L_cmpsf2 -SItype -__cmpsf2 (a, b) - union flt_or_int a, b; -{ - if (a.f > b.f) - return 1; - else if (a.f < b.f) - return -1; - return 0; -} -#endif - -#ifdef L_mulsf3 -SFVALUE -__mulsf3 (a, b) - union flt_or_int a, b; -{ - union flt_or_int intify; - return INTIFY (a.f * b.f); -} -#endif - -#ifdef L_divsf3 -SFVALUE -__divsf3 (a, b) - union flt_or_int a, b; -{ - union flt_or_int intify; - return INTIFY (a.f / b.f); -} -#endif - -#ifdef L_truncdfsf2 -SFVALUE -__truncdfsf2 (a) - double a; -{ - union flt_or_int intify; - return INTIFY (a); -} -#endif - -#ifdef L_extendsfdf2 -double -__extendsfdf2 (a) - union flt_or_int a; -{ - union flt_or_int intify; - return a.f; -} -#endif - -#ifdef L_bb -int __avoid_ranlib_warning; /* Don't let symbol table be empty. */ - -#if defined (sun) && defined (mc68000) -struct bb -{ - int initialized; - char *filename; - int *counts; - int ncounts; - int zero_word; - int *addresses; -}; - -__bb_init_func (blocks) - struct bb *blocks; -{ - extern int ___tcov_init; - - if (! ___tcov_init) - ___tcov_init_func (); - - ___bb_link (blocks->filename, blocks->counts, blocks->ncounts); -} - -#endif -#endif - -/* frills for C++ */ - -#ifdef L_builtin_new -typedef void (*vfp)(); - -extern vfp __new_handler; - -char * -__builtin_new (sz) - long sz; -{ - char *p; - - p = (char *)malloc (sz); - if (p == 0) - (*__new_handler) (); - return p; -} -#endif - -#ifdef L_builtin_New -typedef void (*vfp)(); - -static void -default_new_handler (); - -vfp __new_handler = default_new_handler; - -char * -__builtin_vec_new (p, maxindex, size, ctor) - char *p; - int maxindex, size; - void (*ctor)(); -{ - int i, nelts = maxindex + 1; - char *rval; - - if (p == 0) - p = (char *)__builtin_new (nelts * size); - - rval = p; - - for (i = 0; i < nelts; i++) - { - (*ctor) (p); - p += size; - } - - return rval; -} - -vfp -__set_new_handler (handler) - vfp handler; -{ - vfp prev_handler; - - prev_handler = __new_handler; - if (handler == 0) handler = default_new_handler; - __new_handler = handler; - return prev_handler; -} - -vfp -set_new_handler (handler) - vfp handler; -{ - return __set_new_handler (handler); -} - -static void -default_new_handler () -{ - /* don't use fprintf (stderr, ...) because it may need to call malloc. */ - write (2, "default_new_handler: out of memory... aaaiiiiiieeeeeeeeeeeeee!\n", 65); - /* don't call exit () because that may call global destructors which - may cause a loop. */ - _exit (-1); -} -#endif - -#ifdef L_builtin_del -typedef void (*vfp)(); - -void -__builtin_delete (ptr) - char *ptr; -{ - if (ptr) - free (ptr); -} - -void -__builtin_vec_delete (ptr, maxindex, size, dtor, auto_delete_vec, auto_delete) - char *ptr; - int maxindex, size; - void (*dtor)(); - int auto_delete; -{ - int i, nelts = maxindex + 1; - char *p = ptr; - - ptr += nelts * size; - - for (i = 0; i < nelts; i++) - { - ptr -= size; - (*dtor) (ptr, auto_delete); - } - - if (auto_delete_vec) - free (p); -} - -#endif diff --git a/gnu/usr.bin/gcc1/gnulib/DIST/gnulib2.c b/gnu/usr.bin/gcc1/gnulib/DIST/gnulib2.c deleted file mode 100644 index b31b8f6c3a..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/DIST/gnulib2.c +++ /dev/null @@ -1,922 +0,0 @@ -/* More subroutines needed by GCC output code on some machines. */ -/* Compile this one with gcc. */ - -#include "config.h" -#include - -#ifndef SItype -#define SItype long int -#endif - -/* long long ints are pairs of long ints in the order determined by - WORDS_BIG_ENDIAN. */ - -#ifdef WORDS_BIG_ENDIAN - struct longlong {long high, low;}; -#else - struct longlong {long low, high;}; -#endif - -/* We need this union to unpack/pack longlongs, since we don't have - any arithmetic yet. Incoming long long parameters are stored - into the `ll' field, and the unpacked result is read from the struct - longlong. */ - -typedef union -{ - struct longlong s; - long long ll; - SItype i[2]; - unsigned SItype ui[2]; -} long_long; - -/* Internally, long long ints are strings of unsigned shorts in the - order determined by BYTES_BIG_ENDIAN. */ - -#define B 0x10000 -#define low16 (B - 1) - -#ifdef BYTES_BIG_ENDIAN - -/* Note that HIGH and LOW do not describe the order - of words in a long long. They describe the order of words - in vectors ordered according to the byte order. */ - -#define HIGH 0 -#define LOW 1 - -#define big_end(n) 0 -#define little_end(n) ((n) - 1) -#define next_msd(i) ((i) - 1) -#define next_lsd(i) ((i) + 1) -#define is_not_msd(i,n) ((i) >= 0) -#define is_not_lsd(i,n) ((i) < (n)) - -#else - -#define LOW 0 -#define HIGH 1 - -#define big_end(n) ((n) - 1) -#define little_end(n) 0 -#define next_msd(i) ((i) + 1) -#define next_lsd(i) ((i) - 1) -#define is_not_msd(i,n) ((i) < (n)) -#define is_not_lsd(i,n) ((i) >= 0) - -#endif - -/* These algorithms are all straight out of Knuth, vol. 2, sec. 4.3.1. */ - -static int badd (); -static int bsub (); -static void bmul (); -static int bneg (); -static int bshift (); - -#ifdef L_adddi3 -long long -__adddi3 (u, v) - long long u, v; -{ - long a[2], b[2], c[2]; - long_long w; - long_long uu, vv; - - uu.ll = u; - vv.ll = v; - - a[HIGH] = uu.s.high; - a[LOW] = uu.s.low; - b[HIGH] = vv.s.high; - b[LOW] = vv.s.low; - - badd (a, b, c, sizeof c); - - w.s.high = c[HIGH]; - w.s.low = c[LOW]; - return w.ll; -} - -static int -badd (a, b, c, n) - unsigned short *a, *b, *c; - size_t n; -{ - unsigned long acc; - int i; - - n /= sizeof *c; - - acc = 0; - for (i = little_end (n); is_not_msd (i, n); i = next_msd (i)) - { - /* Widen before adding to avoid loss of high bits. */ - acc += (unsigned long) a[i] + b[i]; - c[i] = acc & low16; - acc = acc >> 16; - } - return acc; -} -#endif - -#ifdef L_anddi3 -long long -__anddi3 (u, v) - long long u, v; -{ - long_long w; - long_long uu, vv; - - uu.ll = u; - vv.ll = v; - - w.s.high = uu.s.high & vv.s.high; - w.s.low = uu.s.low & vv.s.low; - - return w.ll; -} -#endif - -#ifdef L_iordi3 -long long -__iordi3 (u, v) - long long u, v; -{ - long_long w; - long_long uu, vv; - - uu.ll = u; - vv.ll = v; - - w.s.high = uu.s.high | vv.s.high; - w.s.low = uu.s.low | vv.s.low; - - return w.ll; -} -#endif - -#ifdef L_xordi3 -long long -__xordi3 (u, v) - long long u, v; -{ - long_long w; - long_long uu, vv; - - uu.ll = u; - vv.ll = v; - - w.s.high = uu.s.high ^ vv.s.high; - w.s.low = uu.s.low ^ vv.s.low; - - return w.ll; -} -#endif - -#ifdef L_one_cmpldi2 -long long -__one_cmpldi2 (u) - long long u; -{ - long_long w; - long_long uu; - - uu.ll = u; - - w.s.high = ~uu.s.high; - w.s.low = ~uu.s.low; - - return w.ll; -} -#endif - -#ifdef L_lshldi3 -long long -__lshldi3 (u, b1) - long long u; - long long b1; -{ - long_long w; - unsigned long carries; - int bm; - long_long uu; - int b = b1; - - if (b == 0) - return u; - - uu.ll = u; - - bm = (sizeof (int) * BITS_PER_UNIT) - b; - if (bm <= 0) - { - w.s.low = 0; - w.s.high = (unsigned long)uu.s.low << -bm; - } - else - { - carries = (unsigned long)uu.s.low >> bm; - w.s.low = (unsigned long)uu.s.low << b; - w.s.high = ((unsigned long)uu.s.high << b) | carries; - } - - return w.ll; -} -#endif - -#ifdef L_lshrdi3 -long long -__lshrdi3 (u, b1) - long long u; - long long b1; -{ - long_long w; - unsigned long carries; - int bm; - long_long uu; - int b = b1; - - if (b == 0) - return u; - - uu.ll = u; - - bm = (sizeof (int) * BITS_PER_UNIT) - b; - if (bm <= 0) - { - w.s.high = 0; - w.s.low = (unsigned long)uu.s.high >> -bm; - } - else - { - carries = (unsigned long)uu.s.high << bm; - w.s.high = (unsigned long)uu.s.high >> b; - w.s.low = ((unsigned long)uu.s.low >> b) | carries; - } - - return w.ll; -} -#endif - -#ifdef L_ashldi3 -long long -__ashldi3 (u, b1) - long long u; - long long b1; -{ - long_long w; - unsigned long carries; - int bm; - long_long uu; - int b = b1; - - if (b == 0) - return u; - - uu.ll = u; - - bm = (sizeof (int) * BITS_PER_UNIT) - b; - if (bm <= 0) - { - w.s.low = 0; - w.s.high = (unsigned long)uu.s.low << -bm; - } - else - { - carries = (unsigned long)uu.s.low >> bm; - w.s.low = (unsigned long)uu.s.low << b; - w.s.high = ((unsigned long)uu.s.high << b) | carries; - } - - return w.ll; -} -#endif - -#ifdef L_ashrdi3 -long long -__ashrdi3 (u, b1) - long long u; - long long b1; -{ - long_long w; - unsigned long carries; - int bm; - long_long uu; - int b = b1; - - if (b == 0) - return u; - - uu.ll = u; - - bm = (sizeof (int) * BITS_PER_UNIT) - b; - if (bm <= 0) - { - w.s.high = uu.s.high >> 31; /* just to make w.s.high 1..1 or 0..0 */ - w.s.low = uu.s.high >> -bm; - } - else - { - carries = (unsigned long)uu.s.high << bm; - w.s.high = uu.s.high >> b; - w.s.low = ((unsigned long)uu.s.low >> b) | carries; - } - - return w.ll; -} -#endif - -#ifdef L_subdi3 -long long -__subdi3 (u, v) - long long u, v; -{ - long a[2], b[2], c[2]; - long_long w; - long_long uu, vv; - - uu.ll = u; - vv.ll = v; - - a[HIGH] = uu.s.high; - a[LOW] = uu.s.low; - b[HIGH] = vv.s.high; - b[LOW] = vv.s.low; - - bsub (a, b, c, sizeof c); - - w.s.high = c[HIGH]; - w.s.low = c[LOW]; - return w.ll; -} - -static int -bsub (a, b, c, n) - unsigned short *a, *b, *c; - size_t n; -{ - signed long acc; - int i; - - n /= sizeof *c; - - acc = 0; - for (i = little_end (n); is_not_msd (i, n); i = next_msd (i)) - { - /* Widen before subtracting to avoid loss of high bits. */ - acc += (long) a[i] - b[i]; - c[i] = acc & low16; - acc = acc >> 16; - } - return acc; -} -#endif - -#ifdef L_muldi3 -long long -__muldi3 (u, v) - long long u, v; -{ - long a[2], b[2], c[2][2]; - long_long w; - long_long uu, vv; - - uu.ll = u; - vv.ll = v; - - a[HIGH] = uu.s.high; - a[LOW] = uu.s.low; - b[HIGH] = vv.s.high; - b[LOW] = vv.s.low; - - bmul (a, b, c, sizeof a, sizeof b); - - w.s.high = c[LOW][HIGH]; - w.s.low = c[LOW][LOW]; - return w.ll; -} - -static void -bmul (a, b, c, m, n) - unsigned short *a, *b, *c; - size_t m, n; -{ - int i, j; - unsigned long acc; - - bzero (c, m + n); - - m /= sizeof *a; - n /= sizeof *b; - - for (j = little_end (n); is_not_msd (j, n); j = next_msd (j)) - { - unsigned short *c1 = c + j + little_end (2); - acc = 0; - for (i = little_end (m); is_not_msd (i, m); i = next_msd (i)) - { - /* Widen before arithmetic to avoid loss of high bits. */ - acc += (unsigned long) a[i] * b[j] + c1[i]; - c1[i] = acc & low16; - acc = acc >> 16; - } - c1[i] = acc; - } -} -#endif - -#ifdef L_divdi3 -long long -__divdi3 (u, v) - long long u, v; -{ - if (u < 0) - if (v < 0) - return (unsigned long long) -u / (unsigned long long) -v; - else - return - ((unsigned long long) -u / (unsigned long long) v); - else - if (v < 0) - return - ((unsigned long long) u / (unsigned long long) -v); - else - return (unsigned long long) u / (unsigned long long) v; -} -#endif - -#ifdef L_moddi3 -long long -__moddi3 (u, v) - long long u, v; -{ - if (u < 0) - if (v < 0) - return - ((unsigned long long) -u % (unsigned long long) -v); - else - return - ((unsigned long long) -u % (unsigned long long) v); - else - if (v < 0) - return (unsigned long long) u % (unsigned long long) -v; - else - return (unsigned long long) u % (unsigned long long) v; -} -#endif - -#ifdef L_udivdi3 -long long -__udivdi3 (u, v) - long long u, v; -{ - unsigned long a[2][2], b[2], q[2], r[2]; - long_long w; - long_long uu, vv; - - uu.ll = u; - vv.ll = v; - - a[HIGH][HIGH] = 0; - a[HIGH][LOW] = 0; - a[LOW][HIGH] = uu.s.high; - a[LOW][LOW] = uu.s.low; - b[HIGH] = vv.s.high; - b[LOW] = vv.s.low; - - __bdiv (a, b, q, r, sizeof a, sizeof b); - - w.s.high = q[HIGH]; - w.s.low = q[LOW]; - return w.ll; -} -#endif - -#ifdef L_umoddi3 -long long -__umoddi3 (u, v) - long long u, v; -{ - unsigned long a[2][2], b[2], q[2], r[2]; - long_long w; - long_long uu, vv; - - uu.ll = u; - vv.ll = v; - - a[HIGH][HIGH] = 0; - a[HIGH][LOW] = 0; - a[LOW][HIGH] = uu.s.high; - a[LOW][LOW] = uu.s.low; - b[HIGH] = vv.s.high; - b[LOW] = vv.s.low; - - __bdiv (a, b, q, r, sizeof a, sizeof b); - - w.s.high = r[HIGH]; - w.s.low = r[LOW]; - return w.ll; -} -#endif - -#ifdef L_negdi2 -long long -__negdi2 (u) - long long u; -{ - unsigned long a[2], b[2]; - long_long w; - long_long uu; - - uu.ll = u; - - a[HIGH] = uu.s.high; - a[LOW] = uu.s.low; - - bneg (a, b, sizeof b); - - w.s.high = b[HIGH]; - w.s.low = b[LOW]; - return w.ll; -} - -static int -bneg (a, b, n) - unsigned short *a, *b; - size_t n; -{ - signed long acc; - int i; - - n /= sizeof (short); - - acc = 0; - for (i = little_end (n); is_not_msd (i, n); i = next_msd (i)) - { - acc -= a[i]; - b[i] = acc & low16; - acc = acc >> 16; - } - return acc; -} -#endif - -/* Divide a by b, producing quotient q and remainder r. - - sizeof a is m - sizeof b is n - sizeof q is m - n - sizeof r is n - - The quotient must fit in m - n bytes, i.e., the most significant - n digits of a must be less than b, and m must be greater than n. */ - -/* The name of this used to be __div_internal, - but that is too long for SYSV. */ - -#ifdef L_bdiv -void -__bdiv (a, b, q, r, m, n) - unsigned short *a, *b, *q, *r; - size_t m, n; -{ - unsigned long qhat, rhat; - unsigned long acc; - unsigned short *u = (unsigned short *) alloca (m); - unsigned short *v = (unsigned short *) alloca (n); - unsigned short *u0, *u1, *u2; - unsigned short *v0; - int d, qn; - int i, j; - - m /= sizeof *a; - n /= sizeof *b; - qn = m - n; - - /* Remove leading zero digits from divisor, and the same number of - digits (which must be zero) from dividend. */ - - while (b[big_end (n)] == 0) - { - r[big_end (n)] = 0; - - a += little_end (2); - b += little_end (2); - r += little_end (2); - m--; - n--; - - /* Check for zero divisor. */ - if (n == 0) - abort (); - } - - /* If divisor is a single digit, do short division. */ - - if (n == 1) - { - acc = a[big_end (m)]; - a += little_end (2); - for (j = big_end (qn); is_not_lsd (j, qn); j = next_lsd (j)) - { - acc = (acc << 16) | a[j]; - q[j] = acc / *b; - acc = acc % *b; - } - *r = acc; - return; - } - - /* No such luck, must do long division. Shift divisor and dividend - left until the high bit of the divisor is 1. */ - - for (d = 0; d < 16; d++) - if (b[big_end (n)] & (1 << (16 - 1 - d))) - break; - - bshift (a, d, u, 0, m); - bshift (b, d, v, 0, n); - - /* Get pointers to the high dividend and divisor digits. */ - - u0 = u + big_end (m) - big_end (qn); - u1 = next_lsd (u0); - u2 = next_lsd (u1); - u += little_end (2); - - v0 = v + big_end (n); - - /* Main loop: find a quotient digit, multiply it by the divisor, - and subtract that from the dividend, shifted over the right amount. */ - - for (j = big_end (qn); is_not_lsd (j, qn); j = next_lsd (j)) - { - /* Quotient digit initial guess: high 2 dividend digits over high - divisor digit. */ - - if (u0[j] == *v0) - { - qhat = B - 1; - rhat = (unsigned long) *v0 + u1[j]; - } - else - { - unsigned long numerator = ((unsigned long) u0[j] << 16) | u1[j]; - qhat = numerator / *v0; - rhat = numerator % *v0; - } - - /* Now get the quotient right for high 3 dividend digits over - high 2 divisor digits. */ - - while (rhat < B && qhat * *next_lsd (v0) > ((rhat << 16) | u2[j])) - { - qhat -= 1; - rhat += *v0; - } - - /* Multiply quotient by divisor, subtract from dividend. */ - - acc = 0; - for (i = little_end (n); is_not_msd (i, n); i = next_msd (i)) - { - acc += (unsigned long) (u + j)[i] - v[i] * qhat; - (u + j)[i] = acc & low16; - if (acc < B) - acc = 0; - else - acc = (acc >> 16) | -B; - } - - q[j] = qhat; - - /* Quotient may have been too high by 1. If dividend went negative, - decrement the quotient by 1 and add the divisor back. */ - - if ((signed long) (acc + u0[j]) < 0) - { - q[j] -= 1; - acc = 0; - for (i = little_end (n); is_not_msd (i, n); i = next_msd (i)) - { - acc += (unsigned long) (u + j)[i] + v[i]; - (u + j)[i] = acc & low16; - acc = acc >> 16; - } - } - } - - /* Now the remainder is what's left of the dividend, shifted right - by the amount of the normalizing left shift at the top. */ - - r[big_end (n)] = bshift (u + 1 + little_end (j - 1), - 16 - d, - r + little_end (2), - u[little_end (m - 1)] >> d, - n - 1); -} - -/* Left shift U by K giving W; fill the introduced low-order bits with - CARRY_IN. Length of U and W is N. Return carry out. K must be - in 0 .. 16. */ - -static int -bshift (u, k, w, carry_in, n) - unsigned short *u, *w, carry_in; - int k, n; -{ - unsigned long acc; - int i; - - if (k == 0) - { - bcopy (u, w, n * sizeof *u); - return 0; - } - - acc = carry_in; - for (i = little_end (n); is_not_msd (i, n); i = next_msd (i)) - { - acc |= (unsigned long) u[i] << k; - w[i] = acc & low16; - acc = acc >> 16; - } - return acc; -} -#endif - -#ifdef L_cmpdi2 -SItype -__cmpdi2 (a, b) - long long a, b; -{ - long_long au, bu; - - au.ll = a, bu.ll = b; - - if (au.s.high < bu.s.high) - return 0; - else if (au.s.high > bu.s.high) - return 2; - if ((unsigned) au.s.low < (unsigned) bu.s.low) - return 0; - else if ((unsigned) au.s.low > (unsigned) bu.s.low) - return 2; - return 1; -} -#endif - -#ifdef L_ucmpdi2 -SItype -__ucmpdi2 (a, b) - long long a, b; -{ - long_long au, bu; - - au.ll = a, bu.ll = b; - - if ((unsigned) au.s.high < (unsigned) bu.s.high) - return 0; - else if ((unsigned) au.s.high > (unsigned) bu.s.high) - return 2; - if ((unsigned) au.s.low < (unsigned) bu.s.low) - return 0; - else if ((unsigned) au.s.low > (unsigned) bu.s.low) - return 2; - return 1; -} -#endif - -#ifdef L_fixunsdfdi -#define HIGH_WORD_COEFF (((long long) 1) << BITS_PER_WORD) - -long long -__fixunsdfdi (a) - double a; -{ - double b; - unsigned long long v; - - if (a < 0) - return 0; - - /* Compute high word of result, as a flonum. */ - b = (a / HIGH_WORD_COEFF); - /* Convert that to fixed (but not to long long!), - and shift it into the high word. */ - v = (unsigned long int) b; - v <<= BITS_PER_WORD; - /* Remove high part from the double, leaving the low part as flonum. */ - a -= (double)v; - /* Convert that to fixed (but not to long long!) and add it in. - Sometimes A comes out negative. This is significant, since - A has more bits than a long int does. */ - if (a < 0) - v -= (unsigned long int) (- a); - else - v += (unsigned long int) a; - return v; -} -#endif - -#ifdef L_fixdfdi -long long -__fixdfdi (a) - double a; -{ - long long __fixunsdfdi (double a); - - if (a < 0) - return - __fixunsdfdi (-a); - return __fixunsdfdi (a); -} -#endif - -#ifdef L_floatdidf -#define HIGH_HALFWORD_COEFF (((long long) 1) << (BITS_PER_WORD / 2)) -#define HIGH_WORD_COEFF (((long long) 1) << BITS_PER_WORD) - -double -__floatdidf (u) - long long u; -{ - double d; - int negate = 0; - - if (u < 0) - u = -u, negate = 1; - - d = (unsigned int) (u >> BITS_PER_WORD); - d *= HIGH_HALFWORD_COEFF; - d *= HIGH_HALFWORD_COEFF; - d += (unsigned int) (u & (HIGH_WORD_COEFF - 1)); - - return (negate ? -d : d); -} -#endif - -#ifdef L_varargs -#ifdef i860 - asm (" .text"); - asm (" .align 4"); - - asm ("___builtin_saveregs::"); - asm (" mov sp,r30"); - asm (" andnot 0x0f,sp,sp"); - asm (" adds -96,sp,sp"); /* allocate sufficient space on the stack */ - - asm (" st.l r16, 0(sp)"); /* save integer regs (r16-r27) */ - asm (" st.l r17, 4(sp)"); /* int fixed[12] */ - asm (" st.l r18, 8(sp)"); - asm (" st.l r19,12(sp)"); - asm (" st.l r20,16(sp)"); - asm (" st.l r21,20(sp)"); - asm (" st.l r22,24(sp)"); - asm (" st.l r23,28(sp)"); - asm (" st.l r24,32(sp)"); - asm (" st.l r25,36(sp)"); - asm (" st.l r26,40(sp)"); - asm (" st.l r27,44(sp)"); - - asm (" fst.q f8, 48(sp)"); /* save floating regs (f8-f15) */ - asm (" fst.q f12,64(sp)"); /* int floating[8] */ - - asm (" st.l r28,80(sp)"); /* pointer to more args */ - asm (" st.l r0, 84(sp)"); /* nfixed */ - asm (" st.l r0, 88(sp)"); /* nfloating */ - asm (" st.l r0, 92(sp)"); /* pad */ - - asm (" mov sp,r16"); - asm (" bri r1"); - asm (" mov r30,sp"); - /* recover stack and pass address to start - of data. */ -#endif -#ifdef sparc - asm (".global ___builtin_saveregs"); - asm ("___builtin_saveregs:"); - asm ("st %i0,[%fp+68]"); - asm ("st %i1,[%fp+72]"); - asm ("st %i2,[%fp+76]"); - asm ("st %i3,[%fp+80]"); - asm ("st %i4,[%fp+84]"); - asm ("retl"); - asm ("st %i5,[%fp+88]"); -#else /* not sparc */ -#if defined(MIPSEL) | defined(R3000) | defined(R2000) | defined(mips) - - asm (" .text"); - asm (" .ent __builtin_saveregs"); - asm (" .globl __builtin_saveregs"); - asm ("__builtin_saveregs:"); - asm (" sw $4,0($30)"); - asm (" sw $5,4($30)"); - asm (" sw $6,8($30)"); - asm (" sw $7,12($30)"); - asm (" j $31"); - asm (" .end __builtin_saveregs"); -#else /* not mips */ -__builtin_saveregs () -{ - abort (); -} -#endif /* not mips */ -#endif /* not sparc */ -#endif diff --git a/gnu/usr.bin/gcc1/gnulib/Makefile b/gnu/usr.bin/gcc1/gnulib/Makefile deleted file mode 100644 index 482874137e..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# @(#)Makefile 6.4 (Berkeley) 1/30/91 - -LIB= gnulib -CFLAGS+= -I. -I$(.CURDIR)/../cc1 -I$(.CURDIR)/../cc1/config -NOMAN= noman -.PATH: $(.CURDIR)/$(MACHINE) $(.CURDIR)/g++ $(.CURDIR)/longlong - -.include "$(MACHINE)/Makefile.machine" -GPLUS_SRCS= builtin_New.c builtin_del.c builtin_new.c eprintf.c -LONGLONG_SRCS= adddi3.c anddi3.c ashldi3.c ashrdi3.c bdiv.c \ - cmpdi2.c divdi3.c fixdfdi.c fixunsdfdi.c \ - floatdidf.c iordi3.c lshldi3.c lshrdi3.c \ - moddi3.c muldi3.c negdi2.c one_cmpldi2.c \ - subdi3.c ucmpdi2.c udivdi3.c umoddi3.c xordi3.c -SRCS= $(MACHINE_SRCS) $(GPLUS_SRCS) $(LONGLONG_SRCS) - -$(OBJS): config.h - -afterinstall: - -rm -f $(DESTDIR)$(LIBDIR)/gcc-gnulib - cd $(DESTDIR)$(LIBDIR); ln -s libgnulib.a gcc-gnulib - -.include -.include "../Makefile.symlinks" diff --git a/gnu/usr.bin/gcc1/gnulib/g++/builtin_New.c b/gnu/usr.bin/gcc1/gnulib/g++/builtin_New.c deleted file mode 100644 index 98a64e7188..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/g++/builtin_New.c +++ /dev/null @@ -1,58 +0,0 @@ -typedef void (*vfp)(); - -static void -default_new_handler (); - -vfp __new_handler = default_new_handler; - -char * -__builtin_vec_new (p, maxindex, size, ctor) - char *p; - int maxindex, size; - void (*ctor)(); -{ - int i, nelts = maxindex + 1; - char *rval; - - if (p == 0) - p = (char *)__builtin_new (nelts * size); - - rval = p; - - for (i = 0; i < nelts; i++) - { - (*ctor) (p); - p += size; - } - - return rval; -} - -vfp -__set_new_handler (handler) - vfp handler; -{ - vfp prev_handler; - - prev_handler = __new_handler; - if (handler == 0) handler = default_new_handler; - __new_handler = handler; - return prev_handler; -} - -vfp -set_new_handler (handler) - vfp handler; -{ - return __set_new_handler (handler); -} - -static void -default_new_handler () -{ - /* don't use fprintf (stderr, ...) because it may need to call malloc. */ - write (2, "default_new_handler: out of memory... aaaiiiiiieeeeeeeeeeeeee!\n", 65); - /* don't call exit () because that may call global destructors which - may cause a loop. */ - _exit (-1); -} diff --git a/gnu/usr.bin/gcc1/gnulib/g++/builtin_del.c b/gnu/usr.bin/gcc1/gnulib/g++/builtin_del.c deleted file mode 100644 index 8f0df3dfa4..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/g++/builtin_del.c +++ /dev/null @@ -1,31 +0,0 @@ -typedef void (*vfp)(); - -void -__builtin_delete (ptr) - char *ptr; -{ - if (ptr) - free (ptr); -} - -void -__builtin_vec_delete (ptr, maxindex, size, dtor, auto_delete_vec, auto_delete) - char *ptr; - int maxindex, size; - void (*dtor)(); - int auto_delete; -{ - int i, nelts = maxindex + 1; - char *p = ptr; - - ptr += nelts * size; - - for (i = 0; i < nelts; i++) - { - ptr -= size; - (*dtor) (ptr, auto_delete); - } - - if (auto_delete_vec) - free (p); -} diff --git a/gnu/usr.bin/gcc1/gnulib/g++/builtin_new.c b/gnu/usr.bin/gcc1/gnulib/g++/builtin_new.c deleted file mode 100644 index 2f1fdf198f..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/g++/builtin_new.c +++ /dev/null @@ -1,15 +0,0 @@ -typedef void (*vfp)(); - -extern vfp __new_handler; - -char * -__builtin_new (sz) - long sz; -{ - char *p; - - p = (char *)malloc (sz); - if (p == 0) - (*__new_handler) (); - return p; -} diff --git a/gnu/usr.bin/gcc1/gnulib/g++/eprintf.c b/gnu/usr.bin/gcc1/gnulib/g++/eprintf.c deleted file mode 100644 index a53461636d..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/g++/eprintf.c +++ /dev/null @@ -1,13 +0,0 @@ -#include -/* This is used by the GNU `assert' macro. */ -void -__eprintf (string, expression, line, filename) - char *string; - char *expression; - int line; - char *filename; -{ - fprintf (stderr, string, expression, line, filename); - fflush (stderr); - abort (); -} diff --git a/gnu/usr.bin/gcc1/gnulib/i386/DEFS.h b/gnu/usr.bin/gcc1/gnulib/i386/DEFS.h deleted file mode 100644 index 6b888c470a..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/i386/DEFS.h +++ /dev/null @@ -1,47 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * William Jolitz. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)DEFS.h 5.1 (Berkeley) 4/23/90 - */ - -#ifdef PROF -#define ENTRY(x) .globl _/**/x; _/**/x: \ - .data; 1:; .long 0; .text; lea 1b,%eax ; call mcount -#define ASENTRY(x) .globl x; x: \ - .data; 1:; .long 0; .text; lea 1b,%eax ; call mcount -#else -#define ENTRY(x) .globl _/**/x; _/**/x: -#define ASENTRY(x) .globl x; x: -#endif diff --git a/gnu/usr.bin/gcc1/gnulib/i386/Makefile.machine b/gnu/usr.bin/gcc1/gnulib/i386/Makefile.machine deleted file mode 100644 index 459a6a4cdd..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/i386/Makefile.machine +++ /dev/null @@ -1 +0,0 @@ -MACHINE_SRCS= fixdfsi.s fixunsdfsi.s diff --git a/gnu/usr.bin/gcc1/gnulib/i386/fixdfsi.s b/gnu/usr.bin/gcc1/gnulib/i386/fixdfsi.s deleted file mode 100644 index 4704fa6e35..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/i386/fixdfsi.s +++ /dev/null @@ -1,46 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * William Jolitz. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) - .asciz "@(#)fixdfsi.s 5.4 (Berkeley) 5/13/91" -#endif /* LIBC_SCCS and not lint */ - - .globl ___fixdfsi -___fixdfsi: - fldl 4(%esp) - fistpl 4(%esp) - movl 4(%esp),%eax - ret diff --git a/gnu/usr.bin/gcc1/gnulib/i386/fixunsdfsi.s b/gnu/usr.bin/gcc1/gnulib/i386/fixunsdfsi.s deleted file mode 100644 index d3486c4b8a..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/i386/fixunsdfsi.s +++ /dev/null @@ -1,60 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * William Jolitz. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) - .asciz "@(#)fixunsdfsi.s 5.1 12/17/90" -#endif /* LIBC_SCCS and not lint */ - - .globl ___fixunsdfsi -___fixunsdfsi: - fldl 4(%esp) /* argument double to accum stack */ - frndint /* create integer */ - fcoml fbiggestsigned /* bigger than biggest signed? */ - fstsw %ax - sahf - jnb 1f - - fistpl 4(%esp) - movl 4(%esp),%eax - ret - -1: fsubl fbiggestsigned /* reduce for proper conversion */ - fistpl 4(%esp) /* convert */ - movl 4(%esp),%eax - orl $0x80000000,%eax /* restore bias */ - ret - -fbiggestsigned: .double 0r2147483648.0 diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/adddi3.c b/gnu/usr.bin/gcc1/gnulib/longlong/adddi3.c deleted file mode 100644 index 287455b201..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/adddi3.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "longlong.h" - -static int badd (); - -long long -__adddi3 (u, v) - long long u, v; -{ - long a[2], b[2], c[2]; - long_long w; - long_long uu, vv; - - uu.ll = u; - vv.ll = v; - - a[HIGH] = uu.s.high; - a[LOW] = uu.s.low; - b[HIGH] = vv.s.high; - b[LOW] = vv.s.low; - - badd (a, b, c, sizeof c); - - w.s.high = c[HIGH]; - w.s.low = c[LOW]; - return w.ll; -} - -static int -badd (a, b, c, n) - unsigned short *a, *b, *c; - size_t n; -{ - unsigned long acc; - int i; - - n /= sizeof *c; - - acc = 0; - for (i = little_end (n); is_not_msd (i, n); i = next_msd (i)) - { - /* Widen before adding to avoid loss of high bits. */ - acc += (unsigned long) a[i] + b[i]; - c[i] = acc & low16; - acc = acc >> 16; - } - return acc; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/anddi3.c b/gnu/usr.bin/gcc1/gnulib/longlong/anddi3.c deleted file mode 100644 index 351cbdefa2..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/anddi3.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "longlong.h" - -long long -__anddi3 (u, v) - long long u, v; -{ - long_long w; - long_long uu, vv; - - uu.ll = u; - vv.ll = v; - - w.s.high = uu.s.high & vv.s.high; - w.s.low = uu.s.low & vv.s.low; - - return w.ll; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/ashldi3.c b/gnu/usr.bin/gcc1/gnulib/longlong/ashldi3.c deleted file mode 100644 index 9649ff98c0..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/ashldi3.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "longlong.h" - -long long -__ashldi3 (u, b1) - long long u; - long long b1; -{ - long_long w; - unsigned long carries; - int bm; - long_long uu; - int b = b1; - - if (b == 0) - return u; - - uu.ll = u; - - bm = (sizeof (int) * BITS_PER_UNIT) - b; - if (bm <= 0) - { - w.s.low = 0; - w.s.high = (unsigned long)uu.s.low << -bm; - } - else - { - carries = (unsigned long)uu.s.low >> bm; - w.s.low = (unsigned long)uu.s.low << b; - w.s.high = ((unsigned long)uu.s.high << b) | carries; - } - - return w.ll; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/ashrdi3.c b/gnu/usr.bin/gcc1/gnulib/longlong/ashrdi3.c deleted file mode 100644 index fd192bf349..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/ashrdi3.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "longlong.h" - -long long -__ashrdi3 (u, b1) - long long u; - long long b1; -{ - long_long w; - unsigned long carries; - int bm; - long_long uu; - int b = b1; - - if (b == 0) - return u; - - uu.ll = u; - - bm = (sizeof (int) * BITS_PER_UNIT) - b; - if (bm <= 0) - { - w.s.high = uu.s.high >> 31; /* just to make w.s.high 1..1 or 0..0 */ - w.s.low = uu.s.high >> -bm; - } - else - { - carries = (unsigned long)uu.s.high << bm; - w.s.high = uu.s.high >> b; - w.s.low = ((unsigned long)uu.s.low >> b) | carries; - } - - return w.ll; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/bdiv.c b/gnu/usr.bin/gcc1/gnulib/longlong/bdiv.c deleted file mode 100644 index f5d8dd1fd4..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/bdiv.c +++ /dev/null @@ -1,185 +0,0 @@ -#include "longlong.h" - -static int bshift (); - -/* Divide a by b, producing quotient q and remainder r. - - sizeof a is m - sizeof b is n - sizeof q is m - n - sizeof r is n - - The quotient must fit in m - n bytes, i.e., the most significant - n digits of a must be less than b, and m must be greater than n. */ - -/* The name of this used to be __div_internal, - but that is too long for SYSV. */ - -void -__bdiv (a, b, q, r, m, n) - unsigned short *a, *b, *q, *r; - size_t m, n; -{ - unsigned long qhat, rhat; - unsigned long acc; - unsigned short *u = (unsigned short *) alloca (m); - unsigned short *v = (unsigned short *) alloca (n); - unsigned short *u0, *u1, *u2; - unsigned short *v0; - int d, qn; - int i, j; - - m /= sizeof *a; - n /= sizeof *b; - qn = m - n; - - /* Remove leading zero digits from divisor, and the same number of - digits (which must be zero) from dividend. */ - - while (b[big_end (n)] == 0) - { - r[big_end (n)] = 0; - - a += little_end (2); - b += little_end (2); - r += little_end (2); - m--; - n--; - - /* Check for zero divisor. */ - if (n == 0) - abort (); - } - - /* If divisor is a single digit, do short division. */ - - if (n == 1) - { - acc = a[big_end (m)]; - a += little_end (2); - for (j = big_end (qn); is_not_lsd (j, qn); j = next_lsd (j)) - { - acc = (acc << 16) | a[j]; - q[j] = acc / *b; - acc = acc % *b; - } - *r = acc; - return; - } - - /* No such luck, must do long division. Shift divisor and dividend - left until the high bit of the divisor is 1. */ - - for (d = 0; d < 16; d++) - if (b[big_end (n)] & (1 << (16 - 1 - d))) - break; - - bshift (a, d, u, 0, m); - bshift (b, d, v, 0, n); - - /* Get pointers to the high dividend and divisor digits. */ - - u0 = u + big_end (m) - big_end (qn); - u1 = next_lsd (u0); - u2 = next_lsd (u1); - u += little_end (2); - - v0 = v + big_end (n); - - /* Main loop: find a quotient digit, multiply it by the divisor, - and subtract that from the dividend, shifted over the right amount. */ - - for (j = big_end (qn); is_not_lsd (j, qn); j = next_lsd (j)) - { - /* Quotient digit initial guess: high 2 dividend digits over high - divisor digit. */ - - if (u0[j] == *v0) - { - qhat = B - 1; - rhat = (unsigned long) *v0 + u1[j]; - } - else - { - unsigned long numerator = ((unsigned long) u0[j] << 16) | u1[j]; - qhat = numerator / *v0; - rhat = numerator % *v0; - } - - /* Now get the quotient right for high 3 dividend digits over - high 2 divisor digits. */ - - while (rhat < B && qhat * *next_lsd (v0) > ((rhat << 16) | u2[j])) - { - qhat -= 1; - rhat += *v0; - } - - /* Multiply quotient by divisor, subtract from dividend. */ - - acc = 0; - for (i = little_end (n); is_not_msd (i, n); i = next_msd (i)) - { - acc += (unsigned long) (u + j)[i] - v[i] * qhat; - (u + j)[i] = acc & low16; - if (acc < B) - acc = 0; - else - acc = (acc >> 16) | -B; - } - - q[j] = qhat; - - /* Quotient may have been too high by 1. If dividend went negative, - decrement the quotient by 1 and add the divisor back. */ - - if ((signed long) (acc + u0[j]) < 0) - { - q[j] -= 1; - acc = 0; - for (i = little_end (n); is_not_msd (i, n); i = next_msd (i)) - { - acc += (unsigned long) (u + j)[i] + v[i]; - (u + j)[i] = acc & low16; - acc = acc >> 16; - } - } - } - - /* Now the remainder is what's left of the dividend, shifted right - by the amount of the normalizing left shift at the top. */ - - r[big_end (n)] = bshift (u + 1 + little_end (j - 1), - 16 - d, - r + little_end (2), - u[little_end (m - 1)] >> d, - n - 1); -} - -/* Left shift U by K giving W; fill the introduced low-order bits with - CARRY_IN. Length of U and W is N. Return carry out. K must be - in 0 .. 16. */ - -static int -bshift (u, k, w, carry_in, n) - unsigned short *u, *w, carry_in; - int k, n; -{ - unsigned long acc; - int i; - - if (k == 0) - { - bcopy (u, w, n * sizeof *u); - return 0; - } - - acc = carry_in; - for (i = little_end (n); is_not_msd (i, n); i = next_msd (i)) - { - acc |= (unsigned long) u[i] << k; - w[i] = acc & low16; - acc = acc >> 16; - } - return acc; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/cmpdi2.c b/gnu/usr.bin/gcc1/gnulib/longlong/cmpdi2.c deleted file mode 100644 index ac1a4f2e99..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/cmpdi2.c +++ /dev/null @@ -1,20 +0,0 @@ -#include "longlong.h" - -SItype -__cmpdi2 (a, b) - long long a, b; -{ - long_long au, bu; - - au.ll = a, bu.ll = b; - - if (au.s.high < bu.s.high) - return 0; - else if (au.s.high > bu.s.high) - return 2; - if ((unsigned) au.s.low < (unsigned) bu.s.low) - return 0; - else if ((unsigned) au.s.low > (unsigned) bu.s.low) - return 2; - return 1; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/divdi3.c b/gnu/usr.bin/gcc1/gnulib/longlong/divdi3.c deleted file mode 100644 index b912101338..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/divdi3.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "longlong.h" - -long long -__divdi3 (u, v) - long long u, v; -{ - if (u < 0) - if (v < 0) - return (unsigned long long) -u / (unsigned long long) -v; - else - return - ((unsigned long long) -u / (unsigned long long) v); - else - if (v < 0) - return - ((unsigned long long) u / (unsigned long long) -v); - else - return (unsigned long long) u / (unsigned long long) v; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/fixdfdi.c b/gnu/usr.bin/gcc1/gnulib/longlong/fixdfdi.c deleted file mode 100644 index 2f5fc656bb..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/fixdfdi.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "longlong.h" - -long long -__fixdfdi (a) - double a; -{ - long long __fixunsdfdi (double a); - - if (a < 0) - return - __fixunsdfdi (-a); - return __fixunsdfdi (a); -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/fixunsdfdi.c b/gnu/usr.bin/gcc1/gnulib/longlong/fixunsdfdi.c deleted file mode 100644 index ee5bfe8c18..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/fixunsdfdi.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "longlong.h" - -#define HIGH_WORD_COEFF (((long long) 1) << BITS_PER_WORD) - -long long -__fixunsdfdi (a) - double a; -{ - double b; - unsigned long long v; - - if (a < 0) - return 0; - - /* Compute high word of result, as a flonum. */ - b = (a / HIGH_WORD_COEFF); - /* Convert that to fixed (but not to long long!), - and shift it into the high word. */ - v = (unsigned long int) b; - v <<= BITS_PER_WORD; - /* Remove high part from the double, leaving the low part as flonum. */ - a -= (double)v; - /* Convert that to fixed (but not to long long!) and add it in. - Sometimes A comes out negative. This is significant, since - A has more bits than a long int does. */ - if (a < 0) - v -= (unsigned long int) (- a); - else - v += (unsigned long int) a; - return v; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/floatdidf.c b/gnu/usr.bin/gcc1/gnulib/longlong/floatdidf.c deleted file mode 100644 index 5836b73c96..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/floatdidf.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "longlong.h" - -#define HIGH_HALFWORD_COEFF (((long long) 1) << (BITS_PER_WORD / 2)) -#define HIGH_WORD_COEFF (((long long) 1) << BITS_PER_WORD) - -double -__floatdidf (u) - long long u; -{ - double d; - int negate = 0; - - if (u < 0) - u = -u, negate = 1; - - d = (unsigned int) (u >> BITS_PER_WORD); - d *= HIGH_HALFWORD_COEFF; - d *= HIGH_HALFWORD_COEFF; - d += (unsigned int) (u & (HIGH_WORD_COEFF - 1)); - - return (negate ? -d : d); -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/iordi3.c b/gnu/usr.bin/gcc1/gnulib/longlong/iordi3.c deleted file mode 100644 index 956ae1a094..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/iordi3.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "longlong.h" - -long long -__iordi3 (u, v) - long long u, v; -{ - long_long w; - long_long uu, vv; - - uu.ll = u; - vv.ll = v; - - w.s.high = uu.s.high | vv.s.high; - w.s.low = uu.s.low | vv.s.low; - - return w.ll; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/longlong.h b/gnu/usr.bin/gcc1/gnulib/longlong/longlong.h deleted file mode 100644 index 83cb1df4cd..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/longlong.h +++ /dev/null @@ -1,67 +0,0 @@ -/* More subroutines needed by GCC output code on some machines. */ -/* Compile this one with gcc. */ - -#include "config.h" -#include - -#ifndef SItype -#define SItype long int -#endif - -/* long long ints are pairs of long ints in the order determined by - WORDS_BIG_ENDIAN. */ - -#ifdef WORDS_BIG_ENDIAN - struct longlong {long high, low;}; -#else - struct longlong {long low, high;}; -#endif - -/* We need this union to unpack/pack longlongs, since we don't have - any arithmetic yet. Incoming long long parameters are stored - into the `ll' field, and the unpacked result is read from the struct - longlong. */ - -typedef union -{ - struct longlong s; - long long ll; - SItype i[2]; - unsigned SItype ui[2]; -} long_long; - -/* Internally, long long ints are strings of unsigned shorts in the - order determined by BYTES_BIG_ENDIAN. */ - -#define B 0x10000 -#define low16 (B - 1) - -#ifdef BYTES_BIG_ENDIAN - -/* Note that HIGH and LOW do not describe the order - of words in a long long. They describe the order of words - in vectors ordered according to the byte order. */ - -#define HIGH 0 -#define LOW 1 - -#define big_end(n) 0 -#define little_end(n) ((n) - 1) -#define next_msd(i) ((i) - 1) -#define next_lsd(i) ((i) + 1) -#define is_not_msd(i,n) ((i) >= 0) -#define is_not_lsd(i,n) ((i) < (n)) - -#else - -#define LOW 0 -#define HIGH 1 - -#define big_end(n) ((n) - 1) -#define little_end(n) 0 -#define next_msd(i) ((i) + 1) -#define next_lsd(i) ((i) - 1) -#define is_not_msd(i,n) ((i) < (n)) -#define is_not_lsd(i,n) ((i) >= 0) - -#endif diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/lshldi3.c b/gnu/usr.bin/gcc1/gnulib/longlong/lshldi3.c deleted file mode 100644 index 7f9efa3757..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/lshldi3.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "longlong.h" - -long long -__lshldi3 (u, b1) - long long u; - long long b1; -{ - long_long w; - unsigned long carries; - int bm; - long_long uu; - int b = b1; - - if (b == 0) - return u; - - uu.ll = u; - - bm = (sizeof (int) * BITS_PER_UNIT) - b; - if (bm <= 0) - { - w.s.low = 0; - w.s.high = (unsigned long)uu.s.low << -bm; - } - else - { - carries = (unsigned long)uu.s.low >> bm; - w.s.low = (unsigned long)uu.s.low << b; - w.s.high = ((unsigned long)uu.s.high << b) | carries; - } - - return w.ll; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/lshrdi3.c b/gnu/usr.bin/gcc1/gnulib/longlong/lshrdi3.c deleted file mode 100644 index 95e7a63232..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/lshrdi3.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "longlong.h" - -long long -__lshrdi3 (u, b1) - long long u; - long long b1; -{ - long_long w; - unsigned long carries; - int bm; - long_long uu; - int b = b1; - - if (b == 0) - return u; - - uu.ll = u; - - bm = (sizeof (int) * BITS_PER_UNIT) - b; - if (bm <= 0) - { - w.s.high = 0; - w.s.low = (unsigned long)uu.s.high >> -bm; - } - else - { - carries = (unsigned long)uu.s.high << bm; - w.s.high = (unsigned long)uu.s.high >> b; - w.s.low = ((unsigned long)uu.s.low >> b) | carries; - } - - return w.ll; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/moddi3.c b/gnu/usr.bin/gcc1/gnulib/longlong/moddi3.c deleted file mode 100644 index f7288bd5d7..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/moddi3.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "longlong.h" - -long long -__moddi3 (u, v) - long long u, v; -{ - if (u < 0) - if (v < 0) - return - ((unsigned long long) -u % (unsigned long long) -v); - else - return - ((unsigned long long) -u % (unsigned long long) v); - else - if (v < 0) - return (unsigned long long) u % (unsigned long long) -v; - else - return (unsigned long long) u % (unsigned long long) v; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/muldi3.c b/gnu/usr.bin/gcc1/gnulib/longlong/muldi3.c deleted file mode 100644 index 0c7dea8cf3..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/muldi3.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "longlong.h" - -static void bmul (); - -long long -__muldi3 (u, v) - long long u, v; -{ - long a[2], b[2], c[2][2]; - long_long w; - long_long uu, vv; - - uu.ll = u; - vv.ll = v; - - a[HIGH] = uu.s.high; - a[LOW] = uu.s.low; - b[HIGH] = vv.s.high; - b[LOW] = vv.s.low; - - bmul (a, b, c, sizeof a, sizeof b); - - w.s.high = c[LOW][HIGH]; - w.s.low = c[LOW][LOW]; - return w.ll; -} - -static void -bmul (a, b, c, m, n) - unsigned short *a, *b, *c; - size_t m, n; -{ - int i, j; - unsigned long acc; - - bzero (c, m + n); - - m /= sizeof *a; - n /= sizeof *b; - - for (j = little_end (n); is_not_msd (j, n); j = next_msd (j)) - { - unsigned short *c1 = c + j + little_end (2); - acc = 0; - for (i = little_end (m); is_not_msd (i, m); i = next_msd (i)) - { - /* Widen before arithmetic to avoid loss of high bits. */ - acc += (unsigned long) a[i] * b[j] + c1[i]; - c1[i] = acc & low16; - acc = acc >> 16; - } - c1[i] = acc; - } -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/negdi2.c b/gnu/usr.bin/gcc1/gnulib/longlong/negdi2.c deleted file mode 100644 index 068392a731..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/negdi2.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "longlong.h" - -static int bneg (); - -long long -__negdi2 (u) - long long u; -{ - unsigned long a[2], b[2]; - long_long w; - long_long uu; - - uu.ll = u; - - a[HIGH] = uu.s.high; - a[LOW] = uu.s.low; - - bneg (a, b, sizeof b); - - w.s.high = b[HIGH]; - w.s.low = b[LOW]; - return w.ll; -} - -static int -bneg (a, b, n) - unsigned short *a, *b; - size_t n; -{ - signed long acc; - int i; - - n /= sizeof (short); - - acc = 0; - for (i = little_end (n); is_not_msd (i, n); i = next_msd (i)) - { - acc -= a[i]; - b[i] = acc & low16; - acc = acc >> 16; - } - return acc; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/one_cmpldi2.c b/gnu/usr.bin/gcc1/gnulib/longlong/one_cmpldi2.c deleted file mode 100644 index 04e484e85c..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/one_cmpldi2.c +++ /dev/null @@ -1,16 +0,0 @@ -#include "longlong.h" - -long long -__one_cmpldi2 (u) - long long u; -{ - long_long w; - long_long uu; - - uu.ll = u; - - w.s.high = ~uu.s.high; - w.s.low = ~uu.s.low; - - return w.ll; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/subdi3.c b/gnu/usr.bin/gcc1/gnulib/longlong/subdi3.c deleted file mode 100644 index 655aedeaf5..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/subdi3.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "longlong.h" - -static int bsub (); - -long long -__subdi3 (u, v) - long long u, v; -{ - long a[2], b[2], c[2]; - long_long w; - long_long uu, vv; - - uu.ll = u; - vv.ll = v; - - a[HIGH] = uu.s.high; - a[LOW] = uu.s.low; - b[HIGH] = vv.s.high; - b[LOW] = vv.s.low; - - bsub (a, b, c, sizeof c); - - w.s.high = c[HIGH]; - w.s.low = c[LOW]; - return w.ll; -} - -static int -bsub (a, b, c, n) - unsigned short *a, *b, *c; - size_t n; -{ - signed long acc; - int i; - - n /= sizeof *c; - - acc = 0; - for (i = little_end (n); is_not_msd (i, n); i = next_msd (i)) - { - /* Widen before subtracting to avoid loss of high bits. */ - acc += (long) a[i] - b[i]; - c[i] = acc & low16; - acc = acc >> 16; - } - return acc; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/ucmpdi2.c b/gnu/usr.bin/gcc1/gnulib/longlong/ucmpdi2.c deleted file mode 100644 index c586e833b1..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/ucmpdi2.c +++ /dev/null @@ -1,20 +0,0 @@ -#include "longlong.h" - -SItype -__ucmpdi2 (a, b) - long long a, b; -{ - long_long au, bu; - - au.ll = a, bu.ll = b; - - if ((unsigned) au.s.high < (unsigned) bu.s.high) - return 0; - else if ((unsigned) au.s.high > (unsigned) bu.s.high) - return 2; - if ((unsigned) au.s.low < (unsigned) bu.s.low) - return 0; - else if ((unsigned) au.s.low > (unsigned) bu.s.low) - return 2; - return 1; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/udivdi3.c b/gnu/usr.bin/gcc1/gnulib/longlong/udivdi3.c deleted file mode 100644 index febebc687e..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/udivdi3.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "longlong.h" - -extern void __bdiv (); - -long long -__udivdi3 (u, v) - long long u, v; -{ - unsigned long a[2][2], b[2], q[2], r[2]; - long_long w; - long_long uu, vv; - - uu.ll = u; - vv.ll = v; - - a[HIGH][HIGH] = 0; - a[HIGH][LOW] = 0; - a[LOW][HIGH] = uu.s.high; - a[LOW][LOW] = uu.s.low; - b[HIGH] = vv.s.high; - b[LOW] = vv.s.low; - - __bdiv (a, b, q, r, sizeof a, sizeof b); - - w.s.high = q[HIGH]; - w.s.low = q[LOW]; - return w.ll; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/umoddi3.c b/gnu/usr.bin/gcc1/gnulib/longlong/umoddi3.c deleted file mode 100644 index 1d3fdd37be..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/umoddi3.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "longlong.h" - -extern void __bdiv (); - -long long -__umoddi3 (u, v) - long long u, v; -{ - unsigned long a[2][2], b[2], q[2], r[2]; - long_long w; - long_long uu, vv; - - uu.ll = u; - vv.ll = v; - - a[HIGH][HIGH] = 0; - a[HIGH][LOW] = 0; - a[LOW][HIGH] = uu.s.high; - a[LOW][LOW] = uu.s.low; - b[HIGH] = vv.s.high; - b[LOW] = vv.s.low; - - __bdiv (a, b, q, r, sizeof a, sizeof b); - - w.s.high = r[HIGH]; - w.s.low = r[LOW]; - return w.ll; -} diff --git a/gnu/usr.bin/gcc1/gnulib/longlong/xordi3.c b/gnu/usr.bin/gcc1/gnulib/longlong/xordi3.c deleted file mode 100644 index 3981ed6590..0000000000 --- a/gnu/usr.bin/gcc1/gnulib/longlong/xordi3.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "longlong.h" - -long long -__xordi3 (u, v) - long long u, v; -{ - long_long w; - long_long uu, vv; - - uu.ll = u; - vv.ll = v; - - w.s.high = uu.s.high ^ vv.s.high; - w.s.low = uu.s.low ^ vv.s.low; - - return w.ll; -} diff --git a/gnu/usr.bin/gcc1/cc1/COPYING b/gnu/usr.bin/gdb/COPYING similarity index 100% rename from gnu/usr.bin/gcc1/cc1/COPYING rename to gnu/usr.bin/gdb/COPYING diff --git a/gnu/usr.bin/gdb/ChangeLog b/gnu/usr.bin/gdb/ChangeLog new file mode 100644 index 0000000000..1f2342b797 --- /dev/null +++ b/gnu/usr.bin/gdb/ChangeLog @@ -0,0 +1,4887 @@ +Thu Feb 8 01:11:55 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * GDB 3.5 released. + + * version.c: Change version number to 3.5 + +Tue Feb 6 15:58:06 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * m-hp9k320.h: define ATTACH_DETACH. + hp9k320-dep.c [ATTACH_DETACH]: New code. + +Thu Feb 1 17:43:00 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * valprint.c (is_nan, val_print): Use char * not void *. + + * symmisc.c (print_symbol): Print newline after label. + +Tue Jan 30 15:35:52 1990 Jim Kingdon (kingdon at albert.ai.mit.edu) + + * Makefile.dist (READLINE): Add {readline,history}.texinfo. + + * m-merlin.h: Put in clarifying comments about SHELL_FILE. + config.gdb (merlin): Explain about /usr/local/lib/gdb-sh. + +Sat Jan 27 02:30:27 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * version.c: Change version number to 3.5alpha.1. + + * dbxread.c (process_one_symbol): Compare context_stack_depth + with !VARIABLES_INSIDE_BLOCK, not VARIABLES_INSIDE_BLOCK. + +Fri Jan 26 01:21:51 1990 Jim Kingdon (kingdon at mole.ai.mit.edu) + + * main.c [ALIGN_STACK_ON_STARTUP]: New code. + m-i386.h: Define ALIGN_STACK_ON_STARTUP. + + * m-merlin.h (NO_SIGINTERRUPT, SHELL_FILE): Define. + + * umax-dep.c (exec_file_command): Add commas to call to + read_section_hdr. + +Tue Jan 23 15:49:47 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * dbxread.c (define_symbol): Deal with deftype 'X'. + + * convex-dep.c (wait): Make it pid_t. + + * convex-dep.c (comm_registers_info): accept decimal comm register + specification, as "i comm 32768". + + * dbxread.c (process_one_symbol): Make VARIABLES_INSIDE_BLOCK + macro say by itself where variables are. Pass it desc. + m-convex.h (VARIABLES_INSIDE_BLOCK): Nonzero for native compiler. + + * m-convex.h (SET_STACK_LIMIT_HUGE): Define. + (IGNORE_SYMBOL): Take out #ifdef N_MONPT and put in 0xc4. + +Fri Jan 19 20:04:15 1990 Jim Kingdon (kingdon at albert.ai.mit.edu) + + * printcmd.c (print_frame_args): Always set highest_offset to + current_offset when former is -1. + + * dbxread.c (read_struct_type): Print nice error message + when encountering multiple inheritance. + +Thu Jan 18 13:43:30 1990 Jim Kingdon (kingdon at mole.ai.mit.edu) + + * dbxread.c (read_dbx_symtab): Always treat N_FN as a potential + source for a x.o or -lx symbol, ignoring OFILE_FN_FLAGGED. + + * printcmd.c (print_frame_args): Cast -1 to (CORE_ADDR). + + * hp300bsd-dep.c (_initialize_hp300_dep): Get kernel_u_addr. + m-hp300bsd.h (KERNEL_U_ADDR): Use kernel_u_addr. + + * infcmd.c (run_command): #if 0 out call to + breakpoint_clear_ignore_counts. + +Thu Jan 11 12:58:12 1990 Jim Kingdon (kingdon at mole) + + * printcmd.c (print_frame_args) [STRUCT_ARG_SYM_GARBAGE]: + Try looking up name of var before giving up & printing '?'. + +Wed Jan 10 14:00:14 1990 Jim Kingdon (kingdon at pogo) + + * many files: Move stdio.h before param.h. + + * sun3-dep.c (store_inferior_registers): Only try to write FP + regs #ifdef FP0_REGNUM. + +Mon Jan 8 17:56:15 1990 Jim Kingdon (kingdon at pogo) + + * symtab.c: #if 0 out "info methods" code. + +Sat Jan 6 12:33:04 1990 Jim Kingdon (kingdon at pogo) + + * dbxread.c (read_struct_type): Set TYPE_NFN_FIELDS_TOTAL + from all baseclasses; remove vestigial variable baseclass. + + * findvar.c (read_var_value): Check REG_STRUCT_HAS_ADDR. + printcmd.c (print_frame_args): Check STRUCT_ARG_SYM_GARBAGE. + m-sparc.h: Define REG_STRUCT_HAS_ADDR and STRUCT_ARG_SYM_GARBAGE. + + * blockframe.c (get_frame_block): Subtract one from pc if not + innermost frame. + +Fri Dec 29 15:26:33 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * printcmd.c (print_frame_args): check highest_offset != -1, not i. + +Thu Dec 28 16:21:02 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * valops.c (value_struct_elt): Clean up error msg. + + * breakpoint.c (describe_other_breakpoints): + Delete extra space before "also set at" and add period at end. + +Tue Dec 19 10:28:42 1989 Jim Kingdon (kingdon at pogo) + + * source.c (print_source_lines): Tell user which line number + was out of range when printing error message. + +Sun Dec 17 14:14:09 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * blockframe.c (find_pc_partial_function): Use + BLOCK_START (SYMBOL_BLOCK_VALUE (f)) instead of + SYMBOL_VALUE (f) to get start of function. + + * dbxread.c: Make xxmalloc just a #define for xmalloc. + +Thu Dec 14 16:13:16 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * m68k-opcode.h (fseq & following fp instructions): + Change @ to $. + +Fri Dec 8 19:06:44 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * breakpoint.c (breakpoint_clear_ignore_counts): New function. + infcmd.c (run_command): Call it. + +Wed Dec 6 15:03:38 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * valprint.c: Change it so "array-max 0" means there is + no limit. + + * expread.y (yylex): Change error message "invalid token in + expression" to "invalid character '%c' in expression". + +Mon Dec 4 16:12:54 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * blockframe.c (find_pc_partial_function): Always return 1 + for success, 0 for failure, and set *NAME and *ADDRESS to + match the return value. + + * dbxread.c (symbol_file_command): Use perror_with_name on + error from stat. + (psymtab_to_symtab, add_file_command), + core.c (validate_files), source.c (find_source_lines), + default-dep.c (exec_file_command): Check for errors from stat, + fstat, and myread. + +Fri Dec 1 05:16:42 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * valops.c (check_field): When following pointers, just get + their types; don't call value_ind. + +Thu Nov 30 14:45:29 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * config.gdb (pyr): New machine. + core.c [REG_STACK_SEGMENT]: New code. + dbxread.c (process_one_symbol): Cast return from copy_pending + to long before casting to enum namespace. + infrun.c: Split registers_info into DO_REGISTERS_INFO + and registers_info. + m-pyr.h, pyr-{dep.c,opcode.h,pinsn.c}: New files. + + * hp300bsd-dep.c: Stay in sync with default-dep.c. + + * m-hp300bsd.h (IN_SIGTRAMP): Define. + +Mon Nov 27 23:48:21 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * m-sparc.h (EXTRACT_RETURN_VALUE, STORE_RETURN_VALUE): + Return floating point values in %f0. + +Tue Nov 21 00:34:46 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * dbxread.c (read_type): #if 0 out code which skips to + comma following x-ref. + +Sat Nov 18 20:10:54 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * valprint.c (val_print): Undo changes of Nov 11 & 16. + (print_string): Add parameter force_ellipses. + (val_print): Pass force_ellipses true when we stop fetching string + before we get to the end, else pass false. + +Thu Nov 16 11:59:50 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * infrun.c (restore_inferior_status): Don't try to restore + selected frame if the inferior no longer exists. + + * valprint.c (val_print): Rewrite string printing code not to + call print_string. + + * Makefile.dist (clean): Remove xgdb and xgdb.o. + +Tue Nov 14 12:41:47 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * Makefile.dist (XGDB, bindir, xbindir, install, all): New stuff. + +Sat Nov 11 15:29:38 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * valprint.c (val_print): chars_to_get: New variable. + +Thu Nov 9 12:31:47 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * main.c (main): Process "-help" as a switch that doesn't + take an argument. + +Wed Nov 8 13:07:02 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * Makefile.dist (gdb.tar.Z): Add "else true". + +Tue Nov 7 12:25:14 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * infrun.c (restore_inferior_status): Don't dereference fid if NULL. + + * config.gdb (sun3, sun4): Accept "sun3" and "sun4". + +Mon Nov 6 09:49:23 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * Makefile.dist (Makefile): Move comments after commands. + + * *-dep.c [READ_COFF_SYMTAB]: Pass optional header size to + read_section_hdr(). + + * inflow.c: Include regardless of USG. + + * coffread.c (read_section_hdr): Add optional_header_size. + (symbol_file_command): Pass optional header size to + read_section_hdr(). + (read_coff_symtab): Initialize filestring. + + * version.c: Change version to 3.4.xxx. + + * GDB 3.4 released. + +Sun Nov 5 11:39:01 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * version.c: Change version to 3.4. + + * symtab.c (decode_line_1): Only skip past "struct" if it + is there. + + * valops.c (value_ind), eval.c (evaluate_subexp, case UNOP_IND): + Have "*" return an int, not a LONGEST. + + * utils.c (fprintf_filtered): Pass arg{4,5,6} to sprintf. + + * printcmd.c (x_command): Use variable itself rather + than treating it as a pointer only if it is a function. + (See comment "this makes x/i main work"). + + * coffread.c (symbol_file_command): Use error for + "%s does not have a symbol-table.\n". + +Wed Nov 1 19:56:18 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * dbxread.c [BELIEVE_PCC_PROMOTION_TYPE]: New code. + m-sparc.h: Define BELIEVE_PCC_PROMOTION_TYPE. + +Thu Oct 26 12:45:00 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * infrun.c: Include . + + * dbxread.c (read_dbx_symtab, case N_LSYM, case 'T'): + Check for enum types and put constants in psymtab. + +Mon Oct 23 15:02:25 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * dbxread.c (define_symbol, read_dbx_symtab): Handle enum + constants (e.g. "b:c=e6,0"). + +Thu Oct 19 14:57:26 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * stack.c (frame_info): Use FRAME_ARGS_ADDRESS_CORRECT + m-vax.h (FRAME_ARGS_ADDRESS_CORRECT): New macro. + (FRAME_ARGS_ADDRESS): Restore old meaning. + + * frame.h (Frame_unknown): New macro. + stack.c (frame_info): Check for Frame_unknown return from + FRAME_ARGS_ADDRESS. + m-vax.h (FRAME_ARGS_ADDRESS): Sometimes return Frame_unknown. + + * utils.c (fatal_dump_core): Add "internal error" to message. + + * infrun.c (IN_SIGTRAMP): New macro. + (wait_for_inferior): Use IN_SIGTRAMP. + m-vax.h (IN_SIGTRAMP): New macro. + +Wed Oct 18 15:09:22 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * config.gdb, Makefile.dist: Shorten m-i386-sv32.h. + + * coffread.c (symbol_file_command): Pass 0 to select_source_symtab. + +Tue Oct 17 12:24:41 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * i386-dep.c (i386_frame_num_args): Take function from m-i386.h + file. Check for pfi null. + m-i386.h (FRAME_NUM_ARGS): Use i386_frame_num_args. + + * infrun.c (wait_for_inferior): set stop_func_name to 0 + before calling find_pc_partial_function. + +Thu Oct 12 01:08:50 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * breakpoint.c (_initialize_breakpoint): Add "disa". + + * Makefile.dist: Add GLOBAL_CFLAGS and pass to readline. + + * config.gdb (various): "$machine =" -> "machine =". + +Wed Oct 11 11:54:31 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * inflow.c (try_writing_regs): #if 0 out this function. + + * main.c (main): Add "-help" option. + + * dbxread.c (read_dbx_symtab): Merge code for N_FUN with + N_STSYM, etc. + +Mon Oct 9 14:21:55 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * inflow.c (try_writing_regs_command): Don't write past end + of struct user. + + * dbxread.c (read_struct_type): #if 0 out code which checks for + bitpos and bitsize 0. + + * config.gdb: Accept sequent-i386 (not seq386). + (symmetry): Set depfile and paramfile. + + * m-convex.h (IGNORE_SYMBOL): Check for N_MONPT if defined. + +Thu Oct 5 10:14:26 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * default-dep.c (read_inferior_memory): Put #if 0'd out comment + within /* */. + +Wed Oct 4 18:44:41 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * config.gdb: Change /dev/null to m-i386.h for various + 386 machine "opcodefile" entries. + + * config.gdb: Accept seq386 for sequent symmetry. + +Mon Oct 2 09:59:50 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * hp300bsd-dep.c: Fix copyright notice. + +Sun Oct 1 16:25:30 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * Makefile.dist (DEPFILES): Add isi-dep.c. + + * default-dep.c (read_inferior_memory): Move #endif after else. + +Sat Sep 30 12:50:16 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * version.c: Change version number to 3.3.xxx. + + * GDB 3.3 released. + + * version.c: Change version number to 3.3. + + * Makefile.dist (READLINE): Add vi_mode.c + + * config.gdb (i386): Change /dev/null to m-i386.h + + * config.gdb: Add ';;' before 'esac'. + + * Makefile.dist (gdb.tar.Z): Move comment above dependency. + + * dbxread.c (read_ofile_symtab): Check symbol before start + of source file for GCC_COMPILED_FLAG_SYMBOL. + (start_symtab): Don't clear processing_gcc_compilation. + +Thu Sep 28 22:30:23 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * valprint.c (print_string): If LENGTH is zero, print "". + +Wed Sep 27 10:15:10 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * config.gdb: "rm tmp.c" -> "rm -f tmp.c". + +Tue Sep 26 13:02:10 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * utils.c (_initialize_utils): Use termcap to set lines_per_page + and chars_per_line. + +Mon Sep 25 10:06:43 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * dbxread.c (read_dbx_symtab, N_SOL): Do not add the same file + more than once. + +Thu Sep 21 12:43:18 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * infcmd.c (unset_environment_command): Delete all variables + if called with no arg. + + * remote.c, inferior.h (remote_{read,write}_inferior_memory): + New functions. + core.c ({read,write}_memory): Use remote_{read,write}_inferior_memory. + + * valops.c (call_function): When reserving stack space for + arguments, call value_arg_coerce. + + * m-hp9k320.h: define BROKEN_LARGE_ALLOCA. + + * breakpoint.c (delete_command): Ask for confirmation only + when there are breakpoints. + + * dbxread.c (read_struct_type): If lookup_basetype_type has + copied a stub type, call add_undefined_type. + + * sparc_pinsn.c (compare_opcodes): Check for "1+i" anywhere + in args. + + * val_print.c (type_print_base): Print stub types as + "". + +Wed Sep 20 07:32:00 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * sparc-opcode.h (swapa): Remove i bit from match. + (all alternate space instructions): Delete surplus "foo rs1+0" + patterns. + + * Makefile.dist (LDFLAGS): Set to $(CFLAGS). + + * remote-multi.shar (remote_utils.c, putpkt): Change csum to unsigned. + +Tue Sep 19 14:15:16 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * sparc-opcode.h: Set i bit in lose for many instructions which + aren't immediate. + + * stack.c (print_frame_info): add "func = 0". + +Mon Sep 18 16:19:48 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * sparc-opcode.h (mov): Add mov to/from %tbr, %psr, %wim. + + * sparc-opcode.h (rett): Fix notation to use suggested assembler + syntax from architecture manual. + + * symmetry-dep.c (I386_REGNO_TO_SYMMETRY): New macro. + (i386_frame_find_saved_regs): Use I386_REGNO_TO_SYMMETRY. + +Sat Sep 16 22:21:17 1989 Jim Kingdon (kingdon at spiff) + + * remote.c (remote_close): Set remote_desc to -1. + + * gdb.texinfo (Output): Fix description of echo to match + reality and ANSI C. + +Fri Sep 15 14:28:59 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * symtab.c (lookup_symbol): Add comment about "asm". + + * sparc-pinsn.c: Use NUMOPCODES. + + * sparc-opcode.h (NUMOPCODES): Use sparc_opcodes[0] not *sparc_opcodes. + +Thu Sep 14 15:25:20 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * dbxread.c (xxmalloc): Print error message before calling abort(). + + * infrun.c (wait_for_inferior): Check for {stop,prev}_func_name + null before passing to strcmp. + +Wed Sep 13 12:34:15 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * sparc-opcode.h: New field delayed. + sparc-pinsn.c (is_delayed_branch): New function. + (print_insn): Check for delayed branches. + + * stack.c (print_frame_info): Use misc_function_vector in + case where ar truncates file names. + +Tue Sep 12 00:16:14 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * convex-dep.c (psw_info): Move "struct pswbit *p" with declarations. + +Mon Sep 11 14:59:57 1989 Jim Kingdon (kingdon at spiff) + + * convex-dep.c (core_file_command): Delete redundant printing + of "Program %s". + + * m-convex.h (ENTRY_POINT): New macro. + + * m-convex.h (FRAME_CHAIN_VALID): Change outside_first_object_file + to outside_startup_file + + * main.c: #if 0 out catch_termination and related code. + + * command.c (lookup_cmd_1): Consider underscores part of + command names. + +Sun Sep 10 09:20:12 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * printcmd.c: Change asdump_command to disassemble_command + (_initialize_printcmd): Change asdump to diassemble. + + * main.c (main): Exit with code 0 if we hit the end of a batch + file. + + * Makefile.dist (libreadline.a): Fix syntax of "CC=${CC}". + +Sat Sep 9 01:07:18 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * values.c (history_info): Renamed to value_history_info. + Command renamed to "info value" (with "info history" still + accepted). + + * sparc-pinsn.c (print_insn): Extend symbolic address printing + to cover "sethi" following by an insn which uses 1+i. + +Fri Sep 8 14:24:01 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * m-hp9k320.h, m-hp300bsd.h, m-altos.h, m-sparc.h, m-sun3.h + (READ_GDB_SYMSEGS): Remove. + dbxread.c [READ_GDB_SYMSEGS]: Remove code to read symsegs. + + * sparc-pinsn.c (print_insn): Detect "sethi-or" pairs and + print symbolic address. + + * sparc-opcode.h (sethi, set): Change lose from 0xc0000000 to + 0xc0c00000000. + + * remote.c (remote_desc): Initialize to -1. + + * Makefile.dist (libreadline.a): Pass CC='${CC}' to readline makefile. + +Thu Sep 7 00:07:17 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * dbxread.c (read_struct_type): Check for static member functions. + values.c, eval.c, valarith.c, valprint.c, valops.c: Merge changes + from Tiemann for static member functions. + + * sparc-opcode.h (tst): Fix all 3 patterns. + + * Makefile.dist (gdb1): New rule. + + * sparc-opcode.h: Change comment about what the disassembler + does with the order of the opcodes. + + * sparc-pinsn.c (compare_opcodes): Put 1+i before i+1. + Also fix mistaken comment about preserving order of original table. + + * sparc-opcode.h (clr, mov): Fix incorrect lose entries. + + * m-symmetry.h (FRAME_NUM_ARGS): Add check to deal with code that + GCC sometimes generates. + + * config.gdb: Change all occurances of "skip" to "/dev/null". + + * README (about languages other than C): Update comments about + Pascal and FORTRAN. + + * sparc-opcode.h (nop): Change lose from 0xae3fffff to 0xfe3fffff. + + * values.c (value_virtual_fn_field): #if 0-out assignment to + VALUE_TYPE(vtbl). + +Wed Sep 6 12:19:22 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * utils.c (fatal_dump_core): New function. + Makefile.dist (MALLOC_FLAGS): use -Dbotch=fatal_dump_core + +Tue Sep 5 15:47:18 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * breakpoint.c (enable_command): With no arg, enable all bkpts. + + * Makefile.dist (Makefile): Remove \"'s around $(MD). + + * Makefile.dist: In "cd readline; make . . ." change first + SYSV_DEFINE to SYSV. + + * m68k-pinsn.c (_initialize_pinsn): Use alternate assembler + syntax #ifdef HPUX_ASM + +Sat Sep 2 23:24:43 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * values.c (history_info): Don't check num_exp[0] if num_exp + is nil (just like recent editing_info change). + +Fri Sep 1 19:19:01 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * gdb.texinfo (inc-history, inc-readline): Copy in the inc-* files + because people might not have makeinfo. + + * README (xgdb): Strengthen nasty comments. + + * gdb.texinfo: Change @setfilename to "gdb.info". + +Thu Aug 31 17:23:50 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * main.c (editing_info): Don't check arg[0] if arg is null. + + * m-vax.h: Add comment about known sigtramp bug. + + * sun3-dep.c, sparc-dep.c (IS_OBJECT_FILE, exec_file_command): + Get right text & data addresses for .o files. + +Wed Aug 30 13:54:19 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * utils.c (tilde_expand): Remove function (it's in readline). + + * sparc-opcode.h (call): Change "8" to "9" in first two + patterns (%g7->%o7). + +Tue Aug 29 16:44:41 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * printcmd.c (whatis_command): Change 4th arg to type_print + from 1 to -1. + +Mon Aug 28 12:22:41 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * dbxread.c (psymtab_to_symtab_1): In "and %s ..." change + pst->filename to pst->dependencies[i]->filename. + + * blockframe.c (FRAMELESS_LOOK_FOR_PROLOGUE): New macro + made from FRAMELESS_FUNCTION_INVOCATION from m-sun3.h except + that it checks for zero return from get_pc_function_start. + m-hp9k320.h, m-hp300bsd.h, m-i386.h, m-isi.h, m-altos.h, + m-news.h, m-sparc.h, m-sun2.h, m-sun3.h, m-symmetry.h + (FRAMELESS_FUNCTION_INVOCATION): Use FRAMELESS_LOOK_FOR_PROLOGUE. + + * dbxread.c (read_struct_type): Give warning and ignore field + if bitpos and bitsize are zero. + +Sun Aug 27 04:55:20 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * dbxread.c (psymtab_to_symtab{,_1}): Print message about + reading in symbols before reading stringtab, not after. + +Sat Aug 26 02:01:53 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * dbxread.c (IS_OBJECT_FILE, ADDR_OF_TEXT_SEGMENT): New macros. + (read_dbx_symtab): Use text_addr & text_size to set end_of_text_addr. + (symbol_file_command): pass text_addr & text_size to read_dbx_symtab. + +Fri Aug 25 23:08:13 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * valprint.c (value_print): Try to give the name of function + pointed to when printing a function pointer. + +Thu Aug 24 23:18:40 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * core.c (xfer_core_file): In cases where MEMADDR is above the + largest address that makes sense, set i to len. + +Thu Aug 24 16:04:17 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * valprint.c (print_string): New function to print a character + string, doing array-max limiting and repeat count processing. + (val_print, value_print): Use print_string. + (REPEAT_COUNT_THRESHOLD): New #define, the max number of elts to print + without using a repeat count. Set to ten. + (value_print, val_print): Use REPEAT_COUNT_THRESHOLD. + + * utils.c (printchar): Use {fputs,fprintf}_filtered. + + * valprint.c (val_print): Pass the repeat count arg to the + fprintf_filtered call for "" messages. + +Wed Aug 23 22:53:47 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * utils.c: Include . + + * main.c: Declare free. + +Wed Aug 23 05:05:59 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * utils.c, defs.h: Add tilde_expand. + source.c (directory_command), + main.c (cd_command), + main.c (set_history_filename), + dbxread.c (symbol_file_command), + coffread.c (symbol_file_command), + dbxread.c (add_file_command), + symmisc.c (print_symtabs), + *-dep.c (exec_file_command, core_file_command), + main.c (source_command): Use tilde_expand. + + * dbxread.c (read_type): When we get a cross-reference, resolve + it immediately if possible, only calling add_undefined_type if + necessary. + + * gdb.texinfo: Uncomment @includes and put comment at start + of file telling people to use makeinfo. + + * valprint.c (type_print_base): Print the right thing for + bitfields. + + * config.gdb (sun3os3): Set paramfile and depfile. + +Tue Aug 22 05:38:36 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * dbxread.c (symbol_file_command): Pass string table size to + read_dbx_symtab(). + (read_dbx_symtab): Before indexing into string table, check + string table index for reasonableness. + (psymtab_to_symtab{,_1}, read_ofile_symtab): Same. + +Tue Aug 22 04:04:39 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * m68k-pinsn.c: Replaced many calls to fprintf and fputs with + calls to fprintf_filtered and fputs_filtered. + (print_insn_arg): Use normal MIT 68k syntax for postincrement, + predecrement, and register indirect addressing modes. + +Mon Aug 21 10:08:02 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * main.c (initialize_signals): Set signal handler for SIGQUIT + and SIGHUP to do_nothing. + + * ns32k-opcode.h (ord): Change 1D1D to 1D2D. + + * ns32k-pinsn.c (print_insn_arg, print_insn): Handle index + bytes correctly. + + * ns32k-opcode.h: Add comments. + + * dbxread.c (read_type): Put enum fields in type.fields in order + that they were found in the debugging symbols (not reverse order). + +Sun Aug 20 21:17:13 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * main.c (source_command): Read .gdbinit if run without argument. + + * source.c (directory_command): Only print "foo already in path" + if from_tty. + + * version.c: Change version number to 3.2.xxx + +Sat Aug 19 00:24:08 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * m-news.h: Define HAVE_WAIT_STRUCT. + + * m-isi.h, isi-dep.c: Replace with new version from Adam de Boor. + config.gdb: Remove isibsd43. + + * main.c (catch_termination): Don't say we have written + .gdb_history until after we really have. + + * convex-dep.c (attach): Add "sleep (1)". + (write_vector_register): Use "LL" with long long constant. + (wait): Close comment. + (wait): Change "unix 7.1 bug" to "unix 7.1 feature" & related + changes in comment. + (scan_stack): And fp with 0x80000000 in while loop test. + (core_file_command): Move code to set COREFILE. + (many places): Change printf to printf_filtered. + (psw_info): Allow argument giving value to print as a psw. + (_initialize_convex_dep): Update docstrings. + + * m-convex.h (WORDS_BIG_ENDIAN): Correct typo ("WRODS") + define NO_SIGINTERRUPT. + define SET_STACK_LIMIT_HUGE. + add "undef BUILTIN_TYPE_LONGEST" before defining it. + Use "LL" after constants in CALL_DUMMY. + + * dbxread.c: In the 3 places it says error "ridiculous string + table size"... delete extra parameter to error. + + * dbxread.c (scan_file_globals): Check for FORTRAN common block. + Allow multiple references for the sake of common blocks. + + * main.c (initialize_main): Set history_filename to include + current directory. + + * valprint.c (decode_format): Don't return a defaulted size + field if osize is zero. + + * gdb.texinfo (Compilation): Update information on -gg symbols. + Document problem with ar. + +Fri Aug 18 19:45:20 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * valprint.c (val_print, value_print): Add "" code. + Also put "..." outside quotes for strings. + + * main.c (initialize_main): Add comment about history output file + being different from history input file. + + * m-newsos3.h: Undefine NO_SIGINTERRUPT. Rearrange a few comments. + + * m-newsos3.h (REGISTER_U_ADDR): Use new version from Hikichi. + + * sparc-opcode.h: Add comment clarifying meaning of the order of + the entries in sparc_opcodes. + + * eval.c (evaluate_subexp, case UNOP_IND): Deal with deferencing + things that are not pointers. + + * valops.c (value_ind): Make dereferencing an int give a LONGEST. + + * expprint.c (print_subexp): Add (int) cast in OP_LAST case. + + * dbxread.c (read_array_type): Set lower and upper if adjustable. + + * symtab.c (lookup_symbol): Don't abort if symbol found in psymtab + but not in symtab. + +Thu Aug 17 15:51:20 1989 Randy Smith (randy at hobbes.ai.mit.edu) + + * config.gdb: Changed "Makefile.c" to "Makefile.dist". + +Thu Aug 17 01:58:04 1989 Roland McGrath (roland at apple-gunkies.ai.mit.edu) + + * sparc-opcode.h (or): Removed incorrect lose bit 0x08000000. + [many]: Changed many `lose' entries to have the 0x10 bit set, so + they don't think %l0 is %g0. + +Wed Aug 16 00:30:44 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * m-symmetry.h (STORE_STRUCT_RETURN): Also write reg 0. + (EXTRACT_RETURN_VALUE): Call symmetry_extract_return_value. + symmetry-dep.c (symmetry_extract_return_value): New fn. + + * main.c (symbol_completion_function): Deal with changed + result_list from lookup_cmd_1 for ambiguous return. + command.c (lookup_cmd): Same. + + * inflow.c [TIOCGETC]: Move #include "param.h" back before + system #includes. Change all #ifdef TIOCGETC to + #if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN) + m-i386-sysv3.2.h, m-i386gas-sysv3.2.h: Remove "#undef TIOCGETC" + and add "#define TIOCGETC_BROKEN". + + * command.c (lookup_cmd_1): Give the correct result_list in the + case of an ambiguous return where there is a partial match + (e.g. "info a"). Add comment clarifying what is the correct + result_list. + + * gdb.texinfo (GDB History): Document the two changes below. + + * main.c (command_line_input): Make history expansion not + just occur at the beginning of a line. + + * main.c (initialize_main): Make history expansion off by default. + + * inflow.c: Move #include "param.h" after system #includes. + + * i386-dep.c (i386_float_info): Use U_FPSTATE macro. + + * m-i386-sysv3.2.h, m-i386gas-sysv3.2.h: New files. + Makefile.dist, config.gdb: Know about these new files. + +Tue Aug 15 21:36:11 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * symtab.c (lookup_struct_elt_type): Use type_print rather + than assuming type has a name. + +Tue Aug 15 02:25:43 1989 Roland McGrath (roland at apple-gunkies.ai.mit.edu) + + * sparc-opcode.h (mov): Removed bogus "or i,0,d" pattern. + + * sparc-opcode.h (mov, or): Fixed incorrect `lose' members. + + * sparc-dep.c: Don't include "sparc-opcode.h". + (skip_prologue, isanulled): Declare special types to recognize + instructions, and use them. + + * sparc-pinsn.c (print_insn): Sign-extend 13-bit immediate args. + If they are less than +9, print them in signed decimal instead + of unsigned hex. + + * sparc-opcode.h, sparc-pinsn.c: Completely rewritten to share an + opcode table with gas, and thus produce disassembly that looks + like what the assembler accepts. + +Tue Aug 15 16:20:52 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * symtab.c (find_pc_psymbol): Move best_pc=psymtab->textlow-1 + after test for psymtab null. + + * main.c (editing_info): Remove variable retval. + + * config.gdb (sun3, isi): Comment out obsolete message about telling + it whether you have an FPU (now that it detects it). + + * config.gdb (sun3): Accept sun3os3. + + * m68k-insn.h: Include . + + * m68k-pinsn.h (convert_{to,from}_68881): Add have_fpu code + + * m-newsos3.h: Undefine USE_PCB. That code didn't seem to work. + + * sparc-dep.c: Put in insn_fmt and other stuff from the old + sparc-opcode.h. + + * sparc-opcode.h, sparc-pinsn.c: Correct copyright notice. + + * sparc-opcode.h, sparc-pinsn.c: Replace the old ones with the new + ones by roland. + +Tue Aug 15 02:25:43 1989 Roland McGrath (roland at apple-gunkies.ai.mit.edu) + + * Makefile.dist: Don't define CC at all. + + * Makefile.dist (Makefile): Remove tmp.c after preprocessing. + Use $(MD) instead of M_MAKEDEFINE in the cc command. + + * Makefile.dist: Don't define RL_LIB as + "${READLINE}/libreadline.a", since READLINE is a list of files. + +Mon Aug 14 23:49:29 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * main.c (print_version): Change 1988 to 1989. + + * main.c (copying_info, initialize_main): Remove #if 0'd code. + +Tue Aug 1 14:44:56 1989 Hikichi (hikichi at sran203) + + * m-newsos3.h + (NO_SIGINTERRUPT): have SIGINTERRUPT on NEWS os 3. + + * m-news.h(FRAME_FIND_SAVED_REGS): use the sun3's instead of old + one. + +Mon Aug 14 15:27:01 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * m-news.h, m-newsos3.h, news-dep.c: Merge additional changes + by Hikichi (ChangeLog entries above). + + * Makefile.dist (READLINE): List readline files individually + so we don't accidently get random files from the readline + directory. + + * m-news.h (STORE_RETURN_VALUE, EXTRACT_RETURN_VALUE): + Expect floating point returns to be in fp0. + + * gdb.texinfo (Format options): New node. + + * gdb.texinfo: Comment out "@include"s until bfox fixes the + readline & history docs. + + * dbxread.c (read_addl_syms): Set startup_file_* if necessary at + the end (as well as when we hit ".o"). + + * printcmd.c (decode_format): Set val.format & val.size to '?' at + start and set defaults at end. + + * symtab.c (decode_line_1): Check for class_name null. + + * valops.c: Each place where it compares against field names, + check for null field names. (new t_field_name variables). + + * utils.c (fputs_filtered): Check for linebuffer null before + checking whether to call fputs. Remove later check for linebuffer + null. + +Sun Aug 13 15:56:50 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * m-isi.h, m-sun3.h ({PUSH,POP}_FP_REGS): New macros. + m-sun3.h (NUM_REGS): Conditionalize on FPU. + config.gdb (sun3, isi): Add message about support for machines + without FPU. + + * main.c (catch_termination, initialize_signals): new functions. + + * main.c (editing_info): Add "info editing n" and "info editing +". + Rewrite much of this function. + gdb.texinfo (GDB Readline): Document it. + + * values.c (history_info): Add "info history +". Also add code to + do "info history +" when command is repeated. + gdb.texinfo (Value History): Document "info history +". + + * expprint.c (print_subexp): Add OP_THIS to case stmt. + + * config.gdb (sun4os4): Put quotes around make define. + + * config.gdb: Canonicalize machine name at beginning. + +Sat Aug 12 00:50:59 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * config.gdb: define M_MAKEDEFINE + Makefile (Makefile, MD): Be able to re-make Makefile. + + * main.c (command_line_input): Add comments to + the command history. + + * Makefile.dist (Makefile): Add /bin/false. + +Fri Aug 11 14:35:33 1989 Jim Kingdon (kingdon at spiff) + + * Makefile.dist: Comment out .c.o rule and add TARGET_ARCH. + + * m-altos.h: Include sys/page.h & sys/net.h + + * m-altos.h (FRAME_CHAIN{,_VALID}): Use outside_startup_file. + + * config.gdb (altos, altosgas): Add M_SYSV & M_BSD_NM and remove + M_ALLOCA=alloca.o from makedefine. + + * coffread.c (complete_symtab): Change a_entry to entry. + + * m-altosgas.h: New file. + + * m-symmetry (REGISTER_BYTE): Fix dumb mistake. + +Fri Aug 11 06:39:49 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * utils.c (set_screensize_command): Check for ARG being nil, since + that's what execute_command will pass if there's no argument. + + * expread.y (yylex): Recognize "0x" or "0X" as the beginning of a + number. + +Thu Aug 10 15:43:12 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * config.gdb, Makefile.dist: Rename Makefile.c to Makefile.dist. + + * m-altos.h: Add comment about porting to USGR2. + + * config.gdb (sparc): Add -Usparc. + +Wed Aug 9 14:20:39 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * m-sun3os4.h: Define BROKEN_LARGE_ALLOCA. + + * values.c (modify_field): Check for value too large to fit in + bitfield. + + * utils.c (fputs_filtered): Allow LINEBUFFER to be NULL. + + * breakpoint.c (condition_command): Check for attempt to specify + non-numeric breakpoint number. + + * config.gdb, Makefile, m-altos.h, altos-dep.c: Merge Altos + port. + + * README: Change message about editing Makefile. + + * config.gdb: Edit Makefile. + Copied Makefile to Makefile.c and changed to let config.gdb + run us through the C preprocessor. + + * expread.y (yylex): Test correctly for definition of number. + +Wed Aug 9 11:56:05 1989 Randy Smith (randy at hobbes.ai.mit.edu) + + * dbxread.c (read_dbx_symtab): Put bracketing of entry point in + test case for .o symbols so that it will be correct even without + debugging symbols. + (end_psymtab): Took bracketing out. + + * blockframe.c (outside_startup_file): Reverse the sense of the + return value to make the functionality implied by the name + correct. + +Tue Aug 8 11:48:38 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * coffread.c (symbol_file_command): Do not assume presence of a.out + header. + + * blockframe.c: Replace first_object_file_end with + startup_file_{start,end} + (outside_startup_file): New function. + dbxread.c (read_addl_syms, read_dbx_symtab, end_psymbol): set + startup_file_*. Delete first_object_file_end code. + Add entry_point and ENTRY_POINT + coffread.c (complete_symtab): Set startup_file_*. + (first_object_file_end): Add as static. + m-*.h (FRAME_CHAIN, FRAME_CHAIN_VALID): Call outside_startup_file + instead of comparing with first_object_file_end. + + * breakpoint.c (breakpoint_1): Change -1 to (CORE_ADDR)-1. + + * config.gdb (i386, i386gas): Add missing quotes at end of "echo" + + * source.c (directory_command): Add dont_repeat (); + +Mon Aug 7 18:03:51 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * dbxread.c (read_addl_syms): Change strcmp to strncmp and put 3rd + arg back. + + * command.h (struct cmd_list_element): Add comment clarifying + purpose of abbrev_flag. + +Mon Aug 7 12:51:03 1989 Randy Smith (randy at hobbes.ai.mit.edu) + + * printcmd.c (_initialize_printcmd): Changed "undisplay" not to + have abbrev flag set; it isn't an abbreviation of "delete + display", it's an alias. + +Mon Aug 7 00:25:15 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * symtab.c (lookup_symtab_1): Remove filematch (never used). + + * expread.y [type]: Add second argument to 2 calls to + lookup_member_type which were missing them. + + * dbxread.c (symbol_file_command): Add from_tty arg. + Check it before calling query. + + * infcmd.c (tty_command): Add from_tty arg. + + * eval.c (evaluate_subexp): Remove 3rd argument from + calls to value_x_unop. + + * dbxread.c (read_addl_syms): Remove 3rd argument from + call to strcmp. + + * gdb.texinfo (Command editing): @include inc-readline.texinfo + and inc-history.texinfo and reorganize GDB-specific stuff. + + * Makefile: Add line MAKE=make. + + * README (second paragraph): Fix trivial errors. + + * dbxread.c (read_struct_type): Make sure p is initialized. + + * main.c (symbol_completion_function): Complete correctly + on the empty string. + +Sun Aug 6 21:01:59 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * symmetry-dep.c: Remove "long" from definition of i386_follow_jump. + + * gdb.texinfo (Backtrace): Document "where" and "info stack". + + * dbxread.c (cleanup_undefined_types): Strip off "struct " + or "union " from type names before doing comparison + +Sat Aug 5 02:05:36 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * config.gdb (i386, i386gas): Improve makefile editing instructions. + + * Makefile: Fix typo in CLIBS for SYSV. + + * dbxread.c (read_dbx_symtab): Deal with N_GSYM typedefs. + + * dbxread.c (add_file_command): Do not free name. We didn't + allocate it; it just points into arg_string. + + * Makefile, m-*.h: Change LACK_VPRINTF to HAVE_VPRINTF. + +Fri Jul 28 00:07:48 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * valprint.c (val_print): Made sure that all returns returned a + value (usually 0, indicating no memory printed). + + * core.c (read_memory): Changed "return" to "return 0". + + * expread.y (parse_number): Handle scientific notation when the + string does not contain a '.'. + +Thu Jul 27 15:14:03 1989 Randy Smith (randy at hobbes.ai.mit.edu) + + * infrun.c (signals_info): Error if signal number passed is out of + bounds. + + * defs.h: Define alloca to be __builtin_alloca if compiling with + gcc and localized inclusion of alloca.h on the sparc with the + other alloca stuff. + * command.c: Doesn't need to include alloca.h on the sparc; defs.h + does it for you. + + * printcmd.c (print_frame_args): Changed test for call to + print_frame_nameless_args to check i to tell if any args had been + printed. + +Thu Jul 27 04:40:56 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * blockframe.c (find_pc_partial_function): Always check that NAME + and/or ADDRESS are not nil before storing into them. + +Wed Jul 26 23:41:21 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * m-newsos3.h: Define BROKEN_LARGE_ALLOCA. + * dbxread.c (symbol_file_command, psymtab_to_symtab): + Use xmalloc #ifdef BROKEN_LARGE_ALLOCA. + +Tue Jul 25 16:28:18 1989 Jay Fenlason (hack at apple-gunkies.ai.mit.edu) + + * m68k-opcode.h: moved some of the fmovem entries so they're + all consecutive. This way the assembler doesn't bomb. + +Mon Jul 24 22:45:54 1989 Randy Smith (randy at hobbes.ai.mit.edu) + + * symtab.c (lookup_symbol): Changed error to an informational (if + not very comforting) message about internal problems. This will + get a null symbol returned to decode_line_1, which should force + things to be looked up in the misc function vector. + +Wed Jul 19 13:47:34 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * symtab.c (lookup_symbol): Changed "fatal" to "error" in + external symbol not found in symtab in which it was supposed to be + found. This can be reached because of a bug in ar. + +Tue Jul 18 22:57:43 1989 Randy Smith (roland at hobbes.ai.mit.edu) + + * m-news.h [REGISTER_U_ADDR]: Decreased the assumed offset of fp0 + by 4 to bring it into (apparently) appropriate alignment with + reality. + +Tue Jul 18 18:14:42 1989 Randy Smith (randy at hobbes.ai.mit.edu) + + * Makefile: pinsn.o should depend on opcode.h + + * m68k-opcode.h: Moved fmovemx with register lists to before other + fmovemx. + +Tue Jul 18 11:21:42 1989 Jim Kingdon (kingdon at susie) + + * Makefile, m*.h: Only #define vprintf (to _doprnt or printf, + depends on the system) if the library lacks it (controlled by + LACK_VPRINTF_DEFINE in makefile). Unpleasant, but necessary to + make this work with the GNU C library. + +Mon Jul 17 15:17:48 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * breakpoint.c (breakpoint_1): Change addr-b->address to + b->address-addr. + +Sun Jul 16 16:23:39 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * eval.c (evaluate_subexp): Change error message printed when + right operand of '@' is not an integer to English. + + * infcmd.c (registers_info): Fix call to print_spaces_filtered + to specify right # of arguments. + + * gdb.texinfo (Command Editing): Document info editing command. + + * coffread.c (read_file_hdr): Add MC68MAGIC. + + * source.c (select_source_symtab): Change MAX to max. + +Fri Jul 14 21:19:11 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * infcmd.c (registers_info): Clean up display to look good with long + register names, to say "register" instead of "reg", and to put the + "relative to selected stack frame" bit at the top. + +Fri Jul 14 18:23:09 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * dbxread.c (record_misc_function): Put parens around | to force + correct evaluation. + +Wed Jul 12 12:25:53 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * m-newsos3, m-news, infrun.c, Makefile, config.gdb, news-dep.c: + Merge in Hikichi's changes for Sony/News-OS 3 support. + +Tue Jul 11 21:41:32 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * utils.c (fputs_filtered): Don't do any filtering if output is + not to stdout, or if stdout is not a tty. + (fprintf_filtered): Rely on fputs_filtered's check for whether to + do filtering. + +Tue Jul 11 00:33:58 1989 Randy Smith (randy at hobbes.ai.mit.edu) + + * GDB 3.2 Released. + + * valprint.h: Deleted. + + * utils.c (fputs_filtered): Don't do any filtering if filtering is + disabled (lines_per_page == 0). + +Mon Jul 10 22:27:53 1989 Randy Smith (roland at hobbes.ai.mit.edu) + + * expread.y [typebase]: Added "unsigned long int" and "unsigned + short int" to specs. + +Mon Jul 10 21:44:55 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * main.c (main): Make -cd use cd_command to avoid + current_directory with non-absolute pathname. + +Mon Jul 10 00:34:29 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * dbxread.c (symbol_file_command): Catch errors from stat (even + though they should never happen). + + * source.c (openp): If the path is null, use the current + directory. + + * dbxread.c (read_dbx_symtab): Put N_SETV symbols into the misc + function vector ... + (record_misc_function): ... as data symbols. + + * utils.c (fprintf_filtered): Return after printing if we aren't + going to do filtering. + + * Makefile: Added several things for make clean to take care of. + + * expread.y: Lowered "@" in precedence below +,-,*,/,%. + + * eval.c (evaluate_subexp): Return an error if the rhs of "@" + isn't integral. + + * Makefile: Added removal of core and gdb[0-9] files to clean + target. + + * Makefile: Made a new target "distclean", which cleans things up + correctly for making a distribution. + +Sun Jul 9 23:21:27 1989 Randy Smith (randy at hobbes.ai.mit.edu) + + * dbxread.c: Surrounded define of gnu symbols with an #ifndef + NO_GNU_STABS in case you don't want them on some machines. + * m-npl.h, m-pn.h: Defined NO_GNU_STABS. + +Sun Jul 9 19:25:22 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * utils.c (fputs_filtered): New function. + (fprintf_filtered): Use fputs_filtered. + utils.c (print_spaces_filtered), + command.c (help_cmd,help_cmd_list), + printcmd.c (print_frame_args), + stack.c (print_block_frame_locals, print_frame_arg_vars), + valprint.c (many functions): Use fputs_filtered instead of + fprintf_filtered to avoid arbitrary limit. + + * utils.c (fprintf_filtered): Fix incorrect comment. + +Sat Jul 8 18:12:01 1989 Randy Smith (randy at hobbes.ai.mit.edu) + + * valprint.c (val_print): Changed assignment of pretty to use + prettyprint as a conditional rather than rely on values of the + enum. + + * Projects: Cleaned up a little for release. + + * main.c (initialize_main): Initialize + rl_completion_entry_function instead of completion_entry_function. + + * Makefile: Modified to use the new readline library setup. + + * breakpoint.c (break_command_1, delete_breakpoint, + enable_breakpoint, disable_breakpoint): Put in new printouts for + xgdb usage triggered off of xgdb_verbose. + * main.c (main): Added check for flag to set xgdb_verbose. + * stack.c (frame_command): Set frame_changed when frame command + used. + +Fri Jul 7 16:20:58 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * Remove valprint.h and move contents to value.h (more logical). + +Fri Jul 7 02:28:06 1989 Randall Smith (randy at rice-chex) + + * m68k-pinsn.c (print_insn): Included a check for register list; + if there is one, make sure to start p after it. + + * breakpoint.c (break_command_1, delete_breakpoint, + enable_breakpoint, disable_breakpoint): #ifdef'd out changes + below; they produce unwanted output in gdb mode in gnu-emacs. + + * gdb.texinfo: Spelled. Also removed index references from + command editing section; the relevance/volume ratio was too low. + Removed all references to the function index. + + * ns32k-opcode.h, ns32k-pinsn.c: Backed out changes of June 24th; + haven't yet received legal papers. + + * .gdbinit: Included message telling the user what it is doing. + + * symmetry-dep.c: Added static decls for i386_get_frame_setup, + i386_follow_jump. + * values.c (unpack_double): Added a return (double)0 at the end to + silence a compiler warning. + + * printcmd.c (containing_function_bounds, asdump_command): Created + to dump the assembly code of a function (support for xgdb and a + useful hack). + (_initialize_printcmd): Added this to command list. + * gdb.texinfo [Memory]: Added documentation for the asdump + command. + * breakpoint.c (break_command_1, delete_breakpoint, + enable_breakpoint, disable_breakpoint): Added extra verbosity for + xgdb conditionalized on the new external frame_full_file_name. + * source.c (identify_source_line): Increase verbosity of fullname + prointout to include pc value. + * stack.c: Added a new variable; "frame_changed" to indicate when + a frame has been changed so that gdb can print out a frame change + message when the frame only changes implicitly. + (print_frame_info): Check the new variable in determining when to + print out a new message and set it to zero when done. + (up_command): Increment it. + (down_command): Decrement it. + + * m68k-pinsn.c (print_insn_arg [lL]): Modified cases for register + lists to reset the point to point to after the word from which the + list is grabbed *if* that would cause point to point farther than + it currently is. + +Thu Jul 6 14:28:11 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * valprint.c (val_print, value_print): Add parameter to control + prettyprinting. + valprint.h: New file containing constants used for passing + prettyprinting parameter to val{,ue}_print. + expprint.c, infcmd.c, printcmd.c, valprint.c, values.c: + Change all calls to val{,ue}_print to use new parameter. + +Mon Jul 3 22:38:11 1989 Randy Smith (randy at apple-gunkies.ai.mit.edu) + + * dbxread.c (,process_one_symbol): Moved extern declaration for + index out of function to beginning of file. + +Mon Jul 3 18:40:14 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * gdb.texinfo (Registers): Add "ps" to list of standard registers. + +Sun Jul 2 23:13:03 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * printcmd.c (enable_display): Change d->next to d = d->next so + that "enable display" without args works. + +Fri Jun 30 23:42:04 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * source.c (list_command): Made error message given when no + symtab is loaded clearer. + + * valops.c (value_assign): Make it so that when assigning to an + internal variable, the type of the assignment exp is the type of + the value being assigned. + +Fri Jun 30 12:12:43 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * main.c (verbose_info): Created. + (initialize_main): Put "info verbose" into command list. + + * utils.c (screensize_info): Created. + (_initialize_utils): Defined "info screensize" as a normal command. + + * valprint.c (format_info): Added information about maximum number + of array elements to function. + + * blockframe.c (find_pc_partial_function): Again. + + * blockframe.c (find_pc_partial_function): Replaced a "shouldn't + happen" (which does) with a zero return. + + * main.c (dont_repeat): Moved ahead of first use. + +Thu Jun 29 19:15:08 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * vax-opcode.h: Made minor modifications (moved an instruction and + removed a typo) to bring this into accord with gas' table; also + changed copyright to reflect it being part of both gdb and gas. + + * m68k-opcode.h: Added whole scads and bunches of new stuff for + the m68851 and changed the coptyrightto recognize that the file + was shared between gdb and gas. + + * main.c (stop_sig): Use "dont_repeat ()" instead of *line = 0; + + * core.c (read_memory): Don't do anything if length is 0. + + * Makefile: Added readline.c to the list of files screwed by + having the ansi ioctl.h compilation with gcc. + + * config.gdb: Added sun4os3 & sun4-os3 as availible options. + +Wed Jun 28 02:01:26 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu) + + * command.c (lookup_cmd): Add ignore_help_classes argument. + (lookup_cmd_1): Add ignore_help_classes argument. + command.c, main.c: Change callers of lookup_cmd{,_1} to supply + value for ignore_help_classes. + +Tue Jun 27 18:01:31 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * utils.c (print_spaces_filtered): Made more efficient. + * defs.h: Declaration. + * valprint.c (val_print): Used in a couple of new places. + +Mon Jun 26 18:27:28 1989 Randall Smith (randy at gluteus.ai.mit.edu) + + * m68k-pinsn.c (print_insn_arg ['#', '^']): Combined them into one + case which always gets the argument from the word immediately + following the instruction. + (print_insn_arg ["[lL]w"]): Make sure to always get the register + mask from the word immediately following the instruction. + +Sun Jun 25 19:14:56 1989 Randall Smith (randy at galapas.ai.mit.edu) + + * Makefile: Added hp-include back in as something to distribute. + + * stack.c (print_block_frame_locals): Return value changed from + void to int; return 1 if values printed. Use _filtered. + (print_frame_local_vars): Use return value from + print_block_frame_locals to mention if nothing printed; mention + lack of symbol table, use _filtered. + (print_frame_arg_vars): Tell the user if no symbol table + or no values printed. Use fprintf_filtered instead of fprintf. + * blockframe.c (get_prev_frame_info): Check for no inferior or + core file before crashing. + + * inflow.c (inferior_died): Set current frame to zero to keep from + looking like we're in start. + +Sat Jun 24 15:50:53 1989 Randall Smith (randy at gluteus.ai.mit.edu) + + * stack.c (frame_command): Added a check to make sure that there + was an inferior or a core file. + + * expread.y (yylex): Allow floating point numbers of the form ".5" + to be parsed. + + Changes by David Taylor at TMC: + * ns32k-pinsn.c: Added define for ?floating point coprocessor? and + tables for register names to be used for each of the possibilities. + (list_search): Created; searches a list of options for a specific + value. + (print_insn_arg): Added 'Q', 'b', 'M', 'P', 'g', and 'G' options + to the value location switch. + * ns32k-opcode.h: Added several new location flags. + [addr, enter, exit, ext[bwd], exts[bwd], lmr, lpr[bwd], restore, + rett, spr[bwd], smr]: Improved insn format output. + + * symtab.c (list_symbols): Rearrange printing to produce readable + output for "info types". + + * eval.c (evaluate_subexp_for_address): Fixed typo. + + * dbxread.c (read_type): Don't output an error message when + there isn't a ',' after a cross-reference. + + * dbxread.c (read_dbx_symtab): #if'd out N_FN case in + read_dbx_symtab if it has the EXT bit set (otherwise multiple + cases with the same value). + +Fri Jun 23 13:12:08 1989 Randall Smith (randy at plantaris.ai.mit.edu) + + * symmisc.c: Changed decl of print_spaces from static to extern + (since it's defined in utils.c). + + * remote.c (remote_open): Close remote_desc if it's already been + opened. + + * Remote_Makefile, remote_gutils.c, remote_inflow.c, + remote_server.c, remote_utils.c: Combined into remote-multi.shar. + * remote-multi.shar: Created (Vikram Koka's remote stub). + * remote-sa.m68k.shar: Created (Glenn Engel's remcom.c). + * README: Updated to reflect new organization of remote stubs. + + * dbxread.c (read_dbx_symtab): Put an N_FN in with N_FN | N_EXT to + account for those machines which don't use the external bit here. + Sigh. + + * m-symmetry.h: Defined NO_SIGINTERRUPT. + +Thu Jun 22 12:51:37 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * printcmd.c (decode_format): Make sure characters are printed + using a byte size. + + * utils.c (error): Added a terminal_ours here. + + * stack.c (locals_info): Added check for selected frame. + + * dbxread.c (read_type): Checked to make sure that a "," was + actually found in the symbol to end a cross reference. + +Wed Jun 21 10:30:01 1989 Randy Smith (randy at tartarus.uchicago.edu) + + * expread.y (parse_number, [exp]): Allowed for the return of a + number marked as unsigned; this will allow inclusion of unsigned + constants. + + * symtab.h: Put in default definitions for BUILTIN_TYPE_LONGEST + and BUILTIN_TYPE_UNSIGNED_LONGEST. + + * expread.y (parse_number): Will now accept integers suffixed with + a 'u' (though does nothing special with it). + + * valarith.c (value_binop): Added cases to deal with unsigned + arithmetic correctly. + +Tue Jun 20 14:25:54 1989 Randy Smith (randy at tartarus.uchicago.edu) + + * dbxread.c (psymtab_to_symtab_1): Changed reading in info message + to go through printf_filtered. + + * symtab.c (list_symbols): Placed header message after all calls + to psymtab_to_symtab. + + * symtab.c (smash_to_{function, reference, pointer}_type): Carried + attribute of permanence for the type being smashed over the bzero + and allowed any type to point at this one if it is permanent. + + * symtab.c (smash_to_{function, reference, pointer}_type): Fix + typo: check flags of to_type instead of type. + + * m-hp9k320.h: Changed check on __GNU__ predefine to __GNUC__. + + * Makefile: Made MUNCH_DEFINE seperate and based on SYSV_DEFINE; + they aren't the same on hp's. + +Mon Jun 19 17:10:16 1989 Randy Smith (randy at tartarus.uchicago.edu) + + * Makefile: Fixed typo. + + * valops.c (call_function): Error if the inferior has not been + started. + + * ns32k-opcode.h [check[wc], cmpm[bwd], movm[bwd], skpsb]: Fixed + typos. + +Fri Jun 9 16:23:04 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * m-news.h [NO_SIGINTERRUPT]: Defined. + + * dbxread.c (read_type): Start copy of undefined structure name + past [sue] defining type of cross ref. + + * dbxread.c (process_one_symbol): Changed strchr to index. + + * ns32k-opcode.h, ns32k-pinsn.c: More changes to number of + operands, addition of all of the set condition opcodes, addition + of several flag letters, all patterned after the gas code. + + * ns32k-opcode.h [mov{su,us}[bwd], or[bwd]]: Changed number of + operands from 1 to 2. + +Wed Jun 7 15:04:24 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * symseg.h [TYPE_FLAG_STUB]: Created. + * dbxread.c (read_type): Set flag bit if type is stub. + (cleanup_undefined_types): Don't mark it as a stub if it's been + defined since we first learned about it. + * valprint.c (val_print): Print out a message to that effect if + this type is encountered. + + * symseg.h, symtab.h: Moved the definition of TYPE_FLAG_PERM over + to symseg.h so that all such definitions would be in the same place. + + * valprint.c (val_print): Print out for a + structure if there aren't any. + + * dbxread.c (read_type): Set type name of a cross reference type + to "struct whatever" or something. + +Tue Jun 6 19:40:52 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * breakpoint.c (breakpoint_1): Print out symbolic location of + breakpoints for which there are no debugging symbols. + +Mon Jun 5 15:14:51 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * command.c (help_cmd_list): Made line_size static. + +Sat Jun 3 17:33:45 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * Makefile: Don't include the binutils hp-include directory in the + distribution anymore; refer the users to the binutils distribution. + +Thu Jun 1 16:33:07 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * printcmd.c (disable_display_command): Fixed loop iteration for + no arg case. + + * printcmd.c (disable_display_command): Added from_tty parameter + to function. + + * valops.c (value_of_variable): Call read_var_value with 0 cast to + FRAME instead of CORE_ADDR. + + * eval.c (evaluate_subexp): Corrected number of args passed to + value_subscript (to 2). + + * infrun.c (wait_for_inferior), symtab.c (decode_line_1), + m-convex.h: Changed name of FIRSTLINE_DEBUG_BROKEN to + PROLOGUE_FIRSTLINE_OVERLAP. + + * m-merlin.h: Fixed typo. + * ns32k-opcode.h: Added ns32381 opcodes and "cinv" insn, and fixed + errors in movm[wd], rett, and sfsr. + + * eval.c (evaluate_subexp, evaluate_subexp_for_address), valops.c + (value_zero): Change value_zero over to taking two arguments + instead of three. + + * eval.c (evaluate_subexp) + [OP_VAR_VALUE]: Get correct lval type for AVOID_SIDE_EFFECTS for + all types of symbols. + [BINOP_DIV]: Don't divide if avoiding side effects; just return + an object of the correct type. + [BINOP_REPEAT]: Don't call value_repeat, just allocate a + repeated value. + (evaluete_subexp_for_address) [OP_VAR_VALUE]: Just return a thing + of the right type (after checking to make sure that we are allowed + to take the address of whatever variable has been passed). + +Mon May 29 11:01:02 1989 Randall Smith (randy at galapas.ai.mit.edu) + + * breakpoint.c (until_break_command): Set the breakpoint with a + frame specification so that it won't trip in inferior calls to the + function. Also set things up so that it works based on selected + frame, not current one. + +Sun May 28 15:05:33 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * eval.c (evalue_subexp): Change subscript case to use value_zero + in EVAL_AVOID_SIDE_EFFECTS case. + +Fri May 26 12:03:56 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * dbxread.c (read_addl_syms, psymtab_to_symtab): Removed + cleanup_undefined_types; this needs to be done on a symtab basis. + (end_symtab): Called cleanup_undefined_types from here. + (cleanup_undefined_types): No longer uses lookup_symbol (brain + dead idea; oh, well), now it searches through file_symbols. + +Wed May 24 15:52:43 1989 Randall Smith (randy at galapas) + + * source.c (select_source_symtab): Only run through + partial_symtab_list if it exists. + + * coffread.c (read_coff_symtab): Don't unrecord a misc function + when a function symbol is seen for it. + + * expread.y [variable]: Make sure to write a type for memvals if + you don't get a mft you recognize. + +Tue May 23 12:15:57 1989 Randall Smith (randy at plantaris.ai.mit.edu) + + * dbxread.c (read_ofile_symtab, psymtab_to_symtab): Moved cleanup + of undefined types to psymtab_to_symtab. That way it will be + called once for all readins (which will, among other things, + help reduce infinite loops). + + * symtab.h [misc_function_type]: Forced mf_unknown to 0. + * dbxread.c (record_misc_function): Cast enum to unsigned char (to + fit). + * expread.y [variable]: Cast unsigned char back to enum to test. + +Mon May 22 13:08:25 1989 Randall Smith (randy at gluteus.ai.mit.edu) + + Patches by John Gilmore for dealing well with floating point: + * findvar.c (value_from_register, locate_var_value): Used + BYTES_BIG_ENDIAN instead of an inline test. + * m-sparc.h [IEEE_FLOAT]: Created to indicate that the sparc is + IEEE compatible. + * printcmd.c (print_scalar_formatted): Use BYTES_BIG_ENDIAN and + the stream argument for printing; also modify default type for + 'f'. Change handling of invalid floats; changed call syntax for + is_nan. + (print_command): Don't print out anything indicating that + something was recorded on the history list if it wasn't. + * valprint.c (val_print): Fixed to deal properley with new format + of is_nan and unpacking doubles without errors occuring. + (is_nan): Changed argument list and how it figures big endianness + (uses macros). + * values.c (record_latest_value): Return -1 and don't record if + it's an invalid float. + (value_as_double): Changed to use new unpack_double calling + convention. + (unpack_double): Changed not to call error if the float was + invalid; simply to set invp and return. Changed calling syntax. + (unpack_field_as_long, modify_field): Changed to use + BITS_BIG_ENDIAN to determine correct action. + + * m-hp9k320.h [HP_OS_BUG]: Created; deals with problem where a + trap happens after a continue. + * infrun.c (wait_for_inferior): Used. + + * m-convex.h [FIRSTLINE_DEBUG_BROKEN]: Defined a flag to indicate + that the debugging symbols output by the compiler for the first + line of a function were broken. + * infrun.c (wait_for_inferior), symtab.c (decode_line_1): Used. + + * gdb.texinfo [Data, Memory]: Minor cleanups of phrasing. + +Fri May 19 00:16:59 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * dbxread.c (add_undefined_type, cleanup_undefined_types): Created + to keep a list of cross references to as yet undefined types. + (read_type): Call add_undefined_type when we run into such a case. + (read_addl_syms, read_ofile_symtab): Call cleanup_undefined_types + when we're done. + + * dbxread.c (psymtab_to_symtab, psymtab_to_symtab_1): Broke + psymtab_to_symtab out into two routines; made sure the string + table was only readin once and the globals were only scanned once, + for any number of dependencies. + +Thu May 18 19:59:18 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * m-*.h: Defined (or not, as appropriate per machine) + BITS_BIG_ENDIAN, BYTES_BIG_ENDIAN, and WORDS_BIG_ENDIAN. + +Wed May 17 13:37:45 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * main.c (symbol_completion_function): Always complete on result + command list, even if exact match found. If it's really an exact + match, it'll find it again; if there's something longer than it, + it'll get the right result. + + * symtab.c (make_symbol_completion_function): Fixed typo; strcmp + ==> strncmp. + + * dbxread.c (read_dbx_symtab): Change 'G' case to mark symbols as + LOC_EXTERNAL. + + * expread.y [variables]: Changed default type of text symbols to + function returning int so that one can use, eg. strcmp. + + * infrun.c (wait_for_inferior): Include a special flag indicating + that one shouldn't insert the breakpoints on the next step for + returning from a sigtramp and forcing at least one move forward. + + * infrun.c (wait_for_inferior): Change test for nexting into a + function to check for current stack pointer inner than previous + stack pointer. + + * infrun.c (wait_for_inferior): Check for step resume break + address before dealing with normal breakpoints. + + * infrun.c (wait_for_inferior): Added a case to deal with taking + and passing along a signal when single stepping past breakpoints + before inserting breakpoints. + + * infrun.c (wait_for_inferior): Inserted special case to keep + going after taking a signal we are supposed to be taking. + +Tue May 16 12:49:55 1989 Randall Smith (randy at gluteus.ai.mit.edu) + + * inflow.c (terminal_ours_1): Cast result of signal to (int + (*)()). + + * gdb.texinfo: Made sure that references to the program were in + upper case. Modify description of the "set prompt" command. + [Running]: Cleaned up introduction. + [Attach]: Cleaned up. + [Stepping]: Change "Proceed" to "Continue running" or "Execute". + Minor cleanup. + [Source Path]: Cleaned up intro. Cleared up distinction between + the executable search path and the source path. Restated effect + of the "directory" command with no arguments. + [Data]: Fixed typos and trivial details. + [Stepping]: Fixed up explanation of "until". + + * source.c (print_source_lines): Print through filter. + + * printcmd.c (x_command): If the format with which to print is + "i", use the address of anything that isn't a pointer instead of + the value. This is for, eg. "x/10i main". + + * gdb.texinfo: Updated last modification date on manual. + +Mon May 15 12:11:33 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * symtab.c (lookup_symtab): Fixed typo (name ==> copy) in call to + lookup_symtab_1. + + * gdb.texinfo: Added documentation for "break [+-]n" and for new + actions of "directory" command (taking multiple directory names at + the same time). + + * m68k-opcode.h: Replaced the version in gdb with an up-to-date + version from the assembler directory. + * m68k-pinsn.c (print_insn_arg): Added cases 'l' & 'L' to switch + to print register lists for movem instructions. + + * dbxread.c, m-convex.h: Moved convex dependent include files over + from dbxread.c to m-convex.h. + + * printcmd.c (disable_display, disable_display_command): Changed + name of first to second, and created first which takes an int as + arg rather than a char pointer. Changed second to use first. + (_initialize_printcmd): Changed to use second as command to call. + (delete_current_display, disable_current_display): Changed name of + first to second, and changed functionality to match. + * infrun.c (normal_stop), main.c (return_to_top_level): Changed to + call disable_current_display. + + * dbxread.c (process_one_symbol, read_dbx_symtab): Changed N_FN to + be N_FN | N_EXT to deal with new Berkeley define; this works with + either the old or the new. + + * Remote_Makefile, remote_gutils.c, remote_inflow.c, + remote_server.c, remote_utils.c: Created. + * Makefile: Included in tag and tar files. + * README: Included a note about them. + + * printcmd.c (print_address): Use find_pc_partial_function to + remove need to readin symtabs for symbolic addresses. + + * source.c (directory_command): Replaced function with new one + that can accept lists of directories seperated by spaces or :'s. + + * inflow.c (new_tty): Replaced calls to dup2 with calls to dup. + +Sun May 14 12:33:16 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * stack.c (args_info): Make sure that you have an inferior or core + file before taking action. + + * ns32k-opcode.h [deiw, deid]: Fixed machine code values for these + opcodes. + + * dbxread.c (scan_file_globals): Modified to use misc function + vector instead of file itself. Killed all arguments to the + funciton; no longer needed. + (psymtab_to_symtab): Changed call for above to reflect new (void) + argument list. + + * dbxread.c (read_dbx_symtab, ): Moved HASH_OFFSET define out of + read_dbx_symtab. + + * expread.y [variable]: Changed default type of misc function in + text space to be (void ()). + + * Makefile: Modified for proper number of s/r conflicts (order is + confusing; the mod that necessitated this change was on May 12th, + not today). + + * expread.y (yylex): Added SIGNED, LONG, SHORT, and INT keywords. + [typename]: Created. + [typebase]: Added rules for LONG, LONG INT, SHORT, SHORT INT, + SIGNED name, and UNSIGNED name (a good approximation of ansi + standard). + + * Makefile: Included .c.o rule to avoid sun's make from throwing + any curves at us. + + * blockframe.c: Included + + * command.c (lookup_cmd): Clear out trailing whitespace. + + * command.c (lookup_cmd_1): Changed malloc to alloca. + +Fri May 12 12:13:12 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * printcmd.c (print_frame_args): Only print nameless args when you + know how many args there are supposed to be and when you've + printed fewer than them. Don't print nameless args between + printed args. + + * symtab.c (make_symbol_completion_function): Fixed typo (= ==> + ==). + + * remote.c (remote_open): ifdef'd out siginterrupt call by #ifndef + NO_SIGINTERRUPT. + * m-umax.h: Defined NO_SIGINTERRUPT. + + * expread.y [ptype, array_mod, func_mod, direct_abs_decl, + abs_decl]: Added rules for parsing and creating arbitrarily + strange types for casts and sizeofs. + + * symtab.c, symtab.h (create_array_type): Created. Some minor + misfeatures; see comments for details (main one being that you + might end up creating two arrays when you only needed one). + +Thu May 11 13:11:49 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * valops.c (value_zero): Add an argument for type of lval. + * eval.c (evaluate_subexp_for_address): Take address properly in + the avoid side affects case (ie. keep track of whether we have an + lval in memory and we can take the address). + (evaluate_subexp): Set the lval type of expressions created with + value_zero properley. + + * valops.c, value.h (value_zero): Created--will return a value of + any type with contents filled with zero. + * symtab.c, symtab.h (lookup_struct_elt_type): Created. + * eval.c (evaluate_subexp): Modified to not read memory when + called with EVAL_AVOID_SIDE_EFFECTS. + + * Makefile: Moved dbxread.c ahead of coffread.c in the list of + source files. + +Wed May 10 11:29:19 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * munch: Make sure that sysv version substitutes for the whole + line. + + * symtab.h: Created an enum misc_function_type to hold the type of + the misc function being recorded. + * dbxread.c (record_misc_function): Branched on dbx symbols to + decide which type to assign to a misc function. + * coffread.c (record_misc_function): Always assign type unknown. + * expread.y [variable]: Now tests based on new values. + +Tue May 9 13:03:54 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * symtab.c: Changed inclusion of (doesn't work on + SYSV) to declaration of index. + + * Makefile: Changed last couple of READLINE_FLAGS SYSV_DEFINE + + * source.c ({forward, reverse}_search_command): Made a default + search file similar to for the list command. + +Mon May 8 18:07:51 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * printcmd.c (print_frame_args): If we don't know how many + arguments there are to this function, don't print the nameless + arguments. We don't know enough to find them. + + * printcmd.c (print_frame_args): Call print_frame_nameless_args + with proper arguments (start & end as offsets from addr). + + * dbxread.c (read_addl_syms): Removed cases to deal with global + symbols; this should all be done in scan_global_symbols. + +Sun May 7 11:36:23 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * Makefile: Added copying.awk to ${OTHERS}. + +Fri May 5 16:49:01 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * valprint.c (type_print_varspec_prefix): Don't pass + passed_a_pointer onto children. + + * valprint.c (type_print_varspec_suffix): Print "array of" with + whatever the "of" is after tha array brackets. + + * valprint.c (type_print_varspec_{prefix,suffix}): Arrange to + parenthesisze pointers to arrays as well as pointers to other + objects. + + * valprint.c (type_print_varspec_suffix): Make sure to print + subscripts of multi-dimensional arrays in the right order. + + * infcmd.c (run_command): Fixed improper usages of variables + within remote debugging branch. + + * Makefile: Added Convex.notes to the list of extra files to carry + around. + + * dbxread.c (symbol_file_command): Made use of alloca or malloc + dependent on macro define. + +Thu May 4 15:47:04 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * Makefile: Changed READLINE_FLAGS to SYSV_DEFINE and called munch + with it also. + * munch: Check first argument for -DSYSV and be looser about + picking up init routines if you find it. + + * coffread.c: Made fclose be of type int. + + * breakpoint.c (_initialize_breakpoint): Put "unset" into class + alias. + +Wed May 3 14:09:12 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * m-sparc.h [STACK_END_ADDR]: Parameterized off of + machine/vmparam.h (as per John Gilmore's suggestion). + + * blockframe.c (get_prev_frame_info): Changed this function back + to checking frameless invocation first before checking frame + chain. This means that a backtrace up from start will produce the + wrong value, but that a backtrace from a frameless function called + in main will show up correctly. + + * breakpoint.c (_initialize_breakpoint): Added entry in help for + delete that indicates that unset is an alias for it. + + * main.c (symbol_completion_function): Modified recognition of + being within a single command. + +Tue May 2 15:13:45 1989 Randy Smith (randy at gnu) + + * expread.y [variable]: Add some parens to get checking of the + misc function vector right. + +Mon May 1 13:07:03 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * default-dep.c (core_file_command): Made reg_offset unsigned. + + * default-dep.c (core_file_command): Improved error messages for + reading in registers. + + * expread.y: Allowed a BLOCKNAME to be ok for a variable name (as + per C syntax). + + * dbxread.c (psymtab_to_symtab): Flushed stdout after printing + starting message about reading in symbols. + + * printcmd.c (print_frame_args): Switched starting place for + printing of frameless args to be sizeof int above last real arg + printed. + + * printcmd.c (print_frame_args): Modified final call to + print_nameless_args to not use frame slots used array if none had + been used. + + * infrun.c (wait_for_inferior): Take FUNCTION_START_OFFSET into + account when dealing with comparison of pc values to function + addresses. + + * Makefile: Added note about compiling gdb on a Vax running 4.3. + +Sun Apr 30 12:59:46 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * command.c (lookup_cmd): Got correct error message on bad + command. + + * m-sun3.h [ABOUT_TO_RETURN]: Modified to allow any of the return + instructions, including trapv and return from interupt. + + * command.c (lookup_cmd): If a command is found, use it's values + for error reporting and determination of needed subcommands. + + * command.c (lookup_cmd): Use null string for error if cmdtype is + null; pass *line to error instead of **. + + * command.c (lookup_cmd_1): End of command marked by anything but + alpha numeric or '-'. Included ctype.h. + +Fri Apr 28 18:30:49 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * source.c (select_source_symtab): Kept line number from ever + being less than 1 in main decode. + +Wed Apr 26 13:03:20 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * default-dep.c (core_file_command): Fixed typo. + + * utils.c (fprintf_filtered): Don't use return value from + numchars. + + * main.c, command.c (complete_on_cmdlist): Moved function to + command.c. + + * command.c (lookup_cmd): Modified to use my new routine. Old + version is still there, ifdef'd out. + + * command.c, command.h (lookup_cmd_1): Added a routine to do all + of the work of lookup_cmd with no error reporting and full return + of information garnered in search. + +Tue Apr 25 12:37:54 1989 Randall Smith (randy at gluteus.ai.mit.edu) + + * breakpoint.c (_initialize_breakpoint): Change "delete + breakpionts" to be in class alias and not have the abbrev flag + set. + + * main.c (symbol_completion_function): Fix to correctly complete + things that correspond to multiword aliases. + + * main.c (complete_on_cmdlist): Don't complete on something if it + isn't a command or prefix (ie. if it's just a help topic). + + * main.c (symbol_completion_function): Set list index to be 0 if + creating a list with just one element. + + * main.c (complete_on_cmdlist): Don't allow things with + abbrev_flag set to be completion values. + (symbol_completion_function): Don't accept an exact match if the + abbrev flag is set. + + * dbxread.c (read_type): Fixed typo in comparision to check if + type number existed. + + * dbxread.c (read_type): Made sure to only call dbx_lookup_type on + typenums if typenums were not -1. + +Mon Apr 24 17:52:12 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * symtab.c: Added strings.h as an include file. + +Fri Apr 21 15:28:38 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * symtab.c (lookup_partial_symtab): Changed to only return a match + if the name match is exact (which is what I want in all cases in + which this is currently used. + +Thu Apr 20 11:12:34 1989 Randall Smith (randy at gluteus.ai.mit.edu) + + * m-isi.h [REGISTER_U_ADDR]: Installed new version from net. + * default-dep.c: Deleted inclusion of fcntl.h; apparently not + necessary. + * Makefile: Added comment about compiling on isi under 4.3. + + * breakpoint.c (break_command_1): Only give decode_line_1 the + default_breakpoint_defaults if there's nothing better (ie. make + the default be off of the current_source notes if at all + possible). + + * blockframe.c (get_prev_frame_info): Clean up comments and + delete code ifdefed out around FRAMELESS_FUNCTION_INVOCATION test. + + * remote.c: Added a "?" message to protocol. + (remote_open): Used at startup. + (putpkt): Read whatever garbage comes over the line until we see a + '+' (ie. don't treat garbage as a timeout). + + * valops.c (call_function): Eliminated no longer appropriate + comment. + + * infrun.c (wait_for_inferior): Changed several convex conditional + compilations to be conditional on CANNOT_EXECUTE_STACK. + +Wed Apr 19 10:18:17 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * printcmd.c (print_frame_args): Added code to attempt to deal + with arguments that are bigger than an int. + + Continuation of Convex/Fortran changes: + * printcmd.c (print_scalar_formatted): Added leading zeros to + printing of large integers. + (address_info, print_frame_args): Added code to deal with + LOC_REF_ARG. + (print_nameless_args): Allow param file to specify a routine with + which to print typeless integers. + (printf_command): Deal with long long values well. + * stack.c (print_frame_arg_vars): Change to deal with LOC_REF_ARG. + * symmisc.c (print_symbol): Change to deal with LOC_REF_ARG. + * symseg.h: Added LOC_REF_ARG to enum address_class. + * symtab.c (lookup_block_symbol): Changed to deal with + LOC_REF_ARG. + * valarith.c (value_subscripted_rvalue): Created. + (value_subscript): Used above when app. + (value_less, value_equal): Change to cast to (char *) before doing + comparison, for machines where that casting does something. + * valops.c (call_function): Setup to deal with machines where you + cannot execute code on the stack segment. + * valprint.c (val_print): Make sure that array element size isn't + zero before printing. Set address of default array to address of + first element. Put in a couple of int cast. Removed some convex + specific code. Added check for endianness of machine in case of a + packed structure. Added code for printing typeless integers and + for LONG LONG's. + (set_maximum_command): Change to use parse_and_eval_address to get + argument (so can use expressions there). + * values.c (value_of_internalvar, set_internalvar_component, + set_internalvar, convenience_info): Add in hooks for trapped + internal vars. + (unpack_long): Deal with LONG_LONG. + (value_field): Remove LONGEST cast. + (using_struct_return): Fixed typo ENUM ==> UNION. + * xgdb.c (_initialize_xgdb): Make sure that specify_exec_file_hook + is not called unless we are setting up a windowing environ. + +Tue Apr 18 13:43:37 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + Various changes involved in 1) getting gdb to work on the convex, + and 2) Getting gdb to work with fortran (due to convex!csmith): + * convex-dep.c, convex-opcode.h, m-convex.h, convex-pinsn.c: + Created (or replaced with new files). + * Makefile: Add convex dependent files. Changed default flags to + gnu malloc to be CFLAGS. + * config.gdb: Added convex to list of machines. + * core.c (files_info): Added a FILES_INFO_HOOK to be used if + defined. + (xfer_core_file): Conditionalized compilation of xfer_core_file on + the macro XFER_CORE_FILE. + * coffread.c (record_misc_function): Made sure it zerod type field + (which is now being used; see next). + * dbxread.c: Included some convex dependent include files. + (copy_pending, fix_common_blocks): Created. + [STAB_REG_REGNUM, BELIEVE_PCC_PROMOTION]: Created default values; + may be overridden in m-*.h. + Included data structures for keeping track of common blocks. + (dbx_alloc_type): Modified; if called with negative 1's will + create a type without putting it into the type vector. + (read_dbx_symtab, read_addl_syms): Modified calls to + record_misc_function to include the new information. + (symbol_file_command, psymtab_to_symtab, add_file_command): + Modified reading in of string table to adapt to machines which + *don't* store the size of the string table in the first four bytes + of the string table. + (read_dbx_symtab, scan_file_globals, read_ofile_symtab, + read_addl_syms): Modified assignment of namestring to accept null + index into symtab as ok. + (read_addl_syms): Modified readin of a new object file to fiddle + with common blocks correctly. + (process_one_symbol): Fixed incorrect comment about convex. Get + symbols local to a lexical context from correct spot on a per + machine basis. Catch a bug in pcc which occaisionally puts an SO + where there should be an SOL. Seperate sections for N_BCOMM & + N_ECOMM. + (define_symbol): Ignore symbols with no ":". Use + STAB_REG_TO_REGNUM. Added support for function args calling by + reference. + (read_type): Only read type number if one is there. Remove old + (#if 0'd out) array code. + (read_array_type): Added code for dealing with adjustable (by + parameter) arrays half-heartedly. + (read_enum_type): Allow a ',' to end a list of values. + (read_range_type): Added code to check for long long. + * expread.y: Modified to use LONGEST instead of long where + necessary. Modified to use a default type of int for objects that + weren't in text space. + * findvar.c (locate_var_value, read_var_value): Modified to deal + with args passed by reference. + * inflow.c (create_inferior): Used CREATE_INFERIOR_HOOK if it + exists. + * infrun.c (attach_program): Run terminal inferior when attaching. + (wait_for_inferior): Removed several convex dependencies. + * main.c (float_handler): Created. + Made whatever signal indicates a stop configurable (via macro + STOP_SIGNAL). + (main): Setup use of above as a signal handler. Added check for + "-nw" in args already processed. + (command_line_input): SIGTSTP ==>STOP_SIGNAL. + + * expread.y: Added token BLOCKNAME to remove reduce/reduce + conflict. + * Makefile: Change message to reflect new grammar. + +Mon Apr 17 13:24:59 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * printcmd.c (compare_ints): Created. + (print_frame_args): Modified to always print arguments in the + order in which they were found in the symbol table. Figure out + what apots are missing on the fly. + + * stack.c (up_command): Error if no inferior or core file. + + * m-i386.h, m-symmetry.h [FRAMELESS_FUNCTION_INVOCATION]: Created; + same as m68k. + + * dbxread.c (define_symbol): Changed "desc==0" test to + "processing_gcc_compilation", which is the correct way to do it. + +Sat Apr 15 17:18:38 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * expread.y: Added precedence rules for arglists, ?:, and sizeof + to eliminate some shift-reduce conflicts. + * Makefile: Modified "Expect" message to conform to new results. + +Thu Apr 13 12:29:26 1989 Randall Smith (randy at plantaris.ai.mit.edu) + + * inflow.c (terminal_init_inferior): Fixed typo in recent diff + installation; TIOGETC ==> TIOCGETC. + + * m-vax.h, m-sun2.h, m-sun3.h, m-sparc.h, m-hp*.h, m-isi.h, + m-news.h [FRAMELESS_FUNCTION_INVOCATION]: Created macro with + appropriate definition. + +Wed Apr 12 15:30:29 1989 Randall Smith (randy at plantaris.ai.mit.edu) + + * blockframe.c (get_prev_frame_info): Added in a macro to specify + when a "frame" is called without a frame pointer being setup. + + * Makefile [clean]: Made sure to delete gnu malloc if it was being + used. + +Mon Apr 10 12:43:49 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * dbxread.c (process_one_symbol): Reset within_function to 0 after + last RBRAC of a function. + + * dbxread.c (read_struct_type): Changed check for filling in of + TYPE_MAIN_VARIANT of type. + + * inflow.c (create_inferior): Conditionalized fork so that it + would be used if USG was defined and HAVE_VFORK was not defined. + + * defs.h: Added comment about enum command_class element + class_alias. + + * dbxread.c (process_one_symbol): Fixed a typo with interesting + implications for associative processing in the brain (':' ==> 'c'). + + * sparc-dep.c (isabranch): Changed name to isannulled, modified to + deal with coprocessor branches, and improved comment. + (single_step): Changed to trap at npc + 4 instead of pc +8 on + annulled branches. Changed name in call to isabranch as above. + + * m-sun4os4.h (STACK_END_ADDRESS): Changed it to 0xf8000000 under + os 4.0. + +Sat Apr 8 17:04:07 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * dbxread.c (process_one_symbol): In the case N_FUN or N_FNAME the + value being refered to is sometimes just a text segment variable. + Catch this case. + + * infrun.c (wait_for_inferior), breakpoint.c + (breakpoint_stop_status): Move the selection of the frame to + inside breakpoint_stop_status so that the frame only gets selected + (and the symbols potentially read in) if the symbols are needed. + + * symtab.c (find_pc_psymbol): Fixed minor misthough (pc >= + fucntion start, not >). + + * breakpoint.c (_initialize_breakpoint): Change "delete" internal + help entry to simply refer to it being a prefix command (since the + list of subcommands is right there on a "help delete"). + +Fri Apr 7 15:22:18 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * blockframe.c (find_pc_partial_function): Created; figures out + what function pc is in (name and address) without reading in any + new symbols. + * symtab.h: Added decl for above. + * infrun.c (wait_for_inferior): Used instead of + find_pc_function_start. + * stack.c (print_frame_info): Used instead of hand coding for same + thing. + + * dbxread.c (psymtab_to_symtab): No longer patch readin pst's out + of the partial_symtab_list; need them there for some checks. + * blockframe.c (block_for_pc), source.c (select_source_symtab), + symtab.c (lookup_symbol, find_pc_symtab, list_symbols): Made extra + sure not to call psymtab_to_symtab with ->readin == 1, since these + psymtab now stay on the list. + * symtab.c (sources_info): Now distinguishes between psymtabs with + readin set and those with it not set. + + * symtab.c (lookup_symtab): Added check through partial symtabs + for name with .c appended. + + * source.c (select_source_symtab): Changed semantics a little so + that the argument means something. + * source.c (list_command), symtab.c (decode_line_1): Changed call + to select_source_symtab to match new conventions. + + * dbxread.c (add_file_command): This command no longer selects a + symbol table to list from. + + * infrun.c (wait_for_inferior): Only call find_pc_function (to + find out if we have debugging symbols for a function and hence if + we should step over or into it) if we are doing a "step". + +Thu Apr 6 12:42:28 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * main.c (command_line_input): Added a local buffer and only + copied information into the global main.c buffer when it is + appropriate for it to be saved (and repeated). + (dont_repeat): Only nail line when we are reading from stdin + (otherwise null lines won't repeat and what's in line needs to be + saved). + (read_command_lines): Fixed typo; you don't what to repeat when + reading command lines from the input stream unless it's standard + input. + + John Gilmore's (gnu@toad.com) mods for USG gdb: + * inflow.c: Removed inclusion of sys/user.h; no longer necessary. + (, terminal_init_inferior, terminal_inferior, terminal_ours_1, + term_status_command, _initialize_inflow) Seperated out declaration + and usage of terminal mode structures based on the existence of + the individual ioctls. + * utils.c (request_quit): Restore signal handler under USG. If + running under USG initialize sys_siglist at run time (too much + variation between systems). + +Wed Apr 5 13:47:24 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + John Gilmore's (gnu@toad.com) mods for USG gdb: + * default-dep.c: Moved include of sys/user.h to after include of + a.out.h. + (store_inferior_registers): Fixed error message. + (core_file_command): Improved error messages from reading in of + u area in core file. Changed calculation of offset of registers + to account for some machines putting it in as an offset rather + than an absolute address. Changed error messages for reading of + registers from core file. + + * coffread.c (read_file_hdr): Added final check for BADMAG macro + to use if couldn't recognize magic number. + * Makefile: Added explicit directions for alloca addition. + Included alloca.c in list of possible library files. Cleaned up + possible library usage. Included additional information on gcc + and include files. + + * source.c, remote.c, inflow.c, dbxread.c, core.c, coffread.c: + Changed include of sys/fcntl.h to an include of fcntl.h (as per + posix; presumably this will break fewer machines. I hopw). + * README: Added a pointer to comments at top of Makefile. + * Makefile: Added a comment about machines which need fcntl.h in + sys. + +Tue Apr 4 11:29:04 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * valprint.c (set_prettyprint_command, set_unionprint_command, + format_info): Created. + (_initialize_valprint): Added to lists of commands. + + * gdb.texinfo [Backtrace]: Added a section describing the format + if symbols have not yet been read in. + + * valprint.c (val_print): Added code to prettyprint structures if + "prettyprint" is set and only to print unions below the top level + if "unionprint" is set. + + * infcmd.c (registers_info), valprint.c (value_print, val_print): + Added argument to call to val_print indicating deptch of recursion. + + * symtab.[ch] (find_pc_psymbol): Created; finds static function + psymbol with value nearest to but under value passed. + * stack.c (print_frame_info): Used above to make sure I have best + fit to pc value. + + * symseg.h (struct partial_symbol): Added value field. + * dbxread.c (read_dbx_symtab): Set value field for partial symbols + saved (so that we can lookup static symbols). + + * symtab.[ch] (find_pc_symtab): Changed to external. + * stack.c (select_frame): Call above to make sure that symbols for + a selected frame is readin. + +Mon Apr 3 12:48:16 1989 Randall Smith (randy at plantaris.ai.mit.edu) + + * stack.c (print_frame_info): Modified to only print out full + stack frame info on symbols whose tables have been read in. + * symtab.c, symtab.h (find_pc_psymtab): Made function external; + above needed it. + + * main.c (,set_verbose_command, initialize_main): Created a + variable "info_verbose" which says to talk it up in various and + sundry places. Added command to set this variable. + * gdb.texinfo (GDB Output): Added documentation on "set verbose" + and changed the name of the "Screen Output" section to "GDB + Output". + * dbxread.c (psymtab_to_symtab): Added information message about + symbol readin. Conditionalized on above. + + * dbxread.c (define_symbol): Made an "i" constant be of class + LOC_CONST and an "r" constant be of class LOC_CONST_BYTES. + + * README: Made a note about modifications which may be necessary + to the manual for this version of gdb. + + * blockframe.c (get_prev_frame_info): Now we get saved address and + check for validity before we check for leafism. This means that + we will catch the fact that we are in start, but we will miss any + fns that start calls without an fp. This should be fine. + + * m-*.h (FRAME_CHAIN): Modified to return 0 if we are in start. + This is usually a test for within the first object file. + * m-sparc.h (FRAME_CHAIN): The test here is simply if the fp saved + off the the start sp is 0. + + * blockframe.c (get_prev_frame_info): Removed check to see if we + were in start. Screws up sparc. + + * m-sparc.h (FRAME_FIND_SAVED_REGISTERS): Changed test for dummy + frame to not need frame to be innermost. + + * gdb.texinfo: Added section on frameless invocations of functions + and when gdb can and can't deal with this. + + * stack.c (frame_info): Disallowed call if no inferior or core + file; fails gracefully if truely bad stack specfication has been + given (ie. parse_frame_specification returns 0). + +Fri Mar 31 13:59:33 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * infrun.c (normal_stop): Changed references to "unset-env" to + "delete env". + + * infcmd.c (_initialize_infcmd): Change reference to set-args in + help run to "set args". + + * remote.c (getpkt): Allow immediate quit when reading from + device; it could be hung. + + * coffread.c (process_coff_symbol): Modify handling of REG + parameter symbols. + +Thu Mar 30 15:27:23 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * dbxread.c (symbol_file_command): Use malloc to allocate the + space for the string table in symbol_file_command (and setup a + cleanup for this). This allows a more graceful error failure if + there isn't any memory availible (and probably allows more memory + to be avail, depending on the machine). + + Additional mods for handling GNU C++ (from Tiemann): + * dbxread.c (read_type): Added case for '#' type (method type, I + believe). + (read_struct_type): If type code is undefined, make the main + variant for the type be itself. Allow recognition of bad format + in reading of structure fields. + * eval.c (evaluate_subexp): Modify evaluation of a member of a + structure and pointer to same to make sure that the syntax is + being used correctly and that the member is being accessed correctly. + * symseg.h: Added TYPE_CODE_METHOD to enum type_code. Add a + pointer to an array of argument types to the type structure. + * symtab.c (lookout_method_type, smash_to_method_type): Created. + * symtab.h (TYPE_ARG_TYPES): Created. + * valops.c (call_function): Modified handling of methods to be the + same as handling of functions; no longer check for members. + * valprint.c (val_print, type_print_varspec_{prefix,suffix}, + type_print_base): Added code to print method args correctly. + * values.c (value_virtual_fn_field): Modify access to virtual + function table. + +Wed Mar 29 13:19:34 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * findvar.c: Special cases for REGISTER_WINDOWS: 1) Return 0 if we + are the innermost frame, and 2) return the next frame in's value + if the SP is being looked for. + + * blockframe.c (get_next_frame): Created; returns the next (inner) + frame of the called frame. + * frame.h: Extern delcaration for above. + + * main.c (command_line_input): Stick null at end before doing + history expansion. + +Tue Mar 28 17:35:50 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * dbxread.c (read_dbx_symtab): Added namestring assignment to + N_DATA/BSS/ABS case. Sigh. + +Sat Mar 25 17:49:07 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * expread.y: Defined YYDEBUG. + +Fri Mar 24 20:46:55 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * symtab.c (make_symbol_completion_list): Completely rewrote to + never call psymtab_to_symtab, to do a correct search (no + duplicates) through the visible symbols, and to include structure + and union fields in the things that it can match. + +Thu Mar 23 15:27:44 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * dbxread.c (dbx_create_type): Created; allocates and inits space + for a type without putting it on the type vector lists. + (dbx_alloc_type): Uses above. + + * Makefile: xgdb.o now produced by default rules for .o.c. + +Fri Mar 17 14:27:50 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * infrun.c: Fixed up inclusion of aouthdr.h on UMAX_PTRACE. + + * Makefile, config.gdb: Added hp300bsd to potential + configurations. + * hp300bsd-dep.c, m-hp300bsd.h: Created. + + * infrun.c (wait_for_inferior): Rewrote to do no access to + inferior until we make sure it's still there. + + * inflow.c (inferior_died): Added a select to force the selected + frame to null when inferior dies. + + * dbxread.c (symbol_file_command): free and zero symfile when + discarding symbols. + + * core.c (xfer_core_file): Extended and cleaned up logic in + interpeting memory address. + + * core.c (xfer_core_file): Extended opening comment. + +Thu Mar 16 15:39:42 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * coffread.c (symbol_file_command): Free symfile name when freeing + contents. + + * blockframe.c (get_prev_frame_info): Added to fatal error message + to indicate that it should never happen. + + * stack.c (frame_info): Printed out value of "saved" sp seperately + to call attention to the fact that it isn't stored in memory + anywhere; the actual previous frames address is printed. + + * m-sparc.h (FRAME_FIND_SAVED_REGS): Set address of sp saved in + frame to value of fp (rather than value of sp in current frame). + + * expread.y: Allow "unsigned" as a type itself, as well as a type + modifier. + + * coffread.c: Added declaration for fclose + +Fri Mar 10 17:22:31 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * main.c (command_line_input): Checked for -1 return from + readline; indicates EOF. + +Fri Mar 3 00:31:27 1989 Randall Smith (randy at gluteus.ai.mit.edu) + + * remote.c (remote_open): Cast return from signal to (void (*)) to + avoid problems on machines where the return type of signal is (int + (*)). + + * Makefile: Removed deletion of version control from it (users + will need it for their changes). + +Thu Mar 2 15:32:21 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * symmetry-dep.c (print_1167_regs): Print out effective doubles on + even number regs. + (fetch_inferior_registers): Get the floating point regs also. + + * xgdb.c (do_command): Copied command before calling execute + command (so that execute_command wouldn't write into text space). + + * copying.awk: Created (will produce copying.c as output when + given COPYING as input). + * Makefile: Used above to create copying.c. + * main.c: Took out info_warranty and info_copying. + + * *.*: Changed copyright notice to use new GNU General Public + License (includes necessary changes to manual). + + * xgdb.c (create_text_widget): Created text_widget before I create + the source and sink. + (print_prompt): Added fflush (stdout). + + * Makefile: Added -lXmu to the compilation line for xgdb. Left + the old one there incase people still had R2. + + * README: Added note about -gg format. + + * remote.c (getpkt): Fixed typo; && ==> &. + + * Makefile: Added new variable READLINE_FLAGS so that I could + force compilation of readline.c and history.c with -DSYSV on + system V machines. Mentioned in Makefile comments at top. + +Wed Mar 1 17:01:01 1989 Randall Smith (randy at gluteus.ai.mit.edu) + + * hp9k320-dep.c (store_inferior_registers): Fixed typo. + +Fri Feb 24 14:58:45 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * hp9k320-dep.c (store_inferior_registers, + fetch_inferior_registers): Added support for remote debugging. + + * remote.c (remote_timer): Created. + (remote_open, readchar): Setup to timeout reads if they take + longer than "timeout". This allows one to debug how long such + things take. + (putpkt): Modified to print a debugging message (if such things + are enabled) each time it resends a packet. + (getpkt): Modified to make the variable CSUM unsigned and read it + CSUM with an & 0xff (presumably to deal with poor sign extension + on some machines). Also made c1 and c2 unsigned. + (remote_wait): Changed buffer to unsigned status. + (remote_store_registers, remote_write_bytes): Puts a null byte at + the end of the control string. + + * infcmd.c (attach_command, detach_command, _initialize_infcmd): + Made attach_command and detach_command always availible, but + modified them to only allow device file attaches if ATTACH_DETACH + is not defined. + + * gdb.texinfo: Added cross reference from attach command to remote + debugging. + +Thu Feb 23 12:37:59 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * remote.c (remote_close): Created to close the remote connection + and set the remote_debugging flag to 0. + * infcmd.c (detach_command): Now calls the above when appropriate. + + * gdb.texinfo: Removed references to the ``Distribution'' section + in the copyright. + + * main.c, utils.c (ISATTY): Created default defintions of this + macro which use isatty and fileno. + * utils.c (fprintf_filtered, print_spaces_filtered), main.c + (command_loop, command_line_input): Used this macro. + * m-news.h: Created a definition to override this one. + + * utils.c (fprintf_filtered): Made line_size static (clueless). + + * utils.c (fprintf_filtered): Changed max length of line printed + to be 255 chars or twice the format length. + + * symmetry-dep.c, m-symmetry: Fixed typo (^L ==> ). + + * printcmd.c (do_examine): Fixed typo (\n ==> \t). + +Wed Feb 22 16:00:33 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + Contributed by Jay Vosburgh (jay@mentor.cc.purdue.edu) + * m-symmetry.h, symmetry-dep.c: Created. + * Makefile: Added above in appropriate lists. + * config.gdb: Added "symmetry" target. + + * utils.c (prompt_for_continue): Zero'd chars_printed also. + + * utils.c (fprintf_filtered): Call prompt for continue instead of + doing it yourself. + + * dbxread.c (read_dbx_symtab): Added code to conditionalize what + symbol type holds to "x.o" or "-lx" symbol that indicates the + beginning of a new file. + +Tue Feb 21 16:22:13 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * gdb.texinfo: Deleted @ignore block at end of file. + + * findvar.c, stack.c: Changed comments that refered to "frame + address" to "frame id". + + * findvar.c (locate_var_value): Modified so that taking the + address of an array generates an object whose type is a pointer to + the elements of the array. + +Sat Feb 18 16:35:14 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * gdb.texinfo: Removed reference to "!" as a shell escape + character. Added a section on controling screen output + (pagination); changing "Input" section to "User Interface" + section. Changed many inappropriate subsubsection nodes into + subsections nodes (in the readline and history expansion + sections). + +Fri Feb 17 11:10:54 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * utils.c (set_screensize_command): Created. + (_initialize_utils): Added above to setlist. + + * main.c (main): Added check to see if ~/.gdbinit and .gdbinit + were the same file; only one gets read if so. Had to include + sys/stat.h for this. + + * valprint.c (type_print_base): Changed calls to print_spaces to + print_spaces_filtered. + + * main.c (command_line_input): Chaned test for command line + editing to check for stdin and isatty. + + * main.c (command_loop): Call reinitialize_more_filter before each + command (if reading from stdin and it's a tty). + utils.c (initialize_more_filter): Changed name to + reinitialize_more_filter; killed arguments. + utils.c (_initialize_utils): Created; initialized lines_per_page + and chars_per_line here. + + * utils.c (fprintf_filtered): Removed printing of "\\\n" after + printing linesize - 1 chars; assume that the screen display will + take care of that. Still watching that overflow. + + * main.c: Created the global variables linesize and pagesize to + describe the number of chars per line and lines per page. + +Thu Feb 16 17:27:43 1989 Randall Smith (randy at gluteus.ai.mit.edu) + + * printcmd.c (do_examine, print_scalar_formatted, print_address, + whatis_command, do_one_display, ptype_command), valprint.c + (value_print, val_print, type_print_method_args, type_print_1, + type_print_derivation_info, type_print_varspec_suffix, + type_print_base), breakpoint.c (breakpoints_info, breakpoint_1), + values.c (history_info), main.c (editing_info, warranty_info, + copying_info), infcmd.c (registers_info), inflow.c + (term_status_command), infrun.c (signals_info), stack.c + (backtrace_command, print_frame_info), symtab.c (list_symbols, + output_source_filename), command.c (help_cmd, help_list, + help_command_list): Replaced calls to printf, fprintf, and putc + with calls to [f]printf_filtered to handle more processing. + Killed local more emulations where I noticed them. + +Wed Feb 15 15:27:36 1989 Randall Smith (randy at gluteus.ai.mit.edu) + + * defs.h, utils.c (initialize_more_filter, fprintf_filtered, + printf_filtered): Created a printf that will also act as a more + filter, prompting the user for a whenever the page length + is overflowed. + + * symtab.c (list_symbols): Elminated some code inside of an #if 0. + +Tue Feb 14 11:11:24 1989 Randall Smith (randy at gluteus.ai.mit.edu) + + * Makefile: Turned off backup versions for this file; it changes + too often. + + * command.c (lookup_cmd, _initialize_command): Changed '!' so that + it was no longer a shell escape. "sh" must be used. + + * main.c (command_line_input, set_history_expansion, + initialize_main): Turned history expansion on, made it the + default, and only execute it if the first character in the line is + a '!'. + + * version.c, gdb.texinfo: Moved version to 3.2 (as usual, jumping + the gun some time before release). + + * gdb.texinfo: Added sections (adapted from Brian's notes) on + command line editing and history expansion. + + * main.c (set_command_editing, initialize_main): Modified name to + set_editing and modified command to "set editing". + + * Makefile: Put in dependencies for READLINEOBJS. + + * main.c (history_info, command_info): Combined into new command + info; deleted history_info. + (initialize_main): Deleted "info history" command; it was + interfering with the value history. + + * coffread.c (enter_linenos): Modified to do bit copy instead of + pointer dereference, since the clipper machine can't handle having + longs on short boundaries. + (read_file_hdr): Added code to get number of syms for clipper. + + * stack.c (return_command): Fixed method for checking when all of + the necessary frames had been popped. + + * dbxread.c (read_dbx_symtab (ADD_PSYMBOL_TO_LIST)): Fixed typo in + allocation length. + +Mon Feb 13 10:03:27 1989 Randall Smith (randy at gluteus.ai.mit.edu) + + * dbxread.c (read_dbx_symtab): Split assignment to namestring into + several different assignments (so that it wouldn't be done except + when it had to be). Shortened switches and duplicated code to + produce the lowest possible execution time. Commented (at top of + switch) which code I duplicated. + + * dbxread.c (read_dbx_symtab): Modified which variables were + register and deleted several variables which weren't used. Also + eliminated 'F' choice from subswitch, broke out strcmp's, reversed + compare on line 1986, and elminated test for !namestring[0]; it is + caught by following test for null index of ':'. + +Sun Feb 12 12:57:56 1989 Randall Smith (randy at plantaris.ai.mit.edu) + + * main.c (gdb_completer_word_break_characters): Turned \~ into ~. + +Sat Feb 11 15:39:06 1989 Randall Smith (randy at plantaris.ai.mit.edu) + + * symtab.c (find_pc_psymtab): Created; checks all psymtab's till + it finds pc. + (find_pc_symtab): Used; fatal error if psymtab found is readin + (should have been caught in symtab loop). + (lookup_symbol): Added check before scan through partial symtab + list for symbol name to be on the misc function vector (only if in + VAR_NAMESPACE). Also made sure that psymtab's weren't fooled with + if they had already been read in. + (list_symbols): Checked through misc_function_vector for matching + names if we were looking for functions. + (make_symbol_completion_list): Checked through + misc_function_vector for matching names. + * dbxread.c (read_dbx_symtab): Don't bother to do processing on + global function types; this will be taken care of by the + misc_function hack. + + * symtab.h: Modified comment on misc_function structure. + +Fri Feb 10 18:09:33 1989 Randall Smith (randy at plantaris.ai.mit.edu) + + * symseg.h, dbxread.c (read_dbx_symtab, init_psymbol_list, + start_psymtab, end_psymtab), coffread.c (_initialize_coff), + symtab.c (lookup_partial_symbol, list_symbols, + make_symbol_completion_list): Changed separate variables for + description of partial symbol allocation into a specific kind of + structure. + + (read_dbx_symtab, process_symbol_for_psymtab): Moved most of + process_symbol_for_psymtab up into read_dbx_symtab, moved a couple + of symbol types down to the ingore section, streamlined (I hope) + code some, modularized access to psymbol lists. + +Thu Feb 9 13:21:19 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * main.c (command_line_input): Made sure that it could recognize + newlines as indications to repeat the last line. + + * symtab.c (_initialize_symtab): Changed size of builtin_type_void + to be 1 for compatibility with gcc. + + * main.c (initialize_main): Made history_expansion the default + when gdb is compiled with HISTORY_EXPANSION. + + * readline.c, readline.h, history.c, history.h, general.h, + emacs_keymap.c, vi_keymap.c, keymaps.c, funmap.c: Made all of + these links to /gp/gnu/bash/* to keep them updated. + * main.c (initialize_main): Made default be command editing on. + +Wed Feb 8 13:32:04 1989 & Smith (randy at hobbes) + + * dbxread.c (read_dbx_symtab): Ignore N_BSLINE on first + readthrough. + + * Makefile: Removed convex-dep.c from list of distribution files. + +Tue Feb 7 14:06:25 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * main.c: Added command lists sethistlist and unsethistlist to + accesible command lists. + (parse_binary_operation): Created to parse a on/1/yes vs. off/0/no + spec. + (set_command_edit, set_history, set_history_expansion, + set_history_write, set_history_size, set_history_filename, + command_info, history_info): Created to allow users to control + various aspects of command line editing. + + * main.c (symbol_creation_function): Created. + (command_line_input, initialize_main): Added rest of stuff + necessary for calling bfox' command editing routines under + run-time control. + * Makefile: Included readline and history source files for command + editing; also made arrangements to make sure that the termcap + library was available. + * symtab.c (make_symbol_completion_list): Created. + +Mon Feb 6 16:25:25 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * main.c: Invented variables to control command editing. + command_editing_p, history_expansion_p, history_size, + write_history_p, history_filename. Initialized them to default + values in initialize_main. + + * infcmd.c (registers_info), infrun.c (signals_info), + * main.c (gdb_read_line): Changed name to command_line_input. + (readline): Changed name to gdb_readline; added second argument + indicating that the read value shouldn't be saved (via malloc). + * infcmd.c (registers_info), infrun.c (signals_info), main.c + (copying_info), symtab.c (output_source_filename, MORE, + list_symbols): Converted to use gdb_readline in place of + gdb_read_line. + + +Sun Feb 5 17:34:38 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * blockframe.c (get_frame_saved_regs): Removed macro expansion + that had accidentally been left in the code. + +Sat Feb 4 17:54:14 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * main.c (gdb_read_line, readline): Added function readline and + converted gdb_read_line to use it. This was a conversion to the + line at a time style of input, in preparation for full command + editing. + +Fri Feb 3 12:39:03 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * dbxread.c (read_dbx_symtab): Call end_psymtab at the end of + read_dbx_symtab if any psymtab still needs to be completed. + + * config.gdb, sun3-dep.c: Brought these into accord with the + actual sun2 status (no floating point period; sun3-dep.c unless + has os > 3.0). + * m-sun2os2.h: Deleted; not needed. + + * config.gdb: Added a couple of aliases for machines in the + script. + + * infrun.c: Added inclusion of aouthdr.h inside of #ifdef UMAX + because ptrace needs to know about the a.out header. + + * Makefile: Made dep.o depend on dep.c and config.status only. + + * expread.y: Added declarations of all of the new write_exp_elt + functions at the include section in the top. + + * Makefile: Added a YACC definition so that people can use bison + if they wish. + + * Makefile: Added rms' XGDB-README to the distribution. + + * Makefile: Added removal of init.o on a "make clean". + +Thu Feb 2 16:27:06 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * *-dep.c: Deleted definition of COFF_FORMAT if AOUTHDR was + defined since 1) We *may* (recent mail message) want to define + AOUTHDR under a basically BSD system, and 2) AOUTHDR is sometimes + a typedef in coff encapsulation setups. Also removed #define's of + AOUTHDR if AOUTHDR is already defined (inside of coff format). + * core.c, dbxread.c: Removed #define's of AOUTHDR if AOUTHDR is + already defined (inside of coff format). + +Tue Jan 31 12:56:01 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * GDB 3.1 released. + + * values.c (modify_field): Changed test for endianness to assign + to integer and reference character (so that all bits would be + defined). + +Mon Jan 30 11:41:21 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * news-dep.c: Deleted inclusion of fcntl.h; just duplicates stuff + found in sys/file.h. + + * i386-dep.c: Included default definition of N_SET_MAGIC for + COFF_FORMAT. + + * config.gdb: Added checks for several different operating + systems. + + * coffread.c (read_struct_type): Put in a flag variable so that + one could tell when you got to the end of a structure. + + * sun3-dep.c (core_file_command): Changed #ifdef based on SUNOS4 + to ifdef based on FPU. + + * infrun.c (restore_inferior_status): Changed error message to + "unable to restore previously selected frame". + + * dbxread.c (read_dbx_symtab): Used intermediate variable in error + message reporting a bad symbol type. (scan_file_globals, + read_ofile_symtab, read_addl_syms): Data type of "type" changed to + unsigned char (which is what it is). + * i386-dep.c: Removed define of COFF_FORMAT if AOUTHDR is defined. + Removed define of a_magic to magic (taken care of by N_MAGIC). + (core_file_command): Zero'd core_aouthdr instead of setting magic + to zero. + * i386-pinsn.c: Changed jcxz == jCcxz in jump table. + (putop): Added a case for 'C'. + (OP_J): Added code to handle possible masking of PC value on + certain kinds of data. + m-i386gas.h: Moved COFF_ENCAPSULATE to before inclusion of + m-i386.h and defined NAMES_HAVE_UNDERSCORE. + + * coffread.c (unrecrod_misc_function, read_coff_symtab): Added + symbol number on which error occured to error output. + +Fri Jan 27 11:55:04 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * Makefile: Removed init.c in make clean. Removed it without -f + and with leading - in make ?gdb. + +Thu Jan 26 15:08:03 1989 Randall Smith (randy at gluteus.ai.mit.edu) + + Changes to get it to work on gould NP1. + * dbxread.c (read_dbx_symtab): Included cases for N_NBDATA and + N_NBBSS. + (psymtab_to_symtab): Changed declaration of hdr to + DECLARE_FILE_HEADERS. Changed access to use STRING_TABLE_SIZE and + SYMBOL_TABLE_SIZE. + * gld-pinsn.c (findframe): Added declaration of framechain() as + FRAME_ADDR. + + * coffread.c (read_coff_symtab): Avoided treating typedefs as + external symbol definitions. + +Wed Jan 25 14:45:43 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * Makefile: Removed reference to alloca.c. If they need it, they + can pull alloca.o from the gnu-emacs directory. + + * version.c, gdb.texinfo: Updated version to 3.1 (jumping the gun + a bit so that I won't forget when I release). + + * m-sun2.h, m-sun2os2.h, m-sun3os4.h, config.gdb: Modified code so + that default includes new sun core, ptrace, and attach-detach. + Added defaults for sun 2 os 2. + + Modifications to reset stack limit back to what it used to be just + before exec. All mods inside of #ifdef SET_STACK_LIMIT_HUGE. + * main.c: Added global variable original_stack_limit. + (main): Set original_stack_limit to original stack limit. + * inflow.c: Added inclusion of necessary files and external + reference to original_stack_limit. + (create_inferior): Reset stack limit to original_stack_limit. + + * dbxread.c (read_dbx_symtab): Killed PROFILE_SYMBOLS ifdef. + + * sparc-dep.c (isabranch): Multiplied offset by 4 before adding it + to addr to get target. + + * Makefile: Added definition of SHELL to Makefile. + + * m-sun2os4.h: Added code to define NEW_SUN_PTRACE, NEW_SUN_CORE, + and ATTACH_DETACH. + * sun3-dep.c: Added code to avoid fp regs if we are on a sun2. + +Tue Jan 24 17:59:14 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * dbxread.c (read_array_type): Added function. + (read_type): Added call to above instead of inline code. + + * Makefile: Added ${GNU_MALLOC} to the list of dependencies for + the executables. + +Mon Jan 23 15:08:51 1989 Randall Smith (randy at plantaris.ai.mit.edu) + + * gdb.texinfo: Added paragraph to summary describing languages + with which gdb can be run. Also added descriptions of the + "info-methods" and "add-file" commands. + + * symseg.h: Commented a range type as having TYPE_TARGET_TYPE + pointing at the containing type for the range (often int). + * dbxread.c (read_range_type): Added code to do actual range types + if they are defined. Assumed that the length of a range type is + the length of the target type; this is a lie, but will do until + somebody gets back to me as to what these silly dbx symbols mean. + + * dbxread.c (read_range_type): Added code to be more picky about + recognizing builtins as range types, to treat types defined as + subranges of themselves to be subranges of int, and to recognize + the char type idiom from dbx as a special case. + +Sun Jan 22 01:00:13 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * m-vax.h: Removed definition of FUNCTION_HAS_FRAME_POINTER. + * blockframe.c (get_prev_frame_info): Removed default definition + and use of above. Instead conditionalized checking for leaf nodes + on FUNCTION_START_OFFSET (see comment in code). + +Sat Jan 21 16:59:19 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * dbxread.c (read_range_type): Fixed assumption that integer was + always type 1. + + * gdb.texinfo: Fixed spelling mistake and added a note in the + running section making it clear that users may invoke subroutines + directly from gdb. + + * blockframe.c: Setup a default definition for the macro + FUNCTION_HAS_FRAME_POINTER. + (get_prev_frame_info): Used this macro instead of checking + SKIP_PROLOGUE directly. + * m-vax.h: Overroad definition; all functions on the vax have + frame pointers. + +Fri Jan 20 12:25:35 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * core.c: Added default definition of N_MAGIC for COFF_FORMAT. + + * xgdb.c: Installed a fix to keep the thing from dying when there + isn't any frame selected. + + * core.c: Made a change for the UMAX system; needs a different + file included if using that core format. + + * Makefile: Deleted duplicate obstack.h in dbxread.c dependency. + + * munch: Modified (much simpler) to cover (I hope) all cases. + + * utils.c (save_cleanups, restore_cleanups): Added functions to + allow you to push and pop the chain of cleanups to be done. + * defs.h: Declared the new functions. + * main.c (catch_errors): Made sure that the only cleanups which + would be done were the ones put on the chain *after* the current + location. + + * m-*.h (FRAME_CHAIN_VALID): Removed check on pc in the current + frame being valid. + * blockframe.c (get_prev_frame_info): Made the assumption that if + a frame's pc value was within the first object file (presumed to + be /lib/crt0.o), that we shouldn't go any higher. + + * infrun.c (wait_for_inferior): Do *not* execute check for stop pc + at step_resume_break if we are proceeding over a breakpoint (ie. + if trap_expected != 0). + + * Makefile: Added -g to LDFLAGS. + + * m-news.h (POP_FRAME) Fixed typo. + + * printcmd.c (print_frame_args): Modified to print out register + params in order by .stabs entry, not by register number. + + * sparc-opcode.h: Changed declaration of (struct + arith_imm_fmt).simm to be signed (as per architecture manual). + * sparc-pinsn.c (fprint_addr1, print_insn): Forced a cast to an + int, so that we really would get signed behaivior (default for sun + cc is unsigned). + + * i386-dep.c (i386_get_frame_setup): Replace function with new + function provided by pace to fix bug in recognizing prologue. + +Thu Jan 19 11:01:22 1989 Randall Smith (randy at plantaris.ai.mit.edu) + + * infcmd.c (run_command): Changed error message to "Program not + restarted." + + * value.h: Changed "frame" field in value structure to be a + FRAME_ADDR (actually CORE_ADDR) so that it could survive across + calls. + + * m-sun.h (FRAME_FIND_SAVED_REGS): Fixed a typo. + + * value.h: Added lval: "lval_reg_frame_relative" to indicate a + register that must be interpeted relative to a frame. Added + single entry to value structure: "frame", used to indicate which + frame a relative regnum is relative to. + * findvar.c (value_from_register): Modified to correctly setup + these fields when needed. Deleted section to fiddle with last + register copied on little endian machine; multi register + structures will always occupy an integral number of registers. + (find_saved_register): Made extern. + * values.c (allocate_value, allocate_repeat_value): Zero frame + field on creation. + * valops.c (value_assign): Added case for lval_reg_frame_relative; + copy value out, modify it, and copy it back. Desclared + find_saved_register as being external. + * value.h: Removed addition of kludgy structure; thoroughly + commented file. + * values.c (free_value, free_all_values, clear_value_history, + set_internalvar, clear_internavars): Killed free_value. + +Wed Jan 18 20:09:39 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * value.h: Deleted struct partial_storage; left over from + yesterday. + + * findvar.c (value_from_register): Added code to create a value of + type lval_reg_partsaved if a value is in seperate registers and + saved in different places. + +Tue Jan 17 13:50:18 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * value.h: Added lval_reg_partsaved to enum lval_type and + commented enum lval_type. Commented value structure. + Added "struct partial_register_saved" to value struct; added + macros to deal with structure to value.h. + * values.c (free_value): Created; special cases lval_reg_partsaved + (which has a pointer to an array which also needs to be free). + (free_all_values, clear_value_history, set_internalvar, + clear_internalvars): Modified to use free_values. + + * m-sunos4.h: Changed name to sun3os4.h. + * m-sun2os4.h, m-sun4os4.h: Created. + * config.gdb: Added configuration entries for each of the above. + * Makefile: Added into correct lists. + + * Makefile: Added dependencies on a.out.encap.h. Made + a.out.encap.h dependent on a.out.gnu.h and dbxread.c dependent on + stab.gnu.h. + + * infrun.c, remote.c: Removed inclusion of any a.out.h files in + these files; they aren't needed. + + * README: Added comment about bug reporting and comment about + xgdb. + + * Makefile: Added note to HPUX dependent section warning about + problems if compiled with gcc and mentioning the need to add + -Ihp-include to CFLAGS if you compile on those systems. Added a + note about needing the GNU nm with compilers *of gdb* that use the + coff encapsulate feature also. * hp-include: Made symbolic link + over to /gp/gnu/binutils. + + * Makefile: Added TSOBS NTSOBS OBSTACK and REGEX to list of things + to delete in "make clean". Also changed "squeakyclean" target as + "realclean". + + * findvar.c (value_from_register): Added assignment of VALUE_LVAL + to be lval_memory when that is appropriate (original code didn't + bother because it assumed that it was working with a pre lval + memoried value). + + * expread.y (yylex): Changed to only return type THIS if the + symbol "$this" is defined in some block superior or equal to the + current expression context block. + +Mon Jan 16 13:56:44 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * m-*.h (FRAME_CHAIN_VALID): On machines which check the relation + of FRAME_SAVED_PC (thisframe) to first_object_file_end (all except + gould), make sure that the pc of the current frame also passes (in + case someone stops in _start). + + * findvar.c (value_of_register): Changed error message in case of + no inferior or core file. + + * infcmd.c (registers_info): Added a check for inferior or core + file; error message if not. + + * main.c (gdb_read_line): Modified to take prompt as argument and + output it to stdout. + * infcmd.c (registers_info, signals_info), main.c (command_loop, + read_command_lines, copying_info), symtab.c (decode_line_2, + output_source_filename, MORE, list_symbols): Changed calling + convention used to call gdb_read_line. + + * infcmd.c, infrun.c, main.c, symtab.c: Changed the name of the + function "read_line" to "gdb_read_line". + * breakpoint.c: Deleted external referenced to function + "read_line" (not needed by code). + +Fri Jan 13 12:22:05 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * i386-dep.c: Include a.out.encap.h if COFF_ENCAPSULATE. + (N_SET_MAGIC): Defined if not defined by include file. + (core_file_command): Used N_SET_MAGIC instead of assignment to + a_magic. + (exec_file_command): Stuck in a HEADER_SEEK_FD. + + * config.gdb: Added i386-dep.c as depfile for i386gas choice. + + * munch: Added -I. to cc to pick up things included by the param + file. + + * stab.gnu.def: Changed name to stab.def (stab.gnu.h needs this name). + * Makefile: Changed name here also. + * dbxread.c: Changed name of gnu-stab.h to stab.gnu.h. + + * gnu-stab.h: Changed name to stab.gnu.h. + * stab.gnu.def: Added as link to binutils. + * Makefile: Put both in in the distribution. + +Thu Jan 12 11:33:49 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * dbxread.c: Made which stab.h is included dependent on + COFF_ENCAPSULATE; either or "gnu-stab.h". + * Makefile: Included gnu-stab.h in the list of files to include in + the distribution. + * gnu-stab.h: Made a link to /gp/gnu/binutils/stab.h + + * Makefile: Included a.out.gnu.h and m-i386gas.h in list of + distribution files. + * m-i386gas.h: Changed to include m-i386.h and fiddle with it + instead of being a whole new file. + * a.out.gnu.h: Made a link to /gp/gnu/binutils/a.out.gnu.h. + + Chris Hanson's changes to gdb for hp Unix. + * Makefile: Modified comments on hpux. + * hp9k320-dep.c: #define'd WOPR & moved inclusion of signal.h + * inflow.c: Moved around declaratiosn of and + inside of USG depends and deleted all SYSV ifdef's + (use USG instead). + * munch: Modified to accept any number of spaces between the T and + the symbol name. + + Pace's changes to gdb to work with COFF_ENCAPSULATE (robotussin): + * config.gdb: Added i386gas to targets. + * default-dep.c: Include a.out.encap.h if COFF_ENCAPSULATE. + (N_SET_MAGIC): Defined if not defined by include file. + (core_file_command): Used N_SET_MAGIC instead of assignment to a_magic. + (exec_file_command): Stuck in a HEADER_SEEK_FD. + * infrun.c, remote.c: Added an include of a.out.encap.h if + COFF_ENCAPSULATE defined. This is commented out in these two + files, I presume because the definitions aren't used. + * m-i386gas.h: Created. + * dbxread.c: Included defintions for USG. + (READ_FILE_HEADERS): Now uses HEADER_SEEK_FD if it exists. + (symbol_file_command): Deleted use of HEADER_SEEK_FD. + * core.c: Deleted extra definition of COFF_FORMAT. + (N_MAGIC): Defined to be a_magic if not already defined. + (validate_files): USed N_MAGIC instead of reading a_magic. + +Wed Jan 11 12:51:00 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * remote.c: Upped PBUFSIZ. + (getpkt): Added zeroing of c inside loop in case of error retry. + + * dbxread.c (read_dbx_symtab, process_symbol_for_psymtab): Removed + code to not put stuff with debugging symbols in the misc function + list. Had been ifdef'd out. + + * gdb.texinfo: Added the fact that the return value for a function + is printed if you use return. + + * infrun.c (wait_for_inferior): Removed test in "Have we hit + step_resume_breakpoint" for sp values in proper orientation. Was + in there for recursive calls in functions without frame pointers + and it was screwing up calls to alloca. + + * dbxread.c: Added #ifdef COFF_ENCAPSULATE to include + a.out.encap.h. + (symbol_file_command): Do HEADER_SEEK_FD when defined. + * dbxread.c, core.c: Deleted #ifdef ROBOTUSSIN stuff. + * robotussin.h: Deleted local copy (was symlink). + * a.out.encap.h: Created symlink to + /gp/gnu/binutils/a.out.encap.h. + * Makefile: Removed robotussin.h and included a.out.encap.h in + list of files. + + * valprint.c (val_print, print_scalar_formatted): Changed default + precision of printing float value; now 6 for a float and 16 for a + double. + + * findvar.c (value_from_register): Added code to deal with the + case where a value is spread over several registers. Still don't + deal with the case when some registers are saved in memory and + some aren't. + +Tue Jan 10 17:04:04 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * xgdb.c (xgdb_create_window): Removed third arg (XtDepth) to + frameArgs. + + * infrun.c (handle_command): Error if signal number is less or + equal to 0 or greater or equal to NSIG or a signal number is not + provided. + + * command.c (lookup_cmd): Modified to not convert command section + of command line to lower case in place (in case it isn't a + subcommand, but an argument to a command). + +Fri Jan 6 17:57:34 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * dbxread.c: Changed "text area" to "data area" in comments on + N_SETV. + +Wed Jan 4 12:29:54 1989 Randall Smith (randy at gluteus.ai.mit.edu) + + * dbxread.c: Added definitions of gnu symbol types after inclusion + of a.out.h and stab.h. + +Mon Jan 2 20:38:31 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * eval.c (evaluate_subexp): Binary logical operations needed to + know type to determine whether second value should be evaluated. + Modified to discover type before binup_user_defined_p branch. + Also commented "enum noside". + + * Makefile: Changed invocations of munch to be "./munch". + + * gdb.texinfo: Updated to refer to current version of gdb with + January 1989 last update. + + * coffread.c (end_symtab): Zero context stack when finishing + lexical contexts. + (read_coff_symtab): error if context stack 0 in ".ef" else case. + + * m-*.h (FRAME_SAVED_PC): Changed name of argument from "frame" to + "FRAME" to avoid problems with replacement of "->frame" part of + macro. + + * i386-dep.c (i386_get_frame_setup): Added codestream_get() to + move codestream pointer up to the correct location in "subl $X, + %esp" case. + +Sun Jan 1 14:24:35 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * valprint.c (val_print): Rewrote routine to print string pointed + to by char pointer; was producing incorrect results when print_max + was 0. + +Fri Dec 30 12:13:35 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * dbxread.c (read_dbx_symtab, process_symbol_for_psymtab): Put + everything on the misc function list. + + * Checkpointed distribution. + + * Makefile: Added expread.tab.c to the list of things slated for + distribution. + +Thu Dec 29 10:06:41 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * stack.c (set_backtrace_limit_command, backtrace_limit_info, + bactrace_command, _initialize_stack): Removed modifications for + limit on backtrace. Piping the backtrace through an interuptable + "more" emulation is a better way to do it. + +Wed Dec 28 11:43:09 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * stack.c + (set_backtrace_limit_command): Added command to set a limit to the + number of frames for a backtrace to print by default. + (backtrace_limit_info): To print the current limit. + (backtrace_command): To use the limit. + (_initialize_stack): To initialize the limit to its default value + (30), and add the set and info commands onto the appropriate + command lists. + + * gdb.texinfo: Documented changes to "backtrace" and "commands" + commands. + + * stack.c (backtrace_command): Altered so that a negative argument + would show the last few frames on the stack instead of the first + few. + (_initialize_stack): Modified help documentation. + + * breakpoint.c (commands_command): Altered so that "commands" with + no argument would refer to the last breakpoint set. + (_initialize_breakpoint): Modified help documentation. + + * infrun.c (wait_for_inferior): Removed ifdef on Sun4; now you can + single step through compiler generated sub calls and will die if + you next off of the end of a function. + + * sparc-dep.c (single_step): Fixed typo; "break_insn" ==> "sizeof + break_insn". + + * m-sparc.h (INIT_EXTRA_FRAME_INFO): Set the bottom of a stack + frame to be the bottom of the stack frame inner from this, if that + inner one is a leaf node. + + * dbxread.c (read_dbx_symtab): Check to make sure we don't add a + psymtab to it's own dependency list. + + * dbxread.c (read_dbx_symtab): Modified check for duplicate + dependencies to catch them correctly. + +Tue Dec 27 17:02:09 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * m-*.h (FRAME_SAVED_PC): Modified macro to take frame info + pointer as argument. + * stack.c (frame_info), blockframe.c (get_prev_frame_info), + gld-pinsn.c (findframe), m-*.h (SAVED_PC_AFTER_CALL, + FRAME_CHAIN_VALID, FRAME_NUM_ARGS): Changed usage of macros to + conform to above. + * m-sparc.h (FRAME_SAVED_PC), sparc-dep.c (frame_saved_pc): + Changed frame_saved_pc to have a frame info pointer as an + argument. + + * m-vax.h, m-umax.h, m-npl.h, infrun.c (wait_for_inferior), + blockframe.c (get_prev_frame_info): Modified SAVED_PC_AFTER_CALL + to take a frame info pointer as an argument. + + * blockframe.c (get_prev_frame_info): Altered the use of the + macros FRAME_CHAIN, FRAME_CHAIN_VALID, and FRAME_CHAIN_COMBINE to + use frame info pointers as arguments instead of frame addresses. + * m-vax.h, m-umax.h, m-sun3.h, m-sun3.h, m-sparc.h, m-pn.h, + m-npl.h, m-news.h, m-merlin.h, m-isi.h, m-hp9k320.h, m-i386.h: + Modified definitions of the above macros to suit. + * m-pn.h, m-npl.h, gould-dep.c (findframe): Modified findframe to + use a frame info argument; also fixed internals (wouldn't work + before). + + * m-sparc.h: Cosmetic changes; reordered some macros and made sure + that nothing went over 80 lines. + +Thu Dec 22 11:49:15 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * Version 3.0 released. + + * README: Deleted note about changing -lobstack to obstack.o. + +Wed Dec 21 11:12:47 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * m-vax.h (SKIP_PROLOGUE): Now recognizes gcc prologue also. + + * blockframe.c (get_prev_frame_info): Added FUNCTION_START_OFFSET + to result of get_pc_function_start. + * infrun.c (wait_for_inferior): Same. + + * gdb.texinfo: Documented new "step" and "next" behavior in + functions without line number information. + +Tue Dec 20 18:00:45 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * infcmd.c (step_1): Changed behavior of "step" or "next" in a + function witout line number information. It now sets the step + range around the function (to single step out of it) using the + misc function vector, warns the user, and continues. + + * symtab.c (find_pc_line): Zero "end" subsection of returned + symtab_and_line if no symtab found. + +Mon Dec 19 17:44:35 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * i386-pinsn.c (OP_REG): Added code from pace to streamline + disassembly and corrected types. + * i386-dep.c + (i386_follow_jump): Code added to follow byte and word offset + branches. + (i386_get_frame_setup): Expanded to deal with more wide ranging + function prologue. + (i386_frame_find_saved_regs, i386_skip_prologue): Changed to use + i386_get_frame_setup. + + +Sun Dec 18 11:15:03 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * m-sparc.h: Deleted definition of SUN4_COMPILER_BUG; was designed + to avoid something that I consider a bug in our code, not theirs, + and which I fixed earlier. Also deleted definition of + CANNOT_USE_ARBITRARY_FRAME; no longer used anywhere. + FRAME_SPECIFICATION_DYADIC used instead. + + * infrun.c (wait_for_inferior): On the sun 4, if a function + doesn't have a prologue, a next over it single steps into it. + This gets around the problem of a "call .stret4" at the end of + functions returning structures. + * m-sparc.h: Defined SUN4_COMPILER_FEATURE. + + * main.c (copying_info): Seperated the last printf into two + printfs. The 386 compiler will now handle it. + + * i386-pinsn.c, i386-dep.c: Moved print_387_control_word, + print_387_status_word, print_387_status, and i386_float_info to + dep.c Also included reg.h in dep.c. + +Sat Dec 17 15:31:38 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * main.c (source_command): Don't close instream if it's null + (indicating execution of a user-defined command). + (execute_command): Set instream to null before executing + commands and setup clean stuff to put it back on error. + + * inflow.c (terminal_inferior): Went back to not checking the + ioctl returns; there are some systems when this will simply fail. + It seems that, on most of these systems, nothing bad will happen + by that failure. + + * values.c (value_static_field): Fixed dereferencing of null + pointer. + + * i386-dep.c (i386_follow_jump): Modified to deal with + unconditional byte offsets also. + + * dbxread.c (read_type): Fixed typo in function type case of switch. + + * infcmd.c (run_command): Does not prompt to restart if command is + not from a tty. + +Fri Dec 16 15:21:58 1988 Randy Smith (randy at calvin) + + * gdb.texinfo: Added a third option under the "Cannot Insert + Breakpoints" workarounds. + + * printcmd.c (display_command): Don't do the display unless there + is an active inferior; only set it. + + * findvar.c (value_of_register): Added an error check for calling + this when the inferior isn't active and a core file isn't being + read. + + * config.gdb: Added reminder about modifying REGEX in the + makefile for the 386. + + * i386-pinsn.c, i386-dep.c: Moved m-i386.h helper functions over + to i386-dep.c.b + +Thu Dec 15 14:04:25 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * README: Added a couple of notes about compiling gdb with itself. + + * breakpoint.c (set_momentary_breakpoint): Only takes FRAME_FP of + frame if frame is non-zero. + + * printcmd.c (print_scalar_formatted): Implemented /g size for + hexadecimal format on machines without an 8 byte integer type. It + seems to be non-trivial to implement /g for other formats. + (decode_format): Allowed hexadecimal format to make it through /g + fileter. + +Wed Dec 14 13:27:04 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * expread.y: Converted all calls to write_exp_elt from the parser + to calls to one of write_exp_elt_{opcode, sym, longcst, dblcst, + char, type, intern}. Created all of these routines. This gets + around possible problems in passing one of these things in one ear + and getting something different out the other. Eliminated + SUN4_COMPILER_BUG ifdef's; they are now superfluous. + + * symmisc.c (free_all_psymtabs): Reinited partial_symtab_list to 0. + (_initialize_symmisc): Initialized both symtab_list and + partial_symtab_list. + + * dbxread.c (start_psymtab): Didn't allocate anything on + dependency list. + (end_psymtab): Allocate dependency list on psymbol obstack from + local list. + (add_psymtab_dependency): Deleted. + (read_dbx_symtab): Put dependency on local list if it isn't on it + already. + + * symtab.c: Added definition of psymbol_obstack. + * symtab.h: Added declaration of psymbol_obstack. + * symmisc.c (free_all_psymtabs): Added freeing and + reinitionaliztion of psymbol_obstack. + * dbxread.c (free_all_psymbols): Deleted. + (start_psymtab, end_psymtab, + process_symbol_for_psymtab): Changed most allocation + of partial symbol stuff to be off of psymbol_obstack. + + * symmisc.c (free_psymtab, free_all_psymtabs): Deleted + free_psymtab subroutine. + + * symtab.h: Removed num_includes and includes from partial_symtab + structure; no longer needed now that all include files have their + own psymtab. + * dbxread.c (start_psymtab): Eliminated initialization of above. + (end_psymtab): Eliminated finalization of above; get + includes from seperate list. + (read_dbx_symtab): Moved includes from psymtab list to + their own list; included in call to end_psymtab. + * symmisc.c (free_psymtab): Don't free includes. + +Tue Dec 13 14:48:14 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * i386-pinsn.c: Reformatted entire file to correspond to gnu + software indentation conventions. + + * sparc-dep.c (skip_prologue): Added capability of recognizign + stores of input register parameters into stack slots. + + * sparc-dep.c: Added an include of sparc-opcode.h. + * sparc-pinsn.c, sparc-opcode.h: Moved insn_fmt structures and + unions from pinsn.c to opcode.h. + * sparc-pinsn.c, sparc-dep.c (isabranch, skip_prologue): Moved + this function from pinsn.c to dep.c. + + * Makefile: Put in warnings about compiling with gcc (non-ansi + include files) and compiling with shared libs on Sunos 4.0 (can't + debug something that's been compiled that way). + + * sparc-pinsn.c: Put in a completely new file (provided by + Tiemann) to handle floating point disassembly, load and store + instructions, and etc. better. Made the modifications this file + (ChangeLog) list for sparc-pinsn.c again. + + * symtab.c (output_source_filename): Included "more" emulation hack. + + * symtab.c (output_source_filename): Initialized COLUMN to 0. + (sources_info): Modified to not print out a line for + all of the include files within a partial symtab (since + they have pst's of their own now). Also modified to + make a distinction between those pst's read in and + those not. + + * infrun.c: Included void declaration of single_step() if it's + going to be used. + * sparc-dep.c (single_step): Moved function previous to use of it. + + * Makefile: Took removal of expread.tab.c out of make clean entry + and put it into a new "squeakyclean" entry. + +Mon Dec 12 13:21:02 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * sparc-pinsn.c (skip_prologue): Changed a struct insn_fmt to a + union insn_fmt. + + * inflow.c (terminal_inferior): Checked *all* return codes from + ioctl's and fcntl's in routine. + + * inflow.c (terminal_inferior): Added check for sucess of + TIOCSPGRP ioctl call. Just notifies if bad. + + * dbxread.c (symbol_file_command): Close was getting called twice; + once directly and once through cleanup. Killed the direct call. + +Sun Dec 11 19:40:40 1988 & Smith (randy at hobbes.ai.mit.edu) + + * valprint.c (val_print): Deleted spurious printing of "=" from + TYPE_CODE_REF case. + +Sat Dec 10 16:41:07 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * dbxread.c: Changed allocation of psymbols from using malloc and + realloc to using obstacks. This means they aren't realloc'd out + from under the pointers to them. + +Fri Dec 9 10:33:24 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * sparc-dep.c inflow.c core.c expread.y command.c infrun.c + infcmd.c dbxread.c symmisc.c symtab.c printcmd.c valprint.c + values.c source.c stack.c findvar.c breakpoint.c blockframe.c + main.c: Various cleanups inspired by "gcc -Wall" (without checking + for implicit declarations). + + * Makefile: Cleaned up some more. + + * valops.c, m-*.h (FIX_CALL_DUMMY): Modified to take 5 arguments + as per what sparc needs (programming for a superset of needed + args). + + * dbxread.c (process_symbol_for_psymtab): Modified to be slightly + more picky about what it puts on the list of things *not* to be + put on the misc function list. When/if I shift everything over to + being placed on the misc_function_list, this will go away. + + * inferior.h, infrun.c: Added fields to save in inferior_status + structure. + + * maketarfile: Deleted; functionality is in Makefile now. + + * infrun.c (wait_for_inferior): Modified algorithm for determining + whether or not a single-step was through a subroutine call. See + comments at top of file. + + * dbxread.c (read_dbx_symtab): Made sure that the IGNORE_SYMBOL + macro would be checked during initial readin. + + * dbxread.c (read_ofile_symtab): Added macro GCC_COMPILED_FLAG_SYMBOL + into dbxread.c to indicate what string in a local text symbol will + indicate a file compiled with gcc. Defaults to "gcc_compiled.". + +Thu Dec 8 11:46:22 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * m-sparc.h (FRAME_FIND_SAVED_REGS): Cleaned up a little to take + advantage of the new frame cache system. + + * inferior.h, infrun.c, valops.c, valops.c, infcmd.c: Changed + mechanism to save inferior status over calls to inferior (eg. + call_function); implemented save_inferior_info and + restore_inferior_info. + + * blockframe.c (get_prev_frame): Simplified this by a direct call + to get_prev_frame_info. + + * frame.h, stack.c, printcmd.c, m-sparc.h, sparc-dep.c: Removed + all uses of frame_id_from_addr. There are short routines like it + still in frame_saved_pc (m-sparc.h) and parse_frame_spec + (stack.c). Eventually the one in frame_saved_pc will go away. + + * infcmd.c, sparc-dep.c: Implemented a new mechanism for + re-selecting the selected frame on return from a call. + + * blockframe.c, stack.c, findvar.c, printcmd.c, m-*.h: Changed + all routines and macros that took a "struct frame_info" as an + argument to take a "struct frame_info *". Routines: findarg, + framechain, print_frame_args, FRAME_ARGS_ADDRESS, + FRAME_STRUCT_ARGS_ADDRESS, FRAME_LOCALS_ADDRESS, FRAME_NUM_ARGS, + FRAME_FIND_SAVED_REGS. + + * frame.h, stack.c, printcmd.c, infcmd.c, findvar.c, breakpoint.c, + blockframe.c, xgdb.c, i386-pinsn.c, gld-pinsn.c, m-umax.h, + m-sun2.h, m-sun3.h, m-sparc.h, m-pn.h, m-npl.h, m-news.h, + m-merlin.h, m-isi.h, m-i386.h, m-hp9k320.h: Changed routines to + use "struct frame_info *" internally. + +Wed Dec 7 12:07:54 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * frame.h, blockframe.c, m-sparc.h, sparc-dep.c: Changed all calls + to get_[prev_]frame_cache_item to get_[prev_]frame_info. + + * blockframe.c: Elminated get_frame_cache_item and + get_prev_frame_cache_item; functionality now taken care of by + get_frame_info and get_prev_frame_info. + + * blockframe.c: Put allocation on an obstack and eliminated fancy + reallocation routines, several variables, and various nasty + things. + + * frame.h, stack.c, infrun.c, blockframe.c, sparc-dep.c: Changed + type FRAME to be a typedef to "struct frame_info *". Had to also + change routines that returned frame id's to return the pointer + instead of the cache index. + + * infcmd.c (finish_command): Used proper method of getting from + function symbol to start of function. Was treating a symbol as a + value. + + * blockframe.c, breakpoint.c, findvar.c, infcmd.c, stack.c, + xgdb.c, i386-pinsn.c, frame.h, m-hp9k320.h, m-i386.h, m-isi.h, + m-merlin.h, m-news.h, m-npl.h, m-pn.h, m-sparc.h, m-sun2.h, + m-sun3.h, m-umax.h: Changed get_frame_info and get_prev_frame_info + to return pointers instead of structures. + + * blockframe.c (get_pc_function_start): Modified to go to misc + function table instead of bombing if pc was in a block without a + containing function. + + * coffread.c: Dup'd descriptor passed to read_coff_symtab and + fdopen'd it so that there wouldn't be multiple closes on the same + fd. Also put (fclose, stream) on the cleanup list. + + * printcmd.c, stack.c: Changed print_frame_args to take a + frame_info struct as argument instead of the address of the args + to the frame. + + * m-i386.h (STORE_STRUCT_RETURN): Decremented sp by sizeof object + to store (an address) rather than 1. + + * dbxread.c (read_dbx_symtab): Set first_object_file_end in + read_dbx_symtab (oops). + + * coffread.c (fill_in_vptr_fieldno): Rewrote TYPE_BASECLASS as + necessary. + +Tue Dec 6 13:03:43 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * coffread.c: Added fake support for partial_symtabs to allow + compilation and execution without there use. + * inflow.c: Added a couple of minor USG mods. + * munch: Put in appropriate conditionals so that it would work on + USG systems. + * Makefile: Made regex.* handled same as obstack.*; made sure tar + file included everything I wanted it to include (including + malloc.c). + + * dbxread.c (end_psymtab): Create an entry in the + partial_symtab_list for each subfile of the .o file just read in. + This allows a "list expread.y:10" to work when we haven't read in + expread.o's symbol stuff yet. + + * symtab.h, dbxread.c (psymtab_to_symtab): Recognize pst->ldsymlen + == 0 as indicating a dummy psymtab, only in existence to cause the + dependency list to be read in. + + * dbxread.c (sort_symtab_syms): Elminated reversal of symbols to + make sure that register debug symbol decls always come before + parameter symbols. After mod below, this is not needed. + + * symtab.c (lookup_block_symbol): Take parameter type symbols + (LOC_ARG or LOC_REGPARM) after any other symbols which match. + + * dbxread.c (read_type): When defining a type in terms of some + other type and the other type is supposed to have a pointer back + to this specific kind of type (pointer, reference, or function), + check to see if *that* type has been created yet. If it has, use + it and fill in the appropriate slot with a pointer to it. + +Mon Dec 5 11:25:04 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * symmisc.c: Eliminated existence of free_inclink_symtabs and + init_free_inclink_symtabs; they aren't called from anywhere, and + if they were they could disrupt gdb's data structure badly + (elimination of struct type's which values that stick around past + elimination of inclink symtabs). + + * dbxread.c (symbol_file_command): Fixed a return pathway out of + the routine to do_cleanups before it left. + + * infcmd.c (set_environment_command), gdb.texinfo: Added + capability to set environmental variable values to null. + + * gdb.texinfo: Modified doc on "break" without args slightly. + +Sun Dec 4 17:03:16 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * dbxread.c (symbol_file_command): Added check; if there weren't + any debugging symbols in the file just read, the user is warned. + + * infcmd.c: Commented set_environment_command (a little). + + * createtags: Cleaned up and commented. + + * Makefile: Updated depen_memory and write_inferior_memory in that errno is + checked after each ptrace and returned to the caller. Used in + value_at to detect references to addresses which are out of + bounds. Also core.c (xfer_core_file): return 1 if invalid + address, 0 otherwise. + + * inflow.c, -infdep.c: removed all calls to ptrace from + inflo, m-sun3.h: Cleaned up dealings with + functions returning structu0 19:19:36 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * symmisc.c: (read_symsegs) Accept only format number 2. Since + the size of the type structure changed when C++ support was added, + format 1 can no longer be used. + + * core.c, m-sunos4.h: (core_file_command) support for SunOS 4.0. + Slight change in the core structure. #ifdef SUNOS4. New file + m-sunos4.h. May want to change config.gdb also. + +Fri Jul 8 19:59:49 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * breakpoint.c: (break_command_1) Allow `break if condition' + rather than parsing `if' as a function name and returning an + error. + +Thu Jul 7 22:22:47 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * C++: valops.c, valprint.c, value.h, values.c: merged code to deal + with C++ expressions. + +Wed Jul 6 03:28:18 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * C++: dbxread.c: (read_dbx_symtab, condense_misc_bunches, + add_file_command) Merged code to read symbol information from + an incrementally linked file. symmisc.c: + (init_free_inclink_symtabs, free_inclink_symtabs) Cleanup + routines. + +Tue Jul 5 02:50:41 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * C++: symtab.c, breakpoint.c, source.c: Merged code to deal with + ambiguous line specifications. In C++ one can have overloaded + function names, so that `list classname::overloadedfuncname' + refers to several different lines, possibly sure currently configured machine + dependent files come first in e at corn-chex.ai.mit.edu) + + * C++: symtab.c: replaced lookup_symtab_1 and lookup_symtab_2 with + a modified lookup_symbol which checks for fields of the current + implied argument `this'. printcmd.c, source.c, symtab.c, + valops.c: Need to change callers once callers are + installed. + +Wed Jun 29 01:26:56 1988 Peter TerMaat (pete at frosted-flakes.ai.mit.edu) + + * C++: eval.c, expprint.c, expread.y, expression.h, valarith.c, + Merged code to deal with evaluation of user-defined operators, + member functions, and virtual functions. + binop_must_be_user_defined tests for user-defined binops, + value_x_binop calls the appropriate operator function. + +Tue Jun 28 02:56:42 1988 Peter TerMaat (pete at frosted-flakes.ai.mit.edu) + + * C++: Makefile: changed the echo: expect 101 shift/reduce conflicts + and 1 reduce/reduce conflict. + + +Local Variables: +mode: indented-text +eval: (auto-fill-mode 1) +left-margin: 8 +fill-column: 74 +version-control: never +End: +ng destructors and + constructors, and flags being defined via public and via + virtual paths. Added fields NEXT_VARIANT, N_BASECLASSES, + and BASECLASSES to this type (tr: Changed types from + having to be derived from a single baseclass to a multiple + base class). + * symtab.h: Added macros to access new fields defined in symseg.h. + Added decl for lookup_basetype_type. + * dbxread.c + (condense_addl_misc_bunches): Function added to condense the misc + function bunches added by reading in a new .o file. + (read_addl_syms): Function added to read in symbols + from a new .o file (incremental linking). + (add_file_command): Command interface function to indicate + incrmental linking of a new .o file; this now calls + read_addl_syms and condense_addl_misc_bunches. + (define_symbol): Modified code to handle types defined from base + types which were not known when the derived class was + output. + (read_struct_type): Modified to better handle description of + struct types as derived types. Possibly derived from + several different base classes. Also added new code to + mark definitions via virtual paths or via public paths. + Killed seperate code to handle classes with destructors + but without constructors and improved marking of classes + as having destructors and constructors. + * infcmd.c: Modified call to val_print (one more argument). + * symtab.c (lookup_member_type): Modified to deal with new + structure in symseg.h. + (lookup_basetype_type): Function added to find or construct a type + ?derived? from the given type. + (decode_line_1): Modified to deal with new type data structures. + Modified to deal with new number of args for + decode_line_2. + (decode_line_2): Changed number of args (?why?). + (init_type): Added inits for new C++ fields from + symseg.h. + *valarith.c + (value_x_binop, value_binop): Added cases for BINOP_MIN & + BINOP_MAX. + * valops.c + (value_struct_elt, check_field, value_struct_elt_for_address): + Changed to deal with multiple possible baseclasses. + (value_of_this): Made SELECTED_FRAME an extern variable. + * valprint.c + (val_print): Added an argument DEREF_REF to dereference references + automatically, instead of printing them like pointers. + Changed number of arguments in recursive calls to itself. + Changed to deal with varibale numbers of base classes. + (value_print): Changed number of arguments to val_print. Print + type of value also if value is a reference. + (type_print_derivation_info): Added function to print out + derivation info a a type. + (type_print_base): Modified to use type_print_derivation_info and + to handle multiple baseclasses. + +Mon Nov 21 10:32:07 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * inflow.c (term_status_command): Add trailing newline to output. + + * sparc-dep.c (do_save_insn, do_restore_insn): Saved + "stop_registers" over the call for the sake of normal_stop and + run_stack_dummy. + + * m-sparc.h (EXTRACT_RETURN_VALUE): Put in parenthesis to force + addition of 8 to the int pointer, not the char pointer. + + * sparc-pinsn.c (print_addr1): Believe that I have gotten the + syntax right for loads and stores as adb does it. + + * symtab.c (list_symbols): Turned search for match on rexegp into + a single loop. + + * dbxread.c (psymtab_to_symtab): Don't read it in if it's already + been read in. + + * dbxread.c (psymtab_to_symtab): Changed error to fatal in + psymtab_to_symtab. + + * expread.y (parse_number): Fixed bug which treated 'l' at end of + number as '0'. + +Fri Nov 18 13:57:33 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * dbxread.c (read_dbx_symtab, process_symbol_for_psymtab): Was + being foolish and using pointers into an array I could realloc. + Converted these pointers into integers. + +Wed Nov 16 11:43:10 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * m-sparc.h (POP_FRAME): Made the new frame be PC_ADJUST of the + old frame. + + * i386-pinsn.c, m-hp9k320.h, m-isi.h, m-merlin.h, m-news.h, + m-npl.h, m-pn.h, m-sparc.h, m-sun2.h, m-sun3.h, m-umax.h, m-vax.h: + Modified POP_FRAME to use the current frame instead of + read_register (FP_REGNUM) and to flush_cached_frames before + setting the current frame. Also added a call to set the current + frame in those POP_FRAMEs that didn't have it. + + * infrun.c (wait_for_inferior): Moved call to set_current_frame up + to guarrantee that the current frame will always be set when a + POP_FRAME is done. + + * infrun.c (normal_stop): Added something to reset the pc of the + current frame (was incorrect because of DECR_PC_AFTER_BREAK). + + * valprint.c (val_print): Changed to check to see if a string was + out of bounds when being printed and to indicate this if so. + + * convex-dep.c (read_inferior_memory): Changed to return the value + of errno if the call failed (which will be 0 if the call + suceeded). + +Tue Nov 15 10:17:15 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * infrun.c (wait_for_inferior): Two changes: 1) Added code to + not trigger the step breakpoint on recursive calls to functions + without frame info, and 2) Added calls to distinguish recursive + calls within a function without a frame (which next/nexti might + wish to step over) from jumps to the beginning of a function + (which it generally doesn't). + + * m-sparc.h (INIT_EXTRA_FRAME_INFO): Bottom set correctly for leaf + parents. + + * blockframe.c (get_prev_frame_cache_item): Put in mod to check + for a leaf node (by presence or lack of function prologue). If + there is a leaf node, it is assumed that SAVED_PC_AFTER_CALL is + valid. Otherwise, FRAME_SAVED_PC or read_pc is used. + + * blockframe.c, frame.h: Did final deletion of unused routines and + commented problems with getting a pointer into the frame cache in + the frame_info structure comment. + + * blockframe.c, frame.h, stack.c: Killed use of + frame_id_from_frame_info; used frame_id_from_addr instead. + + * blockframe.c, frame.h, stack.c, others (oops): Combined stack + cache and frame info structures. + + * blockframe.c, sparc-dep.c, stack.c: Created the function + create_new_frame and used it in place of bad calls to + frame_id_from_addr. + + * blockframe.c, inflow.c, infrun.c, i386-pinsn.c, m-hp9k320.h, + m-npl.h, m-pn.h, m-sparc.h, m-sun3.h, m-vax.h, default-dep.c, + convex-dep.c, gould-dep.c, hp9k320-dep.c, news-dep.c, sparc-dep.c, + sun3-dep.c, umax-dep.c: Killed use of + set_current_Frame_by_address. Used set_current_frame + (create_new_frame...) instead. + + * frame.h: Killed use of FRAME_FP_ID. + + * infrun.c, blockframe.c: Killed select_frame_by_address. Used + select_frame (get_current_frame (), 0) (which was correct in all + cases that we need to worry about. + +Mon Nov 14 14:19:32 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * frame.h, blockframe.c, stack.c, m-sparc.h, sparc-dep.c: Added + mechanisms to deal with possible specification of frames + dyadically. + +Sun Nov 13 16:03:32 1988 Richard Stallman (rms at sugar-bombs.ai.mit.edu) + + * ns32k-opcode.h: Add insns acbw, acbd. + +Sun Nov 13 15:09:58 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * breakpoint.c: Changed breakpoint structure to use the address of + a given frame (constant across inferior runs) as the criteria for + stopping instead of the frame ident (which varies across inferior + calls). + +Fri Nov 11 13:00:22 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * gld-pinsn.c (findframe): Modified to work with the new frame + id's. Actually, it looks as if this routine should be called with + an address anyway. + + * findvar.c (find_saved_register): Altered bactrace loop to work + off of frames and not frame infos. + + * frame.h, blockframe.c, stack.c, sparc-dep.c, m-sparc.h: Changed + FRAME from being the address of the frame to being a simple ident + which is an index into the frame_cache_item list. + * convex-dep.c, default-dep.c, gould-dep.c, hp9k320-dep.c, + i386-pinsn.c, inflow.c, infrun.c, news-dep.c, sparc-dep.c, + sun3-dep.c, umax-dep.c, m-hp9k320.h, m-npl.h, m-pn.h, m-sparc.h, + m-sun3.h, m-vax.h: Changed calls of the form set_current_frame + (read_register (FP_REGNUM)) to set_current_frame_by_address (...). + +Thu Nov 10 16:57:57 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * frame.h, blockframe.c, gld-pinsn.c, sparc-dep.c, stack.c, + infrun.c, findvar.c, m-sparc.h: Changed the FRAME type to be + purely an identifier, using FRAME_FP and FRAME_FP_ID to convert + back and forth between the two. The identifier is *currently* + still the frame pointer value for that frame. + +Wed Nov 9 17:28:14 1988 Chris Hanson (cph at kleph) + + * m-hp9k320.h (FP_REGISTER_ADDR): Redefine this to return + difference between address of given FP register, and beginning of + `struct user' that it occurs in. + + * hp9k320-dep.c (core_file_command): Fix sign error in size + argument to myread. Change buffer argument to pointer; was + copying entire structure. + (fetch_inferior_registers, store_inferior_registers): Replace + occurrences of `FP_REGISTER_ADDR_DIFF' with `FP_REGISTER_ADDR'. + Flush former definition. + +Wed Nov 9 12:11:37 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * xgdb.c: Killed include of initialize.h. + + * Pulled in xgdb.c from the net. + + * Checkpointed distribution (to provide to 3b2 guy). + + * coffread.c, dbxread.c, symmisc.c, symtab.c, symseg.h: Changed + format of table of line number--pc mapping information. Can + handle negative pc's now. + + * command.c: Deleted local copy of savestring; code in utils.c is + identical. + +Tue Nov 8 11:12:16 1988 Randall Smith (randy at plantaris.ai.mit.edu) + + * gdb.texinfo: Added documentation for shell escape. + +Mon Nov 7 12:27:16 1988 Randall Smith (randy at sugar-bombs.ai.mit.edu) + + * command.c: Added commands for shell escape. + + * core.c, dbxread.c: Added ROBOTUSSIN mods. + + * Checkpointed distribution. + + * printcmd.c (x_command): Yanked error if there is no memory to + examine (could be looking at executable straight). + + * sparc-pinsn.c (print_insn): Amount to leftshift sethi imm by is + now 10 (matches adb in output). + + * printcmd.c (x_command): Don't attempt to set $_ & $__ if there + is no last_examine_value (can happen if you did an x/0). + +Fri Nov 4 13:44:49 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * printcmd.c (x_command): Error if there is no memory to examine. + + * gdb.texinfo: Added "cont" to the command index. + + * sparc-dep.c (do_save_insn): Fixed typo in shift amount. + + * m68k-opcode.h: Fixed opcodes for 68881. + + * breakpoint.c, infcmd.c, source.c: Changed defaults in several + places for decode_line_1 to work off of the default_breakpoint_* + values instead of current_source_* values (the current_source_* + values are off by 5 or so because of listing defaults). + + * stack.c (frame_info): ifdef'd out FRAME_SPECIFCATION_DYADIC in + the stack.c module. If I can't do this right, I don't want to do + it at all. Read the comment there for more info. + +Mon Oct 31 16:23:06 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * gdb.texinfo: Added documentation on the "until" command. + +Sat Oct 29 17:47:10 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * breakpoint.c, infcmd.c: Added UNTIL_COMMAND and subroutines of + it. + + * breakpoint.c, infcmd.c, infrun.c: Added new field to breakpoint + structure (silent, indicating a silent breakpoint), and modified + breakpoint_stop_status and things that read it's return value to + understand it. + +Fri Oct 28 17:45:33 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * dbxread.c, symmisc.c: Assorted speedups for readin, including + special casing most common symbols, and doing buffering instead of + calling malloc. + +Thu Oct 27 11:11:15 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * stack.c, sparc-dep.c, m-sparc.h: Modified to allow "info frame" + to take two arguments on the sparc and do the right thing with + them. + + * dbxread.c (read_dbx_symtab, process_symbol_for_psymtab): Put + stuff to put only symbols that didn't have debugging info on the + misc functions list back in. + +Wed Oct 26 10:10:32 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * valprint.c (type_print_varspec_suffix): Added check for + TYPE_LENGTH(TYPE_TARGET_TYPE(type)) > 0 to prevent divide by 0. + + * printcmd.c (print_formatted): Added check for VALUE_REPEATED; + value_print needs to be called for that. + + * infrun.c (wait_for_inferior): Added break when you decide to + stop on a null function prologue rather than continue stepping. + + * m-sun3.h: Added explanatory comment to REGISTER_RAW_SIZE. + + * expread.y (parse_c_1): Initialized paren_depth for each parse. + +Tue Oct 25 14:19:38 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * valprint.c, coffread.c, dbxread.c: Enum constant values in enum + type now accessed through TYPE_FIELD_BITPOS. + + * dbxread.c (process_symbol_for_psymtab): Added code to deal with + possible lack of a ":" in a debugging symbol (do nothing). + + * symtab.c (decode_line_1): Added check in case of all numbers for + complete lack of symbols. + + * source.c (select_source_symtab): Made sure that this wouldn't + bomb on complete lack of symbols. + +Mon Oct 24 12:28:29 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * m-sparc.h, findvar.c: Ditched REGISTER_SAVED_UNIQUELY and based + code on REGISTER_IN_WINDOW_P and HAVE_REGISTER_WINDOWS. This will + break when we find a register window machine which saves the + window registers within the context of an inferior frame. + + * sparc-dep.c (frame_saved_pc): Put PC_ADJUST return back in for + frame_saved_pc. Seems correct. + + * findvar.c, m-sparc.h: Created the macro REGISTER_SAVED_UNIQUELY + to handle register window issues (ie. that find_saved_register + wasn't checking the selected frame itself for shit). + + * sparc-dep.c (core_file_command): Offset target of o & g register + bcopy by 1 to hit correct registers. + + * m-sparc.h: Changed STACK_END_ADDR. + +Sun Oct 23 19:41:51 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * sparc-dep.c (core_file_command): Added in code to get the i & l + registers from the stack in the corefile, and blew away some wrong + code to get i & l from inferior. + +Fri Oct 21 15:09:19 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * m-sparc.h (PUSH_DUMMY_FRAME): Saved the value of the RP register + in the location reserved for i7 (in the created frame); this way + the rp value won't get lost. The pc (what we put into the rp in + this routine) gets saved seperately, so we loose no information. + + * sparc-dep.c (do_save_insn & do_restore_insn): Added a wrapper to + preserve the proceed status state variables around each call to + proceed (the current frame was getting munged because this wasn't + being done). + + * m-sparc.h (FRAME_FIND_SAVED_REGS): Fix bug: saved registers + addresses were being computed using absolute registers number, + rather than numbers relative to each group of regs. + + * m-sparc.h (POP_FRAME): Fixed a bug (I hope) in the context + within which saved reg numbers were being interpetted. The + values to be restored were being gotten in the inferior frame, and + the restoring was done in the superior frame. This means that i + registers must be restored into o registers. + + * sparc-dep.c (do_restore_insn): Modified to take a pc as an + argument, instead of a raw_buffer. This matches (at least it + appears to match) usage from POP_FRAME, which is the only place + from which do_restore_insn is called. + + * sparc-dep.c (do_save_insn and do_restore_insn): Added comments. + + * m-sparc.h (FRAME_FIND_SAVED_REGS): Modified my code to find the + save addresses of out registers to use the in regs off the stack + pointer when the current frame is 1 from the innermost. + +Thu Oct 20 13:56:15 1988 & Smith (randy at hobbes.ai.mit.edu) + + * blockframe.c, m-sparc.h: Removed code associated with + GET_PREV_FRAME_FROM_CACHE_ITEM. This code was not needed for the + sparc; you can always find the previous frames fp from the fp of + the current frame (which is the sp of the previous). It's getting + the information associated with a given frame (ie. saved + registers) that's a bitch, because that stuff is saved relative to + the stack pointer rather than the frame pointer. + + * m-sparc.h (GET_PREV_FRAME_FROM_CACHE_ITEM): Modified to return + the frame pointer of the previous frame instead of the stack + pointer of same. + + * blockframe.c (flush_cached_frames): Modified call to + obstack_free to free back to frame_cache instead of back to zero. + This leaves the obstack control structure in finite state (and + still frees the entry allocated at frame_cache). + +Sat Oct 15 16:30:47 1988 & Smith (randy at tartarus.uchicago.edu) + + * valops.c (call_function): Suicide material here. Fixed a typo; + CALL_DUMMY_STACK_ADJUST was spelled CAll_DUMMY_STACK_ADJUST on + line 530 of the file. This cost me three days. I'm giving up + typing for lent. + +Fri Oct 14 15:10:43 1988 & Smith (randy at tartarus.uchicago.edu) + + * m-sparc.h: Corrected a minor mistake in the dummy frame code + that was getting the 5th argument and the first argument from the + same place. + +Tue Oct 11 11:49:33 1988 & Smith (randy at tartarus.uchicago.edu) + + * infrun.c: Made stop_after_trap and stop_after_attach extern + instead of static so that code which used proceed from machine + dependent files could fiddle with them. + + * blockframe.c, frame.h, sparc-dep.c, m-sparc.h: Changed sense of + ->prev and ->next in struct frame_cache_item to fit usage in rest + of gdb (oops). + +Mon Oct 10 15:32:42 1988 Randy Smith (randy at gargoyle.uchicago.edu) + + * m-sparc.h, sparc-dep.c, blockframe.c, frame.h: Wrote + get_frame_cache_item. Modified FRAME_SAVED_PC and frame_saved_pc + to take only one argument and do the correct thing with it. Added + the two macros I recently defined in blockframe.c to m-sparc.h. + Have yet to compile this thing on a sparc, but I've now merged in + everything that I received from tiemann, either exactly, or simply + effectively. + + * source.c: Added code to allocated space to sals.sals in the case + where no line was specified. + + * blockframe.c, infrun.c: Modified to cache stack frames requested + to minimize accesses to subprocess. + +Tue Oct 4 15:10:39 1988 Randall Smith (randy at cream-of-wheat.ai.mit.edu) + + * config.gdb: Added sparc. + +Mon Oct 3 23:01:22 1988 Randall Smith (randy at cream-of-wheat.ai.mit.edu) + + * Makefile, blockframe.c, command.c, core.c, dbxread.c, defs.h, + expread.y, findvar.c, infcmd.c, inflow.c, infrun.c, sparc-pinsn.c, + m-sparc.h, sparc-def.c, printcmd.c, stack.c, symmisc.c, symseg.h, + valops.c, values.c: Did initial merge of sparc port. This will + not compile; have to do stack frame caching and finish port. + + * inflow.c, gdb.texinfo: `tty' now resets the controling terminal. + +Fri Sep 30 11:31:16 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * inferior.h, infcmd.c, infrun.c: Changed the variable + stop_random_signal to stopped_by_random signal to fit in better + with name conventions (variable is not a direction to the + proceed/resume set; it is information from it). + +Thu Sep 29 13:30:46 1988 Randall Smith (randy at cream-of-wheat.ai.mit.edu) + + * infcmd.c (finish_command): Value type of return value is now + whatever the function returns, not the type of the function (fixed + a bug in printing said value). + + * dbxread.c (read_dbx_symtab, process_symbol_for_psymtab): + Put *all* global symbols into misc_functions. This is what was + happening anyway, and we need it for find_pc_misc_function. + + ** This was eventually taken out, but I didn't mark it in the + ChangeLog. Oops. + + * dbxread.c (process_symbol_for_psymtab): Put every debugger + symbol which survives the top case except for constants on the + symchain. This means that all of these *won't* show up in misc + functions (this will be fixed once I make sure it's broken the way + it's supposed to be). + + * dbxread.c: Modified placement of debugger globals onto the hash + list; now we exclude the stuff after the colon and don't skip the + first character (debugger symbols don't have underscores). + + * dbxread.c: Killed debuginfo stuff with ifdef's. + +Wed Sep 28 14:31:51 1988 Randall Smith (randy at cream-of-wheat.ai.mit.edu) + + * symtab.h, dbxread.c: Modified to deal with BINCL, EINCL, and + EXCL symbols produced by the sun loader by adding a list of + pre-requisite partial_symtabs that each partial symtab needs. + + * symtab.h, dbxread.c, symtab.c, symmisc.c: Modified to avoid + doing a qsort on the local (static) psymbols for each file to + speed startup. This feature is not completely debugged, but it's + inclusion has forced the inclusion of another feature (dealing + with EINCL's, BINCL's and EXCL's) and so I'm going to go in and + deal with them. + + * dbxread.c (process_symbol_for_psymtab): Made sure that the class + of the symbol made it into the partial_symbol entry. + +Tue Sep 27 15:10:26 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * dbxread.c: Fixed bug; init_psymbol_list was not being called + with the right number of arguments (1). + + * dbxread.c: Put ifdef's around N_MAIN, N_M2C, and N_SCOPE to + allow compilation on a microvax. + + * config.gdb: Modified so that "config.gdb vax" would work. + + * dbxread.c, symtab.h, symmisc.h, symtab.c, source.c: Put in many + and varied hacks to speed up gdb startup including: A complete + rewrite of read_dbx_symtab, a modification of the partial_symtab + data type, deletion of select_source_symtab from + symbol_file_command, and optimiztion of the call to strcmp in + compare_psymbols. + +Thu Sep 22 11:08:54 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * dbxread.c (psymtab_to_symtab): Removed call to + init_misc_functions. + + * dbxread.c: Fixed enumeration type clash (used enum instead of + integer constant). + + * breakpoint.c: Fixed typo; lack of \ at end of line in middle of + string constant. + + * symseg.h: Fixed typo; lack of semicolon after structure + definition. + + * command.c, breakpoint.c, printcmd.c: Added cmdlist editing + functions to add commands with the abbrev flag set. Changed + help_cmd_list to recognize this flag and modified unset, + undisplay, and enable, disable, and delete breakpoints to have + this flag set. + +Wed Sep 21 13:34:19 1988 Randall Smith (randy at plantaris.ai.mit.edu) + + * breakpoint.c, infcmd.c, gdb.texinfo: Created "unset" as an alias + for delete, and changed "unset-environment" to be the + "environment" subcommand of "delete". + + * gdb.texinfo, valprint.c: Added documentation in the manual for + breaking the set-* commands into subcommands of set. Changed "set + maximum" to "set array-max". + + * main.c, printcmd.c, breakpoint.c: Moved the declaration of + command lists into main and setup a function in main initializing + them to guarrantee that they would be initialized before calling + any of the individual files initialize routines. + + * command.c (lookup_cmd): A null string subcommand is treated as + an unknown subcommand rather than an ambiguous one (eg. "set $x = + 1" will now work). + + * infrun.c (wait_for_inferior): Put in ifdef for Sony News in + check for trap by INNER_THAN macro. + + * eval.c (evaluate_subexp): Put in catch to keep the user from + attempting to call a non function as a function. + +Tue Sep 20 10:35:53 1988 Randall Smith (randy at oatmeal.ai.mit.edu) + + * dbxread.c (read_dbx_symtab): Installed code to keep track of + which global symbols did not have debugger symbols refering to + them, and recording these via record_misc_function. + + * dbxread.c: Killed code to check for extra global symbols in the + debugger symbol table. + + * printcmd.c, breakpoint.c: Modified help entries for several + commands to make sure that abbreviations were clearly marked and + that the right commands showed up in the help listings. + + * main.c, command.c, breakpoint.c, infcmd.c, printcmd.c, + valprint.c, defs.h: Modified help system to allow help on a class + name to show subcommands as well as commands and help on a command + to show *all* subcommands of that command. + +Fri Sep 16 16:51:19 1988 Randall Smith (randy at gluteus.ai.mit.edu) + + * breakpoint.c (_initialize_breakpoint): Made "breakpoints" + subcommands of enable, disable, and delete use class 0 (ie. they + show up when you do a help xxx now). + + * infcmd.c,printcmd,c,main.c,valprint.c: Changed the set-* + commands into subcommands of set. Created "set variable" for use + with variables whose names might conflict with other subcommands. + + * blockframe.c, dbxread.c, coffread.c, expread.y, source.c: + Fixed mostly minor (and one major one in block_for_pc) bugs + involving checking the partial_symtab_list when a scan through the + symtab_list fails. + +Wed Sep 14 12:02:05 1988 Randall Smith (randy at sugar-smacks.ai.mit.edu) + + * breakpoint.c, gdb.texinfo: Added enable breakpoints, disable + breakpoints and delete breakpoints as synonyms for enable, + disable, and delete. This seemed reasonable because of the + immeninent arrival of watchpoints & etc. + + * gdb.texinfo: Added enable display, disable display, and delete + display to manual. + +Tue Sep 13 16:53:56 1988 Randall Smith (randy at sugar-smacks.ai.mit.edu) + + * inferior.h, infrun.c, infcmd.c: Added variable + stop_random_signal to indicate when a proceed had been stopped by + an unexpected signal. Used this to determine (in normal_stop) + whether the current display point should be deleted. + + * valops.c: Fix to value_ind to check for reference before doing a + COERCE_ARRAY. + +Sun Jul 31 11:42:36 1988 Richard Stallman (rms at frosted-flakes.ai.mit.edu) + + * breakpoint.c (_initialize_breakpoint): Clean up doc for commands + that can now apply also to auto-displays. + + * coffread.c (record_line): Corrected a spazz in editing. + Also removed the two lines that assume line-numbers appear + only in increasing order. + +Tue Jul 26 22:19:06 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * expression.h, eval.c, expprint.c, printcmd.c, valarith.c, + valops.c, valprint.c, values.c, m-*.h: Changes for evaluating and + displaying 64-bit `long long' integers. Each machine must define + a LONGEST type, and a BUILTIN_TYPE_LONGEST. + + * symmisc.c: (print_symtab) check the status of the fopen and call + perror_with_name if needed. + +Thu Jul 21 00:56:11 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * Convex: core.c: changes required by Convex's SOFF format were + isolated in convex-dep.c. + +Wed Jul 20 21:26:10 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * coffread.c, core.c, expread.y, i386-pinsn.c, infcmd.c, inflow.c, + infrun.c, m-i386.h, main.c, remote.c, source.c, valops.c: + Improvements for the handling of the i386 and other machines + running USG. (Several of these files just needed extra header files + such as types.h.) utils.c: added bcopy, bcmp, bzero, getwd, list + of signals, and queue routines for USG systems. Added vfork macro + to i386 + + * printcmd.c, breakpoint.c: New commands to enable/disable + auto-displays. Also `delete display displaynumber' works like + `undisplay displaynumber'. + +Tue Jul 19 02:17:18 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * coffread.c: (coff_lookup_type) Wrong portion of type_vector was + being bzero'd after type_vector was reallocated. + + * printcmd.c: (delete_display) Check for a display chain before + attempting to delete a display. + + * core.c, *-dep.c (*-infdep moved to *-dep): machine-dependent + parts of core.c (core_file_command, exec_file_command) moved to + *-dep.c. + +Mon Jul 18 19:45:51 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * dbxread.c: typo in read_struct_type (missing '=') was causing a + C struct to be parsed as a C++ struct, resulting in a `invalid + character' message. + +Sun Jul 17 22:27:32 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * printcmd.c, symtab.c, valops.c, expread.y: When an expression is + read, the innermost block required to evaluate the expression is + saved in the global variable `innermost_block'. This information + is saved in the `block' field of an auto-display so that + expressions with inactive variables can be skipped. `info display' + tells the user which displays are active and which are not. New + fn `contained_in' returns nonzero if one block is contained within + another. + +Fri Jul 15 01:53:14 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * infrun.c, m-i386.h: Use macro TRAPS_EXPECTED to set number of + traps to skip when sh execs the program. Default is 2, m-i386.h + overrides this and sets to 4. + + * coffread.c, infrun.c: minor changes for the i386. May be able + to eliminate them with more general code. + + * default-infdep.c: #ifdef SYSTEMV, include header file types.h. + Also switched the order of signal.h and user.h, since System 5 + requires signal.h to come first. + + * core.c main.c, remote,c, source.c, inflow.c: #ifdef SYSTEMV, + include various header files. Usually types.h and fcntl.h. + + * utils.c: added queue routines needed by the i386 (and other sys + 5 machines). + + * sys5.c, regex.c, regex.h: new files for sys 5 systems. (The + regex files are simply links to /gp/gnu/lib.) + +Thu Jul 14 01:47:14 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * config.gdb, README: Provide a list of known machines when user + enters an invalid machine. New second arg is operating system, + currently only used with `sunos4' or `os4'. Entry for i386 added. + + * news-infdep.c: new file. + + * m-news.h: new version which deals with new bugs in news800's OS. + +Tue Jul 12 19:52:16 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * Makefile, *.c, munch, config.gdb, README: New initialization + scheme uses nm to find functions whose names begin with + `_initialize_'. Files `initialize.h', `firstfile.c', + `lastfile.c', `m-*init.h' no longer needed. + + * eval.c, symtab.c, valarith.c, valops.c, value.h, values.c: Bug + fixes from gdb+ 2.5.4. evaluate_subexp takes a new arg, type + expected. New fn value_virtual_fn_field. + +Mon Jul 11 00:48:49 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * core.c (read_memory): xfer_core_file was being called with an + extra argument (0) by read_memory. + + * core.c (read_memory), *-infdep.c (read_inferior_memory), + valops.c (value_at): read_memory and read_inferior_memory now work + like write_memory and write_inferior_memory in that errno is + checked after each ptrace and returned to the caller. Used in + value_at to detect references to addresses which are out of + bounds. Also core.c (xfer_core_file): return 1 if invalid + address, 0 otherwise. + + * inflow.c, -infdep.c: removed all calls to ptrace from + inflow.c and put them in machine-dependent files *-infdep.c. + +Sun Jul 10 19:19:36 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * symmisc.c: (read_symsegs) Accept only format number 2. Since + the size of the type structure changed when C++ support was added, + format 1 can no longer be used. + + * core.c, m-sunos4.h: (core_file_command) support for SunOS 4.0. + Slight change in the core structure. #ifdef SUNOS4. New file + m-sunos4.h. May want to change config.gdb also. + +Fri Jul 8 19:59:49 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * breakpoint.c: (break_command_1) Allow `break if condition' + rather than parsing `if' as a function name and returning an + error. + +Thu Jul 7 22:22:47 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * C++: valops.c, valprint.c, value.h, values.c: merged code to deal + with C++ expressions. + +Wed Jul 6 03:28:18 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * C++: dbxread.c: (read_dbx_symtab, condense_misc_bunches, + add_file_command) Merged code to read symbol information from + an incrementally linked file. symmisc.c: + (init_free_inclink_symtabs, free_inclink_symtabs) Cleanup + routines. + +Tue Jul 5 02:50:41 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * C++: symtab.c, breakpoint.c, source.c: Merged code to deal with + ambiguous line specifications. In C++ one can have overloaded + function names, so that `list classname::overloadedfuncname' + refers to several different lines, possibly in different files. + +Fri Jul 1 02:44:20 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu) + + * C++: symtab.c: replaced lookup_symtab_1 and lookup_symtab_2 with + a modified lookup_symbol which checks for fields of the current + implied argument `this'. printcmd.c, source.c, symtab.c, + valops.c: Need to change callers once callers are + installed. + +Wed Jun 29 01:26:56 1988 Peter TerMaat (pete at frosted-flakes.ai.mit.edu) + + * C++: eval.c, expprint.c, expread.y, expression.h, valarith.c, + Merged code to deal with evaluation of user-defined operators, + member functions, and virtual functions. + binop_must_be_user_defined tests for user-defined binops, + value_x_binop calls the appropriate operator function. + +Tue Jun 28 02:56:42 1988 Peter TerMaat (pete at frosted-flakes.ai.mit.edu) + + * C++: Makefile: changed the echo: expect 101 shift/reduce conflicts + and 1 reduce/reduce conflict. + +Local Variables: +mode: indented-text +left-margin: 8 +fill-column: 74 +version-control: never +End: diff --git a/gnu/usr.bin/gdb/Gdbinit b/gnu/usr.bin/gdb/Gdbinit new file mode 100644 index 0000000000..bcacd5dc7e --- /dev/null +++ b/gnu/usr.bin/gdb/Gdbinit @@ -0,0 +1,15 @@ +echo Setting up the environment for debugging gdb.\n + +b fatal + +b info_command +commands + silent + return +end + +define rr + run +end + +set prompt (top-gdb) diff --git a/gnu/usr.bin/gdb/Makefile b/gnu/usr.bin/gdb/Makefile new file mode 100644 index 0000000000..550ed07a75 --- /dev/null +++ b/gnu/usr.bin/gdb/Makefile @@ -0,0 +1,38 @@ +# @(#)Makefile 6.4 (Berkley) 5/6/91 + +PROG= gdb +GDBSRCS= blockframe.c breakpoint.c command.c copying.c core.c \ + cplus-dem.c dbxread.c environ.c eval.c expprint.c \ + expread.y findvar.c infcmd.c inflow.c infrun.c \ + main.c obstack.c printcmd.c regex.c remote.c \ + remote-sl.c source.c stack.c symmisc.c symtab.c \ + utils.c valarith.c valops.c valprint.c values.c \ + version.c +READLINESRCS= funmap.c history.c keymaps.c readline.c +SRCS= $(CONFIGSRCS) $(GDBSRCS) $(READLINESRCS) init.c +CFLAGS+= -I. -I$(.CURDIR) -I$(.CURDIR)/config -I$(.CURDIR)/readline \ + -DHAVE_VPRINTF -DVI_MODE -DKERNELDEBUG -DNEWVM +LDADD= -ltermcap +YFLAGS= +.PATH: $(.CURDIR)/config $(.CURDIR)/readline + +depend: + +.include "config/Makefile.$(MACHINE)" +.include + +$(OBJS): param.h + +# +# Generate the constructor +# +init.c: $(CONFIGSRCS) $(GDBSRCS) $(READLINESRCS) + -((cd $(.CURDIR)/config; \ + egrep -h '^_initialize_[^ ]* *\(\)' $(CONFIGSRCS)); \ + (cd $(.CURDIR); egrep -h '^_initialize_[^ ]* *\(\)' $(GDBSRCS)); \ + (cd $(.CURDIR)/readline; \ + egrep -h '^_initialize_[^ ]* *\(\)' $(READLINESRCS))) | \ + (echo 'void initialize_all_files () {'; sed -e 's/$$/;/'; echo '}') \ + > init.c + +CLEANFILES+= init.c param.h diff --git a/gnu/usr.bin/gdb/Makefile.dist b/gnu/usr.bin/gdb/Makefile.dist new file mode 100644 index 0000000000..3cbc91f978 --- /dev/null +++ b/gnu/usr.bin/gdb/Makefile.dist @@ -0,0 +1,371 @@ +/* This file should be run through the C preprocessor by config.gdb + to produce the Makefile. */ + +/* Define this to xgdb if you want to compile xgdb as well as gdb. */ +XGDB= +/* Place to install binaries. */ +bindir=/usr/local/bin +/* Place to install X binaries. */ +xbindir=$(bindir) + +/* System V: If you compile gdb with a compiler which uses the coff + encapsulation feature (this is a function of the compiler used, NOT + of the m-?.h file selected by config.gdb), you must make sure that + the GNU nm is the one that is used by munch. */ + +/* If you are compiling with GCC, make sure that either 1) You use the + -traditional flag, or 2) You have the fixed include files where GCC + can reach them. Otherwise the ioctl calls in inflow.c and readline.c + will be incorrectly compiled. The "fixincludes" script in the gcc + distribution will fix your include files up. */ +/* CC=gcc -traditional */ +CC=cc + +/* It is also possible that you will need to add -I/usr/include/sys to the + CFLAGS section if your system doesn't have fcntl.h in /usr/include (which + is where it should be according to Posix). */ + +YACC=bison -y -v +/* YACC=yacc */ +SHELL=/bin/sh +MAKE=make + +/* Set this up with gcc if you have gnu ld and the loader will print out + line numbers for undefinded refs. */ +/* CC-LD=gcc -static */ +CC-LD=${CC} + +/* If you are using the GNU C library, uncomment the following line. */ +/* HAVE_VPRINTF_DEFINE = -DHAVE_VPRINTF */ + +/* -I. for "#include ". Possibly regex.h also. */ + +/* M_CFLAGS, if defined, has system-dependent CFLAGS. */ +#if !defined(M_CFLAGS) +#define M_CFLAGS +#endif + +/* CFLAGS for both GDB and readline. */ +GLOBAL_CFLAGS = -g M_CFLAGS +CFLAGS = -I. ${HAVE_VPRINTF_DEFINE} ${GLOBAL_CFLAGS} +/* None of the things in CFLAGS will do any harm, and on some systems + (e.g. SunOS4) it is important to use the M_CFLAGS. */ +LDFLAGS = $(CFLAGS) + +/* + define this to be "obstack.o" if you don't have the obstack library installed + you must at the same time define OBSTACK1 as "obstack.o" + so that the dependencies work right. Similarly with REGEX and "regex.o". + You must define REGEX and REGEX1 on USG machines. + If your sysyem is missing alloca(), or, more likely, it's there but + it doesn't work, define ALLOCA & ALLOCA1 */ +OBSTACK = obstack.o +OBSTACK1 = obstack.o + +#ifdef M_REGEX +REGEX = M_REGEX +REGEX1 = M_REGEX +#else +REGEX = +REGEX1 = +#endif + +#ifdef M_ALLOCA +ALLOCA = M_ALLOCA +ALLOCA1 = M_ALLOCA +#else +ALLOCA = +ALLOCA1 = +#endif + +/* + define this to be "malloc.o" if you want to use the gnu malloc routine + (useful for debugging memory allocation problems in gdb). Otherwise, leave + it blank. */ +/* GNU_MALLOC = */ +GNU_MALLOC = malloc.o + +/* Flags to be used in compiling malloc.o + Specify range checking for storage allocation. */ +/* MALLOC_FLAGS = ${CFLAGS} */ +MALLOC_FLAGS = ${CFLAGS} -Drcheck -Dbotch=fatal_dump_core -DMSTATS + +/* Define SYSV if compiling on a system V or HP machine. */ +#ifdef M_SYSV +SYSV_DEFINE = -DSYSV +#else +SYSV_DEFINE = +#endif + +/* MUNCH_DEFINE should be -DSYSV if have System V-style nm, + or null if have BSD-style nm. */ +#ifdef M_BSD_NM +MUNCH_DEFINE = +#else +MUNCH_DEFINE = ${SYSV_DEFINE} +#endif + +/* Flags that describe where you can find the termcap library. + You may need to make other arrangements for USG. */ +TERMCAP = -ltermcap + +/* M_CLIBS, if defined, has system-dependent libs + For example, -lPW for System V to get alloca(). */ +#ifndef M_CLIBS +#define M_CLIBS +#endif +CLIBS = ${ADD_FILES} ${TERMCAP} M_CLIBS + +ADD_FILES = ${OBSTACK} ${REGEX} ${ALLOCA} ${GNU_MALLOC} +ADD_DEPS = ${OBSTACK1} ${REGEX1} ${ALLOCA1} ${GNU_MALLOC} + +SFILES = blockframe.c breakpoint.c dbxread.c coffread.c command.c core.c \ + environ.c eval.c expprint.c findvar.c infcmd.c inflow.c infrun.c \ + kdb-start.c main.c printcmd.c \ + remote.c source.c stack.c standalone.c stuff.c symmisc.c symtab.c \ + utils.c valarith.c valops.c valprint.c values.c version.c expread.y \ + xgdb.c + +DEPFILES = umax-dep.c gould-dep.c default-dep.c sun3-dep.c \ + sparc-dep.c hp9k320-dep.c hp300bsd-dep.c news-dep.c i386-dep.c \ + symmetry-dep.c convex-dep.c altos-dep.c isi-dep.c pyr-dep.c + +PINSNS = gld-pinsn.c i386-pinsn.c sparc-pinsn.c vax-pinsn.c m68k-pinsn.c \ + ns32k-pinsn.c convex-pinsn.c pyr-pinsn.c + +HFILES = command.h defs.h environ.h expression.h frame.h getpagesize.h \ + inferior.h symseg.h symtab.h value.h wait.h \ + a.out.encap.h a.out.gnu.h stab.gnu.h + +OPCODES = m68k-opcode.h pn-opcode.h sparc-opcode.h npl-opcode.h vax-opcode.h \ + ns32k-opcode.h convex-opcode.h pyr-opcode.h + +MFILES = m-hp9k320.h m-hp300bsd.h m-i386.h m-i386gas.h \ + m-i386-sv32.h m-i386g-sv32.h m-isi.h m-merlin.h \ + m-altos.h m-news.h m-newsos3.h m-npl.h m-pn.h \ + m-sparc.h m-sun2.h m-sun3.h m-sun2os4.h \ + m-sun3os4.h m-sun4os4.h m-umax.h m-vax.h m-symmetry.h m-convex.h \ + m-pyr.h + +/* This list of files really shouldn't be in this makefile, but I can't think + of any good way to get the readline makefile to tell us what files + to put in our tarfile. */ +READLINE = readline.c history.c funmap.c \ + emacs_keymap.c vi_keymap.c vi_mode.c keymaps.c \ + readline.h history.h keymaps.h chardefs.h \ + inc-readline.texinfo inc-history.texinfo \ + readline.texinfo history.texinfo \ + Makefile ChangeLog + +REMOTE_EXAMPLES = remote-sa.m68k.shar remote-multi.shar + +POSSLIBS = obstack.h obstack.c regex.c regex.h malloc.c alloca.c + +TESTS = testbpt.c testfun.c testrec.c testreg.c testregs.c + +OTHERS = Makefile.dist createtags munch config.gdb ChangeLog README TAGS \ + gdb.texinfo .gdbinit COPYING expread.tab.c stab.def \ + XGDB-README copying.c Projects Convex.notes copying.awk hp-include + +TAGFILES = ${SFILES} ${DEPFILES} ${PINSNS} ${HFILES} ${OPCODES} ${MFILES} \ + ${POSSLIBS} +TARFILES = ${TAGFILES} ${OTHERS} ${REMOTE_EXAMPLES} + +OBS = main.o blockframe.o breakpoint.o findvar.o stack.o source.o \ + values.o eval.o valops.o valarith.o valprint.o printcmd.o \ + symtab.o symmisc.o coffread.o dbxread.o infcmd.o infrun.o remote.o \ + command.o utils.o expread.o expprint.o pinsn.o environ.o version.o \ + copying.o ${READLINEOBS} + +TSOBS = core.o inflow.o dep.o + +NTSOBS = standalone.o + +TSSTART = /lib/crt0.o + +NTSSTART = kdb-start.o + +RL_LIB = readline/libreadline.a + +/* Do some fancy trickery to produce a line like + -DM_MAKEDEFINE="-DM_SYSV -DM_BSD_NM". +*/ +MD=M_MAKEDEFINE + +/* Avoid funny things that Sun's make throws in for us. */ +/* TARGET_ARCH is supposed to get around it putting in the machine type. + If the "things" up there really is plural, we'll need to do something + else as well. */ +/*.c.o: + ${CC} -c ${CFLAGS} $< */ +TARGET_ARCH= + +all: gdb $(XGDB) + +install: gdb $(XGDB) + cp gdb $(bindir)/gdb.new + mv $(bindir)/gdb.new $(bindir)/gdb + -if [ "$(XGDB)" = xgdb ]; then \ + cp xgdb $(xbindir)/xgdb.new; \ + mv $(xbindir)/xgdb.new $(xbindir)xgdb; \ + fi + +gdb : $(OBS) $(TSOBS) ${ADD_DEPS} ${RL_LIB} + rm -f init.c + ./munch ${MUNCH_DEFINE} $(OBS) $(TSOBS) > init.c + ${CC-LD} $(LDFLAGS) -o gdb init.c $(OBS) $(TSOBS) ${RL_LIB} $(CLIBS) + +/* This is useful when debugging GDB, because Unix doesn't let you run GDB + on itself without copying the executable. So "make gdb1" will make + gdb and put a copy in gdb1, and you can run it with "gdb gdb1". */ +gdb1 : gdb + cp gdb gdb1 + +Makefile : Makefile.dist + cp Makefile.dist tmp.c + $(CC) -E >Makefile tmp.c $(MD) "-DM_MAKEDEFINE=$(MD)" + -rm tmp.c +/* This did not work-- -Usparc became "-Usparc" became "-Usparc. + Or something like that. */ +/* $(CC) -E >Makefile tmp.c $(MD) "-DM_MAKEDEFINE=\"$(MD)\"" */ + +xgdb : $(OBS) $(TSOBS) xgdb.o ${ADD_DEPS} ${RL_LIB} + rm -f init.c + ./munch ${MUNCH_DEFINE} $(OBS) $(TSOBS) xgdb.o > init.c + $(CC-LD) $(LDFLAGS) -o xgdb init.c $(OBS) $(TSOBS) xgdb.o \ + -lXaw -lXmu -lXt -lX11 ${RL_LIB} $(CLIBS) + +/* Old (pre R3) xgdb comp. + $(CC-LD) $(LDFLAGS) -o xgdb init.c $(OBS) $(TSOBS) xgdb.o \ + -lXaw -lXt -lX11 $(CLIBS) */ + +kdb : $(NTSSTART) $(OBS) $(NTSOBS) ${ADD_DEPS} ${RL_LIB} + rm -f init.c + ./munch ${MUNCH_DEFINE} $(OBS) $(NTSOBS) > init.c + $(CC) $(LDFLAGS) -c init.c $(CLIBS) + ld -o kdb $(NTSSTART) $(OBS) $(NTSOBS) init.o ${RL_LIB} -lc $(CLIBS) + +/* If it can figure out the appropriate order, createtags will make sure + that the proper m-*, *-dep, *-pinsn, and *-opcode files come first + in the tags list. It will attempt to do the same for dbxread.c and + coffread.c. This makes using M-. on machine dependent routines much + easier. */ + +TAGS: ${TAGFILES} + createtags ${TAGFILES} +tags: TAGS + +gdb.tar: ${TARFILES} + rm -f gdb.tar + mkdir dist-gdb + cd dist-gdb ; for i in ${TARFILES} ; do ln -s ../$$i . ; done + mkdir dist-gdb/readline + cd dist-gdb/readline ; for i in ${READLINE} ; do ln -s ../../readline/$$i . ; done + tar chf gdb.tar dist-gdb + rm -rf dist-gdb + +/* Remove gdb.tar.Z so stupid compress doesn't ask whether we want to + overwrite it. compress -f is not what we want, because we do want + to know if compress would not make it smaller. */ +gdb.tar.Z: gdb.tar + if [ -f gdb.tar.Z ]; then rm -f gdb.tar.Z; else true; fi + compress gdb.tar + +clean: + rm -f ${OBS} ${TSOBS} ${NTSOBS} ${OBSTACK} ${REGEX} ${GNU_MALLOC} + rm -f init.c init.o + rm -f xgdb.o xgdb + rm -f gdb core gdb.tar gdb.tar.Z make.log + rm -f gdb[0-9] + cd readline ; make clean + +distclean: clean expread.tab.c TAGS + rm -f dep.c opcode.h param.h pinsn.c config.status + rm -f y.output yacc.acts yacc.tmp + rm -f ${TESTS} Makefile + +realclean: clean + rm -f expread.tab.c TAGS + rm -f dep.c opcode.h param.h pinsn.c config.status + rm -f Makefile + +xgdb.o : defs.h param.h symtab.h frame.h + +/* Make copying.c from COPYING */ +copying.c : COPYING copying.awk + awk -f copying.awk < COPYING > copying.c + +expread.tab.c : expread.y + @echo 'Expect 4 shift/reduce conflict.' + ${YACC} expread.y + mv y.tab.c expread.tab.c + +expread.o : expread.tab.c defs.h param.h symtab.h frame.h expression.h + $(CC) -c ${CFLAGS} expread.tab.c + mv expread.tab.o expread.o + +readline/libreadline.a : force_update + cd readline ; ${MAKE} "SYSV=${SYSV_DEFINE}" \ + "DEBUG_FLAGS=${GLOBAL_CFLAGS}" "CC=${CC}" libreadline.a + +force_update : + +/* Only useful if you are using the gnu malloc routines. */ +malloc.o : malloc.c + ${CC} -c ${MALLOC_FLAGS} malloc.c + +/* dep.o depends on config.status in case someone reconfigures gdb out + from under an already compiled gdb. */ +dep.o : dep.c config.status defs.h param.h frame.h inferior.h obstack.h \ + a.out.encap.h + +/* pinsn.o depends on config.status in case someone reconfigures gdb out + from under an already compiled gdb. */ +pinsn.o : pinsn.c config.status defs.h param.h symtab.h obstack.h symseg.h \ + frame.h opcode.h + +/* The rest of this is a standard dependencies list (hand edited output of + cpp -M). It does not include dependencies of .o files on .c files. */ +/* All files which depend on config.status also depend on param.h in case + someone reconfigures gdb out from under an already compiled gdb. */ +blockframe.o : defs.h param.h config.status symtab.h obstack.h symseg.h frame.h +breakpoint.o : defs.h param.h config.status symtab.h obstack.h symseg.h frame.h +coffread.o : defs.h param.h config.status +command.o : command.h defs.h +core.o : defs.h param.h config.status a.out.encap.h +dbxread.o : param.h config.status defs.h symtab.h obstack.h symseg.h a.out.encap.h \ + stab.gnu.h +environ.o : environ.h +eval.o : defs.h param.h config.status symtab.h obstack.h symseg.h value.h expression.h +expprint.o : defs.h symtab.h obstack.h symseg.h param.h config.status expression.h +findvar.o : defs.h param.h config.status symtab.h obstack.h symseg.h frame.h value.h +infcmd.o : defs.h param.h config.status symtab.h obstack.h symseg.h frame.h inferior.h \ + environ.h value.h +inflow.o : defs.h param.h config.status frame.h inferior.h +infrun.o : defs.h param.h config.status symtab.h obstack.h symseg.h frame.h inferior.h \ + wait.h +kdb-start.o : defs.h param.h config.status +main.o : defs.h command.h param.h config.status +malloc.o : getpagesize.h +obstack.o : obstack.h +printcmd.o : defs.h param.h config.status frame.h symtab.h obstack.h symseg.h value.h \ + expression.h +regex.o : regex.h +remote.o : defs.h param.h config.status frame.h inferior.h wait.h +source.o : defs.h symtab.h obstack.h symseg.h param.h config.status +stack.o : defs.h param.h config.status symtab.h obstack.h symseg.h frame.h +standalone.o : defs.h param.h config.status symtab.h obstack.h symseg.h frame.h \ + inferior.h wait.h +symmisc.o : defs.h symtab.h obstack.h symseg.h obstack.h +symtab.o : defs.h symtab.h obstack.h symseg.h param.h config.status obstack.h +utils.o : defs.h param.h config.status +valarith.o : defs.h param.h config.status symtab.h obstack.h symseg.h value.h expression.h +valops.o : defs.h param.h config.status symtab.h obstack.h symseg.h value.h frame.h \ + inferior.h +valprint.o : defs.h param.h config.status symtab.h obstack.h symseg.h value.h +values.o : defs.h param.h config.status symtab.h obstack.h symseg.h value.h + +robotussin.h : getpagesize.h +symtab.h : obstack.h symseg.h +a.out.encap.h : a.out.gnu.h + diff --git a/gnu/usr.bin/gdb/Projects b/gnu/usr.bin/gdb/Projects new file mode 100644 index 0000000000..f38f6c74e9 --- /dev/null +++ b/gnu/usr.bin/gdb/Projects @@ -0,0 +1,114 @@ + + Suggested projects for aspiring or current GDB hackers + ====================================================== + + (You should probably chat with kingdon@ai.mit.edu to make sure that + no one else is doing the project you chose). + +Add watchpoints (break if a memory location changes). This would +usually have to involve constant single stepping, but occasionally +there is operating system support which gdb should be able to cleanly +use (e.g. on the 80386, there are 4 debug registers. By ptracing an +address into them, you can get a trap on writes or on reads and +writes). + +Rewrite proceed, wait_for_inferior, and normal_stop to clean them up. +Suggestions: + + 1) Make each test in wait_for_inferior a seperate subroutine + call. + 2) Combine wait_for_inferior and normal_stop to clean up + communication via global variables. + 3) See if you can find some way to clean up the global + variables that are used; possibly group them by data flow + and information content? + +Work out some kind of way to allow running the inferior to be done as +a sub-execution of, eg. breakpoint command lists. Currently running +the inferior interupts any command list execution. This would require +some rewriting of wait_for_inferior & friends, and hence should +probably be done in concert with the above. + +Add function arguments to gdb user defined functions. + +Add convenience variables that refer to exec file, symbol file, +selected frame source file, selected frame function, selected frame +line number, etc. + +Add a "suspend" subcommand of the "continue" command to suspend gdb +while continuing execution of the subprocess. Useful when you are +debugging servers and you want to dodge out and initiate a connection +to a server running under gdb. + +Make "handle" understand symbolic signal names. + +Work out and implement a reasonably general mechanism for multi-threaded +processies. There are parts of one implemented in convex-dep.c, if +you want an example. + +A standalone version of gdb on the i386 exists. Anyone who wants to +do some serious working cleaning it up and making it a general +standalone gdb should contact pace@wheaties.ai.mit.edu. + +Add stab information to allow reasonable debugging of inline functions +(possibly they should show up on a stack backtrace? With a note +indicating that they weren't "real"?). + +Implement support for specifying arbitrary locations of stack frames +(in practice, this usually requires specification of both the top and +bottom of the stack frame (fp and sp), since you *must* retrieve the +pc that was saved in the innermost frame). + +Modify the naked "until" command to step until past the current source +line, rather than past the current pc value. This is tricky simply +because the low level routines have no way of specifying a multi-line +step range, and there is no way of saying "don't print stuff when we +stop" from above (otherwise could just call step many times). + +Modify the handling of symbols grouped through BINCL/EINCL stabs to +allocate a partial symtab for each BINCL/EINCL grouping. This will +seriously decrease the size of inter-psymtab dependencies and hence +lessen the amount that needs to be read in when a new source file is +accessed. + +Work out some method of saving breakpoints across the reloading of an +executable. Probably this should be by saving the commands by which +the breakpoints were set and re-executing them (as text locations may +change). + +Do an "x/i $pc" after each stepi or nexti. + +Modify all of the disassemblers to use printf_filtered to get correct +more filtering. + +Modify gdb to work correctly with Pascal. + +Rewrite macros that handle frame chaining and frameless functions. +They should be able to tell the difference between start, main, and a +frameless function called from main. + +Work out what information would need to be included in an executable +by the compiler to allow gdb to debug functions which do not have a +frame pointer. Modify gdb and gcc to do this. + +When `attached' to a program (via either OS support or remote +debugging), gdb should arrange to catch signals which the terminal +might send, as it is unlikely that the program will be able to notice +them. SIGINT and SIGTSTP are obvious examples. + +Enhance the gdb manual with extra examples where needed. + +Arrange for list_command not to use decode_line_1 and thus not require +symbols to be read in simply to read a source file. + +Problem in xgdb; the readline library needs the terminal in CBREAK +mode for command line editing, but this makes it difficult to dispatch +on button presses. Possible solution: use a define to replace getc in +readline.c with a routine that does button dispatches. You should +probably see XGDB-README before you fiddle with XGDB. Also, someone +is implementing a new xgdb; it may not be worth while fiddling with +the old one. + +# Local Variables: +# mode: text +# End: diff --git a/gnu/usr.bin/gdb/README.gnu b/gnu/usr.bin/gdb/README.gnu new file mode 100644 index 0000000000..fa54dec236 --- /dev/null +++ b/gnu/usr.bin/gdb/README.gnu @@ -0,0 +1,142 @@ +This is GDB, the GNU source-level debugger, presently running under un*x. + +Before compiling GDB, you must tell GDB what kind of machine you are +running on. To do this, type `config.gdb machine', where machine is +something like `vax' or `sun2'. For a list of valid machine types, +type `config.gdb'. + +Normally config.gdb edits the makefile as necessary. If you have to +edit the makefile on a standard machine listed in config.gdb this +should be considered a bug and reported as such. + +Once these files are set up, just `make' will do everything, +producing an executable `gdb' in this directory. + +If you want a new (current to this release) version of the manual, you +will have to use the gdb.texinfo file provided with this distribution. +The gdb.texinfo file requires the texinfo-format-buffer command from +emacs 18.55 or later. + +About languages other than C... + +C++ support has been integrated into gdb. GDB should work with +FORTRAN programs (if you have problem, please send a bug report), but +I am not aware of anyone who is working on getting it to use the +syntax of any language other than C or C++. Pascal programs which use +sets, subranges, file variables, or nested functions will not +currently work. + +About -gg format... + +Currently GDB version 3.x does *not* support GCC's -gg format. This +is because it (in theory) has fast enough startup on dbx debugging +format object files that -gg format is unnecessary (and hence +undesirable, since it wastes space and processing power in gcc). I +would like to hear people's opinions on the amount of time currently +spent in startup; is it fast enough? + +About remote debugging... + +The two files remote-multi.shar and remote-sa.m68k.shar contain two +examples of a remote stub to be used with remote.c. The the -multi +file is a general stub that can probably be running on various +different flavors of unix to allow debugging over a serial line from +one machine to another. The remote-sa.m68k.shar is designed to run +standalone on a 68k type cpu and communicate properley with the +remote.c stub over a serial line. + +About reporting bugs... + +The correct address for reporting bugs found with gdb is +"bug-gdb@prep.ai.mit.edu". Please send all bugs to that address. + +About xgdb... + +xgdb.c was provided to us by the user community; it is not an integral +part of the gdb distribution. The problem of providing visual +debugging support on top of gdb is peripheral to the GNU project and +(at least right now) we can't afford to put time into it. So while we +will be happy to incorporate user fixes to xgdb.c, we do not guarantee +that it will work and we will not fix bugs reported in it. Someone is +working on writing a new XGDB, so improving (e.g. by fixing it so that +it will work, if it doesn't currently) the current one is not worth it. + +For those intersted in auto display of source and the availability of +an editor while debugging I suggest trying gdb-mode in gnu-emacs. +Comments on this mode are welcome. + +About the machine-dependent files... + +m-.h (param.h is a link to this file). +This file contains macro definitions that express information +about the machine's registers, stack frame format and instructions. + +-opcode.h (opcode.h is a link to this file). +-pinsn.c (pinsn.c is a link to this file). +These files contain the information necessary to print instructions +for your cpu type. + +-dep.c (dep.c is a link to this file). +Those routines which provide a low level interface to ptrace and which +tend to be machine-dependent. (The machine-independent routines are in +`infrun.c' and `inflow.c') + +About writing code for GDB... + +We appreciate having users contribute code that is of general use, but +for it to be included in future GDB releases it must be cleanly +written. We do not want to include changes that will needlessly make future +maintainance difficult. It is not much harder to do things right, and +in the long term it is worth it to the GNU project, and probably to +you individually as well. + +Please code according to the GNU coding standards. If you do not have +a copy, you can request one by sending mail to gnu@prep.ai.mit.edu. + +Please try to avoid making machine-specific changes to +machine-independent files (i.e. all files except "param.h" and +"dep.c". "pinsn.c" and "opcode.h" are processor-specific but not +operating system-dependent). If this is unavoidable, put a hook in +the machine-independent file which calls a (possibly) +machine-dependent macro (for example, the IGNORE_SYMBOL macro can be +used for any symbols which need to be ignored on a specific machine. +Calling IGNORE_SYMBOL in dbxread.c is a lot cleaner than a maze of #if +defined's). The machine-independent code should do whatever "most" +machines want if the macro is not defined in param.h. Using #if +defined can sometimes be OK (e.g. SET_STACK_LIMIT_HUGE) but should be +conditionalized on a specific feature of an operating system (set in +param.h) rather than something like #if defined(vax) or #if +defined(SYSV). + +It is better to replace entire routines which may be system-specific, +rather than put in a whole bunch of hooks which are probably not going +to be helpful for any purpose other than your changes. For example, +if you want to modify dbxread.c to deal with DBX debugging symbols +which are in COFF files rather than BSD a.out files, do something +along the lines of a macro GET_NEXT_SYMBOL, which could have +different definitions for COFF and a.out, rather than trying to put +the necessary changes throughout all the code in dbxread.c that +currently assumes BSD format. + +Please avoid duplicating code. For example, if something needs to be +changed in read_inferior_memory, it is very painful because there is a +copy in every dep.c file. The correct way to do this is to put (in +this case) the standard ptrace interfaces in a separate file ptrace.c, +which is used by all systems which have ptrace. ptrace.c would deal +with variations between systems the same way any system-independent +file would (hooks, #if defined, etc.). + +About debugging gdb with itself... + +You probably want to do a "make TAGS" after you configure your +distribution; this will put the machine dependent routines for your +local machine where they will be accessed first by a M-period . + +Also, make sure that you've compiled gdb with your local cc or taken +appropriate precautions regarding ansification of include files. See +the Makefile for more information. + +The "info" command, when executed without a subcommand in a gdb being +debugged by gdb, will pop you back up to the top level gdb. See +.gdbinit for more details. + diff --git a/gnu/usr.bin/gdb/XGdbinit.samp b/gnu/usr.bin/gdb/XGdbinit.samp new file mode 100644 index 0000000000..a99f106956 --- /dev/null +++ b/gnu/usr.bin/gdb/XGdbinit.samp @@ -0,0 +1,15 @@ +button "show" push-to-file %S +button "back" pop-file +button "break in" break %S +button "break at" break %l +button delete delete %b%e +button backtrace +button up +button down +button print print %E +button print* print *(%E) +button next +button step +button "do upto" until %l%e +button finish +button continue cont%e diff --git a/gnu/usr.bin/gdb/Xgdb.ad b/gnu/usr.bin/gdb/Xgdb.ad new file mode 100644 index 0000000000..5f9fe9920b --- /dev/null +++ b/gnu/usr.bin/gdb/Xgdb.ad @@ -0,0 +1,8 @@ +Xgdb*geometry: 580x874-0+28 +Xgdb*src*scrollVertical: whenneeded +Xgdb*src*scrollHorizontal: whenneeded +Xgdb*src*wrap: never +Xgdb*src*editType: read +Xgdb*frame.buttons.allowResize: true +Xgdb*frame.buttons.skipAdjust: true +Xgdb*frame*showGrip: false diff --git a/gnu/usr.bin/gdb/blockframe.c b/gnu/usr.bin/gdb/blockframe.c new file mode 100644 index 0000000000..236d1cdccf --- /dev/null +++ b/gnu/usr.bin/gdb/blockframe.c @@ -0,0 +1,622 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)blockframe.c 6.4 (Berkeley) 5/11/91"; +#endif /* not lint */ + +/* Get info from stack frames; + convert between frames, blocks, functions and pc values. + Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "frame.h" + +#include + +#if defined(NEWVM) && defined(KERNELDEBUG) +#include /* XXX for FRAME_CHAIN_VALID */ +#endif + +/* Start and end of object file containing the entry point. + STARTUP_FILE_END is the first address of the next file. + This file is assumed to be a startup file + and frames with pc's inside it + are treated as nonexistent. + + Setting these variables is necessary so that backtraces do not fly off + the bottom of the stack. */ +CORE_ADDR startup_file_start; +CORE_ADDR startup_file_end; + +/* Is ADDR outside the startup file? */ +int +outside_startup_file (addr) + CORE_ADDR addr; +{ + return !(addr >= startup_file_start && addr < startup_file_end); +} + +/* Address of innermost stack frame (contents of FP register) */ + +static FRAME current_frame; + +struct block *block_for_pc (); +CORE_ADDR get_pc_function_start (); + +/* + * Cache for frame addresses already read by gdb. Valid only while + * inferior is stopped. Control variables for the frame cache should + * be local to this module. + */ +struct obstack frame_cache_obstack; + +/* Return the innermost (currently executing) stack frame. */ + +FRAME +get_current_frame () +{ + /* We assume its address is kept in a general register; + param.h says which register. */ + + return current_frame; +} + +void +set_current_frame (frame) + FRAME frame; +{ + current_frame = frame; +} + +FRAME +create_new_frame (addr, pc) + FRAME_ADDR addr; + CORE_ADDR pc; +{ + struct frame_info *fci; /* Same type as FRAME */ + + fci = (struct frame_info *) + obstack_alloc (&frame_cache_obstack, + sizeof (struct frame_info)); + + /* Arbitrary frame */ + fci->next = (struct frame_info *) 0; + fci->prev = (struct frame_info *) 0; + fci->frame = addr; + fci->next_frame = 0; /* Since arbitrary */ + fci->pc = pc; + +#ifdef INIT_EXTRA_FRAME_INFO + INIT_EXTRA_FRAME_INFO (fci); +#endif + + return fci; +} + +/* Return the frame that called FRAME. + If FRAME is the original frame (it has no caller), return 0. */ + +FRAME +get_prev_frame (frame) + FRAME frame; +{ + /* We're allowed to know that FRAME and "struct frame_info *" are + the same */ + return get_prev_frame_info (frame); +} + +/* Return the frame that FRAME calls (0 if FRAME is the innermost + frame). */ + +FRAME +get_next_frame (frame) + FRAME frame; +{ + /* We're allowed to know that FRAME and "struct frame_info *" are + the same */ + return frame->next; +} + +/* + * Flush the entire frame cache. + */ +void +flush_cached_frames () +{ + /* Since we can't really be sure what the first object allocated was */ + obstack_free (&frame_cache_obstack, 0); + obstack_init (&frame_cache_obstack); + + current_frame = (struct frame_info *) 0; /* Invalidate cache */ +} + +/* Return a structure containing various interesting information + about a specified stack frame. */ +/* How do I justify including this function? Well, the FRAME + identifier format has gone through several changes recently, and + it's not completely inconceivable that it could happen again. If + it does, have this routine around will help */ + +struct frame_info * +get_frame_info (frame) + FRAME frame; +{ + return frame; +} + +/* If a machine allows frameless functions, it should define a macro + FRAMELESS_FUNCTION_INVOCATION(FI, FRAMELESS) in param.h. FI is the struct + frame_info for the frame, and FRAMELESS should be set to nonzero + if it represents a frameless function invocation. */ + +/* Many machines which allow frameless functions can detect them using + this macro. Such machines should define FRAMELESS_FUNCTION_INVOCATION + to just call this macro. */ +#define FRAMELESS_LOOK_FOR_PROLOGUE(FI, FRAMELESS) \ +{ \ + CORE_ADDR func_start, after_prologue; \ + func_start = (get_pc_function_start ((FI)->pc) + \ + FUNCTION_START_OFFSET); \ + if (func_start) \ + { \ + after_prologue = func_start; \ + SKIP_PROLOGUE (after_prologue); \ + (FRAMELESS) = (after_prologue == func_start); \ + } \ + else \ + /* If we can't find the start of the function, we don't really */ \ + /* know whether the function is frameless, but we should be */ \ + /* able to get a reasonable (i.e. best we can do under the */ \ + /* circumstances) backtrace by saying that it isn't. */ \ + (FRAMELESS) = 0; \ +} + +/* Return a structure containing various interesting information + about the frame that called NEXT_FRAME. Returns NULL + if there is no such frame. */ + +struct frame_info * +get_prev_frame_info (next_frame) + FRAME next_frame; +{ + FRAME_ADDR address; + struct frame_info *prev; + int fromleaf = 0; + + /* If the requested entry is in the cache, return it. + Otherwise, figure out what the address should be for the entry + we're about to add to the cache. */ + + if (!next_frame) + { + if (!current_frame) + { + if (!have_inferior_p () && !have_core_file_p ()) + fatal ("get_prev_frame_info: Called before cache primed. \"Shouldn't happen.\""); + else + error ("No inferior or core file."); + } + + return current_frame; + } + + /* If we have the prev one, return it */ + if (next_frame->prev) + return next_frame->prev; + + /* On some machines it is possible to call a function without + setting up a stack frame for it. On these machines, we + define this macro to take two args; a frameinfo pointer + identifying a frame and a variable to set or clear if it is + or isn't leafless. */ +#ifdef FRAMELESS_FUNCTION_INVOCATION + /* Still don't want to worry about this except on the innermost + frame. This macro will set FROMLEAF if NEXT_FRAME is a + frameless function invocation. */ + if (!(next_frame->next)) + { + FRAMELESS_FUNCTION_INVOCATION (next_frame, fromleaf); + if (fromleaf) + address = next_frame->frame; + } +#endif + + if (!fromleaf) + { + /* Two macros defined in param.h specify the machine-dependent + actions to be performed here. + First, get the frame's chain-pointer. + If that is zero, the frame is the outermost frame or a leaf + called by the outermost frame. This means that if start + calls main without a frame, we'll return 0 (which is fine + anyway). + + Nope; there's a problem. This also returns when the current + routine is a leaf of main. This is unacceptable. We move + this to after the ffi test; I'd rather have backtraces from + start go curfluy than have an abort called from main not show + main. */ + address = FRAME_CHAIN (next_frame); + if (!FRAME_CHAIN_VALID (address, next_frame)) + return 0; + /* If this frame is a leaf, this will be superceeded by the + code below. */ + address = FRAME_CHAIN_COMBINE (address, next_frame); + } + if (address == 0) + return 0; + + prev = (struct frame_info *) + obstack_alloc (&frame_cache_obstack, + sizeof (struct frame_info)); + + if (next_frame) + next_frame->prev = prev; + prev->next = next_frame; + prev->prev = (struct frame_info *) 0; + prev->frame = address; + prev->next_frame = prev->next ? prev->next->frame : 0; + +#ifdef INIT_EXTRA_FRAME_INFO + INIT_EXTRA_FRAME_INFO(prev); +#endif + + /* This entry is in the frame queue now, which is good since + FRAME_SAVED_PC may use that queue to figure out it's value + (see m-sparc.h). We want the pc saved in the inferior frame. */ + prev->pc = (fromleaf ? SAVED_PC_AFTER_CALL (next_frame) : + next_frame ? FRAME_SAVED_PC (next_frame) : read_pc ()); + + return prev; +} + +CORE_ADDR +get_frame_pc (frame) + FRAME frame; +{ + struct frame_info *fi; + fi = get_frame_info (frame); + return fi->pc; +} + +/* Find the addresses in which registers are saved in FRAME. */ + +void +get_frame_saved_regs (frame_info_addr, saved_regs_addr) + struct frame_info *frame_info_addr; + struct frame_saved_regs *saved_regs_addr; +{ + FRAME_FIND_SAVED_REGS (frame_info_addr, *saved_regs_addr); +} + +/* Return the innermost lexical block in execution + in a specified stack frame. The frame address is assumed valid. */ + +struct block * +get_frame_block (frame) + FRAME frame; +{ + struct frame_info *fi; + CORE_ADDR pc; + + fi = get_frame_info (frame); + + pc = fi->pc; + if (fi->next_frame != 0) + /* We are not in the innermost frame. We need to subtract one to + get the correct block, in case the call instruction was the + last instruction of the block. If there are any machines on + which the saved pc does not point to after the call insn, we + probably want to make fi->pc point after the call insn anyway. */ + --pc; + return block_for_pc (pc); +} + +struct block * +get_current_block () +{ + return block_for_pc (read_pc ()); +} + +CORE_ADDR +get_pc_function_start (pc) + CORE_ADDR pc; +{ + register struct block *bl = block_for_pc (pc); + register struct symbol *symbol; + if (bl == 0 || (symbol = block_function (bl)) == 0) + { + register int misc_index = find_pc_misc_function (pc); + if (misc_index >= 0) + return misc_function_vector[misc_index].address; + return 0; + } + bl = SYMBOL_BLOCK_VALUE (symbol); + return BLOCK_START (bl); +} + +/* Return the symbol for the function executing in frame FRAME. */ + +struct symbol * +get_frame_function (frame) + FRAME frame; +{ + register struct block *bl = get_frame_block (frame); + if (bl == 0) + return 0; + return block_function (bl); +} + +/* Return the innermost lexical block containing the specified pc value, + or 0 if there is none. */ + +extern struct symtab *psymtab_to_symtab (); + +struct block * +block_for_pc (pc) + register CORE_ADDR pc; +{ + register struct block *b; + register int bot, top, half; + register struct symtab *s; + register struct partial_symtab *ps; + struct blockvector *bl; + + /* First search all symtabs for one whose file contains our pc */ + + for (s = symtab_list; s; s = s->next) + { + bl = BLOCKVECTOR (s); + b = BLOCKVECTOR_BLOCK (bl, 0); + if (BLOCK_START (b) <= pc + && BLOCK_END (b) > pc) + break; + } + + if (s == 0) + for (ps = partial_symtab_list; ps; ps = ps->next) + { + if (ps->textlow <= pc + && ps->texthigh > pc) + { + if (ps->readin) + fatal ("Internal error: pc found in readin psymtab and not in any symtab."); + s = psymtab_to_symtab (ps); + bl = BLOCKVECTOR (s); + b = BLOCKVECTOR_BLOCK (bl, 0); + break; + } + } + + if (s == 0) + return 0; + + /* Then search that symtab for the smallest block that wins. */ + /* Use binary search to find the last block that starts before PC. */ + + bot = 0; + top = BLOCKVECTOR_NBLOCKS (bl); + + while (top - bot > 1) + { + half = (top - bot + 1) >> 1; + b = BLOCKVECTOR_BLOCK (bl, bot + half); + if (BLOCK_START (b) <= pc) + bot += half; + else + top = bot + half; + } + + /* Now search backward for a block that ends after PC. */ + + while (bot >= 0) + { + b = BLOCKVECTOR_BLOCK (bl, bot); + if (BLOCK_END (b) > pc) + return b; + bot--; + } + + return 0; +} + +/* Return the function containing pc value PC. + Returns 0 if function is not known. */ + +struct symbol * +find_pc_function (pc) + CORE_ADDR pc; +{ + register struct block *b = block_for_pc (pc); + if (b == 0) + return 0; + return block_function (b); +} + +/* Finds the "function" (text symbol) that is smaller than PC + but greatest of all of the potential text symbols. Sets + *NAME and/or *ADDRESS conditionally if that pointer is non-zero. + Returns 0 if it couldn't find anything, 1 if it did. On a zero + return, *NAME and *ADDRESS are always set to zero. On a 1 return, + *NAME and *ADDRESS contain real information. */ + +int +find_pc_partial_function (pc, name, address) + CORE_ADDR pc; + char **name; + CORE_ADDR *address; +{ + struct partial_symtab *pst = find_pc_psymtab (pc); + struct symbol *f; + int miscfunc; + struct partial_symbol *psb; + + if (pst) + { + if (pst->readin) + { + /* The information we want has already been read in. + We can go to the already readin symbols and we'll get + the best possible answer. */ + f = find_pc_function (pc); + if (!f) + { + return_error: + /* No availible symbol. */ + if (name != 0) + *name = 0; + if (address != 0) + *address = 0; + return 0; + } + + if (name) + *name = SYMBOL_NAME (f); + if (address) + *address = BLOCK_START (SYMBOL_BLOCK_VALUE (f)); + return 1; + } + + /* Get the information from a combination of the pst + (static symbols), and the misc function vector (extern + symbols). */ + miscfunc = find_pc_misc_function (pc); + psb = find_pc_psymbol (pst, pc); + + if (!psb && miscfunc == -1) + { + goto return_error; + } + if (!psb + || (miscfunc != -1 + && (SYMBOL_VALUE(psb) + < misc_function_vector[miscfunc].address))) + { + if (address) + *address = misc_function_vector[miscfunc].address; + if (name) + *name = misc_function_vector[miscfunc].name; + return 1; + } + else + { + if (address) + *address = SYMBOL_VALUE (psb); + if (name) + *name = SYMBOL_NAME (psb); + return 1; + } + } + else + /* Must be in the misc function stuff. */ + { + miscfunc = find_pc_misc_function (pc); + if (miscfunc == -1) + goto return_error; + if (address) + *address = misc_function_vector[miscfunc].address; + if (name) + *name = misc_function_vector[miscfunc].name; + return 1; + } +} + +/* Find the misc function whose address is the largest + while being less than PC. Return its index in misc_function_vector. + Returns -1 if PC is not in suitable range. */ + +int +find_pc_misc_function (pc) + register CORE_ADDR pc; +{ + register int lo = 0; + register int hi = misc_function_count-1; + register int new; + register int distance; + + /* Note that the last thing in the vector is always _etext. */ + /* Actually, "end", now that non-functions + go on the misc_function_vector. */ + + /* Above statement is not *always* true - fix for case where there are */ + /* no misc functions at all (ie no symbol table has been read). */ + if (hi < 0) return -1; /* no misc functions recorded */ + + /* trivial reject range test */ + if (pc < misc_function_vector[0].address || + pc > misc_function_vector[hi].address) + return -1; + + /* Note that the following search will not return hi if + pc == misc_function_vector[hi].address. If "end" points to the + first unused location, this is correct and the above test + simply needs to be changed to + "pc >= misc_function_vector[hi].address". */ + do { + new = (lo + hi) >> 1; + distance = misc_function_vector[new].address - pc; + if (distance == 0) + return new; /* an exact match */ + else if (distance > 0) + hi = new; + else + lo = new; + } while (hi-lo != 1); + + /* if here, we had no exact match, so return the lower choice */ + return lo; +} + +/* Return the innermost stack frame executing inside of the specified block, + or zero if there is no such frame. */ + +FRAME +block_innermost_frame (block) + struct block *block; +{ + struct frame_info *fi; + register FRAME frame; + register CORE_ADDR start = BLOCK_START (block); + register CORE_ADDR end = BLOCK_END (block); + + frame = 0; + while (1) + { + frame = get_prev_frame (frame); + if (frame == 0) + return 0; + fi = get_frame_info (frame); + if (fi->pc >= start && fi->pc < end) + return frame; + } +} + +void +_initialize_blockframe () +{ + obstack_init (&frame_cache_obstack); +} diff --git a/gnu/usr.bin/gdb/breakpoint.c b/gnu/usr.bin/gdb/breakpoint.c new file mode 100644 index 0000000000..b515ed3f87 --- /dev/null +++ b/gnu/usr.bin/gdb/breakpoint.c @@ -0,0 +1,1383 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)breakpoint.c 6.3 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Everything about breakpoints, for GDB. + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "frame.h" + +/* This is the sequence of bytes we insert for a breakpoint. */ + +static char break_insn[] = BREAKPOINT; + +/* States of enablement of breakpoint. + `temporary' means disable when hit. + `delete' means delete when hit. */ + +enum enable { disabled, enabled, temporary, delete}; + +/* Not that the ->silent field is not currently used by any commands + (though the code is in there if it was to be and set_raw_breakpoint + does set it to 0). I implemented it because I thought it would be + useful for a hack I had to put in; I'm going to leave it in because + I can see how there might be times when it would indeed be useful */ + +struct breakpoint +{ + struct breakpoint *next; + /* Number assigned to distinguish breakpoints. */ + int number; + /* Address to break at. */ + CORE_ADDR address; + /* Line number of this address. Redundant. */ + int line_number; + /* Symtab of file of this address. Redundant. */ + struct symtab *symtab; + /* Zero means disabled; remember the info but don't break here. */ + enum enable enable; + /* Non-zero means a silent breakpoint (don't print frame info + if we stop here). */ + unsigned char silent; + /* Number of stops at this breakpoint that should + be continued automatically before really stopping. */ + int ignore_count; + /* "Real" contents of byte where breakpoint has been inserted. + Valid only when breakpoints are in the program. */ + char shadow_contents[sizeof break_insn]; + /* Nonzero if this breakpoint is now inserted. */ + char inserted; + /* Nonzero if this is not the first breakpoint in the list + for the given address. */ + char duplicate; + /* Chain of command lines to execute when this breakpoint is hit. */ + struct command_line *commands; + /* Stack depth (address of frame). If nonzero, break only if fp + equals this. */ + FRAME_ADDR frame; + /* Conditional. Break only if this expression's value is nonzero. */ + struct expression *cond; +}; + +#define ALL_BREAKPOINTS(b) for (b = breakpoint_chain; b; b = b->next) + +/* Chain of all breakpoints defined. */ + +struct breakpoint *breakpoint_chain; + +/* Number of last breakpoint made. */ + +static int breakpoint_count; + +/* Default address, symtab and line to put a breakpoint at + for "break" command with no arg. + if default_breakpoint_valid is zero, the other three are + not valid, and "break" with no arg is an error. + + This set by print_stack_frame, which calls set_default_breakpoint. */ + +int default_breakpoint_valid; +CORE_ADDR default_breakpoint_address; +struct symtab *default_breakpoint_symtab; +int default_breakpoint_line; + +/* Remaining commands (not yet executed) + of last breakpoint hit. */ + +struct command_line *breakpoint_commands; + +static void delete_breakpoint (); +void clear_momentary_breakpoints (); +void breakpoint_auto_delete (); + +/* Flag indicating extra verbosity for xgdb. */ +extern int xgdb_verbose; + +/* condition N EXP -- set break condition of breakpoint N to EXP. */ + +static void +condition_command (arg, from_tty) + char *arg; + int from_tty; +{ + register struct breakpoint *b; + register char *p; + register int bnum; + register struct expression *expr; + + if (arg == 0) + error_no_arg ("breakpoint number"); + + p = arg; + while (*p >= '0' && *p <= '9') p++; + if (p == arg) + /* There is no number here. (e.g. "cond a == b"). */ + error_no_arg ("breakpoint number"); + bnum = atoi (arg); + + ALL_BREAKPOINTS (b) + if (b->number == bnum) + { + if (b->cond) + { + free (b->cond); + b->cond = 0; /* parse_c_1 can leave this unchanged. */ + } + if (*p == 0) + { + b->cond = 0; + if (from_tty) + printf ("Breakpoint %d now unconditional.\n", bnum); + } + else + { + if (*p != ' ' && *p != '\t') + error ("Arguments must be an integer (breakpoint number) and an expression."); + + /* Find start of expression */ + while (*p == ' ' || *p == '\t') p++; + + arg = p; + b->cond = (struct expression *) parse_c_1 (&arg, block_for_pc (b->address), 0); + if (*arg) + error ("Junk at end of expression"); + } + return; + } + + error ("No breakpoint number %d.", bnum); +} + +static void +commands_command (arg, from_tty) + char *arg; + int from_tty; +{ + register struct breakpoint *b; + register char *p, *p1; + register int bnum; + struct command_line *l; + + /* If we allowed this, we would have problems with when to + free the storage, if we change the commands currently + being read from. */ + + if (breakpoint_commands) + error ("Can't use the \"commands\" command among a breakpoint's commands."); + + /* Allow commands by itself to refer to the last breakpoint. */ + if (arg == 0) + bnum = breakpoint_count; + else + { + p = arg; + if (! (*p >= '0' && *p <= '9')) + error ("Argument must be integer (a breakpoint number)."); + + while (*p >= '0' && *p <= '9') p++; + if (*p) + error ("Unexpected extra arguments following breakpoint number."); + + bnum = atoi (arg); + } + + ALL_BREAKPOINTS (b) + if (b->number == bnum) + { + if (from_tty && input_from_terminal_p ()) + { + printf ("Type commands for when breakpoint %d is hit, one per line.\n\ +End with a line saying just \"end\".\n", bnum); + fflush (stdout); + } + l = read_command_lines (from_tty); + free_command_lines (b->commands); + b->commands = l; + return; + } + error ("No breakpoint number %d.", bnum); +} + +/* Called from command loop to execute the commands + associated with the breakpoint we just stopped at. */ + +void +do_breakpoint_commands () +{ + struct command_line *cmd; + + while (cmd = breakpoint_commands) + { + breakpoint_commands = 0; + execute_command_lines(cmd); + /* If command was "cont", breakpoint_commands is now 0, + of if we stopped at yet another breakpoint which has commands, + it is now the commands for the new breakpoint. */ + } + clear_momentary_breakpoints (); +} + +/* Used when the program is proceeded, to eliminate any remaining + commands attached to the previous breakpoint we stopped at. */ + +void +clear_breakpoint_commands () +{ + breakpoint_commands = 0; + breakpoint_auto_delete (0); +} + +/* Functions to get and set the current list of pending + breakpoint commands. These are used by run_stack_dummy + to preserve the commands around a function call. */ + +struct command_line * +get_breakpoint_commands () +{ + return breakpoint_commands; +} + +void +set_breakpoint_commands (cmds) + struct command_line *cmds; +{ + breakpoint_commands = cmds; +} + +/* insert_breakpoints is used when starting or continuing the program. + remove_breakpoints is used when the program stops. + Both return zero if successful, + or an `errno' value if could not write the inferior. */ + +int +insert_breakpoints () +{ + register struct breakpoint *b; + int val; + +#ifdef BREAKPOINT_DEBUG + printf ("Inserting breakpoints.\n"); +#endif /* BREAKPOINT_DEBUG */ + + ALL_BREAKPOINTS (b) + if (b->enable != disabled && ! b->inserted && ! b->duplicate) + { + read_memory (b->address, b->shadow_contents, sizeof break_insn); + val = write_memory (b->address, break_insn, sizeof break_insn); + if (val) + return val; +#ifdef BREAKPOINT_DEBUG + printf ("Inserted breakpoint at 0x%x, shadow 0x%x, 0x%x.\n", + b->address, b->shadow_contents[0], b->shadow_contents[1]); +#endif /* BREAKPOINT_DEBUG */ + b->inserted = 1; + } + return 0; +} + +int +remove_breakpoints () +{ + register struct breakpoint *b; + int val; + +#ifdef BREAKPOINT_DEBUG + printf ("Removing breakpoints.\n"); +#endif /* BREAKPOINT_DEBUG */ + + ALL_BREAKPOINTS (b) + if (b->inserted) + { + val = write_memory (b->address, b->shadow_contents, sizeof break_insn); + if (val) + return val; + b->inserted = 0; +#ifdef BREAKPOINT_DEBUG + printf ("Removed breakpoint at 0x%x, shadow 0x%x, 0x%x.\n", + b->address, b->shadow_contents[0], b->shadow_contents[1]); +#endif /* BREAKPOINT_DEBUG */ + } + + return 0; +} + +/* Clear the "inserted" flag in all breakpoints. + This is done when the inferior is loaded. */ + +void +mark_breakpoints_out () +{ + register struct breakpoint *b; + + ALL_BREAKPOINTS (b) + b->inserted = 0; +} + +/* breakpoint_here_p (PC) returns 1 if an enabled breakpoint exists at PC. + When continuing from a location with a breakpoint, + we actually single step once before calling insert_breakpoints. */ + +int +breakpoint_here_p (pc) + CORE_ADDR pc; +{ + register struct breakpoint *b; + + ALL_BREAKPOINTS (b) + if (b->enable != disabled && b->address == pc) + return 1; + + return 0; +} + +/* Evaluate the expression EXP and return 1 if value is zero. + This is used inside a catch_errors to evaluate the breakpoint condition. */ + +int +breakpoint_cond_eval (exp) + struct expression *exp; +{ + return value_zerop (evaluate_expression (exp)); +} + +/* Return 0 if PC is not the address just after a breakpoint, + or -1 if breakpoint says do not stop now, + or -2 if breakpoint says it has deleted itself and don't stop, + or -3 if hit a breakpoint number -3 (delete when program stops), + or else the number of the breakpoint, + with 0x1000000 added (or subtracted, for a negative return value) for + a silent breakpoint. */ + +int +breakpoint_stop_status (pc, frame_address) + CORE_ADDR pc; + FRAME_ADDR frame_address; +{ + register struct breakpoint *b; + register int cont = 0; + + /* Get the address where the breakpoint would have been. */ + pc -= DECR_PC_AFTER_BREAK; + + ALL_BREAKPOINTS (b) + if (b->enable != disabled && b->address == pc) + { + if (b->frame && b->frame != frame_address) + cont = -1; + else + { + int value_zero; + if (b->cond) + { + /* Need to select the frame, with all that implies + so that the conditions will have the right context. */ + select_frame (get_current_frame (), 0); + value_zero + = catch_errors (breakpoint_cond_eval, b->cond, + "Error occurred in testing breakpoint condition."); + free_all_values (); + } + if (b->cond && value_zero) + { + cont = -1; + } + else if (b->ignore_count > 0) + { + b->ignore_count--; + cont = -1; + } + else + { + if (b->enable == temporary) + b->enable = disabled; + breakpoint_commands = b->commands; + if (b->silent + || (breakpoint_commands + && !strcmp ("silent", breakpoint_commands->line))) + { + if (breakpoint_commands) + breakpoint_commands = breakpoint_commands->next; + return (b->number > 0 ? + 0x1000000 + b->number : + b->number - 0x1000000); + } + return b->number; + } + } + } + + return cont; +} + +static void +breakpoint_1 (bnum) + int bnum; +{ + register struct breakpoint *b; + register struct command_line *l; + register struct symbol *sym; + CORE_ADDR last_addr = (CORE_ADDR)-1; + + ALL_BREAKPOINTS (b) + if (bnum == -1 || bnum == b->number) + { + printf_filtered ("#%-3d %c 0x%08x", b->number, + "nyod"[(int) b->enable], + b->address); + last_addr = b->address; + if (b->symtab) + { + sym = find_pc_function (b->address); + if (sym) + { + fputs_filtered (" in ", stdout); + fputs_demangled (SYMBOL_NAME (sym), stdout, 1); + fputs_filtered (" (", stdout); + } + fputs_filtered (b->symtab->filename, stdout); + printf_filtered (" line %d", b->line_number); + if (sym) fputs_filtered(")", stdout); + } + else + print_address_symbolic (b->address, stdout); + + printf_filtered ("\n"); + + if (b->ignore_count) + printf_filtered ("\tignore next %d hits\n", b->ignore_count); + if (b->frame) + printf_filtered ("\tstop only in stack frame at 0x%x\n", b->frame); + if (b->cond) + { + printf_filtered ("\tbreak only if "); + print_expression (b->cond, stdout); + printf_filtered ("\n"); + } + if (l = b->commands) + while (l) + { + printf_filtered ("\t%s\n", l->line); + l = l->next; + } + } + + /* Compare against (CORE_ADDR)-1 in case some compiler decides + that a comparison of an unsigned with -1 is always false. */ + if (last_addr != (CORE_ADDR)-1) + set_next_address (last_addr); +} + +static void +breakpoints_info (bnum_exp) + char *bnum_exp; +{ + int bnum = -1; + + if (bnum_exp) + bnum = parse_and_eval_address (bnum_exp); + else if (breakpoint_chain == 0) + printf_filtered ("No breakpoints.\n"); + else + printf_filtered ("Breakpoints:\n\ +Num Enb Address Where\n"); + + breakpoint_1 (bnum); +} + +/* Print a message describing any breakpoints set at PC. */ + +static void +describe_other_breakpoints (pc) + register CORE_ADDR pc; +{ + register int others = 0; + register struct breakpoint *b; + + ALL_BREAKPOINTS (b) + if (b->address == pc) + others++; + if (others > 0) + { + printf ("Note: breakpoint%s ", (others > 1) ? "s" : ""); + ALL_BREAKPOINTS (b) + if (b->address == pc) + { + others--; + printf ("%d%s%s ", + b->number, + (b->enable == disabled) ? " (disabled)" : "", + (others > 1) ? "," : ((others == 1) ? " and" : "")); + } + printf ("also set at pc 0x%x.\n", pc); + } +} + +/* Set the default place to put a breakpoint + for the `break' command with no arguments. */ + +void +set_default_breakpoint (valid, addr, symtab, line) + int valid; + CORE_ADDR addr; + struct symtab *symtab; + int line; +{ + default_breakpoint_valid = valid; + default_breakpoint_address = addr; + default_breakpoint_symtab = symtab; + default_breakpoint_line = line; +} + +/* Rescan breakpoints at address ADDRESS, + marking the first one as "first" and any others as "duplicates". + This is so that the bpt instruction is only inserted once. */ + +static void +check_duplicates (address) + CORE_ADDR address; +{ + register struct breakpoint *b; + register int count = 0; + + ALL_BREAKPOINTS (b) + if (b->enable != disabled && b->address == address) + { + count++; + b->duplicate = count > 1; + } +} + +/* Low level routine to set a breakpoint. + Takes as args the three things that every breakpoint must have. + Returns the breakpoint object so caller can set other things. + Does not set the breakpoint number! + Does not print anything. */ + +static struct breakpoint * +set_raw_breakpoint (sal) + struct symtab_and_line sal; +{ + register struct breakpoint *b, *b1; + + b = (struct breakpoint *) xmalloc (sizeof (struct breakpoint)); + bzero (b, sizeof *b); + b->address = sal.pc; + b->symtab = sal.symtab; + b->line_number = sal.line; + b->enable = enabled; + b->next = 0; + b->silent = 0; + + /* Add this breakpoint to the end of the chain + so that a list of breakpoints will come out in order + of increasing numbers. */ + + b1 = breakpoint_chain; + if (b1 == 0) + breakpoint_chain = b; + else + { + while (b1->next) + b1 = b1->next; + b1->next = b; + } + + check_duplicates (sal.pc); + + return b; +} + +/* Set a breakpoint that will evaporate an end of command + at address specified by SAL. + Restrict it to frame FRAME if FRAME is nonzero. */ + +void +set_momentary_breakpoint (sal, frame) + struct symtab_and_line sal; + FRAME frame; +{ + register struct breakpoint *b; + b = set_raw_breakpoint (sal); + b->number = -3; + b->enable = delete; + b->frame = (frame ? FRAME_FP (frame) : 0); +} + +void +clear_momentary_breakpoints () +{ + register struct breakpoint *b; + ALL_BREAKPOINTS (b) + if (b->number == -3) + { + delete_breakpoint (b); + break; + } +} + +/* Set a breakpoint from a symtab and line. + If TEMPFLAG is nonzero, it is a temporary breakpoint. + Print the same confirmation messages that the breakpoint command prints. */ + +void +set_breakpoint (s, line, tempflag) + struct symtab *s; + int line; + int tempflag; +{ + register struct breakpoint *b; + struct symtab_and_line sal; + + sal.symtab = s; + sal.line = line; + sal.pc = find_line_pc (sal.symtab, sal.line); + if (sal.pc == 0) + error ("No line %d in file \"%s\".\n", sal.line, sal.symtab->filename); + else + { + describe_other_breakpoints (sal.pc); + + b = set_raw_breakpoint (sal); + b->number = ++breakpoint_count; + b->cond = 0; + if (tempflag) + b->enable = temporary; + + printf ("Breakpoint %d at 0x%x", b->number, b->address); + if (b->symtab) + printf (": file %s, line %d.", b->symtab->filename, b->line_number); + printf ("\n"); + } +} + +/* Set a breakpoint according to ARG (function, linenum or *address) + and make it temporary if TEMPFLAG is nonzero. */ + +static void +break_command_1 (arg, tempflag, from_tty) + char *arg; + int tempflag, from_tty; +{ + struct symtabs_and_lines sals; + struct symtab_and_line sal; + register struct expression *cond = 0; + register struct breakpoint *b; + char *save_arg; + int i; + CORE_ADDR pc; + + sals.sals = NULL; + sals.nelts = 0; + + sal.line = sal.pc = sal.end = 0; + sal.symtab = 0; + + /* If no arg given, or if first arg is 'if ', use the default breakpoint. */ + + if (!arg || (arg[0] == 'i' && arg[1] == 'f' + && (arg[2] == ' ' || arg[2] == '\t'))) + { + if (default_breakpoint_valid) + { + sals.sals = (struct symtab_and_line *) + malloc (sizeof (struct symtab_and_line)); + sal.pc = default_breakpoint_address; + sal.line = default_breakpoint_line; + sal.symtab = default_breakpoint_symtab; + sals.sals[0] = sal; + sals.nelts = 1; + } + else + error ("No default breakpoint address now."); + } + else + /* Force almost all breakpoints to be in terms of the + current_source_symtab (which is decode_line_1's default). This + should produce the results we want almost all of the time while + leaving default_breakpoint_* alone. */ + if (default_breakpoint_valid + && (!current_source_symtab + || (arg && (*arg == '+' || *arg == '-')))) + sals = decode_line_1 (&arg, 1, default_breakpoint_symtab, + default_breakpoint_line); + else + sals = decode_line_1 (&arg, 1, 0, 0); + + if (! sals.nelts) + return; + + save_arg = arg; + for (i = 0; i < sals.nelts; i++) + { + sal = sals.sals[i]; + if (sal.pc == 0 && sal.symtab != 0) + { + pc = find_line_pc (sal.symtab, sal.line); + if (pc == 0) + error ("No line %d in file \"%s\".", + sal.line, sal.symtab->filename); + } + else + pc = sal.pc; + + while (arg && *arg) + { + if (arg[0] == 'i' && arg[1] == 'f' + && (arg[2] == ' ' || arg[2] == '\t')) + cond = (struct expression *) parse_c_1 ((arg += 2, &arg), + block_for_pc (pc), 0); + else + error ("Junk at end of arguments."); + } + arg = save_arg; + sals.sals[i].pc = pc; + } + + for (i = 0; i < sals.nelts; i++) + { + sal = sals.sals[i]; + + if (from_tty) + describe_other_breakpoints (sal.pc); + + b = set_raw_breakpoint (sal); + b->number = ++breakpoint_count; + b->cond = cond; + if (tempflag) + b->enable = temporary; + + printf ("Breakpoint %d at 0x%x", b->number, b->address); + if (b->symtab) + printf (": file %s, line %d.", b->symtab->filename, b->line_number); + printf ("\n"); + } + + if (sals.nelts > 1) + { + printf ("Multiple breakpoints were set.\n"); + printf ("Use the \"delete\" command to delete unwanted breakpoints.\n"); + } + free (sals.sals); +} + +static void +break_command (arg, from_tty) + char *arg; + int from_tty; +{ + break_command_1 (arg, 0, from_tty); +} + +static void +tbreak_command (arg, from_tty) + char *arg; + int from_tty; +{ + break_command_1 (arg, 1, from_tty); +} + +/* + * Helper routine for the until_command routine in infcmd.c. Here + * because it uses the mechanisms of breakpoints. + */ +void +until_break_command (arg, from_tty) + char *arg; + int from_tty; +{ + struct symtabs_and_lines sals; + struct symtab_and_line sal; + FRAME prev_frame = get_prev_frame (selected_frame); + + clear_proceed_status (); + + /* Set a breakpoint where the user wants it and at return from + this function */ + + if (default_breakpoint_valid) + sals = decode_line_1 (&arg, 1, default_breakpoint_symtab, + default_breakpoint_line); + else + sals = decode_line_1 (&arg, 1, 0, 0); + + if (sals.nelts != 1) + error ("Couldn't get information on specified line."); + + sal = sals.sals[0]; + free (sals.sals); /* malloc'd, so freed */ + + if (*arg) + error ("Junk at end of arguments."); + + if (sal.pc == 0 && sal.symtab != 0) + sal.pc = find_line_pc (sal.symtab, sal.line); + + if (sal.pc == 0) + error ("No line %d in file \"%s\".", sal.line, sal.symtab->filename); + + set_momentary_breakpoint (sal, selected_frame); + + /* Keep within the current frame */ + + if (prev_frame) + { + struct frame_info *fi; + + fi = get_frame_info (prev_frame); + sal = find_pc_line (fi->pc, 0); + sal.pc = fi->pc; + set_momentary_breakpoint (sal, prev_frame); + } + + proceed (-1, -1, 0); +} + +static void +clear_command (arg, from_tty) + char *arg; + int from_tty; +{ + register struct breakpoint *b, *b1; + struct symtabs_and_lines sals; + struct symtab_and_line sal; + register struct breakpoint *found; + int i; + + if (arg) + { + sals = decode_line_spec (arg, 1); + } + else + { + sals.sals = (struct symtab_and_line *) malloc (sizeof (struct symtab_and_line)); + sal.line = default_breakpoint_line; + sal.symtab = default_breakpoint_symtab; + sal.pc = 0; + if (sal.symtab == 0) + error ("No source file specified."); + + sals.sals[0] = sal; + sals.nelts = 1; + } + + for (i = 0; i < sals.nelts; i++) + { + /* If exact pc given, clear bpts at that pc. + But if sal.pc is zero, clear all bpts on specified line. */ + sal = sals.sals[i]; + found = (struct breakpoint *) 0; + while (breakpoint_chain + && (sal.pc ? breakpoint_chain->address == sal.pc + : (breakpoint_chain->symtab == sal.symtab + && breakpoint_chain->line_number == sal.line))) + { + b1 = breakpoint_chain; + breakpoint_chain = b1->next; + b1->next = found; + found = b1; + } + + ALL_BREAKPOINTS (b) + while (b->next + && (sal.pc ? b->next->address == sal.pc + : (b->next->symtab == sal.symtab + && b->next->line_number == sal.line))) + { + b1 = b->next; + b->next = b1->next; + b1->next = found; + found = b1; + } + + if (found == 0) + error ("No breakpoint at %s.", arg); + + if (found->next) from_tty = 1; /* Always report if deleted more than one */ + if (from_tty) printf ("Deleted breakpoint%s ", found->next ? "s" : ""); + while (found) + { + if (from_tty) printf ("%d ", found->number); + b1 = found->next; + delete_breakpoint (found); + found = b1; + } + if (from_tty) putchar ('\n'); + } + free (sals.sals); +} + +/* Delete breakpoint number BNUM if it is a `delete' breakpoint. + This is called after breakpoint BNUM has been hit. + Also delete any breakpoint numbered -3 unless there are breakpoint + commands to be executed. */ + +void +breakpoint_auto_delete (bnum) + int bnum; +{ + register struct breakpoint *b; + if (bnum != 0) + ALL_BREAKPOINTS (b) + if (b->number == bnum) + { + if (b->enable == delete) + delete_breakpoint (b); + break; + } + if (breakpoint_commands == 0) + clear_momentary_breakpoints (); +} + +static void +delete_breakpoint (bpt) + struct breakpoint *bpt; +{ + register struct breakpoint *b; + + if (bpt->inserted) + write_memory (bpt->address, bpt->shadow_contents, sizeof break_insn); + + if (breakpoint_chain == bpt) + breakpoint_chain = bpt->next; + + ALL_BREAKPOINTS (b) + if (b->next == bpt) + { + b->next = bpt->next; + break; + } + + check_duplicates (bpt->address); + + free_command_lines (bpt->commands); + if (bpt->cond) + free (bpt->cond); + + if (xgdb_verbose && bpt->number >=0) + printf ("breakpoint #%d deleted\n", bpt->number); + + free (bpt); +} + +static void map_breakpoint_numbers (); + +static void +delete_command (arg, from_tty) + char *arg; + int from_tty; +{ + register struct breakpoint *b, *b1; + + if (arg == 0) + { + /* Ask user only if there are some breakpoints to delete. */ + if (!from_tty + || breakpoint_chain && query ("Delete all breakpoints? ")) + { + /* No arg; clear all breakpoints. */ + while (breakpoint_chain) + delete_breakpoint (breakpoint_chain); + } + } + else + map_breakpoint_numbers (arg, delete_breakpoint); +} + +/* Delete all breakpoints. + Done when new symtabs are loaded, since the break condition expressions + may become invalid, and the breakpoints are probably wrong anyway. */ + +void +clear_breakpoints () +{ + delete_command (0, 0); +} + +/* Set ignore-count of breakpoint number BPTNUM to COUNT. + If from_tty is nonzero, it prints a message to that effect, + which ends with a period (no newline). */ + +void +set_ignore_count (bptnum, count, from_tty) + int bptnum, count, from_tty; +{ + register struct breakpoint *b; + + if (count < 0) + count = 0; + + ALL_BREAKPOINTS (b) + if (b->number == bptnum) + { + b->ignore_count = count; + if (!from_tty) + return; + else if (count == 0) + printf ("Will stop next time breakpoint %d is reached.", bptnum); + else if (count == 1) + printf ("Will ignore next crossing of breakpoint %d.", bptnum); + else + printf ("Will ignore next %d crossings of breakpoint %d.", + count, bptnum); + return; + } + + error ("No breakpoint number %d.", bptnum); +} + +/* Clear the ignore counts of all breakpoints. */ +void +breakpoint_clear_ignore_counts () +{ + struct breakpoint *b; + + ALL_BREAKPOINTS (b) + b->ignore_count = 0; +} + +/* Command to set ignore-count of breakpoint N to COUNT. */ + +static void +ignore_command (args, from_tty) + char *args; + int from_tty; +{ + register char *p = args; + register int num; + + if (p == 0) + error_no_arg ("a breakpoint number"); + + while (*p >= '0' && *p <= '9') p++; + if (*p && *p != ' ' && *p != '\t') + error ("First argument must be a breakpoint number."); + + num = atoi (args); + + if (*p == 0) + error ("Second argument (specified ignore-count) is missing."); + + set_ignore_count (num, parse_and_eval_address (p), from_tty); + printf ("\n"); +} + +/* Call FUNCTION on each of the breakpoints + whose numbers are given in ARGS. */ + +static void +map_breakpoint_numbers (args, function) + char *args; + void (*function) (); +{ + register char *p = args; + register char *p1; + register int num; + register struct breakpoint *b; + + if (p == 0) + error_no_arg ("one or more breakpoint numbers"); + + while (*p) + { + p1 = p; + while (*p1 >= '0' && *p1 <= '9') p1++; + if (*p1 && *p1 != ' ' && *p1 != '\t') + error ("Arguments must be breakpoint numbers."); + + num = atoi (p); + + ALL_BREAKPOINTS (b) + if (b->number == num) + { + function (b); + goto win; + } + printf ("No breakpoint number %d.\n", num); + win: + p = p1; + while (*p == ' ' || *p == '\t') p++; + } +} + +static void +enable_breakpoint (bpt) + struct breakpoint *bpt; +{ + bpt->enable = enabled; + + if (xgdb_verbose && bpt->number >= 0) + printf ("breakpoint #%d enabled\n", bpt->number); + + check_duplicates (bpt->address); +} + +static void +enable_command (args) + char *args; +{ + struct breakpoint *bpt; + if (args == 0) + ALL_BREAKPOINTS (bpt) + enable_breakpoint (bpt); + else + map_breakpoint_numbers (args, enable_breakpoint); +} + +static void +disable_breakpoint (bpt) + struct breakpoint *bpt; +{ + bpt->enable = disabled; + + if (xgdb_verbose && bpt->number >= 0) + printf ("breakpoint #%d disabled\n", bpt->number); + + check_duplicates (bpt->address); +} + +static void +disable_command (args) + char *args; +{ + register struct breakpoint *bpt; + if (args == 0) + ALL_BREAKPOINTS (bpt) + disable_breakpoint (bpt); + else + map_breakpoint_numbers (args, disable_breakpoint); +} + +static void +enable_once_breakpoint (bpt) + struct breakpoint *bpt; +{ + bpt->enable = temporary; + + check_duplicates (bpt->address); +} + +static void +enable_once_command (args) + char *args; +{ + map_breakpoint_numbers (args, enable_once_breakpoint); +} + +static void +enable_delete_breakpoint (bpt) + struct breakpoint *bpt; +{ + bpt->enable = delete; + + check_duplicates (bpt->address); +} + +static void +enable_delete_command (args) + char *args; +{ + map_breakpoint_numbers (args, enable_delete_breakpoint); +} + +/* + * Use default_breakpoint_'s, or nothing if they aren't valid. + */ +struct symtabs_and_lines +decode_line_spec_1 (string, funfirstline) + char *string; + int funfirstline; +{ + struct symtabs_and_lines sals; + if (string == 0) + error ("Empty line specification."); + if (default_breakpoint_valid) + sals = decode_line_1 (&string, funfirstline, + default_breakpoint_symtab, default_breakpoint_line); + else + sals = decode_line_1 (&string, funfirstline, 0, 0); + if (*string) + error ("Junk at end of line specification: %s", string); + return sals; +} + + +/* Chain containing all defined enable commands. */ + +extern struct cmd_list_element + *enablelist, *disablelist, + *deletelist, *enablebreaklist; + +extern struct cmd_list_element *cmdlist; + +void +_initialize_breakpoint () +{ + breakpoint_chain = 0; + breakpoint_count = 0; + + add_com ("ignore", class_breakpoint, ignore_command, + "Set ignore-count of breakpoint number N to COUNT."); + + add_com ("commands", class_breakpoint, commands_command, + "Set commands to be executed when a breakpoint is hit.\n\ +Give breakpoint number as argument after \"commands\".\n\ +With no argument, the targeted breakpoint is the last one set.\n\ +The commands themselves follow starting on the next line.\n\ +Type a line containing \"end\" to indicate the end of them.\n\ +Give \"silent\" as the first line to make the breakpoint silent;\n\ +then no output is printed when it is hit, except what the commands print."); + + add_com ("condition", class_breakpoint, condition_command, + "Specify breakpoint number N to break only if COND is true.\n\ +N is an integer; COND is a C expression to be evaluated whenever\n\ +breakpoint N is reached. Actually break only when COND is nonzero."); + + add_com ("tbreak", class_breakpoint, tbreak_command, + "Set a temporary breakpoint. Args like \"break\" command.\n\ +Like \"break\" except the breakpoint is only enabled temporarily,\n\ +so it will be disabled when hit. Equivalent to \"break\" followed\n\ +by using \"enable once\" on the breakpoint number."); + + add_prefix_cmd ("enable", class_breakpoint, enable_command, + "Enable some breakpoints or auto-display expressions.\n\ +Give breakpoint numbers (separated by spaces) as arguments.\n\ +With no subcommand, breakpoints are enabled until you command otherwise.\n\ +This is used to cancel the effect of the \"disable\" command.\n\ +With a subcommand you can enable temporarily.\n\ +\n\ +The \"display\" subcommand applies to auto-displays instead of breakpoints.", + &enablelist, "enable ", 1, &cmdlist); + + add_abbrev_prefix_cmd ("breakpoints", class_breakpoint, enable_command, + "Enable some breakpoints or auto-display expressions.\n\ +Give breakpoint numbers (separated by spaces) as arguments.\n\ +With no subcommand, breakpoints are enabled until you command otherwise.\n\ +This is used to cancel the effect of the \"disable\" command.\n\ +May be abbreviates to simply \"enable\".\n\ +With a subcommand you can enable temporarily.", + &enablebreaklist, "enable breakpoints ", 1, &enablelist); + + add_cmd ("once", no_class, enable_once_command, + "Enable breakpoints for one hit. Give breakpoint numbers.\n\ +If a breakpoint is hit while enabled in this fashion, it becomes disabled.\n\ +See the \"tbreak\" command which sets a breakpoint and enables it once.", + &enablebreaklist); + + add_cmd ("delete", no_class, enable_delete_command, + "Enable breakpoints and delete when hit. Give breakpoint numbers.\n\ +If a breakpoint is hit while enabled in this fashion, it is deleted.", + &enablebreaklist); + + add_cmd ("delete", no_class, enable_delete_command, + "Enable breakpoints and delete when hit. Give breakpoint numbers.\n\ +If a breakpoint is hit while enabled in this fashion, it is deleted.", + &enablelist); + + add_cmd ("once", no_class, enable_once_command, + "Enable breakpoints for one hit. Give breakpoint numbers.\n\ +If a breakpoint is hit while enabled in this fashion, it becomes disabled.\n\ +See the \"tbreak\" command which sets a breakpoint and enables it once.", + &enablelist); + + add_prefix_cmd ("disable", class_breakpoint, disable_command, + "Disable some breakpoints or auto-display expressions.\n\ +Arguments are breakpoint numbers with spaces in between.\n\ +To disable all breakpoints, give no argument.\n\ +A disabled breakpoint is not forgotten, but has no effect until reenabled.\n\ +\n\ +The \"display\" subcommand applies to auto-displays instead of breakpoints.", + &disablelist, "disable ", 1, &cmdlist); + add_com_alias ("dis", "disable", class_breakpoint, 1); + add_com_alias ("disa", "disable", class_breakpoint, 1); + + add_abbrev_cmd ("breakpoints", class_breakpoint, disable_command, + "Disable some breakpoints or auto-display expressions.\n\ +Arguments are breakpoint numbers with spaces in between.\n\ +To disable all breakpoints, give no argument.\n\ +A disabled breakpoint is not forgotten, but has no effect until reenabled.\n\ +This command may be abbreviated \"disable\".", + &disablelist); + + add_prefix_cmd ("delete", class_breakpoint, delete_command, + "Delete some breakpoints or auto-display expressions.\n\ +Arguments are breakpoint numbers with spaces in between.\n\ +To delete all breakpoints, give no argument.\n\ +\n\ +Also a prefix command for deletion of other GDB objects.\n\ +The \"unset\" command is also an alias for \"delete\".", + &deletelist, "delete ", 1, &cmdlist); + add_com_alias ("d", "delete", class_breakpoint, 1); + add_com_alias ("unset", "delete", class_alias, 1); + + add_cmd ("breakpoints", class_alias, delete_command, + "Delete some breakpoints or auto-display expressions.\n\ +Arguments are breakpoint numbers with spaces in between.\n\ +To delete all breakpoints, give no argument.\n\ +This command may be abbreviated \"delete\".", + &deletelist); + + add_com ("clear", class_breakpoint, clear_command, + "Clear breakpoint at specified line or function.\n\ +Argument may be line number, function name, or \"*\" and an address.\n\ +If line number is specified, all breakpoints in that line are cleared.\n\ +If function is specified, breakpoints at beginning of function are cleared.\n\ +If an address is specified, breakpoints at that address are cleared.\n\n\ +With no argument, clears all breakpoints in the line that the selected frame\n\ +is executing in.\n\ +\n\ +See also the \"delete\" command which clears breakpoints by number."); + + add_com ("break", class_breakpoint, break_command, + "Set breakpoint at specified line or function.\n\ +Argument may be line number, function name, or \"*\" and an address.\n\ +If line number is specified, break at start of code for that line.\n\ +If function is specified, break at start of code for that function.\n\ +If an address is specified, break at that exact address.\n\ +With no arg, uses current execution address of selected stack frame.\n\ +This is useful for breaking on return to a stack frame.\n\ +\n\ +Multiple breakpoints at one place are permitted, and useful if conditional.\n\ +\n\ +Do \"help breakpoints\" for info on other commands dealing with breakpoints."); + add_com_alias ("b", "break", class_run, 1); + add_com_alias ("br", "break", class_run, 1); + add_com_alias ("bre", "break", class_run, 1); + add_com_alias ("brea", "break", class_run, 1); + + add_info ("breakpoints", breakpoints_info, + "Status of all breakpoints, or breakpoint number NUMBER.\n\ +Second column is \"y\" for enabled breakpoint, \"n\" for disabled,\n\ +\"o\" for enabled once (disable when hit), \"d\" for enable but delete when hit.\n\ +Then come the address and the file/line number.\n\n\ +Convenience variable \"$_\" and default examine address for \"x\"\n\ +are set to the address of the last breakpoint listed."); +} + diff --git a/gnu/usr.bin/gdb/command.c b/gnu/usr.bin/gdb/command.c new file mode 100644 index 0000000000..79daea4903 --- /dev/null +++ b/gnu/usr.bin/gdb/command.c @@ -0,0 +1,856 @@ +/* Library for reading command lines and decoding commands. + Copyright (C) 1986, 1989 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "command.h" +#include "defs.h" +#include +#include + +extern char *xmalloc (); + +/* Add element named NAME to command list *LIST. + FUN should be the function to execute the command; + it will get a character string as argument, with leading + and trailing blanks already eliminated. + + DOC is a documentation string for the command. + Its first line should be a complete sentence. + It should start with ? for a command that is an abbreviation + or with * for a command that most users don't need to know about. */ + +struct cmd_list_element * +add_cmd (name, class, fun, doc, list) + char *name; + int class; + void (*fun) (); + char *doc; + struct cmd_list_element **list; +{ + register struct cmd_list_element *c + = (struct cmd_list_element *) xmalloc (sizeof (struct cmd_list_element)); + + delete_cmd (name, list); + c->next = *list; + c->name = savestring (name, strlen (name)); + c->class = class; + c->function = fun; + c->doc = doc; + c->prefixlist = 0; + c->allow_unknown = 0; + c->abbrev_flag = 0; + c->aux = 0; + *list = c; + return c; +} + +/* Same as above, except that the abbrev_flag is set. */ + +struct cmd_list_element * +add_abbrev_cmd (name, class, fun, doc, list) + char *name; + int class; + void (*fun) (); + char *doc; + struct cmd_list_element **list; +{ + register struct cmd_list_element *c + = (struct cmd_list_element *) xmalloc (sizeof (struct cmd_list_element)); + + delete_cmd (name, list); + c->next = *list; + c->name = savestring (name, strlen (name)); + c->class = class; + c->function = fun; + c->doc = doc; + c->prefixlist = 0; + c->allow_unknown = 0; + c->abbrev_flag = 1; + c->aux = 0; + *list = c; + return c; +} + +struct cmd_list_element * +add_alias_cmd (name, oldname, class, abbrev_flag, list) + char *name; + char *oldname; + int class; + int abbrev_flag; + struct cmd_list_element **list; +{ + /* Must do this since lookup_cmd tries to side-effect its first arg */ + char *copied_name; + register struct cmd_list_element *old; + register struct cmd_list_element *c; + copied_name = (char *) alloca (strlen (oldname) + 1); + strcpy (copied_name, oldname); + old = lookup_cmd (&copied_name, *list, 0, 1, 1); + + if (old == 0) + { + delete_cmd (name, list); + return 0; + } + + c = add_cmd (name, class, old->function, old->doc, list); + c->prefixlist = old->prefixlist; + c->prefixname = old->prefixname; + c->allow_unknown = old->allow_unknown; + c->abbrev_flag = abbrev_flag; + c->aux = old->aux; + return c; +} + +/* Like add_cmd but adds an element for a command prefix: + a name that should be followed by a subcommand to be looked up + in another command list. PREFIXLIST should be the address + of the variable containing that list. */ + +struct cmd_list_element * +add_prefix_cmd (name, class, fun, doc, prefixlist, prefixname, + allow_unknown, list) + char *name; + int class; + void (*fun) (); + char *doc; + struct cmd_list_element **prefixlist; + char *prefixname; + int allow_unknown; + struct cmd_list_element **list; +{ + register struct cmd_list_element *c = add_cmd (name, class, fun, doc, list); + c->prefixlist = prefixlist; + c->prefixname = prefixname; + c->allow_unknown = allow_unknown; + return c; +} + +/* Like add_prefix_cmd butsets the abbrev_flag on the new command. */ + +struct cmd_list_element * +add_abbrev_prefix_cmd (name, class, fun, doc, prefixlist, prefixname, + allow_unknown, list) + char *name; + int class; + void (*fun) (); + char *doc; + struct cmd_list_element **prefixlist; + char *prefixname; + int allow_unknown; + struct cmd_list_element **list; +{ + register struct cmd_list_element *c = add_cmd (name, class, fun, doc, list); + c->prefixlist = prefixlist; + c->prefixname = prefixname; + c->allow_unknown = allow_unknown; + c->abbrev_flag = 1; + return c; +} + +/* Remove the command named NAME from the command list. */ + +void +delete_cmd (name, list) + char *name; + struct cmd_list_element **list; +{ + register struct cmd_list_element *c; + + while (*list && !strcmp ((*list)->name, name)) + { + *list = (*list)->next; + } + + if (*list) + for (c = *list; c->next;) + { + if (!strcmp (c->next->name, name)) + c->next = c->next->next; + else + c = c->next; + } +} + +void help_cmd (), help_list (), help_cmd_list (); + +/* This command really has to deal with two things: + * 1) I want documentation on *this string* (usually called by + * "help commandname"). + * 2) I want documentation on *this list* (usually called by + * giving a command that requires subcommands. Also called by saying + * just "help".) + * + * I am going to split this into two seperate comamnds, help_cmd and + * help_list. + */ + +void +help_cmd (command, stream) + char *command; + FILE *stream; +{ + struct cmd_list_element *c; + extern struct cmd_list_element *cmdlist; + + if (!command) + { + help_list (cmdlist, "", -2, stream); + return; + } + + c = lookup_cmd (&command, cmdlist, "", 0, 0); + + if (c == 0) + return; + + /* There are three cases here. + If c->prefixlist is nonzer, we have a prefix command. + Print its documentation, then list its subcommands. + + If c->function is nonzero, we really have a command. + Print its documentation and return. + + If c->function is zero, we have a class name. + Print its documentation (as if it were a command) + and then set class to he number of this class + so that the commands in the class will be listed. */ + + fputs_filtered (c->doc, stream); + fputs_filtered ("\n", stream); + + if (c->prefixlist == 0 && c->function != 0) + return; + fprintf_filtered (stream, "\n"); + + /* If this is a prefix command, print it's subcommands */ + if (c->prefixlist) + help_list (*c->prefixlist, c->prefixname, -1, stream); + + /* If this is a class name, print all of the commands in the class */ + if (c->function == 0) + help_list (cmdlist, "", c->class, stream); +} + +/* + * Get a specific kind of help on a command list. + * + * LIST is the list. + * CMDTYPE is the prefix to use in the title string. + * CLASS is the class with which to list the nodes of this list (see + * documentation for help_cmd_list below), As usual, -1 for + * everything, -2 for just classes, and non-negative for only things + * in a specific class. + * and STREAM is the output stream on which to print things. + * If you call this routine with a class >= 0, it recurses. + */ +void +help_list (list, cmdtype, class, stream) + struct cmd_list_element *list; + char *cmdtype; + int class; + FILE *stream; +{ + int len; + char *cmdtype1, *cmdtype2; + + /* If CMDTYPE is "foo ", CMDTYPE1 gets " foo" and CMDTYPE2 gets "foo sub" */ + len = strlen (cmdtype); + cmdtype1 = (char *) alloca (len + 1); + cmdtype1[0] = 0; + cmdtype2 = (char *) alloca (len + 4); + cmdtype2[0] = 0; + if (len) + { + cmdtype1[0] = ' '; + strncpy (cmdtype1 + 1, cmdtype, len - 1); + cmdtype1[len] = 0; + strncpy (cmdtype2, cmdtype, len - 1); + strcpy (cmdtype2 + len - 1, " sub"); + } + + if (class == -2) + fprintf_filtered (stream, "List of classes of %scommands:\n\n", cmdtype2); + else + fprintf_filtered (stream, "List of %scommands:\n\n", cmdtype2); + + help_cmd_list (list, class, cmdtype, (class >= 0), stream); + + if (class == -2) + fprintf_filtered (stream, "\n\ +Type \"help%s\" followed by a class name for a list of commands in that class.", + cmdtype1); + + fprintf_filtered (stream, "\n\ +Type \"help%s\" followed by %scommand name for full documentation.\n\ +Command name abbreviations are allowed if unambiguous.\n", + cmdtype1, cmdtype2); +} + + +/* + * Implement a help command on command list LIST. + * RECURSE should be non-zero if this should be done recursively on + * all sublists of LIST. + * PREFIX is the prefix to print before each command name. + * STREAM is the stream upon which the output should be written. + * CLASS should be: + * A non-negative class number to list only commands in that + * class. + * -1 to list all commands in list. + * -2 to list all classes in list. + * + * Note that RECURSE will be active on *all* sublists, not just the + * ones seclected by the criteria above (ie. the selection mechanism + * is at the low level, not the high-level). + */ +void +help_cmd_list (list, class, prefix, recurse, stream) + struct cmd_list_element *list; + int class; + char *prefix; + int recurse; + FILE *stream; +{ + register struct cmd_list_element *c; + register char *p; + static char *line_buffer = 0; + static int line_size; + + if (!line_buffer) + { + line_size = 80; + line_buffer = (char *) xmalloc (line_size); + } + + for (c = list; c; c = c->next) + { + if (c->abbrev_flag == 0 && + (class == -1 + || (class == -2 && c->function == 0) + || (class == c->class && c->function != 0))) + { + fprintf_filtered (stream, "%s%s -- ", prefix, c->name); + /* Print just the first line */ + p = c->doc; + while (*p && *p != '\n') p++; + if (p - c->doc > line_size - 1) + { + line_size = p - c->doc + 1; + free (line_buffer); + line_buffer = (char *) xmalloc (line_size); + } + strncpy (line_buffer, c->doc, p - c->doc); + line_buffer[p - c->doc] = '\0'; + fputs_filtered (line_buffer, stream); + fputs_filtered ("\n", stream); + } + if (recurse + && c->prefixlist != 0 + && c->abbrev_flag == 0) + help_cmd_list (*c->prefixlist, class, c->prefixname, 1, stream); + } +} + +/* This routine takes a line of TEXT and a CLIST in which to + start the lookup. When it returns it will have incremented the text + pointer past the section of text it matched, set *RESULT_LIST to + the list in which the last word was matched, and will return the + cmd list element which the text matches. It will return 0 if no + match at all was possible. It will return -1 if ambigous matches are + possible; in this case *RESULT_LIST will be set to the list in which + there are ambiguous choices (and text will be set to the ambiguous + text string). + + It does no error reporting whatsoever; control will always return + to the superior routine. + + In the case of an ambiguous return (-1), *RESULT_LIST will be set to + point at the prefix_command (ie. the best match) *or* (special + case) will be 0 if no prefix command was ever found. For example, + in the case of "info a", "info" matches without ambiguity, but "a" + could be "args" or "address", so *RESULT_LIST is set to + the cmd_list_element for "info". So in this case + result list should not be interpeted as a pointer to the beginning + of a list; it simply points to a specific command. + + This routine does *not* modify the text pointed to by TEXT. + + If INGNORE_HELP_CLASSES is nonzero, ignore any command list + elements which are actually help classes rather than commands (i.e. + the function field of the struct cmd_list_element is 0). */ + +struct cmd_list_element * +lookup_cmd_1 (text, clist, result_list, ignore_help_classes) + char **text; + struct cmd_list_element *clist, **result_list; + int ignore_help_classes; +{ + char *p, *command; + int len, tmp, nfound; + struct cmd_list_element *found, *c; + + while (**text == ' ' || **text == '\t') + (*text)++; + + /* Treating underscores as part of command words is important + so that "set args_foo()" doesn't get interpreted as + "set args _foo()". */ + for (p = *text; + *p && (isalnum(*p) || *p == '-' || *p == '_'); + p++) + ; + + /* If nothing but whitespace, return 0. */ + if (p == *text) + return 0; + + len = p - *text; + + /* *text and p now bracket the first command word to lookup (and + it's length is len). We copy this into a local temporary, + converting to lower case as we go. */ + + command = (char *) alloca (len + 1); + for (tmp = 0; tmp < len; tmp++) + { + char x = (*text)[tmp]; + command[tmp] = (x >= 'A' && x <= 'Z') ? x - 'A' + 'a' : x; + } + command[len] = '\0'; + + /* Look it up. */ + found = 0; + nfound = 0; + for (c = clist; c; c = c->next) + if (!strncmp (command, c->name, len) + && (!ignore_help_classes || c->function)) + { + found = c; + nfound++; + if (c->name[len] == '\0') + { + nfound = 1; + break; + } + } + + /* If nothing matches, we have a simple failure. */ + if (nfound == 0) + return 0; + + if (nfound > 1) + { + *result_list = 0; /* Will be modified in calling routine + if we know what the prefix command is. + */ + return (struct cmd_list_element *) -1; /* Ambiguous. */ + } + + /* We've matched something on this list. Move text pointer forward. */ + + *text = p; + if (found->prefixlist) + { + c = lookup_cmd_1 (text, *found->prefixlist, result_list, + ignore_help_classes); + if (!c) + { + /* Didn't find anything; this is as far as we got. */ + *result_list = clist; + return found; + } + else if (c == (struct cmd_list_element *) -1) + { + /* We've gotten this far properley, but the next step + is ambiguous. We need to set the result list to the best + we've found (if an inferior hasn't already set it). */ + if (!*result_list) + /* This used to say *result_list = *found->prefixlist + If that was correct, need to modify the documentation + at the top of this function to clarify what is supposed + to be going on. */ + *result_list = found; + return c; + } + else + { + /* We matched! */ + return c; + } + } + else + { + *result_list = clist; + return found; + } +} + +/* Look up the contents of *LINE as a command in the command list LIST. + LIST is a chain of struct cmd_list_element's. + If it is found, return the struct cmd_list_element for that command + and update *LINE to point after the command name, at the first argument. + If not found, call error if ALLOW_UNKNOWN is zero + otherwise (or if error returns) return zero. + Call error if specified command is ambiguous, + unless ALLOW_UNKNOWN is negative. + CMDTYPE precedes the word "command" in the error message. + + If INGNORE_HELP_CLASSES is nonzero, ignore any command list + elements which are actually help classes rather than commands (i.e. + the function field of the struct cmd_list_element is 0). */ + +struct cmd_list_element * +lookup_cmd (line, list, cmdtype, allow_unknown, ignore_help_classes) + char **line; + struct cmd_list_element *list; + char *cmdtype; + int allow_unknown; + int ignore_help_classes; +{ + struct cmd_list_element *last_list = 0; + struct cmd_list_element *c = + lookup_cmd_1 (line, list, &last_list, ignore_help_classes); + char *ptr = (*line) + strlen (*line) - 1; + + /* Clear off trailing whitespace. */ + while (ptr >= *line && (*ptr == ' ' || *ptr == '\t')) + ptr--; + *(ptr + 1) = '\0'; + + if (!c) + { + if (!allow_unknown) + { + if (!*line) + error ("Lack of needed %scommand", cmdtype); + else + { + char *p = *line, *q; + + while (isalnum(*p) || *p == '-') + p++; + + q = (char *) alloca (p - *line + 1); + strncpy (q, *line, p - *line); + q[p-*line] = '\0'; + + error ("Undefined %scommand: \"%s\".", cmdtype, q); + } + } + else + return 0; + } + else if (c == (struct cmd_list_element *) -1) + { + /* Ambigous. Local values should be off prefixlist or called + values. */ + int local_allow_unknown = (last_list ? last_list->allow_unknown : + allow_unknown); + char *local_cmdtype = last_list ? last_list->prefixname : cmdtype; + struct cmd_list_element *local_list = + (last_list ? *(last_list->prefixlist) : list); + + if (local_allow_unknown < 0) + { + if (last_list) + return last_list; /* Found something. */ + else + return 0; /* Found nothing. */ + } + else + { + /* Report as error. */ + int amb_len; + char ambbuf[100]; + + for (amb_len = 0; + ((*line)[amb_len] && (*line)[amb_len] != ' ' + && (*line)[amb_len] != '\t'); + amb_len++) + ; + + ambbuf[0] = 0; + for (c = local_list; c; c = c->next) + if (!strncmp (*line, c->name, amb_len)) + { + if (strlen (ambbuf) + strlen (c->name) + 6 < sizeof ambbuf) + { + if (strlen (ambbuf)) + strcat (ambbuf, ", "); + strcat (ambbuf, c->name); + } + else + { + strcat (ambbuf, ".."); + break; + } + } + error ("Ambiguous %scommand \"%s\": %s.", local_cmdtype, + *line, ambbuf); + } + } + else + { + /* We've got something. It may still not be what the caller + wants (if this command *needs* a subcommand). */ + while (**line == ' ' || **line == '\t') + (*line)++; + + if (c->prefixlist && **line && !c->allow_unknown) + error ("Undefined %scommand: \"%s\".", c->prefixname, *line); + + /* Seems to be what he wants. Return it. */ + return c; + } +} + +#if 0 +/* Look up the contents of *LINE as a command in the command list LIST. + LIST is a chain of struct cmd_list_element's. + If it is found, return the struct cmd_list_element for that command + and update *LINE to point after the command name, at the first argument. + If not found, call error if ALLOW_UNKNOWN is zero + otherwise (or if error returns) return zero. + Call error if specified command is ambiguous, + unless ALLOW_UNKNOWN is negative. + CMDTYPE precedes the word "command" in the error message. */ + +struct cmd_list_element * +lookup_cmd (line, list, cmdtype, allow_unknown) + char **line; + struct cmd_list_element *list; + char *cmdtype; + int allow_unknown; +{ + register char *p; + register struct cmd_list_element *c, *found; + int nfound; + char ambbuf[100]; + char *processed_cmd; + int i, cmd_len; + + /* Skip leading whitespace. */ + + while (**line == ' ' || **line == '\t') + (*line)++; + + /* Clear out trailing whitespace. */ + + p = *line + strlen (*line); + while (p != *line && (p[-1] == ' ' || p[-1] == '\t')) + p--; + *p = 0; + + /* Find end of command name. */ + + p = *line; + while (*p == '-' + || (*p >= 'a' && *p <= 'z') + || (*p >= 'A' && *p <= 'Z') + || (*p >= '0' && *p <= '9')) + p++; + + /* Look up the command name. + If exact match, keep that. + Otherwise, take command abbreviated, if unique. Note that (in my + opinion) a null string does *not* indicate ambiguity; simply the + end of the argument. */ + + if (p == *line) + { + if (!allow_unknown) + error ("Lack of needed %scommand", cmdtype); + return 0; + } + + /* Copy over to a local buffer, converting to lowercase on the way. + This is in case the command being parsed is a subcommand which + doesn't match anything, and that's ok. We want the original + untouched for the routine of the original command. */ + + processed_cmd = (char *) alloca (p - *line + 1); + for (cmd_len = 0; cmd_len < p - *line; cmd_len++) + { + char x = (*line)[cmd_len]; + if (x >= 'A' && x <= 'Z') + processed_cmd[cmd_len] = x - 'A' + 'a'; + else + processed_cmd[cmd_len] = x; + } + processed_cmd[cmd_len] = '\0'; + + /* Check all possibilities in the current command list. */ + found = 0; + nfound = 0; + for (c = list; c; c = c->next) + { + if (!strncmp (processed_cmd, c->name, cmd_len)) + { + found = c; + nfound++; + if (c->name[cmd_len] == 0) + { + nfound = 1; + break; + } + } + } + + /* Report error for undefined command name. */ + + if (nfound != 1) + { + if (nfound > 1 && allow_unknown >= 0) + { + ambbuf[0] = 0; + for (c = list; c; c = c->next) + if (!strncmp (processed_cmd, c->name, cmd_len)) + { + if (strlen (ambbuf) + strlen (c->name) + 6 < sizeof ambbuf) + { + if (strlen (ambbuf)) + strcat (ambbuf, ", "); + strcat (ambbuf, c->name); + } + else + { + strcat (ambbuf, ".."); + break; + } + } + error ("Ambiguous %scommand \"%s\": %s.", cmdtype, + processed_cmd, ambbuf); + } + else if (!allow_unknown) + error ("Undefined %scommand: \"%s\".", cmdtype, processed_cmd); + return 0; + } + + /* Skip whitespace before the argument. */ + + while (*p == ' ' || *p == '\t') p++; + *line = p; + + if (found->prefixlist && *p) + { + c = lookup_cmd (line, *found->prefixlist, found->prefixname, + found->allow_unknown); + if (c) + return c; + } + + return found; +} +#endif + +/* Helper function for SYMBOL_COMPLETION_FUNCTION. */ + +/* Return a vector of char pointers which point to the different + possible completions in LIST of TEXT. */ + +char ** +complete_on_cmdlist (list, text) + struct cmd_list_element *list; + char *text; +{ + struct cmd_list_element *ptr; + char **matchlist; + int sizeof_matchlist; + int matches; + int textlen = strlen (text); + + sizeof_matchlist = 10; + matchlist = (char **) xmalloc (sizeof_matchlist * sizeof (char *)); + matches = 0; + + for (ptr = list; ptr; ptr = ptr->next) + if (!strncmp (ptr->name, text, textlen) + && !ptr->abbrev_flag + && (ptr->function + || ptr->prefixlist)) + { + if (matches == sizeof_matchlist) + { + sizeof_matchlist *= 2; + matchlist = (char **) xrealloc (matchlist, + (sizeof_matchlist + * sizeof (char *))); + } + + matchlist[matches] = (char *) + xmalloc (strlen (ptr->name) + 1); + strcpy (matchlist[matches++], ptr->name); + } + + if (matches == 0) + { + free (matchlist); + matchlist = 0; + } + else + { + matchlist = (char **) xrealloc (matchlist, ((matches + 1) + * sizeof (char *))); + matchlist[matches] = (char *) 0; + } + + return matchlist; +} + +static void +shell_escape (arg, from_tty) + char *arg; + int from_tty; +{ + int rc, status, pid; + char *p, *user_shell; + extern char *rindex (); + + if ((user_shell = (char *) getenv ("SHELL")) == NULL) + user_shell = "/bin/sh"; + + /* Get the name of the shell for arg0 */ + if ((p = rindex (user_shell, '/')) == NULL) + p = user_shell; + else + p++; /* Get past '/' */ + + if ((pid = fork()) == 0) + { + if (!arg) + execl (user_shell, p, 0); + else + execl (user_shell, p, "-c", arg, 0); + + fprintf (stderr, "Exec of shell failed\n"); + exit (0); + } + + if (pid != -1) + while ((rc = wait (&status)) != pid && rc != -1) + ; + else + error ("Fork failed"); +} + +void +_initialize_command () +{ + add_com ("shell", class_support, shell_escape, + "Execute the rest of the line as a shell command. \n\ +With no arguments, run an inferior shell."); +} diff --git a/gnu/usr.bin/gdb/command.h b/gnu/usr.bin/gdb/command.h new file mode 100644 index 0000000000..fe28aef817 --- /dev/null +++ b/gnu/usr.bin/gdb/command.h @@ -0,0 +1,77 @@ +/* Header file for command-reading library command.c. + Copyright (C) 1986, 1989 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* This structure records one command'd definition. */ + +struct cmd_list_element + { + /* Points to next command in this list. */ + struct cmd_list_element *next; + + /* Name of this command. */ + char *name; + + /* Command class; class values are chosen by application program. */ + int class; + + /* Function definition of this command. + Zero for command class names and for help topics that + are not really commands. */ + void (*function) (); + + /* Documentation of this command (or help topic). + First line is brief documentation; remaining lines form, with it, + the full documentation. First line should end with a period. + Entire string should also end with a period, not a newline. */ + char *doc; + + /* Auxiliary information. + It is up to the calling program to decide what this means. */ + char *aux; + + /* Nonzero identifies a prefix command. For them, the address + of the variable containing the list of subcommands. */ + struct cmd_list_element **prefixlist; + + /* For prefix commands only: + String containing prefix commands to get here: this one + plus any others needed to get to it. Should end in a space. + It is used before the word "command" in describing the + commands reached through this prefix. */ + char *prefixname; + + /* For prefix commands only: + nonzero means do not get an error if subcommand is not + recognized; call the prefix's own function in that case. */ + char allow_unknown; + + /* Nonzero says this is an abbreviation, and should not + be mentioned in lists of commands. + This allows "br" to complete to "break", which it + otherwise wouldn't. */ + char abbrev_flag; + }; + +/* Forward-declarations of the entry-points of command.c. */ + +extern struct cmd_list_element *add_cmd (); +extern struct cmd_list_element *add_alias_cmd (); +extern struct cmd_list_element *add_prefix_cmd (); +extern struct cmd_list_element *lookup_cmd (), *lookup_cmd_1 (); +extern char **complete_on_cmdlist (); +extern void delete_cmd (); +extern void help_cmd (); diff --git a/gnu/usr.bin/gdb/config/Makefile.i386 b/gnu/usr.bin/gdb/config/Makefile.i386 new file mode 100644 index 0000000000..cc52aa3b13 --- /dev/null +++ b/gnu/usr.bin/gdb/config/Makefile.i386 @@ -0,0 +1,6 @@ +# @(#)Makefile.i386 6.2 (Berkeley) 3/21/91 + +CONFIGSRCS= i386bsd-dep.c i386-pinsn.c + +param.h: + ln -s $(.CURDIR)/config/m-i386bsd.h param.h diff --git a/gnu/usr.bin/gdb/config/default-dep.c b/gnu/usr.bin/gdb/config/default-dep.c new file mode 100644 index 0000000000..13fe7b95a4 --- /dev/null +++ b/gnu/usr.bin/gdb/config/default-dep.c @@ -0,0 +1,585 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + */ + +#ifndef lint +static char sccsid[] = "@(#)default-dep.c 6.3 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Low level interface to ptrace, for GDB when running under Unix. + Copyright (C) 1988, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include "defs.h" +#include "param.h" +#include "frame.h" +#include "inferior.h" + +#ifdef USG +#include +#endif + +#include +#include +#include +#include +/* #include Can we live without this? */ + +#ifdef COFF_ENCAPSULATE +#include "a.out.encap.h" +#else +#include +#endif +#ifndef N_SET_MAGIC +#define N_SET_MAGIC(exec, val) ((exec).a_magic = (val)) +#endif + +#include /* After a.out.h */ +#include +#include + +extern int errno; + +/* This function simply calls ptrace with the given arguments. + It exists so that all calls to ptrace are isolated in this + machine-dependent file. */ +int +call_ptrace (request, pid, arg3, arg4) + int request, pid, arg3, arg4; +{ + return ptrace (request, pid, arg3, arg4); +} + +kill_inferior () +{ + if (remote_debugging) + return; + if (inferior_pid == 0) + return; + ptrace (8, inferior_pid, 0, 0); + wait (0); + inferior_died (); +} + +/* This is used when GDB is exiting. It gives less chance of error.*/ + +kill_inferior_fast () +{ + if (remote_debugging) + return; + if (inferior_pid == 0) + return; + ptrace (8, inferior_pid, 0, 0); + wait (0); +} + +/* Resume execution of the inferior process. + If STEP is nonzero, single-step it. + If SIGNAL is nonzero, give it that signal. */ + +void +resume (step, signal) + int step; + int signal; +{ + errno = 0; + if (remote_debugging) + remote_resume (step, signal); + else + { + ptrace (step ? 9 : 7, inferior_pid, 1, signal); + if (errno) + perror_with_name ("ptrace"); + } +} + +void +fetch_inferior_registers () +{ + register int regno; + register unsigned int regaddr; + char buf[MAX_REGISTER_RAW_SIZE]; + register int i; + + struct user u; + unsigned int offset = (char *) &u.u_ar0 - (char *) &u; + offset = ptrace (3, inferior_pid, offset, 0) - KERNEL_U_ADDR; + + for (regno = 0; regno < NUM_REGS; regno++) + { + regaddr = register_addr (regno, offset); + for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (int)) + { + *(int *) &buf[i] = ptrace (3, inferior_pid, regaddr, 0); + regaddr += sizeof (int); + } + supply_register (regno, buf); + } +} + +/* Store our register values back into the inferior. + If REGNO is -1, do this for all registers. + Otherwise, REGNO specifies which register (so we can save time). */ + +store_inferior_registers (regno) + int regno; +{ + register unsigned int regaddr; + char buf[80]; + + struct user u; + unsigned int offset = (char *) &u.u_ar0 - (char *) &u; + offset = ptrace (3, inferior_pid, offset, 0) - KERNEL_U_ADDR; + + if (regno >= 0) + { + regaddr = register_addr (regno, offset); + errno = 0; + ptrace (6, inferior_pid, regaddr, read_register (regno)); + if (errno != 0) + { + sprintf (buf, "writing register number %d", regno); + perror_with_name (buf); + } + } + else for (regno = 0; regno < NUM_REGS; regno++) + { + regaddr = register_addr (regno, offset); + errno = 0; + ptrace (6, inferior_pid, regaddr, read_register (regno)); + if (errno != 0) + { + sprintf (buf, "writing all regs, number %d", regno); + perror_with_name (buf); + } + } +} + +/* Copy LEN bytes from inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. + On failure (cannot read from inferior, usually because address is out + of bounds) returns the value of errno. */ + +int +read_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & - sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + extern int errno; + + /* Read all the longwords */ + for (i = 0; i < count; i++, addr += sizeof (int)) + { + errno = 0; +#if 0 + /* This is now done by read_memory, because when this function did it, + reading a byte or short int hardware port read whole longs, causing + serious side effects + such as bus errors and unexpected hardware operation. This would + also be a problem with ptrace if the inferior process could read + or write hardware registers, but that's not usually the case. */ + if (remote_debugging) + buffer[i] = remote_fetch_word (addr); + else +#endif + buffer[i] = ptrace (1, inferior_pid, addr, 0); + if (errno) + return errno; + } + + /* Copy appropriate bytes out of the buffer. */ + bcopy ((char *) buffer + (memaddr & (sizeof (int) - 1)), myaddr, len); + return 0; +} + +/* Copy LEN bytes of data from debugger memory at MYADDR + to inferior's memory at MEMADDR. + On failure (cannot write the inferior) + returns the value of errno. */ + +int +write_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & - sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + extern int errno; + + /* Fill start and end extra bytes of buffer with existing memory data. */ + + if (remote_debugging) + return (remote_write_inferior_memory(memaddr, myaddr, len)); + + buffer[0] = ptrace (1, inferior_pid, addr, 0); + + if (count > 1) + buffer[count - 1] = ptrace (1, inferior_pid, + addr + (count - 1) * sizeof (int), 0); + + /* Copy data to be written over corresponding part of buffer */ + + bcopy (myaddr, (char *) buffer + (memaddr & (sizeof (int) - 1)), len); + + /* Write the entire buffer. */ + + for (i = 0; i < count; i++, addr += sizeof (int)) + { + errno = 0; + ptrace (4, inferior_pid, addr, buffer[i]); + if (errno) + return errno; + } + + return 0; +} + +/* Work with core dump and executable files, for GDB. + This code would be in core.c if it weren't machine-dependent. */ + +#ifndef N_TXTADDR +#define N_TXTADDR(hdr) 0 +#endif /* no N_TXTADDR */ + +#ifndef N_DATADDR +#define N_DATADDR(hdr) hdr.a_text +#endif /* no N_DATADDR */ + +/* Make COFF and non-COFF names for things a little more compatible + to reduce conditionals later. */ + +#ifdef COFF_FORMAT +#define a_magic magic +#endif + +#ifndef COFF_FORMAT +#ifndef AOUTHDR +#define AOUTHDR struct exec +#endif +#endif + +extern char *sys_siglist[]; + + +/* Hook for `exec_file_command' command to call. */ + +extern void (*exec_file_display_hook) (); + +/* File names of core file and executable file. */ + +extern char *corefile; +extern char *execfile; + +/* Descriptors on which core file and executable file are open. + Note that the execchan is closed when an inferior is created + and reopened if the inferior dies or is killed. */ + +extern int corechan; +extern int execchan; + +/* Last modification time of executable file. + Also used in source.c to compare against mtime of a source file. */ + +extern int exec_mtime; + +/* Virtual addresses of bounds of the two areas of memory in the core file. */ + +extern CORE_ADDR data_start; +extern CORE_ADDR data_end; +extern CORE_ADDR stack_start; +extern CORE_ADDR stack_end; + +/* Virtual addresses of bounds of two areas of memory in the exec file. + Note that the data area in the exec file is used only when there is no core file. */ + +extern CORE_ADDR text_start; +extern CORE_ADDR text_end; + +extern CORE_ADDR exec_data_start; +extern CORE_ADDR exec_data_end; + +/* Address in executable file of start of text area data. */ + +extern int text_offset; + +/* Address in executable file of start of data area data. */ + +extern int exec_data_offset; + +/* Address in core file of start of data area data. */ + +extern int data_offset; + +/* Address in core file of start of stack area data. */ + +extern int stack_offset; + +#ifdef COFF_FORMAT +/* various coff data structures */ + +extern FILHDR file_hdr; +extern SCNHDR text_hdr; +extern SCNHDR data_hdr; + +#endif /* not COFF_FORMAT */ + +/* a.out header saved in core file. */ + +extern AOUTHDR core_aouthdr; + +/* a.out header of exec file. */ + +extern AOUTHDR exec_aouthdr; + +extern void validate_files (); + +core_file_command (filename, from_tty) + char *filename; + int from_tty; +{ + int val; + extern char registers[]; + + /* Discard all vestiges of any previous core file + and mark data and stack spaces as empty. */ + + if (corefile) + free (corefile); + corefile = 0; + + if (corechan >= 0) + close (corechan); + corechan = -1; + + data_start = 0; + data_end = 0; + stack_start = STACK_END_ADDR; + stack_end = STACK_END_ADDR; + + /* Now, if a new core file was specified, open it and digest it. */ + + if (filename) + { + filename = tilde_expand (filename); + make_cleanup (free, filename); + + if (have_inferior_p ()) + error ("To look at a core file, you must kill the inferior with \"kill\"."); + corechan = open (filename, O_RDONLY, 0); + if (corechan < 0) + perror_with_name (filename); + /* 4.2-style (and perhaps also sysV-style) core dump file. */ + { + struct user u; + + unsigned int reg_offset; + + val = myread (corechan, &u, sizeof u); + if (val < 0) + perror_with_name ("Not a core file: reading upage"); + if (val != sizeof u) + error ("Not a core file: could only read %d bytes", val); + + /* We are depending on exec_file_command having been called + previously to set exec_data_start. Since the executable + and the core file share the same text segment, the address + of the data segment will be the same in both. */ + data_start = exec_data_start; + + data_end = data_start + NBPG * u.u_dsize; + stack_start = stack_end - NBPG * u.u_ssize; + data_offset = NBPG * UPAGES; + stack_offset = NBPG * (UPAGES + u.u_dsize); + + /* Some machines put an absolute address in here and some put + the offset in the upage of the regs. */ + reg_offset = (int) u.u_ar0; + if (reg_offset > NBPG * UPAGES) + reg_offset -= KERNEL_U_ADDR; + + /* I don't know where to find this info. + So, for now, mark it as not available. */ + N_SET_MAGIC (core_aouthdr, 0); + + /* Read the register values out of the core file and store + them where `read_register' will find them. */ + + { + register int regno; + + for (regno = 0; regno < NUM_REGS; regno++) + { + char buf[MAX_REGISTER_RAW_SIZE]; + + val = lseek (corechan, register_addr (regno, reg_offset), 0); + if (val < 0 + || (val = myread (corechan, buf, sizeof buf)) < 0) + { + char * buffer = (char *) alloca (strlen (reg_names[regno]) + + 30); + strcpy (buffer, "Reading register "); + strcat (buffer, reg_names[regno]); + + perror_with_name (buffer); + } + + supply_register (regno, buf); + } + } + } + if (filename[0] == '/') + corefile = savestring (filename, strlen (filename)); + else + { + corefile = concat (current_directory, "/", filename); + } + + set_current_frame ( create_new_frame (read_register (FP_REGNUM), + read_pc ())); + select_frame (get_current_frame (), 0); + validate_files (); + } + else if (from_tty) + printf ("No core file now.\n"); +} + +exec_file_command (filename, from_tty) + char *filename; + int from_tty; +{ + int val; + + /* Eliminate all traces of old exec file. + Mark text segment as empty. */ + + if (execfile) + free (execfile); + execfile = 0; + data_start = 0; + data_end -= exec_data_start; + text_start = 0; + text_end = 0; + exec_data_start = 0; + exec_data_end = 0; + if (execchan >= 0) + close (execchan); + execchan = -1; + + /* Now open and digest the file the user requested, if any. */ + + if (filename) + { + filename = tilde_expand (filename); + make_cleanup (free, filename); + + execchan = openp (getenv ("PATH"), 1, filename, O_RDONLY, 0, + &execfile); + if (execchan < 0) + perror_with_name (filename); + +#ifdef COFF_FORMAT + { + int aout_hdrsize; + int num_sections; + + if (read_file_hdr (execchan, &file_hdr) < 0) + error ("\"%s\": not in executable format.", execfile); + + aout_hdrsize = file_hdr.f_opthdr; + num_sections = file_hdr.f_nscns; + + if (read_aout_hdr (execchan, &exec_aouthdr, aout_hdrsize) < 0) + error ("\"%s\": can't read optional aouthdr", execfile); + + if (read_section_hdr (execchan, _TEXT, &text_hdr, num_sections, + aout_hdrsize) < 0) + error ("\"%s\": can't read text section header", execfile); + + if (read_section_hdr (execchan, _DATA, &data_hdr, num_sections, + aout_hdrsize) < 0) + error ("\"%s\": can't read data section header", execfile); + + text_start = exec_aouthdr.text_start; + text_end = text_start + exec_aouthdr.tsize; + text_offset = text_hdr.s_scnptr; + exec_data_start = exec_aouthdr.data_start; + exec_data_end = exec_data_start + exec_aouthdr.dsize; + exec_data_offset = data_hdr.s_scnptr; + data_start = exec_data_start; + data_end += exec_data_start; + exec_mtime = file_hdr.f_timdat; + } +#else /* not COFF_FORMAT */ + { + struct stat st_exec; + +#ifdef HEADER_SEEK_FD + HEADER_SEEK_FD (execchan); +#endif + + val = myread (execchan, &exec_aouthdr, sizeof (AOUTHDR)); + + if (val < 0) + perror_with_name (filename); + + text_start = N_TXTADDR (exec_aouthdr); + exec_data_start = N_DATADDR (exec_aouthdr); + + text_offset = N_TXTOFF (exec_aouthdr); + exec_data_offset = N_TXTOFF (exec_aouthdr) + exec_aouthdr.a_text; + + text_end = text_start + exec_aouthdr.a_text; + exec_data_end = exec_data_start + exec_aouthdr.a_data; + data_start = exec_data_start; + data_end += exec_data_start; + + if (fstat (execchan, &st_exec) < 0) + perror_with_name (filename); + exec_mtime = st_exec.st_mtime; + } +#endif /* not COFF_FORMAT */ + + validate_files (); + } + else if (from_tty) + printf ("No exec file now.\n"); + + /* Tell display code (if any) about the changed file name. */ + if (exec_file_display_hook) + (*exec_file_display_hook) (filename); +} diff --git a/gnu/usr.bin/gdb/config/i386-dep.c b/gnu/usr.bin/gdb/config/i386-dep.c new file mode 100644 index 0000000000..c4630d0c07 --- /dev/null +++ b/gnu/usr.bin/gdb/config/i386-dep.c @@ -0,0 +1,1275 @@ +/* Low level interface to ptrace, for GDB when running on the Intel 386. + Copyright (C) 1988, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include "defs.h" +#include "param.h" +#include "frame.h" +#include "inferior.h" + +#ifdef USG +#include +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef COFF_ENCAPSULATE +#include "a.out.encap.h" +#else +#include +#endif + +#ifndef N_SET_MAGIC +#ifdef COFF_FORMAT +#define N_SET_MAGIC(exec, val) ((exec).magic = (val)) +#else +#define N_SET_MAGIC(exec, val) ((exec).a_magic = (val)) +#endif +#endif + +#include +#include + +#include + +extern int errno; + +/* This function simply calls ptrace with the given arguments. + It exists so that all calls to ptrace are isolated in this + machine-dependent file. */ +int +call_ptrace (request, pid, arg3, arg4) + int request, pid, arg3, arg4; +{ + return ptrace (request, pid, arg3, arg4); +} + +kill_inferior () +{ + if (remote_debugging) + return; + if (inferior_pid == 0) + return; + ptrace (8, inferior_pid, 0, 0); + wait (0); + inferior_died (); +} + +/* This is used when GDB is exiting. It gives less chance of error.*/ + +kill_inferior_fast () +{ + if (remote_debugging) + return; + if (inferior_pid == 0) + return; + ptrace (8, inferior_pid, 0, 0); + wait (0); +} + +/* Resume execution of the inferior process. + If STEP is nonzero, single-step it. + If SIGNAL is nonzero, give it that signal. */ + +void +resume (step, signal) + int step; + int signal; +{ + errno = 0; + if (remote_debugging) + remote_resume (step, signal); + else + { + ptrace (step ? 9 : 7, inferior_pid, 1, signal); + if (errno) + perror_with_name ("ptrace"); + } +} + +void +fetch_inferior_registers () +{ + register int regno; + register unsigned int regaddr; + char buf[MAX_REGISTER_RAW_SIZE]; + register int i; + + struct user u; + unsigned int offset = (char *) &u.u_ar0 - (char *) &u; + offset = ptrace (3, inferior_pid, offset, 0) - KERNEL_U_ADDR; + + for (regno = 0; regno < NUM_REGS; regno++) + { + regaddr = register_addr (regno, offset); + for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (int)) + { + *(int *) &buf[i] = ptrace (3, inferior_pid, regaddr, 0); + regaddr += sizeof (int); + } + supply_register (regno, buf); + } +} + +/* Store our register values back into the inferior. + If REGNO is -1, do this for all registers. + Otherwise, REGNO specifies which register (so we can save time). */ + +store_inferior_registers (regno) + int regno; +{ + register unsigned int regaddr; + char buf[80]; + + struct user u; + unsigned int offset = (char *) &u.u_ar0 - (char *) &u; + offset = ptrace (3, inferior_pid, offset, 0) - KERNEL_U_ADDR; + + if (regno >= 0) + { + regaddr = register_addr (regno, offset); + errno = 0; + ptrace (6, inferior_pid, regaddr, read_register (regno)); + if (errno != 0) + { + sprintf (buf, "writing register number %d", regno); + perror_with_name (buf); + } + } + else for (regno = 0; regno < NUM_REGS; regno++) + { + regaddr = register_addr (regno, offset); + errno = 0; + ptrace (6, inferior_pid, regaddr, read_register (regno)); + if (errno != 0) + { + sprintf (buf, "writing register number %d", regno); + perror_with_name (buf); + } + } +} + +/* Copy LEN bytes from inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. + On failure (cannot read from inferior, usually because address is out + of bounds) returns the value of errno. */ + +int +read_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & - sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + extern int errno; + + /* Read all the longwords */ + for (i = 0; i < count; i++, addr += sizeof (int)) + { + errno = 0; + if (remote_debugging) + buffer[i] = remote_fetch_word (addr); + else + buffer[i] = ptrace (1, inferior_pid, addr, 0); + if (errno) + return errno; + } + + /* Copy appropriate bytes out of the buffer. */ + bcopy ((char *) buffer + (memaddr & (sizeof (int) - 1)), myaddr, len); + return 0; +} + +/* Copy LEN bytes of data from debugger memory at MYADDR + to inferior's memory at MEMADDR. + On failure (cannot write the inferior) + returns the value of errno. */ + +int +write_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & - sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + extern int errno; + + /* Fill start and end extra bytes of buffer with existing memory data. */ + + if (remote_debugging) + buffer[0] = remote_fetch_word (addr); + else + buffer[0] = ptrace (1, inferior_pid, addr, 0); + + if (count > 1) + { + if (remote_debugging) + buffer[count - 1] + = remote_fetch_word (addr + (count - 1) * sizeof (int)); + else + buffer[count - 1] + = ptrace (1, inferior_pid, + addr + (count - 1) * sizeof (int), 0); + } + + /* Copy data to be written over corresponding part of buffer */ + + bcopy (myaddr, (char *) buffer + (memaddr & (sizeof (int) - 1)), len); + + /* Write the entire buffer. */ + + for (i = 0; i < count; i++, addr += sizeof (int)) + { + errno = 0; + if (remote_debugging) + remote_store_word (addr, buffer[i]); + else + ptrace (4, inferior_pid, addr, buffer[i]); + if (errno) + return errno; + } + + return 0; +} + +/* Work with core dump and executable files, for GDB. + This code would be in core.c if it weren't machine-dependent. */ + +#ifndef N_TXTADDR +#define N_TXTADDR(hdr) 0 +#endif /* no N_TXTADDR */ + +#ifndef N_DATADDR +#define N_DATADDR(hdr) hdr.a_text +#endif /* no N_DATADDR */ + +/* Make COFF and non-COFF names for things a little more compatible + to reduce conditionals later. */ + +#ifndef COFF_FORMAT +#ifndef AOUTHDR +#define AOUTHDR struct exec +#endif +#endif + +extern char *sys_siglist[]; + + +/* Hook for `exec_file_command' command to call. */ + +extern void (*exec_file_display_hook) (); + +/* File names of core file and executable file. */ + +extern char *corefile; +extern char *execfile; + +/* Descriptors on which core file and executable file are open. + Note that the execchan is closed when an inferior is created + and reopened if the inferior dies or is killed. */ + +extern int corechan; +extern int execchan; + +/* Last modification time of executable file. + Also used in source.c to compare against mtime of a source file. */ + +extern int exec_mtime; + +/* Virtual addresses of bounds of the two areas of memory in the core file. */ + +extern CORE_ADDR data_start; +extern CORE_ADDR data_end; +extern CORE_ADDR stack_start; +extern CORE_ADDR stack_end; + +/* Virtual addresses of bounds of two areas of memory in the exec file. + Note that the data area in the exec file is used only when there is no core file. */ + +extern CORE_ADDR text_start; +extern CORE_ADDR text_end; + +extern CORE_ADDR exec_data_start; +extern CORE_ADDR exec_data_end; + +/* Address in executable file of start of text area data. */ + +extern int text_offset; + +/* Address in executable file of start of data area data. */ + +extern int exec_data_offset; + +/* Address in core file of start of data area data. */ + +extern int data_offset; + +/* Address in core file of start of stack area data. */ + +extern int stack_offset; + +#ifdef COFF_FORMAT +/* various coff data structures */ + +extern FILHDR file_hdr; +extern SCNHDR text_hdr; +extern SCNHDR data_hdr; + +#endif /* not COFF_FORMAT */ + +/* a.out header saved in core file. */ + +extern AOUTHDR core_aouthdr; + +/* a.out header of exec file. */ + +extern AOUTHDR exec_aouthdr; + +extern void validate_files (); + +core_file_command (filename, from_tty) + char *filename; + int from_tty; +{ + int val; + extern char registers[]; + + /* Discard all vestiges of any previous core file + and mark data and stack spaces as empty. */ + + if (corefile) + free (corefile); + corefile = 0; + + if (corechan >= 0) + close (corechan); + corechan = -1; + + data_start = 0; + data_end = 0; + stack_start = STACK_END_ADDR; + stack_end = STACK_END_ADDR; + + /* Now, if a new core file was specified, open it and digest it. */ + + if (filename) + { + filename = tilde_expand (filename); + make_cleanup (free, filename); + + if (have_inferior_p ()) + error ("To look at a core file, you must kill the inferior with \"kill\"."); + corechan = open (filename, O_RDONLY, 0); + if (corechan < 0) + perror_with_name (filename); + /* 4.2-style (and perhaps also sysV-style) core dump file. */ + { + struct user u; + + int reg_offset; + + val = myread (corechan, &u, sizeof u); + if (val < 0) + perror_with_name (filename); + data_start = exec_data_start; + + data_end = data_start + NBPG * u.u_dsize; + stack_start = stack_end - NBPG * u.u_ssize; + data_offset = NBPG * UPAGES; + stack_offset = NBPG * (UPAGES + u.u_dsize); + reg_offset = (int) u.u_ar0 - KERNEL_U_ADDR; + + /* I don't know where to find this info. + So, for now, mark it as not available. */ +/* N_SET_MAGIC (core_aouthdr, 0); */ + bzero ((char *) &core_aouthdr, sizeof core_aouthdr); + + /* Read the register values out of the core file and store + them where `read_register' will find them. */ + + { + register int regno; + + for (regno = 0; regno < NUM_REGS; regno++) + { + char buf[MAX_REGISTER_RAW_SIZE]; + + val = lseek (corechan, register_addr (regno, reg_offset), 0); + if (val < 0) + perror_with_name (filename); + + val = myread (corechan, buf, sizeof buf); + if (val < 0) + perror_with_name (filename); + supply_register (regno, buf); + } + } + } + if (filename[0] == '/') + corefile = savestring (filename, strlen (filename)); + else + { + corefile = concat (current_directory, "/", filename); + } + + set_current_frame ( create_new_frame (read_register (FP_REGNUM), + read_pc ())); + select_frame (get_current_frame (), 0); + validate_files (); + } + else if (from_tty) + printf ("No core file now.\n"); +} + +exec_file_command (filename, from_tty) + char *filename; + int from_tty; +{ + int val; + + /* Eliminate all traces of old exec file. + Mark text segment as empty. */ + + if (execfile) + free (execfile); + execfile = 0; + data_start = 0; + data_end -= exec_data_start; + text_start = 0; + text_end = 0; + exec_data_start = 0; + exec_data_end = 0; + if (execchan >= 0) + close (execchan); + execchan = -1; + + /* Now open and digest the file the user requested, if any. */ + + if (filename) + { + filename = tilde_expand (filename); + make_cleanup (free, filename); + + execchan = openp (getenv ("PATH"), 1, filename, O_RDONLY, 0, + &execfile); + if (execchan < 0) + perror_with_name (filename); + +#ifdef COFF_FORMAT + { + int aout_hdrsize; + int num_sections; + + if (read_file_hdr (execchan, &file_hdr) < 0) + error ("\"%s\": not in executable format.", execfile); + + aout_hdrsize = file_hdr.f_opthdr; + num_sections = file_hdr.f_nscns; + + if (read_aout_hdr (execchan, &exec_aouthdr, aout_hdrsize) < 0) + error ("\"%s\": can't read optional aouthdr", execfile); + + if (read_section_hdr (execchan, _TEXT, &text_hdr, num_sections, + aout_hdrsize) < 0) + error ("\"%s\": can't read text section header", execfile); + + if (read_section_hdr (execchan, _DATA, &data_hdr, num_sections, + aout_hdrsize) < 0) + error ("\"%s\": can't read data section header", execfile); + + text_start = exec_aouthdr.text_start; + text_end = text_start + exec_aouthdr.tsize; + text_offset = text_hdr.s_scnptr; + exec_data_start = exec_aouthdr.data_start; + exec_data_end = exec_data_start + exec_aouthdr.dsize; + exec_data_offset = data_hdr.s_scnptr; + data_start = exec_data_start; + data_end += exec_data_start; + exec_mtime = file_hdr.f_timdat; + } +#else /* not COFF_FORMAT */ + { + struct stat st_exec; + +#ifdef HEADER_SEEK_FD + HEADER_SEEK_FD (execchan); +#endif + + val = myread (execchan, &exec_aouthdr, sizeof (AOUTHDR)); + + if (val < 0) + perror_with_name (filename); + + text_start = N_TXTADDR (exec_aouthdr); + exec_data_start = N_DATADDR (exec_aouthdr); + + text_offset = N_TXTOFF (exec_aouthdr); + exec_data_offset = N_TXTOFF (exec_aouthdr) + exec_aouthdr.a_text; + + text_end = text_start + exec_aouthdr.a_text; + exec_data_end = exec_data_start + exec_aouthdr.a_data; + data_start = exec_data_start; + data_end += exec_data_start; + + fstat (execchan, &st_exec); + exec_mtime = st_exec.st_mtime; + } +#endif /* not COFF_FORMAT */ + + validate_files (); + } + else if (from_tty) + printf ("No exec file now.\n"); + + /* Tell display code (if any) about the changed file name. */ + if (exec_file_display_hook) + (*exec_file_display_hook) (filename); +} + +/* helper functions for m-i386.h */ + +/* stdio style buffering to minimize calls to ptrace */ +static CORE_ADDR codestream_next_addr; +static CORE_ADDR codestream_addr; +static unsigned char codestream_buf[sizeof (int)]; +static int codestream_off; +static int codestream_cnt; + +#define codestream_tell() (codestream_addr + codestream_off) +#define codestream_peek() (codestream_cnt == 0 ? \ + codestream_fill(1): codestream_buf[codestream_off]) +#define codestream_get() (codestream_cnt-- == 0 ? \ + codestream_fill(0) : codestream_buf[codestream_off++]) + +static unsigned char +codestream_fill (peek_flag) +{ + codestream_addr = codestream_next_addr; + codestream_next_addr += sizeof (int); + codestream_off = 0; + codestream_cnt = sizeof (int); + read_memory (codestream_addr, + (unsigned char *)codestream_buf, + sizeof (int)); + + if (peek_flag) + return (codestream_peek()); + else + return (codestream_get()); +} + +static void +codestream_seek (place) +{ + codestream_next_addr = place & -sizeof (int); + codestream_cnt = 0; + codestream_fill (1); + while (codestream_tell() != place) + codestream_get (); +} + +static void +codestream_read (buf, count) + unsigned char *buf; +{ + unsigned char *p; + int i; + p = buf; + for (i = 0; i < count; i++) + *p++ = codestream_get (); +} + +/* next instruction is a jump, move to target */ +static +i386_follow_jump () +{ + int long_delta; + short short_delta; + char byte_delta; + int data16; + int pos; + + pos = codestream_tell (); + + data16 = 0; + if (codestream_peek () == 0x66) + { + codestream_get (); + data16 = 1; + } + + switch (codestream_get ()) + { + case 0xe9: + /* relative jump: if data16 == 0, disp32, else disp16 */ + if (data16) + { + codestream_read ((unsigned char *)&short_delta, 2); + pos += short_delta + 3; /* include size of jmp inst */ + } + else + { + codestream_read ((unsigned char *)&long_delta, 4); + pos += long_delta + 5; + } + break; + case 0xeb: + /* relative jump, disp8 (ignore data16) */ + codestream_read ((unsigned char *)&byte_delta, 1); + pos += byte_delta + 2; + break; + } + codestream_seek (pos + data16); +} + +/* + * find & return amound a local space allocated, and advance codestream to + * first register push (if any) + * + * if entry sequence doesn't make sense, return -1, and leave + * codestream pointer random + */ +static long +i386_get_frame_setup (pc) +{ + unsigned char op; + + codestream_seek (pc); + + i386_follow_jump (); + + op = codestream_get (); + + if (op == 0x58) /* popl %eax */ + { + /* + * this function must start with + * + * popl %eax 0x58 + * xchgl %eax, (%esp) 0x87 0x04 0x24 + * or xchgl %eax, 0(%esp) 0x87 0x44 0x24 0x00 + * + * (the system 5 compiler puts out the second xchg + * inst, and the assembler doesn't try to optimize it, + * so the 'sib' form gets generated) + * + * this sequence is used to get the address of the return + * buffer for a function that returns a structure + */ + int pos; + unsigned char buf[4]; + static unsigned char proto1[3] = { 0x87,0x04,0x24 }; + static unsigned char proto2[4] = { 0x87,0x44,0x24,0x00 }; + pos = codestream_tell (); + codestream_read (buf, 4); + if (bcmp (buf, proto1, 3) == 0) + pos += 3; + else if (bcmp (buf, proto2, 4) == 0) + pos += 4; + + codestream_seek (pos); + op = codestream_get (); /* update next opcode */ + } + + if (op == 0x55) /* pushl %esp */ + { + /* check for movl %esp, %ebp - can be written two ways */ + switch (codestream_get ()) + { + case 0x8b: + if (codestream_get () != 0xec) + return (-1); + break; + case 0x89: + if (codestream_get () != 0xe5) + return (-1); + break; + default: + return (-1); + } + /* check for stack adjustment + * + * subl $XXX, %esp + * + * note: you can't subtract a 16 bit immediate + * from a 32 bit reg, so we don't have to worry + * about a data16 prefix + */ + op = codestream_peek (); + if (op == 0x83) + { + /* subl with 8 bit immed */ + codestream_get (); + if (codestream_get () != 0xec) + return (-1); + /* subl with signed byte immediate + * (though it wouldn't make sense to be negative) + */ + return (codestream_get()); + } + else if (op == 0x81) + { + /* subl with 32 bit immed */ + int locals; + codestream_get(); + if (codestream_get () != 0xec) + return (-1); + /* subl with 32 bit immediate */ + codestream_read ((unsigned char *)&locals, 4); + return (locals); + } + else + { + return (0); + } + } + else if (op == 0xc8) + { + /* enter instruction: arg is 16 bit unsigned immed */ + unsigned short slocals; + codestream_read ((unsigned char *)&slocals, 2); + codestream_get (); /* flush final byte of enter instruction */ + return (slocals); + } + return (-1); +} + +/* Return number of args passed to a frame. + Can return -1, meaning no way to tell. */ + +/* on the 386, the instruction following the call could be: + * popl %ecx - one arg + * addl $imm, %esp - imm/4 args; imm may be 8 or 32 bits + * anything else - zero args + */ + +int +i386_frame_num_args (fi) + struct frame_info fi; +{ + int retpc; + unsigned char op; + struct frame_info *pfi; + + pfi = get_prev_frame_info ((fi)); + if (pfi == 0) + { + /* Note: this can happen if we are looking at the frame for + main, because FRAME_CHAIN_VALID won't let us go into + start. If we have debugging symbols, that's not really + a big deal; it just means it will only show as many arguments + to main as are declared. */ + return -1; + } + else + { + retpc = pfi->pc; + op = read_memory_integer (retpc, 1); + if (op == 0x59) + /* pop %ecx */ + return 1; + else if (op == 0x83) + { + op = read_memory_integer (retpc+1, 1); + if (op == 0xc4) + /* addl $, %esp */ + return (read_memory_integer (retpc+2,1)&0xff)/4; + else + return 0; + } + else if (op == 0x81) + { /* add with 32 bit immediate */ + op = read_memory_integer (retpc+1, 1); + if (op == 0xc4) + /* addl $, %esp */ + return read_memory_integer (retpc+2, 4) / 4; + else + return 0; + } + else + { + return 0; + } + } +} + +/* + * parse the first few instructions of the function to see + * what registers were stored. + * + * We handle these cases: + * + * The startup sequence can be at the start of the function, + * or the function can start with a branch to startup code at the end. + * + * %ebp can be set up with either the 'enter' instruction, or + * 'pushl %ebp, movl %esp, %ebp' (enter is too slow to be useful, + * but was once used in the sys5 compiler) + * + * Local space is allocated just below the saved %ebp by either the + * 'enter' instruction, or by 'subl $, %esp'. 'enter' has + * a 16 bit unsigned argument for space to allocate, and the + * 'addl' instruction could have either a signed byte, or + * 32 bit immediate. + * + * Next, the registers used by this function are pushed. In + * the sys5 compiler they will always be in the order: %edi, %esi, %ebx + * (and sometimes a harmless bug causes it to also save but not restore %eax); + * however, the code below is willing to see the pushes in any order, + * and will handle up to 8 of them. + * + * If the setup sequence is at the end of the function, then the + * next instruction will be a branch back to the start. + */ + +i386_frame_find_saved_regs (fip, fsrp) + struct frame_info *fip; + struct frame_saved_regs *fsrp; +{ + unsigned long locals; + unsigned char *p; + unsigned char op; + CORE_ADDR dummy_bottom; + CORE_ADDR adr; + int i; + + bzero (fsrp, sizeof *fsrp); + + /* if frame is the end of a dummy, compute where the + * beginning would be + */ + dummy_bottom = fip->frame - 4 - NUM_REGS*4 - CALL_DUMMY_LENGTH; + + /* check if the PC is in the stack, in a dummy frame */ + if (dummy_bottom <= fip->pc && fip->pc <= fip->frame) + { + /* all regs were saved by push_call_dummy () */ + adr = fip->frame - 4; + for (i = 0; i < NUM_REGS; i++) + { + fsrp->regs[i] = adr; + adr -= 4; + } + return; + } + + locals = i386_get_frame_setup (get_pc_function_start (fip->pc)); + + if (locals >= 0) + { + adr = fip->frame - 4 - locals; + for (i = 0; i < 8; i++) + { + op = codestream_get (); + if (op < 0x50 || op > 0x57) + break; + fsrp->regs[op - 0x50] = adr; + adr -= 4; + } + } + + fsrp->regs[PC_REGNUM] = fip->frame + 4; + fsrp->regs[FP_REGNUM] = fip->frame; +} + +/* return pc of first real instruction */ +i386_skip_prologue (pc) +{ + unsigned char op; + int i; + + if (i386_get_frame_setup (pc) < 0) + return (pc); + + /* found valid frame setup - codestream now points to + * start of push instructions for saving registers + */ + + /* skip over register saves */ + for (i = 0; i < 8; i++) + { + op = codestream_peek (); + /* break if not pushl inst */ + if (op < 0x50 || op > 0x57) + break; + codestream_get (); + } + + i386_follow_jump (); + + return (codestream_tell ()); +} + +i386_push_dummy_frame () +{ + CORE_ADDR sp = read_register (SP_REGNUM); + int regnum; + + sp = push_word (sp, read_register (PC_REGNUM)); + sp = push_word (sp, read_register (FP_REGNUM)); + write_register (FP_REGNUM, sp); + for (regnum = 0; regnum < NUM_REGS; regnum++) + sp = push_word (sp, read_register (regnum)); + write_register (SP_REGNUM, sp); +} + +i386_pop_frame () +{ + FRAME frame = get_current_frame (); + CORE_ADDR fp; + int regnum; + struct frame_saved_regs fsr; + struct frame_info *fi; + + fi = get_frame_info (frame); + fp = fi->frame; + get_frame_saved_regs (fi, &fsr); + for (regnum = 0; regnum < NUM_REGS; regnum++) + { + CORE_ADDR adr; + adr = fsr.regs[regnum]; + if (adr) + write_register (regnum, read_memory_integer (adr, 4)); + } + write_register (FP_REGNUM, read_memory_integer (fp, 4)); + write_register (PC_REGNUM, read_memory_integer (fp + 4, 4)); + write_register (SP_REGNUM, fp + 8); + flush_cached_frames (); + set_current_frame ( create_new_frame (read_register (FP_REGNUM), + read_pc ())); +} + +/* this table must line up with REGISTER_NAMES in m-i386.h */ +/* symbols like 'EAX' come from */ +static int regmap[] = +{ + EAX, ECX, EDX, EBX, + UESP, EBP, ESI, EDI, + EIP, EFL, CS, SS, + DS, ES, FS, GS, +}; + +/* blockend is the value of u.u_ar0, and points to the + * place where GS is stored + */ +i386_register_u_addr (blockend, regnum) +{ +#if 0 + /* this will be needed if fp registers are reinstated */ + /* for now, you can look at them with 'info float' + * sys5 wont let you change them with ptrace anyway + */ + if (regnum >= FP0_REGNUM && regnum <= FP7_REGNUM) + { + int ubase, fpstate; + struct user u; + ubase = blockend + 4 * (SS + 1) - KSTKSZ; + fpstate = ubase + ((char *)&u.u_fpstate - (char *)&u); + return (fpstate + 0x1c + 10 * (regnum - FP0_REGNUM)); + } + else +#endif + return (blockend + 4 * regmap[regnum]); + +} + +i387_to_double (from, to) + char *from; + char *to; +{ + long *lp; + /* push extended mode on 387 stack, then pop in double mode + * + * first, set exception masks so no error is generated - + * number will be rounded to inf or 0, if necessary + */ + asm ("pushl %eax"); /* grab a stack slot */ + asm ("fstcw (%esp)"); /* get 387 control word */ + asm ("movl (%esp),%eax"); /* save old value */ + asm ("orl $0x3f,%eax"); /* mask all exceptions */ + asm ("pushl %eax"); + asm ("fldcw (%esp)"); /* load new value into 387 */ + + asm ("movl 8(%ebp),%eax"); + asm ("fldt (%eax)"); /* push extended number on 387 stack */ + asm ("fwait"); + asm ("movl 12(%ebp),%eax"); + asm ("fstpl (%eax)"); /* pop double */ + asm ("fwait"); + + asm ("popl %eax"); /* flush modified control word */ + asm ("fnclex"); /* clear exceptions */ + asm ("fldcw (%esp)"); /* restore original control word */ + asm ("popl %eax"); /* flush saved copy */ +} + +double_to_i387 (from, to) + char *from; + char *to; +{ + /* push double mode on 387 stack, then pop in extended mode + * no errors are possible because every 64-bit pattern + * can be converted to an extended + */ + asm ("movl 8(%ebp),%eax"); + asm ("fldl (%eax)"); + asm ("fwait"); + asm ("movl 12(%ebp),%eax"); + asm ("fstpt (%eax)"); + asm ("fwait"); +} + +struct env387 +{ + unsigned short control; + unsigned short r0; + unsigned short status; + unsigned short r1; + unsigned short tag; + unsigned short r2; + unsigned long eip; + unsigned short code_seg; + unsigned short opcode; + unsigned long operand; + unsigned short operand_seg; + unsigned short r3; + unsigned char regs[8][10]; +}; + +static +print_387_control_word (control) +unsigned short control; +{ + printf ("control 0x%04x: ", control); + printf ("compute to "); + switch ((control >> 8) & 3) + { + case 0: printf ("24 bits; "); break; + case 1: printf ("(bad); "); break; + case 2: printf ("53 bits; "); break; + case 3: printf ("64 bits; "); break; + } + printf ("round "); + switch ((control >> 10) & 3) + { + case 0: printf ("NEAREST; "); break; + case 1: printf ("DOWN; "); break; + case 2: printf ("UP; "); break; + case 3: printf ("CHOP; "); break; + } + if (control & 0x3f) + { + printf ("mask:"); + if (control & 0x0001) printf (" INVALID"); + if (control & 0x0002) printf (" DENORM"); + if (control & 0x0004) printf (" DIVZ"); + if (control & 0x0008) printf (" OVERF"); + if (control & 0x0010) printf (" UNDERF"); + if (control & 0x0020) printf (" LOS"); + printf (";"); + } + printf ("\n"); + if (control & 0xe080) printf ("warning: reserved bits on 0x%x\n", + control & 0xe080); +} + +static +print_387_status_word (status) + unsigned short status; +{ + printf ("status 0x%04x: ", status); + if (status & 0xff) + { + printf ("exceptions:"); + if (status & 0x0001) printf (" INVALID"); + if (status & 0x0002) printf (" DENORM"); + if (status & 0x0004) printf (" DIVZ"); + if (status & 0x0008) printf (" OVERF"); + if (status & 0x0010) printf (" UNDERF"); + if (status & 0x0020) printf (" LOS"); + if (status & 0x0040) printf (" FPSTACK"); + printf ("; "); + } + printf ("flags: %d%d%d%d; ", + (status & 0x4000) != 0, + (status & 0x0400) != 0, + (status & 0x0200) != 0, + (status & 0x0100) != 0); + + printf ("top %d\n", (status >> 11) & 7); +} + +static +print_387_status (status, ep) + unsigned short status; + struct env387 *ep; +{ + int i; + int bothstatus; + int top; + int fpreg; + unsigned char *p; + + bothstatus = ((status != 0) && (ep->status != 0)); + if (status != 0) + { + if (bothstatus) + printf ("u: "); + print_387_status_word (status); + } + + if (ep->status != 0) + { + if (bothstatus) + printf ("e: "); + print_387_status_word (ep->status); + } + + print_387_control_word (ep->control); + printf ("last exception: "); + printf ("opcode 0x%x; ", ep->opcode); + printf ("pc 0x%x:0x%x; ", ep->code_seg, ep->eip); + printf ("operand 0x%x:0x%x\n", ep->operand_seg, ep->operand); + + top = (ep->status >> 11) & 7; + + printf ("regno tag msb lsb value\n"); + for (fpreg = 7; fpreg >= 0; fpreg--) + { + double val; + + printf ("%s %d: ", fpreg == top ? "=>" : " ", fpreg); + + switch ((ep->tag >> (fpreg * 2)) & 3) + { + case 0: printf ("valid "); break; + case 1: printf ("zero "); break; + case 2: printf ("trap "); break; + case 3: printf ("empty "); break; + } + for (i = 9; i >= 0; i--) + printf ("%02x", ep->regs[fpreg][i]); + + i387_to_double (ep->regs[fpreg], (char *)&val); + printf (" %g\n", val); + } + if (ep->r0) + printf ("warning: reserved0 is 0x%x\n", ep->r0); + if (ep->r1) + printf ("warning: reserved1 is 0x%x\n", ep->r1); + if (ep->r2) + printf ("warning: reserved2 is 0x%x\n", ep->r2); + if (ep->r3) + printf ("warning: reserved3 is 0x%x\n", ep->r3); +} + +#ifndef U_FPSTATE +#define U_FPSTATE(u) u.u_fpstate +#endif + +i386_float_info () +{ + struct user u; /* just for address computations */ + int i; + /* fpstate defined in */ + struct fpstate *fpstatep; + char buf[sizeof (struct fpstate) + 2 * sizeof (int)]; + unsigned int uaddr; + char fpvalid; + unsigned int rounded_addr; + unsigned int rounded_size; + extern int corechan; + int skip; + + uaddr = (char *)&u.u_fpvalid - (char *)&u; + if (have_inferior_p()) + { + unsigned int data; + unsigned int mask; + + rounded_addr = uaddr & -sizeof (int); + data = ptrace (3, inferior_pid, rounded_addr, 0); + mask = 0xff << ((uaddr - rounded_addr) * 8); + + fpvalid = ((data & mask) != 0); + } + else + { + if (lseek (corechan, uaddr, 0) < 0) + perror ("seek on core file"); + if (myread (corechan, &fpvalid, 1) < 0) + perror ("read on core file"); + + } + + if (fpvalid == 0) + { + printf ("no floating point status saved\n"); + return; + } + + uaddr = (char *)&U_FPSTATE(u) - (char *)&u; + if (have_inferior_p ()) + { + int *ip; + + rounded_addr = uaddr & -sizeof (int); + rounded_size = (((uaddr + sizeof (struct fpstate)) - uaddr) + + sizeof (int) - 1) / sizeof (int); + skip = uaddr - rounded_addr; + + ip = (int *)buf; + for (i = 0; i < rounded_size; i++) + { + *ip++ = ptrace (3, inferior_pid, rounded_addr, 0); + rounded_addr += sizeof (int); + } + } + else + { + if (lseek (corechan, uaddr, 0) < 0) + perror_with_name ("seek on core file"); + if (myread (corechan, buf, sizeof (struct fpstate)) < 0) + perror_with_name ("read from core file"); + skip = 0; + } + + fpstatep = (struct fpstate *)(buf + skip); + print_387_status (fpstatep->status, (struct env387 *)fpstatep->state); +} + diff --git a/gnu/usr.bin/gdb/config/i386-pinsn.c b/gnu/usr.bin/gdb/config/i386-pinsn.c new file mode 100644 index 0000000000..649baaf56b --- /dev/null +++ b/gnu/usr.bin/gdb/config/i386-pinsn.c @@ -0,0 +1,1812 @@ +/* Print i386 instructions for GDB, the GNU debugger. + Copyright (C) 1988, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * 80386 instruction printer by Pace Willisson (pace@prep.ai.mit.edu) + * July 1988 + */ + +/* + * The main tables describing the instructions is essentially a copy + * of the "Opcode Map" chapter (Appendix A) of the Intel 80386 + * Programmers Manual. Usually, there is a capital letter, followed + * by a small letter. The capital letter tell the addressing mode, + * and the small letter tells about the operand size. Refer to + * the Intel manual for details. + */ + +#include +#include + +#define Eb OP_E, b_mode +#define indirEb OP_indirE, b_mode +#define Gb OP_G, b_mode +#define Ev OP_E, v_mode +#define indirEv OP_indirE, v_mode +#define Ew OP_E, w_mode +#define Ma OP_E, v_mode +#define M OP_E, 0 +#define Mp OP_E, 0 /* ? */ +#define Gv OP_G, v_mode +#define Gw OP_G, w_mode +#define Rw OP_rm, w_mode +#define Rd OP_rm, d_mode +#define Ib OP_I, b_mode +#define sIb OP_sI, b_mode /* sign extened byte */ +#define Iv OP_I, v_mode +#define Iw OP_I, w_mode +#define Jb OP_J, b_mode +#define Jv OP_J, v_mode +#define ONE OP_ONE, 0 +#define Cd OP_C, d_mode +#define Dd OP_D, d_mode +#define Td OP_T, d_mode + +#define eAX OP_REG, eAX_reg +#define eBX OP_REG, eBX_reg +#define eCX OP_REG, eCX_reg +#define eDX OP_REG, eDX_reg +#define eSP OP_REG, eSP_reg +#define eBP OP_REG, eBP_reg +#define eSI OP_REG, eSI_reg +#define eDI OP_REG, eDI_reg +#define AL OP_REG, al_reg +#define CL OP_REG, cl_reg +#define DL OP_REG, dl_reg +#define BL OP_REG, bl_reg +#define AH OP_REG, ah_reg +#define CH OP_REG, ch_reg +#define DH OP_REG, dh_reg +#define BH OP_REG, bh_reg +#define AX OP_REG, ax_reg +#define DX OP_REG, dx_reg +#define indirDX OP_REG, indir_dx_reg + +#define Sw OP_SEG, w_mode +#define Ap OP_DIR, lptr +#define Av OP_DIR, v_mode +#define Ob OP_OFF, b_mode +#define Ov OP_OFF, v_mode +#define Xb OP_DSSI, b_mode +#define Xv OP_DSSI, v_mode +#define Yb OP_ESDI, b_mode +#define Yv OP_ESDI, v_mode + +#define es OP_REG, es_reg +#define ss OP_REG, ss_reg +#define cs OP_REG, cs_reg +#define ds OP_REG, ds_reg +#define fs OP_REG, fs_reg +#define gs OP_REG, gs_reg + +int OP_E(), OP_indirE(), OP_G(), OP_I(), OP_sI(), OP_REG(); +int OP_J(), OP_SEG(); +int OP_DIR(), OP_OFF(), OP_DSSI(), OP_ESDI(), OP_ONE(), OP_C(); +int OP_D(), OP_T(), OP_rm(); + + +#define b_mode 1 +#define v_mode 2 +#define w_mode 3 +#define d_mode 4 + +#define es_reg 100 +#define cs_reg 101 +#define ss_reg 102 +#define ds_reg 103 +#define fs_reg 104 +#define gs_reg 105 +#define eAX_reg 107 +#define eCX_reg 108 +#define eDX_reg 109 +#define eBX_reg 110 +#define eSP_reg 111 +#define eBP_reg 112 +#define eSI_reg 113 +#define eDI_reg 114 + +#define lptr 115 + +#define al_reg 116 +#define cl_reg 117 +#define dl_reg 118 +#define bl_reg 119 +#define ah_reg 120 +#define ch_reg 121 +#define dh_reg 122 +#define bh_reg 123 + +#define ax_reg 124 +#define cx_reg 125 +#define dx_reg 126 +#define bx_reg 127 +#define sp_reg 128 +#define bp_reg 129 +#define si_reg 130 +#define di_reg 131 + +#define indir_dx_reg 150 + +#define GRP1b NULL, NULL, 0 +#define GRP1S NULL, NULL, 1 +#define GRP1Ss NULL, NULL, 2 +#define GRP2b NULL, NULL, 3 +#define GRP2S NULL, NULL, 4 +#define GRP2b_one NULL, NULL, 5 +#define GRP2S_one NULL, NULL, 6 +#define GRP2b_cl NULL, NULL, 7 +#define GRP2S_cl NULL, NULL, 8 +#define GRP3b NULL, NULL, 9 +#define GRP3S NULL, NULL, 10 +#define GRP4 NULL, NULL, 11 +#define GRP5 NULL, NULL, 12 +#define GRP6 NULL, NULL, 13 +#define GRP7 NULL, NULL, 14 +#define GRP8 NULL, NULL, 15 + +#define FLOATCODE 50 +#define FLOAT NULL, NULL, FLOATCODE + +struct dis386 { + char *name; + int (*op1)(); + int bytemode1; + int (*op2)(); + int bytemode2; + int (*op3)(); + int bytemode3; +}; + +struct dis386 dis386[] = { + /* 00 */ + { "addb", Eb, Gb }, + { "addS", Ev, Gv }, + { "addb", Gb, Eb }, + { "addS", Gv, Ev }, + { "addb", AL, Ib }, + { "addS", eAX, Iv }, + { "pushl", es }, + { "popl", es }, + /* 08 */ + { "orb", Eb, Gb }, + { "orS", Ev, Gv }, + { "orb", Gb, Eb }, + { "orS", Gv, Ev }, + { "orb", AL, Ib }, + { "orS", eAX, Iv }, + { "pushl", cs }, + { "(bad)" }, /* 0x0f extended opcode escape */ + /* 10 */ + { "adcb", Eb, Gb }, + { "adcS", Ev, Gv }, + { "adcb", Gb, Eb }, + { "adcS", Gv, Ev }, + { "adcb", AL, Ib }, + { "adcS", eAX, Iv }, + { "pushl", ss }, + { "popl", ss }, + /* 18 */ + { "sbbb", Eb, Gb }, + { "sbbS", Ev, Gv }, + { "sbbb", Gb, Eb }, + { "sbbS", Gv, Ev }, + { "sbbb", AL, Ib }, + { "sbbS", eAX, Iv }, + { "pushl", ds }, + { "popl", ds }, + /* 20 */ + { "andb", Eb, Gb }, + { "andS", Ev, Gv }, + { "andb", Gb, Eb }, + { "andS", Gv, Ev }, + { "andb", AL, Ib }, + { "andS", eAX, Iv }, + { "(bad)" }, /* SEG ES prefix */ + { "daa" }, + /* 28 */ + { "subb", Eb, Gb }, + { "subS", Ev, Gv }, + { "subb", Gb, Eb }, + { "subS", Gv, Ev }, + { "subb", AL, Ib }, + { "subS", eAX, Iv }, + { "(bad)" }, /* SEG CS prefix */ + { "das" }, + /* 30 */ + { "xorb", Eb, Gb }, + { "xorS", Ev, Gv }, + { "xorb", Gb, Eb }, + { "xorS", Gv, Ev }, + { "xorb", AL, Ib }, + { "xorS", eAX, Iv }, + { "(bad)" }, /* SEG SS prefix */ + { "aaa" }, + /* 38 */ + { "cmpb", Eb, Gb }, + { "cmpS", Ev, Gv }, + { "cmpb", Gb, Eb }, + { "cmpS", Gv, Ev }, + { "cmpb", AL, Ib }, + { "cmpS", eAX, Iv }, + { "(bad)" }, /* SEG DS prefix */ + { "aas" }, + /* 40 */ + { "incS", eAX }, + { "incS", eCX }, + { "incS", eDX }, + { "incS", eBX }, + { "incS", eSP }, + { "incS", eBP }, + { "incS", eSI }, + { "incS", eDI }, + /* 48 */ + { "decS", eAX }, + { "decS", eCX }, + { "decS", eDX }, + { "decS", eBX }, + { "decS", eSP }, + { "decS", eBP }, + { "decS", eSI }, + { "decS", eDI }, + /* 50 */ + { "pushS", eAX }, + { "pushS", eCX }, + { "pushS", eDX }, + { "pushS", eBX }, + { "pushS", eSP }, + { "pushS", eBP }, + { "pushS", eSI }, + { "pushS", eDI }, + /* 58 */ + { "popS", eAX }, + { "popS", eCX }, + { "popS", eDX }, + { "popS", eBX }, + { "popS", eSP }, + { "popS", eBP }, + { "popS", eSI }, + { "popS", eDI }, + /* 60 */ + { "pusha" }, + { "popa" }, + { "boundS", Gv, Ma }, + { "arpl", Ew, Gw }, + { "(bad)" }, /* seg fs */ + { "(bad)" }, /* seg gs */ + { "(bad)" }, /* op size prefix */ + { "(bad)" }, /* adr size prefix */ + /* 68 */ + { "pushS", Iv }, /* 386 book wrong */ + { "imulS", Gv, Ev, Iv }, + { "pushl", sIb }, /* push of byte really pushes 4 bytes */ + { "imulS", Gv, Ev, Ib }, + { "insb", Yb, indirDX }, + { "insS", Yv, indirDX }, + { "outsb", indirDX, Xb }, + { "outsS", indirDX, Xv }, + /* 70 */ + { "jo", Jb }, + { "jno", Jb }, + { "jb", Jb }, + { "jae", Jb }, + { "je", Jb }, + { "jne", Jb }, + { "jbe", Jb }, + { "ja", Jb }, + /* 78 */ + { "js", Jb }, + { "jns", Jb }, + { "jp", Jb }, + { "jnp", Jb }, + { "jl", Jb }, + { "jnl", Jb }, + { "jle", Jb }, + { "jg", Jb }, + /* 80 */ + { GRP1b }, + { GRP1S }, + { "(bad)" }, + { GRP1Ss }, + { "testb", Eb, Gb }, + { "testS", Ev, Gv }, + { "xchgb", Eb, Gb }, + { "xchgS", Ev, Gv }, + /* 88 */ + { "movb", Eb, Gb }, + { "movS", Ev, Gv }, + { "movb", Gb, Eb }, + { "movS", Gv, Ev }, + { "movw", Ew, Sw }, + { "leaS", Gv, M }, + { "movw", Sw, Ew }, + { "popS", Ev }, + /* 90 */ + { "nop" }, + { "xchgS", eCX, eAX }, + { "xchgS", eDX, eAX }, + { "xchgS", eBX, eAX }, + { "xchgS", eSP, eAX }, + { "xchgS", eBP, eAX }, + { "xchgS", eSI, eAX }, + { "xchgS", eDI, eAX }, + /* 98 */ + { "cwtl" }, + { "cltd" }, + { "lcall", Ap }, + { "(bad)" }, /* fwait */ + { "pushf" }, + { "popf" }, + { "sahf" }, + { "lahf" }, + /* a0 */ + { "movb", AL, Ob }, + { "movS", eAX, Ov }, + { "movb", Ob, AL }, + { "movS", Ov, eAX }, + { "movsb", Yb, Xb }, + { "movsS", Yv, Xv }, + { "cmpsb", Yb, Xb }, + { "cmpsS", Yv, Xv }, + /* a8 */ + { "testb", AL, Ib }, + { "testS", eAX, Iv }, + { "stosb", Yb, AL }, + { "stosS", Yv, eAX }, + { "lodsb", AL, Xb }, + { "lodsS", eAX, Xv }, + { "scasb", AL, Xb }, + { "scasS", eAX, Xv }, + /* b0 */ + { "movb", AL, Ib }, + { "movb", CL, Ib }, + { "movb", DL, Ib }, + { "movb", BL, Ib }, + { "movb", AH, Ib }, + { "movb", CH, Ib }, + { "movb", DH, Ib }, + { "movb", BH, Ib }, + /* b8 */ + { "movS", eAX, Iv }, + { "movS", eCX, Iv }, + { "movS", eDX, Iv }, + { "movS", eBX, Iv }, + { "movS", eSP, Iv }, + { "movS", eBP, Iv }, + { "movS", eSI, Iv }, + { "movS", eDI, Iv }, + /* c0 */ + { GRP2b }, + { GRP2S }, + { "ret", Iw }, + { "ret" }, + { "lesS", Gv, Mp }, + { "ldsS", Gv, Mp }, + { "movb", Eb, Ib }, + { "movS", Ev, Iv }, + /* c8 */ + { "enter", Iw, Ib }, + { "leave" }, + { "lret", Iw }, + { "lret" }, + { "int3" }, + { "int", Ib }, + { "into" }, + { "iret" }, + /* d0 */ + { GRP2b_one }, + { GRP2S_one }, + { GRP2b_cl }, + { GRP2S_cl }, + { "aam", Ib }, + { "aad", Ib }, + { "(bad)" }, + { "xlat" }, + /* d8 */ + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + /* e0 */ + { "loopne", Jb }, + { "loope", Jb }, + { "loop", Jb }, + { "jCcxz", Jb }, + { "inb", AL, Ib }, + { "inS", eAX, Ib }, + { "outb", Ib, AL }, + { "outS", Ib, eAX }, + /* e8 */ + { "call", Av }, + { "jmp", Jv }, + { "ljmp", Ap }, + { "jmp", Jb }, + { "inb", AL, indirDX }, + { "inS", eAX, indirDX }, + { "outb", indirDX, AL }, + { "outS", indirDX, eAX }, + /* f0 */ + { "(bad)" }, /* lock prefix */ + { "(bad)" }, + { "(bad)" }, /* repne */ + { "(bad)" }, /* repz */ + { "hlt" }, + { "cmc" }, + { GRP3b }, + { GRP3S }, + /* f8 */ + { "clc" }, + { "stc" }, + { "cli" }, + { "sti" }, + { "cld" }, + { "std" }, + { GRP4 }, + { GRP5 }, +}; + +struct dis386 dis386_twobyte[] = { + /* 00 */ + { GRP6 }, + { GRP7 }, + { "larS", Gv, Ew }, + { "lslS", Gv, Ew }, + { "(bad)" }, + { "(bad)" }, + { "clts" }, + { "(bad)" }, + /* 08 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 10 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 18 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 20 */ + /* these are all backward in appendix A of the intel book */ + { "movl", Rd, Cd }, + { "movl", Rd, Dd }, + { "movl", Cd, Rd }, + { "movl", Dd, Rd }, + { "movl", Rd, Td }, + { "(bad)" }, + { "movl", Td, Rd }, + { "(bad)" }, + /* 28 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 30 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 38 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 40 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 48 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 50 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 58 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 60 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 68 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 70 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 78 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 80 */ + { "jo", Jv }, + { "jno", Jv }, + { "jb", Jv }, + { "jae", Jv }, + { "je", Jv }, + { "jne", Jv }, + { "jbe", Jv }, + { "ja", Jv }, + /* 88 */ + { "js", Jv }, + { "jns", Jv }, + { "jp", Jv }, + { "jnp", Jv }, + { "jl", Jv }, + { "jge", Jv }, + { "jle", Jv }, + { "jg", Jv }, + /* 90 */ + { "seto", Eb }, + { "setno", Eb }, + { "setb", Eb }, + { "setae", Eb }, + { "sete", Eb }, + { "setne", Eb }, + { "setbe", Eb }, + { "seta", Eb }, + /* 98 */ + { "sets", Eb }, + { "setns", Eb }, + { "setp", Eb }, + { "setnp", Eb }, + { "setl", Eb }, + { "setge", Eb }, + { "setle", Eb }, + { "setg", Eb }, + /* a0 */ + { "pushl", fs }, + { "popl", fs }, + { "(bad)" }, + { "btS", Ev, Gv }, + { "shldS", Ev, Gv, Ib }, + { "shldS", Ev, Gv, CL }, + { "(bad)" }, + { "(bad)" }, + /* a8 */ + { "pushl", gs }, + { "popl", gs }, + { "(bad)" }, + { "btsS", Ev, Gv }, + { "shrdS", Ev, Gv, Ib }, + { "shrdS", Ev, Gv, CL }, + { "(bad)" }, + { "imulS", Gv, Ev }, + /* b0 */ + { "(bad)" }, + { "(bad)" }, + { "lssS", Gv, Mp }, /* 386 lists only Mp */ + { "btrS", Ev, Gv }, + { "lfsS", Gv, Mp }, /* 386 lists only Mp */ + { "lgsS", Gv, Mp }, /* 386 lists only Mp */ + { "movzbS", Gv, Eb }, + { "movzwS", Gv, Ew }, + /* b8 */ + { "(bad)" }, + { "(bad)" }, + { GRP8 }, + { "btcS", Ev, Gv }, + { "bsfS", Gv, Ev }, + { "bsrS", Gv, Ev }, + { "movsbS", Gv, Eb }, + { "movswS", Gv, Ew }, + /* c0 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* c8 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* d0 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* d8 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* e0 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* e8 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* f0 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* f8 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, +}; + +static char obuf[100]; +static char *obufp; +static char scratchbuf[100]; +static unsigned char *start_codep; +static unsigned char *codep; +static int mod; +static int rm; +static int reg; + +static char *names32[]={ + "%eax","%ecx","%edx","%ebx", "%esp","%ebp","%esi","%edi", +}; +static char *names16[] = { + "%ax","%cx","%dx","%bx","%sp","%bp","%si","%di", +}; +static char *names8[] = { + "%al","%cl","%dl","%bl","%ah","%ch","%dh","%bh", +}; +static char *names_seg[] = { + "%es","%cs","%ss","%ds","%fs","%gs","%?","%?", +}; + +struct dis386 grps[][8] = { + /* GRP1b */ + { + { "addb", Eb, Ib }, + { "orb", Eb, Ib }, + { "adcb", Eb, Ib }, + { "sbbb", Eb, Ib }, + { "andb", Eb, Ib }, + { "subb", Eb, Ib }, + { "xorb", Eb, Ib }, + { "cmpb", Eb, Ib } + }, + /* GRP1S */ + { + { "addS", Ev, Iv }, + { "orS", Ev, Iv }, + { "adcS", Ev, Iv }, + { "sbbS", Ev, Iv }, + { "andS", Ev, Iv }, + { "subS", Ev, Iv }, + { "xorS", Ev, Iv }, + { "cmpS", Ev, Iv } + }, + /* GRP1Ss */ + { + { "addS", Ev, sIb }, + { "orS", Ev, sIb }, + { "adcS", Ev, sIb }, + { "sbbS", Ev, sIb }, + { "andS", Ev, sIb }, + { "subS", Ev, sIb }, + { "xorS", Ev, sIb }, + { "cmpS", Ev, sIb } + }, + /* GRP2b */ + { + { "rolb", Eb, Ib }, + { "rorb", Eb, Ib }, + { "rclb", Eb, Ib }, + { "rcrb", Eb, Ib }, + { "shlb", Eb, Ib }, + { "shrb", Eb, Ib }, + { "(bad)" }, + { "sarb", Eb, Ib }, + }, + /* GRP2S */ + { + { "rolS", Ev, Ib }, + { "rorS", Ev, Ib }, + { "rclS", Ev, Ib }, + { "rcrS", Ev, Ib }, + { "shlS", Ev, Ib }, + { "shrS", Ev, Ib }, + { "(bad)" }, + { "sarS", Ev, Ib }, + }, + /* GRP2b_one */ + { + { "rolb", Eb }, + { "rorb", Eb }, + { "rclb", Eb }, + { "rcrb", Eb }, + { "shlb", Eb }, + { "shrb", Eb }, + { "(bad)" }, + { "sarb", Eb }, + }, + /* GRP2S_one */ + { + { "rolS", Ev }, + { "rorS", Ev }, + { "rclS", Ev }, + { "rcrS", Ev }, + { "shlS", Ev }, + { "shrS", Ev }, + { "(bad)" }, + { "sarS", Ev }, + }, + /* GRP2b_cl */ + { + { "rolb", Eb, CL }, + { "rorb", Eb, CL }, + { "rclb", Eb, CL }, + { "rcrb", Eb, CL }, + { "shlb", Eb, CL }, + { "shrb", Eb, CL }, + { "(bad)" }, + { "sarb", Eb, CL }, + }, + /* GRP2S_cl */ + { + { "rolS", Ev, CL }, + { "rorS", Ev, CL }, + { "rclS", Ev, CL }, + { "rcrS", Ev, CL }, + { "shlS", Ev, CL }, + { "shrS", Ev, CL }, + { "(bad)" }, + { "sarS", Ev, CL } + }, + /* GRP3b */ + { + { "testb", Eb, Ib }, + { "(bad)", Eb }, + { "notb", Eb }, + { "negb", Eb }, + { "mulb", AL, Eb }, + { "imulb", AL, Eb }, + { "divb", AL, Eb }, + { "idivb", AL, Eb } + }, + /* GRP3S */ + { + { "testS", Ev, Iv }, + { "(bad)" }, + { "notS", Ev }, + { "negS", Ev }, + { "mulS", eAX, Ev }, + { "imulS", eAX, Ev }, + { "divS", eAX, Ev }, + { "idivS", eAX, Ev }, + }, + /* GRP4 */ + { + { "incb", Eb }, + { "decb", Eb }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + }, + /* GRP5 */ + { + { "incS", Ev }, + { "decS", Ev }, + { "call", indirEv }, + { "lcall", indirEv }, + { "jmp", indirEv }, + { "ljmp", indirEv }, + { "pushS", Ev }, + { "(bad)" }, + }, + /* GRP6 */ + { + { "sldt", Ew }, + { "str", Ew }, + { "lldt", Ew }, + { "ltr", Ew }, + { "verr", Ew }, + { "verw", Ew }, + { "(bad)" }, + { "(bad)" } + }, + /* GRP7 */ + { + { "sgdt", Ew }, + { "sidt", Ew }, + { "lgdt", Ew }, + { "lidt", Ew }, + { "smsw", Ew }, + { "(bad)" }, + { "lmsw", Ew }, + { "(bad)" }, + }, + /* GRP8 */ + { + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "btS", Ev, Ib }, + { "btsS", Ev, Ib }, + { "btrS", Ev, Ib }, + { "btcS", Ev, Ib }, + } +}; + +#define PREFIX_REPZ 1 +#define PREFIX_REPNZ 2 +#define PREFIX_LOCK 4 +#define PREFIX_CS 8 +#define PREFIX_SS 0x10 +#define PREFIX_DS 0x20 +#define PREFIX_ES 0x40 +#define PREFIX_FS 0x80 +#define PREFIX_GS 0x100 +#define PREFIX_DATA 0x200 +#define PREFIX_ADR 0x400 +#define PREFIX_FWAIT 0x800 + +static int prefixes; + +ckprefix () +{ + prefixes = 0; + while (1) + { + switch (*codep) + { + case 0xf3: + prefixes |= PREFIX_REPZ; + break; + case 0xf2: + prefixes |= PREFIX_REPNZ; + break; + case 0xf0: + prefixes |= PREFIX_LOCK; + break; + case 0x2e: + prefixes |= PREFIX_CS; + break; + case 0x36: + prefixes |= PREFIX_SS; + break; + case 0x3e: + prefixes |= PREFIX_DS; + break; + case 0x26: + prefixes |= PREFIX_ES; + break; + case 0x64: + prefixes |= PREFIX_FS; + break; + case 0x65: + prefixes |= PREFIX_GS; + break; + case 0x66: + prefixes |= PREFIX_DATA; + break; + case 0x67: + prefixes |= PREFIX_ADR; + break; + case 0x9b: + prefixes |= PREFIX_FWAIT; + break; + default: + return; + } + codep++; + } +} + +static int dflag; +static int aflag; + +static char op1out[100], op2out[100], op3out[100]; +static int start_pc; + +/* + * disassemble the first instruction in 'inbuf'. You have to make + * sure all of the bytes of the instruction are filled in. + * On the 386's of 1988, the maximum length of an instruction is 15 bytes. + * (see topic "Redundant prefixes" in the "Differences from 8086" + * section of the "Virtual 8086 Mode" chapter.) + * 'pc' should be the address of this instruction, it will + * be used to print the target address if this is a relative jump or call + * 'outbuf' gets filled in with the disassembled instruction. it should + * be long enough to hold the longest disassembled instruction. + * 100 bytes is certainly enough, unless symbol printing is added later + * The function returns the length of this instruction in bytes. + */ +i386dis (pc, inbuf, outbuf) + int pc; + unsigned char *inbuf; + char *outbuf; +{ + struct dis386 *dp; + char *p; + int i; + int enter_instruction; + char *first, *second, *third; + int needcomma; + + obuf[0] = 0; + op1out[0] = 0; + op2out[0] = 0; + op3out[0] = 0; + + start_pc = pc; + start_codep = inbuf; + codep = inbuf; + + ckprefix (); + + if (*codep == 0xc8) + enter_instruction = 1; + else + enter_instruction = 0; + + obufp = obuf; + + if (prefixes & PREFIX_REPZ) + oappend ("repz "); + if (prefixes & PREFIX_REPNZ) + oappend ("repnz "); + if (prefixes & PREFIX_LOCK) + oappend ("lock "); + + if ((prefixes & PREFIX_FWAIT) + && ((*codep < 0xd8) || (*codep > 0xdf))) + { + /* fwait not followed by floating point instruction */ + oappend ("fwait"); + strcpy (outbuf, obuf); + return (1); + } + + /* these would be initialized to 0 if disassembling for 8086 or 286 */ + dflag = 1; + aflag = 1; + + if (prefixes & PREFIX_DATA) + dflag ^= 1; + + if (prefixes & PREFIX_ADR) + { + aflag ^= 1; + oappend ("addr16 "); + } + + if (*codep == 0x0f) + dp = &dis386_twobyte[*++codep]; + else + dp = &dis386[*codep]; + codep++; + mod = (*codep >> 6) & 3; + reg = (*codep >> 3) & 7; + rm = *codep & 7; + + if (dp->name == NULL && dp->bytemode1 == FLOATCODE) + { + dofloat (); + } + else + { + if (dp->name == NULL) + dp = &grps[dp->bytemode1][reg]; + + putop (dp->name); + + obufp = op1out; + if (dp->op1) + (*dp->op1)(dp->bytemode1); + + obufp = op2out; + if (dp->op2) + (*dp->op2)(dp->bytemode2); + + obufp = op3out; + if (dp->op3) + (*dp->op3)(dp->bytemode3); + } + + obufp = obuf + strlen (obuf); + for (i = strlen (obuf); i < 6; i++) + oappend (" "); + oappend (" "); + + /* enter instruction is printed with operands in the + * same order as the intel book; everything else + * is printed in reverse order + */ + if (enter_instruction) + { + first = op1out; + second = op2out; + third = op3out; + } + else + { + first = op3out; + second = op2out; + third = op1out; + } + needcomma = 0; + if (*first) + { + oappend (first); + needcomma = 1; + } + if (*second) + { + if (needcomma) + oappend (","); + oappend (second); + needcomma = 1; + } + if (*third) + { + if (needcomma) + oappend (","); + oappend (third); + } + strcpy (outbuf, obuf); + return (codep - inbuf); +} + +char *float_mem[] = { + /* d8 */ + "fadds", + "fmuls", + "fcoms", + "fcomps", + "fsubs", + "fsubrs", + "fdivs", + "fdivrs", + /* d9 */ + "flds", + "(bad)", + "fsts", + "fstps", + "fldenv", + "fldcw", + "fNstenv", + "fNstcw", + /* da */ + "fiaddl", + "fimull", + "ficoml", + "ficompl", + "fisubl", + "fisubrl", + "fidivl", + "fidivrl", + /* db */ + "fildl", + "(bad)", + "fistl", + "fistpl", + "(bad)", + "fldt", + "(bad)", + "fstpt", + /* dc */ + "faddl", + "fmull", + "fcoml", + "fcompl", + "fsubl", + "fsubrl", + "fdivl", + "fdivrl", + /* dd */ + "fldl", + "(bad)", + "fstl", + "fstpl", + "frstor", + "(bad)", + "fNsave", + "fNstsw", + /* de */ + "fiadd", + "fimul", + "ficom", + "ficomp", + "fisub", + "fisubr", + "fidiv", + "fidivr", + /* df */ + "fild", + "(bad)", + "fist", + "fistp", + "fbld", + "fildll", + "fbstp", + "fistpll", +}; + +#define ST OP_ST, 0 +#define STi OP_STi, 0 +int OP_ST(), OP_STi(); + +#define FGRPd9_2 NULL, NULL, 0 +#define FGRPd9_4 NULL, NULL, 1 +#define FGRPd9_5 NULL, NULL, 2 +#define FGRPd9_6 NULL, NULL, 3 +#define FGRPd9_7 NULL, NULL, 4 +#define FGRPda_5 NULL, NULL, 5 +#define FGRPdb_4 NULL, NULL, 6 +#define FGRPde_3 NULL, NULL, 7 +#define FGRPdf_4 NULL, NULL, 8 + +struct dis386 float_reg[][8] = { + /* d8 */ + { + { "fadd", ST, STi }, + { "fmul", ST, STi }, + { "fcom", STi }, + { "fcomp", STi }, + { "fsub", ST, STi }, + { "fsubr", ST, STi }, + { "fdiv", ST, STi }, + { "fdivr", ST, STi }, + }, + /* d9 */ + { + { "fld", STi }, + { "fxch", STi }, + { FGRPd9_2 }, + { "(bad)" }, + { FGRPd9_4 }, + { FGRPd9_5 }, + { FGRPd9_6 }, + { FGRPd9_7 }, + }, + /* da */ + { + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { FGRPda_5 }, + { "(bad)" }, + { "(bad)" }, + }, + /* db */ + { + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { FGRPdb_4 }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + }, + /* dc */ + { + { "fadd", STi, ST }, + { "fmul", STi, ST }, + { "(bad)" }, + { "(bad)" }, + { "fsub", STi, ST }, + { "fsubr", STi, ST }, + { "fdiv", STi, ST }, + { "fdivr", STi, ST }, + }, + /* dd */ + { + { "ffree", STi }, + { "(bad)" }, + { "fst", STi }, + { "fstp", STi }, + { "fucom", STi }, + { "fucomp", STi }, + { "(bad)" }, + { "(bad)" }, + }, + /* de */ + { + { "faddp", STi, ST }, + { "fmulp", STi, ST }, + { "(bad)" }, + { FGRPde_3 }, + { "fsubp", STi, ST }, + { "fsubrp", STi, ST }, + { "fdivp", STi, ST }, + { "fdivrp", STi, ST }, + }, + /* df */ + { + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { FGRPdf_4 }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + }, +}; + + +char *fgrps[][8] = { + /* d9_2 0 */ + { + "fnop","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", + }, + + /* d9_4 1 */ + { + "fchs","fabs","(bad)","(bad)","ftst","fxam","(bad)","(bad)", + }, + + /* d9_5 2 */ + { + "fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz","(bad)", + }, + + /* d9_6 3 */ + { + "f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp", + }, + + /* d9_7 4 */ + { + "fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos", + }, + + /* da_5 5 */ + { + "(bad)","fucompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", + }, + + /* db_4 6 */ + { + "feni(287 only)","fdisi(287 only)","fNclex","fNinit", + "fNsetpm(287 only)","(bad)","(bad)","(bad)", + }, + + /* de_3 7 */ + { + "(bad)","fcompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", + }, + + /* df_4 8 */ + { + "fNstsw","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", + }, +}; + + +dofloat () +{ + struct dis386 *dp; + unsigned char floatop; + + floatop = codep[-1]; + + if (mod != 3) + { + putop (float_mem[(floatop - 0xd8) * 8 + reg]); + obufp = op1out; + OP_E (v_mode); + return; + } + codep++; + + dp = &float_reg[floatop - 0xd8][reg]; + if (dp->name == NULL) + { + putop (fgrps[dp->bytemode1][rm]); + /* instruction fnstsw is only one with strange arg */ + if (floatop == 0xdf && *codep == 0xe0) + strcpy (op1out, "%eax"); + } + else + { + putop (dp->name); + obufp = op1out; + if (dp->op1) + (*dp->op1)(dp->bytemode1); + obufp = op2out; + if (dp->op2) + (*dp->op2)(dp->bytemode2); + } +} + +/* ARGSUSED */ +OP_ST (ignore) +{ + oappend ("%st"); +} + +/* ARGSUSED */ +OP_STi (ignore) +{ + sprintf (scratchbuf, "%%st(%d)", rm); + oappend (scratchbuf); +} + + +/* capital letters in template are macros */ +putop (template) + char *template; +{ + char *p; + + for (p = template; *p; p++) + { + switch (*p) + { + default: + *obufp++ = *p; + break; + case 'C': /* For jcxz/jecxz */ + if (aflag == 0) + *obufp++ = 'e'; + break; + case 'N': + if ((prefixes & PREFIX_FWAIT) == 0) + *obufp++ = 'n'; + break; + case 'S': + /* operand size flag */ + if (dflag) + *obufp++ = 'l'; + else + *obufp++ = 'w'; + break; + } + } + *obufp = 0; +} + +oappend (s) +char *s; +{ + strcpy (obufp, s); + obufp += strlen (s); + *obufp = 0; +} + +append_prefix () +{ + if (prefixes & PREFIX_CS) + oappend ("%cs:"); + if (prefixes & PREFIX_DS) + oappend ("%ds:"); + if (prefixes & PREFIX_SS) + oappend ("%ss:"); + if (prefixes & PREFIX_ES) + oappend ("%es:"); + if (prefixes & PREFIX_FS) + oappend ("%fs:"); + if (prefixes & PREFIX_GS) + oappend ("%gs:"); +} + +OP_indirE (bytemode) +{ + oappend ("*"); + OP_E (bytemode); +} + +OP_E (bytemode) +{ + int disp; + int havesib; + int didoutput = 0; + int base; + int index; + int scale; + int havebase; + + /* skip mod/rm byte */ + codep++; + + havesib = 0; + havebase = 0; + disp = 0; + + if (mod == 3) + { + switch (bytemode) + { + case b_mode: + oappend (names8[rm]); + break; + case w_mode: + oappend (names16[rm]); + break; + case v_mode: + if (dflag) + oappend (names32[rm]); + else + oappend (names16[rm]); + break; + default: + oappend (""); + break; + } + return; + } + + append_prefix (); + if (rm == 4) + { + havesib = 1; + havebase = 1; + scale = (*codep >> 6) & 3; + index = (*codep >> 3) & 7; + base = *codep & 7; + codep++; + } + + switch (mod) + { + case 0: + switch (rm) + { + case 4: + /* implies havesib and havebase */ + if (base == 5) { + havebase = 0; + disp = get32 (); + } + break; + case 5: + disp = get32 (); + break; + default: + havebase = 1; + base = rm; + break; + } + break; + case 1: + disp = *(char *)codep++; + if (rm != 4) + { + havebase = 1; + base = rm; + } + break; + case 2: + disp = get32 (); + if (rm != 4) + { + havebase = 1; + base = rm; + } + break; + } + + if (mod != 0 || rm == 5 || (havesib && base == 5)) + { + sprintf (scratchbuf, "%d", disp); + oappend (scratchbuf); + } + + if (havebase || havesib) + { + oappend ("("); + if (havebase) + oappend (names32[base]); + if (havesib) + { + if (index != 4) + { + sprintf (scratchbuf, ",%s", names32[index]); + oappend (scratchbuf); + } + sprintf (scratchbuf, ",%d", 1 << scale); + oappend (scratchbuf); + } + oappend (")"); + } +} + +OP_G (bytemode) +{ + switch (bytemode) + { + case b_mode: + oappend (names8[reg]); + break; + case w_mode: + oappend (names16[reg]); + break; + case d_mode: + oappend (names32[reg]); + break; + case v_mode: + if (dflag) + oappend (names32[reg]); + else + oappend (names16[reg]); + break; + default: + oappend (""); + break; + } +} + +get32 () +{ + int x = 0; + + x = *codep++ & 0xff; + x |= (*codep++ & 0xff) << 8; + x |= (*codep++ & 0xff) << 16; + x |= (*codep++ & 0xff) << 24; + return (x); +} + +get16 () +{ + int x = 0; + + x = *codep++ & 0xff; + x |= (*codep++ & 0xff) << 8; + return (x); +} + +OP_REG (code) +{ + char *s; + + switch (code) + { + case indir_dx_reg: s = "(%dx)"; break; + case ax_reg: case cx_reg: case dx_reg: case bx_reg: + case sp_reg: case bp_reg: case si_reg: case di_reg: + s = names16[code - ax_reg]; + break; + case es_reg: case ss_reg: case cs_reg: + case ds_reg: case fs_reg: case gs_reg: + s = names_seg[code - es_reg]; + break; + case al_reg: case ah_reg: case cl_reg: case ch_reg: + case dl_reg: case dh_reg: case bl_reg: case bh_reg: + s = names8[code - al_reg]; + break; + case eAX_reg: case eCX_reg: case eDX_reg: case eBX_reg: + case eSP_reg: case eBP_reg: case eSI_reg: case eDI_reg: + if (dflag) + s = names32[code - eAX_reg]; + else + s = names16[code - eAX_reg]; + break; + default: + s = ""; + break; + } + oappend (s); +} + +OP_I (bytemode) +{ + int op; + + switch (bytemode) + { + case b_mode: + op = *codep++ & 0xff; + break; + case v_mode: + if (dflag) + op = get32 (); + else + op = get16 (); + break; + case w_mode: + op = get16 (); + break; + default: + oappend (""); + return; + } + sprintf (scratchbuf, "$0x%x", op); + oappend (scratchbuf); +} + +OP_sI (bytemode) +{ + int op; + + switch (bytemode) + { + case b_mode: + op = *(char *)codep++; + break; + case v_mode: + if (dflag) + op = get32 (); + else + op = (short)get16(); + break; + case w_mode: + op = (short)get16 (); + break; + default: + oappend (""); + return; + } + sprintf (scratchbuf, "$0x%x", op); + oappend (scratchbuf); +} + +OP_J (bytemode) +{ + int disp; + int mask = -1; + + switch (bytemode) + { + case b_mode: + disp = *(char *)codep++; + break; + case v_mode: + if (dflag) + disp = get32 (); + else + { + disp = (short)get16 (); + /* for some reason, a data16 prefix on a jump instruction + means that the pc is masked to 16 bits after the + displacement is added! */ + mask = 0xffff; + } + break; + default: + oappend (""); + return; + } + + sprintf (scratchbuf, "0x%x", + (start_pc + codep - start_codep + disp) & mask); + oappend (scratchbuf); +} + +/* ARGSUSED */ +OP_SEG (dummy) +{ + static char *sreg[] = { + "%es","%cs","%ss","%ds","%fs","%gs","%?","%?", + }; + + oappend (sreg[reg]); +} + +OP_DIR (size) +{ + int seg, offset; + + switch (size) + { + case lptr: + if (aflag) + { + offset = get32 (); + seg = get16 (); + } + else + { + offset = get16 (); + seg = get16 (); + } + sprintf (scratchbuf, "0x%x,0x%x", seg, offset); + oappend (scratchbuf); + break; + case v_mode: + if (aflag) + offset = get32 (); + else + offset = (short)get16 (); + + sprintf (scratchbuf, "0x%x", + start_pc + codep - start_codep + offset); + oappend (scratchbuf); + break; + default: + oappend (""); + break; + } +} + +/* ARGSUSED */ +OP_OFF (bytemode) +{ + int off; + + if (aflag) + off = get32 (); + else + off = get16 (); + + sprintf (scratchbuf, "0x%x", off); + oappend (scratchbuf); +} + +/* ARGSUSED */ +OP_ESDI (dummy) +{ + oappend ("%es:("); + oappend (aflag ? "%edi" : "%di"); + oappend (")"); +} + +/* ARGSUSED */ +OP_DSSI (dummy) +{ + oappend ("%ds:("); + oappend (aflag ? "%esi" : "%si"); + oappend (")"); +} + +/* ARGSUSED */ +OP_ONE (dummy) +{ + oappend ("1"); +} + +/* ARGSUSED */ +OP_C (dummy) +{ + codep++; /* skip mod/rm */ + sprintf (scratchbuf, "%%cr%d", reg); + oappend (scratchbuf); +} + +/* ARGSUSED */ +OP_D (dummy) +{ + codep++; /* skip mod/rm */ + sprintf (scratchbuf, "%%db%d", reg); + oappend (scratchbuf); +} + +/* ARGSUSED */ +OP_T (dummy) +{ + codep++; /* skip mod/rm */ + sprintf (scratchbuf, "%%tr%d", reg); + oappend (scratchbuf); +} + +OP_rm (bytemode) +{ + switch (bytemode) + { + case d_mode: + oappend (names32[rm]); + break; + case w_mode: + oappend (names16[rm]); + break; + } +} + +/* GDB interface */ +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "frame.h" +#include "inferior.h" + +#define MAXLEN 20 +print_insn (memaddr, stream) + CORE_ADDR memaddr; + FILE *stream; +{ + unsigned char buffer[MAXLEN]; + /* should be expanded if disassembler prints symbol names */ + char outbuf[100]; + int n; + + read_memory (memaddr, buffer, MAXLEN); + + n = i386dis ((int)memaddr, buffer, outbuf); + + fputs (outbuf, stream); + + return (n); +} + diff --git a/gnu/usr.bin/gdb/config/i386bsd-dep.c b/gnu/usr.bin/gdb/config/i386bsd-dep.c new file mode 100644 index 0000000000..d298fb02ac --- /dev/null +++ b/gnu/usr.bin/gdb/config/i386bsd-dep.c @@ -0,0 +1,1884 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)i386bsd-dep.c 6.10 (Berkeley) 6/26/91"; +#endif /* not lint */ + +/* Low level interface to ptrace, for GDB when running on the Intel 386. + Copyright (C) 1988, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include "defs.h" +#include "param.h" +#include "frame.h" +#include "inferior.h" +#include "value.h" + +#include +#include +#include +#include +#include + +#include + +#ifndef N_SET_MAGIC +#define N_SET_MAGIC(exec, val) ((exec).a_magic = (val)) +#endif + +#include +#include +#include +#define curpcb Xcurpcb /* XXX avoid leaking declaration from pcb.h */ +#include +#undef curpcb +#include +#include +#include + +#include + +#ifdef KERNELDEBUG +#ifndef NEWVM +#include +#include +#else +#include /* for curproc */ +#endif +#include +#include +#include +#include "symtab.h" /* XXX */ + +#undef vtophys /* XXX */ + +extern int kernel_debugging; + +#define KERNOFF ((unsigned)KERNBASE) +#ifndef NEWVM +#define INKERNEL(x) ((x) >= KERNOFF && (x) < KERNOFF + ctob(slr)) +#define INUPAGE(x) \ + ((x) >= KERNEL_U_ADDR && (x) < KERNEL_U_ADDR + NBPG) +#else +#define INKERNEL(x) ((x) >= KERNOFF) +#endif + +#define PT_ADDR_ANY ((caddr_t) 1) + +/* + * Convert from sysmap pte index to system virtual address & vice-versa. + * (why aren't these in one of the system vm macro files???) + */ +#define smxtob(a) (sbr + (a) * sizeof(pte)) +#define btosmx(b) (((b) - sbr) / sizeof(pte)) + +static int ok_to_cache(); +static int found_pcb; +#ifdef NEWVM +static CORE_ADDR curpcb; +static CORE_ADDR kstack; +#endif + +static void setregmap(); + +extern int errno; + +/* + * This function simply calls ptrace with the given arguments. It exists so + * that all calls to ptrace are isolated in this machine-dependent file. + */ +int +call_ptrace(request, pid, arg3, arg4) + int request; + pid_t pid; + caddr_t arg3; + int arg4; +{ + return(ptrace(request, pid, arg3, arg4)); +} + +kill_inferior() +{ + if (remote_debugging) { +#ifdef KERNELDEBUG + if (kernel_debugging) + /* + * It's a very, very bad idea to go away leaving + * breakpoints in a remote kernel or to leave it + * stopped at a breakpoint. + */ + clear_breakpoints(); +#endif + remote_close(0); + inferior_died(); + } else if (inferior_pid != 0) { + ptrace(PT_KILL, inferior_pid, 0, 0); + wait(0); + inferior_died(); + } +} + +/* + * This is used when GDB is exiting. It gives less chance of error. + */ +kill_inferior_fast() +{ + if (remote_debugging) { +#ifdef KERNELDEBUG + if (kernel_debugging) + clear_breakpoints(); +#endif + remote_close(0); + return; + } + if (inferior_pid == 0) + return; + + ptrace(PT_KILL, inferior_pid, 0, 0); + wait(0); +} + +/* + * Resume execution of the inferior process. If STEP is nonzero, single-step + * it. If SIGNAL is nonzero, give it that signal. + */ +void +resume(step, signal) + int step; + int signal; +{ + errno = 0; + if (remote_debugging) + remote_resume(step, signal); + else { + ptrace(step ? PT_STEP : PT_CONTINUE, inferior_pid, + PT_ADDR_ANY, signal); + if (errno) + perror_with_name("ptrace"); + } +} + +#ifdef ATTACH_DETACH +extern int attach_flag; + +/* + * Start debugging the process whose number is PID. + */ +attach(pid) + int pid; +{ + errno = 0; + ptrace(PT_ATTACH, pid, 0, 0); + if (errno) + perror_with_name("ptrace"); + attach_flag = 1; + return pid; +} + +/* + * Stop debugging the process whose number is PID and continue it + * with signal number SIGNAL. SIGNAL = 0 means just continue it. + */ +void +detach(signal) + int signal; +{ + errno = 0; + ptrace(PT_DETACH, inferior_pid, PT_ADDR_ANY, signal); + if (errno) + perror_with_name("ptrace"); + attach_flag = 0; +} +#endif /* ATTACH_DETACH */ + +static unsigned int +get_register_offset() +{ + unsigned int offset; + struct user u; /* XXX */ + unsigned int flags = (char *) &u.u_pcb.pcb_flags - (char *) &u; + + setregmap(ptrace(PT_READ_U, inferior_pid, (caddr_t)flags, 0)); + +#ifdef NEWVM + offset = (char *) &u.u_kproc.kp_proc.p_regs - (char *) &u; + offset = ptrace(PT_READ_U, inferior_pid, (caddr_t)offset, 0) - + USRSTACK; +#else + offset = (char *) &u.u_ar0 - (char *) &u; + offset = ptrace(PT_READ_U, inferior_pid, (caddr_t)offset, 0) - + KERNEL_U_ADDR; +#endif + + return offset; +} + +void +fetch_inferior_registers() +{ + register int regno; + register unsigned int regaddr; + char buf[MAX_REGISTER_RAW_SIZE]; + register int i; + unsigned int offset; + + if (remote_debugging) { + extern char registers[]; + + remote_fetch_registers(registers); + return; + } + + offset = get_register_offset(); + + for (regno = 0; regno < NUM_REGS; regno++) { + regaddr = register_addr(regno, offset); + for (i = 0; i < REGISTER_RAW_SIZE(regno); i += sizeof(int)) { + *(int *)&buf[i] = ptrace(PT_READ_U, inferior_pid, + (caddr_t)regaddr, 0); + regaddr += sizeof(int); + } + supply_register(regno, buf); + } +} + +/* + * Store our register values back into the inferior. If REGNO is -1, do this + * for all registers. Otherwise, REGNO specifies which register (so we can + * save time). + */ +store_inferior_registers(regno) + int regno; +{ + register unsigned int regaddr; + char buf[80]; + extern char registers[]; + register int i; + unsigned int offset; + + if (remote_debugging) { + extern char registers[]; + + remote_store_registers(registers); + return; + } + + offset = get_register_offset(); + + if (regno >= 0) { + regaddr = register_addr(regno, offset); + for (i = 0; i < REGISTER_RAW_SIZE(regno); i += sizeof(int)) { + errno = 0; + ptrace(PT_WRITE_U, inferior_pid, (caddr_t)regaddr, + *(int *) ®isters[REGISTER_BYTE(regno) + i]); + if (errno != 0) { + sprintf(buf, "writing register number %d(%d)", + regno, i); + perror_with_name(buf); + } + regaddr += sizeof(int); + } + } else + for (regno = 0; regno < NUM_REGS; regno++) { + regaddr = register_addr(regno, offset); + for (i = 0; i < REGISTER_RAW_SIZE(regno); + i += sizeof(int)) { + errno = 0; + ptrace(PT_WRITE_U, inferior_pid, + (caddr_t)regaddr, + *(int *) ®isters[REGISTER_BYTE(regno) + i]); + if (errno != 0) { + sprintf(buf, + "writing register number %d(%d)", + regno, i); + perror_with_name(buf); + } + regaddr += sizeof(int); + } + } +} + +/* + * Copy LEN bytes from inferior's memory starting at MEMADDR to debugger + * memory starting at MYADDR. On failure (cannot read from inferior, usually + * because address is out of bounds) returns the value of errno. + */ +int +read_inferior_memory(memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -sizeof(int); + /* Round ending address up; get number of longwords that makes. */ + register int count = (((memaddr + len) - addr) + sizeof(int) - 1) / + sizeof(int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca(count * sizeof(int)); + extern int errno; + + if (remote_debugging) + return (remote_read_inferior_memory(memaddr, myaddr, len)); + + /* Read all the longwords */ + errno = 0; + for (i = 0; i < count && errno == 0; i++, addr += sizeof(int)) + buffer[i] = ptrace(PT_READ_I, inferior_pid, (caddr_t)addr, 0); + + /* Copy appropriate bytes out of the buffer. */ + bcopy((char *) buffer + (memaddr & (sizeof(int) - 1)), myaddr, len); + return(errno); +} + +/* + * Copy LEN bytes of data from debugger memory at MYADDR to inferior's memory + * at MEMADDR. On failure (cannot write the inferior) returns the value of + * errno. + */ + +int +write_inferior_memory(memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -sizeof(int); + /* Round ending address up; get number of longwords that makes. */ + register int count = (((memaddr + len) - addr) + sizeof(int) - 1) / + sizeof(int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca(count * sizeof(int)); + extern int errno; + + /* + * Fill start and end extra bytes of buffer with existing memory + * data. + */ + if (remote_debugging) + return (remote_write_inferior_memory(memaddr, myaddr, len)); + + /* + * Fill start and end extra bytes of buffer with existing memory + * data. + */ + buffer[0] = ptrace(PT_READ_I, inferior_pid, (caddr_t)addr, 0); + + if (count > 1) + buffer[count - 1] = ptrace(PT_READ_I, inferior_pid, + (caddr_t)addr + (count - 1) * sizeof(int), 0); + + /* Copy data to be written over corresponding part of buffer */ + + bcopy(myaddr, (char *) buffer + (memaddr & (sizeof(int) - 1)), len); + + /* Write the entire buffer. */ + + errno = 0; + for (i = 0; i < count && errno == 0; i++, addr += sizeof(int)) + ptrace(PT_WRITE_I, inferior_pid, (caddr_t)addr, buffer[i]); + + return(errno); +} + + +/* + * Work with core dump and executable files, for GDB. + * This code would be in core.c if it weren't machine-dependent. + */ + +#ifndef N_TXTADDR +#define N_TXTADDR(hdr) 0 +#endif /* no N_TXTADDR */ + +#ifndef N_DATADDR +#define N_DATADDR(hdr) hdr.a_text +#endif /* no N_DATADDR */ + +/* + * Make COFF and non-COFF names for things a little more compatible to reduce + * conditionals later. + */ + +#ifndef AOUTHDR +#define AOUTHDR struct exec +#endif + +extern char *sys_siglist[]; + + +/* Hook for `exec_file_command' command to call. */ + +extern void (*exec_file_display_hook) (); + +/* File names of core file and executable file. */ + +extern char *corefile; +extern char *execfile; + +/* Descriptors on which core file and executable file are open. + Note that the execchan is closed when an inferior is created + and reopened if the inferior dies or is killed. */ + +extern int corechan; +extern int execchan; + +/* Last modification time of executable file. + Also used in source.c to compare against mtime of a source file. */ + +extern int exec_mtime; + +/* Virtual addresses of bounds of the two areas of memory in the core file. */ + +extern CORE_ADDR data_start; +extern CORE_ADDR data_end; +extern CORE_ADDR stack_start; +extern CORE_ADDR stack_end; + +/* Virtual addresses of bounds of two areas of memory in the exec file. + Note that the data area in the exec file is used only when there is no core file. */ + +extern CORE_ADDR text_start; +extern CORE_ADDR text_end; + +extern CORE_ADDR exec_data_start; +extern CORE_ADDR exec_data_end; + +/* Address in executable file of start of text area data. */ + +extern int text_offset; + +/* Address in executable file of start of data area data. */ + +extern int exec_data_offset; + +/* Address in core file of start of data area data. */ + +extern int data_offset; + +/* Address in core file of start of stack area data. */ + +extern int stack_offset; + +/* a.out header saved in core file. */ + +extern AOUTHDR core_aouthdr; + +/* a.out header of exec file. */ + +extern AOUTHDR exec_aouthdr; + +extern void validate_files (); + +extern int (*core_file_hook)(); + +#ifdef KERNELDEBUG +/* + * Kernel debugging routines. + */ + +#define IOTOP 0x100000 /* XXX should get this from include file */ +#define IOBASE 0xa0000 /* XXX should get this from include file */ + +static CORE_ADDR file_offset; +static CORE_ADDR lowram; +static CORE_ADDR sbr; +static CORE_ADDR slr; +static struct pcb pcb; + +static CORE_ADDR +ksym_lookup(name) + char *name; +{ + struct symbol *sym; + int i; + + if ((i = lookup_misc_func(name)) < 0) + error("kernel symbol `%s' not found.", name); + + return (misc_function_vector[i].address); +} + +/* + * return true if 'len' bytes starting at 'addr' can be read out as + * longwords and/or locally cached (this is mostly for memory mapped + * i/o register access when debugging remote kernels). + * + * XXX the HP code does this differently with NEWVM + */ +static int +ok_to_cache(addr, len) +{ + static CORE_ADDR atdevbase; + + if (! atdevbase) + atdevbase = ksym_lookup("atdevbase"); + + if (addr >= atdevbase && addr < atdevbase + (IOTOP - IOBASE)) + return (0); + + return (1); +} + +static +physrd(addr, dat, len) + u_int addr; + char *dat; +{ + if (lseek(corechan, addr - file_offset, L_SET) == -1) + return (-1); + if (read(corechan, dat, len) != len) + return (-1); + + return (0); +} + +/* + * When looking at kernel data space through /dev/mem or with a core file, do + * virtual memory mapping. + */ +#ifdef NEWVM +static CORE_ADDR +vtophys(addr) + CORE_ADDR addr; +{ + CORE_ADDR v; + struct pte pte; + static CORE_ADDR PTD = -1; + CORE_ADDR current_ptd; + + /* + * If we're looking at the kernel stack, + * munge the address to refer to the user space mapping instead; + * that way we get the requested process's kstack, not the running one. + */ + if (addr >= kstack && addr < kstack + ctob(UPAGES)) + addr = (addr - kstack) + curpcb; + + /* + * We may no longer have a linear system page table... + * + * Here's the scoop. IdlePTD contains the physical address + * of a page table directory that always maps the kernel. + * IdlePTD is in memory that is mapped 1-to-1, so we can + * find it easily given its 'virtual' address from ksym_lookup(). + * For hysterical reasons, the value of IdlePTD is stored in sbr. + * + * To look up a kernel address, we first convert it to a 1st-level + * address and look it up in IdlePTD. This gives us the physical + * address of a page table page; we extract the 2nd-level part of + * VA and read the 2nd-level pte. Finally, we add the offset part + * of the VA into the physical address from the pte and return it. + * + * User addresses are a little more complicated. If we don't have + * a current PCB from read_pcb(), we use PTD, which is the (fixed) + * virtual address of the current ptd. Since it's NOT in 1-to-1 + * kernel space, we must look it up using IdlePTD. If we do have + * a pcb, we get the ptd from pcb_ptd. + */ + + if (INKERNEL(addr)) + current_ptd = sbr; + else if (found_pcb == 0) { + if (PTD == -1) + PTD = vtophys(ksym_lookup("PTD")); + current_ptd = PTD; + } else + current_ptd = pcb.pcb_ptd; + + /* + * Read the first-level page table (ptd). + */ + v = current_ptd + ((unsigned)addr >> PD_SHIFT) * sizeof pte; + if (physrd(v, (char *)&pte, sizeof pte) || pte.pg_v == 0) + return (~0); + + /* + * Read the second-level page table. + */ + v = i386_ptob(pte.pg_pfnum) + ((addr&PT_MASK) >> PG_SHIFT) * sizeof pte; + if (physrd(v, (char *) &pte, sizeof(pte)) || pte.pg_v == 0) + return (~0); + + addr = i386_ptob(pte.pg_pfnum) + (addr & PGOFSET); +#if 0 + printf("vtophys(%x) -> %x\n", oldaddr, addr); +#endif + return (addr); +} +#else +static CORE_ADDR +vtophys(addr) + CORE_ADDR addr; +{ + CORE_ADDR v; + struct pte pte; + CORE_ADDR oldaddr = addr; + + if (found_pcb == 0 && INUPAGE(addr)) { + static CORE_ADDR pSwtchmap; + + if (pSwtchmap == 0) + pSwtchmap = vtophys(ksym_lookup("Swtchmap")); + addr = pSwtchmap; + } else if (INKERNEL(addr)) { + /* + * In system space get system pte. If valid or reclaimable + * then physical address is combination of its page number + * and the page offset of the original address. + */ + addr = smxtob(btop(addr - KERNOFF)) - KERNOFF; + } else { + v = btop(addr); + if (v < pcb.pcb_p0lr) + addr = (CORE_ADDR) pcb.pcb_p0br + + v * sizeof (struct pte); + else if (v >= pcb.pcb_p1lr && v < P1PAGES) + addr = (CORE_ADDR) pcb.pcb_p0br + + ((pcb.pcb_szpt * NPTEPG - HIGHPAGES) - + (BTOPUSRSTACK - v)) * sizeof (struct pte); + else + return (~0); + + /* + * For p0/p1 address, user-level page table should be in + * kernel vm. Do second-level indirect by recursing. + */ + if (!INKERNEL(addr)) + return (~0); + + addr = vtophys(addr); + } + /* + * Addr is now address of the pte of the page we are interested in; + * get the pte and paste up the physical address. + */ + if (physrd(addr, (char *) &pte, sizeof(pte))) + return (~0); + + if (pte.pg_v == 0 && (pte.pg_fod || pte.pg_pfnum == 0)) + return (~0); + + addr = (CORE_ADDR)ptob(pte.pg_pfnum) + (oldaddr & PGOFSET); +#if 0 + printf("vtophys(%x) -> %x\n", oldaddr, addr); +#endif + return (addr); +} + +#endif + +static +kvread(addr) + CORE_ADDR addr; +{ + CORE_ADDR paddr = vtophys(addr); + + if (paddr != ~0) + if (physrd(paddr, (char *)&addr, sizeof(addr)) == 0); + return (addr); + + return (~0); +} + +static void +read_pcb(uaddr) + u_int uaddr; +{ + int i; + int *pcb_regs = (int *)&pcb; + +#ifdef NEWVM + if (physrd(uaddr, (char *)&pcb, sizeof pcb)) + error("cannot read pcb at %x\n", uaddr); + printf("current pcb at %x\n", uaddr); +#else + if (physrd(uaddr, (char *)&pcb, sizeof pcb)) + error("cannot read pcb at %x\n", uaddr); + printf("p0br %x p0lr %x p1br %x p1lr %x\n", + pcb.pcb_p0br, pcb.pcb_p0lr, pcb.pcb_p1br, pcb.pcb_p1lr); +#endif + + /* + * get the register values out of the sys pcb and + * store them where `read_register' will find them. + */ + for (i = 0; i < 8; ++i) + supply_register(i, &pcb_regs[i+10]); + supply_register(8, &pcb_regs[8]); /* eip */ + supply_register(9, &pcb_regs[9]); /* eflags */ + for (i = 10; i < 13; ++i) /* cs, ss, ds */ + supply_register(i, &pcb_regs[i+9]); + supply_register(13, &pcb_regs[18]); /* es */ + for (i = 14; i < 16; ++i) /* fs, gs */ + supply_register(i, &pcb_regs[i+8]); + + /* XXX 80387 registers? */ +} + +static void +setup_kernel_debugging() +{ + struct stat stb; + int devmem = 0; + CORE_ADDR addr; + + fstat(corechan, &stb); + if ((stb.st_mode & S_IFMT) == S_IFCHR && stb.st_rdev == makedev(2, 0)) + devmem = 1; + +#ifdef NEWVM + physrd(ksym_lookup("IdlePTD") - KERNOFF, &sbr, sizeof sbr); + slr = 2 * NPTEPG; /* XXX temporary */ + printf("IdlePTD %x\n", sbr); + curpcb = ksym_lookup("curpcb") - KERNOFF; + physrd(curpcb, &curpcb, sizeof curpcb); + kstack = ksym_lookup("kstack"); +#else + sbr = ksym_lookup("Sysmap"); + slr = ksym_lookup("Syssize"); + printf("sbr %x slr %x\n", sbr, slr); +#endif + + /* + * pcb where "panic" saved registers in first thing in current + * u area. + */ +#ifndef NEWVM + read_pcb(vtophys(ksym_lookup("u"))); +#endif + found_pcb = 1; + if (!devmem) { + /* find stack frame */ + CORE_ADDR panicstr; + char buf[256]; + register char *cp; + + panicstr = kvread(ksym_lookup("panicstr")); + if (panicstr == ~0) + return; + (void) kernel_core_file_hook(panicstr, buf, sizeof(buf)); + for (cp = buf; cp < &buf[sizeof(buf)] && *cp; cp++) + if (!isascii(*cp) || (!isprint(*cp) && !isspace(*cp))) + *cp = '?'; + if (*cp) + *cp = '\0'; + printf("panic: %s\n", buf); + read_pcb(ksym_lookup("dumppcb") - KERNOFF); + } +#ifdef NEWVM + else + read_pcb(vtophys(kstack)); +#endif + + stack_start = USRSTACK; + stack_end = USRSTACK + ctob(UPAGES); +} + +set_paddr_command(arg) + char *arg; +{ + u_int uaddr; + + if (!arg) + error_no_arg("ps-style address for new current process"); + if (!kernel_debugging) + error("not debugging kernel"); + uaddr = (u_int) parse_and_eval_address(arg); +#ifndef NEWVM + read_pcb(ctob(uaddr)); +#else + /* p_addr is now a pcb virtual address */ + read_pcb(vtophys(uaddr)); + curpcb = uaddr; +#endif + + flush_cached_frames(); + set_current_frame(create_new_frame(read_register(FP_REGNUM), read_pc())); + select_frame(get_current_frame(), 0); +} + +/* + * read len bytes from kernel virtual address 'addr' into local + * buffer 'buf'. Return 0 if read ok, 1 otherwise. On read + * errors, portion of buffer not read is zeroed. + */ +kernel_core_file_hook(addr, buf, len) + CORE_ADDR addr; + char *buf; + int len; +{ + int i; + CORE_ADDR paddr; + + while (len > 0) { + paddr = vtophys(addr); + if (paddr == ~0) { + bzero(buf, len); + return (1); + } + /* we can't read across a page boundary */ + i = min(len, NBPG - (addr & PGOFSET)); + if (physrd(paddr, buf, i)) { + bzero(buf, len); + return (1); + } + buf += i; + addr += i; + len -= i; + } + return (0); +} +#endif + +core_file_command(filename, from_tty) + char *filename; + int from_tty; +{ + int val; + extern char registers[]; +#ifdef KERNELDEBUG + struct stat stb; +#endif + + /* + * Discard all vestiges of any previous core file and mark data and + * stack spaces as empty. + */ + if (corefile) + free(corefile); + corefile = 0; + core_file_hook = 0; + + if (corechan >= 0) + close(corechan); + corechan = -1; + + /* Now, if a new core file was specified, open it and digest it. */ + + if (filename == 0) { + if (from_tty) + printf("No core file now.\n"); + return; + } + filename = tilde_expand(filename); + make_cleanup(free, filename); + if (have_inferior_p()) + error("To look at a core file, you must kill the inferior with \"kill\"."); + corechan = open(filename, O_RDONLY, 0); + if (corechan < 0) + perror_with_name(filename); + +#ifdef KERNELDEBUG + fstat(corechan, &stb); + + if (kernel_debugging) { + setup_kernel_debugging(); + core_file_hook = kernel_core_file_hook; + } else if ((stb.st_mode & S_IFMT) == S_IFCHR && + stb.st_rdev == makedev(2, 1)) { + /* looking at /dev/kmem */ + data_offset = data_start = KERNOFF; + data_end = ~0; /* XXX */ + stack_end = stack_start = data_end; + } else +#endif + { + /* + * 4.2-style core dump file. + */ + struct user u; + unsigned int reg_offset; + + val = myread(corechan, &u, sizeof u); + if (val < 0) + perror_with_name("Not a core file: reading upage"); + if (val != sizeof u) + error("Not a core file: could only read %d bytes", val); + + /* + * We are depending on exec_file_command having been + * called previously to set exec_data_start. Since + * the executable and the core file share the same + * text segment, the address of the data segment will + * be the same in both. + */ + data_start = exec_data_start; + +#ifndef NEWVM + data_end = data_start + NBPG * u.u_dsize; + stack_start = stack_end - NBPG * u.u_ssize; + data_offset = NBPG * UPAGES; + stack_offset = NBPG * (UPAGES + u.u_dsize); + + /* + * Some machines put an absolute address in here and + * some put the offset in the upage of the regs. + */ + reg_offset = (int) u.u_ar0 - KERNEL_U_ADDR; +#else + data_end = data_start + + NBPG * u.u_kproc.kp_eproc.e_vm.vm_dsize; + stack_start = stack_end - + NBPG * u.u_kproc.kp_eproc.e_vm.vm_ssize; + data_offset = NBPG * UPAGES; + stack_offset = NBPG * + (UPAGES + u.u_kproc.kp_eproc.e_vm.vm_dsize); + + reg_offset = (int) u.u_kproc.kp_proc.p_regs - USRSTACK; +#endif + + setregmap(u.u_pcb.pcb_flags); + + /* + * I don't know where to find this info. So, for now, + * mark it as not available. + */ + /* N_SET_MAGIC (core_aouthdr, 0); */ + bzero ((char *) &core_aouthdr, sizeof core_aouthdr); + + /* + * Read the register values out of the core file and + * store them where `read_register' will find them. + */ + { + register int regno; + + for (regno = 0; regno < NUM_REGS; regno++) { + char buf[MAX_REGISTER_RAW_SIZE]; + + val = lseek(corechan, register_addr(regno, reg_offset), 0); + if (val < 0 + || (val = myread(corechan, buf, sizeof buf)) < 0) { + char *buffer = (char *) alloca(strlen(reg_names[regno]) + 30); + strcpy(buffer, "Reading register "); + strcat(buffer, reg_names[regno]); + perror_with_name(buffer); + } + supply_register(regno, buf); + } + } + } +#endif + if (filename[0] == '/') + corefile = savestring(filename, strlen(filename)); + else + corefile = concat(current_directory, "/", filename); + + set_current_frame(create_new_frame(read_register(FP_REGNUM), + read_pc())); + select_frame(get_current_frame(), 0); + validate_files(); +} + +exec_file_command(filename, from_tty) + char *filename; + int from_tty; +{ + int val; + + /* + * Eliminate all traces of old exec file. Mark text segment as empty. + */ + + if (execfile) + free(execfile); + execfile = 0; + data_start = 0; + data_end = 0; + stack_start = 0; + stack_end = 0; + text_start = 0; + text_end = 0; + exec_data_start = 0; + exec_data_end = 0; + if (execchan >= 0) + close(execchan); + execchan = -1; + + /* Now open and digest the file the user requested, if any. */ + + if (filename) { + filename = tilde_expand(filename); + make_cleanup(free, filename); + + execchan = openp(getenv("PATH"), 1, filename, O_RDONLY, 0, + &execfile); + if (execchan < 0) + perror_with_name(filename); + + { + struct stat st_exec; + +#ifdef HEADER_SEEK_FD + HEADER_SEEK_FD(execchan); +#endif + + val = myread(execchan, &exec_aouthdr, sizeof(AOUTHDR)); + + if (val < 0) + perror_with_name(filename); + +#ifdef KERNELDEBUG + if (kernel_debugging) { + /* Gross and disgusting XXX */ + text_start = KERNTEXT_BASE; + exec_data_start = KERNTEXT_BASE + + (exec_aouthdr.a_text + 4095) & ~ 4095; + } else { +#endif + text_start = N_TXTADDR(exec_aouthdr); + exec_data_start = N_DATADDR(exec_aouthdr); +#ifdef KERNELDEBUG + } +#endif + + text_offset = N_TXTOFF(exec_aouthdr); + exec_data_offset = N_TXTOFF(exec_aouthdr) + exec_aouthdr.a_text; + + text_end = text_start + exec_aouthdr.a_text; + exec_data_end = exec_data_start + exec_aouthdr.a_data; + + fstat(execchan, &st_exec); + exec_mtime = st_exec.st_mtime; + } + + validate_files(); + } else if (from_tty) + printf("No exec file now.\n"); + + /* Tell display code (if any) about the changed file name. */ + if (exec_file_display_hook) + (*exec_file_display_hook) (filename); +} + +int dummy_code[] = { + 0xb8909090, /* nop; nop; nop; movl $0x32323232,%eax */ + 0x32323232, +#define DUMMY_CALL_INDEX 1 + 0x90ccd0ff, /* call %eax; int3; nop */ +}; + +/* + * Build `dummy' call instructions on inferior's stack to cause + * it to call a subroutine. + * + * N.B. - code in wait_for_inferior requires that sp < pc < fp when + * we take the trap 2 above so it will recognize that we stopped + * at a `dummy' call. So, after the call sp is *not* decremented + * to clean the arguments, code & other stuff we lay on the stack. + * Since the regs are restored to saved values at the breakpoint, + * sp will get reset correctly. Also, this restore means we don't + * have to construct frame linkage info to save pc & fp. The lack + * of frame linkage means we can't do a backtrace, etc., if the + * called function gets a fault or hits a breakpoint but code in + * run_stack_dummy makes this impossible anyway. + */ +CORE_ADDR +setup_dummy(sp, funaddr, nargs, args, struct_return_bytes, pushfn) + CORE_ADDR sp; + CORE_ADDR funaddr; + int nargs; + value *args; + int struct_return_bytes; + CORE_ADDR (*pushfn)(); +{ + int padding, i; + CORE_ADDR top = sp, struct_addr, pc; + + i = arg_stacklen(nargs, args) + struct_return_bytes + + sizeof(dummy_code); + if (i & 3) + padding = 4 - (i & 3); + else + padding = 0; + pc = sp - sizeof(dummy_code); + sp = pc - padding - struct_return_bytes; + struct_addr = sp; + while (--nargs >= 0) + sp = (*pushfn)(sp, *args++); + if (struct_return_bytes) + STORE_STRUCT_RETURN(struct_addr, sp); + write_register(SP_REGNUM, sp); + + dummy_code[DUMMY_CALL_INDEX] = (int)funaddr; + write_memory(pc, (char *)dummy_code, sizeof(dummy_code)); + + return pc; +} + +/* helper functions for m-i386.h */ + +/* stdio style buffering to minimize calls to ptrace */ +static CORE_ADDR codestream_next_addr; +static CORE_ADDR codestream_addr; +static unsigned char codestream_buf[sizeof (int)]; +static int codestream_off; +static int codestream_cnt; + +#define codestream_tell() (codestream_addr + codestream_off) +#define codestream_peek() (codestream_cnt == 0 ? \ + codestream_fill(1): codestream_buf[codestream_off]) +#define codestream_get() (codestream_cnt-- == 0 ? \ + codestream_fill(0) : codestream_buf[codestream_off++]) + +static unsigned char +codestream_fill (peek_flag) +{ + codestream_addr = codestream_next_addr; + codestream_next_addr += sizeof (int); + codestream_off = 0; + codestream_cnt = sizeof (int); + read_memory (codestream_addr, + (unsigned char *)codestream_buf, + sizeof (int)); + + if (peek_flag) + return (codestream_peek()); + else + return (codestream_get()); +} + +static void +codestream_seek (place) +{ + codestream_next_addr = place & -sizeof (int); + codestream_cnt = 0; + codestream_fill (1); + while (codestream_tell() != place) + codestream_get (); +} + +static void +codestream_read (buf, count) + unsigned char *buf; +{ + unsigned char *p; + int i; + p = buf; + for (i = 0; i < count; i++) + *p++ = codestream_get (); +} + +/* next instruction is a jump, move to target */ +static +i386_follow_jump () +{ + int long_delta; + short short_delta; + char byte_delta; + int data16; + int pos; + + pos = codestream_tell (); + + data16 = 0; + if (codestream_peek () == 0x66) + { + codestream_get (); + data16 = 1; + } + + switch (codestream_get ()) + { + case 0xe9: + /* relative jump: if data16 == 0, disp32, else disp16 */ + if (data16) + { + codestream_read ((unsigned char *)&short_delta, 2); + pos += short_delta + 3; /* include size of jmp inst */ + } + else + { + codestream_read ((unsigned char *)&long_delta, 4); + pos += long_delta + 5; + } + break; + case 0xeb: + /* relative jump, disp8 (ignore data16) */ + codestream_read ((unsigned char *)&byte_delta, 1); + pos += byte_delta + 2; + break; + } + codestream_seek (pos + data16); +} + +/* + * find & return amound a local space allocated, and advance codestream to + * first register push (if any) + * + * if entry sequence doesn't make sense, return -1, and leave + * codestream pointer random + */ +static long +i386_get_frame_setup (pc) +{ + unsigned char op; + + codestream_seek (pc); + + i386_follow_jump (); + + op = codestream_get (); + + if (op == 0x58) /* popl %eax */ + { + /* + * this function must start with + * + * popl %eax 0x58 + * xchgl %eax, (%esp) 0x87 0x04 0x24 + * or xchgl %eax, 0(%esp) 0x87 0x44 0x24 0x00 + * + * (the system 5 compiler puts out the second xchg + * inst, and the assembler doesn't try to optimize it, + * so the 'sib' form gets generated) + * + * this sequence is used to get the address of the return + * buffer for a function that returns a structure + */ + int pos; + unsigned char buf[4]; + static unsigned char proto1[3] = { 0x87,0x04,0x24 }; + static unsigned char proto2[4] = { 0x87,0x44,0x24,0x00 }; + pos = codestream_tell (); + codestream_read (buf, 4); + if (bcmp (buf, proto1, 3) == 0) + pos += 3; + else if (bcmp (buf, proto2, 4) == 0) + pos += 4; + + codestream_seek (pos); + op = codestream_get (); /* update next opcode */ + } + + if (op == 0x55) /* pushl %esp */ + { + /* check for movl %esp, %ebp - can be written two ways */ + switch (codestream_get ()) + { + case 0x8b: + if (codestream_get () != 0xec) + return (-1); + break; + case 0x89: + if (codestream_get () != 0xe5) + return (-1); + break; + default: + return (-1); + } + /* check for stack adjustment + * + * subl $XXX, %esp + * + * note: you can't subtract a 16 bit immediate + * from a 32 bit reg, so we don't have to worry + * about a data16 prefix + */ + op = codestream_peek (); + if (op == 0x83) + { + /* subl with 8 bit immed */ + codestream_get (); + if (codestream_get () != 0xec) + return (-1); + /* subl with signed byte immediate + * (though it wouldn't make sense to be negative) + */ + return (codestream_get()); + } + else if (op == 0x81) + { + /* subl with 32 bit immed */ + int locals; + codestream_get(); + if (codestream_get () != 0xec) + return (-1); + /* subl with 32 bit immediate */ + codestream_read ((unsigned char *)&locals, 4); + return (locals); + } + else + { + return (0); + } + } + else if (op == 0xc8) + { + /* enter instruction: arg is 16 bit unsigned immed */ + unsigned short slocals; + codestream_read ((unsigned char *)&slocals, 2); + codestream_get (); /* flush final byte of enter instruction */ + return (slocals); + } + return (-1); +} + +/* Return number of args passed to a frame. + Can return -1, meaning no way to tell. */ + +/* on the 386, the instruction following the call could be: + * popl %ecx - one arg + * addl $imm, %esp - imm/4 args; imm may be 8 or 32 bits + * anything else - zero args + */ + +int +i386_frame_num_args (fi) + struct frame_info fi; +{ + int retpc; + unsigned char op; + struct frame_info *pfi; + + pfi = get_prev_frame_info ((fi)); + if (pfi == 0) + { + /* Note: this can happen if we are looking at the frame for + main, because FRAME_CHAIN_VALID won't let us go into + start. If we have debugging symbols, that's not really + a big deal; it just means it will only show as many arguments + to main as are declared. */ + return -1; + } + else + { + retpc = pfi->pc; + op = read_memory_integer (retpc, 1); + if (op == 0x59) + /* pop %ecx */ + return 1; + else if (op == 0x83) + { + op = read_memory_integer (retpc+1, 1); + if (op == 0xc4) + /* addl $, %esp */ + return (read_memory_integer (retpc+2,1)&0xff)/4; + else + return 0; + } + else if (op == 0x81) + { /* add with 32 bit immediate */ + op = read_memory_integer (retpc+1, 1); + if (op == 0xc4) + /* addl $, %esp */ + return read_memory_integer (retpc+2, 4) / 4; + else + return 0; + } + else + { + return 0; + } + } +} + +/* + * parse the first few instructions of the function to see + * what registers were stored. + * + * We handle these cases: + * + * The startup sequence can be at the start of the function, + * or the function can start with a branch to startup code at the end. + * + * %ebp can be set up with either the 'enter' instruction, or + * 'pushl %ebp, movl %esp, %ebp' (enter is too slow to be useful, + * but was once used in the sys5 compiler) + * + * Local space is allocated just below the saved %ebp by either the + * 'enter' instruction, or by 'subl $, %esp'. 'enter' has + * a 16 bit unsigned argument for space to allocate, and the + * 'addl' instruction could have either a signed byte, or + * 32 bit immediate. + * + * Next, the registers used by this function are pushed. In + * the sys5 compiler they will always be in the order: %edi, %esi, %ebx + * (and sometimes a harmless bug causes it to also save but not restore %eax); + * however, the code below is willing to see the pushes in any order, + * and will handle up to 8 of them. + * + * If the setup sequence is at the end of the function, then the + * next instruction will be a branch back to the start. + */ + +i386_frame_find_saved_regs (fip, fsrp) + struct frame_info *fip; + struct frame_saved_regs *fsrp; +{ + unsigned long locals; + unsigned char *p; + unsigned char op; + CORE_ADDR dummy_bottom; + CORE_ADDR adr; + int i; + + bzero (fsrp, sizeof *fsrp); + +#if 0 + /* if frame is the end of a dummy, compute where the + * beginning would be + */ + dummy_bottom = fip->frame - 4 - NUM_REGS*4 - CALL_DUMMY_LENGTH; + + /* check if the PC is in the stack, in a dummy frame */ + if (dummy_bottom <= fip->pc && fip->pc <= fip->frame) + { + /* all regs were saved by push_call_dummy () */ + adr = fip->frame - 4; + for (i = 0; i < NUM_REGS; i++) + { + fsrp->regs[i] = adr; + adr -= 4; + } + return; + } +#endif + + locals = i386_get_frame_setup (get_pc_function_start (fip->pc)); + + if (locals >= 0) + { + adr = fip->frame - 4 - locals; + for (i = 0; i < 8; i++) + { + op = codestream_get (); + if (op < 0x50 || op > 0x57) + break; + fsrp->regs[op - 0x50] = adr; + adr -= 4; + } + } + + fsrp->regs[PC_REGNUM] = fip->frame + 4; + fsrp->regs[FP_REGNUM] = fip->frame; +} + +/* return pc of first real instruction */ +i386_skip_prologue (pc) +{ + unsigned char op; + int i; + + if (i386_get_frame_setup (pc) < 0) + return (pc); + + /* found valid frame setup - codestream now points to + * start of push instructions for saving registers + */ + + /* skip over register saves */ + for (i = 0; i < 8; i++) + { + op = codestream_peek (); + /* break if not pushl inst */ + if (op < 0x50 || op > 0x57) + break; + codestream_get (); + } + + i386_follow_jump (); + + return (codestream_tell ()); +} + +i386_pop_frame () +{ + FRAME frame = get_current_frame (); + CORE_ADDR fp; + int regnum; + struct frame_saved_regs fsr; + struct frame_info *fi; + + fi = get_frame_info (frame); + fp = fi->frame; + get_frame_saved_regs (fi, &fsr); + for (regnum = 0; regnum < NUM_REGS; regnum++) + { + CORE_ADDR adr; + adr = fsr.regs[regnum]; + if (adr) + write_register (regnum, read_memory_integer (adr, 4)); + } + write_register (FP_REGNUM, read_memory_integer (fp, 4)); + write_register (PC_REGNUM, read_memory_integer (fp + 4, 4)); + write_register (SP_REGNUM, fp + 8); + flush_cached_frames (); + set_current_frame ( create_new_frame (read_register (FP_REGNUM), + read_pc ())); +} + +/* this table must line up with REGISTER_NAMES in m-i386.h */ +/* symbols like 'EAX' come from */ +static int trapmap[] = +{ + tEAX, tECX, tEDX, tEBX, + tESP, tEBP, tESI, tEDI, + tEIP, tEFLAGS, tCS, tSS, + tDS, tES, tES, tES /* lies: no fs or gs */ +}; +static int syscallmap[] = +{ + sEAX, sECX, sEDX, sEBX, + sESP, sEBP, sESI, sEDI, + sEIP, sEFLAGS, sCS, sSS, + sCS, sCS, sCS, sCS /* lies: no ds, es, fs or gs */ +}; +static int *regmap; + +static void +setregmap(flags) + int flags; +{ +#ifdef FM_TRAP + regmap = flags & FM_TRAP ? trapmap: syscallmap; +#elif EX_TRAPSTK + regmap = flags & EX_TRAPSTK ? trapmap : syscallmap; +#else + regmap = trapmap; /* the lesser evil */ +#endif +} + +/* blockend is the value of u.u_ar0, and points to the + * place where GS is stored + */ +i386_register_u_addr (blockend, regnum) +{ +#if 0 + /* this will be needed if fp registers are reinstated */ + /* for now, you can look at them with 'info float' + * sys5 wont let you change them with ptrace anyway + */ + if (regnum >= FP0_REGNUM && regnum <= FP7_REGNUM) + { + int ubase, fpstate; + struct user u; + ubase = blockend + 4 * (SS + 1) - KSTKSZ; + fpstate = ubase + ((char *)&u.u_fpstate - (char *)&u); + return (fpstate + 0x1c + 10 * (regnum - FP0_REGNUM)); + } + else +#endif + return (blockend + 4 * regmap[regnum]); +} + +i387_to_double (from, to) + char *from; + char *to; +{ + long *lp; + /* push extended mode on 387 stack, then pop in double mode + * + * first, set exception masks so no error is generated - + * number will be rounded to inf or 0, if necessary + */ + asm ("pushl %eax"); /* grab a stack slot */ + asm ("fstcw (%esp)"); /* get 387 control word */ + asm ("movl (%esp),%eax"); /* save old value */ + asm ("orl $0x3f,%eax"); /* mask all exceptions */ + asm ("pushl %eax"); + asm ("fldcw (%esp)"); /* load new value into 387 */ + + asm ("movl 8(%ebp),%eax"); + asm ("fldt (%eax)"); /* push extended number on 387 stack */ + asm ("fwait"); + asm ("movl 12(%ebp),%eax"); + asm ("fstpl (%eax)"); /* pop double */ + asm ("fwait"); + + asm ("popl %eax"); /* flush modified control word */ + asm ("fnclex"); /* clear exceptions */ + asm ("fldcw (%esp)"); /* restore original control word */ + asm ("popl %eax"); /* flush saved copy */ +} + +double_to_i387 (from, to) + char *from; + char *to; +{ + /* push double mode on 387 stack, then pop in extended mode + * no errors are possible because every 64-bit pattern + * can be converted to an extended + */ + asm ("movl 8(%ebp),%eax"); + asm ("fldl (%eax)"); + asm ("fwait"); + asm ("movl 12(%ebp),%eax"); + asm ("fstpt (%eax)"); + asm ("fwait"); +} + +struct env387 +{ + unsigned short control; + unsigned short r0; + unsigned short status; + unsigned short r1; + unsigned short tag; + unsigned short r2; + unsigned long eip; + unsigned short code_seg; + unsigned short opcode; + unsigned long operand; + unsigned short operand_seg; + unsigned short r3; + unsigned char regs[8][10]; +}; + +static +print_387_control_word (control) +unsigned short control; +{ + printf ("control 0x%04x: ", control); + printf ("compute to "); + switch ((control >> 8) & 3) + { + case 0: printf ("24 bits; "); break; + case 1: printf ("(bad); "); break; + case 2: printf ("53 bits; "); break; + case 3: printf ("64 bits; "); break; + } + printf ("round "); + switch ((control >> 10) & 3) + { + case 0: printf ("NEAREST; "); break; + case 1: printf ("DOWN; "); break; + case 2: printf ("UP; "); break; + case 3: printf ("CHOP; "); break; + } + if (control & 0x3f) + { + printf ("mask:"); + if (control & 0x0001) printf (" INVALID"); + if (control & 0x0002) printf (" DENORM"); + if (control & 0x0004) printf (" DIVZ"); + if (control & 0x0008) printf (" OVERF"); + if (control & 0x0010) printf (" UNDERF"); + if (control & 0x0020) printf (" LOS"); + printf (";"); + } + printf ("\n"); + if (control & 0xe080) printf ("warning: reserved bits on 0x%x\n", + control & 0xe080); +} + +static +print_387_status_word (status) + unsigned short status; +{ + printf ("status 0x%04x: ", status); + if (status & 0xff) + { + printf ("exceptions:"); + if (status & 0x0001) printf (" INVALID"); + if (status & 0x0002) printf (" DENORM"); + if (status & 0x0004) printf (" DIVZ"); + if (status & 0x0008) printf (" OVERF"); + if (status & 0x0010) printf (" UNDERF"); + if (status & 0x0020) printf (" LOS"); + if (status & 0x0040) printf (" FPSTACK"); + printf ("; "); + } + printf ("flags: %d%d%d%d; ", + (status & 0x4000) != 0, + (status & 0x0400) != 0, + (status & 0x0200) != 0, + (status & 0x0100) != 0); + + printf ("top %d\n", (status >> 11) & 7); +} + +static +print_387_status (status, ep) + unsigned short status; + struct env387 *ep; +{ + int i; + int bothstatus; + int top; + int fpreg; + unsigned char *p; + + bothstatus = ((status != 0) && (ep->status != 0)); + if (status != 0) + { + if (bothstatus) + printf ("u: "); + print_387_status_word (status); + } + + if (ep->status != 0) + { + if (bothstatus) + printf ("e: "); + print_387_status_word (ep->status); + } + + print_387_control_word (ep->control); + printf ("last exception: "); + printf ("opcode 0x%x; ", ep->opcode); + printf ("pc 0x%x:0x%x; ", ep->code_seg, ep->eip); + printf ("operand 0x%x:0x%x\n", ep->operand_seg, ep->operand); + + top = (ep->status >> 11) & 7; + + printf (" regno tag msb lsb value\n"); + for (fpreg = 7; fpreg >= 0; fpreg--) + { + int st_regno; + double val; + + /* The physical regno `fpreg' is only relevant as an index into the + * tag word. Logical `%st' numbers are required for indexing `p->regs. + */ + st_regno = (fpreg + 8 - top) & 0x7; + + printf ("%%st(%d) %s ", st_regno, fpreg == top ? "=>" : " "); + + switch ((ep->tag >> (fpreg * 2)) & 3) + { + case 0: printf ("valid "); break; + case 1: printf ("zero "); break; + case 2: printf ("trap "); break; + case 3: printf ("empty "); break; + } + for (i = 9; i >= 0; i--) + printf ("%02x", ep->regs[st_regno][i]); + + i387_to_double (ep->regs[st_regno], (char *)&val); + printf (" %g\n", val); + } +#if 0 /* reserved fields are always 0xffff on 486's */ + if (ep->r0) + printf ("warning: reserved0 is 0x%x\n", ep->r0); + if (ep->r1) + printf ("warning: reserved1 is 0x%x\n", ep->r1); + if (ep->r2) + printf ("warning: reserved2 is 0x%x\n", ep->r2); + if (ep->r3) + printf ("warning: reserved3 is 0x%x\n", ep->r3); +#endif +} + +#ifdef __386BSD__ +#define fpstate save87 +#define U_FPSTATE(u) u.u_pcb.pcb_savefpu +#endif + +#ifndef U_FPSTATE +#define U_FPSTATE(u) u.u_fpstate +#endif + +i386_float_info () +{ + struct user u; /* just for address computations */ + int i; + /* fpstate defined in */ + struct fpstate *fpstatep; + char buf[sizeof (struct fpstate) + 2 * sizeof (int)]; + unsigned int uaddr; + char fpvalid; + unsigned int rounded_addr; + unsigned int rounded_size; + extern int corechan; + int skip; + +#ifndef __386BSD__ /* XXX - look at pcb flags */ + uaddr = (char *)&u.u_fpvalid - (char *)&u; + if (have_inferior_p()) + { + unsigned int data; + unsigned int mask; + + rounded_addr = uaddr & -sizeof (int); + data = ptrace (PT_READ_U, inferior_pid, (caddr_t)rounded_addr, 0); + mask = 0xff << ((uaddr - rounded_addr) * 8); + + fpvalid = ((data & mask) != 0); + } + else + { + if (lseek (corechan, uaddr, 0) < 0) + perror ("seek on core file"); + if (myread (corechan, &fpvalid, 1) < 0) + perror ("read on core file"); + + } + + if (fpvalid == 0) + { + printf ("no floating point status saved\n"); + return; + } +#endif /* not __386BSD__ */ + + uaddr = (char *)&U_FPSTATE(u) - (char *)&u; + if (have_inferior_p ()) + { + int *ip; + + rounded_addr = uaddr & -sizeof (int); + rounded_size = (((uaddr + sizeof (struct fpstate)) - uaddr) + + sizeof (int) - 1) / sizeof (int); + skip = uaddr - rounded_addr; + + ip = (int *)buf; + for (i = 0; i < rounded_size; i++) + { + *ip++ = ptrace (PT_READ_U, inferior_pid, (caddr_t)rounded_addr, 0); + rounded_addr += sizeof (int); + } + } + else + { + if (lseek (corechan, uaddr, 0) < 0) + perror_with_name ("seek on core file"); + if (myread (corechan, buf, sizeof (struct fpstate)) < 0) + perror_with_name ("read from core file"); + skip = 0; + } + +#ifdef __386BSD__ + print_387_status (0, (struct env387 *)buf); +#else + fpstatep = (struct fpstate *)(buf + skip); + print_387_status (fpstatep->status, (struct env387 *)fpstatep->state); +#endif +} + +void +_initialize_i386bsd_dep() +{ +#ifdef KERNELDEBUG + add_com ("process-address", class_obscure, set_paddr_command, + "The process identified by (ps-style) ADDR becomes the\n\ +\"current\" process context for kernel debugging."); + add_com_alias ("paddr", "process-address", class_obscure, 0); +#endif +} diff --git a/gnu/usr.bin/gdb/config/m-i386-sv32.h b/gnu/usr.bin/gdb/config/m-i386-sv32.h new file mode 100644 index 0000000000..38fb4eb6d5 --- /dev/null +++ b/gnu/usr.bin/gdb/config/m-i386-sv32.h @@ -0,0 +1,28 @@ +/* Macro defintions for i386, running System V 3.2. + Copyright (C) 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "m-i386.h" + +/* Apparently there is inconsistency among various System V's about what + the name of this field is. */ +#define U_FPSTATE(u) u.u_fps.u_fpstate + +/* TIOCGETC is defined in System V 3.2 termio.h, but struct tchars + is not. This makes problems for inflow.c. */ +#define TIOCGETC_BROKEN diff --git a/gnu/usr.bin/gdb/config/m-i386.h b/gnu/usr.bin/gdb/config/m-i386.h new file mode 100644 index 0000000000..5449ec454c --- /dev/null +++ b/gnu/usr.bin/gdb/config/m-i386.h @@ -0,0 +1,394 @@ +/* Macro defintions for i386. + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Define the bit, byte, and word ordering of the machine. */ +/* #define BITS_BIG_ENDIAN */ +/* #define BYTES_BIG_ENDIAN */ +/* #define WORDS_BIG_ENDIAN */ + +/* + * Changes for 80386 by Pace Willisson (pace@prep.ai.mit.edu) + * July 1988 + */ + + +#ifndef i386 +#define i386 +#endif + +/* I'm running gdb 3.4 under 386/ix 2.0.2, which is a derivative of AT&T's +Sys V/386 3.2. + +On some machines, gdb crashes when it's starting up while calling the +vendor's termio tgetent() routine. It always works when run under +itself (actually, under 3.2, it's not an infinitely recursive bug.) +After some poking around, it appears that depending on the environment +size, or whether you're running YP, or the phase of the moon or something, +the stack is not always long-aligned when main() is called, and tgetent() +takes strong offense at that. On some machines this bug never appears, but +on those where it does, it occurs quite reliably. */ +#define ALIGN_STACK_ON_STARTUP + +/* define USG if you are using sys5 /usr/include's */ +#define USG + +/* USG systems need these */ +#define vfork() fork() +#define MAXPATHLEN 500 + +/* define this if you don't have the extension to coff that allows + * file names to appear in the string table + * (aux.x_file.x_foff) + */ +#define COFF_NO_LONG_FILE_NAMES + +/* turn this on when rest of gdb is ready */ +/* #define IEEE_FLOAT */ + +#define NBPG NBPC +#define UPAGES USIZE + +#define HAVE_TERMIO + +/* Get rid of any system-imposed stack limit if possible. */ + +/* #define SET_STACK_LIMIT_HUGE not in sys5 */ + +/* Define this if the C compiler puts an underscore at the front + of external names before giving them to the linker. */ + +/* #define NAMES_HAVE_UNDERSCORE */ + +/* Specify debugger information format. */ + +/* #define READ_DBX_FORMAT */ +#define COFF_FORMAT + +/* number of traps that happen between exec'ing the shell + * to run an inferior, and when we finally get to + * the inferior code. This is 2 on most implementations. + */ +#define START_INFERIOR_TRAPS_EXPECTED 4 + +/* Offset from address of function to start of its code. + Zero on most machines. */ + +#define FUNCTION_START_OFFSET 0 + +/* Advance PC across any function entry prologue instructions + to reach some "real" code. */ + +#define SKIP_PROLOGUE(frompc) {(frompc) = i386_skip_prologue((frompc));} + +/* Immediately after a function call, return the saved pc. + Can't always go through the frames for this because on some machines + the new frame is not set up until the new function executes + some instructions. */ + +#define SAVED_PC_AFTER_CALL(frame) \ + (read_memory_integer (read_register (SP_REGNUM), 4)) + +/* This is the amount to subtract from u.u_ar0 + to get the offset in the core file of the register values. */ + +#define KERNEL_U_ADDR 0xe0000000 + +/* Address of end of stack space. */ + +#define STACK_END_ADDR 0x80000000 + +/* Stack grows downward. */ + +#define INNER_THAN < + +/* Sequence of bytes for breakpoint instruction. */ + +#define BREAKPOINT {0xcc} + +/* Amount PC must be decremented by after a breakpoint. + This is often the number of bytes in BREAKPOINT + but not always. */ + +#define DECR_PC_AFTER_BREAK 1 + +/* Nonzero if instruction at PC is a return instruction. */ + +#define ABOUT_TO_RETURN(pc) (read_memory_integer (pc, 1) == 0xc3) + +/* Return 1 if P points to an invalid floating point value. + LEN is the length in bytes -- not relevant on the 386. */ + +#define INVALID_FLOAT(p, len) (0) + +/* code to execute to print interesting information about the + * floating point processor (if any) + * No need to define if there is nothing to do. + */ +#define FLOAT_INFO { i386_float_info (); } + + +/* Largest integer type */ +#define LONGEST long + +/* Name of the builtin type for the LONGEST type above. */ +#define BUILTIN_TYPE_LONGEST builtin_type_long + +/* Say how long (ordinary) registers are. */ + +#define REGISTER_TYPE long + +/* Number of machine registers */ + +#define NUM_REGS 16 + +/* Initializer for an array of names of registers. + There should be NUM_REGS strings in this initializer. */ + +/* the order of the first 8 registers must match the compiler's + * numbering scheme (which is the same as the 386 scheme) + * also, this table must match regmap in i386-pinsn.c. + */ +#define REGISTER_NAMES { "eax", "ecx", "edx", "ebx", \ + "esp", "ebp", "esi", "edi", \ + "eip", "ps", "cs", "ss", \ + "ds", "es", "fs", "gs", \ + } + +/* Register numbers of various important registers. + Note that some of these values are "real" register numbers, + and correspond to the general registers of the machine, + and some are "phony" register numbers which are too large + to be actual register numbers as far as the user is concerned + but do serve to get the desired values when passed to read_register. */ + +#define FP_REGNUM 5 /* Contains address of executing stack frame */ +#define SP_REGNUM 4 /* Contains address of top of stack */ + +#define PC_REGNUM 8 +#define PS_REGNUM 9 + +#define REGISTER_U_ADDR(addr, blockend, regno) \ + (addr) = i386_register_u_addr ((blockend),(regno)); + +/* Total amount of space needed to store our copies of the machine's + register state, the array `registers'. */ +#define REGISTER_BYTES (NUM_REGS * 4) + +/* Index within `registers' of the first byte of the space for + register N. */ + +#define REGISTER_BYTE(N) ((N)*4) + +/* Number of bytes of storage in the actual machine representation + for register N. */ + +#define REGISTER_RAW_SIZE(N) (4) + +/* Number of bytes of storage in the program's representation + for register N. */ + +#define REGISTER_VIRTUAL_SIZE(N) (4) + +/* Largest value REGISTER_RAW_SIZE can have. */ + +#define MAX_REGISTER_RAW_SIZE 4 + +/* Largest value REGISTER_VIRTUAL_SIZE can have. */ + +#define MAX_REGISTER_VIRTUAL_SIZE 4 + +/* Nonzero if register N requires conversion + from raw format to virtual format. */ + +#define REGISTER_CONVERTIBLE(N) (0) + +/* Convert data from raw format for register REGNUM + to virtual format for register REGNUM. */ + +#define REGISTER_CONVERT_TO_VIRTUAL(REGNUM,FROM,TO) {bcopy ((FROM), (TO), 4);} + +/* Convert data from virtual format for register REGNUM + to raw format for register REGNUM. */ + +#define REGISTER_CONVERT_TO_RAW(REGNUM,FROM,TO) {bcopy ((FROM), (TO), 4);} + +/* Return the GDB type object for the "standard" data type + of data in register N. */ + +#define REGISTER_VIRTUAL_TYPE(N) (builtin_type_int) + +/* Store the address of the place in which to copy the structure the + subroutine will return. This is called from call_function. */ + +#define STORE_STRUCT_RETURN(ADDR, SP) \ + { (SP) -= sizeof (ADDR); \ + write_memory ((SP), &(ADDR), sizeof (ADDR)); } + +/* Extract from an array REGBUF containing the (raw) register state + a function return value of type TYPE, and copy that, in virtual format, + into VALBUF. */ + +#define EXTRACT_RETURN_VALUE(TYPE,REGBUF,VALBUF) \ + bcopy (REGBUF, VALBUF, TYPE_LENGTH (TYPE)) + +/* Write into appropriate registers a function return value + of type TYPE, given in virtual format. */ + +#define STORE_RETURN_VALUE(TYPE,VALBUF) \ + write_register_bytes (0, VALBUF, TYPE_LENGTH (TYPE)) + +/* Extract from an array REGBUF containing the (raw) register state + the address in which a function should return its structure value, + as a CORE_ADDR (or an expression that can be used as one). */ + +#define EXTRACT_STRUCT_VALUE_ADDRESS(REGBUF) (*(int *)(REGBUF)) + + +/* Describe the pointer in each stack frame to the previous stack frame + (its caller). */ + +/* FRAME_CHAIN takes a frame's nominal address + and produces the frame's chain-pointer. + + FRAME_CHAIN_COMBINE takes the chain pointer and the frame's nominal address + and produces the nominal address of the caller frame. + + However, if FRAME_CHAIN_VALID returns zero, + it means the given frame is the outermost one and has no caller. + In that case, FRAME_CHAIN_COMBINE is not used. */ + +#define FRAME_CHAIN(thisframe) \ + (outside_startup_file ((thisframe)->pc) ? \ + read_memory_integer ((thisframe)->frame, 4) :\ + 0) + +#define FRAME_CHAIN_VALID(chain, thisframe) \ + (chain != 0 && (outside_startup_file (FRAME_SAVED_PC (thisframe)))) + +#define FRAME_CHAIN_COMBINE(chain, thisframe) (chain) + +/* Define other aspects of the stack frame. */ + +/* A macro that tells us whether the function invocation represented + by FI does not have a frame on the stack associated with it. If it + does not, FRAMELESS is set to 1, else 0. */ +#define FRAMELESS_FUNCTION_INVOCATION(FI, FRAMELESS) \ + FRAMELESS_LOOK_FOR_PROLOGUE(FI, FRAMELESS) + +#define FRAME_SAVED_PC(FRAME) (read_memory_integer ((FRAME)->frame + 4, 4)) + +#define FRAME_ARGS_ADDRESS(fi) ((fi)->frame) + +#define FRAME_LOCALS_ADDRESS(fi) ((fi)->frame) + +/* Return number of args passed to a frame. + Can return -1, meaning no way to tell. */ + +#define FRAME_NUM_ARGS(numargs, fi) (numargs) = i386_frame_num_args(fi) + +/* Return number of bytes at start of arglist that are not really args. */ + +#define FRAME_ARGS_SKIP 8 + +/* Put here the code to store, into a struct frame_saved_regs, + the addresses of the saved registers of frame described by FRAME_INFO. + This includes special registers such as pc and fp saved in special + ways in the stack frame. sp is even more special: + the address we return for it IS the sp for the next frame. */ + +#define FRAME_FIND_SAVED_REGS(frame_info, frame_saved_regs) \ +{ i386_frame_find_saved_regs ((frame_info), &(frame_saved_regs)); } + + +/* Things needed for making the inferior call functions. */ + +/* Push an empty stack frame, to record the current PC, etc. */ + +#define PUSH_DUMMY_FRAME { i386_push_dummy_frame (); } + +/* Discard from the stack the innermost frame, restoring all registers. */ + +#define POP_FRAME { i386_pop_frame (); } + +/* this is + * call 11223344 (32 bit relative) + * int3 + */ + +#define CALL_DUMMY { 0x223344e8, 0xcc11 } + +#define CALL_DUMMY_LENGTH 8 + +#define CALL_DUMMY_START_OFFSET 0 /* Start execution at beginning of dummy */ + +/* Insert the specified number of args and function address + into a call sequence of the above form stored at DUMMYNAME. */ + +#define FIX_CALL_DUMMY(dummyname, pc, fun, nargs, type) \ +{ \ + int from, to, delta, loc; \ + loc = (int)(read_register (SP_REGNUM) - CALL_DUMMY_LENGTH); \ + from = loc + 5; \ + to = (int)(fun); \ + delta = to - from; \ + *(int *)((char *)(dummyname) + 1) = delta; \ +} + + +#if 0 +/* Interface definitions for kernel debugger KDB. */ + +/* Map machine fault codes into signal numbers. + First subtract 0, divide by 4, then index in a table. + Faults for which the entry in this table is 0 + are not handled by KDB; the program's own trap handler + gets to handle then. */ + +#define FAULT_CODE_ORIGIN 0 +#define FAULT_CODE_UNITS 4 +#define FAULT_TABLE \ +{ 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0} + +/* Start running with a stack stretching from BEG to END. + BEG and END should be symbols meaningful to the assembler. + This is used only for kdb. */ + +#define INIT_STACK(beg, end) {} + +/* Push the frame pointer register on the stack. */ +#define PUSH_FRAME_PTR {} + +/* Copy the top-of-stack to the frame pointer register. */ +#define POP_FRAME_PTR {} + +/* After KDB is entered by a fault, push all registers + that GDB thinks about (all NUM_REGS of them), + so that they appear in order of ascending GDB register number. + The fault code will be on the stack beyond the last register. */ + +#define PUSH_REGISTERS {} + +/* Assuming the registers (including processor status) have been + pushed on the stack in order of ascending GDB register number, + restore them and return to the address in the saved PC register. */ + +#define POP_REGISTERS {} +#endif diff --git a/gnu/usr.bin/gdb/config/m-i386bsd.h b/gnu/usr.bin/gdb/config/m-i386bsd.h new file mode 100644 index 0000000000..15d97b23d3 --- /dev/null +++ b/gnu/usr.bin/gdb/config/m-i386bsd.h @@ -0,0 +1,375 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1991 by William Jolitz at UUNET Technologies, Inc. + * + * @(#)m-i386bsd.h 6.7 (Berkeley) 5/8/91 + */ + +/* Macro definitions for i386. + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Define the bit, byte, and word ordering of the machine. */ +/* #define BITS_BIG_ENDIAN */ +/* #define BYTES_BIG_ENDIAN */ +/* #define WORDS_BIG_ENDIAN */ + +/* + * Changes for 80386 by Pace Willisson (pace@prep.ai.mit.edu) + * July 1988 + * [ MODIFIED FOR 386BSD W. Jolitz ] + */ + +#ifndef i386 +#define i386 1 +#define i386b 1 +#endif + +#define IEEE_FLOAT +#define LONG_LONG + +/* Library stuff: POSIX tty (not supported yet), V7 tty (sigh), vprintf. */ + +#define HAVE_TERMIOS 1 +#define USE_OLD_TTY 1 +#define HAVE_VPRINTF 1 + +/* We support local and remote kernel debugging. */ + +#define KERNELDEBUG 1 + +/* Get rid of any system-imposed stack limit if possible. */ + +#define SET_STACK_LIMIT_HUGE + +/* Define this if the C compiler puts an underscore at the front + of external names before giving them to the linker. */ + +#define NAMES_HAVE_UNDERSCORE + +/* Specify debugger information format. */ + +#define READ_DBX_FORMAT + +/* number of traps that happen between exec'ing the shell + * to run an inferior, and when we finally get to + * the inferior code. This is 2 on most implementations. + */ +#define START_INFERIOR_TRAPS_EXPECTED 2 + +/* Offset from address of function to start of its code. + Zero on most machines. */ + +#define FUNCTION_START_OFFSET 0 + +/* Advance PC across any function entry prologue instructions + to reach some "real" code. */ + +#define SKIP_PROLOGUE(frompc) {(frompc) = i386_skip_prologue((frompc));} + +/* Immediately after a function call, return the saved pc. + Can't always go through the frames for this because on some machines + the new frame is not set up until the new function executes + some instructions. */ + +#define SAVED_PC_AFTER_CALL(frame) \ + (read_memory_integer (read_register (SP_REGNUM), 4)) + +/* This is the amount to subtract from u.u_ar0 + to get the offset in the core file of the register values. */ + +#ifdef NEWVM +#include +#define KERNEL_U_ADDR USRSTACK +#else +#define KERNEL_U_ADDR 0xfdffd000 +#endif + +/* Address of end of stack space. */ + +#define STACK_END_ADDR KERNEL_U_ADDR + +/* Stack grows downward. */ + +#define INNER_THAN < + +/* Sequence of bytes for breakpoint instruction. */ + +#define BREAKPOINT {0xcc} + +/* Amount PC must be decremented by after a breakpoint. + This is often the number of bytes in BREAKPOINT + but not always. */ + +#define DECR_PC_AFTER_BREAK 1 + +/* Nonzero if instruction at PC is a return instruction. */ + +#define ABOUT_TO_RETURN(pc) \ + strchr("\302\303\312\313\317", read_memory_integer(pc, 1)) + +/* Return 1 if P points to an invalid floating point value. + LEN is the length in bytes -- not relevant on the 386. */ + +#define INVALID_FLOAT(p, len) (0) + +/* code to execute to print interesting information about the + * floating point processor (if any) + * No need to define if there is nothing to do. + */ +#define FLOAT_INFO { i386_float_info (); } + + +/* Largest integer type */ +#define LONGEST long long + +/* Name of the builtin type for the LONGEST type above. */ +#define BUILTIN_TYPE_LONGEST builtin_type_long_long + +/* Say how long (ordinary) registers are. */ + +#define REGISTER_TYPE long + +/* Number of machine registers */ + +#define NUM_REGS 16 + +/* Initializer for an array of names of registers. + There should be NUM_REGS strings in this initializer. */ + +/* the order of the first 8 registers must match the compiler's + * numbering scheme (which is the same as the 386 scheme) + * also, this table must match regmap in i386-pinsn.c. + */ +#define REGISTER_NAMES { "eax", "ecx", "edx", "ebx", \ + "esp", "ebp", "esi", "edi", \ + "eip", "ps", "cs", "ss", \ + "ds", "es", "fs", "gs", \ + } + +/* Register numbers of various important registers. + Note that some of these values are "real" register numbers, + and correspond to the general registers of the machine, + and some are "phony" register numbers which are too large + to be actual register numbers as far as the user is concerned + but do serve to get the desired values when passed to read_register. */ + +#define FP_REGNUM 5 /* Contains address of executing stack frame */ +#define SP_REGNUM 4 /* Contains address of top of stack */ + +#define PC_REGNUM 8 +#define PS_REGNUM 9 + +#define REGISTER_U_ADDR(addr, blockend, regno) \ + (addr) = i386_register_u_addr ((blockend),(regno)); + +/* Total amount of space needed to store our copies of the machine's + register state, the array `registers'. */ +#define REGISTER_BYTES (NUM_REGS * 4) + +/* Index within `registers' of the first byte of the space for + register N. */ + +#define REGISTER_BYTE(N) ((N)*4) + +/* Number of bytes of storage in the actual machine representation + for register N. */ + +#define REGISTER_RAW_SIZE(N) (4) + +/* Number of bytes of storage in the program's representation + for register N. */ + +#define REGISTER_VIRTUAL_SIZE(N) (4) + +/* Largest value REGISTER_RAW_SIZE can have. */ + +#define MAX_REGISTER_RAW_SIZE 4 + +/* Largest value REGISTER_VIRTUAL_SIZE can have. */ + +#define MAX_REGISTER_VIRTUAL_SIZE 4 + +/* Nonzero if register N requires conversion + from raw format to virtual format. */ + +#define REGISTER_CONVERTIBLE(N) (0) + +/* Convert data from raw format for register REGNUM + to virtual format for register REGNUM. */ + +#define REGISTER_CONVERT_TO_VIRTUAL(REGNUM,FROM,TO) {bcopy ((FROM), (TO), 4);} + +/* Convert data from virtual format for register REGNUM + to raw format for register REGNUM. */ + +#define REGISTER_CONVERT_TO_RAW(REGNUM,FROM,TO) {bcopy ((FROM), (TO), 4);} + +/* Return the GDB type object for the "standard" data type + of data in register N. */ + +#define REGISTER_VIRTUAL_TYPE(N) (builtin_type_int) + +/* Store the address of the place in which to copy the structure the + subroutine will return. This is called from call_function. */ + +#define STORE_STRUCT_RETURN(ADDR, SP) \ + { (SP) -= sizeof (ADDR); \ + write_memory ((SP), &(ADDR), sizeof (ADDR)); } + +/* Extract from an array REGBUF containing the (raw) register state + a function return value of type TYPE, and copy that, in virtual format, + into VALBUF. */ + +#define EXTRACT_RETURN_VALUE(TYPE,REGBUF,VALBUF) \ + bcopy (REGBUF, VALBUF, TYPE_LENGTH (TYPE)) + +/* Write into appropriate registers a function return value + of type TYPE, given in virtual format. */ + +#define STORE_RETURN_VALUE(TYPE,VALBUF) \ + write_register_bytes (0, VALBUF, TYPE_LENGTH (TYPE)) + +/* Extract from an array REGBUF containing the (raw) register state + the address in which a function should return its structure value, + as a CORE_ADDR (or an expression that can be used as one). */ + +#define EXTRACT_STRUCT_VALUE_ADDRESS(REGBUF) (*(int *)(REGBUF)) + + +/* Describe the pointer in each stack frame to the previous stack frame + (its caller). */ + +/* FRAME_CHAIN takes a frame's nominal address + and produces the frame's chain-pointer. + + FRAME_CHAIN_COMBINE takes the chain pointer and the frame's nominal address + and produces the nominal address of the caller frame. + + However, if FRAME_CHAIN_VALID returns zero, + it means the given frame is the outermost one and has no caller. + In that case, FRAME_CHAIN_COMBINE is not used. */ + +#define FRAME_CHAIN(thisframe) \ + (outside_startup_file ((thisframe)->pc) ? \ + read_memory_integer ((thisframe)->frame, 4) :\ + 0) + +#ifdef KERNELDEBUG +#define KERNTEXT_BASE 0xfe000000 +#ifdef NEWVM +#define KERNSTACK_TOP (read_register(SP_REGNUM) + 0x2000) /* approximate */ +#else +/* #define KERNSTACK_TOP (P1PAGES << PGSHIFT) */ +#define KERNSTACK_TOP 0xfe000000 +#endif +extern int kernel_debugging; +#define FRAME_CHAIN_VALID(chain, thisframe) \ + (chain != 0 && \ + !kernel_debugging ? outside_startup_file(FRAME_SAVED_PC(thisframe)) :\ + (chain >= read_register(SP_REGNUM) && chain < KERNSTACK_TOP)) +#else +#define FRAME_CHAIN_VALID(chain, thisframe) \ + (chain != 0 && (outside_startup_file (FRAME_SAVED_PC (thisframe)))) +#endif + +#define FRAME_CHAIN_COMBINE(chain, thisframe) (chain) + +/* Define other aspects of the stack frame. */ + +/* A macro that tells us whether the function invocation represented + by FI does not have a frame on the stack associated with it. If it + does not, FRAMELESS is set to 1, else 0. */ +#define FRAMELESS_FUNCTION_INVOCATION(FI, FRAMELESS) \ + FRAMELESS_LOOK_FOR_PROLOGUE(FI, FRAMELESS) + +#define FRAME_SAVED_PC(FRAME) (read_memory_integer ((FRAME)->frame + 4, 4)) + +#define FRAME_ARGS_ADDRESS(fi) ((fi)->frame) + +#define FRAME_LOCALS_ADDRESS(fi) ((fi)->frame) + +/* Return number of args passed to a frame. + Can return -1, meaning no way to tell. */ + +#define FRAME_NUM_ARGS(numargs, fi) (numargs) = i386_frame_num_args(fi) + +/* Return number of bytes at start of arglist that are not really args. */ + +#define FRAME_ARGS_SKIP 8 + +/* Put here the code to store, into a struct frame_saved_regs, + the addresses of the saved registers of frame described by FRAME_INFO. + This includes special registers such as pc and fp saved in special + ways in the stack frame. sp is even more special: + the address we return for it IS the sp for the next frame. */ + +#define FRAME_FIND_SAVED_REGS(frame_info, frame_saved_regs) \ +{ i386_frame_find_saved_regs ((frame_info), &(frame_saved_regs)); } + + +/* Discard from the stack the innermost frame, restoring all registers. */ + +#define POP_FRAME { i386_pop_frame (); } + +#define NEW_CALL_FUNCTION + +#if 0 +/* Interface definitions for kernel debugger KDB. */ + +/* Map machine fault codes into signal numbers. + First subtract 0, divide by 4, then index in a table. + Faults for which the entry in this table is 0 + are not handled by KDB; the program's own trap handler + gets to handle then. */ + +#define FAULT_CODE_ORIGIN 0 +#define FAULT_CODE_UNITS 4 +#define FAULT_TABLE \ +{ 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0} + +/* Start running with a stack stretching from BEG to END. + BEG and END should be symbols meaningful to the assembler. + This is used only for kdb. */ + +#define INIT_STACK(beg, end) {} + +/* Push the frame pointer register on the stack. */ +#define PUSH_FRAME_PTR {} + +/* Copy the top-of-stack to the frame pointer register. */ +#define POP_FRAME_PTR {} + +/* After KDB is entered by a fault, push all registers + that GDB thinks about (all NUM_REGS of them), + so that they appear in order of ascending GDB register number. + The fault code will be on the stack beyond the last register. */ + +#define PUSH_REGISTERS {} + +/* Assuming the registers (including processor status) have been + pushed on the stack in order of ascending GDB register number, + restore them and return to the address in the saved PC register. */ + +#define POP_REGISTERS {} +#endif diff --git a/gnu/usr.bin/gdb/config/m-i386g-sv32.h b/gnu/usr.bin/gdb/config/m-i386g-sv32.h new file mode 100644 index 0000000000..3d69eea184 --- /dev/null +++ b/gnu/usr.bin/gdb/config/m-i386g-sv32.h @@ -0,0 +1,28 @@ +/* Macro defintions for i386, running System V 3.2. + Copyright (C) 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "m-i386gas.h" + +/* Apparently there is inconsistency among various System V's about what + the name of this field is. */ +#define U_FPSTATE(u) u.u_fps.u_fpstate + +/* TIOCGETC is defined in System V 3.2 termio.h, but struct tchars + is not. This makes problems for inflow.c. */ +#define TIOCGETC_BROKEN diff --git a/gnu/usr.bin/gdb/config/m-i386gas.h b/gnu/usr.bin/gdb/config/m-i386gas.h new file mode 100644 index 0000000000..fbd21385cc --- /dev/null +++ b/gnu/usr.bin/gdb/config/m-i386gas.h @@ -0,0 +1,37 @@ +/* Macro definitions for i386 using the GNU object file format. + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * Changes for 80386 by Pace Willisson (pace@prep.ai.mit.edu) + * July 1988 + * + * i386gnu: COFF_ENCAPSULATE + */ + + +#define COFF_ENCAPSULATE + +#include "m-i386.h" + + +#define NAMES_HAVE_UNDERSCORE + +#undef COFF_FORMAT +#define READ_DBX_FORMAT + diff --git a/gnu/usr.bin/gdb/copying.c b/gnu/usr.bin/gdb/copying.c new file mode 100644 index 0000000000..b3d7519807 --- /dev/null +++ b/gnu/usr.bin/gdb/copying.c @@ -0,0 +1,215 @@ +/* Do not modify this file; it is created automatically + by copying.awk. */ +extern int immediate_quit; +static void +copying_info () +{ + immediate_quit++; + printf_filtered ("\n"); + printf_filtered (" GNU GENERAL PUBLIC LICENSE\n"); + printf_filtered (" Version 1, February 1989\n"); + printf_filtered ("\n"); + printf_filtered (" Copyright (C) 1989 Free Software Foundation, Inc.\n"); + printf_filtered (" 675 Mass Ave, Cambridge, MA 02139, USA\n"); + printf_filtered (" Everyone is permitted to copy and distribute verbatim copies\n"); + printf_filtered (" of this license document, but changing it is not allowed.\n"); + printf_filtered ("\n"); + printf_filtered (" Preamble\n"); + printf_filtered ("\n"); + printf_filtered (" The license agreements of most software companies try to keep users\n"); + printf_filtered ("at the mercy of those companies. By contrast, our General Public\n"); + printf_filtered ("License is intended to guarantee your freedom to share and change free\n"); + printf_filtered ("software--to make sure the software is free for all its users. The\n"); + printf_filtered ("General Public License applies to the Free Software Foundation's\n"); + printf_filtered ("software and to any other program whose authors commit to using it.\n"); + printf_filtered ("You can use it for your programs, too.\n"); + printf_filtered ("\n"); + printf_filtered (" When we speak of free software, we are referring to freedom, not\n"); + printf_filtered ("price. Specifically, the General Public License is designed to make\n"); + printf_filtered ("sure that you have the freedom to give away or sell copies of free\n"); + printf_filtered ("software, that you receive source code or can get it if you want it,\n"); + printf_filtered ("that you can change the software or use pieces of it in new free\n"); + printf_filtered ("programs; and that you know you can do these things.\n"); + printf_filtered ("\n"); + printf_filtered (" To protect your rights, we need to make restrictions that forbid\n"); + printf_filtered ("anyone to deny you these rights or to ask you to surrender the rights.\n"); + printf_filtered ("These restrictions translate to certain responsibilities for you if you\n"); + printf_filtered ("distribute copies of the software, or if you modify it.\n"); + printf_filtered ("\n"); + printf_filtered (" For example, if you distribute copies of a such a program, whether\n"); + printf_filtered ("gratis or for a fee, you must give the recipients all the rights that\n"); + printf_filtered ("you have. You must make sure that they, too, receive or can get the\n"); + printf_filtered ("source code. And you must tell them their rights.\n"); + printf_filtered ("\n"); + printf_filtered (" We protect your rights with two steps: (1) copyright the software, and\n"); + printf_filtered ("(2) offer you this license which gives you legal permission to copy,\n"); + printf_filtered ("distribute and/or modify the software.\n"); + printf_filtered ("\n"); + printf_filtered (" Also, for each author's protection and ours, we want to make certain\n"); + printf_filtered ("that everyone understands that there is no warranty for this free\n"); + printf_filtered ("software. If the software is modified by someone else and passed on, we\n"); + printf_filtered ("want its recipients to know that what they have is not the original, so\n"); + printf_filtered ("that any problems introduced by others will not reflect on the original\n"); + printf_filtered ("authors' reputations.\n"); + printf_filtered ("\n"); + printf_filtered (" The precise terms and conditions for copying, distribution and\n"); + printf_filtered ("modification follow.\n"); + printf_filtered (" \n"); + printf_filtered (" GNU GENERAL PUBLIC LICENSE\n"); + printf_filtered (" TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n"); + printf_filtered ("\n"); + printf_filtered (" 0. This License Agreement applies to any program or other work which\n"); + printf_filtered ("contains a notice placed by the copyright holder saying it may be\n"); + printf_filtered ("distributed under the terms of this General Public License. The\n"); + printf_filtered ("\"Program\", below, refers to any such program or work, and a \"work based\n"); + printf_filtered ("on the Program\" means either the Program or any work containing the\n"); + printf_filtered ("Program or a portion of it, either verbatim or with modifications. Each\n"); + printf_filtered ("licensee is addressed as \"you\".\n"); + printf_filtered ("\n"); + printf_filtered (" 1. You may copy and distribute verbatim copies of the Program's source\n"); + printf_filtered ("code as you receive it, in any medium, provided that you conspicuously and\n"); + printf_filtered ("appropriately publish on each copy an appropriate copyright notice and\n"); + printf_filtered ("disclaimer of warranty; keep intact all the notices that refer to this\n"); + printf_filtered ("General Public License and to the absence of any warranty; and give any\n"); + printf_filtered ("other recipients of the Program a copy of this General Public License\n"); + printf_filtered ("along with the Program. You may charge a fee for the physical act of\n"); + printf_filtered ("transferring a copy.\n"); + printf_filtered ("\n"); + printf_filtered (" 2. You may modify your copy or copies of the Program or any portion of\n"); + printf_filtered ("it, and copy and distribute such modifications under the terms of Paragraph\n"); + printf_filtered ("1 above, provided that you also do the following:\n"); + printf_filtered ("\n"); + printf_filtered (" a) cause the modified files to carry prominent notices stating that\n"); + printf_filtered (" you changed the files and the date of any change; and\n"); + printf_filtered ("\n"); + printf_filtered (" b) cause the whole of any work that you distribute or publish, that\n"); + printf_filtered (" in whole or in part contains the Program or any part thereof, either\n"); + printf_filtered (" with or without modifications, to be licensed at no charge to all\n"); + printf_filtered (" third parties under the terms of this General Public License (except\n"); + printf_filtered (" that you may choose to grant warranty protection to some or all\n"); + printf_filtered (" third parties, at your option).\n"); + printf_filtered ("\n"); + printf_filtered (" c) If the modified program normally reads commands interactively when\n"); + printf_filtered (" run, you must cause it, when started running for such interactive use\n"); + printf_filtered (" in the simplest and most usual way, to print or display an\n"); + printf_filtered (" announcement including an appropriate copyright notice and a notice\n"); + printf_filtered (" that there is no warranty (or else, saying that you provide a\n"); + printf_filtered (" warranty) and that users may redistribute the program under these\n"); + printf_filtered (" conditions, and telling the user how to view a copy of this General\n"); + printf_filtered (" Public License.\n"); + printf_filtered ("\n"); + printf_filtered (" d) You may charge a fee for the physical act of transferring a\n"); + printf_filtered (" copy, and you may at your option offer warranty protection in\n"); + printf_filtered (" exchange for a fee.\n"); + printf_filtered ("\n"); + printf_filtered ("Mere aggregation of another independent work with the Program (or its\n"); + printf_filtered ("derivative) on a volume of a storage or distribution medium does not bring\n"); + printf_filtered ("the other work under the scope of these terms.\n"); + printf_filtered (" \n"); + printf_filtered (" 3. You may copy and distribute the Program (or a portion or derivative of\n"); + printf_filtered ("it, under Paragraph 2) in object code or executable form under the terms of\n"); + printf_filtered ("Paragraphs 1 and 2 above provided that you also do one of the following:\n"); + printf_filtered ("\n"); + printf_filtered (" a) accompany it with the complete corresponding machine-readable\n"); + printf_filtered (" source code, which must be distributed under the terms of\n"); + printf_filtered (" Paragraphs 1 and 2 above; or,\n"); + printf_filtered ("\n"); + printf_filtered (" b) accompany it with a written offer, valid for at least three\n"); + printf_filtered (" years, to give any third party free (except for a nominal charge\n"); + printf_filtered (" for the cost of distribution) a complete machine-readable copy of the\n"); + printf_filtered (" corresponding source code, to be distributed under the terms of\n"); + printf_filtered (" Paragraphs 1 and 2 above; or,\n"); + printf_filtered ("\n"); + printf_filtered (" c) accompany it with the information you received as to where the\n"); + printf_filtered (" corresponding source code may be obtained. (This alternative is\n"); + printf_filtered (" allowed only for noncommercial distribution and only if you\n"); + printf_filtered (" received the program in object code or executable form alone.)\n"); + printf_filtered ("\n"); + printf_filtered ("Source code for a work means the preferred form of the work for making\n"); + printf_filtered ("modifications to it. For an executable file, complete source code means\n"); + printf_filtered ("all the source code for all modules it contains; but, as a special\n"); + printf_filtered ("exception, it need not include source code for modules which are standard\n"); + printf_filtered ("libraries that accompany the operating system on which the executable\n"); + printf_filtered ("file runs, or for standard header files or definitions files that\n"); + printf_filtered ("accompany that operating system.\n"); + printf_filtered ("\n"); + printf_filtered (" 4. You may not copy, modify, sublicense, distribute or transfer the\n"); + printf_filtered ("Program except as expressly provided under this General Public License.\n"); + printf_filtered ("Any attempt otherwise to copy, modify, sublicense, distribute or transfer\n"); + printf_filtered ("the Program is void, and will automatically terminate your rights to use\n"); + printf_filtered ("the Program under this License. However, parties who have received\n"); + printf_filtered ("copies, or rights to use copies, from you under this General Public\n"); + printf_filtered ("License will not have their licenses terminated so long as such parties\n"); + printf_filtered ("remain in full compliance.\n"); + printf_filtered ("\n"); + printf_filtered (" 5. By copying, distributing or modifying the Program (or any work based\n"); + printf_filtered ("on the Program) you indicate your acceptance of this license to do so,\n"); + printf_filtered ("and all its terms and conditions.\n"); + printf_filtered ("\n"); + printf_filtered (" 6. Each time you redistribute the Program (or any work based on the\n"); + printf_filtered ("Program), the recipient automatically receives a license from the original\n"); + printf_filtered ("licensor to copy, distribute or modify the Program subject to these\n"); + printf_filtered ("terms and conditions. You may not impose any further restrictions on the\n"); + printf_filtered ("recipients' exercise of the rights granted herein.\n"); + printf_filtered (" \n"); + printf_filtered (" 7. The Free Software Foundation may publish revised and/or new versions\n"); + printf_filtered ("of the General Public License from time to time. Such new versions will\n"); + printf_filtered ("be similar in spirit to the present version, but may differ in detail to\n"); + printf_filtered ("address new problems or concerns.\n"); + printf_filtered ("\n"); + printf_filtered ("Each version is given a distinguishing version number. If the Program\n"); + printf_filtered ("specifies a version number of the license which applies to it and \"any\n"); + printf_filtered ("later version\", you have the option of following the terms and conditions\n"); + printf_filtered ("either of that version or of any later version published by the Free\n"); + printf_filtered ("Software Foundation. If the Program does not specify a version number of\n"); + printf_filtered ("the license, you may choose any version ever published by the Free Software\n"); + printf_filtered ("Foundation.\n"); + printf_filtered ("\n"); + printf_filtered (" 8. If you wish to incorporate parts of the Program into other free\n"); + printf_filtered ("programs whose distribution conditions are different, write to the author\n"); + printf_filtered ("to ask for permission. For software which is copyrighted by the Free\n"); + printf_filtered ("Software Foundation, write to the Free Software Foundation; we sometimes\n"); + printf_filtered ("make exceptions for this. Our decision will be guided by the two goals\n"); + printf_filtered ("of preserving the free status of all derivatives of our free software and\n"); + printf_filtered ("of promoting the sharing and reuse of software generally.\n"); + printf_filtered ("\n"); + immediate_quit--; +} + +static void +warranty_info () +{ + immediate_quit++; + printf_filtered (" NO WARRANTY\n"); + printf_filtered ("\n"); + printf_filtered (" 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\n"); + printf_filtered ("FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN\n"); + printf_filtered ("OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\n"); + printf_filtered ("PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\n"); + printf_filtered ("OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n"); + printf_filtered ("MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS\n"); + printf_filtered ("TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE\n"); + printf_filtered ("PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\n"); + printf_filtered ("REPAIR OR CORRECTION.\n"); + printf_filtered ("\n"); + printf_filtered (" 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n"); + printf_filtered ("WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\n"); + printf_filtered ("REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\n"); + printf_filtered ("INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\n"); + printf_filtered ("OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\n"); + printf_filtered ("TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\n"); + printf_filtered ("YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\n"); + printf_filtered ("PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n"); + printf_filtered ("POSSIBILITY OF SUCH DAMAGES.\n"); + printf_filtered ("\n"); + immediate_quit--; +} + +void +_initialize_copying () +{ + add_info ("copying", copying_info, + "Conditions for redistributing copies of GDB."); + add_info ("warranty", warranty_info, + "Various kinds of warranty you do not have."); +} diff --git a/gnu/usr.bin/gdb/core.c b/gnu/usr.bin/gdb/core.c new file mode 100644 index 0000000000..307addb54a --- /dev/null +++ b/gnu/usr.bin/gdb/core.c @@ -0,0 +1,581 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)core.c 6.3 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Work with core dump and executable files, for GDB. + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include "defs.h" +#include "param.h" +#include "frame.h" /* required by inferior.h */ +#include "inferior.h" + +#ifdef USG +#include +#include +#endif + +#ifdef COFF_ENCAPSULATE +#include "a.out.encap.h" +#else +#include +#endif +#ifndef N_MAGIC +#ifdef COFF_FORMAT +#define N_MAGIC(exec) ((exec).magic) +#else +#define N_MAGIC(exec) ((exec).a_magic) +#endif +#endif +#include +#include +#include +#include +#include + +#ifdef UMAX_CORE +#include +#else +#include +#endif + +#ifndef N_TXTADDR +#define N_TXTADDR(hdr) 0 +#endif /* no N_TXTADDR */ + +#ifndef N_DATADDR +#define N_DATADDR(hdr) hdr.a_text +#endif /* no N_DATADDR */ + +#ifndef COFF_FORMAT +#ifndef AOUTHDR +#define AOUTHDR struct exec +#endif +#endif + +extern char *sys_siglist[]; + +extern core_file_command (), exec_file_command (); + +/* Hook for `exec_file_command' command to call. */ + +void (*exec_file_display_hook) (); + +/* File names of core file and executable file. */ + +char *corefile; +char *execfile; + +/* Descriptors on which core file and executable file are open. + Note that the execchan is closed when an inferior is created + and reopened if the inferior dies or is killed. */ + +int corechan; +int execchan; + +/* Last modification time of executable file. + Also used in source.c to compare against mtime of a source file. */ + +int exec_mtime; + +/* Virtual addresses of bounds of the two areas of memory in the core file. */ + +CORE_ADDR data_start; +CORE_ADDR data_end; +CORE_ADDR stack_start; +CORE_ADDR stack_end; + +#if defined (REG_STACK_SEGMENT) +/* Start and end of the register stack segment. */ +CORE_ADDR reg_stack_start; +CORE_ADDR reg_stack_end; +#endif /* REG_STACK_SEGMENT */ + +/* Virtual addresses of bounds of two areas of memory in the exec file. + Note that the data area in the exec file is used only when there is no core file. */ + +CORE_ADDR text_start; +CORE_ADDR text_end; + +CORE_ADDR exec_data_start; +CORE_ADDR exec_data_end; + +/* Offset within executable file of start of text area data. */ + +int text_offset; + +/* Offset within executable file of start of data area data. */ + +int exec_data_offset; + +/* Offset within core file of start of data area data. */ + +int data_offset; + +/* Offset within core file of start of stack area data. */ + +int stack_offset; + +#ifdef COFF_FORMAT +/* various coff data structures */ + +FILHDR file_hdr; +SCNHDR text_hdr; +SCNHDR data_hdr; + +#endif /* not COFF_FORMAT */ + +/* a.out header saved in core file. */ + +AOUTHDR core_aouthdr; + +/* a.out header of exec file. */ + +AOUTHDR exec_aouthdr; + +void validate_files (); +unsigned int register_addr (); + +/* Call this to specify the hook for exec_file_command to call back. + This is called from the x-window display code. */ + +void +specify_exec_file_hook (hook) + void (*hook) (); +{ + exec_file_display_hook = hook; +} + +/* The exec file must be closed before running an inferior. + If it is needed again after the inferior dies, it must + be reopened. */ + +void +close_exec_file () +{ + if (execchan >= 0) + close (execchan); + execchan = -1; +} + +void +reopen_exec_file () +{ + if (execchan < 0 && execfile != 0) + { + char *filename = concat (execfile, "", ""); + exec_file_command (filename, 0); + free (filename); + } +} + +/* If we have both a core file and an exec file, + print a warning if they don't go together. + This should really check that the core file came + from that exec file, but I don't know how to do it. */ + +void +validate_files () +{ + if (execfile != 0 && corefile != 0) + { + struct stat st_core; + + if (fstat (corechan, &st_core) < 0) + /* It might be a good idea to print an error message. + On the other hand, if the user tries to *do* anything with + the core file, (s)he'll find out soon enough. */ + return; + + if (N_MAGIC (core_aouthdr) != 0 + && bcmp (&core_aouthdr, &exec_aouthdr, sizeof core_aouthdr)) + printf ("Warning: core file does not match specified executable file.\n"); + else if (exec_mtime > st_core.st_mtime) { +#ifdef KERNELDEBUG + extern int kernel_debugging; + if (!kernel_debugging) +#endif + printf ("Warning: exec file is newer than core file.\n"); + } + } +} + +/* Return the name of the executable file as a string. + ERR nonzero means get error if there is none specified; + otherwise return 0 in that case. */ + +char * +get_exec_file (err) + int err; +{ + if (err && execfile == 0) + error ("No executable file specified.\n\ +Use the \"exec-file\" and \"symbol-file\" commands."); + return execfile; +} + +int +have_core_file_p () +{ + return corefile != 0; +} + +static void +files_info () +{ + char *symfile; + extern char *get_sym_file (); + + if (execfile) + printf ("Executable file \"%s\".\n", execfile); + else + printf ("No executable file\n"); + if (corefile == 0) + printf ("No core dump file\n"); + else + printf ("Core dump file \"%s\".\n", corefile); + + if (have_inferior_p ()) + printf ("Using the running image of the program, rather than these files.\n"); + + symfile = get_sym_file (); + if (symfile != 0) + printf ("Symbols from \"%s\".\n", symfile); + +#ifdef FILES_INFO_HOOK + if (FILES_INFO_HOOK ()) + return; +#endif + + if (! have_inferior_p ()) + { + if (execfile) + { + printf ("Text segment in executable from 0x%x to 0x%x.\n", + text_start, text_end); + printf ("Data segment in executable from 0x%x to 0x%x.\n", + exec_data_start, exec_data_end); + if (corefile) + printf ("(But since we have a core file, we're using...)\n"); + } + if (corefile) + { + printf ("Data segment in core file from 0x%x to 0x%x.\n", + data_start, data_end); + printf ("Stack segment in core file from 0x%x to 0x%x.\n", + stack_start, stack_end); + } + } +} + +/* Read "memory data" from core file and/or executable file. + Returns zero if successful, 1 if xfer_core_file failed, errno value if + ptrace failed. */ + +int +read_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + if (len == 0) + return 0; + + if (have_inferior_p ()) + { + if (remote_debugging) + return remote_read_inferior_memory (memaddr, myaddr, len); + else + return read_inferior_memory (memaddr, myaddr, len); + } + else + return xfer_core_file (memaddr, myaddr, len); +} + +/* Write LEN bytes of data starting at address MYADDR + into debugged program memory at address MEMADDR. + Returns zero if successful, or an errno value if ptrace failed. */ + +int +write_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + if (have_inferior_p ()) + { + if (remote_debugging) + return remote_write_inferior_memory (memaddr, myaddr, len); + else + return write_inferior_memory (memaddr, myaddr, len); + } + else + error ("Can write memory only when program being debugged is running."); +} + +#ifndef XFER_CORE_FILE +int (*core_file_hook)(); /* hook to handle special core files like + like /dev/mem and crash dumps */ + +/* Read from the program's memory (except for inferior processes). + This function is misnamed, since it only reads, never writes; and + since it will use the core file and/or executable file as necessary. + + It should be extended to write as well as read, FIXME, for patching files. + + Return 0 if address could be read, 1 if not. */ + +int +xfer_core_file (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + register int val; + int xferchan; + char **xferfile; + int fileptr; + int returnval = 0; + + if (core_file_hook) + return ((*core_file_hook)(memaddr, myaddr, len)); + + while (len > 0) + { + xferfile = 0; + xferchan = 0; + + /* Determine which file the next bunch of addresses reside in, + and where in the file. Set the file's read/write pointer + to point at the proper place for the desired address + and set xferfile and xferchan for the correct file. + + If desired address is nonexistent, leave them zero. + + i is set to the number of bytes that can be handled + along with the next address. + + We put the most likely tests first for efficiency. */ + + /* Note that if there is no core file + data_start and data_end are equal. */ + if (memaddr >= data_start && memaddr < data_end) + { + i = min (len, data_end - memaddr); + fileptr = memaddr - data_start + data_offset; + xferfile = &corefile; + xferchan = corechan; + } + /* Note that if there is no core file + stack_start and stack_end are equal. */ + else if (memaddr >= stack_start && memaddr < stack_end) + { + i = min (len, stack_end - memaddr); + fileptr = memaddr - stack_start + stack_offset; + xferfile = &corefile; + xferchan = corechan; + } +#ifdef REG_STACK_SEGMENT + /* Pyramids have an extra segment in the virtual address space + for the (control) stack of register-window frames */ + else if (memaddr >= reg_stack_start && memaddr < reg_stack_end) + { + i = min (len, reg_stack_end - memaddr); + fileptr = memaddr - reg_stack_start + reg_stack_offset; + xferfile = &corefile; + xferchan = corechan; + } +#endif /* REG_STACK_SEGMENT */ + + else if (corechan < 0 + && memaddr >= exec_data_start && memaddr < exec_data_end) + { + i = min (len, exec_data_end - memaddr); + fileptr = memaddr - exec_data_start + exec_data_offset; + xferfile = &execfile; + xferchan = execchan; + } + else if (memaddr >= text_start && memaddr < text_end) + { + i = min (len, text_end - memaddr); + fileptr = memaddr - text_start + text_offset; + xferfile = &execfile; + xferchan = execchan; + } + else if (memaddr < text_start) + { + i = min (len, text_start - memaddr); + } + else if (memaddr >= text_end + && memaddr < (corechan >= 0? data_start : exec_data_start)) + { + i = min (len, data_start - memaddr); + } + else if (corechan >= 0 + && memaddr >= data_end && memaddr < stack_start) + { + i = min (len, stack_start - memaddr); + } + else if (corechan < 0 && memaddr >= exec_data_end) + { + /* Since there is nothing at higher addresses than data + (without a core file or an inferior, there is no + stack, set i to do the rest of the operation now. */ + i = len; + } +#ifdef REG_STACK_SEGMENT + else if (memaddr >= reg_stack_end && reg_stack_end != 0) + { + i = min (len, reg_stack_start - memaddr); + } + else if (memaddr >= stack_end && memaddr < reg_stack_start) +#else /* no REG_STACK_SEGMENT. */ + else if (memaddr >= stack_end && stack_end != 0) +#endif /* no REG_STACK_SEGMENT. */ + { + /* Since there is nothing at higher addresses than + the stack, set i to do the rest of the operation now. */ + i = len; + } + else + { + /* Address did not classify into one of the known ranges. + This shouldn't happen; we catch the endpoints. */ + fatal ("Internal: Bad case logic in xfer_core_file."); + } + + /* Now we know which file to use. + Set up its pointer and transfer the data. */ + if (xferfile) + { + if (*xferfile == 0) + if (xferfile == &execfile) + error ("No program file to examine."); + else + error ("No core dump file or running program to examine."); + val = lseek (xferchan, fileptr, 0); + if (val == -1) + perror_with_name (*xferfile); + val = myread (xferchan, myaddr, i); + if (val < 0) + perror_with_name (*xferfile); + } + /* If this address is for nonexistent memory, + read zeros if reading, or do nothing if writing. + Actually, we never right. */ + else + { + bzero (myaddr, i); + returnval = 1; + } + + memaddr += i; + myaddr += i; + len -= i; + } + return returnval; +} +#endif /* XFER_CORE_FILE */ + +/* My replacement for the read system call. + Used like `read' but keeps going if `read' returns too soon. */ + +int +myread (desc, addr, len) + int desc; + char *addr; + int len; +{ + register int val; + int orglen = len; + + while (len > 0) + { + val = read (desc, addr, len); + if (val < 0) + return val; + if (val == 0) + return orglen - len; + len -= val; + addr += val; + } + return orglen; +} + +#ifdef REGISTER_U_ADDR + +/* Return the address in the core dump or inferior of register REGNO. + BLOCKEND is the address of the end of the user structure. */ + +unsigned int +register_addr (regno, blockend) + int regno; + int blockend; +{ + int addr; + + if (regno < 0 || regno >= NUM_REGS) + error ("Invalid register number %d.", regno); + + REGISTER_U_ADDR (addr, blockend, regno); + + return addr; +} + +#endif /* REGISTER_U_ADDR */ + +void +_initialize_core() +{ + corechan = -1; + execchan = -1; + corefile = 0; + execfile = 0; + exec_file_display_hook = 0; + + text_start = 0; + text_end = 0; + data_start = 0; + data_end = 0; + exec_data_start = 0; + exec_data_end = 0; + stack_start = STACK_END_ADDR; + stack_end = STACK_END_ADDR; + + add_com ("core-file", class_files, core_file_command, + "Use FILE as core dump for examining memory and registers.\n\ +No arg means have no core file."); + add_com ("exec-file", class_files, exec_file_command, + "Use FILE as program for getting contents of pure memory.\n\ +If FILE cannot be found as specified, your execution directory path\n\ +is searched for a command of that name.\n\ +No arg means have no executable file."); + add_info ("files", files_info, "Names of files being debugged."); +} + diff --git a/gnu/usr.bin/gdb/cplus-dem.c b/gnu/usr.bin/gdb/cplus-dem.c new file mode 100644 index 0000000000..8ea9c8bb55 --- /dev/null +++ b/gnu/usr.bin/gdb/cplus-dem.c @@ -0,0 +1,996 @@ +/* Demangler for GNU C++ + Copyright (C) 1989 Free Software Foundation, Inc. + written by James Clark (jjc@jclark.uucp) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* This is for g++ 1.36.1 (November 6 version). It will probably + require changes for any other version. + + Modified for g++ 1.36.2 (November 18 version). */ + +/* This file exports one function + + char *cplus_demangle (const char *name, int mode) + + If NAME is a mangled function name produced by GNU C++, then + a pointer to a malloced string giving a C++ representation + of the name will be returned; otherwise NULL will be returned. + It is the caller's responsibility to free the string which + is returned. + + If MODE > 0, then ANSI qualifiers such as `const' and `void' are output. + Otherwise they are not. + If MODE >= 0, parameters are emitted; otherwise not. + + For example, + + cplus_demangle ("foo__1Ai", 0) => "A::foo(int)" + cplus_demangle ("foo__1Ai", 1) => "A::foo(int)" + cplus_demangle ("foo__1Ai", -1) => "A::foo" + + cplus_demangle ("foo__1Afe", 0) => "A::foo(float,...)" + cplus_demangle ("foo__1Afe", 1) => "A::foo(float,...)" + cplus_demangle ("foo__1Afe", -1) => "A::foo" + + This file imports xmalloc and xrealloc, which are like malloc and + realloc except that they generate a fatal error if there is no + available memory. */ + +/* #define nounderscore 1 /* define this is names don't start with _ */ + +#include +#include + +#ifdef USG +#include +#include +#else +#include +#define memcpy(s1, s2, n) bcopy ((s2), (s1), (n)) +#define memcmp(s1, s2, n) bcmp ((s2), (s1), (n)) +#define strchr index +#define strrchr rindex +#endif + +#ifndef __STDC__ +#define const +#endif + +#ifdef __STDC__ +extern char *cplus_demangle (const char *type, int mode); +#else +extern char *cplus_demangle (); +#endif + +#ifdef __STDC__ +extern char *xmalloc (int); +extern char *xrealloc (char *, int); +#else +extern char *xmalloc (); +extern char *xrealloc (); +#endif + +static char **typevec = 0; +static int ntypes = 0; +static int typevec_size = 0; + +static struct { + const char *in; + const char *out; +} optable[] = { + "new", " new", + "delete", " delete", + "ne", "!=", + "eq", "==", + "ge", ">=", + "gt", ">", + "le", "<=", + "lt", "<", + "plus", "+", + "minus", "-", + "mult", "*", + "convert", "+", /* unary + */ + "negate", "-", /* unary - */ + "trunc_mod", "%", + "trunc_div", "/", + "truth_andif", "&&", + "truth_orif", "||", + "truth_not", "!", + "postincrement", "++", + "postdecrement", "--", + "bit_ior", "|", + "bit_xor", "^", + "bit_and", "&", + "bit_not", "~", + "call", "()", + "cond", "?:", + "alshift", "<<", + "arshift", ">>", + "component", "->", + "indirect", "*", + "method_call", "->()", + "addr", "&", /* unary & */ + "array", "[]", + "nop", "", /* for operator= */ +}; + +/* Beware: these aren't '\0' terminated. */ + +typedef struct { + char *b; /* pointer to start of string */ + char *p; /* pointer after last character */ + char *e; /* pointer after end of allocated space */ +} string; + +#ifdef __STDC__ +static void string_need (string *s, int n); +static void string_delete (string *s); +static void string_init (string *s); +static void string_clear (string *s); +static int string_empty (string *s); +static void string_append (string *p, const char *s); +static void string_appends (string *p, string *s); +static void string_appendn (string *p, const char *s, int n); +static void string_prepend (string *p, const char *s); +#if 0 +static void string_prepends (string *p, string *s); +#endif +static void string_prependn (string *p, const char *s, int n); +static int get_count (const char **type, int *count); +static int do_args (const char **type, string *decl, int arg_mode); +static int do_type (const char **type, string *result, int arg_mode); +static int do_arg (const char **type, string *result, int arg_mode); +static void munge_function_name (string *name, int arg_mode); +static void remember_type (const char *type, int len); +#else +static void string_need (); +static void string_delete (); +static void string_init (); +static void string_clear (); +static int string_empty (); +static void string_append (); +static void string_appends (); +static void string_appendn (); +static void string_prepend (); +static void string_prepends (); +static void string_prependn (); +static int get_count (); +static int do_args (); +static int do_type (); +static int do_arg (); +static int do_args (); +static void munge_function_name (); +static void remember_type (); +#endif + +char * +cplus_demangle (type, arg_mode) + const char *type; + int arg_mode; +{ + string decl; + int n; + int success = 0; + int constructor = 0; + int const_flag = 0; + int i; + const char *p; +#ifndef LONGERNAMES + const char *premangle; +#endif + +# define print_ansi_qualifiers (arg_mode > 0) +# define print_arg_types (arg_mode >= 0) + + if (type == NULL || *type == '\0') + return NULL; +#ifndef nounderscore + if (*type++ != '_') + return NULL; +#endif + p = type; + while (*p != '\0' && !(*p == '_' && p[1] == '_')) + p++; + if (*p == '\0') + { + /* destructor */ + if (type[0] == '_' && type[1] == '$' && type[2] == '_') + { + int n = (strlen (type) - 3)*2 + 3 + 2 + 1; + char *tem = (char *) xmalloc (n); + strcpy (tem, type + 3); + strcat (tem, "::~"); + strcat (tem, type + 3); + strcat (tem, "()"); + return tem; + } + /* static data member */ + if (*type != '_' && (p = strchr (type, '$')) != NULL) + { + int n = strlen (type) + 2; + char *tem = (char *) xmalloc (n); + memcpy (tem, type, p - type); + strcpy (tem + (p - type), "::"); + strcpy (tem + (p - type) + 2, p + 1); + return tem; + } + /* virtual table "_vt$" */ + if (type[0] == '_' && type[1] == 'v' && type[2] == 't' && type[3] == '$') + { + int n = strlen (type + 4) + 14 + 1; + char *tem = (char *) xmalloc (n); + strcpy (tem, type + 4); + strcat (tem, " virtual table"); + return tem; + } + return NULL; + } + + string_init (&decl); + + if (p == type) + { + if (!isdigit (p[2])) + { + string_delete (&decl); + return NULL; + } + constructor = 1; + } + else + { + string_appendn (&decl, type, p - type); + munge_function_name (&decl, arg_mode); + } + p += 2; + +#ifndef LONGERNAMES + premangle = p; +#endif + switch (*p) + { + case 'C': + /* a const member function */ + if (!isdigit (p[1])) + { + string_delete (&decl); + return NULL; + } + p += 1; + const_flag = 1; + /* fall through */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + n = 0; + do + { + n *= 10; + n += *p - '0'; + p += 1; + } + while (isdigit (*p)); + if (strlen (p) < n) + { + string_delete (&decl); + return NULL; + } + if (constructor) + { + string_appendn (&decl, p, n); + string_append (&decl, "::"); + string_appendn (&decl, p, n); + } + else + { + string_prepend (&decl, "::"); + string_prependn (&decl, p, n); + } + p += n; +#ifndef LONGERNAMES + remember_type (premangle, p - premangle); +#endif + success = do_args (&p, &decl, arg_mode); + if (const_flag && print_arg_types) + string_append (&decl, " const"); + break; + case 'F': + p += 1; + success = do_args (&p, &decl, arg_mode); + break; + } + + for (i = 0; i < ntypes; i++) + if (typevec[i] != NULL) + free (typevec[i]); + ntypes = 0; + if (typevec != NULL) + { + free ((char *)typevec); + typevec = NULL; + typevec_size = 0; + } + + if (success) + { + string_appendn (&decl, "", 1); + return decl.b; + } + else + { + string_delete (&decl); + return NULL; + } +} + +static int +get_count (type, count) + const char **type; + int *count; +{ + if (!isdigit (**type)) + return 0; + *count = **type - '0'; + *type += 1; + /* see flush_repeats in cplus-method.c */ + if (isdigit (**type)) + { + const char *p = *type; + int n = *count; + do + { + n *= 10; + n += *p - '0'; + p += 1; + } + while (isdigit (*p)); + if (*p == '_') + { + *type = p + 1; + *count = n; + } + } + return 1; +} + +/* result will be initialised here; it will be freed on failure */ + +static int +do_type (type, result, arg_mode) + const char **type; + string *result; + int arg_mode; +{ + int n; + int done; + int non_empty = 0; + int success; + string decl; + const char *remembered_type; + + string_init (&decl); + string_init (result); + + done = 0; + success = 1; + while (success && !done) + { + int member; + switch (**type) + { + case 'P': + *type += 1; + string_prepend (&decl, "*"); + break; + + case 'R': + *type += 1; + string_prepend (&decl, "&"); + break; + + case 'T': + *type += 1; + if (!get_count (type, &n) || n >= ntypes) + success = 0; + else + { + remembered_type = typevec[n]; + type = &remembered_type; + } + break; + + case 'F': + *type += 1; + if (!string_empty (&decl) && decl.b[0] == '*') + { + string_prepend (&decl, "("); + string_append (&decl, ")"); + } + if (!do_args (type, &decl, arg_mode) || **type != '_') + success = 0; + else + *type += 1; + break; + + case 'M': + case 'O': + { + int constp = 0; + int volatilep = 0; + + member = **type == 'M'; + *type += 1; + if (!isdigit (**type)) + { + success = 0; + break; + } + n = 0; + do + { + n *= 10; + n += **type - '0'; + *type += 1; + } + while (isdigit (**type)); + if (strlen (*type) < n) + { + success = 0; + break; + } + string_append (&decl, ")"); + string_prepend (&decl, "::"); + string_prependn (&decl, *type, n); + string_prepend (&decl, "("); + *type += n; + if (member) + { + if (**type == 'C') + { + *type += 1; + constp = 1; + } + if (**type == 'V') + { + *type += 1; + volatilep = 1; + } + if (*(*type)++ != 'F') + { + success = 0; + break; + } + } + if ((member && !do_args (type, &decl, arg_mode)) || **type != '_') + { + success = 0; + break; + } + *type += 1; + if (! print_ansi_qualifiers) + break; + if (constp) + { + if (non_empty) + string_append (&decl, " "); + else + non_empty = 1; + string_append (&decl, "const"); + } + if (volatilep) + { + if (non_empty) + string_append (&decl, " "); + else + non_empty = 1; + string_append (&decl, "volatile"); + } + break; + } + + case 'C': + if ((*type)[1] == 'P') + { + *type += 1; + if (print_ansi_qualifiers) + { + if (!string_empty (&decl)) + string_prepend (&decl, " "); + string_prepend (&decl, "const"); + } + break; + } + + /* fall through */ + default: + done = 1; + break; + } + } + + done = 0; + non_empty = 0; + while (success && !done) + { + switch (**type) + { + case 'C': + *type += 1; + if (print_ansi_qualifiers) + { + if (non_empty) + string_append (result, " "); + else + non_empty = 1; + string_append (result, "const"); + } + break; + case 'U': + *type += 1; + if (non_empty) + string_append (result, " "); + else + non_empty = 1; + string_append (result, "unsigned"); + break; + case 'V': + *type += 1; + if (print_ansi_qualifiers) + { + if (non_empty) + string_append (result, " "); + else + non_empty = 1; + string_append (result, "volatile"); + } + break; + default: + done = 1; + break; + } + } + + if (success) + switch (**type) + { + case '\0': + case '_': + break; + case 'v': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "void"); + break; + case 'x': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "long long"); + break; + case 'l': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "long"); + break; + case 'i': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "int"); + break; + case 's': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "short"); + break; + case 'c': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "char"); + break; + case 'r': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "long double"); + break; + case 'd': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "double"); + break; + case 'f': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "float"); + break; + case 'G': + *type += 1; + if (!isdigit (**type)) + { + success = 0; + break; + } + /* fall through */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + n = 0; + do + { + n *= 10; + n += **type - '0'; + *type += 1; + } + while (isdigit (**type)); + if (strlen (*type) < n) + { + success = 0; + break; + } + if (non_empty) + string_append (result, " "); + string_appendn (result, *type, n); + *type += n; + break; + default: + success = 0; + break; + } + + if (success) + { + if (!string_empty (&decl)) + { + string_append (result, " "); + string_appends (result, &decl); + } + string_delete (&decl); + return 1; + } + else + { + string_delete (&decl); + string_delete (result); + return 0; + } +} + +/* `result' will be initialised in do_type; it will be freed on failure */ + +static int +do_arg (type, result, arg_mode) + const char **type; + string *result; + int arg_mode; +{ + const char *start = *type; + + if (!do_type (type, result, arg_mode)) + return 0; + remember_type (start, *type - start); + return 1; +} + +static void +remember_type (start, len) + const char *start; + int len; +{ + char *tem; + + if (ntypes >= typevec_size) + { + if (typevec_size == 0) + { + typevec_size = 3; + typevec = (char **) xmalloc (sizeof (char*)*typevec_size); + } + else + { + typevec_size *= 2; + typevec = (char **) xrealloc ((char *)typevec, sizeof (char*)*typevec_size); + } + } + tem = (char *) xmalloc (len + 1); + memcpy (tem, start, len); + tem[len] = '\0'; + typevec[ntypes++] = tem; +} + +/* `decl' must be already initialised, usually non-empty; + it won't be freed on failure */ + +static int +do_args (type, decl, arg_mode) + const char **type; + string *decl; + int arg_mode; +{ + string arg; + int need_comma = 0; + + if (print_arg_types) + string_append (decl, "("); + + while (**type != '_' && **type != '\0' && **type != 'e' && **type != 'v') + { + if (**type == 'N') + { + int r; + int t; + *type += 1; + if (!get_count (type, &r) || !get_count (type, &t) || t >= ntypes) + return 0; + while (--r >= 0) + { + const char *tem = typevec[t]; + if (need_comma && print_arg_types) + string_append (decl, ", "); + if (!do_arg (&tem, &arg, arg_mode)) + return 0; + if (print_arg_types) + string_appends (decl, &arg); + string_delete (&arg); + need_comma = 1; + } + } + else + { + if (need_comma & print_arg_types) + string_append (decl, ", "); + if (!do_arg (type, &arg, arg_mode)) + return 0; + if (print_arg_types) + string_appends (decl, &arg); + string_delete (&arg); + need_comma = 1; + } + } + + if (**type == 'v') + *type += 1; + else if (**type == 'e') + { + *type += 1; + if (print_arg_types) + { + if (need_comma) + string_append (decl, ","); + string_append (decl, "..."); + } + } + + if (print_arg_types) + string_append (decl, ")"); + return 1; +} + +static void +munge_function_name (name, arg_mode) + string *name; + int arg_mode; +{ + if (!string_empty (name) && name->p - name->b >= 3 + && name->b[0] == 'o' && name->b[1] == 'p' && name->b[2] == '$') + { + int i; + /* see if it's an assignment expression */ + if (name->p - name->b >= 10 /* op$assign_ */ + && memcmp (name->b + 3, "assign_", 7) == 0) + { + for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++) + { + int len = name->p - name->b - 10; + if (strlen (optable[i].in) == len + && memcmp (optable[i].in, name->b + 10, len) == 0) + { + string_clear (name); + string_append (name, "operator"); + string_append (name, optable[i].out); + string_append (name, "="); + return; + } + } + } + else + { + for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++) + { + int len = name->p - name->b - 3; + if (strlen (optable[i].in) == len + && memcmp (optable[i].in, name->b + 3, len) == 0) + { + string_clear (name); + string_append (name, "operator"); + string_append (name, optable[i].out); + return; + } + } + } + return; + } + else if (!string_empty (name) && name->p - name->b >= 5 + && memcmp (name->b, "type$", 5) == 0) + { + /* type conversion operator */ + string type; + const char *tem = name->b + 5; + if (do_type (&tem, &type, arg_mode)) + { + string_clear (name); + string_append (name, "operator "); + string_appends (name, &type); + string_delete (&type); + return; + } + } +} + +/* a mini string-handling package */ + +static void +string_need (s, n) + string *s; + int n; +{ + if (s->b == NULL) + { + if (n < 32) + n = 32; + s->p = s->b = (char *) xmalloc (n); + s->e = s->b + n; + } + else if (s->e - s->p < n) + { + int tem = s->p - s->b; + n += tem; + n *= 2; + s->b = (char *) xrealloc (s->b, n); + s->p = s->b + tem; + s->e = s->b + n; + } +} + +static void +string_delete (s) + string *s; +{ + if (s->b != NULL) + { + free (s->b); + s->b = s->e = s->p = NULL; + } +} + +static void +string_init (s) + string *s; +{ + s->b = s->p = s->e = NULL; +} + +static void +string_clear (s) + string *s; +{ + s->p = s->b; +} + +static int +string_empty (s) + string *s; +{ + return s->b == s->p; +} + +static void +string_append (p, s) + string *p; + const char *s; +{ + int n; + if (s == NULL || *s == '\0') + return; + n = strlen (s); + string_need (p, n); + memcpy (p->p, s, n); + p->p += n; +} + +static void +string_appends (p, s) + string *p, *s; +{ + int n; + if (s->b == s->p) + return; + n = s->p - s->b; + string_need (p, n); + memcpy (p->p, s->b, n); + p->p += n; +} + +static void +string_appendn (p, s, n) + string *p; + const char *s; + int n; +{ + if (n == 0) + return; + string_need (p, n); + memcpy (p->p, s, n); + p->p += n; +} + +static void +string_prepend (p, s) + string *p; + const char *s; +{ + if (s == NULL || *s == '\0') + return; + string_prependn (p, s, strlen (s)); +} + +#if 0 +static void +string_prepends (p, s) + string *p, *s; +{ + if (s->b == s->p) + return; + string_prependn (p, s->b, s->p - s->b); +} +#endif + +static void +string_prependn (p, s, n) + string *p; + const char *s; + int n; +{ + char *q; + + if (n == 0) + return; + string_need (p, n); + for (q = p->p - 1; q >= p->b; q--) + q[n] = q[0]; + memcpy (p->b, s, n); + p->p += n; +} diff --git a/gnu/usr.bin/gdb/dbxread.c b/gnu/usr.bin/gdb/dbxread.c new file mode 100644 index 0000000000..7a25665536 --- /dev/null +++ b/gnu/usr.bin/gdb/dbxread.c @@ -0,0 +1,5727 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)dbxread.c 6.3 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Read dbx symbol tables and convert to internal format, for GDB. + Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Symbol read-in occurs in two phases: + 1. A scan (read_dbx_symtab()) of the entire executable, whose sole + purpose is to make a list of symbols (partial symbol table) + which will cause symbols + to be read in if referenced. This scan happens when the + "symbol-file" command is given (symbol_file_command()). + 2. Full read-in of symbols. (psymtab_to_symtab()). This happens + when a symbol in a file for which symbols have not yet been + read in is referenced. + 2a. The "add-file" command. Similar to #2. */ + +#include +#include "defs.h" +#include "param.h" + +#ifdef READ_DBX_FORMAT + +#ifdef USG +#include +#include +#define L_SET 0 +#define L_INCR 1 +#endif + +#ifdef COFF_ENCAPSULATE +#include "a.out.encap.h" +#include "stab.gnu.h" +#else +#include +#include +#endif +#include + +#ifndef NO_GNU_STABS +/* + * Define specifically gnu symbols here. + */ + +/* The following type indicates the definition of a symbol as being + an indirect reference to another symbol. The other symbol + appears as an undefined reference, immediately following this symbol. + + Indirection is asymmetrical. The other symbol's value will be used + to satisfy requests for the indirect symbol, but not vice versa. + If the other symbol does not have a definition, libraries will + be searched to find a definition. */ +#ifndef N_INDR +#define N_INDR 0xa +#endif + +/* The following symbols refer to set elements. + All the N_SET[ATDB] symbols with the same name form one set. + Space is allocated for the set in the text section, and each set + element's value is stored into one word of the space. + The first word of the space is the length of the set (number of elements). + + The address of the set is made into an N_SETV symbol + whose name is the same as the name of the set. + This symbol acts like a N_DATA global symbol + in that it can satisfy undefined external references. */ + +#ifndef N_SETA +#define N_SETA 0x14 /* Absolute set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETT +#define N_SETT 0x16 /* Text set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETD +#define N_SETD 0x18 /* Data set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETB +#define N_SETB 0x1A /* Bss set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +/* Macros dealing with the set element symbols defined in a.out.h */ +#define SET_ELEMENT_P(x) ((x)>=N_SETA&&(x)<=(N_SETB|N_EXT)) +#define TYPE_OF_SET_ELEMENT(x) ((x)-N_SETA+N_ABS) + +#ifndef N_SETV +#define N_SETV 0x1C /* Pointer to set vector in data area. */ +#endif /* This is output from LD. */ + +#ifndef N_WARNING +#define N_WARNING 0x1E /* Warning message to print if file included */ +#endif /* This is input to ld */ + +#ifndef __GNU_STAB__ + +/* Line number for the data section. This is to be used to describe + the source location of a variable declaration. */ +#ifndef N_DSLINE +#define N_DSLINE (N_SLINE+N_DATA-N_TEXT) +#endif + +/* Line number for the bss section. This is to be used to describe + the source location of a variable declaration. */ +#ifndef N_BSLINE +#define N_BSLINE (N_SLINE+N_BSS-N_TEXT) +#endif + +#endif /* not __GNU_STAB__ */ +#endif /* NO_GNU_STABS */ + +#include +#include +#include +#include + +#include "symtab.h" + +#ifndef COFF_FORMAT +#ifndef AOUTHDR +#define AOUTHDR struct exec +#endif +#endif + +static void add_symbol_to_list (); +static void read_dbx_symtab (); +static void process_one_symbol (); +static void free_all_psymbols (); +static struct type *read_type (); +static struct type *read_range_type (); +static struct type *read_enum_type (); +static struct type *read_struct_type (); +static struct type *read_array_type (); +static long read_number (); +static void read_huge_number (); +static void finish_block (); +static struct blockvector *make_blockvector (); +static struct symbol *define_symbol (); +static void start_subfile (); +static int hashname (); +static void hash_symsegs (); +static struct pending *copy_pending (); +static void fix_common_block (); + +static void add_undefined_type (); +static void cleanup_undefined_types (); + +extern char *index(); + +extern struct symtab *read_symsegs (); +extern void free_all_symtabs (); +extern void free_all_psymtabs (); +extern void free_inclink_symtabs (); + +/* C++ */ +static struct type **read_args (); + +/* Macro to determine which symbols to ignore when reading the first symbol + of a file. Some machines override this definition. */ +#ifdef N_NSYMS +#ifndef IGNORE_SYMBOL +/* This code is used on Ultrix systems. Ignore it */ +#define IGNORE_SYMBOL(type) (type == N_NSYMS) +#endif +#else +#ifndef IGNORE_SYMBOL +/* Don't ignore any symbols. */ +#define IGNORE_SYMBOL(type) (0) +#endif +#endif /* not N_NSYMS */ + +/* Macro for number of symbol table entries (in usual a.out format). + Some machines override this definition. */ +#ifndef NUMBER_OF_SYMBOLS +#ifdef COFF_HEADER +#define NUMBER_OF_SYMBOLS \ + ((COFF_HEADER(hdr) ? hdr.coffhdr.filehdr.f_nsyms : hdr.a_syms) / \ + sizeof (struct nlist)) +#else +#define NUMBER_OF_SYMBOLS (hdr.a_syms / sizeof (struct nlist)) +#endif +#endif + +/* Macro for file-offset of symbol table (in usual a.out format). */ +#ifndef SYMBOL_TABLE_OFFSET +#define SYMBOL_TABLE_OFFSET N_SYMOFF (hdr) +#endif + +/* Macro for file-offset of string table (in usual a.out format). */ +#ifndef STRING_TABLE_OFFSET +#define STRING_TABLE_OFFSET (N_SYMOFF (hdr) + hdr.a_syms) +#endif + +/* Macro to store the length of the string table data in INTO. */ +#ifndef READ_STRING_TABLE_SIZE +#define READ_STRING_TABLE_SIZE(INTO) \ +{ val = myread (desc, &INTO, sizeof INTO); \ + if (val < 0) perror_with_name (name); } +#endif + +/* Macro to declare variables to hold the file's header data. */ +#ifndef DECLARE_FILE_HEADERS +#define DECLARE_FILE_HEADERS AOUTHDR hdr +#endif + +/* Macro to read the header data from descriptor DESC and validate it. + NAME is the file name, for error messages. */ +#ifndef READ_FILE_HEADERS +#ifdef HEADER_SEEK_FD +#define READ_FILE_HEADERS(DESC, NAME) \ +{ HEADER_SEEK_FD (DESC); \ + val = myread (DESC, &hdr, sizeof hdr); \ + if (val < 0) perror_with_name (NAME); \ + if (N_BADMAG (hdr)) \ + error ("File \"%s\" not in executable format.", NAME); } +#else +#define READ_FILE_HEADERS(DESC, NAME) \ +{ val = myread (DESC, &hdr, sizeof hdr); \ + if (val < 0) perror_with_name (NAME); \ + if (N_BADMAG (hdr)) \ + error ("File \"%s\" not in executable format.", NAME); } +#endif +#endif + +/* Non-zero if this is an object (.o) file, rather than an executable. + Distinguishing between the two is rarely necessary (and seems like + a hack, but there is no other way to do ADDR_OF_TEXT_SEGMENT + right for SunOS). */ +#if !defined (IS_OBJECT_FILE) +/* This will not work + if someone decides to make ld preserve relocation info. */ +#define IS_OBJECT_FILE (hdr.a_trsize != 0) +#endif + +/* Macro for size of text segment */ +#ifndef SIZE_OF_TEXT_SEGMENT +#define SIZE_OF_TEXT_SEGMENT hdr.a_text +#endif + +/* Get the address in debugged memory of the start + of the text segment. */ +#if !defined (ADDR_OF_TEXT_SEGMENT) +#if defined (N_TXTADDR) +#define ADDR_OF_TEXT_SEGMENT (IS_OBJECT_FILE ? 0 : N_TXTADDR (hdr)) +#else /* no N_TXTADDR */ +#define ADDR_OF_TEXT_SEGMENT 0 +#endif /* no N_TXTADDR */ +#endif /* no ADDR_OF_TEXT_SEGMENT */ + +/* Macro to get entry point from headers. */ +#ifndef ENTRY_POINT +#define ENTRY_POINT hdr.a_entry +#endif + +/* Macro for name of symbol to indicate a file compiled with gcc. */ +#ifndef GCC_COMPILED_FLAG_SYMBOL +#define GCC_COMPILED_FLAG_SYMBOL "gcc_compiled." +#endif + +/* Convert stab register number (from `r' declaration) to a gdb REGNUM. */ + +#ifndef STAB_REG_TO_REGNUM +#define STAB_REG_TO_REGNUM(VALUE) (VALUE) +#endif + +/* Define this as 1 if a pcc declaration of a char or short argument + gives the correct address. Otherwise assume pcc gives the + address of the corresponding int, which is not the same on a + big-endian machine. */ + +#ifndef BELIEVE_PCC_PROMOTION +#define BELIEVE_PCC_PROMOTION 0 +#endif + +/* Nonzero means give verbose info on gdb action. From main.c. */ +extern int info_verbose; + +/* Chain of symtabs made from reading the file's symsegs. + These symtabs do not go into symtab_list themselves, + but the information is copied from them when appropriate + to make the symtabs that will exist permanently. */ + +static struct symtab *symseg_chain; + +/* Symseg symbol table for the file whose data we are now processing. + It is one of those in symseg_chain. Or 0, for a compilation that + has no symseg. */ + +static struct symtab *current_symseg; + +/* Name of source file whose symbol data we are now processing. + This comes from a symbol of type N_SO. */ + +static char *last_source_file; + +/* Core address of start of text of current source file. + This too comes from the N_SO symbol. */ + +static CORE_ADDR last_source_start_addr; + +/* End of the text segment of the executable file, + as found in the symbol _etext. */ + +static CORE_ADDR end_of_text_addr; + +/* The list of sub-source-files within the current individual compilation. + Each file gets its own symtab with its own linetable and associated info, + but they all share one blockvector. */ + +struct subfile +{ + struct subfile *next; + char *name; + struct linetable *line_vector; + int line_vector_length; + int line_vector_index; + int prev_line_number; +}; + +static struct subfile *subfiles; + +static struct subfile *current_subfile; + +/* Count symbols as they are processed, for error messages. */ + +static int symnum; + +/* Vector of types defined so far, indexed by their dbx type numbers. + (In newer sun systems, dbx uses a pair of numbers in parens, + as in "(SUBFILENUM,NUMWITHINSUBFILE)". Then these numbers must be + translated through the type_translations hash table to get + the index into the type vector.) */ + +static struct typevector *type_vector; + +/* Number of elements allocated for type_vector currently. */ + +static int type_vector_length; + +/* Vector of line number information. */ + +static struct linetable *line_vector; + +/* Index of next entry to go in line_vector_index. */ + +static int line_vector_index; + +/* Last line number recorded in the line vector. */ + +static int prev_line_number; + +/* Number of elements allocated for line_vector currently. */ + +static int line_vector_length; + +/* Hash table of global symbols whose values are not known yet. + They are chained thru the SYMBOL_VALUE, since we don't + have the correct data for that slot yet. */ +/* The use of the LOC_BLOCK code in this chain is nonstandard-- + it refers to a FORTRAN common block rather than the usual meaning. */ + +#define HASHSIZE 127 +static struct symbol *global_sym_chain[HASHSIZE]; + +/* Record the symbols defined for each context in a list. + We don't create a struct block for the context until we + know how long to make it. */ + +#define PENDINGSIZE 100 + +struct pending +{ + struct pending *next; + int nsyms; + struct symbol *symbol[PENDINGSIZE]; +}; + +/* List of free `struct pending' structures for reuse. */ +struct pending *free_pendings; + +/* Here are the three lists that symbols are put on. */ + +struct pending *file_symbols; /* static at top level, and types */ + +struct pending *global_symbols; /* global functions and variables */ + +struct pending *local_symbols; /* everything local to lexical context */ + +/* List of symbols declared since the last BCOMM. This list is a tail + of local_symbols. When ECOMM is seen, the symbols on the list + are noted so their proper addresses can be filled in later, + using the common block base address gotten from the assembler + stabs. */ + +struct pending *common_block; +int common_block_i; + +/* Stack representing unclosed lexical contexts + (that will become blocks, eventually). */ + +struct context_stack +{ + struct pending *locals; + struct pending_block *old_blocks; + struct symbol *name; + CORE_ADDR start_addr; + int depth; +}; + +struct context_stack *context_stack; + +/* Index of first unused entry in context stack. */ +int context_stack_depth; + +/* Currently allocated size of context stack. */ + +int context_stack_size; + +/* Nonzero if within a function (so symbols should be local, + if nothing says specifically). */ + +int within_function; + +/* List of blocks already made (lexical contexts already closed). + This is used at the end to make the blockvector. */ + +struct pending_block +{ + struct pending_block *next; + struct block *block; +}; + +struct pending_block *pending_blocks; + +extern CORE_ADDR startup_file_start; /* From blockframe.c */ +extern CORE_ADDR startup_file_end; /* From blockframe.c */ + +/* File name symbols were loaded from. */ + +static char *symfile; + +/* Low and high symbol values (inclusive) for the global variable + entries in the symbol file. */ + +static int first_global_sym, last_global_sym; + +/* Structures with which to manage partial symbol allocation. */ + +struct psymbol_allocation_list global_psymbols, static_psymbols; + +/* Global variable which, when set, indicates that we are processing a + .o file compiled with gcc */ + +static unsigned char processing_gcc_compilation; + +/* Make a list of forward references which haven't been defined. */ +static struct type **undef_types; +static int undef_types_allocated, undef_types_length; + + /* Setup a define to deal cleanly with the underscore problem */ + +#ifdef NAMES_HAVE_UNDERSCORE +#define HASH_OFFSET 1 +#else +#define HASH_OFFSET 0 +#endif + +#if 0 +/* I'm not sure why this is here. To debug bugs which cause + an infinite loop of allocations, I suppose. In any event, + dumping core when out of memory isn't usually right. */ +static int +xxmalloc (n) +{ + int v = malloc (n); + if (v == 0) + { + fprintf (stderr, "Virtual memory exhausted.\n"); + abort (); + } + return v; +} +#else /* not 0 */ +#define xxmalloc xmalloc +#endif /* not 0 */ + +/* Make a copy of the string at PTR with SIZE characters in the symbol obstack + (and add a null character at the end in the copy). + Returns the address of the copy. */ + +static char * +obsavestring (ptr, size) + char *ptr; + int size; +{ + register char *p = (char *) obstack_alloc (symbol_obstack, size + 1); + /* Open-coded bcopy--saves function call time. + These strings are usually short. */ + { + register char *p1 = ptr; + register char *p2 = p; + char *end = ptr + size; + while (p1 != end) + *p2++ = *p1++; + } + p[size] = 0; + return p; +} + +/* Concatenate strings S1, S2 and S3; return the new string. + Space is found in the symbol_obstack. */ + +static char * +obconcat (s1, s2, s3) + char *s1, *s2, *s3; +{ + register int len = strlen (s1) + strlen (s2) + strlen (s3) + 1; + register char *val = (char *) obstack_alloc (symbol_obstack, len); + strcpy (val, s1); + strcat (val, s2); + strcat (val, s3); + return val; +} + +/* Support for Sun changes to dbx symbol format */ + +/* For each identified header file, we have a table of types defined + in that header file. + + header_files maps header file names to their type tables. + It is a vector of n_header_files elements. + Each element describes one header file. + It contains a vector of types. + + Sometimes it can happen that the same header file produces + different results when included in different places. + This can result from conditionals or from different + things done before including the file. + When this happens, there are multiple entries for the file in this table, + one entry for each distinct set of results. + The entries are distinguished by the INSTANCE field. + The INSTANCE field appears in the N_BINCL and N_EXCL symbol table and is + used to match header-file references to their corresponding data. */ + +struct header_file +{ + char *name; /* Name of header file */ + int instance; /* Numeric code distinguishing instances + of one header file that produced + different results when included. + It comes from the N_BINCL or N_EXCL. */ + struct type **vector; /* Pointer to vector of types */ + int length; /* Allocated length (# elts) of that vector */ +}; + +static struct header_file *header_files; + +static int n_header_files; + +static int n_allocated_header_files; + +/* During initial symbol readin, we need to have a structure to keep + track of which psymtabs have which bincls in them. This structure + is used during readin to setup the list of dependencies within each + partial symbol table. */ + +struct header_file_location +{ + char *name; /* Name of header file */ + int instance; /* See above */ + struct partial_symtab *pst; /* Partial symtab that has the + BINCL/EINCL defs for this file */ +}; + +/* The actual list and controling variables */ +static struct header_file_location *bincl_list, *next_bincl; +static int bincls_allocated; + +/* Within each object file, various header files are assigned numbers. + A type is defined or referred to with a pair of numbers + (FILENUM,TYPENUM) where FILENUM is the number of the header file + and TYPENUM is the number within that header file. + TYPENUM is the index within the vector of types for that header file. + + FILENUM == 1 is special; it refers to the main source of the object file, + and not to any header file. FILENUM != 1 is interpreted by looking it up + in the following table, which contains indices in header_files. */ + +static int *this_object_header_files; + +static int n_this_object_header_files; + +static int n_allocated_this_object_header_files; + +/* When a header file is getting special overriding definitions + for one source file, record here the header_files index + of its normal definition vector. + At other times, this is -1. */ + +static int header_file_prev_index; + +/* At the start of reading dbx symbols, allocate our tables. */ + +static void +init_header_files () +{ + n_allocated_header_files = 10; + header_files = (struct header_file *) xxmalloc (10 * sizeof (struct header_file)); + n_header_files = 0; + + n_allocated_this_object_header_files = 10; + this_object_header_files = (int *) xxmalloc (10 * sizeof (int)); +} + +/* At the end of reading dbx symbols, free our tables. */ + +static void +free_header_files () +{ + register int i; + for (i = 0; i < n_header_files; i++) + free (header_files[i].name); + if (header_files) free (header_files); + if (this_object_header_files) + free (this_object_header_files); +} + +/* Called at the start of each object file's symbols. + Clear out the mapping of header file numbers to header files. */ + +static void +new_object_header_files () +{ + /* Leave FILENUM of 0 free for builtin types and this file's types. */ + n_this_object_header_files = 1; + header_file_prev_index = -1; +} + +/* Add header file number I for this object file + at the next successive FILENUM. */ + +static void +add_this_object_header_file (i) + int i; +{ + if (n_this_object_header_files == n_allocated_this_object_header_files) + { + n_allocated_this_object_header_files *= 2; + this_object_header_files + = (int *) xrealloc (this_object_header_files, + n_allocated_this_object_header_files * sizeof (int)); + } + + this_object_header_files[n_this_object_header_files++] = i; +} + +/* Add to this file an "old" header file, one already seen in + a previous object file. NAME is the header file's name. + INSTANCE is its instance code, to select among multiple + symbol tables for the same header file. */ + +static void +add_old_header_file (name, instance) + char *name; + int instance; +{ + register struct header_file *p = header_files; + register int i; + + for (i = 0; i < n_header_files; i++) + if (!strcmp (p[i].name, name) && instance == p[i].instance) + { + add_this_object_header_file (i); + return; + } + error ("Invalid symbol data: \"repeated\" header file that hasn't been seen before, at symtab pos %d.", + symnum); +} + +/* Add to this file a "new" header file: definitions for its types follow. + NAME is the header file's name. + Most often this happens only once for each distinct header file, + but not necessarily. If it happens more than once, INSTANCE has + a different value each time, and references to the header file + use INSTANCE values to select among them. + + dbx output contains "begin" and "end" markers for each new header file, + but at this level we just need to know which files there have been; + so we record the file when its "begin" is seen and ignore the "end". */ + +static void +add_new_header_file (name, instance) + char *name; + int instance; +{ + register int i; + register struct header_file *p = header_files; + header_file_prev_index = -1; + +#if 0 + /* This code was used before I knew about the instance codes. + My first hypothesis is that it is not necessary now + that instance codes are handled. */ + + /* Has this header file a previous definition? + If so, make a new entry anyway so that this use in this source file + gets a separate entry. Later source files get the old entry. + Record here the index of the old entry, so that any type indices + not previously defined can get defined in the old entry as + well as in the new one. */ + + for (i = 0; i < n_header_files; i++) + if (!strcmp (p[i].name, name)) + { + header_file_prev_index = i; + } + +#endif + + /* Make sure there is room for one more header file. */ + + if (n_header_files == n_allocated_header_files) + { + n_allocated_header_files *= 2; + header_files = (struct header_file *) + xrealloc (header_files, + (n_allocated_header_files + * sizeof (struct header_file))); + } + + /* Create an entry for this header file. */ + + i = n_header_files++; + header_files[i].name = savestring (name, strlen(name)); + header_files[i].instance = instance; + header_files[i].length = 10; + header_files[i].vector + = (struct type **) xxmalloc (10 * sizeof (struct type *)); + bzero (header_files[i].vector, 10 * sizeof (struct type *)); + + add_this_object_header_file (i); +} + +/* Look up a dbx type-number pair. Return the address of the slot + where the type for that number-pair is stored. + The number-pair is in TYPENUMS. + + This can be used for finding the type associated with that pair + or for associating a new type with the pair. */ + +static struct type ** +dbx_lookup_type (typenums) + int typenums[2]; +{ + register int filenum = typenums[0], index = typenums[1]; + + if (filenum < 0 || filenum >= n_this_object_header_files) + error ("Invalid symbol data: type number (%d,%d) out of range at symtab pos %d.", + filenum, index, symnum); + + if (filenum == 0) + { + /* Type is defined outside of header files. + Find it in this object file's type vector. */ + if (index >= type_vector_length) + { + type_vector_length *= 2; + type_vector = (struct typevector *) + xrealloc (type_vector, + (sizeof (struct typevector) + + type_vector_length * sizeof (struct type *))); + bzero (&type_vector->type[type_vector_length / 2], + type_vector_length * sizeof (struct type *) / 2); + } + return &type_vector->type[index]; + } + else + { + register int real_filenum = this_object_header_files[filenum]; + register struct header_file *f; + + if (real_filenum >= n_header_files) + abort (); + + f = &header_files[real_filenum]; + + if (index >= f->length) + { + f->length *= 2; + f->vector = (struct type **) + xrealloc (f->vector, f->length * sizeof (struct type *)); + bzero (&f->vector[f->length / 2], + f->length * sizeof (struct type *) / 2); + } + return &f->vector[index]; + } +} + +/* Create a type object. Occaisionally used when you need a type + which isn't going to be given a type number. */ + +static struct type * +dbx_create_type () +{ + register struct type *type = + (struct type *) obstack_alloc (symbol_obstack, sizeof (struct type)); + + bzero (type, sizeof (struct type)); + TYPE_VPTR_FIELDNO (type) = -1; + return type; +} + +/* Make sure there is a type allocated for type numbers TYPENUMS + and return the type object. + This can create an empty (zeroed) type object. + TYPENUMS may be (-1, -1) to return a new type object that is not + put into the type vector, and so may not be referred to by number. */ + +static struct type * +dbx_alloc_type (typenums) + int typenums[2]; +{ + register struct type **type_addr; + register struct type *type; + + if (typenums[1] != -1) + { + type_addr = dbx_lookup_type (typenums); + type = *type_addr; + } + else + { + type_addr = 0; + type = 0; + } + + /* If we are referring to a type not known at all yet, + allocate an empty type for it. + We will fill it in later if we find out how. */ + if (type == 0) + { + type = dbx_create_type (); + if (type_addr) + *type_addr = type; + } + + return type; +} + +#if 0 +static struct type ** +explicit_lookup_type (real_filenum, index) + int real_filenum, index; +{ + register struct header_file *f = &header_files[real_filenum]; + + if (index >= f->length) + { + f->length *= 2; + f->vector = (struct type **) + xrealloc (f->vector, f->length * sizeof (struct type *)); + bzero (&f->vector[f->length / 2], + f->length * sizeof (struct type *) / 2); + } + return &f->vector[index]; +} +#endif + +/* maintain the lists of symbols and blocks */ + +/* Add a symbol to one of the lists of symbols. */ +static void +add_symbol_to_list (symbol, listhead) + struct symbol *symbol; + struct pending **listhead; +{ + /* We keep PENDINGSIZE symbols in each link of the list. + If we don't have a link with room in it, add a new link. */ + if (*listhead == 0 || (*listhead)->nsyms == PENDINGSIZE) + { + register struct pending *link; + if (free_pendings) + { + link = free_pendings; + free_pendings = link->next; + } + else + link = (struct pending *) xxmalloc (sizeof (struct pending)); + + link->next = *listhead; + *listhead = link; + link->nsyms = 0; + } + + (*listhead)->symbol[(*listhead)->nsyms++] = symbol; +} + +/* At end of reading syms, or in case of quit, + really free as many `struct pending's as we can easily find. */ + +static void +really_free_pendings () +{ + struct pending *next, *next1; + struct pending_block *bnext, *bnext1; + + for (next = free_pendings; next; next = next1) + { + next1 = next->next; + free (next); + } + free_pendings = 0; + + for (bnext = pending_blocks; bnext; bnext = bnext1) + { + bnext1 = bnext->next; + free (bnext); + } + pending_blocks = 0; + + for (next = file_symbols; next; next = next1) + { + next1 = next->next; + free (next); + } + for (next = global_symbols; next; next = next1) + { + next1 = next->next; + free (next); + } +} + +/* Take one of the lists of symbols and make a block from it. + Keep the order the symbols have in the list (reversed from the input file). + Put the block on the list of pending blocks. */ + +static void +finish_block (symbol, listhead, old_blocks, start, end) + struct symbol *symbol; + struct pending **listhead; + struct pending_block *old_blocks; + CORE_ADDR start, end; +{ + register struct pending *next, *next1; + register struct block *block; + register struct pending_block *pblock; + struct pending_block *opblock; + register int i; + + /* Count the length of the list of symbols. */ + + for (next = *listhead, i = 0; next; i += next->nsyms, next = next->next); + + block = (struct block *) obstack_alloc (symbol_obstack, + (sizeof (struct block) + + ((i - 1) + * sizeof (struct symbol *)))); + + /* Copy the symbols into the block. */ + + BLOCK_NSYMS (block) = i; + for (next = *listhead; next; next = next->next) + { + register int j; + for (j = next->nsyms - 1; j >= 0; j--) + BLOCK_SYM (block, --i) = next->symbol[j]; + } + + BLOCK_START (block) = start; + BLOCK_END (block) = end; + BLOCK_SUPERBLOCK (block) = 0; /* Filled in when containing block is made */ + BLOCK_GCC_COMPILED (block) = processing_gcc_compilation; + + /* Put the block in as the value of the symbol that names it. */ + + if (symbol) + { + SYMBOL_BLOCK_VALUE (symbol) = block; + BLOCK_FUNCTION (block) = symbol; + } + else + BLOCK_FUNCTION (block) = 0; + + /* Now "free" the links of the list, and empty the list. */ + + for (next = *listhead; next; next = next1) + { + next1 = next->next; + next->next = free_pendings; + free_pendings = next; + } + *listhead = 0; + + /* Install this block as the superblock + of all blocks made since the start of this scope + that don't have superblocks yet. */ + + opblock = 0; + for (pblock = pending_blocks; pblock != old_blocks; pblock = pblock->next) + { + if (BLOCK_SUPERBLOCK (pblock->block) == 0) + BLOCK_SUPERBLOCK (pblock->block) = block; + opblock = pblock; + } + + /* Record this block on the list of all blocks in the file. + Put it after opblock, or at the beginning if opblock is 0. + This puts the block in the list after all its subblocks. */ + + /* Allocate in the symbol_obstack to save time. + It wastes a little space. */ + pblock = (struct pending_block *) + obstack_alloc (symbol_obstack, + sizeof (struct pending_block)); + pblock->block = block; + if (opblock) + { + pblock->next = opblock->next; + opblock->next = pblock; + } + else + { + pblock->next = pending_blocks; + pending_blocks = pblock; + } +} + +static struct blockvector * +make_blockvector () +{ + register struct pending_block *next, *next1; + register struct blockvector *blockvector; + register int i; + + /* Count the length of the list of blocks. */ + + for (next = pending_blocks, i = 0; next; next = next->next, i++); + + blockvector = (struct blockvector *) + obstack_alloc (symbol_obstack, + (sizeof (struct blockvector) + + (i - 1) * sizeof (struct block *))); + + /* Copy the blocks into the blockvector. + This is done in reverse order, which happens to put + the blocks into the proper order (ascending starting address). + finish_block has hair to insert each block into the list + after its subblocks in order to make sure this is true. */ + + BLOCKVECTOR_NBLOCKS (blockvector) = i; + for (next = pending_blocks; next; next = next->next) + BLOCKVECTOR_BLOCK (blockvector, --i) = next->block; + +#if 0 /* Now we make the links in the obstack, so don't free them. */ + /* Now free the links of the list, and empty the list. */ + + for (next = pending_blocks; next; next = next1) + { + next1 = next->next; + free (next); + } +#endif + pending_blocks = 0; + + return blockvector; +} + +/* Manage the vector of line numbers. */ + +static void +record_line (line, pc) + int line; + CORE_ADDR pc; +{ + struct linetable_entry *e; + /* Ignore the dummy line number in libg.o */ + + if (line == 0xffff) + return; + + /* Make sure line vector is big enough. */ + + if (line_vector_index + 1 >= line_vector_length) + { + line_vector_length *= 2; + line_vector = (struct linetable *) + xrealloc (line_vector, + (sizeof (struct linetable) + + line_vector_length * sizeof (struct linetable_entry))); + current_subfile->line_vector = line_vector; + } + + e = line_vector->item + line_vector_index++; + e->line = line; e->pc = pc; +} + +/* Start a new symtab for a new source file. + This is called when a dbx symbol of type N_SO is seen; + it indicates the start of data for one original source file. */ + +static void +start_symtab (name, start_addr) + char *name; + CORE_ADDR start_addr; +{ + register struct symtab *s; + + last_source_file = name; + last_source_start_addr = start_addr; + file_symbols = 0; + global_symbols = 0; + within_function = 0; + + /* Context stack is initially empty, with room for 10 levels. */ + context_stack + = (struct context_stack *) xxmalloc (10 * sizeof (struct context_stack)); + context_stack_size = 10; + context_stack_depth = 0; + + new_object_header_files (); + + for (s = symseg_chain; s; s = s->next) + if (s->ldsymoff == symnum * sizeof (struct nlist)) + break; + current_symseg = s; + if (s != 0) + return; + + type_vector_length = 160; + type_vector = (struct typevector *) + xxmalloc (sizeof (struct typevector) + + type_vector_length * sizeof (struct type *)); + bzero (type_vector->type, type_vector_length * sizeof (struct type *)); + + /* Initialize the list of sub source files with one entry + for this file (the top-level source file). */ + + subfiles = 0; + current_subfile = 0; + start_subfile (name); + +#if 0 /* This is now set at the beginning of read_ofile_symtab */ + /* Set default for compiler to pcc; assume that we aren't processing + a gcc compiled file until proved otherwise. */ + + processing_gcc_compilation = 0; +#endif +} + +/* Handle an N_SOL symbol, which indicates the start of + code that came from an included (or otherwise merged-in) + source file with a different name. */ + +static void +start_subfile (name) + char *name; +{ + register struct subfile *subfile; + + /* Save the current subfile's line vector data. */ + + if (current_subfile) + { + current_subfile->line_vector_index = line_vector_index; + current_subfile->line_vector_length = line_vector_length; + current_subfile->prev_line_number = prev_line_number; + } + + /* See if this subfile is already known as a subfile of the + current main source file. */ + + for (subfile = subfiles; subfile; subfile = subfile->next) + { + if (!strcmp (subfile->name, name)) + { + line_vector = subfile->line_vector; + line_vector_index = subfile->line_vector_index; + line_vector_length = subfile->line_vector_length; + prev_line_number = subfile->prev_line_number; + current_subfile = subfile; + return; + } + } + + /* This subfile is not known. Add an entry for it. */ + + line_vector_index = 0; + line_vector_length = 1000; + prev_line_number = -2; /* Force first line number to be explicit */ + line_vector = (struct linetable *) + xxmalloc (sizeof (struct linetable) + + line_vector_length * sizeof (struct linetable_entry)); + + /* Make an entry for this subfile in the list of all subfiles + of the current main source file. */ + + subfile = (struct subfile *) xxmalloc (sizeof (struct subfile)); + subfile->next = subfiles; + subfile->name = savestring (name, strlen (name)); + subfile->line_vector = line_vector; + subfiles = subfile; + current_subfile = subfile; +} + +/* Finish the symbol definitions for one main source file, + close off all the lexical contexts for that file + (creating struct block's for them), then make the struct symtab + for that file and put it in the list of all such. + + END_ADDR is the address of the end of the file's text. */ + +static void +end_symtab (end_addr) + CORE_ADDR end_addr; +{ + register struct symtab *symtab; + register struct blockvector *blockvector; + register struct subfile *subfile; + register struct linetable *lv; + struct subfile *nextsub; + + if (current_symseg != 0) + { + last_source_file = 0; + current_symseg = 0; + return; + } + + /* Finish the lexical context of the last function in the file; + pop the context stack. */ + + if (context_stack_depth > 0) + { + register struct context_stack *cstk; + context_stack_depth--; + cstk = &context_stack[context_stack_depth]; + /* Make a block for the local symbols within. */ + finish_block (cstk->name, &local_symbols, cstk->old_blocks, + cstk->start_addr, end_addr); + } + + /* Cleanup any undefined types that have been left hanging around + (this needs to be done before the finish_blocks so that + file_symbols is still good). */ + cleanup_undefined_types (); + + /* Finish defining all the blocks of this symtab. */ + finish_block (0, &file_symbols, 0, last_source_start_addr, end_addr); + finish_block (0, &global_symbols, 0, last_source_start_addr, end_addr); + blockvector = make_blockvector (); + + current_subfile->line_vector_index = line_vector_index; + + /* Now create the symtab objects proper, one for each subfile. */ + /* (The main file is one of them.) */ + + for (subfile = subfiles; subfile; subfile = nextsub) + { + symtab = (struct symtab *) xxmalloc (sizeof (struct symtab)); + symtab->free_ptr = 0; + + /* Fill in its components. */ + symtab->blockvector = blockvector; + type_vector->length = type_vector_length; + symtab->typevector = type_vector; + symtab->free_code = free_linetable; + if (subfile->next == 0) + symtab->free_ptr = (char *) type_vector; + + symtab->filename = subfile->name; + lv = subfile->line_vector; + lv->nitems = subfile->line_vector_index; + symtab->linetable = (struct linetable *) + xrealloc (lv, (sizeof (struct linetable) + + lv->nitems * sizeof (struct linetable_entry))); + symtab->nlines = 0; + symtab->line_charpos = 0; + + /* Link the new symtab into the list of such. */ + symtab->next = symtab_list; + symtab_list = symtab; + + nextsub = subfile->next; + free (subfile); + } + + type_vector = 0; + type_vector_length = -1; + line_vector = 0; + line_vector_length = -1; + last_source_file = 0; +} + +#ifdef N_BINCL + +/* Handle the N_BINCL and N_EINCL symbol types + that act like N_SOL for switching source files + (different subfiles, as we call them) within one object file, + but using a stack rather than in an arbitrary order. */ + +struct subfile_stack +{ + struct subfile_stack *next; + char *name; + int prev_index; +}; + +struct subfile_stack *subfile_stack; + +static void +push_subfile () +{ + register struct subfile_stack *tem + = (struct subfile_stack *) xxmalloc (sizeof (struct subfile_stack)); + + tem->next = subfile_stack; + subfile_stack = tem; + if (current_subfile == 0 || current_subfile->name == 0) + abort (); + tem->name = current_subfile->name; + tem->prev_index = header_file_prev_index; +} + +static char * +pop_subfile () +{ + register char *name; + register struct subfile_stack *link = subfile_stack; + + if (link == 0) + abort (); + + name = link->name; + subfile_stack = link->next; + header_file_prev_index = link->prev_index; + free (link); + + return name; +} +#endif /* Have N_BINCL */ + +/* Accumulate the misc functions in bunches of 127. + At the end, copy them all into one newly allocated structure. */ + +#define MISC_BUNCH_SIZE 127 + +struct misc_bunch +{ + struct misc_bunch *next; + struct misc_function contents[MISC_BUNCH_SIZE]; +}; + +/* Bunch currently being filled up. + The next field points to chain of filled bunches. */ + +static struct misc_bunch *misc_bunch; + +/* Number of slots filled in current bunch. */ + +static int misc_bunch_index; + +/* Total number of misc functions recorded so far. */ + +static int misc_count; + +static void +init_misc_functions () +{ + misc_count = 0; + misc_bunch = 0; + misc_bunch_index = MISC_BUNCH_SIZE; +} + +static void +record_misc_function (name, address, type) + char *name; + CORE_ADDR address; + int type; +{ + register struct misc_bunch *new; + register unsigned char mtype; + + if (misc_bunch_index == MISC_BUNCH_SIZE) + { + new = (struct misc_bunch *) xxmalloc (sizeof (struct misc_bunch)); + misc_bunch_index = 0; + new->next = misc_bunch; + misc_bunch = new; + } + misc_bunch->contents[misc_bunch_index].name = name; + misc_bunch->contents[misc_bunch_index].address = address; + switch (type &~ N_EXT) + { + case N_TEXT: mtype = mf_text; break; + case N_DATA: mtype = mf_data; break; + case N_BSS: mtype = mf_bss; break; + case N_ABS: mtype = mf_abs; break; +#ifdef N_SETV + case N_SETV: mtype = mf_data; break; +#endif + default: mtype = mf_unknown; break; + } + misc_bunch->contents[misc_bunch_index].type = mtype; + misc_bunch_index++; + misc_count++; +} + +static int +compare_misc_functions (fn1, fn2) + struct misc_function *fn1, *fn2; +{ + /* Return a signed result based on unsigned comparisons + so that we sort into unsigned numeric order. */ + if (fn1->address < fn2->address) + return -1; + if (fn1->address > fn2->address) + return 1; + return 0; +} + +static void +discard_misc_bunches () +{ + register struct misc_bunch *next; + + while (misc_bunch) + { + next = misc_bunch->next; + free (misc_bunch); + misc_bunch = next; + } +} + +/* INCLINK nonzero means bunches are from an incrementally-linked file. + Add them to the existing bunches. + Otherwise INCLINK is zero, and we start from scratch. */ +static void +condense_misc_bunches (inclink) + int inclink; +{ + register int i, j; + register struct misc_bunch *bunch; +#ifdef NAMES_HAVE_UNDERSCORE + int offset = 1; +#else + int offset = 0; +#endif + + if (inclink) + { + misc_function_vector + = (struct misc_function *) + xrealloc (misc_function_vector, (misc_count + misc_function_count) + * sizeof (struct misc_function)); + j = misc_function_count; + } + else + { + misc_function_vector + = (struct misc_function *) + xxmalloc (misc_count * sizeof (struct misc_function)); + j = 0; + } + + bunch = misc_bunch; + while (bunch) + { + for (i = 0; i < misc_bunch_index; i++) + { + misc_function_vector[j] = bunch->contents[i]; + misc_function_vector[j].name + = obconcat (misc_function_vector[j].name + + (misc_function_vector[j].name[0] == '_' ? offset : 0), + "", ""); + j++; + } + bunch = bunch->next; + misc_bunch_index = MISC_BUNCH_SIZE; + } + + if (inclink) + misc_function_count += misc_count; + else + misc_function_count = j; + + /* Sort the misc functions by address. */ + + qsort (misc_function_vector, misc_function_count, + sizeof (struct misc_function), + compare_misc_functions); + + /* (re)build the hash table (positions changed during the sort) */ + + for (i = 0; i < MISC_FUNC_HASH_SIZE; ++i) + misc_function_hash_tab[i] = -1; + for (i = 0; i < misc_function_count; ++i) + { + j = hash_symbol(misc_function_vector[i].name) & (MISC_FUNC_HASH_SIZE - 1); + misc_function_vector[i].next = misc_function_hash_tab[j]; + misc_function_hash_tab[j] = i; + } +} + +/* Call sort_syms to sort alphabetically + the symbols of each block of each symtab. */ + +static int +compare_symbols (s1, s2) + struct symbol **s1, **s2; +{ + register int namediff; + + /* Compare the initial characters. */ + namediff = SYMBOL_NAME (*s1)[0] - SYMBOL_NAME (*s2)[0]; + if (namediff != 0) return namediff; + + /* If they match, compare the rest of the names. */ + namediff = strcmp (SYMBOL_NAME (*s1), SYMBOL_NAME (*s2)); + if (namediff != 0) return namediff; + + /* For symbols of the same name, registers should come first. */ + return ((SYMBOL_CLASS (*s2) == LOC_REGISTER) + - (SYMBOL_CLASS (*s1) == LOC_REGISTER)); +} + +static void sort_symtab_syms (); + +static void +sort_syms () +{ + register struct symtab *s; + + for (s = symtab_list; s; s = s->next) + sort_symtab_syms (s); +} + +static void +sort_symtab_syms (s) + register struct symtab *s; +{ + register struct blockvector *bv = BLOCKVECTOR (s); + int nbl = BLOCKVECTOR_NBLOCKS (bv); + int i; + register struct block *b; + + /* Note that in the following sort, we always make sure that + register debug symbol declarations always come before regular + debug symbol declarations (as might happen when parameters are + then put into registers by the compiler). We do this by a + correct compare in compare_symbols, and by the reversal of the + symbols if we don't sort. This works as long as a register debug + symbol always comes after a parameter debug symbol. */ + + /* This is no longer necessary; lookup_block_symbol now always + prefers some other declaration over a parameter declaration. We + still sort the thing (that is necessary), but we don't reverse it + if we shouldn't sort it. */ + + for (i = 0; i < nbl; i++) + { + b = BLOCKVECTOR_BLOCK (bv, i); + if (BLOCK_SHOULD_SORT (b)) + qsort (&BLOCK_SYM (b, 0), BLOCK_NSYMS (b), + sizeof (struct symbol *), compare_symbols); + } +} + + +extern struct symtab *psymtab_to_symtab (); + +/* The entry point. */ +static CORE_ADDR entry_point; + +static char *symfile_string_table; +static int symfile_string_table_size; + +/* This is the symbol-file command. Read the file, analyze its symbols, + and add a struct symtab to symtab_list. */ + +void +symbol_file_command (name, from_tty) + char *name; + int from_tty; +{ + register int desc; + DECLARE_FILE_HEADERS; + struct nlist *nlist; + + /* The string table. */ + char *stringtab; + + /* The size of the string table (buffer is a bizarre name...). */ + long buffer; + + register int val; + extern void close (); + struct cleanup *old_chain; + struct symtab *symseg; + struct stat statbuf; + + dont_repeat (); + + if (name == 0) + { + if ((symtab_list || partial_symtab_list) + && from_tty + && !query ("Discard symbol table? ", 0)) + error ("Not confirmed."); + if (symfile) + free (symfile); + symfile = 0; + free_all_symtabs (); + free_all_psymtabs (); + return; + } + + name = tilde_expand (name); + make_cleanup (free, name); + + if ((symtab_list || partial_symtab_list) + && !query ("Load new symbol table from \"%s\"? ", name)) + error ("Not confirmed."); + + { + char *absolute_name; + desc = openp (getenv ("PATH"), 1, name, O_RDONLY, 0, &absolute_name); + if (desc < 0) + perror_with_name (name); + else + name = absolute_name; + } + + old_chain = make_cleanup (close, desc); + make_cleanup (free_current_contents, &name); + + READ_FILE_HEADERS (desc, name); + + entry_point = ENTRY_POINT; + + if (NUMBER_OF_SYMBOLS == 0) + { + if (symfile) + free (symfile); + symfile = 0; + free_all_symtabs (); + free_all_psymtabs (); + printf ("%s has no symbol-table; symbols discarded.\n", name); + fflush (stdout); + do_cleanups (old_chain); + return; + } + + printf ("Reading symbol data from %s...", name); + fflush (stdout); + + /* Now read the string table, all at once. */ + val = lseek (desc, STRING_TABLE_OFFSET, 0); + if (val < 0) + perror_with_name (name); + if (stat (name, &statbuf) == -1) + perror_with_name (name); + READ_STRING_TABLE_SIZE (buffer); + if (buffer >= 0 && buffer < statbuf.st_size) + { + /* This should speed things up without consuming much + extra memory (because probably little of the space is going + to be reused anyway, whether in data or stack space). + + A quick test (running GDB on itself and setting 9 breakpoints + in different files) showed that memory usage was almost + identical for the two cases. */ +#if 0 +#ifdef BROKEN_LARGE_ALLOCA + stringtab = (char *) xmalloc (buffer); + make_cleanup (free, stringtab); +#else + stringtab = (char *) alloca (buffer); +#endif +#endif + stringtab = (char *) xmalloc (buffer); + symfile_string_table = stringtab; + symfile_string_table_size = buffer; + } + else + stringtab = NULL; + if (stringtab == NULL) + error ("ridiculous string table size: %d bytes", buffer); + + /* Usually READ_STRING_TABLE_SIZE will have shifted the file pointer. + Occaisionally, it won't. */ + val = lseek (desc, STRING_TABLE_OFFSET, L_SET); + if (val < 0) + perror_with_name (name); + val = myread (desc, stringtab, buffer); + if (val < 0) + perror_with_name (name); + + /* Throw away the old symbol table. */ + + if (symfile) + free (symfile); + symfile = 0; + free_all_symtabs (); + free_all_psymtabs (); + + /* Empty the hash table of global syms looking for values. */ + bzero (global_sym_chain, sizeof global_sym_chain); + + /* Symsegs are no longer supported by GDB. Setting symseg_chain to + 0 is easier than finding all the symseg code and eliminating it. */ + symseg_chain = 0; + + /* Position to read the symbol table. Do not read it all at once. */ + val = lseek (desc, SYMBOL_TABLE_OFFSET, 0); + if (val < 0) + perror_with_name (name); + + /* Don't put these on the cleanup chain; they need to stick around + until the next call to symbol_file_command. *Then* we'll free + them. */ + free_header_files (); + init_header_files (); + + init_misc_functions (); + make_cleanup (discard_misc_bunches, 0); + + free_pendings = 0; + pending_blocks = 0; + file_symbols = 0; + global_symbols = 0; + make_cleanup (really_free_pendings, 0); + + /* Now that the symbol table data of the executable file are all in core, + process them and define symbols accordingly. Closes desc. */ + + read_dbx_symtab (desc, stringtab, buffer, NUMBER_OF_SYMBOLS, 0, + ADDR_OF_TEXT_SEGMENT, SIZE_OF_TEXT_SEGMENT); + + /* Go over the misc functions and install them in vector. */ + + condense_misc_bunches (0); + + /* Don't allow char * to have a typename (else would get caddr_t.) */ + + TYPE_NAME (lookup_pointer_type (builtin_type_char)) = 0; + + /* Make a default for file to list. */ + + symfile = savestring (name, strlen (name)); + + /* Call to select_source_symtab used to be here; it was using too + much time. I'll make sure that list_sources can handle the lack + of current_source_symtab */ + + do_cleanups (old_chain); /* Descriptor closed here */ + + /* Free the symtabs made by read_symsegs, but not their contents, + which have been copied into symtabs on symtab_list. */ + while (symseg_chain) + { + register struct symtab *s = symseg_chain->next; + free (symseg_chain); + symseg_chain = s; + } + + if (!partial_symtab_list) + printf ("\n(no debugging symbols found)..."); + + printf ("done.\n"); + fflush (stdout); +} + +/* Return name of file symbols were loaded from, or 0 if none.. */ + +char * +get_sym_file () +{ + return symfile; +} + +/* Buffer for reading the symbol table entries. */ +static struct nlist symbuf[4096]; +static int symbuf_idx; +static int symbuf_end; + +/* I/O descriptor for reading the symbol table. */ +static int symtab_input_desc; + +/* The address of the string table + of the object file we are reading (as copied into core). */ +static char *stringtab_global; + +/* Refill the symbol table input buffer + and set the variables that control fetching entries from it. + Reports an error if no data available. + This function can read past the end of the symbol table + (into the string table) but this does no harm. */ + +static int +fill_symbuf () +{ + int nbytes = myread (symtab_input_desc, symbuf, sizeof (symbuf)); + if (nbytes <= 0) + error ("error or end of file reading symbol table"); + symbuf_end = nbytes / sizeof (struct nlist); + symbuf_idx = 0; + return 1; +} + +/* dbx allows the text of a symbol name to be continued into the + next symbol name! When such a continuation is encountered + (a \ at the end of the text of a name) + call this function to get the continuation. */ + +static char * +next_symbol_text () +{ + if (symbuf_idx == symbuf_end) + fill_symbuf (); + symnum++; + return symbuf[symbuf_idx++].n_un.n_strx + stringtab_global; +} + +/* + * Initializes storage for all of the partial symbols that will be + * created by read_dbx_symtab and subsidiaries. + */ +void +init_psymbol_list (total_symbols) + int total_symbols; +{ + /* Current best guess is that there are approximately a twentieth + of the total symbols (in a debugging file) are global or static + oriented symbols */ + global_psymbols.size = total_symbols / 10; + static_psymbols.size = total_symbols / 10; + global_psymbols.next = global_psymbols.list = (struct partial_symbol *) + xmalloc (global_psymbols.size * sizeof (struct partial_symbol)); + static_psymbols.next = static_psymbols.list = (struct partial_symbol *) + xmalloc (static_psymbols.size * sizeof (struct partial_symbol)); +} + +/* + * Initialize the list of bincls to contain none and have some + * allocated. + */ +static void +init_bincl_list (number) + int number; +{ + bincls_allocated = number; + next_bincl = bincl_list = (struct header_file_location *) + xmalloc (bincls_allocated * sizeof(struct header_file_location)); +} + +/* + * Add a bincl to the list. + */ +static void +add_bincl_to_list (pst, name, instance) + struct partial_symtab *pst; + char *name; + int instance; +{ + if (next_bincl >= bincl_list + bincls_allocated) + { + int offset = next_bincl - bincl_list; + bincls_allocated *= 2; + bincl_list = (struct header_file_location *) + xrealloc (bincl_list, + bincls_allocated * sizeof (struct header_file_location)); + next_bincl = bincl_list + offset; + } + next_bincl->pst = pst; + next_bincl->instance = instance; + next_bincl++->name = name; +} + +/* + * Given a name, value pair, find the corresponding + * bincl in the list. Return the partial symtab associated + * with that header_file_location. + */ +struct partial_symtab * +find_corresponding_bincl_psymtab (name, instance) + char *name; + int instance; +{ + struct header_file_location *bincl; + + for (bincl = bincl_list; bincl < next_bincl; bincl++) + if (bincl->instance == instance + && !strcmp (name, bincl->name)) + return bincl->pst; + + return (struct partial_symtab *) 0; +} + +/* + * Free the storage allocated for the bincl list. + */ +static void +free_bincl_list () +{ + free (bincl_list); + bincls_allocated = 0; +} + +static struct partial_symtab *start_psymtab (); +static void add_psymtab_dependency (); +static void end_psymtab(); + +static int +compare_psymbols (s1, s2) + register struct partial_symbol *s1, *s2; +{ + register char + *st1 = SYMBOL_NAME (s1), + *st2 = SYMBOL_NAME (s2); + register int i; + + if (st1[0] - st2[0]) + return (st1[0] - st2[0]); + if (st1[1] - st2[1]) + return (st1[1] - st2[1]); + if (i = strcmp(st1, st2)) + return (i); + /* Next comparison implements policy that used to be in lookup_symbol: + * it would search psymtabs in psymtab_list order (reverse order of + * declaration) & take first occurance of symbol it found. So, we + * collate duplicate names in reverse psymtab order. */ + return (s2->pst - s1->pst); +} + +/* Given pointers to an a.out symbol table in core containing dbx + style data, setup partial_symtab's describing each source file for + which debugging information is available. NLISTLEN is the number + of symbols in the symbol table. All symbol names are given as + offsets relative to STRINGTAB. STRINGTAB_SIZE is the size of + STRINGTAB. + + I have no idea whether or not this routine should be setup to deal + with inclinks. It seems reasonable to me that they be dealt with + standardly, so I am not going to make a strong effort to deal with + them here. + */ + +static void +read_dbx_symtab (desc, stringtab, stringtab_size, nlistlen, inclink, + text_addr, text_size) + int desc; + register char *stringtab; + register long stringtab_size; + register int nlistlen; + int inclink; + unsigned text_addr; + int text_size; +{ + register struct nlist *bufp; + register char *namestring; + register struct partial_symbol *psym; + register struct psymbol_allocation_list *psymbol_struct; + + int nsl; + int past_first_source_file = 0; + CORE_ADDR last_o_file_start = 0; + char *last_o_file_name = "*bogus*"; + struct cleanup *old_chain; + char *p; + enum namespace ns; + enum address_class class; + +#ifdef PROFILE_TYPES + int i; + int profile_types [256]; + int strcmp_called = 0; + int autovars = 0; + int global_funs = 0; +#endif + + /* Current partial symtab */ + struct partial_symtab *pst; + + /* List of current psymtab's include files */ + char **psymtab_include_list; + int includes_allocated; + int includes_used; + + /* Index within current psymtab dependency list */ + struct partial_symtab **dependency_list; + int dependencies_used, dependencies_allocated; + +#ifdef PROFILE_TYPES + for (i = 0; i < 256; i++) + profile_types[i] = 0; +#endif + + stringtab_global = stringtab; + + pst = (struct partial_symtab *) 0; + + includes_allocated = 30; + includes_used = 0; + psymtab_include_list = (char **) alloca (includes_allocated * + sizeof (char *)); + + dependencies_allocated = 30; + dependencies_used = 0; + dependency_list = + (struct partial_symtab **) alloca (dependencies_allocated * + sizeof (struct partial_symtab *)); + + old_chain = make_cleanup (free_all_psymtabs, 0); + + /* Init bincl list */ + init_bincl_list (20); + make_cleanup (free_bincl_list, 0); + + /* Setup global partial symbol list */ + init_psymbol_list (nlistlen); + + last_source_file = 0; + +#ifdef END_OF_TEXT_DEFAULT + end_of_text_addr = END_OF_TEXT_DEFAULT; +#else + end_of_text_addr = text_addr + text_size; +#endif + + symtab_input_desc = desc; /* This is needed for fill_symbuf below */ + symbuf_end = symbuf_idx = 0; + + for (symnum = 0; symnum < nlistlen; symnum++) + { + /* Get the symbol for this run and pull out some info */ + QUIT; /* allow this to be interruptable */ + if (symbuf_idx == symbuf_end) + fill_symbuf (); + bufp = &symbuf[symbuf_idx++]; + +#ifdef PROFILE_TYPES + profile_types[bufp->n_type]++; +#endif + + /* + * Special case to speed up readin. + */ + if (bufp->n_type == N_SLINE) continue; + + /* Ok. There is a lot of code duplicated in the rest of this + switch statiement (for efficiency reasons). Since I don't + like duplicating code, I will do my penance here, and + describe the code which is duplicated: + + *) The assignment to namestring. + *) The call to index. + *) The addition of a partial symbol the the two partial + symbol lists. This last is a large section of code, so + I've imbedded it in the following macro. + */ + +/* Set namestring based on bufp. */ +#define SET_NAMESTRING()\ + if (bufp->n_un.n_strx < 0 || bufp->n_un.n_strx >= stringtab_size) \ + error ("Invalid symbol data: bad string table offset: %d", \ + bufp->n_un.n_strx); \ + namestring = bufp->n_un.n_strx + stringtab + +#define ADD_PSYMBOL_TO_LIST(NAME, NAMELENGTH, NAMESPACE, CLASS, LIST, VALUE)\ + do { \ + if ((LIST).next >= \ + (LIST).list + (LIST).size) \ + { \ + (LIST).list = (struct partial_symbol *) \ + xrealloc ((LIST).list, \ + ((LIST).size * 2 \ + * sizeof (struct partial_symbol))); \ + /* Next assumes we only went one over. Should be good if \ + program works correctly */ \ + (LIST).next = \ + (LIST).list + (LIST).size; \ + (LIST).size *= 2; \ + } \ + psym = (LIST).next++; \ + \ + SYMBOL_NAME (psym) = (char *) obstack_alloc (psymbol_obstack, \ + (NAMELENGTH) + 1); \ + strncpy (SYMBOL_NAME (psym), (NAME), (NAMELENGTH)); \ + SYMBOL_NAME (psym)[(NAMELENGTH)] = '\0'; \ + SYMBOL_NAMESPACE (psym) = (NAMESPACE); \ + SYMBOL_CLASS (psym) = (CLASS); \ + SYMBOL_VALUE (psym) = (VALUE); \ + } while (0); + + + switch (bufp->n_type) + { + /* + * Standard, non-debugger, symbols + */ + + case N_TEXT | N_EXT: + /* Catch etext */ + + SET_NAMESTRING(); + + if (namestring[6] == '\0' && namestring[5] == 't' + && namestring[4] == 'x' && namestring[3] == 'e' + && namestring[2] == 't' && namestring[1] == 'e' + && namestring[0] == '_') + end_of_text_addr = bufp->n_value; + + /* Figure out beginning and end of global linker symbol + section and put non-debugger specified symbols on + tmp_symchain */ + + last_global_sym = symnum; + if (!first_global_sym) first_global_sym = symnum; + + record_misc_function (namestring, bufp->n_value, + bufp->n_type); /* Always */ + + continue; + +#ifdef N_NBTEXT + case N_NBTEXT | N_EXT: +#endif +#ifdef N_NBDATA + case N_NBDATA | N_EXT: +#endif +#ifdef N_NBBSS + case N_NBBSS | N_EXT: +#endif +#ifdef N_SETV + case N_SETV | N_EXT: +#endif + case N_ABS | N_EXT: + case N_DATA | N_EXT: + case N_BSS | N_EXT: + /* Figure out beginning and end of global linker symbol + section and put non-debugger specified symbols on + tmp_symchain */ + + SET_NAMESTRING(); + + last_global_sym = symnum; + if (!first_global_sym) first_global_sym = symnum; + + /* Not really a function here, but... */ + record_misc_function (namestring, bufp->n_value, + bufp->n_type); /* Always */ + + continue; + +#ifdef N_NBTEXT + case N_NBTEXT: +#endif + + /* We need to be able to deal with both N_FN or N_TEXT, + because we have no way of knowing whether the sys-supplied ld + or GNU ld was used to make the executable. */ +#if ! (N_FN & N_EXT) + case N_FN: +#endif + case N_FN | N_EXT: + case N_TEXT: + SET_NAMESTRING(); + if ((namestring[0] == '-' && namestring[1] == 'l') + || (namestring [(nsl = strlen (namestring)) - 1] == 'o' + && namestring [nsl - 2] == '.')) + { + if (entry_point < bufp->n_value + && entry_point >= last_o_file_start) + { + startup_file_start = last_o_file_start; + startup_file_end = bufp->n_value; + } + if (past_first_source_file && pst) + { + end_psymtab (pst, psymtab_include_list, includes_used, + symnum * sizeof (struct nlist), bufp->n_value, + dependency_list, dependencies_used, + global_psymbols.next, static_psymbols.next); + pst = (struct partial_symtab *) 0; + includes_used = 0; + dependencies_used = 0; + } + else + past_first_source_file = 1; + last_o_file_start = bufp->n_value; + last_o_file_name = namestring; + nsl = strlen(namestring); + if (namestring[nsl-2] == '.' && namestring[nsl-1] == 'o') + namestring[nsl-2] = 0; + } + else if (strcmp(namestring, "gcc_compiled.")) + { + if (*namestring == '_') + ++namestring; + namestring = obconcat(last_o_file_name, ":", namestring); + last_global_sym = symnum; + if (!first_global_sym) + first_global_sym = symnum; + record_misc_function(namestring, bufp->n_value, bufp->n_type); + } + continue; + + case N_ABS: + case N_DATA: + case N_BSS: + SET_NAMESTRING(); + if (*namestring == '_') + ++namestring; + namestring = obconcat(last_o_file_name, ":", namestring); + last_global_sym = symnum; + if (!first_global_sym) + first_global_sym = symnum; + record_misc_function(namestring, bufp->n_value, bufp->n_type); + continue; + + case N_UNDF: + case N_UNDF | N_EXT: +#ifdef N_NBDATA + case N_NBDATA: +#endif +#ifdef N_NBBSS + case N_NBBSS: +#endif + + /* Keep going . . .*/ + + /* + * Special symbol types for GNU + */ +#ifdef N_INDR + case N_INDR: + case N_INDR | N_EXT: +#endif +#ifdef N_SETA + case N_SETA: + case N_SETA | N_EXT: + case N_SETT: + case N_SETT | N_EXT: + case N_SETD: + case N_SETD | N_EXT: + case N_SETB: + case N_SETB | N_EXT: + case N_SETV: +#endif + continue; + + /* + * Debugger symbols + */ + + case N_SO: + /* End the current partial symtab and start a new one */ + + SET_NAMESTRING(); + + if (pst && past_first_source_file) + { + end_psymtab (pst, psymtab_include_list, includes_used, + symnum * sizeof (struct nlist), bufp->n_value, + dependency_list, dependencies_used, + global_psymbols.next, static_psymbols.next); + pst = (struct partial_symtab *) 0; + includes_used = 0; + dependencies_used = 0; + } + else + past_first_source_file = 1; + + pst = start_psymtab (namestring, bufp->n_value, + symnum * sizeof (struct nlist), + global_psymbols.next, static_psymbols.next); + + continue; + +#ifdef N_BINCL + case N_BINCL: + /* Add this bincl to the bincl_list for future EXCLs. No + need to save the string; it'll be around until + read_dbx_symtab function return */ + + SET_NAMESTRING(); + + add_bincl_to_list (pst, namestring, bufp->n_value); + + /* Mark down an include file in the current psymtab */ + + psymtab_include_list[includes_used++] = namestring; + if (includes_used >= includes_allocated) + { + char **orig = psymtab_include_list; + + psymtab_include_list = (char **) + alloca ((includes_allocated *= 2) * + sizeof (char *)); + bcopy (orig, psymtab_include_list, + includes_used * sizeof (char *)); + } + + continue; +#endif + + case N_SOL: + /* Mark down an include file in the current psymtab */ + + SET_NAMESTRING(); + + /* In C++, one may expect the same filename to come round many + times, when code is coming alternately from the main file + and from inline functions in other files. So I check to see + if this is a file we've seen before. + + This seems to be a lot of time to be spending on N_SOL, but + things like "break expread.y:435" need to work (I + suppose the psymtab_include_list could be hashed or put + in a binary tree, if profiling shows this is a major hog). */ + { + register int i; + for (i = 0; i < includes_used; i++) + if (!strcmp (namestring, psymtab_include_list[i])) + { + i = -1; + break; + } + if (i == -1) + continue; + } + + psymtab_include_list[includes_used++] = namestring; + if (includes_used >= includes_allocated) + { + char **orig = psymtab_include_list; + + psymtab_include_list = (char **) + alloca ((includes_allocated *= 2) * + sizeof (char *)); + bcopy (orig, psymtab_include_list, + includes_used * sizeof (char *)); + } + continue; + + case N_LSYM: /* Typedef or automatic variable. */ + SET_NAMESTRING(); + + p = (char *) index (namestring, ':'); + + /* Skip if there is no :. */ + if (!p) continue; + + switch (p[1]) + { + case 'T': + ADD_PSYMBOL_TO_LIST (namestring, p - namestring, + STRUCT_NAMESPACE, LOC_TYPEDEF, + static_psymbols, bufp->n_value); + goto check_enum; + case 't': + ADD_PSYMBOL_TO_LIST (namestring, p - namestring, + VAR_NAMESPACE, LOC_TYPEDEF, + static_psymbols, bufp->n_value); + check_enum: + /* If this is an enumerated type, we need to + add all the enum constants to the partial symbol + table. This does not cover enums without names, e.g. + "enum {a, b} c;" in C, but fortunately those are + rare. There is no way for GDB to find those from the + enum type without spending too much time on it. Thus + to solve this problem, the compiler needs to put out separate + constant symbols ('c' N_LSYMS) for enum constants in + enums without names. */ + + /* We are looking for something of the form + ":" ("t" | "T") [ "="] "e" + { ":" ","} ";". */ + + /* Skip over the colon and the 't' or 'T'. */ + p += 2; + /* This type may be given a number. Skip over it. */ + while ((*p >= '0' && *p <= '9') + || *p == '=') + p++; + + if (*p++ == 'e') + { + /* We have found an enumerated type. */ + /* According to comments in read_enum_type + a comma could end it instead of a semicolon. + I don't know where that happens. + Accept either. */ + while (*p && *p != ';' && *p != ',') + { + char *q; + + /* Check for and handle cretinous dbx symbol name + continuation! */ + if (*p == '\\') + p = next_symbol_text (); + + /* Point to the character after the name + of the enum constant. */ + for (q = p; *q && *q != ':'; q++) + ; + /* Note that the value doesn't matter for + enum constants in psymtabs, just in symtabs. */ + ADD_PSYMBOL_TO_LIST (p, q - p, + VAR_NAMESPACE, LOC_CONST, + static_psymbols, 0); + /* Point past the name. */ + p = q; + /* Skip over the value. */ + while (*p && *p != ',') + p++; + /* Advance past the comma. */ + if (*p) + p++; + } + } + + continue; + case 'c': + /* Constant, e.g. from "const" in Pascal. */ + ADD_PSYMBOL_TO_LIST (namestring, p - namestring, + VAR_NAMESPACE, LOC_CONST, + static_psymbols, bufp->n_value); + continue; + default: +#ifdef PROFILE_TYPES + if (isalpha(p[1])) + printf ("Funny...LSYM with a letter that isn't a type\n"); + autovars++; +#endif + /* Skip if the thing following the : is + not a letter (which indicates declaration of a local + variable, which we aren't interested in). */ + continue; + } + + case N_FUN: +#if 0 + /* This special-casing of N_FUN is just wrong; N_FUN + does not mean "function"; it means "text segment". + So N_FUN can go with 'V', etc. as well as 'f' or 'F'. */ + + SET_NAMESTRING(); + + p = (char *) index (namestring, ':'); + + if (!p || p[1] == 'F') continue; + +#ifdef PROFILE_TYPES + if (p[1] != 'f') + printf ("Funny...FUN with a letter that isn't 'F' or 'f'.\n"); + global_funs++; +#endif + + ADD_PSYMBOL_TO_LIST (namestring, p - namestring, + VAR_NAMESPACE, LOC_BLOCK, + static_psymbols, bufp->n_value); + + continue; +#endif /* 0 */ + case N_GSYM: /* Global (extern) variable; can be + data or bss (sigh). */ + case N_STSYM: /* Data seg var -- static */ + case N_LCSYM: /* BSS " */ + + /* Following may probably be ignored; I'll leave them here + for now (until I do Pascal and Modula 2 extensions). */ + + case N_PC: /* I may or may not need this; I + suspect not. */ +#ifdef N_M2C + case N_M2C: /* I suspect that I can ignore this here. */ + case N_SCOPE: /* Same. */ +#endif + + SET_NAMESTRING(); + + p = (char *) index (namestring, ':'); + if (!p) + continue; /* Not a debugging symbol. */ + + process_symbol_for_psymtab: + + /* Main processing section for debugging symbols which + the initial read through the symbol tables needs to worry + about. If we reach this point, the symbol which we are + considering is definitely one we are interested in. + p must also contain the (valid) index into the namestring + which indicates the debugging type symbol. */ + + switch (p[1]) + { + case 'c': + ADD_PSYMBOL_TO_LIST (namestring, p - namestring, + VAR_NAMESPACE, LOC_CONST, + static_psymbols, bufp->n_value); + continue; + case 'S': + ADD_PSYMBOL_TO_LIST (namestring, p - namestring, + VAR_NAMESPACE, LOC_STATIC, + static_psymbols, bufp->n_value); + continue; + case 'G': + ADD_PSYMBOL_TO_LIST (namestring, p - namestring, + VAR_NAMESPACE, LOC_EXTERNAL, + global_psymbols, bufp->n_value); + continue; + + case 't': + ADD_PSYMBOL_TO_LIST (namestring, p - namestring, + VAR_NAMESPACE, LOC_TYPEDEF, + global_psymbols, bufp->n_value); + continue; + + case 'f': + ADD_PSYMBOL_TO_LIST (namestring, p - namestring, + VAR_NAMESPACE, LOC_BLOCK, + static_psymbols, bufp->n_value); + continue; + + /* Two things show up here (hopefully); static symbols of + local scope (static used inside braces) or extensions + of structure symbols. We can ignore both. */ + case 'V': + case '(': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* Global functions are ignored here. I'm not + sure what psymtab they go into (or just the misc + function vector). */ + case 'F': + continue; + + default: + fatal ("Internal error: Unexpected debugging symbol type '%c' at symnum %d.\n", + p[1], symnum); + } + +#ifdef N_BINCL + case N_EXCL: + + SET_NAMESTRING(); + + /* Find the corresponding bincl and mark that psymtab on the + psymtab dependency list */ + { + struct partial_symtab *needed_pst = + find_corresponding_bincl_psymtab (namestring, bufp->n_value); + + /* If this include file was defined earlier in this file, + leave it alone. */ + if (needed_pst == pst) continue; + + if (needed_pst) + { + int i; + int found = 0; + + for (i = 0; i < dependencies_used; i++) + if (dependency_list[i] == needed_pst) + { + found = 1; + break; + } + + /* If it's already in the list, skip the rest. */ + if (found) continue; + + dependency_list[dependencies_used++] = needed_pst; + if (dependencies_used >= dependencies_allocated) + { + struct partial_symtab **orig = dependency_list; + dependency_list = + (struct partial_symtab **) + alloca ((dependencies_allocated *= 2) + * sizeof (struct partial_symtab *)); + bcopy (orig, dependency_list, + (dependencies_used + * sizeof (struct partial_symtab *))); +#ifdef DEBUG_INFO + fprintf (stderr, "Had to reallocate dependency list.\n"); + fprintf (stderr, "New dependencies allocated: %d\n", + dependencies_allocated); +#endif + } + } + else + error ("Invalid symbol data: \"repeated\" header file not previously seen, at symtab pos %d.", + symnum); + } + continue; + + case N_EINCL: +#endif +#ifdef N_DSLINE + case N_DSLINE: +#endif +#ifdef N_BSLINE + case N_BSLINE: +#endif + case N_SSYM: /* Claim: Structure or union element. + Hopefully, I can ignore this. */ + case N_ENTRY: /* Alternate entry point; can ignore. */ +#ifdef N_MAIN + case N_MAIN: /* Can definitely ignore this. */ +#endif + case N_LENG: + case N_BCOMM: + case N_ECOMM: + case N_ECOML: + case N_FNAME: + case N_SLINE: + case N_RSYM: + case N_PSYM: + case N_LBRAC: + case N_RBRAC: + /* These symbols aren't interesting; don't worry about them */ + + continue; + + default: + /* If we haven't found it yet, we've got problems */ + + if (IGNORE_SYMBOL (bufp->n_type)) + continue; + + fatal ("Bad symbol type 0x%x encountered in gdb scan", bufp->n_type); + } + } + + /* If there's stuff to be cleaned up, clean it up. */ + if (entry_point < bufp->n_value + && entry_point >= last_o_file_start) + { + startup_file_start = last_o_file_start; + startup_file_end = bufp->n_value; + } + + if (pst) + { + end_psymtab (pst, psymtab_include_list, includes_used, + symnum * sizeof (struct nlist), end_of_text_addr, + dependency_list, dependencies_used, + global_psymbols.next, static_psymbols.next); + includes_used = 0; + dependencies_used = 0; + pst = (struct partial_symtab *) 0; + } + + /* sort the global & static symtab list so we can binary search them */ + qsort (global_psymbols.list, global_psymbols.next - global_psymbols.list, + sizeof (struct partial_symbol), compare_psymbols); + qsort (static_psymbols.list, static_psymbols.next - static_psymbols.list, + sizeof (struct partial_symbol), compare_psymbols); + free_bincl_list (); + discard_cleanups (old_chain); +#ifdef PROFILE_TYPES + { + int i, j; +#define __define_stab(SYM, NUMBER, NAME) {NUMBER, NAME}, + static struct xyzzy { + unsigned char symnum; + char *name; + } tmp_list[] = { +#include "stab.def" + {0x1, "eREF"}, + {0x2, "ABS"}, + {0x3, "eABS"}, + {0x4, "TEXT"}, + {0x5, "eTEXT"}, + {0x6, "DATA"}, + {0x7, "eDATA"}, + {0x8, "BSS"}, + {0x9, "eBSS"}, + {0x12, "COMM"}, + {0x13, "eCOMM"}, + {0x1f, "FN"}, + {0, "Unknown"}, +}; + for (i = 0; i < 256; i++) + { + for (j = 0; j < (sizeof (tmp_list) / sizeof (struct xyzzy)) - 1; j++) + if (tmp_list[j].symnum == i) + break; + printf ("Symbol \"%s\" (0x%x) occured %d times.\n", + tmp_list[j].name, i, profile_types[i]); + } + printf ("Auto vars (under LSYM): %d\n", autovars); + printf ("Global funs (under FUN): %d\n", global_funs); + } +#endif +} + +/* + * Allocate and partially fill a partial symtab. It will be + * completely filled at the end of the symbol list. + */ +static struct partial_symtab * +start_psymtab (filename, textlow, ldsymoff, global_syms, static_syms) + char *filename; + int textlow; + int ldsymoff; + struct partial_symbol *global_syms; + struct partial_symbol *static_syms; +{ + struct partial_symtab *result = + (struct partial_symtab *) obstack_alloc (psymbol_obstack, + sizeof (struct partial_symtab)); + + result->filename = + (char *) obstack_alloc (psymbol_obstack, + strlen (filename) + 1); + strcpy (result->filename, filename); + + result->textlow = textlow; + result->ldsymoff = ldsymoff; + + result->readin = 0; + + result->globals_offset = global_syms - global_psymbols.list; + result->statics_offset = static_syms - static_psymbols.list; + + result->n_global_syms = 0; + result->n_static_syms = 0; + + return result; +} + + +/* Close off the current usage of a partial_symbol table entry. This + involves setting the correct number of includes (with a realloc), + setting the high text mark, setting the symbol length in the + executable, and setting the length of the global and static lists + of psymbols. + + The global symbols and static symbols are then seperately sorted. + + Then the partial symtab is put on the global list. + *** List variables and peculiarities of same. *** + */ +static void +end_psymtab (pst, include_list, num_includes, capping_symbol_offset, + capping_text, dependency_list, number_dependencies, + capping_global, capping_static) + struct partial_symtab *pst; + char **include_list; + int num_includes; + int capping_symbol_offset; + int capping_text; + struct partial_symtab **dependency_list; + int number_dependencies; + struct partial_symbol *capping_global, *capping_static; +{ + int i; + register struct partial_symbol *ps; + + pst->ldsymlen = capping_symbol_offset - pst->ldsymoff; + pst->texthigh = capping_text; + + pst->n_global_syms = + capping_global - (global_psymbols.list + pst->globals_offset); + pst->n_static_syms = + capping_static - (static_psymbols.list + pst->statics_offset); + + pst->dependencies = (struct partial_symtab **) + obstack_alloc (psymbol_obstack, + number_dependencies * sizeof (struct partial_symtab *)); + bcopy (dependency_list, pst->dependencies, + number_dependencies * sizeof (struct partial_symtab *)); + pst->number_of_dependencies = number_dependencies; + + for (i = 0; i < num_includes; i++) + { + /* Eventually, put this on obstack */ + struct partial_symtab *subpst = + (struct partial_symtab *) + obstack_alloc (psymbol_obstack, + sizeof (struct partial_symtab)); + + subpst->filename = + (char *) obstack_alloc (psymbol_obstack, + strlen (include_list[i]) + 1); + strcpy (subpst->filename, include_list[i]); + + subpst->ldsymoff = + subpst->ldsymlen = + subpst->textlow = + subpst->texthigh = 0; + subpst->readin = 0; + + subpst->dependencies = (struct partial_symtab **) + obstack_alloc (psymbol_obstack, + sizeof (struct partial_symtab *)); + subpst->dependencies[0] = pst; + subpst->number_of_dependencies = 1; + + subpst->globals_offset = + subpst->n_global_syms = + subpst->statics_offset = + subpst->n_static_syms = 0; + + subpst->next = partial_symtab_list; + partial_symtab_list = subpst; + } + + for (ps = global_psymbols.list + pst->globals_offset; + ps < capping_global; ++ps) + ps->pst = pst; + for (ps = static_psymbols.list + pst->statics_offset; + ps < capping_static; ++ps) + ps->pst = pst; + + /* Put the psymtab on the psymtab list */ + pst->next = partial_symtab_list; + partial_symtab_list = pst; +} + + +/* Helper routines for psymtab_to_symtab. */ +static void scan_file_globals (); +static void read_ofile_symtab (); + +static void +psymtab_to_symtab_1 (pst, desc, stringtab, stringtab_size, sym_offset) + struct partial_symtab *pst; + int desc; + char *stringtab; + int stringtab_size; + int sym_offset; +{ + struct cleanup *old_chain; + int i; + + if (!pst) + return; + + if (pst->readin) + { + fprintf (stderr, "Psymtab for %s already read in. Shouldn't happen.\n", + pst->filename); + return; + } + + /* Read in all partial symbtabs on which this one is dependent */ + for (i = 0; i < pst->number_of_dependencies; i++) + if (!pst->dependencies[i]->readin) + { + /* Inform about additional files that need to be read in. */ + if (info_verbose) + { + printf_filtered (" and %s...", pst->dependencies[i]->filename); + fflush (stdout); + } + psymtab_to_symtab_1 (pst->dependencies[i], desc, + stringtab, stringtab_size, sym_offset); + } + + if (pst->ldsymlen) /* Otherwise it's a dummy */ + { + /* Init stuff necessary for reading in symbols */ + free_pendings = 0; + pending_blocks = 0; + file_symbols = 0; + global_symbols = 0; + old_chain = make_cleanup (really_free_pendings, 0); + + /* Read in this files symbols */ + lseek (desc, sym_offset, L_SET); + read_ofile_symtab (desc, stringtab, stringtab_size, + pst->ldsymoff, + pst->ldsymlen, pst->textlow, + pst->texthigh - pst->textlow, 0); + sort_symtab_syms (symtab_list); /* At beginning since just added */ + + do_cleanups (old_chain); + } + + pst->readin = 1; +} + +/* + * Read in all of the symbols for a given psymtab for real. Return + * the value of the symtab you create. Do not free the storage + * allocated to the psymtab; it may have pointers to it. + */ +struct symtab * +psymtab_to_symtab(pst) + struct partial_symtab *pst; +{ + int desc; + DECLARE_FILE_HEADERS; + char *stringtab; + struct partial_symtab **list_patch; + int stsize, val; + struct stat statbuf; + struct cleanup *old_chain; + extern void close (); + int i; + struct symtab *result; + char *name = symfile; /* Some of the macros require the */ + /* variable "name" to be defined in */ + /* the context in which they execute */ + /* (Yech!) */ + + if (!pst) + return 0; + + if (pst->readin) + { + fprintf (stderr, "Psymtab for %s already read in. Shouldn't happen.\n", + pst->filename); + return 0; + } + + if (!name) + error("No symbol file currently specified; use command symbol-file"); + + if (pst->ldsymlen || pst->number_of_dependencies) + { + /* Print the message now, before reading the string table, + to avoid disconcerting pauses. */ + if (info_verbose) + { + printf_filtered ("Reading in symbols for %s...", pst->filename); + fflush (stdout); + } + + /* Open symbol file and read in string table */ + if (stat (name, &statbuf) < 0) + perror_with_name (name); + desc = open(name, O_RDONLY, 0); /* symbol_file_command + guarrantees that the symbol file name + will be absolute, so there is no + need for openp */ + + old_chain = make_cleanup (close, desc); + + if (desc < 0) + error("Symbol file not readable"); + + READ_FILE_HEADERS (desc, name); + +#if 0 + /* Read in the string table */ + lseek (desc, STRING_TABLE_OFFSET, L_SET); + READ_STRING_TABLE_SIZE (stsize); + if (stsize >= 0 && stsize < statbuf.st_size) + { +#ifdef BROKEN_LARGE_ALLOCA + stringtab = (char *) xmalloc (stsize); + make_cleanup (free, stringtab); +#else + stringtab = (char *) alloca (stsize); +#endif + } + else + stringtab = NULL; + if (stringtab == NULL) + error ("ridiculous string table size: %d bytes", stsize); + + /* Usually READ_STRING_TABLE_SIZE will have shifted the file pointer. + Occaisionally, it won't. */ + val = lseek (desc, STRING_TABLE_OFFSET, L_SET); + if (val < 0) + perror_with_name (name); + val = myread (desc, stringtab, stsize); + if (val < 0) + perror_with_name (name); +#endif /* 0 */ + stringtab = symfile_string_table; + stsize = symfile_string_table_size; + + psymtab_to_symtab_1 (pst, desc, stringtab, stsize, + SYMBOL_TABLE_OFFSET); + + /* Match with global symbols. This only needs to be done once, + after all of the symtabs and dependencies have been read in. */ + scan_file_globals (); + + do_cleanups (old_chain); + + /* Finish up the debug error message. */ + if (info_verbose) + printf_filtered ("done.\n"); + } + + /* Search through list for correct name. */ + for (result = symtab_list; result; result = result->next) + if (!strcmp (result->filename, pst->filename)) + return result; + + return 0; +} + +/* + * Scan through all of the global symbols defined in the object file, + * assigning values to the debugging symbols that need to be assigned + * to. Get these symbols from the misc function list. + */ +static void +scan_file_globals () +{ + int hash; + int mf; + + for (mf = 0; mf < misc_function_count; mf++) + { + char *namestring = misc_function_vector[mf].name; + struct symbol *sym, *prev; + + QUIT; + + prev = (struct symbol *) 0; + + /* Get the hash index and check all the symbols + under that hash index. */ + + hash = hashname (namestring); + + for (sym = global_sym_chain[hash]; sym;) + { + if (*namestring == SYMBOL_NAME (sym)[0] + && !strcmp(namestring + 1, SYMBOL_NAME (sym) + 1)) + { + /* Splice this symbol out of the hash chain and + assign the value we have to it. */ + if (prev) + SYMBOL_VALUE (prev) = SYMBOL_VALUE (sym); + else + global_sym_chain[hash] + = (struct symbol *) SYMBOL_VALUE (sym); + + /* Check to see whether we need to fix up a common block. */ + /* Note: this code might be executed several times for + the same symbol if there are multiple references. */ + if (SYMBOL_CLASS (sym) == LOC_BLOCK) + fix_common_block (sym, misc_function_vector[mf].address); + else + SYMBOL_VALUE (sym) = misc_function_vector[mf].address; + + if (prev) + sym = (struct symbol *) SYMBOL_VALUE (prev); + else + sym = global_sym_chain[hash]; + } + else + { + prev = sym; + sym = (struct symbol *) SYMBOL_VALUE (sym); + } + } + } +} + +/* + * Read in a defined section of a specific object file's symbols. + * + * DESC is the file descriptor for the file, positioned at the + * beginning of the symtab + * STRINGTAB is a pointer to the files string + * table, already read in + * SYM_OFFSET is the offset within the file of + * the beginning of the symbols we want to read, NUM_SUMBOLS is the + * number of symbols to read + * TEXT_OFFSET is the offset to be added to + * all values of symbols coming in and + * TEXT_SIZE is the size of the text segment read in. + * OFFSET is a flag which indicates that the value of all of the + * symbols should be offset by TEXT_OFFSET (for the purposes of + * incremental linking). + */ + +static void +read_ofile_symtab (desc, stringtab, stringtab_size, sym_offset, + sym_size, text_offset, text_size, offset) + int desc; + register char *stringtab; + int sym_offset; + int sym_size; + int text_offset; + int text_size; + int offset; +{ + register char *namestring; + register struct symbol *sym, *prev; + int hash; + struct cleanup *old_chain; + struct nlist *bufp; + unsigned char type; +#ifdef N_BINCL + subfile_stack = 0; +#endif + + stringtab_global = stringtab; + last_source_file = 0; + + symtab_input_desc = desc; + symbuf_end = symbuf_idx = 0; + + /* It is necessary to actually read one symbol *before* the start + of this symtab's symbols, because the GCC_COMPILED_FLAG_SYMBOL + occurs before the N_SO symbol. + + Detecting this in read_dbx_symtab + would slow down initial readin, so we look for it here instead. */ + if (sym_offset >= sizeof (struct nlist)) + { + lseek (desc, sym_offset - sizeof (struct nlist), L_INCR); + fill_symbuf (); + bufp = &symbuf[symbuf_idx++]; + + if (bufp->n_un.n_strx < 0 || bufp->n_un.n_strx >= stringtab_size) + error ("Invalid symbol data: bad string table offset: %d", + bufp->n_un.n_strx); + namestring = bufp->n_un.n_strx + stringtab; + + processing_gcc_compilation = + (bufp->n_type == N_TEXT + && !strcmp (namestring, GCC_COMPILED_FLAG_SYMBOL)); + } + else + { + /* The N_SO starting this symtab is the first symbol, so we + better not check the symbol before it. I'm not this can + happen, but it doesn't hurt to check for it. */ + lseek(desc, sym_offset, L_INCR); + processing_gcc_compilation = 0; + } + + if (symbuf_idx == symbuf_end) + fill_symbuf(); + bufp = &symbuf[symbuf_idx]; + if ((unsigned char) bufp->n_type != N_SO) + fatal("First symbol in segment of executable not a source symbol"); + + for (symnum = 0; + symnum < sym_size / sizeof(struct nlist); + symnum++) + { + QUIT; /* Allow this to be interruptable */ + if (symbuf_idx == symbuf_end) + fill_symbuf(); + bufp = &symbuf[symbuf_idx++]; + type = bufp->n_type; + + if (offset && + (type == N_TEXT || type == N_DATA || type == N_BSS)) + bufp->n_value += text_offset; + + if (bufp->n_un.n_strx < 0 || bufp->n_un.n_strx >= stringtab_size) + error ("Invalid symbol data: bad string table offset: %d", + bufp->n_un.n_strx); + namestring = bufp->n_un.n_strx + stringtab; + + if (type & N_STAB) + process_one_symbol(type, bufp->n_desc, + bufp->n_value, namestring); + /* We skip checking for a new .o or -l file; that should never + happen in this routine. */ + else if (type == N_TEXT + && !strcmp (namestring, GCC_COMPILED_FLAG_SYMBOL)) + /* I don't think this code will ever be executed, because + the GCC_COMPILED_FLAG_SYMBOL usually is right before + the N_SO symbol which starts this source file. + However, there is no reason not to accept + the GCC_COMPILED_FLAG_SYMBOL anywhere. */ + processing_gcc_compilation = 1; + else if (type & N_EXT || type == N_TEXT +#ifdef N_NBTEXT + || type == N_NBTEXT +#endif + ) + /* Global symbol: see if we came across a dbx defintion for + a corresponding symbol. If so, store the value. Remove + syms from the chain when their values are stored, but + search the whole chain, as there may be several syms from + different files with the same name. */ + /* This is probably not true. Since the files will be read + in one at a time, each reference to a global symbol will + be satisfied in each file as it appears. So we skip this + section. */ + &stringtab_global; /* For debugger; am I right? */ + } + end_symtab (text_offset + text_size); +} + +static int +hashname (name) + char *name; +{ + register char *p = name; + register int total = p[0]; + register int c; + + c = p[1]; + total += c << 2; + if (c) + { + c = p[2]; + total += c << 4; + if (c) + total += p[3] << 6; + } + + /* Ensure result is positive. */ + if (total < 0) total += (1000 << 6); + return total % HASHSIZE; +} + +/* Put all appropriate global symbols in the symseg data + onto the hash chains so that their addresses will be stored + when seen later in loader global symbols. */ + +static void +hash_symsegs () +{ + /* Look at each symbol in each block in each symseg symtab. */ + struct symtab *s; + for (s = symseg_chain; s; s = s->next) + { + register int n; + for (n = BLOCKVECTOR_NBLOCKS (BLOCKVECTOR (s)) - 1; n >= 0; n--) + { + register struct block *b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), n); + register int i; + for (i = BLOCK_NSYMS (b) - 1; i >= 0; i--) + { + register struct symbol *sym = BLOCK_SYM (b, i); + + /* Put the symbol on a chain if its value is an address + that is figured out by the loader. */ + + if (SYMBOL_CLASS (sym) == LOC_EXTERNAL) + { + register int hash = hashname (SYMBOL_NAME (sym)); + SYMBOL_VALUE (sym) = (int) global_sym_chain[hash]; + global_sym_chain[hash] = sym; + SYMBOL_CLASS (sym) = LOC_STATIC; + } + } + } + } +} + +static void +process_one_symbol (type, desc, value, name) + int type, desc; + CORE_ADDR value; + char *name; +{ + register struct context_stack *new; + char *colon_pos; + + /* Something is wrong if we see real data before + seeing a source file name. */ + + if (last_source_file == 0 && type != N_SO) + { + /* Currently this ignores N_ENTRY on Gould machines, N_NSYM on machines + where that code is defined. */ + if (IGNORE_SYMBOL (type)) + return; + + error ("Invalid symbol data: does not start by identifying a source file."); + } + + switch (type) + { + case N_FUN: + case N_FNAME: + /* Either of these types of symbols indicates the start of + a new function. We must process its "name" normally for dbx, + but also record the start of a new lexical context, and possibly + also the end of the lexical context for the previous function. */ + /* This is not always true. This type of symbol may indicate a + text segment variable. */ + + colon_pos = index (name, ':'); + if (!colon_pos++ + || (*colon_pos != 'f' && *colon_pos != 'F')) + { + define_symbol (value, name, desc); + break; + } + + within_function = 1; + if (context_stack_depth > 0) + { + new = &context_stack[--context_stack_depth]; + /* Make a block for the local symbols within. */ + finish_block (new->name, &local_symbols, new->old_blocks, + new->start_addr, value); + } + /* Stack must be empty now. */ + if (context_stack_depth != 0) + error ("Invalid symbol data: unmatched N_LBRAC before symtab pos %d.", + symnum); + + new = &context_stack[context_stack_depth++]; + new->old_blocks = pending_blocks; + new->start_addr = value; + new->name = define_symbol (value, name, desc); + local_symbols = 0; + break; + + case N_LBRAC: + /* This "symbol" just indicates the start of an inner lexical + context within a function. */ + + if (context_stack_depth == context_stack_size) + { + context_stack_size *= 2; + context_stack = (struct context_stack *) + xrealloc (context_stack, + (context_stack_size + * sizeof (struct context_stack))); + } + + new = &context_stack[context_stack_depth++]; + new->depth = desc; + new->locals = local_symbols; + new->old_blocks = pending_blocks; + new->start_addr = value; + new->name = 0; + local_symbols = 0; + break; + + case N_RBRAC: + /* This "symbol" just indicates the end of an inner lexical + context that was started with N_LBRAC. */ + new = &context_stack[--context_stack_depth]; + if (desc != new->depth) + error ("Invalid symbol data: N_LBRAC/N_RBRAC symbol mismatch, symtab pos %d.", symnum); + + /* Some native compilers put the variable decls inside of an + LBRAC/RBRAC block. This macro should be nonzero if this + is true. DESC is N_DESC from the N_RBRAC symbol. */ +#if !defined (VARIABLES_INSIDE_BLOCK) +#define VARIABLES_INSIDE_BLOCK(desc) 0 +#endif + + /* Can only use new->locals as local symbols here if we're in + gcc or on a machine that puts them before the lbrack. */ + if (!VARIABLES_INSIDE_BLOCK(desc)) + local_symbols = new->locals; + + /* If this is not the outermost LBRAC...RBRAC pair in the + function, its local symbols preceded it, and are the ones + just recovered from the context stack. Defined the block for them. + + If this is the outermost LBRAC...RBRAC pair, there is no + need to do anything; leave the symbols that preceded it + to be attached to the function's own block. However, if + it is so, we need to indicate that we just moved outside + of the function. */ + if (local_symbols + && context_stack_depth > !VARIABLES_INSIDE_BLOCK(desc)) + { + /* Muzzle a compiler bug that makes end < start. */ + if (new->start_addr > value) + new->start_addr = value; + /* Make a block for the local symbols within. */ + finish_block (0, &local_symbols, new->old_blocks, + new->start_addr + last_source_start_addr, + value + last_source_start_addr); + } + else + { + within_function = 0; + } + if (VARIABLES_INSIDE_BLOCK(desc)) + /* Now pop locals of block just finished. */ + local_symbols = new->locals; + break; + + case N_FN | N_EXT: + /* This kind of symbol supposedly indicates the start + of an object file. In fact this type does not appear. */ + break; + + case N_SO: + /* This type of symbol indicates the start of data + for one source file. + Finish the symbol table of the previous source file + (if any) and start accumulating a new symbol table. */ +#ifdef PCC_SOL_BROKEN + /* pcc bug, occasionally puts out SO for SOL. */ + if (context_stack_depth > 0) + { + start_subfile (name); + break; + } +#endif + if (last_source_file) + end_symtab (value); + start_symtab (name, value); + break; + + case N_SOL: + /* This type of symbol indicates the start of data for + a sub-source-file, one whose contents were copied or + included in the compilation of the main source file + (whose name was given in the N_SO symbol.) */ + start_subfile (name); + break; + +#ifdef N_BINCL + case N_BINCL: + push_subfile (); + add_new_header_file (name, value); + start_subfile (name); + break; + + case N_EINCL: + start_subfile (pop_subfile ()); + break; + + case N_EXCL: + add_old_header_file (name, value); + break; +#endif /* have N_BINCL */ + + case N_SLINE: + /* This type of "symbol" really just records + one line-number -- core-address correspondence. + Enter it in the line list for this symbol table. */ + record_line (desc, value); + break; + + case N_BCOMM: + if (common_block) + error ("Invalid symbol data: common within common at symtab pos %d", + symnum); + common_block = local_symbols; + common_block_i = local_symbols ? local_symbols->nsyms : 0; + break; + + case N_ECOMM: + /* Symbols declared since the BCOMM are to have the common block + start address added in when we know it. common_block points to + the first symbol after the BCOMM in the local_symbols list; + copy the list and hang it off the symbol for the common block name + for later fixup. */ + { + int i; + struct pending *link = local_symbols; + struct symbol *sym = + (struct symbol *) xmalloc (sizeof (struct symbol)); + bzero (sym, sizeof *sym); + SYMBOL_NAME (sym) = savestring (name, strlen (name)); + SYMBOL_CLASS (sym) = LOC_BLOCK; + SYMBOL_NAMESPACE (sym) = (enum namespace)((long) + copy_pending (local_symbols, common_block_i, common_block)); + i = hashname (SYMBOL_NAME (sym)); + SYMBOL_VALUE (sym) = (int) global_sym_chain[i]; + global_sym_chain[i] = sym; + common_block = 0; + break; + } + + case N_ECOML: + case N_LENG: + break; + + default: + if (name) + define_symbol (value, name, desc); + } +} + +/* This function was added for C++ functionality. I presume that it + condenses the bunches formed by reading in an additional .o file + (incremental linking). */ + +static void +condense_addl_misc_bunches () +{ + register int i, j; + register struct misc_bunch *bunch; +#ifdef NAMES_HAVE_UNDERSCORE + int offset = 1; +#else + int offset = 0; +#endif + + misc_function_vector + = (struct misc_function *) xrealloc (misc_function_vector, + (misc_count + misc_function_count) * sizeof (struct misc_function)); + + j = misc_function_count; + bunch = misc_bunch; + while (bunch) + { + for (i = 0; i < misc_bunch_index; i++) + { + misc_function_vector[j] = bunch->contents[i]; + misc_function_vector[j].name + = concat (misc_function_vector[j].name + + (misc_function_vector[j].name[0] == '_' ? offset : 0), + "", ""); + j++; + } + bunch = bunch->next; + misc_bunch_index = MISC_BUNCH_SIZE; + } + + misc_function_count += misc_count; + + /* Sort the misc functions by address. */ + + qsort (misc_function_vector, misc_function_count, + sizeof (struct misc_function), compare_misc_functions); +} + + +/* Read in another .o file and create a symtab entry for it.*/ + +static void +read_addl_syms (desc, stringtab, nlistlen, text_addr, text_size) + int desc; + register char *stringtab; + register int nlistlen; + unsigned text_addr; + int text_size; +{ + FILE *stream = fdopen (desc, "r"); + register char *namestring; + register struct symbol *sym, *prev; + int hash; + +#ifdef N_BINCL + subfile_stack = 0; +#endif + + last_source_file = 0; + bzero (global_sym_chain, sizeof global_sym_chain); + symtab_input_desc = desc; + stringtab_global = stringtab; + fill_symbuf (); + + for (symnum = 0; symnum < nlistlen; symnum++) + { + struct nlist *bufp; + unsigned char type; + + QUIT; /* allow this to be interruptable */ + if (symbuf_idx == symbuf_end) + fill_symbuf (); + bufp = &symbuf[symbuf_idx++]; + type = bufp->n_type & N_TYPE; + namestring = bufp->n_un.n_strx + stringtab; + + if( (type == N_TEXT) || (type == N_DATA) || (type == N_BSS) ) + { + /* Relocate this file's symbol table information + to the address it has been loaded into. */ + bufp->n_value += text_addr; + } + + type = bufp->n_type; + + if (type & N_STAB) + process_one_symbol (type, bufp->n_desc, + bufp->n_value, namestring); + /* A static text symbol whose name ends in ".o" + can only mean the start of another object file. + So end the symtab of the source file we have been processing. + This is how we avoid counting the libraries as part + or the last source file. + Also this way we find end of first object file (crt0). */ + else if ((type == N_TEXT +#ifdef N_NBTEXT + || type == N_NBTEXT +#endif + ) + && (!strcmp (namestring + strlen (namestring) - 2, ".o")) + || ! strncmp (namestring, "-l", 2)) + { + if (last_source_file) + end_symtab (bufp->n_value); + } + else if (type & N_EXT || type == N_TEXT +#ifdef N_NBTEXT + || type == N_NBTEXT +#endif + ) + { + int used_up = 0; + + /* Record the location of _etext. */ + if (type == (N_TEXT | N_EXT) + && !strcmp (namestring, "_etext")) + end_of_text_addr = bufp->n_value; + +#if 0 + /* 25 Sep 89: The following seems to be stolen from + read_ofile_symtab, and is wrong here (i.e. there was no + first pass for add-file symbols). */ + /* This shouldn't be necessary, as we now do all of this work + in scan_global syms and all misc functions should have been + recorded on the first pass. */ + /* Global symbol: see if we came across a dbx definition + for a corresponding symbol. If so, store the value. + Remove syms from the chain when their values are stored, + but search the whole chain, as there may be several syms + from different files with the same name. */ + if (type & N_EXT) + { + prev = 0; +#ifdef NAMES_HAVE_UNDERSCORE + hash = hashname (namestring + 1); +#else /* not NAMES_HAVE_UNDERSCORE */ + hash = hashname (namestring); +#endif /* not NAMES_HAVE_UNDERSCORE */ + for (sym = global_sym_chain[hash]; + sym;) + { + if ( +#ifdef NAMES_HAVE_UNDERSCORE + *namestring == '_' + && namestring[1] == SYMBOL_NAME (sym)[0] + && + !strcmp (namestring + 2, SYMBOL_NAME (sym) + 1) +#else /* NAMES_HAVE_UNDERSCORE */ + namestring[0] == SYMBOL_NAME (sym)[0] + && + !strcmp (namestring + 1, SYMBOL_NAME (sym) + 1) +#endif /* NAMES_HAVE_UNDERSCORE */ + ) + { + if (prev) + SYMBOL_VALUE (prev) = SYMBOL_VALUE (sym); + else + global_sym_chain[hash] + = (struct symbol *) SYMBOL_VALUE (sym); + if (SYMBOL_CLASS (sym) == LOC_BLOCK) + fix_common_block (sym, bufp->n_value); + else + SYMBOL_VALUE (sym) = bufp->n_value; + if (prev) + sym = (struct symbol *) SYMBOL_VALUE (prev); + else + sym = global_sym_chain[hash]; + + used_up = 1; + } + else + { + prev = sym; + sym = (struct symbol *) SYMBOL_VALUE (sym); + } + } + } + + /* Defined global or text symbol: record as a misc function + if it didn't give its address to a debugger symbol above. */ + if (type <= (N_TYPE | N_EXT) + && type != N_EXT + && ! used_up) + record_misc_function (namestring, bufp->n_value, + bufp->n_type); +#endif /* 0 */ + } + } + + if (last_source_file) + end_symtab (text_addr + text_size); + + fclose (stream); +} + +/* C++: + This function allows the addition of incrementally linked object files. + Since this has a fair amount of code in common with symbol_file_command, + it might be worthwhile to consolidate things, as was done with + read_dbx_symtab and condense_misc_bunches. */ + +void +add_file_command (arg_string) + char* arg_string; +{ + register int desc; + DECLARE_FILE_HEADERS; + struct nlist *nlist; + char *stringtab; + long buffer; + register int val; + extern void close (); + struct cleanup *old_chain; + struct symtab *symseg; + struct stat statbuf; + char *name; + unsigned text_addr; + + if (arg_string == 0) + error ("add-file takes a file name and an address"); + + arg_string = tilde_expand (arg_string); + make_cleanup (free, arg_string); + + for( ; *arg_string == ' '; arg_string++ ); + name = arg_string; + for( ; *arg_string && *arg_string != ' ' ; arg_string++ ); + *arg_string++ = (char) 0; + + if (name[0] == 0) + error ("add-file takes a file name and an address"); + + text_addr = parse_and_eval_address (arg_string); + + dont_repeat (); + + if (!query ("add symbol table from filename \"%s\" at text_addr = 0x%x\n", + name, text_addr)) + error ("Not confirmed."); + + desc = open (name, O_RDONLY); + if (desc < 0) + perror_with_name (name); + + old_chain = make_cleanup (close, desc); + + READ_FILE_HEADERS (desc, name); + + if (NUMBER_OF_SYMBOLS == 0) + { + printf ("%s does not have a symbol-table.\n", name); + fflush (stdout); + return; + } + + printf ("Reading symbol data from %s...", name); + fflush (stdout); + + /* Now read the string table, all at once. */ + val = lseek (desc, STRING_TABLE_OFFSET, 0); + if (val < 0) + perror_with_name (name); + if (stat (name, &statbuf) < 0) + perror_with_name (name); + READ_STRING_TABLE_SIZE (buffer); + if (buffer >= 0 && buffer < statbuf.st_size) + { +#ifdef BROKEN_LARGE_ALLOCA + stringtab = (char *) xmalloc (buffer); + make_cleanup (free, stringtab); +#else + stringtab = (char *) alloca (buffer); +#endif + } + else + stringtab = NULL; + if (stringtab == NULL) + error ("ridiculous string table size: %d bytes", buffer); + + /* Usually READ_STRING_TABLE_SIZE will have shifted the file pointer. + Occaisionally, it won't. */ + val = lseek (desc, STRING_TABLE_OFFSET, 0); + if (val < 0) + perror_with_name (name); + val = myread (desc, stringtab, buffer); + if (val < 0) + perror_with_name (name); + + /* Symsegs are no longer supported by GDB. Setting symseg_chain to + 0 is easier than finding all the symseg code and eliminating it. */ + symseg_chain = 0; + + /* Position to read the symbol table. Do not read it all at once. */ + val = lseek (desc, SYMBOL_TABLE_OFFSET, 0); + if (val < 0) + perror_with_name (name); + + init_misc_functions (); + make_cleanup (discard_misc_bunches, 0); + init_header_files (); + make_cleanup (free_header_files, 0); + free_pendings = 0; + pending_blocks = 0; + file_symbols = 0; + global_symbols = 0; + make_cleanup (really_free_pendings, 0); + + read_addl_syms (desc, stringtab, NUMBER_OF_SYMBOLS, text_addr, + SIZE_OF_TEXT_SEGMENT); + + + /* Sort symbols alphabetically within each block. */ + + sort_syms (); + + /* Go over the misc functions and install them in vector. */ + + condense_addl_misc_bunches (1); + + /* Don't allow char * to have a typename (else would get caddr_t.) */ + + TYPE_NAME (lookup_pointer_type (builtin_type_char)) = 0; + + do_cleanups (old_chain); + + /* Free the symtabs made by read_symsegs, but not their contents, + which have been copied into symtabs on symtab_list. */ + while (symseg_chain) + { + register struct symtab *s = symseg_chain->next; + free (symseg_chain); + symseg_chain = s; + } + + printf ("done.\n"); + fflush (stdout); +} + +/* Read a number by which a type is referred to in dbx data, + or perhaps read a pair (FILENUM, TYPENUM) in parentheses. + Just a single number N is equivalent to (0,N). + Return the two numbers by storing them in the vector TYPENUMS. + TYPENUMS will then be used as an argument to dbx_lookup_type. */ + +static void +read_type_number (pp, typenums) + register char **pp; + register int *typenums; +{ + if (**pp == '(') + { + (*pp)++; + typenums[0] = read_number (pp, ','); + typenums[1] = read_number (pp, ')'); + } + else + { + typenums[0] = 0; + typenums[1] = read_number (pp, 0); + } +} + + + +static struct symbol * +define_symbol (value, string, desc) + int value; + char *string; + int desc; +{ + register struct symbol *sym + = (struct symbol *) obstack_alloc (symbol_obstack, sizeof (struct symbol)); + char *p = (char *) index (string, ':'); + int deftype; + register int i; + + /* Ignore syms with empty names. */ + if (string[0] == 0) + return 0; + + /* Ignore old-style symbols from cc -go */ + if (p == 0) + return 0; + + SYMBOL_NAME (sym) + = (char *) obstack_alloc (symbol_obstack, ((p - string) + 1)); + /* Open-coded bcopy--saves function call time. */ + { + register char *p1 = string; + register char *p2 = SYMBOL_NAME (sym); + while (p1 != p) + *p2++ = *p1++; + *p2++ = '\0'; + } + p++; + /* Determine the type of name being defined. */ + if ((*p >= '0' && *p <= '9') || *p == '(') + deftype = 'l'; + else + deftype = *p++; + + /* c is a special case, not followed by a type-number. + SYMBOL:c=iVALUE for an integer constant symbol. + SYMBOL:c=rVALUE for a floating constant symbol. + SYMBOL:c=eTYPE,INTVALUE for an enum constant symbol. + e.g. "b:c=e6,0" for "const b = blob1" + (where type 6 is defined by "blobs:t6=eblob1:0,blob2:1,;"). */ + if (deftype == 'c') + { + if (*p++ != '=') + error ("Invalid symbol data at symtab pos %d.", symnum); + switch (*p++) + { + case 'r': + { + double d = atof (p); + char *value; + + SYMBOL_TYPE (sym) = builtin_type_double; + value = (char *) obstack_alloc (symbol_obstack, sizeof (double)); + bcopy (&d, value, sizeof (double)); + SYMBOL_VALUE_BYTES (sym) = value; + SYMBOL_CLASS (sym) = LOC_CONST_BYTES; + } + break; + case 'i': + { + SYMBOL_TYPE (sym) = builtin_type_int; + SYMBOL_VALUE (sym) = atoi (p); + SYMBOL_CLASS (sym) = LOC_CONST; + } + break; + case 'e': + /* SYMBOL:c=eTYPE,INTVALUE for an enum constant symbol. + e.g. "b:c=e6,0" for "const b = blob1" + (where type 6 is defined by "blobs:t6=eblob1:0,blob2:1,;"). */ + { + int typenums[2]; + + read_type_number (&p, typenums); + if (*p++ != ',') + error ("Invalid symbol data: no comma in enum const symbol"); + + SYMBOL_TYPE (sym) = *dbx_lookup_type (typenums); + SYMBOL_VALUE (sym) = atoi (p); + SYMBOL_CLASS (sym) = LOC_CONST; + } + break; + default: + error ("Invalid symbol data at symtab pos %d.", symnum); + } + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &file_symbols); + return sym; + } + + /* Now usually comes a number that says which data type, + and possibly more stuff to define the type + (all of which is handled by read_type) */ + + if (deftype == 'p' && *p == 'F') + /* pF is a two-letter code that means a function parameter in Fortran. + The type-number specifies the type of the return value. + Translate it into a pointer-to-function type. */ + { + p++; + SYMBOL_TYPE (sym) + = lookup_pointer_type (lookup_function_type (read_type (&p))); + } + else + { + struct type *type = read_type (&p); + + if ((deftype == 'F' || deftype == 'f') + && TYPE_CODE (type) != TYPE_CODE_FUNC) + SYMBOL_TYPE (sym) = lookup_function_type (type); + else + SYMBOL_TYPE (sym) = type; + } + + switch (deftype) + { + case 'f': + SYMBOL_CLASS (sym) = LOC_BLOCK; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &file_symbols); + break; + + case 'F': + SYMBOL_CLASS (sym) = LOC_BLOCK; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &global_symbols); + break; + + case 'G': + /* For a class G (global) symbol, it appears that the + value is not correct. It is necessary to search for the + corresponding linker definition to find the value. + These definitions appear at the end of the namelist. */ + i = hashname (SYMBOL_NAME (sym)); + SYMBOL_VALUE (sym) = (int) global_sym_chain[i]; + global_sym_chain[i] = sym; + SYMBOL_CLASS (sym) = LOC_STATIC; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &global_symbols); + break; + + /* This case is faked by a conditional above, + when there is no code letter in the dbx data. + Dbx data never actually contains 'l'. */ + case 'l': + SYMBOL_CLASS (sym) = LOC_LOCAL; + SYMBOL_VALUE (sym) = value; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &local_symbols); + break; + + case 'p': + SYMBOL_CLASS (sym) = LOC_ARG; + SYMBOL_VALUE (sym) = value; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &local_symbols); + + /* If it's gcc compiled, if it says `short', believe it. */ + if (processing_gcc_compilation || BELIEVE_PCC_PROMOTION) + break; + +#if defined(BELIEVE_PCC_PROMOTION_TYPE) + /* This macro is defined on machines (e.g. sparc) where + we should believe the type of a PCC 'short' argument, + but shouldn't believe the address (the address is + the address of the corresponding int). Note that + this is only different from the BELIEVE_PCC_PROMOTION + case on big-endian machines. + + My guess is that this correction, as opposed to changing + the parameter to an 'int' (as done below, for PCC + on most machines), is the right thing to do + on all machines, but I don't want to risk breaking + something that already works. On most PCC machines, + the sparc problem doesn't come up because the calling + function has to zero the top bytes (not knowing whether + the called function wants an int or a short), so there + is no practical difference between an int and a short + (except perhaps what happens when the GDB user types + "print short_arg = 0x10000;"). + Hacked for SunOS 4.1 by gnu@cygnus.com. In 4.1, the compiler + actually produces the correct address (we don't need to fix it + up). I made this code adapt so that it will offset the symbol + if it was pointing at an int-aligned location and not + otherwise. This way you can use the same gdb for 4.0.x and + 4.1 systems. */ + + if (0 == SYMBOL_VALUE (sym) % sizeof (int)) + { + if (SYMBOL_TYPE (sym) == builtin_type_char + || SYMBOL_TYPE (sym) == builtin_type_unsigned_char) + SYMBOL_VALUE (sym) += 3; + else if (SYMBOL_TYPE (sym) == builtin_type_short + || SYMBOL_TYPE (sym) == builtin_type_unsigned_short) + SYMBOL_VALUE (sym) += 2; + } + break; + +#else /* no BELIEVE_PCC_PROMOTION_TYPE. */ + + /* If PCC says a parameter is a short or a char, + it is really an int. */ + if (SYMBOL_TYPE (sym) == builtin_type_char + || SYMBOL_TYPE (sym) == builtin_type_short) + SYMBOL_TYPE (sym) = builtin_type_int; + else if (SYMBOL_TYPE (sym) == builtin_type_unsigned_char + || SYMBOL_TYPE (sym) == builtin_type_unsigned_short) + SYMBOL_TYPE (sym) = builtin_type_unsigned_int; + break; + +#endif /* no BELIEVE_PCC_PROMOTION_TYPE. */ + + case 'P': + SYMBOL_CLASS (sym) = LOC_REGPARM; + SYMBOL_VALUE (sym) = STAB_REG_TO_REGNUM (value); + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &local_symbols); + break; + + case 'r': +/* XXX */ +#ifdef sparc +{ + struct symbol *s0; + + /* + * If we see a parm decl immediately followed by a reg decl of + * the same name (and in the same block), we change it to a single + * instance of a reg parm. Sun's cc will generate these. + */ + if (local_symbols && + (s0 = local_symbols->symbol[local_symbols->nsyms - 1]) && + SYMBOL_CLASS(s0) == LOC_ARG && + strcmp(SYMBOL_NAME(s0), SYMBOL_NAME(sym)) == 0) { + SYMBOL_CLASS (s0) = LOC_REGPARM; + SYMBOL_VALUE (s0) = STAB_REG_TO_REGNUM (value); + SYMBOL_NAMESPACE (s0) = VAR_NAMESPACE; + return s0; + } +} +#endif + SYMBOL_CLASS (sym) = LOC_REGISTER; + SYMBOL_VALUE (sym) = STAB_REG_TO_REGNUM (value); + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &local_symbols); + break; + + case 'S': + /* Static symbol at top level of file */ + SYMBOL_CLASS (sym) = LOC_STATIC; + SYMBOL_VALUE (sym) = value; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &file_symbols); + break; + + case 't': + SYMBOL_CLASS (sym) = LOC_TYPEDEF; + SYMBOL_VALUE (sym) = value; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + if (TYPE_NAME (SYMBOL_TYPE (sym)) == 0 + && (TYPE_FLAGS (SYMBOL_TYPE (sym)) & TYPE_FLAG_PERM) == 0) + TYPE_NAME (SYMBOL_TYPE (sym)) = + obsavestring (SYMBOL_NAME (sym), + strlen (SYMBOL_NAME (sym))); + /* C++ vagaries: we may have a type which is derived from + a base type which did not have its name defined when the + derived class was output. We fill in the derived class's + base part member's name here in that case. */ + else if ((TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_STRUCT + || TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_UNION) + && TYPE_N_BASECLASSES (SYMBOL_TYPE (sym))) + { + int i; + for (i = TYPE_N_BASECLASSES (SYMBOL_TYPE (sym)); i > 0; i--) + if (TYPE_FIELD_NAME (SYMBOL_TYPE (sym), i - 1) == 0) + TYPE_FIELD_NAME (SYMBOL_TYPE (sym), i - 1) = + TYPE_NAME (TYPE_BASECLASS (SYMBOL_TYPE (sym), i)); + } + + add_symbol_to_list (sym, &file_symbols); + break; + + case 'T': + SYMBOL_CLASS (sym) = LOC_TYPEDEF; + SYMBOL_VALUE (sym) = value; + SYMBOL_NAMESPACE (sym) = STRUCT_NAMESPACE; + if (TYPE_NAME (SYMBOL_TYPE (sym)) == 0 + && (TYPE_FLAGS (SYMBOL_TYPE (sym)) & TYPE_FLAG_PERM) == 0) + TYPE_NAME (SYMBOL_TYPE (sym)) + = obconcat ("", + (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_ENUM + ? "enum " + : (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_STRUCT + ? "struct " : "union ")), + SYMBOL_NAME (sym)); + add_symbol_to_list (sym, &file_symbols); + break; + + case 'V': + /* Static symbol of local scope */ + SYMBOL_CLASS (sym) = LOC_STATIC; + SYMBOL_VALUE (sym) = value; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &local_symbols); + break; + + case 'v': + /* Reference parameter */ + SYMBOL_CLASS (sym) = LOC_REF_ARG; + SYMBOL_VALUE (sym) = value; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &local_symbols); + break; + + case 'X': + /* This is used by Sun FORTRAN for "function result value". + Sun claims ("dbx and dbxtool interfaces", 2nd ed) + that Pascal uses it too, but when I tried it Pascal used + "x:3" (local symbol) instead. */ + SYMBOL_CLASS (sym) = LOC_LOCAL; + SYMBOL_VALUE (sym) = value; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &local_symbols); + break; + + default: + error ("Invalid symbol data: unknown symbol-type code `%c' at symtab pos %d.", deftype, symnum); + } + return sym; +} + +/* What about types defined as forward references inside of a small lexical + scope? */ +/* Add a type to the list of undefined types to be checked through + once this file has been read in. */ +static void +add_undefined_type (type) + struct type *type; +{ + if (undef_types_length == undef_types_allocated) + { + undef_types_allocated *= 2; + undef_types = (struct type **) + xrealloc (undef_types, + undef_types_allocated * sizeof (struct type *)); + } + undef_types[undef_types_length++] = type; +} + +/* Add here something to go through each undefined type, see if it's + still undefined, and do a full lookup if so. */ +static void +cleanup_undefined_types () +{ + struct type **type, *ntype; + struct symbol *sym; + + for (type = undef_types; type < undef_types + undef_types_length; type++) + { + struct type *ntype = 0; + /* Reasonable test to see if it's been defined since. */ + if (TYPE_NFIELDS (*type) == 0) + { + struct pending *ppt; + int i; + /* Name of the type, without "struct" or "union" */ + char *typename = TYPE_NAME (*type); + + if (!strncmp (typename, "struct ", 7)) + typename += 7; + if (!strncmp (typename, "union ", 6)) + typename += 6; + + for (ppt = file_symbols; ppt; ppt = ppt->next) + for (i = 0; i < ppt->nsyms; i++) + { + struct symbol *sym = ppt->symbol[i]; + + if (SYMBOL_CLASS (sym) == LOC_TYPEDEF + && SYMBOL_NAMESPACE (sym) == STRUCT_NAMESPACE + && (TYPE_CODE (SYMBOL_TYPE (sym)) == + TYPE_CODE (*type)) + && !strcmp (SYMBOL_NAME (sym), typename)) + bcopy (SYMBOL_TYPE (sym), *type, sizeof (struct type)); + } + } + else + /* It has been defined; don't mark it as a stub. */ + TYPE_FLAGS (*type) &= ~TYPE_FLAG_STUB; + } + undef_types_length = 0; +} + + + +/* Read a dbx type reference or definition; + return the type that is meant. + This can be just a number, in which case it references + a type already defined and placed in type_vector. + Or the number can be followed by an =, in which case + it means to define a new type according to the text that + follows the =. */ + +static +struct type * +read_type (pp) + register char **pp; +{ + register struct type *type = 0; + register int n; + struct type *type1; + int typenums[2]; + int xtypenums[2]; + char *tmpc; + + /* Read type number if present. The type number may be omitted. + for instance in a two-dimensional array declared with type + "ar1;1;10;ar1;1;10;4". */ + if ((**pp >= '0' && **pp <= '9') + || **pp == '(') + { + read_type_number (pp, typenums); + + /* Detect random reference to type not yet defined. + Allocate a type object but leave it zeroed. */ + if (**pp != '=') + return dbx_alloc_type (typenums); + + *pp += 2; + } + else + { + /* 'typenums=' not present, type is anonymous. Read and return + the definition, but don't put it in the type vector. */ + typenums[0] = typenums[1] = -1; + *pp += 1; + } + + switch ((*pp)[-1]) + { + case 'x': + { + enum type_code code; + + /* Used to index through file_symbols. */ + struct pending *ppt; + int i; + + /* Name including "struct", etc. */ + char *type_name; + + /* Name without "struct", etc. */ + char *type_name_only; + + { + char *prefix; + char *from, *to; + + /* Set the type code according to the following letter. */ + switch ((*pp)[0]) + { + case 's': + code = TYPE_CODE_STRUCT; + prefix = "struct "; + break; + case 'u': + code = TYPE_CODE_UNION; + prefix = "union "; + break; + case 'e': + code = TYPE_CODE_ENUM; + prefix = "enum "; + break; + default: + error ("Bad type cross reference at symnum: %d.", symnum); + } + + to = type_name = (char *) + obstack_alloc (symbol_obstack, + (strlen (prefix) + + ((char *) index (*pp, ':') - (*pp)) + 1)); + + /* Copy the prefix. */ + from = prefix; + while (*to++ = *from++) + ; + to--; + + type_name_only = to; + + /* Copy the name. */ + from = *pp + 1; + while ((*to++ = *from++) != ':') + ; + *--to = '\0'; + + /* Set the pointer ahead of the name which we just read. */ + *pp = from; + +#if 0 + /* The following hack is clearly wrong, because it doesn't + check whether we are in a baseclass. I tried to reproduce + the case that it is trying to fix, but I couldn't get + g++ to put out a cross reference to a basetype. Perhaps + it doesn't do it anymore. */ + /* Note: for C++, the cross reference may be to a base type which + has not yet been seen. In this case, we skip to the comma, + which will mark the end of the base class name. (The ':' + at the end of the base class name will be skipped as well.) + But sometimes (ie. when the cross ref is the last thing on + the line) there will be no ','. */ + from = (char *) index (*pp, ','); + if (from) + *pp = from; +#endif /* 0 */ + } + + /* Now check to see whether the type has already been declared. */ + /* This is necessary at least in the case where the + program says something like + struct foo bar[5]; + The compiler puts out a cross-reference; we better find + set the length of the structure correctly so we can + set the length of the array. */ + for (ppt = file_symbols; ppt; ppt = ppt->next) + for (i = 0; i < ppt->nsyms; i++) + { + struct symbol *sym = ppt->symbol[i]; + + if (SYMBOL_CLASS (sym) == LOC_TYPEDEF + && SYMBOL_NAMESPACE (sym) == STRUCT_NAMESPACE + && (TYPE_CODE (SYMBOL_TYPE (sym)) == code) + && !strcmp (SYMBOL_NAME (sym), type_name_only)) + { + obstack_free (symbol_obstack, type_name); + type = SYMBOL_TYPE (sym); + return type; + } + } + + /* Didn't find the type to which this refers, so we must + be dealing with a forward reference. Allocate a type + structure for it, and keep track of it so we can + fill in the rest of the fields when we get the full + type. */ + type = dbx_alloc_type (typenums); + TYPE_CODE (type) = code; + TYPE_NAME (type) = type_name; + + TYPE_FLAGS (type) |= TYPE_FLAG_STUB; + + add_undefined_type (type); + return type; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '(': + (*pp)--; + read_type_number (pp, xtypenums); + type = *dbx_lookup_type (xtypenums); + if (type == 0) + type = builtin_type_void; + if (typenums[0] != -1) + *dbx_lookup_type (typenums) = type; + break; + + case '*': + type1 = read_type (pp); + if (TYPE_POINTER_TYPE (type1)) + { + type = TYPE_POINTER_TYPE (type1); + if (typenums[0] != -1) + *dbx_lookup_type (typenums) = type; + } + else + { + type = dbx_alloc_type (typenums); + smash_to_pointer_type (type, type1); + } + break; + + case '@': + { + struct type *domain = read_type (pp); + char c; + struct type *memtype; + + if (*(*pp)++ != ',') + error ("invalid member type data format, at symtab pos %d.", + symnum); + + memtype = read_type (pp); + type = dbx_alloc_type (typenums); + smash_to_member_type (type, domain, memtype); + } + break; + + case '#': + { + struct type *domain = read_type (pp); + char c; + struct type *return_type; + struct type **args; + + if (*(*pp)++ != ',') + error ("invalid member type data format, at symtab pos %d.", + symnum); + + return_type = read_type (pp); + args = read_args (pp, ';'); + type = dbx_alloc_type (typenums); + smash_to_method_type (type, domain, return_type, args); + } + break; + + case '&': + type1 = read_type (pp); + if (TYPE_REFERENCE_TYPE (type1)) + { + type = TYPE_REFERENCE_TYPE (type1); + if (typenums[0] != -1) + *dbx_lookup_type (typenums) = type; + } + else + { + type = dbx_alloc_type (typenums); + smash_to_reference_type (type, type1); + } + break; + + case 'f': + type1 = read_type (pp); + if (TYPE_FUNCTION_TYPE (type1)) + { + type = TYPE_FUNCTION_TYPE (type1); + if (typenums[0] != -1) + *dbx_lookup_type (typenums) = type; + } + else + { + type = dbx_alloc_type (typenums); + smash_to_function_type (type, type1); + } + break; + + case 'r': + type = read_range_type (pp, typenums); + if (typenums[0] != -1) + *dbx_lookup_type (typenums) = type; + break; + + case 'e': + type = dbx_alloc_type (typenums); + type = read_enum_type (pp, type); + *dbx_lookup_type (typenums) = type; + break; + + case 's': + type = dbx_alloc_type (typenums); + type = read_struct_type (pp, type); + break; + + case 'u': + type = dbx_alloc_type (typenums); + type = read_struct_type (pp, type); + TYPE_CODE (type) = TYPE_CODE_UNION; + break; + + case 'a': + if (*(*pp)++ != 'r') + error ("Invalid symbol data: unrecognized type-code `a%c' %s %d.", + (*pp)[-1], "at symtab position", symnum); + + type = dbx_alloc_type (typenums); + type = read_array_type (pp, type); + break; + + default: + error ("Invalid symbol data: unrecognized type-code `%c' at symtab pos %d.", + (*pp)[-1], symnum); + } + + if (type == 0) + abort (); + +#if 0 + /* If this is an overriding temporary alteration for a header file's + contents, and this type number is unknown in the global definition, + put this type into the global definition at this type number. */ + if (header_file_prev_index >= 0) + { + register struct type **tp + = explicit_lookup_type (header_file_prev_index, typenums[1]); + if (*tp == 0) + *tp = type; + } +#endif + return type; +} + +/* This page contains subroutines of read_type. */ + +/* Read the description of a structure (or union type) + and return an object describing the type. */ + +static struct type * +read_struct_type (pp, type) + char **pp; + register struct type *type; +{ + struct nextfield + { + struct nextfield *next; + int visibility; + struct field field; + }; + + struct next_fnfield + { + struct next_fnfield *next; + int visibility; + struct fn_field fn_field; + }; + + struct next_fnfieldlist + { + struct next_fnfieldlist *next; + struct fn_fieldlist fn_fieldlist; + }; + + register struct nextfield *list = 0; + struct nextfield *new; + int totalsize; + char *name; + register char *p; + int nfields = 0; + register int n; + + register struct next_fnfieldlist *mainlist = 0; + int nfn_fields = 0; + int read_possible_virtual_info = 0; + + if (TYPE_MAIN_VARIANT (type) == 0) + { + TYPE_MAIN_VARIANT (type) = type; + } + + TYPE_CODE (type) = TYPE_CODE_STRUCT; + + /* First comes the total size in bytes. */ + + TYPE_LENGTH (type) = read_number (pp, 0); + + /* C++: Now, if the class is a derived class, then the next character + will be a '!', followed by the number of base classes derived from. + Each element in the list contains visibility information, + the offset of this base class in the derived structure, + and then the base type. */ + if (**pp == '!') + { + int i, n_baseclasses, offset; + struct type **baseclass_vec; + struct type *baseclass; + int via_public; + + /* Nonzero if it is a virtual baseclass, i.e., + + struct A{}; + struct B{}; + struct C : public B, public virtual A {}; + + B is a baseclass of C; A is a virtual baseclass for C. This is a C++ + 2.0 language feature. */ + int via_virtual; + + *pp += 1; + + n_baseclasses = read_number (pp, ','); + baseclass_vec = (struct type **) + obstack_alloc (symbol_obstack, + (n_baseclasses) * sizeof (struct type **)) - 1; + + for (i = 1; i <= n_baseclasses; i++) + { + if (**pp == '\\') + *pp = next_symbol_text (); + + switch (*(*pp)++) + { + case '0': + via_virtual = 0; + break; + case '1': + via_virtual = 1; + break; + default: + error ("Invalid symbol data: bad visibility format at symtab pos %d", + symnum); + } + + switch (*(*pp)++) + { + case '0': + via_public = 0; + break; + case '2': + via_public = 1; + break; + default: + error ("Invalid symbol data: bad visibility format at symtab pos %d.", + symnum); + } + + /* Offset of the portion of the object corresponding to + this baseclass. Always zero in the absence of + multiple inheritance. */ + offset = read_number (pp, ','); + baseclass = read_type (pp); + *pp += 1; /* skip trailing ';' */ + + if (offset != 0) + { + static int error_printed = 0; + + if (!error_printed) + { + fprintf (stderr, +"\nWarning: GDB has limited understanding of multiple inheritance..."); + error_printed = 1; + } + offset = 0; + } + + baseclass_vec[i] = lookup_basetype_type (baseclass, offset, via_virtual, via_public); + + /* Since lookup_basetype_type can copy the type, + it might copy a stub type (complete with stub flag). + If so, we need to add it to the list of undefined types + to clean up later. Even if lookup_basetype_type + didn't copy the type, adding it to the undefined list + will not do any harm. */ + if (TYPE_FLAGS(baseclass_vec[i]) & TYPE_FLAG_STUB) + add_undefined_type (baseclass_vec[i]); + + /* Make this baseclass visible for structure-printing purposes. */ + new = (struct nextfield *) alloca (sizeof (struct nextfield)); + new->next = list; + list = new; + list->field.type = baseclass_vec[i]; + list->field.name = TYPE_NAME (baseclass_vec[i]); + list->field.bitpos = offset; + list->field.bitsize = 0; /* this should be an unpacked field! */ + nfields++; + } + TYPE_N_BASECLASSES (type) = n_baseclasses; + TYPE_BASECLASSES (type) = baseclass_vec; + } + + /* Now come the fields, as NAME:?TYPENUM,BITPOS,BITSIZE; for each one. + At the end, we see a semicolon instead of a field. + + In C++, this may wind up being NAME:?TYPENUM:PHYSNAME; for + a static field. + + The `?' is a placeholder for one of '+' (public visibility), + '0' (protected visibility), and '-' (private visibility). */ + + /* We better set p right now, in case there are no fields at all... */ + p = *pp; + + while (**pp != ';') + { + int visibility; + + /* Check for and handle cretinous dbx symbol name continuation! */ + if (**pp == '\\') *pp = next_symbol_text (); + + /* Get space to record the next field's data. */ + new = (struct nextfield *) alloca (sizeof (struct nextfield)); + new->next = list; + list = new; + + /* Get the field name. */ + p = *pp; + while (*p != ':') p++; + list->field.name = obsavestring (*pp, p - *pp); + + /* C++: Check to see if we have hit the methods yet. */ + if (p[1] == ':') + break; + + *pp = p + 1; + + /* This means we have a visibility for a field coming. */ + if (**pp == '/') + { + switch (*++*pp) + { + case '0': + visibility = 0; + *pp += 1; + break; + + case '1': + visibility = 1; + *pp += 1; + break; + + case '2': + visibility = 2; + *pp += 1; + break; + } + } + /* else normal dbx-style format. */ + + list->field.type = read_type (pp); + if (**pp == ':') + { + list->field.bitpos = (long)-1; + p = ++(*pp); + while (*p != ';') p++; + list->field.bitsize = (long) savestring (*pp, p - *pp); + *pp = p + 1; + nfields++; + continue; + } + else if (**pp != ',') + error ("Invalid symbol data: bad structure-type format at symtab pos %d.", + symnum); + (*pp)++; /* Skip the comma. */ + list->field.bitpos = read_number (pp, ','); + list->field.bitsize = read_number (pp, ';'); + +#if 0 + /* This is wrong because this is identical to the symbols + produced for GCC 0-size arrays. For example: + typedef union { + int num; + char str[0]; + } foo; + The code which dumped core in such circumstances should be + fixed not to dump core. */ + + /* g++ -g0 can put out bitpos & bitsize zero for a static + field. This does not give us any way of getting its + class, so we can't know its name. But we can just + ignore the field so we don't dump core and other nasty + stuff. */ + if (list->field.bitpos == 0 + && list->field.bitsize == 0) + { + /* Have we given the warning yet? */ + static int warning_given = 0; + + /* Only give the warning once, no matter how many class + variables there are. */ + if (!warning_given) + { + warning_given = 1; + fprintf_filtered (stderr, "\n\ +Warning: DBX-style class variable debugging information encountered.\n\ +You seem to have compiled your program with \ +\"g++ -g0\" instead of \"g++ -g\".\n\ +Therefore GDB will not know about your class variables.\n\ +"); + } + + /* Ignore this field. */ + list = list->next; + } + else +#endif /* 0 */ + { + /* Detect an unpacked field and mark it as such. + dbx gives a bit size for all fields. + Note that forward refs cannot be packed, + and treat enums as if they had the width of ints. */ + if (TYPE_CODE (list->field.type) != TYPE_CODE_INT + && TYPE_CODE (list->field.type) != TYPE_CODE_ENUM) + list->field.bitsize = 0; + if ((list->field.bitsize == 8 * TYPE_LENGTH (list->field.type) + || (TYPE_CODE (list->field.type) == TYPE_CODE_ENUM + && (list->field.bitsize + == 8 * TYPE_LENGTH (builtin_type_int)) + ) + ) + && + list->field.bitpos % 8 == 0) + list->field.bitsize = 0; + nfields++; + } + } + + /* Now come the method fields, as NAME::methods + where each method is of the form TYPENUM,ARGS,...:PHYSNAME; + At the end, we see a semicolon instead of a field. + + For the case of overloaded operators, the format is + OPERATOR::*.methods, where OPERATOR is the string "operator", + `*' holds the place for an operator name (such as `+=') + and `.' marks the end of the operator name. */ + if (p[1] == ':') + { + /* Now, read in the methods. To simplify matters, we + "unread" the name that has been read, so that we can + start from the top. */ + + p = *pp; + + /* chill the list of fields: the last entry (at the head) + is a partially constructed entry which we now scrub. */ + list = list->next; + + /* For each list of method lists... */ + do + { + int i; + struct next_fnfield *sublist = 0; + struct fn_field *fn_fields = 0; + int length = 0; + struct next_fnfieldlist *new_mainlist = + (struct next_fnfieldlist *)alloca (sizeof (struct next_fnfieldlist)); + + /* read in the name. */ + while (*p != ':') p++; + if ((*pp)[0] == 'o' && (*pp)[1] == 'p' && (*pp)[2] == '$') + { + static char opname[] = "operator"; + char *o = opname + strlen(opname); + + /* Skip past '::'. */ + p += 2; + while (*p != '.') + *o++ = *p++; + new_mainlist->fn_fieldlist.name = savestring (opname, o - opname); + /* Skip past '.' */ + *pp = p + 1; + } + else + { + i = 0; + new_mainlist->fn_fieldlist.name = savestring (*pp, p - *pp); + /* Skip past '::'. */ + *pp = p + 2; + } + + do + { + struct next_fnfield *new_sublist = + (struct next_fnfield *)alloca (sizeof (struct next_fnfield)); + + /* Check for and handle cretinous dbx symbol name continuation! */ + if (**pp == '\\') *pp = next_symbol_text (); + + new_sublist->fn_field.type = read_type (pp); + if (**pp != ':') + error ("invalid symtab info for method at symbol number %d.", + symnum); + *pp += 1; + new_sublist->fn_field.args = + TYPE_ARG_TYPES (new_sublist->fn_field.type); + p = *pp; + while (*p != ';') p++; + new_sublist->fn_field.physname = savestring (*pp, p - *pp); + *pp = p + 1; + new_sublist->visibility = *(*pp)++ - '0'; + if (**pp == '\\') *pp = next_symbol_text (); + + switch (*(*pp)++) + { + case '*': + /* virtual member function, followed by index. */ + new_sublist->fn_field.voffset = read_number (pp, ';') + 1; + break; + case '?': + /* static member function. */ + new_sublist->fn_field.voffset = 1; + break; + default: + /* **pp == '.'. */ + /* normal member function. */ + new_sublist->fn_field.voffset = 0; + break; + } + + new_sublist->next = sublist; + sublist = new_sublist; + length++; + } + while (**pp != ';'); + + *pp += 1; + + new_mainlist->fn_fieldlist.fn_fields = + (struct fn_field *) obstack_alloc (symbol_obstack, + sizeof (struct fn_field) * length); + TYPE_FN_PRIVATE_BITS (new_mainlist->fn_fieldlist) = + (int *) obstack_alloc (symbol_obstack, + sizeof (int) * (1 + (length >> 5))); + + TYPE_FN_PROTECTED_BITS (new_mainlist->fn_fieldlist) = + (int *) obstack_alloc (symbol_obstack, + sizeof (int) * (1 + (length >> 5))); + + for (i = length; sublist; sublist = sublist->next) + { + new_mainlist->fn_fieldlist.fn_fields[--i] = sublist->fn_field; + if (sublist->visibility == 0) + B_SET (new_mainlist->fn_fieldlist.private_fn_field_bits, i); + else if (sublist->visibility == 1) + B_SET (new_mainlist->fn_fieldlist.protected_fn_field_bits, i); + } + + new_mainlist->fn_fieldlist.length = length; + new_mainlist->next = mainlist; + mainlist = new_mainlist; + nfn_fields++; + } + while (**pp != ';'); + } + + *pp += 1; + + /* Now create the vector of fields, and record how big it is. */ + + TYPE_NFIELDS (type) = nfields; + TYPE_FIELDS (type) = (struct field *) obstack_alloc (symbol_obstack, + sizeof (struct field) * nfields); + TYPE_FIELD_PRIVATE_BITS (type) = + (int *) obstack_alloc (symbol_obstack, + sizeof (int) * (1 + (nfields >> 5))); + TYPE_FIELD_PROTECTED_BITS (type) = + (int *) obstack_alloc (symbol_obstack, + sizeof (int) * (1 + (nfields >> 5))); + + TYPE_NFN_FIELDS (type) = nfn_fields; + TYPE_NFN_FIELDS_TOTAL (type) = nfn_fields; + + { + int i; + for (i = 1; i <= TYPE_N_BASECLASSES (type); ++i) + TYPE_NFN_FIELDS_TOTAL (type) += + TYPE_NFN_FIELDS_TOTAL (TYPE_BASECLASS (type, i)); + } + + TYPE_FN_FIELDLISTS (type) = + (struct fn_fieldlist *) obstack_alloc (symbol_obstack, + sizeof (struct fn_fieldlist) * nfn_fields); + + /* Copy the saved-up fields into the field vector. */ + + for (n = nfields; list; list = list->next) + { + TYPE_FIELD (type, --n) = list->field; + if (list->visibility == 0) + SET_TYPE_FIELD_PRIVATE (type, n); + else if (list->visibility == 1) + SET_TYPE_FIELD_PROTECTED (type, n); + } + + for (n = nfn_fields; mainlist; mainlist = mainlist->next) + TYPE_FN_FIELDLISTS (type)[--n] = mainlist->fn_fieldlist; + + if (**pp == '~') + { + *pp += 1; + + if (**pp == '=') + { + TYPE_FLAGS (type) + |= TYPE_FLAG_HAS_CONSTRUCTOR | TYPE_FLAG_HAS_DESTRUCTOR; + *pp += 1; + } + else if (**pp == '+') + { + TYPE_FLAGS (type) |= TYPE_FLAG_HAS_CONSTRUCTOR; + *pp += 1; + } + else if (**pp == '-') + { + TYPE_FLAGS (type) |= TYPE_FLAG_HAS_DESTRUCTOR; + *pp += 1; + } + + /* Read either a '%' or the final ';'. */ + if (*(*pp)++ == '%') + { + /* Now we must record the virtual function table pointer's + field information. */ + + struct type *t; + int i; + + t = read_type (pp); + p = (*pp)++; + while (*p != ';') p++; + TYPE_VPTR_BASETYPE (type) = t; + if (type == t) + { + if (TYPE_FIELD_NAME (t, 0) == 0) + TYPE_VPTR_FIELDNO (type) = i = 0; + else for (i = TYPE_NFIELDS (t) - 1; i >= 0; --i) + if (! strncmp (TYPE_FIELD_NAME (t, i), *pp, + strlen (TYPE_FIELD_NAME (t, i)))) + { + TYPE_VPTR_FIELDNO (type) = i; + break; + } + if (i < 0) + error ("virtual function table field not found"); + } + else + TYPE_VPTR_FIELDNO (type) = TYPE_VPTR_FIELDNO (TYPE_BASECLASS (type, 1)); + *pp = p + 1; + } + else + { + TYPE_VPTR_BASETYPE (type) = 0; + TYPE_VPTR_FIELDNO (type) = -1; + } + } + else + { + TYPE_VPTR_BASETYPE (type) = 0; + TYPE_VPTR_FIELDNO (type) = -1; + } + + return type; +} + +/* Read a definition of an array type, + and create and return a suitable type object. + Also creates a range type which represents the bounds of that + array. */ +static struct type * +read_array_type (pp, type) + register char **pp; + register struct type *type; +{ + struct type *index_type, *element_type, *range_type; + int lower, upper; + int adjustable = 0; + + /* Format of an array type: + "ar;lower;upper;". Put code in + to handle this. + + Fortran adjustable arrays use Adigits or Tdigits for lower or upper; + for these, produce a type like float[][]. */ + + index_type = read_type (pp); + if (*(*pp)++ != ';') + error ("Invalid symbol data; improper format of array type decl."); + + if (!(**pp >= '0' && **pp <= '9')) + { + *pp += 1; + adjustable = 1; + } + lower = read_number (pp, ';'); + + if (!(**pp >= '0' && **pp <= '9')) + { + *pp += 1; + adjustable = 1; + } + upper = read_number (pp, ';'); + + element_type = read_type (pp); + + if (adjustable) + { + lower = 0; + upper = -1; + } + + { + /* Create range type. */ + range_type = (struct type *) obstack_alloc (symbol_obstack, + sizeof (struct type)); + TYPE_CODE (range_type) = TYPE_CODE_RANGE; + TYPE_TARGET_TYPE (range_type) = index_type; + + /* This should never be needed. */ + TYPE_LENGTH (range_type) = sizeof (int); + + TYPE_NFIELDS (range_type) = 2; + TYPE_FIELDS (range_type) = + (struct field *) obstack_alloc (symbol_obstack, + 2 * sizeof (struct field)); + TYPE_FIELD_BITPOS (range_type, 0) = lower; + TYPE_FIELD_BITPOS (range_type, 1) = upper; + } + + TYPE_CODE (type) = TYPE_CODE_ARRAY; + TYPE_TARGET_TYPE (type) = element_type; + TYPE_LENGTH (type) = (upper - lower + 1) * TYPE_LENGTH (element_type); + TYPE_NFIELDS (type) = 1; + TYPE_FIELDS (type) = + (struct field *) obstack_alloc (symbol_obstack, + sizeof (struct field)); + TYPE_FIELD_TYPE (type, 0) = range_type; + + return type; +} + + +/* Read a definition of an enumeration type, + and create and return a suitable type object. + Also defines the symbols that represent the values of the type. */ + +static struct type * +read_enum_type (pp, type) + register char **pp; + register struct type *type; +{ + register char *p; + char *name; + register long n; + register struct symbol *sym; + int nsyms = 0; + struct pending **symlist; + struct pending *osyms, *syms; + int o_nsyms; + + if (within_function) + symlist = &local_symbols; + else + symlist = &file_symbols; + osyms = *symlist; + o_nsyms = osyms ? osyms->nsyms : 0; + + /* Read the value-names and their values. + The input syntax is NAME:VALUE,NAME:VALUE, and so on. + A semicolon or comman instead of a NAME means the end. */ + while (**pp && **pp != ';' && **pp != ',') + { + /* Check for and handle cretinous dbx symbol name continuation! */ + if (**pp == '\\') *pp = next_symbol_text (); + + p = *pp; + while (*p != ':') p++; + name = obsavestring (*pp, p - *pp); + *pp = p + 1; + n = read_number (pp, ','); + + sym = (struct symbol *) obstack_alloc (symbol_obstack, sizeof (struct symbol)); + bzero (sym, sizeof (struct symbol)); + SYMBOL_NAME (sym) = name; + SYMBOL_CLASS (sym) = LOC_CONST; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + SYMBOL_VALUE (sym) = n; + add_symbol_to_list (sym, symlist); + nsyms++; + } + + if (**pp == ';') + (*pp)++; /* Skip the semicolon. */ + + /* Now fill in the fields of the type-structure. */ + + TYPE_LENGTH (type) = sizeof (int); + TYPE_CODE (type) = TYPE_CODE_ENUM; + TYPE_NFIELDS (type) = nsyms; + TYPE_FIELDS (type) = (struct field *) obstack_alloc (symbol_obstack, sizeof (struct field) * nsyms); + + /* Find the symbols for the values and put them into the type. + The symbols can be found in the symlist that we put them on + to cause them to be defined. osyms contains the old value + of that symlist; everything up to there was defined by us. */ + /* Note that we preserve the order of the enum constants, so + that in something like "enum {FOO, LAST_THING=FOO}" we print + FOO, not LAST_THING. */ + + for (syms = *symlist, n = 0; syms; syms = syms->next) + { + int j = 0; + if (syms == osyms) + j = o_nsyms; + for (; j < syms->nsyms; j++) + { + struct symbol *sym = syms->symbol[j]; + SYMBOL_TYPE (sym) = type; + TYPE_FIELD_NAME (type, n) = SYMBOL_NAME (sym); + TYPE_FIELD_VALUE (type, n) = 0; + TYPE_FIELD_BITPOS (type, n) = SYMBOL_VALUE (sym); + TYPE_FIELD_BITSIZE (type, n++) = 0; + } + if (syms == osyms) + break; + } + + return type; +} + +#define MAX_OF_TYPE(t) ((1 << (sizeof (t) - 1)) - 1) +#define MIN_OF_TYPE(t) (-(1 << (sizeof (t) - 1))) + +static struct type * +read_range_type (pp, typenums) + char **pp; + int typenums[2]; +{ + int rangenums[2]; + long n2, n3; + int n2bits, n3bits; + int self_subrange; + struct type *result_type; + struct type *index_type; + + /* First comes a type we are a subrange of. + In C it is usually 0, 1 or the type being defined. */ + read_type_number (pp, rangenums); + self_subrange = (rangenums[0] == typenums[0] && + rangenums[1] == typenums[1]); + + /* A semicolon should now follow; skip it. */ + if (**pp == ';') + (*pp)++; + + /* The remaining two operands are usually lower and upper bounds + of the range. But in some special cases they mean something else. */ + read_huge_number (pp, ';', &n2, &n2bits); + read_huge_number (pp, ';', &n3, &n3bits); + + if (n2bits == -1 || n3bits == -1) + error ("Unrecognized type range %s.", pp); + + if (n2bits != 0 || n3bits != 0) +#ifdef LONG_LONG + { + char got_signed = 0; + char got_unsigned = 0; + /* Number of bits in the type. */ + int nbits; + + /* Range from 0 to is an unsigned large integral type. */ + if ((n2bits == 0 && n2 == 0) && n3bits != 0) + { + got_unsigned = 1; + nbits = n3bits; + } + /* Range fro to -1 is a large signed + integral type. */ + else if (n2bits != 0 && n3bits != 0 && n2bits == n3bits + 1) + { + got_signed = 1; + nbits = n2bits; + } + + /* Check for "long long". */ + if (got_signed && nbits == CHAR_BIT * sizeof (long long)) + return builtin_type_long_long; + if (got_unsigned && nbits == CHAR_BIT * sizeof (long long)) + return builtin_type_unsigned_long_long; + + error ("Large type isn't a long long."); + } +#else /* LONG_LONG */ + error ("Type long long not supported on this machine."); +#endif + + /* A type defined as a subrange of itself, with bounds both 0, is void. */ + if (self_subrange && n2 == 0 && n3 == 0) + return builtin_type_void; + + /* If n3 is zero and n2 is not, we want a floating type, + and n2 is the width in bytes. + + Fortran programs appear to use this for complex types also, + and they give no way to distinguish between double and single-complex! + We don't have complex types, so we would lose on all fortran files! + So return type `double' for all of those. It won't work right + for the complex values, but at least it makes the file loadable. */ + + if (n3 == 0 && n2 > 0) + { + if (n2 == sizeof (float)) + return builtin_type_float; + return builtin_type_double; + } + + /* If the upper bound is -1, it must really be an unsigned int. */ + + else if (n2 == 0 && n3 == -1) + { + if (sizeof (int) == sizeof (long)) + return builtin_type_unsigned_int; + else + return builtin_type_unsigned_long; + } + + /* Special case: char is defined (Who knows why) as a subrange of + itself with range 0-127. */ + else if (self_subrange && n2 == 0 && n3 == 127) + return builtin_type_char; + + /* Assumptions made here: Subrange of self is equivalent to subrange + of int. */ + else if (n2 == 0 + && (self_subrange || + *dbx_lookup_type (rangenums) == builtin_type_int)) + { + /* an unsigned type */ + if (n3 == UINT_MAX) + return builtin_type_unsigned_int; + if (n3 == ULONG_MAX) + return builtin_type_unsigned_long; + if (n3 == USHRT_MAX) + return builtin_type_unsigned_short; + if (n3 == UCHAR_MAX) + return builtin_type_unsigned_char; + } +#ifdef LONG_LONG + else if (n3 == 0 && n2 == -sizeof (long long)) + return builtin_type_long_long; +#endif + else if (n2 == -n3 -1) + { + /* a signed type */ + if (n3 == INT_MAX) + return builtin_type_int; + if (n3 == LONG_MAX) + return builtin_type_long; + if (n3 == SHRT_MAX) + return builtin_type_short; + if (n3 == CHAR_MAX) + return builtin_type_char; + } + + /* We have a real range type on our hands. Allocate space and + return a real pointer. */ + + /* At this point I don't have the faintest idea how to deal with + a self_subrange type; I'm going to assume that this is used + as an idiom, and that all of them are special cases. So . . . */ + if (self_subrange) + error ("Type defined as subrange of itself: %s.", pp); + + result_type = (struct type *) obstack_alloc (symbol_obstack, + sizeof (struct type)); + bzero (result_type, sizeof (struct type)); + + TYPE_TARGET_TYPE (result_type) = (self_subrange ? + builtin_type_int : + *dbx_lookup_type(rangenums)); + + /* We have to figure out how many bytes it takes to hold this + range type. I'm going to assume that anything that is pushing + the bounds of a long was taken care of above. */ + if (n2 >= MIN_OF_TYPE(char) && n3 <= MAX_OF_TYPE(char)) + TYPE_LENGTH (result_type) = 1; + else if (n2 >= MIN_OF_TYPE(short) && n3 <= MAX_OF_TYPE(short)) + TYPE_LENGTH (result_type) = sizeof (short); + else if (n2 >= MIN_OF_TYPE(int) && n3 <= MAX_OF_TYPE(int)) + TYPE_LENGTH (result_type) = sizeof (int); + else if (n2 >= MIN_OF_TYPE(long) && n3 <= MAX_OF_TYPE(long)) + TYPE_LENGTH (result_type) = sizeof (long); + else + error ("Ranged type doesn't fit within known sizes."); + + TYPE_LENGTH (result_type) = TYPE_LENGTH (TYPE_TARGET_TYPE (result_type)); + TYPE_CODE (result_type) = TYPE_CODE_RANGE; + TYPE_NFIELDS (result_type) = 2; + TYPE_FIELDS (result_type) = + (struct field *) obstack_alloc (symbol_obstack, + 2 * sizeof (struct field)); + bzero (TYPE_FIELDS (result_type), 2 * sizeof (struct field)); + TYPE_FIELD_BITPOS (result_type, 0) = n2; + TYPE_FIELD_BITPOS (result_type, 1) = n3; + + return result_type; +} + +/* Read a number from the string pointed to by *PP. + The value of *PP is advanced over the number. + If END is nonzero, the character that ends the + number must match END, or an error happens; + and that character is skipped if it does match. + If END is zero, *PP is left pointing to that character. */ + +static long +read_number (pp, end) + char **pp; + int end; +{ + register char *p = *pp; + register long n = 0; + register int c; + int sign = 1; + + /* Handle an optional leading minus sign. */ + + if (*p == '-') + { + sign = -1; + p++; + } + + /* Read the digits, as far as they go. */ + + while ((c = *p++) >= '0' && c <= '9') + { + n *= 10; + n += c - '0'; + } + if (end) + { + if (c && c != end) + error ("Invalid symbol data: invalid character \\%03o at symbol pos %d.", c, symnum); + } + else + --p; + + *pp = p; + return n * sign; +} + +static void +read_huge_number (pp, end, valu, bits) + char **pp; + int end; + long *valu; + int *bits; +{ + char *p = *pp; + int sign = 1; + long n = 0; + int radix = 10; + char overflow = 0; + int nbits = 0; + int c; + long upper_limit; + + /* Handle an optional leading minus sign. */ + + if (*p == '-') + { + sign = -1; + p++; + } + + /* Leading zero means octal. GCC uses this to output values larger + than an int (because that would be hard in decimal). */ + if (*p == '0') + { + radix = 8; + p++; + } + + upper_limit = LONG_MAX / radix; + while ((c = *p++) >= '0' && c <= '9') + { + if (n <= upper_limit) + { + n *= radix; + n += c - '0'; + } + else + overflow = 1; + + /* This depends on large values being output in octal, which is + what GCC does. */ + if (radix == 8) + { + if (nbits == 0) + { + if (c == '0') + /* Ignore leading zeroes. */ + ; + else if (c == '1') + nbits = 1; + else if (c == '2' || c == '3') + nbits = 2; + else + nbits = 3; + } + else + nbits += 3; + } + } + if (end) + { + if (c && c != end) + { + if (bits != NULL) + *bits = -1; + return; + } + } + else + --p; + + *pp = p; + if (overflow) + { + if (nbits == 0) + { + /* Large decimal constants are an error (because it is hard to + count how many bits are in them). */ + if (bits != NULL) + *bits = -1; + return; + } + + /* -0x7f is the same as 0x80. So deal with it by adding one to + the number of bits. */ + if (sign == -1) + ++nbits; + if (bits) + *bits = nbits; + } + else + { + if (valu) + *valu = n * sign; + if (bits) + *bits = 0; + } +} + +/* Read in an argument list. This is a list of types. It is terminated with + a ':', FYI. Return the list of types read in. */ +static struct type ** +read_args (pp, end) + char **pp; + int end; +{ + struct type *types[1024], **rval; /* allow for fns of 1023 parameters */ + int n = 0; + + while (**pp != end) + { + if (**pp != ',') + error ("Invalid argument list: no ',', at symtab pos %d", symnum); + *pp += 1; + + /* Check for and handle cretinous dbx symbol name continuation! */ + if (**pp == '\\') + *pp = next_symbol_text (); + + types[n++] = read_type (pp); + } + *pp += 1; /* get past `end' (the ':' character) */ + + if (n == 1) + { + rval = (struct type **) xmalloc (2 * sizeof (struct type *)); + } + else if (TYPE_CODE (types[n-1]) != TYPE_CODE_VOID) + { + rval = (struct type **) xmalloc ((n + 1) * sizeof (struct type *)); + bzero (rval + n, sizeof (struct type *)); + } + else + { + rval = (struct type **) xmalloc (n * sizeof (struct type *)); + } + bcopy (types, rval, n * sizeof (struct type *)); + return rval; +} + +/* This function is really horrible, but to avoid it, there would need + to be more filling in of forward references. THIS SHOULD BE MOVED OUT + OF COFFREAD.C AND DBXREAD.C TO SOME PLACE WHERE IT CAN BE SHARED */ +int +fill_in_vptr_fieldno (type) + struct type *type; +{ + if (TYPE_VPTR_FIELDNO (type) < 0) + TYPE_VPTR_FIELDNO (type) = + fill_in_vptr_fieldno (TYPE_BASECLASS (type, 1)); + return TYPE_VPTR_FIELDNO (type); +} + +/* Copy a pending list, used to record the contents of a common + block for later fixup. BUG FIX by rde@topexpress.co.uk */ +static struct pending * +copy_pending (beg, begi, end) + struct pending *beg, *end; + int begi; +{ + struct pending *new = 0; + struct pending *next; + + /* rde note: `begi' is an offset in block `end', NOT `beg' */ + for (next = beg; next != 0; next = next->next) + { + register int j; + for (j = next == end ? begi : 0; j < next->nsyms; j++) + add_symbol_to_list (next->symbol[j], &new); + + if (next == end) + break; + } + return new; +} + +/* Add a common block's start address to the offset of each symbol + declared to be in it (by being between a BCOMM/ECOMM pair that uses + the common block name). */ + +static void +fix_common_block (sym, value) + struct symbol *sym; + int value; +{ + struct pending *next = (struct pending *) SYMBOL_NAMESPACE (sym); + for ( ; next; next = next->next) + { + register int j; + for (j = next->nsyms - 1; j >= 0; j--) + SYMBOL_VALUE (next->symbol[j]) += value; + } +} + +void +_initialize_dbxread () +{ + symfile = 0; + header_files = (struct header_file *) 0; + this_object_header_files = (int *) 0; + + undef_types_allocated = 20; + undef_types_length = 0; + undef_types = (struct type **) xmalloc (undef_types_allocated * + sizeof (struct type *)); + + add_com ("symbol-file", class_files, symbol_file_command, + "Load symbol table (in dbx format) from executable file FILE."); + + add_com ("add-file", class_files, add_file_command, + "Load the symbols from FILE, assuming its code is at TEXT_START.") ; +} + +#endif /* READ_DBX_FORMAT */ diff --git a/gnu/usr.bin/gdb/defs.h b/gnu/usr.bin/gdb/defs.h new file mode 100644 index 0000000000..de744fce78 --- /dev/null +++ b/gnu/usr.bin/gdb/defs.h @@ -0,0 +1,122 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + * + * @(#)defs.h 6.3 (Berkeley) 5/8/91 + */ + +/* Basic definitions for GDB, the GNU debugger. + Copyright (C) 1986, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#define CORE_ADDR unsigned int + +#define min(a, b) ((a) < (b) ? (a) : (b)) +#define max(a, b) ((a) > (b) ? (a) : (b)) + +extern char *savestring (); +extern char *concat (); +extern char *xmalloc (), *xrealloc (); +extern int parse_escape (); +extern char *reg_names[]; + +/* Various possibilities for alloca. */ +#ifdef sparc +#include +#else +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +extern char *alloca (); +#endif +#endif + +extern int quit_flag; + +extern int immediate_quit; + +#define QUIT { if (quit_flag) quit (); } + +/* Notes on classes: class_alias is for alias commands which are not + abbreviations of the original command. */ + +enum command_class +{ + no_class = -1, class_run = 0, class_vars, class_stack, + class_files, class_support, class_info, class_breakpoint, + class_alias, class_obscure, class_user, +}; + +/* the cleanup list records things that have to be undone + if an error happens (descriptors to be closed, memory to be freed, etc.) + Each link in the chain records a function to call and an + argument to give it. + + Use make_cleanup to add an element to the cleanup chain. + Use do_cleanups to do all cleanup actions back to a given + point in the chain. Use discard_cleanups to remove cleanups + from the chain back to a given point, not doing them. */ + +struct cleanup +{ + struct cleanup *next; + void (*function) (); + int arg; +}; + +extern void do_cleanups (); +extern void discard_cleanups (); +extern struct cleanup *make_cleanup (); +extern struct cleanup *save_cleanups (); +extern void restore_cleanups (); +extern void free_current_contents (); +extern void reinitialize_more_filter (); +extern void fputs_filtered (); +extern void fprintf_filtered (); +extern void printf_filtered (); +extern void print_spaces_filtered (); +extern char *tilde_expand (); + +/* Structure for saved commands lines + (for breakpoints, defined commands, etc). */ + +struct command_line +{ + struct command_line *next; + char *line; + int type; /* statement type */ +#define CL_END 0 +#define CL_NORMAL 1 +#define CL_WHILE 2 +#define CL_IF 3 +#define CL_EXITLOOP 4 +#define CL_NOP 5 + struct command_line *body; /* body of loop for while, body of if */ + struct command_line *elsebody; /* body of else part of if */ +}; + +extern struct command_line *read_command_lines (); +extern void do_command_lines(); + +/* String containing the current directory (what getwd would return). */ + +char *current_directory; + diff --git a/gnu/usr.bin/gdb/environ.c b/gnu/usr.bin/gdb/environ.c new file mode 100644 index 0000000000..0220166313 --- /dev/null +++ b/gnu/usr.bin/gdb/environ.c @@ -0,0 +1,185 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)environ.c 6.3 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* environ.c -- library for manipulating environments for GNU. + Copyright (C) 1986, 1989 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#define min(a, b) ((a) < (b) ? (a) : (b)) +#define max(a, b) ((a) > (b) ? (a) : (b)) + +#include "environ.h" + +/* Return a new environment object. */ + +struct environ * +make_environ () +{ + register struct environ *e; + + e = (struct environ *) xmalloc (sizeof (struct environ)); + + e->allocated = 10; + e->vector = (char **) xmalloc ((e->allocated + 1) * sizeof (char *)); + e->vector[0] = 0; + return e; +} + +/* Free an environment and all the strings in it. */ + +void +free_environ (e) + register struct environ *e; +{ + register char **vector = e->vector; + + while (*vector) + free (*vector++); + + free (e); +} + +/* Copy the environment given to this process into E. + Also copies all the strings in it, so we can be sure + that all strings in these environments are safe to free. */ + +void +init_environ (e) + register struct environ *e; +{ + extern char **environ; + register int i; + + for (i = 0; environ[i]; i++); + + if (e->allocated < i) + { + e->allocated = max (i, e->allocated + 10); + e->vector = (char **) xrealloc (e->vector, + (e->allocated + 1) * sizeof (char *)); + } + + bcopy (environ, e->vector, (i + 1) * sizeof (char *)); + + while (--i >= 0) + { + register int len = strlen (e->vector[i]) + 1; + register char *new = (char *) xmalloc (len); + bcopy (e->vector[i], new, len); + e->vector[i] = new; + } +} + +/* Return the vector of environment E. + This is used to get something to pass to execve. */ + +char ** +environ_vector (e) + struct environ *e; +{ + return e->vector; +} + +/* Return the value in environment E of variable VAR. */ + +char * +get_in_environ (e, var) + struct environ *e; + char *var; +{ + register int len = strlen (var); + register char **vector = e->vector; + register char *s; + + for (; s = *vector; vector++) + if (!strncmp (s, var, len) + && s[len] == '=') + return &s[len + 1]; + + return 0; +} + +/* Store the value in E of VAR as VALUE. */ + +void +set_in_environ (e, var, value) + struct environ *e; + char *var; + char *value; +{ + register int i; + register int len = strlen (var); + register char **vector = e->vector; + register char *s; + + for (i = 0; s = vector[i]; i++) + if (!strncmp (s, var, len) + && s[len] == '=') + break; + + if (s == 0) + { + if (i == e->allocated) + { + e->allocated += 10; + vector = (char **) xrealloc (vector, + (e->allocated + 1) * sizeof (char *)); + e->vector = vector; + } + vector[i + 1] = 0; + } + else + free (s); + + s = (char *) xmalloc (len + strlen (value) + 2); + strcpy (s, var); + strcat (s, "="); + strcat (s, value); + vector[i] = s; + return; +} + +/* Remove the setting for variable VAR from environment E. */ + +void +unset_in_environ (e, var) + struct environ *e; + char *var; +{ + register int len = strlen (var); + register char **vector = e->vector; + register char *s; + + for (; s = *vector; vector++) + if (!strncmp (s, var, len) + && s[len] == '=') + { + free (s); + bcopy (vector + 1, vector, + (e->allocated - (vector - e->vector)) * sizeof (char *)); + e->vector[e->allocated - 1] = 0; + return; + } +} diff --git a/gnu/usr.bin/gdb/environ.h b/gnu/usr.bin/gdb/environ.h new file mode 100644 index 0000000000..13f31f470a --- /dev/null +++ b/gnu/usr.bin/gdb/environ.h @@ -0,0 +1,39 @@ +/* Header for environment manipulation library. + Copyright (C) 1989, Free Software Foundation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* We manipulate environments represented as these structures. */ + +struct environ +{ + /* Number of usable slots allocated in VECTOR. + VECTOR always has one slot not counted here, + to hold the terminating zero. */ + int allocated; + /* A vector of slots, ALLOCATED + 1 of them. + The first few slots contain strings "VAR=VALUE" + and the next one contains zero. + Then come some unused slots. */ + char **vector; +}; + +struct environ *make_environ (); +void free_environ (); +void init_environ (); +char *get_in_environ (); +void set_in_environ (); +void unset_in_environ (); +char **environ_vector (); diff --git a/gnu/usr.bin/gdb/eval.c b/gnu/usr.bin/gdb/eval.c new file mode 100644 index 0000000000..60779e688f --- /dev/null +++ b/gnu/usr.bin/gdb/eval.c @@ -0,0 +1,1065 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)eval.c 6.3 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Evaluate expressions for GDB. + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "value.h" +#include "expression.h" + + +/* Parse the string EXP as a C expression, evaluate it, + and return the result as a number. */ + +CORE_ADDR +parse_and_eval_address (exp) + char *exp; +{ + struct expression *expr = parse_c_expression (exp); + register CORE_ADDR addr; + register struct cleanup *old_chain + = make_cleanup (free_current_contents, &expr); + + addr = (CORE_ADDR) value_as_long (evaluate_expression (expr)); + do_cleanups (old_chain); + return addr; +} + +/* Like parse_and_eval_address but takes a pointer to a char * variable + and advanced that variable across the characters parsed. */ + +CORE_ADDR +parse_and_eval_address_1 (expptr) + char **expptr; +{ + struct expression *expr = parse_c_1 (expptr, 0, 0); + register CORE_ADDR addr; + register struct cleanup *old_chain + = make_cleanup (free_current_contents, &expr); + + addr = value_as_long (evaluate_expression (expr)); + do_cleanups (old_chain); + return addr; +} + +value +parse_and_eval (exp) + char *exp; +{ + struct expression *expr = parse_c_expression (exp); + register value val; + register struct cleanup *old_chain + = make_cleanup (free_current_contents, &expr); + + val = evaluate_expression (expr); + do_cleanups (old_chain); + return val; +} + +/* Parse up to a comma (or to a closeparen) + in the string EXPP as an expression, evaluate it, and return the value. + EXPP is advanced to point to the comma. */ + +value +parse_to_comma_and_eval (expp) + char **expp; +{ + struct expression *expr = parse_c_1 (expp, 0, 1); + register value val; + register struct cleanup *old_chain + = make_cleanup (free_current_contents, &expr); + + val = evaluate_expression (expr); + do_cleanups (old_chain); + return val; +} + +/* Evaluate an expression in internal prefix form + such as is constructed by expread.y. + + See expression.h for info on the format of an expression. */ + +static value evaluate_subexp (); +static value evaluate_subexp_for_address (); +static value evaluate_subexp_for_sizeof (); +static value evaluate_subexp_with_coercion (); + +/* return true if 'var' has an address in inferior's memory. */ +static int +value_has_lval(var) + register struct symbol *var; +{ + switch (SYMBOL_CLASS(var)) + { + case LOC_STATIC: + case LOC_LABEL: + case LOC_ARG: + case LOC_REF_ARG: + case LOC_LOCAL: + case LOC_BLOCK: + return (1); + } + return (0); +} + +/* Values of NOSIDE argument to eval_subexp. */ +enum noside +{ EVAL_NORMAL, + EVAL_SKIP, /* Only effect is to increment pos. */ + EVAL_AVOID_SIDE_EFFECTS, /* Don't modify any variables or + call any functions. The value + returned will have the correct + type, and will have an + approximately correct lvalue + type (inaccuracy: anything that is + listed as being in a register in + the function in which it was + declared will be lval_register). */ +}; + +value +evaluate_expression (exp) + struct expression *exp; +{ + int pc = 0; + return evaluate_subexp (0, exp, &pc, EVAL_NORMAL); +} + +/* Evaluate an expression, avoiding all memory references + and getting a value whose type alone is correct. */ + +value +evaluate_type (exp) + struct expression *exp; +{ + int pc = 0; + return evaluate_subexp (0, exp, &pc, EVAL_AVOID_SIDE_EFFECTS); +} + +static value +evaluate_subexp (expect_type, exp, pos, noside) + struct type *expect_type; + register struct expression *exp; + register int *pos; + enum noside noside; +{ + enum exp_opcode op; + int tem; + register int pc, pc2, oldpos; + register value arg1, arg2, arg3; + int nargs; + value *argvec; + + pc = (*pos)++; + op = exp->elts[pc].opcode; + + switch (op) + { + case OP_SCOPE: + tem = strlen (&exp->elts[pc + 2].string); + (*pos) += 3 + ((tem + sizeof (union exp_element)) + / sizeof (union exp_element)); + return value_static_field (exp->elts[pc + 1].type, + &exp->elts[pc + 2].string, -1); + + case OP_LONG: + (*pos) += 3; + return value_from_long (exp->elts[pc + 1].type, + exp->elts[pc + 2].longconst); + + case OP_DOUBLE: + (*pos) += 3; + return value_from_double (exp->elts[pc + 1].type, + exp->elts[pc + 2].doubleconst); + + case OP_VAR_VALUE: + (*pos) += 2; + if (noside == EVAL_SKIP) + goto nosideret; + if (noside == EVAL_AVOID_SIDE_EFFECTS) + { + struct symbol * sym = exp->elts[pc + 1].symbol; + enum lval_type lv; + + switch (SYMBOL_CLASS (sym)) + { + case LOC_CONST: + case LOC_LABEL: + case LOC_CONST_BYTES: + lv = not_lval; + case LOC_REGISTER: + case LOC_REGPARM: + lv = lval_register; + default: + lv = lval_memory; + } + + return value_zero (SYMBOL_TYPE (sym), lv); + } + else + return value_of_variable (exp->elts[pc + 1].symbol); + + case OP_LAST: + (*pos) += 2; + return access_value_history ((int) exp->elts[pc + 1].longconst); + + case OP_REGISTER: + (*pos) += 2; + return value_of_register ((int) exp->elts[pc + 1].longconst); + + case OP_INTERNALVAR: + (*pos) += 2; + return value_of_internalvar (exp->elts[pc + 1].internalvar); + + case OP_STRING: + tem = strlen (&exp->elts[pc + 1].string); + (*pos) += 2 + ((tem + sizeof (union exp_element)) + / sizeof (union exp_element)); + if (noside == EVAL_SKIP) + goto nosideret; + return value_string (&exp->elts[pc + 1].string, tem); + + case TERNOP_COND: + /* Skip third and second args to evaluate the first one. */ + arg1 = evaluate_subexp (0, exp, pos, noside); + if (value_zerop (arg1)) + { + evaluate_subexp (0, exp, pos, EVAL_SKIP); + return evaluate_subexp (0, exp, pos, noside); + } + else + { + arg2 = evaluate_subexp (0, exp, pos, noside); + evaluate_subexp (0, exp, pos, EVAL_SKIP); + return arg2; + } + + case OP_FUNCALL: + (*pos) += 2; + op = exp->elts[*pos].opcode; + if (op == STRUCTOP_MEMBER || op == STRUCTOP_MPTR) + { + int fnptr; + int tem2; + + nargs = (int) exp->elts[pc + 1].longconst + 1; + /* First, evaluate the structure into arg2 */ + pc2 = (*pos)++; + + if (noside == EVAL_SKIP) + goto nosideret; + + if (op == STRUCTOP_MEMBER) + { + arg2 = evaluate_subexp_for_address (exp, pos, noside); + } + else + { + arg2 = evaluate_subexp (0, exp, pos, noside); + } + + /* If the function is a virtual function, then the + aggregate value (providing the structure) plays + its part by providing the vtable. Otherwise, + it is just along for the ride: call the function + directly. */ + + arg1 = evaluate_subexp (0, exp, pos, noside); + + fnptr = (int) value_as_long (arg1); + if (fnptr < 128) + { + struct type *basetype; + int i, j; + basetype = TYPE_TARGET_TYPE (VALUE_TYPE (arg2)); + basetype = TYPE_VPTR_BASETYPE (basetype); + for (i = TYPE_NFN_FIELDS (basetype) - 1; i >= 0; i--) + { + struct fn_field *f = TYPE_FN_FIELDLIST1 (basetype, i); + /* If one is virtual, then all are virtual. */ + if (TYPE_FN_FIELD_VIRTUAL_P (f, 0)) + for (j = TYPE_FN_FIELDLIST_LENGTH (basetype, i) - 1; j >= 0; --j) + if (TYPE_FN_FIELD_VOFFSET (f, j) == fnptr) + { + value vtbl; + value base = value_ind (arg2); + struct type *fntype = lookup_pointer_type (TYPE_FN_FIELD_TYPE (f, j)); + + if (TYPE_VPTR_FIELDNO (basetype) < 0) + TYPE_VPTR_FIELDNO (basetype) + = fill_in_vptr_fieldno (basetype); + + VALUE_TYPE (base) = basetype; + vtbl = value_field (base, TYPE_VPTR_FIELDNO (basetype)); + VALUE_TYPE (vtbl) = lookup_pointer_type (fntype); + VALUE_TYPE (arg1) = builtin_type_int; + arg1 = value_subscript (vtbl, arg1); + VALUE_TYPE (arg1) = fntype; + goto got_it; + } + } + if (i < 0) + error ("virtual function at index %d not found", fnptr); + } + else + { + VALUE_TYPE (arg1) = lookup_pointer_type (TYPE_TARGET_TYPE (VALUE_TYPE (arg1))); + } + got_it: + + /* Now, say which argument to start evaluating from */ + tem = 2; + } + else if (op == STRUCTOP_STRUCT || op == STRUCTOP_PTR) + { + /* Hair for method invocations */ + int tem2; + + nargs = (int) exp->elts[pc + 1].longconst + 1; + /* First, evaluate the structure into arg2 */ + pc2 = (*pos)++; + tem2 = strlen (&exp->elts[pc2 + 1].string); + *pos += 2 + (tem2 + sizeof (union exp_element)) / sizeof (union exp_element); + if (noside == EVAL_SKIP) + goto nosideret; + + if (op == STRUCTOP_STRUCT) + { + arg2 = evaluate_subexp_for_address (exp, pos, noside); + } + else + { + arg2 = evaluate_subexp (0, exp, pos, noside); + } + /* Now, say which argument to start evaluating from */ + tem = 2; + } + else + { + nargs = (int) exp->elts[pc + 1].longconst; + tem = 0; + } + argvec = (value *) alloca (sizeof (value) * (nargs + 2)); + for (; tem <= nargs; tem++) + /* Ensure that array expressions are coerced into pointer objects. */ + argvec[tem] = evaluate_subexp_with_coercion (exp, pos, noside); + + /* signal end of arglist */ + argvec[tem] = 0; + + if (op == STRUCTOP_STRUCT || op == STRUCTOP_PTR) + { + int static_memfuncp; + + argvec[1] = arg2; + argvec[0] = + value_struct_elt (arg2, argvec+1, &exp->elts[pc2 + 1].string, + &static_memfuncp, + op == STRUCTOP_STRUCT + ? "structure" : "structure pointer"); + if (static_memfuncp) + { + argvec[1] = argvec[0]; + nargs--; + argvec++; + } + } + else if (op == STRUCTOP_MEMBER || op == STRUCTOP_MPTR) + { + argvec[1] = arg2; + argvec[0] = arg1; + } + + if (noside == EVAL_SKIP) + goto nosideret; + if (noside == EVAL_AVOID_SIDE_EFFECTS) + { + /* If the return type doesn't look like a function type, call an + error. This can happen if somebody tries to turn a variable into + a function call. This is here because people often want to + call, eg, strcmp, which gdb doesn't know is a function. If + gdb isn't asked for it's opinion (ie. through "whatis"), + it won't offer it. */ + + struct type *ftype = + TYPE_TARGET_TYPE (VALUE_TYPE (argvec[0])); + + if (ftype) + return allocate_value (TYPE_TARGET_TYPE (VALUE_TYPE (argvec[0]))); + else + error ("Expression of type other than \"Function returning ...\" used as function"); + } + return call_function (argvec[0], nargs, argvec + 1); + + case STRUCTOP_STRUCT: + tem = strlen (&exp->elts[pc + 1].string); + (*pos) += 2 + ((tem + sizeof (union exp_element)) + / sizeof (union exp_element)); + + /* Try to convert "foo.bar" into "(&foo)->bar" so we won't copy + * the entire contents of a large struct just to extract one + * value from it. */ + if (noside == EVAL_NORMAL && exp->elts[*pos].opcode == OP_VAR_VALUE + && value_has_lval(exp->elts[*pos + 1].symbol)) + arg1 = evaluate_subexp_for_address(exp, pos, noside); + else + arg1 = evaluate_subexp (0, exp, pos, noside); + + if (noside == EVAL_SKIP) + goto nosideret; + if (noside == EVAL_AVOID_SIDE_EFFECTS) + { + register struct type *type = VALUE_TYPE (arg1); + if (TYPE_CODE (type) == TYPE_CODE_PTR) + type = TYPE_TARGET_TYPE (type); + return value_zero (lookup_struct_elt_type (type, + &exp->elts[pc + 1].string), + lval_memory); + } + else + return value_struct_elt (arg1, 0, &exp->elts[pc + 1].string, 0, + "structure"); + + case STRUCTOP_PTR: + tem = strlen (&exp->elts[pc + 1].string); + (*pos) += 2 + (tem + sizeof (union exp_element)) / sizeof (union exp_element); + arg1 = evaluate_subexp (0, exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + if (noside == EVAL_AVOID_SIDE_EFFECTS) + return value_zero (lookup_struct_elt_type (TYPE_TARGET_TYPE + (VALUE_TYPE (arg1)), + &exp->elts[pc + 1].string), + lval_memory); + else + return value_struct_elt (arg1, 0, &exp->elts[pc + 1].string, 0, + "structure pointer"); + + case STRUCTOP_MEMBER: + arg1 = evaluate_subexp_for_address (exp, pos, noside); + arg2 = evaluate_subexp (0, exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + /* Now, convert these values to an address. */ + if (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_PTR + || ((TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2))) + != TYPE_CODE_MEMBER) + && (TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2))) + != TYPE_CODE_METHOD))) + error ("non-pointer-to-member value used in pointer-to-member construct"); + arg3 = value_from_long (builtin_type_long, + value_as_long (arg1) + value_as_long (arg2)); + VALUE_TYPE (arg3) = + lookup_pointer_type (TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2)))); + return value_ind (arg3); + + case STRUCTOP_MPTR: + arg1 = evaluate_subexp (0, exp, pos, noside); + arg2 = evaluate_subexp (0, exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + /* Now, convert these values to an address. */ + if (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_PTR + || (TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2))) != TYPE_CODE_MEMBER + && TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2))) != TYPE_CODE_METHOD)) + error ("non-pointer-to-member value used in pointer-to-member construct"); + arg3 = value_from_long (builtin_type_long, + value_as_long (arg1) + value_as_long (arg2)); + VALUE_TYPE (arg3) = + lookup_pointer_type (TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2)))); + return value_ind (arg3); + + case BINOP_ASSIGN: + arg1 = evaluate_subexp (0, exp, pos, noside); + arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside); + if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS) + return arg1; + if (binop_user_defined_p (op, arg1, arg2)) + return value_x_binop (arg1, arg2, op, 0); + else + return value_assign (arg1, arg2); + + case BINOP_ASSIGN_MODIFY: + (*pos) += 2; + arg1 = evaluate_subexp (0, exp, pos, noside); + arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside); + if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS) + return arg1; + op = exp->elts[pc + 1].opcode; + if (binop_user_defined_p (op, arg1, arg2)) + return value_x_binop (arg1, arg2, BINOP_ASSIGN_MODIFY, op); + else if (op == BINOP_ADD) + arg2 = value_add (arg1, arg2); + else if (op == BINOP_SUB) + arg2 = value_sub (arg1, arg2); + else + arg2 = value_binop (arg1, arg2, op); + return value_assign (arg1, arg2); + + case BINOP_ADD: + arg1 = evaluate_subexp_with_coercion (exp, pos, noside); + arg2 = evaluate_subexp_with_coercion (exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + if (binop_user_defined_p (op, arg1, arg2)) + return value_x_binop (arg1, arg2, op, 0); + else + return value_add (arg1, arg2); + + case BINOP_SUB: + arg1 = evaluate_subexp_with_coercion (exp, pos, noside); + arg2 = evaluate_subexp_with_coercion (exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + if (binop_user_defined_p (op, arg1, arg2)) + return value_x_binop (arg1, arg2, op, 0); + else + return value_sub (arg1, arg2); + + case BINOP_MUL: + case BINOP_DIV: + case BINOP_REM: + case BINOP_LSH: + case BINOP_RSH: + case BINOP_LOGAND: + case BINOP_LOGIOR: + case BINOP_LOGXOR: + arg1 = evaluate_subexp (0, exp, pos, noside); + arg2 = evaluate_subexp (0, exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + if (binop_user_defined_p (op, arg1, arg2)) + return value_x_binop (arg1, arg2, op, 0); + else + if (noside == EVAL_AVOID_SIDE_EFFECTS + && op == BINOP_DIV) + return value_zero (VALUE_TYPE (arg1), not_lval); + else + return value_binop (arg1, arg2, op); + + case BINOP_SUBSCRIPT: + arg1 = evaluate_subexp_with_coercion (exp, pos, noside); + arg2 = evaluate_subexp_with_coercion (exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + if (noside == EVAL_AVOID_SIDE_EFFECTS) + return value_zero (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)), + VALUE_LVAL (arg1)); + + if (binop_user_defined_p (op, arg1, arg2)) + return value_x_binop (arg1, arg2, op, 0); + else + return value_subscript (arg1, arg2); + + case BINOP_AND: + arg1 = evaluate_subexp (0, exp, pos, noside); + if (noside == EVAL_SKIP) + { + arg2 = evaluate_subexp (0, exp, pos, noside); + goto nosideret; + } + + oldpos = *pos; + arg2 = evaluate_subexp (0, exp, pos, EVAL_AVOID_SIDE_EFFECTS); + *pos = oldpos; + + if (binop_user_defined_p (op, arg1, arg2)) + { + arg2 = evaluate_subexp (0, exp, pos, noside); + return value_x_binop (arg1, arg2, op, 0); + } + else + { + tem = value_zerop (arg1); + arg2 = evaluate_subexp (0, exp, pos, + (tem ? EVAL_SKIP : noside)); + return value_from_long (builtin_type_int, + (LONGEST) (!tem && !value_zerop (arg2))); + } + + case BINOP_OR: + arg1 = evaluate_subexp (0, exp, pos, noside); + if (noside == EVAL_SKIP) + { + arg2 = evaluate_subexp (0, exp, pos, noside); + goto nosideret; + } + + oldpos = *pos; + arg2 = evaluate_subexp (0, exp, pos, EVAL_AVOID_SIDE_EFFECTS); + *pos = oldpos; + + if (binop_user_defined_p (op, arg1, arg2)) + { + arg2 = evaluate_subexp (0, exp, pos, noside); + return value_x_binop (arg1, arg2, op, 0); + } + else + { + tem = value_zerop (arg1); + arg2 = evaluate_subexp (0, exp, pos, + (!tem ? EVAL_SKIP : noside)); + return value_from_long (builtin_type_int, + (LONGEST) (!tem || !value_zerop (arg2))); + } + + case BINOP_EQUAL: + arg1 = evaluate_subexp (0, exp, pos, noside); + arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + if (binop_user_defined_p (op, arg1, arg2)) + { + return value_x_binop (arg1, arg2, op, 0); + } + else + { + tem = value_equal (arg1, arg2); + return value_from_long (builtin_type_int, (LONGEST) tem); + } + + case BINOP_NOTEQUAL: + arg1 = evaluate_subexp (0, exp, pos, noside); + arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + if (binop_user_defined_p (op, arg1, arg2)) + { + return value_x_binop (arg1, arg2, op, 0); + } + else + { + tem = value_equal (arg1, arg2); + return value_from_long (builtin_type_int, (LONGEST) ! tem); + } + + case BINOP_LESS: + arg1 = evaluate_subexp (0, exp, pos, noside); + arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + if (binop_user_defined_p (op, arg1, arg2)) + { + return value_x_binop (arg1, arg2, op, 0); + } + else + { + tem = value_less (arg1, arg2); + return value_from_long (builtin_type_int, (LONGEST) tem); + } + + case BINOP_GTR: + arg1 = evaluate_subexp (0, exp, pos, noside); + arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + if (binop_user_defined_p (op, arg1, arg2)) + { + return value_x_binop (arg1, arg2, op, 0); + } + else + { + tem = value_less (arg2, arg1); + return value_from_long (builtin_type_int, (LONGEST) tem); + } + + case BINOP_GEQ: + arg1 = evaluate_subexp (0, exp, pos, noside); + arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + if (binop_user_defined_p (op, arg1, arg2)) + { + return value_x_binop (arg1, arg2, op, 0); + } + else + { + tem = value_less (arg1, arg2); + return value_from_long (builtin_type_int, (LONGEST) ! tem); + } + + case BINOP_LEQ: + arg1 = evaluate_subexp (0, exp, pos, noside); + arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + if (binop_user_defined_p (op, arg1, arg2)) + { + return value_x_binop (arg1, arg2, op, 0); + } + else + { + tem = value_less (arg2, arg1); + return value_from_long (builtin_type_int, (LONGEST) ! tem); + } + + case BINOP_REPEAT: + arg1 = evaluate_subexp (0, exp, pos, noside); + arg2 = evaluate_subexp (0, exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + if (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_INT) + error ("Non-integral right operand for \"@\" operator."); + if (noside == EVAL_AVOID_SIDE_EFFECTS) + return allocate_repeat_value (VALUE_TYPE (arg1), + (int) value_as_long (arg2)); + else + return value_repeat (arg1, (int) value_as_long (arg2)); + + case BINOP_COMMA: + evaluate_subexp (0, exp, pos, noside); + return evaluate_subexp (0, exp, pos, noside); + + case UNOP_NEG: + arg1 = evaluate_subexp (0, exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + if (unop_user_defined_p (op, arg1)) + return value_x_unop (arg1, op); + else + return value_neg (arg1); + + case UNOP_LOGNOT: + arg1 = evaluate_subexp (0, exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + if (unop_user_defined_p (op, arg1)) + return value_x_unop (arg1, op); + else + return value_lognot (arg1); + + case UNOP_ZEROP: + arg1 = evaluate_subexp (0, exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + if (unop_user_defined_p (op, arg1)) + return value_x_unop (arg1, op); + else + return value_from_long (builtin_type_int, + (LONGEST) value_zerop (arg1)); + + case UNOP_IND: + if (expect_type && TYPE_CODE (expect_type) == TYPE_CODE_PTR) + expect_type = TYPE_TARGET_TYPE (expect_type); + arg1 = evaluate_subexp (expect_type, exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + if (noside == EVAL_AVOID_SIDE_EFFECTS) + { + if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR + || TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF + /* In C you can dereference an array to get the 1st elt. */ + || TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_ARRAY + ) + return value_zero (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)), + lval_memory); + else if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_INT) + /* GDB allows dereferencing an int. */ + return value_zero (builtin_type_int, lval_memory); + else + error ("Attempt to take contents of a non-pointer value."); + } + return value_ind (arg1); + + case UNOP_ADDR: + /* C++: check for and handle pointer to members. */ + + op = exp->elts[*pos].opcode; + + if (noside == EVAL_SKIP) + { + if (op == OP_SCOPE) + { + char *name = &exp->elts[pc+3].string; + int tem = strlen (name); + (*pos) += 2 + (tem + sizeof (union exp_element)) / sizeof (union exp_element); + } + else + evaluate_subexp (expect_type, exp, pos, EVAL_SKIP); + goto nosideret; + } + + if (op == OP_SCOPE) + { + char *name = &exp->elts[pc+3].string; + int tem = strlen (name); + struct type *domain = exp->elts[pc+2].type; + (*pos) += 2 + (tem + sizeof (union exp_element)) / sizeof (union exp_element); + arg1 = value_struct_elt_for_address (domain, expect_type, name); + if (arg1) + return arg1; + error ("no field `%s' in structure", name); + } + else + return evaluate_subexp_for_address (exp, pos, noside); + + case UNOP_SIZEOF: + if (noside == EVAL_SKIP) + { + evaluate_subexp (0, exp, pos, EVAL_SKIP); + goto nosideret; + } + return evaluate_subexp_for_sizeof (exp, pos); + + case UNOP_CAST: + (*pos) += 2; + arg1 = evaluate_subexp (expect_type, exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + return value_cast (exp->elts[pc + 1].type, arg1); + + case UNOP_MEMVAL: + (*pos) += 2; + arg1 = evaluate_subexp (expect_type, exp, pos, noside); + if (noside == EVAL_SKIP) + goto nosideret; + if (noside == EVAL_AVOID_SIDE_EFFECTS) + return value_zero (exp->elts[pc + 1].type, lval_memory); + else + return value_at (exp->elts[pc + 1].type, + (CORE_ADDR) value_as_long (arg1)); + + case UNOP_PREINCREMENT: + arg1 = evaluate_subexp (expect_type, exp, pos, noside); + if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS) + return arg1; + else if (unop_user_defined_p (op, arg1)) + { + return value_x_unop (arg1, op); + } + else + { + arg2 = value_add (arg1, value_from_long (builtin_type_char, + (LONGEST) 1)); + return value_assign (arg1, arg2); + } + + case UNOP_PREDECREMENT: + arg1 = evaluate_subexp (expect_type, exp, pos, noside); + if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS) + return arg1; + else if (unop_user_defined_p (op, arg1)) + { + return value_x_unop (arg1, op); + } + else + { + arg2 = value_sub (arg1, value_from_long (builtin_type_char, + (LONGEST) 1)); + return value_assign (arg1, arg2); + } + + case UNOP_POSTINCREMENT: + arg1 = evaluate_subexp (expect_type, exp, pos, noside); + if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS) + return arg1; + else if (unop_user_defined_p (op, arg1)) + { + return value_x_unop (arg1, op); + } + else + { + arg2 = value_add (arg1, value_from_long (builtin_type_char, + (LONGEST) 1)); + value_assign (arg1, arg2); + return arg1; + } + + case UNOP_POSTDECREMENT: + arg1 = evaluate_subexp (expect_type, exp, pos, noside); + if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS) + return arg1; + else if (unop_user_defined_p (op, arg1)) + { + return value_x_unop (arg1, op); + } + else + { + arg2 = value_sub (arg1, value_from_long (builtin_type_char, + (LONGEST) 1)); + value_assign (arg1, arg2); + return arg1; + } + + case OP_THIS: + (*pos) += 1; + return value_of_this (1); + + default: + error ("internal error: I do not know how to evaluate what you gave me"); + } + + nosideret: + return value_from_long (builtin_type_long, (LONGEST) 1); +} + +/* Evaluate a subexpression of EXP, at index *POS, + and return the address of that subexpression. + Advance *POS over the subexpression. + If the subexpression isn't an lvalue, get an error. + NOSIDE may be EVAL_AVOID_SIDE_EFFECTS; + then only the type of the result need be correct. */ + +static value +evaluate_subexp_for_address (exp, pos, noside) + register struct expression *exp; + register int *pos; + enum noside noside; +{ + enum exp_opcode op; + register int pc; + + pc = (*pos); + op = exp->elts[pc].opcode; + + switch (op) + { + case UNOP_IND: + (*pos)++; + return evaluate_subexp (0, exp, pos, noside); + + case UNOP_MEMVAL: + (*pos) += 3; + return value_cast (lookup_pointer_type (exp->elts[pc + 1].type), + evaluate_subexp (0, exp, pos, noside)); + + case OP_VAR_VALUE: + (*pos) += 3; + if (noside == EVAL_AVOID_SIDE_EFFECTS) + { + struct type *type = + lookup_pointer_type (SYMBOL_TYPE (exp->elts[pc + 1].symbol)); + enum address_class sym_class = + SYMBOL_CLASS (exp->elts[pc + 1].symbol); + + if (sym_class == LOC_CONST + || sym_class == LOC_CONST_BYTES + || sym_class == LOC_REGISTER + || sym_class == LOC_REGPARM) + error ("Attempt to take address of register or constant."); + + return + value_zero (type, not_lval); + } + else + return locate_var_value (exp->elts[pc + 1].symbol, (CORE_ADDR) 0); + + default: + if (noside == EVAL_AVOID_SIDE_EFFECTS) + { + value x = evaluate_subexp (0, exp, pos, noside); + if (VALUE_LVAL (x) == lval_memory) + return value_zero (TYPE_POINTER_TYPE (VALUE_TYPE (x)), + not_lval); + else + error ("Attempt to take address of non-lval"); + } + return value_addr (evaluate_subexp (0, exp, pos, noside)); + } +} + +/* Evaluate like `evaluate_subexp' except coercing arrays to pointers. + When used in contexts where arrays will be coerced anyway, + this is equivalent to `evaluate_subexp' + but much faster because it avoids actually fetching array contents. */ + +static value +evaluate_subexp_with_coercion (exp, pos, noside) + register struct expression *exp; + register int *pos; + enum noside noside; +{ + register enum exp_opcode op; + register int pc; + register value val; + + pc = (*pos); + op = exp->elts[pc].opcode; + + switch (op) + { + case OP_VAR_VALUE: + if (TYPE_CODE (SYMBOL_TYPE (exp->elts[pc + 1].symbol)) == TYPE_CODE_ARRAY) + { + (*pos) += 3; + val = locate_var_value (exp->elts[pc + 1].symbol, (CORE_ADDR) 0); + return value_cast (lookup_pointer_type (TYPE_TARGET_TYPE (SYMBOL_TYPE (exp->elts[pc + 1].symbol))), + val); + } + } + + return evaluate_subexp (0, exp, pos, noside); +} + +/* Evaluate a subexpression of EXP, at index *POS, + and return a value for the size of that subexpression. + Advance *POS over the subexpression. */ + +static value +evaluate_subexp_for_sizeof (exp, pos) + register struct expression *exp; + register int *pos; +{ + enum exp_opcode op; + register int pc; + value val; + + pc = (*pos); + op = exp->elts[pc].opcode; + + switch (op) + { + /* This case is handled specially + so that we avoid creating a value for the result type. + If the result type is very big, it's desirable not to + create a value unnecessarily. */ + case UNOP_IND: + (*pos)++; + val = evaluate_subexp (0, exp, pos, EVAL_AVOID_SIDE_EFFECTS); + return value_from_long (builtin_type_int, (LONGEST) + TYPE_LENGTH (TYPE_TARGET_TYPE (VALUE_TYPE (val)))); + + case UNOP_MEMVAL: + (*pos) += 3; + return value_from_long (builtin_type_int, + (LONGEST) TYPE_LENGTH (exp->elts[pc + 1].type)); + + case OP_VAR_VALUE: + (*pos) += 3; + return value_from_long (builtin_type_int, + (LONGEST) TYPE_LENGTH (SYMBOL_TYPE (exp->elts[pc + 1].symbol))); + + default: + val = evaluate_subexp (0, exp, pos, EVAL_AVOID_SIDE_EFFECTS); + return value_from_long (builtin_type_int, + (LONGEST) TYPE_LENGTH (VALUE_TYPE (val))); + } +} diff --git a/gnu/usr.bin/gdb/expprint.c b/gnu/usr.bin/gdb/expprint.c new file mode 100644 index 0000000000..8ac95e9325 --- /dev/null +++ b/gnu/usr.bin/gdb/expprint.c @@ -0,0 +1,324 @@ +/* Print in infix form a struct expression. + Copyright (C) 1986, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include "defs.h" +#include "symtab.h" +#include "param.h" +#include "expression.h" +#include "value.h" + + +/* These codes indicate operator precedences, least tightly binding first. */ +/* Adding 1 to a precedence value is done for binary operators, + on the operand which is more tightly bound, so that operators + of equal precedence within that operand will get parentheses. */ +/* PREC_HYPER and PREC_ABOVE_COMMA are not the precedence of any operator; + they are used as the "surrounding precedence" to force + various kinds of things to be parenthesized. */ +enum precedence +{ PREC_NULL, PREC_COMMA, PREC_ABOVE_COMMA, PREC_ASSIGN, PREC_OR, PREC_AND, + PREC_LOGIOR, PREC_LOGAND, PREC_LOGXOR, PREC_EQUAL, PREC_ORDER, + PREC_SHIFT, PREC_ADD, PREC_MUL, PREC_REPEAT, + PREC_HYPER, PREC_PREFIX, PREC_SUFFIX }; + +/* Table mapping opcodes into strings for printing operators + and precedences of the operators. */ + +struct op_print +{ + char *string; + enum exp_opcode opcode; + /* Precedence of operator. These values are used only by comparisons. */ + enum precedence precedence; + int right_assoc; +}; + +static struct op_print op_print_tab[] = + { + {",", BINOP_COMMA, PREC_COMMA, 0}, + {"=", BINOP_ASSIGN, PREC_ASSIGN, 1}, + {"||", BINOP_OR, PREC_OR, 0}, + {"&&", BINOP_AND, PREC_AND, 0}, + {"|", BINOP_LOGIOR, PREC_LOGIOR, 0}, + {"&", BINOP_LOGAND, PREC_LOGAND, 0}, + {"^", BINOP_LOGXOR, PREC_LOGXOR, 0}, + {"==", BINOP_EQUAL, PREC_EQUAL, 0}, + {"!=", BINOP_NOTEQUAL, PREC_EQUAL, 0}, + {"<=", BINOP_LEQ, PREC_ORDER, 0}, + {">=", BINOP_GEQ, PREC_ORDER, 0}, + {">", BINOP_GTR, PREC_ORDER, 0}, + {"<", BINOP_LESS, PREC_ORDER, 0}, + {">>", BINOP_RSH, PREC_SHIFT, 0}, + {"<<", BINOP_LSH, PREC_SHIFT, 0}, + {"+", BINOP_ADD, PREC_ADD, 0}, + {"-", BINOP_SUB, PREC_ADD, 0}, + {"*", BINOP_MUL, PREC_MUL, 0}, + {"/", BINOP_DIV, PREC_MUL, 0}, + {"%", BINOP_REM, PREC_MUL, 0}, + {"@", BINOP_REPEAT, PREC_REPEAT, 0}, + {"-", UNOP_NEG, PREC_PREFIX, 0}, + {"!", UNOP_ZEROP, PREC_PREFIX, 0}, + {"~", UNOP_LOGNOT, PREC_PREFIX, 0}, + {"*", UNOP_IND, PREC_PREFIX, 0}, + {"&", UNOP_ADDR, PREC_PREFIX, 0}, + {"sizeof ", UNOP_SIZEOF, PREC_PREFIX, 0}, + {"++", UNOP_PREINCREMENT, PREC_PREFIX, 0}, + {"--", UNOP_PREDECREMENT, PREC_PREFIX, 0}, + /* C++ */ + {"::", BINOP_SCOPE, PREC_PREFIX, 0}, + }; + +static void print_subexp (); + +void +print_expression (exp, stream) + struct expression *exp; + FILE *stream; +{ + int pc = 0; + print_subexp (exp, &pc, stream, PREC_NULL); +} + +/* Print the subexpression of EXP that starts in position POS, on STREAM. + PREC is the precedence of the surrounding operator; + if the precedence of the main operator of this subexpression is less, + parentheses are needed here. */ + +static void +print_subexp (exp, pos, stream, prec) + register struct expression *exp; + register int *pos; + FILE *stream; + enum precedence prec; +{ + register int tem; + register int pc; + int nargs; + register char *op_str; + int assign_modify = 0; + enum exp_opcode opcode; + enum precedence myprec; + /* Set to 1 for a right-associative operator. */ + int assoc; + + pc = (*pos)++; + opcode = exp->elts[pc].opcode; + switch (opcode) + { + case OP_SCOPE: + myprec = PREC_PREFIX; + assoc = 0; + (*pos) += 2; + print_subexp (exp, pos, stream, (int) myprec + assoc); + fprintf (stream, " :: "); + nargs = strlen (&exp->elts[pc + 2].string); + (*pos) += 1 + (nargs + sizeof (union exp_element)) / sizeof (union exp_element); + + fprintf (stream, &exp->elts[pc + 2].string); + return; + + case OP_LONG: + (*pos) += 3; + value_print (value_from_long (exp->elts[pc + 1].type, + exp->elts[pc + 2].longconst), + stream, 0, Val_no_prettyprint); + return; + + case OP_DOUBLE: + (*pos) += 3; + value_print (value_from_double (exp->elts[pc + 1].type, + exp->elts[pc + 2].doubleconst), + stream, 0, Val_no_prettyprint); + return; + + case OP_VAR_VALUE: + (*pos) += 2; + fprintf (stream, "%s", SYMBOL_NAME (exp->elts[pc + 1].symbol)); + return; + + case OP_LAST: + (*pos) += 2; + fprintf (stream, "$%d", (int) exp->elts[pc + 1].longconst); + return; + + case OP_REGISTER: + (*pos) += 2; + fprintf (stream, "$%s", reg_names[exp->elts[pc + 1].longconst]); + return; + + case OP_INTERNALVAR: + (*pos) += 2; + fprintf (stream, "$%s", + internalvar_name (exp->elts[pc + 1].internalvar)); + return; + + case OP_FUNCALL: + (*pos) += 2; + nargs = exp->elts[pc + 1].longconst; + print_subexp (exp, pos, stream, PREC_SUFFIX); + fprintf (stream, " ("); + for (tem = 0; tem < nargs; tem++) + { + if (tem > 0) + fprintf (stream, ", "); + print_subexp (exp, pos, stream, PREC_ABOVE_COMMA); + } + fprintf (stream, ")"); + return; + + case OP_STRING: + nargs = strlen (&exp->elts[pc + 1].string); + (*pos) += 2 + (nargs + sizeof (union exp_element)) / sizeof (union exp_element); + fprintf (stream, "\""); + for (tem = 0; tem < nargs; tem++) + printchar ((&exp->elts[pc + 1].string)[tem], stream, '"'); + fprintf (stream, "\""); + return; + + case TERNOP_COND: + if ((int) prec > (int) PREC_COMMA) + fprintf (stream, "("); + /* Print the subexpressions, forcing parentheses + around any binary operations within them. + This is more parentheses than are strictly necessary, + but it looks clearer. */ + print_subexp (exp, pos, stream, PREC_HYPER); + fprintf (stream, " ? "); + print_subexp (exp, pos, stream, PREC_HYPER); + fprintf (stream, " : "); + print_subexp (exp, pos, stream, PREC_HYPER); + if ((int) prec > (int) PREC_COMMA) + fprintf (stream, ")"); + return; + + case STRUCTOP_STRUCT: + tem = strlen (&exp->elts[pc + 1].string); + (*pos) += 2 + (tem + sizeof (union exp_element)) / sizeof (union exp_element); + print_subexp (exp, pos, stream, PREC_SUFFIX); + fprintf (stream, ".%s", &exp->elts[pc + 1].string); + return; + + case STRUCTOP_PTR: + tem = strlen (&exp->elts[pc + 1].string); + (*pos) += 2 + (tem + sizeof (union exp_element)) / sizeof (union exp_element); + print_subexp (exp, pos, stream, PREC_SUFFIX); + fprintf (stream, "->%s", &exp->elts[pc + 1].string); + return; + + case BINOP_SUBSCRIPT: + print_subexp (exp, pos, stream, PREC_SUFFIX); + fprintf (stream, "["); + print_subexp (exp, pos, stream, PREC_ABOVE_COMMA); + fprintf (stream, "]"); + return; + + case UNOP_POSTINCREMENT: + print_subexp (exp, pos, stream, PREC_SUFFIX); + fprintf (stream, "++"); + return; + + case UNOP_POSTDECREMENT: + print_subexp (exp, pos, stream, PREC_SUFFIX); + fprintf (stream, "--"); + return; + + case UNOP_CAST: + (*pos) += 2; + if ((int) prec > (int) PREC_PREFIX) + fprintf (stream, "("); + fprintf (stream, "("); + type_print (exp->elts[pc + 1].type, "", stream, 0); + fprintf (stream, ") "); + print_subexp (exp, pos, stream, PREC_PREFIX); + if ((int) prec > (int) PREC_PREFIX) + fprintf (stream, ")"); + return; + + case UNOP_MEMVAL: + (*pos) += 2; + if ((int) prec > (int) PREC_PREFIX) + fprintf (stream, "("); + fprintf (stream, "{"); + type_print (exp->elts[pc + 1].type, "", stream, 0); + fprintf (stream, "} "); + print_subexp (exp, pos, stream, PREC_PREFIX); + if ((int) prec > (int) PREC_PREFIX) + fprintf (stream, ")"); + return; + + case BINOP_ASSIGN_MODIFY: + opcode = exp->elts[pc + 1].opcode; + (*pos) += 2; + myprec = PREC_ASSIGN; + assoc = 1; + assign_modify = 1; + for (tem = 0; tem < sizeof op_print_tab / sizeof op_print_tab[0]; tem++) + if (op_print_tab[tem].opcode == opcode) + { + op_str = op_print_tab[tem].string; + break; + } + + case OP_THIS: + ++(*pos); + fprintf (stream, "this"); + return; + + default: + for (tem = 0; tem < sizeof op_print_tab / sizeof op_print_tab[0]; tem++) + if (op_print_tab[tem].opcode == opcode) + { + op_str = op_print_tab[tem].string; + myprec = op_print_tab[tem].precedence; + assoc = op_print_tab[tem].right_assoc; + break; + } + } + + if ((int) myprec < (int) prec) + fprintf (stream, "("); + if ((int) opcode > (int) BINOP_END) + { + /* Unary prefix operator. */ + fprintf (stream, "%s", op_str); + print_subexp (exp, pos, stream, PREC_PREFIX); + } + else + { + /* Binary operator. */ + /* Print left operand. + If operator is right-associative, + increment precedence for this operand. */ + print_subexp (exp, pos, stream, (int) myprec + assoc); + /* Print the operator itself. */ + if (assign_modify) + fprintf (stream, " %s= ", op_str); + else if (op_str[0] == ',') + fprintf (stream, "%s ", op_str); + else + fprintf (stream, " %s ", op_str); + /* Print right operand. + If operator is left-associative, + increment precedence for this operand. */ + print_subexp (exp, pos, stream, (int) myprec + !assoc); + } + if ((int) myprec < (int) prec) + fprintf (stream, ")"); +} diff --git a/gnu/usr.bin/gdb/expread.y b/gnu/usr.bin/gdb/expread.y new file mode 100644 index 0000000000..96a12c48c0 --- /dev/null +++ b/gnu/usr.bin/gdb/expread.y @@ -0,0 +1,1782 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +/* Parse C expressions for GDB. + Copyright (C) 1986, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Parse a C expression from text in a string, + and return the result as a struct expression pointer. + That structure contains arithmetic operations in reverse polish, + with constants represented by operations that are followed by special data. + See expression.h for the details of the format. + What is important here is that it can be built up sequentially + during the process of parsing; the lower levels of the tree always + come first in the result. */ + +%{ +#ifndef lint +static char sccsid[] = "@(#)expread.y 6.3 (Berkeley) 5/8/91"; +#endif /* not lint */ + +#include +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "frame.h" +#include "expression.h" + +#include + +static struct expression *expout; +static int expout_size; +static int expout_ptr; + +static int yylex (); +static void yyerror (); +static void write_exp_elt (); +static void write_exp_elt_opcode (); +static void write_exp_elt_sym (); +static void write_exp_elt_longcst (); +static void write_exp_elt_dblcst (); +static void write_exp_elt_type (); +static void write_exp_elt_intern (); +static void write_exp_string (); +static void start_arglist (); +static int end_arglist (); +static void free_funcalls (); +static char *copy_name (); + +/* If this is nonzero, this block is used as the lexical context + for symbol names. */ + +static struct block *expression_context_block; + +/* The innermost context required by the stack and register variables + we've encountered so far. */ +struct block *innermost_block; + +/* The block in which the most recently discovered symbol was found. */ +struct block *block_found; + +/* Number of arguments seen so far in innermost function call. */ +static int arglist_len; + +/* Data structure for saving values of arglist_len + for function calls whose arguments contain other function calls. */ + +struct funcall + { + struct funcall *next; + int arglist_len; + }; + +struct funcall *funcall_chain; + +/* This kind of datum is used to represent the name + of a symbol token. */ + +struct stoken + { + char *ptr; + int length; + }; + +/* For parsing of complicated types. + An array should be preceded in the list by the size of the array. */ +enum type_pieces + {tp_end = -1, tp_pointer, tp_reference, tp_array, tp_function}; +static enum type_pieces *type_stack; +static int type_stack_depth, type_stack_size; + +static void push_type (); +static enum type_pieces pop_type (); + +/* Allow debugging of parsing. */ +#define YYDEBUG 1 +%} + +/* Although the yacc "value" of an expression is not used, + since the result is stored in the structure being created, + other node types do have values. */ + +%union + { + LONGEST lval; + unsigned LONGEST ulval; + double dval; + struct symbol *sym; + struct type *tval; + struct stoken sval; + int voidval; + struct block *bval; + enum exp_opcode opcode; + struct internalvar *ivar; + + struct type **tvec; + int *ivec; + } + +%type exp exp1 start variable +%type type typebase +%type nonempty_typelist +%type block + +/* Fancy type parsing. */ +%type func_mod direct_abs_decl abs_decl +%type ptype +%type array_mod + +%token INT CHAR +%token UINT +%token FLOAT + +/* Both NAME and TYPENAME tokens represent symbols in the input, + and both convey their data as strings. + But a TYPENAME is a string that happens to be defined as a typedef + or builtin type name (such as int or char) + and a NAME is any other symbol. + + Contexts where this distinction is not important can use the + nonterminal "name", which matches either NAME or TYPENAME. */ + +%token NAME TYPENAME BLOCKNAME STRING +%type name name_not_typename typename + +%token STRUCT UNION ENUM SIZEOF UNSIGNED COLONCOLON + +/* Special type cases, put in to allow the parser to distinguish different + legal basetypes. */ +%token SIGNED LONG SHORT INT_KEYWORD + +%token LAST REGNAME + +%token VARIABLE + +%token ASSIGN_MODIFY + +/* C++ */ +%token THIS + +%left ',' +%left ABOVE_COMMA +%right '=' ASSIGN_MODIFY +%right '?' +%left OR +%left AND +%left '|' +%left '^' +%left '&' +%left EQUAL NOTEQUAL +%left '<' '>' LEQ GEQ +%left LSH RSH +%left '@' +%left '+' '-' +%left '*' '/' '%' +%right UNARY INCREMENT DECREMENT +%right ARROW '.' '[' '(' +%left COLONCOLON + +%% + +start : exp1 + ; + +/* Expressions, including the comma operator. */ +exp1 : exp + | exp1 ',' exp + { write_exp_elt_opcode (BINOP_COMMA); } + ; + +/* Expressions, not including the comma operator. */ +exp : '*' exp %prec UNARY + { write_exp_elt_opcode (UNOP_IND); } + +exp : '&' exp %prec UNARY + { write_exp_elt_opcode (UNOP_ADDR); } + +exp : '-' exp %prec UNARY + { write_exp_elt_opcode (UNOP_NEG); } + ; + +exp : '!' exp %prec UNARY + { write_exp_elt_opcode (UNOP_ZEROP); } + ; + +exp : '~' exp %prec UNARY + { write_exp_elt_opcode (UNOP_LOGNOT); } + ; + +exp : INCREMENT exp %prec UNARY + { write_exp_elt_opcode (UNOP_PREINCREMENT); } + ; + +exp : DECREMENT exp %prec UNARY + { write_exp_elt_opcode (UNOP_PREDECREMENT); } + ; + +exp : exp INCREMENT %prec UNARY + { write_exp_elt_opcode (UNOP_POSTINCREMENT); } + ; + +exp : exp DECREMENT %prec UNARY + { write_exp_elt_opcode (UNOP_POSTDECREMENT); } + ; + +exp : SIZEOF exp %prec UNARY + { write_exp_elt_opcode (UNOP_SIZEOF); } + ; + +exp : exp ARROW name + { write_exp_elt_opcode (STRUCTOP_PTR); + write_exp_string ($3); + write_exp_elt_opcode (STRUCTOP_PTR); } + ; + +exp : exp ARROW '*' exp + { write_exp_elt_opcode (STRUCTOP_MPTR); } + ; + +exp : exp '.' name + { write_exp_elt_opcode (STRUCTOP_STRUCT); + write_exp_string ($3); + write_exp_elt_opcode (STRUCTOP_STRUCT); } + ; + +exp : exp '.' '*' exp + { write_exp_elt_opcode (STRUCTOP_MEMBER); } + ; + +exp : exp '[' exp1 ']' + { write_exp_elt_opcode (BINOP_SUBSCRIPT); } + ; + +exp : exp '(' + /* This is to save the value of arglist_len + being accumulated by an outer function call. */ + { start_arglist (); } + arglist ')' %prec ARROW + { write_exp_elt_opcode (OP_FUNCALL); + write_exp_elt_longcst ((LONGEST) end_arglist ()); + write_exp_elt_opcode (OP_FUNCALL); } + ; + +arglist : + ; + +arglist : exp + { arglist_len = 1; } + ; + +arglist : arglist ',' exp %prec ABOVE_COMMA + { arglist_len++; } + ; + +exp : '{' type '}' exp %prec UNARY + { write_exp_elt_opcode (UNOP_MEMVAL); + write_exp_elt_type ($2); + write_exp_elt_opcode (UNOP_MEMVAL); } + ; + +exp : '(' type ')' exp %prec UNARY + { write_exp_elt_opcode (UNOP_CAST); + write_exp_elt_type ($2); + write_exp_elt_opcode (UNOP_CAST); } + ; + +exp : '(' exp1 ')' + { } + ; + +/* Binary operators in order of decreasing precedence. */ + +exp : exp '@' exp + { write_exp_elt_opcode (BINOP_REPEAT); } + ; + +exp : exp '*' exp + { write_exp_elt_opcode (BINOP_MUL); } + ; + +exp : exp '/' exp + { write_exp_elt_opcode (BINOP_DIV); } + ; + +exp : exp '%' exp + { write_exp_elt_opcode (BINOP_REM); } + ; + +exp : exp '+' exp + { write_exp_elt_opcode (BINOP_ADD); } + ; + +exp : exp '-' exp + { write_exp_elt_opcode (BINOP_SUB); } + ; + +exp : exp LSH exp + { write_exp_elt_opcode (BINOP_LSH); } + ; + +exp : exp RSH exp + { write_exp_elt_opcode (BINOP_RSH); } + ; + +exp : exp EQUAL exp + { write_exp_elt_opcode (BINOP_EQUAL); } + ; + +exp : exp NOTEQUAL exp + { write_exp_elt_opcode (BINOP_NOTEQUAL); } + ; + +exp : exp LEQ exp + { write_exp_elt_opcode (BINOP_LEQ); } + ; + +exp : exp GEQ exp + { write_exp_elt_opcode (BINOP_GEQ); } + ; + +exp : exp '<' exp + { write_exp_elt_opcode (BINOP_LESS); } + ; + +exp : exp '>' exp + { write_exp_elt_opcode (BINOP_GTR); } + ; + +exp : exp '&' exp + { write_exp_elt_opcode (BINOP_LOGAND); } + ; + +exp : exp '^' exp + { write_exp_elt_opcode (BINOP_LOGXOR); } + ; + +exp : exp '|' exp + { write_exp_elt_opcode (BINOP_LOGIOR); } + ; + +exp : exp AND exp + { write_exp_elt_opcode (BINOP_AND); } + ; + +exp : exp OR exp + { write_exp_elt_opcode (BINOP_OR); } + ; + +exp : exp '?' exp ':' exp %prec '?' + { write_exp_elt_opcode (TERNOP_COND); } + ; + +exp : exp '=' exp + { write_exp_elt_opcode (BINOP_ASSIGN); } + ; + +exp : exp ASSIGN_MODIFY exp + { write_exp_elt_opcode (BINOP_ASSIGN_MODIFY); + write_exp_elt_opcode ($2); + write_exp_elt_opcode (BINOP_ASSIGN_MODIFY); } + ; + +exp : INT + { write_exp_elt_opcode (OP_LONG); + if ($1 == (int) $1 || $1 == (unsigned int) $1) + write_exp_elt_type (builtin_type_int); + else + write_exp_elt_type (BUILTIN_TYPE_LONGEST); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_LONG); } + ; + +exp : UINT + { + write_exp_elt_opcode (OP_LONG); + if ($1 == (unsigned int) $1) + write_exp_elt_type (builtin_type_unsigned_int); + else + write_exp_elt_type (BUILTIN_TYPE_UNSIGNED_LONGEST); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_LONG); + } + ; + +exp : CHAR + { write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (builtin_type_char); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_LONG); } + ; + +exp : FLOAT + { write_exp_elt_opcode (OP_DOUBLE); + write_exp_elt_type (builtin_type_double); + write_exp_elt_dblcst ($1); + write_exp_elt_opcode (OP_DOUBLE); } + ; + +exp : variable + ; + +exp : LAST + { write_exp_elt_opcode (OP_LAST); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_LAST); } + ; + +exp : REGNAME + { write_exp_elt_opcode (OP_REGISTER); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_REGISTER); } + ; + +exp : VARIABLE + { write_exp_elt_opcode (OP_INTERNALVAR); + write_exp_elt_intern ($1); + write_exp_elt_opcode (OP_INTERNALVAR); } + ; + +exp : SIZEOF '(' type ')' %prec UNARY + { write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (builtin_type_int); + write_exp_elt_longcst ((LONGEST) TYPE_LENGTH ($3)); + write_exp_elt_opcode (OP_LONG); } + ; + +exp : STRING + { write_exp_elt_opcode (OP_STRING); + write_exp_string ($1); + write_exp_elt_opcode (OP_STRING); } + ; + +/* C++. */ +exp : THIS + { write_exp_elt_opcode (OP_THIS); + write_exp_elt_opcode (OP_THIS); } + ; + +/* end of C++. */ + +block : BLOCKNAME + { + struct symtab *tem = lookup_symtab (copy_name ($1)); + struct symbol *sym; + + if (tem) + $$ = BLOCKVECTOR_BLOCK (BLOCKVECTOR (tem), 1); + else + { + sym = lookup_symbol (copy_name ($1), + expression_context_block, + VAR_NAMESPACE, 0); + if (sym && SYMBOL_CLASS (sym) == LOC_BLOCK) + $$ = SYMBOL_BLOCK_VALUE (sym); + else + error ("No file or function \"%s\".", + copy_name ($1)); + } + } + ; + +block : block COLONCOLON name + { struct symbol *tem + = lookup_symbol (copy_name ($3), $1, VAR_NAMESPACE, 0); + if (!tem || SYMBOL_CLASS (tem) != LOC_BLOCK) + error ("No function \"%s\" in specified context.", + copy_name ($3)); + $$ = SYMBOL_BLOCK_VALUE (tem); } + ; + +variable: block COLONCOLON name + { struct symbol *sym; + sym = lookup_symbol (copy_name ($3), $1, VAR_NAMESPACE, 0); + if (sym == 0) + error ("No symbol \"%s\" in specified context.", + copy_name ($3)); + write_exp_elt_opcode (OP_VAR_VALUE); + write_exp_elt_sym (sym); + write_exp_elt_opcode (OP_VAR_VALUE); } + ; + +variable: typebase COLONCOLON name + { + struct type *type = $1; + if (TYPE_CODE (type) != TYPE_CODE_STRUCT + && TYPE_CODE (type) != TYPE_CODE_UNION) + error ("`%s' is not defined as an aggregate type.", + TYPE_NAME (type)); + + write_exp_elt_opcode (OP_SCOPE); + write_exp_elt_type (type); + write_exp_string ($3); + write_exp_elt_opcode (OP_SCOPE); + } + | COLONCOLON name + { + char *name = copy_name ($2); + struct symbol *sym; + int i; + + sym = lookup_symbol (name, 0, VAR_NAMESPACE, 0); + if (sym) + { + write_exp_elt_opcode (OP_VAR_VALUE); + write_exp_elt_sym (sym); + write_exp_elt_opcode (OP_VAR_VALUE); + break; + } + for (i = 0; i < misc_function_count; i++) + if (!strcmp (misc_function_vector[i].name, name)) + break; + + if (i < misc_function_count) + { + enum misc_function_type mft = + (enum misc_function_type) + misc_function_vector[i].type; + + write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (builtin_type_int); + write_exp_elt_longcst ((LONGEST) misc_function_vector[i].address); + write_exp_elt_opcode (OP_LONG); + write_exp_elt_opcode (UNOP_MEMVAL); + if (mft == mf_data || mft == mf_bss) + write_exp_elt_type (builtin_type_int); + else if (mft == mf_text) + write_exp_elt_type (lookup_function_type (builtin_type_int)); + else + write_exp_elt_type (builtin_type_char); + write_exp_elt_opcode (UNOP_MEMVAL); + } + else + if (symtab_list == 0 + && partial_symtab_list == 0) + error ("No symbol table is loaded. Use the \"symbol-file\" command."); + else + error ("No symbol \"%s\" in current context.", name); + } + ; + +variable: name_not_typename + { struct symbol *sym; + int is_a_field_of_this; + + sym = lookup_symbol (copy_name ($1), + expression_context_block, + VAR_NAMESPACE, + &is_a_field_of_this); + if (sym) + { + switch (sym->class) + { + case LOC_REGISTER: + case LOC_ARG: + case LOC_LOCAL: + if (innermost_block == 0 || + contained_in (block_found, + innermost_block)) + innermost_block = block_found; + } + write_exp_elt_opcode (OP_VAR_VALUE); + write_exp_elt_sym (sym); + write_exp_elt_opcode (OP_VAR_VALUE); + } + else if (is_a_field_of_this) + { + /* C++: it hangs off of `this'. Must + not inadvertently convert from a method call + to data ref. */ + if (innermost_block == 0 || + contained_in (block_found, innermost_block)) + innermost_block = block_found; + write_exp_elt_opcode (OP_THIS); + write_exp_elt_opcode (OP_THIS); + write_exp_elt_opcode (STRUCTOP_PTR); + write_exp_string ($1); + write_exp_elt_opcode (STRUCTOP_PTR); + } + else + { + register int i; + register char *arg = copy_name ($1); + + for (i = 0; i < misc_function_count; i++) + if (!strcmp (misc_function_vector[i].name, arg)) + break; + + if (i < misc_function_count) + { + enum misc_function_type mft = + (enum misc_function_type) + misc_function_vector[i].type; + + write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (builtin_type_int); + write_exp_elt_longcst ((LONGEST) misc_function_vector[i].address); + write_exp_elt_opcode (OP_LONG); + write_exp_elt_opcode (UNOP_MEMVAL); + if (mft == mf_data || mft == mf_bss) + write_exp_elt_type (builtin_type_int); + else if (mft == mf_text) + write_exp_elt_type (lookup_function_type (builtin_type_int)); + else + write_exp_elt_type (builtin_type_char); + write_exp_elt_opcode (UNOP_MEMVAL); + } + else if (symtab_list == 0 + && partial_symtab_list == 0) + error ("No symbol table is loaded. Use the \"symbol-file\" command."); + else + error ("No symbol \"%s\" in current context.", + copy_name ($1)); + } + } + ; + + +ptype : typebase + | typebase abs_decl + { + /* This is where the interesting stuff happens. */ + int done = 0; + int array_size; + struct type *follow_type = $1; + + while (!done) + switch (pop_type ()) + { + case tp_end: + done = 1; + break; + case tp_pointer: + follow_type = lookup_pointer_type (follow_type); + break; + case tp_reference: + follow_type = lookup_reference_type (follow_type); + break; + case tp_array: + array_size = (int) pop_type (); + if (array_size != -1) + follow_type = create_array_type (follow_type, + array_size); + else + follow_type = lookup_pointer_type (follow_type); + break; + case tp_function: + follow_type = lookup_function_type (follow_type); + break; + } + $$ = follow_type; + } + ; + +abs_decl: '*' + { push_type (tp_pointer); $$ = 0; } + | '*' abs_decl + { push_type (tp_pointer); $$ = $2; } + | direct_abs_decl + ; + +direct_abs_decl: '(' abs_decl ')' + { $$ = $2; } + | direct_abs_decl array_mod + { + push_type ((enum type_pieces) $2); + push_type (tp_array); + } + | array_mod + { + push_type ((enum type_pieces) $1); + push_type (tp_array); + $$ = 0; + } + | direct_abs_decl func_mod + { push_type (tp_function); } + | func_mod + { push_type (tp_function); } + ; + +array_mod: '[' ']' + { $$ = -1; } + | '[' INT ']' + { $$ = $2; } + ; + +func_mod: '(' ')' + { $$ = 0; } + ; + +type : ptype + | typebase COLONCOLON '*' + { $$ = lookup_member_type (builtin_type_int, $1); } + | type '(' typebase COLONCOLON '*' ')' + { $$ = lookup_member_type ($1, $3); } + | type '(' typebase COLONCOLON '*' ')' '(' ')' + { $$ = lookup_member_type + (lookup_function_type ($1), $3); } + | type '(' typebase COLONCOLON '*' ')' '(' nonempty_typelist ')' + { $$ = lookup_member_type + (lookup_function_type ($1), $3); + free ($8); } + ; + +typebase + : TYPENAME + { $$ = lookup_typename (copy_name ($1), + expression_context_block, 0); } + | INT_KEYWORD + { $$ = builtin_type_int; } + | LONG + { $$ = builtin_type_long; } + | SHORT + { $$ = builtin_type_short; } + | LONG INT_KEYWORD + { $$ = builtin_type_long; } + | UNSIGNED LONG INT_KEYWORD + { $$ = builtin_type_unsigned_long; } + | SHORT INT_KEYWORD + { $$ = builtin_type_short; } + | UNSIGNED SHORT INT_KEYWORD + { $$ = builtin_type_unsigned_short; } + | STRUCT name + { $$ = lookup_struct (copy_name ($2), + expression_context_block); } + | UNION name + { $$ = lookup_union (copy_name ($2), + expression_context_block); } + | ENUM name + { $$ = lookup_enum (copy_name ($2), + expression_context_block); } + | UNSIGNED typename + { $$ = lookup_unsigned_typename (copy_name ($2)); } + | UNSIGNED + { $$ = builtin_type_unsigned_int; } + | SIGNED typename + { $$ = lookup_typename (copy_name ($2), + expression_context_block, 0); } + | SIGNED + { $$ = builtin_type_int; } + ; + +typename: TYPENAME + | INT_KEYWORD + { + $$.ptr = "int"; + $$.length = 3; + } + | LONG + { + $$.ptr = "long"; + $$.length = 4; + } + | SHORT + { + $$.ptr = "short"; + $$.length = 5; + } + ; + +nonempty_typelist + : type + { $$ = (struct type **)xmalloc (sizeof (struct type *) * 2); + $$[0] = (struct type *)0; + $$[1] = $1; + } + | nonempty_typelist ',' type + { int len = sizeof (struct type *) * ++($1[0]); + $$ = (struct type **)xrealloc ($1, len); + $$[$$[0]] = $3; + } + ; + +name : NAME + | BLOCKNAME + | TYPENAME + ; + +name_not_typename : NAME + | BLOCKNAME + ; + +%% + +/* Begin counting arguments for a function call, + saving the data about any containing call. */ + +static void +start_arglist () +{ + register struct funcall *new = (struct funcall *) xmalloc (sizeof (struct funcall)); + + new->next = funcall_chain; + new->arglist_len = arglist_len; + arglist_len = 0; + funcall_chain = new; +} + +/* Return the number of arguments in a function call just terminated, + and restore the data for the containing function call. */ + +static int +end_arglist () +{ + register int val = arglist_len; + register struct funcall *call = funcall_chain; + funcall_chain = call->next; + arglist_len = call->arglist_len; + free (call); + return val; +} + +/* Free everything in the funcall chain. + Used when there is an error inside parsing. */ + +static void +free_funcalls () +{ + register struct funcall *call, *next; + + for (call = funcall_chain; call; call = next) + { + next = call->next; + free (call); + } +} + +/* This page contains the functions for adding data to the struct expression + being constructed. */ + +/* Add one element to the end of the expression. */ + +/* To avoid a bug in the Sun 4 compiler, we pass things that can fit into + a register through here */ + +static void +write_exp_elt (expelt) + union exp_element expelt; +{ + if (expout_ptr >= expout_size) + { + expout_size *= 2; + expout = (struct expression *) xrealloc (expout, + sizeof (struct expression) + + expout_size * sizeof (union exp_element)); + } + expout->elts[expout_ptr++] = expelt; +} + +static void +write_exp_elt_opcode (expelt) + enum exp_opcode expelt; +{ + union exp_element tmp; + + tmp.opcode = expelt; + + write_exp_elt (tmp); +} + +static void +write_exp_elt_sym (expelt) + struct symbol *expelt; +{ + union exp_element tmp; + + tmp.symbol = expelt; + + write_exp_elt (tmp); +} + +static void +write_exp_elt_longcst (expelt) + LONGEST expelt; +{ + union exp_element tmp; + + tmp.longconst = expelt; + + write_exp_elt (tmp); +} + +static void +write_exp_elt_dblcst (expelt) + double expelt; +{ + union exp_element tmp; + + tmp.doubleconst = expelt; + + write_exp_elt (tmp); +} + +static void +write_exp_elt_type (expelt) + struct type *expelt; +{ + union exp_element tmp; + + tmp.type = expelt; + + write_exp_elt (tmp); +} + +static void +write_exp_elt_intern (expelt) + struct internalvar *expelt; +{ + union exp_element tmp; + + tmp.internalvar = expelt; + + write_exp_elt (tmp); +} + +/* Add a string constant to the end of the expression. + Follow it by its length in bytes, as a separate exp_element. */ + +static void +write_exp_string (str) + struct stoken str; +{ + register int len = str.length; + register int lenelt + = (len + sizeof (union exp_element)) / sizeof (union exp_element); + + expout_ptr += lenelt; + + if (expout_ptr >= expout_size) + { + expout_size = max (expout_size * 2, expout_ptr + 10); + expout = (struct expression *) + xrealloc (expout, (sizeof (struct expression) + + (expout_size * sizeof (union exp_element)))); + } + bcopy (str.ptr, (char *) &expout->elts[expout_ptr - lenelt], len); + ((char *) &expout->elts[expout_ptr - lenelt])[len] = 0; + write_exp_elt_longcst ((LONGEST) len); +} + +/* During parsing of a C expression, the pointer to the next character + is in this variable. */ + +static char *lexptr; + +/* Tokens that refer to names do so with explicit pointer and length, + so they can share the storage that lexptr is parsing. + + When it is necessary to pass a name to a function that expects + a null-terminated string, the substring is copied out + into a block of storage that namecopy points to. + + namecopy is allocated once, guaranteed big enough, for each parsing. */ + +static char *namecopy; + +/* Current depth in parentheses within the expression. */ + +static int paren_depth; + +/* Nonzero means stop parsing on first comma (if not within parentheses). */ + +static int comma_terminates; + +/* Take care of parsing a number (anything that starts with a digit). + Set yylval and return the token type; update lexptr. + LEN is the number of characters in it. */ + +/*** Needs some error checking for the float case ***/ + +static int +parse_number (olen) + int olen; +{ + register char *p = lexptr; + register LONGEST n = 0; + register int c; + register int base = 10; + register int len = olen; + char *err_copy; + int unsigned_p = 0; + + extern double atof (); + + for (c = 0; c < len; c++) + if (p[c] == '.') + { + /* It's a float since it contains a point. */ + yylval.dval = atof (p); + lexptr += len; + return FLOAT; + } + + if (len >= 3 && (!strncmp (p, "0x", 2) || !strncmp (p, "0X", 2))) + { + p += 2; + base = 16; + len -= 2; + } + else if (*p == '0') + base = 8; + + while (len-- > 0) + { + c = *p++; + if (c >= 'A' && c <= 'Z') c += 'a' - 'A'; + if (c != 'l' && c != 'u') + n *= base; + if (c >= '0' && c <= '9') + n += c - '0'; + else + { + if (base == 16 && c >= 'a' && c <= 'f') + n += c - 'a' + 10; + else if (len == 0 && c == 'l') + ; + else if (len == 0 && c == 'u') + unsigned_p = 1; + else if (base == 10 && len != 0 && (c == 'e' || c == 'E')) + { + /* Scientific notation, where we are unlucky enough not + to have a '.' in the string. */ + yylval.dval = atof (lexptr); + lexptr += olen; + return FLOAT; + } + else + { + err_copy = (char *) alloca (olen + 1); + bcopy (lexptr, err_copy, olen); + err_copy[olen] = 0; + error ("Invalid number \"%s\".", err_copy); + } + } + } + + lexptr = p; + if (unsigned_p) + { + yylval.ulval = n; + return UINT; + } + else + { + yylval.lval = n; + return INT; + } +} + +struct token +{ + char *operator; + int token; + enum exp_opcode opcode; +}; + +static struct token tokentab3[] = + { + {">>=", ASSIGN_MODIFY, BINOP_RSH}, + {"<<=", ASSIGN_MODIFY, BINOP_LSH} + }; + +static struct token tokentab2[] = + { + {"+=", ASSIGN_MODIFY, BINOP_ADD}, + {"-=", ASSIGN_MODIFY, BINOP_SUB}, + {"*=", ASSIGN_MODIFY, BINOP_MUL}, + {"/=", ASSIGN_MODIFY, BINOP_DIV}, + {"%=", ASSIGN_MODIFY, BINOP_REM}, + {"|=", ASSIGN_MODIFY, BINOP_LOGIOR}, + {"&=", ASSIGN_MODIFY, BINOP_LOGAND}, + {"^=", ASSIGN_MODIFY, BINOP_LOGXOR}, + {"++", INCREMENT, BINOP_END}, + {"--", DECREMENT, BINOP_END}, + {"->", ARROW, BINOP_END}, + {"&&", AND, BINOP_END}, + {"||", OR, BINOP_END}, + {"::", COLONCOLON, BINOP_END}, + {"<<", LSH, BINOP_END}, + {">>", RSH, BINOP_END}, + {"==", EQUAL, BINOP_END}, + {"!=", NOTEQUAL, BINOP_END}, + {"<=", LEQ, BINOP_END}, + {">=", GEQ, BINOP_END} + }; + +/* assign machine-independent names to certain registers + * (unless overridden by the REGISTER_NAMES table) + */ +struct std_regs { + char *name; + int regnum; +} std_regs[] = { +#ifdef PC_REGNUM + { "pc", PC_REGNUM }, +#endif +#ifdef FP_REGNUM + { "fp", FP_REGNUM }, +#endif +#ifdef SP_REGNUM + { "sp", SP_REGNUM }, +#endif +#ifdef PS_REGNUM + { "ps", PS_REGNUM }, +#endif +}; + +#define NUM_STD_REGS (sizeof std_regs / sizeof std_regs[0]) + +/* Read one token, getting characters through lexptr. */ + +static int +yylex () +{ + register int c; + register int namelen; + register int i; + register char *tokstart; + + retry: + + tokstart = lexptr; + /* See if it is a special token of length 3. */ + for (i = 0; i < sizeof tokentab3 / sizeof tokentab3[0]; i++) + if (!strncmp (tokstart, tokentab3[i].operator, 3)) + { + lexptr += 3; + yylval.opcode = tokentab3[i].opcode; + return tokentab3[i].token; + } + + /* See if it is a special token of length 2. */ + for (i = 0; i < sizeof tokentab2 / sizeof tokentab2[0]; i++) + if (!strncmp (tokstart, tokentab2[i].operator, 2)) + { + lexptr += 2; + yylval.opcode = tokentab2[i].opcode; + return tokentab2[i].token; + } + + switch (c = *tokstart) + { + case 0: + return 0; + + case ' ': + case '\t': + case '\n': + lexptr++; + goto retry; + + case '\'': + lexptr++; + c = *lexptr++; + if (c == '\\') + c = parse_escape (&lexptr); + yylval.lval = c; + c = *lexptr++; + if (c != '\'') + error ("Invalid character constant."); + return CHAR; + + case '(': + paren_depth++; + lexptr++; + return c; + + case ')': + if (paren_depth == 0) + return 0; + paren_depth--; + lexptr++; + return c; + + case ',': + if (comma_terminates && paren_depth == 0) + return 0; + lexptr++; + return c; + + case '.': + /* Might be a floating point number. */ + if (lexptr[1] >= '0' && lexptr[1] <= '9') + break; /* Falls into number code. */ + + case '+': + case '-': + case '*': + case '/': + case '%': + case '|': + case '&': + case '^': + case '~': + case '!': + case '@': + case '<': + case '>': + case '[': + case ']': + case '?': + case ':': + case '=': + case '{': + case '}': + lexptr++; + return c; + + case '"': + for (namelen = 1; (c = tokstart[namelen]) != '"'; namelen++) + if (c == '\\') + { + c = tokstart[++namelen]; + if (c >= '0' && c <= '9') + { + c = tokstart[++namelen]; + if (c >= '0' && c <= '9') + c = tokstart[++namelen]; + } + } + yylval.sval.ptr = tokstart + 1; + yylval.sval.length = namelen - 1; + lexptr += namelen + 1; + return STRING; + } + + /* Is it a number? */ + /* Note: We have already dealt with the case of the token '.'. + See case '.' above. */ + if ((c >= '0' && c <= '9') || c == '.') + { + /* It's a number. */ + int got_dot = 0, got_e = 0; + register char *p = tokstart; + int hex = c == '0' && (p[1] == 'x' || p[1] == 'X'); + if (hex) + p += 2; + for (;; ++p) + { + if (!hex && !got_e && (*p == 'e' || *p == 'E')) + got_dot = got_e = 1; + else if (!hex && !got_dot && *p == '.') + got_dot = 1; + else if (got_e && (p[-1] == 'e' || p[-1] == 'E') + && (*p == '-' || *p == '+')) + /* This is the sign of the exponent, not the end of the + number. */ + continue; + else if (!got_dot && !got_e && (*p=='l'||*p=='L')){ + ++p; break; + } + else if (!got_dot && !got_e && !hex && (*p=='u'||*p=='U')){ + ++p; break; + } + else if (*p < '0' || *p > '9' + && (!hex || ((*p < 'a' || *p > 'f') + && (*p < 'A' || *p > 'F')))) + break; + } + return parse_number (p - tokstart); + } + + if (!(c == '_' || c == '$' + || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) + /* We must have come across a bad character (e.g. ';'). */ + error ("Invalid character '%c' in expression.", c); + + /* It's a name. See how long it is. */ + namelen = 0; + for (c = tokstart[namelen]; + (c == '_' || c == '$' || (c >= '0' && c <= '9') + || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); + c = tokstart[++namelen]) + ; + + /* The token "if" terminates the expression and is NOT + removed from the input stream. */ + if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f') + { + return 0; + } + + lexptr += namelen; + + /* Handle the tokens $digits; also $ (short for $0) and $$ (short for $$1) + and $$digits (equivalent to $<-digits> if you could type that). + Make token type LAST, and put the number (the digits) in yylval. */ + + if (*tokstart == '$') + { + register int negate = 0; + c = 1; + /* Double dollar means negate the number and add -1 as well. + Thus $$ alone means -1. */ + if (namelen >= 2 && tokstart[1] == '$') + { + negate = 1; + c = 2; + } + if (c == namelen) + { + /* Just dollars (one or two) */ + yylval.lval = - negate; + return LAST; + } + /* Is the rest of the token digits? */ + for (; c < namelen; c++) + if (!(tokstart[c] >= '0' && tokstart[c] <= '9')) + break; + if (c == namelen) + { + yylval.lval = atoi (tokstart + 1 + negate); + if (negate) + yylval.lval = - yylval.lval; + return LAST; + } + } + + /* Handle tokens that refer to machine registers: + $ followed by a register name. */ + + if (*tokstart == '$') { + for (c = 0; c < NUM_REGS; c++) + if (namelen - 1 == strlen (reg_names[c]) + && !strncmp (tokstart + 1, reg_names[c], namelen - 1)) + { + yylval.lval = c; + return REGNAME; + } + for (c = 0; c < NUM_STD_REGS; c++) + if (namelen - 1 == strlen (std_regs[c].name) + && !strncmp (tokstart + 1, std_regs[c].name, namelen - 1)) + { + yylval.lval = std_regs[c].regnum; + return REGNAME; + } + } + /* Catch specific keywords. Should be done with a data structure. */ + switch (namelen) + { + case 8: + if (!strncmp (tokstart, "unsigned", 8)) + return UNSIGNED; + break; + case 6: + if (!strncmp (tokstart, "struct", 6)) + return STRUCT; + if (!strncmp (tokstart, "signed", 6)) + return SIGNED; + if (!strncmp (tokstart, "sizeof", 6)) + return SIZEOF; + break; + case 5: + if (!strncmp (tokstart, "union", 5)) + return UNION; + if (!strncmp (tokstart, "short", 5)) + return SHORT; + break; + case 4: + if (!strncmp (tokstart, "enum", 4)) + return ENUM; + if (!strncmp (tokstart, "long", 4)) + return LONG; + if (!strncmp (tokstart, "this", 4) + && lookup_symbol ("$this", expression_context_block, + VAR_NAMESPACE, 0)) + return THIS; + break; + case 3: + if (!strncmp (tokstart, "int", 3)) + return INT_KEYWORD; + break; + default: + break; + } + + yylval.sval.ptr = tokstart; + yylval.sval.length = namelen; + + /* Any other names starting in $ are debugger internal variables. */ + + if (*tokstart == '$') + { + yylval.ivar = (struct internalvar *) lookup_internalvar (copy_name (yylval.sval) + 1); + return VARIABLE; + } + + /* Use token-type BLOCKNAME for symbols that happen to be defined as + functions or symtabs. If this is not so, then ... + Use token-type TYPENAME for symbols that happen to be defined + currently as names of types; NAME for other symbols. + The caller is not constrained to care about the distinction. */ + { + char *tmp = copy_name (yylval.sval); + struct symbol *sym; + + if (lookup_partial_symtab (tmp)) + return BLOCKNAME; + sym = lookup_symbol (tmp, expression_context_block, + VAR_NAMESPACE, 0); + if (sym && SYMBOL_CLASS (sym) == LOC_BLOCK) + return BLOCKNAME; + if (lookup_typename (copy_name (yylval.sval), expression_context_block, 1)) + return TYPENAME; + return NAME; + } +} + +static void +yyerror () +{ + error ("Invalid syntax in expression."); +} + +/* Return a null-terminated temporary copy of the name + of a string token. */ + +static char * +copy_name (token) + struct stoken token; +{ + bcopy (token.ptr, namecopy, token.length); + namecopy[token.length] = 0; + return namecopy; +} + +/* Reverse an expression from suffix form (in which it is constructed) + to prefix form (in which we can conveniently print or execute it). */ + +static void prefixify_subexp (); + +static void +prefixify_expression (expr) + register struct expression *expr; +{ + register int len = sizeof (struct expression) + + expr->nelts * sizeof (union exp_element); + register struct expression *temp; + register int inpos = expr->nelts, outpos = 0; + + temp = (struct expression *) alloca (len); + + /* Copy the original expression into temp. */ + bcopy (expr, temp, len); + + prefixify_subexp (temp, expr, inpos, outpos); +} + +/* Return the number of exp_elements in the subexpression of EXPR + whose last exp_element is at index ENDPOS - 1 in EXPR. */ + +static int +length_of_subexp (expr, endpos) + register struct expression *expr; + register int endpos; +{ + register int oplen = 1; + register int args = 0; + register int i; + + if (endpos < 0) + error ("?error in length_of_subexp"); + + i = (int) expr->elts[endpos - 1].opcode; + + switch (i) + { + /* C++ */ + case OP_SCOPE: + oplen = 4 + ((expr->elts[endpos - 2].longconst + + sizeof (union exp_element)) + / sizeof (union exp_element)); + break; + + case OP_LONG: + case OP_DOUBLE: + oplen = 4; + break; + + case OP_VAR_VALUE: + case OP_LAST: + case OP_REGISTER: + case OP_INTERNALVAR: + oplen = 3; + break; + + case OP_FUNCALL: + oplen = 3; + args = 1 + expr->elts[endpos - 2].longconst; + break; + + case UNOP_CAST: + case UNOP_MEMVAL: + oplen = 3; + args = 1; + break; + + case STRUCTOP_STRUCT: + case STRUCTOP_PTR: + args = 1; + case OP_STRING: + oplen = 3 + ((expr->elts[endpos - 2].longconst + + sizeof (union exp_element)) + / sizeof (union exp_element)); + break; + + case TERNOP_COND: + args = 3; + break; + + case BINOP_ASSIGN_MODIFY: + oplen = 3; + args = 2; + break; + + /* C++ */ + case OP_THIS: + oplen = 2; + break; + + default: + args = 1 + (i < (int) BINOP_END); + } + + while (args > 0) + { + oplen += length_of_subexp (expr, endpos - oplen); + args--; + } + + return oplen; +} + +/* Copy the subexpression ending just before index INEND in INEXPR + into OUTEXPR, starting at index OUTBEG. + In the process, convert it from suffix to prefix form. */ + +static void +prefixify_subexp (inexpr, outexpr, inend, outbeg) + register struct expression *inexpr; + struct expression *outexpr; + register int inend; + int outbeg; +{ + register int oplen = 1; + register int args = 0; + register int i; + int *arglens; + enum exp_opcode opcode; + + /* Compute how long the last operation is (in OPLEN), + and also how many preceding subexpressions serve as + arguments for it (in ARGS). */ + + opcode = inexpr->elts[inend - 1].opcode; + switch (opcode) + { + /* C++ */ + case OP_SCOPE: + oplen = 4 + ((inexpr->elts[inend - 2].longconst + + sizeof (union exp_element)) + / sizeof (union exp_element)); + break; + + case OP_LONG: + case OP_DOUBLE: + oplen = 4; + break; + + case OP_VAR_VALUE: + case OP_LAST: + case OP_REGISTER: + case OP_INTERNALVAR: + oplen = 3; + break; + + case OP_FUNCALL: + oplen = 3; + args = 1 + inexpr->elts[inend - 2].longconst; + break; + + case UNOP_CAST: + case UNOP_MEMVAL: + oplen = 3; + args = 1; + break; + + case STRUCTOP_STRUCT: + case STRUCTOP_PTR: + args = 1; + case OP_STRING: + oplen = 3 + ((inexpr->elts[inend - 2].longconst + + sizeof (union exp_element)) + / sizeof (union exp_element)); + + break; + + case TERNOP_COND: + args = 3; + break; + + case BINOP_ASSIGN_MODIFY: + oplen = 3; + args = 2; + break; + + /* C++ */ + case OP_THIS: + oplen = 2; + break; + + default: + args = 1 + ((int) opcode < (int) BINOP_END); + } + + /* Copy the final operator itself, from the end of the input + to the beginning of the output. */ + inend -= oplen; + bcopy (&inexpr->elts[inend], &outexpr->elts[outbeg], + oplen * sizeof (union exp_element)); + outbeg += oplen; + + /* Find the lengths of the arg subexpressions. */ + arglens = (int *) alloca (args * sizeof (int)); + for (i = args - 1; i >= 0; i--) + { + oplen = length_of_subexp (inexpr, inend); + arglens[i] = oplen; + inend -= oplen; + } + + /* Now copy each subexpression, preserving the order of + the subexpressions, but prefixifying each one. + In this loop, inend starts at the beginning of + the expression this level is working on + and marches forward over the arguments. + outbeg does similarly in the output. */ + for (i = 0; i < args; i++) + { + oplen = arglens[i]; + inend += oplen; + prefixify_subexp (inexpr, outexpr, inend, outbeg); + outbeg += oplen; + } +} + +/* This page contains the two entry points to this file. */ + +/* Read a C expression from the string *STRINGPTR points to, + parse it, and return a pointer to a struct expression that we malloc. + Use block BLOCK as the lexical context for variable names; + if BLOCK is zero, use the block of the selected stack frame. + Meanwhile, advance *STRINGPTR to point after the expression, + at the first nonwhite character that is not part of the expression + (possibly a null character). + + If COMMA is nonzero, stop if a comma is reached. */ + +struct expression * +parse_c_1 (stringptr, block, comma) + char **stringptr; + struct block *block; +{ + struct cleanup *old_chain; + + lexptr = *stringptr; + + paren_depth = 0; + type_stack_depth = 0; + + comma_terminates = comma; + + if (lexptr == 0 || *lexptr == 0) + error_no_arg ("expression to compute"); + + old_chain = make_cleanup (free_funcalls, 0); + funcall_chain = 0; + + expression_context_block = block ? block : get_selected_block (); + + namecopy = (char *) alloca (strlen (lexptr) + 1); + expout_size = 10; + expout_ptr = 0; + expout = (struct expression *) + xmalloc (sizeof (struct expression) + + expout_size * sizeof (union exp_element)); + make_cleanup (free_current_contents, &expout); + if (yyparse ()) + yyerror (); + discard_cleanups (old_chain); + expout->nelts = expout_ptr; + expout = (struct expression *) + xrealloc (expout, + sizeof (struct expression) + + expout_ptr * sizeof (union exp_element)); + prefixify_expression (expout); + *stringptr = lexptr; + return expout; +} + +/* Parse STRING as an expression, and complain if this fails + to use up all of the contents of STRING. */ + +struct expression * +parse_c_expression (string) + char *string; +{ + register struct expression *exp; + exp = parse_c_1 (&string, 0, 0); + if (*string) + error ("Junk after end of expression."); + return exp; +} + +static void +push_type (tp) + enum type_pieces tp; +{ + if (type_stack_depth == type_stack_size) + { + type_stack_size *= 2; + type_stack = (enum type_pieces *) + xrealloc (type_stack, type_stack_size * sizeof (enum type_pieces)); + } + type_stack[type_stack_depth++] = tp; +} + +static enum type_pieces +pop_type () +{ + if (type_stack_depth) + return type_stack[--type_stack_depth]; + return tp_end; +} + +void +_initialize_expread () +{ + type_stack_size = 80; + type_stack_depth = 0; + type_stack = (enum type_pieces *) + xmalloc (type_stack_size * sizeof (enum type_pieces)); +} diff --git a/gnu/usr.bin/gdb/expression.h b/gnu/usr.bin/gdb/expression.h new file mode 100644 index 0000000000..5a5e20e060 --- /dev/null +++ b/gnu/usr.bin/gdb/expression.h @@ -0,0 +1,191 @@ +/* Definitions for expressions stored in reversed prefix form, for GDB. + Copyright (C) 1986, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Definitions for saved C expressions. */ + +/* An expression is represented as a vector of union exp_element's. + Each exp_element is an opcode, except that some opcodes cause + the following exp_element to be treated as a long or double constant + or as a variable. The opcodes are obeyed, using a stack for temporaries. + The value is left on the temporary stack at the end. */ + +/* When it is necessary to include a string, + it can occupy as many exp_elements as it needs. + We find the length of the string using strlen, + divide to find out how many exp_elements are used up, + and skip that many. Strings, like numbers, are indicated + by the preceding opcode. */ + +enum exp_opcode +{ +/* BINOP_... operate on two values computed by following subexpressions, + replacing them by one result value. They take no immediate arguments. */ + BINOP_ADD, /* + */ + BINOP_SUB, /* - */ + BINOP_MUL, /* * */ + BINOP_DIV, /* / */ + BINOP_REM, /* % */ + BINOP_LSH, /* << */ + BINOP_RSH, /* >> */ + BINOP_AND, /* && */ + BINOP_OR, /* || */ + BINOP_LOGAND, /* & */ + BINOP_LOGIOR, /* | */ + BINOP_LOGXOR, /* ^ */ + BINOP_EQUAL, /* == */ + BINOP_NOTEQUAL, /* != */ + BINOP_LESS, /* < */ + BINOP_GTR, /* > */ + BINOP_LEQ, /* <= */ + BINOP_GEQ, /* >= */ + BINOP_REPEAT, /* @ */ + BINOP_ASSIGN, /* = */ + BINOP_COMMA, /* , */ + BINOP_SUBSCRIPT, /* x[y] */ + BINOP_EXP, /* Exponentiation */ + +/* C++. */ + BINOP_MIN, /* ? */ + BINOP_SCOPE, /* :: */ + + /* STRUCTOP_MEMBER is used for pointer-to-member constructs. + X . * Y translates into X STRUCTOP_MEMBER Y. */ + STRUCTOP_MEMBER, + /* STRUCTOP_MPTR is used for pointer-to-member constructs + when X is a pointer instead of an aggregate. */ + STRUCTOP_MPTR, +/* end of C++. */ + + BINOP_END, + + BINOP_ASSIGN_MODIFY, /* +=, -=, *=, and so on. + The following exp_element is another opcode, + a BINOP_, saying how to modify. + Then comes another BINOP_ASSIGN_MODIFY, + making three exp_elements in total. */ + +/* Operates on three values computed by following subexpressions. */ + TERNOP_COND, /* ?: */ + +/* The OP_... series take immediate following arguments. + After the arguments come another OP_... (the same one) + so that the grouping can be recognized from the end. */ + +/* OP_LONG is followed by a type pointer in the next exp_element + and the long constant value in the following exp_element. + Then comes another OP_LONG. + Thus, the operation occupies four exp_elements. */ + + OP_LONG, +/* OP_DOUBLE is similar but takes a double constant instead of a long one. */ + OP_DOUBLE, +/* OP_VAR_VALUE takes one struct symbol * in the following exp_element, + followed by another OP_VAR_VALUE, making three exp_elements. */ + OP_VAR_VALUE, +/* OP_LAST is followed by an integer in the next exp_element. + The integer is zero for the last value printed, + or it is the absolute number of a history element. + With another OP_LAST at the end, this makes three exp_elements. */ + OP_LAST, +/* OP_REGISTER is followed by an integer in the next exp_element. + This is the number of a register to fetch (as an int). + With another OP_REGISTER at the end, this makes three exp_elements. */ + OP_REGISTER, +/* OP_INTERNALVAR is followed by an internalvar ptr in the next exp_element. + With another OP_INTERNALVAR at the end, this makes three exp_elements. */ + OP_INTERNALVAR, +/* OP_FUNCALL is followed by an integer in the next exp_element. + The integer is the number of args to the function call. + That many plus one values from following subexpressions + are used, the first one being the function. + The integer is followed by a repeat of OP_FUNCALL, + making three exp_elements. */ + OP_FUNCALL, +/* OP_STRING represents a string constant. + Its format is the same as that of a STRUCTOP, but the string + data is just made into a string constant when the operation + is executed. */ + OP_STRING, + +/* UNOP_CAST is followed by a type pointer in the next exp_element. + With another UNOP_CAST at the end, this makes three exp_elements. + It casts the value of the following subexpression. */ + UNOP_CAST, +/* UNOP_MEMVAL is followed by a type pointer in the next exp_element + With another UNOP_MEMVAL at the end, this makes three exp_elements. + It casts the contents of the word addressed by the value of the + following subexpression. */ + UNOP_MEMVAL, +/* UNOP_... operate on one value from a following subexpression + and replace it with a result. They take no immediate arguments. */ + UNOP_NEG, /* Unary - */ + UNOP_ZEROP, /* Unary ! */ + UNOP_LOGNOT, /* Unary ~ */ + UNOP_IND, /* Unary * */ + UNOP_ADDR, /* Unary & */ + UNOP_PREINCREMENT, /* ++ before an expression */ + UNOP_POSTINCREMENT, /* ++ after an expression */ + UNOP_PREDECREMENT, /* -- before an expression */ + UNOP_POSTDECREMENT, /* -- after an expression */ + UNOP_SIZEOF, /* Unary sizeof (followed by expression) */ + +/* STRUCTOP_... operate on a value from a following subexpression + by extracting a structure component specified by a string + that appears in the following exp_elements (as many as needed). + STRUCTOP_STRUCT is used for "." and STRUCTOP_PTR for "->". + They differ only in the error message given in case the value is + not suitable or the structure component specified is not found. + + The length of the string follows in the next exp_element, + (after the string), followed by another STRUCTOP_... code. */ + STRUCTOP_STRUCT, + STRUCTOP_PTR, + +/* C++ */ + /* OP_THIS is just a placeholder for the class instance variable. + It just comes in a tight (OP_THIS, OP_THIS) pair. */ + OP_THIS, + + /* OP_SCOPE surrounds a type name and a field name. The type + name is encoded as one element, but the field name stays as + a string, which, of course, is variable length. */ + OP_SCOPE, + +}; + +union exp_element +{ + enum exp_opcode opcode; + struct symbol *symbol; + LONGEST longconst; + double doubleconst; + char string; + struct type *type; + struct internalvar *internalvar; +}; + +struct expression +{ + int nelts; + union exp_element elts[1]; +}; + +struct expression *parse_c_expression (); +struct expression *parse_c_1 (); diff --git a/gnu/usr.bin/gdb/findvar.c b/gnu/usr.bin/gdb/findvar.c new file mode 100644 index 0000000000..0157d101e9 --- /dev/null +++ b/gnu/usr.bin/gdb/findvar.c @@ -0,0 +1,579 @@ +/* Find a variable's value in memory, for GDB, the GNU debugger. + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "frame.h" +#include "value.h" + +CORE_ADDR read_register (); + +/* Return the address in which frame FRAME's value of register REGNUM + has been saved in memory. Or return zero if it has not been saved. + If REGNUM specifies the SP, the value we return is actually + the SP value, not an address where it was saved. */ + +CORE_ADDR +find_saved_register (frame, regnum) + FRAME frame; + int regnum; +{ + struct frame_info *fi; + struct frame_saved_regs saved_regs; + + register FRAME frame1 = 0; + register CORE_ADDR addr = 0; + +#ifdef HAVE_REGISTER_WINDOWS + /* We assume that a register in a register window will only be saved + in one place (since the name changes and disappears as you go + towards inner frames), so we only call get_frame_saved_regs on + the current frame. This is directly in contradiction to the + usage below, which assumes that registers used in a frame must be + saved in a lower (more interior) frame. This change is a result + of working on a register window machine; get_frame_saved_regs + always returns the registers saved within a frame, within the + context (register namespace) of that frame. */ + + /* However, note that we don't want this to return anything if + nothing is saved (if there's a frame inside of this one). Also, + callers to this routine asking for the stack pointer want the + stack pointer saved for *this* frame; this is returned from the + next frame. */ + + + if (REGISTER_IN_WINDOW_P(regnum)) + { + frame1 = get_next_frame (frame); + if (!frame1) return 0; /* Registers of this frame are + active. */ + + /* Get the SP from the next frame in; it will be this + current frame. */ + if (regnum != SP_REGNUM) + frame1 = frame; + + fi = get_frame_info (frame1); + get_frame_saved_regs (fi, &saved_regs); + return (saved_regs.regs[regnum] ? + saved_regs.regs[regnum] : 0); + } +#endif /* HAVE_REGISTER_WINDOWS */ + + /* Note that this next routine assumes that registers used in + frame x will be saved only in the frame that x calls and + frames interior to it. This is not true on the sparc, but the + above macro takes care of it, so we should be all right. */ + while (1) + { + QUIT; + frame1 = get_prev_frame (frame1); + if (frame1 == 0 || frame1 == frame) + break; + fi = get_frame_info (frame1); + get_frame_saved_regs (fi, &saved_regs); + if (saved_regs.regs[regnum]) + addr = saved_regs.regs[regnum]; + } + + return addr; +} + +/* Copy the bytes of register REGNUM, relative to the current stack frame, + into our memory at MYADDR. + The number of bytes copied is REGISTER_RAW_SIZE (REGNUM). */ + +void +read_relative_register_raw_bytes (regnum, myaddr) + int regnum; + char *myaddr; +{ + register CORE_ADDR addr; + + if (regnum == FP_REGNUM) + { + bcopy (&FRAME_FP(selected_frame), myaddr, sizeof (CORE_ADDR)); + return; + } + + addr = find_saved_register (selected_frame, regnum); + + if (addr) + { + if (regnum == SP_REGNUM) + { + CORE_ADDR buffer = addr; + bcopy (&buffer, myaddr, sizeof (CORE_ADDR)); + } + else + read_memory (addr, myaddr, REGISTER_RAW_SIZE (regnum)); + return; + } + read_register_bytes (REGISTER_BYTE (regnum), + myaddr, REGISTER_RAW_SIZE (regnum)); +} + +/* Return a `value' with the contents of register REGNUM + in its virtual format, with the type specified by + REGISTER_VIRTUAL_TYPE. */ + +value +value_of_register (regnum) + int regnum; +{ + register CORE_ADDR addr; + register value val; + char raw_buffer[MAX_REGISTER_RAW_SIZE]; + char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE]; + + if (! (have_inferior_p () || have_core_file_p ())) + error ("Can't get value of register without inferior or core file"); + + addr = find_saved_register (selected_frame, regnum); + if (addr) + { + if (regnum == SP_REGNUM) + return value_from_long (builtin_type_int, (LONGEST) addr); + read_memory (addr, raw_buffer, REGISTER_RAW_SIZE (regnum)); + } + else + read_register_bytes (REGISTER_BYTE (regnum), raw_buffer, + REGISTER_RAW_SIZE (regnum)); + + REGISTER_CONVERT_TO_VIRTUAL (regnum, raw_buffer, virtual_buffer); + val = allocate_value (REGISTER_VIRTUAL_TYPE (regnum)); + bcopy (virtual_buffer, VALUE_CONTENTS (val), REGISTER_VIRTUAL_SIZE (regnum)); + VALUE_LVAL (val) = addr ? lval_memory : lval_register; + VALUE_ADDRESS (val) = addr ? addr : REGISTER_BYTE (regnum); + VALUE_REGNO (val) = regnum; + return val; +} + +/* Low level examining and depositing of registers. + + Note that you must call `fetch_registers' once + before examining or depositing any registers. */ + +char registers[REGISTER_BYTES]; + +/* Copy LEN bytes of consecutive data from registers + starting with the REGBYTE'th byte of register data + into memory at MYADDR. */ + +void +read_register_bytes (regbyte, myaddr, len) + int regbyte; + char *myaddr; + int len; +{ + bcopy (®isters[regbyte], myaddr, len); +} + +/* Copy LEN bytes of consecutive data from memory at MYADDR + into registers starting with the REGBYTE'th byte of register data. */ + +void +write_register_bytes (regbyte, myaddr, len) + int regbyte; + char *myaddr; + int len; +{ + bcopy (myaddr, ®isters[regbyte], len); + if (have_inferior_p ()) + store_inferior_registers (-1); +} + +/* Return the contents of register REGNO, + regarding it as an integer. */ + +CORE_ADDR +read_register (regno) + int regno; +{ + /* This loses when REGISTER_RAW_SIZE (regno) != sizeof (int) */ + return *(int *) ®isters[REGISTER_BYTE (regno)]; +} + +/* Store VALUE in the register number REGNO, regarded as an integer. */ + +void +write_register (regno, val) + int regno, val; +{ + /* This loses when REGISTER_RAW_SIZE (regno) != sizeof (int) */ +#if defined(sun4) + /* This is a no-op on a Sun 4. */ + if (regno == 0) + return; +#endif + + *(int *) ®isters[REGISTER_BYTE (regno)] = val; + + if (have_inferior_p ()) + store_inferior_registers (regno); +} + +/* Record that register REGNO contains VAL. + This is used when the value is obtained from the inferior or core dump, + so there is no need to store the value there. */ + +void +supply_register (regno, val) + int regno; + char *val; +{ + bcopy (val, ®isters[REGISTER_BYTE (regno)], REGISTER_RAW_SIZE (regno)); +} + +/* Given a struct symbol for a variable, + and a stack frame id, read the value of the variable + and return a (pointer to a) struct value containing the value. */ + +value +read_var_value (var, frame) + register struct symbol *var; + FRAME frame; +{ + register value v; + + struct frame_info *fi; + + struct type *type = SYMBOL_TYPE (var); + register CORE_ADDR addr = 0; + int val = SYMBOL_VALUE (var); + register int len; + + v = allocate_value (type); + VALUE_LVAL (v) = lval_memory; /* The most likely possibility. */ + len = TYPE_LENGTH (type); + + if (frame == 0) frame = selected_frame; + + switch (SYMBOL_CLASS (var)) + { + case LOC_CONST: + case LOC_LABEL: + bcopy (&val, VALUE_CONTENTS (v), len); + VALUE_LVAL (v) = not_lval; + return v; + + case LOC_CONST_BYTES: + bcopy (val, VALUE_CONTENTS (v), len); + VALUE_LVAL (v) = not_lval; + return v; + + case LOC_STATIC: + addr = val; + break; + +/* Nonzero if a struct which is located in a register or a LOC_ARG + really contains + the address of the struct, not the struct itself. GCC_P is nonzero + if the function was compiled with GCC. */ +#if !defined (REG_STRUCT_HAS_ADDR) +#define REG_STRUCT_HAS_ADDR(gcc_p) 0 +#endif + + case LOC_ARG: + fi = get_frame_info (frame); + addr = val + FRAME_ARGS_ADDRESS (fi); + break; + + case LOC_REF_ARG: + fi = get_frame_info (frame); + addr = val + FRAME_ARGS_ADDRESS (fi); + addr = read_memory_integer (addr, sizeof (CORE_ADDR)); + break; + + case LOC_LOCAL: + fi = get_frame_info (frame); + addr = val + FRAME_LOCALS_ADDRESS (fi); + break; + + case LOC_TYPEDEF: + error ("Cannot look up value of a typedef"); + + case LOC_BLOCK: + VALUE_ADDRESS (v) = BLOCK_START (SYMBOL_BLOCK_VALUE (var)); + return v; + + case LOC_REGISTER: + case LOC_REGPARM: + { + struct block *b = get_frame_block (frame); + + v = value_from_register (type, val, frame); + + if (REG_STRUCT_HAS_ADDR(b->gcc_compile_flag) + && TYPE_CODE (type) == TYPE_CODE_STRUCT) + addr = *(CORE_ADDR *)VALUE_CONTENTS (v); + else + return v; + } + } + + read_memory (addr, VALUE_CONTENTS (v), len); + VALUE_ADDRESS (v) = addr; + return v; +} + +/* Return a value of type TYPE, stored in register REGNUM, in frame + FRAME. */ + +value +value_from_register (type, regnum, frame) + struct type *type; + int regnum; + FRAME frame; +{ + char raw_buffer [MAX_REGISTER_RAW_SIZE]; + char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE]; + CORE_ADDR addr; + value v = allocate_value (type); + int len = TYPE_LENGTH (type); + char *value_bytes = 0; + int value_bytes_copied = 0; + int num_storage_locs; + + VALUE_REGNO (v) = regnum; + + num_storage_locs = (len > REGISTER_VIRTUAL_SIZE (regnum) ? + ((len - 1) / REGISTER_RAW_SIZE (regnum)) + 1 : + 1); + + if (num_storage_locs > 1) + { + /* Value spread across multiple storage locations. */ + + int local_regnum; + int mem_stor = 0, reg_stor = 0; + int mem_tracking = 1; + CORE_ADDR last_addr = 0; + + value_bytes = (char *) alloca (len + MAX_REGISTER_RAW_SIZE); + + /* Copy all of the data out, whereever it may be. */ + + for (local_regnum = regnum; + value_bytes_copied < len; + (value_bytes_copied += REGISTER_RAW_SIZE (local_regnum), + ++local_regnum)) + { + int register_index = local_regnum - regnum; + addr = find_saved_register (frame, local_regnum); + if (addr == 0) + { + read_register_bytes (REGISTER_BYTE (local_regnum), + value_bytes + value_bytes_copied, + REGISTER_RAW_SIZE (local_regnum)); + reg_stor++; + } + else + { + read_memory (addr, value_bytes + value_bytes_copied, + REGISTER_RAW_SIZE (local_regnum)); + mem_stor++; + mem_tracking = + (mem_tracking + && (regnum == local_regnum + || addr == last_addr)); + } + last_addr = addr; + } + + if ((reg_stor && mem_stor) + || (mem_stor && !mem_tracking)) + /* Mixed storage; all of the hassle we just went through was + for some good purpose. */ + { + VALUE_LVAL (v) = lval_reg_frame_relative; + VALUE_FRAME (v) = FRAME_FP (frame); + VALUE_FRAME_REGNUM (v) = regnum; + } + else if (mem_stor) + { + VALUE_LVAL (v) = lval_memory; + VALUE_ADDRESS (v) = find_saved_register (frame, regnum); + } + else if (reg_stor) + { + VALUE_LVAL (v) = lval_register; + VALUE_ADDRESS (v) = REGISTER_BYTE (regnum); + } + else + fatal ("value_from_register: Value not stored anywhere!"); + + /* Any structure stored in more than one register will always be + an inegral number of registers. Otherwise, you'd need to do + some fiddling with the last register copied here for little + endian machines. */ + + /* Copy into the contents section of the value. */ + bcopy (value_bytes, VALUE_CONTENTS (v), len); + + return v; + } + + /* Data is completely contained within a single register. Locate the + register's contents in a real register or in core; + read the data in raw format. */ + + addr = find_saved_register (frame, regnum); + if (addr == 0) + { + /* Value is really in a register. */ + + VALUE_LVAL (v) = lval_register; + VALUE_ADDRESS (v) = REGISTER_BYTE (regnum); + + read_register_bytes (REGISTER_BYTE (regnum), + raw_buffer, REGISTER_RAW_SIZE (regnum)); + } + else + { + /* Value was in a register that has been saved in memory. */ + + read_memory (addr, raw_buffer, REGISTER_RAW_SIZE (regnum)); + VALUE_LVAL (v) = lval_memory; + VALUE_ADDRESS (v) = addr; + } + + /* Convert the raw contents to virtual contents. + (Just copy them if the formats are the same.) */ + + REGISTER_CONVERT_TO_VIRTUAL (regnum, raw_buffer, virtual_buffer); + + if (REGISTER_CONVERTIBLE (regnum)) + { + /* When the raw and virtual formats differ, the virtual format + corresponds to a specific data type. If we want that type, + copy the data into the value. + Otherwise, do a type-conversion. */ + + if (type != REGISTER_VIRTUAL_TYPE (regnum)) + { + /* eg a variable of type `float' in a 68881 register + with raw type `extended' and virtual type `double'. + Fetch it as a `double' and then convert to `float'. */ + v = allocate_value (REGISTER_VIRTUAL_TYPE (regnum)); + bcopy (virtual_buffer, VALUE_CONTENTS (v), len); + v = value_cast (type, v); + } + else + bcopy (virtual_buffer, VALUE_CONTENTS (v), len); + } + else + { + /* Raw and virtual formats are the same for this register. */ + +#ifdef BYTES_BIG_ENDIAN + if (len < REGISTER_RAW_SIZE (regnum)) + { + /* Big-endian, and we want less than full size. */ + VALUE_OFFSET (v) = REGISTER_RAW_SIZE (regnum) - len; + } +#endif + + bcopy (virtual_buffer + VALUE_OFFSET (v), + VALUE_CONTENTS (v), len); + } + + return v; +} + +/* Given a struct symbol for a variable, + and a stack frame id, + return a (pointer to a) struct value containing the variable's address. */ + +value +locate_var_value (var, frame) + register struct symbol *var; + FRAME frame; +{ + register CORE_ADDR addr = 0; + int val = SYMBOL_VALUE (var); + struct frame_info *fi; + struct type *type = SYMBOL_TYPE (var); + struct type *result_type; + + if (frame == 0) frame = selected_frame; + + switch (SYMBOL_CLASS (var)) + { + case LOC_CONST: + case LOC_CONST_BYTES: + error ("Address requested for identifier \"%s\" which is a constant.", + SYMBOL_NAME (var)); + + case LOC_REGISTER: + case LOC_REGPARM: + addr = find_saved_register (frame, val); + if (addr != 0) + { + int len = TYPE_LENGTH (type); +#ifdef BYTES_BIG_ENDIAN + if (len < REGISTER_RAW_SIZE (val)) + /* Big-endian, and we want less than full size. */ + addr += REGISTER_RAW_SIZE (val) - len; +#endif + break; + } + error ("Address requested for identifier \"%s\" which is in a register.", + SYMBOL_NAME (var)); + + case LOC_STATIC: + case LOC_LABEL: + addr = val; + break; + + case LOC_ARG: + fi = get_frame_info (frame); + addr = val + FRAME_ARGS_ADDRESS (fi); + break; + + case LOC_REF_ARG: + fi = get_frame_info (frame); + addr = val + FRAME_ARGS_ADDRESS (fi); + addr = read_memory_integer (addr, sizeof (CORE_ADDR)); + break; + + case LOC_LOCAL: + fi = get_frame_info (frame); + addr = val + FRAME_LOCALS_ADDRESS (fi); + break; + + case LOC_TYPEDEF: + error ("Address requested for identifier \"%s\" which is a typedef.", + SYMBOL_NAME (var)); + + case LOC_BLOCK: + addr = BLOCK_START (SYMBOL_BLOCK_VALUE (var)); + break; + } + + /* Address of an array is of the type of address of it's elements. */ + result_type = + lookup_pointer_type (TYPE_CODE (type) == TYPE_CODE_ARRAY ? + TYPE_TARGET_TYPE (type) : type); + + return value_cast (result_type, + value_from_long (builtin_type_long, (LONGEST) addr)); +} + diff --git a/gnu/usr.bin/gdb/frame.h b/gnu/usr.bin/gdb/frame.h new file mode 100644 index 0000000000..322ddbae04 --- /dev/null +++ b/gnu/usr.bin/gdb/frame.h @@ -0,0 +1,115 @@ +/* Definitions for dealing with stack frames, for GDB, the GNU debugger. + Copyright (C) 1986, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Note that frame.h requires param.h! */ + +/* + * FRAME is the type of the identifier of a specific stack frame. It + * is a pointer to the frame cache item corresponding to this frame. + * Please note that frame id's are *not* constant over calls to the + * inferior. Use frame addresses, which are. + * + * FRAME_ADDR is the type of the address of a specific frame. I + * cannot imagine a case in which this would not be CORE_ADDR, so + * maybe it's silly to give it it's own type. Life's rough. + * + * FRAME_FP is a macro which converts from a frame identifier into a + * frame_address. + * + * FRAME_INFO_ID is a macro which "converts" from a frame info pointer + * to a frame id. This is here in case I or someone else decides to + * change the FRAME type again. + * + * This file and blockframe.c are the only places which are allowed to + * use the equivalence between FRAME and struct frame_info *. EXCEPTION: + * value.h uses CORE_ADDR instead of FRAME_ADDR because the compiler + * will accept that in the absense of this file. + */ +typedef struct frame_info *FRAME; +typedef CORE_ADDR FRAME_ADDR; +#define FRAME_FP(fr) ((fr)->frame) +#define FRAME_INFO_ID(f) (f) + +/* + * Caching structure for stack frames. This is also the structure + * used for extended info about stack frames. May add more to this + * structure as it becomes necessary. + * + * Note that the first entry in the cache will always refer to the + * innermost executing frame. This value should be set (is it? + * Check) in something like normal_stop. + */ +struct frame_info + { + /* Nominal address of the frame described. */ + FRAME_ADDR frame; + /* Address at which execution is occurring in this frame. + For the innermost frame, it's the current pc. + For other frames, it is a pc saved in the next frame. */ + CORE_ADDR pc; + /* The frame called by the frame we are describing, or 0. + This may be set even if there isn't a frame called by the one + we are describing (.->next == 0); in that case it is simply the + bottom of this frame */ + FRAME_ADDR next_frame; + /* Anything extra for this structure that may have been defined + in the machine depedent files. */ +#ifdef EXTRA_FRAME_INFO + EXTRA_FRAME_INFO +#endif + /* Pointers to the next and previous frame_info's in this stack. */ + FRAME next, prev; + }; + +/* Describe the saved registers of a frame. */ + +struct frame_saved_regs + { + /* For each register, address of where it was saved on entry to the frame, + or zero if it was not saved on entry to this frame. */ + CORE_ADDR regs[NUM_REGS]; + }; + +/* The stack frame that the user has specified for commands to act on. + Note that one cannot assume this is the address of valid data. */ + +extern FRAME selected_frame; + +extern struct frame_info *get_frame_info (); +extern struct frame_info *get_prev_frame_info (); + +extern FRAME create_new_frame (); + +extern void get_frame_saved_regs (); + +extern FRAME get_prev_frame (); +extern FRAME get_current_frame (); +extern FRAME get_next_frame (); + +extern struct block *get_frame_block (); +extern struct block *get_current_block (); +extern struct block *get_selected_block (); +extern struct symbol *get_frame_function (); +extern struct symbol *get_pc_function (); + +/* In stack.c */ +extern FRAME find_relative_frame (); + +/* Generic pointer value indicating "I don't know." */ +#define Frame_unknown (CORE_ADDR)-1 diff --git a/gnu/usr.bin/gdb/gdb.1 b/gnu/usr.bin/gdb/gdb.1 new file mode 100644 index 0000000000..57d744b293 --- /dev/null +++ b/gnu/usr.bin/gdb/gdb.1 @@ -0,0 +1,3 @@ +.\" %W% (Berkeley) %G% +.\" +.\" placeholder, until we can produce the manual page diff --git a/gnu/usr.bin/gdb/getpagesize.h b/gnu/usr.bin/gdb/getpagesize.h new file mode 100644 index 0000000000..32adae61ef --- /dev/null +++ b/gnu/usr.bin/gdb/getpagesize.h @@ -0,0 +1,25 @@ +#ifdef BSD +#ifndef BSD4_1 +#define HAVE_GETPAGESIZE +#endif +#endif + +#ifndef HAVE_GETPAGESIZE + +#include + +#ifdef EXEC_PAGESIZE +#define getpagesize() EXEC_PAGESIZE +#else +#ifdef NBPG +#define getpagesize() NBPG * CLSIZE +#ifndef CLSIZE +#define CLSIZE 1 +#endif /* no CLSIZE */ +#else /* no NBPG */ +#define getpagesize() NBPC +#endif /* no NBPG */ +#endif /* no EXEC_PAGESIZE */ + +#endif /* not HAVE_GETPAGESIZE */ + diff --git a/gnu/usr.bin/gdb/infcmd.c b/gnu/usr.bin/gdb/infcmd.c new file mode 100644 index 0000000000..378784f22b --- /dev/null +++ b/gnu/usr.bin/gdb/infcmd.c @@ -0,0 +1,1204 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)infcmd.c 6.4 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Memory-access and commands for inferior process, for GDB. + Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "frame.h" +#include "inferior.h" +#include "environ.h" +#include "value.h" + +#include +#include + +extern char *sys_siglist[]; + +#define ERROR_NO_INFERIOR \ + if (inferior_pid == 0) error ("The program is not being run."); + +/* String containing arguments to give to the program, + with a space added at the front. Just a space means no args. */ + +static char *inferior_args; + +/* File name for default use for standard in/out in the inferior. */ + +char *inferior_io_terminal; + +/* Pid of our debugged inferior, or 0 if no inferior now. */ + +int inferior_pid; + +/* Last signal that the inferior received (why it stopped). */ + +int stop_signal; + +/* Address at which inferior stopped. */ + +CORE_ADDR stop_pc; + +/* Stack frame when program stopped. */ + +FRAME_ADDR stop_frame_address; + +/* Number of breakpoint it stopped at, or 0 if none. */ + +int stop_breakpoint; + +/* Nonzero if stopped due to a step command. */ + +int stop_step; + +/* Nonzero if stopped due to completion of a stack dummy routine. */ + +int stop_stack_dummy; + +/* Nonzero if stopped due to a random (unexpected) signal in inferior + process. */ + +int stopped_by_random_signal; + +/* Range to single step within. + If this is nonzero, respond to a single-step signal + by continuing to step if the pc is in this range. */ + +CORE_ADDR step_range_start; /* Inclusive */ +CORE_ADDR step_range_end; /* Exclusive */ + +/* Stack frame address as of when stepping command was issued. + This is how we know when we step into a subroutine call, + and how to set the frame for the breakpoint used to step out. */ + +FRAME_ADDR step_frame_address; + +/* 1 means step over all subroutine calls. + -1 means step over calls to undebuggable functions. */ + +int step_over_calls; + +/* If stepping, nonzero means step count is > 1 + so don't print frame next time inferior stops + if it stops due to stepping. */ + +int step_multi; + +/* Environment to use for running inferior, + in format described in environ.h. */ + +struct environ *inferior_environ; + +CORE_ADDR read_pc (); +struct command_line *get_breakpoint_commands (); +void breakpoint_clear_ignore_counts (); + + +int +have_inferior_p () +{ + return inferior_pid != 0; +} + +static void +set_args_command (args) + char *args; +{ + free (inferior_args); + if (!args) args = ""; + inferior_args = concat (" ", args, ""); +} + +void +tty_command (file, from_tty) + char *file; + int from_tty; +{ + if (file == 0) + error_no_arg ("terminal name for running target process"); + + inferior_io_terminal = savestring (file, strlen (file)); +} + +static void +run_command (args, from_tty) + char *args; + int from_tty; +{ + extern char **environ; + register int i; + char *exec_file; + char *allargs; + + extern int sys_nerr; + extern char *sys_errlist[]; + extern int errno; + + dont_repeat (); + + if (inferior_pid) + { + extern int inhibit_confirm; + if (!(inhibit_confirm || + query ("The program being debugged has been started already.\n\ +Start it from the beginning? "))) + error ("Program not restarted."); + kill_inferior (); + } + +#if 0 + /* On the other hand, some users want to do + break open + ignore 1 40 + run + So it's not clear what is best. */ + + /* It is confusing to the user for ignore counts to stick around + from previous runs of the inferior. So clear them. */ + breakpoint_clear_ignore_counts (); +#endif + + exec_file = (char *) get_exec_file (1); + + if (remote_debugging) + { + if (from_tty) + { + printf ("Starting program: %s\n", exec_file); + fflush (stdout); + } + } + else + { + if (args) + set_args_command (args); + + if (from_tty) + { + printf ("Starting program: %s%s\n", + exec_file, inferior_args); + fflush (stdout); + } + + allargs = concat ("exec ", exec_file, inferior_args); + inferior_pid = create_inferior (allargs, environ_vector (inferior_environ)); + } + + clear_proceed_status (); + + start_inferior (); +} + +void +cont_command (proc_count_exp, from_tty) + char *proc_count_exp; + int from_tty; +{ + ERROR_NO_INFERIOR; + + clear_proceed_status (); + + /* If have argument, set proceed count of breakpoint we stopped at. */ + + if (stop_breakpoint > 0 && proc_count_exp) + { + set_ignore_count (stop_breakpoint, + parse_and_eval_address (proc_count_exp) - 1, + from_tty); + if (from_tty) + printf (" "); + } + + if (from_tty) + printf ("Continuing.\n"); + + proceed (-1, -1, 0); +} + +/* Step until outside of current statement. */ +static void step_1 (); + +static void +step_command (count_string) +{ + step_1 (0, 0, count_string); +} + +/* Likewise, but skip over subroutine calls as if single instructions. */ + +static void +next_command (count_string) +{ + step_1 (1, 0, count_string); +} + +/* Likewise, but step only one instruction. */ + +static void +stepi_command (count_string) +{ + step_1 (0, 1, count_string); +} + +static void +nexti_command (count_string) +{ + step_1 (1, 1, count_string); +} + +static void +step_1 (skip_subroutines, single_inst, count_string) + int skip_subroutines; + int single_inst; + char *count_string; +{ + register int count = 1; + + ERROR_NO_INFERIOR; + count = count_string ? parse_and_eval_address (count_string) : 1; + + for (; count > 0; count--) + { + clear_proceed_status (); + + step_frame_address = FRAME_FP (get_current_frame ()); + + if (! single_inst) + { + find_pc_line_pc_range (stop_pc, &step_range_start, &step_range_end); + if (step_range_end == 0) + { + int misc; + + misc = find_pc_misc_function (stop_pc); + terminal_ours (); + printf ("Current function has no line number information.\n"); + fflush (stdout); + + /* No info or after _etext ("Can't happen") */ + if (misc == -1 || misc == misc_function_count - 1) + error ("No data available on pc function."); + + printf ("Single stepping until function exit.\n"); + fflush (stdout); + + step_range_start = misc_function_vector[misc].address; + step_range_end = misc_function_vector[misc + 1].address; + } + } + else + { + /* Say we are stepping, but stop after one insn whatever it does. + Don't step through subroutine calls even to undebuggable + functions. */ + step_range_start = step_range_end = 1; + if (!skip_subroutines) + step_over_calls = 0; + } + + if (skip_subroutines) + step_over_calls = 1; + + step_multi = (count > 1); + proceed (-1, -1, 1); + if (! stop_step) + break; + } +} + +/* Continue program at specified address. */ + +static void +jump_command (arg, from_tty) + char *arg; + int from_tty; +{ + register CORE_ADDR addr; + struct symtabs_and_lines sals; + struct symtab_and_line sal; + + ERROR_NO_INFERIOR; + + if (!arg) + error_no_arg ("starting address"); + + sals = decode_line_spec_1 (arg, 1); + if (sals.nelts != 1) + { + error ("Unreasonable jump request"); + } + + sal = sals.sals[0]; + free (sals.sals); + + if (sal.symtab == 0 && sal.pc == 0) + error ("No source file has been specified."); + + if (sal.pc == 0) + sal.pc = find_line_pc (sal.symtab, sal.line); + + { + struct symbol *fn = get_frame_function (get_current_frame ()); + struct symbol *sfn = find_pc_function (sal.pc); + if (fn != 0 && sfn != fn + && ! query ("Line %d is not in `%s'. Jump anyway? ", + sal.line, SYMBOL_NAME (fn))) + error ("Not confirmed."); + } + + if (sal.pc == 0) + error ("No line %d in file \"%s\".", sal.line, sal.symtab->filename); + + addr = sal.pc; + + clear_proceed_status (); + + if (from_tty) + printf ("Continuing at 0x%x.\n", addr); + + proceed (addr, 0, 0); +} + +/* Continue program giving it specified signal. */ + +static void +signal_command (signum_exp, from_tty) + char *signum_exp; + int from_tty; +{ + register int signum; + + dont_repeat (); /* Too dangerous. */ + ERROR_NO_INFERIOR; + + if (!signum_exp) + error_no_arg ("signal number"); + + signum = parse_and_eval_address (signum_exp); + + clear_proceed_status (); + + if (from_tty) + printf ("Continuing with signal %d.\n", signum); + + proceed (stop_pc, signum, 0); +} + +/* Execute a "stack dummy", a piece of code stored in the stack + by the debugger to be executed in the inferior. + + To call: first, do PUSH_DUMMY_FRAME. + Then push the contents of the dummy. It should end with a breakpoint insn. + Then call here, passing address at which to start the dummy. + + The contents of all registers are saved before the dummy frame is popped + and copied into the buffer BUFFER. + + The dummy's frame is automatically popped whenever that break is hit. + If that is the first time the program stops, run_stack_dummy + returns to its caller with that frame already gone. + Otherwise, the caller never gets returned to. */ + +/* 4 => return instead of letting the stack dummy run. */ + +static int stack_dummy_testing = 0; + +void +run_stack_dummy (addr, buffer) + CORE_ADDR addr; + REGISTER_TYPE *buffer; +{ + /* Now proceed, having reached the desired place. */ + clear_proceed_status (); +#ifdef notdef + if (stack_dummy_testing & 4) + { + POP_FRAME; + return; + } +#endif + proceed (addr, 0, 0); + + if (!stop_stack_dummy) + error ("Cannot continue previously requested operation."); + + /* On return, the stack dummy has been popped already. */ + + read_register_bytes(0, buffer, REGISTER_BYTES); +} + +/* Proceed until we reach the given line as argument or exit the + function. When called with no argument, proceed until we reach a + different source line with pc greater than our current one or exit + the function. We skip calls in both cases. + + The effect of this command with an argument is identical to setting + a momentary breakpoint at the line specified and executing + "finish". + + Note that eventually this command should probably be changed so + that only source lines are printed out when we hit the breakpoint + we set. I'm going to postpone this until after a hopeful rewrite + of wait_for_inferior and the proceed status code. -- randy */ + +void +until_next_command (arg, from_tty) + char *arg; + int from_tty; +{ + FRAME frame; + CORE_ADDR pc; + struct symbol *func; + struct symtab_and_line sal; + + clear_proceed_status (); + + frame = get_current_frame (); + + /* Step until either exited from this function or greater + than the current line (if in symbolic section) or pc (if + not). */ + + pc = read_pc (); + func = find_pc_function (pc); + + if (!func) + { + int misc_func = find_pc_misc_function (pc); + + if (misc_func != -1) + error ("Execution is not within a known function."); + + step_range_start = misc_function_vector[misc_func].address; + step_range_end = pc; + } + else + { + sal = find_pc_line (pc, 0); + + step_range_start = BLOCK_START (SYMBOL_BLOCK_VALUE (func)); + step_range_end = sal.end; + } + + step_over_calls = 1; + step_frame_address = FRAME_FP (frame); + + step_multi = 0; /* Only one call to proceed */ + + proceed (-1, -1, 1); +} + +void +until_command (arg, from_tty) + char *arg; + int from_tty; +{ + if (!have_inferior_p ()) + error ("The program is not being run."); + + if (arg) + until_break_command (arg, from_tty); + else + until_next_command (arg, from_tty); +} + +/* "finish": Set a temporary breakpoint at the place + the selected frame will return to, then continue. */ + +static void +finish_command (arg, from_tty) + char *arg; + int from_tty; +{ + struct symtab_and_line sal; + register FRAME frame; + struct frame_info *fi; + register struct symbol *function; + + if (!have_inferior_p ()) + error ("The program is not being run."); + if (arg) + error ("The \"finish\" command does not take any arguments."); + + frame = get_prev_frame (selected_frame); + if (frame == 0) + error ("\"finish\" not meaningful in the outermost frame."); + + clear_proceed_status (); + + fi = get_frame_info (frame); + sal = find_pc_line (fi->pc, 0); + sal.pc = fi->pc; + set_momentary_breakpoint (sal, frame); + + /* Find the function we will return from. */ + + fi = get_frame_info (selected_frame); + function = find_pc_function (fi->pc); + + if (from_tty) + { + printf ("Run till exit from "); + print_selected_frame (); + } + + proceed (-1, -1, 0); + + if (stop_breakpoint == -3 && function != 0) + { + struct type *value_type; + register value val; + CORE_ADDR funcaddr; + extern char registers[]; + + value_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (function)); + if (!value_type) + fatal ("internal: finish_command: function has no target type"); + + if (TYPE_CODE (value_type) == TYPE_CODE_VOID) + return; + + funcaddr = BLOCK_START (SYMBOL_BLOCK_VALUE (function)); + + val = value_being_returned (value_type, registers, + using_struct_return (function, + funcaddr, + value_type)); + + printf ("Value returned is $%d = ", record_latest_value (val)); + value_print (val, stdout, 0, Val_no_prettyprint); + putchar ('\n'); + } +} + +static void +program_info () +{ + if (inferior_pid == 0) + { + printf ("The program being debugged is not being run.\n"); + return; + } + + printf ("Program being debugged is in process %d, stopped at 0x%x.\n", + inferior_pid, stop_pc); + if (stop_step) + printf ("It stopped after being stepped.\n"); + else if (stop_breakpoint > 0) + printf ("It stopped at breakpoint %d.\n", stop_breakpoint); + else if (stop_signal) + printf ("It stopped with signal %d (%s).\n", + stop_signal, sys_siglist[stop_signal]); + + printf ("\nType \"info stack\" or \"info reg\" for more information.\n"); +} + +static void +environment_info (var) + char *var; +{ + if (var) + { + register char *val = get_in_environ (inferior_environ, var); + if (val) + printf ("%s = %s\n", var, val); + else + printf ("Environment variable \"%s\" not defined.\n", var); + } + else + { + register char **vector = environ_vector (inferior_environ); + while (*vector) + printf ("%s\n", *vector++); + } +} + +static void +set_environment_command (arg) + char *arg; +{ + register char *p, *val, *var; + int nullset = 0; + + if (arg == 0) + error_no_arg ("environment variable and value"); + + /* Find seperation between variable name and value */ + p = (char *) index (arg, '='); + val = (char *) index (arg, ' '); + + if (p != 0 && val != 0) + { + /* We have both a space and an equals. If the space is before the + equals and the only thing between the two is more space, use + the equals */ + if (p > val) + while (*val == ' ') + val++; + + /* Take the smaller of the two. If there was space before the + "=", they will be the same right now. */ + p = arg + min (p - arg, val - arg); + } + else if (val != 0 && p == 0) + p = val; + + if (p == arg) + error_no_arg ("environment variable to set"); + + if (p == 0 || p[1] == 0) + { + nullset = 1; + if (p == 0) + p = arg + strlen (arg); /* So that savestring below will work */ + } + else + { + /* Not setting variable value to null */ + val = p + 1; + while (*val == ' ' || *val == '\t') + val++; + } + + while (p != arg && (p[-1] == ' ' || p[-1] == '\t')) p--; + + var = savestring (arg, p - arg); + if (nullset) + { + printf ("Setting environment variable \"%s\" to null value.\n", var); + set_in_environ (inferior_environ, var, ""); + } + else + set_in_environ (inferior_environ, var, val); + free (var); +} + +static void +unset_environment_command (var, from_tty) + char *var; + int from_tty; +{ + if (var == 0) + /* If there is no argument, delete all environment variables. + Ask for confirmation if reading from the terminal. */ + if (!from_tty || query ("Delete all environment variables? ")) + { + free_environ (inferior_environ); + inferior_environ = make_environ (); + } + + unset_in_environ (inferior_environ, var); +} + +/* Read an integer from debugged memory, given address and number of bytes. */ + +long +read_memory_integer (memaddr, len) + CORE_ADDR memaddr; + int len; +{ + char cbuf; + short sbuf; + int ibuf; + long lbuf; + int result_err; + extern int sys_nerr; + extern char *sys_errlist[]; + + if (len == sizeof (char)) + { + result_err = read_memory (memaddr, &cbuf, len); + if (result_err) + error ("Error reading memory address 0x%x: %s (%d).", + memaddr, (result_err < sys_nerr ? + sys_errlist[result_err] : + "uknown error"), result_err); + return cbuf; + } + if (len == sizeof (short)) + { + result_err = read_memory (memaddr, &sbuf, len); + if (result_err) + error ("Error reading memory address 0x%x: %s (%d).", + memaddr, (result_err < sys_nerr ? + sys_errlist[result_err] : + "uknown error"), result_err); + return sbuf; + } + if (len == sizeof (int)) + { + result_err = read_memory (memaddr, &ibuf, len); + if (result_err) + error ("Error reading memory address 0x%x: %s (%d).", + memaddr, (result_err < sys_nerr ? + sys_errlist[result_err] : + "uknown error"), result_err); + return ibuf; + } + if (len == sizeof (lbuf)) + { + result_err = read_memory (memaddr, &lbuf, len); + if (result_err) + error ("Error reading memory address 0x%x: %s (%d).", + memaddr, (result_err < sys_nerr ? + sys_errlist[result_err] : + "uknown error"), result_err); + return lbuf; + } + error ("Cannot handle integers of %d bytes.", len); +} + +CORE_ADDR +read_pc () +{ + return (CORE_ADDR) read_register (PC_REGNUM); +} + +void +write_pc (val) + CORE_ADDR val; +{ + write_register (PC_REGNUM, (long) val); +#ifdef NPC_REGNUM + write_register (NPC_REGNUM, (long) val+4); +#endif +} + +char *reg_names[] = REGISTER_NAMES; + +#if !defined (DO_REGISTERS_INFO) +static void +print_one_register(i) + int i; +{ + unsigned char raw_buffer[MAX_REGISTER_RAW_SIZE]; + unsigned char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE]; + REGISTER_TYPE val; + + /* Get the data in raw format, then convert also to virtual format. */ + read_relative_register_raw_bytes (i, raw_buffer); + REGISTER_CONVERT_TO_VIRTUAL (i, raw_buffer, virtual_buffer); + + fputs_filtered (reg_names[i], stdout); + print_spaces_filtered (15 - strlen (reg_names[i]), stdout); + + /* If virtual format is floating, print it that way. */ + if (TYPE_CODE (REGISTER_VIRTUAL_TYPE (i)) == TYPE_CODE_FLT + && ! INVALID_FLOAT (virtual_buffer, REGISTER_VIRTUAL_SIZE (i))) + val_print (REGISTER_VIRTUAL_TYPE (i), virtual_buffer, 0, + stdout, 0, 1, 0, Val_pretty_default); + /* Else if virtual format is too long for printf, + print in hex a byte at a time. */ + else if (REGISTER_VIRTUAL_SIZE (i) > sizeof (long)) + { + register int j; + printf_filtered ("0x"); + for (j = 0; j < REGISTER_VIRTUAL_SIZE (i); j++) + printf_filtered ("%02x", virtual_buffer[j]); + } + /* Else print as integer in hex and in decimal. */ + else + { + long val; + + bcopy (virtual_buffer, &val, sizeof (long)); + if (val == 0) + printf_filtered ("0"); + else + printf_filtered ("0x%08x %d", val, val); + } + + /* If register has different raw and virtual formats, + print the raw format in hex now. */ + + if (REGISTER_CONVERTIBLE (i)) + { + register int j; + + printf_filtered (" (raw 0x"); + for (j = 0; j < REGISTER_RAW_SIZE (i); j++) + printf_filtered ("%02x", raw_buffer[j]); + printf_filtered (")"); + } + printf_filtered ("\n"); +} + + +/* Print out the machine register regnum. If regnum is -1, + print all registers. + For most machines, having all_registers_info() print the + register(s) one per line is good enough. If a different format + is required, (eg, for SPARC or Pyramid 90x, which both have + lots of regs), or there is an existing convention for showing + all the registers, define the macro DO_REGISTERS_INFO(regnum) + to provide that format. */ +static void +do_registers_info (regnum, fpregs) + int regnum; + int fpregs; +{ + register int i; + + if (regnum >= 0) { + print_one_register(regnum); + return; + } +#ifdef notdef + printf_filtered ( +"Register Contents (relative to selected stack frame)\n\n"); +#endif + for (i = 0; i < NUM_REGS; i++) + if (TYPE_CODE(REGISTER_VIRTUAL_TYPE(i)) != TYPE_CODE_FLT || + fpregs) + print_one_register(i); +} +#endif /* no DO_REGISTERS_INFO. */ + +static void +registers_info (addr_exp, fpregs) + char *addr_exp; + int fpregs; +{ + int regnum; + + if (!have_inferior_p () && !have_core_file_p ()) + error ("No inferior or core file"); + + if (addr_exp) + { + if (*addr_exp >= '0' && *addr_exp <= '9') + regnum = atoi (addr_exp); + else + { + register char *p = addr_exp; + if (p[0] == '$') + p++; + for (regnum = 0; regnum < NUM_REGS; regnum++) + if (!strcmp (p, reg_names[regnum])) + break; + if (regnum == NUM_REGS) + error ("%s: invalid register name.", addr_exp); + } + } + else + regnum = -1; + +#ifdef DO_REGISTERS_INFO + DO_REGISTERS_INFO(regnum); +#else + do_registers_info(regnum, fpregs); +#endif +} + +static void +all_registers_info (addr_exp) + char *addr_exp; +{ + registers_info(addr_exp, 1); +} + +static void +nofp_registers_info (addr_exp) + char *addr_exp; +{ + registers_info(addr_exp, 0); +} + + +#ifdef ATTACH_DETACH +#define PROCESS_ATTACH_ALLOWED 1 +#else +#define PROCESS_ATTACH_ALLOWED 0 +#endif +/* + * TODO: + * Should save/restore the tty state since it might be that the + * program to be debugged was started on this tty and it wants + * the tty in some state other than what we want. If it's running + * on another terminal or without a terminal, then saving and + * restoring the tty state is a harmless no-op. + * This only needs to be done if we are attaching to a process. + */ + +/* + * attach_command -- + * takes a program started up outside of gdb and ``attaches'' to it. + * This stops it cold in its tracks and allows us to start tracing it. + * For this to work, we must be able to send the process a + * signal and we must have the same effective uid as the program. + */ +static void +attach_command (args, from_tty) + char *args; + int from_tty; +{ + char *exec_file; + int pid; + int remote = 0; + + dont_repeat(); + + if (!args) + error_no_arg ("process-id or device file to attach"); + + while (*args == ' ' || *args == '\t') args++; + + if (args[0] < '0' || args[0] > '9') + remote = 1; + else +#ifndef ATTACH_DETACH + error ("Can't attach to a process on this machine."); +#else + pid = atoi (args); +#endif + + if (inferior_pid) + { + if (query ("A program is being debugged already. Kill it? ")) + kill_inferior (); + else + error ("Inferior not killed."); + } + + exec_file = (char *) get_exec_file (1); + + if (from_tty) + { + if (remote) + printf ("Attaching remote machine\n"); + else + printf ("Attaching program: %s pid %d\n", + exec_file, pid); + fflush (stdout); + } + + if (remote) + { + remote_open (args, from_tty); + start_remote (); + } +#ifdef ATTACH_DETACH + else + attach_program (pid); +#endif +} + +/* + * detach_command -- + * takes a program previously attached to and detaches it. + * The program resumes execution and will no longer stop + * on signals, etc. We better not have left any breakpoints + * in the program or it'll die when it hits one. For this + * to work, it may be necessary for the process to have been + * previously attached. It *might* work if the program was + * started via the normal ptrace (PTRACE_TRACEME). + */ + +static void +detach_command (args, from_tty) + char *args; + int from_tty; +{ + int signal = 0; + +#ifdef ATTACH_DETACH + if (inferior_pid && !remote_debugging) + { + if (from_tty) + { + char *exec_file = (char *)get_exec_file (0); + if (exec_file == 0) + exec_file = ""; + printf ("Detaching program: %s pid %d\n", + exec_file, inferior_pid); + fflush (stdout); + } + if (args) + signal = atoi (args); + + detach (signal); + inferior_pid = 0; + } + else +#endif + { + if (!remote_debugging) + error ("Not currently attached to subsidiary or remote process."); + + if (args) + error ("Argument given to \"detach\" when remotely debugging."); + + inferior_pid = 0; + remote_close (from_tty); + } +} + +/* ARGSUSED */ +static void +float_info (addr_exp) + char *addr_exp; +{ +#ifdef FLOAT_INFO + FLOAT_INFO; +#else + printf ("No floating point info available for this processor.\n"); +#endif +} + +extern struct cmd_list_element *setlist, *deletelist; + +void +_initialize_infcmd () +{ + add_com ("tty", class_run, tty_command, + "Set terminal for future runs of program being debugged."); + + add_cmd ("args", class_run, set_args_command, + "Specify arguments to give program being debugged when it is started.\n\ +Follow this command with any number of args, to be passed to the program.", + &setlist); + + add_info ("environment", environment_info, + "The environment to give the program, or one variable's value.\n\ +With an argument VAR, prints the value of environment variable VAR to\n\ +give the program being debugged. With no arguments, prints the entire\n\ +environment to be given to the program."); + + add_cmd ("environment", class_run, unset_environment_command, + "Cancel environment variable VAR for the program.\n\ +This does not affect the program until the next \"run\" command.", + &deletelist); + + add_cmd ("environment", class_run, set_environment_command, + "Set environment variable value to give the program.\n\ +Arguments are VAR VALUE where VAR is variable name and VALUE is value.\n\ +VALUES of environment variables are uninterpreted strings.\n\ +This does not affect the program until the next \"run\" command.", + &setlist); + +#ifdef ATTACH_DETACH + add_com ("attach", class_run, attach_command, + "Attach to a process that was started up outside of GDB.\n\ +This command may take as argument a process id or a device file.\n\ +For a process id, you must have permission to send the process a signal,\n\ +and it must have the same effective uid as the debugger.\n\ +For a device file, the file must be a connection to a remote debug server.\n\n\ +Before using \"attach\", you must use the \"exec-file\" command\n\ +to specify the program running in the process,\n\ +and the \"symbol-file\" command to load its symbol table."); +#else + add_com ("attach", class_run, attach_command, + "Attach to a process that was started up outside of GDB.\n\ +This commands takes as an argument the name of a device file.\n\ +This file must be a connection to a remote debug server.\n\n\ +Before using \"attach\", you must use the \"exec-file\" command\n\ +to specify the program running in the process,\n\ +and the \"symbol-file\" command to load its symbol table."); +#endif + add_com ("detach", class_run, detach_command, + "Detach the process previously attached.\n\ +The process is no longer traced and continues its execution."); + + add_com ("signal", class_run, signal_command, + "Continue program giving it signal number SIGNUMBER."); + + add_com ("stepi", class_run, stepi_command, + "Step one instruction exactly.\n\ +Argument N means do this N times (or till program stops for another reason)."); + add_com_alias ("si", "stepi", class_alias, 0); + + add_com ("nexti", class_run, nexti_command, + "Step one instruction, but proceed through subroutine calls.\n\ +Argument N means do this N times (or till program stops for another reason)."); + add_com_alias ("ni", "nexti", class_alias, 0); + + add_com ("finish", class_run, finish_command, + "Execute until selected stack frame returns.\n\ +Upon return, the value returned is printed and put in the value history."); + + add_com ("next", class_run, next_command, + "Step program, proceeding through subroutine calls.\n\ +Like the \"step\" command as long as subroutine calls do not happen;\n\ +when they do, the call is treated as one instruction.\n\ +Argument N means do this N times (or till program stops for another reason)."); + add_com_alias ("n", "next", class_run, 1); + + add_com ("step", class_run, step_command, + "Step program until it reaches a different source line.\n\ +Argument N means do this N times (or till program stops for another reason)."); + add_com_alias ("s", "step", class_run, 1); + + add_com ("until", class_run, until_command, + "Execute until the program reaches a source line greater than the current\n\ +or a specified line or address or function (same args as break command).\n\ +Execution will also stop upon exit from the current stack frame."); + add_com_alias ("u", "until", class_run, 1); + + add_com ("jump", class_run, jump_command, + "Continue program being debugged at specified line or address.\n\ +Give as argument either LINENUM or *ADDR, where ADDR is an expression\n\ +for an address to start at."); + + add_com ("cont", class_run, cont_command, + "Continue program being debugged, after signal or breakpoint.\n\ +If proceeding from breakpoint, a number N may be used as an argument:\n\ +then the same breakpoint won't break until the Nth time it is reached."); + add_com_alias ("c", "cont", class_run, 1); + + add_com ("run", class_run, run_command, + "Start debugged program. You may specify arguments to give it.\n\ +Args may include \"*\", or \"[...]\"; they are expanded using \"sh\".\n\ +Input and output redirection with \">\", \"<\", or \">>\" are also allowed.\n\n\ +With no arguments, uses arguments last specified (with \"run\" or \"set args\".\n\ +To cancel previous arguments and run with no arguments,\n\ +use \"set args\" without arguments."); + add_com_alias ("r", "run", class_run, 1); + + add_info ("registers", nofp_registers_info, + "List of registers and their contents, for selected stack frame.\n\ +Register name as argument means describe only that register.\n\ +(Doesn't display floating point registers; use 'info all-registers'.)\n"); + + add_info ("all-registers", all_registers_info, + "List of registers and their contents, for selected stack frame.\n\ +Register name as argument means describe only that register."); + + add_info ("program", program_info, + "Execution status of the program."); + + add_info ("float", float_info, + "Print the status of the floating point unit\n"); + + inferior_args = savestring (" ", 1); /* By default, no args. */ + inferior_environ = make_environ (); + init_environ (inferior_environ); +} + diff --git a/gnu/usr.bin/gdb/inferior.h b/gnu/usr.bin/gdb/inferior.h new file mode 100644 index 0000000000..04c662e462 --- /dev/null +++ b/gnu/usr.bin/gdb/inferior.h @@ -0,0 +1,142 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + * + * @(#)inferior.h 6.3 (Berkeley) 5/8/91 + */ + +/* Variables that describe the inferior process running under GDB: + Where it is, why it stopped, and how to step it. + Copyright (C) 1986, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * Structure in which to save the status of the inferior. Save + * through "save_inferior_status", restore through + * "restore_inferior_status". + * This pair of routines should be called around any transfer of + * control to the inferior which you don't want showing up in your + * control variables. + */ +struct inferior_status { + int pc_changed; + int stop_signal; + int stop_pc; + int stop_frame_address; + int stop_breakpoint; + int stop_step; + int stop_stack_dummy; + int stopped_by_random_signal; + int trap_expected; + CORE_ADDR step_range_start; + CORE_ADDR step_range_end; + FRAME_ADDR step_frame_address; + int step_over_calls; + CORE_ADDR step_resume_break_address; + int stop_after_trap; + int stop_after_attach; + FRAME_ADDR selected_frame_address; + int selected_level; + struct command_line *breakpoint_commands; + char register_context[REGISTER_BYTES]; + int restore_stack_info; +}; + +void save_inferior_status (), restore_inferior_status (); + +/* File name for default use for standard in/out in the inferior. */ + +extern char *inferior_io_terminal; + +/* Pid of our debugged inferior, or 0 if no inferior now. */ + +extern int inferior_pid; + +/* Nonzero if debugging a remote machine via a serial link or ethernet. */ +extern int remote_debugging; + +/* Routines for use in remote debugging. Documented in remote.c. */ +int remote_read_inferior_memory (); +int remote_write_inferior_memory (); + +/* Last signal that the inferior received (why it stopped). */ + +extern int stop_signal; + +/* Address at which inferior stopped. */ + +extern CORE_ADDR stop_pc; + +/* Stack frame when program stopped. */ + +extern FRAME_ADDR stop_frame_address; + +/* Number of breakpoint it stopped at, or 0 if none. */ + +extern int stop_breakpoint; + +/* Nonzero if stopped due to a step command. */ + +extern int stop_step; + +/* Nonzero if stopped due to completion of a stack dummy routine. */ + +extern int stop_stack_dummy; + +/* Nonzero if program stopped due to a random (unexpected) signal in + inferior process. */ + +extern int stopped_by_random_signal; + +/* Range to single step within. + If this is nonzero, respond to a single-step signal + by continuing to step if the pc is in this range. */ + +extern CORE_ADDR step_range_start; /* Inclusive */ +extern CORE_ADDR step_range_end; /* Exclusive */ + +/* Stack frame address as of when stepping command was issued. + This is how we know when we step into a subroutine call, + and how to set the frame for the breakpoint used to step out. */ + +extern FRAME_ADDR step_frame_address; + +/* 1 means step over all subroutine calls. + -1 means step over calls to undebuggable functions. */ + +extern int step_over_calls; + +/* If stepping, nonzero means step count is > 1 + so don't print frame next time inferior stops + if it stops due to stepping. */ + +extern int step_multi; + +/* Save register contents here when about to pop a stack dummy frame. */ + +extern char stop_registers[REGISTER_BYTES]; + +/* Nonzero if pc has been changed by the debugger + since the inferior stopped. */ + +extern int pc_changed; + +long read_memory_integer (); diff --git a/gnu/usr.bin/gdb/inflow.c b/gnu/usr.bin/gdb/inflow.c new file mode 100644 index 0000000000..209fcf3734 --- /dev/null +++ b/gnu/usr.bin/gdb/inflow.c @@ -0,0 +1,569 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)inflow.c 6.5 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Low level interface to ptrace, for GDB when running under Unix. + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include "defs.h" +#include "param.h" +#include "frame.h" +#include "inferior.h" + +#ifdef USG +#include +#endif + +/* Some USG-esque systems (some of which are BSD-esque enough so that USG + is not defined) want this header, and it won't do any harm. */ +#include + +#include +#include +#include + +#ifdef HAVE_TERMIO +#include +#undef TIOCGETP +#define TIOCGETP TCGETA +#undef TIOCSETN +#define TIOCSETN TCSETA +#undef TIOCSETP +#define TIOCSETP TCSETAF +#define TERMINAL struct termio +#else +#include +#include +#include +#define TERMINAL struct sgttyb +#endif + +#ifdef SET_STACK_LIMIT_HUGE +#include +#include +extern int original_stack_limit; +#endif /* SET_STACK_LIMIT_HUGE */ + +extern int errno; + +/* Nonzero if we are debugging an attached outside process + rather than an inferior. */ + +int attach_flag; + + +/* Record terminal status separately for debugger and inferior. */ + +static TERMINAL sg_inferior; +static TERMINAL sg_ours; + +static int tflags_inferior; +static int tflags_ours; + +#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN) +static struct tchars tc_inferior; +static struct tchars tc_ours; +#endif + +#ifdef TIOCGLTC +static struct ltchars ltc_inferior; +static struct ltchars ltc_ours; +#endif + +#ifdef TIOCLGET +static int lmode_inferior; +static int lmode_ours; +#endif + +#ifdef TIOCGPGRP +static int pgrp_inferior; +static int pgrp_ours; +#else +static int (*sigint_ours) (); +static int (*sigquit_ours) (); +#endif /* TIOCGPGRP */ + +/* Copy of inferior_io_terminal when inferior was last started. */ +static char *inferior_thisrun_terminal; + +static void terminal_ours_1 (); + +/* Nonzero if our terminal settings are in effect. + Zero if the inferior's settings are in effect. */ +static int terminal_is_ours; + +/* Initialize the terminal settings we record for the inferior, + before we actually run the inferior. */ + +void +terminal_init_inferior () +{ + if (remote_debugging) + return; + + sg_inferior = sg_ours; + tflags_inferior = tflags_ours; + +#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN) + tc_inferior = tc_ours; +#endif + +#ifdef TIOCGLTC + ltc_inferior = ltc_ours; +#endif + +#ifdef TIOCLGET + lmode_inferior = lmode_ours; +#endif + +#ifdef TIOCGPGRP + pgrp_inferior = inferior_pid; +#endif /* TIOCGPGRP */ + + terminal_is_ours = 1; +} + +/* Put the inferior's terminal settings into effect. + This is preparation for starting or resuming the inferior. */ + +void +terminal_inferior () +{ + if (remote_debugging) + return; + + if (terminal_is_ours) /* && inferior_thisrun_terminal == 0) */ + { + fcntl (0, F_SETFL, tflags_inferior); + fcntl (0, F_SETFL, tflags_inferior); + ioctl (0, TIOCSETN, &sg_inferior); + +#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN) + ioctl (0, TIOCSETC, &tc_inferior); +#endif +#ifdef TIOCGLTC + ioctl (0, TIOCSLTC, <c_inferior); +#endif +#ifdef TIOCLGET + ioctl (0, TIOCLSET, &lmode_inferior); +#endif + +#ifdef TIOCGPGRP + ioctl (0, TIOCSPGRP, &pgrp_inferior); +#else + sigint_ours = (int (*) ()) signal (SIGINT, SIG_IGN); + sigquit_ours = (int (*) ()) signal (SIGQUIT, SIG_IGN); +#endif /* TIOCGPGRP */ + } + terminal_is_ours = 0; +} + +/* Put some of our terminal settings into effect, + enough to get proper results from our output, + but do not change into or out of RAW mode + so that no input is discarded. + + After doing this, either terminal_ours or terminal_inferior + should be called to get back to a normal state of affairs. */ + +void +terminal_ours_for_output () +{ + if (remote_debugging) + return; + + terminal_ours_1 (1); +} + +/* Put our terminal settings into effect. + First record the inferior's terminal settings + so they can be restored properly later. */ + +void +terminal_ours () +{ + if (remote_debugging) + return; + + terminal_ours_1 (0); +} + +static void +terminal_ours_1 (output_only) + int output_only; +{ +#ifdef TIOCGPGRP + /* Ignore this signal since it will happen when we try to set the pgrp. */ + void (*osigttou) (); +#endif /* TIOCGPGRP */ + + if (!terminal_is_ours) /* && inferior_thisrun_terminal == 0) */ + { + terminal_is_ours = 1; + +#ifdef TIOCGPGRP + osigttou = signal (SIGTTOU, SIG_IGN); + + ioctl (0, TIOCGPGRP, &pgrp_inferior); + ioctl (0, TIOCSPGRP, &pgrp_ours); + + signal (SIGTTOU, osigttou); +#else + signal (SIGINT, sigint_ours); + signal (SIGQUIT, sigquit_ours); +#endif /* TIOCGPGRP */ + + tflags_inferior = fcntl (0, F_GETFL, 0); + ioctl (0, TIOCGETP, &sg_inferior); + +#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN) + ioctl (0, TIOCGETC, &tc_inferior); +#endif +#ifdef TIOCGLTC + ioctl (0, TIOCGLTC, <c_inferior); +#endif +#ifdef TIOCLGET + ioctl (0, TIOCLGET, &lmode_inferior); +#endif + } + +#ifdef HAVE_TERMIO + sg_ours.c_lflag |= ICANON; + if (output_only && !(sg_inferior.c_lflag & ICANON)) + sg_ours.c_lflag &= ~ICANON; +#else /* not HAVE_TERMIO */ + sg_ours.sg_flags &= ~RAW & ~CBREAK; + if (output_only) + sg_ours.sg_flags |= (RAW | CBREAK) & sg_inferior.sg_flags; +#endif /* not HAVE_TERMIO */ + + fcntl (0, F_SETFL, tflags_ours); + fcntl (0, F_SETFL, tflags_ours); + ioctl (0, TIOCSETN, &sg_ours); + +#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN) + ioctl (0, TIOCSETC, &tc_ours); +#endif +#ifdef TIOCGLTC + ioctl (0, TIOCSLTC, <c_ours); +#endif +#ifdef TIOCLGET + ioctl (0, TIOCLSET, &lmode_ours); +#endif + +#ifdef HAVE_TERMIO + sg_ours.c_lflag |= ICANON; +#else /* not HAVE_TERMIO */ + sg_ours.sg_flags &= ~RAW & ~CBREAK; +#endif /* not HAVE_TERMIO */ +} + +static void +term_status_command () +{ + register int i; + + if (remote_debugging) + { + printf_filtered ("No terminal status when remote debugging.\n"); + return; + } + + printf_filtered ("Inferior's terminal status (currently saved by GDB):\n"); + +#ifdef HAVE_TERMIO + + printf_filtered ("fcntl flags = 0x%x, c_iflag = 0x%x, c_oflag = 0x%x,\n", + tflags_inferior, sg_inferior.c_iflag, sg_inferior.c_oflag); + printf_filtered ("c_cflag = 0x%x, c_lflag = 0x%x, c_line = 0x%x.\n", + sg_inferior.c_cflag, sg_inferior.c_lflag, sg_inferior.c_line); + printf_filtered ("c_cc: "); + for (i = 0; (i < NCC); i += 1) + printf_filtered ("0x%x ", sg_inferior.c_cc[i]); + printf_filtered ("\n"); + +#else /* not HAVE_TERMIO */ + + printf_filtered ("fcntl flags = 0x%x, sgttyb.sg_flags = 0x%x, owner pid = %d.\n", + tflags_inferior, sg_inferior.sg_flags, pgrp_inferior); + +#endif /* not HAVE_TERMIO */ + +#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN) + printf_filtered ("tchars: "); + for (i = 0; i < sizeof (struct tchars); i++) + printf_filtered ("0x%x ", ((char *)&tc_inferior)[i]); + printf_filtered ("\n"); +#endif + +#ifdef TIOCGLTC + printf_filtered ("ltchars: "); + for (i = 0; i < sizeof (struct ltchars); i++) + printf_filtered ("0x%x ", ((char *)<c_inferior)[i]); + printf_filtered ("\n"); + ioctl (0, TIOCSLTC, <c_ours); +#endif + +#ifdef TIOCLGET + printf_filtered ("lmode: %x\n", lmode_inferior); +#endif +} + +static void +new_tty (ttyname) + char *ttyname; +{ + register int tty; + register int fd; + +#ifdef TIOCNOTTY + /* Disconnect the child process from our controlling terminal. */ + tty = open("/dev/tty", O_RDWR); + if (tty > 0) + { + ioctl(tty, TIOCNOTTY, 0); + close(tty); + } +#endif + + /* Now open the specified new terminal. */ + + tty = open(ttyname, O_RDWR); + if (tty == -1) + _exit(1); + + /* Avoid use of dup2; doesn't exist on all systems. */ + if (tty != 0) + { close (0); dup (tty); } + if (tty != 1) + { close (1); dup (tty); } + if (tty != 2) + { close (2); dup (tty); } + if (tty > 2) + close(tty); +} + +/* Start an inferior process and returns its pid. + ALLARGS is a string containing shell command to run the program. + ENV is the environment vector to pass. */ + +#ifndef SHELL_FILE +#define SHELL_FILE "/bin/sh" +#endif + +int +create_inferior (allargs, env) + char *allargs; + char **env; +{ + int pid; + char *shell_command; + extern int sys_nerr; + extern char *sys_errlist[]; + extern int errno; + + /* If desired, concat something onto the front of ALLARGS. + SHELL_COMMAND is the result. */ +#ifdef SHELL_COMMAND_CONCAT + shell_command = (char *) alloca (strlen (SHELL_COMMAND_CONCAT) + strlen (allargs) + 1); + strcpy (shell_command, SHELL_COMMAND_CONCAT); + strcat (shell_command, allargs); +#else + shell_command = allargs; +#endif + + /* exec is said to fail if the executable is open. */ + close_exec_file (); + +#if defined(USG) && !defined(HAVE_VFORK) + pid = fork (); +#else + pid = vfork (); +#endif + + if (pid < 0) + perror_with_name ("vfork"); + + if (pid == 0) + { +#ifdef TIOCGPGRP + /* Run inferior in a separate process group. */ + setpgrp (getpid (), getpid ()); +#endif /* TIOCGPGRP */ + +#ifdef SET_STACK_LIMIT_HUGE + /* Reset the stack limit back to what it was. */ + { + struct rlimit rlim; + + getrlimit (RLIMIT_STACK, &rlim); + rlim.rlim_cur = original_stack_limit; + setrlimit (RLIMIT_STACK, &rlim); + } +#endif /* SET_STACK_LIMIT_HUGE */ + + + inferior_thisrun_terminal = inferior_io_terminal; + if (inferior_io_terminal != 0) + new_tty (inferior_io_terminal); + +/* It seems that changing the signal handlers for the inferior after + a vfork also changes them for the superior. See comments in + initialize_signals for how we get the right signal handlers + for the inferior. */ +/* Not needed on Sun, at least, and loses there + because it clobbers the superior. */ +/*??? signal (SIGQUIT, SIG_DFL); + signal (SIGINT, SIG_DFL); */ + + call_ptrace (0); + execle (SHELL_FILE, "sh", "-c", shell_command, 0, env); + + fprintf (stderr, "Cannot exec %s: %s.\n", SHELL_FILE, + errno < sys_nerr ? sys_errlist[errno] : "unknown error"); + fflush (stderr); + _exit (0177); + } + +#ifdef TIOCGPGRP + /* Avoid race with TIOCSPGRP: guarantee that inferior's pgrp exists. */ + setpgrp (pid, pid); +#endif /* TIOCGPGRP */ + +#ifdef CREATE_INFERIOR_HOOK + CREATE_INFERIOR_HOOK (pid); +#endif + return pid; +} + +/* Kill the inferior process. Make us have no inferior. */ + +static void +kill_command () +{ + if (remote_debugging) + { + inferior_pid = 0; + return; + } + if (inferior_pid == 0) + error ("The program is not being run."); + if (!query ("Kill the inferior process? ")) + error ("Not confirmed."); + kill_inferior (); +} + +void +inferior_died () +{ + inferior_pid = 0; + attach_flag = 0; + mark_breakpoints_out (); + select_frame ((FRAME) 0, -1); + reopen_exec_file (); + if (have_core_file_p ()) + set_current_frame ( create_new_frame (read_register (FP_REGNUM), + read_pc ())); + else + set_current_frame (0); +} + +#if 0 +/* This function is just for testing, and on some systems (Sony NewsOS + 3.2) also includes which leads to errors + (since on this system at least sys/time.h is not protected against + multiple inclusion). */ +static void +try_writing_regs_command () +{ + register int i; + register int value; + extern int errno; + + if (inferior_pid == 0) + error ("There is no inferior process now."); + + /* A Sun 3/50 or 3/60 (at least) running SunOS 4.0.3 will have a + kernel panic if we try to write past the end of the user area. + Presumably Sun will fix this bug (it has been reported), but it + is tacky to crash the system, so at least on SunOS4 we need to + stop writing when we hit the end of the user area. */ + for (i = 0; i < sizeof (struct user); i += 2) + { + QUIT; + errno = 0; + value = call_ptrace (3, inferior_pid, i, 0); + call_ptrace (6, inferior_pid, i, value); + if (errno == 0) + { + printf (" Succeeded with address 0x%x; value 0x%x (%d).\n", + i, value, value); + } + else if ((i & 0377) == 0) + printf (" Failed at 0x%x.\n", i); + } +} +#endif + +void +_initialize_inflow () +{ + add_com ("term-status", class_obscure, term_status_command, + "Print info on inferior's saved terminal status."); + +#if 0 + add_com ("try-writing-regs", class_obscure, try_writing_regs_command, + "Try writing all locations in inferior's system block.\n\ +Report which ones can be written."); +#endif + + add_com ("kill", class_run, kill_command, + "Kill execution of program being debugged."); + + inferior_pid = 0; + + ioctl (0, TIOCGETP, &sg_ours); + tflags_ours = fcntl (0, F_GETFL, 0); + +#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN) + ioctl (0, TIOCGETC, &tc_ours); +#endif +#ifdef TIOCGLTC + ioctl (0, TIOCGLTC, <c_ours); +#endif +#ifdef TIOCLGET + ioctl (0, TIOCLGET, &lmode_ours); +#endif + +#ifdef TIOCGPGRP + ioctl (0, TIOCGPGRP, &pgrp_ours); +#endif /* TIOCGPGRP */ + + terminal_is_ours = 1; +} + diff --git a/gnu/usr.bin/gdb/infrun.c b/gnu/usr.bin/gdb/infrun.c new file mode 100644 index 0000000000..887a0bb4e4 --- /dev/null +++ b/gnu/usr.bin/gdb/infrun.c @@ -0,0 +1,1459 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)infrun.c 6.4 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Start and stop the inferior process, for GDB. + Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Notes on the algorithm used in wait_for_inferior to determine if we + just did a subroutine call when stepping. We have the following + information at that point: + + Current and previous (just before this step) pc. + Current and previous sp. + Current and previous start of current function. + + If the start's of the functions don't match, then + + a) We did a subroutine call. + + In this case, the pc will be at the beginning of a function. + + b) We did a subroutine return. + + Otherwise. + + c) We did a longjmp. + + If we did a longjump, we were doing "nexti", since a next would + have attempted to skip over the assembly language routine in which + the longjmp is coded and would have simply been the equivalent of a + continue. I consider this ok behaivior. We'd like one of two + things to happen if we are doing a nexti through the longjmp() + routine: 1) It behaves as a stepi, or 2) It acts like a continue as + above. Given that this is a special case, and that anybody who + thinks that the concept of sub calls is meaningful in the context + of a longjmp, I'll take either one. Let's see what happens. + + Acts like a subroutine return. I can handle that with no problem + at all. + + -->So: If the current and previous beginnings of the current + function don't match, *and* the pc is at the start of a function, + we've done a subroutine call. If the pc is not at the start of a + function, we *didn't* do a subroutine call. + + -->If the beginnings of the current and previous function do match, + either: + + a) We just did a recursive call. + + In this case, we would be at the very beginning of a + function and 1) it will have a prologue (don't jump to + before prologue, or 2) (we assume here that it doesn't have + a prologue) there will have been a change in the stack + pointer over the last instruction. (Ie. it's got to put + the saved pc somewhere. The stack is the usual place. In + a recursive call a register is only an option if there's a + prologue to do something with it. This is even true on + register window machines; the prologue sets up the new + window. It might not be true on a register window machine + where the call instruction moved the register window + itself. Hmmm. One would hope that the stack pointer would + also change. If it doesn't, somebody send me a note, and + I'll work out a more general theory. + randy@wheaties.ai.mit.edu). This is true (albeit slipperly + so) on all machines I'm aware of: + + m68k: Call changes stack pointer. Regular jumps don't. + + sparc: Recursive calls must have frames and therefor, + prologues. + + vax: All calls have frames and hence change the + stack pointer. + + b) We did a return from a recursive call. I don't see that we + have either the ability or the need to distinguish this + from an ordinary jump. The stack frame will be printed + when and if the frame pointer changes; if we are in a + function without a frame pointer, it's the users own + lookout. + + c) We did a jump within a function. We assume that this is + true if we didn't do a recursive call. + + d) We are in no-man's land ("I see no symbols here"). We + don't worry about this; it will make calls look like simple + jumps (and the stack frames will be printed when the frame + pointer moves), which is a reasonably non-violent response. + +#if 0 + We skip this; it causes more problems than it's worth. +#ifdef SUN4_COMPILER_FEATURE + We do a special ifdef for the sun 4, forcing it to single step + into calls which don't have prologues. This means that we can't + nexti over leaf nodes, we can probably next over them (since they + won't have debugging symbols, usually), and we can next out of + functions returning structures (with a "call .stret4" at the end). +#endif +#endif +*/ + + + + + +#include +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "frame.h" +#include "inferior.h" +#include "wait.h" + +#include + +/* unistd.h is needed to #define X_OK */ +#ifdef USG +#include +#else +#include +#endif + +#ifdef UMAX_PTRACE +#include +#include +#include +#endif /* UMAX_PTRACE */ + +/* Required by . */ +#include +/* Required by , at least on system V. */ +#include +/* Needed by IN_SIGTRAMP on some machines (e.g. vax). */ +#include +/* Needed by IN_SIGTRAMP on some machines (e.g. vax). */ +#include + +extern char *sys_siglist[]; +extern int errno; + +/* Sigtramp is a routine that the kernel calls (which then calls the + signal handler). On most machines it is a library routine that + is linked into the executable. + + This macro, given a program counter value and the name of the + function in which that PC resides (which can be null if the + name is not known), returns nonzero if the PC and name show + that we are in sigtramp. + + On most machines just see if the name is sigtramp (and if we have + no name, assume we are not in sigtramp). */ +#if !defined (IN_SIGTRAMP) +#define IN_SIGTRAMP(pc, name) \ + name && !strcmp ("_sigtramp", name) +#endif + +/* Tables of how to react to signals; the user sets them. */ + +static char signal_stop[NSIG]; +static char signal_print[NSIG]; +static char signal_program[NSIG]; + +/* Nonzero if breakpoints are now inserted in the inferior. */ + +static int breakpoints_inserted; + +/* Function inferior was in as of last step command. */ + +static struct symbol *step_start_function; + +/* This is the sequence of bytes we insert for a breakpoint. */ + +static char break_insn[] = BREAKPOINT; + +/* Nonzero => address for special breakpoint for resuming stepping. */ + +static CORE_ADDR step_resume_break_address; + +/* Original contents of the byte where the special breakpoint is. */ + +static char step_resume_break_shadow[sizeof break_insn]; + +/* Nonzero means the special breakpoint is a duplicate + so it has not itself been inserted. */ + +static int step_resume_break_duplicate; + +/* Nonzero if we are expecting a trace trap and should proceed from it. + 2 means expecting 2 trace traps and should continue both times. + That occurs when we tell sh to exec the program: we will get + a trap after the exec of sh and a second when the program is exec'd. */ + +static int trap_expected; + +/* Nonzero if the next time we try to continue the inferior, it will + step one instruction and generate a spurious trace trap. + This is used to compensate for a bug in HP-UX. */ + +static int trap_expected_after_continue; + +/* Nonzero means expecting a trace trap + and should stop the inferior and return silently when it happens. */ + +int stop_after_trap; + +/* Nonzero means expecting a trace trap due to attaching to a process. */ + +int stop_after_attach; + +/* Nonzero if pc has been changed by the debugger + since the inferior stopped. */ + +int pc_changed; + +/* Nonzero if debugging a remote machine via a serial link or ethernet. */ + +int remote_debugging; + +/* Nonzero if program stopped due to error trying to insert breakpoints. */ + +static int breakpoints_failed; + +/* Nonzero if inferior is in sh before our program got exec'd. */ + +static int running_in_shell; + +/* Nonzero after stop if current stack frame should be printed. */ + +static int stop_print_frame; + +#ifdef NO_SINGLE_STEP +extern int one_stepped; /* From machine dependent code */ +extern void single_step (); /* Same. */ +#endif /* NO_SINGLE_STEP */ + +static void insert_step_breakpoint (); +static void remove_step_breakpoint (); +static void wait_for_inferior (); +static void normal_stop (); + + +/* Clear out all variables saying what to do when inferior is continued. + First do this, then set the ones you want, then call `proceed'. */ + +void +clear_proceed_status () +{ + trap_expected = 0; + step_range_start = 0; + step_range_end = 0; + step_frame_address = 0; + step_over_calls = -1; + step_resume_break_address = 0; + stop_after_trap = 0; + stop_after_attach = 0; + + /* Discard any remaining commands left by breakpoint we had stopped at. */ + clear_breakpoint_commands (); +} + +/* Basic routine for continuing the program in various fashions. + + ADDR is the address to resume at, or -1 for resume where stopped. + SIGNAL is the signal to give it, or 0 for none, + or -1 for act according to how it stopped. + STEP is nonzero if should trap after one instruction. + -1 means return after that and print nothing. + You should probably set various step_... variables + before calling here, if you are stepping. + + You should call clear_proceed_status before calling proceed. */ + +void +proceed (addr, signal, step) + CORE_ADDR addr; + int signal; + int step; +{ + int oneproc = 0; + + if (step > 0) + step_start_function = find_pc_function (read_pc ()); + if (step < 0) + stop_after_trap = 1; + + if (addr == -1) + { + /* If there is a breakpoint at the address we will resume at, + step one instruction before inserting breakpoints + so that we do not stop right away. */ + + if (!pc_changed && breakpoint_here_p (read_pc ())) + oneproc = 1; + } + else + { + write_register (PC_REGNUM, addr); +#ifdef NPC_REGNUM + write_register (NPC_REGNUM, addr + 4); +#endif + } + + if (trap_expected_after_continue) + { + /* If (step == 0), a trap will be automatically generated after + the first instruction is executed. Force step one + instruction to clear this condition. This should not occur + if step is nonzero, but it is harmless in that case. */ + oneproc = 1; + trap_expected_after_continue = 0; + } + + if (oneproc) + /* We will get a trace trap after one instruction. + Continue it automatically and insert breakpoints then. */ + trap_expected = 1; + else + { + int temp = insert_breakpoints (); + if (temp) + { + print_sys_errmsg ("ptrace", temp); + error ("Cannot insert breakpoints.\n\ +The same program may be running in another process."); + } + breakpoints_inserted = 1; + } + + /* Install inferior's terminal modes. */ + terminal_inferior (); + + if (signal >= 0) + stop_signal = signal; + /* If this signal should not be seen by program, + give it zero. Used for debugging signals. */ + else if (stop_signal < NSIG && !signal_program[stop_signal]) + stop_signal= 0; + + /* Resume inferior. */ + resume (oneproc || step, stop_signal); + + /* Wait for it to stop (if not standalone) + and in any case decode why it stopped, and act accordingly. */ + + wait_for_inferior (); + normal_stop (); +} + +/* Writing the inferior pc as a register calls this function + to inform infrun that the pc has been set in the debugger. */ + +void +writing_pc (val) + CORE_ADDR val; +{ + stop_pc = val; + pc_changed = 1; +} + +/* Start an inferior process for the first time. + Actually it was started by the fork that created it, + but it will have stopped one instruction after execing sh. + Here we must get it up to actual execution of the real program. */ + +void +start_inferior () +{ + /* We will get a trace trap after one instruction. + Continue it automatically. Eventually (after shell does an exec) + it will get another trace trap. Then insert breakpoints and continue. */ + +#ifdef START_INFERIOR_TRAPS_EXPECTED + trap_expected = START_INFERIOR_TRAPS_EXPECTED; +#else + trap_expected = 2; +#endif + + running_in_shell = 0; /* Set to 1 at first SIGTRAP, 0 at second. */ + trap_expected_after_continue = 0; + breakpoints_inserted = 0; + mark_breakpoints_out (); + + /* Set up the "saved terminal modes" of the inferior + based on what modes we are starting it with. */ + terminal_init_inferior (); + + /* Install inferior's terminal modes. */ + terminal_inferior (); + + if (remote_debugging) + { + trap_expected = 0; + fetch_inferior_registers(); + set_current_frame (create_new_frame (read_register (FP_REGNUM), + read_pc ())); + stop_frame_address = FRAME_FP (get_current_frame()); + inferior_pid = 3; + if (insert_breakpoints()) + fatal("Can't insert breakpoints"); + breakpoints_inserted = 1; + proceed(-1, -1, 0); + } + else + { + wait_for_inferior (); + normal_stop (); + } +} + +/* Start or restart remote-debugging of a machine over a serial link. */ + +void +restart_remote () +{ + clear_proceed_status (); + running_in_shell = 0; + trap_expected = 0; + stop_after_attach = 1; + inferior_pid = 3; + wait_for_inferior (); + normal_stop(); +} + +void +start_remote () +{ + breakpoints_inserted = 0; + mark_breakpoints_out (); + restart_remote(); +} + +#ifdef ATTACH_DETACH + +/* Attach to process PID, then initialize for debugging it + and wait for the trace-trap that results from attaching. */ + +void +attach_program (pid) + int pid; +{ + attach (pid); + inferior_pid = pid; + + mark_breakpoints_out (); + terminal_init_inferior (); + clear_proceed_status (); + stop_after_attach = 1; + /*proceed (-1, 0, -2);*/ + terminal_inferior (); + wait_for_inferior (); + normal_stop (); +} +#endif /* ATTACH_DETACH */ + +/* Wait for control to return from inferior to debugger. + If inferior gets a signal, we may decide to start it up again + instead of returning. That is why there is a loop in this function. + When this function actually returns it means the inferior + should be left stopped and GDB should read more commands. */ + +static void +wait_for_inferior () +{ + register int pid; + WAITTYPE w; + CORE_ADDR pc; + int tem; + int another_trap; + int random_signal; + CORE_ADDR stop_sp, prev_sp; + CORE_ADDR prev_func_start, stop_func_start; + char *prev_func_name, *stop_func_name; + CORE_ADDR prologue_pc; + int stop_step_resume_break; + CORE_ADDR step_resume_break_sp; + int newmisc; + int newfun_pc; + struct symtab_and_line sal; + int prev_pc; + extern CORE_ADDR text_end; + int remove_breakpoints_on_following_step = 0; + + prev_pc = read_pc (); + (void) find_pc_partial_function (prev_pc, &prev_func_name, + &prev_func_start); + prev_func_start += FUNCTION_START_OFFSET; + prev_sp = read_register (SP_REGNUM); + + while (1) + { + /* Clean up saved state that will become invalid. */ + pc_changed = 0; + flush_cached_frames (); + + if (remote_debugging) + remote_wait (&w); + else + { + pid = wait (&w); + if (pid != inferior_pid) + continue; + } + + /* See if the process still exists; clean up if it doesn't. */ + if (WIFEXITED (w)) + { + terminal_ours_for_output (); + if (WEXITSTATUS (w)) + printf ("\nProgram exited with code 0%o.\n", WEXITSTATUS (w)); + else + printf ("\nProgram exited normally.\n"); + fflush (stdout); + inferior_died (); +#ifdef NO_SINGLE_STEP + one_stepped = 0; +#endif + stop_print_frame = 0; + break; + } + else if (!WIFSTOPPED (w)) + { + kill_inferior (); + stop_print_frame = 0; + stop_signal = WTERMSIG (w); + terminal_ours_for_output (); + printf ("\nProgram terminated with signal %d, %s\n", + stop_signal, + stop_signal < NSIG + ? sys_siglist[stop_signal] + : "(undocumented)"); + printf ("The inferior process no longer exists.\n"); + fflush (stdout); +#ifdef NO_SINGLE_STEP + one_stepped = 0; +#endif + break; + } + +#ifdef NO_SINGLE_STEP + if (one_stepped) + single_step (0); /* This actually cleans up the ss */ +#endif /* NO_SINGLE_STEP */ + + fetch_inferior_registers (); + stop_pc = read_pc (); + set_current_frame ( create_new_frame (read_register (FP_REGNUM), + read_pc ())); + + stop_frame_address = FRAME_FP (get_current_frame ()); + stop_sp = read_register (SP_REGNUM); + stop_func_start = 0; + stop_func_name = 0; + /* Don't care about return value; stop_func_start and stop_func_name + will both be 0 if it doesn't work. */ + (void) find_pc_partial_function (stop_pc, &stop_func_name, + &stop_func_start); + stop_func_start += FUNCTION_START_OFFSET; + another_trap = 0; + stop_breakpoint = 0; + stop_step = 0; + stop_stack_dummy = 0; + stop_print_frame = 1; + stop_step_resume_break = 0; + random_signal = 0; + stopped_by_random_signal = 0; + breakpoints_failed = 0; + + /* Look at the cause of the stop, and decide what to do. + The alternatives are: + 1) break; to really stop and return to the debugger, + 2) drop through to start up again + (set another_trap to 1 to single step once) + 3) set random_signal to 1, and the decision between 1 and 2 + will be made according to the signal handling tables. */ + + stop_signal = WSTOPSIG (w); + + /* First, distinguish signals caused by the debugger from signals + that have to do with the program's own actions. + Note that breakpoint insns may cause SIGTRAP or SIGILL + or SIGEMT, depending on the operating system version. + Here we detect when a SIGILL or SIGEMT is really a breakpoint + and change it to SIGTRAP. */ + + if (stop_signal == SIGTRAP + || (breakpoints_inserted && + (stop_signal == SIGILL + || stop_signal == SIGEMT)) + || stop_after_attach) + { + if (stop_signal == SIGTRAP && stop_after_trap) + { + stop_print_frame = 0; + break; + } + if (stop_after_attach) + break; + /* Don't even think about breakpoints + if still running the shell that will exec the program + or if just proceeded over a breakpoint. */ + if (stop_signal == SIGTRAP && trap_expected) + stop_breakpoint = 0; + else + { + /* See if there is a breakpoint at the current PC. */ +#if DECR_PC_AFTER_BREAK + /* Notice the case of stepping through a jump + that leads just after a breakpoint. + Don't confuse that with hitting the breakpoint. + What we check for is that 1) stepping is going on + and 2) the pc before the last insn does not match + the address of the breakpoint before the current pc. */ + if (!(prev_pc != stop_pc - DECR_PC_AFTER_BREAK + && step_range_end && !step_resume_break_address)) +#endif /* DECR_PC_AFTER_BREAK not zero */ + { + /* See if we stopped at the special breakpoint for + stepping over a subroutine call. */ + if (stop_pc - DECR_PC_AFTER_BREAK + == step_resume_break_address) + { + stop_step_resume_break = 1; + if (DECR_PC_AFTER_BREAK) + { + stop_pc -= DECR_PC_AFTER_BREAK; + write_register (PC_REGNUM, stop_pc); + pc_changed = 0; + } + } + else + { + stop_breakpoint = + breakpoint_stop_status (stop_pc, stop_frame_address); + /* Following in case break condition called a + function. */ + stop_print_frame = 1; + if (stop_breakpoint && DECR_PC_AFTER_BREAK) + { + stop_pc -= DECR_PC_AFTER_BREAK; + write_register (PC_REGNUM, stop_pc); +#ifdef NPC_REGNUM + write_register (NPC_REGNUM, stop_pc + 4); +#endif + pc_changed = 0; + } + } + } + } + + if (stop_signal == SIGTRAP) + random_signal + = !(stop_breakpoint || trap_expected + || stop_step_resume_break +#ifndef CANNOT_EXECUTE_STACK + || (stop_sp INNER_THAN stop_pc + && stop_pc INNER_THAN stop_frame_address) +#else + || stop_pc == text_end - 2 +#endif + || (step_range_end && !step_resume_break_address)); + else + { + random_signal + = !(stop_breakpoint + || stop_step_resume_break +#ifdef sony_news + || (stop_sp INNER_THAN stop_pc + && stop_pc INNER_THAN stop_frame_address) +#endif + + ); + if (!random_signal) + stop_signal = SIGTRAP; + } + } + else + random_signal = 1; + + /* For the program's own signals, act according to + the signal handling tables. */ + + if (random_signal + && !(running_in_shell && stop_signal == SIGSEGV)) + { + /* Signal not for debugging purposes. */ + int printed = 0; + + stopped_by_random_signal = 1; + + if (stop_signal >= NSIG + || signal_print[stop_signal]) + { + printed = 1; + terminal_ours_for_output (); + printf ("\nProgram received signal %d, %s\n", + stop_signal, + stop_signal < NSIG + ? sys_siglist[stop_signal] + : "(undocumented)"); + fflush (stdout); + } + if (stop_signal >= NSIG + || signal_stop[stop_signal]) + break; + /* If not going to stop, give terminal back + if we took it away. */ + else if (printed) + terminal_inferior (); + } + + /* Handle cases caused by hitting a breakpoint. */ + + if (!random_signal + && (stop_breakpoint || stop_step_resume_break)) + { + /* Does a breakpoint want us to stop? */ + if (stop_breakpoint && stop_breakpoint != -1 + && stop_breakpoint != -0x1000001) + { + /* 0x1000000 is set in stop_breakpoint as returned by + breakpoint_stop_status to indicate a silent + breakpoint. */ + if ((stop_breakpoint > 0 ? stop_breakpoint : + -stop_breakpoint) + & 0x1000000) + { + stop_print_frame = 0; + if (stop_breakpoint > 0) + stop_breakpoint -= 0x1000000; + else + stop_breakpoint += 0x1000000; + } + break; + } + /* But if we have hit the step-resumption breakpoint, + remove it. It has done its job getting us here. + The sp test is to make sure that we don't get hung + up in recursive calls in functions without frame + pointers. If the stack pointer isn't outside of + where the breakpoint was set (within a routine to be + stepped over), we're in the middle of a recursive + call. Not true for reg window machines (sparc) + because the must change frames to call things and + the stack pointer doesn't have to change if it + the bp was set in a routine without a frame (pc can + be stored in some other window). + + The removal of the sp test is to allow calls to + alloca. Nasty things were happening. Oh, well, + gdb can only handle one level deep of lack of + frame pointer. */ + if (stop_step_resume_break + && (step_frame_address == 0 + || (stop_frame_address == step_frame_address))) + { + remove_step_breakpoint (); + step_resume_break_address = 0; + } + /* Otherwise, must remove breakpoints and single-step + to get us past the one we hit. */ + else + { + remove_breakpoints (); + remove_step_breakpoint (); + breakpoints_inserted = 0; + another_trap = 1; + } + + /* We come here if we hit a breakpoint but should not + stop for it. Possibly we also were stepping + and should stop for that. So fall through and + test for stepping. But, if not stepping, + do not stop. */ + } + + /* If this is the breakpoint at the end of a stack dummy, + just stop silently. */ +#ifndef CANNOT_EXECUTE_STACK + if (stop_sp INNER_THAN stop_pc + && stop_pc INNER_THAN stop_frame_address) +#else + if (stop_pc == text_end - 2) +#endif + { + stop_print_frame = 0; + stop_stack_dummy = 1; +#ifdef HP_OS_BUG + trap_expected_after_continue = 1; +#endif + break; + } + + if (step_resume_break_address) + /* Having a step-resume breakpoint overrides anything + else having to do with stepping commands until + that breakpoint is reached. */ + ; + /* If stepping through a line, keep going if still within it. */ + else if (!random_signal + && step_range_end + && stop_pc >= step_range_start + && stop_pc < step_range_end + /* The step range might include the start of the + function, so if we are at the start of the + step range and either the stack or frame pointers + just changed, we've stepped outside */ + && !(stop_pc == step_range_start + && stop_frame_address + && (stop_sp INNER_THAN prev_sp + || stop_frame_address != step_frame_address))) + { + /* Don't step through the return from a function + unless that is the first instruction stepped through. */ + if (ABOUT_TO_RETURN (stop_pc)) + { + stop_step = 1; + break; + } + } + + /* We stepped out of the stepping range. See if that was due + to a subroutine call that we should proceed to the end of. */ + else if (!random_signal && step_range_end) + { + if (stop_func_start) + { + prologue_pc = stop_func_start; + SKIP_PROLOGUE (prologue_pc); + } + + /* Did we just take a signal? */ + if (IN_SIGTRAMP (stop_pc, stop_func_name) + && !IN_SIGTRAMP (prev_pc, prev_func_name)) + { + /* This code is needed at least in the following case: + The user types "next" and then a signal arrives (before + the "next" is done). */ + /* We've just taken a signal; go until we are back to + the point where we took it and one more. */ + step_resume_break_address = prev_pc; + step_resume_break_duplicate = + breakpoint_here_p (step_resume_break_address); + step_resume_break_sp = stop_sp; + if (breakpoints_inserted) + insert_step_breakpoint (); + /* Make sure that the stepping range gets us past + that instruction. */ + if (step_range_end == 1) + step_range_end = (step_range_start = prev_pc) + 1; + remove_breakpoints_on_following_step = 1; + } + + /* ==> See comments at top of file on this algorithm. <==*/ + + else if (stop_pc == stop_func_start + && (stop_func_start != prev_func_start + || prologue_pc != stop_func_start + || stop_sp != prev_sp)) + { + /* It's a subroutine call */ + if (step_over_calls > 0 + || (step_over_calls && find_pc_function (stop_pc) == 0)) + { + /* A subroutine call has happened. */ + /* Set a special breakpoint after the return */ + step_resume_break_address = + SAVED_PC_AFTER_CALL (get_current_frame ()); + step_resume_break_duplicate + = breakpoint_here_p (step_resume_break_address); + step_resume_break_sp = stop_sp; + if (breakpoints_inserted) + insert_step_breakpoint (); + } + /* Subroutine call with source code we should not step over. + Do step to the first line of code in it. */ + else if (step_over_calls) + { + SKIP_PROLOGUE (stop_func_start); + sal = find_pc_line (stop_func_start, 0); + /* Use the step_resume_break to step until + the end of the prologue, even if that involves jumps + (as it seems to on the vax under 4.2). */ + /* If the prologue ends in the middle of a source line, + continue to the end of that source line. + Otherwise, just go to end of prologue. */ +#ifdef PROLOGUE_FIRSTLINE_OVERLAP + /* no, don't either. It skips any code that's + legitimately on the first line. */ +#else + if (sal.end && sal.pc != stop_func_start) + stop_func_start = sal.end; +#endif + + if (stop_func_start == stop_pc) + { + /* We are already there: stop now. */ + stop_step = 1; + break; + } + else + /* Put the step-breakpoint there and go until there. */ + { + step_resume_break_address = stop_func_start; + step_resume_break_sp = stop_sp; + + step_resume_break_duplicate + = breakpoint_here_p (step_resume_break_address); + if (breakpoints_inserted) + insert_step_breakpoint (); + /* Do not specify what the fp should be when we stop + since on some machines the prologue + is where the new fp value is established. */ + step_frame_address = 0; + /* And make sure stepping stops right away then. */ + step_range_end = step_range_start; + } + } + else + { + /* We get here only if step_over_calls is 0 and we + just stepped into a subroutine. I presume + that step_over_calls is only 0 when we're + supposed to be stepping at the assembly + language level.*/ + stop_step = 1; + break; + } + } + /* No subroutince call; stop now. */ + else + { + stop_step = 1; + break; + } + } + + /* Save the pc before execution, to compare with pc after stop. */ + prev_pc = read_pc (); /* Might have been DECR_AFTER_BREAK */ + prev_func_start = stop_func_start; /* Ok, since if DECR_PC_AFTER + BREAK is defined, the + original pc would not have + been at the start of a + function. */ + prev_func_name = stop_func_name; + prev_sp = stop_sp; + + /* If we did not do break;, it means we should keep + running the inferior and not return to debugger. */ + + /* If trap_expected is 2, it means continue once more + and insert breakpoints at the next trap. + If trap_expected is 1 and the signal was SIGSEGV, it means + the shell is doing some memory allocation--just resume it + with SIGSEGV. + Otherwise insert breakpoints now, and possibly single step. */ + + if (trap_expected > 1) + { + trap_expected--; + running_in_shell = 1; + resume (0, 0); + } + else if (running_in_shell && stop_signal == SIGSEGV) + { + resume (0, SIGSEGV); + } + else if (trap_expected && stop_signal != SIGTRAP) + { + /* We took a signal which we are supposed to pass through to + the inferior and we haven't yet gotten our trap. Simply + continue. */ + resume ((step_range_end && !step_resume_break_address) + || trap_expected, + stop_signal); + } + else + { + /* Here, we are not awaiting another exec to get + the program we really want to debug. + Insert breakpoints now, unless we are trying + to one-proceed past a breakpoint. */ + running_in_shell = 0; + /* If we've just finished a special step resume and we don't + want to hit a breakpoint, pull em out. */ + if (!step_resume_break_address && + remove_breakpoints_on_following_step) + { + remove_breakpoints_on_following_step = 0; + remove_breakpoints (); + breakpoints_inserted = 0; + } + else if (!breakpoints_inserted && !another_trap) + { + insert_step_breakpoint (); + breakpoints_failed = insert_breakpoints (); + if (breakpoints_failed) + break; + breakpoints_inserted = 1; + } + + trap_expected = another_trap; + + if (stop_signal == SIGTRAP) + stop_signal = 0; + + resume ((step_range_end && !step_resume_break_address) + || trap_expected, + stop_signal); + } + } +} + +/* Here to return control to GDB when the inferior stops for real. + Print appropriate messages, remove breakpoints, give terminal our modes. + + RUNNING_IN_SHELL nonzero means the shell got a signal before + exec'ing the program we wanted to run. + STOP_PRINT_FRAME nonzero means print the executing frame + (pc, function, args, file, line number and line text). + BREAKPOINTS_FAILED nonzero means stop was due to error + attempting to insert breakpoints. */ + +static void +normal_stop () +{ + /* Make sure that the current_frame's pc is correct. This + is a correction for setting up the frame info before doing + DECR_PC_AFTER_BREAK */ + if (inferior_pid) + (get_current_frame ())->pc = read_pc (); + + if (breakpoints_failed) + { + terminal_ours_for_output (); + print_sys_errmsg ("ptrace", breakpoints_failed); + printf ("Stopped; cannot insert breakpoints.\n\ +The same program may be running in another process.\n"); + } + + if (inferior_pid) + remove_step_breakpoint (); + + if (inferior_pid && breakpoints_inserted) + if (remove_breakpoints ()) + { + terminal_ours_for_output (); + printf ("Cannot remove breakpoints because program is no longer writable.\n\ +It must be running in another process.\n\ +Further execution is probably impossible.\n"); + } + + breakpoints_inserted = 0; + + /* Delete the breakpoint we stopped at, if it wants to be deleted. + Delete any breakpoint that is to be deleted at the next stop. */ + + breakpoint_auto_delete (stop_breakpoint); + + /* If an auto-display called a function and that got a signal, + delete that auto-display to avoid an infinite recursion. */ + + if (stopped_by_random_signal) + disable_current_display (); + + if (step_multi && stop_step) + return; + + terminal_ours (); + + if (running_in_shell) + { + if (stop_signal == SIGSEGV) + { + char *exec_file = (char *) get_exec_file (1); + + if (access (exec_file, X_OK) != 0) + printf ("The file \"%s\" is not executable.\n", exec_file); + else + /* I don't think we should ever get here. + wait_for_inferior now ignores SIGSEGV's which happen in + the shell (since the Bourne shell (/bin/sh) has some + rather, er, uh, *unorthodox* memory management + involving catching SIGSEGV). */ + printf ("\ +You have just encountered a bug in \"sh\". GDB starts your program\n\ +by running \"sh\" with a command to exec your program.\n\ +This is so that \"sh\" will process wildcards and I/O redirection.\n\ +This time, \"sh\" crashed.\n\ +\n\ +One known bug in \"sh\" bites when the environment takes up a lot of space.\n\ +Try \"info env\" to see the environment; then use \"delete env\" to kill\n\ +some variables whose values are large; then do \"run\" again.\n\ +\n\ +If that works, you might want to put those \"delete env\" commands\n\ +into a \".gdbinit\" file in this directory so they will happen every time.\n"); + } + /* Don't confuse user with his program's symbols on sh's data. */ + stop_print_frame = 0; + } + + if (inferior_pid == 0) + return; + + /* Select innermost stack frame except on return from a stack dummy routine, + or if the program has exited. */ + if (!stop_stack_dummy) + { + select_frame (get_current_frame (), 0); + + if (stop_print_frame) + { + if (stop_breakpoint > 0) + printf ("\nBpt %d, ", stop_breakpoint); + print_sel_frame (stop_step + && step_frame_address == stop_frame_address + && step_start_function == find_pc_function (stop_pc)); + /* Display the auto-display expressions. */ + do_displays (); + } + } + + if (stop_stack_dummy) + { + /* Pop the empty frame that contains the stack dummy. + POP_FRAME ends with a setting of the current frame, so we + can use that next. */ +#ifndef NEW_CALL_FUNCTION + POP_FRAME; +#endif + select_frame (get_current_frame (), 0); + } +} + +static void +insert_step_breakpoint () +{ + if (step_resume_break_address && !step_resume_break_duplicate) + { + read_memory (step_resume_break_address, + step_resume_break_shadow, sizeof break_insn); + write_memory (step_resume_break_address, + break_insn, sizeof break_insn); + } +} + +static void +remove_step_breakpoint () +{ + if (step_resume_break_address && !step_resume_break_duplicate) + write_memory (step_resume_break_address, step_resume_break_shadow, + sizeof break_insn); +} + +/* Specify how various signals in the inferior should be handled. */ + +static void +handle_command (args, from_tty) + char *args; + int from_tty; +{ + register char *p = args; + int signum = 0; + register int digits, wordlen; + + if (!args) + error_no_arg ("signal to handle"); + + while (*p) + { + /* Find the end of the next word in the args. */ + for (wordlen = 0; p[wordlen] && p[wordlen] != ' ' && p[wordlen] != '\t'; + wordlen++); + for (digits = 0; p[digits] >= '0' && p[digits] <= '9'; digits++); + + /* If it is all digits, it is signal number to operate on. */ + if (digits == wordlen) + { + signum = atoi (p); + if (signum <= 0 || signum >= NSIG) + { + p[wordlen] = '\0'; + error ("Invalid signal %s given as argument to \"handle\".", p); + } + if (signum == SIGTRAP || signum == SIGINT) + { + if (!query ("Signal %d is used by the debugger.\nAre you sure you want to change it? ", signum)) + error ("Not confirmed."); + } + } + else if (signum == 0) + error ("First argument is not a signal number."); + + /* Else, if already got a signal number, look for flag words + saying what to do for it. */ + else if (!strncmp (p, "stop", wordlen)) + { + signal_stop[signum] = 1; + signal_print[signum] = 1; + } + else if (wordlen >= 2 && !strncmp (p, "print", wordlen)) + signal_print[signum] = 1; + else if (wordlen >= 2 && !strncmp (p, "pass", wordlen)) + signal_program[signum] = 1; + else if (!strncmp (p, "ignore", wordlen)) + signal_program[signum] = 0; + else if (wordlen >= 3 && !strncmp (p, "nostop", wordlen)) + signal_stop[signum] = 0; + else if (wordlen >= 4 && !strncmp (p, "noprint", wordlen)) + { + signal_print[signum] = 0; + signal_stop[signum] = 0; + } + else if (wordlen >= 4 && !strncmp (p, "nopass", wordlen)) + signal_program[signum] = 0; + else if (wordlen >= 3 && !strncmp (p, "noignore", wordlen)) + signal_program[signum] = 1; + /* Not a number and not a recognized flag word => complain. */ + else + { + p[wordlen] = 0; + error ("Unrecognized flag word: \"%s\".", p); + } + + /* Find start of next word. */ + p += wordlen; + while (*p == ' ' || *p == '\t') p++; + } + + if (from_tty) + { + /* Show the results. */ + printf ("Number\tStop\tPrint\tPass to program\tDescription\n"); + printf ("%d\t", signum); + printf ("%s\t", signal_stop[signum] ? "Yes" : "No"); + printf ("%s\t", signal_print[signum] ? "Yes" : "No"); + printf ("%s\t\t", signal_program[signum] ? "Yes" : "No"); + printf ("%s\n", sys_siglist[signum]); + } +} + +/* Print current contents of the tables set by the handle command. */ + +static void +signals_info (signum_exp) + char *signum_exp; +{ + register int i; + printf_filtered ("Number\tStop\tPrint\tPass to program\tDescription\n"); + + if (signum_exp) + { + i = parse_and_eval_address (signum_exp); + if (i >= NSIG || i < 0) + error ("Signal number out of bounds."); + printf_filtered ("%d\t", i); + printf_filtered ("%s\t", signal_stop[i] ? "Yes" : "No"); + printf_filtered ("%s\t", signal_print[i] ? "Yes" : "No"); + printf_filtered ("%s\t\t", signal_program[i] ? "Yes" : "No"); + printf_filtered ("%s\n", sys_siglist[i]); + return; + } + + printf_filtered ("\n"); + for (i = 0; i < NSIG; i++) + { + QUIT; + + printf_filtered ("%d\t", i); + printf_filtered ("%s\t", signal_stop[i] ? "Yes" : "No"); + printf_filtered ("%s\t", signal_print[i] ? "Yes" : "No"); + printf_filtered ("%s\t\t", signal_program[i] ? "Yes" : "No"); + printf_filtered ("%s\n", sys_siglist[i]); + } + + printf_filtered ("\nUse the \"handle\" command to change these tables.\n"); +} + +/* Save all of the information associated with the inferior<==>gdb + connection. INF_STATUS is a pointer to a "struct inferior_status" + (defined in inferior.h). */ + +struct command_line *get_breakpoint_commands (); + +void +save_inferior_status (inf_status, restore_stack_info) + struct inferior_status *inf_status; + int restore_stack_info; +{ + inf_status->pc_changed = pc_changed; + inf_status->stop_signal = stop_signal; + inf_status->stop_pc = stop_pc; + inf_status->stop_frame_address = stop_frame_address; + inf_status->stop_breakpoint = stop_breakpoint; + inf_status->stop_step = stop_step; + inf_status->stop_stack_dummy = stop_stack_dummy; + inf_status->stopped_by_random_signal = stopped_by_random_signal; + inf_status->trap_expected = trap_expected; + inf_status->step_range_start = step_range_start; + inf_status->step_range_end = step_range_end; + inf_status->step_frame_address = step_frame_address; + inf_status->step_over_calls = step_over_calls; + inf_status->step_resume_break_address = step_resume_break_address; + inf_status->stop_after_trap = stop_after_trap; + inf_status->stop_after_attach = stop_after_attach; + inf_status->breakpoint_commands = get_breakpoint_commands (); + inf_status->restore_stack_info = restore_stack_info; + + read_register_bytes(0, inf_status->register_context, REGISTER_BYTES); + record_selected_frame (&(inf_status->selected_frame_address), + &(inf_status->selected_level)); + return; +} + +void +restore_inferior_status (inf_status) + struct inferior_status *inf_status; +{ + FRAME fid; + int level = inf_status->selected_level; + + pc_changed = inf_status->pc_changed; + stop_signal = inf_status->stop_signal; + stop_pc = inf_status->stop_pc; + stop_frame_address = inf_status->stop_frame_address; + stop_breakpoint = inf_status->stop_breakpoint; + stop_step = inf_status->stop_step; + stop_stack_dummy = inf_status->stop_stack_dummy; + stopped_by_random_signal = inf_status->stopped_by_random_signal; + trap_expected = inf_status->trap_expected; + step_range_start = inf_status->step_range_start; + step_range_end = inf_status->step_range_end; + step_frame_address = inf_status->step_frame_address; + step_over_calls = inf_status->step_over_calls; + step_resume_break_address = inf_status->step_resume_break_address; + stop_after_trap = inf_status->stop_after_trap; + stop_after_attach = inf_status->stop_after_attach; + set_breakpoint_commands (inf_status->breakpoint_commands); + + write_register_bytes(0, inf_status->register_context, REGISTER_BYTES); + + /* The inferior can be gone if the user types "print exit(0)" + (and perhaps other times). */ + if (have_inferior_p() && inf_status->restore_stack_info) + { + flush_cached_frames(); + set_current_frame(create_new_frame(read_register (FP_REGNUM), + read_pc())); + + fid = find_relative_frame (get_current_frame (), &level); + + if (fid == 0 || + FRAME_FP (fid) != inf_status->selected_frame_address || + level != 0) + { + /* I'm not sure this error message is a good idea. I have + only seen it occur after "Can't continue previously + requested operation" (we get called from do_cleanups), in + which case it just adds insult to injury (one confusing + error message after another. Besides which, does the + user really care if we can't restore the previously + selected frame? */ + fprintf (stderr, "Unable to restore previously selected frame.\n"); + select_frame (get_current_frame (), 0); + return; + } + + select_frame (fid, inf_status->selected_level); + } + return; +} + + +void +_initialize_infrun () +{ + register int i; + + add_info ("signals", signals_info, + "What debugger does when program gets various signals.\n\ +Specify a signal number as argument to print info on that signal only."); + + add_com ("handle", class_run, handle_command, + "Specify how to handle a signal.\n\ +Args are signal number followed by flags.\n\ +Flags allowed are \"stop\", \"print\", \"pass\",\n\ + \"nostop\", \"noprint\" or \"nopass\".\n\ +Print means print a message if this signal happens.\n\ +Stop means reenter debugger if this signal happens (implies print).\n\ +Pass means let program see this signal; otherwise program doesn't know.\n\ +Pass and Stop may be combined."); + + for (i = 0; i < NSIG; i++) + { + signal_stop[i] = 1; + signal_print[i] = 1; + signal_program[i] = 1; + } + + /* Signals caused by debugger's own actions + should not be given to the program afterwards. */ + signal_program[SIGTRAP] = 0; + signal_program[SIGINT] = 0; + + /* Signals that are not errors should not normally enter the debugger. */ +#ifdef SIGALRM + signal_stop[SIGALRM] = 0; + signal_print[SIGALRM] = 0; +#endif /* SIGALRM */ +#ifdef SIGVTALRM + signal_stop[SIGVTALRM] = 0; + signal_print[SIGVTALRM] = 0; +#endif /* SIGVTALRM */ +#ifdef SIGPROF + signal_stop[SIGPROF] = 0; + signal_print[SIGPROF] = 0; +#endif /* SIGPROF */ +#ifdef SIGCHLD + signal_stop[SIGCHLD] = 0; + signal_print[SIGCHLD] = 0; +#endif /* SIGCHLD */ +#ifdef SIGCLD + signal_stop[SIGCLD] = 0; + signal_print[SIGCLD] = 0; +#endif /* SIGCLD */ +#ifdef SIGIO + signal_stop[SIGIO] = 0; + signal_print[SIGIO] = 0; +#endif /* SIGIO */ +#ifdef SIGURG + signal_stop[SIGURG] = 0; + signal_print[SIGURG] = 0; +#endif /* SIGURG */ +} + diff --git a/gnu/usr.bin/gdb/kgdb_proto.h b/gnu/usr.bin/gdb/kgdb_proto.h new file mode 100644 index 0000000000..8bbd5bea33 --- /dev/null +++ b/gnu/usr.bin/gdb/kgdb_proto.h @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Steven McCanne of Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)kgdb_proto.h 6.3 (Berkeley) 5/8/91 + * + * + * $Header: /home/cvs/386BSD/src/usr.bin/gdb/kgdb_proto.h,v 1.1.1.1 1993/06/12 14:52:25 rgrimes Exp $ (LBL) + */ + +/* + * Message types. + */ +#define KGDB_MEM_R 0x01 +#define KGDB_MEM_W 0x02 +#define KGDB_REG_R 0x03 +#define KGDB_REG_W 0x04 +#define KGDB_CONT 0x05 +#define KGDB_STEP 0x06 +#define KGDB_KILL 0x07 +#define KGDB_SIGNAL 0x08 +#define KGDB_EXEC 0x09 + +#define KGDB_CMD(x) ((x) & 0x0f) + +/* + * Message flags. + */ +#define KGDB_ACK 0x80 +#define KGDB_DELTA 0x40 +#define KGDB_MORE 0x20 +#define KGDB_SEQ 0x10 diff --git a/gnu/usr.bin/gdb/main.c b/gnu/usr.bin/gdb/main.c new file mode 100644 index 0000000000..323de87975 --- /dev/null +++ b/gnu/usr.bin/gdb/main.c @@ -0,0 +1,2241 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 6.6 (Berkeley) 5/13/91"; +#endif /* not lint */ + +/* Top level for GDB, the GNU debugger. + Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include "defs.h" +#include "command.h" +#include "param.h" +#include "expression.h" + +#ifdef USG +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef SET_STACK_LIMIT_HUGE +#include +#include + +int original_stack_limit; +#endif + +/* If this definition isn't overridden by the header files, assume + that isatty and fileno exist on this system. */ +#ifndef ISATTY +#define ISATTY(FP) (isatty (fileno (FP))) +#endif + +extern void free (); + +/* Version number of GDB, as a string. */ + +extern char *version; + +/* + * Declare all cmd_list_element's + */ + +/* Chain containing all defined commands. */ + +struct cmd_list_element *cmdlist; + +/* Chain containing all defined info subcommands. */ + +struct cmd_list_element *infolist; + +/* Chain containing all defined enable subcommands. */ + +struct cmd_list_element *enablelist; + +/* Chain containing all defined disable subcommands. */ + +struct cmd_list_element *disablelist; + +/* Chain containing all defined delete subcommands. */ + +struct cmd_list_element *deletelist; + +/* Chain containing all defined "enable breakpoint" subcommands. */ + +struct cmd_list_element *enablebreaklist; + +/* Chain containing all defined set subcommands */ + +struct cmd_list_element *setlist; + +/* Chain containing all defined \"set history\". */ + +struct cmd_list_element *sethistlist; + +/* Chain containing all defined \"unset history\". */ + +struct cmd_list_element *unsethistlist; + +/* stdio stream that command input is being read from. */ + +FILE *instream; + +/* Current working directory. */ + +char *current_directory; + +/* The directory name is actually stored here (usually). */ +static char dirbuf[MAXPATHLEN]; + +#ifdef KERNELDEBUG +/* Nonzero if we're debugging /dev/mem or a kernel crash dump */ + +int kernel_debugging; +#endif + +/* Nonzero to inhibit confirmation of quitting or restarting + a stopped inferior. */ +int inhibit_confirm; + +/* Nonzero if we can write in text or core file */ + +int writeable_text; + +/* The number of lines on a page, and the number of spaces + in a line. */ +int linesize, pagesize; + +/* Nonzero if we should refrain from using an X window. */ + +int inhibit_windows = 0; + +/* Function to call before reading a command, if nonzero. + The function receives two args: an input stream, + and a prompt string. */ + +void (*window_hook) (); + +extern int frame_file_full_name; +int xgdb_verbose; + +void execute_command(); +void free_command_lines (); +char *gdb_readline (); +char *command_line_input (); +static void initialize_main (); +static void initialize_cmd_lists (); +void command_loop (); +static void source_command (); +static void print_gdb_version (); +static void float_handler (); +static void cd_command (); + +char *getenv (); + +/* gdb prints this when reading a command interactively */ +static char *prompt; + +/* Buffer used for reading command lines, and the size + allocated for it so far. */ + +char *line; +int linesize; + + +/* This is how `error' returns to command level. */ + +jmp_buf to_top_level; + +void +return_to_top_level () +{ + quit_flag = 0; + immediate_quit = 0; + clear_breakpoint_commands (); + clear_momentary_breakpoints (); + disable_current_display (); + do_cleanups (0); + longjmp (to_top_level, 1); +} + +/* Call FUNC with arg ARG, catching any errors. + If there is no error, return the value returned by FUNC. + If there is an error, return zero after printing ERRSTRING + (which is in addition to the specific error message already printed). */ + +int +catch_errors (func, arg, errstring) + int (*func) (); + int arg; + char *errstring; +{ + jmp_buf saved; + int val; + struct cleanup *saved_cleanup_chain; + + saved_cleanup_chain = save_cleanups (); + + bcopy (to_top_level, saved, sizeof (jmp_buf)); + + if (setjmp (to_top_level) == 0) + val = (*func) (arg); + else + { + fprintf (stderr, "%s\n", errstring); + val = 0; + } + + restore_cleanups (saved_cleanup_chain); + + bcopy (saved, to_top_level, sizeof (jmp_buf)); + return val; +} + +/* Handler for SIGHUP. */ + +static void +disconnect () +{ + kill_inferior_fast (); + signal (SIGHUP, SIG_DFL); + kill (getpid (), SIGHUP); +} + +/* Clean up on error during a "source" command (or execution of a + user-defined command). + Close the file opened by the command + and restore the previous input stream. */ + +static void +source_cleanup (stream) + FILE *stream; +{ + /* Instream may be 0; set to it when executing user-defined command. */ + if (instream) + fclose (instream); + instream = stream; +} + +/* + * Source $HOME/.gdbinit and $cwd/.gdbinit. + * If X is enabled, also $HOME/.xgdbinit and $cwd/.xgdbinit.source + */ +void +source_init_files() +{ + char *homedir, initfile[256]; + int samedir = 0; + + /* Read init file, if it exists in home directory */ + homedir = getenv ("HOME"); + if (homedir) { + struct stat homebuf, cwdbuf; + + sprintf(initfile, "%s/.gdbinit", homedir); + if (access (initfile, R_OK) == 0) + if (!setjmp (to_top_level)) + source_command (initfile); + if (!inhibit_windows) { + sprintf(initfile, "%s/.xgdbinit", homedir); + if (access (initfile, R_OK) == 0) + if (!setjmp (to_top_level)) + source_command (initfile); + } + /* Determine if current directory is the same as the home + directory, so we don't source the same file twice. */ + + bzero (&homebuf, sizeof (struct stat)); + bzero (&cwdbuf, sizeof (struct stat)); + + stat(homedir, &homebuf); + stat(".", &cwdbuf); + + samedir = bcmp(&homebuf, &cwdbuf, sizeof(struct stat)) == 0; + } + /* Read the input file in the current directory, *if* it isn't + the same file (it should exist, also). */ + if (!samedir) { + if (access (".gdbinit", R_OK) == 0) + if (!setjmp (to_top_level)) + source_command (".gdbinit"); + if (access (".xgdbinit", R_OK) == 0) + if (!setjmp (to_top_level)) + source_command (".xgdbinit"); + } +} + + +int +main (argc, argv, envp) + int argc; + char **argv; + char **envp; +{ + int count; + int inhibit_gdbinit = 0; + int quiet = 1; + int batch = 0; + register int i; + char *cp; + + /* XXX Windows only for xgdb. */ + char *strrchr(); + if (cp = strrchr(argv[0], '/')) + ++cp; + else + cp = argv[0]; + if (*cp != 'x') + inhibit_windows = 1; + +#if defined (ALIGN_STACK_ON_STARTUP) + i = (int) &count & 0x3; + if (i != 0) + alloca (4 - i); +#endif + + quit_flag = 0; + linesize = 100; + line = (char *) xmalloc (linesize); + *line = 0; + instream = stdin; + + getwd (dirbuf); + current_directory = dirbuf; + +#ifdef SET_STACK_LIMIT_HUGE + { + struct rlimit rlim; + + /* Set the stack limit huge so that alloca (particularly stringtab + * in dbxread.c) does not fail. */ + getrlimit (RLIMIT_STACK, &rlim); + original_stack_limit = rlim.rlim_cur; + rlim.rlim_cur = rlim.rlim_max; + setrlimit (RLIMIT_STACK, &rlim); + } +#endif /* SET_STACK_LIMIT_HUGE */ + + /* Look for flag arguments. */ + + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-q") || !strcmp (argv[i], "-quiet")) + quiet = 1; + else if (!strcmp (argv[i], "-nx")) + inhibit_gdbinit = 1; + else if (!strcmp (argv[i], "-nw")) + inhibit_windows = 1; + else if (!strcmp (argv[i], "-batch")) + batch = 1, quiet = 1; + else if (!strcmp (argv[i], "-fullname")) + frame_file_full_name = 1; + else if (!strcmp (argv[i], "-xgdb_verbose")) + xgdb_verbose = 1; + /* -help: print a summary of command line switches. */ + else if (!strcmp (argv[i], "-help")) + { + fputs ("\ +This is GDB, the GNU debugger. Use the command\n\ + gdb [options] [executable [core-file]]\n\ +to enter the debugger.\n\ +\n\ +Options available are:\n\ + -help Print this message.\n\ + -quiet Do not print version number on startup.\n\ + -fullname Output information used by emacs-GDB interface.\n\ + -batch Exit after processing options.\n\ + -nx Do not read .gdbinit file.\n\ + -tty TTY Use TTY for input/output by the program being debugged.\n\ + -cd DIR Change current directory to DIR.\n\ + -directory DIR Search for source files in DIR.\n\ + -command FILE Execute GDB commands from FILE.\n\ + -symbols SYMFILE Read symbols from SYMFILE.\n\ + -exec EXECFILE Use EXECFILE as the executable.\n\ + -se FILE Use FILE as symbol file and executable file.\n\ + -core COREFILE Analyze the core dump COREFILE.\n\ + -k Kernel debugging.\n\ + -w Writeable text.\n\ + -v Print GNU message and version number on startup.\n\ + -nc Don't confirm quit or run commands.\n\ +\n\ +For more information, type \"help\" from within GDB, or consult the\n\ +GDB manual (available as on-line info or a printed manual).\n", stderr); + /* Exiting after printing this message seems like + the most useful thing to do. */ + exit (0); + } +#ifdef KERNELDEBUG + else if (!strcmp (argv[i], "-k")) + kernel_debugging = 1; +#endif + else if (!strcmp (argv[i], "-w")) + writeable_text = 1; + else if (!strcmp (argv[i], "-v")) + quiet = 0; + else if (!strcmp (argv[i], "-nc")) + inhibit_confirm = 1; + else if (argv[i][0] == '-') + /* Other options take arguments, so don't confuse an + argument with an option. */ + i++; + } + + /* Run the init function of each source file */ + + initialize_cmd_lists (); /* This needs to be done first */ + initialize_all_files (); + initialize_main (); /* But that omits this file! Do it now */ + initialize_signals (); + + if (!quiet) + print_gdb_version (); + + /* Process the command line arguments. */ + + count = 0; + for (i = 1; i < argc; i++) + { + extern void exec_file_command (), symbol_file_command (); + extern void core_file_command (); + register char *arg = argv[i]; + /* Args starting with - say what to do with the following arg + as a filename. */ + if (arg[0] == '-') + { + extern void tty_command (), directory_command (); + + if (!strcmp (arg, "-q") || !strcmp (arg, "-nx") + || !strcmp (arg, "-quiet") || !strcmp (arg, "-batch") + || !strcmp (arg, "-fullname") || !strcmp (arg, "-nw") + || !strcmp (arg, "-xgdb_verbose") + || !strcmp (arg, "-help") + || !strcmp (arg, "-k") + || !strcmp (arg, "-w") + || !strcmp (arg, "-v") + || !strcmp (arg, "-nc")) + /* Already processed above */ + continue; + + if (++i == argc) + fprintf (stderr, "No argument follows \"%s\".\n", arg); + if (!setjmp (to_top_level)) + { + /* -s foo: get syms from foo. -e foo: execute foo. + -se foo: do both with foo. -c foo: use foo as core dump. */ + if (!strcmp (arg, "-se")) + { + exec_file_command (argv[i], !batch); + symbol_file_command (argv[i], !batch); + } + else if (!strcmp (arg, "-s") || !strcmp (arg, "-symbols")) + symbol_file_command (argv[i], !batch); + else if (!strcmp (arg, "-e") || !strcmp (arg, "-exec")) + exec_file_command (argv[i], !batch); + else if (!strcmp (arg, "-c") || !strcmp (arg, "-core")) + core_file_command (argv[i], !batch); + /* -x foo: execute commands from foo. */ + else if (!strcmp (arg, "-x") || !strcmp (arg, "-command") + || !strcmp (arg, "-commands")) + source_command (argv[i]); + /* -d foo: add directory `foo' to source-file directory + search-list */ + else if (!strcmp (arg, "-d") || !strcmp (arg, "-dir") + || !strcmp (arg, "-directory")) + directory_command (argv[i], 0); + /* -cd FOO: specify current directory as FOO. + GDB remembers the precise string FOO as the dirname. */ + else if (!strcmp (arg, "-cd")) + { + cd_command (argv[i], 0); + init_source_path (); + } + /* -t /def/ttyp1: use /dev/ttyp1 for inferior I/O. */ + else if (!strcmp (arg, "-t") || !strcmp (arg, "-tty")) + tty_command (argv[i], 0); + + else + error ("Unknown command-line switch: \"%s\"\n", arg); + } + } + else + { + /* Args not thus accounted for + are treated as, first, the symbol/executable file + and, second, the core dump file. */ + count++; + if (!setjmp (to_top_level)) + switch (count) + { + case 1: + exec_file_command (arg, !batch); + symbol_file_command (arg, !batch); + break; + + case 2: + core_file_command (arg, !batch); + break; + + case 3: + fprintf (stderr, "Excess command line args ignored. (%s%s)\n", + arg, (i == argc - 1) ? "" : " ..."); + } + } + } + + if (!inhibit_gdbinit) + source_init_files(); + + if (batch) + { +#if 0 + fatal ("Attempt to read commands from stdin in batch mode."); +#endif + /* We have hit the end of the batch file. */ + exit (0); + } + + if (!quiet) + printf ("Type \"help\" for a list of commands.\n"); + + /* The command loop. */ + + while (1) + { + if (!setjmp (to_top_level)) + command_loop (); + if (ISATTY(stdin)) + clearerr (stdin); /* Don't get hung if C-d is typed. */ + else if (feof(instream)) /* Avoid endless loops for redirected stdin */ + break; + } + exit (0); +} + + +static void +do_nothing () +{ +} + +/* Read commands from `instream' and execute them + until end of file. */ +void +command_loop () +{ + struct cleanup *old_chain; + register int toplevel = (instream == stdin); + register int interactive = (toplevel && ISATTY(stdin)); + + while (!feof (instream)) + { + register char *cmd_line; + + quit_flag = 0; + if (interactive) + reinitialize_more_filter (); + old_chain = make_cleanup (do_nothing, 0); + cmd_line = command_line_input (prompt, toplevel); + execute_command (cmd_line, toplevel); + /* Do any commands attached to breakpoint we stopped at. */ + do_breakpoint_commands (); + do_cleanups (old_chain); + } +} + +/* Commands call this if they do not want to be repeated by null lines. */ + +void +dont_repeat () +{ + /* If we aren't reading from standard input, we are saving the last + thing read from stdin in line and don't want to delete it. Null lines + won't repeat here in any case. */ + if (instream == stdin) + *line = 0; +} + +/* Read a line from the stream "instream" without command line editing. + + It prints PROMPT once at the start. + Action is compatible with "readline" (i.e., space for typing is + malloced & should be freed by caller). */ +char * +gdb_readline (prompt) + char *prompt; +{ + int c; + char *result; + int input_index = 0; + int result_size = 80; + + if (prompt) + { + printf (prompt); + fflush (stdout); + } + + result = (char *) xmalloc (result_size); + + while (1) + { + c = fgetc (instream ? instream : stdin); + if (c == EOF) + { + free(result); + return ((char *)0); + } + if (c == '\n') + break; + + result[input_index++] = c; + if (input_index >= result_size) + { + result_size <= 1; + result = (char *)xrealloc(result, result_size); + } + } + result[input_index++] = '\0'; + return result; +} + +/* Declaration for fancy readline with command line editing. */ +char *readline (); + +/* Variables which control command line editing and history + substitution. These variables are given default values at the end + of this file. */ +static int command_editing_p; +static int history_expansion_p; +static int write_history_p; +static int history_size; +static char *history_filename; + +/* Variables which are necessary for fancy command line editing. */ +char *gdb_completer_word_break_characters = + " \t\n!@#$%^&*()-+=|~`}{[]\"';:?/>.<,"; + +/* Functions that are used as part of the fancy command line editing. */ + +/* Generate symbol names one by one for the completer. If STATE is + zero, then we need to initialize, otherwise the initialization has + already taken place. TEXT is what we expect the symbol to start + with. RL_LINE_BUFFER is available to be looked at; it contains the + entire text of the line. RL_POINT is the offset in that line of + the cursor. You should pretend that the line ends at RL_POINT. */ +char * +symbol_completion_function (text, state) + char *text; + int state; +{ + char **make_symbol_completion_list (); + static char **list = (char **)NULL; + static int index; + char *output; + extern char *rl_line_buffer; + extern int rl_point; + char *tmp_command, *p; + struct cmd_list_element *c, *result_list; + + if (!state) + { + /* Free the storage used by LIST, but not by the strings inside. This is + because rl_complete_internal () frees the strings. */ + if (list) + free (list); + list = 0; + index = 0; + + /* Decide whether to complete on a list of gdb commands or on + symbols. */ + tmp_command = (char *) alloca (rl_point + 1); + p = tmp_command; + + strncpy (tmp_command, rl_line_buffer, rl_point); + tmp_command[rl_point] = '\0'; + + if (rl_point == 0) + { + /* An empty line we want to consider ambiguous; that is, + it could be any command. */ + c = (struct cmd_list_element *) -1; + result_list = 0; + } + else + c = lookup_cmd_1 (&p, cmdlist, &result_list, 1); + + /* Move p up to the next interesting thing. */ + while (*p == ' ' || *p == '\t') + p++; + + if (!c) + /* He's typed something unrecognizable. Sigh. */ + list = (char **) 0; + else if (c == (struct cmd_list_element *) -1) + { + if (p + strlen(text) != tmp_command + rl_point) + error ("Unrecognized command."); + + /* He's typed something ambiguous. This is easier. */ + if (result_list) + list = complete_on_cmdlist (*result_list->prefixlist, text); + else + list = complete_on_cmdlist (cmdlist, text); + } + else + { + /* If we've gotten this far, gdb has recognized a full + command. There are several possibilities: + + 1) We need to complete on the command. + 2) We need to complete on the possibilities coming after + the command. + 2) We need to complete the text of what comes after the + command. */ + + if (!*p && *text) + /* Always (might be longer versions of thie command). */ + list = complete_on_cmdlist (result_list, text); + else if (!*p && !*text) + { + if (c->prefixlist) + list = complete_on_cmdlist (*c->prefixlist, ""); + else + list = make_symbol_completion_list (""); + } + else + { + if (c->prefixlist && !c->allow_unknown) + { + *p = '\0'; + error ("\"%s\" command requires a subcommand.", + tmp_command); + } + else + list = make_symbol_completion_list (text); + } + } + } + + /* If the debugged program wasn't compiled with symbols, or if we're + clearly completing on a command and no command matches, return + NULL. */ + if (!list) + return ((char *)NULL); + + output = list[index]; + if (output) + index++; + + return (output); +} + + +void +print_prompt () +{ + if (prompt) + { + printf ("%s", prompt); + fflush (stdout); + } +} + + +#ifdef HAVE_TERMIO +#include +static struct termio norm_tty; + +static void +suspend_sig() +{ + int tty = fileno(stdin); + struct termio cur_tty; + + ioctl(tty, TCGETA, &cur_tty); + ioctl(tty, TCSETAW, &norm_tty); + + (void) sigsetmask(0); + signal(SIGTSTP, SIG_DFL); + kill(0, SIGTSTP); + + /* + * we've just been resumed -- current tty params become new + * 'normal' params (in case tset/stty was done while we were + * suspended). Merge values that readline might have changed + * into new params, then restore term mode. + */ + ioctl(tty, TCGETA, &norm_tty); + cur_tty.c_lflag = (cur_tty.c_lflag & (ICANON|ECHO|ISIG)) | + (norm_tty.c_lflag &~ (ICANON|ECHO|ISIG)); + cur_tty.c_iflag = (cur_tty.c_iflag & (IXON|ISTRIP|INPCK)) | + (norm_tty.c_iflag &~ (IXON|ISTRIP|INPCK)); + ioctl(tty, TCSETAW, &cur_tty); + + signal(SIGTSTP, suspend_sig); + print_prompt(); + + /* + * Forget about any previous command -- null line now will do + * nothing. + */ + dont_repeat(); +} + +#else + +#include +#include +#include + +static struct sgttyb norm_tty; +static struct tchars norm_tchars; +static struct ltchars norm_ltchars; +static int norm_lflags; + +#ifdef PASS8 +#define RL_TFLAGS (RAW|CRMOD|ECHO|CBREAK|PASS8) +#else +#define RL_TFLAGS (RAW|CRMOD|ECHO|CBREAK) +#endif + +static void +suspend_sig() +{ + int tty = fileno(stdin); + struct sgttyb cur_tty; + struct tchars cur_tchars; + struct ltchars cur_ltchars; + int cur_lflags; + int cur_flags; + + ioctl(tty, TIOCGETP, &cur_tty); + ioctl(tty, TIOCGETC, &cur_tchars); + ioctl(tty, TIOCLGET, &cur_lflags); + ioctl(tty, TIOCGLTC, &cur_ltchars); + + ioctl(tty, TIOCSETP, &norm_tty); + ioctl(tty, TIOCSETC, &norm_tchars); + ioctl(tty, TIOCLSET, &norm_lflags); + ioctl(tty, TIOCSLTC, &norm_ltchars); + + (void) sigsetmask(0); + signal(SIGTSTP, SIG_DFL); + kill(0, SIGTSTP); + + /* + * we've just been resumed -- current tty params become new + * 'normal' params (in case tset/stty was done while we were + * suspended). Merge values that readline might have changed + * into new params, then restore term mode. + */ + ioctl(tty, TIOCGETP, &norm_tty); + cur_flags = cur_tty.sg_flags; + cur_tty = norm_tty; + cur_tty.sg_flags = (cur_tty.sg_flags &~ RL_TFLAGS) + | (cur_flags & RL_TFLAGS); + + ioctl(tty, TIOCLGET, &norm_lflags); +#ifdef LPASS8 + cur_lflags = (cur_lflags &~ LPASS8) | (cur_flags & LPASS8); +#endif + ioctl(tty, TIOCGETC, &norm_tchars); + ioctl(tty, TIOCGLTC, &norm_ltchars); + + ioctl(tty, TIOCSETP, &cur_tty); + ioctl(tty, TIOCSETC, &cur_tchars); + ioctl(tty, TIOCLSET, &cur_lflags); + ioctl(tty, TIOCSLTC, &cur_ltchars); + + signal(SIGTSTP, suspend_sig); + print_prompt(); + + /* + * Forget about any previous command -- null line now will do + * nothing. + */ + dont_repeat(); +} +#endif /* HAVE_TERMIO */ + +/* Initialize signal handlers. */ +initialize_signals () +{ + extern void request_quit (); + int tty = fileno(stdin); + + signal (SIGINT, request_quit); + + /* If we initialize SIGQUIT to SIG_IGN, then the SIG_IGN will get + passed to the inferior, which we don't want. It would be + possible to do a "signal (SIGQUIT, SIG_DFL)" after we fork, but + on BSD4.3 systems using vfork, that will (apparently) affect the + GDB process as well as the inferior (the signal handling tables + being shared between the two, apparently). Since we establish + a handler for SIGQUIT, when we call exec it will set the signal + to SIG_DFL for us. */ + signal (SIGQUIT, do_nothing); + if (signal (SIGHUP, do_nothing) != SIG_IGN) + signal (SIGHUP, disconnect); + signal (SIGFPE, float_handler); + + ioctl(tty, TIOCGETP, &norm_tty); + ioctl(tty, TIOCLGET, &norm_lflags); + ioctl(tty, TIOCGETC, &norm_tchars); + ioctl(tty, TIOCGLTC, &norm_ltchars); + signal(SIGTSTP, suspend_sig); +} + +char * +finish_command_input(inputline, repeat, interactive) + register char *inputline; + int repeat; + int interactive; +{ + static char *do_free; + + if (do_free) { + free(do_free); + do_free = NULL; + } + + /* Do history expansion if that is wished. */ + if (interactive && history_expansion_p) { + int expanded; + + expanded = history_expand(inputline, &do_free); + if (expanded) { + /* Print the changes. */ + puts(do_free); + + /* An error acts like no input. */ + if (expanded < 0) { + *do_free = 0; + return (do_free); + } + } + inputline = do_free; + } + /* get rid of any leading whitespace */ + while (isspace(*inputline)) + ++inputline; + /* + * If we just got an empty line, and that is supposed to repeat the + * previous command, return the value in the global buffer. + */ + if (*inputline == 0) { + if (repeat) + return (line); + } else if (interactive) + add_history(inputline); + + /* + * If line is a comment, clear it out. + * Note: comments are added to the command history. This is useful + * when you type a command, and then realize you don't want to + * execute it quite yet. You can comment out the command and then + * later fetch it from the value history and remove the '#'. + */ + if (*inputline == '#') + *inputline = 0; + else if (repeat) { + /* Save into global buffer. */ + register int i = strlen(inputline) + 1; + + if (i > linesize) { + line = xrealloc(line, i); + linesize = i; + } + strcpy(line, inputline); + } + return (inputline); +} + +static char * +get_a_cmd_line(prompt, interactive) + char *prompt; + int interactive; +{ + register char *cp; + + /* Control-C quits instantly if typed while reading input. */ + immediate_quit++; + if (interactive && command_editing_p) { + extern void (*rl_event_hook)(); + + rl_event_hook = window_hook; + cp = readline(prompt); + } else { + if (interactive) { + if (window_hook) { + print_prompt(); + (*window_hook)(); + } + } else + prompt = NULL; + cp = gdb_readline(prompt); + } + --immediate_quit; + return (cp); +} + +/* Read one line from the command input stream `instream' + Returns the address of the start of the line. + + *If* the instream == stdin & stdin is a terminal, the line read + is copied into the file line saver (global var char *line, + length linesize) so that it can be duplicated. + + This routine either uses fancy command line editing or + simple input as the user has requested. */ + +char * +command_line_input(prompt, repeat) + char *prompt; + int repeat; +{ + static char *do_free; + register int interactive = (instream == stdin && ISATTY(instream)); + register char *cp; + register int i; + + if (do_free) { + free(do_free); + do_free = NULL; + } + cp = get_a_cmd_line(prompt, interactive); + + /* + * handle continued lines (this loop is not particularly + * efficient because it's rare). + */ + while (cp && cp[i = strlen(cp) - 1] == '\\') { + register char *np = get_a_cmd_line(prompt, interactive); + register int j; + + if (np == NULL) { + cp[i] = 0; + break; + } + j = strlen(np); + cp = xrealloc(cp, i + j + 1); + strcpy(cp + i, np); + free(np); + } + if (cp == NULL) + return (""); + do_free = cp; + return (finish_command_input(cp, repeat, interactive)); +} + + +#define MAX_USER_ARGS 32 + +static struct user_args { + struct { + char *arg; + int len; + } a[10]; +} uargs[MAX_USER_ARGS]; + +static struct user_args *user_arg = uargs; + +static void +arg_cleanup(ap) + struct user_args *ap; +{ + user_arg = ap; +} + +/* Bind arguments $arg0, $arg1, ..., for a user defined command. */ +struct cleanup * +setup_user_args(p) + char *p; +{ + register int i; + struct cleanup *old_chain = make_cleanup(arg_cleanup, user_arg); + + if (++user_arg >= &uargs[MAX_USER_ARGS]) + error("user defined functions nested too deeply\n"); + + bzero(user_arg, sizeof(*user_arg)); + + i = 0; + while (*p) { + while (isspace(*p)) + ++p; + user_arg->a[i].arg = p; + while (*p && ! isspace(*p)) + ++p; + user_arg->a[i].len = p - user_arg->a[i].arg; + ++i; + } + return (old_chain); +} + +static char * +findarg(str) + register char *str; +{ + register char *cp = str; + extern char *index(); + + while (cp = index(cp, '$')) { + if (strncmp(cp, "$arg", 4) == 0 && isdigit(cp[4])) + return (cp); + ++cp; + } + return (char *)0; +} + +/* expand arguments from "line" into "new" */ +static void +expand_args(line, new) + register char *line, *new; +{ + register char *cp = findarg(line); + + while (cp = findarg(line)) { + int i, len; + + bcopy(line, new, cp - line); + new += cp - line; + i = cp[4] - '0'; + if (len = user_arg->a[i].len) { + bcopy(user_arg->a[i].arg, new, len); + new += len; + } + line = cp + 5; + } + strcpy(new, line); +} + +/* expand any arguments in "line" then execute the result */ +static void +expand_and_execute(line, from_tty) + char *line; + int from_tty; +{ + void execute_command(); + char new[1024]; + + if (! findarg(line)) { + execute_command(line, from_tty); + return; + } + expand_args(line, new); + execute_command(new, from_tty); +} + +char * +read_one_command_line(prompt, from_tty) + char *prompt; +{ + register char *p, *p1; + + dont_repeat(); + p = command_line_input(prompt, from_tty); + + /* Remove trailing blanks. */ + p1 = p + strlen(p); + while (--p1 > p && (*p1 == ' ' || *p1 == '\t')) + ; + *++p1 = 0; + return (p); +} + +static char cmd_prompt[] = " > "; + +int +parse_control_structure(rootcmd, from_tty, level) + struct command_line *rootcmd; + int from_tty; +{ + struct command_line *cmd = (struct command_line *)xmalloc(sizeof(*cmd)); + char *prompt; + + ++level; + prompt = from_tty? &cmd_prompt[sizeof(cmd_prompt) - 1 - 2*level] : + (char *)0; + bzero(cmd, sizeof(*cmd)); + rootcmd->body = cmd; + while (1) { + char *p = read_one_command_line(prompt, from_tty); + + p = savestring(p, strlen(p)); + cmd->line = p; + if (!strncmp(p, "while ", 6)) { + cmd->type = CL_WHILE; + if (parse_control_structure(cmd, from_tty, level)) + return (1); + } else if (!strncmp(p, "if ", 3)) { + cmd->type = CL_IF; + if (parse_control_structure(cmd, from_tty, level)) { + struct command_line *tmp; + int stat; + + cmd->elsebody = cmd->body; + stat = parse_control_structure(cmd, from_tty, + level); + tmp = cmd->elsebody; + cmd->elsebody = cmd->body; + cmd->body = tmp; + if (stat) + return (1); + } + } else if (!strcmp(p, "else")) { + cmd->type = CL_END; + return (1); + } else if (!strcmp(p, "end")) { + cmd->type = CL_END; + return (0); + } else if (!strcmp(p, "exitloop")) { + cmd->type = CL_EXITLOOP; + } else { + cmd->type = CL_NORMAL; + } + cmd->next = (struct command_line *)xmalloc(sizeof(*cmd)); + cmd = cmd->next; + bzero(cmd, sizeof(*cmd)); + } + /* NOTREACHED */ +} + +int +execute_control_structure(cmd) + register struct command_line *cmd; +{ + char expn[1024]; + struct expression *cond; + int stat; + + while (cmd) { + QUIT; + switch (cmd->type) { + case CL_END: + return (0); + case CL_NORMAL: + expand_and_execute(cmd->line, 0); + break; + case CL_WHILE: + expand_args(cmd->line + 6, expn); + cond = parse_c_expression(expn); + while (breakpoint_cond_eval(cond) == 0) + if (execute_control_structure(cmd->body)) + break; + free(cond); + break; + case CL_IF: + expand_args(cmd->line + 3, expn); + cond = parse_c_expression(expn); + stat = breakpoint_cond_eval(cond); + free(cond); + if (stat == 0) { + if (execute_control_structure(cmd->body)) + return (1); + } else if (cmd->elsebody) { + if (execute_control_structure(cmd->elsebody)) + return (1); + } + break; + case CL_EXITLOOP: + return (1); + } + cmd = cmd->next; + } + free_all_values(); +} + +execute_command_lines(cmd) + struct command_line *cmd; +{ + struct cleanup *old_chain = make_cleanup(source_cleanup, instream); + + /* + * Set the instream to 0, indicating execution of a user-defined + * function. + */ + ++immediate_quit; + instream = (FILE *) 0; + (void)execute_control_structure(cmd); + --immediate_quit; + do_cleanups(old_chain); +} + +/* do following command lines if expression true */ +if_command(p, from_tty) + char *p; + int from_tty; +{ + struct cleanup *old_chain; + struct command_line *cmd = (struct command_line *)xmalloc(sizeof(*cmd)); + char buf[128]; + + sprintf(buf, "if %s", p); + + bzero(cmd, sizeof(*cmd)); + old_chain = make_cleanup(free_command_lines, cmd); + cmd->type = CL_IF; + cmd->line = savestring(buf, strlen(buf)); + /* XXX cmd->line? */ + if (parse_control_structure(cmd, from_tty, 0)) { + struct command_line *tmp; + + cmd->elsebody = cmd->body; + (void) parse_control_structure(cmd, from_tty, 0); + tmp = cmd->elsebody; + cmd->elsebody = cmd->body; + cmd->body = tmp; + } + (void) execute_command_lines(cmd); + do_cleanups(old_chain); +} + +/* do following command lines while expression true */ +while_command(p, from_tty) + char *p; + int from_tty; +{ + struct cleanup *old_chain; + struct command_line *cmd = (struct command_line *)xmalloc(sizeof(*cmd)); + char buf[128]; + + sprintf(buf, "while %s", p); + + bzero(cmd, sizeof(*cmd)); + old_chain = make_cleanup(free_command_lines, cmd); + cmd->type = CL_WHILE; + cmd->line = savestring(buf, strlen(buf)); + (void)parse_control_structure(cmd, from_tty, 0); + (void)execute_command_lines(cmd); + do_cleanups(old_chain); +} + +/* + * Execute the line P as a command. + * Pass FROM_TTY as second argument to the defining function. + */ +void +execute_command (p, from_tty) + char *p; + int from_tty; +{ + register struct cmd_list_element *c; + register struct command_line *cmdlines; + + free_all_values(); + if (*p) { + c = lookup_cmd(&p, cmdlist, "", 0, 1); + if (c->function == 0) + error("That is not a command, just a help topic."); + else if (c->class == (int) class_user) { + struct cleanup *old_chain = setup_user_args(p); + + cmdlines = (struct command_line *) c->function; + if (cmdlines) + (void)execute_command_lines(cmdlines); + + do_cleanups(old_chain); + } else + /* Pass null arg rather than an empty one. */ + (*c->function) (*p ? p : 0, from_tty); + } +} + +/* + * Read lines from the input stream and accumulate them in a chain of struct + * command_line's which is then returned. + */ +struct command_line * +read_command_lines(from_tty) + int from_tty; +{ + struct cleanup *old_chain; + struct command_line *cmd = (struct command_line *)xmalloc(sizeof(*cmd)); + struct command_line *next; + + bzero(cmd, sizeof(*cmd)); + old_chain = make_cleanup(free_command_lines, cmd); + cmd->type = CL_NOP; + (void)parse_control_structure(cmd, from_tty, 0); + dont_repeat(); + discard_cleanups(old_chain); + next = cmd->body; + free(cmd); + return (next); +} + +/* Free a chain of struct command_line's. */ + +void +free_command_lines(cmds) + struct command_line *cmds; +{ + struct command_line *next; + + while (cmds) { + if (cmds->body) + free(cmds->body); + if (cmds->elsebody) + free(cmds->elsebody); + if (cmds->line) + free(cmds->line); + next = cmds->next; + free(cmds); + cmds = next; + } +} + +/* Add an element to the list of info subcommands. */ + +void +add_info (name, fun, doc) + char *name; + void (*fun) (); + char *doc; +{ + add_cmd (name, no_class, fun, doc, &infolist); +} + +/* Add an alias to the list of info subcommands. */ + +void +add_info_alias (name, oldname, abbrev_flag) + char *name; + char *oldname; + int abbrev_flag; +{ + add_alias_cmd (name, oldname, 0, abbrev_flag, &infolist); +} + +/* The "info" command is defined as a prefix, with allow_unknown = 0. + Therefore, its own definition is called only for "info" with no args. */ + +static void +info_command () +{ + printf ("\"info\" must be followed by the name of an info command.\n"); + help_list (infolist, "info ", -1, stdout); +} + +/* Add an element to the list of commands. */ + +void +add_com (name, class, fun, doc) + char *name; + int class; + void (*fun) (); + char *doc; +{ + add_cmd (name, class, fun, doc, &cmdlist); +} + +/* Add an alias or abbreviation command to the list of commands. */ + +void +add_com_alias (name, oldname, class, abbrev_flag) + char *name; + char *oldname; + int class; + int abbrev_flag; +{ + add_alias_cmd (name, oldname, class, abbrev_flag, &cmdlist); +} + +void +error_no_arg (why) + char *why; +{ + error ("Argument required (%s).", why); +} + +static void +help_command (command, from_tty) + char *command; + int from_tty; /* Ignored */ +{ + help_cmd (command, stdout); +} + +static void +validate_comname (comname) + char *comname; +{ + register char *p; + + if (comname == 0) + error_no_arg ("name of command to define"); + + p = comname; + while (*p) + { + if (!(*p >= 'A' && *p <= 'Z') + && !(*p >= 'a' && *p <= 'z') + && !(*p >= '0' && *p <= '9') + && *p != '-') + error ("Junk in argument list: \"%s\"", p); + p++; + } +} + +static void +define_command (comname, from_tty) + char *comname; + int from_tty; +{ + register struct command_line *cmds; + register struct cmd_list_element *c; + char *tem = comname; + + validate_comname (comname); + + c = lookup_cmd (&tem, cmdlist, "", -1, 1); + if (c) + { + if (c->class == (int) class_user || c->class == (int) class_alias) + tem = "Redefine command \"%s\"? "; + else + tem = "Really redefine built-in command \"%s\"? "; + if (!query (tem, comname)) + error ("Command \"%s\" not redefined.", comname); + } + + if (from_tty) + { + printf ("Type commands for definition of \"%s\".\n\ +End with a line saying just \"end\".\n", comname); + fflush (stdout); + } + comname = savestring (comname, strlen (comname)); + + cmds = read_command_lines (from_tty); + + if (c && c->class == (int) class_user) + free_command_lines (c->function); + + add_com (comname, class_user, cmds, + (c && c->class == (int) class_user) + ? c->doc : savestring ("User-defined.", 13)); +} + +static void +document_command (comname, from_tty) + char *comname; + int from_tty; +{ + register struct cmd_list_element *c; + register char *p; + register char *cp; + register char *doc = 0; + register int len; + char *tmp = comname; + + validate_comname (comname); + c = lookup_cmd (&tmp, cmdlist, "", 0, 1); + if (c->class != (int) class_user) + error ("Command \"%s\" is built-in.", comname); + + if (from_tty) + printf ("Type documentation for \"%s\". \ +End with a line saying just \"end\".\n", comname); + + while (p = read_one_command_line(from_tty? "> " : 0, from_tty)) + { + if (strcmp(p, "end") == 0) + break; + len = strlen(p) + 1; + if (! doc) + { + doc = xmalloc(len); + cp = doc; + } + else + { + int i = cp - doc; + doc = xrealloc(doc, i + len); + cp = doc + i; + } + strcpy(cp, p); + cp += len; + cp[-1] = '\n'; + } + if (doc && cp > doc) + cp[-1] = 0; + if (c->doc) + free (c->doc); + c->doc = doc; +} + +static void +print_gdb_version () +{ + printf ("GDB %s, Copyright (C) 1989 Free Software Foundation, Inc.\n\ +There is ABSOLUTELY NO WARRANTY for GDB; type \"info warranty\" for details.\n\ +GDB is free software and you are welcome to distribute copies of it\n\ + under certain conditions; type \"info copying\" to see the conditions.\n", + version); +} + +static void +version_info () +{ + immediate_quit++; + print_gdb_version (); + immediate_quit--; +} + + +/* Command to specify a prompt string instead of "(gdb) ". */ + +void +set_prompt_command (text) + char *text; +{ + char *p, *q; + register int c; + char *new; + + if (text == 0) + error_no_arg ("string to which to set prompt"); + + new = (char *) xmalloc (strlen (text) + 2); + p = text; q = new; + while (c = *p++) + { + if (c == '\\') + { + /* \ at end of argument is used after spaces + so they won't be lost. */ + if (*p == 0) + break; + c = parse_escape (&p); + if (c == 0) + break; /* C loses */ + else if (c > 0) + *q++ = c; + } + else + *q++ = c; + } + if (*(p - 1) != '\\') + *q++ = ' '; + *q++ = '\0'; + new = (char *) xrealloc (new, q - new); + free (prompt); + prompt = new; +} + +static void +quit_command () +{ + extern void exec_file_command (); + if (have_inferior_p ()) + { + if (inhibit_confirm || query ("The program is running. Quit anyway? ")) + { + /* Prevent any warning message from reopen_exec_file, in case + we have a core file that's inconsistent with the exec file. */ + exec_file_command (0, 0); + kill_inferior (); + } + else + error ("Not confirmed."); + } + /* Save the history information if it is appropriate to do so. */ + if (write_history_p && history_filename) + write_history (history_filename); + exit (0); +} + +int +input_from_terminal_p () +{ + return instream == stdin; +} + +static void +pwd_command (arg, from_tty) + char *arg; + int from_tty; +{ + if (arg) error ("The \"pwd\" command does not take an argument: %s", arg); + getwd (dirbuf); + + if (strcmp (dirbuf, current_directory)) + printf ("Working directory %s\n (canonically %s).\n", + current_directory, dirbuf); + else + printf ("Working directory %s.\n", current_directory); +} + +static void +cd_command (dir, from_tty) + char *dir; + int from_tty; +{ + int len; + int change; + + if (dir == 0) + error_no_arg ("new working directory"); + + dir = tilde_expand (dir); + make_cleanup (free, dir); + + len = strlen (dir); + dir = savestring (dir, len - (len > 1 && dir[len-1] == '/')); + if (dir[0] == '/') + current_directory = dir; + else + { + current_directory = concat (current_directory, "/", dir); + free (dir); + } + + /* Now simplify any occurrences of `.' and `..' in the pathname. */ + + change = 1; + while (change) + { + char *p; + change = 0; + + for (p = current_directory; *p;) + { + if (!strncmp (p, "/./", 2) + && (p[2] == 0 || p[2] == '/')) + strcpy (p, p + 2); + else if (!strncmp (p, "/..", 3) + && (p[3] == 0 || p[3] == '/') + && p != current_directory) + { + char *q = p; + while (q != current_directory && q[-1] != '/') q--; + if (q != current_directory) + { + strcpy (q-1, p+3); + p = q-1; + } + } + else p++; + } + } + + if (chdir (dir) < 0) + perror_with_name (dir); + + if (from_tty) + pwd_command ((char *) 0, 1); +} + +static void +source_command (arg, from_tty) + char *arg; + int from_tty; +{ + FILE *stream; + struct cleanup *cleanups; + char *file = arg; + char *path; + + if (file == 0) + /* Let source without arguments read .gdbinit. */ + file = ".gdbinit"; + + file = tilde_expand (file); + make_cleanup (free, file); + +#ifdef KERNELDEBUG + if (path = getenv(kernel_debugging? "KGDBPATH" : "GDBPATH")) +#else + if (path = getenv("GDBPATH")) +#endif + { + int fd = openp(path, 1, file, O_RDONLY, 0, 0); + + if (fd == -1) + stream = 0; + else + stream = fdopen(fd, "r"); + } + else + stream = fopen (file, "r"); + + if (stream == 0) + perror_with_name (file); + + cleanups = make_cleanup (source_cleanup, instream); + + instream = stream; + + command_loop (); + + do_cleanups (cleanups); +} + +static void +echo_command (text) + char *text; +{ + char *p = text; + register int c; + + if (text) + while (c = *p++) + { + if (c == '\\') + { + /* \ at end of argument is used after spaces + so they won't be lost. */ + if (*p == 0) + return; + + c = parse_escape (&p); + if (c >= 0) + fputc (c, stdout); + } + else + fputc (c, stdout); + } + fflush(stdout); +} + +static void +dump_me_command () +{ + if (query ("Should GDB dump core? ")) + { + signal (SIGQUIT, SIG_DFL); + kill (getpid (), SIGQUIT); + } +} + +int +parse_binary_operation (caller, arg) + char *caller, *arg; +{ + int length; + + if (!arg || !*arg) + return 1; + + length = strlen (arg); + + while (arg[length - 1] == ' ' || arg[length - 1] == '\t') + length--; + + if (!strncmp (arg, "on", length) + || !strncmp (arg, "1", length) + || !strncmp (arg, "yes", length)) + return 1; + else + if (!strncmp (arg, "off", length) + || !strncmp (arg, "0", length) + || !strncmp (arg, "no", length)) + return 0; + else + error ("\"%s\" not given a binary valued argument.", caller); +} + +/* Functions to manipulate command line editing control variables. */ + +static void +set_editing (arg, from_tty) + char *arg; + int from_tty; +{ + command_editing_p = parse_binary_operation ("set command-editing", arg); +} + +/* Number of commands to print in each call to editing_info. */ +#define Hist_print 10 +static void +editing_info (arg, from_tty) + char *arg; + int from_tty; +{ + /* Index for history commands. Relative to history_base. */ + int offset; + + /* Number of the history entry which we are planning to display next. + Relative to history_base. */ + static int num = 0; + + /* The first command in the history which doesn't exist (i.e. one more + than the number of the last command). Relative to history_base. */ + int hist_len; + + struct _hist_entry { + char *line; + char *data; + } *history_get(); + extern int history_base; + + printf_filtered ("Interactive command editing is %s.\n", + command_editing_p ? "on" : "off"); + + printf_filtered ("History expansion of command input is %s.\n", + history_expansion_p ? "on" : "off"); + printf_filtered ("Writing of a history record upon exit is %s.\n", + write_history_p ? "enabled" : "disabled"); + printf_filtered ("The size of the history list (number of stored commands) is %d.\n", + history_size); + printf_filtered ("The name of the history record is \"%s\".\n\n", + history_filename ? history_filename : ""); + + /* Print out some of the commands from the command history. */ + /* First determine the length of the history list. */ + hist_len = history_size; + for (offset = 0; offset < history_size; offset++) + { + if (!history_get (history_base + offset)) + { + hist_len = offset; + break; + } + } + + if (arg) + { + if (arg[0] == '+' && arg[1] == '\0') + /* "info editing +" should print from the stored position. */ + ; + else + /* "info editing " should print around command number . */ + num = (parse_and_eval_address (arg) - history_base) - Hist_print / 2; + } + /* "info editing" means print the last Hist_print commands. */ + else + { + num = hist_len - Hist_print; + } + + if (num < 0) + num = 0; + + /* If there are at least Hist_print commands, we want to display the last + Hist_print rather than, say, the last 6. */ + if (hist_len - num < Hist_print) + { + num = hist_len - Hist_print; + if (num < 0) + num = 0; + } + + if (num == hist_len - Hist_print) + printf_filtered ("The list of the last %d commands is:\n\n", Hist_print); + else + printf_filtered ("Some of the stored commands are:\n\n"); + + for (offset = num; offset < num + Hist_print && offset < hist_len; offset++) + { + printf_filtered ("%5d %s\n", history_base + offset, + (history_get (history_base + offset))->line); + } + + /* The next command we want to display is the next one that we haven't + displayed yet. */ + num += Hist_print; + + /* If the user repeats this command with return, it should do what + "info editing +" does. This is unnecessary if arg is null, + because "info editing +" is not useful after "info editing". */ + if (from_tty && arg) + { + arg[0] = '+'; + arg[1] = '\0'; + } +} + +static void +set_history_expansion (arg, from_tty) + char *arg; + int from_tty; +{ + history_expansion_p = parse_binary_operation ("set history expansion", arg); +} + +static void +set_history_write (arg, from_tty) + char *arg; + int from_tty; +{ + write_history_p = parse_binary_operation ("set history write", arg); +} + +static void +set_history (arg, from_tty) + char *arg; + int from_tty; +{ + printf ("\"set history\" must be followed by the name of a history subcommand.\n"); + help_list (sethistlist, "set history ", -1, stdout); +} + +static void +set_history_size (arg, from_tty) + char *arg; + int from_tty; +{ + if (!*arg) + error_no_arg ("set history size"); + + history_size = atoi (arg); +} + +static void +set_history_filename (arg, from_tty) + char *arg; + int from_tty; +{ + int i; + + if (!arg) + error_no_arg ("history file name"); + + arg = tilde_expand (arg); + make_cleanup (free, arg); + + i = strlen (arg) - 1; + + free (history_filename); + + while (i > 0 && (arg[i] == ' ' || arg[i] == '\t')) + i--; + ++i; + + if (!*arg) + history_filename = (char *) 0; + else + history_filename = savestring (arg, i + 1); + history_filename[i] = '\0'; +} + +int info_verbose; + +static void +set_verbose_command (arg, from_tty) + char *arg; + int from_tty; +{ + info_verbose = parse_binary_operation ("set verbose", arg); +} + +static void +verbose_info (arg, from_tty) + char *arg; + int from_tty; +{ + if (arg) + error ("\"info verbose\" does not take any arguments.\n"); + + printf ("Verbose printing of information is %s.\n", + info_verbose ? "on" : "off"); +} + +static void +float_handler () +{ + error ("Invalid floating value encountered or computed."); +} + + +static void +initialize_cmd_lists () +{ + cmdlist = (struct cmd_list_element *) 0; + infolist = (struct cmd_list_element *) 0; + enablelist = (struct cmd_list_element *) 0; + disablelist = (struct cmd_list_element *) 0; + deletelist = (struct cmd_list_element *) 0; + enablebreaklist = (struct cmd_list_element *) 0; + setlist = (struct cmd_list_element *) 0; + sethistlist = (struct cmd_list_element *) 0; + unsethistlist = (struct cmd_list_element *) 0; +} + +static void +initialize_main () +{ + char *tmpenv; + /* Command line editing externals. */ + extern int (*rl_completion_entry_function)(); + extern char *rl_completer_word_break_characters; + extern char *rl_readline_name; + + /* Set default verbose mode on. */ + info_verbose = 1; + +#ifdef KERNELDEBUG + if (kernel_debugging) + prompt = savestring ("(kgdb) ", 7); + else +#endif + prompt = savestring ("(gdb) ", 6); + + /* Set the important stuff up for command editing. */ + command_editing_p = 1; + history_expansion_p = 0; + write_history_p = 0; + + if (tmpenv = getenv ("HISTSIZE")) + history_size = atoi (tmpenv); + else + history_size = 256; + + stifle_history (history_size); + + if (tmpenv = getenv ("GDBHISTFILE")) + history_filename = savestring (tmpenv, strlen(tmpenv)); + else + /* We include the current directory so that if the user changes + directories the file written will be the same as the one + that was read. */ + history_filename = concat (current_directory, "/.gdb_history", ""); + + read_history (history_filename); + + /* Setup important stuff for command line editing. */ + rl_completion_entry_function = (int (*)()) symbol_completion_function; + rl_completer_word_break_characters = gdb_completer_word_break_characters; + rl_readline_name = "gdb"; + + /* Define the classes of commands. + They will appear in the help list in the reverse of this order. */ + + add_cmd ("obscure", class_obscure, 0, "Obscure features.", &cmdlist); + add_cmd ("alias", class_alias, 0, "Aliases of other commands.", &cmdlist); + add_cmd ("user", class_user, 0, "User-defined commands.\n\ +The commands in this class are those defined by the user.\n\ +Use the \"define\" command to define a command.", &cmdlist); + add_cmd ("support", class_support, 0, "Support facilities.", &cmdlist); + add_cmd ("status", class_info, 0, "Status inquiries.", &cmdlist); + add_cmd ("files", class_files, 0, "Specifying and examining files.", &cmdlist); + add_cmd ("breakpoints", class_breakpoint, 0, "Making program stop at certain points.", &cmdlist); + add_cmd ("data", class_vars, 0, "Examining data.", &cmdlist); + add_cmd ("stack", class_stack, 0, "Examining the stack.\n\ +The stack is made up of stack frames. Gdb assigns numbers to stack frames\n\ +counting from zero for the innermost (currently executing) frame.\n\n\ +At any time gdb identifies one frame as the \"selected\" frame.\n\ +Variable lookups are done with respect to the selected frame.\n\ +When the program being debugged stops, gdb selects the innermost frame.\n\ +The commands below can be used to select other frames by number or address.", + &cmdlist); + add_cmd ("running", class_run, 0, "Running the program.", &cmdlist); + + add_com ("pwd", class_files, pwd_command, + "Print working directory. This is used for your program as well."); + add_com ("cd", class_files, cd_command, + "Set working directory to DIR for debugger and program being debugged.\n\ +The change does not take effect for the program being debugged\n\ +until the next time it is started."); + + add_cmd ("prompt", class_support, set_prompt_command, + "Change gdb's prompt from the default of \"(gdb)\"", + &setlist); + add_com ("echo", class_support, echo_command, + "Print a constant string. Give string as argument.\n\ +C escape sequences may be used in the argument.\n\ +No newline is added at the end of the argument;\n\ +use \"\\n\" if you want a newline to be printed.\n\ +Since leading and trailing whitespace are ignored in command arguments,\n\ +if you want to print some you must use \"\\\" before leading whitespace\n\ +to be printed or after trailing whitespace."); + add_com ("document", class_support, document_command, + "Document a user-defined command.\n\ +Give command name as argument. Give documentation on following lines.\n\ +End with a line of just \"end\"."); + add_com ("define", class_support, define_command, + "Define a new command name. Command name is argument.\n\ +Definition appears on following lines, one command per line.\n\ +End with a line of just \"end\".\n\ +Use the \"document\" command to give documentation for the new command.\n\ +Commands defined in this way do not take arguments."); + + add_com ("source", class_support, source_command, + "Read commands from a file named FILE.\n\ +Note that the file \".gdbinit\" is read automatically in this way\n\ +when gdb is started."); + add_com ("quit", class_support, quit_command, "Exit gdb."); + add_com ("help", class_support, help_command, "Print list of commands."); + add_com_alias ("q", "quit", class_support, 1); + add_com_alias ("h", "help", class_support, 1); + add_com ("while", class_support, while_command, + "execute following commands while condition is true.\n\ +Expression for condition follows \"while\" keyword."); + add_com ("if", class_support, if_command, + "execute following commands if condition is true.\n\ +Expression for condition follows \"if\" keyword."); + add_cmd ("verbose", class_support, set_verbose_command, + "Change the number of informational messages gdb prints.", + &setlist); + add_info ("verbose", verbose_info, + "Status of gdb's verbose printing option.\n"); + + add_com ("dump-me", class_obscure, dump_me_command, + "Get fatal error; make debugger dump its core."); + + add_cmd ("editing", class_support, set_editing, + "Enable or disable command line editing.\n\ +Use \"on\" to enable to enable the editing, and \"off\" to disable it.\n\ +Without an argument, command line editing is enabled.", &setlist); + + add_prefix_cmd ("history", class_support, set_history, + "Generic command for setting command history parameters.", + &sethistlist, "set history ", 0, &setlist); + + add_cmd ("expansion", no_class, set_history_expansion, + "Enable or disable history expansion on command input.\n\ +Without an argument, history expansion is enabled.", &sethistlist); + + add_cmd ("write", no_class, set_history_write, + "Enable or disable saving of the history record on exit.\n\ +Use \"on\" to enable to enable the saving, and \"off\" to disable it.\n\ +Without an argument, saving is enabled.", &sethistlist); + + add_cmd ("size", no_class, set_history_size, + "Set the size of the command history, \n\ +ie. the number of previous commands to keep a record of.", &sethistlist); + + add_cmd ("filename", no_class, set_history_filename, + "Set the filename in which to record the command history\n\ + (the list of previous commands of which a record is kept).", &sethistlist); + + add_prefix_cmd ("info", class_info, info_command, + "Generic command for printing status.", + &infolist, "info ", 0, &cmdlist); + add_com_alias ("i", "info", class_info, 1); + + add_info ("editing", editing_info, "Status of command editor."); + + add_info ("version", version_info, "Report what version of GDB this is."); +} diff --git a/gnu/usr.bin/gdb/ngdb.i386/Makefile b/gnu/usr.bin/gdb/ngdb.i386/Makefile new file mode 100644 index 0000000000..3bf4c6c949 --- /dev/null +++ b/gnu/usr.bin/gdb/ngdb.i386/Makefile @@ -0,0 +1,27 @@ +# %W% (Berkeley) %G% + +.include "../config/Makefile.$(MACHINE)" + +PROG= ngdb +SRCS= i386bsd-dep.c blockframe.c +GDBOBJS+= i386-pinsn.o \ + breakpoint.o command.o copying.o core.o \ + cplus-dem.o dbxread.o environ.o eval.o expprint.o \ + expread.o findvar.o infcmd.o inflow.o infrun.o \ + main.o obstack.o printcmd.o regex.o remote.o \ + remote-sl.o source.o stack.o symmisc.o symtab.o \ + utils.o valarith.o valops.o valprint.o values.o \ + version.o \ + funmap.o history.o keymaps.o readline.o \ + init.o +CFLAGS+= -g -I$(.CURDIR) -I.. -I$(.CURDIR)/.. -I$(.CURDIR)/../config \ + -I/usr/src/sys.newvm \ + -DNEWVM -DHAVE_VPRINTF -DVI_MODE -DKERNELDEBUG +# CC= /usr/old/bin/cc +# CC= cc -traditional +LDADD+= $(GDBOBJS:S/^/..\//g) -ltermcap +NOMAN= noman + +.PATH: $(.CURDIR)/../config $(.CURDIR)/.. + +.include diff --git a/gnu/usr.bin/gdb/obstack.c b/gnu/usr.bin/gdb/obstack.c new file mode 100644 index 0000000000..6f4b282d1e --- /dev/null +++ b/gnu/usr.bin/gdb/obstack.c @@ -0,0 +1,313 @@ +/* obstack.c - subroutines used implicitly by object stack macros + Copyright (C) 1988 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 1, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + +In other words, you are welcome to use, share and improve this program. +You are forbidden to forbid anyone else to use, share and improve +what you give them. Help stamp out software-hoarding! */ + + +#include "obstack.h" + +#ifdef __STDC__ +#define POINTER void * +#else +#define POINTER char * +#endif + +/* Determine default alignment. */ +struct fooalign {char x; double d;}; +#define DEFAULT_ALIGNMENT ((char *)&((struct fooalign *) 0)->d - (char *)0) +/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT. + But in fact it might be less smart and round addresses to as much as + DEFAULT_ROUNDING. So we prepare for it to do that. */ +union fooround {long x; double d;}; +#define DEFAULT_ROUNDING (sizeof (union fooround)) + +/* When we copy a long block of data, this is the unit to do it with. + On some machines, copying successive ints does not work; + in such a case, redefine COPYING_UNIT to `long' (if that works) + or `char' as a last resort. */ +#ifndef COPYING_UNIT +#define COPYING_UNIT int +#endif + +/* The non-GNU-C macros copy the obstack into this global variable + to avoid multiple evaluation. */ + +struct obstack *_obstack; + +/* Initialize an obstack H for use. Specify chunk size SIZE (0 means default). + Objects start on multiples of ALIGNMENT (0 means use default). + CHUNKFUN is the function to use to allocate chunks, + and FREEFUN the function to free them. */ + +void +_obstack_begin (h, size, alignment, chunkfun, freefun) + struct obstack *h; + int size; + int alignment; + POINTER (*chunkfun) (); + void (*freefun) (); +{ + register struct _obstack_chunk* chunk; /* points to new chunk */ + + if (alignment == 0) + alignment = DEFAULT_ALIGNMENT; + if (size == 0) + /* Default size is what GNU malloc can fit in a 4096-byte block. + Pick a number small enough that when rounded up to DEFAULT_ROUNDING + it is still smaller than 4096 - 4. */ + { + int extra = 4; + if (extra < DEFAULT_ROUNDING) + extra = DEFAULT_ROUNDING; + size = 4096 - extra; + } + + h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun; + h->freefun = freefun; + h->chunk_size = size; + h->alignment_mask = alignment - 1; + + chunk = h->chunk = (*h->chunkfun) (h->chunk_size); + h->next_free = h->object_base = chunk->contents; + h->chunk_limit = chunk->limit + = (char *) chunk + h->chunk_size; + chunk->prev = 0; +} + +/* Allocate a new current chunk for the obstack *H + on the assumption that LENGTH bytes need to be added + to the current object, or a new object of length LENGTH allocated. + Copies any partial object from the end of the old chunk + to the beginning of the new one. */ + +void +_obstack_newchunk (h, length) + struct obstack *h; + int length; +{ + register struct _obstack_chunk* old_chunk = h->chunk; + register struct _obstack_chunk* new_chunk; + register long new_size; + register int obj_size = h->next_free - h->object_base; + register int i; + + /* Compute size for new chunk. */ + new_size = (obj_size + length) << 1; + if (new_size < h->chunk_size) + new_size = h->chunk_size; + + /* Allocate and initialize the new chunk. */ + new_chunk = h->chunk = (*h->chunkfun) (new_size); + new_chunk->prev = old_chunk; + new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size; + + /* Move the existing object to the new chunk. + Word at a time is fast and is safe because these + structures are aligned at least that much. */ + for (i = (obj_size + sizeof (COPYING_UNIT) - 1) / sizeof (COPYING_UNIT) - 1; + i >= 0; i--) + ((COPYING_UNIT *)new_chunk->contents)[i] + = ((COPYING_UNIT *)h->object_base)[i]; + + h->object_base = new_chunk->contents; + h->next_free = h->object_base + obj_size; +} + +/* Return nonzero if object OBJ has been allocated from obstack H. + This is here for debugging. + If you use it in a program, you are probably losing. */ + +int +_obstack_allocated_p (h, obj) + struct obstack *h; + POINTER obj; +{ + register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */ + register struct _obstack_chunk* plp; /* point to previous chunk if any */ + + lp = (h)->chunk; + while (lp != 0 && ((POINTER)lp > obj || (POINTER)(lp)->limit < obj)) + { + plp = lp -> prev; + lp = plp; + } + return lp != 0; +} + +/* Free objects in obstack H, including OBJ and everything allocate + more recently than OBJ. If OBJ is zero, free everything in H. */ + +void +#ifdef __STDC__ +#undef obstack_free +obstack_free (struct obstack *h, POINTER obj) +#else +_obstack_free (h, obj) + struct obstack *h; + POINTER obj; +#endif +{ + register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */ + register struct _obstack_chunk* plp; /* point to previous chunk if any */ + + lp = (h)->chunk; + while (lp != 0 && ((POINTER)lp > obj || (POINTER)(lp)->limit < obj)) + { + plp = lp -> prev; + (*h->freefun) (lp); + lp = plp; + } + if (lp) + { + (h)->object_base = (h)->next_free = (char *)(obj); + (h)->chunk_limit = lp->limit; + (h)->chunk = lp; + } + else if (obj != 0) + /* obj is not in any of the chunks! */ + abort (); +} + +/* Let same .o link with output of gcc and other compilers. */ + +#ifdef __STDC__ +void +_obstack_free (h, obj) + struct obstack *h; + POINTER obj; +{ + obstack_free (h, obj); +} +#endif + +#if 0 +/* These are now turned off because the applications do not use it + and it uses bcopy via obstack_grow, which causes trouble on sysV. */ + +/* Now define the functional versions of the obstack macros. + Define them to simply use the corresponding macros to do the job. */ + +#ifdef __STDC__ +/* These function definitions do not work with non-ANSI preprocessors; + they won't pass through the macro names in parentheses. */ + +/* The function names appear in parentheses in order to prevent + the macro-definitions of the names from being expanded there. */ + +POINTER (obstack_base) (obstack) + struct obstack *obstack; +{ + return obstack_base (obstack); +} + +POINTER (obstack_next_free) (obstack) + struct obstack *obstack; +{ + return obstack_next_free (obstack); +} + +int (obstack_object_size) (obstack) + struct obstack *obstack; +{ + return obstack_object_size (obstack); +} + +int (obstack_room) (obstack) + struct obstack *obstack; +{ + return obstack_room (obstack); +} + +void (obstack_grow) (obstack, pointer, length) + struct obstack *obstack; + POINTER pointer; + int length; +{ + obstack_grow (obstack, pointer, length); +} + +void (obstack_grow0) (obstack, pointer, length) + struct obstack *obstack; + POINTER pointer; + int length; +{ + obstack_grow0 (obstack, pointer, length); +} + +void (obstack_1grow) (obstack, character) + struct obstack *obstack; + int character; +{ + obstack_1grow (obstack, character); +} + +void (obstack_blank) (obstack, length) + struct obstack *obstack; + int length; +{ + obstack_blank (obstack, length); +} + +void (obstack_1grow_fast) (obstack, character) + struct obstack *obstack; + int character; +{ + obstack_1grow_fast (obstack, character); +} + +void (obstack_blank_fast) (obstack, length) + struct obstack *obstack; + int length; +{ + obstack_blank_fast (obstack, length); +} + +POINTER (obstack_finish) (obstack) + struct obstack *obstack; +{ + return obstack_finish (obstack); +} + +POINTER (obstack_alloc) (obstack, length) + struct obstack *obstack; + int length; +{ + return obstack_alloc (obstack, length); +} + +POINTER (obstack_copy) (obstack, pointer, length) + struct obstack *obstack; + POINTER pointer; + int length; +{ + return obstack_copy (obstack, pointer, length); +} + +POINTER (obstack_copy0) (obstack, pointer, length) + struct obstack *obstack; + POINTER pointer; + int length; +{ + return obstack_copy0 (obstack, pointer, length); +} + +#endif /* __STDC__ */ + +#endif /* 0 */ diff --git a/gnu/usr.bin/gdb/obstack.h b/gnu/usr.bin/gdb/obstack.h new file mode 100644 index 0000000000..27c017e2f2 --- /dev/null +++ b/gnu/usr.bin/gdb/obstack.h @@ -0,0 +1,372 @@ +/* obstack.h - object stack macros + Copyright (C) 1988 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 1, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + +In other words, you are welcome to use, share and improve this program. +You are forbidden to forbid anyone else to use, share and improve +what you give them. Help stamp out software-hoarding! */ + + +/* Summary: + +All the apparent functions defined here are macros. The idea +is that you would use these pre-tested macros to solve a +very specific set of problems, and they would run fast. +Caution: no side-effects in arguments please!! They may be +evaluated MANY times!! + +These macros operate a stack of objects. Each object starts life +small, and may grow to maturity. (Consider building a word syllable +by syllable.) An object can move while it is growing. Once it has +been "finished" it never changes address again. So the "top of the +stack" is typically an immature growing object, while the rest of the +stack is of mature, fixed size and fixed address objects. + +These routines grab large chunks of memory, using a function you +supply, called `obstack_chunk_alloc'. On occasion, they free chunks, +by calling `obstack_chunk_free'. You must define them and declare +them before using any obstack macros. + +Each independent stack is represented by a `struct obstack'. +Each of the obstack macros expects a pointer to such a structure +as the first argument. + +One motivation for this package is the problem of growing char strings +in symbol tables. Unless you are "facist pig with a read-only mind" +[Gosper's immortal quote from HAKMEM item 154, out of context] you +would not like to put any arbitrary upper limit on the length of your +symbols. + +In practice this often means you will build many short symbols and a +few long symbols. At the time you are reading a symbol you don't know +how long it is. One traditional method is to read a symbol into a +buffer, realloc()ating the buffer every time you try to read a symbol +that is longer than the buffer. This is beaut, but you still will +want to copy the symbol from the buffer to a more permanent +symbol-table entry say about half the time. + +With obstacks, you can work differently. Use one obstack for all symbol +names. As you read a symbol, grow the name in the obstack gradually. +When the name is complete, finalize it. Then, if the symbol exists already, +free the newly read name. + +The way we do this is to take a large chunk, allocating memory from +low addresses. When you want to build a aymbol in the chunk you just +add chars above the current "high water mark" in the chunk. When you +have finished adding chars, because you got to the end of the symbol, +you know how long the chars are, and you can create a new object. +Mostly the chars will not burst over the highest address of the chunk, +because you would typically expect a chunk to be (say) 100 times as +long as an average object. + +In case that isn't clear, when we have enough chars to make up +the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed) +so we just point to it where it lies. No moving of chars is +needed and this is the second win: potentially long strings need +never be explicitly shuffled. Once an object is formed, it does not +change its address during its lifetime. + +When the chars burst over a chunk boundary, we allocate a larger +chunk, and then copy the partly formed object from the end of the old +chunk to the beggining of the new larger chunk. We then carry on +accreting characters to the end of the object as we normaly would. + +A special macro is provided to add a single char at a time to a +growing object. This allows the use of register variables, which +break the ordinary 'growth' macro. + +Summary: + We allocate large chunks. + We carve out one object at a time from the current chunk. + Once carved, an object never moves. + We are free to append data of any size to the currently + growing object. + Exactly one object is growing in an obstack at any one time. + You can run one obstack per control block. + You may have as many control blocks as you dare. + Because of the way we do it, you can `unwind' a obstack + back to a previous state. (You may remove objects much + as you would with a stack.) +*/ + + +/* Don't do the contents of this file more than once. */ + +#ifndef __OBSTACKS__ +#define __OBSTACKS__ + +/* We use subtraction of (char *)0 instead of casting to int + because on word-addressable machines a simple cast to int + may ignore the byte-within-word field of the pointer. */ + +#ifndef __PTR_TO_INT +#define __PTR_TO_INT(P) ((P) - (char *)0) +#endif + +#ifndef __INT_TO_PTR +#define __INT_TO_PTR(P) ((P) + (char *)0) +#endif + +struct _obstack_chunk /* Lives at front of each chunk. */ +{ + char *limit; /* 1 past end of this chunk */ + struct _obstack_chunk *prev; /* address of prior chunk or NULL */ + char contents[4]; /* objects begin here */ +}; + +struct obstack /* control current object in current chunk */ +{ + long chunk_size; /* preferred size to allocate chunks in */ + struct _obstack_chunk* chunk; /* address of current struct obstack_chunk */ + char *object_base; /* address of object we are building */ + char *next_free; /* where to add next char to current object */ + char *chunk_limit; /* address of char after current chunk */ + int temp; /* Temporary for some macros. */ + int alignment_mask; /* Mask of alignment for each object. */ + struct _obstack_chunk *(*chunkfun) (); /* User's fcn to allocate a chunk. */ + void (*freefun) (); /* User's function to free a chunk. */ +}; + +#ifdef __STDC__ + +/* Do the function-declarations after the structs + but before defining the macros. */ + +void obstack_init (struct obstack *obstack); + +void * obstack_alloc (struct obstack *obstack, int size); + +void * obstack_copy (struct obstack *obstack, void *address, int size); +void * obstack_copy0 (struct obstack *obstack, void *address, int size); + +void obstack_free (struct obstack *obstack, void *block); + +void obstack_blank (struct obstack *obstack, int size); + +void obstack_grow (struct obstack *obstack, void *data, int size); +void obstack_grow0 (struct obstack *obstack, void *data, int size); + +void obstack_1grow (struct obstack *obstack, int data_char); + +void * obstack_finish (struct obstack *obstack); + +int obstack_object_size (struct obstack *obstack); + +int obstack_room (struct obstack *obstack); +void obstack_1grow_fast (struct obstack *obstack, int data_char); +void obstack_blank_fast (struct obstack *obstack, int size); + +void * obstack_base (struct obstack *obstack); +void * obstack_next_free (struct obstack *obstack); +int obstack_alignment_mask (struct obstack *obstack); +int obstack_chunk_size (struct obstack *obstack); + +#endif /* __STDC__ */ + +/* Non-ANSI C cannot really support alternative functions for these macros, + so we do not declare them. */ + +/* Pointer to beginning of object being allocated or to be allocated next. + Note that this might not be the final address of the object + because a new chunk might be needed to hold the final size. */ + +#define obstack_base(h) ((h)->object_base) + +/* Size for allocating ordinary chunks. */ + +#define obstack_chunk_size(h) ((h)->chunk_size) + +/* Pointer to next byte not yet allocated in current chunk. */ + +#define obstack_next_free(h) ((h)->next_free) + +/* Mask specifying low bits that should be clear in address of an object. */ + +#define obstack_alignment_mask(h) ((h)->alignment_mask) + +#define obstack_init(h) \ + _obstack_begin ((h), 0, 0, obstack_chunk_alloc, obstack_chunk_free) + +#define obstack_begin(h, size) \ + _obstack_begin ((h), (size), 0, obstack_chunk_alloc, obstack_chunk_free) + +#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = achar) + +#define obstack_blank_fast(h,n) ((h)->next_free += (n)) + +#if defined (__GNUC__) && defined (__STDC__) + +/* For GNU C, if not -traditional, + we can define these macros to compute all args only once + without using a global variable. + Also, we can avoid using the `temp' slot, to make faster code. */ + +#define obstack_object_size(OBSTACK) \ + ({ struct obstack *__o = (OBSTACK); \ + (unsigned) (__o->next_free - __o->object_base); }) + +#define obstack_room(OBSTACK) \ + ({ struct obstack *__o = (OBSTACK); \ + (unsigned) (__o->chunk_limit - __o->next_free); }) + +#define obstack_grow(OBSTACK,where,length) \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + ((__o->next_free + __len > __o->chunk_limit) \ + ? _obstack_newchunk (__o, __len) : 0); \ + bcopy (where, __o->next_free, __len); \ + __o->next_free += __len; \ + (void) 0; }) + +#define obstack_grow0(OBSTACK,where,length) \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + ((__o->next_free + __len + 1 > __o->chunk_limit) \ + ? _obstack_newchunk (__o, __len + 1) : 0), \ + bcopy (where, __o->next_free, __len), \ + __o->next_free += __len, \ + *(__o->next_free)++ = 0; \ + (void) 0; }) + +#define obstack_1grow(OBSTACK,datum) \ +({ struct obstack *__o = (OBSTACK); \ + ((__o->next_free + 1 > __o->chunk_limit) \ + ? _obstack_newchunk (__o, 1) : 0), \ + *(__o->next_free)++ = (datum); \ + (void) 0; }) + +#define obstack_blank(OBSTACK,length) \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + ((__o->next_free + __len > __o->chunk_limit) \ + ? _obstack_newchunk (__o, __len) : 0); \ + __o->next_free += __len; \ + (void) 0; }) + +#define obstack_alloc(OBSTACK,length) \ +({ struct obstack *__h = (OBSTACK); \ + obstack_blank (__h, (length)); \ + obstack_finish (__h); }) + +#define obstack_copy(OBSTACK,where,length) \ +({ struct obstack *__h = (OBSTACK); \ + obstack_grow (__h, (where), (length)); \ + obstack_finish (__h); }) + +#define obstack_copy0(OBSTACK,where,length) \ +({ struct obstack *__h = (OBSTACK); \ + obstack_grow0 (__h, (where), (length)); \ + obstack_finish (__h); }) + +#define obstack_finish(OBSTACK) \ +({ struct obstack *__o = (OBSTACK); \ + void *value = (void *) __o->object_base; \ + __o->next_free \ + = __INT_TO_PTR ((__PTR_TO_INT (__o->next_free)+__o->alignment_mask)\ + & ~ (__o->alignment_mask)); \ + ((__o->next_free - (char *)__o->chunk \ + > __o->chunk_limit - (char *)__o->chunk) \ + ? (__o->next_free = __o->chunk_limit) : 0); \ + __o->object_base = __o->next_free; \ + value; }) + +#define obstack_free(OBSTACK, OBJ) \ +({ struct obstack *__o = (OBSTACK); \ + void *__obj = (OBJ); \ + if (__obj >= (void *)__o->chunk && __obj < (void *)__o->chunk_limit) \ + __o->next_free = __o->object_base = __obj; \ + else (obstack_free) (__o, __obj); }) + +#else /* not __GNUC__ or not __STDC__ */ + +/* The non-GNU macros copy the obstack-pointer into this global variable + to avoid multiple evaluation. */ + +extern struct obstack *_obstack; + +#define obstack_object_size(h) \ + (unsigned) (_obstack = (h), (h)->next_free - (h)->object_base) + +#define obstack_room(h) \ + (unsigned) (_obstack = (h), (h)->chunk_limit - (h)->next_free) + +#define obstack_grow(h,where,length) \ +( (h)->temp = (length), \ + (((h)->next_free + (h)->temp > (h)->chunk_limit) \ + ? _obstack_newchunk ((h), (h)->temp) : 0), \ + bcopy (where, (h)->next_free, (h)->temp), \ + (h)->next_free += (h)->temp) + +#define obstack_grow0(h,where,length) \ +( (h)->temp = (length), \ + (((h)->next_free + (h)->temp + 1 > (h)->chunk_limit) \ + ? _obstack_newchunk ((h), (h)->temp + 1) : 0), \ + bcopy (where, (h)->next_free, (h)->temp), \ + (h)->next_free += (h)->temp, \ + *((h)->next_free)++ = 0) + +#define obstack_1grow(h,datum) \ +( (((h)->next_free + 1 > (h)->chunk_limit) \ + ? _obstack_newchunk ((h), 1) : 0), \ + *((h)->next_free)++ = (datum)) + +#define obstack_blank(h,length) \ +( (h)->temp = (length), \ + (((h)->next_free + (h)->temp > (h)->chunk_limit) \ + ? _obstack_newchunk ((h), (h)->temp) : 0), \ + (h)->next_free += (h)->temp) + +#define obstack_alloc(h,length) \ + (obstack_blank ((h), (length)), obstack_finish ((h))) + +#define obstack_copy(h,where,length) \ + (obstack_grow ((h), (where), (length)), obstack_finish ((h))) + +#define obstack_copy0(h,where,length) \ + (obstack_grow0 ((h), (where), (length)), obstack_finish ((h))) + +#define obstack_finish(h) \ +( (h)->temp = __PTR_TO_INT ((h)->object_base), \ + (h)->next_free \ + = __INT_TO_PTR ((__PTR_TO_INT ((h)->next_free)+(h)->alignment_mask) \ + & ~ ((h)->alignment_mask)), \ + (((h)->next_free - (char *)(h)->chunk \ + > (h)->chunk_limit - (char *)(h)->chunk) \ + ? ((h)->next_free = (h)->chunk_limit) : 0), \ + (h)->object_base = (h)->next_free, \ + __INT_TO_PTR ((h)->temp)) + +#ifdef __STDC__ +#define obstack_free(h,obj) \ +( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \ + (((h)->temp >= 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\ + ? (int) ((h)->next_free = (h)->object_base \ + = (h)->temp + (char *) (h)->chunk) \ + : ((obstack_free) ((h), (h)->temp + (char *) (h)->chunk), 0))) +#else +#define obstack_free(h,obj) \ +( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \ + (((h)->temp >= 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\ + ? (int) ((h)->next_free = (h)->object_base \ + = (h)->temp + (char *) (h)->chunk) \ + : (int) _obstack_free ((h), (h)->temp + (char *) (h)->chunk))) +#endif + +#endif /* not __GNUC__ or not __STDC__ */ + +#endif /* not __OBSTACKS__ */ + diff --git a/gnu/usr.bin/gdb/printcmd.c b/gnu/usr.bin/gdb/printcmd.c new file mode 100644 index 0000000000..6edd7bd2c2 --- /dev/null +++ b/gnu/usr.bin/gdb/printcmd.c @@ -0,0 +1,1867 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)printcmd.c 6.5 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Print values for GNU debugger GDB. + Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include "defs.h" +#include "param.h" +#include "frame.h" +#include "symtab.h" +#include "value.h" +#include "expression.h" + +struct format_data +{ + int count; + char format; + char size; +}; + +/* Last specified output format. */ + +static char last_format = 'x'; + +/* Last specified examination size. 'b', 'h', 'w' or `q'. */ + +static char last_size = 'w'; + +/* Default address to examine next. */ + +static CORE_ADDR next_address; + +/* Last address examined. */ + +static CORE_ADDR last_examine_address; + +/* Contents of last address examined. + This is not valid past the end of the `x' command! */ + +static value last_examine_value; + +/* Number of auto-display expression currently being displayed. + So that we can deleted it if we get an error or a signal within it. + -1 when not doing one. */ + +int current_display_number; + +static void do_one_display (); + +void do_displays (); +void print_address (); +void print_floating (); +void print_scalar_formatted (); +void print_formatted_address (); + + +/* Decode a format specification. *STRING_PTR should point to it. + OFORMAT and OSIZE are used as defaults for the format and size + if none are given in the format specification. + If OSIZE is zero, then the size field of the returned value + should be set only if a size is explicitly specified by the + user. + The structure returned describes all the data + found in the specification. In addition, *STRING_PTR is advanced + past the specification and past all whitespace following it. */ + +struct format_data +decode_format (string_ptr, oformat, osize) + char **string_ptr; + char oformat; + char osize; +{ + struct format_data val; + register char *p = *string_ptr; + + val.format = '?'; + val.size = '?'; + val.count = 1; + + if (*p >= '0' && *p <= '9') + val.count = atoi (p); + while (*p >= '0' && *p <= '9') p++; + + /* Now process size or format letters that follow. */ + + while (1) + { + if (*p == 'b' || *p == 'h' || *p == 'w' || *p == 'g') + val.size = *p++; +#ifdef LONG_LONG + else if (*p == 'l') + { + val.size = 'g'; + p++; + } +#endif + else if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z')) + val.format = *p++; + else + break; + } + +#ifndef LONG_LONG + /* Make sure 'g' size is not used on integer types. + Well, actually, we can handle hex. */ + if (val.size == 'g' && val.format != 'f' && val.format != 'x') + val.size = 'w'; +#endif + + while (*p == ' ' || *p == '\t') p++; + *string_ptr = p; + + /* Set defaults for format and size if not specified. */ + if (val.format == '?') + { + if (val.size == '?') + { + /* Neither has been specified. */ + val.format = oformat; + val.size = osize; + } + else + /* If a size is specified, any format makes a reasonable + default except 'i'. */ + val.format = oformat == 'i' ? 'x' : oformat; + } + else if (val.size == '?') + switch (val.format) + { + case 'a': + case 's': + case 'A': + /* Addresses must be words. */ + val.size = osize ? 'w' : osize; + break; + case 'f': + /* Floating point has to be word or giantword. */ + if (osize == 'w' || osize == 'g') + val.size = osize; + else + /* Default it to giantword if the last used size is not + appropriate. */ + val.size = osize ? 'g' : osize; + break; + case 'c': + /* Characters default to one byte. */ + val.size = osize ? 'b' : osize; + break; + default: + /* The default is the size most recently specified. */ + val.size = osize; + } + + return val; +} + +/* Print value VAL on stdout according to FORMAT, a letter or 0. + Do not end with a newline. + 0 means print VAL according to its own type. + SIZE is the letter for the size of datum being printed. + This is used to pad hex numbers so they line up. */ + +static void +print_formatted (val, format, size) + register value val; + register char format; + char size; +{ + int len = TYPE_LENGTH (VALUE_TYPE (val)); + + if (VALUE_LVAL (val) == lval_memory) + next_address = VALUE_ADDRESS (val) + len; + + switch (format) + { + case 's': + next_address = VALUE_ADDRESS (val) + + value_print (value_addr (val), stdout, 0, Val_pretty_default); + break; + + case 'i': + next_address = VALUE_ADDRESS (val) + + print_insn (VALUE_ADDRESS (val), stdout); + break; + + default: + if (format == 0 + || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_ARRAY + || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_STRUCT + || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_UNION + || VALUE_REPEATED (val)) + value_print (val, stdout, format, Val_pretty_default); + else + print_scalar_formatted (VALUE_CONTENTS (val), VALUE_TYPE (val), + format, size, stdout); + } +} + +/* Print a scalar of data of type TYPE, pointed to in GDB by VALADDR, + according to letters FORMAT and SIZE on STREAM. + FORMAT may not be zero. Formats s and i are not supported at this level. + + This is how the elements of an array or structure are printed + with a format. */ + +void +print_scalar_formatted (valaddr, type, format, size, stream) + char *valaddr; + struct type *type; + char format; + int size; + FILE *stream; +{ + LONGEST val_long; + int len = TYPE_LENGTH (type); + + if (size == 'g' && sizeof (LONGEST) < 8 + && format == 'x') + { + /* ok, we're going to have to get fancy here. Assumption: a + long is four bytes. */ + unsigned long v1, v2, tmp; + + v1 = unpack_long (builtin_type_long, valaddr); + v2 = unpack_long (builtin_type_long, valaddr + 4); + +#ifdef BYTES_BIG_ENDIAN +#else + /* Little endian -- swap the two for printing */ + tmp = v1; + v1 = v2; + v2 = tmp; +#endif + + switch (format) + { + case 'x': + fprintf_filtered (stream, "0x%08x%08x", v1, v2); + break; + default: + error ("Output size \"g\" unimplemented for format \"%c\".", + format); + } + return; + } + + val_long = unpack_long (type, valaddr); + + /* If value is unsigned, truncate it in case negative. */ + if (format != 'd') + { + if (len == sizeof (char)) + val_long &= (1 << 8 * sizeof(char)) - 1; + else if (len == sizeof (short)) + val_long &= (1 << 8 * sizeof(short)) - 1; + else if (len == sizeof (long)) + val_long &= (unsigned long) - 1; + } + + switch (format) + { + case 'x': +#ifdef LONG_LONG + if (!size) + size = (len < sizeof (long long) ? 'w' : 'g'); + switch (size) + { + case 'b': + fprintf_filtered (stream, "0x%02llx", val_long); + break; + case 'h': + fprintf_filtered (stream, "0x%04llx", val_long); + break; + case 0: /* no size specified, like in print */ + case 'w': + fprintf_filtered (stream, "0x%08llx", val_long); + break; + case 'g': + fprintf_filtered (stream, "0x%016llx", val_long); + break; + default: + error ("Undefined output size \"%c\".", size); + } +#else + switch (size) + { + case 'b': + fprintf_filtered (stream, "0x%02x", val_long); + break; + case 'h': + fprintf_filtered (stream, "0x%04x", val_long); + break; + case 0: /* no size specified, like in print */ + case 'w': + fprintf_filtered (stream, "0x%08x", val_long); + break; + case 'g': + fprintf_filtered (stream, "0x%o16x", val_long); + break; + default: + error ("Undefined output size \"%c\".", size); + } +#endif /* not LONG_LONG */ + break; + + case 'd': +#ifdef LONG_LONG + fprintf_filtered (stream, "%lld", val_long); +#else + fprintf_filtered (stream, "%d", val_long); +#endif + break; + + case 'u': +#ifdef LONG_LONG + fprintf_filtered (stream, "%llu", val_long); +#else + fprintf_filtered (stream, "%u", val_long); +#endif + break; + + case 'o': + if (val_long) +#ifdef LONG_LONG + fprintf_filtered (stream, "0%llo", val_long); +#else + fprintf_filtered (stream, "0%o", val_long); +#endif + else + fprintf_filtered (stream, "0"); + break; + + case 'a': + print_address ((CORE_ADDR) val_long, stream); + break; + + case 'A': + print_formatted_address ((CORE_ADDR) val_long, stream); + break; + + case 'c': + value_print (value_from_long (builtin_type_char, val_long), stream, 0, + Val_pretty_default); + break; + + case 'f': + if (len == sizeof (float)) + type = builtin_type_float; + else if (len == sizeof (double)) + type = builtin_type_double; + print_floating(valaddr, type, stream); + break; + + case 0: + abort (); + + default: + error ("Undefined output format \"%c\".", format); + } +} + +/* Print a floating point value of type TYPE, pointed to in GDB by VALADDR, + on STREAM. */ + +void +print_floating(valaddr, type, stream) + char *valaddr; + struct type *type; + FILE *stream; +{ + double doub; + int inv; + int len = TYPE_LENGTH (type); + + doub = unpack_double (type, valaddr, &inv); + if (inv) + fprintf_filtered (stream, "Invalid float value"); + else if (doub != doub) + { + /* Surely it is an IEEE floating point NaN. */ + + long low, high, *arg = (long *)valaddr; /* ASSUMED 32 BITS */ + int nonneg; + + if (len <= sizeof(float)) + { + /* It's single precision. */ + low = *arg; + nonneg = low >= 0; + low &= 0x7fffff; + high = 0; + } + else + { + /* It's double precision. + Get the high and low words of the fraction. + Distinguish big and little-endian machines. */ +#ifdef WORDS_BIG_ENDIAN + low = arg[1], high = arg[0]; +#else + low = arg[0], high = arg[1]; +#endif + nonneg = high >= 0; + high &= 0xfffff; + } + if (high) + fprintf_filtered (stream, "-NaN(0x%lx%.8lx)" + nonneg, high, low); + else + fprintf_filtered (stream, "-NaN(0x%lx)" + nonneg, low); + } + else + fprintf_filtered (stream, len <= sizeof(float) ? "%.6g" : "%.17g", doub); +} + +/* Specify default address for `x' command. + `info lines' uses this. */ + +void +set_next_address (addr) + CORE_ADDR addr; +{ + next_address = addr; + + /* Make address available to the user as $_. */ + set_internalvar (lookup_internalvar ("_"), + value_from_long (builtin_type_int, (LONGEST) addr)); +} + +/* Optionally print address ADDR symbolically as on STREAM. */ + +void +print_address_symbolic (addr, stream) + CORE_ADDR addr; + FILE *stream; +{ + register char *format; + int name_location; + register int i = find_pc_misc_function (addr); + + /* If nothing comes out, don't print anything symbolic. */ + if (i < 0) return; + name_location = misc_function_vector[i].address; + + if (addr - name_location) + format = " <%s+%d>"; + else + format = " <%s>"; + + fprintf_filtered (stream, format, + misc_function_vector[i].name, addr - name_location); +} + +/* Print address ADDR symbolically on STREAM. + First print it as a number. Then perhaps print + after the number. */ + +void +print_address (addr, stream) + CORE_ADDR addr; + FILE *stream; +{ + fprintf_filtered (stream, "0x%x", addr); + print_address_symbolic (addr, stream); +} + +/* Like print_address but opnly prints symbolically. */ + +void +print_formatted_address (addr, stream) + CORE_ADDR addr; + FILE *stream; +{ + register int i = 0; + register char *format; + register struct symbol *fs; + char *name; + int name_location; + + i = find_pc_partial_function (addr, &name, &name_location); + + /* If nothing comes out, don't print anything symbolic. */ + + if (i == 0) + fprintf_filtered (stream, "0x%x", addr); + else if (addr - name_location) + fprintf_filtered (stream, "%s+%d", name, addr - name_location); + else + fprintf_filtered (stream, "%s", name); +} + +/* Examine data at address ADDR in format FMT. + Fetch it from memory and print on stdout. */ + +static void +do_examine (fmt, addr) + struct format_data fmt; + CORE_ADDR addr; +{ + register char format = 0; + register char size; + register int count = 1; + struct type *val_type; + register int i; + register int maxelts; + + format = fmt.format; + size = fmt.size; + count = fmt.count; + next_address = addr; + + /* String or instruction format implies fetch single bytes + regardless of the specified size. */ + if (format == 's' || format == 'i') + size = 'b'; + + if (size == 'b') + val_type = builtin_type_char; + else if (size == 'h') + val_type = builtin_type_short; + else if (size == 'w') + val_type = builtin_type_long; + else if (size == 'g') +#ifndef LONG_LONG + val_type = builtin_type_double; +#else + val_type = builtin_type_long_long; +#endif + + maxelts = 8; + if (size == 'w') + maxelts = 4; + if (size == 'g') + maxelts = 2; + if (format == 's' || format == 'i') + maxelts = 1; + + /* Print as many objects as specified in COUNT, at most maxelts per line, + with the address of the next one at the start of each line. */ + + while (count > 0) + { + print_address (next_address, stdout); + printf_filtered (":"); + for (i = maxelts; + i > 0 && count > 0; + i--, count--) + { + printf_filtered ("\t"); + /* Note that print_formatted sets next_address for the next + object. */ + last_examine_address = next_address; + last_examine_value = value_at (val_type, next_address); + print_formatted (last_examine_value, format, size); + } + printf_filtered ("\n"); + fflush (stdout); + } +} + +static void +validate_format (fmt, cmdname) + struct format_data fmt; + char *cmdname; +{ + if (fmt.size != 0) + error ("Size letters are meaningless in \"%s\" command.", cmdname); + if (fmt.count != 1) + error ("Item count other than 1 is meaningless in \"%s\" command.", + cmdname); + if (fmt.format == 'i' || fmt.format == 's') + error ("Format letter \"%c\" is meaningless in \"%s\" command.", + fmt.format, cmdname); +} + +static void +print_command (exp) + char *exp; +{ + struct expression *expr; + register struct cleanup *old_chain = 0; + register char format = 0; + register value val; + struct format_data fmt; + int histindex; + int cleanup = 0; + + if (exp && *exp == '/') + { + exp++; + fmt = decode_format (&exp, last_format, 0); + validate_format (fmt, "print"); + last_format = format = fmt.format; + } + + if (exp && *exp) + { + expr = parse_c_expression (exp); + old_chain = make_cleanup (free_current_contents, &expr); + cleanup = 1; + val = evaluate_expression (expr); + } + else + val = access_value_history (0); + + histindex = record_latest_value (val); + if (histindex >= 0) printf_filtered ("$%d = ", histindex); + + print_formatted (val, format, fmt.size); + printf_filtered ("\n"); + + if (cleanup) + do_cleanups (old_chain); +} + +static void +output_command (exp) + char *exp; +{ + struct expression *expr; + register struct cleanup *old_chain; + register char format = 0; + register value val; + struct format_data fmt; + + if (exp && *exp == '/') + { + exp++; + fmt = decode_format (&exp, 0, 0); + validate_format (fmt, "print"); + format = fmt.format; + } + + expr = parse_c_expression (exp); + old_chain = make_cleanup (free_current_contents, &expr); + + val = evaluate_expression (expr); + + print_formatted (val, format, fmt.size); + + do_cleanups (old_chain); +} + +static void +set_command (exp) + char *exp; +{ + struct expression *expr = parse_c_expression (exp); + register struct cleanup *old_chain + = make_cleanup (free_current_contents, &expr); + evaluate_expression (expr); + do_cleanups (old_chain); +} + +static void +address_info (exp) + char *exp; +{ + register struct symbol *sym; + register CORE_ADDR val; + int is_a_field_of_this; /* C++: lookup_symbol sets this to nonzero + if exp is a field of `this'. */ + + if (exp == 0) + error ("Argument required."); + + sym = lookup_symbol (exp, get_selected_block (), VAR_NAMESPACE, + &is_a_field_of_this); + if (sym == 0) + { + register int i; + + if (is_a_field_of_this) + { + printf ("Symbol \"%s\" is a field of the local class variable `this'\n", exp); + return; + } + + for (i = 0; i < misc_function_count; i++) + if (!strcmp (misc_function_vector[i].name, exp)) + break; + + if (i < misc_function_count) + printf ("Symbol \"%s\" is at 0x%x in a file compiled without -g.\n", + exp, misc_function_vector[i].address); + else + error ("No symbol \"%s\" in current context.", exp); + return; + } + + printf ("Symbol \"%s\" is ", SYMBOL_NAME (sym)); + val = SYMBOL_VALUE (sym); + + switch (SYMBOL_CLASS (sym)) + { + case LOC_CONST: + case LOC_CONST_BYTES: + printf ("constant"); + break; + + case LOC_LABEL: + printf ("a label at address 0x%x", val); + break; + + case LOC_REGISTER: + printf ("a variable in register %s", reg_names[val]); + break; + + case LOC_STATIC: + printf ("static at address 0x%x", val); + break; + + case LOC_REGPARM: + printf ("an argument in register %s", reg_names[val]); + break; + + case LOC_ARG: + printf ("an argument at offset %d", val); + break; + + case LOC_LOCAL: + printf ("a local variable at frame offset %d", val); + break; + + case LOC_REF_ARG: + printf ("a reference argument at offset %d", val); + break; + + case LOC_TYPEDEF: + printf ("a typedef"); + break; + + case LOC_BLOCK: + printf ("a function at address 0x%x", + BLOCK_START (SYMBOL_BLOCK_VALUE (sym))); + break; + } + printf (".\n"); +} + +static void +x_command (exp, from_tty) + char *exp; + int from_tty; +{ + struct expression *expr; + struct format_data fmt; + struct cleanup *old_chain; + struct value *val; + + fmt.format = last_format; + fmt.size = last_size; + fmt.count = 1; + + if (exp && *exp == '/') + { + exp++; + fmt = decode_format (&exp, last_format, last_size); + last_size = fmt.size; + last_format = fmt.format; + } + + /* If we have an expression, evaluate it and use it as the address. */ + + if (exp != 0 && *exp != 0) + { + expr = parse_c_expression (exp); + /* Cause expression not to be there any more + if this command is repeated with Newline. + But don't clobber a user-defined command's definition. */ + if (from_tty) + *exp = 0; + old_chain = make_cleanup (free_current_contents, &expr); + val = evaluate_expression (expr); + /* In rvalue contexts, such as this, functions are coerced into + pointers to functions. This makes "x/i main" work. */ + if (/* last_format == 'i' + && */ TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_FUNC + && VALUE_LVAL (val) == lval_memory) + next_address = VALUE_ADDRESS (val); + else + next_address = (CORE_ADDR) value_as_long (val); + do_cleanups (old_chain); + } + + do_examine (fmt, next_address); + + /* Set a couple of internal variables if appropriate. */ + if (last_examine_value) + { + /* Make last address examined available to the user as $_. */ + set_internalvar (lookup_internalvar ("_"), + value_from_long (builtin_type_int, + (LONGEST) last_examine_address)); + + /* Make contents of last address examined available to the user as $__.*/ + set_internalvar (lookup_internalvar ("__"), last_examine_value); + } +} + +/* Commands for printing types of things. */ + +static void +whatis_command (exp) + char *exp; +{ + struct expression *expr; + register value val; + register struct cleanup *old_chain; + + if (exp) + { + expr = parse_c_expression (exp); + old_chain = make_cleanup (free_current_contents, &expr); + val = evaluate_type (expr); + } + else + val = access_value_history (0); + + printf_filtered ("type = "); + /* Most of the time users do not want to see all the fields + in a structure. If they do they can use the "ptype" command. + Hence the "-1" below. */ + type_print (VALUE_TYPE (val), "", stdout, -1); + printf_filtered ("\n"); + + if (exp) + do_cleanups (old_chain); +} + +static void +ptype_command (typename) + char *typename; +{ + register char *p = typename; + register int len; + extern struct block *get_current_block (); + register struct block *b + = (have_inferior_p () || have_core_file_p ()) ? get_current_block () : 0; + register struct type *type; + + if (typename == 0) + error_no_arg ("type name"); + + while (*p && *p != ' ' && *p != '\t') p++; + len = p - typename; + while (*p == ' ' || *p == '\t') p++; + + if (len == 6 && !strncmp (typename, "struct", 6)) + type = lookup_struct (p, b); + else if (len == 5 && !strncmp (typename, "union", 5)) + type = lookup_union (p, b); + else if (len == 4 && !strncmp (typename, "enum", 4)) + type = lookup_enum (p, b); + else + { + type = lookup_typename (typename, b, 1); + if (type == 0) + { + register struct symbol *sym + = lookup_symbol (typename, b, STRUCT_NAMESPACE, 0); + if (sym == 0) + error ("No type named %s.", typename); + printf_filtered ("No type named %s, but there is a ", + typename); + switch (TYPE_CODE (SYMBOL_TYPE (sym))) + { + case TYPE_CODE_STRUCT: + printf_filtered ("struct"); + break; + + case TYPE_CODE_UNION: + printf_filtered ("union"); + break; + + case TYPE_CODE_ENUM: + printf_filtered ("enum"); + } + printf_filtered (" %s. Type \"help ptype\".\n", typename); + type = SYMBOL_TYPE (sym); + } + } + + type_print (type, "", stdout, 1); + printf_filtered ("\n"); +} + +enum display_status {disabled, enabled}; + +struct display +{ + /* Chain link to next auto-display item. */ + struct display *next; + /* Expression to be evaluated and displayed. */ + struct expression *exp; + /* Item number of this auto-display item. */ + int number; + /* Display format specified. */ + struct format_data format; + /* Innermost block required by this expression when evaluated */ + struct block *block; + /* Status of this display (enabled or disabled) */ + enum display_status status; +}; + +/* Chain of expressions whose values should be displayed + automatically each time the program stops. */ + +static struct display *display_chain; + +static int display_number; + +/* Add an expression to the auto-display chain. + Specify the expression. */ + +static void +display_command (exp, from_tty) + char *exp; + int from_tty; +{ + struct format_data fmt; + register struct expression *expr; + register struct display *new; + extern struct block *innermost_block; + + if (exp == 0) + { + do_displays (); + return; + } + + if (*exp == '/') + { + exp++; + fmt = decode_format (&exp, 0, 0); + if (fmt.size && fmt.format == 0) + fmt.format = 'x'; + if (fmt.format == 'i' || fmt.format == 's') + fmt.size = 'b'; + } + else + { + fmt.format = 0; + fmt.size = 0; + fmt.count = 0; + } + + innermost_block = 0; + expr = parse_c_expression (exp); + + new = (struct display *) xmalloc (sizeof (struct display)); + + new->exp = expr; + new->block = innermost_block; + new->next = display_chain; + new->number = ++display_number; + new->format = fmt; + new->status = enabled; + display_chain = new; + + if (from_tty && have_inferior_p ()) + do_one_display (new); + + dont_repeat (); +} + +static void +free_display (d) + struct display *d; +{ + free (d->exp); + free (d); +} + +/* Clear out the display_chain. + Done when new symtabs are loaded, since this invalidates + the types stored in many expressions. */ + +void +clear_displays () +{ + register struct display *d; + + while (d = display_chain) + { + free (d->exp); + display_chain = d->next; + free (d); + } +} + +/* Delete the auto-display number NUM. */ + +void +delete_display (num) + int num; +{ + register struct display *d1, *d; + + if (!display_chain) + error ("No display number %d.", num); + + if (display_chain->number == num) + { + d1 = display_chain; + display_chain = d1->next; + free_display (d1); + } + else + for (d = display_chain; ; d = d->next) + { + if (d->next == 0) + error ("No display number %d.", num); + if (d->next->number == num) + { + d1 = d->next; + d->next = d1->next; + free_display (d1); + break; + } + } +} + +/* Delete some values from the auto-display chain. + Specify the element numbers. */ + +static void +undisplay_command (args) + char *args; +{ + register char *p = args; + register char *p1; + register int num; + register struct display *d, *d1; + + if (args == 0) + { + if (query ("Delete all auto-display expressions? ")) + clear_displays (); + dont_repeat (); + return; + } + + while (*p) + { + p1 = p; + while (*p1 >= '0' && *p1 <= '9') p1++; + if (*p1 && *p1 != ' ' && *p1 != '\t') + error ("Arguments must be display numbers."); + + num = atoi (p); + + delete_display (num); + + p = p1; + while (*p == ' ' || *p == '\t') p++; + } + dont_repeat (); +} + +/* Display a single auto-display. + Do nothing if the display cannot be printed in the current context, + or if the display is disabled. */ + +static void +do_one_display (d) + struct display *d; +{ + int within_current_scope; + + if (d->status == disabled) + return; + + if (d->block) + within_current_scope = contained_in (get_selected_block (), d->block); + else + within_current_scope = 1; + if (!within_current_scope) + return; + + current_display_number = d->number; + + printf_filtered ("%d: ", d->number); + if (d->format.size) + { + printf_filtered ("x/"); + if (d->format.count != 1) + printf_filtered ("%d", d->format.count); + printf_filtered ("%c", d->format.format); + if (d->format.format != 'i' && d->format.format != 's') + printf_filtered ("%c", d->format.size); + printf_filtered (" "); + print_expression (d->exp, stdout); + if (d->format.count != 1) + printf_filtered ("\n"); + else + printf_filtered (" "); + do_examine (d->format, + (CORE_ADDR) value_as_long (evaluate_expression (d->exp))); + + } + else + { + if (d->format.format) + printf_filtered ("/%c ", d->format.format); + print_expression (d->exp, stdout); + printf_filtered (" = "); + print_formatted (evaluate_expression (d->exp), + d->format.format, d->format.size); + printf_filtered ("\n"); + } + + fflush (stdout); + current_display_number = -1; +} + +/* Display all of the values on the auto-display chain which can be + evaluated in the current scope. */ + +void +do_displays () +{ + register struct display *d; + + for (d = display_chain; d; d = d->next) + do_one_display (d); +} + +/* Delete the auto-display which we were in the process of displaying. + This is done when there is an error or a signal. */ + +void +disable_display (num) + int num; +{ + register struct display *d; + + for (d = display_chain; d; d = d->next) + if (d->number == num) + { + d->status = disabled; + return; + } + printf ("No display number %d.\n", num); +} + +void +disable_current_display () +{ + if (current_display_number >= 0) + { + disable_display (current_display_number); + fprintf (stderr, "Disabling display %d to avoid infinite recursion.\n", + current_display_number); + } + current_display_number = -1; +} + +static void +display_info () +{ + register struct display *d; + + if (!display_chain) + printf ("There are no auto-display expressions now.\n"); + else + printf_filtered ("Auto-display expressions now in effect:\n\ +Num Enb Expression\n"); + + for (d = display_chain; d; d = d->next) + { + printf_filtered ("%d: %c ", d->number, "ny"[(int)d->status]); + if (d->format.size) + printf_filtered ("/%d%c%c ", d->format.count, d->format.size, + d->format.format); + else if (d->format.format) + printf_filtered ("/%c ", d->format.format); + print_expression (d->exp, stdout); + if (d->block && !contained_in (get_selected_block (), d->block)) + printf_filtered (" (cannot be evaluated in the current context)"); + printf_filtered ("\n"); + fflush (stdout); + } +} + +void +enable_display (args) + char *args; +{ + register char *p = args; + register char *p1; + register int num; + register struct display *d; + + if (p == 0) + { + for (d = display_chain; d; d = d->next) + d->status = enabled; + } + else + while (*p) + { + p1 = p; + while (*p1 >= '0' && *p1 <= '9') + p1++; + if (*p1 && *p1 != ' ' && *p1 != '\t') + error ("Arguments must be display numbers."); + + num = atoi (p); + + for (d = display_chain; d; d = d->next) + if (d->number == num) + { + d->status = enabled; + goto win; + } + printf ("No display number %d.\n", num); + win: + p = p1; + while (*p == ' ' || *p == '\t') + p++; + } +} + +void +disable_display_command (args, from_tty) + char *args; + int from_tty; +{ + register char *p = args; + register char *p1; + register int num; + register struct display *d; + + if (p == 0) + { + for (d = display_chain; d; d = d->next) + d->status = disabled; + } + else + while (*p) + { + p1 = p; + while (*p1 >= '0' && *p1 <= '9') + p1++; + if (*p1 && *p1 != ' ' && *p1 != '\t') + error ("Arguments must be display numbers."); + + num = atoi (p); + + disable_display (atoi (p)); + + p = p1; + while (*p == ' ' || *p == '\t') + p++; + } +} + + +/* Print the value in stack frame FRAME of a variable + specified by a struct symbol. */ + +void +print_variable_value (var, frame, stream) + struct symbol *var; + FRAME frame; + FILE *stream; +{ + value val = read_var_value (var, frame); + value_print (val, stream, 0, Val_pretty_default); +} + +static int +compare_ints (i, j) + int *i, *j; +{ + return *i - *j; +} + +/* Print the arguments of a stack frame, given the function FUNC + running in that frame (as a symbol), the info on the frame, + and the number of args according to the stack frame (or -1 if unknown). */ + +static void print_frame_nameless_args (); + +void +print_frame_args (func, fi, num, stream) + struct symbol *func; + struct frame_info *fi; + int num; + FILE *stream; +{ + struct block *b; + int nsyms = 0; + int first = 1; + register int i; + register int last_regparm = 0; + register struct symbol *lastsym, *sym, *nextsym; + register value val; + /* Offset of stack argument that is at the highest offset. + -1 if we haven't come to a stack argument yet. */ + CORE_ADDR highest_offset = (CORE_ADDR) -1; + register CORE_ADDR addr = FRAME_ARGS_ADDRESS (fi); + + if (func) + { + b = SYMBOL_BLOCK_VALUE (func); + nsyms = BLOCK_NSYMS (b); + } + + for (i = 0; i < nsyms; i++) + { + QUIT; + sym = BLOCK_SYM (b, i); + + if (SYMBOL_CLASS (sym) != LOC_REGPARM + && SYMBOL_CLASS (sym) != LOC_ARG + && SYMBOL_CLASS (sym) != LOC_REF_ARG) + continue; + + /* Print the next arg. */ + if (SYMBOL_CLASS (sym) == LOC_REGPARM) + val = value_from_register (SYMBOL_TYPE (sym), + SYMBOL_VALUE (sym), + FRAME_INFO_ID (fi)); + else + { + int current_offset = SYMBOL_VALUE (sym); + int arg_size = TYPE_LENGTH (SYMBOL_TYPE (sym)); + + if (SYMBOL_CLASS (sym) == LOC_REF_ARG) + val = value_at (SYMBOL_TYPE (sym), + read_memory_integer (addr + current_offset, + sizeof (CORE_ADDR))); + else + val = value_at (SYMBOL_TYPE (sym), addr + current_offset); + + /* Round up address of next arg to multiple of size of int. */ + current_offset + = (((current_offset + sizeof (int) - 1) / sizeof (int)) + * sizeof (int)); + + /* If this is the highest offset seen yet, set highest_offset. */ + if (highest_offset == (CORE_ADDR)-1 + || ((current_offset + + (arg_size - sizeof (int) + 3) / (sizeof (int))) + > highest_offset)) + highest_offset = current_offset; + } + + if (! first) + fprintf_filtered (stream, ", "); + fputs_filtered (SYMBOL_NAME (sym), stream); + fputs_filtered ("=", stream); + +/* Nonzero if a LOC_ARG which is a struct is useless. */ +#if !defined (STRUCT_ARG_SYM_GARBAGE) +#define STRUCT_ARG_SYM_GARBAGE(gcc_p) 0 +#endif + + if (STRUCT_ARG_SYM_GARBAGE (b->gcc_compile_flag) + && TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_STRUCT + && SYMBOL_CLASS (sym) == LOC_ARG) + { + /* Try looking up that name. SunOS4 puts out a usable + symbol as a local variable (in addition to the one + for the arg). */ + struct symbol *sym2 = + lookup_symbol (SYMBOL_NAME (sym), b, VAR_NAMESPACE, 0); + + if (sym2 != NULL) + val = value_of_variable (sym2); + else + { + fputs_filtered ("?", stream); + first = 0; + continue; + } + } + + value_print (val, stream, 0, Val_no_prettyprint); + first = 0; + } + + /* Don't print nameless args in situations where we don't know + enough about the stack to find them. */ + if (num != -1) + { + if (highest_offset != (CORE_ADDR) -1 + && num * sizeof (int) + FRAME_ARGS_SKIP > highest_offset) + print_frame_nameless_args (fi, addr, + highest_offset + sizeof (int), + num * sizeof (int) + FRAME_ARGS_SKIP, + stream); + else + print_frame_nameless_args (fi, addr, FRAME_ARGS_SKIP, + num * sizeof (int) + FRAME_ARGS_SKIP, + stream); + } +} + +static void +print_frame_nameless_args (fi, argsaddr, start, end, stream) + struct frame_info *fi; + CORE_ADDR argsaddr; + int start; + int end; + FILE *stream; +{ + extern void (*default_scalar_print)(); + LONGEST v; + int p = start; + char *s = ""; + + for (p = start; p < end; p += sizeof(int)) { + QUIT; +#if defined(NAMELESS_ARG) + v = NAMELESS_ARG(fi, (p - start) / sizeof(int)); +#else + v = read_memory_integer (argsaddr + p, sizeof (int)); +#endif + fprintf_filtered (stream, s); + s = ", "; + (*default_scalar_print) (stream, builtin_type_int, v); + } +} + +static void +printf_command (arg) + char *arg; +{ + register char *f; + register char *s = arg; + char *string; + value *val_args; + int nargs = 0; + int allocated_args = 20; + char *arg_bytes; + + val_args = (value *) xmalloc (allocated_args * sizeof (value)); + + if (s == 0) + error_no_arg ("format-control string and values to print"); + + /* Skip white space before format string */ + while (*s == ' ' || *s == '\t') s++; + + /* A format string should follow, enveloped in double quotes */ + if (*s++ != '"') + error ("Bad format string, missing '\"'."); + + /* Parse the format-control string and copy it into the string STRING, + processing some kinds of escape sequence. */ + + f = string = (char *) alloca (strlen (s) + 1); + while (*s != '"') + { + int c = *s++; + switch (c) + { + case '\0': + error ("Bad format string, non-terminated '\"'."); + /* doesn't return */ + + case '\\': + switch (c = *s++) + { + case '\\': + *f++ = '\\'; + break; + case 'n': + *f++ = '\n'; + break; + case 't': + *f++ = '\t'; + break; + case 'r': + *f++ = '\r'; + break; + case '"': + *f++ = '"'; + break; + default: + /* ??? TODO: handle other escape sequences */ + error ("Unrecognized \\ escape character in format string."); + } + break; + + default: + *f++ = c; + } + } + + /* Skip over " and following space and comma. */ + s++; + *f++ = '\0'; + while (*s == ' ' || *s == '\t') s++; + + if (*s != ',' && *s != 0) + error ("Invalid argument syntax"); + + if (*s == ',') s++; + while (*s == ' ' || *s == '\t') s++; + + { + /* Now scan the string for %-specs and see what kinds of args they want. + argclass[I] classifies the %-specs so we can give vprintf something + of the right size. */ + + enum argclass {int_arg, string_arg, double_arg, long_long_arg}; + enum argclass *argclass; + int nargs_wanted; + int argindex; + int lcount; + int i; + + argclass = (enum argclass *) alloca (strlen (s) * sizeof *argclass); + nargs_wanted = 0; + f = string; + while (*f) + if (*f++ == '%') + { + lcount = 0; + while (index ("0123456789.hlL-+ #", *f)) + { + if (*f == 'l' || *f == 'L') + lcount++; + f++; + } + if (*f == 's') + argclass[nargs_wanted++] = string_arg; + else if (*f == 'e' || *f == 'f' || *f == 'g') + argclass[nargs_wanted++] = double_arg; + else if (lcount > 1) + argclass[nargs_wanted++] = long_long_arg; + else if (*f != '%') + argclass[nargs_wanted++] = int_arg; + f++; + } + + /* Now, parse all arguments and evaluate them. + Store the VALUEs in VAL_ARGS. */ + + while (*s != '\0') + { + char *s1; + if (nargs == allocated_args) + val_args = (value *) xrealloc (val_args, + (allocated_args *= 2) + * sizeof (value)); + s1 = s; + val_args[nargs] = parse_to_comma_and_eval (&s1); + + /* If format string wants a float, unchecked-convert the value to + floating point of the same size */ + + if (argclass[nargs] == double_arg) + { + if (TYPE_LENGTH (VALUE_TYPE (val_args[nargs])) == sizeof (float)) + VALUE_TYPE (val_args[nargs]) = builtin_type_float; + if (TYPE_LENGTH (VALUE_TYPE (val_args[nargs])) == sizeof (double)) + VALUE_TYPE (val_args[nargs]) = builtin_type_double; + } + nargs++; + s = s1; + if (*s == ',') + s++; + } + + if (nargs != nargs_wanted) + error ("Wrong number of arguments for specified format-string"); + + /* Now lay out an argument-list containing the arguments + as doubles, integers and C pointers. */ + + arg_bytes = (char *) alloca (sizeof (double) * nargs); + argindex = 0; + for (i = 0; i < nargs; i++) + { + if (argclass[i] == string_arg) + { + char *str; + int tem, j; + tem = value_as_long (val_args[i]); + + /* This is a %s argument. Find the length of the string. */ + for (j = 0; ; j++) + { + char c; + QUIT; + read_memory (tem + j, &c, 1); + if (c == 0) + break; + } + + /* Copy the string contents into a string inside GDB. */ + str = (char *) alloca (j + 1); + read_memory (tem, str, j); + str[j] = 0; + + /* Pass address of internal copy as the arg to vprintf. */ + *((int *) &arg_bytes[argindex]) = (int) str; + argindex += sizeof (int); + } + else if (VALUE_TYPE (val_args[i])->code == TYPE_CODE_FLT) + { + *((double *) &arg_bytes[argindex]) = value_as_double (val_args[i]); + argindex += sizeof (double); + } + else +#ifdef LONG_LONG + if (argclass[i] == long_long_arg) + { + *(long long *) &arg_bytes[argindex] = value_as_long (val_args[i]); + argindex += sizeof (long long); + } + else +#endif + { + *((int *) &arg_bytes[argindex]) = value_as_long (val_args[i]); + argindex += sizeof (int); + } + } + } + vprintf (string, arg_bytes); +} + +/* Helper function for asdump_command. Finds the bounds of a function + for a specified section of text. PC is an address within the + function which you want bounds for; *LOW and *HIGH are set to the + beginning (inclusive) and end (exclusive) of the function. This + function returns 1 on success and 0 on failure. */ + +static int +containing_function_bounds (pc, low, high) + CORE_ADDR pc, *low, *high; +{ + int scan; + + if (!find_pc_partial_function (pc, 0, low)) + return 0; + + scan = *low; + do { + scan++; + if (!find_pc_partial_function (scan, 0, high)) + return 0; + } while (*low == *high); + + return 1; +} + +/* Dump a specified section of assembly code. With no command line + arguments, this command will dump the assembly code for the + function surrounding the pc value in the selected frame. With one + argument, it will dump the assembly code surrounding that pc value. + Two arguments are interpeted as bounds within which to dump + assembly. */ + +static void +disassemble_command (arg, from_tty) + char *arg; + int from_tty; +{ + CORE_ADDR low, high; + CORE_ADDR pc; + char *space_index; + + if (!arg) + { + if (!selected_frame) + error ("No frame selected.\n"); + + pc = get_frame_pc (selected_frame); + if (!containing_function_bounds (pc, &low, &high)) + error ("No function contains pc specified by selected frame.\n"); + } + else if (!(space_index = (char *) index (arg, ' '))) + { + /* One argument. */ + pc = parse_and_eval_address (arg); + if (!containing_function_bounds (pc, &low, &high)) + error ("No function contains specified pc.\n"); + } + else + { + /* Two arguments. */ + *space_index = '\0'; + low = parse_and_eval_address (arg); + high = parse_and_eval_address (space_index + 1); + } + + printf_filtered ("Dump of assembler code "); + if (!space_index) + { + char *name; + find_pc_partial_function (pc, &name, 0); + printf_filtered ("for function %s:\n", name); + } + else + printf_filtered ("from 0x%x to 0x%x:\n", low, high); + + /* Dump the specified range. */ + for (pc = low; pc < high; ) + { + QUIT; + print_address (pc, stdout); + printf_filtered (":\t"); + pc += print_insn (pc, stdout); + printf_filtered ("\n"); + } + printf_filtered ("End of assembler dump.\n"); + fflush (stdout); +} + + +extern struct cmd_list_element *enablelist, *disablelist, *deletelist; +extern struct cmd_list_element *cmdlist, *setlist; + +void +_initialize_printcmd () +{ + current_display_number = -1; + + add_info ("address", address_info, + "Describe where variable VAR is stored."); + + add_com ("x", class_vars, x_command, + "Examine memory: x/FMT ADDRESS.\n\ +ADDRESS is an expression for the memory address to examine.\n\ +FMT is a repeat count followed by a format letter and a size letter.\n\ +Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),\n\ + f(float), a(address), i(instruction), c(char) and s(string).\n\ +Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).\n\ + g is meaningful only with f, for type double.\n\ +The specified number of objects of the specified size are printed\n\ +according to the format.\n\n\ +Defaults for format and size letters are those previously used.\n\ +Default count is 1. Default address is following last thing printed\n\ +with this command or \"print\"."); + + add_com ("disassemble", class_vars, disassemble_command, + "Disassemble a specified section of memory.\n\ +Default is the function surrounding the pc of the selected frame.\n\ +With a single argument, the function surrounding that address is dumped.\n\ +Two arguments are taken as a range of memory to dump."); + + add_com ("ptype", class_vars, ptype_command, + "Print definition of type TYPE.\n\ +Argument may be a type name defined by typedef, or \"struct STRUCTNAME\"\n\ +or \"union UNIONNAME\" or \"enum ENUMNAME\".\n\ +The selected stack frame's lexical context is used to look up the name."); + + add_com ("whatis", class_vars, whatis_command, + "Print data type of expression EXP."); + + add_info ("display", display_info, + "Expressions to display when program stops, with code numbers."); + + add_cmd ("undisplay", class_vars, undisplay_command, + "Cancel some expressions to be displayed when program stops.\n\ +Arguments are the code numbers of the expressions to stop displaying.\n\ +No argument means cancel all automatic-display expressions.\n\ +\"delete display\" has the same effect as this command.\n\ +Do \"info display\" to see current list of code numbers.", + &cmdlist); + + add_com ("display", class_vars, display_command, + "Print value of expression EXP each time the program stops.\n\ +/FMT may be used before EXP as in the \"print\" command.\n\ +/FMT \"i\" or \"s\" or including a size-letter is allowed,\n\ +as in the \"x\" command, and then EXP is used to get the address to examine\n\ +and examining is done as in the \"x\" command.\n\n\ +With no argument, display all currently requested auto-display expressions.\n\ +Use \"undisplay\" to cancel display requests previously made."); + + add_cmd ("display", class_vars, enable_display, + "Enable some expressions to be displayed when program stops.\n\ +Arguments are the code numbers of the expressions to resume displaying.\n\ +No argument means enable all automatic-display expressions.\n\ +Do \"info display\" to see current list of code numbers.", &enablelist); + + add_cmd ("display", class_vars, disable_display_command, + "Disable some expressions to be displayed when program stops.\n\ +Arguments are the code numbers of the expressions to stop displaying.\n\ +No argument means disable all automatic-display expressions.\n\ +Do \"info display\" to see current list of code numbers.", &disablelist); + + add_cmd ("display", class_vars, undisplay_command, + "Cancel some expressions to be displayed when program stops.\n\ +Arguments are the code numbers of the expressions to stop displaying.\n\ +No argument means cancel all automatic-display expressions.\n\ +Do \"info display\" to see current list of code numbers.", &deletelist); + + add_com ("printf", class_vars, printf_command, + "printf \"printf format string\", arg1, arg2, arg3, ..., argn\n\ +This is useful for formatted output in user-defined commands."); + add_com ("output", class_vars, output_command, + "Like \"print\" but don't put in value history and don't print newline.\n\ +This is useful in user-defined commands."); + + add_prefix_cmd ("set", class_vars, set_command, +"Perform an assignment VAR = EXP.\n\ +You must type the \"=\". VAR may be a debugger \"convenience\" variable\n\ +(names starting with $), a register (a few standard names starting with $),\n\ +or an actual variable in the program being debugged. EXP is any expression.\n\ +Use \"set variable\" for variables with names identical to set subcommands.\n\ +\nWith a subcommand, this command modifies parts of the gdb environment", + &setlist, "set ", 1, &cmdlist); + + add_cmd ("variable", class_vars, set_command, + "Perform an assignment VAR = EXP.\n\ +You must type the \"=\". VAR may be a debugger \"convenience\" variable\n\ +(names starting with $), a register (a few standard names starting with $),\n\ +or an actual variable in the program being debugged. EXP is any expression.\n\ +This may usually be abbreviated to simply \"set\".", + &setlist); + + add_com ("print", class_vars, print_command, + concat ("Print value of expression EXP.\n\ +Variables accessible are those of the lexical environment of the selected\n\ +stack frame, plus all those whose scope is global or an entire file.\n\ +\n\ +$NUM gets previous value number NUM. $ and $$ are the last two values.\n\ +$$NUM refers to NUM'th value back from the last one.\n\ +Names starting with $ refer to registers (with the values they would have\n\ +if the program were to return to the stack frame now selected, restoring\n\ +all registers saved by frames farther in) or else to debugger\n\ +\"convenience\" variables (any such name not a known register).\n\ +Use assignment expressions to give values to convenience variables.\n", + "\n\ +\{TYPE}ADREXP refers to a datum of data type TYPE, located at address ADREXP.\n\ +@ is a binary operator for treating consecutive data objects\n\ +anywhere in memory as an array. FOO@NUM gives an array whose first\n\ +element is FOO, whose second element is stored in the space following\n\ +where FOO is stored, etc. FOO must be an expression whose value\n\ +resides in memory.\n", + "\n\ +EXP may be preceded with /FMT, where FMT is a format letter\n\ +but no count or size letter (see \"x\" command).")); + add_com_alias ("p", "print", class_vars, 1); +} diff --git a/gnu/usr.bin/gdb/readline/ChangeLog b/gnu/usr.bin/gdb/readline/ChangeLog new file mode 100644 index 0000000000..b72a59daed --- /dev/null +++ b/gnu/usr.bin/gdb/readline/ChangeLog @@ -0,0 +1,98 @@ +Thu Feb 8 01:04:00 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * Makefile (the *other* libreadline.a): Uncomment out ranlib line. + +Thu Feb 1 17:50:22 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * Makefile (libreadline.a): Uncomment out ranlib line. + +Sun Nov 26 16:29:11 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * readline.c (rl_deprep_terminal): Only restore local_mode_flags + if they had been set. + +Thu Oct 19 17:18:40 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * Move vi_doing_insert from vi_mode.c to readline.c + + * readline.c: Move compare_strings before its use. + Remove declarations. + + * readline.c: Move defining_kbd_macro above rl_dispatch. + (rl_dispatch): Remove "extern int defining_kbd_macro". + +Mon Oct 16 11:56:03 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * readline.c (rl_set_signals): Remove unnecessary "static int + rl_signal_handler()". + +Sat Sep 30 14:51:56 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * readline.c (rl_initialize): Change parsing_conditionalized_out + to static. + (rl_dispatch): Change defining_kbd_macro to static. + (rl_newline): Change vi_doing_insert to static. + +Fri Sep 8 09:00:45 1989 Brian Fox (bfox at aurel) + + * readline.c: rl_prep_terminal (). Only turn on 8th bit + as meta-bit iff the terminal is not using parity. + +Sun Sep 3 08:57:40 1989 Brian Fox (bfox at aurel) + + * readline.c: start_insert (). Uses multiple + insertion call in cases where that makes sense. + + rl_insert (). Read type-ahead buffer for additional + keys that are bound to rl_insert, and insert them + all at once. Make insertion of single keys given + with an argument much more efficient. + +Tue Aug 8 18:13:57 1989 Brian Fox (bfox at aurel) + + * readline.c: Changed handling of EOF. readline () returns + (char *)EOF or consed string. The EOF character is read from the + tty, or if the tty doesn't have one, defaults to C-d. + + * readline.c: Added support for event driven programs. + rl_event_hook is the address of a function you want called + while Readline is waiting for input. + + * readline.c: Cleanup time. Functions without type declarations + do not use return with a value. + + * history.c: history_expand () has new variable which is the + characters to ignore immediately following history_expansion_char. + +Sun Jul 16 08:14:00 1989 Brian Fox (bfox at aurel) + + * rl_prep_terminal () + BSD version turns off C-s, C-q, C-y, C-v. + + * readline.c -- rl_prep_terminal () + SYSV version hacks readline_echoing_p. + BSD version turns on passing of the 8th bit for the duration + of reading the line. + +Tue Jul 11 06:25:01 1989 Brian Fox (bfox at aurel) + + * readline.c: new variable rl_tilde_expander. + If non-null, this contains the address of a function to call if + the standard meaning for expanding a tilde fails. The function is + called with the text sans tilde (as in "foo"), and returns a + malloc()'ed string which is the expansion, or a NULL pointer if + there is no expansion. + + * readline.h - new file chardefs.h + Separates things that only readline.c needs from the standard + header file publishing interesting things about readline. + + * readline.c: + readline_default_bindings () now looks at terminal chararacters + and binds those as well. + +Wed Jun 28 20:20:51 1989 Brian Fox (bfox at aurel) + + * Made readline and history into independent libraries. + + diff --git a/gnu/usr.bin/gdb/readline/Makefile.gnu b/gnu/usr.bin/gdb/readline/Makefile.gnu new file mode 100644 index 0000000000..dc1153984e --- /dev/null +++ b/gnu/usr.bin/gdb/readline/Makefile.gnu @@ -0,0 +1,114 @@ +## -*- text -*- #################################################### +# # +# Makefile for readline and history libraries. # +# # +#################################################################### + +# Here is a rule for making .o files from .c files that doesn't force +# the type of the machine (like -sun3) into the flags. +.c.o: + $(CC) -c $(CFLAGS) $(LOCAL_INCLUDES) $(CPPFLAGS) $*.c + +# Destination installation directory. The libraries are copied to DESTDIR +# when you do a `make install', and the header files to INCDIR/readline/*.h. +DESTDIR = /usr/gnu/lib +INCDIR = /usr/gnu/include + +# Define TYPES as -DVOID_SIGHANDLER if your operating system uses +# a return type of "void" for signal handlers. +TYPES = -DVOID_SIGHANDLER + +# Define SYSV as -DSYSV if you are using a System V operating system. +#SYSV = -DSYSV + +# HP-UX compilation requires the BSD library. +#LOCAL_LIBS = -lBSD + +# Xenix compilation requires -ldir -lx +#LOCAL_LIBS = -ldir -lx + +# Comment this out if you don't think that anyone will ever desire +# the vi line editing mode and features. +READLINE_DEFINES = -DVI_MODE + +DEBUG_FLAGS = -g +LDFLAGS = $(DEBUG_FLAGS) +CFLAGS = $(DEBUG_FLAGS) $(TYPE) $(SYSV) -I. + +# A good alternative is gcc -traditional. +#CC = gcc -traditional +CC = cc +RANLIB = /usr/bin/ranlib +AR = ar +RM = rm +CP = cp + +LOCAL_INCLUDES = -I../ + +CSOURCES = readline.c history.c funmap.c keymaps.c vi_mode.c \ + emacs_keymap.c vi_keymap.c keymaps.c + +HSOURCES = readline.h chardefs.h history.h keymaps.h +SOURCES = $(CSOURCES) $(HSOURCES) + +DOCUMENTATION = readline.texinfo inc-readline.texinfo \ + history.texinfo inc-history.texinfo + +SUPPORT = COPYING Makefile $(DOCUMENTATION) ChangeLog + +THINGS_TO_TAR = $(SOURCES) $(SUPPORT) + +########################################################################## + +all: libreadline.a + +libreadline.a: readline.o history.o funmap.o keymaps.o + $(RM) -f libreadline.a + $(AR) clq libreadline.a readline.o history.o funmap.o keymaps.o + if [ -f $(RANLIB) ]; then $(RANLIB) libreadline.a; fi + +readline.o: readline.h chardefs.h keymaps.h history.h readline.c vi_mode.c + $(CC) -c $(CFLAGS) $(CPPFLAGS) $(READLINE_DEFINES) \ + $(LOCAL_INCLUDES) $*.c + +history.o: history.c history.h + $(CC) -c $(CFLAGS) $(CPPFLAGS) $(READLINE_DEFINES) \ + $(LOCAL_INCLUDES) $*.c + +funmap.o: readline.h + $(CC) -c $(CFLAGS) $(CPPFLAGS) $(READLINE_DEFINES) \ + $(LOCAL_INCLUDES) $*.c + +keymaps.o: emacs_keymap.c vi_keymap.c keymaps.h chardefs.h keymaps.c + $(CC) -c $(CFLAGS) $(CPPFLAGS) $(READLINE_DEFINES) \ + $(LOCAL_INCLUDES) $*.c + +libtest: libreadline.a libtest.c + $(CC) -o libtest $(CFLAGS) $(CPPFLAGS) -L. libtest.c -lreadline -ltermcap + +readline: readline.c history.o keymaps.o funmap.o readline.h chardefs.h + $(CC) $(CFLAGS) $(CPPFLAGS) $(READLINE_DEFINES) \ + $(LOCAL_INCLUDES) -DTEST -o readline readline.c funmap.o \ + keymaps.o history.o -L. -ltermcap + +readline.tar: $(THINGS_TO_TAR) + tar -cf readline.tar $(THINGS_TO_TAR) + +readline.tar.Z: readline.tar + compress -f readline.tar + +install: $(DESTDIR)/libreadline.a includes + +includes: + if [ ! -r $(INCDIR)/readline ]; then\ + mkdir $(INCDIR)/readline;\ + chmod a+r $(INCDIR)/readline;\ + fi + $(CP) readline.h keymaps.h chardefs.h $(INCDIR)/readline/ +clean: + rm -f *.o *.a *.log *.cp *.tp *.vr *.fn *.aux *.pg *.toc + +$(DESTDIR)/libreadline.a: libreadline.a + -mv $(DESTDIR)/libreadline.a $(DESTDIR)/libreadline.old + cp libreadline.a $(DESTDIR)/libreadline.a + $(RANLIB) -t $(DESTDIR)/libreadline.a diff --git a/gnu/usr.bin/gdb/readline/chardefs.h b/gnu/usr.bin/gdb/readline/chardefs.h new file mode 100644 index 0000000000..9749ae489f --- /dev/null +++ b/gnu/usr.bin/gdb/readline/chardefs.h @@ -0,0 +1,50 @@ +/* chardefs.h -- Character definitions for readline. */ +#ifndef _CHARDEFS_ + +#ifndef savestring +#define savestring(x) (char *)strcpy (xmalloc (1 + strlen (x)), (x)) +#endif + +#ifndef whitespace +#define whitespace(c) (((c) == ' ') || ((c) == '\t')) +#endif + +#ifdef CTRL +#undef CTRL +#endif + +/* Some character stuff. */ +#define control_character_threshold 0x020 /* smaller than this is control */ +#define meta_character_threshold 0x07f /* larger than this is Meta. */ +#define control_character_bit 0x40 /* 0x000000, must be off. */ +#define meta_character_bit 0x080 /* x0000000, must be on. */ + +#define CTRL(c) ((c) & (~control_character_bit)) +#define META(c) ((c) | meta_character_bit) + +#define UNMETA(c) ((c) & (~meta_character_bit)) +#define UNCTRL(c) to_upper(((c)|control_character_bit)) + +#define lowercase_p(c) (((c) > ('a' - 1) && (c) < ('z' + 1))) +#define uppercase_p(c) (((c) > ('A' - 1) && (c) < ('Z' + 1))) + +#define pure_alphabetic(c) (lowercase_p(c) || uppercase_p(c)) + +#ifndef to_upper +#define to_upper(c) (lowercase_p(c) ? ((c) - 32) : (c)) +#define to_lower(c) (uppercase_p(c) ? ((c) + 32) : (c)) +#endif + +#define CTRL_P(c) ((c) < control_character_threshold) +#define META_P(c) ((c) > meta_character_threshold) + +#define NEWLINE '\n' +#define RETURN CTRL('M') +#define RUBOUT 0x07f +#define TAB '\t' +#define ABORT_CHAR CTRL('G') +#define PAGE CTRL('L') +#define SPACE 0x020 +#define ESC CTRL('[') + +#endif /* _CHARDEFS_ */ diff --git a/gnu/usr.bin/gdb/readline/emacs_keymap.c b/gnu/usr.bin/gdb/readline/emacs_keymap.c new file mode 100644 index 0000000000..7030e69f6b --- /dev/null +++ b/gnu/usr.bin/gdb/readline/emacs_keymap.c @@ -0,0 +1,472 @@ +/* emacs_keymap.c -- the keymap for emacs_mode in readline (). */ + +/* Copyright (C) 1988,1989 Free Software Foundation, Inc. + + This file is part of GNU Readline, a library for reading lines + of text with interactive input and history editing. + + Readline is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 1, or (at your option) any + later version. + + Readline is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Readline; see the file COPYING. If not, write to the Free + Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef FILE +#include +#endif /* FILE */ + +#include "readline.h" + +/* An array of function pointers, one for each possible key. + If the type byte is ISKMAP, then the pointer is the address of + a keymap. */ + +KEYMAP_ENTRY_ARRAY emacs_standard_keymap = { + + /* Control keys. */ + { ISFUNC, (Function *)0x0 }, /* Control-@ */ + { ISFUNC, rl_beg_of_line }, /* Control-a */ + { ISFUNC, rl_backward }, /* Control-b */ + { ISFUNC, (Function *)0x0 }, /* Control-c */ + { ISFUNC, rl_delete }, /* Control-d */ + { ISFUNC, rl_end_of_line }, /* Control-e */ + { ISFUNC, rl_forward }, /* Control-f */ + { ISFUNC, rl_abort }, /* Control-g */ + { ISFUNC, rl_backward }, /* Control-h */ + { ISFUNC, rl_complete }, /* Control-i */ + { ISFUNC, rl_newline }, /* Control-j */ + { ISFUNC, rl_kill_line }, /* Control-k */ + { ISFUNC, rl_clear_screen }, /* Control-l */ + { ISFUNC, rl_newline }, /* Control-m */ + { ISFUNC, rl_get_next_history }, /* Control-n */ + { ISFUNC, (Function *)0x0 }, /* Control-o */ + { ISFUNC, rl_get_previous_history }, /* Control-p */ + { ISFUNC, rl_quoted_insert }, /* Control-q */ + { ISFUNC, rl_reverse_search_history }, /* Control-r */ + { ISFUNC, rl_forward_search_history }, /* Control-s */ + { ISFUNC, rl_transpose_chars }, /* Control-t */ + { ISFUNC, rl_unix_line_discard }, /* Control-u */ + { ISFUNC, rl_quoted_insert }, /* Control-v */ + { ISFUNC, rl_unix_word_rubout }, /* Control-w */ + { ISKMAP, (Function *)emacs_ctlx_keymap }, /* Control-x */ + { ISFUNC, rl_yank }, /* Control-y */ + { ISFUNC, (Function *)0x0 }, /* Control-z */ + { ISKMAP, (Function *)emacs_meta_keymap }, /* Control-[ */ + { ISFUNC, (Function *)0x0 }, /* Control-\ */ + { ISFUNC, (Function *)0x0 }, /* Control-] */ + { ISFUNC, (Function *)0x0 }, /* Control-^ */ + { ISFUNC, rl_undo_command }, /* Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, rl_insert }, /* SPACE */ + { ISFUNC, rl_insert }, /* ! */ + { ISFUNC, rl_insert }, /* " */ + { ISFUNC, rl_insert }, /* # */ + { ISFUNC, rl_insert }, /* $ */ + { ISFUNC, rl_insert }, /* % */ + { ISFUNC, rl_insert }, /* & */ + { ISFUNC, rl_insert }, /* ' */ + { ISFUNC, rl_insert }, /* ( */ + { ISFUNC, rl_insert }, /* ) */ + { ISFUNC, rl_insert }, /* * */ + { ISFUNC, rl_insert }, /* + */ + { ISFUNC, rl_insert }, /* , */ + { ISFUNC, rl_insert }, /* - */ + { ISFUNC, rl_insert }, /* . */ + { ISFUNC, rl_insert }, /* / */ + + /* Regular digits. */ + { ISFUNC, rl_insert }, /* 0 */ + { ISFUNC, rl_insert }, /* 1 */ + { ISFUNC, rl_insert }, /* 2 */ + { ISFUNC, rl_insert }, /* 3 */ + { ISFUNC, rl_insert }, /* 4 */ + { ISFUNC, rl_insert }, /* 5 */ + { ISFUNC, rl_insert }, /* 6 */ + { ISFUNC, rl_insert }, /* 7 */ + { ISFUNC, rl_insert }, /* 8 */ + { ISFUNC, rl_insert }, /* 9 */ + + /* A little more punctuation. */ + { ISFUNC, rl_insert }, /* : */ + { ISFUNC, rl_insert }, /* ; */ + { ISFUNC, rl_insert }, /* < */ + { ISFUNC, rl_insert }, /* = */ + { ISFUNC, rl_insert }, /* > */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* @ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_insert }, /* A */ + { ISFUNC, rl_insert }, /* B */ + { ISFUNC, rl_insert }, /* C */ + { ISFUNC, rl_insert }, /* D */ + { ISFUNC, rl_insert }, /* E */ + { ISFUNC, rl_insert }, /* F */ + { ISFUNC, rl_insert }, /* G */ + { ISFUNC, rl_insert }, /* H */ + { ISFUNC, rl_insert }, /* I */ + { ISFUNC, rl_insert }, /* J */ + { ISFUNC, rl_insert }, /* K */ + { ISFUNC, rl_insert }, /* L */ + { ISFUNC, rl_insert }, /* M */ + { ISFUNC, rl_insert }, /* N */ + { ISFUNC, rl_insert }, /* O */ + { ISFUNC, rl_insert }, /* P */ + { ISFUNC, rl_insert }, /* Q */ + { ISFUNC, rl_insert }, /* R */ + { ISFUNC, rl_insert }, /* S */ + { ISFUNC, rl_insert }, /* T */ + { ISFUNC, rl_insert }, /* U */ + { ISFUNC, rl_insert }, /* V */ + { ISFUNC, rl_insert }, /* W */ + { ISFUNC, rl_insert }, /* X */ + { ISFUNC, rl_insert }, /* Y */ + { ISFUNC, rl_insert }, /* Z */ + + /* Some more punctuation. */ + { ISFUNC, rl_insert }, /* [ */ + { ISFUNC, rl_insert }, /* \ */ + { ISFUNC, rl_insert }, /* ] */ + { ISFUNC, rl_insert }, /* ^ */ + { ISFUNC, rl_insert }, /* _ */ + { ISFUNC, rl_insert }, /* ` */ + + /* Lowercase alphabet. */ + { ISFUNC, rl_insert }, /* a */ + { ISFUNC, rl_insert }, /* b */ + { ISFUNC, rl_insert }, /* c */ + { ISFUNC, rl_insert }, /* d */ + { ISFUNC, rl_insert }, /* e */ + { ISFUNC, rl_insert }, /* f */ + { ISFUNC, rl_insert }, /* g */ + { ISFUNC, rl_insert }, /* h */ + { ISFUNC, rl_insert }, /* i */ + { ISFUNC, rl_insert }, /* j */ + { ISFUNC, rl_insert }, /* k */ + { ISFUNC, rl_insert }, /* l */ + { ISFUNC, rl_insert }, /* m */ + { ISFUNC, rl_insert }, /* n */ + { ISFUNC, rl_insert }, /* o */ + { ISFUNC, rl_insert }, /* p */ + { ISFUNC, rl_insert }, /* q */ + { ISFUNC, rl_insert }, /* r */ + { ISFUNC, rl_insert }, /* s */ + { ISFUNC, rl_insert }, /* t */ + { ISFUNC, rl_insert }, /* u */ + { ISFUNC, rl_insert }, /* v */ + { ISFUNC, rl_insert }, /* w */ + { ISFUNC, rl_insert }, /* x */ + { ISFUNC, rl_insert }, /* y */ + { ISFUNC, rl_insert }, /* z */ + + /* Final punctuation. */ + { ISFUNC, rl_insert }, /* { */ + { ISFUNC, rl_insert }, /* | */ + { ISFUNC, rl_insert }, /* } */ + { ISFUNC, rl_insert }, /* ~ */ + { ISFUNC, rl_rubout } /* RUBOUT */ +}; + +KEYMAP_ENTRY_ARRAY emacs_meta_keymap = { + + /* Meta keys. Just like above, but the high bit is set. */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-@ */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-a */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-b */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-c */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-d */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-e */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-f */ + { ISFUNC, rl_abort }, /* Meta-Control-g */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-h */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-i */ + { ISFUNC, rl_vi_editing_mode }, /* Meta-Control-j */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-k */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-l */ + { ISFUNC, rl_vi_editing_mode }, /* Meta-Control-m */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-n */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-o */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-p */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-q */ + { ISFUNC, rl_revert_line }, /* Meta-Control-r */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-s */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-t */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-u */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-v */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-w */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-x */ + { ISFUNC, rl_yank_nth_arg }, /* Meta-Control-y */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-z */ + + { ISFUNC, (Function *)0x0 }, /* Meta-Control-[ */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-\ */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-] */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-^ */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, (Function *)0x0 }, /* Meta-SPACE */ + { ISFUNC, (Function *)0x0 }, /* Meta-! */ + { ISFUNC, (Function *)0x0 }, /* Meta-" */ + { ISFUNC, (Function *)0x0 }, /* Meta-# */ + { ISFUNC, (Function *)0x0 }, /* Meta-$ */ + { ISFUNC, (Function *)0x0 }, /* Meta-% */ + { ISFUNC, (Function *)0x0 }, /* Meta-& */ + { ISFUNC, (Function *)0x0 }, /* Meta-' */ + { ISFUNC, (Function *)0x0 }, /* Meta-( */ + { ISFUNC, (Function *)0x0 }, /* Meta-) */ + { ISFUNC, (Function *)0x0 }, /* Meta-* */ + { ISFUNC, (Function *)0x0 }, /* Meta-+ */ + { ISFUNC, (Function *)0x0 }, /* Meta-, */ + { ISFUNC, rl_digit_argument }, /* Meta-- */ + { ISFUNC, (Function *)0x0 }, /* Meta-. */ + { ISFUNC, (Function *)0x0 }, /* Meta-/ */ + + /* Regular digits. */ + { ISFUNC, rl_digit_argument }, /* Meta-0 */ + { ISFUNC, rl_digit_argument }, /* Meta-1 */ + { ISFUNC, rl_digit_argument }, /* Meta-2 */ + { ISFUNC, rl_digit_argument }, /* Meta-3 */ + { ISFUNC, rl_digit_argument }, /* Meta-4 */ + { ISFUNC, rl_digit_argument }, /* Meta-5 */ + { ISFUNC, rl_digit_argument }, /* Meta-6 */ + { ISFUNC, rl_digit_argument }, /* Meta-7 */ + { ISFUNC, rl_digit_argument }, /* Meta-8 */ + { ISFUNC, rl_digit_argument }, /* Meta-9 */ + + /* A little more punctuation. */ + { ISFUNC, (Function *)0x0 }, /* Meta-: */ + { ISFUNC, (Function *)0x0 }, /* Meta-; */ + { ISFUNC, rl_beginning_of_history }, /* Meta-< */ + { ISFUNC, (Function *)0x0 }, /* Meta-= */ + { ISFUNC, rl_end_of_history }, /* Meta-> */ + { ISFUNC, rl_possible_completions }, /* Meta-? */ + { ISFUNC, (Function *)0x0 }, /* Meta-@ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-A */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-B */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-C */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-D */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-E */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-F */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-G */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-H */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-I */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-J */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-K */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-L */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-M */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-N */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-O */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-P */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-Q */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-R */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-S */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-T */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-U */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-V */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-W */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-X */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-Y */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-Z */ + + /* Some more punctuation. */ + { ISFUNC, (Function *)0x0 }, /* Meta-[ */ + { ISFUNC, (Function *)0x0 }, /* Meta-\ */ + { ISFUNC, (Function *)0x0 }, /* Meta-] */ + { ISFUNC, (Function *)0x0 }, /* Meta-^ */ + { ISFUNC, (Function *)0x0 }, /* Meta-_ */ + { ISFUNC, (Function *)0x0 }, /* Meta-` */ + + /* Lowercase alphabet. */ + { ISFUNC, (Function *)0x0 }, /* Meta-a */ + { ISFUNC, rl_backward_word }, /* Meta-b */ + { ISFUNC, rl_capitalize_word }, /* Meta-c */ + { ISFUNC, rl_kill_word }, /* Meta-d */ + { ISFUNC, (Function *)0x0 }, /* Meta-e */ + { ISFUNC, rl_forward_word }, /* Meta-f */ + { ISFUNC, (Function *)0x0 }, /* Meta-g */ + { ISFUNC, (Function *)0x0 }, /* Meta-h */ + { ISFUNC, (Function *)0x0 }, /* Meta-i */ + { ISFUNC, (Function *)0x0 }, /* Meta-j */ + { ISFUNC, (Function *)0x0 }, /* Meta-k */ + { ISFUNC, rl_downcase_word }, /* Meta-l */ + { ISFUNC, (Function *)0x0 }, /* Meta-m */ + { ISFUNC, (Function *)0x0 }, /* Meta-n */ + { ISFUNC, (Function *)0x0 }, /* Meta-o */ + { ISFUNC, (Function *)0x0 }, /* Meta-p */ + { ISFUNC, (Function *)0x0 }, /* Meta-q */ + { ISFUNC, rl_revert_line }, /* Meta-r */ + { ISFUNC, (Function *)0x0 }, /* Meta-s */ + { ISFUNC, rl_transpose_words }, /* Meta-t */ + { ISFUNC, rl_upcase_word }, /* Meta-u */ + { ISFUNC, (Function *)0x0 }, /* Meta-v */ + { ISFUNC, (Function *)0x0 }, /* Meta-w */ + { ISFUNC, (Function *)0x0 }, /* Meta-x */ + { ISFUNC, rl_yank_pop }, /* Meta-y */ + { ISFUNC, (Function *)0x0 }, /* Meta-z */ + + /* Final punctuation. */ + { ISFUNC, (Function *)0x0 }, /* Meta-{ */ + { ISFUNC, (Function *)0x0 }, /* Meta-| */ + { ISFUNC, (Function *)0x0 }, /* Meta-} */ + { ISFUNC, (Function *)0x0 }, /* Meta-~ */ + { ISFUNC, rl_backward_kill_word } /* Meta-rubout */ +}; + +KEYMAP_ENTRY_ARRAY emacs_ctlx_keymap = { + + /* Control keys. */ + { ISFUNC, (Function *)0x0 }, /* Control-@ */ + { ISFUNC, (Function *)0x0 }, /* Control-a */ + { ISFUNC, (Function *)0x0 }, /* Control-b */ + { ISFUNC, (Function *)0x0 }, /* Control-c */ + { ISFUNC, (Function *)0x0 }, /* Control-d */ + { ISFUNC, (Function *)0x0 }, /* Control-e */ + { ISFUNC, (Function *)0x0 }, /* Control-f */ + { ISFUNC, rl_abort }, /* Control-g */ + { ISFUNC, (Function *)0x0 }, /* Control-h */ + { ISFUNC, (Function *)0x0 }, /* Control-i */ + { ISFUNC, (Function *)0x0 }, /* Control-j */ + { ISFUNC, (Function *)0x0 }, /* Control-k */ + { ISFUNC, (Function *)0x0 }, /* Control-l */ + { ISFUNC, (Function *)0x0 }, /* Control-m */ + { ISFUNC, (Function *)0x0 }, /* Control-n */ + { ISFUNC, (Function *)0x0 }, /* Control-o */ + { ISFUNC, (Function *)0x0 }, /* Control-p */ + { ISFUNC, (Function *)0x0 }, /* Control-q */ + { ISFUNC, rl_re_read_init_file }, /* Control-r */ + { ISFUNC, (Function *)0x0 }, /* Control-s */ + { ISFUNC, (Function *)0x0 }, /* Control-t */ + { ISFUNC, rl_undo_command }, /* Control-u */ + { ISFUNC, (Function *)0x0 }, /* Control-v */ + { ISFUNC, (Function *)0x0 }, /* Control-w */ + { ISFUNC, (Function *)0x0 }, /* Control-x */ + { ISFUNC, (Function *)0x0 }, /* Control-y */ + { ISFUNC, (Function *)0x0 }, /* Control-z */ + { ISFUNC, (Function *)0x0 }, /* Control-[ */ + { ISFUNC, (Function *)0x0 }, /* Control-\ */ + { ISFUNC, (Function *)0x0 }, /* Control-] */ + { ISFUNC, (Function *)0x0 }, /* Control-^ */ + { ISFUNC, (Function *)0x0 }, /* Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, (Function *)0x0 }, /* SPACE */ + { ISFUNC, (Function *)0x0 }, /* ! */ + { ISFUNC, (Function *)0x0 }, /* " */ + { ISFUNC, (Function *)0x0 }, /* # */ + { ISFUNC, (Function *)0x0 }, /* $ */ + { ISFUNC, (Function *)0x0 }, /* % */ + { ISFUNC, (Function *)0x0 }, /* & */ + { ISFUNC, (Function *)0x0 }, /* ' */ + { ISFUNC, rl_start_kbd_macro }, /* ( */ + { ISFUNC, rl_end_kbd_macro }, /* ) */ + { ISFUNC, (Function *)0x0 }, /* * */ + { ISFUNC, (Function *)0x0 }, /* + */ + { ISFUNC, (Function *)0x0 }, /* , */ + { ISFUNC, (Function *)0x0 }, /* - */ + { ISFUNC, (Function *)0x0 }, /* . */ + { ISFUNC, (Function *)0x0 }, /* / */ + + /* Regular digits. */ + { ISFUNC, (Function *)0x0 }, /* 0 */ + { ISFUNC, (Function *)0x0 }, /* 1 */ + { ISFUNC, (Function *)0x0 }, /* 2 */ + { ISFUNC, (Function *)0x0 }, /* 3 */ + { ISFUNC, (Function *)0x0 }, /* 4 */ + { ISFUNC, (Function *)0x0 }, /* 5 */ + { ISFUNC, (Function *)0x0 }, /* 6 */ + { ISFUNC, (Function *)0x0 }, /* 7 */ + { ISFUNC, (Function *)0x0 }, /* 8 */ + { ISFUNC, (Function *)0x0 }, /* 9 */ + + /* A little more punctuation. */ + { ISFUNC, (Function *)0x0 }, /* : */ + { ISFUNC, (Function *)0x0 }, /* ; */ + { ISFUNC, (Function *)0x0 }, /* < */ + { ISFUNC, (Function *)0x0 }, /* = */ + { ISFUNC, (Function *)0x0 }, /* > */ + { ISFUNC, (Function *)0x0 }, /* ? */ + { ISFUNC, (Function *)0x0 }, /* @ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_do_lowercase_version }, /* A */ + { ISFUNC, rl_do_lowercase_version }, /* B */ + { ISFUNC, rl_do_lowercase_version }, /* C */ + { ISFUNC, rl_do_lowercase_version }, /* D */ + { ISFUNC, rl_do_lowercase_version }, /* E */ + { ISFUNC, rl_do_lowercase_version }, /* F */ + { ISFUNC, rl_do_lowercase_version }, /* G */ + { ISFUNC, rl_do_lowercase_version }, /* H */ + { ISFUNC, rl_do_lowercase_version }, /* I */ + { ISFUNC, rl_do_lowercase_version }, /* J */ + { ISFUNC, rl_do_lowercase_version }, /* K */ + { ISFUNC, rl_do_lowercase_version }, /* L */ + { ISFUNC, rl_do_lowercase_version }, /* M */ + { ISFUNC, rl_do_lowercase_version }, /* N */ + { ISFUNC, rl_do_lowercase_version }, /* O */ + { ISFUNC, rl_do_lowercase_version }, /* P */ + { ISFUNC, rl_do_lowercase_version }, /* Q */ + { ISFUNC, rl_do_lowercase_version }, /* R */ + { ISFUNC, rl_do_lowercase_version }, /* S */ + { ISFUNC, rl_do_lowercase_version }, /* T */ + { ISFUNC, rl_do_lowercase_version }, /* U */ + { ISFUNC, rl_do_lowercase_version }, /* V */ + { ISFUNC, rl_do_lowercase_version }, /* W */ + { ISFUNC, rl_do_lowercase_version }, /* X */ + { ISFUNC, rl_do_lowercase_version }, /* Y */ + { ISFUNC, rl_do_lowercase_version }, /* Z */ + + /* Some more punctuation. */ + { ISFUNC, (Function *)0x0 }, /* [ */ + { ISFUNC, (Function *)0x0 }, /* \ */ + { ISFUNC, (Function *)0x0 }, /* ] */ + { ISFUNC, (Function *)0x0 }, /* ^ */ + { ISFUNC, (Function *)0x0 }, /* _ */ + { ISFUNC, (Function *)0x0 }, /* ` */ + + /* Lowercase alphabet. */ + { ISFUNC, (Function *)0x0 }, /* a */ + { ISFUNC, (Function *)0x0 }, /* b */ + { ISFUNC, (Function *)0x0 }, /* c */ + { ISFUNC, (Function *)0x0 }, /* d */ + { ISFUNC, rl_call_last_kbd_macro }, /* e */ + { ISFUNC, (Function *)0x0 }, /* f */ + { ISFUNC, (Function *)0x0 }, /* g */ + { ISFUNC, (Function *)0x0 }, /* h */ + { ISFUNC, (Function *)0x0 }, /* i */ + { ISFUNC, (Function *)0x0 }, /* j */ + { ISFUNC, (Function *)0x0 }, /* k */ + { ISFUNC, (Function *)0x0 }, /* l */ + { ISFUNC, (Function *)0x0 }, /* m */ + { ISFUNC, (Function *)0x0 }, /* n */ + { ISFUNC, (Function *)0x0 }, /* o */ + { ISFUNC, (Function *)0x0 }, /* p */ + { ISFUNC, (Function *)0x0 }, /* q */ + { ISFUNC, rl_re_read_init_file }, /* r */ + { ISFUNC, (Function *)0x0 }, /* s */ + { ISFUNC, (Function *)0x0 }, /* t */ + { ISFUNC, (Function *)0x0 }, /* u */ + { ISFUNC, (Function *)0x0 }, /* v */ + { ISFUNC, (Function *)0x0 }, /* w */ + { ISFUNC, (Function *)0x0 }, /* x */ + { ISFUNC, (Function *)0x0 }, /* y */ + { ISFUNC, (Function *)0x0 }, /* z */ + + /* Final punctuation. */ + { ISFUNC, (Function *)0x0 }, /* { */ + { ISFUNC, (Function *)0x0 }, /* | */ + { ISFUNC, (Function *)0x0 }, /* } */ + { ISFUNC, (Function *)0x0 }, /* ~ */ + { ISFUNC, rl_backward_kill_line } /* RUBOUT */ +}; diff --git a/gnu/usr.bin/gdb/readline/funmap.c b/gnu/usr.bin/gdb/readline/funmap.c new file mode 100644 index 0000000000..357e716f20 --- /dev/null +++ b/gnu/usr.bin/gdb/readline/funmap.c @@ -0,0 +1,217 @@ +/* funmap.c -- attach names to functions. */ + +/* Copyright (C) 1988,1989 Free Software Foundation, Inc. + + This file is part of GNU Readline, a library for reading lines + of text with interactive input and history editing. + + Readline is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 1, or (at your option) any + later version. + + Readline is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Readline; see the file COPYING. If not, write to the Free + Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#define STATIC_MALLOC +#ifndef STATIC_MALLOC +extern char *xmalloc (), *xrealloc (); +#else +static char *xmalloc (), *xrealloc (); +#endif + +#ifndef FILE +#include +#endif /* FILE */ + +#include "readline.h" + +FUNMAP **funmap = (FUNMAP **)NULL; +static int funmap_size = 0; + +static int just_testing_ar_tmp = 0; +static int just_testing_ar_tmp_2 = 5; +int foo_testing_ar; + +static int funmap_entry = 0; + +static FUNMAP default_funmap[] = { + { "beginning-of-line", rl_beg_of_line }, + { "backward-char", rl_backward }, + { "delete-char", rl_delete }, + { "end-of-line", rl_end_of_line }, + { "forward-char", rl_forward }, + { "accept-line", rl_newline }, + { "kill-line", rl_kill_line }, + { "clear-screen", rl_clear_screen }, + { "next-history", rl_get_next_history }, + { "previous-history", rl_get_previous_history }, + { "quoted-insert", rl_quoted_insert }, + { "reverse-search-history", rl_reverse_search_history }, + { "forward-search-history", rl_forward_search_history }, + { "transpose-chars", rl_transpose_chars }, + { "unix-line-discard", rl_unix_line_discard }, + { "unix-word-rubout", rl_unix_word_rubout }, + { "yank", rl_yank }, + { "yank-pop", rl_yank_pop }, + { "yank-nth-arg", rl_yank_nth_arg }, + { "backward-delete-char", rl_rubout }, + { "backward-word", rl_backward_word }, + { "kill-word", rl_kill_word }, + { "forward-word", rl_forward_word }, + { "tab-insert", rl_tab_insert }, + { "backward-kill-word", rl_backward_kill_word }, + { "backward-kill-line", rl_backward_kill_line }, + { "transpose-words", rl_transpose_words }, + { "digit-argument", rl_digit_argument }, + { "complete", rl_complete }, + { "possible-completions", rl_possible_completions }, + { "do-lowercase-version", rl_do_lowercase_version }, + { "digit-argument", rl_digit_argument }, + { "universal-argument", rl_universal_argument }, + { "abort", rl_abort }, + { "undo", rl_undo_command }, + { "upcase-word", rl_upcase_word }, + { "downcase-word", rl_downcase_word }, + { "capitalize-word", rl_capitalize_word }, + { "revert-line", rl_revert_line }, + { "beginning-of-history", rl_beginning_of_history }, + { "end-of-history", rl_end_of_history }, + { "self-insert", rl_insert }, + { "start-kbd-macro", rl_start_kbd_macro }, + { "end-kbd-macro", rl_end_kbd_macro }, + { "re-read-init-file", rl_re_read_init_file }, +#ifdef VI_MODE + { "vi-movement-mode", rl_vi_movement_mode }, + { "vi-insertion-mode", rl_vi_insertion_mode }, + { "vi-arg-digit", rl_vi_arg_digit }, + { "vi-prev-word", rl_vi_prev_word }, + { "vi-next-word", rl_vi_next_word }, + { "vi-char-search", rl_vi_char_search }, + { "vi-editing-mode", rl_vi_editing_mode }, + { "vi-eof-maybe", rl_vi_eof_maybe }, + { "vi-append-mode", rl_vi_append_mode }, + { "vi-put", rl_vi_put }, + { "vi-append-eol", rl_vi_append_eol }, + { "vi-insert-beg", rl_vi_insert_beg }, + { "vi-delete", rl_vi_delete }, + { "vi-comment", rl_vi_comment }, + { "vi-first-print", rl_vi_first_print }, + { "vi-fword", rl_vi_fword }, + { "vi-fWord", rl_vi_fWord }, + { "vi-bword", rl_vi_bword }, + { "vi-bWord", rl_vi_bWord }, + { "vi-eword", rl_vi_eword }, + { "vi-eWord", rl_vi_eWord }, + { "vi-end-word", rl_vi_end_word }, + { "vi-change-case", rl_vi_change_case }, + { "vi-match", rl_vi_match }, + { "vi-bracktype", rl_vi_bracktype }, + { "vi-change-char", rl_vi_change_char }, + { "vi-yank-arg", rl_vi_yank_arg }, + { "vi-search", rl_vi_search }, + { "vi-search-again", rl_vi_search_again }, + { "vi-dosearch", rl_vi_dosearch }, + { "vi-subst", rl_vi_subst }, + { "vi-overstrike", rl_vi_overstrike }, + { "vi-overstrike-delete", rl_vi_overstrike_delete }, + { "vi-replace, ", rl_vi_replace }, + { "vi-column", rl_vi_column }, + { "vi-delete-to", rl_vi_delete_to }, + { "vi-change-to", rl_vi_change_to }, + { "vi-yank-to", rl_vi_yank_to }, + { "vi-complete", rl_vi_complete }, +#endif /* VI_MODE */ + + {(char *)NULL, (Function *)NULL } +}; + +rl_add_funmap_entry (name, function) + char *name; + Function *function; +{ + if (funmap_entry + 2 >= funmap_size) + if (!funmap) + funmap = (FUNMAP **)xmalloc ((funmap_size = 80) * sizeof (FUNMAP *)); + else + funmap = + (FUNMAP **)xrealloc (funmap, (funmap_size += 80) * sizeof (FUNMAP *)); + + funmap[funmap_entry] = (FUNMAP *)xmalloc (sizeof (FUNMAP)); + funmap[funmap_entry]->name = name; + funmap[funmap_entry]->function = function; + + funmap[++funmap_entry] = (FUNMAP *)NULL; +} + +static int funmap_initialized = 0; + +/* Make the funmap contain all of the default entries. */ +rl_initialize_funmap () +{ + register int i; + + if (funmap_initialized) + return; + + for (i = 0; default_funmap[i].name; i++) + rl_add_funmap_entry (default_funmap[i].name, default_funmap[i].function); + + funmap_initialized = 1; +} + +/* Things that mean `Control'. */ +char *possible_control_prefixes[] = { + "Control-", "C-", "CTRL-", (char *)NULL +}; + +char *possible_meta_prefixes[] = { + "Meta", "M-", (char *)NULL +}; + +#ifdef STATIC_MALLOC + +/* **************************************************************** */ +/* */ +/* xmalloc and xrealloc () */ +/* */ +/* **************************************************************** */ + +static char * +xmalloc (bytes) + int bytes; +{ + static memory_error_and_abort (); + char *temp = (char *)malloc (bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static char * +xrealloc (pointer, bytes) + char *pointer; + int bytes; +{ + static memory_error_and_abort (); + char *temp = (char *)realloc (pointer, bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static +memory_error_and_abort () +{ + fprintf (stderr, "history: Out of virtual memory!\n"); + abort (); +} +#endif /* STATIC_MALLOC */ diff --git a/gnu/usr.bin/gdb/readline/history.c b/gnu/usr.bin/gdb/readline/history.c new file mode 100644 index 0000000000..7087718edc --- /dev/null +++ b/gnu/usr.bin/gdb/readline/history.c @@ -0,0 +1,1462 @@ +/* History.c -- standalone history library */ + +/* Copyright (C) 1989 Free Software Foundation, Inc. + + This file contains the GNU History Library (the Library), a set of + routines for managing the text of previously typed lines. + + The Library is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + The Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + The GNU General Public License is often shipped with GNU software, and + is generally kept in a file called COPYING or LICENSE. If you do not + have a copy of the license, write to the Free Software Foundation, + 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* The goal is to make the implementation transparent, so that you + don't have to know what data types are used, just what functions + you can call. I think I have done that. */ + +/* Remove these declarations when we have a complete libgnu.a. */ +#define STATIC_MALLOC +#ifndef STATIC_MALLOC +extern char *xmalloc (), *xrealloc (); +#else +static char *xmalloc (), *xrealloc (); +#endif + +#include + +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +#if defined (sparc) && defined (sun) +#include +#else +extern char *alloca (); +#endif +#endif + +#include "history.h" + +#ifndef savestring +#define savestring(x) (char *)strcpy (xmalloc (1 + strlen (x)), (x)) +#endif + +#ifndef whitespace +#define whitespace(c) (((c) == ' ') || ((c) == '\t')) +#endif + +#ifndef digit +#define digit(c) ((c) >= '0' && (c) <= '9') +#endif + +#ifndef member +#define member(c, s) ((c) ? index ((s), (c)) : 0) +#endif + +/* **************************************************************** */ +/* */ +/* History functions */ +/* */ +/* **************************************************************** */ + +/* An array of HIST_ENTRY. This is where we store the history. */ +static HIST_ENTRY **the_history = (HIST_ENTRY **)NULL; + +/* Non-zero means that we have enforced a limit on the amount of + history that we save. */ +static int history_stifled = 0; + +/* If HISTORY_STIFLED is non-zero, then this is the maximum number of + entries to remember. */ +static int max_input_history; + +/* The current location of the interactive history pointer. Just makes + life easier for outside callers. */ +static int history_offset = 0; + +/* The number of strings currently stored in the input_history list. */ +static int history_length = 0; + +/* The current number of slots allocated to the input_history. */ +static int history_size = 0; + +/* The number of slots to increase the_history by. */ +#define DEFAULT_HISTORY_GROW_SIZE 50 + +/* The character that represents the start of a history expansion + request. This is usually `!'. */ +char history_expansion_char = '!'; + +/* The character that invokes word substitution if found at the start of + a line. This is usually `^'. */ +char history_subst_char = '^'; + +/* During tokenization, if this character is seen as the first character + of a word, then it, and all subsequent characters upto a newline are + ignored. For a Bourne shell, this should be '#'. Bash special cases + the interactive comment character to not be a comment delimiter. */ +char history_comment_char = '\0'; + +/* The list of characters which inhibit the expansion of text if found + immediately following history_expansion_char. */ +char *history_no_expand_chars = " \t\n\r="; + +/* The logical `base' of the history array. It defaults to 1. */ +int history_base = 1; + +/* Begin a session in which the history functions might be used. This + initializes interactive variables. */ +void +using_history () +{ + history_offset = history_length; +} + +/* Place STRING at the end of the history list. The data field + is set to NULL. */ +void +add_history (string) + char *string; +{ + HIST_ENTRY *temp; + + if (history_stifled && (history_length == max_input_history)) { + register int i; + + /* If the history is stifled, and history_length is zero, + and it equals max_input_history, we don't save items. */ + if (!history_length) + return; + + /* If there is something in the slot, then remove it. */ + if (the_history[0]) { + free (the_history[0]->line); + free (the_history[0]); + } + + for (i = 0; i < history_length; i++) + the_history[i] = the_history[i + 1]; + + history_base++; + + } else { + + if (!history_size) { + the_history = + (HIST_ENTRY **)xmalloc ((history_size = DEFAULT_HISTORY_GROW_SIZE) + * sizeof (HIST_ENTRY *)); + history_length = 1; + + } else { + if (history_length == (history_size - 1)) { + the_history = + (HIST_ENTRY **)xrealloc (the_history, + ((history_size += DEFAULT_HISTORY_GROW_SIZE) + * sizeof (HIST_ENTRY *))); + } + history_length++; + } + } + + temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY)); + temp->line = savestring (string); + temp->data = (char *)NULL; + + the_history[history_length] = (HIST_ENTRY *)NULL; + the_history[history_length - 1] = temp; +} + +/* Make the history entry at WHICH have LINE and DATA. This returns + the old entry so you can dispose of the data. In the case of an + invalid WHICH, a NULL pointer is returned. */ +HIST_ENTRY * +replace_history_entry (which, line, data) + int which; + char *line; + char *data; +{ + HIST_ENTRY *temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY)); + HIST_ENTRY *old_value; + + if (which >= history_length) + return ((HIST_ENTRY *)NULL); + + old_value = the_history[which]; + + temp->line = savestring (line); + temp->data = data; + the_history[which] = temp; + + return (old_value); +} + +/* Returns the magic number which says what history element we are + looking at now. In this implementation, it returns history_offset. */ +int +where_history () +{ + return (history_offset); +} + +/* Search the history for STRING, starting at history_offset. + If DIRECTION < 0, then the search is through previous entries, + else through subsequent. If the string is found, then + current_history () is the history entry, and the value of this function + is the offset in the line of that history entry that the string was + found in. Otherwise, nothing is changed, and a -1 is returned. */ +int +history_search (string, direction) + char *string; + int direction; +{ + register int i = history_offset; + register int reverse = (direction < 0); + register char *line; + register int index; + int string_len = strlen (string); + + /* Take care of trivial cases first. */ + + if (!history_length || (i == history_length) && !reverse) + return (-1); + + if (reverse && (i == history_length)) + i--; + + while (1) + { + /* Search each line in the history list for STRING. */ + + /* At limit for direction? */ + if ((reverse && i < 0) || + (!reverse && i == history_length)) + return (-1); + + line = the_history[i]->line; + index = strlen (line); + + /* If STRING is longer than line, no match. */ + if (string_len > index) + goto next_line; + + /* Do the actual search. */ + if (reverse) + { + index -= string_len; + + while (index >= 0) + { + if (strncmp (string, line + index, string_len) == 0) + { + history_offset = i; + return (index); + } + index--; + } + } + else + { + register int limit = (string_len - index) + 1; + index = 0; + + while (index < limit) + { + if (strncmp (string, line + index, string_len) == 0) + { + history_offset = i; + return (index); + } + index++; + } + } + next_line: + if (reverse) + i--; + else + i++; + } +} + +/* Remove history element WHICH from the history. The removed + element is returned to you so you can free the line, data, + and containing structure. */ +HIST_ENTRY * +remove_history (which) + int which; +{ + HIST_ENTRY *return_value; + + if (which >= history_length || !history_length) + return_value = (HIST_ENTRY *)NULL; + else + { + register int i; + return_value = the_history[which]; + + for (i = which; i < history_length; i++) + the_history[i] = the_history[i + 1]; + + history_length--; + } + return (return_value); +} + +/* Stifle the history list, remembering only MAX number of lines. */ +void +stifle_history (max) + int max; +{ + if (history_length > max) + { + register int i, j; + + /* This loses because we cannot free the data. */ + for (i = 0; i < (history_length - max); i++) + { + free (the_history[i]->line); + free (the_history[i]); + } + history_base = i; + for (j = 0, i = history_length - max; j < max; i++, j++) + the_history[j] = the_history[i]; + the_history[j] = (HIST_ENTRY *)NULL; + history_length = j; + } + history_stifled = 1; + max_input_history = max; +} + +/* Stop stifling the history. This returns the previous amount the history + was stifled by. The value is positive if the history was stifled, negative + if it wasn't. */ +int +unstifle_history () +{ + int result = max_input_history; + if (history_stifled) + { + result = - result; + history_stifled = 0; + } + return (result); +} + +/* Return the string that should be used in the place of this + filename. This only matters when you don't specify the + filename to read_history (), or write_history (). */ +static char * +history_filename (filename) + char *filename; +{ + char *return_val = filename ? savestring (filename) : (char *)NULL; + + if (!return_val) + { + char *home = (char *)getenv ("HOME"); + if (!home) home = "."; + return_val = (char *)xmalloc (2 + strlen (home) + strlen (".history")); + strcpy (return_val, home); + strcat (return_val, "/"); + strcat (return_val, ".history"); + } + return (return_val); +} + +/* What to use until the line gets too big. */ +#define TYPICAL_LINE_SIZE 2048 + +/* Add the contents of FILENAME to the history list, a line at a time. + If FILENAME is NULL, then read from ~/.history. Returns 0 if + successful, or errno if not. */ +int +read_history (filename) + char *filename; +{ + char *input = history_filename (filename); + FILE *file = fopen (input, "r"); + char *line = (char *)xmalloc (TYPICAL_LINE_SIZE); + int line_size = TYPICAL_LINE_SIZE; + int done = 0; + + if (!file) + { + extern int errno; + free (line); + return (errno); + } + + while (!done) + { + int c; + int i; + + i = 0; + while (!(done = ((c = getc (file)) == EOF))) + { + if (c == '\n') + break; + + line [i++] = c; + if (i == line_size) + line = (char *)xrealloc (line, line_size += TYPICAL_LINE_SIZE); + } + line[i] = '\0'; + if (line[0]) + add_history (line); + } + free (line); + fclose (file); + return (0); +} + +/* Overwrite FILENAME with the current history. If FILENAME is NULL, + then write the history list to ~/.history. Values returned + are as in read_history ().*/ +int +write_history (filename) + char *filename; +{ + extern int errno; + char *output = history_filename (filename); + FILE *file = fopen (output, "w"); + register int i; + + if (!file) return (errno); + if (!history_length) return (0); + + for (i = 0; i < history_length; i++) + fprintf (file, "%s\n", the_history[i]->line); + + fclose (file); + return (0); +} + +/* Return the history entry at the current position, as determined by + history_offset. If there is no entry there, return a NULL pointer. */ +HIST_ENTRY * +current_history () +{ + if ((history_offset == history_length) || !the_history) + return ((HIST_ENTRY *)NULL); + else + return (the_history[history_offset]); +} + +/* Back up history_offset to the previous history entry, and return + a pointer to that entry. If there is no previous entry then return + a NULL pointer. */ +HIST_ENTRY * +previous_history () +{ + if (!history_offset) + return ((HIST_ENTRY *)NULL); + else + return (the_history[--history_offset]); +} + +/* Move history_offset forward to the next history entry, and return + a pointer to that entry. If there is no next entry then return a + NULL pointer. */ +HIST_ENTRY * +next_history () +{ + if (history_offset == history_length) + return ((HIST_ENTRY *)NULL); + else + return (the_history[++history_offset]); +} + +/* Return the current history array. The caller has to be carefull, since this + is the actual array of data, and could be bashed or made corrupt easily. + The array is terminated with a NULL pointer. */ +HIST_ENTRY ** +history_list () +{ + return (the_history); +} + +/* Return the history entry which is logically at OFFSET in the history array. + OFFSET is relative to history_base. */ +HIST_ENTRY * +history_get (offset) + int offset; +{ + int index = offset - history_base; + + if (index >= history_length || + index < 0 || + !the_history) + return ((HIST_ENTRY *)NULL); + return (the_history[index]); +} + +/* Search for STRING in the history list. DIR is < 0 for searching + backwards. POS is an absolute index into the history list at + which point to begin searching. */ +int +history_search_pos (string, dir, pos) + char *string; + int dir, pos; +{ + int ret, old = where_history (); + history_set_pos (pos); + if (history_search (string, dir) == -1) + { + history_set_pos (old); + return (-1); + } + ret = where_history (); + history_set_pos (old); + return ret; +} + +/* Make the current history item be the one at POS, an absolute index. + Returns zero if POS is out of range, else non-zero. */ +int +history_set_pos (pos) + int pos; +{ + if (pos > history_length || pos < 0 || !the_history) + return (0); + history_offset = pos; + return (1); +} + + +/* **************************************************************** */ +/* */ +/* History Expansion */ +/* */ +/* **************************************************************** */ + +/* Hairy history expansion on text, not tokens. This is of general + use, and thus belongs in this library. */ + +/* The last string searched for in a !?string? search. */ +static char *search_string = (char *)NULL; + +/* Return the event specified at TEXT + OFFSET modifying OFFSET to + point to after the event specifier. Just a pointer to the history + line is returned; NULL is returned in the event of a bad specifier. + You pass STRING with *INDEX equal to the history_expansion_char that + begins this specification. + DELIMITING_QUOTE is a character that is allowed to end the string + specification for what to search for in addition to the normal + characters `:', ` ', `\t', `\n', and sometimes `?'. + So you might call this function like: + line = get_history_event ("!echo:p", &index, 0); */ +char * +get_history_event (string, caller_index, delimiting_quote) + char *string; + int *caller_index; + int delimiting_quote; +{ + register int i = *caller_index; + int which, sign = 1; + HIST_ENTRY *entry; + + /* The event can be specified in a number of ways. + + !! the previous command + !n command line N + !-n current command-line minus N + !str the most recent command starting with STR + !?str[?] + the most recent command containing STR + + All values N are determined via HISTORY_BASE. */ + + if (string[i] != history_expansion_char) + return ((char *)NULL); + + /* Move on to the specification. */ + i++; + + /* Handle !! case. */ + if (string[i] == history_expansion_char) + { + i++; + which = history_base + (history_length - 1); + *caller_index = i; + goto get_which; + } + + /* Hack case of numeric line specification. */ + read_which: + if (string[i] == '-') + { + sign = -1; + i++; + } + + if (digit (string[i])) + { + int start = i; + + /* Get the extent of the digits. */ + for (; digit (string[i]); i++); + + /* Get the digit value. */ + sscanf (string + start, "%d", &which); + + *caller_index = i; + + if (sign < 0) + which = (history_length + history_base) - which; + + get_which: + if (entry = history_get (which)) + return (entry->line); + + return ((char *)NULL); + } + + /* This must be something to search for. If the spec begins with + a '?', then the string may be anywhere on the line. Otherwise, + the string must be found at the start of a line. */ + { + int index; + char *temp; + int substring_okay = 0; + + if (string[i] == '?') + { + substring_okay++; + i++; + } + + for (index = i; string[i]; i++) + if (whitespace (string[i]) || + string[i] == '\n' || + string[i] == ':' || + (substring_okay && string[i] == '?') || + string[i] == delimiting_quote) + break; + + temp = (char *)alloca (1 + (i - index)); + strncpy (temp, &string[index], (i - index)); + temp[i - index] = '\0'; + + if (string[i] == '?') + i++; + + *caller_index = i; + + search_again: + + index = history_search (temp, -1); + + if (index < 0) + search_lost: + { + history_offset = history_length; + return ((char *)NULL); + } + + if (index == 0 || substring_okay || + (strncmp (temp, the_history[history_offset]->line, + strlen (temp)) == 0)) + { + search_won: + entry = current_history (); + history_offset = history_length; + + /* If this was a substring search, then remember the string that + we matched for word substitution. */ + if (substring_okay) + { + if (search_string) + free (search_string); + search_string = savestring (temp); + } + + return (entry->line); + } + + if (history_offset) + history_offset--; + else + goto search_lost; + + goto search_again; + } +} + +/* Expand the string STRING, placing the result into OUTPUT, a pointer + to a string. Returns: + + 0) If no expansions took place (or, if the only change in + the text was the de-slashifying of the history expansion + character) + 1) If expansions did take place + -1) If there was an error in expansion. + + If an error ocurred in expansion, then OUTPUT contains a descriptive + error message. */ +int +history_expand (string, output) + char *string; + char **output; +{ + register int j, l = strlen (string); + int i, word_spec_error = 0; + int cc, modified = 0; + char *word_spec, *event; + int starting_index, only_printing = 0, substitute_globally = 0; + + char *get_history_word_specifier (), *rindex (); + + /* The output string, and its length. */ + int len = 0; + char *result = (char *)NULL; + + /* Used in add_string; */ + char *temp, tt[2], tbl[3]; + + /* Prepare the buffer for printing error messages. */ + result = (char *)xmalloc (len = 255); + + result[0] = tt[1] = tbl[2] = '\0'; + tbl[0] = '\\'; + tbl[1] = history_expansion_char; + + /* Grovel the string. Only backslash can quote the history escape + character. We also handle arg specifiers. */ + + /* Before we grovel forever, see if the history_expansion_char appears + anywhere within the text. */ + + /* The quick substitution character is a history expansion all right. That + is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact, + that is the substitution that we do. */ + if (string[0] == history_subst_char) + { + char *format_string = (char *)alloca (10 + strlen (string)); + + sprintf (format_string, "%c%c:s%s", + history_expansion_char, history_expansion_char, + string); + string = format_string; + l += 4; + goto grovel; + } + + /* If not quick substitution, still maybe have to do expansion. */ + + /* `!' followed by one of the characters in history_no_expand_chars + is NOT an expansion. */ + for (i = 0; string[i]; i++) + if (string[i] == history_expansion_char) + if (!string[i + 1] || member (string[i + 1], history_no_expand_chars)) + continue; + else + goto grovel; + + free (result); + *output = savestring (string); + return (0); + + grovel: + + for (i = j = 0; i < l; i++) + { + int tchar = string[i]; + if (tchar == history_expansion_char) + tchar = -3; + + switch (tchar) + { + case '\\': + if (string[i + 1] == history_expansion_char) + { + i++; + temp = tbl; + goto do_add; + } + else + goto add_char; + + /* case history_expansion_char: */ + case -3: + starting_index = i + 1; + cc = string[i + 1]; + + /* If the history_expansion_char is followed by one of the + characters in history_no_expand_chars, then it is not a + candidate for expansion of any kind. */ + if (member (cc, history_no_expand_chars)) + goto add_char; + + /* There is something that is listed as a `word specifier' in csh + documentation which means `the expanded text to this point'. + That is not a word specifier, it is an event specifier. */ + + if (cc == '#') + goto hack_pound_sign; + + /* If it is followed by something that starts a word specifier, + then !! is implied as the event specifier. */ + + if (member (cc, ":$*%^")) + { + char fake_s[2]; + int fake_i = 0; + i++; + fake_s[0] = fake_s[1] = history_expansion_char; + fake_s[2] = '\0'; + event = get_history_event (fake_s, &fake_i); + } + else + { + int quoted_search_delimiter = 0; + + /* If the character before this `!' is a double or single + quote, then this expansion takes place inside of the + quoted string. If we have to search for some text ("!foo"), + allow the delimiter to end the search string. */ + if (i && (string[i - 1] == '\'' || string[i - 1] == '"')) + quoted_search_delimiter = string[i - 1]; + + event = get_history_event (string, &i, quoted_search_delimiter); + } + + if (!event) + event_not_found: + { + int l = 1 + (i - starting_index); + + temp = (char *)alloca (1 + l); + strncpy (temp, string + starting_index, l); + temp[l - 1] = 0; + sprintf (result, "%s: %s.", temp, + word_spec_error ? "Bad word specifier" : "Event not found"); + error_exit: + *output = result; + return (-1); + } + + /* If a word specifier is found, then do what that requires. */ + starting_index = i; + + word_spec = get_history_word_specifier (string, event, &i); + + /* There is no such thing as a `malformed word specifier'. However, + it is possible for a specifier that has no match. In that case, + we complain. */ + if (word_spec == (char *)-1) + bad_word_spec: + { + word_spec_error++; + goto event_not_found; + } + + /* If no word specifier, than the thing of interest was the event. */ + if (!word_spec) + temp = event; + else + { + temp = (char *)alloca (1 + strlen (word_spec)); + strcpy (temp, word_spec); + free (word_spec); + } + + /* Perhaps there are other modifiers involved. Do what they say. */ + + hack_specials: + + if (string[i] == ':') + { + char *tstr; + + switch (string[i + 1]) + { + /* :p means make this the last executed line. So we + return an error state after adding this line to the + history. */ + case 'p': + only_printing++; + goto next_special; + + /* :t discards all but the last part of the pathname. */ + case 't': + tstr = rindex (temp, '/'); + if (tstr) + temp = ++tstr; + goto next_special; + + /* :h discards the last part of a pathname. */ + case 'h': + tstr = rindex (temp, '/'); + if (tstr) + *tstr = '\0'; + goto next_special; + + /* :r discards the suffix. */ + case 'r': + tstr = rindex (temp, '.'); + if (tstr) + *tstr = '\0'; + goto next_special; + + /* :e discards everything but the suffix. */ + case 'e': + tstr = rindex (temp, '.'); + if (tstr) + temp = tstr; + goto next_special; + + /* :s/this/that substitutes `this' for `that'. */ + /* :gs/this/that substitutes `this' for `that' globally. */ + case 'g': + if (string[i + 2] == 's') + { + i++; + substitute_globally = 1; + goto substitute; + } + else + + case 's': + substitute: + { + char *this, *that, *new_event; + int delimiter = 0; + int si, l_this, l_that, l_temp = strlen (temp); + + if (i + 2 < strlen (string)) + delimiter = string[i + 2]; + + if (!delimiter) + break; + + i += 3; + + /* Get THIS. */ + for (si = i; string[si] && string[si] != delimiter; si++); + l_this = (si - i); + this = (char *)alloca (1 + l_this); + strncpy (this, string + i, l_this); + this[l_this] = '\0'; + + i = si; + if (string[si]) + i++; + + /* Get THAT. */ + for (si = i; string[si] && string[si] != delimiter; si++); + l_that = (si - i); + that = (char *)alloca (1 + l_that); + strncpy (that, string + i, l_that); + that[l_that] = '\0'; + + i = si; + if (string[si]) i++; + + /* Ignore impossible cases. */ + if (l_this > l_temp) + goto cant_substitute; + + /* Find the first occurrence of THIS in TEMP. */ + si = 0; + for (; (si + l_this) <= l_temp; si++) + if (strncmp (temp + si, this, l_this) == 0) + { + new_event = + (char *)alloca (1 + (l_that - l_this) + l_temp); + strncpy (new_event, temp, si); + strncpy (new_event + si, that, l_that); + strncpy (new_event + si + l_that, + temp + si + l_this, + l_temp - (si + l_this)); + new_event[(l_that - l_this) + l_temp] = '\0'; + temp = new_event; + + if (substitute_globally) + { + si += l_that; + l_temp = strlen (temp); + substitute_globally++; + continue; + } + + goto hack_specials; + } + + cant_substitute: + + if (substitute_globally > 1) + { + substitute_globally = 0; + goto hack_specials; + } + + goto event_not_found; + } + + /* :# is the line so far. Note that we have to + alloca () it since RESULT could be realloc ()'ed + below in add_string. */ + case '#': + hack_pound_sign: + if (result) + { + temp = (char *)alloca (1 + strlen (result)); + strcpy (temp, result); + } + else + temp = ""; + + next_special: + i += 2; + goto hack_specials; + } + + } + /* Believe it or not, we have to back the pointer up by one. */ + --i; + goto add_string; + + /* A regular character. Just add it to the output string. */ + default: + add_char: + tt[0] = string[i]; + temp = tt; + goto do_add; + + add_string: + modified++; + + do_add: + j += strlen (temp); + while (j > len) + result = (char *)xrealloc (result, (len += 255)); + + strcpy (result + (j - strlen (temp)), temp); + } + } + + *output = result; + + if (only_printing) + { + add_history (result); + return (-1); + } + + return (modified != 0); +} + +/* Return a consed string which is the word specified in SPEC, and found + in FROM. NULL is returned if there is no spec. -1 is returned if + the word specified cannot be found. CALLER_INDEX is the offset in + SPEC to start looking; it is updated to point to just after the last + character parsed. */ +char * +get_history_word_specifier (spec, from, caller_index) + char *spec, *from; + int *caller_index; +{ + register int i = *caller_index; + int first, last; + int expecting_word_spec = 0; + char *history_arg_extract (); + + /* The range of words to return doesn't exist yet. */ + first = last = 0; + + /* If we found a colon, then this *must* be a word specification. If + it isn't, then it is an error. */ + if (spec[i] == ':') + i++, expecting_word_spec++; + + /* Handle special cases first. */ + + /* `%' is the word last searched for. */ + if (spec[i] == '%') + { + *caller_index = i + 1; + if (search_string) + return (savestring (search_string)); + else + return (savestring ("")); + } + + /* `*' matches all of the arguments, but not the command. */ + if (spec[i] == '*') + { + *caller_index = i + 1; + return (history_arg_extract (1, '$', from)); + } + + /* `$' is last arg. */ + if (spec[i] == '$') + { + *caller_index = i + 1; + return (history_arg_extract ('$', '$', from)); + } + + /* Try to get FIRST and LAST figured out. */ + if (spec[i] == '-' || spec[i] == '^') + { + first = 1; + goto get_last; + } + + get_first: + if (digit (spec[i]) && expecting_word_spec) + { + sscanf (spec + i, "%d", &first); + for (; digit (spec[i]); i++); + } + else + return ((char *)NULL); + + get_last: + if (spec[i] == '^') + { + i++; + last = 1; + goto get_args; + } + + if (spec[i] != '-') + { + last = first; + goto get_args; + } + + i++; + + if (digit (spec[i])) + { + sscanf (spec + i, "%d", &last); + for (; digit (spec[i]); i++); + } + else + if (spec[i] == '$') + { + i++; + last = '$'; + } + + get_args: + { + char *result = (char *)NULL; + + *caller_index = i; + + if (last >= first) + result = history_arg_extract (first, last, from); + + if (result) + return (result); + else + return ((char *)-1); + } +} + +/* Extract the args specified, starting at FIRST, and ending at LAST. + The args are taken from STRING. */ +char * +history_arg_extract (first, last, string) + int first, last; + char *string; +{ + register int i, len; + char *result = (char *)NULL; + int size = 0, offset = 0; + + char **history_tokenize (), **list; + + if (!(list = history_tokenize (string))) + return ((char *)NULL); + + for (len = 0; list[len]; len++); + + if (last == '$') + last = len - 1; + + if (first == '$') + first = len - 1; + + last++; + + if (first > len || last > len) + result = ((char *)NULL); + else { + for (i = first; i < last; i++) + { + int l = strlen (list[i]); + + if (!result) + result = (char *)xmalloc ((size = (2 + l))); + else + result = (char *)xrealloc (result, (size += (2 + l))); + strcpy (result + offset, list[i]); + offset += l; + if (i + 1 < last) + { + strcpy (result + offset, " "); + offset++; + } + } + } + + for (i = 0; i < len; i++) + free (list[i]); + + free (list); + + return (result); +} + +#define slashify_in_quotes "\\`\"$" + +/* Return an array of tokens, much as the shell might. The tokens are + parsed out of STRING. */ +char ** +history_tokenize (string) + char *string; +{ + char **result = (char **)NULL; + register int i, start, result_index, size; + int len; + + i = result_index = size = 0; + + /* Get a token, and stuff it into RESULT. The tokens are split + exactly where the shell would split them. */ + get_token: + + /* Skip leading whitespace. */ + for (; string[i] && whitespace(string[i]); i++); + + start = i; + + if (!string[i] || string[i] == history_comment_char) + return (result); + + if (member (string[i], "()\n")) { + i++; + goto got_token; + } + + if (member (string[i], "<>;&|")) { + int peek = string[i + 1]; + + if (peek == string[i]) { + if (peek == '<') { + if (string[1 + 2] == '-') + i++; + i += 2; + goto got_token; + } + + if (member (peek, ">:&|")) { + i += 2; + goto got_token; + } + } else { + if ((peek == '&' && + (string[i] == '>' || string[i] == '<')) || + ((peek == '>') && + (string[i] == '&'))) { + i += 2; + goto got_token; + } + } + i++; + goto got_token; + } + + /* Get word from string + i; */ + { + int delimiter = 0; + + if (member (string[i], "\"'`")) + delimiter = string[i++]; + + for (;string[i]; i++) { + + if (string[i] == '\\') { + + if (string[i + 1] == '\n') { + i++; + continue; + } else { + if (delimiter != '\'') + if ((delimiter != '"') || + (member (string[i], slashify_in_quotes))) { + i++; + continue; + } + } + } + + if (delimiter && string[i] == delimiter) { + delimiter = 0; + continue; + } + + if (!delimiter && (member (string[i], " \t\n;&()|<>"))) + goto got_token; + + if (!delimiter && member (string[i], "\"'`")) { + delimiter = string[i]; + continue; + } + } + got_token: + + len = i - start; + if (result_index + 2 >= size) { + if (!size) + result = (char **)xmalloc ((size = 10) * (sizeof (char *))); + else + result = + (char **)xrealloc (result, ((size += 10) * (sizeof (char *)))); + } + result[result_index] = (char *)xmalloc (1 + len); + strncpy (result[result_index], string + start, len); + result[result_index][len] = '\0'; + result_index++; + result[result_index] = (char *)NULL; + } + if (string[i]) + goto get_token; + + return (result); +} + +#ifdef STATIC_MALLOC + +/* **************************************************************** */ +/* */ +/* xmalloc and xrealloc () */ +/* */ +/* **************************************************************** */ + +static char * +xmalloc (bytes) + int bytes; +{ + static memory_error_and_abort (); + char *temp = (char *)malloc (bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static char * +xrealloc (pointer, bytes) + char *pointer; + int bytes; +{ + static memory_error_and_abort (); + char *temp = (char *)realloc (pointer, bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static +memory_error_and_abort () +{ + fprintf (stderr, "history: Out of virtual memory!\n"); + abort (); +} +#endif /* STATIC_MALLOC */ + + +/* **************************************************************** */ +/* */ +/* Test Code */ +/* */ +/* **************************************************************** */ +#ifdef TEST +main () +{ + char line[1024], *t; + int done = 0; + + line[0] = 0; + + while (!done) + { + fprintf (stdout, "history%% "); + t = gets (line); + + if (!t) + strcpy (line, "quit"); + + if (line[0]) + { + char *expansion; + int result; + + using_history (); + + result = history_expand (line, &expansion); + strcpy (line, expansion); + free (expansion); + if (result) + fprintf (stderr, "%s\n", line); + + if (result < 0) + continue; + + add_history (line); + } + + if (strcmp (line, "quit") == 0) done = 1; + if (strcmp (line, "save") == 0) write_history (0); + if (strcmp (line, "read") == 0) read_history (0); + if (strcmp (line, "list") == 0) + { + register HIST_ENTRY **the_list = history_list (); + register int i; + + if (the_list) + for (i = 0; the_list[i]; i++) + fprintf (stdout, "%d: %s\n", i + history_base, the_list[i]->line); + } + if (strncmp (line, "delete", strlen ("delete")) == 0) + { + int which; + if ((sscanf (line + strlen ("delete"), "%d", &which)) == 1) + { + HIST_ENTRY *entry = remove_history (which); + if (!entry) + fprintf (stderr, "No such entry %d\n", which); + else + { + free (entry->line); + free (entry); + } + } + else + { + fprintf (stderr, "non-numeric arg given to `delete'\n"); + } + } + } +} + +#endif /* TEST */ + +/* +* Local variables: +* compile-command: "gcc -g -DTEST -o history history.c" +* end: +*/ diff --git a/gnu/usr.bin/gdb/readline/history.h b/gnu/usr.bin/gdb/readline/history.h new file mode 100644 index 0000000000..0bac209207 --- /dev/null +++ b/gnu/usr.bin/gdb/readline/history.h @@ -0,0 +1,108 @@ +/* History.h -- the names of functions that you can call in history. */ + +typedef struct _hist_entry { + char *line; + char *data; +} HIST_ENTRY; + +/* For convenience only. You set this when interpreting history commands. + It is the logical offset of the first history element. */ +extern int history_base; + +/* Begin a session in which the history functions might be used. This + just initializes the interactive variables. */ +extern void using_history (); + +/* Place STRING at the end of the history list. + The associated data field (if any) is set to NULL. */ +extern void add_history (); + +/* Returns the number which says what history element we are now + looking at. */ +extern int where_history (); + +/* Set the position in the history list to POS. */ +int history_set_pos (); + +/* Search for STRING in the history list, starting at POS, an + absolute index into the list. DIR, if negative, says to search + backwards from POS, else forwards. + Returns the absolute index of the history element where STRING + was found, or -1 otherwise. */ +extern int history_search_pos (); + +/* A reasonably useless function, only here for completeness. WHICH + is the magic number that tells us which element to delete. The + elements are numbered from 0. */ +extern HIST_ENTRY *remove_history (); + +/* Stifle the history list, remembering only MAX number of entries. */ +extern void stifle_history (); + +/* Stop stifling the history. This returns the previous amount the + history was stifled by. The value is positive if the history was + stifled, negative if it wasn't. */ +extern int unstifle_history (); + +/* Add the contents of FILENAME to the history list, a line at a time. + If FILENAME is NULL, then read from ~/.history. Returns 0 if + successful, or errno if not. */ +extern int read_history (); + +/* Append the current history to FILENAME. If FILENAME is NULL, + then append the history list to ~/.history. Values returned + are as in read_history (). */ +extern int write_history (); + + +/* Make the history entry at WHICH have LINE and DATA. This returns + the old entry so you can dispose of the data. In the case of an + invalid WHICH, a NULL pointer is returned. */ +extern HIST_ENTRY *replace_history_entry (); + +/* Return the history entry at the current position, as determined by + history_offset. If there is no entry there, return a NULL pointer. */ +HIST_ENTRY *current_history (); + +/* Back up history_offset to the previous history entry, and return + a pointer to that entry. If there is no previous entry, return + a NULL pointer. */ +extern HIST_ENTRY *previous_history (); + +/* Move history_offset forward to the next item in the input_history, + and return the a pointer to that entry. If there is no next entry, + return a NULL pointer. */ +extern HIST_ENTRY *next_history (); + +/* Return a NULL terminated array of HIST_ENTRY which is the current input + history. Element 0 of this list is the beginning of time. If there + is no history, return NULL. */ +extern HIST_ENTRY **history_list (); + +/* Search the history for STRING, starting at history_offset. + If DIRECTION < 0, then the search is through previous entries, + else through subsequent. If the string is found, then + current_history () is the history entry, and the value of this function + is the offset in the line of that history entry that the string was + found in. Otherwise, nothing is changed, and a -1 is returned. */ +extern int history_search (); + +/* Expand the string STRING, placing the result into OUTPUT, a pointer + to a string. Returns: + + 0) If no expansions took place (or, if the only change in + the text was the de-slashifying of the history expansion + character) + 1) If expansions did take place + -1) If there was an error in expansion. + + If an error ocurred in expansion, then OUTPUT contains a descriptive + error message. */ +extern int history_expand (); + +/* Extract a string segment consisting of the FIRST through LAST + arguments present in STRING. Arguments are broken up as in + the shell. */ +extern char *history_arg_extract (); + + diff --git a/gnu/usr.bin/gdb/readline/keymaps.c b/gnu/usr.bin/gdb/readline/keymaps.c new file mode 100644 index 0000000000..e0c5e394a4 --- /dev/null +++ b/gnu/usr.bin/gdb/readline/keymaps.c @@ -0,0 +1,172 @@ +/* keymaps.c -- Functions and keymaps for the GNU Readline library. */ + +/* Copyright (C) 1988,1989 Free Software Foundation, Inc. + + This file is part of GNU Readline, a library for reading lines + of text with interactive input and history editing. + + Readline is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 1, or (at your option) any + later version. + + Readline is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Readline; see the file COPYING. If not, write to the Free + Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "keymaps.h" +#include "emacs_keymap.c" + +#ifdef VI_MODE +#include "vi_keymap.c" +#endif + +/* Remove these declarations when we have a complete libgnu.a. */ +#define STATIC_MALLOC +#ifndef STATIC_MALLOC +extern char *xmalloc (), *xrealloc (); +#else +static char *xmalloc (), *xrealloc (); +#endif + +/* **************************************************************** */ +/* */ +/* Functions for manipulating Keymaps. */ +/* */ +/* **************************************************************** */ + + +/* Return a new, empty keymap. + Free it with free() when you are done. */ +Keymap +rl_make_bare_keymap () +{ + register int i; + Keymap keymap = (Keymap)xmalloc (128 * sizeof (KEYMAP_ENTRY)); + + for (i = 0; i < 128; i++) + { + keymap[i].type = ISFUNC; + keymap[i].function = (Function *)NULL; + } + + for (i = 'A'; i < ('Z' + 1); i++) + { + keymap[i].type = ISFUNC; + keymap[i].function = rl_do_lowercase_version; + } + + return (keymap); +} + +/* Return a new keymap which is a copy of MAP. */ +Keymap +rl_copy_keymap (map) + Keymap map; +{ + register int i; + Keymap temp = rl_make_bare_keymap (); + + for (i = 0; i < 128; i++) + { + temp[i].type = map[i].type; + temp[i].function = map[i].function; + } + return (temp); +} + +/* Return a new keymap with the printing characters bound to rl_insert, + the uppercase Meta characters bound to run their lowercase equivalents, + and the Meta digits bound to produce numeric arguments. */ +Keymap +rl_make_keymap () +{ + extern rl_insert (), rl_rubout (), rl_do_lowercase_version (); + extern rl_digit_argument (); + register int i; + Keymap newmap; + + newmap = rl_make_bare_keymap (); + + /* All printing characters are self-inserting. */ + for (i = ' '; i < 126; i++) + newmap[i].function = rl_insert; + + newmap[TAB].function = rl_insert; + newmap[RUBOUT].function = rl_rubout; + + return (newmap); +} + +/* Free the storage associated with MAP. */ +rl_discard_keymap (map) + Keymap (map); +{ + int i; + + if (!map) + return; + + for (i = 0; i < 128; i++) + { + switch (map[i].type) + { + case ISFUNC: + break; + + case ISKMAP: + rl_discard_keymap ((Keymap)map[i].function); + break; + + case ISMACR: + free ((char *)map[i].function); + break; + } + } +} + +#ifdef STATIC_MALLOC + +/* **************************************************************** */ +/* */ +/* xmalloc and xrealloc () */ +/* */ +/* **************************************************************** */ + +static char * +xmalloc (bytes) + int bytes; +{ + static memory_error_and_abort (); + char *temp = (char *)malloc (bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static char * +xrealloc (pointer, bytes) + char *pointer; + int bytes; +{ + static memory_error_and_abort (); + char *temp = (char *)realloc (pointer, bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static +memory_error_and_abort () +{ + fprintf (stderr, "readline: Out of virtual memory!\n"); + abort (); +} +#endif /* STATIC_MALLOC */ diff --git a/gnu/usr.bin/gdb/readline/keymaps.h b/gnu/usr.bin/gdb/readline/keymaps.h new file mode 100644 index 0000000000..3c577b398f --- /dev/null +++ b/gnu/usr.bin/gdb/readline/keymaps.h @@ -0,0 +1,53 @@ +/* keymaps.h -- Manipulation of readline keymaps. */ + +#ifndef _KEYMAPS_H_ +#define _KEYMAPS_H_ + +#include + +#ifndef __FUNCTION_DEF +typedef int Function (); +#define __FUNCTION_DEF +#endif + +/* A keymap contains one entry for each key in the ASCII set. + Each entry consists of a type and a pointer. + POINTER is the address of a function to run, or the + address of a keymap to indirect through. + TYPE says which kind of thing POINTER is. */ +typedef struct _keymap_entry { + char type; + Function *function; +} KEYMAP_ENTRY; + +/* I wanted to make the above structure contain a union of: + union { Function *function; struct _keymap_entry *keymap; } value; + but this made it impossible for me to create a static array. + Maybe I need C lessons. */ + +typedef KEYMAP_ENTRY KEYMAP_ENTRY_ARRAY[128]; +typedef KEYMAP_ENTRY *Keymap; + +/* The values that TYPE can have in a keymap entry. */ +#define ISFUNC 0 +#define ISKMAP 1 +#define ISMACR 2 + +extern KEYMAP_ENTRY_ARRAY emacs_standard_keymap, emacs_meta_keymap, emacs_ctlx_keymap; +extern KEYMAP_ENTRY_ARRAY vi_insertion_keymap, vi_movement_keymap; + +/* Return a new, empty keymap. + Free it with free() when you are done. */ +Keymap rl_make_bare_keymap (); + +/* Return a new keymap which is a copy of MAP. */ +Keymap rl_copy_keymap (); + +/* Return a new keymap with the printing characters bound to rl_insert, + the lowercase Meta characters bound to run their equivalents, and + the Meta digits bound to produce numeric arguments. */ +Keymap rl_make_keymap (); + +#endif /* _KEYMAPS_H_ */ + + diff --git a/gnu/usr.bin/gdb/readline/readline.c b/gnu/usr.bin/gdb/readline/readline.c new file mode 100644 index 0000000000..a29efe1ef3 --- /dev/null +++ b/gnu/usr.bin/gdb/readline/readline.c @@ -0,0 +1,5557 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)readline.c 6.4 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* readline.c -- a general facility for reading lines of input + with emacs style editing and completion. */ + +/* Copyright (C) 1987,1989 Free Software Foundation, Inc. + + This file contains the Readline Library (the Library), a set of + routines for providing Emacs style line input to programs that ask + for it. + + The Library is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + The Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + The GNU General Public License is often shipped with GNU software, and + is generally kept in a file called COPYING or LICENSE. If you do not + have a copy of the license, write to the Free Software Foundation, + 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Remove these declarations when we have a complete libgnu.a. */ +#define STATIC_MALLOC +#ifndef STATIC_MALLOC +extern char *xmalloc (), *xrealloc (); +#else +static char *xmalloc (), *xrealloc (); +#endif + +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +#if defined (sparc) && defined (sun) +#include +#endif +#endif + +#define NEW_TTY_DRIVER +#if defined (SYSV) || defined (hpux) +#undef NEW_TTY_DRIVER +#include +#else +#include +#endif + +#include +extern int errno; + +#include + +/* These next are for filename completion. Perhaps this belongs + in a different place. */ +#include + +#include +#ifdef SYSV +struct passwd *getpwuid (), *getpwent (); +#endif + +#define HACK_TERMCAP_MOTION + +#ifndef SYSV +#include +#else /* SYSV */ +#ifdef hpux +#include +#else +#include +#define direct dirent +#define d_namlen d_reclen +#endif /* hpux */ +#endif /* SYSV */ + +/* Some standard library routines. */ +#include "readline.h" +#include "history.h" + +#ifndef digit +#define digit(c) ((c) >= '0' && (c) <= '9') +#endif + +#ifndef isletter +#define isletter(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z')) +#endif + +#ifndef digit_value +#define digit_value(c) ((c) - '0') +#endif + +#ifndef member +char *index (); +#define member(c, s) ((c) ? index ((s), (c)) : 0) +#endif + +#ifndef isident +#define isident(c) ((isletter(c) || digit(c) || c == '_')) +#endif + +#ifndef exchange +#define exchange(x, y) {int temp = x; x = y; y = temp;} +#endif + +static update_line (); +static void output_character_function (); +static delete_chars (); +static start_insert (); +static end_insert (); + +/* This typedef is equivalant to the one for Function; it allows us + to say SigHandler *foo = signal (SIGKILL, SIG_IGN); */ +typedef void SigHandler (); + +#ifdef SIGWINCH +static void rl_handle_sigwinch (); +static SigHandler *old_sigwinch = (SigHandler *)NULL; +#endif + +/* If on, then readline handles signals in a way that doesn't screw. */ +/* #define HANDLE_SIGNALS */ + +#if defined (SYSV) +#ifdef HANDLE_SIGNALS +#undef HANDLE_SIGNALS +#endif +#endif + +/* Stupid comparison routine for qsort () ing strings. */ +static int +compare_strings (s1, s2) + char **s1, **s2; +{ + return (strcmp (*s1, *s2)); +} + + +/* **************************************************************** */ +/* */ +/* Line editing input utility */ +/* */ +/* **************************************************************** */ + +/* A pointer to the keymap that is currently in use. + By default, it is the standard emacs keymap. */ +Keymap keymap = emacs_standard_keymap; + +#define vi_mode 0 +#define emacs_mode 1 + +/* The current style of editing. */ +int rl_editing_mode = emacs_mode; + +/* Non-zero if the previous command was a kill command. */ +static int last_command_was_kill = 0; + +/* The current value of the numeric argument specified by the user. */ +int rl_numeric_arg = 1; + +/* Non-zero if an argument was typed. */ +int rl_explicit_arg = 0; + +/* Temporary value used while generating the argument. */ +static int arg_sign = 1; + +/* Non-zero means we have been called at least once before. */ +static int rl_initialized = 0; + +/* If non-zero, this program is running in an EMACS buffer. */ +static char *running_in_emacs = (char *)NULL; + +/* The current offset in the current input line. */ +int rl_point; + +/* Mark in the current input line. */ +int rl_mark; + +/* Length of the current input line. */ +int rl_end; + +/* Make this non-zero to return the current input_line. */ +int rl_done; + +/* The last function executed by readline. */ +Function *rl_last_func = (Function *)NULL; + +/* Top level environment for readline_internal (). */ +static jmp_buf readline_top_level; + +/* The streams we interact with. */ +static FILE *in_stream, *out_stream; + +/* The names of the streams that we do input and output to. */ +FILE *rl_instream = stdin, *rl_outstream = stdout; + +/* Non-zero means echo characters as they are read. */ +int readline_echoing_p = 1; + +/* Current prompt. */ +char *rl_prompt; + +/* The number of characters read in order to type this complete command. */ +int rl_key_sequence_length = 0; + +/* If non-zero, then this is the address of a function to call just + before readline_internal () prints the first prompt. */ +Function *rl_startup_hook = (Function *)NULL; + +/* What we use internally. You should always refer to RL_LINE_BUFFER. */ +static char *the_line; + +/* The character that can generate an EOF. Really read from + the terminal driver... just defaulted here. */ +static int eof_char = CTRL ('D'); + +/* Non-zero makes this the next keystroke to read. */ +int rl_pending_input = 0; + +/* Pointer to a useful terminal name. */ +char *rl_terminal_name = (char *)NULL; + +/* Line buffer and maintenence. */ +char *rl_line_buffer = (char *)NULL; +static int rl_line_buffer_len = 0; +#define DEFAULT_BUFFER_SIZE 256 + + +/* **************************************************************** */ +/* */ +/* Top Level Functions */ +/* */ +/* **************************************************************** */ + +/* Read a line of input. Prompt with PROMPT. A NULL PROMPT means + none. A return value of NULL means that EOF was encountered. */ +char * +readline (prompt) + char *prompt; +{ + static rl_prep_terminal (), rl_deprep_terminal (); + char *readline_internal (); + char *value; + + rl_prompt = prompt; + + /* If we are at EOF return a NULL string. */ + if (rl_pending_input == EOF) + { + rl_pending_input = 0; + return ((char *)NULL); + } + + rl_initialize (); + rl_prep_terminal (); + +#ifdef SIGWINCH + old_sigwinch = (SigHandler *)signal (SIGWINCH, rl_handle_sigwinch); +#endif + +#ifdef HANDLE_SIGNALS + rl_set_signals (); +#endif + + value = readline_internal (); + rl_deprep_terminal (); + +#ifdef SIGWINCH + signal (SIGWINCH, old_sigwinch); +#endif + +#ifdef HANDLE_SIGNALS + rl_clear_signals (); +#endif + + return (value); +} + +/* Read a line of input from the global rl_instream, doing output on + the global rl_outstream. + If rl_prompt is non-null, then that is our prompt. */ +char * +readline_internal () +{ + int lastc, c, eof_found; + + in_stream = rl_instream; out_stream = rl_outstream; + lastc = eof_found = 0; + + if (rl_startup_hook) + (*rl_startup_hook) (); + + if (!readline_echoing_p) + { + if (rl_prompt) { + fprintf (out_stream, "%s", rl_prompt); + fflush(out_stream); + } + } + else + { + rl_on_new_line (); + rl_redisplay (); +#ifdef VI_MODE + if (rl_editing_mode == vi_mode) + rl_vi_insertion_mode (); +#endif /* VI_MODE */ + } + + while (!rl_done) + { + int lk = last_command_was_kill; + int code = setjmp (readline_top_level); + + if (code) + rl_redisplay (); + + if (!rl_pending_input) + { + /* Then initialize the argument and number of keys read. */ + rl_init_argument (); + rl_key_sequence_length = 0; + } + + c = rl_read_key (); + + /* EOF typed to a non-blank line is a . */ + if (c == EOF && rl_end) + c = NEWLINE; + + /* The character eof_char typed to blank line, and not as the + previous character is interpreted as EOF. */ + if (((c == eof_char && lastc != c) || c == EOF) && !rl_end) + { + eof_found = 1; + break; + } + + lastc = c; + rl_dispatch (c, keymap); + + /* If there was no change in last_command_was_kill, then no kill + has taken place. Note that if input is pending we are reading + a prefix command, so nothing has changed yet. */ + if (!rl_pending_input) + { + if (lk == last_command_was_kill) + last_command_was_kill = 0; + } + +#ifdef VI_MODE + /* In vi mode, when you exit insert mode, the cursor moves back + over the previous character. We explicitly check for that here. */ + if (rl_editing_mode == vi_mode && keymap == vi_movement_keymap) + rl_vi_check (); +#endif + + if (!rl_done) + rl_redisplay (); + } + + /* Restore the original of this history line, iff the line that we + are editing was originally in the history, AND the line has changed. */ + { + HIST_ENTRY *entry = current_history (); + + if (entry && rl_undo_list) + { + char *temp = savestring (the_line); + rl_revert_line (); + entry = replace_history_entry (where_history (), the_line, + (HIST_ENTRY *)NULL); + free_history_entry (entry); + + strcpy (the_line, temp); + free (temp); + } + } + + /* At any rate, it is highly likely that this line has an undo list. Get + rid of it now. */ + if (rl_undo_list) + free_undo_list (); + + if (eof_found) + return (char *)NULL; + else + return (savestring (the_line)); +} + + +/* Variables for keyboard macros. */ + +/* The currently executing macro string. If this is non-zero, + then it is a malloc ()'ed string where input is coming from. */ +static char *executing_macro = (char *)NULL; + +/* The offset in the above string to the next character to be read. */ +static int executing_macro_index = 0; + +/* Non-zero means to save keys that we dispatch on in a kbd macro. */ +static int defining_kbd_macro = 0; + +/* The current macro string being built. Characters get stuffed + in here by add_macro_char (). */ +static char *current_macro = (char *)NULL; + +/* The size of the buffer allocated to current_macro. */ +static int current_macro_size = 0; + +/* The index at which characters are being added to current_macro. */ +static int current_macro_index = 0; + +/* A structure used to save nested macro strings. + It is a linked list of string/index for each saved macro. */ +struct saved_macro { + struct saved_macro *next; + char *string; + int index; +}; + +/* The list of saved macros. */ +struct saved_macro *macro_list = (struct saved_macro *)NULL; + + +/* **************************************************************** */ +/* */ +/* Signal Handling */ +/* */ +/* **************************************************************** */ + +#ifdef SIGWINCH +static void +rl_handle_sigwinch (sig, code, scp) + int sig, code; + struct sigcontext *scp; +{ + char *term = rl_terminal_name, *getenv (); + + if (readline_echoing_p) + { + if (!term) + term = getenv ("TERM"); + if (!term) + term = "dumb"; + rl_reset_terminal (term); +#ifdef NEVER + crlf (); + rl_forced_update_display (); +#endif + } + + if (old_sigwinch && + old_sigwinch != (SigHandler *)SIG_IGN && + old_sigwinch != (SigHandler *)SIG_DFL) + (*old_sigwinch)(sig, code, scp); +} +#endif /* SIGWINCH */ + +#ifdef HANDLE_SIGNALS +/* Interrupt handling. */ +static SigHandler *old_int = (SigHandler *)NULL, + *old_tstp = (SigHandler *)NULL, + *old_ttou = (SigHandler *)NULL, + *old_ttin = (SigHandler *)NULL, + *old_cont = (SigHandler *)NULL; + +/* Handle an interrupt character. */ +static void +rl_signal_handler (sig, code, scp) + int sig, code; + struct sigcontext *scp; +{ + static rl_prep_terminal (), rl_deprep_terminal (); + + switch (sig) + { + case SIGINT: + free_undo_list (); + rl_clear_message (); + rl_init_argument (); +#ifdef SIGWINCH + signal (SIGWINCH, old_sigwinch); +#endif + +#ifdef SIGTSTP + case SIGTSTP: + case SIGTTOU: + case SIGTTIN: +#endif + + rl_clean_up_for_exit (); + rl_deprep_terminal (); + rl_clear_signals (); + rl_pending_input = 0; + + kill (getpid (), sig); + sigsetmask (0); + + rl_prep_terminal (); + rl_set_signals (); + } +} + +rl_set_signals () +{ + old_int = (SigHandler *)signal (SIGINT, rl_signal_handler); + + if (old_int == (SigHandler *)SIG_IGN) + signal (SIGINT, SIG_IGN); + +#ifdef SIGTSTP + old_tstp = (SigHandler *)signal (SIGTSTP, rl_signal_handler); + if (old_tstp == (SigHandler *)SIG_IGN) + signal (SIGTSTP, SIG_IGN); +#endif +#ifdef SIGTTOU + old_ttou = (SigHandler *)signal (SIGTTOU, rl_signal_handler); + old_ttin = (SigHandler *)signal (SIGTTIN, rl_signal_handler); +#endif +} + +rl_clear_signals () +{ + signal (SIGINT, old_int); + +#ifdef SIGTSTP + signal (SIGTSTP, old_tstp); +#endif +#ifdef SIGTTOU + signal (SIGTTOU, old_ttou); + signal (SIGTTIN, old_ttin); +#endif +} +#endif /* HANDLE_SIGNALS */ + + + +/* **************************************************************** */ +/* */ +/* Character Input Buffering */ +/* */ +/* **************************************************************** */ + +/* If the terminal was in xoff state when we got to it, then xon_char + contains the character that is supposed to start it again. */ +static int xon_char, xoff_state; +static int pop_index = 0, push_index = 0, ibuffer_len = 511; +static unsigned char ibuffer[512]; + +/* Non-null means it is a pointer to a function to run while waiting for + character input. */ +Function *rl_event_hook = (Function *)NULL; + +#define any_typein (push_index != pop_index) + +/* Add KEY to the buffer of characters to be read. */ +rl_stuff_char (key) + int key; +{ + if (key == EOF) + { + key = NEWLINE; + rl_pending_input = EOF; + } + ibuffer[push_index++] = key; + if (push_index >= ibuffer_len) + push_index = 0; +} + +/* Return the amount of space available in the + buffer for stuffing characters. */ +int +ibuffer_space () +{ + if (pop_index > push_index) + return (pop_index - push_index); + else + return (ibuffer_len - (push_index - pop_index)); +} + +/* Get a key from the buffer of characters to be read. + Result is KEY if there was a key, or -2 if there wasn't. */ +int +rl_get_char () +{ + int key; + + if (push_index == pop_index) + return (-2); + + key = ibuffer[pop_index++]; + + if (pop_index >= ibuffer_len) + pop_index = 0; + + return (key); +} + +/* Stuff KEY into the *front* of the input buffer. + Returns non-zero if successful, zero if there is + no space left in the buffer. */ +int +rl_unget_char (key) + int key; +{ + if (ibuffer_space ()) + { + pop_index--; + if (pop_index < 0) + pop_index = ibuffer_len - 1; + ibuffer[pop_index] = key; + return (1); + } + return (0); +} + + + +static void +rl_getc (stream) + FILE *stream; +{ + int result; + int nchar; + int tty = fileno(stream); + char buf[512]; /* XXX - must be at least as large as ibuffer */ + + while (1) + { + if (ioctl(tty, FIONREAD, &nchar) == -1) + nchar = sizeof(buf); + else if (nchar <= 0) + nchar = 1; + result = ibuffer_space(); + if (nchar > result) + nchar = result; + result = read(tty, buf, nchar); + if (result > 0) + { + register char *cp = buf; + + while (--result >= 0) + rl_stuff_char(*cp++); + return; + } + if (errno != EINTR) + { + rl_stuff_char(EOF); + return; + } + } +} + +/* Read a key, including pending input. */ +int +rl_read_key () +{ + int c; + + rl_key_sequence_length++; + + if (rl_pending_input) + { + c = rl_pending_input; + rl_pending_input = 0; + } + else + { + static int next_macro_key (); + + /* If input is coming from a macro, then use that. */ + if (c = next_macro_key ()) + return (c); + + while ((c = rl_get_char()) == -2) + { + if (rl_event_hook) + { + (*rl_event_hook) (); + if ((c = rl_get_char()) != -2) + return (c); + } + rl_getc(in_stream); + } + } +#ifdef TIOCSTART + /* Ugh. But I can't think of a better way. */ + if (xoff_state && c == xon_char) + { + ioctl (fileno (in_stream), TIOCSTART, 0); + xoff_state = 0; + return rl_read_key (); + } +#endif /* TIOCSTART */ + return (c); +} + +/* Do the command associated with KEY in MAP. + If the associated command is really a keymap, then read + another key, and dispatch into that map. */ +rl_dispatch (key, map) + register int key; + Keymap map; +{ + if (defining_kbd_macro) + { + static add_macro_char (); + + add_macro_char (key); + } + + if (key > 127 && key < 256) + { + if (map[ESC].type == ISKMAP) + { + map = (Keymap)map[ESC].function; + key -= 128; + rl_dispatch (key, map); + } + else + ding (); + return; + } + + switch (map[key].type) + { + case ISFUNC: + { + Function *func = map[key].function; + + if (func != (Function *)NULL) + { + /* Special case rl_do_lowercase_version (). */ + if (func == rl_do_lowercase_version) + { + rl_dispatch (to_lower (key), map); + return; + } + + (*map[key].function)(rl_numeric_arg * arg_sign, key); + } + else + { + ding (); + return; + } + } + break; + + case ISKMAP: + if (map[key].function != (Function *)NULL) + { + int newkey; + + rl_key_sequence_length++; + newkey = rl_read_key (); + rl_dispatch (newkey, (Keymap)map[key].function); + } + else + { + ding (); + return; + } + break; + + case ISMACR: + if (map[key].function != (Function *)NULL) + { + static with_macro_input (); + char *macro = savestring ((char *)map[key].function); + + with_macro_input (macro); + return; + } + break; + } + + /* If we have input pending, then the last command was a prefix + command. Don't change the state of rl_last_func. */ + if (!rl_pending_input) + rl_last_func = map[key].function; +} + + +/* **************************************************************** */ +/* */ +/* Hacking Keyboard Macros */ +/* */ +/* **************************************************************** */ + +/* Set up to read subsequent input from STRING. + STRING is free ()'ed when we are done with it. */ +static +with_macro_input (string) + char *string; +{ + static push_executing_macro (); + + push_executing_macro (); + executing_macro = string; + executing_macro_index = 0; +} + +/* Return the next character available from a macro, or 0 if + there are no macro characters. */ +static int +next_macro_key () +{ + if (!executing_macro) + return (0); + + if (!executing_macro[executing_macro_index]) + { + static pop_executing_macro (); + + pop_executing_macro (); + return (next_macro_key ()); + } + + return (executing_macro[executing_macro_index++]); +} + +/* Save the currently executing macro on a stack of saved macros. */ +static +push_executing_macro () +{ + struct saved_macro *saver; + + saver = (struct saved_macro *)xmalloc (sizeof (struct saved_macro)); + saver->next = macro_list; + saver->index = executing_macro_index; + saver->string = executing_macro; + + macro_list = saver; +} + +/* Discard the current macro, replacing it with the one + on the top of the stack of saved macros. */ +static +pop_executing_macro () +{ + if (executing_macro) + free (executing_macro); + + executing_macro = (char *)NULL; + executing_macro_index = 0; + + if (macro_list) + { + struct saved_macro *disposer = macro_list; + executing_macro = macro_list->string; + executing_macro_index = macro_list->index; + macro_list = macro_list->next; + free (disposer); + } +} + +/* Add a character to the macro being built. */ +static +add_macro_char (c) + int c; +{ + if (current_macro_index + 1 >= current_macro_size) + { + if (!current_macro) + current_macro = (char *)xmalloc (current_macro_size = 25); + else + current_macro = + (char *)xrealloc (current_macro, current_macro_size += 25); + } + + current_macro[current_macro_index++] = c; + current_macro[current_macro_index] = '\0'; +} + +/* Begin defining a keyboard macro. + Keystrokes are recorded as they are executed. + End the definition with rl_end_kbd_macro (). + If a numeric argument was explicitly typed, then append this + definition to the end of the existing macro, and start by + re-executing the existing macro. */ +rl_start_kbd_macro (ignore1, ignore2) + int ignore1, ignore2; +{ + if (defining_kbd_macro) + rl_abort (); + + if (rl_explicit_arg) + { + if (current_macro) + with_macro_input (savestring (current_macro)); + } + else + current_macro_index = 0; + + defining_kbd_macro = 1; +} + +/* Stop defining a keyboard macro. + A numeric argument says to execute the macro right now, + that many times, counting the definition as the first time. */ +rl_end_kbd_macro (count, ignore) + int count, ignore; +{ + if (!defining_kbd_macro) + rl_abort (); + + current_macro_index -= (rl_key_sequence_length - 1); + current_macro[current_macro_index] = '\0'; + + defining_kbd_macro = 0; + + rl_call_last_kbd_macro (--count, 0); +} + +/* Execute the most recently defined keyboard macro. + COUNT says how many times to execute it. */ +rl_call_last_kbd_macro (count, ignore) + int count, ignore; +{ + if (!current_macro) + rl_abort (); + + while (count--) + with_macro_input (savestring (current_macro)); +} + + +/* Non-zero means do not parse any lines other than comments and + parser directives. */ +static unsigned char parsing_conditionalized_out = 0; + +/* **************************************************************** */ +/* */ +/* Initializations */ +/* */ +/* **************************************************************** */ + +/* Initliaze readline (and terminal if not already). */ +rl_initialize () +{ + extern char *rl_display_prompt; + + /* If we have never been called before, initialize the + terminal and data structures. */ + if (!rl_initialized) + { + readline_initialize_everything (); + rl_initialized++; + } + + /* Initalize the current line information. */ + rl_point = rl_end = 0; + the_line = rl_line_buffer; + the_line[0] = 0; + + /* We aren't done yet. We haven't even gotten started yet! */ + rl_done = 0; + + /* Tell the history routines what is going on. */ + start_using_history (); + + /* Make the display buffer match the state of the line. */ + { + extern char *rl_display_prompt; + extern int forced_display; + + rl_on_new_line (); + + rl_display_prompt = rl_prompt ? rl_prompt : ""; + forced_display = 1; + } + + /* No such function typed yet. */ + rl_last_func = (Function *)NULL; + + /* Parsing of key-bindings begins in an enabled state. */ + { + parsing_conditionalized_out = 0; + } +} + +/* Initialize the entire state of the world. */ +readline_initialize_everything () +{ + /* Find out if we are running in Emacs. */ + running_in_emacs = (char *)getenv ("EMACS"); + + /* Allocate data structures. */ + if (!rl_line_buffer) + rl_line_buffer = + (char *)xmalloc (rl_line_buffer_len = DEFAULT_BUFFER_SIZE); + + /* Initialize the terminal interface. */ + init_terminal_io ((char *)NULL); + + /* Bind tty characters to readline functions. */ + readline_default_bindings (); + + /* Initialize the function names. */ + rl_initialize_funmap (); + + /* Read in the init file. */ + rl_read_init_file ((char *)NULL); + + /* If the completion parser's default word break characters haven't + been set yet, then do so now. */ + { + extern char *rl_completer_word_break_characters; + extern char *rl_basic_word_break_characters; + + if (rl_completer_word_break_characters == (char *)NULL) + rl_completer_word_break_characters = rl_basic_word_break_characters; + } +} + +/* If this system allows us to look at the values of the regular + input editing characters, then bind them to their readline + equivalents. */ +readline_default_bindings () +{ +#ifdef TIOCGETP + struct sgttyb ttybuff; + int tty = fileno (rl_instream); + + if (ioctl (tty, TIOCGETP, &ttybuff) != -1) + { + int erase = ttybuff.sg_erase, kill = ttybuff.sg_kill; + + if (erase != -1 && keymap[erase].type == ISFUNC) + keymap[erase].function = rl_rubout; + + if (kill != -1 && keymap[kill].type == ISFUNC) + keymap[kill].function = rl_unix_line_discard; + } + +#ifdef TIOCGLTC + { + struct ltchars lt; + + if (ioctl (tty, TIOCGLTC, <) != -1) + { + int erase = lt.t_werasc, nextc = lt.t_lnextc; + + if (erase != -1 && keymap[erase].type == ISFUNC) + keymap[erase].function = rl_unix_word_rubout; + + if (nextc != -1 && keymap[nextc].type == ISFUNC) + keymap[nextc].function = rl_quoted_insert; + } + } +#endif /* TIOCGLTC */ +#endif /* TIOCGETP */ +} + + +/* **************************************************************** */ +/* */ +/* Numeric Arguments */ +/* */ +/* **************************************************************** */ + +/* Handle C-u style numeric args, as well as M--, and M-digits. */ + +/* Add the current digit to the argument in progress. */ +rl_digit_argument (ignore, key) + int ignore, key; +{ + rl_pending_input = key; + rl_digit_loop (); +} + +/* What to do when you abort reading an argument. */ +rl_discard_argument () +{ + ding (); + rl_clear_message (); + rl_init_argument (); +} + +/* Create a default argument. */ +rl_init_argument () +{ + rl_numeric_arg = arg_sign = 1; + rl_explicit_arg = 0; +} + +/* C-u, universal argument. Multiply the current argument by 4. + Read a key. If the key has nothing to do with arguments, then + dispatch on it. If the key is the abort character then abort. */ +rl_universal_argument () +{ + rl_numeric_arg *= 4; + rl_digit_loop (); +} + +rl_digit_loop () +{ + int key, c; + while (1) + { + rl_message ("(arg: %d) ", arg_sign * rl_numeric_arg); + key = c = rl_read_key (); + + if (keymap[c].type == ISFUNC && + keymap[c].function == rl_universal_argument) + { + rl_numeric_arg *= 4; + continue; + } + c = UNMETA (c); + if (numeric (c)) + { + if (rl_explicit_arg) + rl_numeric_arg = (rl_numeric_arg * 10) + (c - '0'); + else + rl_numeric_arg = (c - '0'); + rl_explicit_arg = 1; + } + else + { + if (c == '-' && !rl_explicit_arg) + { + rl_numeric_arg = 1; + arg_sign = -1; + } + else + { + rl_clear_message (); + rl_dispatch (key, keymap); + return; + } + } + } +} + + +/* **************************************************************** */ +/* */ +/* Display stuff */ +/* */ +/* **************************************************************** */ + +/* This is the stuff that is hard for me. I never seem to write good + display routines in C. Let's see how I do this time. */ + +/* (PWP) Well... Good for a simple line updater, but totally ignores + the problems of input lines longer than the screen width. + + update_line and the code that calls it makes a multiple line, + automatically wrapping line update. Carefull attention needs + to be paid to the vertical position variables. + + handling of terminals with autowrap on (incl. DEC braindamage) + could be improved a bit. Right now I just cheat and decrement + screenwidth by one. */ + +/* Keep two buffers; one which reflects the current contents of the + screen, and the other to draw what we think the new contents should + be. Then compare the buffers, and make whatever changes to the + screen itself that we should. Finally, make the buffer that we + just drew into be the one which reflects the current contents of the + screen, and place the cursor where it belongs. + + Commands that want to can fix the display themselves, and then let + this function know that the display has been fixed by setting the + RL_DISPLAY_FIXED variable. This is good for efficiency. */ + +/* Termcap variables: */ +extern char *term_up, *term_dc, *term_cr; +extern int screenheight, screenwidth, terminal_can_insert; + +/* What YOU turn on when you have handled all redisplay yourself. */ +int rl_display_fixed = 0; + +/* The visible cursor position. If you print some text, adjust this. */ +int last_c_pos = 0; +int last_v_pos = 0; + +/* The last left edge of text that was displayed. This is used when + doing horizontal scrolling. It shifts in thirds of a screenwidth. */ +static int last_lmargin = 0; + +/* The line display buffers. One is the line currently displayed on + the screen. The other is the line about to be displayed. */ +static char *visible_line = (char *)NULL; +static char *invisible_line = (char *)NULL; + +/* Number of lines currently on screen minus 1. */ +int vis_botlin = 0; + +/* A buffer for `modeline' messages. */ +char msg_buf[128]; + +/* Non-zero forces the redisplay even if we thought it was unnecessary. */ +int forced_display = 0; + +/* The stuff that gets printed out before the actual text of the line. + This is usually pointing to rl_prompt. */ +char *rl_display_prompt = (char *)NULL; + +/* Default and initial buffer size. Can grow. */ +static int line_size = 1024; + +/* Non-zero means to always use horizontal scrolling in line display. */ +int horizontal_scroll_mode = 0; + +/* I really disagree with this, but my boss (among others) insists that we + support compilers that don't work. I don't think we are gaining by doing + so; what is the advantage in producing better code if we can't use it? */ +/* The following two declarations belong inside the + function block, not here. */ +static void move_cursor_relative (); +static void output_some_chars (); + +/* Basic redisplay algorithm. */ +rl_redisplay () +{ + register int in, out, c, linenum; + register char *line = invisible_line; + int c_pos = 0; + int inv_botlin = 0; /* Number of lines in newly drawn buffer. */ + + extern int readline_echoing_p; + + if (!readline_echoing_p) + return; + + if (!rl_display_prompt) + rl_display_prompt = ""; + + if (!invisible_line) + { + visible_line = (char *)xmalloc (line_size); + invisible_line = (char *)xmalloc (line_size); + line = invisible_line; + for (in = 0; in < line_size; in++) + { + visible_line[in] = 0; + invisible_line[in] = 1; + } + rl_on_new_line (); + } + + /* Draw the line into the buffer. */ + c_pos = -1; + + /* Mark the line as modified or not. We only do this for history + lines. */ + out = 0; + if (current_history () && rl_undo_list) + { + line[out++] = '*'; + line[out] = '\0'; + } + + /* If someone thought that the redisplay was handled, but the currently + visible line has a different modification state than the one about + to become visible, then correct the callers misconception. */ + if (visible_line[0] != invisible_line[0]) + rl_display_fixed = 0; + + strncpy (line + out, rl_display_prompt, strlen (rl_display_prompt)); + out += strlen (rl_display_prompt); + line[out] = '\0'; + + for (in = 0; in < rl_end; in++) + { + c = the_line[in]; + + if (out + 1 >= line_size) + { + line_size *= 2; + visible_line = (char *)xrealloc (visible_line, line_size); + invisible_line = (char *)xrealloc (invisible_line, line_size); + line = invisible_line; + } + + if (in == rl_point) + c_pos = out; + + if (c > 127) + { + line[out++] = 'M'; + line[out++] = '-'; + line[out++] = c - 128; + } +#define DISPLAY_TABS +#ifdef DISPLAY_TABS + else if (c == '\t') + { + register int newout = (out | (int)7) + 1; + while (out < newout) + line[out++] = ' '; + } +#endif + else if (c < 32) + { + line[out++] = 'C'; + line[out++] = '-'; + line[out++] = c + 64; + } + else + line[out++] = c; + } + line[out] = '\0'; + if (c_pos < 0) + c_pos = out; + + /* PWP: now is when things get a bit hairy. The visible and invisible + line buffers are really multiple lines, which would wrap every + (screenwidth - 1) characters. Go through each in turn, finding + the changed region and updating it. The line order is top to bottom. */ + + /* If we can move the cursor up and down, then use multiple lines, + otherwise, let long lines display in a single terminal line, and + horizontally scroll it. */ + + if (!horizontal_scroll_mode && term_up && *term_up) + { + int total_screen_chars = (screenwidth * screenheight); + + if (!rl_display_fixed || forced_display) + { + forced_display = 0; + + /* If we have more than a screenful of material to display, then + only display a screenful. We should display the last screen, + not the first. I'll fix this in a minute. */ + if (out >= total_screen_chars) + out = total_screen_chars - 1; + + /* Number of screen lines to display. */ + inv_botlin = out / screenwidth; + + /* For each line in the buffer, do the updating display. */ + for (linenum = 0; linenum <= inv_botlin; linenum++) + update_line (linenum > vis_botlin ? "" + : &visible_line[linenum * screenwidth], + &invisible_line[linenum * screenwidth], + linenum); + + /* We may have deleted some lines. If so, clear the left over + blank ones at the bottom out. */ + if (vis_botlin > inv_botlin) + { + char *tt; + for (; linenum <= vis_botlin; linenum++) + { + tt = &visible_line[linenum * screenwidth]; + move_vert (linenum); + move_cursor_relative (0, tt); + clear_to_eol ((linenum == vis_botlin)? + strlen (tt) : screenwidth); + } + } + vis_botlin = inv_botlin; + + /* Move the cursor where it should be. */ + move_vert (c_pos / screenwidth); + move_cursor_relative (c_pos % screenwidth, + &invisible_line[(c_pos / screenwidth) * screenwidth]); + } + } + else /* Do horizontal scrolling. */ + { + int lmargin; + + /* Always at top line. */ + last_v_pos = 0; + + /* If the display position of the cursor would be off the edge + of the screen, start the display of this line at an offset that + leaves the cursor on the screen. */ + if (c_pos - last_lmargin > screenwidth - 2) + lmargin = (c_pos / (screenwidth / 3) - 2) * (screenwidth / 3); + else if (c_pos - last_lmargin < 1) + lmargin = ((c_pos - 1) / (screenwidth / 3)) * (screenwidth / 3); + else + lmargin = last_lmargin; + + /* If the first character on the screen isn't the first character + in the display line, indicate this with a special character. */ + if (lmargin > 0) + line[lmargin] = '<'; + + if (lmargin + screenwidth < out) + line[lmargin + screenwidth - 1] = '>'; + + if (!rl_display_fixed || forced_display || lmargin != last_lmargin) + { + forced_display = 0; + update_line (&visible_line[last_lmargin], + &invisible_line[lmargin], 0); + + move_cursor_relative (c_pos - lmargin, &invisible_line[lmargin]); + last_lmargin = lmargin; + } + } + fflush (out_stream); + + /* Swap visible and non-visible lines. */ + { + char *temp = visible_line; + visible_line = invisible_line; + invisible_line = temp; + rl_display_fixed = 0; + } +} + +/* PWP: update_line() is based on finding the middle difference of each + line on the screen; vis: + + /old first difference + /beginning of line | /old last same /old EOL + v v v v +old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as +new: eddie> Oh, my little buggy says to me, as lurgid as + ^ ^ ^ ^ + \beginning of line | \new last same \new end of line + \new first difference + + All are character pointers for the sake of speed. Special cases for + no differences, as well as for end of line additions must be handeled. + + Could be made even smarter, but this works well enough */ +static +update_line (old, new, current_line) + register char *old, *new; + int current_line; +{ + register char *ofd, *ols, *oe, *nfd, *nls, *ne; + int lendiff, wsatend; + + /* Find first difference. */ + for (ofd = old, nfd = new; + (ofd - old < screenwidth) && *ofd && (*ofd == *nfd); + ofd++, nfd++) + ; + + /* Move to the end of the screen line. */ + for (oe = ofd; ((oe - old) < screenwidth) && *oe; oe++); + for (ne = nfd; ((ne - new) < screenwidth) && *ne; ne++); + + /* If no difference, continue to next line. */ + if (ofd == oe && nfd == ne) + return; + + wsatend = 1; /* flag for trailing whitespace */ + ols = oe - 1; /* find last same */ + nls = ne - 1; + while ((*ols == *nls) && (ols > ofd) && (nls > nfd)) + { + if (*ols != ' ') + wsatend = 0; + ols--; + nls--; + } + + if (wsatend) + { + ols = oe; + nls = ne; + } + else if (*ols != *nls) + { + if (*ols) /* don't step past the NUL */ + ols++; + if (*nls) + nls++; + } + + move_vert (current_line); + move_cursor_relative (ofd - old, old); + + /* if (len (new) > len (old)) */ + lendiff = (nls - nfd) - (ols - ofd); + + /* Insert (diff(len(old),len(new)) ch */ + if (lendiff > 0) + { + if (terminal_can_insert) + { + extern char *term_IC; + + /* Sometimes it is cheaper to print the characters rather than + use the terminal's capabilities. */ + if ((2 * (ne - nfd)) < lendiff && (!term_IC || !*term_IC)) + { + output_some_chars (nfd, (ne - nfd)); + last_c_pos += (ne - nfd); + } + else + { + if (*ols) + { + start_insert (lendiff); + output_some_chars (nfd, lendiff); + last_c_pos += lendiff; + end_insert (); + } + else + { + /* At the end of a line the characters do not have to + be "inserted". They can just be placed on the screen. */ + output_some_chars (nfd, lendiff); + last_c_pos += lendiff; + } + /* Copy (new) chars to screen from first diff to last match. */ + if (((nls - nfd) - lendiff) > 0) + { + output_some_chars (&nfd[lendiff], ((nls - nfd) - lendiff)); + last_c_pos += ((nls - nfd) - lendiff); + } + } + } + else + { /* cannot insert chars, write to EOL */ + output_some_chars (nfd, (ne - nfd)); + last_c_pos += (ne - nfd); + } + } + else /* Delete characters from line. */ + { + /* If possible and inexpensive to use terminal deletion, then do so. */ + if (term_dc && (2 * (ne - nfd)) >= (-lendiff)) + { + if (lendiff) + delete_chars (-lendiff); /* delete (diff) characters */ + + /* Copy (new) chars to screen from first diff to last match */ + if ((nls - nfd) > 0) + { + output_some_chars (nfd, (nls - nfd)); + last_c_pos += (nls - nfd); + } + } + /* Otherwise, print over the existing material. */ + else + { + output_some_chars (nfd, (ne - nfd)); + last_c_pos += (ne - nfd); + clear_to_eol ((oe - old) - (ne - new)); + } + } +} + +/* (PWP) tell the update routines that we have moved onto a + new (empty) line. */ +rl_on_new_line () +{ + if (visible_line) + visible_line[0] = '\0'; + + last_c_pos = last_v_pos = 0; + vis_botlin = last_lmargin = 0; +} + +/* Actually update the display, period. */ +rl_forced_update_display () +{ + if (visible_line) + { + register char *temp = visible_line; + + while (*temp) *temp++ = '\0'; + } + rl_on_new_line (); + forced_display++; + rl_redisplay (); +} + +/* Move the cursor from last_c_pos to NEW, which are buffer indices. + DATA is the contents of the screen line of interest; i.e., where + the movement is being done. */ +static void +move_cursor_relative (new, data) + int new; + char *data; +{ + register int i; + static void output_character_function (); + + /* It may be faster to output a CR, and then move forwards instead + of moving backwards. */ + if (new + 1 < last_c_pos - new) + { + tputs (term_cr, 1, output_character_function); + last_c_pos = 0; + } + + if (last_c_pos == new) return; + + if (last_c_pos < new) + { + /* Move the cursor forward. We do it by printing the command + to move the cursor forward if there is one, else print that + portion of the output buffer again. Which is cheaper? */ + + /* The above comment is left here for posterity. It is faster + to print one character (non-control) than to print a control + sequence telling the terminal to move forward one character. + That kind of control is for people who don't know what the + data is underneath the cursor. */ +#ifdef HACK_TERMCAP_MOTION + extern char *term_forward_char; + + if (term_forward_char) + for (i = last_c_pos; i < new; i++) + tputs (term_forward_char, 1, output_character_function); + else + for (i = last_c_pos; i < new; i++) + putc (data[i], out_stream); +#else + for (i = last_c_pos; i < new; i++) + putc (data[i], out_stream); +#endif /* HACK_TERMCAP_MOTION */ + } + else + backspace (last_c_pos - new); + last_c_pos = new; +} + +/* PWP: move the cursor up or down. */ +move_vert (to) + int to; +{ + void output_character_function (); + register int delta, i; + + if (last_v_pos == to) return; + + if (to > screenheight) + return; + + if ((delta = to - last_v_pos) > 0) + { + for (i = 0; i < delta; i++) + putc ('\n', out_stream); + tputs (term_cr, 1, output_character_function); + last_c_pos = 0; /* because crlf() will do \r\n */ + } + else + { /* delta < 0 */ + if (term_up && *term_up) + for (i = 0; i < -delta; i++) + tputs (term_up, 1, output_character_function); + } + last_v_pos = to; /* now to is here */ +} + +/* Physically print C on out_stream. This is for functions which know + how to optimize the display. */ +rl_show_char (c) + int c; +{ + if (c > 127) + { + fprintf (out_stream, "M-"); + c -= 128; + } + +#ifdef DISPLAY_TABS + if (c < 32 && c != '\t') +#else + if (c < 32) +#endif + { + + c += 64; + } + + putc (c, out_stream); + fflush (out_stream); +} + +#ifdef DISPLAY_TABS +int +rl_character_len (c, pos) + register int c, pos; +{ + if (c < ' ' || c > 126) + { + if (c == '\t') + return (((pos | (int)7) + 1) - pos); + else + return (3); + } + else + return (1); +} +#else +int +rl_character_len (c) + int c; +{ + if (c < ' ' || c > 126) + return (3); + else + return (1); +} +#endif /* DISPLAY_TAB */ + +/* How to print things in the "echo-area". The prompt is treated as a + mini-modeline. */ +rl_message (string, arg1, arg2) + char *string; +{ + sprintf (msg_buf, string, arg1, arg2); + rl_display_prompt = msg_buf; + rl_redisplay (); +} + +/* How to clear things from the "echo-area". */ +rl_clear_message () +{ + rl_display_prompt = rl_prompt; + rl_redisplay (); +} + +/* **************************************************************** */ +/* */ +/* Terminal and Termcap */ +/* */ +/* **************************************************************** */ + +static char *term_buffer = (char *)NULL; +static char *term_string_buffer = (char *)NULL; + +/* Non-zero means this terminal can't really do anything. */ +int dumb_term = 0; + +char PC; +char *BC, *UP; + +/* Some strings to control terminal actions. These are output by tputs (). */ +char *term_goto, *term_clreol, *term_cr, *term_clrpag, *term_backspace; + +int screenwidth, screenheight; + +/* Non-zero if we determine that the terminal can do character insertion. */ +int terminal_can_insert = 0; + +/* How to insert characters. */ +char *term_im, *term_ei, *term_ic, *term_ip, *term_IC; + +/* How to delete characters. */ +char *term_dc, *term_DC; + +#ifdef HACK_TERMCAP_MOTION +char *term_forward_char; +#endif /* HACK_TERMCAP_MOTION */ + +/* How to go up a line. */ +char *term_up; + +/* Re-initialize the terminal considering that the TERM/TERMCAP variable + has changed. */ +rl_reset_terminal (terminal_name) + char *terminal_name; +{ + init_terminal_io (terminal_name); +} + +init_terminal_io (terminal_name) + char *terminal_name; +{ + char *term = (terminal_name? terminal_name : (char *)getenv ("TERM")); + char *tgetstr (), *buffer; + + + if (!term_string_buffer) + term_string_buffer = (char *)xmalloc (2048); + + if (!term_buffer) + term_buffer = (char *)xmalloc (2048); + + buffer = term_string_buffer; + + term_clrpag = term_cr = term_clreol = (char *)NULL; + + if (!term) + term = "dumb"; + + if (tgetent (term_buffer, term) < 0) + { + dumb_term = 1; + return; + } + + BC = tgetstr ("pc", &buffer); + PC = buffer ? *buffer : 0; + + term_backspace = tgetstr ("le", &buffer); + + term_cr = tgetstr ("cr", &buffer); + term_clreol = tgetstr ("ce", &buffer); + term_clrpag = tgetstr ("cl", &buffer); + + if (!term_cr) + term_cr = "\r"; + +#ifdef HACK_TERMCAP_MOTION + term_forward_char = tgetstr ("nd", &buffer); +#endif /* HACK_TERMCAP_MOTION */ + + screenwidth = tgetnum ("co"); + if (screenwidth <= 0) + screenwidth = 80; + screenwidth--; /* PWP: avoid autowrap bugs */ + + screenheight = tgetnum ("li"); + if (screenheight <= 0) + screenheight = 24; + + term_im = tgetstr ("im", &buffer); + term_ei = tgetstr ("ei", &buffer); + term_IC = tgetstr ("IC", &buffer); + term_ic = tgetstr ("ic", &buffer); + term_ip = tgetstr ("ip", &buffer); + term_IC = tgetstr ("IC", &buffer); + + /* "An application program can assume that the terminal can do + character insertion if *any one of* the capabilities `IC', + `im', `ic' or `ip' is provided." */ +#ifdef notdef + /* XXX Circumvent broken code. */ + terminal_can_insert = (term_IC || term_im || term_ic || term_ip); +#endif + + term_up = tgetstr ("up", &buffer); + term_dc = tgetstr ("dc", &buffer); + term_DC = tgetstr ("DC", &buffer); +} + +/* A function for the use of tputs () */ +static void +output_character_function (c) + int c; +{ + putc (c, out_stream); +} + +/* Write COUNT characters from STRING to the output stream. */ +static void +output_some_chars (string, count) + char *string; + int count; +{ + fwrite (string, 1, count, out_stream); +} + + +/* Delete COUNT characters from the display line. */ +static +delete_chars (count) + int count; +{ + if (count > screenwidth) + return; + + if (term_DC && *term_DC) + { + char *tgoto (), *buffer; + buffer = tgoto (term_DC, 0, count); + tputs (buffer, 1, output_character_function); + } + else + { + if (term_dc && *term_dc) + while (count--) + tputs (term_dc, 1, output_character_function); + } +} + +/* Prepare to insert by inserting COUNT blank spaces. */ +static +start_insert (count) + int count; +{ + if (term_im && *term_im) + tputs (term_im, 1, output_character_function); + + if (term_IC && *term_IC && + (count > 1 || !term_ic || !*term_ic)) + { + char *tgoto (), *buffer; + buffer = tgoto (term_IC, 0, count); + tputs (buffer, 1, output_character_function); + } + else + { + if (term_ic && *term_ic) + while (count--) + tputs (term_ic, 1, output_character_function); + } +} + +/* We are finished doing our insertion. Send ending string. */ +static +end_insert () +{ + if (term_ei && *term_ei) + tputs (term_ei, 1, output_character_function); +} + +/* Move the cursor back. */ +backspace (count) + int count; +{ + register int i; + + if (term_backspace) + for (i = 0; i < count; i++) + tputs (term_backspace, 1, output_character_function); + else + for (i = 0; i < count; i++) + putc ('\b', out_stream); +} + +/* Move to the start of the next line. */ +crlf () +{ + tputs (term_cr, 1, output_character_function); + putc ('\n', out_stream); +} + +/* Clear to the end of the line. COUNT is the minimum + number of character spaces to clear, */ +clear_to_eol (count) + int count; +{ + if (term_clreol) { + tputs (term_clreol, 1, output_character_function); + } else { + register int i; + /* Do one more character space. */ + count++; + for (i = 0; i < count; i++) + putc (' ', out_stream); + backspace (count); + } +} + + +/* **************************************************************** */ +/* */ +/* Saving and Restoring the TTY */ +/* */ +/* **************************************************************** */ + +#ifdef NEW_TTY_DRIVER + +/* Standard flags, including ECHO. */ +static int original_tty_flags = 0; + +/* Local mode flags, like LPASS8. */ +static int local_mode_flags = 0; + +/* Terminal characters. This has C-s and C-q in it. */ +static struct tchars original_tchars; + +/* Local special characters. This has the interrupt characters in it. */ +static struct ltchars original_ltchars; + +/* We use this to get and set the tty_flags. */ +static struct sgttyb the_ttybuff; + +/* Put the terminal in CBREAK mode so that we can detect key presses. */ +static +rl_prep_terminal () +{ + int tty = fileno (rl_instream); + + /* We always get the latest tty values. Maybe stty changed them. */ + + ioctl (tty, TIOCGETP, &the_ttybuff); + original_tty_flags = the_ttybuff.sg_flags; + + readline_echoing_p = (original_tty_flags & ECHO); + + /* If this terminal doesn't care how the 8th bit is used, + then we can use it for the meta-key. + We check by seeing if BOTH odd and even parity are allowed. */ + if ((the_ttybuff.sg_flags & (ODDP | EVENP)) == (ODDP | EVENP)) + { +#ifdef PASS8 + the_ttybuff.sg_flags |= PASS8; +#endif + +#if defined (TIOCLGET) && defined (LPASS8) + { + int flags; + ioctl (tty, TIOCLGET, &flags); + local_mode_flags = flags; + flags |= LPASS8; + ioctl (tty, TIOCLSET, &flags); + } +#endif + } + +#ifdef TIOCGETC + { + struct tchars temp; + + ioctl (tty, TIOCGETC, &original_tchars); + bcopy (&original_tchars, &temp, sizeof (struct tchars)); + + /* Get rid of C-s and C-q. + We remember the value of startc (C-q) so that if the terminal is in + xoff state, the user can xon it by pressing that character. */ + xon_char = temp.t_startc; + temp.t_stopc = -1; + temp.t_startc = -1; + + /* If there is an XON character, bind it to restart the output. */ + if (xon_char != -1) + rl_bind_key (xon_char, rl_restart_output); + + /* If there is an EOF char, bind eof_char to it. */ + if (temp.t_eofc != -1) + eof_char = temp.t_eofc; + +#ifdef NEVER + /* Get rid of C-\ and C-c. */ + temp.t_intrc = temp.t_quitc = -1; +#endif + + ioctl (tty, TIOCSETC, &temp); + } +#endif /* TIOCGETC */ + +#ifdef TIOCGLTC + { + struct ltchars temp; + + ioctl (tty, TIOCGLTC, &original_ltchars); + bcopy (&original_ltchars, &temp, sizeof (struct ltchars)); + + /* Make the interrupt keys go away. Just enough to make people happy. */ + temp.t_dsuspc = -1; /* C-y */ + temp.t_lnextc = -1; /* C-v */ + + ioctl (tty, TIOCSLTC, &temp); + } +#endif /* TIOCGLTC */ + + the_ttybuff.sg_flags &= ~ECHO; + the_ttybuff.sg_flags |= CBREAK; + ioctl (tty, TIOCSETN, &the_ttybuff); +} + +/* Restore the terminal to its original state. */ +static +rl_deprep_terminal () +{ + int tty = fileno (rl_instream); + +#if defined (TIOCLGET) && defined (LPASS8) + if ((the_ttybuff.sg_flags & (ODDP | EVENP)) == (ODDP | EVENP)) + ioctl (tty, TIOCLSET, &local_mode_flags); +#endif + +#ifdef TIOCSLTC + ioctl (tty, TIOCSLTC, &original_ltchars); +#endif + +#ifdef TIOCSETC + ioctl (tty, TIOCSETC, &original_tchars); +#endif + + the_ttybuff.sg_flags = original_tty_flags; + ioctl (tty, TIOCSETN, &the_ttybuff); + readline_echoing_p = 1; +} + +#else /* !defined (NEW_TTY_DRIVER) */ +static struct termio otio; + +static +rl_prep_terminal () +{ + int tty = fileno (rl_instream); + struct termio tio; + + ioctl (tty, TCGETA, &tio); + ioctl (tty, TCGETA, &otio); + + readline_echoing_p = (tio.c_lflag & ECHO); + + tio.c_lflag &= ~(ICANON|ECHO); + tio.c_iflag &= ~(IXON|ISTRIP|INPCK); + +#ifndef HANDLE_SIGNALS + tio.c_lflag &= ~ISIG; +#endif + + tio.c_cc[VEOF] = 1; /* really: MIN */ + tio.c_cc[VEOL] = 0; /* really: TIME */ + ioctl (tty, TCSETAW,&tio); +} + +static +rl_deprep_terminal () +{ + int tty = fileno (rl_instream); + ioctl (tty, TCSETAW, &otio); +} +#endif /* NEW_TTY_DRIVER */ + + +/* **************************************************************** */ +/* */ +/* Utility Functions */ +/* */ +/* **************************************************************** */ + +/* Return 0 if C is not a member of the class of characters that belong + in words, or 1 if it is. */ + +int allow_pathname_alphabetic_chars = 0; +char *pathname_alphabetic_chars = "/-_=~.#$"; + +int +alphabetic (c) + int c; +{ + char *rindex (); + if (pure_alphabetic (c) || (numeric (c))) + return (1); + + if (allow_pathname_alphabetic_chars) + return ((int)rindex (pathname_alphabetic_chars, c)); + else + return (0); +} + +/* Return non-zero if C is a numeric character. */ +int +numeric (c) + int c; +{ + return (c >= '0' && c <= '9'); +} + +/* Ring the terminal bell. */ +int +ding () +{ + if (readline_echoing_p) + { + fprintf (stderr, "\007"); + fflush (stderr); + } + return (-1); +} + +/* How to abort things. */ +rl_abort () +{ + ding (); + rl_clear_message (); + rl_init_argument (); + rl_pending_input = 0; + + defining_kbd_macro = 0; + while (executing_macro) + pop_executing_macro (); + + longjmp (readline_top_level, 1); +} + +/* Return a copy of the string between FROM and TO. + FROM is inclusive, TO is not. */ +char * +rl_copy (from, to) + int from, to; +{ + register int length; + char *copy; + + /* Fix it if the caller is confused. */ + if (from > to) { + int t = from; + from = to; + to = t; + } + + length = to - from; + copy = (char *)xmalloc (1 + length); + strncpy (copy, the_line + from, length); + copy[length] = '\0'; + return (copy); +} + + +/* **************************************************************** */ +/* */ +/* Insert and Delete */ +/* */ +/* **************************************************************** */ + + +/* Insert a string of text into the line at point. This is the only + way that you should do insertion. rl_insert () calls this + function. */ +rl_insert_text (string) + char *string; +{ + extern int doing_an_undo; + register int i, l = strlen (string); + while (rl_end + l >= rl_line_buffer_len) + { + rl_line_buffer = + (char *)xrealloc (rl_line_buffer, + rl_line_buffer_len += DEFAULT_BUFFER_SIZE); + the_line = rl_line_buffer; + } + + for (i = rl_end; i >= rl_point; i--) + the_line[i + l] = the_line[i]; + strncpy (the_line + rl_point, string, l); + + /* Remember how to undo this if we aren't undoing something. */ + if (!doing_an_undo) + { + /* If possible and desirable, concatenate the undos. */ + if ((strlen (string) == 1) && + rl_undo_list && + (rl_undo_list->what == UNDO_INSERT) && + (rl_undo_list->end == rl_point) && + (rl_undo_list->end - rl_undo_list->start < 20)) + rl_undo_list->end++; + else + rl_add_undo (UNDO_INSERT, rl_point, rl_point + l, (char *)NULL); + } + rl_point += l; + rl_end += l; + the_line[rl_end] = '\0'; +} + +/* Delete the string between FROM and TO. FROM is + inclusive, TO is not. */ +rl_delete_text (from, to) + int from, to; +{ + extern int doing_an_undo; + register char *text; + + /* Fix it if the caller is confused. */ + if (from > to) { + int t = from; + from = to; + to = t; + } + text = rl_copy (from, to); + strncpy (the_line + from, the_line + to, rl_end - to); + + /* Remember how to undo this delete. */ + if (!doing_an_undo) + rl_add_undo (UNDO_DELETE, from, to, text); + else + free (text); + + rl_end -= (to - from); + the_line[rl_end] = '\0'; +} + + +/* **************************************************************** */ +/* */ +/* Readline character functions */ +/* */ +/* **************************************************************** */ + +/* This is not a gap editor, just a stupid line input routine. No hair + is involved in writing any of the functions, and none should be. */ + +/* Note that: + + rl_end is the place in the string that we would place '\0'; + i.e., it is always safe to place '\0' there. + + rl_point is the place in the string where the cursor is. Sometimes + this is the same as rl_end. + + Any command that is called interactively receives two arguments. + The first is a count: the numeric arg pased to this command. + The second is the key which invoked this command. +*/ + + +/* **************************************************************** */ +/* */ +/* Movement Commands */ +/* */ +/* **************************************************************** */ + +/* Note that if you `optimize' the display for these functions, you cannot + use said functions in other functions which do not do optimizing display. + I.e., you will have to update the data base for rl_redisplay, and you + might as well let rl_redisplay do that job. */ + +/* Move forward COUNT characters. */ +rl_forward (count) + int count; +{ + if (count < 0) + rl_backward (-count); + else + while (count) + { +#ifdef VI_MODE + if (rl_point == (rl_end - (rl_editing_mode == vi_mode))) +#else + if (rl_point == rl_end) +#endif + { + ding (); + return; + } + else + rl_point++; + --count; + } +} + +/* Move backward COUNT characters. */ +rl_backward (count) + int count; +{ + if (count < 0) + rl_forward (-count); + else + while (count) + { + if (!rl_point) + { + ding (); + return; + } + else + --rl_point; + --count; + } +} + +/* Move to the beginning of the line. */ +rl_beg_of_line () +{ + rl_point = 0; +} + +/* Move to the end of the line. */ +rl_end_of_line () +{ + rl_point = rl_end; +} + +/* Move forward a word. We do what Emacs does. */ +rl_forward_word (count) + int count; +{ + int c; + + if (count < 0) + { + rl_backward_word (-count); + return; + } + + while (count) + { + if (rl_point == rl_end) + return; + + /* If we are not in a word, move forward until we are in one. + Then, move forward until we hit a non-alphabetic character. */ + c = the_line[rl_point]; + if (!alphabetic (c)) + { + while (++rl_point < rl_end) + { + c = the_line[rl_point]; + if (alphabetic (c)) break; + } + } + if (rl_point == rl_end) return; + while (++rl_point < rl_end) + { + c = the_line[rl_point]; + if (!alphabetic (c)) break; + } + --count; + } +} + +/* Move backward a word. We do what Emacs does. */ +rl_backward_word (count) + int count; +{ + int c; + + if (count < 0) + { + rl_forward_word (-count); + return; + } + + while (count) + { + if (!rl_point) + return; + + /* Like rl_forward_word (), except that we look at the characters + just before point. */ + + c = the_line[rl_point - 1]; + if (!alphabetic (c)) + { + while (--rl_point) + { + c = the_line[rl_point - 1]; + if (alphabetic (c)) break; + } + } + + while (rl_point) + { + c = the_line[rl_point - 1]; + if (!alphabetic (c)) + break; + else --rl_point; + } + --count; + } +} + +/* Clear the current line. Numeric argument to C-l does this. */ +rl_refresh_line () +{ + int curr_line = last_c_pos / screenwidth; + + move_vert(curr_line); + move_cursor_relative (0, the_line); /* XXX is this right */ + rl_forced_update_display (); + rl_display_fixed = 1; +} + +/* C-l typed to a line without quoting clears the screen, and then reprints + the prompt and the current input line. Given a numeric arg, redraw only + the current line. */ +rl_clear_screen () +{ + extern char *term_clrpag; + static void output_character_function (); + + if (rl_explicit_arg) + { + rl_refresh_line (); + return; + } + + if (term_clrpag) + tputs (term_clrpag, 1, output_character_function); + else + crlf (); + + rl_forced_update_display (); + rl_display_fixed = 1; +} + + +/* **************************************************************** */ +/* */ +/* Text commands */ +/* */ +/* **************************************************************** */ + +/* Insert the character C at the current location, moving point forward. */ +rl_insert (count, c) + int count, c; +{ + register int i; + char *string; + + if (count <= 0) + return; + + /* If we can optimize, then do it. But don't let people crash + readline because of extra large arguments. */ + if (count > 1 && count < 1024) + { + string = (char *)alloca (1 + count); + + for (i = 0; i < count; i++) + string[i] = c; + + string[i] = '\0'; + rl_insert_text (string); + return; + } + + if (count > 1024) + { + int descreaser; + + string = (char *)alloca (1024 + 1); + + for (i = 0; i < 1024; i++) + string[i] = c; + + while (count) + { + descreaser = (count > 1024 ? 1024 : count); + string[descreaser] = '\0'; + rl_insert_text (string); + count -= descreaser; + } + return; + } + + /* We are inserting a single character. + If there is pending input, then make a string of all of the + pending characters that are bound to rl_insert, and insert + them all. */ + if (any_typein) + { + int slen, key = 0, t; + + i = 0; + string = (char *)alloca (ibuffer_len + 1); + string[i++] = c; + + while ((key = rl_get_char()) != -2 && + (keymap[key].type == ISFUNC && + keymap[key].function == rl_insert)) + string[i++] = key; + + if (key != -2) + rl_unget_char (key); + + string[i] = '\0'; + rl_insert_text (string); + return; + } + else + { + /* Inserting a single character. */ + string = (char *)alloca (2); + + string[1] = '\0'; + string[0] = c; + rl_insert_text (string); + } +} + +/* Insert the next typed character verbatim. */ +rl_quoted_insert (count) + int count; +{ + int c = rl_read_key (in_stream); + rl_insert (count, c); +} + +/* Insert a tab character. */ +rl_tab_insert (count) + int count; +{ + rl_insert (count, '\t'); +} + +#ifdef VI_MODE +/* Non-zero means enter insertion mode. */ +static vi_doing_insert = 0; +#endif + +/* What to do when a NEWLINE is pressed. We accept the whole line. + KEY is the key that invoked this command. I guess it could have + meaning in the future. */ +rl_newline (count, key) + int count, key; +{ + + rl_done = 1; + +#ifdef VI_MODE + { + if (vi_doing_insert) + { + rl_end_undo_group (); + vi_doing_insert = 0; + } + } +#endif /* VI_MODE */ + + if (readline_echoing_p) + { + move_vert (vis_botlin); + vis_botlin = 0; + crlf (); + fflush (out_stream); + rl_display_fixed++; + } +} + +rl_clean_up_for_exit () +{ + if (readline_echoing_p) + { + move_vert (vis_botlin); + vis_botlin = 0; + fflush (out_stream); + rl_restart_output (); + } +} + +/* What to do for some uppercase characters, like meta characters, + and some characters appearing in emacs_ctlx_keymap. This function + is just a stub, you bind keys to it and the code in rl_dispatch () + is special cased. */ +rl_do_lowercase_version (ignore1, ignore2) + int ignore1, ignore2; +{ +} + +/* Rubout the character behind point. */ +rl_rubout (count) + int count; +{ + if (count < 0) + { + rl_delete (-count); + return; + } + + if (!rl_point) + { + ding (); + return; + } + + if (count > 1) + { + int orig_point = rl_point; + rl_backward (count); + rl_kill_text (orig_point, rl_point); + } + else + { + int c = the_line[--rl_point]; + rl_delete_text (rl_point, rl_point + 1); + + if (rl_point == rl_end && alphabetic (c) && last_c_pos) + { + backspace (1); + putc (' ', out_stream); + backspace (1); + last_c_pos--; + rl_display_fixed++; + } + } +} + +/* Delete the character under the cursor. Given a numeric argument, + kill that many characters instead. */ +rl_delete (count, invoking_key) + int count; +{ + if (count < 0) + { + rl_rubout (-count); + return; + } + + if (rl_point == rl_end) + { + ding (); + return; + } + +#ifdef VI_MODE + if ((count > 1) || ((count == 1) && (rl_editing_mode == vi_mode))) +#else + if (count > 1) +#endif + { + int orig_point = rl_point; + while (count && (rl_point < rl_end)) + { + rl_point++; + count--; + } + rl_kill_text (orig_point, rl_point); + rl_point = orig_point; + } + else + rl_delete_text (rl_point, rl_point + 1); +} + + +/* **************************************************************** */ +/* */ +/* Kill commands */ +/* */ +/* **************************************************************** */ + +/* The next two functions mimic unix line editing behaviour, except they + save the deleted text on the kill ring. This is safer than not saving + it, and since we have a ring, nobody should get screwed. */ + +/* This does what C-w does in Unix. We can't prevent people from + using behaviour that they expect. */ +rl_unix_word_rubout () +{ + if (!rl_point) ding (); + else { + int orig_point = rl_point; + while (rl_point && whitespace (the_line[rl_point - 1])) + rl_point--; + while (rl_point && !whitespace (the_line[rl_point - 1])) + rl_point--; + rl_kill_text (rl_point, orig_point); + } +} + +/* Here is C-u doing what Unix does. You don't *have* to use these + key-bindings. We have a choice of killing the entire line, or + killing from where we are to the start of the line. We choose the + latter, because if you are a Unix weenie, then you haven't backspaced + into the line at all, and if you aren't, then you know what you are + doing. */ +rl_unix_line_discard () +{ + if (!rl_point) ding (); + else { + rl_kill_text (rl_point, 0); + rl_point = 0; + } +} + + + +/* **************************************************************** */ +/* */ +/* Commands For Typos */ +/* */ +/* **************************************************************** */ + +/* Random and interesting things in here. */ + + +/* **************************************************************** */ +/* */ +/* Changing Case */ +/* */ +/* **************************************************************** */ + +/* The three kinds of things that we know how to do. */ +#define UpCase 1 +#define DownCase 2 +#define CapCase 3 + +/* Uppercase the word at point. */ +rl_upcase_word (count) + int count; +{ + rl_change_case (count, UpCase); +} + +/* Lowercase the word at point. */ +rl_downcase_word (count) + int count; +{ + rl_change_case (count, DownCase); +} + +/* Upcase the first letter, downcase the rest. */ +rl_capitalize_word (count) + int count; +{ + rl_change_case (count, CapCase); +} + +/* The meaty function. + Change the case of COUNT words, performing OP on them. + OP is one of UpCase, DownCase, or CapCase. + If a negative argument is given, leave point where it started, + otherwise, leave it where it moves to. */ +rl_change_case (count, op) + int count, op; +{ + register int start = rl_point, end; + int state = 0; + + rl_forward_word (count); + end = rl_point; + + if (count < 0) + { + int temp = start; + start = end; + end = temp; + } + + /* We are going to modify some text, so let's prepare to undo it. */ + rl_modifying (start, end); + + for (; start < end; start++) + { + switch (op) + { + case UpCase: + the_line[start] = to_upper (the_line[start]); + break; + + case DownCase: + the_line[start] = to_lower (the_line[start]); + break; + + case CapCase: + if (state == 0) + { + the_line[start] = to_upper (the_line[start]); + state = 1; + } + else + { + the_line[start] = to_lower (the_line[start]); + } + if (!pure_alphabetic (the_line[start])) + state = 0; + break; + + default: + abort (); + } + } + rl_point = end; +} + +/* **************************************************************** */ +/* */ +/* Transposition */ +/* */ +/* **************************************************************** */ + +/* Transpose the words at point. */ +rl_transpose_words (count) + int count; +{ + char *word1, *word2; + int w1_beg, w1_end, w2_beg, w2_end; + int orig_point = rl_point; + + if (!count) return; + + /* Find the two words. */ + rl_forward_word (count); + w2_end = rl_point; + rl_backward_word (1); + w2_beg = rl_point; + rl_backward_word (count); + w1_beg = rl_point; + rl_forward_word (1); + w1_end = rl_point; + + /* Do some check to make sure that there really are two words. */ + if ((w1_beg == w2_beg) || (w2_beg < w1_end)) + { + ding (); + rl_point = orig_point; + return; + } + + /* Get the text of the words. */ + word1 = rl_copy (w1_beg, w1_end); + word2 = rl_copy (w2_beg, w2_end); + + /* We are about to do many insertions and deletions. Remember them + as one operation. */ + rl_begin_undo_group (); + + /* Do the stuff at word2 first, so that we don't have to worry + about word1 moving. */ + rl_point = w2_beg; + rl_delete_text (w2_beg, w2_end); + rl_insert_text (word1); + + rl_point = w1_beg; + rl_delete_text (w1_beg, w1_end); + rl_insert_text (word2); + + /* This is exactly correct since the text before this point has not + changed in length. */ + rl_point = w2_end; + + /* I think that does it. */ + rl_end_undo_group (); + free (word1); free (word2); +} + +/* Transpose the characters at point. If point is at the end of the line, + then transpose the characters before point. */ +rl_transpose_chars (count) + int count; +{ + if (!count) + return; + + if (!rl_point || rl_end < 2) { + ding (); + return; + } + + while (count) { + if (rl_point == rl_end) { + int t = the_line[rl_point - 1]; + the_line[rl_point - 1] = the_line[rl_point - 2]; + the_line[rl_point - 2] = t; + } else { + int t = the_line[rl_point]; + the_line[rl_point] = the_line[rl_point - 1]; + the_line[rl_point - 1] = t; + if (count < 0 && rl_point) + rl_point--; + else + rl_point++; + } + if (count < 0) + count++; + else + count--; + } +} + + +/* **************************************************************** */ +/* */ +/* Bogus Flow Control */ +/* */ +/* **************************************************************** */ + +rl_restart_output (count, key) + int count, key; +{ + int fildes = fileno (stdin); +#ifdef TIOCSTART + ioctl (fildes, TIOCSTART, 0); +#endif /* TIOCSTART */ +} + +/* **************************************************************** */ +/* */ +/* Completion matching, from readline's point of view. */ +/* */ +/* **************************************************************** */ + +/* Pointer to the generator function for completion_matches (). + NULL means to use filename_entry_function (), the default filename + completer. */ +Function *rl_completion_entry_function = (Function *)NULL; + +/* Pointer to alternative function to create matches. + Function is called with TEXT, START, and END. + START and END are indices in RL_LINE_BUFFER saying what the boundaries + of TEXT are. + If this function exists and returns NULL then call the value of + rl_completion_entry_function to try to match, otherwise use the + array of strings returned. */ +Function *rl_attempted_completion_function = (Function *)NULL; + +/* Complete the word at or before point. You have supplied the function + that does the initial simple matching selection algorithm (see + completion_matches ()). The default is to do filename completion. */ +rl_complete (ignore, invoking_key) + int ignore, invoking_key; +{ + rl_complete_internal (TAB); + if (running_in_emacs) + printf ("%s", the_line); +} + +/* List the possible completions. See description of rl_complete (). */ +rl_possible_completions () +{ + rl_complete_internal ('?'); +} + +/* The user must press "y" or "n". Non-zero return means "y" pressed. */ +get_y_or_n () +{ + int c; + loop: + c = rl_read_key (in_stream); + if (c == 'y' || c == 'Y') return (1); + if (c == 'n' || c == 'N') return (0); + if (c == ABORT_CHAR) rl_abort (); + ding (); goto loop; +} + +/* Up to this many items will be displayed in response to a + possible-completions call. After that, we ask the user if + she is sure she wants to see them all. */ +int rl_completion_query_items = 100; + +/* The basic list of characters that signal a break between words for the + completer routine. The contents of this variable is what breaks words + in the shell, i.e. " \t\n\"\\'`@$><=" */ +char *rl_basic_word_break_characters = " \t\n\"\\'`@$><="; + +/* The list of characters that signal a break between words for + rl_complete_internal. The default list is the contents of + rl_basic_word_break_characters. */ +char *rl_completer_word_break_characters = (char *)NULL; + +/* List of characters that are word break characters, but should be left + in TEXT when it is passed to the completion function. The shell uses + this to help determine what kind of completing to do. */ +char *rl_special_prefixes = (char *)NULL; + +/* If non-zero, then disallow duplicates in the matches. */ +int rl_ignore_completion_duplicates = 1; + +/* Non-zero means that the results of the matches are to be treated + as filenames. This is ALWAYS zero on entry, and can only be changed + within a completion entry finder function. */ +int rl_filename_completion_desired = 0; + +/* Complete the word at or before point. + WHAT_TO_DO says what to do with the completion. + `?' means list the possible completions. + TAB means do standard completion. + `*' means insert all of the possible completions. */ +rl_complete_internal (what_to_do) + int what_to_do; +{ + char *filename_completion_function (); + char **completion_matches (), **matches; + Function *our_func; + int start, end, delimiter = 0; + char *text; + + if (rl_completion_entry_function) + our_func = rl_completion_entry_function; + else + our_func = (int (*)())filename_completion_function; + + /* Only the completion entry function can change this. */ + rl_filename_completion_desired = 0; + + /* We now look backwards for the start of a filename/variable word. */ + end = rl_point; + if (rl_point) + { + while (--rl_point && + !rindex (rl_completer_word_break_characters, the_line[rl_point])); + + /* If we are at a word break, then advance past it. */ + if (rindex (rl_completer_word_break_characters, (the_line[rl_point]))) + { + /* If the character that caused the word break was a quoting + character, then remember it as the delimiter. */ + if (rindex ("\"'", the_line[rl_point]) && (end - rl_point) > 1) + delimiter = the_line[rl_point]; + + /* If the character isn't needed to determine something special + about what kind of completion to perform, then advance past it. */ + + if (!rl_special_prefixes || + !rindex (rl_special_prefixes, the_line[rl_point])) + rl_point++; + } + } + + start = rl_point; + rl_point = end; + text = rl_copy (start, end); + + /* If the user wants to TRY to complete, but then wants to give + up and use the default completion function, they set the + variable rl_attempted_completion_function. */ + if (rl_attempted_completion_function) + { + matches = + (char **)(*rl_attempted_completion_function) (text, start, end); + + if (matches) + goto after_usual_completion; + } + + matches = completion_matches (text, our_func, start, end); + + after_usual_completion: + free (text); + + if (!matches) + ding (); + else + { + register int i; + + some_matches: + + /* It seems to me that in all the cases we handle we would like + to ignore duplicate possiblilities. Scan for the text to + insert being identical to the other completions. */ + if (rl_ignore_completion_duplicates) + { + char *lowest_common; + int j, newlen = 0; + + /* Sort the items. */ + /* It is safe to sort this array, because the lowest common + denominator found in matches[0] will remain in place. */ + for (i = 0; matches[i]; i++); + qsort (matches, i, sizeof (char *), compare_strings); + + /* Remember the lowest common denimator for it may be unique. */ + lowest_common = savestring (matches[0]); + + for (i = 0; matches[i + 1]; i++) + { + if (strcmp (matches[i], matches[i + 1]) == 0) + { + free (matches[i]); + matches[i] = (char *)-1; + } + else + newlen++; + } + + /* We have marked all the dead slots with (char *)-1. + Copy all the non-dead entries into a new array. */ + { + char **temp_array = + (char **)malloc ((3 + newlen) * sizeof (char *)); + + for (i = 1, j = 1; matches[i]; i++) + if (matches[i] != (char *)-1) + temp_array[j++] = matches[i]; + temp_array[j] = (char *)NULL; + + if (matches[0] != (char *)-1) + free (matches[0]); + free (matches); + + matches = temp_array; + } + + /* Place the lowest common denominator back in [0]. */ + matches[0] = lowest_common; + + /* If there is one string left, and it is identical to the + lowest common denominator, then the LCD is the string to + insert. */ + if (j == 2 && strcmp (matches[0], matches[1]) == 0) + { + free (matches[1]); + matches[1] = (char *)NULL; + } + } + + switch (what_to_do) + { + case TAB: + rl_delete_text (start, rl_point); + rl_point = start; + rl_insert_text (matches[0]); + + /* If there are more matches, ring the bell to indicate. + If this was the only match, and we are hacking files, + check the file to see if it was a directory. If so, + add a '/' to the name. If not, and we are at the end + of the line, then add a space. */ + if (matches[1]) + { + ding (); /* There are other matches remaining. */ + } + else + { + char temp_string[2]; + + temp_string[0] = delimiter ? delimiter : ' '; + temp_string[1] = '\0'; + + if (rl_filename_completion_desired) + { + struct stat finfo; + char *tilde_expand (); + char *filename = tilde_expand (matches[0]); + + if ((stat (filename, &finfo) == 0) && + ((finfo.st_mode & S_IFMT) == S_IFDIR)) + { + if (the_line[rl_point] != '/') + rl_insert_text ("/"); + } + else + { + if (rl_point == rl_end) + rl_insert_text (temp_string); + } + free (filename); + } + else + { + if (rl_point == rl_end) + rl_insert_text (temp_string); + } + } + break; + + case '*': + { + int i = 1; + + rl_delete_text (start, rl_point); + rl_point = start; + rl_begin_undo_group (); + if (matches[1]) + { + while (matches[i]) + { + rl_insert_text (matches[i++]); + rl_insert_text (" "); + } + } + else + { + rl_insert_text (matches[0]); + rl_insert_text (" "); + } + rl_end_undo_group (); + } + break; + + + case '?': + { + int len, count, limit, max = 0; + int j, k, l; + + /* Handle simple case first. What if there is only one answer? */ + if (!matches[1]) + { + char *rindex (), *temp; + + if (rl_filename_completion_desired) + temp = rindex (matches[0], '/'); + else + temp = (char *)NULL; + + if (!temp) + temp = matches[0]; + else + temp++; + + crlf (); + fprintf (out_stream, "%s", temp); + crlf (); + goto restart; + } + + /* There is more than one answer. Find out how many there are, + and find out what the maximum printed length of a single entry + is. */ + for (i = 1; matches[i]; i++) + { + char *rindex (), *temp = (char *)NULL; + + /* If we are hacking filenames, then only count the characters + after the last slash in the pathname. */ + if (rl_filename_completion_desired) + temp = rindex (matches[i], '/'); + else + temp = (char *)NULL; + + if (!temp) + temp = matches[i]; + else + temp++; + + if (strlen (temp) > max) + max = strlen (temp); + } + + len = i; + + /* If there are many items, then ask the user if she + really wants to see them all. */ + if (len >= rl_completion_query_items) + { + crlf (); + fprintf (out_stream, + "There are %d possibilities. Do you really", len); + crlf (); + fprintf (out_stream, "wish to see them all? (y or n)"); + fflush (out_stream); + if (!get_y_or_n ()) + { + crlf (); + goto restart; + } + } + /* How many items of MAX length can we fit in the screen window? */ + max += 2; + limit = screenwidth / max; + if (limit != 1 && (limit * max == screenwidth)) + limit--; + + /* How many iterations of the printing loop? */ + count = (len + (limit - 1)) / limit; + + /* Watch out for special case. If LEN is less than LIMIT, then + just do the inner printing loop. */ + if (len < limit) count = 1; + + /* Sort the items if they are not already sorted. */ + if (!rl_ignore_completion_duplicates) + { + qsort (matches, len, sizeof (char *), compare_strings); + } + + /* Print the sorted items, up-and-down alphabetically, like + ls might. */ + crlf (); + + for (i = 1; i < count + 1; i++) + { + for (j = 0, l = i; j < limit; j++) + { + if (l > len || !matches[l]) + { + break; + } + else + { + char *rindex (), *temp = (char *)NULL; + + if (rl_filename_completion_desired) + temp = rindex (matches[l], '/'); + else + temp = (char *)NULL; + + if (!temp) + temp = matches[l]; + else + temp++; + + fprintf (out_stream, "%s", temp); + for (k = 0; k < max - strlen (temp); k++) + putc (' ', out_stream); + } + l += count; + } + crlf (); + } + restart: + + rl_on_new_line (); + } + break; + + default: + abort (); + } + + for (i = 0; matches[i]; i++) + free (matches[i]); + free (matches); + } +} + +/* A completion function for usernames. + TEXT contains a partial username preceded by a random + character (usually `~'). */ +char * +username_completion_function (text, state) + int state; + char *text; +{ + static char *username = (char *)NULL; + static struct passwd *entry; + static int namelen; + + if (!state) + { + if (username) + free (username); + username = savestring (&text[1]); + namelen = strlen (username); + setpwent (); + } + + while (entry = getpwent ()) + { + if (strncmp (username, entry->pw_name, namelen) == 0) + break; + } + + if (!entry) + { + endpwent (); + return ((char *)NULL); + } + else + { + char *value = (char *)xmalloc (2 + strlen (entry->pw_name)); + *value = *text; + strcpy (value + 1, entry->pw_name); + rl_filename_completion_desired = 1; + return (value); + } +} + +/* If non-null, this contains the address of a function to call if the + standard meaning for expanding a tilde fails. The function is called + with the text (sans tilde, as in "foo"), and returns a malloc()'ed string + which is the expansion, or a NULL pointer if there is no expansion. */ +Function *rl_tilde_expander = (Function *)NULL; + +/* Expand FILENAME if it begins with a tilde. This always returns + a new string. */ +char * +tilde_expand (filename) + char *filename; +{ + char *dirname = filename ? savestring (filename) : (char *)NULL; + + if (dirname && *dirname == '~') + { + char *temp_name; + if (!dirname[1] || dirname[1] == '/') + { + /* Prepend $HOME to the rest of the string. */ + char *temp_home = (char *)getenv ("HOME"); + + temp_name = (char *)alloca (1 + strlen (&dirname[1]) + + (temp_home? strlen (temp_home) : 0)); + temp_name[0] = '\0'; + if (temp_home) + strcpy (temp_name, temp_home); + strcat (temp_name, &dirname[1]); + free (dirname); + dirname = savestring (temp_name); + } + else + { + struct passwd *getpwnam (), *user_entry; + char *username = (char *)alloca (257); + int i, c; + + for (i = 1; c = dirname[i]; i++) + { + if (c == '/') break; + else username[i - 1] = c; + } + username[i - 1] = '\0'; + + if (!(user_entry = getpwnam (username))) + { + /* If the calling program has a special syntax for + expanding tildes, and we couldn't find a standard + expansion, then let them try. */ + if (rl_tilde_expander) + { + char *expansion; + + expansion = (char *)(*rl_tilde_expander) (username); + + if (expansion) + { + temp_name = (char *)alloca (1 + strlen (expansion) + + strlen (&dirname[i])); + strcpy (temp_name, expansion); + strcat (temp_name, &dirname[i]); + free (expansion); + goto return_name; + } + } + /* + * We shouldn't report errors. + */ + } + else + { + temp_name = (char *)alloca (1 + strlen (user_entry->pw_dir) + + strlen (&dirname[i])); + strcpy (temp_name, user_entry->pw_dir); + strcat (temp_name, &dirname[i]); + return_name: + free (dirname); + dirname = savestring (temp_name); + } + } + } + return (dirname); +} + + +/* **************************************************************** */ +/* */ +/* Undo, and Undoing */ +/* */ +/* **************************************************************** */ + +/* Non-zero tells rl_delete_text and rl_insert_text to not add to + the undo list. */ +int doing_an_undo = 0; + +/* The current undo list for THE_LINE. */ +UNDO_LIST *rl_undo_list = (UNDO_LIST *)NULL; + +/* Remember how to undo something. Concatenate some undos if that + seems right. */ +rl_add_undo (what, start, end, text) + enum undo_code what; + int start, end; + char *text; +{ + UNDO_LIST *temp = (UNDO_LIST *)xmalloc (sizeof (UNDO_LIST)); + temp->what = what; + temp->start = start; + temp->end = end; + temp->text = text; + temp->next = rl_undo_list; + rl_undo_list = temp; +} + +/* Free the existing undo list. */ +free_undo_list () +{ + while (rl_undo_list) { + UNDO_LIST *release = rl_undo_list; + rl_undo_list = rl_undo_list->next; + + if (release->what == UNDO_DELETE) + free (release->text); + + free (release); + } +} + +/* Undo the next thing in the list. Return 0 if there + is nothing to undo, or non-zero if there was. */ +int +rl_do_undo () +{ + UNDO_LIST *release; + int waiting_for_begin = 0; + +undo_thing: + if (!rl_undo_list) + return (0); + + doing_an_undo = 1; + + switch (rl_undo_list->what) { + + /* Undoing deletes means inserting some text. */ + case UNDO_DELETE: + rl_point = rl_undo_list->start; + rl_insert_text (rl_undo_list->text); + free (rl_undo_list->text); + break; + + /* Undoing inserts means deleting some text. */ + case UNDO_INSERT: + rl_delete_text (rl_undo_list->start, rl_undo_list->end); + rl_point = rl_undo_list->start; + break; + + /* Undoing an END means undoing everything 'til we get to + a BEGIN. */ + case UNDO_END: + waiting_for_begin++; + break; + + /* Undoing a BEGIN means that we are done with this group. */ + case UNDO_BEGIN: + if (waiting_for_begin) + waiting_for_begin--; + else + abort (); + break; + } + + doing_an_undo = 0; + + release = rl_undo_list; + rl_undo_list = rl_undo_list->next; + free (release); + + if (waiting_for_begin) + goto undo_thing; + + return (1); +} + +/* Begin a group. Subsequent undos are undone as an atomic operation. */ +rl_begin_undo_group () +{ + rl_add_undo (UNDO_BEGIN, 0, 0, 0); +} + +/* End an undo group started with rl_begin_undo_group (). */ +rl_end_undo_group () +{ + rl_add_undo (UNDO_END, 0, 0, 0); +} + +/* Save an undo entry for the text from START to END. */ +rl_modifying (start, end) + int start, end; +{ + if (start > end) + { + int t = start; + start = end; + end = t; + } + + if (start != end) + { + char *temp = rl_copy (start, end); + rl_begin_undo_group (); + rl_add_undo (UNDO_DELETE, start, end, temp); + rl_add_undo (UNDO_INSERT, start, end, (char *)NULL); + rl_end_undo_group (); + } +} + +/* Revert the current line to its previous state. */ +rl_revert_line () +{ + if (!rl_undo_list) ding (); + else { + while (rl_undo_list) + rl_do_undo (); + } +} + +/* Do some undoing of things that were done. */ +rl_undo_command (count) +{ + if (count < 0) return; /* Nothing to do. */ + + while (count) + { + if (rl_do_undo ()) + { + count--; + } + else + { + ding (); + break; + } + } +} + +/* **************************************************************** */ +/* */ +/* History Utilities */ +/* */ +/* **************************************************************** */ + +/* We already have a history library, and that is what we use to control + the history features of readline. However, this is our local interface + to the history mechanism. */ + +/* While we are editing the history, this is the saved + version of the original line. */ +HIST_ENTRY *saved_line_for_history = (HIST_ENTRY *)NULL; + +/* Set the history pointer back to the last entry in the history. */ +start_using_history () +{ + using_history (); + if (saved_line_for_history) + free_history_entry (saved_line_for_history); + + saved_line_for_history = (HIST_ENTRY *)NULL; +} + +/* Free the contents (and containing structure) of a HIST_ENTRY. */ +free_history_entry (entry) + HIST_ENTRY *entry; +{ + if (!entry) return; + if (entry->line) + free (entry->line); + free (entry); +} + +/* Perhaps put back the current line if it has changed. */ +maybe_replace_line () +{ + HIST_ENTRY *temp = current_history (); + + /* If the current line has changed, save the changes. */ + if (temp && ((UNDO_LIST *)(temp->data) != rl_undo_list)) { + temp = replace_history_entry (where_history (), the_line, rl_undo_list); + free (temp->line); + free (temp); + } +} + +/* Put back the saved_line_for_history if there is one. */ +maybe_unsave_line () +{ + if (saved_line_for_history) { + strcpy (the_line, saved_line_for_history->line); + rl_undo_list = (UNDO_LIST *)saved_line_for_history->data; + free_history_entry (saved_line_for_history); + saved_line_for_history = (HIST_ENTRY *)NULL; + rl_end = rl_point = strlen (the_line); + } else { + ding (); + } +} + +/* Save the current line in saved_line_for_history. */ +maybe_save_line () +{ + if (!saved_line_for_history) { + saved_line_for_history = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY)); + saved_line_for_history->line = savestring (the_line); + saved_line_for_history->data = (char *)rl_undo_list; + } +} + + + +/* **************************************************************** */ +/* */ +/* History Commands */ +/* */ +/* **************************************************************** */ + +/* Meta-< goes to the start of the history. */ +rl_beginning_of_history () +{ + rl_get_previous_history (1 + where_history ()); +} + +/* Meta-> goes to the end of the history. (The current line). */ +rl_end_of_history () +{ + maybe_replace_line (); + using_history (); + maybe_unsave_line (); +} + +/* Move down to the next history line. */ +rl_get_next_history (count) + int count; +{ + HIST_ENTRY *temp = (HIST_ENTRY *)NULL; + + if (count < 0) + { + rl_get_previous_history (-count); + return; + } + + if (!count) + return; + + maybe_replace_line (); + + while (count) + { + temp = next_history (); + if (!temp) + break; + --count; + } + + if (!temp) + maybe_unsave_line (); + else + { + strcpy (the_line, temp->line); + rl_undo_list = (UNDO_LIST *)temp->data; + rl_end = rl_point = strlen (the_line); + } +} + +/* Get the previous item out of our interactive history, making it the current + line. If there is no previous history, just ding. */ +rl_get_previous_history (count) + int count; +{ + HIST_ENTRY *old_temp = (HIST_ENTRY *)NULL; + HIST_ENTRY *temp = (HIST_ENTRY *)NULL; + + if (count < 0) + { + rl_get_next_history (-count); + return; + } + + if (!count) + return; + + /* If we don't have a line saved, then save this one. */ + maybe_save_line (); + + /* If the current line has changed, save the changes. */ + maybe_replace_line (); + + while (count) + { + temp = previous_history (); + if (!temp) + break; + else + old_temp = temp; + --count; + } + + /* If there was a large argument, and we moved back to the start of the + history, that is not an error. So use the last value found. */ + if (!temp && old_temp) + temp = old_temp; + + if (!temp) + ding (); + else + { + strcpy (the_line, temp->line); + rl_undo_list = (UNDO_LIST *)temp->data; + rl_end = rl_point = strlen (the_line); +#ifdef VI_MODE + if (rl_editing_mode == vi_mode) + rl_point = 0; +#endif /* VI_MODE */ + } +} + +/* There is a command in ksh which yanks into this line, the last word + of the previous line. Here it is. We left it on M-. */ +rl_yank_previous_last_arg (ignore) + int ignore; +{ +} + + + +/* **************************************************************** */ +/* */ +/* I-Search and Searching */ +/* */ +/* **************************************************************** */ + +/* Search backwards through the history looking for a string which is typed + interactively. Start with the current line. */ +rl_reverse_search_history (sign, key) + int sign; + int key; +{ + rl_search_history (-sign, key); +} + +/* Search forwards through the history looking for a string which is typed + interactively. Start with the current line. */ +rl_forward_search_history (sign, key) + int sign; + int key; +{ + rl_search_history (sign, key); +} + +/* Display the current state of the search in the echo-area. + SEARCH_STRING contains the string that is being searched for, + DIRECTION is zero for forward, or 1 for reverse, + WHERE is the history list number of the current line. If it is + -1, then this line is the starting one. */ +rl_display_search (search_string, reverse_p, where) + char *search_string; + int reverse_p, where; +{ + char *message = (char *)NULL; + + message = + (char *)alloca (1 + (search_string ? strlen (search_string) : 0) + 30); + + *message = '\0'; + +#ifdef NEVER + if (where != -1) + sprintf (message, "[%d]", where + history_base); +#endif + + strcat (message, "("); + + if (reverse_p) + strcat (message, "reverse-"); + + strcat (message, "i-search)`"); + + if (search_string) + strcat (message, search_string); + + strcat (message, "': "); + rl_message (message, 0, 0); + rl_redisplay (); +} + +/* Search through the history looking for an interactively typed string. + This is analogous to i-search. We start the search in the current line. + DIRECTION is which direction to search; > 0 means forward, < 0 means + backwards. */ +rl_search_history (direction, invoking_key) + int direction; + int invoking_key; +{ + /* The string that the user types in to search for. */ + char *search_string = (char *)alloca (128); + + /* The current length of SEARCH_STRING. */ + int search_string_index; + + /* The list of lines to search through. */ + char **lines; + + /* The length of LINES. */ + int hlen; + + /* Where we get LINES from. */ + HIST_ENTRY **hlist = history_list (); + + int orig_point = rl_point; + int orig_line = where_history (); + int last_found_line = orig_line; + int c, done = 0; + register int i = 0; + + + /* The line currently being searched. */ + char *sline; + + /* Offset in that line. */ + int index; + + /* Non-zero if we are doing a reverse search. */ + int reverse = (direction < 0); + + /* Create an arrary of pointers to the lines that we want to search. */ + + maybe_replace_line (); + if (hlist) + for (i = 0; hlist[i]; i++); + + /* Allocate space for this many lines, +1 for the current input line, + and remember those lines. */ + lines = (char **)alloca ((1 + (hlen = i)) * sizeof (char *)); + for (i = 0; i < hlen; i++) + lines[i] = hlist[i]->line; + + if (saved_line_for_history) + lines[i] = saved_line_for_history->line; + else + { + /* So I have to type it in this way instead. */ + lines[i] = (char *)alloca (1 + strlen (the_line)); + strcpy (lines[i], &the_line[0]); + } + + hlen++; + + /* The line where we start the search. */ + i = orig_line; + + /* Initialize search parameters. */ + *search_string = '\0'; + search_string_index = 0; + + rl_display_search (search_string, reverse, -1); + + sline = the_line; + index = rl_point; + + while (!done) + { + c = rl_read_key (in_stream); + + /* Hack C to Do What I Mean. */ + { + Function *f = (Function *)NULL; + + if (keymap[c].type == ISFUNC) + f = keymap[c].function; + + if (f == rl_reverse_search_history) + c = reverse ? -1 : -2; + else if (f == rl_forward_search_history) + c = !reverse ? -1 : -2; + } + + switch (c) + { + case ESC: + done = 1; + continue; + + /* case invoking_key: */ + case -1: + goto search_again; + + /* switch directions */ + case -2: + direction = -direction; + reverse = (direction < 0); + + goto do_search; + + case CTRL ('G'): + strcpy (the_line, lines[orig_line]); + rl_point = orig_point; + rl_end = strlen (the_line); + rl_clear_message (); + return; + + default: + if (c < 32 || c > 126) + { + rl_execute_next (c); + done = 1; + continue; + } + else + { + search_string[search_string_index++] = c; + search_string[search_string_index] = '\0'; + goto do_search; + + search_again: + + if (!search_string_index) + continue; + else + { + if (reverse) + --index; + else + if (index != strlen (sline)) + ++index; + else + ding (); + } + do_search: + + while (1) + { + if (reverse) + { + while (index >= 0) + if (strncmp + (search_string, + sline + index, + search_string_index) == 0) + goto string_found; + else + index--; + } + else + { + register int limit = + (strlen (sline) - search_string_index) + 1; + + while (index < limit) + { + if (strncmp (search_string, + sline + index, + search_string_index) == 0) + goto string_found; + index++; + } + } + + next_line: + i += direction; + + /* At limit for direction? */ + if ((reverse && i < 0) || + (!reverse && i == hlen)) + goto search_failed; + + sline = lines[i]; + if (reverse) + index = strlen (sline); + else + index = 0; + + /* If the search string is longer than the current + line, no match. */ + if (search_string_index > strlen (sline)) + goto next_line; + + /* Start actually searching. */ + if (reverse) + index -= search_string_index; + } + + search_failed: + /* We cannot find the search string. Ding the bell. */ + ding (); + i = last_found_line; + break; + + string_found: + /* We have found the search string. Just display it. But don't + actually move there in the history list until the user accepts + the location. */ + strcpy (the_line, lines[i]); + rl_point = index; + rl_end = strlen (the_line); + last_found_line = i; + rl_display_search (search_string, reverse, + (i == orig_line) ? -1 : i); + } + } + continue; + } + /* The user has won. They found the string that they wanted. Now all + we have to do is place them there. */ + { + int now = last_found_line; + + /* First put back the original state. */ + strcpy (the_line, lines[orig_line]); + + if (now < orig_line) + rl_get_previous_history (orig_line - now); + else + rl_get_next_history (now - orig_line); + + rl_point = index; + rl_clear_message (); + } +} + +/* Make C be the next command to be executed. */ +rl_execute_next (c) + int c; +{ + rl_pending_input = c; +} + +/* **************************************************************** */ +/* */ +/* Killing Mechanism */ +/* */ +/* **************************************************************** */ + +/* What we assume for a max number of kills. */ +#define DEFAULT_MAX_KILLS 10 + +/* The real variable to look at to find out when to flush kills. */ +int rl_max_kills = DEFAULT_MAX_KILLS; + +/* Where to store killed text. */ +char **rl_kill_ring = (char **)NULL; + +/* Where we are in the kill ring. */ +int rl_kill_index = 0; + +/* How many slots we have in the kill ring. */ +int rl_kill_ring_length = 0; + +/* How to say that you only want to save a certain amount + of kill material. */ +rl_set_retained_kills (num) + int num; +{} + +/* The way to kill something. This appends or prepends to the last + kill, if the last command was a kill command. if FROM is less + than TO, then the text is appended, otherwise prepended. If the + last command was not a kill command, then a new slot is made for + this kill. */ +rl_kill_text (from, to) + int from, to; +{ + int slot; + char *text = rl_copy (from, to); + + /* Is there anything to kill? */ + if (from == to) { + free (text); + last_command_was_kill++; + return; + } + + /* Delete the copied text from the line. */ + rl_delete_text (from, to); + + /* First, find the slot to work with. */ + if (!last_command_was_kill) { + + /* Get a new slot. */ + if (!rl_kill_ring) { + + /* If we don't have any defined, then make one. */ + rl_kill_ring = + (char **)xmalloc (((rl_kill_ring_length = 1) + 1) * sizeof (char *)); + slot = 1; + + } else { + + /* We have to add a new slot on the end, unless we have exceeded + the max limit for remembering kills. */ + slot = rl_kill_ring_length; + if (slot == rl_max_kills) { + register int i; + free (rl_kill_ring[0]); + for (i = 0; i < slot; i++) + rl_kill_ring[i] = rl_kill_ring[i + 1]; + } else { + rl_kill_ring = + (char **)xrealloc (rl_kill_ring, + ((slot = (rl_kill_ring_length += 1)) + 1) + * sizeof (char *)); + } + } + slot--; + } else { + slot = rl_kill_ring_length - 1; + } + + /* If the last command was a kill, prepend or append. */ + if (last_command_was_kill) { + char *old = rl_kill_ring[slot]; + char *new = (char *)xmalloc (1 + strlen (old) + strlen (text)); + + if (from < to) { + strcpy (new, old); + strcat (new, text); + } else { + strcpy (new, text); + strcat (new, old); + } + free (old); + free (text); + rl_kill_ring[slot] = new; + } else { + rl_kill_ring[slot] = text; + } + rl_kill_index = slot; + last_command_was_kill++; +} + +/* Now REMEMBER! In order to do prepending or appending correctly, kill + commands always make rl_point's original position be the FROM argument, + and rl_point's extent be the TO argument. */ + + +/* **************************************************************** */ +/* */ +/* Killing Commands */ +/* */ +/* **************************************************************** */ + +/* Delete the word at point, saving the text in the kill ring. */ +rl_kill_word (count) + int count; +{ + int orig_point = rl_point; + + if (count < 0) + rl_backward_kill_word (-count); + else + { + rl_forward_word (count); + + if (rl_point != orig_point) + rl_kill_text (orig_point, rl_point); + + rl_point = orig_point; + } +} + +/* Rubout the word before point, placing it on the kill ring. */ +rl_backward_kill_word (count) + int count; +{ + int orig_point = rl_point; + + if (count < 0) + rl_kill_word (-count); + else + { + rl_backward_word (count); + + if (rl_point != orig_point) + rl_kill_text (orig_point, rl_point); + } +} + +/* Kill from here to the end of the line. If DIRECTION is negative, kill + back to the line start instead. */ +rl_kill_line (direction) + int direction; +{ + int orig_point = rl_point; + + if (direction < 0) + rl_backward_kill_line (1); + else + { + rl_end_of_line (); + if (orig_point != rl_point) + rl_kill_text (orig_point, rl_point); + rl_point = orig_point; + } +} + +/* Kill backwards to the start of the line. If DIRECTION is negative, kill + forwards to the line end instead. */ +rl_backward_kill_line (direction) + int direction; +{ + int orig_point = rl_point; + + if (direction < 0) + rl_kill_line (1); + else + { + if (!rl_point) + ding (); + else + { + rl_beg_of_line (); + rl_kill_text (orig_point, rl_point); + } + } +} + +/* Yank back the last killed text. This ignores arguments. */ +rl_yank () +{ + if (!rl_kill_ring) rl_abort (); + rl_insert_text (rl_kill_ring[rl_kill_index]); +} + +/* If the last command was yank, or yank_pop, and the text just + before point is identical to the current kill item, then + delete that text from the line, rotate the index down, and + yank back some other text. */ +rl_yank_pop () +{ + int l; + + if (((rl_last_func != rl_yank_pop) && (rl_last_func != rl_yank)) || + !rl_kill_ring) + { + rl_abort (); + } + + l = strlen (rl_kill_ring[rl_kill_index]); + if (((rl_point - l) >= 0) && + (strncmp (the_line + (rl_point - l), + rl_kill_ring[rl_kill_index], l) == 0)) + { + rl_delete_text ((rl_point - l), rl_point); + rl_point -= l; + rl_kill_index--; + if (rl_kill_index < 0) + rl_kill_index = rl_kill_ring_length - 1; + rl_yank (); + } + else + rl_abort (); + +} + +/* Yank the COUNTth argument from the previous history line. */ +rl_yank_nth_arg (count, ignore) + int count; +{ + register HIST_ENTRY *entry = previous_history (); + char *arg; + + if (entry) + next_history (); + else + { + ding (); + return; + } + + arg = history_arg_extract (count, count, entry->line); + if (!arg || !*arg) + { + ding (); + return; + } + + rl_begin_undo_group (); + if (rl_point && the_line[rl_point - 1] != ' ') + rl_insert_text (" "); + rl_insert_text (arg); + free (arg); + rl_end_undo_group (); +} + +/* Vi Mode. */ +#ifdef VI_MODE +#include "vi_mode.c" +#endif /* VI_MODE */ + +/* How to toggle back and forth between editing modes. */ +rl_vi_editing_mode () +{ +#ifdef VI_MODE + rl_editing_mode = vi_mode; + rl_vi_insertion_mode (); +#endif /* VI_MODE */ +} + +rl_emacs_editing_mode () +{ + rl_editing_mode = emacs_mode; + keymap = emacs_standard_keymap; +} + + +/* **************************************************************** */ +/* */ +/* Completion */ +/* */ +/* **************************************************************** */ + +/* Non-zero means that case is not significant in completion. */ +int completion_case_fold = 0; + +/* Return an array of (char *) which is a list of completions for TEXT. + If there are no completions, return a NULL pointer. + The first entry in the returned array is the substitution for TEXT. + The remaining entries are the possible completions. + The array is terminated with a NULL pointer. + + ENTRY_FUNCTION is a function of two args, and returns a (char *). + The first argument is TEXT. + The second is a state argument; it should be zero on the first call, and + non-zero on subsequent calls. It returns a NULL pointer to the caller + when there are no more matches. + */ +char ** +completion_matches (text, entry_function) + char *text; + char *(*entry_function) (); +{ + /* Number of slots in match_list. */ + int match_list_size; + + /* The list of matches. */ + char **match_list = + (char **)xmalloc (((match_list_size = 10) + 1) * sizeof (char *)); + + /* Number of matches actually found. */ + int matches = 0; + + /* Temporary string binder. */ + char *string; + + match_list[1] = (char *)NULL; + + while (string = (*entry_function) (text, matches)) + { + if (matches + 1 == match_list_size) + match_list = + (char **)xrealloc (match_list, + ((match_list_size += 10) + 1) * sizeof (char *)); + + match_list[++matches] = string; + match_list[matches + 1] = (char *)NULL; + } + + /* If there were any matches, then look through them finding out the + lowest common denominator. That then becomes match_list[0]. */ + if (matches) + { + register int i = 1; + int low = 100000; /* Count of max-matched characters. */ + + /* If only one match, just use that. */ + if (matches == 1) + { + match_list[0] = match_list[1]; + match_list[1] = (char *)NULL; + } + else + { + /* Otherwise, compare each member of the list with + the next, finding out where they stop matching. */ + + while (i < matches) + { + register int c1, c2, si; + + if (completion_case_fold) + { + for (si = 0; + (c1 = to_lower(match_list[i][si])) && + (c2 = to_lower(match_list[i + 1][si])); + si++) + if (c1 != c2) break; + } + else + { + for (si = 0; + (c1 = match_list[i][si]) && + (c2 = match_list[i + 1][si]); + si++) + if (c1 != c2) break; + } + + if (low > si) low = si; + i++; + } + match_list[0] = (char *)xmalloc (low + 1); + strncpy (match_list[0], match_list[1], low); + match_list[0][low] = '\0'; + } + } + else /* There were no matches. */ + { + free (match_list); + match_list = (char **)NULL; + } + return (match_list); +} + +/* Okay, now we write the entry_function for filename completion. In the + general case. Note that completion in the shell is a little different + because of all the pathnames that must be followed when looking up the + completion for a command. */ +char * +filename_completion_function (text, state) + int state; + char *text; +{ + static DIR *directory; + static char *filename = (char *)NULL; + static char *dirname = (char *)NULL; + static char *users_dirname = (char *)NULL; + static int filename_len; + + struct direct *entry = (struct direct *)NULL; + + /* If we don't have any state, then do some initialization. */ + if (!state) + { + char *rindex (), *temp; + + if (dirname) free (dirname); + if (filename) free (filename); + if (users_dirname) free (users_dirname); + + filename = savestring (text); + if (!*text) text = "."; + dirname = savestring (text); + + temp = rindex (dirname, '/'); + + if (temp) + { + strcpy (filename, ++temp); + *temp = '\0'; + } + else + strcpy (dirname, "."); + + /* We aren't done yet. We also support the "~user" syntax. */ + + /* Save the version of the directory that the user typed. */ + users_dirname = savestring (dirname); + { + char *tilde_expand (), *temp_dirname = tilde_expand (dirname); + free (dirname); + dirname = temp_dirname; +#ifdef SHELL + { + extern int follow_symbolic_links; + char *make_absolute (); + + if (follow_symbolic_links && (strcmp (dirname, ".") != 0)) + { + temp_dirname = make_absolute (dirname, get_working_directory ("")); + + if (temp_dirname) + { + free (dirname); + dirname = temp_dirname; + } + } + } +#endif /* SHELL */ + } + directory = opendir (dirname); + filename_len = strlen (filename); + + rl_filename_completion_desired = 1; + } + + /* At this point we should entertain the possibility of hacking wildcarded + filenames, like /usr/man*\/te. If the directory name contains + globbing characters, then build an array of directories to glob on, and + glob on the first one. */ + + /* Now that we have some state, we can read the directory. */ + + while (directory && (entry = readdir (directory))) + { + /* Special case for no filename. + All entries except "." and ".." match. */ + if (!filename_len) + { + if ((strcmp (entry->d_name, ".") != 0) && + (strcmp (entry->d_name, "..") != 0)) + break; + } + else + { + /* Otherwise, if these match upto the length of filename, then + it is a match. */ +#ifdef TMB_SYSV + if ((strlen (entry->d_name) >= filename_len) && + (strncmp (filename, entry->d_name, filename_len) == 0)) +#else + if ((entry->d_namlen >= filename_len) && + (strncmp (filename, entry->d_name, filename_len) == 0)) +#endif /* TMB_SYSV */ + { + break; + } + } + } + + if (!entry) + { + if (directory) + { + closedir (directory); + directory = (DIR *)NULL; + } + return (char *)NULL; + } + else + { + char *temp; + + if (dirname && (strcmp (dirname, ".") != 0)) + { +#ifdef TMB_SYSV + temp = (char *)xmalloc (1 + strlen (users_dirname) + + strlen (entry->d_name)); +#else + temp = (char *)xmalloc (1 + strlen (users_dirname) + + entry->d_namlen); +#endif /* TMB_SYSV */ + strcpy (temp, users_dirname); + strcat (temp, entry->d_name); + } + else + { + temp = (savestring (entry->d_name)); + } + return (temp); + } +} + + +/* **************************************************************** */ +/* */ +/* Binding keys */ +/* */ +/* **************************************************************** */ + +/* rl_add_defun (char *name, Function *function, int key) + Add NAME to the list of named functions. Make FUNCTION + be the function that gets called. + If KEY is not -1, then bind it. */ +rl_add_defun (name, function, key) + char *name; + Function *function; + int key; +{ + if (key != -1) + rl_bind_key (key, function); + rl_add_funmap_entry (name, function); +} + +/* Bind KEY to FUNCTION. Returns non-zero if KEY is out of range. */ +int +rl_bind_key (key, function) + int key; + Function *function; +{ + if (key < 0) + return (key); + + if (key > 127 && key < 256) + { + if (keymap[ESC].type == ISKMAP) + { + Keymap escmap = (Keymap)keymap[ESC].function; + + key -= 128; + escmap[key].type = ISFUNC; + escmap[key].function = function; + return (0); + } + return (key); + } + + keymap[key].type = ISFUNC; + keymap[key].function = function; + return (0); +} + +/* Bind KEY to FUNCTION in MAP. Returns non-zero in case of invalid + KEY. */ +int +rl_bind_key_in_map (key, function, map) + int key; + Function *function; + Keymap map; +{ + int result; + Keymap oldmap = keymap; + + keymap = map; + result = rl_bind_key (key, function); + keymap = oldmap; + return (result); +} + +/* Make KEY do nothing in the currently selected keymap. + Returns non-zero in case of error. */ +int +rl_unbind_key (key) + int key; +{ + return (rl_bind_key (key, (Function *)NULL)); +} + +/* Make KEY do nothing in MAP. + Returns non-zero in case of error. */ +int +rl_unbind_key_in_map (key, map) + int key; + Keymap map; +{ + return (rl_bind_key_in_map (key, (Function *)NULL, map)); +} + +/* Bind the key sequence represented by the string KEYSEQ to + FUNCTION. This makes new keymaps as necessary. The initial + place to do bindings is in MAP. */ +rl_set_key (keyseq, function, map) + char *keyseq; + Function *function; + Keymap map; +{ + rl_generic_bind (ISFUNC, keyseq, function, map); +} + +/* Bind the key sequence represented by the string KEYSEQ to + the string of characters MACRO. This makes new keymaps as + necessary. The initial place to do bindings is in MAP. */ +rl_macro_bind (keyseq, macro, map) + char *keyseq, *macro; + Keymap map; +{ + char *macro_keys = (char *)xmalloc (2 * (strlen (macro))); + int macro_keys_len; + + if (rl_translate_keyseq (macro, macro_keys, ¯o_keys_len)) + { + free (macro_keys); + return; + } + rl_generic_bind (ISMACR, keyseq, macro_keys, map); +} + +/* Bind the key sequence represented by the string KEYSEQ to + the arbitrary pointer DATA. TYPE says what kind of data is + pointed to by DATA, right now this can be a function (ISFUNC), + a macro (ISMACR), or a keymap (ISKMAP). This makes new keymaps + as necessary. The initial place to do bindings is in MAP. */ +rl_generic_bind (type, keyseq, data, map) + int type; + char *keyseq, *data; + Keymap map; +{ + char *keys; + int keys_len; + register int i; + int start; + + /* If no keys to bind to, exit right away. */ + if (!keyseq || !*keyseq) + { + if (type == ISMACR) + free (data); + return; + } + + keys = (char *)alloca (1 + (2 * strlen (keyseq))); + + /* Translate the ASCII representation of KEYSEQ into an array + of characters. Stuff the characters into ARRAY, and the + length of ARRAY into LENGTH. */ + if (rl_translate_keyseq (keyseq, keys, &keys_len)) + return; + + /* Handle mapping of the ESC Key in vi mode */ + start = 0; +#ifdef VI_MODE + if ((rl_editing_mode == vi_mode) && (keys[0] == ESC)) + { + start++; + map = vi_movement_keymap; + if(keys[1] == ESC) + { + extern KEYMAP_ENTRY_ARRAY vi_escape_keymap; + + start++; + map = vi_escape_keymap; + } + } +#endif + + /* Bind keys, making new keymaps as necessary. */ + for (i = start; i < keys_len; i++) + { + if (i + 1 < keys_len) + { + if (map[keys[i]].type != ISKMAP) + { + if (map[i].type == ISMACR) + free ((char *)map[i].function); + + map[keys[i]].type = ISKMAP; + map[keys[i]].function = (Function *)rl_make_bare_keymap (); + } + map = (Keymap)map[keys[i]].function; + } + else + { + if (map[keys[i]].type == ISMACR) + free ((char *)map[keys[i]].function); + + map[keys[i]].function = (Function *)data; + map[keys[i]].type = type; + } + } +} + +/* Translate the ASCII representation of SEQ, stuffing the + values into ARRAY, an array of characters. LEN gets the + final length of ARRAY. Return non-zero if there was an + error parsing SEQ. */ +rl_translate_keyseq (seq, array, len) + char *seq, *array; + int *len; +{ + register int i, c, l = 0; + + for (i = 0; c = seq[i]; i++) + { + if (c == '\\') + { + c = seq[++i]; + + if (!c) + break; + + if (((c == 'C' || c == 'M') && seq[i + 1] == '-') || + (c == 'e')) + { + /* Handle special case of backwards define. */ + if (strncmp (&seq[i], "C-\\M-", 5) == 0) + { + array[l++] = ESC; + i += 5; + array[l++] = CTRL (to_upper (seq[i])); + if (!seq[i]) + i--; + continue; + } + + switch (c) + { + case 'M': + i++; + array[l++] = ESC; + break; + + case 'C': + i += 2; + array[l++] = CTRL (to_upper (seq[i])); + break; + + case 'e': + array[l++] = ESC; + } + + continue; + } + } + array[l++] = c; + } + + array[l] = '\0'; + *len = l; + return (0); +} + +/* Return a pointer to the function that STRING represents. + If STRING doesn't have a matching function, then a NULL pointer + is returned. */ +Function * +rl_named_function (string) + char *string; +{ + register int i; + static int stricmp (); + + for (i = 0; funmap[i]; i++) + if (stricmp (funmap[i]->name, string) == 0) + return (funmap[i]->function); + return ((Function *)NULL); +} + +/* The last key bindings file read. */ +static char *last_readline_init_file = "~/.inputrc"; + +/* Re-read the current keybindings file. */ +rl_re_read_init_file (count, ignore) + int count, ignore; +{ + rl_read_init_file (last_readline_init_file); +} + +/* Do key bindings from a file. If FILENAME is NULL it defaults + to `~/.inputrc'. If the file existed and could be opened and + read, 0 is returned, otherwise errno is returned. */ +int +rl_read_init_file (filename) + char *filename; +{ + int line_size, line_index; + char *line = (char *)xmalloc (line_size = 100); + char *openname; + FILE *file; + + int c; + + /* Default the filename. */ + if (!filename) + filename = "~/.inputrc"; + + openname = tilde_expand (filename); + + /* Open the file. */ + file = fopen (openname, "r"); + free (openname); + + if (!file) + return (errno); + + last_readline_init_file = filename; + + /* Loop reading lines from the file. Lines that start with `#' are + comments, all other lines are commands for readline initialization. */ + while ((c = getc(file)) != EOF) + { + /* If comment, flush to EOL. */ + if (c == '#') + { + while ((c = getc(file)) != EOF && c != '\n'); + if (c == EOF) + goto function_exit; + continue; + } + + /* Otherwise, this is the start of a line. Read the + line from the file. */ + line_index = 0; + while (c != EOF && c != '\n') + { + line[line_index++] = c; + if (line_index == line_size) + line = (char *)xrealloc (line, line_size += 100); + c = getc (file); + } + line[line_index] = '\0'; + + /* Parse the line. */ + rl_parse_and_bind (line); + } + +function_exit: + + free (line); + /* Close up the file and exit. */ + fclose (file); + return (0); +} + + +/* **************************************************************** */ +/* */ +/* Parser Directives */ +/* */ +/* **************************************************************** */ + +/* Conditionals. */ + +/* Calling programs set this to have their argv[0]. */ +char *rl_readline_name = "other"; + +/* Stack of previous values of parsing_conditionalized_out. */ +static unsigned char *if_stack = (unsigned char *)NULL; +static int if_stack_depth = 0; +static int if_stack_size = 0; + +/* Push parsing_conditionalized_out, and set parser state based on ARGS. */ +parser_if (args) + char *args; +{ + register int i; + static int stricmp (); + + /* Push parser state. */ + if (if_stack_depth + 1 >= if_stack_size) + { + if (!if_stack) + if_stack = (unsigned char *)xmalloc (if_stack_size = 20); + else + if_stack = (unsigned char *)xrealloc (if_stack, if_stack_size += 20); + } + if_stack[if_stack_depth++] = parsing_conditionalized_out; + + /* We only check to see if the first word in ARGS is the same as the + value stored in rl_readline_name. */ + + /* Isolate first argument. */ + for (i = 0; args[i] && !whitespace (args[i]); i++); + + if (args[i]) + args[i++] = '\0'; + + if (stricmp (args, rl_readline_name) == 0) + parsing_conditionalized_out = 0; + else + parsing_conditionalized_out = 1; +} + +/* Invert the current parser state if there is anything on the stack. */ +parser_else (args) + char *args; +{ + if (if_stack_depth) + parsing_conditionalized_out = !parsing_conditionalized_out; + else + { + /* *** What, no error message? *** */ + } +} + +/* Terminate a conditional, popping the value of + parsing_conditionalized_out from the stack. */ +parser_endif (args) + char *args; +{ + if (if_stack_depth) + parsing_conditionalized_out = if_stack[--if_stack_depth]; + else + { + /* *** What, no error message? *** */ + } +} + +/* Associate textual names with actual functions. */ +static struct { + char *name; + Function *function; +} parser_directives [] = { + { "if", parser_if }, + { "endif", parser_endif }, + { "else", parser_else }, + { (char *)0x0, (Function *)0x0 } +}; + +/* Handle a parser directive. STATEMENT is the line of the directive + without any leading `$'. */ +static int +handle_parser_directive (statement) + char *statement; +{ + register int i; + char *directive, *args; + static int stricmp (); + + /* Isolate the actual directive. */ + + /* Skip whitespace. */ + for (i = 0; whitespace (statement[i]); i++); + + directive = &statement[i]; + + for (; statement[i] && !whitespace (statement[i]); i++); + + if (statement[i]) + statement[i++] = '\0'; + + for (; statement[i] && whitespace (statement[i]); i++); + + args = &statement[i]; + + /* Lookup the command, and act on it. */ + for (i = 0; parser_directives[i].name; i++) + if (stricmp (directive, parser_directives[i].name) == 0) + { + (*parser_directives[i].function) (args); + return (0); + } + + /* *** Should an error message be output? */ + return (1); +} + +/* Read the binding command from STRING and perform it. + A key binding command looks like: Keyname: function-name\0, + a variable binding command looks like: set variable value. + A new-style keybinding looks like "\C-x\C-x": exchange-point-and-mark. */ +rl_parse_and_bind (string) + char *string; +{ + extern char *possible_control_prefixes[], *possible_meta_prefixes[]; + char *rindex (), *funname, *kname; + static int substring_member_of_array (), stricmp (); + register int c; + int key, i; + + if (!string || !*string || *string == '#') + return; + + /* If this is a parser directive, act on it. */ + if (*string == '$') + { + handle_parser_directive (&string[1]); + return; + } + + /* If we are supposed to be skipping parsing right now, then do it. */ + if (parsing_conditionalized_out) + return; + + i = 0; + /* If this keyname is a complex key expression surrounded by quotes, + advance to after the matching close quote. */ + if (*string == '"') + { + for (i = 1; c = string[i]; i++) + { + if (c == '"' && string[i - 1] != '\\') + break; + } + } + + /* Advance to the colon (:) or whitespace which separates the two objects. */ + for (; (c = string[i]) && c != ':' && c != ' ' && c != '\t'; i++ ); + + /* Mark the end of the command (or keyname). */ + if (string[i]) + string[i++] = '\0'; + + /* If this is a command to set a variable, then do that. */ + if (stricmp (string, "set") == 0) + { + char *var = string + i; + char *value; + + /* Make VAR point to start of variable name. */ + while (*var && whitespace (*var)) var++; + + /* Make value point to start of value string. */ + value = var; + while (*value && !whitespace (*value)) value++; + if (*value) + *value++ = '\0'; + while (*value && whitespace (*value)) value++; + + rl_variable_bind (var, value); + return; + } + + /* Skip any whitespace between keyname and funname. */ + for (; string[i] && whitespace (string[i]); i++); + funname = &string[i]; + + /* Now isolate funname. + For straight function names just look for whitespace, since + that will signify the end of the string. But this could be a + macro definition. In that case, the string is quoted, so skip + to the matching delimiter. */ + if (*funname == '\'' || *funname == '"') + { + int delimiter = string[i++]; + + for (; c = string[i]; i++) + { + if (c == delimiter && string[i - 1] != '\\') + break; + } + if (c) + i++; + } + + /* Advance to the end of the string. */ + for (; string[i] && !whitespace (string[i]); i++); + + /* No extra whitespace at the end of the string. */ + string[i] = '\0'; + + /* If this is a new-style key-binding, then do the binding with + rl_set_key (). Otherwise, let the older code deal with it. */ + if (*string == '"') + { + char *seq = (char *)alloca (1 + strlen (string)); + register int j, k = 0; + + for (j = 1; string[j]; j++) + { + if (string[j] == '"' && string[j - 1] != '\\') + break; + + seq[k++] = string[j]; + } + seq[k] = '\0'; + + /* Binding macro? */ + if (*funname == '\'' || *funname == '"') + { + j = strlen (funname); + + if (j && funname[j - 1] == *funname) + funname[j - 1] = '\0'; + + rl_macro_bind (seq, &funname[1], keymap); + } + else + rl_set_key (seq, rl_named_function (funname), keymap); + + return; + } + + /* Get the actual character we want to deal with. */ + kname = rindex (string, '-'); + if (!kname) + kname = string; + else + kname++; + + key = glean_key_from_name (kname); + + /* Add in control and meta bits. */ + if (substring_member_of_array (string, possible_control_prefixes)) + key = CTRL (to_upper (key)); + + if (substring_member_of_array (string, possible_meta_prefixes)) + key = META (key); + + /* Temporary. Handle old-style keyname with macro-binding. */ + if (*funname == '\'' || *funname == '"') + { + char seq[2]; + int fl = strlen (funname); + + seq[0] = key; seq[1] = '\0'; + if (fl && funname[fl - 1] == *funname) + funname[fl - 1] = '\0'; + + rl_macro_bind (seq, &funname[1], keymap); + } + else + rl_bind_key (key, rl_named_function (funname)); +} + +rl_variable_bind (name, value) + char *name, *value; +{ + static int strnicmp (), stricmp (); + + if (stricmp (name, "editing-mode") == 0) + { + if (strnicmp (value, "vi", 2) == 0) + { +#ifdef VI_MODE + keymap = vi_insertion_keymap; + rl_editing_mode = vi_mode; +#endif /* VI_MODE */ + } + else if (strnicmp (value, "emacs", 5) == 0) + { + keymap = emacs_standard_keymap; + rl_editing_mode = emacs_mode; + } + } + else if (stricmp (name, "horizontal-scroll-mode") == 0) + { + if (!*value || stricmp (value, "On") == 0) + horizontal_scroll_mode = 1; + else + horizontal_scroll_mode = 0; + } +} + +/* Return the character which matches NAME. + For example, `Space' returns ' '. */ + +typedef struct { + char *name; + int value; +} assoc_list; + +assoc_list name_key_alist[] = { + { "Space", ' ' }, + { "SPC", ' ' }, + { "Rubout", 0x7f }, + { "DEL", 0x7f }, + { "Tab", 0x09 }, + { "Newline", '\n' }, + { "Return", '\r' }, + { "RET", '\r' }, + { "LFD", '\n' }, + { "Escape", '\033' }, + { "ESC", '\033' }, + + { (char *)0x0, 0 } +}; + +int +glean_key_from_name (name) + char *name; +{ + register int i; + static int stricmp (); + + for (i = 0; name_key_alist[i].name; i++) + if (stricmp (name, name_key_alist[i].name) == 0) + return (name_key_alist[i].value); + + return (*name); +} + + +/* **************************************************************** */ +/* */ +/* String Utility Functions */ +/* */ +/* **************************************************************** */ + +/* Return non-zero if any members of ARRAY are a substring in STRING. */ +static int +substring_member_of_array (string, array) + char *string, **array; +{ + static char *strindex (); + + while (*array) + { + if (strindex (string, *array)) + return (1); + array++; + } + return (0); +} + +/* Whoops, Unix doesn't have strnicmp. */ + +/* Compare at most COUNT characters from string1 to string2. Case + doesn't matter. */ +static int +strnicmp (string1, string2, count) + char *string1, *string2; +{ + register char ch1, ch2; + + while (count) { + ch1 = *string1++; + ch2 = *string2++; + if (to_upper(ch1) == to_upper(ch2)) + count--; + else break; + } + return (count); +} + +/* strcmp (), but caseless. */ +static int +stricmp (string1, string2) + char *string1, *string2; +{ + register char ch1, ch2; + + while (*string1 && *string2) { + ch1 = *string1++; + ch2 = *string2++; + if (to_upper(ch1) != to_upper(ch2)) + return (1); + } + return (*string1 | *string2); +} + +/* Determine if s2 occurs in s1. If so, return a pointer to the + match in s1. The compare is case insensitive. */ +static char * +strindex (s1, s2) + register char *s1, *s2; +{ + register int i, l = strlen (s2); + register int len = strlen (s1); + + for (i = 0; (len - i) >= l; i++) + if (strnicmp (&s1[i], s2, l) == 0) + return (s1 + i); + return ((char *)NULL); +} + + +#ifdef STATIC_MALLOC + +/* **************************************************************** */ +/* */ +/* xmalloc and xrealloc () */ +/* */ +/* **************************************************************** */ + +static char * +xmalloc (bytes) + int bytes; +{ + static memory_error_and_abort (); + char *temp = (char *)malloc (bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static char * +xrealloc (pointer, bytes) + char *pointer; + int bytes; +{ + static memory_error_and_abort (); + char *temp = (char *)realloc (pointer, bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static +memory_error_and_abort () +{ + fprintf (stderr, "readline: Out of virtual memory!\n"); + abort (); +} +#endif /* STATIC_MALLOC */ + + +/* **************************************************************** */ +/* */ +/* Testing Readline */ +/* */ +/* **************************************************************** */ + +#ifdef TEST + +main () +{ + HIST_ENTRY **history_list (); + char *temp = (char *)NULL; + char *prompt = "readline% "; + int done = 0; + + while (!done) + { + temp = readline (prompt); + + /* Test for EOF. */ + if (!temp) + exit (1); + + /* If there is anything on the line, print it and remember it. */ + if (*temp) + { + fprintf (stderr, "%s\r\n", temp); + add_history (temp); + } + + /* Check for `command' that we handle. */ + if (strcmp (temp, "quit") == 0) + done = 1; + + if (strcmp (temp, "list") == 0) { + HIST_ENTRY **list = history_list (); + register int i; + if (list) { + for (i = 0; list[i]; i++) { + fprintf (stderr, "%d: %s\r\n", i, list[i]->line); + free (list[i]->line); + } + free (list); + } + } + free (temp); + } +} + +#endif /* TEST */ + + +/* + * Local variables: + * compile-command: "gcc -g -traditional -I. -I.. -DTEST -o readline readline.c keymaps.o funmap.o history.o -ltermcap" + * end: + */ diff --git a/gnu/usr.bin/gdb/readline/readline.h b/gnu/usr.bin/gdb/readline/readline.h new file mode 100644 index 0000000000..7d7fbe7c3c --- /dev/null +++ b/gnu/usr.bin/gdb/readline/readline.h @@ -0,0 +1,161 @@ +/* Readline.h -- the names of functions callable from within readline. */ + +#ifndef _READLINE_H_ +#define _READLINE_H_ + +#include + +#ifndef __FUNCTION_DEF +typedef int Function (); +#define __FUNCTION_DEF +#endif + +/* The functions for manipulating the text of the line within readline. +Most of these functions are bound to keys by default. */ +extern int +rl_beg_of_line (), rl_backward (), rl_delete (), rl_end_of_line (), +rl_forward (), ding (), rl_backward (), rl_newline (), rl_kill_line (), +rl_clear_screen (), rl_get_next_history (), rl_get_previous_history (), +rl_quoted_insert (), rl_reverse_search_history (), rl_transpose_chars +(), rl_unix_line_discard (), rl_quoted_insert (), rl_unix_word_rubout +(), rl_yank (), rl_rubout (), rl_backward_word (), rl_kill_word (), +rl_forward_word (), rl_tab_insert (), rl_yank_pop (), rl_yank_nth_arg (), +rl_backward_kill_word (), rl_backward_kill_line (), rl_transpose_words +(), rl_complete (), rl_possible_completions (), rl_do_lowercase_version +(), rl_digit_argument (), rl_universal_argument (), rl_abort (), +rl_undo_command (), rl_revert_line (), rl_beginning_of_history (), +rl_end_of_history (), rl_forward_search_history (), rl_insert (), +rl_upcase_word (), rl_downcase_word (), rl_capitalize_word (), +rl_restart_output (), rl_re_read_init_file (); + +/* These are *both* defined even when VI_MODE is not. */ +extern int rl_vi_editing_mode (), rl_emacs_editing_mode (); + +#ifdef VI_MODE +/* Things for vi mode. */ +extern int rl_vi_movement_mode (), rl_vi_insertion_mode (), rl_vi_arg_digit (), +rl_vi_prev_word (), rl_vi_next_word (), rl_vi_char_search (), +rl_vi_eof_maybe (), rl_vi_append_mode (), rl_vi_put (), +rl_vi_append_eol (), rl_vi_insert_beg (), rl_vi_delete (), rl_vi_comment (), +rl_vi_first_print (), rl_vi_fword (), rl_vi_fWord (), rl_vi_bword (), +rl_vi_bWord (), rl_vi_eword (), rl_vi_eWord (), rl_vi_end_word (), +rl_vi_change_case (), rl_vi_match (), rl_vi_bracktype (), rl_vi_change_char (), +rl_vi_yank_arg (), rl_vi_search (), rl_vi_search_again (), +rl_vi_dosearch (), rl_vi_subst (), rl_vi_overstrike (), +rl_vi_overstrike_delete (), rl_vi_replace(), rl_vi_column (), +rl_vi_delete_to (), rl_vi_change_to (), rl_vi_yank_to (), rl_vi_complete (); +#endif /* VI_MODE */ + +/* Keyboard macro commands. */ +extern int +rl_start_kbd_macro (), rl_end_kbd_macro (), rl_call_last_kbd_macro (); + +/* Maintaining the state of undo. We remember individual deletes and inserts + on a chain of things to do. */ + +/* The actions that undo knows how to undo. Notice that UNDO_DELETE means + to insert some text, and UNDO_INSERT means to delete some text. I.e., + the code tells undo what to undo, not how to undo it. */ +enum undo_code { UNDO_DELETE, UNDO_INSERT, UNDO_BEGIN, UNDO_END }; + +/* What an element of THE_UNDO_LIST looks like. */ +typedef struct undo_list { + struct undo_list *next; + int start, end; /* Where the change took place. */ + char *text; /* The text to insert, if undoing a delete. */ + enum undo_code what; /* Delete, Insert, Begin, End. */ +} UNDO_LIST; + +/* The current undo list for RL_LINE_BUFFER. */ +extern UNDO_LIST *rl_undo_list; + +/* The data structure for mapping textual names to code addresses. */ +typedef struct { + char *name; + Function *function; +} FUNMAP; + +extern FUNMAP **funmap; + +/* **************************************************************** */ +/* */ +/* Well Published Variables */ +/* */ +/* **************************************************************** */ + +/* The name of the calling program. You should initialize this to + whatever was in argv[0]. It is used when parsing conditionals. */ +extern char *rl_readline_name; + +/* The line buffer that is in use. */ +extern char *rl_line_buffer; + +/* The location of point, and end. */ +extern int rl_point, rl_end; + +/* The name of the terminal to use. */ +extern char *rl_terminal_name; + +/* The input and output streams. */ +extern FILE *rl_instream, *rl_outstream; + +/* The basic list of characters that signal a break between words for the + completer routine. The contents of this variable is what breaks words + in the shell, i.e. "n\"\\'`@$>". */ +extern char *rl_basic_word_break_characters; + +/* The list of characters that signal a break between words for + rl_complete_internal. The default list is the contents of + rl_basic_word_break_characters. */ +extern char *rl_completer_word_break_characters; + +/* List of characters that are word break characters, but should be left + in TEXT when it is passed to the completion function. The shell uses + this to help determine what kind of completing to do. */ +extern char *rl_special_prefixes; + +/* Pointer to the generator function for completion_matches (). + NULL means to use filename_entry_function (), the default filename + completer. */ +extern Function *rl_completion_entry_function; + +/* Pointer to alternative function to create matches. + Function is called with TEXT, START, and END. + START and END are indices in RL_LINE_BUFFER saying what the boundaries + of TEXT are. + If this function exists and returns NULL then call the value of + rl_completion_entry_function to try to match, otherwise use the + array of strings returned. */ +extern Function *rl_attempted_completion_function; + +/* If non-null, this contains the address of a function to call if the + standard meaning for expanding a tilde fails. The function is called + with the text (sans tilde, as in "foo"), and returns a malloc()'ed string + which is the expansion, or a NULL pointer if there is no expansion. */ +extern Function *rl_tilde_expander; + +/* If non-zero, then this is the address of a function to call just + before readline_internal () prints the first prompt. */ +extern Function *rl_startup_hook; + +/* **************************************************************** */ +/* */ +/* Well Published Functions */ +/* */ +/* **************************************************************** */ + +/* Read a line of input. Prompt with PROMPT. A NULL PROMPT means none. */ +extern char *readline (); + +/* Return an array of strings which are the result of repeatadly calling + FUNC with TEXT. */ +extern char **completion_matches (); + +/* rl_add_defun (char *name, Function *function, int key) + Add NAME to the list of named functions. Make FUNCTION + be the function that gets called. + If KEY is not -1, then bind it. */ +extern int rl_add_defun (); + +#endif /* _READLINE_H_ */ + diff --git a/gnu/usr.bin/gdb/readline/vi_keymap.c b/gnu/usr.bin/gdb/readline/vi_keymap.c new file mode 100644 index 0000000000..71c7ec8506 --- /dev/null +++ b/gnu/usr.bin/gdb/readline/vi_keymap.c @@ -0,0 +1,484 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + * + * @(#)vi_keymap.c 6.4 (Berkeley) 5/8/91 + */ + +/* vi_keymap.c -- the keymap for vi_mode in readline (). */ + +/* Copyright (C) 1988,1989 Free Software Foundation, Inc. + + This file is part of GNU Readline, a library for reading lines + of text with interactive input and history editing. + + Readline is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 1, or (at your option) any + later version. + + Readline is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Readline; see the file COPYING. If not, write to the Free + Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef FILE +#include +#endif /* FILE */ + +#include "readline.h" + +extern KEYMAP_ENTRY_ARRAY vi_escape_keymap; + +/* The keymap arrays for handling vi mode. */ +KEYMAP_ENTRY_ARRAY vi_movement_keymap = { + + /* The regular control keys come first. */ + { ISFUNC, (Function *)0x0 }, /* Control-@ */ + { ISFUNC, (Function *)0x0 }, /* Control-a */ + { ISFUNC, (Function *)0x0 }, /* Control-b */ + { ISFUNC, (Function *)0x0 }, /* Control-c */ + { ISFUNC, rl_vi_eof_maybe }, /* Control-d */ + { ISFUNC, rl_emacs_editing_mode }, /* Control-e */ + { ISFUNC, (Function *)0x0 }, /* Control-f */ + { ISFUNC, rl_abort }, /* Control-g */ + { ISFUNC, rl_backward }, /* Control-h */ + { ISFUNC, (Function *)0x0 }, /* Control-i */ + { ISFUNC, rl_newline }, /* Control-j */ + { ISFUNC, rl_kill_line }, /* Control-k */ + { ISFUNC, rl_clear_screen }, /* Control-l */ + { ISFUNC, rl_newline }, /* Control-m */ + { ISFUNC, rl_get_next_history }, /* Control-n */ + { ISFUNC, (Function *)0x0 }, /* Control-o */ + { ISFUNC, rl_get_previous_history }, /* Control-p */ + { ISFUNC, rl_quoted_insert }, /* Control-q */ + { ISFUNC, rl_reverse_search_history }, /* Control-r */ + { ISFUNC, rl_forward_search_history }, /* Control-s */ + { ISFUNC, rl_transpose_chars }, /* Control-t */ + { ISFUNC, rl_unix_line_discard }, /* Control-u */ + { ISFUNC, rl_quoted_insert }, /* Control-v */ + { ISFUNC, rl_unix_word_rubout }, /* Control-w */ + { ISFUNC, (Function *)0x0 }, /* Control-x */ + { ISFUNC, rl_yank }, /* Control-y */ + { ISFUNC, (Function *)0x0 }, /* Control-z */ + + { ISKMAP, (Function *)vi_escape_keymap }, /* Control-[ */ + { ISFUNC, (Function *)0x0 }, /* Control-\ */ + { ISFUNC, (Function *)0x0 }, /* Control-] */ + { ISFUNC, (Function *)0x0 }, /* Control-^ */ + { ISFUNC, rl_undo_command }, /* Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, rl_forward }, /* SPACE */ + { ISFUNC, (Function *)0x0 }, /* ! */ + { ISFUNC, (Function *)0x0 }, /* " */ + { ISFUNC, rl_vi_comment }, /* # */ + { ISFUNC, rl_end_of_line }, /* $ */ + { ISFUNC, rl_vi_match }, /* % */ + { ISFUNC, (Function *)0x0 }, /* & */ + { ISFUNC, (Function *)0x0 }, /* ' */ + { ISFUNC, (Function *)0x0 }, /* ( */ + { ISFUNC, (Function *)0x0 }, /* ) */ + { ISFUNC, rl_vi_complete }, /* * */ + { ISFUNC, rl_get_previous_history}, /* + */ + { ISFUNC, rl_vi_char_search }, /* , */ + { ISFUNC, rl_get_next_history }, /* - */ + { ISFUNC, (Function *)0x0 }, /* . */ + { ISFUNC, rl_vi_search }, /* / */ + + /* Regular digits. */ + { ISFUNC, rl_vi_arg_digit }, /* 0 */ + { ISFUNC, rl_vi_arg_digit }, /* 1 */ + { ISFUNC, rl_vi_arg_digit }, /* 2 */ + { ISFUNC, rl_vi_arg_digit }, /* 3 */ + { ISFUNC, rl_vi_arg_digit }, /* 4 */ + { ISFUNC, rl_vi_arg_digit }, /* 5 */ + { ISFUNC, rl_vi_arg_digit }, /* 6 */ + { ISFUNC, rl_vi_arg_digit }, /* 7 */ + { ISFUNC, rl_vi_arg_digit }, /* 8 */ + { ISFUNC, rl_vi_arg_digit }, /* 9 */ + + /* A little more punctuation. */ + { ISFUNC, (Function *)0x0 }, /* : */ + { ISFUNC, rl_vi_char_search }, /* ; */ + { ISFUNC, (Function *)0x0 }, /* < */ + { ISFUNC, (Function *)0x0 }, /* = */ + { ISFUNC, (Function *)0x0 }, /* > */ + { ISFUNC, rl_vi_search }, /* ? */ + { ISFUNC, (Function *)0x0 }, /* @ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_vi_append_eol }, /* A */ + { ISFUNC, rl_vi_prev_word}, /* B */ + { ISFUNC, rl_vi_change_to }, /* C */ + { ISFUNC, rl_vi_delete_to }, /* D */ + { ISFUNC, rl_vi_end_word }, /* E */ + { ISFUNC, rl_vi_char_search }, /* F */ + { ISFUNC, (Function *)0x0 }, /* G */ + { ISFUNC, (Function *)0x0 }, /* H */ + { ISFUNC, rl_vi_insert_beg }, /* I */ + { ISFUNC, (Function *)0x0 }, /* J */ + { ISFUNC, (Function *)0x0 }, /* K */ + { ISFUNC, (Function *)0x0 }, /* L */ + { ISFUNC, (Function *)0x0 }, /* M */ + { ISFUNC, rl_vi_search_again }, /* N */ + { ISFUNC, (Function *)0x0 }, /* O */ + { ISFUNC, rl_vi_put }, /* P */ + { ISFUNC, (Function *)0x0 }, /* Q */ + { ISFUNC, rl_vi_replace }, /* R */ + { ISFUNC, rl_vi_subst }, /* S */ + { ISFUNC, rl_vi_char_search }, /* T */ + { ISFUNC, rl_revert_line }, /* U */ + { ISFUNC, (Function *)0x0 }, /* V */ + { ISFUNC, rl_vi_next_word }, /* W */ + { ISFUNC, rl_rubout }, /* X */ + { ISFUNC, rl_vi_yank_to }, /* Y */ + { ISFUNC, (Function *)0x0 }, /* Z */ + + /* Some more punctuation. */ + { ISFUNC, (Function *)0x0 }, /* [ */ + { ISFUNC, (Function *)0x0 }, /* \ */ + { ISFUNC, (Function *)0x0 }, /* ] */ + { ISFUNC, rl_vi_first_print }, /* ^ */ + { ISFUNC, rl_vi_yank_arg }, /* _ */ + { ISFUNC, (Function *)0x0 }, /* ` */ + + /* Lowercase alphabet. */ + { ISFUNC, rl_vi_append_mode }, /* a */ + { ISFUNC, rl_vi_prev_word }, /* b */ + { ISFUNC, rl_vi_change_to }, /* c */ + { ISFUNC, rl_vi_delete_to }, /* d */ + { ISFUNC, rl_vi_end_word }, /* e */ + { ISFUNC, rl_vi_char_search }, /* f */ + { ISFUNC, (Function *)0x0 }, /* g */ + { ISFUNC, rl_backward }, /* h */ + { ISFUNC, rl_vi_insertion_mode }, /* i */ + { ISFUNC, rl_get_next_history }, /* j */ + { ISFUNC, rl_get_previous_history }, /* k */ + { ISFUNC, rl_forward }, /* l */ + { ISFUNC, (Function *)0x0 }, /* m */ + { ISFUNC, rl_vi_search_again }, /* n */ + { ISFUNC, (Function *)0x0 }, /* o */ + { ISFUNC, rl_vi_put }, /* p */ + { ISFUNC, (Function *)0x0 }, /* q */ + { ISFUNC, rl_vi_change_char }, /* r */ + { ISFUNC, rl_vi_subst }, /* s */ + { ISFUNC, rl_vi_char_search }, /* t */ + { ISFUNC, rl_undo_command }, /* u */ + { ISFUNC, (Function *)0x0 }, /* v */ + { ISFUNC, rl_vi_next_word }, /* w */ + { ISFUNC, rl_vi_delete }, /* x */ + { ISFUNC, rl_vi_yank_to }, /* y */ + { ISFUNC, (Function *)0x0 }, /* z */ + + /* Final punctuation. */ + { ISFUNC, (Function *)0x0 }, /* { */ + { ISFUNC, rl_vi_column }, /* | */ + { ISFUNC, (Function *)0x0 }, /* } */ + { ISFUNC, rl_vi_change_case }, /* ~ */ + { ISFUNC, rl_backward } /* RUBOUT */ +}; + + +KEYMAP_ENTRY_ARRAY vi_insertion_keymap = { + + /* The regular control keys come first. */ + { ISFUNC, (Function *)0x0 }, /* Control-@ */ + { ISFUNC, rl_insert }, /* Control-a */ + { ISFUNC, rl_insert }, /* Control-b */ + { ISFUNC, rl_insert }, /* Control-c */ + { ISFUNC, rl_vi_eof_maybe }, /* Control-d */ + { ISFUNC, rl_insert }, /* Control-e */ + { ISFUNC, rl_insert }, /* Control-f */ + { ISFUNC, rl_insert }, /* Control-g */ + { ISFUNC, rl_rubout }, /* Control-h */ + { ISFUNC, rl_complete }, /* Control-i */ + { ISFUNC, rl_newline }, /* Control-j */ + { ISFUNC, rl_insert }, /* Control-k */ + { ISFUNC, rl_insert }, /* Control-l */ + { ISFUNC, rl_newline }, /* Control-m */ + { ISFUNC, rl_insert }, /* Control-n */ + { ISFUNC, rl_insert }, /* Control-o */ + { ISFUNC, rl_insert }, /* Control-p */ + { ISFUNC, rl_insert }, /* Control-q */ + { ISFUNC, rl_reverse_search_history }, /* Control-r */ + { ISFUNC, rl_forward_search_history }, /* Control-s */ + { ISFUNC, rl_transpose_chars }, /* Control-t */ + { ISFUNC, rl_unix_line_discard }, /* Control-u */ + { ISFUNC, rl_quoted_insert }, /* Control-v */ + { ISFUNC, rl_unix_word_rubout }, /* Control-w */ + { ISFUNC, rl_insert }, /* Control-x */ + { ISFUNC, rl_yank }, /* Control-y */ + { ISFUNC, rl_insert }, /* Control-z */ + + { ISFUNC, rl_vi_movement_mode }, /* Control-[ */ + { ISFUNC, rl_insert }, /* Control-\ */ + { ISFUNC, rl_insert }, /* Control-] */ + { ISFUNC, rl_insert }, /* Control-^ */ + { ISFUNC, rl_undo_command }, /* Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, rl_insert }, /* SPACE */ + { ISFUNC, rl_insert }, /* ! */ + { ISFUNC, rl_insert }, /* " */ + { ISFUNC, rl_insert }, /* # */ + { ISFUNC, rl_insert }, /* $ */ + { ISFUNC, rl_insert }, /* % */ + { ISFUNC, rl_insert }, /* & */ + { ISFUNC, rl_insert }, /* ' */ + { ISFUNC, rl_insert }, /* ( */ + { ISFUNC, rl_insert }, /* ) */ + { ISFUNC, rl_insert }, /* * */ + { ISFUNC, rl_insert }, /* + */ + { ISFUNC, rl_insert }, /* , */ + { ISFUNC, rl_insert }, /* - */ + { ISFUNC, rl_insert }, /* . */ + { ISFUNC, rl_insert }, /* / */ + + /* Regular digits. */ + { ISFUNC, rl_insert }, /* 0 */ + { ISFUNC, rl_insert }, /* 1 */ + { ISFUNC, rl_insert }, /* 2 */ + { ISFUNC, rl_insert }, /* 3 */ + { ISFUNC, rl_insert }, /* 4 */ + { ISFUNC, rl_insert }, /* 5 */ + { ISFUNC, rl_insert }, /* 6 */ + { ISFUNC, rl_insert }, /* 7 */ + { ISFUNC, rl_insert }, /* 8 */ + { ISFUNC, rl_insert }, /* 9 */ + + /* A little more punctuation. */ + { ISFUNC, rl_insert }, /* : */ + { ISFUNC, rl_insert }, /* ; */ + { ISFUNC, rl_insert }, /* < */ + { ISFUNC, rl_insert }, /* = */ + { ISFUNC, rl_insert }, /* > */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* @ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_insert }, /* A */ + { ISFUNC, rl_insert }, /* B */ + { ISFUNC, rl_insert }, /* C */ + { ISFUNC, rl_insert }, /* D */ + { ISFUNC, rl_insert }, /* E */ + { ISFUNC, rl_insert }, /* F */ + { ISFUNC, rl_insert }, /* G */ + { ISFUNC, rl_insert }, /* H */ + { ISFUNC, rl_insert }, /* I */ + { ISFUNC, rl_insert }, /* J */ + { ISFUNC, rl_insert }, /* K */ + { ISFUNC, rl_insert }, /* L */ + { ISFUNC, rl_insert }, /* M */ + { ISFUNC, rl_insert }, /* N */ + { ISFUNC, rl_insert }, /* O */ + { ISFUNC, rl_insert }, /* P */ + { ISFUNC, rl_insert }, /* Q */ + { ISFUNC, rl_insert }, /* R */ + { ISFUNC, rl_insert }, /* S */ + { ISFUNC, rl_insert }, /* T */ + { ISFUNC, rl_insert }, /* U */ + { ISFUNC, rl_insert }, /* V */ + { ISFUNC, rl_insert }, /* W */ + { ISFUNC, rl_insert }, /* X */ + { ISFUNC, rl_insert }, /* Y */ + { ISFUNC, rl_insert }, /* Z */ + + /* Some more punctuation. */ + { ISFUNC, rl_insert }, /* [ */ + { ISFUNC, rl_insert }, /* \ */ + { ISFUNC, rl_insert }, /* ] */ + { ISFUNC, rl_insert }, /* ^ */ + { ISFUNC, rl_insert }, /* _ */ + { ISFUNC, rl_insert }, /* ` */ + + /* Lowercase alphabet. */ + { ISFUNC, rl_insert }, /* a */ + { ISFUNC, rl_insert }, /* b */ + { ISFUNC, rl_insert }, /* c */ + { ISFUNC, rl_insert }, /* d */ + { ISFUNC, rl_insert }, /* e */ + { ISFUNC, rl_insert }, /* f */ + { ISFUNC, rl_insert }, /* g */ + { ISFUNC, rl_insert }, /* h */ + { ISFUNC, rl_insert }, /* i */ + { ISFUNC, rl_insert }, /* j */ + { ISFUNC, rl_insert }, /* k */ + { ISFUNC, rl_insert }, /* l */ + { ISFUNC, rl_insert }, /* m */ + { ISFUNC, rl_insert }, /* n */ + { ISFUNC, rl_insert }, /* o */ + { ISFUNC, rl_insert }, /* p */ + { ISFUNC, rl_insert }, /* q */ + { ISFUNC, rl_insert }, /* r */ + { ISFUNC, rl_insert }, /* s */ + { ISFUNC, rl_insert }, /* t */ + { ISFUNC, rl_insert }, /* u */ + { ISFUNC, rl_insert }, /* v */ + { ISFUNC, rl_insert }, /* w */ + { ISFUNC, rl_insert }, /* x */ + { ISFUNC, rl_insert }, /* y */ + { ISFUNC, rl_insert }, /* z */ + + /* Final punctuation. */ + { ISFUNC, rl_insert }, /* { */ + { ISFUNC, rl_insert }, /* | */ + { ISFUNC, rl_insert }, /* } */ + { ISFUNC, rl_insert }, /* ~ */ + { ISFUNC, rl_rubout } /* RUBOUT */ +}; + +KEYMAP_ENTRY_ARRAY vi_escape_keymap = { + + /* The regular control keys come first. */ + { ISFUNC, (Function *)0x0 }, /* Control-@ */ + { ISFUNC, (Function *)0x0 }, /* Control-a */ + { ISFUNC, (Function *)0x0 }, /* Control-b */ + { ISFUNC, (Function *)0x0 }, /* Control-c */ + { ISFUNC, (Function *)0x0 }, /* Control-d */ + { ISFUNC, (Function *)0x0 }, /* Control-e */ + { ISFUNC, (Function *)0x0 }, /* Control-f */ + { ISFUNC, (Function *)0x0 }, /* Control-g */ + { ISFUNC, (Function *)0x0 }, /* Control-h */ + { ISFUNC, rl_tab_insert}, /* Control-i */ + { ISFUNC, rl_emacs_editing_mode}, /* Control-j */ + { ISFUNC, rl_kill_line }, /* Control-k */ + { ISFUNC, (Function *)0x0 }, /* Control-l */ + { ISFUNC, rl_emacs_editing_mode}, /* Control-m */ + { ISFUNC, (Function *)0x0 }, /* Control-n */ + { ISFUNC, (Function *)0x0 }, /* Control-o */ + { ISFUNC, (Function *)0x0 }, /* Control-p */ + { ISFUNC, (Function *)0x0 }, /* Control-q */ + { ISFUNC, (Function *)0x0 }, /* Control-r */ + { ISFUNC, (Function *)0x0 }, /* Control-s */ + { ISFUNC, (Function *)0x0 }, /* Control-t */ + { ISFUNC, (Function *)0x0 }, /* Control-u */ + { ISFUNC, (Function *)0x0 }, /* Control-v */ + { ISFUNC, (Function *)0x0 }, /* Control-w */ + { ISFUNC, (Function *)0x0 }, /* Control-x */ + { ISFUNC, (Function *)0x0 }, /* Control-y */ + { ISFUNC, (Function *)0x0 }, /* Control-z */ + + { ISFUNC, rl_vi_movement_mode }, /* Control-[ */ + { ISFUNC, (Function *)0x0 }, /* Control-\ */ + { ISFUNC, (Function *)0x0 }, /* Control-] */ + { ISFUNC, (Function *)0x0 }, /* Control-^ */ + { ISFUNC, rl_undo_command }, /* Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, (Function *)0x0 }, /* SPACE */ + { ISFUNC, (Function *)0x0 }, /* ! */ + { ISFUNC, (Function *)0x0 }, /* " */ + { ISFUNC, (Function *)0x0 }, /* # */ + { ISFUNC, (Function *)0x0 }, /* $ */ + { ISFUNC, (Function *)0x0 }, /* % */ + { ISFUNC, (Function *)0x0 }, /* & */ + { ISFUNC, (Function *)0x0 }, /* ' */ + { ISFUNC, (Function *)0x0 }, /* ( */ + { ISFUNC, (Function *)0x0 }, /* ) */ + { ISFUNC, (Function *)0x0 }, /* * */ + { ISFUNC, (Function *)0x0 }, /* + */ + { ISFUNC, (Function *)0x0 }, /* , */ + { ISFUNC, (Function *)0x0 }, /* - */ + { ISFUNC, (Function *)0x0 }, /* . */ + { ISFUNC, (Function *)0x0 }, /* / */ + + /* Regular digits. */ + { ISFUNC, rl_vi_arg_digit }, /* 0 */ + { ISFUNC, rl_vi_arg_digit }, /* 1 */ + { ISFUNC, rl_vi_arg_digit }, /* 2 */ + { ISFUNC, rl_vi_arg_digit }, /* 3 */ + { ISFUNC, rl_vi_arg_digit }, /* 4 */ + { ISFUNC, rl_vi_arg_digit }, /* 5 */ + { ISFUNC, rl_vi_arg_digit }, /* 6 */ + { ISFUNC, rl_vi_arg_digit }, /* 7 */ + { ISFUNC, rl_vi_arg_digit }, /* 8 */ + { ISFUNC, rl_vi_arg_digit }, /* 9 */ + + /* A little more punctuation. */ + { ISFUNC, (Function *)0x0 }, /* : */ + { ISFUNC, (Function *)0x0 }, /* ; */ + { ISFUNC, (Function *)0x0 }, /* < */ + { ISFUNC, (Function *)0x0 }, /* = */ + { ISFUNC, (Function *)0x0 }, /* > */ + { ISFUNC, (Function *)0x0 }, /* ? */ + { ISFUNC, (Function *)0x0 }, /* @ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_do_lowercase_version }, /* A */ + { ISFUNC, rl_do_lowercase_version }, /* B */ + { ISFUNC, rl_do_lowercase_version }, /* C */ + { ISFUNC, rl_do_lowercase_version }, /* D */ + { ISFUNC, rl_do_lowercase_version }, /* E */ + { ISFUNC, rl_do_lowercase_version }, /* F */ + { ISFUNC, rl_do_lowercase_version }, /* G */ + { ISFUNC, rl_do_lowercase_version }, /* H */ + { ISFUNC, rl_do_lowercase_version }, /* I */ + { ISFUNC, rl_do_lowercase_version }, /* J */ + { ISFUNC, rl_do_lowercase_version }, /* K */ + { ISFUNC, rl_do_lowercase_version }, /* L */ + { ISFUNC, rl_do_lowercase_version }, /* M */ + { ISFUNC, rl_do_lowercase_version }, /* N */ + { ISFUNC, rl_do_lowercase_version }, /* O */ + { ISFUNC, rl_do_lowercase_version }, /* P */ + { ISFUNC, rl_do_lowercase_version }, /* Q */ + { ISFUNC, rl_do_lowercase_version }, /* R */ + { ISFUNC, rl_do_lowercase_version }, /* S */ + { ISFUNC, rl_do_lowercase_version }, /* T */ + { ISFUNC, rl_do_lowercase_version }, /* U */ + { ISFUNC, rl_do_lowercase_version }, /* V */ + { ISFUNC, rl_do_lowercase_version }, /* W */ + { ISFUNC, rl_do_lowercase_version }, /* X */ + { ISFUNC, rl_do_lowercase_version }, /* Y */ + { ISFUNC, rl_do_lowercase_version }, /* Z */ + + /* Some more punctuation. */ + { ISFUNC, (Function *)0x0 }, /* [ */ + { ISFUNC, (Function *)0x0 }, /* \ */ + { ISFUNC, (Function *)0x0 }, /* ] */ + { ISFUNC, (Function *)0x0 }, /* ^ */ + { ISFUNC, (Function *)0x0 }, /* _ */ + { ISFUNC, (Function *)0x0 }, /* ` */ + + /* Lowercase alphabet. */ + { ISFUNC, (Function *)0x0 }, /* a */ + { ISFUNC, (Function *)0x0 }, /* b */ + { ISFUNC, (Function *)0x0 }, /* c */ + { ISFUNC, (Function *)0x0 }, /* d */ + { ISFUNC, (Function *)0x0 }, /* e */ + { ISFUNC, (Function *)0x0 }, /* f */ + { ISFUNC, (Function *)0x0 }, /* g */ + { ISFUNC, (Function *)0x0 }, /* h */ + { ISFUNC, (Function *)0x0 }, /* i */ + { ISFUNC, (Function *)0x0 }, /* j */ + { ISFUNC, (Function *)0x0 }, /* k */ + { ISFUNC, (Function *)0x0 }, /* l */ + { ISFUNC, (Function *)0x0 }, /* m */ + { ISFUNC, (Function *)0x0 }, /* n */ + { ISFUNC, (Function *)0x0 }, /* o */ + { ISFUNC, (Function *)0x0 }, /* p */ + { ISFUNC, (Function *)0x0 }, /* q */ + { ISFUNC, (Function *)0x0 }, /* r */ + { ISFUNC, (Function *)0x0 }, /* s */ + { ISFUNC, (Function *)0x0 }, /* t */ + { ISFUNC, (Function *)0x0 }, /* u */ + { ISFUNC, (Function *)0x0 }, /* v */ + { ISFUNC, (Function *)0x0 }, /* w */ + { ISFUNC, (Function *)0x0 }, /* x */ + { ISFUNC, (Function *)0x0 }, /* y */ + { ISFUNC, (Function *)0x0 }, /* z */ + + /* Final punctuation. */ + { ISFUNC, (Function *)0x0 }, /* { */ + { ISFUNC, (Function *)0x0 }, /* | */ + { ISFUNC, (Function *)0x0 }, /* } */ + { ISFUNC, (Function *)0x0 }, /* ~ */ + { ISFUNC, rl_backward_kill_word } /* RUBOUT */ +}; diff --git a/gnu/usr.bin/gdb/readline/vi_mode.c b/gnu/usr.bin/gdb/readline/vi_mode.c new file mode 100644 index 0000000000..3a13cc6c86 --- /dev/null +++ b/gnu/usr.bin/gdb/readline/vi_mode.c @@ -0,0 +1,875 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + * + * @(#)vi_mode.c 6.4 (Berkeley) 5/8/91 + */ + +/* vi_mode.c -- A vi emulation mode for Bash. + Mostly written by Jeff Sparkes (jeff1@????). + */ + + +/* **************************************************************** */ +/* */ +/* VI Emulation Mode */ +/* */ +/* **************************************************************** */ + +/* Last string searched for from `/' or `?'. */ +static char *vi_last_search = (char *)NULL; +static int vi_histpos; + +/* *** UNCLEAN *** */ +/* Command keys which do movement for xxx_to commands. */ +static char *vi_motion = " hl^$0ftFt;,%wbeWBE|"; + +/* Keymap used for vi replace characters. Created dynamically since + rarely used. */ +static Keymap vi_replace_map = (Keymap)NULL; + +/* The number of characters inserted in the last replace operation. */ +static vi_replace_count = 0; + +/* Yank the nth arg from the previous line into this line at point. */ +rl_vi_yank_arg (count) + int count; +{ + rl_yank_nth_arg (count); +} + +/* Search again for the last thing searched for. */ +rl_vi_search_again (ignore, key) + int ignore, key; +{ + switch (key) + { + case 'n': + rl_vi_dosearch (vi_last_search, -1); + break; + + case 'N': + rl_vi_dosearch (vi_last_search, 1); + break; + } +} + +/* Do a vi style search. */ +rl_vi_search (count, key) + int count, key; +{ + int dir, c; + char *p; + + switch (key) + { + case '?': + dir = 1; + break; + + case '/': + dir = -1; + break; + + default: + ding (); + return; + } + + vi_histpos = where_history (); + maybe_save_line (); + + /* Reuse the line input buffer to read the search string. */ + the_line[0] = 0; + rl_end = rl_point = 0; + p = (char *)alloca (2 + (rl_prompt ? strlen (rl_prompt) : 0)); + + sprintf (p, "%s%c", rl_prompt ? rl_prompt : "", key); + + rl_message (p); + + while (c = rl_read_key (in_stream)) + { + switch (c) + { + case CTRL('W'): + case CTRL('U'): + case CTRL('H'): + case RUBOUT: + rl_dispatch (c, keymap); + break; + + case ESC: + case RETURN: + case NEWLINE: + goto dosearch; + break; + + case CTRL('C'): + maybe_unsave_line (); + rl_clear_message (); + rl_point = 0; + ding (); + return; + + default: + rl_insert (1, c); + break; + } + rl_redisplay (); + } + dosearch: + if (vi_last_search) + free (vi_last_search); + + vi_last_search = savestring (the_line); + rl_vi_dosearch (the_line, dir); +} + +rl_vi_dosearch (string, dir) + char *string; + int dir; +{ + int old, save = vi_histpos; + HIST_ENTRY *h; + + if (string == 0 || *string == 0 || vi_histpos < 0) + { + ding (); + return; + } + + if ((save = history_search_pos (string, dir, vi_histpos + dir)) == -1) + { + maybe_unsave_line (); + rl_clear_message (); + rl_point = 0; + ding (); + return; + } + + vi_histpos = save; + + old = where_history (); + history_set_pos (vi_histpos); + h = current_history (); + history_set_pos (old); + + strcpy (the_line, h->line); + rl_undo_list = (UNDO_LIST *)h->data; + rl_end = strlen (the_line); + rl_point = 0; + rl_clear_message (); +} + +/* Completion, from vi's point of view. */ +rl_vi_complete (ignore, key) + int ignore, key; +{ + if (!whitespace (the_line[rl_point])) + { + rl_vi_end_word (1, 'E'); + rl_point++; + } + rl_complete_internal ('*'); + rl_vi_insertion_mode (); +} + +/* Previous word in vi mode. */ +rl_vi_prev_word (count, key) + int count, key; +{ + if (count < 0) + { + rl_vi_next_word (-count, key); + return; + } + + if (uppercase_p (key)) + rl_vi_bWord (count); + else + rl_vi_bword (count); +} + +/* Next word in vi mode. */ +rl_vi_next_word (count, key) + int count; +{ + if (count < 0) + { + rl_vi_prev_word (-count, key); + return; + } + + if (uppercase_p (key)) + rl_vi_fWord (count); + else + rl_vi_fword (count); +} + +/* Move to the end of the ?next? word. */ +rl_vi_end_word (count, key) + int count, key; +{ + if (count < 0) + { + ding (); + return; + } + + if (uppercase_p (key)) + rl_vi_eWord (count); + else + rl_vi_eword (count); +} + +/* Move forward a word the way that 'W' does. */ +rl_vi_fWord (count) + int count; +{ + while (count-- && rl_point < (rl_end - 1)) + { + /* Skip until whitespace. */ + while (!whitespace (the_line[rl_point]) && rl_point < rl_end) + rl_point++; + + /* Now skip whitespace. */ + while (whitespace (the_line[rl_point]) && rl_point < rl_end) + rl_point++; + } +} + +rl_vi_bWord (count) + int count; +{ + while (count-- && rl_point > 0) + { + while (rl_point-- >= 0 && whitespace (the_line[rl_point])); + while (rl_point >= 0 && !whitespace (the_line[rl_point])) + rl_point--; + rl_point++; + } +} + +rl_vi_eWord (count) + int count; +{ + while (count -- && rl_point < (rl_end - 1)) + { + while (rl_point++ < rl_end && whitespace (the_line[rl_point])); + while (rl_point++ < rl_end && !whitespace (the_line[rl_point])); + rl_point--; + } +} + +rl_vi_fword (count) + int count; +{ + while (count -- && rl_point < (rl_end - 1)) + { + if (isident (the_line[rl_point])) + { + while (isident (the_line[rl_point]) && rl_point < rl_end) + rl_point += 1; + } + else if (!whitespace (the_line[rl_point])) + { + while (!isident (the_line[rl_point]) && + !whitespace (the_line[rl_point]) && rl_point < rl_end) + rl_point += 1; + } + + while (whitespace (the_line[rl_point]) && rl_point < rl_end) + rl_point++; + } +} + +rl_vi_bword (count) + int count; +{ + while (count -- && rl_point > 0) + { + while (--rl_point > 0 && whitespace (the_line[rl_point])); + if (rl_point > 0) + { + if (isident (the_line[rl_point])) + while (--rl_point >= 0 && isident (the_line[rl_point])); + else + while (--rl_point >= 0 && !isident (the_line[rl_point]) && + !whitespace (the_line[rl_point])); + rl_point++; + } + } +} + +rl_vi_eword (count) + int count; +{ + while (count -- && rl_point < rl_end - 1) + { + while (++rl_point < rl_end && whitespace (the_line[rl_point])); + + if (rl_point < rl_end) + { + if (isident (the_line[rl_point])) + while (++rl_point < rl_end && isident (the_line[rl_point])); + else + while (++rl_point < rl_end && !isident (the_line[rl_point]) + && !whitespace (the_line[rl_point])); + rl_point--; + } + } +} + +rl_vi_insert_beg () +{ + rl_beg_of_line (); + rl_vi_insertion_mode (); + return 0; +} + +rl_vi_append_mode () +{ + if (rl_point < rl_end) + rl_point += 1; + rl_vi_insertion_mode (); + return 0; +} + +rl_vi_append_eol () +{ + rl_end_of_line (); + rl_vi_append_mode (); + return 0; +} + +/* What to do in the case of C-d. */ +rl_vi_eof_maybe (count, c) + int count, c; +{ + rl_newline (1, '\n'); +} + +/* Insertion mode stuff. */ + +/* Switching from one mode to the other really just involves + switching keymaps. */ +rl_vi_insertion_mode () +{ + keymap = vi_insertion_keymap; +} + +rl_vi_movement_mode () +{ + if (rl_point > 0) + rl_backward (1); + + keymap = vi_movement_keymap; + if (vi_doing_insert) + { + rl_end_undo_group (); + vi_doing_insert = 0; + } +} + +rl_vi_arg_digit (count, c) + int count, c; +{ + if (c == '0' && rl_numeric_arg == 1 && !rl_explicit_arg) + rl_beg_of_line (); + else + rl_digit_argument (count, c); +} + +/* Doesn't take an arg count in vi */ +rl_vi_change_case (ignore1, ignore2) + int ignore1, ignore2; +{ + char c = 0; + + if (uppercase_p (the_line[rl_point])) + c = to_lower (the_line[rl_point]); + else if (lowercase_p (the_line[rl_point])) + c = to_upper (the_line[rl_point]); + + /* Vi is kind of strange here. */ + if (c) + { + rl_begin_undo_group (); + rl_delete (1); + rl_insert (1, c); + rl_end_undo_group (); + rl_vi_check (); + } + else + rl_forward (1); +} + +rl_vi_put (count, key) + int count, key; +{ + if (!uppercase_p (key)) + { + if(rl_point != rl_end) + rl_point++; + } + + rl_yank (); + rl_backward (1); +} + +rl_vi_check () +{ + if (rl_point && rl_point == rl_end) + rl_point--; +} + +rl_vi_column (count) +{ + if (count > rl_end) + rl_end_of_line (); + else + rl_point = count - 1; +} + +int +rl_vi_domove () +{ + int c, save; + + rl_mark = rl_point; + c = rl_read_key (in_stream); + + if (!member (c, vi_motion)) + { + if (digit (c)) + { + save = rl_numeric_arg; + rl_digit_loop1 (); + rl_numeric_arg *= save; + } + else + return (-1); + } + + rl_dispatch (c, keymap); + + /* No change in position means the command failed. */ + if (rl_mark == rl_point) + return (-1); + + if ((c == 'w' || c == 'W') && rl_point < rl_end) + { + rl_point--; + while((rl_point > 0) && whitespace (the_line[rl_point])) + rl_point--; + rl_point++; + } + + if (rl_mark < rl_point) + exchange (rl_point, rl_mark); + + return (0); +} + +/* A simplified loop for vi. Don't dispatch key at end. + Don't recognize minus sign? */ +rl_digit_loop1 () +{ + int key, c; + + while (1) + { + rl_message ("(arg: %d) ", arg_sign * rl_numeric_arg); + key = c = rl_read_key (); + + if (keymap[c].type == ISFUNC && + keymap[c].function == rl_universal_argument) + { + rl_numeric_arg *= 4; + continue; + } + c = UNMETA (c); + if (numeric (c)) + { + if (rl_explicit_arg) + rl_numeric_arg = (rl_numeric_arg * 10) + (c - '0'); + else + rl_numeric_arg = (c - '0'); + rl_explicit_arg = 1; + } + else + { + rl_clear_message (); + rl_stuff_char (key); + } + } +} + +rl_vi_delete_to (count, key) + int count, key; +{ + if (uppercase_p (key)) + rl_stuff_char ('$'); + + if (rl_vi_domove ()) + { + ding (); + return; + } + + rl_kill_text (rl_point, rl_mark); +} + +rl_vi_change_to (count, key) + int count, key; +{ + if (uppercase_p (key)) + rl_stuff_char ('$'); + + if (rl_vi_domove ()) + { + ding (); + return; + } + + rl_begin_undo_group (); + vi_doing_insert = 1; + rl_kill_text (rl_point, rl_mark); + rl_vi_insertion_mode (); +} + +rl_vi_yank_to (count, key) + int count, key; +{ + int save = rl_point; + + if (uppercase_p (key)) + rl_stuff_char ('$'); + + if (rl_vi_domove ()) + { + ding (); + return; + } + + rl_begin_undo_group (); + rl_kill_text (rl_point, rl_mark); + rl_end_undo_group (); + rl_do_undo (); + rl_point = save; +} + +rl_vi_delete (count) +{ + if (rl_point >= rl_end - 1) + { + rl_delete (count); + if (rl_point > 0) + rl_backward (1); + } + else + rl_delete (count); +} + +/* Turn the current line into a comment in shell history. A ksh function */ +rl_vi_comment () +{ + rl_beg_of_line (); + rl_insert_text (": "); /* # doesn't work in interactive mode */ + rl_redisplay (); + rl_newline (1, '\010'); +} + +rl_vi_first_print () +{ + rl_back_to_indent (); +} + +rl_back_to_indent (ignore1, ignore2) + int ignore1, ignore2; +{ + rl_beg_of_line (); + while (rl_point < rl_end && whitespace (the_line[rl_point])) + rl_point++; +} + +/* NOTE: it is necessary that opposite directions are inverses */ +#define FTO 1 /* forward to */ +#define BTO -1 /* backward to */ +#define FFIND 2 /* forward find */ +#define BFIND -2 /* backward find */ + +rl_vi_char_search (count, key) + int count, key; +{ + static char target; + static int orig_dir, dir; + int pos; + + if (key == ';' || key == ',') + dir = (key == ';' ? orig_dir : -orig_dir); + else + { + target = rl_read_key(); + + switch (key) + { + case 't': + orig_dir = dir = FTO; + break; + + case 'T': + orig_dir = dir = BTO; + break; + + case 'f': + orig_dir = dir = FFIND; + break; + + case 'F': + orig_dir = dir = BFIND; + break; + } + } + + pos = rl_point; + + if (dir < 0) + { + pos--; + do + { + if (the_line[pos] == target) + { + if (dir == BTO) + rl_point = pos + 1; + else + rl_point = pos; + return; + } + } + while (pos--); + + if (pos < 0) + { + ding (); + return; + } + } + else + { /* dir > 0 */ + pos++; + do + { + if (the_line[pos] == target) + { + if (dir == FTO) + rl_point = pos - 1; + else + rl_point = pos; + return; + } + } + while (++pos < rl_end); + + if (pos >= (rl_end - 1)) + ding (); + } +} + +/* Match brackets */ +rl_vi_match () +{ + int count = 1, brack, pos; + + pos = rl_point; + if ((brack = rl_vi_bracktype (the_line[rl_point])) == 0) + { + while ((brack = rl_vi_bracktype (the_line[rl_point])) == 0 && + rl_point < rl_end - 1) + rl_forward (1); + + if (brack <= 0) + { + rl_point = pos; + ding (); + return; + } + } + + pos = rl_point; + + if (brack < 0) + { + while (count) + { + if (--pos >= 0) + { + int b = rl_vi_bracktype (the_line[pos]); + if (b == -brack) + count--; + else if (b == brack) + count++; + } + else + { + ding (); + return; + } + } + } + else + { /* brack > 0 */ + while (count) + { + if (++pos < rl_end) + { + int b = rl_vi_bracktype (the_line[pos]); + if (b == -brack) + count--; + else if (b == brack) + count++; + } + else + { + ding (); + return; + } + } + } + rl_point = pos; +} + +int +rl_vi_bracktype (c) + int c; +{ + switch (c) + { + case '(': return 1; + case ')': return -1; + case '[': return 2; + case ']': return -2; + case '{': return 3; + case '}': return -3; + default: return 0; + } +} + +rl_vi_change_char () +{ + int c; + + c = rl_read_key(); + + switch (c) + { + case '\033': + case CTRL('C'): + return; + + default: + rl_begin_undo_group (); + rl_delete (1); + rl_insert (1, c); + rl_end_undo_group (); + break; + } +} + +rl_vi_subst (count, key) + int count, key; +{ + rl_begin_undo_group (); + vi_doing_insert = 1; + + if (uppercase_p (key)) + { + rl_beg_of_line (); + rl_kill_line (1); + } + else + rl_delete (1); + + rl_vi_insertion_mode (); +} + +rl_vi_overstrike (count, key) + int count, key; +{ + int i; + + if (vi_doing_insert == 0) + { + vi_doing_insert = 1; + rl_begin_undo_group (); + } + + for (i = 0; i < count; i++) + { + vi_replace_count++; + rl_begin_undo_group (); + + if (rl_point < rl_end) + { + rl_delete (1); + rl_insert (1, key); + } + else + rl_insert (1, key); + + rl_end_undo_group (); + } +} + +rl_vi_overstrike_delete (count) + int count; +{ + int i, s; + + for (i = 0; i < count; i++) + { + if (vi_replace_count == 0) + { + ding (); + break; + } + s = rl_point; + + if (rl_do_undo ()) + vi_replace_count--; + + if (rl_point == s) + rl_backward (1); + } + + if (vi_replace_count == 0 && vi_doing_insert) + { + rl_end_undo_group (); + rl_do_undo (); + vi_doing_insert = 0; + } +} + +rl_vi_replace () +{ + int i; + + vi_replace_count = 0; + + vi_replace_map = rl_make_bare_keymap (); + + for (i = ' '; i < 127; i++) + vi_replace_map[i].function = rl_vi_overstrike; + + vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete; + vi_replace_map[CTRL('H')].function = rl_vi_overstrike_delete; + vi_replace_map[ESC].function = rl_vi_movement_mode; + vi_replace_map[RETURN].function = rl_newline; + vi_replace_map[NEWLINE].function = rl_newline; + keymap = vi_replace_map; +} diff --git a/gnu/usr.bin/gdb/regex.c b/gnu/usr.bin/gdb/regex.c new file mode 100644 index 0000000000..45c34780f6 --- /dev/null +++ b/gnu/usr.bin/gdb/regex.c @@ -0,0 +1,1738 @@ +/* Extended regular expression matching and search library. + Copyright (C) 1985, 1989 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! */ + + +/* To test, compile with -Dtest. + This Dtestable feature turns this into a self-contained program + which reads a pattern, describes how it compiles, + then reads a string and searches for it. */ + +#ifdef emacs + +/* The `emacs' switch turns on certain special matching commands + that make sense only in emacs. */ + +#include "config.h" +#include "lisp.h" +#include "buffer.h" +#include "syntax.h" + +#else /* not emacs */ + +#ifdef USG +#ifndef BSTRING +#define bcopy(s,d,n) memcpy((d),(s),(n)) +#define bcmp(s1,s2,n) memcmp((s1),(s2),(n)) +#define bzero(s,n) memset((s),0,(n)) +#endif +#endif + +/* Make alloca work the best possible way. */ +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +#ifdef sparc +#include +#endif +#endif + +/* + * Define the syntax stuff, so we can do the \<...\> things. + */ + +#ifndef Sword /* must be non-zero in some of the tests below... */ +#define Sword 1 +#endif + +#define SYNTAX(c) re_syntax_table[c] + +#ifdef SYNTAX_TABLE + +char *re_syntax_table; + +#else + +static char re_syntax_table[256]; + +static void +init_syntax_once () +{ + register int c; + static int done = 0; + + if (done) + return; + + bzero (re_syntax_table, sizeof re_syntax_table); + + for (c = 'a'; c <= 'z'; c++) + re_syntax_table[c] = Sword; + + for (c = 'A'; c <= 'Z'; c++) + re_syntax_table[c] = Sword; + + for (c = '0'; c <= '9'; c++) + re_syntax_table[c] = Sword; + + done = 1; +} + +#endif /* SYNTAX_TABLE */ +#endif /* not emacs */ + +#include "regex.h" + +/* Number of failure points to allocate space for initially, + when matching. If this number is exceeded, more space is allocated, + so it is not a hard limit. */ + +#ifndef NFAILURES +#define NFAILURES 80 +#endif /* NFAILURES */ + +/* width of a byte in bits */ + +#define BYTEWIDTH 8 + +#ifndef SIGN_EXTEND_CHAR +#define SIGN_EXTEND_CHAR(x) (x) +#endif + +static int obscure_syntax = 0; + +/* Specify the precise syntax of regexp for compilation. + This provides for compatibility for various utilities + which historically have different, incompatible syntaxes. + + The argument SYNTAX is a bit-mask containing the two bits + RE_NO_BK_PARENS and RE_NO_BK_VBAR. */ + +int +re_set_syntax (syntax) +{ + int ret; + + ret = obscure_syntax; + obscure_syntax = syntax; + return ret; +} + +/* re_compile_pattern takes a regular-expression string + and converts it into a buffer full of byte commands for matching. + + PATTERN is the address of the pattern string + SIZE is the length of it. + BUFP is a struct re_pattern_buffer * which points to the info + on where to store the byte commands. + This structure contains a char * which points to the + actual space, which should have been obtained with malloc. + re_compile_pattern may use realloc to grow the buffer space. + + The number of bytes of commands can be found out by looking in + the struct re_pattern_buffer that bufp pointed to, + after re_compile_pattern returns. +*/ + +#define PATPUSH(ch) (*b++ = (char) (ch)) + +#define PATFETCH(c) \ + {if (p == pend) goto end_of_pattern; \ + c = * (unsigned char *) p++; \ + if (translate) c = translate[c]; } + +#define PATFETCH_RAW(c) \ + {if (p == pend) goto end_of_pattern; \ + c = * (unsigned char *) p++; } + +#define PATUNFETCH p-- + +#define EXTEND_BUFFER \ + { char *old_buffer = bufp->buffer; \ + if (bufp->allocated == (1<<16)) goto too_big; \ + bufp->allocated *= 2; \ + if (bufp->allocated > (1<<16)) bufp->allocated = (1<<16); \ + if (!(bufp->buffer = (char *) realloc (bufp->buffer, bufp->allocated))) \ + goto memory_exhausted; \ + c = bufp->buffer - old_buffer; \ + b += c; \ + if (fixup_jump) \ + fixup_jump += c; \ + if (laststart) \ + laststart += c; \ + begalt += c; \ + if (pending_exact) \ + pending_exact += c; \ + } + +static int store_jump (), insert_jump (); + +char * +re_compile_pattern (pattern, size, bufp) + char *pattern; + int size; + struct re_pattern_buffer *bufp; +{ + register char *b = bufp->buffer; + register char *p = pattern; + char *pend = pattern + size; + register unsigned c, c1; + char *p1; + unsigned char *translate = (unsigned char *) bufp->translate; + + /* address of the count-byte of the most recently inserted "exactn" command. + This makes it possible to tell whether a new exact-match character + can be added to that command or requires a new "exactn" command. */ + + char *pending_exact = 0; + + /* address of the place where a forward-jump should go + to the end of the containing expression. + Each alternative of an "or", except the last, ends with a forward-jump + of this sort. */ + + char *fixup_jump = 0; + + /* address of start of the most recently finished expression. + This tells postfix * where to find the start of its operand. */ + + char *laststart = 0; + + /* In processing a repeat, 1 means zero matches is allowed */ + + char zero_times_ok; + + /* In processing a repeat, 1 means many matches is allowed */ + + char many_times_ok; + + /* address of beginning of regexp, or inside of last \( */ + + char *begalt = b; + + /* Stack of information saved by \( and restored by \). + Four stack elements are pushed by each \(: + First, the value of b. + Second, the value of fixup_jump. + Third, the value of regnum. + Fourth, the value of begalt. */ + + int stackb[40]; + int *stackp = stackb; + int *stacke = stackb + 40; + int *stackt; + + /* Counts \('s as they are encountered. Remembered for the matching \), + where it becomes the "register number" to put in the stop_memory command */ + + int regnum = 1; + + bufp->fastmap_accurate = 0; + +#ifndef emacs +#ifndef SYNTAX_TABLE + /* + * Initialize the syntax table. + */ + init_syntax_once(); +#endif +#endif + + if (bufp->allocated == 0) + { + bufp->allocated = 28; + if (bufp->buffer) + /* EXTEND_BUFFER loses when bufp->allocated is 0 */ + bufp->buffer = (char *) realloc (bufp->buffer, 28); + else + /* Caller did not allocate a buffer. Do it for him */ + bufp->buffer = (char *) malloc (28); + if (!bufp->buffer) goto memory_exhausted; + begalt = b = bufp->buffer; + } + + while (p != pend) + { + if (b - bufp->buffer > bufp->allocated - 10) + /* Note that EXTEND_BUFFER clobbers c */ + EXTEND_BUFFER; + + PATFETCH (c); + + switch (c) + { + case '$': + if (obscure_syntax & RE_TIGHT_VBAR) + { + if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS) && p != pend) + goto normal_char; + /* Make operand of last vbar end before this `$'. */ + if (fixup_jump) + store_jump (fixup_jump, jump, b); + fixup_jump = 0; + PATPUSH (endline); + break; + } + + /* $ means succeed if at end of line, but only in special contexts. + If randomly in the middle of a pattern, it is a normal character. */ + if (p == pend || *p == '\n' + || (obscure_syntax & RE_CONTEXT_INDEP_OPS) + || (obscure_syntax & RE_NO_BK_PARENS + ? *p == ')' + : *p == '\\' && p[1] == ')') + || (obscure_syntax & RE_NO_BK_VBAR + ? *p == '|' + : *p == '\\' && p[1] == '|')) + { + PATPUSH (endline); + break; + } + goto normal_char; + + case '^': + /* ^ means succeed if at beg of line, but only if no preceding pattern. */ + + if (laststart && p[-2] != '\n' + && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + if (obscure_syntax & RE_TIGHT_VBAR) + { + if (p != pattern + 1 + && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + PATPUSH (begline); + begalt = b; + } + else + PATPUSH (begline); + break; + + case '+': + case '?': + if (obscure_syntax & RE_BK_PLUS_QM) + goto normal_char; + handle_plus: + case '*': + /* If there is no previous pattern, char not special. */ + if (!laststart && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + /* If there is a sequence of repetition chars, + collapse it down to equivalent to just one. */ + zero_times_ok = 0; + many_times_ok = 0; + while (1) + { + zero_times_ok |= c != '+'; + many_times_ok |= c != '?'; + if (p == pend) + break; + PATFETCH (c); + if (c == '*') + ; + else if (!(obscure_syntax & RE_BK_PLUS_QM) + && (c == '+' || c == '?')) + ; + else if ((obscure_syntax & RE_BK_PLUS_QM) + && c == '\\') + { + int c1; + PATFETCH (c1); + if (!(c1 == '+' || c1 == '?')) + { + PATUNFETCH; + PATUNFETCH; + break; + } + c = c1; + } + else + { + PATUNFETCH; + break; + } + } + + /* Star, etc. applied to an empty pattern is equivalent + to an empty pattern. */ + if (!laststart) + break; + + /* Now we know whether 0 matches is allowed, + and whether 2 or more matches is allowed. */ + if (many_times_ok) + { + /* If more than one repetition is allowed, + put in a backward jump at the end. */ + store_jump (b, maybe_finalize_jump, laststart - 3); + b += 3; + } + insert_jump (on_failure_jump, laststart, b + 3, b); + pending_exact = 0; + b += 3; + if (!zero_times_ok) + { + /* At least one repetition required: insert before the loop + a skip over the initial on-failure-jump instruction */ + insert_jump (dummy_failure_jump, laststart, laststart + 6, b); + b += 3; + } + break; + + case '.': + laststart = b; + PATPUSH (anychar); + break; + + case '[': + while (b - bufp->buffer + > bufp->allocated - 3 - (1 << BYTEWIDTH) / BYTEWIDTH) + /* Note that EXTEND_BUFFER clobbers c */ + EXTEND_BUFFER; + + laststart = b; + if (*p == '^') + PATPUSH (charset_not), p++; + else + PATPUSH (charset); + p1 = p; + + PATPUSH ((1 << BYTEWIDTH) / BYTEWIDTH); + /* Clear the whole map */ + bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH); + /* Read in characters and ranges, setting map bits */ + while (1) + { + PATFETCH (c); + if (c == ']' && p != p1 + 1) break; + if (*p == '-' && p[1] != ']') + { + PATFETCH (c1); + PATFETCH (c1); + while (c <= c1) + b[c / BYTEWIDTH] |= 1 << (c % BYTEWIDTH), c++; + } + else + { + b[c / BYTEWIDTH] |= 1 << (c % BYTEWIDTH); + } + } + /* Discard any bitmap bytes that are all 0 at the end of the map. + Decrement the map-length byte too. */ + while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) + b[-1]--; + b += b[-1]; + break; + + case '(': + if (! (obscure_syntax & RE_NO_BK_PARENS)) + goto normal_char; + else + goto handle_open; + + case ')': + if (! (obscure_syntax & RE_NO_BK_PARENS)) + goto normal_char; + else + goto handle_close; + + case '\n': + if (! (obscure_syntax & RE_NEWLINE_OR)) + goto normal_char; + else + goto handle_bar; + + case '|': + if (! (obscure_syntax & RE_NO_BK_VBAR)) + goto normal_char; + else + goto handle_bar; + + case '\\': + if (p == pend) goto invalid_pattern; + PATFETCH_RAW (c); + switch (c) + { + case '(': + if (obscure_syntax & RE_NO_BK_PARENS) + goto normal_backsl; + handle_open: + if (stackp == stacke) goto nesting_too_deep; + if (regnum < RE_NREGS) + { + PATPUSH (start_memory); + PATPUSH (regnum); + } + *stackp++ = b - bufp->buffer; + *stackp++ = fixup_jump ? fixup_jump - bufp->buffer + 1 : 0; + *stackp++ = regnum++; + *stackp++ = begalt - bufp->buffer; + fixup_jump = 0; + laststart = 0; + begalt = b; + break; + + case ')': + if (obscure_syntax & RE_NO_BK_PARENS) + goto normal_backsl; + handle_close: + if (stackp == stackb) goto unmatched_close; + begalt = *--stackp + bufp->buffer; + if (fixup_jump) + store_jump (fixup_jump, jump, b); + if (stackp[-1] < RE_NREGS) + { + PATPUSH (stop_memory); + PATPUSH (stackp[-1]); + } + stackp -= 2; + fixup_jump = 0; + if (*stackp) + fixup_jump = *stackp + bufp->buffer - 1; + laststart = *--stackp + bufp->buffer; + break; + + case '|': + if (obscure_syntax & RE_NO_BK_VBAR) + goto normal_backsl; + handle_bar: + insert_jump (on_failure_jump, begalt, b + 6, b); + pending_exact = 0; + b += 3; + if (fixup_jump) + store_jump (fixup_jump, jump, b); + fixup_jump = b; + b += 3; + laststart = 0; + begalt = b; + break; + +#ifdef emacs + case '=': + PATPUSH (at_dot); + break; + + case 's': + laststart = b; + PATPUSH (syntaxspec); + PATFETCH (c); + PATPUSH (syntax_spec_code[c]); + break; + + case 'S': + laststart = b; + PATPUSH (notsyntaxspec); + PATFETCH (c); + PATPUSH (syntax_spec_code[c]); + break; +#endif /* emacs */ + + case 'w': + laststart = b; + PATPUSH (wordchar); + break; + + case 'W': + laststart = b; + PATPUSH (notwordchar); + break; + + case '<': + PATPUSH (wordbeg); + break; + + case '>': + PATPUSH (wordend); + break; + + case 'b': + PATPUSH (wordbound); + break; + + case 'B': + PATPUSH (notwordbound); + break; + + case '`': + PATPUSH (begbuf); + break; + + case '\'': + PATPUSH (endbuf); + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + c1 = c - '0'; + if (c1 >= regnum) + goto normal_char; + for (stackt = stackp - 2; stackt > stackb; stackt -= 4) + if (*stackt == c1) + goto normal_char; + laststart = b; + PATPUSH (duplicate); + PATPUSH (c1); + break; + + case '+': + case '?': + if (obscure_syntax & RE_BK_PLUS_QM) + goto handle_plus; + + default: + normal_backsl: + /* You might think it would be useful for \ to mean + not to translate; but if we don't translate it + it will never match anything. */ + if (translate) c = translate[c]; + goto normal_char; + } + break; + + default: + normal_char: + if (!pending_exact || pending_exact + *pending_exact + 1 != b + || *pending_exact == 0177 || *p == '*' || *p == '^' + || ((obscure_syntax & RE_BK_PLUS_QM) + ? *p == '\\' && (p[1] == '+' || p[1] == '?') + : (*p == '+' || *p == '?'))) + { + laststart = b; + PATPUSH (exactn); + pending_exact = b; + PATPUSH (0); + } + PATPUSH (c); + (*pending_exact)++; + } + } + + if (fixup_jump) + store_jump (fixup_jump, jump, b); + + if (stackp != stackb) goto unmatched_open; + + bufp->used = b - bufp->buffer; + return 0; + + invalid_pattern: + return "Invalid regular expression"; + + unmatched_open: + return "Unmatched \\("; + + unmatched_close: + return "Unmatched \\)"; + + end_of_pattern: + return "Premature end of regular expression"; + + nesting_too_deep: + return "Nesting too deep"; + + too_big: + return "Regular expression too big"; + + memory_exhausted: + return "Memory exhausted"; +} + +/* Store where `from' points a jump operation to jump to where `to' points. + `opcode' is the opcode to store. */ + +static int +store_jump (from, opcode, to) + char *from, *to; + char opcode; +{ + from[0] = opcode; + from[1] = (to - (from + 3)) & 0377; + from[2] = (to - (from + 3)) >> 8; +} + +/* Open up space at char FROM, and insert there a jump to TO. + CURRENT_END gives te end of the storage no in use, + so we know how much data to copy up. + OP is the opcode of the jump to insert. + + If you call this function, you must zero out pending_exact. */ + +static int +insert_jump (op, from, to, current_end) + char op; + char *from, *to, *current_end; +{ + register char *pto = current_end + 3; + register char *pfrom = current_end; + while (pfrom != from) + *--pto = *--pfrom; + store_jump (from, op, to); +} + +/* Given a pattern, compute a fastmap from it. + The fastmap records which of the (1 << BYTEWIDTH) possible characters + can start a string that matches the pattern. + This fastmap is used by re_search to skip quickly over totally implausible text. + + The caller must supply the address of a (1 << BYTEWIDTH)-byte data area + as bufp->fastmap. + The other components of bufp describe the pattern to be used. */ + +void +re_compile_fastmap (bufp) + struct re_pattern_buffer *bufp; +{ + unsigned char *pattern = (unsigned char *) bufp->buffer; + int size = bufp->used; + register char *fastmap = bufp->fastmap; + register unsigned char *p = pattern; + register unsigned char *pend = pattern + size; + register int j, k; + unsigned char *translate = (unsigned char *) bufp->translate; + + unsigned char *stackb[NFAILURES]; + unsigned char **stackp = stackb; + + bzero (fastmap, (1 << BYTEWIDTH)); + bufp->fastmap_accurate = 1; + bufp->can_be_null = 0; + + while (p) + { + if (p == pend) + { + bufp->can_be_null = 1; + break; + } +#ifdef SWITCH_ENUM_BUG + switch ((int) ((enum regexpcode) *p++)) +#else + switch ((enum regexpcode) *p++) +#endif + { + case exactn: + if (translate) + fastmap[translate[p[1]]] = 1; + else + fastmap[p[1]] = 1; + break; + + case begline: + case before_dot: + case at_dot: + case after_dot: + case begbuf: + case endbuf: + case wordbound: + case notwordbound: + case wordbeg: + case wordend: + continue; + + case endline: + if (translate) + fastmap[translate['\n']] = 1; + else + fastmap['\n'] = 1; + if (bufp->can_be_null != 1) + bufp->can_be_null = 2; + break; + + case finalize_jump: + case maybe_finalize_jump: + case jump: + case dummy_failure_jump: + bufp->can_be_null = 1; + j = *p++ & 0377; + j += SIGN_EXTEND_CHAR (*(char *)p) << 8; + p += j + 1; /* The 1 compensates for missing ++ above */ + if (j > 0) + continue; + /* Jump backward reached implies we just went through + the body of a loop and matched nothing. + Opcode jumped to should be an on_failure_jump. + Just treat it like an ordinary jump. + For a * loop, it has pushed its failure point already; + if so, discard that as redundant. */ + if ((enum regexpcode) *p != on_failure_jump) + continue; + p++; + j = *p++ & 0377; + j += SIGN_EXTEND_CHAR (*(char *)p) << 8; + p += j + 1; /* The 1 compensates for missing ++ above */ + if (stackp != stackb && *stackp == p) + stackp--; + continue; + + case on_failure_jump: + j = *p++ & 0377; + j += SIGN_EXTEND_CHAR (*(char *)p) << 8; + p++; + *++stackp = p + j; + continue; + + case start_memory: + case stop_memory: + p++; + continue; + + case duplicate: + bufp->can_be_null = 1; + fastmap['\n'] = 1; + case anychar: + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (j != '\n') + fastmap[j] = 1; + if (bufp->can_be_null) + return; + /* Don't return; check the alternative paths + so we can set can_be_null if appropriate. */ + break; + + case wordchar: + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) == Sword) + fastmap[j] = 1; + break; + + case notwordchar: + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) != Sword) + fastmap[j] = 1; + break; + +#ifdef emacs + case syntaxspec: + k = *p++; + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) == (enum syntaxcode) k) + fastmap[j] = 1; + break; + + case notsyntaxspec: + k = *p++; + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) != (enum syntaxcode) k) + fastmap[j] = 1; + break; +#endif /* emacs */ + + case charset: + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) + { + if (translate) + fastmap[translate[j]] = 1; + else + fastmap[j] = 1; + } + break; + + case charset_not: + /* Chars beyond end of map must be allowed */ + for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++) + if (translate) + fastmap[translate[j]] = 1; + else + fastmap[j] = 1; + + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) + { + if (translate) + fastmap[translate[j]] = 1; + else + fastmap[j] = 1; + } + break; + } + + /* Get here means we have successfully found the possible starting characters + of one path of the pattern. We need not follow this path any farther. + Instead, look at the next alternative remembered in the stack. */ + if (stackp != stackb) + p = *stackp--; + else + break; + } +} + +/* Like re_search_2, below, but only one string is specified. */ + +int +re_search (pbufp, string, size, startpos, range, regs) + struct re_pattern_buffer *pbufp; + char *string; + int size, startpos, range; + struct re_registers *regs; +{ + return re_search_2 (pbufp, 0, 0, string, size, startpos, range, regs, size); +} + +/* Like re_match_2 but tries first a match starting at index STARTPOS, + then at STARTPOS + 1, and so on. + RANGE is the number of places to try before giving up. + If RANGE is negative, the starting positions tried are + STARTPOS, STARTPOS - 1, etc. + It is up to the caller to make sure that range is not so large + as to take the starting position outside of the input strings. + +The value returned is the position at which the match was found, + or -1 if no match was found, + or -2 if error (such as failure stack overflow). */ + +int +re_search_2 (pbufp, string1, size1, string2, size2, startpos, range, regs, mstop) + struct re_pattern_buffer *pbufp; + char *string1, *string2; + int size1, size2; + int startpos; + register int range; + struct re_registers *regs; + int mstop; +{ + register char *fastmap = pbufp->fastmap; + register unsigned char *translate = (unsigned char *) pbufp->translate; + int total = size1 + size2; + int val; + + /* Update the fastmap now if not correct already */ + if (fastmap && !pbufp->fastmap_accurate) + re_compile_fastmap (pbufp); + + /* Don't waste time in a long search for a pattern + that says it is anchored. */ + if (pbufp->used > 0 && (enum regexpcode) pbufp->buffer[0] == begbuf + && range > 0) + { + if (startpos > 0) + return -1; + else + range = 1; + } + + while (1) + { + /* If a fastmap is supplied, skip quickly over characters + that cannot possibly be the start of a match. + Note, however, that if the pattern can possibly match + the null string, we must test it at each starting point + so that we take the first null string we get. */ + + if (fastmap && startpos < total && pbufp->can_be_null != 1) + { + if (range > 0) + { + register int lim = 0; + register unsigned char *p; + int irange = range; + if (startpos < size1 && startpos + range >= size1) + lim = range - (size1 - startpos); + + p = ((unsigned char *) + &(startpos >= size1 ? string2 - size1 : string1)[startpos]); + + if (translate) + { + while (range > lim && !fastmap[translate[*p++]]) + range--; + } + else + { + while (range > lim && !fastmap[*p++]) + range--; + } + startpos += irange - range; + } + else + { + register unsigned char c; + if (startpos >= size1) + c = string2[startpos - size1]; + else + c = string1[startpos]; + c &= 0xff; + if (translate ? !fastmap[translate[c]] : !fastmap[c]) + goto advance; + } + } + + if (range >= 0 && startpos == total + && fastmap && pbufp->can_be_null == 0) + return -1; + + val = re_match_2 (pbufp, string1, size1, string2, size2, startpos, regs, mstop); + if (0 <= val) + { + if (val == -2) + return -2; + return startpos; + } + +#ifdef C_ALLOCA + alloca (0); +#endif /* C_ALLOCA */ + + advance: + if (!range) break; + if (range > 0) range--, startpos++; else range++, startpos--; + } + return -1; +} + +#ifndef emacs /* emacs never uses this */ +int +re_match (pbufp, string, size, pos, regs) + struct re_pattern_buffer *pbufp; + char *string; + int size, pos; + struct re_registers *regs; +{ + return re_match_2 (pbufp, 0, 0, string, size, pos, regs, size); +} +#endif /* emacs */ + +/* Maximum size of failure stack. Beyond this, overflow is an error. */ + +int re_max_failures = 2000; + +static int bcmp_translate(); +/* Match the pattern described by PBUFP + against data which is the virtual concatenation of STRING1 and STRING2. + SIZE1 and SIZE2 are the sizes of the two data strings. + Start the match at position POS. + Do not consider matching past the position MSTOP. + + If pbufp->fastmap is nonzero, then it had better be up to date. + + The reason that the data to match are specified as two components + which are to be regarded as concatenated + is so this function can be used directly on the contents of an Emacs buffer. + + -1 is returned if there is no match. -2 is returned if there is + an error (such as match stack overflow). Otherwise the value is the length + of the substring which was matched. */ + +int +re_match_2 (pbufp, string1, size1, string2, size2, pos, regs, mstop) + struct re_pattern_buffer *pbufp; + unsigned char *string1, *string2; + int size1, size2; + int pos; + struct re_registers *regs; + int mstop; +{ + register unsigned char *p = (unsigned char *) pbufp->buffer; + register unsigned char *pend = p + pbufp->used; + /* End of first string */ + unsigned char *end1; + /* End of second string */ + unsigned char *end2; + /* Pointer just past last char to consider matching */ + unsigned char *end_match_1, *end_match_2; + register unsigned char *d, *dend; + register int mcnt; + unsigned char *translate = (unsigned char *) pbufp->translate; + + /* Failure point stack. Each place that can handle a failure further down the line + pushes a failure point on this stack. It consists of two char *'s. + The first one pushed is where to resume scanning the pattern; + the second pushed is where to resume scanning the strings. + If the latter is zero, the failure point is a "dummy". + If a failure happens and the innermost failure point is dormant, + it discards that failure point and tries the next one. */ + + unsigned char *initial_stack[2 * NFAILURES]; + unsigned char **stackb = initial_stack; + unsigned char **stackp = stackb, **stacke = &stackb[2 * NFAILURES]; + + /* Information on the "contents" of registers. + These are pointers into the input strings; they record + just what was matched (on this attempt) by some part of the pattern. + The start_memory command stores the start of a register's contents + and the stop_memory command stores the end. + + At that point, regstart[regnum] points to the first character in the register, + regend[regnum] points to the first character beyond the end of the register, + regstart_seg1[regnum] is true iff regstart[regnum] points into string1, + and regend_seg1[regnum] is true iff regend[regnum] points into string1. */ + + unsigned char *regstart[RE_NREGS]; + unsigned char *regend[RE_NREGS]; + unsigned char regstart_seg1[RE_NREGS], regend_seg1[RE_NREGS]; + + /* Set up pointers to ends of strings. + Don't allow the second string to be empty unless both are empty. */ + if (!size2) + { + string2 = string1; + size2 = size1; + string1 = 0; + size1 = 0; + } + end1 = string1 + size1; + end2 = string2 + size2; + + /* Compute where to stop matching, within the two strings */ + if (mstop <= size1) + { + end_match_1 = string1 + mstop; + end_match_2 = string2; + } + else + { + end_match_1 = end1; + end_match_2 = string2 + mstop - size1; + } + + /* Initialize \) text positions to -1 + to mark ones that no \( or \) has been seen for. */ + + for (mcnt = 0; mcnt < sizeof (regend) / sizeof (*regend); mcnt++) + regend[mcnt] = (unsigned char *) -1; + + /* `p' scans through the pattern as `d' scans through the data. + `dend' is the end of the input string that `d' points within. + `d' is advanced into the following input string whenever necessary, + but this happens before fetching; + therefore, at the beginning of the loop, + `d' can be pointing at the end of a string, + but it cannot equal string2. */ + + if (pos <= size1) + d = string1 + pos, dend = end_match_1; + else + d = string2 + pos - size1, dend = end_match_2; + +/* Write PREFETCH; just before fetching a character with *d. */ +#define PREFETCH \ + while (d == dend) \ + { if (dend == end_match_2) goto fail; /* end of string2 => failure */ \ + d = string2; /* end of string1 => advance to string2. */ \ + dend = end_match_2; } + + /* This loop loops over pattern commands. + It exits by returning from the function if match is complete, + or it drops through if match fails at this starting point in the input data. */ + + while (1) + { + if (p == pend) + /* End of pattern means we have succeeded! */ + { + /* If caller wants register contents data back, convert it to indices */ + if (regs) + { + regs->start[0] = pos; + if (dend == end_match_1) + regs->end[0] = d - string1; + else + regs->end[0] = d - string2 + size1; + for (mcnt = 1; mcnt < RE_NREGS; mcnt++) + { + if (regend[mcnt] == (unsigned char *) -1) + { + regs->start[mcnt] = -1; + regs->end[mcnt] = -1; + continue; + } + if (regstart_seg1[mcnt]) + regs->start[mcnt] = regstart[mcnt] - string1; + else + regs->start[mcnt] = regstart[mcnt] - string2 + size1; + if (regend_seg1[mcnt]) + regs->end[mcnt] = regend[mcnt] - string1; + else + regs->end[mcnt] = regend[mcnt] - string2 + size1; + } + } + if (dend == end_match_1) + return (d - string1 - pos); + else + return d - string2 + size1 - pos; + } + + /* Otherwise match next pattern command */ +#ifdef SWITCH_ENUM_BUG + switch ((int) ((enum regexpcode) *p++)) +#else + switch ((enum regexpcode) *p++) +#endif + { + + /* \( is represented by a start_memory, \) by a stop_memory. + Both of those commands contain a "register number" argument. + The text matched within the \( and \) is recorded under that number. + Then, \ turns into a `duplicate' command which + is followed by the numeric value of as the register number. */ + + case start_memory: + regstart[*p] = d; + regstart_seg1[*p++] = (dend == end_match_1); + break; + + case stop_memory: + regend[*p] = d; + regend_seg1[*p++] = (dend == end_match_1); + break; + + case duplicate: + { + int regno = *p++; /* Get which register to match against */ + register unsigned char *d2, *dend2; + + d2 = regstart[regno]; + dend2 = ((regstart_seg1[regno] == regend_seg1[regno]) + ? regend[regno] : end_match_1); + while (1) + { + /* Advance to next segment in register contents, if necessary */ + while (d2 == dend2) + { + if (dend2 == end_match_2) break; + if (dend2 == regend[regno]) break; + d2 = string2, dend2 = regend[regno]; /* end of string1 => advance to string2. */ + } + /* At end of register contents => success */ + if (d2 == dend2) break; + + /* Advance to next segment in data being matched, if necessary */ + PREFETCH; + + /* mcnt gets # consecutive chars to compare */ + mcnt = dend - d; + if (mcnt > dend2 - d2) + mcnt = dend2 - d2; + /* Compare that many; failure if mismatch, else skip them. */ + if (translate ? bcmp_translate (d, d2, mcnt, translate) : bcmp (d, d2, mcnt)) + goto fail; + d += mcnt, d2 += mcnt; + } + } + break; + + case anychar: + /* fetch a data character */ + PREFETCH; + /* Match anything but a newline. */ + if ((translate ? translate[*d++] : *d++) == '\n') + goto fail; + break; + + case charset: + case charset_not: + { + /* Nonzero for charset_not */ + int not = 0; + register int c; + if (*(p - 1) == (unsigned char) charset_not) + not = 1; + + /* fetch a data character */ + PREFETCH; + + if (translate) + c = translate [*d]; + else + c = *d; + + if (c < *p * BYTEWIDTH + && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) + not = !not; + + p += 1 + *p; + + if (!not) goto fail; + d++; + break; + } + + case begline: + if (d == string1 || d[-1] == '\n') + break; + goto fail; + + case endline: + if (d == end2 + || (d == end1 ? (size2 == 0 || *string2 == '\n') : *d == '\n')) + break; + goto fail; + + /* "or" constructs ("|") are handled by starting each alternative + with an on_failure_jump that points to the start of the next alternative. + Each alternative except the last ends with a jump to the joining point. + (Actually, each jump except for the last one really jumps + to the following jump, because tensioning the jumps is a hassle.) */ + + /* The start of a stupid repeat has an on_failure_jump that points + past the end of the repeat text. + This makes a failure point so that, on failure to match a repetition, + matching restarts past as many repetitions have been found + with no way to fail and look for another one. */ + + /* A smart repeat is similar but loops back to the on_failure_jump + so that each repetition makes another failure point. */ + + case on_failure_jump: + if (stackp == stacke) + { + unsigned char **stackx; + if (stacke - stackb > re_max_failures * 2) + return -2; + stackx = (unsigned char **) alloca (2 * (stacke - stackb) + * sizeof (char *)); + bcopy (stackb, stackx, (stacke - stackb) * sizeof (char *)); + stackp = stackx + (stackp - stackb); + stacke = stackx + 2 * (stacke - stackb); + stackb = stackx; + } + mcnt = *p++ & 0377; + mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8; + p++; + *stackp++ = mcnt + p; + *stackp++ = d; + break; + + /* The end of a smart repeat has an maybe_finalize_jump back. + Change it either to a finalize_jump or an ordinary jump. */ + + case maybe_finalize_jump: + mcnt = *p++ & 0377; + mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8; + p++; + { + register unsigned char *p2 = p; + /* Compare what follows with the begining of the repeat. + If we can establish that there is nothing that they would + both match, we can change to finalize_jump */ + while (p2 != pend + && (*p2 == (unsigned char) stop_memory + || *p2 == (unsigned char) start_memory)) + p2++; + if (p2 == pend) + p[-3] = (unsigned char) finalize_jump; + else if (*p2 == (unsigned char) exactn + || *p2 == (unsigned char) endline) + { + register int c = *p2 == (unsigned char) endline ? '\n' : p2[2]; + register unsigned char *p1 = p + mcnt; + /* p1[0] ... p1[2] are an on_failure_jump. + Examine what follows that */ + if (p1[3] == (unsigned char) exactn && p1[5] != c) + p[-3] = (unsigned char) finalize_jump; + else if (p1[3] == (unsigned char) charset + || p1[3] == (unsigned char) charset_not) + { + int not = p1[3] == (unsigned char) charset_not; + if (c < p1[4] * BYTEWIDTH + && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) + not = !not; + /* not is 1 if c would match */ + /* That means it is not safe to finalize */ + if (!not) + p[-3] = (unsigned char) finalize_jump; + } + } + } + p -= 2; + if (p[-1] != (unsigned char) finalize_jump) + { + p[-1] = (unsigned char) jump; + goto nofinalize; + } + + /* The end of a stupid repeat has a finalize-jump + back to the start, where another failure point will be made + which will point after all the repetitions found so far. */ + + case finalize_jump: + stackp -= 2; + + case jump: + nofinalize: + mcnt = *p++ & 0377; + mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8; + p += mcnt + 1; /* The 1 compensates for missing ++ above */ + break; + + case dummy_failure_jump: + if (stackp == stacke) + { + unsigned char **stackx + = (unsigned char **) alloca (2 * (stacke - stackb) + * sizeof (char *)); + bcopy (stackb, stackx, (stacke - stackb) * sizeof (char *)); + stackp = stackx + (stackp - stackb); + stacke = stackx + 2 * (stacke - stackb); + stackb = stackx; + } + *stackp++ = 0; + *stackp++ = 0; + goto nofinalize; + + case wordbound: + if (d == string1 /* Points to first char */ + || d == end2 /* Points to end */ + || (d == end1 && size2 == 0)) /* Points to end */ + break; + if ((SYNTAX (d[-1]) == Sword) + != (SYNTAX (d == end1 ? *string2 : *d) == Sword)) + break; + goto fail; + + case notwordbound: + if (d == string1 /* Points to first char */ + || d == end2 /* Points to end */ + || (d == end1 && size2 == 0)) /* Points to end */ + goto fail; + if ((SYNTAX (d[-1]) == Sword) + != (SYNTAX (d == end1 ? *string2 : *d) == Sword)) + goto fail; + break; + + case wordbeg: + if (d == end2 /* Points to end */ + || (d == end1 && size2 == 0) /* Points to end */ + || SYNTAX (* (d == end1 ? string2 : d)) != Sword) /* Next char not a letter */ + goto fail; + if (d == string1 /* Points to first char */ + || SYNTAX (d[-1]) != Sword) /* prev char not letter */ + break; + goto fail; + + case wordend: + if (d == string1 /* Points to first char */ + || SYNTAX (d[-1]) != Sword) /* prev char not letter */ + goto fail; + if (d == end2 /* Points to end */ + || (d == end1 && size2 == 0) /* Points to end */ + || SYNTAX (d == end1 ? *string2 : *d) != Sword) /* Next char not a letter */ + break; + goto fail; + +#ifdef emacs + case before_dot: + if (((d - string2 <= (unsigned) size2) + ? d - bf_p2 : d - bf_p1) + <= point) + goto fail; + break; + + case at_dot: + if (((d - string2 <= (unsigned) size2) + ? d - bf_p2 : d - bf_p1) + == point) + goto fail; + break; + + case after_dot: + if (((d - string2 <= (unsigned) size2) + ? d - bf_p2 : d - bf_p1) + >= point) + goto fail; + break; + + case wordchar: + mcnt = (int) Sword; + goto matchsyntax; + + case syntaxspec: + mcnt = *p++; + matchsyntax: + PREFETCH; + if (SYNTAX (*d++) != (enum syntaxcode) mcnt) goto fail; + break; + + case notwordchar: + mcnt = (int) Sword; + goto matchnotsyntax; + + case notsyntaxspec: + mcnt = *p++; + matchnotsyntax: + PREFETCH; + if (SYNTAX (*d++) == (enum syntaxcode) mcnt) goto fail; + break; +#else + case wordchar: + PREFETCH; + if (SYNTAX (*d++) == 0) goto fail; + break; + + case notwordchar: + PREFETCH; + if (SYNTAX (*d++) != 0) goto fail; + break; +#endif /* not emacs */ + + case begbuf: + if (d == string1) /* Note, d cannot equal string2 */ + break; /* unless string1 == string2. */ + goto fail; + + case endbuf: + if (d == end2 || (d == end1 && size2 == 0)) + break; + goto fail; + + case exactn: + /* Match the next few pattern characters exactly. + mcnt is how many characters to match. */ + mcnt = *p++; + if (translate) + { + do + { + PREFETCH; + if (translate[*d++] != *p++) goto fail; + } + while (--mcnt); + } + else + { + do + { + PREFETCH; + if (*d++ != *p++) goto fail; + } + while (--mcnt); + } + break; + } + continue; /* Successfully matched one pattern command; keep matching */ + + /* Jump here if any matching operation fails. */ + fail: + if (stackp != stackb) + /* A restart point is known. Restart there and pop it. */ + { + if (!stackp[-2]) + { /* If innermost failure point is dormant, flush it and keep looking */ + stackp -= 2; + goto fail; + } + d = *--stackp; + p = *--stackp; + if (d >= string1 && d <= end1) + dend = end_match_1; + } + else break; /* Matching at this starting point really fails! */ + } + return -1; /* Failure to match */ +} + +static int +bcmp_translate (s1, s2, len, translate) + unsigned char *s1, *s2; + register int len; + unsigned char *translate; +{ + register unsigned char *p1 = s1, *p2 = s2; + while (len) + { + if (translate [*p1++] != translate [*p2++]) return 1; + len--; + } + return 0; +} + +/* Entry points compatible with bsd4.2 regex library */ + +#ifndef emacs + +static struct re_pattern_buffer re_comp_buf; + +char * +re_comp (s) + char *s; +{ + if (!s) + { + if (!re_comp_buf.buffer) + return "No previous regular expression"; + return 0; + } + + if (!re_comp_buf.buffer) + { + if (!(re_comp_buf.buffer = (char *) malloc (200))) + return "Memory exhausted"; + re_comp_buf.allocated = 200; + if (!(re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH))) + return "Memory exhausted"; + } + return re_compile_pattern (s, strlen (s), &re_comp_buf); +} + +int +re_exec (s) + char *s; +{ + int len = strlen (s); + return 0 <= re_search (&re_comp_buf, s, len, 0, len, 0); +} + +#endif /* emacs */ + +#ifdef test + +#include + +/* Indexed by a character, gives the upper case equivalent of the character */ + +static char upcase[0400] = + { 000, 001, 002, 003, 004, 005, 006, 007, + 010, 011, 012, 013, 014, 015, 016, 017, + 020, 021, 022, 023, 024, 025, 026, 027, + 030, 031, 032, 033, 034, 035, 036, 037, + 040, 041, 042, 043, 044, 045, 046, 047, + 050, 051, 052, 053, 054, 055, 056, 057, + 060, 061, 062, 063, 064, 065, 066, 067, + 070, 071, 072, 073, 074, 075, 076, 077, + 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107, + 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, + 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, + 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137, + 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107, + 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, + 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, + 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177, + 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, + 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, + 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, + 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, + 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, + 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, + 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, + 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, + 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, + 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, + 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377 + }; + +main (argc, argv) + int argc; + char **argv; +{ + char pat[80]; + struct re_pattern_buffer buf; + int i; + char c; + char fastmap[(1 << BYTEWIDTH)]; + + /* Allow a command argument to specify the style of syntax. */ + if (argc > 1) + obscure_syntax = atoi (argv[1]); + + buf.allocated = 40; + buf.buffer = (char *) malloc (buf.allocated); + buf.fastmap = fastmap; + buf.translate = upcase; + + while (1) + { + gets (pat); + + if (*pat) + { + re_compile_pattern (pat, strlen(pat), &buf); + + for (i = 0; i < buf.used; i++) + printchar (buf.buffer[i]); + + putchar ('\n'); + + printf ("%d allocated, %d used.\n", buf.allocated, buf.used); + + re_compile_fastmap (&buf); + printf ("Allowed by fastmap: "); + for (i = 0; i < (1 << BYTEWIDTH); i++) + if (fastmap[i]) printchar (i); + putchar ('\n'); + } + + gets (pat); /* Now read the string to match against */ + + i = re_match (&buf, pat, strlen (pat), 0, 0); + printf ("Match value %d.\n", i); + } +} + +#ifdef NOTDEF +print_buf (bufp) + struct re_pattern_buffer *bufp; +{ + int i; + + printf ("buf is :\n----------------\n"); + for (i = 0; i < bufp->used; i++) + printchar (bufp->buffer[i]); + + printf ("\n%d allocated, %d used.\n", bufp->allocated, bufp->used); + + printf ("Allowed by fastmap: "); + for (i = 0; i < (1 << BYTEWIDTH); i++) + if (bufp->fastmap[i]) + printchar (i); + printf ("\nAllowed by translate: "); + if (bufp->translate) + for (i = 0; i < (1 << BYTEWIDTH); i++) + if (bufp->translate[i]) + printchar (i); + printf ("\nfastmap is%s accurate\n", bufp->fastmap_accurate ? "" : "n't"); + printf ("can %s be null\n----------", bufp->can_be_null ? "" : "not"); +} +#endif + +printchar (c) + char c; +{ + if (c < 041 || c >= 0177) + { + putchar ('\\'); + putchar (((c >> 6) & 3) + '0'); + putchar (((c >> 3) & 7) + '0'); + putchar ((c & 7) + '0'); + } + else + putchar (c); +} + +error (string) + char *string; +{ + puts (string); + exit (1); +} + +#endif /* test */ diff --git a/gnu/usr.bin/gdb/regex.h b/gnu/usr.bin/gdb/regex.h new file mode 100644 index 0000000000..d0d8a82835 --- /dev/null +++ b/gnu/usr.bin/gdb/regex.h @@ -0,0 +1,185 @@ +/* Definitions for data structures callers pass the regex library. + Copyright (C) 1985, 1989 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! */ + + +/* Define number of parens for which we record the beginnings and ends. + This affects how much space the `struct re_registers' type takes up. */ +#ifndef RE_NREGS +#define RE_NREGS 10 +#endif + +/* These bits are used in the obscure_syntax variable to choose among + alternative regexp syntaxes. */ + +/* 1 means plain parentheses serve as grouping, and backslash + parentheses are needed for literal searching. + 0 means backslash-parentheses are grouping, and plain parentheses + are for literal searching. */ +#define RE_NO_BK_PARENS 1 + +/* 1 means plain | serves as the "or"-operator, and \| is a literal. + 0 means \| serves as the "or"-operator, and | is a literal. */ +#define RE_NO_BK_VBAR 2 + +/* 0 means plain + or ? serves as an operator, and \+, \? are literals. + 1 means \+, \? are operators and plain +, ? are literals. */ +#define RE_BK_PLUS_QM 4 + +/* 1 means | binds tighter than ^ or $. + 0 means the contrary. */ +#define RE_TIGHT_VBAR 8 + +/* 1 means treat \n as an _OR operator + 0 means treat it as a normal character */ +#define RE_NEWLINE_OR 16 + +/* 0 means that a special characters (such as *, ^, and $) always have + their special meaning regardless of the surrounding context. + 1 means that special characters may act as normal characters in some + contexts. Specifically, this applies to: + ^ - only special at the beginning, or after ( or | + $ - only special at the end, or before ) or | + *, +, ? - only special when not after the beginning, (, or | */ +#define RE_CONTEXT_INDEP_OPS 32 + +/* Now define combinations of bits for the standard possibilities. */ +#define RE_SYNTAX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_CONTEXT_INDEP_OPS) +#define RE_SYNTAX_EGREP (RE_SYNTAX_AWK | RE_NEWLINE_OR) +#define RE_SYNTAX_GREP (RE_BK_PLUS_QM | RE_NEWLINE_OR) +#define RE_SYNTAX_EMACS 0 + +/* This data structure is used to represent a compiled pattern. */ + +struct re_pattern_buffer + { + char *buffer; /* Space holding the compiled pattern commands. */ + int allocated; /* Size of space that buffer points to */ + int used; /* Length of portion of buffer actually occupied */ + char *fastmap; /* Pointer to fastmap, if any, or zero if none. */ + /* re_search uses the fastmap, if there is one, + to skip quickly over totally implausible characters */ + char *translate; /* Translate table to apply to all characters before comparing. + Or zero for no translation. + The translation is applied to a pattern when it is compiled + and to data when it is matched. */ + char fastmap_accurate; + /* Set to zero when a new pattern is stored, + set to one when the fastmap is updated from it. */ + char can_be_null; /* Set to one by compiling fastmap + if this pattern might match the null string. + It does not necessarily match the null string + in that case, but if this is zero, it cannot. + 2 as value means can match null string + but at end of range or before a character + listed in the fastmap. */ + }; + +/* Structure to store "register" contents data in. + + Pass the address of such a structure as an argument to re_match, etc., + if you want this information back. + + start[i] and end[i] record the string matched by \( ... \) grouping i, + for i from 1 to RE_NREGS - 1. + start[0] and end[0] record the entire string matched. */ + +struct re_registers + { + int start[RE_NREGS]; + int end[RE_NREGS]; + }; + +/* These are the command codes that appear in compiled regular expressions, one per byte. + Some command codes are followed by argument bytes. + A command code can specify any interpretation whatever for its arguments. + Zero-bytes may appear in the compiled regular expression. */ + +enum regexpcode + { + unused, + exactn, /* followed by one byte giving n, and then by n literal bytes */ + begline, /* fails unless at beginning of line */ + endline, /* fails unless at end of line */ + jump, /* followed by two bytes giving relative address to jump to */ + on_failure_jump, /* followed by two bytes giving relative address of place + to resume at in case of failure. */ + finalize_jump, /* Throw away latest failure point and then jump to address. */ + maybe_finalize_jump, /* Like jump but finalize if safe to do so. + This is used to jump back to the beginning + of a repeat. If the command that follows + this jump is clearly incompatible with the + one at the beginning of the repeat, such that + we can be sure that there is no use backtracking + out of repetitions already completed, + then we finalize. */ + dummy_failure_jump, /* jump, and push a dummy failure point. + This failure point will be thrown away + if an attempt is made to use it for a failure. + A + construct makes this before the first repeat. */ + anychar, /* matches any one character */ + charset, /* matches any one char belonging to specified set. + First following byte is # bitmap bytes. + Then come bytes for a bit-map saying which chars are in. + Bits in each byte are ordered low-bit-first. + A character is in the set if its bit is 1. + A character too large to have a bit in the map + is automatically not in the set */ + charset_not, /* similar but match any character that is NOT one of those specified */ + start_memory, /* starts remembering the text that is matched + and stores it in a memory register. + followed by one byte containing the register number. + Register numbers must be in the range 0 through NREGS. */ + stop_memory, /* stops remembering the text that is matched + and stores it in a memory register. + followed by one byte containing the register number. + Register numbers must be in the range 0 through NREGS. */ + duplicate, /* match a duplicate of something remembered. + Followed by one byte containing the index of the memory register. */ + before_dot, /* Succeeds if before dot */ + at_dot, /* Succeeds if at dot */ + after_dot, /* Succeeds if after dot */ + begbuf, /* Succeeds if at beginning of buffer */ + endbuf, /* Succeeds if at end of buffer */ + wordchar, /* Matches any word-constituent character */ + notwordchar, /* Matches any char that is not a word-constituent */ + wordbeg, /* Succeeds if at word beginning */ + wordend, /* Succeeds if at word end */ + wordbound, /* Succeeds if at a word boundary */ + notwordbound, /* Succeeds if not at a word boundary */ + syntaxspec, /* Matches any character whose syntax is specified. + followed by a byte which contains a syntax code, Sword or such like */ + notsyntaxspec /* Matches any character whose syntax differs from the specified. */ + }; + +extern char *re_compile_pattern (); +/* Is this really advertised? */ +extern void re_compile_fastmap (); +extern int re_search (), re_search_2 (); +extern int re_match (), re_match_2 (); + +/* 4.2 bsd compatibility (yuck) */ +extern char *re_comp (); +extern int re_exec (); + +#ifdef SYNTAX_TABLE +extern char *re_syntax_table; +#endif diff --git a/gnu/usr.bin/gdb/remote-sl.c b/gnu/usr.bin/gdb/remote-sl.c new file mode 100644 index 0000000000..4c72197f1a --- /dev/null +++ b/gnu/usr.bin/gdb/remote-sl.c @@ -0,0 +1,10 @@ +/* + * The binary remote protocol is still under development at LBL; + * the current version can't be released. + * Sorry, folks... + */ +int +sl_open() +{ + return -1; +} diff --git a/gnu/usr.bin/gdb/remote.c b/gnu/usr.bin/gdb/remote.c new file mode 100644 index 0000000000..59658a8867 --- /dev/null +++ b/gnu/usr.bin/gdb/remote.c @@ -0,0 +1,626 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson and Steven McCanne of Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Header: /home/cvs/386BSD/src/usr.bin/gdb/remote.c,v 1.1.1.1 1993/06/12 14:52:22 rgrimes Exp $; + */ + +#ifndef lint +static char sccsid[] = "@(#)remote.c 6.5 (Berkeley) 5/8/91"; +#endif /* not lint */ + +#include "param.h" + +#include +#include + +#include +#include +#include +#include + +#include "defs.h" +#include "frame.h" +#include "inferior.h" +#include "wait.h" + +#include "kgdb_proto.h" + +static FILE *kiodebug; +static int icache = 1; +extern int kernel_debugging; + +static int remote_cache_valid; +static int remote_instub; + +static void remote_signal(); +static void remote_debug(); +static void print_msg(); + +static int remote_mtu; +static int (*send_msg)(); +static int (*recv_msg)(); +static void (*closelink)(); + +static u_char *inbuffer; +static u_char *outbuffer; + +/* + * Statistics. + */ +static int remote_ierrs; +static int remote_oerrs; +static int remote_seqerrs; +static int remote_spurious; + +#define PUTCMD(cmd) m_xchg(cmd, (u_char *)0, 0, (u_char *)0, (int *)0) + +/* + * Send an outbound message to the remote machine and read the reply. + * Either or both message buffers may be NULL. + */ +static int +m_xchg(type, out, outlen, in, inlen) + int type; + u_char *out; + int outlen; + u_char *in; + int *inlen; +{ + register int err, (*send)() = send_msg, (*recv)() = recv_msg; + int ack; + static int seqbit = 0; + + if (!remote_instub) { + remote_instub = 1; + PUTCMD(KGDB_EXEC); + } + + seqbit ^= KGDB_SEQ; + while (1) { + err = (*send)(type | seqbit, out, outlen); + if (err) { + ++remote_oerrs; + if (kiodebug) + remote_debug("send error %d\n", err); + } + if (kiodebug) + print_msg(type | seqbit, out, outlen, 'O'); + + recv: + err = (*recv)(&ack, in, inlen); + if (err) { + ++remote_ierrs; + if (kiodebug) + remote_debug("recv error %d\n", err); + remote_cache_valid = 0; + } else if (kiodebug) + print_msg(ack, in, inlen ? *inlen : 0, 'I'); + + if (err) + continue; + + if ((ack & KGDB_ACK) == 0 || KGDB_CMD(ack) != KGDB_CMD(type)) { + ++remote_spurious; + continue; + } + if ((ack & KGDB_SEQ) ^ seqbit) { + ++remote_seqerrs; + goto recv; + } + return ack; + } +} + +/* + * Wait for the specified message type. Discard anything else. + * (this is used by 'remote-signal' to help us resync with other side.) + */ +static void +m_recv(type, in, inlen) + int type; + u_char *in; + int *inlen; +{ + int reply, err; + + while (1) { + err = (*recv_msg)(&reply, in, inlen); + if (err) { + ++remote_ierrs; + if (kiodebug) + remote_debug("recv error %d\n", err); + } else if (kiodebug) + print_msg(reply, in, inlen ? *inlen : 0, 'I'); + + if (KGDB_CMD(reply) == type) + return; + ++remote_spurious; + } +} + +/* + * Send a message. Do not wait for *any* response from the other side. + * Some other thread of control will pick up the ack that will be generated. + */ +static void +m_send(type, buf, len) + int type; + u_char *buf; + int len; +{ + int err; + + if (!remote_instub) { + remote_instub = 1; + PUTCMD(KGDB_EXEC); + } + + err = (*send_msg)(type, buf, len); + if (err) { + ++remote_ierrs; + if (kiodebug) + remote_debug("[send error %d] ", err); + } + if (kiodebug) + print_msg(type, buf, len, 'O'); +} + +/* + * Open a connection to a remote debugger. + * NAME is the filename used for communication. + */ +void +remote_open(name, from_tty) + char *name; + int from_tty; +{ + int bufsize; + + remote_debugging = 0; + if (sl_open(name, &send_msg, &recv_msg, &closelink, &remote_mtu, + &bufsize)) + return; + if (from_tty) + printf("Remote debugging using %s\n", name); + remote_debugging = 1; + + remote_cache_valid = 0; + + inbuffer = (u_char *)malloc(bufsize); + outbuffer = (u_char *)malloc(bufsize); + + remote_signal(); + + remote_ierrs = 0; + remote_oerrs = 0; + remote_spurious = 0; +} + +/* + * Close the open connection to the remote debugger. Use this when you want + * to detach and do something else with your gdb. + */ +void +remote_close(from_tty) + int from_tty; +{ + if (!remote_debugging) + error("remote debugging not enabled"); + + remote_debugging = 0; + /* + * Take remote machine out of debug mode. + */ + (void)PUTCMD(KGDB_KILL); + (*closelink)(); + if (from_tty) + printf("Ending remote debugging\n"); + + free((char *)inbuffer); + free((char *)outbuffer); +} + +/* + * Tell the remote machine to resume. + */ +int +remote_resume(step, signal) + int step, signal; +{ + if (!step) { + (void)PUTCMD(KGDB_CONT); + remote_instub = 0; + } else { +#ifdef NO_SINGLE_STEP + single_step(0); +#else + (void)PUTCMD(KGDB_STEP); +#endif + } +} + +/* + * Wait until the remote machine stops, then return, storing status in STATUS + * just as `wait' would. + */ +int +remote_wait(status) + WAITTYPE *status; +{ + int len; + + WSETEXIT((*status), 0); + /* + * When the machine stops, it will send us a KGDB_SIGNAL message, + * so we wait for one of these. + */ + m_recv(KGDB_SIGNAL, inbuffer, &len); + WSETSTOP((*status), inbuffer[0]); +} + +/* + * Register context as of last remote_fetch_registers(). + */ +static char reg_cache[REGISTER_BYTES]; + +/* + * Read the remote registers into the block REGS. + */ +void +remote_fetch_registers(regs) + char *regs; +{ + int regno, len, rlen, ack; + u_char *cp, *ep; + + regno = -1; + do { + outbuffer[0] = regno + 1; + ack = m_xchg(remote_cache_valid ? + KGDB_REG_R|KGDB_DELTA : KGDB_REG_R, + outbuffer, 1, inbuffer, &len); + cp = inbuffer; + ep = cp + len; + while (cp < ep) { + regno = *cp++; + rlen = REGISTER_RAW_SIZE(regno); + bcopy((char *)cp, + ®_cache[REGISTER_BYTE(regno)], rlen); + cp += rlen; + } + } while (ack & KGDB_MORE); + + remote_cache_valid = 1; + bcopy(reg_cache, regs, REGISTER_BYTES); +} + +/* + * Store the remote registers from the contents of the block REGS. + */ +void +remote_store_registers(regs) + char *regs; +{ + u_char *cp, *ep; + int regno, off, rlen; + + cp = outbuffer; + ep = cp + remote_mtu; + + for (regno = 0; regno < NUM_REGS; ++regno) { + off = REGISTER_BYTE(regno); + rlen = REGISTER_RAW_SIZE(regno); + if (!remote_cache_valid || + bcmp(®s[off], ®_cache[off], rlen) != 0) { + if (cp + rlen + 1 >= ep) { + (void)m_xchg(KGDB_REG_W, + outbuffer, cp - outbuffer, + (u_char *)0, (int *)0); + cp = outbuffer; + } + *cp++ = regno; + bcopy(®s[off], cp, rlen); + cp += rlen; + } + } + if (cp != outbuffer) + (void)m_xchg(KGDB_REG_W, outbuffer, cp - outbuffer, + (u_char *)0, (int *)0); + bcopy(regs, reg_cache, REGISTER_BYTES); +} + +/* + * Store a chunk of memory into the remote host. + * 'remote_addr' is the address in the remote memory space. + * 'cp' is the address of the buffer in our space, and 'len' is + * the number of bytes. Returns an errno status. + */ +int +remote_write_inferior_memory(remote_addr, cp, len) + CORE_ADDR remote_addr; + u_char *cp; + int len; +{ + int cnt; + + while (len > 0) { + cnt = min(len, remote_mtu - 4); + bcopy((char *)&remote_addr, outbuffer, 4); + bcopy(cp, outbuffer + 4, cnt); + (void)m_xchg(KGDB_MEM_W, outbuffer, cnt + 4, inbuffer, &len); + + if (inbuffer[0]) + return inbuffer[0]; + + remote_addr += cnt; + cp += cnt; + len -= cnt; + } + return 0; +} + +/* + * Read memory data directly from the remote machine. + * 'remote_addr' is the address in the remote memory space. + * 'cp' is the address of the buffer in our space, and 'len' is + * the number of bytes. Returns an errno status. + */ +static int +remote_read_memory(remote_addr, cp, len) + CORE_ADDR remote_addr; + u_char *cp; + int len; +{ + int cnt, inlen; + + while (len > 0) { + cnt = min(len, remote_mtu - 1); + outbuffer[0] = cnt; + bcopy((char *)&remote_addr, (char *)&outbuffer[1], 4); + + (void)m_xchg(KGDB_MEM_R, outbuffer, 5, inbuffer, &inlen); + + if (inbuffer[0] != 0) + return inbuffer[0]; + + if (cnt != inlen - 1) + /* XXX */ + error("remote_read_memory() request botched"); + + bcopy((char *)&inbuffer[1], (char *)cp, cnt); + + remote_addr += cnt; + cp += cnt; + len -= cnt; + } + return 0; +} + +int +remote_read_inferior_memory(remote_addr, cp, len) + CORE_ADDR remote_addr; + char *cp; + int len; +{ + int stat = 0; + + if (icache) { + extern CORE_ADDR text_start, text_end; + CORE_ADDR xferend = remote_addr + len; + + if (remote_addr < text_end && text_start < xferend) { + /* + * at least part of this xfer is in the text + * space -- xfer the overlap from the exec file. + */ + if (remote_addr >= text_start && xferend < text_end) + return (xfer_core_file(remote_addr, cp, len)); + if (remote_addr >= text_start) { + int i = text_end - remote_addr; + + if (stat = xfer_core_file(remote_addr, cp, i)) + return (stat); + remote_addr += i; + cp += i; + len -= i; + } else if (xferend <= text_end) { + int i = xferend - text_start; + + len = text_start - remote_addr; + if (stat = xfer_core_file(text_start, + cp + len, i)) + return (stat); + } + } + } + return remote_read_memory(remote_addr, cp, len); +} + +/* + * Signal the remote machine. The remote end might be idle or it might + * already be in debug mode -- we need to handle both case. Thus, we use + * the framing character as the wakeup byte, and send a SIGNAL packet. + * If the remote host is idle, the framing character will wake it up. + * If it is in the kgdb stub, then we will get a SIGNAL reply. + */ +static void +remote_signal() +{ + if (!remote_debugging) + printf("Remote debugging not enabled.\n"); + else { + remote_instub = 0; + m_send(KGDB_SIGNAL, (u_char *)0, 0); + } +} + +static void +remote_signal_command() +{ + extern int stop_after_attach; + + if (!remote_debugging) + error("Not debugging remote."); + remote_cache_valid = 0; + remote_signal(); + restart_remote(); +} + +/* + * Print a message for debugging. + */ +static void +print_msg(type, buf, len, dir) + int type; + u_char *buf; + int len; + int dir; +{ + int i; + char *s; + + switch (KGDB_CMD(type)) { + case KGDB_MEM_R: s = "memr"; break; + case KGDB_MEM_W: s = "memw"; break; + case KGDB_REG_R: s = "regr"; break; + case KGDB_REG_W: s = "regw"; break; + case KGDB_CONT: s = "cont"; break; + case KGDB_STEP: s = "step"; break; + case KGDB_KILL: s = "kill"; break; + case KGDB_SIGNAL: s = "sig "; break; + case KGDB_EXEC: s = "exec"; break; + default: s = "unk "; break; + } + remote_debug("%c %c%c%c%c %s (%02x): ", dir, + (type & KGDB_ACK) ? 'A' : '.', + (type & KGDB_DELTA) ? 'D' : '.', + (type & KGDB_MORE) ? 'M' : '.', + (type & KGDB_SEQ) ? '-' : '+', + s, type); + if (buf) + for (i = 0; i < len; ++i) + remote_debug("%02x", buf[i]); + remote_debug("\n"); +} + +static void +set_remote_text_refs_command(arg, from_tty) + char *arg; + int from_tty; +{ + icache = !parse_binary_operation("set remote-text-refs", arg); +} + +static void +remote_debug_command(arg, from_tty) + char *arg; + int from_tty; +{ + char *name; + + if (kiodebug != 0 && kiodebug != stderr) + (void)fclose(kiodebug); + + if (arg == 0) { + kiodebug = 0; + printf("Remote debugging off.\n"); + return; + } + if (arg[0] == '-') { + kiodebug = stderr; + name = "stderr"; + } else { + kiodebug = fopen(arg, "w"); + if (kiodebug == 0) { + printf("Cannot open '%s'.\n", arg); + return; + } + name = arg; + } + printf("Remote debugging output routed to %s.\n", name); +} + +/* ARGSUSED */ +static void +remote_info(arg, from_tty) + char *arg; + int from_tty; +{ + printf("Using %s for text references.\n", + icache? "local executable" : "remote"); + printf("Protocol debugging is %s.\n", kiodebug? "on" : "off"); + printf("%d spurious input messages.\n", remote_spurious); + printf("%d input errors; %d output errors; %d sequence errors.\n", + remote_ierrs, remote_oerrs, remote_seqerrs); +} + +/* VARARGS */ +static void +remote_debug(va_alist) + va_dcl +{ + register char *cp; + va_list ap; + + va_start(ap); + cp = va_arg(ap, char *); + (void)vfprintf(kiodebug, cp, ap); + va_end(ap); + fflush(kiodebug); +} + +extern struct cmd_list_element *setlist; + +void +_initialize_remote() +{ + add_com("remote-signal", class_run, remote_signal_command, + "If remote debugging, send interrupt signal to remote."); + add_cmd("remote-text-refs", class_support, + set_remote_text_refs_command, +"Enable/disable use of local executable for text segment references.\n\ +If on, all memory read/writes go to remote.\n\ +If off, text segment reads use the local executable.", + &setlist); + + add_com("remote-debug", class_run, remote_debug_command, +"With a file name argument, enables output of remote protocol debugging\n\ +messages to said file. If file is `-', stderr is used.\n\ +With no argument, remote debugging is disabled."); + + add_info("remote", remote_info, + "Show current settings of remote debugging options."); +} + diff --git a/gnu/usr.bin/gdb/source.c b/gnu/usr.bin/gdb/source.c new file mode 100644 index 0000000000..d9ae20f6c2 --- /dev/null +++ b/gnu/usr.bin/gdb/source.c @@ -0,0 +1,1166 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)source.c 6.3 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* List lines of source files for GDB, the GNU debugger. + Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include "defs.h" +#include "symtab.h" +#include "param.h" + +#ifdef USG +#include +#include +#endif + +#include +#include +#include + +/* Path of directories to search for source files. + Same format as the PATH environment variable's value. */ + +static char *source_path; + +/* Symtab of default file for listing lines of. */ + +struct symtab *current_source_symtab; + +/* Default next line to list. */ + +int current_source_line; + +/* Line number of last line printed. Default for various commands. + current_source_line is usually, but not always, the same as this. */ + +static int last_line_listed; + +/* First line number listed by last listing command. */ + +static int first_line_listed; + + +struct symtab *psymtab_to_symtab (); + +/* Set the source file default for the "list" command, specifying a + symtab. Sigh. Behaivior specification: If it is called with a + non-zero argument, that is the symtab to select. If it is not, + first lookup "main"; if it exists, use the symtab and line it + defines. If not, take the last symtab in the symtab_list (if it + exists) or the last symtab in the psytab_list (if *it* exists). If + none of this works, report an error. */ + +void +select_source_symtab (s) + register struct symtab *s; +{ + struct symtabs_and_lines sals; + struct symtab_and_line sal; + struct partial_symtab *ps, *cs_pst; + + if (s) + { + current_source_symtab = s; + current_source_line = 1; + return; + } + + /* Make the default place to list be the function `main' + if one exists. */ + if (lookup_symbol ("main", 0, VAR_NAMESPACE, 0)) + { + sals = decode_line_spec ("main", 1); + sal = sals.sals[0]; + free (sals.sals); + current_source_symtab = sal.symtab; + current_source_line = max (sal.line - 9, 1); + return; + } + + /* All right; find the last file in the symtab list (ignoring .h's). */ + + if (s = symtab_list) + { + do + { + char *name = s->filename; + int len = strlen (name); + if (! (len > 2 && !strcmp (&name[len - 2], ".h"))) + current_source_symtab = s; + s = s->next; + } + while (s); + current_source_line = 1; + } + else if (partial_symtab_list) + { + ps = partial_symtab_list; + while (ps) + { + char *name = ps->filename; + int len = strlen (name); + if (! (len > 2 && !strcmp (&name[len - 2], ".h"))) + cs_pst = ps; + ps = ps->next; + } + if (cs_pst) + if (cs_pst->readin) + fatal ("Internal: select_source_symtab: readin pst found and no symtabs."); + else + current_source_symtab = psymtab_to_symtab (cs_pst); + else + current_source_symtab = 0; + current_source_line = 1; + } +} + +static void +directories_info () +{ + printf ("Source directories searched: %s\n", source_path); +} + +void +init_source_path () +{ + register struct symtab *s; + + source_path = savestring (current_directory, strlen (current_directory)); + + /* Forget what we learned about line positions in source files; + must check again now since files may be found in + a different directory now. */ + for (s = symtab_list; s; s = s->next) + if (s->line_charpos != 0) + { + free (s->line_charpos); + s->line_charpos = 0; + } +} + +void +directory_command (dirname, from_tty) + char *dirname; + int from_tty; +{ + char *old = source_path; + + dont_repeat (); + + if (dirname == 0) + { + if (query ("Reinitialize source path to %s? ", current_directory)) + { + init_source_path (); + free (old); + } + } + else + { + dirname = tilde_expand (dirname); + make_cleanup (free, dirname); + + do + { + extern char *index (); + char *name = dirname; + register char *p; + struct stat st; + + { + char *colon = index (name, ':'); + char *space = index (name, ' '); + char *tab = index (name, '\t'); + if (colon == 0 && space == 0 && tab == 0) + p = dirname = name + strlen (name); + else + { + p = 0; + if (colon != 0 && (p == 0 || colon < p)) + p = colon; + if (space != 0 && (p == 0 || space < p)) + p = space; + if (tab != 0 && (p == 0 || tab < p)) + p = tab; + dirname = p + 1; + while (*dirname == ':' || *dirname == ' ' || *dirname == '\t') + ++dirname; + } + } + + if (p[-1] == '/') + /* Sigh. "foo/" => "foo" */ + --p; + *p = '\0'; + + while (p[-1] == '.') + { + if (p - name == 1) + { + /* "." => getwd (). */ + name = current_directory; + goto append; + } + else if (p[-2] == '/') + { + if (p - name == 2) + { + /* "/." => "/". */ + *--p = '\0'; + goto append; + } + else + { + /* "...foo/." => "...foo". */ + p -= 2; + *p = '\0'; + continue; + } + } + else + break; + } + + if (*name != '/') + name = concat (current_directory, "/", name); + else + name = savestring (name, p - name); + make_cleanup (free, name); + + if (stat (name, &st) < 0) + perror_with_name (name); + if ((st.st_mode & S_IFMT) != S_IFDIR) + error ("%s is not a directory.", name); + + append: + { + register unsigned int len = strlen (name); + + p = source_path; + while (1) + { + if (!strncmp (p, name, len) + && (p[len] == '\0' || p[len] == ':')) + { + if (from_tty) + printf ("\"%s\" is already in the source path.\n", name); + break; + } + p = index (p, ':'); + if (p != 0) + ++p; + else + break; + } + if (p == 0) + { + source_path = concat (old, ":", name); + free (old); + old = source_path; + } + } + } while (*dirname != '\0'); + if (from_tty) + directories_info (); + } +} + +/* Open a file named STRING, searching path PATH (dir names sep by colons) + using mode MODE and protection bits PROT in the calls to open. + If TRY_CWD_FIRST, try to open ./STRING before searching PATH. + (ie pretend the first element of PATH is ".") + If FILENAMED_OPENED is non-null, set it to a newly allocated string naming + the actual file opened (this string will always start with a "/" + + If a file is found, return the descriptor. + Otherwise, return -1, with errno set for the last name we tried to open. */ + +/* >>>> This should only allow files of certain types, + >>>> eg executable, non-directory */ +int +openp (path, try_cwd_first, string, mode, prot, filename_opened) + char *path; + int try_cwd_first; + char *string; + int mode; + int prot; + char **filename_opened; +{ + register int fd; + register char *filename; + register char *p, *p1; + register int len; + + if (!path) + path = "."; + + /* ./foo => foo */ + while (string[0] == '.' && string[1] == '/') + string += 2; + + if (try_cwd_first || string[0] == '/') + { + filename = string; + fd = open (filename, mode, prot); + if (fd >= 0 || string[0] == '/') + goto done; + } + + filename = (char *) alloca (strlen (path) + strlen (string) + 2); + fd = -1; + for (p = path; p; p = p1 ? p1 + 1 : 0) + { + p1 = (char *) index (p, ':'); + if (p1) + len = p1 - p; + else + len = strlen (p); + + strncpy (filename, p, len); + filename[len] = 0; + strcat (filename, "/"); + strcat (filename, string); + + fd = open (filename, mode, prot); + if (fd >= 0) break; + } + + done: + if (filename_opened) + if (fd < 0) + *filename_opened = (char *) 0; + else if (filename[0] == '/') + *filename_opened = savestring (filename, strlen (filename)); + else + { + *filename_opened = concat (current_directory, "/", filename); + } + + return fd; +} + +/* Create and initialize the table S->line_charpos that records + the positions of the lines in the source file, which is assumed + to be open on descriptor DESC. + All set S->nlines to the number of such lines. */ + +static void +find_source_lines (s, desc) + struct symtab *s; + int desc; +{ + struct stat st; + register char *data, *p, *end; + int nlines = 0; + int lines_allocated = 1000; + int *line_charpos = (int *) xmalloc (lines_allocated * sizeof (int)); + extern int exec_mtime; + + if (fstat (desc, &st) < 0) + perror_with_name (s->filename); + if (get_exec_file (0) != 0 && exec_mtime < st.st_mtime) + printf ("Source file is more recent than executable.\n"); + + data = (char *) alloca (st.st_size); + if (myread (desc, data, st.st_size) < 0) + perror_with_name (s->filename); + end = data + st.st_size; + p = data; + line_charpos[0] = 0; + nlines = 1; + while (p != end) + { + if (*p++ == '\n' + /* A newline at the end does not start a new line. */ + && p != end) + { + if (nlines == lines_allocated) + { + lines_allocated *= 2; + line_charpos = (int *) xrealloc (line_charpos, + sizeof (int) * lines_allocated); + } + line_charpos[nlines++] = p - data; + } + } + s->nlines = nlines; + s->line_charpos = (int *) xrealloc (line_charpos, nlines * sizeof (int)); +} + +/* Return the character position of a line LINE in symtab S. + Return 0 if anything is invalid. */ + +int +source_line_charpos (s, line) + struct symtab *s; + int line; +{ + if (!s) return 0; + if (!s->line_charpos || line <= 0) return 0; + if (line > s->nlines) + line = s->nlines; + return s->line_charpos[line - 1]; +} + +/* Return the line number of character position POS in symtab S. */ + +int +source_charpos_line (s, chr) + register struct symtab *s; + register int chr; +{ + register int line = 0; + register int *lnp; + + if (s == 0 || s->line_charpos == 0) return 0; + lnp = s->line_charpos; + /* Files are usually short, so sequential search is Ok */ + while (line < s->nlines && *lnp <= chr) + { + line++; + lnp++; + } + if (line >= s->nlines) + line = s->nlines; + return line; +} + +/* Get full pathname and line number positions for a symtab. + Return nonzero if line numbers may have changed. + Set *FULLNAME to actual name of the file as found by `openp', + or to 0 if the file is not found. */ + +int +get_filename_and_charpos (s, line, fullname) + struct symtab *s; + int line; + char **fullname; +{ + register int desc, linenums_changed = 0; + + desc = openp (source_path, 0, s->filename, O_RDONLY, 0, &s->fullname); + if (desc < 0) + { + if (fullname) + *fullname = NULL; + return 0; + } + if (fullname) + *fullname = s->fullname; + if (s->line_charpos == 0) linenums_changed = 1; + if (linenums_changed) find_source_lines (s, desc); + close (desc); + return linenums_changed; +} + +/* Print text describing the full name of the source file S + and the line number LINE and its corresponding character position. + The text starts with two Ctrl-z so that the Emacs-GDB interface + can easily find it. + + MID_STATEMENT is nonzero if the PC is not at the beginning of that line. + + Return 1 if successful, 0 if could not find the file. */ + +int +identify_source_line (s, line, mid_statement) + struct symtab *s; + int line; + int mid_statement; +{ + if (s->line_charpos == 0) + get_filename_and_charpos (s, line, 0); + if (s->fullname == 0) + return 0; + printf ("\032\032%s:%d:%d:%s:0x%x\n", s->fullname, + line, s->line_charpos[line - 1], + mid_statement ? "middle" : "beg", + get_frame_pc (get_current_frame())); + current_source_line = line; + first_line_listed = line; + last_line_listed = line; + current_source_symtab = s; + return 1; +} + +/* Print source lines from the file of symtab S, + starting with line number LINE and stopping before line number STOPLINE. */ + +void +print_source_lines (s, line, stopline, noerror) + struct symtab *s; + int line, stopline; + int noerror; +{ + register int c; + register int desc; + register FILE *stream; + int nlines = stopline - line; + + desc = openp (source_path, 0, s->filename, O_RDONLY, 0, &s->fullname); + if (desc < 0) + { + extern int errno; + if (noerror && line + 1 == stopline) + { + /* can't find the file - tell user where we are anyway */ + current_source_symtab = s; + current_source_line = line; + first_line_listed = line; + last_line_listed = line; + printf_filtered ("%d\t(%s)\n", current_source_line++, s->filename); + } + else + { + if (! noerror) + perror_with_name (s->filename); + print_sys_errmsg (s->filename, errno); + } + return; + } + + if (s->line_charpos == 0) + find_source_lines (s, desc); + + if (line < 1 || line > s->nlines) + { + close (desc); + error ("Line number %d out of range; %s has %d lines.", + line, s->filename, s->nlines); + } + + if (lseek (desc, s->line_charpos[line - 1], 0) < 0) + { + close (desc); + perror_with_name (s->filename); + } + + current_source_symtab = s; + current_source_line = line; + first_line_listed = line; + + stream = fdopen (desc, "r"); + clearerr (stream); + + while (nlines-- > 0) + { + c = fgetc (stream); + if (c == EOF) break; + last_line_listed = current_source_line; + printf_filtered ("%d\t", current_source_line++); + do + { + if (c < 040 && c != '\t' && c != '\n') + printf_filtered ("^%c", c + 0100); + else if (c == 0177) + printf_filtered ("^?"); + else + printf_filtered ("%c", c); + } while (c != '\n' && (c = fgetc (stream)) >= 0); + } + + fclose (stream); +} + + + +/* + C++ + Print a list of files and line numbers which a user may choose from + in order to list a function which was specified ambiguously + (as with `list classname::overloadedfuncname', for example). + The vector in SALS provides the filenames and line numbers. + */ +static void +ambiguous_line_spec (sals) + struct symtabs_and_lines *sals; +{ + int i; + + for (i = 0; i < sals->nelts; ++i) + printf("file: \"%s\", line number: %d\n", + sals->sals[i].symtab->filename, sals->sals[i].line); +} + + +static void +file_command(arg, from_tty) + char *arg; + int from_tty; +{ + struct symtabs_and_lines sals; + struct symtab_and_line sal; + struct symbol *sym; + char *arg1; + int linenum_beg = 0; + char *p; + + if (symtab_list == 0 && partial_symtab_list == 0) + error ("No symbol table is loaded. Use the \"symbol-file\" command."); + + /* Pull in a current source symtab if necessary */ + if (arg == 0 || arg[0] == 0) { + if (current_source_symtab == 0) + select_source_symtab(0); + else + printf("%s\n", current_source_symtab->filename); + return; + } + arg1 = arg; + sals = decode_line_1 (&arg1, 0, 0, 0); + + if (! sals.nelts) + return; /* C++ */ + + if (sals.nelts > 1) + { + ambiguous_line_spec (&sals); + free (sals.sals); + return; + } + + sal = sals.sals[0]; + free (sals.sals); + + /* Record whether the BEG arg is all digits. */ + + for (p = arg; p != arg1 && *p >= '0' && *p <= '9'; ++p) + ; + linenum_beg = (p == arg1); + + /* if line was specified by address, + print exactly which line, and which file. + In this case, sal.symtab == 0 means address is outside + of all known source files, not that user failed to give a filename. */ + if (*arg == '*') + { + if (sal.symtab == 0) + error ("No source file for address 0x%x.", sal.pc); + sym = find_pc_function (sal.pc); + if (sym) + printf ("0x%x is in %s (%s, line %d).\n", + sal.pc, SYMBOL_NAME (sym), sal.symtab->filename, sal.line); + else + printf ("0x%x is in %s, line %d.\n", + sal.pc, sal.symtab->filename, sal.line); + } + + /* If line was not specified by just a line number, + and it does not imply a symtab, it must be an undebuggable symbol + which means no source code. */ + + if (sal.symtab == 0) + { + if (! linenum_beg) + error ("No line number known for %s.", arg); + else + error ("No default source file yet. Do \"help list\"."); + } + else + { + current_source_symtab = sal.symtab; + current_source_line = sal.line; + first_line_listed = sal.line; + } +} + +#define PUSH_STACK_SIZE 32 +static struct { + struct symtab *symtab; + int line; +} push_stack[PUSH_STACK_SIZE]; + +static unsigned int push_stack_ptr; + +static void +push_to_file_command (arg, from_tty) + char *arg; + int from_tty; +{ + struct symtab *cursym = current_source_symtab; + int curline = current_source_line; + register unsigned int i; + + file_command(arg, from_tty); + + /* if we got back, command was successful */ + i = push_stack_ptr; + push_stack[i].symtab = cursym; + push_stack[i].line = curline; + push_stack_ptr = (i + 1) & (PUSH_STACK_SIZE - 1); +} + +static void +pop_file_command (arg, from_tty) + char *arg; + int from_tty; +{ + register unsigned int i = push_stack_ptr; + + /* if there's something on the stack, pop it & clear the slot. */ + i = (i + (PUSH_STACK_SIZE - 1)) & (PUSH_STACK_SIZE - 1); + if (push_stack[i].symtab) { + current_source_symtab = push_stack[i].symtab; + first_line_listed = current_source_line = push_stack[i].line; + push_stack[i].symtab = NULL; + push_stack_ptr = i; + } +} + + +static void +list_command (arg, from_tty) + char *arg; + int from_tty; +{ + struct symtabs_and_lines sals, sals_end; + struct symtab_and_line sal, sal_end; + struct symbol *sym; + char *arg1; + int no_end = 1; + int dummy_end = 0; + int dummy_beg = 0; + int linenum_beg = 0; + char *p; + + if (symtab_list == 0 && partial_symtab_list == 0) + error ("No symbol table is loaded. Use the \"symbol-file\" command."); + + /* Pull in a current source symtab if necessary */ + if (current_source_symtab == 0 && + (arg == 0 || arg[0] == '+' || arg[0] == '-')) + select_source_symtab (0); + + /* "l" or "l +" lists next ten lines. */ + + if (arg == 0 || !strcmp (arg, "+")) + { + if (current_source_symtab == 0) + error ("No default source file yet. Do \"help list\"."); + print_source_lines (current_source_symtab, current_source_line, + current_source_line + 10, 0); + return; + } + + /* "l -" lists previous ten lines, the ones before the ten just listed. */ + if (!strcmp (arg, "-")) + { + if (current_source_symtab == 0) + error ("No default source file yet. Do \"help list\"."); + print_source_lines (current_source_symtab, + max (first_line_listed - 10, 1), + first_line_listed, 0); + return; + } + + /* Now if there is only one argument, decode it in SAL + and set NO_END. + If there are two arguments, decode them in SAL and SAL_END + and clear NO_END; however, if one of the arguments is blank, + set DUMMY_BEG or DUMMY_END to record that fact. */ + + arg1 = arg; + if (*arg1 == ',') + dummy_beg = 1; + else + { + sals = decode_line_1 (&arg1, 0, 0, 0); + + if (! sals.nelts) return; /* C++ */ + if (sals.nelts > 1) + { + ambiguous_line_spec (&sals); + free (sals.sals); + return; + } + + sal = sals.sals[0]; + free (sals.sals); + } + + /* Record whether the BEG arg is all digits. */ + + for (p = arg; p != arg1 && *p >= '0' && *p <= '9'; p++); + linenum_beg = (p == arg1); + + while (*arg1 == ' ' || *arg1 == '\t') + arg1++; + if (*arg1 == ',') + { + no_end = 0; + arg1++; + while (*arg1 == ' ' || *arg1 == '\t') + arg1++; + if (*arg1 == 0) + dummy_end = 1; + else + { + if (dummy_beg) + sals_end = decode_line_1 (&arg1, 0, 0, 0); + else + sals_end = decode_line_1 (&arg1, 0, sal.symtab, sal.line); + if (sals_end.nelts == 0) + return; + if (sals_end.nelts > 1) + { + ambiguous_line_spec (&sals_end); + free (sals_end.sals); + return; + } + sal_end = sals_end.sals[0]; + free (sals_end.sals); + } + } + + if (*arg1) + error ("Junk at end of line specification."); + + if (!no_end && !dummy_beg && !dummy_end + && sal.symtab != sal_end.symtab) + error ("Specified start and end are in different files."); + if (dummy_beg && dummy_end) + error ("Two empty args do not say what lines to list."); + + /* if line was specified by address, + first print exactly which line, and which file. + In this case, sal.symtab == 0 means address is outside + of all known source files, not that user failed to give a filename. */ + if (*arg == '*') + { + if (sal.symtab == 0) + error ("No source file for address 0x%x.", sal.pc); + sym = find_pc_function (sal.pc); + if (sym) + printf ("0x%x is in %s (%s, line %d).\n", + sal.pc, SYMBOL_NAME (sym), sal.symtab->filename, sal.line); + else + printf ("0x%x is in %s, line %d.\n", + sal.pc, sal.symtab->filename, sal.line); + } + + /* If line was not specified by just a line number, + and it does not imply a symtab, it must be an undebuggable symbol + which means no source code. */ + + if (! linenum_beg && sal.symtab == 0) + error ("No line number known for %s.", arg); + + /* If this command is repeated with RET, + turn it into the no-arg variant. */ + + if (from_tty) + *arg = 0; + + if (dummy_beg && sal_end.symtab == 0) + error ("No default source file yet. Do \"help list\"."); + if (dummy_beg) + print_source_lines (sal_end.symtab, max (sal_end.line - 9, 1), + sal_end.line + 1, 0); + else if (sal.symtab == 0) + error ("No default source file yet. Do \"help list\"."); + else if (no_end) + print_source_lines (sal.symtab, max (sal.line - 5, 1), sal.line + 5, 0); + else + print_source_lines (sal.symtab, sal.line, + dummy_end ? sal.line + 10 : sal_end.line + 1, + 0); +} + +/* Print info on range of pc's in a specified line. */ + +static void +line_info (arg, from_tty) + char *arg; + int from_tty; +{ + struct symtabs_and_lines sals; + struct symtab_and_line sal; + int start_pc, end_pc; + int i; + + if (arg == 0) + { + sal.symtab = current_source_symtab; + sal.line = last_line_listed; + sals.nelts = 1; + sals.sals = (struct symtab_and_line *) + xmalloc (sizeof (struct symtab_and_line)); + sals.sals[0] = sal; + } + else + { + sals = decode_line_spec_1 (arg, 0); + + /* If this command is repeated with RET, + turn it into the no-arg variant. */ + if (from_tty) + *arg = 0; + } + + /* C++ More than one line may have been specified, as when the user + specifies an overloaded function name. Print info on them all. */ + for (i = 0; i < sals.nelts; i++) + { + sal = sals.sals[i]; + + if (sal.symtab == 0) + error ("No source file specified."); + + if (sal.line > 0 + && find_line_pc_range (sal.symtab, sal.line, &start_pc, &end_pc)) + { + if (start_pc == end_pc) + printf ("Line %d of \"%s\" is at pc 0x%x but contains no code.\n", + sal.line, sal.symtab->filename, start_pc); + else + printf ("Line %d of \"%s\" starts at pc 0x%x and ends at 0x%x.\n", + sal.line, sal.symtab->filename, start_pc, end_pc); + /* x/i should display this line's code. */ + set_next_address (start_pc); + /* Repeating "info line" should do the following line. */ + last_line_listed = sal.line + 1; + } + else + printf ("Line number %d is out of range for \"%s\".\n", + sal.line, sal.symtab->filename); + } +} + +/* Commands to search the source file for a regexp. */ + +static void +forward_search_command (regex, from_tty) + char *regex; +{ + register int c; + register int desc; + register FILE *stream; + int line = last_line_listed + 1; + char *msg; + + msg = (char *) re_comp (regex); + if (msg) + error (msg); + + if (current_source_symtab == 0) + select_source_symtab (0); + + /* Search from last_line_listed+1 in current_source_symtab */ + + desc = openp (source_path, 0, current_source_symtab->filename, + O_RDONLY, 0, ¤t_source_symtab->fullname); + if (desc < 0) + perror_with_name (current_source_symtab->filename); + + if (current_source_symtab->line_charpos == 0) + find_source_lines (current_source_symtab, desc); + + if (line < 1 || line > current_source_symtab->nlines) + { + close (desc); + error ("Expression not found"); + } + + if (lseek (desc, current_source_symtab->line_charpos[line - 1], 0) < 0) + { + close (desc); + perror_with_name (current_source_symtab->filename); + } + + stream = fdopen (desc, "r"); + clearerr (stream); + while (1) { + char buf[4096]; /* Should be reasonable??? */ + register char *p = buf; + + c = fgetc (stream); + if (c == EOF) + break; + do { + *p++ = c; + } while (c != '\n' && (c = fgetc (stream)) >= 0); + + /* we now have a source line in buf, null terminate and match */ + *p = 0; + if (re_exec (buf) > 0) + { + /* Match! */ + fclose (stream); + print_source_lines (current_source_symtab, + line, line+1, 0); + current_source_line = max (line - 5, 1); + return; + } + line++; + } + + printf ("Expression not found\n"); + fclose (stream); +} + +static void +reverse_search_command (regex, from_tty) + char *regex; +{ + register int c; + register int desc; + register FILE *stream; + int line = last_line_listed - 1; + char *msg; + + msg = (char *) re_comp (regex); + if (msg) + error (msg); + + if (current_source_symtab == 0) + select_source_symtab (0); + + /* Search from last_line_listed-1 in current_source_symtab */ + + desc = openp (source_path, 0, current_source_symtab->filename, + O_RDONLY, 0, ¤t_source_symtab->fullname); + if (desc < 0) + perror_with_name (current_source_symtab->filename); + + if (current_source_symtab->line_charpos == 0) + find_source_lines (current_source_symtab, desc); + + if (line < 1 || line > current_source_symtab->nlines) + { + close (desc); + error ("Expression not found"); + } + + if (lseek (desc, current_source_symtab->line_charpos[line - 1], 0) < 0) + { + close (desc); + perror_with_name (current_source_symtab->filename); + } + + stream = fdopen (desc, "r"); + clearerr (stream); + while (1) + { + char buf[4096]; /* Should be reasonable??? */ + register char *p = buf; + + c = fgetc (stream); + if (c == EOF) + break; + do { + *p++ = c; + } while (c != '\n' && (c = fgetc (stream)) >= 0); + + /* We now have a source line in buf; null terminate and match. */ + *p = 0; + if (re_exec (buf) > 0) + { + /* Match! */ + fclose (stream); + print_source_lines (current_source_symtab, + line, line+1, 0); + current_source_line = max (line - 5, 1); + return; + } + line--; + if (fseek (stream, current_source_symtab->line_charpos[line - 1], 0) < 0) + { + fclose (stream); + perror_with_name (current_source_symtab->filename); + } + } + + printf ("Expression not found\n"); + fclose (stream); + return; +} + +void +_initialize_source () +{ + current_source_symtab = 0; + init_source_path (); + + add_com ("directory", class_files, directory_command, + "Add directory DIR to end of search path for source files.\n\ +With no argument, reset the search path to just the working directory\n\ +and forget cached info on line positions in source files."); + + add_info ("directories", directories_info, + "Current search path for finding source files."); + + add_info ("line", line_info, + "Core addresses of the code for a source line.\n\ +Line can be specified as\n\ + LINENUM, to list around that line in current file,\n\ + FILE:LINENUM, to list around that line in that file,\n\ + FUNCTION, to list around beginning of that function,\n\ + FILE:FUNCTION, to distinguish among like-named static functions.\n\ +Default is to describe the last source line that was listed.\n\n\ +This sets the default address for \"x\" to the line's first instruction\n\ +so that \"x/i\" suffices to start examining the machine code.\n\ +The address is also stored as the value of \"$_\"."); + + add_com ("forward-search", class_files, forward_search_command, + "Search for regular expression (see regex(3)) from last line listed."); + add_com_alias ("search", "forward-search", class_files, 0); + + add_com ("reverse-search", class_files, reverse_search_command, + "Search backward for regular expression (see regex(3)) from last line listed."); + + add_com ("list", class_files, list_command, + "List specified function or line.\n\ +With no argument, lists ten more lines after or around previous listing.\n\ +\"list -\" lists the ten lines before a previous ten-line listing.\n\ +One argument specifies a line, and ten lines are listed around that line.\n\ +Two arguments with comma between specify starting and ending lines to list.\n\ +Lines can be specified in these ways:\n\ + LINENUM, to list around that line in current file,\n\ + FILE:LINENUM, to list around that line in that file,\n\ + FUNCTION, to list around beginning of that function,\n\ + FILE:FUNCTION, to distinguish among like-named static functions.\n\ + *ADDRESS, to list around the line containing that address.\n\ +With two args if one is empty it stands for ten lines away from the other arg."); + add_com ("file", class_files, file_command, + "Select current file, function and line for display or list.\n\ +Specification can have the form:\n\ + LINENUM, to select that line in current file,\n\ + FILE:LINENUM, to select that line in that file,\n\ + FUNCTION, to select beginning of that function,\n\ + FILE:FUNCTION, to distinguish among like-named static functions.\n\ + *ADDRESS, to select the line containing that address."); + add_com ("push-to-file", class_files, push_to_file_command, + "Like \"file\" command but remembers current file & line on a stack.\n\ +Can later return to current file with \"pop-file\" command.\n\ +Up to 32 file positions can be pushed on stack."); + add_com ("pop-file", class_files, pop_file_command, + "Pops back to file position saved by most recent \"push-to-file\".\n\ +If everything has been popped from stack, command does nothing."); +} + diff --git a/gnu/usr.bin/gcc1/cc1/stab.def b/gnu/usr.bin/gdb/stab.def similarity index 100% rename from gnu/usr.bin/gcc1/cc1/stab.def rename to gnu/usr.bin/gdb/stab.def diff --git a/gnu/usr.bin/gdb/stack.c b/gnu/usr.bin/gdb/stack.c new file mode 100644 index 0000000000..91218aae36 --- /dev/null +++ b/gnu/usr.bin/gdb/stack.c @@ -0,0 +1,960 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)stack.c 6.3 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Print and select stack frames for GDB, the GNU debugger. + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* modified by rjc Thu Nov 1 16:46:57 1990, fixed return_command so that + it can return values, it still has problems when running on pmax, + cannot write register 65 */ + +#include + +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "frame.h" +#include "value.h" + + +/* Thie "selected" stack frame is used by default for local and arg access. + May be zero, for no selected frame. */ + +FRAME selected_frame; + +/* Level of the selected frame: + 0 for innermost, 1 for its caller, ... + or -1 for frame specified by address with no defined level. */ + +int selected_frame_level; + +/* Nonzero means print the full filename and linenumber + when a frame is printed, and do so in a format programs can parse. */ + +int frame_file_full_name = 0; + +static void select_calling_frame (); + +void print_frame_info (); + +/* Print a stack frame briefly. FRAME should be the frame id + and LEVEL should be its level in the stack (or -1 for level not defined). + This prints the level, the function executing, the arguments, + and the file name and line number. + If the pc is not at the beginning of the source line, + the actual pc is printed at the beginning. + + If SOURCE is 1, print the source line as well. + If SOURCE is -1, print ONLY the source line. */ + +static void +print_stack_frame (frame, level, source) + FRAME frame; + int level; + int source; +{ + struct frame_info *fi; + + fi = get_frame_info (frame); + + print_frame_info (fi, level, source, 1); +} + +/* Flag which will indicate when the frame has been changed + by and "up" or "down" command. */ +static int frame_changed; + +void +print_frame_info (fi, level, source, args) + struct frame_info *fi; + register int level; + int source; + int args; +{ + struct symtab_and_line sal; + struct symbol *func; + register char *funname = 0; + int numargs; + struct partial_symtab *pst; + + /* Don't give very much information if we haven't readin the + symbol table yet. */ + pst = find_pc_psymtab (fi->pc); + if (pst && !pst->readin) + { + /* Abbreviated information. */ + char *fname; + + if (!find_pc_partial_function (fi->pc, &fname, 0)) + fname = "??"; + + printf_filtered ("#%-2d ", level); + printf_filtered ("0x%x in ", fi->pc); + + fputs_demangled(fname, stdout, -1); + fputs_filtered(" (...)\n", stdout); + + return; + } + + sal = find_pc_line (fi->pc, fi->next_frame); + func = find_pc_function (fi->pc); + if (func) + { + /* In certain pathological cases, the symtabs give the wrong + function (when we are in the first function in a file which + is compiled without debugging symbols, the previous function + is compiled with debugging symbols, and the "foo.o" symbol + that is supposed to tell us where the file with debugging symbols + ends has been truncated by ar because it is longer than 15 + characters). + + So look in the misc_function_vector as well, and if it comes + up with a larger address for the function use that instead. + I don't think this can ever cause any problems; + there shouldn't be any + misc_function_vector symbols in the middle of a function. */ + int misc_index = find_pc_misc_function (fi->pc); + if (misc_index >= 0 + && (misc_function_vector[misc_index].address + > BLOCK_START (SYMBOL_BLOCK_VALUE (func)))) + { + /* In this case we have no way of knowing the source file + and line number, so don't print them. */ + sal.symtab = 0; + /* We also don't know anything about the function besides + its address and name. */ + func = 0; + funname = misc_function_vector[misc_index].name; + } + else + funname = SYMBOL_NAME (func); + } + else + { + register int misc_index = find_pc_misc_function (fi->pc); + if (misc_index >= 0) + funname = misc_function_vector[misc_index].name; + } + + if (frame_changed || source >= 0 || !sal.symtab) + { + if (level >= 0) + printf_filtered ("#%-2d ", level); + else if (frame_changed) + printf ("#%-2d ", 0); + if (fi->pc != sal.pc || !sal.symtab) + printf_filtered ("0x%x in ", fi->pc); + fputs_demangled(funname ? funname : "??", stdout, -1); + printf_filtered(" ("); + if (args) + { + if (func) + numargs = -1; + else + FRAME_NUM_ARGS (numargs, fi); + + print_frame_args (func, fi, numargs, stdout); + } + printf_filtered (")"); + if (sal.symtab) + printf_filtered (" (%s line %d)", sal.symtab->filename, sal.line); + printf_filtered ("\n"); + } + + if ((frame_changed || source != 0) && sal.symtab) + { + int done = 0; + int mid_statement = source < 0 && fi->pc != sal.pc; + if (frame_file_full_name) + done = identify_source_line (sal.symtab, sal.line, mid_statement); + if (!done) + { + if (mid_statement) + printf_filtered ("0x%x\t", fi->pc); + print_source_lines (sal.symtab, sal.line, sal.line + 1, 1); + } + current_source_line = max (sal.line - 5, 1); + } + frame_changed = 0; + if (source != 0) + set_default_breakpoint (1, fi->pc, sal.symtab, sal.line); + + fflush (stdout); +} + +/* Call here to print info on selected frame, after a trap. */ + +void +print_sel_frame (just_source) + int just_source; +{ + print_stack_frame (selected_frame, -1, just_source ? -1 : 1); +} + +/* Print info on the selected frame, including level number + but not source. */ + +void +print_selected_frame () +{ + print_stack_frame (selected_frame, selected_frame_level, 0); +} + +void flush_cached_frames (); + +#ifdef FRAME_SPECIFICATION_DYADIC +extern FRAME setup_arbitrary_frame (); +#endif + +/* + * Read a frame specification in whatever the appropriate format is. + */ +static FRAME +parse_frame_specification (frame_exp) + char *frame_exp; +{ + int numargs = 0; + int arg1, arg2; + + if (frame_exp) + { + char *addr_string, *p; + struct cleanup *tmp_cleanup; + struct frame_info *fci; + + while (*frame_exp == ' ') frame_exp++; + for (p = frame_exp; *p && *p != ' '; p++) + ; + + if (*frame_exp) + { + numargs = 1; + addr_string = savestring(frame_exp, p - frame_exp); + + { + tmp_cleanup = make_cleanup (free, addr_string); + arg1 = parse_and_eval_address (addr_string); + do_cleanups (tmp_cleanup); + } + + while (*p == ' ') p++; + + if (*p) + { + numargs = 2; + arg2 = parse_and_eval_address (p); + } + } + } + + switch (numargs) + { + case 0: + return selected_frame; + /* NOTREACHED */ + case 1: + { + int level = arg1; + FRAME fid = find_relative_frame (get_current_frame (), &level); + FRAME tfid; + + if (level == 0) + /* find_relative_frame was successful */ + return fid; + + /* If (s)he specifies the frame with an address, he deserves what + (s)he gets. Still, give the highest one that matches. */ + + for (fid = get_current_frame (); + fid && FRAME_FP (fid) != arg1; + fid = get_prev_frame (fid)) + ; + + if (fid) + while ((tfid = get_prev_frame (fid)) && + (FRAME_FP (tfid) == arg1)) + fid = tfid; + +#ifdef FRAME_SPECIFICATION_DYADIC + if (!fid) + error ("Incorrect number of args in frame specification"); + + return fid; +#else + return create_new_frame (arg1, 0); +#endif + } + /* NOTREACHED */ + case 2: + /* Must be addresses */ +#ifndef FRAME_SPECIFICATION_DYADIC + error ("Incorrect number of args in frame specification"); +#else + return setup_arbitrary_frame (arg1, arg2); +#endif + /* NOTREACHED */ + } + fatal ("Internal: Error in parsing in parse_frame_specification"); + /* NOTREACHED */ +} + +/* FRAME_ARGS_ADDRESS_CORRECT is just like FRAME_ARGS_ADDRESS except + that if it is unsure about the answer, it returns Frame_unknown + instead of guessing (this happens on the VAX, for example). + + On most machines, we never have to guess about the args address, + so FRAME_ARGS_ADDRESS{,_CORRECT} are the same. */ +#if !defined (FRAME_ARGS_ADDRESS_CORRECT) +#define FRAME_ARGS_ADDRESS_CORRECT FRAME_ARGS_ADDRESS +#endif + +/* Print verbosely the selected frame or the frame at address ADDR. + This means absolutely all information in the frame is printed. */ + +static void +frame_info (addr_exp) + char *addr_exp; +{ + FRAME frame; + struct frame_info *fi; + struct frame_saved_regs fsr; + struct symtab_and_line sal; + struct symbol *func; + FRAME calling_frame; + int i, count; + char *funname = 0; + + if (!(have_inferior_p () || have_core_file_p ())) + error ("No inferior or core file."); + + frame = parse_frame_specification (addr_exp); + if (!frame) + error ("Invalid frame specified."); + + fi = get_frame_info (frame); + get_frame_saved_regs (fi, &fsr); + sal = find_pc_line (fi->pc, fi->next_frame); + func = get_frame_function (frame); + if (func) + funname = SYMBOL_NAME (func); + else + { + register int misc_index = find_pc_misc_function (fi->pc); + if (misc_index >= 0) + funname = misc_function_vector[misc_index].name; + } + calling_frame = get_prev_frame (frame); + + if (!addr_exp && selected_frame_level >= 0) + printf ("Stack level %d, frame at 0x%x:\n pc = 0x%x", + selected_frame_level, FRAME_FP(frame), fi->pc); + else + printf ("Stack frame at 0x%x:\n pc = 0x%x", + FRAME_FP(frame), fi->pc); + + if (funname) + printf (" in %s", funname); + if (sal.symtab) + printf (" (%s line %d)", sal.symtab->filename, sal.line); + printf ("; saved pc 0x%x\n", FRAME_SAVED_PC (frame)); + if (calling_frame) + printf (" called by frame at 0x%x", FRAME_FP (calling_frame)); + if (fi->next_frame && calling_frame) + printf (","); + if (fi->next_frame) + printf (" caller of frame at 0x%x", fi->next_frame); + if (fi->next_frame || calling_frame) + printf ("\n"); + + { + /* Address of the argument list for this frame, or Frame_unknown. */ + CORE_ADDR arg_list = FRAME_ARGS_ADDRESS_CORRECT (fi); + /* Number of args for this frame, or -1 if unknown. */ + int numargs; + + if (arg_list != Frame_unknown) + { + printf (" Arglist at 0x%x,", arg_list); + + FRAME_NUM_ARGS (numargs, fi); + if (numargs < 0) + printf (" args: "); + else if (numargs == 0) + printf (" no args."); + else if (numargs == 1) + printf (" 1 arg: "); + else + printf (" %d args: ", numargs); + print_frame_args (func, fi, numargs, stdout); + printf ("\n"); + } + } + + /* The sp is special; what's returned isn't the save address, but + actually the value of the previous frame's sp. */ + printf (" Previous frame's sp is 0x%x\n", fsr.regs[SP_REGNUM]); + count = 0; + for (i = 0; i < NUM_REGS; i++) + if (fsr.regs[i] && i != SP_REGNUM) + { + if (count % 4 != 0) + printf (", "); + else + { + if (count == 0) + printf (" Saved registers:"); + printf ("\n "); + } + printf ("%s at 0x%x", reg_names[i], fsr.regs[i]); + count++; + } + if (count) + printf ("\n"); +} + +#if 0 +/* Set a limit on the number of frames printed by default in a + backtrace. */ + +static int backtrace_limit; + +static void +set_backtrace_limit_command (count_exp, from_tty) + char *count_exp; + int from_tty; +{ + int count = parse_and_eval_address (count_exp); + + if (count < 0) + error ("Negative argument not meaningful as backtrace limit."); + + backtrace_limit = count; +} + +static void +backtrace_limit_info (arg, from_tty) + char *arg; + int from_tty; +{ + if (arg) + error ("\"Info backtrace-limit\" takes no arguments."); + + printf ("Backtrace limit: %d.\n", backtrace_limit); +} +#endif + +/* Print briefly all stack frames or just the innermost COUNT frames. */ + +static void +backtrace_command (count_exp) + char *count_exp; +{ + struct frame_info *fi; + register int count; + register FRAME frame; + register int i; + register FRAME trailing; + register int trailing_level; + + /* The following code must do two things. First, it must + set the variable TRAILING to the frame from which we should start + printing. Second, it must set the variable count to the number + of frames which we should print, or -1 if all of them. */ + trailing = get_current_frame (); + trailing_level = 0; + if (count_exp) + { + count = parse_and_eval_address (count_exp); + if (count < 0) + { + FRAME current; + + count = -count; + + current = trailing; + while (current && count--) + current = get_prev_frame (current); + + /* Will stop when CURRENT reaches the top of the stack. TRAILING + will be COUNT below it. */ + while (current) + { + trailing = get_prev_frame (trailing); + current = get_prev_frame (current); + trailing_level++; + } + + count = -1; + } + } + else + count = -1; + + for (i = 0, frame = trailing; + frame && count--; + i++, frame = get_prev_frame (frame)) + { + QUIT; + fi = get_frame_info (frame); + print_frame_info (fi, trailing_level + i, 0, 1); + } + + /* If we've stopped before the end, mention that. */ + if (frame) + printf_filtered ("(More stack frames follow...)\n"); +} + +/* Print the local variables of a block B active in FRAME. + Return 1 if any variables were printed; 0 otherwise. */ + +static int +print_block_frame_locals (b, frame, stream) + struct block *b; + register FRAME frame; + register FILE *stream; +{ + int nsyms; + register int i; + register struct symbol *sym; + register int values_printed = 0; + + nsyms = BLOCK_NSYMS (b); + + for (i = 0; i < nsyms; i++) + { + sym = BLOCK_SYM (b, i); + if (SYMBOL_CLASS (sym) == LOC_LOCAL + || SYMBOL_CLASS (sym) == LOC_REGISTER + || SYMBOL_CLASS (sym) == LOC_STATIC) + { + values_printed = 1; + fputs_filtered (SYMBOL_NAME (sym), stream); + fputs_filtered (" = ", stream); + print_variable_value (sym, frame, stream); + fprintf_filtered (stream, "\n"); + fflush (stream); + } + } + return values_printed; +} + +/* Print on STREAM all the local variables in frame FRAME, + including all the blocks active in that frame + at its current pc. + + Returns 1 if the job was done, + or 0 if nothing was printed because we have no info + on the function running in FRAME. */ + +static int +print_frame_local_vars (frame, stream) + register FRAME frame; + register FILE *stream; +{ + register struct block *block = get_frame_block (frame); + register int values_printed = 0; + + if (block == 0) + { + fprintf_filtered (stream, "No symbol table info available.\n"); + fflush (stream); + return 0; + } + + while (block != 0) + { + if (print_block_frame_locals (block, frame, stream)) + values_printed = 1; + /* After handling the function's top-level block, stop. + Don't continue to its superblock, the block of + per-file symbols. */ + if (BLOCK_FUNCTION (block)) + break; + block = BLOCK_SUPERBLOCK (block); + } + + if (!values_printed) + { + fprintf_filtered (stream, "No locals.\n"); + fflush (stream); + } + + return 1; +} + +static void +locals_info () +{ + if (!have_inferior_p () && !have_core_file_p ()) + error ("No inferior or core file."); + + print_frame_local_vars (selected_frame, stdout); +} + +static int +print_frame_arg_vars (frame, stream) + register FRAME frame; + register FILE *stream; +{ + struct symbol *func = get_frame_function (frame); + register struct block *b; + int nsyms; + register int i; + register struct symbol *sym; + register int values_printed = 0; + + if (func == 0) + { + fprintf_filtered (stream, "No symbol table info available.\n"); + fflush (stream); + return 0; + } + + b = SYMBOL_BLOCK_VALUE (func); + nsyms = BLOCK_NSYMS (b); + + for (i = 0; i < nsyms; i++) + { + sym = BLOCK_SYM (b, i); + if (SYMBOL_CLASS (sym) == LOC_ARG + || SYMBOL_CLASS (sym) == LOC_REF_ARG + || SYMBOL_CLASS (sym) == LOC_REGPARM) + { + values_printed = 1; + fputs_filtered (SYMBOL_NAME (sym), stream); + fputs_filtered (" = ", stream); + print_variable_value (sym, frame, stream); + fprintf_filtered (stream, "\n"); + fflush (stream); + } + } + + if (!values_printed) + { + fprintf_filtered (stream, "No arguments.\n"); + fflush (stream); + } + + return 1; +} + +static void +args_info () +{ + if (!have_inferior_p () && !have_core_file_p ()) + error ("No inferior or core file."); + print_frame_arg_vars (selected_frame, stdout); +} + +/* Select frame FRAME, and note that its stack level is LEVEL. + LEVEL may be -1 if an actual level number is not known. */ + +void +select_frame (frame, level) + FRAME frame; + int level; +{ + selected_frame = frame; + selected_frame_level = level; + /* Ensure that symbols for this frame are readin. */ + if (frame) + find_pc_symtab (get_frame_info (frame)->pc); +} + +/* Store the selected frame and its level into *FRAMEP and *LEVELP. */ + +void +record_selected_frame (frameaddrp, levelp) + FRAME_ADDR *frameaddrp; + int *levelp; +{ + *frameaddrp = FRAME_FP (selected_frame); + *levelp = selected_frame_level; +} + +/* Return the symbol-block in which the selected frame is executing. + Can return zero under various legitimate circumstances. */ + +struct block * +get_selected_block () +{ + if (!have_inferior_p () && !have_core_file_p ()) + return 0; + + if (!selected_frame) + return get_current_block (); + return get_frame_block (selected_frame); +} + +/* Find a frame a certain number of levels away from FRAME. + LEVEL_OFFSET_PTR points to an int containing the number of levels. + Positive means go to earlier frames (up); negative, the reverse. + The int that contains the number of levels is counted toward + zero as the frames for those levels are found. + If the top or bottom frame is reached, that frame is returned, + but the final value of *LEVEL_OFFSET_PTR is nonzero and indicates + how much farther the original request asked to go. */ + +FRAME +find_relative_frame (frame, level_offset_ptr) + register FRAME frame; + register int* level_offset_ptr; +{ + register FRAME prev; + register FRAME frame1, frame2; + + /* Going up is simple: just do get_prev_frame enough times + or until initial frame is reached. */ + while (*level_offset_ptr > 0) + { + prev = get_prev_frame (frame); + if (prev == 0) + break; + (*level_offset_ptr)--; + frame = prev; + } + /* Going down could be done by iterating get_frame_info to + find the next frame, but that would be quadratic + since get_frame_info must scan all the way from the current frame. + The following algorithm is linear. */ + if (*level_offset_ptr < 0) + { + /* First put frame1 at innermost frame + and frame2 N levels up from there. */ + frame1 = get_current_frame (); + frame2 = frame1; + while (*level_offset_ptr < 0 && frame2 != frame) + { + frame2 = get_prev_frame (frame2); + (*level_offset_ptr) ++; + } + /* Then slide frame1 and frame2 up in synchrony + and when frame2 reaches our starting point + frame1 must be N levels down from there. */ + while (frame2 != frame) + { + frame1 = get_prev_frame (frame1); + frame2 = get_prev_frame (frame2); + } + return frame1; + } + return frame; +} + +/* The "frame" command. With no arg, print selected frame briefly. + With arg LEVEL_EXP, select the frame at level LEVEL if it is a + valid level. Otherwise, treat level_exp as an address expression + and print it. See parse_frame_specification for more info on proper + frame expressions. */ + +static void +frame_command (level_exp, from_tty) + char *level_exp; + int from_tty; +{ + register FRAME frame, frame1; + unsigned int level = 0; + + if (!have_inferior_p () && ! have_core_file_p ()) + error ("No inferior or core file."); + + frame = parse_frame_specification (level_exp); + + for (frame1 = get_prev_frame (0); + frame1 && frame1 != frame; + frame1 = get_prev_frame (frame1)) + level++; + + if (!frame1) + level = 0; + + frame_changed = level; + select_frame (frame, level); + + if (!from_tty) + return; + + print_stack_frame (selected_frame, selected_frame_level, 1); +} + +/* Select the frame up one or COUNT stack levels + from the previously selected frame, and print it briefly. */ + +static void +up_command (count_exp) + char *count_exp; +{ + register FRAME frame; + int count = 1, count1; + if (count_exp) + count = parse_and_eval_address (count_exp); + count1 = count; + + if (!have_inferior_p () && !have_core_file_p ()) + error ("No inferior or core file."); + + frame = find_relative_frame (selected_frame, &count1); + if (count1 != 0 && count_exp == 0) + error ("Initial frame selected; you cannot go up."); + select_frame (frame, selected_frame_level + count - count1); + + print_stack_frame (selected_frame, selected_frame_level, 1); + frame_changed++; +} + +/* Select the frame down one or COUNT stack levels + from the previously selected frame, and print it briefly. */ + +static void +down_command (count_exp) + char *count_exp; +{ + register FRAME frame; + int count = -1, count1; + if (count_exp) + count = - parse_and_eval_address (count_exp); + count1 = count; + + frame = find_relative_frame (selected_frame, &count1); + if (count1 != 0 && count_exp == 0) + error ("Bottom (i.e., innermost) frame selected; you cannot go down."); + select_frame (frame, selected_frame_level + count - count1); + + print_stack_frame (selected_frame, selected_frame_level, 1); + frame_changed--; +} + +static void +return_command (retval_exp, from_tty) + char *retval_exp; + int from_tty; +{ + value return_value; + struct symbol *thisfun = get_frame_function (selected_frame); + FRAME_ADDR selected_frame_addr = FRAME_FP (selected_frame); + + /* If interactive, require confirmation. */ + + if (from_tty) + { + if (thisfun != 0) + { + if (!query ("Make %s return now? ", SYMBOL_NAME (thisfun))) + error ("Not confirmed."); + } + else + if (!query ("Make selected stack frame return now? ")) + error ("Not confirmed."); + } + + /* Do the real work. Pop until the specified frame is current. We + use this method because the selected_frame is not valid after + a POP_FRAME. Note that this will not work if the selected frame + shares it's fp with another frame. */ + + while (selected_frame_addr != FRAME_FP (get_current_frame())) + POP_FRAME; + + /* get the return value while still in this frame */ + if (retval_exp) + return_value = parse_and_eval (retval_exp); + + /* Then pop that frame. */ + POP_FRAME; + + /* Store the return value if there was one */ + + if (retval_exp) + set_return_value (return_value); + + /* If interactive, print the frame that is now current. */ + + if (from_tty) + frame_command ("0", 1); +} + +extern struct cmd_list_element *setlist; + +void +_initialize_stack () +{ +#if 0 + backtrace_limit = 30; +#endif + + add_com ("return", class_stack, return_command, + "Make selected stack frame return to its caller.\n\ +Control remains in the debugger, but when you continue\n\ +execution will resume in the frame above the one now selected.\n\ +If an argument is given, it is an expression for the value to return."); + + add_com ("up", class_stack, up_command, + "Select and print stack frame that called this one.\n\ +An argument says how many frames up to go."); + + add_com ("down", class_stack, down_command, + "Select and print stack frame called by this one.\n\ +An argument says how many frames down to go."); + add_com_alias ("do", "down", class_stack, 1); + + add_com ("frame", class_stack, frame_command, + "Select and print a stack frame.\n\ +With no argument, print the selected stack frame. (See also \"info frame\").\n\ +An argument specifies the frame to select.\n\ +It can be a stack frame number or the address of the frame.\n\ +With argument, nothing is printed if input is coming from\n\ +a command file or a user-defined command."); + + add_com_alias ("f", "frame", class_stack, 1); + + add_com ("backtrace", class_stack, backtrace_command, + "Print backtrace of all stack frames, or innermost COUNT frames.\n\ +With a negative argument, print outermost -COUNT frames."); + add_com_alias ("bt", "backtrace", class_stack, 0); + add_com_alias ("where", "backtrace", class_alias, 0); + add_info ("stack", backtrace_command, + "Backtrace of the stack, or innermost COUNT frames."); + add_info_alias ("s", "stack", 1); + add_info ("frame", frame_info, + "All about selected stack frame, or frame at ADDR."); + add_info_alias ("f", "frame", 1); + add_info ("locals", locals_info, + "Local variables of current stack frame."); + add_info ("args", args_info, + "Argument variables of current stack frame."); + +#if 0 + add_cmd ("backtrace-limit", class_stack, set_backtrace_limit_command, + "Specify maximum number of frames for \"backtrace\" to print by default.", + &setlist); + add_info ("backtrace-limit", backtrace_limit_info, + "The maximum number of frames for \"backtrace\" to print by default."); +#endif +} + diff --git a/gnu/usr.bin/gdb/symmisc.c b/gnu/usr.bin/gdb/symmisc.c new file mode 100644 index 0000000000..bb4eb50674 --- /dev/null +++ b/gnu/usr.bin/gdb/symmisc.c @@ -0,0 +1,584 @@ +/* Do various things to symbol tables (other than lookup)), for GDB. + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include "defs.h" +#include "symtab.h" + +#include +#include + +static void free_symtab (); + + +/* Free all the symtabs that are currently installed, + and all storage associated with them. + Leaves us in a consistent state with no symtabs installed. */ + +void +free_all_symtabs () +{ + register struct symtab *s, *snext; + + /* All values will be invalid because their types will be! */ + + clear_value_history (); + clear_displays (); + clear_internalvars (); + clear_breakpoints (); + set_default_breakpoint (0, 0, 0, 0); + + current_source_symtab = 0; + + for (s = symtab_list; s; s = snext) + { + snext = s->next; + free_symtab (s); + } + symtab_list = 0; + obstack_free (symbol_obstack, 0); + obstack_init (symbol_obstack); + + if (misc_function_vector) + free (misc_function_vector); + misc_function_count = 0; + misc_function_vector = 0; +} + +/* Free a struct block <- B and all the symbols defined in that block. */ + +static void +free_symtab_block (b) + struct block *b; +{ + register int i, n; + n = BLOCK_NSYMS (b); + for (i = 0; i < n; i++) + { + free (SYMBOL_NAME (BLOCK_SYM (b, i))); + free (BLOCK_SYM (b, i)); + } + free (b); +} + +/* Free all the storage associated with the struct symtab <- S. + Note that some symtabs have contents malloc'ed structure by structure, + while some have contents that all live inside one big block of memory, + and some share the contents of another symbol table and so you should + not free the contents on their behalf (except sometimes the linetable, + which maybe per symtab even when the rest is not). + It is s->free_code that says which alternative to use. */ + +static void +free_symtab (s) + register struct symtab *s; +{ + register int i, n; + register struct blockvector *bv; + register struct type *type; + register struct typevector *tv; + + switch (s->free_code) + { + case free_nothing: + /* All the contents are part of a big block of memory + and some other symtab is in charge of freeing that block. + Therefore, do nothing. */ + break; + + case free_contents: + /* Here all the contents were malloc'ed structure by structure + and must be freed that way. */ + /* First free the blocks (and their symbols. */ + bv = BLOCKVECTOR (s); + n = BLOCKVECTOR_NBLOCKS (bv); + for (i = 0; i < n; i++) + free_symtab_block (BLOCKVECTOR_BLOCK (bv, i)); + /* Free the blockvector itself. */ + free (bv); + /* Free the type vector. */ + tv = TYPEVECTOR (s); + free (tv); + /* Also free the linetable. */ + + case free_linetable: + /* Everything will be freed either by our `free_ptr' + or by some other symbatb, except for our linetable. + Free that now. */ + free (LINETABLE (s)); + break; + } + + /* If there is a single block of memory to free, free it. */ + if (s->free_ptr) + free (s->free_ptr); + + if (s->line_charpos) + free (s->line_charpos); + free (s->filename); + free (s); +} + +/* Convert a raw symbol-segment to a struct symtab, + and relocate its internal pointers so that it is valid. */ + +/* This is how to relocate one pointer, given a name for it. + Works independent of the type of object pointed to. */ +#define RELOCATE(slot) (slot ? (* (char **) &slot += relocation) : 0) + +/* This is the inverse of RELOCATE. We use it when storing + a core address into a slot that has yet to be relocated. */ +#define UNRELOCATE(slot) (slot ? (* (char **) &slot -= relocation) : 0) + +/* During the process of relocation, this holds the amount to relocate by + (the address of the file's symtab data, in core in the debugger). */ +static int relocation; + +#define CORE_RELOCATE(slot) \ + ((slot) += (((slot) < data_start) ? text_relocation \ + : ((slot) < bss_start) ? data_relocation : bss_relocation)) + +#define TEXT_RELOCATE(slot) ((slot) += text_relocation) + +/* Relocation amounts for addresses in the program's core image. */ +static int text_relocation, data_relocation, bss_relocation; + +/* Boundaries that divide program core addresses into text, data and bss; + used to determine which relocation amount to use. */ +static int data_start, bss_start; + +static void relocate_typevector (); +static void relocate_blockvector (); +static void relocate_type (); +static void relocate_block (); +static void relocate_symbol (); +static void relocate_source (); + +/* Relocate a file's symseg so that all the pointers are valid C pointers. + Value is a `struct symtab'; but it is not suitable for direct + insertion into the `symtab_list' because it describes several files. */ + +static struct symtab * +relocate_symtab (root) + struct symbol_root *root; +{ + struct symtab *sp = (struct symtab *) xmalloc (sizeof (struct symtab)); + bzero (sp, sizeof (struct symtab)); + + relocation = (int) root; + text_relocation = root->textrel; + data_relocation = root->datarel; + bss_relocation = root->bssrel; + data_start = root->databeg; + bss_start = root->bssbeg; + + sp->filename = root->filename; + sp->ldsymoff = root->ldsymoff; + sp->language = root->language; + sp->compilation = root->compilation; + sp->version = root->version; + sp->blockvector = root->blockvector; + sp->typevector = root->typevector; + + RELOCATE (TYPEVECTOR (sp)); + RELOCATE (BLOCKVECTOR (sp)); + RELOCATE (sp->version); + RELOCATE (sp->compilation); + RELOCATE (sp->filename); + + relocate_typevector (TYPEVECTOR (sp)); + relocate_blockvector (BLOCKVECTOR (sp)); + + return sp; +} + +static void +relocate_blockvector (blp) + register struct blockvector *blp; +{ + register int nblocks = BLOCKVECTOR_NBLOCKS (blp); + register int i; + for (i = 0; i < nblocks; i++) + RELOCATE (BLOCKVECTOR_BLOCK (blp, i)); + for (i = 0; i < nblocks; i++) + relocate_block (BLOCKVECTOR_BLOCK (blp, i)); +} + +static void +relocate_block (bp) + register struct block *bp; +{ + register int nsyms = BLOCK_NSYMS (bp); + register int i; + + TEXT_RELOCATE (BLOCK_START (bp)); + TEXT_RELOCATE (BLOCK_END (bp)); + + /* These two should not be recursively processed. + The superblock need not be because all blocks are + processed from relocate_blockvector. + The function need not be because it will be processed + under the block which is its scope. */ + RELOCATE (BLOCK_SUPERBLOCK (bp)); + RELOCATE (BLOCK_FUNCTION (bp)); + + for (i = 0; i < nsyms; i++) + RELOCATE (BLOCK_SYM (bp, i)); + + for (i = 0; i < nsyms; i++) + relocate_symbol (BLOCK_SYM (bp, i)); +} + +static void +relocate_symbol (sp) + register struct symbol *sp; +{ + RELOCATE (SYMBOL_NAME (sp)); + if (SYMBOL_CLASS (sp) == LOC_BLOCK) + { + RELOCATE (SYMBOL_BLOCK_VALUE (sp)); + /* We can assume the block that belongs to this symbol + is not relocated yet, since it comes after + the block that contains this symbol. */ + BLOCK_FUNCTION (SYMBOL_BLOCK_VALUE (sp)) = sp; + UNRELOCATE (BLOCK_FUNCTION (SYMBOL_BLOCK_VALUE (sp))); + } + else if (SYMBOL_CLASS (sp) == LOC_STATIC) + CORE_RELOCATE (SYMBOL_VALUE (sp)); + else if (SYMBOL_CLASS (sp) == LOC_LABEL) + TEXT_RELOCATE (SYMBOL_VALUE (sp)); + RELOCATE (SYMBOL_TYPE (sp)); +} + +static void +relocate_typevector (tv) + struct typevector *tv; +{ + register int ntypes = TYPEVECTOR_NTYPES (tv); + register int i; + + for (i = 0; i < ntypes; i++) + RELOCATE (TYPEVECTOR_TYPE (tv, i)); + for (i = 0; i < ntypes; i++) + relocate_type (TYPEVECTOR_TYPE (tv, i)); +} + +/* We cannot come up with an a priori spanning tree + for the network of types, since types can be used + for many symbols and also as components of other types. + Therefore, we need to be able to mark types that we + already have relocated (or are already in the middle of relocating) + as in a garbage collector. */ + +static void +relocate_type (tp) + register struct type *tp; +{ + register int nfields = TYPE_NFIELDS (tp); + register int i; + + RELOCATE (TYPE_NAME (tp)); + RELOCATE (TYPE_TARGET_TYPE (tp)); + RELOCATE (TYPE_FIELDS (tp)); + RELOCATE (TYPE_POINTER_TYPE (tp)); + + for (i = 0; i < nfields; i++) + { + RELOCATE (TYPE_FIELD_TYPE (tp, i)); + RELOCATE (TYPE_FIELD_NAME (tp, i)); + } +} + +static void +relocate_sourcevector (svp) + register struct sourcevector *svp; +{ + register int nfiles = svp->length; + register int i; + for (i = 0; i < nfiles; i++) + RELOCATE (svp->source[i]); + for (i = 0; i < nfiles; i++) + relocate_source (svp->source[i]); +} + +static void +relocate_source (sp) + register struct source *sp; +{ + register int nitems = sp->contents.nitems; + register int i; + + RELOCATE (sp->name); + for (i = 0; i < nitems; i++) + TEXT_RELOCATE (sp->contents.item[i].pc); +} + +/* Read symsegs from file named NAME open on DESC, + make symtabs from them, and return a chain of them. + These symtabs are not suitable for direct use in `symtab_list' + because each one describes a single object file, perhaps many source files. + `symbol_file_command' takes each of these, makes many real symtabs + from it, and then frees it. + + We assume DESC is prepositioned at the end of the string table, + just before the symsegs if there are any. */ + +struct symtab * +read_symsegs (desc, name) + int desc; + char *name; +{ + struct symbol_root root; + register char *data; + register struct symtab *sp, *sp1, *chain = 0; + register int len; + + while (1) + { + len = myread (desc, &root, sizeof root); + if (len == 0 || root.format == 0) + break; + /* format 1 was ok for the original gdb, but since the size of the + type structure changed when C++ support was added, it can no + longer be used. Accept only format 2. */ + if (root.format != 2 || + root.length < sizeof root) + error ("\nInvalid symbol segment format code"); + data = (char *) xmalloc (root.length); + bcopy (&root, data, sizeof root); + len = myread (desc, data + sizeof root, + root.length - sizeof root); + sp = relocate_symtab (data); + RELOCATE (((struct symbol_root *)data)->sourcevector); + relocate_sourcevector (((struct symbol_root *)data)->sourcevector); + sp->next = chain; + chain = sp; + sp->linetable = (struct linetable *) ((struct symbol_root *)data)->sourcevector; + } + + return chain; +} + +static int block_depth (); +void print_spaces (); +static void print_symbol (); + +void +print_symtabs (filename) + char *filename; +{ + FILE *outfile; + register struct symtab *s; + register int i, j; + int len, line, blen; + register struct linetable *l; + struct blockvector *bv; + register struct block *b; + int depth; + struct cleanup *cleanups; + extern int fclose(); + + if (filename == 0) + error_no_arg ("file to write symbol data in"); + + filename = tilde_expand (filename); + make_cleanup (free, filename); + + outfile = fopen (filename, "w"); + if (outfile == 0) + perror_with_name (filename); + + cleanups = make_cleanup (fclose, outfile); + immediate_quit++; + + for (s = symtab_list; s; s = s->next) + { + /* First print the line table. */ + fprintf (outfile, "Symtab for file %s\n\n", s->filename); + fprintf (outfile, "Line table:\n\n"); + l = LINETABLE (s); + len = l->nitems; + for (i = 0; i < len; i++) + fprintf (outfile, " line %d at %x\n", l->item[i].line, + l->item[i].pc); + /* Now print the block info. */ + fprintf (outfile, "\nBlockvector:\n\n"); + bv = BLOCKVECTOR (s); + len = BLOCKVECTOR_NBLOCKS (bv); + for (i = 0; i < len; i++) + { + b = BLOCKVECTOR_BLOCK (bv, i); + depth = block_depth (b) * 2; + print_spaces (depth, outfile); + fprintf (outfile, "block #%03d (object 0x%x) ", i, b); + fprintf (outfile, "[0x%x..0x%x]", BLOCK_START (b), BLOCK_END (b)); + if (BLOCK_SUPERBLOCK (b)) + fprintf (outfile, " (under 0x%x)", BLOCK_SUPERBLOCK (b)); + if (BLOCK_FUNCTION (b)) + fprintf (outfile, " %s", SYMBOL_NAME (BLOCK_FUNCTION (b))); + fputc ('\n', outfile); + blen = BLOCK_NSYMS (b); + for (j = 0; j < blen; j++) + { + print_symbol (BLOCK_SYM (b, j), depth + 1, outfile); + } + } + + fprintf (outfile, "\n\n"); + } + + immediate_quit--; + do_cleanups (cleanups); +} + +static void +print_symbol (symbol, depth, outfile) + struct symbol *symbol; + int depth; + FILE *outfile; +{ + print_spaces (depth, outfile); + if (SYMBOL_NAMESPACE (symbol) == LABEL_NAMESPACE) + { + fprintf (outfile, "label %s at 0x%x\n", SYMBOL_NAME (symbol), + SYMBOL_VALUE (symbol)); + return; + } + if (SYMBOL_NAMESPACE (symbol) == STRUCT_NAMESPACE) + { + if (TYPE_NAME (SYMBOL_TYPE (symbol))) + { + type_print_1 (SYMBOL_TYPE (symbol), "", outfile, 1, depth); + } + else + { + fprintf (outfile, "%s %s = ", + (TYPE_CODE (SYMBOL_TYPE (symbol)) == TYPE_CODE_ENUM + ? "enum" + : (TYPE_CODE (SYMBOL_TYPE (symbol)) == TYPE_CODE_STRUCT + ? "struct" : "union")), + SYMBOL_NAME (symbol)); + type_print_1 (SYMBOL_TYPE (symbol), "", outfile, 1, depth); + } + fprintf (outfile, ";\n"); + } + else + { + if (SYMBOL_CLASS (symbol) == LOC_TYPEDEF) + fprintf (outfile, "typedef "); + if (SYMBOL_TYPE (symbol)) + { + type_print_1 (SYMBOL_TYPE (symbol), SYMBOL_NAME (symbol), + outfile, 1, depth); + fprintf (outfile, "; "); + } + else + fprintf (outfile, "%s ", SYMBOL_NAME (symbol)); + + switch (SYMBOL_CLASS (symbol)) + { + case LOC_CONST: + fprintf (outfile, "const %d (0x%x),", + SYMBOL_VALUE (symbol), SYMBOL_VALUE (symbol)); + break; + + case LOC_CONST_BYTES: + fprintf (outfile, "const %d hex bytes:", + TYPE_LENGTH (SYMBOL_TYPE (symbol))); + { + int i; + for (i = 0; i < TYPE_LENGTH (SYMBOL_TYPE (symbol)); i++) + fprintf (outfile, " %2x", SYMBOL_VALUE_BYTES (symbol) [i]); + fprintf (outfile, ","); + } + break; + + case LOC_STATIC: + fprintf (outfile, "static at 0x%x,", SYMBOL_VALUE (symbol)); + break; + + case LOC_REGISTER: + fprintf (outfile, "register %d,", SYMBOL_VALUE (symbol)); + break; + + case LOC_ARG: + fprintf (outfile, "arg at 0x%x,", SYMBOL_VALUE (symbol)); + break; + + case LOC_REF_ARG: + fprintf (outfile, "reference arg at 0x%x,", SYMBOL_VALUE (symbol)); + break; + + case LOC_REGPARM: + fprintf (outfile, "parameter register %d,", SYMBOL_VALUE (symbol)); + break; + + case LOC_LOCAL: + fprintf (outfile, "local at 0x%x,", SYMBOL_VALUE (symbol)); + break; + + case LOC_TYPEDEF: + break; + + case LOC_LABEL: + fprintf (outfile, "label at 0x%x", SYMBOL_VALUE (symbol)); + break; + + case LOC_BLOCK: + fprintf (outfile, "block (object 0x%x) starting at 0x%x,", + SYMBOL_VALUE (symbol), + BLOCK_START (SYMBOL_BLOCK_VALUE (symbol))); + break; + } + } + fprintf (outfile, "\n"); +} + +/* Return the nexting depth of a block within other blocks in its symtab. */ + +static int +block_depth (block) + struct block *block; +{ + register int i = 0; + while (block = BLOCK_SUPERBLOCK (block)) i++; + return i; +} + +/* + * Free all partial_symtab storage. + */ +void +free_all_psymtabs() +{ + obstack_free (psymbol_obstack, 0); + obstack_init (psymbol_obstack); + partial_symtab_list = (struct partial_symtab *) 0; +} + +void +_initialize_symmisc () +{ + symtab_list = (struct symtab *) 0; + partial_symtab_list = (struct partial_symtab *) 0; + + add_com ("printsyms", class_obscure, print_symtabs, + "Print dump of current symbol definitions to file OUTFILE."); +} + diff --git a/gnu/usr.bin/gdb/symseg.h b/gnu/usr.bin/gdb/symseg.h new file mode 100644 index 0000000000..6a61a1791a --- /dev/null +++ b/gnu/usr.bin/gdb/symseg.h @@ -0,0 +1,523 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + * + * @(#)symseg.h 6.3 (Berkeley) 5/8/91 + */ + +/* GDB symbol table format definitions. + Copyright (C) 1986, 1989 Free Software Foundation, Inc. + Hacked by Michael Tiemann (tiemann@mcc.com) + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Format of GDB symbol table data. + There is one symbol segment for each source file or + independant compilation. These segments are simply concatenated + to form the GDB symbol table. A zero word where the beginning + of a segment is expected indicates there are no more segments. + +Format of a symbol segment: + + The symbol segment begins with a word containing 1 + if it is in the format described here. Other formats may + be designed, with other code numbers. + + The segment contains many objects which point at each other. + The pointers are offsets in bytes from the beginning of the segment. + Thus, each segment can be loaded into core and its pointers relocated + to make valid in-core pointers. + + All the data objects in the segment can be found indirectly from + one of them, the root object, of type `struct symbol_root'. + It appears at the beginning of the segment. + + The total size of the segment, in bytes, appears as the `length' + field of this object. This size includes the size of the + root object. + + All the object data types are defined here to contain pointer types + appropriate for in-core use on a relocated symbol segment. + Casts to and from type int are required for working with + unrelocated symbol segments such as are found in the file. + + The ldsymaddr word is filled in by the loader to contain + the offset (in bytes) within the ld symbol table + of the first nonglobal symbol from this compilation. + This makes it possible to match those symbols + (which contain line number information) reliably with + the segment they go with. + + Core addresses within the program that appear in the symbol segment + are not relocated by the loader. They are inserted by the assembler + and apply to addresses as output by the assembler, so GDB must + relocate them when it loads the symbol segment. It gets the information + on how to relocate from the textrel, datarel, bssrel, databeg and bssbeg + words of the root object. + + The words textrel, datarel and bssrel + are filled in by ld with the amounts to relocate within-the-file + text, data and bss addresses by; databeg and bssbeg can be + used to tell which kind of relocation an address needs. */ + +enum language {language_c}; + +struct symbol_root +{ + int format; /* Data format version */ + int length; /* # bytes in this symbol segment */ + int ldsymoff; /* Offset in ld symtab of this file's syms */ + int textrel; /* Relocation for text addresses */ + int datarel; /* Relocation for data addresses */ + int bssrel; /* Relocation for bss addresses */ + char *filename; /* Name of main source file compiled */ + char *filedir; /* Name of directory it was reached from */ + struct blockvector *blockvector; /* Vector of all symbol-naming blocks */ + struct typevector *typevector; /* Vector of all data types */ + enum language language; /* Code identifying the language used */ + char *version; /* Version info. Not fully specified */ + char *compilation; /* Compilation info. Not fully specified */ + int databeg; /* Address within the file of data start */ + int bssbeg; /* Address within the file of bss start */ + struct sourcevector *sourcevector; /* Vector of line-number info */ +}; + +/* All data types of symbols in the compiled program + are represented by `struct type' objects. + All of these objects are pointed to by the typevector. + The type vector may have empty slots that contain zero. */ + +struct typevector +{ + int length; /* Number of types described */ + struct type *type[1]; +}; + +/* Different kinds of data types are distinguished by the `code' field. */ + +enum type_code +{ + TYPE_CODE_UNDEF, /* Not used; catches errors */ + TYPE_CODE_PTR, /* Pointer type */ + TYPE_CODE_ARRAY, /* Array type, lower bound zero */ + TYPE_CODE_STRUCT, /* C struct or Pascal record */ + TYPE_CODE_UNION, /* C union or Pascal variant part */ + TYPE_CODE_ENUM, /* Enumeration type */ + TYPE_CODE_FUNC, /* Function type */ + TYPE_CODE_INT, /* Integer type */ + TYPE_CODE_FLT, /* Floating type */ + TYPE_CODE_VOID, /* Void type (values zero length) */ + TYPE_CODE_SET, /* Pascal sets */ + TYPE_CODE_RANGE, /* Range (integers within spec'd bounds) */ + TYPE_CODE_PASCAL_ARRAY, /* Array with explicit type of index */ + + /* C++ */ + TYPE_CODE_MEMBER, /* Member type */ + TYPE_CODE_METHOD, /* Method type */ + TYPE_CODE_REF, /* C++ Reference types */ +}; + +/* This appears in a type's flags word for an unsigned integer type. */ +#define TYPE_FLAG_UNSIGNED 1 +/* This appears in a type's flags word + if it is a (pointer to a|function returning a)* built in scalar type. + These types are never freed. */ +#define TYPE_FLAG_PERM 4 +/* This appears in a type's flags word if it is a stub type (eg. if + someone referenced a type that wasn't definined in a source file + via (struct sir_not_appearing_in_this_film *)). */ +#define TYPE_FLAG_STUB 8 +/* Set when a class has a constructor defined */ +#define TYPE_FLAG_HAS_CONSTRUCTOR 256 +/* Set when a class has a destructor defined */ +#define TYPE_FLAG_HAS_DESTRUCTOR 512 +/* Indicates that this type is a public baseclass of another class, + i.e. that all its public methods are available in the derived + class. */ +#define TYPE_FLAG_VIA_PUBLIC 1024 +/* Indicates that this type is a virtual baseclass of another class, + i.e. that if this class is inherited more than once by another + class, only one set of member variables will be included. */ +#define TYPE_FLAG_VIA_VIRTUAL 2048 + +struct type +{ + /* Code for kind of type */ + enum type_code code; + /* Name of this type, or zero if none. + This is used for printing only. + Type names specified as input are defined by symbols. */ + char *name; + /* Length in bytes of storage for a value of this type */ + int length; + /* For a pointer type, describes the type of object pointed to. + For an array type, describes the type of the elements. + For a function or method type, describes the type of the value. + For a range type, describes the type of the full range. + Unused otherwise. */ + struct type *target_type; + /* Type that is a pointer to this type. + Zero if no such pointer-to type is known yet. + The debugger may add the address of such a type + if it has to construct one later. */ + struct type *pointer_type; + /* C++: also need a reference type. */ + struct type *reference_type; + struct type **arg_types; + + /* Type that is a function returning this type. + Zero if no such function type is known here. + The debugger may add the address of such a type + if it has to construct one later. */ + struct type *function_type; + +/* Handling of pointers to members: + TYPE_MAIN_VARIANT is used for pointer and pointer + to member types. Normally it the value of the address of its + containing type. However, for pointers to members, we must be + able to allocate pointer to member types and look them up + from some place of reference. + NEXT_VARIANT is the next element in the chain. */ + struct type *main_variant, *next_variant; + + /* Flags about this type. */ + short flags; + /* Number of fields described for this type */ + short nfields; + /* For structure and union types, a description of each field. + For set and pascal array types, there is one "field", + whose type is the domain type of the set or array. + For range types, there are two "fields", + the minimum and maximum values (both inclusive). + For enum types, each possible value is described by one "field". + + Using a pointer to a separate array of fields + allows all types to have the same size, which is useful + because we can allocate the space for a type before + we know what to put in it. */ + struct field + { + /* Position of this field, counting in bits from start of + containing structure. For a function type, this is the + position in the argument list of this argument. + For a range bound or enum value, this is the value itself. */ + int bitpos; + /* Size of this field, in bits, or zero if not packed. + For an unpacked field, the field's type's length + says how many bytes the field occupies. */ + int bitsize; + /* In a struct or enum type, type of this field. + In a function type, type of this argument. + In an array type, the domain-type of the array. */ + struct type *type; + /* Name of field, value or argument. + Zero for range bounds and array domains. */ + char *name; + } *fields; + + /* C++ */ + int *private_field_bits; + int *protected_field_bits; + + /* Number of methods described for this type */ + short nfn_fields; + /* Number of base classes this type derives from. */ + short n_baseclasses; + + /* Number of methods described for this type plus all the + methods that it derives from. */ + int nfn_fields_total; + + /* For classes, structures, and unions, a description of each field, + which consists of an overloaded name, followed by the types of + arguments that the method expects, and then the name after it + has been renamed to make it distinct. */ + struct fn_fieldlist + { + /* The overloaded name. */ + char *name; + /* The number of methods with this name. */ + int length; + /* The list of methods. */ + struct fn_field + { +#if 0 + /* The overloaded name */ + char *name; +#endif + /* The return value of the method */ + struct type *type; + /* The argument list */ + struct type **args; + /* The name after it has been processed */ + char *physname; + /* If this is a virtual function, the offset into the vtbl-1, + else 0. */ + int voffset; + } *fn_fields; + + int *private_fn_field_bits; + int *protected_fn_field_bits; + + } *fn_fieldlists; + + unsigned char via_protected; + unsigned char via_public; + + /* For types with virtual functions, VPTR_BASETYPE is the base class which + defined the virtual function table pointer. VPTR_FIELDNO is + the field number of that pointer in the structure. + + For types that are pointer to member types, VPTR_BASETYPE + ifs the type that this pointer is a member of. + + Unused otherwise. */ + struct type *vptr_basetype; + + int vptr_fieldno; + + /* If this type has a base class, put it here. + If this type is a pointer type, the chain of member pointer + types goes here. + Unused otherwise. + + Contrary to all maxims of C style and common sense, the baseclasses + are indexed from 1 to N_BASECLASSES rather than 0 to N_BASECLASSES-1 + (i.e. BASECLASSES points to one *before* the first element of + the array). */ + struct type **baseclasses; +}; + +/* All of the name-scope contours of the program + are represented by `struct block' objects. + All of these objects are pointed to by the blockvector. + + Each block represents one name scope. + Each lexical context has its own block. + + The first two blocks in the blockvector are special. + The first one contains all the symbols defined in this compilation + whose scope is the entire program linked together. + The second one contains all the symbols whose scope is the + entire compilation excluding other separate compilations. + In C, these correspond to global symbols and static symbols. + + Each block records a range of core addresses for the code that + is in the scope of the block. The first two special blocks + give, for the range of code, the entire range of code produced + by the compilation that the symbol segment belongs to. + + The blocks appear in the blockvector + in order of increasing starting-address, + and, within that, in order of decreasing ending-address. + + This implies that within the body of one function + the blocks appear in the order of a depth-first tree walk. */ + +struct blockvector +{ + /* Number of blocks in the list. */ + int nblocks; + /* The blocks themselves. */ + struct block *block[1]; +}; + +struct block +{ + /* Addresses in the executable code that are in this block. + Note: in an unrelocated symbol segment in a file, + these are always zero. They can be filled in from the + N_LBRAC and N_RBRAC symbols in the loader symbol table. */ + int startaddr, endaddr; + /* The symbol that names this block, + if the block is the body of a function; + otherwise, zero. + Note: In an unrelocated symbol segment in an object file, + this field may be zero even when the block has a name. + That is because the block is output before the name + (since the name resides in a higher block). + Since the symbol does point to the block (as its value), + it is possible to find the block and set its name properly. */ + struct symbol *function; + /* The `struct block' for the containing block, or 0 if none. */ + /* Note that in an unrelocated symbol segment in an object file + this pointer may be zero when the correct value should be + the second special block (for symbols whose scope is one compilation). + This is because the compiler ouptuts the special blocks at the + very end, after the other blocks. */ + struct block *superblock; + /* A flag indicating whether or not the fucntion corresponding + to this block was compiled with gcc or not. If there is no + function corresponding to this block, this meaning of this flag + is undefined. (In practice it will be 1 if the block was created + while processing a file compiled with gcc and 0 when not). */ + unsigned char gcc_compile_flag; + /* Number of local symbols. */ + int nsyms; + /* The symbols. */ + struct symbol *sym[1]; +}; + +/* Represent one symbol name; a variable, constant, function or typedef. */ + +/* Different name spaces for symbols. Looking up a symbol specifies + a namespace and ignores symbol definitions in other name spaces. + + VAR_NAMESPACE is the usual namespace. + In C, this contains variables, function names, typedef names + and enum type values. + + STRUCT_NAMESPACE is used in C to hold struct, union and enum type names. + Thus, if `struct foo' is used in a C program, + it produces a symbol named `foo' in the STRUCT_NAMESPACE. + + LABEL_NAMESPACE may be used for names of labels (for gotos); + currently it is not used and labels are not recorded at all. */ + +/* For a non-global symbol allocated statically, + the correct core address cannot be determined by the compiler. + The compiler puts an index number into the symbol's value field. + This index number can be matched with the "desc" field of + an entry in the loader symbol table. */ + +enum namespace +{ + UNDEF_NAMESPACE, VAR_NAMESPACE, STRUCT_NAMESPACE, LABEL_NAMESPACE, +}; + +/* An address-class says where to find the value of the symbol in core. */ + +enum address_class +{ + LOC_UNDEF, /* Not used; catches errors */ + LOC_CONST, /* Value is constant int */ + LOC_STATIC, /* Value is at fixed address */ + LOC_REGISTER, /* Value is in register */ + LOC_ARG, /* Value is at spec'd position in arglist */ + LOC_REF_ARG, /* Value address is at spec'd position in */ + /* arglist. */ + LOC_REGPARM, /* Value is at spec'd position in register window */ + LOC_LOCAL, /* Value is at spec'd pos in stack frame */ + LOC_TYPEDEF, /* Value not used; definition in SYMBOL_TYPE + Symbols in the namespace STRUCT_NAMESPACE + all have this class. */ + LOC_LABEL, /* Value is address in the code */ + LOC_BLOCK, /* Value is address of a `struct block'. + Function names have this class. */ + LOC_EXTERNAL, /* Value is at address not in this compilation. + This is used for .comm symbols + and for extern symbols within functions. + Inside GDB, this is changed to LOC_STATIC once the + real address is obtained from a loader symbol. */ + LOC_CONST_BYTES /* Value is a constant byte-sequence. */ +}; + +struct symbol +{ + /* Symbol name */ + char *name; + /* Name space code. */ + enum namespace namespace; + /* Address class */ + enum address_class class; + /* Data type of value */ + struct type *type; + /* constant value, or address if static, or register number, + or offset in arguments, or offset in stack frame. */ + union + { + long value; + struct block *block; /* for LOC_BLOCK */ + char *bytes; /* for LOC_CONST_BYTES */ + } + value; +}; + +struct partial_symbol +{ + /* Symbol name */ + char *name; + /* Name space code. */ + enum namespace namespace; + /* Address class (for info_symbols) */ + enum address_class class; + /* Associated partial symbol table */ + struct partial_symtab *pst; + /* Value (only used for static functions currently). Done this + way so that we can use the struct symbol macros. + Note that the address of a function is SYMBOL_VALUE (pst) + in a partial symbol table, but BLOCK_START (SYMBOL_BLOCK_VALUE (st)) + in a symbol table. */ + union + { + long value; + } + value; +}; + +/* + * Vectors of all partial symbols read in from file; actually declared + * and used in dbxread.c. + */ +extern struct psymbol_allocation_list { + struct partial_symbol *list, *next; + int size; +} global_psymbols, static_psymbols; + + +/* Source-file information. + This describes the relation between source files and line numbers + and addresses in the program text. */ + +struct sourcevector +{ + int length; /* Number of source files described */ + struct source *source[1]; /* Descriptions of the files */ +}; + +/* Each item represents a line-->pc (or the reverse) mapping. This is + somewhat more wasteful of space than one might wish, but since only + the files which are actually debugged are read in to core, we don't + waste much space. + + Each item used to be an int; either minus a line number, or a + program counter. If it represents a line number, that is the line + described by the next program counter value. If it is positive, it + is the program counter at which the code for the next line starts. */ + +struct linetable_entry +{ + int line; + CORE_ADDR pc; +}; + +struct linetable +{ + int nitems; + struct linetable_entry item[1]; +}; + +/* All the information on one source file. */ + +struct source +{ + char *name; /* Name of file */ + struct linetable contents; +}; diff --git a/gnu/usr.bin/gdb/symtab.c b/gnu/usr.bin/gdb/symtab.c new file mode 100644 index 0000000000..a6feff4d19 --- /dev/null +++ b/gnu/usr.bin/gdb/symtab.c @@ -0,0 +1,2473 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + * + * $Header: /home/cvs/386BSD/src/usr.bin/gdb/symtab.c,v 1.1.1.1 1993/06/12 14:52:20 rgrimes Exp $; + */ + +#ifndef lint +static char sccsid[] = "@(#)symtab.c 6.3 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Symbol table lookup for the GNU debugger, GDB. + Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include "defs.h" +#include "symtab.h" +#include "param.h" + +#include +#include + +char *index (); +extern char *cplus_demangle (); +extern struct value * value_of_this (); + +/* Allocate an obstack to hold objects that should be freed + when we load a new symbol table. + This includes the symbols made by dbxread + and the types that are not permanent. */ + +struct obstack obstack1; + +struct obstack *symbol_obstack = &obstack1; + +/* This obstack will be used for partial_symbol objects. It can + probably actually be the same as the symbol_obstack above, but I'd + like to keep them seperate for now. If I want to later, I'll + replace one with the other. */ + +struct obstack obstack2; + +struct obstack *psymbol_obstack = &obstack2; + +/* These variables point to the objects + representing the predefined C data types. */ + +struct type *builtin_type_void; +struct type *builtin_type_char; +struct type *builtin_type_short; +struct type *builtin_type_int; +struct type *builtin_type_long; +#ifdef LONG_LONG +struct type *builtin_type_long_long; +#endif +struct type *builtin_type_unsigned_char; +struct type *builtin_type_unsigned_short; +struct type *builtin_type_unsigned_int; +struct type *builtin_type_unsigned_long; +#ifdef LONG_LONG +struct type *builtin_type_unsigned_long_long; +#endif +struct type *builtin_type_float; +struct type *builtin_type_double; + +/* Block in which the most recently searched-for symbol was found. + Might be better to make this a parameter to lookup_symbol and + value_of_this. */ +struct block *block_found; + +/* Functions */ +static int find_line_common (); +int lookup_misc_func (); +struct partial_symtab *lookup_partial_symtab (); +struct symtab *psymtab_to_symtab (); +static struct partial_symbol *lookup_partial_symbol (); + +/* Check for a symtab of a specific name; first in symtabs, then in + psymtabs. *If* there is no '/' in the name, a match after a '/' + in the symtab filename will also work. */ + +static struct symtab * +lookup_symtab_1 (name) + char *name; +{ + register struct symtab *s; + register struct partial_symtab *ps; + register char *slash = index (name, '/'); + register int len = strlen (name); + + for (s = symtab_list; s; s = s->next) + if (!strcmp (name, s->filename)) + return s; + + for (ps = partial_symtab_list; ps; ps = ps->next) + if (!strcmp (name, ps->filename)) + { + if (ps->readin) + fatal ("Internal: readin pst found when no symtab found."); + s = psymtab_to_symtab (ps); + return s; + } + + if (!slash) + { + for (s = symtab_list; s; s = s->next) + { + int l = strlen (s->filename); + + if (s->filename[l - len -1] == '/' + && !strcmp (s->filename + l - len, name)) + return s; + } + + for (ps = partial_symtab_list; ps; ps = ps->next) + { + int l = strlen (ps->filename); + + if (ps->filename[l - len - 1] == '/' + && !strcmp (ps->filename + l - len, name)) + { + if (ps->readin) + fatal ("Internal: readin pst found when no symtab found."); + s = psymtab_to_symtab (ps); + return s; + } + } + } + return 0; +} + +/* Lookup the symbol table of a source file named NAME. Try a couple + of variations if the first lookup doesn't work. */ + +struct symtab * +lookup_symtab (name) + char *name; +{ + register struct symtab *s; + register char *copy; + + s = lookup_symtab_1 (name); + if (s) return s; + + /* If name not found as specified, see if adding ".c" helps. */ + + copy = (char *) alloca (strlen (name) + 3); + strcpy (copy, name); + strcat (copy, ".c"); + s = lookup_symtab_1 (copy); + if (s) return s; + + /* We didn't find anything; die. */ + return 0; +} + +/* Lookup the partial symbol table of a source file named NAME. This + only returns true on an exact match (ie. this semantics are + different from lookup_symtab. */ + +struct partial_symtab * +lookup_partial_symtab (name) +char *name; +{ + register struct partial_symtab *s; + register char *copy; + + for (s = partial_symtab_list; s; s = s->next) + if (!strcmp (name, s->filename)) + return s; + + return 0; +} + +/* Lookup a typedef or primitive type named NAME, + visible in lexical block BLOCK. + If NOERR is nonzero, return zero if NAME is not suitably defined. */ + +struct type * +lookup_typename (name, block, noerr) + char *name; + struct block *block; + int noerr; +{ + register struct symbol *sym = lookup_symbol (name, block, VAR_NAMESPACE, 0); + if (sym == 0 || SYMBOL_CLASS (sym) != LOC_TYPEDEF) + { + if (!strcmp (name, "int")) + return builtin_type_int; + if (!strcmp (name, "long")) + return builtin_type_long; + if (!strcmp (name, "short")) + return builtin_type_short; + if (!strcmp (name, "char")) + return builtin_type_char; + if (!strcmp (name, "float")) + return builtin_type_float; + if (!strcmp (name, "double")) + return builtin_type_double; + if (!strcmp (name, "void")) + return builtin_type_void; + + if (noerr) + return 0; + error ("No type named %s.", name); + } + return SYMBOL_TYPE (sym); +} + +struct type * +lookup_unsigned_typename (name) + char *name; +{ + if (!strcmp (name, "int")) + return builtin_type_unsigned_int; + if (!strcmp (name, "long")) + return builtin_type_unsigned_long; + if (!strcmp (name, "short")) + return builtin_type_unsigned_short; + if (!strcmp (name, "char")) + return builtin_type_unsigned_char; + error ("No type named unsigned %s.", name); +} + +/* Lookup a structure type named "struct NAME", + visible in lexical block BLOCK. */ + +struct type * +lookup_struct (name, block) + char *name; + struct block *block; +{ + register struct symbol *sym + = lookup_symbol (name, block, STRUCT_NAMESPACE, 0); + + if (sym == 0) + error ("No struct type named %s.", name); + if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_STRUCT) + error ("This context has class, union or enum %s, not a struct.", name); + return SYMBOL_TYPE (sym); +} + +/* Lookup a union type named "union NAME", + visible in lexical block BLOCK. */ + +struct type * +lookup_union (name, block) + char *name; + struct block *block; +{ + register struct symbol *sym + = lookup_symbol (name, block, STRUCT_NAMESPACE, 0); + + if (sym == 0) + error ("No union type named %s.", name); + if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_UNION) + error ("This context has class, struct or enum %s, not a union.", name); + return SYMBOL_TYPE (sym); +} + +/* Lookup an enum type named "enum NAME", + visible in lexical block BLOCK. */ + +struct type * +lookup_enum (name, block) + char *name; + struct block *block; +{ + register struct symbol *sym + = lookup_symbol (name, block, STRUCT_NAMESPACE, 0); + if (sym == 0) + error ("No enum type named %s.", name); + if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_ENUM) + error ("This context has class, struct or union %s, not an enum.", name); + return SYMBOL_TYPE (sym); +} + +/* Given a type TYPE, lookup the type of the component of type named + NAME. */ + +struct type * +lookup_struct_elt_type (type, name) + struct type *type; + char *name; +{ + struct type *t; + int i; + char *errmsg; + + if (TYPE_CODE (type) != TYPE_CODE_STRUCT + && TYPE_CODE (type) != TYPE_CODE_UNION) + { + terminal_ours (); + fflush (stdout); + fprintf (stderr, "Type "); + type_print (type, "", stderr, -1); + fprintf (stderr, " is not a structure or union type.\n"); + return_to_top_level (); + } + + for (i = TYPE_NFIELDS (type) - 1; i >= 0; i--) + if (!strcmp (TYPE_FIELD_NAME (type, i), name)) + return TYPE_FIELD_TYPE (type, i); + + terminal_ours (); + fflush (stdout); + fprintf (stderr, "Type "); + type_print (type, "", stderr, -1); + fprintf (stderr, " has no component named %s\n", name); + return_to_top_level (); +} + +/* Given a type TYPE, return a type of pointers to that type. + May need to construct such a type if this is the first use. + + C++: use TYPE_MAIN_VARIANT and TYPE_CHAIN to keep pointer + to member types under control. */ + +struct type * +lookup_pointer_type (type) + struct type *type; +{ + register struct type *ptype = TYPE_POINTER_TYPE (type); + if (ptype) return TYPE_MAIN_VARIANT (ptype); + + /* This is the first time anyone wanted a pointer to a TYPE. */ + if (TYPE_FLAGS (type) & TYPE_FLAG_PERM) + ptype = (struct type *) xmalloc (sizeof (struct type)); + else + ptype = (struct type *) obstack_alloc (symbol_obstack, + sizeof (struct type)); + + bzero (ptype, sizeof (struct type)); + TYPE_MAIN_VARIANT (ptype) = ptype; + TYPE_TARGET_TYPE (ptype) = type; + TYPE_POINTER_TYPE (type) = ptype; + /* New type is permanent if type pointed to is permanent. */ + if (TYPE_FLAGS (type) & TYPE_FLAG_PERM) + TYPE_FLAGS (ptype) |= TYPE_FLAG_PERM; + /* We assume the machine has only one representation for pointers! */ + TYPE_LENGTH (ptype) = sizeof (char *); + TYPE_CODE (ptype) = TYPE_CODE_PTR; + return ptype; +} + +struct type * +lookup_reference_type (type) + struct type *type; +{ + register struct type *rtype = TYPE_REFERENCE_TYPE (type); + if (rtype) return TYPE_MAIN_VARIANT (rtype); + + /* This is the first time anyone wanted a pointer to a TYPE. */ + if (TYPE_FLAGS (type) & TYPE_FLAG_PERM) + rtype = (struct type *) xmalloc (sizeof (struct type)); + else + rtype = (struct type *) obstack_alloc (symbol_obstack, + sizeof (struct type)); + + bzero (rtype, sizeof (struct type)); + TYPE_MAIN_VARIANT (rtype) = rtype; + TYPE_TARGET_TYPE (rtype) = type; + TYPE_REFERENCE_TYPE (type) = rtype; + /* New type is permanent if type pointed to is permanent. */ + if (TYPE_FLAGS (type) & TYPE_FLAG_PERM) + TYPE_FLAGS (rtype) |= TYPE_FLAG_PERM; + /* We assume the machine has only one representation for pointers! */ + TYPE_LENGTH (rtype) = sizeof (char *); + TYPE_CODE (rtype) = TYPE_CODE_REF; + return rtype; +} + + +/* Implement direct support for MEMBER_TYPE in GNU C++. + May need to construct such a type if this is the first use. + The TYPE is the type of the member. The DOMAIN is the type + of the aggregate that the member belongs to. */ + +struct type * +lookup_member_type (type, domain) + struct type *type, *domain; +{ + register struct type *mtype = TYPE_MAIN_VARIANT (type); + struct type *main_type; + + main_type = mtype; + while (mtype) + { + if (TYPE_DOMAIN_TYPE (mtype) == domain) + return mtype; + mtype = TYPE_NEXT_VARIANT (mtype); + } + + /* This is the first time anyone wanted this member type. */ + if (TYPE_FLAGS (type) & TYPE_FLAG_PERM) + mtype = (struct type *) xmalloc (sizeof (struct type)); + else + mtype = (struct type *) obstack_alloc (symbol_obstack, + sizeof (struct type)); + + bzero (mtype, sizeof (struct type)); + if (main_type == 0) + main_type = mtype; + else + { + TYPE_NEXT_VARIANT (mtype) = TYPE_NEXT_VARIANT (main_type); + TYPE_NEXT_VARIANT (main_type) = mtype; + } + TYPE_MAIN_VARIANT (mtype) = main_type; + TYPE_TARGET_TYPE (mtype) = type; + TYPE_DOMAIN_TYPE (mtype) = domain; + /* New type is permanent if type pointed to is permanent. */ + if (TYPE_FLAGS (type) & TYPE_FLAG_PERM) + TYPE_FLAGS (mtype) |= TYPE_FLAG_PERM; + + /* In practice, this is never used. */ + TYPE_LENGTH (mtype) = 1; + TYPE_CODE (mtype) = TYPE_CODE_MEMBER; + +#if 0 + /* Now splice in the new member pointer type. */ + if (main_type) + { + /* This type was not "smashed". */ + TYPE_CHAIN (mtype) = TYPE_CHAIN (main_type); + TYPE_CHAIN (main_type) = mtype; + } +#endif + + return mtype; +} + +struct type * +lookup_method_type (type, domain, args) + struct type *type, *domain, **args; +{ + register struct type *mtype = TYPE_MAIN_VARIANT (type); + struct type *main_type; + + main_type = mtype; + while (mtype) + { + if (TYPE_DOMAIN_TYPE (mtype) == domain) + { + struct type **t1 = args; + struct type **t2 = TYPE_ARG_TYPES (mtype); + if (t2) + { + int i; + for (i = 0; t1[i] != 0 && t1[i]->code != TYPE_CODE_VOID; i++) + if (t1[i] != t2[i]) + break; + if (t1[i] == t2[i]) + return mtype; + } + } + mtype = TYPE_NEXT_VARIANT (mtype); + } + + /* This is the first time anyone wanted this member type. */ + if (TYPE_FLAGS (type) & TYPE_FLAG_PERM) + mtype = (struct type *) xmalloc (sizeof (struct type)); + else + mtype = (struct type *) obstack_alloc (symbol_obstack, + sizeof (struct type)); + + bzero (mtype, sizeof (struct type)); + if (main_type == 0) + main_type = mtype; + else + { + TYPE_NEXT_VARIANT (mtype) = TYPE_NEXT_VARIANT (main_type); + TYPE_NEXT_VARIANT (main_type) = mtype; + } + TYPE_MAIN_VARIANT (mtype) = main_type; + TYPE_TARGET_TYPE (mtype) = type; + TYPE_DOMAIN_TYPE (mtype) = domain; + TYPE_ARG_TYPES (mtype) = args; + /* New type is permanent if type pointed to is permanent. */ + if (TYPE_FLAGS (type) & TYPE_FLAG_PERM) + TYPE_FLAGS (mtype) |= TYPE_FLAG_PERM; + + /* In practice, this is never used. */ + TYPE_LENGTH (mtype) = 1; + TYPE_CODE (mtype) = TYPE_CODE_METHOD; + +#if 0 + /* Now splice in the new member pointer type. */ + if (main_type) + { + /* This type was not "smashed". */ + TYPE_CHAIN (mtype) = TYPE_CHAIN (main_type); + TYPE_CHAIN (main_type) = mtype; + } +#endif + + return mtype; +} + +/* Given a type TYPE, return a type which has offset OFFSET, + via_virtual VIA_VIRTUAL, and via_public VIA_PUBLIC. + May need to construct such a type if none exists. */ +struct type * +lookup_basetype_type (type, offset, via_virtual, via_public) + struct type *type; + int offset; + int via_virtual, via_public; +{ + register struct type *btype = TYPE_MAIN_VARIANT (type); + struct type *main_type; + + if (offset != 0) + { + printf ("Internal error: type offset non-zero in lookup_basetype_type"); + offset = 0; + } + + main_type = btype; + while (btype) + { + if (/* TYPE_OFFSET (btype) == offset + && */ TYPE_VIA_PUBLIC (btype) == via_public + && TYPE_VIA_VIRTUAL (btype) == via_virtual) + return btype; + btype = TYPE_NEXT_VARIANT (btype); + } + + /* This is the first time anyone wanted this member type. */ + if (TYPE_FLAGS (type) & TYPE_FLAG_PERM) + btype = (struct type *) xmalloc (sizeof (struct type)); + else + btype = (struct type *) obstack_alloc (symbol_obstack, + sizeof (struct type)); + + if (main_type == 0) + { + main_type = btype; + bzero (btype, sizeof (struct type)); + TYPE_MAIN_VARIANT (btype) = main_type; + } + else + { + bcopy (main_type, btype, sizeof (struct type)); + TYPE_NEXT_VARIANT (main_type) = btype; + } +/* TYPE_OFFSET (btype) = offset; */ + if (via_public) + TYPE_FLAGS (btype) |= TYPE_FLAG_VIA_PUBLIC; + if (via_virtual) + TYPE_FLAGS (btype) |= TYPE_FLAG_VIA_VIRTUAL; + /* New type is permanent if type pointed to is permanent. */ + if (TYPE_FLAGS (type) & TYPE_FLAG_PERM) + TYPE_FLAGS (btype) |= TYPE_FLAG_PERM; + + /* In practice, this is never used. */ + TYPE_LENGTH (btype) = 1; + TYPE_CODE (btype) = TYPE_CODE_STRUCT; + + return btype; +} + +/* Given a type TYPE, return a type of functions that return that type. + May need to construct such a type if this is the first use. */ + +struct type * +lookup_function_type (type) + struct type *type; +{ + register struct type *ptype = TYPE_FUNCTION_TYPE (type); + if (ptype) return ptype; + + /* This is the first time anyone wanted a function returning a TYPE. */ + if (TYPE_FLAGS (type) & TYPE_FLAG_PERM) + ptype = (struct type *) xmalloc (sizeof (struct type)); + else + ptype = (struct type *) obstack_alloc (symbol_obstack, + sizeof (struct type)); + + bzero (ptype, sizeof (struct type)); + TYPE_TARGET_TYPE (ptype) = type; + TYPE_FUNCTION_TYPE (type) = ptype; + /* New type is permanent if type returned is permanent. */ + if (TYPE_FLAGS (type) & TYPE_FLAG_PERM) + TYPE_FLAGS (ptype) |= TYPE_FLAG_PERM; + TYPE_LENGTH (ptype) = 1; + TYPE_CODE (ptype) = TYPE_CODE_FUNC; + TYPE_NFIELDS (ptype) = 0; + return ptype; +} + +/* Create an array type. Elements will be of type TYPE, and there will + be NUM of them. + + Eventually this should be extended to take two more arguments which + specify the bounds of the array and the type of the index. + It should also be changed to be a "lookup" function, with the + appropriate data structures added to the type field. + Then read array type should call here. */ + +struct type * +create_array_type (element_type, number) + struct type *element_type; + int number; +{ + struct type *result_type = (struct type *) + obstack_alloc (symbol_obstack, sizeof (struct type)); + + bzero (result_type, sizeof (struct type)); + + TYPE_CODE (result_type) = TYPE_CODE_ARRAY; + TYPE_TARGET_TYPE (result_type) = element_type; + TYPE_LENGTH (result_type) = number * TYPE_LENGTH (element_type); + TYPE_NFIELDS (result_type) = 1; + TYPE_FIELDS (result_type) = + (struct field *) obstack_alloc (symbol_obstack, sizeof (struct field)); + TYPE_FIELD_TYPE (result_type, 0) = builtin_type_int; + TYPE_VPTR_FIELDNO (result_type) = -1; + + return result_type; +} + + +/* Smash TYPE to be a type of pointers to TO_TYPE. + If TO_TYPE is not permanent and has no pointer-type yet, + record TYPE as its pointer-type. */ + +void +smash_to_pointer_type (type, to_type) + struct type *type, *to_type; +{ + int type_permanent = (TYPE_FLAGS (type) & TYPE_FLAG_PERM); + + bzero (type, sizeof (struct type)); + TYPE_TARGET_TYPE (type) = to_type; + /* We assume the machine has only one representation for pointers! */ + TYPE_LENGTH (type) = sizeof (char *); + TYPE_CODE (type) = TYPE_CODE_PTR; + + TYPE_MAIN_VARIANT (type) = type; + + if (type_permanent) + TYPE_FLAGS (type) |= TYPE_FLAG_PERM; + + if (TYPE_POINTER_TYPE (to_type) == 0 + && (!(TYPE_FLAGS (to_type) & TYPE_FLAG_PERM) + || type_permanent)) + { + TYPE_POINTER_TYPE (to_type) = type; + } +} + +/* Smash TYPE to be a type of members of DOMAIN with type TO_TYPE. */ + +void +smash_to_member_type (type, domain, to_type) + struct type *type, *domain, *to_type; +{ + bzero (type, sizeof (struct type)); + TYPE_TARGET_TYPE (type) = to_type; + TYPE_DOMAIN_TYPE (type) = domain; + + /* In practice, this is never needed. */ + TYPE_LENGTH (type) = 1; + TYPE_CODE (type) = TYPE_CODE_MEMBER; + + TYPE_MAIN_VARIANT (type) = lookup_member_type (domain, to_type); +} + +/* Smash TYPE to be a type of method of DOMAIN with type TO_TYPE. */ + +void +smash_to_method_type (type, domain, to_type, args) + struct type *type, *domain, *to_type, **args; +{ + bzero (type, sizeof (struct type)); + TYPE_TARGET_TYPE (type) = to_type; + TYPE_DOMAIN_TYPE (type) = domain; + TYPE_ARG_TYPES (type) = args; + + /* In practice, this is never needed. */ + TYPE_LENGTH (type) = 1; + TYPE_CODE (type) = TYPE_CODE_METHOD; + + TYPE_MAIN_VARIANT (type) = lookup_method_type (domain, to_type, args); +} + +/* Smash TYPE to be a type of reference to TO_TYPE. + If TO_TYPE is not permanent and has no pointer-type yet, + record TYPE as its pointer-type. */ + +void +smash_to_reference_type (type, to_type) + struct type *type, *to_type; +{ + int type_permanent = (TYPE_FLAGS (type) & TYPE_FLAG_PERM); + + bzero (type, sizeof (struct type)); + TYPE_TARGET_TYPE (type) = to_type; + /* We assume the machine has only one representation for pointers! */ + TYPE_LENGTH (type) = sizeof (char *); + TYPE_CODE (type) = TYPE_CODE_REF; + + TYPE_MAIN_VARIANT (type) = type; + + if (type_permanent) + TYPE_FLAGS (type) |= TYPE_FLAG_PERM; + + if (TYPE_REFERENCE_TYPE (to_type) == 0 + && (!(TYPE_FLAGS (to_type) & TYPE_FLAG_PERM) + || type_permanent)) + { + TYPE_REFERENCE_TYPE (to_type) = type; + } +} + +/* Smash TYPE to be a type of functions returning TO_TYPE. + If TO_TYPE is not permanent and has no function-type yet, + record TYPE as its function-type. */ + +void +smash_to_function_type (type, to_type) + struct type *type, *to_type; +{ + int type_permanent = (TYPE_FLAGS (type) & TYPE_FLAG_PERM); + + bzero (type, sizeof (struct type)); + TYPE_TARGET_TYPE (type) = to_type; + TYPE_LENGTH (type) = 1; + TYPE_CODE (type) = TYPE_CODE_FUNC; + TYPE_NFIELDS (type) = 0; + + if (type_permanent) + TYPE_FLAGS (type) |= TYPE_FLAG_PERM; + + if (TYPE_FUNCTION_TYPE (to_type) == 0 + && (!(TYPE_FLAGS (to_type) & TYPE_FLAG_PERM) + || type_permanent)) + { + TYPE_FUNCTION_TYPE (to_type) = type; + } +} + +/* Find which partial symtab on the partial_symtab_list contains + PC. Return 0 if none. */ + +struct partial_symtab * +find_pc_psymtab (pc) + register CORE_ADDR pc; +{ + register struct partial_symtab *ps; + + for (ps = partial_symtab_list; ps; ps = ps->next) + if (pc >= ps->textlow && pc < ps->texthigh) + return ps; + + return 0; +} + +/* Find which partial symbol within a psymtab contains PC. Return 0 + if none. Check all psymtabs if PSYMTAB is 0. */ +struct partial_symbol * +find_pc_psymbol (psymtab, pc) + struct partial_symtab *psymtab; + CORE_ADDR pc; +{ + struct partial_symbol *best, *p; + int best_pc; + + if (!psymtab) + psymtab = find_pc_psymtab (pc); + if (!psymtab) + return 0; + + best_pc = psymtab->textlow - 1; + + for (p = static_psymbols.list + psymtab->statics_offset; + (p - (static_psymbols.list + psymtab->statics_offset) + < psymtab->n_static_syms); + p++) + if (SYMBOL_NAMESPACE (p) == VAR_NAMESPACE + && SYMBOL_CLASS (p) == LOC_BLOCK + && pc >= SYMBOL_VALUE (p) + && SYMBOL_VALUE (p) > best_pc) + { + best_pc = SYMBOL_VALUE (p); + best = p; + } + if (best_pc == psymtab->textlow - 1) + return 0; + return best; +} + + +static struct symbol *lookup_block_symbol (); + +/* Find the definition for a specified symbol name NAME + in namespace NAMESPACE, visible from lexical block BLOCK. + Returns the struct symbol pointer, or zero if no symbol is found. + C++: if IS_A_FIELD_OF_THIS is nonzero on entry, check to see if + NAME is a field of the current implied argument `this'. If so set + *IS_A_FIELD_OF_THIS to 1, otherwise set it to zero. + BLOCK_FOUND is set to the block in which NAME is found (in the case of + a field of `this', value_of_this sets BLOCK_FOUND to the proper value.) */ + +struct symbol * +lookup_symbol (name, block, namespace, is_a_field_of_this) + char *name; + register struct block *block; + enum namespace namespace; + int *is_a_field_of_this; +{ + register int i, n; + register struct symbol *sym; + register struct symtab *s; + register struct partial_symtab *ps; + register struct partial_symbol *psym; + struct blockvector *bv; + + /* Search specified block and its superiors. */ + + while (block != 0) + { + sym = lookup_block_symbol (block, name, namespace); + if (sym) + { + block_found = block; + return sym; + } + block = BLOCK_SUPERBLOCK (block); + } + + /* C++: If requested to do so by the caller, + check to see if NAME is a field of `this'. */ + if (is_a_field_of_this) + { + struct value *v = value_of_this (0); + + *is_a_field_of_this = 0; + if (v && check_field (v, name)) + { + *is_a_field_of_this = 1; + return 0; + } + } + + /* Now search all global blocks. Do the symtab's first, then + check the psymtab's */ + + for (s = symtab_list; s; s = s->next) + { + bv = BLOCKVECTOR (s); + block = BLOCKVECTOR_BLOCK (bv, 0); + sym = lookup_block_symbol (block, name, namespace); + if (sym) + { + block_found = block; + return sym; + } + } + + /* Check for the possibility of the symbol being a global function + that is stored on the misc function vector. Eventually, all + global symbols might be resolved in this way. */ + + if (namespace == VAR_NAMESPACE) + { + int index = lookup_misc_func (name); + + if (index == -1) + { /* Look for a mangled C++ name for NAME. */ + int name_len = strlen (name); + for (index = misc_function_count; --index >= 0; ) + /* Assume orginal name is prefix of mangled name. */ + if (!strncmp (misc_function_vector[index].name, name, name_len)) + { + char *demangled = + cplus_demangle(misc_function_vector[index].name, -1); + if (demangled != NULL) + { + int cond = strcmp (demangled, name); + free (demangled); + if (!cond) + break; + } + } + /* Loop terminates on no match with index == -1. */ + } + + if (index != -1) + { + ps = find_pc_psymtab (misc_function_vector[index].address); + if (ps && !ps->readin) + { + s = psymtab_to_symtab (ps); + bv = BLOCKVECTOR (s); + block = BLOCKVECTOR_BLOCK (bv, 0); + sym = lookup_block_symbol (block, name, namespace); + /* sym == 0 if symbol was found in the psymtab but not + in the symtab. + Return 0 to use the misc_function definition of "foo_". + + This happens for Fortran "foo_" symbols, + which are "foo" in the symtab. + + This can also happen if "asm" is used to make a + regular symbol but not a debugging symbol, e.g. + asm(".globl _main"); + asm("_main:"); + */ + + return sym; + } + } + } + + if (psym = lookup_partial_symbol (name, 1, namespace)) + { + ps = psym->pst; + s = psymtab_to_symtab(ps); + bv = BLOCKVECTOR (s); + block = BLOCKVECTOR_BLOCK (bv, 0); + sym = lookup_block_symbol (block, name, namespace); + if (!sym) + fatal ("Internal: global symbol found in psymtab but not in symtab"); + return sym; + } + + /* Now search all per-file blocks. + Not strictly correct, but more useful than an error. + Do the symtabs first, then check the psymtabs */ + + for (s = symtab_list; s; s = s->next) + { + bv = BLOCKVECTOR (s); + block = BLOCKVECTOR_BLOCK (bv, 1); + sym = lookup_block_symbol (block, name, namespace); + if (sym) + { + block_found = block; + return sym; + } + } + + if (psym = lookup_partial_symbol(name, 0, namespace)) + { + ps = psym->pst; + s = psymtab_to_symtab(ps); + bv = BLOCKVECTOR (s); + block = BLOCKVECTOR_BLOCK (bv, 1); + sym = lookup_block_symbol (block, name, namespace); + if (!sym) + fatal ("Internal: static symbol found in psymtab but not in symtab"); + return sym; + } + + return 0; +} + +/* Look, in partial_symtab PST, for symbol NAME. Check the global + symbols if GLOBAL, the static symbols if not */ + +static struct partial_symbol * +lookup_partial_symbol (name, global, namespace) + register char *name; + register int global; + register enum namespace namespace; +{ + register struct partial_symbol *start, *psym; + register struct partial_symbol *top, *bottom, *center; + register struct partial_symtab *pst; + register int length; + + if (global) + { + start = global_psymbols.list; + length = global_psymbols.next - start; + } + else + { + start = static_psymbols.list; + length = static_psymbols.next - start; + } + + if (!length) + return (struct partial_symbol *) 0; + + /* Binary search. This search is guarranteed to end with center + pointing at the earliest partial symbol with the correct + name. At that point *all* partial symbols with that name + will be checked against the correct namespace. */ + bottom = start; + top = start + length - 1; + while (top > bottom) + { + center = bottom + (top - bottom) / 2; + + assert (center < top); + + if (strcmp (SYMBOL_NAME (center), name) >= 0) + top = center; + else + bottom = center + 1; + } + assert (top == bottom); + + while (strcmp (SYMBOL_NAME (top), name) == 0) + { + if (!top->pst->readin && SYMBOL_NAMESPACE (top) == namespace) + return top; + top ++; + } + + return (struct partial_symbol *) 0; +} + +/* Look for a symbol in block BLOCK. */ + +static struct symbol * +lookup_block_symbol (block, name, namespace) + register struct block *block; + char *name; + enum namespace namespace; +{ + register int bot, top, inc; + register struct symbol *sym, *parameter_sym; + + top = BLOCK_NSYMS (block); + bot = 0; + + /* If the blocks's symbols were sorted, start with a binary search. */ + + if (BLOCK_SHOULD_SORT (block)) + { + /* First, advance BOT to not far before + the first symbol whose name is NAME. */ + + while (1) + { + inc = (top - bot + 1); + /* No need to keep binary searching for the last few bits worth. */ + if (inc < 4) + break; + inc = (inc >> 1) + bot; + sym = BLOCK_SYM (block, inc); + if (SYMBOL_NAME (sym)[0] < name[0]) + bot = inc; + else if (SYMBOL_NAME (sym)[0] > name[0]) + top = inc; + else if (strcmp (SYMBOL_NAME (sym), name) < 0) + bot = inc; + else + top = inc; + } + + /* Now scan forward until we run out of symbols, + find one whose name is greater than NAME, + or find one we want. + If there is more than one symbol with the right name and namespace, + we return the first one. dbxread.c is careful to make sure + that if one is a register then it comes first. */ + + top = BLOCK_NSYMS (block); + while (bot < top) + { + sym = BLOCK_SYM (block, bot); + inc = SYMBOL_NAME (sym)[0] - name[0]; + if (inc == 0) + inc = strcmp (SYMBOL_NAME (sym), name); + if (inc == 0 && SYMBOL_NAMESPACE (sym) == namespace) + return sym; + if (inc > 0) + return 0; + bot++; + } + return 0; + } + + /* Here if block isn't sorted. + This loop is equivalent to the loop above, + but hacked greatly for speed. + + Note that parameter symbols do not always show up last in the + list; this loop makes sure to take anything else other than + parameter symbols first; it only uses parameter symbols as a + last resort. Note that this only takes up extra computation + time on a match. */ + + parameter_sym = (struct symbol *) 0; + top = BLOCK_NSYMS (block); + inc = name[0]; + while (bot < top) + { + sym = BLOCK_SYM (block, bot); + if (SYMBOL_NAME (sym)[0] == inc + && !strcmp (SYMBOL_NAME (sym), name) + && SYMBOL_NAMESPACE (sym) == namespace) + { + if (SYMBOL_CLASS (sym) == LOC_ARG + || SYMBOL_CLASS (sym) == LOC_REF_ARG + || SYMBOL_CLASS (sym) == LOC_REGPARM) + parameter_sym = sym; + else + return sym; + } + bot++; + } + return parameter_sym; /* Will be 0 if not found. */ +} + +/* Return the symbol for the function which contains a specified + lexical block, described by a struct block BL. */ + +struct symbol * +block_function (bl) + struct block *bl; +{ + while (BLOCK_FUNCTION (bl) == 0 && BLOCK_SUPERBLOCK (bl) != 0) + bl = BLOCK_SUPERBLOCK (bl); + + return BLOCK_FUNCTION (bl); +} + +/* Subroutine of find_pc_line */ + +struct symtab * +find_pc_symtab (pc) + register CORE_ADDR pc; +{ + register struct block *b; + struct blockvector *bv; + register struct symtab *s; + register struct partial_symtab *ps; + + /* Search all symtabs for one whose file contains our pc */ + + for (s = symtab_list; s; s = s->next) + { + bv = BLOCKVECTOR (s); + b = BLOCKVECTOR_BLOCK (bv, 0); + if (BLOCK_START (b) <= pc + && BLOCK_END (b) > pc) + break; + } + + if (!s) + { + ps = find_pc_psymtab (pc); + if (ps && ps->readin) + fatal ("Internal error: pc in read in psymtab, but not in symtab."); + + if (ps) + s = psymtab_to_symtab (ps); + } + + return s; +} + +/* Find the source file and line number for a given PC value. + Return a structure containing a symtab pointer, a line number, + and a pc range for the entire source line. + The value's .pc field is NOT the specified pc. + NOTCURRENT nonzero means, if specified pc is on a line boundary, + use the line that ends there. Otherwise, in that case, the line + that begins there is used. */ + +struct symtab_and_line +find_pc_line (pc, notcurrent) + CORE_ADDR pc; + int notcurrent; +{ + struct symtab *s; + register struct linetable *l; + register int len; + register int i; + register struct linetable_entry *item; + struct symtab_and_line value; + struct blockvector *bv; + + /* Info on best line seen so far, and where it starts, and its file. */ + + int best_line = 0; + CORE_ADDR best_pc = 0; + CORE_ADDR best_end = 0; + struct symtab *best_symtab = 0; + + /* Store here the first line number + of a file which contains the line at the smallest pc after PC. + If we don't find a line whose range contains PC, + we will use a line one less than this, + with a range from the start of that file to the first line's pc. */ + int alt_line = 0; + CORE_ADDR alt_pc = 0; + struct symtab *alt_symtab = 0; + + /* Info on best line seen in this file. */ + + int prev_line; + CORE_ADDR prev_pc; + + /* Info on first line of this file. */ + + int first_line; + CORE_ADDR first_pc; + + /* If this pc is not from the current frame, + it is the address of the end of a call instruction. + Quite likely that is the start of the following statement. + But what we want is the statement containing the instruction. + Fudge the pc to make sure we get that. */ + + if (notcurrent) pc -= 1; + + s = find_pc_symtab (pc); + if (s == 0) + { + value.symtab = 0; + value.line = 0; + value.pc = pc; + value.end = 0; + return value; + } + + bv = BLOCKVECTOR (s); + + /* Look at all the symtabs that share this blockvector. + They all have the same apriori range, that we found was right; + but they have different line tables. */ + + for (; s && BLOCKVECTOR (s) == bv; s = s->next) + { + /* Find the best line in this symtab. */ + l = LINETABLE (s); + len = l->nitems; + prev_line = -1; + first_line = -1; + for (i = 0; i < len; i++) + { + item = &(l->item[i]); + + if (first_line < 0) + { + first_line = item->line; + first_pc = item->pc; + } + /* Return the last line that did not start after PC. */ + if (pc >= item->pc) + { + prev_line = item->line; + prev_pc = item->pc; + } + else + break; + } + + /* Is this file's best line closer than the best in the other files? + If so, record this file, and its best line, as best so far. */ + if (prev_line >= 0 && prev_pc > best_pc) + { + best_pc = prev_pc; + best_line = prev_line; + best_symtab = s; + if (i < len) + best_end = item->pc; + else + best_end = 0; + } + /* Is this file's first line closer than the first lines of other files? + If so, record this file, and its first line, as best alternate. */ + if (first_line >= 0 && first_pc > pc + && (alt_pc == 0 || first_pc < alt_pc)) + { + alt_pc = first_pc; + alt_line = first_line; + alt_symtab = s; + } + } + if (best_symtab == 0) + { + value.symtab = alt_symtab; + value.line = alt_line - 1; + value.pc = BLOCK_END (BLOCKVECTOR_BLOCK (bv, 0)); + value.end = alt_pc; + } + else + { + value.symtab = best_symtab; + value.line = best_line; + value.pc = best_pc; + value.end = (best_end ? best_end + : (alt_pc ? alt_pc + : BLOCK_END (BLOCKVECTOR_BLOCK (bv, 0)))); + } + return value; +} + +/* Find the PC value for a given source file and line number. + Returns zero for invalid line number. + The source file is specified with a struct symtab. */ + +CORE_ADDR +find_line_pc (symtab, line) + struct symtab *symtab; + int line; +{ + register struct linetable *l; + register int index; + int dummy; + + if (symtab == 0) + return 0; + l = LINETABLE (symtab); + index = find_line_common(l, line, &dummy); + return index ? l->item[index].pc : 0; +} + +/* Find the range of pc values in a line. + Store the starting pc of the line into *STARTPTR + and the ending pc (start of next line) into *ENDPTR. + Returns 1 to indicate success. + Returns 0 if could not find the specified line. */ + +int +find_line_pc_range (symtab, thisline, startptr, endptr) + struct symtab *symtab; + int thisline; + CORE_ADDR *startptr, *endptr; +{ + register struct linetable *l; + register int index; + int exact_match; /* did we get an exact linenumber match */ + register CORE_ADDR prev_pc; + CORE_ADDR last_pc; + + if (symtab == 0) + return 0; + + l = LINETABLE (symtab); + index = find_line_common (l, thisline, &exact_match); + if (index) + { + *startptr = l->item[index].pc; + /* If we have not seen an entry for the specified line, + assume that means the specified line has zero bytes. */ + if (!exact_match || index == l->nitems-1) + *endptr = *startptr; + else + /* Perhaps the following entry is for the following line. + It's worth a try. */ + if (l->item[index+1].line == thisline + 1) + *endptr = l->item[index+1].pc; + else + *endptr = find_line_pc (symtab, thisline+1); + return 1; + } + + return 0; +} + +/* Given a line table and a line number, return the index into the line + table for the pc of the nearest line whose number is >= the specified one. + Return 0 if none is found. The value is never zero is it is an index. + + Set *EXACT_MATCH nonzero if the value returned is an exact match. */ + +static int +find_line_common (l, lineno, exact_match) + register struct linetable *l; + register int lineno; + int *exact_match; +{ + register int i; + register int len; + + /* BEST is the smallest linenumber > LINENO so far seen, + or 0 if none has been seen so far. + BEST_INDEX identifies the item for it. */ + + int best_index = 0; + int best = 0; + + int nextline = -1; + + if (lineno <= 0) + return 0; + + len = l->nitems; + for (i = 0; i < len; i++) + { + register struct linetable_entry *item = &(l->item[i]); + + if (item->line == lineno) + { + *exact_match = 1; + return i; + } + + if (item->line > lineno && (best == 0 || item->line < best)) + { + best = item->line; + best_index = i; + } + } + + /* If we got here, we didn't get an exact match. */ + + *exact_match = 0; + return best_index; +} + +int +find_pc_line_pc_range (pc, startptr, endptr) + CORE_ADDR pc; + CORE_ADDR *startptr, *endptr; +{ + struct symtab_and_line sal; + sal = find_pc_line (pc, 0); + *startptr = sal.pc; + *endptr = sal.end; + return sal.symtab != 0; +} + +/* Parse a string that specifies a line number. + Pass the address of a char * variable; that variable will be + advanced over the characters actually parsed. + + The string can be: + + LINENUM -- that line number in current file. PC returned is 0. + FILE:LINENUM -- that line in that file. PC returned is 0. + FUNCTION -- line number of openbrace of that function. + PC returned is the start of the function. + FILE:FUNCTION -- likewise, but prefer functions in that file. + *EXPR -- line in which address EXPR appears. + + FUNCTION may be an undebuggable function found in misc_function_vector. + + If the argument FUNFIRSTLINE is nonzero, we want the first line + of real code inside a function when a function is specified. + + DEFAULT_SYMTAB specifies the file to use if none is specified. + It defaults to current_source_symtab. + DEFAULT_LINE specifies the line number to use for relative + line numbers (that start with signs). Defaults to current_source_line. + + Note that it is possible to return zero for the symtab + if no file is validly specified. Callers must check that. + Also, the line number returned may be invalid. */ + +struct symtabs_and_lines +decode_line_1 (argptr, funfirstline, default_symtab, default_line) + char **argptr; + int funfirstline; + struct symtab *default_symtab; + int default_line; +{ + struct symtabs_and_lines decode_line_2 (); + struct symtabs_and_lines values; + struct symtab_and_line value; + register char *p, *p1; + register struct symtab *s; + register struct symbol *sym; + register CORE_ADDR pc; + register int i; + char *copy; + struct symbol *sym_class; + char *class_name, *method_name, *phys_name; + int method_counter; + int i1; + struct symbol **sym_arr; + struct type *t, *field; + char **physnames; + + /* Defaults have defaults. */ + + if (default_symtab == 0) + { + default_symtab = current_source_symtab; + default_line = current_source_line; + } + + /* See if arg is *PC */ + + if (**argptr == '*') + { + (*argptr)++; + pc = parse_and_eval_address_1 (argptr); + values.sals = (struct symtab_and_line *) + malloc (sizeof (struct symtab_and_line)); + values.nelts = 1; + values.sals[0] = find_pc_line (pc, 0); + values.sals[0].pc = pc; + return values; + } + + /* Maybe arg is FILE : LINENUM or FILE : FUNCTION */ + + s = 0; + + for (p = *argptr; *p; p++) + { + if (p[0] == ':' || p[0] == ' ' || p[0] == '\t') + break; + } + while (p[0] == ' ' || p[0] == '\t') p++; + + if (p[0] == ':') + { + + /* C++ */ + if (p[1] ==':') + { + /* Extract the class name. */ + p1 = p; + while (p != *argptr && p[-1] == ' ') --p; + copy = (char *) alloca (p - *argptr + 1); + bcopy (*argptr, copy, p - *argptr); + copy[p - *argptr] = 0; + + /* Discard the class name from the arg. */ + p = p1 + 2; + while (*p == ' ' || *p == '\t') p++; + *argptr = p; + + sym_class = lookup_symbol (copy, 0, STRUCT_NAMESPACE, 0); + + if (sym_class && + (TYPE_CODE (SYMBOL_TYPE (sym_class)) == TYPE_CODE_STRUCT + || TYPE_CODE (SYMBOL_TYPE (sym_class)) == TYPE_CODE_UNION)) + { + /* Arg token is not digits => try it as a function name + Find the next token (everything up to end or next whitespace). */ + p = *argptr; + while (*p && *p != ' ' && *p != '\t' && *p != ',' && *p !=':') p++; + copy = (char *) alloca (p - *argptr + 1); + bcopy (*argptr, copy, p - *argptr); + copy[p - *argptr] = '\0'; + + /* no line number may be specified */ + while (*p == ' ' || *p == '\t') p++; + *argptr = p; + + sym = 0; + i1 = 0; /* counter for the symbol array */ + t = SYMBOL_TYPE (sym_class); + sym_arr = (struct symbol **) alloca(TYPE_NFN_FIELDS_TOTAL (t) * sizeof(struct symbol*)); + physnames = (char **) alloca (TYPE_NFN_FIELDS_TOTAL (t) * sizeof(char*)); + + if (destructor_name_p (copy, t)) + { + /* destructors are a special case. */ + struct fn_field *f = TYPE_FN_FIELDLIST1 (t, 0); + int len = TYPE_FN_FIELDLIST_LENGTH (t, 0) - 1; + phys_name = TYPE_FN_FIELD_PHYSNAME (f, len); + physnames[i1] = (char *)alloca (strlen (phys_name) + 1); + strcpy (physnames[i1], phys_name); + sym_arr[i1] = lookup_symbol (phys_name, SYMBOL_BLOCK_VALUE (sym_class), VAR_NAMESPACE, 0); + if (sym_arr[i1]) i1++; + } + else while (t) + { + class_name = TYPE_NAME (t); + /* Ignore this class if it doesn't have a name. + This prevents core dumps, but is just a workaround + because we might not find the function in + certain cases, such as + struct D {virtual int f();} + struct C : D {virtual int g();} + (in this case g++ 1.35.1- does not put out a name + for D as such, it defines type 19 (for example) in + the same stab as C, and then does a + .stabs "D:T19" and a .stabs "D:t19". + Thus + "break C::f" should not be looking for field f in + the class named D, + but just for the field f in the baseclasses of C + (no matter what their names). + + However, I don't know how to replace the code below + that depends on knowing the name of D. */ + if (class_name) + { + /* We just want the class name. In the context + of C++, stripping off "struct " is always + sensible. */ + if (strncmp("struct ", class_name, 7) == 0) + class_name += 7; + if (strncmp("union ", class_name, 6) == 0) + class_name += 6; + + sym_class = lookup_symbol (class_name, 0, STRUCT_NAMESPACE, 0); + for (method_counter = TYPE_NFN_FIELDS (SYMBOL_TYPE (sym_class)) - 1; + method_counter >= 0; + --method_counter) + { + int field_counter; + struct fn_field *f = + TYPE_FN_FIELDLIST1 (SYMBOL_TYPE (sym_class), method_counter); + + method_name = TYPE_FN_FIELDLIST_NAME (SYMBOL_TYPE (sym_class), method_counter); + if (!strcmp (copy, method_name)) + /* Find all the fields with that name. */ + for (field_counter = TYPE_FN_FIELDLIST_LENGTH (SYMBOL_TYPE (sym_class), method_counter) - 1; + field_counter >= 0; + --field_counter) + { + phys_name = TYPE_FN_FIELD_PHYSNAME (f, field_counter); + physnames[i1] = (char*) alloca (strlen (phys_name) + 1); + strcpy (physnames[i1], phys_name); + sym_arr[i1] = lookup_symbol (phys_name, SYMBOL_BLOCK_VALUE (sym_class), VAR_NAMESPACE, 0); + if (sym_arr[i1]) i1++; + } + } + } + if (TYPE_N_BASECLASSES (t)) + t = TYPE_BASECLASS(t, 1); + else + break; + } + + if (i1 == 1) + { + /* There is exactly one field with that name. */ + sym = sym_arr[0]; + + if (sym && SYMBOL_CLASS (sym) == LOC_BLOCK) + { + /* Arg is the name of a function */ + pc = BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) + FUNCTION_START_OFFSET; + if (funfirstline) + SKIP_PROLOGUE (pc); + values.sals = (struct symtab_and_line *)malloc (sizeof (struct symtab_and_line)); + values.nelts = 1; + values.sals[0] = find_pc_line (pc, 0); + values.sals[0].pc = (values.sals[0].end && values.sals[0].pc != pc) ? values.sals[0].end : pc; + } + else + { + values.nelts = 0; + } + return values; + } + if (i1 > 0) + { + /* There is more than one field with that name + (overloaded). Ask the user which one to use. */ + return decode_line_2 (argptr, sym_arr, physnames, + i1, funfirstline); + } + else + error ("that class does not have any method named %s",copy); + } + else + error("no class, struct, or union named %s", copy ); + } + /* end of C++ */ + + + /* Extract the file name. */ + p1 = p; + while (p != *argptr && p[-1] == ' ') --p; + copy = (char *) alloca (p - *argptr + 1); + bcopy (*argptr, copy, p - *argptr); + copy[p - *argptr] = 0; + + /* Find that file's data. */ + s = lookup_symtab (copy); + if (s == 0) + { + if (symtab_list == 0 && partial_symtab_list == 0) + error ("No symbol table is loaded. Use the \"symbol-file\" command."); + error ("No source file named %s.", copy); + } + + /* Discard the file name from the arg. */ + p = p1 + 1; + while (*p == ' ' || *p == '\t') p++; + *argptr = p; + } + + /* S is specified file's symtab, or 0 if no file specified. + arg no longer contains the file name. */ + + /* Check whether arg is all digits (and sign) */ + + p = *argptr; + if (*p == '-' || *p == '+') p++; + while (*p >= '0' && *p <= '9') + p++; + + if (p != *argptr && (*p == 0 || *p == ' ' || *p == '\t' || *p == ',')) + { + /* We found a token consisting of all digits -- at least one digit. */ + enum sign {none, plus, minus} sign = none; + + /* This is where we need to make sure that we have good defaults. + We must guarrantee that this section of code is never executed + when we are called with just a function name, since + select_source_symtab calls us with such an argument */ + + if (s == 0 && default_symtab == 0) + { + if (symtab_list == 0 && partial_symtab_list == 0) + error ("No symbol table is loaded. Use the \"symbol-file\" command."); + select_source_symtab (0); + default_symtab = current_source_symtab; + default_line = current_source_line; + } + + if (**argptr == '+') + sign = plus, (*argptr)++; + else if (**argptr == '-') + sign = minus, (*argptr)++; + value.line = atoi (*argptr); + switch (sign) + { + case plus: + if (p == *argptr) + value.line = 5; + if (s == 0) + value.line = default_line + value.line; + break; + case minus: + if (p == *argptr) + value.line = 15; + if (s == 0) + value.line = default_line - value.line; + else + value.line = 1; + break; + } + + while (*p == ' ' || *p == '\t') p++; + *argptr = p; + if (s == 0) + s = default_symtab; + value.symtab = s; + value.pc = 0; + values.sals = (struct symtab_and_line *)malloc (sizeof (struct symtab_and_line)); + values.sals[0] = value; + values.nelts = 1; + return values; + } + + /* Arg token is not digits => try it as a function name + Find the next token (everything up to end or next whitespace). */ + p = *argptr; + while (*p && *p != ' ' && *p != '\t' && *p != ',') p++; + copy = (char *) alloca (p - *argptr + 1); + bcopy (*argptr, copy, p - *argptr); + copy[p - *argptr] = 0; + while (*p == ' ' || *p == '\t') p++; + *argptr = p; + + /* Look up that token as a function. + If file specified, use that file's per-file block to start with. */ + + if (s == 0) + /* use current file as default if none is specified. */ + s = default_symtab; + + sym = lookup_symbol (copy, s ? BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), 1) : 0, + VAR_NAMESPACE, 0); + + if (sym && SYMBOL_CLASS (sym) == LOC_BLOCK) + { + /* Arg is the name of a function */ + pc = BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) + FUNCTION_START_OFFSET; + if (funfirstline) + SKIP_PROLOGUE (pc); + value = find_pc_line (pc, 0); +#ifdef PROLOGUE_FIRSTLINE_OVERLAP + /* Convex: no need to suppress code on first line, if any */ + value.pc = pc; +#else + value.pc = (value.end && value.pc != pc) ? value.end : pc; +#endif + values.sals = (struct symtab_and_line *)malloc (sizeof (struct symtab_and_line)); + values.sals[0] = value; + values.nelts = 1; + return values; + } + + if (sym) + error ("%s is not a function.", copy); + + if (symtab_list == 0 && partial_symtab_list == 0) + error ("No symbol table is loaded. Use the \"symbol-file\" command."); + + if ((i = lookup_misc_func (copy)) >= 0) + { + value.symtab = 0; + value.line = 0; + value.pc = misc_function_vector[i].address + FUNCTION_START_OFFSET; + if (funfirstline) + SKIP_PROLOGUE (value.pc); + values.sals = (struct symtab_and_line *)malloc (sizeof (struct symtab_and_line)); + values.sals[0] = value; + values.nelts = 1; + return values; + } + + error ("Function %s not defined.", copy); +} + +struct symtabs_and_lines +decode_line_spec (string, funfirstline) + char *string; + int funfirstline; +{ + struct symtabs_and_lines sals; + if (string == 0) + error ("Empty line specification."); + sals = decode_line_1 (&string, funfirstline, + current_source_symtab, current_source_line); + if (*string) + error ("Junk at end of line specification: %s", string); + return sals; +} + +/* Given a list of NELTS symbols in sym_arr (with corresponding + mangled names in physnames), return a list of lines to operate on + (ask user if necessary). */ +struct symtabs_and_lines +decode_line_2 (argptr, sym_arr, physnames, nelts, funfirstline) + char **argptr; + struct symbol *sym_arr[]; + char *physnames[]; + int nelts; + int funfirstline; +{ + char *getenv(); + struct symtabs_and_lines values, return_values; + register CORE_ADDR pc; + char *args, *arg1, *command_line_input (); + int i; + char *prompt; + + values.sals = (struct symtab_and_line *) alloca (nelts * sizeof(struct symtab_and_line)); + return_values.sals = (struct symtab_and_line *) malloc (nelts * sizeof(struct symtab_and_line)); + + i = 0; + printf("[0] cancel\n[1] all\n"); + while (i < nelts) + { + if (sym_arr[i] && SYMBOL_CLASS (sym_arr[i]) == LOC_BLOCK) + { + /* Arg is the name of a function */ + pc = BLOCK_START (SYMBOL_BLOCK_VALUE (sym_arr[i])) + + FUNCTION_START_OFFSET; + if (funfirstline) + SKIP_PROLOGUE (pc); + values.sals[i] = find_pc_line (pc, 0); + values.sals[i].pc = (values.sals[i].end && values.sals[i].pc != pc) ? values.sals[i].end : pc; + printf("[%d] file:%s; line number:%d\n", + (i+2), values.sals[i].symtab->filename, values.sals[i].line); + } + else printf ("?HERE\n"); + i++; + } + + if ((prompt = getenv ("PS2")) == NULL) + { + prompt = ">"; + } + printf("%s ",prompt); + fflush(stdout); + + args = command_line_input (0, 0); + + if (args == 0) + error_no_arg ("one or more choice numbers"); + + i = 0; + while (*args) + { + int num; + + arg1 = args; + while (*arg1 >= '0' && *arg1 <= '9') arg1++; + if (*arg1 && *arg1 != ' ' && *arg1 != '\t') + error ("Arguments must be choice numbers."); + + num = atoi (args); + + if (num == 0) + error ("cancelled"); + else if (num == 1) + { + bcopy (values.sals, return_values.sals, (nelts * sizeof(struct symtab_and_line))); + return_values.nelts = nelts; + return return_values; + } + + if (num > nelts + 2) + { + printf ("No choice number %d.\n", num); + } + else + { + num -= 2; + if (values.sals[num].pc) + { + return_values.sals[i++] = values.sals[num]; + values.sals[num].pc = 0; + } + else + { + printf ("duplicate request for %d ignored.\n", num); + } + } + + args = arg1; + while (*args == ' ' || *args == '\t') args++; + } + return_values.nelts = i; + return return_values; +} + +/* hash a symbol ("hashpjw" from Aho, Sethi & Ullman, p.436) */ + +int +hash_symbol(str) + register char *str; +{ + register unsigned int h = 0, g; + register unsigned char c; + + while (c = *(unsigned char *)str++) { + h = (h << 4) + c; + if (g = h & 0xf0000000) { + h = h ^ (g >> 24); + h = h ^ g; + } + } + return ((int)h); +} + +/* Return the index of misc function named NAME. */ + +int +lookup_misc_func (name) + register char *name; +{ + register int i = hash_symbol(name) & (MISC_FUNC_HASH_SIZE - 1); + + if (misc_function_vector == 0) + error("No symbol file"); + + i = misc_function_hash_tab[i]; + while (i >= 0) + { + if (strcmp(misc_function_vector[i].name, name) == 0) + break; + i = misc_function_vector[i].next; + } + return (i); +} + +/* + * Slave routine for sources_info. Force line breaks at ,'s. + */ +static void +output_source_filename (name, next) +char *name; +int next; +{ + static int column = 0; + + if (column != 0 && column + strlen (name) >= 70) + { + printf_filtered ("\n"); + column = 0; + } + else if (column != 0) + { + printf_filtered (" "); + column++; + } + printf_filtered ("%s", name); + column += strlen (name); + if (next) + { + printf_filtered (","); + column++; + } + + if (!next) column = 0; +} + +static void +sources_info () +{ + register struct symtab *s; + register struct partial_symtab *ps; + register int column = 0; + + if (symtab_list == 0 && partial_symtab_list == 0) + { + printf ("No symbol table is loaded.\n"); + return; + } + + printf_filtered ("Source files for which symbols have been read in:\n\n"); + + for (s = symtab_list; s; s = s->next) + output_source_filename (s->filename, s->next); + printf_filtered ("\n\n"); + + printf_filtered ("Source files for which symbols will be read in on demand:\n\n"); + + for (ps = partial_symtab_list; ps; ps = ps->next) + if (!ps->readin) + output_source_filename (ps->filename, ps->next); + printf_filtered ("\n"); +} + +/* List all symbols (if REGEXP is 0) or all symbols matching REGEXP. + If CLASS is zero, list all symbols except functions and type names. + If CLASS is 1, list only functions. + If CLASS is 2, list only type names. */ + +static void sort_block_syms (); + +static void +list_symbols (regexp, class) + char *regexp; + int class; +{ + register struct symtab *s; + register struct partial_symtab *ps; + register struct blockvector *bv; + struct blockvector *prev_bv = 0; + register struct block *b; + register int i, j; + register struct symbol *sym; + struct partial_symbol *psym, *bound; + char *val; + static char *classnames[] + = {"variable", "function", "type", "method"}; + int print_count = 0; + int found_in_file = 0; + + if (regexp) + if (val = (char *) re_comp (regexp)) + error ("Invalid regexp: %s", val); + + /* Search through the partial_symtab_list *first* for all symbols + matching the regexp. That way we don't have to reproduce all of + the machinery below. */ + for (psym = global_psymbols.list, bound = global_psymbols.next; ; + psym = static_psymbols.list, bound = static_psymbols.next) + { + for (; psym < bound; ++psym) + { + if (psym->pst->readin) + continue; + + QUIT; + /* If it would match (logic taken from loop below) + load the file and go on to the next one */ + if ((regexp == 0 || re_exec (SYMBOL_NAME (psym))) + && ((class == 0 && SYMBOL_CLASS (psym) != LOC_TYPEDEF + && SYMBOL_CLASS (psym) != LOC_BLOCK) + || (class == 1 && SYMBOL_CLASS (psym) == LOC_BLOCK) + || (class == 2 && SYMBOL_CLASS (psym) == LOC_TYPEDEF) + || (class == 3 && SYMBOL_CLASS (psym) == LOC_BLOCK))) + psymtab_to_symtab(psym->pst); + } + if (psym == static_psymbols.next) + break; + } + + /* Printout here so as to get after the "Reading in symbols" + messages which will be generated above. */ + printf_filtered (regexp + ? "All %ss matching regular expression \"%s\":\n" + : "All defined %ss:\n", + classnames[class], + regexp); + + /* Here, *if* the class is correct (function only, right now), we + should search through the misc function vector for symbols that + match and call find_pc_psymtab on them. If find_pc_psymtab returns + 0, don't worry about it (already read in or no debugging info). */ + + if (class == 1) + { + for (i = 0; i < misc_function_count; i++) + if (regexp == 0 || re_exec (misc_function_vector[i].name)) + { + ps = find_pc_psymtab (misc_function_vector[i].address); + if (ps && !ps->readin) + psymtab_to_symtab (ps); + } + } + + for (s = symtab_list; s; s = s->next) + { + found_in_file = 0; + bv = BLOCKVECTOR (s); + /* Often many files share a blockvector. + Scan each blockvector only once so that + we don't get every symbol many times. + It happens that the first symtab in the list + for any given blockvector is the main file. */ + if (bv != prev_bv) + for (i = 0; i < 2; i++) + { + b = BLOCKVECTOR_BLOCK (bv, i); + /* Skip the sort if this block is always sorted. */ + if (!BLOCK_SHOULD_SORT (b)) + sort_block_syms (b); + for (j = 0; j < BLOCK_NSYMS (b); j++) + { + QUIT; + sym = BLOCK_SYM (b, j); + if ((regexp == 0 || re_exec (SYMBOL_NAME (sym))) + && ((class == 0 && SYMBOL_CLASS (sym) != LOC_TYPEDEF + && SYMBOL_CLASS (sym) != LOC_BLOCK) + || (class == 1 && SYMBOL_CLASS (sym) == LOC_BLOCK) + || (class == 2 && SYMBOL_CLASS (sym) == LOC_TYPEDEF) + || (class == 3 && SYMBOL_CLASS (sym) == LOC_BLOCK))) + { + if (!found_in_file) + { + printf_filtered ("\nFile %s:\n", s->filename); + print_count += 2; + } + found_in_file = 1; + if (class != 2 && i == 1) + printf_filtered ("static "); + if (class == 2 + && SYMBOL_NAMESPACE (sym) != STRUCT_NAMESPACE) + printf_filtered ("typedef "); + + if (class < 3) + { + type_print (SYMBOL_TYPE (sym), + (SYMBOL_CLASS (sym) == LOC_TYPEDEF + ? "" : SYMBOL_NAME (sym)), + stdout, 0); + + if (class == 2 + && SYMBOL_NAMESPACE (sym) != STRUCT_NAMESPACE + && (TYPE_NAME ((SYMBOL_TYPE (sym))) == 0 + || 0 != strcmp (TYPE_NAME ((SYMBOL_TYPE (sym))), + SYMBOL_NAME (sym)))) + printf_filtered (" %s", SYMBOL_NAME (sym)); + + printf_filtered (";\n"); + } + else + { +# if 0 + char buf[1024]; + type_print_base (TYPE_FN_FIELD_TYPE(t, i), stdout, 0, 0); + type_print_varspec_prefix (TYPE_FN_FIELD_TYPE(t, i), stdout, 0); + sprintf (buf, " %s::", TYPE_NAME (t)); + type_print_method_args (TYPE_FN_FIELD_ARGS (t, i), buf, name, stdout); +# endif + } + } + } + } + prev_bv = bv; + } +} + +static void +variables_info (regexp) + char *regexp; +{ + list_symbols (regexp, 0); +} + +static void +functions_info (regexp) + char *regexp; +{ + list_symbols (regexp, 1); +} + +static void +types_info (regexp) + char *regexp; +{ + list_symbols (regexp, 2); +} + +#if 0 +/* Tiemann says: "info methods was never implemented." */ +static void +methods_info (regexp) + char *regexp; +{ + list_symbols (regexp, 3); +} +#endif /* 0 */ + +/* Call sort_block_syms to sort alphabetically the symbols of one block. */ + +static int +compare_symbols (s1, s2) + struct symbol **s1, **s2; +{ + /* Names that are less should come first. */ + register int namediff = strcmp (SYMBOL_NAME (*s1), SYMBOL_NAME (*s2)); + if (namediff != 0) return namediff; + /* For symbols of the same name, registers should come first. */ + return ((SYMBOL_CLASS (*s2) == LOC_REGISTER) + - (SYMBOL_CLASS (*s1) == LOC_REGISTER)); +} + +static void +sort_block_syms (b) + register struct block *b; +{ + qsort (&BLOCK_SYM (b, 0), BLOCK_NSYMS (b), + sizeof (struct symbol *), compare_symbols); +} + +/* Initialize the standard C scalar types. */ + +static +struct type * +init_type (code, length, uns, name) + enum type_code code; + int length, uns; + char *name; +{ + register struct type *type; + + type = (struct type *) xmalloc (sizeof (struct type)); + bzero (type, sizeof *type); + TYPE_MAIN_VARIANT (type) = type; + TYPE_CODE (type) = code; + TYPE_LENGTH (type) = length; + TYPE_FLAGS (type) = uns ? TYPE_FLAG_UNSIGNED : 0; + TYPE_FLAGS (type) |= TYPE_FLAG_PERM; + TYPE_NFIELDS (type) = 0; + TYPE_NAME (type) = name; + + /* C++ fancies. */ + TYPE_NFN_FIELDS (type) = 0; + TYPE_N_BASECLASSES (type) = 0; + TYPE_BASECLASSES (type) = 0; + return type; +} + +/* Return Nonzero if block a is lexically nested within block b, + or if a and b have the same pc range. + Return zero otherwise. */ +int +contained_in (a, b) + struct block *a, *b; +{ + if (!a || !b) + return 0; + return a->startaddr >= b->startaddr && a->endaddr <= b->endaddr; +} + + +/* Helper routine for make_symbol_completion_list. */ + +int return_val_size, return_val_index; +char **return_val; + +void +completion_list_add_symbol (symname) + char *symname; +{ + if (return_val_index + 3 > return_val_size) + return_val = + (char **)xrealloc (return_val, + (return_val_size *= 2) * sizeof (char *)); + + return_val[return_val_index] = + (char *)xmalloc (1 + strlen (symname)); + + strcpy (return_val[return_val_index], symname); + + return_val[++return_val_index] = (char *)NULL; +} + +/* Return a NULL terminated array of all symbols (regardless of class) which + begin by matching TEXT. If the answer is no symbols, then the return value + is an array which contains only a NULL pointer. + + Problem: All of the symbols have to be copied because readline + frees them. I'm not going to worry about this; hopefully there + won't be that many. */ + +char ** +make_symbol_completion_list (text) + char *text; +{ + register struct symtab *s; + register struct partial_symtab *ps; + register struct blockvector *bv; + struct blockvector *prev_bv = 0; + register struct block *b, *surrounding_static_block; + extern struct block *get_selected_block (); + register int i, j; + register struct symbol *sym; + struct partial_symbol *psym; + + int text_len = strlen (text); + return_val_size = 100; + return_val_index = 0; + return_val = + (char **)xmalloc ((1 + return_val_size) *sizeof (char *)); + return_val[0] = (char *)NULL; + + /* Look through the partial symtabs for all symbols which begin + by matching TEXT. Add each one that you find to the list. */ + + for (ps = partial_symtab_list; ps; ps = ps->next) + { + /* If the psymtab's been read in we'll get it when we search + through the blockvector. */ + if (ps->readin) continue; + + for (psym = global_psymbols.list + ps->globals_offset; + psym < (global_psymbols.list + ps->globals_offset + + ps->n_global_syms); + psym++) + { + QUIT; /* If interrupted, then quit. */ + if ((strncmp (SYMBOL_NAME (psym), text, text_len) == 0)) + completion_list_add_symbol (SYMBOL_NAME (psym)); + } + + for (psym = static_psymbols.list + ps->statics_offset; + psym < (static_psymbols.list + ps->statics_offset + + ps->n_static_syms); + psym++) + { + QUIT; + if ((strncmp (SYMBOL_NAME (psym), text, text_len) == 0)) + completion_list_add_symbol (SYMBOL_NAME (psym)); + } + } + + /* At this point scan through the misc function vector and add each + symbol you find to the list. Eventually we want to ignore + anything that isn't a text symbol (everything else will be + handled by the psymtab code above). */ + + for (i = 0; i < misc_function_count; i++) + if (!strncmp (text, misc_function_vector[i].name, text_len)) + completion_list_add_symbol (misc_function_vector[i].name); + + /* Search upwards from currently selected frame (so that we can + complete on local vars. */ + for (b = get_selected_block (); b; b = BLOCK_SUPERBLOCK (b)) + { + if (!BLOCK_SUPERBLOCK (b)) + surrounding_static_block = b; /* For elmin of dups */ + + /* Also catch fields of types defined in this places which + match our text string. Only complete on types visible + from current context. */ + for (i = 0; i < BLOCK_NSYMS (b); i++) + { + register struct symbol *sym = BLOCK_SYM (b, i); + + if (!strncmp (SYMBOL_NAME (sym), text, text_len)) + completion_list_add_symbol (SYMBOL_NAME (sym)); + + if (SYMBOL_CLASS (sym) == LOC_TYPEDEF) + { + struct type *t = SYMBOL_TYPE (sym); + enum type_code c = TYPE_CODE (t); + + if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT) + for (j = 0; j < TYPE_NFIELDS (t); j++) + if (TYPE_FIELD_NAME (t, j) && + !strncmp (TYPE_FIELD_NAME (t, j), text, text_len)) + completion_list_add_symbol (TYPE_FIELD_NAME (t, j)); + } + } + } + + /* Go through the symtabs and check the externs and statics for + symbols which match. */ + + for (s = symtab_list; s; s = s->next) + { + struct block *b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), 0); + + for (i = 0; i < BLOCK_NSYMS (b); i++) + if (!strncmp (SYMBOL_NAME (BLOCK_SYM (b, i)), text, text_len)) + completion_list_add_symbol (SYMBOL_NAME (BLOCK_SYM (b, i))); + } + + for (s = symtab_list; s; s = s->next) + { + struct block *b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), 1); + + /* Don't do this block twice. */ + if (b == surrounding_static_block) continue; + + for (i = 0; i < BLOCK_NSYMS (b); i++) + if (!strncmp (SYMBOL_NAME (BLOCK_SYM (b, i)), text, text_len)) + completion_list_add_symbol (SYMBOL_NAME (BLOCK_SYM (b, i))); + } + + return (return_val); +} + +void +_initialize_symtab () +{ + add_info ("variables", variables_info, + "All global and static variable names, or those matching REGEXP."); + add_info ("functions", functions_info, + "All function names, or those matching REGEXP."); + add_info ("types", types_info, + "All types names, or those matching REGEXP."); +#if 0 + add_info ("methods", methods_info, + "All method names, or those matching REGEXP::REGEXP.\n\ +If the class qualifier is ommited, it is assumed to be the current scope.\n\ +If the first REGEXP is ommited, then all methods matching the second REGEXP\n\ +are listed."); +#endif + add_info ("sources", sources_info, + "Source files in the program."); + + obstack_init (symbol_obstack); + obstack_init (psymbol_obstack); + + builtin_type_void = init_type (TYPE_CODE_VOID, 1, 0, "void"); + + builtin_type_float = init_type (TYPE_CODE_FLT, sizeof (float), 0, "float"); + builtin_type_double = init_type (TYPE_CODE_FLT, sizeof (double), 0, "double"); + + builtin_type_char = init_type (TYPE_CODE_INT, sizeof (char), 0, "char"); + builtin_type_short = init_type (TYPE_CODE_INT, sizeof (short), 0, "short"); + builtin_type_long = init_type (TYPE_CODE_INT, sizeof (long), 0, "long"); + builtin_type_int = init_type (TYPE_CODE_INT, sizeof (int), 0, "int"); + + builtin_type_unsigned_char = init_type (TYPE_CODE_INT, sizeof (char), 1, "unsigned char"); + builtin_type_unsigned_short = init_type (TYPE_CODE_INT, sizeof (short), 1, "unsigned short"); + builtin_type_unsigned_long = init_type (TYPE_CODE_INT, sizeof (long), 1, "unsigned long"); + builtin_type_unsigned_int = init_type (TYPE_CODE_INT, sizeof (int), 1, "unsigned int"); +#ifdef LONG_LONG + builtin_type_long_long = + init_type (TYPE_CODE_INT, sizeof (long long), 0, "long long"); + builtin_type_unsigned_long_long = + init_type (TYPE_CODE_INT, sizeof (long long), 1, "unsigned long long"); +#endif +} + diff --git a/gnu/usr.bin/gdb/symtab.h b/gnu/usr.bin/gdb/symtab.h new file mode 100644 index 0000000000..fefed6001e --- /dev/null +++ b/gnu/usr.bin/gdb/symtab.h @@ -0,0 +1,384 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + * + * @(#)symtab.h 6.3 (Berkeley) 5/8/91 + */ + +/* Symbol table definitions for GDB. + Copyright (C) 1986, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include + +/* An obstack to hold objects that should be freed + when we load a new symbol table. + This includes the symbols made by dbxread + and the types that are not permanent. */ + +extern struct obstack *symbol_obstack; +extern struct obstack *psymbol_obstack; + +/* Some definitions and declarations to go with use of obstacks. */ +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free +extern char *xmalloc (); +extern void free (); + +/* gdb can know one or several symbol tables at the same time; + the ultimate intent is to have one for each separately-compiled module. + Each such symbol table is recorded by a struct symtab, and they + are all chained together. */ + +/* In addition, gdb can record any number of miscellaneous undebuggable + functions' addresses. In a system that appends _ to function names, + the _'s are removed from the names stored in this table. */ + +/* Actually, the misc function list is used to store *all* of the + global symbols (text, data, bss, and abs). It is sometimes used + to figure out what symtabs to read in. The "type" field appears + never to be used. */ + +enum misc_function_type {mf_unknown = 0, mf_text, mf_data, mf_bss, mf_abs}; + +struct misc_function +{ + char *name; + CORE_ADDR address; + int next; /* index of next in this hash bucket */ + unsigned char type; /* Really enum misc_function_type. */ +}; + +/* Address and length of the vector recording all misc function names/addresses. */ + +struct misc_function *misc_function_vector; +int misc_function_count; +#define MISC_FUNC_HASH_SIZE (2048) +int misc_function_hash_tab[MISC_FUNC_HASH_SIZE]; + +#include "symseg.h" + +/* Each source file is represented by a struct symtab. */ +/* These objects are chained through the `next' field. */ + +struct symtab + { + /* Chain of all existing symtabs. */ + struct symtab *next; + /* List of all symbol scope blocks for this symtab. */ + struct blockvector *blockvector; + /* Table mapping core addresses to line numbers for this file. */ + struct linetable *linetable; + /* Vector containing all types defined for this symtab. */ + struct typevector *typevector; + /* Name of this source file. */ + char *filename; + /* This component says how to free the data we point to: + free_contents => do a tree walk and free each object. + free_nothing => do nothing; some other symtab will free + the data this one uses. + free_linetable => free just the linetable. */ + enum free_code {free_nothing, free_contents, free_linetable} + free_code; + /* Pointer to one block of storage to be freed, if nonzero. */ + char *free_ptr; + /* Total number of lines found in source file. */ + int nlines; + /* Array mapping line number to character position. */ + int *line_charpos; + /* Language of this source file. */ + enum language language; + /* String of version information. May be zero. */ + char *version; + /* String of compilation information. May be zero. */ + char *compilation; + /* Offset within loader symbol table + of first local symbol for this file. */ + int ldsymoff; + /* Full name of file as found by searching the source path. + 0 if not yet known. */ + char *fullname; + }; + +/* + * Each source file that has not been fully read in is represented by + * a partial_symtab. This contains the information on where in the + * executable the debugging symbols for a specific file are, and a + * list of names of global symbols which are located in this file. + */ +struct partial_symtab +{ + /* Chain of all existing partial symtabs. */ + struct partial_symtab *next; + /* Name of the source file which this partial_symtab defines */ + char *filename; + /* Offset within loader symbol table of first local symbol for this + file and length (in bytes) of the section of the symbol table + devoted to this file's symbols (actually, the section bracketed + may contain more than just this files symbols + If ldsymlen is 0, the only reason for this things existence is + the dependency list below. Nothing else will happen when it is + read in. */ + int ldsymoff, ldsymlen; + /* Range of text addresses covered by this file; texthigh is the + beginning of the next section. */ + int textlow, texthigh; + /* Non-zero if the symtab corresponding to this psymtab has been + readin */ + unsigned char readin; + /* Array of pointers to all of the partial_symtab s which this one + depends one. Since this array can only be set to previous or + the current (?) psymtab, this dependency tree is guarranteed not + to have any loops. */ + struct partial_symtab **dependencies; + int number_of_dependencies; + /* Global symbol list. This list will be sorted after readin to + improve access. Binary search will be the usual method of + finding a symbol within it. globals_offset is an integer offset + within ps_globals */ + int globals_offset, n_global_syms; + /* Static symbol list. This list will *not* be sorted after readin; + to find a symbol in it, exhaustive search must be used. This is + reasonable because searches through this list will eventually + lead to either the read in of a files symbols for real (assumed + to take a *lot* of time; check) or an error (and we don't care + how long errors take). */ + int statics_offset, n_static_syms; +}; + +/* This is the list of struct symtab's that gdb considers current. */ + +struct symtab *symtab_list; + +/* This is the list of struct partial_symtab's that gdb may need to access */ + +struct partial_symtab *partial_symtab_list; + +/* This symtab variable specifies the current file for printing source lines */ + +struct symtab *current_source_symtab; + +/* This is the next line to print for listing source lines. */ + +int current_source_line; + +#define BLOCKLIST(symtab) (symtab)->blockvector +#define BLOCKVECTOR(symtab) (symtab)->blockvector + +#define TYPEVECTOR(symtab) (symtab)->typevector + +#define LINELIST(symtab) (symtab)->linetable +#define LINETABLE(symtab) (symtab)->linetable + +/* Macros normally used to access components of symbol table structures. */ + +#define BLOCKLIST_NBLOCKS(blocklist) (blocklist)->nblocks +#define BLOCKLIST_BLOCK(blocklist,n) (blocklist)->block[n] +#define BLOCKVECTOR_NBLOCKS(blocklist) (blocklist)->nblocks +#define BLOCKVECTOR_BLOCK(blocklist,n) (blocklist)->block[n] + +#define TYPEVECTOR_NTYPES(typelist) (typelist)->length +#define TYPEVECTOR_TYPE(typelist,n) (typelist)->type[n] + +#define BLOCK_START(bl) (bl)->startaddr +#define BLOCK_END(bl) (bl)->endaddr +#define BLOCK_NSYMS(bl) (bl)->nsyms +#define BLOCK_SYM(bl, n) (bl)->sym[n] +#define BLOCK_FUNCTION(bl) (bl)->function +#define BLOCK_SUPERBLOCK(bl) (bl)->superblock +#define BLOCK_GCC_COMPILED(bl) (bl)->gcc_compile_flag + +/* Nonzero if symbols of block BL should be sorted alphabetically. */ +#define BLOCK_SHOULD_SORT(bl) ((bl)->nsyms >= 40) + +#define SYMBOL_NAME(symbol) (symbol)->name +#define SYMBOL_NAMESPACE(symbol) (symbol)->namespace +#define SYMBOL_CLASS(symbol) (symbol)->class +#define SYMBOL_VALUE(symbol) (symbol)->value.value +#define SYMBOL_VALUE_BYTES(symbol) (symbol)->value.bytes +#define SYMBOL_BLOCK_VALUE(symbol) (symbol)->value.block +#define SYMBOL_TYPE(symbol) (symbol)->type + +/* Some macros for bitfields. */ +#define B_SET(a,x) (a[x>>5] |= (1 << (x&31))) +#define B_CLR(a,x) (a[x>>5] &= ~(1 << (x&31))) +#define B_TST(a,x) (a[x>>5] & (1 << (x&31))) + +#define TYPE_NAME(thistype) (thistype)->name +#define TYPE_TARGET_TYPE(thistype) (thistype)->target_type +#define TYPE_POINTER_TYPE(thistype) (thistype)->pointer_type +#define TYPE_REFERENCE_TYPE(thistype) (thistype)->reference_type +#define TYPE_FUNCTION_TYPE(thistype) (thistype)->function_type +#define TYPE_MAIN_VARIANT(thistype) (thistype)->main_variant +#define TYPE_NEXT_VARIANT(thistype) (thistype)->next_variant +#define TYPE_LENGTH(thistype) (thistype)->length +#define TYPE_FLAGS(thistype) (thistype)->flags +#define TYPE_UNSIGNED(thistype) ((thistype)->flags & TYPE_FLAG_UNSIGNED) +#define TYPE_CODE(thistype) (thistype)->code +#define TYPE_NFIELDS(thistype) (thistype)->nfields +#define TYPE_FIELDS(thistype) (thistype)->fields +/* C++ */ +#define TYPE_VPTR_BASETYPE(thistype) (thistype)->vptr_basetype +#define TYPE_DOMAIN_TYPE(thistype) (thistype)->vptr_basetype +#define TYPE_VPTR_FIELDNO(thistype) (thistype)->vptr_fieldno +#define TYPE_FN_FIELDS(thistype) (thistype)->fn_fields +#define TYPE_NFN_FIELDS(thistype) (thistype)->nfn_fields +#define TYPE_NFN_FIELDS_TOTAL(thistype) (thistype)->nfn_fields_total +#define TYPE_BASECLASSES(thistype) (thistype)->baseclasses +#define TYPE_ARG_TYPES(thistype) (thistype)->arg_types +#define TYPE_BASECLASS(thistype,index) (thistype)->baseclasses[index] +#define TYPE_N_BASECLASSES(thistype) (thistype)->n_baseclasses +#define TYPE_VIA_PUBLIC(thistype) ((thistype)->flags & TYPE_FLAG_VIA_PUBLIC) +#define TYPE_VIA_VIRTUAL(thistype) ((thistype)->flags & TYPE_FLAG_VIA_VIRTUAL) + +#define TYPE_FIELD(thistype, n) (thistype)->fields[n] +#define TYPE_FIELD_TYPE(thistype, n) (thistype)->fields[n].type +#define TYPE_FIELD_NAME(thistype, n) (thistype)->fields[n].name +#define TYPE_FIELD_VALUE(thistype, n) (* (int*) &(thistype)->fields[n].type) +#define TYPE_FIELD_BITPOS(thistype, n) (thistype)->fields[n].bitpos +#define TYPE_FIELD_BITSIZE(thistype, n) (thistype)->fields[n].bitsize +#define TYPE_FIELD_PACKED(thistype, n) (thistype)->fields[n].bitsize + +#define TYPE_FIELD_PRIVATE_BITS(thistype) (thistype)->private_field_bits +#define TYPE_FIELD_PROTECTED_BITS(thistype) (thistype)->protected_field_bits +#define SET_TYPE_FIELD_PRIVATE(thistype, n) B_SET ((thistype)->private_field_bits, (n)) +#define SET_TYPE_FIELD_PROTECTED(thistype, n) B_SET ((thistype)->protected_field_bits, (n)) +#define TYPE_FIELD_PRIVATE(thistype, n) B_TST((thistype)->private_field_bits, (n)) +#define TYPE_FIELD_PROTECTED(thistype, n) B_TST((thistype)->protected_field_bits, (n)) + +#define TYPE_HAS_DESTRUCTOR(thistype) ((thistype)->flags & TYPE_FLAG_HAS_DESTRUCTOR) +#define TYPE_HAS_CONSTRUCTOR(thistype) ((thistype)->flags & TYPE_FLAG_HAS_CONSTRUCTOR) + +#define TYPE_FIELD_STATIC(thistype, n) ((thistype)->fields[n].bitpos == -1) +#define TYPE_FIELD_STATIC_PHYSNAME(thistype, n) ((char *)(thistype)->fields[n].bitsize) + +#define TYPE_FN_FIELDLISTS(thistype) (thistype)->fn_fieldlists +#define TYPE_FN_FIELDLIST(thistype, n) (thistype)->fn_fieldlists[n] +#define TYPE_FN_FIELDLIST1(thistype, n) (thistype)->fn_fieldlists[n].fn_fields +#define TYPE_FN_FIELDLIST_NAME(thistype, n) (thistype)->fn_fieldlists[n].name +#define TYPE_FN_FIELDLIST_LENGTH(thistype, n) (thistype)->fn_fieldlists[n].length + +#define TYPE_FN_FIELD(thistype, n) (thistype)[n] +#define TYPE_FN_FIELD_NAME(thistype, n) (thistype)[n].name +#define TYPE_FN_FIELD_TYPE(thistype, n) (thistype)[n].type +#define TYPE_FN_FIELD_ARGS(thistype, n) (thistype)[n].args +#define TYPE_FN_FIELD_PHYSNAME(thistype, n) (thistype)[n].physname +#define TYPE_FN_FIELD_VIRTUAL_P(thistype, n) ((thistype)[n].voffset < 0) +#define TYPE_FN_FIELD_STATIC_P(thistype, n) ((thistype)[n].voffset > 0) +#define TYPE_FN_FIELD_VOFFSET(thistype, n) ((thistype)[n].voffset-1) + +#define TYPE_FN_PRIVATE_BITS(thistype) (thistype).private_fn_field_bits +#define TYPE_FN_PROTECTED_BITS(thistype) (thistype).protected_fn_field_bits +#define SET_TYPE_FN_PRIVATE(thistype, n) B_SET ((thistype).private_fn_field_bits, n) +#define SET_TYPE_FN_PROTECTED(thistype, n) B_SET ((thistype).protected_fn_field_bits, n) +#define TYPE_FN_PRIVATE(thistype, n) B_TST ((thistype).private_fn_field_bits, n) +#define TYPE_FN_PROTECTED(thistype, n) B_TST ((thistype).protected_fn_field_bits, n) + +/* Functions that work on the objects described above */ + +extern struct symtab *lookup_symtab (); +extern struct symbol *lookup_symbol (); +extern struct type *lookup_typename (); +extern struct type *lookup_unsigned_typename (); +extern struct type *lookup_struct (); +extern struct type *lookup_union (); +extern struct type *lookup_enum (); +extern struct type *lookup_struct_elt_type (); +extern struct type *lookup_pointer_type (); +extern struct type *lookup_function_type (); +extern struct type *lookup_basetype_type (); +extern struct type *create_array_type (); +extern struct symbol *block_function (); +extern struct symbol *find_pc_function (); +extern int find_pc_partial_function (); +extern struct partial_symtab *find_pc_psymtab (); +extern struct symtab *find_pc_symtab (); +extern struct partial_symbol *find_pc_psymbol (); +extern int find_pc_misc_function (); + +/* C++ stuff. */ +extern struct type *lookup_reference_type (); +extern struct type *lookup_member_type (); +extern struct type *lookup_class (); +/* end of C++ stuff. */ + +extern struct type *builtin_type_void; +extern struct type *builtin_type_char; +extern struct type *builtin_type_short; +extern struct type *builtin_type_int; +extern struct type *builtin_type_long; +extern struct type *builtin_type_unsigned_char; +extern struct type *builtin_type_unsigned_short; +extern struct type *builtin_type_unsigned_int; +extern struct type *builtin_type_unsigned_long; +extern struct type *builtin_type_float; +extern struct type *builtin_type_double; + +#ifdef LONG_LONG +extern struct type *builtin_type_long_long; +extern struct type *builtin_type_unsigned_long_long; + +#ifndef BUILTIN_TYPE_LONGEST +#define BUILTIN_TYPE_LONGEST builtin_type_long_long +#endif + +#ifndef BUILTIN_TYPE_UNSIGNED_LONGEST +#define BUILTIN_TYPE_UNSIGNED_LONGEST builtin_type_unsigned_long_long +#endif + +#else /* LONG_LONG */ + +#ifndef BUILTIN_TYPE_LONGEST +#define BUILTIN_TYPE_LONGEST builtin_type_long +#endif + +#ifndef BUILTIN_TYPE_UNSIGNED_LONGEST +#define BUILTIN_TYPE_UNSIGNED_LONGEST builtin_type_unsigned_long +#endif + +#endif + +struct symtab_and_line +{ + struct symtab *symtab; + int line; + CORE_ADDR pc; + CORE_ADDR end; +}; + +struct symtabs_and_lines +{ + struct symtab_and_line *sals; + int nelts; +}; + +/* Given a pc value, return line number it is in. + Second arg nonzero means if pc is on the boundary + use the previous statement's line number. */ + +struct symtab_and_line find_pc_line (); + +/* Given a string, return the line specified by it. + For commands like "list" and "breakpoint". */ + +struct symtabs_and_lines decode_line_spec (); +struct symtabs_and_lines decode_line_spec_1 (); +struct symtabs_and_lines decode_line_1 (); diff --git a/gnu/usr.bin/gdb/utils.c b/gnu/usr.bin/gdb/utils.c new file mode 100644 index 0000000000..b03f2bedec --- /dev/null +++ b/gnu/usr.bin/gdb/utils.c @@ -0,0 +1,1096 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + * + * $Header: /home/cvs/386BSD/src/usr.bin/gdb/utils.c,v 1.1.1.1 1993/06/12 14:52:20 rgrimes Exp $; + */ + +#ifndef lint +static char sccsid[] = "@(#)utils.c 6.4 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* General utility routines for GDB, the GNU debugger. + Copyright (C) 1986, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "param.h" + +#include +#include +#include +#include +#include +#include +#include "defs.h" +#ifdef HAVE_TERMIO +#include +#endif + +/* If this definition isn't overridden by the header files, assume + that isatty and fileno exist on this system. */ +#ifndef ISATTY +#define ISATTY(FP) (isatty (fileno (FP))) +#endif + +extern FILE *instream; + +void error (); +void fatal (); + +/* Chain of cleanup actions established with make_cleanup, + to be executed if an error happens. */ + +static struct cleanup *cleanup_chain; + +/* Nonzero means a quit has been requested. */ + +int quit_flag; + +/* Nonzero means quit immediately if Control-C is typed now, + rather than waiting until QUIT is executed. */ + +int immediate_quit; + +/* Add a new cleanup to the cleanup_chain, + and return the previous chain pointer + to be passed later to do_cleanups or discard_cleanups. + Args are FUNCTION to clean up with, and ARG to pass to it. */ + +struct cleanup * +make_cleanup (function, arg) + void (*function) (); + int arg; +{ + register struct cleanup *new + = (struct cleanup *) xmalloc (sizeof (struct cleanup)); + register struct cleanup *old_chain = cleanup_chain; + + new->next = cleanup_chain; + new->function = function; + new->arg = arg; + cleanup_chain = new; + + return old_chain; +} + +/* Discard cleanups and do the actions they describe + until we get back to the point OLD_CHAIN in the cleanup_chain. */ + +void +do_cleanups (old_chain) + register struct cleanup *old_chain; +{ + register struct cleanup *ptr; + while ((ptr = cleanup_chain) != old_chain) + { + (*ptr->function) (ptr->arg); + cleanup_chain = ptr->next; + free (ptr); + } +} + +/* Discard cleanups, not doing the actions they describe, + until we get back to the point OLD_CHAIN in the cleanup_chain. */ + +void +discard_cleanups (old_chain) + register struct cleanup *old_chain; +{ + register struct cleanup *ptr; + while ((ptr = cleanup_chain) != old_chain) + { + cleanup_chain = ptr->next; + free (ptr); + } +} + +/* Set the cleanup_chain to 0, and return the old cleanup chain. */ +struct cleanup * +save_cleanups () +{ + struct cleanup *old_chain = cleanup_chain; + + cleanup_chain = 0; + return old_chain; +} + +/* Restore the cleanup chain from a previously saved chain. */ +void +restore_cleanups (chain) + struct cleanup *chain; +{ + cleanup_chain = chain; +} + +/* This function is useful for cleanups. + Do + + foo = xmalloc (...); + old_chain = make_cleanup (free_current_contents, &foo); + + to arrange to free the object thus allocated. */ + +void +free_current_contents (location) + char **location; +{ + free (*location); +} + +/* Generally useful subroutines used throughout the program. */ + +/* Like malloc but get error if no storage available. */ + +char * +xmalloc (size) + long size; +{ + register char *val = (char *) malloc (size); + if (!val) + fatal ("virtual memory exhausted.", 0); + return val; +} + +/* Like realloc but get error if no storage available. */ + +char * +xrealloc (ptr, size) + char *ptr; + long size; +{ + register char *val = (char *) realloc (ptr, size); + if (!val) + fatal ("virtual memory exhausted.", 0); + return val; +} + +/* Print the system error message for errno, and also mention STRING + as the file name for which the error was encountered. + Then return to command level. */ + +void +perror_with_name (string) + char *string; +{ + extern int sys_nerr; + extern char *sys_errlist[]; + extern int errno; + char *err; + char *combined; + + if (errno < sys_nerr) + err = sys_errlist[errno]; + else + err = "unknown error"; + + combined = (char *) alloca (strlen (err) + strlen (string) + 3); + strcpy (combined, string); + strcat (combined, ": "); + strcat (combined, err); + + error ("%s.", combined); +} + +/* Print the system error message for ERRCODE, and also mention STRING + as the file name for which the error was encountered. */ + +void +print_sys_errmsg (string, errcode) + char *string; + int errcode; +{ + extern int sys_nerr; + extern char *sys_errlist[]; + char *err; + char *combined; + + if (errcode < sys_nerr) + err = sys_errlist[errcode]; + else + err = "unknown error"; + + combined = (char *) alloca (strlen (err) + strlen (string) + 3); + strcpy (combined, string); + strcat (combined, ": "); + strcat (combined, err); + + printf ("%s.\n", combined); +} + +void +quit () +{ +#ifdef HAVE_TERMIO + ioctl (fileno (stdout), TCFLSH, 1); +#else /* not HAVE_TERMIO */ + ioctl (fileno (stdout), TIOCFLUSH, 0); +#endif /* not HAVE_TERMIO */ +#ifdef TIOCGPGRP + error ("Quit"); +#else + error ("Quit (expect signal %d when inferior is resumed)", SIGINT); +#endif /* TIOCGPGRP */ +} + +/* Control C comes here */ + +void +request_quit () +{ + extern int remote_debugging; + + quit_flag = 1; + +#ifdef USG + /* Restore the signal handler. */ + signal (SIGINT, request_quit); +#endif + + if (immediate_quit) + quit(); +} + +/* Print an error message and return to command level. + STRING is the error message, used as a fprintf string, + and ARG is passed as an argument to it. */ + +void +error (string, arg1, arg2, arg3) + char *string; + int arg1, arg2, arg3; +{ + terminal_ours (); /* Should be ok even if no inf. */ + fflush (stdout); + fprintf (stderr, string, arg1, arg2, arg3); + fprintf (stderr, "\n"); + return_to_top_level (); +} + +/* Print an error message and exit reporting failure. + This is for a error that we cannot continue from. + STRING and ARG are passed to fprintf. */ + +void +fatal (string, arg) + char *string; + int arg; +{ + fprintf (stderr, "gdb: "); + fprintf (stderr, string, arg); + fprintf (stderr, "\n"); + exit (1); +} + +/* Print an error message and exit, dumping core. + STRING is a printf-style control string, and ARG is a corresponding + argument. */ +void +fatal_dump_core (string, arg) + char *string; + int arg; +{ + /* "internal error" is always correct, since GDB should never dump + core, no matter what the input. */ + fprintf (stderr, "gdb internal error: "); + fprintf (stderr, string, arg); + fprintf (stderr, "\n"); + signal (SIGQUIT, SIG_DFL); + kill (getpid (), SIGQUIT); + /* We should never get here, but just in case... */ + exit (1); +} + +/* Make a copy of the string at PTR with SIZE characters + (and add a null character at the end in the copy). + Uses malloc to get the space. Returns the address of the copy. */ + +char * +savestring (ptr, size) + char *ptr; + int size; +{ + register char *p = (char *) xmalloc (size + 1); + bcopy (ptr, p, size); + p[size] = 0; + return p; +} + +char * +concat (s1, s2, s3) + char *s1, *s2, *s3; +{ + register int len = strlen (s1) + strlen (s2) + strlen (s3) + 1; + register char *val = (char *) xmalloc (len); + strcpy (val, s1); + strcat (val, s2); + strcat (val, s3); + return val; +} + +void +print_spaces (n, file) + register int n; + register FILE *file; +{ + while (n-- > 0) + fputc (' ', file); +} + +/* Ask user a y-or-n question and return 1 iff answer is yes. + Takes three args which are given to printf to print the question. + The first, a control string, should end in "? ". + It should not say how to answer, because we do that. */ + +int +query (ctlstr, arg1, arg2) + char *ctlstr; +{ + register int answer; + + /* Automatically answer "yes" if input is not from a terminal. */ + if (!input_from_terminal_p ()) + return 1; + + while (1) + { + printf (ctlstr, arg1, arg2); + printf ("(y or n) "); + fflush (stdout); + answer = fgetc (stdin); + clearerr (stdin); /* in case of C-d */ + if (answer != '\n') + while (fgetc (stdin) != '\n') clearerr (stdin); + if (answer >= 'a') + answer -= 040; + if (answer == 'Y') + return 1; + if (answer == 'N') + return 0; + printf ("Please answer y or n.\n"); + } +} + +/* Parse a C escape sequence. STRING_PTR points to a variable + containing a pointer to the string to parse. That pointer + is updated past the characters we use. The value of the + escape sequence is returned. + + A negative value means the sequence \ newline was seen, + which is supposed to be equivalent to nothing at all. + + If \ is followed by a null character, we return a negative + value and leave the string pointer pointing at the null character. + + If \ is followed by 000, we return 0 and leave the string pointer + after the zeros. A value of 0 does not mean end of string. */ + +int +parse_escape (string_ptr) + char **string_ptr; +{ + register int c = *(*string_ptr)++; + switch (c) + { + case 'a': + return '\a'; + case 'b': + return '\b'; + case 'e': + return 033; + case 'f': + return '\f'; + case 'n': + return '\n'; + case 'r': + return '\r'; + case 't': + return '\t'; + case 'v': + return '\v'; + case '\n': + return -2; + case 0: + (*string_ptr)--; + return 0; + case '^': + c = *(*string_ptr)++; + if (c == '\\') + c = parse_escape (string_ptr); + if (c == '?') + return 0177; + return (c & 0200) | (c & 037); + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + register int i = c - '0'; + register int count = 0; + while (++count < 3) + { + if ((c = *(*string_ptr)++) >= '0' && c <= '7') + { + i *= 8; + i += c - '0'; + } + else + { + (*string_ptr)--; + break; + } + } + return i; + } + default: + return c; + } +} + +/* Print the character CH on STREAM as part of the contents + of a literal string whose delimiter is QUOTER. */ + +void +printchar (ch, stream, quoter) + unsigned char ch; + FILE *stream; + int quoter; +{ + register int c = ch; + if (c < 040 || c >= 0177) + switch (c) + { + case '\n': + fputs_filtered ("\\n", stream); + break; + case '\b': + fputs_filtered ("\\b", stream); + break; + case '\t': + fputs_filtered ("\\t", stream); + break; + case '\f': + fputs_filtered ("\\f", stream); + break; + case '\r': + fputs_filtered ("\\r", stream); + break; + case '\033': + fputs_filtered ("\\e", stream); + break; + case '\007': + fputs_filtered ("\\a", stream); + break; + default: + fprintf_filtered (stream, "\\%.3o", (unsigned int) c); + break; + } + else + { + if (c == '\\' || c == quoter) + fputs_filtered ("\\", stream); + fprintf_filtered (stream, "%c", c); + } +} + +static int lines_per_page, lines_printed, chars_per_line, chars_printed; + +/* Set values of page and line size. */ +static void +set_screensize_command (arg, from_tty) + char *arg; + int from_tty; +{ + char *p = arg; + char *p1; + int tolinesize = lines_per_page; + int tocharsize = chars_per_line; + + if (p == 0) + error_no_arg ("set screensize"); + + while (*p >= '0' && *p <= '9') + p++; + + if (*p && *p != ' ' && *p != '\t') + error ("Non-integral argument given to \"set screensize\"."); + + tolinesize = atoi (arg); + + while (*p == ' ' || *p == '\t') + p++; + + if (*p) + { + p1 = p; + while (*p1 >= '0' && *p1 <= '9') + p1++; + + if (*p1) + error ("Non-integral second argument given to \"set screensize\"."); + + tocharsize = atoi (p); + } + + lines_per_page = tolinesize; + chars_per_line = tocharsize; +} + +static void +instream_cleanup(stream) + FILE *stream; +{ + instream = stream; +} + +static void +prompt_for_continue () +{ + if (ISATTY(stdin) && ISATTY(stdout)) + { + struct cleanup *old_chain = make_cleanup(instream_cleanup, instream); + char *cp, *gdb_readline(); + + instream = stdin; + immediate_quit++; + if (cp = gdb_readline ("---Type to continue---")) + free(cp); + chars_printed = lines_printed = 0; + immediate_quit--; + do_cleanups(old_chain); + } +} + +/* Reinitialize filter; ie. tell it to reset to original values. */ + +void +reinitialize_more_filter () +{ + lines_printed = 0; + chars_printed = 0; +} + +static void +screensize_info (arg, from_tty) + char *arg; + int from_tty; +{ + if (arg) + error ("\"info screensize\" does not take any arguments."); + + if (!lines_per_page) + printf ("Output more filtering is disabled.\n"); + else + { + printf ("Output more filtering is enabled with\n"); + printf ("%d lines per page and %d characters per line.\n", + lines_per_page, chars_per_line); + } +} + +/* Like fputs but pause after every screenful. + Unlike fputs, fputs_filtered does not return a value. + It is OK for LINEBUFFER to be NULL, in which case just don't print + anything. + + Note that a longjmp to top level may occur in this routine + (since prompt_for_continue may do so) so this routine should not be + called when cleanups are not in place. */ + +void +fputs_filtered (linebuffer, stream) + char *linebuffer; + FILE *stream; +{ + char *lineptr; + + if (linebuffer == 0) + return; + + /* Don't do any filtering if it is disabled. */ + if (stream != stdout || !ISATTY(stdout) || lines_per_page == 0) + { + fputs (linebuffer, stream); + return; + } + + /* Go through and output each character. Show line extension + when this is necessary; prompt user for new page when this is + necessary. */ + + lineptr = linebuffer; + while (*lineptr) + { + /* Possible new page. */ + if (lines_printed >= lines_per_page - 1) + prompt_for_continue (); + + while (*lineptr && *lineptr != '\n') + { + /* Print a single line. */ + if (*lineptr == '\t') + { + putc ('\t', stream); + /* Shifting right by 3 produces the number of tab stops + we have already passed, and then adding one and + shifting left 3 advances to the next tab stop. */ + chars_printed = ((chars_printed >> 3) + 1) << 3; + lineptr++; + } + else + { + putc (*lineptr, stream); + chars_printed++; + lineptr++; + } + + if (chars_printed >= chars_per_line) + { + chars_printed = 0; + lines_printed++; + /* Possible new page. */ + if (lines_printed >= lines_per_page - 1) + prompt_for_continue (); + } + } + + if (*lineptr == '\n') + { + lines_printed++; + putc ('\n', stream); + lineptr++; + chars_printed = 0; + } + } +} + +/* fputs_demangled is a variant of fputs_filtered that + demangles g++ names.*/ + +void +fputs_demangled (linebuffer, stream, arg_mode) + char *linebuffer; + FILE *stream; +{ +#ifdef __STDC__ + extern char *cplus_demangle (const char *, int); +#else + extern char *cplus_demangle (); +#endif +#define SYMBOL_MAX 1024 + +#define SYMBOL_CHAR(c) (isascii(c) && (isalnum(c) || (c) == '_' || (c) == '$')) + + char buf[SYMBOL_MAX+1]; + char *p; + + if (linebuffer == NULL) + return; + + p = linebuffer; + + while ( *p != (char) 0 ) { + int i = 0; + + /* collect non-interesting characters into buf */ + while ( *p != (char) 0 && !SYMBOL_CHAR(*p) ) { + buf[i++] = *p; + p++; + } + if (i > 0) { + /* output the non-interesting characters without demangling */ + buf[i] = (char) 0; + fputs_filtered(buf, stream); + i = 0; /* reset buf */ + } + + /* and now the interesting characters */ + while (i < SYMBOL_MAX && *p != (char) 0 && SYMBOL_CHAR(*p) ) { + buf[i++] = *p; + p++; + } + buf[i] = (char) 0; + if (i > 0) { + char * result; + + if ( (result = cplus_demangle(buf, arg_mode)) != NULL ) { + fputs_filtered(result, stream); + free(result); + } + else { + fputs_filtered(buf, stream); + } + } + } +} + +/* Print ARG1, ARG2, and ARG3 on stdout using format FORMAT. If this + information is going to put the amount written since the last call + to INIIALIZE_MORE_FILTER or the last page break over the page size, + print out a pause message and do a gdb_readline to get the users + permision to continue. + + Unlike fprintf, this function does not return a value. + + Note that this routine has a restriction that the length of the + final output line must be less than 255 characters *or* it must be + less than twice the size of the format string. This is a very + arbitrary restriction, but it is an internal restriction, so I'll + put it in. This means that the %s format specifier is almost + useless; unless the caller can GUARANTEE that the string is short + enough, fputs_filtered should be used instead. + + Note also that a longjmp to top level may occur in this routine + (since prompt_for_continue may do so) so this routine should not be + called when cleanups are not in place. */ + +void +fprintf_filtered (stream, format, arg1, arg2, arg3, arg4, arg5, arg6) + FILE *stream; + char *format; + int arg1, arg2, arg3, arg4, arg5, arg6; +{ + static char *linebuffer = (char *) 0; + static int line_size; + int format_length = strlen (format); + int numchars; + + /* Allocated linebuffer for the first time. */ + if (!linebuffer) + { + linebuffer = (char *) xmalloc (255); + line_size = 255; + } + + /* Reallocate buffer to a larger size if this is necessary. */ + if (format_length * 2 > line_size) + { + line_size = format_length * 2; + + /* You don't have to copy. */ + free (linebuffer); + linebuffer = (char *) xmalloc (line_size); + } + + /* This won't blow up if the restrictions described above are + followed. */ + (void) sprintf (linebuffer, format, arg1, arg2, arg3, arg4, arg5, arg6); + + fputs_filtered (linebuffer, stream); +} + +void +printf_filtered (format, arg1, arg2, arg3, arg4, arg5, arg6) + char *format; + int arg1, arg2, arg3, arg4, arg5, arg6; +{ + fprintf_filtered (stdout, format, arg1, arg2, arg3, arg4, arg5, arg6); +} + +/* Print N spaces. */ +void +print_spaces_filtered (n, stream) + int n; + FILE *stream; +{ + register char *s = (char *) alloca (n + 1); + register char *t = s; + + while (n--) + *t++ = ' '; + *t = '\0'; + + fputs_filtered (s, stream); +} + + +#ifdef USG +bcopy (from, to, count) +char *from, *to; +{ + memcpy (to, from, count); +} + +bcmp (from, to, count) +{ + return (memcmp (to, from, count)); +} + +bzero (to, count) +char *to; +{ + while (count--) + *to++ = 0; +} + +getwd (buf) +char *buf; +{ + getcwd (buf, MAXPATHLEN); +} + +char * +index (s, c) + char *s; +{ + char *strchr (); + return strchr (s, c); +} + +char * +rindex (s, c) + char *s; +{ + char *strrchr (); + return strrchr (s, c); +} + +#ifndef USG +char *sys_siglist[32] = { + "SIG0", + "SIGHUP", + "SIGINT", + "SIGQUIT", + "SIGILL", + "SIGTRAP", + "SIGIOT", + "SIGEMT", + "SIGFPE", + "SIGKILL", + "SIGBUS", + "SIGSEGV", + "SIGSYS", + "SIGPIPE", + "SIGALRM", + "SIGTERM", + "SIGUSR1", + "SIGUSR2", + "SIGCLD", + "SIGPWR", + "SIGWIND", + "SIGPHONE", + "SIGPOLL", +}; +#endif + +/* Queue routines */ + +struct queue { + struct queue *forw; + struct queue *back; +}; + +insque (item, after) +struct queue *item; +struct queue *after; +{ + item->forw = after->forw; + after->forw->back = item; + + item->back = after; + after->forw = item; +} + +remque (item) +struct queue *item; +{ + item->forw->back = item->back; + item->back->forw = item->forw; +} +#endif /* USG */ + +#ifdef USG +/* There is too much variation in Sys V signal numbers and names, so + we must initialize them at runtime. */ +static char undoc[] = "(undocumented)"; + +char *sys_siglist[NSIG]; +#endif /* USG */ + +extern struct cmd_list_element *setlist; + +void +_initialize_utils () +{ + int i; + add_cmd ("screensize", class_support, set_screensize_command, + "Change gdb's notion of the size of the output screen.\n\ +The first argument is the number of lines on a page.\n\ +The second argument (optional) is the number of characters on a line.", + &setlist); + add_info ("screensize", screensize_info, + "Show gdb's current notion of the size of the output screen."); + + /* These defaults will be used if we are unable to get the correct + values from termcap. */ + lines_per_page = 24; + chars_per_line = 80; + /* Initialize the screen height and width from termcap. */ + { + int termtype = getenv ("TERM"); + + /* Positive means success, nonpositive means failure. */ + int status; + + /* 2048 is large enough for all known terminals, according to the + GNU termcap manual. */ + char term_buffer[2048]; + + if (termtype) + { + status = tgetent (term_buffer, termtype); + if (status > 0) + { + int val; + + val = tgetnum ("li"); + if (val >= 0) + lines_per_page = val; + else + /* The number of lines per page is not mentioned + in the terminal description. This probably means + that paging is not useful (e.g. emacs shell window), + so disable paging. */ + lines_per_page = 0; + + val = tgetnum ("co"); + if (val >= 0) + chars_per_line = val; + } + } + } + +#ifdef USG + /* Initialize signal names. */ + for (i = 0; i < NSIG; i++) + sys_siglist[i] = undoc; + +#ifdef SIGHUP + sys_siglist[SIGHUP ] = "SIGHUP"; +#endif +#ifdef SIGINT + sys_siglist[SIGINT ] = "SIGINT"; +#endif +#ifdef SIGQUIT + sys_siglist[SIGQUIT ] = "SIGQUIT"; +#endif +#ifdef SIGILL + sys_siglist[SIGILL ] = "SIGILL"; +#endif +#ifdef SIGTRAP + sys_siglist[SIGTRAP ] = "SIGTRAP"; +#endif +#ifdef SIGIOT + sys_siglist[SIGIOT ] = "SIGIOT"; +#endif +#ifdef SIGEMT + sys_siglist[SIGEMT ] = "SIGEMT"; +#endif +#ifdef SIGFPE + sys_siglist[SIGFPE ] = "SIGFPE"; +#endif +#ifdef SIGKILL + sys_siglist[SIGKILL ] = "SIGKILL"; +#endif +#ifdef SIGBUS + sys_siglist[SIGBUS ] = "SIGBUS"; +#endif +#ifdef SIGSEGV + sys_siglist[SIGSEGV ] = "SIGSEGV"; +#endif +#ifdef SIGSYS + sys_siglist[SIGSYS ] = "SIGSYS"; +#endif +#ifdef SIGPIPE + sys_siglist[SIGPIPE ] = "SIGPIPE"; +#endif +#ifdef SIGALRM + sys_siglist[SIGALRM ] = "SIGALRM"; +#endif +#ifdef SIGTERM + sys_siglist[SIGTERM ] = "SIGTERM"; +#endif +#ifdef SIGUSR1 + sys_siglist[SIGUSR1 ] = "SIGUSR1"; +#endif +#ifdef SIGUSR2 + sys_siglist[SIGUSR2 ] = "SIGUSR2"; +#endif +#ifdef SIGCLD + sys_siglist[SIGCLD ] = "SIGCLD"; +#endif +#ifdef SIGCHLD + sys_siglist[SIGCHLD ] = "SIGCHLD"; +#endif +#ifdef SIGPWR + sys_siglist[SIGPWR ] = "SIGPWR"; +#endif +#ifdef SIGTSTP + sys_siglist[SIGTSTP ] = "SIGTSTP"; +#endif +#ifdef SIGTTIN + sys_siglist[SIGTTIN ] = "SIGTTIN"; +#endif +#ifdef SIGTTOU + sys_siglist[SIGTTOU ] = "SIGTTOU"; +#endif +#ifdef SIGSTOP + sys_siglist[SIGSTOP ] = "SIGSTOP"; +#endif +#ifdef SIGXCPU + sys_siglist[SIGXCPU ] = "SIGXCPU"; +#endif +#ifdef SIGXFSZ + sys_siglist[SIGXFSZ ] = "SIGXFSZ"; +#endif +#ifdef SIGVTALRM + sys_siglist[SIGVTALRM ] = "SIGVTALRM"; +#endif +#ifdef SIGPROF + sys_siglist[SIGPROF ] = "SIGPROF"; +#endif +#ifdef SIGWINCH + sys_siglist[SIGWINCH ] = "SIGWINCH"; +#endif +#ifdef SIGCONT + sys_siglist[SIGCONT ] = "SIGCONT"; +#endif +#ifdef SIGURG + sys_siglist[SIGURG ] = "SIGURG"; +#endif +#ifdef SIGIO + sys_siglist[SIGIO ] = "SIGIO"; +#endif +#ifdef SIGWIND + sys_siglist[SIGWIND ] = "SIGWIND"; +#endif +#ifdef SIGPHONE + sys_siglist[SIGPHONE ] = "SIGPHONE"; +#endif +#ifdef SIGPOLL + sys_siglist[SIGPOLL ] = "SIGPOLL"; +#endif +#endif /* USG */ +} diff --git a/gnu/usr.bin/gdb/valarith.c b/gnu/usr.bin/gdb/valarith.c new file mode 100644 index 0000000000..8e76899884 --- /dev/null +++ b/gnu/usr.bin/gdb/valarith.c @@ -0,0 +1,690 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)valarith.c 6.3 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Perform arithmetic and other operations on values, for GDB. + Copyright (C) 1986, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "value.h" +#include "expression.h" + + +value value_x_binop (); +value value_subscripted_rvalue (); + +value +value_add (arg1, arg2) + value arg1, arg2; +{ + register value val, valint, valptr; + register int len; + + COERCE_ARRAY (arg1); + COERCE_ARRAY (arg2); + + if ((TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR + || TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_PTR) + && + (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_INT + || TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_INT)) + /* Exactly one argument is a pointer, and one is an integer. */ + { + if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR) + { + valptr = arg1; + valint = arg2; + } + else + { + valptr = arg2; + valint = arg1; + } + len = TYPE_LENGTH (TYPE_TARGET_TYPE (VALUE_TYPE (valptr))); + if (len == 0) len = 1; /* For (void *) */ + val = value_from_long (builtin_type_long, + value_as_long (valptr) + + (len * value_as_long (valint))); + VALUE_TYPE (val) = VALUE_TYPE (valptr); + return val; + } + + return value_binop (arg1, arg2, BINOP_ADD); +} + +value +value_sub (arg1, arg2) + value arg1, arg2; +{ + register value val; + + COERCE_ARRAY (arg1); + COERCE_ARRAY (arg2); + + if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR + && + TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_INT) + { + val = value_from_long (builtin_type_long, + value_as_long (arg1) + - TYPE_LENGTH (TYPE_TARGET_TYPE (VALUE_TYPE (arg1))) * value_as_long (arg2)); + VALUE_TYPE (val) = VALUE_TYPE (arg1); + return val; + } + + if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR + && + VALUE_TYPE (arg1) == VALUE_TYPE (arg2)) + { + val = value_from_long (builtin_type_long, + (value_as_long (arg1) - value_as_long (arg2)) + / TYPE_LENGTH (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)))); + return val; + } + + return value_binop (arg1, arg2, BINOP_SUB); +} + +/* Return the value of ARRAY[IDX]. */ + +value +value_subscript (array, idx) + value array, idx; +{ + if (TYPE_CODE (VALUE_TYPE (array)) == TYPE_CODE_ARRAY + && VALUE_LVAL (array) != lval_memory) + return value_subscripted_rvalue (array, idx); + else + return value_ind (value_add (array, idx)); +} + +/* Return the value of EXPR[IDX], expr an aggregate rvalue + (eg, a vector register) */ + +value +value_subscripted_rvalue (array, idx) + value array, idx; +{ + struct type *elt_type = TYPE_TARGET_TYPE (VALUE_TYPE (array)); + int elt_size = TYPE_LENGTH (elt_type); + int elt_offs = elt_size * value_as_long (idx); + value v; + + if (elt_offs >= TYPE_LENGTH (VALUE_TYPE (array))) + error ("no such vector element"); + + if (TYPE_CODE (elt_type) == TYPE_CODE_FLT) + { + if (elt_size == sizeof (float)) + v = value_from_double (elt_type, (double) *(float *) + (VALUE_CONTENTS (array) + elt_offs)); + else + v = value_from_double (elt_type, *(double *) + (VALUE_CONTENTS (array) + elt_offs)); + } + else + { + int offs; + union {int i; char c;} test; + test.i = 1; + if (test.c == 1) + offs = 0; + else + offs = sizeof (LONGEST) - elt_size; + v = value_from_long (elt_type, *(LONGEST *) + (VALUE_CONTENTS (array) + elt_offs - offs)); + } + + if (VALUE_LVAL (array) == lval_internalvar) + VALUE_LVAL (v) = lval_internalvar_component; + else + VALUE_LVAL (v) = not_lval; + VALUE_ADDRESS (v) = VALUE_ADDRESS (array); + VALUE_OFFSET (v) = VALUE_OFFSET (array) + elt_offs; + VALUE_BITSIZE (v) = elt_size * 8; + return v; +} + +/* Check to see if either argument is a structure. This is called so + we know whether to go ahead with the normal binop or look for a + user defined function instead. + + For now, we do not overload the `=' operator. */ + +int +binop_user_defined_p (op, arg1, arg2) + enum exp_opcode op; + value arg1, arg2; +{ + if (op == BINOP_ASSIGN) + return 0; + return (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_STRUCT + || TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_STRUCT + || (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF + && TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg1))) == TYPE_CODE_STRUCT) + || (TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_REF + && TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2))) == TYPE_CODE_STRUCT)); +} + +/* Check to see if argument is a structure. This is called so + we know whether to go ahead with the normal unop or look for a + user defined function instead. + + For now, we do not overload the `&' operator. */ + +int unop_user_defined_p (op, arg1) + enum exp_opcode op; + value arg1; +{ + if (op == UNOP_ADDR) + return 0; + return (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_STRUCT + || (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF + && TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg1))) == TYPE_CODE_STRUCT)); +} + +/* We know either arg1 or arg2 is a structure, so try to find the right + user defined function. Create an argument vector that calls + arg1.operator @ (arg1,arg2) and return that value (where '@' is any + binary operator which is legal for GNU C++). */ + +value +value_x_binop (arg1, arg2, op, otherop) + value arg1, arg2; + int op, otherop; +{ + value * argvec; + char *ptr; + char tstr[13]; + int static_memfuncp; + + COERCE_ENUM (arg1); + COERCE_ENUM (arg2); + + /* now we know that what we have to do is construct our + arg vector and find the right function to call it with. */ + + if (TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_STRUCT) + error ("friend functions not implemented yet"); + + argvec = (value *) alloca (sizeof (value) * 4); + argvec[1] = value_addr (arg1); + argvec[2] = arg2; + argvec[3] = 0; + + /* make the right function name up */ + strcpy(tstr, "operator __"); + ptr = tstr+9; + switch (op) + { + case BINOP_ADD: strcpy(ptr,"+"); break; + case BINOP_SUB: strcpy(ptr,"-"); break; + case BINOP_MUL: strcpy(ptr,"*"); break; + case BINOP_DIV: strcpy(ptr,"/"); break; + case BINOP_REM: strcpy(ptr,"%"); break; + case BINOP_LSH: strcpy(ptr,"<<"); break; + case BINOP_RSH: strcpy(ptr,">>"); break; + case BINOP_LOGAND: strcpy(ptr,"&"); break; + case BINOP_LOGIOR: strcpy(ptr,"|"); break; + case BINOP_LOGXOR: strcpy(ptr,"^"); break; + case BINOP_AND: strcpy(ptr,"&&"); break; + case BINOP_OR: strcpy(ptr,"||"); break; + case BINOP_MIN: strcpy(ptr,"?"); break; + case BINOP_ASSIGN: strcpy(ptr,"="); break; + case BINOP_ASSIGN_MODIFY: + switch (otherop) + { + case BINOP_ADD: strcpy(ptr,"+="); break; + case BINOP_SUB: strcpy(ptr,"-="); break; + case BINOP_MUL: strcpy(ptr,"*="); break; + case BINOP_DIV: strcpy(ptr,"/="); break; + case BINOP_REM: strcpy(ptr,"%="); break; + case BINOP_LOGAND: strcpy(ptr,"&="); break; + case BINOP_LOGIOR: strcpy(ptr,"|="); break; + case BINOP_LOGXOR: strcpy(ptr,"^="); break; + default: + error ("Invalid binary operation specified."); + } + break; + case BINOP_SUBSCRIPT: strcpy(ptr,"[]"); break; + case BINOP_EQUAL: strcpy(ptr,"=="); break; + case BINOP_NOTEQUAL: strcpy(ptr,"!="); break; + case BINOP_LESS: strcpy(ptr,"<"); break; + case BINOP_GTR: strcpy(ptr,">"); break; + case BINOP_GEQ: strcpy(ptr,">="); break; + case BINOP_LEQ: strcpy(ptr,"<="); break; + default: + error ("Invalid binary operation specified."); + } + argvec[0] = value_struct_elt (arg1, argvec+1, tstr, &static_memfuncp, "structure"); + if (argvec[0]) + { + if (static_memfuncp) + { + argvec[1] = argvec[0]; + argvec++; + } + return call_function (argvec[0], 2 - static_memfuncp, argvec + 1); + } + error ("member function %s not found", tstr); +} + +/* We know that arg1 is a structure, so try to find a unary user + defined operator that matches the operator in question. + Create an argument vector that calls arg1.operator @ (arg1) + and return that value (where '@' is (almost) any unary operator which + is legal for GNU C++). */ + +value +value_x_unop (arg1, op) + value arg1; + int op; +{ + value * argvec; + char *ptr; + char tstr[13]; + int static_memfuncp; + + COERCE_ENUM (arg1); + + /* now we know that what we have to do is construct our + arg vector and find the right function to call it with. */ + + if (TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_STRUCT) + error ("friend functions not implemented yet"); + + argvec = (value *) alloca (sizeof (value) * 3); + argvec[1] = value_addr (arg1); + argvec[2] = 0; + + /* make the right function name up */ + strcpy(tstr,"operator __"); + ptr = tstr+9; + switch (op) + { + case UNOP_PREINCREMENT: strcpy(ptr,"++"); break; + case UNOP_PREDECREMENT: strcpy(ptr,"++"); break; + case UNOP_POSTINCREMENT: strcpy(ptr,"++"); break; + case UNOP_POSTDECREMENT: strcpy(ptr,"++"); break; + case UNOP_ZEROP: strcpy(ptr,"!"); break; + case UNOP_LOGNOT: strcpy(ptr,"~"); break; + case UNOP_NEG: strcpy(ptr,"-"); break; + default: + error ("Invalid binary operation specified."); + } + argvec[0] = value_struct_elt (arg1, argvec+1, tstr, &static_memfuncp, "structure"); + if (argvec[0]) + { + if (static_memfuncp) + { + argvec[1] = argvec[0]; + argvec++; + } + return call_function (argvec[0], 1 - static_memfuncp, argvec + 1); + } + error ("member function %s not found", tstr); +} + +/* Perform a binary operation on two integers or two floats. + Does not support addition and subtraction on pointers; + use value_add or value_sub if you want to handle those possibilities. */ + +value +value_binop (arg1, arg2, op) + value arg1, arg2; + int op; +{ + register value val; + + COERCE_ENUM (arg1); + COERCE_ENUM (arg2); + + if ((TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_FLT + && + TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_INT) + || + (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_FLT + && + TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_INT)) + error ("Argument to arithmetic operation not a number."); + + if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_FLT + || + TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_FLT) + { + double v1, v2, v; + v1 = value_as_double (arg1); + v2 = value_as_double (arg2); + switch (op) + { + case BINOP_ADD: + v = v1 + v2; + break; + + case BINOP_SUB: + v = v1 - v2; + break; + + case BINOP_MUL: + v = v1 * v2; + break; + + case BINOP_DIV: + v = v1 / v2; + break; + + default: + error ("Integer-only operation on floating point number."); + } + + val = allocate_value (builtin_type_double); + *(double *) VALUE_CONTENTS (val) = v; + } + else + /* Integral operations here. */ + { + /* Should we promote to unsigned longest? */ + if ((TYPE_UNSIGNED (VALUE_TYPE (arg1)) + || TYPE_UNSIGNED (VALUE_TYPE (arg2))) + && (TYPE_LENGTH (VALUE_TYPE (arg1)) >= sizeof (unsigned LONGEST) + || TYPE_LENGTH (VALUE_TYPE (arg2)) >= sizeof (unsigned LONGEST))) + { + unsigned LONGEST v1, v2, v; + v1 = (unsigned LONGEST) value_as_long (arg1); + v2 = (unsigned LONGEST) value_as_long (arg2); + + switch (op) + { + case BINOP_ADD: + v = v1 + v2; + break; + + case BINOP_SUB: + v = v1 - v2; + break; + + case BINOP_MUL: + v = v1 * v2; + break; + + case BINOP_DIV: + v = v1 / v2; + break; + + case BINOP_REM: + v = v1 % v2; + break; + + case BINOP_LSH: + v = v1 << v2; + break; + + case BINOP_RSH: + v = v1 >> v2; + break; + + case BINOP_LOGAND: + v = v1 & v2; + break; + + case BINOP_LOGIOR: + v = v1 | v2; + break; + + case BINOP_LOGXOR: + v = v1 ^ v2; + break; + + case BINOP_AND: + v = v1 && v2; + break; + + case BINOP_OR: + v = v1 || v2; + break; + + case BINOP_MIN: + v = v1 < v2 ? v1 : v2; + break; + + case BINOP_MAX: + v = v1 > v2 ? v1 : v2; + break; + + default: + error ("Invalid binary operation on numbers."); + } + + val = allocate_value (BUILTIN_TYPE_UNSIGNED_LONGEST); + *(unsigned LONGEST *) VALUE_CONTENTS (val) = v; + } + else + { + LONGEST v1, v2, v; + v1 = value_as_long (arg1); + v2 = value_as_long (arg2); + + switch (op) + { + case BINOP_ADD: + v = v1 + v2; + break; + + case BINOP_SUB: + v = v1 - v2; + break; + + case BINOP_MUL: + v = v1 * v2; + break; + + case BINOP_DIV: + v = v1 / v2; + break; + + case BINOP_REM: + v = v1 % v2; + break; + + case BINOP_LSH: + v = v1 << v2; + break; + + case BINOP_RSH: + v = v1 >> v2; + break; + + case BINOP_LOGAND: + v = v1 & v2; + break; + + case BINOP_LOGIOR: + v = v1 | v2; + break; + + case BINOP_LOGXOR: + v = v1 ^ v2; + break; + + case BINOP_AND: + v = v1 && v2; + break; + + case BINOP_OR: + v = v1 || v2; + break; + + case BINOP_MIN: + v = v1 < v2 ? v1 : v2; + break; + + case BINOP_MAX: + v = v1 > v2 ? v1 : v2; + break; + + default: + error ("Invalid binary operation on numbers."); + } + + val = allocate_value (BUILTIN_TYPE_LONGEST); + *(LONGEST *) VALUE_CONTENTS (val) = v; + } + } + + return val; +} + +/* Simulate the C operator ! -- return 1 if ARG1 contains zeros. */ + +int +value_zerop (arg1) + value arg1; +{ + register int len; + register char *p; + + COERCE_ARRAY (arg1); + + len = TYPE_LENGTH (VALUE_TYPE (arg1)); + p = VALUE_CONTENTS (arg1); + + while (--len >= 0) + { + if (*p++) + break; + } + + return len < 0; +} + +/* Simulate the C operator == by returning a 1 + iff ARG1 and ARG2 have equal contents. */ + +int +value_equal (arg1, arg2) + register value arg1, arg2; + +{ + register int len; + register char *p1, *p2; + enum type_code code1; + enum type_code code2; + + COERCE_ARRAY (arg1); + COERCE_ARRAY (arg2); + + code1 = TYPE_CODE (VALUE_TYPE (arg1)); + code2 = TYPE_CODE (VALUE_TYPE (arg2)); + + if (code1 == TYPE_CODE_INT && code2 == TYPE_CODE_INT) + return value_as_long (arg1) == value_as_long (arg2); + else if ((code1 == TYPE_CODE_FLT || code1 == TYPE_CODE_INT) + && (code2 == TYPE_CODE_FLT || code2 == TYPE_CODE_INT)) + return value_as_double (arg1) == value_as_double (arg2); + else if ((code1 == TYPE_CODE_PTR && code2 == TYPE_CODE_INT) + || (code2 == TYPE_CODE_PTR && code1 == TYPE_CODE_INT)) + return (char *) value_as_long (arg1) == (char *) value_as_long (arg2); + else if (code1 == code2 + && ((len = TYPE_LENGTH (VALUE_TYPE (arg1))) + == TYPE_LENGTH (VALUE_TYPE (arg2)))) + { + p1 = VALUE_CONTENTS (arg1); + p2 = VALUE_CONTENTS (arg2); + while (--len >= 0) + { + if (*p1++ != *p2++) + break; + } + return len < 0; + } + else + error ("Invalid type combination in equality test."); +} + +/* Simulate the C operator < by returning 1 + iff ARG1's contents are less than ARG2's. */ + +int +value_less (arg1, arg2) + register value arg1, arg2; +{ + register enum type_code code1; + register enum type_code code2; + + COERCE_ARRAY (arg1); + COERCE_ARRAY (arg2); + + code1 = TYPE_CODE (VALUE_TYPE (arg1)); + code2 = TYPE_CODE (VALUE_TYPE (arg2)); + + if (code1 == TYPE_CODE_INT && code2 == TYPE_CODE_INT) + return value_as_long (arg1) < value_as_long (arg2); + else if ((code1 == TYPE_CODE_FLT || code1 == TYPE_CODE_INT) + && (code2 == TYPE_CODE_FLT || code2 == TYPE_CODE_INT)) + return value_as_double (arg1) < value_as_double (arg2); + else if ((code1 == TYPE_CODE_PTR || code1 == TYPE_CODE_INT) + && (code2 == TYPE_CODE_PTR || code2 == TYPE_CODE_INT)) + return (char *) value_as_long (arg1) < (char *) value_as_long (arg2); + else + error ("Invalid type combination in ordering comparison."); +} + +/* The unary operators - and ~. Both free the argument ARG1. */ + +value +value_neg (arg1) + register value arg1; +{ + register struct type *type; + + COERCE_ENUM (arg1); + + type = VALUE_TYPE (arg1); + + if (TYPE_CODE (type) == TYPE_CODE_FLT) + return value_from_double (type, - value_as_double (arg1)); + else if (TYPE_CODE (type) == TYPE_CODE_INT) + return value_from_long (type, - value_as_long (arg1)); + else + error ("Argument to negate operation not a number."); +} + +value +value_lognot (arg1) + register value arg1; +{ + COERCE_ENUM (arg1); + + if (TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_INT) + error ("Argument to complement operation not an integer."); + + return value_from_long (VALUE_TYPE (arg1), ~ value_as_long (arg1)); +} + diff --git a/gnu/usr.bin/gdb/valops.c b/gnu/usr.bin/gdb/valops.c new file mode 100644 index 0000000000..ab5652cc18 --- /dev/null +++ b/gnu/usr.bin/gdb/valops.c @@ -0,0 +1,1418 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)valops.c 6.4 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Perform non-arithmetic operations on values, for GDB. + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "stdio.h" +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "value.h" +#include "frame.h" +#include "inferior.h" + +/* Cast value ARG2 to type TYPE and return as a value. + More general than a C cast: accepts any two types of the same length, + and if ARG2 is an lvalue it can be cast into anything at all. */ + +value +value_cast (type, arg2) + struct type *type; + register value arg2; +{ + register enum type_code code1; + register enum type_code code2; + register int scalar; + + /* Coerce arrays but not enums. Enums will work as-is + and coercing them would cause an infinite recursion. */ + if (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_ENUM) + COERCE_ARRAY (arg2); + + code1 = TYPE_CODE (type); + code2 = TYPE_CODE (VALUE_TYPE (arg2)); + scalar = (code2 == TYPE_CODE_INT || code2 == TYPE_CODE_FLT + || code2 == TYPE_CODE_ENUM); + + if (code1 == TYPE_CODE_FLT && scalar) + return value_from_double (type, value_as_double (arg2)); + else if ((code1 == TYPE_CODE_INT || code1 == TYPE_CODE_ENUM) + && (scalar || code2 == TYPE_CODE_PTR)) + return value_from_long (type, value_as_long (arg2)); + else if (TYPE_LENGTH (type) == TYPE_LENGTH (VALUE_TYPE (arg2))) + { + VALUE_TYPE (arg2) = type; + return arg2; + } + else if (VALUE_LVAL (arg2) == lval_memory) + { + return value_at (type, VALUE_ADDRESS (arg2) + VALUE_OFFSET (arg2)); + } + else + error ("Invalid cast."); +} + +/* Create a value of type TYPE that is zero, and return it. */ + +value +value_zero (type, lv) + struct type *type; + enum lval_type lv; +{ + register value val = allocate_value (type); + + bzero (VALUE_CONTENTS (val), TYPE_LENGTH (type)); + VALUE_LVAL (val) = lv; + + return val; +} + +/* Return the value with a specified type located at specified address. */ + +value +value_at (type, addr) + struct type *type; + CORE_ADDR addr; +{ + register value val = allocate_value (type); + int temp; + + temp = read_memory (addr, VALUE_CONTENTS (val), TYPE_LENGTH (type)); + if (temp) + { + if (have_inferior_p () && !remote_debugging) + print_sys_errmsg ("ptrace", temp); + /* Actually, address between addr and addr + len was out of bounds. */ + error ("Cannot read memory: address 0x%x out of bounds.", addr); + } + + VALUE_LVAL (val) = lval_memory; + VALUE_ADDRESS (val) = addr; + + return val; +} + +/* Store the contents of FROMVAL into the location of TOVAL. + Return a new value with the location of TOVAL and contents of FROMVAL. */ + +value +value_assign (toval, fromval) + register value toval, fromval; +{ + register struct type *type = VALUE_TYPE (toval); + register value val; + char raw_buffer[MAX_REGISTER_RAW_SIZE]; + char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE]; + int use_buffer = 0; + + extern CORE_ADDR find_saved_register (); + + COERCE_ARRAY (fromval); + + if (VALUE_LVAL (toval) != lval_internalvar) + fromval = value_cast (type, fromval); + + /* If TOVAL is a special machine register requiring conversion + of program values to a special raw format, + convert FROMVAL's contents now, with result in `raw_buffer', + and set USE_BUFFER to the number of bytes to write. */ + + if (VALUE_REGNO (toval) >= 0 + && REGISTER_CONVERTIBLE (VALUE_REGNO (toval))) + { + int regno = VALUE_REGNO (toval); + if (VALUE_TYPE (fromval) != REGISTER_VIRTUAL_TYPE (regno)) + fromval = value_cast (REGISTER_VIRTUAL_TYPE (regno), fromval); + bcopy (VALUE_CONTENTS (fromval), virtual_buffer, + REGISTER_VIRTUAL_SIZE (regno)); + REGISTER_CONVERT_TO_RAW (regno, virtual_buffer, raw_buffer); + use_buffer = REGISTER_RAW_SIZE (regno); + } + + switch (VALUE_LVAL (toval)) + { + case lval_internalvar: + set_internalvar (VALUE_INTERNALVAR (toval), fromval); + break; + + case lval_internalvar_component: + set_internalvar_component (VALUE_INTERNALVAR (toval), + VALUE_OFFSET (toval), + VALUE_BITPOS (toval), + VALUE_BITSIZE (toval), + fromval); + break; + + case lval_memory: + if (VALUE_BITSIZE (toval)) + { + int val; + read_memory (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval), + &val, sizeof val); + modify_field (&val, (int) value_as_long (fromval), + VALUE_BITPOS (toval), VALUE_BITSIZE (toval)); + write_memory (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval), + &val, sizeof val); + } + else if (use_buffer) + write_memory (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval), + raw_buffer, use_buffer); + else + write_memory (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval), + VALUE_CONTENTS (fromval), TYPE_LENGTH (type)); + break; + + case lval_register: + if (VALUE_BITSIZE (toval)) + { + int val; + + read_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval), + &val, sizeof val); + modify_field (&val, (int) value_as_long (fromval), + VALUE_BITPOS (toval), VALUE_BITSIZE (toval)); + write_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval), + &val, sizeof val); + } + else if (use_buffer) + write_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval), + raw_buffer, use_buffer); + else + write_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval), + VALUE_CONTENTS (fromval), TYPE_LENGTH (type)); + break; + + case lval_reg_frame_relative: + { + /* value is stored in a series of registers in the frame + specified by the structure. Copy that value out, modify + it, and copy it back in. */ + int amount_to_copy = (VALUE_BITSIZE (toval) ? 1 : TYPE_LENGTH (type)); + int reg_size = REGISTER_RAW_SIZE (VALUE_FRAME_REGNUM (toval)); + int byte_offset = VALUE_OFFSET (toval) % reg_size; + int reg_offset = VALUE_OFFSET (toval) / reg_size; + int amount_copied; + char *buffer = (char *) alloca (amount_to_copy); + int regno; + FRAME frame; + CORE_ADDR addr; + + /* Figure out which frame this is in currently. */ + for (frame = get_current_frame (); + frame && FRAME_FP (frame) != VALUE_FRAME (toval); + frame = get_prev_frame (frame)) + ; + + if (!frame) + error ("Value being assigned to is no longer active."); + + amount_to_copy += (reg_size - amount_to_copy % reg_size); + + /* Copy it out. */ + for ((regno = VALUE_FRAME_REGNUM (toval) + reg_offset, + amount_copied = 0); + amount_copied < amount_to_copy; + amount_copied += reg_size, regno++) + { + addr = find_saved_register (frame, regno); + if (addr == 0) + read_register_bytes (REGISTER_BYTE (regno), + buffer + amount_copied, + reg_size); + else + read_memory (addr, buffer + amount_copied, reg_size); + } + + /* Modify what needs to be modified. */ + if (VALUE_BITSIZE (toval)) + modify_field (buffer + byte_offset, + (int) value_as_long (fromval), + VALUE_BITPOS (toval), VALUE_BITSIZE (toval)); + else if (use_buffer) + bcopy (raw_buffer, buffer + byte_offset, use_buffer); + else + bcopy (VALUE_CONTENTS (fromval), buffer + byte_offset, + TYPE_LENGTH (type)); + + /* Copy it back. */ + for ((regno = VALUE_FRAME_REGNUM (toval) + reg_offset, + amount_copied = 0); + amount_copied < amount_to_copy; + amount_copied += reg_size, regno++) + { + addr = find_saved_register (frame, regno); + if (addr == 0) + write_register_bytes (REGISTER_BYTE (regno), + buffer + amount_copied, + reg_size); + else + write_memory (addr, buffer + amount_copied, reg_size); + } + } + break; + + + default: + error ("Left side of = operation is not an lvalue."); + } + + /* Return a value just like TOVAL except with the contents of FROMVAL + (except in the case of the type if TOVAL is an internalvar). */ + + if (VALUE_LVAL (toval) == lval_internalvar + || VALUE_LVAL (toval) == lval_internalvar_component) + { + type = VALUE_TYPE (fromval); + } + + val = allocate_value (type); + bcopy (toval, val, VALUE_CONTENTS (val) - (char *) val); + bcopy (VALUE_CONTENTS (fromval), VALUE_CONTENTS (val), TYPE_LENGTH (type)); + VALUE_TYPE (val) = type; + + return val; +} + +/* Extend a value VAL to COUNT repetitions of its type. */ + +value +value_repeat (arg1, count) + value arg1; + int count; +{ + register value val; + + if (VALUE_LVAL (arg1) != lval_memory) + error ("Only values in memory can be extended with '@'."); + if (count < 1) + error ("Invalid number %d of repetitions.", count); + + val = allocate_repeat_value (VALUE_TYPE (arg1), count); + + read_memory (VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1), + VALUE_CONTENTS (val), + TYPE_LENGTH (VALUE_TYPE (val)) * count); + VALUE_LVAL (val) = lval_memory; + VALUE_ADDRESS (val) = VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1); + + return val; +} + +value +value_of_variable (var) + struct symbol *var; +{ + return read_var_value (var, (FRAME) 0); +} + +/* Given a value which is an array, return a value which is + a pointer to its first element. */ + +value +value_coerce_array (arg1) + value arg1; +{ + register struct type *type; + register value val; + + if (VALUE_LVAL (arg1) != lval_memory) + error ("Attempt to take address of value not located in memory."); + + /* Get type of elements. */ + if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_ARRAY) + type = TYPE_TARGET_TYPE (VALUE_TYPE (arg1)); + else + /* A phony array made by value_repeat. + Its type is the type of the elements, not an array type. */ + type = VALUE_TYPE (arg1); + + /* Get the type of the result. */ + type = lookup_pointer_type (type); + val = value_from_long (builtin_type_long, + (LONGEST) (VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1))); + VALUE_TYPE (val) = type; + return val; +} + +/* Return a pointer value for the object for which ARG1 is the contents. */ + +value +value_addr (arg1) + value arg1; +{ + register struct type *type; + register value val, arg1_coerced; + + /* Taking the address of an array is really a no-op + once the array is coerced to a pointer to its first element. */ + arg1_coerced = arg1; + COERCE_ARRAY (arg1_coerced); + if (arg1 != arg1_coerced) + return arg1_coerced; + + if (VALUE_LVAL (arg1) != lval_memory) + error ("Attempt to take address of value not located in memory."); + + /* Get the type of the result. */ + type = lookup_pointer_type (VALUE_TYPE (arg1)); + val = value_from_long (builtin_type_long, + (LONGEST) (VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1))); + VALUE_TYPE (val) = type; + return val; +} + +/* Given a value of a pointer type, apply the C unary * operator to it. */ + +value +value_ind (arg1) + value arg1; +{ + /* Must do this before COERCE_ARRAY, otherwise an infinite loop + will result */ + if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF) + return value_at (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)), + (CORE_ADDR) value_as_long (arg1)); + + COERCE_ARRAY (arg1); + + if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_MEMBER) + error ("not implemented: member types in value_ind"); + + /* Allow * on an integer so we can cast it to whatever we want. + This returns an int, which seems like the most C-like thing + to do. "long long" variables are rare enough that + BUILTIN_TYPE_LONGEST would seem to be a mistake. */ + if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_INT) + return value_at (builtin_type_int, + (CORE_ADDR) value_as_long (arg1)); + else if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR) + return value_at (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)), + (CORE_ADDR) value_as_long (arg1)); + else if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF) + return value_at (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)), + (CORE_ADDR) value_as_long (arg1)); + error ("Attempt to take contents of a non-pointer value."); +} + +/* Pushing small parts of stack frames. */ + +/* Push one word (the size of object that a register holds). */ + +CORE_ADDR +push_word (sp, buffer) + CORE_ADDR sp; + REGISTER_TYPE buffer; +{ + register int len = sizeof (REGISTER_TYPE); + +#if 1 INNER_THAN 2 + sp -= len; + write_memory (sp, &buffer, len); +#else /* stack grows upward */ + write_memory (sp, &buffer, len); + sp += len; +#endif /* stack grows upward */ + + return sp; +} + +/* Push LEN bytes with data at BUFFER. */ + +CORE_ADDR +push_bytes (sp, buffer, len) + CORE_ADDR sp; + char *buffer; + int len; +{ +#if 1 INNER_THAN 2 + sp -= len; + write_memory (sp, buffer, len); +#else /* stack grows upward */ + write_memory (sp, buffer, len); + sp += len; +#endif /* stack grows upward */ + + return sp; +} + +/* Push onto the stack the specified value VALUE. */ + +CORE_ADDR +value_push (sp, arg) + register CORE_ADDR sp; + value arg; +{ + register int len = TYPE_LENGTH (VALUE_TYPE (arg)); + +#if 1 INNER_THAN 2 + sp -= len; + write_memory (sp, VALUE_CONTENTS (arg), len); +#else /* stack grows upward */ + write_memory (sp, VALUE_CONTENTS (arg), len); + sp += len; +#endif /* stack grows upward */ + + return sp; +} + +/* Perform the standard coercions that are specified + for arguments to be passed to C functions. */ + +value +value_arg_coerce (arg) + value arg; +{ + register struct type *type; + + COERCE_ENUM (arg); + + type = VALUE_TYPE (arg); + + if (TYPE_CODE (type) == TYPE_CODE_INT + && TYPE_LENGTH (type) < sizeof (int)) + return value_cast (builtin_type_int, arg); + + if (type == builtin_type_float) + return value_cast (builtin_type_double, arg); + + return arg; +} + +/* Push the value ARG, first coercing it as an argument + to a C function. */ + +CORE_ADDR +value_arg_push (sp, arg) + register CORE_ADDR sp; + value arg; +{ + return value_push (sp, value_arg_coerce (arg)); +} + +#ifdef NEW_CALL_FUNCTION + +int +arg_stacklen(nargs, args) + int nargs; + value *args; +{ + int len = 0; + + while (--nargs >= 0) + len += TYPE_LENGTH(VALUE_TYPE(value_arg_coerce(args[nargs]))); + + return len; +} + +CORE_ADDR +function_address(function, type) + value function; + struct type **type; +{ + register CORE_ADDR funaddr; + register struct type *ftype = VALUE_TYPE(function); + register enum type_code code = TYPE_CODE(ftype); + + /* + * If it's a member function, just look at the function part + * of it. + */ + + /* Determine address to call. */ + if (code == TYPE_CODE_FUNC || code == TYPE_CODE_METHOD) { + funaddr = VALUE_ADDRESS(function); + *type = TYPE_TARGET_TYPE(ftype); + } else if (code == TYPE_CODE_PTR) { + funaddr = value_as_long(function); + if (TYPE_CODE(TYPE_TARGET_TYPE(ftype)) == TYPE_CODE_FUNC + || TYPE_CODE(TYPE_TARGET_TYPE(ftype)) == TYPE_CODE_METHOD) + *type = TYPE_TARGET_TYPE(TYPE_TARGET_TYPE(ftype)); + else + *type = builtin_type_int; + } else if (code == TYPE_CODE_INT) { + /* + * Handle the case of functions lacking debugging + * info. Their values are characters since their + * addresses are char + */ + if (TYPE_LENGTH(ftype) == 1) + + funaddr = value_as_long(value_addr(function)); + else + /* + * Handle integer used as address of a + * function. + */ + funaddr = value_as_long(function); + + *type = builtin_type_int; + } else + error("Invalid data type for function to be called."); + + return funaddr; +} + +/* Perform a function call in the inferior. + ARGS is a vector of values of arguments (NARGS of them). + FUNCTION is a value, the function to be called. + Returns a value representing what the function returned. + May fail to return, if a breakpoint or signal is hit + during the execution of the function. */ + +value +call_function(function, nargs, args) + value function; + int nargs; + value *args; +{ + register CORE_ADDR sp, pc; + struct type *value_type; + struct inferior_status inf_status; + struct cleanup *old_chain; + register CORE_ADDR funaddr; + int struct_return_bytes; + char retbuf[REGISTER_BYTES]; + + if (!have_inferior_p()) + error("Cannot invoke functions if the inferior is not running."); + + save_inferior_status(&inf_status, 1); + old_chain = make_cleanup(restore_inferior_status, &inf_status); + + sp = read_register(SP_REGNUM); + funaddr = function_address(function, &value_type); + /* + * Are we returning a value using a structure return or a + * normal value return? + */ + if (using_struct_return(function, funaddr, value_type)) + struct_return_bytes = TYPE_LENGTH(value_type); + else + struct_return_bytes = 0; + /* + * Create a call sequence customized for this function and + * the number of arguments for it. + */ + pc = setup_dummy(sp, funaddr, nargs, args, + struct_return_bytes, value_arg_push); + + /* + * Execute the stack dummy stub. The register state will be + * returned in retbuf. It is restored below. + */ + run_stack_dummy(pc, retbuf); + + /* + * This will restore the register context that existed before + * we called the dummy function. + */ + do_cleanups(old_chain); + + return value_being_returned(value_type, retbuf, struct_return_bytes); +} +#else + +/* Perform a function call in the inferior. + ARGS is a vector of values of arguments (NARGS of them). + FUNCTION is a value, the function to be called. + Returns a value representing what the function returned. + May fail to return, if a breakpoint or signal is hit + during the execution of the function. */ + +value +call_function (function, nargs, args) + value function; + int nargs; + value *args; +{ + register CORE_ADDR sp; + register int i; + CORE_ADDR start_sp; + static REGISTER_TYPE dummy[] = CALL_DUMMY; + REGISTER_TYPE dummy1[sizeof dummy / sizeof (REGISTER_TYPE)]; + CORE_ADDR old_sp; + struct type *value_type; + unsigned char struct_return; + CORE_ADDR struct_addr; + struct inferior_status inf_status; + struct cleanup *old_chain; + + if (!have_inferior_p ()) + error ("Cannot invoke functions if the inferior is not running."); + + save_inferior_status (&inf_status, 1); + old_chain = make_cleanup (restore_inferior_status, &inf_status); + + /* PUSH_DUMMY_FRAME is responsible for saving the inferior registers + (and POP_FRAME for restoring them). (At least on most machines) + they are saved on the stack in the inferior. */ + PUSH_DUMMY_FRAME; + + old_sp = sp = read_register (SP_REGNUM); + +#if 1 INNER_THAN 2 /* Stack grows down */ + sp -= sizeof dummy; + start_sp = sp; +#else /* Stack grows up */ + start_sp = sp; + sp += sizeof dummy; +#endif + + { + register CORE_ADDR funaddr; + register struct type *ftype = VALUE_TYPE (function); + register enum type_code code = TYPE_CODE (ftype); + + /* If it's a member function, just look at the function + part of it. */ + + /* Determine address to call. */ + if (code == TYPE_CODE_FUNC || code == TYPE_CODE_METHOD) + { + funaddr = VALUE_ADDRESS (function); + value_type = TYPE_TARGET_TYPE (ftype); + } + else if (code == TYPE_CODE_PTR) + { + funaddr = value_as_long (function); + if (TYPE_CODE (TYPE_TARGET_TYPE (ftype)) == TYPE_CODE_FUNC + || TYPE_CODE (TYPE_TARGET_TYPE (ftype)) == TYPE_CODE_METHOD) + value_type = TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (ftype)); + else + value_type = builtin_type_int; + } + else if (code == TYPE_CODE_INT) + { + /* Handle the case of functions lacking debugging info. + Their values are characters since their addresses are char */ + if (TYPE_LENGTH (ftype) == 1) + funaddr = value_as_long (value_addr (function)); + else + /* Handle integer used as address of a function. */ + funaddr = value_as_long (function); + + value_type = builtin_type_int; + } + else + error ("Invalid data type for function to be called."); + + /* Are we returning a value using a structure return or a normal + value return? */ + + struct_return = using_struct_return (function, funaddr, value_type); + + /* Create a call sequence customized for this function + and the number of arguments for it. */ + bcopy (dummy, dummy1, sizeof dummy); + FIX_CALL_DUMMY (dummy1, start_sp, funaddr, nargs, value_type); + } + +#ifndef CANNOT_EXECUTE_STACK + write_memory (start_sp, dummy1, sizeof dummy); + +#else + /* Convex Unix prohibits executing in the stack segment. */ + /* Hope there is empty room at the top of the text segment. */ + { + extern CORE_ADDR text_end; + static checked = 0; + if (!checked) + for (start_sp = text_end - sizeof dummy; start_sp < text_end; ++start_sp) + if (read_memory_integer (start_sp, 1) != 0) + error ("text segment full -- no place to put call"); + checked = 1; + sp = old_sp; + start_sp = text_end - sizeof dummy; + write_memory (start_sp, dummy1, sizeof dummy); + } +#endif /* CANNOT_EXECUTE_STACK */ +#ifdef STACK_ALIGN + /* If stack grows down, we must leave a hole at the top. */ + { + int len = 0; + + /* Reserve space for the return structure to be written on the + stack, if necessary */ + + if (struct_return) + len += TYPE_LENGTH (value_type); + + for (i = nargs - 1; i >= 0; i--) + len += TYPE_LENGTH (VALUE_TYPE (value_arg_coerce (args[i]))); +#ifdef CALL_DUMMY_STACK_ADJUST + len += CALL_DUMMY_STACK_ADJUST; +#endif +#if 1 INNER_THAN 2 + sp -= STACK_ALIGN (len) - len; +#else + sp += STACK_ALIGN (len) - len; +#endif + } +#endif /* STACK_ALIGN */ + + /* Reserve space for the return structure to be written on the + stack, if necessary */ + + if (struct_return) + { +#if 1 INNER_THAN 2 + sp -= TYPE_LENGTH (value_type); + struct_addr = sp; +#else + struct_addr = sp; + sp += TYPE_LENGTH (value_type); +#endif + } + + for (i = nargs - 1; i >= 0; i--) + sp = value_arg_push (sp, args[i]); + +#ifdef CALL_DUMMY_STACK_ADJUST +#if 1 INNER_THAN 2 + sp -= CALL_DUMMY_STACK_ADJUST; +#else + sp += CALL_DUMMY_STACK_ADJUST; +#endif +#endif /* CALL_DUMMY_STACK_ADJUST */ + + /* Store the address at which the structure is supposed to be + written. Note that this (and the code which reserved the space + above) assumes that gcc was used to compile this function. Since + it doesn't cost us anything but space and if the function is pcc + it will ignore this value, we will make that assumption. + + Also note that on some machines (like the sparc) pcc uses this + convention in a slightly twisted way also. */ + + if (struct_return) + STORE_STRUCT_RETURN (struct_addr, sp); + + /* Write the stack pointer. This is here because the statement above + might fool with it */ + write_register (SP_REGNUM, sp); + + /* Figure out the value returned by the function. */ + { + char retbuf[REGISTER_BYTES]; + + /* Execute the stack dummy routine, calling FUNCTION. + When it is done, discard the empty frame + after storing the contents of all regs into retbuf. */ + run_stack_dummy (start_sp + CALL_DUMMY_START_OFFSET, retbuf); + + do_cleanups (old_chain); + + return value_being_returned (value_type, retbuf, struct_return); + } +} +#endif + +/* Create a value for a string constant: + Call the function malloc in the inferior to get space for it, + then copy the data into that space + and then return the address with type char *. + PTR points to the string constant data; LEN is number of characters. */ + +value +value_string (ptr, len) + char *ptr; + int len; +{ + register value val; + register struct symbol *sym; + value blocklen; + register char *copy = (char *) alloca (len + 1); + char *i = ptr; + register char *o = copy, *ibeg = ptr; + register int c; +#ifdef KERNELDEBUG + extern int kernel_debugging; + + if (kernel_debugging) + error("Can't stuff string constants into kernel (yet)."); +#endif + + /* Copy the string into COPY, processing escapes. + We could not conveniently process them in expread + because the string there wants to be a substring of the input. */ + + while (i - ibeg < len) + { + c = *i++; + if (c == '\\') + { + c = parse_escape (&i); + if (c == -1) + continue; + } + *o++ = c; + } + *o = 0; + + /* Get the length of the string after escapes are processed. */ + + len = o - copy; + + /* Find the address of malloc in the inferior. */ + + sym = lookup_symbol ("malloc", 0, VAR_NAMESPACE, 0); + if (sym != 0) + { + if (SYMBOL_CLASS (sym) != LOC_BLOCK) + error ("\"malloc\" exists in this program but is not a function."); + val = value_of_variable (sym); + } + else + { + register int i; + for (i = 0; i < misc_function_count; i++) + if (!strcmp (misc_function_vector[i].name, "malloc")) + break; + if (i < misc_function_count) + val = value_from_long (builtin_type_long, + (LONGEST) misc_function_vector[i].address); + else + error ("String constants require the program to have a function \"malloc\"."); + } + + blocklen = value_from_long (builtin_type_int, (LONGEST) (len + 1)); + val = call_function (val, 1, &blocklen); + if (value_zerop (val)) + error ("No memory available for string constant."); + write_memory ((CORE_ADDR) value_as_long (val), copy, len + 1); + VALUE_TYPE (val) = lookup_pointer_type (builtin_type_char); + return val; +} + +static int +type_field_index(t, name) + register struct type *t; + register char *name; +{ + register int i; + + for (i = TYPE_NFIELDS(t); --i >= 0;) + { + register char *t_field_name = TYPE_FIELD_NAME (t, i); + + if (t_field_name && !strcmp (t_field_name, name)) + break; + } + return (i); +} + +/* Given ARG1, a value of type (pointer to a)* structure/union, + extract the component named NAME from the ultimate target structure/union + and return it as a value with its appropriate type. + ERR is used in the error message if ARG1's type is wrong. + + C++: ARGS is a list of argument types to aid in the selection of + an appropriate method. Also, handle derived types. + + STATIC_MEMFUNCP, if non-NULL, points to a caller-supplied location + where the truthvalue of whether the function that was resolved was + a static member function or not. + + ERR is an error message to be printed in case the field is not found. */ + +value +value_struct_elt (arg1, args, name, static_memfuncp, err) + register value arg1, *args; + char *name; + int *static_memfuncp; + char *err; +{ + register struct type *t; + register int i; + int found = 0; + + struct type *baseclass; + + COERCE_ARRAY (arg1); + + t = VALUE_TYPE (arg1); + + /* Check for the usual case: we have pointer, target type is a struct + * and `name' is a legal field of the struct. In this case, we can + * just snarf the value of the field & not waste time while value_ind + * sucks over the entire struct. */ + if (! args) + { + if (TYPE_CODE(t) == TYPE_CODE_PTR + && (TYPE_CODE((baseclass = TYPE_TARGET_TYPE(t))) == TYPE_CODE_STRUCT + || TYPE_CODE(baseclass) == TYPE_CODE_UNION) + && (i = type_field_index(baseclass, name)) >= 0) + { + register int offset; + register struct type *f = TYPE_FIELD_TYPE(baseclass, i); + + offset = TYPE_FIELD_BITPOS(baseclass, i) >> 3; + if (TYPE_FIELD_BITSIZE(baseclass, i) == 0) + return value_at(f, (CORE_ADDR)(value_as_long(arg1) + offset)); + } + } + + /* Follow pointers until we get to a non-pointer. */ + + while (TYPE_CODE (t) == TYPE_CODE_PTR || TYPE_CODE (t) == TYPE_CODE_REF) + { + arg1 = value_ind (arg1); + COERCE_ARRAY (arg1); + t = VALUE_TYPE (arg1); + } + + if (TYPE_CODE (t) == TYPE_CODE_MEMBER) + error ("not implemented: member type in value_struct_elt"); + + if (TYPE_CODE (t) != TYPE_CODE_STRUCT + && TYPE_CODE (t) != TYPE_CODE_UNION) + error ("Attempt to extract a component of a value that is not a %s.", err); + + baseclass = t; + + /* Assume it's not, unless we see that it is. */ + if (static_memfuncp) + *static_memfuncp =0; + + if (!args) + { + /* if there are no arguments ...do this... */ + + /* Try as a variable first, because if we succeed, there + is less work to be done. */ + while (t) + { + i = type_field_index(t, name); + if (i >= 0) + return TYPE_FIELD_STATIC (t, i) + ? value_static_field (t, name, i) : value_field (arg1, i); + + if (TYPE_N_BASECLASSES (t) == 0) + break; + + t = TYPE_BASECLASS (t, 1); + VALUE_TYPE (arg1) = t; /* side effect! */ + } + + /* C++: If it was not found as a data field, then try to + return it as a pointer to a method. */ + t = baseclass; + VALUE_TYPE (arg1) = t; /* side effect! */ + + if (destructor_name_p (name, t)) + error ("use `info method' command to print out value of destructor"); + + while (t) + { + for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; --i) + { + if (! strcmp (TYPE_FN_FIELDLIST_NAME (t, i), name)) + { + error ("use `info method' command to print value of method \"%s\"", name); + } + } + + if (TYPE_N_BASECLASSES (t) == 0) + break; + + t = TYPE_BASECLASS (t, 1); + } + + error ("There is no field named %s.", name); + return 0; + } + + if (destructor_name_p (name, t)) + { + if (!args[1]) + { + /* destructors are a special case. */ + return (value)value_fn_field (arg1, 0, + TYPE_FN_FIELDLIST_LENGTH (t, 0)); + } + else + { + error ("destructor should not have any argument"); + } + } + + /* This following loop is for methods with arguments. */ + while (t) + { + /* Look up as method first, because that is where we + expect to find it first. */ + for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; i--) + { + struct fn_field *f = TYPE_FN_FIELDLIST1 (t, i); + + if (!strcmp (TYPE_FN_FIELDLIST_NAME (t, i), name)) + { + int j; + struct fn_field *f = TYPE_FN_FIELDLIST1 (t, i); + + found = 1; + for (j = TYPE_FN_FIELDLIST_LENGTH (t, i) - 1; j >= 0; --j) + if (!typecmp (TYPE_FN_FIELD_STATIC_P (f, j), + TYPE_FN_FIELD_ARGS (f, j), args)) + { + if (TYPE_FN_FIELD_VIRTUAL_P (f, j)) + return (value)value_virtual_fn_field (arg1, f, j, t); + if (TYPE_FN_FIELD_STATIC_P (f, j) && static_memfuncp) + *static_memfuncp = 1; + return (value)value_fn_field (arg1, i, j); + } + } + } + + if (TYPE_N_BASECLASSES (t) == 0) + break; + + t = TYPE_BASECLASS (t, 1); + VALUE_TYPE (arg1) = t; /* side effect! */ + } + + if (found) + { + error ("Structure method %s not defined for arglist.", name); + return 0; + } + else + { + /* See if user tried to invoke data as function */ + t = baseclass; + while (t) + { + i = type_field_index(t, name); + if (i >= 0) + return TYPE_FIELD_STATIC (t, i) + ? value_static_field (t, name, i) : value_field (arg1, i); + + if (TYPE_N_BASECLASSES (t) == 0) + break; + + t = TYPE_BASECLASS (t, 1); + VALUE_TYPE (arg1) = t; /* side effect! */ + } + error ("Structure has no component named %s.", name); + } +} + +/* C++: return 1 is NAME is a legitimate name for the destructor + of type TYPE. If TYPE does not have a destructor, or + if NAME is inappropriate for TYPE, an error is signaled. */ +int +destructor_name_p (name, type) + char *name; + struct type *type; +{ + /* destructors are a special case. */ + char *dname = TYPE_NAME (type); + + if (name[0] == '~') + { + if (! TYPE_HAS_DESTRUCTOR (type)) + error ("type `%s' does not have destructor defined", + TYPE_NAME (type)); + /* Skip past the "struct " at the front. */ + while (*dname++ != ' ') ; + if (strcmp (dname, name+1)) + error ("destructor specification error"); + else + return 1; + } + return 0; +} + +/* C++: Given ARG1, a value of type (pointer to a)* structure/union, + return 1 if the component named NAME from the ultimate + target structure/union is defined, otherwise, return 0. */ + +int +check_field (arg1, name) + register value arg1; + char *name; +{ + register struct type *t; + register int i; + int found = 0; + + struct type *baseclass; + + COERCE_ARRAY (arg1); + + t = VALUE_TYPE (arg1); + + /* Follow pointers until we get to a non-pointer. */ + + while (TYPE_CODE (t) == TYPE_CODE_PTR || TYPE_CODE (t) == TYPE_CODE_REF) + t = TYPE_TARGET_TYPE (t); + + if (TYPE_CODE (t) == TYPE_CODE_MEMBER) + error ("not implemented: member type in check_field"); + + if (TYPE_CODE (t) != TYPE_CODE_STRUCT + && TYPE_CODE (t) != TYPE_CODE_UNION) + error ("Internal error: `this' is not an aggregate"); + + baseclass = t; + + while (t) + { + for (i = TYPE_NFIELDS (t) - 1; i >= 0; i--) + { + char *t_field_name = TYPE_FIELD_NAME (t, i); + if (t_field_name && !strcmp (t_field_name, name)) + goto success; + } + if (TYPE_N_BASECLASSES (t) == 0) + break; + + t = TYPE_BASECLASS (t, 1); + VALUE_TYPE (arg1) = t; /* side effect! */ + } + + /* C++: If it was not found as a data field, then try to + return it as a pointer to a method. */ + t = baseclass; + + /* Destructors are a special case. */ + if (destructor_name_p (name, t)) + goto success; + + while (t) + { + for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; --i) + { + if (!strcmp (TYPE_FN_FIELDLIST_NAME (t, i), name)) + return 1; + } + + if (TYPE_N_BASECLASSES (t) == 0) + break; + + t = TYPE_BASECLASS (t, 1); + } + return 0; + + success: + t = VALUE_TYPE (arg1); + while (TYPE_CODE (t) == TYPE_CODE_PTR || TYPE_CODE (t) == TYPE_CODE_REF) + { + arg1 = value_ind (arg1); + COERCE_ARRAY (arg1); + t = VALUE_TYPE (arg1); + } +} + +/* C++: Given an aggregate type DOMAIN, and a member name NAME, + return the address of this member as a pointer to member + type. If INTYPE is non-null, then it will be the type + of the member we are looking for. This will help us resolve + pointers to member functions. */ + +value +value_struct_elt_for_address (domain, intype, name) + struct type *domain, *intype; + char *name; +{ + register struct type *t = domain; + register int i; + int found = 0; + value v; + + struct type *baseclass; + + if (TYPE_CODE (t) != TYPE_CODE_STRUCT + && TYPE_CODE (t) != TYPE_CODE_UNION) + error ("Internal error: non-aggregate type to value_struct_elt_for_address"); + + baseclass = t; + + while (t) + { + for (i = TYPE_NFIELDS (t) - 1; i >= 0; i--) + { + char *t_field_name = TYPE_FIELD_NAME (t, i); + if (t_field_name && !strcmp (t_field_name, name)) + { + if (TYPE_FIELD_PACKED (t, i)) + error ("pointers to bitfield members not allowed"); + + v = value_from_long (builtin_type_int, + (LONGEST) (TYPE_FIELD_BITPOS (t, i) >> 3)); + VALUE_TYPE (v) = lookup_pointer_type ( + lookup_member_type (TYPE_FIELD_TYPE (t, i), baseclass)); + return v; + } + } + + if (TYPE_N_BASECLASSES (t) == 0) + break; + + t = TYPE_BASECLASS (t, 1); + } + + /* C++: If it was not found as a data field, then try to + return it as a pointer to a method. */ + t = baseclass; + + /* Destructors are a special case. */ + if (destructor_name_p (name, t)) + { + error ("pointers to destructors not implemented yet"); + } + + /* Perform all necessary dereferencing. */ + while (intype && TYPE_CODE (intype) == TYPE_CODE_PTR) + intype = TYPE_TARGET_TYPE (intype); + + while (t) + { + for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; --i) + { + if (!strcmp (TYPE_FN_FIELDLIST_NAME (t, i), name)) + { + int j = TYPE_FN_FIELDLIST_LENGTH (t, i); + struct fn_field *f = TYPE_FN_FIELDLIST1 (t, i); + + if (intype == 0 && j > 1) + error ("non-unique member `%s' requires type instantiation", name); + if (intype) + { + while (j--) + if (TYPE_FN_FIELD_TYPE (f, j) == intype) + break; + if (j < 0) + error ("no member function matches that type instantiation"); + } + else + j = 0; + + if (TYPE_FN_FIELD_VIRTUAL_P (f, j)) + { + v = value_from_long (builtin_type_long, + (LONGEST) TYPE_FN_FIELD_VOFFSET (f, j)); + } + else + { + struct symbol *s = lookup_symbol (TYPE_FN_FIELD_PHYSNAME (f, j), + 0, VAR_NAMESPACE, 0); + v = locate_var_value (s, 0); + } + VALUE_TYPE (v) = lookup_pointer_type (lookup_member_type (TYPE_FN_FIELD_TYPE (f, j), baseclass)); + return v; + } + } + + if (TYPE_N_BASECLASSES (t) == 0) + break; + + t = TYPE_BASECLASS (t, 1); + } + return 0; +} + +/* Compare two argument lists and return the position in which they differ, + or zero if equal. + + STATICP is nonzero if the T1 argument list came from a + static member function. + + For non-static member functions, we ignore the first argument, + which is the type of the instance variable. This is because we want + to handle calls with objects from derived classes. This is not + entirely correct: we should actually check to make sure that a + requested operation is type secure, shouldn't we? */ + +int +typecmp (staticp, t1, t2) + int staticp; + struct type *t1[]; + value t2[]; +{ + int i; + + if (staticp && t1 == 0) + return t2[1] != 0; + if (t1 == 0) + return 1; + if (t1[0]->code == TYPE_CODE_VOID) return 0; + if (t1[!staticp] == 0) return 0; + for (i = !staticp; t1[i] && t1[i]->code != TYPE_CODE_VOID; i++) + { + if (! t2[i] + || t1[i]->code != t2[i]->type->code + || t1[i]->target_type != t2[i]->type->target_type) + return i+1; + } + if (!t1[i]) return 0; + return t2[i] ? i+1 : 0; +} + +/* C++: return the value of the class instance variable, if one exists. + Flag COMPLAIN signals an error if the request is made in an + inappropriate context. */ +value +value_of_this (complain) + int complain; +{ + extern FRAME selected_frame; + struct symbol *func, *sym; + char *funname = 0; + struct block *b; + int i; + + if (selected_frame == 0) + if (complain) + error ("no frame selected"); + else return 0; + + func = get_frame_function (selected_frame); + if (func) + funname = SYMBOL_NAME (func); + else + if (complain) + error ("no `this' in nameless context"); + else return 0; + + b = SYMBOL_BLOCK_VALUE (func); + i = BLOCK_NSYMS (b); + if (i <= 0) + if (complain) + error ("no args, no `this'"); + else return 0; + + sym = BLOCK_SYM (b, 0); + if (strncmp ("$this", SYMBOL_NAME (sym), 5)) + if (complain) + error ("current stack frame not in method"); + else return 0; + + return read_var_value (sym, selected_frame); +} diff --git a/gnu/usr.bin/gdb/valprint.c b/gnu/usr.bin/gdb/valprint.c new file mode 100644 index 0000000000..781eb296d3 --- /dev/null +++ b/gnu/usr.bin/gdb/valprint.c @@ -0,0 +1,1430 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)valprint.c 6.5 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Print values for GNU debugger gdb. + Copyright (C) 1986, 1988, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "value.h" + +/* GNU software is only expected to run on systems with 32-bit integers. */ +#define UINT_MAX 0xffffffff + +/* Maximum number of chars to print for a string pointer value + or vector contents, or UINT_MAX for no limit. */ + +static unsigned int print_max; + +static void type_print_varspec_suffix (); +static void type_print_varspec_prefix (); +static void type_print_base (); +static void type_print_method_args (); + + +char **unsigned_type_table; +char **signed_type_table; +char **float_type_table; + + +/* Print repeat counts if there are more than this + many repetitions of an element in an array. */ +#define REPEAT_COUNT_THRESHOLD 10 + +/* Print the character string STRING, printing at most LENGTH characters. + Printing stops early if the number hits print_max; repeat counts + are printed as appropriate. Print ellipses at the end if we + had to stop before printing LENGTH characters, or if FORCE_ELLIPSES. */ + +void +print_string (stream, string, length, force_ellipses) + FILE *stream; + char *string; + unsigned int length; + int force_ellipses; +{ + register unsigned int i; + unsigned int things_printed = 0; + int in_quotes = 0; + int need_comma = 0; + + if (length == 0) + { + fputs_filtered ("\"\"", stdout); + return; + } + + for (i = 0; i < length && things_printed < print_max; ++i) + { + /* Position of the character we are examining + to see whether it is repeated. */ + unsigned int rep1; + /* Number of repititions we have detected so far. */ + unsigned int reps; + + QUIT; + + if (need_comma) + { + fputs_filtered (", ", stream); + need_comma = 0; + } + + rep1 = i + 1; + reps = 1; + while (rep1 < length && string[rep1] == string[i]) + { + ++rep1; + ++reps; + } + + if (reps > REPEAT_COUNT_THRESHOLD) + { + if (in_quotes) + { + fputs_filtered ("\", ", stream); + in_quotes = 0; + } + fputs_filtered ("'", stream); + printchar (string[i], stream, '\''); + fprintf_filtered (stream, "' ", reps); + i = rep1 - 1; + things_printed += REPEAT_COUNT_THRESHOLD; + need_comma = 1; + } + else + { + if (!in_quotes) + { + fputs_filtered ("\"", stream); + in_quotes = 1; + } + printchar (string[i], stream, '"'); + ++things_printed; + } + } + + /* Terminate the quotes if necessary. */ + if (in_quotes) + fputs_filtered ("\"", stream); + + if (force_ellipses || i < length) + fputs_filtered ("...", stream); +} + +/* Print the value VAL in C-ish syntax on stream STREAM. + FORMAT is a format-letter, or 0 for print in natural format of data type. + If the object printed is a string pointer, returns + the number of string bytes printed. */ + +int +value_print (val, stream, format, pretty) + value val; + FILE *stream; + char format; + enum val_prettyprint pretty; +{ + register unsigned int i, n, typelen; + + /* A "repeated" value really contains several values in a row. + They are made by the @ operator. + Print such values as if they were arrays. */ + + if (VALUE_REPEATED (val)) + { + n = VALUE_REPETITIONS (val); + typelen = TYPE_LENGTH (VALUE_TYPE (val)); + fprintf_filtered (stream, "{"); + /* Print arrays of characters using string syntax. */ + if (typelen == 1 && TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_INT + && format == 0) + print_string (stream, VALUE_CONTENTS (val), n, 0); + else + { + unsigned int things_printed = 0; + + for (i = 0; i < n && things_printed < print_max; i++) + { + /* Position of the array element we are examining to see + whether it is repeated. */ + unsigned int rep1; + /* Number of repititions we have detected so far. */ + unsigned int reps; + + if (i != 0) + fprintf_filtered (stream, ", "); + + rep1 = i + 1; + reps = 1; + while (rep1 < n + && !bcmp (VALUE_CONTENTS (val) + typelen * i, + VALUE_CONTENTS (val) + typelen * rep1, typelen)) + { + ++reps; + ++rep1; + } + + if (reps > REPEAT_COUNT_THRESHOLD) + { + val_print (VALUE_TYPE (val), + VALUE_CONTENTS (val) + typelen * i, + VALUE_ADDRESS (val) + typelen * i, + stream, format, 1, 0, pretty); + fprintf (stream, " ", reps); + i = rep1 - 1; + things_printed += REPEAT_COUNT_THRESHOLD; + } + else + { + val_print (VALUE_TYPE (val), + VALUE_CONTENTS (val) + typelen * i, + VALUE_ADDRESS (val) + typelen * i, + stream, format, 1, 0, pretty); + things_printed++; + } + } + if (i < n) + fprintf_filtered (stream, "..."); + } + fprintf_filtered (stream, "}"); + return n * typelen; + } + else + { + /* If it is a pointer, indicate what it points to. + + Print type also if it is a reference. + + C++: if it is a member pointer, we will take care + of that when we print it. */ + if (TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_PTR + || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_REF) + { + fprintf_filtered (stream, "("); + type_print (VALUE_TYPE (val), "", stream, -1); + fprintf_filtered (stream, ") "); + + /* If this is a function pointer, try to print what + function it is pointing to by name. */ + if (TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (val))) + == TYPE_CODE_FUNC) + { + print_address (((int *) VALUE_CONTENTS (val))[0], stream); + /* Return value is irrelevant except for string pointers. */ + return 0; + } + } + return val_print (VALUE_TYPE (val), VALUE_CONTENTS (val), + VALUE_ADDRESS (val), stream, format, 1, 0, pretty); + } +} + +static int prettyprint; /* Controls prettyprinting of structures. */ +int unionprint; /* Controls printing of nested unions. */ +static void scalar_print_hack(); +void (*default_scalar_print)() = scalar_print_hack; + +/* Print data of type TYPE located at VALADDR (within GDB), + which came from the inferior at address ADDRESS, + onto stdio stream STREAM according to FORMAT + (a letter or 0 for natural format). + + If the data are a string pointer, returns the number of + sting characters printed. + + if DEREF_REF is nonzero, then dereference references, + otherwise just print them like pointers. + + The PRETTY parameter controls prettyprinting. */ + +int +val_print (type, valaddr, address, stream, format, + deref_ref, recurse, pretty) + struct type *type; + char *valaddr; + CORE_ADDR address; + FILE *stream; + char format; + int deref_ref; + int recurse; + enum val_prettyprint pretty; +{ + register unsigned int i; + int len, n_baseclasses; + struct type *elttype; + int eltlen; + LONGEST val; + unsigned char c; + + if (pretty == Val_pretty_default) + { + pretty = prettyprint ? Val_prettyprint : Val_no_prettyprint; + } + + QUIT; + + if (TYPE_FLAGS (type) & TYPE_FLAG_STUB) + { + fprintf_filtered (stream, ""); + fflush (stream); + return 0; + } + + switch (TYPE_CODE (type)) + { + case TYPE_CODE_ARRAY: + if (TYPE_LENGTH (type) >= 0 + && TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > 0) + { + elttype = TYPE_TARGET_TYPE (type); + eltlen = TYPE_LENGTH (elttype); + len = TYPE_LENGTH (type) / eltlen; + fprintf_filtered (stream, "{"); + /* For an array of chars, print with string syntax. */ + if (eltlen == 1 && TYPE_CODE (elttype) == TYPE_CODE_INT + && format == 0) + print_string (stream, valaddr, len, 0); + else + { + unsigned int things_printed = 0; + + for (i = 0; i < len && things_printed < print_max; i++) + { + /* Position of the array element we are examining to see + whether it is repeated. */ + unsigned int rep1; + /* Number of repititions we have detected so far. */ + unsigned int reps; + + if (i > 0) + fprintf_filtered (stream, ", "); + + rep1 = i + 1; + reps = 1; + while (rep1 < len + && !bcmp (valaddr + i * eltlen, + valaddr + rep1 * eltlen, eltlen)) + { + ++reps; + ++rep1; + } + + if (reps > REPEAT_COUNT_THRESHOLD) + { + val_print (elttype, valaddr + i * eltlen, + 0, stream, format, deref_ref, + recurse + 1, pretty); + fprintf_filtered (stream, " ", reps); + i = rep1 - 1; + things_printed += REPEAT_COUNT_THRESHOLD; + } + else + { + val_print (elttype, valaddr + i * eltlen, + 0, stream, format, deref_ref, + recurse + 1, pretty); + things_printed++; + } + } + if (i < len) + fprintf_filtered (stream, "..."); + } + fprintf_filtered (stream, "}"); + break; + } + /* Array of unspecified length: treat like pointer to first elt. */ + valaddr = (char *) &address; + + case TYPE_CODE_PTR: + if (format) + { + print_scalar_formatted (valaddr, type, format, 0, stream); + break; + } + if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_METHOD) + { + struct type *domain = TYPE_DOMAIN_TYPE (TYPE_TARGET_TYPE (type)); + struct type *target = TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (type)); + struct fn_field *f; + int j, len2; + char *kind = ""; + + val = unpack_long (builtin_type_int, valaddr); + if (val < 128) + { + len = TYPE_NFN_FIELDS (domain); + for (i = 0; i < len; i++) + { + f = TYPE_FN_FIELDLIST1 (domain, i); + len2 = TYPE_FN_FIELDLIST_LENGTH (domain, i); + + for (j = 0; j < len2; j++) + { + QUIT; + if (TYPE_FN_FIELD_VOFFSET (f, j) == val) + { + kind = "virtual"; + goto common; + } + } + } + } + else + { + struct symbol *sym = find_pc_function ((CORE_ADDR) val); + if (sym == 0) + error ("invalid pointer to member function"); + len = TYPE_NFN_FIELDS (domain); + for (i = 0; i < len; i++) + { + f = TYPE_FN_FIELDLIST1 (domain, i); + len2 = TYPE_FN_FIELDLIST_LENGTH (domain, i); + + for (j = 0; j < len2; j++) + { + QUIT; + if (!strcmp (SYMBOL_NAME (sym), TYPE_FN_FIELD_PHYSNAME (f, j))) + goto common; + } + } + } + common: + if (i < len) + { + fprintf_filtered (stream, "&"); + type_print_varspec_prefix (TYPE_FN_FIELD_TYPE (f, j), stream, 0, 0); + fprintf (stream, kind); + if (TYPE_FN_FIELD_PHYSNAME (f, j)[0] == '_' + && TYPE_FN_FIELD_PHYSNAME (f, j)[1] == '$') + type_print_method_args + (TYPE_FN_FIELD_ARGS (f, j) + 1, "~", + TYPE_FN_FIELDLIST_NAME (domain, i), 0, stream); + else + type_print_method_args + (TYPE_FN_FIELD_ARGS (f, j), "", + TYPE_FN_FIELDLIST_NAME (domain, i), 0, stream); + break; + } + fprintf_filtered (stream, "("); + type_print (type, "", stream, -1); + fprintf_filtered (stream, ") %d", (int) val >> 3); + } + else if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_MEMBER) + { + struct type *domain = TYPE_DOMAIN_TYPE (TYPE_TARGET_TYPE (type)); + struct type *target = TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (type)); + char *kind = ""; + + /* VAL is a byte offset into the structure type DOMAIN. + Find the name of the field for that offset and + print it. */ + int extra = 0; + int bits = 0; + len = TYPE_NFIELDS (domain); + /* @@ Make VAL into bit offset */ + val = unpack_long (builtin_type_int, valaddr) << 3; + for (i = 0; i < len; i++) + { + int bitpos = TYPE_FIELD_BITPOS (domain, i); + QUIT; + if (val == bitpos) + break; + if (val < bitpos && i > 0) + { + int ptrsize = (TYPE_LENGTH (builtin_type_char) * TYPE_LENGTH (target)); + /* Somehow pointing into a field. */ + i -= 1; + extra = (val - TYPE_FIELD_BITPOS (domain, i)); + if (extra & 0x3) + bits = 1; + else + extra >>= 3; + break; + } + } + if (i < len) + { + fprintf_filtered (stream, "&"); + type_print_base (domain, stream, 0, 0); + fprintf_filtered (stream, "::"); + fputs_filtered (TYPE_FIELD_NAME (domain, i), stream); + if (extra) + fprintf_filtered (stream, " + %d bytes", extra); + if (bits) + fprintf_filtered (stream, " (offset in bits)"); + break; + } + fprintf_filtered (stream, "%d", val >> 3); + } + else + { + fprintf_filtered (stream, "0x%x", * (int *) valaddr); + /* For a pointer to char or unsigned char, + also print the string pointed to, unless pointer is null. */ + + /* For an array of chars, print with string syntax. */ + elttype = TYPE_TARGET_TYPE (type); + i = 0; /* Number of characters printed. */ + if (TYPE_LENGTH (elttype) == 1 + && TYPE_CODE (elttype) == TYPE_CODE_INT + && format == 0 + && unpack_long (type, valaddr) != 0 + /* If print_max is UINT_MAX, the alloca below will fail. + In that case don't try to print the string. */ + && print_max < UINT_MAX) + { + fprintf_filtered (stream, " "); + + /* Get first character. */ + if (read_memory ( (CORE_ADDR) unpack_long (type, valaddr), + &c, 1)) + { + /* First address out of bounds. */ + fprintf_filtered (stream, "
", + (* (int *) valaddr)); + break; + } + else + { + /* A real string. */ + int out_of_bounds = 0; + char *string = (char *) alloca (print_max); + + /* If the loop ends by us hitting print_max characters, + we need to have elipses at the end. */ + int force_ellipses = 1; + + /* This loop only fetches print_max characters, even + though print_string might want to print more + (with repeated characters). This is so that + we don't spend forever fetching if we print + a long string consisting of the same character + repeated. */ + while (i < print_max) + { + QUIT; + if (read_memory ((CORE_ADDR) unpack_long (type, valaddr) + + i, &c, 1)) + { + out_of_bounds = 1; + force_ellipses = 0; + break; + } + else if (c == '\0') + { + force_ellipses = 0; + break; + } + else + string[i++] = c; + } + + if (i != 0) + print_string (stream, string, i, force_ellipses); + if (out_of_bounds) + fprintf_filtered (stream, + "
", + (*(int *) valaddr) + i); + } + + fflush (stream); + } + /* Return number of characters printed, plus one for the + terminating null if we have "reached the end". */ + return i + (print_max && i != print_max); + } + break; + + case TYPE_CODE_MEMBER: + error ("not implemented: member type in val_print"); + break; + + case TYPE_CODE_REF: + fprintf_filtered (stream, "(0x%x &) = ", * (int *) valaddr); + /* De-reference the reference. */ + if (deref_ref) + { + if (TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_UNDEF) + { + value val = value_at (TYPE_TARGET_TYPE (type), * (int *) valaddr); + val_print (VALUE_TYPE (val), VALUE_CONTENTS (val), + VALUE_ADDRESS (val), stream, format, + deref_ref, recurse + 1, pretty); + } + else + fprintf_filtered (stream, "???"); + } + break; + + case TYPE_CODE_UNION: + if (recurse && !unionprint) + { + fprintf_filtered (stream, "{...}"); + break; + } + /* Fall through. */ + case TYPE_CODE_STRUCT: + fprintf_filtered (stream, "{"); + len = TYPE_NFIELDS (type); + n_baseclasses = TYPE_N_BASECLASSES (type); + for (i = 1; i <= n_baseclasses; i++) + { + fprintf_filtered (stream, "\n"); + if (pretty) + print_spaces_filtered (2 + 2 * recurse, stream); + fputs_filtered ("<", stream); + fputs_filtered (TYPE_NAME (TYPE_BASECLASS (type, i)), stream); + fputs_filtered ("> = ", stream); + val_print (TYPE_FIELD_TYPE (type, 0), + valaddr + TYPE_FIELD_BITPOS (type, i-1) / 8, + 0, stream, 0, 0, recurse + 1, pretty); + } + if (i > 1) { + fprintf_filtered (stream, "\n"); + print_spaces_filtered (2 + 2 * recurse, stream); + fputs_filtered ("members of ", stream); + fputs_filtered (TYPE_NAME (type), stream); + fputs_filtered (": ", stream); + } + if (!len && i == 1) + fprintf_filtered (stream, ""); + else + { + for (i -= 1; i < len; i++) + { + if (i > n_baseclasses) fprintf_filtered (stream, ", "); + if (pretty) + { + fprintf_filtered (stream, "\n"); + print_spaces_filtered (2 + 2 * recurse, stream); + } + fputs_filtered (TYPE_FIELD_NAME (type, i), stream); + fputs_filtered (" = ", stream); + /* check if static field */ + if (TYPE_FIELD_STATIC (type, i)) + { + value v; + + v = value_static_field (type, TYPE_FIELD_NAME (type, i), i); + val_print (TYPE_FIELD_TYPE (type, i), + VALUE_CONTENTS (v), 0, stream, format, + deref_ref, recurse + 1, pretty); + } + else if (TYPE_FIELD_PACKED (type, i)) + { + char *valp = (char *) & val; + union {int i; char c;} test; + test.i = 1; + if (test.c != 1) + valp += sizeof val - TYPE_LENGTH (TYPE_FIELD_TYPE (type, i)); + val = unpack_field_as_long (type, valaddr, i); + val_print (TYPE_FIELD_TYPE (type, i), valp, 0, + stream, format, deref_ref, recurse + 1, pretty); + } + else + { + val_print (TYPE_FIELD_TYPE (type, i), + valaddr + TYPE_FIELD_BITPOS (type, i) / 8, + 0, stream, format, deref_ref, + recurse + 1, pretty); + } + } + if (pretty) + { + fprintf_filtered (stream, "\n"); + print_spaces_filtered (2 * recurse, stream); + } + } + fprintf_filtered (stream, "}"); + break; + + case TYPE_CODE_ENUM: + if (format) + { + print_scalar_formatted (valaddr, type, format, 0, stream); + break; + } + len = TYPE_NFIELDS (type); + val = unpack_long (builtin_type_int, valaddr); + for (i = 0; i < len; i++) + { + QUIT; + if (val == TYPE_FIELD_BITPOS (type, i)) + break; + } + if (i < len) + fputs_filtered (TYPE_FIELD_NAME (type, i), stream); + else + fprintf_filtered (stream, "%d", (int) val); + break; + + case TYPE_CODE_FUNC: + if (format) + { + print_scalar_formatted (valaddr, type, format, 0, stream); + break; + } + fprintf_filtered (stream, "{"); + type_print (type, "", stream, -1); + fprintf_filtered (stream, "} "); + fprintf_filtered (stream, "0x%x", address); + break; + + case TYPE_CODE_INT: + if (format) + print_scalar_formatted (valaddr, type, format, 0, stream); + else + { + (*default_scalar_print)(stream, type, unpack_long(type, valaddr)); +#ifdef notdef + if (TYPE_LENGTH (type) == 1) + { + fprintf_filtered (stream, " '"); + printchar ((unsigned char) unpack_long (type, valaddr), + stream, '\''); + fprintf_filtered (stream, "'"); + } +#endif + } + break; + + case TYPE_CODE_FLT: + if (format) + print_scalar_formatted (valaddr, type, format, 0, stream); + else + print_floating (valaddr, type, stream); + break; + + case TYPE_CODE_VOID: + fprintf_filtered (stream, "void"); + break; + + default: + error ("Invalid type code in symbol table."); + } + fflush (stream); + return 0; +} + +/* Print a description of a type TYPE + in the form of a declaration of a variable named VARSTRING. + Output goes to STREAM (via stdio). + If SHOW is positive, we show the contents of the outermost level + of structure even if there is a type name that could be used instead. + If SHOW is negative, we never show the details of elements' types. */ + +void +type_print (type, varstring, stream, show) + struct type *type; + char *varstring; + FILE *stream; + int show; +{ + type_print_1 (type, varstring, stream, show, 0); +} + +/* LEVEL is the depth to indent lines by. */ + +void +type_print_1 (type, varstring, stream, show, level) + struct type *type; + char *varstring; + FILE *stream; + int show; + int level; +{ + register enum type_code code; + type_print_base (type, stream, show, level); + code = TYPE_CODE (type); + if ((varstring && *varstring) + || + /* Need a space if going to print stars or brackets; + but not if we will print just a type name. */ + ((show > 0 || TYPE_NAME (type) == 0) + && + (code == TYPE_CODE_PTR || code == TYPE_CODE_FUNC + || code == TYPE_CODE_METHOD + || code == TYPE_CODE_ARRAY + || code == TYPE_CODE_MEMBER + || code == TYPE_CODE_REF))) + fprintf_filtered (stream, " "); + type_print_varspec_prefix (type, stream, show, 0); + fputs_filtered (varstring, stream); + type_print_varspec_suffix (type, stream, show, 0); +} + +/* Print the method arguments ARGS to the file STREAM. */ +static void +type_print_method_args (args, prefix, varstring, staticp, stream) + struct type **args; + char *prefix, *varstring; + int staticp; + FILE *stream; +{ + int i; + + fputs_filtered (" ", stream); + fputs_filtered (prefix, stream); + fputs_filtered (varstring, stream); + fputs_filtered (" (", stream); + if (args && args[!staticp] && args[!staticp]->code != TYPE_CODE_VOID) + { + i = !staticp; /* skip the class variable */ + while (1) + { + type_print (args[i++], "", stream, 0); + if (!args[i]) + { + fprintf_filtered (stream, " ..."); + break; + } + else if (args[i]->code != TYPE_CODE_VOID) + { + fprintf_filtered (stream, ", "); + } + else break; + } + } + fprintf_filtered (stream, ")"); +} + +/* If TYPE is a derived type, then print out derivation + information. Print out all layers of the type heirarchy + until we encounter one with multiple inheritance. + At that point, print out that ply, and return. */ +static void +type_print_derivation_info (stream, type) + FILE *stream; + struct type *type; +{ + char *name; + int i, n_baseclasses = TYPE_N_BASECLASSES (type); + struct type *basetype = 0; + + while (type && n_baseclasses == 1) + { + basetype = TYPE_BASECLASS (type, 1); + if (TYPE_NAME (basetype) && (name = TYPE_NAME (basetype))) + { + while (*name != ' ') name++; + fprintf_filtered (stream, ": %s%s ", + TYPE_VIA_PUBLIC (basetype) ? "public" : "private", + TYPE_VIA_VIRTUAL (basetype) ? " virtual" : ""); + fputs_filtered (name + 1, stream); + fputs_filtered (" ", stream); + } + n_baseclasses = TYPE_N_BASECLASSES (basetype); + type = basetype; + } + + if (type) + { + if (n_baseclasses != 0) + fprintf_filtered (stream, ": "); + for (i = 1; i <= n_baseclasses; i++) + { + basetype = TYPE_BASECLASS (type, i); + if (TYPE_NAME (basetype) && (name = TYPE_NAME (basetype))) + { + while (*name != ' ') name++; + fprintf_filtered (stream, "%s%s ", + TYPE_VIA_PUBLIC (basetype) ? "public" : "private", + TYPE_VIA_VIRTUAL (basetype) ? " virtual" : ""); + fputs_filtered (name + 1, stream); + } + if (i < n_baseclasses) + fprintf_filtered (stream, ", "); + } + fprintf_filtered (stream, " "); + } +} + +/* Print any asterisks or open-parentheses needed before the + variable name (to describe its type). + + On outermost call, pass 0 for PASSED_A_PTR. + On outermost call, SHOW > 0 means should ignore + any typename for TYPE and show its details. + SHOW is always zero on recursive calls. */ + +static void +type_print_varspec_prefix (type, stream, show, passed_a_ptr) + struct type *type; + FILE *stream; + int show; + int passed_a_ptr; +{ + if (type == 0) + return; + + if (TYPE_NAME (type) && show <= 0) + return; + + QUIT; + + switch (TYPE_CODE (type)) + { + case TYPE_CODE_PTR: + type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, 1); + fprintf_filtered (stream, "*"); + break; + + case TYPE_CODE_MEMBER: + if (passed_a_ptr) + fprintf_filtered (stream, "("); + type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, + 0); + fprintf_filtered (stream, " "); + type_print_base (TYPE_DOMAIN_TYPE (type), stream, 0, + passed_a_ptr); + fprintf_filtered (stream, "::"); + break; + + case TYPE_CODE_METHOD: + if (passed_a_ptr) + fprintf (stream, "("); + type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, + 0); + fprintf_filtered (stream, " "); + type_print_base (TYPE_DOMAIN_TYPE (type), stream, 0, + passed_a_ptr); + fprintf_filtered (stream, "::"); + break; + + case TYPE_CODE_REF: + type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, 1); + fprintf_filtered (stream, "&"); + break; + + case TYPE_CODE_FUNC: + type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, + 0); + if (passed_a_ptr) + fprintf_filtered (stream, "("); + break; + + case TYPE_CODE_ARRAY: + type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, + 0); + if (passed_a_ptr) + fprintf_filtered (stream, "("); + } +} + +/* Print any array sizes, function arguments or close parentheses + needed after the variable name (to describe its type). + Args work like type_print_varspec_prefix. */ + +static void +type_print_varspec_suffix (type, stream, show, passed_a_ptr) + struct type *type; + FILE *stream; + int show; + int passed_a_ptr; +{ + if (type == 0) + return; + + if (TYPE_NAME (type) && show <= 0) + return; + + QUIT; + + switch (TYPE_CODE (type)) + { + case TYPE_CODE_ARRAY: + if (passed_a_ptr) + fprintf_filtered (stream, ")"); + + fprintf_filtered (stream, "["); + if (TYPE_LENGTH (type) >= 0 + && TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > 0) + fprintf_filtered (stream, "%d", + (TYPE_LENGTH (type) + / TYPE_LENGTH (TYPE_TARGET_TYPE (type)))); + fprintf_filtered (stream, "]"); + + type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, + 0); + break; + + case TYPE_CODE_MEMBER: + if (passed_a_ptr) + fprintf_filtered (stream, ")"); + type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, 0); + break; + + case TYPE_CODE_METHOD: + if (passed_a_ptr) + fprintf_filtered (stream, ")"); + type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, 0); + if (passed_a_ptr) + { + int i; + struct type **args = TYPE_ARG_TYPES (type); + + fprintf_filtered (stream, "("); + if (args[1] == 0) + fprintf_filtered (stream, "..."); + else for (i = 1; args[i] != 0 && args[i]->code != TYPE_CODE_VOID; i++) + { + type_print_1 (args[i], "", stream, -1, 0); + if (args[i+1] == 0) + fprintf_filtered (stream, "..."); + else if (args[i+1]->code != TYPE_CODE_VOID) + fprintf_filtered (stream, ","); + } + fprintf_filtered (stream, ")"); + } + break; + + case TYPE_CODE_PTR: + case TYPE_CODE_REF: + type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, 1); + break; + + case TYPE_CODE_FUNC: + type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, + passed_a_ptr); + if (passed_a_ptr) + fprintf_filtered (stream, ")"); + fprintf_filtered (stream, "()"); + break; + } +} + +/* Print the name of the type (or the ultimate pointer target, + function value or array element), or the description of a + structure or union. + + SHOW nonzero means don't print this type as just its name; + show its real definition even if it has a name. + SHOW zero means print just typename or struct tag if there is one + SHOW negative means abbreviate structure elements. + SHOW is decremented for printing of structure elements. + + LEVEL is the depth to indent by. + We increase it for some recursive calls. */ + +static void +type_print_base (type, stream, show, level) + struct type *type; + FILE *stream; + int show; + int level; +{ + char *name; + register int i; + register int len; + register int lastval; + + QUIT; + + if (type == 0) + { + fprintf_filtered (stream, "type unknown"); + return; + } + + if (TYPE_NAME (type) && show <= 0) + { + fputs_filtered (TYPE_NAME (type), stream); + return; + } + + switch (TYPE_CODE (type)) + { + case TYPE_CODE_ARRAY: + case TYPE_CODE_PTR: + case TYPE_CODE_MEMBER: + case TYPE_CODE_REF: + case TYPE_CODE_FUNC: + case TYPE_CODE_METHOD: + type_print_base (TYPE_TARGET_TYPE (type), stream, show, level); + break; + + case TYPE_CODE_STRUCT: + fprintf_filtered (stream, "struct "); + goto struct_union; + + case TYPE_CODE_UNION: + fprintf_filtered (stream, "union "); + struct_union: + if (TYPE_NAME (type) && (name = TYPE_NAME (type))) + { + while (*name != ' ') name++; + fputs_filtered (name + 1, stream); + fputs_filtered (" ", stream); + } + if (show < 0) + fprintf_filtered (stream, "{...}"); + else + { + int i; + + type_print_derivation_info (stream, type); + + fprintf_filtered (stream, "{"); + len = TYPE_NFIELDS (type); + if (len) + fprintf_filtered (stream, "\n"); + else + { + if (TYPE_FLAGS (type) & TYPE_FLAG_STUB) + fprintf_filtered (stream, "\n"); + else + fprintf_filtered (stream, "\n"); + } + + /* If there is a base class for this type, + do not print the field that it occupies. */ + for (i = TYPE_N_BASECLASSES (type); i < len; i++) + { + QUIT; + /* Don't print out virtual function table. */ + if (! strncmp (TYPE_FIELD_NAME (type, i), + "_vptr$", 6)) + continue; + + print_spaces_filtered (level + 4, stream); + if (TYPE_FIELD_STATIC (type, i)) + { + fprintf_filtered (stream, "static "); + } + type_print_1 (TYPE_FIELD_TYPE (type, i), + TYPE_FIELD_NAME (type, i), + stream, show - 1, level + 4); + if (!TYPE_FIELD_STATIC (type, i) + && TYPE_FIELD_PACKED (type, i)) + { + /* It is a bitfield. This code does not attempt + to look at the bitpos and reconstruct filler, + unnamed fields. This would lead to misleading + results if the compiler does not put out fields + for such things (I don't know what it does). */ + fprintf_filtered (stream, " : %d", + TYPE_FIELD_BITSIZE (type, i)); + } + fprintf_filtered (stream, ";\n"); + } + + /* C++: print out the methods */ + len = TYPE_NFN_FIELDS (type); + if (len) fprintf_filtered (stream, "\n"); + for (i = 0; i < len; i++) + { + struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i); + int j, len2 = TYPE_FN_FIELDLIST_LENGTH (type, i); + + for (j = 0; j < len2; j++) + { + QUIT; + print_spaces_filtered (level + 4, stream); + if (TYPE_FN_FIELD_VIRTUAL_P (f, j)) + fprintf_filtered (stream, "virtual "); + else if (TYPE_FN_FIELD_STATIC_P (f, j)) + fprintf_filtered (stream, "static "); + type_print (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)), "", stream, 0); + if (TYPE_FN_FIELD_PHYSNAME (f, j)[0] == '_' + && TYPE_FN_FIELD_PHYSNAME (f, j)[1] == '$') + type_print_method_args + (TYPE_FN_FIELD_ARGS (f, j) + 1, "~", + TYPE_FN_FIELDLIST_NAME (type, i), 0, stream); + else + type_print_method_args + (TYPE_FN_FIELD_ARGS (f, j), "", + TYPE_FN_FIELDLIST_NAME (type, i), + TYPE_FN_FIELD_STATIC_P (f, j), stream); + + fprintf_filtered (stream, ";\n"); + } + if (len2) fprintf_filtered (stream, "\n"); + } + + print_spaces_filtered (level, stream); + fprintf_filtered (stream, "}"); + } + break; + + case TYPE_CODE_ENUM: + fprintf_filtered (stream, "enum "); + if (TYPE_NAME (type)) + { + name = TYPE_NAME (type); + while (*name != ' ') name++; + fputs_filtered (name + 1, stream); + fputs_filtered (" ", stream); + } + if (show < 0) + fprintf_filtered (stream, "{...}"); + else + { + fprintf_filtered (stream, "{"); + len = TYPE_NFIELDS (type); + lastval = 0; + for (i = 0; i < len; i++) + { + QUIT; + if (i) fprintf_filtered (stream, ", "); + fputs_filtered (TYPE_FIELD_NAME (type, i), stream); + if (lastval != TYPE_FIELD_BITPOS (type, i)) + { + fprintf_filtered (stream, " : %d", TYPE_FIELD_BITPOS (type, i)); + lastval = TYPE_FIELD_BITPOS (type, i); + } + lastval++; + } + fprintf_filtered (stream, "}"); + } + break; + + case TYPE_CODE_INT: + if (TYPE_UNSIGNED (type)) + name = unsigned_type_table[TYPE_LENGTH (type)]; + else + name = signed_type_table[TYPE_LENGTH (type)]; + fputs_filtered (name, stream); + break; + + case TYPE_CODE_FLT: + name = float_type_table[TYPE_LENGTH (type)]; + fputs_filtered (name, stream); + break; + + case TYPE_CODE_VOID: + fprintf_filtered (stream, "void"); + break; + + case 0: + fprintf_filtered (stream, "struct unknown"); + break; + + default: + error ("Invalid type code in symbol table."); + } +} + +static void +scalar_print_decimal(stream, type, val) + FILE *stream; + struct type *type; + LONGEST val; +{ + fprintf_filtered(stream, TYPE_UNSIGNED(type)? "%lu":"%ld", val); +} + +static void +scalar_print_hex(stream, type, val) + FILE *stream; + struct type *type; + LONGEST val; +{ + switch (TYPE_LENGTH(type)) { + case 1: + fprintf_filtered (stream, "0x%02lx", val); + break; + case 2: + fprintf_filtered (stream, "0x%04lx", val); + break; + case 4: + fprintf_filtered (stream, "0x%08lx", val); + break; + default: + fprintf_filtered (stream, "0x%lx", val); + break; + } +} + +static void +scalar_print_octal(stream, type, val) + FILE *stream; + struct type *type; + LONGEST val; +{ + switch (TYPE_LENGTH(type)) { + case 1: + fprintf_filtered (stream, "0%03lo", val); + break; + case 2: + fprintf_filtered (stream, "0%06lo", val); + break; + case 4: + fprintf_filtered (stream, "0%012lo", val); + break; + default: + fprintf_filtered (stream, "0%lo", val); + break; + } +} + +static void +scalar_print_hack(stream, type, val) + FILE *stream; + struct type *type; + LONGEST val; +{ + if (TYPE_UNSIGNED(type)) + scalar_print_hex(stream, type, val); + else + scalar_print_decimal(stream, type, val); +} + +static void +set_maximum_command (arg) + char *arg; +{ + if (!arg) error_no_arg ("value for maximum elements to print"); + print_max = parse_and_eval_address (arg); + if (print_max == 0) + print_max = UINT_MAX; +} + +static void +set_base_command(arg) + char *arg; +{ + int base; + + if (!arg) + base = 0; + else + base = parse_and_eval_address (arg); + switch (base) { + default: + default_scalar_print = scalar_print_hack; + break; + case 8: + default_scalar_print = scalar_print_octal; + break; + case 10: + default_scalar_print = scalar_print_decimal; + break; + case 16: + default_scalar_print = scalar_print_hex; + break; + } +} + +static void +set_prettyprint_command (arg, from_tty) + char *arg; + int from_tty; +{ + prettyprint = parse_binary_operation ("set prettyprint", arg); +} + +static void +set_unionprint_command (arg, from_tty) + char *arg; + int from_tty; +{ + unionprint = parse_binary_operation ("set unionprint", arg); +} + +format_info (arg, from_tty) + char *arg; + int from_tty; +{ + if (arg) + error ("\"info format\" does not take any arguments."); + printf ("Prettyprinting of structures is %s.\n", + prettyprint ? "on" : "off"); + printf ("Printing of unions interior to structures is %s.\n", + unionprint ? "on" : "off"); + if (print_max == UINT_MAX) + printf_filtered + ("There is no maximum number of array elements printed.\n"); + else + printf_filtered + ("The maximum number of array elements printed is %d.\n", print_max); +} + +extern struct cmd_list_element *setlist; + +void +_initialize_valprint () +{ + add_cmd ("base", class_support, set_base_command, + "Change default integer print radix to 8, 10 or 16\n\ +No args returns to the ad-hoc default of `16' for unsigned values\n\ +and `10' otherwise.", + &setlist); + add_cmd ("array-max", class_vars, set_maximum_command, + "Set NUMBER as limit on string chars or array elements to print.\n\ +\"set array-max 0\" causes there to be no limit.", + &setlist); + + add_cmd ("prettyprint", class_support, set_prettyprint_command, + "Turn prettyprinting of structures on and off.", + &setlist); + add_alias_cmd ("pp", "prettyprint", class_support, 1, &setlist); + + add_cmd ("unionprint", class_support, set_unionprint_command, + "Turn printing of unions interior to structures on and off.", + &setlist); + + add_info ("format", format_info, + "Show current settings of data formatting options."); + + /* Give people the defaults which they are used to. */ + prettyprint = 0; + unionprint = 1; + + print_max = 200; + + unsigned_type_table + = (char **) xmalloc ((1 + sizeof (unsigned LONGEST)) * sizeof (char *)); + bzero (unsigned_type_table, (1 + sizeof (unsigned LONGEST))); + unsigned_type_table[sizeof (unsigned char)] = "unsigned char"; + unsigned_type_table[sizeof (unsigned short)] = "unsigned short"; + unsigned_type_table[sizeof (unsigned long)] = "unsigned long"; + unsigned_type_table[sizeof (unsigned int)] = "unsigned int"; +#ifdef LONG_LONG + unsigned_type_table[sizeof (unsigned long long)] = "unsigned long long"; +#endif + + signed_type_table + = (char **) xmalloc ((1 + sizeof (LONGEST)) * sizeof (char *)); + bzero (signed_type_table, (1 + sizeof (LONGEST))); + signed_type_table[sizeof (char)] = "char"; + signed_type_table[sizeof (short)] = "short"; + signed_type_table[sizeof (long)] = "long"; + signed_type_table[sizeof (int)] = "int"; +#ifdef LONG_LONG + signed_type_table[sizeof (long long)] = "long long"; +#endif + + float_type_table + = (char **) xmalloc ((1 + sizeof (double)) * sizeof (char *)); + bzero (float_type_table, (1 + sizeof (double))); + float_type_table[sizeof (float)] = "float"; + float_type_table[sizeof (double)] = "double"; +} + diff --git a/gnu/usr.bin/gdb/value.h b/gnu/usr.bin/gdb/value.h new file mode 100644 index 0000000000..07dd8e840e --- /dev/null +++ b/gnu/usr.bin/gdb/value.h @@ -0,0 +1,212 @@ +/* Definitions for values of C expressions, for GDB. + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * The structure which defines the type of a value. It should never + * be possible for a program lval value to survive over a call to the inferior + * (ie to be put into the history list or an internal variable). + */ +enum lval_type { + /* Not an lval. */ + not_lval, + /* In memory. Could be a saved register. */ + lval_memory, + /* In a register. */ + lval_register, + /* In a gdb internal variable. */ + lval_internalvar, + /* Part of a gdb internal variable (structure field). */ + lval_internalvar_component, + /* In a register series in a frame not the current one, which may have been + partially saved or saved in different places (otherwise would be + lval_register or lval_memory). */ + lval_reg_frame_relative, +}; + +struct value + { + /* Type of value; either not an lval, or one of the various + different possible kinds of lval. */ + enum lval_type lval; + /* Location of value (if lval). */ + union + { + /* Address in inferior or byte of registers structure. */ + CORE_ADDR address; + /* Pointer to interrnal variable. */ + struct internalvar *internalvar; + /* Number of register. Only used with + lval_reg_frame_relative. */ + int regnum; + } location; + /* Describes offset of a value within lval a structure in bytes. */ + int offset; + /* Only used for bitfields; number of bits contained in them. */ + int bitsize; + /* Only used for bitfields; position of start of field. */ + int bitpos; + /* Frame value is relative to. In practice, this address is only + used if the value is stored in several registers in other than + the current frame, and these registers have not all been saved + at the same place in memory. This will be described in the + lval enum above as "lval_reg_frame_relative". */ + CORE_ADDR frame_addr; + /* Type of the value. */ + struct type *type; + /* Values are stored in a chain, so that they can be deleted + easily over calls to the inferior. Values assigned to internal + variables or put into the value history are taken off this + list. */ + struct value *next; + /* If an lval is forced to repeat, a new value is created with + these fields set. The new value is not an lval. */ + short repeated; + short repetitions; + /* Register number if the value is from a register. Is not kept + if you take a field of a structure that is stored in a + register. Shouldn't it be? */ + short regno; + /* Actual contents of the value. For use of this value; setting + it uses the stuff above. */ + long contents[1]; + }; + +typedef struct value *value; + +#define VALUE_TYPE(val) (val)->type +#define VALUE_CONTENTS(val) ((char *) (val)->contents) +#define VALUE_LVAL(val) (val)->lval +#define VALUE_ADDRESS(val) (val)->location.address +#define VALUE_INTERNALVAR(val) (val)->location.internalvar +#define VALUE_FRAME_REGNUM(val) ((val)->location.regnum) +#define VALUE_FRAME(val) ((val)->frame_addr) +#define VALUE_OFFSET(val) (val)->offset +#define VALUE_BITSIZE(val) (val)->bitsize +#define VALUE_BITPOS(val) (val)->bitpos +#define VALUE_NEXT(val) (val)->next +#define VALUE_REPEATED(val) (val)->repeated +#define VALUE_REPETITIONS(val) (val)->repetitions +#define VALUE_REGNO(val) (val)->regno + +/* If ARG is an array, convert it to a pointer. + If ARG is an enum, convert it to an integer. + + References are dereferenced. */ + +#define COERCE_ARRAY(arg) \ +{ if (TYPE_CODE ( VALUE_TYPE (arg)) == TYPE_CODE_REF) \ + arg = value_ind (arg); \ + if (VALUE_REPEATED (arg) \ + || TYPE_CODE (VALUE_TYPE (arg)) == TYPE_CODE_ARRAY) \ + arg = value_coerce_array (arg); \ + if (TYPE_CODE (VALUE_TYPE (arg)) == TYPE_CODE_ENUM) \ + arg = value_cast (builtin_type_unsigned_int, arg); \ +} + +/* If ARG is an enum, convert it to an integer. */ + +#define COERCE_ENUM(arg) \ +{ if (TYPE_CODE ( VALUE_TYPE (arg)) == TYPE_CODE_REF) \ + arg = value_ind (arg); \ + if (TYPE_CODE (VALUE_TYPE (arg)) == TYPE_CODE_ENUM) \ + arg = value_cast (builtin_type_unsigned_int, arg); \ +} + +/* Internal variables (variables for convenience of use of debugger) + are recorded as a chain of these structures. */ + +struct internalvar +{ + struct internalvar *next; + char *name; + value value; +}; + +LONGEST value_as_long (); +double value_as_double (); +LONGEST unpack_long (); +double unpack_double (); +long unpack_field_as_long (); +value value_from_long (); +value value_from_double (); +value value_at (); +value value_from_register (); +value value_of_variable (); +value value_of_register (); +value read_var_value (); +value locate_var_value (); +value allocate_value (); +value allocate_repeat_value (); +value value_string (); + +value value_binop (); +value value_add (); +value value_sub (); +value value_coerce_array (); +value value_ind (); +value value_addr (); +value value_assign (); +value value_neg (); +value value_lognot (); +value value_struct_elt (), value_struct_elt_for_address (); +value value_field (); +value value_cast (); +value value_zero (); +value value_repeat (); +value value_subscript (); + +value call_function (); +value value_being_returned (); +int using_struct_return (); + +value evaluate_expression (); +value evaluate_type (); +value parse_and_eval (); +value parse_to_comma_and_eval (); + +value access_value_history (); +value value_of_internalvar (); +struct internalvar *lookup_internalvar (); + +int value_equal (); +int value_less (); +int value_zerop (); + +/* C++ */ +value value_of_this (); +value value_static_field (); +value value_x_binop (); +value value_x_unop (); +int binop_user_defined_p (); +int unop_user_defined_p (); + +void read_register_bytes (); +void modify_field (); +void type_print (); +void type_print_1 (); + +/* Possibilities for prettyprint parameters to routines which print + things. */ +enum val_prettyprint { + Val_no_prettyprint = 0, + Val_prettyprint, + /* Use the default setting which the user has specified. */ + Val_pretty_default + }; + diff --git a/gnu/usr.bin/gdb/values.c b/gnu/usr.bin/gdb/values.c new file mode 100644 index 0000000000..93a291112a --- /dev/null +++ b/gnu/usr.bin/gdb/values.c @@ -0,0 +1,1059 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + */ + +#ifndef lint +static char sccsid[] = "@(#)values.c 6.3 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Low level packing and unpacking of values for GDB. + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "value.h" + +/* The value-history records all the values printed + by print commands during this session. Each chunk + records 60 consecutive values. The first chunk on + the chain records the most recent values. + The total number of values is in value_history_count. */ + +#define VALUE_HISTORY_CHUNK 60 + +struct value_history_chunk +{ + struct value_history_chunk *next; + value values[VALUE_HISTORY_CHUNK]; +}; + +/* Chain of chunks now in use. */ + +static struct value_history_chunk *value_history_chain; + +static int value_history_count; /* Abs number of last entry stored */ + + +/* List of all value objects currently allocated + (except for those released by calls to release_value) + This is so they can be freed after each command. */ + +static value all_values; + +/* Allocate a value that has the correct length for type TYPE. */ + +value +allocate_value (type) + struct type *type; +{ + register value val; + + /* If the type we want had no definition in the file it first + * appeared in, it will be marked a `stub'. The real definition + * probably appeared later so try to find it. */ + if (TYPE_FLAGS(type) & TYPE_FLAG_STUB) + { + register char *cp; + register struct symbol *sym; + extern char *index(); + + if (cp = index(TYPE_NAME(type), ' ')) + ++cp; + else + cp = TYPE_NAME(type); + + sym = lookup_symbol(cp, 0, STRUCT_NAMESPACE, 0); + + if (sym && TYPE_CODE(SYMBOL_TYPE(sym)) == TYPE_CODE(type)) + bcopy (SYMBOL_TYPE (sym), type, sizeof (*type)); + } + val = (value) xmalloc (sizeof (struct value) + TYPE_LENGTH (type)); + VALUE_NEXT (val) = all_values; + all_values = val; + VALUE_TYPE (val) = type; + VALUE_LVAL (val) = not_lval; + VALUE_ADDRESS (val) = 0; + VALUE_FRAME (val) = 0; + VALUE_OFFSET (val) = 0; + VALUE_BITPOS (val) = 0; + VALUE_BITSIZE (val) = 0; + VALUE_REPEATED (val) = 0; + VALUE_REPETITIONS (val) = 0; + VALUE_REGNO (val) = -1; + return val; +} + +/* Allocate a value that has the correct length + for COUNT repetitions type TYPE. */ + +value +allocate_repeat_value (type, count) + struct type *type; + int count; +{ + register value val; + + val = (value) xmalloc (sizeof (struct value) + TYPE_LENGTH (type) * count); + VALUE_NEXT (val) = all_values; + all_values = val; + VALUE_TYPE (val) = type; + VALUE_LVAL (val) = not_lval; + VALUE_ADDRESS (val) = 0; + VALUE_FRAME (val) = 0; + VALUE_OFFSET (val) = 0; + VALUE_BITPOS (val) = 0; + VALUE_BITSIZE (val) = 0; + VALUE_REPEATED (val) = 1; + VALUE_REPETITIONS (val) = count; + VALUE_REGNO (val) = -1; + return val; +} + +/* Free all the values that have been allocated (except for those released). + Called after each command, successful or not. */ + +void +free_all_values () +{ + register value val, next; + + for (val = all_values; val; val = next) + { + next = VALUE_NEXT (val); + free (val); + } + + all_values = 0; +} + +/* Remove VAL from the chain all_values + so it will not be freed automatically. */ + +void +release_value (val) + register value val; +{ + register value v; + + if (all_values == val) + { + all_values = val->next; + return; + } + + for (v = all_values; v; v = v->next) + { + if (v->next == val) + { + v->next = val->next; + break; + } + } +} + +/* Return a copy of the value ARG. + It contains the same contents, for same memory address, + but it's a different block of storage. */ + +static value +value_copy (arg) + value arg; +{ + register value val; + register struct type *type = VALUE_TYPE (arg); + if (VALUE_REPEATED (arg)) + val = allocate_repeat_value (type, VALUE_REPETITIONS (arg)); + else + val = allocate_value (type); + VALUE_LVAL (val) = VALUE_LVAL (arg); + VALUE_ADDRESS (val) = VALUE_ADDRESS (arg); + VALUE_OFFSET (val) = VALUE_OFFSET (arg); + VALUE_BITPOS (val) = VALUE_BITPOS (arg); + VALUE_BITSIZE (val) = VALUE_BITSIZE (arg); + VALUE_REGNO (val) = VALUE_REGNO (arg); + bcopy (VALUE_CONTENTS (arg), VALUE_CONTENTS (val), + TYPE_LENGTH (VALUE_TYPE (arg)) + * (VALUE_REPEATED (arg) ? VALUE_REPETITIONS (arg) : 1)); + return val; +} + +/* Access to the value history. */ + +/* Record a new value in the value history. + Returns the absolute history index of the entry. */ + +int +record_latest_value (val) + value val; +{ + int i; + double foo; + + /* Check error now if about to store an invalid float. We return -1 + to the caller, but allow them to continue, e.g. to print it as "Nan". */ + if (TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_FLT) { + foo = unpack_double (VALUE_TYPE (val), VALUE_CONTENTS (val), &i); + if (i) return -1; /* Indicate value not saved in history */ + } + + /* Here we treat value_history_count as origin-zero + and applying to the value being stored now. */ + + i = value_history_count % VALUE_HISTORY_CHUNK; + if (i == 0) + { + register struct value_history_chunk *new + = (struct value_history_chunk *) + xmalloc (sizeof (struct value_history_chunk)); + bzero (new->values, sizeof new->values); + new->next = value_history_chain; + value_history_chain = new; + } + + value_history_chain->values[i] = val; + release_value (val); + + /* Now we regard value_history_count as origin-one + and applying to the value just stored. */ + + return ++value_history_count; +} + +/* Return a copy of the value in the history with sequence number NUM. */ + +value +access_value_history (num) + int num; +{ + register struct value_history_chunk *chunk; + register int i; + register int absnum = num; + + if (absnum <= 0) + absnum += value_history_count; + + if (absnum <= 0) + { + if (num == 0) + error ("The history is empty."); + else if (num == 1) + error ("There is only one value in the history."); + else + error ("History does not go back to $$%d.", -num); + } + if (absnum > value_history_count) + error ("History has not yet reached $%d.", absnum); + + absnum--; + + /* Now absnum is always absolute and origin zero. */ + + chunk = value_history_chain; + for (i = (value_history_count - 1) / VALUE_HISTORY_CHUNK - absnum / VALUE_HISTORY_CHUNK; + i > 0; i--) + chunk = chunk->next; + + return value_copy (chunk->values[absnum % VALUE_HISTORY_CHUNK]); +} + +/* Clear the value history entirely. + Must be done when new symbol tables are loaded, + because the type pointers become invalid. */ + +void +clear_value_history () +{ + register struct value_history_chunk *next; + register int i; + register value val; + + while (value_history_chain) + { + for (i = 0; i < VALUE_HISTORY_CHUNK; i++) + if (val = value_history_chain->values[i]) + free (val); + next = value_history_chain->next; + free (value_history_chain); + value_history_chain = next; + } + value_history_count = 0; +} + +static void +value_history_info (num_exp, from_tty) + char *num_exp; + int from_tty; +{ + register int i; + register value val; + static int num = 1; + + if (num_exp) + { + if (num_exp[0] == '+' && num_exp[1] == '\0') + /* "info history +" should print from the stored position. */ + ; + else + /* "info history " should print around value number . */ + num = parse_and_eval_address (num_exp) - 5; + } + else + { + /* "info history" means print the last 10 values. */ + num = value_history_count - 9; + } + + if (num <= 0) + num = 1; + + for (i = num; i < num + 10 && i <= value_history_count; i++) + { + val = access_value_history (i); + printf_filtered ("$%d = ", i); + value_print (val, stdout, 0, Val_pretty_default); + printf_filtered ("\n"); + } + + /* The next "info history +" should start after what we just printed. */ + num += 10; + + /* Hitting just return after this command should do the same thing as + "info history +". If num_exp is null, this is unnecessary, since + "info history +" is not useful after "info history". */ + if (from_tty && num_exp) + { + num_exp[0] = '+'; + num_exp[1] = '\0'; + } +} + +/* Internal variables. These are variables within the debugger + that hold values assigned by debugger commands. + The user refers to them with a '$' prefix + that does not appear in the variable names stored internally. */ + +static struct internalvar *internalvars; + +/* Look up an internal variable with name NAME. NAME should not + normally include a dollar sign. + + If the specified internal variable does not exist, + one is created, with a void value. */ + +struct internalvar * +lookup_internalvar (name) + char *name; +{ + register struct internalvar *var; + + for (var = internalvars; var; var = var->next) + if (!strcmp (var->name, name)) + return var; + + var = (struct internalvar *) xmalloc (sizeof (struct internalvar)); + var->name = concat (name, "", ""); + var->value = allocate_value (builtin_type_void); + release_value (var->value); + var->next = internalvars; + internalvars = var; + return var; +} + +value +value_of_internalvar (var) + struct internalvar *var; +{ + register value val; + +#ifdef IS_TRAPPED_INTERNALVAR + if (IS_TRAPPED_INTERNALVAR (var->name)) + return VALUE_OF_TRAPPED_INTERNALVAR (var); +#endif + + val = value_copy (var->value); + VALUE_LVAL (val) = lval_internalvar; + VALUE_INTERNALVAR (val) = var; + return val; +} + +void +set_internalvar_component (var, offset, bitpos, bitsize, newval) + struct internalvar *var; + int offset, bitpos, bitsize; + value newval; +{ + register char *addr = VALUE_CONTENTS (var->value) + offset; + +#ifdef IS_TRAPPED_INTERNALVAR + if (IS_TRAPPED_INTERNALVAR (var->name)) + SET_TRAPPED_INTERNALVAR (var, newval, bitpos, bitsize, offset); +#endif + + if (bitsize) + modify_field (addr, (int) value_as_long (newval), + bitpos, bitsize); + else + bcopy (VALUE_CONTENTS (newval), addr, + TYPE_LENGTH (VALUE_TYPE (newval))); +} + +void +set_internalvar (var, val) + struct internalvar *var; + value val; +{ +#ifdef IS_TRAPPED_INTERNALVAR + if (IS_TRAPPED_INTERNALVAR (var->name)) + SET_TRAPPED_INTERNALVAR (var, val, 0, 0, 0); +#endif + + free (var->value); + var->value = value_copy (val); + release_value (var->value); +} + +char * +internalvar_name (var) + struct internalvar *var; +{ + return var->name; +} + +/* Free all internalvars. Done when new symtabs are loaded, + because that makes the values invalid. */ + +void +clear_internalvars () +{ + register struct internalvar *var; + + while (internalvars) + { + var = internalvars; + internalvars = var->next; + free (var->name); + free (var->value); + free (var); + } +} + +static void +convenience_info () +{ + register struct internalvar *var; + int varseen = 0; + + for (var = internalvars; var; var = var->next) + { +#ifdef IS_TRAPPED_INTERNALVAR + if (IS_TRAPPED_INTERNALVAR (var->name)) + continue; +#endif + if (!varseen) + { + printf ("Debugger convenience variables:\n\n"); + varseen = 1; + } + printf ("$%s: ", var->name); + value_print (var->value, stdout, 0, Val_pretty_default); + printf ("\n"); + } + if (!varseen) + printf ("No debugger convenience variables now defined.\n\ +Convenience variables have names starting with \"$\";\n\ +use \"set\" as in \"set $foo = 5\" to define them.\n"); +} + +/* Extract a value as a C number (either long or double). + Knows how to convert fixed values to double, or + floating values to long. + Does not deallocate the value. */ + +LONGEST +value_as_long (val) + register value val; +{ + return unpack_long (VALUE_TYPE (val), VALUE_CONTENTS (val)); +} + +double +value_as_double (val) + register value val; +{ + double foo; + int inv; + + foo = unpack_double (VALUE_TYPE (val), VALUE_CONTENTS (val), &inv); + if (inv) + error ("Invalid floating value found in program."); + return foo; +} + +/* Unpack raw data (copied from debugee) at VALADDR + as a long, or as a double, assuming the raw data is described + by type TYPE. Knows how to convert different sizes of values + and can convert between fixed and floating point. + + C++: It is assumed that the front-end has taken care of + all matters concerning pointers to members. A pointer + to member which reaches here is considered to be equivalent + to an INT (or some size). After all, it is only an offset. */ + +LONGEST +unpack_long (type, valaddr) + struct type *type; + char *valaddr; +{ + register enum type_code code = TYPE_CODE (type); + register int len = TYPE_LENGTH (type); + register int nosign = TYPE_UNSIGNED (type); + + if (code == TYPE_CODE_ENUM) + code = TYPE_CODE_INT; + if (code == TYPE_CODE_FLT) + { + if (len == sizeof (float)) + return * (float *) valaddr; + + if (len == sizeof (double)) + return * (double *) valaddr; + } + else if (code == TYPE_CODE_INT && nosign) + { + if (len == sizeof (char)) + return * (unsigned char *) valaddr; + + if (len == sizeof (short)) + return * (unsigned short *) valaddr; + + if (len == sizeof (int)) + return * (unsigned int *) valaddr; + + if (len == sizeof (long)) + return * (unsigned long *) valaddr; +#ifdef LONG_LONG + if (len == sizeof (long long)) + return * (unsigned long long *) valaddr; +#endif + } + else if (code == TYPE_CODE_INT) + { + if (len == sizeof (char)) + return * (char *) valaddr; + + if (len == sizeof (short)) + return * (short *) valaddr; + + if (len == sizeof (int)) + return * (int *) valaddr; + + if (len == sizeof (long)) + return * (long *) valaddr; + +#ifdef LONG_LONG + if (len == sizeof (long long)) + return * (long long *) valaddr; +#endif + } + else if (code == TYPE_CODE_PTR + || code == TYPE_CODE_REF) + { + if (len == sizeof (char *)) + return (CORE_ADDR) * (char **) valaddr; + } + else if (code == TYPE_CODE_MEMBER) + error ("not implemented: member types in unpack_long"); + + error ("Value not integer or pointer."); +} + +/* Return a double value from the specified type and address. + INVP points to an int which is set to 0 for valid value, + 1 for invalid value (bad float format). In either case, + the returned double is OK to use. */ + +double +unpack_double (type, valaddr, invp) + struct type *type; + char *valaddr; + int *invp; +{ + register enum type_code code = TYPE_CODE (type); + register int len = TYPE_LENGTH (type); + register int nosign = TYPE_UNSIGNED (type); + + *invp = 0; /* Assume valid. */ + if (code == TYPE_CODE_FLT) + { + if (INVALID_FLOAT (valaddr, len)) + { + *invp = 1; + return 1.234567891011121314; + } + + if (len == sizeof (float)) + return * (float *) valaddr; + + if (len == sizeof (double)) + { + /* Some machines require doubleword alignment for doubles. + This code works on them, and on other machines. */ + double temp; + bcopy ((char *) valaddr, (char *) &temp, sizeof (double)); + return temp; + } + } + else if (code == TYPE_CODE_INT && nosign) + { + if (len == sizeof (char)) + return * (unsigned char *) valaddr; + + if (len == sizeof (short)) + return * (unsigned short *) valaddr; + + if (len == sizeof (int)) + return * (unsigned int *) valaddr; + + if (len == sizeof (long)) + return * (unsigned long *) valaddr; + +#ifdef LONG_LONG + if (len == sizeof (long long)) + return * (unsigned long long *) valaddr; +#endif + } + else if (code == TYPE_CODE_INT) + { + if (len == sizeof (char)) + return * (char *) valaddr; + + if (len == sizeof (short)) + return * (short *) valaddr; + + if (len == sizeof (int)) + return * (int *) valaddr; + + if (len == sizeof (long)) + return * (long *) valaddr; + +#ifdef LONG_LONG + if (len == sizeof (long long)) + return * (long long *) valaddr; +#endif + } + + error ("Value not floating number."); + /* NOTREACHED */ + return (double) 0; /* To silence compiler warning. */ +} + +/* Given a value ARG1 of a struct or union type, + extract and return the value of one of its fields. + FIELDNO says which field. + + For C++, must also be able to return values from static fields */ + +value +value_field (arg1, fieldno) + register value arg1; + register int fieldno; +{ + register value v; + register struct type *type = TYPE_FIELD_TYPE (VALUE_TYPE (arg1), fieldno); + register int offset; + + /* Handle packed fields */ + + offset = TYPE_FIELD_BITPOS (VALUE_TYPE (arg1), fieldno) / 8; + if (TYPE_FIELD_BITSIZE (VALUE_TYPE (arg1), fieldno)) + { + v = value_from_long (type, + unpack_field_as_long (VALUE_TYPE (arg1), + VALUE_CONTENTS (arg1), + fieldno)); + VALUE_BITPOS (v) = TYPE_FIELD_BITPOS (VALUE_TYPE (arg1), fieldno) % 8; + VALUE_BITSIZE (v) = TYPE_FIELD_BITSIZE (VALUE_TYPE (arg1), fieldno); + } + else + { + v = allocate_value (type); + bcopy (VALUE_CONTENTS (arg1) + offset, + VALUE_CONTENTS (v), + TYPE_LENGTH (type)); + } + VALUE_LVAL (v) = VALUE_LVAL (arg1); + if (VALUE_LVAL (arg1) == lval_internalvar) + VALUE_LVAL (v) = lval_internalvar_component; + VALUE_ADDRESS (v) = VALUE_ADDRESS (arg1); + VALUE_OFFSET (v) = offset + VALUE_OFFSET (arg1); + return v; +} + +value +value_fn_field (arg1, fieldno, subfieldno) + register value arg1; + register int fieldno; +{ + register value v; + struct fn_field *f = TYPE_FN_FIELDLIST1 (VALUE_TYPE (arg1), fieldno); + register struct type *type = TYPE_FN_FIELD_TYPE (f, subfieldno); + struct symbol *sym; + + sym = lookup_symbol (TYPE_FN_FIELD_PHYSNAME (f, subfieldno), + 0, VAR_NAMESPACE, 0); + if (! sym) error ("Internal error: could not find physical method named %s", + TYPE_FN_FIELD_PHYSNAME (f, subfieldno)); + + v = allocate_value (type); + VALUE_ADDRESS (v) = BLOCK_START (SYMBOL_BLOCK_VALUE (sym)); + VALUE_TYPE (v) = type; + return v; +} + +/* Return a virtual function as a value. + ARG1 is the object which provides the virtual function + table pointer. + F is the list of member functions which contains the desired virtual + function. + J is an index into F which provides the desired virtual function. + TYPE is the basetype which first provides the virtual function table. */ +value +value_virtual_fn_field (arg1, f, j, type) + value arg1; + struct fn_field *f; + int j; + struct type *type; +{ + /* First, get the virtual function table pointer. That comes + with a strange type, so cast it to type `pointer to long' (which + should serve just fine as a function type). Then, index into + the table, and convert final value to appropriate function type. */ + value vfn, vtbl; + value vi = value_from_long (builtin_type_int, + (LONGEST) TYPE_FN_FIELD_VOFFSET (f, j)); + VALUE_TYPE (arg1) = TYPE_VPTR_BASETYPE (type); + + /* This type may have been defined before its virtual function table + was. If so, fill in the virtual function table entry for the + type now. */ + if (TYPE_VPTR_FIELDNO (type) < 0) + TYPE_VPTR_FIELDNO (type) + = fill_in_vptr_fieldno (type); + + /* The virtual function table is now an array of structures + which have the form { int16 offset, delta; void *pfn; }. */ + vtbl = value_ind (value_field (arg1, TYPE_VPTR_FIELDNO (type))); + + /* Index into the virtual function table. This is hard-coded because + looking up a field is not cheap, and it may be important to save + time, e.g. if the user has set a conditional breakpoint calling + a virtual function. */ + vfn = value_field (value_subscript (vtbl, vi), 2); + + /* Reinstantiate the function pointer with the correct type. */ + VALUE_TYPE (vfn) = lookup_pointer_type (TYPE_FN_FIELD_TYPE (f, j)); + return vfn; +} + +/* The value of a static class member does not depend + on its instance, only on its type. If FIELDNO >= 0, + then fieldno is a valid field number and is used directly. + Otherwise, FIELDNAME is the name of the field we are + searching for. If it is not a static field name, an + error is signaled. TYPE is the type in which we look for the + static field member. */ +value +value_static_field (type, fieldname, fieldno) + register struct type *type; + char *fieldname; + register int fieldno; +{ + register value v; + struct symbol *sym; + + if (fieldno < 0) + { + register struct type *t = type; + /* Look for static field. */ + while (t) + { + int i; + for (i = TYPE_NFIELDS (t) - 1; i >= 0; i--) + if (! strcmp (TYPE_FIELD_NAME (t, i), fieldname)) + { + if (TYPE_FIELD_STATIC (t, i)) + { + fieldno = i; + goto found; + } + else + error ("field `%s' is not static"); + } + t = TYPE_BASECLASSES (t) ? TYPE_BASECLASS (t, 1) : 0; + } + + t = type; + + if (destructor_name_p (fieldname, t)) + error ("use `info method' command to print out value of destructor"); + + while (t) + { + int i, j; + + for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; i--) + { + if (! strcmp (TYPE_FN_FIELDLIST_NAME (t, i), fieldname)) + { + error ("use `info method' command to print value of method \"%s\"", fieldname); + } + } + t = TYPE_BASECLASSES (t) ? TYPE_BASECLASS (t, 1) : 0; + } + error("there is no field named %s", fieldname); + } + + found: + + sym = lookup_symbol (TYPE_FIELD_STATIC_PHYSNAME (type, fieldno), + 0, VAR_NAMESPACE, 0); + if (! sym) error ("Internal error: could not find physical static variable named %s", TYPE_FIELD_BITSIZE (type, fieldno)); + + type = TYPE_FIELD_TYPE (type, fieldno); + v = value_at (type, (CORE_ADDR)SYMBOL_BLOCK_VALUE (sym)); + return v; +} + +long +unpack_field_as_long (type, valaddr, fieldno) + struct type *type; + char *valaddr; + int fieldno; +{ + long val; + int bitpos = TYPE_FIELD_BITPOS (type, fieldno); + int bitsize = TYPE_FIELD_BITSIZE (type, fieldno); + + bcopy (valaddr + bitpos / 8, &val, sizeof val); + + /* Extracting bits depends on endianness of the machine. */ +#ifdef BITS_BIG_ENDIAN + val = val >> (sizeof val * 8 - bitpos % 8 - bitsize); +#else + val = val >> (bitpos % 8); +#endif + + val &= (1 << bitsize) - 1; + return val; +} + +void +modify_field (addr, fieldval, bitpos, bitsize) + char *addr; + int fieldval; + int bitpos, bitsize; +{ + long oword; + + /* Reject values too big to fit in the field in question. + Otherwise adjoining fields may be corrupted. */ + if (fieldval & ~((1<> 8) +#define WSTOPSIG(w) ((w) >> 8) +#define WCOREDUMP(w) (((w)&0200) != 0) +#define WTERMSIG(w) ((w) & 0177) +#define WSETEXIT(w, status) ((w) = (status)) +#define WSETSTOP(w,sig) ((w) = (0177 | ((sig) << 8))) + +#else + +#include + +#define WAITTYPE union wait +#ifndef WEXITSTATUS +#define WEXITSTATUS(w) (w).w_retcode +#endif +#ifndef WSTOPSIG +#define WSTOPSIG(w) (w).w_stopsig +#endif +#ifndef WCOREDUMP +#define WCOREDUMP(w) (w).w_coredump +#endif +#ifndef WTERMSIG +#define WTERMSIG(w) (w).w_termsig +#endif +#ifndef WSETEXIT +#define WSETEXIT(w, status) ((w).w_status = (status)) +#endif +#ifndef WSETSTOP +#define WSETSTOP(w,sig) \ + ((w).w_stopsig = (sig), (w).w_coredump = 0, (w).w_termsig = 0177) +#endif + +#endif diff --git a/gnu/usr.bin/gdb/xgdb/Makefile b/gnu/usr.bin/gdb/xgdb/Makefile new file mode 100644 index 0000000000..72c53598dd --- /dev/null +++ b/gnu/usr.bin/gdb/xgdb/Makefile @@ -0,0 +1,33 @@ +# %W% (Berkeley) %G% + +.include "../config/Makefile.$(MACHINE)" + +PROG= xgdb +SRCS= xgdb.c xgdbinit.c +GDBOBJS+= $(CONFIGSRCS:R:S/$/.o/g) \ + blockframe.o breakpoint.o command.o copying.o core.o \ + cplus-dem.o dbxread.o environ.o eval.o expprint.o \ + expread.o findvar.o infcmd.o inflow.o infrun.o \ + main.o obstack.o printcmd.o regex.o remote.o \ + remote-sl.o source.o stack.o symmisc.o symtab.o \ + utils.o valarith.o valops.o valprint.o values.o \ + version.o \ + funmap.o history.o keymaps.o readline.o +CFLAGS+= -I.. -I$(.CURDIR)/.. -I$(.CURDIR)/../config \ + -DHAVE_VPRINTF -DVI_MODE -DKERNELDEBUG +LDFLAGS+= -L/usr/lib/X11 +LDADD+= $(GDBOBJS:S/^/..\//g) -lXaw -lXmu -lXt -lXext -lX11 -ltermcap +NOMAN= noman + +.include "../../Makefile.inc" +.include + +# +# Generate the constructor +# +xgdbinit.c: ../init.c xgdb.c + -(sed -e '/^}$$/d' ../init.c; \ + egrep -h '^_initialize_[^ ]* *\(\)' $(.CURDIR)/xgdb.c; \ + echo ';}') > xgdbinit.c + +CLEANFILES+= xgdbinit.c diff --git a/gnu/usr.bin/gdb/xgdb/xgdb.c b/gnu/usr.bin/gdb/xgdb/xgdb.c new file mode 100644 index 0000000000..a2bd4f6e92 --- /dev/null +++ b/gnu/usr.bin/gdb/xgdb/xgdb.c @@ -0,0 +1,700 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory. + * + * static char rcsid[] = "$Header: /home/cvs/386BSD/src/usr.bin/gdb/xgdb/xgdb.c,v 1.1.1.1 1993/06/12 14:52:36 rgrimes Exp $"; + */ + +#ifndef lint +static char sccsid[] = "@(#)xgdb.c 6.3 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* + * Interface from GDB to X windows. Copyright (C) 1987 Free Software + * Foundation, Inc. + * + * GDB is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY. No author or distributor accepts responsibility to anyone for + * the consequences of using it or for whether it serves any particular + * purpose or works at all, unless he says so in writing. Refer to the GDB + * General Public License for full details. + * + * Everyone is granted permission to copy, modify and redistribute GDB, but only + * under the conditions described in the GDB General Public License. A copy + * of this license is supposed to have been given to you along with GDB so + * you can know your rights and responsibilities. It should be in a file + * named COPYING. Among other things, the copyright notice and this notice + * must be preserved on all copies. + * + * In other words, go ahead and share GDB, but don't try to stop anyone else + * from sharing it farther. Help stamp out software hoarding! + */ + +/* + * Original version was contributed by Derek Beatty, 30 June 87. + * This version is essentially a re-write of the original by Van + * Jacobson (van@helios.ee.lbl.gov), Nov, 90. + */ + +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "frame.h" + +extern int stop_breakpoint; + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +extern int errno; +extern char *getenv(); +extern char *malloc(); +extern void bcopy(); +extern int select(); + +extern int get_filename_and_charpos(); +extern int source_line_charpos(); +extern int source_charpos_line(); +extern void execute_command(); +extern void error_no_arg(); +extern void add_com(); + +/* The X display where the window appears. */ + +static char *displayname; +static Display *display; + +static XtAppContext app_context; + +/* Windows manipulated by this package. */ + +static Widget main_widget; +static Widget containing_widget; +static Widget source_name_widget; +static Widget source_text_widget; +static Widget button_box_widget; + +/* Source text display. */ + +static struct frame_info *last_fi; +static CORE_ADDR last_pc; +static struct symtab *last_cur_symtab; +static int last_cur_line; + +static int source_window_line; +static char *source_window_file; +static struct symtab *source_window_symtab; + +static char version_label[64]; +extern char *version; + +/* Forward declarations */ + +static Widget create_text_widget(); + +static int +safe_strcmp(a, b) + register char *a, *b; +{ + register int i; + + if (a == b) + return (0); + if (!a && b) + return (1); + if (a && !b) + return (-1); + return (strcmp(a, b)); +} + + +/* Display an appropriate piece of source code in the source window. */ + +void +xgdb_display_source() +{ + char *filename = NULL; + struct symtab_and_line get_selected_frame_sal(); + struct symtab_and_line sal; + struct frame_info *fi; + + /* Do nothing if called before we are initialized */ + + if (!containing_widget) + return; + + /* + * Figure out what to display (the appropriate hooks to tell + * us don't exist so we guess): If there's a current frame + * and it or its pc changed from the last time we were here, + * display appropriate source line. Otherwise if the current + * source symtab or line is different, display that line. + * Otherwise nothing changed so leave the display alone. + */ + fi = get_frame_info(selected_frame); + if (fi && (fi != last_fi || fi->pc != last_pc)) { + last_fi = fi; + last_pc = fi->pc; + sal = find_pc_line(fi->pc, fi->next_frame); + if (sal.symtab == NULL) { /* XXX */ + sal.symtab = current_source_symtab; + sal.line = current_source_line; + } + current_source_symtab = sal.symtab; + current_source_line = sal.line; + } else if (current_source_symtab != last_cur_symtab || + current_source_line != last_cur_line) { + sal.symtab = last_cur_symtab = current_source_symtab; + sal.line = last_cur_line = current_source_line; + } else + return; + /* + * Do a path search and get the exact filename of this source file. + * Also scan it and find its source lines if not already done. + */ + if (sal.symtab && filename == NULL) { + if (get_filename_and_charpos(sal.symtab, sal.line, &filename)) + /* line numbers may have changed - force highlight */ + source_window_line = -1; + } + + /* + * If the source window is wrong, destroy it and make a new one. + */ + if (safe_strcmp(filename, source_window_file)) { + Arg args[1]; + Widget src = XawTextGetSource(source_text_widget); + + if (filename) { + XtSetArg(args[0], XtNstring, filename); + XtSetValues(src, args, XtNumber(args)); + args[0].name = XtNlabel; + XtSetValues(source_name_widget, args, XtNumber(args)); + } else { + XtSetArg(args[0], XtNstring, "/dev/null"); + XtSetValues(src, args, XtNumber(args)); + XtSetArg(args[0], XtNlabel, ""); + XtSetValues(source_name_widget, args, XtNumber(args)); + } + if (source_window_file) + free(source_window_file); + source_window_file = filename; + source_window_line = sal.line + 1; /* force highlight */ + } + if (sal.symtab && source_window_line != sal.line) { + /* + * Update display and cursor positions as necessary. + * Cursor should be placed on line sal.line. + */ + XawTextPosition l, r; + + source_window_symtab = sal.symtab; + source_window_line = sal.line; + l = source_line_charpos(source_window_symtab, sal.line); + r = source_line_charpos(source_window_symtab, sal.line + 1); + if (r < l) + r = l + 1; + XawTextSetSelection(source_text_widget, l, r); + XawTextScrollToLine(source_text_widget, l, 10, 3); + XawTextSetInsertionPoint(source_text_widget, l); + } +} + + +/* + * Handlers for buttons. + */ + +static int +current_lineno() +{ + XawTextPosition start, finish; + + XawTextGetSelectionPos(source_text_widget, &start, &finish); + if (start >= finish) + start = XawTextGetInsertionPoint(source_text_widget); + + return (source_charpos_line(source_window_symtab, start)); +} + +static char * +append_selection(cp) + char *cp; +{ + int len; + XawTextPosition l, r; + + XawTextGetSelectionPos(source_text_widget, &l, &r); + if ((len = r - l) > 0) { + Widget src = XawTextGetSource(source_text_widget); + + while (len > 0) { + XawTextBlock tb; + + XawTextSourceRead(src, l, &tb, len); + bcopy(tb.ptr, cp, tb.length); + cp += tb.length; + len -= tb.length; + } + if (cp[-1] == 0) + --cp; + } + return (cp); +} + +static char * +append_selection_word(cp) + register char *cp; +{ + register int len; + XawTextPosition l, r; + XawTextBlock tb; + register char c; + register Widget src = XawTextGetSource(source_text_widget); + + XawTextGetSelectionPos(source_text_widget, &l, &r); + if ((len = r - l) <= 0) { + l = XawTextGetInsertionPoint(source_text_widget); + len = 128; /* XXX */ + + /* might have clicked in middle of word -- back up to start */ + for ( ; l > 0; --l) { + XawTextSourceRead(src, l - 1, &tb, 1); + c = tb.ptr[0]; + if (! isalnum(c) && c != '_' && c != '$') + break; + } + } + while (len > 0) { + char *sp; + int i; + + XawTextSourceRead(src, l, &tb, len); + for (sp = tb.ptr, i = tb.length; --i >= 0; ) { + c = *sp++; + if (!isalnum(c) && c != '_' && c != '$') + return (cp); + *cp++ = c; + } + len -= tb.length; + } + return (cp); +} + +static char * +append_selection_expr(cp) + char *cp; +{ + int len; + XawTextPosition l, r; + Widget src = XawTextGetSource(source_text_widget); + XawTextBlock tb; + char *sp; + char c; + + XawTextGetSelectionPos(source_text_widget, &l, &r); + if (r > l) + return (append_selection(cp)); + + l = XawTextGetInsertionPoint(source_text_widget); + + /* might have clicked in middle of word -- back up to start */ + for ( ; l > 0; --l) { + XawTextSourceRead(src, l - 1, &tb, 1); + c = tb.ptr[0]; + if (! isalnum(c) && c != '_' && c != '$') + break; + } + + len = 128; /* XXX */ + while (len > 0) { + int i; + char pstack[64]; + int pcnt = 0; + + XawTextSourceRead(src, l, &tb, len); + for (sp = tb.ptr, i = tb.length; --i >= 0; ) { + switch (c = *sp++) { + case '\n': + case ';': + return (cp); + case '=': + if (cp[-1] != '=') + return (cp - 1); + if (len == 128) + return (cp); + break; + case ',': + if (pcnt <= 0) + return (cp); + break; + case '(': + pstack[pcnt] = ')'; + if (++pcnt >= sizeof(pstack)) + return (cp); + break; + case '[': + pstack[pcnt] = ']'; + if (++pcnt >= sizeof(pstack)) + return (cp); + break; + case ')': + case ']': + if (--pcnt < 0 || pstack[pcnt] != c) + return (cp); + break; + } + *cp++ = c; + } + len -= tb.length; + } + return (cp); +} + +static int input_avail; /* XXX kluge: do_command sets this when command + * data from button is avaialble to force top level + * to break out of its loop. */ +/* + * Handle a button by running the command COMMAND. + */ +static void +do_command(w, command, call_data) + Widget w; + register char *command; + caddr_t call_data; +{ + char cmd_line[256]; + char buf[256]; + register char *out = cmd_line; + char *cp; + register char c; + extern char *finish_command_input(); + + while (c = *command++) { + if (c == '%') { + switch (*command++) { + case 's': /* current selection */ + out = append_selection(out); + break; + case 'S': /* 1st selected "word" at curor */ + out = append_selection_word(out); + break; + case 'e': /* echo cmd before executing */ + break; + case 'E': /* 1st selected expression at curor */ + out = append_selection_expr(out); + break; + + case 'l': /* current line number */ + (void) sprintf(buf, "%d", current_lineno()); + for (cp = buf; c = *cp++; *out++ = c) + ; + break; + case 'L': /* line we're stopped at */ + (void) sprintf(buf, "%d", source_window_line); + for (cp = buf; c = *cp++; *out++ = c) + ; + break; + case 'f': /* current file name */ + for (cp = source_window_symtab->filename; + c = *cp++; *out++ = c) + ; + break; + case 'b': /* break # we're stopped at */ + if (stop_breakpoint <= 0) + /* if no breakpoint, don't do cmd */ + return; + + (void) sprintf(buf, "%d", stop_breakpoint); + for (cp = buf; c = *cp++; *out++ = c) + ; + break; + } + } else + *out++ = c; + } + *out = 0; + reinitialize_more_filter(); + /* have to exit via readline or tty modes stay messed up */ + for (cp = cmd_line; c = *cp++; ) + rl_stuff_char(c); + rl_stuff_char('\n'); + input_avail = 1; +} + +/* + * Define and display all the buttons. + */ +static void +addbutton(parent, name, function, closure) + Widget parent; + char *name; + void (*function) (); + caddr_t closure; +{ + static XtCallbackRec Callback[] = { + {NULL, (caddr_t) NULL}, + {NULL, (caddr_t) NULL}, + }; + static Arg commandArgs[] = { + {XtNlabel, (XtArgVal) NULL}, + {XtNcallback, (XtArgVal) Callback}, + }; + Widget w; + char wname[128]; + register char *cp; + + strcpy(wname, name); + while ((cp = index(wname, '*')) || (cp = index(wname, '.'))) + *cp -= 0x10; + + if (w = XtNameToWidget(parent, wname)) + XtDestroyWidget(w); + + Callback[0].callback = (XtCallbackProc) function; + Callback[0].closure = (caddr_t) closure; + commandArgs[0].value = (XtArgVal) name; + XtCreateManagedWidget(wname, commandWidgetClass, parent, + commandArgs, XtNumber(commandArgs)); +} + +/* + * Create the button windows and store them in `buttons'. + */ +static void +create_buttons(parent) + Widget parent; +{ + addbutton(parent, "quit", do_command, "quit"); +} + +static void +button_command(arg) + char *arg; +{ + char *label; + unsigned int len; + + if (! arg) + error_no_arg("button label and command"); + + for (len = strlen(arg); len > 0 && isspace(arg[len - 1]); --len) + ; + if (len == 0) + error_no_arg("button label and command"); + arg[len] = 0; + + /* make a copy of button label & command for toolkit to use */ + label = malloc(len + 1); + strcpy(label, arg); + + /* find the end of the label */ + if (*label == '"') { + if ((arg = index(++label, '"')) == 0) { + printf("button label missing closing quote\n"); + return; + } + *arg++ = 0; + } else if (arg = index(label, ' ')) + *arg++ = 0; + else + arg = label; + + while (*arg && isspace(*arg)) + ++arg; + + addbutton(button_box_widget, label, do_command, arg); +} + +static void +button_delete_command(arg) + char *arg; +{ + unsigned int len; + Widget w; + register char *cp; + + if (! arg) + error_no_arg("button name"); + + for (len = strlen(arg); len > 0 && isspace(arg[len - 1]); --len) + ; + if (len == 0) + error_no_arg("button name"); + arg[len] = 0; + + /* find the end of the label */ + if (*arg == '"') { + if ((cp = index(++arg, '"')) == 0) { + printf("button label missing closing quote\n"); + return; + } + *cp++ = 0; + } + while ((cp = index(arg, '*')) || (cp = index(arg, '.'))) + *cp -= 0x10; + + if (w = XtNameToWidget(button_box_widget, arg)) + XtDestroyWidget(w); +} + +/* + * Create a "label window" that just displays the string LABEL. + */ +static Widget +create_label(name, label) + char *name, *label; +{ + Arg args[1]; + Widget w; + + XtSetArg(args[0], XtNlabel, label); + w = XtCreateManagedWidget(name, labelWidgetClass, containing_widget, + args, XtNumber(args)); + return (w); +} + +/* + * Create a subwindow of PARENT that displays and scrolls the contents of + * file FILENAME. + */ +static Widget +create_text_widget(parent, filename) + Widget parent; + char *filename; +{ + static Arg arg[] = { + {XtNstring, NULL}, + {XtNtype, XawAsciiFile}, + {XtNcursor, None}, + }; + Widget text_widget; + + arg[0].value = (XtArgVal)filename; + text_widget = XtCreateManagedWidget("src", asciiTextWidgetClass, + parent, arg, XtNumber(arg)); + return (text_widget); +} + +/* + * Entry point to create the widgets representing our display. + */ +void +xgdb_create_window() +{ + /* initialize toolkit, setup defaults */ +#ifdef notyet + main_widget = XtAppInitialize(&app_context, "Xgdb", NULL, 0, + argcptr, argv, NULL, NULL, 0); +#else + char *dummy_argv[] = { "xgdb", 0 }; + int dummy_argc = 1; + main_widget = XtAppInitialize(&app_context, "Xgdb", NULL, 0, + &dummy_argc, dummy_argv, NULL, NULL, 0); +#endif + display = XtDisplay(main_widget); + containing_widget = XtCreateManagedWidget("frame", panedWidgetClass, + main_widget, NULL, 0); + + sprintf(version_label, "XGDB %s", version); + button_box_widget = XtCreateManagedWidget("buttons", boxWidgetClass, + containing_widget, NULL, 0); + create_buttons(button_box_widget); + source_name_widget = create_label("srcLabel", "No source file yet."); + source_text_widget = create_text_widget(containing_widget, "/dev/null"); + + XtRealizeWidget(main_widget); + XFlush(display); +} + +/* + * If we use an X window, the readline input loop is told to call + * this function before reading a character from stdin. + */ +/*ARGSUSED*/ +static void +xgdb_window_hook() +{ + register int inmask = 1 << fileno(stdin); + register int xmask = 1 << ConnectionNumber(display); + register int nfds, pend; + int input_rfds; + XEvent ev; + + /* + * Display our current idea of the `interesting' source file then + * loop, dispatching window events until data is available on + * stdin. Then return so the input data can be processed. + */ + input_avail = 0; + xgdb_display_source(); + + input_rfds = 0; + while (input_avail == 0 && (input_rfds & inmask) == 0) { + pend = XPending(display); + if (!pend) { + input_rfds = inmask | xmask; + nfds = select(32, &input_rfds, 0, 0, + (struct timeval *)0); + if (nfds == -1 && errno == EINTR) + continue; + } + if (pend || (input_rfds & xmask)) { + XNextEvent(display, &ev); + XtDispatchEvent(&ev); + } + } +} + +void +_initialize_xgdb() +{ + extern void (*window_hook) (); + extern int inhibit_windows; + extern struct cmd_list_element *deletelist; + + if (inhibit_windows) + return; + + if (! displayname) { + displayname = getenv("DISPLAY"); + if (! displayname) { + fprintf(stderr, "xgdb: no display name\n"); + inhibit_windows = 1; + return; + } + } + xgdb_create_window(); + window_hook = xgdb_window_hook; + add_com("button", class_support, button_command, +"Add command button to xgdb window. First argument is button\n\ +label, second is command associated with button. Command can\n\ +include printf-like escapes:\n\ + %s for current selection,\n\ + %S for first 'word' of current selection,\n\ + %e for current selection or expression at insertion pt,\n\ + %E for current selection or expression at insertion pt,\n\ + %l for current line number,\n\ + %L for line program stopped at,\n\ + %f for current file name,\n\ + %b for current breakpoint number."); + add_cmd("button", class_support, button_delete_command, +"Delete a button from the xgdb window.\n\ +Argument is name of button to be deleted.", + &deletelist); +} diff --git a/gnu/usr.bin/grep/AUTHORS b/gnu/usr.bin/grep/AUTHORS new file mode 100644 index 0000000000..e3e033b19a --- /dev/null +++ b/gnu/usr.bin/grep/AUTHORS @@ -0,0 +1,29 @@ +Mike Haertel wrote the main program and the dfa and kwset matchers. + +Arthur David Olson contributed the heuristics for finding fixed substrings +at the end of dfa.c. + +Richard Stallman and Karl Berry wrote the regex backtracking matcher. + +Henry Spencer wrote the original test suite from which grep's was derived. + +Scott Anderson invented the Khadafy test. + +David MacKenzie wrote the automatic configuration software use to +produce the configure script. + +Authors of the replacements for standard library routines are identified +in the corresponding source files. + +The idea of using Boyer-Moore type algorithms to quickly filter out +non-matching text before calling the regexp matcher was originally due +to James Woods. He also contributed some code to early versions of +GNU grep. + +Finally, I would like to thank Andrew Hume for many fascinating discussions +of string searching issues over the years. Hume & Sunday's excellent +paper on fast string searching (AT&T Bell Laboratories CSTR #156) +describes some of the history of the subject, as well as providing +exhaustive performance analysis of various implementation alternatives. +The inner loop of GNU grep is similar to Hume & Sunday's recommended +"Tuned Boyer Moore" inner loop. diff --git a/gnu/usr.bin/grep/Makefile b/gnu/usr.bin/grep/Makefile index 092e523ebe..c8d4915880 100644 --- a/gnu/usr.bin/grep/Makefile +++ b/gnu/usr.bin/grep/Makefile @@ -1,7 +1,14 @@ - PROG= grep -SRCS= dfa.c regex.o grep.o -CFLAGS+=-DSTDC_HEADERS=1 -DHAVE_UNISTD_H=1 -MLINKS= grep.1 egrep.1 +SRCS= dfa.c grep.c getopt.c kwset.c obstack.c regex.c search.c +CFLAGS+=-DGREP -DHAVE_STRING_H=1 -DHAVE_SYS_PARAM_H=1 -DHAVE_UNISTD_H=1 \ + -DHAVE_GETPAGESIZE=1 -DHAVE_MEMCHR=1 -DHAVE_STRERROR=1 \ + -DHAVE_VALLOC=1 + +LINKS+= ${BINDIR}/grep ${BINDIR}/egrep \ + ${BINDIR}/grep ${BINDIR}/fgrep +MLINKS= grep.1 egrep.1 grep.1 fgrep.1 + +check: all + sh ${.CURDIR}/tests/check.sh ${.CURDIR}/tests .include diff --git a/gnu/usr.bin/grep/NEWS b/gnu/usr.bin/grep/NEWS new file mode 100644 index 0000000000..eb0b513d6c --- /dev/null +++ b/gnu/usr.bin/grep/NEWS @@ -0,0 +1,35 @@ +Version 2.0: + +The most important user visible change is that egrep and fgrep have +disappeared as separate programs into the single grep program mandated +by POSIX 1003.2. New options -G, -E, and -F have been added, +selecting grep, egrep, and fgrep behavior respectively. For +compatibility with historical practice, hard links named egrep and +fgrep are also provided. See the manual page for details. + +In addition, the regular expression facilities described in Posix +draft 11.2 are now supported, except for internationalization features +related to locale-dependent collating sequence information. + +There is a new option, -L, which is like -l except it lists +files which don't contain matches. The reason this option was +added is because '-l -v' doesn't do what you expect. + +Performance has been improved; the amount of improvement is platform +dependent, but (for example) grep 2.0 typically runs at least 30% faster +than grep 1.6 on a DECstation using the MIPS compiler. Where possible, +grep now uses mmap() for file input; on a Sun 4 running SunOS 4.1 this +may cut system time by as much as half, for a total reduction in running +time by nearly 50%. On machines that don't use mmap(), the buffering +code has been rewritten to choose more favorable alignments and buffer +sizes for read(). + +Portability has been substantially cleaned up, and an automatic +configure script is now provided. + +The internals have changed in ways too numerous to mention. +People brave enough to reuse the DFA matcher in other programs +will now have their bravery amply "rewarded", for the interface +to that file has been completely changed. Some changes were +necessary to track the evolution of the regex package, and since +I was changing it anyway I decided to do a general cleanup. diff --git a/gnu/usr.bin/grep/PROJECTS b/gnu/usr.bin/grep/PROJECTS new file mode 100644 index 0000000000..67e9a2aad6 --- /dev/null +++ b/gnu/usr.bin/grep/PROJECTS @@ -0,0 +1,15 @@ +Write Texinfo documentation for grep. The manual page would be a good +place to start, but Info documents are also supposed to contain a +tutorial and examples. + +Fix the DFA matcher to never use exponential space. (Fortunately, these +cases are rare.) + +Improve the performance of the regex backtracking matcher. This matcher +is agonizingly slow, and is responsible for grep sometimes being slower +than Unix grep when backreferences are used. + +Provide support for the Posix [= =] and [. .] constructs. This is +difficult because it requires locale-dependent details of the character +set and collating sequence, but Posix does not standardize any method +for accessing this information! diff --git a/gnu/usr.bin/grep/README b/gnu/usr.bin/grep/README index 27f5bae42d..bc34a85906 100644 --- a/gnu/usr.bin/grep/README +++ b/gnu/usr.bin/grep/README @@ -1,70 +1,28 @@ -This README documents GNU e?grep version 1.6. All bugs reported for -previous versions have been fixed. +This is GNU grep 2.0, the "fastest grep in the west" (we hope). All +bugs reported in previous releases have been fixed. Many exciting new +bugs have probably been introduced in this major revision. -See the file INSTALL for compilation and installation instructions. - -Send bug reports to bug-gnu-utils@prep.ai.mit.edu. - -GNU e?grep is provided "as is" with no warranty. The exact terms +GNU grep is provided "as is" with no warranty. The exact terms under which you may use and (re)distribute this program are detailed in the GNU General Public License, in the file COPYING. -GNU e?grep is based on a fast lazy-state deterministic matcher (about +GNU grep is based on a fast lazy-state deterministic matcher (about twice as fast as stock Unix egrep) hybridized with a Boyer-Moore-Gosper search for a fixed string that eliminates impossible text from being considered by the full regexp matcher without necessarily having to look at every character. The result is typically many times faster than Unix grep or egrep. (Regular expressions containing backreferencing -may run more slowly, however.) - -GNU e?grep is brought to you by the efforts of several people: - - Mike Haertel wrote the deterministic regexp code and the bulk - of the program. - - James A. Woods is responsible for the hybridized search strategy - of using Boyer-Moore-Gosper fixed-string search as a filter - before calling the general regexp matcher. +will run more slowly, however.) - Arthur David Olson contributed code that finds fixed strings for - the aforementioned BMG search for a large class of regexps. +See the file AUTHORS for a list of authors and other contributors. - Richard Stallman wrote the backtracking regexp matcher that is - used for \ backreferences, as well as the getopt that - is provided for 4.2BSD sites. The backtracking matcher was - originally written for GNU Emacs. - - D. A. Gwyn wrote the C alloca emulation that is provided so - System V machines can run this program. (Alloca is used only - by RMS' backtracking matcher, and then only rarely, so there - is no loss if your machine doesn't have a "real" alloca.) - - Scott Anderson and Henry Spencer designed the regression tests - used in the "regress" script. - - Paul Placeway wrote the manual page, based on this README. - -If you are interested in improving this program, you may wish to try -any of the following: +See the file INSTALL for compilation and installation instructions. -1. Replace the fast search loop with a faster search loop. - There are several things that could be improved, the most notable - of which would be to calculate a minimal delta2 to use. +See the file MANIFEST for a list of files in this distribution. -2. Make backreferencing \ faster. Right now, backreferencing is - handled by calling the Emacs backtracking matcher to verify the partial - match. This is slow; if the DFA routines could handle backreferencing - themselves a speedup on the order of three to four times might occur - in those cases where the backtracking matcher is called to verify nearly - every line. Also, some portability problems due to the inclusion of the - emacs matcher would be solved because it could then be eliminated. - Note that expressions with backreferencing are not true regular - expressions, and thus are not equivalent to any DFA. So this is hard. +See the file NEWS for a description of major changes in this release. -3. Handle POSIX style regexps. I'm not sure if this could be called an - improvement; some of the things on regexps in the POSIX draft I have - seen are pretty sickening. But it would be useful in the interests of - conforming to the standard. +See the file PROJECTS if you want to be mentioned in AUTHORS. -4. Replace the main driver program grep.c with the much cleaner main driver - program used in GNU fgrep. +Send bug reports to bug-gnu-utils@prep.ai.mit.edu. Be sure to +include the word "grep" in your Subject: header field. diff --git a/gnu/usr.bin/grep/dfa.c b/gnu/usr.bin/grep/dfa.c index 08b383d04b..fc649af0cb 100644 --- a/gnu/usr.bin/grep/dfa.c +++ b/gnu/usr.bin/grep/dfa.c @@ -1,4 +1,4 @@ -/* dfa.c - determinisitic extended regexp routines for GNU +/* dfa.c - deterministic extended regexp routines for GNU Copyright (C) 1988 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify @@ -17,20 +17,59 @@ /* Written June, 1988 by Mike Haertel Modified July, 1988 by Arthur David Olson to assist BMG speedups */ - -#include + #include +#include +#include + +#ifdef STDC_HEADERS +#include +#else +#include +extern char *calloc(), *malloc(), *realloc(); +extern void free(); +#endif -#if defined(USG) || defined(STDC_HEADERS) +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) #include -#ifndef index +#undef index #define index strchr -#endif #else #include #endif +#ifndef isgraph +#define isgraph(C) (isprint(C) && !isspace(C)) +#endif + +#ifdef isascii +#define ISALPHA(C) (isascii(C) && isalpha(C)) +#define ISUPPER(C) (isascii(C) && isupper(C)) +#define ISLOWER(C) (isascii(C) && islower(C)) +#define ISDIGIT(C) (isascii(C) && isdigit(C)) +#define ISXDIGIT(C) (isascii(C) && isxdigit(C)) +#define ISSPACE(C) (isascii(C) && isspace(C)) +#define ISPUNCT(C) (isascii(C) && ispunct(C)) +#define ISALNUM(C) (isascii(C) && isalnum(C)) +#define ISPRINT(C) (isascii(C) && isprint(C)) +#define ISGRAPH(C) (isascii(C) && isgraph(C)) +#define ISCNTRL(C) (isascii(C) && iscntrl(C)) +#else +#define ISALPHA(C) isalpha(C) +#define ISUPPER(C) isupper(C) +#define ISLOWER(C) islower(C) +#define ISDIGIT(C) isdigit(C) +#define ISXDIGIT(C) isxdigit(C) +#define ISSPACE(C) isspace(C) +#define ISPUNCT(C) ispunct(C) +#define ISALNUM(C) isalnum(C) +#define ISPRINT(C) isprint(C) +#define ISGRAPH(C) isgraph(C) +#define ISCNTRL(C) iscntrl(C) +#endif + #include "dfa.h" +#include "regex.h" #if __STDC__ typedef void *ptr_t; @@ -38,7 +77,7 @@ typedef void *ptr_t; typedef char *ptr_t; #endif -static void regmust(); +static void dfamust(); static ptr_t xcalloc(n, s) @@ -48,11 +87,11 @@ xcalloc(n, s) ptr_t r = calloc(n, s); if (!r) - regerror("Memory exhausted"); + dfaerror("Memory exhausted"); return r; } -ptr_t /* Not static, so alloca.o can use it. */ +static ptr_t xmalloc(n) size_t n; { @@ -60,7 +99,7 @@ xmalloc(n) assert(n != 0); if (!r) - regerror("Memory exhausted"); + dfaerror("Memory exhausted"); return r; } @@ -73,7 +112,7 @@ xrealloc(p, n) assert(n != 0); if (!r) - regerror("Memory exhausted"); + dfaerror("Memory exhausted"); return r; } @@ -89,54 +128,52 @@ xrealloc(p, n) (nalloc) *= 2; \ REALLOC(p, t, nalloc); \ } - + #ifdef DEBUG -#include static void prtok(t) - _token t; + token t; { char *s; if (t < 0) fprintf(stderr, "END"); - else if (t < _NOTCHAR) + else if (t < NOTCHAR) fprintf(stderr, "%c", t); else { switch (t) { - case _EMPTY: s = "EMPTY"; break; - case _BACKREF: s = "BACKREF"; break; - case _BEGLINE: s = "BEGLINE"; break; - case _ALLBEGLINE: s = "ALLBEGLINE"; break; - case _ENDLINE: s = "ENDLINE"; break; - case _ALLENDLINE: s = "ALLENDLINE"; break; - case _BEGWORD: s = "BEGWORD"; break; - case _ENDWORD: s = "ENDWORD"; break; - case _LIMWORD: s = "LIMWORD"; break; - case _NOTLIMWORD: s = "NOTLIMWORD"; break; - case _QMARK: s = "QMARK"; break; - case _STAR: s = "STAR"; break; - case _PLUS: s = "PLUS"; break; - case _CAT: s = "CAT"; break; - case _OR: s = "OR"; break; - case _LPAREN: s = "LPAREN"; break; - case _RPAREN: s = "RPAREN"; break; - default: s = "SET"; break; + case EMPTY: s = "EMPTY"; break; + case BACKREF: s = "BACKREF"; break; + case BEGLINE: s = "BEGLINE"; break; + case ENDLINE: s = "ENDLINE"; break; + case BEGWORD: s = "BEGWORD"; break; + case ENDWORD: s = "ENDWORD"; break; + case LIMWORD: s = "LIMWORD"; break; + case NOTLIMWORD: s = "NOTLIMWORD"; break; + case QMARK: s = "QMARK"; break; + case STAR: s = "STAR"; break; + case PLUS: s = "PLUS"; break; + case CAT: s = "CAT"; break; + case OR: s = "OR"; break; + case ORTOP: s = "ORTOP"; break; + case LPAREN: s = "LPAREN"; break; + case RPAREN: s = "RPAREN"; break; + default: s = "CSET"; break; } fprintf(stderr, "%s", s); } } #endif /* DEBUG */ -/* Stuff pertaining to charsets. */ +/* Stuff pertaining to charclasses. */ static int tstbit(b, c) int b; - _charset c; + charclass c; { return c[b / INTBITS] & 1 << b % INTBITS; } @@ -144,7 +181,7 @@ tstbit(b, c) static void setbit(b, c) int b; - _charset c; + charclass c; { c[b / INTBITS] |= 1 << b % INTBITS; } @@ -152,83 +189,83 @@ setbit(b, c) static void clrbit(b, c) int b; - _charset c; + charclass c; { c[b / INTBITS] &= ~(1 << b % INTBITS); } static void copyset(src, dst) - const _charset src; - _charset dst; + charclass src; + charclass dst; { int i; - for (i = 0; i < _CHARSET_INTS; ++i) + for (i = 0; i < CHARCLASS_INTS; ++i) dst[i] = src[i]; } static void zeroset(s) - _charset s; + charclass s; { int i; - for (i = 0; i < _CHARSET_INTS; ++i) + for (i = 0; i < CHARCLASS_INTS; ++i) s[i] = 0; } static void notset(s) - _charset s; + charclass s; { int i; - for (i = 0; i < _CHARSET_INTS; ++i) + for (i = 0; i < CHARCLASS_INTS; ++i) s[i] = ~s[i]; } static int equal(s1, s2) - const _charset s1; - const _charset s2; + charclass s1; + charclass s2; { int i; - for (i = 0; i < _CHARSET_INTS; ++i) + for (i = 0; i < CHARCLASS_INTS; ++i) if (s1[i] != s2[i]) return 0; return 1; } - -/* A pointer to the current regexp is kept here during parsing. */ -static struct regexp *reg; -/* Find the index of charset s in reg->charsets, or allocate a new charset. */ +/* A pointer to the current dfa is kept here during parsing. */ +static struct dfa *dfa; + +/* Find the index of charclass s in dfa->charclasses, or allocate a new charclass. */ static int -charset_index(s) - const _charset s; +charclass_index(s) + charclass s; { int i; - for (i = 0; i < reg->cindex; ++i) - if (equal(s, reg->charsets[i])) + for (i = 0; i < dfa->cindex; ++i) + if (equal(s, dfa->charclasses[i])) return i; - REALLOC_IF_NECESSARY(reg->charsets, _charset, reg->calloc, reg->cindex); - ++reg->cindex; - copyset(s, reg->charsets[i]); + REALLOC_IF_NECESSARY(dfa->charclasses, charclass, dfa->calloc, dfa->cindex); + ++dfa->cindex; + copyset(s, dfa->charclasses[i]); return i; } /* Syntax bits controlling the behavior of the lexical analyzer. */ -static syntax_bits, syntax_bits_set; +static int syntax_bits, syntax_bits_set; /* Flag for case-folding letters into sets. */ -static case_fold; +static int case_fold; /* Entry point to set syntax options. */ void -regsyntax(bits, fold) +dfasyntax(bits, fold) int bits; int fold; { @@ -237,63 +274,132 @@ regsyntax(bits, fold) case_fold = fold; } -/* Lexical analyzer. */ -static const char *lexstart; /* Pointer to beginning of input string. */ -static const char *lexptr; /* Pointer to next input character. */ +/* Lexical analyzer. All the dross that deals with the obnoxious + GNU Regex syntax bits is located here. The poor, suffering + reader is referred to the GNU Regex documentation for the + meaning of the @#%!@#%^!@ syntax bits. */ + +static char *lexstart; /* Pointer to beginning of input string. */ +static char *lexptr; /* Pointer to next input character. */ static lexleft; /* Number of characters remaining. */ -static caret_allowed; /* True if backward context allows ^ - (meaningful only if RE_CONTEXT_INDEP_OPS - is turned off). */ -static closure_allowed; /* True if backward context allows closures - (meaningful only if RE_CONTEXT_INDEP_OPS - is turned off). */ +static token lasttok; /* Previous token returned; initially END. */ +static int laststart; /* True if we're separated from beginning or (, | + only by zero-width characters. */ +static int parens; /* Count of outstanding left parens. */ +static int minrep, maxrep; /* Repeat counts for {m,n}. */ /* Note that characters become unsigned here. */ #define FETCH(c, eoferr) \ { \ if (! lexleft) \ - if (eoferr) \ - regerror(eoferr); \ + if (eoferr != 0) \ + dfaerror(eoferr); \ else \ - return _END; \ + return END; \ (c) = (unsigned char) *lexptr++; \ --lexleft; \ } -static _token +#define FUNC(F, P) static int F(c) int c; { return P(c); } + +FUNC(is_alpha, ISALPHA) +FUNC(is_upper, ISUPPER) +FUNC(is_lower, ISLOWER) +FUNC(is_digit, ISDIGIT) +FUNC(is_xdigit, ISXDIGIT) +FUNC(is_space, ISSPACE) +FUNC(is_punct, ISPUNCT) +FUNC(is_alnum, ISALNUM) +FUNC(is_print, ISPRINT) +FUNC(is_graph, ISGRAPH) +FUNC(is_cntrl, ISCNTRL) + +/* The following list maps the names of the Posix named character classes + to predicate functions that determine whether a given character is in + the class. The leading [ has already been eaten by the lexical analyzer. */ +static struct { + char *name; + int (*pred)(); +} prednames[] = { + ":alpha:]", is_alpha, + ":upper:]", is_upper, + ":lower:]", is_lower, + ":digit:]", is_digit, + ":xdigit:]", is_xdigit, + ":space:]", is_space, + ":punct:]", is_punct, + ":alnum:]", is_alnum, + ":print:]", is_print, + ":graph:]", is_graph, + ":cntrl:]", is_cntrl, + 0 +}; + +static int +looking_at(s) + char *s; +{ + int len; + + len = strlen(s); + if (lexleft < len) + return 0; + return strncmp(s, lexptr, len) == 0; +} + +static token lex() { - _token c, c2; - int invert; - _charset cset; + token c, c1, c2; + int backslash = 0, invert; + charclass ccl; + int i; - FETCH(c, (char *) 0); - switch (c) + /* Basic plan: We fetch a character. If it's a backslash, + we set the backslash flag and go through the loop again. + On the plus side, this avoids having a duplicate of the + main switch inside the backslash case. On the minus side, + it means that just about every case begins with + "if (backslash) ...". */ + for (i = 0; i < 2; ++i) { - case '^': - if (! (syntax_bits & RE_CONTEXT_INDEP_OPS) - && (!caret_allowed || - (syntax_bits & RE_TIGHT_VBAR) && lexptr - 1 != lexstart)) - goto normal_char; - caret_allowed = 0; - return syntax_bits & RE_TIGHT_VBAR ? _ALLBEGLINE : _BEGLINE; - - case '$': - if (syntax_bits & RE_CONTEXT_INDEP_OPS || !lexleft - || (! (syntax_bits & RE_TIGHT_VBAR) - && ((syntax_bits & RE_NO_BK_PARENS - ? lexleft > 0 && *lexptr == ')' - : lexleft > 1 && *lexptr == '\\' && lexptr[1] == ')') - || (syntax_bits & RE_NO_BK_VBAR - ? lexleft > 0 && *lexptr == '|' - : lexleft > 1 && *lexptr == '\\' && lexptr[1] == '|')))) - return syntax_bits & RE_TIGHT_VBAR ? _ALLENDLINE : _ENDLINE; - goto normal_char; - - case '\\': - FETCH(c, "Unfinished \\ quote"); + FETCH(c, 0); switch (c) { + case '\\': + if (backslash) + goto normal_char; + if (lexleft == 0) + dfaerror("Unfinished \\ escape"); + backslash = 1; + break; + + case '^': + if (backslash) + goto normal_char; + if (syntax_bits & RE_CONTEXT_INDEP_ANCHORS + || lasttok == END + || lasttok == LPAREN + || lasttok == OR) + return lasttok = BEGLINE; + goto normal_char; + + case '$': + if (backslash) + goto normal_char; + if (syntax_bits & RE_CONTEXT_INDEP_ANCHORS + || lexleft == 0 + || (syntax_bits & RE_NO_BK_PARENS + ? lexleft > 0 && *lexptr == ')' + : lexleft > 1 && lexptr[0] == '\\' && lexptr[1] == ')') + || (syntax_bits & RE_NO_BK_VBAR + ? lexleft > 0 && *lexptr == '|' + : lexleft > 1 && lexptr[0] == '\\' && lexptr[1] == '|') + || ((syntax_bits & RE_NEWLINE_ALT) + && lexleft > 0 && *lexptr == '\n')) + return lasttok = ENDLINE; + goto normal_char; + case '1': case '2': case '3': @@ -303,238 +409,307 @@ lex() case '7': case '8': case '9': - caret_allowed = 0; - closure_allowed = 1; - return _BACKREF; + if (backslash && !(syntax_bits & RE_NO_BK_REFS)) + { + laststart = 0; + return lasttok = BACKREF; + } + goto normal_char; case '<': - caret_allowed = 0; - return _BEGWORD; + if (backslash) + return lasttok = BEGWORD; + goto normal_char; case '>': - caret_allowed = 0; - return _ENDWORD; + if (backslash) + return lasttok = ENDWORD; + goto normal_char; case 'b': - caret_allowed = 0; - return _LIMWORD; + if (backslash) + return lasttok = LIMWORD; + goto normal_char; case 'B': - caret_allowed = 0; - return _NOTLIMWORD; - - case 'w': - case 'W': - zeroset(cset); - for (c2 = 0; c2 < _NOTCHAR; ++c2) - if (ISALNUM(c2)) - setbit(c2, cset); - if (c == 'W') - notset(cset); - caret_allowed = 0; - closure_allowed = 1; - return _SET + charset_index(cset); + if (backslash) + return lasttok = NOTLIMWORD; + goto normal_char; case '?': - if (syntax_bits & RE_BK_PLUS_QM) - goto qmark; - goto normal_char; + if (syntax_bits & RE_LIMITED_OPS) + goto normal_char; + if (backslash != ((syntax_bits & RE_BK_PLUS_QM) != 0)) + goto normal_char; + if (!(syntax_bits & RE_CONTEXT_INDEP_OPS) && laststart) + goto normal_char; + return lasttok = QMARK; + + case '*': + if (backslash) + goto normal_char; + if (!(syntax_bits & RE_CONTEXT_INDEP_OPS) && laststart) + goto normal_char; + return lasttok = STAR; case '+': - if (syntax_bits & RE_BK_PLUS_QM) - goto plus; - goto normal_char; + if (syntax_bits & RE_LIMITED_OPS) + goto normal_char; + if (backslash != ((syntax_bits & RE_BK_PLUS_QM) != 0)) + goto normal_char; + if (!(syntax_bits & RE_CONTEXT_INDEP_OPS) && laststart) + goto normal_char; + return lasttok = PLUS; + + case '{': + if (!(syntax_bits & RE_INTERVALS)) + goto normal_char; + if (backslash != ((syntax_bits & RE_NO_BK_BRACES) == 0)) + goto normal_char; + minrep = maxrep = 0; + /* Cases: + {M} - exact count + {M,} - minimum count, maximum is infinity + {,M} - 0 through M + {M,N} - M through N */ + FETCH(c, "unfinished repeat count"); + if (ISDIGIT(c)) + { + minrep = c - '0'; + for (;;) + { + FETCH(c, "unfinished repeat count"); + if (!ISDIGIT(c)) + break; + minrep = 10 * minrep + c - '0'; + } + } + else if (c != ',') + dfaerror("malformed repeat count"); + if (c == ',') + for (;;) + { + FETCH(c, "unfinished repeat count"); + if (!ISDIGIT(c)) + break; + maxrep = 10 * maxrep + c - '0'; + } + else + maxrep = minrep; + if (!(syntax_bits & RE_NO_BK_BRACES)) + { + if (c != '\\') + dfaerror("malformed repeat count"); + FETCH(c, "unfinished repeat count"); + } + if (c != '}') + dfaerror("malformed repeat count"); + laststart = 0; + return lasttok = REPMN; case '|': - if (! (syntax_bits & RE_NO_BK_VBAR)) - goto or; - goto normal_char; + if (syntax_bits & RE_LIMITED_OPS) + goto normal_char; + if (backslash != ((syntax_bits & RE_NO_BK_VBAR) == 0)) + goto normal_char; + laststart = 1; + return lasttok = OR; + + case '\n': + if (syntax_bits & RE_LIMITED_OPS + || backslash + || !(syntax_bits & RE_NEWLINE_ALT)) + goto normal_char; + laststart = 1; + return lasttok = OR; case '(': - if (! (syntax_bits & RE_NO_BK_PARENS)) - goto lparen; - goto normal_char; + if (backslash != ((syntax_bits & RE_NO_BK_PARENS) == 0)) + goto normal_char; + ++parens; + laststart = 1; + return lasttok = LPAREN; case ')': - if (! (syntax_bits & RE_NO_BK_PARENS)) - goto rparen; - goto normal_char; - - default: - goto normal_char; - } + if (backslash != ((syntax_bits & RE_NO_BK_PARENS) == 0)) + goto normal_char; + if (parens == 0 && syntax_bits & RE_UNMATCHED_RIGHT_PAREN_ORD) + goto normal_char; + --parens; + laststart = 0; + return lasttok = RPAREN; + + case '.': + if (backslash) + goto normal_char; + zeroset(ccl); + notset(ccl); + if (!(syntax_bits & RE_DOT_NEWLINE)) + clrbit('\n', ccl); + if (syntax_bits & RE_DOT_NOT_NULL) + clrbit('\0', ccl); + laststart = 0; + return lasttok = CSET + charclass_index(ccl); - case '?': - if (syntax_bits & RE_BK_PLUS_QM) - goto normal_char; - qmark: - if (! (syntax_bits & RE_CONTEXT_INDEP_OPS) && !closure_allowed) - goto normal_char; - return _QMARK; - - case '*': - if (! (syntax_bits & RE_CONTEXT_INDEP_OPS) && !closure_allowed) - goto normal_char; - return _STAR; - - case '+': - if (syntax_bits & RE_BK_PLUS_QM) - goto normal_char; - plus: - if (! (syntax_bits & RE_CONTEXT_INDEP_OPS) && !closure_allowed) - goto normal_char; - return _PLUS; - - case '|': - if (! (syntax_bits & RE_NO_BK_VBAR)) - goto normal_char; - or: - caret_allowed = 1; - closure_allowed = 0; - return _OR; - - case '\n': - if (! (syntax_bits & RE_NEWLINE_OR)) - goto normal_char; - goto or; - - case '(': - if (! (syntax_bits & RE_NO_BK_PARENS)) - goto normal_char; - lparen: - caret_allowed = 1; - closure_allowed = 0; - return _LPAREN; - - case ')': - if (! (syntax_bits & RE_NO_BK_PARENS)) - goto normal_char; - rparen: - caret_allowed = 0; - closure_allowed = 1; - return _RPAREN; - - case '.': - zeroset(cset); - notset(cset); - clrbit('\n', cset); - caret_allowed = 0; - closure_allowed = 1; - return _SET + charset_index(cset); - - case '[': - zeroset(cset); - FETCH(c, "Unbalanced ["); - if (c == '^') - { + case 'w': + case 'W': + if (!backslash) + goto normal_char; + zeroset(ccl); + for (c2 = 0; c2 < NOTCHAR; ++c2) + if (ISALNUM(c2)) + setbit(c2, ccl); + if (c == 'W') + notset(ccl); + laststart = 0; + return lasttok = CSET + charclass_index(ccl); + + case '[': + if (backslash) + goto normal_char; + zeroset(ccl); FETCH(c, "Unbalanced ["); - invert = 1; - } - else - invert = 0; - do - { - FETCH(c2, "Unbalanced ["); - if (c2 == '-') + if (c == '^') { - FETCH(c2, "Unbalanced ["); + FETCH(c, "Unbalanced ["); + invert = 1; + } + else + invert = 0; + do + { + /* Nobody ever said this had to be fast. :-) + Note that if we're looking at some other [:...:] + construct, we just treat it as a bunch of ordinary + characters. We can do this because we assume + regex has checked for syntax errors before + dfa is ever called. */ + if (c == '[' && (syntax_bits & RE_CHAR_CLASSES)) + for (c1 = 0; prednames[c1].name; ++c1) + if (looking_at(prednames[c1].name)) + { + for (c2 = 0; c2 < NOTCHAR; ++c2) + if ((*prednames[c1].pred)(c2)) + setbit(c2, ccl); + lexptr += strlen(prednames[c1].name); + lexleft -= strlen(prednames[c1].name); + FETCH(c1, "Unbalanced ["); + goto skip; + } + if (c == '\\' && (syntax_bits & RE_BACKSLASH_ESCAPE_IN_LISTS)) + FETCH(c, "Unbalanced ["); + FETCH(c1, "Unbalanced ["); + if (c1 == '-') + { + FETCH(c2, "Unbalanced ["); + if (c2 == ']') + { + /* In the case [x-], the - is an ordinary hyphen, + which is left in c1, the lookahead character. */ + --lexptr; + ++lexleft; + c2 = c; + } + else + { + if (c2 == '\\' + && (syntax_bits & RE_BACKSLASH_ESCAPE_IN_LISTS)) + FETCH(c2, "Unbalanced ["); + FETCH(c1, "Unbalanced ["); + } + } + else + c2 = c; while (c <= c2) { - setbit(c, cset); + setbit(c, ccl); if (case_fold) if (ISUPPER(c)) - setbit(tolower(c), cset); + setbit(tolower(c), ccl); else if (ISLOWER(c)) - setbit(toupper(c), cset); + setbit(toupper(c), ccl); ++c; } - FETCH(c, "Unbalanced ["); + skip: + ; } - else + while ((c = c1) != ']'); + if (invert) { - setbit(c, cset); - if (case_fold) - if (ISUPPER(c)) - setbit(tolower(c), cset); - else if (ISLOWER(c)) - setbit(toupper(c), cset); - c = c2; + notset(ccl); + if (syntax_bits & RE_HAT_LISTS_NOT_NEWLINE) + clrbit('\n', ccl); } - } - while (c != ']'); - if (invert) - notset(cset); - caret_allowed = 0; - closure_allowed = 1; - return _SET + charset_index(cset); + laststart = 0; + return lasttok = CSET + charclass_index(ccl); - default: - normal_char: - caret_allowed = 0; - closure_allowed = 1; - if (case_fold && ISALPHA(c)) - { - zeroset(cset); - if (isupper(c)) - c = tolower(c); - setbit(c, cset); - setbit(toupper(c), cset); - return _SET + charset_index(cset); + default: + normal_char: + laststart = 0; + if (case_fold && ISALPHA(c)) + { + zeroset(ccl); + setbit(c, ccl); + if (isupper(c)) + setbit(tolower(c), ccl); + else + setbit(toupper(c), ccl); + return lasttok = CSET + charclass_index(ccl); + } + return c; } - return c; } + + /* The above loop should consume at most a backslash + and some other character. */ + abort(); } - + /* Recursive descent parser for regular expressions. */ -static _token tok; /* Lookahead token. */ +static token tok; /* Lookahead token. */ static depth; /* Current depth of a hypothetical stack holding deferred productions. This is used to determine the depth that will be required of the real stack later on in - reganalyze(). */ + dfaanalyze(). */ /* Add the given token to the parse tree, maintaining the depth count and updating the maximum depth if necessary. */ static void addtok(t) - _token t; + token t; { - REALLOC_IF_NECESSARY(reg->tokens, _token, reg->talloc, reg->tindex); - reg->tokens[reg->tindex++] = t; + REALLOC_IF_NECESSARY(dfa->tokens, token, dfa->talloc, dfa->tindex); + dfa->tokens[dfa->tindex++] = t; switch (t) { - case _QMARK: - case _STAR: - case _PLUS: + case QMARK: + case STAR: + case PLUS: break; - case _CAT: - case _OR: + case CAT: + case OR: + case ORTOP: --depth; break; default: - ++reg->nleaves; - case _EMPTY: + ++dfa->nleaves; + case EMPTY: ++depth; break; } - if (depth > reg->depth) - reg->depth = depth; + if (depth > dfa->depth) + dfa->depth = depth; } /* The grammar understood by the parser is as follows. - start: - regexp - _ALLBEGLINE regexp - regexp _ALLENDLINE - _ALLBEGLINE regexp _ALLENDLINE - regexp: - regexp _OR branch + regexp OR branch branch branch: @@ -542,27 +717,27 @@ addtok(t) closure closure: - closure _QMARK - closure _STAR - closure _PLUS + closure QMARK + closure STAR + closure PLUS atom atom: - _SET - _BACKREF - _BEGLINE - _ENDLINE - _BEGWORD - _ENDWORD - _LIMWORD - _NOTLIMWORD + CSET + BACKREF + BEGLINE + ENDLINE + BEGWORD + ENDWORD + LIMWORD + NOTLIMWORD The parser builds a parse tree in postfix form in an array of tokens. */ #if __STDC__ -static void regexp(void); +static void regexp(int); #else static void regexp(); #endif @@ -570,116 +745,164 @@ static void regexp(); static void atom() { - if (tok >= 0 && tok < _NOTCHAR || tok >= _SET || tok == _BACKREF - || tok == _BEGLINE || tok == _ENDLINE || tok == _BEGWORD - || tok == _ENDWORD || tok == _LIMWORD || tok == _NOTLIMWORD) + if ((tok >= 0 && tok < NOTCHAR) || tok >= CSET || tok == BACKREF + || tok == BEGLINE || tok == ENDLINE || tok == BEGWORD + || tok == ENDWORD || tok == LIMWORD || tok == NOTLIMWORD) { addtok(tok); tok = lex(); } - else if (tok == _LPAREN) + else if (tok == LPAREN) { tok = lex(); - regexp(); - if (tok != _RPAREN) - regerror("Unbalanced ("); + regexp(0); + if (tok != RPAREN) + dfaerror("Unbalanced ("); tok = lex(); } else - addtok(_EMPTY); + addtok(EMPTY); +} + +/* Return the number of tokens in the given subexpression. */ +static int +nsubtoks(tindex) +{ + int ntoks1; + + switch (dfa->tokens[tindex - 1]) + { + default: + return 1; + case QMARK: + case STAR: + case PLUS: + return 1 + nsubtoks(tindex - 1); + case CAT: + case OR: + case ORTOP: + ntoks1 = nsubtoks(tindex - 1); + return 1 + ntoks1 + nsubtoks(tindex - 1 - ntoks1); + } +} + +/* Copy the given subexpression to the top of the tree. */ +static void +copytoks(tindex, ntokens) + int tindex, ntokens; +{ + int i; + + for (i = 0; i < ntokens; ++i) + addtok(dfa->tokens[tindex + i]); } static void closure() { + int tindex, ntokens, i; + atom(); - while (tok == _QMARK || tok == _STAR || tok == _PLUS) - { - addtok(tok); - tok = lex(); - } + while (tok == QMARK || tok == STAR || tok == PLUS || tok == REPMN) + if (tok == REPMN) + { + ntokens = nsubtoks(dfa->tindex); + tindex = dfa->tindex - ntokens; + if (maxrep == 0) + addtok(PLUS); + if (minrep == 0) + addtok(QMARK); + for (i = 1; i < minrep; ++i) + { + copytoks(tindex, ntokens); + addtok(CAT); + } + for (; i < maxrep; ++i) + { + copytoks(tindex, ntokens); + addtok(QMARK); + addtok(CAT); + } + tok = lex(); + } + else + { + addtok(tok); + tok = lex(); + } } static void branch() { closure(); - while (tok != _RPAREN && tok != _OR && tok != _ALLENDLINE && tok >= 0) + while (tok != RPAREN && tok != OR && tok >= 0) { closure(); - addtok(_CAT); + addtok(CAT); } } static void -regexp() +regexp(toplevel) + int toplevel; { branch(); - while (tok == _OR) + while (tok == OR) { tok = lex(); branch(); - addtok(_OR); + if (toplevel) + addtok(ORTOP); + else + addtok(OR); } } /* Main entry point for the parser. S is a string to be parsed, len is the - length of the string, so s can include NUL characters. R is a pointer to - the struct regexp to parse into. */ + length of the string, so s can include NUL characters. D is a pointer to + the struct dfa to parse into. */ void -regparse(s, len, r) - const char *s; +dfaparse(s, len, d) + char *s; size_t len; - struct regexp *r; + struct dfa *d; + { - reg = r; + dfa = d; lexstart = lexptr = s; lexleft = len; - caret_allowed = 1; - closure_allowed = 0; + lasttok = END; + laststart = 1; + parens = 0; if (! syntax_bits_set) - regerror("No syntax specified"); + dfaerror("No syntax specified"); tok = lex(); - depth = r->depth; - - if (tok == _ALLBEGLINE) - { - addtok(_BEGLINE); - tok = lex(); - regexp(); - addtok(_CAT); - } - else - regexp(); + depth = d->depth; - if (tok == _ALLENDLINE) - { - addtok(_ENDLINE); - addtok(_CAT); - tok = lex(); - } + regexp(1); - if (tok != _END) - regerror("Unbalanced )"); + if (tok != END) + dfaerror("Unbalanced )"); - addtok(_END - r->nregexps); - addtok(_CAT); + addtok(END - d->nregexps); + addtok(CAT); - if (r->nregexps) - addtok(_OR); + if (d->nregexps) + addtok(ORTOP); - ++r->nregexps; + ++d->nregexps; } - + /* Some primitives for operating on sets of positions. */ /* Copy one set to another; the destination must be large enough. */ static void copy(src, dst) - const _position_set *src; - _position_set *dst; + position_set *src; + position_set *dst; { int i; @@ -694,11 +917,11 @@ copy(src, dst) S->elems must point to an array large enough to hold the resulting set. */ static void insert(p, s) - _position p; - _position_set *s; + position p; + position_set *s; { int i; - _position t1, t2; + position t1, t2; for (i = 0; i < s->nelem && p.index < s->elems[i].index; ++i) ; @@ -721,9 +944,9 @@ insert(p, s) the positions of both sets were inserted into an initially empty set. */ static void merge(s1, s2, m) - _position_set *s1; - _position_set *s2; - _position_set *m; + position_set *s1; + position_set *s2; + position_set *m; { int i = 0, j = 0; @@ -747,8 +970,8 @@ merge(s1, s2, m) /* Delete a position from a set. */ static void delete(p, s) - _position p; - _position_set *s; + position p; + position_set *s; { int i; @@ -759,15 +982,15 @@ delete(p, s) for (--s->nelem; i < s->nelem; ++i) s->elems[i] = s->elems[i + 1]; } - + /* Find the index of the state corresponding to the given position set with the given preceding context, or create a new state if there is no such state. Newline and letter tell whether we got here on a newline or letter, respectively. */ static int -state_index(r, s, newline, letter) - struct regexp *r; - _position_set *s; +state_index(d, s, newline, letter) + struct dfa *d; + position_set *s; int newline; int letter; { @@ -782,75 +1005,75 @@ state_index(r, s, newline, letter) hash ^= s->elems[i].index + s->elems[i].constraint; /* Try to find a state that exactly matches the proposed one. */ - for (i = 0; i < r->sindex; ++i) + for (i = 0; i < d->sindex; ++i) { - if (hash != r->states[i].hash || s->nelem != r->states[i].elems.nelem - || newline != r->states[i].newline || letter != r->states[i].letter) + if (hash != d->states[i].hash || s->nelem != d->states[i].elems.nelem + || newline != d->states[i].newline || letter != d->states[i].letter) continue; for (j = 0; j < s->nelem; ++j) if (s->elems[j].constraint - != r->states[i].elems.elems[j].constraint - || s->elems[j].index != r->states[i].elems.elems[j].index) + != d->states[i].elems.elems[j].constraint + || s->elems[j].index != d->states[i].elems.elems[j].index) break; if (j == s->nelem) return i; } /* We'll have to create a new state. */ - REALLOC_IF_NECESSARY(r->states, _dfa_state, r->salloc, r->sindex); - r->states[i].hash = hash; - MALLOC(r->states[i].elems.elems, _position, s->nelem); - copy(s, &r->states[i].elems); - r->states[i].newline = newline; - r->states[i].letter = letter; - r->states[i].backref = 0; - r->states[i].constraint = 0; - r->states[i].first_end = 0; + REALLOC_IF_NECESSARY(d->states, dfa_state, d->salloc, d->sindex); + d->states[i].hash = hash; + MALLOC(d->states[i].elems.elems, position, s->nelem); + copy(s, &d->states[i].elems); + d->states[i].newline = newline; + d->states[i].letter = letter; + d->states[i].backref = 0; + d->states[i].constraint = 0; + d->states[i].first_end = 0; for (j = 0; j < s->nelem; ++j) - if (r->tokens[s->elems[j].index] < 0) + if (d->tokens[s->elems[j].index] < 0) { constraint = s->elems[j].constraint; - if (_SUCCEEDS_IN_CONTEXT(constraint, newline, 0, letter, 0) - || _SUCCEEDS_IN_CONTEXT(constraint, newline, 0, letter, 1) - || _SUCCEEDS_IN_CONTEXT(constraint, newline, 1, letter, 0) - || _SUCCEEDS_IN_CONTEXT(constraint, newline, 1, letter, 1)) - r->states[i].constraint |= constraint; - if (! r->states[i].first_end) - r->states[i].first_end = r->tokens[s->elems[j].index]; + if (SUCCEEDS_IN_CONTEXT(constraint, newline, 0, letter, 0) + || SUCCEEDS_IN_CONTEXT(constraint, newline, 0, letter, 1) + || SUCCEEDS_IN_CONTEXT(constraint, newline, 1, letter, 0) + || SUCCEEDS_IN_CONTEXT(constraint, newline, 1, letter, 1)) + d->states[i].constraint |= constraint; + if (! d->states[i].first_end) + d->states[i].first_end = d->tokens[s->elems[j].index]; } - else if (r->tokens[s->elems[j].index] == _BACKREF) + else if (d->tokens[s->elems[j].index] == BACKREF) { - r->states[i].constraint = _NO_CONSTRAINT; - r->states[i].backref = 1; + d->states[i].constraint = NO_CONSTRAINT; + d->states[i].backref = 1; } - ++r->sindex; + ++d->sindex; return i; } - + /* Find the epsilon closure of a set of positions. If any position of the set contains a symbol that matches the empty string in some context, replace that position with the elements of its follow labeled with an appropriate constraint. Repeat exhaustively until no funny positions are left. S->elems must be large enough to hold the result. */ void -epsclosure(s, r) - _position_set *s; - struct regexp *r; +epsclosure(s, d) + position_set *s; + struct dfa *d; { int i, j; int *visited; - _position p, old; + position p, old; - MALLOC(visited, int, r->tindex); - for (i = 0; i < r->tindex; ++i) + MALLOC(visited, int, d->tindex); + for (i = 0; i < d->tindex; ++i) visited[i] = 0; for (i = 0; i < s->nelem; ++i) - if (r->tokens[s->elems[i].index] >= _NOTCHAR - && r->tokens[s->elems[i].index] != _BACKREF - && r->tokens[s->elems[i].index] < _SET) + if (d->tokens[s->elems[i].index] >= NOTCHAR + && d->tokens[s->elems[i].index] != BACKREF + && d->tokens[s->elems[i].index] < CSET) { old = s->elems[i]; p.constraint = old.constraint; @@ -861,32 +1084,32 @@ epsclosure(s, r) continue; } visited[old.index] = 1; - switch (r->tokens[old.index]) + switch (d->tokens[old.index]) { - case _BEGLINE: - p.constraint &= _BEGLINE_CONSTRAINT; + case BEGLINE: + p.constraint &= BEGLINE_CONSTRAINT; break; - case _ENDLINE: - p.constraint &= _ENDLINE_CONSTRAINT; + case ENDLINE: + p.constraint &= ENDLINE_CONSTRAINT; break; - case _BEGWORD: - p.constraint &= _BEGWORD_CONSTRAINT; + case BEGWORD: + p.constraint &= BEGWORD_CONSTRAINT; break; - case _ENDWORD: - p.constraint &= _ENDWORD_CONSTRAINT; + case ENDWORD: + p.constraint &= ENDWORD_CONSTRAINT; break; - case _LIMWORD: - p.constraint &= _LIMWORD_CONSTRAINT; + case LIMWORD: + p.constraint &= LIMWORD_CONSTRAINT; break; - case _NOTLIMWORD: - p.constraint &= _NOTLIMWORD_CONSTRAINT; + case NOTLIMWORD: + p.constraint &= NOTLIMWORD_CONSTRAINT; break; default: break; } - for (j = 0; j < r->follows[old.index].nelem; ++j) + for (j = 0; j < d->follows[old.index].nelem; ++j) { - p.index = r->follows[old.index].elems[j].index; + p.index = d->follows[old.index].elems[j].index; insert(p, s); } /* Force rescan to start at the beginning. */ @@ -895,41 +1118,41 @@ epsclosure(s, r) free(visited); } - + /* Perform bottom-up analysis on the parse tree, computing various functions. Note that at this point, we're pretending constructs like \< are real characters rather than constraints on what can follow them. Nullable: A node is nullable if it is at the root of a regexp that can match the empty string. - * _EMPTY leaves are nullable. + * EMPTY leaves are nullable. * No other leaf is nullable. - * A _QMARK or _STAR node is nullable. - * A _PLUS node is nullable if its argument is nullable. - * A _CAT node is nullable if both its arguments are nullable. - * An _OR node is nullable if either argument is nullable. + * A QMARK or STAR node is nullable. + * A PLUS node is nullable if its argument is nullable. + * A CAT node is nullable if both its arguments are nullable. + * An OR node is nullable if either argument is nullable. Firstpos: The firstpos of a node is the set of positions (nonempty leaves) that could correspond to the first character of a string matching the regexp rooted at the given node. - * _EMPTY leaves have empty firstpos. + * EMPTY leaves have empty firstpos. * The firstpos of a nonempty leaf is that leaf itself. - * The firstpos of a _QMARK, _STAR, or _PLUS node is the firstpos of its + * The firstpos of a QMARK, STAR, or PLUS node is the firstpos of its argument. - * The firstpos of a _CAT node is the firstpos of the left argument, union + * The firstpos of a CAT node is the firstpos of the left argument, union the firstpos of the right if the left argument is nullable. - * The firstpos of an _OR node is the union of firstpos of each argument. + * The firstpos of an OR node is the union of firstpos of each argument. Lastpos: The lastpos of a node is the set of positions that could correspond to the last character of a string matching the regexp at the given node. - * _EMPTY leaves have empty lastpos. + * EMPTY leaves have empty lastpos. * The lastpos of a nonempty leaf is that leaf itself. - * The lastpos of a _QMARK, _STAR, or _PLUS node is the lastpos of its + * The lastpos of a QMARK, STAR, or PLUS node is the lastpos of its argument. - * The lastpos of a _CAT node is the lastpos of its right argument, union + * The lastpos of a CAT node is the lastpos of its right argument, union the lastpos of the left if the right argument is nullable. - * The lastpos of an _OR node is the union of the lastpos of each argument. + * The lastpos of an OR node is the union of the lastpos of each argument. Follow: The follow of a position is the set of positions that could correspond to the character following a character matching the node in @@ -938,9 +1161,9 @@ epsclosure(s, r) Later, if we find that a special symbol is in a follow set, we will replace it with the elements of its follow, labeled with an appropriate constraint. - * Every node in the firstpos of the argument of a _STAR or _PLUS node is in + * Every node in the firstpos of the argument of a STAR or PLUS node is in the follow of every node in the lastpos. - * Every node in the firstpos of the second argument of a _CAT node is in + * Every node in the firstpos of the second argument of a CAT node is in the follow of every node in the lastpos of the first argument. Because of the postfix representation of the parse tree, the depth-first @@ -949,61 +1172,61 @@ epsclosure(s, r) scheme; the number of elements in each set deeper in the stack can be used to determine the address of a particular set's array. */ void -reganalyze(r, searchflag) - struct regexp *r; +dfaanalyze(d, searchflag) + struct dfa *d; int searchflag; { int *nullable; /* Nullable stack. */ int *nfirstpos; /* Element count stack for firstpos sets. */ - _position *firstpos; /* Array where firstpos elements are stored. */ + position *firstpos; /* Array where firstpos elements are stored. */ int *nlastpos; /* Element count stack for lastpos sets. */ - _position *lastpos; /* Array where lastpos elements are stored. */ + position *lastpos; /* Array where lastpos elements are stored. */ int *nalloc; /* Sizes of arrays allocated to follow sets. */ - _position_set tmp; /* Temporary set for merging sets. */ - _position_set merged; /* Result of merging sets. */ + position_set tmp; /* Temporary set for merging sets. */ + position_set merged; /* Result of merging sets. */ int wants_newline; /* True if some position wants newline info. */ int *o_nullable; int *o_nfirst, *o_nlast; - _position *o_firstpos, *o_lastpos; + position *o_firstpos, *o_lastpos; int i, j; - _position *pos; + position *pos; #ifdef DEBUG - fprintf(stderr, "reganalyze:\n"); - for (i = 0; i < r->tindex; ++i) + fprintf(stderr, "dfaanalyze:\n"); + for (i = 0; i < d->tindex; ++i) { fprintf(stderr, " %d:", i); - prtok(r->tokens[i]); + prtok(d->tokens[i]); } putc('\n', stderr); #endif - r->searchflag = searchflag; + d->searchflag = searchflag; - MALLOC(nullable, int, r->depth); + MALLOC(nullable, int, d->depth); o_nullable = nullable; - MALLOC(nfirstpos, int, r->depth); + MALLOC(nfirstpos, int, d->depth); o_nfirst = nfirstpos; - MALLOC(firstpos, _position, r->nleaves); - o_firstpos = firstpos, firstpos += r->nleaves; - MALLOC(nlastpos, int, r->depth); + MALLOC(firstpos, position, d->nleaves); + o_firstpos = firstpos, firstpos += d->nleaves; + MALLOC(nlastpos, int, d->depth); o_nlast = nlastpos; - MALLOC(lastpos, _position, r->nleaves); - o_lastpos = lastpos, lastpos += r->nleaves; - MALLOC(nalloc, int, r->tindex); - for (i = 0; i < r->tindex; ++i) + MALLOC(lastpos, position, d->nleaves); + o_lastpos = lastpos, lastpos += d->nleaves; + MALLOC(nalloc, int, d->tindex); + for (i = 0; i < d->tindex; ++i) nalloc[i] = 0; - MALLOC(merged.elems, _position, r->nleaves); + MALLOC(merged.elems, position, d->nleaves); - CALLOC(r->follows, _position_set, r->tindex); + CALLOC(d->follows, position_set, d->tindex); - for (i = 0; i < r->tindex; ++i) + for (i = 0; i < d->tindex; ++i) #ifdef DEBUG { /* Nonsyntactic #ifdef goo... */ #endif - switch (r->tokens[i]) + switch (d->tokens[i]) { - case _EMPTY: + case EMPTY: /* The empty set is nullable. */ *nullable++ = 1; @@ -1011,8 +1234,8 @@ reganalyze(r, searchflag) *nfirstpos++ = *nlastpos++ = 0; break; - case _STAR: - case _PLUS: + case STAR: + case PLUS: /* Every element in the firstpos of the argument is in the follow of every element in the lastpos. */ tmp.nelem = nfirstpos[-1]; @@ -1020,19 +1243,19 @@ reganalyze(r, searchflag) pos = lastpos; for (j = 0; j < nlastpos[-1]; ++j) { - merge(&tmp, &r->follows[pos[j].index], &merged); - REALLOC_IF_NECESSARY(r->follows[pos[j].index].elems, _position, + merge(&tmp, &d->follows[pos[j].index], &merged); + REALLOC_IF_NECESSARY(d->follows[pos[j].index].elems, position, nalloc[pos[j].index], merged.nelem - 1); - copy(&merged, &r->follows[pos[j].index]); + copy(&merged, &d->follows[pos[j].index]); } - case _QMARK: - /* A _QMARK or _STAR node is automatically nullable. */ - if (r->tokens[i] != _PLUS) + case QMARK: + /* A QMARK or STAR node is automatically nullable. */ + if (d->tokens[i] != PLUS) nullable[-1] = 1; break; - case _CAT: + case CAT: /* Every element in the firstpos of the second argument is in the follow of every element in the lastpos of the first argument. */ tmp.nelem = nfirstpos[-1]; @@ -1040,13 +1263,13 @@ reganalyze(r, searchflag) pos = lastpos + nlastpos[-1]; for (j = 0; j < nlastpos[-2]; ++j) { - merge(&tmp, &r->follows[pos[j].index], &merged); - REALLOC_IF_NECESSARY(r->follows[pos[j].index].elems, _position, + merge(&tmp, &d->follows[pos[j].index], &merged); + REALLOC_IF_NECESSARY(d->follows[pos[j].index].elems, position, nalloc[pos[j].index], merged.nelem - 1); - copy(&merged, &r->follows[pos[j].index]); + copy(&merged, &d->follows[pos[j].index]); } - /* The firstpos of a _CAT node is the firstpos of the first argument, + /* The firstpos of a CAT node is the firstpos of the first argument, union that of the second argument if the first is nullable. */ if (nullable[-2]) nfirstpos[-2] += nfirstpos[-1]; @@ -1054,7 +1277,7 @@ reganalyze(r, searchflag) firstpos += nfirstpos[-1]; --nfirstpos; - /* The lastpos of a _CAT node is the lastpos of the second argument, + /* The lastpos of a CAT node is the lastpos of the second argument, union that of the first argument if the second is nullable. */ if (nullable[-1]) nlastpos[-2] += nlastpos[-1]; @@ -1068,12 +1291,13 @@ reganalyze(r, searchflag) } --nlastpos; - /* A _CAT node is nullable if both arguments are nullable. */ + /* A CAT node is nullable if both arguments are nullable. */ nullable[-2] = nullable[-1] && nullable[-2]; --nullable; break; - case _OR: + case OR: + case ORTOP: /* The firstpos is the union of the firstpos of each argument. */ nfirstpos[-2] += nfirstpos[-1]; --nfirstpos; @@ -1082,7 +1306,7 @@ reganalyze(r, searchflag) nlastpos[-2] += nlastpos[-1]; --nlastpos; - /* An _OR node is nullable if either argument is nullable. */ + /* An OR node is nullable if either argument is nullable. */ nullable[-2] = nullable[-1] || nullable[-2]; --nullable; break; @@ -1093,36 +1317,36 @@ reganalyze(r, searchflag) an "epsilon closure" effectively makes them nullable later. Backreferences have to get a real position so we can detect transitions on them later. But they are nullable. */ - *nullable++ = r->tokens[i] == _BACKREF; + *nullable++ = d->tokens[i] == BACKREF; /* This position is in its own firstpos and lastpos. */ *nfirstpos++ = *nlastpos++ = 1; --firstpos, --lastpos; firstpos->index = lastpos->index = i; - firstpos->constraint = lastpos->constraint = _NO_CONSTRAINT; + firstpos->constraint = lastpos->constraint = NO_CONSTRAINT; /* Allocate the follow set for this position. */ nalloc[i] = 1; - MALLOC(r->follows[i].elems, _position, nalloc[i]); + MALLOC(d->follows[i].elems, position, nalloc[i]); break; } #ifdef DEBUG /* ... balance the above nonsyntactic #ifdef goo... */ fprintf(stderr, "node %d:", i); - prtok(r->tokens[i]); + prtok(d->tokens[i]); putc('\n', stderr); fprintf(stderr, nullable[-1] ? " nullable: yes\n" : " nullable: no\n"); fprintf(stderr, " firstpos:"); for (j = nfirstpos[-1] - 1; j >= 0; --j) { fprintf(stderr, " %d:", firstpos[j].index); - prtok(r->tokens[firstpos[j].index]); + prtok(d->tokens[firstpos[j].index]); } fprintf(stderr, "\n lastpos:"); for (j = nlastpos[-1] - 1; j >= 0; --j) { fprintf(stderr, " %d:", lastpos[j].index); - prtok(r->tokens[lastpos[j].index]); + prtok(d->tokens[lastpos[j].index]); } putc('\n', stderr); } @@ -1130,26 +1354,26 @@ reganalyze(r, searchflag) /* For each follow set that is the follow set of a real position, replace it with its epsilon closure. */ - for (i = 0; i < r->tindex; ++i) - if (r->tokens[i] < _NOTCHAR || r->tokens[i] == _BACKREF - || r->tokens[i] >= _SET) + for (i = 0; i < d->tindex; ++i) + if (d->tokens[i] < NOTCHAR || d->tokens[i] == BACKREF + || d->tokens[i] >= CSET) { #ifdef DEBUG fprintf(stderr, "follows(%d:", i); - prtok(r->tokens[i]); + prtok(d->tokens[i]); fprintf(stderr, "):"); - for (j = r->follows[i].nelem - 1; j >= 0; --j) + for (j = d->follows[i].nelem - 1; j >= 0; --j) { - fprintf(stderr, " %d:", r->follows[i].elems[j].index); - prtok(r->tokens[r->follows[i].elems[j].index]); + fprintf(stderr, " %d:", d->follows[i].elems[j].index); + prtok(d->tokens[d->follows[i].elems[j].index]); } putc('\n', stderr); #endif - copy(&r->follows[i], &merged); - epsclosure(&merged, r); - if (r->follows[i].nelem < merged.nelem) - REALLOC(r->follows[i].elems, _position, merged.nelem); - copy(&merged, &r->follows[i]); + copy(&d->follows[i], &merged); + epsclosure(&merged, d); + if (d->follows[i].nelem < merged.nelem) + REALLOC(d->follows[i].elems, position, merged.nelem); + copy(&merged, &d->follows[i]); } /* Get the epsilon closure of the firstpos of the regexp. The result will @@ -1157,19 +1381,19 @@ reganalyze(r, searchflag) merged.nelem = 0; for (i = 0; i < nfirstpos[-1]; ++i) insert(firstpos[i], &merged); - epsclosure(&merged, r); + epsclosure(&merged, d); /* Check if any of the positions of state 0 will want newline context. */ wants_newline = 0; for (i = 0; i < merged.nelem; ++i) - if (_PREV_NEWLINE_DEPENDENT(merged.elems[i].constraint)) + if (PREV_NEWLINE_DEPENDENT(merged.elems[i].constraint)) wants_newline = 1; /* Build the initial state. */ - r->salloc = 1; - r->sindex = 0; - MALLOC(r->states, _dfa_state, r->salloc); - state_index(r, &merged, wants_newline, 0); + d->salloc = 1; + d->sindex = 0; + MALLOC(d->states, dfa_state, d->salloc); + state_index(d, &merged, wants_newline, 0); free(o_nullable); free(o_nfirst); @@ -1179,8 +1403,8 @@ reganalyze(r, searchflag) free(nalloc); free(merged.elems); } - -/* Find, for each character, the transition out of state s of r, and store + +/* Find, for each character, the transition out of state s of d, and store it in the appropriate slot of trans. We divide the positions of s into groups (positions can appear in more @@ -1211,25 +1435,25 @@ reganalyze(r, searchflag) create a new group labeled with the characters of C and insert this position in that group. */ void -regstate(s, r, trans) +dfastate(s, d, trans) int s; - struct regexp *r; + struct dfa *d; int trans[]; { - _position_set grps[_NOTCHAR]; /* As many as will ever be needed. */ - _charset labels[_NOTCHAR]; /* Labels corresponding to the groups. */ + position_set grps[NOTCHAR]; /* As many as will ever be needed. */ + charclass labels[NOTCHAR]; /* Labels corresponding to the groups. */ int ngrps = 0; /* Number of groups actually used. */ - _position pos; /* Current position being considered. */ - _charset matches; /* Set of matching characters. */ + position pos; /* Current position being considered. */ + charclass matches; /* Set of matching characters. */ int matchesf; /* True if matches is nonempty. */ - _charset intersect; /* Intersection with some label set. */ + charclass intersect; /* Intersection with some label set. */ int intersectf; /* True if intersect is nonempty. */ - _charset leftovers; /* Stuff in the label that didn't match. */ + charclass leftovers; /* Stuff in the label that didn't match. */ int leftoversf; /* True if leftovers is nonempty. */ - static _charset letters; /* Set of characters considered letters. */ - static _charset newline; /* Set of characters that aren't newline. */ - _position_set follows; /* Union of the follows of some group. */ - _position_set tmp; /* Temporary space for merging sets. */ + static charclass letters; /* Set of characters considered letters. */ + static charclass newline; /* Set of characters that aren't newline. */ + position_set follows; /* Union of the follows of some group. */ + position_set tmp; /* Temporary space for merging sets. */ int state; /* New state. */ int wants_newline; /* New state wants to know newline context. */ int state_newline; /* New state on a newline transition. */ @@ -1242,7 +1466,7 @@ regstate(s, r, trans) if (! initialized) { initialized = 1; - for (i = 0; i < _NOTCHAR; ++i) + for (i = 0; i < NOTCHAR; ++i) if (ISALNUM(i)) setbit(i, letters); setbit('\n', newline); @@ -1250,13 +1474,13 @@ regstate(s, r, trans) zeroset(matches); - for (i = 0; i < r->states[s].elems.nelem; ++i) + for (i = 0; i < d->states[s].elems.nelem; ++i) { - pos = r->states[s].elems.elems[i]; - if (r->tokens[pos.index] >= 0 && r->tokens[pos.index] < _NOTCHAR) - setbit(r->tokens[pos.index], matches); - else if (r->tokens[pos.index] >= _SET) - copyset(r->charsets[r->tokens[pos.index] - _SET], matches); + pos = d->states[s].elems.elems[i]; + if (d->tokens[pos.index] >= 0 && d->tokens[pos.index] < NOTCHAR) + setbit(d->tokens[pos.index], matches); + else if (d->tokens[pos.index] >= CSET) + copyset(d->charclasses[d->tokens[pos.index] - CSET], matches); else continue; @@ -1264,26 +1488,26 @@ regstate(s, r, trans) they fail in the current context. */ if (pos.constraint != 0xFF) { - if (! _MATCHES_NEWLINE_CONTEXT(pos.constraint, - r->states[s].newline, 1)) + if (! MATCHES_NEWLINE_CONTEXT(pos.constraint, + d->states[s].newline, 1)) clrbit('\n', matches); - if (! _MATCHES_NEWLINE_CONTEXT(pos.constraint, - r->states[s].newline, 0)) - for (j = 0; j < _CHARSET_INTS; ++j) + if (! MATCHES_NEWLINE_CONTEXT(pos.constraint, + d->states[s].newline, 0)) + for (j = 0; j < CHARCLASS_INTS; ++j) matches[j] &= newline[j]; - if (! _MATCHES_LETTER_CONTEXT(pos.constraint, - r->states[s].letter, 1)) - for (j = 0; j < _CHARSET_INTS; ++j) + if (! MATCHES_LETTER_CONTEXT(pos.constraint, + d->states[s].letter, 1)) + for (j = 0; j < CHARCLASS_INTS; ++j) matches[j] &= ~letters[j]; - if (! _MATCHES_LETTER_CONTEXT(pos.constraint, - r->states[s].letter, 0)) - for (j = 0; j < _CHARSET_INTS; ++j) + if (! MATCHES_LETTER_CONTEXT(pos.constraint, + d->states[s].letter, 0)) + for (j = 0; j < CHARCLASS_INTS; ++j) matches[j] &= letters[j]; /* If there are no characters left, there's no point in going on. */ - for (j = 0; j < _CHARSET_INTS && !matches[j]; ++j) + for (j = 0; j < CHARCLASS_INTS && !matches[j]; ++j) ; - if (j == _CHARSET_INTS) + if (j == CHARCLASS_INTS) continue; } @@ -1292,21 +1516,21 @@ regstate(s, r, trans) /* If matches contains a single character only, and the current group's label doesn't contain that character, go on to the next group. */ - if (r->tokens[pos.index] >= 0 && r->tokens[pos.index] < _NOTCHAR - && !tstbit(r->tokens[pos.index], labels[j])) + if (d->tokens[pos.index] >= 0 && d->tokens[pos.index] < NOTCHAR + && !tstbit(d->tokens[pos.index], labels[j])) continue; /* Check if this group's label has a nonempty intersection with matches. */ intersectf = 0; - for (k = 0; k < _CHARSET_INTS; ++k) + for (k = 0; k < CHARCLASS_INTS; ++k) (intersect[k] = matches[k] & labels[j][k]) ? intersectf = 1 : 0; if (! intersectf) continue; /* It does; now find the set differences both ways. */ leftoversf = matchesf = 0; - for (k = 0; k < _CHARSET_INTS; ++k) + for (k = 0; k < CHARCLASS_INTS; ++k) { /* Even an optimizing compiler can't know this for sure. */ int match = matches[k], label = labels[j][k]; @@ -1320,7 +1544,7 @@ regstate(s, r, trans) { copyset(leftovers, labels[ngrps]); copyset(intersect, labels[j]); - MALLOC(grps[ngrps].elems, _position, r->nleaves); + MALLOC(grps[ngrps].elems, position, d->nleaves); copy(&grps[j], &grps[ngrps]); ++ngrps; } @@ -1341,41 +1565,41 @@ regstate(s, r, trans) { copyset(matches, labels[ngrps]); zeroset(matches); - MALLOC(grps[ngrps].elems, _position, r->nleaves); + MALLOC(grps[ngrps].elems, position, d->nleaves); grps[ngrps].nelem = 1; grps[ngrps].elems[0] = pos; ++ngrps; } } - MALLOC(follows.elems, _position, r->nleaves); - MALLOC(tmp.elems, _position, r->nleaves); + MALLOC(follows.elems, position, d->nleaves); + MALLOC(tmp.elems, position, d->nleaves); /* If we are a searching matcher, the default transition is to a state containing the positions of state 0, otherwise the default transition is to fail miserably. */ - if (r->searchflag) + if (d->searchflag) { wants_newline = 0; wants_letter = 0; - for (i = 0; i < r->states[0].elems.nelem; ++i) + for (i = 0; i < d->states[0].elems.nelem; ++i) { - if (_PREV_NEWLINE_DEPENDENT(r->states[0].elems.elems[i].constraint)) + if (PREV_NEWLINE_DEPENDENT(d->states[0].elems.elems[i].constraint)) wants_newline = 1; - if (_PREV_LETTER_DEPENDENT(r->states[0].elems.elems[i].constraint)) + if (PREV_LETTER_DEPENDENT(d->states[0].elems.elems[i].constraint)) wants_letter = 1; } - copy(&r->states[0].elems, &follows); - state = state_index(r, &follows, 0, 0); + copy(&d->states[0].elems, &follows); + state = state_index(d, &follows, 0, 0); if (wants_newline) - state_newline = state_index(r, &follows, 1, 0); + state_newline = state_index(d, &follows, 1, 0); else state_newline = state; if (wants_letter) - state_letter = state_index(r, &follows, 0, 1); + state_letter = state_index(d, &follows, 0, 1); else state_letter = state; - for (i = 0; i < _NOTCHAR; ++i) + for (i = 0; i < NOTCHAR; ++i) if (i == '\n') trans[i] = state_newline; else if (ISALNUM(i)) @@ -1384,7 +1608,7 @@ regstate(s, r, trans) trans[i] = state; } else - for (i = 0; i < _NOTCHAR; ++i) + for (i = 0; i < NOTCHAR; ++i) trans[i] = -1; for (i = 0; i < ngrps; ++i) @@ -1394,44 +1618,44 @@ regstate(s, r, trans) /* Find the union of the follows of the positions of the group. This is a hideously inefficient loop. Fix it someday. */ for (j = 0; j < grps[i].nelem; ++j) - for (k = 0; k < r->follows[grps[i].elems[j].index].nelem; ++k) - insert(r->follows[grps[i].elems[j].index].elems[k], &follows); + for (k = 0; k < d->follows[grps[i].elems[j].index].nelem; ++k) + insert(d->follows[grps[i].elems[j].index].elems[k], &follows); /* If we are building a searching matcher, throw in the positions of state 0 as well. */ - if (r->searchflag) - for (j = 0; j < r->states[0].elems.nelem; ++j) - insert(r->states[0].elems.elems[j], &follows); + if (d->searchflag) + for (j = 0; j < d->states[0].elems.nelem; ++j) + insert(d->states[0].elems.elems[j], &follows); /* Find out if the new state will want any context information. */ wants_newline = 0; if (tstbit('\n', labels[i])) for (j = 0; j < follows.nelem; ++j) - if (_PREV_NEWLINE_DEPENDENT(follows.elems[j].constraint)) + if (PREV_NEWLINE_DEPENDENT(follows.elems[j].constraint)) wants_newline = 1; wants_letter = 0; - for (j = 0; j < _CHARSET_INTS; ++j) + for (j = 0; j < CHARCLASS_INTS; ++j) if (labels[i][j] & letters[j]) break; - if (j < _CHARSET_INTS) + if (j < CHARCLASS_INTS) for (j = 0; j < follows.nelem; ++j) - if (_PREV_LETTER_DEPENDENT(follows.elems[j].constraint)) + if (PREV_LETTER_DEPENDENT(follows.elems[j].constraint)) wants_letter = 1; /* Find the state(s) corresponding to the union of the follows. */ - state = state_index(r, &follows, 0, 0); + state = state_index(d, &follows, 0, 0); if (wants_newline) - state_newline = state_index(r, &follows, 1, 0); + state_newline = state_index(d, &follows, 1, 0); else state_newline = state; if (wants_letter) - state_letter = state_index(r, &follows, 0, 1); + state_letter = state_index(d, &follows, 0, 1); else state_letter = state; /* Set the transitions for each character in the current label. */ - for (j = 0; j < _CHARSET_INTS; ++j) + for (j = 0; j < CHARCLASS_INTS; ++j) for (k = 0; k < INTBITS; ++k) if (labels[i][j] & 1 << k) { @@ -1441,7 +1665,7 @@ regstate(s, r, trans) trans[c] = state_newline; else if (ISALNUM(c)) trans[c] = state_letter; - else if (c < _NOTCHAR) + else if (c < NOTCHAR) trans[c] = state; } } @@ -1451,18 +1675,18 @@ regstate(s, r, trans) free(follows.elems); free(tmp.elems); } - -/* Some routines for manipulating a compiled regexp's transition tables. + +/* Some routines for manipulating a compiled dfa's transition tables. Each state may or may not have a transition table; if it does, and it - is a non-accepting state, then r->trans[state] points to its table. - If it is an accepting state then r->fails[state] points to its table. - If it has no table at all, then r->trans[state] is NULL. + is a non-accepting state, then d->trans[state] points to its table. + If it is an accepting state then d->fails[state] points to its table. + If it has no table at all, then d->trans[state] is NULL. TODO: Improve this comment, get rid of the unnecessary redundancy. */ static void -build_state(s, r) +build_state(s, d) int s; - struct regexp *r; + struct dfa *d; { int *trans; /* The new transition table. */ int i; @@ -1471,87 +1695,87 @@ build_state(s, r) exist at once. 1024 is arbitrary. The idea is that the frequently used transition tables will be quickly rebuilt, whereas the ones that were only needed once or twice will be cleared away. */ - if (r->trcount >= 1024) + if (d->trcount >= 1024) { - for (i = 0; i < r->tralloc; ++i) - if (r->trans[i]) + for (i = 0; i < d->tralloc; ++i) + if (d->trans[i]) { - free((ptr_t) r->trans[i]); - r->trans[i] = NULL; + free((ptr_t) d->trans[i]); + d->trans[i] = NULL; } - else if (r->fails[i]) + else if (d->fails[i]) { - free((ptr_t) r->fails[i]); - r->fails[i] = NULL; + free((ptr_t) d->fails[i]); + d->fails[i] = NULL; } - r->trcount = 0; + d->trcount = 0; } - ++r->trcount; + ++d->trcount; /* Set up the success bits for this state. */ - r->success[s] = 0; - if (ACCEPTS_IN_CONTEXT(r->states[s].newline, 1, r->states[s].letter, 0, - s, *r)) - r->success[s] |= 4; - if (ACCEPTS_IN_CONTEXT(r->states[s].newline, 0, r->states[s].letter, 1, - s, *r)) - r->success[s] |= 2; - if (ACCEPTS_IN_CONTEXT(r->states[s].newline, 0, r->states[s].letter, 0, - s, *r)) - r->success[s] |= 1; - - MALLOC(trans, int, _NOTCHAR); - regstate(s, r, trans); + d->success[s] = 0; + if (ACCEPTS_IN_CONTEXT(d->states[s].newline, 1, d->states[s].letter, 0, + s, *d)) + d->success[s] |= 4; + if (ACCEPTS_IN_CONTEXT(d->states[s].newline, 0, d->states[s].letter, 1, + s, *d)) + d->success[s] |= 2; + if (ACCEPTS_IN_CONTEXT(d->states[s].newline, 0, d->states[s].letter, 0, + s, *d)) + d->success[s] |= 1; + + MALLOC(trans, int, NOTCHAR); + dfastate(s, d, trans); /* Now go through the new transition table, and make sure that the trans and fail arrays are allocated large enough to hold a pointer for the largest state mentioned in the table. */ - for (i = 0; i < _NOTCHAR; ++i) - if (trans[i] >= r->tralloc) + for (i = 0; i < NOTCHAR; ++i) + if (trans[i] >= d->tralloc) { - int oldalloc = r->tralloc; - - while (trans[i] >= r->tralloc) - r->tralloc *= 2; - REALLOC(r->realtrans, int *, r->tralloc + 1); - r->trans = r->realtrans + 1; - REALLOC(r->fails, int *, r->tralloc); - REALLOC(r->success, int, r->tralloc); - REALLOC(r->newlines, int, r->tralloc); - while (oldalloc < r->tralloc) + int oldalloc = d->tralloc; + + while (trans[i] >= d->tralloc) + d->tralloc *= 2; + REALLOC(d->realtrans, int *, d->tralloc + 1); + d->trans = d->realtrans + 1; + REALLOC(d->fails, int *, d->tralloc); + REALLOC(d->success, int, d->tralloc); + REALLOC(d->newlines, int, d->tralloc); + while (oldalloc < d->tralloc) { - r->trans[oldalloc] = NULL; - r->fails[oldalloc++] = NULL; + d->trans[oldalloc] = NULL; + d->fails[oldalloc++] = NULL; } } /* Keep the newline transition in a special place so we can use it as a sentinel. */ - r->newlines[s] = trans['\n']; + d->newlines[s] = trans['\n']; trans['\n'] = -1; - if (ACCEPTING(s, *r)) - r->fails[s] = trans; + if (ACCEPTING(s, *d)) + d->fails[s] = trans; else - r->trans[s] = trans; + d->trans[s] = trans; } static void -build_state_zero(r) - struct regexp *r; +build_state_zero(d) + struct dfa *d; { - r->tralloc = 1; - r->trcount = 0; - CALLOC(r->realtrans, int *, r->tralloc + 1); - r->trans = r->realtrans + 1; - CALLOC(r->fails, int *, r->tralloc); - MALLOC(r->success, int, r->tralloc); - MALLOC(r->newlines, int, r->tralloc); - build_state(0, r); + d->tralloc = 1; + d->trcount = 0; + CALLOC(d->realtrans, int *, d->tralloc + 1); + d->trans = d->realtrans + 1; + CALLOC(d->fails, int *, d->tralloc); + MALLOC(d->success, int, d->tralloc); + MALLOC(d->newlines, int, d->tralloc); + build_state(0, d); } - -/* Search through a buffer looking for a match to the given struct regexp. + +/* Search through a buffer looking for a match to the given struct dfa. Find the first occurrence of a string matching the regexp in the buffer, and the shortest possible version thereof. Return a pointer to the first character after the match, or NULL if none is found. Begin points to @@ -1565,8 +1789,8 @@ build_state_zero(r) match needs to be verified by a backtracking matcher. Otherwise we store a 0 in *backref. */ char * -regexecute(r, begin, end, newline, count, backref) - struct regexp *r; +dfaexec(d, begin, end, newline, count, backref) + struct dfa *d; char *begin; char *end; int newline; @@ -1575,9 +1799,9 @@ regexecute(r, begin, end, newline, count, backref) { register s, s1, tmp; /* Current state. */ register unsigned char *p; /* Current input character. */ - register **trans, *t; /* Copy of r->trans so it can be optimized + register **trans, *t; /* Copy of d->trans so it can be optimized into a register. */ - static sbit[_NOTCHAR]; /* Table for anding with r->success. */ + static sbit[NOTCHAR]; /* Table for anding with d->success. */ static sbit_init; if (! sbit_init) @@ -1585,7 +1809,7 @@ regexecute(r, begin, end, newline, count, backref) int i; sbit_init = 1; - for (i = 0; i < _NOTCHAR; ++i) + for (i = 0; i < NOTCHAR; ++i) if (i == '\n') sbit[i] = 4; else if (ISALNUM(i)) @@ -1594,18 +1818,18 @@ regexecute(r, begin, end, newline, count, backref) sbit[i] = 1; } - if (! r->tralloc) - build_state_zero(r); + if (! d->tralloc) + build_state_zero(d); - s = 0; + s = s1 = 0; p = (unsigned char *) begin; - trans = r->trans; + trans = d->trans; *end = '\n'; for (;;) { /* The dreaded inner loop. */ - if (t = trans[s]) + if ((t = trans[s]) != 0) do { s1 = t[*p++]; @@ -1613,18 +1837,18 @@ regexecute(r, begin, end, newline, count, backref) goto last_was_s; s = t[*p++]; } - while (t = trans[s]); + while ((t = trans[s]) != 0); goto last_was_s1; last_was_s: tmp = s, s = s1, s1 = tmp; last_was_s1: - if (s >= 0 && p <= (unsigned char *) end && r->fails[s]) + if (s >= 0 && p <= (unsigned char *) end && d->fails[s]) { - if (r->success[s] & sbit[*p]) + if (d->success[s] & sbit[*p]) { if (backref) - if (r->states[s].backref) + if (d->states[s].backref) *backref = 1; else *backref = 0; @@ -1632,7 +1856,7 @@ regexecute(r, begin, end, newline, count, backref) } s1 = s; - s = r->fails[s][*p++]; + s = d->fails[s][*p++]; continue; } @@ -1641,63 +1865,64 @@ regexecute(r, begin, end, newline, count, backref) ++*count; /* Check if we've run off the end of the buffer. */ - if ((char *) p >= end) + if ((char *) p > end) return NULL; if (s >= 0) { - build_state(s, r); - trans = r->trans; + build_state(s, d); + trans = d->trans; continue; } if (p[-1] == '\n' && newline) { - s = r->newlines[s1]; + s = d->newlines[s1]; continue; } s = 0; } } - -/* Initialize the components of a regexp that the other routines don't + +/* Initialize the components of a dfa that the other routines don't initialize for themselves. */ void -reginit(r) - struct regexp *r; +dfainit(d) + struct dfa *d; { - r->calloc = 1; - MALLOC(r->charsets, _charset, r->calloc); - r->cindex = 0; + d->calloc = 1; + MALLOC(d->charclasses, charclass, d->calloc); + d->cindex = 0; - r->talloc = 1; - MALLOC(r->tokens, _token, r->talloc); - r->tindex = r->depth = r->nleaves = r->nregexps = 0; + d->talloc = 1; + MALLOC(d->tokens, token, d->talloc); + d->tindex = d->depth = d->nleaves = d->nregexps = 0; - r->searchflag = 0; - r->tralloc = 0; + d->searchflag = 0; + d->tralloc = 0; + + d->musts = 0; } /* Parse and analyze a single string of the given length. */ void -regcompile(s, len, r, searchflag) - const char *s; +dfacomp(s, len, d, searchflag) + char *s; size_t len; - struct regexp *r; + struct dfa *d; int searchflag; { - if (case_fold) /* dummy folding in service of regmust() */ + if (case_fold) /* dummy folding in service of dfamust() */ { char *copy; int i; copy = malloc(len); if (!copy) - regerror("out of memory"); + dfaerror("out of memory"); - /* This is a complete kludge and could potentially break - \ escapes . . . */ + /* This is a kludge. */ case_fold = 0; for (i = 0; i < len; ++i) if (ISUPPER(s[i])) @@ -1705,572 +1930,596 @@ regcompile(s, len, r, searchflag) else copy[i] = s[i]; - reginit(r); - r->mustn = 0; - r->must[0] = '\0'; - regparse(copy, len, r); + dfainit(d); + dfaparse(copy, len, d); free(copy); - regmust(r); - reganalyze(r, searchflag); + dfamust(d); + d->cindex = d->tindex = d->depth = d->nleaves = d->nregexps = 0; case_fold = 1; - reginit(r); - regparse(s, len, r); - reganalyze(r, searchflag); + dfaparse(s, len, d); + dfaanalyze(d, searchflag); } else { - reginit(r); - regparse(s, len, r); - regmust(r); - reganalyze(r, searchflag); + dfainit(d); + dfaparse(s, len, d); + dfamust(d); + dfaanalyze(d, searchflag); } } -/* Free the storage held by the components of a regexp. */ +/* Free the storage held by the components of a dfa. */ void -regfree(r) - struct regexp *r; +dfafree(d) + struct dfa *d; { int i; - - free((ptr_t) r->charsets); - free((ptr_t) r->tokens); - for (i = 0; i < r->sindex; ++i) - free((ptr_t) r->states[i].elems.elems); - free((ptr_t) r->states); - for (i = 0; i < r->tindex; ++i) - if (r->follows[i].elems) - free((ptr_t) r->follows[i].elems); - free((ptr_t) r->follows); - for (i = 0; i < r->tralloc; ++i) - if (r->trans[i]) - free((ptr_t) r->trans[i]); - else if (r->fails[i]) - free((ptr_t) r->fails[i]); - free((ptr_t) r->realtrans); - free((ptr_t) r->fails); - free((ptr_t) r->newlines); + struct dfamust *dm, *ndm; + + free((ptr_t) d->charclasses); + free((ptr_t) d->tokens); + for (i = 0; i < d->sindex; ++i) + free((ptr_t) d->states[i].elems.elems); + free((ptr_t) d->states); + for (i = 0; i < d->tindex; ++i) + if (d->follows[i].elems) + free((ptr_t) d->follows[i].elems); + free((ptr_t) d->follows); + for (i = 0; i < d->tralloc; ++i) + if (d->trans[i]) + free((ptr_t) d->trans[i]); + else if (d->fails[i]) + free((ptr_t) d->fails[i]); + free((ptr_t) d->realtrans); + free((ptr_t) d->fails); + free((ptr_t) d->newlines); + for (dm = d->musts; dm; dm = ndm) + { + ndm = dm->next; + free(dm->must); + free((ptr_t) dm); + } } -/* -Having found the postfix representation of the regular expression, -try to find a long sequence of characters that must appear in any line -containing the r.e. -Finding a "longest" sequence is beyond the scope here; -we take an easy way out and hope for the best. -(Take "(ab|a)b"--please.) - -We do a bottom-up calculation of sequences of characters that must appear -in matches of r.e.'s represented by trees rooted at the nodes of the postfix -representation: +/* Having found the postfix representation of the regular expression, + try to find a long sequence of characters that must appear in any line + containing the r.e. + Finding a "longest" sequence is beyond the scope here; + we take an easy way out and hope for the best. + (Take "(ab|a)b"--please.) + + We do a bottom-up calculation of sequences of characters that must appear + in matches of r.e.'s represented by trees rooted at the nodes of the postfix + representation: sequences that must appear at the left of the match ("left") sequences that must appear at the right of the match ("right") lists of sequences that must appear somewhere in the match ("in") sequences that must constitute the match ("is") -When we get to the root of the tree, we use one of the longest of its -calculated "in" sequences as our answer. The sequence we find is returned in -r->must (where "r" is the single argument passed to "regmust"); -the length of the sequence is returned in r->mustn. - -The sequences calculated for the various types of node (in pseudo ANSI c) -are shown below. "p" is the operand of unary operators (and the left-hand -operand of binary operators); "q" is the right-hand operand of binary operators -. -"ZERO" means "a zero-length sequence" below. - -Type left right is in ----- ---- ----- -- -- -char c # c # c # c # c - -SET ZERO ZERO ZERO ZERO - -STAR ZERO ZERO ZERO ZERO - -QMARK ZERO ZERO ZERO ZERO - -PLUS p->left p->right ZERO p->in - -CAT (p->is==ZERO)? (q->is==ZERO)? (p->is!=ZERO && p->in plus - p->left : q->right : q->is!=ZERO) ? q->in plus - p->is##q->left p->right##q->is p->is##q->is : p->right##q->left - ZERO - -OR longest common longest common (do p->is and substrings common to - leading trailing q->is have same p->in and q->in - (sub)sequence (sub)sequence length and - of p->left of p->right content) ? - and q->left and q->right p->is : NULL - -If there's anything else we recognize in the tree, all four sequences get set -to zero-length sequences. If there's something we don't recognize in the tree, -we just return a zero-length sequence. -Break ties in favor of infrequent letters (choosing 'zzz' in preference to -'aaa')? - -And. . .is it here or someplace that we might ponder "optimizations" such as + When we get to the root of the tree, we use one of the longest of its + calculated "in" sequences as our answer. The sequence we find is returned in + d->must (where "d" is the single argument passed to "dfamust"); + the length of the sequence is returned in d->mustn. + + The sequences calculated for the various types of node (in pseudo ANSI c) + are shown below. "p" is the operand of unary operators (and the left-hand + operand of binary operators); "q" is the right-hand operand of binary + operators. + + "ZERO" means "a zero-length sequence" below. + + Type left right is in + ---- ---- ----- -- -- + char c # c # c # c # c + + CSET ZERO ZERO ZERO ZERO + + STAR ZERO ZERO ZERO ZERO + + QMARK ZERO ZERO ZERO ZERO + + PLUS p->left p->right ZERO p->in + + CAT (p->is==ZERO)? (q->is==ZERO)? (p->is!=ZERO && p->in plus + p->left : q->right : q->is!=ZERO) ? q->in plus + p->is##q->left p->right##q->is p->is##q->is : p->right##q->left + ZERO + + OR longest common longest common (do p->is and substrings common to + leading trailing q->is have same p->in and q->in + (sub)sequence (sub)sequence length and + of p->left of p->right content) ? + and q->left and q->right p->is : NULL + + If there's anything else we recognize in the tree, all four sequences get set + to zero-length sequences. If there's something we don't recognize in the tree, + we just return a zero-length sequence. + + Break ties in favor of infrequent letters (choosing 'zzz' in preference to + 'aaa')? + + And. . .is it here or someplace that we might ponder "optimizations" such as egrep 'psi|epsilon' -> egrep 'psi' egrep 'pepsi|epsilon' -> egrep 'epsi' (Yes, we now find "epsi" as a "string that must occur", but we might also - simplify the *entire* r.e. being sought -) + simplify the *entire* r.e. being sought) grep '[c]' -> grep 'c' grep '(ab|a)b' -> grep 'ab' grep 'ab*' -> grep 'a' grep 'a*b' -> grep 'b' -There are several issues: - Is optimization easy (enough)? - Does optimization actually accomplish anything, - or is the automaton you get from "psi|epsilon" (for example) - the same as the one you get from "psi" (for example)? + There are several issues: + + Is optimization easy (enough)? - Are optimizable r.e.'s likely to be used in real-life situations - (something like 'ab*' is probably unlikely; something like is - 'psi|epsilon' is likelier)? -*/ + Does optimization actually accomplish anything, + or is the automaton you get from "psi|epsilon" (for example) + the same as the one you get from "psi" (for example)? + + Are optimizable r.e.'s likely to be used in real-life situations + (something like 'ab*' is probably unlikely; something like is + 'psi|epsilon' is likelier)? */ static char * icatalloc(old, new) -char * old; -char * new; + char *old; + char *new; { - register char * result; - register int oldsize, newsize; - - newsize = (new == NULL) ? 0 : strlen(new); - if (old == NULL) - oldsize = 0; - else if (newsize == 0) - return old; - else oldsize = strlen(old); - if (old == NULL) - result = (char *) malloc(newsize + 1); - else result = (char *) realloc((void *) old, oldsize + newsize + 1); - if (result != NULL && new != NULL) - (void) strcpy(result + oldsize, new); - return result; + char *result; + int oldsize, newsize; + + newsize = (new == NULL) ? 0 : strlen(new); + if (old == NULL) + oldsize = 0; + else if (newsize == 0) + return old; + else oldsize = strlen(old); + if (old == NULL) + result = (char *) malloc(newsize + 1); + else + result = (char *) realloc((void *) old, oldsize + newsize + 1); + if (result != NULL && new != NULL) + (void) strcpy(result + oldsize, new); + return result; } static char * icpyalloc(string) -const char * string; + char *string; { - return icatalloc((char *) NULL, string); + return icatalloc((char *) NULL, string); } static char * istrstr(lookin, lookfor) -char * lookin; -register char * lookfor; + char *lookin; + char *lookfor; { - register char * cp; - register int len; - - len = strlen(lookfor); - for (cp = lookin; *cp != '\0'; ++cp) - if (strncmp(cp, lookfor, len) == 0) - return cp; - return NULL; + char *cp; + int len; + + len = strlen(lookfor); + for (cp = lookin; *cp != '\0'; ++cp) + if (strncmp(cp, lookfor, len) == 0) + return cp; + return NULL; } static void ifree(cp) -char * cp; + char *cp; { - if (cp != NULL) - free(cp); + if (cp != NULL) + free(cp); } static void freelist(cpp) -register char ** cpp; + char **cpp; { - register int i; + int i; - if (cpp == NULL) - return; - for (i = 0; cpp[i] != NULL; ++i) { - free(cpp[i]); - cpp[i] = NULL; - } + if (cpp == NULL) + return; + for (i = 0; cpp[i] != NULL; ++i) + { + free(cpp[i]); + cpp[i] = NULL; + } } static char ** enlist(cpp, new, len) -register char ** cpp; -register char * new; -int len; + char **cpp; + char *new; + int len; { - register int i, j; + int i, j; - if (cpp == NULL) - return NULL; - if ((new = icpyalloc(new)) == NULL) { - freelist(cpp); - return NULL; - } - new[len] = '\0'; - /* - ** Is there already something in the list that's new (or longer)? - */ - for (i = 0; cpp[i] != NULL; ++i) - if (istrstr(cpp[i], new) != NULL) { - free(new); - return cpp; - } - /* - ** Eliminate any obsoleted strings. - */ - j = 0; - while (cpp[j] != NULL) - if (istrstr(new, cpp[j]) == NULL) - ++j; - else { - free(cpp[j]); - if (--i == j) - break; - cpp[j] = cpp[i]; - cpp[i] = 0; - } - /* - ** Add the new string. - */ - cpp = (char **) realloc((char *) cpp, (i + 2) * sizeof *cpp); - if (cpp == NULL) - return NULL; - cpp[i] = new; - cpp[i + 1] = NULL; + if (cpp == NULL) + return NULL; + if ((new = icpyalloc(new)) == NULL) + { + freelist(cpp); + return NULL; + } + new[len] = '\0'; + /* Is there already something in the list that's new (or longer)? */ + for (i = 0; cpp[i] != NULL; ++i) + if (istrstr(cpp[i], new) != NULL) + { + free(new); return cpp; + } + /* Eliminate any obsoleted strings. */ + j = 0; + while (cpp[j] != NULL) + if (istrstr(new, cpp[j]) == NULL) + ++j; + else + { + free(cpp[j]); + if (--i == j) + break; + cpp[j] = cpp[i]; + cpp[i] = NULL; + } + /* Add the new string. */ + cpp = (char **) realloc((char *) cpp, (i + 2) * sizeof *cpp); + if (cpp == NULL) + return NULL; + cpp[i] = new; + cpp[i + 1] = NULL; + return cpp; } -/* -** Given pointers to two strings, -** return a pointer to an allocated list of their distinct common substrings. -** Return NULL if something seems wild. -*/ - +/* Given pointers to two strings, return a pointer to an allocated + list of their distinct common substrings. Return NULL if something + seems wild. */ static char ** comsubs(left, right) -char * left; -char * right; + char *left; + char *right; { - register char ** cpp; - register char * lcp; - register char * rcp; - register int i, len; - - if (left == NULL || right == NULL) - return NULL; - cpp = (char **) malloc(sizeof *cpp); - if (cpp == NULL) - return NULL; - cpp[0] = NULL; - for (lcp = left; *lcp != '\0'; ++lcp) { - len = 0; - rcp = index(right, *lcp); - while (rcp != NULL) { - for (i = 1; lcp[i] != '\0' && lcp[i] == rcp[i]; ++i) - ; - if (i > len) - len = i; - rcp = index(rcp + 1, *lcp); - } - if (len == 0) - continue; - if ((cpp = enlist(cpp, lcp, len)) == NULL) - break; + char **cpp; + char *lcp; + char *rcp; + int i, len; + + if (left == NULL || right == NULL) + return NULL; + cpp = (char **) malloc(sizeof *cpp); + if (cpp == NULL) + return NULL; + cpp[0] = NULL; + for (lcp = left; *lcp != '\0'; ++lcp) + { + len = 0; + rcp = index(right, *lcp); + while (rcp != NULL) + { + for (i = 1; lcp[i] != '\0' && lcp[i] == rcp[i]; ++i) + ; + if (i > len) + len = i; + rcp = index(rcp + 1, *lcp); } - return cpp; + if (len == 0) + continue; + if ((cpp = enlist(cpp, lcp, len)) == NULL) + break; + } + return cpp; } static char ** addlists(old, new) -char ** old; -char ** new; +char **old; +char **new; { - register int i; - - if (old == NULL || new == NULL) - return NULL; - for (i = 0; new[i] != NULL; ++i) { - old = enlist(old, new[i], strlen(new[i])); - if (old == NULL) - break; - } - return old; -} + int i; -/* -** Given two lists of substrings, -** return a new list giving substrings common to both. -*/ + if (old == NULL || new == NULL) + return NULL; + for (i = 0; new[i] != NULL; ++i) + { + old = enlist(old, new[i], strlen(new[i])); + if (old == NULL) + break; + } + return old; +} +/* Given two lists of substrings, return a new list giving substrings + common to both. */ static char ** inboth(left, right) -char ** left; -char ** right; + char **left; + char **right; { - register char ** both; - register char ** temp; - register int lnum, rnum; - - if (left == NULL || right == NULL) - return NULL; - both = (char **) malloc(sizeof *both); - if (both == NULL) - return NULL; - both[0] = NULL; - for (lnum = 0; left[lnum] != NULL; ++lnum) { - for (rnum = 0; right[rnum] != NULL; ++rnum) { - temp = comsubs(left[lnum], right[rnum]); - if (temp == NULL) { - freelist(both); - return NULL; - } - both = addlists(both, temp); - freelist(temp); - if (both == NULL) - return NULL; - } + char **both; + char **temp; + int lnum, rnum; + + if (left == NULL || right == NULL) + return NULL; + both = (char **) malloc(sizeof *both); + if (both == NULL) + return NULL; + both[0] = NULL; + for (lnum = 0; left[lnum] != NULL; ++lnum) + { + for (rnum = 0; right[rnum] != NULL; ++rnum) + { + temp = comsubs(left[lnum], right[rnum]); + if (temp == NULL) + { + freelist(both); + return NULL; + } + both = addlists(both, temp); + freelist(temp); + if (both == NULL) + return NULL; } - return both; + } + return both; } -typedef struct { - char ** in; - char * left; - char * right; - char * is; +typedef struct +{ + char **in; + char *left; + char *right; + char *is; } must; static void resetmust(mp) -register must * mp; +must *mp; { - mp->left[0] = mp->right[0] = mp->is[0] = '\0'; - freelist(mp->in); + mp->left[0] = mp->right[0] = mp->is[0] = '\0'; + freelist(mp->in); } static void -regmust(reg) -register struct regexp * reg; +dfamust(dfa) +struct dfa *dfa; { - register must * musts; - register must * mp; - register char * result; - register int ri; - register int i; - register _token t; - static must must0; - - reg->mustn = 0; - reg->must[0] = '\0'; - musts = (must *) malloc((reg->tindex + 1) * sizeof *musts); - if (musts == NULL) - return; - mp = musts; - for (i = 0; i <= reg->tindex; ++i) - mp[i] = must0; - for (i = 0; i <= reg->tindex; ++i) { - mp[i].in = (char **) malloc(sizeof *mp[i].in); - mp[i].left = malloc(2); - mp[i].right = malloc(2); - mp[i].is = malloc(2); - if (mp[i].in == NULL || mp[i].left == NULL || - mp[i].right == NULL || mp[i].is == NULL) - goto done; - mp[i].left[0] = mp[i].right[0] = mp[i].is[0] = '\0'; - mp[i].in[0] = NULL; - } - result = ""; + must *musts; + must *mp; + char *result; + int ri; + int i; + int exact; + token t; + static must must0; + struct dfamust *dm; + + result = ""; + exact = 0; + musts = (must *) malloc((dfa->tindex + 1) * sizeof *musts); + if (musts == NULL) + return; + mp = musts; + for (i = 0; i <= dfa->tindex; ++i) + mp[i] = must0; + for (i = 0; i <= dfa->tindex; ++i) + { + mp[i].in = (char **) malloc(sizeof *mp[i].in); + mp[i].left = malloc(2); + mp[i].right = malloc(2); + mp[i].is = malloc(2); + if (mp[i].in == NULL || mp[i].left == NULL || + mp[i].right == NULL || mp[i].is == NULL) + goto done; + mp[i].left[0] = mp[i].right[0] = mp[i].is[0] = '\0'; + mp[i].in[0] = NULL; + } #ifdef DEBUG - fprintf(stderr, "regmust:\n"); - for (i = 0; i < reg->tindex; ++i) { - fprintf(stderr, " %d:", i); - prtok(reg->tokens[i]); - } - putc('\n', stderr); + fprintf(stderr, "dfamust:\n"); + for (i = 0; i < dfa->tindex; ++i) + { + fprintf(stderr, " %d:", i); + prtok(dfa->tokens[i]); + } + putc('\n', stderr); #endif - for (ri = 0; ri < reg->tindex; ++ri) { - switch (t = reg->tokens[ri]) { - case _ALLBEGLINE: - case _ALLENDLINE: - case _LPAREN: - case _RPAREN: - goto done; /* "cannot happen" */ - case _EMPTY: - case _BEGLINE: - case _ENDLINE: - case _BEGWORD: - case _ENDWORD: - case _LIMWORD: - case _NOTLIMWORD: - case _BACKREF: - resetmust(mp); - break; - case _STAR: - case _QMARK: - if (mp <= musts) - goto done; /* "cannot happen" */ - --mp; - resetmust(mp); - break; - case _OR: - if (mp < &musts[2]) - goto done; /* "cannot happen" */ - { - register char ** new; - register must * lmp; - register must * rmp; - register int j, ln, rn, n; - - rmp = --mp; - lmp = --mp; - /* Guaranteed to be. Unlikely, but. . . */ - if (strcmp(lmp->is, rmp->is) != 0) - lmp->is[0] = '\0'; - /* Left side--easy */ - i = 0; - while (lmp->left[i] != '\0' && - lmp->left[i] == rmp->left[i]) - ++i; - lmp->left[i] = '\0'; - /* Right side */ - ln = strlen(lmp->right); - rn = strlen(rmp->right); - n = ln; - if (n > rn) - n = rn; - for (i = 0; i < n; ++i) - if (lmp->right[ln - i - 1] != - rmp->right[rn - i - 1]) - break; - for (j = 0; j < i; ++j) - lmp->right[j] = - lmp->right[(ln - i) + j]; - lmp->right[j] = '\0'; - new = inboth(lmp->in, rmp->in); - if (new == NULL) - goto done; - freelist(lmp->in); - free((char *) lmp->in); - lmp->in = new; - } - break; - case _PLUS: - if (mp <= musts) - goto done; /* "cannot happen" */ - --mp; - mp->is[0] = '\0'; - break; - case _END: - if (mp != &musts[1]) - goto done; /* "cannot happen" */ - for (i = 0; musts[0].in[i] != NULL; ++i) - if (strlen(musts[0].in[i]) > strlen(result)) - result = musts[0].in[i]; - goto done; - case _CAT: - if (mp < &musts[2]) - goto done; /* "cannot happen" */ - { - register must * lmp; - register must * rmp; - - rmp = --mp; - lmp = --mp; - /* - ** In. Everything in left, plus everything in - ** right, plus catenation of - ** left's right and right's left. - */ - lmp->in = addlists(lmp->in, rmp->in); - if (lmp->in == NULL) - goto done; - if (lmp->right[0] != '\0' && - rmp->left[0] != '\0') { - register char * tp; - - tp = icpyalloc(lmp->right); - if (tp == NULL) - goto done; - tp = icatalloc(tp, rmp->left); - if (tp == NULL) - goto done; - lmp->in = enlist(lmp->in, tp, - strlen(tp)); - free(tp); - if (lmp->in == NULL) - goto done; - } - /* Left-hand */ - if (lmp->is[0] != '\0') { - lmp->left = icatalloc(lmp->left, - rmp->left); - if (lmp->left == NULL) - goto done; - } - /* Right-hand */ - if (rmp->is[0] == '\0') - lmp->right[0] = '\0'; - lmp->right = icatalloc(lmp->right, rmp->right); - if (lmp->right == NULL) - goto done; - /* Guaranteed to be */ - if (lmp->is[0] != '\0' && rmp->is[0] != '\0') { - lmp->is = icatalloc(lmp->is, rmp->is); - if (lmp->is == NULL) - goto done; - } else - lmp->is[0] = '\0'; - } - break; - default: - if (t < _END) { - /* "cannot happen" */ - goto done; - } else if (t == '\0') { - /* not on *my* shift */ - goto done; - } else if (t >= _SET) { - /* easy enough */ - resetmust(mp); - } else { - /* plain character */ - resetmust(mp); - mp->is[0] = mp->left[0] = mp->right[0] = t; - mp->is[1] = mp->left[1] = mp->right[1] = '\0'; - mp->in = enlist(mp->in, mp->is, 1); - if (mp->in == NULL) - goto done; - } - break; - } + for (ri = 0; ri < dfa->tindex; ++ri) + { + switch (t = dfa->tokens[ri]) + { + case LPAREN: + case RPAREN: + goto done; /* "cannot happen" */ + case EMPTY: + case BEGLINE: + case ENDLINE: + case BEGWORD: + case ENDWORD: + case LIMWORD: + case NOTLIMWORD: + case BACKREF: + resetmust(mp); + break; + case STAR: + case QMARK: + if (mp <= musts) + goto done; /* "cannot happen" */ + --mp; + resetmust(mp); + break; + case OR: + case ORTOP: + if (mp < &musts[2]) + goto done; /* "cannot happen" */ + { + char **new; + must *lmp; + must *rmp; + int j, ln, rn, n; + + rmp = --mp; + lmp = --mp; + /* Guaranteed to be. Unlikely, but. . . */ + if (strcmp(lmp->is, rmp->is) != 0) + lmp->is[0] = '\0'; + /* Left side--easy */ + i = 0; + while (lmp->left[i] != '\0' && lmp->left[i] == rmp->left[i]) + ++i; + lmp->left[i] = '\0'; + /* Right side */ + ln = strlen(lmp->right); + rn = strlen(rmp->right); + n = ln; + if (n > rn) + n = rn; + for (i = 0; i < n; ++i) + if (lmp->right[ln - i - 1] != rmp->right[rn - i - 1]) + break; + for (j = 0; j < i; ++j) + lmp->right[j] = lmp->right[(ln - i) + j]; + lmp->right[j] = '\0'; + new = inboth(lmp->in, rmp->in); + if (new == NULL) + goto done; + freelist(lmp->in); + free((char *) lmp->in); + lmp->in = new; + } + break; + case PLUS: + if (mp <= musts) + goto done; /* "cannot happen" */ + --mp; + mp->is[0] = '\0'; + break; + case END: + if (mp != &musts[1]) + goto done; /* "cannot happen" */ + for (i = 0; musts[0].in[i] != NULL; ++i) + if (strlen(musts[0].in[i]) > strlen(result)) + result = musts[0].in[i]; + if (strcmp(result, musts[0].is) == 0) + exact = 1; + goto done; + case CAT: + if (mp < &musts[2]) + goto done; /* "cannot happen" */ + { + must *lmp; + must *rmp; + + rmp = --mp; + lmp = --mp; + /* In. Everything in left, plus everything in + right, plus catenation of + left's right and right's left. */ + lmp->in = addlists(lmp->in, rmp->in); + if (lmp->in == NULL) + goto done; + if (lmp->right[0] != '\0' && + rmp->left[0] != '\0') + { + char *tp; + + tp = icpyalloc(lmp->right); + if (tp == NULL) + goto done; + tp = icatalloc(tp, rmp->left); + if (tp == NULL) + goto done; + lmp->in = enlist(lmp->in, tp, + strlen(tp)); + free(tp); + if (lmp->in == NULL) + goto done; + } + /* Left-hand */ + if (lmp->is[0] != '\0') + { + lmp->left = icatalloc(lmp->left, + rmp->left); + if (lmp->left == NULL) + goto done; + } + /* Right-hand */ + if (rmp->is[0] == '\0') + lmp->right[0] = '\0'; + lmp->right = icatalloc(lmp->right, rmp->right); + if (lmp->right == NULL) + goto done; + /* Guaranteed to be */ + if (lmp->is[0] != '\0' && rmp->is[0] != '\0') + { + lmp->is = icatalloc(lmp->is, rmp->is); + if (lmp->is == NULL) + goto done; + } + else + lmp->is[0] = '\0'; + } + break; + default: + if (t < END) + { + /* "cannot happen" */ + goto done; + } + else if (t == '\0') + { + /* not on *my* shift */ + goto done; + } + else if (t >= CSET) + { + /* easy enough */ + resetmust(mp); + } + else + { + /* plain character */ + resetmust(mp); + mp->is[0] = mp->left[0] = mp->right[0] = t; + mp->is[1] = mp->left[1] = mp->right[1] = '\0'; + mp->in = enlist(mp->in, mp->is, 1); + if (mp->in == NULL) + goto done; + } + break; + } #ifdef DEBUG - fprintf(stderr, " node: %d:", ri); - prtok(reg->tokens[ri]); - fprintf(stderr, "\n in:"); - for (i = 0; mp->in[i]; ++i) - fprintf(stderr, " \"%s\"", mp->in[i]); - fprintf(stderr, "\n is: \"%s\"\n", mp->is); - fprintf(stderr, " left: \"%s\"\n", mp->left); - fprintf(stderr, " right: \"%s\"\n", mp->right); + fprintf(stderr, " node: %d:", ri); + prtok(dfa->tokens[ri]); + fprintf(stderr, "\n in:"); + for (i = 0; mp->in[i]; ++i) + fprintf(stderr, " \"%s\"", mp->in[i]); + fprintf(stderr, "\n is: \"%s\"\n", mp->is); + fprintf(stderr, " left: \"%s\"\n", mp->left); + fprintf(stderr, " right: \"%s\"\n", mp->right); #endif - ++mp; - } -done: - (void) strncpy(reg->must, result, MUST_MAX - 1); - reg->must[MUST_MAX - 1] = '\0'; - reg->mustn = strlen(reg->must); - mp = musts; - for (i = 0; i <= reg->tindex; ++i) { - freelist(mp[i].in); - ifree((char *) mp[i].in); - ifree(mp[i].left); - ifree(mp[i].right); - ifree(mp[i].is); - } - free((char *) mp); + ++mp; + } + done: + if (strlen(result)) + { + dm = (struct dfamust *) malloc(sizeof (struct dfamust)); + dm->exact = exact; + dm->must = malloc(strlen(result) + 1); + strcpy(dm->must, result); + dm->next = dfa->musts; + dfa->musts = dm; + } + mp = musts; + for (i = 0; i <= dfa->tindex; ++i) + { + freelist(mp[i].in); + ifree((char *) mp[i].in); + ifree(mp[i].left); + ifree(mp[i].right); + ifree(mp[i].is); + } + free((char *) mp); } diff --git a/gnu/usr.bin/grep/dfa.h b/gnu/usr.bin/grep/dfa.h index 33e4bf1419..32e05fc2aa 100644 --- a/gnu/usr.bin/grep/dfa.h +++ b/gnu/usr.bin/grep/dfa.h @@ -16,210 +16,115 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Written June, 1988 by Mike Haertel */ - -#ifdef STDC_HEADERS - -#include -#include - -#else /* !STDC_HEADERS */ - -#define const -#include /* For size_t. */ -extern char *calloc(), *malloc(), *realloc(); -extern void free(); - -#ifndef NULL -#define NULL 0 -#endif - -#endif /* ! STDC_HEADERS */ - -#include -#ifndef isascii -#define ISALNUM(c) isalnum(c) -#define ISALPHA(c) isalpha(c) -#define ISUPPER(c) isupper(c) -#define ISLOWER(c) islower(c) -#else -#define ISALNUM(c) (isascii(c) && isalnum(c)) -#define ISALPHA(c) (isascii(c) && isalpha(c)) -#define ISUPPER(c) (isascii(c) && isupper(c)) -#define ISLOWER(c) (isascii(c) && islower(c)) -#endif - -/* 1 means plain parentheses serve as grouping, and backslash - parentheses are needed for literal searching. - 0 means backslash-parentheses are grouping, and plain parentheses - are for literal searching. */ -#define RE_NO_BK_PARENS 1 - -/* 1 means plain | serves as the "or"-operator, and \| is a literal. - 0 means \| serves as the "or"-operator, and | is a literal. */ -#define RE_NO_BK_VBAR 2 - -/* 0 means plain + or ? serves as an operator, and \+, \? are literals. - 1 means \+, \? are operators and plain +, ? are literals. */ -#define RE_BK_PLUS_QM 4 - -/* 1 means | binds tighter than ^ or $. - 0 means the contrary. */ -#define RE_TIGHT_VBAR 8 - -/* 1 means treat \n as an _OR operator - 0 means treat it as a normal character */ -#define RE_NEWLINE_OR 16 - -/* 0 means that a special characters (such as *, ^, and $) always have - their special meaning regardless of the surrounding context. - 1 means that special characters may act as normal characters in some - contexts. Specifically, this applies to: - ^ - only special at the beginning, or after ( or | - $ - only special at the end, or before ) or | - *, +, ? - only special when not after the beginning, (, or | */ -#define RE_CONTEXT_INDEP_OPS 32 - -/* Now define combinations of bits for the standard possibilities. */ -#define RE_SYNTAX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_CONTEXT_INDEP_OPS) -#define RE_SYNTAX_EGREP (RE_SYNTAX_AWK | RE_NEWLINE_OR) -#define RE_SYNTAX_GREP (RE_BK_PLUS_QM | RE_NEWLINE_OR) -#define RE_SYNTAX_EMACS 0 + +/* FIXME: + 2. We should not export so much of the DFA internals. + In addition to clobbering modularity, we eat up valuable + name space. */ /* Number of bits in an unsigned char. */ #define CHARBITS 8 /* First integer value that is greater than any character code. */ -#define _NOTCHAR (1 << CHARBITS) +#define NOTCHAR (1 << CHARBITS) /* INTBITS need not be exact, just a lower bound. */ #define INTBITS (CHARBITS * sizeof (int)) /* Number of ints required to hold a bit for every character. */ -#define _CHARSET_INTS ((_NOTCHAR + INTBITS - 1) / INTBITS) +#define CHARCLASS_INTS ((NOTCHAR + INTBITS - 1) / INTBITS) /* Sets of unsigned characters are stored as bit vectors in arrays of ints. */ -typedef int _charset[_CHARSET_INTS]; +typedef int charclass[CHARCLASS_INTS]; /* The regexp is parsed into an array of tokens in postfix form. Some tokens are operators and others are terminal symbols. Most (but not all) of these codes are returned by the lexical analyzer. */ -#if __STDC__ typedef enum { - _END = -1, /* _END is a terminal symbol that matches the - end of input; any value of _END or less in + END = -1, /* END is a terminal symbol that matches the + end of input; any value of END or less in the parse tree is such a symbol. Accepting states of the DFA are those that would have - a transition on _END. */ + a transition on END. */ /* Ordinary character values are terminal symbols that match themselves. */ - _EMPTY = _NOTCHAR, /* _EMPTY is a terminal symbol that matches + EMPTY = NOTCHAR, /* EMPTY is a terminal symbol that matches the empty string. */ - _BACKREF, /* _BACKREF is generated by \; it + BACKREF, /* BACKREF is generated by \; it it not completely handled. If the scanner detects a transition on backref, it returns a kind of "semi-success" indicating that the match will have to be verified with a backtracking matcher. */ - _BEGLINE, /* _BEGLINE is a terminal symbol that matches + BEGLINE, /* BEGLINE is a terminal symbol that matches the empty string if it is at the beginning of a line. */ - _ALLBEGLINE, /* _ALLBEGLINE is a terminal symbol that - matches the empty string if it is at the - beginning of a line; _ALLBEGLINE applies - to the entire regexp and can only occur - as the first token thereof. _ALLBEGLINE - never appears in the parse tree; a _BEGLINE - is prepended with _CAT to the entire - regexp instead. */ - - _ENDLINE, /* _ENDLINE is a terminal symbol that matches + ENDLINE, /* ENDLINE is a terminal symbol that matches the empty string if it is at the end of a line. */ - _ALLENDLINE, /* _ALLENDLINE is to _ENDLINE as _ALLBEGLINE - is to _BEGLINE. */ - - _BEGWORD, /* _BEGWORD is a terminal symbol that matches + BEGWORD, /* BEGWORD is a terminal symbol that matches the empty string if it is at the beginning of a word. */ - _ENDWORD, /* _ENDWORD is a terminal symbol that matches + ENDWORD, /* ENDWORD is a terminal symbol that matches the empty string if it is at the end of a word. */ - _LIMWORD, /* _LIMWORD is a terminal symbol that matches + LIMWORD, /* LIMWORD is a terminal symbol that matches the empty string if it is at the beginning or the end of a word. */ - _NOTLIMWORD, /* _NOTLIMWORD is a terminal symbol that + NOTLIMWORD, /* NOTLIMWORD is a terminal symbol that matches the empty string if it is not at the beginning or end of a word. */ - _QMARK, /* _QMARK is an operator of one argument that + QMARK, /* QMARK is an operator of one argument that matches zero or one occurences of its argument. */ - _STAR, /* _STAR is an operator of one argument that + STAR, /* STAR is an operator of one argument that matches the Kleene closure (zero or more occurrences) of its argument. */ - _PLUS, /* _PLUS is an operator of one argument that + PLUS, /* PLUS is an operator of one argument that matches the positive closure (one or more occurrences) of its argument. */ - _CAT, /* _CAT is an operator of two arguments that + REPMN, /* REPMN is a lexical token corresponding + to the {m,n} construct. REPMN never + appears in the compiled token vector. */ + + CAT, /* CAT is an operator of two arguments that matches the concatenation of its - arguments. _CAT is never returned by the + arguments. CAT is never returned by the lexical analyzer. */ - _OR, /* _OR is an operator of two arguments that + OR, /* OR is an operator of two arguments that matches either of its arguments. */ - _LPAREN, /* _LPAREN never appears in the parse tree, + ORTOP, /* OR at the toplevel in the parse tree. + This is used for a boyer-moore heuristic. */ + + LPAREN, /* LPAREN never appears in the parse tree, it is only a lexeme. */ - _RPAREN, /* _RPAREN never appears in the parse tree. */ + RPAREN, /* RPAREN never appears in the parse tree. */ - _SET /* _SET and (and any value greater) is a + CSET /* CSET and (and any value greater) is a terminal symbol that matches any of a class of characters. */ -} _token; - -#else /* ! __STDC__ */ - -typedef short _token; - -#define _END -1 -#define _EMPTY _NOTCHAR -#define _BACKREF (_EMPTY + 1) -#define _BEGLINE (_EMPTY + 2) -#define _ALLBEGLINE (_EMPTY + 3) -#define _ENDLINE (_EMPTY + 4) -#define _ALLENDLINE (_EMPTY + 5) -#define _BEGWORD (_EMPTY + 6) -#define _ENDWORD (_EMPTY + 7) -#define _LIMWORD (_EMPTY + 8) -#define _NOTLIMWORD (_EMPTY + 9) -#define _QMARK (_EMPTY + 10) -#define _STAR (_EMPTY + 11) -#define _PLUS (_EMPTY + 12) -#define _CAT (_EMPTY + 13) -#define _OR (_EMPTY + 14) -#define _LPAREN (_EMPTY + 15) -#define _RPAREN (_EMPTY + 16) -#define _SET (_EMPTY + 17) - -#endif /* ! __STDC__ */ +} token; -/* Sets are stored in an array in the compiled regexp; the index of the - array corresponding to a given set token is given by _SET_INDEX(t). */ -#define _SET_INDEX(t) ((t) - _SET) +/* Sets are stored in an array in the compiled dfa; the index of the + array corresponding to a given set token is given by SET_INDEX(t). */ +#define SET_INDEX(t) ((t) - CSET) /* Sometimes characters can only be matched depending on the surrounding context. Such context decisions depend on what the previous character @@ -239,36 +144,36 @@ typedef short _token; Word-constituent characters are those that satisfy isalnum(). - The macro _SUCCEEDS_IN_CONTEXT determines whether a a given constraint + The macro SUCCEEDS_IN_CONTEXT determines whether a a given constraint succeeds in a particular context. Prevn is true if the previous character was a newline, currn is true if the lookahead character is a newline. Prevl and currl similarly depend upon whether the previous and current characters are word-constituent letters. */ -#define _MATCHES_NEWLINE_CONTEXT(constraint, prevn, currn) \ - ((constraint) & 1 << ((prevn) ? 2 : 0) + ((currn) ? 1 : 0) + 4) -#define _MATCHES_LETTER_CONTEXT(constraint, prevl, currl) \ - ((constraint) & 1 << ((prevl) ? 2 : 0) + ((currl) ? 1 : 0)) -#define _SUCCEEDS_IN_CONTEXT(constraint, prevn, currn, prevl, currl) \ - (_MATCHES_NEWLINE_CONTEXT(constraint, prevn, currn) \ - && _MATCHES_LETTER_CONTEXT(constraint, prevl, currl)) +#define MATCHES_NEWLINE_CONTEXT(constraint, prevn, currn) \ + ((constraint) & 1 << (((prevn) ? 2 : 0) + ((currn) ? 1 : 0) + 4)) +#define MATCHES_LETTER_CONTEXT(constraint, prevl, currl) \ + ((constraint) & 1 << (((prevl) ? 2 : 0) + ((currl) ? 1 : 0))) +#define SUCCEEDS_IN_CONTEXT(constraint, prevn, currn, prevl, currl) \ + (MATCHES_NEWLINE_CONTEXT(constraint, prevn, currn) \ + && MATCHES_LETTER_CONTEXT(constraint, prevl, currl)) /* The following macros give information about what a constraint depends on. */ -#define _PREV_NEWLINE_DEPENDENT(constraint) \ +#define PREV_NEWLINE_DEPENDENT(constraint) \ (((constraint) & 0xc0) >> 2 != ((constraint) & 0x30)) -#define _PREV_LETTER_DEPENDENT(constraint) \ +#define PREV_LETTER_DEPENDENT(constraint) \ (((constraint) & 0x0c) >> 2 != ((constraint) & 0x03)) /* Tokens that match the empty string subject to some constraint actually work by applying that constraint to determine what may follow them, taking into account what has gone before. The following values are the constraints corresponding to the special tokens previously defined. */ -#define _NO_CONSTRAINT 0xff -#define _BEGLINE_CONSTRAINT 0xcf -#define _ENDLINE_CONSTRAINT 0xaf -#define _BEGWORD_CONSTRAINT 0xf2 -#define _ENDWORD_CONSTRAINT 0xf4 -#define _LIMWORD_CONSTRAINT 0xf6 -#define _NOTLIMWORD_CONSTRAINT 0xf9 +#define NO_CONSTRAINT 0xff +#define BEGLINE_CONSTRAINT 0xcf +#define ENDLINE_CONSTRAINT 0xaf +#define BEGWORD_CONSTRAINT 0xf2 +#define ENDWORD_CONSTRAINT 0xf4 +#define LIMWORD_CONSTRAINT 0xf6 +#define NOTLIMWORD_CONSTRAINT 0xf9 /* States of the recognizer correspond to sets of positions in the parse tree, together with the constraints under which they may be matched. @@ -278,44 +183,48 @@ typedef struct { unsigned index; /* Index into the parse array. */ unsigned constraint; /* Constraint for matching this position. */ -} _position; +} position; /* Sets of positions are stored as arrays. */ typedef struct { - _position *elems; /* Elements of this position set. */ + position *elems; /* Elements of this position set. */ int nelem; /* Number of elements in this set. */ -} _position_set; +} position_set; -/* A state of the regexp consists of a set of positions, some flags, +/* A state of the dfa consists of a set of positions, some flags, and the token value of the lowest-numbered position of the state that - contains an _END token. */ + contains an END token. */ typedef struct { int hash; /* Hash of the positions of this state. */ - _position_set elems; /* Positions this state could match. */ + position_set elems; /* Positions this state could match. */ char newline; /* True if previous state matched newline. */ char letter; /* True if previous state matched a letter. */ char backref; /* True if this state matches a \. */ unsigned char constraint; /* Constraint for this state to accept. */ - int first_end; /* Token value of the first _END in elems. */ -} _dfa_state; + int first_end; /* Token value of the first END in elems. */ +} dfa_state; -/* If an r.e. is at most MUST_MAX characters long, we look for a string which - must appear in it; whatever's found is dropped into the struct reg. */ - -#define MUST_MAX 50 +/* Element of a list of strings, at least one of which is known to + appear in any R.E. matching the DFA. */ +struct dfamust +{ + int exact; + char *must; + struct dfamust *next; +}; /* A compiled regular expression. */ -struct regexp +struct dfa { /* Stuff built by the scanner. */ - _charset *charsets; /* Array of character sets for _SET tokens. */ - int cindex; /* Index for adding new charsets. */ - int calloc; /* Number of charsets currently allocated. */ + charclass *charclasses; /* Array of character sets for CSET tokens. */ + int cindex; /* Index for adding new charclasses. */ + int calloc; /* Number of charclasses currently allocated. */ /* Stuff built by the parser. */ - _token *tokens; /* Postfix parse array. */ + token *tokens; /* Postfix parse array. */ int tindex; /* Index for adding new tokens. */ int talloc; /* Number of tokens currently allocated. */ int depth; /* Depth required of an evaluation stack @@ -323,15 +232,15 @@ struct regexp parse tree. */ int nleaves; /* Number of leaves on the parse tree. */ int nregexps; /* Count of parallel regexps being built - with regparse(). */ + with dfaparse(). */ /* Stuff owned by the state builder. */ - _dfa_state *states; /* States of the regexp. */ + dfa_state *states; /* States of the dfa. */ int sindex; /* Index for adding new states. */ int salloc; /* Number of states currently allocated. */ /* Stuff built by the structure analyzer. */ - _position_set *follows; /* Array of follow sets, indexed by position + position_set *follows; /* Array of follow sets, indexed by position index. The follow of a position is the set of positions containing characters that could conceivably follow a character @@ -361,7 +270,7 @@ struct regexp int **fails; /* Transition tables after failing to accept on a state that potentially could do so. */ int *success; /* Table of acceptance conditions used in - regexecute and computed in build_state. */ + dfaexec and computed in build_state. */ int *newlines; /* Transitions on newlines. The entry for a newline in any transition table is always -1 so we can count lines without wasting @@ -369,40 +278,41 @@ struct regexp newline is stored separately and handled as a special case. Newline is also used as a sentinel at the end of the buffer. */ - char must[MUST_MAX]; - int mustn; + struct dfamust *musts; /* List of strings, at least one of which + is known to appear in any r.e. matching + the dfa. */ }; -/* Some macros for user access to regexp internals. */ +/* Some macros for user access to dfa internals. */ /* ACCEPTING returns true if s could possibly be an accepting state of r. */ #define ACCEPTING(s, r) ((r).states[s].constraint) /* ACCEPTS_IN_CONTEXT returns true if the given state accepts in the specified context. */ -#define ACCEPTS_IN_CONTEXT(prevn, currn, prevl, currl, state, reg) \ - _SUCCEEDS_IN_CONTEXT((reg).states[state].constraint, \ +#define ACCEPTS_IN_CONTEXT(prevn, currn, prevl, currl, state, dfa) \ + SUCCEEDS_IN_CONTEXT((dfa).states[state].constraint, \ prevn, currn, prevl, currl) /* FIRST_MATCHING_REGEXP returns the index number of the first of parallel regexps that a given state could accept. Parallel regexps are numbered starting at 1. */ -#define FIRST_MATCHING_REGEXP(state, reg) (-(reg).states[state].first_end) +#define FIRST_MATCHING_REGEXP(state, dfa) (-(dfa).states[state].first_end) /* Entry points. */ #if __STDC__ -/* Regsyntax() takes two arguments; the first sets the syntax bits described +/* dfasyntax() takes two arguments; the first sets the syntax bits described earlier in this file, and the second sets the case-folding flag. */ -extern void regsyntax(int, int); +extern void dfasyntax(int, int); -/* Compile the given string of the given length into the given struct regexp. +/* Compile the given string of the given length into the given struct dfa. Final argument is a flag specifying whether to build a searching or an exact matcher. */ -extern void regcompile(const char *, size_t, struct regexp *, int); +extern void dfacomp(char *, size_t, struct dfa *, int); -/* Execute the given struct regexp on the buffer of characters. The +/* Execute the given struct dfa on the buffer of characters. The first char * points to the beginning, and the second points to the first character after the end of the buffer, which must be a writable place so a sentinel end-of-buffer marker can be stored there. The @@ -414,37 +324,37 @@ extern void regcompile(const char *, size_t, struct regexp *, int); order to verify backreferencing; otherwise the flag will be cleared. Returns NULL if no match is found, or a pointer to the first character after the first & shortest matching string in the buffer. */ -extern char *regexecute(struct regexp *, char *, char *, int, int *, int *); +extern char *dfaexec(struct dfa *, char *, char *, int, int *, int *); -/* Free the storage held by the components of a struct regexp. */ -extern void regfree(struct regexp *); +/* Free the storage held by the components of a struct dfa. */ +extern void dfafree(struct dfa *); /* Entry points for people who know what they're doing. */ -/* Initialize the components of a struct regexp. */ -extern void reginit(struct regexp *); +/* Initialize the components of a struct dfa. */ +extern void dfainit(struct dfa *); -/* Incrementally parse a string of given length into a struct regexp. */ -extern void regparse(const char *, size_t, struct regexp *); +/* Incrementally parse a string of given length into a struct dfa. */ +extern void dfaparse(char *, size_t, struct dfa *); /* Analyze a parsed regexp; second argument tells whether to build a searching or an exact matcher. */ -extern void reganalyze(struct regexp *, int); +extern void dfaanalyze(struct dfa *, int); /* Compute, for each possible character, the transitions out of a given state, storing them in an array of integers. */ -extern void regstate(int, struct regexp *, int []); +extern void dfastate(int, struct dfa *, int []); /* Error handling. */ -/* Regerror() is called by the regexp routines whenever an error occurs. It +/* dfaerror() is called by the regexp routines whenever an error occurs. It takes a single argument, a NUL-terminated string describing the error. - The default regerror() prints the error message to stderr and exits. - The user can provide a different regfree() if so desired. */ -extern void regerror(const char *); + The default dfaerror() prints the error message to stderr and exits. + The user can provide a different dfafree() if so desired. */ +extern void dfaerror(char *); #else /* ! __STDC__ */ -extern void regsyntax(), regcompile(), regfree(), reginit(), regparse(); -extern void reganalyze(), regstate(), regerror(); -extern char *regexecute(); +extern void dfasyntax(), dfacomp(), dfafree(), dfainit(), dfaparse(); +extern void dfaanalyze(), dfastate(), dfaerror(); +extern char *dfaexec(); #endif /* ! __STDC__ */ diff --git a/gnu/usr.bin/grep/getopt.c b/gnu/usr.bin/grep/getopt.c index 0661bdf762..a59a013398 100644 --- a/gnu/usr.bin/grep/getopt.c +++ b/gnu/usr.bin/grep/getopt.c @@ -3,12 +3,13 @@ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu before changing it! - Copyright (C) 1987, 88, 89, 90, 91, 1992 Free Software Foundation, Inc. + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 + Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -17,49 +18,67 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* AIX requires this to be the first thing in the file. */ +/* NOTE!!! AIX requires this to be the first thing in the file. + Do not put ANYTHING before it! */ +#if !defined (__GNUC__) && defined (_AIX) + #pragma alloca +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #ifdef __GNUC__ #define alloca __builtin_alloca #else /* not __GNUC__ */ -#if defined(sparc) && !defined(USG) && !defined(SVR4) && !defined(__svr4__) +#if defined (HAVE_ALLOCA_H) || (defined(sparc) && (defined(sun) || (!defined(USG) && !defined(SVR4) && !defined(__svr4__)))) #include #else -#ifdef _AIX - #pragma alloca -#else +#ifndef _AIX char *alloca (); #endif -#endif /* sparc */ +#endif /* alloca.h */ #endif /* not __GNUC__ */ -#ifdef LIBC -/* For when compiled as part of the GNU C library. */ -#include +#if !__STDC__ && !defined(const) && IN_GCC +#define const +#endif + +/* This tells Alpha OSF/1 not to define a getopt prototype in . */ +#ifndef _NO_PROTO +#define _NO_PROTO #endif #include +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ #undef alloca +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ #include -#include #else /* Not GNU C library. */ #define __alloca alloca #endif /* GNU C library. */ - -#ifndef __STDC__ -#define const -#endif - /* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a long-named option. Because this is not POSIX.2 compliant, it is - being phased out. */ -#define GETOPT_COMPAT + being phased out. */ +/* #define GETOPT_COMPAT */ /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user @@ -97,6 +116,7 @@ char *optarg = 0; Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ +/* XXX 1003.2 says this must be 1 before any call. */ int optind = 0; /* The next char to be scanned in the option-element @@ -113,6 +133,12 @@ static char *nextchar; int opterr = 1; +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, @@ -148,6 +174,10 @@ static enum } ordering; #ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ #include #define my_index strchr #define my_bcopy(src, dst, n) memcpy ((dst), (src), (n)) @@ -159,22 +189,23 @@ static enum char *getenv (); static char * -my_index (string, chr) - char *string; +my_index (str, chr) + const char *str; int chr; { - while (*string) + while (*str) { - if (*string == chr) - return string; - string++; + if (*str == chr) + return (char *) str; + str++; } return 0; } static void my_bcopy (from, to, size) - char *from, *to; + const char *from; + char *to; int size; { int i; @@ -210,10 +241,12 @@ exchange (argv) /* Interchange the two blocks of data in ARGV. */ - my_bcopy (&argv[first_nonopt], temp, nonopts_size); - my_bcopy (&argv[last_nonopt], &argv[first_nonopt], + my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size); + my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt], (optind - last_nonopt) * sizeof (char *)); - my_bcopy (temp, &argv[first_nonopt + optind - last_nonopt], nonopts_size); + my_bcopy ((char *) temp, + (char *) &argv[first_nonopt + optind - last_nonopt], + nonopts_size); /* Update records for the slots the non-options now occupy. */ @@ -457,7 +490,7 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) if (*s) { /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ + allow it to be used on enums. */ if (pfound->has_arg) optarg = s + 1; else @@ -489,7 +522,7 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) fprintf (stderr, "%s: option `%s' requires an argument\n", argv[0], argv[optind - 1]); nextchar += strlen (nextchar); - return '?'; + return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen (nextchar); @@ -505,7 +538,7 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. - Otherwise interpret it as a short option. */ + Otherwise interpret it as a short option. */ if (!long_only || argv[optind][1] == '-' #ifdef GETOPT_COMPAT || argv[optind][0] == '+' @@ -523,7 +556,7 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) fprintf (stderr, "%s: unrecognized option `%c%s'\n", argv[0], argv[optind][0], nextchar); } - nextchar += strlen (nextchar); + nextchar = (char *) ""; optind++; return '?'; } @@ -537,18 +570,24 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') - optind++; + ++optind; if (temp == NULL || c == ':') { if (opterr) { +#if 0 if (c < 040 || c >= 0177) fprintf (stderr, "%s: unrecognized option, character code 0%o\n", argv[0], c); else fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); +#endif } + optopt = c; return '?'; } if (temp[1] == ':') @@ -568,7 +607,7 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) else { /* This is an option that requires an argument. */ - if (*nextchar != 0) + if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, @@ -578,9 +617,21 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) else if (optind == argc) { if (opterr) - fprintf (stderr, "%s: option `-%c' requires an argument\n", - argv[0], c); - c = '?'; + { +#if 0 + fprintf (stderr, "%s: option `-%c' requires an argument\n", + argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; } else /* We already incremented `optind' once; @@ -604,6 +655,8 @@ getopt (argc, argv, optstring) (int *) 0, 0); } + +#endif /* _LIBC or not __GNU_LIBRARY__. */ #ifdef TEST diff --git a/gnu/usr.bin/grep/getopt.h b/gnu/usr.bin/grep/getopt.h index f64de31161..45541f5ac0 100644 --- a/gnu/usr.bin/grep/getopt.h +++ b/gnu/usr.bin/grep/getopt.h @@ -1,10 +1,10 @@ /* Declarations for getopt. - Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. + Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -13,11 +13,15 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _GETOPT_H #define _GETOPT_H 1 +#ifdef __cplusplus +extern "C" { +#endif + /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. @@ -45,6 +49,10 @@ extern int optind; extern int opterr; +/* Set to an option character which was unrecognized. */ + +extern int optopt; + /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of `struct option' terminated by an element containing a name which is @@ -82,15 +90,19 @@ struct option /* Names for the values of the `has_arg' field of `struct option'. */ -enum _argtype -{ - no_argument, - required_argument, - optional_argument -}; +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 #if __STDC__ +#if defined(__GNU_LIBRARY__) +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ +extern int getopt (); +#endif /* not __GNU_LIBRARY__ */ extern int getopt_long (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); extern int getopt_long_only (int argc, char *const *argv, @@ -110,4 +122,8 @@ extern int getopt_long_only (); extern int _getopt_internal (); #endif /* not __STDC__ */ +#ifdef __cplusplus +} +#endif + #endif /* _GETOPT_H */ diff --git a/gnu/usr.bin/grep/getpagesize.h b/gnu/usr.bin/grep/getpagesize.h new file mode 100644 index 0000000000..e6bd561067 --- /dev/null +++ b/gnu/usr.bin/grep/getpagesize.h @@ -0,0 +1,42 @@ +#ifdef BSD +#ifndef BSD4_1 +#define HAVE_GETPAGESIZE +#endif +#endif + +#ifndef HAVE_GETPAGESIZE + +#ifdef VMS +#define getpagesize() 512 +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef _SC_PAGESIZE +#define getpagesize() sysconf(_SC_PAGESIZE) +#else + +#ifdef HAVE_SYS_PARAM_H +#include + +#ifdef EXEC_PAGESIZE +#define getpagesize() EXEC_PAGESIZE +#else +#ifdef NBPG +#define getpagesize() NBPG * CLSIZE +#ifndef CLSIZE +#define CLSIZE 1 +#endif /* no CLSIZE */ +#else /* no NBPG */ +#define getpagesize() NBPC +#endif /* no NBPG */ +#endif /* no EXEC_PAGESIZE */ +#else /* !HAVE_SYS_PARAM_H */ +#define getpagesize() 8192 /* punt totally */ +#endif /* !HAVE_SYS_PARAM_H */ +#endif /* no _SC_PAGESIZE */ + +#endif /* not HAVE_GETPAGESIZE */ + diff --git a/gnu/usr.bin/grep/grep.1 b/gnu/usr.bin/grep/grep.1 index 57093b6403..27c6b0e419 100644 --- a/gnu/usr.bin/grep/grep.1 +++ b/gnu/usr.bin/grep/grep.1 @@ -1,234 +1,375 @@ -.TH GREP 1 "1988 December 13" "GNU Project" \" -*- nroff -*- -.UC 4 +.TH GREP 1 "1992 September 10" "GNU Project" .SH NAME -grep, egrep \- print lines matching a regular expression -.SH SYNOPSIS +grep, egrep, fgrep \- print lines matching a pattern +.SH SYNOPOSIS .B grep [ -.B \-CVbchilnsvwx -] [ -.BI \- num -] [ -.B \-AB -.I num -] [ [ +.BR \- [[ AB "] ]\c" +.I "num" +] +[ +.BR \- [ CEFGVBchilnsvwx ] +] +[ .B \-e ] -.I expr +.I pattern | -.B \-f -.I file +.BI \-f file ] [ -.I "files ..." +.I files... ] .SH DESCRIPTION -.I Grep -searches the files listed in the arguments (or standard -input if no files are given) for all lines that contain a match for -the given -.IR expr . -If any lines match, they are printed. -.PP -Also, if any matches were found, -.I grep -exits with a status of 0, but if no matches were found it exits -with a status of 1. This is useful for building shell scripts that -use -.I grep -as a condition for, for example, the -.I if -statement. -.PP -When invoked as -.I egrep -the syntax of the -.I expr -is slightly different; See below. -.br -.SH "REGULAR EXPRESSIONS" -.RS 2.5i -.ta 1i 2i -.sp -.ti -2.0i -(grep) (egrep) (explanation) -.sp -.ti -2.0i -\fIc\fP \fIc\fP a single (non-meta) character matches itself. -.sp -.ti -2.0i -\&. . matches any single character except newline. -.sp -.ti -2.0i -\\? ? postfix operator; preceeding item is optional. -.sp -.ti -2.0i -\(** \(** postfix operator; preceeding item 0 or -more times. -.sp -.ti -2.0i -\\+ + postfix operator; preceeding item 1 or -more times. -.sp -.ti -2.0i -\\| | infix operator; matches either -argument. -.sp -.ti -2.0i -^ ^ matches the empty string at the beginning of a line. -.sp -.ti -2.0i -$ $ matches the empty string at the end of a line. -.sp -.ti -2.0i -\\< \\< matches the empty string at the beginning of a word. -.sp -.ti -2.0i -\\> \\> matches the empty string at the end of a word. -.sp -.ti -2.0i -[\fIchars\fP] [\fIchars\fP] match any character in the given class; if the -first character after [ is ^, match any character -not in the given class; a range of characters may -be specified by \fIfirst\-last\fP; for example, \\W -(below) is equivalent to the class [^A\-Za\-z0\-9] -.sp -.ti -2.0i -\\( \\) ( ) parentheses are used to override operator precedence. -.sp -.ti -2.0i -\\\fIdigit\fP \\\fIdigit\fP \\\fIn\fP matches a repeat of the text -matched earlier in the regexp by the subexpression inside the nth -opening parenthesis. -.sp -.ti -2.0i -\\ \\ any special character may be preceded -by a backslash to match it literally. -.sp -.ti -2.0i -(the following are for compatibility with GNU Emacs) -.sp -.ti -2.0i -\\b \\b matches the empty string at the edge of a word. -.sp -.ti -2.0i -\\B \\B matches the empty string if not at the edge of a word. -.sp -.ti -2.0i -\\w \\w matches word-constituent characters (letters & digits). -.sp -.ti -2.0i -\\W \\W matches characters that are not word-constituent. -.RE -.PP -Operator precedence is (highest to lowest) ?, \(**, and +, concatenation, -and finally |. All other constructs are syntactically identical to -normal characters. For the truly interested, the file dfa.c describes -(and implements) the exact grammar understood by the parser. -.SH OPTIONS +.PP +.B Grep +searches the named input +.I files +(or standard input if no files are named, or +the file name +.B \- +is given) +for lines containing a match to the given +.IR pattern . +By default, +.B grep +prints the matching lines. +.PP +There are three major variants of +.BR grep , +controlled by the following options. +.PD 0 +.TP +.B \-G +Interpret +.I pattern +as a basic regular expression (see below). This is the default. +.TP +.B \-E +Interpret +.I pattern +as an extended regular expression (see below). +.TP +.B \-F +Interpret +.I pattern +as a list of fixed strings, separated by newlines, +any of which is to be matched. +.LP +In addition, two variant programs +.B egrep +and +.B fgrep +are available. +.B Egrep +is similiar (but not identical) to +.BR "grep\ \-E" , +and is compatible with the historical Unix +.BR egrep . +.B Fgrep +is the same as +.BR "grep\ \-F" . +.PD +.LP +All variants of +.B grep +understand the following options: +.PD 0 +.TP +.BI \- num +Matches will be printed with +.I num +lines of leading and trailing context. However, +.B grep +will never print any given line more than once. .TP .BI \-A " num" -print lines of context after every matching line +Print +.I num +lines of trailing context after matching lines. .TP .BI \-B " num" -print +Print .I num -lines of context before every matching line +lines of leading context before matching lines. .TP .B \-C -print 2 lines of context on each side of every match -.TP -.BI \- num -print -.I num -lines of context on each side of every match +Equivalent to +.BR \-2 . .TP .B \-V -print the version number on the diagnostic output +Print the version number of +.B grep +to standard error. This version number should +be included in all bug reports (see below). .TP .B \-b -print every match preceded by its byte offset +Print the byte offset within the input file before +each line of output. .TP .B \-c -print a total count of matching lines only +Suppress normal output; instead print a count of +matching lines for each input file. +With the +.B \-v +option (see below), count non-matching lines. .TP -.BI \-e " expr" -search for -.IR expr ; -useful if -.I expr -begins with \- +.BI \-e " pattern" +Use +.I pattern +as the pattern; useful to protect patterns beginning with +.BR \- . .TP .BI \-f " file" -search for the expression contained in -.I file +Obtain the pattern from +.IR file . .TP .B \-h -don't display filenames on matches +Suppress the prefixing of filenames on output +when multiple files are searched. .TP .B \-i -ignore case difference when comparing strings +Ignore case distinctions in both the +.I pattern +and the input files. +.TP +.B \-L +Suppress normal output; instead print the name +of each input file from which no output would +normally have been printed. .TP .B \-l -list files containing matches only +Suppress normal output; instead print +the name of each input file from which output +would normally have been printed. .TP .B \-n -print each match preceded by its line number +Prefix each line of output with the line number +within its input file. +.TP +.B \-q +Quiet; suppress normal output. .TP .B \-s -run silently producing no output except error messages +Suppress error messages about nonexistent or unreadable files. .TP .B \-v -print only lines that contain no matches for the +Invert the sense of matching, to select non-matching lines. .TP .B \-w -print only lines where the match is a complete word +Select only those lines containing matches that form whole words. +The test is that the matching substring must either be at the +beginning of the line, or preceded by a non-word constituent +character. Similarly, it must be either at the end of the line +or followed by a non-word constituent character. Word-constituent +characters are letters, digits, and the underscore. .TP .B \-x -print only lines where the match is a whole line -.SH "SEE ALSO" -emacs(1), ed(1), sh(1), -.I "GNU Emacs Manual" -.SH INCOMPATIBILITIES -The following incompatibilities with UNIX -.I grep -exist: -.PP -.RS 0.5i -The context-dependent meaning of \(** is not quite the same (grep only). +Select only those matches that exactly match the whole line. +.PD +.SH "REGULAR EXPRESSIONS" .PP -.B \-b -prints a byte offset instead of a block offset. +A regular expression is a pattern that describes a set of strings. +Regular expressions are constructed analagously to arithmetic +expressions, by using various operators to combine smaller expressions. .PP -The {\fIm,n\fP} construct of System V grep is not implemented. +.B Grep +understands two different versions of regular expression syntax: +``basic'' and ``extended.'' In +.RB "GNU\ " grep , +there is no difference in available functionality using either syntax. +In other implementations, basic regular expressions are less powerful. +The following description applies to extended regular expressions; +differences for basic regular expressions are summarized afterwards. .PP -.SH BUGS -GNU \fIe?grep\fP has been thoroughly debugged and tested over a period -of several years; we think it's a reliable beast or we wouldn't -distribute it. If by some fluke of the universe you discover a bug, -send a detailed description (including options, regular expressions, -and a copy of an input file that can reproduce it) to mike@ai.mit.edu. +The fundamental building blocks are the regular expressions that match +a single character. Most characters, including all letters and digits, +are regular expressions that match themselves. Any metacharacter with +special meaning may be quoted by preceding it with a backslash. +.PP +A list of characters enclosed by +.B [ +and +.B ] +matches any single +character in that list; if the first character of the list +is the caret +.B ^ +then it matches any character +.I not +in the list. +For example, the regular expression +.B [0123456789] +matches any single digit. A range of ASCII characters +may be specified by giving the first and last characters, separated +by a hyphen. +Finally, certain named classes of characters are predefined. +Their names are self explanatory, and they are +.BR [:alnum:] , +.BR [:alpha:] , +.BR [:cntrl:] , +.BR [:digit:] , +.BR [:graph:] , +.BR [:lower:] , +.BR [:print:] , +.BR [:punct:] , +.BR [:space:] , +.BR [:upper:] , +and +.BR [:xdigit:]. +For example, +.B [[:alnum:]] +means +.BR [0-9A-Za-z] , +except the latter form is dependent upon the ASCII character encoding, +whereas the former is portable. +(Note that the brackets in these class names are part of the symbolic +names, and must be included in addition to the brackets delimiting +the bracket list.) Most metacharacters lose their special meaning +inside lists. To include a literal +.B ] +place it first in the list. Similarly, to include a literal +.B ^ +place it anywhere but first. Finally, to include a literal +.B \- +place it last. +.PP +The period +.B . +matches any single character. +The symbol +.B \ew +is a synonym for +.B [[:alnum:]] +and +.B \eW +is a synonym for +.BR [^[:alnum]] . +.PP +The caret +.B ^ +and the dollar sign +.B $ +are metacharacters that respectively match the empty string at the +beginning and end of a line. +The symbols +.B \e< +and +.B \e> +respectively match the empty string at the beginning and end of a word. +The symbol +.B \eb +matches the empty string at the edge of a word, +and +.B \eB +matches the empty string provided it's +.I not +at the edge of a word. +.PP +A regular expression matching a single character may be followed +by one of several repetition operators: +.PD 0 +.TP +.B ? +The preceding item is optional and matched at most once. +.TP +.B * +The preceding item will be matched zero or more times. +.TP +.B + +The preceding item will be matched one or more times. +.TP +.BI { n } +The preceding item is matched exactly +.I n +times. +.TP +.BI { n ,} +The preceding item is matched +.I n +or more times. +.TP +.BI {, m } +The preceding item is optional and is matched at most +.I m +times. +.TP +.BI { n , m } +The preceding item is matched at least +.I n +times, but not more than +.I m +times. +.PD .PP -.SH AUTHORS -Mike Haertel wrote the deterministic regexp code and the bulk -of the program. +Two regular expressions may be concatenated; the resulting +regular expression matches any string formed by concatenating +two substrings that respectively match the concatenated +subexpressions. .PP -James A. Woods is responsible for the hybridized search strategy -of using Boyer-Moore-Gosper fixed-string search as a filter -before calling the general regexp matcher. +Two regular expressions may be joined by the infix operator +.BR | ; +the resulting regular expression matches any string matching +either subexpression. .PP -Arthur David Olson contributed code that finds fixed strings for -the aforementioned BMG search for a large class of regexps. +Repetition takes precedence over concatenation, which in turn +takes precedence over alternation. A whole subexpression may be +enclosed in parentheses to override these precedence rules. .PP -Richard Stallman wrote the backtracking regexp matcher that is used -for \\\fIdigit\fP backreferences, as well as the GNU getopt. The -backtracking matcher was originally written for GNU Emacs. +The backreference +.BI \e n\c +\&, where +.I n +is a single digit, matches the substring +previously matched by the +.IR n th +parenthesized subexpression of the regular expression. .PP -D. A. Gwyn wrote the C alloca emulation that is provided so -System V machines can run this program. (Alloca is used only -by RMS' backtracking matcher, and then only rarely, so there -is no loss if your machine doesn't have a "real" alloca.) +In basic regular expressions the metacharacters +.BR ? , +.BR + , +.BR { , +.BR | , +.BR ( , +and +.BR ) +lose their special meaning; instead use the backslashed +versions +.BR \e? , +.BR \e+ , +.BR \e{ , +.BR \e| , +.BR \e( , +and +.BR \e) . .PP -Scott Anderson and Henry Spencer designed the regression tests -used in the "regress" script. +In +.B egrep +the metacharacter +.B { +loses its special meaning; instead use +.BR \e{ . +.SH DIAGNOSTICS +.PP +Normally, exit status is 0 if matches were found, +and 1 if no matches were found. (The +.B \-v +option inverts the sense of the exit status.) +Exit status is 2 if there were syntax errors +in the pattern, inaccessible input files, or +other system errors. +.SH BUGS +.PP +Email bug reports to +.BR bug-gnu-utils@prep.ai.mit.edu . +Be sure to include the word ``grep'' somewhere in the ``Subject:'' field. +.PP +Large repetition counts in the +.BI { m , n } +construct may cause grep to use lots of memory. +In addition, +certain other obscure regular expressions require exponential time +and space, and may cause +.B grep +to run out of memory. .PP -Paul Placeway wrote the original version of this manual page. +Backreferences are very slow, and may require exponential time. diff --git a/gnu/usr.bin/grep/grep.c b/gnu/usr.bin/grep/grep.c index 1c45c45fa4..07872a1666 100644 --- a/gnu/usr.bin/grep/grep.c +++ b/gnu/usr.bin/grep/grep.c @@ -1,5 +1,5 @@ -/* grep - print lines matching an extended regular expression - Copyright (C) 1988 Free Software Foundation, Inc. +/* grep.c - main driver file for grep. + Copyright (C) 1992 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -13,569 +13,616 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -/* Written June, 1988 by Mike Haertel - BMG speedups added July, 1988 by James A. Woods and Arthur David Olson */ - + Written July 1992 by Mike Haertel. */ + +#include #include -#if defined(USG) || defined(STDC_HEADERS) -#include -#ifndef bcopy -#define bcopy(s,d,n) memcpy((d),(s),(n)) +#ifndef errno +extern int errno; +#endif + +#ifdef STDC_HEADERS +#include +#else +#include +extern char *malloc(), *realloc(); +extern void free(); #endif -#ifndef index -#define index strchr + +#if defined(STDC_HEADERS) || defined(HAVE_STRING_H) +#include +#ifdef NEED_MEMORY_H +#include #endif #else #include +#ifdef __STDC__ +extern void *memchr(); +#else +extern char *memchr(); +#endif +#define strrchr rindex #endif #ifdef HAVE_UNISTD_H +#include +#include #include +#else +#define O_RDONLY 0 +extern int open(), read(), close(); #endif -#ifndef STDC_HEADERS -extern char *getenv(); -#endif -extern int errno; +#include "getpagesize.h" +#include "grep.h" -extern char *sys_errlist[]; +#undef MAX +#define MAX(A,B) ((A) > (B) ? (A) : (B)) -#include "dfa.h" -#include "regex.h" -#include "getopt.h" +/* Provide missing ANSI features if necessary. */ -/* Used by -w */ -#define WCHAR(C) (ISALNUM(C) || (C) == '_') +#ifndef HAVE_STRERROR +extern int sys_nerr; +extern char *sys_errlist[]; +#define strerror(E) ((E) < sys_nerr ? sys_errlist[(E)] : "bogus error number") +#endif -#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#ifndef HAVE_MEMCHR +#ifdef __STDC__ +#define VOID void +#else +#define VOID char +#endif +VOID * +memchr(vp, c, n) + VOID *vp; + int c; + size_t n; +{ + unsigned char *p; -/* Exit status codes. */ -#define MATCHES_FOUND 0 /* Exit 0 if no errors and matches found. */ -#define NO_MATCHES_FOUND 1 /* Exit 1 if no matches were found. */ -#define ERROR 2 /* Exit 2 if some error occurred. */ + for (p = (unsigned char *) vp; n--; ++p) + if (*p == c) + return (VOID *) p; + return 0; +} +#endif + +/* Define flags declared in grep.h. */ +char *matcher; +int match_icase; +int match_words; +int match_lines; -/* Error is set true if something awful happened. */ -static int error; +/* Functions we'll use to search. */ +static void (*compile)(); +static char *(*execute)(); -/* The program name for error messages. */ +/* For error messages. */ static char *prog; +static char *filename; +static int errseen; -/* We do all our own buffering by hand for efficiency. */ -static char *buffer; /* The buffer itself, grown as needed. */ -static bufbytes; /* Number of bytes in the buffer. */ -static size_t bufalloc; /* Number of bytes allocated to the buffer. */ -static bufprev; /* Number of bytes that have been forgotten. - This is used to get byte offsets from the - beginning of the file. */ -static bufread; /* Number of bytes to get with each read(). */ - +/* Print a message and possibly an error string. Remember + that something awful happened. */ static void -initialize_buffer() +error(mesg, errnum) +#ifdef __STDC__ + const +#endif + char *mesg; + int errnum; +{ + if (errnum) + fprintf(stderr, "%s: %s: %s\n", prog, mesg, strerror(errnum)); + else + fprintf(stderr, "%s: %s\n", prog, mesg); + errseen = 1; +} + +/* Like error(), but die horribly after printing. */ +void +fatal(mesg, errnum) +#ifdef __STDC__ + const +#endif + char *mesg; + int errnum; { - bufread = 8192; - bufalloc = bufread + bufread / 2; - buffer = malloc(bufalloc); - if (! buffer) + error(mesg, errnum); + exit(2); +} + +/* Interface to handle errors and fix library lossage. */ +char * +xmalloc(size) + size_t size; +{ + char *result; + + result = malloc(size); + if (size && !result) + fatal("memory exhausted", 0); + return result; +} + +/* Interface to handle errors and fix some library lossage. */ +char * +xrealloc(ptr, size) + char *ptr; + size_t size; +{ + char *result; + + if (ptr) + result = realloc(ptr, size); + else + result = malloc(size); + if (size && !result) + fatal("memory exhausted", 0); + return result; +} + +#if !defined(HAVE_VALLOC) +#define valloc malloc +#else +#ifdef __STDC__ +extern void *valloc(size_t); +#else +extern char *valloc(); +#endif +#endif + +/* Hairy buffering mechanism for grep. The intent is to keep + all reads aligned on a page boundary and multiples of the + page size. */ + +static char *buffer; /* Base of buffer. */ +static size_t bufsalloc; /* Allocated size of buffer save region. */ +static size_t bufalloc; /* Total buffer size. */ +static int bufdesc; /* File descriptor. */ +static char *bufbeg; /* Beginning of user-visible stuff. */ +static char *buflim; /* Limit of user-visible stuff. */ + +#if defined(HAVE_WORKING_MMAP) +#include +#include +#include + +static int bufmapped; /* True for ordinary files. */ +static struct stat bufstat; /* From fstat(). */ +static off_t bufoffset; /* What read() normally remembers. */ +#endif + +/* Reset the buffer for a new file. Initialize + on the first time through. */ +void +reset(fd) + int fd; +{ + static int initialized; + + if (!initialized) { - fprintf(stderr, "%s: Memory exhausted (%s)\n", prog, - sys_errlist[errno]); - exit(ERROR); + initialized = 1; +#ifndef BUFSALLOC + bufsalloc = MAX(8192, getpagesize()); +#else + bufsalloc = BUFSALLOC; +#endif + bufalloc = 5 * bufsalloc; + /* The 1 byte of overflow is a kludge for dfaexec(), which + inserts a sentinel newline at the end of the buffer + being searched. There's gotta be a better way... */ + buffer = valloc(bufalloc + 1); + if (!buffer) + fatal("memory exhausted", 0); + bufbeg = buffer; + buflim = buffer; } + bufdesc = fd; +#if defined(HAVE_WORKING_MMAP) + if (fstat(fd, &bufstat) < 0 || !S_ISREG(bufstat.st_mode)) + bufmapped = 0; + else + { + bufmapped = 1; + bufoffset = lseek(fd, 0, 1); + } +#endif } -/* The current input file. */ -static fd; -static char *filename; -static eof; - -/* Fill the buffer retaining the last n bytes at the beginning of the - newly filled buffer (for backward context). Returns the number of new - bytes read from disk. */ +/* Read new stuff into the buffer, saving the specified + amount of old stuff. When we're done, 'bufbeg' points + to the beginning of the buffer contents, and 'buflim' + points just after the end. Return count of new stuff. */ static int -fill_buffer_retaining(n) - int n; +fillbuf(save) + size_t save; { - char *p, *q; - int i; + char *nbuffer, *dp, *sp; + int cc; +#if defined(HAVE_WORKING_MMAP) + caddr_t maddr; +#endif + static int pagesize; + + if (pagesize == 0 && (pagesize = getpagesize()) == 0) + abort(); - /* See if we need to grow the buffer. */ - if (bufalloc - n <= bufread) + if (save > bufsalloc) { - while (bufalloc - n <= bufread) + while (save > bufsalloc) + bufsalloc *= 2; + bufalloc = 5 * bufsalloc; + nbuffer = valloc(bufalloc + 1); + if (!nbuffer) + fatal("memory exhausted", 0); + } + else + nbuffer = buffer; + + sp = buflim - save; + dp = nbuffer + bufsalloc - save; + bufbeg = dp; + while (save--) + *dp++ = *sp++; + + /* We may have allocated a new, larger buffer. Since + there is no portable vfree(), we just have to forget + about the old one. Sorry. */ + buffer = nbuffer; + +#if defined(HAVE_WORKING_MMAP) + if (bufmapped && bufoffset % pagesize == 0 + && bufstat.st_size - bufoffset >= bufalloc - bufsalloc) + { + maddr = buffer + bufsalloc; + maddr = mmap(maddr, bufalloc - bufsalloc, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED, bufdesc, bufoffset); + if (maddr == (caddr_t) -1) { - bufalloc *= 2; - bufread *= 2; + fprintf(stderr, "%s: warning: %s: %s\n", filename, + strerror(errno)); + goto tryread; } - buffer = realloc(buffer, bufalloc); - if (! buffer) +#if 0 + /* You might thing this (or MADV_WILLNEED) would help, + but it doesn't, at least not on a Sun running 4.1. + In fact, it actually slows us down about 30%! */ + madvise(maddr, bufalloc - bufsalloc, MADV_SEQUENTIAL); +#endif + cc = bufalloc - bufsalloc; + bufoffset += cc; + } + else + { + tryread: + /* We come here when we're not going to use mmap() any more. + Note that we need to synchronize the file offset the + first time through. */ + if (bufmapped) { - fprintf(stderr, "%s: Memory exhausted (%s)\n", prog, - sys_errlist[errno]); - exit(ERROR); + bufmapped = 0; + lseek(bufdesc, bufoffset, 0); } + cc = read(bufdesc, buffer + bufsalloc, bufalloc - bufsalloc); } +#else + cc = read(bufdesc, buffer + bufsalloc, bufalloc - bufsalloc); +#endif + if (cc > 0) + buflim = buffer + bufsalloc + cc; + else + buflim = buffer + bufsalloc; + return cc; +} - bufprev += bufbytes - n; +/* Flags controlling the style of output. */ +static int out_quiet; /* Suppress all normal output. */ +static int out_invert; /* Print nonmatching stuff. */ +static int out_file; /* Print filenames. */ +static int out_line; /* Print line numbers. */ +static int out_byte; /* Print byte offsets. */ +static int out_before; /* Lines of leading context. */ +static int out_after; /* Lines of trailing context. */ + +/* Internal variables to keep track of byte count, context, etc. */ +static size_t totalcc; /* Total character count before bufbeg. */ +static char *lastnl; /* Pointer after last newline counted. */ +static char *lastout; /* Pointer after last character output; + NULL if no character has been output + or if it's conceptually before bufbeg. */ +static size_t totalnl; /* Total newline count before lastnl. */ +static int pending; /* Pending lines of output. */ - /* Shift stuff down. */ - for (i = n, p = buffer, q = p + bufbytes - n; i--; ) - *p++ = *q++; - bufbytes = n; +static void +nlscan(lim) + char *lim; +{ + char *beg; - if (eof) - return 0; + for (beg = lastnl; beg < lim; ++beg) + if (*beg == '\n') + ++totalnl; + lastnl = beg; +} - /* Read in new stuff. */ - i = read(fd, buffer + bufbytes, bufread); - if (i < 0) +static void +prline(beg, lim, sep) + char *beg; + char *lim; + char sep; +{ + if (out_file) + printf("%s%c", filename, sep); + if (out_line) { - fprintf(stderr, "%s: read on %s failed (%s)\n", prog, - filename ? filename : "", sys_errlist[errno]); - error = 1; + nlscan(beg); + printf("%d%c", ++totalnl, sep); + lastnl = lim; } + if (out_byte) + printf("%lu%c", totalcc + (beg - bufbeg), sep); + fwrite(beg, 1, lim - beg, stdout); + if (ferror(stdout)) + error("writing output", errno); + lastout = lim; +} - /* Kludge to pretend every nonempty file ends with a newline. */ - if (i == 0 && bufbytes > 0 && buffer[bufbytes - 1] != '\n') +/* Print pending lines of trailing context prior to LIM. */ +static void +prpending(lim) + char *lim; +{ + char *nl; + + if (!lastout) + lastout = bufbeg; + while (pending > 0 && lastout < lim) { - eof = i = 1; - buffer[bufbytes] = '\n'; + --pending; + if ((nl = memchr(lastout, '\n', lim - lastout)) != 0) + ++nl; + else + nl = lim; + prline(lastout, nl, '-'); } - - bufbytes += i; - return i; } - -/* Various flags set according to the argument switches. */ -static trailing_context; /* Lines of context to show after matches. */ -static leading_context; /* Lines of context to show before matches. */ -static byte_count; /* Precede output lines the byte count of the - first character on the line. */ -static no_filenames; /* Do not display filenames. */ -static line_numbers; /* Precede output lines with line numbers. */ -static silent; /* Produce no output at all. This switch - is bogus, ever hear of /dev/null? */ -static int whole_word; /* Match only whole words. Note that if - backreferences are used this depends on - the regex routines getting leftmost-longest - right, which they don't right now if | - is also used. */ -static int whole_line; /* Match only whole lines. Backreference - caveat applies here too. */ -static nonmatching_lines; /* Print lines that don't match the regexp. */ - -static bmgexec; /* Invoke Boyer-Moore-Gosper routines */ - -/* The compiled regular expression lives here. */ -static struct regexp reg; - -/* The compiled regular expression for the backtracking matcher lives here. */ -static struct re_pattern_buffer regex; - -/* Pointer in the buffer after the last character printed. */ -static char *printed_limit; - -/* True when printed_limit has been artifically advanced without printing - anything. */ -static int printed_limit_fake; - -/* Print a line at the given line number, returning the number of - characters actually printed. Matching is true if the line is to - be considered a "matching line". This is only meaningful if - surrounding context is turned on. */ -static int -print_line(p, number, matching) - char *p; - int number; - int matching; + +/* Print the lines between BEG and LIM. Deal with context crap. + If NLINESP is non-null, store a count of lines between BEG and LIM. */ +static void +prtext(beg, lim, nlinesp) + char *beg; + char *lim; + int *nlinesp; { - int count = 0; + static int used; /* avoid printing "--" before any output */ + char *bp, *p, *nl; + int i, n; + + if (!out_quiet && pending > 0) + prpending(beg); - if (silent) + p = beg; + + if (!out_quiet) { - do - ++count; - while (*p++ != '\n'); - printed_limit_fake = 0; - printed_limit = p; - return count; + /* Deal with leading context crap. */ + + bp = lastout ? lastout : bufbeg; + for (i = 0; i < out_before; ++i) + if (p > bp) + do + --p; + while (p > bp && p[-1] != '\n'); + + /* We only print the "--" separator if our output is + discontiguous from the last output in the file. */ + if ((out_before || out_after) && used && p != lastout) + puts("--"); + + while (p < beg) + { + nl = memchr(p, '\n', beg - p); + prline(p, nl + 1, '-'); + p = nl + 1; + } } - if (filename && !no_filenames) - printf("%s%c", filename, matching ? ':' : '-'); - if (byte_count) - printf("%d%c", p - buffer + bufprev, matching ? ':' : '-'); - if (line_numbers) - printf("%d%c", number, matching ? ':' : '-'); - do + if (nlinesp) { - ++count; - putchar(*p); + /* Caller wants a line count. */ + for (n = 0; p < lim; ++n) + { + if ((nl = memchr(p, '\n', lim - p)) != 0) + ++nl; + else + nl = lim; + if (!out_quiet) + prline(p, nl, ':'); + p = nl; + } + *nlinesp = n; } - while (*p++ != '\n'); - printed_limit_fake = 0; - printed_limit = p; - return count; + else + if (!out_quiet) + prline(beg, lim, ':'); + + pending = out_after; + used = 1; } -/* Print matching or nonmatching lines from the current file. Returns a - count of matching or nonmatching lines. */ +/* Scan the specified portion of the buffer, matching lines (or + between matching lines if OUT_INVERT is true). Return a count of + lines printed. */ static int -grep() +grepbuf(beg, lim) + char *beg; + char *lim; { - int retain = 0; /* Number of bytes to retain on next call - to fill_buffer_retaining(). */ - char *search_limit; /* Pointer to the character after the last - newline in the buffer. */ - char saved_char; /* Character after the last newline. */ - char *resume; /* Pointer to where to resume search. */ - int resume_index = 0; /* Count of characters to ignore after - refilling the buffer. */ - int line_count = 1; /* Line number. */ - int try_backref; /* Set to true if we need to verify the - match with a backtracking matcher. */ - int initial_line_count; /* Line count at beginning of last search. */ - char *match; /* Pointer to the first character after the - string matching the regexp. */ - int match_count = 0; /* Count of matching lines. */ - char *matching_line; /* Pointer to first character of the matching - line, or of the first line of context to - print if context is turned on. */ - char *real_matching_line; /* Pointer to the first character of the - real matching line. */ - char *next_line; /* Pointer to first character of the line - following the matching line. */ - char *last_match_limit; /* Pointer after last matched line. */ - int pending_lines = 0; /* Lines of context left over from last match - that we have to print. */ - static first_match = 1; /* True when nothing has been printed. */ - int i; - char *tmp; - char *execute(); + int nlines, n; + register char *p, *b; + char *endp; - printed_limit_fake = 0; - - while (fill_buffer_retaining(retain) > 0) + nlines = 0; + p = beg; + while ((b = (*execute)(p, lim - p, &endp)) != 0) { - /* Find the last newline in the buffer. */ - search_limit = buffer + bufbytes; - while (search_limit > buffer && search_limit[-1] != '\n') - --search_limit; - if (search_limit == buffer) + /* Avoid matching the empty line at the end of the buffer. */ + if (b == lim && ((b > beg && b[-1] == '\n') || b == beg)) + break; + if (!out_invert) { - retain = bufbytes; - continue; + prtext(b, endp, (int *) 0); + nlines += 1; } + else if (p < b) + { + prtext(p, b, &n); + nlines += n; + } + p = endp; + } + if (out_invert && p < lim) + { + prtext(p, lim, &n); + nlines += n; + } + return nlines; +} - /* Save the character after the last newline so regexecute can write - its own sentinel newline. */ - saved_char = *search_limit; - - /* Search the buffer for a match. */ - printed_limit = buffer; - resume = buffer + resume_index; - last_match_limit = resume; - initial_line_count = line_count; +/* Search a given file. Return a count of lines printed. */ +static int +grep(fd) + int fd; +{ + int nlines, i; + size_t residue, save; + char *beg, *lim; + reset(fd); - /* In retrospect, I have to say that the following code sucks. - For an example of how to do this right, see the fgrep - driver program that I wrote around a year later. I'm - too lazy to retrofit that to egrep right now (the - pattern matchers have different needs). */ + totalcc = 0; + lastout = 0; + totalnl = 0; + pending = 0; + nlines = 0; + residue = 0; + save = 0; - while (match = execute(®, resume, search_limit, 0, &line_count, &try_backref)) + for (;;) + { + if (fillbuf(save) < 0) { - /* Find the beginning of the matching line. */ - matching_line = match; - while (matching_line > resume && matching_line[-1] != '\n') - --matching_line; - real_matching_line = matching_line; - - /* Find the beginning of the next line. */ - next_line = match; - while (next_line < search_limit && *next_line++ != '\n') - ; - - /* If a potential backreference is indicated, try it out with - a backtracking matcher to make sure the line is a match. - This is hairy because we need to handle whole_line and - whole_word matches specially. The method was stolen from - GNU fgrep. */ - if (try_backref) - { - struct re_registers regs; - int beg, len, maxlen, ret; - - beg = 0; - for (maxlen = next_line - matching_line - 1; beg <= maxlen; ++beg) - { - /* See if the matching line matches when backreferences - are considered... */ - ret = re_search (®ex, matching_line, maxlen, - beg, maxlen - beg, ®s); - if (ret == -1) - goto fail; - beg = ret; - len = regs.end[0] - beg; - /* Ok, now check if it subsumed the whole line if -x */ - if (whole_line && (beg != 0 || len != maxlen)) - goto fail; - /* If -w then check if the match aligns with word - boundaries. We have to do this iteratively, because - (a) The line may contain more than one occurence - of the pattern, and; - (b) Several alternatives in the pattern might - be valid at a given point, and we may need to - consider a shorter one in order to align with - word boundaries. */ - else if (whole_word) - while (len > 0) - { - /* If it's preceeded by a word constituent, then no go. */ - if (beg > 0 - && WCHAR((unsigned char) matching_line[beg - 1])) - break; - /* If it's followed by a word constituent, look for - a shorter match. */ - else if (beg + len < maxlen - && WCHAR((unsigned char) matching_line[beg + len])) - /* This is sheer incest. */ - len = re_match_2 (®ex, (unsigned char *) 0, 0, - matching_line, maxlen, - beg, ®s, beg + len - 1); - else - goto succeed; - } - else - goto succeed; - } - fail: - resume = next_line; - if (resume == search_limit) - break; - else - continue; - } - - succeed: - /* Print out the matching or nonmatching lines as necessary. */ - if (! nonmatching_lines) - { - /* Not -v, so nothing hairy... */ - ++match_count; - - /* Print leftover trailing context from last time around. */ - while (pending_lines && last_match_limit < matching_line) - { - last_match_limit += print_line(last_match_limit, - initial_line_count++, - 0); - --pending_lines; - } - - /* Back up over leading context if necessary. */ - for (i = leading_context; - i > 0 && matching_line > printed_limit; - --i) - { - while (matching_line > printed_limit - && (--matching_line)[-1] != '\n') - ; - --line_count; - } - - /* If context is enabled, we may have to print a separator. */ - if ((leading_context || trailing_context) && !silent - && !first_match && (printed_limit_fake - || matching_line > printed_limit)) - printf("----------\n"); - first_match = 0; - - /* Print the matching line and its leading context. */ - while (matching_line < real_matching_line) - matching_line += print_line(matching_line, line_count++, 0); - matching_line += print_line(matching_line, line_count++, 1); - - /* If there's trailing context, leave some lines pending until - next time. */ - pending_lines = trailing_context; - } - else if (matching_line == last_match_limit) - { - /* In the -v case, this is where we deal with leftover - trailing context from last time... */ - if (pending_lines > 0) - { - --pending_lines; - print_line(matching_line, line_count, 0); - } - ++line_count; - } - else if (matching_line > last_match_limit) - { - char *start = last_match_limit; - - /* Back up over leading context if necessary. */ - for (i = leading_context; start > printed_limit && i; --i) - { - while (start > printed_limit && (--start)[-1] != '\n') - ; - --initial_line_count; - } - - /* If context is enabled, we may have to print a separator. */ - if ((leading_context || trailing_context) && !silent - && !first_match && (printed_limit_fake - || start > printed_limit)) - printf("----------\n"); - first_match = 0; - - /* Print out the presumably matching leading context. */ - while (start < last_match_limit) - start += print_line(start, initial_line_count++, 0); - - /* Print out the nonmatching lines prior to the matching line. */ - while (start < matching_line) - { - /* This counts as a "matching line" in -v. */ - ++match_count; - start += print_line(start, initial_line_count++, 1); - } - - /* Deal with trailing context. In -v what this means is - we print the current (matching) line, marked as a non - matching line. */ - if (trailing_context) - { - print_line(matching_line, line_count, 0); - pending_lines = trailing_context - 1; - } - - /* Count the current line. */ - ++line_count; - } - else - /* Let us pray this never happens... */ - abort(); - - /* Resume searching at the beginning of the next line. */ - initial_line_count = line_count; - resume = next_line; - last_match_limit = next_line; - - if (resume == search_limit) - break; + error(filename, errno); + return nlines; } - - /* Restore the saved character. */ - *search_limit = saved_char; - - if (! nonmatching_lines) + lastnl = bufbeg; + if (lastout) + lastout = bufbeg; + if (buflim - bufbeg == save) + break; + beg = bufbeg + save - residue; + for (lim = buflim; lim > beg && lim[-1] != '\n'; --lim) + ; + residue = buflim - lim; + if (beg < lim) { - while (last_match_limit < search_limit && pending_lines) - { - last_match_limit += print_line(last_match_limit, - initial_line_count++, - 0); - --pending_lines; - } + nlines += grepbuf(beg, lim); + if (pending) + prpending(lim); } - else if (search_limit > last_match_limit) + i = 0; + beg = lim; + while (i < out_before && beg > bufbeg && beg != lastout) { - char *start = last_match_limit; - - /* Back up over leading context if necessary. */ - for (i = leading_context; start > printed_limit && i; --i) - { - while (start > printed_limit && (--start)[-1] != '\n') - ; - --initial_line_count; - } - - /* If context is enabled, we may have to print a separator. */ - if ((leading_context || trailing_context) && !silent - && !first_match && (printed_limit_fake - || start > printed_limit)) - printf("----------\n"); - first_match = 0; - - /* Print out all the nonmatching lines up to the search limit. */ - while (start < last_match_limit) - start += print_line(start, initial_line_count++, 0); - while (start < search_limit) - { - ++match_count; - start += print_line(start, initial_line_count++, 1); - } - - pending_lines = trailing_context; - resume_index = 0; - retain = bufbytes - (search_limit - buffer); - continue; + ++i; + do + --beg; + while (beg > bufbeg && beg[-1] != '\n'); } - - /* Save the trailing end of the buffer for possible use as leading - context in the future. */ - i = leading_context; - tmp = search_limit; - while (tmp > printed_limit && i--) - while (tmp > printed_limit && (--tmp)[-1] != '\n') - ; - resume_index = search_limit - tmp; - retain = bufbytes - (tmp - buffer); - if (tmp > printed_limit) - printed_limit_fake = 1; + if (beg != lastout) + lastout = 0; + save = residue + lim - beg; + totalcc += buflim - bufbeg - save; + if (out_line) + nlscan(beg); } - - return match_count; + if (residue) + { + nlines += grepbuf(bufbeg + save - residue, buflim); + if (pending) + prpending(buflim); + } + return nlines; } - -void -usage_and_die() + +static char version[] = "GNU grep version 2.0"; + +#define USAGE \ + "usage: %s [-[[AB] ]] [-[CEFGVchilnqsvwx]] [-[ef]] []\n" + +static void +usage() { - fprintf(stderr, "\ -Usage: %s [-CVbchilnsvwx] [-num] [-A num] [-B num] [-f file]\n\ - [-e] expr [file...]\n", prog); - exit(ERROR); + fprintf(stderr, USAGE, prog); + exit(2); } -static char version[] = "GNU e?grep, version 1.6"; +/* Go through the matchers vector and look for the specified matcher. + If we find it, install it in compile and execute, and return 1. */ +int +setmatcher(name) + char *name; +{ + int i; + + for (i = 0; matchers[i].name; ++i) + if (strcmp(name, matchers[i].name) == 0) + { + compile = matchers[i].compile; + execute = matchers[i].execute; + return 1; + } + return 0; +} int main(argc, argv) int argc; - char **argv; + char *argv[]; { - int c; - int ignore_case = 0; /* Compile the regexp to ignore case. */ - char *the_regexp = 0; /* The regular expression. */ - int regexp_len; /* Length of the regular expression. */ - char *regexp_file = 0; /* File containing parallel regexps. */ - int count_lines = 0; /* Display only a count of matching lines. */ - int list_files = 0; /* Display only the names of matching files. */ - int line_count = 0; /* Count of matching lines for a file. */ - int matches_found = 0; /* True if matches were found. */ - char *regex_errmesg; /* Error message from regex routines. */ - char translate[_NOTCHAR]; /* Translate table for case conversion - (needed by the backtracking matcher). */ - - if (prog = index(argv[0], '/')) - ++prog; - else - prog = argv[0]; - - opterr = 0; - while ((c = getopt(argc, argv, "0123456789A:B:CVbce:f:hilnsvwx")) != EOF) - switch (c) + char *keys; + size_t keycc, oldcc, keyalloc; + int keyfound, count_matches, no_filenames, list_files, suppress_errors; + int opt, cc, desc, count, status; + FILE *fp; + extern char *optarg; + extern int optind; + + prog = argv[0]; + if (prog && strrchr(prog, '/')) + prog = strrchr(prog, '/') + 1; + + keys = NULL; + keycc = 0; + keyfound = 0; + count_matches = 0; + no_filenames = 0; + list_files = 0; + suppress_errors = 0; + matcher = NULL; + + while ((opt = getopt(argc, argv, "0123456789A:B:CEFGVX:bce:f:hiLlnqsvwxy")) + != EOF) + switch (opt) { - case '?': - usage_and_die(); - break; - case '0': case '1': case '2': @@ -586,422 +633,194 @@ main(argc, argv) case '7': case '8': case '9': - trailing_context = 10 * trailing_context + c - '0'; - leading_context = 10 * leading_context + c - '0'; + out_before = 10 * out_before + opt - '0'; + out_after = 10 * out_after + opt - '0'; break; - case 'A': - if (! sscanf(optarg, "%d", &trailing_context) - || trailing_context < 0) - usage_and_die(); + out_after = atoi(optarg); + if (out_after < 0) + usage(); break; - case 'B': - if (! sscanf(optarg, "%d", &leading_context) - || leading_context < 0) - usage_and_die(); + out_before = atoi(optarg); + if (out_before < 0) + usage(); break; - case 'C': - trailing_context = leading_context = 2; + out_before = out_after = 2; + break; + case 'E': + if (matcher && strcmp(matcher, "egrep") != 0) + fatal("you may specify only one of -E, -F, or -G", 0); + matcher = "posix-egrep"; + break; + case 'F': + if (matcher && strcmp(matcher, "fgrep") != 0) + fatal("you may specify only one of -E, -F, or -G", 0);; + matcher = "fgrep"; + break; + case 'G': + if (matcher && strcmp(matcher, "grep") != 0) + fatal("you may specify only one of -E, -F, or -G", 0); + matcher = "grep"; break; - case 'V': fprintf(stderr, "%s\n", version); break; - + case 'X': + if (matcher) + fatal("matcher already specified", 0); + matcher = optarg; + break; case 'b': - byte_count = 1; + out_byte = 1; break; - case 'c': - count_lines = 1; - silent = 1; + out_quiet = 1; + count_matches = 1; break; - case 'e': - /* It doesn't make sense to mix -f and -e. */ - if (regexp_file) - usage_and_die(); - the_regexp = optarg; + cc = strlen(optarg); + keys = xrealloc(keys, keycc + cc + 1); + if (keyfound) + keys[keycc++] = '\n'; + strcpy(&keys[keycc], optarg); + keycc += cc; + keyfound = 1; break; - case 'f': - /* It doesn't make sense to mix -f and -e. */ - if (the_regexp) - usage_and_die(); - regexp_file = optarg; + fp = strcmp(optarg, "-") != 0 ? fopen(optarg, "r") : stdin; + if (!fp) + fatal(optarg, errno); + for (keyalloc = 1; keyalloc <= keycc; keyalloc *= 2) + ; + keys = xrealloc(keys, keyalloc); + oldcc = keycc; + if (keyfound) + keys[keycc++] = '\n'; + while (!feof(fp) + && (cc = fread(keys + keycc, 1, keyalloc - keycc, fp)) > 0) + { + keycc += cc; + if (keycc == keyalloc) + keys = xrealloc(keys, keyalloc *= 2); + } + if (fp != stdin) + fclose(fp); + /* Nuke the final newline to avoid matching a null string. */ + if (keycc - oldcc > 0 && keys[keycc - 1] == '\n') + --keycc; + keyfound = 1; break; - case 'h': no_filenames = 1; break; - case 'i': - ignore_case = 1; - for (c = 0; c < _NOTCHAR; ++c) - if (isupper(c)) - translate[c] = tolower(c); - else - translate[c] = c; - regex.translate = translate; + case 'y': /* For old-timers . . . */ + match_icase = 1; + break; + case 'L': + /* Like -l, except list files that don't contain matches. + Inspired by the same option in Hume's gre. */ + out_quiet = 1; + list_files = -1; break; - case 'l': + out_quiet = 1; list_files = 1; - silent = 1; break; - case 'n': - line_numbers = 1; + out_line = 1; + break; + case 'q': + out_quiet = 1; break; - case 's': - silent = 1; + suppress_errors = 1; break; - case 'v': - nonmatching_lines = 1; + out_invert = 1; break; - case 'w': - whole_word = 1; + match_words = 1; break; - case 'x': - whole_line = 1; + match_lines = 1; break; - default: - /* This can't happen. */ - fprintf(stderr, "%s: getopt(3) let one by!\n", prog); - usage_and_die(); + usage(); break; } - /* Set the syntax depending on whether we are EGREP or not. */ -#ifdef EGREP - regsyntax(RE_SYNTAX_EGREP, ignore_case); - re_set_syntax(RE_SYNTAX_EGREP); -#else - regsyntax(RE_SYNTAX_GREP, ignore_case); - re_set_syntax(RE_SYNTAX_GREP); -#endif + if (!keyfound) + if (optind < argc) + { + keys = argv[optind++]; + keycc = strlen(keys); + } + else + usage(); - /* Compile the regexp according to all the options. */ - if (regexp_file) - { - FILE *fp = fopen(regexp_file, "r"); - int len = 256; - int i = 0; + if (!matcher) + matcher = prog; - if (! fp) - { - fprintf(stderr, "%s: %s: %s\n", prog, regexp_file, - sys_errlist[errno]); - exit(ERROR); - } + if (!setmatcher(matcher) && !setmatcher("default")) + abort(); - the_regexp = malloc(len); - while ((c = getc(fp)) != EOF) - { - the_regexp[i++] = c; - if (i == len) - the_regexp = realloc(the_regexp, len *= 2); - } - fclose(fp); - /* Nuke the concluding newline so we won't match the empty string. */ - if (i > 0 && the_regexp[i - 1] == '\n') - --i; - regexp_len = i; - } - else if (! the_regexp) - { - if (optind >= argc) - usage_and_die(); - the_regexp = argv[optind++]; - regexp_len = strlen(the_regexp); - } - else - regexp_len = strlen(the_regexp); - - if (whole_word || whole_line) - { - /* In the whole-word case, we use the pattern: - (^|[^A-Za-z_])(userpattern)([^A-Za-z_]|$). - In the whole-line case, we use the pattern: - ^(userpattern)$. - BUG: Using [A-Za-z_] is locale-dependent! */ - - char *n = malloc(regexp_len + 50); - int i = 0; - -#ifdef EGREP - if (whole_word) - strcpy(n, "(^|[^A-Za-z_])("); - else - strcpy(n, "^("); -#else - /* Todo: Make *sure* this is the right syntax. Down with grep! */ - if (whole_word) - strcpy(n, "\\(^\\|[^A-Za-z_]\\)\\("); - else - strcpy(n, "^\\("); -#endif - i = strlen(n); - bcopy(the_regexp, n + i, regexp_len); - i += regexp_len; -#ifdef EGREP - if (whole_word) - strcpy(n + i, ")([^A-Za-z_]|$)"); - else - strcpy(n + i, ")$"); -#else - if (whole_word) - strcpy(n + i, "\\)\\([^A-Za-z_]\\|$\\)"); - else - strcpy(n + i, "\\)$"); -#endif - i += strlen(n + i); - regcompile(n, i, ®, 1); - } - else - regcompile(the_regexp, regexp_len, ®, 1); - - - if (regex_errmesg = re_compile_pattern(the_regexp, regexp_len, ®ex)) - regerror(regex_errmesg); - - /* - Find the longest metacharacter-free string which must occur in the - regexpr, before short-circuiting regexecute() with Boyer-Moore-Gosper. - (Conjecture: The problem in general is NP-complete.) If there is no - such string (like for many alternations), then default to full automaton - search. regmust() code and heuristics [see dfa.c] courtesy - Arthur David Olson. - */ - if (line_numbers == 0 && nonmatching_lines == 0) - { - if (reg.mustn == 0 || reg.mustn == MUST_MAX || - index(reg.must, '\0') != reg.must + reg.mustn) - bmgexec = 0; - else - { - reg.must[reg.mustn] = '\0'; - if (getenv("MUSTDEBUG") != NULL) - (void) printf("must have: \"%s\"\n", reg.must); - bmg_setup(reg.must, ignore_case); - bmgexec = 1; - } - } - - if (argc - optind < 2) - no_filenames = 1; + (*compile)(keys, keycc); + + if (argc - optind > 1 && !no_filenames) + out_file = 1; - initialize_buffer(); + status = 1; - if (argc > optind) + if (optind < argc) while (optind < argc) { - bufprev = eof = 0; - filename = argv[optind++]; - fd = open(filename, 0, 0); - if (fd < 0) + desc = strcmp(argv[optind], "-") ? open(argv[optind], O_RDONLY) : 0; + if (desc < 0) { - fprintf(stderr, "%s: %s: %s\n", prog, filename, - sys_errlist[errno]); - error = 1; - continue; + if (!suppress_errors) + error(argv[optind], errno); } - if (line_count = grep()) - matches_found = 1; - close(fd); - if (count_lines) - if (!no_filenames) - printf("%s:%d\n", filename, line_count); - else - printf("%d\n", line_count); - else if (list_files && line_count) - printf("%s\n", filename); + else + { + filename = desc == 0 ? "(standard input)" : argv[optind]; + count = grep(desc); + if (count_matches) + { + if (out_file) + printf("%s:", filename); + printf("%d\n", count); + } + if (count) + { + status = 0; + if (list_files == 1) + printf("%s\n", filename); + } + else if (list_files == -1) + printf("%s\n", filename); + } + if (desc != 0) + close(desc); + ++optind; } else { - if (line_count = grep()) - matches_found = 1; - if (count_lines) - printf("%d\n", line_count); - else if (list_files && line_count) - printf("\n"); - } - - if (error) - exit(ERROR); - if (matches_found) - exit(MATCHES_FOUND); - exit(NO_MATCHES_FOUND); - return NO_MATCHES_FOUND; -} - -/* Needed by the regexp routines. This could be fancier, especially when - dealing with parallel regexps in files. */ -void -regerror(s) - const char *s; -{ - fprintf(stderr, "%s: %s\n", prog, s); - exit(ERROR); -} - -/* - bmg_setup() and bmg_search() adapted from: - Boyer/Moore/Gosper-assisted 'egrep' search, with delta0 table as in - original paper (CACM, October, 1977). No delta1 or delta2. According to - experiment (Horspool, Soft. Prac. Exp., 1982), delta2 is of minimal - practical value. However, to improve for worst case input, integrating - the improved Galil strategies (Apostolico/Giancarlo, Siam. J. Comput., - February 1986) deserves consideration. - - James A. Woods Copyleft (C) 1986, 1988 - NASA Ames Research Center -*/ - -char * -execute(r, begin, end, newline, count, try_backref) - struct regexp *r; - char *begin; - char *end; - int newline; - int *count; - int *try_backref; -{ - register char *p, *s; - char *match; - char *start = begin; - char save; /* regexecute() sentinel */ - int len; - char *bmg_search(); - - if (!bmgexec) /* full automaton search */ - return(regexecute(r, begin, end, newline, count, try_backref)); - else - { - len = end - begin; - while ((match = bmg_search((unsigned char *) start, len)) != NULL) - { - p = match; /* narrow search range to submatch line */ - while (p > begin && *p != '\n') - p--; - s = match; - while (s < end && *s != '\n') - s++; - s++; - - save = *s; - *s = '\0'; - match = regexecute(r, p, s, newline, count, try_backref); - *s = save; - - if (match != NULL) - return((char *) match); - else - { - start = s; - len = end - start; - } - } - return(NULL); - } -} - -int delta0[256]; -unsigned char cmap[256]; /* (un)folded characters */ -unsigned char pattern[5000]; -int patlen; - -char * -bmg_search(buffer, buflen) - unsigned char *buffer; - int buflen; -{ - register unsigned char *k, *strend, *s, *buflim; - register int t; - int j; - - if (patlen > buflen) - return NULL; - - buflim = buffer + buflen; - if (buflen > patlen * 4) - strend = buflim - patlen * 4; - else - strend = buffer; - - s = buffer; - k = buffer + patlen - 1; - - for (;;) - { - /* The dreaded inner loop, revisited. */ - while (k < strend && (t = delta0[*k])) + filename = "(standard input)"; + count = grep(0); + if (count_matches) + printf("%d\n", count); + if (count) { - k += t; - k += delta0[*k]; - k += delta0[*k]; + status = 0; + if (list_files == 1) + printf("(standard input)\n"); } - while (k < buflim && delta0[*k]) - ++k; - if (k == buflim) - break; - - j = patlen - 1; - s = k; - while (--j >= 0 && cmap[*--s] == pattern[j]) - ; - /* - delta-less shortcut for literati, but - short shrift for genetic engineers. - */ - if (j >= 0) - k++; - else /* submatch */ - return ((char *)k); + else if (list_files == -1) + printf("(standard input)\n"); } - return(NULL); -} -int -bmg_setup(pat, folded) /* compute "boyer-moore" delta table */ - char *pat; - int folded; -{ /* ... HAKMEM lives ... */ - int j; - - patlen = strlen(pat); - - if (folded) /* fold case while saving pattern */ - for (j = 0; j < patlen; j++) - pattern[j] = (isupper((int) pat[j]) ? - (char) tolower((int) pat[j]) : pat[j]); - else - bcopy(pat, pattern, patlen); - - for (j = 0; j < 256; j++) - { - delta0[j] = patlen; - cmap[j] = (char) j; /* could be done at compile time */ - } - for (j = 0; j < patlen - 1; j++) - delta0[pattern[j]] = patlen - j - 1; - delta0[pattern[patlen - 1]] = 0; - - if (folded) - { - for (j = 0; j < patlen - 1; j++) - if (islower((int) pattern[j])) - delta0[toupper((int) pattern[j])] = patlen - j - 1; - if (islower((int) pattern[patlen - 1])) - delta0[toupper((int) pattern[patlen - 1])] = 0; - for (j = 'A'; j <= 'Z'; j++) - cmap[j] = (char) tolower((int) j); - } + exit(errseen ? 2 : status); } diff --git a/gnu/usr.bin/grep/grep.h b/gnu/usr.bin/grep/grep.h new file mode 100644 index 0000000000..a3316c501f --- /dev/null +++ b/gnu/usr.bin/grep/grep.h @@ -0,0 +1,53 @@ +/* grep.h - interface to grep driver for searching subroutines. + Copyright (C) 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#if __STDC__ + +extern void fatal(const char *, int); + +/* Grep.c expects the matchers vector to be terminated + by an entry with a NULL name, and to contain at least + an entry named "default". */ + +extern struct matcher +{ + char *name; + void (*compile)(char *, size_t); + char *(*execute)(char *, size_t, char **); +} matchers[]; + +#else + +extern void fatal(); + +extern struct matcher +{ + char *name; + void (*compile)(); + char *(*execute)(); +} matchers[]; + +#endif + +/* Exported from grep.c. */ +extern char *matcher; + +/* The following flags are exported from grep for the matchers + to look at. */ +extern int match_icase; /* -i */ +extern int match_words; /* -w */ +extern int match_lines; /* -x */ diff --git a/gnu/usr.bin/grep/kwset.c b/gnu/usr.bin/grep/kwset.c new file mode 100644 index 0000000000..9b09071dbf --- /dev/null +++ b/gnu/usr.bin/grep/kwset.c @@ -0,0 +1,805 @@ +/* kwset.c - search for any of a set of keywords. + Copyright 1989 Free Software Foundation + Written August 1989 by Mike Haertel. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +/* The algorithm implemented by these routines bears a startling resemblence + to one discovered by Beate Commentz-Walter, although it is not identical. + See "A String Matching Algorithm Fast on the Average," Technical Report, + IBM-Germany, Scientific Center Heidelberg, Tiergartenstrasse 15, D-6900 + Heidelberg, Germany. See also Aho, A.V., and M. Corasick, "Efficient + String Matching: An Aid to Bibliographic Search," CACM June 1975, + Vol. 18, No. 6, which describes the failure function used below. */ + + +#ifdef STDC_HEADERS +#include +#include +#else +#define INT_MAX 2147483647 +#define UCHAR_MAX 255 +#ifdef __STDC__ +#include +#else +#include +#endif +extern char *malloc(); +extern void free(); +#endif + +#ifdef HAVE_MEMCHR +#include +#ifdef NEED_MEMORY_H +#include +#endif +#else +#ifdef __STDC__ +extern void *memchr(); +#else +extern char *memchr(); +#endif +#endif + +#ifdef GREP +extern char *xmalloc(); +#define malloc xmalloc +#endif + +#include "kwset.h" +#include "obstack.h" + +#define NCHAR (UCHAR_MAX + 1) +#define obstack_chunk_alloc malloc +#define obstack_chunk_free free + +/* Balanced tree of edges and labels leaving a given trie node. */ +struct tree +{ + struct tree *llink; /* Left link; MUST be first field. */ + struct tree *rlink; /* Right link (to larger labels). */ + struct trie *trie; /* Trie node pointed to by this edge. */ + unsigned char label; /* Label on this edge. */ + char balance; /* Difference in depths of subtrees. */ +}; + +/* Node of a trie representing a set of reversed keywords. */ +struct trie +{ + unsigned int accepting; /* Word index of accepted word, or zero. */ + struct tree *links; /* Tree of edges leaving this node. */ + struct trie *parent; /* Parent of this node. */ + struct trie *next; /* List of all trie nodes in level order. */ + struct trie *fail; /* Aho-Corasick failure function. */ + int depth; /* Depth of this node from the root. */ + int shift; /* Shift function for search failures. */ + int maxshift; /* Max shift of self and descendents. */ +}; + +/* Structure returned opaquely to the caller, containing everything. */ +struct kwset +{ + struct obstack obstack; /* Obstack for node allocation. */ + int words; /* Number of words in the trie. */ + struct trie *trie; /* The trie itself. */ + int mind; /* Minimum depth of an accepting node. */ + int maxd; /* Maximum depth of any node. */ + unsigned char delta[NCHAR]; /* Delta table for rapid search. */ + struct trie *next[NCHAR]; /* Table of children of the root. */ + char *target; /* Target string if there's only one. */ + int mind2; /* Used in Boyer-Moore search for one string. */ + char *trans; /* Character translation table. */ +}; + +/* Allocate and initialize a keyword set object, returning an opaque + pointer to it. Return NULL if memory is not available. */ +kwset_t +kwsalloc(trans) + char *trans; +{ + struct kwset *kwset; + + kwset = (struct kwset *) malloc(sizeof (struct kwset)); + if (!kwset) + return 0; + + obstack_init(&kwset->obstack); + kwset->words = 0; + kwset->trie + = (struct trie *) obstack_alloc(&kwset->obstack, sizeof (struct trie)); + if (!kwset->trie) + { + kwsfree((kwset_t) kwset); + return 0; + } + kwset->trie->accepting = 0; + kwset->trie->links = 0; + kwset->trie->parent = 0; + kwset->trie->next = 0; + kwset->trie->fail = 0; + kwset->trie->depth = 0; + kwset->trie->shift = 0; + kwset->mind = INT_MAX; + kwset->maxd = -1; + kwset->target = 0; + kwset->trans = trans; + + return (kwset_t) kwset; +} + +/* Add the given string to the contents of the keyword set. Return NULL + for success, an error message otherwise. */ +char * +kwsincr(kws, text, len) + kwset_t kws; + char *text; + size_t len; +{ + struct kwset *kwset; + register struct trie *trie; + register unsigned char label; + register struct tree *link; + register int depth; + struct tree *links[12]; + enum { L, R } dirs[12]; + struct tree *t, *r, *l, *rl, *lr; + + kwset = (struct kwset *) kws; + trie = kwset->trie; + text += len; + + /* Descend the trie (built of reversed keywords) character-by-character, + installing new nodes when necessary. */ + while (len--) + { + label = kwset->trans ? kwset->trans[(unsigned char) *--text] : *--text; + + /* Descend the tree of outgoing links for this trie node, + looking for the current character and keeping track + of the path followed. */ + link = trie->links; + links[0] = (struct tree *) &trie->links; + dirs[0] = L; + depth = 1; + + while (link && label != link->label) + { + links[depth] = link; + if (label < link->label) + dirs[depth++] = L, link = link->llink; + else + dirs[depth++] = R, link = link->rlink; + } + + /* The current character doesn't have an outgoing link at + this trie node, so build a new trie node and install + a link in the current trie node's tree. */ + if (!link) + { + link = (struct tree *) obstack_alloc(&kwset->obstack, + sizeof (struct tree)); + if (!link) + return "memory exhausted"; + link->llink = 0; + link->rlink = 0; + link->trie = (struct trie *) obstack_alloc(&kwset->obstack, + sizeof (struct trie)); + if (!link->trie) + return "memory exhausted"; + link->trie->accepting = 0; + link->trie->links = 0; + link->trie->parent = trie; + link->trie->next = 0; + link->trie->fail = 0; + link->trie->depth = trie->depth + 1; + link->trie->shift = 0; + link->label = label; + link->balance = 0; + + /* Install the new tree node in its parent. */ + if (dirs[--depth] == L) + links[depth]->llink = link; + else + links[depth]->rlink = link; + + /* Back up the tree fixing the balance flags. */ + while (depth && !links[depth]->balance) + { + if (dirs[depth] == L) + --links[depth]->balance; + else + ++links[depth]->balance; + --depth; + } + + /* Rebalance the tree by pointer rotations if necessary. */ + if (depth && ((dirs[depth] == L && --links[depth]->balance) + || (dirs[depth] == R && ++links[depth]->balance))) + { + switch (links[depth]->balance) + { + case (char) -2: + switch (dirs[depth + 1]) + { + case L: + r = links[depth], t = r->llink, rl = t->rlink; + t->rlink = r, r->llink = rl; + t->balance = r->balance = 0; + break; + case R: + r = links[depth], l = r->llink, t = l->rlink; + rl = t->rlink, lr = t->llink; + t->llink = l, l->rlink = lr, t->rlink = r, r->llink = rl; + l->balance = t->balance != 1 ? 0 : -1; + r->balance = t->balance != (char) -1 ? 0 : 1; + t->balance = 0; + break; + } + break; + case 2: + switch (dirs[depth + 1]) + { + case R: + l = links[depth], t = l->rlink, lr = t->llink; + t->llink = l, l->rlink = lr; + t->balance = l->balance = 0; + break; + case L: + l = links[depth], r = l->rlink, t = r->llink; + lr = t->llink, rl = t->rlink; + t->llink = l, l->rlink = lr, t->rlink = r, r->llink = rl; + l->balance = t->balance != 1 ? 0 : -1; + r->balance = t->balance != (char) -1 ? 0 : 1; + t->balance = 0; + break; + } + break; + } + + if (dirs[depth - 1] == L) + links[depth - 1]->llink = t; + else + links[depth - 1]->rlink = t; + } + } + + trie = link->trie; + } + + /* Mark the node we finally reached as accepting, encoding the + index number of this word in the keyword set so far. */ + if (!trie->accepting) + trie->accepting = 1 + 2 * kwset->words; + ++kwset->words; + + /* Keep track of the longest and shortest string of the keyword set. */ + if (trie->depth < kwset->mind) + kwset->mind = trie->depth; + if (trie->depth > kwset->maxd) + kwset->maxd = trie->depth; + + return 0; +} + +/* Enqueue the trie nodes referenced from the given tree in the + given queue. */ +static void +enqueue(tree, last) + struct tree *tree; + struct trie **last; +{ + if (!tree) + return; + enqueue(tree->llink, last); + enqueue(tree->rlink, last); + (*last) = (*last)->next = tree->trie; +} + +/* Compute the Aho-Corasick failure function for the trie nodes referenced + from the given tree, given the failure function for their parent as + well as a last resort failure node. */ +static void +treefails(tree, fail, recourse) + register struct tree *tree; + struct trie *fail; + struct trie *recourse; +{ + register struct tree *link; + + if (!tree) + return; + + treefails(tree->llink, fail, recourse); + treefails(tree->rlink, fail, recourse); + + /* Find, in the chain of fails going back to the root, the first + node that has a descendent on the current label. */ + while (fail) + { + link = fail->links; + while (link && tree->label != link->label) + if (tree->label < link->label) + link = link->llink; + else + link = link->rlink; + if (link) + { + tree->trie->fail = link->trie; + return; + } + fail = fail->fail; + } + + tree->trie->fail = recourse; +} + +/* Set delta entries for the links of the given tree such that + the preexisting delta value is larger than the current depth. */ +static void +treedelta(tree, depth, delta) + register struct tree *tree; + register unsigned int depth; + unsigned char delta[]; +{ + if (!tree) + return; + treedelta(tree->llink, depth, delta); + treedelta(tree->rlink, depth, delta); + if (depth < delta[tree->label]) + delta[tree->label] = depth; +} + +/* Return true if A has every label in B. */ +static int +hasevery(a, b) + register struct tree *a; + register struct tree *b; +{ + if (!b) + return 1; + if (!hasevery(a, b->llink)) + return 0; + if (!hasevery(a, b->rlink)) + return 0; + while (a && b->label != a->label) + if (b->label < a->label) + a = a->llink; + else + a = a->rlink; + return !!a; +} + +/* Compute a vector, indexed by character code, of the trie nodes + referenced from the given tree. */ +static void +treenext(tree, next) + struct tree *tree; + struct trie *next[]; +{ + if (!tree) + return; + treenext(tree->llink, next); + treenext(tree->rlink, next); + next[tree->label] = tree->trie; +} + +/* Compute the shift for each trie node, as well as the delta + table and next cache for the given keyword set. */ +char * +kwsprep(kws) + kwset_t kws; +{ + register struct kwset *kwset; + register int i; + register struct trie *curr, *fail; + register char *trans; + unsigned char delta[NCHAR]; + struct trie *last, *next[NCHAR]; + + kwset = (struct kwset *) kws; + + /* Initial values for the delta table; will be changed later. The + delta entry for a given character is the smallest depth of any + node at which an outgoing edge is labeled by that character. */ + if (kwset->mind < 256) + for (i = 0; i < NCHAR; ++i) + delta[i] = kwset->mind; + else + for (i = 0; i < NCHAR; ++i) + delta[i] = 255; + + /* Check if we can use the simple boyer-moore algorithm, instead + of the hairy commentz-walter algorithm. */ + if (kwset->words == 1 && kwset->trans == 0) + { + /* Looking for just one string. Extract it from the trie. */ + kwset->target = obstack_alloc(&kwset->obstack, kwset->mind); + for (i = kwset->mind - 1, curr = kwset->trie; i >= 0; --i) + { + kwset->target[i] = curr->links->label; + curr = curr->links->trie; + } + /* Build the Boyer Moore delta. Boy that's easy compared to CW. */ + for (i = 0; i < kwset->mind; ++i) + delta[(unsigned char) kwset->target[i]] = kwset->mind - (i + 1); + kwset->mind2 = kwset->mind; + /* Find the minimal delta2 shift that we might make after + a backwards match has failed. */ + for (i = 0; i < kwset->mind - 1; ++i) + if (kwset->target[i] == kwset->target[kwset->mind - 1]) + kwset->mind2 = kwset->mind - (i + 1); + } + else + { + /* Traverse the nodes of the trie in level order, simultaneously + computing the delta table, failure function, and shift function. */ + for (curr = last = kwset->trie; curr; curr = curr->next) + { + /* Enqueue the immediate descendents in the level order queue. */ + enqueue(curr->links, &last); + + curr->shift = kwset->mind; + curr->maxshift = kwset->mind; + + /* Update the delta table for the descendents of this node. */ + treedelta(curr->links, curr->depth, delta); + + /* Compute the failure function for the decendents of this node. */ + treefails(curr->links, curr->fail, kwset->trie); + + /* Update the shifts at each node in the current node's chain + of fails back to the root. */ + for (fail = curr->fail; fail; fail = fail->fail) + { + /* If the current node has some outgoing edge that the fail + doesn't, then the shift at the fail should be no larger + than the difference of their depths. */ + if (!hasevery(fail->links, curr->links)) + if (curr->depth - fail->depth < fail->shift) + fail->shift = curr->depth - fail->depth; + + /* If the current node is accepting then the shift at the + fail and its descendents should be no larger than the + difference of their depths. */ + if (curr->accepting && fail->maxshift > curr->depth - fail->depth) + fail->maxshift = curr->depth - fail->depth; + } + } + + /* Traverse the trie in level order again, fixing up all nodes whose + shift exceeds their inherited maxshift. */ + for (curr = kwset->trie->next; curr; curr = curr->next) + { + if (curr->maxshift > curr->parent->maxshift) + curr->maxshift = curr->parent->maxshift; + if (curr->shift > curr->maxshift) + curr->shift = curr->maxshift; + } + + /* Create a vector, indexed by character code, of the outgoing links + from the root node. */ + for (i = 0; i < NCHAR; ++i) + next[i] = 0; + treenext(kwset->trie->links, next); + + if ((trans = kwset->trans) != 0) + for (i = 0; i < NCHAR; ++i) + kwset->next[i] = next[(unsigned char) trans[i]]; + else + for (i = 0; i < NCHAR; ++i) + kwset->next[i] = next[i]; + } + + /* Fix things up for any translation table. */ + if ((trans = kwset->trans) != 0) + for (i = 0; i < NCHAR; ++i) + kwset->delta[i] = delta[(unsigned char) trans[i]]; + else + for (i = 0; i < NCHAR; ++i) + kwset->delta[i] = delta[i]; + + return 0; +} + +#define U(C) ((unsigned char) (C)) + +/* Fast boyer-moore search. */ +static char * +bmexec(kws, text, size) + kwset_t kws; + char *text; + size_t size; +{ + struct kwset *kwset; + register unsigned char *d1; + register char *ep, *sp, *tp; + register int d, gc, i, len, md2; + + kwset = (struct kwset *) kws; + len = kwset->mind; + + if (len == 0) + return text; + if (len > size) + return 0; + if (len == 1) + return memchr(text, kwset->target[0], size); + + d1 = kwset->delta; + sp = kwset->target + len; + gc = U(sp[-2]); + md2 = kwset->mind2; + tp = text + len; + + /* Significance of 12: 1 (initial offset) + 10 (skip loop) + 1 (md2). */ + if (size > 12 * len) + /* 11 is not a bug, the initial offset happens only once. */ + for (ep = text + size - 11 * len;;) + { + while (tp <= ep) + { + d = d1[U(tp[-1])], tp += d; + d = d1[U(tp[-1])], tp += d; + if (d == 0) + goto found; + d = d1[U(tp[-1])], tp += d; + d = d1[U(tp[-1])], tp += d; + d = d1[U(tp[-1])], tp += d; + if (d == 0) + goto found; + d = d1[U(tp[-1])], tp += d; + d = d1[U(tp[-1])], tp += d; + d = d1[U(tp[-1])], tp += d; + if (d == 0) + goto found; + d = d1[U(tp[-1])], tp += d; + d = d1[U(tp[-1])], tp += d; + } + break; + found: + if (U(tp[-2]) == gc) + { + for (i = 3; i <= len && U(tp[-i]) == U(sp[-i]); ++i) + ; + if (i > len) + return tp - len; + } + tp += md2; + } + + /* Now we have only a few characters left to search. We + carefully avoid ever producing an out-of-bounds pointer. */ + ep = text + size; + d = d1[U(tp[-1])]; + while (d <= ep - tp) + { + d = d1[U((tp += d)[-1])]; + if (d != 0) + continue; + if (tp[-2] == gc) + { + for (i = 3; i <= len && U(tp[-i]) == U(sp[-i]); ++i) + ; + if (i > len) + return tp - len; + } + d = md2; + } + + return 0; +} + +/* Hairy multiple string search. */ +static char * +cwexec(kws, text, len, kwsmatch) + kwset_t kws; + char *text; + size_t len; + struct kwsmatch *kwsmatch; +{ + struct kwset *kwset; + struct trie **next, *trie, *accept; + char *beg, *lim, *mch, *lmch; + register unsigned char c, *delta; + register int d; + register char *end, *qlim; + register struct tree *tree; + register char *trans; + + /* Initialize register copies and look for easy ways out. */ + kwset = (struct kwset *) kws; + if (len < kwset->mind) + return 0; + next = kwset->next; + delta = kwset->delta; + trans = kwset->trans; + lim = text + len; + end = text; + if ((d = kwset->mind) != 0) + mch = 0; + else + { + mch = text, accept = kwset->trie; + goto match; + } + + if (len >= 4 * kwset->mind) + qlim = lim - 4 * kwset->mind; + else + qlim = 0; + + while (lim - end >= d) + { + if (qlim && end <= qlim) + { + end += d - 1; + while ((d = delta[c = *end]) && end < qlim) + { + end += d; + end += delta[(unsigned char) *end]; + end += delta[(unsigned char) *end]; + } + ++end; + } + else + d = delta[c = (end += d)[-1]]; + if (d) + continue; + beg = end - 1; + trie = next[c]; + if (trie->accepting) + { + mch = beg; + accept = trie; + } + d = trie->shift; + while (beg > text) + { + c = trans ? trans[(unsigned char) *--beg] : *--beg; + tree = trie->links; + while (tree && c != tree->label) + if (c < tree->label) + tree = tree->llink; + else + tree = tree->rlink; + if (tree) + { + trie = tree->trie; + if (trie->accepting) + { + mch = beg; + accept = trie; + } + } + else + break; + d = trie->shift; + } + if (mch) + goto match; + } + return 0; + + match: + /* Given a known match, find the longest possible match anchored + at or before its starting point. This is nearly a verbatim + copy of the preceding main search loops. */ + if (lim - mch > kwset->maxd) + lim = mch + kwset->maxd; + lmch = 0; + d = 1; + while (lim - end >= d) + { + if ((d = delta[c = (end += d)[-1]]) != 0) + continue; + beg = end - 1; + if (!(trie = next[c])) + { + d = 1; + continue; + } + if (trie->accepting && beg <= mch) + { + lmch = beg; + accept = trie; + } + d = trie->shift; + while (beg > text) + { + c = trans ? trans[(unsigned char) *--beg] : *--beg; + tree = trie->links; + while (tree && c != tree->label) + if (c < tree->label) + tree = tree->llink; + else + tree = tree->rlink; + if (tree) + { + trie = tree->trie; + if (trie->accepting && beg <= mch) + { + lmch = beg; + accept = trie; + } + } + else + break; + d = trie->shift; + } + if (lmch) + { + mch = lmch; + goto match; + } + if (!d) + d = 1; + } + + if (kwsmatch) + { + kwsmatch->index = accept->accepting / 2; + kwsmatch->beg[0] = mch; + kwsmatch->size[0] = accept->depth; + } + return mch; +} + +/* Search through the given text for a match of any member of the + given keyword set. Return a pointer to the first character of + the matching substring, or NULL if no match is found. If FOUNDLEN + is non-NULL store in the referenced location the length of the + matching substring. Similarly, if FOUNDIDX is non-NULL, store + in the referenced location the index number of the particular + keyword matched. */ +char * +kwsexec(kws, text, size, kwsmatch) + kwset_t kws; + char *text; + size_t size; + struct kwsmatch *kwsmatch; +{ + struct kwset *kwset; + char *ret; + + kwset = (struct kwset *) kws; + if (kwset->words == 1 && kwset->trans == 0) + { + ret = bmexec(kws, text, size); + if (kwsmatch != 0 && ret != 0) + { + kwsmatch->index = 0; + kwsmatch->beg[0] = ret; + kwsmatch->size[0] = kwset->mind; + } + return ret; + } + else + return cwexec(kws, text, size, kwsmatch); +} + +/* Free the components of the given keyword set. */ +void +kwsfree(kws) + kwset_t kws; +{ + struct kwset *kwset; + + kwset = (struct kwset *) kws; + obstack_free(&kwset->obstack, 0); + free(kws); +} diff --git a/gnu/usr.bin/grep/kwset.h b/gnu/usr.bin/grep/kwset.h new file mode 100644 index 0000000000..95f62e715d --- /dev/null +++ b/gnu/usr.bin/grep/kwset.h @@ -0,0 +1,69 @@ +/* kwset.h - header declaring the keyword set library. + Copyright 1989 Free Software Foundation + Written August 1989 by Mike Haertel. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +struct kwsmatch +{ + int index; /* Index number of matching keyword. */ + char *beg[1]; /* Begin pointer for each submatch. */ + size_t size[1]; /* Length of each submatch. */ +}; + +#if __STDC__ + +typedef void *kwset_t; + +/* Return an opaque pointer to a newly allocated keyword set, or NULL + if enough memory cannot be obtained. The argument if non-NULL + specifies a table of character translations to be applied to all + pattern and search text. */ +extern kwset_t kwsalloc(char *); + +/* Incrementally extend the keyword set to include the given string. + Return NULL for success, or an error message. Remember an index + number for each keyword included in the set. */ +extern char *kwsincr(kwset_t, char *, size_t); + +/* When the keyword set has been completely built, prepare it for + use. Return NULL for success, or an error message. */ +extern char *kwsprep(kwset_t); + +/* Search through the given buffer for a member of the keyword set. + Return a pointer to the leftmost longest match found, or NULL if + no match is found. If foundlen is non-NULL, store the length of + the matching substring in the integer it points to. Similarly, + if foundindex is non-NULL, store the index of the particular + keyword found therein. */ +extern char *kwsexec(kwset_t, char *, size_t, struct kwsmatch *); + +/* Deallocate the given keyword set and all its associated storage. */ +extern void kwsfree(kwset_t); + +#else + +typedef char *kwset_t; + +extern kwset_t kwsalloc(); +extern char *kwsincr(); +extern char *kwsprep(); +extern char *kwsexec(); +extern void kwsfree(); + +#endif diff --git a/gnu/usr.bin/grep/obstack.c b/gnu/usr.bin/grep/obstack.c new file mode 100644 index 0000000000..7b9d3b9046 --- /dev/null +++ b/gnu/usr.bin/grep/obstack.c @@ -0,0 +1,454 @@ +/* obstack.c - subroutines used implicitly by object stack macros + Copyright (C) 1988, 1993 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "obstack.h" + +/* This is just to get __GNU_LIBRARY__ defined. */ +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +#ifdef __STDC__ +#define POINTER void * +#else +#define POINTER char * +#endif + +/* Determine default alignment. */ +struct fooalign {char x; double d;}; +#define DEFAULT_ALIGNMENT \ + ((PTR_INT_TYPE) ((char *)&((struct fooalign *) 0)->d - (char *)0)) +/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT. + But in fact it might be less smart and round addresses to as much as + DEFAULT_ROUNDING. So we prepare for it to do that. */ +union fooround {long x; double d;}; +#define DEFAULT_ROUNDING (sizeof (union fooround)) + +/* When we copy a long block of data, this is the unit to do it with. + On some machines, copying successive ints does not work; + in such a case, redefine COPYING_UNIT to `long' (if that works) + or `char' as a last resort. */ +#ifndef COPYING_UNIT +#define COPYING_UNIT int +#endif + +/* The non-GNU-C macros copy the obstack into this global variable + to avoid multiple evaluation. */ + +struct obstack *_obstack; + +/* Define a macro that either calls functions with the traditional malloc/free + calling interface, or calls functions with the mmalloc/mfree interface + (that adds an extra first argument), based on the state of use_extra_arg. + For free, do not use ?:, since some compilers, like the MIPS compilers, + do not allow (expr) ? void : void. */ + +#define CALL_CHUNKFUN(h, size) \ + (((h) -> use_extra_arg) \ + ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \ + : (*(h)->chunkfun) ((size))) + +#define CALL_FREEFUN(h, old_chunk) \ + do { \ + if ((h) -> use_extra_arg) \ + (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \ + else \ + (*(h)->freefun) ((old_chunk)); \ + } while (0) + + +/* Initialize an obstack H for use. Specify chunk size SIZE (0 means default). + Objects start on multiples of ALIGNMENT (0 means use default). + CHUNKFUN is the function to use to allocate chunks, + and FREEFUN the function to free them. */ + +void +_obstack_begin (h, size, alignment, chunkfun, freefun) + struct obstack *h; + int size; + int alignment; + POINTER (*chunkfun) (); + void (*freefun) (); +{ + register struct _obstack_chunk* chunk; /* points to new chunk */ + + if (alignment == 0) + alignment = DEFAULT_ALIGNMENT; + if (size == 0) + /* Default size is what GNU malloc can fit in a 4096-byte block. */ + { + /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc. + Use the values for range checking, because if range checking is off, + the extra bytes won't be missed terribly, but if range checking is on + and we used a larger request, a whole extra 4096 bytes would be + allocated. + + These number are irrelevant to the new GNU malloc. I suspect it is + less sensitive to the size of the request. */ + int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1)) + + 4 + DEFAULT_ROUNDING - 1) + & ~(DEFAULT_ROUNDING - 1)); + size = 4096 - extra; + } + + h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun; + h->freefun = freefun; + h->chunk_size = size; + h->alignment_mask = alignment - 1; + h->use_extra_arg = 0; + + chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size); + h->next_free = h->object_base = chunk->contents; + h->chunk_limit = chunk->limit + = (char *) chunk + h->chunk_size; + chunk->prev = 0; + /* The initial chunk now contains no empty object. */ + h->maybe_empty_object = 0; +} + +void +_obstack_begin_1 (h, size, alignment, chunkfun, freefun, arg) + struct obstack *h; + int size; + int alignment; + POINTER (*chunkfun) (); + void (*freefun) (); + POINTER arg; +{ + register struct _obstack_chunk* chunk; /* points to new chunk */ + + if (alignment == 0) + alignment = DEFAULT_ALIGNMENT; + if (size == 0) + /* Default size is what GNU malloc can fit in a 4096-byte block. */ + { + /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc. + Use the values for range checking, because if range checking is off, + the extra bytes won't be missed terribly, but if range checking is on + and we used a larger request, a whole extra 4096 bytes would be + allocated. + + These number are irrelevant to the new GNU malloc. I suspect it is + less sensitive to the size of the request. */ + int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1)) + + 4 + DEFAULT_ROUNDING - 1) + & ~(DEFAULT_ROUNDING - 1)); + size = 4096 - extra; + } + + h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun; + h->freefun = freefun; + h->chunk_size = size; + h->alignment_mask = alignment - 1; + h->extra_arg = arg; + h->use_extra_arg = 1; + + chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size); + h->next_free = h->object_base = chunk->contents; + h->chunk_limit = chunk->limit + = (char *) chunk + h->chunk_size; + chunk->prev = 0; + /* The initial chunk now contains no empty object. */ + h->maybe_empty_object = 0; +} + +/* Allocate a new current chunk for the obstack *H + on the assumption that LENGTH bytes need to be added + to the current object, or a new object of length LENGTH allocated. + Copies any partial object from the end of the old chunk + to the beginning of the new one. */ + +void +_obstack_newchunk (h, length) + struct obstack *h; + int length; +{ + register struct _obstack_chunk* old_chunk = h->chunk; + register struct _obstack_chunk* new_chunk; + register long new_size; + register int obj_size = h->next_free - h->object_base; + register int i; + int already; + + /* Compute size for new chunk. */ + new_size = (obj_size + length) + (obj_size >> 3) + 100; + if (new_size < h->chunk_size) + new_size = h->chunk_size; + + /* Allocate and initialize the new chunk. */ + new_chunk = h->chunk = CALL_CHUNKFUN (h, new_size); + new_chunk->prev = old_chunk; + new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size; + + /* Move the existing object to the new chunk. + Word at a time is fast and is safe if the object + is sufficiently aligned. */ + if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT) + { + for (i = obj_size / sizeof (COPYING_UNIT) - 1; + i >= 0; i--) + ((COPYING_UNIT *)new_chunk->contents)[i] + = ((COPYING_UNIT *)h->object_base)[i]; + /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT, + but that can cross a page boundary on a machine + which does not do strict alignment for COPYING_UNITS. */ + already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT); + } + else + already = 0; + /* Copy remaining bytes one by one. */ + for (i = already; i < obj_size; i++) + new_chunk->contents[i] = h->object_base[i]; + + /* If the object just copied was the only data in OLD_CHUNK, + free that chunk and remove it from the chain. + But not if that chunk might contain an empty object. */ + if (h->object_base == old_chunk->contents && ! h->maybe_empty_object) + { + new_chunk->prev = old_chunk->prev; + CALL_FREEFUN (h, old_chunk); + } + + h->object_base = new_chunk->contents; + h->next_free = h->object_base + obj_size; + /* The new chunk certainly contains no empty object yet. */ + h->maybe_empty_object = 0; +} + +/* Return nonzero if object OBJ has been allocated from obstack H. + This is here for debugging. + If you use it in a program, you are probably losing. */ + +int +_obstack_allocated_p (h, obj) + struct obstack *h; + POINTER obj; +{ + register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */ + register struct _obstack_chunk* plp; /* point to previous chunk if any */ + + lp = (h)->chunk; + /* We use >= rather than > since the object cannot be exactly at + the beginning of the chunk but might be an empty object exactly + at the end of an adjacent chunk. */ + while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj)) + { + plp = lp->prev; + lp = plp; + } + return lp != 0; +} + +/* Free objects in obstack H, including OBJ and everything allocate + more recently than OBJ. If OBJ is zero, free everything in H. */ + +#undef obstack_free + +/* This function has two names with identical definitions. + This is the first one, called from non-ANSI code. */ + +void +_obstack_free (h, obj) + struct obstack *h; + POINTER obj; +{ + register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */ + register struct _obstack_chunk* plp; /* point to previous chunk if any */ + + lp = h->chunk; + /* We use >= because there cannot be an object at the beginning of a chunk. + But there can be an empty object at that address + at the end of another chunk. */ + while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj)) + { + plp = lp->prev; + CALL_FREEFUN (h, lp); + lp = plp; + /* If we switch chunks, we can't tell whether the new current + chunk contains an empty object, so assume that it may. */ + h->maybe_empty_object = 1; + } + if (lp) + { + h->object_base = h->next_free = (char *)(obj); + h->chunk_limit = lp->limit; + h->chunk = lp; + } + else if (obj != 0) + /* obj is not in any of the chunks! */ + abort (); +} + +/* This function is used from ANSI code. */ + +void +obstack_free (h, obj) + struct obstack *h; + POINTER obj; +{ + register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */ + register struct _obstack_chunk* plp; /* point to previous chunk if any */ + + lp = h->chunk; + /* We use >= because there cannot be an object at the beginning of a chunk. + But there can be an empty object at that address + at the end of another chunk. */ + while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj)) + { + plp = lp->prev; + CALL_FREEFUN (h, lp); + lp = plp; + /* If we switch chunks, we can't tell whether the new current + chunk contains an empty object, so assume that it may. */ + h->maybe_empty_object = 1; + } + if (lp) + { + h->object_base = h->next_free = (char *)(obj); + h->chunk_limit = lp->limit; + h->chunk = lp; + } + else if (obj != 0) + /* obj is not in any of the chunks! */ + abort (); +} + +#if 0 +/* These are now turned off because the applications do not use it + and it uses bcopy via obstack_grow, which causes trouble on sysV. */ + +/* Now define the functional versions of the obstack macros. + Define them to simply use the corresponding macros to do the job. */ + +#ifdef __STDC__ +/* These function definitions do not work with non-ANSI preprocessors; + they won't pass through the macro names in parentheses. */ + +/* The function names appear in parentheses in order to prevent + the macro-definitions of the names from being expanded there. */ + +POINTER (obstack_base) (obstack) + struct obstack *obstack; +{ + return obstack_base (obstack); +} + +POINTER (obstack_next_free) (obstack) + struct obstack *obstack; +{ + return obstack_next_free (obstack); +} + +int (obstack_object_size) (obstack) + struct obstack *obstack; +{ + return obstack_object_size (obstack); +} + +int (obstack_room) (obstack) + struct obstack *obstack; +{ + return obstack_room (obstack); +} + +void (obstack_grow) (obstack, pointer, length) + struct obstack *obstack; + POINTER pointer; + int length; +{ + obstack_grow (obstack, pointer, length); +} + +void (obstack_grow0) (obstack, pointer, length) + struct obstack *obstack; + POINTER pointer; + int length; +{ + obstack_grow0 (obstack, pointer, length); +} + +void (obstack_1grow) (obstack, character) + struct obstack *obstack; + int character; +{ + obstack_1grow (obstack, character); +} + +void (obstack_blank) (obstack, length) + struct obstack *obstack; + int length; +{ + obstack_blank (obstack, length); +} + +void (obstack_1grow_fast) (obstack, character) + struct obstack *obstack; + int character; +{ + obstack_1grow_fast (obstack, character); +} + +void (obstack_blank_fast) (obstack, length) + struct obstack *obstack; + int length; +{ + obstack_blank_fast (obstack, length); +} + +POINTER (obstack_finish) (obstack) + struct obstack *obstack; +{ + return obstack_finish (obstack); +} + +POINTER (obstack_alloc) (obstack, length) + struct obstack *obstack; + int length; +{ + return obstack_alloc (obstack, length); +} + +POINTER (obstack_copy) (obstack, pointer, length) + struct obstack *obstack; + POINTER pointer; + int length; +{ + return obstack_copy (obstack, pointer, length); +} + +POINTER (obstack_copy0) (obstack, pointer, length) + struct obstack *obstack; + POINTER pointer; + int length; +{ + return obstack_copy0 (obstack, pointer, length); +} + +#endif /* __STDC__ */ + +#endif /* 0 */ + +#endif /* _LIBC or not __GNU_LIBRARY__. */ diff --git a/gnu/usr.bin/grep/obstack.h b/gnu/usr.bin/grep/obstack.h new file mode 100644 index 0000000000..8a18e4566e --- /dev/null +++ b/gnu/usr.bin/grep/obstack.h @@ -0,0 +1,484 @@ +/* obstack.h - object stack macros + Copyright (C) 1988, 1992 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Summary: + +All the apparent functions defined here are macros. The idea +is that you would use these pre-tested macros to solve a +very specific set of problems, and they would run fast. +Caution: no side-effects in arguments please!! They may be +evaluated MANY times!! + +These macros operate a stack of objects. Each object starts life +small, and may grow to maturity. (Consider building a word syllable +by syllable.) An object can move while it is growing. Once it has +been "finished" it never changes address again. So the "top of the +stack" is typically an immature growing object, while the rest of the +stack is of mature, fixed size and fixed address objects. + +These routines grab large chunks of memory, using a function you +supply, called `obstack_chunk_alloc'. On occasion, they free chunks, +by calling `obstack_chunk_free'. You must define them and declare +them before using any obstack macros. + +Each independent stack is represented by a `struct obstack'. +Each of the obstack macros expects a pointer to such a structure +as the first argument. + +One motivation for this package is the problem of growing char strings +in symbol tables. Unless you are "fascist pig with a read-only mind" +--Gosper's immortal quote from HAKMEM item 154, out of context--you +would not like to put any arbitrary upper limit on the length of your +symbols. + +In practice this often means you will build many short symbols and a +few long symbols. At the time you are reading a symbol you don't know +how long it is. One traditional method is to read a symbol into a +buffer, realloc()ating the buffer every time you try to read a symbol +that is longer than the buffer. This is beaut, but you still will +want to copy the symbol from the buffer to a more permanent +symbol-table entry say about half the time. + +With obstacks, you can work differently. Use one obstack for all symbol +names. As you read a symbol, grow the name in the obstack gradually. +When the name is complete, finalize it. Then, if the symbol exists already, +free the newly read name. + +The way we do this is to take a large chunk, allocating memory from +low addresses. When you want to build a symbol in the chunk you just +add chars above the current "high water mark" in the chunk. When you +have finished adding chars, because you got to the end of the symbol, +you know how long the chars are, and you can create a new object. +Mostly the chars will not burst over the highest address of the chunk, +because you would typically expect a chunk to be (say) 100 times as +long as an average object. + +In case that isn't clear, when we have enough chars to make up +the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed) +so we just point to it where it lies. No moving of chars is +needed and this is the second win: potentially long strings need +never be explicitly shuffled. Once an object is formed, it does not +change its address during its lifetime. + +When the chars burst over a chunk boundary, we allocate a larger +chunk, and then copy the partly formed object from the end of the old +chunk to the beginning of the new larger chunk. We then carry on +accreting characters to the end of the object as we normally would. + +A special macro is provided to add a single char at a time to a +growing object. This allows the use of register variables, which +break the ordinary 'growth' macro. + +Summary: + We allocate large chunks. + We carve out one object at a time from the current chunk. + Once carved, an object never moves. + We are free to append data of any size to the currently + growing object. + Exactly one object is growing in an obstack at any one time. + You can run one obstack per control block. + You may have as many control blocks as you dare. + Because of the way we do it, you can `unwind' an obstack + back to a previous state. (You may remove objects much + as you would with a stack.) +*/ + + +/* Don't do the contents of this file more than once. */ + +#ifndef __OBSTACKS__ +#define __OBSTACKS__ + +/* We use subtraction of (char *)0 instead of casting to int + because on word-addressable machines a simple cast to int + may ignore the byte-within-word field of the pointer. */ + +#ifndef __PTR_TO_INT +#define __PTR_TO_INT(P) ((P) - (char *)0) +#endif + +#ifndef __INT_TO_PTR +#define __INT_TO_PTR(P) ((P) + (char *)0) +#endif + +/* We need the type of the resulting object. In ANSI C it is ptrdiff_t + but in traditional C it is usually long. If we are in ANSI C and + don't already have ptrdiff_t get it. */ + +#if defined (__STDC__) && ! defined (offsetof) +#if defined (__GNUC__) && defined (IN_GCC) +/* On Next machine, the system's stddef.h screws up if included + after we have defined just ptrdiff_t, so include all of gstddef.h. + Otherwise, define just ptrdiff_t, which is all we need. */ +#ifndef __NeXT__ +#define __need_ptrdiff_t +#endif + +/* While building GCC, the stddef.h that goes with GCC has this name. */ +#include "gstddef.h" +#else +#include +#endif +#endif + +#ifdef __STDC__ +#define PTR_INT_TYPE ptrdiff_t +#else +#define PTR_INT_TYPE long +#endif + +struct _obstack_chunk /* Lives at front of each chunk. */ +{ + char *limit; /* 1 past end of this chunk */ + struct _obstack_chunk *prev; /* address of prior chunk or NULL */ + char contents[4]; /* objects begin here */ +}; + +struct obstack /* control current object in current chunk */ +{ + long chunk_size; /* preferred size to allocate chunks in */ + struct _obstack_chunk* chunk; /* address of current struct obstack_chunk */ + char *object_base; /* address of object we are building */ + char *next_free; /* where to add next char to current object */ + char *chunk_limit; /* address of char after current chunk */ + PTR_INT_TYPE temp; /* Temporary for some macros. */ + int alignment_mask; /* Mask of alignment for each object. */ + struct _obstack_chunk *(*chunkfun) (); /* User's fcn to allocate a chunk. */ + void (*freefun) (); /* User's function to free a chunk. */ + char *extra_arg; /* first arg for chunk alloc/dealloc funcs */ + unsigned use_extra_arg:1; /* chunk alloc/dealloc funcs take extra arg */ + unsigned maybe_empty_object:1;/* There is a possibility that the current + chunk contains a zero-length object. This + prevents freeing the chunk if we allocate + a bigger chunk to replace it. */ +}; + +/* Declare the external functions we use; they are in obstack.c. */ + +#ifdef __STDC__ +extern void _obstack_newchunk (struct obstack *, int); +extern void _obstack_free (struct obstack *, void *); +extern void _obstack_begin (struct obstack *, int, int, + void *(*) (), void (*) ()); +extern void _obstack_begin_1 (struct obstack *, int, int, + void *(*) (), void (*) (), void *); +#else +extern void _obstack_newchunk (); +extern void _obstack_free (); +extern void _obstack_begin (); +extern void _obstack_begin_1 (); +#endif + +#ifdef __STDC__ + +/* Do the function-declarations after the structs + but before defining the macros. */ + +void obstack_init (struct obstack *obstack); + +void * obstack_alloc (struct obstack *obstack, int size); + +void * obstack_copy (struct obstack *obstack, void *address, int size); +void * obstack_copy0 (struct obstack *obstack, void *address, int size); + +void obstack_free (struct obstack *obstack, void *block); + +void obstack_blank (struct obstack *obstack, int size); + +void obstack_grow (struct obstack *obstack, void *data, int size); +void obstack_grow0 (struct obstack *obstack, void *data, int size); + +void obstack_1grow (struct obstack *obstack, int data_char); +void obstack_ptr_grow (struct obstack *obstack, void *data); +void obstack_int_grow (struct obstack *obstack, int data); + +void * obstack_finish (struct obstack *obstack); + +int obstack_object_size (struct obstack *obstack); + +int obstack_room (struct obstack *obstack); +void obstack_1grow_fast (struct obstack *obstack, int data_char); +void obstack_ptr_grow_fast (struct obstack *obstack, void *data); +void obstack_int_grow_fast (struct obstack *obstack, int data); +void obstack_blank_fast (struct obstack *obstack, int size); + +void * obstack_base (struct obstack *obstack); +void * obstack_next_free (struct obstack *obstack); +int obstack_alignment_mask (struct obstack *obstack); +int obstack_chunk_size (struct obstack *obstack); + +#endif /* __STDC__ */ + +/* Non-ANSI C cannot really support alternative functions for these macros, + so we do not declare them. */ + +/* Pointer to beginning of object being allocated or to be allocated next. + Note that this might not be the final address of the object + because a new chunk might be needed to hold the final size. */ + +#define obstack_base(h) ((h)->object_base) + +/* Size for allocating ordinary chunks. */ + +#define obstack_chunk_size(h) ((h)->chunk_size) + +/* Pointer to next byte not yet allocated in current chunk. */ + +#define obstack_next_free(h) ((h)->next_free) + +/* Mask specifying low bits that should be clear in address of an object. */ + +#define obstack_alignment_mask(h) ((h)->alignment_mask) + +#define obstack_init(h) \ + _obstack_begin ((h), 0, 0, \ + (void *(*) ()) obstack_chunk_alloc, (void (*) ()) obstack_chunk_free) + +#define obstack_begin(h, size) \ + _obstack_begin ((h), (size), 0, \ + (void *(*) ()) obstack_chunk_alloc, (void (*) ()) obstack_chunk_free) + +#define obstack_specify_allocation(h, size, alignment, chunkfun, freefun) \ + _obstack_begin ((h), (size), (alignment), \ + (void *(*) ()) (chunkfun), (void (*) ()) (freefun)) + +#define obstack_specify_allocation_with_arg(h, size, alignment, chunkfun, freefun, arg) \ + _obstack_begin_1 ((h), (size), (alignment), \ + (void *(*) ()) (chunkfun), (void (*) ()) (freefun), (arg)) + +#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = achar) + +#define obstack_blank_fast(h,n) ((h)->next_free += (n)) + +#if defined (__GNUC__) && defined (__STDC__) +#if __GNUC__ < 2 || defined(NeXT) +#define __extension__ +#endif + +/* For GNU C, if not -traditional, + we can define these macros to compute all args only once + without using a global variable. + Also, we can avoid using the `temp' slot, to make faster code. */ + +#define obstack_object_size(OBSTACK) \ + __extension__ \ + ({ struct obstack *__o = (OBSTACK); \ + (unsigned) (__o->next_free - __o->object_base); }) + +#define obstack_room(OBSTACK) \ + __extension__ \ + ({ struct obstack *__o = (OBSTACK); \ + (unsigned) (__o->chunk_limit - __o->next_free); }) + +/* Note that the call to _obstack_newchunk is enclosed in (..., 0) + so that we can avoid having void expressions + in the arms of the conditional expression. + Casting the third operand to void was tried before, + but some compilers won't accept it. */ +#define obstack_grow(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + ((__o->next_free + __len > __o->chunk_limit) \ + ? (_obstack_newchunk (__o, __len), 0) : 0); \ + bcopy (where, __o->next_free, __len); \ + __o->next_free += __len; \ + (void) 0; }) + +#define obstack_grow0(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + ((__o->next_free + __len + 1 > __o->chunk_limit) \ + ? (_obstack_newchunk (__o, __len + 1), 0) : 0), \ + bcopy (where, __o->next_free, __len), \ + __o->next_free += __len, \ + *(__o->next_free)++ = 0; \ + (void) 0; }) + +#define obstack_1grow(OBSTACK,datum) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + ((__o->next_free + 1 > __o->chunk_limit) \ + ? (_obstack_newchunk (__o, 1), 0) : 0), \ + *(__o->next_free)++ = (datum); \ + (void) 0; }) + +/* These assume that the obstack alignment is good enough for pointers or ints, + and that the data added so far to the current object + shares that much alignment. */ + +#define obstack_ptr_grow(OBSTACK,datum) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + ((__o->next_free + sizeof (void *) > __o->chunk_limit) \ + ? (_obstack_newchunk (__o, sizeof (void *)), 0) : 0), \ + *((void **)__o->next_free)++ = ((void *)datum); \ + (void) 0; }) + +#define obstack_int_grow(OBSTACK,datum) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + ((__o->next_free + sizeof (int) > __o->chunk_limit) \ + ? (_obstack_newchunk (__o, sizeof (int)), 0) : 0), \ + *((int *)__o->next_free)++ = ((int)datum); \ + (void) 0; }) + +#define obstack_ptr_grow_fast(h,aptr) (*((void **)(h)->next_free)++ = (void *)aptr) +#define obstack_int_grow_fast(h,aint) (*((int *)(h)->next_free)++ = (int)aint) + +#define obstack_blank(OBSTACK,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + ((__o->chunk_limit - __o->next_free < __len) \ + ? (_obstack_newchunk (__o, __len), 0) : 0); \ + __o->next_free += __len; \ + (void) 0; }) + +#define obstack_alloc(OBSTACK,length) \ +__extension__ \ +({ struct obstack *__h = (OBSTACK); \ + obstack_blank (__h, (length)); \ + obstack_finish (__h); }) + +#define obstack_copy(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__h = (OBSTACK); \ + obstack_grow (__h, (where), (length)); \ + obstack_finish (__h); }) + +#define obstack_copy0(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__h = (OBSTACK); \ + obstack_grow0 (__h, (where), (length)); \ + obstack_finish (__h); }) + +/* The local variable is named __o1 to avoid a name conflict + when obstack_blank is called. */ +#define obstack_finish(OBSTACK) \ +__extension__ \ +({ struct obstack *__o1 = (OBSTACK); \ + void *value = (void *) __o1->object_base; \ + if (__o1->next_free == value) \ + __o1->maybe_empty_object = 1; \ + __o1->next_free \ + = __INT_TO_PTR ((__PTR_TO_INT (__o1->next_free)+__o1->alignment_mask)\ + & ~ (__o1->alignment_mask)); \ + ((__o1->next_free - (char *)__o1->chunk \ + > __o1->chunk_limit - (char *)__o1->chunk) \ + ? (__o1->next_free = __o1->chunk_limit) : 0); \ + __o1->object_base = __o1->next_free; \ + value; }) + +#define obstack_free(OBSTACK, OBJ) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + void *__obj = (OBJ); \ + if (__obj > (void *)__o->chunk && __obj < (void *)__o->chunk_limit) \ + __o->next_free = __o->object_base = __obj; \ + else (obstack_free) (__o, __obj); }) + +#else /* not __GNUC__ or not __STDC__ */ + +#define obstack_object_size(h) \ + (unsigned) ((h)->next_free - (h)->object_base) + +#define obstack_room(h) \ + (unsigned) ((h)->chunk_limit - (h)->next_free) + +#define obstack_grow(h,where,length) \ +( (h)->temp = (length), \ + (((h)->next_free + (h)->temp > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp), 0) : 0), \ + bcopy (where, (h)->next_free, (h)->temp), \ + (h)->next_free += (h)->temp) + +#define obstack_grow0(h,where,length) \ +( (h)->temp = (length), \ + (((h)->next_free + (h)->temp + 1 > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp + 1), 0) : 0), \ + bcopy (where, (h)->next_free, (h)->temp), \ + (h)->next_free += (h)->temp, \ + *((h)->next_free)++ = 0) + +#define obstack_1grow(h,datum) \ +( (((h)->next_free + 1 > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), 1), 0) : 0), \ + *((h)->next_free)++ = (datum)) + +#define obstack_ptr_grow(h,datum) \ +( (((h)->next_free + sizeof (char *) > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), sizeof (char *)), 0) : 0), \ + *((char **)(((h)->next_free+=sizeof(char *))-sizeof(char *))) = ((char *)datum)) + +#define obstack_int_grow(h,datum) \ +( (((h)->next_free + sizeof (int) > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), sizeof (int)), 0) : 0), \ + *((int *)(((h)->next_free+=sizeof(int))-sizeof(int))) = ((int)datum)) + +#define obstack_ptr_grow_fast(h,aptr) (*((char **)(h)->next_free)++ = (char *)aptr) +#define obstack_int_grow_fast(h,aint) (*((int *)(h)->next_free)++ = (int)aint) + +#define obstack_blank(h,length) \ +( (h)->temp = (length), \ + (((h)->chunk_limit - (h)->next_free < (h)->temp) \ + ? (_obstack_newchunk ((h), (h)->temp), 0) : 0), \ + (h)->next_free += (h)->temp) + +#define obstack_alloc(h,length) \ + (obstack_blank ((h), (length)), obstack_finish ((h))) + +#define obstack_copy(h,where,length) \ + (obstack_grow ((h), (where), (length)), obstack_finish ((h))) + +#define obstack_copy0(h,where,length) \ + (obstack_grow0 ((h), (where), (length)), obstack_finish ((h))) + +#define obstack_finish(h) \ +( ((h)->next_free == (h)->object_base \ + ? (((h)->maybe_empty_object = 1), 0) \ + : 0), \ + (h)->temp = __PTR_TO_INT ((h)->object_base), \ + (h)->next_free \ + = __INT_TO_PTR ((__PTR_TO_INT ((h)->next_free)+(h)->alignment_mask) \ + & ~ ((h)->alignment_mask)), \ + (((h)->next_free - (char *)(h)->chunk \ + > (h)->chunk_limit - (char *)(h)->chunk) \ + ? ((h)->next_free = (h)->chunk_limit) : 0), \ + (h)->object_base = (h)->next_free, \ + __INT_TO_PTR ((h)->temp)) + +#ifdef __STDC__ +#define obstack_free(h,obj) \ +( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \ + (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\ + ? (int) ((h)->next_free = (h)->object_base \ + = (h)->temp + (char *) (h)->chunk) \ + : (((obstack_free) ((h), (h)->temp + (char *) (h)->chunk), 0), 0))) +#else +#define obstack_free(h,obj) \ +( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \ + (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\ + ? (int) ((h)->next_free = (h)->object_base \ + = (h)->temp + (char *) (h)->chunk) \ + : (_obstack_free ((h), (h)->temp + (char *) (h)->chunk), 0))) +#endif + +#endif /* not __GNUC__ or not __STDC__ */ + +#endif /* not __OBSTACKS__ */ diff --git a/gnu/usr.bin/grep/regex.c b/gnu/usr.bin/grep/regex.c index d69e78438c..e8b588207d 100644 --- a/gnu/usr.bin/grep/regex.c +++ b/gnu/usr.bin/grep/regex.c @@ -1,5 +1,9 @@ -/* Extended regular expression matching and search library. - Copyright (C) 1985, 1989 Free Software Foundation, Inc. +/* Extended regular expression matching and search library, + version 0.12. + (Implements POSIX draft P10003.2/D11.2, except for + internationalization features.) + + Copyright (C) 1993 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -13,84 +17,78 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - In other words, you are welcome to use, share and improve this program. - You are forbidden to forbid anyone else to use, share and improve - what you give them. Help stamp out software-hoarding! */ +/* AIX requires this to be the first thing in the file. */ +#if defined (_AIX) && !defined (REGEX_MALLOC) + #pragma alloca +#endif +#define _GNU_SOURCE -/* To test, compile with -Dtest. - This Dtestable feature turns this into a self-contained program - which reads a pattern, describes how it compiles, - then reads a string and searches for it. */ +/* We need this for `regex.h', and perhaps for the Emacs include files. */ +#include -/* AIX requires this to be the first thing in the file. */ -#ifdef __GNUC__ -#undef alloca -#define alloca __builtin_alloca -#else /* not __GNUC__ */ -#if defined(sparc) && !defined(USG) && !defined(SVR4) && !defined(__svr4__) -#include -#else -#ifdef _AIX - #pragma alloca -#else -char *alloca (); +#ifdef HAVE_CONFIG_H +#include "config.h" #endif -#endif /* sparc */ -#endif /* not __GNUC__ */ - +/* The `emacs' switch turns on certain matching commands + that make sense only in Emacs. */ #ifdef emacs -/* The `emacs' switch turns on certain special matching commands - that make sense only in emacs. */ - -#include "config.h" #include "lisp.h" #include "buffer.h" #include "syntax.h" +/* Emacs uses `NULL' as a predicate. */ +#undef NULL + #else /* not emacs */ -#if defined(USG) || defined(STDC_HEADERS) +/* We used to test for `BSTRING' here, but only GCC and Emacs define + `BSTRING', as far as I know, and neither of them use this code. */ +#if HAVE_STRING_H || STDC_HEADERS #include -#ifndef bcopy -#define bcopy(s,d,n) memcpy((d),(s),(n)) -#endif #ifndef bcmp -#define bcmp(s1,s2,n) memcmp((s1),(s2),(n)) +#define bcmp(s1, s2, n) memcmp ((s1), (s2), (n)) +#endif +#ifndef bcopy +#define bcopy(s, d, n) memcpy ((d), (s), (n)) #endif #ifndef bzero -#define bzero(s,n) memset((s),0,(n)) +#define bzero(s, n) memset ((s), 0, (n)) #endif +#else +#include #endif #ifdef STDC_HEADERS #include #else char *malloc (); +char *realloc (); #endif -/* - * Define the syntax stuff, so we can do the \<...\> things. - */ -#ifndef Sword /* must be non-zero in some of the tests below... */ +/* Define the syntax stuff for \<, \>, etc. */ + +/* This must be nonzero for the wordchar and notwordchar pattern + commands in re_match_2. */ +#ifndef Sword #define Sword 1 #endif -#define SYNTAX(c) re_syntax_table[c] - #ifdef SYNTAX_TABLE -char *re_syntax_table; +extern char *re_syntax_table; -#else +#else /* not SYNTAX_TABLE */ -static char re_syntax_table[256]; +/* How many characters in the character set. */ +#define CHAR_SET_SIZE 256 + +static char re_syntax_table[CHAR_SET_SIZE]; static void init_syntax_once () @@ -112,850 +110,2833 @@ init_syntax_once () for (c = '0'; c <= '9'; c++) re_syntax_table[c] = Sword; + re_syntax_table['_'] = Sword; + done = 1; } -#endif /* SYNTAX_TABLE */ -#endif /* not emacs */ - -#include "regex.h" - -/* Number of failure points to allocate space for initially, - when matching. If this number is exceeded, more space is allocated, - so it is not a hard limit. */ +#endif /* not SYNTAX_TABLE */ -#ifndef NFAILURES -#define NFAILURES 80 -#endif /* NFAILURES */ +#define SYNTAX(c) re_syntax_table[c] -/* width of a byte in bits */ +#endif /* not emacs */ + +/* Get the interface, including the syntax bits. */ +#include "regex.h" -#define BYTEWIDTH 8 +/* isalpha etc. are used for the character classes. */ +#include + +/* Jim Meyering writes: + + "... Some ctype macros are valid only for character codes that + isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when + using /bin/cc or gcc but without giving an ansi option). So, all + ctype uses should be through macros like ISPRINT... If + STDC_HEADERS is defined, then autoconf has verified that the ctype + macros don't need to be guarded with references to isascii. ... + Defining isascii to 1 should let any compiler worth its salt + eliminate the && through constant folding." */ +#if ! defined (isascii) || defined (STDC_HEADERS) +#undef isascii +#define isascii(c) 1 +#endif -#ifndef SIGN_EXTEND_CHAR -#ifdef __CHAR_UNSIGNED__ -#define SIGN_EXTEND_CHAR(c) ((c)>(char)127?(c)-256:(c)) +#ifdef isblank +#define ISBLANK(c) (isascii (c) && isblank (c)) +#else +#define ISBLANK(c) ((c) == ' ' || (c) == '\t') +#endif +#ifdef isgraph +#define ISGRAPH(c) (isascii (c) && isgraph (c)) #else -#define SIGN_EXTEND_CHAR(x) (x) +#define ISGRAPH(c) (isascii (c) && isprint (c) && !isspace (c)) +#endif + +#define ISPRINT(c) (isascii (c) && isprint (c)) +#define ISDIGIT(c) (isascii (c) && isdigit (c)) +#define ISALNUM(c) (isascii (c) && isalnum (c)) +#define ISALPHA(c) (isascii (c) && isalpha (c)) +#define ISCNTRL(c) (isascii (c) && iscntrl (c)) +#define ISLOWER(c) (isascii (c) && islower (c)) +#define ISPUNCT(c) (isascii (c) && ispunct (c)) +#define ISSPACE(c) (isascii (c) && isspace (c)) +#define ISUPPER(c) (isascii (c) && isupper (c)) +#define ISXDIGIT(c) (isascii (c) && isxdigit (c)) + +#ifndef NULL +#define NULL 0 #endif + +/* We remove any previous definition of `SIGN_EXTEND_CHAR', + since ours (we hope) works properly with all combinations of + machines, compilers, `char' and `unsigned char' argument types. + (Per Bothner suggested the basic approach.) */ +#undef SIGN_EXTEND_CHAR +#if __STDC__ +#define SIGN_EXTEND_CHAR(c) ((signed char) (c)) +#else /* not __STDC__ */ +/* As in Harbison and Steele. */ +#define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128) #endif -static int obscure_syntax = 0; +/* Should we use malloc or alloca? If REGEX_MALLOC is not defined, we + use `alloca' instead of `malloc'. This is because using malloc in + re_search* or re_match* could cause memory leaks when C-g is used in + Emacs; also, malloc is slower and causes storage fragmentation. On + the other hand, malloc is more portable, and easier to debug. + + Because we sometimes use alloca, some routines have to be macros, + not functions -- `alloca'-allocated space disappears at the end of the + function it is called in. */ -/* Specify the precise syntax of regexp for compilation. - This provides for compatibility for various utilities - which historically have different, incompatible syntaxes. +#ifdef REGEX_MALLOC - The argument SYNTAX is a bit-mask containing the two bits - RE_NO_BK_PARENS and RE_NO_BK_VBAR. */ +#define REGEX_ALLOCATE malloc +#define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize) -int -re_set_syntax (syntax) - int syntax; +#else /* not REGEX_MALLOC */ + +/* Emacs already defines alloca, sometimes. */ +#ifndef alloca + +/* Make alloca work the best possible way. */ +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else /* not __GNUC__ */ +#if HAVE_ALLOCA_H +#include +#else /* not __GNUC__ or HAVE_ALLOCA_H */ +#ifndef _AIX /* Already did AIX, up at the top. */ +char *alloca (); +#endif /* not _AIX */ +#endif /* not HAVE_ALLOCA_H */ +#endif /* not __GNUC__ */ + +#endif /* not alloca */ + +#define REGEX_ALLOCATE alloca + +/* Assumes a `char *destination' variable. */ +#define REGEX_REALLOCATE(source, osize, nsize) \ + (destination = (char *) alloca (nsize), \ + bcopy (source, destination, osize), \ + destination) + +#endif /* not REGEX_MALLOC */ + + +/* True if `size1' is non-NULL and PTR is pointing anywhere inside + `string1' or just past its end. This works if PTR is NULL, which is + a good thing. */ +#define FIRST_STRING_P(ptr) \ + (size1 && string1 <= (ptr) && (ptr) <= string1 + size1) + +/* (Re)Allocate N items of type T using malloc, or fail. */ +#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t))) +#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t))) +#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t))) + +#define BYTEWIDTH 8 /* In bits. */ + +#define STREQ(s1, s2) ((strcmp (s1, s2) == 0)) + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +typedef char boolean; +#define false 0 +#define true 1 + +/* These are the command codes that appear in compiled regular + expressions. Some opcodes are followed by argument bytes. A + command code can specify any interpretation whatsoever for its + arguments. Zero bytes may appear in the compiled regular expression. + + The value of `exactn' is needed in search.c (search_buffer) in Emacs. + So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of + `exactn' we use here must also be 1. */ + +typedef enum { - int ret; + no_op = 0, + + /* Followed by one byte giving n, then by n literal bytes. */ + exactn = 1, + + /* Matches any (more or less) character. */ + anychar, + + /* Matches any one char belonging to specified set. First + following byte is number of bitmap bytes. Then come bytes + for a bitmap saying which chars are in. Bits in each byte + are ordered low-bit-first. A character is in the set if its + bit is 1. A character too large to have a bit in the map is + automatically not in the set. */ + charset, + + /* Same parameters as charset, but match any character that is + not one of those specified. */ + charset_not, + + /* Start remembering the text that is matched, for storing in a + register. Followed by one byte with the register number, in + the range 0 to one less than the pattern buffer's re_nsub + field. Then followed by one byte with the number of groups + inner to this one. (This last has to be part of the + start_memory only because we need it in the on_failure_jump + of re_match_2.) */ + start_memory, + + /* Stop remembering the text that is matched and store it in a + memory register. Followed by one byte with the register + number, in the range 0 to one less than `re_nsub' in the + pattern buffer, and one byte with the number of inner groups, + just like `start_memory'. (We need the number of inner + groups here because we don't have any easy way of finding the + corresponding start_memory when we're at a stop_memory.) */ + stop_memory, + + /* Match a duplicate of something remembered. Followed by one + byte containing the register number. */ + duplicate, + + /* Fail unless at beginning of line. */ + begline, + + /* Fail unless at end of line. */ + endline, + + /* Succeeds if at beginning of buffer (if emacs) or at beginning + of string to be matched (if not). */ + begbuf, + + /* Analogously, for end of buffer/string. */ + endbuf, + + /* Followed by two byte relative address to which to jump. */ + jump, + + /* Same as jump, but marks the end of an alternative. */ + jump_past_alt, + + /* Followed by two-byte relative address of place to resume at + in case of failure. */ + on_failure_jump, + + /* Like on_failure_jump, but pushes a placeholder instead of the + current string position when executed. */ + on_failure_keep_string_jump, + + /* Throw away latest failure point and then jump to following + two-byte relative address. */ + pop_failure_jump, + + /* Change to pop_failure_jump if know won't have to backtrack to + match; otherwise change to jump. This is used to jump + back to the beginning of a repeat. If what follows this jump + clearly won't match what the repeat does, such that we can be + sure that there is no use backtracking out of repetitions + already matched, then we change it to a pop_failure_jump. + Followed by two-byte address. */ + maybe_pop_jump, + + /* Jump to following two-byte address, and push a dummy failure + point. This failure point will be thrown away if an attempt + is made to use it for a failure. A `+' construct makes this + before the first repeat. Also used as an intermediary kind + of jump when compiling an alternative. */ + dummy_failure_jump, + + /* Push a dummy failure point and continue. Used at the end of + alternatives. */ + push_dummy_failure, + + /* Followed by two-byte relative address and two-byte number n. + After matching N times, jump to the address upon failure. */ + succeed_n, + + /* Followed by two-byte relative address, and two-byte number n. + Jump to the address N times, then fail. */ + jump_n, + + /* Set the following two-byte relative address to the + subsequent two-byte number. The address *includes* the two + bytes of number. */ + set_number_at, + + wordchar, /* Matches any word-constituent character. */ + notwordchar, /* Matches any char that is not a word-constituent. */ + + wordbeg, /* Succeeds if at word beginning. */ + wordend, /* Succeeds if at word end. */ + + wordbound, /* Succeeds if at a word boundary. */ + notwordbound /* Succeeds if not at a word boundary. */ - ret = obscure_syntax; - obscure_syntax = syntax; - return ret; -} +#ifdef emacs + ,before_dot, /* Succeeds if before point. */ + at_dot, /* Succeeds if at point. */ + after_dot, /* Succeeds if after point. */ + + /* Matches any character whose syntax is specified. Followed by + a byte which contains a syntax code, e.g., Sword. */ + syntaxspec, + + /* Matches any character whose syntax is not that specified. */ + notsyntaxspec +#endif /* emacs */ +} re_opcode_t; -/* re_compile_pattern takes a regular-expression string - and converts it into a buffer full of byte commands for matching. - - PATTERN is the address of the pattern string - SIZE is the length of it. - BUFP is a struct re_pattern_buffer * which points to the info - on where to store the byte commands. - This structure contains a char * which points to the - actual space, which should have been obtained with malloc. - re_compile_pattern may use realloc to grow the buffer space. - - The number of bytes of commands can be found out by looking in - the struct re_pattern_buffer that bufp pointed to, - after re_compile_pattern returns. -*/ +/* Common operations on the compiled pattern. */ -#define PATPUSH(ch) (*b++ = (char) (ch)) +/* Store NUMBER in two contiguous bytes starting at DESTINATION. */ -#define PATFETCH(c) \ - {if (p == pend) goto end_of_pattern; \ - c = * (unsigned char *) p++; \ - if (translate) c = translate[c]; } +#define STORE_NUMBER(destination, number) \ + do { \ + (destination)[0] = (number) & 0377; \ + (destination)[1] = (number) >> 8; \ + } while (0) -#define PATFETCH_RAW(c) \ - {if (p == pend) goto end_of_pattern; \ - c = * (unsigned char *) p++; } +/* Same as STORE_NUMBER, except increment DESTINATION to + the byte after where the number is stored. Therefore, DESTINATION + must be an lvalue. */ -#define PATUNFETCH p-- +#define STORE_NUMBER_AND_INCR(destination, number) \ + do { \ + STORE_NUMBER (destination, number); \ + (destination) += 2; \ + } while (0) -#define EXTEND_BUFFER \ - { char *old_buffer = bufp->buffer; \ - if (bufp->allocated == (1<<16)) goto too_big; \ - bufp->allocated *= 2; \ - if (bufp->allocated > (1<<16)) bufp->allocated = (1<<16); \ - if (!(bufp->buffer = (char *) realloc (bufp->buffer, bufp->allocated))) \ - goto memory_exhausted; \ - c = bufp->buffer - old_buffer; \ - b += c; \ - if (fixup_jump) \ - fixup_jump += c; \ - if (laststart) \ - laststart += c; \ - begalt += c; \ - if (pending_exact) \ - pending_exact += c; \ - } +/* Put into DESTINATION a number stored in two contiguous bytes starting + at SOURCE. */ -static void store_jump (), insert_jump (); +#define EXTRACT_NUMBER(destination, source) \ + do { \ + (destination) = *(source) & 0377; \ + (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8; \ + } while (0) -char * -re_compile_pattern (pattern, size, bufp) - char *pattern; - int size; - struct re_pattern_buffer *bufp; +#ifdef DEBUG +static void +extract_number (dest, source) + int *dest; + unsigned char *source; { - register char *b = bufp->buffer; - register char *p = pattern; - char *pend = pattern + size; - register unsigned c, c1; - char *p1; - unsigned char *translate = (unsigned char *) bufp->translate; + int temp = SIGN_EXTEND_CHAR (*(source + 1)); + *dest = *source & 0377; + *dest += temp << 8; +} - /* address of the count-byte of the most recently inserted "exactn" command. - This makes it possible to tell whether a new exact-match character - can be added to that command or requires a new "exactn" command. */ - - char *pending_exact = 0; +#ifndef EXTRACT_MACROS /* To debug the macros. */ +#undef EXTRACT_NUMBER +#define EXTRACT_NUMBER(dest, src) extract_number (&dest, src) +#endif /* not EXTRACT_MACROS */ - /* address of the place where a forward-jump should go - to the end of the containing expression. - Each alternative of an "or", except the last, ends with a forward-jump - of this sort. */ +#endif /* DEBUG */ - char *fixup_jump = 0; +/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number. + SOURCE must be an lvalue. */ - /* address of start of the most recently finished expression. - This tells postfix * where to find the start of its operand. */ +#define EXTRACT_NUMBER_AND_INCR(destination, source) \ + do { \ + EXTRACT_NUMBER (destination, source); \ + (source) += 2; \ + } while (0) - char *laststart = 0; +#ifdef DEBUG +static void +extract_number_and_incr (destination, source) + int *destination; + unsigned char **source; +{ + extract_number (destination, *source); + *source += 2; +} - /* In processing a repeat, 1 means zero matches is allowed */ +#ifndef EXTRACT_MACROS +#undef EXTRACT_NUMBER_AND_INCR +#define EXTRACT_NUMBER_AND_INCR(dest, src) \ + extract_number_and_incr (&dest, &src) +#endif /* not EXTRACT_MACROS */ - char zero_times_ok; +#endif /* DEBUG */ + +/* If DEBUG is defined, Regex prints many voluminous messages about what + it is doing (if the variable `debug' is nonzero). If linked with the + main program in `iregex.c', you can enter patterns and strings + interactively. And if linked with the main program in `main.c' and + the other test files, you can run the already-written tests. */ - /* In processing a repeat, 1 means many matches is allowed */ +#ifdef DEBUG - char many_times_ok; +/* We use standard I/O for debugging. */ +#include - /* address of beginning of regexp, or inside of last \( */ +/* It is useful to test things that ``must'' be true when debugging. */ +#include - char *begalt = b; +static int debug = 0; - /* Stack of information saved by \( and restored by \). - Four stack elements are pushed by each \(: - First, the value of b. - Second, the value of fixup_jump. - Third, the value of regnum. - Fourth, the value of begalt. */ +#define DEBUG_STATEMENT(e) e +#define DEBUG_PRINT1(x) if (debug) printf (x) +#define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2) +#define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3) +#define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4) +#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) \ + if (debug) print_partial_compiled_pattern (s, e) +#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) \ + if (debug) print_double_string (w, s1, sz1, s2, sz2) - int stackb[40]; - int *stackp = stackb; - int *stacke = stackb + 40; - int *stackt; - /* Counts \('s as they are encountered. Remembered for the matching \), - where it becomes the "register number" to put in the stop_memory command */ +extern void printchar (); - int regnum = 1; +/* Print the fastmap in human-readable form. */ - bufp->fastmap_accurate = 0; +void +print_fastmap (fastmap) + char *fastmap; +{ + unsigned was_a_range = 0; + unsigned i = 0; + + while (i < (1 << BYTEWIDTH)) + { + if (fastmap[i++]) + { + was_a_range = 0; + printchar (i - 1); + while (i < (1 << BYTEWIDTH) && fastmap[i]) + { + was_a_range = 1; + i++; + } + if (was_a_range) + { + printf ("-"); + printchar (i - 1); + } + } + } + putchar ('\n'); +} -#ifndef emacs -#ifndef SYNTAX_TABLE - /* - * Initialize the syntax table. - */ - init_syntax_once(); -#endif -#endif - if (bufp->allocated == 0) +/* Print a compiled pattern string in human-readable form, starting at + the START pointer into it and ending just before the pointer END. */ + +void +print_partial_compiled_pattern (start, end) + unsigned char *start; + unsigned char *end; +{ + int mcnt, mcnt2; + unsigned char *p = start; + unsigned char *pend = end; + + if (start == NULL) { - bufp->allocated = 28; - if (bufp->buffer) - /* EXTEND_BUFFER loses when bufp->allocated is 0 */ - bufp->buffer = (char *) realloc (bufp->buffer, 28); - else - /* Caller did not allocate a buffer. Do it for him */ - bufp->buffer = (char *) malloc (28); - if (!bufp->buffer) goto memory_exhausted; - begalt = b = bufp->buffer; + printf ("(null)\n"); + return; } - - while (p != pend) + + /* Loop over pattern commands. */ + while (p < pend) { - if (b - bufp->buffer > bufp->allocated - 10) - /* Note that EXTEND_BUFFER clobbers c */ - EXTEND_BUFFER; + printf ("%d:\t", p - start); - PATFETCH (c); - - switch (c) + switch ((re_opcode_t) *p++) { - case '$': - if (obscure_syntax & RE_TIGHT_VBAR) - { - if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS) && p != pend) - goto normal_char; - /* Make operand of last vbar end before this `$'. */ - if (fixup_jump) - store_jump (fixup_jump, jump, b); - fixup_jump = 0; - PATPUSH (endline); - break; - } + case no_op: + printf ("/no_op"); + break; - /* $ means succeed if at end of line, but only in special contexts. - If randomly in the middle of a pattern, it is a normal character. */ - if (p == pend || *p == '\n' - || (obscure_syntax & RE_CONTEXT_INDEP_OPS) - || (obscure_syntax & RE_NO_BK_PARENS - ? *p == ')' - : *p == '\\' && p[1] == ')') - || (obscure_syntax & RE_NO_BK_VBAR - ? *p == '|' - : *p == '\\' && p[1] == '|')) + case exactn: + mcnt = *p++; + printf ("/exactn/%d", mcnt); + do { - PATPUSH (endline); - break; - } - goto normal_char; + putchar ('/'); + printchar (*p++); + } + while (--mcnt); + break; - case '^': - /* ^ means succeed if at beg of line, but only if no preceding pattern. */ + case start_memory: + mcnt = *p++; + printf ("/start_memory/%d/%d", mcnt, *p++); + break; - if (laststart && p[-2] != '\n' - && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) - goto normal_char; - if (obscure_syntax & RE_TIGHT_VBAR) - { - if (p != pattern + 1 - && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) - goto normal_char; - PATPUSH (begline); - begalt = b; - } - else - PATPUSH (begline); + case stop_memory: + mcnt = *p++; + printf ("/stop_memory/%d/%d", mcnt, *p++); + break; + + case duplicate: + printf ("/duplicate/%d", *p++); break; - case '+': - case '?': - if (obscure_syntax & RE_BK_PLUS_QM) - goto normal_char; - handle_plus: - case '*': - /* If there is no previous pattern, char not special. */ - if (!laststart && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) - goto normal_char; - /* If there is a sequence of repetition chars, - collapse it down to equivalent to just one. */ - zero_times_ok = 0; - many_times_ok = 0; - while (1) - { - zero_times_ok |= c != '+'; - many_times_ok |= c != '?'; - if (p == pend) - break; - PATFETCH (c); - if (c == '*') - ; - else if (!(obscure_syntax & RE_BK_PLUS_QM) - && (c == '+' || c == '?')) - ; - else if ((obscure_syntax & RE_BK_PLUS_QM) - && c == '\\') + case anychar: + printf ("/anychar"); + break; + + case charset: + case charset_not: + { + register int c, last = -100; + register int in_range = 0; + + printf ("/charset [%s", + (re_opcode_t) *(p - 1) == charset_not ? "^" : ""); + + assert (p + *p < pend); + + for (c = 0; c < 256; c++) + if (c / 8 < *p + && (p[1 + (c/8)] & (1 << (c % 8)))) { - int c1; - PATFETCH (c1); - if (!(c1 == '+' || c1 == '?')) + /* Are we starting a range? */ + if (last + 1 == c && ! in_range) { - PATUNFETCH; - PATUNFETCH; - break; + putchar ('-'); + in_range = 1; } - c = c1; - } - else - { - PATUNFETCH; - break; - } - } + /* Have we broken a range? */ + else if (last + 1 != c && in_range) + { + printchar (last); + in_range = 0; + } + + if (! in_range) + printchar (c); - /* Star, etc. applied to an empty pattern is equivalent - to an empty pattern. */ - if (!laststart) - break; + last = c; + } - /* Now we know whether 0 matches is allowed, - and whether 2 or more matches is allowed. */ - if (many_times_ok) - { - /* If more than one repetition is allowed, - put in a backward jump at the end. */ - store_jump (b, maybe_finalize_jump, laststart - 3); - b += 3; - } - insert_jump (on_failure_jump, laststart, b + 3, b); - pending_exact = 0; - b += 3; - if (!zero_times_ok) - { - /* At least one repetition required: insert before the loop - a skip over the initial on-failure-jump instruction */ - insert_jump (dummy_failure_jump, laststart, laststart + 6, b); - b += 3; - } - break; + if (in_range) + printchar (last); - case '.': - laststart = b; - PATPUSH (anychar); + putchar (']'); + + p += 1 + *p; + } break; - case '[': - while (b - bufp->buffer - > bufp->allocated - 3 - (1 << BYTEWIDTH) / BYTEWIDTH) - /* Note that EXTEND_BUFFER clobbers c */ - EXTEND_BUFFER; + case begline: + printf ("/begline"); + break; - laststart = b; - if (*p == '^') - PATPUSH (charset_not), p++; - else - PATPUSH (charset); - p1 = p; - - PATPUSH ((1 << BYTEWIDTH) / BYTEWIDTH); - /* Clear the whole map */ - bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH); - /* Read in characters and ranges, setting map bits */ - while (1) - { - PATFETCH (c); - if (c == ']' && p != p1 + 1) break; - if (*p == '-' && p[1] != ']') - { - PATFETCH (c1); - PATFETCH (c1); - while (c <= c1) - b[c / BYTEWIDTH] |= 1 << (c % BYTEWIDTH), c++; - } - else - { - b[c / BYTEWIDTH] |= 1 << (c % BYTEWIDTH); - } - } - /* Discard any bitmap bytes that are all 0 at the end of the map. - Decrement the map-length byte too. */ - while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) - b[-1]--; - b += b[-1]; - break; + case endline: + printf ("/endline"); + break; - case '(': - if (! (obscure_syntax & RE_NO_BK_PARENS)) - goto normal_char; - else - goto handle_open; + case on_failure_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/on_failure_jump to %d", p + mcnt - start); + break; - case ')': - if (! (obscure_syntax & RE_NO_BK_PARENS)) - goto normal_char; - else - goto handle_close; + case on_failure_keep_string_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/on_failure_keep_string_jump to %d", p + mcnt - start); + break; - case '\n': - if (! (obscure_syntax & RE_NEWLINE_OR)) - goto normal_char; - else - goto handle_bar; + case dummy_failure_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/dummy_failure_jump to %d", p + mcnt - start); + break; + + case push_dummy_failure: + printf ("/push_dummy_failure"); + break; + + case maybe_pop_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/maybe_pop_jump to %d", p + mcnt - start); + break; - case '|': - if (! (obscure_syntax & RE_NO_BK_VBAR)) - goto normal_char; - else - goto handle_bar; + case pop_failure_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/pop_failure_jump to %d", p + mcnt - start); + break; + + case jump_past_alt: + extract_number_and_incr (&mcnt, &p); + printf ("/jump_past_alt to %d", p + mcnt - start); + break; + + case jump: + extract_number_and_incr (&mcnt, &p); + printf ("/jump to %d", p + mcnt - start); + break; - case '\\': - if (p == pend) goto invalid_pattern; - PATFETCH_RAW (c); - switch (c) - { - case '(': - if (obscure_syntax & RE_NO_BK_PARENS) - goto normal_backsl; - handle_open: - if (stackp == stacke) goto nesting_too_deep; - if (regnum < RE_NREGS) - { - PATPUSH (start_memory); - PATPUSH (regnum); - } - *stackp++ = b - bufp->buffer; - *stackp++ = fixup_jump ? fixup_jump - bufp->buffer + 1 : 0; - *stackp++ = regnum++; - *stackp++ = begalt - bufp->buffer; - fixup_jump = 0; - laststart = 0; - begalt = b; - break; - - case ')': - if (obscure_syntax & RE_NO_BK_PARENS) - goto normal_backsl; - handle_close: - if (stackp == stackb) goto unmatched_close; - begalt = *--stackp + bufp->buffer; - if (fixup_jump) - store_jump (fixup_jump, jump, b); - if (stackp[-1] < RE_NREGS) - { - PATPUSH (stop_memory); - PATPUSH (stackp[-1]); - } - stackp -= 2; - fixup_jump = 0; - if (*stackp) - fixup_jump = *stackp + bufp->buffer - 1; - laststart = *--stackp + bufp->buffer; - break; - - case '|': - if (obscure_syntax & RE_NO_BK_VBAR) - goto normal_backsl; - handle_bar: - insert_jump (on_failure_jump, begalt, b + 6, b); - pending_exact = 0; - b += 3; - if (fixup_jump) - store_jump (fixup_jump, jump, b); - fixup_jump = b; - b += 3; - laststart = 0; - begalt = b; - break; + case succeed_n: + extract_number_and_incr (&mcnt, &p); + extract_number_and_incr (&mcnt2, &p); + printf ("/succeed_n to %d, %d times", p + mcnt - start, mcnt2); + break; + + case jump_n: + extract_number_and_incr (&mcnt, &p); + extract_number_and_incr (&mcnt2, &p); + printf ("/jump_n to %d, %d times", p + mcnt - start, mcnt2); + break; + + case set_number_at: + extract_number_and_incr (&mcnt, &p); + extract_number_and_incr (&mcnt2, &p); + printf ("/set_number_at location %d to %d", p + mcnt - start, mcnt2); + break; + + case wordbound: + printf ("/wordbound"); + break; + case notwordbound: + printf ("/notwordbound"); + break; + + case wordbeg: + printf ("/wordbeg"); + break; + + case wordend: + printf ("/wordend"); + #ifdef emacs - case '=': - PATPUSH (at_dot); - break; - - case 's': - laststart = b; - PATPUSH (syntaxspec); - PATFETCH (c); - PATPUSH (syntax_spec_code[c]); - break; - - case 'S': - laststart = b; - PATPUSH (notsyntaxspec); - PATFETCH (c); - PATPUSH (syntax_spec_code[c]); - break; -#endif /* emacs */ + case before_dot: + printf ("/before_dot"); + break; - case 'w': - laststart = b; - PATPUSH (wordchar); - break; - - case 'W': - laststart = b; - PATPUSH (notwordchar); - break; - - case '<': - PATPUSH (wordbeg); - break; - - case '>': - PATPUSH (wordend); - break; - - case 'b': - PATPUSH (wordbound); - break; - - case 'B': - PATPUSH (notwordbound); - break; - - case '`': - PATPUSH (begbuf); - break; - - case '\'': - PATPUSH (endbuf); - break; - - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - c1 = c - '0'; - if (c1 >= regnum) - goto normal_char; - for (stackt = stackp - 2; stackt > stackb; stackt -= 4) - if (*stackt == c1) - goto normal_char; - laststart = b; - PATPUSH (duplicate); - PATPUSH (c1); - break; - - case '+': - case '?': - if (obscure_syntax & RE_BK_PLUS_QM) - goto handle_plus; - - default: - normal_backsl: - /* You might think it would be useful for \ to mean - not to translate; but if we don't translate it - it will never match anything. */ - if (translate) c = translate[c]; - goto normal_char; - } + case at_dot: + printf ("/at_dot"); + break; + + case after_dot: + printf ("/after_dot"); + break; + + case syntaxspec: + printf ("/syntaxspec"); + mcnt = *p++; + printf ("/%d", mcnt); + break; + + case notsyntaxspec: + printf ("/notsyntaxspec"); + mcnt = *p++; + printf ("/%d", mcnt); break; +#endif /* emacs */ - default: - normal_char: - if (!pending_exact || pending_exact + *pending_exact + 1 != b - || *pending_exact == 0177 || *p == '*' || *p == '^' - || ((obscure_syntax & RE_BK_PLUS_QM) - ? *p == '\\' && (p[1] == '+' || p[1] == '?') - : (*p == '+' || *p == '?'))) - { - laststart = b; - PATPUSH (exactn); - pending_exact = b; - PATPUSH (0); - } - PATPUSH (c); - (*pending_exact)++; - } - } + case wordchar: + printf ("/wordchar"); + break; + + case notwordchar: + printf ("/notwordchar"); + break; - if (fixup_jump) - store_jump (fixup_jump, jump, b); + case begbuf: + printf ("/begbuf"); + break; - if (stackp != stackb) goto unmatched_open; + case endbuf: + printf ("/endbuf"); + break; - bufp->used = b - bufp->buffer; - return 0; + default: + printf ("?%d", *(p-1)); + } - invalid_pattern: - return "Invalid regular expression"; + putchar ('\n'); + } - unmatched_open: - return "Unmatched \\("; + printf ("%d:\tend of pattern.\n", p - start); +} - unmatched_close: - return "Unmatched \\)"; - end_of_pattern: - return "Premature end of regular expression"; +void +print_compiled_pattern (bufp) + struct re_pattern_buffer *bufp; +{ + unsigned char *buffer = bufp->buffer; - nesting_too_deep: - return "Nesting too deep"; + print_partial_compiled_pattern (buffer, buffer + bufp->used); + printf ("%d bytes used/%d bytes allocated.\n", bufp->used, bufp->allocated); - too_big: - return "Regular expression too big"; + if (bufp->fastmap_accurate && bufp->fastmap) + { + printf ("fastmap: "); + print_fastmap (bufp->fastmap); + } - memory_exhausted: - return "Memory exhausted"; + printf ("re_nsub: %d\t", bufp->re_nsub); + printf ("regs_alloc: %d\t", bufp->regs_allocated); + printf ("can_be_null: %d\t", bufp->can_be_null); + printf ("newline_anchor: %d\n", bufp->newline_anchor); + printf ("no_sub: %d\t", bufp->no_sub); + printf ("not_bol: %d\t", bufp->not_bol); + printf ("not_eol: %d\t", bufp->not_eol); + printf ("syntax: %d\n", bufp->syntax); + /* Perhaps we should print the translate table? */ } -/* Store where `from' points a jump operation to jump to where `to' points. - `opcode' is the opcode to store. */ -static void -store_jump (from, opcode, to) - char *from, *to; - char opcode; +void +print_double_string (where, string1, size1, string2, size2) + const char *where; + const char *string1; + const char *string2; + int size1; + int size2; { - from[0] = opcode; - from[1] = (to - (from + 3)) & 0377; - from[2] = (to - (from + 3)) >> 8; + unsigned this_char; + + if (where == NULL) + printf ("(null)"); + else + { + if (FIRST_STRING_P (where)) + { + for (this_char = where - string1; this_char < size1; this_char++) + printchar (string1[this_char]); + + where = string2; + } + + for (this_char = where - string2; this_char < size2; this_char++) + printchar (string2[this_char]); + } } -/* Open up space at char FROM, and insert there a jump to TO. - CURRENT_END gives te end of the storage no in use, - so we know how much data to copy up. - OP is the opcode of the jump to insert. +#else /* not DEBUG */ - If you call this function, you must zero out pending_exact. */ +#undef assert +#define assert(e) -static void -insert_jump (op, from, to, current_end) - char op; - char *from, *to, *current_end; +#define DEBUG_STATEMENT(e) +#define DEBUG_PRINT1(x) +#define DEBUG_PRINT2(x1, x2) +#define DEBUG_PRINT3(x1, x2, x3) +#define DEBUG_PRINT4(x1, x2, x3, x4) +#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) +#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) + +#endif /* not DEBUG */ + +/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can + also be assigned to arbitrarily: each pattern buffer stores its own + syntax, so it can be changed between regex compilations. */ +reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS; + + +/* Specify the precise syntax of regexps for compilation. This provides + for compatibility for various utilities which historically have + different, incompatible syntaxes. + + The argument SYNTAX is a bit mask comprised of the various bits + defined in regex.h. We return the old syntax. */ + +reg_syntax_t +re_set_syntax (syntax) + reg_syntax_t syntax; { - register char *pto = current_end + 3; - register char *pfrom = current_end; - while (pfrom != from) - *--pto = *--pfrom; - store_jump (from, op, to); + reg_syntax_t ret = re_syntax_options; + + re_syntax_options = syntax; + return ret; } -/* Given a pattern, compute a fastmap from it. - The fastmap records which of the (1 << BYTEWIDTH) possible characters - can start a string that matches the pattern. - This fastmap is used by re_search to skip quickly over totally implausible text. +/* This table gives an error message for each of the error codes listed + in regex.h. Obviously the order here has to be same as there. */ + +static const char *re_error_msg[] = + { NULL, /* REG_NOERROR */ + "No match", /* REG_NOMATCH */ + "Invalid regular expression", /* REG_BADPAT */ + "Invalid collation character", /* REG_ECOLLATE */ + "Invalid character class name", /* REG_ECTYPE */ + "Trailing backslash", /* REG_EESCAPE */ + "Invalid back reference", /* REG_ESUBREG */ + "Unmatched [ or [^", /* REG_EBRACK */ + "Unmatched ( or \\(", /* REG_EPAREN */ + "Unmatched \\{", /* REG_EBRACE */ + "Invalid content of \\{\\}", /* REG_BADBR */ + "Invalid range end", /* REG_ERANGE */ + "Memory exhausted", /* REG_ESPACE */ + "Invalid preceding regular expression", /* REG_BADRPT */ + "Premature end of regular expression", /* REG_EEND */ + "Regular expression too big", /* REG_ESIZE */ + "Unmatched ) or \\)", /* REG_ERPAREN */ + }; + +/* Subroutine declarations and macros for regex_compile. */ + +static void store_op1 (), store_op2 (); +static void insert_op1 (), insert_op2 (); +static boolean at_begline_loc_p (), at_endline_loc_p (); +static boolean group_in_compile_stack (); +static reg_errcode_t compile_range (); + +/* Fetch the next character in the uncompiled pattern---translating it + if necessary. Also cast from a signed character in the constant + string passed to us by the user to an unsigned char that we can use + as an array index (in, e.g., `translate'). */ +#define PATFETCH(c) \ + do {if (p == pend) return REG_EEND; \ + c = (unsigned char) *p++; \ + if (translate) c = translate[c]; \ + } while (0) + +/* Fetch the next character in the uncompiled pattern, with no + translation. */ +#define PATFETCH_RAW(c) \ + do {if (p == pend) return REG_EEND; \ + c = (unsigned char) *p++; \ + } while (0) + +/* Go backwards one character in the pattern. */ +#define PATUNFETCH p-- - The caller must supply the address of a (1 << BYTEWIDTH)-byte data area - as bufp->fastmap. - The other components of bufp describe the pattern to be used. */ -void -re_compile_fastmap (bufp) +/* If `translate' is non-null, return translate[D], else just D. We + cast the subscript to translate because some data is declared as + `char *', to avoid warnings when a string constant is passed. But + when we use a character as a subscript we must make it unsigned. */ +#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d)) + + +/* Macros for outputting the compiled pattern into `buffer'. */ + +/* If the buffer isn't allocated when it comes in, use this. */ +#define INIT_BUF_SIZE 32 + +/* Make sure we have at least N more bytes of space in buffer. */ +#define GET_BUFFER_SPACE(n) \ + while (b - bufp->buffer + (n) > bufp->allocated) \ + EXTEND_BUFFER () + +/* Make sure we have one more byte of buffer space and then add C to it. */ +#define BUF_PUSH(c) \ + do { \ + GET_BUFFER_SPACE (1); \ + *b++ = (unsigned char) (c); \ + } while (0) + + +/* Ensure we have two more bytes of buffer space and then append C1 and C2. */ +#define BUF_PUSH_2(c1, c2) \ + do { \ + GET_BUFFER_SPACE (2); \ + *b++ = (unsigned char) (c1); \ + *b++ = (unsigned char) (c2); \ + } while (0) + + +/* As with BUF_PUSH_2, except for three bytes. */ +#define BUF_PUSH_3(c1, c2, c3) \ + do { \ + GET_BUFFER_SPACE (3); \ + *b++ = (unsigned char) (c1); \ + *b++ = (unsigned char) (c2); \ + *b++ = (unsigned char) (c3); \ + } while (0) + + +/* Store a jump with opcode OP at LOC to location TO. We store a + relative address offset by the three bytes the jump itself occupies. */ +#define STORE_JUMP(op, loc, to) \ + store_op1 (op, loc, (to) - (loc) - 3) + +/* Likewise, for a two-argument jump. */ +#define STORE_JUMP2(op, loc, to, arg) \ + store_op2 (op, loc, (to) - (loc) - 3, arg) + +/* Like `STORE_JUMP', but for inserting. Assume `b' is the buffer end. */ +#define INSERT_JUMP(op, loc, to) \ + insert_op1 (op, loc, (to) - (loc) - 3, b) + +/* Like `STORE_JUMP2', but for inserting. Assume `b' is the buffer end. */ +#define INSERT_JUMP2(op, loc, to, arg) \ + insert_op2 (op, loc, (to) - (loc) - 3, arg, b) + + +/* This is not an arbitrary limit: the arguments which represent offsets + into the pattern are two bytes long. So if 2^16 bytes turns out to + be too small, many things would have to change. */ +#define MAX_BUF_SIZE (1L << 16) + + +/* Extend the buffer by twice its current size via realloc and + reset the pointers that pointed into the old block to point to the + correct places in the new one. If extending the buffer results in it + being larger than MAX_BUF_SIZE, then flag memory exhausted. */ +#define EXTEND_BUFFER() \ + do { \ + unsigned char *old_buffer = bufp->buffer; \ + if (bufp->allocated == MAX_BUF_SIZE) \ + return REG_ESIZE; \ + bufp->allocated <<= 1; \ + if (bufp->allocated > MAX_BUF_SIZE) \ + bufp->allocated = MAX_BUF_SIZE; \ + bufp->buffer = (unsigned char *) realloc (bufp->buffer, bufp->allocated);\ + if (bufp->buffer == NULL) \ + return REG_ESPACE; \ + /* If the buffer moved, move all the pointers into it. */ \ + if (old_buffer != bufp->buffer) \ + { \ + b = (b - old_buffer) + bufp->buffer; \ + begalt = (begalt - old_buffer) + bufp->buffer; \ + if (fixup_alt_jump) \ + fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\ + if (laststart) \ + laststart = (laststart - old_buffer) + bufp->buffer; \ + if (pending_exact) \ + pending_exact = (pending_exact - old_buffer) + bufp->buffer; \ + } \ + } while (0) + + +/* Since we have one byte reserved for the register number argument to + {start,stop}_memory, the maximum number of groups we can report + things about is what fits in that byte. */ +#define MAX_REGNUM 255 + +/* But patterns can have more than `MAX_REGNUM' registers. We just + ignore the excess. */ +typedef unsigned regnum_t; + + +/* Macros for the compile stack. */ + +/* Since offsets can go either forwards or backwards, this type needs to + be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1. */ +typedef int pattern_offset_t; + +typedef struct +{ + pattern_offset_t begalt_offset; + pattern_offset_t fixup_alt_jump; + pattern_offset_t inner_group_offset; + pattern_offset_t laststart_offset; + regnum_t regnum; +} compile_stack_elt_t; + + +typedef struct +{ + compile_stack_elt_t *stack; + unsigned size; + unsigned avail; /* Offset of next open position. */ +} compile_stack_type; + + +#define INIT_COMPILE_STACK_SIZE 32 + +#define COMPILE_STACK_EMPTY (compile_stack.avail == 0) +#define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size) + +/* The next available element. */ +#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail]) + + +/* Set the bit for character C in a list. */ +#define SET_LIST_BIT(c) \ + (b[((unsigned char) (c)) / BYTEWIDTH] \ + |= 1 << (((unsigned char) c) % BYTEWIDTH)) + + +/* Get the next unsigned number in the uncompiled pattern. */ +#define GET_UNSIGNED_NUMBER(num) \ + { if (p != pend) \ + { \ + PATFETCH (c); \ + while (ISDIGIT (c)) \ + { \ + if (num < 0) \ + num = 0; \ + num = num * 10 + c - '0'; \ + if (p == pend) \ + break; \ + PATFETCH (c); \ + } \ + } \ + } + +#define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */ + +#define IS_CHAR_CLASS(string) \ + (STREQ (string, "alpha") || STREQ (string, "upper") \ + || STREQ (string, "lower") || STREQ (string, "digit") \ + || STREQ (string, "alnum") || STREQ (string, "xdigit") \ + || STREQ (string, "space") || STREQ (string, "print") \ + || STREQ (string, "punct") || STREQ (string, "graph") \ + || STREQ (string, "cntrl") || STREQ (string, "blank")) + +/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX. + Returns one of error codes defined in `regex.h', or zero for success. + + Assumes the `allocated' (and perhaps `buffer') and `translate' + fields are set in BUFP on entry. + + If it succeeds, results are put in BUFP (if it returns an error, the + contents of BUFP are undefined): + `buffer' is the compiled pattern; + `syntax' is set to SYNTAX; + `used' is set to the length of the compiled pattern; + `fastmap_accurate' is zero; + `re_nsub' is the number of subexpressions in PATTERN; + `not_bol' and `not_eol' are zero; + + The `fastmap' and `newline_anchor' fields are neither + examined nor set. */ + +static reg_errcode_t +regex_compile (pattern, size, syntax, bufp) + const char *pattern; + int size; + reg_syntax_t syntax; struct re_pattern_buffer *bufp; { - unsigned char *pattern = (unsigned char *) bufp->buffer; - int size = bufp->used; - register char *fastmap = bufp->fastmap; - register unsigned char *p = pattern; - register unsigned char *pend = pattern + size; - register int j, k; - unsigned char *translate = (unsigned char *) bufp->translate; + /* We fetch characters from PATTERN here. Even though PATTERN is + `char *' (i.e., signed), we declare these variables as unsigned, so + they can be reliably used as array indices. */ + register unsigned char c, c1; + + /* A random tempory spot in PATTERN. */ + const char *p1; - unsigned char *stackb[NFAILURES]; - unsigned char **stackp = stackb; + /* Points to the end of the buffer, where we should append. */ + register unsigned char *b; + + /* Keeps track of unclosed groups. */ + compile_stack_type compile_stack; - bzero (fastmap, (1 << BYTEWIDTH)); - bufp->fastmap_accurate = 1; - bufp->can_be_null = 0; - - while (p) + /* Points to the current (ending) position in the pattern. */ + const char *p = pattern; + const char *pend = pattern + size; + + /* How to translate the characters in the pattern. */ + char *translate = bufp->translate; + + /* Address of the count-byte of the most recently inserted `exactn' + command. This makes it possible to tell if a new exact-match + character can be added to that command or if the character requires + a new `exactn' command. */ + unsigned char *pending_exact = 0; + + /* Address of start of the most recently finished expression. + This tells, e.g., postfix * where to find the start of its + operand. Reset at the beginning of groups and alternatives. */ + unsigned char *laststart = 0; + + /* Address of beginning of regexp, or inside of last group. */ + unsigned char *begalt; + + /* Place in the uncompiled pattern (i.e., the {) to + which to go back if the interval is invalid. */ + const char *beg_interval; + + /* Address of the place where a forward jump should go to the end of + the containing expression. Each alternative of an `or' -- except the + last -- ends with a forward jump of this sort. */ + unsigned char *fixup_alt_jump = 0; + + /* Counts open-groups as they are encountered. Remembered for the + matching close-group on the compile stack, so the same register + number is put in the stop_memory as the start_memory. */ + regnum_t regnum = 0; + +#ifdef DEBUG + DEBUG_PRINT1 ("\nCompiling pattern: "); + if (debug) { - if (p == pend) - { - bufp->can_be_null = 1; - break; - } -#ifdef SWITCH_ENUM_BUG - switch ((int) ((enum regexpcode) *p++)) -#else - switch ((enum regexpcode) *p++) -#endif - { - case exactn: - if (translate) - fastmap[translate[p[1]]] = 1; - else - fastmap[p[1]] = 1; + unsigned debug_count; + + for (debug_count = 0; debug_count < size; debug_count++) + printchar (pattern[debug_count]); + putchar ('\n'); + } +#endif /* DEBUG */ + + /* Initialize the compile stack. */ + compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t); + if (compile_stack.stack == NULL) + return REG_ESPACE; + + compile_stack.size = INIT_COMPILE_STACK_SIZE; + compile_stack.avail = 0; + + /* Initialize the pattern buffer. */ + bufp->syntax = syntax; + bufp->fastmap_accurate = 0; + bufp->not_bol = bufp->not_eol = 0; + + /* Set `used' to zero, so that if we return an error, the pattern + printer (for debugging) will think there's no pattern. We reset it + at the end. */ + bufp->used = 0; + + /* Always count groups, whether or not bufp->no_sub is set. */ + bufp->re_nsub = 0; + +#if !defined (emacs) && !defined (SYNTAX_TABLE) + /* Initialize the syntax table. */ + init_syntax_once (); +#endif + + if (bufp->allocated == 0) + { + if (bufp->buffer) + { /* If zero allocated, but buffer is non-null, try to realloc + enough space. This loses if buffer's address is bogus, but + that is the user's responsibility. */ + RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char); + } + else + { /* Caller did not allocate a buffer. Do it for them. */ + bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char); + } + if (!bufp->buffer) return REG_ESPACE; + + bufp->allocated = INIT_BUF_SIZE; + } + + begalt = b = bufp->buffer; + + /* Loop through the uncompiled pattern until we're at the end. */ + while (p != pend) + { + PATFETCH (c); + + switch (c) + { + case '^': + { + if ( /* If at start of pattern, it's an operator. */ + p == pattern + 1 + /* If context independent, it's an operator. */ + || syntax & RE_CONTEXT_INDEP_ANCHORS + /* Otherwise, depends on what's come before. */ + || at_begline_loc_p (pattern, p, syntax)) + BUF_PUSH (begline); + else + goto normal_char; + } + break; + + + case '$': + { + if ( /* If at end of pattern, it's an operator. */ + p == pend + /* If context independent, it's an operator. */ + || syntax & RE_CONTEXT_INDEP_ANCHORS + /* Otherwise, depends on what's next. */ + || at_endline_loc_p (p, pend, syntax)) + BUF_PUSH (endline); + else + goto normal_char; + } + break; + + + case '+': + case '?': + if ((syntax & RE_BK_PLUS_QM) + || (syntax & RE_LIMITED_OPS)) + goto normal_char; + handle_plus: + case '*': + /* If there is no previous pattern... */ + if (!laststart) + { + if (syntax & RE_CONTEXT_INVALID_OPS) + return REG_BADRPT; + else if (!(syntax & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + } + + { + /* Are we optimizing this jump? */ + boolean keep_string_p = false; + + /* 1 means zero (many) matches is allowed. */ + char zero_times_ok = 0, many_times_ok = 0; + + /* If there is a sequence of repetition chars, collapse it + down to just one (the right one). We can't combine + interval operators with these because of, e.g., `a{2}*', + which should only match an even number of `a's. */ + + for (;;) + { + zero_times_ok |= c != '+'; + many_times_ok |= c != '?'; + + if (p == pend) + break; + + PATFETCH (c); + + if (c == '*' + || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?'))) + ; + + else if (syntax & RE_BK_PLUS_QM && c == '\\') + { + if (p == pend) return REG_EESCAPE; + + PATFETCH (c1); + if (!(c1 == '+' || c1 == '?')) + { + PATUNFETCH; + PATUNFETCH; + break; + } + + c = c1; + } + else + { + PATUNFETCH; + break; + } + + /* If we get here, we found another repeat character. */ + } + + /* Star, etc. applied to an empty pattern is equivalent + to an empty pattern. */ + if (!laststart) + break; + + /* Now we know whether or not zero matches is allowed + and also whether or not two or more matches is allowed. */ + if (many_times_ok) + { /* More than one repetition is allowed, so put in at the + end a backward relative jump from `b' to before the next + jump we're going to put in below (which jumps from + laststart to after this jump). + + But if we are at the `*' in the exact sequence `.*\n', + insert an unconditional jump backwards to the ., + instead of the beginning of the loop. This way we only + push a failure point once, instead of every time + through the loop. */ + assert (p - 1 > pattern); + + /* Allocate the space for the jump. */ + GET_BUFFER_SPACE (3); + + /* We know we are not at the first character of the pattern, + because laststart was nonzero. And we've already + incremented `p', by the way, to be the character after + the `*'. Do we have to do something analogous here + for null bytes, because of RE_DOT_NOT_NULL? */ + if (TRANSLATE (*(p - 2)) == TRANSLATE ('.') + && zero_times_ok + && p < pend && TRANSLATE (*p) == TRANSLATE ('\n') + && !(syntax & RE_DOT_NEWLINE)) + { /* We have .*\n. */ + STORE_JUMP (jump, b, laststart); + keep_string_p = true; + } + else + /* Anything else. */ + STORE_JUMP (maybe_pop_jump, b, laststart - 3); + + /* We've added more stuff to the buffer. */ + b += 3; + } + + /* On failure, jump from laststart to b + 3, which will be the + end of the buffer after this jump is inserted. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump + : on_failure_jump, + laststart, b + 3); + pending_exact = 0; + b += 3; + + if (!zero_times_ok) + { + /* At least one repetition is required, so insert a + `dummy_failure_jump' before the initial + `on_failure_jump' instruction of the loop. This + effects a skip over that instruction the first time + we hit that loop. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6); + b += 3; + } + } break; - case begline: - case before_dot: - case at_dot: - case after_dot: - case begbuf: - case endbuf: - case wordbound: - case notwordbound: - case wordbeg: - case wordend: - continue; - case endline: - if (translate) - fastmap[translate['\n']] = 1; - else - fastmap['\n'] = 1; - if (bufp->can_be_null != 1) - bufp->can_be_null = 2; + case '.': + laststart = b; + BUF_PUSH (anychar); + break; + + + case '[': + { + boolean had_char_class = false; + + if (p == pend) return REG_EBRACK; + + /* Ensure that we have enough space to push a charset: the + opcode, the length count, and the bitset; 34 bytes in all. */ + GET_BUFFER_SPACE (34); + + laststart = b; + + /* We test `*p == '^' twice, instead of using an if + statement, so we only need one BUF_PUSH. */ + BUF_PUSH (*p == '^' ? charset_not : charset); + if (*p == '^') + p++; + + /* Remember the first position in the bracket expression. */ + p1 = p; + + /* Push the number of bytes in the bitmap. */ + BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH); + + /* Clear the whole map. */ + bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH); + + /* charset_not matches newline according to a syntax bit. */ + if ((re_opcode_t) b[-2] == charset_not + && (syntax & RE_HAT_LISTS_NOT_NEWLINE)) + SET_LIST_BIT ('\n'); + + /* Read in characters and ranges, setting map bits. */ + for (;;) + { + if (p == pend) return REG_EBRACK; + + PATFETCH (c); + + /* \ might escape characters inside [...] and [^...]. */ + if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\') + { + if (p == pend) return REG_EESCAPE; + + PATFETCH (c1); + SET_LIST_BIT (c1); + continue; + } + + /* Could be the end of the bracket expression. If it's + not (i.e., when the bracket expression is `[]' so + far), the ']' character bit gets set way below. */ + if (c == ']' && p != p1 + 1) + break; + + /* Look ahead to see if it's a range when the last thing + was a character class. */ + if (had_char_class && c == '-' && *p != ']') + return REG_ERANGE; + + /* Look ahead to see if it's a range when the last thing + was a character: if this is a hyphen not at the + beginning or the end of a list, then it's the range + operator. */ + if (c == '-' + && !(p - 2 >= pattern && p[-2] == '[') + && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^') + && *p != ']') + { + reg_errcode_t ret + = compile_range (&p, pend, translate, syntax, b); + if (ret != REG_NOERROR) return ret; + } + + else if (p[0] == '-' && p[1] != ']') + { /* This handles ranges made up of characters only. */ + reg_errcode_t ret; + + /* Move past the `-'. */ + PATFETCH (c1); + + ret = compile_range (&p, pend, translate, syntax, b); + if (ret != REG_NOERROR) return ret; + } + + /* See if we're at the beginning of a possible character + class. */ + + else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':') + { /* Leave room for the null. */ + char str[CHAR_CLASS_MAX_LENGTH + 1]; + + PATFETCH (c); + c1 = 0; + + /* If pattern is `[[:'. */ + if (p == pend) return REG_EBRACK; + + for (;;) + { + PATFETCH (c); + if (c == ':' || c == ']' || p == pend + || c1 == CHAR_CLASS_MAX_LENGTH) + break; + str[c1++] = c; + } + str[c1] = '\0'; + + /* If isn't a word bracketed by `[:' and:`]': + undo the ending character, the letters, and leave + the leading `:' and `[' (but set bits for them). */ + if (c == ':' && *p == ']') + { + int ch; + boolean is_alnum = STREQ (str, "alnum"); + boolean is_alpha = STREQ (str, "alpha"); + boolean is_blank = STREQ (str, "blank"); + boolean is_cntrl = STREQ (str, "cntrl"); + boolean is_digit = STREQ (str, "digit"); + boolean is_graph = STREQ (str, "graph"); + boolean is_lower = STREQ (str, "lower"); + boolean is_print = STREQ (str, "print"); + boolean is_punct = STREQ (str, "punct"); + boolean is_space = STREQ (str, "space"); + boolean is_upper = STREQ (str, "upper"); + boolean is_xdigit = STREQ (str, "xdigit"); + + if (!IS_CHAR_CLASS (str)) return REG_ECTYPE; + + /* Throw away the ] at the end of the character + class. */ + PATFETCH (c); + + if (p == pend) return REG_EBRACK; + + for (ch = 0; ch < 1 << BYTEWIDTH; ch++) + { + if ( (is_alnum && ISALNUM (ch)) + || (is_alpha && ISALPHA (ch)) + || (is_blank && ISBLANK (ch)) + || (is_cntrl && ISCNTRL (ch)) + || (is_digit && ISDIGIT (ch)) + || (is_graph && ISGRAPH (ch)) + || (is_lower && ISLOWER (ch)) + || (is_print && ISPRINT (ch)) + || (is_punct && ISPUNCT (ch)) + || (is_space && ISSPACE (ch)) + || (is_upper && ISUPPER (ch)) + || (is_xdigit && ISXDIGIT (ch))) + SET_LIST_BIT (ch); + } + had_char_class = true; + } + else + { + c1++; + while (c1--) + PATUNFETCH; + SET_LIST_BIT ('['); + SET_LIST_BIT (':'); + had_char_class = false; + } + } + else + { + had_char_class = false; + SET_LIST_BIT (c); + } + } + + /* Discard any (non)matching list bytes that are all 0 at the + end of the map. Decrease the map-length byte too. */ + while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) + b[-1]--; + b += b[-1]; + } + break; + + + case '(': + if (syntax & RE_NO_BK_PARENS) + goto handle_open; + else + goto normal_char; + + + case ')': + if (syntax & RE_NO_BK_PARENS) + goto handle_close; + else + goto normal_char; + + + case '\n': + if (syntax & RE_NEWLINE_ALT) + goto handle_alt; + else + goto normal_char; + + + case '|': + if (syntax & RE_NO_BK_VBAR) + goto handle_alt; + else + goto normal_char; + + + case '{': + if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES) + goto handle_interval; + else + goto normal_char; + + + case '\\': + if (p == pend) return REG_EESCAPE; + + /* Do not translate the character after the \, so that we can + distinguish, e.g., \B from \b, even if we normally would + translate, e.g., B to b. */ + PATFETCH_RAW (c); + + switch (c) + { + case '(': + if (syntax & RE_NO_BK_PARENS) + goto normal_backslash; + + handle_open: + bufp->re_nsub++; + regnum++; + + if (COMPILE_STACK_FULL) + { + RETALLOC (compile_stack.stack, compile_stack.size << 1, + compile_stack_elt_t); + if (compile_stack.stack == NULL) return REG_ESPACE; + + compile_stack.size <<= 1; + } + + /* These are the values to restore when we hit end of this + group. They are all relative offsets, so that if the + whole pattern moves because of realloc, they will still + be valid. */ + COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer; + COMPILE_STACK_TOP.fixup_alt_jump + = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0; + COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer; + COMPILE_STACK_TOP.regnum = regnum; + + /* We will eventually replace the 0 with the number of + groups inner to this one. But do not push a + start_memory for groups beyond the last one we can + represent in the compiled pattern. */ + if (regnum <= MAX_REGNUM) + { + COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2; + BUF_PUSH_3 (start_memory, regnum, 0); + } + + compile_stack.avail++; + + fixup_alt_jump = 0; + laststart = 0; + begalt = b; + /* If we've reached MAX_REGNUM groups, then this open + won't actually generate any code, so we'll have to + clear pending_exact explicitly. */ + pending_exact = 0; + break; + + + case ')': + if (syntax & RE_NO_BK_PARENS) goto normal_backslash; + + if (COMPILE_STACK_EMPTY) + if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) + goto normal_backslash; + else + return REG_ERPAREN; + + handle_close: + if (fixup_alt_jump) + { /* Push a dummy failure point at the end of the + alternative for a possible future + `pop_failure_jump' to pop. See comments at + `push_dummy_failure' in `re_match_2'. */ + BUF_PUSH (push_dummy_failure); + + /* We allocated space for this jump when we assigned + to `fixup_alt_jump', in the `handle_alt' case below. */ + STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1); + } + + /* See similar code for backslashed left paren above. */ + if (COMPILE_STACK_EMPTY) + if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) + goto normal_char; + else + return REG_ERPAREN; + + /* Since we just checked for an empty stack above, this + ``can't happen''. */ + assert (compile_stack.avail != 0); + { + /* We don't just want to restore into `regnum', because + later groups should continue to be numbered higher, + as in `(ab)c(de)' -- the second group is #2. */ + regnum_t this_group_regnum; + + compile_stack.avail--; + begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset; + fixup_alt_jump + = COMPILE_STACK_TOP.fixup_alt_jump + ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1 + : 0; + laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset; + this_group_regnum = COMPILE_STACK_TOP.regnum; + /* If we've reached MAX_REGNUM groups, then this open + won't actually generate any code, so we'll have to + clear pending_exact explicitly. */ + pending_exact = 0; + + /* We're at the end of the group, so now we know how many + groups were inside this one. */ + if (this_group_regnum <= MAX_REGNUM) + { + unsigned char *inner_group_loc + = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset; + + *inner_group_loc = regnum - this_group_regnum; + BUF_PUSH_3 (stop_memory, this_group_regnum, + regnum - this_group_regnum); + } + } + break; + + + case '|': /* `\|'. */ + if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR) + goto normal_backslash; + handle_alt: + if (syntax & RE_LIMITED_OPS) + goto normal_char; + + /* Insert before the previous alternative a jump which + jumps to this alternative if the former fails. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (on_failure_jump, begalt, b + 6); + pending_exact = 0; + b += 3; + + /* The alternative before this one has a jump after it + which gets executed if it gets matched. Adjust that + jump so it will jump to this alternative's analogous + jump (put in below, which in turn will jump to the next + (if any) alternative's such jump, etc.). The last such + jump jumps to the correct final destination. A picture: + _____ _____ + | | | | + | v | v + a | b | c + + If we are at `b', then fixup_alt_jump right now points to a + three-byte space after `a'. We'll put in the jump, set + fixup_alt_jump to right after `b', and leave behind three + bytes which we'll fill in when we get to after `c'. */ + + if (fixup_alt_jump) + STORE_JUMP (jump_past_alt, fixup_alt_jump, b); + + /* Mark and leave space for a jump after this alternative, + to be filled in later either by next alternative or + when know we're at the end of a series of alternatives. */ + fixup_alt_jump = b; + GET_BUFFER_SPACE (3); + b += 3; + + laststart = 0; + begalt = b; + break; + + + case '{': + /* If \{ is a literal. */ + if (!(syntax & RE_INTERVALS) + /* If we're at `\{' and it's not the open-interval + operator. */ + || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) + || (p - 2 == pattern && p == pend)) + goto normal_backslash; + + handle_interval: + { + /* If got here, then the syntax allows intervals. */ + + /* At least (most) this many matches must be made. */ + int lower_bound = -1, upper_bound = -1; + + beg_interval = p - 1; + + if (p == pend) + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + return REG_EBRACE; + } + + GET_UNSIGNED_NUMBER (lower_bound); + + if (c == ',') + { + GET_UNSIGNED_NUMBER (upper_bound); + if (upper_bound < 0) upper_bound = RE_DUP_MAX; + } + else + /* Interval such as `{1}' => match exactly once. */ + upper_bound = lower_bound; + + if (lower_bound < 0 || upper_bound > RE_DUP_MAX + || lower_bound > upper_bound) + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + return REG_BADBR; + } + + if (!(syntax & RE_NO_BK_BRACES)) + { + if (c != '\\') return REG_EBRACE; + + PATFETCH (c); + } + + if (c != '}') + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + return REG_BADBR; + } + + /* We just parsed a valid interval. */ + + /* If it's invalid to have no preceding re. */ + if (!laststart) + { + if (syntax & RE_CONTEXT_INVALID_OPS) + return REG_BADRPT; + else if (syntax & RE_CONTEXT_INDEP_OPS) + laststart = b; + else + goto unfetch_interval; + } + + /* If the upper bound is zero, don't want to succeed at + all; jump from `laststart' to `b + 3', which will be + the end of the buffer after we insert the jump. */ + if (upper_bound == 0) + { + GET_BUFFER_SPACE (3); + INSERT_JUMP (jump, laststart, b + 3); + b += 3; + } + + /* Otherwise, we have a nontrivial interval. When + we're all done, the pattern will look like: + set_number_at + set_number_at + succeed_n + + jump_n + (The upper bound and `jump_n' are omitted if + `upper_bound' is 1, though.) */ + else + { /* If the upper bound is > 1, we need to insert + more at the end of the loop. */ + unsigned nbytes = 10 + (upper_bound > 1) * 10; + + GET_BUFFER_SPACE (nbytes); + + /* Initialize lower bound of the `succeed_n', even + though it will be set during matching by its + attendant `set_number_at' (inserted next), + because `re_compile_fastmap' needs to know. + Jump to the `jump_n' we might insert below. */ + INSERT_JUMP2 (succeed_n, laststart, + b + 5 + (upper_bound > 1) * 5, + lower_bound); + b += 5; + + /* Code to initialize the lower bound. Insert + before the `succeed_n'. The `5' is the last two + bytes of this `set_number_at', plus 3 bytes of + the following `succeed_n'. */ + insert_op2 (set_number_at, laststart, 5, lower_bound, b); + b += 5; + + if (upper_bound > 1) + { /* More than one repetition is allowed, so + append a backward jump to the `succeed_n' + that starts this interval. + + When we've reached this during matching, + we'll have matched the interval once, so + jump back only `upper_bound - 1' times. */ + STORE_JUMP2 (jump_n, b, laststart + 5, + upper_bound - 1); + b += 5; + + /* The location we want to set is the second + parameter of the `jump_n'; that is `b-2' as + an absolute address. `laststart' will be + the `set_number_at' we're about to insert; + `laststart+3' the number to set, the source + for the relative address. But we are + inserting into the middle of the pattern -- + so everything is getting moved up by 5. + Conclusion: (b - 2) - (laststart + 3) + 5, + i.e., b - laststart. + + We insert this at the beginning of the loop + so that if we fail during matching, we'll + reinitialize the bounds. */ + insert_op2 (set_number_at, laststart, b - laststart, + upper_bound - 1, b); + b += 5; + } + } + pending_exact = 0; + beg_interval = NULL; + } + break; + + unfetch_interval: + /* If an invalid interval, match the characters as literals. */ + assert (beg_interval); + p = beg_interval; + beg_interval = NULL; + + /* normal_char and normal_backslash need `c'. */ + PATFETCH (c); + + if (!(syntax & RE_NO_BK_BRACES)) + { + if (p > pattern && p[-1] == '\\') + goto normal_backslash; + } + goto normal_char; + +#ifdef emacs + /* There is no way to specify the before_dot and after_dot + operators. rms says this is ok. --karl */ + case '=': + BUF_PUSH (at_dot); + break; + + case 's': + laststart = b; + PATFETCH (c); + BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]); + break; + + case 'S': + laststart = b; + PATFETCH (c); + BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]); + break; +#endif /* emacs */ + + + case 'w': + laststart = b; + BUF_PUSH (wordchar); + break; + + + case 'W': + laststart = b; + BUF_PUSH (notwordchar); + break; + + + case '<': + BUF_PUSH (wordbeg); + break; + + case '>': + BUF_PUSH (wordend); + break; + + case 'b': + BUF_PUSH (wordbound); + break; + + case 'B': + BUF_PUSH (notwordbound); + break; + + case '`': + BUF_PUSH (begbuf); + break; + + case '\'': + BUF_PUSH (endbuf); + break; + + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + if (syntax & RE_NO_BK_REFS) + goto normal_char; + + c1 = c - '0'; + + if (c1 > regnum) + return REG_ESUBREG; + + /* Can't back reference to a subexpression if inside of it. */ + if (group_in_compile_stack (compile_stack, c1)) + goto normal_char; + + laststart = b; + BUF_PUSH_2 (duplicate, c1); + break; + + + case '+': + case '?': + if (syntax & RE_BK_PLUS_QM) + goto handle_plus; + else + goto normal_backslash; + + default: + normal_backslash: + /* You might think it would be useful for \ to mean + not to translate; but if we don't translate it + it will never match anything. */ + c = TRANSLATE (c); + goto normal_char; + } + break; + + + default: + /* Expects the character in `c'. */ + normal_char: + /* If no exactn currently being built. */ + if (!pending_exact + + /* If last exactn not at current position. */ + || pending_exact + *pending_exact + 1 != b + + /* We have only one byte following the exactn for the count. */ + || *pending_exact == (1 << BYTEWIDTH) - 1 + + /* If followed by a repetition operator. */ + || *p == '*' || *p == '^' + || ((syntax & RE_BK_PLUS_QM) + ? *p == '\\' && (p[1] == '+' || p[1] == '?') + : (*p == '+' || *p == '?')) + || ((syntax & RE_INTERVALS) + && ((syntax & RE_NO_BK_BRACES) + ? *p == '{' + : (p[0] == '\\' && p[1] == '{')))) + { + /* Start building a new exactn. */ + + laststart = b; + + BUF_PUSH_2 (exactn, 0); + pending_exact = b - 1; + } + + BUF_PUSH (c); + (*pending_exact)++; break; + } /* switch (c) */ + } /* while p != pend */ - case finalize_jump: - case maybe_finalize_jump: - case jump: - case dummy_failure_jump: - bufp->can_be_null = 1; - j = *p++ & 0377; - j += SIGN_EXTEND_CHAR (*(char *)p) << 8; - p += j + 1; /* The 1 compensates for missing ++ above */ - if (j > 0) - continue; - /* Jump backward reached implies we just went through - the body of a loop and matched nothing. - Opcode jumped to should be an on_failure_jump. - Just treat it like an ordinary jump. - For a * loop, it has pushed its failure point already; - if so, discard that as redundant. */ - if ((enum regexpcode) *p != on_failure_jump) - continue; - p++; - j = *p++ & 0377; - j += SIGN_EXTEND_CHAR (*(char *)p) << 8; - p += j + 1; /* The 1 compensates for missing ++ above */ - if (stackp != stackb && *stackp == p) - stackp--; - continue; - - case on_failure_jump: - j = *p++ & 0377; - j += SIGN_EXTEND_CHAR (*(char *)p) << 8; - p++; - *++stackp = p + j; - continue; + + /* Through the pattern now. */ + + if (fixup_alt_jump) + STORE_JUMP (jump_past_alt, fixup_alt_jump, b); - case start_memory: - case stop_memory: - p++; - continue; + if (!COMPILE_STACK_EMPTY) + return REG_EPAREN; + + free (compile_stack.stack); + + /* We have succeeded; set the length of the buffer. */ + bufp->used = b - bufp->buffer; + +#ifdef DEBUG + if (debug) + { + DEBUG_PRINT1 ("\nCompiled pattern: \n"); + print_compiled_pattern (bufp); + } +#endif /* DEBUG */ + + return REG_NOERROR; +} /* regex_compile */ + +/* Subroutines for `regex_compile'. */ + +/* Store OP at LOC followed by two-byte integer parameter ARG. */ + +static void +store_op1 (op, loc, arg) + re_opcode_t op; + unsigned char *loc; + int arg; +{ + *loc = (unsigned char) op; + STORE_NUMBER (loc + 1, arg); +} + + +/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2. */ + +static void +store_op2 (op, loc, arg1, arg2) + re_opcode_t op; + unsigned char *loc; + int arg1, arg2; +{ + *loc = (unsigned char) op; + STORE_NUMBER (loc + 1, arg1); + STORE_NUMBER (loc + 3, arg2); +} + + +/* Copy the bytes from LOC to END to open up three bytes of space at LOC + for OP followed by two-byte integer parameter ARG. */ + +static void +insert_op1 (op, loc, arg, end) + re_opcode_t op; + unsigned char *loc; + int arg; + unsigned char *end; +{ + register unsigned char *pfrom = end; + register unsigned char *pto = end + 3; + + while (pfrom != loc) + *--pto = *--pfrom; + + store_op1 (op, loc, arg); +} + + +/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2. */ + +static void +insert_op2 (op, loc, arg1, arg2, end) + re_opcode_t op; + unsigned char *loc; + int arg1, arg2; + unsigned char *end; +{ + register unsigned char *pfrom = end; + register unsigned char *pto = end + 5; + + while (pfrom != loc) + *--pto = *--pfrom; + + store_op2 (op, loc, arg1, arg2); +} + + +/* P points to just after a ^ in PATTERN. Return true if that ^ comes + after an alternative or a begin-subexpression. We assume there is at + least one character before the ^. */ + +static boolean +at_begline_loc_p (pattern, p, syntax) + const char *pattern, *p; + reg_syntax_t syntax; +{ + const char *prev = p - 2; + boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\'; + + return + /* After a subexpression? */ + (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash)) + /* After an alternative? */ + || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash)); +} + + +/* The dual of at_begline_loc_p. This one is for $. We assume there is + at least one character after the $, i.e., `P < PEND'. */ + +static boolean +at_endline_loc_p (p, pend, syntax) + const char *p, *pend; + int syntax; +{ + const char *next = p; + boolean next_backslash = *next == '\\'; + const char *next_next = p + 1 < pend ? p + 1 : NULL; + + return + /* Before a subexpression? */ + (syntax & RE_NO_BK_PARENS ? *next == ')' + : next_backslash && next_next && *next_next == ')') + /* Before an alternative? */ + || (syntax & RE_NO_BK_VBAR ? *next == '|' + : next_backslash && next_next && *next_next == '|'); +} + + +/* Returns true if REGNUM is in one of COMPILE_STACK's elements and + false if it's not. */ + +static boolean +group_in_compile_stack (compile_stack, regnum) + compile_stack_type compile_stack; + regnum_t regnum; +{ + int this_element; + + for (this_element = compile_stack.avail - 1; + this_element >= 0; + this_element--) + if (compile_stack.stack[this_element].regnum == regnum) + return true; + + return false; +} + + +/* Read the ending character of a range (in a bracket expression) from the + uncompiled pattern *P_PTR (which ends at PEND). We assume the + starting character is in `P[-2]'. (`P[-1]' is the character `-'.) + Then we set the translation of all bits between the starting and + ending characters (inclusive) in the compiled pattern B. + + Return an error code. + + We use these short variable names so we can use the same macros as + `regex_compile' itself. */ + +static reg_errcode_t +compile_range (p_ptr, pend, translate, syntax, b) + const char **p_ptr, *pend; + char *translate; + reg_syntax_t syntax; + unsigned char *b; +{ + unsigned this_char; + + const char *p = *p_ptr; + int range_start, range_end; + + if (p == pend) + return REG_ERANGE; + + /* Even though the pattern is a signed `char *', we need to fetch + with unsigned char *'s; if the high bit of the pattern character + is set, the range endpoints will be negative if we fetch using a + signed char *. + + We also want to fetch the endpoints without translating them; the + appropriate translation is done in the bit-setting loop below. */ + range_start = ((unsigned char *) p)[-2]; + range_end = ((unsigned char *) p)[0]; + + /* Have to increment the pointer into the pattern string, so the + caller isn't still at the ending character. */ + (*p_ptr)++; + + /* If the start is after the end, the range is empty. */ + if (range_start > range_end) + return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR; + + /* Here we see why `this_char' has to be larger than an `unsigned + char' -- the range is inclusive, so if `range_end' == 0xff + (assuming 8-bit characters), we would otherwise go into an infinite + loop, since all characters <= 0xff. */ + for (this_char = range_start; this_char <= range_end; this_char++) + { + SET_LIST_BIT (TRANSLATE (this_char)); + } + + return REG_NOERROR; +} + +/* Failure stack declarations and macros; both re_compile_fastmap and + re_match_2 use a failure stack. These have to be macros because of + REGEX_ALLOCATE. */ + + +/* Number of failure points for which to initially allocate space + when matching. If this number is exceeded, we allocate more + space, so it is not a hard limit. */ +#ifndef INIT_FAILURE_ALLOC +#define INIT_FAILURE_ALLOC 5 +#endif + +/* Roughly the maximum number of failure points on the stack. Would be + exactly that if always used MAX_FAILURE_SPACE each time we failed. + This is a variable only so users of regex can assign to it; we never + change it ourselves. */ +int re_max_failures = 2000; + +typedef const unsigned char *fail_stack_elt_t; + +typedef struct +{ + fail_stack_elt_t *stack; + unsigned size; + unsigned avail; /* Offset of next open position. */ +} fail_stack_type; + +#define FAIL_STACK_EMPTY() (fail_stack.avail == 0) +#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0) +#define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size) +#define FAIL_STACK_TOP() (fail_stack.stack[fail_stack.avail]) + + +/* Initialize `fail_stack'. Do `return -2' if the alloc fails. */ + +#define INIT_FAIL_STACK() \ + do { \ + fail_stack.stack = (fail_stack_elt_t *) \ + REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \ + \ + if (fail_stack.stack == NULL) \ + return -2; \ + \ + fail_stack.size = INIT_FAILURE_ALLOC; \ + fail_stack.avail = 0; \ + } while (0) + + +/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items. + + Return 1 if succeeds, and 0 if either ran out of memory + allocating space for it or it was already too large. + + REGEX_REALLOCATE requires `destination' be declared. */ + +#define DOUBLE_FAIL_STACK(fail_stack) \ + ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS \ + ? 0 \ + : ((fail_stack).stack = (fail_stack_elt_t *) \ + REGEX_REALLOCATE ((fail_stack).stack, \ + (fail_stack).size * sizeof (fail_stack_elt_t), \ + ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)), \ + \ + (fail_stack).stack == NULL \ + ? 0 \ + : ((fail_stack).size <<= 1, \ + 1))) + + +/* Push PATTERN_OP on FAIL_STACK. + + Return 1 if was able to do so and 0 if ran out of memory allocating + space to do so. */ +#define PUSH_PATTERN_OP(pattern_op, fail_stack) \ + ((FAIL_STACK_FULL () \ + && !DOUBLE_FAIL_STACK (fail_stack)) \ + ? 0 \ + : ((fail_stack).stack[(fail_stack).avail++] = pattern_op, \ + 1)) + +/* This pushes an item onto the failure stack. Must be a four-byte + value. Assumes the variable `fail_stack'. Probably should only + be called from within `PUSH_FAILURE_POINT'. */ +#define PUSH_FAILURE_ITEM(item) \ + fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item + +/* The complement operation. Assumes `fail_stack' is nonempty. */ +#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail] + +/* Used to omit pushing failure point id's when we're not debugging. */ +#ifdef DEBUG +#define DEBUG_PUSH PUSH_FAILURE_ITEM +#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_ITEM () +#else +#define DEBUG_PUSH(item) +#define DEBUG_POP(item_addr) +#endif + + +/* Push the information about the state we will need + if we ever fail back to it. + + Requires variables fail_stack, regstart, regend, reg_info, and + num_regs be declared. DOUBLE_FAIL_STACK requires `destination' be + declared. + + Does `return FAILURE_CODE' if runs out of memory. */ + +#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \ + do { \ + char *destination; \ + /* Must be int, so when we don't save any registers, the arithmetic \ + of 0 + -1 isn't done as unsigned. */ \ + int this_reg; \ + \ + DEBUG_STATEMENT (failure_id++); \ + DEBUG_STATEMENT (nfailure_points_pushed++); \ + DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \ + DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\ + DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\ + \ + DEBUG_PRINT2 (" slots needed: %d\n", NUM_FAILURE_ITEMS); \ + DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \ + \ + /* Ensure we have enough space allocated for what we will push. */ \ + while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \ + { \ + if (!DOUBLE_FAIL_STACK (fail_stack)) \ + return failure_code; \ + \ + DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \ + (fail_stack).size); \ + DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\ + } \ + \ + /* Push the info, starting with the registers. */ \ + DEBUG_PRINT1 ("\n"); \ + \ + for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \ + this_reg++) \ + { \ + DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \ + DEBUG_STATEMENT (num_regs_pushed++); \ + \ + DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ + PUSH_FAILURE_ITEM (regstart[this_reg]); \ + \ + DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ + PUSH_FAILURE_ITEM (regend[this_reg]); \ + \ + DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \ + DEBUG_PRINT2 (" match_null=%d", \ + REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \ + DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \ + DEBUG_PRINT2 (" matched_something=%d", \ + MATCHED_SOMETHING (reg_info[this_reg])); \ + DEBUG_PRINT2 (" ever_matched=%d", \ + EVER_MATCHED_SOMETHING (reg_info[this_reg])); \ + DEBUG_PRINT1 ("\n"); \ + PUSH_FAILURE_ITEM (reg_info[this_reg].word); \ + } \ + \ + DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg);\ + PUSH_FAILURE_ITEM (lowest_active_reg); \ + \ + DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\ + PUSH_FAILURE_ITEM (highest_active_reg); \ + \ + DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \ + DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \ + PUSH_FAILURE_ITEM (pattern_place); \ + \ + DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \ + DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \ + size2); \ + DEBUG_PRINT1 ("'\n"); \ + PUSH_FAILURE_ITEM (string_place); \ + \ + DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \ + DEBUG_PUSH (failure_id); \ + } while (0) + +/* This is the number of items that are pushed and popped on the stack + for each register. */ +#define NUM_REG_ITEMS 3 + +/* Individual items aside from the registers. */ +#ifdef DEBUG +#define NUM_NONREG_ITEMS 5 /* Includes failure point id. */ +#else +#define NUM_NONREG_ITEMS 4 +#endif + +/* We push at most this many items on the stack. */ +#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS) + +/* We actually push this many items. */ +#define NUM_FAILURE_ITEMS \ + ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS \ + + NUM_NONREG_ITEMS) + +/* How many items can still be added to the stack without overflowing it. */ +#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail) + + +/* Pops what PUSH_FAIL_STACK pushes. + + We restore into the parameters, all of which should be lvalues: + STR -- the saved data position. + PAT -- the saved pattern position. + LOW_REG, HIGH_REG -- the highest and lowest active registers. + REGSTART, REGEND -- arrays of string positions. + REG_INFO -- array of information about each subexpression. + + Also assumes the variables `fail_stack' and (if debugging), `bufp', + `pend', `string1', `size1', `string2', and `size2'. */ + +#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\ +{ \ + DEBUG_STATEMENT (fail_stack_elt_t failure_id;) \ + int this_reg; \ + const unsigned char *string_temp; \ + \ + assert (!FAIL_STACK_EMPTY ()); \ + \ + /* Remove failure points and point to how many regs pushed. */ \ + DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \ + DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \ + DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \ + \ + assert (fail_stack.avail >= NUM_NONREG_ITEMS); \ + \ + DEBUG_POP (&failure_id); \ + DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \ + \ + /* If the saved string location is NULL, it came from an \ + on_failure_keep_string_jump opcode, and we want to throw away the \ + saved NULL, thus retaining our current position in the string. */ \ + string_temp = POP_FAILURE_ITEM (); \ + if (string_temp != NULL) \ + str = (const char *) string_temp; \ + \ + DEBUG_PRINT2 (" Popping string 0x%x: `", str); \ + DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \ + DEBUG_PRINT1 ("'\n"); \ + \ + pat = (unsigned char *) POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" Popping pattern 0x%x: ", pat); \ + DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \ + \ + /* Restore register info. */ \ + high_reg = (unsigned) POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" Popping high active reg: %d\n", high_reg); \ + \ + low_reg = (unsigned) POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" Popping low active reg: %d\n", low_reg); \ + \ + for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \ + { \ + DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \ + \ + reg_info[this_reg].word = POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \ + \ + regend[this_reg] = (const char *) POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ + \ + regstart[this_reg] = (const char *) POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ + } \ + \ + DEBUG_STATEMENT (nfailure_points_popped++); \ +} /* POP_FAILURE_POINT */ + +/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in + BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible + characters can start a string that matches the pattern. This fastmap + is used by re_search to skip quickly over impossible starting points. + + The caller must supply the address of a (1 << BYTEWIDTH)-byte data + area as BUFP->fastmap. + + We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in + the pattern buffer. + + Returns 0 if we succeed, -2 if an internal error. */ + +int +re_compile_fastmap (bufp) + struct re_pattern_buffer *bufp; +{ + int j, k; + fail_stack_type fail_stack; +#ifndef REGEX_MALLOC + char *destination; +#endif + /* We don't push any register information onto the failure stack. */ + unsigned num_regs = 0; + + register char *fastmap = bufp->fastmap; + unsigned char *pattern = bufp->buffer; + unsigned long size = bufp->used; + const unsigned char *p = pattern; + register unsigned char *pend = pattern + size; + + /* Assume that each path through the pattern can be null until + proven otherwise. We set this false at the bottom of switch + statement, to which we get only if a particular path doesn't + match the empty string. */ + boolean path_can_be_null = true; + + /* We aren't doing a `succeed_n' to begin with. */ + boolean succeed_n_p = false; + assert (fastmap != NULL && p != NULL); + + INIT_FAIL_STACK (); + bzero (fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid. */ + bufp->fastmap_accurate = 1; /* It will be when we're done. */ + bufp->can_be_null = 0; + + while (p != pend || !FAIL_STACK_EMPTY ()) + { + if (p == pend) + { + bufp->can_be_null |= path_can_be_null; + + /* Reset for next path. */ + path_can_be_null = true; + + p = fail_stack.stack[--fail_stack.avail]; + } + + /* We should never be about to go beyond the end of the pattern. */ + assert (p < pend); + +#ifdef SWITCH_ENUM_BUG + switch ((int) ((re_opcode_t) *p++)) +#else + switch ((re_opcode_t) *p++) +#endif + { + + /* I guess the idea here is to simply not bother with a fastmap + if a backreference is used, since it's too hard to figure out + the fastmap for the corresponding group. Setting + `can_be_null' stops `re_search_2' from using the fastmap, so + that is all we do. */ case duplicate: bufp->can_be_null = 1; - fastmap['\n'] = 1; - case anychar: - for (j = 0; j < (1 << BYTEWIDTH); j++) - if (j != '\n') - fastmap[j] = 1; - if (bufp->can_be_null) - return; - /* Don't return; check the alternative paths - so we can set can_be_null if appropriate. */ + return 0; + + + /* Following are the cases which match a character. These end + with `break'. */ + + case exactn: + fastmap[p[1]] = 1; + break; + + + case charset: + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) + fastmap[j] = 1; break; + + case charset_not: + /* Chars beyond end of map must be allowed. */ + for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++) + fastmap[j] = 1; + + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) + fastmap[j] = 1; + break; + + case wordchar: for (j = 0; j < (1 << BYTEWIDTH); j++) if (SYNTAX (j) == Sword) fastmap[j] = 1; break; + case notwordchar: for (j = 0; j < (1 << BYTEWIDTH); j++) if (SYNTAX (j) != Sword) fastmap[j] = 1; break; + + case anychar: + /* `.' matches anything ... */ + for (j = 0; j < (1 << BYTEWIDTH); j++) + fastmap[j] = 1; + + /* ... except perhaps newline. */ + if (!(bufp->syntax & RE_DOT_NEWLINE)) + fastmap['\n'] = 0; + + /* Return if we have already set `can_be_null'; if we have, + then the fastmap is irrelevant. Something's wrong here. */ + else if (bufp->can_be_null) + return 0; + + /* Otherwise, have to check alternative paths. */ + break; + + #ifdef emacs - case syntaxspec: + case syntaxspec: k = *p++; for (j = 0; j < (1 << BYTEWIDTH); j++) if (SYNTAX (j) == (enum syntaxcode) k) fastmap[j] = 1; break; + case notsyntaxspec: k = *p++; for (j = 0; j < (1 << BYTEWIDTH); j++) if (SYNTAX (j) != (enum syntaxcode) k) fastmap[j] = 1; break; -#endif /* emacs */ - case charset: - for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) - if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) - { - if (translate) - fastmap[translate[j]] = 1; - else - fastmap[j] = 1; - } - break; - case charset_not: - /* Chars beyond end of map must be allowed */ - for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++) - if (translate) - fastmap[translate[j]] = 1; - else - fastmap[j] = 1; + /* All cases after this match the empty string. These end with + `continue'. */ + + + case before_dot: + case at_dot: + case after_dot: + continue; +#endif /* not emacs */ + + + case no_op: + case begline: + case endline: + case begbuf: + case endbuf: + case wordbound: + case notwordbound: + case wordbeg: + case wordend: + case push_dummy_failure: + continue; + + + case jump_n: + case pop_failure_jump: + case maybe_pop_jump: + case jump: + case jump_past_alt: + case dummy_failure_jump: + EXTRACT_NUMBER_AND_INCR (j, p); + p += j; + if (j > 0) + continue; + + /* Jump backward implies we just went through the body of a + loop and matched nothing. Opcode jumped to should be + `on_failure_jump' or `succeed_n'. Just treat it like an + ordinary jump. For a * loop, it has pushed its failure + point already; if so, discard that as redundant. */ + if ((re_opcode_t) *p != on_failure_jump + && (re_opcode_t) *p != succeed_n) + continue; + + p++; + EXTRACT_NUMBER_AND_INCR (j, p); + p += j; + + /* If what's on the stack is where we are now, pop it. */ + if (!FAIL_STACK_EMPTY () + && fail_stack.stack[fail_stack.avail - 1] == p) + fail_stack.avail--; + + continue; + + + case on_failure_jump: + case on_failure_keep_string_jump: + handle_on_failure_jump: + EXTRACT_NUMBER_AND_INCR (j, p); + + /* For some patterns, e.g., `(a?)?', `p+j' here points to the + end of the pattern. We don't want to push such a point, + since when we restore it above, entering the switch will + increment `p' past the end of the pattern. We don't need + to push such a point since we obviously won't find any more + fastmap entries beyond `pend'. Such a pattern can match + the null string, though. */ + if (p + j < pend) + { + if (!PUSH_PATTERN_OP (p + j, fail_stack)) + return -2; + } + else + bufp->can_be_null = 1; + + if (succeed_n_p) + { + EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */ + succeed_n_p = false; + } + + continue; + + + case succeed_n: + /* Get to the number of times to succeed. */ + p += 2; + + /* Increment p past the n for when k != 0. */ + EXTRACT_NUMBER_AND_INCR (k, p); + if (k == 0) + { + p -= 4; + succeed_n_p = true; /* Spaghetti code alert. */ + goto handle_on_failure_jump; + } + continue; + + + case set_number_at: + p += 4; + continue; + + + case start_memory: + case stop_memory: + p += 2; + continue; - for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) - if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) - { - if (translate) - fastmap[translate[j]] = 1; - else - fastmap[j] = 1; - } - break; default: - break; - } + abort (); /* We have listed all the cases. */ + } /* switch *p++ */ + + /* Getting here means we have found the possible starting + characters for one path of the pattern -- and that the empty + string does not match. We need not follow this path further. + Instead, look at the next alternative (remembered on the + stack), or quit if no more. The test at the top of the loop + does these things. */ + path_can_be_null = false; + p = pend; + } /* while p */ + + /* Set `can_be_null' for the last path (also the first path, if the + pattern is empty). */ + bufp->can_be_null |= path_can_be_null; + return 0; +} /* re_compile_fastmap */ + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use + this memory for recording register information. STARTS and ENDS + must be allocated using the malloc library routine, and must each + be at least NUM_REGS * sizeof (regoff_t) bytes long. - /* Get here means we have successfully found the possible starting characters - of one path of the pattern. We need not follow this path any farther. - Instead, look at the next alternative remembered in the stack. */ - if (stackp != stackb) - p = *stackp--; - else - break; + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ + +void +re_set_registers (bufp, regs, num_regs, starts, ends) + struct re_pattern_buffer *bufp; + struct re_registers *regs; + unsigned num_regs; + regoff_t *starts, *ends; +{ + if (num_regs) + { + bufp->regs_allocated = REGS_REALLOCATE; + regs->num_regs = num_regs; + regs->start = starts; + regs->end = ends; + } + else + { + bufp->regs_allocated = REGS_UNALLOCATED; + regs->num_regs = 0; + regs->start = regs->end = (regoff_t) 0; } } -/* Like re_search_2, below, but only one string is specified. */ +/* Searching routines. */ + +/* Like re_search_2, below, but only one string is specified, and + doesn't let you say where to stop matching. */ int -re_search (pbufp, string, size, startpos, range, regs) - struct re_pattern_buffer *pbufp; - char *string; +re_search (bufp, string, size, startpos, range, regs) + struct re_pattern_buffer *bufp; + const char *string; int size, startpos, range; struct re_registers *regs; { - return re_search_2 (pbufp, 0, 0, string, size, startpos, range, regs, size); + return re_search_2 (bufp, NULL, 0, string, size, startpos, range, + regs, size); } -/* Like re_match_2 but tries first a match starting at index STARTPOS, - then at STARTPOS + 1, and so on. - RANGE is the number of places to try before giving up. - If RANGE is negative, the starting positions tried are - STARTPOS, STARTPOS - 1, etc. - It is up to the caller to make sure that range is not so large - as to take the starting position outside of the input strings. -The value returned is the position at which the match was found, - or -1 if no match was found, - or -2 if error (such as failure stack overflow). */ +/* Using the compiled pattern in BUFP->buffer, first tries to match the + virtual concatenation of STRING1 and STRING2, starting first at index + STARTPOS, then at STARTPOS + 1, and so on. + + STRING1 and STRING2 have length SIZE1 and SIZE2, respectively. + + RANGE is how far to scan while trying to match. RANGE = 0 means try + only at STARTPOS; in general, the last start tried is STARTPOS + + RANGE. + + In REGS, return the indices of the virtual concatenation of STRING1 + and STRING2 that matched the entire BUFP->buffer and its contained + subexpressions. + + Do not consider matching one past the index STOP in the virtual + concatenation of STRING1 and STRING2. + + We return either the position in the strings at which the match was + found, -1 if no match, or -2 if error (such as failure + stack overflow). */ int -re_search_2 (pbufp, string1, size1, string2, size2, startpos, range, regs, mstop) - struct re_pattern_buffer *pbufp; - char *string1, *string2; +re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; int size1, size2; int startpos; - register int range; + int range; struct re_registers *regs; - int mstop; + int stop; { - register char *fastmap = pbufp->fastmap; - register unsigned char *translate = (unsigned char *) pbufp->translate; - int total = size1 + size2; int val; - - /* Update the fastmap now if not correct already */ - if (fastmap && !pbufp->fastmap_accurate) - re_compile_fastmap (pbufp); - - /* Don't waste time in a long search for a pattern - that says it is anchored. */ - if (pbufp->used > 0 && (enum regexpcode) pbufp->buffer[0] == begbuf - && range > 0) + register char *fastmap = bufp->fastmap; + register char *translate = bufp->translate; + int total_size = size1 + size2; + int endpos = startpos + range; + + /* Check for out-of-range STARTPOS. */ + if (startpos < 0 || startpos > total_size) + return -1; + + /* Fix up RANGE if it might eventually take us outside + the virtual concatenation of STRING1 and STRING2. */ + if (endpos < -1) + range = -1 - startpos; + else if (endpos > total_size) + range = total_size - startpos; + + /* If the search isn't to be a backwards one, don't waste time in a + search for a pattern that must be anchored. */ + if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf && range > 0) { if (startpos > 0) return -1; @@ -963,159 +2944,420 @@ re_search_2 (pbufp, string1, size1, string2, size2, startpos, range, regs, mstop range = 1; } - while (1) - { - /* If a fastmap is supplied, skip quickly over characters - that cannot possibly be the start of a match. - Note, however, that if the pattern can possibly match - the null string, we must test it at each starting point - so that we take the first null string we get. */ - - if (fastmap && startpos < total && pbufp->can_be_null != 1) + /* Update the fastmap now if not correct already. */ + if (fastmap && !bufp->fastmap_accurate) + if (re_compile_fastmap (bufp) == -2) + return -2; + + /* Loop through the string, looking for a place to start matching. */ + for (;;) + { + /* If a fastmap is supplied, skip quickly over characters that + cannot be the start of a match. If the pattern can match the + null string, however, we don't need to skip characters; we want + the first null string. */ + if (fastmap && startpos < total_size && !bufp->can_be_null) { - if (range > 0) + if (range > 0) /* Searching forwards. */ { + register const char *d; register int lim = 0; - register unsigned char *p; int irange = range; - if (startpos < size1 && startpos + range >= size1) - lim = range - (size1 - startpos); - p = ((unsigned char *) - &(startpos >= size1 ? string2 - size1 : string1)[startpos]); + if (startpos < size1 && startpos + range >= size1) + lim = range - (size1 - startpos); + d = (startpos >= size1 ? string2 - size1 : string1) + startpos; + + /* Written out as an if-else to avoid testing `translate' + inside the loop. */ if (translate) - { - while (range > lim && !fastmap[translate[*p++]]) - range--; - } + while (range > lim + && !fastmap[(unsigned char) + translate[(unsigned char) *d++]]) + range--; else - { - while (range > lim && !fastmap[*p++]) - range--; - } + while (range > lim && !fastmap[(unsigned char) *d++]) + range--; + startpos += irange - range; } - else + else /* Searching backwards. */ { - register unsigned char c; - if (startpos >= size1) - c = string2[startpos - size1]; - else - c = string1[startpos]; - c &= 0xff; - if (translate ? !fastmap[translate[c]] : !fastmap[c]) + register char c = (size1 == 0 || startpos >= size1 + ? string2[startpos - size1] + : string1[startpos]); + + if (!fastmap[(unsigned char) TRANSLATE (c)]) goto advance; } } - if (range >= 0 && startpos == total - && fastmap && pbufp->can_be_null == 0) + /* If can't match the null string, and that's all we have left, fail. */ + if (range >= 0 && startpos == total_size && fastmap + && !bufp->can_be_null) return -1; - val = re_match_2 (pbufp, string1, size1, string2, size2, startpos, regs, mstop); - if (0 <= val) - { - if (val == -2) - return -2; - return startpos; - } - -#ifdef C_ALLOCA - alloca (0); -#endif /* C_ALLOCA */ + val = re_match_2 (bufp, string1, size1, string2, size2, + startpos, regs, stop); + if (val >= 0) + return startpos; + + if (val == -2) + return -2; advance: - if (!range) break; - if (range > 0) range--, startpos++; else range++, startpos--; + if (!range) + break; + else if (range > 0) + { + range--; + startpos++; + } + else + { + range++; + startpos--; + } } return -1; -} +} /* re_search_2 */ -#ifndef emacs /* emacs never uses this */ -int -re_match (pbufp, string, size, pos, regs) - struct re_pattern_buffer *pbufp; - char *string; - int size, pos; - struct re_registers *regs; +/* Declarations and macros for re_match_2. */ + +static int bcmp_translate (); +static boolean alt_match_null_string_p (), + common_op_match_null_string_p (), + group_match_null_string_p (); + +/* Structure for per-register (a.k.a. per-group) information. + This must not be longer than one word, because we push this value + onto the failure stack. Other register information, such as the + starting and ending positions (which are addresses), and the list of + inner groups (which is a bits list) are maintained in separate + variables. + + We are making a (strictly speaking) nonportable assumption here: that + the compiler will pack our bit fields into something that fits into + the type of `word', i.e., is something that fits into one item on the + failure stack. */ +typedef union { - return re_match_2 (pbufp, 0, 0, string, size, pos, regs, size); -} -#endif /* emacs */ + fail_stack_elt_t word; + struct + { + /* This field is one if this group can match the empty string, + zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */ +#define MATCH_NULL_UNSET_VALUE 3 + unsigned match_null_string_p : 2; + unsigned is_active : 1; + unsigned matched_something : 1; + unsigned ever_matched_something : 1; + } bits; +} register_info_type; + +#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p) +#define IS_ACTIVE(R) ((R).bits.is_active) +#define MATCHED_SOMETHING(R) ((R).bits.matched_something) +#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something) + + +/* Call this when have matched a real character; it sets `matched' flags + for the subexpressions which we are currently inside. Also records + that those subexprs have matched. */ +#define SET_REGS_MATCHED() \ + do \ + { \ + unsigned r; \ + for (r = lowest_active_reg; r <= highest_active_reg; r++) \ + { \ + MATCHED_SOMETHING (reg_info[r]) \ + = EVER_MATCHED_SOMETHING (reg_info[r]) \ + = 1; \ + } \ + } \ + while (0) + + +/* This converts PTR, a pointer into one of the search strings `string1' + and `string2' into an offset from the beginning of that string. */ +#define POINTER_TO_OFFSET(ptr) \ + (FIRST_STRING_P (ptr) ? (ptr) - string1 : (ptr) - string2 + size1) + +/* Registers are set to a sentinel when they haven't yet matched. */ +#define REG_UNSET_VALUE ((char *) -1) +#define REG_UNSET(e) ((e) == REG_UNSET_VALUE) + + +/* Macros for dealing with the split strings in re_match_2. */ + +#define MATCHING_IN_FIRST_STRING (dend == end_match_1) + +/* Call before fetching a character with *d. This switches over to + string2 if necessary. */ +#define PREFETCH() \ + while (d == dend) \ + { \ + /* End of string2 => fail. */ \ + if (dend == end_match_2) \ + goto fail; \ + /* End of string1 => advance to string2. */ \ + d = string2; \ + dend = end_match_2; \ + } -/* Maximum size of failure stack. Beyond this, overflow is an error. */ -int re_max_failures = 2000; +/* Test if at very beginning or at very end of the virtual concatenation + of `string1' and `string2'. If only one string, it's `string2'. */ +#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2) +#define AT_STRINGS_END(d) ((d) == end2) + + +/* Test if D points to a character which is word-constituent. We have + two special cases to check for: if past the end of string1, look at + the first character in string2; and if before the beginning of + string2, look at the last character in string1. */ +#define WORDCHAR_P(d) \ + (SYNTAX ((d) == end1 ? *string2 \ + : (d) == string2 - 1 ? *(end1 - 1) : *(d)) \ + == Sword) + +/* Test if the character before D and the one at D differ with respect + to being word-constituent. */ +#define AT_WORD_BOUNDARY(d) \ + (AT_STRINGS_BEG (d) || AT_STRINGS_END (d) \ + || WORDCHAR_P (d - 1) != WORDCHAR_P (d)) + + +/* Free everything we malloc. */ +#ifdef REGEX_MALLOC +#define FREE_VAR(var) if (var) free (var); var = NULL +#define FREE_VARIABLES() \ + do { \ + FREE_VAR (fail_stack.stack); \ + FREE_VAR (regstart); \ + FREE_VAR (regend); \ + FREE_VAR (old_regstart); \ + FREE_VAR (old_regend); \ + FREE_VAR (best_regstart); \ + FREE_VAR (best_regend); \ + FREE_VAR (reg_info); \ + FREE_VAR (reg_dummy); \ + FREE_VAR (reg_info_dummy); \ + } while (0) +#else /* not REGEX_MALLOC */ +/* Some MIPS systems (at least) want this to free alloca'd storage. */ +#define FREE_VARIABLES() alloca (0) +#endif /* not REGEX_MALLOC */ + + +/* These values must meet several constraints. They must not be valid + register values; since we have a limit of 255 registers (because + we use only one byte in the pattern for the register number), we can + use numbers larger than 255. They must differ by 1, because of + NUM_FAILURE_ITEMS above. And the value for the lowest register must + be larger than the value for the highest register, so we do not try + to actually save any registers when none are active. */ +#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH) +#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1) + +/* Matching routines. */ + +#ifndef emacs /* Emacs never uses this. */ +/* re_match is like re_match_2 except it takes only a single string. */ -static int bcmp_translate(); -/* Match the pattern described by PBUFP - against data which is the virtual concatenation of STRING1 and STRING2. - SIZE1 and SIZE2 are the sizes of the two data strings. - Start the match at position POS. - Do not consider matching past the position MSTOP. +int +re_match (bufp, string, size, pos, regs) + struct re_pattern_buffer *bufp; + const char *string; + int size, pos; + struct re_registers *regs; + { + return re_match_2 (bufp, NULL, 0, string, size, pos, regs, size); +} +#endif /* not emacs */ - If pbufp->fastmap is nonzero, then it had better be up to date. - The reason that the data to match are specified as two components - which are to be regarded as concatenated - is so this function can be used directly on the contents of an Emacs buffer. +/* re_match_2 matches the compiled pattern in BUFP against the + the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1 + and SIZE2, respectively). We start matching at POS, and stop + matching at STOP. + + If REGS is non-null and the `no_sub' field of BUFP is nonzero, we + store offsets for the substring each group matched in REGS. See the + documentation for exactly how many groups we fill. - -1 is returned if there is no match. -2 is returned if there is - an error (such as match stack overflow). Otherwise the value is the length - of the substring which was matched. */ + We return -1 if no match, -2 if an internal error (such as the + failure stack overflowing). Otherwise, we return the length of the + matched substring. */ int -re_match_2 (pbufp, string1, size1, string2, size2, pos, regs, mstop) - struct re_pattern_buffer *pbufp; - unsigned char *string1, *string2; +re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; int size1, size2; int pos; struct re_registers *regs; - int mstop; + int stop; { - register unsigned char *p = (unsigned char *) pbufp->buffer; - register unsigned char *pend = p + pbufp->used; - /* End of first string */ - unsigned char *end1; - /* End of second string */ - unsigned char *end2; - /* Pointer just past last char to consider matching */ - unsigned char *end_match_1, *end_match_2; - register unsigned char *d, *dend; - register int mcnt; - unsigned char *translate = (unsigned char *) pbufp->translate; - - /* Failure point stack. Each place that can handle a failure further down the line - pushes a failure point on this stack. It consists of two char *'s. - The first one pushed is where to resume scanning the pattern; - the second pushed is where to resume scanning the strings. - If the latter is zero, the failure point is a "dummy". - If a failure happens and the innermost failure point is dormant, - it discards that failure point and tries the next one. */ - - unsigned char *initial_stack[2 * NFAILURES]; - unsigned char **stackb = initial_stack; - unsigned char **stackp = stackb, **stacke = &stackb[2 * NFAILURES]; - - /* Information on the "contents" of registers. - These are pointers into the input strings; they record - just what was matched (on this attempt) by some part of the pattern. - The start_memory command stores the start of a register's contents - and the stop_memory command stores the end. - - At that point, regstart[regnum] points to the first character in the register, - regend[regnum] points to the first character beyond the end of the register, - regstart_seg1[regnum] is true iff regstart[regnum] points into string1, - and regend_seg1[regnum] is true iff regend[regnum] points into string1. */ - - unsigned char *regstart[RE_NREGS]; - unsigned char *regend[RE_NREGS]; - unsigned char regstart_seg1[RE_NREGS], regend_seg1[RE_NREGS]; - - /* Set up pointers to ends of strings. - Don't allow the second string to be empty unless both are empty. */ - if (!size2) + /* General temporaries. */ + int mcnt; + unsigned char *p1; + + /* Just past the end of the corresponding string. */ + const char *end1, *end2; + + /* Pointers into string1 and string2, just past the last characters in + each to consider matching. */ + const char *end_match_1, *end_match_2; + + /* Where we are in the data, and the end of the current string. */ + const char *d, *dend; + + /* Where we are in the pattern, and the end of the pattern. */ + unsigned char *p = bufp->buffer; + register unsigned char *pend = p + bufp->used; + + /* We use this to map every character in the string. */ + char *translate = bufp->translate; + + /* Failure point stack. Each place that can handle a failure further + down the line pushes a failure point on this stack. It consists of + restart, regend, and reg_info for all registers corresponding to + the subexpressions we're currently inside, plus the number of such + registers, and, finally, two char *'s. The first char * is where + to resume scanning the pattern; the second one is where to resume + scanning the strings. If the latter is zero, the failure point is + a ``dummy''; if a failure happens and the failure point is a dummy, + it gets discarded and the next next one is tried. */ + fail_stack_type fail_stack; +#ifdef DEBUG + static unsigned failure_id = 0; + unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0; +#endif + + /* We fill all the registers internally, independent of what we + return, for use in backreferences. The number here includes + an element for register zero. */ + unsigned num_regs = bufp->re_nsub + 1; + + /* The currently active registers. */ + unsigned lowest_active_reg = NO_LOWEST_ACTIVE_REG; + unsigned highest_active_reg = NO_HIGHEST_ACTIVE_REG; + + /* Information on the contents of registers. These are pointers into + the input strings; they record just what was matched (on this + attempt) by a subexpression part of the pattern, that is, the + regnum-th regstart pointer points to where in the pattern we began + matching and the regnum-th regend points to right after where we + stopped matching the regnum-th subexpression. (The zeroth register + keeps track of what the whole pattern matches.) */ + const char **regstart, **regend; + + /* If a group that's operated upon by a repetition operator fails to + match anything, then the register for its start will need to be + restored because it will have been set to wherever in the string we + are when we last see its open-group operator. Similarly for a + register's end. */ + const char **old_regstart, **old_regend; + + /* The is_active field of reg_info helps us keep track of which (possibly + nested) subexpressions we are currently in. The matched_something + field of reg_info[reg_num] helps us tell whether or not we have + matched any of the pattern so far this time through the reg_num-th + subexpression. These two fields get reset each time through any + loop their register is in. */ + register_info_type *reg_info; + + /* The following record the register info as found in the above + variables when we find a match better than any we've seen before. + This happens as we backtrack through the failure points, which in + turn happens only if we have not yet matched the entire string. */ + unsigned best_regs_set = false; + const char **best_regstart, **best_regend; + + /* Logically, this is `best_regend[0]'. But we don't want to have to + allocate space for that if we're not allocating space for anything + else (see below). Also, we never need info about register 0 for + any of the other register vectors, and it seems rather a kludge to + treat `best_regend' differently than the rest. So we keep track of + the end of the best match so far in a separate variable. We + initialize this to NULL so that when we backtrack the first time + and need to test it, it's not garbage. */ + const char *match_end = NULL; + + /* Used when we pop values we don't care about. */ + const char **reg_dummy; + register_info_type *reg_info_dummy; + +#ifdef DEBUG + /* Counts the total number of registers pushed. */ + unsigned num_regs_pushed = 0; +#endif + + DEBUG_PRINT1 ("\n\nEntering re_match_2.\n"); + + INIT_FAIL_STACK (); + + /* Do not bother to initialize all the register variables if there are + no groups in the pattern, as it takes a fair amount of time. If + there are groups, we include space for register 0 (the whole + pattern), even though we never use it, since it simplifies the + array indexing. We should fix this. */ + if (bufp->re_nsub) + { + regstart = REGEX_TALLOC (num_regs, const char *); + regend = REGEX_TALLOC (num_regs, const char *); + old_regstart = REGEX_TALLOC (num_regs, const char *); + old_regend = REGEX_TALLOC (num_regs, const char *); + best_regstart = REGEX_TALLOC (num_regs, const char *); + best_regend = REGEX_TALLOC (num_regs, const char *); + reg_info = REGEX_TALLOC (num_regs, register_info_type); + reg_dummy = REGEX_TALLOC (num_regs, const char *); + reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type); + + if (!(regstart && regend && old_regstart && old_regend && reg_info + && best_regstart && best_regend && reg_dummy && reg_info_dummy)) + { + FREE_VARIABLES (); + return -2; + } + } +#ifdef REGEX_MALLOC + else + { + /* We must initialize all our variables to NULL, so that + `FREE_VARIABLES' doesn't try to free them. */ + regstart = regend = old_regstart = old_regend = best_regstart + = best_regend = reg_dummy = NULL; + reg_info = reg_info_dummy = (register_info_type *) NULL; + } +#endif /* REGEX_MALLOC */ + + /* The starting position is bogus. */ + if (pos < 0 || pos > size1 + size2) + { + FREE_VARIABLES (); + return -1; + } + + /* Initialize subexpression text positions to -1 to mark ones that no + start_memory/stop_memory has been seen for. Also initialize the + register information struct. */ + for (mcnt = 1; mcnt < num_regs; mcnt++) + { + regstart[mcnt] = regend[mcnt] + = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE; + + REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE; + IS_ACTIVE (reg_info[mcnt]) = 0; + MATCHED_SOMETHING (reg_info[mcnt]) = 0; + EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0; + } + + /* We move `string1' into `string2' if the latter's empty -- but not if + `string1' is null. */ + if (size2 == 0 && string1 != NULL) { string2 = string1; size2 = size1; @@ -1125,482 +3367,1329 @@ re_match_2 (pbufp, string1, size1, string2, size2, pos, regs, mstop) end1 = string1 + size1; end2 = string2 + size2; - /* Compute where to stop matching, within the two strings */ - if (mstop <= size1) + /* Compute where to stop matching, within the two strings. */ + if (stop <= size1) { - end_match_1 = string1 + mstop; + end_match_1 = string1 + stop; end_match_2 = string2; } else { end_match_1 = end1; - end_match_2 = string2 + mstop - size1; + end_match_2 = string2 + stop - size1; + } + + /* `p' scans through the pattern as `d' scans through the data. + `dend' is the end of the input string that `d' points within. `d' + is advanced into the following input string whenever necessary, but + this happens before fetching; therefore, at the beginning of the + loop, `d' can be pointing at the end of a string, but it cannot + equal `string2'. */ + if (size1 > 0 && pos <= size1) + { + d = string1 + pos; + dend = end_match_1; + } + else + { + d = string2 + pos - size1; + dend = end_match_2; } - /* Initialize \) text positions to -1 - to mark ones that no \( or \) has been seen for. */ + DEBUG_PRINT1 ("The compiled pattern is: "); + DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend); + DEBUG_PRINT1 ("The string to match is: `"); + DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2); + DEBUG_PRINT1 ("'\n"); + + /* This loops over pattern commands. It exits by returning from the + function if the match is complete, or it drops through if the match + fails at this starting point in the input data. */ + for (;;) + { + DEBUG_PRINT2 ("\n0x%x: ", p); - for (mcnt = 0; mcnt < sizeof (regend) / sizeof (*regend); mcnt++) - regend[mcnt] = (unsigned char *) -1; + if (p == pend) + { /* End of pattern means we might have succeeded. */ + DEBUG_PRINT1 ("end of pattern ... "); + + /* If we haven't matched the entire string, and we want the + longest match, try backtracking. */ + if (d != end_match_2) + { + DEBUG_PRINT1 ("backtracking.\n"); + + if (!FAIL_STACK_EMPTY ()) + { /* More failure points to try. */ + boolean same_str_p = (FIRST_STRING_P (match_end) + == MATCHING_IN_FIRST_STRING); + + /* If exceeds best match so far, save it. */ + if (!best_regs_set + || (same_str_p && d > match_end) + || (!same_str_p && !MATCHING_IN_FIRST_STRING)) + { + best_regs_set = true; + match_end = d; + + DEBUG_PRINT1 ("\nSAVING match as best so far.\n"); + + for (mcnt = 1; mcnt < num_regs; mcnt++) + { + best_regstart[mcnt] = regstart[mcnt]; + best_regend[mcnt] = regend[mcnt]; + } + } + goto fail; + } + + /* If no failure points, don't restore garbage. */ + else if (best_regs_set) + { + restore_best_regs: + /* Restore best match. It may happen that `dend == + end_match_1' while the restored d is in string2. + For example, the pattern `x.*y.*z' against the + strings `x-' and `y-z-', if the two strings are + not consecutive in memory. */ + DEBUG_PRINT1 ("Restoring best registers.\n"); + + d = match_end; + dend = ((d >= string1 && d <= end1) + ? end_match_1 : end_match_2); + + for (mcnt = 1; mcnt < num_regs; mcnt++) + { + regstart[mcnt] = best_regstart[mcnt]; + regend[mcnt] = best_regend[mcnt]; + } + } + } /* d != end_match_2 */ - /* `p' scans through the pattern as `d' scans through the data. - `dend' is the end of the input string that `d' points within. - `d' is advanced into the following input string whenever necessary, - but this happens before fetching; - therefore, at the beginning of the loop, - `d' can be pointing at the end of a string, - but it cannot equal string2. */ + DEBUG_PRINT1 ("Accepting match.\n"); - if (pos <= size1) - d = string1 + pos, dend = end_match_1; - else - d = string2 + pos - size1, dend = end_match_2; + /* If caller wants register contents data back, do it. */ + if (regs && !bufp->no_sub) + { + /* Have the register data arrays been allocated? */ + if (bufp->regs_allocated == REGS_UNALLOCATED) + { /* No. So allocate them with malloc. We need one + extra element beyond `num_regs' for the `-1' marker + GNU code uses. */ + regs->num_regs = MAX (RE_NREGS, num_regs + 1); + regs->start = TALLOC (regs->num_regs, regoff_t); + regs->end = TALLOC (regs->num_regs, regoff_t); + if (regs->start == NULL || regs->end == NULL) + return -2; + bufp->regs_allocated = REGS_REALLOCATE; + } + else if (bufp->regs_allocated == REGS_REALLOCATE) + { /* Yes. If we need more elements than were already + allocated, reallocate them. If we need fewer, just + leave it alone. */ + if (regs->num_regs < num_regs + 1) + { + regs->num_regs = num_regs + 1; + RETALLOC (regs->start, regs->num_regs, regoff_t); + RETALLOC (regs->end, regs->num_regs, regoff_t); + if (regs->start == NULL || regs->end == NULL) + return -2; + } + } + else + { + /* These braces fend off a "empty body in an else-statement" + warning under GCC when assert expands to nothing. */ + assert (bufp->regs_allocated == REGS_FIXED); + } + + /* Convert the pointer data in `regstart' and `regend' to + indices. Register zero has to be set differently, + since we haven't kept track of any info for it. */ + if (regs->num_regs > 0) + { + regs->start[0] = pos; + regs->end[0] = (MATCHING_IN_FIRST_STRING ? d - string1 + : d - string2 + size1); + } + + /* Go through the first `min (num_regs, regs->num_regs)' + registers, since that is all we initialized. */ + for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++) + { + if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt])) + regs->start[mcnt] = regs->end[mcnt] = -1; + else + { + regs->start[mcnt] = POINTER_TO_OFFSET (regstart[mcnt]); + regs->end[mcnt] = POINTER_TO_OFFSET (regend[mcnt]); + } + } + + /* If the regs structure we return has more elements than + were in the pattern, set the extra elements to -1. If + we (re)allocated the registers, this is the case, + because we always allocate enough to have at least one + -1 at the end. */ + for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++) + regs->start[mcnt] = regs->end[mcnt] = -1; + } /* regs && !bufp->no_sub */ + + FREE_VARIABLES (); + DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n", + nfailure_points_pushed, nfailure_points_popped, + nfailure_points_pushed - nfailure_points_popped); + DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed); + + mcnt = d - pos - (MATCHING_IN_FIRST_STRING + ? string1 + : string2 - size1); + + DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt); + + return mcnt; + } + + /* Otherwise match next pattern command. */ +#ifdef SWITCH_ENUM_BUG + switch ((int) ((re_opcode_t) *p++)) +#else + switch ((re_opcode_t) *p++) +#endif + { + /* Ignore these. Used to ignore the n of succeed_n's which + currently have n == 0. */ + case no_op: + DEBUG_PRINT1 ("EXECUTING no_op.\n"); + break; -/* Write PREFETCH; just before fetching a character with *d. */ -#define PREFETCH \ - while (d == dend) \ - { if (dend == end_match_2) goto fail; /* end of string2 => failure */ \ - d = string2; /* end of string1 => advance to string2. */ \ - dend = end_match_2; } - /* This loop loops over pattern commands. - It exits by returning from the function if match is complete, - or it drops through if match fails at this starting point in the input data. */ + /* Match the next n pattern characters exactly. The following + byte in the pattern defines n, and the n bytes after that + are the characters to match. */ + case exactn: + mcnt = *p++; + DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt); - while (1) - { - if (p == pend) - /* End of pattern means we have succeeded! */ - { - /* If caller wants register contents data back, convert it to indices */ - if (regs) + /* This is written out as an if-else so we don't waste time + testing `translate' inside the loop. */ + if (translate) { - regs->start[0] = pos; - if (dend == end_match_1) - regs->end[0] = d - string1; - else - regs->end[0] = d - string2 + size1; - for (mcnt = 1; mcnt < RE_NREGS; mcnt++) + do { - if (regend[mcnt] == (unsigned char *) -1) - { - regs->start[mcnt] = -1; - regs->end[mcnt] = -1; - continue; - } - if (regstart_seg1[mcnt]) - regs->start[mcnt] = regstart[mcnt] - string1; - else - regs->start[mcnt] = regstart[mcnt] - string2 + size1; - if (regend_seg1[mcnt]) - regs->end[mcnt] = regend[mcnt] - string1; - else - regs->end[mcnt] = regend[mcnt] - string2 + size1; + PREFETCH (); + if (translate[(unsigned char) *d++] != (char) *p++) + goto fail; } + while (--mcnt); } - if (dend == end_match_1) - return (d - string1 - pos); else - return d - string2 + size1 - pos; - } + { + do + { + PREFETCH (); + if (*d++ != (char) *p++) goto fail; + } + while (--mcnt); + } + SET_REGS_MATCHED (); + break; - /* Otherwise match next pattern command */ -#ifdef SWITCH_ENUM_BUG - switch ((int) ((enum regexpcode) *p++)) -#else - switch ((enum regexpcode) *p++) -#endif - { - /* \( is represented by a start_memory, \) by a stop_memory. - Both of those commands contain a "register number" argument. - The text matched within the \( and \) is recorded under that number. - Then, \ turns into a `duplicate' command which - is followed by the numeric value of as the register number. */ + /* Match any character except possibly a newline or a null. */ + case anychar: + DEBUG_PRINT1 ("EXECUTING anychar.\n"); - case start_memory: - regstart[*p] = d; - regstart_seg1[*p++] = (dend == end_match_1); - break; + PREFETCH (); - case stop_memory: - regend[*p] = d; - regend_seg1[*p++] = (dend == end_match_1); + if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n') + || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000')) + goto fail; + + SET_REGS_MATCHED (); + DEBUG_PRINT2 (" Matched `%d'.\n", *d); + d++; break; - case duplicate: + + case charset: + case charset_not: { - int regno = *p++; /* Get which register to match against */ - register unsigned char *d2, *dend2; + register unsigned char c; + boolean not = (re_opcode_t) *(p - 1) == charset_not; + + DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : ""); + + PREFETCH (); + c = TRANSLATE (*d); /* The character to match. */ + + /* Cast to `unsigned' instead of `unsigned char' in case the + bit list is a full 32 bytes long. */ + if (c < (unsigned) (*p * BYTEWIDTH) + && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) + not = !not; + + p += 1 + *p; + + if (!not) goto fail; + + SET_REGS_MATCHED (); + d++; + break; + } + - d2 = regstart[regno]; - dend2 = ((regstart_seg1[regno] == regend_seg1[regno]) + /* The beginning of a group is represented by start_memory. + The arguments are the register number in the next byte, and the + number of groups inner to this one in the next. The text + matched within the group is recorded (in the internal + registers data structure) under the register number. */ + case start_memory: + DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]); + + /* Find out if this group can match the empty string. */ + p1 = p; /* To send to group_match_null_string_p. */ + + if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE) + REG_MATCH_NULL_STRING_P (reg_info[*p]) + = group_match_null_string_p (&p1, pend, reg_info); + + /* Save the position in the string where we were the last time + we were at this open-group operator in case the group is + operated upon by a repetition operator, e.g., with `(a*)*b' + against `ab'; then we want to ignore where we are now in + the string in case this attempt to match fails. */ + old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) + ? REG_UNSET (regstart[*p]) ? d : regstart[*p] + : regstart[*p]; + DEBUG_PRINT2 (" old_regstart: %d\n", + POINTER_TO_OFFSET (old_regstart[*p])); + + regstart[*p] = d; + DEBUG_PRINT2 (" regstart: %d\n", POINTER_TO_OFFSET (regstart[*p])); + + IS_ACTIVE (reg_info[*p]) = 1; + MATCHED_SOMETHING (reg_info[*p]) = 0; + + /* This is the new highest active register. */ + highest_active_reg = *p; + + /* If nothing was active before, this is the new lowest active + register. */ + if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) + lowest_active_reg = *p; + + /* Move past the register number and inner group count. */ + p += 2; + break; + + + /* The stop_memory opcode represents the end of a group. Its + arguments are the same as start_memory's: the register + number, and the number of inner groups. */ + case stop_memory: + DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]); + + /* We need to save the string position the last time we were at + this close-group operator in case the group is operated + upon by a repetition operator, e.g., with `((a*)*(b*)*)*' + against `aba'; then we want to ignore where we are now in + the string in case this attempt to match fails. */ + old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) + ? REG_UNSET (regend[*p]) ? d : regend[*p] + : regend[*p]; + DEBUG_PRINT2 (" old_regend: %d\n", + POINTER_TO_OFFSET (old_regend[*p])); + + regend[*p] = d; + DEBUG_PRINT2 (" regend: %d\n", POINTER_TO_OFFSET (regend[*p])); + + /* This register isn't active anymore. */ + IS_ACTIVE (reg_info[*p]) = 0; + + /* If this was the only register active, nothing is active + anymore. */ + if (lowest_active_reg == highest_active_reg) + { + lowest_active_reg = NO_LOWEST_ACTIVE_REG; + highest_active_reg = NO_HIGHEST_ACTIVE_REG; + } + else + { /* We must scan for the new highest active register, since + it isn't necessarily one less than now: consider + (a(b)c(d(e)f)g). When group 3 ends, after the f), the + new highest active register is 1. */ + unsigned char r = *p - 1; + while (r > 0 && !IS_ACTIVE (reg_info[r])) + r--; + + /* If we end up at register zero, that means that we saved + the registers as the result of an `on_failure_jump', not + a `start_memory', and we jumped to past the innermost + `stop_memory'. For example, in ((.)*) we save + registers 1 and 2 as a result of the *, but when we pop + back to the second ), we are at the stop_memory 1. + Thus, nothing is active. */ + if (r == 0) + { + lowest_active_reg = NO_LOWEST_ACTIVE_REG; + highest_active_reg = NO_HIGHEST_ACTIVE_REG; + } + else + highest_active_reg = r; + } + + /* If just failed to match something this time around with a + group that's operated on by a repetition operator, try to + force exit from the ``loop'', and restore the register + information for this group that we had before trying this + last match. */ + if ((!MATCHED_SOMETHING (reg_info[*p]) + || (re_opcode_t) p[-3] == start_memory) + && (p + 2) < pend) + { + boolean is_a_jump_n = false; + + p1 = p + 2; + mcnt = 0; + switch ((re_opcode_t) *p1++) + { + case jump_n: + is_a_jump_n = true; + case pop_failure_jump: + case maybe_pop_jump: + case jump: + case dummy_failure_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + if (is_a_jump_n) + p1 += 2; + break; + + default: + /* do nothing */ ; + } + p1 += mcnt; + + /* If the next operation is a jump backwards in the pattern + to an on_failure_jump right before the start_memory + corresponding to this stop_memory, exit from the loop + by forcing a failure after pushing on the stack the + on_failure_jump's jump in the pattern, and d. */ + if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump + && (re_opcode_t) p1[3] == start_memory && p1[4] == *p) + { + /* If this group ever matched anything, then restore + what its registers were before trying this last + failed match, e.g., with `(a*)*b' against `ab' for + regstart[1], and, e.g., with `((a*)*(b*)*)*' + against `aba' for regend[3]. + + Also restore the registers for inner groups for, + e.g., `((a*)(b*))*' against `aba' (register 3 would + otherwise get trashed). */ + + if (EVER_MATCHED_SOMETHING (reg_info[*p])) + { + unsigned r; + + EVER_MATCHED_SOMETHING (reg_info[*p]) = 0; + + /* Restore this and inner groups' (if any) registers. */ + for (r = *p; r < *p + *(p + 1); r++) + { + regstart[r] = old_regstart[r]; + + /* xx why this test? */ + if ((int) old_regend[r] >= (int) regstart[r]) + regend[r] = old_regend[r]; + } + } + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + PUSH_FAILURE_POINT (p1 + mcnt, d, -2); + + goto fail; + } + } + + /* Move past the register number and the inner group count. */ + p += 2; + break; + + + /* \ has been turned into a `duplicate' command which is + followed by the numeric value of as the register number. */ + case duplicate: + { + register const char *d2, *dend2; + int regno = *p++; /* Get which register to match against. */ + DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno); + + /* Can't back reference a group which we've never matched. */ + if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno])) + goto fail; + + /* Where in input to try to start matching. */ + d2 = regstart[regno]; + + /* Where to stop matching; if both the place to start and + the place to stop matching are in the same string, then + set to the place to stop, otherwise, for now have to use + the end of the first string. */ + + dend2 = ((FIRST_STRING_P (regstart[regno]) + == FIRST_STRING_P (regend[regno])) ? regend[regno] : end_match_1); - while (1) + for (;;) { - /* Advance to next segment in register contents, if necessary */ + /* If necessary, advance to next segment in register + contents. */ while (d2 == dend2) { if (dend2 == end_match_2) break; if (dend2 == regend[regno]) break; - d2 = string2, dend2 = regend[regno]; /* end of string1 => advance to string2. */ + + /* End of string1 => advance to string2. */ + d2 = string2; + dend2 = regend[regno]; } /* At end of register contents => success */ if (d2 == dend2) break; - /* Advance to next segment in data being matched, if necessary */ - PREFETCH; + /* If necessary, advance to next segment in data. */ + PREFETCH (); - /* mcnt gets # consecutive chars to compare */ + /* How many characters left in this segment to match. */ mcnt = dend - d; - if (mcnt > dend2 - d2) + + /* Want how many consecutive characters we can match in + one shot, so, if necessary, adjust the count. */ + if (mcnt > dend2 - d2) mcnt = dend2 - d2; - /* Compare that many; failure if mismatch, else skip them. */ - if (translate ? bcmp_translate (d, d2, mcnt, translate) : bcmp (d, d2, mcnt)) + + /* Compare that many; failure if mismatch, else move + past them. */ + if (translate + ? bcmp_translate (d, d2, mcnt, translate) + : bcmp (d, d2, mcnt)) goto fail; d += mcnt, d2 += mcnt; } } break; - case anychar: - /* fetch a data character */ - PREFETCH; - /* Match anything but a newline. */ - if ((translate ? translate[*d++] : *d++) == '\n') - goto fail; - break; - - case charset: - case charset_not: - { - /* Nonzero for charset_not */ - int not = 0; - register int c; - if (*(p - 1) == (unsigned char) charset_not) - not = 1; - - /* fetch a data character */ - PREFETCH; - - if (translate) - c = translate [*d]; - else - c = *d; - - if (c < *p * BYTEWIDTH - && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) - not = !not; - - p += 1 + *p; - - if (!not) goto fail; - d++; - break; - } + /* begline matches the empty string at the beginning of the string + (unless `not_bol' is set in `bufp'), and, if + `newline_anchor' is set, after newlines. */ case begline: - if (d == string1 || d[-1] == '\n') - break; - goto fail; - + DEBUG_PRINT1 ("EXECUTING begline.\n"); + + if (AT_STRINGS_BEG (d)) + { + if (!bufp->not_bol) break; + } + else if (d[-1] == '\n' && bufp->newline_anchor) + { + break; + } + /* In all other cases, we fail. */ + goto fail; + + + /* endline is the dual of begline. */ case endline: - if (d == end2 - || (d == end1 ? (size2 == 0 || *string2 == '\n') : *d == '\n')) + DEBUG_PRINT1 ("EXECUTING endline.\n"); + + if (AT_STRINGS_END (d)) + { + if (!bufp->not_eol) break; + } + + /* We have to ``prefetch'' the next character. */ + else if ((d == end1 ? *string2 : *d) == '\n' + && bufp->newline_anchor) + { + break; + } + goto fail; + + + /* Match at the very beginning of the data. */ + case begbuf: + DEBUG_PRINT1 ("EXECUTING begbuf.\n"); + if (AT_STRINGS_BEG (d)) + break; + goto fail; + + + /* Match at the very end of the data. */ + case endbuf: + DEBUG_PRINT1 ("EXECUTING endbuf.\n"); + if (AT_STRINGS_END (d)) break; - goto fail; - - /* "or" constructs ("|") are handled by starting each alternative - with an on_failure_jump that points to the start of the next alternative. - Each alternative except the last ends with a jump to the joining point. - (Actually, each jump except for the last one really jumps - to the following jump, because tensioning the jumps is a hassle.) */ - - /* The start of a stupid repeat has an on_failure_jump that points - past the end of the repeat text. - This makes a failure point so that, on failure to match a repetition, - matching restarts past as many repetitions have been found - with no way to fail and look for another one. */ - - /* A smart repeat is similar but loops back to the on_failure_jump - so that each repetition makes another failure point. */ - + goto fail; + + + /* on_failure_keep_string_jump is used to optimize `.*\n'. It + pushes NULL as the value for the string on the stack. Then + `pop_failure_point' will keep the current value for the + string, instead of restoring it. To see why, consider + matching `foo\nbar' against `.*\n'. The .* matches the foo; + then the . fails against the \n. But the next thing we want + to do is match the \n against the \n; if we restored the + string value, we would be back at the foo. + + Because this is used only in specific cases, we don't need to + check all the things that `on_failure_jump' does, to make + sure the right things get saved on the stack. Hence we don't + share its code. The only reason to push anything on the + stack at all is that otherwise we would have to change + `anychar's code to do something besides goto fail in this + case; that seems worse than this. */ + case on_failure_keep_string_jump: + DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump"); + + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt); + + PUSH_FAILURE_POINT (p + mcnt, NULL, -2); + break; + + + /* Uses of on_failure_jump: + + Each alternative starts with an on_failure_jump that points + to the beginning of the next alternative. Each alternative + except the last ends with a jump that in effect jumps past + the rest of the alternatives. (They really jump to the + ending jump of the following alternative, because tensioning + these jumps is a hassle.) + + Repeats start with an on_failure_jump that points past both + the repetition text and either the following jump or + pop_failure_jump back to this on_failure_jump. */ case on_failure_jump: - if (stackp == stacke) - { - unsigned char **stackx; - if (stacke - stackb > re_max_failures * 2) - return -2; - stackx = (unsigned char **) alloca (2 * (stacke - stackb) - * sizeof (char *)); - bcopy (stackb, stackx, (stacke - stackb) * sizeof (char *)); - stackp = stackx + (stackp - stackb); - stacke = stackx + 2 * (stacke - stackb); - stackb = stackx; - } - mcnt = *p++ & 0377; - mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8; - p++; - *stackp++ = mcnt + p; - *stackp++ = d; - break; - - /* The end of a smart repeat has an maybe_finalize_jump back. - Change it either to a finalize_jump or an ordinary jump. */ - - case maybe_finalize_jump: - mcnt = *p++ & 0377; - mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8; - p++; - { + on_failure: + DEBUG_PRINT1 ("EXECUTING on_failure_jump"); + + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt); + + /* If this on_failure_jump comes right before a group (i.e., + the original * applied to a group), save the information + for that group and all inner ones, so that if we fail back + to this point, the group's information will be correct. + For example, in \(a*\)*\1, we need the preceding group, + and in \(\(a*\)b*\)\2, we need the inner group. */ + + /* We can't use `p' to check ahead because we push + a failure point to `p + mcnt' after we do this. */ + p1 = p; + + /* We need to skip no_op's before we look for the + start_memory in case this on_failure_jump is happening as + the result of a completed succeed_n, as in \(a\)\{1,3\}b\1 + against aba. */ + while (p1 < pend && (re_opcode_t) *p1 == no_op) + p1++; + + if (p1 < pend && (re_opcode_t) *p1 == start_memory) + { + /* We have a new highest active register now. This will + get reset at the start_memory we are about to get to, + but we will have saved all the registers relevant to + this repetition op, as described above. */ + highest_active_reg = *(p1 + 1) + *(p1 + 2); + if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) + lowest_active_reg = *(p1 + 1); + } + + DEBUG_PRINT1 (":\n"); + PUSH_FAILURE_POINT (p + mcnt, d, -2); + break; + + + /* A smart repeat ends with `maybe_pop_jump'. + We change it to either `pop_failure_jump' or `jump'. */ + case maybe_pop_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt); + { register unsigned char *p2 = p; - /* Compare what follows with the begining of the repeat. - If we can establish that there is nothing that they would - both match, we can change to finalize_jump */ - while (p2 != pend - && (*p2 == (unsigned char) stop_memory - || *p2 == (unsigned char) start_memory)) - p2++; - if (p2 == pend) - p[-3] = (unsigned char) finalize_jump; - else if (*p2 == (unsigned char) exactn - || *p2 == (unsigned char) endline) + + /* Compare the beginning of the repeat with what in the + pattern follows its end. If we can establish that there + is nothing that they would both match, i.e., that we + would have to backtrack because of (as in, e.g., `a*a') + then we can change to pop_failure_jump, because we'll + never have to backtrack. + + This is not true in the case of alternatives: in + `(a|ab)*' we do need to backtrack to the `ab' alternative + (e.g., if the string was `ab'). But instead of trying to + detect that here, the alternative has put on a dummy + failure point which is what we will end up popping. */ + + /* Skip over open/close-group commands. */ + while (p2 + 2 < pend + && ((re_opcode_t) *p2 == stop_memory + || (re_opcode_t) *p2 == start_memory)) + p2 += 3; /* Skip over args, too. */ + + /* If we're at the end of the pattern, we can change. */ + if (p2 == pend) { - register int c = *p2 == (unsigned char) endline ? '\n' : p2[2]; - register unsigned char *p1 = p + mcnt; - /* p1[0] ... p1[2] are an on_failure_jump. - Examine what follows that */ - if (p1[3] == (unsigned char) exactn && p1[5] != c) - p[-3] = (unsigned char) finalize_jump; - else if (p1[3] == (unsigned char) charset - || p1[3] == (unsigned char) charset_not) + /* Consider what happens when matching ":\(.*\)" + against ":/". I don't really understand this code + yet. */ + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 + (" End of pattern: change to `pop_failure_jump'.\n"); + } + + else if ((re_opcode_t) *p2 == exactn + || (bufp->newline_anchor && (re_opcode_t) *p2 == endline)) + { + register unsigned char c + = *p2 == (unsigned char) endline ? '\n' : p2[2]; + p1 = p + mcnt; + + /* p1[0] ... p1[2] are the `on_failure_jump' corresponding + to the `maybe_finalize_jump' of this case. Examine what + follows. */ + if ((re_opcode_t) p1[3] == exactn && p1[5] != c) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n", + c, p1[5]); + } + + else if ((re_opcode_t) p1[3] == charset + || (re_opcode_t) p1[3] == charset_not) { - int not = p1[3] == (unsigned char) charset_not; - if (c < p1[4] * BYTEWIDTH + int not = (re_opcode_t) p1[3] == charset_not; + + if (c < (unsigned char) (p1[4] * BYTEWIDTH) && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) not = !not; - /* not is 1 if c would match */ - /* That means it is not safe to finalize */ + + /* `not' is equal to 1 if c would match, which means + that we can't change to pop_failure_jump. */ if (!not) - p[-3] = (unsigned char) finalize_jump; + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); + } } } } - p -= 2; - if (p[-1] != (unsigned char) finalize_jump) + p -= 2; /* Point at relative address again. */ + if ((re_opcode_t) p[-1] != pop_failure_jump) { p[-1] = (unsigned char) jump; - goto nofinalize; + DEBUG_PRINT1 (" Match => jump.\n"); + goto unconditional_jump; } - - /* The end of a stupid repeat has a finalize-jump - back to the start, where another failure point will be made - which will point after all the repetitions found so far. */ - - case finalize_jump: - stackp -= 2; - - case jump: - nofinalize: - mcnt = *p++ & 0377; - mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8; - p += mcnt + 1; /* The 1 compensates for missing ++ above */ + /* Note fall through. */ + + + /* The end of a simple repeat has a pop_failure_jump back to + its matching on_failure_jump, where the latter will push a + failure point. The pop_failure_jump takes off failure + points put on by this pop_failure_jump's matching + on_failure_jump; we got through the pattern to here from the + matching on_failure_jump, so didn't fail. */ + case pop_failure_jump: + { + /* We need to pass separate storage for the lowest and + highest registers, even though we don't care about the + actual values. Otherwise, we will restore only one + register from the stack, since lowest will == highest in + `pop_failure_point'. */ + unsigned dummy_low_reg, dummy_high_reg; + unsigned char *pdummy; + const char *sdummy; + + DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n"); + POP_FAILURE_POINT (sdummy, pdummy, + dummy_low_reg, dummy_high_reg, + reg_dummy, reg_dummy, reg_info_dummy); + } + /* Note fall through. */ + + + /* Unconditionally jump (without popping any failure points). */ + case jump: + unconditional_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p); /* Get the amount to jump. */ + DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt); + p += mcnt; /* Do the jump. */ + DEBUG_PRINT2 ("(to 0x%x).\n", p); break; - case dummy_failure_jump: - if (stackp == stacke) - { - unsigned char **stackx - = (unsigned char **) alloca (2 * (stacke - stackb) - * sizeof (char *)); - bcopy (stackb, stackx, (stacke - stackb) * sizeof (char *)); - stackp = stackx + (stackp - stackb); - stacke = stackx + 2 * (stacke - stackb); - stackb = stackx; - } - *stackp++ = 0; - *stackp++ = 0; - goto nofinalize; - - case wordbound: - if (d == string1 /* Points to first char */ - || d == end2 /* Points to end */ - || (d == end1 && size2 == 0)) /* Points to end */ - break; - if ((SYNTAX (d[-1]) == Sword) - != (SYNTAX (d == end1 ? *string2 : *d) == Sword)) + + /* We need this opcode so we can detect where alternatives end + in `group_match_null_string_p' et al. */ + case jump_past_alt: + DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n"); + goto unconditional_jump; + + + /* Normally, the on_failure_jump pushes a failure point, which + then gets popped at pop_failure_jump. We will end up at + pop_failure_jump, also, and with a pattern of, say, `a+', we + are skipping over the on_failure_jump, so we have to push + something meaningless for pop_failure_jump to pop. */ + case dummy_failure_jump: + DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n"); + /* It doesn't matter what we push for the string here. What + the code at `fail' tests is the value for the pattern. */ + PUSH_FAILURE_POINT (0, 0, -2); + goto unconditional_jump; + + + /* At the end of an alternative, we need to push a dummy failure + point in case we are followed by a `pop_failure_jump', because + we don't want the failure point for the alternative to be + popped. For example, matching `(a|ab)*' against `aab' + requires that we match the `ab' alternative. */ + case push_dummy_failure: + DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n"); + /* See comments just above at `dummy_failure_jump' about the + two zeroes. */ + PUSH_FAILURE_POINT (0, 0, -2); + break; + + /* Have to succeed matching what follows at least n times. + After that, handle like `on_failure_jump'. */ + case succeed_n: + EXTRACT_NUMBER (mcnt, p + 2); + DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt); + + assert (mcnt >= 0); + /* Originally, this is how many times we HAVE to succeed. */ + if (mcnt > 0) + { + mcnt--; + p += 2; + STORE_NUMBER_AND_INCR (p, mcnt); + DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p, mcnt); + } + else if (mcnt == 0) + { + DEBUG_PRINT2 (" Setting two bytes from 0x%x to no_op.\n", p+2); + p[2] = (unsigned char) no_op; + p[3] = (unsigned char) no_op; + goto on_failure; + } + break; + + case jump_n: + EXTRACT_NUMBER (mcnt, p + 2); + DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt); + + /* Originally, this is how many times we CAN jump. */ + if (mcnt) + { + mcnt--; + STORE_NUMBER (p + 2, mcnt); + goto unconditional_jump; + } + /* If don't have to jump any more, skip over the rest of command. */ + else + p += 4; + break; + + case set_number_at: + { + DEBUG_PRINT1 ("EXECUTING set_number_at.\n"); + + EXTRACT_NUMBER_AND_INCR (mcnt, p); + p1 = p + mcnt; + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p1, mcnt); + STORE_NUMBER (p1, mcnt); + break; + } + + case wordbound: + DEBUG_PRINT1 ("EXECUTING wordbound.\n"); + if (AT_WORD_BOUNDARY (d)) break; - goto fail; + goto fail; case notwordbound: - if (d == string1 /* Points to first char */ - || d == end2 /* Points to end */ - || (d == end1 && size2 == 0)) /* Points to end */ - goto fail; - if ((SYNTAX (d[-1]) == Sword) - != (SYNTAX (d == end1 ? *string2 : *d) == Sword)) + DEBUG_PRINT1 ("EXECUTING notwordbound.\n"); + if (AT_WORD_BOUNDARY (d)) goto fail; - break; + break; case wordbeg: - if (d == end2 /* Points to end */ - || (d == end1 && size2 == 0) /* Points to end */ - || SYNTAX (* (d == end1 ? string2 : d)) != Sword) /* Next char not a letter */ - goto fail; - if (d == string1 /* Points to first char */ - || SYNTAX (d[-1]) != Sword) /* prev char not letter */ + DEBUG_PRINT1 ("EXECUTING wordbeg.\n"); + if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1))) break; - goto fail; + goto fail; case wordend: - if (d == string1 /* Points to first char */ - || SYNTAX (d[-1]) != Sword) /* prev char not letter */ - goto fail; - if (d == end2 /* Points to end */ - || (d == end1 && size2 == 0) /* Points to end */ - || SYNTAX (d == end1 ? *string2 : *d) != Sword) /* Next char not a letter */ + DEBUG_PRINT1 ("EXECUTING wordend.\n"); + if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1) + && (!WORDCHAR_P (d) || AT_STRINGS_END (d))) break; - goto fail; + goto fail; #ifdef emacs - case before_dot: - if (((d - string2 <= (unsigned) size2) - ? d - bf_p2 : d - bf_p1) - <= point) - goto fail; - break; - +#ifdef emacs19 + case before_dot: + DEBUG_PRINT1 ("EXECUTING before_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) >= point) + goto fail; + break; + + case at_dot: + DEBUG_PRINT1 ("EXECUTING at_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) != point) + goto fail; + break; + + case after_dot: + DEBUG_PRINT1 ("EXECUTING after_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) <= point) + goto fail; + break; +#else /* not emacs19 */ case at_dot: - if (((d - string2 <= (unsigned) size2) - ? d - bf_p2 : d - bf_p1) - == point) - goto fail; - break; - - case after_dot: - if (((d - string2 <= (unsigned) size2) - ? d - bf_p2 : d - bf_p1) - >= point) + DEBUG_PRINT1 ("EXECUTING at_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) + 1 != point) goto fail; break; - - case wordchar: - mcnt = (int) Sword; - goto matchsyntax; +#endif /* not emacs19 */ case syntaxspec: + DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt); mcnt = *p++; - matchsyntax: - PREFETCH; - if (SYNTAX (*d++) != (enum syntaxcode) mcnt) goto fail; - break; - - case notwordchar: + goto matchsyntax; + + case wordchar: + DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n"); mcnt = (int) Sword; - goto matchnotsyntax; + matchsyntax: + PREFETCH (); + if (SYNTAX (*d++) != (enum syntaxcode) mcnt) + goto fail; + SET_REGS_MATCHED (); + break; case notsyntaxspec: + DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt); mcnt = *p++; - matchnotsyntax: - PREFETCH; - if (SYNTAX (*d++) == (enum syntaxcode) mcnt) goto fail; - break; -#else + goto matchnotsyntax; + + case notwordchar: + DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n"); + mcnt = (int) Sword; + matchnotsyntax: + PREFETCH (); + if (SYNTAX (*d++) == (enum syntaxcode) mcnt) + goto fail; + SET_REGS_MATCHED (); + break; + +#else /* not emacs */ case wordchar: - PREFETCH; - if (SYNTAX (*d++) == 0) goto fail; + DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n"); + PREFETCH (); + if (!WORDCHAR_P (d)) + goto fail; + SET_REGS_MATCHED (); + d++; break; case notwordchar: - PREFETCH; - if (SYNTAX (*d++) != 0) goto fail; + DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n"); + PREFETCH (); + if (WORDCHAR_P (d)) + goto fail; + SET_REGS_MATCHED (); + d++; break; #endif /* not emacs */ + + default: + abort (); + } + continue; /* Successfully executed one pattern command; keep going. */ - case begbuf: - if (d == string1) /* Note, d cannot equal string2 */ - break; /* unless string1 == string2. */ - goto fail; - case endbuf: - if (d == end2 || (d == end1 && size2 == 0)) - break; - goto fail; + /* We goto here if a matching operation fails. */ + fail: + if (!FAIL_STACK_EMPTY ()) + { /* A restart point is known. Restore to that state. */ + DEBUG_PRINT1 ("\nFAIL:\n"); + POP_FAILURE_POINT (d, p, + lowest_active_reg, highest_active_reg, + regstart, regend, reg_info); + + /* If this failure point is a dummy, try the next one. */ + if (!p) + goto fail; - case exactn: - /* Match the next few pattern characters exactly. - mcnt is how many characters to match. */ - mcnt = *p++; - if (translate) - { - do - { - PREFETCH; - if (translate[*d++] != *p++) goto fail; - } - while (--mcnt); - } - else + /* If we failed to the end of the pattern, don't examine *p. */ + assert (p <= pend); + if (p < pend) + { + boolean is_a_jump_n = false; + + /* If failed to a backwards jump that's part of a repetition + loop, need to pop this failure point and use the next one. */ + switch ((re_opcode_t) *p) + { + case jump_n: + is_a_jump_n = true; + case maybe_pop_jump: + case pop_failure_jump: + case jump: + p1 = p + 1; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + p1 += mcnt; + + if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n) + || (!is_a_jump_n + && (re_opcode_t) *p1 == on_failure_jump)) + goto fail; + break; + default: + /* do nothing */ ; + } + } + + if (d >= string1 && d <= end1) + dend = end_match_1; + } + else + break; /* Matching at this starting point really fails. */ + } /* for (;;) */ + + if (best_regs_set) + goto restore_best_regs; + + FREE_VARIABLES (); + + return -1; /* Failure to match. */ +} /* re_match_2 */ + +/* Subroutine definitions for re_match_2. */ + + +/* We are passed P pointing to a register number after a start_memory. + + Return true if the pattern up to the corresponding stop_memory can + match the empty string, and false otherwise. + + If we find the matching stop_memory, sets P to point to one past its number. + Otherwise, sets P to an undefined byte less than or equal to END. + + We don't handle duplicates properly (yet). */ + +static boolean +group_match_null_string_p (p, end, reg_info) + unsigned char **p, *end; + register_info_type *reg_info; +{ + int mcnt; + /* Point to after the args to the start_memory. */ + unsigned char *p1 = *p + 2; + + while (p1 < end) + { + /* Skip over opcodes that can match nothing, and return true or + false, as appropriate, when we get to one that can't, or to the + matching stop_memory. */ + + switch ((re_opcode_t) *p1) + { + /* Could be either a loop or a series of alternatives. */ + case on_failure_jump: + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + + /* If the next operation is not a jump backwards in the + pattern. */ + + if (mcnt >= 0) { - do - { - PREFETCH; - if (*d++ != *p++) goto fail; - } - while (--mcnt); - } - break; + /* Go through the on_failure_jumps of the alternatives, + seeing if any of the alternatives cannot match nothing. + The last alternative starts with only a jump, + whereas the rest start with on_failure_jump and end + with a jump, e.g., here is the pattern for `a|b|c': + + /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6 + /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3 + /exactn/1/c + + So, we have to first go through the first (n-1) + alternatives and then deal with the last one separately. */ + + + /* Deal with the first (n-1) alternatives, which start + with an on_failure_jump (see above) that jumps to right + past a jump_past_alt. */ + + while ((re_opcode_t) p1[mcnt-3] == jump_past_alt) + { + /* `mcnt' holds how many bytes long the alternative + is, including the ending `jump_past_alt' and + its number. */ + + if (!alt_match_null_string_p (p1, p1 + mcnt - 3, + reg_info)) + return false; + + /* Move to right after this alternative, including the + jump_past_alt. */ + p1 += mcnt; + + /* Break if it's the beginning of an n-th alternative + that doesn't begin with an on_failure_jump. */ + if ((re_opcode_t) *p1 != on_failure_jump) + break; + + /* Still have to check that it's not an n-th + alternative that starts with an on_failure_jump. */ + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + if ((re_opcode_t) p1[mcnt-3] != jump_past_alt) + { + /* Get to the beginning of the n-th alternative. */ + p1 -= 3; + break; + } + } + + /* Deal with the last alternative: go back and get number + of the `jump_past_alt' just before it. `mcnt' contains + the length of the alternative. */ + EXTRACT_NUMBER (mcnt, p1 - 2); + + if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info)) + return false; + + p1 += mcnt; /* Get past the n-th alternative. */ + } /* if mcnt > 0 */ + break; + + + case stop_memory: + assert (p1[1] == **p); + *p = p1 + 2; + return true; + + + default: + if (!common_op_match_null_string_p (&p1, end, reg_info)) + return false; + } + } /* while p1 < end */ + + return false; +} /* group_match_null_string_p */ + + +/* Similar to group_match_null_string_p, but doesn't deal with alternatives: + It expects P to be the first byte of a single alternative and END one + byte past the last. The alternative can contain groups. */ + +static boolean +alt_match_null_string_p (p, end, reg_info) + unsigned char *p, *end; + register_info_type *reg_info; +{ + int mcnt; + unsigned char *p1 = p; + + while (p1 < end) + { + /* Skip over opcodes that can match nothing, and break when we get + to one that can't. */ + + switch ((re_opcode_t) *p1) + { + /* It's a loop. */ + case on_failure_jump: + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + p1 += mcnt; + break; + + default: + if (!common_op_match_null_string_p (&p1, end, reg_info)) + return false; + } + } /* while p1 < end */ + + return true; +} /* alt_match_null_string_p */ + + +/* Deals with the ops common to group_match_null_string_p and + alt_match_null_string_p. + + Sets P to one after the op and its arguments, if any. */ + +static boolean +common_op_match_null_string_p (p, end, reg_info) + unsigned char **p, *end; + register_info_type *reg_info; +{ + int mcnt; + boolean ret; + int reg_no; + unsigned char *p1 = *p; - default: - break; - } - continue; /* Successfully matched one pattern command; keep matching */ + switch ((re_opcode_t) *p1++) + { + case no_op: + case begline: + case endline: + case begbuf: + case endbuf: + case wordbeg: + case wordend: + case wordbound: + case notwordbound: +#ifdef emacs + case before_dot: + case at_dot: + case after_dot: +#endif + break; - /* Jump here if any matching operation fails. */ - fail: - if (stackp != stackb) - /* A restart point is known. Restart there and pop it. */ - { - if (!stackp[-2]) - { /* If innermost failure point is dormant, flush it and keep looking */ - stackp -= 2; - goto fail; - } - d = *--stackp; - p = *--stackp; - if (d >= string1 && d <= end1) - dend = end_match_1; - } - else break; /* Matching at this starting point really fails! */ - } - return -1; /* Failure to match */ -} + case start_memory: + reg_no = *p1; + assert (reg_no > 0 && reg_no <= MAX_REGNUM); + ret = group_match_null_string_p (&p1, end, reg_info); + + /* Have to set this here in case we're checking a group which + contains a group and a back reference to it. */ + + if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE) + REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret; + + if (!ret) + return false; + break; + + /* If this is an optimized succeed_n for zero times, make the jump. */ + case jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + if (mcnt >= 0) + p1 += mcnt; + else + return false; + break; + + case succeed_n: + /* Get to the number of times to succeed. */ + p1 += 2; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + + if (mcnt == 0) + { + p1 -= 4; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + p1 += mcnt; + } + else + return false; + break; + + case duplicate: + if (!REG_MATCH_NULL_STRING_P (reg_info[*p1])) + return false; + break; + case set_number_at: + p1 += 4; + + default: + /* All other opcodes mean we cannot match the empty string. */ + return false; + } + + *p = p1; + return true; +} /* common_op_match_null_string_p */ + + +/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN + bytes; nonzero otherwise. */ + static int bcmp_translate (s1, s2, len, translate) unsigned char *s1, *s2; register int len; - unsigned char *translate; + char *translate; { register unsigned char *p1 = s1, *p2 = s2; while (len) { - if (translate [*p1++] != translate [*p2++]) return 1; + if (translate[*p1++] != translate[*p2++]) return 1; len--; } return 0; } -/* Entry points compatible with bsd4.2 regex library */ +/* Entry points for GNU code. */ + +/* re_compile_pattern is the GNU regular expression compiler: it + compiles PATTERN (of length SIZE) and puts the result in BUFP. + Returns 0 if the pattern was valid, otherwise an error string. + + Assumes the `allocated' (and perhaps `buffer') and `translate' fields + are set in BUFP on entry. + + We call regex_compile to do the actual compilation. */ + +const char * +re_compile_pattern (pattern, length, bufp) + const char *pattern; + int length; + struct re_pattern_buffer *bufp; +{ + reg_errcode_t ret; + + /* GNU code is written to assume at least RE_NREGS registers will be set + (and at least one extra will be -1). */ + bufp->regs_allocated = REGS_UNALLOCATED; + + /* And GNU code determines whether or not to get register information + by passing null for the REGS argument to re_match, etc., not by + setting no_sub. */ + bufp->no_sub = 0; + + /* Match anchors at newline. */ + bufp->newline_anchor = 1; + + ret = regex_compile (pattern, length, re_syntax_options, bufp); + + return re_error_msg[(int) ret]; +} + +/* Entry points compatible with 4.2 BSD regex library. We don't define + them if this is an Emacs or POSIX compilation. */ -#ifndef emacs +#if !defined (emacs) && !defined (_POSIX_SOURCE) +/* BSD has one and only one pattern buffer. */ static struct re_pattern_buffer re_comp_buf; char * re_comp (s) - char *s; + const char *s; { + reg_errcode_t ret; + if (!s) { if (!re_comp_buf.buffer) @@ -1610,159 +4699,289 @@ re_comp (s) if (!re_comp_buf.buffer) { - if (!(re_comp_buf.buffer = (char *) malloc (200))) - return "Memory exhausted"; + re_comp_buf.buffer = (unsigned char *) malloc (200); + if (re_comp_buf.buffer == NULL) + return "Memory exhausted"; re_comp_buf.allocated = 200; - if (!(re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH))) + + re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH); + if (re_comp_buf.fastmap == NULL) return "Memory exhausted"; } - return re_compile_pattern (s, strlen (s), &re_comp_buf); + + /* Since `re_exec' always passes NULL for the `regs' argument, we + don't need to initialize the pattern buffer fields which affect it. */ + + /* Match anchors at newlines. */ + re_comp_buf.newline_anchor = 1; + + ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf); + + /* Yes, we're discarding `const' here. */ + return (char *) re_error_msg[(int) ret]; } + int re_exec (s) - char *s; + const char *s; { - int len = strlen (s); - return 0 <= re_search (&re_comp_buf, s, len, 0, len, 0); + const int len = strlen (s); + return + 0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0); } - -#endif /* emacs */ +#endif /* not emacs and not _POSIX_SOURCE */ -#ifdef test +/* POSIX.2 functions. Don't define these for Emacs. */ -#include +#ifndef emacs -/* Indexed by a character, gives the upper case equivalent of the character */ - -static char upcase[0400] = - { 000, 001, 002, 003, 004, 005, 006, 007, - 010, 011, 012, 013, 014, 015, 016, 017, - 020, 021, 022, 023, 024, 025, 026, 027, - 030, 031, 032, 033, 034, 035, 036, 037, - 040, 041, 042, 043, 044, 045, 046, 047, - 050, 051, 052, 053, 054, 055, 056, 057, - 060, 061, 062, 063, 064, 065, 066, 067, - 070, 071, 072, 073, 074, 075, 076, 077, - 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107, - 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, - 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, - 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137, - 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107, - 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, - 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, - 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177, - 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, - 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, - 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, - 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, - 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, - 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, - 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, - 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, - 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, - 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, - 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, - 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, - 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, - 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, - 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, - 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377 - }; +/* regcomp takes a regular expression as a string and compiles it. -main (argc, argv) - int argc; - char **argv; -{ - char pat[80]; - struct re_pattern_buffer buf; - int i; - char c; - char fastmap[(1 << BYTEWIDTH)]; - - /* Allow a command argument to specify the style of syntax. */ - if (argc > 1) - obscure_syntax = atoi (argv[1]); - - buf.allocated = 40; - buf.buffer = (char *) malloc (buf.allocated); - buf.fastmap = fastmap; - buf.translate = upcase; - - while (1) - { - gets (pat); + PREG is a regex_t *. We do not expect any fields to be initialized, + since POSIX says we shouldn't. Thus, we set - if (*pat) - { - re_compile_pattern (pat, strlen(pat), &buf); + `buffer' to the compiled pattern; + `used' to the length of the compiled pattern; + `syntax' to RE_SYNTAX_POSIX_EXTENDED if the + REG_EXTENDED bit in CFLAGS is set; otherwise, to + RE_SYNTAX_POSIX_BASIC; + `newline_anchor' to REG_NEWLINE being set in CFLAGS; + `fastmap' and `fastmap_accurate' to zero; + `re_nsub' to the number of subexpressions in PATTERN. - for (i = 0; i < buf.used; i++) - printchar (buf.buffer[i]); + PATTERN is the address of the pattern string. - putchar ('\n'); + CFLAGS is a series of bits which affect compilation. - printf ("%d allocated, %d used.\n", buf.allocated, buf.used); + If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we + use POSIX basic syntax. - re_compile_fastmap (&buf); - printf ("Allowed by fastmap: "); - for (i = 0; i < (1 << BYTEWIDTH); i++) - if (fastmap[i]) printchar (i); - putchar ('\n'); - } + If REG_NEWLINE is set, then . and [^...] don't match newline. + Also, regexec will try a match beginning after every newline. + + If REG_ICASE is set, then we considers upper- and lowercase + versions of letters to be equivalent when matching. - gets (pat); /* Now read the string to match against */ + If REG_NOSUB is set, then when PREG is passed to regexec, that + routine will report only success or failure, and nothing about the + registers. - i = re_match (&buf, pat, strlen (pat), 0, 0); - printf ("Match value %d.\n", i); + It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for + the return codes and their meanings.) */ + +int +regcomp (preg, pattern, cflags) + regex_t *preg; + const char *pattern; + int cflags; +{ + reg_errcode_t ret; + unsigned syntax + = (cflags & REG_EXTENDED) ? + RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC; + + /* regex_compile will allocate the space for the compiled pattern. */ + preg->buffer = 0; + preg->allocated = 0; + preg->used = 0; + + /* Don't bother to use a fastmap when searching. This simplifies the + REG_NEWLINE case: if we used a fastmap, we'd have to put all the + characters after newlines into the fastmap. This way, we just try + every character. */ + preg->fastmap = 0; + + if (cflags & REG_ICASE) + { + unsigned i; + + preg->translate = (char *) malloc (CHAR_SET_SIZE); + if (preg->translate == NULL) + return (int) REG_ESPACE; + + /* Map uppercase characters to corresponding lowercase ones. */ + for (i = 0; i < CHAR_SET_SIZE; i++) + preg->translate[i] = ISUPPER (i) ? tolower (i) : i; } + else + preg->translate = NULL; + + /* If REG_NEWLINE is set, newlines are treated differently. */ + if (cflags & REG_NEWLINE) + { /* REG_NEWLINE implies neither . nor [^...] match newline. */ + syntax &= ~RE_DOT_NEWLINE; + syntax |= RE_HAT_LISTS_NOT_NEWLINE; + /* It also changes the matching behavior. */ + preg->newline_anchor = 1; + } + else + preg->newline_anchor = 0; + + preg->no_sub = !!(cflags & REG_NOSUB); + + /* POSIX says a null character in the pattern terminates it, so we + can use strlen here in compiling the pattern. */ + ret = regex_compile (pattern, strlen (pattern), syntax, preg); + + /* POSIX doesn't distinguish between an unmatched open-group and an + unmatched close-group: both are REG_EPAREN. */ + if (ret == REG_ERPAREN) ret = REG_EPAREN; + + return (int) ret; } -#ifdef NOTDEF -print_buf (bufp) - struct re_pattern_buffer *bufp; + +/* regexec searches for a given pattern, specified by PREG, in the + string STRING. + + If NMATCH is zero or REG_NOSUB was set in the cflags argument to + `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at + least NMATCH elements, and we set them to the offsets of the + corresponding matched substrings. + + EFLAGS specifies `execution flags' which affect matching: if + REG_NOTBOL is set, then ^ does not match at the beginning of the + string; if REG_NOTEOL is set, then $ does not match at the end. + + We return 0 if we find a match and REG_NOMATCH if not. */ + +int +regexec (preg, string, nmatch, pmatch, eflags) + const regex_t *preg; + const char *string; + size_t nmatch; + regmatch_t pmatch[]; + int eflags; { - int i; + int ret; + struct re_registers regs; + regex_t private_preg; + int len = strlen (string); + boolean want_reg_info = !preg->no_sub && nmatch > 0; - printf ("buf is :\n----------------\n"); - for (i = 0; i < bufp->used; i++) - printchar (bufp->buffer[i]); + private_preg = *preg; - printf ("\n%d allocated, %d used.\n", bufp->allocated, bufp->used); + private_preg.not_bol = !!(eflags & REG_NOTBOL); + private_preg.not_eol = !!(eflags & REG_NOTEOL); + + /* The user has told us exactly how many registers to return + information about, via `nmatch'. We have to pass that on to the + matching routines. */ + private_preg.regs_allocated = REGS_FIXED; + + if (want_reg_info) + { + regs.num_regs = nmatch; + regs.start = TALLOC (nmatch, regoff_t); + regs.end = TALLOC (nmatch, regoff_t); + if (regs.start == NULL || regs.end == NULL) + return (int) REG_NOMATCH; + } + + /* Perform the searching operation. */ + ret = re_search (&private_preg, string, len, + /* start: */ 0, /* range: */ len, + want_reg_info ? ®s : (struct re_registers *) 0); - printf ("Allowed by fastmap: "); - for (i = 0; i < (1 << BYTEWIDTH); i++) - if (bufp->fastmap[i]) - printchar (i); - printf ("\nAllowed by translate: "); - if (bufp->translate) - for (i = 0; i < (1 << BYTEWIDTH); i++) - if (bufp->translate[i]) - printchar (i); - printf ("\nfastmap is%s accurate\n", bufp->fastmap_accurate ? "" : "n't"); - printf ("can %s be null\n----------", bufp->can_be_null ? "" : "not"); + /* Copy the register information to the POSIX structure. */ + if (want_reg_info) + { + if (ret >= 0) + { + unsigned r; + + for (r = 0; r < nmatch; r++) + { + pmatch[r].rm_so = regs.start[r]; + pmatch[r].rm_eo = regs.end[r]; + } + } + + /* If we needed the temporary register info, free the space now. */ + free (regs.start); + free (regs.end); + } + + /* We want zero return to mean success, unlike `re_search'. */ + return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH; } -#endif -printchar (c) - char c; + +/* Returns a message corresponding to an error code, ERRCODE, returned + from either regcomp or regexec. We don't use PREG here. */ + +size_t +regerror (errcode, preg, errbuf, errbuf_size) + int errcode; + const regex_t *preg; + char *errbuf; + size_t errbuf_size; { - if (c < 041 || c >= 0177) + const char *msg; + size_t msg_size; + + if (errcode < 0 + || errcode >= (sizeof (re_error_msg) / sizeof (re_error_msg[0]))) + /* Only error codes returned by the rest of the code should be passed + to this routine. If we are given anything else, or if other regex + code generates an invalid error code, then the program has a bug. + Dump core so we can fix it. */ + abort (); + + msg = re_error_msg[errcode]; + + /* POSIX doesn't require that we do anything in this case, but why + not be nice. */ + if (! msg) + msg = "Success"; + + msg_size = strlen (msg) + 1; /* Includes the null. */ + + if (errbuf_size != 0) { - putchar ('\\'); - putchar (((c >> 6) & 3) + '0'); - putchar (((c >> 3) & 7) + '0'); - putchar ((c & 7) + '0'); + if (msg_size > errbuf_size) + { + strncpy (errbuf, msg, errbuf_size - 1); + errbuf[errbuf_size - 1] = 0; + } + else + strcpy (errbuf, msg); } - else - putchar (c); + + return msg_size; } -error (string) - char *string; + +/* Free dynamically allocated space used by PREG. */ + +void +regfree (preg) + regex_t *preg; { - puts (string); - exit (1); + if (preg->buffer != NULL) + free (preg->buffer); + preg->buffer = NULL; + + preg->allocated = 0; + preg->used = 0; + + if (preg->fastmap != NULL) + free (preg->fastmap); + preg->fastmap = NULL; + preg->fastmap_accurate = 0; + + if (preg->translate != NULL) + free (preg->translate); + preg->translate = NULL; } -#endif /* test */ +#endif /* not emacs */ + +/* +Local variables: +make-backup-files: t +version-control: t +trim-versions-without-asking: nil +End: +*/ diff --git a/gnu/usr.bin/grep/regex.h b/gnu/usr.bin/grep/regex.h index a5e89d32cc..408dd21034 100644 --- a/gnu/usr.bin/grep/regex.h +++ b/gnu/usr.bin/grep/regex.h @@ -1,5 +1,7 @@ -/* Definitions for data structures callers pass the regex library. - Copyright (C) 1985, 1989 Free Software Foundation, Inc. +/* Definitions for data structures and routines for the regular + expression library, version 0.12. + + Copyright (C) 1985, 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -13,173 +15,476 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - In other words, you are welcome to use, share and improve this program. - You are forbidden to forbid anyone else to use, share and improve - what you give them. Help stamp out software-hoarding! */ +#ifndef __REGEXP_LIBRARY_H__ +#define __REGEXP_LIBRARY_H__ +/* POSIX says that must be included (by the caller) before + . */ -/* Define number of parens for which we record the beginnings and ends. - This affects how much space the `struct re_registers' type takes up. */ -#ifndef RE_NREGS -#define RE_NREGS 10 +#ifdef VMS +/* VMS doesn't have `size_t' in , even though POSIX says it + should be there. */ +#include #endif -/* These bits are used in the obscure_syntax variable to choose among - alternative regexp syntaxes. */ - -/* 1 means plain parentheses serve as grouping, and backslash - parentheses are needed for literal searching. - 0 means backslash-parentheses are grouping, and plain parentheses - are for literal searching. */ -#define RE_NO_BK_PARENS 1 - -/* 1 means plain | serves as the "or"-operator, and \| is a literal. - 0 means \| serves as the "or"-operator, and | is a literal. */ -#define RE_NO_BK_VBAR 2 - -/* 0 means plain + or ? serves as an operator, and \+, \? are literals. - 1 means \+, \? are operators and plain +, ? are literals. */ -#define RE_BK_PLUS_QM 4 - -/* 1 means | binds tighter than ^ or $. - 0 means the contrary. */ -#define RE_TIGHT_VBAR 8 - -/* 1 means treat \n as an _OR operator - 0 means treat it as a normal character */ -#define RE_NEWLINE_OR 16 - -/* 0 means that a special characters (such as *, ^, and $) always have - their special meaning regardless of the surrounding context. - 1 means that special characters may act as normal characters in some - contexts. Specifically, this applies to: - ^ - only special at the beginning, or after ( or | - $ - only special at the end, or before ) or | - *, +, ? - only special when not after the beginning, (, or | */ -#define RE_CONTEXT_INDEP_OPS 32 - -/* Now define combinations of bits for the standard possibilities. */ -#define RE_SYNTAX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_CONTEXT_INDEP_OPS) -#define RE_SYNTAX_EGREP (RE_SYNTAX_AWK | RE_NEWLINE_OR) -#define RE_SYNTAX_GREP (RE_BK_PLUS_QM | RE_NEWLINE_OR) + +/* The following bits are used to determine the regexp syntax we + recognize. The set/not-set meanings are chosen so that Emacs syntax + remains the value 0. The bits are given in alphabetical order, and + the definitions shifted by one from the previous bit; thus, when we + add or remove a bit, only one other definition need change. */ +typedef unsigned reg_syntax_t; + +/* If this bit is not set, then \ inside a bracket expression is literal. + If set, then such a \ quotes the following character. */ +#define RE_BACKSLASH_ESCAPE_IN_LISTS (1) + +/* If this bit is not set, then + and ? are operators, and \+ and \? are + literals. + If set, then \+ and \? are operators and + and ? are literals. */ +#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1) + +/* If this bit is set, then character classes are supported. They are: + [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], + [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. + If not set, then character classes are not supported. */ +#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1) + +/* If this bit is set, then ^ and $ are always anchors (outside bracket + expressions, of course). + If this bit is not set, then it depends: + ^ is an anchor if it is at the beginning of a regular + expression or after an open-group or an alternation operator; + $ is an anchor if it is at the end of a regular expression, or + before a close-group or an alternation operator. + + This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because + POSIX draft 11.2 says that * etc. in leading positions is undefined. + We already implemented a previous draft which made those constructs + invalid, though, so we haven't changed the code back. */ +#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1) + +/* If this bit is set, then special characters are always special + regardless of where they are in the pattern. + If this bit is not set, then special characters are special only in + some contexts; otherwise they are ordinary. Specifically, + * + ? and intervals are only special when not after the beginning, + open-group, or alternation operator. */ +#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1) + +/* If this bit is set, then *, +, ?, and { cannot be first in an re or + immediately after an alternation or begin-group operator. */ +#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1) + +/* If this bit is set, then . matches newline. + If not set, then it doesn't. */ +#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1) + +/* If this bit is set, then . doesn't match NUL. + If not set, then it does. */ +#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1) + +/* If this bit is set, nonmatching lists [^...] do not match newline. + If not set, they do. */ +#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1) + +/* If this bit is set, either \{...\} or {...} defines an + interval, depending on RE_NO_BK_BRACES. + If not set, \{, \}, {, and } are literals. */ +#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1) + +/* If this bit is set, +, ? and | aren't recognized as operators. + If not set, they are. */ +#define RE_LIMITED_OPS (RE_INTERVALS << 1) + +/* If this bit is set, newline is an alternation operator. + If not set, newline is literal. */ +#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1) + +/* If this bit is set, then `{...}' defines an interval, and \{ and \} + are literals. + If not set, then `\{...\}' defines an interval. */ +#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1) + +/* If this bit is set, (...) defines a group, and \( and \) are literals. + If not set, \(...\) defines a group, and ( and ) are literals. */ +#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1) + +/* If this bit is set, then \ matches . + If not set, then \ is a back-reference. */ +#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1) + +/* If this bit is set, then | is an alternation operator, and \| is literal. + If not set, then \| is an alternation operator, and | is literal. */ +#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1) + +/* If this bit is set, then an ending range point collating higher + than the starting range point, as in [z-a], is invalid. + If not set, then when ending range point collates higher than the + starting range point, the range is ignored. */ +#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1) + +/* If this bit is set, then an unmatched ) is ordinary. + If not set, then an unmatched ) is invalid. */ +#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1) + +/* This global variable defines the particular regexp syntax to use (for + some interfaces). When a regexp is compiled, the syntax used is + stored in the pattern buffer, so changing this does not affect + already-compiled regexps. */ +extern reg_syntax_t re_syntax_options; + +/* Define combinations of the above bits for the standard possibilities. + (The [[[ comments delimit what gets put into the Texinfo file, so + don't delete them!) */ +/* [[[begin syntaxes]]] */ #define RE_SYNTAX_EMACS 0 -/* This data structure is used to represent a compiled pattern. */ +#define RE_SYNTAX_AWK \ + (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \ + | RE_UNMATCHED_RIGHT_PAREN_ORD) + +#define RE_SYNTAX_POSIX_AWK \ + (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS) + +#define RE_SYNTAX_GREP \ + (RE_BK_PLUS_QM | RE_CHAR_CLASSES \ + | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \ + | RE_NEWLINE_ALT) + +#define RE_SYNTAX_EGREP \ + (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \ + | RE_NEWLINE_ALT | RE_NO_BK_PARENS \ + | RE_NO_BK_VBAR) + +#define RE_SYNTAX_POSIX_EGREP \ + (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES) + +/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */ +#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC + +#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC + +/* Syntax bits common to both basic and extended POSIX regex syntax. */ +#define _RE_SYNTAX_POSIX_COMMON \ + (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \ + | RE_INTERVALS | RE_NO_EMPTY_RANGES) + +#define RE_SYNTAX_POSIX_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM) + +/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes + RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this + isn't minimal, since other operators, such as \`, aren't disabled. */ +#define RE_SYNTAX_POSIX_MINIMAL_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) + +#define RE_SYNTAX_POSIX_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_UNMATCHED_RIGHT_PAREN_ORD) + +/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS + replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added. */ +#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) +/* [[[end syntaxes]]] */ + +/* Maximum number of duplicates an interval can allow. Some systems + (erroneously) define this in other header files, but we want our + value, so remove any previous define. */ +#ifdef RE_DUP_MAX +#undef RE_DUP_MAX +#endif +#define RE_DUP_MAX ((1 << 15) - 1) + + +/* POSIX `cflags' bits (i.e., information for `regcomp'). */ + +/* If this bit is set, then use extended regular expression syntax. + If not set, then use basic regular expression syntax. */ +#define REG_EXTENDED 1 + +/* If this bit is set, then ignore case when matching. + If not set, then case is significant. */ +#define REG_ICASE (REG_EXTENDED << 1) + +/* If this bit is set, then anchors do not match at newline + characters in the string. + If not set, then anchors do match at newlines. */ +#define REG_NEWLINE (REG_ICASE << 1) + +/* If this bit is set, then report only success or fail in regexec. + If not set, then returns differ between not matching and errors. */ +#define REG_NOSUB (REG_NEWLINE << 1) + + +/* POSIX `eflags' bits (i.e., information for regexec). */ + +/* If this bit is set, then the beginning-of-line operator doesn't match + the beginning of the string (presumably because it's not the + beginning of a line). + If not set, then the beginning-of-line operator does match the + beginning of the string. */ +#define REG_NOTBOL 1 + +/* Like REG_NOTBOL, except for the end-of-line. */ +#define REG_NOTEOL (1 << 1) + + +/* If any error codes are removed, changed, or added, update the + `re_error_msg' table in regex.c. */ +typedef enum +{ + REG_NOERROR = 0, /* Success. */ + REG_NOMATCH, /* Didn't find a match (for regexec). */ + + /* POSIX regcomp return error codes. (In the order listed in the + standard.) */ + REG_BADPAT, /* Invalid pattern. */ + REG_ECOLLATE, /* Not implemented. */ + REG_ECTYPE, /* Invalid character class name. */ + REG_EESCAPE, /* Trailing backslash. */ + REG_ESUBREG, /* Invalid back reference. */ + REG_EBRACK, /* Unmatched left bracket. */ + REG_EPAREN, /* Parenthesis imbalance. */ + REG_EBRACE, /* Unmatched \{. */ + REG_BADBR, /* Invalid contents of \{\}. */ + REG_ERANGE, /* Invalid range end. */ + REG_ESPACE, /* Ran out of memory. */ + REG_BADRPT, /* No preceding re for repetition op. */ + + /* Error codes we've added. */ + REG_EEND, /* Premature end. */ + REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */ + REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ +} reg_errcode_t; + +/* This data structure represents a compiled pattern. Before calling + the pattern compiler, the fields `buffer', `allocated', `fastmap', + `translate', and `no_sub' can be set. After the pattern has been + compiled, the `re_nsub' field is available. All other fields are + private to the regex routines. */ struct re_pattern_buffer - { - char *buffer; /* Space holding the compiled pattern commands. */ - int allocated; /* Size of space that buffer points to */ - int used; /* Length of portion of buffer actually occupied */ - char *fastmap; /* Pointer to fastmap, if any, or zero if none. */ - /* re_search uses the fastmap, if there is one, - to skip quickly over totally implausible characters */ - char *translate; /* Translate table to apply to all characters before comparing. - Or zero for no translation. - The translation is applied to a pattern when it is compiled - and to data when it is matched. */ - char fastmap_accurate; - /* Set to zero when a new pattern is stored, - set to one when the fastmap is updated from it. */ - char can_be_null; /* Set to one by compiling fastmap - if this pattern might match the null string. - It does not necessarily match the null string - in that case, but if this is zero, it cannot. - 2 as value means can match null string - but at end of range or before a character - listed in the fastmap. */ - }; - -/* Structure to store "register" contents data in. - - Pass the address of such a structure as an argument to re_match, etc., - if you want this information back. - - start[i] and end[i] record the string matched by \( ... \) grouping i, - for i from 1 to RE_NREGS - 1. - start[0] and end[0] record the entire string matched. */ +{ +/* [[[begin pattern_buffer]]] */ + /* Space that holds the compiled pattern. It is declared as + `unsigned char *' because its elements are + sometimes used as array indexes. */ + unsigned char *buffer; -struct re_registers - { - int start[RE_NREGS]; - int end[RE_NREGS]; - }; - -/* These are the command codes that appear in compiled regular expressions, one per byte. - Some command codes are followed by argument bytes. - A command code can specify any interpretation whatever for its arguments. - Zero-bytes may appear in the compiled regular expression. */ - -enum regexpcode - { - unused, - exactn, /* followed by one byte giving n, and then by n literal bytes */ - begline, /* fails unless at beginning of line */ - endline, /* fails unless at end of line */ - jump, /* followed by two bytes giving relative address to jump to */ - on_failure_jump, /* followed by two bytes giving relative address of place - to resume at in case of failure. */ - finalize_jump, /* Throw away latest failure point and then jump to address. */ - maybe_finalize_jump, /* Like jump but finalize if safe to do so. - This is used to jump back to the beginning - of a repeat. If the command that follows - this jump is clearly incompatible with the - one at the beginning of the repeat, such that - we can be sure that there is no use backtracking - out of repetitions already completed, - then we finalize. */ - dummy_failure_jump, /* jump, and push a dummy failure point. - This failure point will be thrown away - if an attempt is made to use it for a failure. - A + construct makes this before the first repeat. */ - anychar, /* matches any one character */ - charset, /* matches any one char belonging to specified set. - First following byte is # bitmap bytes. - Then come bytes for a bit-map saying which chars are in. - Bits in each byte are ordered low-bit-first. - A character is in the set if its bit is 1. - A character too large to have a bit in the map - is automatically not in the set */ - charset_not, /* similar but match any character that is NOT one of those specified */ - start_memory, /* starts remembering the text that is matched - and stores it in a memory register. - followed by one byte containing the register number. - Register numbers must be in the range 0 through NREGS. */ - stop_memory, /* stops remembering the text that is matched - and stores it in a memory register. - followed by one byte containing the register number. - Register numbers must be in the range 0 through NREGS. */ - duplicate, /* match a duplicate of something remembered. - Followed by one byte containing the index of the memory register. */ - before_dot, /* Succeeds if before dot */ - at_dot, /* Succeeds if at dot */ - after_dot, /* Succeeds if after dot */ - begbuf, /* Succeeds if at beginning of buffer */ - endbuf, /* Succeeds if at end of buffer */ - wordchar, /* Matches any word-constituent character */ - notwordchar, /* Matches any char that is not a word-constituent */ - wordbeg, /* Succeeds if at word beginning */ - wordend, /* Succeeds if at word end */ - wordbound, /* Succeeds if at a word boundary */ - notwordbound, /* Succeeds if not at a word boundary */ - syntaxspec, /* Matches any character whose syntax is specified. - followed by a byte which contains a syntax code, Sword or such like */ - notsyntaxspec /* Matches any character whose syntax differs from the specified. */ - }; + /* Number of bytes to which `buffer' points. */ + unsigned long allocated; + + /* Number of bytes actually used in `buffer'. */ + unsigned long used; + + /* Syntax setting with which the pattern was compiled. */ + reg_syntax_t syntax; + + /* Pointer to a fastmap, if any, otherwise zero. re_search uses + the fastmap, if there is one, to skip over impossible + starting points for matches. */ + char *fastmap; + + /* Either a translate table to apply to all characters before + comparing them, or zero for no translation. The translation + is applied to a pattern when it is compiled and to a string + when it is matched. */ + char *translate; + + /* Number of subexpressions found by the compiler. */ + size_t re_nsub; + + /* Zero if this pattern cannot match the empty string, one else. + Well, in truth it's used only in `re_search_2', to see + whether or not we should use the fastmap, so we don't set + this absolutely perfectly; see `re_compile_fastmap' (the + `duplicate' case). */ + unsigned can_be_null : 1; + + /* If REGS_UNALLOCATED, allocate space in the `regs' structure + for `max (RE_NREGS, re_nsub + 1)' groups. + If REGS_REALLOCATE, reallocate space if necessary. + If REGS_FIXED, use what's there. */ +#define REGS_UNALLOCATED 0 +#define REGS_REALLOCATE 1 +#define REGS_FIXED 2 + unsigned regs_allocated : 2; + + /* Set to zero when `regex_compile' compiles a pattern; set to one + by `re_compile_fastmap' if it updates the fastmap. */ + unsigned fastmap_accurate : 1; + + /* If set, `re_match_2' does not return information about + subexpressions. */ + unsigned no_sub : 1; + + /* If set, a beginning-of-line anchor doesn't match at the + beginning of the string. */ + unsigned not_bol : 1; + + /* Similarly for an end-of-line anchor. */ + unsigned not_eol : 1; + + /* If true, an anchor at a newline matches. */ + unsigned newline_anchor : 1; + +/* [[[end pattern_buffer]]] */ +}; + +typedef struct re_pattern_buffer regex_t; + + +/* search.c (search_buffer) in Emacs needs this one opcode value. It is + defined both in `regex.c' and here. */ +#define RE_EXACTN_VALUE 1 -extern char *re_compile_pattern (); -/* Is this really advertised? */ -extern void re_compile_fastmap (); -extern int re_search (), re_search_2 (); -extern int re_match (), re_match_2 (); - -/* 4.2 bsd compatibility (yuck) */ -extern char *re_comp (); -extern int re_exec (); - -#ifdef SYNTAX_TABLE -extern char *re_syntax_table; +/* Type for byte offsets within the string. POSIX mandates this. */ +typedef int regoff_t; + + +/* This is the structure we store register match data in. See + regex.texinfo for a full description of what registers match. */ +struct re_registers +{ + unsigned num_regs; + regoff_t *start; + regoff_t *end; +}; + + +/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer, + `re_match_2' returns information about at least this many registers + the first time a `regs' structure is passed. */ +#ifndef RE_NREGS +#define RE_NREGS 30 #endif + + +/* POSIX specification for registers. Aside from the different names than + `re_registers', POSIX uses an array of structures, instead of a + structure of arrays. */ +typedef struct +{ + regoff_t rm_so; /* Byte offset from string's start to substring's start. */ + regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ +} regmatch_t; + +/* Declarations for routines. */ + +/* To avoid duplicating every routine declaration -- once with a + prototype (if we are ANSI), and once without (if we aren't) -- we + use the following macro to declare argument types. This + unfortunately clutters up the declarations a bit, but I think it's + worth it. */ + +#if __STDC__ + +#define _RE_ARGS(args) args + +#else /* not __STDC__ */ + +#define _RE_ARGS(args) () + +#endif /* not __STDC__ */ + +/* Sets the current default syntax to SYNTAX, and return the old syntax. + You can also simply assign to the `re_syntax_options' variable. */ +extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax)); + +/* Compile the regular expression PATTERN, with length LENGTH + and syntax given by the global `re_syntax_options', into the buffer + BUFFER. Return NULL if successful, and an error string if not. */ +extern const char *re_compile_pattern + _RE_ARGS ((const char *pattern, int length, + struct re_pattern_buffer *buffer)); + + +/* Compile a fastmap for the compiled pattern in BUFFER; used to + accelerate searches. Return 0 if successful and -2 if was an + internal error. */ +extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer)); + + +/* Search in the string STRING (with length LENGTH) for the pattern + compiled into BUFFER. Start searching at position START, for RANGE + characters. Return the starting position of the match, -1 for no + match, or -2 for an internal error. Also return register + information in REGS (if REGS and BUFFER->no_sub are nonzero). */ +extern int re_search + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, + int length, int start, int range, struct re_registers *regs)); + + +/* Like `re_search', but search in the concatenation of STRING1 and + STRING2. Also, stop searching at index START + STOP. */ +extern int re_search_2 + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, int range, struct re_registers *regs, int stop)); + + +/* Like `re_search', but return how many characters in STRING the regexp + in BUFFER matched, starting at position START. */ +extern int re_match + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, + int length, int start, struct re_registers *regs)); + + +/* Relates to `re_match' as `re_search_2' relates to `re_search'. */ +extern int re_match_2 + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, struct re_registers *regs, int stop)); + + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using BUFFER and REGS will use this memory + for recording register information. STARTS and ENDS must be + allocated with malloc, and must each be at least `NUM_REGS * sizeof + (regoff_t)' bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ +extern void re_set_registers + _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs, + unsigned num_regs, regoff_t *starts, regoff_t *ends)); + +/* 4.2 bsd compatibility. */ +extern char *re_comp _RE_ARGS ((const char *)); +extern int re_exec _RE_ARGS ((const char *)); + +/* POSIX compatibility. */ +extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags)); +extern int regexec + _RE_ARGS ((const regex_t *preg, const char *string, size_t nmatch, + regmatch_t pmatch[], int eflags)); +extern size_t regerror + _RE_ARGS ((int errcode, const regex_t *preg, char *errbuf, + size_t errbuf_size)); +extern void regfree _RE_ARGS ((regex_t *preg)); + +#endif /* not __REGEXP_LIBRARY_H__ */ + +/* +Local variables: +make-backup-files: t +version-control: t +trim-versions-without-asking: nil +End: +*/ diff --git a/gnu/usr.bin/grep/search.c b/gnu/usr.bin/grep/search.c new file mode 100644 index 0000000000..d2be489f57 --- /dev/null +++ b/gnu/usr.bin/grep/search.c @@ -0,0 +1,481 @@ +/* search.c - searching subroutines using dfa, kwset and regex for grep. + Copyright (C) 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Written August 1992 by Mike Haertel. */ + +#include + +#ifdef STDC_HEADERS +#include +#include +#else +#define UCHAR_MAX 255 +#include +extern char *malloc(); +#endif + +#ifdef HAVE_MEMCHR +#include +#ifdef NEED_MEMORY_H +#include +#endif +#else +#ifdef __STDC__ +extern void *memchr(); +#else +extern char *memchr(); +#endif +#endif + +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) +#undef bcopy +#define bcopy(s, d, n) memcpy((d), (s), (n)) +#endif + +#ifdef isascii +#define ISALNUM(C) (isascii(C) && isalnum(C)) +#define ISUPPER(C) (isascii(C) && isupper(C)) +#else +#define ISALNUM(C) isalnum(C) +#define ISUPPER(C) isupper(C) +#endif + +#define TOLOWER(C) (ISUPPER(C) ? tolower(C) : (C)) + +#include "grep.h" +#include "dfa.h" +#include "kwset.h" +#include "regex.h" + +#define NCHAR (UCHAR_MAX + 1) + +#if __STDC__ +static void Gcompile(char *, size_t); +static void Ecompile(char *, size_t); +static char *EGexecute(char *, size_t, char **); +static void Fcompile(char *, size_t); +static char *Fexecute(char *, size_t, char **); +#else +static void Gcompile(); +static void Ecompile(); +static char *EGexecute(); +static void Fcompile(); +static char *Fexecute(); +#endif + +/* Here is the matchers vector for the main program. */ +struct matcher matchers[] = { + { "default", Gcompile, EGexecute }, + { "grep", Gcompile, EGexecute }, + { "ggrep", Gcompile, EGexecute }, + { "egrep", Ecompile, EGexecute }, + { "posix-egrep", Ecompile, EGexecute }, + { "gegrep", Ecompile, EGexecute }, + { "fgrep", Fcompile, Fexecute }, + { "gfgrep", Fcompile, Fexecute }, + { 0, 0, 0 }, +}; + +/* For -w, we also consider _ to be word constituent. */ +#define WCHAR(C) (ISALNUM(C) || (C) == '_') + +/* DFA compiled regexp. */ +static struct dfa dfa; + +/* Regex compiled regexp. */ +static struct re_pattern_buffer regex; + +/* KWset compiled pattern. For Ecompile and Gcompile, we compile + a list of strings, at least one of which is known to occur in + any string matching the regexp. */ +static kwset_t kwset; + +/* Last compiled fixed string known to exactly match the regexp. + If kwsexec() returns < lastexact, then we don't need to + call the regexp matcher at all. */ +static int lastexact; + +void +dfaerror(mesg) + char *mesg; +{ + fatal(mesg, 0); +} + +static void +kwsinit() +{ + static char trans[NCHAR]; + int i; + + if (match_icase) + for (i = 0; i < NCHAR; ++i) + trans[i] = TOLOWER(i); + + if (!(kwset = kwsalloc(match_icase ? trans : (char *) 0))) + fatal("memory exhausted", 0); +} + +/* If the DFA turns out to have some set of fixed strings one of + which must occur in the match, then we build a kwset matcher + to find those strings, and thus quickly filter out impossible + matches. */ +static void +kwsmusts() +{ + struct dfamust *dm; + char *err; + + if (dfa.musts) + { + kwsinit(); + /* First, we compile in the substrings known to be exact + matches. The kwset matcher will return the index + of the matching string that it chooses. */ + for (dm = dfa.musts; dm; dm = dm->next) + { + if (!dm->exact) + continue; + ++lastexact; + if ((err = kwsincr(kwset, dm->must, strlen(dm->must))) != 0) + fatal(err, 0); + } + /* Now, we compile the substrings that will require + the use of the regexp matcher. */ + for (dm = dfa.musts; dm; dm = dm->next) + { + if (dm->exact) + continue; + if ((err = kwsincr(kwset, dm->must, strlen(dm->must))) != 0) + fatal(err, 0); + } + if ((err = kwsprep(kwset)) != 0) + fatal(err, 0); + } +} + +static void +Gcompile(pattern, size) + char *pattern; + size_t size; +{ +#ifdef __STDC__ + const +#endif + char *err; + + re_set_syntax(RE_SYNTAX_GREP | RE_HAT_LISTS_NOT_NEWLINE); + dfasyntax(RE_SYNTAX_GREP | RE_HAT_LISTS_NOT_NEWLINE, match_icase); + + if ((err = re_compile_pattern(pattern, size, ®ex)) != 0) + fatal(err, 0); + + dfainit(&dfa); + + /* In the match_words and match_lines cases, we use a different pattern + for the DFA matcher that will quickly throw out cases that won't work. + Then if DFA succeeds we do some hairy stuff using the regex matcher + to decide whether the match should really count. */ + if (match_words || match_lines) + { + /* In the whole-word case, we use the pattern: + (^|[^A-Za-z_])(userpattern)([^A-Za-z_]|$). + In the whole-line case, we use the pattern: + ^(userpattern)$. + BUG: Using [A-Za-z_] is locale-dependent! */ + + char *n = malloc(size + 50); + int i = 0; + + strcpy(n, ""); + + if (match_lines) + strcpy(n, "^\\("); + if (match_words) + strcpy(n, "\\(^\\|[^0-9A-Za-z_]\\)\\("); + + i = strlen(n); + bcopy(pattern, n + i, size); + i += size; + + if (match_words) + strcpy(n + i, "\\)\\([^0-9A-Za-z_]\\|$\\)"); + if (match_lines) + strcpy(n + i, "\\)$"); + + i += strlen(n + i); + dfacomp(n, i, &dfa, 1); + } + else + dfacomp(pattern, size, &dfa, 1); + + kwsmusts(); +} + +static void +Ecompile(pattern, size) + char *pattern; + size_t size; +{ +#ifdef __STDC__ + const +#endif + char *err; + + if (strcmp(matcher, "posix-egrep") == 0) + { + re_set_syntax(RE_SYNTAX_POSIX_EGREP); + dfasyntax(RE_SYNTAX_POSIX_EGREP, match_icase); + } + else + { + re_set_syntax(RE_SYNTAX_EGREP); + dfasyntax(RE_SYNTAX_EGREP, match_icase); + } + + if ((err = re_compile_pattern(pattern, size, ®ex)) != 0) + fatal(err, 0); + + dfainit(&dfa); + + /* In the match_words and match_lines cases, we use a different pattern + for the DFA matcher that will quickly throw out cases that won't work. + Then if DFA succeeds we do some hairy stuff using the regex matcher + to decide whether the match should really count. */ + if (match_words || match_lines) + { + /* In the whole-word case, we use the pattern: + (^|[^A-Za-z_])(userpattern)([^A-Za-z_]|$). + In the whole-line case, we use the pattern: + ^(userpattern)$. + BUG: Using [A-Za-z_] is locale-dependent! */ + + char *n = malloc(size + 50); + int i = 0; + + strcpy(n, ""); + + if (match_lines) + strcpy(n, "^("); + if (match_words) + strcpy(n, "(^|[^0-9A-Za-z_])("); + + i = strlen(n); + bcopy(pattern, n + i, size); + i += size; + + if (match_words) + strcpy(n + i, ")([^0-9A-Za-z_]|$)"); + if (match_lines) + strcpy(n + i, ")$"); + + i += strlen(n + i); + dfacomp(n, i, &dfa, 1); + } + else + dfacomp(pattern, size, &dfa, 1); + + kwsmusts(); +} + +static char * +EGexecute(buf, size, endp) + char *buf; + size_t size; + char **endp; +{ + register char *buflim, *beg, *end, save; + int backref, start, len; + struct kwsmatch kwsm; + static struct re_registers regs; /* This is static on account of a BRAIN-DEAD + Q@#%!# library interface in regex.c. */ + + buflim = buf + size; + + for (beg = end = buf; end < buflim; beg = end + 1) + { + if (kwset) + { + /* Find a possible match using the KWset matcher. */ + beg = kwsexec(kwset, beg, buflim - beg, &kwsm); + if (!beg) + goto failure; + /* Narrow down to the line containing the candidate, and + run it through DFA. */ + end = memchr(beg, '\n', buflim - beg); + if (!end) + end = buflim; + while (beg > buf && beg[-1] != '\n') + --beg; + save = *end; + if (kwsm.index < lastexact) + goto success; + if (!dfaexec(&dfa, beg, end, 0, (int *) 0, &backref)) + { + *end = save; + continue; + } + *end = save; + /* Successful, no backreferences encountered. */ + if (!backref) + goto success; + } + else + { + /* No good fixed strings; start with DFA. */ + save = *buflim; + beg = dfaexec(&dfa, beg, buflim, 0, (int *) 0, &backref); + *buflim = save; + if (!beg) + goto failure; + /* Narrow down to the line we've found. */ + end = memchr(beg, '\n', buflim - beg); + if (!end) + end = buflim; + while (beg > buf && beg[-1] != '\n') + --beg; + /* Successful, no backreferences encountered! */ + if (!backref) + goto success; + } + /* If we've made it to this point, this means DFA has seen + a probable match, and we need to run it through Regex. */ + regex.not_eol = 0; + if ((start = re_search(®ex, beg, end - beg, 0, end - beg, ®s)) >= 0) + { + len = regs.end[0] - start; + if (!match_lines && !match_words || match_lines && len == end - beg) + goto success; + /* If -w, check if the match aligns with word boundaries. + We do this iteratively because: + (a) the line may contain more than one occurence of the pattern, and + (b) Several alternatives in the pattern might be valid at a given + point, and we may need to consider a shorter one to find a word + boundary. */ + if (match_words) + while (start >= 0) + { + if ((start == 0 || !WCHAR(beg[start - 1])) + && (len == end - beg || !WCHAR(beg[start + len]))) + goto success; + if (len > 0) + { + /* Try a shorter length anchored at the same place. */ + --len; + regex.not_eol = 1; + len = re_match(®ex, beg, start + len, start, ®s); + } + if (len <= 0) + { + /* Try looking further on. */ + if (start == end - beg) + break; + ++start; + regex.not_eol = 0; + start = re_search(®ex, beg, end - beg, + start, end - beg - start, ®s); + len = regs.end[0] - start; + } + } + } + } + + failure: + return 0; + + success: + *endp = end < buflim ? end + 1 : end; + return beg; +} + +static void +Fcompile(pattern, size) + char *pattern; + size_t size; +{ + char *beg, *lim, *err; + + kwsinit(); + beg = pattern; + do + { + for (lim = beg; lim < pattern + size && *lim != '\n'; ++lim) + ; + if ((err = kwsincr(kwset, beg, lim - beg)) != 0) + fatal(err, 0); + if (lim < pattern + size) + ++lim; + beg = lim; + } + while (beg < pattern + size); + + if ((err = kwsprep(kwset)) != 0) + fatal(err, 0); +} + +static char * +Fexecute(buf, size, endp) + char *buf; + size_t size; + char **endp; +{ + register char *beg, *try, *end; + register size_t len; + struct kwsmatch kwsmatch; + + for (beg = buf; beg <= buf + size; ++beg) + { + if (!(beg = kwsexec(kwset, beg, buf + size - beg, &kwsmatch))) + return 0; + len = kwsmatch.size[0]; + if (match_lines) + { + if (beg > buf && beg[-1] != '\n') + continue; + if (beg + len < buf + size && beg[len] != '\n') + continue; + goto success; + } + else if (match_words) + for (try = beg; len && try;) + { + if (try > buf && WCHAR((unsigned char) try[-1])) + break; + if (try + len < buf + size && WCHAR((unsigned char) try[len])) + { + try = kwsexec(kwset, beg, --len, &kwsmatch); + len = kwsmatch.size[0]; + } + else + goto success; + } + else + goto success; + } + + return 0; + + success: + if ((end = memchr(beg + len, '\n', (buf + size) - (beg + len))) != 0) + ++end; + else + end = buf + size; + *endp = end; + while (beg > buf && beg[-1] != '\n') + --beg; + return beg; +} diff --git a/gnu/usr.bin/grep/tests/check.sh b/gnu/usr.bin/grep/tests/check.sh new file mode 100644 index 0000000000..d2c8fdbc64 --- /dev/null +++ b/gnu/usr.bin/grep/tests/check.sh @@ -0,0 +1,24 @@ +#! /bin/sh +# Regression test for GNU grep. +# Usage: regress.sh [testdir] + +testdir=${1-tests} + +failures=0 + +# The Khadafy test is brought to you by Scott Anderson . . . +./grep -E -f $testdir/khadafy.regexp $testdir/khadafy.lines > khadafy.out +if cmp $testdir/khadafy.lines khadafy.out +then + : +else + echo Khadafy test failed -- output left on khadafy.out + failures=1 +fi + +# . . . and the following by Henry Spencer. + +${AWK-awk} -F: -f $testdir/scriptgen.awk $testdir/spencer.tests > tmp.script + +sh tmp.script && exit $failures +exit 1 diff --git a/gnu/usr.bin/grep/tests/regress.sh b/gnu/usr.bin/grep/tests/regress.sh deleted file mode 100644 index b947036467..0000000000 --- a/gnu/usr.bin/grep/tests/regress.sh +++ /dev/null @@ -1,30 +0,0 @@ -#! /bin/sh -# Regression test for GNU e?grep. -# Usage: regress.sh [dir-containing-egrep] - -builddir=${1-..} - -failures=0 - -# The Khadafy test is brought to you by Scott Anderson . . . -$builddir/egrep -f khadafy.regexp khadafy.lines > khadafy.out -if cmp khadafy.lines khadafy.out -then - rm khadafy.out -else - echo Khadafy test failed -- output left on khadafy.out - failures=1 -fi - -# . . . and the following by Henry Spencer. - -awk -F: -f scriptgen.awk spencer.tests > tmp.script - -if sh tmp.script $builddir -then - rm tmp.script - exit $failures -else - rm tmp.script - exit 1 -fi diff --git a/gnu/usr.bin/grep/tests/scriptgen.awk b/gnu/usr.bin/grep/tests/scriptgen.awk index cdadc917ae..44ef4df16e 100644 --- a/gnu/usr.bin/grep/tests/scriptgen.awk +++ b/gnu/usr.bin/grep/tests/scriptgen.awk @@ -1,6 +1,6 @@ BEGIN { print "failures=0"; } -!/^#/ && NF == 3 { - print "echo '" $3 "' | $1/egrep -e '" $2 "' > /dev/null 2>&1"; +$0 !~ /^#/ && NF == 3 { + print "echo '" $3 "' | ./grep -E -e '" $2 "' > /dev/null 2>&1"; print "if [ $? != " $1 " ]" print "then" printf "\techo Spencer test \\#%d failed\n", ++n diff --git a/gnu/usr.bin/grep/tests/spencer.tests b/gnu/usr.bin/grep/tests/spencer.tests index c7eee9bdde..913f1980c8 100644 --- a/gnu/usr.bin/grep/tests/spencer.tests +++ b/gnu/usr.bin/grep/tests/spencer.tests @@ -33,7 +33,7 @@ 0:a[b-d]e:ace 0:a[b-d]:aac 0:a[-b]:a- -2:a[b-]:a- +0:a[b-]:a- 1:a[b-a]:- 2:a[]b:- 2:a[:- diff --git a/gnu/usr.bin/groff/Makefile b/gnu/usr.bin/groff/Makefile index b6d02e1db0..0e300d08ec 100644 --- a/gnu/usr.bin/groff/Makefile +++ b/gnu/usr.bin/groff/Makefile @@ -4,8 +4,7 @@ SUBDIR= libgroff libdriver libbib\ groff troff nroff tbl pic eqn grops grotty grodvi\ refer lookbib indxbib lkbib \ tfmtodit addftinfo pfbtops psbb \ - devices tmac mm man \ - xditview + devices tmac mm man xditview # BSD already provides soelim MISC= soelim diff --git a/gnu/usr.bin/groff/Makefile.cfg b/gnu/usr.bin/groff/Makefile.cfg index 3df6c85e74..e47a622d96 100644 --- a/gnu/usr.bin/groff/Makefile.cfg +++ b/gnu/usr.bin/groff/Makefile.cfg @@ -1,12 +1,14 @@ # Libraries -.if defined(NOOBJ) -LIBGROFF= $(.CURDIR)/../libgroff/libgroff.a -LIBDRIVER= $(.CURDIR)/../libdriver/libdriver.a -LIBBIB= $(.CURDIR)/../libbib/libbib.a -.else + +# Bad assumption, if one exists they all exist +.if exists(${.CURDIR}/../libgroff/obj) LIBGROFF= $(.CURDIR)/../libgroff/obj/libgroff.a LIBDRIVER= $(.CURDIR)/../libdriver/obj/libdriver.a LIBBIB= $(.CURDIR)/../libbib/obj/libbib.a +.else +LIBGROFF= $(.CURDIR)/../libgroff/libgroff.a +LIBDRIVER= $(.CURDIR)/../libdriver/libdriver.a +LIBBIB= $(.CURDIR)/../libbib/libbib.a .endif CC= gcc diff --git a/gnu/usr.bin/groff/afmtodit/Makefile b/gnu/usr.bin/groff/afmtodit/Makefile index 199d16a385..be4e1a2894 100644 --- a/gnu/usr.bin/groff/afmtodit/Makefile +++ b/gnu/usr.bin/groff/afmtodit/Makefile @@ -1,4 +1,4 @@ -MAN1= afmtodit.0 +MAN1= afmtodit.1 afterinstall: install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ diff --git a/gnu/usr.bin/groff/devices/Makefile.dev b/gnu/usr.bin/groff/devices/Makefile.dev index 1792ffcb6b..3d1b375521 100644 --- a/gnu/usr.bin/groff/devices/Makefile.dev +++ b/gnu/usr.bin/groff/devices/Makefile.dev @@ -5,8 +5,8 @@ .include "../../Makefile.cfg" -DESTDIR?= /usr/share/groff_font -DEVICEDIR?= $(fontdir)/dev$(DEVICE) +FONTDIR?= /usr/share/groff_font +DEVICEDIR?= $(FONTDIR)/dev$(DEVICE) FONTOWN?= bin FONTGRP?= bin FONTMODE?= 444 @@ -42,6 +42,11 @@ cleandir: cd ${.CURDIR}; rm -rf obj; .endif +.if !target(maninstall) +maninstall: + @echo -n +.endif + .if !target(install) install: -if test ! -d $(DESTDIR)$(DEVICEDIR); then \ diff --git a/gnu/usr.bin/groff/grog/Makefile b/gnu/usr.bin/groff/grog/Makefile index 4c134f2254..4e112b1c92 100644 --- a/gnu/usr.bin/groff/grog/Makefile +++ b/gnu/usr.bin/groff/grog/Makefile @@ -1,4 +1,4 @@ -MAN1= grog.0 +MAN1= grog.1 afterinstall: install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ diff --git a/gnu/usr.bin/groff/indxbib/Makefile b/gnu/usr.bin/groff/indxbib/Makefile index ccf17ea698..ce36aaa7af 100644 --- a/gnu/usr.bin/groff/indxbib/Makefile +++ b/gnu/usr.bin/groff/indxbib/Makefile @@ -12,7 +12,7 @@ DPADD+= $(LIBBIB) $(LIBGROFF) $(LIBMATH) # afterinstall: install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/eign \ - /usr/share/${common_words_file} + $(DESTDIR)/usr/share/dict .include .include "../../../usr.bin/Makefile.inc" diff --git a/gnu/usr.bin/groff/libdriver/Makefile b/gnu/usr.bin/groff/libdriver/Makefile index 926e0679f0..151c8aa731 100644 --- a/gnu/usr.bin/groff/libdriver/Makefile +++ b/gnu/usr.bin/groff/libdriver/Makefile @@ -9,6 +9,9 @@ NOPROFILE= noprofile install: +depend: + @echo "BROKEN MAKEDEPEND!" + .include .include "../Makefile.cfg" .include "Makefile.dep" diff --git a/gnu/usr.bin/groff/man/Makefile b/gnu/usr.bin/groff/man/Makefile index ef61aca3da..dd132a5447 100644 --- a/gnu/usr.bin/groff/man/Makefile +++ b/gnu/usr.bin/groff/man/Makefile @@ -1,7 +1,7 @@ # Makefile for manpages -MAN5= groff_font.0 groff_out.0 -MAN7= groff_char.0 +MAN5= groff_font.5 groff_out.5 +MAN7= groff_char.7 .include .include "../Makefile.cfg" diff --git a/gnu/usr.bin/groff/mm/Makefile b/gnu/usr.bin/groff/mm/Makefile index 09d614cf24..821acb0671 100644 --- a/gnu/usr.bin/groff/mm/Makefile +++ b/gnu/usr.bin/groff/mm/Makefile @@ -6,14 +6,14 @@ TMACDIR?= /usr/share/tmac FILES= 0.MT 5.MT 4.MT ms.cov se_ms.cov LOCALE= locale se_locale -MAN7= groff_mm.0 groff_mmse.0 +MAN7= groff_mm.7 groff_mmse.7 MLINKS= groff_mm.7 mm.7 groff_mmse.7 mmse.7 afterinstall: install -c -o $(TMACOWN) -g $(TMACGRP) -m $(TMACMODE) \ - $(.CURDIR)/tmac.m $(DESTDIR)$(TMACDIR)/tmac.$(tmac_m) + $(.CURDIR)/tmac.m $(DESTDIR)$(TMACDIR)/tmac.m install -c -o $(TMACOWN) -g $(TMACGRP) -m $(TMACMODE) \ - $(.CURDIR)/tmac.m $(DESTDIR)$(TMACDIR)/tmac.$(tmac_m)se + $(.CURDIR)/tmac.m $(DESTDIR)$(TMACDIR)/tmac.mse test -d $(DESTDIR)$(TMACDIR)/mm || mkdir $(DESTDIR)$(TMACDIR)/mm chown $(TMACOWN).$(TMACGRP) $(DESTDIR)$(TMACDIR)/mm diff --git a/gnu/usr.bin/groff/nroff/Makefile b/gnu/usr.bin/groff/nroff/Makefile index 71f62f4057..644d3e8896 100644 --- a/gnu/usr.bin/groff/nroff/Makefile +++ b/gnu/usr.bin/groff/nroff/Makefile @@ -1,4 +1,4 @@ -MAN1= nroff.0 +MAN1= nroff.1 afterinstall: install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ diff --git a/gnu/usr.bin/groff/tmac/Makefile b/gnu/usr.bin/groff/tmac/Makefile index eac75f94ea..145b9453c8 100644 --- a/gnu/usr.bin/groff/tmac/Makefile +++ b/gnu/usr.bin/groff/tmac/Makefile @@ -5,7 +5,7 @@ TMACGRP?= bin TMACMODE?= 444 TMACDIR?= /usr/share/tmac -MAN7= groff_ms.0 +MAN7= groff_ms.7 MLINKS= groff_ms.7 ms.7 FILES= tmac.pic tmac.ps tmac.psnew tmac.psold tmac.pspic tmac.psatk\ @@ -18,7 +18,7 @@ afterinstall: ${.CURDIR}/$$f ${DESTDIR}${TMACDIR}; \ done install -c -o ${TMACOWN} -g ${TMACGRP} -m ${TMACMODE} \ - ${.CURDIR}/tmac.s ${DESTDIR}${TMACDIR}/tmac.${tmac_s} + ${.CURDIR}/tmac.s ${DESTDIR}${TMACDIR}/tmac.s install -c -o ${TMACOWN} -g ${TMACGRP} -m ${TMACMODE} \ ${.CURDIR}/tmac.an ${DESTDIR}${TMACDIR}/tmac.an.old diff --git a/gnu/usr.bin/groff/troff/Makefile b/gnu/usr.bin/groff/troff/Makefile index 0da8255823..5bc36c9ca6 100644 --- a/gnu/usr.bin/groff/troff/Makefile +++ b/gnu/usr.bin/groff/troff/Makefile @@ -9,6 +9,7 @@ DPADD+= $(LIBGROFF) $(LIBMATH) tmacdir?= /usr/share/tmac CLEANFILES+= majorminor.cc + majorminor.cc: $(.CURDIR)/../VERSION @echo Making $@ @-rm -f $@ diff --git a/gnu/usr.bin/groff/xditview/Makefile b/gnu/usr.bin/groff/xditview/Makefile index 1e2a9a6e30..fa0590d0e5 100644 --- a/gnu/usr.bin/groff/xditview/Makefile +++ b/gnu/usr.bin/groff/xditview/Makefile @@ -1,7 +1,7 @@ -.if exists(/usr/X386) +.if exists(${DESTDIR}/usr/X386) BINDIR= /usr/X386/bin -MANDIR= /usr/X386/man/cat +MANDIR= /usr/X386/man/man PROG= gxditview CFLAGS+= -I/usr/X386/include -DFONTPATH=\"/usr/share/groff_font\" @@ -11,7 +11,7 @@ LDADD+= -L/usr/X386/lib -lXaw -lXmu -lXt -lXext -lX11 -lm afterinstall: install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/GXditview.ad \ - $(DESTDIR)/usr/X386/lib/X11/app-defaults + $(DESTDIR)/usr/X386/lib/X11/app-defaults/GXditview .endif .include diff --git a/gnu/usr.bin/gzip/ChangeLog b/gnu/usr.bin/gzip/ChangeLog index 5238071112..7dd0f86516 100644 --- a/gnu/usr.bin/gzip/ChangeLog +++ b/gnu/usr.bin/gzip/ChangeLog @@ -1,3 +1,140 @@ +Wed Aug 18 09:34:23 1993 Jean-loup Gailly (jloup@chorus.fr) + + * version 1.2.4 + By default, do not restore file name and timestamp from those saved + inside the .gz file (behave as 'compress'). Added the --name option + to force name and timestamp restoration. + Accept - as synonym for stdin. + Use manlinks=so or ln to support either hard links or .so in man pages + Accept foo.gz~ in zdiff. + Added support for Windows NT + Handle ENAMETOOLONG for strict Posix systems + Use --recursive instead of --recurse to comply with Webster and + the GNU stdandard. + Allow installation of shell scripts with a g prefix: make G=g install + Install by default zcat as gzcat if gzcat already exists in path. + Let zmore behave as more when invoked without parameters (give help) + Let gzip --list reject files not in gzip format even with --force. + Don't complain about non gzip files for options -rt or -rl. + Added advice in INSTALL for several systems. + Added makefile entries for NeXTstep 3.1 (if configure fails) + Avoid problem with memcpy on Pyramid (gave crc error on some files) + Support the -r option when compiled with Borland C++ on msdos. + Force lower case file names only for FAT file systems (not HPFS) + Rewrite one expression in inflate.c to avoid cc bug on Solaris x86. + In the msdos makefiles, get match.asm from the msdos subdirectory. + Catch SIGTERM and SIGHUP only if they are not ignored. + getopt.c: on Amiga, "#if !defined(const)" does not compile. + Use register parameters on Amiga. + Do not force names to lower case on Amiga. + Fix support of Atari TOS (Makefile.st and tailor.h) + In unlzw.c, do not suggest using zcat if zcat already used. + In INSTALL, suggest using bsdinst for HPUX. + Document Turbo C++ 1.0 bug in INSTALL. + Improved the documentation relative to the --no-name option. + Avoid signed/unsigned warnings in several files. + Added pointer to jka-compr19.el in README. + Added pointer to OS/2 executables in README. + Added --block-compress in tar -z example (gzip.1 and gzip.texi). + Don't keep rcsid in executable (avoid compilation warnings). + Check also the correctness of the first byte of an .Z file. + Return non zero status for an invalid option. + Remove "NEWFILES" from os2/gzip.def for Borland C++ on OS/2. + Remove "time stamp restored" message (just obey the -N request). + +Thu Jun 24 10:27:57 1993 Jean-loup Gailly (jloup@chorus.fr) + + * version 1.2.3 + Don't display the output name when decompressing except with --verbose. + Remove usage of alloca in getopt.c and all makefiles. + Use ASCPP instead of CPP to avoid breaking AC_HEADER_CHECK on RiscOS. + Added the zfile shell script in subdirectory sample. + Moved the list of compiler bugs from README to INSTALL. + Added vms/Readme.vms. + Fix DIST_BUFSIZE check in unlzh.c for 16 bit machines. + Fix REGSIGTYP macro in configure.in. + Use 'define' instead of == in vms/gzip.hlp. + Avoid warnings in unlzh.c + Allow separate installation of binaries and man pages. + Simplified handling of file names with spaces in zgrep and znew. + Fix dependencies and remove rule for trees.c in amiga/Makefile.sasc + Add missing quote in gzexe. + +Thu Jun 17 13:47:05 1993 Jean-loup Gailly (jloup@chorus.fr) + + * version 1.2.2 + Fix a compilation error in gzip.c on Sun with cc (worked with gcc). + +Wed Jun 16 11:20:27 1993 Jean-loup Gailly (jloup@chorus.fr) + + * version 1.2.1 + Let zmore act as more if the data is not gzipped. + By default, display output name only when name was actually truncated. + Use absolute path names in gzexe'd programs for better security. + In gzexe, use chmod 700 instead of 755 and don't gzexe tail,rm,etc... + Update vms/gzip.hlp. + Added a note about the fast options (-1 to -3) in algorithm.doc. + Improved man page for zgrep. + Minor fixes to gzip.texi. + Always set LC_ALL and LANG in configure (for tr on HPUX) + +Mon Jun 14 10:03:24 1993 Jean-loup Gailly (jloup@chorus.fr) + + * version 1.2 + Added the --list option to display the file characteristics. + Added the --no-name option: do not save or restore original filename + Save the original name by default. + Allow gunzip --suffix "" to attempt decompression on any file + regardless of its extension if an original name is present. + Add support for the SCO compress -H format. + gzip --fast now compresses faster (speed close to that of compress) + with degraded compression ratio (but still better than compress). + Default level changed to -6 (acts exactly as previous level -5) to + be a better indication of its placement in the speed/ratio range. + Use smart name truncation: 123456789012.c -> 123456789.c.gz + instead of 12345678901.gz + With --force, let zcat pass non gzip'ed data unchanged (zcat == cat) + Added the zgrep shell script. + Made sub.c useful for 16 bit sound, 24 bit images, etc.. + Supress warnings about suffix for gunzip -r, except with --verbose. + Moved the sample programs to a subdirectory sample. + On MSDOS, use .gz extension when possible (files without extension) + Added a "Special targets" section in INSTALL. + Use stty -g correctly in zmore.in. + Use cheaper test for gzipness in zforce.in. + Remove space before $ in match.S (no longer accepted by gas 2.x) + For the shell scripts, do not assume that gzip is in the path. + Fix syntax error and define lnk$library in vms/Makefile.mms + REGSIGTYPE is void on the Amiga. + Do not write empty line when decompressing stdin with --verbose. + Fix the 1.1.2 fix for VMS (bug in get_suffix) + Added warning in README about compiler bug on Solaris 2.1 for x86. + Added warning about 'rehash' in INSTALL. + Removed default value of read_buf in bits.c (supermax doesn't like). + In tailor.h, added support for Borland C and Zortech C on OS/2. + Added warning in gzexe about Ultrix buggy sh (use /bin/sh5 instead). + Added warning in zdiff about AIX buggy sh (use /bin/ksh instead). + In configure.in, do not try the asm code if DEFS contains NO_ASM + +Fri Jun 4 09:49:33 1993 Jean-loup Gailly (jloup@chorus.fr) + + * version 1.1.2 + Fix serious bug for VMS (-gz not removed when decompressing). + Allow suffix other than .gz in znew. + Do not display compression ratio when decompressing stdin. + In zmore.in, work around brain damaged stty -g (Ultrix). + Display a correct compression ratio for .Z files. + Added .z to .gz renaming script in INTALL. + Allow setting CFLAGS in configure. + Add warning in README about bug in Concentrix cc compiler. + Avoid || in Makefile.in (at least one make doesn't support this). + Disable useless --ascii option for the Amiga. + Add a pointer to the Primos executable in README. + Added description of extra field in algorithm.doc. + Do not redefine NULL in alloca.c. + Added check for unsupported compression methods. + Avoid getopt redeclaration on OSF/1. + Tue Jun 1 09:07:15 1993 Jean-loup Gailly (jloup@chorus.fr) * version 1.1.1 diff --git a/gnu/usr.bin/gzip/Makefile b/gnu/usr.bin/gzip/Makefile index 36200b83eb..cd9ae24ebc 100644 --- a/gnu/usr.bin/gzip/Makefile +++ b/gnu/usr.bin/gzip/Makefile @@ -2,18 +2,18 @@ PROG= gzip SRCS= gzip.c zip.c deflate.c trees.c bits.c unzip.c inflate.c util.c \ - crypt.c lzw.c unlzw.c unpack.c getopt.c match.S -MAN1= gzexe.0 gzip.0 zdiff.0 zforce.0 zmore.0 znew.0 -CFLAGS+=-DASMV -DHAVE_UNISTD_H=1 -DDIRENT=1 -MLINKS= zdiff.1 zcmp.1 gzip.1 gunzip.1 gzip.1 zcat.1 gzip.1 gzcat.1 -LINKS+= ${DESTDIR}${BINDIR}/gzip ${DESTDIR}${BINDIR}/gunzip -LINKS+= ${DESTDIR}${BINDIR}/gzip ${DESTDIR}${BINDIR}/gzcat -LINKS+= ${DESTDIR}${BINDIR}/gzip ${DESTDIR}${BINDIR}/zcat + crypt.c lzw.c unlzw.c unlzh.c unpack.c getopt.c match.S +MAN1= gzexe.1 gzip.1 zdiff.1 zforce.1 zmore.1 znew.1 +CFLAGS+=-DASMV -DSTDC_HEADERS=1 -DHAVE_UNISTD_H=1 -DDIRENT=1 +MLINKS= gzip.1 gunzip.1 gzip.1 zcat.1 gzip.1 gzcat.1 +LINKS+= ${BINDIR}/gzip ${BINDIR}/gunzip +LINKS+= ${BINDIR}/gzip ${BINDIR}/gzcat +LINKS+= ${BINDIR}/gzip ${BINDIR}/zcat afterinstall: install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ ${.CURDIR}/zforce ${.CURDIR}/gzexe ${.CURDIR}/znew \ - ${.CURDIR}/zmore ${.CURDIR}/zdiff ${.CURDIR}/zcmp \ + ${.CURDIR}/zmore ${.CURDIR}/zdiff ${.CURDIR}/zgrep \ ${DESTDIR}${BINDIR} match.o: ${.CURDIR}/match.S diff --git a/gnu/usr.bin/gzip/NEWS b/gnu/usr.bin/gzip/NEWS index 097f7b177d..ef5f83359e 100644 --- a/gnu/usr.bin/gzip/NEWS +++ b/gnu/usr.bin/gzip/NEWS @@ -1,6 +1,69 @@ -Current Version: 1.1.1 +Current Version: 1.2.4. See the file ChangeLog for the details of all changes. +Major changes from 1.2.3 to 1.2.4 +* By default, do not restore file name and timestamp from those saved + inside the .gz file (behave as 'compress'). Added the --name option + to force name and timestamp restoration. +* Accept - as synonym for stdin. +* Use manlinks=so or ln to support either hard links or .so in man pages +* Accept foo.gz~ in zdiff. +* Added support for Windows NT +* Handle ENAMETOOLONG for strict Posix systems +* Use --recursive instead of --recurse to comply with Webster and + the GNU stdandard. +* Allow installation of shell scripts with a g prefix: make G=g install +* Install by default zcat as gzcat if gzcat already exists in path. +* Let zmore behave as more when invoked without parameters (give help) +* Let gzip --list reject files not in gzip format even with --force. +* Don't complain about non gzip files for options -rt or -rl. +* Added advice in INSTALL for several systems. + +Major changes from 1.2.2 to 1.2.3 +* Don't display the output name when decompressing except with --verbose. +* Remove usage of alloca in getopt.c and all makefiles. +* Added the zfile shell script in subdirectory sample. +* Moved the list of compiler bugs from README to INSTALL. +* Added vms/Readme.vms. + +Major changes from 1.2.1 to 1.2.2 +* Fix a compilation error on Sun with cc (worked with gcc). + +Major changes from 1.2 to 1.2.1 +* Let zmore act as more if the data is not gzipped. +* made gzexe more secure (don't rely on PATH). +* By default, display output name only when the name was actually truncated. + +Major changes from 1.1.2 to 1.2 +* Added the --list option to display the file characteristics. +* Added the --no-name option: do not save or restore original filename + Save the original name by default. +* Allow gunzip --suffix "" to attempt decompression on any file + regardless of its extension if an original name is present. +* Add support for the SCO compress -H format. +* gzip --fast now compresses faster (speed close to that of compress) + with degraded compression ratio (but still better than compress). + Default level changed to -6 (acts exactly as previous level -5) to + be a better indication of its placement in the speed/ratio range. +* Use smart name truncation: 123456789012.c -> 123456789.c.gz + instead of 12345678901.gz +* With --force, let zcat pass non gzip'ed data unchanged (zcat == cat) +* Added the zgrep shell script. +* Made sub.c useful for 16 bit sound, 24 bit images, etc.. +* Supress warnings about suffix for gunzip -r, except with --verbose. +* On MSDOS, use .gz extension when possible (files without extension) +* Moved the sample programs to a subdirectory sample. +* Added a "Special targets" section in INSTALL. + +Major changes from 1.1.1 to 1.1.2. +* Fix serious bug for VMS (-gz not removed when decompressing). +* Allow suffix other than .gz in znew. +* Do not display compression ratio when decompressing stdin. +* In zmore.in, work around brain damaged stty -g (Ultrix). +* Display a correct compression ratio for .Z files. +* Added .z to .gz renaming script in INTALL. +* Allow setting CFLAGS in configure. + Major changes from 1.1 to 1.1.1. * Fix serious bug in vms.c (affects Vax/VMS only). * Added --ascii option. diff --git a/gnu/usr.bin/gzip/README b/gnu/usr.bin/gzip/README index 152b7e5b2b..fdd7311448 100644 --- a/gnu/usr.bin/gzip/README +++ b/gnu/usr.bin/gzip/README @@ -1,4 +1,4 @@ -This is the file README for the gzip distribution, version 1.1.1. +This is the file README for the gzip distribution, version 1.2.4. gzip (GNU zip) is a compression utility designed to be a replacement for 'compress'. Its main advantages over compress are much better @@ -32,28 +32,12 @@ INSTALL for installation instructions. Some answers to frequently asked questions are given in the file INSTALL, please read it. (In particular, please don't ask me once more for an /etc/magic entry.) -WARNINGS about broken optimizers: - -- on the NeXT, "cc -finline-functions" is broken. gzip produces - valid .z files but they are much too large because the string - matching code misses most matches. Use "cc -O" instead. - -- on the Mips R4000, gcc -O (version 2.3.1) generates bad code, use cc - or just gcc -g instead. - -- gcc 2.3.3 on the SGI Indigo IRIX 4.0.5 also produces bad code. Use - instead: make CC='cc -O2' or gcc without -O. - -- on Sparc with SunOS 4.1.1 and the SC1.0 compiler, the optimizer - works up to -O3 but -O4 does not work. - -- MSC 5.1 with -Ox and -DDYN_ALLOC generates bad code in inflate.c. - The default is static allocation (no DYN_ALLOC) and -Ox works on inflate.c. - But -Ox does not work on util.c, so you must use -Oait -Gs. - -- On dnix 5.3 2.2 cc version 2.37c is buggy. Version 2.38d works. - -For all machines, Use "make check" to check that gzip was compiled correctly. +WARNING: on several systems, compiler bugs cause gzip to fail, in +particular when optimization options are on. See the section "Special +targets" at the end of the INSTALL file for a list of known problems. +For all machines, use "make check" to check that gzip was compiled +correctly. Try compiling gzip without any optimization if you have a +problem. Please send all comments and bug reports by electronic mail to: Jean-loup Gailly @@ -68,23 +52,27 @@ Bug reports should ideally include: * A description of the bug behavior * The input to gzip, that triggered the bug +If you send me patches for machines I don't have access to, please test them +very carefully. gzip is used for backups, it must be extremely reliable. + The package crypt++.el is highly recommended to manipulate gzip'ed file from emacs. It recognizes automatically encrypted and compressed files when they are first visited or written. It is available via anonymous ftp to roebling.poly.edu [128.238.5.31] in /pub/crypt++.el. The same directory contains also patches to dired, ange-ftp and info. -GNU tar 1.11.2 has a -z option to invoke directly gzip, so you don't have -to patch it. +GNU tar 1.11.2 has a -z option to invoke directly gzip, so you don't have to +patch it. The package ftp.uu.net:/languages/emacs-lisp/misc/jka-compr19.el.Z +also supports gzip'ed files. The znew and gzexe shell scripts provided with gzip benefit from (but do not require) the cpmod utility to transfer file attributes. It is available by anonymous ftp on gatekeeper.dec.com in /.0/usenet/comp.sources.unix/volume11/cpmod.Z. -The sample programs zread.c, sub.c and add.c are provided as examples -of useful complements to gzip. Read the comments inside each source file. -The perl script ztouch is also provided as example (not installed -by default since it relies on perl). +The sample programs zread.c, sub.c and add.c in subdirectory sample +are provided as examples of useful complements to gzip. Read the +comments inside each source file. The perl script ztouch is also +provided as example (not installed by default since it relies on perl). gzip is free software, you can redistribute it and/or modify it under @@ -93,13 +81,22 @@ provided under the name COPYING. The latest version of gzip are always available by ftp in prep.ai.mit.edu:/pub/gnu, or in any of the prep mirror sites: -- sources in gzip-*.tar (or .shar or .tar.z) +- sources in gzip-*.tar (or .shar or .tar.gz). - Solaris 2 executables in sparc-sun-solaris2/gzip-binaries-*.tar - MSDOS lha self-extracting exe in gzip-msdos-*.exe. Once extracted, copy gzip.exe to gunzip.exe and zcat.exe, or use "gzip -d" to decompress. + gzip386.exe runs much faster but only on 386 and above; it is compiled with + djgpp 1.10 available in directory omnigate.clarkson.edu:/pub/msdos/djgpp. A VMS executable is available in ftp.spc.edu:[.macro32.savesets]gzip-1-*.zip -(use [.macro32]unzip.exe to extract). +(use [.macro32]unzip.exe to extract). A PRIMOS executable is available +in ftp.lysator.liu.se:/pub/primos/run/gzip.run. +OS/2 executables (16 and 32 bits versions) are available in +ftp.tu-muenchen.de:/pub/comp/os/os2/archiver/gz*-[16,32].zip + +Some ftp servers can automatically make a tar.Z from a tar file. If +you are getting gzip for the first time, you can ask for a tar.Z file +instead of the much larger tar file. Many thanks to those who provided me with bug reports and feedback. See the files THANKS and ChangeLog for more details. @@ -129,11 +126,11 @@ On Unix, gzip is mostly useful in combination with tar. GNU tar 1.11.2 has a -z option to invoke gzip automatically. "tar -z" compresses better than zip, since gzip can then take advantage of redundancy between distinct files. The drawback is that you must -scan the whole tar.z file in order to extract a single file near +scan the whole tar.gz file in order to extract a single file near the end; unzip can directly seek to the end of the zip file. There is no overhead when you extract the whole archive anyway. If a member of a .zip archive is damaged, other files can still -be recovered. If a .tar.z file is damaged, files beyond the failure +be recovered. If a .tar.gz file is damaged, files beyond the failure point cannot be recovered. (Future versions of gzip will have error recovery features.) diff --git a/gnu/usr.bin/gzip/THANKS b/gnu/usr.bin/gzip/THANKS index 5c9a47bcce..6a545cb6a1 100644 --- a/gnu/usr.bin/gzip/THANKS +++ b/gnu/usr.bin/gzip/THANKS @@ -1,9 +1,11 @@ -gzip was written by Jean-loup Gailly , with -portions written by Mark Adler (inflate.c) and Peter Jannesen -(unlzw.c). The zip deflate format was defined by Phil Katz. -Thanks to those who reported problems and suggested -various improvements. Here is a partial list of them: +gzip was written by Jean-loup Gailly , with portions +written by Mark Adler (inflate.c), Peter Jannesen (unlzw.c) and +Haruhiko Okumura (unlzh.c). The zip deflate format was defined by Phil Katz. +Thanks to those who reported problems and suggested various +improvements. Here is a partial list of them: +Robert Abramovitz bromo@cougar.tandem.com +Jay Adams jka@ece.cmu.edu Mark Adler madler@cco.caltech.edu Edwin Allum edwin@csri.toronto.edu Joseph Arceneaux jla@gnu.ai.mit.edu @@ -12,6 +14,7 @@ Ken-ichiro Aoki aoki@madonna.physics.ucla.edu David Ascher da@marlowe.cog.brown.edu Eric Backus ericb@lsid.hp.com Becky A. Badgett badgett@cs.utexas.edu +Bo Nygaard Bai bai@iesd.auc.dk Dave Barber dbarber@apocalypse.bbn.com Rene Beaulieu reneb@distri.hydro.qc.ca Neal Becker neal@ctd.comsat.com @@ -19,36 +22,53 @@ Dieter Becker becker@med-in.uni-sb.de Nelson H. F. Beebe beebe@geronimo.math.utah.edu Jeff Beadles jeff@onion.rain.com David J. N. Begley dbegley@st.nepean.uws.edu.au +Bob Beresh rberesh@rd.hydro.on.ca Jim Bernard jbernard@iola.mines.colorado.edu Karl Berry karl@cs.umb.edu James W. Birdsall jwbirdsa@picarefy.picarefy.com +Scott Bolte scott@craycos.com Wayne E. Bouchard web@paladine.hacks.arizona.edu Marc Boucher marc@cam.org Ola Brahammar pt90ob@pt.hk-r.se Dave Brennan brennan@hal.com Alan Brown dogbowl@dogbox.acme.gen.nz +Michael L. Brown brown@wi.extrel.com Rodney Brown rdb@mel.cocam.oz.au Bruce bde@runx.oz.au +Bill Bumgarner bbum@stone.com Leila Burrell-Davis leilabd@syma.sussex.ac.uk Roger Butenuth butenuth@ira.uka.de +Jon Cargille jcargill@cs.wisc.edu Bud Carlson bud@isle.pegasus.com Lim Fung Chai fclim@i1sin.daq.semi.harris.com Wes Chalfant wes@kofax.com +Andrew A. Chernov ache@astral.msk.su Paul Close pdc@lunch.wpd.sgi.com +Jeff Coffler coffler@jac.enet.dec.com +Will Colley wcc3@occs.cs.oberlin.edu +Roger Cornelius sherpa!rac@uunet.uu.net Kevin Cosgrove kevinc@tekig6.pen.tek.com Stephen J Cowley s.j.cowley@amtp.cam.ac.uk +Ron Cox roncox@indirect.com Frank Crawford frank@photon.ansto.gov.au James R. Crawford qralston@cislabs.pitt.edu Lawrence Crowl crowl@research.cs.orst.edu Klaus Dahlenburg kdburg@incoahe.hanse.de William E Davidsen davidsen@ariel.crd.ge.com +John M. DeDourek dedourek@aixive2.cs.unb.ca Jeff Deifik jdeifik@isi.edu Vince DeMarco vince@whatnxt.cuc.ab.ca Michael De La Rue p91152@cplab.physics.edinburgh.ac.uk +Jeff Delinck delinck@pa621a.inland.com +John DeRoo deroo@grout.adv.shr.dec.com +Jim Diamond zsd@axe.drea.dnd.ca +Stefano Diomedi sd@teculx.tecsiel.it Lawrence R. Dodd dodd@roebling.poly.edu Matthew Donadio donadio@mxd120.rh.psu.edu Andy Dougherty andy@crystal.phys.lafayette.edu +Darrell Duane dduane@mason1.gmu.edu John Eaton jwe@che.utexas.edu +Will Edgington wedgingt@ptolemy.arc.nasa.gov Brian Edmonds edmonds@edmonds.home.cs.ubc.ca Paul Eggert eggert@twinsun.com Enami enami@sys.ptg.sony.co.jp @@ -58,12 +78,16 @@ Rik Faith faith@cs.unc.edu Larry Fahnoe fahnoe@c1mpls.mn.org Cristian Ferretti cfs@poincare.mat.puc.cl Karl-Jose Filler pla_jfi@pki-nbg.philips.de +Valery Fine fine@vxcern.cern.ch +Bob Fischer bobf@milne.geology.yale.edu Per Foreby perf@efd.lth.se Alexander Fraser alex@cs.umb.edu Noah Friedman friedman@gnu.ai.mit.edu Bob Friesenhahn bfriesen@iphase.com +Gerhard Friesland-Koepke frieslan@rzdspc3.informatik.uni-hamburg.de Andy Fyfe andy@scp.caltech.edu Geoff geoff@frs.faxon.com +Arnd Gerns gerns@informatik.uni-hildesheim.de Kaveh R. Ghazi ghazi@staccato.rutgers.edu Torbjorn Granlund tege@sics.se Carl Greco cgreco@parrot.creighton.edu @@ -71,68 +95,106 @@ Bruno Haible haible@ma2s2.mathematik.uni-karlsruhe.de Junio Hamano junio@shadow.twinsun.com Harald Hanche-Olsen hanche@ams.sunysb.edu Darrel R. Hankerson hankedr@mail.auburn.edu +Mark Hanning-Lee markhl@romeo.caltech.edu +Lars Hecking st000002@hrz1.hrz.th-darmstadt.de Ruediger Helsch ruediger@ramz.ing.tu-bs.de Mark C. Henderson mch@sqwest.wimsey.bc.ca Karl Heuer karl@kelp.boston.ma.us +Jarkko Hietaniemi jhi@dol-guldur.hut.fi Thomas Hiller hiller@fzi.de Eiji Hirai hirai@cc.swarthmore.edu Kjetil Torgrim Homme kjetilho@ifi.uio.no +Robert D. Houk rdh@sli.com +Jim Howard jim_howard@mentorg.com Preston Hunt gt5708a@prism.gatech.edu Shane C Hutchins sch@nymph.msel.unh.edu Hutch hutchinson@wrair-emh1.army.mil Lester Ingber ingber@alumni.caltech.edu Ken Ishii ishii@sni-usa.com Per Steinar Iversen iversen@vsfys1.fi.uib.no +Chris Jacobsen jacobsen@xray1.physics.sunysb.edu Michal Jaegermann ntomczak@vm.ucs.ualberta.ca Brian Jones brianj@skat.usc.edu Denny de Jonge witaddj@dutrex.tudelft.nl Arne H. Juul arnej@lise.unit.no Dana Jacobsen jacobsd@solar.cor2.epa.gov Peter Jannesen peter@ncs.nl +Brian D. Johnston johnstonb@med.ge.com +Walter W. Jones wwj@candela.cfr.nist.gov +Tom Judson judson@scf.usc.edu +Henry G. Juengst juengst@saph2.physik.uni-bonn.de Sarantos Kapidakis sarantos%manteion@ics.forth.gr Amir J. Katz amir@matis.ingr.com Steve Kelem kelem@castor.xilinx.com +Steven Kimball kimball@shrew.sanders.lockheed.com Randy Kirchhof rkk@posms.aus.tx.us Ned Kittlitz kittlitz@seagoon.sw.stratus.com +Sakai Kiyotaka ksakai@mtl.t.u-tokyo.ac.jp +Philip C Kizer pckizer@gonzo.tamu.edu Pete Klammer pklammer@ouray.denver.colorado.edu Fritz Kleemann kleemann@informatik.uni-wuerzburg.dbp.de +Wilhelm B. Kloke wb@ifado.arb-phys.uni-dortmund.de Tom Kloos tk@sequent.com Carsten Koch carsten.koch@icem.de Winfried Koenig win@in.rhein-main.de +Mathias Koerber mathias@solomon.technet.sg Steph Konigsdorfer s.konigsdorfer@frmy.bull.fr +Leif Kornstaedt leif@rumtifsl.ruessel.sub.org Michael D. Lawler mdlawler@bsu-cs.bsu.edu Kevin Layer layer@franz.com Howard D. Leadmon howardl@wb3ffv.ampr.org Alexander Lehmann alex@hal.rhein-main.de Simon Leinen simon@lia.di.epfl.ch +Burt Leland burt@molecular.com +Tony Leneis tony@plaza.adp.ds.com Hugues Leroy hugues.leroy@irisa.fr +Marty Leisner leisner@eso.mc.xerox.com Charles Levert charles@aramis.comm.polymtl.ca +Richard Levitte levitte@e.kth.se Torbj|rn Lindh toobii@elixir.e.kth.se David R. Linn drl@vuse.vanderbilt.edu Antonio Lioy cat@athena.polito.it Jamie Lokier u90jl@ecs.oxford.ac.uk +Richard Lloyd R.K.Lloyd@csc.liv.ac.uk David J. MacKenzie djm@eng.umd.edu John R MacMillan john@chance.gts.org Ron Male male@eso.mc.xerox.com +Don R. Maszle maze@bea.lbl.gov +Jaye Mathisen osyjm@cs.montana.edu +Telly Mavroidis mavroidi@acf2.nyu.edu +Imed Eddine Mbarki mbarki@pacific.cmpe.psu.edu Steeve McCauley steeve@pooh.geophys.mcgill.ca +Tom McConnell tmcconne@sedona.intel.com Tod McQuillin mcquill@ccit05.duq.edu Tye McQueen tye@spillman.com Bernd Melchers melchers@chemie.fu-berlin.de Jason Merrill jason@jarthur.claremont.edu Dean S. Messing deanm@medulla.labs.tek.com +M. Mesturino mesturino@cselt.stet.it Luke Mewburn zak@rmit.edu.au Jim Meyering meyering@cs.utexas.edu -Frederic Miserey miserey@laguna.ics.uci.edu +Dragan Milicic milicic@math.utah.edu +Frederic Miserey none.fred@applelink.apple.com Marcel J.E. Mol marcel@duteca.et.tudelft.nl +Soren Juul Moller sjm@dde.dk Chris Moore moore@src.bae.co.uk +Dan Mosedale mosedale@genome.stanford.edu Helmut Muelner hmuelner@fiicmds04.tu-graz.ac.at Urban D Mueller umueller@amiga.physik.unizh.ch +Ulrich Mueller ulm@vsnhdb.cern.ch Timothy Murphy tim@maths.tcd.ie Greg Naber greg@squally.halcyon.com +Jay Nayegandhi jayng@bbiv02.enet.dec.com +Paul K. Neville II pkn2@idsi.com Karl L. Noell noell@informatik.fh-wiesbaden.dbp.de +Demizu Noritoshi nori-d@is.aist-nara.ac.jp +Todd Ogasawara todd@protege.pegasus.com +Helge Oldach helge.oldach@stollmann.de Arthur David Olson ado@elsie.nci.nih.gov Piet van Oostrum piet@cs.ruu.nl Rafael R. Pappalardo rafapa@obelix.cica.es +Mike Pearlman canuck@masc38.rice.edu +Yves Perrenoud pyves@nuga.alphanet.ch Hal Peterson hrp@pecan.cray.com Pascal Petit petit@cadillac.ibp.fr Bruno Pillard bp@chorus.fr @@ -141,12 +203,17 @@ Jay Pinkos pinkos@butyng.bu.edu Thomas Plass thomas@cogsci.ed.ac.uk Mike Polo mikep@cfsmo.honeywell.com Francesco Potorti pot@fly.cnuce.cnr.it +Will Priest bpriest@lobby.ti.com David Purves purves@apogee.com Andreas Raab ar@nvmr.robin.de Eric S. Raymond esr@snark.thyrsus.com Klaus Reimann kr@cip.physik.uni-stuttgart.de Michael Rendell michael@mercury.cs.mun.ca +Hal Render render@massive.uccs.edu +Julian F. Reschke julian@math.uni-muenster.de +Phil Richards Phil.Richards@prg.oxford.ac.uk Roland B Roberts roberts@nsrl31.nsrl.rochester.edu +Arnold Robbins arnold@cc.gatech.edu Kevin Rodgers kevin@rolling-stone.den.mmc.com Kai Uwe Rommel rommel@informatik.tu-muenchen.de Paul Rubin phr@america.telebit.com @@ -159,13 +226,20 @@ Niimi Satoshi a01309@cfi.waseda.ac.jp Marc Schaefer sysadm@alphanet.ch Andreas Schwab schwab@lamothe.informatik.uni-dortmund.de Eric Schenk schenk@cs.toronto.edu +Eric P. Scott eps@cs.sfsu.edu +Olaf Seibert rhialto@mbfys.kun.nl +Sunando Sen sens@fasecon.econ.nyu.edu +Harry Shamansky hts@hertz.eng.ohio-state.edu +Amos Shapira amoss@cs.huji.ac.il Rick Sladkey jrs@world.std.com Daniel L Smith dls@autodesk.com Fred Smith fredex%fcshome@merk.merk.com +Stephen Soliday soliday@ncat.edu Paul Southworth pauls@css.itd.umich.edu Rob Spencer robbie@winkle.bhpese.oz.au Richard Stallman rms@gnu.ai.mit.edu Carsten Steger carsten.steger@informatik.tu-muenchen.de +David Sundstrom sunds@anon.asic.sc.ti.com Ed Sznyter ews@babel.babel.com Hideaki Tanabe arctanx@iyeyasu.ynl.t.u-tokyo.ac.jp Andrew Telford ajt@peregrin.resmel.bhp.com.au @@ -177,19 +251,25 @@ Jeff Treece treece@sabbagh.com Oliver Trepte oliver@ikaros.fysik4.kth.se Stephane Tsacas slt@is21.isoft.fr Stephen Tweedie sct@dcs.ed.ac.uk +John R. Vanderpool fish@daacdev1.stx.com Sotiris Vassilopoulos vassilopoulos@virginia.edu Pedro A. M. Vazquez vazquez@iqm.unicamp.br Arjan de Vet devet@win.tue.nl +Larry W. Virden lvirden@cas.org Vadim V. Vlasov vvlasov@inucres.msk.su Eduard Vopicka eduard.vopicka@vse.cs Theo Vosse vosse@ruls41.leidenuniv.nl +Darin Wayrynen darin@pcg.uucp Marcel Waldvogel marcel@nice.usergroup.ethz.ch Stephen J. Walick steve@nshore.org Gray Watson gray@antaire.com +David Watt dmwatt@smersh.cambridge.ma.us Scott Weikart scott@igc.apc.org Ivo Welch iwelch@agsm.ucla.edu +Jochen Wiedmann zrawi01@zmcipdec1.zdv.uni-tuebingen.de Gijsb. Wiesenekker wiesenecker@sara.nl Wietze van Winden wietze@swi.psy.uva.nl +Frank Wuebbeling wuebbel@math.uni-muenster.de Larry W. Virden lwv26@cas.org Bill Wohler wohler@sap-ag.de Jamie Zawinski jwz@lucid.com diff --git a/gnu/usr.bin/gzip/TODO b/gnu/usr.bin/gzip/TODO index 1587c759ac..865be92507 100644 --- a/gnu/usr.bin/gzip/TODO +++ b/gnu/usr.bin/gzip/TODO @@ -8,6 +8,11 @@ Some of the planned features include: library, but this would degrade performance. In the meantime, you can look at the sample program zread.c. + The library should have one mode in which compressed data is sent + as soon as input is available, instead of waiting for complete + blocks. This can be useful for sending compressed data to/from interactive + programs. + - Make it convenient to define alternative user interfaces (in particular for windowing environments). @@ -48,11 +53,6 @@ Some of the planned features include: - Use a larger window size to deal with some large redundant files that 'compress' currently handles better than gzip. -- implement the following options: - - -e encrypt - -l list .z file contents +- Implement the -e (encrypt) option. -- support .Z files in SCO 'compress -H' format. - Send comments to Jean-loup Gailly . diff --git a/gnu/usr.bin/gzip/algorithm.doc b/gnu/usr.bin/gzip/algorithm.doc index 9c4b0c2d2d..24f7619ab6 100644 --- a/gnu/usr.bin/gzip/algorithm.doc +++ b/gnu/usr.bin/gzip/algorithm.doc @@ -48,6 +48,12 @@ match, thus speeding up the whole process. If compression ratio is more important than speed, zip attempts a complete second search even if the first match is already long enough. +The lazy match evaluation is no performed for the fastest compression +modes (speed options -1 to -3). For these fast modes, new strings +are inserted in the hash table only when no match was found, or +when the match is not too long. This degrades the compression ratio +but saves time since there are both fewer insertions and fewer searches. + 2. gzip file format @@ -100,6 +106,23 @@ cleared indicating binary data. For systems which have different file formats for ascii text and binary data, the decompressor can use the flag to choose the appropriate format. +The extra field, if present, must consist of one or more subfields, +each with the following format: + + subfield id : 2 bytes + subfield size : 2 bytes (little-endian format) + subfield data + +The subfield id can consist of two letters with some mnemonic value. +Please send any such id to jloup@chorus.fr. Ids with a zero second +byte are reserved for future use. The following ids are defined: + + Ap (0x41, 0x70) : Apollo file type information + +The subfield size is the size of the subfield data and does not +include the id and the size itself. The field 'extra field length' is +the total size of the extra field, including subfield ids and sizes. + It must be possible to detect the end of the compressed data with any compression format, regardless of the actual size of the compressed data. If the compressed data cannot fit in one file (in particular for diff --git a/gnu/usr.bin/gzip/bits.c b/gnu/usr.bin/gzip/bits.c index 65e78a653b..544d6da9d3 100644 --- a/gnu/usr.bin/gzip/bits.c +++ b/gnu/usr.bin/gzip/bits.c @@ -59,8 +59,8 @@ # include #endif -#ifndef lint -static char rcsid[] = "$Id: bits.c,v 0.8 1993/02/04 13:21:06 jloup Exp $"; +#ifdef RCSID +static char rcsid[] = "$Id: bits.c,v 0.9 1993/06/11 10:16:58 jloup Exp $"; #endif /* =========================================================================== @@ -84,7 +84,7 @@ local int bi_valid; * are always zero. */ -int (*read_buf) OF((char *buf, unsigned size)) = file_read; +int (*read_buf) OF((char *buf, unsigned size)); /* Current input function. Set to mem_read for in-memory compression */ #ifdef DEBUG diff --git a/gnu/usr.bin/gzip/crypt.c b/gnu/usr.bin/gzip/crypt.c index c6c7358650..cbce024a01 100644 --- a/gnu/usr.bin/gzip/crypt.c +++ b/gnu/usr.bin/gzip/crypt.c @@ -1,6 +1,6 @@ /* crypt.c (dummy version) -- do not perform encryption * Hardly worth copyrighting :-) */ -#ifndef lint +#ifdef RCSID static char rcsid[] = "$Id: crypt.c,v 0.6 1993/03/22 09:48:47 jloup Exp $"; #endif diff --git a/gnu/usr.bin/gzip/deflate.c b/gnu/usr.bin/gzip/deflate.c index 0b3af93ed8..7f52b64fa7 100644 --- a/gnu/usr.bin/gzip/deflate.c +++ b/gnu/usr.bin/gzip/deflate.c @@ -67,8 +67,8 @@ #include "gzip.h" #include "lzw.h" /* just for consistency checking */ -#ifndef lint -static char rcsid[] = "$Id: deflate.c,v 0.13 1993/05/25 16:25:40 jloup Exp $"; +#ifdef RCSID +static char rcsid[] = "$Id: deflate.c,v 0.15 1993/06/24 10:53:53 jloup Exp $"; #endif /* =========================================================================== @@ -97,7 +97,7 @@ static char rcsid[] = "$Id: deflate.c,v 0.13 1993/05/25 16:25:40 jloup Exp $"; /* To save space (see unlzw.c), we overlay prev+head with tab_prefix and * window with tab_suffix. Check that we can do this: */ -#if WSIZE<<1 > 1< (1< BITS-1 @@ -187,10 +187,19 @@ unsigned near max_chain_length; local unsigned int max_lazy_match; /* Attempt to find a better match only when the current match is strictly - * smaller than this value. + * smaller than this value. This mechanism is used only for compression + * levels >= 4. */ +#define max_insert_length max_lazy_match +/* Insert new strings in the hash table only if the match length + * is not greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + +local int compr_level; +/* compression level (1..9) */ -int near good_match; +unsigned near good_match; /* Use a faster search when the previous match is longer than this */ @@ -216,18 +225,20 @@ typedef struct config { local config configuration_table[10] = { /* good lazy nice chain */ /* 0 */ {0, 0, 0, 0}, /* store only */ -/* 1 */ {4, 4, 16, 16}, /* maximum speed */ -/* 2 */ {6, 8, 16, 16}, -/* 3 */ {8, 16, 32, 32}, -/* 4 */ {8, 16, 64, 64}, -/* 5 */ {8, 16, 128, 128}, -/* 6 */ {8, 32, 128, 256}, -/* 7 */ {8, 64, 128, 512}, +/* 1 */ {4, 4, 8, 4}, /* maximum speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8}, +/* 3 */ {4, 6, 32, 32}, + +/* 4 */ {4, 4, 16, 16}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32}, +/* 6 */ {8, 16, 128, 128}, +/* 7 */ {8, 32, 128, 256}, /* 8 */ {32, 128, 258, 1024}, /* 9 */ {32, 258, 258, 4096}}; /* maximum compression */ -/* Note: the current code requires max_lazy >= MIN_MATCH and max_chain >= 4 - * but these restrictions can easily be removed at a small cost. +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. */ #define EQUAL 0 @@ -237,6 +248,8 @@ local config configuration_table[10] = { * Prototypes for local functions. */ local void fill_window OF((void)); +local ulg deflate_fast OF((void)); + int longest_match OF((IPos cur_match)); #ifdef ASMV void match_init OF((void)); /* asm code initialization */ @@ -277,6 +290,7 @@ void lm_init (pack_level, flags) register unsigned j; if (pack_level < 1 || pack_level > 9) error("bad pack level"); + compr_level = pack_level; /* Initialize the hash table. */ #if defined(MAXSEG_64K) && HASH_BITS == 15 @@ -558,10 +572,12 @@ local void fill_window() (char*)NULL, (long)strstart - block_start, (eof)) /* =========================================================================== - * Processes a new input file and return its compressed length. + * Processes a new input file and return its compressed length. This + * function does not perform lazy evaluationof matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. */ -#ifdef NO_LAZY -ulg deflate() +local ulg deflate_fast() { IPos hash_head; /* head of the hash chain */ int flush; /* set if current block must be flushed */ @@ -592,22 +608,38 @@ ulg deflate() flush = ct_tally(strstart-match_start, match_length - MIN_MATCH); lookahead -= match_length; - match_length--; /* string at strstart already in hash table */ - do { - strstart++; - INSERT_STRING(strstart, hash_head); - /* strstart never exceeds WSIZE-MAX_MATCH, so there are - * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH - * these bytes are garbage, but it does not matter since the - * next lookahead bytes will always be emitted as literals. - */ - } while (--match_length != 0); + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if (match_length <= max_insert_length) { + match_length--; /* string at strstart already in hash table */ + do { + strstart++; + INSERT_STRING(strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since + * the next lookahead bytes will be emitted as literals. + */ + } while (--match_length != 0); + strstart++; + } else { + strstart += match_length; + match_length = 0; + ins_h = window[strstart]; + UPDATE_HASH(ins_h, window[strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } } else { /* No match, output a literal byte */ + Tracevv((stderr,"%c",window[strstart])); flush = ct_tally (0, window[strstart]); lookahead--; + strstart++; } - strstart++; if (flush) FLUSH_BLOCK(0), block_start = strstart; /* Make sure that we always have enough lookahead, except @@ -620,7 +652,7 @@ ulg deflate() } return FLUSH_BLOCK(1); /* eof */ } -#else /* LAZY */ + /* =========================================================================== * Same as above, but achieves better compression. We use a lazy * evaluation for matches: a match is finally adopted only if there is @@ -637,6 +669,8 @@ ulg deflate() extern long isize; /* byte length of input file, for debug only */ #endif + if (compr_level <= 3) return deflate_fast(); /* optimized for speed */ + /* Process the input block. */ while (lookahead != 0) { /* Insert the string window[strstart .. strstart+2] in the @@ -727,4 +761,3 @@ ulg deflate() return FLUSH_BLOCK(1); /* eof */ } -#endif /* LAZY */ diff --git a/gnu/usr.bin/gzip/getopt.c b/gnu/usr.bin/gzip/getopt.c index 3bf027710f..55fad84331 100644 --- a/gnu/usr.bin/gzip/getopt.c +++ b/gnu/usr.bin/gzip/getopt.c @@ -3,62 +3,64 @@ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu before changing it! - Copyright (C) 1987, 88, 89, 90, 91, 1992 Free Software Foundation, Inc. + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 + Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* AIX requires this to be the first thing in the file. */ -#ifdef __GNUC__ -#define alloca __builtin_alloca -#else /* not __GNUC__ */ -#if defined (HAVE_ALLOCA_H) || (defined(sparc) && (defined(sun) || (!defined(USG) && !defined(SVR4) && !defined(__svr4__)))) -#include -#else -#ifdef _AIX - #pragma alloca -#else -char *alloca (); +#ifdef HAVE_CONFIG_H +#include "config.h" #endif -#endif /* alloca.h */ -#endif /* not __GNUC__ */ -#include +#ifndef __STDC__ +# ifndef const +# define const +# endif +#endif -#if defined(USG) || defined(STDC_HEADERS) || defined(__GNU_LIBRARY__) -#include +/* This tells Alpha OSF/1 not to define a getopt prototype in . */ +#ifndef _NO_PROTO +#define _NO_PROTO #endif +#include +#include "tailor.h" + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ -#undef alloca /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ #include -#else /* Not GNU C library. */ -#define __alloca alloca #endif /* GNU C library. */ -#if !__STDC__ -#define const -#endif - /* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a long-named option. Because this is not POSIX.2 compliant, it is being phased out. */ -#define GETOPT_COMPAT +/* #define GETOPT_COMPAT */ /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user @@ -96,6 +98,7 @@ char *optarg = 0; Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ +/* XXX 1003.2 says this must be 1 before any call. */ int optind = 0; /* The next char to be scanned in the option-element @@ -112,6 +115,13 @@ static char *nextchar; int opterr = 1; +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +#define BAD_OPTION '\0' +int optopt = BAD_OPTION; + /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, @@ -147,39 +157,53 @@ static enum } ordering; #ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ #include #define my_index strchr -#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n)) +#define my_strlen strlen #else /* Avoid depending on library functions or files whose names are inconsistent. */ -char *getenv (); +#if __STDC__ || defined(PROTO) +extern char *getenv(const char *name); +extern int strcmp (const char *s1, const char *s2); +extern int strncmp(const char *s1, const char *s2, int n); + +static int my_strlen(const char *s); +static char *my_index (const char *str, int chr); +#else +extern char *getenv (); +#endif + +static int +my_strlen (str) + const char *str; +{ + int n = 0; + while (*str++) + n++; + return n; +} static char * -my_index (string, chr) - char *string; +my_index (str, chr) + const char *str; int chr; { - while (*string) + while (*str) { - if (*string == chr) - return string; - string++; + if (*str == chr) + return (char *) str; + str++; } return 0; } -static void -my_bcopy (from, to, size) - char *from, *to; - int size; -{ - int i; - for (i = 0; i < size; i++) - to[i] = from[i]; -} #endif /* GNU C library. */ /* Handle permutation of arguments. */ @@ -198,32 +222,49 @@ static int last_nonopt; the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe - the new indices of the non-options in ARGV after they are moved. */ + the new indices of the non-options in ARGV after they are moved. + + To perform the swap, we first reverse the order of all elements. So + all options now come before all non options, but they are in the + wrong order. So we put back the options and non options in original + order by reversing them again. For example: + original input: a b c -x -y + reverse all: -y -x c b a + reverse options: -x -y c b a + reverse non options: -x -y a b c +*/ + +#if __STDC__ || defined(PROTO) +static void exchange (char **argv); +#endif static void exchange (argv) char **argv; { - int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); -#ifdef _CRAY - char *temp[last_nonopt - first_nonopt]; -#else - char **temp = (char **) __alloca (nonopts_size); -#endif - - /* Interchange the two blocks of data in ARGV. */ - - my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size); - my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt], - (optind - last_nonopt) * sizeof (char *)); - my_bcopy ((char *) temp, - (char *) &argv[first_nonopt + optind - last_nonopt], - nonopts_size); - - /* Update records for the slots the non-options now occupy. */ + char *temp, **first, **last; + /* Reverse all the elements [first_nonopt, optind) */ + first = &argv[first_nonopt]; + last = &argv[optind-1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } + /* Put back the options in order */ + first = &argv[first_nonopt]; first_nonopt += (optind - last_nonopt); + last = &argv[first_nonopt - 1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } + + /* Put back the non options in order */ + first = &argv[first_nonopt]; last_nonopt = optind; + last = &argv[last_nonopt-1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } } /* Scan elements of ARGV (whose length is ARGC) for option characters @@ -246,8 +287,8 @@ exchange (argv) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, - return '?' after printing an error message. If you set `opterr' to - zero, the error message is suppressed but we still return '?'. + return BAD_OPTION after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return BAD_OPTION. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following @@ -426,7 +467,7 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) p++, option_index++) if (!strncmp (p->name, nextchar, s - nextchar)) { - if (s - nextchar == strlen (p->name)) + if (s - nextchar == my_strlen (p->name)) { /* Exact match found. */ pfound = p; @@ -450,9 +491,9 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) if (opterr) fprintf (stderr, "%s: option `%s' is ambiguous\n", argv[0], argv[optind]); - nextchar += strlen (nextchar); + nextchar += my_strlen (nextchar); optind++; - return '?'; + return BAD_OPTION; } if (pfound != NULL) @@ -480,8 +521,8 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) "%s: option `%c%s' doesn't allow an argument\n", argv[0], argv[optind - 1][0], pfound->name); } - nextchar += strlen (nextchar); - return '?'; + nextchar += my_strlen (nextchar); + return BAD_OPTION; } } else if (pfound->has_arg == 1) @@ -493,11 +534,11 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) if (opterr) fprintf (stderr, "%s: option `%s' requires an argument\n", argv[0], argv[optind - 1]); - nextchar += strlen (nextchar); - return '?'; + nextchar += my_strlen (nextchar); + return optstring[0] == ':' ? ':' : BAD_OPTION; } } - nextchar += strlen (nextchar); + nextchar += my_strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) @@ -515,7 +556,7 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) #ifdef GETOPT_COMPAT || argv[optind][0] == '+' #endif /* GETOPT_COMPAT */ - || my_index ((char*)optstring, *nextchar) == NULL) + || my_index (optstring, *nextchar) == NULL) { if (opterr) { @@ -530,7 +571,7 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) } nextchar = (char *) ""; optind++; - return '?'; + return BAD_OPTION; } } @@ -538,7 +579,7 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) { char c = *nextchar++; - char *temp = my_index ((char*)optstring, c); + char *temp = my_index (optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') @@ -548,13 +589,19 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) { if (opterr) { +#if 0 if (c < 040 || c >= 0177) fprintf (stderr, "%s: unrecognized option, character code 0%o\n", argv[0], c); else fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); +#endif } - return '?'; + optopt = c; + return BAD_OPTION; } if (temp[1] == ':') { @@ -583,9 +630,21 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) else if (optind == argc) { if (opterr) - fprintf (stderr, "%s: option `-%c' requires an argument\n", - argv[0], c); - c = '?'; + { +#if 0 + fprintf (stderr, "%s: option `-%c' requires an argument\n", + argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = BAD_OPTION; } else /* We already incremented `optind' once; @@ -621,22 +680,7 @@ getopt_long (argc, argv, options, long_options, opt_index) return _getopt_internal (argc, argv, options, long_options, opt_index, 0); } -/* Like getopt_long, but '-' as well as '--' can indicate a long option. - If an option that starts with '-' (not '--') doesn't match a long option, - but does match a short option, it is parsed as a short option - instead. */ - -int -getopt_long_only (argc, argv, options, long_options, opt_index) - int argc; - char *const *argv; - const char *options; - const struct option *long_options; - int *opt_index; -{ - return _getopt_internal (argc, argv, options, long_options, opt_index, 1); -} - +#endif /* _LIBC or not __GNU_LIBRARY__. */ #ifdef TEST @@ -689,7 +733,7 @@ main (argc, argv) printf ("option c with value `%s'\n", optarg); break; - case '?': + case BAD_OPTION: break; default: diff --git a/gnu/usr.bin/gzip/getopt.h b/gnu/usr.bin/gzip/getopt.h index 764f2f4cf5..0abce6e921 100644 --- a/gnu/usr.bin/gzip/getopt.h +++ b/gnu/usr.bin/gzip/getopt.h @@ -1,16 +1,16 @@ /* Declarations for getopt. - Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. + Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ @@ -49,6 +49,10 @@ extern int optind; extern int opterr; +/* Set to an option character which was unrecognized. */ + +extern int optopt; + /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of `struct option' terminated by an element containing a name which is @@ -86,12 +90,9 @@ struct option /* Names for the values of the `has_arg' field of `struct option'. */ -enum _argtype -{ - no_argument, - required_argument, - optional_argument -}; +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 #if __STDC__ || defined(PROTO) #if defined(__GNU_LIBRARY__) @@ -99,8 +100,6 @@ enum _argtype differences in the consts, in stdlib.h. To avoid compilation errors, only prototype getopt for the GNU C library. */ extern int getopt (int argc, char *const *argv, const char *shortopts); -#else /* not __GNU_LIBRARY__ */ -extern int getopt (); #endif /* not __GNU_LIBRARY__ */ extern int getopt_long (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); diff --git a/gnu/usr.bin/gzip/gzexe b/gnu/usr.bin/gzip/gzexe index ba22eb1e6f..0c248d9920 100644 --- a/gnu/usr.bin/gzip/gzexe +++ b/gnu/usr.bin/gzip/gzexe @@ -8,8 +8,9 @@ # We also try to retain the original file permissions on the compressed file. # For safety reasons, gzexe will not create setuid or setgid shell scripts. -# Warning: the first line of this file must be either : or #!/bin/sh +# WARNING: the first line of this file must be either : or #!/bin/sh # The : is required for some old versions of csh. +# On Ultrix, /bin/sh is too buggy, change the first line to: #!/bin/sh5 x=`basename $0` if test $# = 0; then @@ -37,6 +38,21 @@ if test -z "`(${CPMOD-cpmod} zfoo1$$ zfoo2$$) 2>&1`"; then fi rm -f zfoo[12]$$ +tail="" +IFS="${IFS= }"; saveifs="$IFS"; IFS="${IFS}:" +for dir in $PATH; do + test -z "$dir" && dir=. + if test -f $dir/tail; then + tail="$dir/tail" + break + fi +done +IFS="$saveifs" +if test -z "$tail"; then + echo cannot find tail + exit 1 +fi + for i do if test ! -f "$i" ; then echo ${x}: $i not a file @@ -57,10 +73,10 @@ for i do echo "${x}: $i has setgid permission, unchanged" continue fi - if test "`basename $i`" = gzip; then - echo "${x}: cannot compress gzip itself" - continue - fi + case "`basename $i`" in + gzip | tail | chmod | ln | sleep | rm) + echo "${x}: $i would depend on itself"; continue ;; + esac if test -z "$cpmod"; then cp -p "$i" $tmp 2>/dev/null || cp "$i" $tmp if test -w $tmp 2>/dev/null; then @@ -72,18 +88,18 @@ for i do fi if test $decomp -eq 0; then sed 1q $0 > $tmp - cat >> $tmp <<'EOF' + sed "s|^if tail|if $tail|" >> $tmp <<'EOF' skip=18 if tail +$skip $0 | gzip -cd > /tmp/gztmp$$; then - chmod 755 /tmp/gztmp$$ - prog="`basename $0`" - if ln /tmp/gztmp$$ "/tmp/$prog" 2>/dev/null; then + chmod 700 /tmp/gztmp$$ + prog="`echo $0 | sed 's|^.*/||'`" + if /bin/ln /tmp/gztmp$$ "/tmp/$prog" 2>/dev/null; then trap '/bin/rm -f /tmp/gztmp$$ "/tmp/$prog"; exit $res' 0 - (sleep 5; /bin/rm -f /tmp/gztmp$$ "/tmp/$prog") 2>/dev/null & + (/bin/sleep 5; /bin/rm -f /tmp/gztmp$$ "/tmp/$prog") 2>/dev/null & /tmp/"$prog" ${1+"$@"}; res=$? else trap '/bin/rm -f /tmp/gztmp$$; exit $res' 0 - (sleep 5; /bin/rm -f /tmp/gztmp$$) 2>/dev/null & + (/bin/sleep 5; /bin/rm -f /tmp/gztmp$$) 2>/dev/null & /tmp/gztmp$$ ${1+"$@"}; res=$? fi else diff --git a/gnu/usr.bin/gzip/gzexe.1 b/gnu/usr.bin/gzip/gzexe.1 index 68e41279d7..8b62cd6063 100644 --- a/gnu/usr.bin/gzip/gzexe.1 +++ b/gnu/usr.bin/gzip/gzexe.1 @@ -27,6 +27,13 @@ This utility is most useful on systems with very small disks. Decompress the given executables instead of compressing them. .SH "SEE ALSO" gzip(1), znew(1), zmore(1), zcmp(1), zforce(1) +.SH CAVEATS +The compressed executable is a shell script. This may create some +security holes. In particular, the compressed executable relies +on the PATH environment variable to find +.I gzip +and some other utilities +.I (tail, chmod, ln, sleep). .SH "BUGS" .I gzexe attempts to retain the original file attributes on the compressed executable, diff --git a/gnu/usr.bin/gzip/gzexe.in b/gnu/usr.bin/gzip/gzexe.in deleted file mode 100644 index dcbab88d3b..0000000000 --- a/gnu/usr.bin/gzip/gzexe.in +++ /dev/null @@ -1,135 +0,0 @@ -: -#!/bin/sh -# gzexe: compressor for Unix executables. -# Use this only for binaries that you do not use frequently. -# -# The compressed version is a shell script which decompresses itself after -# skipping $skip lines of shell commands. We try invoking the compressed -# executable with the original name (for programs looking at their name). -# We also try to retain the original file permissions on the compressed file. -# For safety reasons, gzexe will not create setuid or setgid shell scripts. - -# Warning: the first line of this file must be either : or #!/bin/sh -# The : is required for some old versions of csh. - -x=`basename $0` -if test $# = 0; then - echo compress executables. original file foo is renamed to foo~ - echo usage: ${x} [-d] files... - echo " -d decompress the executables" - exit 1 -fi - -tmp=gz$$ -trap "rm -f $tmp; exit 1" 1 2 3 5 10 13 15 - -decomp=0 -res=0 -test "$x" = "ungzexe" && decomp=1 -if test "x$1" = "x-d"; then - decomp=1 - shift -fi - -echo hi > zfoo1$$ -echo hi > zfoo2$$ -if test -z "`(${CPMOD-cpmod} zfoo1$$ zfoo2$$) 2>&1`"; then - cpmod=${CPMOD-cpmod} -fi -rm -f zfoo[12]$$ - -for i do - if test ! -f "$i" ; then - echo ${x}: $i not a file - res=1 - continue - fi - if test $decomp -eq 0; then - if sed -e 1d -e 2q "$i" | grep "^skip=[0-9]*$" >/dev/null; then - echo "${x}: $i is already gzexe'd" - continue - fi - fi - if ls -l "$i" | grep '^...[sS]' > /dev/null; then - echo "${x}: $i has setuid permission, unchanged" - continue - fi - if ls -l "$i" | grep '^......[sS]' > /dev/null; then - echo "${x}: $i has setgid permission, unchanged" - continue - fi - if test "`basename $i`" = gzip; then - echo "${x}: cannot compress gzip itself" - continue - fi - if test -z "$cpmod"; then - cp -p "$i" $tmp 2>/dev/null || cp "$i" $tmp - if test -w $tmp 2>/dev/null; then - writable=1 - else - writable=0 - chmod u+w $tmp 2>/dev/null - fi - fi - if test $decomp -eq 0; then - sed 1q $0 > $tmp - cat >> $tmp <<'EOF' -skip=18 -if tail +$skip $0 | gzip -cd > /tmp/gztmp$$; then - chmod 755 /tmp/gztmp$$ - prog="`basename $0`" - if ln /tmp/gztmp$$ "/tmp/$prog" 2>/dev/null; then - trap '/bin/rm -f /tmp/gztmp$$ "/tmp/$prog"; exit $res' 0 - (sleep 5; /bin/rm -f /tmp/gztmp$$ "/tmp/$prog") 2>/dev/null & - /tmp/"$prog" ${1+"$@"}; res=$? - else - trap '/bin/rm -f /tmp/gztmp$$; exit $res' 0 - (sleep 5; /bin/rm -f /tmp/gztmp$$) 2>/dev/null & - /tmp/gztmp$$ ${1+"$@"}; res=$? - fi -else - echo Cannot decompress $0; exit 1 -fi; exit $res -EOF - gzip -cv9 "$i" >> $tmp || { - /bin/rm -f $tmp - echo ${x}: compression not possible for $i, file unchanged. - res=1 - continue - } - - else - # decompression - skip=18 - if sed -e 1d -e 2q "$i" | grep "^skip=[0-9]*$" >/dev/null; then - eval `sed -e 1d -e 2q "$i"` - fi - if tail +$skip "$i" | gzip -cd > $tmp; then - : - else - echo ${x}: $i probably not in gzexe format, file unchanged. - res=1 - continue - fi - fi - rm -f "$i~" - mv "$i" "$i~" || { - echo ${x}: cannot backup $i as $i~ - rm -f $tmp - res=1 - continue - } - mv $tmp "$i" || cp -p $tmp "$i" 2>/dev/null || cp $tmp "$i" || { - echo ${x}: cannot create $i - rm -f $tmp - res=1 - continue - } - rm -f $tmp - if test -n "$cpmod"; then - $cpmod "$i~" "$i" 2>/dev/null - elif test $writable -eq 0; then - chmod u-w $i 2>/dev/null - fi -done -exit $res diff --git a/gnu/usr.bin/gzip/gzip.1 b/gnu/usr.bin/gzip/gzip.1 index a7d331f6fb..084dffd5e1 100644 --- a/gnu/usr.bin/gzip/gzip.1 +++ b/gnu/usr.bin/gzip/gzip.1 @@ -5,7 +5,7 @@ gzip, gunzip, zcat \- compress or expand files .SH SYNOPSIS .ll +8 .B gzip -.RB [ " \-acdfhLrtvV19 " ] +.RB [ " \-acdfhlLnNrtvV19 " ] .RB [ \-S\ suffix ] [ .I "name \&..." @@ -13,14 +13,14 @@ gzip, gunzip, zcat \- compress or expand files .ll -8 .br .B gunzip -.RB [ " \-acfhLrtvV " ] +.RB [ " \-acfhlLnNrtvV " ] .RB [ \-S\ suffix ] [ .I "name \&..." ] .br .B zcat -.RB [ " \-hLV " ] +.RB [ " \-fhLV " ] [ .I "name \&..." ] @@ -35,21 +35,41 @@ while keeping the same ownership modes, access and modification times. .B "\-gz" for VMS, .B "z" -for MSDOS, OS/2 and Atari.) -If no files are specified, the standard input is compressed to the -standard output. If the new file name is too long, -.I gzip -truncates it and keeps the original file name in the compressed file. +for MSDOS, OS/2 FAT, Windows NT FAT and Atari.) +If no files are specified, or if a file name is "-", the standard input is +compressed to the standard output. .I Gzip will only attempt to compress regular files. In particular, it will ignore symbolic links. .PP +If the compressed file name is too long for its file system, +.I gzip +truncates it. +.I Gzip +attempts to truncate only the parts of the file name longer than 3 characters. +(A part is delimited by dots.) If the name consists of small parts only, +the longest parts are truncated. For example, if file names are limited +to 14 characters, gzip.msdos.exe is compressed to gzi.msd.exe.gz. +Names are not truncated on systems which do not have a limit on file name +length. +.PP +By default, +.I gzip +keeps the original file name and timestamp in the compressed file. These +are used when decompressing the file with the +.B \-N +option. This is useful when the compressed file name was truncated or +when the time stamp was not preserved after a file transfer. +.PP Compressed files can be restored to their original form using .I gzip -d or .I gunzip or .I zcat. +If the original name saved in the compressed file is not suitable for its +file system, a new name is constructed from the original one to make it +legal. .PP .I gunzip takes a list of files on its command line and replaces each @@ -66,10 +86,17 @@ as shorthands for and .B "\&.tar.Z" respectively. +When compressing, +.I gzip +uses the +.B "\&.tgz" +extension if necessary instead of truncating a file with a +.B "\&.tar" +extension. .PP .I gunzip can currently decompress files created by -.I gzip, zip, compress +.I gzip, zip, compress, compress -H or .I pack. The detection of the input format is automatic. When using @@ -77,7 +104,7 @@ the first two formats, .I gunzip checks a 32 bit CRC. For .I pack, gunzip -checks the uncompressed length. The +checks the uncompressed length. The standard .I compress format was not designed to allow consistency checks. However .I gunzip @@ -88,6 +115,8 @@ correct simply because the standard does not complain. This generally means that the standard .I uncompress does not check its input, and happily generates garbage output. +The SCO compress -H format (lzh compression method) does not include a CRC +but also allows some consistency checks. .PP Files created by .I zip @@ -150,7 +179,7 @@ Ascii text mode: convert end-of-lines using local conventions. This option is supported only on some non-Unix systems. For MSDOS, CR LF is converted to LF when compressing, and LF is converted to CR LF when decompressing. .TP -.B \-c --stdout +.B \-c --stdout --to-stdout Write output on standard output; keep original files unchanged. If there are several input files, the output consists of a sequence of independently compressed members. To obtain better compression, @@ -162,7 +191,15 @@ Decompress. .B \-f --force Force compression or decompression even if the file has multiple links or the corresponding file already exists, or if the compressed data -is read from or written to a terminal. If +is read from or written to a terminal. If the input data is not in +a format recognized by +.I gzip, +and if the option --stdout is also given, copy the input data without change +to the standard ouput: let +.I zcat +behave as +.I cat. +If .B \-f is not given, and when not running in the background, @@ -172,15 +209,64 @@ prompts to verify whether an existing file should be overwritten. .B \-h --help Display a help screen and quit. .TP +.B \-l --list +For each compressed file, list the following fields: + + compressed size: size of the compressed file + uncompressed size: size of the uncompressed file + ratio: compression ratio (0.0% if unknown) + uncompressed_name: name of the uncompressed file + +The uncompressed size is given as -1 for files not in gzip format, +such as compressed .Z files. To get the uncompressed size for such a file, +you can use: + + zcat file.Z | wc -c + +In combination with the --verbose option, the following fields are also +displayed: + + method: compression method + crc: the 32-bit CRC of the uncompressed data + date & time: time stamp for the uncompressed file + +The compression methods currently supported are deflate, compress, lzh +(SCO compress -H) and pack. The crc is given as ffffffff for a file +not in gzip format. + +With --name, the uncompressed name, date and time are +those stored within the compress file if present. + +With --verbose, the size totals and compression ratio for all files +is also displayed, unless some sizes are unknown. With --quiet, +the title and totals lines are not displayed. +.TP .B \-L --license Display the .I gzip -license. +license and quit. +.TP +.B \-n --no-name +When compressing, do not save the original file name and time stamp by +default. (The original name is always saved if the name had to be +truncated.) When decompressing, do not restore the original file name +if present (remove only the +.I gzip +suffix from the compressed file name) and do not restore the original +time stamp if present (copy it from the compressed file). This option +is the default when decompressing. +.TP +.B \-N --name +When compressing, always save the original file name and time stamp; this +is the default. When decompressing, restore the original file name and +time stamp if present. This option is useful on systems which have +a limit on file name length or when the time stamp has been lost after +a file transfer. .TP .B \-q --quiet Suppress all warnings. .TP -.B \-r --recurse +.B \-r --recursive Travel the directory structure recursively. If any of the file names specified on the command line are directories, .I gzip @@ -189,10 +275,15 @@ will descend into the directory and compress all the files it finds there .I gunzip ). .TP -.B \-S .z --suffix .z -Use suffix .z instead of .gz. Any suffix can be given, but suffixes +.B \-S .suf --suffix .suf +Use suffix .suf instead of .gz. Any suffix can be given, but suffixes other than .z and .gz should be avoided to avoid confusion when files -are transferred to other systems. Previous versions of gzip used +are transferred to other systems. A null suffix forces gunzip to try +decompression on all given files regardless of suffix, as in: + + gunzip -S "" * (*.* for MSDOS) + +Previous versions of gzip used the .z suffix. This was changed to avoid a conflict with .IR pack "(1)". .TP @@ -200,7 +291,8 @@ the .z suffix. This was changed to avoid a conflict with Test. Check the compressed file integrity. .TP .B \-v --verbose -Verbose. Display the name and percentage reduction for each file compressed. +Verbose. Display the name and percentage reduction for each file compressed +or decompressed. .TP .B \-V --version Version. Display the version number and compilation options then quit. @@ -217,9 +309,10 @@ and .B \-9 or .B \-\-best -indicates the slowest compression method (optimal compression). +indicates the slowest compression method (best compression). The default compression level is -.BR \-5. +.BR \-6 +(that is, biased towards high compression at expense of speed). .SH "ADVANCED USAGE" Multiple compressed files can be concatenated. In this case, .I gunzip @@ -247,7 +340,19 @@ compresses better than If you want to recompress concatenated files to get better compression, do: - zcat old.gz | gzip > new.gz + gzip -cd old.gz | gzip > new.gz + +If a compressed file consists of several members, the uncompressed +size and CRC reported by the --list option applies to the last member +only. If you need the uncompressed size for all members, you can use: + + gzip -cd file.gz | wc -c + +If you wish to create a single archive file with multiple members so +that members can later be extracted independently, use an archiver +such as tar or zip. GNU tar supports the -z option to invoke gzip +transparently. gzip is designed as a complement to tar, not as a +replacement. .SH "ENVIRONMENT" The environment variable .B GZIP @@ -255,9 +360,9 @@ can hold a set of default options for .I gzip. These options are interpreted first and can be overwritten by explicit command line parameters. For example: - for sh: GZIP="-8 -v"; export GZIP - for csh: setenv GZIP "-8 -v" - for MSDOS: set GZIP=-8 -v + for sh: GZIP="-8v --name"; export GZIP + for csh: setenv GZIP "-8v --name" + for MSDOS: set GZIP=-8v --name On Vax/VMS, the name of the environment variable is GZIP_OPT, to avoid a conflict with the symbol set for invocation of the program. @@ -268,7 +373,7 @@ pack(1), compact(1) Exit status is normally 0; if an error occurs, exit status is 1. If a warning occurs, exit status is 2. .PP -Usage: gzip [-cdfhLrtvV19] [-S suffix] [file ...] +Usage: gzip [-cdfhlLnNrtvV19] [-S suffix] [file ...] .in +8 Invalid options were specified on the command line. .in -8 @@ -322,7 +427,9 @@ been corrupted. .in +8 Percentage of the input saved by compression. (Relevant only for -.BR \-v \.) +.BR \-v +and +.BR \-l \.) .in -8 -- not a regular file or directory: ignored .in +8 @@ -352,9 +459,20 @@ and emits a warning by default. You have to use the --quiet option to suppress the warning. This option can be set in the .B GZIP environment variable as in: - for sh: GZIP="-q" tar xfz /dev/rmt/datn - for csh: (setenv GZIP "-q"; tar xfz /dev/rmt/datn) + for sh: GZIP="-q" tar -xfz --block-compress /dev/rst0 + for csh: (setenv GZIP -q; tar -xfz --block-compr /dev/rst0 + +In the above example, gzip is invoked implicitly by the -z option of +GNU tar. Make sure that the same block size (-b option of tar) is used +for reading and writing compressed data on tapes. (This example +assumes you are using the GNU version of tar.) +.SH BUGS +The --list option reports incorrect sizes if they exceed 2 gigabytes. +The --list option reports sizes as -1 and crc as ffffffff if the +compressed file is on a non seekable media. -In the above example, gzip is invoked implicitly by the -z option -of GNU tar. Make sure that the same block size (-b option of -tar) is used for reading and writing compressed data on tapes. +In some rare cases, the --best option gives worse compression than +the default compression level (-6). On some highly redundant files, +.I compress +compresses better than +.I gzip. diff --git a/gnu/usr.bin/gzip/gzip.c b/gnu/usr.bin/gzip/gzip.c index 2bc1c5fc48..09fe9a16f2 100644 --- a/gnu/usr.bin/gzip/gzip.c +++ b/gnu/usr.bin/gzip/gzip.c @@ -31,19 +31,23 @@ static char *license_msg[] = { * Outputs: * file.gz: compressed file with same mode, owner, and utimes * or stdout with -c option or if stdin used as input. - * If the OS does not support file names with multiple dots (MSDOS, VMS) or - * if the output file name had to be truncated, the original name is kept + * If the output file name had to be truncated, the original name is kept * in the compressed file. * On MSDOS, file.tmp -> file.tmz. On VMS, file.tmp -> file.tmp-gz. * + * Using gz on MSDOS would create too many file name conflicts. For + * example, foo.txt -> foo.tgz (.tgz must be reserved as shorthand for + * tar.gz). Similarly, foo.dir and foo.doc would both be mapped to foo.dgz. + * I also considered 12345678.txt -> 12345txt.gz but this truncates the name + * too heavily. There is no ideal solution given the MSDOS 8+3 limitation. + * * For the meaning of all compilation flags, see comments in Makefile.in. */ -#ifndef lint -static char rcsid[] = "$Id: gzip.c,v 0.19 1993/06/01 14:21:46 jloup Exp $"; +#ifdef RCSID +static char rcsid[] = "$Id: gzip.c,v 0.24 1993/06/24 10:52:07 jloup Exp $"; #endif -#include #include #include #include @@ -58,6 +62,12 @@ static char rcsid[] = "$Id: gzip.c,v 0.19 1993/06/01 14:21:46 jloup Exp $"; /* configuration */ +#ifdef NO_TIME_H +# include +#else +# include +#endif + #ifndef NO_FCNTL_H # include #endif @@ -157,8 +167,21 @@ typedef RETSIGTYPE (*sig_type) OF((int)); # define MAX_PATH_LEN 1024 /* max pathname length */ #endif -#define MAX_HEADER_LEN 16 -/* max length of a compressed file header, fixed part only */ +#ifndef SEEK_END +# define SEEK_END 2 +#endif + +#ifdef NO_OFF_T + typedef long off_t; + off_t lseek OF((int fd, off_t offset, int whence)); +#endif + +/* Separator for file name parts (see shorten_name()) */ +#ifdef NO_MULTIPLE_DOTS +# define PART_SEP "-" +#else +# define PART_SEP "." +#endif /* global buffers */ @@ -179,7 +202,10 @@ int ascii = 0; /* convert end-of-lines to local OS conventions */ int to_stdout = 0; /* output to stdout (-c) */ int decompress = 0; /* decompress (-d) */ int force = 0; /* don't ask questions, compress links (-f) */ +int no_name = -1; /* don't save or restore the original file name */ +int no_time = -1; /* don't save or restore the original file time */ int recursive = 0; /* recurse through directories (-r) */ +int list = 0; /* list the file contents (-l) */ int verbose = 0; /* be verbose (-v) */ int quiet = 0; /* be very quiet (-q) */ int do_lzw = 0; /* generate output compatible with old compress (-Z) */ @@ -188,12 +214,12 @@ int foreground; /* set if program run in foreground */ char *progname; /* program name */ int maxbits = BITS; /* max bits per code for LZW */ int method = DEFLATED;/* compression method */ -int level = 5; /* compression level */ +int level = 6; /* compression level */ int exit_code = OK; /* program exit code */ int save_orig_name; /* set if original name must be saved */ int last_member; /* set for .zip and .Z files */ int part_nb; /* number of parts in .gz file */ -ulg time_stamp; /* original time stamp (modification time) */ +long time_stamp; /* original time stamp (modification time) */ long ifile_size; /* input file size, -1 for devices (debug only) */ char *env; /* contents of GZIP env variable */ char **args = NULL; /* argv pointer if GZIP env variable defined */ @@ -202,6 +228,8 @@ int z_len; /* strlen(z_suffix) */ long bytes_in; /* number of input bytes */ long bytes_out; /* number of output bytes */ +long total_in = 0; /* input bytes for all files */ +long total_out = 0; /* output bytes for all files */ char ifname[MAX_PATH_LEN]; /* input file name */ char ofname[MAX_PATH_LEN]; /* output file name */ int remove_ofname = 0; /* remove output file on error */ @@ -224,13 +252,16 @@ struct option longopts[] = {"force", 0, 0, 'f'}, /* force overwrite of output file */ {"help", 0, 0, 'h'}, /* give help */ /* {"pkzip", 0, 0, 'k'}, force output in pkzip format */ - /* {"list", 0, 0, 'l'}, list .gz file contents */ + {"list", 0, 0, 'l'}, /* list .gz file contents */ {"license", 0, 0, 'L'}, /* display software license */ + {"no-name", 0, 0, 'n'}, /* don't save or restore original name & time */ + {"name", 0, 0, 'N'}, /* save or restore original name & time */ {"quiet", 0, 0, 'q'}, /* quiet mode */ {"silent", 0, 0, 'q'}, /* quiet mode */ - {"recurse", 0, 0, 'r'}, /* recurse through directories */ + {"recursive", 0, 0, 'r'}, /* recurse through directories */ {"suffix", 1, 0, 'S'}, /* use given suffix instead of .gz */ {"test", 0, 0, 't'}, /* test compressed file integrity */ + {"no-time", 0, 0, 'T'}, /* don't save or restore the time stamp */ {"verbose", 0, 0, 'v'}, /* verbose mode */ {"version", 0, 0, 'V'}, /* display version number */ {"fast", 0, 0, '1'}, /* compress faster */ @@ -255,22 +286,28 @@ local int get_istat OF((char *iname, struct stat *sbuf)); local int make_ofname OF((void)); local int same_file OF((struct stat *stat1, struct stat *stat2)); local int name_too_long OF((char *name, struct stat *statb)); +local void shorten_name OF((char *name)); local int get_method OF((int in)); +local void do_list OF((int ifd, int method)); local int check_ofname OF((void)); -local void reset_times OF((char *name, struct stat *statb)); local void copy_stat OF((struct stat *ifstat)); -local void treat_dir OF((char *dir)); local void do_exit OF((int exitcode)); int main OF((int argc, char **argv)); - int (*work) OF((int infile, int outfile)) = zip; /* function to call */ +#ifndef NO_DIR +local void treat_dir OF((char *dir)); +#endif +#ifndef NO_UTIME +local void reset_times OF((char *name, struct stat *statb)); +#endif + #define strequ(s1, s2) (strcmp((s1),(s2)) == 0) /* ======================================================================== */ local void usage() { - fprintf(stderr, "usage: %s [-%scdfhL%stvV19] [-S suffix] [file ...]\n", + fprintf(stderr, "usage: %s [-%scdfhlLnN%stvV19] [-S suffix] [file ...]\n", progname, #if O_BINARY "a", @@ -298,17 +335,19 @@ local void help() " -f --force force overwrite of output file and compress links", " -h --help give this help", /* -k --pkzip force output in pkzip format */ -/* -l --list list .gz file contents */ + " -l --list list compressed file contents", " -L --license display software license", +#ifdef UNDOCUMENTED + " -m --no-time do not save or restore the original modification time", + " -M --time save or restore the original modification time", +#endif + " -n --no-name do not save or restore the original name and time stamp", + " -N --name save or restore the original name and time stamp", " -q --quiet suppress all warnings", #ifndef NO_DIR - " -r --recurse recurse through directories", -#endif -#ifdef MAX_EXT_CHARS - " -S --suffix .gz use suffix .gz instead of .z", -#else - " -S --suffix .z use suffix .z instead of .gz", + " -r --recursive operate recursively on directories", #endif + " -S .suf --suffix .suf use suffix .suf on compressed files", " -t --test test compressed file integrity", " -v --verbose verbose mode", " -V --version display version number", @@ -386,7 +425,7 @@ int main (argc, argv) int argc; char **argv; { - int file_count = 0; /* number of files to precess */ + int file_count; /* number of files to precess */ int proglen; /* length of progname */ int optc; /* current option */ @@ -406,13 +445,17 @@ int main (argc, argv) foreground = signal(SIGINT, SIG_IGN) != SIG_IGN; if (foreground) { - signal (SIGINT, (sig_type)abort_gzip); + (void) signal (SIGINT, (sig_type)abort_gzip); } #ifdef SIGTERM - signal(SIGTERM, (sig_type)abort_gzip); + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) { + (void) signal(SIGTERM, (sig_type)abort_gzip); + } #endif #ifdef SIGHUP - signal(SIGHUP, (sig_type)abort_gzip); + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) { + (void) signal(SIGHUP, (sig_type)abort_gzip); + } #endif #ifndef GNU_STANDARD @@ -435,7 +478,7 @@ int main (argc, argv) strncpy(z_suffix, Z_SUFFIX, sizeof(z_suffix)-1); z_len = strlen(z_suffix); - while ((optc = getopt_long (argc, argv, "ab:cdfhLqrS:tvVZ123456789", + while ((optc = getopt_long (argc, argv, "ab:cdfhH?lLmMnNqrS:tvVZ123456789", longopts, (int *)0)) != EOF) { switch (optc) { case 'a': @@ -451,8 +494,18 @@ int main (argc, argv) force++; break; case 'h': case 'H': case '?': help(); do_exit(OK); break; + case 'l': + list = decompress = to_stdout = 1; break; case 'L': license(); do_exit(OK); break; + case 'm': /* undocumented, may change later */ + no_time = 1; break; + case 'M': /* undocumented, may change later */ + no_time = 0; break; + case 'n': + no_name = no_time = 1; break; + case 'N': + no_name = no_time = 0; break; case 'q': quiet = 1; verbose = 0; break; case 'r': @@ -468,11 +521,6 @@ int main (argc, argv) if (*optarg == '.') optarg++; #endif z_len = strlen(optarg); - if (z_len == 0 || z_len > MAX_SUFFIX) { - fprintf(stderr, "%s: incorrect suffix '%s'\n", - progname, optarg); - do_exit(ERROR); - } strcpy(z_suffix, optarg); break; case 't': @@ -502,6 +550,12 @@ int main (argc, argv) } } /* loop on all arguments */ + /* By default, save name and timestamp on compression but do not + * restore them on decompression. + */ + if (no_time < 0) no_time = decompress; + if (no_name < 0) no_name = decompress; + file_count = argc - optind; #if O_BINARY @@ -511,6 +565,11 @@ int main (argc, argv) progname); } #endif + if ((z_len == 0 && !decompress) || z_len > MAX_SUFFIX) { + fprintf(stderr, "%s: incorrect suffix '%s'\n", + progname, optarg); + do_exit(ERROR); + } if (do_lzw && !decompress) work = lzw; /* Allocate all global buffers (for DYN_ALLOC option) */ @@ -527,7 +586,7 @@ int main (argc, argv) /* And get to work */ if (file_count != 0) { - if (to_stdout && !test && (!decompress || !ascii)) { + if (to_stdout && !test && !list && (!decompress || !ascii)) { SET_BINARY_MODE(fileno(stdout)); } while (optind < argc) { @@ -536,6 +595,9 @@ int main (argc, argv) } else { /* Standard input */ treat_stdin(); } + if (list && !quiet && file_count > 1) { + do_list(-1, -1); /* print totals */ + } do_exit(exit_code); return exit_code; /* just to avoid lint warning */ } @@ -545,7 +607,8 @@ int main (argc, argv) */ local void treat_stdin() { - if (!force && isatty(fileno((FILE *)(decompress ? stdin : stdout)))) { + if (!force && !list && + isatty(fileno((FILE *)(decompress ? stdin : stdout)))) { /* Do not send compressed data to the terminal or read it from * the terminal. We get here when user invoked the program * without parameters, so be helpful. According to the GNU standards: @@ -569,29 +632,26 @@ local void treat_stdin() if (decompress || !ascii) { SET_BINARY_MODE(fileno(stdin)); } - if (!test && (!decompress || !ascii)) { + if (!test && !list && (!decompress || !ascii)) { SET_BINARY_MODE(fileno(stdout)); } strcpy(ifname, "stdin"); strcpy(ofname, "stdout"); /* Get the time stamp on the input file. */ -#ifdef NO_STDIN_FSTAT - time_stamp = 0; /* time unknown */ -#else - if (fstat(fileno(stdin), &istat) != 0) { - error("fstat(stdin)"); + time_stamp = 0; /* time unknown by default */ + +#ifndef NO_STDIN_FSTAT + if (list || !no_time) { + if (fstat(fileno(stdin), &istat) != 0) { + error("fstat(stdin)"); + } +# ifdef NO_PIPE_TIMESTAMP + if (S_ISREG(istat.st_mode)) +# endif + time_stamp = istat.st_mtime; +#endif /* NO_STDIN_FSTAT */ } - /* If you do not wish to save the time stamp when input comes from a pipe, - * compile with -DNO_PIPE_TIMESTAMP. - */ -#ifdef NO_PIPE_TIMESTAMP - if (!S_ISREG(istat.st_mode)) - time_stamp = 0; - else -#endif - time_stamp = istat.st_mtime; -#endif ifile_size = -1L; /* convention for unknown size */ clear_bufs(); /* clear input and output buffers */ @@ -604,6 +664,10 @@ local void treat_stdin() do_exit(exit_code); /* error message already emitted */ } } + if (list) { + do_list(ifd, method); + return; + } /* Actually do the compression/decompression. Loop over zipped members. */ @@ -620,14 +684,17 @@ local void treat_stdin() if (verbose) { if (test) { - fprintf(stderr, " OK"); + fprintf(stderr, " OK\n"); - } else if (decompress) { - display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out); + } else if (!decompress) { + display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in, stderr); + fprintf(stderr, "\n"); +#ifdef DISPLAY_STDIN_RATIO } else { - display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in); + display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out,stderr); + fprintf(stderr, "\n"); +#endif } - fprintf(stderr, "\n"); } } @@ -637,6 +704,14 @@ local void treat_stdin() local void treat_file(iname) char *iname; { + /* Accept "-" as synonym for stdin */ + if (strequ(iname, "-")) { + int cflag = to_stdout; + treat_stdin(); + to_stdout = cflag; + return; + } + /* Check if the input file is present, set ifname and istat: */ if (get_istat(iname, &istat) != OK) return; @@ -648,7 +723,9 @@ local void treat_file(iname) st = istat; treat_dir(iname); /* Warning: ifname is now garbage */ +# ifndef NO_UTIME reset_times (iname, &st); +# endif } else #endif WARN((stderr,"%s: %s is a directory -- ignored\n", progname, ifname)); @@ -668,10 +745,12 @@ local void treat_file(iname) } ifile_size = istat.st_size; - time_stamp = istat.st_mtime; + time_stamp = no_time && !list ? 0 : istat.st_mtime; - /* Generate output file name */ - if (to_stdout) { + /* Generate output file name. For -r and (-t or -l), skip files + * without a valid gzip suffix (check done in make_ofname). + */ + if (to_stdout && !list && !test) { strcpy(ofname, "stdout"); } else if (make_ofname() != OK) { @@ -700,6 +779,11 @@ local void treat_file(iname) return; /* error message already emitted */ } } + if (list) { + do_list(ifd, method); + close(ifd); + return; + } /* If compressing to a file, check if ofname is not ambiguous * because the operating system truncates names. Otherwise, generate @@ -711,11 +795,14 @@ local void treat_file(iname) } else { if (create_outfile() != OK) return; - if (save_orig_name && !verbose && !quiet) { + if (!decompress && save_orig_name && !verbose && !quiet) { fprintf(stderr, "%s: %s compressed to %s\n", progname, ifname, ofname); } } + /* Keep the name even if not truncated except with --no-name: */ + if (!save_orig_name) save_orig_name = !no_name; + if (verbose) { fprintf(stderr, "%s:\t%s", ifname, (int)strlen(ifname) >= 15 ? "" : ((int)strlen(ifname) >= 7 ? "\t" : "\t\t")); @@ -749,9 +836,9 @@ local void treat_file(iname) if (test) { fprintf(stderr, " OK"); } else if (decompress) { - display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out); + display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out,stderr); } else { - display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in); + display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in, stderr); } if (!test && !to_stdout) { fprintf(stderr, " -- replaced with %s", ofname); @@ -768,6 +855,7 @@ local void treat_file(iname) * Create the output file. Return OK or ERROR. * Try several times if necessary to avoid truncating the z_suffix. For * example, do not create a compressed file of name "1234567890123." + * Sets save_orig_name to true if the file name has been truncated. * IN assertions: the input file has already been open (ifd is set) and * ofname has already been updated if there was an original name. * OUT assertions: ifd and ofd are closed in case of error. @@ -775,15 +863,13 @@ local void treat_file(iname) local int create_outfile() { struct stat ostat; /* stat for ofname */ - int len; int flags = O_WRONLY | O_CREAT | O_EXCL | O_BINARY; if (ascii && decompress) { flags &= ~O_BINARY; /* force ascii text mode */ } for (;;) { - len = strlen(ofname); - if (len == 0 || ofname[len] == PATH_SEP) break; + /* Make sure that ofname is not an existing file */ if (check_ofname() != OK) { close(ifd); return ERROR; @@ -799,7 +885,11 @@ local int create_outfile() } /* Check for name truncation on new file (1234567890123.gz) */ +#ifdef NO_FSTAT + if (stat(ofname, &ostat) != 0) { +#else if (fstat(ofd, &ostat) != 0) { +#endif fprintf(stderr, "%s: ", progname); perror(ofname); close(ifd); close(ofd); @@ -814,25 +904,16 @@ local int create_outfile() WARN((stderr, "%s: %s: warning, name truncated\n", progname, ofname)); return OK; - } else { + } + close(ofd); + unlink(ofname); #ifdef NO_MULTIPLE_DOTS - /* Should never happen, see check_ofname() */ - fprintf(stderr, "%s: %s: name too long\n", progname, ofname); - do_exit(ERROR); -#else - close(ofd); - unlink(ofname); - save_orig_name = 1; - strcpy(ofname+strlen(ofname)-z_len-1, z_suffix); - /* 123456789012.gz -> 12345678901.gz */ + /* Should never happen, see check_ofname() */ + fprintf(stderr, "%s: %s: name too long\n", progname, ofname); + do_exit(ERROR); #endif - } /* decompress ? */ - } /* while non null name */ - - close(ifd); - fprintf(stderr, "%s: %s: name too long\n", progname, ofname); - exit_code = ERROR; - return ERROR; + shorten_name(ofname); + } } /* ======================================================================== @@ -861,6 +942,8 @@ local int do_stat(name, sbuf) * also accepted suffixes. For Unix, we do not want to accept any * .??z suffix as indicating a compressed file; some people use .xyz * to denote volume data. + * On systems allowing multiple versions of the same file (such as VMS), + * this function removes any version suffix in the given name. */ local char *get_suffix(name) char *name; @@ -877,19 +960,19 @@ local char *get_suffix(name) if (strequ(z_suffix, "z")) suf++; /* check long suffixes first */ +#ifdef SUFFIX_SEP + /* strip a version number from the file name */ + { + char *v = strrchr(name, SUFFIX_SEP); + if (v != NULL) *v = '\0'; + } +#endif nlen = strlen(name); if (nlen <= MAX_SUFFIX+2) { strcpy(suffix, name); } else { strcpy(suffix, name+nlen-MAX_SUFFIX-2); } -#ifdef SUFFIX_SEP - /* strip a version number from the file name */ - { - char *v = strrchr(suffix, SUFFIX_SEP); - if (v != NULL) *v = '\0', nlen = v - name; - } -#endif strlwr(suffix); slen = strlen(suffix); do { @@ -933,7 +1016,8 @@ local int get_istat(iname, sbuf) exit_code = ERROR; return ERROR; } - /* file.ext doesn't exist, try adding a suffix. + /* file.ext doesn't exist, try adding a suffix (after removing any + * version number for VMS). */ s = get_suffix(ifname); if (s != NULL) { @@ -941,10 +1025,6 @@ local int get_istat(iname, sbuf) exit_code = ERROR; return ERROR; } -#ifdef SUFFIX_SEP - /* strip a version number from the input file name */ - if ((s = strrchr(ifname, SUFFIX_SEP)) != NULL) *s = '\0'; -#endif #ifdef NO_MULTIPLE_DOTS dot = strrchr(ifname, '.'); if (dot == NULL) { @@ -987,20 +1067,28 @@ local int get_istat(iname, sbuf) /* ======================================================================== * Generate ofname given ifname. Return OK, or WARNING if file must be skipped. - * Initializes save_orig_name. - * IN assertion: this function is not called if to_stdout is true. + * Sets save_orig_name to true if the file name has been truncated. */ local int make_ofname() { char *suff; /* ofname z suffix */ strcpy(ofname, ifname); + /* strip a version number if any and get the gzip suffix if present: */ suff = get_suffix(ofname); if (decompress) { if (suff == NULL) { - WARN((stderr,"%s: %s: unknown suffix -- ignored\n", - progname, ifname)); + /* Whith -t or -l, try all files (even without .gz suffix) + * except with -r (behave as with just -dr). + */ + if (!recursive && (list || test)) return OK; + + /* Avoid annoying messages with -r */ + if (verbose || (!recursive && !quiet)) { + WARN((stderr,"%s: %s: unknown suffix -- ignored\n", + progname, ifname)); + } return WARNING; } /* Make a special case for .tgz and .taz: */ @@ -1008,7 +1096,7 @@ local int make_ofname() if (strequ(suff, ".tgz") || strequ(suff, ".taz")) { strcpy(suff, ".tar"); } else { - *suff = '\0'; /* strip z suffix and optional version number */ + *suff = '\0'; /* strip the z suffix */ } /* ofname might be changed later if infile contains an original name */ @@ -1023,16 +1111,15 @@ local int make_ofname() } else { save_orig_name = 0; -#ifdef SUFFIX_SEP - /* strip a version number from the file name */ - if ((suff = strrchr(ofname, SUFFIX_SEP)) != NULL) *suff = '\0'; -#endif - #ifdef NO_MULTIPLE_DOTS suff = strrchr(ofname, '.'); if (suff == NULL) { strcat(ofname, "."); # ifdef MAX_EXT_CHARS + if (strequ(z_suffix, "z")) { + strcat(ofname, "gz"); /* enough room */ + return OK; + } /* On the Atari and some versions of MSDOS, name_too_long() * does not work correctly because of a bug in stat(). So we * must truncate here. @@ -1055,6 +1142,7 @@ local int make_ofname() * original name was given and to_stdout is not set. * Return the compression method, -1 for error, -2 for warning. * Set inptr to the offset of the next byte to be processed. + * Updates time_stamp if there is one and --no-time is not used. * This function may be called repeatedly for an input file consisting * of several contiguous gzip'ed members. * IN assertions: there is at least one remaining compressed member. @@ -1063,13 +1151,21 @@ local int make_ofname() local int get_method(in) int in; /* input file descriptor */ { - uch flags; + uch flags; /* compression flags */ char magic[2]; /* magic header */ + ulg stamp; /* time stamp */ - magic[0] = (char)get_byte(); - magic[1] = (char)get_byte(); - - time_stamp = istat.st_mtime; /* may be modified later for some methods */ + /* If --force and --stdout, zcat == cat, so do not complain about + * premature end of file: use try_byte instead of get_byte. + */ + if (force && to_stdout) { + magic[0] = (char)try_byte(); + magic[1] = (char)try_byte(); + /* If try_byte returned EOF, magic[1] == 0xff */ + } else { + magic[0] = (char)get_byte(); + magic[1] = (char)get_byte(); + } method = -1; /* unknown yet */ part_nb++; /* number of parts in gzip file */ header_bytes = 0; @@ -1079,8 +1175,15 @@ local int get_method(in) if (memcmp(magic, GZIP_MAGIC, 2) == 0 || memcmp(magic, OLD_GZIP_MAGIC, 2) == 0) { - work = unzip; method = (int)get_byte(); + if (method != DEFLATED) { + fprintf(stderr, + "%s: %s: unknown method %d -- get newer version of gzip\n", + progname, ifname, method); + exit_code = ERROR; + return -1; + } + work = unzip; flags = (uch)get_byte(); if ((flags & ENCRYPTED) != 0) { @@ -1104,10 +1207,11 @@ local int get_method(in) exit_code = ERROR; if (force <= 1) return -1; } - time_stamp = (ulg)get_byte(); - time_stamp |= ((ulg)get_byte()) << 8; - time_stamp |= ((ulg)get_byte()) << 16; - time_stamp |= ((ulg)get_byte()) << 24; + stamp = (ulg)get_byte(); + stamp |= ((ulg)get_byte()) << 8; + stamp |= ((ulg)get_byte()) << 16; + stamp |= ((ulg)get_byte()) << 24; + if (stamp != 0 && !no_time) time_stamp = stamp; (void)get_byte(); /* Ignore extra flags for the moment */ (void)get_byte(); /* Ignore OS type for the moment */ @@ -1132,13 +1236,14 @@ local int get_method(in) /* Get original file name if it was truncated */ if ((flags & ORIG_NAME) != 0) { - if (to_stdout || part_nb > 1) { + if (no_name || (to_stdout && !list) || part_nb > 1) { /* Discard the old name */ char c; /* dummy used for NeXTstep 3.0 cc optimizer bug */ - while ((c=get_byte()) != 0) c++; + do {c=get_byte();} while (c != 0); } else { /* Copy the base name. Keep a directory prefix intact. */ - char *p = basename(ofname); + char *p = basename(ofname); + char *base = p; for (;;) { *p = (char)get_char(); if (*p++ == '\0') break; @@ -1146,8 +1251,13 @@ local int get_method(in) error("corrupted input -- file name too large"); } } - } /* to_stdout */ - } /* orig_name */ + /* If necessary, adapt the name to local OS conventions: */ + if (!list) { + MAKE_LEGAL_NAME(base); + if (base) list=0; /* avoid warning about unused variable */ + } + } /* no_name || to_stdout */ + } /* ORIG_NAME */ /* Discard file comment if any */ if ((flags & COMMENT) != 0) { @@ -1171,23 +1281,125 @@ local int get_method(in) } else if (memcmp(magic, PACK_MAGIC, 2) == 0) { work = unpack; method = PACKED; + } else if (memcmp(magic, LZW_MAGIC, 2) == 0) { work = unlzw; method = COMPRESSED; last_member = 1; + + } else if (memcmp(magic, LZH_MAGIC, 2) == 0) { + work = unlzh; + method = LZHED; + last_member = 1; + + } else if (force && to_stdout && !list) { /* pass input unchanged */ + method = STORED; + work = copy; + inptr = 0; + last_member = 1; } if (method >= 0) return method; + if (part_nb == 1) { fprintf(stderr, "\n%s: %s: not in gzip format\n", progname, ifname); exit_code = ERROR; return -1; } else { - WARN((stderr, "\n%s: %s: trailing garbage ignored\n", + WARN((stderr, "\n%s: %s: decompression OK, trailing garbage ignored\n", progname, ifname)); return -2; } } +/* ======================================================================== + * Display the characteristics of the compressed file. + * If the given method is < 0, display the accumulated totals. + * IN assertions: time_stamp, header_bytes and ifile_size are initialized. + */ +local void do_list(ifd, method) + int ifd; /* input file descriptor */ + int method; /* compression method */ +{ + ulg crc; /* original crc */ + static int first_time = 1; + static char* methods[MAX_METHODS] = { + "store", /* 0 */ + "compr", /* 1 */ + "pack ", /* 2 */ + "lzh ", /* 3 */ + "", "", "", "", /* 4 to 7 reserved */ + "defla"}; /* 8 */ + char *date; + + if (first_time && method >= 0) { + first_time = 0; + if (verbose) { + printf("method crc date time "); + } + if (!quiet) { + printf("compressed uncompr. ratio uncompressed_name\n"); + } + } else if (method < 0) { + if (total_in <= 0 || total_out <= 0) return; + if (verbose) { + printf(" %9lu %9lu ", + total_in, total_out); + } else if (!quiet) { + printf("%9ld %9ld ", total_in, total_out); + } + display_ratio(total_out-(total_in-header_bytes), total_out, stdout); + /* header_bytes is not meaningful but used to ensure the same + * ratio if there is a single file. + */ + printf(" (totals)\n"); + return; + } + crc = (ulg)~0; /* unknown */ + bytes_out = -1L; + bytes_in = ifile_size; + +#if RECORD_IO == 0 + if (method == DEFLATED && !last_member) { + /* Get the crc and uncompressed size for gzip'ed (not zip'ed) files. + * If the lseek fails, we could use read() to get to the end, but + * --list is used to get quick results. + * Use "gunzip < foo.gz | wc -c" to get the uncompressed size if + * you are not concerned about speed. + */ + bytes_in = (long)lseek(ifd, (off_t)(-8), SEEK_END); + if (bytes_in != -1L) { + uch buf[8]; + bytes_in += 8L; + if (read(ifd, (char*)buf, sizeof(buf)) != sizeof(buf)) { + read_error(); + } + crc = LG(buf); + bytes_out = LG(buf+4); + } + } +#endif /* RECORD_IO */ + date = ctime((time_t*)&time_stamp) + 4; /* skip the day of the week */ + date[12] = '\0'; /* suppress the 1/100sec and the year */ + if (verbose) { + printf("%5s %08lx %11s ", methods[method], crc, date); + } + printf("%9ld %9ld ", bytes_in, bytes_out); + if (bytes_in == -1L) { + total_in = -1L; + bytes_in = bytes_out = header_bytes = 0; + } else if (total_in >= 0) { + total_in += bytes_in; + } + if (bytes_out == -1L) { + total_out = -1L; + bytes_in = bytes_out = header_bytes = 0; + } else if (total_out >= 0) { + total_out += bytes_out; + } + display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out, stdout); + printf(" %s\n", ofname); +} + /* ======================================================================== * Return true if the two stat structures correspond to the same file. */ @@ -1231,6 +1443,67 @@ local int name_too_long(name, statb) return res; } +/* ======================================================================== + * Shorten the given name by one character, or replace a .tar extension + * with .tgz. Truncate the last part of the name which is longer than + * MIN_PART characters: 1234.678.012.gz -> 123.678.012.gz. If the name + * has only parts shorter than MIN_PART truncate the longest part. + * For decompression, just remove the last character of the name. + * + * IN assertion: for compression, the suffix of the given name is z_suffix. + */ +local void shorten_name(name) + char *name; +{ + int len; /* length of name without z_suffix */ + char *trunc = NULL; /* character to be truncated */ + int plen; /* current part length */ + int min_part = MIN_PART; /* current minimum part length */ + char *p; + + len = strlen(name); + if (decompress) { + if (len <= 1) error("name too short"); + name[len-1] = '\0'; + return; + } + p = get_suffix(name); + if (p == NULL) error("can't recover suffix\n"); + *p = '\0'; + save_orig_name = 1; + + /* compress 1234567890.tar to 1234567890.tgz */ + if (len > 4 && strequ(p-4, ".tar")) { + strcpy(p-4, ".tgz"); + return; + } + /* Try keeping short extensions intact: + * 1234.678.012.gz -> 123.678.012.gz + */ + do { + p = strrchr(name, PATH_SEP); + p = p ? p+1 : name; + while (*p) { + plen = strcspn(p, PART_SEP); + p += plen; + if (plen > min_part) trunc = p-1; + if (*p) p++; + } + } while (trunc == NULL && --min_part != 0); + + if (trunc != NULL) { + do { + trunc[0] = trunc[1]; + } while (*trunc++); + trunc--; + } else { + trunc = strrchr(name, PART_SEP[0]); + if (trunc == NULL) error("internal error in shorten_name"); + if (trunc[1] == '\0') trunc--; /* force truncation */ + } + strcpy(trunc, z_suffix); +} + /* ======================================================================== * If compressing to a file, check if ofname is not ambiguous * because the operating system truncates names. Otherwise, generate @@ -1247,29 +1520,40 @@ local int name_too_long(name, statb) */ local int check_ofname() { - int s = strlen(ofname); struct stat ostat; /* stat for ofname */ - if (stat(ofname, &ostat) != 0) return 0; - - /* Check for name truncation on existing file: */ -#ifdef NO_MULTIPLE_DOTS - if (!decompress && name_too_long(ofname, &ostat)) { +#ifdef ENAMETOOLONG + /* Check for strictly conforming Posix systems (which return ENAMETOOLONG + * instead of silently truncating filenames). + */ + errno = 0; + while (stat(ofname, &ostat) != 0) { + if (errno != ENAMETOOLONG) return 0; /* ofname does not exist */ + shorten_name(ofname); + } #else - if (!decompress && s > 8 && name_too_long(ofname, &ostat)) { + if (stat(ofname, &ostat) != 0) return 0; #endif - save_orig_name = 1; - strcpy(ofname+s-z_len-1, z_suffix); /* f.ext.gz -> f.ex.gz */ - + /* Check for name truncation on existing file. Do this even on systems + * defining ENAMETOOLONG, because on most systems the strict Posix + * behavior is disabled by default (silent name truncation allowed). + */ + if (!decompress && name_too_long(ofname, &ostat)) { + shorten_name(ofname); if (stat(ofname, &ostat) != 0) return 0; - } /* !decompress && name_too_long */ + } /* Check that the input and output files are different (could be * the same by name truncation or links). */ if (same_file(&istat, &ostat)) { - fprintf(stderr, "%s: %s and %s are the same file\n", - progname, ifname, ofname); + if (strequ(ifname, ofname)) { + fprintf(stderr, "%s: %s: cannot %scompress onto itself\n", + progname, ifname, decompress ? "de" : ""); + } else { + fprintf(stderr, "%s: %s and %s are the same file\n", + progname, ifname, ofname); + } exit_code = ERROR; return ERROR; } @@ -1300,6 +1584,7 @@ local int check_ofname() } +#ifndef NO_UTIME /* ======================================================================== * Set the access and modification times from the given stat buffer. */ @@ -1307,7 +1592,6 @@ local void reset_times (name, statb) char *name; struct stat *statb; { -#ifndef NO_UTIME struct utimbuf timep; /* Copy the time stamp */ @@ -1319,10 +1603,8 @@ local void reset_times (name, statb) WARN((stderr, "%s: ", progname)); if (!quiet) perror(ofname); } -#else - name = name; statb = statb; /* avoid warnings */ -#endif } +#endif /* ======================================================================== @@ -1335,7 +1617,7 @@ local void copy_stat(ifstat) #ifndef NO_UTIME if (decompress && time_stamp != 0 && ifstat->st_mtime != time_stamp) { ifstat->st_mtime = time_stamp; - if (verbose) { + if (verbose > 1) { fprintf(stderr, "%s: time stamp restored\n", ofname); } } @@ -1430,6 +1712,10 @@ local void treat_dir(dir) local void do_exit(exitcode) int exitcode; { + static int in_exit = 0; + + if (in_exit) exit(exitcode); + in_exit = 1; if (env != NULL) free(env), env = NULL; if (args != NULL) free((char*)args), args = NULL; FREE(inbuf); diff --git a/gnu/usr.bin/gzip/gzip.h b/gnu/usr.bin/gzip/gzip.h index a4d6376260..88b0710170 100644 --- a/gnu/usr.bin/gzip/gzip.h +++ b/gnu/usr.bin/gzip/gzip.h @@ -16,7 +16,10 @@ typedef char *voidp; #endif -/* I don't like nested includes, but the string functions are used too often */ +/* I don't like nested includes, but the string and io functions are used + * too often + */ +#include #if !defined(NO_STRING_H) || defined(STDC_HEADERS) # include # if !defined(STDC_HEADERS) && !defined(NO_MEMORY_H) && !defined(__GNUC__) @@ -48,11 +51,13 @@ typedef unsigned long ulg; #define WARNING 2 /* Compression methods (see algorithm.doc) */ -#define STORED 0 -#define COMPRESSED 1 -#define PACKED 2 -/* methods 3 to 7 reserved */ -#define DEFLATED 8 +#define STORED 0 +#define COMPRESSED 1 +#define PACKED 2 +#define LZHED 3 +/* methods 4 to 7 reserved */ +#define DEFLATED 8 +#define MAX_METHODS 9 extern int method; /* compression method */ /* To save memory for 16 bit systems, some arrays are overlaid between @@ -60,7 +65,8 @@ extern int method; /* compression method */ * deflate: prev+head window d_buf l_buf outbuf * unlzw: tab_prefix tab_suffix stack inbuf outbuf * inflate: window inbuf - * unpack: window inbuf + * unpack: window inbuf prefix_len + * unlzh: left+right window c_table inbuf c_len * For compression, input is done in window[]. For decompression, output * is done in window except for unlzw. */ @@ -139,17 +145,18 @@ extern char ifname[]; /* input file name or "stdin" */ extern char ofname[]; /* output file name or "stdout" */ extern char *progname; /* program name */ -extern ulg time_stamp; /* original time stamp (modification time) */ +extern long time_stamp; /* original time stamp (modification time) */ extern long ifile_size; /* input file size, -1 for devices (debug only) */ typedef int file_t; /* Do not use stdio */ #define NO_FILE (-1) /* in memory compression */ +#define PACK_MAGIC "\037\036" /* Magic header for packed files */ #define GZIP_MAGIC "\037\213" /* Magic header for gzip files, 1F 8B */ #define OLD_GZIP_MAGIC "\037\236" /* Magic header for gzip 0.5 = freeze 1.x */ +#define LZH_MAGIC "\037\240" /* Magic header for SCO LZH Compress files*/ #define PKZIP_MAGIC "\120\113\003\004" /* Magic header for pkzip files */ -#define PACK_MAGIC "\037\036" /* Magic header for packed files */ /* gzip flag byte */ #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ @@ -192,7 +199,8 @@ extern int test; /* check .z file integrity */ extern int to_stdout; /* output to stdout (-c) */ extern int save_orig_name; /* set if original name must be saved */ -#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) +#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf(0)) +#define try_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf(1)) /* put_byte is used for the compressed output, put_ubyte for the * uncompressed output. However unlzw() uses window for its @@ -261,6 +269,9 @@ extern int check_zipfile OF((int in)); /* in unpack.c */ extern int unpack OF((int in, int out)); + /* in unlzh.c */ +extern int unlzh OF((int in, int out)); + /* in gzip.c */ RETSIGTYPE abort_gzip OF((void)); @@ -282,20 +293,22 @@ void copy_block OF((char *buf, unsigned len, int header)); extern int (*read_buf) OF((char *buf, unsigned size)); /* in util.c: */ +extern int copy OF((int in, int out)); extern ulg updcrc OF((uch *s, unsigned n)); extern void clear_bufs OF((void)); -extern int fill_inbuf OF((void)); +extern int fill_inbuf OF((int eof_ok)); extern void flush_outbuf OF((void)); extern void flush_window OF((void)); extern void write_buf OF((int fd, voidp buf, unsigned cnt)); extern char *strlwr OF((char *s)); extern char *basename OF((char *fname)); +extern void make_simple_name OF((char *name)); extern char *add_envopt OF((int *argcp, char ***argvp, char *env)); extern void error OF((char *m)); extern void warn OF((char *a, char *b)); extern void read_error OF((void)); extern void write_error OF((void)); -extern void display_ratio OF((long num, long den)); +extern void display_ratio OF((long num, long den, FILE *file)); extern voidp xmalloc OF((unsigned int size)); /* in inflate.c */ diff --git a/gnu/usr.bin/gzip/inflate.c b/gnu/usr.bin/gzip/inflate.c index aa31f9efd2..ede365622b 100644 --- a/gnu/usr.bin/gzip/inflate.c +++ b/gnu/usr.bin/gzip/inflate.c @@ -96,11 +96,10 @@ the two sets of lengths. */ -#ifndef lint -static char rcsid[] = "$Id: inflate.c,v 0.13 1993/04/26 14:18:22 jloup Exp $"; +#ifdef RCSID +static char rcsid[] = "$Id: inflate.c,v 0.14 1993/06/10 13:27:04 jloup Exp $"; #endif -#include #include #include "tailor.h" @@ -310,7 +309,8 @@ int *m; /* maximum lookup bits, returns actual */ do { Tracecv(*p, (stderr, (n-i >= ' ' && n-i <= '~' ? "%c %d\n" : "0x%x %d\n"), n-i, *p)); - c[*p++]++; /* assume all entries <= BMAX */ + c[*p]++; /* assume all entries <= BMAX */ + p++; /* Can't combine with above line (Solaris bug) */ } while (--i); if (c[0] == n) /* null input--all zero length codes */ { diff --git a/gnu/usr.bin/gzip/lzw.c b/gnu/usr.bin/gzip/lzw.c index cd09047047..12bf5c611d 100644 --- a/gnu/usr.bin/gzip/lzw.c +++ b/gnu/usr.bin/gzip/lzw.c @@ -2,16 +2,14 @@ * This is a dummy version avoiding patent problems. */ -#ifndef lint -static char rcsid[] = "$Id: lzw.c,v 0.8 1993/04/25 08:09:58 jloup Exp $"; +#ifdef RCSID +static char rcsid[] = "$Id: lzw.c,v 0.9 1993/06/10 13:27:31 jloup Exp $"; #endif #include "tailor.h" #include "gzip.h" #include "lzw.h" -#include - static int msg_done = 0; /* Compress in to out with lzw method. */ @@ -21,7 +19,8 @@ int lzw(in, out) if (msg_done) return ERROR; msg_done = 1; fprintf(stderr,"output in compress .Z format not supported\n"); - in++, out++; /* avoid warnings on unused variables */ - exit_code = ERROR; + if (in != out) { /* avoid warnings on unused variables */ + exit_code = ERROR; + } return ERROR; } diff --git a/gnu/usr.bin/gzip/match.S b/gnu/usr.bin/gzip/match.S index cd3176bbab..4a3d681c3d 100644 --- a/gnu/usr.bin/gzip/match.S +++ b/gnu/usr.bin/gzip/match.S @@ -9,7 +9,7 @@ * Kristoffer Eriksson */ -/* $Id: match.S,v 0.13 1993/05/24 12:03:03 jloup Exp $ */ +/* $Id: match.S,v 0.14 1993/06/11 18:33:24 jloup Exp $ */ /* Preprocess with -DNO_UNDERLINE if your C compiler does not prefix * external symbols with an underline character '_'. @@ -44,11 +44,11 @@ .file "match.S" -#define MAX_MATCH 258 -#define MAX_MATCH2 128 /* MAX_MATCH/2-1 */ +#define MAX_MATCH 258 +#define MAX_MATCH2 $128 /* MAX_MATCH/2-1 */ #define MIN_MATCH 3 -#define WSIZE 32768 -#define MAX_DIST WSIZE - MAX_MATCH - MIN_MATCH - 1 +#define WSIZE $32768 +#define MAX_DIST WSIZE - MAX_MATCH - MIN_MATCH - 1 .globl _match_init .globl _longest_match @@ -87,11 +87,11 @@ _longest_match: /* int longest_match(cur_match) */ mov _max_chain_length,%ebp /* chain_length = max_chain_length */ mov _strstart,%edi mov %edi,%edx - sub $ MAX_DIST,%edx /* limit = strstart-MAX_DIST */ + sub MAX_DIST,%edx /* limit = strstart-MAX_DIST */ jae limit_ok sub %edx,%edx /* limit = NIL */ limit_ok: - add $ _window+2,%edi /* edi = offset(window+strstart+2) */ + add $2+_window,%edi /* edi = offset(window+strstart+2) */ mov _prev_length,%ebx /* best_len = prev_length */ movw -3(%ebx,%edi),%ax /* ax = scan[best_len-1..best_len] */ movw -2(%edi),%cx /* cx = scan[0..1] */ @@ -110,7 +110,7 @@ short_loop: * at this point, di == scan+2, si == cur_match, * ax = scan[best_len-1..best_len] and cx = scan[0..1] */ - and $ WSIZE-1, %esi + and WSIZE-1, %esi movw _prev(%esi,%esi),%si /* cur_match = prev[cur_match] */ /* top word of esi is still 0 */ cmp %edx,%esi /* cur_match <= limit ? */ @@ -125,7 +125,7 @@ do_scan: lea _window+2(%esi),%esi /* si = match */ mov %edi,%eax /* ax = scan+2 */ - mov $ MAX_MATCH2,%ecx /* scan for at most MAX_MATCH bytes */ + mov MAX_MATCH2,%ecx /* scan for at most MAX_MATCH bytes */ rep; cmpsw /* loop until mismatch */ je maxmatch /* match of length MAX_MATCH? */ mismatch: @@ -134,7 +134,7 @@ mismatch: xchg %edi,%eax /* edi = scan+2, eax = end of scan */ sub %edi,%eax /* eax = len */ sub %eax,%esi /* esi = cur_match + 2 + offset(window) */ - sub $ _window+2,%esi /* esi = cur_match */ + sub $2+_window,%esi /* esi = cur_match */ subb $1,%cl /* set carry if cl == 0 (cannot use DEC) */ adc $0,%eax /* eax = carry ? len+1 : len */ cmp %ebx,%eax /* len > best_len ? */ diff --git a/gnu/usr.bin/gzip/revision.h b/gnu/usr.bin/gzip/revision.h index 5a4e1e6f75..f99b65f9bb 100644 --- a/gnu/usr.bin/gzip/revision.h +++ b/gnu/usr.bin/gzip/revision.h @@ -4,13 +4,13 @@ * terms of the GNU General Public License, see the file COPYING. */ -#define VERSION "1.1.1" +#define VERSION "1.2.4" #define PATCHLEVEL 0 -#define REVDATE "1 Jun 93" +#define REVDATE "18 Aug 93" /* This version does not support compression into old compress format: */ #ifdef LZW # undef LZW #endif -/* $Id: revision.h,v 0.20 1993/06/01 14:03:17 jloup Exp $ */ +/* $Id: revision.h,v 0.25 1993/06/24 08:29:52 jloup Exp $ */ diff --git a/gnu/usr.bin/gzip/tailor.h b/gnu/usr.bin/gzip/tailor.h index 915760a108..a97d8bee95 100644 --- a/gnu/usr.bin/gzip/tailor.h +++ b/gnu/usr.bin/gzip/tailor.h @@ -8,12 +8,16 @@ * The target dependent functions should be defined in tailor.c. */ -/* $Id: tailor.h,v 0.16 1993/06/01 12:46:03 jloup Exp $ */ +/* $Id: tailor.h,v 0.18 1993/06/14 19:32:20 jloup Exp $ */ #if defined(__MSDOS__) && !defined(MSDOS) # define MSDOS #endif +#if defined(__OS2__) && !defined(OS2) +# define OS2 +#endif + #if defined(OS2) && defined(MSDOS) /* MS C under OS/2 */ # undef MSDOS #endif @@ -30,7 +34,12 @@ # else # define MAXSEG_64K # ifdef __TURBOC__ -# define NO_UTIME +# define NO_OFF_T +# ifdef __BORLANDC__ +# define DIRENT +# else +# define NO_UTIME +# endif # else /* MSC */ # define HAVE_SYS_UTIME_H # define NO_UTIME_H @@ -65,26 +74,59 @@ # define NO_MULTIPLE_DOTS # define MAX_EXT_CHARS 3 # define Z_SUFFIX "z" +# define casemap(c) tolow(c) # endif # define NO_CHOWN # define PROTO # define STDC_HEADERS -# define HAVE_SYS_UTIME_H -# define NO_UTIME_H -# define casemap(c) tolow(c) # include # define OS_CODE 0x06 # define SET_BINARY_MODE(fd) setmode(fd, O_BINARY) # ifdef _MSC_VER +# define HAVE_SYS_UTIME_H +# define NO_UTIME_H # define MAXSEG_64K # undef near # define near _near # endif # ifdef __EMX__ +# define HAVE_SYS_UTIME_H +# define NO_UTIME_H # define DIRENT # define EXPAND(argc,argv) \ {_response(&argc, &argv); _wildcard(&argc, &argv);} # endif +# ifdef __BORLANDC__ +# define DIRENT +# endif +# ifdef __ZTC__ +# define NO_DIR +# define NO_UTIME_H +# include +# define EXPAND(argc,argv) \ + {response_expand(&argc, &argv);} +# endif +#endif + +#ifdef WIN32 /* Windows NT */ +# define HAVE_SYS_UTIME_H +# define NO_UTIME_H +# define PATH_SEP2 '\\' +# define PATH_SEP3 ':' +# define MAX_PATH_LEN 260 +# define NO_CHOWN +# define PROTO +# define STDC_HEADERS +# define SET_BINARY_MODE(fd) setmode(fd, O_BINARY) +# include +# include +# ifdef NTFAT +# define NO_MULTIPLE_DOTS +# define MAX_EXT_CHARS 3 +# define Z_SUFFIX "z" +# define casemap(c) tolow(c) /* Force file names to lower case */ +# endif +# define OS_CODE 0x0b #endif #ifdef MSDOS @@ -132,13 +174,11 @@ #ifdef AMIGA # define PATH_SEP2 ':' # define STDC_HEADERS -# define casemap(c) tolow(c) /* Force file names to lower case */ # define OS_CODE 0x01 # define ASMV # ifdef __GNUC__ # define DIRENT # define HAVE_UNISTD_H -# define RETSIGTYPE int # else /* SASC */ # define NO_STDIN_FSTAT # define SYSDIR @@ -149,6 +189,7 @@ # define direct dirent extern void _expand_args(int *argc, char ***argv); # define EXPAND(argc,argv) _expand_args(&argc,&argv); +# undef O_BINARY /* disable useless --ascii option */ # endif #endif @@ -161,11 +202,15 @@ # define ASMV # define OS_CODE 0x05 # ifdef TOSFS -# define NO_SYMLINK +# define PATH_SEP2 '\\' +# define PATH_SEP3 ':' +# define MAX_PATH_LEN 128 # define NO_MULTIPLE_DOTS # define MAX_EXT_CHARS 3 # define Z_SUFFIX "z" # define NO_CHOWN +# define casemap(c) tolow(c) /* Force file names to lower case */ +# define NO_SYMLINK # endif #endif @@ -199,11 +244,13 @@ # define put_char(c) put_byte((c) & 0x7F) # define get_char(c) ascii2pascii(get_byte()) # define OS_CODE 0x0F /* temporary, subject to change */ -# undef SIGTERM /* We don't want a signal handler for SIGTERM */ +# ifdef SIGTERM +# undef SIGTERM /* We don't want a signal handler for SIGTERM */ +# endif #endif -#ifdef WIN32 -# define OS_CODE 0x0b +#if defined(pyr) && !defined(NOMEMCPY) /* Pyramid */ +# define NOMEMCPY /* problem with overlapping copies */ #endif #ifdef TOPS20 @@ -243,6 +290,19 @@ # define MAX_SUFFIX 30 #endif +#ifndef MAKE_LEGAL_NAME +# ifdef NO_MULTIPLE_DOTS +# define MAKE_LEGAL_NAME(name) make_simple_name(name) +# else +# define MAKE_LEGAL_NAME(name) +# endif +#endif + +#ifndef MIN_PART +# define MIN_PART 3 + /* keep at least MIN_PART chars between dots in a file name. */ +#endif + #ifndef EXPAND # define EXPAND(argc,argv) #endif diff --git a/gnu/usr.bin/gzip/trees.c b/gnu/usr.bin/gzip/trees.c index 2cf380a278..db3b4b700a 100644 --- a/gnu/usr.bin/gzip/trees.c +++ b/gnu/usr.bin/gzip/trees.c @@ -54,13 +54,12 @@ */ #include -#include #include "tailor.h" #include "gzip.h" -#ifndef lint -static char rcsid[] = "$Id: trees.c,v 0.11 1993/03/26 14:55:43 jloup Exp $"; +#ifdef RCSID +static char rcsid[] = "$Id: trees.c,v 0.12 1993/06/10 13:27:54 jloup Exp $"; #endif /* =========================================================================== diff --git a/gnu/usr.bin/gzip/unlzh.c b/gnu/usr.bin/gzip/unlzh.c new file mode 100644 index 0000000000..e318e5ed94 --- /dev/null +++ b/gnu/usr.bin/gzip/unlzh.c @@ -0,0 +1,401 @@ +/* unlzh.c -- decompress files in SCO compress -H (LZH) format. + * The code in this file is directly derived from the public domain 'ar002' + * written by Haruhiko Okumura. + */ + +#ifdef RCSID +static char rcsid[] = "$Id: unlzh.c,v 1.2 1993/06/24 10:59:01 jloup Exp $"; +#endif + +#include + +#include "tailor.h" +#include "gzip.h" +#include "lzw.h" /* just for consistency checking */ + +/* decode.c */ + +local unsigned decode OF((unsigned count, uch buffer[])); +local void decode_start OF((void)); + +/* huf.c */ +local void huf_decode_start OF((void)); +local unsigned decode_c OF((void)); +local unsigned decode_p OF((void)); +local void read_pt_len OF((int nn, int nbit, int i_special)); +local void read_c_len OF((void)); + +/* io.c */ +local void fillbuf OF((int n)); +local unsigned getbits OF((int n)); +local void init_getbits OF((void)); + +/* maketbl.c */ + +local void make_table OF((int nchar, uch bitlen[], + int tablebits, ush table[])); + + +#define DICBIT 13 /* 12(-lh4-) or 13(-lh5-) */ +#define DICSIZ ((unsigned) 1 << DICBIT) + +#ifndef CHAR_BIT +# define CHAR_BIT 8 +#endif + +#ifndef UCHAR_MAX +# define UCHAR_MAX 255 +#endif + +#define BITBUFSIZ (CHAR_BIT * 2 * sizeof(char)) +/* Do not use CHAR_BIT * sizeof(bitbuf), does not work on machines + * for which short is not on 16 bits (Cray). + */ + +/* encode.c and decode.c */ + +#define MAXMATCH 256 /* formerly F (not more than UCHAR_MAX + 1) */ +#define THRESHOLD 3 /* choose optimal value */ + +/* huf.c */ + +#define NC (UCHAR_MAX + MAXMATCH + 2 - THRESHOLD) + /* alphabet = {0, 1, 2, ..., NC - 1} */ +#define CBIT 9 /* $\lfloor \log_2 NC \rfloor + 1$ */ +#define CODE_BIT 16 /* codeword length */ + +#define NP (DICBIT + 1) +#define NT (CODE_BIT + 3) +#define PBIT 4 /* smallest integer such that (1U << PBIT) > NP */ +#define TBIT 5 /* smallest integer such that (1U << TBIT) > NT */ +#if NT > NP +# define NPT NT +#else +# define NPT NP +#endif + +/* local ush left[2 * NC - 1]; */ +/* local ush right[2 * NC - 1]; */ +#define left prev +#define right head +#if NC > (1<<(BITS-2)) + error cannot overlay left+right and prev +#endif + +/* local uch c_len[NC]; */ +#define c_len outbuf +#if NC > OUTBUFSIZ + error cannot overlay c_len and outbuf +#endif + +local uch pt_len[NPT]; +local unsigned blocksize; +local ush pt_table[256]; + +/* local ush c_table[4096]; */ +#define c_table d_buf +#if (DIST_BUFSIZE-1) < 4095 + error cannot overlay c_table and d_buf +#endif + +/*********************************************************** + io.c -- input/output +***********************************************************/ + +local ush bitbuf; +local unsigned subbitbuf; +local int bitcount; + +local void fillbuf(n) /* Shift bitbuf n bits left, read n bits */ + int n; +{ + bitbuf <<= n; + while (n > bitcount) { + bitbuf |= subbitbuf << (n -= bitcount); + subbitbuf = (unsigned)try_byte(); + if ((int)subbitbuf == EOF) subbitbuf = 0; + bitcount = CHAR_BIT; + } + bitbuf |= subbitbuf >> (bitcount -= n); +} + +local unsigned getbits(n) + int n; +{ + unsigned x; + + x = bitbuf >> (BITBUFSIZ - n); fillbuf(n); + return x; +} + +local void init_getbits() +{ + bitbuf = 0; subbitbuf = 0; bitcount = 0; + fillbuf(BITBUFSIZ); +} + +/*********************************************************** + maketbl.c -- make table for decoding +***********************************************************/ + +local void make_table(nchar, bitlen, tablebits, table) + int nchar; + uch bitlen[]; + int tablebits; + ush table[]; +{ + ush count[17], weight[17], start[18], *p; + unsigned i, k, len, ch, jutbits, avail, nextcode, mask; + + for (i = 1; i <= 16; i++) count[i] = 0; + for (i = 0; i < (unsigned)nchar; i++) count[bitlen[i]]++; + + start[1] = 0; + for (i = 1; i <= 16; i++) + start[i + 1] = start[i] + (count[i] << (16 - i)); + if ((start[17] & 0xffff) != 0) + error("Bad table\n"); + + jutbits = 16 - tablebits; + for (i = 1; i <= (unsigned)tablebits; i++) { + start[i] >>= jutbits; + weight[i] = (unsigned) 1 << (tablebits - i); + } + while (i <= 16) { + weight[i] = (unsigned) 1 << (16 - i); + i++; + } + + i = start[tablebits + 1] >> jutbits; + if (i != 0) { + k = 1 << tablebits; + while (i != k) table[i++] = 0; + } + + avail = nchar; + mask = (unsigned) 1 << (15 - tablebits); + for (ch = 0; ch < (unsigned)nchar; ch++) { + if ((len = bitlen[ch]) == 0) continue; + nextcode = start[len] + weight[len]; + if (len <= (unsigned)tablebits) { + for (i = start[len]; i < nextcode; i++) table[i] = ch; + } else { + k = start[len]; + p = &table[k >> jutbits]; + i = len - tablebits; + while (i != 0) { + if (*p == 0) { + right[avail] = left[avail] = 0; + *p = avail++; + } + if (k & mask) p = &right[*p]; + else p = &left[*p]; + k <<= 1; i--; + } + *p = ch; + } + start[len] = nextcode; + } +} + +/*********************************************************** + huf.c -- static Huffman +***********************************************************/ + +local void read_pt_len(nn, nbit, i_special) + int nn; + int nbit; + int i_special; +{ + int i, c, n; + unsigned mask; + + n = getbits(nbit); + if (n == 0) { + c = getbits(nbit); + for (i = 0; i < nn; i++) pt_len[i] = 0; + for (i = 0; i < 256; i++) pt_table[i] = c; + } else { + i = 0; + while (i < n) { + c = bitbuf >> (BITBUFSIZ - 3); + if (c == 7) { + mask = (unsigned) 1 << (BITBUFSIZ - 1 - 3); + while (mask & bitbuf) { mask >>= 1; c++; } + } + fillbuf((c < 7) ? 3 : c - 3); + pt_len[i++] = c; + if (i == i_special) { + c = getbits(2); + while (--c >= 0) pt_len[i++] = 0; + } + } + while (i < nn) pt_len[i++] = 0; + make_table(nn, pt_len, 8, pt_table); + } +} + +local void read_c_len() +{ + int i, c, n; + unsigned mask; + + n = getbits(CBIT); + if (n == 0) { + c = getbits(CBIT); + for (i = 0; i < NC; i++) c_len[i] = 0; + for (i = 0; i < 4096; i++) c_table[i] = c; + } else { + i = 0; + while (i < n) { + c = pt_table[bitbuf >> (BITBUFSIZ - 8)]; + if (c >= NT) { + mask = (unsigned) 1 << (BITBUFSIZ - 1 - 8); + do { + if (bitbuf & mask) c = right[c]; + else c = left [c]; + mask >>= 1; + } while (c >= NT); + } + fillbuf((int) pt_len[c]); + if (c <= 2) { + if (c == 0) c = 1; + else if (c == 1) c = getbits(4) + 3; + else c = getbits(CBIT) + 20; + while (--c >= 0) c_len[i++] = 0; + } else c_len[i++] = c - 2; + } + while (i < NC) c_len[i++] = 0; + make_table(NC, c_len, 12, c_table); + } +} + +local unsigned decode_c() +{ + unsigned j, mask; + + if (blocksize == 0) { + blocksize = getbits(16); + if (blocksize == 0) { + return NC; /* end of file */ + } + read_pt_len(NT, TBIT, 3); + read_c_len(); + read_pt_len(NP, PBIT, -1); + } + blocksize--; + j = c_table[bitbuf >> (BITBUFSIZ - 12)]; + if (j >= NC) { + mask = (unsigned) 1 << (BITBUFSIZ - 1 - 12); + do { + if (bitbuf & mask) j = right[j]; + else j = left [j]; + mask >>= 1; + } while (j >= NC); + } + fillbuf((int) c_len[j]); + return j; +} + +local unsigned decode_p() +{ + unsigned j, mask; + + j = pt_table[bitbuf >> (BITBUFSIZ - 8)]; + if (j >= NP) { + mask = (unsigned) 1 << (BITBUFSIZ - 1 - 8); + do { + if (bitbuf & mask) j = right[j]; + else j = left [j]; + mask >>= 1; + } while (j >= NP); + } + fillbuf((int) pt_len[j]); + if (j != 0) j = ((unsigned) 1 << (j - 1)) + getbits((int) (j - 1)); + return j; +} + +local void huf_decode_start() +{ + init_getbits(); blocksize = 0; +} + +/*********************************************************** + decode.c +***********************************************************/ + +local int j; /* remaining bytes to copy */ +local int done; /* set at end of input */ + +local void decode_start() +{ + huf_decode_start(); + j = 0; + done = 0; +} + +/* Decode the input and return the number of decoded bytes put in buffer + */ +local unsigned decode(count, buffer) + unsigned count; + uch buffer[]; + /* The calling function must keep the number of + bytes to be processed. This function decodes + either 'count' bytes or 'DICSIZ' bytes, whichever + is smaller, into the array 'buffer[]' of size + 'DICSIZ' or more. + Call decode_start() once for each new file + before calling this function. + */ +{ + local unsigned i; + unsigned r, c; + + r = 0; + while (--j >= 0) { + buffer[r] = buffer[i]; + i = (i + 1) & (DICSIZ - 1); + if (++r == count) return r; + } + for ( ; ; ) { + c = decode_c(); + if (c == NC) { + done = 1; + return r; + } + if (c <= UCHAR_MAX) { + buffer[r] = c; + if (++r == count) return r; + } else { + j = c - (UCHAR_MAX + 1 - THRESHOLD); + i = (r - decode_p() - 1) & (DICSIZ - 1); + while (--j >= 0) { + buffer[r] = buffer[i]; + i = (i + 1) & (DICSIZ - 1); + if (++r == count) return r; + } + } + } +} + + +/* =========================================================================== + * Unlzh in to out. Return OK or ERROR. + */ +int unlzh(in, out) + int in; + int out; +{ + unsigned n; + ifd = in; + ofd = out; + + decode_start(); + while (!done) { + n = decode((unsigned) DICSIZ, window); + if (!test && n > 0) { + write_buf(out, (char*)window, n); + } + } + return OK; +} diff --git a/gnu/usr.bin/gzip/unlzw.c b/gnu/usr.bin/gzip/unlzw.c index 0687c1b09f..15d2a313b4 100644 --- a/gnu/usr.bin/gzip/unlzw.c +++ b/gnu/usr.bin/gzip/unlzw.c @@ -7,11 +7,10 @@ * to accommodate in-memory decompression. */ -#ifndef lint -static char rcsid[] = "$Id: unlzw.c,v 0.13 1993/05/27 10:32:55 jloup Exp $"; +#ifdef RCSID +static char rcsid[] = "$Id: unlzw.c,v 0.15 1993/06/10 13:28:35 jloup Exp $"; #endif -#include #include #include "tailor.h" @@ -263,6 +262,7 @@ int unlzw(in, out) read_error(); } insize += rsize; + bytes_in += (ulg)rsize; } inbits = ((rsize != 0) ? ((long)insize - insize%n_bits)<<3 : ((long)insize<<3)-(n_bits-1)); @@ -281,8 +281,10 @@ int unlzw(in, out) goto resetbuf; } input(inbuf,posbits,code,n_bits,bitmask); - + Tracev((stderr, "%d ", code)); + if (oldcode == -1) { + if (code >= 256) error("corrupt input."); outbuf[outpos++] = (char_type)(finchar = (int)(oldcode=code)); continue; } @@ -313,9 +315,11 @@ int unlzw(in, out) posbits, p[-1],p[0],p[1],p[2],p[3]); #endif if (!test && outpos > 0) { - write_buf(out, outbuf, outpos); + write_buf(out, (char*)outbuf, outpos); + bytes_out += (ulg)outpos; } - error("corrupt input. Use zcat to recover some data."); + error(to_stdout ? "corrupt input." : + "corrupt input. Use zcat to recover some data."); } *--stackp = (char_type)finchar; code = oldcode; @@ -341,7 +345,10 @@ int unlzw(in, out) outpos += i; } if (outpos >= OUTBUFSIZ) { - if (!test) write_buf(out, outbuf, outpos); + if (!test) { + write_buf(out, (char*)outbuf, outpos); + bytes_out += (ulg)outpos; + } outpos = 0; } stackp+= i; @@ -360,10 +367,11 @@ int unlzw(in, out) } oldcode = incode; /* Remember previous code. */ } - bytes_in += rsize; - } while (rsize != 0); - if (!test && outpos > 0) write_buf(out, outbuf, outpos); + if (!test && outpos > 0) { + write_buf(out, (char*)outbuf, outpos); + bytes_out += (ulg)outpos; + } return OK; } diff --git a/gnu/usr.bin/gzip/unpack.c b/gnu/usr.bin/gzip/unpack.c index e6cf7ca7f7..a00fdaefc6 100644 --- a/gnu/usr.bin/gzip/unpack.c +++ b/gnu/usr.bin/gzip/unpack.c @@ -4,12 +4,10 @@ * terms of the GNU General Public License, see the file COPYING. */ -#ifndef lint -static char rcsid[] = "$Id: unpack.c,v 1.3 1993/05/28 17:56:07 jloup Exp $"; +#ifdef RCSID +static char rcsid[] = "$Id: unpack.c,v 1.4 1993/06/11 19:25:36 jloup Exp $"; #endif -#include - #include "tailor.h" #include "gzip.h" #include "crypt.h" @@ -26,11 +24,7 @@ static char rcsid[] = "$Id: unpack.c,v 1.3 1993/05/28 17:56:07 jloup Exp $"; #define LITERALS 256 /* Number of literals, excluding the End of Block (EOB) code */ -#ifdef SMALL_MEM -# define MAX_PEEK 10 -#else -# define MAX_PEEK 12 -#endif +#define MAX_PEEK 12 /* Maximum number of 'peek' bits used to optimize traversal of the * Huffman tree. */ @@ -54,7 +48,8 @@ local int parents[MAX_BITLEN+1]; /* Number of parents for each bit length */ local int peek_bits; /* Number of peek bits currently used */ -local uch prefix_len[1 << MAX_PEEK]; +/* local uch prefix_len[1 << MAX_PEEK]; */ +#define prefix_len outbuf /* For each bit pattern b of peek_bits bits, prefix_len[b] is the length * of the Huffman code starting with a prefix of b (upper bits), or 0 * if all codes of prefix b have more than peek_bits bits. It is not @@ -62,6 +57,9 @@ local uch prefix_len[1 << MAX_PEEK]; * codes encountered in the input stream are short codes (by construction). * So for most codes a single lookup will be necessary. */ +#if (1< OUTBUFSIZ + error cannot overlay prefix_len and outbuf +#endif local ulg bitbuf; /* Bits are added on the low part of bitbuf and read from the high part. */ @@ -221,7 +219,7 @@ int unpack(in, out) do { len++, mask = (mask<<1)+1; look_bits(peek, len, mask); - } while (peek < parents[len]); + } while (peek < (unsigned)parents[len]); /* loop as long as peek is a parent node */ } /* At this point, peek is the next complete code, of len bits */ @@ -234,7 +232,7 @@ int unpack(in, out) flush_window(); Trace((stderr, "bytes_out %ld\n", bytes_out)); - if (orig_len != bytes_out) { + if (orig_len != (ulg)bytes_out) { error("invalid compressed data--length error"); } return OK; diff --git a/gnu/usr.bin/gzip/unzip.c b/gnu/usr.bin/gzip/unzip.c index a78622a805..7e287a157a 100644 --- a/gnu/usr.bin/gzip/unzip.c +++ b/gnu/usr.bin/gzip/unzip.c @@ -13,12 +13,10 @@ either deflated or stored. */ -#ifndef lint -static char rcsid[] = "$Id: unzip.c,v 0.12 1993/05/28 17:56:23 jloup Exp $"; +#ifdef RCSID +static char rcsid[] = "$Id: unzip.c,v 0.13 1993/06/10 13:29:00 jloup Exp $"; #endif -#include - #include "tailor.h" #include "gzip.h" #include "crypt.h" @@ -176,7 +174,7 @@ int unzip(in, out) if (orig_crc != updcrc(outbuf, 0)) { error("invalid compressed data--crc error"); } - if (orig_len != bytes_out) { + if (orig_len != (ulg)bytes_out) { error("invalid compressed data--length error"); } diff --git a/gnu/usr.bin/gzip/util.c b/gnu/usr.bin/gzip/util.c index dc3695e93c..70375d8536 100644 --- a/gnu/usr.bin/gzip/util.c +++ b/gnu/usr.bin/gzip/util.c @@ -4,11 +4,10 @@ * terms of the GNU General Public License, see the file COPYING. */ -#ifndef lint -static char rcsid[] = "$Id: util.c,v 0.14 1993/05/27 10:31:52 jloup Exp $"; +#ifdef RCSID +static char rcsid[] = "$Id: util.c,v 0.15 1993/06/15 09:04:13 jloup Exp $"; #endif -#include #include #include #include @@ -33,6 +32,26 @@ static char rcsid[] = "$Id: util.c,v 0.14 1993/05/27 10:31:52 jloup Exp $"; extern ulg crc_32_tab[]; /* crc table, defined below */ +/* =========================================================================== + * Copy input to output unchanged: zcat == cat with --force. + * IN assertion: insize bytes have already been read in inbuf. + */ +int copy(in, out) + int in, out; /* input and output file descriptors */ +{ + errno = 0; + while (insize != 0 && (int)insize != EOF) { + write_buf(out, (char*)inbuf, insize); + bytes_out += insize; + insize = read(in, (char*)inbuf, INBUFSIZ); + } + if ((int)insize == EOF && errno != 0) { + read_error(); + } + bytes_in = bytes_out; + return OK; +} + /* =========================================================================== * Run a set of bytes through the crc shift register. If s is a NULL * pointer, then initialize the crc shift register contents instead. @@ -69,10 +88,10 @@ void clear_bufs() } /* =========================================================================== - * Fill the input buffer. This is called only when the buffer is empty - * and at least one byte is really needed. + * Fill the input buffer. This is called only when the buffer is empty. */ -int fill_inbuf() +int fill_inbuf(eof_ok) + int eof_ok; /* set if EOF acceptable as a result */ { int len; @@ -86,6 +105,7 @@ int fill_inbuf() } while (insize < INBUFSIZ); if (insize == 0) { + if (eof_ok) return EOF; read_error(); } bytes_in += (ulg)insize; @@ -177,6 +197,26 @@ char *basename(fname) return fname; } +/* ======================================================================== + * Make a file name legal for file systems not allowing file names with + * multiple dots or starting with a dot (such as MSDOS), by changing + * all dots except the last one into underlines. A target dependent + * function can be used instead of this simple function by defining the macro + * MAKE_LEGAL_NAME in tailor.h and providing the function in a target + * dependent module. + */ +void make_simple_name(name) + char *name; +{ + char *p = strrchr(name, '.'); + if (p == NULL) return; + if (p == name) p++; + do { + if (*--p == '.') *p = '_'; + } while (p != name); +} + + #if defined(NO_STRING_H) && !defined(STDC_HEADERS) /* Provide missing strspn and strcspn functions. */ @@ -261,7 +301,7 @@ char *add_envopt(argcp, argvp, env) if (*p) *p++ = '\0'; /* mark it */ } if (nargc == 0) { - free(env); env = NULL; + free(env); return NULL; } *argcp += nargc; @@ -325,11 +365,12 @@ void write_error() } /* ======================================================================== - * Display compression ratio on stderr. + * Display compression ratio on the given stream on 6 characters. */ -void display_ratio(num, den) +void display_ratio(num, den, file) long num; long den; + FILE *file; { long ratio; /* 1000 times the compression ratio */ @@ -341,10 +382,12 @@ void display_ratio(num, den) ratio = num/(den/1000L); } if (ratio < 0) { - putc('-', stderr); + putc('-', file); ratio = -ratio; + } else { + putc(' ', file); } - fprintf(stderr, "%2ld.%ld%%", ratio / 10L, ratio % 10L); + fprintf(file, "%2ld.%1ld%%", ratio / 10L, ratio % 10L); } diff --git a/gnu/usr.bin/gzip/zcmp b/gnu/usr.bin/gzip/zcmp deleted file mode 100644 index c21e7ef8ae..0000000000 --- a/gnu/usr.bin/gzip/zcmp +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/sh - -# Zcmp and zdiff are used to invoke the cmp or the diff pro- -# gram on compressed files. All options specified are passed -# directly to cmp or diff. If only 1 file is specified, then -# the files compared are file1 and an uncompressed file1.gz. -# If two files are specified, then they are uncompressed (if -# necessary) and fed to cmp or diff. The exit status from cmp -# or diff is preserved. - -prog=`echo $0 | sed 's|.*/||'` -case "$prog" in - *cmp) comp=${CMP-cmp} ;; - *) comp=${DIFF-diff} ;; -esac - -OPTIONS= -FILES= -for ARG -do - case "$ARG" in - -*) OPTIONS="$OPTIONS $ARG";; - *) if test -f "$ARG"; then - FILES="$FILES $ARG" - else - echo "${prog}: $ARG not found or not a regular file" - exit 1 - fi ;; - esac -done -if test -z "$FILES"; then - echo "Usage: $prog [${comp}_options] file [file]" - exit 1 -fi -set $FILES -if test $# -eq 1; then - FILE=`echo "$1" | sed 's/[-.][zZtga]*$//'` - gzip -cd "$1" | $comp $OPTIONS - "$FILE" - STAT="$?" - -elif test $# -eq 2; then - case "$1" in - *[-.]gz | *[-.][zZ] | *.t[ga]z) - case "$2" in - *[-.]gz | *[-.][zZ] | *.t[ga]z) - F=`echo "$2" | sed 's|.*/||;s|[-.][zZtga]*$||'` - gzip -cd "$2" > /tmp/"$F".$$ - gzip -cd "$1" | $comp $OPTIONS - /tmp/"$F".$$ - STAT="$?" - /bin/rm -f /tmp/"$F".$$;; - - *) gzip -cd "$1" | $comp $OPTIONS - "$2" - STAT="$?";; - esac;; - *) case "$2" in - *[-.]gz | *[-.][zZ] | *.t[ga]z) - gzip -cd "$2" | $comp $OPTIONS "$1" - - STAT="$?";; - *) $comp $OPTIONS "$1" "$2" - STAT="$?";; - esac;; - esac - exit "$STAT" -else - echo "Usage: $prog [${comp}_options] file [file]" - exit 1 -fi diff --git a/gnu/usr.bin/gzip/zdiff b/gnu/usr.bin/gzip/zdiff index c21e7ef8ae..84e65d329e 100644 --- a/gnu/usr.bin/gzip/zdiff +++ b/gnu/usr.bin/gzip/zdiff @@ -1,4 +1,5 @@ #!/bin/sh +# sh is buggy on RS/6000 AIX 3.2. Replace above line with #!/bin/ksh # Zcmp and zdiff are used to invoke the cmp or the diff pro- # gram on compressed files. All options specified are passed @@ -8,6 +9,7 @@ # necessary) and fed to cmp or diff. The exit status from cmp # or diff is preserved. +PATH="/usr/local/bin:$PATH"; export PATH prog=`echo $0 | sed 's|.*/||'` case "$prog" in *cmp) comp=${CMP-cmp} ;; @@ -40,21 +42,21 @@ if test $# -eq 1; then elif test $# -eq 2; then case "$1" in - *[-.]gz | *[-.][zZ] | *.t[ga]z) + *[-.]gz* | *[-.][zZ] | *.t[ga]z) case "$2" in - *[-.]gz | *[-.][zZ] | *.t[ga]z) - F=`echo "$2" | sed 's|.*/||;s|[-.][zZtga]*$||'` - gzip -cd "$2" > /tmp/"$F".$$ - gzip -cd "$1" | $comp $OPTIONS - /tmp/"$F".$$ + *[-.]gz* | *[-.][zZ] | *.t[ga]z) + F=`echo "$2" | sed 's|.*/||;s|[-.][zZtga]*||'` + gzip -cdfq "$2" > /tmp/"$F".$$ + gzip -cdfq "$1" | $comp $OPTIONS - /tmp/"$F".$$ STAT="$?" /bin/rm -f /tmp/"$F".$$;; - *) gzip -cd "$1" | $comp $OPTIONS - "$2" + *) gzip -cdfq "$1" | $comp $OPTIONS - "$2" STAT="$?";; esac;; *) case "$2" in - *[-.]gz | *[-.][zZ] | *.t[ga]z) - gzip -cd "$2" | $comp $OPTIONS "$1" - + *[-.]gz* | *[-.][zZ] | *.t[ga]z) + gzip -cdfq "$2" | $comp $OPTIONS "$1" - STAT="$?";; *) $comp $OPTIONS "$1" "$2" STAT="$?";; diff --git a/gnu/usr.bin/gzip/zdiff.1 b/gnu/usr.bin/gzip/zdiff.1 index 44f9137c9f..ea3bf4103f 100644 --- a/gnu/usr.bin/gzip/zdiff.1 +++ b/gnu/usr.bin/gzip/zdiff.1 @@ -35,7 +35,7 @@ or .I diff is preserved. .SH "SEE ALSO" -cmp(1), diff(1), zmore(1), znew(1), zforce(1), gzip(1), gzexe(1) +cmp(1), diff(1), zmore(1), zgrep(1), znew(1), zforce(1), gzip(1), gzexe(1) .SH BUGS Messages from the .I cmp diff --git a/gnu/usr.bin/gzip/zforce b/gnu/usr.bin/gzip/zforce index 9fe85ad866..17258a4855 100644 --- a/gnu/usr.bin/gzip/zforce +++ b/gnu/usr.bin/gzip/zforce @@ -5,6 +5,7 @@ # This can be useful for files with names truncated after a file transfer. # 12345678901234 is renamed to 12345678901.gz +PATH="/usr/local/bin:$PATH"; export PATH x=`basename $0` if test $# = 0; then echo "force a '.gz' extension on all gzip files" @@ -23,17 +24,18 @@ for i do test `expr "$i" : '.*[.-]gz$'` -eq 0 || continue test `expr "$i" : '.*[.]t[ag]z$'` -eq 0 || continue - gzip -t "$i" 2>/dev/null || continue + if gzip -l < "$i" 2>/dev/null | grep '^defl' > /dev/null; then - if test `expr "$i" : '^............'` -eq 12; then - new=`expr "$i" : '\(.*\)...$`.gz - else - new="$i.gz" + if test `expr "$i" : '^............'` -eq 12; then + new=`expr "$i" : '\(.*\)...$`.gz + else + new="$i.gz" + fi + if mv "$i" "$new" 2>/dev/null; then + echo $i -- replaced with $new + continue + fi + res=1; echo ${x}: cannot rename $i to $new fi - if mv "$i" "$new" 2>/dev/null; then - echo $i -- replaced with $new - continue - fi - res=1; echo ${x}: cannot rename $i to $new done exit $res diff --git a/gnu/usr.bin/gzip/zforce.1 b/gnu/usr.bin/gzip/zforce.1 index e2318150ef..37c6aba6ac 100644 --- a/gnu/usr.bin/gzip/zforce.1 +++ b/gnu/usr.bin/gzip/zforce.1 @@ -17,4 +17,4 @@ is truncated to make room for the .gz suffix. For example, 12345678901234 is renamed to 12345678901.gz. A file name such as foo.tgz is left intact. .SH "SEE ALSO" -gzip(1), znew(1), zmore(1), zcmp(1), gzexe(1) +gzip(1), znew(1), zmore(1), zgrep(1), zdiff(1), gzexe(1) diff --git a/gnu/usr.bin/gzip/zgrep b/gnu/usr.bin/gzip/zgrep new file mode 100644 index 0000000000..bcc10cc1db --- /dev/null +++ b/gnu/usr.bin/gzip/zgrep @@ -0,0 +1,72 @@ +#!/bin/sh + +# zgrep -- a wrapper around a grep program that decompresses files as needed +# Adapted from a version sent by Charles Levert + +PATH="/usr/local/bin:$PATH"; export PATH + +prog=`echo $0 | sed 's|.*/||'` +case "$prog" in + *egrep) grep=${EGREP-egrep} ;; + *fgrep) grep=${FGREP-fgrep} ;; + *) grep=${GREP-grep} ;; +esac +A= +fileno=0 +pat="" +while test $# -ne 0; do + case "$1" in + -e | -f) opt="$opt $1"; shift; pat="$1" + if test "$grep" = grep; then # grep is buggy with -e on SVR4 + grep=egrep + fi;; + -*) opt="$opt $1";; + *) if test -z "$pat"; then + pat="$1" + else + fileno=`expr $fileno + 1` + eval A$fileno=\$1 + A="$A \"\$A$fileno\"" + fi + ;; + esac + shift +done + +if test -z "$pat"; then + echo "grep through gzip files" + echo "usage: $prog [grep_options] pattern [files]" + exit 1 +fi + +list=0 +silent=0 +op=`echo "$opt" | sed -e 's/ //g' -e 's/-//g'` +case "$op" in + *l*) list=1 +esac +case "$op" in + *h*) silent=1 +esac + +if test $fileno -eq 0; then + gzip -cdfq | $grep $opt "$pat" + exit $? +fi +eval set "$A" # files in $1, $2 ... + +res=0 +for i do + if test $list -eq 1; then + gzip -cdfq "$i" | $grep $opt "$pat" > /dev/null && echo $i + r=$? + elif test $# -eq 1 -o $silent -eq 1; then + gzip -cdfq "$i" | $grep $opt "$pat" + r=$? + else + gzip -cdfq "$i" | $grep $opt "$pat" | sed "s|^|${i}:|" + r=$? + fi + test "$r" -ne 0 && res="$r" +done +exit $res diff --git a/gnu/usr.bin/gzip/zgrep.1 b/gnu/usr.bin/gzip/zgrep.1 new file mode 100644 index 0000000000..a52a88abcf --- /dev/null +++ b/gnu/usr.bin/gzip/zgrep.1 @@ -0,0 +1,44 @@ +.TH ZGREP 1 +.SH NAME +zgrep \- search possibly compressed files for a regular expression +.SH SYNOPSIS +.B zgrep +[ grep_options ] +.BI [\ -e\ ] " pattern" +.IR filename ".\|.\|." +.SH DESCRIPTION +.IR Zgrep +is used to invoke the +.I grep +on compress'ed or gzip'ed files. All options specified are passed directly to +.I grep. +If no file is specified, then the standard input is decompressed +if necessary and fed to grep. +Otherwise the given files are uncompressed if necessary and fed to +.I grep. +.PP +If +.I zgrep +is invoked as +.I zegrep +or +.I zfgrep +then +.I egrep +or +.I fgrep +is used instead of +.I grep. +If the GREP environment variable is set, +.I zgrep +uses it as the +.I grep +program to be invoked. For example: + + for sh: GREP=fgrep zgrep string files + for csh: (setenv GREP fgrep; zgrep string files) +.SH AUTHOR +Charles Levert (charles@comm.polymtl.ca) +.SH "SEE ALSO" +grep(1), egrep(1), fgrep(1), zdiff(1), zmore(1), znew(1), zforce(1), +gzip(1), gzexe(1) diff --git a/gnu/usr.bin/gzip/zip.c b/gnu/usr.bin/gzip/zip.c index 5bf172f81b..507d1616ef 100644 --- a/gnu/usr.bin/gzip/zip.c +++ b/gnu/usr.bin/gzip/zip.c @@ -4,12 +4,11 @@ * terms of the GNU General Public License, see the file COPYING. */ -#ifndef lint -static char rcsid[] = "$Id: zip.c,v 0.16 1993/05/28 14:51:17 jloup Exp $"; +#ifdef RCSID +static char rcsid[] = "$Id: zip.c,v 0.17 1993/06/10 13:29:25 jloup Exp $"; #endif #include -#include #include #include "tailor.h" diff --git a/gnu/usr.bin/gzip/zmore b/gnu/usr.bin/gzip/zmore index 64f7b1b99a..ca933c7118 100644 --- a/gnu/usr.bin/gzip/zmore +++ b/gnu/usr.bin/gzip/zmore @@ -1,22 +1,31 @@ #!/bin/sh +PATH="/usr/local/bin:$PATH"; export PATH if test "`echo -n a`" = "-n a"; then # looks like a SysV system: n1=''; n2='\c' else n1='-n'; n2='' fi +oldtty=`stty -g 2>/dev/null` if stty -cbreak 2>/dev/null; then cb='cbreak'; ncb='-cbreak' else # 'stty min 1' resets eof to ^a on both SunOS and SysV! cb='min 1 -icanon'; ncb='icanon eof ^d' fi -oldtty=`stty -g` -trap 'stty -g $oldtty 2>/dev/null; exit' 0 2 3 5 10 13 15 +if test $? -eq 0 -a -n "$oldtty"; then + trap 'stty $oldtty 2>/dev/null; exit' 0 2 3 5 10 13 15 +else + trap 'stty $ncb echo 2>/dev/null; exit' 0 2 3 5 10 13 15 +fi if test $# = 0; then - gzip -cd | eval ${PAGER-more} + if test -t 0; then + echo usage: zmore files... + else + gzip -cdfq | eval ${PAGER-more} + fi else FIRST=1 for FILE @@ -33,7 +42,7 @@ else fi if test "$ANS" != 's'; then echo "------> $FILE <------" - gzip -cd "$FILE" | eval ${PAGER-more} + gzip -cdfq "$FILE" | eval ${PAGER-more} fi if test -t; then FIRST=0 diff --git a/gnu/usr.bin/gzip/zmore.1 b/gnu/usr.bin/gzip/zmore.1 index 08b49fbae5..f7f1843de0 100644 --- a/gnu/usr.bin/gzip/zmore.1 +++ b/gnu/usr.bin/gzip/zmore.1 @@ -6,9 +6,20 @@ zmore \- file perusal filter for crt viewing of compressed text [ name ... ] .SH DESCRIPTION .I Zmore -is a filter which allows examination of compressed text files +is a filter which allows examination of compressed or plain text files one screenful at a time on a soft-copy terminal. -It normally pauses after each screenful, printing --More-- +.I zmore +works on files compressed with +.I compress, pack +or +.I gzip, +and also on uncompressed files. +If a file does not exist, +.I zmore +looks for a file of the same name with the addition of a .gz, .z or .Z suffix. +.PP +.I Zmore +normally pauses after each screenful, printing --More-- at the bottom of the screen. If the user then types a carriage return, one more line is displayed. If the user hits a space, @@ -131,4 +142,4 @@ except that a header is printed before each file. .DT /etc/termcap Terminal data base .SH "SEE ALSO" -more(1), gzip(1), zcmp(1), znew(1), zforce(1), gzexe(1) +more(1), gzip(1), zdiff(1), zgrep(1), znew(1), zforce(1), gzexe(1) diff --git a/gnu/usr.bin/gzip/znew b/gnu/usr.bin/gzip/znew index fcbc466ae2..5c832e826a 100644 --- a/gnu/usr.bin/gzip/znew +++ b/gnu/usr.bin/gzip/znew @@ -1,5 +1,6 @@ #!/bin/sh +PATH="/usr/local/bin:$PATH"; export PATH check=0 pipe=0 opt= @@ -25,35 +26,39 @@ if test -z "$cpmod" && ${TOUCH-touch} -r $tmp.1 $tmp.2 2>/dev/null; then cpmodarg="-r" warn="(does not preserve file modes)" fi -rm -f $tmp.[12] -A= -fileno=0 +# check if GZIP env. variable uses -S or --suffix +gzip -q $tmp.1 +ext=`echo $tmp.1* | sed "s|$tmp.1||"` +rm -f $tmp.[12]* +if test -z "$ext"; then + echo znew: error determining gzip extension + exit 1 +fi +if test "$ext" = ".Z"; then + echo znew: cannot use .Z as gzip extension. + exit 1 +fi for arg do case "$arg" in - -*) opt="$opt $arg";; - *) fileno=`expr $fileno + 1` - eval A$fileno=\$arg - A="$A \"\$A$fileno\"" - ;; + -*) opt="$opt $arg"; shift;; + *) break;; esac done -if test $fileno -eq 0; then - echo 'recompress .Z files into .gz (gzip) files' - echo usage: `echo $0 | sed 's,^.*/,,'` "[-tv9P]" file.Z... +if test $# -eq 0; then + echo "recompress .Z files into $ext (gzip) files" + echo usage: `echo $0 | sed 's,^.*/,,'` "[-tv9KP]" file.Z... echo " -t tests the new files before deleting originals" echo " -v be verbose" echo " -9 use the slowest compression method (optimal compression)" - echo " -K keep a .Z file when it is smaller than the .gz file" + echo " -K keep a .Z file when it is smaller than the $ext file" echo " -P use pipes for the conversion $warn" exit 1 fi -eval set "$A" # the files are now in $1, $2, ... - opt=`echo "$opt" | sed -e 's/ //g' -e 's/-//g'` case "$opt" in *t*) check=1; opt=`echo "$opt" | sed 's/t//g'` @@ -76,9 +81,9 @@ for i do fi test $keep -eq 1 && old=`wc -c < "$n.Z"` if test $pipe -eq 1; then - if gzip -d < "$n.Z" | gzip $opt > "$n.gz"; then + if gzip -d < "$n.Z" | gzip $opt > "$n$ext"; then # Copy file attributes from old file to new one, if possible. - test -n "$cpmod" && $cpmod $cpmodarg "$n.Z" "$n.gz" 2> /dev/null + test -n "$cpmod" && $cpmod $cpmodarg "$n.Z" "$n$ext" 2> /dev/null else echo error while recompressing $n.Z res=1; continue @@ -112,25 +117,25 @@ for i do res=1; continue fi fi - test $keep -eq 1 && new=`wc -c < "$n.gz"` + test $keep -eq 1 && new=`wc -c < "$n$ext"` if test $keep -eq 1 -a `expr \( $old + $block - 1 \) / $block` -lt \ `expr \( $new + $block - 1 \) / $block`; then if test $pipe -eq 1; then - rm -f "$n.gz" + rm -f "$n$ext" elif test $check -eq 1; then - mv "$n.$$" "$n.Z" && rm -f "$n.gz" + mv "$n.$$" "$n.Z" && rm -f "$n$ext" else - gzip -d "$n.gz" && compress "$n" && rm -f "$n.gz" + gzip -d "$n$ext" && compress "$n" && rm -f "$n$ext" fi - echo "$n.Z smaller than $n.gz -- unchanged" + echo "$n.Z smaller than $n$ext -- unchanged" elif test $check -eq 1; then - if gzip -t "$n.gz" ; then + if gzip -t "$n$ext" ; then rm -f "$n.$$" "$n.Z" else test $pipe -eq 0 && mv "$n.$$" "$n.Z" - rm -f "$n.gz" - echo error while testing $n.gz, $n.Z unchanged + rm -f "$n$ext" + echo error while testing $n$ext, $n.Z unchanged res=1; continue fi elif test $pipe -eq 1; then diff --git a/gnu/usr.bin/gzip/znew.1 b/gnu/usr.bin/gzip/znew.1 index 644873bdf5..5cfb47246e 100644 --- a/gnu/usr.bin/gzip/znew.1 +++ b/gnu/usr.bin/gzip/znew.1 @@ -7,7 +7,10 @@ znew \- recompress .Z files to .gz files .SH DESCRIPTION .I Znew recompresses files from .Z (compress) format to .gz (gzip) format. +If you want to recompress a file already in gzip format, rename the file +to force a .Z extension then apply znew. .SH OPTIONS +.TP .B \-f Force recompression from .Z to .gz format even if a .gz file already exists. .TP @@ -26,7 +29,7 @@ Use pipes for the conversion to reduce disk space usage. .B \-K Keep a .Z file when it is smaller than the .gz file .SH "SEE ALSO" -gzip(1), zmore(1), zcmp(1), zforce(1), gzexe(1), compress(1) +gzip(1), zmore(1), zdiff(1), zgrep(1), zforce(1), gzexe(1), compress(1) .SH BUGS .I Znew does not maintain the time stamp with the -P option if diff --git a/gnu/usr.bin/ld/Makefile b/gnu/usr.bin/ld/Makefile new file mode 100644 index 0000000000..6816916a42 --- /dev/null +++ b/gnu/usr.bin/ld/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 6.2 (Berkeley) 4/30/91 + +PROG= ld +SRCS= ld.c cplus-dem.c +NOMAN= noman +LINKS= ${BINDIR}/ld ${BINDIR}/../libexec/ld++ + +.include diff --git a/gnu/usr.bin/ld/cplus-dem.c b/gnu/usr.bin/ld/cplus-dem.c new file mode 100644 index 0000000000..c24ca7ab2d --- /dev/null +++ b/gnu/usr.bin/ld/cplus-dem.c @@ -0,0 +1,974 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + */ + +#ifndef lint +static char sccsid[] = "@(#)cplus-dem.c 5.4 (Berkeley) 4/30/91"; +#endif /* not lint */ + +/* Demangler for GNU C++ + Copyright (C) 1989 Free Software Foundation, Inc. + written by James Clark (jjc@jclark.uucp) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* This is for g++ 1.36.1 (November 6 version). It will probably + require changes for any other version. + + Modified for g++ 1.36.2 (November 18 version). */ + +/* This file exports one function + + char *cplus_demangle (const char *name) + + If `name' is a mangled function name produced by g++, then + a pointer to a malloced string giving a C++ representation + of the name will be returned; otherwise NULL will be returned. + It is the caller's responsibility to free the string which + is returned. + + For example, + + cplus_demangle ("_foo__1Ai") + + returns + + "A::foo(int)" + + This file imports xmalloc and xrealloc, which are like malloc and + realloc except that they generate a fatal error if there is no + available memory. */ + +/* #define nounderscore 1 /* define this is names don't start with _ */ + +#include +#include + +#ifdef USG +#include +#include +#else +#include +#define memcpy(s1, s2, n) bcopy ((s2), (s1), (n)) +#define memcmp(s1, s2, n) bcmp ((s2), (s1), (n)) +#define strchr index +#define strrchr rindex +#endif + +#ifndef __STDC__ +#define const +#endif + +#ifdef __STDC__ +extern char *cplus_demangle (const char *type); +#else +extern char *cplus_demangle (); +#endif + +#ifdef __STDC__ +extern char *xmalloc (int); +extern char *xrealloc (char *, int); +#else +extern char *xmalloc (); +extern char *xrealloc (); +#endif + +static char **typevec = 0; +static int ntypes = 0; +static int typevec_size = 0; + +static struct { + const char *in; + const char *out; +} optable[] = { + "new", " new", + "delete", " delete", + "ne", "!=", + "eq", "==", + "ge", ">=", + "gt", ">", + "le", "<=", + "lt", "<", + "plus", "+", + "minus", "-", + "mult", "*", + "convert", "+", /* unary + */ + "negate", "-", /* unary - */ + "trunc_mod", "%", + "trunc_div", "/", + "truth_andif", "&&", + "truth_orif", "||", + "truth_not", "!", + "postincrement", "++", + "postdecrement", "--", + "bit_ior", "|", + "bit_xor", "^", + "bit_and", "&", + "bit_not", "~", + "call", "()", + "cond", "?:", + "alshift", "<<", + "arshift", ">>", + "component", "->", + "indirect", "*", + "method_call", "->()", + "addr", "&", /* unary & */ + "array", "[]", + "nop", "", /* for operator= */ +}; + +/* Beware: these aren't '\0' terminated. */ + +typedef struct { + char *b; /* pointer to start of string */ + char *p; /* pointer after last character */ + char *e; /* pointer after end of allocated space */ +} string; + +#ifdef __STDC__ +static void string_need (string *s, int n); +static void string_delete (string *s); +static void string_init (string *s); +static void string_clear (string *s); +static int string_empty (string *s); +static void string_append (string *p, const char *s); +static void string_appends (string *p, string *s); +static void string_appendn (string *p, const char *s, int n); +static void string_prepend (string *p, const char *s); +#if 0 +static void string_prepends (string *p, string *s); +#endif +static void string_prependn (string *p, const char *s, int n); +static int get_count (const char **type, int *count); +static int do_args (const char **type, string *decl); +static int do_type (const char **type, string *result); +static int do_arg (const char **type, string *result); +static int do_args (const char **type, string *decl); +static void munge_function_name (string *name); +static void remember_type (const char *type, int len); +#else +static void string_need (); +static void string_delete (); +static void string_init (); +static void string_clear (); +static int string_empty (); +static void string_append (); +static void string_appends (); +static void string_appendn (); +static void string_prepend (); +static void string_prepends (); +static void string_prependn (); +static int get_count (); +static int do_args (); +static int do_type (); +static int do_arg (); +static int do_args (); +static void munge_function_name (); +static void remember_type (); +#endif + +char * +cplus_demangle (type) + const char *type; +{ + string decl; + int n; + int success = 0; + int constructor = 0; + int const_flag = 0; + int i; + const char *p; +#ifndef LONGERNAMES + const char *premangle; +#endif + + if (type == NULL || *type == '\0') + return NULL; +#ifndef nounderscore + if (*type++ != '_') + return NULL; +#endif + p = type; + while (*p != '\0' && !(*p == '_' && p[1] == '_')) + p++; + if (*p == '\0') + { + /* destructor */ + if (type[0] == '_' && type[1] == '$' && type[2] == '_') + { + int n = (strlen (type) - 3)*2 + 3 + 2 + 1; + char *tem = (char *) xmalloc (n); + strcpy (tem, type + 3); + strcat (tem, "::~"); + strcat (tem, type + 3); + strcat (tem, "()"); + return tem; + } + /* static data member */ + if (*type != '_' && (p = strchr (type, '$')) != NULL) + { + int n = strlen (type) + 2; + char *tem = (char *) xmalloc (n); + memcpy (tem, type, p - type); + strcpy (tem + (p - type), "::"); + strcpy (tem + (p - type) + 2, p + 1); + return tem; + } + /* virtual table */ + if (type[0] == '_' && type[1] == 'v' && type[2] == 't' && type[3] == '$') + { + int n = strlen (type + 4) + 14 + 1; + char *tem = (char *) xmalloc (n); + strcpy (tem, type + 4); + strcat (tem, " virtual table"); + return tem; + } + return NULL; + } + + string_init (&decl); + + if (p == type) + { + if (!isdigit (p[2])) + { + string_delete (&decl); + return NULL; + } + constructor = 1; + } + else + { + string_appendn (&decl, type, p - type); + munge_function_name (&decl); + } + p += 2; + +#ifndef LONGERNAMES + premangle = p; +#endif + switch (*p) + { + case 'C': + /* a const member function */ + if (!isdigit (p[1])) + { + string_delete (&decl); + return NULL; + } + p += 1; + const_flag = 1; + /* fall through */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + n = 0; + do + { + n *= 10; + n += *p - '0'; + p += 1; + } + while (isdigit (*p)); + if (strlen (p) < n) + { + string_delete (&decl); + return NULL; + } + if (constructor) + { + string_appendn (&decl, p, n); + string_append (&decl, "::"); + string_appendn (&decl, p, n); + } + else + { + string_prepend (&decl, "::"); + string_prependn (&decl, p, n); + } + p += n; +#ifndef LONGERNAMES + remember_type (premangle, p - premangle); +#endif + success = do_args (&p, &decl); + if (const_flag) + string_append (&decl, " const"); + break; + case 'F': + p += 1; + success = do_args (&p, &decl); + break; + } + + for (i = 0; i < ntypes; i++) + if (typevec[i] != NULL) + free (typevec[i]); + ntypes = 0; + if (typevec != NULL) + { + free ((char *)typevec); + typevec = NULL; + typevec_size = 0; + } + + if (success) + { + string_appendn (&decl, "", 1); + return decl.b; + } + else + { + string_delete (&decl); + return NULL; + } +} + +static int +get_count (type, count) + const char **type; + int *count; +{ + if (!isdigit (**type)) + return 0; + *count = **type - '0'; + *type += 1; + /* see flush_repeats in cplus-method.c */ + if (isdigit (**type)) + { + const char *p = *type; + int n = *count; + do + { + n *= 10; + n += *p - '0'; + p += 1; + } + while (isdigit (*p)); + if (*p == '_') + { + *type = p + 1; + *count = n; + } + } + return 1; +} + +/* result will be initialised here; it will be freed on failure */ + +static int +do_type (type, result) + const char **type; + string *result; +{ + int n; + int done; + int non_empty = 0; + int success; + string decl; + const char *remembered_type; + + string_init (&decl); + string_init (result); + + done = 0; + success = 1; + while (success && !done) + { + int member; + switch (**type) + { + case 'P': + *type += 1; + string_prepend (&decl, "*"); + break; + + case 'R': + *type += 1; + string_prepend (&decl, "&"); + break; + + case 'T': + *type += 1; + if (!get_count (type, &n) || n >= ntypes) + success = 0; + else + { + remembered_type = typevec[n]; + type = &remembered_type; + } + break; + + case 'F': + *type += 1; + if (!string_empty (&decl) && decl.b[0] == '*') + { + string_prepend (&decl, "("); + string_append (&decl, ")"); + } + if (!do_args (type, &decl) || **type != '_') + success = 0; + else + *type += 1; + break; + + case 'M': + case 'O': + { + int constp = 0; + int volatilep = 0; + + member = **type == 'M'; + *type += 1; + if (!isdigit (**type)) + { + success = 0; + break; + } + n = 0; + do + { + n *= 10; + n += **type - '0'; + *type += 1; + } + while (isdigit (**type)); + if (strlen (*type) < n) + { + success = 0; + break; + } + string_append (&decl, ")"); + string_prepend (&decl, "::"); + string_prependn (&decl, *type, n); + string_prepend (&decl, "("); + *type += n; + if (member) + { + if (**type == 'C') + { + *type += 1; + constp = 1; + } + if (**type == 'V') + { + *type += 1; + volatilep = 1; + } + if (*(*type)++ != 'F') + { + success = 0; + break; + } + } + if ((member && !do_args (type, &decl)) || **type != '_') + { + success = 0; + break; + } + *type += 1; + if (constp) + { + if (non_empty) + string_append (&decl, " "); + else + non_empty = 1; + string_append (&decl, "const"); + } + if (volatilep) + { + if (non_empty) + string_append (&decl, " "); + else + non_empty = 1; + string_append (&decl, "volatilep"); + } + break; + } + + case 'C': + if ((*type)[1] == 'P') + { + *type += 1; + if (!string_empty (&decl)) + string_prepend (&decl, " "); + string_prepend (&decl, "const"); + break; + } + + /* fall through */ + default: + done = 1; + break; + } + } + + done = 0; + non_empty = 0; + while (success && !done) + { + switch (**type) + { + case 'C': + *type += 1; + if (non_empty) + string_append (result, " "); + else + non_empty = 1; + string_append (result, "const"); + break; + case 'U': + *type += 1; + if (non_empty) + string_append (result, " "); + else + non_empty = 1; + string_append (result, "unsigned"); + break; + case 'V': + *type += 1; + if (non_empty) + string_append (result, " "); + else + non_empty = 1; + string_append (result, "volatile"); + break; + default: + done = 1; + break; + } + } + + if (success) + switch (**type) + { + case '\0': + case '_': + break; + case 'v': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "void"); + break; + case 'x': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "long long"); + break; + case 'l': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "long"); + break; + case 'i': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "int"); + break; + case 's': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "short"); + break; + case 'c': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "char"); + break; + case 'r': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "long double"); + break; + case 'd': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "double"); + break; + case 'f': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "float"); + break; + case 'G': + *type += 1; + if (!isdigit (**type)) + { + success = 0; + break; + } + /* fall through */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + n = 0; + do + { + n *= 10; + n += **type - '0'; + *type += 1; + } + while (isdigit (**type)); + if (strlen (*type) < n) + { + success = 0; + break; + } + if (non_empty) + string_append (result, " "); + string_appendn (result, *type, n); + *type += n; + break; + default: + success = 0; + break; + } + + if (success) + { + if (!string_empty (&decl)) + { + string_append (result, " "); + string_appends (result, &decl); + } + string_delete (&decl); + return 1; + } + else + { + string_delete (&decl); + string_delete (result); + return 0; + } +} + +/* `result' will be initialised in do_type; it will be freed on failure */ + +static int +do_arg (type, result) + const char **type; + string *result; +{ + const char *start = *type; + + if (!do_type (type, result)) + return 0; + remember_type (start, *type - start); + return 1; +} + +static void +remember_type (start, len) + const char *start; + int len; +{ + char *tem; + + if (ntypes >= typevec_size) + { + if (typevec_size == 0) + { + typevec_size = 3; + typevec = (char **) xmalloc (sizeof (char*)*typevec_size); + } + else + { + typevec_size *= 2; + typevec = (char **) xrealloc ((char *)typevec, sizeof (char*)*typevec_size); + } + } + tem = (char *) xmalloc (len + 1); + memcpy (tem, start, len); + tem[len] = '\0'; + typevec[ntypes++] = tem; +} + +/* `decl' must be already initialised, usually non-empty; + it won't be freed on failure */ + +static int +do_args (type, decl) + const char **type; + string *decl; +{ + string arg; + int need_comma = 0; + + string_append (decl, "("); + + while (**type != '_' && **type != '\0' && **type != 'e' && **type != 'v') + { + if (**type == 'N') + { + int r; + int t; + *type += 1; + if (!get_count (type, &r) || !get_count (type, &t) || t >= ntypes) + return 0; + while (--r >= 0) + { + const char *tem = typevec[t]; + if (need_comma) + string_append (decl, ", "); + if (!do_arg (&tem, &arg)) + return 0; + string_appends (decl, &arg); + string_delete (&arg); + need_comma = 1; + } + } + else + { + if (need_comma) + string_append (decl, ", "); + if (!do_arg (type, &arg)) + return 0; + string_appends (decl, &arg); + string_delete (&arg); + need_comma = 1; + } + } + + if (**type == 'v') + *type += 1; + else if (**type == 'e') + { + *type += 1; + if (need_comma) + string_append (decl, ","); + string_append (decl, "..."); + } + + string_append (decl, ")"); + return 1; +} + +static void +munge_function_name (name) + string *name; +{ + if (!string_empty (name) && name->p - name->b >= 3 + && name->b[0] == 'o' && name->b[1] == 'p' && name->b[2] == '$') + { + int i; + /* see if it's an assignment expression */ + if (name->p - name->b >= 10 /* op$assign_ */ + && memcmp (name->b + 3, "assign_", 7) == 0) + { + for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++) + { + int len = name->p - name->b - 10; + if (strlen (optable[i].in) == len + && memcmp (optable[i].in, name->b + 10, len) == 0) + { + string_clear (name); + string_append (name, "operator"); + string_append (name, optable[i].out); + string_append (name, "="); + return; + } + } + } + else + { + for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++) + { + int len = name->p - name->b - 3; + if (strlen (optable[i].in) == len + && memcmp (optable[i].in, name->b + 3, len) == 0) + { + string_clear (name); + string_append (name, "operator"); + string_append (name, optable[i].out); + return; + } + } + } + return; + } + else if (!string_empty (name) && name->p - name->b >= 5 + && memcmp (name->b, "type$", 5) == 0) + { + /* type conversion operator */ + string type; + const char *tem = name->b + 5; + if (do_type (&tem, &type)) + { + string_clear (name); + string_append (name, "operator "); + string_appends (name, &type); + string_delete (&type); + return; + } + } +} + +/* a mini string-handling package */ + +static void +string_need (s, n) + string *s; + int n; +{ + if (s->b == NULL) + { + if (n < 32) + n = 32; + s->p = s->b = (char *) xmalloc (n); + s->e = s->b + n; + } + else if (s->e - s->p < n) + { + int tem = s->p - s->b; + n += tem; + n *= 2; + s->b = (char *) xrealloc (s->b, n); + s->p = s->b + tem; + s->e = s->b + n; + } +} + +static void +string_delete (s) + string *s; +{ + if (s->b != NULL) + { + free (s->b); + s->b = s->e = s->p = NULL; + } +} + +static void +string_init (s) + string *s; +{ + s->b = s->p = s->e = NULL; +} + +static void +string_clear (s) + string *s; +{ + s->p = s->b; +} + +static int +string_empty (s) + string *s; +{ + return s->b == s->p; +} + +static void +string_append (p, s) + string *p; + const char *s; +{ + int n; + if (s == NULL || *s == '\0') + return; + n = strlen (s); + string_need (p, n); + memcpy (p->p, s, n); + p->p += n; +} + +static void +string_appends (p, s) + string *p, *s; +{ + int n; + if (s->b == s->p) + return; + n = s->p - s->b; + string_need (p, n); + memcpy (p->p, s->b, n); + p->p += n; +} + +static void +string_appendn (p, s, n) + string *p; + const char *s; + int n; +{ + if (n == 0) + return; + string_need (p, n); + memcpy (p->p, s, n); + p->p += n; +} + +static void +string_prepend (p, s) + string *p; + const char *s; +{ + if (s == NULL || *s == '\0') + return; + string_prependn (p, s, strlen (s)); +} + +#if 0 +static void +string_prepends (p, s) + string *p, *s; +{ + if (s->b == s->p) + return; + string_prependn (p, s->b, s->p - s->b); +} +#endif + +static void +string_prependn (p, s, n) + string *p; + const char *s; + int n; +{ + char *q; + + if (n == 0) + return; + string_need (p, n); + for (q = p->p - 1; q >= p->b; q--) + q[n] = q[0]; + memcpy (p->b, s, n); + p->p += n; +} diff --git a/gnu/usr.bin/ld/ld.c b/gnu/usr.bin/ld/ld.c new file mode 100644 index 0000000000..3de1b9c89d --- /dev/null +++ b/gnu/usr.bin/ld/ld.c @@ -0,0 +1,4717 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + */ + +#ifndef lint +static char sccsid[] = "@(#)ld.c 6.10 (Berkeley) 5/22/91"; +#endif /* not lint */ + +/* Linker `ld' for GNU + Copyright (C) 1988 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by Richard Stallman with some help from Eric Albert. + Set, indirect, and warning symbol features added by Randy Smith. */ + +/* Define how to initialize system-dependent header fields. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* symseg.h defines the obsolete GNU debugging format; we should nuke it. */ +#define CORE_ADDR unsigned long /* For symseg.h */ +#include "symseg.h" + +#define N_SET_MAGIC(exec, val) ((exec).a_magic = val) + +/* If compiled with GNU C, use the built-in alloca */ +#ifdef __GNUC__ +#define alloca __builtin_alloca +#endif + +#define min(a,b) ((a) < (b) ? (a) : (b)) + +/* Macro to control the number of undefined references printed */ +#define MAX_UREFS_PRINTED 10 + +/* Size of a page; obtained from the operating system. */ + +int page_size; + +/* Name this program was invoked by. */ + +char *progname; + +/* System dependencies */ + +/* Define this to specify the default executable format. */ + +#ifndef DEFAULT_MAGIC +#define DEFAULT_MAGIC ZMAGIC +#endif + +#ifdef hp300 +#define INITIALIZE_HEADER outheader.a_mid = MID_HP300 +#endif + +/* create screwball format for 386BSD to save space on floppies -wfj */ +int screwballmode; + +/* + * Ok. Following are the relocation information macros. If your + * system should not be able to use the default set (below), you must + * define the following: + + * relocation_info: This must be typedef'd (or #define'd) to the type + * of structure that is stored in the relocation info section of your + * a.out files. Often this is defined in the a.out.h for your system. + * + * RELOC_ADDRESS (rval): Offset into the current section of the + * to be relocated. *Must be an lvalue*. + * + * RELOC_EXTERN_P (rval): Is this relocation entry based on an + * external symbol (1), or was it fully resolved upon entering the + * loader (0) in which case some combination of the value in memory + * (if RELOC_MEMORY_ADD_P) and the extra (if RELOC_ADD_EXTRA) contains + * what the value of the relocation actually was. *Must be an lvalue*. + * + * RELOC_TYPE (rval): If this entry was fully resolved upon + * entering the loader, what type should it be relocated as? + * + * RELOC_SYMBOL (rval): If this entry was not fully resolved upon + * entering the loader, what is the index of it's symbol in the symbol + * table? *Must be a lvalue*. + * + * RELOC_MEMORY_ADD_P (rval): This should return true if the final + * relocation value output here should be added to memory, or if the + * section of memory described should simply be set to the relocation + * value. + * + * RELOC_ADD_EXTRA (rval): (Optional) This macro, if defined, gives + * an extra value to be added to the relocation value based on the + * individual relocation entry. *Must be an lvalue if defined*. + * + * RELOC_PCREL_P (rval): True if the relocation value described is + * pc relative. + * + * RELOC_VALUE_RIGHTSHIFT (rval): Number of bits right to shift the + * final relocation value before putting it where it belongs. + * + * RELOC_TARGET_SIZE (rval): log to the base 2 of the number of + * bytes of size this relocation entry describes; 1 byte == 0; 2 bytes + * == 1; 4 bytes == 2, and etc. This is somewhat redundant (we could + * do everything in terms of the bit operators below), but having this + * macro could end up producing better code on machines without fancy + * bit twiddling. Also, it's easier to understand/code big/little + * endian distinctions with this macro. + * + * RELOC_TARGET_BITPOS (rval): The starting bit position within the + * object described in RELOC_TARGET_SIZE in which the relocation value + * will go. + * + * RELOC_TARGET_BITSIZE (rval): How many bits are to be replaced + * with the bits of the relocation value. It may be assumed by the + * code that the relocation value will fit into this many bits. This + * may be larger than RELOC_TARGET_SIZE if such be useful. + * + * + * Things I haven't implemented + * ---------------------------- + * + * Values for RELOC_TARGET_SIZE other than 0, 1, or 2. + * + * Pc relative relocation for External references. + * + * + */ + +/* The following #if has been modifed for cross compilation */ +/* It originally read: #if defined(sun) && defined(sparc) */ +/* Marc Ullman, Stanford University Nov. 1 1989 */ +#if defined(sun) && (TARGET == SUN4) +/* Sparc (Sun 4) macros */ +#undef relocation_info +#define relocation_info reloc_info_sparc +#define RELOC_ADDRESS(r) ((r)->r_address) +#define RELOC_EXTERN_P(r) ((r)->r_extern) +#define RELOC_TYPE(r) ((r)->r_index) +#define RELOC_SYMBOL(r) ((r)->r_index) +#define RELOC_MEMORY_SUB_P(r) 0 +#define RELOC_MEMORY_ADD_P(r) 0 +#define RELOC_ADD_EXTRA(r) ((r)->r_addend) +#define RELOC_PCREL_P(r) \ + ((r)->r_type >= RELOC_DISP8 && (r)->r_type <= RELOC_WDISP22) +#define RELOC_VALUE_RIGHTSHIFT(r) (reloc_target_rightshift[(r)->r_type]) +#define RELOC_TARGET_SIZE(r) (reloc_target_size[(r)->r_type]) +#define RELOC_TARGET_BITPOS(r) 0 +#define RELOC_TARGET_BITSIZE(r) (reloc_target_bitsize[(r)->r_type]) + +/* Note that these are very dependent on the order of the enums in + enum reloc_type (in a.out.h); if they change the following must be + changed */ +/* Also note that the last few may be incorrect; I have no information */ +static int reloc_target_rightshift[] = { + 0, 0, 0, 0, 0, 0, 2, 2, 10, 0, 0, 0, 0, 0, 0, +}; +static int reloc_target_size[] = { + 0, 1, 2, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +}; +static int reloc_target_bitsize[] = { + 8, 16, 32, 8, 16, 32, 30, 22, 22, 22, 13, 10, 32, 32, 16, +}; + +#define MAX_ALIGNMENT (sizeof (double)) +#endif + +/* Default macros */ +#ifndef RELOC_ADDRESS +#define RELOC_ADDRESS(r) ((r)->r_address) +#define RELOC_EXTERN_P(r) ((r)->r_extern) +#define RELOC_TYPE(r) ((r)->r_symbolnum) +#define RELOC_SYMBOL(r) ((r)->r_symbolnum) +#define RELOC_MEMORY_SUB_P(r) 0 +#define RELOC_MEMORY_ADD_P(r) 1 +#undef RELOC_ADD_EXTRA +#define RELOC_PCREL_P(r) ((r)->r_pcrel) +#define RELOC_VALUE_RIGHTSHIFT(r) 0 +#define RELOC_TARGET_SIZE(r) ((r)->r_length) +#define RELOC_TARGET_BITPOS(r) 0 +#define RELOC_TARGET_BITSIZE(r) 32 +#endif + +#ifndef MAX_ALIGNMENT +#define MAX_ALIGNMENT (sizeof (int)) +#endif + +#ifdef nounderscore +#define LPREFIX '.' +#else +#define LPREFIX 'L' +#endif + +#ifndef TEXT_START +#define TEXT_START(x) N_TXTADDR(x) +#endif + +/* Special global symbol types understood by GNU LD. */ + +/* The following type indicates the definition of a symbol as being + an indirect reference to another symbol. The other symbol + appears as an undefined reference, immediately following this symbol. + + Indirection is asymmetrical. The other symbol's value will be used + to satisfy requests for the indirect symbol, but not vice versa. + If the other symbol does not have a definition, libraries will + be searched to find a definition. + + So, for example, the following two lines placed in an assembler + input file would result in an object file which would direct gnu ld + to resolve all references to symbol "foo" as references to symbol + "bar". + + .stabs "_foo",11,0,0,0 + .stabs "_bar",1,0,0,0 + + Note that (11 == (N_INDR | N_EXT)) and (1 == (N_UNDF | N_EXT)). */ + +#ifndef N_INDR +#define N_INDR 0xa +#endif + +/* The following symbols refer to set elements. These are expected + only in input to the loader; they should not appear in loader + output (unless relocatable output is requested). To be recognized + by the loader, the input symbols must have their N_EXT bit set. + All the N_SET[ATDB] symbols with the same name form one set. The + loader collects all of these elements at load time and outputs a + vector for each name. + Space (an array of 32 bit words) is allocated for the set in the + data section, and the n_value field of each set element value is + stored into one word of the array. + The first word of the array is the length of the set (number of + elements). The last word of the vector is set to zero for possible + use by incremental loaders. The array is ordered by the linkage + order; the first symbols which the linker encounters will be first + in the array. + + In C syntax this looks like: + + struct set_vector { + unsigned int length; + unsigned int vector[length]; + unsigned int always_zero; + }; + + Before being placed into the array, each element is relocated + according to its type. This allows the loader to create an array + of pointers to objects automatically. N_SETA type symbols will not + be relocated. + + The address of the set is made into an N_SETV symbol + whose name is the same as the name of the set. + This symbol acts like a N_DATA global symbol + in that it can satisfy undefined external references. + + For the purposes of determining whether or not to load in a library + file, set element definitions are not considered "real + definitions"; they will not cause the loading of a library + member. + + If relocatable output is requested, none of this processing is + done. The symbols are simply relocated and passed through to the + output file. + + So, for example, the following three lines of assembler code + (whether in one file or scattered between several different ones) + will produce a three element vector (total length is five words; + see above), referenced by the symbol "_xyzzy", which will have the + addresses of the routines _init1, _init2, and _init3. + + *NOTE*: If symbolic addresses are used in the n_value field of the + defining .stabs, those symbols must be defined in the same file as + that containing the .stabs. + + .stabs "_xyzzy",23,0,0,_init1 + .stabs "_xyzzy",23,0,0,_init2 + .stabs "_xyzzy",23,0,0,_init3 + + Note that (23 == (N_SETT | N_EXT)). */ + +#ifndef N_SETA +#define N_SETA 0x14 /* Absolute set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETT +#define N_SETT 0x16 /* Text set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETD +#define N_SETD 0x18 /* Data set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETB +#define N_SETB 0x1A /* Bss set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +/* Macros dealing with the set element symbols defined in a.out.h */ +#define SET_ELEMENT_P(x) ((x)>=N_SETA&&(x)<=(N_SETB|N_EXT)) +#define TYPE_OF_SET_ELEMENT(x) ((x)-N_SETA+N_ABS) + +#ifndef N_SETV +#define N_SETV 0x1C /* Pointer to set vector in data area. */ +#endif /* This is output from LD. */ + +/* If a this type of symbol is encountered, its name is a warning + message to print each time the symbol referenced by the next symbol + table entry is referenced. + + This feature may be used to allow backwards compatibility with + certain functions (eg. gets) but to discourage programmers from + their use. + + So if, for example, you wanted to have ld print a warning whenever + the function "gets" was used in their C program, you would add the + following to the assembler file in which gets is defined: + + .stabs "Obsolete function \"gets\" referenced",30,0,0,0 + .stabs "_gets",1,0,0,0 + + These .stabs do not necessarily have to be in the same file as the + gets function, they simply must exist somewhere in the compilation. */ + +#ifndef N_WARNING +#define N_WARNING 0x1E /* Warning message to print if symbol + included */ +#endif /* This is input to ld */ + +#ifndef __GNU_STAB__ + +/* Line number for the data section. This is to be used to describe + the source location of a variable declaration. */ +#ifndef N_DSLINE +#define N_DSLINE (N_SLINE+N_DATA-N_TEXT) +#endif + +/* Line number for the bss section. This is to be used to describe + the source location of a variable declaration. */ +#ifndef N_BSLINE +#define N_BSLINE (N_SLINE+N_BSS-N_TEXT) +#endif + +#endif /* not __GNU_STAB__ */ + +/* Symbol table */ + +/* Global symbol data is recorded in these structures, + one for each global symbol. + They are found via hashing in 'symtab', which points to a vector of buckets. + Each bucket is a chain of these structures through the link field. */ + +typedef + struct glosym + { + /* Pointer to next symbol in this symbol's hash bucket. */ + struct glosym *link; + /* Name of this symbol. */ + char *name; + /* Value of this symbol as a global symbol. */ + long value; + /* Chain of external 'nlist's in files for this symbol, both defs + and refs. */ + struct nlist *refs; + /* Any warning message that might be associated with this symbol + from an N_WARNING symbol encountered. */ + char *warning; + /* Nonzero means definitions of this symbol as common have been seen, + and the value here is the largest size specified by any of them. */ + int max_common_size; + /* For relocatable_output, records the index of this global sym in the + symbol table to be written, with the first global sym given index 0.*/ + int def_count; + /* Nonzero means a definition of this global symbol is known to exist. + Library members should not be loaded on its account. */ + char defined; + /* Nonzero means a reference to this global symbol has been seen + in a file that is surely being loaded. + A value higher than 1 is the n_type code for the symbol's + definition. */ + char referenced; + /* A count of the number of undefined references printed for a + specific symbol. If a symbol is unresolved at the end of + digest_symbols (and the loading run is supposed to produce + relocatable output) do_file_warnings keeps track of how many + unresolved reference error messages have been printed for + each symbol here. When the number hits MAX_UREFS_PRINTED, + messages stop. */ + unsigned char undef_refs; + /* 1 means that this symbol has multiple definitions. 2 means + that it has multiple definitions, and some of them are set + elements, one of which has been printed out already. */ + unsigned char multiply_defined; + /* Nonzero means print a message at all refs or defs of this symbol */ + char trace; + } + symbol; + +/* Demangler for C++. */ +extern char *cplus_demangle (); + +/* Demangler function to use. */ +char *(*demangler)() = NULL; + +/* Number of buckets in symbol hash table */ +#define TABSIZE 1009 + +/* The symbol hash table: a vector of TABSIZE pointers to struct glosym. */ +symbol *symtab[TABSIZE]; + +/* Number of symbols in symbol hash table. */ +int num_hash_tab_syms = 0; + +/* Count the number of nlist entries that are for local symbols. + This count and the three following counts + are incremented as as symbols are entered in the symbol table. */ +int local_sym_count; + +/* Count number of nlist entries that are for local symbols + whose names don't start with L. */ +int non_L_local_sym_count; + +/* Count the number of nlist entries for debugger info. */ +int debugger_sym_count; + +/* Count the number of global symbols referenced and not defined. */ +int undefined_global_sym_count; + +/* Count the number of global symbols multiply defined. */ +int multiple_def_count; + +/* Count the number of defined global symbols. + Each symbol is counted only once + regardless of how many different nlist entries refer to it, + since the output file will need only one nlist entry for it. + This count is computed by `digest_symbols'; + it is undefined while symbols are being loaded. */ +int defined_global_sym_count; + +/* Count the number of symbols defined through common declarations. + This count is kept in symdef_library, linear_library, and + enter_global_ref. It is incremented when the defined flag is set + in a symbol because of a common definition, and decremented when + the symbol is defined "for real" (ie. by something besides a common + definition). */ +int common_defined_global_count; + +/* Count the number of set element type symbols and the number of + separate vectors which these symbols will fit into. See the + GNU a.out.h for more info. + This count is computed by 'enter_file_symbols' */ +int set_symbol_count; +int set_vector_count; + +/* Define a linked list of strings which define symbols which should + be treated as set elements even though they aren't. Any symbol + with a prefix matching one of these should be treated as a set + element. + + This is to make up for deficiencies in many assemblers which aren't + willing to pass any stabs through to the loader which they don't + understand. */ +struct string_list_element { + char *str; + struct string_list_element *next; +}; + +struct string_list_element *set_element_prefixes; + +/* Count the number of definitions done indirectly (ie. done relative + to the value of some other symbol. */ +int global_indirect_count; + +/* Count the number of warning symbols encountered. */ +int warning_count; + +/* Total number of symbols to be written in the output file. + Computed by digest_symbols from the variables above. */ +int nsyms; + + +/* Nonzero means ptr to symbol entry for symbol to use as start addr. + -e sets this. */ +symbol *entry_symbol; + +symbol *edata_symbol; /* the symbol _edata */ +symbol *etext_symbol; /* the symbol _etext */ +symbol *end_symbol; /* the symbol _end */ + +/* Each input file, and each library member ("subfile") being loaded, + has a `file_entry' structure for it. + + For files specified by command args, these are contained in the vector + which `file_table' points to. + + For library members, they are dynamically allocated, + and chained through the `chain' field. + The chain is found in the `subfiles' field of the `file_entry'. + The `file_entry' objects for the members have `superfile' fields pointing + to the one for the library. */ + +struct file_entry { + /* Name of this file. */ + char *filename; + /* Name to use for the symbol giving address of text start */ + /* Usually the same as filename, but for a file spec'd with -l + this is the -l switch itself rather than the filename. */ + char *local_sym_name; + + /* Describe the layout of the contents of the file */ + + /* The file's a.out header. */ + struct exec header; + /* Offset in file of GDB symbol segment, or 0 if there is none. */ + int symseg_offset; + + /* Describe data from the file loaded into core */ + + /* Symbol table of the file. */ + struct nlist *symbols; + /* Size in bytes of string table. */ + int string_size; + /* Pointer to the string table. + The string table is not kept in core all the time, + but when it is in core, its address is here. */ + char *strings; + + /* Next two used only if `relocatable_output' or if needed for */ + /* output of undefined reference line numbers. */ + + /* Text reloc info saved by `write_text' for `coptxtrel'. */ + struct relocation_info *textrel; + /* Data reloc info saved by `write_data' for `copdatrel'. */ + struct relocation_info *datarel; + + /* Relation of this file's segments to the output file */ + + /* Start of this file's text seg in the output file core image. */ + int text_start_address; + /* Start of this file's data seg in the output file core image. */ + int data_start_address; + /* Start of this file's bss seg in the output file core image. */ + int bss_start_address; + /* Offset in bytes in the output file symbol table + of the first local symbol for this file. Set by `write_file_symbols'. */ + int local_syms_offset; + + /* For library members only */ + + /* For a library, points to chain of entries for the library members. */ + struct file_entry *subfiles; + /* For a library member, offset of the member within the archive. + Zero for files that are not library members. */ + int starting_offset; + /* Size of contents of this file, if library member. */ + int total_size; + /* For library member, points to the library's own entry. */ + struct file_entry *superfile; + /* For library member, points to next entry for next member. */ + struct file_entry *chain; + + /* 1 if file is a library. */ + char library_flag; + + /* 1 if file's header has been read into this structure. */ + char header_read_flag; + + /* 1 means search a set of directories for this file. */ + char search_dirs_flag; + + /* 1 means this is base file of incremental load. + Do not load this file's text or data. + Also default text_start to after this file's bss. */ + char just_syms_flag; +}; + +/* Vector of entries for input files specified by arguments. + These are all the input files except for members of specified libraries. */ +struct file_entry *file_table; + +/* Length of that vector. */ +int number_of_files; + +/* When loading the text and data, we can avoid doing a close + and another open between members of the same library. + + These two variables remember the file that is currently open. + Both are zero if no file is open. + + See `each_file' and `file_close'. */ + +struct file_entry *input_file; +int input_desc; + +/* The name of the file to write; "a.out" by default. */ + +char *output_filename; + +/* Descriptor for writing that file with `mywrite'. */ + +int outdesc; + +/* Header for that file (filled in by `write_header'). */ + +struct exec outheader; + +#ifdef COFF_ENCAPSULATE +struct coffheader coffheader; +int need_coff_header; +#endif + +/* The following are computed by `digest_symbols'. */ + +int text_size; /* total size of text of all input files. */ +int data_size; /* total size of data of all input files. */ +int bss_size; /* total size of bss of all input files. */ +int text_reloc_size; /* total size of text relocation of all input files. */ +int data_reloc_size; /* total size of data relocation of all input */ + /* files. */ + +/* Specifications of start and length of the area reserved at the end + of the text segment for the set vectors. Computed in 'digest_symbols' */ +int set_sect_start; +int set_sect_size; + +/* Pointer for in core storage for the above vectors, before they are + written. */ +unsigned long *set_vectors; + +/* Amount of cleared space to leave between the text and data segments. */ + +int text_pad; + +/* Amount of bss segment to include as part of the data segment. */ + +int data_pad; + +/* Format of __.SYMDEF: + First, a longword containing the size of the 'symdef' data that follows. + Second, zero or more 'symdef' structures. + Third, a longword containing the length of symbol name strings. + Fourth, zero or more symbol name strings (each followed by a null). */ + +struct symdef { + int symbol_name_string_index; + int library_member_offset; +}; + +/* Record most of the command options. */ + +/* Address we assume the text section will be loaded at. + We relocate symbols and text and data for this, but we do not + write any padding in the output file for it. */ +int text_start; + +/* Offset of default entry-pc within the text section. */ +int entry_offset; + +/* Address we decide the data section will be loaded at. */ +int data_start; + +/* `text-start' address is normally this much plus a page boundary. + This is not a user option; it is fixed for each system. */ +int text_start_alignment; + +/* Nonzero if -T was specified in the command line. + This prevents text_start from being set later to default values. */ +int T_flag_specified; + +/* Nonzero if -Tdata was specified in the command line. + This prevents data_start from being set later to default values. */ +int Tdata_flag_specified; + +/* Size to pad data section up to. + We simply increase the size of the data section, padding with zeros, + and reduce the size of the bss section to match. */ +int specified_data_size; + +/* Magic number to use for the output file, set by switch. */ +int magic; + +/* Nonzero means print names of input files as processed. */ +int trace_files; + +/* Which symbols should be stripped (omitted from the output): + none, all, or debugger symbols. */ +enum { STRIP_NONE, STRIP_ALL, STRIP_DEBUGGER } strip_symbols; + +/* Which local symbols should be omitted: + none, all, or those starting with L. + This is irrelevant if STRIP_NONE. */ +enum { DISCARD_NONE, DISCARD_ALL, DISCARD_L } discard_locals; + +/* 1 => write load map. */ +int write_map; + +/* 1 => write relocation into output file so can re-input it later. */ +int relocatable_output; + +/* 1 => assign space to common symbols even if `relocatable_output'. */ +int force_common_definition; + +/* Standard directories to search for files specified by -l. */ +char *standard_search_dirs[] = +#ifdef STANDARD_SEARCH_DIRS + {STANDARD_SEARCH_DIRS}; +#else +#ifdef NON_NATIVE + {"/usr/local/lib/gnu"}; +#else + {"/lib", "/usr/lib", "/usr/local/lib"}; +#endif +#endif + +/* Actual vector of directories to search; + this contains those specified with -L plus the standard ones. */ +char **search_dirs; + +/* Length of the vector `search_dirs'. */ +int n_search_dirs; + +/* Non zero means to create the output executable. */ +/* Cleared by nonfatal errors. */ +int make_executable; + +/* Force the executable to be output, even if there are non-fatal + errors */ +int force_executable; + +/* Keep a list of any symbols referenced from the command line (so + that error messages for these guys can be generated). This list is + zero terminated. */ +struct glosym **cmdline_references; +int cl_refs_allocated; + +void bcopy (), bzero (); +int malloc (), realloc (); +#ifndef alloca +int alloca (); +#endif +int free (); + +int xmalloc (); +int xrealloc (); +void fatal (); +void fatal_with_file (); +void perror_name (); +void perror_file (); +void error (); + +void digest_symbols (); +void print_symbols (); +void load_symbols (); +void decode_command (); +void list_undefined_symbols (); +void list_unresolved_references (); +void write_output (); +void write_header (); +void write_text (); +void read_file_relocation (); +void write_data (); +void write_rel (); +void write_syms (); +void write_symsegs (); +void mywrite (); +void symtab_init (); +void padfile (); +char *concat (); +char *get_file_name (); +symbol *getsym (), *getsym_soft (); + +int +main (argc, argv) + char **argv; + int argc; +{ +/* Added this to stop ld core-dumping on very large .o files. */ +#ifdef RLIMIT_STACK + /* Get rid of any avoidable limit on stack size. */ + { + struct rlimit rlim; + + /* Set the stack limit huge so that alloca does not fail. */ + getrlimit (RLIMIT_STACK, &rlim); + rlim.rlim_cur = rlim.rlim_max; + setrlimit (RLIMIT_STACK, &rlim); + } +#endif /* RLIMIT_STACK */ + + page_size = getpagesize (); + progname = argv[0]; + + /* Clear the cumulative info on the output file. */ + + text_size = 0; + data_size = 0; + bss_size = 0; + text_reloc_size = 0; + data_reloc_size = 0; + + data_pad = 0; + text_pad = 0; + + /* Initialize the data about options. */ + + specified_data_size = 0; + strip_symbols = STRIP_NONE; + trace_files = 0; + discard_locals = DISCARD_NONE; + entry_symbol = 0; + write_map = 0; + relocatable_output = 0; + force_common_definition = 0; + T_flag_specified = 0; + Tdata_flag_specified = 0; + magic = DEFAULT_MAGIC; + make_executable = 1; + force_executable = 0; + set_element_prefixes = 0; + + /* Initialize the cumulative counts of symbols. */ + + local_sym_count = 0; + non_L_local_sym_count = 0; + debugger_sym_count = 0; + undefined_global_sym_count = 0; + set_symbol_count = 0; + set_vector_count = 0; + global_indirect_count = 0; + warning_count = 0; + multiple_def_count = 0; + common_defined_global_count = 0; + + /* Keep a list of symbols referenced from the command line */ + cl_refs_allocated = 10; + cmdline_references + = (struct glosym **) xmalloc (cl_refs_allocated + * sizeof(struct glosym *)); + *cmdline_references = 0; + + /* Completely decode ARGV. */ + + decode_command (argc, argv); + + /* Create the symbols `etext', `edata' and `end'. */ + + if (!relocatable_output) + symtab_init (); + + /* Determine whether to count the header as part of + the text size, and initialize the text size accordingly. + This depends on the kind of system and on the output format selected. */ + + N_SET_MAGIC (outheader, magic); +#ifdef INITIALIZE_HEADER + INITIALIZE_HEADER; +#endif + + text_size = sizeof (struct exec); +#ifdef COFF_ENCAPSULATE + if (relocatable_output == 0 && file_table[0].just_syms_flag == 0) + { + need_coff_header = 1; + /* set this flag now, since it will change the values of N_TXTOFF, etc */ + N_SET_FLAGS (outheader, N_FLAGS_COFF_ENCAPSULATE); + text_size += sizeof (struct coffheader); + } +#endif + + text_size -= N_TXTOFF (outheader); + + if (text_size < 0) + text_size = 0; + entry_offset = text_size; + + if (!T_flag_specified && !relocatable_output && !screwballmode) + text_start = TEXT_START (outheader); + + /* The text-start address is normally this far past a page boundary. */ + text_start_alignment = text_start % page_size; + + /* Load symbols of all input files. + Also search all libraries and decide which library members to load. */ + + load_symbols (); + + /* Compute where each file's sections go, and relocate symbols. */ + + digest_symbols (); + + /* Print error messages for any missing symbols, for any warning + symbols, and possibly multiple definitions */ + + do_warnings (stderr); + + /* Print a map, if requested. */ + + if (write_map) print_symbols (stdout); + + /* Write the output file. */ + + if (make_executable || force_executable) + write_output (); + + exit (!make_executable); +} + +void decode_option (); + +/* Analyze a command line argument. + Return 0 if the argument is a filename. + Return 1 if the argument is a option complete in itself. + Return 2 if the argument is a option which uses an argument. + + Thus, the value is the number of consecutive arguments + that are part of options. */ + +int +classify_arg (arg) + register char *arg; +{ + if (*arg != '-') return 0; + switch (arg[1]) + { + case 'A': + case 'D': + case 'e': + case 'L': + case 'l': + case 'o': + case 'u': + case 'V': + case 'y': + if (arg[2]) + return 1; + return 2; + + case 'B': + if (! strcmp (&arg[2], "static")) + return 1; + + case 'T': + if (arg[2] == 0) + return 2; + if (! strcmp (&arg[2], "text")) + return 2; + if (! strcmp (&arg[2], "data")) + return 2; + return 1; + } + + return 1; +} + +/* Process the command arguments, + setting up file_table with an entry for each input file, + and setting variables according to the options. */ + +void +decode_command (argc, argv) + char **argv; + int argc; +{ + register int i; + register struct file_entry *p; + char *cp; + + number_of_files = 0; + output_filename = "a.out"; + + n_search_dirs = 0; + search_dirs = (char **) xmalloc (sizeof (char *)); + + /* First compute number_of_files so we know how long to make file_table. */ + /* Also process most options completely. */ + + for (i = 1; i < argc; i++) + { + register int code = classify_arg (argv[i]); + if (code) + { + if (i + code > argc) + fatal ("no argument following %s\n", argv[i]); + + decode_option (argv[i], argv[i+1]); + + if (argv[i][1] == 'l' || argv[i][1] == 'A') + number_of_files++; + + i += code - 1; + } + else + number_of_files++; + } + + if (!number_of_files) + fatal ("no input files", 0); + + p = file_table + = (struct file_entry *) xmalloc (number_of_files * sizeof (struct file_entry)); + bzero (p, number_of_files * sizeof (struct file_entry)); + + /* Now scan again and fill in file_table. */ + /* All options except -A and -l are ignored here. */ + + for (i = 1; i < argc; i++) + { + register int code = classify_arg (argv[i]); + + if (code) + { + char *string; + if (code == 2) + string = argv[i+1]; + else + string = &argv[i][2]; + + if (argv[i][1] == 'A') + { + if (p != file_table) + fatal ("-A specified before an input file other than the first"); + + p->filename = string; + p->local_sym_name = string; + p->just_syms_flag = 1; + p++; + } + if (argv[i][1] == 'l') + { + if (cp = rindex(string, '/')) + { + *cp++ = '\0'; + cp = concat (string, "/lib", cp); + p->filename = concat (cp, ".a", ""); + } + else + p->filename = concat ("lib", string, ".a"); + + p->local_sym_name = concat ("-l", string, ""); + p->search_dirs_flag = 1; + p++; + } + i += code - 1; + } + else + { + p->filename = argv[i]; + p->local_sym_name = argv[i]; + p++; + } + } + + /* Now check some option settings for consistency. */ + +#ifdef NMAGIC + if ((magic == ZMAGIC || magic == NMAGIC) +#else + if ((magic == ZMAGIC) +#endif + && (text_start - text_start_alignment) & (page_size - 1)) + fatal ("-T argument not multiple of page size, with sharable output", 0); + + /* Append the standard search directories to the user-specified ones. */ + { + int n = sizeof standard_search_dirs / sizeof standard_search_dirs[0]; + n_search_dirs += n; + search_dirs + = (char **) xrealloc (search_dirs, n_search_dirs * sizeof (char *)); + bcopy (standard_search_dirs, &search_dirs[n_search_dirs - n], + n * sizeof (char *)); + } +} + + +void +add_cmdline_ref (sp) + struct glosym *sp; +{ + struct glosym **ptr; + + for (ptr = cmdline_references; + ptr < cmdline_references + cl_refs_allocated && *ptr; + ptr++) + ; + + if (ptr >= cmdline_references + cl_refs_allocated - 1) + { + int diff = ptr - cmdline_references; + + cl_refs_allocated *= 2; + cmdline_references = (struct glosym **) + xrealloc (cmdline_references, + cl_refs_allocated * sizeof (struct glosym *)); + ptr = cmdline_references + diff; + } + + *ptr++ = sp; + *ptr = (struct glosym *) 0; +} + +int +set_element_prefixed_p (name) + char *name; +{ + struct string_list_element *p; + int i; + + for (p = set_element_prefixes; p; p = p->next) + { + for (i = 0; p->str[i] != '\0' && (p->str[i] == name[i]); i++) + ; + + if (p->str[i] == '\0') + return 1; + } + return 0; +} + +int parse (); + +/* Record an option and arrange to act on it later. + ARG should be the following command argument, + which may or may not be used by this option. + + The `l' and `A' options are ignored here since they actually + specify input files. */ + +void +decode_option (swt, arg) + register char *swt, *arg; +{ + /* We get Bstatic from gcc on suns. */ + if (! strcmp (swt + 1, "Bstatic")) + return; + if (! strcmp (swt + 1, "Ttext")) + { + text_start = parse (arg, "%x", "invalid argument to -Ttext"); + T_flag_specified = 1; + return; + } + if (! strcmp (swt + 1, "Tdata")) + { + data_start = parse (arg, "%x", "invalid argument to -Tdata"); + Tdata_flag_specified = 1; + return; + } + if (! strcmp (swt + 1, "noinhibit-exec")) + { + force_executable = 1; + return; + } + if (! strcmp (swt + 1, "screwballmode")) + { + screwballmode = 1; + magic = OMAGIC; + text_start = sizeof(struct exec); + return; + } + + if (swt[2] != 0) + arg = &swt[2]; + + switch (swt[1]) + { + case 'A': + return; + + case 'D': + specified_data_size = parse (arg, "%x", "invalid argument to -D"); + return; + + case 'd': + force_common_definition = 1; + return; + + case 'e': + entry_symbol = getsym (arg); + if (!entry_symbol->defined && !entry_symbol->referenced) + undefined_global_sym_count++; + entry_symbol->referenced = 1; + add_cmdline_ref (entry_symbol); + return; + + case 'l': + /* If linking with libg++, use the C++ demangler. */ + if (arg != NULL && strcmp (arg, "g++") == 0) + demangler = cplus_demangle; + return; + magic = OMAGIC; + + case 'L': + n_search_dirs++; + search_dirs + = (char **) xrealloc (search_dirs, n_search_dirs * sizeof (char *)); + search_dirs[n_search_dirs - 1] = arg; + return; + + case 'M': + write_map = 1; + return; + + case 'N': + magic = OMAGIC; +#ifdef notnow +text_start = sizeof(struct exec); /* XXX */ +screwballmode=1; +#endif + return; + +#ifdef NMAGIC + case 'n': + magic = NMAGIC; + return; +#endif + + case 'o': + output_filename = arg; + return; + + case 'r': + relocatable_output = 1; + magic = OMAGIC; + text_start = 0; + return; + + case 'S': + strip_symbols = STRIP_DEBUGGER; + return; + + case 's': + strip_symbols = STRIP_ALL; + return; + + case 'T': + text_start = parse (arg, "%x", "invalid argument to -T"); + T_flag_specified = 1; + return; + + case 't': + trace_files = 1; + return; + + case 'u': + { + register symbol *sp = getsym (arg); + if (!sp->defined && !sp->referenced) + undefined_global_sym_count++; + sp->referenced = 1; + add_cmdline_ref (sp); + } + return; + + case 'V': + { + struct string_list_element *new + = (struct string_list_element *) + xmalloc (sizeof (struct string_list_element)); + + new->str = arg; + new->next = set_element_prefixes; + set_element_prefixes = new; + return; + } + + case 'X': + discard_locals = DISCARD_L; + return; + + case 'x': + discard_locals = DISCARD_ALL; + return; + + case 'y': + { + register symbol *sp = getsym (&swt[2]); + sp->trace = 1; + } + return; + + case 'z': + magic = ZMAGIC; + return; + + default: + fatal ("invalid command option `%s'", swt); + } +} + +/** Convenient functions for operating on one or all files being */ + /** loaded. */ +void print_file_name (); + +/* Call FUNCTION on each input file entry. + Do not call for entries for libraries; + instead, call once for each library member that is being loaded. + + FUNCTION receives two arguments: the entry, and ARG. */ + +void +each_file (function, arg) + register void (*function)(); + register int arg; +{ + register int i; + + for (i = 0; i < number_of_files; i++) + { + register struct file_entry *entry = &file_table[i]; + if (entry->library_flag) + { + register struct file_entry *subentry = entry->subfiles; + for (; subentry; subentry = subentry->chain) + (*function) (subentry, arg); + } + else + (*function) (entry, arg); + } +} + +/* Call FUNCTION on each input file entry until it returns a non-zero + value. Return this value. + Do not call for entries for libraries; + instead, call once for each library member that is being loaded. + + FUNCTION receives two arguments: the entry, and ARG. It must be a + function returning unsigned long (though this can probably be fudged). */ + +unsigned long +check_each_file (function, arg) + register unsigned long (*function)(); + register int arg; +{ + register int i; + register unsigned long return_val; + + for (i = 0; i < number_of_files; i++) + { + register struct file_entry *entry = &file_table[i]; + if (entry->library_flag) + { + register struct file_entry *subentry = entry->subfiles; + for (; subentry; subentry = subentry->chain) + if (return_val = (*function) (subentry, arg)) + return return_val; + } + else + if (return_val = (*function) (entry, arg)) + return return_val; + } + return 0; +} + +/* Like `each_file' but ignore files that were just for symbol definitions. */ + +void +each_full_file (function, arg) + register void (*function)(); + register int arg; +{ + register int i; + + for (i = 0; i < number_of_files; i++) + { + register struct file_entry *entry = &file_table[i]; + if (entry->just_syms_flag) + continue; + if (entry->library_flag) + { + register struct file_entry *subentry = entry->subfiles; + for (; subentry; subentry = subentry->chain) + (*function) (subentry, arg); + } + else + (*function) (entry, arg); + } +} + +/* Close the input file that is now open. */ + +void +file_close () +{ + close (input_desc); + input_desc = 0; + input_file = 0; +} + +/* Open the input file specified by 'entry', and return a descriptor. + The open file is remembered; if the same file is opened twice in a row, + a new open is not actually done. */ + +int +file_open (entry) + register struct file_entry *entry; +{ + register int desc; + + if (entry->superfile) + return file_open (entry->superfile); + + if (entry == input_file) + return input_desc; + + if (input_file) file_close (); + + if (entry->search_dirs_flag) + { + int i; + + for (i = 0; i < n_search_dirs; i++) + { + register char *string + = concat (search_dirs[i], "/", entry->filename); + desc = open (string, O_RDONLY, 0); + if (desc > 0) + { + entry->filename = string; + entry->search_dirs_flag = 0; + break; + } + free (string); + } + } + else + desc = open (entry->filename, O_RDONLY, 0); + + if (desc > 0) + { + input_file = entry; + input_desc = desc; + return desc; + } + + perror_file (entry); + /* NOTREACHED */ +} + +/* Print the filename of ENTRY on OUTFILE (a stdio stream), + and then a newline. */ + +void +prline_file_name (entry, outfile) + struct file_entry *entry; + FILE *outfile; +{ + print_file_name (entry, outfile); + fprintf (outfile, "\n"); +} + +/* Print the filename of ENTRY on OUTFILE (a stdio stream). */ + +void +print_file_name (entry, outfile) + struct file_entry *entry; + FILE *outfile; +{ + if (entry->superfile) + { + print_file_name (entry->superfile, outfile); + fprintf (outfile, "(%s)", entry->filename); + } + else + fprintf (outfile, "%s", entry->filename); +} + +/* Return the filename of entry as a string (malloc'd for the purpose) */ + +char * +get_file_name (entry) + struct file_entry *entry; +{ + char *result, *supfile; + if (entry->superfile) + { + supfile = get_file_name (entry->superfile); + result = (char *) xmalloc (strlen (supfile) + + strlen (entry->filename) + 3); + sprintf (result, "%s(%s)", supfile, entry->filename); + free (supfile); + } + else + { + result = (char *) xmalloc (strlen (entry->filename) + 1); + strcpy (result, entry->filename); + } + return result; +} + +/* Medium-level input routines for rel files. */ + +/* Read a file's header into the proper place in the file_entry. + DESC is the descriptor on which the file is open. + ENTRY is the file's entry. */ + +void +read_header (desc, entry) + int desc; + register struct file_entry *entry; +{ + register int len; + struct exec *loc = (struct exec *) &entry->header; + + lseek (desc, entry->starting_offset, 0); +#ifdef COFF_ENCAPSULATE + if (entry->just_syms_flag) + lseek (desc, sizeof(coffheader), 1); +#endif + len = read (desc, loc, sizeof (struct exec)); + if (len != sizeof (struct exec)) + fatal_with_file ("failure reading header of ", entry); + if (N_BADMAG (*loc)) + fatal_with_file ("bad magic number in ", entry); + + entry->header_read_flag = 1; +} + +/* Read the symbols of file ENTRY into core. + Assume it is already open, on descriptor DESC. + Also read the length of the string table, which follows the symbol table, + but don't read the contents of the string table. */ + +void +read_entry_symbols (desc, entry) + struct file_entry *entry; + int desc; +{ + int str_size; + + if (!entry->header_read_flag) + read_header (desc, entry); + + entry->symbols = (struct nlist *) xmalloc (entry->header.a_syms); + + lseek (desc, N_SYMOFF (entry->header) + entry->starting_offset, 0); + if (entry->header.a_syms != read (desc, entry->symbols, entry->header.a_syms)) + fatal_with_file ("premature end of file in symbols of ", entry); + + lseek (desc, N_STROFF (entry->header) + entry->starting_offset, 0); + if (sizeof str_size != read (desc, &str_size, sizeof str_size)) + fatal_with_file ("bad string table size in ", entry); + + entry->string_size = str_size; +} + +/* Read the string table of file ENTRY into core. + Assume it is already open, on descriptor DESC. + Also record whether a GDB symbol segment follows the string table. */ + +void +read_entry_strings (desc, entry) + struct file_entry *entry; + int desc; +{ + int buffer; + + if (!entry->header_read_flag) + read_header (desc, entry); + + lseek (desc, N_STROFF (entry->header) + entry->starting_offset, 0); + if (entry->string_size != read (desc, entry->strings, entry->string_size)) + fatal_with_file ("premature end of file in strings of ", entry); + +#if 0 + /* While we are here, see if the file has a symbol segment at the end. + For a separate file, just try reading some more. + For a library member, compare current pos against total size. */ + if (entry->superfile) + { + if (entry->total_size == N_STROFF (entry->header) + entry->string_size) + return; + } + else + { + buffer = read (desc, &buffer, sizeof buffer); + if (buffer == 0) + return; + if (buffer != sizeof buffer) + fatal_with_file ("premature end of file in GDB symbol segment of ", entry); + } +#endif + /* Don't try to do anything with symsegs. */ + return; +#if 0 + /* eliminate warning of `statement not reached'. */ + entry->symseg_offset = N_STROFF (entry->header) + entry->string_size; +#endif +} + +/* Read in the symbols of all input files. */ + +void read_file_symbols (), read_entry_symbols (), read_entry_strings (); +void enter_file_symbols (), enter_global_ref (), search_library (); + +void +load_symbols () +{ + register int i; + + if (trace_files) fprintf (stderr, "Loading symbols:\n\n"); + + for (i = 0; i < number_of_files; i++) + { + register struct file_entry *entry = &file_table[i]; + read_file_symbols (entry); + } + + if (trace_files) fprintf (stderr, "\n"); +} + +/* If ENTRY is a rel file, read its symbol and string sections into core. + If it is a library, search it and load the appropriate members + (which means calling this function recursively on those members). */ + +void +read_file_symbols (entry) + register struct file_entry *entry; +{ + register int desc; + register int len; + struct exec hdr; + + desc = file_open (entry); + +#ifdef COFF_ENCAPSULATE + if (entry->just_syms_flag) + lseek (desc, sizeof(coffheader),0); +#endif + + len = read (desc, &hdr, sizeof hdr); + if (len != sizeof hdr) + fatal_with_file ("failure reading header of ", entry); + + if (!N_BADMAG (hdr)) + { + read_entry_symbols (desc, entry); + entry->strings = (char *) alloca (entry->string_size); + read_entry_strings (desc, entry); + enter_file_symbols (entry); + entry->strings = 0; + } + else + { + char armag[SARMAG]; + + lseek (desc, 0, 0); + if (SARMAG != read (desc, armag, SARMAG) || strncmp (armag, ARMAG, SARMAG)) + fatal_with_file ("malformed input file (not rel or archive) ", entry); + entry->library_flag = 1; + search_library (desc, entry); + } + + file_close (); +} + +/* Enter the external symbol defs and refs of ENTRY in the hash table. */ + +void +enter_file_symbols (entry) + struct file_entry *entry; +{ + register struct nlist + *p, + *end = entry->symbols + entry->header.a_syms / sizeof (struct nlist); + + if (trace_files) prline_file_name (entry, stderr); + + for (p = entry->symbols; p < end; p++) + { + if (p->n_type == (N_SETV | N_EXT)) continue; + if (set_element_prefixes + && set_element_prefixed_p (p->n_un.n_strx + entry->strings)) + p->n_type += (N_SETA - N_ABS); + + if (SET_ELEMENT_P (p->n_type)) + { + set_symbol_count++; + if (!relocatable_output) + enter_global_ref (p, p->n_un.n_strx + entry->strings, entry); + } + else if (p->n_type == N_WARNING) + { + char *name = p->n_un.n_strx + entry->strings; + + /* Grab the next entry. */ + p++; + if (p->n_type != (N_UNDF | N_EXT)) + { + fprintf (stderr, "%s: Warning symbol found in %s without external reference following.\n", + progname, entry->filename); + make_executable = 0; + p--; /* Process normally. */ + } + else + { + symbol *sp; + char *sname = p->n_un.n_strx + entry->strings; + /* Deal with the warning symbol. */ + enter_global_ref (p, p->n_un.n_strx + entry->strings, entry); + sp = getsym (sname); + sp->warning = (char *) xmalloc (strlen(name) + 1); + strcpy (sp->warning, name); + warning_count++; + } + } + else if (p->n_type & N_EXT) + enter_global_ref (p, p->n_un.n_strx + entry->strings, entry); + else if (p->n_un.n_strx && !(p->n_type & (N_STAB | N_EXT))) + { + if ((p->n_un.n_strx + entry->strings)[0] != LPREFIX) + non_L_local_sym_count++; + local_sym_count++; + } + else debugger_sym_count++; + } + + /* Count one for the local symbol that we generate, + whose name is the file's name (usually) and whose address + is the start of the file's text. */ + + local_sym_count++; + non_L_local_sym_count++; +} + +/* Enter one global symbol in the hash table. + NLIST_P points to the `struct nlist' read from the file + that describes the global symbol. NAME is the symbol's name. + ENTRY is the file entry for the file the symbol comes from. + + The `struct nlist' is modified by placing it on a chain of + all such structs that refer to the same global symbol. + This chain starts in the `refs' field of the symbol table entry + and is chained through the `n_name'. */ + +void +enter_global_ref (nlist_p, name, entry) + register struct nlist *nlist_p; + char *name; + struct file_entry *entry; +{ + register symbol *sp = getsym (name); + register int type = nlist_p->n_type; + int oldref = sp->referenced; + int olddef = sp->defined; + + nlist_p->n_un.n_name = (char *) sp->refs; + sp->refs = nlist_p; + + sp->referenced = 1; + if (type != (N_UNDF | N_EXT) || nlist_p->n_value) + { + if (!sp->defined || sp->defined == (N_UNDF | N_EXT)) + sp->defined = type; + + if (oldref && !olddef) + /* It used to be undefined and we're defining it. */ + undefined_global_sym_count--; + + if (!olddef && type == (N_UNDF | N_EXT) && nlist_p->n_value) + { + /* First definition and it's common. */ + common_defined_global_count++; + sp->max_common_size = nlist_p->n_value; + } + else if (olddef && sp->max_common_size && type != (N_UNDF | N_EXT)) + { + /* It used to be common and we're defining it as + something else. */ + common_defined_global_count--; + sp->max_common_size = 0; + } + else if (olddef && sp->max_common_size && type == (N_UNDF | N_EXT) + && sp->max_common_size < nlist_p->n_value) + /* It used to be common and this is a new common entry to + which we need to pay attention. */ + sp->max_common_size = nlist_p->n_value; + + /* Are we defining it as a set element? */ + if (SET_ELEMENT_P (type) + && (!olddef || (olddef && sp->max_common_size))) + set_vector_count++; + /* As an indirection? */ + else if (type == (N_INDR | N_EXT)) + { + /* Indirect symbols value should be modified to point + a symbol being equivalenced to. */ + nlist_p->n_value + = (unsigned int) getsym ((nlist_p + 1)->n_un.n_strx + + entry->strings); + if ((symbol *) nlist_p->n_value == sp) + { + /* Somebody redefined a symbol to be itself. */ + fprintf (stderr, "%s: Symbol %s indirected to itself.\n", + entry->filename, name); + /* Rewrite this symbol as being a global text symbol + with value 0. */ + nlist_p->n_type = sp->defined = N_TEXT | N_EXT; + nlist_p->n_value = 0; + /* Don't make the output executable. */ + make_executable = 0; + } + else + global_indirect_count++; + } + } + else + if (!oldref) +#ifndef DOLLAR_KLUDGE + undefined_global_sym_count++; +#else + { + if (entry->superfile && type == (N_UNDF | N_EXT) && name[1] == '$') + { + /* This is an (ISI?) $-conditional; skip it */ + sp->referenced = 0; + if (sp->trace) + { + fprintf (stderr, "symbol %s is a $-conditional ignored in ", sp->name); + print_file_name (entry, stderr); + fprintf (stderr, "\n"); + } + return; + } + else + undefined_global_sym_count++; + } +#endif + + if (sp == end_symbol && entry->just_syms_flag && !T_flag_specified + && !screwballmode) + text_start = nlist_p->n_value; + + if (sp->trace) + { + register char *reftype; + switch (type & N_TYPE) + { + case N_UNDF: + if (nlist_p->n_value) + reftype = "defined as common"; + else reftype = "referenced"; + break; + + case N_ABS: + reftype = "defined as absolute"; + break; + + case N_TEXT: + reftype = "defined in text section"; + break; + + case N_DATA: + reftype = "defined in data section"; + break; + + case N_BSS: + reftype = "defined in BSS section"; + break; + + case N_SETT: + reftype = "is a text set element"; + break; + + case N_SETD: + reftype = "is a data set element"; + break; + + case N_SETB: + reftype = "is a BSS set element"; + break; + + case N_SETA: + reftype = "is an absolute set element"; + break; + + case N_SETV: + reftype = "defined in data section as vector"; + break; + + case N_INDR: + reftype = (char *) alloca (23 + + strlen ((nlist_p + 1)->n_un.n_strx + + entry->strings)); + sprintf (reftype, "defined equivalent to %s", + (nlist_p + 1)->n_un.n_strx + entry->strings); + break; + +#ifdef sequent + case N_SHUNDF: + reftype = "shared undf"; + break; + +/* These conflict with cases above. + case N_SHDATA: + reftype = "shared data"; + break; + + case N_SHBSS: + reftype = "shared BSS"; + break; +*/ + default: + reftype = "I don't know this type"; + break; +#endif + } + + fprintf (stderr, "symbol %s %s in ", sp->name, reftype); + print_file_name (entry, stderr); + fprintf (stderr, "\n"); + } +} + +/* This return 0 if the given file entry's symbol table does *not* + contain the nlist point entry, and it returns the files entry + pointer (cast to unsigned long) if it does. */ + +unsigned long +contains_symbol (entry, n_ptr) + struct file_entry *entry; + register struct nlist *n_ptr; +{ + if (n_ptr >= entry->symbols && + n_ptr < (entry->symbols + + (entry->header.a_syms / sizeof (struct nlist)))) + return (unsigned long) entry; + return 0; +} + + +/* Searching libraries */ + +struct file_entry *decode_library_subfile (); +void linear_library (), symdef_library (); + +/* Search the library ENTRY, already open on descriptor DESC. + This means deciding which library members to load, + making a chain of `struct file_entry' for those members, + and entering their global symbols in the hash table. */ + +void +search_library (desc, entry) + int desc; + struct file_entry *entry; +{ + int member_length; + register char *name; + register struct file_entry *subentry; + + if (!undefined_global_sym_count) return; + + /* Examine its first member, which starts SARMAG bytes in. */ + subentry = decode_library_subfile (desc, entry, SARMAG, &member_length); + if (!subentry) return; + + name = subentry->filename; + free (subentry); + + /* Search via __.SYMDEF if that exists, else linearly. */ + + if (!strcmp (name, "__.SYMDEF")) + symdef_library (desc, entry, member_length); + else + linear_library (desc, entry); +} + +/* Construct and return a file_entry for a library member. + The library's file_entry is library_entry, and the library is open on DESC. + SUBFILE_OFFSET is the byte index in the library of this member's header. + We store the length of the member into *LENGTH_LOC. */ + +struct file_entry * +decode_library_subfile (desc, library_entry, subfile_offset, length_loc) + int desc; + struct file_entry *library_entry; + int subfile_offset; + int *length_loc; +{ + int bytes_read; + register int namelen; + int member_length; + register char *name; + struct ar_hdr hdr1; + register struct file_entry *subentry; + + lseek (desc, subfile_offset, 0); + + bytes_read = read (desc, &hdr1, sizeof hdr1); + if (!bytes_read) + return 0; /* end of archive */ + + if (sizeof hdr1 != bytes_read) + fatal_with_file ("malformed library archive ", library_entry); + + if (sscanf (hdr1.ar_size, "%d", &member_length) != 1) + fatal_with_file ("malformatted header of archive member in ", library_entry); + + subentry = (struct file_entry *) xmalloc (sizeof (struct file_entry)); + bzero (subentry, sizeof (struct file_entry)); + + for (namelen = 0; + namelen < sizeof hdr1.ar_name + && hdr1.ar_name[namelen] != 0 && hdr1.ar_name[namelen] != ' ' + && hdr1.ar_name[namelen] != '/'; + namelen++); + + name = (char *) xmalloc (namelen+1); + strncpy (name, hdr1.ar_name, namelen); + name[namelen] = 0; + + subentry->filename = name; + subentry->local_sym_name = name; + subentry->symbols = 0; + subentry->strings = 0; + subentry->subfiles = 0; + subentry->starting_offset = subfile_offset + sizeof hdr1; + subentry->superfile = library_entry; + subentry->library_flag = 0; + subentry->header_read_flag = 0; + subentry->just_syms_flag = 0; + subentry->chain = 0; + subentry->total_size = member_length; + + (*length_loc) = member_length; + + return subentry; +} + +int subfile_wanted_p (); + +/* Search a library that has a __.SYMDEF member. + DESC is a descriptor on which the library is open. + The file pointer is assumed to point at the __.SYMDEF data. + ENTRY is the library's file_entry. + MEMBER_LENGTH is the length of the __.SYMDEF data. */ + +void +symdef_library (desc, entry, member_length) + int desc; + struct file_entry *entry; + int member_length; +{ + int *symdef_data = (int *) xmalloc (member_length); + register struct symdef *symdef_base; + char *sym_name_base; + int number_of_symdefs; + int length_of_strings; + int not_finished; + int bytes_read; + register int i; + struct file_entry *prev = 0; + int prev_offset = 0; + + bytes_read = read (desc, symdef_data, member_length); + if (bytes_read != member_length) + fatal_with_file ("malformatted __.SYMDEF in ", entry); + + number_of_symdefs = *symdef_data / sizeof (struct symdef); + if (number_of_symdefs < 0 || + number_of_symdefs * sizeof (struct symdef) + 2 * sizeof (int) > member_length) + fatal_with_file ("malformatted __.SYMDEF in ", entry); + + symdef_base = (struct symdef *) (symdef_data + 1); + length_of_strings = *(int *) (symdef_base + number_of_symdefs); + + if (length_of_strings < 0 + || number_of_symdefs * sizeof (struct symdef) + length_of_strings + + 2 * sizeof (int) > member_length) + fatal_with_file ("malformatted __.SYMDEF in ", entry); + + sym_name_base = sizeof (int) + (char *) (symdef_base + number_of_symdefs); + + /* Check all the string indexes for validity. */ + + for (i = 0; i < number_of_symdefs; i++) + { + register int index = symdef_base[i].symbol_name_string_index; + if (index < 0 || index >= length_of_strings + || (index && *(sym_name_base + index - 1))) + fatal_with_file ("malformatted __.SYMDEF in ", entry); + } + + /* Search the symdef data for members to load. + Do this until one whole pass finds nothing to load. */ + + not_finished = 1; + while (not_finished) + { + not_finished = 0; + + /* Scan all the symbols mentioned in the symdef for ones that we need. + Load the library members that contain such symbols. */ + + for (i = 0; + (i < number_of_symdefs + && (undefined_global_sym_count || common_defined_global_count)); + i++) + if (symdef_base[i].symbol_name_string_index >= 0) + { + register symbol *sp; + + sp = getsym_soft (sym_name_base + + symdef_base[i].symbol_name_string_index); + + /* If we find a symbol that appears to be needed, think carefully + about the archive member that the symbol is in. */ + + if (sp && ((sp->referenced && !sp->defined) + || (sp->defined && sp->max_common_size))) + { + int junk; + register int j; + register int offset = symdef_base[i].library_member_offset; + struct file_entry *subentry; + + /* Don't think carefully about any archive member + more than once in a given pass. */ + + if (prev_offset == offset) + continue; + prev_offset = offset; + + /* Read the symbol table of the archive member. */ + + subentry = decode_library_subfile (desc, entry, offset, &junk); + if (subentry == 0) + fatal ("invalid offset for %s in symbol table of %s", + sym_name_base + + symdef_base[i].symbol_name_string_index, + entry->filename); + read_entry_symbols (desc, subentry); + subentry->strings = (char *) malloc (subentry->string_size); + read_entry_strings (desc, subentry); + + /* Now scan the symbol table and decide whether to load. */ + + if (!subfile_wanted_p (subentry)) + { + free (subentry->symbols); + free (subentry); + } + else + { + /* This member is needed; load it. + Since we are loading something on this pass, + we must make another pass through the symdef data. */ + + not_finished = 1; + + enter_file_symbols (subentry); + + if (prev) + prev->chain = subentry; + else entry->subfiles = subentry; + prev = subentry; + + /* Clear out this member's symbols from the symdef data + so that following passes won't waste time on them. */ + + for (j = 0; j < number_of_symdefs; j++) + { + if (symdef_base[j].library_member_offset == offset) + symdef_base[j].symbol_name_string_index = -1; + } + } + + /* We'll read the strings again if we need them again. */ + free (subentry->strings); + subentry->strings = 0; + } + } + } + + free (symdef_data); +} + +/* Search a library that has no __.SYMDEF. + ENTRY is the library's file_entry. + DESC is the descriptor it is open on. */ + +void +linear_library (desc, entry) + int desc; + struct file_entry *entry; +{ + register struct file_entry *prev = 0; + register int this_subfile_offset = SARMAG; + + while (undefined_global_sym_count || common_defined_global_count) + { + int member_length; + register struct file_entry *subentry; + + subentry = decode_library_subfile (desc, entry, this_subfile_offset, + &member_length); + + if (!subentry) return; + + read_entry_symbols (desc, subentry); + subentry->strings = (char *) alloca (subentry->string_size); + read_entry_strings (desc, subentry); + + if (!subfile_wanted_p (subentry)) + { + free (subentry->symbols); + free (subentry); + } + else + { + enter_file_symbols (subentry); + + if (prev) + prev->chain = subentry; + else entry->subfiles = subentry; + prev = subentry; + subentry->strings = 0; /* Since space will dissapear on return */ + } + + this_subfile_offset += member_length + sizeof (struct ar_hdr); + if (this_subfile_offset & 1) this_subfile_offset++; + } +} + +/* ENTRY is an entry for a library member. + Its symbols have been read into core, but not entered. + Return nonzero if we ought to load this member. */ + +int +subfile_wanted_p (entry) + struct file_entry *entry; +{ + register struct nlist *p; + register struct nlist *end + = entry->symbols + entry->header.a_syms / sizeof (struct nlist); +#ifdef DOLLAR_KLUDGE + register int dollar_cond = 0; +#endif + + for (p = entry->symbols; p < end; p++) + { + register int type = p->n_type; + register char *name = p->n_un.n_strx + entry->strings; + + /* If the symbol has an interesting definition, we could + potentially want it. */ + if (type & N_EXT + && (type != (N_UNDF | N_EXT) || p->n_value + +#ifdef DOLLAR_KLUDGE + || name[1] == '$' +#endif + ) + && !SET_ELEMENT_P (type) + && !set_element_prefixed_p (name)) + { + register symbol *sp = getsym_soft (name); + +#ifdef DOLLAR_KLUDGE + if (name[1] == '$') + { + sp = getsym_soft (&name[2]); + dollar_cond = 1; + if (!sp) continue; + if (sp->referenced) + { + if (write_map) + { + print_file_name (entry, stdout); + fprintf (stdout, " needed due to $-conditional %s\n", name); + } + return 1; + } + continue; + } +#endif + + /* If this symbol has not been hashed, we can't be looking for it. */ + + if (!sp) continue; + + if ((sp->referenced && !sp->defined) + || (sp->defined && sp->max_common_size && (type & N_TEXT) == 0)) + { + /* This is a symbol we are looking for. It is either + not yet defined or defined as a common. */ +#ifdef DOLLAR_KLUDGE + if (dollar_cond) continue; +#endif + if (type == (N_UNDF | N_EXT)) + { + /* Symbol being defined as common. + Remember this, but don't load subfile just for this. */ + + /* If it didn't used to be common, up the count of + common symbols. */ + if (!sp->max_common_size) + common_defined_global_count++; + + if (sp->max_common_size < p->n_value) + sp->max_common_size = p->n_value; + if (!sp->defined) + undefined_global_sym_count--; + sp->defined = 1; + continue; + } + + if (write_map) + { + print_file_name (entry, stdout); + fprintf (stdout, " needed due to %s\n", sp->name); + } + return 1; + } + } + } + + return 0; +} + +void consider_file_section_lengths (), relocate_file_addresses (); + +/* Having entered all the global symbols and found the sizes of sections + of all files to be linked, make all appropriate deductions from this data. + + We propagate global symbol values from definitions to references. + We compute the layout of the output file and where each input file's + contents fit into it. */ + +void +digest_symbols () +{ + register int i; + int setv_fill_count; + + if (trace_files) + fprintf (stderr, "Digesting symbol information:\n\n"); + + /* Compute total size of sections */ + + each_file (consider_file_section_lengths, 0); + + /* If necessary, pad text section to full page in the file. + Include the padding in the text segment size. */ + + if (magic == ZMAGIC) + { + int text_end = text_size + N_TXTOFF (outheader); + text_pad = ((text_end + page_size - 1) & (- page_size)) - text_end; + text_size += text_pad; + } + +#ifdef _N_BASEADDR + /* SunOS 4.1 N_TXTADDR depends on the value of outheader.a_entry. */ + outheader.a_entry = N_PAGSIZ (outheader); +#endif + + outheader.a_text = text_size; +#ifdef sequent + outheader.a_text += N_ADDRADJ (outheader); +#endif + + /* Make the data segment address start in memory on a suitable boundary. */ + + if (! Tdata_flag_specified) + data_start = N_DATADDR (outheader) + text_start - TEXT_START (outheader); + + /* Set up the set element vector */ + + if (!relocatable_output) + { + /* The set sector size is the number of set elements + a word + for each symbol for the length word at the beginning of the + vector, plus a word for each symbol for a zero at the end of + the vector (for incremental linking). */ + set_sect_size + = (2 * set_symbol_count + set_vector_count) * sizeof (unsigned long); + set_sect_start = data_start + data_size; + data_size += set_sect_size; + set_vectors = (unsigned long *) xmalloc (set_sect_size); + setv_fill_count = 0; + } + + /* Compute start addresses of each file's sections and symbols. */ + + each_full_file (relocate_file_addresses, 0); + + /* Now, for each symbol, verify that it is defined globally at most once. + Put the global value into the symbol entry. + Common symbols are allocated here, in the BSS section. + Each defined symbol is given a '->defined' field + which is the correct N_ code for its definition, + except in the case of common symbols with -r. + Then make all the references point at the symbol entry + instead of being chained together. */ + + defined_global_sym_count = 0; + + for (i = 0; i < TABSIZE; i++) + { + register symbol *sp; + for (sp = symtab[i]; sp; sp = sp->link) + { + /* For each symbol */ + register struct nlist *p, *next; + int defs = 0, com = sp->max_common_size; + struct nlist *first_definition; + for (p = sp->refs; p; p = next) + { + register int type = p->n_type; + + if (SET_ELEMENT_P (type)) + { + if (relocatable_output) + fatal ("internal: global ref to set element with -r"); + if (!defs++) + { + sp->value = set_sect_start + + setv_fill_count++ * sizeof (unsigned long); + sp->defined = N_SETV | N_EXT; + first_definition = p; + } + else if ((sp->defined & ~N_EXT) != N_SETV) + { + sp->multiply_defined = 1; + multiple_def_count++; + } + set_vectors[setv_fill_count++] = p->n_value; + } + else if ((type & N_EXT) && type != (N_UNDF | N_EXT) + && (type & N_TYPE) != N_FN) + { + /* non-common definition */ + if (defs++ && sp->value != p->n_value) + { + sp->multiply_defined = 1; + multiple_def_count++; + } + sp->value = p->n_value; + sp->defined = type; + first_definition = p; + } + next = (struct nlist *) p->n_un.n_name; + p->n_un.n_name = (char *) sp; + } + /* Allocate as common if defined as common and not defined for real */ + if (com && !defs) + { + if (!relocatable_output || force_common_definition) + { + int align = sizeof (int); + + /* Round up to nearest sizeof (int). I don't know + whether this is necessary or not (given that + alignment is taken care of later), but it's + traditional, so I'll leave it in. Note that if + this size alignment is ever removed, ALIGN above + will have to be initialized to 1 instead of + sizeof (int). */ + + com = (com + sizeof (int) - 1) & (- sizeof (int)); + + while (!(com & align)) + align <<= 1; + + align = align > MAX_ALIGNMENT ? MAX_ALIGNMENT : align; + + bss_size = ((((bss_size + data_size + data_start) + + (align - 1)) & (- align)) + - data_size - data_start); + + sp->value = data_start + data_size + bss_size; + sp->defined = N_BSS | N_EXT; + bss_size += com; + if (write_map) + printf ("Allocating common %s: %x at %x\n", + sp->name, com, sp->value); + } + else + { + sp->defined = 0; + undefined_global_sym_count++; + } + } + /* Set length word at front of vector and zero byte at end. + Reverse the vector itself to put it in file order. */ + if ((sp->defined & ~N_EXT) == N_SETV) + { + unsigned long length_word_index + = (sp->value - set_sect_start) / sizeof (unsigned long); + unsigned long i, tmp; + + set_vectors[length_word_index] + = setv_fill_count - 1 - length_word_index; + + /* Reverse the vector. */ + for (i = 1; + i < (setv_fill_count - length_word_index - 1) / 2 + 1; + i++) + { + tmp = set_vectors[length_word_index + i]; + set_vectors[length_word_index + i] + = set_vectors[setv_fill_count - i]; + set_vectors[setv_fill_count - i] = tmp; + } + + set_vectors[setv_fill_count++] = 0; + } + if (sp->defined) + defined_global_sym_count++; + } + } + + if (end_symbol) /* These are null if -r. */ + { + etext_symbol->value = text_size + text_start; + edata_symbol->value = data_start + data_size; + end_symbol->value = data_start + data_size + bss_size; + } + + /* Figure the data_pad now, so that it overlaps with the bss addresses. */ + + if (specified_data_size && specified_data_size > data_size) + data_pad = specified_data_size - data_size; + + if (magic == ZMAGIC) + data_pad = ((data_pad + data_size + page_size - 1) & (- page_size)) + - data_size; + + bss_size -= data_pad; + if (bss_size < 0) bss_size = 0; + + data_size += data_pad; +} + +/* Accumulate the section sizes of input file ENTRY + into the section sizes of the output file. */ + +void +consider_file_section_lengths (entry) + register struct file_entry *entry; +{ + if (entry->just_syms_flag) + return; + + entry->text_start_address = text_size; + /* If there were any vectors, we need to chop them off */ + text_size += entry->header.a_text; + entry->data_start_address = data_size; + data_size += entry->header.a_data; + entry->bss_start_address = bss_size; + bss_size += entry->header.a_bss; + + text_reloc_size += entry->header.a_trsize; + data_reloc_size += entry->header.a_drsize; +} + +/* Determine where the sections of ENTRY go into the output file, + whose total section sizes are already known. + Also relocate the addresses of the file's local and debugger symbols. */ + +void +relocate_file_addresses (entry) + register struct file_entry *entry; +{ + entry->text_start_address += text_start; + /* Note that `data_start' and `data_size' have not yet been + adjusted for `data_pad'. If they had been, we would get the wrong + results here. */ + entry->data_start_address += data_start; + entry->bss_start_address += data_start + data_size; + + { + register struct nlist *p; + register struct nlist *end + = entry->symbols + entry->header.a_syms / sizeof (struct nlist); + + for (p = entry->symbols; p < end; p++) + { + /* If this belongs to a section, update it by the section's start address */ + register int type = p->n_type & N_TYPE; + + switch (type) + { + case N_TEXT: + case N_SETT: + p->n_value += entry->text_start_address; + break; + case N_DATA: + case N_SETV: + case N_SETD: + /* A symbol whose value is in the data section + is present in the input file as if the data section + started at an address equal to the length of the file's text. */ + p->n_value += entry->data_start_address - entry->header.a_text; + break; + case N_BSS: + case N_SETB: + /* likewise for symbols with value in BSS. */ + p->n_value += entry->bss_start_address + - entry->header.a_text - entry->header.a_data; + break; + } + } + } +} + +void describe_file_sections (), list_file_locals (); + +/* Print a complete or partial map of the output file. */ + +void +print_symbols (outfile) + FILE *outfile; +{ + register int i; + + fprintf (outfile, "\nFiles:\n\n"); + + each_file (describe_file_sections, outfile); + + fprintf (outfile, "\nGlobal symbols:\n\n"); + + for (i = 0; i < TABSIZE; i++) + { + register symbol *sp; + for (sp = symtab[i]; sp; sp = sp->link) + { + if (sp->defined == 1) + fprintf (outfile, " %s: common, length 0x%x\n", sp->name, sp->max_common_size); + if (sp->defined) + fprintf (outfile, " %s: 0x%x\n", sp->name, sp->value); + else if (sp->referenced) + fprintf (outfile, " %s: undefined\n", sp->name); + } + } + + each_file (list_file_locals, outfile); +} + +void +describe_file_sections (entry, outfile) + struct file_entry *entry; + FILE *outfile; +{ + fprintf (outfile, " "); + print_file_name (entry, outfile); + if (entry->just_syms_flag) + fprintf (outfile, " symbols only\n", 0); + else + fprintf (outfile, " text %x(%x), data %x(%x), bss %x(%x) hex\n", + entry->text_start_address, entry->header.a_text, + entry->data_start_address, entry->header.a_data, + entry->bss_start_address, entry->header.a_bss); +} + +void +list_file_locals (entry, outfile) + struct file_entry *entry; + FILE *outfile; +{ + register struct nlist + *p, + *end = entry->symbols + entry->header.a_syms / sizeof (struct nlist); + + entry->strings = (char *) alloca (entry->string_size); + read_entry_strings (file_open (entry), entry); + + fprintf (outfile, "\nLocal symbols of "); + print_file_name (entry, outfile); + fprintf (outfile, ":\n\n"); + + for (p = entry->symbols; p < end; p++) + /* If this is a definition, + update it if necessary by this file's start address. */ + if (!(p->n_type & (N_STAB | N_EXT))) + fprintf (outfile, " %s: 0x%x\n", + entry->strings + p->n_un.n_strx, p->n_value); + + entry->strings = 0; /* All done with them. */ +} + + +/* Static vars for do_warnings and subroutines of it */ +int list_unresolved_refs; /* List unresolved refs */ +int list_warning_symbols; /* List warning syms */ +int list_multiple_defs; /* List multiple definitions */ + +/* + * Structure for communication between do_file_warnings and it's + * helper routines. Will in practice be an array of three of these: + * 0) Current line, 1) Next line, 2) Source file info. + */ +struct line_debug_entry +{ + int line; + char *filename; + struct nlist *sym; +}; + +void qsort (); +/* + * Helper routines for do_file_warnings. + */ + +/* Return an integer less than, equal to, or greater than 0 as per the + relation between the two relocation entries. Used by qsort. */ + +int +relocation_entries_relation (rel1, rel2) + struct relocation_info *rel1, *rel2; +{ + return RELOC_ADDRESS(rel1) - RELOC_ADDRESS(rel2); +} + +/* Moves to the next debugging symbol in the file. USE_DATA_SYMBOLS + determines the type of the debugging symbol to look for (DSLINE or + SLINE). STATE_POINTER keeps track of the old and new locatiosn in + the file. It assumes that state_pointer[1] is valid; ie + that it.sym points into some entry in the symbol table. If + state_pointer[1].sym == 0, this routine should not be called. */ + +int +next_debug_entry (use_data_symbols, state_pointer) + register int use_data_symbols; + /* Next must be passed by reference! */ + struct line_debug_entry state_pointer[3]; +{ + register struct line_debug_entry + *current = state_pointer, + *next = state_pointer + 1, + /* Used to store source file */ + *source = state_pointer + 2; + struct file_entry *entry = (struct file_entry *) source->sym; + + current->sym = next->sym; + current->line = next->line; + current->filename = next->filename; + + while (++(next->sym) < (entry->symbols + + entry->header.a_syms/sizeof (struct nlist))) + { + /* n_type is a char, and N_SOL, N_EINCL and N_BINCL are > 0x80, so + * may look negative...therefore, must mask to low bits + */ + switch (next->sym->n_type & 0xff) + { + case N_SLINE: + if (use_data_symbols) continue; + next->line = next->sym->n_desc; + return 1; + case N_DSLINE: + if (!use_data_symbols) continue; + next->line = next->sym->n_desc; + return 1; +#ifdef HAVE_SUN_STABS + case N_EINCL: + next->filename = source->filename; + continue; +#endif + case N_SO: + source->filename = next->sym->n_un.n_strx + entry->strings; + source->line++; +#ifdef HAVE_SUN_STABS + case N_BINCL: +#endif + case N_SOL: + next->filename + = next->sym->n_un.n_strx + entry->strings; + default: + continue; + } + } + next->sym = (struct nlist *) 0; + return 0; +} + +/* Create a structure to save the state of a scan through the debug + symbols. USE_DATA_SYMBOLS is set if we should be scanning for + DSLINE's instead of SLINE's. entry is the file entry which points + at the symbols to use. */ + +struct line_debug_entry * +init_debug_scan (use_data_symbols, entry) + int use_data_symbols; + struct file_entry *entry; +{ + struct line_debug_entry + *state_pointer + = (struct line_debug_entry *) + xmalloc (3 * sizeof (struct line_debug_entry)); + register struct line_debug_entry + *current = state_pointer, + *next = state_pointer + 1, + *source = state_pointer + 2; /* Used to store source file */ + + struct nlist *tmp; + + for (tmp = entry->symbols; + tmp < (entry->symbols + + entry->header.a_syms/sizeof (struct nlist)); + tmp++) + if (tmp->n_type == (int) N_SO) + break; + + if (tmp >= (entry->symbols + + entry->header.a_syms/sizeof (struct nlist))) + { + /* I believe this translates to "We lose" */ + current->filename = next->filename = entry->filename; + current->line = next->line = -1; + current->sym = next->sym = (struct nlist *) 0; + return state_pointer; + } + + next->line = source->line = 0; + next->filename = source->filename + = (tmp->n_un.n_strx + entry->strings); + source->sym = (struct nlist *) entry; + next->sym = tmp; + + next_debug_entry (use_data_symbols, state_pointer); /* To setup next */ + + if (!next->sym) /* No line numbers for this section; */ + /* setup output results as appropriate */ + { + if (source->line) + { + current->filename = source->filename = entry->filename; + current->line = -1; /* Don't print lineno */ + } + else + { + current->filename = source->filename; + current->line = 0; + } + return state_pointer; + } + + + next_debug_entry (use_data_symbols, state_pointer); /* To setup current */ + + return state_pointer; +} + +/* Takes an ADDRESS (in either text or data space) and a STATE_POINTER + which describes the current location in the implied scan through + the debug symbols within the file which ADDRESS is within, and + returns the source line number which corresponds to ADDRESS. */ + +int +address_to_line (address, state_pointer) + unsigned long address; + /* Next must be passed by reference! */ + struct line_debug_entry state_pointer[3]; +{ + struct line_debug_entry + *current = state_pointer, + *next = state_pointer + 1; + struct line_debug_entry *tmp_pointer; + + int use_data_symbols; + + if (next->sym) + use_data_symbols = (next->sym->n_type & N_TYPE) == N_DATA; + else + return current->line; + + /* Go back to the beginning if we've already passed it. */ + if (current->sym->n_value > address) + { + tmp_pointer = init_debug_scan (use_data_symbols, + (struct file_entry *) + ((state_pointer + 2)->sym)); + state_pointer[0] = tmp_pointer[0]; + state_pointer[1] = tmp_pointer[1]; + state_pointer[2] = tmp_pointer[2]; + free (tmp_pointer); + } + + /* If we're still in a bad way, return -1, meaning invalid line. */ + if (current->sym->n_value > address) + return -1; + + while (next->sym + && next->sym->n_value <= address + && next_debug_entry (use_data_symbols, state_pointer)) + ; + return current->line; +} + + +/* Macros for manipulating bitvectors. */ +#define BIT_SET_P(bv, index) ((bv)[(index) >> 3] & 1 << ((index) & 0x7)) +#define SET_BIT(bv, index) ((bv)[(index) >> 3] |= 1 << ((index) & 0x7)) + +/* This routine will scan through the relocation data of file ENTRY, + printing out references to undefined symbols and references to + symbols defined in files with N_WARNING symbols. If DATA_SEGMENT + is non-zero, it will scan the data relocation segment (and use + N_DSLINE symbols to track line number); otherwise it will scan the + text relocation segment. Warnings will be printed on the output + stream OUTFILE. Eventually, every nlist symbol mapped through will + be marked in the NLIST_BITVECTOR, so we don't repeat ourselves when + we scan the nlists themselves. */ + +do_relocation_warnings (entry, data_segment, outfile, nlist_bitvector) + struct file_entry *entry; + int data_segment; + FILE *outfile; + unsigned char *nlist_bitvector; +{ + struct relocation_info + *reloc_start = data_segment ? entry->datarel : entry->textrel, + *reloc; + int reloc_size + = ((data_segment ? entry->header.a_drsize : entry->header.a_trsize) + / sizeof (struct relocation_info)); + int start_of_segment + = (data_segment ? entry->data_start_address : entry->text_start_address); + struct nlist *start_of_syms = entry->symbols; + struct line_debug_entry *state_pointer + = init_debug_scan (data_segment != 0, entry); + register struct line_debug_entry + *current = state_pointer; + /* Assigned to generally static values; should not be written into. */ + char *errfmt; + /* Assigned to alloca'd values cand copied into; should be freed + when done. */ + char *errmsg; + int invalidate_line_number; + + /* We need to sort the relocation info here. Sheesh, so much effort + for one lousy error optimization. */ + + qsort (reloc_start, reloc_size, sizeof (struct relocation_info), + relocation_entries_relation); + + for (reloc = reloc_start; + reloc < (reloc_start + reloc_size); + reloc++) + { + register struct nlist *s; + register symbol *g; + + /* If the relocation isn't resolved through a symbol, continue */ + if (!RELOC_EXTERN_P(reloc)) + continue; + + s = &(entry->symbols[RELOC_SYMBOL(reloc)]); + + /* Local symbols shouldn't ever be used by relocation info, so + the next should be safe. + This is, of course, wrong. References to local BSS symbols can be + the targets of relocation info, and they can (must) be + resolved through symbols. However, these must be defined properly, + (the assembler would have caught it otherwise), so we can + ignore these cases. */ + if (!(s->n_type & N_EXT)) + continue; + + g = (symbol *) s->n_un.n_name; + errmsg = 0; + + if (!g->defined && list_unresolved_refs) /* Reference */ + { + /* Mark as being noted by relocation warning pass. */ + SET_BIT (nlist_bitvector, s - start_of_syms); + + if (g->undef_refs >= MAX_UREFS_PRINTED) /* Listed too many */ + continue; + + /* Undefined symbol which we should mention */ + + if (++(g->undef_refs) == MAX_UREFS_PRINTED) + { + errfmt = "More undefined symbol %s refs follow"; + invalidate_line_number = 1; + } + else + { + errfmt = "Undefined symbol %s referenced from %s segment"; + invalidate_line_number = 0; + } + } + else /* Defined */ + { + /* Potential symbol warning here */ + if (!g->warning) continue; + + /* Mark as being noted by relocation warning pass. */ + SET_BIT (nlist_bitvector, s - start_of_syms); + + errfmt = 0; + errmsg = g->warning; + invalidate_line_number = 0; + } + + + /* If errfmt == 0, errmsg has already been defined. */ + if (errfmt != 0) + { + char *nm; + + if (demangler == NULL || (nm = (*demangler)(g->name)) == NULL) + nm = g->name; + errmsg = (char *) xmalloc (strlen (errfmt) + strlen (nm) + 1); + sprintf (errmsg, errfmt, nm, data_segment ? "data" : "text"); + if (nm != g->name) + free (nm); + } + + address_to_line (RELOC_ADDRESS (reloc) + start_of_segment, + state_pointer); + + if (current->line >=0) + fprintf (outfile, "%s:%d: %s\n", current->filename, + invalidate_line_number ? 0 : current->line, errmsg); + else + fprintf (outfile, "%s: %s\n", current->filename, errmsg); + + if (errfmt != 0) + free (errmsg); + } + + free (state_pointer); +} + +/* Print on OUTFILE a list of all warnings generated by references + and/or definitions in the file ENTRY. List source file and line + number if possible, just the .o file if not. */ + +void +do_file_warnings (entry, outfile) + struct file_entry *entry; + FILE *outfile; +{ + int number_of_syms = entry->header.a_syms / sizeof (struct nlist); + unsigned char *nlist_bitvector + = (unsigned char *) alloca ((number_of_syms >> 3) + 1); + struct line_debug_entry *text_scan, *data_scan; + int i; + char *errfmt, *file_name; + int line_number; + int dont_allow_symbol_name; + + bzero (nlist_bitvector, (number_of_syms >> 3) + 1); + + /* Read in the files strings if they aren't available */ + if (!entry->strings) + { + int desc; + + entry->strings = (char *) alloca (entry->string_size); + desc = file_open (entry); + read_entry_strings (desc, entry); + } + + read_file_relocation (entry); + + /* Do text warnings based on a scan through the relocation info. */ + do_relocation_warnings (entry, 0, outfile, nlist_bitvector); + + /* Do data warnings based on a scan through the relocation info. */ + do_relocation_warnings (entry, 1, outfile, nlist_bitvector); + + /* Scan through all of the nlist entries in this file and pick up + anything that the scan through the relocation stuff didn't. */ + + text_scan = init_debug_scan (0, entry); + data_scan = init_debug_scan (1, entry); + + for (i = 0; i < number_of_syms; i++) + { + struct nlist *s; + struct glosym *g; + + s = entry->symbols + i; + + if (!(s->n_type & N_EXT)) + continue; + + g = (symbol *) s->n_un.n_name; + dont_allow_symbol_name = 0; + + if (list_multiple_defs && g->multiply_defined) + { + errfmt = "Definition of symbol %s (multiply defined)"; + switch (s->n_type) + { + case N_TEXT | N_EXT: + line_number = address_to_line (s->n_value, text_scan); + file_name = text_scan[0].filename; + break; + case N_DATA | N_EXT: + line_number = address_to_line (s->n_value, data_scan); + file_name = data_scan[0].filename; + break; + case N_SETA | N_EXT: + case N_SETT | N_EXT: + case N_SETD | N_EXT: + case N_SETB | N_EXT: + if (g->multiply_defined == 2) + continue; + errfmt = "First set element definition of symbol %s (multiply defined)"; + break; + default: + continue; /* Don't print out multiple defs + at references. */ + } + } + else if (BIT_SET_P (nlist_bitvector, i)) + continue; + else if (list_unresolved_refs && !g->defined) + { + if (g->undef_refs >= MAX_UREFS_PRINTED) + continue; + + if (++(g->undef_refs) == MAX_UREFS_PRINTED) + errfmt = "More undefined \"%s\" refs follow"; + else + errfmt = "Undefined symbol \"%s\" referenced"; + line_number = -1; + } + else if (g->warning) + { + /* There are two cases in which we don't want to + do this. The first is if this is a definition instead of + a reference. The second is if it's the reference used by + the warning stabs itself. */ + if (s->n_type != (N_EXT | N_UNDF) + || (i && (s-1)->n_type == N_WARNING)) + continue; + + errfmt = g->warning; + line_number = -1; + dont_allow_symbol_name = 1; + } + else + continue; + + if (line_number == -1) + fprintf (outfile, "%s: ", entry->filename); + else + fprintf (outfile, "%s:%d: ", file_name, line_number); + + if (dont_allow_symbol_name) + fprintf (outfile, "%s", errfmt); + else + { + char *nm; + if (demangler != NULL && (nm = (*demangler)(g->name)) != NULL) + { + fprintf (outfile, errfmt, nm); + free (nm); + } + else + fprintf (outfile, errfmt, g->name); + } + + fputc ('\n', outfile); + } + free (text_scan); + free (data_scan); + entry->strings = 0; /* Since it will dissapear anyway. */ +} + +do_warnings (outfile) + FILE *outfile; +{ + list_unresolved_refs = !relocatable_output && undefined_global_sym_count; + list_warning_symbols = warning_count; + list_multiple_defs = multiple_def_count != 0; + + if (!(list_unresolved_refs || + list_warning_symbols || + list_multiple_defs )) + /* No need to run this routine */ + return; + + each_file (do_file_warnings, outfile); + + if (list_unresolved_refs || list_multiple_defs) + make_executable = 0; +} + +/* Write the output file */ + +void +write_output () +{ + struct stat statbuf; + int filemode; + + (void) unlink (output_filename); + outdesc = open (output_filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (outdesc < 0) perror_name (output_filename); + + if (fstat (outdesc, &statbuf) < 0) + perror_name (output_filename); + + filemode = statbuf.st_mode; + + chmod (output_filename, filemode & ~0111); + + /* Output the a.out header. */ + write_header (); + + /* Output the text and data segments, relocating as we go. */ + write_text (); + write_data (); + + /* Output the merged relocation info, if requested with `-r'. */ + if (relocatable_output) + write_rel (); + + /* Output the symbol table (both globals and locals). */ + write_syms (); + + /* Copy any GDB symbol segments from input files. */ + write_symsegs (); + + close (outdesc); + + if (chmod (output_filename, filemode | 0111) == -1) + perror_name (output_filename); +} + +void modify_location (), perform_relocation (), copy_text (), copy_data (); + +void +write_header () +{ + N_SET_MAGIC (outheader, magic); + outheader.a_text = text_size; +#ifdef sequent + outheader.a_text += N_ADDRADJ (outheader); + if (entry_symbol == 0) + entry_symbol = getsym("start"); +#endif + outheader.a_data = data_size; + outheader.a_bss = bss_size; + outheader.a_entry = (entry_symbol ? entry_symbol->value + : text_start + entry_offset); +if (screwballmode) { + N_SET_MAGIC (outheader, ZMAGIC); + outheader.a_text = 0; + outheader.a_data = text_size + data_size; + outheader.a_entry = (entry_symbol ? entry_symbol->value + : sizeof(struct exec)); +} +#ifdef COFF_ENCAPSULATE + if (need_coff_header) + { + /* We are encapsulating BSD format within COFF format. */ + struct coffscn *tp, *dp, *bp; + + tp = &coffheader.scns[0]; + dp = &coffheader.scns[1]; + bp = &coffheader.scns[2]; + + strcpy (tp->s_name, ".text"); + tp->s_paddr = text_start; + tp->s_vaddr = text_start; + tp->s_size = text_size; + tp->s_scnptr = sizeof (struct coffheader) + sizeof (struct exec); + tp->s_relptr = 0; + tp->s_lnnoptr = 0; + tp->s_nreloc = 0; + tp->s_nlnno = 0; + tp->s_flags = 0x20; + strcpy (dp->s_name, ".data"); + dp->s_paddr = data_start; + dp->s_vaddr = data_start; + dp->s_size = data_size; + dp->s_scnptr = tp->s_scnptr + tp->s_size; + dp->s_relptr = 0; + dp->s_lnnoptr = 0; + dp->s_nreloc = 0; + dp->s_nlnno = 0; + dp->s_flags = 0x40; + strcpy (bp->s_name, ".bss"); + bp->s_paddr = dp->s_vaddr + dp->s_size; + bp->s_vaddr = bp->s_paddr; + bp->s_size = bss_size; + bp->s_scnptr = 0; + bp->s_relptr = 0; + bp->s_lnnoptr = 0; + bp->s_nreloc = 0; + bp->s_nlnno = 0; + bp->s_flags = 0x80; + + coffheader.f_magic = COFF_MAGIC; + coffheader.f_nscns = 3; + /* store an unlikely time so programs can + * tell that there is a bsd header + */ + coffheader.f_timdat = 1; + coffheader.f_symptr = 0; + coffheader.f_nsyms = 0; + coffheader.f_opthdr = 28; + coffheader.f_flags = 0x103; + /* aouthdr */ + coffheader.magic = ZMAGIC; + coffheader.vstamp = 0; + coffheader.tsize = tp->s_size; + coffheader.dsize = dp->s_size; + coffheader.bsize = bp->s_size; + coffheader.entry = outheader.a_entry; + coffheader.text_start = tp->s_vaddr; + coffheader.data_start = dp->s_vaddr; + } +#endif + +#ifdef INITIALIZE_HEADER + INITIALIZE_HEADER; +#endif + + if (strip_symbols == STRIP_ALL) + nsyms = 0; + else + { + nsyms = (defined_global_sym_count + + undefined_global_sym_count); + if (discard_locals == DISCARD_L) + nsyms += non_L_local_sym_count; + else if (discard_locals == DISCARD_NONE) + nsyms += local_sym_count; + /* One extra for following reference on indirects */ + if (relocatable_output) + nsyms += set_symbol_count + global_indirect_count; + } + + if (strip_symbols == STRIP_NONE) + nsyms += debugger_sym_count; + + outheader.a_syms = nsyms * sizeof (struct nlist); + + if (relocatable_output) + { + outheader.a_trsize = text_reloc_size; + outheader.a_drsize = data_reloc_size; + } + else + { + outheader.a_trsize = 0; + outheader.a_drsize = 0; + } + +#ifdef COFF_ENCAPSULATE + if (need_coff_header) + mywrite (&coffheader, sizeof coffheader, 1, outdesc); +#endif + mywrite (&outheader, sizeof (struct exec), 1, outdesc); +if (screwballmode) + N_SET_MAGIC (outheader, OMAGIC); + + /* Output whatever padding is required in the executable file + between the header and the start of the text. */ + +#ifndef COFF_ENCAPSULATE + padfile (N_TXTOFF (outheader) - sizeof outheader, outdesc); +#endif +} + +/* Relocate the text segment of each input file + and write to the output file. */ + +void +write_text () +{ + if (trace_files) + fprintf (stderr, "Copying and relocating text:\n\n"); + + each_full_file (copy_text, 0); + file_close (); + + if (trace_files) + fprintf (stderr, "\n"); + + padfile (text_pad, outdesc); +} + +int +text_offset (entry) + struct file_entry *entry; +{ + return entry->starting_offset + N_TXTOFF (entry->header); +} + +/* Read in all of the relocation information */ + +void +read_relocation () +{ + each_full_file (read_file_relocation, 0); +} + +/* Read in the relocation sections of ENTRY if necessary */ + +void +read_file_relocation (entry) + struct file_entry *entry; +{ + register struct relocation_info *reloc; + int desc; + int read_return; + + desc = -1; + if (!entry->textrel) + { + reloc = (struct relocation_info *) xmalloc (entry->header.a_trsize); + desc = file_open (entry); + lseek (desc, + text_offset (entry) + entry->header.a_text + entry->header.a_data, + L_SET); + if (entry->header.a_trsize != (read_return = read (desc, reloc, entry->header.a_trsize))) + { + fprintf (stderr, "Return from read: %d\n", read_return); + fatal_with_file ("premature eof in text relocation of ", entry); + } + entry->textrel = reloc; + } + + if (!entry->datarel) + { + reloc = (struct relocation_info *) xmalloc (entry->header.a_drsize); + if (desc == -1) desc = file_open (entry); + lseek (desc, + text_offset (entry) + entry->header.a_text + + entry->header.a_data + entry->header.a_trsize, + L_SET); + if (entry->header.a_drsize != read (desc, reloc, entry->header.a_drsize)) + fatal_with_file ("premature eof in data relocation of ", entry); + entry->datarel = reloc; + } +} + +/* Read the text segment contents of ENTRY, relocate them, + and write the result to the output file. + If `-r', save the text relocation for later reuse. */ + +void +copy_text (entry) + struct file_entry *entry; +{ + register char *bytes; + register int desc; + register struct relocation_info *reloc; + + if (trace_files) + prline_file_name (entry, stderr); + + desc = file_open (entry); + + /* Allocate space for the file's text section */ + + bytes = (char *) alloca (entry->header.a_text); + + /* Deal with relocation information however is appropriate */ + + if (entry->textrel) reloc = entry->textrel; + else if (relocatable_output) + { + read_file_relocation (entry); + reloc = entry->textrel; + } + else + { + reloc = (struct relocation_info *) alloca (entry->header.a_trsize); + lseek (desc, text_offset (entry) + entry->header.a_text + entry->header.a_data, 0); + if (entry->header.a_trsize != read (desc, reloc, entry->header.a_trsize)) + fatal_with_file ("premature eof in text relocation of ", entry); + } + + /* Read the text section into core. */ + + lseek (desc, text_offset (entry), 0); + if (entry->header.a_text != read (desc, bytes, entry->header.a_text)) + fatal_with_file ("premature eof in text section of ", entry); + + + /* Relocate the text according to the text relocation. */ + + perform_relocation (bytes, entry->text_start_address, entry->header.a_text, + reloc, entry->header.a_trsize, entry); + + /* Write the relocated text to the output file. */ + + mywrite (bytes, 1, entry->header.a_text, outdesc); +} + +/* Relocate the data segment of each input file + and write to the output file. */ + +void +write_data () +{ + if (trace_files) + fprintf (stderr, "Copying and relocating data:\n\n"); + + each_full_file (copy_data, 0); + file_close (); + + /* Write out the set element vectors. See digest symbols for + description of length of the set vector section. */ + + if (set_vector_count) + mywrite (set_vectors, 2 * set_symbol_count + set_vector_count, + sizeof (unsigned long), outdesc); + + if (trace_files) + fprintf (stderr, "\n"); + + padfile (data_pad, outdesc); +} + +/* Read the data segment contents of ENTRY, relocate them, + and write the result to the output file. + If `-r', save the data relocation for later reuse. + See comments in `copy_text'. */ + +void +copy_data (entry) + struct file_entry *entry; +{ + register struct relocation_info *reloc; + register char *bytes; + register int desc; + + if (trace_files) + prline_file_name (entry, stderr); + + desc = file_open (entry); + + bytes = (char *) alloca (entry->header.a_data); + + if (entry->datarel) reloc = entry->datarel; + else if (relocatable_output) /* Will need this again */ + { + read_file_relocation (entry); + reloc = entry->datarel; + } + else + { + reloc = (struct relocation_info *) alloca (entry->header.a_drsize); + lseek (desc, text_offset (entry) + entry->header.a_text + + entry->header.a_data + entry->header.a_trsize, + 0); + if (entry->header.a_drsize != read (desc, reloc, entry->header.a_drsize)) + fatal_with_file ("premature eof in data relocation of ", entry); + } + + lseek (desc, text_offset (entry) + entry->header.a_text, 0); + if (entry->header.a_data != read (desc, bytes, entry->header.a_data)) + fatal_with_file ("premature eof in data section of ", entry); + + perform_relocation (bytes, entry->data_start_address - entry->header.a_text, + entry->header.a_data, reloc, entry->header.a_drsize, entry); + + mywrite (bytes, 1, entry->header.a_data, outdesc); +} + +/* Relocate ENTRY's text or data section contents. + DATA is the address of the contents, in core. + DATA_SIZE is the length of the contents. + PC_RELOCATION is the difference between the address of the contents + in the output file and its address in the input file. + RELOC_INFO is the address of the relocation info, in core. + RELOC_SIZE is its length in bytes. */ +/* This version is about to be severly hacked by Randy. Hope it + works afterwards. */ +void +perform_relocation (data, pc_relocation, data_size, reloc_info, reloc_size, entry) + char *data; + struct relocation_info *reloc_info; + struct file_entry *entry; + int pc_relocation; + int data_size; + int reloc_size; +{ + register struct relocation_info *p = reloc_info; + struct relocation_info *end + = reloc_info + reloc_size / sizeof (struct relocation_info); + int text_relocation = entry->text_start_address; + int data_relocation = entry->data_start_address - entry->header.a_text; + int bss_relocation + = entry->bss_start_address - entry->header.a_text - entry->header.a_data; + + for (; p < end; p++) + { + register int relocation = 0; + register int addr = RELOC_ADDRESS(p); + register unsigned int mask = 0; + + if (addr >= data_size) + fatal_with_file ("relocation address out of range in ", entry); + + if (RELOC_EXTERN_P(p)) + { + int symindex = RELOC_SYMBOL (p) * sizeof (struct nlist); + symbol *sp = ((symbol *) + (((struct nlist *) + (((char *)entry->symbols) + symindex)) + ->n_un.n_name)); + +#ifdef N_INDR + /* Resolve indirection */ + if ((sp->defined & ~N_EXT) == N_INDR) + sp = (symbol *) sp->value; +#endif + + if (symindex >= entry->header.a_syms) + fatal_with_file ("relocation symbolnum out of range in ", entry); + + /* If the symbol is undefined, leave it at zero. */ + if (! sp->defined) + relocation = 0; + else + relocation = sp->value; + } + else switch (RELOC_TYPE(p)) + { + case N_TEXT: + case N_TEXT | N_EXT: + relocation = text_relocation; + break; + + case N_DATA: + case N_DATA | N_EXT: + /* A word that points to beginning of the the data section + initially contains not 0 but rather the "address" of that section + in the input file, which is the length of the file's text. */ + relocation = data_relocation; + break; + + case N_BSS: + case N_BSS | N_EXT: + /* Similarly, an input word pointing to the beginning of the bss + initially contains the length of text plus data of the file. */ + relocation = bss_relocation; + break; + + case N_ABS: + case N_ABS | N_EXT: + /* Don't know why this code would occur, but apparently it does. */ + break; + + default: + fatal_with_file ("nonexternal relocation code invalid in ", entry); + } + +#ifdef RELOC_ADD_EXTRA + relocation += RELOC_ADD_EXTRA(p); + if (relocatable_output) + { + /* Non-PC relative relocations which are absolute + or which have become non-external now have fixed + relocations. Set the ADD_EXTRA of this relocation + to be the relocation we have now determined. */ + if (! RELOC_PCREL_P (p)) + { + if ((int)p->r_type <= RELOC_32 + || RELOC_EXTERN_P (p) == 0) + RELOC_ADD_EXTRA (p) = relocation; + } + /* External PC-relative relocations continue to move around; + update their relocations by the amount they have moved + so far. */ + else if (RELOC_EXTERN_P (p)) + RELOC_ADD_EXTRA (p) -= pc_relocation; + continue; + } +#endif + + if (RELOC_PCREL_P(p)) + relocation -= pc_relocation; + + relocation >>= RELOC_VALUE_RIGHTSHIFT(p); + + /* Unshifted mask for relocation */ + mask = 1 << RELOC_TARGET_BITSIZE(p) - 1; + mask |= mask - 1; + relocation &= mask; + + /* Shift everything up to where it's going to be used */ + relocation <<= RELOC_TARGET_BITPOS(p); + mask <<= RELOC_TARGET_BITPOS(p); + + switch (RELOC_TARGET_SIZE(p)) + { + case 0: + if (RELOC_MEMORY_SUB_P(p)) + relocation -= mask & *(char *) (data + addr); + else if (RELOC_MEMORY_ADD_P(p)) + relocation += mask & *(char *) (data + addr); + *(char *) (data + addr) &= ~mask; + *(char *) (data + addr) |= relocation; + break; + + case 1: +#ifdef tahoe + if (((int) data + addr & 1) == 0) + { +#endif + if (RELOC_MEMORY_SUB_P(p)) + relocation -= mask & *(short *) (data + addr); + else if (RELOC_MEMORY_ADD_P(p)) + relocation += mask & *(short *) (data + addr); + *(short *) (data + addr) &= ~mask; + *(short *) (data + addr) |= relocation; +#ifdef tahoe + } + /* + * The CCI Power 6 (aka Tahoe) architecture has byte-aligned + * instruction operands but requires data accesses to be aligned. + * Brain-damage... + */ + else + { + unsigned char *da = (unsigned char *) (data + addr); + unsigned short s = da[0] << 8 | da[1]; + + if (RELOC_MEMORY_SUB_P(p)) + relocation -= mask & s; + else if (RELOC_MEMORY_ADD_P(p)) + relocation += mask & s; + s &= ~mask; + s |= relocation; + da[0] = s >> 8; + da[1] = s; + } +#endif + break; + + case 2: +#ifndef _CROSS_TARGET_ARCH +#ifdef tahoe + if (((int) data + addr & 3) == 0) + { +#endif + if (RELOC_MEMORY_SUB_P(p)) + relocation -= mask & *(long *) (data + addr); + else if (RELOC_MEMORY_ADD_P(p)) + relocation += mask & *(long *) (data + addr); + *(long *) (data + addr) &= ~mask; + *(long *) (data + addr) |= relocation; +#ifdef tahoe + } + else + { + unsigned char *da = (unsigned char *) (data + addr); + unsigned long l = da[0] << 24 | da[1] << 16 | da[2] << 8 | da[3]; + + if (RELOC_MEMORY_SUB_P(p)) + relocation -= mask & l; + else if (RELOC_MEMORY_ADD_P(p)) + relocation += mask & l; + l &= ~mask; + l |= relocation; + da[0] = l >> 24; + da[1] = l >> 16; + da[2] = l >> 8; + da[3] = l; + } +#endif +#else + /* Handle long word alignment requirements of SPARC architecture */ + /* WARNING: This fix makes an assumption on byte ordering */ + /* Marc Ullman, Stanford University Nov. 1 1989 */ + if (RELOC_MEMORY_SUB_P(p)) { + relocation -= mask & + ((*(unsigned short *) (data + addr) << 16) | + *(unsigned short *) (data + addr + 2)); + } else if (RELOC_MEMORY_ADD_P(p)) { + relocation += mask & + ((*(unsigned short *) (data + addr) << 16) | + *(unsigned short *) (data + addr + 2)); + } + *(unsigned short *) (data + addr) &= (~mask >> 16); + *(unsigned short *) (data + addr + 2) &= (~mask & 0xffff); + *(unsigned short *) (data + addr) |= (relocation >> 16); + *(unsigned short *) (data + addr + 2) |= (relocation & 0xffff); +#endif + break; + + default: + fatal_with_file ("Unimplemented relocation field length in ", entry); + } + } +} + +/* For relocatable_output only: write out the relocation, + relocating the addresses-to-be-relocated. */ + +void coptxtrel (), copdatrel (); + +void +write_rel () +{ + register int i; + register int count = 0; + + if (trace_files) + fprintf (stderr, "Writing text relocation:\n\n"); + + /* Assign each global symbol a sequence number, giving the order + in which `write_syms' will write it. + This is so we can store the proper symbolnum fields + in relocation entries we write. */ + + for (i = 0; i < TABSIZE; i++) + { + symbol *sp; + for (sp = symtab[i]; sp; sp = sp->link) + if (sp->referenced || sp->defined) + { + sp->def_count = count++; + /* Leave room for the reference required by N_INDR, if + necessary. */ + if ((sp->defined & ~N_EXT) == N_INDR) + count++; + } + } + /* Correct, because if (relocatable_output), we will also be writing + whatever indirect blocks we have. */ + if (count != defined_global_sym_count + + undefined_global_sym_count + global_indirect_count) + fatal ("internal error"); + + /* Write out the relocations of all files, remembered from copy_text. */ + + each_full_file (coptxtrel, 0); + + if (trace_files) + fprintf (stderr, "\nWriting data relocation:\n\n"); + + each_full_file (copdatrel, 0); + + if (trace_files) + fprintf (stderr, "\n"); +} + +void +coptxtrel (entry) + struct file_entry *entry; +{ + register struct relocation_info *p, *end; + register int reloc = entry->text_start_address; + + p = entry->textrel; + end = (struct relocation_info *) (entry->header.a_trsize + (char *) p); + while (p < end) + { + RELOC_ADDRESS(p) += reloc; + if (RELOC_EXTERN_P(p)) + { + register int symindex = RELOC_SYMBOL(p) * sizeof (struct nlist); + symbol *symptr = ((symbol *) + (((struct nlist *) + (((char *)entry->symbols) + symindex)) + ->n_un.n_name)); + + if (symindex >= entry->header.a_syms) + fatal_with_file ("relocation symbolnum out of range in ", entry); + +#ifdef N_INDR + /* Resolve indirection. */ + if ((symptr->defined & ~N_EXT) == N_INDR) + symptr = (symbol *) symptr->value; +#endif + + /* If the symbol is now defined, change the external relocation + to an internal one. */ + + if (symptr->defined) + { + RELOC_EXTERN_P(p) = 0; + RELOC_SYMBOL(p) = (symptr->defined & N_TYPE); +#ifdef RELOC_ADD_EXTRA + /* If we aren't going to be adding in the value in + memory on the next pass of the loader, then we need + to add it in from the relocation entry. Otherwise + the work we did in this pass is lost. */ + if (!RELOC_MEMORY_ADD_P(p)) + RELOC_ADD_EXTRA (p) += symptr->value; +#endif + } + else + /* Debugger symbols come first, so have to start this + after them. */ + RELOC_SYMBOL(p) = (symptr->def_count + nsyms + - defined_global_sym_count + - undefined_global_sym_count + - global_indirect_count); + } + p++; + } + mywrite (entry->textrel, 1, entry->header.a_trsize, outdesc); +} + +void +copdatrel (entry) + struct file_entry *entry; +{ + register struct relocation_info *p, *end; + /* Relocate the address of the relocation. + Old address is relative to start of the input file's data section. + New address is relative to start of the output file's data section. */ + register int reloc = entry->data_start_address - text_size; + + p = entry->datarel; + end = (struct relocation_info *) (entry->header.a_drsize + (char *) p); + while (p < end) + { + RELOC_ADDRESS(p) += reloc; + if (RELOC_EXTERN_P(p)) + { + register int symindex = RELOC_SYMBOL(p) * sizeof (struct nlist); + symbol *symptr = ((symbol *) + (((struct nlist *) + (((char *)entry->symbols) + symindex)) + ->n_un.n_name)); + int symtype; + + if (symindex >= entry->header.a_syms) + fatal_with_file ("relocation symbolnum out of range in ", entry); + +#ifdef N_INDR + /* Resolve indirection. */ + if ((symptr->defined & ~N_EXT) == N_INDR) + symptr = (symbol *) symptr->value; +#endif + + symtype = symptr->defined & N_TYPE; + + if (force_common_definition + || symtype == N_DATA || symtype == N_TEXT || symtype == N_ABS) + { + RELOC_EXTERN_P(p) = 0; + RELOC_SYMBOL(p) = symtype; + } + else + /* Debugger symbols come first, so have to start this + after them. */ + RELOC_SYMBOL(p) + = (((symbol *) + (((struct nlist *) + (((char *)entry->symbols) + symindex)) + ->n_un.n_name)) + ->def_count + + nsyms - defined_global_sym_count + - undefined_global_sym_count + - global_indirect_count); + } + p++; + } + mywrite (entry->datarel, 1, entry->header.a_drsize, outdesc); +} + +void write_file_syms (); +void write_string_table (); + +/* Offsets and current lengths of symbol and string tables in output file. */ + +int symbol_table_offset; +int symbol_table_len; + +/* Address in output file where string table starts. */ +int string_table_offset; + +/* Offset within string table + where the strings in `strtab_vector' should be written. */ +int string_table_len; + +/* Total size of string table strings allocated so far, + including strings in `strtab_vector'. */ +int strtab_size; + +/* Vector whose elements are strings to be added to the string table. */ +char **strtab_vector; + +/* Vector whose elements are the lengths of those strings. */ +int *strtab_lens; + +/* Index in `strtab_vector' at which the next string will be stored. */ +int strtab_index; + +/* Add the string NAME to the output file string table. + Record it in `strtab_vector' to be output later. + Return the index within the string table that this string will have. */ + +int +assign_string_table_index (name) + char *name; +{ + register int index = strtab_size; + register int len = strlen (name) + 1; + + strtab_size += len; + strtab_vector[strtab_index] = name; + strtab_lens[strtab_index++] = len; + + return index; +} + +FILE *outstream = (FILE *) 0; + +/* Write the contents of `strtab_vector' into the string table. + This is done once for each file's local&debugger symbols + and once for the global symbols. */ + +void +write_string_table () +{ + register int i; + + lseek (outdesc, string_table_offset + string_table_len, 0); + + if (!outstream) + outstream = fdopen (outdesc, "w"); + + for (i = 0; i < strtab_index; i++) + { + fwrite (strtab_vector[i], 1, strtab_lens[i], outstream); + string_table_len += strtab_lens[i]; + } + + fflush (outstream); + + /* Report I/O error such as disk full. */ + if (ferror (outstream)) + perror_name (output_filename); +} + +/* Write the symbol table and string table of the output file. */ + +void +write_syms () +{ + /* Number of symbols written so far. */ + int syms_written = 0; + register int i; + register symbol *sp; + + /* Buffer big enough for all the global symbols. One + extra struct for each indirect symbol to hold the extra reference + following. */ + struct nlist *buf + = (struct nlist *) alloca ((defined_global_sym_count + + undefined_global_sym_count + + global_indirect_count) + * sizeof (struct nlist)); + /* Pointer for storing into BUF. */ + register struct nlist *bufp = buf; + + /* Size of string table includes the bytes that store the size. */ + strtab_size = sizeof strtab_size; + + symbol_table_offset = N_SYMOFF (outheader); + symbol_table_len = 0; + string_table_offset = N_STROFF (outheader); + string_table_len = strtab_size; + + if (strip_symbols == STRIP_ALL) + return; + + /* Write the local symbols defined by the various files. */ + + each_file (write_file_syms, &syms_written); + file_close (); + + /* Now write out the global symbols. */ + + /* Allocate two vectors that record the data to generate the string + table from the global symbols written so far. This must include + extra space for the references following indirect outputs. */ + + strtab_vector = (char **) alloca ((num_hash_tab_syms + + global_indirect_count) * sizeof (char *)); + strtab_lens = (int *) alloca ((num_hash_tab_syms + + global_indirect_count) * sizeof (int)); + strtab_index = 0; + + /* Scan the symbol hash table, bucket by bucket. */ + + for (i = 0; i < TABSIZE; i++) + for (sp = symtab[i]; sp; sp = sp->link) + { + struct nlist nl; + + nl.n_other = 0; + nl.n_desc = 0; + + /* Compute a `struct nlist' for the symbol. */ + + if (sp->defined || sp->referenced) + { + /* common condition needs to be before undefined condition */ + /* because unallocated commons are set undefined in */ + /* digest_symbols */ + if (sp->defined > 1) /* defined with known type */ + { + /* If the target of an indirect symbol has been + defined and we are outputting an executable, + resolve the indirection; it's no longer needed */ + if (!relocatable_output + && ((sp->defined & N_TYPE) == N_INDR) + && (((symbol *) sp->value)->defined > 1)) + { + symbol *newsp = (symbol *) sp->value; + nl.n_type = newsp->defined; + nl.n_value = newsp->value; + } + else + { + nl.n_type = sp->defined; + if (sp->defined != (N_INDR | N_EXT)) + nl.n_value = sp->value; + else + nl.n_value = 0; + } + } + else if (sp->max_common_size) /* defined as common but not allocated. */ + { + /* happens only with -r and not -d */ + /* write out a common definition */ + nl.n_type = N_UNDF | N_EXT; + nl.n_value = sp->max_common_size; + } + else if (!sp->defined) /* undefined -- legit only if -r */ + { + nl.n_type = N_UNDF | N_EXT; + nl.n_value = 0; + } + else + fatal ("internal error: %s defined in mysterious way", sp->name); + + /* Allocate string table space for the symbol name. */ + + nl.n_un.n_strx = assign_string_table_index (sp->name); + + /* Output to the buffer and count it. */ + + *bufp++ = nl; + syms_written++; + if (nl.n_type == (N_INDR | N_EXT)) + { + struct nlist xtra_ref; + xtra_ref.n_type = N_EXT | N_UNDF; + xtra_ref.n_un.n_strx + = assign_string_table_index (((symbol *) sp->value)->name); + xtra_ref.n_other = 0; + xtra_ref.n_desc = 0; + xtra_ref.n_value = 0; + *bufp++ = xtra_ref; + syms_written++; + } + } + } + + /* Output the buffer full of `struct nlist's. */ + + lseek (outdesc, symbol_table_offset + symbol_table_len, 0); + mywrite (buf, sizeof (struct nlist), bufp - buf, outdesc); + symbol_table_len += sizeof (struct nlist) * (bufp - buf); + + if (syms_written != nsyms) + fatal ("internal error: wrong number of symbols written into output file", 0); + + if (symbol_table_offset + symbol_table_len != string_table_offset) + fatal ("internal error: inconsistent symbol table length", 0); + + /* Now the total string table size is known, so write it. + We are already positioned at the right place in the file. */ + + mywrite (&strtab_size, sizeof (int), 1, outdesc); /* we're at right place */ + + /* Write the strings for the global symbols. */ + + write_string_table (); +} + +/* Write the local and debugger symbols of file ENTRY. + Increment *SYMS_WRITTEN_ADDR for each symbol that is written. */ + +/* Note that we do not combine identical names of local symbols. + dbx or gdb would be confused if we did that. */ + +void +write_file_syms (entry, syms_written_addr) + struct file_entry *entry; + int *syms_written_addr; +{ + register struct nlist *p = entry->symbols; + register struct nlist *end = p + entry->header.a_syms / sizeof (struct nlist); + + /* Buffer to accumulate all the syms before writing them. + It has one extra slot for the local symbol we generate here. */ + struct nlist *buf + = (struct nlist *) alloca (entry->header.a_syms + sizeof (struct nlist)); + register struct nlist *bufp = buf; + + /* Upper bound on number of syms to be written here. */ + int max_syms = (entry->header.a_syms / sizeof (struct nlist)) + 1; + + /* Make tables that record, for each symbol, its name and its name's length. + The elements are filled in by `assign_string_table_index'. */ + + strtab_vector = (char **) alloca (max_syms * sizeof (char *)); + strtab_lens = (int *) alloca (max_syms * sizeof (int)); + strtab_index = 0; + + /* Generate a local symbol for the start of this file's text. */ + + if (discard_locals != DISCARD_ALL) + { + struct nlist nl; + + nl.n_type = N_FN | N_EXT; + nl.n_un.n_strx = assign_string_table_index (entry->local_sym_name); + nl.n_value = entry->text_start_address; + nl.n_desc = 0; + nl.n_other = 0; + *bufp++ = nl; + (*syms_written_addr)++; + entry->local_syms_offset = *syms_written_addr * sizeof (struct nlist); + } + + /* Read the file's string table. */ + + entry->strings = (char *) alloca (entry->string_size); + read_entry_strings (file_open (entry), entry); + + for (; p < end; p++) + { + register int type = p->n_type; + register int write = 0; + + /* WRITE gets 1 for a non-global symbol that should be written. */ + + + if (SET_ELEMENT_P (type)) /* This occurs even if global. These */ + /* types of symbols are never written */ + /* globally, though they are stored */ + /* globally. */ + write = relocatable_output; + else if (!(type & (N_STAB | N_EXT))) + /* ordinary local symbol */ + write = ((discard_locals != DISCARD_ALL) + && !(discard_locals == DISCARD_L && + (p->n_un.n_strx + entry->strings)[0] == LPREFIX) + && type != N_WARNING); + else if (!(type & N_EXT)) + /* debugger symbol */ + write = (strip_symbols == STRIP_NONE); + + if (write) + { + /* If this symbol has a name, + allocate space for it in the output string table. */ + + if (p->n_un.n_strx) + p->n_un.n_strx = assign_string_table_index (p->n_un.n_strx + + entry->strings); + + /* Output this symbol to the buffer and count it. */ + + *bufp++ = *p; + (*syms_written_addr)++; + } + } + + /* All the symbols are now in BUF; write them. */ + + lseek (outdesc, symbol_table_offset + symbol_table_len, 0); + mywrite (buf, sizeof (struct nlist), bufp - buf, outdesc); + symbol_table_len += sizeof (struct nlist) * (bufp - buf); + + /* Write the string-table data for the symbols just written, + using the data in vectors `strtab_vector' and `strtab_lens'. */ + + write_string_table (); + entry->strings = 0; /* Since it will dissapear anyway. */ +} + +/* Copy any GDB symbol segments from the input files to the output file. + The contents of the symbol segment is copied without change + except that we store some information into the beginning of it. */ + +void write_file_symseg (); + +void +write_symsegs () +{ + each_file (write_file_symseg, 0); +} + +void +write_file_symseg (entry) + struct file_entry *entry; +{ + char buffer[4096]; + struct symbol_root root; + int indesc; + int len; + + if (entry->symseg_offset == 0) + return; + + /* This entry has a symbol segment. Read the root of the segment. */ + + indesc = file_open (entry); + lseek (indesc, entry->symseg_offset + entry->starting_offset, 0); + if (sizeof root != read (indesc, &root, sizeof root)) + fatal_with_file ("premature end of file in symbol segment of ", entry); + + /* Store some relocation info into the root. */ + + root.ldsymoff = entry->local_syms_offset; + root.textrel = entry->text_start_address; + root.datarel = entry->data_start_address - entry->header.a_text; + root.bssrel = entry->bss_start_address + - entry->header.a_text - entry->header.a_data; + root.databeg = entry->data_start_address - root.datarel; + root.bssbeg = entry->bss_start_address - root.bssrel; + + /* Write the modified root into the output file. */ + + mywrite (&root, sizeof root, 1, outdesc); + + /* Copy the rest of the symbol segment unchanged. */ + + if (entry->superfile) + { + /* Library member: number of bytes to copy is determined + from the member's total size. */ + + int total = entry->total_size - entry->symseg_offset - sizeof root; + + while (total > 0) + { + len = read (indesc, buffer, min (sizeof buffer, total)); + + if (len != min (sizeof buffer, total)) + fatal_with_file ("premature end of file in symbol segment of ", entry); + total -= len; + mywrite (buffer, len, 1, outdesc); + } + } + else + { + /* A separate file: copy until end of file. */ + + while (len = read (indesc, buffer, sizeof buffer)) + { + mywrite (buffer, len, 1, outdesc); + if (len < sizeof buffer) + break; + } + } + + file_close (); +} + +/* Create the symbol table entries for `etext', `edata' and `end'. */ + +void +symtab_init () +{ +#ifndef nounderscore + edata_symbol = getsym ("_edata"); + etext_symbol = getsym ("_etext"); + end_symbol = getsym ("_end"); +#else + edata_symbol = getsym ("edata"); + etext_symbol = getsym ("etext"); + end_symbol = getsym ("end"); +#endif + +#ifdef sun + { + symbol *dynamic_symbol = getsym ("__DYNAMIC"); + dynamic_symbol->defined = N_ABS | N_EXT; + dynamic_symbol->referenced = 1; + dynamic_symbol->value = 0; + } +#endif + +#ifdef sequent + { + symbol *_387_flt_symbol = getsym ("_387_flt"); + _387_flt_symbol->defined = N_ABS | N_EXT; + _387_flt_symbol->referenced = 1; + _387_flt_symbol->value = 0; + } +#endif + + edata_symbol->defined = N_DATA | N_EXT; + etext_symbol->defined = N_TEXT | N_EXT; + end_symbol->defined = N_BSS | N_EXT; + + edata_symbol->referenced = 1; + etext_symbol->referenced = 1; + end_symbol->referenced = 1; +} + +/* Compute the hash code for symbol name KEY. */ + +int +hash_string (key) + char *key; +{ + register char *cp; + register int k; + + cp = key; + k = 0; + while (*cp) + k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; + + return k; +} + +/* Get the symbol table entry for the global symbol named KEY. + Create one if there is none. */ + +symbol * +getsym (key) + char *key; +{ + register int hashval; + register symbol *bp; + + /* Determine the proper bucket. */ + + hashval = hash_string (key) % TABSIZE; + + /* Search the bucket. */ + + for (bp = symtab[hashval]; bp; bp = bp->link) + if (! strcmp (key, bp->name)) + return bp; + + /* Nothing was found; create a new symbol table entry. */ + + bp = (symbol *) xmalloc (sizeof (symbol)); + bp->refs = 0; + bp->name = (char *) xmalloc (strlen (key) + 1); + strcpy (bp->name, key); + bp->defined = 0; + bp->referenced = 0; + bp->trace = 0; + bp->value = 0; + bp->max_common_size = 0; + bp->warning = 0; + bp->undef_refs = 0; + bp->multiply_defined = 0; + + /* Add the entry to the bucket. */ + + bp->link = symtab[hashval]; + symtab[hashval] = bp; + + ++num_hash_tab_syms; + + return bp; +} + +/* Like `getsym' but return 0 if the symbol is not already known. */ + +symbol * +getsym_soft (key) + char *key; +{ + register int hashval; + register symbol *bp; + + /* Determine which bucket. */ + + hashval = hash_string (key) % TABSIZE; + + /* Search the bucket. */ + + for (bp = symtab[hashval]; bp; bp = bp->link) + if (! strcmp (key, bp->name)) + return bp; + + return 0; +} + +/* Report a fatal error. + STRING is a printf format string and ARG is one arg for it. */ + +void +fatal (string, arg) + char *string, *arg; +{ + fprintf (stderr, "ld: "); + fprintf (stderr, string, arg); + fprintf (stderr, "\n"); + exit (1); +} + +/* Report a fatal error. The error message is STRING + followed by the filename of ENTRY. */ + +void +fatal_with_file (string, entry) + char *string; + struct file_entry *entry; +{ + fprintf (stderr, "ld: "); + fprintf (stderr, string); + print_file_name (entry, stderr); + fprintf (stderr, "\n"); + exit (1); +} + +/* Report a fatal error using the message for the last failed system call, + followed by the string NAME. */ + +void +perror_name (name) + char *name; +{ + extern int errno, sys_nerr; + extern char *sys_errlist[]; + char *s; + + if (errno < sys_nerr) + s = concat ("", sys_errlist[errno], " for %s"); + else + s = "cannot open %s"; + fatal (s, name); +} + +/* Report a fatal error using the message for the last failed system call, + followed by the name of file ENTRY. */ + +void +perror_file (entry) + struct file_entry *entry; +{ + extern int errno, sys_nerr; + extern char *sys_errlist[]; + char *s; + + if (errno < sys_nerr) + s = concat ("", sys_errlist[errno], " for "); + else + s = "cannot open "; + fatal_with_file (s, entry); +} + +/* Report a nonfatal error. + STRING is a format for printf, and ARG1 ... ARG3 are args for it. */ + +void +error (string, arg1, arg2, arg3) + char *string, *arg1, *arg2, *arg3; +{ + fprintf (stderr, "%s: ", progname); + fprintf (stderr, string, arg1, arg2, arg3); + fprintf (stderr, "\n"); +} + + +/* Output COUNT*ELTSIZE bytes of data at BUF + to the descriptor DESC. */ + +void +mywrite (buf, count, eltsize, desc) + char *buf; + int count; + int eltsize; + int desc; +{ + register int val; + register int bytes = count * eltsize; + + while (bytes > 0) + { + val = write (desc, buf, bytes); + if (val <= 0) + perror_name (output_filename); + buf += val; + bytes -= val; + } +} + +/* Output PADDING zero-bytes to descriptor OUTDESC. + PADDING may be negative; in that case, do nothing. */ + +void +padfile (padding, outdesc) + int padding; + int outdesc; +{ + register char *buf; + if (padding <= 0) + return; + + buf = (char *) alloca (padding); + bzero (buf, padding); + mywrite (buf, padding, 1, outdesc); +} + +/* Return a newly-allocated string + whose contents concatenate the strings S1, S2, S3. */ + +char * +concat (s1, s2, s3) + char *s1, *s2, *s3; +{ + register int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); + register char *result = (char *) xmalloc (len1 + len2 + len3 + 1); + + strcpy (result, s1); + strcpy (result + len1, s2); + strcpy (result + len1 + len2, s3); + result[len1 + len2 + len3] = 0; + + return result; +} + +/* Parse the string ARG using scanf format FORMAT, and return the result. + If it does not parse, report fatal error + generating the error message using format string ERROR and ARG as arg. */ + +int +parse (arg, format, error) + char *arg, *format; +{ + int x; + if (1 != sscanf (arg, format, &x)) + fatal (error, arg); + return x; +} + +/* Like malloc but get fatal error if memory is exhausted. */ + +int +xmalloc (size) + int size; +{ + register int result = malloc (size); + if (!result) + fatal ("virtual memory exhausted", 0); + return result; +} + +/* Like realloc but get fatal error if memory is exhausted. */ + +int +xrealloc (ptr, size) + char *ptr; + int size; +{ + register int result = realloc (ptr, size); + if (!result) + fatal ("virtual memory exhausted", 0); + return result; +} + +#ifdef USG + +void +bzero (p, n) + char *p; +{ + memset (p, 0, n); +} + +void +bcopy (from, to, n) + char *from, *to; +{ + memcpy (to, from, n); +} + +getpagesize () +{ + return (4096); +} + +#endif + +#if defined(sun) && (TARGET == SUN4) + +/* Don't use local pagesize to build for Sparc. */ + +getpagesize () +{ + return (8192); +} +#endif diff --git a/gnu/usr.bin/ld/symseg.h b/gnu/usr.bin/ld/symseg.h new file mode 100644 index 0000000000..fe9939f390 --- /dev/null +++ b/gnu/usr.bin/ld/symseg.h @@ -0,0 +1,358 @@ +/*- + * + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * @(#)symseg.h 5.4 (Berkeley) 4/30/91 + */ + +/* GDB symbol table format definitions. + Copyright (C) 1987, 1988 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Format of GDB symbol table data. + There is one symbol segment for each source file or + independant compilation. These segments are simply concatenated + to form the GDB symbol table. A zero word where the beginning + of a segment is expected indicates there are no more segments. + +Format of a symbol segment: + + The symbol segment begins with a word containing 1 + if it is in the format described here. Other formats may + be designed, with other code numbers. + + The segment contains many objects which point at each other. + The pointers are offsets in bytes from the beginning of the segment. + Thus, each segment can be loaded into core and its pointers relocated + to make valid in-core pointers. + + All the data objects in the segment can be found indirectly from + one of them, the root object, of type `struct symbol_root'. + It appears at the beginning of the segment. + + The total size of the segment, in bytes, appears as the `length' + field of this object. This size includes the size of the + root object. + + All the object data types are defined here to contain pointer types + appropriate for in-core use on a relocated symbol segment. + Casts to and from type int are required for working with + unrelocated symbol segments such as are found in the file. + + The ldsymaddr word is filled in by the loader to contain + the offset (in bytes) within the ld symbol table + of the first nonglobal symbol from this compilation. + This makes it possible to match those symbols + (which contain line number information) reliably with + the segment they go with. + + Core addresses within the program that appear in the symbol segment + are not relocated by the loader. They are inserted by the assembler + and apply to addresses as output by the assembler, so GDB must + relocate them when it loads the symbol segment. It gets the information + on how to relocate from the textrel, datarel, bssrel, databeg and bssbeg + words of the root object. + + The words textrel, datarel and bssrel + are filled in by ld with the amounts to relocate within-the-file + text, data and bss addresses by; databeg and bssbeg can be + used to tell which kind of relocation an address needs. */ + +enum language {language_c}; + +struct symbol_root +{ + int format; /* Data format version */ + int length; /* # bytes in this symbol segment */ + int ldsymoff; /* Offset in ld symtab of this file's syms */ + int textrel; /* Relocation for text addresses */ + int datarel; /* Relocation for data addresses */ + int bssrel; /* Relocation for bss addresses */ + char *filename; /* Name of main source file compiled */ + char *filedir; /* Name of directory it was reached from */ + struct blockvector *blockvector; /* Vector of all symbol-naming blocks */ + struct typevector *typevector; /* Vector of all data types */ + enum language language; /* Code identifying the language used */ + char *version; /* Version info. Not fully specified */ + char *compilation; /* Compilation info. Not fully specified */ + int databeg; /* Address within the file of data start */ + int bssbeg; /* Address within the file of bss start */ + struct sourcevector *sourcevector; /* Vector of line-number info */ +}; + +/* All data types of symbols in the compiled program + are represented by `struct type' objects. + All of these objects are pointed to by the typevector. + The type vector may have empty slots that contain zero. */ + +struct typevector +{ + int length; /* Number of types described */ + struct type *type[1]; +}; + +/* Different kinds of data types are distinguished by the `code' field. */ + +enum type_code +{ + TYPE_CODE_UNDEF, /* Not used; catches errors */ + TYPE_CODE_PTR, /* Pointer type */ + TYPE_CODE_ARRAY, /* Array type, lower bound zero */ + TYPE_CODE_STRUCT, /* C struct or Pascal record */ + TYPE_CODE_UNION, /* C union or Pascal variant part */ + TYPE_CODE_ENUM, /* Enumeration type */ + TYPE_CODE_FUNC, /* Function type */ + TYPE_CODE_INT, /* Integer type */ + TYPE_CODE_FLT, /* Floating type */ + TYPE_CODE_VOID, /* Void type (values zero length) */ + TYPE_CODE_SET, /* Pascal sets */ + TYPE_CODE_RANGE, /* Range (integers within spec'd bounds) */ + TYPE_CODE_PASCAL_ARRAY, /* Array with explicit type of index */ +}; + +/* This appears in a type's flags word for an unsigned integer type. */ +#define TYPE_FLAG_UNSIGNED 1 + +/* Other flag bits are used with GDB. */ + +struct type +{ + /* Code for kind of type */ + enum type_code code; + /* Name of this type, or zero if none. + This is used for printing only. + Type names specified as input are defined by symbols. */ + char *name; + /* Length in bytes of storage for a value of this type */ + int length; + /* For a pointer type, describes the type of object pointed to. + For an array type, describes the type of the elements. + For a function type, describes the type of the value. + Unused otherwise. */ + struct type *target_type; + /* Type that is a pointer to this type. + Zero if no such pointer-to type is known yet. + The debugger may add the address of such a type + if it has to construct one later. */ + struct type *pointer_type; + /* Type that is a function returning this type. + Zero if no such function type is known here. + The debugger may add the address of such a type + if it has to construct one later. */ + struct type *function_type; + /* Flags about this type. */ + short flags; + /* Number of fields described for this type */ + short nfields; + /* For structure and union types, a description of each field. + For set and pascal array types, there is one "field", + whose type is the domain type of the set or array. + For range types, there are two "fields", + the minimum and maximum values (both inclusive). + For enum types, each possible value is described by one "field". + For range types, there are two "fields", that record constant values + (inclusive) for the minimum and maximum. + + Using a pointer to a separate array of fields + allows all types to have the same size, which is useful + because we can allocate the space for a type before + we know what to put in it. */ + struct field + { + /* Position of this field, counting in bits from start of + containing structure. For a function type, this is the + position in the argument list of this argument. + For a range bound or enum value, this is the value itself. */ + int bitpos; + /* Size of this field, in bits, or zero if not packed. + For an unpacked field, the field's type's length + says how many bytes the field occupies. */ + int bitsize; + /* In a struct or enum type, type of this field. + In a function type, type of this argument. + In an array type, the domain-type of the array. */ + struct type *type; + /* Name of field, value or argument. + Zero for range bounds and array domains. */ + char *name; + } *fields; +}; + +/* All of the name-scope contours of the program + are represented by `struct block' objects. + All of these objects are pointed to by the blockvector. + + Each block represents one name scope. + Each lexical context has its own block. + + The first two blocks in the blockvector are special. + The first one contains all the symbols defined in this compilation + whose scope is the entire program linked together. + The second one contains all the symbols whose scope is the + entire compilation excluding other separate compilations. + In C, these correspond to global symbols and static symbols. + + Each block records a range of core addresses for the code that + is in the scope of the block. The first two special blocks + give, for the range of code, the entire range of code produced + by the compilation that the symbol segment belongs to. + + The blocks appear in the blockvector + in order of increasing starting-address, + and, within that, in order of decreasing ending-address. + + This implies that within the body of one function + the blocks appear in the order of a depth-first tree walk. */ + +struct blockvector +{ + /* Number of blocks in the list. */ + int nblocks; + /* The blocks themselves. */ + struct block *block[1]; +}; + +struct block +{ + /* Addresses in the executable code that are in this block. + Note: in an unrelocated symbol segment in a file, + these are always zero. They can be filled in from the + N_LBRAC and N_RBRAC symbols in the loader symbol table. */ + int startaddr, endaddr; + /* The symbol that names this block, + if the block is the body of a function; + otherwise, zero. + Note: In an unrelocated symbol segment in an object file, + this field may be zero even when the block has a name. + That is because the block is output before the name + (since the name resides in a higher block). + Since the symbol does point to the block (as its value), + it is possible to find the block and set its name properly. */ + struct symbol *function; + /* The `struct block' for the containing block, or 0 if none. */ + /* Note that in an unrelocated symbol segment in an object file + this pointer may be zero when the correct value should be + the second special block (for symbols whose scope is one compilation). + This is because the compiler ouptuts the special blocks at the + very end, after the other blocks. */ + struct block *superblock; + /* Number of local symbols. */ + int nsyms; + /* The symbols. */ + struct symbol *sym[1]; +}; + +/* Represent one symbol name; a variable, constant, function or typedef. */ + +/* Different name spaces for symbols. Looking up a symbol specifies + a namespace and ignores symbol definitions in other name spaces. + + VAR_NAMESPACE is the usual namespace. + In C, this contains variables, function names, typedef names + and enum type values. + + STRUCT_NAMESPACE is used in C to hold struct, union and enum type names. + Thus, if `struct foo' is used in a C program, + it produces a symbol named `foo' in the STRUCT_NAMESPACE. + + LABEL_NAMESPACE may be used for names of labels (for gotos); + currently it is not used and labels are not recorded at all. */ + +/* For a non-global symbol allocated statically, + the correct core address cannot be determined by the compiler. + The compiler puts an index number into the symbol's value field. + This index number can be matched with the "desc" field of + an entry in the loader symbol table. */ + +enum namespace +{ + UNDEF_NAMESPACE, VAR_NAMESPACE, STRUCT_NAMESPACE, LABEL_NAMESPACE, +}; + +/* An address-class says where to find the value of the symbol in core. */ + +enum address_class +{ + LOC_UNDEF, /* Not used; catches errors */ + LOC_CONST, /* Value is constant int */ + LOC_STATIC, /* Value is at fixed address */ + LOC_REGISTER, /* Value is in register */ + LOC_ARG, /* Value is at spec'd position in arglist */ + LOC_LOCAL, /* Value is at spec'd pos in stack frame */ + LOC_TYPEDEF, /* Value not used; definition in SYMBOL_TYPE + Symbols in the namespace STRUCT_NAMESPACE + all have this class. */ + LOC_LABEL, /* Value is address in the code */ + LOC_BLOCK, /* Value is address of a `struct block'. + Function names have this class. */ + LOC_EXTERNAL, /* Value is at address not in this compilation. + This is used for .comm symbols + and for extern symbols within functions. + Inside GDB, this is changed to LOC_STATIC once the + real address is obtained from a loader symbol. */ + LOC_CONST_BYTES /* Value is a constant byte-sequence. */ +}; + +struct symbol +{ + /* Symbol name */ + char *name; + /* Name space code. */ + enum namespace namespace; + /* Address class */ + enum address_class class; + /* Data type of value */ + struct type *type; + /* constant value, or address if static, or register number, + or offset in arguments, or offset in stack frame. */ + union + { + long value; + struct block *block; /* for LOC_BLOCK */ + char *bytes; /* for LOC_CONST_BYTES */ + } + value; +}; + +/* Source-file information. + This describes the relation between source files and line numbers + and addresses in the program text. */ + +struct sourcevector +{ + int length; /* Number of source files described */ + struct source *source[1]; /* Descriptions of the files */ +}; + +/* Line number and address of one line. */ + +struct line +{ + int linenum; + int address; +}; + +/* All the information on one source file. */ + +struct source +{ + char *name; /* Name of file */ + int nlines; /* Number of lines that follow */ + struct line lines[1]; /* Information on each line */ +}; diff --git a/gnu/usr.bin/man/Makefile b/gnu/usr.bin/man/Makefile index 1777b6669a..0e7e5061f9 100644 --- a/gnu/usr.bin/man/Makefile +++ b/gnu/usr.bin/man/Makefile @@ -5,6 +5,6 @@ # distribution. # -SUBDIR = lib man manpath apropos whatis makewhatis +SUBDIR = lib man manpath apropos whatis makewhatis catman .include diff --git a/gnu/usr.bin/man/Makefile.inc b/gnu/usr.bin/man/Makefile.inc index b3ccb46744..c0df250c0a 100644 --- a/gnu/usr.bin/man/Makefile.inc +++ b/gnu/usr.bin/man/Makefile.inc @@ -1,6 +1,34 @@ +# +# Set a bunch of things to hardcoded paths so that we don't accidently +# pick up a user's own version of some utility and hose ourselves. +# BINDIR?= /usr/bin libdir= /etc bindir= ${BINDIR} -pager= /usr/gnu/bin/less -sC +pager= more -cs manpath_config_file= /etc/manpath.config troff= /usr/bin/groff -Tps -man +nroff= /usr/bin/groff -Tascii -man +apropos= /usr/bin/apropos +whatis= /usr/bin/whatis +neqn= /usr/bin/eqn -Tascii +tbl= /usr/bin/tbl +col= /usr/bin/col +vgrind= /usr/bin/vgrind +refer= /usr/bin/refer +grap= # no grap +pic= /usr/bin/pic +zcat= /usr/bin/zcat + +# For scripts. +.if !target(obj) +obj: + @cd ${.CURDIR}; rm -rf obj; \ + here=`pwd`; dest=/usr/obj/`echo $$here | sed 's,/usr/src/,,'`; \ + echo "$$here -> $$dest"; ln -s $$dest obj; \ + if test -d /usr/obj -a ! -d $$dest; then \ + mkdir -p $$dest; \ + else \ + true; \ + fi; +.endif diff --git a/gnu/usr.bin/man/apropos/Makefile b/gnu/usr.bin/man/apropos/Makefile index 892af95051..6208d5e6ba 100644 --- a/gnu/usr.bin/man/apropos/Makefile +++ b/gnu/usr.bin/man/apropos/Makefile @@ -1,26 +1,37 @@ -.include "../Makefile.inc" +.if exists(${.CURDIR}/obj) +MANP= ${.CURDIR}/obj/apropos.1 +TARG= ${.CURDIR}/obj/apropos +.else +MANP= ${.CURDIR}/apropos.1 +TARG= ${.CURDIR}/apropos +.endif -all: apropos apropos.1 +all: ${TARG} ${MANP} -obj depend rcsfreeze all: +depend rcsfreeze tags all: @echo -n cleandir: clean + cd ${.CURDIR}; rm -rf obj; clean: - @rm -f apropos apropos.1 + @rm -f ${TARG} ${MANP} -apropos: apropos.sh +${TARG}: ${.CURDIR}/apropos.sh sed -e 's,%libdir%,${libdir},' -e 's,%bindir%,${bindir},' \ -e 's,%pager%,${pager},' \ - apropos.sh > apropos + ${.CURDIR}/apropos.sh > $@ -apropos.1: apropos.man +${MANP}: ${.CURDIR}/apropos.man sed -e 's,%libdir%,${libdir},' -e 's,%bindir%,${bindir},' \ -e 's,%pager%,${pager},' -e 's,%troff%,${troff},' \ -e 's,%manpath_config_file%,${manpath_config_file},' \ - apropos.man > apropos.1 + ${.CURDIR}/apropos.man > $@ + +install: ${TARG} maninstall + install -c -o bin -g bin -m 555 ${TARG} ${DESTDIR}/usr/bin -install: apropos apropos.1 - install -c -o bin -g bin -m 555 apropos /usr/bin - install -c -o bin -g bin -m 444 apropos.1 /usr/share/man/man1 +maninstall: ${MANP} + install -c -o bin -g bin -m 444 ${MANP} ${DESTDIR}/usr/share/man/man1 + +.include "../Makefile.inc" diff --git a/gnu/usr.bin/man/apropos/apropos b/gnu/usr.bin/man/apropos/apropos deleted file mode 100644 index 8735e5fcdd..0000000000 --- a/gnu/usr.bin/man/apropos/apropos +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/sh -# -# apropos -- search the whatis database for keywords. -# -# Copyright (c) 1990, 1991, John W. Eaton. -# -# You may distribute under the terms of the GNU General Public -# License as specified in the README file that comes with the man -# distribution. -# -# John W. Eaton -# jwe@che.utexas.edu -# Department of Chemical Engineering -# The University of Texas at Austin -# Austin, Texas 78712 - -PATH=/usr/local/bin:/bin:/usr/ucb:/usr/bin - -libdir=/etc - -if [ $# = 0 ] -then - echo "usage: `basename $0` keyword ..." - exit 1 -fi - -manpath=`/usr/bin/manpath -q | tr : '\040'` - -if [ "$manpath" = "" ] -then - echo "whatis: manpath is null" - exit 1 -fi - -if [ "$PAGER" = "" ] -then - PAGER="/usr/gnu/bin/less -sC" -fi - -while [ $1 ] -do - found=0 - for d in $manpath /usr/lib - do - if [ -f $d/whatis ] - then - grep -i "$1" $d/whatis - status=$? - if [ "$status" = "0" ] - then - found=1 - fi - fi - done - - if [ "$found" = "0" ] - then - echo "$1: nothing appropriate" - fi - - shift -done | $PAGER - -exit diff --git a/gnu/usr.bin/man/apropos/apropos.1 b/gnu/usr.bin/man/apropos/apropos.1 deleted file mode 100644 index 3bb3e17d61..0000000000 --- a/gnu/usr.bin/man/apropos/apropos.1 +++ /dev/null @@ -1,27 +0,0 @@ -.\" Man page for apropos -.\" -.\" Copyright (c) 1990, 1991, John W. Eaton. -.\" -.\" You may distribute under the terms of the GNU General Public -.\" License as specified in the README file that comes with the man 1.0 -.\" distribution. -.\" -.\" John W. Eaton -.\" jwe@che.utexas.edu -.\" Department of Chemical Engineering -.\" The University of Texas at Austin -.\" Austin, Texas 78712 -.\" -.TH apropos 1 "Jan 15, 1991" -.LO 1 -.SH NAME -apropos \- search the whatis database for strings -.SH SYNOPSIS -.BI apropos -keyword ... -.SH DESCRIPTION -apropos searches a set of database files containing short descriptions -of system commands for keywords and displays the result on the -standard output. -.SH "SEE ALSO" -whatis(1), man(1). diff --git a/gnu/usr.bin/man/catman/Makefile b/gnu/usr.bin/man/catman/Makefile new file mode 100644 index 0000000000..de870e395a --- /dev/null +++ b/gnu/usr.bin/man/catman/Makefile @@ -0,0 +1,8 @@ +obj cleandir clean depend rcsfreeze tags all: + @echo -n + +install: + install -c -o bin -g bin -m 555 catman ${DESTDIR}/usr/bin + +.include "../Makefile.inc" +.include diff --git a/gnu/usr.bin/man/catman/catman b/gnu/usr.bin/man/catman/catman new file mode 100644 index 0000000000..a2d16a1170 --- /dev/null +++ b/gnu/usr.bin/man/catman/catman @@ -0,0 +1,36 @@ +#!/bin/sh +# usage: sh catman +# put the section numbers here: +SECTIONS="1 2 3 4 5 6 7 8" +MANDIR=/usr/share/man + +formatman() +{ + echo " "$1 "->" $* + (cd cat$section; rm -f $*) + nroff -man < man$section/$1 > cat$section/$1 + catfile=$1; shift + while [ $# -gt 0 ] + do + ln cat$section/$catfile cat$section/$1 + shift + done +} + +cd $MANDIR +for section in $SECTIONS +do + echo formatting section $section ... + + IFS=" " + allfiles=`ls -i1 man$section | sort | awk '{if (inode ~ $1) printf "/" $2; + else printf " " $2; inode = $1 } END {printf "\n"}'` + for files in $allfiles + do + IFS="/" + tfiles=`echo $files` + IFS=" " + formatman $tfiles + done +done +exit 0 diff --git a/gnu/usr.bin/man/lib/Makefile b/gnu/usr.bin/man/lib/Makefile index 33bd40b9e0..d364b32f4b 100644 --- a/gnu/usr.bin/man/lib/Makefile +++ b/gnu/usr.bin/man/lib/Makefile @@ -1,10 +1,30 @@ LIB = man -CFLAGS+= -I${.CURDIR} -DSTDC_HEADERS -DPOSIX -DHAS_TROFF -DDO_UNCOMPRESS -DALT_SYSTEMS +.if exists(${.CURDIR}/obj) +CONFH= ${.CURDIR}/obj/config.h +.else +CONFH= ${.CURDIR}/config.h +.endif + +CFLAGS+= -I${.CURDIR} -DSTDC_HEADERS -DPOSIX -DHAS_TROFF -DDO_UNCOMPRESS -DALT_SYSTEMS +CLEANFILES+= ${CONFH} SRCS = util.c gripes.c +libman.a:: ${CONFH} + install: @echo -n +depend ${CONFH}: ${.CURDIR}/config.h_dist ../Makefile.inc + sed -e 's,%apropos%,${apropos},' -e 's,%whatis%,${whatis},' \ + -e 's,%pager%,${pager},' -e 's,%troff%,${troff},' \ + -e 's,%nroff%,${nroff},' -e 's,%tbl%,${tbl},' \ + -e 's,%col%,${col},' -e 's,%pic%,${pic},' \ + -e 's,%eqn%,${eqn},' -e 's,%neqn%,${neqn},' \ + -e 's,%vgrind%,${vgrind},' -e 's,%refer%,${refer},' \ + -e 's,%grap%,${grap},' -e 's,%zcat%,${zcat},' \ + -e 's,%manpath_config_file%,${manpath_config_file},' \ + ${.CURDIR}/config.h_dist > ${CONFH} + .include diff --git a/gnu/usr.bin/man/lib/config.h b/gnu/usr.bin/man/lib/config.h deleted file mode 100644 index 2c231681b1..0000000000 --- a/gnu/usr.bin/man/lib/config.h +++ /dev/null @@ -1,216 +0,0 @@ -/* - * config.h - * - * If you haven't read the README file, now might be a good time. - * - * This file is edited by configure, so you shouldn't have to. - * If that doesn't work, edit this file to match your site. - * - * Sorry it's so long, but there are lots of things you might want to - * customize for your site. - * - * Copyright (c) 1990, 1991, John W. Eaton. - * - * You may distribute under the terms of the GNU General Public - * License as specified in the file COPYING that comes with the man - * distribution. - * - * John W. Eaton - * jwe@che.utexas.edu - * Department of Chemical Engineering - * The University of Texas at Austin - * Austin, Texas 78712 - */ - -#ifdef COMPRESS -#define DO_COMPRESS -#define DO_UNCOMPRESS -#endif - -/* - * This is the size of a number of internal buffers. It should - * probably not be less than 512. - */ -#ifndef BUFSIZ -#define BUFSIZ 1024 -#endif - -/* - * This should be at least the size of the longest path. - */ -#ifndef MAXPATHLEN -#define MAXPATHLEN 1024 -#endif - -/* - * This is the maximum number of directories expected in the manpath. - */ -#ifndef MAXDIRS -#define MAXDIRS 64 -#endif - -/* - * This is the name of the group that owns the preformatted man pages. - * If you are running man as a setgid program, you should make sure - * that all of the preformatted man pages and the directories that - * they live in are readable and writeable and owned by this group. - */ -#ifdef SECURE_MAN_UID -#define MAN_USER "" -#endif - -/* - * It's probably best to define absolute paths to all of these. If - * you don't, you'll be depending on the user's path to be correct - * when system () is called. This can result in weird behavior that's - * hard to track down, especially after you forget how this program - * works... If you don't have some of these programs, simply define - * them to be empty strings (i.e. ""). As a minimum, you must have - * nroff installed. - */ -#ifndef APROPOS -#define APROPOS "/usr/bin/apropos" -#endif - -#ifndef WHATIS -#define WHATIS "/usr/bin/whatis" -#endif - -#ifndef PAGER -#define PAGER "/usr/gnu/bin/less -sC" -#endif - -#ifdef HAS_TROFF -#ifndef TROFF -#define TROFF "/usr/bin/groff -Tps -man" -#endif -#endif - -#ifndef NROFF -#define NROFF "/usr/bin/groff -Tascii -man" -#endif - -#ifndef EQN -#define EQN "/usr/bin/eqn -Tps" -#endif - -#ifndef NEQN -#define NEQN "/usr/bin/eqn -Tascii" -#endif - -#ifndef TBL -#define TBL "/usr/bin/tbl" -#endif - -#ifndef COL -#define COL "/usr/bin/col" -#endif - -#ifndef VGRIND -#define VGRIND "/usr/bin/vgrind" -#endif - -#ifndef REFER -#define REFER "/usr/bin/refer" -#endif - -#ifndef GRAP -#define GRAP "" -#endif - -#ifndef PIC -#define PIC "/usr/bin/pic" -#endif - -/* - * Define the absolute path to the configuration file. - */ -#ifndef MAN_MAIN - static char config_file[] = "/etc/manpath.config"; -#endif - -/* - * Define the uncompression program(s) to use for those preformatted - * pages that end in the given character. If you add extras here, you - * may need to change man.c. - */ -#ifdef DO_UNCOMPRESS -/* .F files */ -#define FCAT "" -/* .Y files */ -#define YCAT "" -/* .Z files */ -#define ZCAT "/usr/bin/zcat" -#endif - -/* - * This is the standard program to use on this system for compressing - * pages once they have been formatted, and the character to tack on - * to the end of those files. The program listed is expected to read - * from the standard input and write compressed output to the standard - * output. - */ -#ifdef DO_COMPRESS -#define COMPRESSOR "" -#define COMPRESS_EXT "" -#endif - -/* - * Define the standard manual sections. For example, if your man - * directory tree has subdirectories man1, man2, man3, mann, - * and man3foo, std_sections[] would have "1", "2", "3", "n", and - * "3foo". Directories are searched in the order they appear. Having - * extras isn't fatal, it just slows things down a bit. - * - * Note that this is just for directories to search. If you have - * files like .../man3/foobar.3Xtc, you don't need to have "3Xtc" in - * the list below -- this is handled separately, so that `man 3Xtc foobar', - * `man 3 foobar', and `man foobar' should find the file .../man3/foo.3Xtc, - * (assuming, of course, that there isn't a .../man1/foo.1 or somesuch - * that we would find first). - * - * Note that this list should be in the order that you want the - * directories to be searched. Is there a standard for this? What is - * the normal order? If anyone knows, please tell me! - */ -#ifndef MANPATH_MAIN - static char *std_sections[] = - { - "1", "n", "l", "6", "8", "2", "3", "4", "5", "7", "p", "o", NULL - }; -#endif - -/* - * Not all systems define these in stat.h. - */ -#ifndef S_IRUSR -#define S_IRUSR 00400 /* read permission: owner */ -#endif -#ifndef S_IWUSR -#define S_IWUSR 00200 /* write permission: owner */ -#endif -#ifndef S_IRGRP -#define S_IRGRP 00040 /* read permission: group */ -#endif -#ifndef S_IWGRP -#define S_IWGRP 00020 /* write permission: group */ -#endif -#ifndef S_IROTH -#define S_IROTH 00004 /* read permission: other */ -#endif -#ifndef S_IWOTH -#define S_IWOTH 00002 /* write permission: other */ -#endif - -/* - * This is the mode used for formatted pages that we create. If you - * are using the setgid option, you should use 664. If you are not, - * you should use 666 and make the cat* directories mode 777. - */ -#ifndef CATMODE -#ifdef SECURE_MAN_UID -#define CATMODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH -#else -#define CATMODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH -#endif -#endif diff --git a/gnu/usr.bin/man/lib/config.h_dist b/gnu/usr.bin/man/lib/config.h_dist new file mode 100644 index 0000000000..74122df037 --- /dev/null +++ b/gnu/usr.bin/man/lib/config.h_dist @@ -0,0 +1,216 @@ +/* + * config.h + * + * If you haven't read the README file, now might be a good time. + * + * This file is edited by configure, so you shouldn't have to. + * If that doesn't work, edit this file to match your site. + * + * Sorry it's so long, but there are lots of things you might want to + * customize for your site. + * + * Copyright (c) 1990, 1991, John W. Eaton. + * + * You may distribute under the terms of the GNU General Public + * License as specified in the file COPYING that comes with the man + * distribution. + * + * John W. Eaton + * jwe@che.utexas.edu + * Department of Chemical Engineering + * The University of Texas at Austin + * Austin, Texas 78712 + */ + +#ifdef COMPRESS +#define DO_COMPRESS +#define DO_UNCOMPRESS +#endif + +/* + * This is the size of a number of internal buffers. It should + * probably not be less than 512. + */ +#ifndef BUFSIZ +#define BUFSIZ 1024 +#endif + +/* + * This should be at least the size of the longest path. + */ +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +/* + * This is the maximum number of directories expected in the manpath. + */ +#ifndef MAXDIRS +#define MAXDIRS 64 +#endif + +/* + * This is the name of the group that owns the preformatted man pages. + * If you are running man as a setgid program, you should make sure + * that all of the preformatted man pages and the directories that + * they live in are readable and writeable and owned by this group. + */ +#ifdef SECURE_MAN_UID +#define MAN_USER "" +#endif + +/* + * It's probably best to define absolute paths to all of these. If + * you don't, you'll be depending on the user's path to be correct + * when system () is called. This can result in weird behavior that's + * hard to track down, especially after you forget how this program + * works... If you don't have some of these programs, simply define + * them to be empty strings (i.e. ""). As a minimum, you must have + * nroff installed. + */ +#ifndef APROPOS +#define APROPOS "%apropos%" +#endif + +#ifndef WHATIS +#define WHATIS "%whatis%" +#endif + +#ifndef PAGER +#define PAGER "%pager%" +#endif + +#ifdef HAS_TROFF +#ifndef TROFF +#define TROFF "%troff%" +#endif +#endif + +#ifndef NROFF +#define NROFF "%nroff%" +#endif + +#ifndef EQN +#define EQN "%eqn%" +#endif + +#ifndef NEQN +#define NEQN "%neqn%" +#endif + +#ifndef TBL +#define TBL "%tbl%" +#endif + +#ifndef COL +#define COL "%col%" +#endif + +#ifndef VGRIND +#define VGRIND "%vgrind%" +#endif + +#ifndef REFER +#define REFER "%refer%" +#endif + +#ifndef GRAP +#define GRAP "%grap%" +#endif + +#ifndef PIC +#define PIC "%pic%" +#endif + +/* + * Define the absolute path to the configuration file. + */ +#ifndef MAN_MAIN + static char config_file[] = "%manpath_config_file%" ; +#endif + +/* + * Define the uncompression program(s) to use for those preformatted + * pages that end in the given character. If you add extras here, you + * may need to change man.c. + */ +#ifdef DO_UNCOMPRESS +/* .F files */ +#define FCAT "" +/* .Y files */ +#define YCAT "" +/* .Z files */ +#define ZCAT "%zcat%" +#endif + +/* + * This is the standard program to use on this system for compressing + * pages once they have been formatted, and the character to tack on + * to the end of those files. The program listed is expected to read + * from the standard input and write compressed output to the standard + * output. + */ +#ifdef DO_COMPRESS +#define COMPRESSOR "" +#define COMPRESS_EXT "" +#endif + +/* + * Define the standard manual sections. For example, if your man + * directory tree has subdirectories man1, man2, man3, mann, + * and man3foo, std_sections[] would have "1", "2", "3", "n", and + * "3foo". Directories are searched in the order they appear. Having + * extras isn't fatal, it just slows things down a bit. + * + * Note that this is just for directories to search. If you have + * files like .../man3/foobar.3Xtc, you don't need to have "3Xtc" in + * the list below -- this is handled separately, so that `man 3Xtc foobar', + * `man 3 foobar', and `man foobar' should find the file .../man3/foo.3Xtc, + * (assuming, of course, that there isn't a .../man1/foo.1 or somesuch + * that we would find first). + * + * Note that this list should be in the order that you want the + * directories to be searched. Is there a standard for this? What is + * the normal order? If anyone knows, please tell me! + */ +#ifndef MANPATH_MAIN + static char *std_sections[] = + { + "1", "n", "l", "6", "8", "2", "3", "4", "5", "7", "p", "o", NULL + }; +#endif + +/* + * Not all systems define these in stat.h. + */ +#ifndef S_IRUSR +#define S_IRUSR 00400 /* read permission: owner */ +#endif +#ifndef S_IWUSR +#define S_IWUSR 00200 /* write permission: owner */ +#endif +#ifndef S_IRGRP +#define S_IRGRP 00040 /* read permission: group */ +#endif +#ifndef S_IWGRP +#define S_IWGRP 00020 /* write permission: group */ +#endif +#ifndef S_IROTH +#define S_IROTH 00004 /* read permission: other */ +#endif +#ifndef S_IWOTH +#define S_IWOTH 00002 /* write permission: other */ +#endif + +/* + * This is the mode used for formatted pages that we create. If you + * are using the setgid option, you should use 664. If you are not, + * you should use 666 and make the cat* directories mode 777. + */ +#ifndef CATMODE +#ifdef SECURE_MAN_UID +#define CATMODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH +#else +#define CATMODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH +#endif +#endif diff --git a/gnu/usr.bin/man/lib/gripes.po b/gnu/usr.bin/man/lib/gripes.po deleted file mode 100644 index 6bfd455e595a166a674bd4b37f7e99e588786115..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3219 zcmbW3ZHN_B7{^E3^tN4Jptbaac!JifXtPW-p`>WH1z+0kDyyIj%y8GuPcE zqf9JIsT-OkhAx;;e$gmmiwa>kazQFdL?}y2b74yaEo5`M{r@v(#=EI~XxN?qd7kq; zzjMxW=G>X_V-%(TW<{B=#8(4sUPo?StyZ@-XEq-Wr?%O3=&|3!ZEy?RoBFduQ!15; zJtuXqi`^&*(PB>!N z9TBX#R6?%qd+W_o=y-V$MCc10i}$pGS+Cq60F{veKyz(JIbPr2(b_gWpH;&8HhTzt zLzzmY&Ca62@vuDz!8&7OJB+ns2Dd@tam+4LFGushk_uWFH8DZM35u@Pjfoa8ve8tp z1l>Vs77~XryPOb(=yXC`1fdp|1JP*6TD13m%gN2;)G+yleKAvO6RaQ7>C9eqOIvpsv29(@YP;xt@or99wD)kX4IS)h0 zIg3Nb{L@h8e-CB;327gLGJgQdynZP2-i9)7JCu1_q0D<(+8d$F3#H$e{IrY_lj5`k~Y_8GuFlUC14efFx#eO?w*Jj6aP32W$NEr2jK;7RmNI2wYba(^=Dg zLB=ct=XKfMfN^)Doi8~>GiWOMN?z9=Qu!5u4@aX(QB^hXl~aeRD{2(0mhioa_la64 zqSLdSeA1COqwlk8JFb3Cs{Dvd;`u%zpI>k)-$$zHt-o<6Usg$P{Dw)A6R&*dtMW@+ zDd>LL3zJ^Y4^;~eBXP*rxS|$IejXE3RI6BWywFMl(v@nS8jr53K}*tG`^lB~u3b{H aKEAFLm7k>;#&XMno#c4_sH#L?0?I$yzGRC4 diff --git a/gnu/usr.bin/man/lib/util.c b/gnu/usr.bin/man/lib/util.c index b0babed3c6..e8253a8a99 100644 --- a/gnu/usr.bin/man/lib/util.c +++ b/gnu/usr.bin/man/lib/util.c @@ -19,6 +19,7 @@ #include #include #include +#include #ifdef STDC_HEADERS #include @@ -137,14 +138,12 @@ do_system_command (command) else status = system (command); - /* - * Ultrix returns 127 for failure. Is this normal? - */ - if (status == 127) - { - gripe_system_command (status); - return 0; - } + if (WIFSIGNALED(status)) + return 0; + else if (WEXITSTATUS(status)) { + gripe_system_command (status); + return 0; + } else return 1; } diff --git a/gnu/usr.bin/man/lib/util.po b/gnu/usr.bin/man/lib/util.po deleted file mode 100644 index 37dffc26d95efbb2f2f089c37519ea99430d8b7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1013 zcmZuvO=uHA7@gYKZbO>1(1W6AF9k)cv}g0coD=y$w72&a%e7!;-Mgvf(Q#K(o;Ps#`kTevWOr1?R($*X5RcTy)B5?m~Oi`Qt7`@P+6m#3v`N8;{RP`e=&9V<{;ZFNn?+>c)KSF?`sHBQ6d zk}@T=hN{)bo8_$nG}g^~3d_TH22bA|T>NCY&AR7*YombPrFd}L zfYMtka^Hz8ZC0X%Slm@079HMgzu!^ZvIff0^@-?{$YW=u4Wm||wE1JT@{DF)zADzR zHzsR0$o>G-ZSTFFjWv)eM^{qs+_1gg)vQ;m)f)76vmWGTUenXVN;Z>8=*bhqBe`Jb zKIky++HV{%Dvf=yPhnNY_^#M2tv~x5Ki5ImZiHH4_{H2;s7iIa5wL&WR91NaUC2T|_>2GQRQ?18=x(*e|< zfuw%`lKxQWivlgtH$;C57=|C6jpiK}`XS&>^6%9B7~}Q(L?3%$`;inOB-IpqklXw) zI6S<1LhvN;3=-S+;=GSv@bN1?Zi17~5GECU`aPi^1wIgb1^5)X?f=5Zmwf!Sk1zXp z6Wrq3+^CUdX+;^A@hS{$PEC$7S(ekf^;eJ;CTFf2W;U%SGAfg`oUE(!s>$RWH makewhatis: makewhatis.sh - sed -e 's/%sections%/ "1", "n", "l", "6", "8", "2", "3", "4", "5", "7", "p", "o", NULL/' makewhatis.sh > makewhatis + sed -e 's/%sections%/ "1", "n", "l", "6", "8", "2", "3", "4", "5", "7", "p", "o", NULL/' \ + ${.CURDIR}/makewhatis.sh > makewhatis + diff --git a/gnu/usr.bin/man/makewhatis/makewhatis b/gnu/usr.bin/man/makewhatis/makewhatis deleted file mode 100644 index e6c238c2ba..0000000000 --- a/gnu/usr.bin/man/makewhatis/makewhatis +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/sh -# -# makewhatis -- update the whatis database in the man directories. -# -# Copyright (c) 1990, 1991, John W. Eaton. -# -# You may distribute under the terms of the GNU General Public -# License as specified in the README file that comes with the man -# distribution. -# -# John W. Eaton -# jwe@che.utexas.edu -# Department of Chemical Engineering -# The University of Texas at Austin -# Austin, Texas 78712 - -PATH=/bin:/usr/local/bin:/usr/ucb:/usr/bin - -if [ $# = 0 ] -then - echo "usage: makewhatis directory [...]" - exit 1 -fi - -for dir in $* -do - cd $dir - for subdir in man* - do - if [ -d $subdir ] - then - for f in `find . -name '*' -print` - do - sed -n '/^\.TH.*$/p - /^\.SH[ ]*NAME/,/^\.SH/p' $f |\ - sed -e 's/\\[ ]*\-/-/ - s/^.PP.*$// - s/\\(em// - s/\\fI// - s/\\fR//' |\ - awk 'BEGIN {insh = 0} { - if ($1 == ".TH") - sect = $3 - else if ($1 == ".SH" && insh == 1) { - if (i > 0 && name != NULL) { - namesect = sprintf("%s (%s)", name, sect) - printf("%-20.20s", namesect) - printf(" - ") - for (j = 0; j < i-1; j++) - printf("%s ", desc[j]) - printf("%s\n", desc[i-1]) - } - } else if ($1 == ".SH" && insh == 0) { - insh = 1 - count = 0 - i = 0 - } else if (insh == 1) { - count++ - if (count == 1 && NF > 2) { - start = 2 - if ($2 == "-") start = 3 - if (NF > start + 1) - for (j = start; j <= NF; j++) - desc[i++] = $j - name = $1 - } else { - for (j = 1; j <= NF; j++) - desc[i++] = $j - } - } - }' - done - cd .. - fi - done | sort | colrm 80 > $dir/whatis.db.tmp - mv $dir/whatis.db.tmp $dir/whatis -done - -exit diff --git a/gnu/usr.bin/man/makewhatis/makewhatis.sh b/gnu/usr.bin/man/makewhatis/makewhatis.sh index e6c238c2ba..1d86d19939 100644 --- a/gnu/usr.bin/man/makewhatis/makewhatis.sh +++ b/gnu/usr.bin/man/makewhatis/makewhatis.sh @@ -21,7 +21,6 @@ then echo "usage: makewhatis directory [...]" exit 1 fi - for dir in $* do cd $dir @@ -29,50 +28,85 @@ do do if [ -d $subdir ] then - for f in `find . -name '*' -print` + for f in `find $subdir -type f -print` do sed -n '/^\.TH.*$/p - /^\.SH[ ]*NAME/,/^\.SH/p' $f |\ + /^\.Dt.*$/p + /^\.S[hH][ ]*NAME/,/^\.S[hH]/p' $f |\ sed -e 's/\\[ ]*\-/-/ - s/^.PP.*$// + s/^.P[Pp].*$// s/\\(em// s/\\fI// s/\\fR//' |\ - awk 'BEGIN {insh = 0} { - if ($1 == ".TH") + awk 'BEGIN {insh = 0; inSh = 0; Nd = 0} { + if ($1 == ".TH" || $1 == ".Dt") sect = $3 - else if ($1 == ".SH" && insh == 1) { - if (i > 0 && name != NULL) { - namesect = sprintf("%s (%s)", name, sect) - printf("%-20.20s", namesect) - printf(" - ") - for (j = 0; j < i-1; j++) - printf("%s ", desc[j]) - printf("%s\n", desc[i-1]) + else if (($1 == ".br" && insh == 1)\ + || ($1 == ".SH" && insh == 1)\ + || ($1 == ".Sh" && inSh == 1)) { + if (i > 0 && nc > 0) { + for (k= 1; k <= nc; k++) { + namesect = sprintf("%s (%s)", name[k], sect) + printf("%s", namesect) + printf(" - ") + for (j = 0; j < i-1; j++) + printf("%s ", desc[j]) + printf("%s\n", desc[i-1]) + } } + count = 0 + i = 0 + nc = 0 } else if ($1 == ".SH" && insh == 0) { insh = 1 count = 0 i = 0 + nc = 0 + } else if ($1 == ".Sh" && inSh == 0) { + inSh = 1 + i = 0 + nc = 0 } else if (insh == 1) { count++ if (count == 1 && NF > 2) { - start = 2 - if ($2 == "-") start = 3 - if (NF > start + 1) + start = 2 + for (k = 1; k <= NF; k++) + if ($k == "-") { + start = k + 1 + break + } else { + sub(",","",$k) + if ($k != "") + name[++nc] = $k + } + if (NF >= start) for (j = start; j <= NF; j++) desc[i++] = $j - name = $1 } else { for (j = 1; j <= NF; j++) desc[i++] = $j } - } + } else if ($1 == ".Nm" && inSh == 1 && Nd == 0) { + for (k = 2; k <= NF; k++) { + sub(",","",$k) + if ($k != "") + name[++nc] = $k + } + } else if ($1 == ".Nd" && inSh == 1) { + Nd = 1 + for (j = 2; j <= NF; j++) + desc[i++] = $j + } else if (Nd == 1) { + start = 1 + if ($1 ~ /\..*/) + start = 2 + for (j = start; j <= NF; j++) + desc[i++] = $j + } }' done - cd .. fi - done | sort | colrm 80 > $dir/whatis.db.tmp + done | sort | colrm 80 | uniq > $dir/whatis.db.tmp mv $dir/whatis.db.tmp $dir/whatis done diff --git a/gnu/usr.bin/man/man/Makefile b/gnu/usr.bin/man/man/Makefile index d116ad93ba..c00976b606 100644 --- a/gnu/usr.bin/man/man/Makefile +++ b/gnu/usr.bin/man/man/Makefile @@ -1,14 +1,29 @@ PROG= man SRCS= man.c manpath.c glob.c -MAN1= man.1 -LDADD+= -L${.CURDIR}/../lib -lman +BINMODE=6555 -CFLAGS+= -I${.CURDIR}/../lib -DSTDC_HEADERS -DPOSIX -DHAS_TROFF -DDO_UNCOMPRESS -DALT_SYSTEMS +.if exists(${.CURDIR}/../lib/obj) +LDADD= -L${.CURDIR}/../lib/obj -lman +CFLAGS+= -I${.CURDIR}/../lib/obj +.else +LDADD= -L${.CURDIR}/../lib/ -lman +.endif -man.1: man.man +.if exists(${.CURDIR}/obj) +MAN1= ${.CURDIR}/obj/man.1 +.else +MAN1= ${.CURDIR}/man.1 +.endif + +DPADD+= ${MAN1} +CFLAGS+= -I${.CURDIR}/../lib -DSTDC_HEADERS -DPOSIX -DHAS_TROFF +CFLAGS+= -DDO_UNCOMPRESS -DALT_SYSTEMS -DSETREUID -DCATMODE=0664 +CLEANFILES+= ${MAN1} + +${MAN1}: ${.CURDIR}/man.man sed -e 's,%libdir%,${libdir},' -e 's,%bindir%,${bindir},' \ -e 's,%pager%,${pager},' -e 's,%troff%,${troff},' \ -e 's,%manpath_config_file%,${manpath_config_file},' \ - man.man > man.1 + ${.CURDIR}/man.man > ${MAN1} .include diff --git a/gnu/usr.bin/man/man/man.1 b/gnu/usr.bin/man/man/man.1 deleted file mode 100644 index f17aced5c2..0000000000 --- a/gnu/usr.bin/man/man/man.1 +++ /dev/null @@ -1,132 +0,0 @@ -.\" Man page for man -.\" -.\" Copyright (c) 1990, 1991, John W. Eaton. -.\" -.\" You may distribute under the terms of the GNU General Public -.\" License as specified in the README file that comes with the man 1.0 -.\" distribution. -.\" -.\" John W. Eaton -.\" jwe@che.utexas.edu -.\" Department of Chemical Engineering -.\" The University of Texas at Austin -.\" Austin, Texas 78712 -.\" -.TH man 1 "Jan 5, 1991" -.LO 1 -.SH NAME -man \- format and display the on-line manual pages -.SH SYNOPSIS -man [\-adfhktw] [\-m system] [\-p string] [\-M path] [\-P pager] -[\-S list] [section] name ... -.SH DESCRIPTION -man formats and displays the on-line manual pages. This version knows -about the MANPATH and PAGER environment variables, so you can have -your own set(s) of personal man pages and choose whatever program you -like to display the formatted pages. If section is specified, man -only looks in that section of the manual. You may also specify the -order to search the sections for entries and which preprocessors to -run on the source files via command line options or environment -variables. -.SH OPTIONS -.TP -.B \-\^M " path" -Specify an alternate manpath. By default, man uses -.B manpath -to determine the path to search. This option overrides the -.B MANPATH -environment variable. -.TP -.B \-\^P " pager" -Specify which pager to use. By default, man uses -.B /usr/local/bin/less -sC, -This option overrides the -.B PAGER -environment variable. -.TP -.B \-\^S " list" -List is a colon separated list of manual sections to search. -This option overrides the -.B MANSECT -environment variable. -.TP -.B \-\^a -By default, man will exit after displaying the first manual page it -finds. Using this option forces man to display all the manual pages -that match -.B name, -not just the first. -.TP -.B \-\^d -Don't actually display the man pages, but do print gobs of debugging -information. -.TP -.B \-\^f -Equivalent to -.B whatis. -.TP -.B \-\^h -Print a one line help message and exit. -.TP -.B \-\^k -Equivalent to -.B apropos. -.TP -.B \-\^m " system" -Specify an alternate set of man pages to search based on the system -name given. -.TP -.B \-\^p " string" -Specify the sequence of preprocessors to run before nroff or troff. -Not all installations will have a full set of preprocessors. -Some of the preprocessors and the letters used to designate them are: -eqn (e), grap (g), pic (p), tbl (t), vgrind (v), refer (r). -This option overrides the -.B MANROFFSEQ -environment variable. -.TP -.B \-\^t -Use -.B /usr/bin/groff -Tps -man -to format the manual page, passing the output to -.B stdout. -The output from -.B /usr/bin/groff -Tps -man -may need to be passed through some filter or another before being -printed. -.TP -.B \-\^w -Don't actually display the man pages, but do print the location(s) of -the files that would be formatted or displayed. -.SH ENVIRONMENT -.TP \w'MANROFFSEQ\ \ 'u -.B MANPATH -If -.B MANPATH -is set, its value is used as the path to search for manual pages. -.TP -.B MANROFFSEQ -If -.B MANROFFSEQ -is set, its value is used to determine the set of preprocessors run -before running nroff or troff. By default, pages are passed through -the table preprocessor before nroff. -.TP -.B MANSEC -If -.B MANSEC -is set, its value is used to determine which manual sections to search. -.TP -.B PAGER -If -.B PAGER -is set, its value is used as the name of the program to use to display -the man page. By default, -.B /usr/local/bin/less -sC -is used. -.SH "SEE ALSO" -apropos(1), whatis(1), manpath(1), less(1), groff(1). -.SH BUGS -The -.B \-t -option only works if a troff-like program is installed. diff --git a/gnu/usr.bin/man/man/man.c b/gnu/usr.bin/man/man/man.c index 398368960e..c5b4ee0af0 100644 --- a/gnu/usr.bin/man/man/man.c +++ b/gnu/usr.bin/man/man/man.c @@ -16,6 +16,7 @@ #define MAN_MAIN +#include #include #include #include @@ -104,6 +105,13 @@ static char args[] = "M:P:S:adfhkp:w?"; #endif #endif +#ifdef SETREUID +uid_t ruid; +uid_t euid; +uid_t rgid; +uid_t egid; +#endif + int main (argc, argv) int argc; @@ -138,6 +146,15 @@ main (argc, argv) gripe_no_name (tmp); } +#ifdef SETREUID + ruid = getuid(); + rgid = getgid(); + euid = geteuid(); + egid = getegid(); + setreuid(-1, ruid); + setregid(-1, rgid); +#endif + while (optind < argc) { nextarg = argv[optind++]; @@ -399,7 +416,7 @@ man_getopt (argc, argv) #ifdef ALT_SYSTEMS if (alt_system) { - char buf[BUFSIZ]; + char buf[FILENAME_MAX]; if (debug) fprintf (stderr, "Alternate system `%s' specified\n", @@ -564,7 +581,7 @@ glob_for_file (path, section, name, cat) register char *name; register int cat; { - char pathname[BUFSIZ]; + char pathname[FILENAME_MAX]; char **gf; if (cat) @@ -586,6 +603,16 @@ glob_for_file (path, section, name, cat) gf = glob_filename (pathname); } + if ((gf == (char **) -1 || *gf == NULL) && isdigit (*section)) + { + if (cat) + sprintf (pathname, "%s/cat%s/%s.0*", path, section, name); + else + sprintf (pathname, "%s/man%s/%s.0*", path, section, name); + if (debug) + fprintf (stderr, "globbing %s\n", pathname); + gf = glob_filename (pathname); + } return gf; } @@ -602,7 +629,7 @@ make_name (path, section, name, cat) { register int i = 0; static char *names[3]; - char buf[BUFSIZ]; + char buf[FILENAME_MAX]; if (cat) sprintf (buf, "%s/cat%s/%s.%s", path, section, name, section); @@ -679,7 +706,7 @@ display_cat_file (file) register char *file; { register int found; - char command[BUFSIZ]; + char command[FILENAME_MAX]; found = 0; @@ -714,9 +741,10 @@ ultimate_source (name, path) char *name; char *path; { + static char buf[BUFSIZ]; + static char ult[FILENAME_MAX]; + FILE *fp; - char buf[BUFSIZ]; - char ult[BUFSIZ]; char *beg; char *end; @@ -919,7 +947,7 @@ make_roff_command (file) if ((fp = fopen (file, "r")) != NULL) { - cp = &line[0]; + cp = line; fgets (line, 100, fp); if (*cp++ == '\'' && *cp++ == '\\' && *cp++ == '"' && *cp++ == ' ') { @@ -1023,34 +1051,53 @@ make_cat_file (path, man_file, cat_file) int mode; FILE *fp; char *roff_command; - char command[BUFSIZ]; + char command[FILENAME_MAX]; + char temp[FILENAME_MAX]; - if ((fp = fopen (cat_file, "w")) != NULL) + sprintf(temp, "%s.tmp", cat_file); + if ((fp = fopen (temp, "w")) != NULL) { fclose (fp); - unlink (cat_file); + unlink (temp); - roff_command = make_roff_command (man_file, 0); + roff_command = make_roff_command (man_file); if (roff_command == NULL) return 0; else #ifdef DO_COMPRESS sprintf (command, "(cd %s ; %s | %s > %s)", path, - roff_command, COMPRESSOR, cat_file); + roff_command, COMPRESSOR, temp); #else sprintf (command, "(cd %s ; %s > %s)", path, - roff_command, cat_file); + roff_command, temp); #endif /* * Don't let the user interrupt the system () call and screw up * the formmatted man page if we're not done yet. */ - signal (SIGINT, SIG_IGN); - - fprintf (stderr, "Formatting page, please wait...\n"); + fprintf (stderr, "Formatting page, please wait..."); + fflush(stderr); status = do_system_command (command); + if (!status) { + fprintf(stderr, "Failed.\n"); + unlink(temp); + exit(1); + } + else { + if (rename(temp, cat_file) == -1) { + /* FS might be sticky */ + sprintf(command, "cp %s %s", temp, cat_file); + if (system(command)) + fprintf(stderr, + "\nHmm! Can't seem to rename %s to %s, check permissions on man dir!\n", + temp, cat_file); + unlink(temp); + return 0; + } + } + fprintf(stderr, "Done.\n"); if (status == 1) { mode = CATMODE; @@ -1060,7 +1107,6 @@ make_cat_file (path, man_file, cat_file) fprintf (stderr, "mode of %s is now %o\n", cat_file, mode); } - signal (SIGINT, SIG_DFL); return 1; } @@ -1090,7 +1136,7 @@ format_and_display (path, man_file, cat_file) int status; register int found; char *roff_command; - char command[BUFSIZ]; + char command[FILENAME_MAX]; found = 0; @@ -1099,7 +1145,7 @@ format_and_display (path, man_file, cat_file) if (troff) { - roff_command = make_roff_command (man_file, 1); + roff_command = make_roff_command (man_file); if (roff_command == NULL) return 0; else @@ -1125,7 +1171,28 @@ format_and_display (path, man_file, cat_file) } else { + +#ifdef SETREUID + setreuid(-1, euid); + setregid(-1, egid); +#endif + found = make_cat_file (path, man_file, cat_file); + +#ifdef SETREUID + setreuid(-1, ruid); + setregid(-1, rgid); + + if (!found) + { + /* Try again as real user - see note below. + By running with + effective group (user) ID == real group (user) ID + except for the call above, I believe the problems + of reading private man pages is avoided. */ + found = make_cat_file (path, man_file, cat_file); + } +#endif #ifdef SECURE_MAN_UID if (!found) { @@ -1158,7 +1225,7 @@ format_and_display (path, man_file, cat_file) * Couldn't create cat file. Just format it and * display it through the pager. */ - roff_command = make_roff_command (man_file, 0); + roff_command = make_roff_command (man_file); if (roff_command == NULL) return 0; else diff --git a/gnu/usr.bin/man/manpath/Makefile b/gnu/usr.bin/man/manpath/Makefile index 81897ab3ab..c86bf1be84 100644 --- a/gnu/usr.bin/man/manpath/Makefile +++ b/gnu/usr.bin/man/manpath/Makefile @@ -1,14 +1,30 @@ PROG= manpath -MAN1= manpath.1 SRCS= manpath.c -LDADD+= -L${.CURDIR}/../lib -lman -CFLAGS+= -I${.CURDIR}/../lib -DMAIN -DSTDC_HEADERS -DPOSIX -DHAS_TROFF -DDO_UNCOMPRESS -DALT_SYSTEMS +.if exists(${.CURDIR}/../lib/obj) +LDADD= -L${.CURDIR}/../lib/obj -lman +.else +LDADD= -L${.CURDIR}/../lib/ -lman +.endif -manpath.1: manpath.man +.if exists(${.CURDIR}/obj) +MAN1=${.CURDIR}/obj/manpath.1 +.else +MAN1=${.CURDIR}/manpath.1 +.endif + +DPADD+= ${MAN1} +CFLAGS+= -DMAIN -DSTDC_HEADERS -DPOSIX -DHAS_TROFF -DDO_UNCOMPRESS +CFLAGS+= -DALT_SYSTEMS -I${.CURDIR}/../lib -I${.CURDIR}/../lib/obj +CLEANFILES+= ${MAN1} + +${MAN1}: ${.CURDIR}/manpath.man sed -e 's,%libdir%,${libdir},' -e 's,%bindir%,${bindir},' \ -e 's,%pager%,${pager},' -e 's,%troff%,${troff},' \ -e 's,%manpath_config_file%,${manpath_config_file},' \ - manpath.man > manpath.1 + ${.CURDIR}/manpath.man > ${MAN1} + +afterinstall: + install -c -o bin -g bin -m 555 ${.CURDIR}/manpath.config ${DESTDIR}${manpath_config_file} .include diff --git a/gnu/usr.bin/man/manpath/manpath b/gnu/usr.bin/man/manpath/manpath deleted file mode 100644 index 049d5b9b31ae6755dbfde12f653300c7c5961a66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61908 zcmeFa4R}=5)jzsVG6|C~Fav}S6(uSt5rl*fVGv3n89*@_i4**aijWW}Uro*dtR$Iq z$XgD_(P<0SDzvm}Yg^w}|1BtffCtgz<~=KxWIu6 z9Js)N3mmw>feReCz<~=KxWIu69Js)N|9^3yR`mMwsvi^<1Qc7~9$TQ$?ys~7IYmqG z@3Ym^DxK~@>k|F@y4ArWeL{qi#^Rek{_BN2YWeu+M7<LIi`z^Wc8m$F56Czc*8iDo;fag)J;B&?@Yj;?Q3k zAjB)=umFL-I+4|ZAZ29>27{$r5L~!WpFV^_9R9AToayf>m9MPo=kJ;+5BGO1k^4pl z2mM{&!?j;9n$-B?1QBV-IvzQo&qUhDo?wCg20(u4&+uJ(Q;iV%J;P$C<0>JtY6Ha` zcinUE!Tn14qNT;t(npo2-*xTxim0F{XLK?Zr}R1$uyhBdPDkqHlK!WwQOUenH|Ni~ zZJrpta8cQ+WviA>OE*K(i&w2(R~+^46KRDm(?7X2bQj}3cq+Lx8g$m4S0qkca?6Z1SOQ9o)Vl@`XRxD(qADsa#=8# zd06e-)Y$z}6ra=o zzk^FmrmEY7O$bQV-o_t+8(IRsGJU9_&t!?cCaO4%pp?F*kS#2wM1dO79gXJeCn@HB z$Xd;D=f)3EO&_{}%XfXcoaDPcMM=)Vz0&Tx{z^IIyFOLkw>-I^G=O~ciy*m_tXBT7 zO75RgDTc+^OpspaC8wZ0}Vd<}l7c9T`4d#M| zzn~KJEhufElHS$dEWNX)mBLZd;P6;UFQSrGTeqpZNgTo_5HD6=Vk-ty&zf&ZY~@=K3eRWDAXD!C#_`r;HJs_SiFb-4SBXtbWA ziuz<>@So}fz3_C$=g}yu28bq!-HK)9a!(&!yjoeYWYyK^y7D49y<{1B_^9&B<2o_I zA=_n3dP#NqlGVlKl&W|=s3AH&&@=uv7X&x!IEyYCkJObsD)VqKz6W-`sphpyuNx)z z?{pWm0x-3?OMxRXPgwMDzr8 zAEiB9(cfQT7qY#uvJ z1czBd6r)!SoHa58iXLW11&SLeKE+H=<(?j|5z@u7 z^7Lh^(&f_P^u?=Jl`LDzY)_?N(xG4Xh!p5DnYj){y0$od5i6S2Wu&Xq%a!}B3{nM} z%W58uODgHBF_#rX<)kiJvIHvs8}t`NVLffMYZT+$UBqWnv%%o`Mbu_pJf_Nt zXd<)6%7Y3^A&4fY|A4^}YUMRjXucWrE#$j{zSl^!g*-R#LzeME{V?Rn7@jA_=nO1< z$0GAxCS#8(_JyU57|-<6&GaZRacYX4-Ic8ps^>ax0J7U6*!3{#aXhm7^q9qT+1lv?YaVmzRkv3V> zqhz2{OxeU!c{NEW^(558w7ZJP?$Rn;Wa5o3goU>St#3%rfLQZjL?anx&a8#C7-Oc|(AgMR6Aj#ME6}r$p=s?v^X&og0(}`e z2dYy@BB7r$#=C&+JKEq{YD?GV!3S{Nly6(8_c5x3GRjwSj6az%4#r}90HsHdZ5VbF z)UH?|VP=#<>7$&=q7~_DU^qf?q(87)Nnf`JCSOU}>XoqQ7R$?4ufm`(6D{^HqT?~w zQ}e*SmWSk*E`h2rc=U&UR24^LP_XnTR^WCHv>P!pF)$z8zW`#AzG(5{;_~wJCB>_j z6)#CIf#Na;C6hk*QF_{yRr9=?y5qLAiK+@nVH@M$uO; zk{O~%OO}EoKKx9(};?u<1 z(~HrUOv0i03rc5L>^^}eAZ?fyTu{2)3T@y}lQ(yvFbDUKDxZejQEhbJWi)|Uy@mt| zwZr%WUZXwD=}E{UQ*E{HP|yH>hl9;TNLj^m5a@%FWfyk=E73TJS}B5KHVPZ{sF=(d1AMh@i$-SsBjWvkXMTCr@2#qZIh z$}@1Y0v)NC8njnAcUtrU7QHJCdZSHxXd1R5$UIP1te|FL8pfQ?2J_q~#!VK1lg8}s zL(qX$5RpP?!sDnKP%rbL+VDbT894d&-jH zl10i2xg325G)u!4!37pU9i!6u1iz+qW+zNvv20Z_sB!&Er(sMsIGJG4dyMJvbLxZ3 z#~0|A8bdcmi)6Jak{AqT=j$Gr6x{7lE-lX<8R*=*@2qcfrF zZkskNdL$Ado<&Z;CvyOm(*Ix#oLW&=jCV8U`9Mi=hLQ+SrSzx4+!*}=4a%^{q2;+E z!-LKj5}Yp-L^B#ALxP(VbEYOJ|Ja-`dn5c9H`E+d{$VkJWCF?Z9GDDq1SKh}7VWTE znl&{+{s)t}6=cjZkWWdmXV$8nmZk7|=aB-vA2HnYh&T!s7V6W{x@uP>p~v?l#I=h> zd8rtG|FTu%agFgGBxwSq^y6j{44iw}Ap#2!`(0dFV7XX{Xv{!y8&9mL zy@^*9cK?i46;)Ce>vvFP>eb0~zg*}aVL*X|&52&#=&!Y97~Y|Z#~l&3>yx1H6^#Nr8&HGILAGhZrA-&z4R2W8mINwce^{+_2=lA?ZW92jSSW} zqQ=o8eJHIwGACdg;ts9r(9*V-A~mL>gW)ehCOrXUlMU2TRR_nYPxxfhol;=7t;a^Q z>KV8taW9GFk-yiRt-Qg_C8->s^`r8s4L|GrQr#bMLMMEh$U*FmyjA~zc(Tv`SyUdP zrtP6pMC?HnOHEMs&96CL1!LXt93mf*@0!E%Wq}*Jec$i?L>cUSVfGj3bCgQGvs-mA zGCQ|buB@~0NcE%H;WA8k!XXqfaBsJ-3IzJhalSD3i^$c>r2>67@s^yg6~m&FUDagc zK5Ydh?Y@kX%}eA`A9>$>Db;FiAOHUDNMG3B`@0i41g$n58ZkXEFM);mNUJ-EVpb;k zYr7*U_)6q^cadXVs@vmOn}jYhHMmf}3~hvlrc&;Qvn1v!Oa}Ka3I^Ss@&H&l?xwW~ zYEwR(weLa{)%F-`NY=PpqVs3fLN2lnYH7?1@X~!+9x_XVn?bg@k13yK`@1f!e3xZ5 zP~2sxWy9cDwqix`QW)BX0R*>S`l!XJY>Jp6WyRl9;5x*NX;u27vZV?;Ud?FSmt$r4 zWiHs~n<|v~?#bDJ72|$M7tQ8>(s**MGTex>UJ~+E*;eQ!(Iko(N}_0o;3nj9hDS)y z2%NyVS3LO)X;_Zo@0upNFg&^R57}8lp7Y!NGbqBfN?#-G`)D^KEfomfbjrX#V>-U! zxO)QM?0>)0BG5-mjF4YEJ35&N<(K{i<%oPY=vyyMlZS+oj*`m{AFGm8k#!cnhbx-> zBxT~RubSIa6V9b$pebWY5^_lhW}5|(hr}}b5SbaH*>F^PH~ZJG3|j1?%zJ1WmH8zV zv!^W~3;s46sV^G0sjLV0TYW+OEbAs7 zNEBgGE9vU##6c8ksM<6piK5kYC19jbrP*pDhFQox&*2;5bv*LY1|LF^piKt7tV8%i zNpwpnG6iYAZyQlLzI2m7P}64+^?itF_&YJ`1cRa3>KCK^xSpsxmSMQ|4*d{y6X;bt`sHZ-*Q9Fp!>w{@=?h>kJPefb zSsrCk-HLuCp&Ud(mz$1ql&CrVOVo0Qde`t!&d4=aqaHb9xAA3k6JOFDqlGx@>9)(M z%M+GEU(}I8Z{l8Qmc`D)CHn!sl-7;TstpHFw_vuqe@szV#=SPR4?K7%Mwi|Xx?S^j z*V_h($TH|Tm>4$)wR_U!RXz`D121 zhMA^?TGSEpbW!`5MpEF$sHx~`k{ZxpN*_ERp$mBQ{D7w;e}O&`bZTlh^wB&zjXT^e zwW3pT_C$p}>u}cDtm8rVnX(CiNy-vRZ`(!LP;YQ)0|>BB7eLXKmF404#t>ewH$ zX>+?(+vCu@NjZ_1A!8LTA@|W{!V*&5YGZFb38|rqI`Z`jC|wc{2gGBbvBF0F4E-Sc zHXPceV|_4#^&g-^sEp&)rqRzJYhITrZVU9WaRi-cSf=)1Z)%gyr-tFpRA zCPLa^X+6>eI7+@glX@W>sCHUj`YE!e<)nVEG$0DwC-4cte0|q4UkyjsSDjz$qk-Hh z2{Xv~yLQ}id+DD+0H)t?NH;i;pbr9A?kYWE1SjFne_&+zdk}$Op}EvmpkIg7^&7`g z)yFRkY?p8i*e+w$dQG1@}#Q%{;mY) z#sQ!oj(`RJu70x9-*vHb<7V82oA4D3)eu)uWf2aOb2sZOECN#PY@^ubki)eTJqy}I zKI$8aJ?7U^CaEOFu-QPOF;*`n_jq z%orZ=qVMgYHcD_lat%rvidmD2@hgRf*8nF4+*b@qv%{xH=x)9QU&~kf1uel$O zlL4=N0c`oZFw;gvGUCntXpW=&T+*-q%5!?4H>J?X1CIX$0G~h-+EqMGu#YGU?+-s1 z3y1!V52y7pd;&>V^$wpO3m?}zd}1sd`ZS)-C9&}A-r_zRkyg>Y zX~e9#YDM>r%BSk-x{ng%!I)aR(NgO5{yH114CHj9@(=a2|74~B;);auhZ zz2o!1*=|Ul5ZR-z%qYLV?7QR2QznngN*_JDxa_;|s6CL8K56`fiR0awD6w|aNZT(R zevHAnBW-~sOz^EnbQt0N=;{9Oe3BE(o_2x5U^OxM6@k-Od*d8ZGNDn*GxcA6%5$$!4mFd|a;B>}SmTvbwSuYYpvYp9 zi3eRm!2Z4RI`vBEzn6?GwE~wqC|aJjAX+|L%NZGM1dWpF#g6E^evJQB zmnjZppB|pesxN3(4?)@CNeGHLW4M3WF^wv+BaZ~-%7CW}R#Z29ZOf3gT2HHDPkuth zg_cu>fj~kaCr3?Gb6laEG*bK#H_97IdKS#gg_T7TC$ z(n;fy|4u4NDR%^lA<(flfuD~0d2AW0#Q$J_v0-2U?m#2$F-}{N5@KnMTpDCA-YC&t zGDD<0w-`P^wU57|T~xV3$ux@Yrd2{R-{3ZDSNqe~{F`FQUZ#gg>r?tyKgb5QlJqjA zBJ*vh+0nqCSef9`nqDRktg=sEB#)&<^6BejSL8a_mq;UEPaXs(c2oPmOl)~cz|4=<0I>O=e*>k|S~FEakqX>Y_Bxv%f~`xTe(L1_$TCoZBxLs80~)gis+S_YE> zwkHBMf529Q`2$8)@i9ieT7`Tk8G(F92!W``?KU;vj-6ESfxNwU4My$mm*9gEXP)-& zyKVjh5z9(7`b1BM_&XxK(oq9J*K+Jyfi03+;$M&DpHcqW+irZ|3=Qh8<*E8=2$Gt} z;x8v_mqZT34^SJqwIr17kc3f$P&P3{gtCc8xKOolNi#0oOAx(3%(!gs3!&^u#-m*0 zGLJ4j{*?m#6}UyXXk(JPP{5kn%B%b{sHj4wHB0T1G1QeR2T&PL17KRwK!$CiK?Lnt zdiQAbqH?qmBoFsJq8mygjX;eTlSvZC*bIf_w;yuH%pyt3$&Vy+Z~MfeYxJRhl01Os zW`@lElXn6+jF|C+=E0UWs}<8n@sIaVx-MufBV{C|qcSNpQq7cbtSo~0Li(IXYBG#+Bz5JC?0J@#u;@A zbo~%*VVb(o9vKf=y~UBux-c?27@EuN8yN=P7ZgU<4cF$5jW+URu=4GU{Zc&~Y9Gc; z)=@^-ILOMJ&AmoV2iNK~)OPFam|jMPl=v$lYmPz~4bj6XyaC2v4|)Ain+d<#F?M;z z2Ey2di?#_%OeFV`Qh$bhf66Ipl@s_n%C;b=J#T~n6$IxBJVXYAuSv;a(*C+Lbe`9kNV6MKo$We1p9*n4v z4Xi~>ZBmi~v!juu8B55Nq7kH}SUt6mRH9$rK7)a}!OQD0SQ3wb(-0U=L2 zUuwqU388?uP2EZx0jb{yD83q)gm33wNv)X*0JePO4u3ti1GHoNTf4ujPv!9FF3OQA_#Uq8tZ>%+ z2IC#dDs88t`S%cQxPoPb8u^B5GARh3P>sVvqb)QNh(7f-ITUtYG0n1XC$j$it^Ut% zcm8lErjF5(+oxhi7u+^ph*MLI|J`vo78bi6z_a$)b6mSkybbE&nQ!qQqx0{s)3Z<( z`bU}4CiIjomQF`rFxf z|2*mpyaU+_)x^Xm{5p4ntm7JseFTGTVE7a2_6`JV0TP?Q-FGy8eHOKi{;rg@jd8Sp z(wp{1rfrnl7hZ%rxK-omb59D~eP^T51)Y0SRL;_@LS3W(9MdOmgr^WOgO)u!7&iva z4yRby75)M)Eh9WV{3*G&YVwg*t)bp&a%1y??oC@MG~9^Ug4G5tjho_ZUAjCFG3^@o zHoHq(&>gvnbp*B3**cfpkI8skT6Q;P5874^-14ZX{3N;$|T6QUi(~_v5uh8VizlU26{$y^^9E16h zt58Pc5eycC0g@S&Jeib+jG$f>Q)`AJSlybA?*;jK7|TIw4Yj-BMjdY5WAWgO_ph(CKvJZ zqq|tnYUa^Rpyn1Mj2Ii;#kGwm{fTDX zmBU~0Fz+azrApE(#kf;hDPjN)vE(ncA{1B6(6}t~RK6I+!_2oUUvK5yhk=Y5O58)G z$6)GbAUPk|1R9asjIxv{*BfOKl~-w8D6@UASima%8A-m|C}d#6G%1oF)VOK=p@c|o zP}@%Bc5kN~ss^g>#)MOnM@L3+Thnt`6uiFv+=@V zU^Dx4+0qY?%&o0xc#X%$pJ@TQQ6=?OTlif}Mo9jRBD>^^d!$(l(2%93hF|FwCp$h) zi4my;v9?kyPR*T1+sdh3;R*^jME0Be;N-$ydyn}-u;#S_zKObLp1`mx*rm2lqtawkM(#Iu3-Xk>9>*`(u$!W=jBLc zw(^Dbqn1P^g=m+!>gUu4FWSI2#b};nZpzq1U>=`e^tH zJpF&3SBBPk)Z)hXQLhN<7~@Qg{ps-e_fnM;Vsh&GAGHxTK#aHqmAZ%YBAi{Ori`h! z1VfyK7N9<^2FNa|Ik+LU`e{-cRnQv&7{i8=Fi8wCNhtly26ArPhSv0dE;%=TjEYvJ zpj`%smv9(XHu?aB3%R|cY}0MtN@ek#?Bm;)X;^ui1{fM%W4-NswlT5pqr`ZTixq!w zEbm=b-JBaQ18tu>(|BFy2E+QSmomfjei!s9=eb;95p`D@@{R7I-1Py|NVB@IPDLGb zYdgL-2MGJ>GBvL6Ci_$tEx#)apg!HoW*L9Q}uU!%);f*#4i{7xUkRSK+sHZqBI9;t6A zw1t?42Y3iqxdU!yqhV7ZACF%H`%G;o)ID~DnC6%42+W0TPa*KK#Tp_(?&IgM1dXLg z4cHJt;~;lkMY38$_h(uAZFJUb0dZ~nXatSfP7koC_plFP{?A>NxuN8dKZO36V{=i6 zr^EG)=Ko3d@xmSBta%2rL74yk{q3Mp^{PSR-^?DK>`A`NNFJ->F!iJAePnH_Lr@N4 zMby)Qp(Uoj6XezPHQ9KcxtVRROz7j#wkEmDz zRV=T47G6#ApX)WRN$DxsUtz7FV#dHRY9mt4@Zsy<@^I3(+DNsvG|dZ+~D5X0}^~}Eb8hI zwbCY4bcAU?gw-dCoe#f`f#KN4Eo{=g6DE@_*VmEltT{qW#-ohD8iHwfD8oX@PovZI z$Vm8mJnRHR$-gneJ2YiKQyi;}4~+cdV4CA^V@Zs%>Et$N%2pY?G*T#hCSFEHB8Fj(Cj z)A`7ks7TBcVWd{)*tD%gPx3OEukF3_d&)AvEDM{VW4K}ehkfE~n^nPAQP%%1e_rfK zUSTAU39)ly8Dt@p{JY+Q{0!<2eP{L-EkMo7OeAnV6XSg{c|AD|GIQd@5)X6d=ktxa zJ-LU>A(W5$g;f@6fHAQWNqO87R_Ag)Oxwi6)%c>h95+@KM%zpzI=c^MiXBgkR^6Pk ziIsDd$H)4bS%@-D)st9JC!eRNEs2S*?0pfdRjx8h*;{#deBIgd6^2QnCL5x2qgn|6 zM8^$@hG6D(M2ay_p>lGc|FzkBFb;m(zVpsURw;I%a^FfeW=BS%|HpXRqrtv$Oj3F> zx!%Y`)%~?yR4bmx725t)pP15NHOprpx(( zw7yK~av09a#gJH!-5BdwTvK@OUq+LfoH!)z8I6OAi{5_d3%Q{ek~5>8F1NRwq^G3Y^t9lT^qP3fwm6@>l4T1 zsA0IlDkleTcnywK@v~0Y*bV3uoB12Fg-rwSyz0FhY?+%Rces4;<^h`q&B}3y%g#np z>Ju|_5T2N$Hf6MflSs#<;5Cn_oPVIGo%UOhHD|6Agouy0aTphvfDlYiYkmQ?@vlZS zUSg}TtL@=8@g%60;{`n(+l~DdZ9<-cosaGu`-W6ykmhM~=Qt|*YV++Gc{*0j!5^=# z<+Z5=c6GiZX)m~LHqrFN=>-0F_v*)T~5hjTj-&J~f z1ZlGlqh&{7YE?A@zU?X{m0tV6UIFI|jU2^TcN$C8BbWW=43xhsiZ_n>T5uxN?S7aN?<OMhS zS6wT7GzX;yYzo+K!>j}wK-}Jr4fR;y^|%Y|RTH#1w(NX;1R6x$ADHh5R64XmSL}_a zT6_M&TlG{31BAO!&!bo;$ca4t$MP zaH)%Wy0Z@Q228x_TbUA+&dzcx-Bn$;>OV&pWQC*7N(;?$k!|Ya8=(mZ#pTpW+lK*BrOvsQMs>_H6j~r_!quz2Z^lR_ly-uBs16<;0e%P5x)q zcJ9NJ4dTXoJnBxm=pOqpll`^!x|4RkYo|L~-ANnY)zV!;-N}TS!yEc%b5ORDv^yQi z%r?WanKt0;zCQBRup(rvo$|W>z681Nn;)qISZ5yGk8N(iumq4QtF{C#Ewcz8x$r6; zXaAA?>^`TF_eZv~`x1?eKS}^q#gYByM@kAb7;exP1-& zwn=9D;MMdTmwS$@lJ@YaXEdc-%dycOCU2K^vrEnEkp2vt*Ii(*kRT!C;z#r0^{!AF z%;7-VqpJ6m`|a}n#^xXFzjjxgT6fyNKTRzaR~zm1fP`5lH(fmQ3-g zoWCwj%{%J;o6}GJAJ%=%&KC!&UL9|z6loQww4$S??n67i*atGLJ74T8H6(i4R8OP2 z58|zQ+q6E0yz5%uy08oKT`&C&WuU!n^cb7=6H2ta#uBwJGFNOa?)^+fSH#zi2IM3TOCJlE780lRgh4kP6RnCZ!VWwpQyCOWlfp=5%E0DHoxa6 zOt&U?UbFKDgVA@uH*g?u_m8k(UYUl|WulnMV{=rcoi@#3i|!{z4E0tQZ>fO7#lkt* z1RX$)R^d>)uyIlz3isxunximNNJSq1oLa^X39pML$ferkr${}WRW2_59wbZGAqh`X z5C#WKZ@Ei#hDa*pi+E4@ha@WdiPB1#RYFgw>H=AuB(I@5S$~`x;^Och5CQd zhJH^Mi<4T>o>hyE?ry3W0=jwaTsncBiEwpfyPGz&!TRy|dz-k)3nR28xrAe!3$G$VjLq)rGF_dxyXK+F9tvjKPpm#r_*r7oCW3*I? z=)^-KE=7r;iEe<2qTYffZo7U53JUWjc3cFv1?q|3js8>U(q_3b8Wnfojx{pRD82wKjdWkx;#z z3XI;3P8pc~Q?&wvfdgzsCx>M9=4_kMQ)%7M`C>Ap5?wT-QEE~>r_}v*pS!eVbU*M; z8;agS4-EeK>uvnFM4g51S8=LDb)ywEZ=2iOUiN}p=~z3J=$%IgslPTI;;(y?UWY|L z>7p&UZXy3ugLXn47aBdJ$><@>J9>Y$`aLo#s_TTP?XEdk>4fx|@2W&@2A$4hb?4Ij z=LS^`*-dYj9_b=}8t<}1#^JfS1 z&mDPREo$SDAfpS-rPdw!NNr@|DQGk%pRC=}%`!pH&mB2w#zxeL+JrptGHLbeTpVoa zMig%s>KG_)?Xl1OZB$o`F74RrkM3s-2E=tT_wW*QbukRTXcgqJ?qdjKJ32MCto5nR z>!Ks_!6Xy~hSP1domGDuD^TXE2l8b`VVZCz4a)O(wY@Kfnm+2WBP+pi^i%BUh6UZF z&b4t@qTMq*!@z(e?KqD{Q`!R?FZ&Dh z9lahTy@b63BIB5*q^bQQ8I&JAek1l+Q`(~Pi{1KPIzjz(-N~ekeOg|ciq|pmW@<0~ z4CC$#El4#i)SIC>TZ-DLXa)LT`9_BYHU=_0$2BGgqLL)x)wjyNYGYk%8r&pUNMxAd zX>0McH!C{R=P>{!q)#?~i`JQ2C-2yTRpObGKX?wMkKO-uCSs+9a@7f-E@I!RlPf3xfT%h_Uv@-wPO?i4^HAfIx?W=^B8^2ZRk+9{_FqUcz*u> zzf1FNpA+408}~P6+#R@vJlu1mo}HR^Cth(-`iILgCI%4@c0p1(G*cN429bnNI9TAD znkoMiuRJ(6=3;7zII8#gtm9eHkmmp*iLA5Ey{VC_{9oFu`UY&T1vboTq`u?-(plAq z_5j~Kt8tD&2)y*48;|#X&$%lbRi(jyZk*v7HVOnXm}1tv0Gj@DnF=-`M?JN1DKq6y z0<->#7k`{T{5j$n1<^dUlIMWgu!!%kYm`@M-df3fAXKv+b)t&6w-TzDq_)pN@+t=v zbGio)M8xU;(p8m8%yx#ZTt=v#eZ+ohW@^7Hvgw{)d9h zFi|D?{&U6H7I4nvtT~N4SRupw6}3Vc=&yKHV02<(3;zRiXOcBoEg?@MCBtSdl05&p zF*3fc#T!tbMvF;iHb0~}9Wlyjwi74FlusK>T2}^Er~#@tbLv@mYSZcmC^>M;hW4xZ+G|^7_)Wf;167gs5lMeoK$)7 z**eK2+OiEQ5KPH=^BkU3{k@vB-Sgd?VeA=ZMb+=Kz3{mZJ2tHQQ8li?4QmHOHAbVK zR6$Wf5TfC;$RA@PQOHjW>$symVy(DhkJ$OunG|EMS)G><4aGDHNWPx9G9Kt%Mij2a zs~bh+N8T$P% z_%>t);uW4^JTu1w)4Yk;TAEs#7Gi?X5LKSnuf!6Y>Iq^IT=NE_Wj6g|tiPz9Etpu> zIW#<|Mc)o;3tX5It6MjKYnb~l$iG#8;~@JE0-pB#fVTtgMSjuwc^XJPkHM0-5lfe< z=OMiONCLZ|f7U^~uUK(Zt!R!6q;dFY=Lz*w)BBYbB|qtg~ zjx9sfXJ;g!-75Oyw3X`p6S0&+-8T3N_5zAAMeWiF>0NafMRR{ynXGar*St-Jszd;b zq@ROD<=`(3BtFnxMVA4fbXddz?$mb5z-)kLBPk2(F1%Iu}yVOIDD-qKRWRQBJ=yazac*B8qRF zp{zG)YaDo|12w}G@S%KtDTNw6nBEGim`wxAJ&B|Sx}bq>1L`E9cH(9(Zk9_?Ws~k< z;+vSlC{RH2*3?$r5gvsYSPLOuAwIoo1}knNrlj!OImHb2?cE2;QRPkwq%H&*a-nC& ztm*Jc8fJrLp2|cjhH@=TpK2HuFwpnm8xC8_q598kJbEc9ZlmGfaMIve*xAmFkK=~w zG8@K|DLl>&NbgE?6*|-L*vCDtXy?XmR585NNRL`k9g89BEk#cuYD>}zSToH?T za@IUz)N4e@^AzzhvRZE_#Yt6FCdF5;-lqpH>&(d5mL!>ql=l=$NIt~)`}l%jRI#^so+7XPHq3si zKV&At3))(xtcG1ouXTBJn8Bu+GdcAE+P67xo6*%R=7Sa0nAHAa!`ji+6<9X=gq^T@ z6!k&ydAg5AaZC3_R-iuB$TmB1-Hko<`tlH#t;*eI;ZaODalEHqaH!taV=G&aZ{L8X zJGS!F@frdx-fgH&Dp!SG#l)CpmUc>fX;DZ^J-gpkR#ELi)^lp7L-jCO79Nm&=V_86 zFc4&&y&G$EOVWm(CilqsElC^W?tR^oJcHl;YDs!1E?gc>9qm3W%}~d@ov#n+`F!1^ z&%-r*2Ao3KDc)2wUZxpuG+%>yI$t;G!*DfD>Vu7e8Mm4M4ezhmjn_MlwFaJEfn0jl z9@2UK@6gt|Xgcq)yVtlXQgLF$A27xtKTNFHqt=EOQPJ}-)5L^as#g^qUO#fZy(@*Z zWEfMYa3n-y*5OX&6n4R5!wa8!Y>f^bB%_$rXhUe?t+Y8`+y^%sCXI=C9ppdzoAWuP z-EbtIMk8WIqPsBNbG|sy`Qn=yf5YiLd0lElT^9now25^eyPPk2TM>R*+K0FtYB5J3Y=tsK!5`X%qDDRK z=arpn%de-Wsh&3QCB80JJ>Edkr2pG$@VHTU6f`j1Pqn%9YwN-7rmlg10-o`s+_ZR^ zW7(=@@&m>j9mIV8bj@6pvwHDwet3B19~8B1NXP(zBwSg4t+d547(oLA#L%FKI6E#kDU{;`#wq?fKnn3}f*3xz_y z$@f5_z?&s?c1+$i1lHk&`gGMEftxi*tBQbDT71`5`@HL+-jaJ z;n^7J0^Y6#<{=yGtHeeam2|bE?xVEolTjF(su!=hx!^_~i=IP04I=IBs4m8M;S|+* zkoxwS)t*K=e}R=f41{H5i+3erT-rAQ1_M#}EWI-QrEC-@<4w>wauQx-e_bmm9?{jh zlsCyN3BHt$=VS63DYR9sQyY40aZLBC>FpQSm&l4749JQq*OD|9m1$nt-jb9JaN=M~ z(mcA>wIt=^`c7WsiMnGI@N~A3;?$3=)Q@`)99!9T?Bk|m#=7m0;nHE({7*)fj4`JlPNj%EDFZ0NrOohRgJ zYCBN|mDnWBuATvX`EsH$2sB7Iydd+IF*~xyD7{M)Re2H(>EOkUS2m-d4VCZArS19(iDC>e7<525D&~Jn60GAA(9T zWoB~zm~Iq~bqxBFr;V%y>PvW&@%ZqOccDLl1qJVn#Jt*u^85B@YThlIE{3aM3-PVU zC=7axbK^tMD97rWi+hK@f#QJsD1h7B=nTAuA|0!3&Z9!mmD{5l`9on~vi>J3qES8) zJEQQu%h=Q3|3w)+TQ&{DsJ_L>R+-+?gv`KZkG<(lW?u`AgqVmdjvWL;Oa>|$@S7va z7T+}Zv#5&US21Ts>0>1gv*m<&9%gVv-P4YhIyhxiFJ^PKDilE8A+~fM8UxzU4*i{v zTn>cJWy&x0#tJtza@EA%jZ9(AhwghEIH1|A@JK>GT z{DoE2PEk1=Q_NxPY-@CPRVJzx5Z=ZNZ*zusXU5w+*SwL-H4&56QJA!D(;vnL2oQ)W zPvz@>CSsV~KBecs3q%vCG_9y97k5ny=cRmIHIsHSa?>Ishl4M~L+?%re6eQhPwAIg)iY=7kLi8m>I2Q`!9;)<(TZSj0ZlUz znv!1$Kcrk{&R9X2rW7;CbG z{R*|2@S8@J9aR2TDN{%kDJ3<+D)?=5*r+m~cFRwXXnHcpP-RwCUQSg3se+9floOv7vevry6D&SN4?Oed8 zkZZm^mCAra-*BKpRf+;r{V4XirGN@ZPLqdjhHn-s*A<=*j)4LO!=rATZS37rpWtRi zN(M8^hfKe$r4b|{+@C^l&v!kl$+__<$Y4woGEosj62Ne`cZV{Bo-ELZaridXvjhB$ zToE+l@6ZK@?=Wt+>7O2_5YVh(+px2A3wFTcL!^rGeb{iA%?;1}j*9BQ9mqkK&m6Gp$E&{)6=#x`$WbDl#e&8$~WrzOvcla?$)o}w_dK&H6fFL7( zr9U0bqhT8|W!GYPDXPD~ZsR$$+gG9QDHVy`!z7N{jT+WQ!vw&>{c@F;r*oL-o zF*?iHx%^su6@+NOLVX!^ANsu)XU)xkh=~&$^1A2?2VZq*-@^$?b_5_*f_xWFcA>!Y zAHsd6tL@ca3aI+ENxSKSzT6!4%UWrk#H{o-yMDWi!i;O_j6k%D(VlNVbrH zjyfTdpBiEyy?@e=`D7%zTk2DVwvC2A~;ta+6La}Ua ziDiSzoL>oNO%Yfh=G~ON&IZ6aj%Ss0b35BN@{atz%D6&0+p=3fvIioHRZ~7UdtSyq z>|Y>d5x7~x!Z;|QTxcb>w~ zv);-II-8X0+_d4Muc>q8OS%VuJrVMn7)y09hQ>>bQ%{?b% zoMMIJua1X|L$owHnhGaH+5Hbftdk4$pQ5oqMUvCZlQFb{w9a?bf?*LlvnQ(-&k}G9 zUQU{tW4DgTpz^^zQ#wc$`a$!MeKY{$ca7w}`K3eX7DIk8zjQEQf&LJP2Z|edd}B6Dtemf5 z69u+VdAk>su9a=Re7(3=8Tc%}(siP4W$ptBE3L=0)h)s!WIUZH#0y{^Pj#P4-o77 z97~~Ch(hPBByoMr!{t#YM7jr&sA+CCqZ#M8$JHBlHTliD66DLO?DT^xePL<(yAtI~ z=>0%XH_{0@Qiw3vCn8rG7?t4e9(PWbNfpn@FH`D5dr;Q=?h{IA?s@E$cV=vVg#Ysd z`O>OBco9@ijV$qhZmYT%g9j?G8WrH*T?1YPT&Vjg=Y`1QD0WnQ{|l0E6g($UFpa0E|{r~WX`Fb1esIpvm&?m5QjTH`T zl~=Kkv#3oif}z_EyOQRtWZ*XmV@*dZYF3M$*F4W-W>|g^tQOeyok}x#iM-fadH}5` zFxes#Ul~Tfu!xiC$yJUR&#T^NwW883Jp>nKqvdfr{syfz_%{VfetPh^27 zW4z6m5GhQHj`BXT|3Qhar4m7(ri1Dj&#S5jKCV}>ntJ3U<(pTFHo&g3qG!n)eXJFKVQ1YaHKm6z)~evtUzS@3X{liVrIs1wPcNptNW_G($sY zF`5Y*a&~J)yJHn@EE@r?#46mVT*tFrgXKrd5^+k0R@5-xh|PH*g9fDMJs-`N6g8=! zMKfs@4Q`Ws6cf!drfBCSL>aurZjD+Lhc*GCz^Lm{ss>3*-=b$B2NZ_KsKnQ{A+R7cYD+aNq(5E^y!i2QF~n0tYT|-~tE!KfnP#d$vr@wKnQ@jVUvtLmgz*z5QSmqq|HdcRAS*MT5F*9uK=F)UW;EjUI^I4m z9{ie2b7`@R--|-rN#n96egjRKkkCK<#1qj+n&MS!nf}zTOG0rL$@CMX9H;j&yIJG2 zP@znaV_N1Rs2uTp9%h>DLi9>j;|ySq)r*A_#!ogPaU2w^oa6L5CU^39=Ae<^x<$+6 zHOrQKHC7yNh*tTsrK=XL_-cmNjnCrJQylsw7LMVL=K^BjP+}4&laBFgr5y2m4jTod zcsL4nN%8uxQj2f@o+!NpI2MKTyMOWOH4iYEtX@rj-S`P8c$R3wPg2o8<+)gXU`_G8 z9Jv?kV|7fAeMZwKcGGj+%aT4vu5Z?byLm%KFw4??#!Og zj{hOeHq?)tp;S-eUAequ+3K&hK(GE&QnErRhsHnumqB8Uz-_Q@(TeYKlwPW>7adFc z^-@KMVk&B)^KJV4%HoxaOUpR3UVSt+#1NftqZeYQO*5s*y+#i@MjbLZLG<)&17)7~ zVIHn1Hjg6jRUE5gTps++t|95Yx_z{Pys`1YfGE&j`2 z=$d}-j)d8`>TBmsq3f@<_kO{tDONWd~mU8?KYC`sa5YIl|XE<$?XPa>S0N znuU|r=7@%ym#(b)eU6y%qx(+X{$5V@L%Y^r`rP2${7+8)?bOBdbAS9+Uh-eteYvr8 zLsup3+O_*9bN+d5W;pSdsd*DF%{(`6-4E`Z{#toe%PoVISJkFR|8RKQ^ohTD=B+Ew z-r%0`n~vNsoKH{K{^&!0wZDAVsGQZ$E*YKNkV?EOS-Y+bKLO6o-AgH1qakX8jT{tr zU3sx=+8$pk&=f~5E?c>5wV|DQ4R@1c+KK30_*~$?1rA)`zy%Im;K2X8I6$`V2(w7E zQJwG>{Ab{wo)MlJhi?R4VusTya||y9_TisC44`v~B(5C1*__#DT-j{rXB z@b4pl&rqoc-xY@|;&5FYJ`smMi^GnD9y%l9@RT^bBo0@_;V0tot8uu2@i;E$VaBwc zjvd@_ar(lr_Hn2X6XI}D9Nrp->*Fx}WLzxm6}BFHM;v~Lv0>G1Wo%4Nb}+`I1)twD zrWO=pC*x}v?`1rOaRXyIGeL;A85{LF$#^`6M;Nn9LUb@T+(BP3W;0XRVQBiqM2QR?pHE4>N|n4QQw)2=QGV48G9Mu#<+y>y^I+Ojqz%}`Dt8u zT^v3Whw0UGpOIAD7l$8@!*9jm4}q@|)glv#66`{N6k{iFjLxl$;f%tkl(A8la>fSF zf5!NFjzeF$B3tY*?{dW~@xLs5LmZwjUa-RRxF6GPuDC_;3pk7y2;LLKxIlbK5%9?s z;2Zj-feS@~h3^!6&NRm<5-C>ry~1JPMPi_Z?-y5Cc!?Nm;bJikxEA#qDZayXX#m~< zjEiVwJX6q%#x20v2nQ?02aIPjA3tV13!(TKJI1qle%#GChr{V-NNa&E#lH=>6<6L# zgKMr>Cgxe?SuSYLEq!vucg1}cULi_>laO{~ulUuX)r#{25w@@zhc}DUR`^4r)51Rz zsn9(}UOyK7E&LOaZsDzBw1poR*IW1rG1J1^M6QK*h5zpllV}X2Pp%jtePH3srH?Iqh16-`E2S?jJW6uV zAj4^|mIhjQv_#{l5uPDo`=^P=NTV%0HV%)I##rH*(nJeSkg_d2QJQ1nNzyzEUnecF z@KkAuh27E$3tumN&%)EC8Vk>mwpjQ(68+i`eR9Q2>6aFsCH=<2InquGd!$z^JV)9K z96%e56oa^39tYk5OgimJ;A}BiqHg*-V4oO5Z|6o|GI19$9Rr#T%a%yi1J~k8@)_dr zF%m>Tyvgy$N_f*Zdd!SN$JxIF+=}OekodTXuOUMC3s`7A?X(0E_GP==TZXN$4L7N$!_6aN@*7Um1O?RIe7J? zlx~GTEseGCucd4Y|3=EO@NXs3Lk9idNd*@Ey>zFA|5I9S;pZgMJx2WJCB?%3OImN? zKS~=dyi+2%HsbG+p0@A{(lZu*QF_+GyQSwW{Iayu!mmoZExbp11-KdiBgJhjt8KtL zfQfDYWbBp5Hv1CTCl+$wv87Ufxr6&uD&sr3@3#o|J zNIWM4*W&sG`pb2|t-x0!ANu5qkTeWo25yk9w(#q5_%G5}EBvq0^%icF=v*};?VHk# z7Cs`~WZ|RIEf#K)ZnN+^(w!D=mWnOhB7N7wC!{qNZk5(s_&sTpg-=QkS@@LnBMW~Z zJ#OI-rT?^WSlR_V1^*+(0j~Re;2pr!77G~viQ9t8w?ouRE`%=z&PE*4Z_63~iQA%# zaXs_-LB{co^^!Gki=Oo&@YeIDHD^aA_QVBo03lhwJ0;sW?0uozlk?g}5;e zKOBdDABRJ6_*fini^H~lJv6i8@J(^JG!CnAcq?PWNAU~B1`mH57pEx>N8|7{{d@Ag zKMwyS4j+iapTyy-oIPppU~Kqa?q_^8b7&>w(TvwK&R|^4_!`C<<1vi4GQO7aQ;f$k zeva{Y#&o8g4?lcMpE|~d@8!>o4d2UKj1Aul{l1`27`~SXW5f5-!Pwx{7mN*gOGfAR zv1bnleK0nBFPAXpY(+L>!}l^jE_^j(!}qeDvEh69VO*SN85?|fH7@)VW25fvjK_0k zQO22!9ayCIiTR9&GB$iKmoqNm@Ue^;irX2l=9|Tg4LYkB8};47INtAZx!4JLr%$f9 zLcDC@E5%+5UnP!Oc(nMtg)>Au@GGD>61|-0`~`RiFzJe8z}djDPcDADI1T-PKDlDN z7;51xkzwHpVj}PdO!HbJKF$x>0Y4Sdq)#r|YrPfUE%1xxX81I*(ZbWkLl*vy_=SbD z#cwS@&FzI2&lPl&5r-kQ<%Pf4O z7-!-6Vk+=V&dbATZ)f_zpm9Ib_r#|1pqVc=T4`?;0Shk>KeDh_JY(U7;uQG(LWTR1P(#KeZJobN3n0r=-82INn!C=#bEn)iqgEqtFKy+WT{alc4`o-puY z;R600=!_J1bJ{x2>+ZO=D;1em{AFUIg}*E2TX?1LT6mSX8#u%?mvY+ondZ_sn&o1X z6<-#bh1ZHFExca*r-dI7JAvDnW*Mio4Wi=$a7nymO$;<$yY#Tg6x#TORdC~VLx z13+gab{kOIF-)@}j^-vY9B~X@sbYkMH8IJ;0r4FRKP=_}PhpzzX}5?KR-CP3wS^xO zn=Smf*ka))#5Uj?n9h$m?HydUA9LC6XZ#bEbE9lKM1z&~m*RB`KPg%){FL|*cqP;P z1;>Aw>HH$DzR!r^&}RnC--;0y{=FDy;b+BE;76HGeEjFd3MbC;rqpyde(%EDrCA!=X6*P8>cRher(Qp+7AS7slaraoEpz9M>_x_&Uaq#>J_R z!~clG!-n?cl^=(zls8Y+#z-4Q6b}+a4Ho{6c*DXo#Zll(L4Tx}!Zfb|-T_RuK_+lEumpS^V>gf2*8|rg zjHG@#a4W9!@J}DIADS%sw~D`8cwrpAU7WVU?-E~F_-@e$_Lo7YNc6Svz5iEx=NcnP zRmJhESybYNpdukY2VG=C2Bz!LJw4qF+2HOXWaT+1l7*rl)jh>@clB0P&+<}7MSNr< z%R@pUnw6-4ZU_h;2%s);O)x;R8WJ@S+?f3U1eNHj#P|Py?x~ud9o!fbzs)4G^{Z2- z?mhS1d(XXfYwE5!uHdG5d%;(k4F$K%M(`}`*zP`?@fhzgTMKz+&MP>PIkin^(Od!ftw?_ZzTsz@cZ2tUS+4JamrPIWe-OOM^m&_k z47?A=`|Om1C~+MRQZZUp99Ye6zWy;9Jbzg1fHkeO|4L=9(0kbSGfH#5p^RlpgS9KV?53=9m_fO!1IF`^T z{|x4syB}%(OL;fuR&RcAU<8Kd;pv&zX<-M@@B+?8a2!2tdav&uuW~ zB<1WBthFew`w;$iMVWuU?I=%dG^VD!AAGj*2^eC|Q=SB0q@002p?o8_sr)cFRz`?6 zpH!X)-=zEq_-5rv4B2-oZv%f*`5N$j%D00bRzC1y^cUrIThO+Y=fKY>XW(Bc?*boG z{ucN}<;TIVDL)HlLuFfi1$?aX;|qANpnT!LnD>qGsmiZH?kGRK13pwf1U_5&@8FLr zZ%mB2OnDC60Uv{Wues8gLC&c6Oqu1n0sLm@Z2An^B=}hHy}L1&RUJMzeOsB&J3q|j zxc4WjzZd!kR8GJBLYZyp1!cCGm%+?y+Y(;Sshs_5-GsICdDPu{<>N7rJ3;yO3o#BV z(mg;%8GfW(g8fhDjC6jj%rWsL<$GbwINC2Q4Tw4rI!F&bn3kAnM^R<;{qP-%{R-dbwYj z@BJSGGq1ON4u4Cg@`Grm1`qwcsH0n*}MclsC*aN{G%%OK4;92RnGC{DP_jhpDQ!&y`Vgg zx_C)>7xMapGGp~$!OV-_$zZ}D%>Tq=J(#|I8a_M`OuuogdA~Bp+>Ofoe#lYgyV|pq zOB2RyQQi%{Sedc(3gwSL-co)A@~f52k}*5Lv~2_UI+b(H>MP3kP8f5CGUp86P`&~6 za-Z_6n6o?xW?2rPERU+3W78ALoVWfI%)AbN9zImLLAhQ~z8z^E%H`01MVVv4Uz8ul zeB+7JC z7kp?|t&-!GrX2^uZmZ=6{PZ&Fh3zv;5Cq-9 zs1O7#Gc_Og!>Owm=J}cL>CPF>lqtUVHPIleWzKBk)K7OEf7-8l6~9_9aJp_<^NpmI zxwDB|FV!6vl3qOMPRNbk(QX_K_-$#}wyHi=OZC$oy?^D^^77SmK29=wPeqdV-04cS z&VTc~mkpSf`7)Q!zyr2$oIK&4#v%4(tHvQleCw8nF*mz>wj1eR*y zt^1sk6vc6?)`dl~dRc0qRCmkBywi&ttQ5H1sr&GmX~&~xFN8?^U8P8jE9Bx_k+UK6 z!fUMmobqg1O~OX6=1i6*J+~h98(pyH@&r|@%6X3Tcg1FzUn;Vor>E;H@>ppDJy^90 zi*eM3wKKEM4BT*C7@#=5=?vqPij zMqwFl-7!K9&3Wt*i|7zuMSbJc%VSlh~q!~ifoDa8D{4@(th3HnWty0Gs>akWv6Sqwt0#-$hT}&0E zS|LCSws)W!acVp1b|fC4Iw8=0iu1(gz>aF~khbdKL^}a=8Z2N9GTV0{Nr#OjbRg1> z3sGN{w8KhfqUKV~c2}siR89M7o|XrtqKiV9cN$31QPI;y29Kvu^c{%_5ZNeHp49$p zOBfE|B<;iu!T66@x;8*v;uo!KrtTuxEMGI5&%JotxtGi#j?SQcl*%rK@OG5c%B8$v zmRzsyqAsdzD9s4DShj-g9P`_aEKaUP&vq(mcirq{k7fg^nsy`|vh1MI?-<1l0pjH( z>;v;bqnE2O7>gQP3Vc8{MmTaE*};UZj%g&_R&6N9#oDlpfa>{Hn5Zx*d!|*x*^W_! zCJE>dYYDXQu$4BtAyS#iXDqd?kPpKoNtvbv-eC7H=|w5-rv^4N(Il`{MgT|2C72U2 z{iaRd{>w;NE}3=@r)9(_e?}_>r)7jJmlM0N6G)mXvhSfPkI0;P*YJ;~hNS^?m{CeK zxgVGCLz~p(BML;jhfreDj?uzMNyI$Qgo8HIA20BDb_St{#SOAJ&?3(m+zQ8|dJ{>z zaGxegxj0SS3QL!5#%CvuG)PBH=%m)v!aOKi`!FA;-_I%Mv7_SE;Y7?+RX(jv6mr-) zOk(Uf(yyIEyRlV7w|0(Wx=Az))BkZct2ehWkf0r}O=h#T$t;?|+GKWZGF#UP(%NK} zbKkYe?8?dP>ZuPWw*SS{2lKR}O?_lM3h+>aVb0E8(hw8NcpzhE&Kz>BCLbQ7L6=hk zI}zlBNABfR!0KQwxELlG)rwRL6%6(*%*!z&647$XscfXi#UP~JmWh-O?$BKx=5vEF zV~t0uS0tX>OIwQMbK!DU(SS<^n2U3zf(5_~t*{;w9x=T?)Jd&q$W!^UQsIW9DMbT3 z8sSkdH$ZY4^R6vQF}Z>rR?_I(|Y(bYuFj2q^%Fd&(gapnPT-x!@L5r_VmM2`O~J;%mETE?qXSoGR5UNq9lrCAWWIKhKPh^ zL5tHjo6@*yI}TP&J^8jtW}&<#XWBA;V}MC;G{C$lkbNa%T(nyc3vqK{0q!6Su-gR3 zSPkRsj2DpTWTpAgs$Jpr<7CsO0PDqK)c<{oWfnJ*SIqHa1$ zvDc0bjDb0<37HzDK^|hBC=;DF3G-_ zJj&jexDaL$SJs&=r};Sa_+|`8R6;vz;GseN)yW-e_ryeTbc09cX#dZ0+@(7?#wOpc zk$LpcHgPSATN$U;!3ggk+L0%SMe8w?3#>)FEpKOW!vw556Ig?JhR2n*9DydVv-(v} zG5Lj6PcBRe)-w54FKi4(LrMk>WFb|{3#ryAm^Bo%NB#b_c%#zlAz9gY8gCE9{cCAwv|7dIO{7CK2O+B|o`#R1Z1bgk;UG`Jc7;_$Nhb^0gXB zA`eq%FJKa|C4bFe`q2RW+2l+7G$?IHc*Kv;l!}01f+N=I0K zMh{7l(yAbv+rKjO+EnHn8D4b2r_yb6%_E+sO}~ZbeirCnw71zevI`lwAUE^vmU20@Hvwz whatis + ${.CURDIR}/whatis.sh > ${TARG} -whatis.1: whatis.man +${MANP}: ${.CURDIR}/whatis.man sed -e 's,%libdir%,${libdir},' -e 's,%bindir%,${bindir},' \ -e 's,%pager%,${pager},' -e 's,%troff%,${troff},' \ -e 's,%manpath_config_file%,${manpath_config_file},' \ - whatis.man > whatis.1 + ${.CURDIR}/whatis.man > ${MANP} + +install: ${TARG} maninstall + install -c -o bin -g bin -m 555 ${TARG} ${DESTDIR}/usr/bin -install: whatis whatis.1 - install -c -o bin -g bin -m 555 whatis /usr/bin - install -c -o bin -g bin -m 444 whatis.1 /usr/share/man/man1 +maninstall: ${MANP} + install -c -o bin -g bin -m 444 ${MANP} ${DESTDIR}/usr/share/man/man1 + +.include "../Makefile.inc" diff --git a/gnu/usr.bin/man/whatis/whatis b/gnu/usr.bin/man/whatis/whatis deleted file mode 100644 index e8cae0a12a..0000000000 --- a/gnu/usr.bin/man/whatis/whatis +++ /dev/null @@ -1,66 +0,0 @@ -#!/bin/sh -# -# whatis -- search the whatis database for keywords. Like apropos, -# but match only commands (as whole words). -# -# Copyright (c) 1990, 1991, John W. Eaton. -# -# You may distribute under the terms of the GNU General Public -# License as specified in the README file that comes with the man -# distribution. -# -# John W. Eaton -# jwe@che.utexas.edu -# Department of Chemical Engineering -# The University of Texas at Austin -# Austin, Texas 78712 - -PATH=/usr/local/bin:/bin:/usr/ucb:/usr/bin - -libdir=/etc - -if [ $# = 0 ] -then - echo "usage: `basename $0` name ..." - exit 1 -fi - -manpath=`/usr/bin/manpath -q | tr : '\040'` - -if [ "$manpath" = "" ] -then - echo "whatis: manpath is null" - exit 1 -fi - -if [ "$PAGER" = "" ] -then - PAGER="/usr/gnu/bin/less -sC" -fi - -while [ $1 ] -do - found=0 - for d in $manpath /usr/lib - do - if [ -f $d/whatis ] - then - grep -iw "^$1" $d/whatis - status=$? - if [ "$status" = "0" ] - then - found=1 - export found; - fi - fi - done - - if [ "$found" = "0" ] - then - echo "$1: nothing appropriate" - fi - - shift -done | $PAGER - -exit diff --git a/gnu/usr.bin/man/whatis/whatis.1 b/gnu/usr.bin/man/whatis/whatis.1 deleted file mode 100644 index 9e5528d62b..0000000000 --- a/gnu/usr.bin/man/whatis/whatis.1 +++ /dev/null @@ -1,27 +0,0 @@ -.\" Man page for whatis -.\" -.\" Copyright (c) 1990, 1991, John W. Eaton. -.\" -.\" You may distribute under the terms of the GNU General Public -.\" License as specified in the README file that comes with the man 1.0 -.\" distribution. -.\" -.\" John W. Eaton -.\" jwe@che.utexas.edu -.\" Department of Chemical Engineering -.\" The University of Texas at Austin -.\" Austin, Texas 78712 -.\" -.TH whatis 1 "Jan 5, 1991" -.LO 1 -.SH NAME -whatis \- search the whatis database for complete words. -.SH SYNOPSIS -.BI whatis -keyword ... -.SH DESCRIPTION -whatis searches a set of database files containing short descriptions -of system commands for keywords and displays the result on the -standard output. Only complete word matches are displayed. -.SH "SEE ALSO" -apropos(1), man(1). diff --git a/gnu/usr.bin/patch/common.h b/gnu/usr.bin/patch/common.h index d1906fdaad..9bae5bd1c4 100644 --- a/gnu/usr.bin/patch/common.h +++ b/gnu/usr.bin/patch/common.h @@ -1,6 +1,9 @@ -/* $Header: common.h,v 2.0.1.2 88/06/22 20:44:53 lwall Locked $ +/* $Header: /a/cvs/386BSD/src/gnu/patch/common.h,v 1.1.1.1 1993/06/19 14:21:52 paul Exp $ + * + * $Log: common.h,v $ + * Revision 1.1.1.1 1993/06/19 14:21:52 paul + * b-maked patch-2.10 * - * $Log: common.h,v $ * Revision 2.0.1.2 88/06/22 20:44:53 lwall * patch12: sprintf was declared wrong * @@ -58,10 +61,10 @@ #define TRUE (1) #define FALSE (0) -#define MAXHUNKSIZE 100000 /* is this enough lines? */ +#define MAXHUNKSIZE 200000 /* is this enough lines? */ #define INITHUNKMAX 125 /* initial dynamic allocation size */ -#define MAXLINELEN 1024 -#define BUFFERSIZE 1024 +#define MAXLINELEN 4096 +#define BUFFERSIZE 4096 #define SCCSPREFIX "s." #define GET "get %s" diff --git a/gnu/usr.bin/pr/system.h b/gnu/usr.bin/pr/system.h index 2e03ea8af1..d26022dde7 100644 --- a/gnu/usr.bin/pr/system.h +++ b/gnu/usr.bin/pr/system.h @@ -80,8 +80,10 @@ off_t lseek (); #endif #else #include +#ifndef __386BSD__ char *memchr (); #endif +#endif #include #ifdef STDC_HEADERS diff --git a/gnu/usr.bin/rcs/Makefile b/gnu/usr.bin/rcs/Makefile index 21818151d9..4a9fd0838a 100644 --- a/gnu/usr.bin/rcs/Makefile +++ b/gnu/usr.bin/rcs/Makefile @@ -1,3 +1,3 @@ -SUBDIR= lib ci co ident merge rcs rcsdiff rcsmerge rlog rcsfreeze +SUBDIR= lib ci co ident merge rcs rcsclean rcsdiff rcsmerge rlog rcsfreeze .include diff --git a/gnu/usr.bin/rcs/Makefile.inc b/gnu/usr.bin/rcs/Makefile.inc index b9eca7d521..cd593eda1c 100644 --- a/gnu/usr.bin/rcs/Makefile.inc +++ b/gnu/usr.bin/rcs/Makefile.inc @@ -1,3 +1,7 @@ -# @(#)Makefile.inc 5.1 (Berkeley) 5/11/90 +# Location of librcs -BINDIR?= /usr/bin +.if exists(${.CURDIR}/../lib/obj) +LIBRCS= ${.CURDIR}/../lib/obj/librcs.a +.else +LIBRCS= ${.CURDIR}/../lib/librcs.a +.endif diff --git a/gnu/usr.bin/rcs/ci/Makefile b/gnu/usr.bin/rcs/ci/Makefile index 9b64e0848a..3ac3d29abe 100644 --- a/gnu/usr.bin/rcs/ci/Makefile +++ b/gnu/usr.bin/rcs/ci/Makefile @@ -1,7 +1,8 @@ -PROG= ci - +PROG= ci SRCS= ci.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} +.include "../../Makefile.inc" .include diff --git a/gnu/usr.bin/rcs/co/Makefile b/gnu/usr.bin/rcs/co/Makefile index e9de8da3c0..a759d1fa4a 100644 --- a/gnu/usr.bin/rcs/co/Makefile +++ b/gnu/usr.bin/rcs/co/Makefile @@ -1,7 +1,8 @@ -PROG= co - +PROG= co SRCS= co.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} +.include "../../Makefile.inc" .include diff --git a/gnu/usr.bin/rcs/ident/Makefile b/gnu/usr.bin/rcs/ident/Makefile index 1a618e529b..25e028bf64 100644 --- a/gnu/usr.bin/rcs/ident/Makefile +++ b/gnu/usr.bin/rcs/ident/Makefile @@ -1,7 +1,8 @@ -PROG= ident - +PROG= ident SRCS= ident.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} +.include "../../Makefile.inc" .include diff --git a/gnu/usr.bin/rcs/lib/Makefile b/gnu/usr.bin/rcs/lib/Makefile index b198e9ec6f..8428686b10 100644 --- a/gnu/usr.bin/rcs/lib/Makefile +++ b/gnu/usr.bin/rcs/lib/Makefile @@ -1,5 +1,14 @@ -LIB= rcs -SRCS= maketime.c partime.c rcsedit.c rcsfcmp.c rcsfnms.c rcsgen.c rcskeep.c \ - rcskeys.c rcslex.c rcsmap.c rcsrev.c rcssyn.c rcsutil.c merger.c +# Define FSYNC_ALL to get slower but safer writes in case of crashes in +# the middle of CVS/RCS changes +CFLAGS += -DFSYNC_ALL + +LIB = rcs +SRCS = maketime.c partime.c rcsedit.c rcsfcmp.c rcsfnms.c rcsgen.c \ + rcskeep.c rcskeys.c rcslex.c rcsmap.c rcsrev.c rcssyn.c rcsutil.c \ + merger.c + +NOPROFILE=noprofile + +install: .include diff --git a/gnu/usr.bin/rcs/lib/rcslex.c b/gnu/usr.bin/rcs/lib/rcslex.c index 51e31f3445..cedbc402f2 100644 --- a/gnu/usr.bin/rcs/lib/rcslex.c +++ b/gnu/usr.bin/rcs/lib/rcslex.c @@ -39,6 +39,9 @@ Report problems and direct all questions to: /* $Log: rcslex.c,v $ + * Revision 1.1.1.1 1993/06/18 04:22:12 jkh + * Updated GNU utilities + * * Revision 5.11 1991/11/03 03:30:44 eggert * Fix porting bug to ancient hosts lacking vfprintf. * @@ -132,7 +135,7 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(lexId, "$Id: rcslex.c,v 5.11 1991/11/03 03:30:44 eggert Exp $") +libId(lexId, "$Id: rcslex.c,v 1.1.1.1 1993/06/18 04:22:12 jkh Exp $") static struct hshentry *nexthsh; /*pointer to next hash entry, set by lookup*/ @@ -935,7 +938,13 @@ void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); } void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); } void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); } +#ifndef FSYNC_ALL void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); } +#else +void Ofclose(f) FILE *f; { if (f && (fflush(f)!=0 || + fsync(fileno(f))!=0 || + fclose(f)!=0)) Oerror(); } +#endif void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; } void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; } diff --git a/gnu/usr.bin/rcs/merge/Makefile b/gnu/usr.bin/rcs/merge/Makefile index d14afb2885..9022bc4bf6 100644 --- a/gnu/usr.bin/rcs/merge/Makefile +++ b/gnu/usr.bin/rcs/merge/Makefile @@ -1,7 +1,8 @@ -PROG= merge - +PROG= merge SRCS= merge.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} +.include "../../Makefile.inc" .include diff --git a/gnu/usr.bin/rcs/rcs/Makefile b/gnu/usr.bin/rcs/rcs/Makefile index d62c8d1d94..e3cd12ec58 100644 --- a/gnu/usr.bin/rcs/rcs/Makefile +++ b/gnu/usr.bin/rcs/rcs/Makefile @@ -1,10 +1,11 @@ -PROG= rcs - +PROG= rcs SRCS= rcs.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} -MAN1= rcs.0 rcsintro.0 -MAN5= rcsfile.0 +MAN1= rcs.1 rcsintro.1 +MAN5= rcsfile.5 +.include "../../Makefile.inc" .include diff --git a/gnu/usr.bin/rcs/rcsclean/Makefile b/gnu/usr.bin/rcs/rcsclean/Makefile index fc0c626552..2651a3f4b6 100644 --- a/gnu/usr.bin/rcs/rcsclean/Makefile +++ b/gnu/usr.bin/rcs/rcsclean/Makefile @@ -1,7 +1,8 @@ -PROG= rcsclean - +PROG= rcsclean SRCS= rcsclean.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} +.include "../../Makefile.inc" .include diff --git a/gnu/usr.bin/rcs/rcsclean/rcsclean b/gnu/usr.bin/rcs/rcsclean/rcsclean deleted file mode 100644 index 8aee2dc5136445cc902cf331c643aca1ecac853c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 101972 zcmeFadwf*YwFiEZnS@E0FaZ(>?@>WO9!Y=zNeBkw0D{4RA&IRtK5l=% zd;j=-44gUV?8n+`uf6u#Yp=cbKKZz%8$1-_xcHx&4W0^d;J8wz|wf&YI|piOEk(cbd~%k9A>cI`zyK5)q1(x#rv zA6Rb-9O}{1$HF?We3>zw-3Kdym)&+&l7jx8>yNZc$cJAkdM{d6x2e8*m8`h4b7xPR zjGx)`lk2`^le?^9jpDC&FPQ3HQC%r-O7^PtD^n-na&Qqka_DojY4v>GqbZYvu3%oXXQdPA!)vug{dGO7;Gg z{;KM=)k^icweEFQZse?Ppy#z}?JfR#NnN{o?Ya$!s-qNn?r{y{8l-xO>f@fGj%z?b zvQ%H|o>Il1i2i}jm1U(qz1X1@@6{WftR+v9LY{{W{DnMwh+{(ur|aURwP?eTXD5Df zdyv9|o@c#fhPsz6oh?b42f3dO9CjMTdjpMorL7|=TCPWWcF5iv?+7nIP#|R2$Lz2b z&6(nAnThuuHv40)18qft?n_;FrXj!>+p;oVlFDq7^qv2YuP(r~U=EFe?Z)#JG4D%b zo~!U2Xna`OoLsY->Fe=w*zRK=6i~e$#xsw*DJ*fK?Ihwvl0ymGzY`~+E>cszhp}+B_32h}%X)SH8yNgiopr_3%|AN5e zFo0#pz9UJyPhJy;G)|;x9)mROrMZ%n{Tj#hawCUg<)ip9)IIF35(3AQo&gj+ogyN| zfr7hGRY|!j(Dq%D3YLiTJ_YBr?AxSAsDAM(G$qTuM zR5Uy#KoTI#eg+=l4ALs8&OqZal(!Etku0D`HNwqMpJ3l520NVUr3Ml-XJh;GAmH-d z?Z}azL+rF=l#!$0j>;NY@*Wf-w=PCQwktd_s==Cg9UzI76@dcEBdHmpx#ee~>3_h} zJ7sEJ_I{m-<4#F&o1C#2R$br?CG` z8mtTrpn1B=*i!z`X{ZjiYA1y;^qoK>1F`la3x%IUb%J${Qg&niIH^$coCbxP=1x_I zH*JZ7(6|@L@HACTZrT!$_h!78vX3{KZKH0`i%;t%XM$dU1DtagUnus=r(JbU%M8-fO5`Y#&e5g+zM!8NH-SFYeJs?l3$( zrFwB^(B9w6Mm~+sUVJ*}9HteYLAp@FghHYdRf4g{P#exGu9hrFz(4_yR9zG|?LW*o z#X_u17e%Gm2E#A_{QA#c%ZNq!M2KuIS%j0be%7DKI$tfuZAvEw)1Xt1+i@F=G~ zB9JesAGDlR?Jb6qQX`c?NPKGmWpkQ9!AJxYq8LDta~&RP;>u89f?i}Naq4B`d5#Q# zuxGbLT=Gfk8+or{>^k zI%0?=3}syIC+a0~E8%yAEJ@3a9RBtSpo$Qc<_$_nY8jAn}~Wh)V75t8dk4sZh~0#^K3`=sTF zl6&tXv`nSOOO-e%G{I}G8LIe*CoL@@;OI2cVo9=O#2qY&% zt*w;kmW=z-Ygc&k+$-ej1~>lW&8^6O_nLL9+_nA&Qob)q2A?-n)>m_-SW>2tMl9&u z>7*{MI`tG9T(shDP@V{`%ndX;Bz3lta~$FFZ@`K2CJYbD*tbCnGj+<#h(p7)=U^~M zpcncJ-;I0ks|65D#eR&0=oB@D0$>vW8frJ|2aqH*&Z?8;(`Z?FJ0j#g^wWwT_A~(Q z?nH!^-i2Qz4Znqg0tFO?vE)^}G|io&4qQ;mh9)4{Arun+8A9d#$Y07X{V(dK)PGK> ziKQ%n)~<=;MX`4wk6WowJxlw@w#cp(_aGgyOMf5x{+%3O!7P$t(>!NLb~g_6u_AP3 zlp;7rb?2)+^@D?+9~9C-vS2`2;fL|c9mJE_VJ`tJ!^R> zd$7ka`c%-3`@iS7D|#e(HEP zB%~km5|b;MeC2`3=FW9XLXy_|>-`lgAwgGG*ZV6ecZ0IBdMy{L;~MfY*YmrljBA*K zJQC#+^D8?aMx5+2R1)1i(AXm>8HSqblYa|wBl?OB-2@FA8QCg2MNuE6;?r5~O@OIB@5BG+>8gE&-qj0-ts-nkvx z@j`UXG?s(mL@!R$v#Yf3?e^(KpcQ-WJTMEz6z#fKXm#rMeWlF!)G)Yo61tl;o>o=X z)|mrp?$;bo#c&Ee_Pu6zz%dztdK{q{r*%Mv zj-tp+fheM?&dnoS`=J;T3VQJ#6T%)gw->@53xbEwmt5H5=HEDOjN zq5Lq^r0{+C2|~S$q2{z+z@Mk)Un)&Pd>Lw8w?`{?QPdu^fP4{ZNx%x!RL6mKihFCn zz-P(Iut-ASvpi)GH?Pyg!Y1o-pV+ z<7FQ$69U)Xa>8}rp>v0*{anpIKpM0Z;20=USp=RbW$%+v=s?SpA>|D&A@)KL1U@s+ z2RR!)iV~tP59=lycmXWCE&Mnj17}zBF8*+__TLsI(dUW0`4%4e5A2+Ie}o`n*OpuH5X%#zdkgzKy027x4RlX7MJs$!bhleTz6jla5Gl_m zx?0}B1BuFIX?AJ%_fr>!@m1|-Fb#fDr zJ#wK;8bW{`xDe14#Nkek*1iZ0$AgIdQ^IW+0$}LKFMgE77Pm!_IMR_{_dIA#-7VRV z0Z_3fQL}O*1LX`||DRg?{ifgyaRn`$BiIzIt6R#sYtrZi61?k%F)v4nU_ttUWZam zeV<@KryPv}5M$&}M%IQ`^Rf0OQ7yuS6p%)T&eLETIb9#cYiTG$2u2yk_$7(NaA~m7 zZS9OCBVausK{Y648jwRB1C6N?ioqP$4IKK+i!2>FohqDP;#fbx#O-#Z#Q3X^BHZ)> zDpN0UD8()q1eyHW-<5l^BQP(G6#N_t>r0!-BUj8@WRYD%BpW>q{!Q%Wjjr2pOh=aZ%kfRNc-U9u`ww*kKqO&0(6=ZM=aOKx$fY;C!JnD*O;|5EVBPo&bI( zTH(7WT6u-uQ#zOR0e9JXkw<9bjNXSimU)a)Z5UY#$u}X z;*9*oj;+IZy$6#`i|W1oxK(W@>|Q=L(E{%Z?jnX$d)V!wkns2L9`v6y4VJt=Q%|J7 zPy+F@c(QtSZ*PYc@{bj!xbU(cK&+Z#0=0;W2?;I5Y$yVq8L$JLS!n-|hXDsePbZ+F zUcI72Pbap}VE9TWMuNFdZ#)(Bbip>4$;kqnm{xK!TpmwE=*ll)^$9EvrK8l15D>bj zYmt{-0|f&nLapTV#F$b<2L+wYj`ibB9Ae*&HP*+Lew&AzW`e8%8#_>{x_(6Fz$yV}V_QPxsGaW-c#Yk7;9mG%u zlQbIK(ze+}CApe^f++U!KS3fC@M5?DuUh$W<66uf%GmpSEVcvZex#Q7<7zp2?tFoB zps`a@mT2lxkck+?Wim*wAUH`Ja%_j^sKp`>1SbvkD6@0O#Dm;GS@A7S1c1W)6L5SP z`My|za{MB2juV_`H&8~-%RV+jKmom6%@2T;0-qJSTC%D6MRkULL3J9;eE)uRa-wMP z>U{q?UL7jGnbw;?TeRt2-4L1F_7RyRIy}N%E%#8T0MD5%c8c9Kg>t82jUsp_q-@ZC zSacZ9)00W2f%8h7T1mIwn4ykvW+He=|MuPD{9If#b`ZV~Mx7T+} z$&$=brnc=X@2UjukSv?D@?#g3cc-W|j_>dsi)qzUhI))mU2nBUK^Y5{aUw*fnFaoc zM_y21dMPUw>8j4rRMgdSEeZ4U(78z*Y#jh`>1qz6LgBlqMbD!}F{Q;c0j#&xgnZ@2 zAVYLNARr4N`9G{mrApYFLh?{4z1T@=_ZLDk{3!HN&Kos zMLLTqE(iL%2<<>yQoiTR`cb<5p_AJIsykupz(BWsvvd1#2*d61;VaPF%x6>%VIA=z zXQ7upUW7K|t3i<8PKujvNa6n=;dma?O`1^&;@Hx=If-CaiXqPN27&v9J!Xc$_1_{+ z4`2VICIfMp9cl<@Mm2RcJMm<8st?4<58$#@(C&9pQH{>QShgYN)5#k5NyEJJcWD(-|HqhD5Y@Z^nbjsFyITYz6HD zgZ>x95{~SzTn$4btc=`JIb}7uxJctFxeZ+0UJG} zknQ?n7>$qV>GyE;G4py7_k*}z#063VA*0?Ao=%xnm;+B? ztUAViltxr{c*7%z{~BNZj3*4->V%E1HHh*fiByyMsa}!a%2#3LqtI!A^L8Z>nT^I& zu?EkE2wf4YSF>+Hh2(q9n#|wR{h8yiljS zn-CdDC8%4>pr4x%PjgOoMygDhO1TBeWkM3DZS`0*a6)c@^SHABs})L+H!|9co)$^N z6Wdk+v?EDYo>Vr6m*ebZ_`r!Toso~o4`*X!)0P^qb<%h`1>-`2Q;<)@$z9%F9} z5>3Z`vBo5n!|R)P+-ppe|H~wfz-2b4a0X_SnAII`+B^-*aWIQdGm=!>Qngc*nkp(# zYN*Fq7mVZ3G$9?XzE&?exnsEDIoX66&mWLf_h4xb%y2wtAL3;lJS7IMRM&xaFMDY@ zM+q4pi_NIM9rfhaC<~rM`p2^udQDsNM%81ch3<(fJXErylbuQjy!C zj?ubaJ>?DZXfL}O8iD>0CDos#WRd)9Ri{{YpNqVaO&#_Ocot>Ci1J#*MFyd4wo52P z+9AB^aUJ$}$+wJ2JdhSId731?(twpXL)GhQ|-8YYG7%zP; z1Tw91U2R>3Qhf{7Uc@>KcYXV!^xQ1O)7?C-ay(Y?HV_rL>{=mYSD=8_@g&QFzsJYw zNnCrL&^@XBX)yr)15a$}2gB)$#I%F8l4qB*YpLEH+9#9BpVdqD1n2x=1z;HJvt=v= zF}5*yWLxpX68swP=Q=JBH+?s!85bISXNtyr$WbGIU4k_Q8>a9YeMc zUQpCDS5p7cQDjJwSvwN9+ZGfxx2b0X?VmN}-=tpZI$(oGDZk6rl@4U>Cy?s~zV=bZ)%AA6<72>~O; z)$$4;2H{)eW6y*H_qMrOS`m0~Ab!HX#!qVx6p4@o^+h^JKY_+4Bt@as+-<;)i14!| zR7k5RgfFbX?`k=B6uU-v=d@k}+{UAHBZr8{?46)KEd-)lCYs2NMKoTWB1su@EJX!m zp8O#$I303;7r2)dpwI877Uk+K&7OI(L&YfNH&_VNcy-|m74+}qthn0mufy7$;;*f< z>@eJqV$sGC`zY%(RLA_0ht#ByeH-csbM2x{f0ysQ0stof4`GtIj{li!ezg^={a7!o zty@=LQNM}vwA*BA44~sGZyHxA(vJr?@FEzIuN`B($jM$_2I2+~f$1%?p>|pbk}^1w zvsiFp656AJizJ7S?S$;rr)!@CM~*?G7wzyk?O9lOU@xPh@|(cW$L{+9E$8muO)cmq z(;+d}P=Yu=%UhF)LdxX7P+e{Sg%BLF-$xY&oABj(;S?fp+)qusWMl3@N}$j9DOv=k z;dnKH$&kOsj6ZWqU-bc5@a!UDH6lXxpCX^MWYf!*36Sax+2V0v8NEs`hpVBI1pRd} z;b?a~-R>=AZ}9;Dnj=PA(%K^EQmLhE6@n3#YFdM>Ncgvy%X_uz8l}1pvcYn5ZNRcV z*M^`1(RwT5;MePJsPk7=S8Z~yTk8jIqJG)N0q)>61MbHMKmadO{$?`1ItrqDD5Ir0 zuYAj7kn0yuiUzG&_26%?b{v;|JB!LFxMF&Z%C<(b&><7^EN!F_iDn2!4oN$9Nt*R&Sa26x>|nAUxbg%Xi;0!GM<1H z2;l%)wt%Bj6(|@Eq~q2P7Fw))7L{hd1yUFd*dZDPA41c|c;UixRzVK4u7V6VLQ9FM z@Lyp*Wb2WQJp=%x{+&=R5!uSvVvr2o%aw)b9}@Xq1$nr}gRTf3i^@-u=6^U%`GrHv zk*mzL7HfS>RjSum>tkQT*>If-Uaf+^Qti6>O(=@^9TF8&|73YdHW0ZB*qG}+RCe(P zB+&i@W)t>f3^f$|ScHPxL@@6&n9kKWSO-G%^f|CnXRfZ}!lmt(Sq4rrrKw|QA99|OTI z>R^^^2FgzpguNbg;8ZQ0KpQ#q%YHNu6I^;a(!Qu-?JBiSx|&><$U9S-U%zg(f33T& zy3Q{xQ>zteMbwu`DyjqHLEm0eU5mw3|HewcemeBebZm5mJD<7hOM4x;D_h1W6;Joa|ISn@(TMv_^7lpj-6Wivwu|US6f|<^ zi9Li#3w3v{xsjrlf(t{XNJ&fKhBxDy?S!Dki8ZWPIuY&sP5pXNF`?_U|HCf3$N3G{$R ziTjcAIt$)%^SLuEA2d3J^-!G&qMtI1QdvS|O`MPY7)#t@QigTv-&{#;Sq6=ID8QkK zL@woM@R2ibn=@77Tbd{WYPl1x&D+pS#@6-OWis7s;PiVNH$tTr&^y*ax-iw=M&fhp zwsv6IBrc$&6ga*8 zurMl{GjFM0o7o^sd0aQDA{{STg78}{ZK!}8fzDJ_0ZFkE=r7%Lme7u?+Mv{ zGYb`w>6b0!@iZ=!zX^6RrlTvJe+SL$l%GL7-ae$ac=p2sC>jod$#MtfI|y`wYELN} z4V1vrRWM5@%P&#X!-%TNZCBqt=jqvsx+p2e28VoVJX$^VehnGX987kq$ z9r5$$OjZ9GobU7o=O^Jx#AWj?Uj|q47Z!P&HpNdR+acLnrOh-U5ZeTTO;_ZS9AZ6p zfVa$%9qn1&K>q&~*6u)rn3_gCMioCv_Nn8jxvhTD=m_2ve*o$rr1nVap-OTM)~2X} zhwY&R3PK?5XH$ycirPPDf68@Wa8Q|lvD}&Z0;mJ(e zHvFAZwe0X-AcSo#f#>7lyR5xQ6inHgJjc{SRHqN=pwsZ5WMMwfl0BmTpJd8~ln|y;7nnky||TUf-bB!H{9? z@hJ6GK^C%~p|jG&s-Et-$CYKSr#rlCFPCnl;3msI$p=PD=HO=w6$Q~UB~UP#<1sdr zKo5jR6?DaZt;9aGNhR3y2y%JZQDn2UL&%j-LYA3a=&peRdY9AzYz&I$y3#Fp;A3TP z+@H{yc-#UWXX)7uo2)%OvxN(C*6hseTzA&=yzJ?DIXQhd<ttDM5S!r{0$O`14sdBC49ss|He9q21&+j9{DR*H%Myy%ngZu+f;HWmWLF8rd6QRk@lGW8oMor z^7+_w%zelqS{VgVIRG|0z~KAtU`LxSdkKmgP^`Wm#UgJ!tsM28==W|Rj$&)>0Qx#f z#|w}|hl^zA!PYKhK{t2px|NVMkbp7$I+RdHG7Nw6N5oe9$v!X$ZM&Q1yCUo*w0S6j zZxy9z)UFh6ZHKo z%@(}Qjuh+}M3SS3dOBp|xbQ4KhDDZ~}gmGi{$5a?_gLLT^+~}YZ7g{C$HbhF8B+B^ z&1ONn*Mjx|K~tj8DlBMAOt-S2r#n<=r-HrgADI8bMD)i}=rRb{O+YvYG&+5y@^GsC zh)`+)zW%t9ZEP- zC`m8lI*RKxT*q+HAN50_m%YlzTFh+#f*)tlJAPuM+JC$d2w)(XTv~?C`i~`q4mcKD z2yIPs60w;}J3fWdJl`E3NrApZ8v>7UQ=T;r5L+!*K83)lj(%dtrbn&WVb@C~)M`;xIjF7n)SlJ_F7zx3~Nw%k> z$lY5OJTjf~dqlAefYla)5eN+`0#W_t4vP90-n50-r-OhvVx>+VkQz8VRz5}v*hJ80 z!${um?*BUq%g_>ob-dqAq_T;9Pm5ifBEtz-TjU)zC*_Xpv(URH@GLMA=ciLcsVTbHxHuS% zNNc)qp6&0G&tPuGHex2_V}FAoD!Qc(x_1)n*_e3aCgo$#5r4{~7tGHQLMeDEzV45A z&W~a)W9`0A+C{Qcr#bABBCsfzqVhF(#G1iOLJ+2QvGe{?p+g#*hZ#9h!D%MlD3X>6 z`FRE2g!vN^3FFUD3^yw{NH4`)kM@MoPz`fICVz{^%;b55+$4+QaF~FR?M5CsjAw%| zOhvZ$xzsbW86b?#jno__JS(_e+{$>0qOUhIo(>d{)WSYuF0te)4*NMIL0)+^e|wz2 zVKJLaF=ZB)M)F!7_XEUXHk(6HMh;0Xn$TSW{T?eTX8v_nelE9^%W4XD(UUQwg?cY` z@sWg)>p8q4o;IV@$RWvM2itTD~*e9?hW0{xz>{==k zroKk(%u9f*vt$6@-N@MjJOL)iH*d*8Rq+cx5o}}0A3j@dIqPa}L7{;ysS!Xm--Q6OY9siS_ZNsrl#tbQ8&*n_A&3gmH$j0?qhSI& zj%WS)k&*tTken~^7cdyDZv4$uAyueeSni>{cb;n(kaDxKx~huiHL!gV9pv>3(8;dH zfOMq29>r1^m77*0$-5EdV{en*fJI3s2L5D}529lxg*B;>hKd&ClWnpjZYAM|iTa(S zdtp+=*Gyt`7^u$JV=O5%TMJQ)_7NI*lLM`qzs0dNrlFJ3%5B9~E5k!=4B$b&!2X~$ z!Y?un(~1_u)x3>>e4;mYH(gn%!V|Fj^6;V{RIm^GJFEEV)K7j=$5o^TrTB__8<{<@My&h-rpqu8b?a5mV-p;w{W(Bxht=eJ<9RoduTeX zU3nNA8;8@81Z5EYMBn00u_e#d5(H-i3~D8~06VbnG=GU>Yl=R^YNbRlFk0zFKJ-)H zPJ*wwfTA^?7JkDbQ#y8GqJakJLw0yhixx_ZIKhOc*{ zml7I>?b=&m*hpYYay7q+d-w(X#8&Pl4(Rih8%LG9RRE1vPJy%a87xgb1|aNLq&OYOc$oE^Cjfr^dOjSR+a_{jkE^R?Hfn9)xQWIyhO1=~o~hC>q0x4Q!My6+@z{|Rc;TYWX<*SK z?K~adU1ZLa$ZE6(mLfApG|vZ==M)+mv$;xm<;50Y8JIX64q)BdRnjdLm8&<FH0rzd4PH4{5WlB=D`^XRt9fchGY5zagB#B}A?EfE$ z{r^|+aMQ8BKbLBo*yGT+_~S-+hpP=HjK+(>0Dr$BU{KPLz&q8KtI%mQU@1syVvW;r6xtQ_qusS zX6g5FKdvDC2JPPi$bWK>1A;3Ok7_(J_h>KRUt_`)1NeE2Q%Qm6xYw8(ExiPZ1l*u~ zcy4=d?m9&5;MAW}p(M^(sazf@f$M$#w;c@wP1I!%5pdQdh`~4Jkz4*^N2EZcsZYrN zwzD_kYDzc&2`SaL9oicL7hcq@QVty;?m>W;|=~M_%e#aCRx78C|~vpDcxb zACDRKYihs!=F98km6PCl$`)A%d!Xvz{2ZxV&@?wo`83;z#4U6ku+K8BrJpqAXQ_!r z_(j}nk+}JY8*atreySvGXyfuMav5#YdlVWRDK3~y24~EIrx1!#;uetEaYs?)&ZxE( ztz$8K$5SH%=%_W=m3PF=quXNj@3Ezxjl?ZLsiUn@n{t8CJpA@zlx)T2e!SlF;TfF! zv6@`E%*WDz6I4thok=D)8Pd6R%q{U}VPvBhHyPC2xA?K_;CD+)*F{}>$X7#Xwh+Q_ zF2KQcz5gJEdAs5KC1?Upm~O#OXl=W86eSbcy?7f2HPMc9S%h62LQo1e4fk^7L=r@4 zFGMpOQPQD@#wNZzHt`9?MQ*@8b^~^0VRVb;I{_Q#^q7=1SQO!%!6gwCI0=Kl?#UyT@fwW9%_~pBFFOwd2RC>8 z48oF{T-Zv*dJU}F#XVwy3)a}Bs9`ekh$n^<<^PwQIDt5EG^~w+6Ng!xI8`N1#BUL) zk{UU`h(#8w&(X3QscWU|31F@#0=a>aMDmzSENq%RRsBcd67^h1p@ArqlRzsFihLMK z--fuu(L0Om@Y;-r1&>=^ET2+0{PUDeCb`Y!4&oy*&Tf#b2DwPo@))0Zu$^E~tcmw= zmZu1li79FhFK+JVk_eus6!_!q%=bVwIzpb~6cqBDq?|X>z%y2dz+S0EkE_^lO04~mTXD1;r9i*?Nw59A-q!kT$10N3u(PUk9vVlAt zO5NrxGG8bN$5x$GHYG&JB3#YjlZDHT2DE7!OSi(6G=XnnEWSUWwiX56Y!#?vA)ip- zc}?wFcf^HhIIq`>+-T_ZvIH7kaO6!TY9Ijr6;a`Z(aNS;@YL}lK13s(kJsQjw6&=14ih z|BV$!FNVfM-B)ZufyYckHB9)H;emGMlO!{by}5X= zlX04>*#~G}Nyu}|h7vGH>$i>!mK@hgjs{EITFJ{WKz_^Y9eT;p(0mfxSff3*$O|T1 z&f3wqUO8L>>gAxt^{qHkOr7w#3?MR3elJg+REp$@NRD8=A6zTJ&L4D}ExmO=Z8_&`n_FPxYY zU}qxf(2hk{!RbRdr?0m&`(KFDXL3%Dtl%Ub4eJzW#f}`ZnA}MrGdYt-AcsKi>Bf3a z?nk0&E+I=-FMlrpf6o;B{puKQ+PPHUg3UO`f{U?4jBSvqxi7i`%_JnE^u^g`V&ewz z7P=G0D$Cb(1c}L+R&gpngfB_>u)asVq}MJ)b5rh#w-Ru0)C7DhXf2SR;=2Yjy@pti52ERtt{kftG{Lx~&LVkU5l^(nqK>Qir-%hV|p-OH@T-bDFmhv|`ISlQ2hH$5Gkc6~jN5AcfI1U&o7cL-|f;X@2 zSb*)1kAkY2+65z7v2)Te?32S;mh}V8%pZ6->QIRp0D};I}1QBlSY+X#<2-`u2)3&K(1>1kHQCD&1H?oF)ICi4n!pP$k{d0t>_fUWde(D#1 zeX9CBL0#@^Y$76Cg zUxy$L8rJ<%o|0#sN5MpX_z|+Xz2OrPj%7sP;J3;lU6BG-SOIEL)P?+0^v3<@C{iR_ zV3&zRk&IGSCXkAxMhkhv9>x1Ga%G>un4D!2{z{OC>Q8h`5&NNF~c#+avK^lZg*|{MxCAyl| zAu!O`B`I)fbOM9!j8ogSuJ-#U5yAb~^X^iZ!YqsWG0(cisft z+rd&K6hs)eftWVEU+~qd3+#d!K2~mFR^)VJF3Nrb4vWBV1p*t3?~JB7=J3b;=+sKt zy+N+V1izuKqS8-`GZ!Gk=S1AEi!W7i&2}UWT&e6^qpAC)>ce4UqAkUA4rL@M`$X?) z%P5VX8FuFbcreC3*>bZaJzN22*sr+eBmV28`zkmWiznEz;Wy&lJc2 z>aQe0P4)Dkp8PP*$Yi9jb=aUNb>kpZIR({WW6;p>4vQ?|frVU$%ERhZzIiT)G2PpebQaw_f*) z8geT9Ju#%{{a`>#WWyM5`e^V(kM$-h`6QYM~)6&m670;`B3vy z3`>YXvNG3!ex`aeEJoEyY1$f}2?g~xU=Em(Xw34;PvFJK;Q$suOa~56rh2f~01RS@ zukUnN&v0$ohiBb$I_S5SHK<>cEs~5XjK(Ut>N`rZA^5i;Wl<6)|GR3zs3j>=PKFX)q;4V?U-RV&Xic#t^C@12wS-r6lY} z`$s*|L|&Trl{>SKzz(a7Xv&+axLgO~O`Bo@iXA#{KAC9WgM%+O(dE8m+J;U4nqIwR zQ-i6OO#5oRWS;QtyNcZO7QuPXUoTy~QXG{~kJ~ERl1#dJ)Q%(h)~WTCe#yr@`-p+@ z_n**Teu3-PxSqt-it8Y*c3dyvI)>{6F0(xv9S8a&S7d^o_9Q;;^vH1 z>~r$efp7(P?HD!>Cg$1g%DYR|KhP|~B6U;;TjSqoRyF#?5^==d^t|a=pZnrSiTUM^ zI=LQOB5AW5wyoZbu8yyVkSik0tq{j_GO}eeBk2vCcddY{LVy0P3UzoWf!?V7gcFf` zH%1UP7cJYz#^}gyYs$q=Nchvb4h#(1<6$k9)Q=7t$eI1xxpSBp&;jf-TCM{$+Dxeo zp=qUqbv;2Gh&i=#Nw8sZW;mIio*HCGTKaJm*z#HgXfyqX`v16~nn9 z9}3>)Rg$z2ww1lOKZmQRT3qy=RRg}nou1*C&+pbV4jPd!ML0W`ist1tH-H)j%+IFb%yKK#BQ1owg_j~6D~N#t?yiR&2RS^!5y;jE_EYkg*+<~r zN~}zWa!cWAu-%u}eK5x`X6Dh`ENcX||8Cv0Cz(wm>Mr(vVvDoIZFJNL&XrD})BiXX z;`>nHhCWnK(k&`nC8%K5chL)0eYrf`ydUi)MO3q4XL1xSTDk9nK43>)xdpm|+K`?2Z#>ajNR8U`N6Xfo=8q!8XYl_7hD>He_!z$M69p4Kn3g?#sj_DYz09f`K z0Gks9Cd#xb;?hU)??gOI7R}>)_W{=&2%bpeRFQ@T6xQ_lG(r!yfTe>TCJhRIZBZqK z4qw-fYafTpG1X=5u)hjE`JB!yvSQx@v@H`%_%N!2Bh)rMf^B@BZkVaCW5vd8W_k`W z9u_!)#b@BA#7$CX>IXrefmD^I|R@9@OB}xHBcM{ zvZRRO0|Lt~0*HK}ekFj!R0;kV3J%{cRFWgV5V%BknLwOh07Mo*kjd5ZJIr-1#O5T1 zxzCY~e;LFC{9jl{6~i8;9rSqvIzJZJxlF)wIF)VcGJ(*A@Hbx!z6tueF97{SKVGES zfProsDhE0^ZTRo-P-GKDu}`orfTKw#!F!+c zdt?Afi`Y3%KzyS^oQf+XbMyq@kH%iS&c%s59QN-8|MP=?(>PEXK5+6|Ljs2sWzUpI zvuLUCCUgLk^mInq#TcnWWd8Ti2wp}&O*A^~CK`pVW(nmA>h+QN@F{>kd62#aCUi;K zttOPH7dfbyu@ zKIiWC`1rb`sSxkaSrO2q3j}y-2zDOlDU+dVK0nbZ}yaU0W`an7vnSW0tjt+vx zsbP%SIb5?LDk1!VC;%rb@RK`TsHE~Qt(Z*l0Q!lEPRSRe^J@X*-$O^egfKACITmlB zQ>a`@RZHP&j381M3`vtb_`=W`^e`yxkt?thF~7K5r7s)67Uf{ipkHgyD(OQl=+xab zx^##4K$u#6uzD@bh&ULbcM1awKPHk`4_>QOS5@;l4feNP@a|r@&d>Lr!Xu(y5odeB za}iS@K5o;*r`WLrJ6?b9dMYluW@LxL-JPJWK0u?}yd48wPaV?jcR(^JAJfXshqQ9T zXyxYJ=GT`9rcd3<%}t*sDc3iBnyi#=KZ=|bHi*JfD9nN6_Svti?_*I1 zCYm;{{9Xgb@LAxBFHb9{C`ji*I_-t%3>|z)>wFG29n5;99dm5#u@mRbRA7B?l?u;L zzT^j9;5=MtnrRxHbUe}t)fOyogZ1>`N6dy)63f`%(U4Vok)vvy)Kyg=H7R4t50#jls8)fl=3J`k$BItjDE4HNfklzek+s0YNG?IPCGTwR_4d+5H z6Tb6qoG*cmkOJodFDBO5J-{BghxXvn8{Q@P2%%Jjf@pFZb;OVPUXI8w8qBrFZSHPnXc+wp|g-m`!lDgfDs#B-8UTOgF#+)^AgVcWN~5-n+X6P z`9yqSO#Fo2c%ln77yK3>V$)yD9xgNPsV|E2F%nSd|50C37v4<(D4W(Jh%=t#nmW0a zBry8!g=KRW-}-srCl=xZ0)o&ub?XjvI&;0Q2?H*y?Yrm>b0JA|FRQv(re!=6T;}0h z#&`8XlOvv!PvVYAK=5{L1KAqsGhVE4E9m|b&NhN`a)W4)Jhw>Cnt>$GB=1KCk-iSi zXK;%O$BW&3ku;rKcr#L&sJD?r#|biUtl75wtO7L1ZVIOr-a43n*(V(9FU$jugG$hw zXAbhcnf-*B*peq?R;z(h~~qF$tk!qeo!@^0gP*)mGV3s>_U|Vp6~}o z`9{tYpssna5K&A1*+y&>6`+QoR>2bIasc-+?m2bXjU}ty$k*G}jM1w=K@e;VN=7nT zA>$;5UpF185-oQOA_S2~Z;Fp)(E%k^i%^R%xqyOna)?bVy_-kqs{tw4vvk}V6qDib z`N?ooaM6~fowlJD#m0C&2VRBW^Y(Ir3z`v6t`5dvO=n8 zsFm{b-Eg;8q&zf8rR2T>zmq0OnVGm(N|kkWQsu^tSjR>>B&@7miK|Mg#AAcxN2pt> zqQf$rKvYu>XvHPNVT<6ZG%)N;h-U@ZjCg~OPo{!H8}Y|^_h!u0 zCG~2#a5E}&DFpIv3W4N>WnsF!kV2h(Lr2L=DD*78d&tuwAwDfjxx5J9MGw>~$5pPd z4s87D10G{jV;0JBqrQT(@a)eu10o8!o=tiNh7vlLKIYjvXCpv;Qk^`qbXXdH^XoBxMLS4tPJ!1|~ z$-4a~0FdfsZ}^}Z*fBcr-nL}Z`<6oz^3XY4(6YEU`|Nf#UVD{RB0eT%Cc1AK)xnw1 z#ZEX*kK~FMPV#_Jf8x=3_!{>q#f2&kgm-&b*pM87IX_HLPR6)BQG077vj=B`U*M%~%sA&` zi;##bz{=nEKMDw~6sfQ1vTK74rOVFJ?uEKmWM{|z+AEnj{ivR*Grz&UehMu<(#Z*I z2?z(Sia0|>cBpyqk9@V#jwBnp;iAcc&=Sbin0t6}dIALh4($5z1VT+(j&nPKPb33- zRbM@YVsj5|IK#KQvXy3LqBQbiNDE*!L4iLa`@=(G*d*G4{#YFIIAMJtwZ@pnCLyDj z6;Sr}NdM*RAPV4wAoG*ex||n9+cn^9p+bn+>yb|HZ!^!T)VgdI=25;Ci|%j-AAsZB zI=|1WjxylnP{#g@S1fAA;k?d4092+Ox8VS_%dRA)GP*VuL&s9~OXQ$(e~NL9_G_M- zOKB}yk)_~;9jS%Ty_1?g%~a25&qumw3x-KYA3witqIp;iNKZ5>acHmZh{u^K_Bokq zA{}}YvCWTsXpK|C7C=KoCd#IX4Kh3*bPl95V$$hD=~zENbJB-geuYYqah|?GJ0`#J z%m(bnhe@}*d*XQ?yBT&wKJ=)CFc zu)ZWV-DYIM5fBUYFhlnQokQWTumh8)9=LwpPMt%14X7T~qIzM#lP}xGRWh{iO=qmJ zj$}!(GyAn&52Htu3(26CBiQOA4l%YqWd6rxaw^zdevo=SOxl0iP3CEQ^_+%IN=?dD zyp{TOOfc-Q^o$f|JZc5^Bb$*!nPn=4<)XD>BukVxFgrn}lCu^^wsy8sDT9NaGf&f-;QxI#Um zsTmGR!*&y#?ZnSx;og{Z;wjF$$jW>)_NY? zpy}X-T~M~0uAHLcyrqY3$Ewo1P}<8FEI-&n(5;;ZLqnWHkdNJP4oinm2cYOSOVZDA z|FWwbY3Ta0H2-egg>aXkoSLy^ay!sxZgh<>Y5HaIJ5-%CRL67?EMwQPZ$B zsl1y$zf~;IsUUQ?L(?*r+gk(W>Pem+!ebsG&rk^nlKhf0N)jwJ_)K)BI)VF?z%jO) z{f66csk>v9)oD7(n_rWN42XJ2odF7%_Hi)Nmy7Pc7#O1$2@yg906td6Yg~lr=_EO{ zbdnI*<`CH`(`YNQ4MsN9ihoQNor|_u}A{&>Rz1!8KB69|*n55ji4iz?nUJJZ#i53NW~UqjR=>27(=)$${N zmZ#l`;)Y;l%pBI#>*P2)!Z#AW!wJj7j{TeQ5*Z-|g-g{5x(%Nc5GlH0YDWqVT{r5*OUI1UyL*p1y<0mm+< z3B_f%>FIXwAkk$r@7DWQ<@>tv1P`Jwdf_qp92;&u zTk%-;;OR)K{yG=gi&D%y7A(3$-bi4r|C1}W+}LND?fhR{v1QWp*KMQ!&2$&y^nd;S zzuERwmoJ0=zuo3k{#TVzIN`F1bTaDHhNPUS+3pGR{q?JH^unfz?ircWrf246N#O*{ zK%`&-LHjW-beZ}{;8R$!Tj{Nx>uXT&RXAoUV`$l`+=*S-I|k01eN=I}o|Y^5OWJC z2Xw}4l6D-51v%kaPA-6H+NEm@cGDp2yg1lK6DlgJ;0(qwNga;!zV?uSeHaac4R2|o z1d1RD(n{gv$X&HiHy9=4*$KJH77R6Tkw9qpN`9qn})q`H=3pJpoGpylP~)zavO z%_Z2rmopY>5XKV+8-?m4RD2c({_TXvC{mJIp+sSBVa;uEHZBJjEa`!f7@JhbBH99o z55)9vXurqk5CiQLbk9D}FsnA(Ar`kcXntnmMhC$-7|a`_VOm(-e<2)>6>m>b71O-x zMe!W(BkkUKD;7zaYC}C80h~uaQl2yw=UVzJ*VpB_r92#ouE^$Y>9wnpC7cp$ofb^A z+t!%@%3!w2zcIqToyidb;V-2L_6dp_eC%yn7L6JV{X4w@{i$q47^7g<86z!08AODh7GO+X0_;Ryhy0Av1mELyF*A zpo4Blc2Zu5dI|Kxzo|ru1Of|4`mCQ)_ULS4`-(-b1^$lNWh3EAR&$Pnf2OJA$s1=^yH{77e%}vPGH%p{0=p2CL!8xOmlFCo&7R67X z;(Pcr6>sOo+W=h)h3wUQUlM!($*8a4el8|`Y>&bRVoaReUgJnQ7KJhE?ff} zXy+kD-5If?Zg%FhnVGrSB#TV@y9<^wTF{=T{kQADFdSmz#EydbIQDxG8NjlSMC={& z;GpfokeG7B?MmT4#oql|^I0;P27^sli`_XZj0btx-ynYJ*kfkp2~(P4Err^ z%g}Dax^+Y~1(t`obU+^EjTaQ!Bnt6%>DhDgeHJMvB3V2_)HFTM57|3ItgIrf2qXQ0YJ z+OT)9626;WgdYriWT}!EFz7R7w5iZm#zr?I2poa43OA3|-UO=$K8;s0u&n`IF88Q1 zppQGoGS)$?bO@)=e1xX2(&jsH`aI3zlmX}3>8>VLZCmV=;&9#@Vh3o;M~3E)-3v(@ znm_p@zfH&(N^kCKaEn!5cpYYQ&I^`zk)W`n8fYsbpy|R;))oFeVzdU>bQrMVpNpNLFLC4-v(3qnL`$6c#hqB#TH=^k%s^fG5NRiUj%f+b zsbSRCAx|d($59Tnv>Wssv(8i#hnoaTUH}UWUeW#ZW=w9sA5dnAWeQ>Q?*OjlUHFg% z%?eUM!#?ep$F986cR`+Y|MaA@B zH~V)QPI51gL5I``;9icJPIo>1I?RjF`qHF<`2-HIA&Tk(4fnr=1_MPn4k1oi6blQZ zLrbTc@Fhcv7VSfHroT`EuO^voBzHncU zS}FqY@Qoo3+-c?O5X|2c=HK)_WgfB00yYL1L!XyJ6g8L z4!+vj`|7}Jop{w6oX}!p!F|*RoR;&C3*P_P=e@=*{|fSuABj3SJuWJV*_J+TkNFG?EO#TR7cD?D1^SnHs?%E)VwL)leT~jS$G1=UM9e zjn881>fg}wkW4Rrkgx6Ct2f?zEjoP2bHB)OKU>0c+{JU;g&ez)Lp!wX5RqCdxkvLn z+96HtK(Rr5^isA17uL3dqzmZnYEh9IpVDos(n=oIOAc!#I|Yt}amcfW*p%`a>RoI- zP&9L0$+1G}y=LAgs`5)7RwwBtd-F?ns+R%9Ox<(uMB<2N%UIVos@z?S0ae}eYm{vNX`IIu5?#6nd5(cl6cW7gHw zOT{eIoj$hKrVN3Gl_;jNy)=*9AI0ZxPR+yW9onapN}kn9nq5!dh{0+P7S4qSg89W0 zdq%#ec{7fzdXRrch&b$0G#melo|$#KUViVy^RN0J1dGw%3>qZLpg~E4lJHQ%L`^bD z2!UWQB!C(;K!yav!(>iEBoh)R5e~;u+G?e}+G@pHz4f;IS`<{ogm4L{ZKH52Dr!_( zPdc* zCN?;jzc8L4Y%s0IL>!%1?L+le?Z=2+s{Igu%eC)s0EwgQ&w$B;6c=$yX8$^iv6QAJ z4U7y8L$J08t+yDXWIwPJ*>1>b;(`ML zDcU|STD^RdQnnL^Q#dE1j&~WdVT^bG!_?1xyA9rpHew|2mb`0&_4qY%{~pkMyYd2g5gUiJrFkR>{H)v60`e@_8%( zGx%=M3FrmKM>5&1XJYF>DKxj6%8p5wTS|hLo{u=T_K!heohByGgb`~iJrXuz&uN9v%m&s1(#E>7)MmNNR< z+TTaUfr4yTLAuuxYDm|z?!f|3fKy9!=Y9C27R)XZ?nLiDb%2h>Y#nq0#3pRzv%`HIag*HeW7N)8&=E9bl)&*XLZMR!2C=-# z@rg51@ICO@5W-5en=hvsZpIjpqSFk=+DD#5BRwV!(zkKRn4I6op&A*rjE5mhNj`ll zE*YtvK*1ZYJlBUj{5Loeg7FiYK|r~7`=vxz%hwVilH|QY`_IYjRJ^460~A%RJwJ|O zTccv@8Cnj+&nh?&?<@d55LE~kW=)i{VM}YZJZpC7IjWL20g084V}!+i^A>5dDa2lI z6Mm^%4N&+NLnKfR22HRKGJ8t`SiRri_#7AQFrkaQA&2zlv?%A*|`XU<6eJtD*Io1awL{?n&xq{u>DIkX*Fe zhzcK&468;~xBa^WQ8w)#I6Xu~c|B;;=ADRo7$JTB623f1>BQi_6%!SFi48(BheCLk z-aMU%E*v8pkh`GK!#rQi8l+vz$o5C1g!e?5R2FO z2^v|v)&wCj!f{ZK3R<*OBxdIt{TLkzH0zEVLG+ zAUC8U?evOI{tZ?foxtuZKH%8xGG&^60^xQLKBe00=&es~CpmzKuY}pf3ni!1NjMQZ zlhK)RsH@Lnd)_g2SZ|L1D^ts7oEyP+EV{ebqXs?Fq^DUwrJsyLFWqz}Hd1pfzVGd& zuk2Mh*wtqaw5lu7$|YuXy``-uts3QK{Rn4imgc^UL%Uh3LbETOC16Qy&$o{~iY?(c zq%}9cqo1>}jU~KURl)^1B)i$=Nj>a1R86@DZpLfYc-GS92Y9!g&~TbfEQ1e{6mr}v z?*s4Fn`X%d7`U6_owN6^crVvlYKX>*vIxI?&;S6beK!kJ9&^Qf)n3>A?uv zI;J3!Ka^&>?|iu3^F6ylYrWq%ysy&J@_O1 z1%mfKz&=}%xE+wXz~XwCumZpHGW|-nj#>(bO~V{BZa7RiX2dbPXQ_tePhctFP2WKW!?*RIOIdOo{bO9pTH>h z_$?!GhP?UyOQ?$!psz5%m20sD{)+BBo9EH|Hli+22GNHQ5O~xu4=W$^u#l}{LXNiQ z<8h!`Ktrh$#uGM}4YrP7Avp`T6i{`4hF7Fj7g$}T7Tf(>!ofR3Pxf)5W(Cp(Jxl)nBNP9 z5vP2iZ|CN*8ZEd651TunEjS92#3?U5Q~*X|@4he1CbN8T>*>i}==xpbwYHv4^Cm!< zul-H=!-#l0rKORHjg*f(eNTfhCJqr+3Mb6 z!gBl(EWHb0V#-R)e~ItvnGxdB)#zC}~m$6x}MBquR}AV*k-LD)dW0&@sE zU@~LvgEvx>zYlU|*3^AUFC9bS3z=<_etaqf!m`(`K!Lx;F?Rt>FwL$SNzpWK<1mb2 zTR*c&2%XacZh{vU>T=D6HJz;sz_3n-3G5;8wK>Sge~;N^d*FINZ3pOM|73vJJ=Wx! zMcn>N5u&^g9FfUA)|+7gB=Zj~Brb=0yMTESElk@aY^NjNYs@N7oSx$ zoUVgi(e-sQ9GC+WKJFIsA+1!rPBI_z74SWRwG%d^K1lPuuK4y7w-PdLoXEH2|1i;F z8ma^a`THU8OJ4`4HYD`q@1Kesp@HN5BaPlt?Ma$AX>r9EfzdZ>wPy!7p|Rj~84rcK zpOSL88@((J$PF{XXD`kB}t823NvR0SNn?y&&ke`nfB9H1P z?RPYOTh9Q*O(9lEv4ot$6|HBWN>4rt=!0935pew7k4-Y4e&j5NE{yEzCEcu${-)$dtwLpo>$Ht z;5FvJofu%$<5b`7fMX`kc0&1k+I)Nd4zz6#xIw#qu$9y;Y#lFQS;vWJ$8YgVf=WI4 z=nz!bo%EA&sQ+j9VazCK7-*kKL}WTXH>tjzitkx&-c?B6D+h@cN$oPNE5NL3g7rBrbSG|A}-jiq*4;x)%MTj)pYK zB23zyl6SZ|+nR04?^I@^2&;dib((F{bwCs^fuEf~nvlF7_R>qWg_z7hGAM>%%j~8d z7$qMak~>2(#^Vh3x$}T|JiEGioN+9SkHEcobqW}AEf-sC8tU+Q3h!Y&?57yUd{tR^ zE9X0^kD;lx`;eX3AK~`+A>iG=34eOxRIz14Z;@SW&(TA*qFpxoX{CB|NUt#~DURw= zq8$M14jh6rt7*GZs{IN(TaMFJt*%*K@>s_|!~ro>l8|K~S~^-2(~Pzs>{!$~7{Q1| z!!_w`;VebUFEQ*X?1Mq>A=kqD?9K6rZEl94@b>h# z#8&Llcp?(%7lfwKJgn_Ow(17(*LJ1J3AH-4)Y7((2o`WmRU93(3se?N3X6@v2z_ zO(^jklQt~Hdaoa~vodp#n3wq@DwAIIGPyiI3e7kqlrvOQ&=q*7o0xTV7Gy|VH*QQ* zXSr^aof(HPcg2HykTzP?o!!ZZ;i@zHA`D59IcO?u+DC#X@vqR#MeA`l7WveJ0S7ou zpQCLb1<&nxv-$7A1ibv}Y{=bqdLzO%ex~&9B`);Gr_QRfLn*M}Jqy*--_1C>1PkLe zhT_44?WHCapURVvwb@^gl<)#g=@5e`7pAV1gB^$T=~wxE>H%c{H5jQJ+{?MEv*51i zjnBRGhP8H(=nd%&S8@kUz|D(t(l`|Sk2(Jf*_wD}>wnwU^$^x;s_J})vNSnh&L*yE zD{)ml1$N*d1tD1LfLF}4ZXi92H(eF7+pc!jH=v&~gVx2j(&JtQ@RZHyH=$6<6 zKRCM^U(}K$dQA%apr2l=uXuqNXzIgeB-~99-yhJ~&z~@FmS{gi*W_X7lh`((6JhUJ zVRwz)XjctCK6S#XtXt6}bVO}}q}oxn$)fm`(C32GWxf_ijil@Y^UF3EVt3)tzr4T) z!B^<^kuW|KC@hYx%BuCZhT=ZTyyfYasBiwS;eGh8rPl-TlpVMaNWuLV2Q;v-7&qY&z$79bvHJGF)1w_R!(?tY_cJ3$-a8D%>x zDd8i<3R+*GILh0HKT&+!1I14(#rz`M9?0Kbf({mXg9wkiZS4c%Qm`W?Z(Y-sqjTv5 zqw8@6)k=1w zsOG0a`CHZbkg(cndys6W@^h65xN$ZVZ^sy=vwodmY!L8Ca(kevU7WFCYzg~kK8t^& z)6fFJIVZ)Ul@iIZOIf7-79$;J4s0vh@!$A}m^f^E4N#a)Y$XrI?Ik*i`L_SZh=3`X zsIhew?*7I*fi}nz zhg-1dyW~6Wq+o9ik6cb z9TC_dF4lu@k{#yMwP>`hV>}!`eO69qd`X$(9OnjaLT&+I`J{Ro33ijH_;t)3{ZbrO zGgeZ*T>0Dt%hWf>W1un;N3n1cfWfO6l-}&%a6flU|0>-b>Y}HFoBzZ@T(x+x;e#ka z{<8|M$M;~zfo9r>O#uDyep#Z~W}<(UJ$t~&`J(iUsne#~k zt^0C!2ZdDia#f0NhhLz~&b2nyK(I7|AgdeG>!6`!)tXVfzVV-={karxu$zYqlIJl1 zN$3>4@8A+aP2X@&kJ}1-JA*fQ6!%Cb1nRabwB2w>BBlvN~AQ@xWk&tJDm0iXW)sAQtfj0*cl7X4hORR+aeIHwOQu?HfhClU*alH&lY+lTp0F-*std8dio3Jq)ZQF$lnNo9+T5nJN zqw;y?K6Ye2l7bDmg@Bp(AJKt4S5hKb9oU(xt@kn+|BUo#K^@5HWHKRt-TRepq%0kV z5~}Jdnd9R%YrRgqtgeAE!4>2{w4S_)_G34!+?@t6Kj8?^(;M>g%OBYhZk^ zPF_cj;~@dzty=BG-CixiVV-VEj0wCvc!XSD<)o4z_~5BoDZ_Y#lvB4-UQJ%_VD!lw zR+W*TraO%#|+9j zmo=d@$`o|~PEiz|1FLhSYn_dC za6|6&Hm$2-q!yWlKeLV)Mm;7t+(+OwgJH%pp#+gHks8H@hcoh2t|t1#e$C;1+IhZ@ z)FIdD$nyx29zTj|WL*X0=xTIX44IW{7%4y@vH~MU%={`Z8Dk*RD=XGEu92eNA@?|G1nnEri3+^V`v!Sf3#bb7XiNg#tO72J^mV(uys)|hv z%VdwIs!q5}igXh}RA8r9OO%XZ9r;Fti-hwU!#AgH>xMU{v%XsPvO0#xWL*`?tXhMq zaTbs!I@PbMrBNP*DG5HTAVW0Kfk;SIj2bgl4OO+Uw&TIbUcGEBGQkWk8Lm>I~Bx&YzJveRfvkgOsOF-mE)tkG^4q(dIOdW8w@40gt+Rq^e);yjx!Dt;FQE zysC<3Q21}bEHS^Jln?W2_@hQkh7;C?QLe+(N>dF6ArxSxTT0yUTU{qFXIR3s77J-W zG1S~_sIU+qv+`PoC2KDU-FanQ4F zS`6K^97#a2PG*5PZpkA@od5K1fEVjy?8#slZZ|K zWIXwJ?!dDF&kynJ#PdfyK|E*iIA4W5K|I-b7T~GEvmVbTJpYF0|G{$@&!6y|!jp(y z#soYrJoE9a#8ZOm_D{QoNQpgR2mbzqK)zQTr-yrE!%celqcP#&uZV4@12KqVzw1rL z1^6t3HxZVOZO`9AD*sln^pFQM&6SVS?uuVyvwdH&bzX|a{}pK<=cSmjt%I{Yb(n&{ z-L&t}`@msDvm%<;s2E5}T^$uP=^hys#tqq!(7+V?2fpDb)kg6Mqzr(@6D^K@ z;y;lmwgWz^HVJz*&V0ZA zhE~@Uc?Ryk0+7`%*FFUE&n}PTq$oMH^NX`&@?yr3XJ}wRkxzb)kYLj zuAMDH=?Uy)pjl{IKz)uxbw_zG5B?V~!>5;lJ3(P~?nv+%wPFup{Y0zl9y$JisnQ)B zfSgTdrADp-7VufKapr4mEUK>|cxL@3WjI%y6EtJUxus9_PsDpVT!VRTnhsr$OPd%WXFD z?|sqV`+rdk#Aq&}9-#L_oLfDphf{k-!Y8KGuc=uH3+qd-qPqA`%%gqNi&+l2I2?keuSI(RbB#KN zN>}cs($VusJT{T52K8S7+(=fH;TPILF*KD;PgeBY39vhi2%<8WY-}AFh#}G*28ioB zJE=Xu6(^MeiTGe5hsKl?qRg!{+N+0vTWAJFh%V=cC`akml5HHTGa>K4x1FJ=@+=mu zZD!?|cuBs7z2LszBwxWm7*VPirzj^!Lyzr}oR)q6YE^ z-YVKtXt3ZCQF%K~3?Dy&r6t=s_%{96v)GSgEBi@Waf||!hu)$erj#DMic^7ig>3~a zd){{1ebiu=(OgB7>(i05b9Q_Ki)G@}<`VT{g%Mv%0y0W>xi*hdM#PnA?684jVb?Ug zTG^I2(dS7R64e8y7F4?@JBh#1buHc;{B3d^-qaDShizs(tQ7$)8-Hh3iPp~0hJ2B& zJ%A5%Dw~R9mm-WgL6}@S8!Kv&9d|4DK)%-8b`LrOZgLA~CG?6;JlCugPNs#`JCfzD z&b2G7K>r2|Qxi^Z{2LsSH?3`h<3scr?o1K0XHW{ximnaW8?` zoCrt)Q?vW7DY8dMCeAP9&g9Ivj!AFFJ{)X`#6 zmg2gv1S}T2V8HB+K2TnY1I!y}UVxO>Ngeyp4(jWeNPh}ZRX*#*tISFQqlbTYFFs4& zQni(UP_2JXq(~a z%%V)_d5!1g?%76znhr>JGDvISiQMe7YugTdZ9c-|?xvYE1T!3b9F@4E$I*>Q*AvI8 zO~t_=pTerg&pa9D60tRet^kClsCGW2MD|1>$vcJAxU@z{`!X`g!!#25MRYL`N3qyF ztu(pNKV}&PbZ83ZbOO)-x`B^_IYssX9bWhk!*#|DMjRCj{EvyG*=6)uyIBeSzsnF zn4@48`(6T`I+qj zEF{>#Y%rP{Ze$;iV~$8Y=*l0Eli{~lIcSQP z2}3(7_bXH`c$;lk9ztj1%G<6OY`e_$wq5h;L8aqq@Bd_<{H(aj5wz+vte@i1&2xhX+&UT z^Y149L~4d`h{GsfDMmL7LvT;46JIGpPiqqW;2)>{{$>*v})fjMPGB=G(rN5w?jDzP|`%@PZj0>NZ!)W_DamyE_o)KtMg>OY52kxq&&s68=xPN8ECMljiS@PP#d*KPn+I!LCy^kYMUvHI+AV*mhX$m_8_Z7DBG#7cPd|9SvU#E zDQDHf+NQyNC9g((WE@KqxI6sC}gd+n1PnyUw*_SwYK!n6-*`r0*n78OF< zi+8ufPynLA03E94-3mN0o7+5}AbdcP&|a7M~0V(^CevoZkPN+XJA&dD$}B#B+b9k2i# zWkfMJ9p)oHXqn83vz3wieQL@-llxXOE(I^t4=B@r;^vgj{9xme=uUz8ML(+>>*;#U z()F61ew0hqCFbb&Tg~bcOH6=8U1E(1u&PT~T0M(f?dlRoObmy*Bq=5!NnMg06OgPf zamEBV)g>u00V(Q|NihMF)FqQ+0w$|V(qjVB)g{wo0;a1=X2t}}gazl@+p?m=ZofCn zqduBLEDZ&Flt*(3&h`(EZ#m2v0nko#^WmPZLzb>XsQw{~G91Rvqu-0nY7s~miBM$0 z&NU_g_R(2dJ&PhJxqx(0NgQesNEa25q!xj6Q31(n5l9ym;8cr1x~PB@wFsn(3Yer8 zfpk#;lhq=SE-D~hEduGH0;a1)AYD`dkq$fX=rDR!?`PzqV2{FKPCCx((Af|vD|dPm zm3OujTQ?8SEtF43j)(Xj1m`!lU(+GdQV7xzQ(*>6o&c`(26`XR@6^!_bfD6f$weI~ zEsmerVv_M@W>-ssp))tLYvWf+FXIvETnLJ|n1X}WyLugg*Bry^EK~y7_o((ojLQhI!8_C+gS&e+n#?j&~Cq>JM+=Z*W?c) zd?MK+$#@gkU=ev>JNi#xaYEM_=w-p{X@YGNaVPLfP<&jHR^vH{=OsM9!t(^4hwyB| za}OT)I<0y0CQO+DhcV~>!V2^^-CimTg!5316HCXV+#NET8Zw`NwU$5*=A2#@o|q*+-KWWalF*Fs|$;B zzznki@Vfmu`eCUyPe$x)9ey+en3qQIG+uBFjYhat z@Bu+U3}x+nC=(uELwi7T0d07+WjGI{R%o7{yhhlZ;y4}mq|TcigCF>SU;>L{M6w}H z(AGH)@Wnvj_Sr^HTlXM1bVBnt;Gg=B0#`;-EkaU(ALy)+AgLYHca>>!oW~Ehx&M^Z z1KOtRiPTyc?SGBbs0vvI^iC$48yU^-17E;s4x=tZBy7tV_Ka{vgYgvCL^VH8oi)^X z6jIQ73;8_8KwwM>c~;p1Q+ptWq%MW=B;tY~a|}g<1nj8HChuzcF^V@*AQ(blq#Ybi zTNAPKLVLT7k_-;fgobvXZJqDHF1(y=qef$oOw%RmbCOwGI;qv>6Qcotu_q}^9?ojQ zY(=5?;Cqa5@o zCflxFh{!YM!{22x9P+5^t)A3Bc$DT+bd%Be#zNY9AoYeY$YonviyQ___rr zl25|+3`WdByz{I&(?gh{b%9q#Gkav*!ZP>*2we_9VwiYm?6`_gvUDDGvDK57d{5Ae zt^fix84dE?=xj6|v1e!snBgw$)dVV)T!}Q`6AjPl7LX-1e~|4%krG3w2@>puJ$rhm z9yKe6eWh4wh(!)HB8gcc46iwn5^NpMAQRqiN3eOB_TD+l?+pi3pIy1$wrc_HlonyA zI&f&IA+pDxX_Qk~BSrSKA$Ce1*%$WH;ZaPrj>Mt<{EmWcEYWD2`Fqd3`bJA-U({@n z6QJ z*8Xcig48;ZJbsr^9&;|~`#TVm#+n6l7y6$Y{jZXynJAL80SXg!EGU-NMPH-!%yTkx zAT#}7Vk$O#qH<;%HuT3Z_}J9g@ny@!tO<%Ot{kg(R76CWPx4nmTbI+h*lOBLHHIM` zM#y4|DF+R4Q3is*gYxY)AHz84NkYRje6)$ECT{UTWO%CLnc8@cTOE|$XlgbRePCnM zDz>P1@N)}s?uLWFwBnO!HjjEU+6c3wj z-jy-LW3}bPV_o>@6izwrB90rDsarm!wq_YU%copBWyP{7)s^+tu)x3auBxVMm%~v1 zIua0s5E49@Lh6Z0pItGr{tAf5K^}6&EH39nA(Kh^s){L=ff$Uo$)GJ*F9!&CLiHs> zDuslg2VnMZumrlV+D+&b%s8W5$i&(QwK4DkgnhXyKgsqm#F-6ntwpB7v;}bCkafd|GEa|lkYY{Wd@ zY{updxDZw}$Qv-D*VAwox$9-Mna*OK1HSuEHZmZXa-~z4?_I}_*4_m1J+R75)r1NU z7%;Pv5Hw-Br18N|L9v27fD1srRVg9}BdPf!)#RnkNj{e1r&5@Fl zZfW5%ux*5mjp+`7Yp{@Fz#}slw?KE1bLp$>9`*rU8sexfYfJDt)uhrDsF~D*6BP@e zS2phR~z$A6aZ@eQImyO99*H6zbt|lsshVSMa08LotAAUtm^8 zDi*rImxm)?v#lNa2qf$PUI=1+?58<=HJARe4rNH6uGne4LKh$8?c7x1( zOV#l{u%kS@>p9jXNkz(ASGHKyaw{FE{e|CZs>xf?j+3h2MeOQ5pX1N_^rxTxuu=RD zIu3^3gZRNHW?A`}5P{@PA{^vlr_Ag^80ys=fO@t0C1eNTC%V11wf{R{6&B68^qJEA zr5iQt*osg#~}HsHzkQ-*>Nj46`Bm@Lur^zacoFgX_C0xa-hH=!2MIa1ldx@Lh{NvEY$oYIZKOMg@?nTUP{FAbk zx`uL_Ss5ozUf8@Z7F5|P+JLeGaet^MQ@2q_u!(8*#1oCZld&6CgMZq`ruZ#cFNId~JEpx;n0 zovV?_D*dR9*3&4Pnb#ujq3%E&e)KuHTbJ#!QZJDcg|H%~u}hfP)5n)k2jn&5YBkYq z)^=hcm^a>X9(9q`l;QGiD#kkAa+^TD1^xBvo#yN@TtduuB zSI(CrdV7`2manMtNR!scwY8~H;W@ePy!--bnY@xDsT({{;8^4J%yL428V@A#!~a0P zwi6`JLv@5kI5w|=LaczqmY>QYE4T6fCJ3sICCaOpvOWbyL+saIvZ1AkfoIaOP(T_S zE{}s65)9TlLeEh?%zVRd0rWqJxXN>R00Z()Y;eUHgV25L>6nQxgjN9jWx$FTH9(P- zfP}xnG5(jc@jKBwHq=4E{G)B}FeO0YSZ?3vL1`?$5*Wh%*tD|!<19NF2QXjlrS_s45b62e+N2BDD0d%=oO>-1akWPgNtpO-bTm7f$aNV!PeIqNQA?G z6Fs*@!FrhfyAt1iEWQWu`;>r}VfkD0Egn6efA)LnKP2fsJQPp=jZX7lC`*4|0P^T? z^luZ+kM;N0qhLMkLcjgUdx9vR%BJTH5vHekPn7@pSxH)q^0!Cf@S%X|%1@7f6maXy ztYK^&m1qOrO3wZ=!2STWv88_w{;F(%1Rx3VFdHdFLrQ?!xZ{v5kc!_^kI@@XVeh{L zFwZHiKN0YG8{Pla_vGp4%a{5dYMAA#vW~13A6^!C?*i{V%iJ&R^VeWXmh%?9V-?=zqOPF)$ec`=xLZ#(pw~ z#EUcc9}dHY{vN=Az+w=9&kW~MjkU>^q9OHR32`h7GF?!`~dkw8`QvV?nH3apFzyi2oW8Fh^l0o%W z^pi=1_hXasH}o9;OCj1`2TiObzBTQ=4hnfMZZeLk-hcapzMnQEh$4DAs78IKXr6HZ zLRnJ(Ur>XXtZoWtHI^qh9-H6=B=EmX9q8(oOg@{fgwrxMyUv!aoGi2)dTnZeqWQaE z!I);V9@g!^R*<7GmIGYW@<3vJq8kND`GL0EL6VXMq376q*0B&dl!Tt6VHA3f@{Pgz zF*+e;wSoz<)|V+V7JX51K8d%&tZBy?R64}y&_4h$szIkmHRvS04fG+Ji}OD=n|qXi z1$lcEOaEhEeSIkB4)JLC+=9>l!)~+do4O5n-R&UAxtP)9LEVU{d-rtMft-d0<0WVJ zbP(~@Q$w%3Kw|0>SpCot1bszmS=&g>pf=GNjQ4Z&Nqg!d>SQ<#@J>u)kvYM6D`$4u z0*`B#o;eV4(Y#;@jIVmIz0$5@WlO?LXzi|M6 zV@ohnNA+{N7}4GYL3Q5k!u5B8ARULA6Z}t6=lvj1ypzv7BAE=cxI}001U9r0S*l#g zELD!~{{Rig90~rCS`d=x5J~PUq$HTrdDq@-==x9U1Sa`|2wt}KP9$i}Ot-b~ z3a8}p4;a$>pF|kPU|7TTJX^jxgjH0Ohs{)0fd002rRx)-n8gN%H6Dt_CW1?FZgh73 zKAZY5b;~VQyPgHd)-1mjbnU#}AE13Nr@XzTxF>$v9O!El zm{D!XpFadE#^>WMn{=yhwvM%!fEjm5Ego1fpfCp0f7-tBw>-0d&a0+Ha+|<&(`j(A z(%;rqf@&xuffG>Z4t-9!Vn9{c;!{ewL^TH=BfcYhm-Cqzp72k^Qe#VNCYPVpCD4OHO%YdtsWLussg{hwi45y`t$&-;9R zFW84@3W)AQ`u1{6WP7<@&q}USS?_kSd4=Yn z>7X#pLDc6t`w?(}d7Oz?b^#xi%hYf#lf}LnIQ6q;qpNxtuOj!;7Hv4<(Iv5nBsTLn zRfudPDEq)u1DJz!u|(8R|Cf@z!SSovAKue`(>3no;78Y8dK1nG{3j;UAp!1faZXT% z3vW#=!|BIywalrM^$B}r!X;sO>3ilB44C)gvLHFn`PfVKz_1kPPuKVT9$Oc zcnvB~zqkS!B-au*ffszQ;%F$pPlcU1Ib}YWmiO`p-#-4{QLw|_`4kQB$7bXqYX~$V zCoPfElnoqFcbmu7yTIs zVU@YNfYl=wcL(7f9LefAl6~hXC7c4{7Dm3FeaYzIzAbz5k5Oj@CbqrtB?1?6-YBp+ zj=2xm`1dD=icbXBMPXlzE{fep2(CLz&n3!!Y-B(b*5L*O5oMUgI}tn*A=DZ~5`_9T zZQ#3b8_=y-OdE`jWKBKw8`|LN=r)L|_X*62yxttQF!jj*qj#L>DDp)vUtdG~&8;`3 zd;i{g)3vhU=Gh_nI5Z{}X}1#PdivXu5u>{aMhW42)Kzw(`3h=1d3qEvCjovhZee(g zC!stVQAKBQwTCGInMnP#<6`Z8F>6AH-ngtEe||n1CNeb<)*)cIDpDS2g$XTNdk3;U zU&offOvVs(FYS24!~eZ}y|ZOlEh-WQN*)i;=Myn4(SeK}s}2 z6++&WnBfr|gG2C9vF+kD%vJ`|^7{f+2V-XGlx$>)fA(xPFEjZHpPkI8RwQjHOMWlk zqR#+O49093cN}xrv0{rD`|n{OzIQZehua2!XK%^7G*z45s~CJQ?gZjtcH|RwYO4!9 zWA0f~t1Gi5>wqae^L;pT4q;zz$_OO{zjweL%9tO#a&8dn1J2gWYvd%Jpw*Qwk3igb z1mh%NZcvMnOK3YW6WxNZB>C1YW%kXZ-DLUe)X@W`Yl5Z&rZl(GkMpRO>*nkwWor_C zefS{y&=&TWH19{@^TrS7U`976TIRuqFUYR0@1+bi4v0T%aWN4jj|Zwn|3trI}we7?m%e zJwU>lBki>L{gV))a+3iaf zb^aW>uvF(?u@OxAcx!^wsJ`7B=_csh`oj0!7^PdJeI9A`x~5hP$c{zYN|p^|j^wMu zRXJZ<+B!yp2)J&7gbXfp*j-&%Itudpu)pT1S--+lU&9|?C`+xHU!zAtm|oeRf4Qe*}&VY2WVy(!bpF6 zHFS9G;{M)_n`?YHRy7zT)jxKpRtC=<9yK@K)1{dwhQJ!!K!4=Xr*fgjX@fa(y3ZCS zu@7w%d3-kh%BNEy5>MrqZh@%;lA`clgeb7rGP7HbQ+f-7 zadYI)qU4l_9gWyq*~P{)xqz38g@F~p@HTS0D_(9aCC0GfJ766#;DxaiGPCloxFcjX zP{_;)bhj}%#FuIi5no7Xj^}I`Cd6SGp}jPD1m{GV4{<^&0~(2+82r8gX=4J9 zU{YlWI!0VU%Fr%nRxRNnvTd$af|%=oQaTUP!Sn{j@}IIosPkS-^)m+CZd_Wr?LD|^ z22#N(J#lp3`s^5kL`hoVa2{WkdIx6z(Y99z&jipKM~S$moNF}%s&+=?iP=q}<*ow@ z{hbg)nuelCh0+WO6e3M(;11!jL%{@jhb1_A)xy&1dWcE*yBBK3nD8;5%y0S&iK9l{ zKSvt*O(6KvopZ6{Dz)HNo5s~W5S_Wwzi(k-(-+X-m+h;DYhDaGxL=})+-p|H1>Z!` zkO9fXgxUklF(Yxw;htPjnBHjrh##Du(@zrpAUeiwPi{I7o6djo_)j7I=#u>Wj#(QA z;xW{M31mrNm^vTma%yBxlPt3I-$iAETZ>zQzcI2>G=BM|IPX=@pfx%=o8t`b66&q6H}q+kUKE#x zj%4tTBJ->~#e+xzM0woktP1cUCT;Y`C6o%*O)NZJa2OjIcEw%7lTe*7@!%#1U40M8 zL=C~lFv${$tLs--FKmQ|o6z&$#=;dXPXZ}` z>e1fG*|b@w<0-wJvB%cc-}3FJ_ED$w&V_76#SxOUs5q)FwkQ?HAXcta^g-TDsW=X$ zNCm0}ixKC9x;Pn9%j#k$Tm`9%QJr5IXy^+y1LP8W_8KHhlmwd zv+Wh~T+C^yGA%?MPC)?#llZ7bDVjtE2cq6n^mmB2p?I=2K`HdH(B@<|tnW0RZM)OD z5;Bl&cUnT&)wLB`B;Hua3(PKc8C}32f2j4+c!(iYZ}3Vmt+k1|DdeAFd;q?6A$N&S z^q&TM+!X5`7ORqXW?f#ESzKVoXpui6Ekz`v$E>eKi_i}elO#Gnb|X}*4BlbEEBu2d zuOm24Xix;N1OTc@!Ak)Ed9Zrt%KpM{FKBER#Qz=QP~R0x8*t*Z^q)v484i*+gq08P z*tin7j3$4XWbXwE^THYeqZPI-umye>MjQSG z;wtbrGXDEZ^>A8OqrzQ!IM~zZ@GJH38)L&0_3+zc!@nSMQn}bxN99BP40j)N8$HxdN}63mG-rAq{=;A9t{^!RK!O4R??PlzfNw7GrrtSv#=|UlGi7`(O1C1y1y$k zZbOjD()qiGGSAeFWKlD|da7PHEz zaxE3v^B1D$80?s=&ryT}#!sPMwmH`j$Tdf=>AZe{n@w!9fB4UkfG=>(pt9heW(55z z2Wjuu9UP&2W?})%V$1@q2LypYJv!nT#qlzg5lje}FIM0@el8ZLli^bMy37_&@7O@$ zP;2I=@+jN$IffS*=E?UeV-@SYV#=rah~Z>~tr=KlkP{baGM4QAKTySrErBINt*%ey z1o)yXH3ZX0P}19c-=g58uUz5VBCwB<1J8RVwfNP*u|(^9|t-I69euc zrCYSum);v;w&03Dk?}=TPCERNYYd}AoPQt1gz&*W6AMA}49h%S4?%28Fq7tNi4j>y{VMXrzd&3z{zk?Fv;DWMjvM_u zZ?^|s=cSmRSP!}x@>0x!I2c3=XJF;wBj{^)|JC?{f0XBP*o|IWcj3YGBuzvNM`1fi z$?&E}`OK6028qZ+ zdCKdK3}4O*F&z=iq;*gNxR?$>%D^l(m%~DdUC5O`21UF6_dz_!;u^4hM+7ym{A_R- z6FBv#+1Qx&P-q&G(dIt0*9#3fD2*+I5H5~g$TF1$J_u2NESA!*6Hj^`*7fyQ`^@5M09*Z8YdJM{;8Jwb+6<~W8yfnk_>w_TBsL9m##BiOYH zHzxXuo-xt^KZii(HM`zV1oOKk6+46_$cn`L9C< zXDR64o4W57+per6(;WEdAC(Ta@KDPC>Fi~4{BU)Pe;&&nyphmR%QI+?QAH4w>}1y9B|tn zwE~?7ymtk->t40u7(8668|*6ha3~iR!?omolJCVok2)TT#Xv-$0?LH{& zN`Z<#rDA8GLQ^Vsc%W=&YHXk%$-9=Shz3LR?ZNNsxI)3zg?0?~DbdVy1^WfHWW`Bf zLrRc;VsFP-!TZ&U0iZ|0zh}V$AGHEni?37ga1^ZyWC!~n0EQ@MH~P(NBMubX7{rAJ+kKPRySb71 zh15{=v$kEyjW{FCKY@9m(P%h>eGmmH728>WQt`A}u|2KgY3z)k?%jwESMe;=?Ed@3 zaOYmCR_w>A3k*+PslxqIr`eQ$0)L?$jP6%z%-mN8Tz7`#9dK<6K?c z8|tZ&cO_Gc&B`$~R**jlQzQI{kX#cT!J30JnTqbh93wPch;*~fnZ5NR$`>urLMW&I zT{L%$>grf@XZ^st-H)#*jf#I+f#eLM4+mMR1TQX!)Ap@<*$dbjv2B_`ROE_Dp`3K4PCr)7apF*UP|ZpI z@hK<(6EqpB)4l|4!7ebuY)2!k7e(j@)@iAowB4DVAxxEn9>|u%)ygUZwBNC_)EuvZ z2nCStWNcZ1TEMjDJSu6cGe!62xmaYH;e4F5-kaybMggQ;DTJo3K}=nx(0tFM-4YwA ziV0chik_fL3@$#dUZQTsNf-3N2ASzLIAOzyO#ANPqdSM{hj$>Kx=v%alk%}iV-G1*( zq*n&n!EwNW%VHiMx-uUry_oF=wWCZthi$*1**XN)bWnSi!mwAt9Ru2FES6^PSiY;g z8R#;Gu74{a4|&9fnjfTiJw4dkiJ1gTZClrA&}}{j2CcjE&B0~tTibeOG1R-z?@NOA zx>{oGI*rE6+}Dz;TJQ5jnUAsFmz}w9<6nZ`^Qegw?onXd;0^yiBNI!^+}AwHqe4BA zeX+U`*Y_z6uCUH6&Kv|gt+xX+wdLnl9_7(m9=5co+;Em(za4GFNuxKHjw&wp9kHm+ zxB#k`f%gJ}R2_z0fxxbSVMoe?=WXPbFZ?TwoPku|zLCFRvO3(Dqu$8BrZ6SBp89Eez4_HF+uo-LU}J{sdzAh znA7rJoEf&5#t?JmpOmL^k*M(-mtmlzAc8&Lc2;rcUsee$uVgu=L-!Su!+Ro4wRm+%F>D503 zBLSU9_uuH|t^YA#PW(Ib5sdnTrf<>;Q7n9q>rU3kBN*N*St-if%<(!aMEM}948$2` zenjs`ptja#L-ZahII#YSIh*y;Nv_Bp`OZM9e$-wm-<*f;SP#RVX)cFS?k8adV_yLm(C*dJ-TRWbU{i{gZMiKo~?*#J?X=BrX ztz`HJUCa$&574%fzj(-i$B4(=Ry-tSV|ZiXrBhQ_N`2;Mp-^=<4iU7s0P#+%n>e@A zR5XEqo$zxYcaDYgXM%(&Q_sf{u}P~y4va2hM(4Jf+0rCr3><6^LA(_$AyCL>TIg&8 zO(qw~cmnR~7S4qZFd3u4rrML%h~?iq_iU&!ydS?_b3=qr?9<73j52*L-ePv_6A%Nl z(@BrN3v4bp2hzqy%!Obedh+ICtb?E6Hl(1S2ZN2+%7S4-ydhY2Vw(1d%2eRl*1j)l zFGME_te)IhvF|#Xb*n)PWgiPx9%6=#F_WOKI|_V9f$u2r9RJ7db!>nJ+K@if-ecyaJFY{s=QlIByV zOr5UBX{@Z_-os;r*AYV$O8Km;^46|hS+j=alo?%GY`iLX@BVt6>!xH}&nrNA)xaJ3 zdKM|Bk~&HZ?`6x^u7jaQpq&vTd2-#9sTn%na4fq*=Jh`p^GMy`447qeE``z~_`un< z@SYxBFHkWm4tzwD%*xjh3S-S>TRh}unnjC2X~qkMFL)+h_}3vBNa<6$Gm4=Y8k%)t;D!@dW+G?zlafR-aP&g?6BTuI zzgDrRz6&?NtN9R^dM>?LtVvHx5f4Lj%lHB+ZF`RF=1@wpQ<77Z?sK9u0XtUJMOHc8pHcufA!r7G=U4 zcuj5Xg|Vl+R^R1?FK-5y84~nzAvKG$DzaB@Q+$jzw#i_i!*aqebWxT9v4_usoL!`drJf3d#_*@vqf}7TvbQlU4H-G-fF4hiutK{lg7Y%)b>y25H_U zu&4*UrX>6x!|(s}pM|MCMN;klU&wQ~1Ocw7e{>c3ui&{DxZq|}qIy!BdpxzWKB}Wf zncfX~zo_(jIU^d=p3!5q-n*h^E$gH)y(>I;SXqsgwVW3^e```4Z&lsunzeidj9G7H zgjrF&AL^s^!u>EZ$6SatBHcu(4~lB^Q2F16=jPYg#Fod@RcG+;pm}mJ`xn(<-+|vz z;5!QZQwq>{94} xK7~#4mj#_!>L}Cu3?Rtt;9O^+GlxSA-8ho^F8%KZfvIMfjr# zUnbxzz;}r_M-itHFqQQ*GHVs^APf*}5%9?|a2+OK2Tmb=D&XcUN&1C=7tV);SOFio zO_E*|aP107+ArY6Rgy%?Z&X(GJ(Bd6fVXePJS5;_5ET7Hz|Mdq4G4H4U>MQmcmk$n zhVtnIoG9SwkdwYhz|B99B+_K1IQIj-TEN=@lS~zb?*Z%*@Dady0zLt_SiokO_bwA~ zGGH>ZKxtjZp1)TwR~@OHq30^S4o76Bgte7k_HuSimr zfF}c9BjCk=8wBhHyivd>0dEm-dZ#2kEZ~O#ld9Z!31b29F9dvNmn4z3Hwr%vm<&e} zymg->y&~Xe0Ur=>AK+dA@9vf)vb<06r=tUZ4A?B-&!XV3M4aB&CCLcpg5qm{M+ulZ zJUQs3@Fc)#0xkhOUBC}Pt~Xb}LxATBIJ*~pPr$dofxZ_7uN1Hs;p+r^Ki~}lp82sP zwF~%mzySeo1N@MHcLIJ?z&ri~OVa|b{Y;Xc6>vLXQlljNcL07thrh(w6YxpEuL{_5 zN|N3ba1vlLRYPf$A%p&jfOn2ENS_H<13W0;LK?gvGAj%4FaaL~e35_$0FM*!qoWPd zL;<%u4AQj%J_?1I>jk_XJZ!drw*f8@F!8vH1$-xX-8%*RG~iWw{7a!}smBMrLBIzA z-z(rgz*_`72>1sAwvRJNKN0XGz)uRe1n@5fTn+d)0&WFNT544HZGiU)_;tXq33xpw zw6_Jk6>y(`j|1)(uwy)EE8vrW2LVH5Sz3%XN&rA`FYp{8;I~2lBmtWPm@@@Tbh}Ex z$1F3GB$Wzy0OO)kz<&ZBs|8%RNs@d5 zJ^=#~TLiox^YB9gruE`c0Uty7b^$k|Kl~CfG*cupdGWjmAH*rnUIEj(_Fp1SAL9H@ zgzsE0Nk>KaafE*)!l@tj3;6a~lJs{06K#hCyc_G?Fs!3^m9}D>UL;^E@`Rc?3!i+u zBwZ!o-B@rl1l-#oNwWYG&Kk-s6yf$ptla{(f}TqRd=h0<3V0jxsS$81=3!aDk3t5! zRlxTrLQg}$i?72xDd5d$$Dix*F?N0>U<=li=k@R=jB5cWUoJ_n113CA0MDZ$oaU20 z0Y9B9N&Nz*x#cqfCxiZH1-un~CLw{-BoB2TE#RZsm>UI5b9;^f*@;q^$zp{ul5z0;c)@ z7Xt1@9d`)08D+g7V45rT3-}?dYp)A#bEfHlfcI{tM2NCS17bB=Cc z=oArVEu~y#z%W}{#=W%J4^|E6^z3`j`JQ`^J-;~Td)nKj(k?8a(Yg(&rp^T~VX{Vx zDW+>WS!@$3sadsk=^rH&Epf(zWMfdx6&K>`{dwNsdx=St^X~I~ete$K@8`Ln^F5c1 z*P$EVGFBe-DpRa@>C_s-&sng>E-|+6_B(%$JkVJFYOnEk?<9XRR{nF)Sb5iHjraW{xr*^o zZ0AeHito?W{n^)7!CLn<);;I;)5N<=#`^u9ZyPTxru2Q|+tJ}48Xx&MIWLolSBhCT z87o%4*?2W}`c`A*V;hX8--z8AAHEx10waQ?uYLc9+t=K@D$N=z7VoIrpX7yL#s^Pf zYsRNb_ygu&N}Qt)x&6S$h^NL+v-iI-KJ|HWW8)l0>6L&7Cj4_@$+tf(>`(! z;|H;wSB%f!wkrM5_|zK4;Wx#TC2aqCW5u<##+Uy*rJpxGgYIoGo_Yu0rZ&F12On;{ z9b3EKcr|+X+s5l};{L>VKl1GxE3eySyyp#kN5*&xJ9^ys5c50<)*dT({y8vPNUP!7 z7v1M7dUf3GYiK`dd_DM#v0koo!TmQP$8Xel5r67F+v)Rtw@=-H&KYkfj{F2X60g=H z)9Z|to2@mz`mZVdqVdz5Wq!q2_hIig{t|Qky75uuyVZE@&BR(`LT~zA@z<2X?!?Xd}u-Q9^-lNC*5b?=9E5F_eTewG~U9P&l$heO6f1b;_ZFYe0|>ShoJ56 zjg=3dGoFUduN%LH&i|Y75#;!v;Lt;4^gXw)hCi#&Gx6;rxz=jq!+(sv8E@m+8;#GN zg@?vEi){pJk3;tLez$LdCm%50e;m4vPy8}I-1u#eq|`Om8K(kko{D)MaeMdo$#0F< zyiA^8tUJOl7+*Zg&tMxLfu7^W)6a1hFh0fnFBtzVw7qD2$0>Z2@glj?OU8Qt)62#O zx2E(1uxML=wjcYc%&YI*0)LEkUb_XXKKtnNR=3~Ce&1z$buaPG`0_FA(fCXke{QVT zdVSQmLLX+0m+&n+jJGh)g7J3Z&ZEZD?@8$~;|K48SH^GQy#I``^7*62i|p~Faa!a> zN5)5(|3%~0G&X3g{QQdX#ZBZ+#t%Nj-))Wey?s@B{k54tA0gJ-`KN1){@-VO^j}hX2rNE73ZHv!{|m{+Sa%nDjg@16(*5_-|I=>o za%rHqBx;@mv>$f+v0sBf##8u{FB|L3^%eJjg8t{+{^&=sHRJX0?LUkc@x$LUo^pQ8N{<3;cvxX*W>`Jl1xF8<6|=e}o*?^7H! z-UlE4&iK)vLMeChuf>zugi`pmbprUoY<8^<8zJWFWVdmfD_N(XNq49zHQ~IE>?so12tN&5@|CZZt zC#E-zbtm?Fb^niZr(rz*Eq?LHSm)5kjW3hqeAf6he0awABmc!2-1sT*NwC&E#=2+R zzJ#t`FxKC0zTrM6=yS#G!+qpbb)QA_&3#VM=bE3+eA~xa@;c+`_oei+#+T1?HZe&Sj5&3N^CUL9-v&;+4X&wr3#t#Z5WqaJtrTj+Dh?Yc8uG@km^l)h+u761RN@tyd_Q^wQq@nTf$yo?iM^YRb!RnWJPi%^8?WEZ*~eJ-u8q3SCeDdq@k+0`8@XNgT?@t+*zaEB zJ;anhG#;|wPZ=*g#tVFnt9N31VD04sd--d(--r)<*6kN*f8Om2@Z>AThmplU7+;k|8?X2e~OJ8uX_OA8XrR^uNptW-v8HF_jN!1Gnvn)(E01Y+Hbta%aV+4j*!t@g4u1(ua%>lhgjD@zfGLHQtDCnK#xw?a25N`m;#GyznQH$8JZtz;7&bk&hzSHK2<85zZrv05&O45*q%)~kye>9$3q?Tb#idJpKrJ7|g0IzFR(j?Y{oN0&^i>8Y#PvV2sphB!wf zHVXKEIy`PR$DLM8P!06BtTfA@-6}*Y|CKWLt5K(vCdGELg68rynmTjC(V*EY#-Uod zVr`J>k~Fc2(zFJHF@a@W8HOS)*IR{hsGN%Kxt;;Rs4!}fa_!R~xwc6(1oxtjiRMt8 zC`N)I%d86XV)Sxl5f5D)1oCUVSyeCR7O|=citVZHC#!1ctSfkEahZ-rMgeB^hU1<@5&{BPML_pzk+eI~w4X4I<(q=E^Sfl#|dqc5Vcjlg% z?WqyWh^E<5(QnT8VMu2@H!6nnY{xS&V9;&J$3{=UU|6Nby$^?kvZJSW}k^Gv0Wf zvLO+%_1ZZmqSb@=thZRSdfGyVitk+|!4LXz_%uM~sN@%pnF;NJlEz?XyDKfM zX&>)~OAXClb_FndbfKDzm`^%^JBn?z%5hUGv^sMgIK=IM%W4$}+PWaF09!knP0IiE#>?JsMY_}=MbKhP;o zO0?O1#G-_4xJiZrm7!t7N!6;*xaD0zYjZwmM5T#Ov+NdS@9h|?rkP2~YFLbU!Vrfz zWLJ#(SxI0XIZiRD<|VH1>-AF2HpJI^&2bq9%$Lm_3S|vi8c4YtEUq5TzD2*Kosq2R zUTZ;vEp{q&tRg>R74{HnwYwY-Mv_`1JU4T8=4P`3v^CybYN5kN|-0`Ga$}!tg|914K!Wm^kR2$tuWyLj$*wHpy6+RMfLGNVW^PH|IF1 z7lxG@Tg%;~)KJT>sTXjf}eIo8OWDzr1_;Yg11|5y$QLqv`$TeCGH0q-TJ9C?c> zhs|!Z)8@nh(c*qc6~ULIT76@|P(>YNh)ld73mZn+r9olGIE`^AarH23$E3sg5#|O* zRzmcQaDHY>*j19LhTbIEwJ1;i#1sCv*HudQ%o)gBg|Q7s;JRLiE%j1kD2ZE<5% zpjmB*Zj6ha43-C&fvXA&ipY&15=+GZnw1rvTu5$7{W#a@BWG`^@v%v#ta3bxQ1~NJ zavJ?X;-;=$=eLZF?<0gd@%AzuN1-UM9x+wUeNqij9oknoUx)b_^0qL%TJ%}_h{9-f z&IsE%9od>$mDBoIH^l#e*eL&x6F_QgBPS34Q;l}_6k}pp(~YK}_RDli`NXn>g)LOo z07oY`n`w$OUr5gxyrMl54Ch8zD%&di-GY;&G6>QbStvAwJygxss8Hn8DS26Au^T04 zq1JUI$kqG`b(mihWg&|V8$oIAj>UC0&AXLD+&t*@!bNt*lOf(hzJ@Q#MYxadI?)QC z%uwa{B=5jhCF)TB8BS)ih?iW7F zip&laP#8WLA-bT8jyWY($n8{9Vth`F*kyfgfL15m<6+T+zl_rLL~x0y&#ojm*((|x zaVLBLhaD0m@@Uc{QW>6M6SZ2k@gR|%$H7NSC=#M$vQWi4rk8&V=@2D5qflh)4dIV_ z6=#t7rh&mLz5}($2YVz{41>=)7)dp_l@R5qWUKahE910cs5LgNQHu5DC>w7!rN}Re zNZxX0p&-sN2gb`F`Ragulbmy2ugiK$XtMfg+7$e888Z8jIwe&)Vvnw8J-)hXX)6Xo-e)<0Z1%ujI diff --git a/gnu/usr.bin/rcs/rcsclean/rcsclean.0 b/gnu/usr.bin/rcs/rcsclean/rcsclean.0 deleted file mode 100644 index d17b4cd8c5..0000000000 --- a/gnu/usr.bin/rcs/rcsclean/rcsclean.0 +++ /dev/null @@ -1,132 +0,0 @@ - - - -RCSCLEAN(1) RCSCLEAN(1) - - -NNAAMMEE - rcsclean - clean up working files - -SSYYNNOOPPSSIISS - rrccsscclleeaann [_o_p_t_i_o_n_s] [ _f_i_l_e ... ] - -DDEESSCCRRIIPPTTIIOONN - rrccsscclleeaann removes working files that were checked out and - never modified. For each _f_i_l_e given, rrccsscclleeaann compares - the working file and a revision in the corresponding RCS - file. If it finds a difference, it does nothing. Other- - wise, it first unlocks the revision if the --uu option is - given, and then removes the working file unless the work- - ing file is writable and the revision is locked. It logs - its actions by outputting the corresponding rrccss --uu and rrmm - --ff commands on the standard output. - - If no _f_i_l_e is given, all working files in the current - directory are cleaned. Pathnames matching an RCS suffix - denote RCS files; all others denote working files. Names - are paired as explained in ccii(1). - - The number of the revision to which the working file is - compared may be attached to any of the options --nn, --qq, --rr, - or --uu. If no revision number is specified, then if the --uu - option is given and the caller has one revision locked, - rrccsscclleeaann uses that revision; otherwise rrccsscclleeaann uses the - latest revision on the default branch, normally the root. - - rrccsscclleeaann is useful for cclleeaann targets in Makefiles. See - also rrccssddiiffff(1), which prints out the differences, and - ccii(1), which normally asks whether to check in a file if - it was not changed. - -OOPPTTIIOONNSS - --kk_s_u_b_s_t - Use _s_u_b_s_t style keyword substitution when retriev- - ing the revision for comparison. See ccoo(1) for - details. - - --nn[_r_e_v] - Do not actually remove any files or unlock any - revisions. Using this option will tell you what - rrccsscclleeaann would do without actually doing it. - - --qq[_r_e_v] - Do not log the actions taken on standard output. - - --rr[_r_e_v] - This option has no effect other than specifying the - revision for comparison. - - --uu[_r_e_v] - Unlock the revision if it is locked and no - - - -GNU 1991/11/03 1 - - - - - -RCSCLEAN(1) RCSCLEAN(1) - - - difference is found. - - --VV_n Emulate RCS version _n. See ccoo(1) for details. - - --xx_s_u_f_f_i_x_e_s - Use _s_u_f_f_i_x_e_s to characterize RCS files. See ccii(1) - for details. - -EEXXAAMMPPLLEESS - rrccsscclleeaann **..cc **..hh - - removes all working files ending in ..cc or ..hh that were not - changed since their checkout. - - rrccsscclleeaann - - removes all working files in the current directory that - were not changed since their checkout. - -FFIILLEESS - rrccsscclleeaann accesses files much as ccii(1) does. - -EENNVVIIRROONNMMEENNTT - RRCCSSIINNIITT - options prepended to the argument list, separated - by spaces. A backslash escapes spaces within an - option. The RRCCSSIINNIITT options are prepended to the - argument lists of most RCS commands. Useful - RRCCSSIINNIITT options include --qq, --VV, and --xx. - -DDIIAAGGNNOOSSTTIICCSS - The exit status is zero if and only if all operations were - successful. Missing working files and RCS files are - silently ignored. - -IIDDEENNTTIIFFIICCAATTIIOONN - Author: Walter F. Tichy. - Revision Number: 1.8; Release Date: 1991/11/03. - Copyright (C) 1982, 1988, 1989 by Walter F. Tichy. - Copyright (C) 1990, 1991 by Paul Eggert. - -SSEEEE AALLSSOO - ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), - rcsmerge(1), rlog(1), rcsfile(5) - Walter F. Tichy, RCS--A System for Version Control, - _S_o_f_t_w_a_r_e_-_-_P_r_a_c_t_i_c_e _& _E_x_p_e_r_i_e_n_c_e 1155, 7 (July 1985), - 637-654. - -BBUUGGSS - At least one _f_i_l_e must be given in older Unix versions - that do not provide the needed directory scanning opera- - tions. - - - - - -GNU 1991/11/03 2 - - diff --git a/gnu/usr.bin/rcs/rcsdiff/Makefile b/gnu/usr.bin/rcs/rcsdiff/Makefile index 837c241dbc..070e231bf7 100644 --- a/gnu/usr.bin/rcs/rcsdiff/Makefile +++ b/gnu/usr.bin/rcs/rcsdiff/Makefile @@ -1,7 +1,8 @@ -PROG= rcsdiff - +PROG= rcsdiff SRCS= rcsdiff.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} +.include "../../Makefile.inc" .include diff --git a/gnu/usr.bin/rcs/rcsfreeze/Makefile b/gnu/usr.bin/rcs/rcsfreeze/Makefile index 825d4bffec..c1b48417ea 100644 --- a/gnu/usr.bin/rcs/rcsfreeze/Makefile +++ b/gnu/usr.bin/rcs/rcsfreeze/Makefile @@ -1,7 +1,8 @@ -# Do nothing for the following -obj clean cleandir depend rcsfreeze all: - @echo No need to make $@ for rcsfreeze\; ignored +MAN1= rcsfreeze.1 -install: - install -c -o bin -g bin -m 555 rcsfreeze.sh /usr/bin/rcsfreeze - install -c -o bin -g bin -m 444 rcsfreeze.1 /usr/share/man/man1 +afterinstall: + install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + ${.CURDIR}/rcsfreeze.sh ${DESTDIR}${BINDIR}/rcsfreeze + +.include "../../Makefile.inc" +.include diff --git a/gnu/usr.bin/rcs/rcsmerge/Makefile b/gnu/usr.bin/rcs/rcsmerge/Makefile index 0c1f643891..801b01ee69 100644 --- a/gnu/usr.bin/rcs/rcsmerge/Makefile +++ b/gnu/usr.bin/rcs/rcsmerge/Makefile @@ -1,7 +1,8 @@ -PROG= rcsmerge - +PROG= rcsmerge SRCS= rcsmerge.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} +.include "../../Makefile.inc" .include diff --git a/gnu/usr.bin/rcs/rlog/Makefile b/gnu/usr.bin/rcs/rlog/Makefile index b6a126865a..a1d19aad82 100644 --- a/gnu/usr.bin/rcs/rlog/Makefile +++ b/gnu/usr.bin/rcs/rlog/Makefile @@ -1,7 +1,8 @@ -PROG= rlog - +PROG= rlog SRCS= rlog.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} +.include "../../Makefile.inc" .include diff --git a/gnu/usr.bin/rcs/rlog/rlog.c b/gnu/usr.bin/rcs/rlog/rlog.c index b18b0c97ce..dbcf2d4141 100644 --- a/gnu/usr.bin/rcs/rlog/rlog.c +++ b/gnu/usr.bin/rcs/rlog/rlog.c @@ -36,6 +36,9 @@ Report problems and direct all questions to: /* $Log: rlog.c,v $ + * Revision 1.1.1.1 1993/06/18 04:22:17 jkh + * Updated GNU utilities + * * Revision 5.9 1991/09/17 19:07:40 eggert * Getscript() didn't uncache partial lines. * @@ -191,7 +194,7 @@ static struct lockers *lockerlist; static struct stateattri *statelist; -mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.9 1991/09/17 19:07:40 eggert Exp $") +mainProg(rlogId, "rlog", "$Id: rlog.c,v 1.1.1.1 1993/06/18 04:22:17 jkh Exp $") { static char const cmdusage[] = "\nrlog usage: rlog -{bhLRt} -ddates -l[lockers] -rrevs -sstates -w[logins] -Vn file ..."; @@ -402,6 +405,7 @@ mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.9 1991/09/17 19:07:40 eggert Exp $") putrunk(); putree(Head); } + aputs("----------------------------\n", out); aputs("=============================================================================\n",out); } while (cleanup(), ++argv, --argc >= 1); diff --git a/gnu/usr.bin/sdiff/Makefile b/gnu/usr.bin/sdiff/Makefile new file mode 100644 index 0000000000..d2976c928f --- /dev/null +++ b/gnu/usr.bin/sdiff/Makefile @@ -0,0 +1,11 @@ +PROG= sdiff +SRCS= sdiff.c getopt.c getopt1.c version.c +CFLAGS+= -I$(.CURDIR)/../diff\ + -DDIRENT=1 -DHAVE_UNISTD_H=1 -DHAVE_DUP2=1 -DHAVE_MEMCHR=1\ + -DHAVE_STRERROR=1 -DHAVE_WAITPID=1 -DHAVE_FCNTL_H=1\ + -DHAVE_STRING_H=1 -DHAVE_SYS_WAIT_H=1 -DHAVE_TIME_H=1\ + -DHAVE_ST_BLKSIZE=1 -DDIFF_PROGRAM=\"/usr/bin/diff\" +NOMAN= noman + +.include +.PATH: $(.CURDIR)/../diff diff --git a/gnu/usr.bin/sort/system.h b/gnu/usr.bin/sort/system.h index 2e03ea8af1..d26022dde7 100644 --- a/gnu/usr.bin/sort/system.h +++ b/gnu/usr.bin/sort/system.h @@ -80,8 +80,10 @@ off_t lseek (); #endif #else #include +#ifndef __386BSD__ char *memchr (); #endif +#endif #include #ifdef STDC_HEADERS diff --git a/gnu/usr.bin/tar/Makefile b/gnu/usr.bin/tar/Makefile index 810fe3b7a7..69a1f52cf9 100644 --- a/gnu/usr.bin/tar/Makefile +++ b/gnu/usr.bin/tar/Makefile @@ -8,6 +8,7 @@ CFLAGS+= -DHAVE_LIMITS_H=1 -DHAVE_STRSTR=1 -DHAVE_VALLOC=1 -DHAVE_MKDIR=1 CFLAGS+= -DHAVE_MKNOD=1 -DHAVE_RENAME=1 -DHAVE_FTRUNCATE=1 -DHAVE_GETCWD=1 CFLAGS+= -DHAVE_VPRINTF=1 -DNEEDPAD -I${.CURDIR} CFLAGS+= -DDEF_AR_FILE=\"/dev/rst0\" -DDEFBLOCKING=20 +CLEANFILES+=y.tab.h NOMAN=noman .include diff --git a/gnu/usr.bin/tar/y.tab.h b/gnu/usr.bin/tar/y.tab.h deleted file mode 100644 index 4a541d2c97..0000000000 --- a/gnu/usr.bin/tar/y.tab.h +++ /dev/null @@ -1,18 +0,0 @@ -#define tAGO 257 -#define tDAY 258 -#define tDAYZONE 259 -#define tID 260 -#define tMERIDIAN 261 -#define tMINUTE_UNIT 262 -#define tMONTH 263 -#define tMONTH_UNIT 264 -#define tSEC_UNIT 265 -#define tSNUMBER 266 -#define tUNUMBER 267 -#define tZONE 268 -#define tDST 269 -typedef union { - time_t Number; - enum _MERIDIAN Meridian; -} YYSTYPE; -extern YYSTYPE yylval; diff --git a/include/Makefile b/include/Makefile index b854a00fe0..eb10d2f3c1 100644 --- a/include/Makefile +++ b/include/Makefile @@ -1,33 +1,27 @@ # @(#)Makefile 5.45.1.1 (Berkeley) 5/6/91 # -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 1 00123 -# -------------------- ----- ---------------------- -# -# 04 Apr 93 Rodney W. Grimes Removed added netccitt and vm -# -# Doing a make install builds /usr/include -# # The ``rm -rf''s used below are safe because rm doesn't follow symbolic # links. # -all clean cleandir depend lint tags: +all depend lint tags: + +SUBDIR= rpcsvc # Missing: mp.h FILES= a.out.h ar.h assert.h bitstring.h ctype.h db.h dirent.h disktab.h \ - fstab.h fts.h glob.h grp.h kvm.h limits.h locale.h math.h memory.h \ - ndbm.h netdb.h nlist.h paths.h pwd.h ranlib.h regexp.h \ - resolv.h setjmp.h sgtty.h stab.h stdarg.h stddef.h stdio.h \ - stdlib.h string.h strings.h struct.h sysexits.h time.h ttyent.h \ - tzfile.h unistd.h utime.h utmp.h varargs.h vis.h + err.h fnmatch.h fstab.h fts.h glob.h grp.h kvm.h limits.h locale.h \ + math.h memory.h mpool.h ndbm.h netdb.h nlist.h paths.h pwd.h \ + ranlib.h regexp.h resolv.h setjmp.h sgtty.h stab.h stdarg.h \ + stddef.h stdio.h stdlib.h string.h strings.h struct.h sysexits.h \ + time.h ttyent.h tzfile.h unistd.h utime.h utmp.h varargs.h vis.h -MFILES= float.h frame.h -LFILES= errno.h fcntl.h signal.h syslog.h termios.h +MFILES= float.h floatingpoint.h frame.h +LFILES= errno.h fcntl.h signal.h syslog.h syscall.h termios.h DIRS=arpa protocols -LDIRS= net netccitt netimp netinet netiso netns nfs sys ufs vm +LDIRS= net netccitt netinet netiso netns nfs sys ufs vm +# removed netimp depricated? NOOBJ= noobj @@ -35,9 +29,9 @@ NOOBJ= noobj # source (``symlinks''), or a separate copy (``copies''); (latter useful # in environments where it's not possible to keep /sys publicly readable) # SHARED= copies -SHARED= symlinks +SHARED?= symlinks -install: ${SHARED} +realinstall: ${SHARED} @echo installing ${FILES} @-for i in ${FILES}; do \ cmp -s $$i ${DESTDIR}/usr/include/$$i || \ @@ -77,11 +71,17 @@ copies: rm -rf ${DESTDIR}/usr/include/$$i; \ cd /sys; \ tar cf - $$i/*.h | (cd ${DESTDIR}/usr/include; tar xpfB -); \ + chown -R ${BINOWN}.${BINGRP} ${DESTDIR}/usr/include/$$i; \ + chmod -R 444 ${DESTDIR}/usr/include/$$i; \ + chmod 755 ${DESTDIR}/usr/include/$$i; \ done - rm -f ${DESTDIR}/usr/include/machine + rm -rf ${DESTDIR}/usr/include/machine mkdir ${DESTDIR}/usr/include/machine cd /sys/${MACHINE}/include; \ - tar cf - *.h | (cd ${DESTDIR}/usr/include/machine; tar xpfB -); \ + tar cf - *.h | (cd ${DESTDIR}/usr/include/machine; tar xpfB -); + chown -R ${BINOWN}.${BINGRP} ${DESTDIR}/usr/include/machine; + chmod -R 444 ${DESTDIR}/usr/include/machine; + chmod 755 ${DESTDIR}/usr/include/machine; symlinks: @echo symlinks: ${LDIRS} @@ -90,7 +90,7 @@ symlinks: ln -s /sys/$$i ${DESTDIR}/usr/include/$$i; \ chown ${BINOWN}.${BINGRP} ${DESTDIR}/usr/include/$$i; \ done - rm -f ${DESTDIR}/usr/include/machine + rm -rf ${DESTDIR}/usr/include/machine ln -s /sys/${MACHINE}/include ${DESTDIR}/usr/include/machine chown ${BINOWN}.${BINGRP} ${DESTDIR}/usr/include/machine diff --git a/include/a.out.h b/include/a.out.h index 1008155192..9133f81bc3 100644 --- a/include/a.out.h +++ b/include/a.out.h @@ -45,32 +45,38 @@ #define __LDPGSZ 1024 #endif +#define N_ALIGN(ex,x) \ + ((ex).a_magic == ZMAGIC || (ex).a_magic == QMAGIC ? \ + ((x) + __LDPGSZ - 1) & ~(__LDPGSZ - 1) : (x)) + /* Valid magic number check. */ #define N_BADMAG(ex) \ ((ex).a_magic != NMAGIC && (ex).a_magic != OMAGIC && \ - (ex).a_magic != ZMAGIC) + (ex).a_magic != ZMAGIC && (ex).a_magic != QMAGIC) /* Address of the bottom of the text segment. */ -#define N_TXTADDR(X) 0 +#define N_TXTADDR(ex) ((ex).a_magic == QMAGIC ? __LDPGSZ : 0) /* Address of the bottom of the data segment. */ #define N_DATADDR(ex) \ - (N_TXTADDR(ex) + ((ex).a_magic == OMAGIC ? (ex).a_text \ - : __LDPGSZ + ((ex).a_text - 1 & ~(__LDPGSZ - 1)))) + N_ALIGN(ex, N_TXTADDR(ex) + (ex).a_text) /* Text segment offset. */ #define N_TXTOFF(ex) \ - ((ex).a_magic == ZMAGIC ? __LDPGSZ : sizeof(struct exec)) + ((ex).a_magic == ZMAGIC ? __LDPGSZ \ + : ((ex).a_magic == QMAGIC ? 0 : sizeof(struct exec))) /* Data segment offset. */ #define N_DATOFF(ex) \ - (N_TXTOFF(ex) + ((ex).a_magic != ZMAGIC ? (ex).a_text \ - : __LDPGSZ + ((ex).a_text - 1 & ~(__LDPGSZ - 1)))) + N_ALIGN(ex, N_TXTOFF(ex) + (ex).a_text) + +/* Relocation table offset. */ +#define N_RELOFF(ex) \ + N_ALIGN(ex, N_DATOFF(ex) + (ex).a_data) /* Symbol table offset. */ #define N_SYMOFF(ex) \ - (N_TXTOFF(ex) + (ex).a_text + (ex).a_data + (ex).a_trsize + \ - (ex).a_drsize) + (N_RELOFF(ex) + (ex).a_trsize + (ex).a_drsize) /* String table offset. */ #define N_STROFF(ex) (N_SYMOFF(ex) + (ex).a_syms) diff --git a/include/arpa/inet.h b/include/arpa/inet.h index cfe2a2f1bf..6aec760c4d 100644 --- a/include/arpa/inet.h +++ b/include/arpa/inet.h @@ -42,9 +42,9 @@ __BEGIN_DECLS extern unsigned long inet_addr __P((const char *)); +extern unsigned long inet_aton __P((const char *, struct in_addr *)); extern unsigned long inet_lnaof __P((struct in_addr)); extern struct in_addr inet_makeaddr __P((u_long , u_long)); -extern unsigned long inet_netof __P((struct in_addr)); extern unsigned long inet_network __P((const char *)); extern char *inet_ntoa __P((struct in_addr)); __END_DECLS diff --git a/include/arpa/nameser.h b/include/arpa/nameser.h index 84ed990919..158c4ab5a7 100644 --- a/include/arpa/nameser.h +++ b/include/arpa/nameser.h @@ -36,6 +36,8 @@ #ifndef _NAMESER_H_ #define _NAMESER_H_ +#include + /* * Define constants based on rfc883 */ @@ -106,6 +108,7 @@ #define T_UID 101 /* user ID */ #define T_GID 102 /* group ID */ #define T_UNSPEC 103 /* Unspecified format (binary data) */ +#define T_UNSPECA 104 /* Unspecified format (ASCII) */ /* Query type values which do not appear in resource records */ #define T_AXFR 252 /* transfer zone of authority */ #define T_MAILB 253 /* transfer mailbox records */ @@ -131,27 +134,6 @@ #define CONV_BADCKSUM -3 #define CONV_BADBUFLEN -4 -#ifndef BYTE_ORDER -#define LITTLE_ENDIAN 1234 /* least-significant byte first (vax) */ -#define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */ -#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp) */ - -#if defined(vax) || defined(ns32000) || defined(sun386) || defined(MIPSEL) || \ - defined(BIT_ZERO_ON_RIGHT) -#define BYTE_ORDER LITTLE_ENDIAN - -#endif -#if defined(sel) || defined(pyr) || defined(mc68000) || defined(sparc) || \ - defined(is68k) || defined(tahoe) || defined(ibm032) || defined(ibm370) || \ - defined(MIPSEB) || defined (BIT_ZERO_ON_LEFT) -#define BYTE_ORDER BIG_ENDIAN -#endif -#endif /* BYTE_ORDER */ - -#ifndef BYTE_ORDER - /* you must determine what the correct bit order is for your compiler */ - UNDEFINED_BIT_ORDER; -#endif /* * Structure for query header, the order of the fields is machine and * compiler dependent, in our case, the bits within a byte are assignd diff --git a/include/assert.h b/include/assert.h index 2c2c5be078..91bed604f4 100644 --- a/include/assert.h +++ b/include/assert.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 1990 The Regents of the University of California. + * Copyright (c) 1992 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,25 +30,30 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)assert.h 4.4 (Berkeley) 4/3/91 + * @(#)assert.h 5.2 (Berkeley) 6/18/92 */ -#ifndef _ASSERT_H_ -#define _ASSERT_H_ +/* + * Unlike other ANSI header files, may usefully be included + * multiple times, with and without NDEBUG defined. + */ + +#undef assert #ifdef NDEBUG -#define assert -#define _assert +#define assert(e) ((void)0) +#define _assert(e) ((void)0) #else -#define assert(expression) { \ - if (!(expression)) { \ - (void)fprintf(stderr, \ - "assertion \"%s\" failed: file \"%s\", line %d\n", \ - "expression", __FILE__, __LINE__); \ - exit(2); \ - } \ -} -#define _assert(expression) assert(expression) +#define _assert(e) assert(e) +#ifdef __STDC__ +#define assert(e) ((e) ? (void)0 : __assert(__FILE__, __LINE__, #e)) +#else /* PCC */ +#define assert(e) ((e) ? (void)0 : __assert(__FILE__, __LINE__, "e")) #endif +#endif + +#include -#endif /* !_ASSERT_H_ */ +__BEGIN_DECLS +void __assert __P((const char *, int, const char *)); +__END_DECLS diff --git a/include/ctype.h b/include/ctype.h index 880c4d3885..b7d292c3e6 100644 --- a/include/ctype.h +++ b/include/ctype.h @@ -36,6 +36,8 @@ #ifndef _CTYPE_H_ #define _CTYPE_H_ +#include + #define _U 0x01 #define _L 0x02 #define _N 0x04 @@ -47,6 +49,22 @@ extern char _ctype_[]; +__BEGIN_DECLS +int isdigit __P((int)); +int islower __P((int)); +int isspace __P((int)); +int ispunkt __P((int)); +int isupper __P((int)); +int isalpha __P((int)); +int isxdigit __P((int)); +int isalnum __P((int)); +int isprint __P((int)); +int isgraph __P((int)); +int iscntrl __P((int)); +int toupper __P((int)); +int tolower __P((int)); +__END_DECLS + #define isdigit(c) ((_ctype_ + 1)[c] & _N) #define islower(c) ((_ctype_ + 1)[c] & _L) #define isspace(c) ((_ctype_ + 1)[c] & _S) @@ -59,8 +77,6 @@ extern char _ctype_[]; #define isgraph(c) ((_ctype_ + 1)[c] & (_P|_U|_L|_N)) #define iscntrl(c) ((_ctype_ + 1)[c] & _C) #define isascii(c) ((unsigned)(c) <= 0177) -#define toupper(c) ((c) - 'a' + 'A') -#define tolower(c) ((c) - 'A' + 'a') #define toascii(c) ((c) & 0177) #endif /* !_CTYPE_H_ */ diff --git a/include/db.h b/include/db.h index 6a30111d53..2b7de273a5 100644 --- a/include/db.h +++ b/include/db.h @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,119 +30,165 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)db.h 5.10 (Berkeley) 4/2/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00093 - * -------------------- ----- ---------------------- - * - * 27 Feb 93 Charles Hannum Better byte-swapping macros for - * i386/i486. + * @(#)db.h 8.1 (Berkeley) 6/2/93 */ #ifndef _DB_H_ #define _DB_H_ -#include +#include #include +#include -/* flags for DB.put() call */ -#define R_IBEFORE 1 /* RECNO */ -#define R_IAFTER 2 /* RECNO */ -#define R_NOOVERWRITE 3 /* BTREE, HASH, RECNO */ -#define R_PUT 4 /* BTREE, HASH, RECNO */ +#define RET_ERROR -1 /* Return values. */ +#define RET_SUCCESS 0 +#define RET_SPECIAL 1 -/* flags for DB.seq() call */ -#define R_CURSOR 1 /* BTREE, RECNO */ -#define R_FIRST 2 /* BTREE, HASH, RECNO */ -#define R_LAST 3 /* BTREE, RECNO */ -#define R_NEXT 4 /* BTREE, HASH, RECNO */ -#define R_PREV 5 /* BTREE, RECNO */ +#define MAX_PAGE_NUMBER ULONG_MAX /* >= # of pages in a file */ +typedef u_long pgno_t; +#define MAX_PAGE_OFFSET USHRT_MAX /* >= # of bytes in a page */ +typedef u_short indx_t; +#define MAX_REC_NUMBER ULONG_MAX /* >= # of records in a tree */ +typedef u_long recno_t; -/* key/data structure -- a data-base thang */ +/* Key/data structure -- a Data-Base Thang. */ typedef struct { - void *data; - int size; + void *data; /* data */ + size_t size; /* data length */ } DBT; -/* access method description structure */ +/* Routine flags. */ +#define R_CURSOR 1 /* del, put, seq */ +#define __R_UNUSED 2 /* UNUSED */ +#define R_FIRST 3 /* seq */ +#define R_IAFTER 4 /* put (RECNO) */ +#define R_IBEFORE 5 /* put (RECNO) */ +#define R_LAST 6 /* seq (BTREE, RECNO) */ +#define R_NEXT 7 /* seq */ +#define R_NOOVERWRITE 8 /* put */ +#define R_PREV 9 /* seq (BTREE, RECNO) */ +#define R_SETCURSOR 10 /* put (RECNO) */ +#define R_RECNOSYNC 11 /* sync (RECNO) */ + +typedef enum { DB_BTREE, DB_HASH, DB_RECNO } DBTYPE; + +#define __USE_OPEN_FLAGS \ + (O_CREAT|O_EXCL|O_EXLOCK|O_RDONLY|O_RDWR|O_SHLOCK|O_TRUNC) + +/* Access method description structure. */ typedef struct __db { - void *internal; /* access method private */ -#define DB_BTREE 1 -#define DB_HASH 2 -#define DB_RECNO 3 - int type; /* type of underlying db */ - int (*close) __P((const struct __db *)); - int (*del) __P((const struct __db *, const DBT *, unsigned int)); - int (*get) __P((const struct __db *, DBT *, DBT *, unsigned int)); - int (*put) __P((const struct __db *, const DBT *, const DBT *, - unsigned int)); - int (*seq) __P((const struct __db *, DBT *, DBT *, unsigned int)); - int (*sync) __P((const struct __db *)); + DBTYPE type; /* underlying db type */ + int (*close) __P((struct __db *)); + int (*del) __P((const struct __db *, const DBT *, u_int)); + int (*fd) __P((const struct __db *)); + int (*get) __P((const struct __db *, const DBT *, DBT *, u_int)); + int (*put) __P((const struct __db *, DBT *, const DBT *, u_int)); + int (*seq) __P((const struct __db *, DBT *, DBT *, u_int)); + int (*sync) __P((const struct __db *, u_int)); + void *internal; /* access method private */ } DB; #define BTREEMAGIC 0x053162 -#define BTREEVERSION 2 +#define BTREEVERSION 3 -/* structure used to pass parameters to the btree routines */ +/* Structure used to pass parameters to the btree routines. */ typedef struct { #define R_DUP 0x01 /* duplicate keys */ - u_long flags; - int cachesize; /* bytes to cache */ - int psize; /* page size */ - int (*compare)(); /* compare function */ - int lorder; /* byte order */ + u_long flags; + int cachesize; /* bytes to cache */ + int maxkeypage; /* maximum keys per page */ + int minkeypage; /* minimum keys per page */ + int psize; /* page size */ + /* comparison, prefix functions */ + int (*compare) __P((const DBT *, const DBT *)); + int (*prefix) __P((const DBT *, const DBT *)); + int lorder; /* byte order */ } BTREEINFO; #define HASHMAGIC 0x061561 -#define HASHVERSION 1 +#define HASHVERSION 2 -/* structure used to pass parameters to the hashing routines */ +/* Structure used to pass parameters to the hashing routines. */ typedef struct { - int bsize; /* bucket size */ - int ffactor; /* fill factor */ - int nelem; /* number of elements */ - int cachesize; /* bytes to cache */ - int (*hash)(); /* hash function */ - int lorder; /* byte order */ + int bsize; /* bucket size */ + int ffactor; /* fill factor */ + int nelem; /* number of elements */ + int cachesize; /* bytes to cache */ + /* hash function */ + int (*hash) __P((const void *, size_t)); + int lorder; /* byte order */ } HASHINFO; -/* structure used to pass parameters to the record routines */ +/* Structure used to pass parameters to the record routines. */ typedef struct { #define R_FIXEDLEN 0x01 /* fixed-length records */ - u_long flags; - int cachesize; /* bytes to cache */ - size_t reclen; /* record length (fixed-length records) */ - u_char bval; /* delimiting byte (variable-length records */ +#define R_NOKEY 0x02 /* key not required */ +#define R_SNAPSHOT 0x04 /* snapshot the input */ + u_long flags; + int cachesize; /* bytes to cache */ + int psize; /* page size */ + int lorder; /* byte order */ + size_t reclen; /* record length (fixed-length records) */ + u_char bval; /* delimiting byte (variable-length records */ + char *bfname; /* btree file name */ } RECNOINFO; -/* key structure for the record routines */ -typedef struct { - u_long number; - u_long offset; - u_long length; -#define R_LENGTH 0x01 /* length is valid */ -#define R_NUMBER 0x02 /* record number is valid */ -#define R_OFFSET 0x04 /* offset is valid */ - u_char valid; -} RECNOKEY; - -/* Little endian <--> big endian long swap macros. */ -#define BLSWAP(X) {(X) = __byte_swap_long(X);} -#define BLSWAP_COPY(X,Y) {(Y) = __byte_swap_long(X);} - -/* Little endian <--> big endian short swap macros. */ -#define BSSWAP(X) {(X) = __byte_swap_word(X);} -#define BSSWAP_COPY(X,Y) {(Y) = __byte_swap_word(X);} +/* + * Little endian <==> big endian long swap macros. + * BLSWAP swap a memory location + * BLPSWAP swap a referenced memory location + * BLSWAP_COPY swap from one location to another + */ +#define BLSWAP(a) { \ + u_long _tmp = a; \ + ((char *)&a)[0] = ((char *)&_tmp)[3]; \ + ((char *)&a)[1] = ((char *)&_tmp)[2]; \ + ((char *)&a)[2] = ((char *)&_tmp)[1]; \ + ((char *)&a)[3] = ((char *)&_tmp)[0]; \ +} +#define BLPSWAP(a) { \ + u_long _tmp = *(u_long *)a; \ + ((char *)a)[0] = ((char *)&_tmp)[3]; \ + ((char *)a)[1] = ((char *)&_tmp)[2]; \ + ((char *)a)[2] = ((char *)&_tmp)[1]; \ + ((char *)a)[3] = ((char *)&_tmp)[0]; \ +} +#define BLSWAP_COPY(a, b) { \ + ((char *)&(b))[0] = ((char *)&(a))[3]; \ + ((char *)&(b))[1] = ((char *)&(a))[2]; \ + ((char *)&(b))[2] = ((char *)&(a))[1]; \ + ((char *)&(b))[3] = ((char *)&(a))[0]; \ +} + +/* + * Little endian <==> big endian short swap macros. + * BSSWAP swap a memory location + * BSPSWAP swap a referenced memory location + * BSSWAP_COPY swap from one location to another + */ +#define BSSWAP(a) { \ + u_short _tmp = a; \ + ((char *)&a)[0] = ((char *)&_tmp)[1]; \ + ((char *)&a)[1] = ((char *)&_tmp)[0]; \ +} +#define BSPSWAP(a) { \ + u_short _tmp = *(u_short *)a; \ + ((char *)a)[0] = ((char *)&_tmp)[1]; \ + ((char *)a)[1] = ((char *)&_tmp)[0]; \ +} +#define BSSWAP_COPY(a, b) { \ + ((char *)&(b))[0] = ((char *)&(a))[1]; \ + ((char *)&(b))[1] = ((char *)&(a))[0]; \ +} __BEGIN_DECLS -DB *btree_open - __P((const char *, int, int, const BTREEINFO *)); -DB *hash_open - __P((const char *, int, int, const HASHINFO *)); -DB *recno_open - __P((const char *, int, int, const RECNOINFO *)); +DB *dbopen __P((const char *, int, int, DBTYPE, const void *)); + +#ifdef __DBINTERFACE_PRIVATE +DB *__bt_open __P((const char *, int, int, const BTREEINFO *)); +DB *__hash_open __P((const char *, int, int, const HASHINFO *)); +DB *__rec_open __P((const char *, int, int, const RECNOINFO *)); +void __dbpanic __P((DB *dbp)); +#endif __END_DECLS - #endif /* !_DB_H_ */ diff --git a/include/dirent.h b/include/dirent.h index fb42a44932..e3bf09bdab 100644 --- a/include/dirent.h +++ b/include/dirent.h @@ -73,6 +73,7 @@ typedef struct _dirdesc { char *dd_buf; /* data buffer */ int dd_len; /* size of data buffer */ long dd_seek; /* magic cookie returned by getdirentries */ + void *dd_ddloc; /* Linked list of ddloc structs for telldir/seekdir */ } DIR; #define dirfd(dirp) ((dirp)->dd_fd) diff --git a/include/err.h b/include/err.h new file mode 100644 index 0000000000..ba743c5727 --- /dev/null +++ b/include/err.h @@ -0,0 +1,62 @@ +/*- + * Copyright (c) 1993 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)err.h 5.1 (Berkeley) 3/4/93 + */ + +#ifndef _ERR_H_ +#define _ERR_H_ + +/* + * Don't use va_list in the err/warn prototypes. Va_list is typedef'd in two + * places ( and ), so if we include one + * of them here we may collide with the utility's includes. It's unreasonable + * for utilities to have to include one of them to include err.h, so we get + * _BSD_VA_LIST_ from and use it. + */ +/* s/_BSD_VA_LIST_/_VA_LIST_ to avoid major changes at this time */ + +#include +#include + +__BEGIN_DECLS +volatile void err __P((int, const char *, ...)); +volatile void verr __P((int, const char *, _VA_LIST_)); +volatile void errx __P((int, const char *, ...)); +volatile void verrx __P((int, const char *, _VA_LIST_)); +void warn __P((const char *, ...)); +void vwarn __P((const char *, _VA_LIST_)); +void warnx __P((const char *, ...)); +void vwarnx __P((const char *, _VA_LIST_)); +__END_DECLS + +#endif /* !_ERR_H_ */ diff --git a/include/fnmatch.h b/include/fnmatch.h new file mode 100644 index 0000000000..9701b5090c --- /dev/null +++ b/include/fnmatch.h @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)fnmatch.h 5.1 (Berkeley) 6/28/92 + */ + +#ifndef _FNMATCH_H_ +#define _FNMATCH_H_ + +#define FNM_NOMATCH 1 /* Match failed. */ + +#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */ +#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */ +#define FNM_PERIOD 0x04 /* Period must be matched by period. */ + +#include + +__BEGIN_DECLS +int fnmatch __P((const char *, const char *, int)); +__END_DECLS + +#endif /* !_FNMATCH_H_ */ diff --git a/include/fts.h b/include/fts.h index b7af5d5b61..84dbe14bc7 100644 --- a/include/fts.h +++ b/include/fts.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)fts.h 5.14 (Berkeley) 4/3/91 + * @(#)fts.h 8.1 (Berkeley) 6/2/93 */ #ifndef _FTS_H_ @@ -39,40 +39,44 @@ typedef struct { struct _ftsent *fts_cur; /* current node */ struct _ftsent *fts_child; /* linked list of children */ - struct _ftsent *fts_savelink; /* saved link if node had a cycle */ struct _ftsent **fts_array; /* sort array */ - dev_t rdev; /* starting device # */ + dev_t fts_dev; /* starting device # */ char *fts_path; /* path for this descent */ - int fts_dfd; /* fd for directories */ int fts_rfd; /* fd for root */ int fts_pathlen; /* sizeof(path) */ int fts_nitems; /* elements in the sort array */ int (*fts_compar)(); /* compare function */ -#define FTS_LOGICAL 0x001 /* logical walk */ -#define FTS_NOCHDIR 0x002 /* don't change directories */ -#define FTS_NOSTAT 0x004 /* don't get stat info */ -#define FTS_PHYSICAL 0x008 /* physical walk */ -#define FTS_SEEDOT 0x010 /* return dot and dot-dot */ -#define FTS_STOP 0x020 /* (private) unrecoverable error */ +#define FTS_COMFOLLOW 0x001 /* follow command line symlinks */ +#define FTS_LOGICAL 0x002 /* logical walk */ +#define FTS_NOCHDIR 0x004 /* don't change directories */ +#define FTS_NOSTAT 0x008 /* don't get stat info */ +#define FTS_PHYSICAL 0x010 /* physical walk */ +#define FTS_SEEDOT 0x020 /* return dot and dot-dot */ #define FTS_XDEV 0x040 /* don't cross devices */ - int fts_options; /* openfts() options */ +#define FTS_OPTIONMASK 0x07f /* valid user option mask */ + +#define FTS_NAMEONLY 0x080 /* (private) child names only */ +#define FTS_STOP 0x100 /* (private) unrecoverable error */ + int fts_options; /* fts_open options, global flags */ } FTS; typedef struct _ftsent { + struct _ftsent *fts_cycle; /* cycle node */ struct _ftsent *fts_parent; /* parent directory */ - struct _ftsent *fts_link; /* cycle or next file structure */ - union { - long number; /* local numeric value */ - void *pointer; /* local address value */ - } fts_local; -#define fts_number fts_local.number -#define fts_pointer fts_local.pointer + struct _ftsent *fts_link; /* next file in directory */ + long fts_number; /* local numeric value */ + void *fts_pointer; /* local address value */ char *fts_accpath; /* access path */ char *fts_path; /* root path */ - int fts_cderr; /* chdir failed -- errno */ - short fts_pathlen; /* strlen(fts_path) */ - short fts_namelen; /* strlen(fts_name) */ + int fts_errno; /* errno for this node */ + int fts_symfd; /* fd for symlink */ + u_short fts_pathlen; /* strlen(fts_path) */ + u_short fts_namelen; /* strlen(fts_name) */ + + ino_t fts_ino; /* inode */ + dev_t fts_dev; /* device */ + nlink_t fts_nlink; /* link count */ #define FTS_ROOTPARENTLEVEL -1 #define FTS_ROOTLEVEL 0 @@ -82,32 +86,38 @@ typedef struct _ftsent { #define FTS_DC 2 /* directory that causes cycles */ #define FTS_DEFAULT 3 /* none of the above */ #define FTS_DNR 4 /* unreadable directory */ -#define FTS_DP 5 /* postorder directory */ -#define FTS_ERR 6 /* error; errno is set */ -#define FTS_F 7 /* regular file */ -#define FTS_NS 8 /* stat(2) failed */ -#define FTS_NSOK 9 /* no stat(2) requested */ -#define FTS_SL 10 /* symbolic link */ -#define FTS_SLNONE 11 /* symbolic link without target */ +#define FTS_DOT 5 /* dot or dot-dot */ +#define FTS_DP 6 /* postorder directory */ +#define FTS_ERR 7 /* error; errno is set */ +#define FTS_F 8 /* regular file */ +#define FTS_INIT 9 /* initialized only */ +#define FTS_NS 10 /* stat(2) failed */ +#define FTS_NSOK 11 /* no stat(2) requested */ +#define FTS_SL 12 /* symbolic link */ +#define FTS_SLNONE 13 /* symbolic link without target */ u_short fts_info; /* user flags for FTSENT structure */ +#define FTS_DONTCHDIR 0x01 /* don't chdir .. to the parent */ +#define FTS_SYMFOLLOW 0x02 /* followed a symlink to get here */ + u_short fts_flags; /* private flags for FTSENT structure */ + #define FTS_AGAIN 1 /* read node again */ #define FTS_FOLLOW 2 /* follow symbolic link */ #define FTS_NOINSTR 3 /* no instructions */ #define FTS_SKIP 4 /* discard node */ u_short fts_instr; /* fts_set() instructions */ - struct stat fts_statb; /* stat(2) information */ + struct stat *fts_statp; /* stat(2) information */ char fts_name[1]; /* file name */ } FTSENT; #include __BEGIN_DECLS -FTSENT *fts_children __P((FTS *)); +FTSENT *fts_children __P((FTS *, int)); int fts_close __P((FTS *)); -FTS *fts_open - __P((char * const *, int, int (*)(const FTSENT *, const FTSENT *))); +FTS *fts_open __P((char * const *, int, + int (*)(const FTSENT **, const FTSENT **))); FTSENT *fts_read __P((FTS *)); int fts_set __P((FTS *, FTSENT *, int)); __END_DECLS diff --git a/include/glob.h b/include/glob.h index 4bc13208ce..cedabb23f8 100644 --- a/include/glob.h +++ b/include/glob.h @@ -33,7 +33,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)glob.h 5.6 (Berkeley) 4/3/91 + * @(#)glob.h 5.8 (Berkeley) 12/2/92 */ #ifndef _GLOB_H_ @@ -45,20 +45,26 @@ typedef struct { int gl_offs; /* reserved at beginning of gl_pathv */ int gl_flags; /* copy of flags parameter to glob() */ int (*gl_errfunc)(); /* copy of errfunc parameter to glob() */ + void *(*gl_opendir)(); /* alternate opendir() function for glob() */ + struct dirent *(*gl_readdir)(); /* alternate readdir() function */ + void (*gl_closedir)(); /* alternate closedir() function for glob() */ + int (*gl_lstat)(); /* alternate lstat() function for glob() */ + int (*gl_stat)(); /* alternate stat() function for glob() */ char **gl_pathv; /* list of paths matching pattern */ } glob_t; -#define GLOB_APPEND 0x01 /* append to output from previous call */ -#define GLOB_DOOFFS 0x02 /* use gl_offs */ -#define GLOB_ERR 0x04 /* return on error */ -#ifndef _POSIX_SOURCE -#define GLOB_MAGCHAR 0x08 /* pattern had globbing characters */ -#endif -#define GLOB_MARK 0x10 /* append / to matching directories */ -#define GLOB_NOCHECK 0x20 /* return pattern itself if nothing matches */ -#define GLOB_NOSORT 0x40 /* don't sort */ +#define GLOB_APPEND 0x001 /* append to output from previous call */ +#define GLOB_DOOFFS 0x002 /* use gl_offs */ +#define GLOB_ERR 0x004 /* return on error */ +#define GLOB_MARK 0x008 /* append / to matching directories */ +#define GLOB_NOCHECK 0x010 /* return pattern itself if nothing matches */ +#define GLOB_NOSORT 0x020 /* don't sort */ + #ifndef _POSIX_SOURCE -#define GLOB_QUOTE 0x80 /* quote special chars with \ */ +#define GLOB_MAGCHAR 0x040 /* pattern had globbing characters */ +#define GLOB_NOMAGIC 0x080 /* GLOB_NOCHECK without magic chars (csh) */ +#define GLOB_QUOTE 0x100 /* quote special chars with \ */ +#define GLOB_ALTDIRFUNC 0x200 /* use alternately specified directory funcs */ #endif #define GLOB_NOSPACE (-1) /* malloc call failed */ @@ -67,8 +73,8 @@ typedef struct { #include __BEGIN_DECLS -int glob __P((const char *, int, int (*)(char *, int), glob_t *)); -void globfree __P((glob_t *)); +int glob __P((const char *, int, int (*)(char *, int), glob_t *)); +void globfree __P((glob_t *)); __END_DECLS #endif /* !_GLOB_H_ */ diff --git a/include/math.h b/include/math.h index 50b0a76f91..036e6e4540 100644 --- a/include/math.h +++ b/include/math.h @@ -31,22 +31,16 @@ * SUCH DAMAGE. * * @(#)math.h 5.8 (Berkeley) 4/2/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00086 - * -------------------- ----- ---------------------- - * - * 27 Feb 93 Handel/da Silva/Poirot Adjust value for HUGE_VAL */ #ifndef _MATH_H_ #define _MATH_H_ -#if defined(vax) || defined(tahoe) /* DBL_MAX from float.h */ +#if defined(vax) || defined(tahoe) /* DBL_MAX from float.h */ #define HUGE_VAL 1.701411834604692294E+38 #else -#define HUGE_VAL 1.797693134862315E+308 /* IEEE: positive infinity */ +extern char __infinity[]; /* bytes for IEEE754 +Infinity */ +#define HUGE_VAL (*(double *) __infinity) #endif #if !defined(_ANSI_SOURCE) && !defined(_POSIX_SOURCE) diff --git a/include/mpool.h b/include/mpool.h new file mode 100644 index 0000000000..0cbdd39d26 --- /dev/null +++ b/include/mpool.h @@ -0,0 +1,139 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mpool.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _MPOOL_H_ +#define _MPOOL_H +/* + * The memory pool scheme is a simple one. Each in memory page is referenced + * by a bucket which is threaded in three ways. All active pages are threaded + * on a hash chain (hashed by the page number) and an lru chain. Inactive + * pages are threaded on a free chain. Each reference to a memory pool is + * handed an MPOOL which is the opaque cookie passed to all of the memory + * routines. + */ +#define HASHSIZE 128 +#define HASHKEY(pgno) ((pgno - 1) % HASHSIZE) + +/* The BKT structures are the elements of the lists. */ +typedef struct BKT { + struct BKT *hnext; /* next hash bucket */ + struct BKT *hprev; /* previous hash bucket */ + struct BKT *cnext; /* next free/lru bucket */ + struct BKT *cprev; /* previous free/lru bucket */ + void *page; /* page */ + pgno_t pgno; /* page number */ + +#define MPOOL_DIRTY 0x01 /* page needs to be written */ +#define MPOOL_PINNED 0x02 /* page is pinned into memory */ + unsigned long flags; /* flags */ +} BKT; + +/* The BKTHDR structures are the heads of the lists. */ +typedef struct BKTHDR { + struct BKT *hnext; /* next hash bucket */ + struct BKT *hprev; /* previous hash bucket */ + struct BKT *cnext; /* next free/lru bucket */ + struct BKT *cprev; /* previous free/lru bucket */ +} BKTHDR; + +typedef struct MPOOL { + BKTHDR free; /* The free list. */ + BKTHDR lru; /* The LRU list. */ + BKTHDR hashtable[HASHSIZE]; /* Hashed list by page number. */ + pgno_t curcache; /* Current number of cached pages. */ + pgno_t maxcache; /* Max number of cached pages. */ + pgno_t npages; /* Number of pages in the file. */ + u_long pagesize; /* File page size. */ + int fd; /* File descriptor. */ + /* Page in conversion routine. */ + void (*pgin) __P((void *, pgno_t, void *)); + /* Page out conversion routine. */ + void (*pgout) __P((void *, pgno_t, void *)); + void *pgcookie; /* Cookie for page in/out routines. */ +#ifdef STATISTICS + unsigned long cachehit; + unsigned long cachemiss; + unsigned long pagealloc; + unsigned long pageflush; + unsigned long pageget; + unsigned long pagenew; + unsigned long pageput; + unsigned long pageread; + unsigned long pagewrite; +#endif +} MPOOL; + +#ifdef __MPOOLINTERFACE_PRIVATE +/* Macros to insert/delete into/from hash chain. */ +#define rmhash(bp) { \ + (bp)->hprev->hnext = (bp)->hnext; \ + (bp)->hnext->hprev = (bp)->hprev; \ +} +#define inshash(bp, pg) { \ + hp = &mp->hashtable[HASHKEY(pg)]; \ + (bp)->hnext = hp->hnext; \ + (bp)->hprev = (struct BKT *)hp; \ + hp->hnext->hprev = (bp); \ + hp->hnext = (bp); \ +} + +/* Macros to insert/delete into/from lru and free chains. */ +#define rmchain(bp) { \ + (bp)->cprev->cnext = (bp)->cnext; \ + (bp)->cnext->cprev = (bp)->cprev; \ +} +#define inschain(bp, dp) { \ + (bp)->cnext = (dp)->cnext; \ + (bp)->cprev = (struct BKT *)(dp); \ + (dp)->cnext->cprev = (bp); \ + (dp)->cnext = (bp); \ +} +#endif + +__BEGIN_DECLS +MPOOL *mpool_open __P((DBT *, int, pgno_t, pgno_t)); +void mpool_filter __P((MPOOL *, void (*)(void *, pgno_t, void *), + void (*)(void *, pgno_t, void *), void *)); +void *mpool_new __P((MPOOL *, pgno_t *)); +void *mpool_get __P((MPOOL *, pgno_t, u_int)); +int mpool_put __P((MPOOL *, void *, u_int)); +int mpool_sync __P((MPOOL *)); +int mpool_close __P((MPOOL *)); +#ifdef STATISTICS +void mpool_stat __P((MPOOL *)); +#endif +__END_DECLS + +#endif /* _MPOOL_H_ */ diff --git a/include/ndbm.h b/include/ndbm.h index 67f0293742..a545bca132 100644 --- a/include/ndbm.h +++ b/include/ndbm.h @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. @@ -33,13 +33,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ndbm.h 5.6 (Berkeley) 4/3/91 + * @(#)ndbm.h 8.1 (Berkeley) 6/2/93 */ #ifndef _NDBM_H_ #define _NDBM_H_ -#include #include /* Map dbm interface onto db(3). */ @@ -61,6 +60,7 @@ typedef struct { } datum; typedef DB DBM; +#define dbm_pagfno(a) DBM_PAGFNO_NOT_AVAILABLE __BEGIN_DECLS void dbm_close __P((DBM *)); @@ -71,6 +71,7 @@ long dbm_forder __P((DBM *, datum)); datum dbm_nextkey __P((DBM *)); DBM *dbm_open __P((const char *, int, int)); int dbm_store __P((DBM *, datum, datum, int)); +int dbm_dirfno __P((DBM *)); __END_DECLS #endif /* !_NDBM_H_ */ diff --git a/include/netdb.h b/include/netdb.h index ec6ab51942..2fa4468071 100644 --- a/include/netdb.h +++ b/include/netdb.h @@ -99,7 +99,7 @@ void endnetent __P((void)); void endprotoent __P((void)); void endservent __P((void)); struct hostent *gethostbyaddr __P((const char *, int, int)); -struct hostent *gethostbyname __P((char *)); +struct hostent *gethostbyname __P((const char *)); struct hostent *gethostent __P((void)); struct netent *getnetbyaddr __P((long, int)); /* u_long? */ struct netent *getnetbyname __P((const char *)); diff --git a/include/pwd.h b/include/pwd.h index 7c78d53245..8b25a47875 100644 --- a/include/pwd.h +++ b/include/pwd.h @@ -39,11 +39,16 @@ #include #ifndef _POSIX_SOURCE +#define _PATH_PWD "/etc" #define _PATH_PASSWD "/etc/passwd" +#define _PASSWD "passwd" #define _PATH_MASTERPASSWD "/etc/master.passwd" +#define _MASTERPASSWD "master.passwd" #define _PATH_MP_DB "/etc/pwd.db" +#define _MP_DB "pwd.db" #define _PATH_SMP_DB "/etc/spwd.db" +#define _SMP_DB "spwd.db" #define _PATH_PWD_MKDB "/usr/sbin/pwd_mkdb" diff --git a/include/rpcsvc/Makefile b/include/rpcsvc/Makefile new file mode 100644 index 0000000000..84fef6f4e3 --- /dev/null +++ b/include/rpcsvc/Makefile @@ -0,0 +1,37 @@ +# from: @(#)Makefile 2.3 88/08/11 4.0 RPCSRC +# $Id: Makefile,v 1.9 1993/08/01 05:41:52 mycroft Exp $ + +.SUFFIXES: .x + +RPCCOM = rpcgen + +HDRS= klm_prot.h mount.h nfs_prot.h nlm_prot.h rex.h rnusers.h\ + rquota.h rstat.h rwall.h sm_inter.h spray.h yppasswd.h yp.h +XFILES= bootparam_prot.x klm_prot.x mount.x nfs_prot.x nlm_prot.x \ + rex.x rnusers.x rquota.x rstat.x rwall.x sm_inter.x spray.x \ + yppasswd.x yp.x +HFILES= yp_prot.h ypclnt.h + +CLEANFILES+= ${HDRS} + +all: ${HDRS} + +install: all + @echo "Creating RPC service headers directory" + @/bin/rm -rf ${DESTDIR}/usr/include/rpcsvc + @-mkdir ${DESTDIR}/usr/include/rpcsvc + @echo "Installing RPC service header and definition files" + @for i in $(XFILES); do \ + (install -c -m 644 ${.CURDIR}/$$i ${DESTDIR}/usr/include/rpcsvc) done + @for i in $(HDRS); do \ + (install ${COPY} -m 644 $$i ${DESTDIR}/usr/include/rpcsvc) done + @for i in $(HFILES); do \ + (install -c -m 644 ${.CURDIR}/$$i ${DESTDIR}/usr/include/rpcsvc) done + @chown -R ${BINOWN}.${BINGRP} ${DESTDIR}/usr/include/rpcsvc + @chmod -R a-w ${DESTDIR}/usr/include/rpcsvc + +.x.h: + @echo generating $@... + @PWD=`pwd` ; cd ${.CURDIR} ; ${RPCCOM} -h $*.x -o $$PWD/$@ + +.include diff --git a/include/rpcsvc/bootparam_prot.x b/include/rpcsvc/bootparam_prot.x new file mode 100644 index 0000000000..01f31a6568 --- /dev/null +++ b/include/rpcsvc/bootparam_prot.x @@ -0,0 +1,100 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * RPC for bootparms service. + * There are two procedures: + * WHOAMI takes a net address and returns a client name and also a + * likely net address for routing + * GETFILE takes a client name and file identifier and returns the + * server name, server net address and pathname for the file. + * file identifiers typically include root, swap, pub and dump + */ + +#ifdef RPC_HDR +%#include +%#include +%#include +%#include +#else +%#ifndef lint +%/*static char sccsid[] = "from: @(#)bootparam_prot.x 1.2 87/06/24 Copyr 1987 Sun Micro";*/ +%/*static char sccsid[] = "from: @(#)bootparam_prot.x 2.1 88/08/01 4.0 RPCSRC";*/ +%static char rcsid[] = "$Id: bootparam_prot.x,v 1.4 1993/08/03 21:53:13 mycroft Exp $"; +%#endif /* not lint */ +#endif + +const MAX_MACHINE_NAME = 255; +const MAX_PATH_LEN = 1024; +const MAX_FILEID = 32; +const IP_ADDR_TYPE = 1; + +typedef string bp_machine_name_t; +typedef string bp_path_t; +typedef string bp_fileid_t; + +struct ip_addr_t { + char net; + char host; + char lh; + char impno; +}; + +union bp_address switch (int address_type) { + case IP_ADDR_TYPE: + ip_addr_t ip_addr; +}; + +struct bp_whoami_arg { + bp_address client_address; +}; + +struct bp_whoami_res { + bp_machine_name_t client_name; + bp_machine_name_t domain_name; + bp_address router_address; +}; + +struct bp_getfile_arg { + bp_machine_name_t client_name; + bp_fileid_t file_id; +}; + +struct bp_getfile_res { + bp_machine_name_t server_name; + bp_address server_address; + bp_path_t server_path; +}; + +program BOOTPARAMPROG { + version BOOTPARAMVERS { + bp_whoami_res BOOTPARAMPROC_WHOAMI(bp_whoami_arg) = 1; + bp_getfile_res BOOTPARAMPROC_GETFILE(bp_getfile_arg) = 2; + } = 1; +} = 100026; diff --git a/include/rpcsvc/klm_prot.x b/include/rpcsvc/klm_prot.x new file mode 100644 index 0000000000..f8982a0c8f --- /dev/null +++ b/include/rpcsvc/klm_prot.x @@ -0,0 +1,138 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Kernel/lock manager protocol definition + * Copyright (C) 1986 Sun Microsystems, Inc. + * + * protocol used between the UNIX kernel (the "client") and the + * local lock manager. The local lock manager is a deamon running + * above the kernel. + */ + +#ifndef RPC_HDR +%#ifndef lint +%/*static char sccsid[] = "from: @(#)klm_prot.x 1.7 87/07/08 Copyr 1987 Sun Micro";*/ +%/*static char sccsid[] = "from: @(#)klm_prot.x 2.1 88/08/01 4.0 RPCSRC";*/ +%static char rcsid[] = "$Id: klm_prot.x,v 1.5 1993/08/03 21:53:16 mycroft Exp $"; +%#endif /* not lint */ +#endif + +const LM_MAXSTRLEN = 1024; + +/* + * lock manager status returns + */ +enum klm_stats { + klm_granted = 0, /* lock is granted */ + klm_denied = 1, /* lock is denied */ + klm_denied_nolocks = 2, /* no lock entry available */ + klm_working = 3 /* lock is being processed */ +}; + +/* + * lock manager lock identifier + */ +struct klm_lock { + string server_name; + netobj fh; /* a counted file handle */ + int pid; /* holder of the lock */ + unsigned l_offset; /* beginning offset of the lock */ + unsigned l_len; /* byte length of the lock; + * zero means through end of file */ +}; + +/* + * lock holder identifier + */ +struct klm_holder { + bool exclusive; /* FALSE if shared lock */ + int svid; /* holder of the lock (pid) */ + unsigned l_offset; /* beginning offset of the lock */ + unsigned l_len; /* byte length of the lock; + * zero means through end of file */ +}; + +/* + * reply to KLM_LOCK / KLM_UNLOCK / KLM_CANCEL + */ +struct klm_stat { + klm_stats stat; +}; + +/* + * reply to a KLM_TEST call + */ +union klm_testrply switch (klm_stats stat) { + case klm_denied: + struct klm_holder holder; + default: /* All other cases return no arguments */ + void; +}; + + +/* + * arguments to KLM_LOCK + */ +struct klm_lockargs { + bool block; + bool exclusive; + struct klm_lock alock; +}; + +/* + * arguments to KLM_TEST + */ +struct klm_testargs { + bool exclusive; + struct klm_lock alock; +}; + +/* + * arguments to KLM_UNLOCK + */ +struct klm_unlockargs { + struct klm_lock alock; +}; + +program KLM_PROG { + version KLM_VERS { + + klm_testrply KLM_TEST (struct klm_testargs) = 1; + + klm_stat KLM_LOCK (struct klm_lockargs) = 2; + + klm_stat KLM_CANCEL (struct klm_lockargs) = 3; + /* klm_granted=> the cancel request fails due to lock is already granted */ + /* klm_denied=> the cancel request successfully aborts +lock request */ + + klm_stat KLM_UNLOCK (struct klm_unlockargs) = 4; + } = 1; +} = 100020; diff --git a/include/rpcsvc/mount.x b/include/rpcsvc/mount.x new file mode 100644 index 0000000000..b1e2c31fb9 --- /dev/null +++ b/include/rpcsvc/mount.x @@ -0,0 +1,165 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Protocol description for the mount program + */ + +#ifndef RPC_HDR +%#ifndef lint +%/*static char sccsid[] = "from: @(#)mount.x 1.2 87/09/18 Copyr 1987 Sun Micro";*/ +%/*static char sccsid[] = "from: @(#)mount.x 2.1 88/08/01 4.0 RPCSRC";*/ +%static char rcsid[] = "$Id: mount.x,v 1.4 1993/08/03 21:53:18 mycroft Exp $"; +%#endif /* not lint */ +#endif + +const MNTPATHLEN = 1024; /* maximum bytes in a pathname argument */ +const MNTNAMLEN = 255; /* maximum bytes in a name argument */ +const FHSIZE = 32; /* size in bytes of a file handle */ + +/* + * The fhandle is the file handle that the server passes to the client. + * All file operations are done using the file handles to refer to a file + * or a directory. The file handle can contain whatever information the + * server needs to distinguish an individual file. + */ +typedef opaque fhandle[FHSIZE]; + +/* + * If a status of zero is returned, the call completed successfully, and + * a file handle for the directory follows. A non-zero status indicates + * some sort of error. The status corresponds with UNIX error numbers. + */ +union fhstatus switch (unsigned fhs_status) { +case 0: + fhandle fhs_fhandle; +default: + void; +}; + +/* + * The type dirpath is the pathname of a directory + */ +typedef string dirpath; + +/* + * The type name is used for arbitrary names (hostnames, groupnames) + */ +typedef string name; + +/* + * A list of who has what mounted + */ +typedef struct mountbody *mountlist; +struct mountbody { + name ml_hostname; + dirpath ml_directory; + mountlist ml_next; +}; + +/* + * A list of netgroups + */ +typedef struct groupnode *groups; +struct groupnode { + name gr_name; + groups gr_next; +}; + +/* + * A list of what is exported and to whom + */ +typedef struct exportnode *exports; +struct exportnode { + dirpath ex_dir; + groups ex_groups; + exports ex_next; +}; + +program MOUNTPROG { + /* + * Version one of the mount protocol communicates with version two + * of the NFS protocol. The only connecting point is the fhandle + * structure, which is the same for both protocols. + */ + version MOUNTVERS { + /* + * Does no work. It is made available in all RPC services + * to allow server reponse testing and timing + */ + void + MOUNTPROC_NULL(void) = 0; + + /* + * If fhs_status is 0, then fhs_fhandle contains the + * file handle for the directory. This file handle may + * be used in the NFS protocol. This procedure also adds + * a new entry to the mount list for this client mounting + * the directory. + * Unix authentication required. + */ + fhstatus + MOUNTPROC_MNT(dirpath) = 1; + + /* + * Returns the list of remotely mounted filesystems. The + * mountlist contains one entry for each hostname and + * directory pair. + */ + mountlist + MOUNTPROC_DUMP(void) = 2; + + /* + * Removes the mount list entry for the directory + * Unix authentication required. + */ + void + MOUNTPROC_UMNT(dirpath) = 3; + + /* + * Removes all of the mount list entries for this client + * Unix authentication required. + */ + void + MOUNTPROC_UMNTALL(void) = 4; + + /* + * Returns a list of all the exported filesystems, and which + * machines are allowed to import it. + */ + exports + MOUNTPROC_EXPORT(void) = 5; + + /* + * Identical to MOUNTPROC_EXPORT above + */ + exports + MOUNTPROC_EXPORTALL(void) = 6; + } = 1; +} = 100005; diff --git a/include/rpcsvc/nfs_prot.x b/include/rpcsvc/nfs_prot.x new file mode 100644 index 0000000000..818e5a2316 --- /dev/null +++ b/include/rpcsvc/nfs_prot.x @@ -0,0 +1,357 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#ifndef RPC_HDR +%#ifndef lint +%/*static char sccsid[] = "from: @(#)nfs_prot.x 1.2 87/10/12 Copyr 1987 Sun Micro";*/ +%/*static char sccsid[] = "from: @(#)nfs_prot.x 2.1 88/08/01 4.0 RPCSRC";*/ +%static char rcsid[] = "$Id: nfs_prot.x,v 1.4 1993/08/03 21:53:20 mycroft Exp $"; +%#endif /* not lint */ +#endif + +const NFS_PORT = 2049; +const NFS_MAXDATA = 8192; +const NFS_MAXPATHLEN = 1024; +const NFS_MAXNAMLEN = 255; +const NFS_FHSIZE = 32; +const NFS_COOKIESIZE = 4; +const NFS_FIFO_DEV = -1; /* size kludge for named pipes */ + +/* + * File types + */ +const NFSMODE_FMT = 0170000; /* type of file */ +const NFSMODE_DIR = 0040000; /* directory */ +const NFSMODE_CHR = 0020000; /* character special */ +const NFSMODE_BLK = 0060000; /* block special */ +const NFSMODE_REG = 0100000; /* regular */ +const NFSMODE_LNK = 0120000; /* symbolic link */ +const NFSMODE_SOCK = 0140000; /* socket */ +const NFSMODE_FIFO = 0010000; /* fifo */ + +/* + * Error status + */ +enum nfsstat { + NFS_OK= 0, /* no error */ + NFSERR_PERM=1, /* Not owner */ + NFSERR_NOENT=2, /* No such file or directory */ + NFSERR_IO=5, /* I/O error */ + NFSERR_NXIO=6, /* No such device or address */ + NFSERR_ACCES=13, /* Permission denied */ + NFSERR_EXIST=17, /* File exists */ + NFSERR_NODEV=19, /* No such device */ + NFSERR_NOTDIR=20, /* Not a directory*/ + NFSERR_ISDIR=21, /* Is a directory */ + NFSERR_FBIG=27, /* File too large */ + NFSERR_NOSPC=28, /* No space left on device */ + NFSERR_ROFS=30, /* Read-only file system */ + NFSERR_NAMETOOLONG=63, /* File name too long */ + NFSERR_NOTEMPTY=66, /* Directory not empty */ + NFSERR_DQUOT=69, /* Disc quota exceeded */ + NFSERR_STALE=70, /* Stale NFS file handle */ + NFSERR_WFLUSH=99 /* write cache flushed */ +}; + +/* + * File types + */ +enum ftype { + NFNON = 0, /* non-file */ + NFREG = 1, /* regular file */ + NFDIR = 2, /* directory */ + NFBLK = 3, /* block special */ + NFCHR = 4, /* character special */ + NFLNK = 5, /* symbolic link */ + NFSOCK = 6, /* unix domain sockets */ + NFBAD = 7, /* unused */ + NFFIFO = 8 /* named pipe */ +}; + +/* + * File access handle + */ +struct nfs_fh { + opaque data[NFS_FHSIZE]; +}; + +/* + * Timeval + */ +struct nfstime { + unsigned seconds; + unsigned useconds; +}; + + +/* + * File attributes + */ +struct fattr { + ftype type; /* file type */ + unsigned mode; /* protection mode bits */ + unsigned nlink; /* # hard links */ + unsigned uid; /* owner user id */ + unsigned gid; /* owner group id */ + unsigned size; /* file size in bytes */ + unsigned blocksize; /* prefered block size */ + unsigned rdev; /* special device # */ + unsigned blocks; /* Kb of disk used by file */ + unsigned fsid; /* device # */ + unsigned fileid; /* inode # */ + nfstime atime; /* time of last access */ + nfstime mtime; /* time of last modification */ + nfstime ctime; /* time of last change */ +}; + +/* + * File attributes which can be set + */ +struct sattr { + unsigned mode; /* protection mode bits */ + unsigned uid; /* owner user id */ + unsigned gid; /* owner group id */ + unsigned size; /* file size in bytes */ + nfstime atime; /* time of last access */ + nfstime mtime; /* time of last modification */ +}; + + +typedef string filename; +typedef string nfspath; + +/* + * Reply status with file attributes + */ +union attrstat switch (nfsstat status) { +case NFS_OK: + fattr attributes; +default: + void; +}; + +struct sattrargs { + nfs_fh file; + sattr attributes; +}; + +/* + * Arguments for directory operations + */ +struct diropargs { + nfs_fh dir; /* directory file handle */ + filename name; /* name (up to NFS_MAXNAMLEN bytes) */ +}; + +struct diropokres { + nfs_fh file; + fattr attributes; +}; + +/* + * Results from directory operation + */ +union diropres switch (nfsstat status) { +case NFS_OK: + diropokres diropres; +default: + void; +}; + +union readlinkres switch (nfsstat status) { +case NFS_OK: + nfspath data; +default: + void; +}; + +/* + * Arguments to remote read + */ +struct readargs { + nfs_fh file; /* handle for file */ + unsigned offset; /* byte offset in file */ + unsigned count; /* immediate read count */ + unsigned totalcount; /* total read count (from this offset)*/ +}; + +/* + * Status OK portion of remote read reply + */ +struct readokres { + fattr attributes; /* attributes, need for pagin*/ + opaque data; +}; + +union readres switch (nfsstat status) { +case NFS_OK: + readokres reply; +default: + void; +}; + +/* + * Arguments to remote write + */ +struct writeargs { + nfs_fh file; /* handle for file */ + unsigned beginoffset; /* beginning byte offset in file */ + unsigned offset; /* current byte offset in file */ + unsigned totalcount; /* total write count (to this offset)*/ + opaque data; +}; + +struct createargs { + diropargs where; + sattr attributes; +}; + +struct renameargs { + diropargs from; + diropargs to; +}; + +struct linkargs { + nfs_fh from; + diropargs to; +}; + +struct symlinkargs { + diropargs from; + nfspath to; + sattr attributes; +}; + + +typedef opaque nfscookie[NFS_COOKIESIZE]; + +/* + * Arguments to readdir + */ +struct readdirargs { + nfs_fh dir; /* directory handle */ + nfscookie cookie; + unsigned count; /* number of directory bytes to read */ +}; + +struct entry { + unsigned fileid; + filename name; + nfscookie cookie; + entry *nextentry; +}; + +struct dirlist { + entry *entries; + bool eof; +}; + +union readdirres switch (nfsstat status) { +case NFS_OK: + dirlist reply; +default: + void; +}; + +struct statfsokres { + unsigned tsize; /* preferred transfer size in bytes */ + unsigned bsize; /* fundamental file system block size */ + unsigned blocks; /* total blocks in file system */ + unsigned bfree; /* free blocks in fs */ + unsigned bavail; /* free blocks avail to non-superuser */ +}; + +union statfsres switch (nfsstat status) { +case NFS_OK: + statfsokres reply; +default: + void; +}; + +/* + * Remote file service routines + */ +program NFS_PROGRAM { + version NFS_VERSION { + void + NFSPROC_NULL(void) = 0; + + attrstat + NFSPROC_GETATTR(nfs_fh) = 1; + + attrstat + NFSPROC_SETATTR(sattrargs) = 2; + + void + NFSPROC_ROOT(void) = 3; + + diropres + NFSPROC_LOOKUP(diropargs) = 4; + + readlinkres + NFSPROC_READLINK(nfs_fh) = 5; + + readres + NFSPROC_READ(readargs) = 6; + + void + NFSPROC_WRITECACHE(void) = 7; + + attrstat + NFSPROC_WRITE(writeargs) = 8; + + diropres + NFSPROC_CREATE(createargs) = 9; + + nfsstat + NFSPROC_REMOVE(diropargs) = 10; + + nfsstat + NFSPROC_RENAME(renameargs) = 11; + + nfsstat + NFSPROC_LINK(linkargs) = 12; + + nfsstat + NFSPROC_SYMLINK(symlinkargs) = 13; + + diropres + NFSPROC_MKDIR(createargs) = 14; + + nfsstat + NFSPROC_RMDIR(diropargs) = 15; + + readdirres + NFSPROC_READDIR(readdirargs) = 16; + + statfsres + NFSPROC_STATFS(nfs_fh) = 17; + } = 2; +} = 100003; + diff --git a/include/rpcsvc/nlm_prot.x b/include/rpcsvc/nlm_prot.x new file mode 100644 index 0000000000..27bbd9490f --- /dev/null +++ b/include/rpcsvc/nlm_prot.x @@ -0,0 +1,182 @@ +/* + * Network lock manager protocol definition + * Copyright (C) 1986 Sun Microsystems, Inc. + * + * protocol used between local lock manager and remote lock manager + */ + +#ifdef RPC_HDR +%#define LM_MAXSTRLEN 1024 +%#define MAXNAMELEN LM_MAXSTRLEN+1 +#else +%#ifndef lint +%/*static char sccsid[] = "from: @(#)nlm_prot.x 1.8 87/09/21 Copyr 1987 Sun Micro";*/ +%/*static char sccsid[] = "from: * @(#)nlm_prot.x 2.1 88/08/01 4.0 RPCSRC";*/ +%static char rcsid[] = "$Id: nlm_prot.x,v 1.4 1993/08/03 21:53:22 mycroft Exp $"; +%#endif /* not lint */ +#endif + +/* + * status of a call to the lock manager + */ +enum nlm_stats { + nlm_granted = 0, + nlm_denied = 1, + nlm_denied_nolocks = 2, + nlm_blocked = 3, + nlm_denied_grace_period = 4 +}; + +struct nlm_holder { + bool exclusive; + int svid; + netobj oh; + unsigned l_offset; + unsigned l_len; +}; + +union nlm_testrply switch (nlm_stats stat) { + case nlm_denied: + struct nlm_holder holder; + default: + void; +}; + +struct nlm_stat { + nlm_stats stat; +}; + +struct nlm_res { + netobj cookie; + nlm_stat stat; +}; + +struct nlm_testres { + netobj cookie; + nlm_testrply stat; +}; + +struct nlm_lock { + string caller_name; + netobj fh; /* identify a file */ + netobj oh; /* identify owner of a lock */ + int svid; /* generated from pid for svid */ + unsigned l_offset; + unsigned l_len; +}; + +struct nlm_lockargs { + netobj cookie; + bool block; + bool exclusive; + struct nlm_lock alock; + bool reclaim; /* used for recovering locks */ + int state; /* specify local status monitor state */ +}; + +struct nlm_cancargs { + netobj cookie; + bool block; + bool exclusive; + struct nlm_lock alock; +}; + +struct nlm_testargs { + netobj cookie; + bool exclusive; + struct nlm_lock alock; +}; + +struct nlm_unlockargs { + netobj cookie; + struct nlm_lock alock; +}; + + +#ifdef RPC_HDR +%/* +% * The following enums are actually bit encoded for efficient +% * boolean algebra.... DON'T change them..... +% */ +#endif +enum fsh_mode { + fsm_DN = 0, /* deny none */ + fsm_DR = 1, /* deny read */ + fsm_DW = 2, /* deny write */ + fsm_DRW = 3 /* deny read/write */ +}; + +enum fsh_access { + fsa_NONE = 0, /* for completeness */ + fsa_R = 1, /* read only */ + fsa_W = 2, /* write only */ + fsa_RW = 3 /* read/write */ +}; + +struct nlm_share { + string caller_name; + netobj fh; + netobj oh; + fsh_mode mode; + fsh_access access; +}; + +struct nlm_shareargs { + netobj cookie; + nlm_share share; + bool reclaim; +}; + +struct nlm_shareres { + netobj cookie; + nlm_stats stat; + int sequence; +}; + +struct nlm_notify { + string name; + long state; +}; + +/* + * Over-the-wire protocol used between the network lock managers + */ + +program NLM_PROG { + version NLM_VERS { + + nlm_testres NLM_TEST(struct nlm_testargs) = 1; + + nlm_res NLM_LOCK(struct nlm_lockargs) = 2; + + nlm_res NLM_CANCEL(struct nlm_cancargs) = 3; + nlm_res NLM_UNLOCK(struct nlm_unlockargs) = 4; + + /* + * remote lock manager call-back to grant lock + */ + nlm_res NLM_GRANTED(struct nlm_testargs)= 5; + /* + * message passing style of requesting lock + */ + void NLM_TEST_MSG(struct nlm_testargs) = 6; + void NLM_LOCK_MSG(struct nlm_lockargs) = 7; + void NLM_CANCEL_MSG(struct nlm_cancargs) =8; + void NLM_UNLOCK_MSG(struct nlm_unlockargs) = 9; + void NLM_GRANTED_MSG(struct nlm_testargs) = 10; + void NLM_TEST_RES(nlm_testres) = 11; + void NLM_LOCK_RES(nlm_res) = 12; + void NLM_CANCEL_RES(nlm_res) = 13; + void NLM_UNLOCK_RES(nlm_res) = 14; + void NLM_GRANTED_RES(nlm_res) = 15; + } = 1; + + version NLM_VERSX { + nlm_shareres NLM_SHARE(nlm_shareargs) = 20; + nlm_shareres NLM_UNSHARE(nlm_shareargs) = 21; + nlm_res NLM_NM_LOCK(nlm_lockargs) = 22; + void NLM_FREE_ALL(nlm_notify) = 23; + } = 3; + +} = 100021; + diff --git a/include/rpcsvc/rex.x b/include/rpcsvc/rex.x new file mode 100644 index 0000000000..d3ab0320a7 --- /dev/null +++ b/include/rpcsvc/rex.x @@ -0,0 +1,234 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Remote execution (rex) protocol specification + */ + +#ifndef RPC_HDR +%#ifndef lint +%/*static char sccsid[] = "from: @(#)rex.x 1.3 87/09/18 Copyr 1987 Sun Micro";*/ +%/*static char sccsid[] = "from: @(#)rex.x 2.1 88/08/01 4.0 RPCSRC";*/ +%static char rcsid[] = "$Id: rex.x,v 1.4 1993/08/03 21:53:23 mycroft Exp $"; +%#endif /* not lint */ +#endif + +const STRINGSIZE = 1024; +typedef string rexstring<1024>; + +/* + * values to pass to REXPROC_SIGNAL + */ +const SIGINT = 2; /* interrupt */ + +/* + * Values for rst_flags, below + */ +const REX_INTERACTIVE = 1; /* interactive mode */ + +struct rex_start { + rexstring rst_cmd<>; /* list of command and args */ + rexstring rst_host; /* working directory host name */ + rexstring rst_fsname; /* working directory file system name */ + rexstring rst_dirwithin;/* working directory within file system */ + rexstring rst_env<>; /* list of environment */ + unsigned int rst_port0; /* port for stdin */ + unsigned int rst_port1; /* port for stdout */ + unsigned int rst_port2; /* port for stderr */ + unsigned int rst_flags; /* options - see const above */ +}; + +struct rex_result { + int rlt_stat; /* integer status code */ + rexstring rlt_message; /* string message for human consumption */ +}; + + +struct sgttyb { + unsigned four; /* always equals 4 */ + opaque chars[4]; + /* chars[0] == input speed */ + /* chars[1] == output speed */ + /* chars[2] == kill character */ + /* chars[3] == erase character */ + unsigned flags; +}; +/* values for speeds above (baud rates) */ +const B0 = 0; +const B50 = 1; +const B75 = 2; +const B110 = 3; +const B134 = 4; +const B150 = 5; +const B200 = 6; +const B300 = 7; +const B600 = 8; +const B1200 = 9; +const B1800 = 10; +const B2400 = 11; +const B4800 = 12; +const B9600 = 13; +const B19200 = 14; +const B38400 = 15; + +/* values for flags above */ +const TANDEM = 0x00000001; /* send stopc on out q full */ +const CBREAK = 0x00000002; /* half-cooked mode */ +const LCASE = 0x00000004; /* simulate lower case */ +const ECHO = 0x00000008; /* echo input */ +const CRMOD = 0x00000010; /* map \r to \r\n on output */ +const RAW = 0x00000020; /* no i/o processing */ +const ODDP = 0x00000040; /* get/send odd parity */ +const EVENP = 0x00000080; /* get/send even parity */ +const ANYP = 0x000000c0; /* get any parity/send none */ +const NLDELAY = 0x00000300; /* \n delay */ +const NL0 = 0x00000000; +const NL1 = 0x00000100; /* tty 37 */ +const NL2 = 0x00000200; /* vt05 */ +const NL3 = 0x00000300; +const TBDELAY = 0x00000c00; /* horizontal tab delay */ +const TAB0 = 0x00000000; +const TAB1 = 0x00000400; /* tty 37 */ +const TAB2 = 0x00000800; +const XTABS = 0x00000c00; /* expand tabs on output */ +const CRDELAY = 0x00003000; /* \r delay */ +const CR0 = 0x00000000; +const CR1 = 0x00001000; /* tn 300 */ +const CR2 = 0x00002000; /* tty 37 */ +const CR3 = 0x00003000; /* concept 100 */ +const VTDELAY = 0x00004000; /* vertical tab delay */ +const FF0 = 0x00000000; +const FF1 = 0x00004000; /* tty 37 */ +const BSDELAY = 0x00008000; /* \b delay */ +const BS0 = 0x00000000; +const BS1 = 0x00008000; +const CRTBS = 0x00010000; /* do backspacing for crt */ +const PRTERA = 0x00020000; /* \ ... / erase */ +const CRTERA = 0x00040000; /* " \b " to wipe out char */ +const TILDE = 0x00080000; /* hazeltine tilde kludge */ +const MDMBUF = 0x00100000; /* start/stop output on carrier intr */ +const LITOUT = 0x00200000; /* literal output */ +const TOSTOP = 0x00400000; /* SIGTTOU on background output */ +const FLUSHO = 0x00800000; /* flush output to terminal */ +const NOHANG = 0x01000000; /* no SIGHUP on carrier drop */ +const L001000 = 0x02000000; +const CRTKIL = 0x04000000; /* kill line with " \b " */ +const PASS8 = 0x08000000; +const CTLECH = 0x10000000; /* echo control chars as ^X */ +const PENDIN = 0x20000000; /* tp->t_rawq needs reread */ +const DECCTQ = 0x40000000; /* only ^Q starts after ^S */ +const NOFLSH = 0x80000000; /* no output flush on signal */ + +struct tchars { + unsigned six; /* always equals 6 */ + opaque chars[6]; + /* chars[0] == interrupt char */ + /* chars[1] == quit char */ + /* chars[2] == start output char */ + /* chars[3] == stop output char */ + /* chars[4] == end-of-file char */ + /* chars[5] == input delimeter (like nl) */ +}; + +struct ltchars { + unsigned six; /* always equals 6 */ + opaque chars[6]; + /* chars[0] == stop process signal */ + /* chars[1] == delayed stop process signal */ + /* chars[2] == reprint line */ + /* chars[3] == flush output */ + /* chars[4] == word erase */ + /* chars[5] == literal next character */ + unsigned mode; +}; + +struct rex_ttysize { + int ts_lines; + int ts_cols; +}; + +struct rex_ttymode { + sgttyb basic; /* standard unix tty flags */ + tchars more; /* interrupt, kill characters, etc. */ + ltchars yetmore; /* special Berkeley characters */ + unsigned andmore; /* and Berkeley modes */ +}; + +/* values for andmore above */ +const LCRTBS = 0x0001; /* do backspacing for crt */ +const LPRTERA = 0x0002; /* \ ... / erase */ +const LCRTERA = 0x0004; /* " \b " to wipe out char */ +const LTILDE = 0x0008; /* hazeltine tilde kludge */ +const LMDMBUF = 0x0010; /* start/stop output on carrier intr */ +const LLITOUT = 0x0020; /* literal output */ +const LTOSTOP = 0x0040; /* SIGTTOU on background output */ +const LFLUSHO = 0x0080; /* flush output to terminal */ +const LNOHANG = 0x0100; /* no SIGHUP on carrier drop */ +const LL001000 = 0x0200; +const LCRTKIL = 0x0400; /* kill line with " \b " */ +const LPASS8 = 0x0800; +const LCTLECH = 0x1000; /* echo control chars as ^X */ +const LPENDIN = 0x2000; /* needs reread */ +const LDECCTQ = 0x4000; /* only ^Q starts after ^S */ +const LNOFLSH = 0x8000; /* no output flush on signal */ + +program REXPROG { + version REXVERS { + + /* + * Start remote execution + */ + rex_result + REXPROC_START(rex_start) = 1; + + /* + * Wait for remote execution to terminate + */ + rex_result + REXPROC_WAIT(void) = 2; + + /* + * Send tty modes + */ + void + REXPROC_MODES(rex_ttymode) = 3; + + /* + * Send window size change + */ + void + REXPROC_WINCH(rex_ttysize) = 4; + + /* + * Send other signal + */ + void + REXPROC_SIGNAL(int) = 5; + } = 1; +} = 100017; diff --git a/include/rpcsvc/rnusers.x b/include/rpcsvc/rnusers.x new file mode 100644 index 0000000000..cce3353d1c --- /dev/null +++ b/include/rpcsvc/rnusers.x @@ -0,0 +1,91 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Find out about remote users + */ + +#ifndef RPC_HDR +%#ifndef lint +%/*static char sccsid[] = "from: @(#)rnusers.x 1.2 87/09/20 Copyr 1987 Sun Micro";*/ +%/*static char sccsid[] = "from: @(#)rnusers.x 2.1 88/08/01 4.0 RPCSRC";*/ +%static char rcsid[] = "$Id: rnusers.x,v 1.5 1993/08/03 21:53:25 mycroft Exp $"; +%#endif /* not lint */ +#endif + +const MAXUSERS = 100; +const MAXUTLEN = 256; + +struct utmp { + string ut_line; + string ut_name; + string ut_host; + int ut_time; +}; + + +struct utmpidle { + utmp ui_utmp; + unsigned int ui_idle; +}; + +typedef utmp utmparr; + +typedef utmpidle utmpidlearr; + +program RUSERSPROG { + /* + * Old version does not include idle information + */ + version RUSERSVERS_ORIG { + int + RUSERSPROC_NUM(void) = 1; + + utmparr + RUSERSPROC_NAMES(void) = 2; + + utmparr + RUSERSPROC_ALLNAMES(void) = 3; + } = 1; + + /* + * Includes idle information + */ + version RUSERSVERS_IDLE { + int + RUSERSPROC_NUM(void) = 1; + + utmpidlearr + RUSERSPROC_NAMES(void) = 2; + + utmpidlearr + RUSERSPROC_ALLNAMES(void) = 3; + } = 2; +} = 100002; + diff --git a/include/rpcsvc/rquota.x b/include/rpcsvc/rquota.x new file mode 100644 index 0000000000..296e8589f4 --- /dev/null +++ b/include/rpcsvc/rquota.x @@ -0,0 +1,66 @@ +/* + * Remote quota protocol + * Requires unix authentication + */ + +#ifndef RPC_HDR +%#ifndef lint +%/*static char sccsid[] = "from: @(#)rquota.x 1.2 87/09/20 Copyr 1987 Sun Micro";*/ +%/*static char sccsid[] = "from: @(#)rquota.x 2.1 88/08/01 4.0 RPCSRC";*/ +%static char rcsid[] = "$Id: rquota.x,v 1.4 1993/08/03 21:53:26 mycroft Exp $"; +%#endif /* not lint */ +#endif + +const RQ_PATHLEN = 1024; + +struct getquota_args { + string gqa_pathp; /* path to filesystem of interest */ + int gqa_uid; /* inquire about quota for uid */ +}; + +/* + * remote quota structure + */ +struct rquota { + int rq_bsize; /* block size for block counts */ + bool rq_active; /* indicates whether quota is active */ + unsigned int rq_bhardlimit; /* absolute limit on disk blks alloc */ + unsigned int rq_bsoftlimit; /* preferred limit on disk blks */ + unsigned int rq_curblocks; /* current block count */ + unsigned int rq_fhardlimit; /* absolute limit on allocated files */ + unsigned int rq_fsoftlimit; /* preferred file limit */ + unsigned int rq_curfiles; /* current # allocated files */ + unsigned int rq_btimeleft; /* time left for excessive disk use */ + unsigned int rq_ftimeleft; /* time left for excessive files */ +}; + +enum gqr_status { + Q_OK = 1, /* quota returned */ + Q_NOQUOTA = 2, /* noquota for uid */ + Q_EPERM = 3 /* no permission to access quota */ +}; + +union getquota_rslt switch (gqr_status status) { +case Q_OK: + rquota gqr_rquota; /* valid if status == Q_OK */ +case Q_NOQUOTA: + void; +case Q_EPERM: + void; +}; + +program RQUOTAPROG { + version RQUOTAVERS { + /* + * Get all quotas + */ + getquota_rslt + RQUOTAPROC_GETQUOTA(getquota_args) = 1; + + /* + * Get active quotas only + */ + getquota_rslt + RQUOTAPROC_GETACTIVEQUOTA(getquota_args) = 2; + } = 1; +} = 100011; diff --git a/include/rpcsvc/rstat.x b/include/rpcsvc/rstat.x new file mode 100644 index 0000000000..8557dd38e4 --- /dev/null +++ b/include/rpcsvc/rstat.x @@ -0,0 +1,150 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Gather statistics on remote machines + */ + +#ifdef RPC_HDR + +%#ifndef FSCALE +%/* +% * Scale factor for scaled integers used to count load averages. +% */ +%#define FSHIFT 8 /* bits to right of fixed binary point */ +%#define FSCALE (1<; +}; + +struct my_id { + string my_name; /* name of the site iniates the monitoring request*/ + int my_prog; /* rpc program # of the requesting process */ + int my_vers; /* rpc version # of the requesting process */ + int my_proc; /* rpc procedure # of the requesting process */ +}; + +struct mon_id { + string mon_name; /* name of the site to be monitored */ + struct my_id my_id; +}; + + +struct mon{ + struct mon_id mon_id; + opaque priv[16]; /* private information to store at monitor for requesting process */ +}; + + +/* + * state # of status monitor monitonically increases each time + * status of the site changes: + * an even number (>= 0) indicates the site is down and + * an odd number (> 0) indicates the site is up; + */ +struct sm_stat { + int state; /* state # of status monitor */ +}; + +enum res { + stat_succ = 0, /* status monitor agrees to monitor */ + stat_fail = 1 /* status monitor cannot monitor */ +}; + +struct sm_stat_res { + res res_stat; + int state; +}; + +/* + * structure of the status message sent back by the status monitor + * when monitor site status changes + */ +struct status { + string mon_name; + int state; + opaque priv[16]; /* stored private information */ +}; diff --git a/include/rpcsvc/spray.x b/include/rpcsvc/spray.x new file mode 100644 index 0000000000..b20cd325e0 --- /dev/null +++ b/include/rpcsvc/spray.x @@ -0,0 +1,89 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Spray a server with packets + * Useful for testing flakiness of network interfaces + */ + +#ifndef RPC_HDR +%#ifndef lint +%/*static char sccsid[] = "from: @(#)spray.x 1.2 87/09/18 Copyr 1987 Sun Micro";*/ +%/*static char sccsid[] = "from: @(#)spray.x 2.1 88/08/01 4.0 RPCSRC";*/ +%static char rcsid[] = "$Id: spray.x,v 1.4 1993/08/03 21:53:31 mycroft Exp $"; +%#endif /* not lint */ +#endif + +const SPRAYMAX = 8845; /* max amount can spray */ + +/* + * GMT since 0:00, 1 January 1970 + */ +struct spraytimeval { + unsigned int sec; + unsigned int usec; +}; + +/* + * spray statistics + */ +struct spraycumul { + unsigned int counter; + spraytimeval clock; +}; + +/* + * spray data + */ +typedef opaque sprayarr; + +program SPRAYPROG { + version SPRAYVERS { + /* + * Just throw away the data and increment the counter + * This call never returns, so the client should always + * time it out. + */ + void + SPRAYPROC_SPRAY(sprayarr) = 1; + + /* + * Get the value of the counter and elapsed time since + * last CLEAR. + */ + spraycumul + SPRAYPROC_GET(void) = 2; + + /* + * Clear the counter and reset the elapsed time + */ + void + SPRAYPROC_CLEAR(void) = 3; + } = 1; +} = 100012; diff --git a/include/rpcsvc/yp.x b/include/rpcsvc/yp.x new file mode 100644 index 0000000000..0aad705466 --- /dev/null +++ b/include/rpcsvc/yp.x @@ -0,0 +1,296 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Protocol description file for the Yellow Pages Service + */ + +#ifndef RPC_HDR +%#ifndef lint +%/*static char sccsid[] = "from: @(#)yp.x 2.1 88/08/01 4.0 RPCSRC";*/ +%static char rcsid[] = "$Id: yp.x,v 1.4 1993/08/03 21:53:32 mycroft Exp $"; +%#endif /* not lint */ +#endif + +const YPMAXRECORD = 1024; +const YPMAXDOMAIN = 64; +const YPMAXMAP = 64; +const YPMAXPEER = 64; + + +enum ypstat { + YP_TRUE = 1, + YP_NOMORE = 2, + YP_FALSE = 0, + YP_NOMAP = -1, + YP_NODOM = -2, + YP_NOKEY = -3, + YP_BADOP = -4, + YP_BADDB = -5, + YP_YPERR = -6, + YP_BADARGS = -7, + YP_VERS = -8 +}; + + +enum ypxfrstat { + YPXFR_SUCC = 1, + YPXFR_AGE = 2, + YPXFR_NOMAP = -1, + YPXFR_NODOM = -2, + YPXFR_RSRC = -3, + YPXFR_RPC = -4, + YPXFR_MADDR = -5, + YPXFR_YPERR = -6, + YPXFR_BADARGS = -7, + YPXFR_DBM = -8, + YPXFR_FILE = -9, + YPXFR_SKEW = -10, + YPXFR_CLEAR = -11, + YPXFR_FORCE = -12, + YPXFR_XFRERR = -13, + YPXFR_REFUSED = -14 +}; + + +typedef string domainname; +typedef string mapname; +typedef string peername; +typedef opaque keydat; +typedef opaque valdat; + + +struct ypmap_parms { + domainname domain; + mapname map; + unsigned int ordernum; + peername peer; +}; + +struct ypreq_key { + domainname domain; + mapname map; + keydat key; +}; + +struct ypreq_nokey { + domainname domain; + mapname map; +}; + +struct ypreq_xfr { + ypmap_parms map_parms; + unsigned int transid; + unsigned int prog; + unsigned int port; +}; + + +struct ypresp_val { + ypstat stat; + valdat val; +}; + +struct ypresp_key_val { + ypstat stat; + keydat key; + valdat val; +}; + + +struct ypresp_master { + ypstat stat; + peername peer; +}; + +struct ypresp_order { + ypstat stat; + unsigned int ordernum; +}; + +union ypresp_all switch (bool more) { +case TRUE: + ypresp_key_val val; +case FALSE: + void; +}; + +struct ypresp_xfr { + unsigned int transid; + ypxfrstat xfrstat; +}; + +struct ypmaplist { + mapname map; + ypmaplist *next; +}; + +struct ypresp_maplist { + ypstat stat; + ypmaplist *maps; +}; + +enum yppush_status { + YPPUSH_SUCC = 1, /* Success */ + YPPUSH_AGE = 2, /* Master's version not newer */ + YPPUSH_NOMAP = -1, /* Can't find server for map */ + YPPUSH_NODOM = -2, /* Domain not supported */ + YPPUSH_RSRC = -3, /* Local resource alloc failure */ + YPPUSH_RPC = -4, /* RPC failure talking to server */ + YPPUSH_MADDR = -5, /* Can't get master address */ + YPPUSH_YPERR = -6, /* YP server/map db error */ + YPPUSH_BADARGS = -7, /* Request arguments bad */ + YPPUSH_DBM = -8, /* Local dbm operation failed */ + YPPUSH_FILE = -9, /* Local file I/O operation failed */ + YPPUSH_SKEW = -10, /* Map version skew during transfer */ + YPPUSH_CLEAR = -11, /* Can't send "Clear" req to local ypserv */ + YPPUSH_FORCE = -12, /* No local order number in map use -f flag. */ + YPPUSH_XFRERR = -13, /* ypxfr error */ + YPPUSH_REFUSED = -14 /* Transfer request refused by ypserv */ +}; + +struct yppushresp_xfr { + unsigned transid; + yppush_status status; +}; + +/* + * Response structure and overall result status codes. Success and failure + * represent two separate response message types. + */ + +enum ypbind_resptype { + YPBIND_SUCC_VAL = 1, + YPBIND_FAIL_VAL = 2 +}; + +struct ypbind_binding { + opaque ypbind_binding_addr[4]; /* In network order */ + opaque ypbind_binding_port[2]; /* In network order */ +}; + +union ypbind_resp switch (ypbind_resptype ypbind_status) { +case YPBIND_FAIL_VAL: + unsigned ypbind_error; +case YPBIND_SUCC_VAL: + ypbind_binding ypbind_bindinfo; +}; + +/* Detailed failure reason codes for response field ypbind_error*/ + +const YPBIND_ERR_ERR = 1; /* Internal error */ +const YPBIND_ERR_NOSERV = 2; /* No bound server for passed domain */ +const YPBIND_ERR_RESC = 3; /* System resource allocation failure */ + + +/* + * Request data structure for ypbind "Set domain" procedure. + */ +struct ypbind_setdom { + domainname ypsetdom_domain; + ypbind_binding ypsetdom_binding; + unsigned ypsetdom_vers; +}; + + +/* + * YP access protocol + */ +program YPPROG { + version YPVERS { + void + YPPROC_NULL(void) = 0; + + bool + YPPROC_DOMAIN(domainname) = 1; + + bool + YPPROC_DOMAIN_NONACK(domainname) = 2; + + ypresp_val + YPPROC_MATCH(ypreq_key) = 3; + + ypresp_key_val + YPPROC_FIRST(ypreq_key) = 4; + + ypresp_key_val + YPPROC_NEXT(ypreq_key) = 5; + + ypresp_xfr + YPPROC_XFR(ypreq_xfr) = 6; + + void + YPPROC_CLEAR(void) = 7; + + ypresp_all + YPPROC_ALL(ypreq_nokey) = 8; + + ypresp_master + YPPROC_MASTER(ypreq_nokey) = 9; + + ypresp_order + YPPROC_ORDER(ypreq_nokey) = 10; + + ypresp_maplist + YPPROC_MAPLIST(domainname) = 11; + } = 2; +} = 100004; + + +/* + * YPPUSHPROC_XFRRESP is the callback routine for result of YPPROC_XFR + */ +program YPPUSH_XFRRESPPROG { + version YPPUSH_XFRRESPVERS { + void + YPPUSHPROC_NULL(void) = 0; + + yppushresp_xfr + YPPUSHPROC_XFRRESP(void) = 1; + } = 1; +} = 0x40000000; /* transient: could be anything up to 0x5fffffff */ + + +/* + * YP binding protocol + */ +program YPBINDPROG { + version YPBINDVERS { + void + YPBINDPROC_NULL(void) = 0; + + ypbind_resp + YPBINDPROC_DOMAIN(domainname) = 1; + + void + YPBINDPROC_SETDOM(ypbind_setdom) = 2; + } = 2; +} = 100007; + + diff --git a/include/rpcsvc/yp_prot.h b/include/rpcsvc/yp_prot.h new file mode 100644 index 0000000000..7e72e66340 --- /dev/null +++ b/include/rpcsvc/yp_prot.h @@ -0,0 +1,330 @@ +/* + * Copyright (c) 1992/3 Theo de Raadt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: yp_prot.h,v 1.3 1993/08/02 17:49:19 mycroft Exp $ + */ + +#ifndef _YP_PROT_H_ +#define _YP_PROT_H_ + +/* + * YPSERV PROTOCOL: + * + * ypserv supports the following procedures: + * + * YPPROC_NULL takes (void), returns (void). + * called to check if server is alive. + * YPPROC_DOMAIN takes (char *), returns (bool_t). + * true if ypserv serves the named domain. + * YPPROC_DOMAIN_NOACK takes (char *), returns (bool_t). + * true if ypserv serves the named domain. + * used for broadcasts, does not ack if ypserv + * doesn't handle named domain. + * YPPROC_MATCH takes (struct ypreq_key), returns (struct ypresp_val) + * does a lookup. + * YPPROC_FIRST takes (struct ypreq_nokey) returns (ypresp_key_val). + * gets the first key/datum from the map. + * YPPROC_NEXT takes (struct ypreq_key) returns (ypresp_key_val). + * gets the next key/datum from the map. + * YPPROC_XFR takes (struct ypreq_xfr), returns (void). + * tells ypserv to check if there is a new version of + * the map. + * YPPROC_CLEAR takes (void), returns (void). + * tells ypserv to flush it's file cache, so that + * newly transferred files will get read. + * YPPROC_ALL takes (struct ypreq_nokey), returns (bool_t and + * struct ypresp_key_val). + * returns an array of data, with the bool_t being + * false on the last datum. read the source, it's + * convoluted. + * YPPROC_MASTER takes (struct ypreq_nokey), returns (ypresp_master). + * YPPROC_ORDER takes (struct ypreq_nokey), returns (ypresp_order). + * YPPROC_MAPLIST takes (char *), returns (struct ypmaplist *). + */ + +#ifndef BOOL_DEFINED +typedef u_int bool; +#define BOOL_DEFINED +#endif + +bool_t xdr_datum(); +bool_t xdr_ypdomain_wrap_string(); +bool_t xdr_ypmap_wrap_string(); +bool_t xdr_ypreq_key(); +bool_t xdr_ypreq_nokey(); +bool_t xdr_ypreq_xfr(); +bool_t xdr_ypresp_val(); +bool_t xdr_ypresp_key_val(); +bool_t xdr_ypbind_resp(); +bool_t xdr_ypbind_setdom(); +bool_t xdr_yp_inaddr(); +bool_t xdr_ypmap_parms(); +bool_t xdr_ypowner_wrap_string(); +bool_t xdr_yppushresp_xfr(); +bool_t xdr_ypresp_order(); +bool_t xdr_ypresp_master(); +bool_t xdr_ypall(); +bool_t xdr_ypresp_maplist(); + +/* Program and version symbols, magic numbers */ + +#define YPPROG ((u_long)100004) +#define YPVERS ((u_long)2) +#define YPVERS_ORIG ((u_long)1) +#define YPMAXRECORD ((u_long)1024) +#define YPMAXDOMAIN ((u_long)64) +#define YPMAXMAP ((u_long)64) +#define YPMAXPEER ((u_long)256) + +/* + * I don't know if anything of sun's depends on this, or if they + * simply defined it so that their own code wouldn't try to send + * packets over the ethernet MTU. This YP code doesn't use it. + */ +#define YPMSGSZ 1600 + +#ifndef DATUM +typedef struct { + char *dptr; + int dsize; +} datum; +#define DATUM +#endif + +struct ypmap_parms { + char *domain; + char *map; + u_long ordernum; + char *owner; +}; + +struct ypreq_key { + char *domain; + char *map; + datum keydat; +}; + +struct ypreq_nokey { + char *domain; + char *map; +}; + +struct ypreq_xfr { + struct ypmap_parms map_parms; + u_long transid; + u_long proto; + u_short port; +}; +#define ypxfr_domain map_parms.domain +#define ypxfr_map map_parms.map +#define ypxfr_ordernum map_parms.ordernum +#define ypxfr_owner map_parms.owner + +struct ypresp_val { + u_long status; + datum valdat; +}; + +struct ypresp_key_val { + u_long status; + datum keydat; + datum valdat; +}; + +struct ypresp_master { + u_long status; + char *master; +}; + +struct ypresp_order { + u_long status; + u_long ordernum; +}; + +struct ypmaplist { + char ypml_name[YPMAXMAP + 1]; + struct ypmaplist *ypml_next; +}; + +struct ypresp_maplist { + u_long status; + struct ypmaplist *list; +}; + +/* ypserv procedure numbers */ +#define YPPROC_NULL ((u_long)0) +#define YPPROC_DOMAIN ((u_long)1) +#define YPPROC_DOMAIN_NONACK ((u_long)2) +#define YPPROC_MATCH ((u_long)3) +#define YPPROC_FIRST ((u_long)4) +#define YPPROC_NEXT ((u_long)5) +#define YPPROC_XFR ((u_long)6) +#define YPPROC_CLEAR ((u_long)7) +#define YPPROC_ALL ((u_long)8) +#define YPPROC_MASTER ((u_long)9) +#define YPPROC_ORDER ((u_long)10) +#define YPPROC_MAPLIST ((u_long)11) + +/* ypserv procedure return status values */ +#define YP_TRUE ((long)1) /* general purpose success code */ +#define YP_NOMORE ((long)2) /* no more entries in map */ +#define YP_FALSE ((long)0) /* general purpose failure code */ +#define YP_NOMAP ((long)-1) /* no such map in domain */ +#define YP_NODOM ((long)-2) /* domain not supported */ +#define YP_NOKEY ((long)-3) /* no such key in map */ +#define YP_BADOP ((long)-4) /* invalid operation */ +#define YP_BADDB ((long)-5) /* server data base is bad */ +#define YP_YPERR ((long)-6) /* YP server error */ +#define YP_BADARGS ((long)-7) /* request arguments bad */ +#define YP_VERS ((long)-8) /* YP server version mismatch */ + +/* + * Sun's header file says: + * "Domain binding data structure, used by ypclnt package and ypserv modules. + * Users of the ypclnt package (or of this protocol) don't HAVE to know about + * it, but it must be available to users because _yp_dobind is a public + * interface." + * + * This is totally bogus! Nowhere else does Sun state that _yp_dobind() is + * a public interface, and I don't know any reason anyone would want to call + * it. But, just in case anyone does actually expect it to be available.. + * we provide this.. exactly as Sun wants it. + */ +struct dom_binding { + struct dom_binding *dom_pnext; + char dom_domain[YPMAXDOMAIN + 1]; + struct sockaddr_in dom_server_addr; + u_short dom_server_port; + int dom_socket; + CLIENT *dom_client; + u_short dom_local_port; + long dom_vers; +}; + +/* + * YPBIND PROTOCOL: + * + * ypbind supports the following procedures: + * + * YPBINDPROC_NULL takes (void), returns (void). + * to check if ypbind is running. + * YPBINDPROC_DOMAIN takes (char *), returns (struct ypbind_resp). + * requests that ypbind start to serve the + * named domain (if it doesn't already) + * YPBINDPROC_SETDOM takes (struct ypbind_setdom), returns (void). + * used by ypset. + */ + +#define YPBINDPROG ((u_long)100007) +#define YPBINDVERS ((u_long)2) +#define YPBINDVERS_ORIG ((u_long)1) + +/* ypbind procedure numbers */ +#define YPBINDPROC_NULL ((u_long)0) +#define YPBINDPROC_DOMAIN ((u_long)1) +#define YPBINDPROC_SETDOM ((u_long)2) + +/* error code in ypbind_resp.ypbind_status */ +enum ypbind_resptype { + YPBIND_SUCC_VAL = 1, + YPBIND_FAIL_VAL = 2 +}; + +/* network order, of course */ +struct ypbind_binding { + struct in_addr ypbind_binding_addr; + u_short ypbind_binding_port; +}; + +struct ypbind_resp { + enum ypbind_resptype ypbind_status; + union { + u_long ypbind_error; + struct ypbind_binding ypbind_bindinfo; + } ypbind_respbody; +}; + +/* error code in ypbind_resp.ypbind_respbody.ypbind_error */ +#define YPBIND_ERR_ERR 1 /* internal error */ +#define YPBIND_ERR_NOSERV 2 /* no bound server for passed domain */ +#define YPBIND_ERR_RESC 3 /* system resource allocation failure */ + +/* + * Request data structure for ypbind "Set domain" procedure. + */ +struct ypbind_setdom { + char ypsetdom_domain[YPMAXDOMAIN + 1]; + struct ypbind_binding ypsetdom_binding; + u_short ypsetdom_vers; +}; +#define ypsetdom_addr ypsetdom_binding.ypbind_binding_addr +#define ypsetdom_port ypsetdom_binding.ypbind_binding_port + +/* + * YPPUSH PROTOCOL: + * + * Sun says: + * "Protocol between clients (ypxfr, only) and yppush + * yppush speaks a protocol in the transient range, which + * is supplied to ypxfr as a command-line parameter when it + * is activated by ypserv." + * + * This protocol is not implimented, naturally, because this YP + * implimentation only does the client side. + */ +#define YPPUSHVERS ((u_long)1) +#define YPPUSHVERS_ORIG ((u_long)1) + +/* yppush procedure numbers */ +#define YPPUSHPROC_NULL ((u_long)0) +#define YPPUSHPROC_XFRRESP ((u_long)1) + +struct yppushresp_xfr { + u_long transid; + u_long status; +}; + +/* yppush status value in yppushresp_xfr.status */ +#define YPPUSH_SUCC ((long)1) /* Success */ +#define YPPUSH_AGE ((long)2) /* Master's version not newer */ +#define YPPUSH_NOMAP ((long)-1) /* Can't find server for map */ +#define YPPUSH_NODOM ((long)-2) /* Domain not supported */ +#define YPPUSH_RSRC ((long)-3) /* Local resouce alloc failure */ +#define YPPUSH_RPC ((long)-4) /* RPC failure talking to server */ +#define YPPUSH_MADDR ((long)-5) /* Can't get master address */ +#define YPPUSH_YPERR ((long)-6) /* YP server/map db error */ +#define YPPUSH_BADARGS ((long)-7) /* Request arguments bad */ +#define YPPUSH_DBM ((long)-8) /* Local dbm operation failed */ +#define YPPUSH_FILE ((long)-9) /* Local file I/O operation failed */ +#define YPPUSH_SKEW ((long)-10) /* Map version skew during transfer */ +#define YPPUSH_CLEAR ((long)-11) /* Can't send "Clear" req to local ypserv */ +#define YPPUSH_FORCE ((long)-12) /* No local order number in map - use -f */ +#define YPPUSH_XFRERR ((long)-13) /* ypxfr error */ +#define YPPUSH_REFUSED ((long)-14) /* Transfer request refused by ypserv */ + +#endif /* _YP_PROT_H_ */ diff --git a/include/rpcsvc/ypclnt.h b/include/rpcsvc/ypclnt.h new file mode 100644 index 0000000000..8a29965e1f --- /dev/null +++ b/include/rpcsvc/ypclnt.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 1992/3 Theo de Raadt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ypclnt.h,v 1.3 1993/08/02 17:49:21 mycroft Exp $ + */ + +#ifndef _YPCLNT_H_ +#define _YPCLNT_H_ + +#define YPERR_BADARGS 1 /* args to function are bad */ +#define YPERR_RPC 2 /* RPC failure */ +#define YPERR_DOMAIN 3 /* can't bind to a server for domain */ +#define YPERR_MAP 4 /* no such map in server's domain */ +#define YPERR_KEY 5 /* no such key in map */ +#define YPERR_YPERR 6 /* some internal YP server or client error */ +#define YPERR_RESRC 7 /* local resource allocation failure */ +#define YPERR_NOMORE 8 /* no more records in map database */ +#define YPERR_PMAP 9 /* can't communicate with portmapper */ +#define YPERR_YPBIND 10 /* can't communicate with ypbind */ +#define YPERR_YPSERV 11 /* can't communicate with ypserv */ +#define YPERR_NODOM 12 /* local domain name not set */ +#define YPERR_BADDB 13 /* YP data base is bad */ +#define YPERR_VERS 14 /* YP version mismatch */ +#define YPERR_ACCESS 15 /* access violation */ +#define YPERR_BUSY 16 /* database is busy */ + +/* + * Types of update operations + */ +#define YPOP_CHANGE 1 /* change, do not add */ +#define YPOP_INSERT 2 /* add, do not change */ +#define YPOP_DELETE 3 /* delete this entry */ +#define YPOP_STORE 4 /* add, or change */ + +struct ypall_callback { + int (*foreach)(); /* return non-0 to stop getting called */ + char *data; /* opaque pointer for use of callback fn */ +}; + +int yp_bind __P((char *dom)); +int _yp_dobind __P((char *dom, struct dom_binding **ypdb)); +void yp_unbind __P((char *dom)); +int yp_get_default_domain __P((char **domp)); +int yp_match __P((char *indomain, char *inmap, + const char *inkey, int inkeylen, char **outval, + int *outvallen)); +int yp_first __P((char *indomain, char *inmap, + char **outkey, int *outkeylen, char **outval, + int *outvallen)); +int yp_next __P((char *indomain, char *inmap, + char *inkey, int inkeylen, char **outkey, + int *outkeylen, char **outval, int *outvallen)); +int yp_master __P((char *indomain, char *inmap, char **outname)); +int yp_order __P((char *indomain, char *inmap, int *outorder)); +int yp_all __P((char *indomain, char *inmap, + struct ypall_callback *incallback)); +char * yperr_string __P((int incode)); +int ypprot_err __P((unsigned int incode)); + +#endif /* _YPCLNT_H_ */ + diff --git a/include/rpcsvc/yppasswd.x b/include/rpcsvc/yppasswd.x new file mode 100644 index 0000000000..03d6701daa --- /dev/null +++ b/include/rpcsvc/yppasswd.x @@ -0,0 +1,69 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * YP password update protocol + * Requires unix authentication + */ + +#ifndef RPC_HDR +%#ifndef lint +%/*static char sccsid[] = "from: @(#)yppasswd.x 1.1 87/04/13 Copyr 1987 Sun Micro";*/ +%/*static char sccsid[] = "from: @(#)yppasswd.x 2.1 88/08/01 4.0 RPCSRC";*/ +%static char rcsid[] = "$Id: yppasswd.x,v 1.5 1993/08/03 21:53:34 mycroft Exp $"; +%#endif /* not lint */ +#endif + +program YPPASSWDPROG { + version YPPASSWDVERS { + /* + * Update my passwd entry + */ + int + YPPASSWDPROC_UPDATE(yppasswd) = 1; + } = 1; +} = 100009; + + +struct x_passwd { + string pw_name<>; /* username */ + string pw_passwd<>; /* encrypted password */ + int pw_uid; /* user id */ + int pw_gid; /* group id */ + string pw_gecos<>; /* in real life name */ + string pw_dir<>; /* home directory */ + string pw_shell<>; /* default shell */ +}; + +struct yppasswd { + string oldpass<>; /* unencrypted old password */ + x_passwd newpw; /* new passwd entry */ +}; + + diff --git a/include/setjmp.h b/include/setjmp.h index 685182161d..99b9790dc5 100644 --- a/include/setjmp.h +++ b/include/setjmp.h @@ -52,15 +52,12 @@ #define _JBLEN 10 #endif +typedef int jmp_buf[_JBLEN]; + #ifndef _ANSI_SOURCE -/* - * WARNING: sigsetjmp() isn't supported yet, this is a placeholder. - */ typedef int sigjmp_buf[_JBLEN + 1]; #endif /* not ANSI */ -typedef int jmp_buf[_JBLEN]; - #include __BEGIN_DECLS @@ -68,9 +65,6 @@ int setjmp __P((jmp_buf)); void longjmp __P((jmp_buf, int)); #ifndef _ANSI_SOURCE -/* - * WARNING: sigsetjmp() isn't supported yet, this is a placeholder. - */ int sigsetjmp __P((sigjmp_buf, int)); void siglongjmp __P((sigjmp_buf, int)); #endif /* not ANSI */ diff --git a/include/stab.h b/include/stab.h index 8cb8902b11..f83ae19c71 100644 --- a/include/stab.h +++ b/include/stab.h @@ -33,6 +33,9 @@ * @(#)stab.h 5.2 (Berkeley) 4/4/91 */ +#ifndef _STAB_H_ +#define _STAB_H_ + /* * The following are symbols used by various debuggers and by the Pascal * compiler. Each of them must have one (or more) of the bits defined by @@ -65,3 +68,5 @@ #define N_ECOMM 0xe4 /* end common */ #define N_ECOML 0xe8 /* end common (local name) */ #define N_LENG 0xfe /* length of preceding entry */ + +#endif /* _STAB_H_ */ diff --git a/include/stdio.h b/include/stdio.h index 5489652a63..d9fbe98eb2 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -231,7 +231,7 @@ int scanf __P((const char *, ...)); void setbuf __P((FILE *, char *)); int setvbuf __P((FILE *, char *, int, size_t)); int sprintf __P((char *, const char *, ...)); -int sscanf __P((char *, const char *, ...)); +int sscanf __P((const char *, const char *, ...)); FILE *tmpfile __P((void)); char *tmpnam __P((char *)); int ungetc __P((int, FILE *)); @@ -309,8 +309,8 @@ __END_DECLS * define function versions in the C library. */ #define __sgetc(p) (--(p)->_r < 0 ? __srget(p) : (int)(*(p)->_p++)) -#if defined(__GNUC__) && defined(__STDC__) -static inline int __sputc(int _c, FILE *_p) { +#if defined(__GNUC__) +static __inline__ int __sputc(int _c, FILE *_p) { if (--_p->_w >= 0 || (_p->_w >= _p->_lbfsize && (char)_c != '\n')) return (*_p->_p++ = _c); else diff --git a/include/stdlib.h b/include/stdlib.h index 6dc7b67b3c..b1a65e36a1 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -102,6 +102,17 @@ int wctomb __P((char *, wchar_t)); int mbtowc __P((wchar_t *, const char *, size_t)); size_t wcstombs __P((char *, const wchar_t *, size_t)); +/* don't ask me where to put these -- MB XXX */ +double drand48 __P((void)); +double erand48 __P((unsigned short[3])); +long lrand48 __P((void)); +long nrand48 __P((unsigned short[3])); +long mrand48 __P((void)); +long jrand48 __P((unsigned short[3])); +void srand48 __P((long)); +unsigned short *seed48 __P((unsigned short[3])); +void lcong48 __P((unsigned short[7])); + #ifndef _ANSI_SOURCE void cfree __P((void *)); int putenv __P((const char *)); diff --git a/include/unistd.h b/include/unistd.h index 9675bd641c..3e781ce216 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -44,12 +44,6 @@ #define STDOUT_FILENO 1 /* standard output file descriptor */ #define STDERR_FILENO 2 /* standard error file descriptor */ -/* fnmatch(3) defines */ -#define FNM_PATHNAME 0x01 /* match pathnames, not filenames */ -#ifndef _POSIX_SOURCE -#define FNM_QUOTE 0x02 /* escape special chars with \ */ -#endif - #ifndef NULL #define NULL 0 /* null pointer constant */ #endif @@ -123,7 +117,6 @@ int exect __P((const char *, char * const *, char * const *)); int fchdir __P((int)); int fchflags __P((int, long)); int fchown __P((int, int, int)); -int fnmatch __P((const char *, const char *, int)); int fsync __P((int)); int ftruncate __P((int, off_t)); int getdtablesize __P((void)); diff --git a/include/vis.h b/include/vis.h index 72d61146c4..71030b7142 100644 --- a/include/vis.h +++ b/include/vis.h @@ -74,7 +74,7 @@ #include __BEGIN_DECLS -char *vis __P((char *, char, int, char)); +char *vis __P((char *, int, int, int)); int strvis __P((char *, const char *, int)); int strvisx __P((char *, const char *, size_t, int)); int strunvis __P((char *, const char *)); diff --git a/lib/Makefile b/lib/Makefile index b21b2ec801..8995044c55 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,6 +1,12 @@ # @(#)Makefile 5.25.1.1 (Berkeley) 5/7/91 -SUBDIR= csu.${MACHINE} libc libcurses libg++ libm \ - libresolv librpc/rpc libtelnet libterm libutil liby +SUBDIR= csu.${MACHINE} libc libcurses libm \ + libresolv librpc librpcsvc libtelnet libterm libutil liby + +.if exists(libcrypt) +.if !defined(NOCRYPT) +SUBDIR+= libcrypt +.endif +.endif .include diff --git a/lib/csu.i386/Makefile b/lib/csu.i386/Makefile index bb1c66e8ed..aeb328edc7 100644 --- a/lib/csu.i386/Makefile +++ b/lib/csu.i386/Makefile @@ -25,7 +25,7 @@ gmon.o: gmon.c gmon.h mv a.out ${.TARGET} install: - install -o ${BINOWN} -g ${BINGRP} -m 444 ${OBJS} \ + install ${COPY} -o ${BINOWN} -g ${BINGRP} -m 444 ${OBJS} \ ${DESTDIR}/usr/lib depend lint tags: diff --git a/lib/csu.i386/crt0.c b/lib/csu.i386/crt0.c index 363098b326..7c3cef10e6 100644 --- a/lib/csu.i386/crt0.c +++ b/lib/csu.i386/crt0.c @@ -45,11 +45,22 @@ static char sccsid[] = "@(#)crt0.c 5.7 (Berkeley) 7/3/91"; * ebp, which points to the base of the kernel calling frame. */ +#include +#include +#include + char **environ = (char **)0; +static char empty[1]; +char *__progname = empty; int errno = 0; asm(".text"); asm(".long 0xc000c000"); +#if 1 /* more needed for alignment on i486/gcc-2.3.3 */ +asm(".long 0xc000c000"); +asm(".long 0xc000c000"); +asm(".long 0xc000c000"); +#endif extern unsigned char etext; extern unsigned char eprol asm ("eprol"); @@ -90,6 +101,12 @@ asm("eprol:"); atexit(_mcleanup); monstartup(&eprol, &etext); #endif MCRT0 + errno = 0; + if (argv[0]) + if ((__progname = strrchr(argv[0], '/')) == NULL) + __progname = argv[0]; + else + ++__progname; exit(main(kfp->kargc, argv, environ)); } diff --git a/lib/libc/compat-43/Makefile.inc b/lib/libc/compat-43/Makefile.inc index 34afc6ff64..63c330fcd8 100644 --- a/lib/libc/compat-43/Makefile.inc +++ b/lib/libc/compat-43/Makefile.inc @@ -5,4 +5,5 @@ SRCS+= creat.c getwd.c killpg.c setpgrp.c sigcompat.c -MAN2+= creat.0 killpg.0 sigblock.0 sigpause.0 sigsetmask.0 sigvec.0 +MAN2+= compat-43/creat.2 compat-43/killpg.2 compat-43/sigblock.2 \ + compat-43/sigpause.2 compat-43/sigsetmask.2 compat-43/sigvec.2 diff --git a/lib/libc/db/Makefile.inc b/lib/libc/db/Makefile.inc index 56f305ff39..a8bae3a861 100644 --- a/lib/libc/db/Makefile.inc +++ b/lib/libc/db/Makefile.inc @@ -1,8 +1,8 @@ -# @(#)Makefile.inc 5.1 (Berkeley) 2/24/91 +# @(#)Makefile.inc 8.1 (Berkeley) 6/2/93 # -.PATH: ${.CURDIR}/db - .include "${.CURDIR}/db/btree/Makefile.inc" +.include "${.CURDIR}/db/db/Makefile.inc" .include "${.CURDIR}/db/hash/Makefile.inc" - -MAN3+= db.0 +.include "${.CURDIR}/db/man/Makefile.inc" +.include "${.CURDIR}/db/mpool/Makefile.inc" +.include "${.CURDIR}/db/recno/Makefile.inc" diff --git a/lib/libc/db/PORT/Makefile b/lib/libc/db/PORT/Makefile new file mode 100644 index 0000000000..9b0b56b4bf --- /dev/null +++ b/lib/libc/db/PORT/Makefile @@ -0,0 +1,55 @@ +# @(#)Makefile 8.1 (Berkeley) 6/2/93 + +LIBDB= libdb.a +OBJ1= bt_close.o bt_conv.o bt_debug.o bt_delete.o bt_get.o bt_open.o \ + bt_overflow.o bt_page.o bt_put.o bt_search.o bt_seq.o bt_split.o \ + bt_stack.o bt_utils.o +OBJ2= db.o +OBJ3= hash.o hash_bigkey.o hash_buf.o hash_func.o hash_log2.o hash_page.o \ + hsearch.o ndbm.o +OBJ4= mpool.o +OBJ5= rec_close.o rec_delete.o rec_get.o rec_open.o rec_put.o rec_search.o \ + rec_seq.o rec_utils.o + +# If you don't have memmove(3), add memmove.o to the MISC line. +# +# If you don't have mktemp(3) or mkstemp(3), add mktemp.o to the MISC line. +# +# If realloc(3) of a NULL pointer on your system isn't the same as +# a malloc(3) call, add realloc.o to the MISC line. +# +# If you don't have snprintf/vsnprintf(3), add snprintf.o to the MISC line. +# Note, this depends you your having vsprintf(3) -- if you don't, there's +# no workaround other than changing the source code to not use the snprintf +# calls. If you have to make that change, check to see if your vsprintf +# returns a length or a char *; if it's the latter, set VSPRINTF_CHARSTAR +# in the include/compat.h file. +MISC= + +${LIBDB}: ${OBJ1} ${OBJ2} ${OBJ3} ${OBJ4} ${OBJ5} ${MISC} + ar cq $@ \ + `lorder ${OBJ1} ${OBJ2} ${OBJ3} ${OBJ4} ${OBJ5} ${MISC} | tsort` + ranlib $@ + +${OBJ1}: + ${CC} -c -O -I. -Iinclude -I../btree ../btree/*.c +${OBJ2}: + ${CC} -c -O -I. -Iinclude -I../db ../db/*.c +${OBJ3}: + ${CC} -c -O -I. -Iinclude -I../hash ../hash/*.c +${OBJ4}: + ${CC} -c -O -I. -Iinclude -I../mpool ../mpool/*.c +${OBJ5}: + ${CC} -c -O -I. -Iinclude -I../recno ../recno/*.c + +memmove.o: + ${CC} -DMEMMOVE -c -O -I. -Iinclude clib/memmove.c +mktemp.o: + ${CC} -c -O -I. -Iinclude clib/mktemp.c +realloc.o: + ${CC} -c -O -I. -Iinclude clib/realloc.c +snprintf.o: + ${CC} -c -O -I. -Iinclude clib/snprintf.c + +clean: + rm -f ${LIBDB} ${OBJ1} ${OBJ2} ${OBJ3} ${OBJ4} ${OBJ5} ${MISC} diff --git a/lib/libc/db/PORT/README b/lib/libc/db/PORT/README new file mode 100644 index 0000000000..cbe7f11362 --- /dev/null +++ b/lib/libc/db/PORT/README @@ -0,0 +1,60 @@ +# @(#)README 8.1 (Berkeley) 6/2/93 + +This is the directory to use for creating a library of the dbopen(3) +routines. The Makefile builds the base system. By changing it and +the compat.h file, you should be able to pick and choose the various +things your system needs to make libdb run. + +The knobs that you may have to turn: + +In the Makefile: + If you don't have mktemp(3) or mkstemp(3) on your system, add + "mktemp.o" to the MISC list. + + If you don't have snprintf(3) on your system, add snprintf.o + to the MISC list. + + If you don't have memmove(3), add memmove.o to the MISC list. + +In include/compat.h: + Before attempting to build this library, you should skim through + the compat.h file, and adjust it as necessary for your system. + It's possible to use the #ifndef construct to figure out if a + #ifdef has been set, but C provides no similar method to figure + out if a typedef has been done. All of the typedef's are grouped + at the top of compat.h, your compile errors will tell you which + ones you need. + + Particularly subtle problems can occur if you don't have the + BYTE_ORDER set right or if the realloc define is wrong. + +Some other problems: + You may see warning messages about illegal pointer combinations. + It's because systems prototype malloc, calloc and realloc in + different places. If you want to stop the warnings, find out + where your system prototypes them, and include it in compat.h, + or, just prototype them yourself. + +To install: + Programs using the dbopen(3) interface have to include db.h. + To install the library, you'll need to put db.h (found in the + directory PORT/include) and the library libdb.a in some place + accesible to your program. + +Hints: + SunOS 4.1.2 works with ``MISC=realloc.o snprintf.o memmove.o'' + and the include/compat.h realloc ``#if 0'' changed to ``#if 1''. + SunOS gives a bunch of warning messages on the malloc family + routines. + + Ultrix 4.2A works with ``MISC=realloc.o snprintf.o memmove.o'' + and the include/compat.h realloc ``#if 0'' changed to ``#if 1'' + and BYTE_ORDER changed from BIG_ENDIAN to LITTLE_ENDIAN. + Ultrix does not give warning messages on the malloc family + routines. + + The recno routines require the mmap(2) call. It wouldn't be + difficult to port this package to a system without mmap. Change + rec_open.c to do slightly different initialization and to write + two new routines for rec_get.c that handle regular files using + reads and writes. diff --git a/lib/libc/db/PORT/clib/memmove.c b/lib/libc/db/PORT/clib/memmove.c new file mode 100644 index 0000000000..f90b09c9b7 --- /dev/null +++ b/lib/libc/db/PORT/clib/memmove.c @@ -0,0 +1,139 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bcopy.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +/* + * sizeof(word) MUST BE A POWER OF TWO + * SO THAT wmask BELOW IS ALL ONES + */ +typedef int word; /* "word" used for optimal copy speed */ + +#define wsize sizeof(word) +#define wmask (wsize - 1) + +/* + * Copy a block of memory, handling overlap. + * This is the routine that actually implements + * (the portable versions of) bcopy, memcpy, and memmove. + */ +#ifdef MEMCOPY +void * +memcpy(dst0, src0, length) +#else +#ifdef MEMMOVE +void * +memmove(dst0, src0, length) +#else +void +bcopy(src0, dst0, length) +#endif +#endif + void *dst0; + const void *src0; + register size_t length; +{ + register char *dst = dst0; + register const char *src = src0; + register size_t t; + + if (length == 0 || dst == src) /* nothing to do */ + goto done; + + /* + * Macros: loop-t-times; and loop-t-times, t>0 + */ +#define TLOOP(s) if (t) TLOOP1(s) +#define TLOOP1(s) do { s; } while (--t) + + if ((unsigned long)dst < (unsigned long)src) { + /* + * Copy forward. + */ + t = (int)src; /* only need low bits */ + if ((t | (int)dst) & wmask) { + /* + * Try to align operands. This cannot be done + * unless the low bits match. + */ + if ((t ^ (int)dst) & wmask || length < wsize) + t = length; + else + t = wsize - (t & wmask); + length -= t; + TLOOP1(*dst++ = *src++); + } + /* + * Copy whole words, then mop up any trailing bytes. + */ + t = length / wsize; + TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize); + t = length & wmask; + TLOOP(*dst++ = *src++); + } else { + /* + * Copy backwards. Otherwise essentially the same. + * Alignment works as before, except that it takes + * (t&wmask) bytes to align, not wsize-(t&wmask). + */ + src += length; + dst += length; + t = (int)src; + if ((t | (int)dst) & wmask) { + if ((t ^ (int)dst) & wmask || length <= wsize) + t = length; + else + t &= wmask; + length -= t; + TLOOP1(*--dst = *--src); + } + t = length / wsize; + TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src); + t = length & wmask; + TLOOP(*--dst = *--src); + } +done: +#if defined(MEMCOPY) || defined(MEMMOVE) + return (dst0); +#else + return; +#endif +} diff --git a/lib/libc/db/PORT/clib/mktemp.c b/lib/libc/db/PORT/clib/mktemp.c new file mode 100644 index 0000000000..6cedd6a6be --- /dev/null +++ b/lib/libc/db/PORT/clib/mktemp.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include +#include +#include + +static int _gettemp(); + +mkstemp(path) + char *path; +{ + int fd; + + return (_gettemp(path, &fd) ? fd : -1); +} + +char * +mktemp(path) + char *path; +{ + return(_gettemp(path, (int *)NULL) ? path : (char *)NULL); +} + +static +_gettemp(path, doopen) + char *path; + register int *doopen; +{ + extern int errno; + register char *start, *trv; + struct stat sbuf; + u_int pid; + + pid = getpid(); + for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ + while (*--trv == 'X') { + *trv = (pid % 10) + '0'; + pid /= 10; + } + + /* + * check the target directory; if you have six X's and it + * doesn't exist this runs for a *very* long time. + */ + for (start = trv + 1;; --trv) { + if (trv <= path) + break; + if (*trv == '/') { + *trv = '\0'; + if (stat(path, &sbuf)) + return(0); + if (!S_ISDIR(sbuf.st_mode)) { + errno = ENOTDIR; + return(0); + } + *trv = '/'; + break; + } + } + + for (;;) { + if (doopen) { + if ((*doopen = + open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0) + return(1); + if (errno != EEXIST) + return(0); + } + else if (stat(path, &sbuf)) + return(errno == ENOENT ? 1 : 0); + + /* tricky little algorithm for backward compatibility */ + for (trv = start;;) { + if (!*trv) + return(0); + if (*trv == 'z') + *trv++ = 'a'; + else { + if (isdigit(*trv)) + *trv = 'a'; + else + ++*trv; + break; + } + } + } + /*NOTREACHED*/ +} diff --git a/lib/libc/db/PORT/clib/realloc.c b/lib/libc/db/PORT/clib/realloc.c new file mode 100644 index 0000000000..11e5cd28c5 --- /dev/null +++ b/lib/libc/db/PORT/clib/realloc.c @@ -0,0 +1,11 @@ +#include + +#include + +void * +__fix_realloc(p, n) + void *p; + size_t n; +{ + return (p == 0 ? malloc(n) : realloc(p, n)); +} diff --git a/lib/libc/db/PORT/clib/snprintf.c b/lib/libc/db/PORT/clib/snprintf.c new file mode 100644 index 0000000000..2863fa28d6 --- /dev/null +++ b/lib/libc/db/PORT/clib/snprintf.c @@ -0,0 +1,54 @@ +#include +#include + +#include + +#ifdef __STDC__ +#include +#else +#include +#endif + +int +#ifdef __STDC__ +snprintf(char *str, size_t n, const char *fmt, ...) +#else +snprintf(str, n, fmt, va_alist) + char *str; + size_t n; + const char *fmt; + va_dcl +#endif +{ + va_list ap; + char *rp; + int rval; +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif +#ifdef VSPRINTF_CHARSTAR + rp = vsprintf(str, fmt, ap); + va_end(ap); + return (strlen(rp)); +#else + rval = vsprintf(str, fmt, ap); + va_end(ap); + return (rval); +#endif +} + +int +vsnprintf(str, n, fmt, ap) + char *str; + size_t n; + const char *fmt; + va_list ap; +{ +#ifdef VSPRINTF_CHARSTAR + return (strlen(vsprintf(str, fmt, ap))); +#else + return (vsprintf(str, fmt, ap)); +#endif +} diff --git a/lib/libc/db/PORT/include/cdefs.h b/lib/libc/db/PORT/include/cdefs.h new file mode 100644 index 0000000000..a04665e30e --- /dev/null +++ b/lib/libc/db/PORT/include/cdefs.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cdefs.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _CDEFS_H_ +#define _CDEFS_H_ + +#if defined(__cplusplus) +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS }; +#else +#define __BEGIN_DECLS +#define __END_DECLS +#endif + +/* + * The __CONCAT macro is used to concatenate parts of symbol names, e.g. + * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo. + * The __CONCAT macro is a bit tricky -- make sure you don't put spaces + * in between its arguments. __CONCAT can also concatenate double-quoted + * strings produced by the __STRING macro, but this only works with ANSI C. + */ +#if defined(__STDC__) || defined(__cplusplus) +#define __P(protos) protos /* full-blown ANSI C */ +#define __CONCAT(x,y) x ## y +#define __STRING(x) #x + +#else /* !(__STDC__ || __cplusplus) */ +#define __P(protos) () /* traditional C preprocessor */ +#define __CONCAT(x,y) x/**/y +#define __STRING(x) "x" + +#ifdef __GNUC__ +#define const __const /* GCC: ANSI C with -traditional */ +#define inline __inline +#define signed __signed +#define volatile __volatile + +#else /* !__GNUC__ */ +#define const /* delete ANSI C keywords */ +#define inline +#define signed +#define volatile +#endif /* !__GNUC__ */ +#endif /* !(__STDC__ || __cplusplus) */ + +/* + * GCC has extensions for declaring functions as `pure' (always returns + * the same value given the same inputs, i.e., has no external state and + * no side effects) and `dead' (nonreturning). These mainly affect + * optimization and warnings. Unfortunately, GCC complains if these are + * used under strict ANSI mode (`gcc -ansi -pedantic'), hence we need to + * define them only if compiling without this. + */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define __dead __volatile +#define __pure __const +#else +#define __dead +#define __pure +#endif + +#endif /* !_CDEFS_H_ */ diff --git a/lib/libc/db/PORT/include/compat.h b/lib/libc/db/PORT/include/compat.h new file mode 100644 index 0000000000..bb3f914667 --- /dev/null +++ b/lib/libc/db/PORT/include/compat.h @@ -0,0 +1,217 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)compat.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _COMPAT_H_ +#define _COMPAT_H_ + +/* + * If your system doesn't typedef u_long, u_short, or u_char, change + * the 0 to a 1. + */ +#if 0 +typedef unsigned long u_long; +typedef unsigned short u_short; +typedef unsigned char u_char; +#endif + +/* If your system doesn't typedef size_t, change the 0 to a 1. */ +#if 0 +typedef unsigned int size_t; +#define SIZE_T_MAX UINT_MAX +#endif + +/* + * If your system doesn't specify a max size for a SIZE_T, check + * to make sure this is the right one. + */ +#ifndef SIZE_T_MAX +#define SIZE_T_MAX UINT_MAX +#endif + +/* + * If your system doesn't have the POSIX type for a signal mask, + * change the 0 to a 1. + */ +#if 0 +typedef unsigned int sigset_t; +#endif + +/* + * If your system's vsprintf returns a char *, not an int, + * change the 0 to a 1. + */ +#if 0 +#define VSPRINTF_CHARSTAR +#endif + +/* + * If you don't have POSIX 1003.1 signals, the signal code surrounding the + * temporary file creation is intended to block all of the possible signals + * long enough to create the file and unlink it. All of this stuff is + * intended to use old-style BSD calls to fake POSIX 1003.1 calls. + */ +#ifdef NO_POSIX_SIGNALS +#define sigemptyset(set) (*(set) = 0) +#define sigfillset(set) (*(set) = ~(sigset_t)0, 0) +#define sigaddset(set,signo) (*(set) |= sigmask(signo), 0) +#define sigdelset(set,signo) (*(set) &= ~sigmask(signo), 0) +#define sigismember(set,signo) ((*(set) & sigmask(signo)) != 0) + +#define SIG_BLOCK 1 +#define SIG_UNBLOCK 2 +#define SIG_SETMASK 3 + +static int __sigtemp; /* For the use of sigprocmask */ + +#define sigprocmask(how,set,oset) \ + ((__sigtemp = (((how) == SIG_BLOCK) ? \ + sigblock(0) | *(set) : (((how) == SIG_UNBLOCK) ? \ + sigblock(0) & ~(*(set)) : ((how) == SIG_SETMASK ? \ + *(set) : sigblock(0))))), ((oset) ? \ + (*(oset) = sigsetmask(__sigtemp)) : sigsetmask(__sigtemp)), 0) +#endif + +/* + * If realloc(3) of a NULL pointer on your system isn't the same as + * a malloc(3) call, change the 0 to a 1, and add realloc.o to the + * MISC line in your Makefile. + */ +#if 0 +#define realloc __fix_realloc +#endif + +/* + * If your system doesn't have an include file with the appropriate + * byte order set, make sure you specify the correct one. + */ +#ifndef BYTE_ORDER +#define LITTLE_ENDIAN 1234 /* LSB first: i386, vax */ +#define BIG_ENDIAN 4321 /* MSB first: 68000, ibm, net */ +#define BYTE_ORDER BIG_ENDIAN /* Set for your system. */ +#endif + +#if defined(SYSV) || defined(SYSTEM5) +#define index(a, b) strchr(a, b) +#define rindex(a, b) strrchr(a, b) +#define bzero(a, b) memset(a, 0, b) +#define bcmp(a, b, n) memcmp(a, b, n) +#define bcopy(a, b, n) memmove(b, a, n) +#endif + +#if defined(BSD) || defined(BSD4_3) +#define strchr(a, b) index(a, b) +#define strrchr(a, b) rindex(a, b) +#define memcmp(a, b, n) bcmp(a, b, n) +#define memmove(a, b, n) bcopy(b, a, n) +#endif + +/* + * 32-bit machine. The db routines are theoretically independent of + * the size of u_shorts and u_longs, but I don't know that anyone has + * ever actually tried it. At a minimum, change the following #define's + * if you are trying to compile on a different type of system. + */ +#ifndef USHRT_MAX +#define USHRT_MAX 0xFFFF +#define ULONG_MAX 0xFFFFFFFF +#endif + +/* POSIX 1003.1 access mode mask. */ +#ifndef O_ACCMODE +#define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) +#endif + +/* POSIX 1003.2 RE limit. */ +#ifndef _POSIX2_RE_DUP_MAX +#define _POSIX2_RE_DUP_MAX 255 +#endif + +/* + * If you can't provide lock values in the open(2) call. Note, this + * allows races to happen. + */ +#ifndef O_EXLOCK +#define O_EXLOCK 0 +#endif + +#ifndef O_SHLOCK +#define O_SHLOCK 0 +#endif + +#ifndef EFTYPE +#define EFTYPE EINVAL /* POSIX 1003.1 format errno. */ +#endif + +#ifndef WCOREDUMP /* 4.4BSD extension */ +#define WCOREDUMP(a) 0 +#endif + +#ifndef STDERR_FILENO +#define STDIN_FILENO 0 /* ANSI C #defines */ +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#endif + +#ifndef SEEK_END +#define SEEK_SET 0 /* POSIX 1003.1 seek values */ +#define SEEK_CUR 1 +#define SEEK_END 2 +#endif + +#ifndef S_ISLNK /* BSD POSIX 1003.1 extensions */ +#define S_ISLNK(m) ((m & 0170000) == 0120000) +#define S_ISSOCK(m) ((m & 0170000) == 0140000) +#endif + +#ifndef _POSIX2_RE_DUP_MAX +#define _POSIX2_RE_DUP_MAX 255 +#endif + +#ifndef NULL /* ANSI C #defines NULL everywhere. */ +#define NULL 0 +#endif + +#ifndef MAX +#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a)) +#endif +#ifndef MIN +#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b)) +#endif + +#ifndef _BSD_VA_LIST_ +#define _BSD_VA_LIST_ char * +#endif + +#endif /* !_COMPAT_H_ */ diff --git a/lib/libc/db/PORT/include/db.h b/lib/libc/db/PORT/include/db.h new file mode 100644 index 0000000000..2b7de273a5 --- /dev/null +++ b/lib/libc/db/PORT/include/db.h @@ -0,0 +1,194 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)db.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _DB_H_ +#define _DB_H_ + +#include +#include +#include + +#define RET_ERROR -1 /* Return values. */ +#define RET_SUCCESS 0 +#define RET_SPECIAL 1 + +#define MAX_PAGE_NUMBER ULONG_MAX /* >= # of pages in a file */ +typedef u_long pgno_t; +#define MAX_PAGE_OFFSET USHRT_MAX /* >= # of bytes in a page */ +typedef u_short indx_t; +#define MAX_REC_NUMBER ULONG_MAX /* >= # of records in a tree */ +typedef u_long recno_t; + +/* Key/data structure -- a Data-Base Thang. */ +typedef struct { + void *data; /* data */ + size_t size; /* data length */ +} DBT; + +/* Routine flags. */ +#define R_CURSOR 1 /* del, put, seq */ +#define __R_UNUSED 2 /* UNUSED */ +#define R_FIRST 3 /* seq */ +#define R_IAFTER 4 /* put (RECNO) */ +#define R_IBEFORE 5 /* put (RECNO) */ +#define R_LAST 6 /* seq (BTREE, RECNO) */ +#define R_NEXT 7 /* seq */ +#define R_NOOVERWRITE 8 /* put */ +#define R_PREV 9 /* seq (BTREE, RECNO) */ +#define R_SETCURSOR 10 /* put (RECNO) */ +#define R_RECNOSYNC 11 /* sync (RECNO) */ + +typedef enum { DB_BTREE, DB_HASH, DB_RECNO } DBTYPE; + +#define __USE_OPEN_FLAGS \ + (O_CREAT|O_EXCL|O_EXLOCK|O_RDONLY|O_RDWR|O_SHLOCK|O_TRUNC) + +/* Access method description structure. */ +typedef struct __db { + DBTYPE type; /* underlying db type */ + int (*close) __P((struct __db *)); + int (*del) __P((const struct __db *, const DBT *, u_int)); + int (*fd) __P((const struct __db *)); + int (*get) __P((const struct __db *, const DBT *, DBT *, u_int)); + int (*put) __P((const struct __db *, DBT *, const DBT *, u_int)); + int (*seq) __P((const struct __db *, DBT *, DBT *, u_int)); + int (*sync) __P((const struct __db *, u_int)); + void *internal; /* access method private */ +} DB; + +#define BTREEMAGIC 0x053162 +#define BTREEVERSION 3 + +/* Structure used to pass parameters to the btree routines. */ +typedef struct { +#define R_DUP 0x01 /* duplicate keys */ + u_long flags; + int cachesize; /* bytes to cache */ + int maxkeypage; /* maximum keys per page */ + int minkeypage; /* minimum keys per page */ + int psize; /* page size */ + /* comparison, prefix functions */ + int (*compare) __P((const DBT *, const DBT *)); + int (*prefix) __P((const DBT *, const DBT *)); + int lorder; /* byte order */ +} BTREEINFO; + +#define HASHMAGIC 0x061561 +#define HASHVERSION 2 + +/* Structure used to pass parameters to the hashing routines. */ +typedef struct { + int bsize; /* bucket size */ + int ffactor; /* fill factor */ + int nelem; /* number of elements */ + int cachesize; /* bytes to cache */ + /* hash function */ + int (*hash) __P((const void *, size_t)); + int lorder; /* byte order */ +} HASHINFO; + +/* Structure used to pass parameters to the record routines. */ +typedef struct { +#define R_FIXEDLEN 0x01 /* fixed-length records */ +#define R_NOKEY 0x02 /* key not required */ +#define R_SNAPSHOT 0x04 /* snapshot the input */ + u_long flags; + int cachesize; /* bytes to cache */ + int psize; /* page size */ + int lorder; /* byte order */ + size_t reclen; /* record length (fixed-length records) */ + u_char bval; /* delimiting byte (variable-length records */ + char *bfname; /* btree file name */ +} RECNOINFO; + +/* + * Little endian <==> big endian long swap macros. + * BLSWAP swap a memory location + * BLPSWAP swap a referenced memory location + * BLSWAP_COPY swap from one location to another + */ +#define BLSWAP(a) { \ + u_long _tmp = a; \ + ((char *)&a)[0] = ((char *)&_tmp)[3]; \ + ((char *)&a)[1] = ((char *)&_tmp)[2]; \ + ((char *)&a)[2] = ((char *)&_tmp)[1]; \ + ((char *)&a)[3] = ((char *)&_tmp)[0]; \ +} +#define BLPSWAP(a) { \ + u_long _tmp = *(u_long *)a; \ + ((char *)a)[0] = ((char *)&_tmp)[3]; \ + ((char *)a)[1] = ((char *)&_tmp)[2]; \ + ((char *)a)[2] = ((char *)&_tmp)[1]; \ + ((char *)a)[3] = ((char *)&_tmp)[0]; \ +} +#define BLSWAP_COPY(a, b) { \ + ((char *)&(b))[0] = ((char *)&(a))[3]; \ + ((char *)&(b))[1] = ((char *)&(a))[2]; \ + ((char *)&(b))[2] = ((char *)&(a))[1]; \ + ((char *)&(b))[3] = ((char *)&(a))[0]; \ +} + +/* + * Little endian <==> big endian short swap macros. + * BSSWAP swap a memory location + * BSPSWAP swap a referenced memory location + * BSSWAP_COPY swap from one location to another + */ +#define BSSWAP(a) { \ + u_short _tmp = a; \ + ((char *)&a)[0] = ((char *)&_tmp)[1]; \ + ((char *)&a)[1] = ((char *)&_tmp)[0]; \ +} +#define BSPSWAP(a) { \ + u_short _tmp = *(u_short *)a; \ + ((char *)a)[0] = ((char *)&_tmp)[1]; \ + ((char *)a)[1] = ((char *)&_tmp)[0]; \ +} +#define BSSWAP_COPY(a, b) { \ + ((char *)&(b))[0] = ((char *)&(a))[1]; \ + ((char *)&(b))[1] = ((char *)&(a))[0]; \ +} + +__BEGIN_DECLS +DB *dbopen __P((const char *, int, int, DBTYPE, const void *)); + +#ifdef __DBINTERFACE_PRIVATE +DB *__bt_open __P((const char *, int, int, const BTREEINFO *)); +DB *__hash_open __P((const char *, int, int, const HASHINFO *)); +DB *__rec_open __P((const char *, int, int, const RECNOINFO *)); +void __dbpanic __P((DB *dbp)); +#endif +__END_DECLS +#endif /* !_DB_H_ */ diff --git a/lib/libc/db/PORT/include/mpool.h b/lib/libc/db/PORT/include/mpool.h new file mode 100644 index 0000000000..910e0782aa --- /dev/null +++ b/lib/libc/db/PORT/include/mpool.h @@ -0,0 +1,135 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mpool.h 8.1 (Berkeley) 6/2/93 + */ + +/* + * The memory pool scheme is a simple one. Each in memory page is referenced + * by a bucket which is threaded in three ways. All active pages are threaded + * on a hash chain (hashed by the page number) and an lru chain. Inactive + * pages are threaded on a free chain. Each reference to a memory pool is + * handed an MPOOL which is the opaque cookie passed to all of the memory + * routines. + */ +#define HASHSIZE 128 +#define HASHKEY(pgno) ((pgno - 1) % HASHSIZE) + +/* The BKT structures are the elements of the lists. */ +typedef struct BKT { + struct BKT *hnext; /* next hash bucket */ + struct BKT *hprev; /* previous hash bucket */ + struct BKT *cnext; /* next free/lru bucket */ + struct BKT *cprev; /* previous free/lru bucket */ + void *page; /* page */ + pgno_t pgno; /* page number */ + +#define MPOOL_DIRTY 0x01 /* page needs to be written */ +#define MPOOL_PINNED 0x02 /* page is pinned into memory */ + unsigned long flags; /* flags */ +} BKT; + +/* The BKTHDR structures are the heads of the lists. */ +typedef struct BKTHDR { + struct BKT *hnext; /* next hash bucket */ + struct BKT *hprev; /* previous hash bucket */ + struct BKT *cnext; /* next free/lru bucket */ + struct BKT *cprev; /* previous free/lru bucket */ +} BKTHDR; + +typedef struct MPOOL { + BKTHDR free; /* The free list. */ + BKTHDR lru; /* The LRU list. */ + BKTHDR hashtable[HASHSIZE]; /* Hashed list by page number. */ + pgno_t curcache; /* Current number of cached pages. */ + pgno_t maxcache; /* Max number of cached pages. */ + pgno_t npages; /* Number of pages in the file. */ + u_long pagesize; /* File page size. */ + int fd; /* File descriptor. */ + /* Page in conversion routine. */ + void (*pgin) __P((void *, pgno_t, void *)); + /* Page out conversion routine. */ + void (*pgout) __P((void *, pgno_t, void *)); + void *pgcookie; /* Cookie for page in/out routines. */ +#ifdef STATISTICS + unsigned long cachehit; + unsigned long cachemiss; + unsigned long pagealloc; + unsigned long pageflush; + unsigned long pageget; + unsigned long pagenew; + unsigned long pageput; + unsigned long pageread; + unsigned long pagewrite; +#endif +} MPOOL; + +#ifdef __MPOOLINTERFACE_PRIVATE +/* Macros to insert/delete into/from hash chain. */ +#define rmhash(bp) { \ + (bp)->hprev->hnext = (bp)->hnext; \ + (bp)->hnext->hprev = (bp)->hprev; \ +} +#define inshash(bp, pg) { \ + hp = &mp->hashtable[HASHKEY(pg)]; \ + (bp)->hnext = hp->hnext; \ + (bp)->hprev = (struct BKT *)hp; \ + hp->hnext->hprev = (bp); \ + hp->hnext = (bp); \ +} + +/* Macros to insert/delete into/from lru and free chains. */ +#define rmchain(bp) { \ + (bp)->cprev->cnext = (bp)->cnext; \ + (bp)->cnext->cprev = (bp)->cprev; \ +} +#define inschain(bp, dp) { \ + (bp)->cnext = (dp)->cnext; \ + (bp)->cprev = (struct BKT *)(dp); \ + (dp)->cnext->cprev = (bp); \ + (dp)->cnext = (bp); \ +} +#endif + +__BEGIN_DECLS +MPOOL *mpool_open __P((DBT *, int, pgno_t, pgno_t)); +void mpool_filter __P((MPOOL *, void (*)(void *, pgno_t, void *), + void (*)(void *, pgno_t, void *), void *)); +void *mpool_new __P((MPOOL *, pgno_t *)); +void *mpool_get __P((MPOOL *, pgno_t, u_int)); +int mpool_put __P((MPOOL *, void *, u_int)); +int mpool_sync __P((MPOOL *)); +int mpool_close __P((MPOOL *)); +#ifdef STATISTICS +void mpool_stat __P((MPOOL *)); +#endif +__END_DECLS diff --git a/lib/libc/db/PORT/include/ndbm.h b/lib/libc/db/PORT/include/ndbm.h new file mode 100644 index 0000000000..a545bca132 --- /dev/null +++ b/lib/libc/db/PORT/include/ndbm.h @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ndbm.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _NDBM_H_ +#define _NDBM_H_ + +#include + +/* Map dbm interface onto db(3). */ +#define DBM_RDONLY O_RDONLY + +/* Flags to dbm_store(). */ +#define DBM_INSERT 0 +#define DBM_REPLACE 1 + +/* + * The db(3) support for ndbm(3) always appends this suffix to the + * file name to avoid overwriting the user's original database. + */ +#define DBM_SUFFIX ".db" + +typedef struct { + char *dptr; + int dsize; +} datum; + +typedef DB DBM; +#define dbm_pagfno(a) DBM_PAGFNO_NOT_AVAILABLE + +__BEGIN_DECLS +void dbm_close __P((DBM *)); +int dbm_delete __P((DBM *, datum)); +datum dbm_fetch __P((DBM *, datum)); +datum dbm_firstkey __P((DBM *)); +long dbm_forder __P((DBM *, datum)); +datum dbm_nextkey __P((DBM *)); +DBM *dbm_open __P((const char *, int, int)); +int dbm_store __P((DBM *, datum, datum, int)); +int dbm_dirfno __P((DBM *)); +__END_DECLS + +#endif /* !_NDBM_H_ */ diff --git a/lib/libc/db/VERSION b/lib/libc/db/VERSION new file mode 100644 index 0000000000..ceca0b2061 --- /dev/null +++ b/lib/libc/db/VERSION @@ -0,0 +1,58 @@ +# @(#)VERSION 8.1 (Berkeley) 6/6/93 + +This is version 1.6 of the Berkeley DB code. + +If your version of the DB code doesn't have a copy of +this version file, it's really old, please update it! + +New versions of this software are periodically made +available by anonymous ftp from ftp.cs.berkeley.edu, +in the file ucb/4bsd/db.tar.Z, or from ftp.uu.net. + +Email questions may be addressed to Keith Bostic at +bostic@cs.berkeley.edu. + +============================================ +1.5 -> 1.6 06 Jun 1993 + hash: In PAIRFITS, the first comparison should look at (P)[2]. + The hash_realloc function was walking off the end of memory. + The overflow page number was wrong when bumping splitpoint. +1.4 -> 1.5 23 May 1993 + hash: Set hash default fill factor dynamically. + recno: Fixed bug in sorted page splits. + Add page size parameter support. + Allow recno to specify the name of the underlying btree; + used for vi recovery. + btree/recno: + Support 64K pages. + btree/hash/recno: + Provide access to an underlying file descriptor. + Change sync routines to take a flag argument, recno + uses this to sync out the underlying btree. + +1.3 -> 1.4 10 May 1993 + recno: Delete the R_CURSORLOG flag from the recno interface. + Zero-length record fix for non-mmap reads. + Try and make SIZE_T_MAX test in open portable. + +1.2 -> 1.3 1 May 1993 + btree: Ignore user byte-order setting when reading already + existing database. Fixes to byte-order conversions. + +1.1 -> 1.2 15 Apr 1993 + No bug fixes, only compatibility hacks. +============================================ + +Distribution contents: + Makefile.inc Ignore this, it's Berkeley's internal Makefile. + PORT The directory to build in. + README This file. + VERSION This file. + btree B+tree routines. + db Dbopen(3) interface routine. + doc USENIX papers. + hash Extended linear hashing routines. + man Man pages. + mpool Memory pool routines. + recno Fixed/variable length routines. + test Test package. diff --git a/lib/libc/db/btree/Makefile.inc b/lib/libc/db/btree/Makefile.inc index 6461bff27b..71f8dfcf9e 100644 --- a/lib/libc/db/btree/Makefile.inc +++ b/lib/libc/db/btree/Makefile.inc @@ -1,8 +1,7 @@ -# @(#)Makefile.inc 5.3 (Berkeley) 2/12/91 - -# btree sources +# @(#)Makefile.inc 8.1 (Berkeley) 6/4/93 .PATH: ${.CURDIR}/db/btree -SRCS+= big.c btree.c delete.c insert.c lrucache.c lruhash.c lrutils.c \ - search.c seq.c split.c storage.c updutils.c utils.c +SRCS+= bt_close.c bt_conv.c bt_debug.c bt_delete.c bt_get.c bt_open.c \ + bt_overflow.c bt_page.c bt_put.c bt_search.c bt_seq.c bt_split.c \ + bt_stack.c bt_utils.c diff --git a/lib/libc/db/btree/big.c b/lib/libc/db/btree/big.c deleted file mode 100644 index 8ae3f3d8f3..0000000000 --- a/lib/libc/db/btree/big.c +++ /dev/null @@ -1,383 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Mike Olson. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)big.c 5.2 (Berkeley) 2/22/91"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include -#include -#include -#include "btree.h" - -/* - * _BT_GETBIG -- Get big data from indirect pages. - * - * This routine chases indirect blocks for the big object at the - * specified page to a buffer, and return the address of the buffer. - * - * Parameters: - * t -- btree with the indirect blocks - * pgno -- page number that starts the chain - * p -- (char **) to get the address of the buffer containing - * the key or datum. - * sz -- pointer to an int to get the size of the instantiated - * object. - * - * Returns: - * RET_SUCCESS, RET_ERROR. - * - * Side Effects: - * None. - */ - -int -_bt_getbig(t, pgno, p, sz) - BTREE_P t; - pgno_t pgno; - char **p; - int *sz; -{ - pgno_t save; - size_t nbytes; - size_t nhere; - BTHEADER *h; - char *top, *from, *where; - - save = t->bt_curpage->h_pgno; - if (_bt_getpage(t, pgno) == RET_ERROR) - return (RET_ERROR); - - h = t->bt_curpage; - - bcopy((char *) &(h->h_linp[0]), - (char *) &nbytes, - (size_t) sizeof(nbytes)); - - if ((*p = (char *) malloc(nbytes)) == (char *) NULL) - return (RET_ERROR); - - *sz = nbytes; - from = ((char *) (&h->h_linp[0])) + sizeof(nbytes); - top = ((char *) h) + t->bt_psize; - - /* need more space for data? */ - - where = *p; - - while (nbytes > 0) { - nhere = (int) (top - from); - if (nhere > nbytes) { - (void) bcopy(from, where, (size_t) nbytes); - nbytes = 0; - } else { - (void) bcopy(from, where, nhere); - where += nhere; - nbytes -= nhere; - if (_bt_getpage(t, h->h_nextpg) == RET_ERROR) - return (RET_ERROR); - h = t->bt_curpage; - top = ((char *) h) + t->bt_psize; - from = (char *) &(h->h_linp[0]); - } - } - - if (_bt_getpage(t, save) == RET_ERROR) - return (RET_ERROR); - - return (RET_SUCCESS); -} - -/* - * _BT_DELINDIR -- Delete a chain of indirect blocks from the btree. - * - * When a large item is deleted from the tree, this routine puts the - * space that it occupied onto the free list for later reuse. The - * bt_free entry in the btree structure points at the head of this list. - * This value is also stored on disk in the btree's metadata. - * - * Parameters: - * t -- btree from which to delete pages - * chain -- page number that starts the chain. - * - * Returns: - * RET_SUCCESS, RET_ERROR. - * - * Side Effects: - * Invalidates the current on-disk version of the btree's - * metadata (if any), and updates the free list appropriately. - */ - -int -_bt_delindir(t, chain) - BTREE_P t; - pgno_t chain; -{ - BTHEADER *h; - pgno_t save; - pgno_t oldfree; - - h = t->bt_curpage; - save = h->h_pgno; - if (_bt_getpage(t, chain) == RET_ERROR) - return (RET_ERROR); - - /* - * If some internal node is pointing at this chain, don't - * delete it. - */ - - if (t->bt_curpage->h_flags & F_PRESERVE) { - if (_bt_getpage(t, save) == RET_ERROR) - return (RET_ERROR); - return (RET_SUCCESS); - } - - /* if there's nothing on the free list, this is easy... */ - if (t->bt_free == P_NONE) { - t->bt_free = chain; - } else { - oldfree = t->bt_free; - - /* find the end of the data chain for the deleted datum */ - t->bt_free = chain; - do { - if (_bt_getpage(t, chain) == RET_ERROR) - return (RET_ERROR); - h = t->bt_curpage; - if (h->h_nextpg != P_NONE) - chain = h->h_nextpg; - } while (h->h_nextpg != P_NONE); - - /* link freed pages into free list */ - h->h_nextpg = oldfree; - if (_bt_write(t, h, RELEASE) == RET_ERROR) - return (RET_ERROR); - if (_bt_getpage(t, oldfree) == RET_ERROR) - return (RET_ERROR); - h = t->bt_curpage; - h->h_prevpg = chain; - if (_bt_write(t, h, RELEASE) == RET_ERROR) - return (RET_ERROR); - } - - /* restore the tree's current page pointer */ - if (_bt_getpage(t, save) == RET_ERROR) - return (RET_ERROR); - - /* we have trashed the tree metadata; rewrite it later */ - t->bt_flags &= ~BTF_METAOK; - - return (RET_SUCCESS); -} - -/* - * _BT_INDIRECT -- Write a series of indirect pages for big objects. - * - * A chain of indirect pages looks like - * - * +-------------------+ +---------------------+ - * |hdr|size| | |hdr| | - * +---+----+ | +---+ | - * | ... data ... | | ... data ... | ... - * | | | | - * +-------------------+ +---------------------+ - * - * where hdr is a standard btree page header (with the indirect bit - * set), size on the first page is the real size of the datum, and - * data are bytes of the datum, split across as many pages as necessary. - * Indirect pages are chained together with the h_prevpg and h_nextpg - * entries of the page header struct. - * - * A single DBT is written per chain, so space on the last page is - * wasted. - * - * We return the page number of the start of the chain. - * - * When a big object is deleted from a tree, the space that it occupied - * is placed on a free list for later reuse. This routine checks that - * free list before allocating new pages to the big datum being inserted. - * - * Parameters: - * t -- btree in which to store indirect blocks - * data -- DBT with the big datum in it - * pgno -- place to put the starting page number of the chain - * - * Returns: - * RET_SUCCESS, RET_ERROR. - * - * Side Effects: - * Current page is changed on return. - */ - -int -_bt_indirect(t, data, pgno) - BTREE_P t; - DBT *data; - pgno_t *pgno; -{ - pgno_t prev; - char *top; - char *where; - char *from; - size_t dsize; - pgno_t nextchn; - int ischain; - BTHEADER *cur; - - /* get set for first page in chain */ - prev = P_NONE; - dsize = data->size; - from = (char *) data->data; - - /* if there are blocks on the free list, use them first */ - if ((*pgno = t->bt_free) == P_NONE) { - if ((cur = _bt_allocpg(t)) == (BTHEADER *) NULL) - return (RET_ERROR); - - ischain = 0; - *pgno = cur->h_pgno = ++(t->bt_npages); - } else { - if (_bt_getpage(t, *pgno) == RET_ERROR) - return (RET_ERROR); - ischain = 1; - cur = t->bt_curpage; - } - - cur->h_flags = F_CONT|F_LEAF; - (void) bcopy((char *) &dsize, (char *) &cur->h_linp[0], sizeof(size_t)); - where = ((char *) (&cur->h_linp[0])) + sizeof(size_t); - - /* fill and write pages in the chain */ - for (;;) { - int nhere; - - top = ((char *) cur) + t->bt_psize; - cur->h_prevpg = prev; - nextchn = cur->h_nextpg; - nhere = (int) (top - where); - - if (nhere >= dsize) { - (void) bcopy(from, where, (int) dsize); - cur->h_nextpg = P_NONE; - dsize = 0; - } else { - (void) bcopy(from, where, (int) nhere); - dsize -= nhere; - from += nhere; - if (nextchn == P_NONE) - cur->h_nextpg = t->bt_npages + 1; - prev = cur->h_pgno; - } - - /* current page is ready to go; write it out */ - if (_bt_write(t, cur, RELEASE) == RET_ERROR) - return (RET_ERROR); - - /* free the current page, if appropriate */ - if (ISDISK(t) && !ISCACHE(t) && !ischain) { - (void) free ((char *) cur); - } - - /* done? */ - if (dsize == 0) - break; - - /* allocate another page */ - if (nextchn == P_NONE) { - if ((cur = _bt_allocpg(t)) == (BTHEADER *) NULL) - return (RET_ERROR); - ischain = 0; - cur->h_pgno = ++(t->bt_npages); - } else { - if (_bt_getpage(t, nextchn) == RET_ERROR) - return (RET_ERROR); - ischain = 1; - cur = t->bt_curpage; - } - cur->h_flags = F_LEAF | F_CONT; - - where = (char *) (&cur->h_linp[0]); - } - - /* if we used pages from the free list, record changes to it */ - if (*pgno == t->bt_free) { - t->bt_free = nextchn; - t->bt_flags &= ~BTF_METAOK; - } - - return (RET_SUCCESS); -} - -/* - * _BT_MARKCHAIN -- Mark a chain of pages as used by an internal node. - * - * Chains of indirect blocks pointed to by leaf nodes get reclaimed - * when the item that points to them gets deleted. Chains pointed - * to by internal nodes never get deleted. This routine marks a - * chain as pointed to by an internal node. - * - * Parameters: - * t -- tree in which to mark - * chain -- number of first page in chain - * - * Returns: - * RET_SUCCESS, RET_ERROR. - * - * Side Effects: - * None. - */ - -int -_bt_markchain(t, chain) - BTREE_P t; - pgno_t chain; -{ - pgno_t save; - - save = t->bt_curpage->h_pgno; - - if (_bt_getpage(t, chain) == RET_ERROR) - return (RET_ERROR); - - t->bt_curpage->h_flags |= (F_DIRTY|F_PRESERVE); - - if (_bt_getpage(t, save) == RET_ERROR) - return (RET_ERROR); - - return (RET_SUCCESS); -} diff --git a/lib/libc/db/btree/bt_close.c b/lib/libc/db/btree/bt_close.c new file mode 100644 index 0000000000..22c739b7b7 --- /dev/null +++ b/lib/libc/db/btree/bt_close.c @@ -0,0 +1,191 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_close.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include +#include +#include + +#include +#include "btree.h" + +static int bt_meta __P((BTREE *)); + +/* + * BT_CLOSE -- Close a btree. + * + * Parameters: + * dbp: pointer to access method + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__bt_close(dbp) + DB *dbp; +{ + BTREE *t; + int fd; + + t = dbp->internal; + + /* + * Delete any already deleted record that we've been saving + * because the cursor pointed to it. + */ + if (ISSET(t, B_DELCRSR) && __bt_crsrdel(t, &t->bt_bcursor)) + return (RET_ERROR); + + if (__bt_sync(dbp, 0) == RET_ERROR) + return (RET_ERROR); + + if (mpool_close(t->bt_mp) == RET_ERROR) + return (RET_ERROR); + + if (t->bt_stack) + free(t->bt_stack); + if (t->bt_kbuf) + free(t->bt_kbuf); + if (t->bt_dbuf) + free(t->bt_dbuf); + + fd = t->bt_fd; + free(t); + free(dbp); + return (close(fd) ? RET_ERROR : RET_SUCCESS); +} + +/* + * BT_SYNC -- sync the btree to disk. + * + * Parameters: + * dbp: pointer to access method + * + * Returns: + * RET_SUCCESS, RET_ERROR. + */ +int +__bt_sync(dbp, flags) + const DB *dbp; + u_int flags; +{ + BTREE *t; + int status; + PAGE *h; + void *p; + + if (flags != 0) { + errno = EINVAL; + return (RET_ERROR); + } + + t = dbp->internal; + + if (ISSET(t, B_INMEM | B_RDONLY) || !ISSET(t, B_MODIFIED)) + return (RET_SUCCESS); + + if (ISSET(t, B_METADIRTY) && bt_meta(t) == RET_ERROR) + return (RET_ERROR); + + /* + * Nastiness. If the cursor has been marked for deletion, but not + * actually deleted, we have to make a copy of the page, delete the + * key/data item, sync the file, and then restore the original page + * contents. + */ + if (ISSET(t, B_DELCRSR)) { + if ((p = malloc(t->bt_psize)) == NULL) + return (RET_ERROR); + if ((h = mpool_get(t->bt_mp, t->bt_bcursor.pgno, 0)) == NULL) + return (RET_ERROR); + memmove(p, h, t->bt_psize); + if ((status = + __bt_dleaf(t, h, t->bt_bcursor.index)) == RET_ERROR) + goto ecrsr; + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + } + + if ((status = mpool_sync(t->bt_mp)) == RET_SUCCESS) + CLR(t, B_MODIFIED); + +ecrsr: if (ISSET(t, B_DELCRSR)) { + if ((h = mpool_get(t->bt_mp, t->bt_bcursor.pgno, 0)) == NULL) + return (RET_ERROR); + memmove(h, p, t->bt_psize); + free(p); + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + } + return (status); +} + +/* + * BT_META -- write the tree meta data to disk. + * + * Parameters: + * t: tree + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +static int +bt_meta(t) + BTREE *t; +{ + BTMETA m; + void *p; + + if ((p = mpool_get(t->bt_mp, P_META, 0)) == NULL) + return (RET_ERROR); + + /* Fill in metadata. */ + m.m_magic = BTREEMAGIC; + m.m_version = BTREEVERSION; + m.m_psize = t->bt_psize; + m.m_free = t->bt_free; + m.m_nrecs = t->bt_nrecs; + m.m_flags = t->bt_flags & SAVEMETA; + + memmove(p, &m, sizeof(BTMETA)); + mpool_put(t->bt_mp, p, MPOOL_DIRTY); + return (RET_SUCCESS); +} diff --git a/lib/libc/db/btree/bt_conv.c b/lib/libc/db/btree/bt_conv.c new file mode 100644 index 0000000000..1de66e75da --- /dev/null +++ b/lib/libc/db/btree/bt_conv.c @@ -0,0 +1,221 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_conv.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include + +#include +#include "btree.h" + +static void mswap __P((PAGE *)); + +/* + * __BT_BPGIN, __BT_BPGOUT -- + * Convert host-specific number layout to/from the host-independent + * format stored on disk. + * + * Parameters: + * t: tree + * pg: page number + * h: page to convert + */ +void +__bt_pgin(t, pg, pp) + void *t; + pgno_t pg; + void *pp; +{ + PAGE *h; + int i, top; + u_char flags; + char *p; + + if (!ISSET(((BTREE *)t), B_NEEDSWAP)) + return; + if (pg == P_META) { + mswap(pp); + return; + } + + h = pp; + BLSWAP(h->pgno); + BLSWAP(h->prevpg); + BLSWAP(h->nextpg); + BLSWAP(h->flags); + BSSWAP(h->lower); + BSSWAP(h->upper); + + top = NEXTINDEX(h); + if ((h->flags & P_TYPE) == P_BINTERNAL) + for (i = 0; i < top; i++) { + BSSWAP(h->linp[i]); + p = (char *)GETBINTERNAL(h, i); + BLPSWAP(p); + p += sizeof(size_t); + BLPSWAP(p); + p += sizeof(pgno_t); + if (*(u_char *)p & P_BIGKEY) { + p += sizeof(u_char); + BLPSWAP(p); + p += sizeof(pgno_t); + BLPSWAP(p); + } + } + else if ((h->flags & P_TYPE) == P_BLEAF) + for (i = 0; i < top; i++) { + BSSWAP(h->linp[i]); + p = (char *)GETBLEAF(h, i); + BLPSWAP(p); + p += sizeof(size_t); + BLPSWAP(p); + p += sizeof(size_t); + flags = *(u_char *)p; + if (flags & (P_BIGKEY | P_BIGDATA)) { + p += sizeof(u_char); + if (flags & P_BIGKEY) { + BLPSWAP(p); + p += sizeof(pgno_t); + BLPSWAP(p); + } + if (flags & P_BIGDATA) { + p += sizeof(size_t); + BLPSWAP(p); + p += sizeof(pgno_t); + BLPSWAP(p); + } + } + } +} + +void +__bt_pgout(t, pg, pp) + void *t; + pgno_t pg; + void *pp; +{ + PAGE *h; + int i, top; + u_char flags; + char *p; + + if (!ISSET(((BTREE *)t), B_NEEDSWAP)) + return; + if (pg == P_META) { + mswap(pp); + return; + } + + h = pp; + top = NEXTINDEX(h); + if ((h->flags & P_TYPE) == P_BINTERNAL) + for (i = 0; i < top; i++) { + p = (char *)GETBINTERNAL(h, i); + BLPSWAP(p); + p += sizeof(size_t); + BLPSWAP(p); + p += sizeof(pgno_t); + if (*(u_char *)p & P_BIGKEY) { + p += sizeof(u_char); + BLPSWAP(p); + p += sizeof(pgno_t); + BLPSWAP(p); + } + BSSWAP(h->linp[i]); + } + else if ((h->flags & P_TYPE) == P_BLEAF) + for (i = 0; i < top; i++) { + p = (char *)GETBLEAF(h, i); + BLPSWAP(p); + p += sizeof(size_t); + BLPSWAP(p); + p += sizeof(size_t); + flags = *(u_char *)p; + if (flags & (P_BIGKEY | P_BIGDATA)) { + p += sizeof(u_char); + if (flags & P_BIGKEY) { + BLPSWAP(p); + p += sizeof(pgno_t); + BLPSWAP(p); + } + if (flags & P_BIGDATA) { + p += sizeof(size_t); + BLPSWAP(p); + p += sizeof(pgno_t); + BLPSWAP(p); + } + } + BSSWAP(h->linp[i]); + } + + BLSWAP(h->pgno); + BLSWAP(h->prevpg); + BLSWAP(h->nextpg); + BLSWAP(h->flags); + BSSWAP(h->lower); + BSSWAP(h->upper); +} + +/* + * MSWAP -- Actually swap the bytes on the meta page. + * + * Parameters: + * p: page to convert + */ +static void +mswap(pg) + PAGE *pg; +{ + char *p; + + p = (char *)pg; + BLPSWAP(p); /* m_magic */ + p += sizeof(u_long); + BLPSWAP(p); /* m_version */ + p += sizeof(u_long); + BLPSWAP(p); /* m_psize */ + p += sizeof(u_long); + BLPSWAP(p); /* m_free */ + p += sizeof(u_long); + BLPSWAP(p); /* m_nrecs */ + p += sizeof(u_long); + BLPSWAP(p); /* m_flags */ + p += sizeof(u_long); +} diff --git a/lib/libc/db/btree/bt_debug.c b/lib/libc/db/btree/bt_debug.c new file mode 100644 index 0000000000..ad88336d91 --- /dev/null +++ b/lib/libc/db/btree/bt_debug.c @@ -0,0 +1,332 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_debug.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +#include +#include "btree.h" + +#ifdef DEBUG +/* + * BT_DUMP -- Dump the tree + * + * Parameters: + * dbp: pointer to the DB + */ +void +__bt_dump(dbp) + DB *dbp; +{ + BTREE *t; + PAGE *h; + pgno_t i; + char *sep; + + t = dbp->internal; + (void)fprintf(stderr, "%s: pgsz %d", + ISSET(t, B_INMEM) ? "memory" : "disk", t->bt_psize); + if (ISSET(t, R_RECNO)) + (void)fprintf(stderr, " keys %lu", t->bt_nrecs); +#undef X +#define X(flag, name) \ + if (ISSET(t, flag)) { \ + (void)fprintf(stderr, "%s%s", sep, name); \ + sep = ", "; \ + } + if (t->bt_flags) { + sep = " flags ("; + X(B_DELCRSR, "DELCRSR"); + X(R_FIXLEN, "FIXLEN"); + X(B_INMEM, "INMEM"); + X(B_NODUPS, "NODUPS"); + X(B_RDONLY, "RDONLY"); + X(R_RECNO, "RECNO"); + X(B_SEQINIT, "SEQINIT"); + X(B_METADIRTY,"METADIRTY"); + (void)fprintf(stderr, ")\n"); + } +#undef X + + for (i = P_ROOT; (h = mpool_get(t->bt_mp, i, 0)) != NULL; ++i) { + __bt_dpage(h); + (void)mpool_put(t->bt_mp, h, 0); + } +} + +/* + * BT_DMPAGE -- Dump the meta page + * + * Parameters: + * h: pointer to the PAGE + */ +void +__bt_dmpage(h) + PAGE *h; +{ + BTMETA *m; + char *sep; + + m = (BTMETA *)h; + (void)fprintf(stderr, "magic %lx\n", m->m_magic); + (void)fprintf(stderr, "version %lu\n", m->m_version); + (void)fprintf(stderr, "psize %lu\n", m->m_psize); + (void)fprintf(stderr, "free %lu\n", m->m_free); + (void)fprintf(stderr, "nrecs %lu\n", m->m_nrecs); + (void)fprintf(stderr, "flags %lu", m->m_flags); +#undef X +#define X(flag, name) \ + if (m->m_flags & flag) { \ + (void)fprintf(stderr, "%s%s", sep, name); \ + sep = ", "; \ + } + if (m->m_flags) { + sep = " ("; + X(B_NODUPS, "NODUPS"); + X(R_RECNO, "RECNO"); + (void)fprintf(stderr, ")"); + } +} + +/* + * BT_DNPAGE -- Dump the page + * + * Parameters: + * n: page number to dump. + */ +void +__bt_dnpage(dbp, pgno) + DB *dbp; + pgno_t pgno; +{ + BTREE *t; + PAGE *h; + + t = dbp->internal; + if ((h = mpool_get(t->bt_mp, pgno, 0)) != NULL) { + __bt_dpage(h); + (void)mpool_put(t->bt_mp, h, 0); + } +} + +/* + * BT_DPAGE -- Dump the page + * + * Parameters: + * h: pointer to the PAGE + */ +void +__bt_dpage(h) + PAGE *h; +{ + BINTERNAL *bi; + BLEAF *bl; + RINTERNAL *ri; + RLEAF *rl; + indx_t cur, top; + char *sep; + + (void)fprintf(stderr, " page %d: (", h->pgno); +#undef X +#define X(flag, name) \ + if (h->flags & flag) { \ + (void)fprintf(stderr, "%s%s", sep, name); \ + sep = ", "; \ + } + sep = ""; + X(P_BINTERNAL, "BINTERNAL") /* types */ + X(P_BLEAF, "BLEAF") + X(P_RINTERNAL, "RINTERNAL") /* types */ + X(P_RLEAF, "RLEAF") + X(P_OVERFLOW, "OVERFLOW") + X(P_PRESERVE, "PRESERVE"); + (void)fprintf(stderr, ")\n"); +#undef X + + (void)fprintf(stderr, "\tprev %2d next %2d", h->prevpg, h->nextpg); + if (h->flags & P_OVERFLOW) + return; + + top = NEXTINDEX(h); + (void)fprintf(stderr, " lower %3d upper %3d nextind %d\n", + h->lower, h->upper, top); + for (cur = 0; cur < top; cur++) { + (void)fprintf(stderr, "\t[%03d] %4d ", cur, h->linp[cur]); + switch(h->flags & P_TYPE) { + case P_BINTERNAL: + bi = GETBINTERNAL(h, cur); + (void)fprintf(stderr, + "size %03d pgno %03d", bi->ksize, bi->pgno); + if (bi->flags & P_BIGKEY) + (void)fprintf(stderr, " (indirect)"); + else if (bi->ksize) + (void)fprintf(stderr, + " {%.*s}", (int)bi->ksize, bi->bytes); + break; + case P_RINTERNAL: + ri = GETRINTERNAL(h, cur); + (void)fprintf(stderr, "entries %03d pgno %03d", + ri->nrecs, ri->pgno); + break; + case P_BLEAF: + bl = GETBLEAF(h, cur); + if (bl->flags & P_BIGKEY) + (void)fprintf(stderr, + "big key page %lu size %u/", + *(pgno_t *)bl->bytes, + *(size_t *)(bl->bytes + sizeof(pgno_t))); + else if (bl->ksize) + (void)fprintf(stderr, "%s/", bl->bytes); + if (bl->flags & P_BIGDATA) + (void)fprintf(stderr, + "big data page %lu size %u", + *(pgno_t *)(bl->bytes + bl->ksize), + *(size_t *)(bl->bytes + bl->ksize + + sizeof(pgno_t))); + else if (bl->dsize) + (void)fprintf(stderr, "%.*s", + (int)bl->dsize, bl->bytes + bl->ksize); + break; + case P_RLEAF: + rl = GETRLEAF(h, cur); + if (rl->flags & P_BIGDATA) + (void)fprintf(stderr, + "big data page %lu size %u", + *(pgno_t *)rl->bytes, + *(size_t *)(rl->bytes + sizeof(pgno_t))); + else if (rl->dsize) + (void)fprintf(stderr, + "%.*s", (int)rl->dsize, rl->bytes); + break; + } + (void)fprintf(stderr, "\n"); + } +} +#endif + +#ifdef STATISTICS +/* + * BT_STAT -- Gather/print the tree statistics + * + * Parameters: + * dbp: pointer to the DB + */ +void +__bt_stat(dbp) + DB *dbp; +{ + extern u_long bt_cache_hit, bt_cache_miss; + extern u_long bt_rootsplit, bt_split, bt_sortsplit; + extern u_long bt_pfxsaved; + BTREE *t; + PAGE *h; + pgno_t i, pcont, pinternal, pleaf; + u_long ifree, lfree, nkeys; + int levels; + + t = dbp->internal; + pcont = pinternal = pleaf = 0; + nkeys = ifree = lfree = 0; + for (i = P_ROOT; (h = mpool_get(t->bt_mp, i, 0)) != NULL; ++i) { + switch(h->flags & P_TYPE) { + case P_BINTERNAL: + case P_RINTERNAL: + ++pinternal; + ifree += h->upper - h->lower; + break; + case P_BLEAF: + case P_RLEAF: + ++pleaf; + lfree += h->upper - h->lower; + nkeys += NEXTINDEX(h); + break; + case P_OVERFLOW: + ++pcont; + break; + } + (void)mpool_put(t->bt_mp, h, 0); + } + + /* Count the levels of the tree. */ + for (i = P_ROOT, levels = 0 ;; ++levels) { + h = mpool_get(t->bt_mp, i, 0); + if (h->flags & (P_BLEAF|P_RLEAF)) { + if (levels == 0) + levels = 1; + (void)mpool_put(t->bt_mp, h, 0); + break; + } + i = ISSET(t, R_RECNO) ? + GETRINTERNAL(h, 0)->pgno : + GETBINTERNAL(h, 0)->pgno; + (void)mpool_put(t->bt_mp, h, 0); + } + + (void)fprintf(stderr, "%d level%s with %ld keys", + levels, levels == 1 ? "" : "s", nkeys); + if (ISSET(t, R_RECNO)) + (void)fprintf(stderr, " (%ld header count)", t->bt_nrecs); + (void)fprintf(stderr, + "\n%lu pages (leaf %ld, internal %ld, overflow %ld)\n", + pinternal + pleaf + pcont, pleaf, pinternal, pcont); + (void)fprintf(stderr, "%ld cache hits, %ld cache misses\n", + bt_cache_hit, bt_cache_miss); + (void)fprintf(stderr, "%ld splits (%ld root splits, %ld sort splits)\n", + bt_split, bt_rootsplit, bt_sortsplit); + pleaf *= t->bt_psize - BTDATAOFF; + if (pleaf) + (void)fprintf(stderr, + "%.0f%% leaf fill (%ld bytes used, %ld bytes free)\n", + ((double)(pleaf - lfree) / pleaf) * 100, + pleaf - lfree, lfree); + pinternal *= t->bt_psize - BTDATAOFF; + if (pinternal) + (void)fprintf(stderr, + "%.0f%% internal fill (%ld bytes used, %ld bytes free\n", + ((double)(pinternal - ifree) / pinternal) * 100, + pinternal - ifree, ifree); + if (bt_pfxsaved) + (void)fprintf(stderr, "prefix checking removed %lu bytes.\n", + bt_pfxsaved); +} +#endif diff --git a/lib/libc/db/btree/bt_delete.c b/lib/libc/db/btree/bt_delete.c new file mode 100644 index 0000000000..326618b1f6 --- /dev/null +++ b/lib/libc/db/btree/bt_delete.c @@ -0,0 +1,317 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_delete.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +#include +#include "btree.h" + +static int bt_bdelete __P((BTREE *, const DBT *)); + +/* + * __BT_DELETE -- Delete the item(s) referenced by a key. + * + * Parameters: + * dbp: pointer to access method + * key: key to delete + * flags: R_CURSOR if deleting what the cursor references + * + * Returns: + * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found. + */ +int +__bt_delete(dbp, key, flags) + const DB *dbp; + const DBT *key; + u_int flags; +{ + BTREE *t; + int status; + + t = dbp->internal; + if (ISSET(t, B_RDONLY)) { + errno = EPERM; + return (RET_ERROR); + } + switch(flags) { + case 0: + status = bt_bdelete(t, key); + break; + case R_CURSOR: + /* + * If flags is R_CURSOR, delete the cursor; must already have + * started a scan and not have already deleted the record. For + * the delete cursor bit to have been set requires that the + * scan be initialized, so no reason to check. + */ + if (!ISSET(t, B_SEQINIT)) + goto einval; + status = ISSET(t, B_DELCRSR) ? + RET_SPECIAL : __bt_crsrdel(t, &t->bt_bcursor); + break; + default: +einval: errno = EINVAL; + return (RET_ERROR); + } + if (status == RET_SUCCESS) + SET(t, B_MODIFIED); + return (status); +} + +/* + * BT_BDELETE -- Delete all key/data pairs matching the specified key. + * + * Parameters: + * tree: tree + * key: key to delete + * + * Returns: + * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found. + */ +static int +bt_bdelete(t, key) + BTREE *t; + const DBT *key; +{ + EPG *e, save; + PAGE *h; + pgno_t cpgno, pg; + indx_t cindex; + int deleted, dirty1, dirty2, exact; + + /* Find any matching record; __bt_search pins the page. */ + if ((e = __bt_search(t, key, &exact)) == NULL) + return (RET_ERROR); + if (!exact) { + mpool_put(t->bt_mp, e->page, 0); + return (RET_SPECIAL); + } + + /* + * Delete forward, then delete backward, from the found key. The + * ordering is so that the deletions don't mess up the page refs. + * The first loop deletes the key from the original page, the second + * unpins the original page. In the first loop, dirty1 is set if + * the original page is modified, and dirty2 is set if any subsequent + * pages are modified. In the second loop, dirty1 starts off set if + * the original page has been modified, and is set if any subsequent + * pages are modified. + * + * If find the key referenced by the cursor, don't delete it, just + * flag it for future deletion. The cursor page number is P_INVALID + * unless the sequential scan is initialized, so no reason to check. + * A special case is when the already deleted cursor record was the + * only record found. If so, then the delete opertion fails as no + * records were deleted. + * + * Cycle in place in the current page until the current record doesn't + * match the key or the page is empty. If the latter, walk forward, + * skipping empty pages and repeating until a record doesn't match + * the key or the end of the tree is reached. + */ + cpgno = t->bt_bcursor.pgno; + cindex = t->bt_bcursor.index; + save = *e; + dirty1 = 0; + for (h = e->page, deleted = 0;;) { + dirty2 = 0; + do { + if (h->pgno == cpgno && e->index == cindex) { + if (!ISSET(t, B_DELCRSR)) { + SET(t, B_DELCRSR); + deleted = 1; + } + ++e->index; + } else { + if (__bt_dleaf(t, h, e->index)) { + if (h->pgno != save.page->pgno) + mpool_put(t->bt_mp, h, dirty2); + mpool_put(t->bt_mp, save.page, dirty1); + return (RET_ERROR); + } + if (h->pgno == save.page->pgno) + dirty1 = MPOOL_DIRTY; + else + dirty2 = MPOOL_DIRTY; + deleted = 1; + } + } while (e->index < NEXTINDEX(h) && __bt_cmp(t, key, e) == 0); + + /* + * Quit if didn't find a match, no next page, or first key on + * the next page doesn't match. Don't unpin the original page + * unless an error occurs. + */ + if (e->index < NEXTINDEX(h)) + break; + for (;;) { + if ((pg = h->nextpg) == P_INVALID) + goto done1; + if (h->pgno != save.page->pgno) + mpool_put(t->bt_mp, h, dirty2); + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) { + mpool_put(t->bt_mp, save.page, dirty1); + return (RET_ERROR); + } + if (NEXTINDEX(h) != 0) { + e->page = h; + e->index = 0; + break; + } + } + + if (__bt_cmp(t, key, e) != 0) + break; + } + + /* + * Reach here with the original page and the last page referenced + * pinned (they may be the same). Release it if not the original. + */ +done1: if (h->pgno != save.page->pgno) + mpool_put(t->bt_mp, h, dirty2); + + /* + * Walk backwards from the record previous to the record returned by + * __bt_search, skipping empty pages, until a record doesn't match + * the key or reach the beginning of the tree. + */ + *e = save; + for (;;) { + if (e->index) + --e->index; + for (h = e->page; e->index; --e->index) { + if (__bt_cmp(t, key, e) != 0) + goto done2; + if (h->pgno == cpgno && e->index == cindex) { + if (!ISSET(t, B_DELCRSR)) { + SET(t, B_DELCRSR); + deleted = 1; + } + } else { + if (__bt_dleaf(t, h, e->index) == RET_ERROR) { + mpool_put(t->bt_mp, h, dirty1); + return (RET_ERROR); + } + if (h->pgno == save.page->pgno) + dirty1 = MPOOL_DIRTY; + deleted = 1; + } + } + + if ((pg = h->prevpg) == P_INVALID) + goto done2; + mpool_put(t->bt_mp, h, dirty1); + dirty1 = 0; + if ((e->page = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + e->index = NEXTINDEX(e->page); + } + + /* + * Reach here with the last page that was looked at pinned. Release + * it. + */ +done2: mpool_put(t->bt_mp, h, dirty1); + return (deleted ? RET_SUCCESS : RET_SPECIAL); +} + +/* + * __BT_DLEAF -- Delete a single record from a leaf page. + * + * Parameters: + * t: tree + * index: index on current page to delete + * + * Returns: + * RET_SUCCESS, RET_ERROR. + */ +int +__bt_dleaf(t, h, index) + BTREE *t; + PAGE *h; + int index; +{ + register BLEAF *bl; + register indx_t *ip, offset; + register size_t nbytes; + register int cnt; + char *from; + void *to; + + /* + * Delete a record from a btree leaf page. Internal records are never + * deleted from internal pages, regardless of the records that caused + * them to be added being deleted. Pages made empty by deletion are + * not reclaimed. They are, however, made available for reuse. + * + * Pack the remaining entries at the end of the page, shift the indices + * down, overwriting the deleted record and its index. If the record + * uses overflow pages, make them available for reuse. + */ + to = bl = GETBLEAF(h, index); + if (bl->flags & P_BIGKEY && __ovfl_delete(t, bl->bytes) == RET_ERROR) + return (RET_ERROR); + if (bl->flags & P_BIGDATA && + __ovfl_delete(t, bl->bytes + bl->ksize) == RET_ERROR) + return (RET_ERROR); + nbytes = NBLEAF(bl); + + /* + * Compress the key/data pairs. Compress and adjust the [BR]LEAF + * offsets. Reset the headers. + */ + from = (char *)h + h->upper; + memmove(from + nbytes, from, (char *)to - from); + h->upper += nbytes; + + offset = h->linp[index]; + for (cnt = index, ip = &h->linp[0]; cnt--; ++ip) + if (ip[0] < offset) + ip[0] += nbytes; + for (cnt = NEXTINDEX(h) - index; --cnt; ++ip) + ip[0] = ip[1] < offset ? ip[1] + nbytes : ip[1]; + h->lower -= sizeof(indx_t); + return (RET_SUCCESS); +} diff --git a/lib/libc/db/btree/bt_get.c b/lib/libc/db/btree/bt_get.c new file mode 100644 index 0000000000..4f473bc4f1 --- /dev/null +++ b/lib/libc/db/btree/bt_get.c @@ -0,0 +1,222 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_get.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +#include +#include "btree.h" + +/* + * __BT_GET -- Get a record from the btree. + * + * Parameters: + * dbp: pointer to access method + * key: key to find + * data: data to return + * flag: currently unused + * + * Returns: + * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found. + */ +int +__bt_get(dbp, key, data, flags) + const DB *dbp; + const DBT *key; + DBT *data; + u_int flags; +{ + BTREE *t; + EPG *e; + int exact, status; + + if (flags) { + errno = EINVAL; + return (RET_ERROR); + } + t = dbp->internal; + if ((e = __bt_search(t, key, &exact)) == NULL) + return (RET_ERROR); + if (!exact) { + mpool_put(t->bt_mp, e->page, 0); + return (RET_SPECIAL); + } + + /* + * A special case is if we found the record but it's flagged for + * deletion. In this case, we want to find another record with the + * same key, if it exists. Rather than look around the tree we call + * __bt_first and have it redo the search, as __bt_first will not + * return keys marked for deletion. Slow, but should never happen. + */ + if (ISSET(t, B_DELCRSR) && e->page->pgno == t->bt_bcursor.pgno && + e->index == t->bt_bcursor.index) { + mpool_put(t->bt_mp, e->page, 0); + if ((e = __bt_first(t, key, &exact)) == NULL) + return (RET_ERROR); + if (!exact) + return (RET_SPECIAL); + } + + status = __bt_ret(t, e, NULL, data); + mpool_put(t->bt_mp, e->page, 0); + return (status); +} + +/* + * __BT_FIRST -- Find the first entry. + * + * Parameters: + * t: the tree + * key: the key + * + * Returns: + * The first entry in the tree greater than or equal to key. + */ +EPG * +__bt_first(t, key, exactp) + BTREE *t; + const DBT *key; + int *exactp; +{ + register PAGE *h; + register EPG *e; + EPG save; + pgno_t cpgno, pg; + indx_t cindex; + int found; + + /* + * Find any matching record; __bt_search pins the page. Only exact + * matches are tricky, otherwise just return the location of the key + * if it were to be inserted into the tree. + */ + if ((e = __bt_search(t, key, exactp)) == NULL) + return (NULL); + if (!*exactp) + return (e); + + if (ISSET(t, B_DELCRSR)) { + cpgno = t->bt_bcursor.pgno; + cindex = t->bt_bcursor.index; + } else { + cpgno = P_INVALID; + cindex = 0; /* GCC thinks it's uninitialized. */ + } + + /* + * Walk backwards, skipping empty pages, as long as the entry matches + * and there are keys left in the tree. Save a copy of each match in + * case we go too far. A special case is that we don't return a match + * on records that the cursor references that have already been flagged + * for deletion. + */ + save = *e; + h = e->page; + found = 0; + do { + if (cpgno != h->pgno || cindex != e->index) { + if (save.page->pgno != e->page->pgno) { + mpool_put(t->bt_mp, save.page, 0); + save = *e; + } else + save.index = e->index; + found = 1; + } + /* + * Make a special effort not to unpin the page the last (or + * original) match was on, but also make sure it's unpinned + * if an error occurs. + */ + while (e->index == 0) { + if (h->prevpg == P_INVALID) + goto done1; + if (h->pgno != save.page->pgno) + mpool_put(t->bt_mp, h, 0); + if ((h = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL) { + if (h->pgno == save.page->pgno) + mpool_put(t->bt_mp, save.page, 0); + return (NULL); + } + e->page = h; + e->index = NEXTINDEX(h); + } + --e->index; + } while (__bt_cmp(t, key, e) == 0); + + /* + * Reach here with the last page that was looked at pinned, which may + * or may not be the same as the last (or original) match page. If + * it's not useful, release it. + */ +done1: if (h->pgno != save.page->pgno) + mpool_put(t->bt_mp, h, 0); + + /* + * If still haven't found a record, the only possibility left is the + * next one. Move forward one slot, skipping empty pages and check. + */ + if (!found) { + h = save.page; + if (++save.index == NEXTINDEX(h)) { + do { + pg = h->nextpg; + mpool_put(t->bt_mp, h, 0); + if (pg == P_INVALID) { + *exactp = 0; + return (e); + } + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (NULL); + } while ((save.index = NEXTINDEX(h)) == 0); + save.page = h; + } + if (__bt_cmp(t, key, &save) != 0) { + *exactp = 0; + return (e); + } + } + *e = save; + *exactp = 1; + return (e); +} diff --git a/lib/libc/db/btree/bt_open.c b/lib/libc/db/btree/bt_open.c new file mode 100644 index 0000000000..f918124042 --- /dev/null +++ b/lib/libc/db/btree/bt_open.c @@ -0,0 +1,428 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_open.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Implementation of btree access method for 4.4BSD. + * + * The design here was originally based on that of the btree access method + * used in the Postgres database system at UC Berkeley. This implementation + * is wholly independent of the Postgres code. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define __DBINTERFACE_PRIVATE +#include +#include "btree.h" + +static int byteorder __P((void)); +static int nroot __P((BTREE *)); +static int tmp __P((void)); + +/* + * __BT_OPEN -- Open a btree. + * + * Creates and fills a DB struct, and calls the routine that actually + * opens the btree. + * + * Parameters: + * fname: filename (NULL for in-memory trees) + * flags: open flag bits + * mode: open permission bits + * b: BTREEINFO pointer + * + * Returns: + * NULL on failure, pointer to DB on success. + * + */ +DB * +__bt_open(fname, flags, mode, openinfo) + const char *fname; + int flags, mode; + const BTREEINFO *openinfo; +{ + BTMETA m; + BTREE *t; + BTREEINFO b; + DB *dbp; + pgno_t ncache; + struct stat sb; + int machine_lorder, nr; + + t = NULL; + + /* + * Intention is to make sure all of the user's selections are okay + * here and then use them without checking. Can't be complete, since + * we don't know the right page size, lorder or flags until the backing + * file is opened. Also, the file's page size can cause the cachesize + * to change. + */ + machine_lorder = byteorder(); + if (openinfo) { + b = *openinfo; + + /* Flags: R_DUP. */ + if (b.flags & ~(R_DUP)) + goto einval; + + /* + * Page size must be indx_t aligned and >= MINPSIZE. Default + * page size is set farther on, based on the underlying file + * transfer size. + */ + if (b.psize && + (b.psize < MINPSIZE || b.psize > MAX_PAGE_OFFSET + 1 || + b.psize & sizeof(indx_t) - 1)) + goto einval; + + /* Minimum number of keys per page; absolute minimum is 2. */ + if (b.minkeypage) { + if (b.minkeypage < 2) + goto einval; + } else + b.minkeypage = DEFMINKEYPAGE; + + /* If no comparison, use default comparison and prefix. */ + if (b.compare == NULL) { + b.compare = __bt_defcmp; + if (b.prefix == NULL) + b.prefix = __bt_defpfx; + } + + if (b.lorder == 0) + b.lorder = machine_lorder; + } else { + b.compare = __bt_defcmp; + b.cachesize = 0; + b.flags = 0; + b.lorder = machine_lorder; + b.minkeypage = DEFMINKEYPAGE; + b.prefix = __bt_defpfx; + b.psize = 0; + } + + /* Check for the ubiquitous PDP-11. */ + if (b.lorder != BIG_ENDIAN && b.lorder != LITTLE_ENDIAN) + goto einval; + + /* Allocate and initialize DB and BTREE structures. */ + if ((t = malloc(sizeof(BTREE))) == NULL) + goto err; + t->bt_fd = -1; /* Don't close unopened fd on error. */ + if ((t->bt_dbp = dbp = malloc(sizeof(DB))) == NULL) + goto err; + t->bt_bcursor.pgno = P_INVALID; + t->bt_bcursor.index = 0; + t->bt_stack = NULL; + t->bt_sp = t->bt_maxstack = 0; + t->bt_kbuf = t->bt_dbuf = NULL; + t->bt_kbufsz = t->bt_dbufsz = 0; + t->bt_lorder = b.lorder; + t->bt_order = NOT; + t->bt_cmp = b.compare; + t->bt_pfx = b.prefix; + t->bt_flags = 0; + if (t->bt_lorder != machine_lorder) + SET(t, B_NEEDSWAP); + + dbp->type = DB_BTREE; + dbp->internal = t; + dbp->close = __bt_close; + dbp->del = __bt_delete; + dbp->fd = __bt_fd; + dbp->get = __bt_get; + dbp->put = __bt_put; + dbp->seq = __bt_seq; + dbp->sync = __bt_sync; + + /* + * If no file name was supplied, this is an in-memory btree and we + * open a backing temporary file. Otherwise, it's a disk-based tree. + */ + if (fname) { + switch(flags & O_ACCMODE) { + case O_RDONLY: + SET(t, B_RDONLY); + break; + case O_RDWR: + break; + case O_WRONLY: + default: + goto einval; + } + + if ((t->bt_fd = + open(fname, flags & __USE_OPEN_FLAGS, mode)) < 0) + goto err; + + } else { + if ((flags & O_ACCMODE) != O_RDWR) + goto einval; + if ((t->bt_fd = tmp()) == -1) + goto err; + SET(t, B_INMEM); + } + + if (fcntl(t->bt_fd, F_SETFD, 1) == -1) + goto err; + + if (fstat(t->bt_fd, &sb)) + goto err; + if (sb.st_size) { + nr = read(t->bt_fd, &m, sizeof(BTMETA)); + if (nr < 0) + goto err; + if (nr != sizeof(BTMETA)) + goto eftype; + + /* + * Read in the meta-data. This can change the notion of what + * the lorder, page size and flags are, and, when the page size + * changes, the cachesize value can change too. If the user + * specified the wrong byte order for an existing database, we + * don't bother to return an error, we just clear the NEEDSWAP + * bit. + */ + if (m.m_magic == BTREEMAGIC) + CLR(t, B_NEEDSWAP); + else { + SET(t, B_NEEDSWAP); + BLSWAP(m.m_magic); + BLSWAP(m.m_version); + BLSWAP(m.m_psize); + BLSWAP(m.m_free); + BLSWAP(m.m_nrecs); + BLSWAP(m.m_flags); + } + if (m.m_magic != BTREEMAGIC || m.m_version != BTREEVERSION) + goto eftype; + if (m.m_psize < MINPSIZE || m.m_psize > MAX_PAGE_OFFSET + 1 || + m.m_psize & sizeof(indx_t) - 1) + goto eftype; + if (m.m_flags & ~SAVEMETA) + goto eftype; + b.psize = m.m_psize; + t->bt_flags |= m.m_flags; + t->bt_free = m.m_free; + t->bt_nrecs = m.m_nrecs; + } else { + /* + * Set the page size to the best value for I/O to this file. + * Don't overflow the page offset type. + */ + if (b.psize == 0) { + b.psize = sb.st_blksize; + if (b.psize < MINPSIZE) + b.psize = MINPSIZE; + if (b.psize > MAX_PAGE_OFFSET + 1) + b.psize = MAX_PAGE_OFFSET + 1; + } + + /* Set flag if duplicates permitted. */ + if (!(b.flags & R_DUP)) + SET(t, B_NODUPS); + + t->bt_free = P_INVALID; + t->bt_nrecs = 0; + SET(t, B_METADIRTY); + } + + t->bt_psize = b.psize; + + /* Set the cache size; must be a multiple of the page size. */ + if (b.cachesize && b.cachesize & b.psize - 1) + b.cachesize += (~b.cachesize & b.psize - 1) + 1; + if (b.cachesize < b.psize * MINCACHE) + b.cachesize = b.psize * MINCACHE; + + /* Calculate number of pages to cache. */ + ncache = (b.cachesize + t->bt_psize - 1) / t->bt_psize; + + /* + * The btree data structure requires that at least two keys can fit on + * a page, but other than that there's no fixed requirement. The user + * specified a minimum number per page, and we translated that into the + * number of bytes a key/data pair can use before being placed on an + * overflow page. This calculation includes the page header, the size + * of the index referencing the leaf item and the size of the leaf item + * structure. Also, don't let the user specify a minkeypage such that + * a key/data pair won't fit even if both key and data are on overflow + * pages. + */ + t->bt_ovflsize = (t->bt_psize - BTDATAOFF) / b.minkeypage - + (sizeof(indx_t) + NBLEAFDBT(0, 0)); + if (t->bt_ovflsize < NBLEAFDBT(NOVFLSIZE, NOVFLSIZE) + sizeof(indx_t)) + t->bt_ovflsize = + NBLEAFDBT(NOVFLSIZE, NOVFLSIZE) + sizeof(indx_t); + + /* Initialize the buffer pool. */ + if ((t->bt_mp = + mpool_open(NULL, t->bt_fd, t->bt_psize, ncache)) == NULL) + goto err; + if (!ISSET(t, B_INMEM)) + mpool_filter(t->bt_mp, __bt_pgin, __bt_pgout, t); + + /* Create a root page if new tree. */ + if (nroot(t) == RET_ERROR) + goto err; + + return (dbp); + +einval: errno = EINVAL; + goto err; + +eftype: errno = EFTYPE; + goto err; + +err: if (t) { + if (t->bt_dbp) + free(t->bt_dbp); + if (t->bt_fd != -1) + (void)close(t->bt_fd); + free(t); + } + return (NULL); +} + +/* + * NROOT -- Create the root of a new tree. + * + * Parameters: + * t: tree + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +static int +nroot(t) + BTREE *t; +{ + PAGE *meta, *root; + pgno_t npg; + + if ((meta = mpool_get(t->bt_mp, 0, 0)) != NULL) { + mpool_put(t->bt_mp, meta, 0); + return (RET_SUCCESS); + } + if (errno != EINVAL) + return (RET_ERROR); + + if ((meta = mpool_new(t->bt_mp, &npg)) == NULL) + return (RET_ERROR); + + if ((root = mpool_new(t->bt_mp, &npg)) == NULL) + return (RET_ERROR); + + if (npg != P_ROOT) + return (RET_ERROR); + root->pgno = npg; + root->prevpg = root->nextpg = P_INVALID; + root->lower = BTDATAOFF; + root->upper = t->bt_psize; + root->flags = P_BLEAF; + memset(meta, 0, t->bt_psize); + mpool_put(t->bt_mp, meta, MPOOL_DIRTY); + mpool_put(t->bt_mp, root, MPOOL_DIRTY); + return (RET_SUCCESS); +} + +static int +tmp() +{ + sigset_t set, oset; + int fd; + char *envtmp; + char path[MAXPATHLEN]; + + envtmp = getenv("TMPDIR"); + (void)snprintf(path, + sizeof(path), "%s/bt.XXXXXX", envtmp ? envtmp : "/tmp"); + + (void)sigfillset(&set); + (void)sigprocmask(SIG_BLOCK, &set, &oset); + if ((fd = mkstemp(path)) != -1) + (void)unlink(path); + (void)sigprocmask(SIG_SETMASK, &oset, NULL); + return(fd); +} + +static int +byteorder() +{ + u_long x; /* XXX: 32-bit assumption. */ + u_char *p; + + x = 0x01020304; + p = (u_char *)&x; + switch (*p) { + case 1: + return (BIG_ENDIAN); + case 4: + return (LITTLE_ENDIAN); + default: + return (0); + } +} + +int +__bt_fd(dbp) + const DB *dbp; +{ + BTREE *t; + + t = dbp->internal; + + if (ISSET(t, B_INMEM)) { + errno = ENOENT; + return (-1); + } + return (t->bt_fd); +} diff --git a/lib/libc/db/btree/bt_overflow.c b/lib/libc/db/btree/bt_overflow.c new file mode 100644 index 0000000000..e3544e5ac2 --- /dev/null +++ b/lib/libc/db/btree/bt_overflow.c @@ -0,0 +1,224 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_overflow.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +#include +#include "btree.h" + +/* + * Big key/data code. + * + * Big key and data entries are stored on linked lists of pages. The initial + * reference is byte string stored with the key or data and is the page number + * and size. The actual record is stored in a chain of pages linked by the + * nextpg field of the PAGE header. + * + * The first page of the chain has a special property. If the record is used + * by an internal page, it cannot be deleted and the P_PRESERVE bit will be set + * in the header. + * + * XXX + * A single DBT is written to each chain, so a lot of space on the last page + * is wasted. This is a fairly major bug for some data sets. + */ + +/* + * __OVFL_GET -- Get an overflow key/data item. + * + * Parameters: + * t: tree + * p: pointer to { pgno_t, size_t } + * buf: storage address + * bufsz: storage size + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__ovfl_get(t, p, ssz, buf, bufsz) + BTREE *t; + void *p; + size_t *ssz; + char **buf; + size_t *bufsz; +{ + PAGE *h; + pgno_t pg; + size_t nb, plen, sz; + + memmove(&pg, p, sizeof(pgno_t)); + memmove(&sz, (char *)p + sizeof(pgno_t), sizeof(size_t)); + *ssz = sz; + +#ifdef DEBUG + if (pg == P_INVALID || sz == 0) + abort(); +#endif + /* Make the buffer bigger as necessary. */ + if (*bufsz < sz) { + if ((*buf = realloc(*buf, sz)) == NULL) + return (RET_ERROR); + *bufsz = sz; + } + + /* + * Step through the linked list of pages, copying the data on each one + * into the buffer. Never copy more than the data's length. + */ + plen = t->bt_psize - BTDATAOFF; + for (p = *buf;; p = (char *)p + nb, pg = h->nextpg) { + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + + nb = MIN(sz, plen); + memmove(p, (char *)h + BTDATAOFF, nb); + mpool_put(t->bt_mp, h, 0); + + if ((sz -= nb) == 0) + break; + } + return (RET_SUCCESS); +} + +/* + * __OVFL_PUT -- Store an overflow key/data item. + * + * Parameters: + * t: tree + * data: DBT to store + * pgno: storage page number + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__ovfl_put(t, dbt, pg) + BTREE *t; + const DBT *dbt; + pgno_t *pg; +{ + PAGE *h, *last; + void *p; + pgno_t npg; + size_t nb, plen, sz; + + /* + * Allocate pages and copy the key/data record into them. Store the + * number of the first page in the chain. + */ + plen = t->bt_psize - BTDATAOFF; + for (last = NULL, p = dbt->data, sz = dbt->size;; + p = (char *)p + plen, last = h) { + if ((h = __bt_new(t, &npg)) == NULL) + return (RET_ERROR); + + h->pgno = npg; + h->nextpg = h->prevpg = P_INVALID; + h->flags = P_OVERFLOW; + h->lower = h->upper = 0; + + nb = MIN(sz, plen); + memmove((char *)h + BTDATAOFF, p, nb); + + if (last) { + last->nextpg = h->pgno; + mpool_put(t->bt_mp, last, MPOOL_DIRTY); + } else + *pg = h->pgno; + + if ((sz -= nb) == 0) { + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + break; + } + } + return (RET_SUCCESS); +} + +/* + * __OVFL_DELETE -- Delete an overflow chain. + * + * Parameters: + * t: tree + * p: pointer to { pgno_t, size_t } + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__ovfl_delete(t, p) + BTREE *t; + void *p; +{ + PAGE *h; + pgno_t pg; + size_t plen, sz; + + memmove(&pg, p, sizeof(pgno_t)); + memmove(&sz, (char *)p + sizeof(pgno_t), sizeof(size_t)); + +#ifdef DEBUG + if (pg == P_INVALID || sz == 0) + abort(); +#endif + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + + /* Don't delete chains used by internal pages. */ + if (h->flags & P_PRESERVE) { + mpool_put(t->bt_mp, h, 0); + return (RET_SUCCESS); + } + + /* Step through the chain, calling the free routine for each page. */ + for (plen = t->bt_psize - BTDATAOFF;; sz -= plen) { + pg = h->nextpg; + __bt_free(t, h); + if (sz <= plen) + break; + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + } + return (RET_SUCCESS); +} diff --git a/lib/libc/db/btree/bt_page.c b/lib/libc/db/btree/bt_page.c new file mode 100644 index 0000000000..a1401099b9 --- /dev/null +++ b/lib/libc/db/btree/bt_page.c @@ -0,0 +1,94 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_page.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#define __DBINTERFACE_PRIVATE +#include + +#include +#include "btree.h" + +/* + * __BT_FREE -- Put a page on the freelist. + * + * Parameters: + * t: tree + * h: page to free + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__bt_free(t, h) + BTREE *t; + PAGE *h; +{ + /* Insert the page at the start of the free list. */ + h->prevpg = P_INVALID; + h->nextpg = t->bt_free; + t->bt_free = h->pgno; + + /* Make sure the page gets written back. */ + return (mpool_put(t->bt_mp, h, MPOOL_DIRTY)); +} + +/* + * __BT_NEW -- Get a new page, preferably from the freelist. + * + * Parameters: + * t: tree + * npg: storage for page number. + * + * Returns: + * Pointer to a page, NULL on error. + */ +PAGE * +__bt_new(t, npg) + BTREE *t; + pgno_t *npg; +{ + PAGE *h; + + if (t->bt_free != P_INVALID && + (h = mpool_get(t->bt_mp, t->bt_free, 0)) != NULL) { + *npg = t->bt_free; + t->bt_free = h->nextpg; + return (h); + } + return (mpool_new(t->bt_mp, npg)); +} diff --git a/lib/libc/db/btree/bt_put.c b/lib/libc/db/btree/bt_put.c new file mode 100644 index 0000000000..2e0918b8f0 --- /dev/null +++ b/lib/libc/db/btree/bt_put.c @@ -0,0 +1,313 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_put.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include +#include + +#include +#include "btree.h" + +static EPG *bt_fast __P((BTREE *, const DBT *, const DBT *, int *)); + +/* + * __BT_PUT -- Add a btree item to the tree. + * + * Parameters: + * dbp: pointer to access method + * key: key + * data: data + * flag: R_NOOVERWRITE + * + * Returns: + * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key is already in the + * tree and R_NOOVERWRITE specified. + */ +int +__bt_put(dbp, key, data, flags) + const DB *dbp; + DBT *key; + const DBT *data; + u_int flags; +{ + BTREE *t; + DBT tkey, tdata; + EPG *e; + PAGE *h; + indx_t index, nxtindex; + pgno_t pg; + size_t nbytes; + int dflags, exact, status; + char *dest, db[NOVFLSIZE], kb[NOVFLSIZE]; + + t = dbp->internal; + + switch (flags) { + case R_CURSOR: + if (!ISSET(t, B_SEQINIT)) + goto einval; + if (ISSET(t, B_DELCRSR)) + goto einval; + break; + case 0: + case R_NOOVERWRITE: + break; + default: +einval: errno = EINVAL; + return (RET_ERROR); + } + + if (ISSET(t, B_RDONLY)) { + errno = EPERM; + return (RET_ERROR); + } + + /* + * If the key/data won't fit on a page, store it on indirect pages. + * Only store the key on the overflow page if it's too big after the + * data is on an overflow page. + * + * XXX + * If the insert fails later on, these pages aren't recovered. + */ + dflags = 0; + if (key->size + data->size > t->bt_ovflsize) { + if (key->size > t->bt_ovflsize) { +storekey: if (__ovfl_put(t, key, &pg) == RET_ERROR) + return (RET_ERROR); + tkey.data = kb; + tkey.size = NOVFLSIZE; + memmove(kb, &pg, sizeof(pgno_t)); + memmove(kb + sizeof(pgno_t), + &key->size, sizeof(size_t)); + dflags |= P_BIGKEY; + key = &tkey; + } + if (key->size + data->size > t->bt_ovflsize) { + if (__ovfl_put(t, data, &pg) == RET_ERROR) + return (RET_ERROR); + tdata.data = db; + tdata.size = NOVFLSIZE; + memmove(db, &pg, sizeof(pgno_t)); + memmove(db + sizeof(pgno_t), + &data->size, sizeof(size_t)); + dflags |= P_BIGDATA; + data = &tdata; + } + if (key->size + data->size > t->bt_ovflsize) + goto storekey; + } + + /* Replace the cursor. */ + if (flags == R_CURSOR) { + if ((h = mpool_get(t->bt_mp, t->bt_bcursor.pgno, 0)) == NULL) + return (RET_ERROR); + index = t->bt_bcursor.index; + goto delete; + } + + /* + * Find the key to delete, or, the location at which to insert. Bt_fast + * and __bt_search pin the returned page. + */ + if (t->bt_order == NOT || (e = bt_fast(t, key, data, &exact)) == NULL) + if ((e = __bt_search(t, key, &exact)) == NULL) + return (RET_ERROR); + h = e->page; + index = e->index; + + /* + * Add the specified key/data pair to the tree. If an identical key + * is already in the tree, and R_NOOVERWRITE is set, an error is + * returned. If R_NOOVERWRITE is not set, the key is either added (if + * duplicates are permitted) or an error is returned. + * + * Pages are split as required. + */ + switch (flags) { + case R_NOOVERWRITE: + if (!exact) + break; + /* + * One special case is if the cursor references the record and + * it's been flagged for deletion. Then, we delete the record, + * leaving the cursor there -- this means that the inserted + * record will not be seen in a cursor scan. + */ + if (ISSET(t, B_DELCRSR) && t->bt_bcursor.pgno == h->pgno && + t->bt_bcursor.index == index) { + CLR(t, B_DELCRSR); + goto delete; + } + mpool_put(t->bt_mp, h, 0); + return (RET_SPECIAL); + default: + if (!exact || !ISSET(t, B_NODUPS)) + break; +delete: if (__bt_dleaf(t, h, index) == RET_ERROR) { + mpool_put(t->bt_mp, h, 0); + return (RET_ERROR); + } + break; + } + + /* + * If not enough room, or the user has put a ceiling on the number of + * keys permitted in the page, split the page. The split code will + * insert the key and data and unpin the current page. If inserting + * into the offset array, shift the pointers up. + */ + nbytes = NBLEAFDBT(key->size, data->size); + if (h->upper - h->lower < nbytes + sizeof(indx_t)) { + if ((status = __bt_split(t, h, key, + data, dflags, nbytes, index)) != RET_SUCCESS) + return (status); + goto success; + } + + if (index < (nxtindex = NEXTINDEX(h))) + memmove(h->linp + index + 1, h->linp + index, + (nxtindex - index) * sizeof(indx_t)); + h->lower += sizeof(indx_t); + + h->linp[index] = h->upper -= nbytes; + dest = (char *)h + h->upper; + WR_BLEAF(dest, key, data, dflags); + + if (t->bt_order == NOT) + if (h->nextpg == P_INVALID) { + if (index == NEXTINDEX(h) - 1) { + t->bt_order = FORWARD; + t->bt_last.index = index; + t->bt_last.pgno = h->pgno; + } + } else if (h->prevpg == P_INVALID) { + if (index == 0) { + t->bt_order = BACK; + t->bt_last.index = 0; + t->bt_last.pgno = h->pgno; + } + } + + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + +success: + if (flags == R_SETCURSOR) { + t->bt_bcursor.pgno = e->page->pgno; + t->bt_bcursor.index = e->index; + } + SET(t, B_MODIFIED); + return (RET_SUCCESS); +} + +#ifdef STATISTICS +u_long bt_cache_hit, bt_cache_miss; +#endif + +/* + * BT_FAST -- Do a quick check for sorted data. + * + * Parameters: + * t: tree + * key: key to insert + * + * Returns: + * EPG for new record or NULL if not found. + */ +static EPG * +bt_fast(t, key, data, exactp) + BTREE *t; + const DBT *key, *data; + int *exactp; +{ + EPG e; + PAGE *h; + size_t nbytes; + int cmp; + + if ((h = mpool_get(t->bt_mp, t->bt_last.pgno, 0)) == NULL) { + t->bt_order = NOT; + return (NULL); + } + e.page = h; + e.index = t->bt_last.index; + + /* + * If won't fit in this page or have too many keys in this page, have + * to search to get split stack. + */ + nbytes = NBLEAFDBT(key->size, data->size); + if (h->upper - h->lower < nbytes + sizeof(indx_t)) + goto miss; + + if (t->bt_order == FORWARD) { + if (e.page->nextpg != P_INVALID) + goto miss; + if (e.index != NEXTINDEX(h) - 1) + goto miss; + if ((cmp = __bt_cmp(t, key, &e)) < 0) + goto miss; + t->bt_last.index = cmp ? ++e.index : e.index; + } else { + if (e.page->prevpg != P_INVALID) + goto miss; + if (e.index != 0) + goto miss; + if ((cmp = __bt_cmp(t, key, &e)) > 0) + goto miss; + t->bt_last.index = 0; + } + *exactp = cmp == 0; +#ifdef STATISTICS + ++bt_cache_hit; +#endif + return (&e); + +miss: +#ifdef STATISTICS + ++bt_cache_miss; +#endif + t->bt_order = NOT; + mpool_put(t->bt_mp, h, 0); + return (NULL); +} diff --git a/lib/libc/db/btree/bt_search.c b/lib/libc/db/btree/bt_search.c new file mode 100644 index 0000000000..06aba1126b --- /dev/null +++ b/lib/libc/db/btree/bt_search.c @@ -0,0 +1,119 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_search.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include + +#include +#include "btree.h" + +/* + * __BT_SEARCH -- Search a btree for a key. + * + * Parameters: + * t: tree to search + * key: key to find + * exactp: pointer to exact match flag + * + * Returns: + * EPG for matching record, if any, or the EPG for the location of the + * key, if it were inserted into the tree. + * + * Warnings: + * The EPG returned is in static memory, and will be overwritten by the + * next search of any kind in any tree. + */ +EPG * +__bt_search(t, key, exactp) + BTREE *t; + const DBT *key; + int *exactp; +{ + register indx_t index; + register int base, cmp, lim; + register PAGE *h; + pgno_t pg; + static EPG e; + + BT_CLR(t); + for (pg = P_ROOT;;) { + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (NULL); + + /* Do a binary search on the current page. */ + e.page = h; + for (base = 0, lim = NEXTINDEX(h); lim; lim >>= 1) { + e.index = index = base + (lim >> 1); + if ((cmp = __bt_cmp(t, key, &e)) == 0) { + if (h->flags & P_BLEAF) { + *exactp = 1; + return (&e); + } + goto next; + } + if (cmp > 0) { + base = index + 1; + --lim; + } + } + + /* If it's a leaf page, we're done. */ + if (h->flags & P_BLEAF) { + e.index = base; + *exactp = 0; + return (&e); + } + + /* + * No match found. Base is the smallest index greater than + * key and may be zero or a last + 1 index. If it's non-zero, + * decrement by one, and record the internal page which should + * be a parent page for the key. If a split later occurs, the + * inserted page will be to the right of the saved page. + */ + index = base ? base - 1 : base; + +next: if (__bt_push(t, h->pgno, index) == RET_ERROR) + return (NULL); + pg = GETBINTERNAL(h, index)->pgno; + mpool_put(t->bt_mp, h, 0); + } +} diff --git a/lib/libc/db/btree/bt_seq.c b/lib/libc/db/btree/bt_seq.c new file mode 100644 index 0000000000..03f8f09a28 --- /dev/null +++ b/lib/libc/db/btree/bt_seq.c @@ -0,0 +1,365 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_seq.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include +#include + +#include +#include "btree.h" + +static int bt_seqadv __P((BTREE *, EPG *, int)); +static int bt_seqset __P((BTREE *, EPG *, DBT *, int)); + +/* + * Sequential scan support. + * + * The tree can be scanned sequentially, starting from either end of the tree + * or from any specific key. A scan request before any scanning is done is + * initialized as starting from the least node. + * + * Each tree has an EPGNO which has the current position of the cursor. The + * cursor has to survive deletions/insertions in the tree without losing its + * position. This is done by noting deletions without doing them, and then + * doing them when the cursor moves (or the tree is closed). + */ + +/* + * __BT_SEQ -- Btree sequential scan interface. + * + * Parameters: + * dbp: pointer to access method + * key: key for positioning and return value + * data: data return value + * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV. + * + * Returns: + * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. + */ +int +__bt_seq(dbp, key, data, flags) + const DB *dbp; + DBT *key, *data; + u_int flags; +{ + BTREE *t; + EPG e; + int status; + + /* + * If scan unitialized as yet, or starting at a specific record, set + * the scan to a specific key. Both bt_seqset and bt_seqadv pin the + * page the cursor references if they're successful. + */ + t = dbp->internal; + switch(flags) { + case R_NEXT: + case R_PREV: + if (ISSET(t, B_SEQINIT)) { + status = bt_seqadv(t, &e, flags); + break; + } + /* FALLTHROUGH */ + case R_CURSOR: + case R_FIRST: + case R_LAST: + status = bt_seqset(t, &e, key, flags); + break; + default: + errno = EINVAL; + return (RET_ERROR); + } + + if (status == RET_SUCCESS) { + status = __bt_ret(t, &e, key, data); + + /* Update the actual cursor. */ + t->bt_bcursor.pgno = e.page->pgno; + t->bt_bcursor.index = e.index; + mpool_put(t->bt_mp, e.page, 0); + SET(t, B_SEQINIT); + } + return (status); +} + +/* + * BT_SEQSET -- Set the sequential scan to a specific key. + * + * Parameters: + * t: tree + * ep: storage for returned key + * key: key for initial scan position + * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV + * + * Side effects: + * Pins the page the cursor references. + * + * Returns: + * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. + */ +static int +bt_seqset(t, ep, key, flags) + BTREE *t; + EPG *ep; + DBT *key; + int flags; +{ + EPG *e; + PAGE *h; + pgno_t pg; + int exact; + + /* + * Delete any already deleted record that we've been saving because + * the cursor pointed to it. Since going to a specific key, should + * delete any logically deleted records so they aren't found. + */ + if (ISSET(t, B_DELCRSR) && __bt_crsrdel(t, &t->bt_bcursor)) + return (RET_ERROR); + + /* + * Find the first, last or specific key in the tree and point the cursor + * at it. The cursor may not be moved until a new key has been found. + */ + switch(flags) { + case R_CURSOR: /* Keyed scan. */ + /* + * Find the first instance of the key or the smallest key which + * is greater than or equal to the specified key. If run out + * of keys, return RET_SPECIAL. + */ + if (key->data == NULL || key->size == 0) { + errno = EINVAL; + return (RET_ERROR); + } + e = __bt_first(t, key, &exact); /* Returns pinned page. */ + if (e == NULL) + return (RET_ERROR); + /* + * If at the end of a page, skip any empty pages and find the + * next entry. + */ + if (e->index == NEXTINDEX(e->page)) { + h = e->page; + do { + pg = h->nextpg; + mpool_put(t->bt_mp, h, 0); + if (pg == P_INVALID) + return (RET_SPECIAL); + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + } while (NEXTINDEX(h) == 0); + e->index = 0; + e->page = h; + } + *ep = *e; + break; + case R_FIRST: /* First record. */ + case R_NEXT: + /* Walk down the left-hand side of the tree. */ + for (pg = P_ROOT;;) { + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + if (h->flags & (P_BLEAF | P_RLEAF)) + break; + pg = GETBINTERNAL(h, 0)->pgno; + mpool_put(t->bt_mp, h, 0); + } + + /* Skip any empty pages. */ + while (NEXTINDEX(h) == 0 && h->nextpg != P_INVALID) { + pg = h->nextpg; + mpool_put(t->bt_mp, h, 0); + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + } + + if (NEXTINDEX(h) == 0) { + mpool_put(t->bt_mp, h, 0); + return (RET_SPECIAL); + } + + ep->page = h; + ep->index = 0; + break; + case R_LAST: /* Last record. */ + case R_PREV: + /* Walk down the right-hand side of the tree. */ + for (pg = P_ROOT;;) { + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + if (h->flags & (P_BLEAF | P_RLEAF)) + break; + pg = GETBINTERNAL(h, NEXTINDEX(h) - 1)->pgno; + mpool_put(t->bt_mp, h, 0); + } + + /* Skip any empty pages. */ + while (NEXTINDEX(h) == 0 && h->prevpg != P_INVALID) { + pg = h->prevpg; + mpool_put(t->bt_mp, h, 0); + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + } + + if (NEXTINDEX(h) == 0) { + mpool_put(t->bt_mp, h, 0); + return (RET_SPECIAL); + } + + ep->page = h; + ep->index = NEXTINDEX(h) - 1; + break; + } + return (RET_SUCCESS); +} + +/* + * BT_SEQADVANCE -- Advance the sequential scan. + * + * Parameters: + * t: tree + * flags: R_NEXT, R_PREV + * + * Side effects: + * Pins the page the new key/data record is on. + * + * Returns: + * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. + */ +static int +bt_seqadv(t, e, flags) + BTREE *t; + EPG *e; + int flags; +{ + EPGNO *c, delc; + PAGE *h; + indx_t index; + pgno_t pg; + + /* Save the current cursor if going to delete it. */ + c = &t->bt_bcursor; + if (ISSET(t, B_DELCRSR)) + delc = *c; + + if ((h = mpool_get(t->bt_mp, c->pgno, 0)) == NULL) + return (RET_ERROR); + + /* + * Find the next/previous record in the tree and point the cursor at it. + * The cursor may not be moved until a new key has been found. + */ + index = c->index; + switch(flags) { + case R_NEXT: /* Next record. */ + if (++index == NEXTINDEX(h)) { + do { + pg = h->nextpg; + mpool_put(t->bt_mp, h, 0); + if (pg == P_INVALID) + return (RET_SPECIAL); + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + } while (NEXTINDEX(h) == 0); + index = 0; + } + break; + case R_PREV: /* Previous record. */ + if (index-- == 0) { + do { + pg = h->prevpg; + mpool_put(t->bt_mp, h, 0); + if (pg == P_INVALID) + return (RET_SPECIAL); + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + } while (NEXTINDEX(h) == 0); + index = NEXTINDEX(h) - 1; + } + break; + } + + e->page = h; + e->index = index; + + /* + * Delete any already deleted record that we've been saving because the + * cursor pointed to it. This could cause the new index to be shifted + * down by one if the record we're deleting is on the same page and has + * a larger index. + */ + if (ISSET(t, B_DELCRSR)) { + CLR(t, B_DELCRSR); /* Don't try twice. */ + if (c->pgno == delc.pgno && c->index > delc.index) + --c->index; + if (__bt_crsrdel(t, &delc)) + return (RET_ERROR); + } + return (RET_SUCCESS); +} + +/* + * __BT_CRSRDEL -- Delete the record referenced by the cursor. + * + * Parameters: + * t: tree + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__bt_crsrdel(t, c) + BTREE *t; + EPGNO *c; +{ + PAGE *h; + int status; + + CLR(t, B_DELCRSR); /* Don't try twice. */ + if ((h = mpool_get(t->bt_mp, c->pgno, 0)) == NULL) + return (RET_ERROR); + status = __bt_dleaf(t, h, c->index); + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + return (status); +} diff --git a/lib/libc/db/btree/bt_split.c b/lib/libc/db/btree/bt_split.c new file mode 100644 index 0000000000..754108e6a8 --- /dev/null +++ b/lib/libc/db/btree/bt_split.c @@ -0,0 +1,826 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_split.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#define __DBINTERFACE_PRIVATE +#include +#include +#include +#include + +#include +#include "btree.h" + +static int bt_broot __P((BTREE *, PAGE *, PAGE *, PAGE *)); +static PAGE *bt_page + __P((BTREE *, PAGE *, PAGE **, PAGE **, u_int *, size_t)); +static int bt_preserve __P((BTREE *, pgno_t)); +static PAGE *bt_psplit + __P((BTREE *, PAGE *, PAGE *, PAGE *, u_int *, size_t)); +static PAGE *bt_root + __P((BTREE *, PAGE *, PAGE **, PAGE **, u_int *, size_t)); +static int bt_rroot __P((BTREE *, PAGE *, PAGE *, PAGE *)); +static recno_t rec_total __P((PAGE *)); + +#ifdef STATISTICS +u_long bt_rootsplit, bt_split, bt_sortsplit, bt_pfxsaved; +#endif + +/* + * __BT_SPLIT -- Split the tree. + * + * Parameters: + * t: tree + * sp: page to split + * key: key to insert + * data: data to insert + * flags: BIGKEY/BIGDATA flags + * ilen: insert length + * skip: index to leave open + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__bt_split(t, sp, key, data, flags, ilen, skip) + BTREE *t; + PAGE *sp; + const DBT *key, *data; + u_long flags; + size_t ilen; + u_int skip; +{ + BINTERNAL *bi; + BLEAF *bl, *tbl; + DBT a, b; + EPGNO *parent; + PAGE *h, *l, *r, *lchild, *rchild; + indx_t nxtindex; + size_t n, nbytes, nksize; + int parentsplit; + char *dest; + + /* + * Split the page into two pages, l and r. The split routines return + * a pointer to the page into which the key should be inserted and with + * skip set to the offset which should be used. Additionally, l and r + * are pinned. + */ + h = sp->pgno == P_ROOT ? + bt_root(t, sp, &l, &r, &skip, ilen) : + bt_page(t, sp, &l, &r, &skip, ilen); + if (h == NULL) + return (RET_ERROR); + + /* + * Insert the new key/data pair into the leaf page. (Key inserts + * always cause a leaf page to split first.) + */ + h->linp[skip] = h->upper -= ilen; + dest = (char *)h + h->upper; + if (ISSET(t, R_RECNO)) + WR_RLEAF(dest, data, flags) + else + WR_BLEAF(dest, key, data, flags) + + /* If the root page was split, make it look right. */ + if (sp->pgno == P_ROOT && + (ISSET(t, R_RECNO) ? + bt_rroot(t, sp, l, r) : bt_broot(t, sp, l, r)) == RET_ERROR) + goto err2; + + /* + * Now we walk the parent page stack -- a LIFO stack of the pages that + * were traversed when we searched for the page that split. Each stack + * entry is a page number and a page index offset. The offset is for + * the page traversed on the search. We've just split a page, so we + * have to insert a new key into the parent page. + * + * If the insert into the parent page causes it to split, may have to + * continue splitting all the way up the tree. We stop if the root + * splits or the page inserted into didn't have to split to hold the + * new key. Some algorithms replace the key for the old page as well + * as the new page. We don't, as there's no reason to believe that the + * first key on the old page is any better than the key we have, and, + * in the case of a key being placed at index 0 causing the split, the + * key is unavailable. + * + * There are a maximum of 5 pages pinned at any time. We keep the left + * and right pages pinned while working on the parent. The 5 are the + * two children, left parent and right parent (when the parent splits) + * and the root page or the overflow key page when calling bt_preserve. + * This code must make sure that all pins are released other than the + * root page or overflow page which is unlocked elsewhere. + */ + while ((parent = BT_POP(t)) != NULL) { + lchild = l; + rchild = r; + + /* Get the parent page. */ + if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL) + goto err2; + + /* + * The new key goes ONE AFTER the index, because the split + * was to the right. + */ + skip = parent->index + 1; + + /* + * Calculate the space needed on the parent page. + * + * Prefix trees: space hack when inserting into BINTERNAL + * pages. Retain only what's needed to distinguish between + * the new entry and the LAST entry on the page to its left. + * If the keys compare equal, retain the entire key. Note, + * we don't touch overflow keys, and the entire key must be + * retained for the next-to-left most key on the leftmost + * page of each level, or the search will fail. Applicable + * ONLY to internal pages that have leaf pages as children. + * Further reduction of the key between pairs of internal + * pages loses too much information. + */ + switch (rchild->flags & P_TYPE) { + case P_BINTERNAL: + bi = GETBINTERNAL(rchild, 0); + nbytes = NBINTERNAL(bi->ksize); + break; + case P_BLEAF: + bl = GETBLEAF(rchild, 0); + nbytes = NBINTERNAL(bl->ksize); + if (t->bt_pfx && !(bl->flags & P_BIGKEY) && + (h->prevpg != P_INVALID || skip > 1)) { + tbl = GETBLEAF(lchild, NEXTINDEX(lchild) - 1); + a.size = tbl->ksize; + a.data = tbl->bytes; + b.size = bl->ksize; + b.data = bl->bytes; + nksize = t->bt_pfx(&a, &b); + n = NBINTERNAL(nksize); + if (n < nbytes) { +#ifdef STATISTICS + bt_pfxsaved += nbytes - n; +#endif + nbytes = n; + } else + nksize = 0; + } else + nksize = 0; + break; + case P_RINTERNAL: + case P_RLEAF: + nbytes = NRINTERNAL; + break; + default: + abort(); + } + + /* Split the parent page if necessary or shift the indices. */ + if (h->upper - h->lower < nbytes + sizeof(indx_t)) { + sp = h; + h = h->pgno == P_ROOT ? + bt_root(t, h, &l, &r, &skip, nbytes) : + bt_page(t, h, &l, &r, &skip, nbytes); + if (h == NULL) + goto err1; + parentsplit = 1; + } else { + if (skip < (nxtindex = NEXTINDEX(h))) + memmove(h->linp + skip + 1, h->linp + skip, + (nxtindex - skip) * sizeof(indx_t)); + h->lower += sizeof(indx_t); + parentsplit = 0; + } + + /* Insert the key into the parent page. */ + switch(rchild->flags & P_TYPE) { + case P_BINTERNAL: + h->linp[skip] = h->upper -= nbytes; + dest = (char *)h + h->linp[skip]; + memmove(dest, bi, nbytes); + ((BINTERNAL *)dest)->pgno = rchild->pgno; + break; + case P_BLEAF: + h->linp[skip] = h->upper -= nbytes; + dest = (char *)h + h->linp[skip]; + WR_BINTERNAL(dest, nksize ? nksize : bl->ksize, + rchild->pgno, bl->flags & P_BIGKEY); + memmove(dest, bl->bytes, nksize ? nksize : bl->ksize); + if (bl->flags & P_BIGKEY && + bt_preserve(t, *(pgno_t *)bl->bytes) == RET_ERROR) + goto err1; + break; + case P_RINTERNAL: + /* + * Update the left page count. If split + * added at index 0, fix the correct page. + */ + if (skip > 0) + dest = (char *)h + h->linp[skip - 1]; + else + dest = (char *)l + l->linp[NEXTINDEX(l) - 1]; + ((RINTERNAL *)dest)->nrecs = rec_total(lchild); + ((RINTERNAL *)dest)->pgno = lchild->pgno; + + /* Update the right page count. */ + h->linp[skip] = h->upper -= nbytes; + dest = (char *)h + h->linp[skip]; + ((RINTERNAL *)dest)->nrecs = rec_total(rchild); + ((RINTERNAL *)dest)->pgno = rchild->pgno; + break; + case P_RLEAF: + /* + * Update the left page count. If split + * added at index 0, fix the correct page. + */ + if (skip > 0) + dest = (char *)h + h->linp[skip - 1]; + else + dest = (char *)l + l->linp[NEXTINDEX(l) - 1]; + ((RINTERNAL *)dest)->nrecs = NEXTINDEX(lchild); + ((RINTERNAL *)dest)->pgno = lchild->pgno; + + /* Update the right page count. */ + h->linp[skip] = h->upper -= nbytes; + dest = (char *)h + h->linp[skip]; + ((RINTERNAL *)dest)->nrecs = NEXTINDEX(rchild); + ((RINTERNAL *)dest)->pgno = rchild->pgno; + break; + default: + abort(); + } + + /* Unpin the held pages. */ + if (!parentsplit) { + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + break; + } + + /* If the root page was split, make it look right. */ + if (sp->pgno == P_ROOT && + (ISSET(t, R_RECNO) ? + bt_rroot(t, sp, l, r) : bt_broot(t, sp, l, r)) == RET_ERROR) + goto err1; + + mpool_put(t->bt_mp, lchild, MPOOL_DIRTY); + mpool_put(t->bt_mp, rchild, MPOOL_DIRTY); + } + + /* Unpin the held pages. */ + mpool_put(t->bt_mp, l, MPOOL_DIRTY); + mpool_put(t->bt_mp, r, MPOOL_DIRTY); + + /* Clear any pages left on the stack. */ + return (RET_SUCCESS); + + /* + * If something fails in the above loop we were already walking back + * up the tree and the tree is now inconsistent. Nothing much we can + * do about it but release any memory we're holding. + */ +err1: mpool_put(t->bt_mp, lchild, MPOOL_DIRTY); + mpool_put(t->bt_mp, rchild, MPOOL_DIRTY); + +err2: mpool_put(t->bt_mp, l, 0); + mpool_put(t->bt_mp, r, 0); + __dbpanic(t->bt_dbp); + return (RET_ERROR); +} + +/* + * BT_PAGE -- Split a non-root page of a btree. + * + * Parameters: + * t: tree + * h: root page + * lp: pointer to left page pointer + * rp: pointer to right page pointer + * skip: pointer to index to leave open + * ilen: insert length + * + * Returns: + * Pointer to page in which to insert or NULL on error. + */ +static PAGE * +bt_page(t, h, lp, rp, skip, ilen) + BTREE *t; + PAGE *h, **lp, **rp; + u_int *skip; + size_t ilen; +{ + PAGE *l, *r, *tp; + pgno_t npg; + +#ifdef STATISTICS + ++bt_split; +#endif + /* Put the new right page for the split into place. */ + if ((r = __bt_new(t, &npg)) == NULL) + return (NULL); + r->pgno = npg; + r->lower = BTDATAOFF; + r->upper = t->bt_psize; + r->nextpg = h->nextpg; + r->prevpg = h->pgno; + r->flags = h->flags & P_TYPE; + + /* + * If we're splitting the last page on a level because we're appending + * a key to it (skip is NEXTINDEX()), it's likely that the data is + * sorted. Adding an empty page on the side of the level is less work + * and can push the fill factor much higher than normal. If we're + * wrong it's no big deal, we'll just do the split the right way next + * time. It may look like it's equally easy to do a similar hack for + * reverse sorted data, that is, split the tree left, but it's not. + * Don't even try. + */ + if (h->nextpg == P_INVALID && *skip == NEXTINDEX(h)) { +#ifdef STATISTICS + ++bt_sortsplit; +#endif + h->nextpg = r->pgno; + r->lower = BTDATAOFF + sizeof(indx_t); + *skip = 0; + *lp = h; + *rp = r; + return (r); + } + + /* Put the new left page for the split into place. */ + if ((l = malloc(t->bt_psize)) == NULL) { + mpool_put(t->bt_mp, r, 0); + return (NULL); + } + l->pgno = h->pgno; + l->nextpg = r->pgno; + l->prevpg = h->prevpg; + l->lower = BTDATAOFF; + l->upper = t->bt_psize; + l->flags = h->flags & P_TYPE; + + /* Fix up the previous pointer of the page after the split page. */ + if (h->nextpg != P_INVALID) { + if ((tp = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL) { + free(l); + /* XXX mpool_free(t->bt_mp, r->pgno); */ + return (NULL); + } + tp->prevpg = r->pgno; + mpool_put(t->bt_mp, tp, 0); + } + + /* + * Split right. The key/data pairs aren't sorted in the btree page so + * it's simpler to copy the data from the split page onto two new pages + * instead of copying half the data to the right page and compacting + * the left page in place. Since the left page can't change, we have + * to swap the original and the allocated left page after the split. + */ + tp = bt_psplit(t, h, l, r, skip, ilen); + + /* Move the new left page onto the old left page. */ + memmove(h, l, t->bt_psize); + if (tp == l) + tp = h; + free(l); + + *lp = h; + *rp = r; + return (tp); +} + +/* + * BT_ROOT -- Split the root page of a btree. + * + * Parameters: + * t: tree + * h: root page + * lp: pointer to left page pointer + * rp: pointer to right page pointer + * skip: pointer to index to leave open + * ilen: insert length + * + * Returns: + * Pointer to page in which to insert or NULL on error. + */ +static PAGE * +bt_root(t, h, lp, rp, skip, ilen) + BTREE *t; + PAGE *h, **lp, **rp; + u_int *skip; + size_t ilen; +{ + PAGE *l, *r, *tp; + pgno_t lnpg, rnpg; + +#ifdef STATISTICS + ++bt_split; + ++bt_rootsplit; +#endif + /* Put the new left and right pages for the split into place. */ + if ((l = __bt_new(t, &lnpg)) == NULL || + (r = __bt_new(t, &rnpg)) == NULL) + return (NULL); + l->pgno = lnpg; + r->pgno = rnpg; + l->nextpg = r->pgno; + r->prevpg = l->pgno; + l->prevpg = r->nextpg = P_INVALID; + l->lower = r->lower = BTDATAOFF; + l->upper = r->upper = t->bt_psize; + l->flags = r->flags = h->flags & P_TYPE; + + /* Split the root page. */ + tp = bt_psplit(t, h, l, r, skip, ilen); + + *lp = l; + *rp = r; + return (tp); +} + +/* + * BT_RROOT -- Fix up the recno root page after it has been split. + * + * Parameters: + * t: tree + * h: root page + * l: left page + * r: right page + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +static int +bt_rroot(t, h, l, r) + BTREE *t; + PAGE *h, *l, *r; +{ + char *dest; + + /* Insert the left and right keys, set the header information. */ + h->linp[0] = h->upper = t->bt_psize - NRINTERNAL; + dest = (char *)h + h->upper; + WR_RINTERNAL(dest, + l->flags & P_RLEAF ? NEXTINDEX(l) : rec_total(l), l->pgno); + + h->linp[1] = h->upper -= NRINTERNAL; + dest = (char *)h + h->upper; + WR_RINTERNAL(dest, + r->flags & P_RLEAF ? NEXTINDEX(r) : rec_total(r), r->pgno); + + h->lower = BTDATAOFF + 2 * sizeof(indx_t); + + /* Unpin the root page, set to recno internal page. */ + h->flags &= ~P_TYPE; + h->flags |= P_RINTERNAL; + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + + return (RET_SUCCESS); +} + +/* + * BT_BROOT -- Fix up the btree root page after it has been split. + * + * Parameters: + * t: tree + * h: root page + * l: left page + * r: right page + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +static int +bt_broot(t, h, l, r) + BTREE *t; + PAGE *h, *l, *r; +{ + BINTERNAL *bi; + BLEAF *bl; + size_t nbytes; + char *dest; + + /* + * If the root page was a leaf page, change it into an internal page. + * We copy the key we split on (but not the key's data, in the case of + * a leaf page) to the new root page. + * + * The btree comparison code guarantees that the left-most key on any + * level of the tree is never used, so it doesn't need to be filled in. + */ + nbytes = NBINTERNAL(0); + h->linp[0] = h->upper = t->bt_psize - nbytes; + dest = (char *)h + h->upper; + WR_BINTERNAL(dest, 0, l->pgno, 0); + + switch(h->flags & P_TYPE) { + case P_BLEAF: + bl = GETBLEAF(r, 0); + nbytes = NBINTERNAL(bl->ksize); + h->linp[1] = h->upper -= nbytes; + dest = (char *)h + h->upper; + WR_BINTERNAL(dest, bl->ksize, r->pgno, 0); + memmove(dest, bl->bytes, bl->ksize); + + /* + * If the key is on an overflow page, mark the overflow chain + * so it isn't deleted when the leaf copy of the key is deleted. + */ + if (bl->flags & P_BIGKEY && + bt_preserve(t, *(pgno_t *)bl->bytes) == RET_ERROR) + return (RET_ERROR); + break; + case P_BINTERNAL: + bi = GETBINTERNAL(r, 0); + nbytes = NBINTERNAL(bi->ksize); + h->linp[1] = h->upper -= nbytes; + dest = (char *)h + h->upper; + memmove(dest, bi, nbytes); + ((BINTERNAL *)dest)->pgno = r->pgno; + break; + default: + abort(); + } + + /* There are two keys on the page. */ + h->lower = BTDATAOFF + 2 * sizeof(indx_t); + + /* Unpin the root page, set to btree internal page. */ + h->flags &= ~P_TYPE; + h->flags |= P_BINTERNAL; + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + + return (RET_SUCCESS); +} + +/* + * BT_PSPLIT -- Do the real work of splitting the page. + * + * Parameters: + * t: tree + * h: page to be split + * l: page to put lower half of data + * r: page to put upper half of data + * pskip: pointer to index to leave open + * ilen: insert length + * + * Returns: + * Pointer to page in which to insert. + */ +static PAGE * +bt_psplit(t, h, l, r, pskip, ilen) + BTREE *t; + PAGE *h, *l, *r; + u_int *pskip; + size_t ilen; +{ + BINTERNAL *bi; + BLEAF *bl; + RLEAF *rl; + EPGNO *c; + PAGE *rval; + void *src; + indx_t full, half, nxt, off, skip, top, used; + size_t nbytes; + int bigkeycnt, isbigkey; + + /* + * Split the data to the left and right pages. Leave the skip index + * open. Additionally, make some effort not to split on an overflow + * key. This makes internal page processing faster and can save + * space as overflow keys used by internal pages are never deleted. + */ + bigkeycnt = 0; + skip = *pskip; + full = t->bt_psize - BTDATAOFF; + half = full / 2; + used = 0; + for (nxt = off = 0, top = NEXTINDEX(h); nxt < top; ++off) { + if (skip == off) { + nbytes = ilen; + isbigkey = 0; /* XXX: not really known. */ + } else + switch (h->flags & P_TYPE) { + case P_BINTERNAL: + src = bi = GETBINTERNAL(h, nxt); + nbytes = NBINTERNAL(bi->ksize); + isbigkey = bi->flags & P_BIGKEY; + break; + case P_BLEAF: + src = bl = GETBLEAF(h, nxt); + nbytes = NBLEAF(bl); + isbigkey = bl->flags & P_BIGKEY; + break; + case P_RINTERNAL: + src = GETRINTERNAL(h, nxt); + nbytes = NRINTERNAL; + isbigkey = 0; + break; + case P_RLEAF: + src = rl = GETRLEAF(h, nxt); + nbytes = NRLEAF(rl); + isbigkey = 0; + break; + default: + abort(); + } + + /* + * If the key/data pairs are substantial fractions of the max + * possible size for the page, it's possible to get situations + * where we decide to try and copy too much onto the left page. + * Make sure that doesn't happen. + */ + if (skip <= off && used + nbytes >= full) { + --off; + break; + } + + /* Copy the key/data pair, if not the skipped index. */ + if (skip != off) { + ++nxt; + + l->linp[off] = l->upper -= nbytes; + memmove((char *)l + l->upper, src, nbytes); + } + + used += nbytes; + if (used >= half) { + if (!isbigkey || bigkeycnt == 3) + break; + else + ++bigkeycnt; + } + } + + /* + * Off is the last offset that's valid for the left page. + * Nxt is the first offset to be placed on the right page. + */ + l->lower += (off + 1) * sizeof(indx_t); + + /* + * If splitting the page that the cursor was on, the cursor has to be + * adjusted to point to the same record as before the split. If the + * cursor is at or past the skipped slot, the cursor is incremented by + * one. If the cursor is on the right page, it is decremented by the + * number of records split to the left page. + * + * Don't bother checking for the B_SEQINIT flag, the page number will + * be P_INVALID. + */ + c = &t->bt_bcursor; + if (c->pgno == h->pgno) { + if (c->index >= skip) + ++c->index; + if (c->index < nxt) /* Left page. */ + c->pgno = l->pgno; + else { /* Right page. */ + c->pgno = r->pgno; + c->index -= nxt; + } + } + + /* + * If the skipped index was on the left page, just return that page. + * Otherwise, adjust the skip index to reflect the new position on + * the right page. + */ + if (skip <= off) { + skip = 0; + rval = l; + } else { + rval = r; + *pskip -= nxt; + } + + for (off = 0; nxt < top; ++off) { + if (skip == nxt) { + ++off; + skip = 0; + } + switch (h->flags & P_TYPE) { + case P_BINTERNAL: + src = bi = GETBINTERNAL(h, nxt); + nbytes = NBINTERNAL(bi->ksize); + break; + case P_BLEAF: + src = bl = GETBLEAF(h, nxt); + nbytes = NBLEAF(bl); + break; + case P_RINTERNAL: + src = GETRINTERNAL(h, nxt); + nbytes = NRINTERNAL; + break; + case P_RLEAF: + src = rl = GETRLEAF(h, nxt); + nbytes = NRLEAF(rl); + break; + default: + abort(); + } + ++nxt; + r->linp[off] = r->upper -= nbytes; + memmove((char *)r + r->upper, src, nbytes); + } + r->lower += off * sizeof(indx_t); + + /* If the key is being appended to the page, adjust the index. */ + if (skip == top) + r->lower += sizeof(indx_t); + + return (rval); +} + +/* + * BT_PRESERVE -- Mark a chain of pages as used by an internal node. + * + * Chains of indirect blocks pointed to by leaf nodes get reclaimed when the + * record that references them gets deleted. Chains pointed to by internal + * pages never get deleted. This routine marks a chain as pointed to by an + * internal page. + * + * Parameters: + * t: tree + * pg: page number of first page in the chain. + * + * Returns: + * RET_SUCCESS, RET_ERROR. + */ +static int +bt_preserve(t, pg) + BTREE *t; + pgno_t pg; +{ + PAGE *h; + + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + h->flags |= P_PRESERVE; + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + return (RET_SUCCESS); +} + +/* + * REC_TOTAL -- Return the number of recno entries below a page. + * + * Parameters: + * h: page + * + * Returns: + * The number of recno entries below a page. + * + * XXX + * These values could be set by the bt_psplit routine. The problem is that the + * entry has to be popped off of the stack etc. or the values have to be passed + * all the way back to bt_split/bt_rroot and it's not very clean. + */ +static recno_t +rec_total(h) + PAGE *h; +{ + recno_t recs; + indx_t nxt, top; + + for (recs = 0, nxt = 0, top = NEXTINDEX(h); nxt < top; ++nxt) + recs += GETRINTERNAL(h, nxt)->nrecs; + return (recs); +} diff --git a/lib/libc/db/btree/bt_stack.c b/lib/libc/db/btree/bt_stack.c new file mode 100644 index 0000000000..5179157792 --- /dev/null +++ b/lib/libc/db/btree/bt_stack.c @@ -0,0 +1,92 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_stack.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +#include +#include "btree.h" + +/* + * When a page splits, a new record has to be inserted into its parent page. + * This page may have to split as well, all the way up to the root. Since + * parent pointers in each page would be expensive, we maintain a stack of + * parent pages as we descend the tree. + * + * XXX + * This is a concurrency problem -- if user a builds a stack, then user b + * splits the tree, then user a tries to split the tree, there's a new level + * in the tree that user a doesn't know about. + */ + +/* + * __BT_PUSH -- Push parent page info onto the stack (LIFO). + * + * Parameters: + * t: tree + * pgno: page + * index: page index + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__bt_push(t, pgno, index) + BTREE *t; + pgno_t pgno; + int index; +{ + if (t->bt_sp == t->bt_maxstack) { + t->bt_maxstack += 50; + if ((t->bt_stack = realloc(t->bt_stack, + t->bt_maxstack * sizeof(EPGNO))) == NULL) { + t->bt_maxstack -= 50; + return (RET_ERROR); + } + } + + t->bt_stack[t->bt_sp].pgno = pgno; + t->bt_stack[t->bt_sp].index = index; + ++t->bt_sp; + return (RET_SUCCESS); +} diff --git a/lib/libc/db/btree/bt_utils.c b/lib/libc/db/btree/bt_utils.c new file mode 100644 index 0000000000..452eb87e42 --- /dev/null +++ b/lib/libc/db/btree/bt_utils.c @@ -0,0 +1,227 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_utils.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +#include +#include "btree.h" + +/* + * __BT_RET -- Build return key/data pair as a result of search or scan. + * + * Parameters: + * t: tree + * d: LEAF to be returned to the user. + * key: user's key structure (NULL if not to be filled in) + * data: user's data structure + * + * Returns: + * RET_SUCCESS, RET_ERROR. + */ +int +__bt_ret(t, e, key, data) + BTREE *t; + EPG *e; + DBT *key, *data; +{ + register BLEAF *bl; + register void *p; + + bl = GETBLEAF(e->page, e->index); + + if (bl->flags & P_BIGDATA) { + if (__ovfl_get(t, bl->bytes + bl->ksize, + &data->size, &t->bt_dbuf, &t->bt_dbufsz)) + return (RET_ERROR); + } else { + /* Use +1 in case the first record retrieved is 0 length. */ + if (bl->dsize + 1 > t->bt_dbufsz) { + if ((p = realloc(t->bt_dbuf, bl->dsize + 1)) == NULL) + return (RET_ERROR); + t->bt_dbuf = p; + t->bt_dbufsz = bl->dsize + 1; + } + memmove(t->bt_dbuf, bl->bytes + bl->ksize, bl->dsize); + data->size = bl->dsize; + } + data->data = t->bt_dbuf; + + if (key == NULL) + return (RET_SUCCESS); + + if (bl->flags & P_BIGKEY) { + if (__ovfl_get(t, bl->bytes, + &key->size, &t->bt_kbuf, &t->bt_kbufsz)) + return (RET_ERROR); + } else { + if (bl->ksize > t->bt_kbufsz) { + if ((p = realloc(t->bt_kbuf, bl->ksize)) == NULL) + return (RET_ERROR); + t->bt_kbuf = p; + t->bt_kbufsz = bl->ksize; + } + memmove(t->bt_kbuf, bl->bytes, bl->ksize); + key->size = bl->ksize; + } + key->data = t->bt_kbuf; + return (RET_SUCCESS); +} + +/* + * __BT_CMP -- Compare a key to a given record. + * + * Parameters: + * t: tree + * k1: DBT pointer of first arg to comparison + * e: pointer to EPG for comparison + * + * Returns: + * < 0 if k1 is < record + * = 0 if k1 is = record + * > 0 if k1 is > record + */ +int +__bt_cmp(t, k1, e) + BTREE *t; + const DBT *k1; + EPG *e; +{ + BINTERNAL *bi; + BLEAF *bl; + DBT k2; + PAGE *h; + void *bigkey; + + /* + * The left-most key on internal pages, at any level of the tree, is + * guaranteed by the following code to be less than any user key. + * This saves us from having to update the leftmost key on an internal + * page when the user inserts a new key in the tree smaller than + * anything we've yet seen. + */ + h = e->page; + if (e->index == 0 && h->prevpg == P_INVALID && !(h->flags & P_BLEAF)) + return (1); + + bigkey = NULL; + if (h->flags & P_BLEAF) { + bl = GETBLEAF(h, e->index); + if (bl->flags & P_BIGKEY) + bigkey = bl->bytes; + else { + k2.data = bl->bytes; + k2.size = bl->ksize; + } + } else { + bi = GETBINTERNAL(h, e->index); + if (bi->flags & P_BIGKEY) + bigkey = bi->bytes; + else { + k2.data = bi->bytes; + k2.size = bi->ksize; + } + } + + if (bigkey) { + if (__ovfl_get(t, bigkey, + &k2.size, &t->bt_dbuf, &t->bt_dbufsz)) + return (RET_ERROR); + k2.data = t->bt_dbuf; + } + return ((*t->bt_cmp)(k1, &k2)); +} + +/* + * __BT_DEFCMP -- Default comparison routine. + * + * Parameters: + * a: DBT #1 + * b: DBT #2 + * + * Returns: + * < 0 if a is < b + * = 0 if a is = b + * > 0 if a is > b + */ +int +__bt_defcmp(a, b) + const DBT *a, *b; +{ + register u_char *p1, *p2; + register int diff, len; + + len = MIN(a->size, b->size); + for (p1 = a->data, p2 = b->data; len--; ++p1, ++p2) + if (diff = *p1 - *p2) + return (diff); + return (a->size - b->size); +} + +/* + * __BT_DEFPFX -- Default prefix routine. + * + * Parameters: + * a: DBT #1 + * b: DBT #2 + * + * Returns: + * Number of bytes needed to distinguish b from a. + */ +int +__bt_defpfx(a, b) + const DBT *a, *b; +{ + register u_char *p1, *p2; + register int len; + int cnt; + + cnt = 1; + len = MIN(a->size, b->size); + for (p1 = a->data, p2 = b->data; len--; ++p1, ++p2, ++cnt) + if (*p1 != *p2) + return (cnt); + + /* a->size must be <= b->size, or they wouldn't be in this order. */ + return (a->size < b->size ? a->size + 1 : a->size); +} diff --git a/lib/libc/db/btree/btree.c b/lib/libc/db/btree/btree.c deleted file mode 100644 index d47699a783..0000000000 --- a/lib/libc/db/btree/btree.c +++ /dev/null @@ -1,752 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Mike Olson. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)btree.c 5.9 (Berkeley) 5/7/91"; -#endif /* LIBC_SCCS and not lint */ - -/* - * btree.c -- implementation of btree access method for 4.4BSD. - * - * The design here is based on that of the btree access method used - * in the Postgres database system at UC Berkeley. The implementation - * is wholly independent of the Postgres code. - * - * This implementation supports btrees on disk (supply a filename) or - * in memory (don't). Public interfaces defined here are: - * - * btree_open() -- wrapper; returns a filled DB struct for - * a btree. - * - * bt_open() -- open a new or existing btree. - * bt_get() -- fetch data from a tree by key. - * bt_seq() -- do a sequential scan on a tree. - * bt_put() -- add data to a tree by key. - * bt_delete() -- remove data from a tree by key. - * bt_close() -- close a btree. - * bt_sync() -- force btree pages to disk (disk trees only). - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "btree.h" - -BTREEINFO _DefaultBTInfo = { - 0, /* flags */ - 0, /* cachesize */ - 0, /* psize */ - strcmp, /* compare */ - 0 -}; - -/* - * BTREE_OPEN -- Wrapper routine to open a btree. - * - * Creates and fills a DB struct, and calls the routine that actually - * opens the btree. - * - * Parameters: - * f: filename to open - * flags: flag bits passed to open - * mode: permission bits, used if O_CREAT specified - * b: BTREEINFO pointer - * - * Returns: - * Filled-in DBT on success; NULL on failure, with errno - * set as appropriate. - * - * Side Effects: - * Allocates memory for the DB struct. - */ - -DB * -btree_open(f, flags, mode, b) - const char *f; - int flags; - int mode; - const BTREEINFO *b; -{ - DB *db; - BTREE t; - - if ((db = (DB *) malloc((unsigned) sizeof(DB))) == (DB *) NULL) - return ((DB *) NULL); - - if ((t = bt_open(f, flags, mode, b)) == (BTREE) NULL) { - (void) free ((char *) db); - return ((DB *) NULL); - } - - db->internal = (char *) t; - db->close = bt_close; - db->del = bt_delete; - db->get = bt_get; - db->put = bt_put; - db->seq = bt_seq; - db->sync = bt_sync; - db->type = DB_BTREE; - - return (db); -} - -/* - * BT_OPEN -- Open a btree. - * - * This routine creates the correct kind (disk or in-memory) of - * btree and initializes it as required. - * - * Parameters: - * f -- name of btree (NULL for in-memory btrees) - * flags -- flags passed to open() - * mode -- mode passed to open () - * b -- BTREEINFO structure, describing btree - * - * Returns: - * (Opaque) pointer to the btree. On failure, returns NULL - * with errno set as appropriate. - * - * Side Effects: - * Allocates memory, opens files. - */ - -BTREE -bt_open(f, flags, mode, b) - char *f; - int flags; - int mode; - BTREEINFO *b; -{ - BTREE_P t; - HTABLE ht; - int nbytes; - int fd; - CURSOR *c; - BTMETA m; - struct stat buf; - - /* use the default info if none was provided */ - if (b == (BTREEINFO *) NULL) - b = &_DefaultBTInfo; - - if ((t = (BTREE_P) malloc((unsigned) sizeof *t)) == (BTREE_P) NULL) - return ((BTREE) NULL); - - if (b->compare) - t->bt_compare = b->compare; - else - t->bt_compare = strcmp; - - t->bt_fname = f; - t->bt_curpage = (BTHEADER *) NULL; - t->bt_free = P_NONE; - c = &(t->bt_cursor); - c->c_pgno = P_NONE; - c->c_index = 0; - c->c_flags = (u_char) NULL; - c->c_key = (char *) NULL; - t->bt_stack = (BTSTACK *) NULL; - t->bt_flags = 0; - - /* - * If no file name was supplied, this is an in-memory btree. - * Otherwise, it's a disk-based btree. - */ - if (f == (char *) NULL) { - /* in memory */ - if ((t->bt_psize = b->psize) < MINPSIZE) { - if (t->bt_psize != 0) { - (void) free ((char *) t); - errno = EINVAL; - return ((BTREE) NULL); - } - t->bt_psize = getpagesize(); - } - - nbytes = HTSIZE * sizeof(HTBUCKET *); - if ((ht = (HTABLE) malloc((unsigned) nbytes)) - == (HTABLE) NULL) { - (void) free((char *) t); - return ((BTREE) NULL); - } - (void) bzero((char *) ht, nbytes); - t->bt_s.bt_ht = ht; - t->bt_npages = 0; - t->bt_lorder = BYTE_ORDER; - if (!(b->flags & R_DUP)) - t->bt_flags |= BTF_NODUPS; - } else { - /* on disk */ - if ((fd = open(f, O_RDONLY, 0)) < 0) { - /* doesn't exist yet, be sure page is big enough */ - if ((t->bt_psize = b->psize) < sizeof(BTHEADER) - && b->psize != 0) { - (void) free((char *) t); - errno = EINVAL; - return ((BTREE) NULL); - } - if (b->lorder == 0) - b->lorder = BYTE_ORDER; - - if (b->lorder != BIG_ENDIAN - && b->lorder != LITTLE_ENDIAN) { - (void) free((char *) t); - errno = EINVAL; - return ((BTREE) NULL); - } - t->bt_lorder = b->lorder; - if (!(b->flags & R_DUP)) - t->bt_flags |= BTF_NODUPS; - } else { - /* exists, get page size from file */ - if (read(fd, &m, sizeof(m)) < sizeof(m)) { - (void) close(fd); - (void) free((char *) t); - errno = EINVAL; - return ((BTREE) NULL); - } - - /* lorder always stored in host-independent format */ - NTOHL(m.m_lorder); - if (m.m_lorder != BIG_ENDIAN - && m.m_lorder != LITTLE_ENDIAN) { - (void) free((char *) t); - errno = EINVAL; - return ((BTREE) NULL); - } - t->bt_lorder = m.m_lorder; - - if (t->bt_lorder != BYTE_ORDER) { - BLSWAP(m.m_magic); - BLSWAP(m.m_version); - BLSWAP(m.m_psize); - BLSWAP(m.m_free); - BLSWAP(m.m_flags); - } - - if (m.m_magic != BTREEMAGIC - || m.m_version != BTREEVERSION - || m.m_psize < MINPSIZE) { - (void) close(fd); - (void) free((char *) t); -#ifndef EFTYPE -#define EFTYPE -100 -#endif - errno = EFTYPE; - return ((BTREE) NULL); - } - t->bt_psize = m.m_psize; - t->bt_free = m.m_free; - t->bt_flags |= (m.m_flags & BTF_NODUPS) | BTF_METAOK; - (void) close(fd); - } - - /* now open the file the way the user wanted it */ - if ((t->bt_s.bt_d.d_fd = open(f, flags, mode)) < 0) { - (void) free ((char *) t); - return ((BTREE) NULL); - } - - /* access method files are always close-on-exec */ - if (fcntl(t->bt_s.bt_d.d_fd, F_SETFL, 1) == -1) { - (void) close(t->bt_s.bt_d.d_fd); - (void) free ((char *) t); - return ((BTREE) NULL); - } - - /* get number of pages, page size if necessary */ - (void) fstat(t->bt_s.bt_d.d_fd, &buf); - if (t->bt_psize == 0) - t->bt_psize = buf.st_blksize; - t->bt_npages = (pgno_t) (buf.st_size / t->bt_psize); - - /* page zero is metadata, doesn't count */ - if (t->bt_npages > 0) - --(t->bt_npages); - - if (b->cachesize == 0) - b->cachesize = DEFCACHE; - - /* get an lru buffer cache, if the user asked for one */ - if ((b->cachesize / t->bt_psize) > 0) { - BTDISK *d = &(t->bt_s.bt_d); - - d->d_cache = lruinit(d->d_fd, - (int) (b->cachesize / t->bt_psize), - (int) t->bt_psize, - (char *) t->bt_lorder, - _bt_pgin, _bt_pgout); - - if (d->d_cache == (char *) NULL) { - (void) free((char *) t); - return ((BTREE) NULL); - } - } - } - - /* remember if tree was opened for write */ - if (((flags & O_WRONLY) == O_WRONLY) - || ((flags & O_RDWR) == O_RDWR)) - t->bt_flags |= BTF_ISWRITE; - - return ((BTREE) t); -} - -/* - * BT_GET -- Get an entry from a btree. - * - * Does a key lookup in the tree to find the specified key, and returns - * the key/data pair found. - * - * Parameters: - * tree -- btree in which to do lookup - * key -- key to find - * data -- pointer to DBT in which to return data - * flag -- ignored - * - * Returns: - * RET_SUCCESS, RET_ERROR, or RET_SPECIAL if the key is not - * found. If key is not found, nothing is stored in the - * return DBT 'data'. - * - * Side Effects: - * None. - * - * Warnings: - * Return data is statically allocated, and will be overwritten - * at the next call. - */ - -int -bt_get(dbp, key, data, flag) - DB *dbp; - DBT *key, *data; - u_long flag; -{ - BTREE_P t = (BTREE_P) (dbp->internal); - BTHEADER *h; - DATUM *d; - BTITEM *item; - -#ifdef lint - flag = flag; -#endif /* lint */ - - /* lookup */ - item = _bt_search(t, key); - - /* clear parent pointer stack */ - while (_bt_pop(t) != P_NONE) - continue; - - if (item == (BTITEM *) NULL) - return (RET_ERROR); - - h = (BTHEADER *) t->bt_curpage; - data->size = 0; - data->data = (u_char *) NULL; - - /* match? */ - if (VALIDITEM(t, item) - && _bt_cmp(t, key->data, item->bti_index) == 0) { - d = (DATUM *) GETDATUM(h, item->bti_index); - return (_bt_buildret(t, d, data, key)); - } - - /* nope */ - return (RET_SPECIAL); -} - -/* - * BT_PUT -- Add an entry to a btree. - * - * The specified (key, data) pair is added to the tree. If the tree - * was created for unique keys only, then duplicates will not be - * entered. If the requested key exists in the tree, it will be over- - * written unless the flags parameter is R_NOOVERWRITE, in which case - * the update will not be done. If duplicate keys are permitted in the - * tree, duplicates will be inserted and will not overwrite existing - * keys. Nodes are split as required. - * - * Parameters: - * tree -- btree in which to put the new entry - * key -- key component to add - * data -- data corresponding to key - * flag -- R_NOOVERWRITE or zero. - * - * Returns: - * RET_SUCCESS, RET_ERROR, or RET_SPECIAL if the - * NOOVERWRITE flag was set and the specified key exists - * in the database. - * - * Side Effects: - * None. - */ - -int -bt_put(dbp, key, data, flag) - DB *dbp; - DBT *key, *data; - u_long flag; -{ - BTREE_P t; - BTITEM *item; - - t = (BTREE_P)dbp->internal; - - /* look for this key in the tree */ - item = _bt_search(t, key); - - /* - * If this tree was originally created without R_DUP, then duplicate - * keys are not allowed. We need to check this at insertion time. - */ - - if (VALIDITEM(t, item) && _bt_cmp(t, key->data, item->bti_index) == 0) { - if ((t->bt_flags & BTF_NODUPS) && flag == R_NOOVERWRITE) { - if (_bt_delone(t, item->bti_index) == RET_ERROR) { - while (_bt_pop(t) != P_NONE) - continue; - return (RET_ERROR); - } - } - } - - return (_bt_insert(t, item, key, data, flag)); -} - -/* - * BT_DELETE -- delete a key from the tree. - * - * Deletes all keys (and their associated data items) matching the - * supplied key from the tree. If the flags entry is R_CURSOR, then - * the current item in the active scan is deleted. - * - * Parameters: - * tree -- btree from which to delete key - * key -- key to delete - * flags -- R_CURSOR or zero - * - * Returns: - * RET_SUCCESS, RET_ERROR, or RET_SPECIAL if the specified - * key was not in the tree. - * - * Side Effects: - * None. - */ - -int -bt_delete(dbp, key, flags) - DB *dbp; - DBT *key; - u_long flags; -{ - BTREE_P t; - BTHEADER *h; - BTITEM *item; - int ndel = 0; - - t = (BTREE_P)dbp->internal; - - if (flags == R_CURSOR) - return (_bt_crsrdel(t)); - - /* find the first matching key in the tree */ - item = _bt_first(t, key); - h = t->bt_curpage; - - /* don't need the descent stack for deletes */ - while (_bt_pop(t) != P_NONE) - continue; - - /* delete all matching keys */ - for (;;) { - while (VALIDITEM(t, item) - && (_bt_cmp(t, key->data, item->bti_index) == 0)) { - if (_bt_delone(t, item->bti_index) == RET_ERROR) - return (RET_ERROR); - ndel++; - } - - if (VALIDITEM(t, item) || h->h_nextpg == P_NONE) - break; - - /* next page, if necessary */ - do { - if (_bt_getpage(t, h->h_nextpg) == RET_ERROR) - return (RET_ERROR); - h = t->bt_curpage; - } while (NEXTINDEX(h) == 0 && h->h_nextpg != P_NONE); - - item->bti_pgno = h->h_pgno; - item->bti_index = 0; - - if (!VALIDITEM(t, item) - || _bt_cmp(t, key->data, item->bti_index) != 0) - break; - } - - /* flush changes to disk */ - if (ISDISK(t)) { - if (h->h_flags & F_DIRTY) { - if (_bt_write(t, t->bt_curpage, NORELEASE) == RET_ERROR) - return (RET_ERROR); - } - } - - if (ndel == 0) - return (RET_SPECIAL); - - return (RET_SUCCESS); -} - -/* - * BT_SYNC -- sync the btree to disk. - * - * Parameters: - * tree -- btree to sync. - * - * Returns: - * RET_SUCCESS, RET_ERROR. - */ - -bt_sync(dbp) - DB *dbp; -{ - BTREE_P t; - BTHEADER *h; - pgno_t pgno; - - t = (BTREE_P)dbp->internal; - - /* if this is an in-memory btree, syncing is a no-op */ - if (!ISDISK(t)) - return (RET_SUCCESS); - - h = (BTHEADER *) t->bt_curpage; - h->h_flags &= ~F_DIRTY; - - if (ISCACHE(t)) { - pgno = t->bt_curpage->h_pgno; - if (_bt_write(t, h, RELEASE) == RET_ERROR) - return(RET_ERROR); - if (lrusync(t->bt_s.bt_d.d_cache) < RET_ERROR) - return (RET_ERROR); - if (_bt_getpage(t, pgno) == RET_ERROR) - return (RET_ERROR); - } else { - if (_bt_write(t, h, NORELEASE) == RET_ERROR) - return (RET_ERROR); - } - - return (fsync(t->bt_s.bt_d.d_fd)); -} - -/* - * BT_SEQ -- Sequential scan interface. - * - * This routine supports sequential scans on the btree. If called with - * flags set to R_CURSOR, or if no seq scan has been initialized in the - * current tree, we initialize the scan. Otherwise, we advance the scan - * and return the next item. - * - * Scans can be either keyed or non-keyed. Keyed scans basically have - * a starting point somewhere in the middle of the tree. Non-keyed - * scans start at an endpoint. Also, scans can be backward (descending - * order), forward (ascending order), or no movement (keep returning - * the same item). - * - * Flags is checked every time we enter the routine, so the user can - * change directions on an active scan if desired. The key argument - * is examined only when we initialize the scan, in order to position - * it properly. - * - * Items are returned via the key and data arguments passed in. - * - * Parameters: - * tree -- btree in which to do scan - * key -- key, used to position scan on initialization, and - * used to return key components to the user. - * data -- used to return data components to the user. - * flags -- specify R_CURSOR, R_FIRST, R_LAST, R_NEXT, or - * R_PREV. - * - * Returns: - * RET_SUCCESS, RET_ERROR, or RET_SPECIAL if no more data - * exists in the tree in the specified direction. - * - * Side Effects: - * Changes the btree's notion of the current position in the - * scan. - * - * Warnings: - * The key and data items returned are static and will be - * overwritten by the next bt_get or bt_seq. - */ - -int -bt_seq(dbp, key, data, flags) - DB *dbp; - DBT *key, *data; - u_long flags; -{ - BTREE_P t; - BTHEADER *h; - DATUM *d; - int status; - - t = (BTREE_P)dbp->internal; - - /* do we need to initialize the scan? */ - if (flags == R_CURSOR || flags == R_LAST || flags == R_FIRST - || !(t->bt_flags & BTF_SEQINIT)) { - - /* initialize it */ - status = _bt_seqinit(t, key, flags); - } else { - /* just advance the current scan pointer */ - status = _bt_seqadvance(t, flags); - } - - key->size = data->size = 0; - key->data = data->data = (u_char *) NULL; - - h = t->bt_curpage; - - /* is there a valid item at the current scan location? */ - if (status == RET_SPECIAL) { - if (flags == R_NEXT) { - if (t->bt_cursor.c_index >= NEXTINDEX(h)) { - if (NEXTINDEX(h) > 0) - t->bt_cursor.c_index = NEXTINDEX(h) - 1; - else - t->bt_cursor.c_index = 0; - } - } else { - t->bt_cursor.c_index = 0; - } - return (RET_SPECIAL); - } else if (status == RET_ERROR) - return (RET_ERROR); - - /* okay, return the data */ - d = (DATUM *) GETDATUM(h, t->bt_cursor.c_index); - - return (_bt_buildret(t, d, data, key)); -} - -/* - * BT_CLOSE -- Close a btree - * - * Parameters: - * tree -- tree to close - * - * Returns: - * RET_SUCCESS, RET_ERROR. - * - * Side Effects: - * Frees space occupied by the tree. - */ - -int -bt_close(dbp) - DB *dbp; -{ - struct HTBUCKET *b, *sb; - BTREE_P t; - BTHEADER *h; - HTABLE ht; - int fd, i; - char *cache; - - t = (BTREE_P)dbp->internal; - - if (t->bt_cursor.c_key != (char *) NULL) - (void) free(t->bt_cursor.c_key); - - if (!ISDISK(t)) { - /* in-memory tree, release hash table memory */ - ht = t->bt_s.bt_ht; - for (i = 0; i < HTSIZE; i++) { - if ((b = ht[i]) == (struct HTBUCKET *) NULL) - break; - do { - sb = b; - (void) free((char *) b->ht_page); - b = b->ht_next; - (void) free((char *) sb); - } while (b != (struct HTBUCKET *) NULL); - } - (void) free ((char *) ht); - (void) free ((char *) t); - return (RET_SUCCESS); - } - - if ((t->bt_flags & BTF_ISWRITE) && !(t->bt_flags & BTF_METAOK)) { - if (_bt_wrtmeta(t) == RET_ERROR) - return (RET_ERROR); - } - - if (t->bt_curpage != (BTHEADER *) NULL) { - h = t->bt_curpage; - if (h->h_flags & F_DIRTY) { - if (_bt_write(t, h, RELEASE) == RET_ERROR) - return (RET_ERROR); - } else { - if (_bt_release(t, h) == RET_ERROR) - return (RET_ERROR); - } - - /* flush and free the cache, if there is one */ - if (ISCACHE(t)) { - cache = t->bt_s.bt_d.d_cache; - if (lrusync(cache) == RET_ERROR) - return (RET_ERROR); - lrufree(cache); - } - (void) free ((char *) h); - } - - fd = t->bt_s.bt_d.d_fd; - (void) free ((char *) t); - return (close(fd)); -} diff --git a/lib/libc/db/btree/btree.h b/lib/libc/db/btree/btree.h index 2c78618474..1e393236b0 100644 --- a/lib/libc/db/btree/btree.h +++ b/lib/libc/db/btree/btree.h @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. @@ -32,295 +32,314 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * @(#)btree.h 8.1 (Berkeley) 6/4/93 */ -/* - * @(#)btree.h 5.2 (Berkeley) 2/22/91 - */ +#include -typedef char *BTREE; /* should really be (void *) */ - -/* #define DEBUG */ - -#define RET_ERROR -1 -#define RET_SUCCESS 0 -#define RET_SPECIAL 1 - -#ifndef TRUE -#define TRUE 1 -#define FALSE 0 -#endif /* ndef TRUE */ - -#ifndef NULL -#define NULL 0 -#endif /* ndef NULL */ - -/* these are defined in lrucache.c */ -extern char *lruinit(); -extern char *lruget(); -extern char *lrugetnew(); -extern int lrusync(); -extern int lruwrite(); -extern int lrurelease(); -extern void lrufree(); - -/* these are defined here */ -extern BTREE bt_open(); -extern int bt_close(); -extern int bt_delete(); -extern int bt_get(); -extern int bt_put(); -extern int bt_seq(); -extern int bt_sync(); +#define DEFMINKEYPAGE (2) /* Minimum keys per page */ +#define MINCACHE (5) /* Minimum cached pages */ +#define MINPSIZE (512) /* Minimum page size */ /* - * Private types. What you choose for these depends on how big you - * want to let files get, and how big you want to let pages get. + * Page 0 of a btree file contains a copy of the meta-data. This page is also + * used as an out-of-band page, i.e. page pointers that point to nowhere point + * to page 0. Page 1 is the root of the btree. */ - -typedef u_long index_t; /* so # bytes on a page fits in a long */ -typedef u_long pgno_t; /* so # of pages in a btree fits in a long */ +#define P_INVALID 0 /* Invalid tree page number. */ +#define P_META 0 /* Tree metadata page number. */ +#define P_ROOT 1 /* Tree root page number. */ /* - * When we do searches, we push the parent page numbers onto a stack - * as we descend the tree. This is so that for insertions, we can - * find our way back up to do internal page insertions and splits. + * There are five page layouts in the btree: btree internal pages (BINTERNAL), + * btree leaf pages (BLEAF), recno internal pages (RINTERNAL), recno leaf pages + * (RLEAF) and overflow pages. All five page types have a page header (PAGE). + * This implementation requires that longs within structures are NOT padded. + * (ANSI C permits random padding.) If your compiler pads randomly you'll have + * to do some work to get this package to run. */ - -typedef struct BTSTACK { - pgno_t bts_pgno; - struct BTSTACK *bts_next; -} BTSTACK; +typedef struct PAGE { + pgno_t pgno; /* this page's page number */ + pgno_t prevpg; /* left sibling */ + pgno_t nextpg; /* right sibling */ + +#define P_BINTERNAL 0x01 /* btree internal page */ +#define P_BLEAF 0x02 /* leaf page */ +#define P_OVERFLOW 0x04 /* overflow page */ +#define P_RINTERNAL 0x08 /* recno internal page */ +#define P_RLEAF 0x10 /* leaf page */ +#define P_TYPE 0x1f /* type mask */ + +#define P_PRESERVE 0x20 /* never delete this chain of pages */ + u_long flags; + + indx_t lower; /* lower bound of free space on page */ + indx_t upper; /* upper bound of free space on page */ + indx_t linp[1]; /* long-aligned VARIABLE LENGTH DATA */ +} PAGE; + +/* First and next index. */ +#define BTDATAOFF (sizeof(pgno_t) + sizeof(pgno_t) + sizeof(pgno_t) + \ + sizeof(u_long) + sizeof(indx_t) + sizeof(indx_t)) +#define NEXTINDEX(p) (((p)->lower - BTDATAOFF) / sizeof(indx_t)) /* - * Every btree page has a header that looks like this. Flags are given - * in the #define's for the F_ flags (see below). - */ - -typedef struct BTHEADER { - pgno_t h_pgno; /* page number of this page */ - pgno_t h_prevpg; /* left sibling */ - pgno_t h_nextpg; /* right sibling */ - -#define F_LEAF 0x01 /* leaf page, contains user data */ -#define F_CONT 0x02 /* continuation page (large items) */ -#define F_DIRTY 0x04 /* need to write to disk */ -#define F_PRESERVE 0x08 /* never delete this chain of pages */ - - u_long h_flags; /* page state */ - index_t h_lower; /* lower bound of free space on page */ - index_t h_upper; /* upper bound of free space on page */ - index_t h_linp[1]; /* VARIABLE LENGTH DATA AT END OF STRUCT */ -} BTHEADER; - -/* - * HTBUCKETs are hash table buckets for looking up pages of in-memory - * btrees by page number. We use this indirection, rather than direct - * pointers, so that the code for manipulating in-memory trees is the - * same as that for manipulating on-disk trees. + * For pages other than overflow pages, there is an array of offsets into the + * rest of the page immediately following the page header. Each offset is to + * an item which is unique to the type of page. The h_lower offset is just + * past the last filled-in index. The h_upper offset is the first item on the + * page. Offsets are from the beginning of the page. + * + * If an item is too big to store on a single page, a flag is set and the item + * is a { page, size } pair such that the page is the first page of an overflow + * chain with size bytes of item. Overflow pages are simply bytes without any + * external structure. + * + * The size and page number fields in the items are long aligned so they can be + * manipulated without copying. */ - -typedef struct HTBUCKET { - pgno_t ht_pgno; - BTHEADER *ht_page; - struct HTBUCKET *ht_next; -} HTBUCKET; - -typedef HTBUCKET **HTABLE; - -/* minimum size we'll let a page be */ -#define MINPSIZE 512 - -/* default cache size, in bytes */ -#define DEFCACHE (20 * 1024) - -/* hash table size for in-memory trees */ -#define HTSIZE 128 - -/* generate a hash key from a page number */ -#define HASHKEY(pgno) ((pgno - 1) % HTSIZE) +#define LALIGN(n) (((n) + sizeof(u_long) - 1) & ~(sizeof(u_long) - 1)) +#define NOVFLSIZE (sizeof(pgno_t) + sizeof(size_t)) /* - * Disk btrees have a file descriptor, and may also have an lru buffer - * cache, if the user asked for one. + * For the btree internal pages, the item is a key. BINTERNALs are {key, pgno} + * pairs, such that the key compares less than or equal to all of the records + * on that page. For a tree without duplicate keys, an internal page with two + * consecutive keys, a and b, will have all records greater than or equal to a + * and less than b stored on the page associated with a. Duplicate keys are + * somewhat special and can cause duplicate internal and leaf page records and + * some minor modifications of the above rule. */ - -typedef struct BTDISK { - int d_fd; - char *d_cache; -} BTDISK; +typedef struct BINTERNAL { + size_t ksize; /* key size */ + pgno_t pgno; /* page number stored on */ +#define P_BIGDATA 0x01 /* overflow data */ +#define P_BIGKEY 0x02 /* overflow key */ + u_char flags; + char bytes[1]; /* data */ +} BINTERNAL; + +/* Get the page's BINTERNAL structure at index indx. */ +#define GETBINTERNAL(pg, indx) \ + ((BINTERNAL *)((char *)(pg) + (pg)->linp[indx])) + +/* Get the number of bytes in the entry. */ +#define NBINTERNAL(len) \ + LALIGN(sizeof(size_t) + sizeof(pgno_t) + sizeof(u_char) + (len)) + +/* Copy a BINTERNAL entry to the page. */ +#define WR_BINTERNAL(p, size, pgno, flags) { \ + *(size_t *)p = size; \ + p += sizeof(size_t); \ + *(pgno_t *)p = pgno; \ + p += sizeof(pgno_t); \ + *(u_char *)p = flags; \ + p += sizeof(u_char); \ +} /* - * Cursors keep track of the current location in a sequential scan of - * the database. Since btrees impose a total ordering on keys, we can - * walk forward or backward through the database from any point. Cursors - * survive updates to the tree, and can be used to delete a particular - * record. + * For the recno internal pages, the item is a page number with the number of + * keys found on that page and below. */ - -typedef struct CURSOR { - pgno_t c_pgno; /* pgno of current item in scan */ - index_t c_index; /* index of current item in scan */ - char *c_key; /* current key, used for updates */ - -#define CRSR_BEFORE 0x01 - - u_char c_flags; /* to handle updates properly */ -} CURSOR; +typedef struct RINTERNAL { + recno_t nrecs; /* number of records */ + pgno_t pgno; /* page number stored below */ +} RINTERNAL; + +/* Get the page's RINTERNAL structure at index indx. */ +#define GETRINTERNAL(pg, indx) \ + ((RINTERNAL *)((char *)(pg) + (pg)->linp[indx])) + +/* Get the number of bytes in the entry. */ +#define NRINTERNAL \ + LALIGN(sizeof(recno_t) + sizeof(pgno_t)) + +/* Copy a RINTERAL entry to the page. */ +#define WR_RINTERNAL(p, nrecs, pgno) { \ + *(recno_t *)p = nrecs; \ + p += sizeof(recno_t); \ + *(pgno_t *)p = pgno; \ +} + +/* For the btree leaf pages, the item is a key and data pair. */ +typedef struct BLEAF { + size_t ksize; /* size of key */ + size_t dsize; /* size of data */ + u_char flags; /* P_BIGDATA, P_BIGKEY */ + char bytes[1]; /* data */ +} BLEAF; + +/* Get the page's BLEAF structure at index indx. */ +#define GETBLEAF(pg, indx) \ + ((BLEAF *)((char *)(pg) + (pg)->linp[indx])) + +/* Get the number of bytes in the entry. */ +#define NBLEAF(p) NBLEAFDBT((p)->ksize, (p)->dsize) + +/* Get the number of bytes in the user's key/data pair. */ +#define NBLEAFDBT(ksize, dsize) \ + LALIGN(sizeof(size_t) + sizeof(size_t) + sizeof(u_char) + \ + (ksize) + (dsize)) + +/* Copy a BLEAF entry to the page. */ +#define WR_BLEAF(p, key, data, flags) { \ + *(size_t *)p = key->size; \ + p += sizeof(size_t); \ + *(size_t *)p = data->size; \ + p += sizeof(size_t); \ + *(u_char *)p = flags; \ + p += sizeof(u_char); \ + memmove(p, key->data, key->size); \ + p += key->size; \ + memmove(p, data->data, data->size); \ +} + +/* For the recno leaf pages, the item is a data entry. */ +typedef struct RLEAF { + size_t dsize; /* size of data */ + u_char flags; /* P_BIGDATA */ + char bytes[1]; +} RLEAF; + +/* Get the page's RLEAF structure at index indx. */ +#define GETRLEAF(pg, indx) \ + ((RLEAF *)((char *)(pg) + (pg)->linp[indx])) + +/* Get the number of bytes in the entry. */ +#define NRLEAF(p) NRLEAFDBT((p)->dsize) + +/* Get the number of bytes from the user's data. */ +#define NRLEAFDBT(dsize) \ + LALIGN(sizeof(size_t) + sizeof(u_char) + (dsize)) + +/* Copy a RLEAF entry to the page. */ +#define WR_RLEAF(p, data, flags) { \ + *(size_t *)p = data->size; \ + p += sizeof(size_t); \ + *(u_char *)p = flags; \ + p += sizeof(u_char); \ + memmove(p, data->data, data->size); \ +} /* - * The private btree data structure. The user passes a pointer to one of - * these when we are to manipulate a tree, but the BTREE type is opaque - * to him. + * A record in the tree is either a pointer to a page and an index in the page + * or a page number and an index. These structures are used as a cursor, stack + * entry and search returns as well as to pass records to other routines. + * + * One comment about searches. Internal page searches must find the largest + * record less than key in the tree so that descents work. Leaf page searches + * must find the smallest record greater than key so that the returned index + * is the record's correct position for insertion. + * + * One comment about cursors. The cursor key is never removed from the tree, + * even if deleted. This is because it is quite difficult to decide where the + * cursor should be when other keys have been inserted/deleted in the tree; + * duplicate keys make it impossible. This scheme does require extra work + * though, to make sure that we don't perform an operation on a deleted key. */ +typedef struct EPGNO { + pgno_t pgno; /* the page number */ + indx_t index; /* the index on the page */ +} EPGNO; -typedef struct BTREEDATA_P { - char *bt_fname; /* NULL for in-memory trees */ - union { - BTDISK bt_d; /* for on-disk btrees */ - HTABLE bt_ht; /* hash table for mem trees */ - } bt_s; - size_t bt_psize; /* page size for btree pages */ - int (*bt_compare)(); /* key comparison function */ - pgno_t bt_npages; /* number of pages in tree */ - BTHEADER *bt_curpage; /* current page contents */ - pgno_t bt_free; /* free pg list for big data */ - CURSOR bt_cursor; /* cursor for scans */ - BTSTACK *bt_stack; /* parent stack for inserts */ - u_long bt_lorder; /* byte order (endian.h) */ - -#define BTF_METAOK 0x01 /* meta-data written to start of file */ -#define BTF_SEQINIT 0x02 /* we have called bt_seq */ -#define BTF_ISWRITE 0x04 /* tree was opened for write */ -#define BTF_NODUPS 0x08 /* tree created for unique keys */ - - u_long bt_flags; /* btree state */ -} BTREEDATA_P; - -typedef BTREEDATA_P *BTREE_P; +typedef struct EPG { + PAGE *page; /* the (pinned) page */ + indx_t index; /* the index on the page */ +} EPG; /* - * The first thing in a btree file is a BTMETA structure. The rest of - * the first page is empty, so that all disk operations are page-aligned. + * The metadata of the tree. The m_nrecs field is used only by the RECNO code. + * This is because the btree doesn't really need it and it requires that every + * put or delete call modify the metadata. */ - typedef struct BTMETA { - u_long m_magic; - u_long m_version; - size_t m_psize; - pgno_t m_free; - u_long m_flags; - u_long m_lorder; + u_long m_magic; /* magic number */ + u_long m_version; /* version */ + u_long m_psize; /* page size */ + u_long m_free; /* page number of first free page */ + u_long m_nrecs; /* R: number of records */ +#define SAVEMETA (B_NODUPS | R_RECNO) + u_long m_flags; /* bt_flags & SAVEMETA */ + u_long m_unused; /* unused */ } BTMETA; -#define P_NONE 0 /* invalid page number in tree */ -#define P_ROOT 1 /* page number of root pg in btree */ +/* The in-memory btree/recno data structure. */ +typedef struct BTREE { + MPOOL *bt_mp; /* memory pool cookie */ -#define NORELEASE 0 /* don't release a page during write */ -#define RELEASE 1 /* release a page during write */ + DB *bt_dbp; /* pointer to enclosing DB */ -#define INSERT 0 /* doing an insert operation */ -#define DELETE 1 /* doing a delete operation */ + EPGNO bt_bcursor; /* B: btree cursor */ + recno_t bt_rcursor; /* R: recno cursor (1-based) */ -/* get the next free index on a btree page */ -#define NEXTINDEX(p) ((((int)(p)->h_lower) - ((int)((((char *)(&(p)->h_linp[0]))) - ((char *) (p)))))/(sizeof(index_t))) +#define BT_POP(t) (t->bt_sp ? t->bt_stack + --t->bt_sp : NULL) +#define BT_CLR(t) (t->bt_sp = 0) + EPGNO *bt_stack; /* stack of parent pages */ + u_int bt_sp; /* current stack pointer */ + u_int bt_maxstack; /* largest stack */ -/* is a BTITEM actually on the btree page? */ -#define VALIDITEM(t, i) ((i)->bti_index < NEXTINDEX((t)->bt_curpage)) + char *bt_kbuf; /* key buffer */ + size_t bt_kbufsz; /* key buffer size */ + char *bt_dbuf; /* data buffer */ + size_t bt_dbufsz; /* data buffer size */ -/* guarantee longword alignment so structure refs work */ -#define LONGALIGN(p) (((long)(p) + 3) & ~ 0x03) + int bt_fd; /* tree file descriptor */ -/* get a particular datum (or idatum) off a page */ -#define GETDATUM(h,i) (((char *) h) + h->h_linp[i]) + pgno_t bt_free; /* next free page */ + u_long bt_psize; /* page size */ + indx_t bt_ovflsize; /* cut-off for key/data overflow */ + int bt_lorder; /* byte order */ + /* sorted order */ + enum { NOT, BACK, FORWARD, } bt_order; + EPGNO bt_last; /* last insert */ -/* is a {key,datum} too big to put on a single page? */ -#define TOOBIG(t, sz) (sz >= t->bt_psize / 5) + /* B: key comparison function */ + int (*bt_cmp) __P((const DBT *, const DBT *)); + /* B: prefix comparison function */ + int (*bt_pfx) __P((const DBT *, const DBT *)); + /* R: recno input function */ + int (*bt_irec) __P((struct BTREE *, recno_t)); -/* is this a disk tree or a memory tree? */ -#define ISDISK(t) (t->bt_fname != (char *) NULL) + FILE *bt_rfp; /* R: record FILE pointer */ + int bt_rfd; /* R: record file descriptor */ -/* does the disk tree use a cache? */ -#define ISCACHE(t) (t->bt_s.bt_d.d_cache != (char *) NULL) + caddr_t bt_cmap; /* R: current point in mapped space */ + caddr_t bt_smap; /* R: start of mapped space */ + caddr_t bt_emap; /* R: end of mapped space */ + size_t bt_msize; /* R: size of mapped region. */ -/* - * DATUMs are for user data -- one appears on leaf pages for every - * tree entry. The d_bytes[] array contains the key first, then the data. - * - * If either the key or the datum is too big to store on a single page, - * a bit is set in the flags entry, and the d_bytes[] array contains a - * pgno pointing to the page at which the data is actually stored. - * - * Note on alignment: every DATUM is guaranteed to be longword aligned - * on the disk page. In order to force longword alignment of user key - * and data values, we must guarantee that the d_bytes[] array starts - * on a longword boundary. This is the reason that d_flags is a u_long, - * rather than a u_char (it really only needs to be two bits big). This - * is necessary because we call the user's comparison function with a - * pointer to the start of the d_bytes array. We don't need to force - * longword alignment of the data following the key, since that is copied - * to a longword-aligned buffer before being returned to the user. - */ - -typedef struct DATUM { - size_t d_ksize; /* size of key */ - size_t d_dsize; /* size of data */ - -#define D_BIGDATA 0x01 /* indirect datum ptr flag */ -#define D_BIGKEY 0x02 /* indirect key ptr flag */ - - u_long d_flags; /* flags (indirect bit) */ - char d_bytes[1]; /* VARIABLE LENGTH DATA AT END OF STRUCT */ -} DATUM; - -/* BTITEMs are used to return (page, index, datum) tuples from searches */ -typedef struct BTITEM { - pgno_t bti_pgno; - index_t bti_index; - DATUM *bti_datum; -} BTITEM; + recno_t bt_nrecs; /* R: number of records */ + size_t bt_reclen; /* R: fixed record length */ + u_char bt_bval; /* R: delimiting byte/pad character */ /* - * IDATUMs are for data stored on internal pages. This is the (key, pgno) - * pair, such that key 'key' is the first entry on page 'pgno'. If our - * internal page contains keys (a) and (b) next to each other, then all - * items >= to (a) and < (b) go on the same page as (a). There are some - * gotchas with duplicate keys, however. See the split code for details. - * - * If a key is too big to fit on a single page, then the i_bytes[] array - * contains a pgno pointing to the start of a chain that actually stores - * the bytes. Since items on internal pages are never deleted from the - * tree, these indirect chains are marked as special, so that they won't - * be deleted if the corresponding leaf item is deleted. - * - * As for DATUMs, IDATUMs have a u_long flag entry (rather than u_char) - * in order to guarantee that user keys are longword aligned on the disk - * page. + * NB: + * B_NODUPS and R_RECNO are stored on disk, and may not be changed. */ - -typedef struct IDATUM { - size_t i_size; - pgno_t i_pgno; - u_long i_flags; /* see DATUM.d_flags, above */ - char i_bytes[1]; /* VARIABLE LENGTH DATA AT END OF STRUCT */ -} IDATUM; - -/* all private interfaces have a leading _ in their names */ -extern BTITEM *_bt_search(); -extern BTITEM *_bt_searchr(); -extern BTHEADER *_bt_allocpg(); -extern index_t _bt_binsrch(); -extern int _bt_isonpage(); -extern BTITEM *_bt_first(); -extern int _bt_release(); -extern int _bt_wrtmeta(); -extern int _bt_delindir(); -extern int _bt_pgout(); -extern int _bt_pgin(); -extern int _bt_fixscan(); -extern int _bt_indirect(); -extern int _bt_crsrdel(); -extern int _bt_push(); -extern pgno_t _bt_pop(); -extern int strcmp(); - +#define B_DELCRSR 0x00001 /* cursor has been deleted */ +#define B_INMEM 0x00002 /* in-memory tree */ +#define B_METADIRTY 0x00004 /* need to write metadata */ +#define B_MODIFIED 0x00008 /* tree modified */ +#define B_NEEDSWAP 0x00010 /* if byte order requires swapping */ +#define B_NODUPS 0x00020 /* no duplicate keys permitted */ +#define B_RDONLY 0x00040 /* read-only tree */ +#define B_SEQINIT 0x00100 /* sequential scan initialized */ + +#define R_CLOSEFP 0x00200 /* opened a file pointer */ +#define R_EOF 0x00400 /* end of input file reached. */ +#define R_FIXLEN 0x00800 /* fixed length records */ +#define R_MEMMAPPED 0x01000 /* memory mapped file. */ +#define R_RECNO 0x00080 /* record oriented tree */ +#define R_INMEM 0x02000 /* in-memory file */ +#define R_MODIFIED 0x04000 /* modified file */ +#define R_RDONLY 0x08000 /* read-only file */ + + u_long bt_flags; /* btree state */ +} BTREE; + +#define SET(t, f) ((t)->bt_flags |= (f)) +#define CLR(t, f) ((t)->bt_flags &= ~(f)) +#define ISSET(t, f) ((t)->bt_flags & (f)) + +#include "extern.h" diff --git a/lib/libc/db/btree/delete.c b/lib/libc/db/btree/delete.c deleted file mode 100644 index 9ed4287182..0000000000 --- a/lib/libc/db/btree/delete.c +++ /dev/null @@ -1,201 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Mike Olson. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)delete.c 5.2 (Berkeley) 2/22/91"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include -#include -#include -#include "btree.h" - -/* - * _BT_CRSRDEL -- Delete the item pointed to by the cursor. - * - * This routine deletes the item most recently returned by a scan - * through the tree. Since it only makes sense to delete the current - * record once, we make sure that we don't try to delete twice without - * advancing the scan. - * - * Parameters: - * t -- tree in which to do deletion - * - * Returns: - * RET_SUCCESS, RET_ERROR. - * - * Side Effects: - * The call to _bt_delone marks the cursor, so we can tell that - * the current record has been deleted. - */ - -int -_bt_crsrdel(t) - BTREE_P t; -{ - CURSOR *c; - - c = &(t->bt_cursor); - - /* a cursor must exist, and can't have deleted the current key yet */ - if (!(t->bt_flags & BTF_SEQINIT) || (c->c_flags & CRSR_BEFORE)) { - errno = EINVAL; - return (RET_ERROR); - } - - if (_bt_getpage(t, c->c_pgno) == RET_ERROR) - return (RET_ERROR); - - if (c->c_index >= NEXTINDEX(t->bt_curpage)) { - errno = EINVAL; - return (RET_ERROR); - } - - return (_bt_delone(t, c->c_index)); -} - -/* - * _BT_DELONE -- Delete a single entry from a btree. - * - * This routine physically removes a btree entry from a leaf page. - * IDATUM items are *never* removed from internal nodes, regardless - * of whether the entries that originally caused them to be added - * are removed from the tree or not. In addition, pages made empty - * by element deletion are not actually reclaimed. They are, - * however, made available for reuse. - * - * To delete an item from a page, we pack the remaining items at - * the end of the page, overwriting the deleted item's entry. We - * move the line pointers backward on the page, overwriting the - * original item's line pointer. This guarantees that the space in - * the middle of the page is free -- a property that our insertion - * strategy relies on. - * - * This routine doesn't reclaim pages all of whose entries have - * been deleted. These pages are available for reuse, however. - * If an item is deleted that was too big to fit on a page, then - * the blocks that it occupies are put on a free list for reuse. - * - * Parameters: - * t -- btree from which to delete item - * index -- index of entry on current page to delete - * - * Returns: - * RET_SUCCESS, RET_ERROR. - * - * Side Effects: - * Physically changes page layout, adjusts internal page - * state to reflect the deletion of the item, and updates - * the list of free pages for this tree. - */ - -int -_bt_delone(t, index) - BTREE_P t; - index_t index; -{ - char *src, *dest; - int nbytes, nmoved; - index_t off; - index_t top; - index_t i; - pgno_t chain; - BTHEADER *h; - CURSOR *c; - DATUM *d; - - /* deletion may confuse an active scan. fix it. */ - c = &(t->bt_cursor); - if (t->bt_flags & BTF_SEQINIT && t->bt_curpage->h_pgno == c->c_pgno) - if (_bt_fixscan(t, index, (DATUM *) NULL, DELETE) == RET_ERROR) - return (RET_ERROR); - - h = t->bt_curpage; - off = h->h_linp[index]; - d = (DATUM *) GETDATUM(h, index); - - /* if this is a big item, reclaim the space it occupies */ - if (d->d_flags & D_BIGKEY) { - bcopy(&(d->d_bytes[0]), - (char *) &chain, - sizeof(chain)); - if (_bt_delindir(t, chain) == RET_ERROR) - return (RET_ERROR); - h = t->bt_curpage; - d = (DATUM *) GETDATUM(h, index); - } - if (d->d_flags & D_BIGDATA) { - bcopy(&(d->d_bytes[d->d_ksize]), - (char *) &chain, - sizeof(chain)); - if (_bt_delindir(t, chain) == RET_ERROR) - return (RET_ERROR); - h = t->bt_curpage; - d = (DATUM *) GETDATUM(h, index); - } - - /* move the data down on the page */ - nbytes = d->d_ksize + d->d_dsize - + (sizeof(DATUM) - sizeof(char)); - nbytes = LONGALIGN(nbytes); - src = ((char *) h) + h->h_upper; - dest = src + nbytes; - nmoved = (int) (((char *) d) - src); - (void) bcopy(src, dest, nmoved); - - /* next move the line pointers up */ - src = (char *) &(h->h_linp[index + 1]); - dest = (char *) &(h->h_linp[index]); - nmoved = (int) (((char *) &(h->h_linp[NEXTINDEX(h)])) - src); - (void) bcopy(src, dest, nmoved); - - /* remember that we freed up some space */ - h->h_upper += nbytes; - h->h_lower -= sizeof(index_t); - - /* adjust offsets in line pointers affected by moving the data */ - top = NEXTINDEX(h); - for (i = 0; i < top; i++) { - if (h->h_linp[i] < off) - h->h_linp[i] += nbytes; - } - - /* it's gone */ - h->h_flags |= F_DIRTY; - - return (RET_SUCCESS); -} diff --git a/lib/libc/db/btree/extern.h b/lib/libc/db/btree/extern.h new file mode 100644 index 0000000000..99b6f31b73 --- /dev/null +++ b/lib/libc/db/btree/extern.h @@ -0,0 +1,71 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/4/93 + */ + +int __bt_close __P((DB *)); +int __bt_cmp __P((BTREE *, const DBT *, EPG *)); +int __bt_crsrdel __P((BTREE *, EPGNO *)); +int __bt_defcmp __P((const DBT *, const DBT *)); +int __bt_defpfx __P((const DBT *, const DBT *)); +int __bt_delete __P((const DB *, const DBT *, u_int)); +int __bt_dleaf __P((BTREE *, PAGE *, int)); +int __bt_fd __P((const DB *)); +EPG *__bt_first __P((BTREE *, const DBT *, int *)); +int __bt_free __P((BTREE *, PAGE *)); +int __bt_get __P((const DB *, const DBT *, DBT *, u_int)); +PAGE *__bt_new __P((BTREE *, pgno_t *)); +DB *__bt_open __P((const char *, int, int, const BTREEINFO *)); +void __bt_pgin __P((void *, pgno_t, void *)); +void __bt_pgout __P((void *, pgno_t, void *)); +int __bt_push __P((BTREE *, pgno_t, int)); +int __bt_put __P((const DB *dbp, DBT *, const DBT *, u_int)); +int __bt_ret __P((BTREE *, EPG *, DBT *, DBT *)); +EPG *__bt_search __P((BTREE *, const DBT *, int *)); +int __bt_seq __P((const DB *, DBT *, DBT *, u_int)); +int __bt_split __P((BTREE *, PAGE *, + const DBT *, const DBT *, u_long, size_t, u_int)); +int __bt_sync __P((const DB *, u_int)); + +int __ovfl_delete __P((BTREE *, void *)); +int __ovfl_get __P((BTREE *, void *, size_t *, char **, size_t *)); +int __ovfl_put __P((BTREE *, const DBT *, pgno_t *)); + +#ifdef DEBUG +void __bt_dnpage __P((DB *, pgno_t)); +void __bt_dpage __P((PAGE *)); +void __bt_dump __P((DB *)); +#endif +#ifdef STATISTICS +void __bt_stat __P((DB *)); +#endif diff --git a/lib/libc/db/btree/insert.c b/lib/libc/db/btree/insert.c deleted file mode 100644 index d6c1b11757..0000000000 --- a/lib/libc/db/btree/insert.c +++ /dev/null @@ -1,312 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Mike Olson. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)insert.c 5.3 (Berkeley) 2/22/91"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include -#include -#include -#include "btree.h" - -/* - * _BT_INSERT -- Insert a new user datum in the btree. - * - * This routine is called by bt_put, the public interface, once the - * location for the new item is known. We do the work here, and - * handle splits if necessary. - * - * Parameters: - * t -- btree in which to do the insertion. - * item -- BTITEM describing location of new datum - * key -- key to insert - * data -- data associated with key - * flag -- magic cookie passed recursively to bt_put if we - * have to do a split - * - * Returns: - * RET_SUCCESS, RET_ERROR. - */ - -int -_bt_insert(t, item, key, data, flag) - BTREE_P t; - BTITEM *item; - DBT *key; - DBT *data; - int flag; -{ - index_t index; - BTHEADER *h; - DATUM *d; - int nbytes; - int status; - pgno_t pgno; - int keysize, datasize; - int bigkey, bigdata; - - if (_bt_getpage(t, item->bti_pgno) == RET_ERROR) - return (RET_ERROR); - h = t->bt_curpage; - - if (TOOBIG(t, data->size)) { - bigdata = TRUE; - datasize = sizeof(pgno_t); - } else { - bigdata = FALSE; - datasize = data->size; - } - - if (TOOBIG(t, key->size)) { - bigkey = TRUE; - keysize = sizeof(pgno_t); - } else { - bigkey = FALSE; - keysize = key->size; - } - - nbytes = keysize + datasize + (sizeof(DATUM) - sizeof(char)); - nbytes = LONGALIGN(nbytes) + sizeof(index_t); - - /* if there's not enough room here, split the page */ - if ((h->h_upper - h->h_lower) < nbytes) { - if (_bt_split(t) == RET_ERROR) - return (RET_ERROR); - - /* okay, try again (empty the stack first, though) */ - while (_bt_pop((BTREE) t) != P_NONE) - continue; - - return (bt_put((BTREE) t, key, data, flag)); - } - - /* put together a leaf page datum from the key/data pair */ - index = item->bti_index; - nbytes = keysize + datasize + (sizeof(DATUM) - sizeof(char)); - - if ((d = (DATUM *) malloc((unsigned) nbytes)) == (DATUM *) NULL) - return (RET_ERROR); - - d->d_ksize = keysize; - d->d_dsize = datasize; - d->d_flags = 0; - - if (bigkey) { - if (_bt_indirect(t, key, &pgno) == RET_ERROR) - return (RET_ERROR); - (void) bcopy((char *) &pgno, &(d->d_bytes[0]), sizeof(pgno)); - d->d_flags |= D_BIGKEY; - if (_bt_getpage(t, item->bti_pgno) == RET_ERROR) - return (RET_ERROR); - } else { - if (d->d_ksize > 0) { - (void) bcopy((char *) key->data, - (char *) &(d->d_bytes[0]), - (int) d->d_ksize); - } - } - - if (bigdata) { - if (_bt_indirect(t, data, &pgno) == RET_ERROR) - return (RET_ERROR); - (void) bcopy((char *) &pgno, - &(d->d_bytes[keysize]), - sizeof(pgno)); - d->d_flags |= D_BIGDATA; - if (_bt_getpage(t, item->bti_pgno) == RET_ERROR) - return (RET_ERROR); - } else { - if (d->d_dsize > 0) { - (void) bcopy((char *) data->data, - (char *) &(d->d_bytes[keysize]), - (int) d->d_dsize); - } - } - - /* do the insertion */ - status = _bt_insertat(t, (char *) d, index); - - (void) free((char *) d); - - return (status); -} - -/* - * _BT_INSERTI -- Insert IDATUM on current page in the btree. - * - * This routine handles insertions to internal pages after splits - * lower in the tree. On entry, t->bt_curpage is the page to get - * the new IDATUM. We are also given pgno, the page number of the - * IDATUM that is immediately left of the new IDATUM's position. - * This guarantees that the IDATUM for the right half of the page - * after a split goes next to the IDATUM for its left half. - * - * Parameters: - * t -- tree in which to do insertion. - * id -- new IDATUM to insert - * pgno -- page number of IDATUM left of id's position - * - * Returns: - * RET_SUCCESS, RET_ERROR. - */ - -int -_bt_inserti(t, id, pgno) - BTREE_P t; - IDATUM *id; - pgno_t pgno; -{ - BTHEADER *h = t->bt_curpage; - index_t next, i; - IDATUM *idx; - char *key; - pgno_t chain; - int free_key; - int ignore; - - if (id->i_flags & D_BIGKEY) { - free_key = TRUE; - bcopy(&(id->i_bytes[0]), (char *) &chain, sizeof(chain)); - if (_bt_getbig(t, chain, &key, &ignore) == RET_ERROR) - return (RET_ERROR); - } else { - free_key = FALSE; - key = &(id->i_bytes[0]); - } - i = _bt_binsrch(t, key); - - next = NEXTINDEX(h); - while (i < next && _bt_cmp(t, key, i) >= 0) - i++; - - if (free_key) - (void) free(key); - - /* okay, now we're close; find adjacent IDATUM */ - for (;;) { - idx = (IDATUM *) GETDATUM(h,i); - if (idx->i_pgno == pgno) { - i++; - break; - } - --i; - } - - /* correctly positioned, do the insertion */ - return (_bt_insertat(t, (char *) id, i)); -} - -/* - * _BT_INSERTAT -- Insert a datum at a given location on the current page. - * - * This routine does insertions on both leaf and internal pages. - * - * Parameters: - * t -- tree in which to do insertion. - * p -- DATUM or IDATUM to insert. - * index -- index in line pointer array to put this item. - * - * Returns: - * RET_SUCCESS, RET_ERROR. - * - * Side Effects: - * Will rearrange line pointers to make space for the new - * entry. This means that any scans currently active are - * invalid after this. - * - * Warnings: - * There must be sufficient room for the new item on the page. - */ - -int -_bt_insertat(t, p, index) - BTREE_P t; - char *p; - index_t index; -{ - IDATUM *id = (IDATUM *) p; - DATUM *d = (DATUM *) p; - BTHEADER *h; - CURSOR *c; - index_t nxtindex; - char *src, *dest; - int nbytes; - - /* insertion may confuse an active scan. fix it. */ - c = &(t->bt_cursor); - if (t->bt_flags & BTF_SEQINIT && t->bt_curpage->h_pgno == c->c_pgno) - if (_bt_fixscan(t, index, d, INSERT) == RET_ERROR) - return (RET_ERROR); - - h = t->bt_curpage; - nxtindex = (index_t) NEXTINDEX(h); - - /* - * If we're inserting at the middle of the line pointer array, - * copy pointers that will follow the new one up on the page. - */ - - if (index < nxtindex) { - src = (char *) &(h->h_linp[index]); - dest = (char *) &(h->h_linp[index + 1]); - nbytes = (h->h_lower - (src - ((char *) h))) - + sizeof(h->h_linp[0]); - (void) bcopy(src, dest, nbytes); - } - - /* compute size and copy data to page */ - if (h->h_flags & F_LEAF) { - nbytes = d->d_ksize + d->d_dsize - + (sizeof(DATUM) - sizeof(char)); - } else { - nbytes = id->i_size + (sizeof(IDATUM) - sizeof(char)); - } - dest = (((char *) h) + h->h_upper) - LONGALIGN(nbytes); - (void) bcopy((char *) p, dest, nbytes); - - /* update statistics */ - dest -= (int) h; - h->h_linp[index] = (index_t) dest; - h->h_upper = (index_t) dest; - h->h_lower += sizeof(index_t); - - /* we're done */ - h->h_flags |= F_DIRTY; - - return (RET_SUCCESS); -} diff --git a/lib/libc/db/btree/lrucache.c b/lib/libc/db/btree/lrucache.c deleted file mode 100644 index 567ba08a18..0000000000 --- a/lib/libc/db/btree/lrucache.c +++ /dev/null @@ -1,377 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Mike Olson. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -char sccsid[] = "@(#)lrucache.c 5.3 (Berkeley) 2/22/91"; -#endif /* LIBC_SCCS and not lint */ - -/* - * lrucache.c -- LRU cache for disk-based btree pages. - * - * This file implements an LRU cache in user space for disk-based - * btrees. - */ -#include -#include -#include -#include -#include "lrucache.h" - -/* - * LRUINIT -- Initialize a new LRU cache. - * - * There's a separate LRU cache for every open file descriptor on which - * the user wants caching. The desired cache size and logical page - * size are passed in. We try to avoid growing the cache beyond the - * limit specified by the user, but if we cannot satisfy a block request - * without growing the cache, we do so. - * - * Note that the page size passed in is the logical page size for - * use with this file descriptor, and doesn't necessarily have anything - * to do with the underlying hardware page size. - * - * Parameters: - * fd -- file descriptor for this cache - * cachesz -- number of buffers in cache (suggested) - * pagesz -- logical page size inside this file - * inproc -- routine to call when a buffer is read - * outproc -- routine to call when a buffer is written - * - * Returns: - * Opaque pointer to the LRU cache on success, NULL on failure. - * - * Side Effects: - * Allocates memory for the hash table and LRU cache. Buffers - * are allocated on demand, later. - */ -LRU -lruinit(fd, cachesz, pagesz, opaque, inproc, outproc) - int fd; - int cachesz; - int pagesz; - char *opaque; - int (*inproc)(); - int (*outproc)(); -{ - register LRUCACHE *l; - int nbytes; - - /* allocate the LRU cache struct */ - if ((l = (LRUCACHE *) malloc((unsigned) sizeof(LRUCACHE))) - == (LRUCACHE *) NULL) - return ((LRU) NULL); - - /* allocate the hash table */ - nbytes = cachesz * sizeof(CACHE_ENT *); - if ((l->lru_cache = (CACHE_ENT **) malloc((unsigned) nbytes)) - == (CACHE_ENT **) NULL) { - (void) free((char *) l); - return ((LRU) NULL); - } - - /* init memory */ - bzero((char *) (l->lru_cache), (size_t) nbytes); - l->lru_fd = fd; - l->lru_psize = pagesz; - l->lru_csize = cachesz; - l->lru_cursz = 0; - l->lru_opaque = opaque; - l->lru_head = l->lru_tail = (LRU_ENT *) NULL; - l->lru_inproc = inproc; - l->lru_outproc = outproc; - - return ((LRU) l); -} - -/* - * LRUGET -- Get a buffer from an LRU cache. - * - * If the buffer is not in the cache at present, this routine will - * instantiate it from the file. This REQUIRES that the desired - * block actually be on disk; we don't do non-blocking reads, so - * if it's not actually out there, this routine won't return for - * a very long time. In order to instantiate a new buffer, use - * lrugetnew(). - * - * Parameters: - * lru -- the LRU cache to use. - * pgno -- the logical block number to get. - * nread -- pointer to an int, in which the number of bytes - * read is returned. - * - * Returns: - * (char *) pointer to the buffer for the desired block. The - * number of bytes actually read is returned in *nread. - * - * Warnings: - * The requested buffer is locked down until the user does - * an explicit lrurelease() on it. - */ - -char * -lruget(lru, pgno, nread) - LRU lru; - int pgno; - int *nread; -{ - LRUCACHE *l = (LRUCACHE *) lru; - CACHE_ENT *ce; - LRU_ENT *lruent; - char *buffer; - long pos; - - /* is it already in the cache? */ - if ((ce = lruhashget(l, pgno)) != (CACHE_ENT *) NULL) { - - /* yes, move it to the head and return it */ - lruent = ce->c_lruent; - lruent->l_flags &= ~F_FREE; - lruhead(l, ce->c_lruent); - *nread = l->lru_psize; - return (ce->c_lruent->l_buffer); - } - - /* not there, get a free page */ - if ((buffer = lrugetpg(l, pgno, nread, lruget)) == (char *) NULL) - return ((char *) NULL); - - /* okay, got a buffer -- fill it */ - pos = (long) (l->lru_psize * pgno); - if (lseek(l->lru_fd, pos, L_SET) != pos) - return ((char *) NULL); - - *nread = read(l->lru_fd, buffer, l->lru_psize); - - if (l->lru_inproc) - (*(l->lru_inproc))(buffer, l->lru_opaque); - - return (buffer); -} - -/* - * LRUGETNEW -- Get a page for a new block in an LRU cache. - * - * This routine allows users to instantiate pages for a file if they - * don't exist on disk yet. It's used to make a file bigger. - * - * Parameters: - * lru -- the LRU cache to use - * pgno -- the (new) logical page number to instantiate - * nread -- ptr to int to get number of bytes read (this is - * guaranteed to be zero, since the page isn't on disk) - * - * Returns: - * Pointer to a buffer for the associated page, or NULL on - * failure. - * - * Warnings: - * The associated buffer is locked down until the user - * explicitly does an lrurelease() on it. - */ - -char * -lrugetnew(lru, pgno, nread) - LRU lru; - int pgno; - int *nread; -{ - LRUCACHE *l = (LRUCACHE *) lru; - - *nread = 0; - return (lrugetpg(l, pgno, nread, lrugetnew)); -} - -/* - * LRUFLUSH -- flush an LRU page to disk. - * - * This routine forces a page to disk. Users should use lruwrite(), - * which simply marks a page dirty and does late writing. - * - * Parameters: - * l -- LRU cache - * lruent -- the LRU cache entry whose page we should flush - * - * Returns: - * Zero on success, -1 on failure. - */ - -lruflush(l, lruent) - LRUCACHE *l; - LRU_ENT *lruent; -{ - long pos; - - if (l->lru_outproc) - (*(l->lru_outproc))(lruent->l_buffer, l->lru_opaque); - - pos = (long) (l->lru_psize * lruent->l_pgno); - if (lseek(l->lru_fd, pos, L_SET) != pos) - return (-1); - if (write(l->lru_fd, lruent->l_buffer, l->lru_psize) != l->lru_psize) - return (-1); - - if (l->lru_inproc) - (*(l->lru_inproc))(lruent->l_buffer, l->lru_opaque); - - lruent->l_flags &= ~F_DIRTY; - return (0); -} - -/* - * LRUWRITE -- Mark an LRU cache buffer dirty - * - * This routine is how users should move their changes to disk. The - * cache code marks the associated buffer dirty, and flushes it to - * disk if we need to reuse the buffer for some other page. - * - * Parameters: - * lru -- LRU cache - * pgno -- page number to flush - * - * Returns: - * Zero on success, -1 on failure. - */ - -int -lruwrite(lru, pgno) - LRU lru; - int pgno; -{ - LRUCACHE *l = (LRUCACHE *) lru; - CACHE_ENT *ce; - - if ((ce = lruhashget(l, pgno)) == (CACHE_ENT *) NULL) - return (-1); - - /* mark the entry dirty */ - ce->c_lruent->l_flags |= F_DIRTY; - - return (0); -} - -/* - * LRUSYNC -- Flush all changes to disk - * - * This routine allows users to force all changes to buffers currently - * in memory to disk. This is expensive. - * - * Parameters: - * lru -- LRU cache to flush - * - * Returns: - * Zero on success, -1 on failure - * - * Side Effects: - * After this call, all buffers are clean. - */ - -int -lrusync(lru) - LRU lru; -{ - LRUCACHE *l = (LRUCACHE *) lru; - LRU_ENT *le; - - for (le = l->lru_head; le != (LRU_ENT *) NULL; le = le->l_next) - if (le->l_flags & F_DIRTY) - if (lruflush(l, le) < 0) - return (-1); - return (0); -} - -/* - * LRURELEASE -- Release a buffer in the LRU cache for reuse - * - * When the user does an lruget() or lrugetnew(), the buffer we return - * is locked down, to guarantee that it's not reused while the user - * still needs it. Once a buffer is no longer needed, it should be - * released (via this routine) so that we can use it for other pages - * if necessary. - * - * Parameters: - * lru -- LRU cache - * pgno -- page number of buffer to release - * - * Returns: - * Zero on success, -1 on failure - */ - -int -lrurelease(lru, pgno) - LRU lru; - int pgno; -{ - LRUCACHE *l = (LRUCACHE *) lru; - CACHE_ENT *ce; - - if ((ce = lruhashget(l, pgno)) == (CACHE_ENT *) NULL) - return (-1); - ce->c_lruent->l_flags |= F_FREE; - return (0); -} - -/* - * LRUFREE -- Free an entire LRU cache - * - * This routine releases an LRU cache. The cache should not be - * used again. - * - * Parameters: - * lru -- LRU cache to free - * - * Returns: - * None. - * - * Side Effects: - * Frees a lot of memory. - */ - -void -lrufree(lru) - LRU lru; -{ - LRUCACHE *l = (LRUCACHE *) lru; - LRU_ENT *le; - LRU_ENT *sle; - - for (le = l->lru_head; le != (LRU_ENT *) NULL; ) { - free((char *) (le->l_buffer)); - sle = le; - le = le->l_next; - free((char *) sle); - } - free ((char *) l); -} diff --git a/lib/libc/db/btree/lrucache.h b/lib/libc/db/btree/lrucache.h deleted file mode 100644 index 9a44613636..0000000000 --- a/lib/libc/db/btree/lrucache.h +++ /dev/null @@ -1,110 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Mike Olson. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* - * @(#)lrucache.h 5.1 (Berkeley) 1/23/91 - */ - -/* - * LRU list entries. The head of the list is the most-recently requested - * block; the tail is the least-recently requested one. - */ - -typedef struct LRU_ENT { - char *l_buffer; /* buffer we return to user */ - int l_pgno; /* logical page number */ - int l_flags; /* FREE and DIRTY bits */ - struct LRU_ENT *l_prev; /* predecessor in LRU list */ - struct LRU_ENT *l_next; /* successor in LRU list */ -} LRU_ENT; - -/* - * Cache entries. We use a hash table to avoid a linear walk of the LRU - * list when we need to look up blocks by number. The hash table is - * chained. - */ - -typedef struct CACHE_ENT { - int c_pgno; - LRU_ENT *c_lruent; - struct CACHE_ENT *c_chain; -} CACHE_ENT; - -/* - * The LRU cache structure. The cache size (lru_csize) is the largest size - * the user wants us to grow to; current size (lru_cursz) is always less than - * or equal to lru_csize. Note that we will grow the cache (lru_csize) if - * it's the only way that we can satisfy a user's block request. - */ - -typedef struct LRUCACHE { - int lru_fd; - int lru_csize; - int lru_psize; - int lru_cursz; - char *lru_opaque; /* passed to inproc, outproc */ - int (*lru_inproc)(); - int (*lru_outproc)(); - LRU_ENT *lru_head; - LRU_ENT *lru_tail; - CACHE_ENT **lru_cache; -} LRUCACHE; - -#ifndef NULL -#define NULL 0 -#endif /* ndef NULL */ - -/* this is the opaque type we return for LRU caches */ -typedef char *LRU; - -/* bits for l_flags in LRU_ENT structure */ -#define F_DIRTY (1 << 0) -#define F_FREE (1 << 1) - -/* lru module routines */ -extern CACHE_ENT *lruhashget(); -extern CACHE_ENT *lruhashput(); -extern int lruhashdel(); -extern void lruhead(); -extern int lrugrow(); -extern LRU lruinit(); -extern int lruwrite(); -extern int lrusync(); -extern char *lruget(); -extern char *lrugetnew(); -extern char *lrugetpg(); -extern int lrurelease(); -extern void lrufree(); diff --git a/lib/libc/db/btree/lruhash.c b/lib/libc/db/btree/lruhash.c deleted file mode 100644 index 91a9fdbb2d..0000000000 --- a/lib/libc/db/btree/lruhash.c +++ /dev/null @@ -1,173 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Mike Olson. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)lruhash.c 5.2 (Berkeley) 2/22/91"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include "lrucache.h" - -#define HASH(l, pgno) (pgno % l->lru_csize) - -/* - * LRUHASHGET -- Look up a block in the LRU cache by page number. - * - * Parameters: - * l -- LRU cache - * pgno -- number of the logical page to get - * - * Returns: - * (CACHE_ENT *) pointer to the associated hash table entry - * (if any), or NULL (if none). - */ - -CACHE_ENT * -lruhashget(l, pgno) - LRUCACHE *l; - int pgno; -{ - int hashind; - CACHE_ENT *ce; - - hashind = HASH(l, pgno); - - /* walk the chain */ - for (ce = l->lru_cache[hashind]; - ce != (CACHE_ENT *) NULL; - ce = ce->c_chain) { - if (ce->c_pgno == pgno) - return (ce); - } - - return ((CACHE_ENT *) NULL); -} - -/* - * LRUHASHPUT -- Add an entry for a given page to the cache hash table. - * - * This routine assumes that the given page does not yet have an entry - * in the table. - * - * Parameters: - * l -- LRU cache - * pgno -- logical page number for this entry - * lruent -- LRU list entry at which hash table entry should - * point - * - * Returns: - * (CACHE_ENT *) pointer to the new hash table entry on success, - * or NULL on failure. - * - * Side Effects: - * Allocates memory which should be freed when the hash table - * entry is removed. - */ - -CACHE_ENT * -lruhashput(l, pgno, lruent) - LRUCACHE *l; - int pgno; - LRU_ENT *lruent; -{ - int hashind; - CACHE_ENT *ce; - - if ((ce = (CACHE_ENT *) malloc((unsigned) sizeof(CACHE_ENT))) - == (CACHE_ENT *) NULL) - return ((CACHE_ENT *) NULL); - - hashind = HASH(l, pgno); - - ce->c_pgno = pgno; - ce->c_lruent = lruent; - ce->c_chain = l->lru_cache[hashind]; - l->lru_cache[hashind] = ce; - - return (ce); -} - -/* - * LRUHASHDEL -- Delete the entry for a given page from the LRU cache - * hash table. - * - * Parameters: - * l -- LRU cache - * pgno -- page number for which we should delete htable entry - * - * Returns: - * Zero on success, -1 on failure. - * - * Side Effects: - * Releases the memory occupied by the hash table entry. - */ - -int -lruhashdel(l, pgno) - LRUCACHE *l; - int pgno; -{ - CACHE_ENT *ce; - CACHE_ENT *sce; - int hashind; - - sce = (CACHE_ENT *) NULL; - hashind = HASH(l, pgno); - - /* find the entry we want to delete */ - for (ce = l->lru_cache[hashind]; - ce != (CACHE_ENT *) NULL; - ce = ce->c_chain) { - if (ce->c_pgno == pgno) - break; - sce = ce; - } - - if (ce == (CACHE_ENT *) NULL) - return (-1); - - /* remove it from the chain */ - if (sce == (CACHE_ENT *) NULL) - l->lru_cache[hashind] = ce->c_chain; - else - sce->c_chain = ce->c_chain; - - /* release it */ - free ((char *) ce); - - /* success */ - return (0); -} diff --git a/lib/libc/db/btree/lrutils.c b/lib/libc/db/btree/lrutils.c deleted file mode 100644 index a8a016bc75..0000000000 --- a/lib/libc/db/btree/lrutils.c +++ /dev/null @@ -1,244 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Mike Olson. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)lrutils.c 5.2 (Berkeley) 2/22/91"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include -#include "lrucache.h" - -/* - * LRUGETPG -- Get a free page from the LRU cache. - * - * This routine grows the cache if necessary, finds an unused page if - * it can, and handles flushing dirty buffers to disk. - * - * One of the parameters to this routine (f) is the routine that called - * us. If we have to grow the cache, we call this routine recursively - * in order to fill the buffer. The reason for this is that we have - * two interfaces that call lrugetpg(). Lruget() fills a page from disk, - * and lrugetnew() just allocates a new (empty) page. - * - * Parameters: - * l -- LRU cache to use. - * pgno -- page number for which we want a buffer - * nread -- pointer to an int to get number of bytes read - * f -- who called us - * - * Returns: - * (char *) pointer to buffer to use, or NULL on failure. - * - * Warnings: - * The buffer returned is locked down until the user does an - * explicit lrurelease() on it. - */ - -char * -lrugetpg(l, pgno, nread, f) - LRUCACHE *l; - int pgno; - int *nread; - char *(*f)(); -{ - CACHE_ENT *ce; - LRU_ENT *lruent; - char *buffer; - - /* if we're allowed to grow the cache, do so */ - if (l->lru_cursz < l->lru_csize) { - - /* get a buffer */ - if ((buffer = (char *) malloc((unsigned) l->lru_psize)) - == (char *) NULL) - return ((char *) NULL); - - /* get and LRU list entry */ - if ((lruent = (LRU_ENT *) malloc((unsigned) sizeof(LRU_ENT))) - == (LRU_ENT *) NULL) - return ((char *) NULL); - lruent->l_buffer = buffer; - lruent->l_pgno = pgno; - lruent->l_flags = NULL; - - /* manage spaghetti */ - lruent->l_prev = (LRU_ENT *) NULL; - lruent->l_next = l->lru_head; - if (l->lru_head != (LRU_ENT *) NULL) - l->lru_head->l_prev = lruent; - l->lru_head = lruent; - if (l->lru_tail == (LRU_ENT *) NULL) - l->lru_tail = lruent; - - /* add it to the hash table */ - ce = lruhashput(l, pgno, lruent); - l->lru_cursz++; - } else { - lruent = l->lru_tail; - - /* find the oldest unused buffer */ - while (lruent != (LRU_ENT *) NULL - && !(lruent->l_flags & F_FREE)) - lruent = lruent->l_prev; - - /* if no free buffer was found, we have to grow the cache */ - if (lruent == (LRU_ENT *) NULL) { - if (lrugrow(l) < 0) - return ((char *) NULL); - return ((*f)((LRU) l, pgno, nread)); - } - - /* okay, found a free buffer -- update hash table and list */ - ce = lruhashget(l, lruent->l_pgno); - if (lruhashdel(l, lruent->l_pgno) < 0) - return ((char *) NULL); - - /* flush the old page to disk, if necessary */ - if (lruent->l_flags & F_DIRTY) - if (lruflush(l, lruent) < 0) - return ((char *) NULL); - - /* update stats, hash table, and list */ - lruent->l_pgno = pgno; - lruhead(l, lruent); - ce = lruhashput(l, pgno, lruent); - buffer = lruent->l_buffer; - } -#ifdef lint - ce = ce; -#endif /* lint */ - - /* lock this page down */ - lruent->l_flags &= ~F_FREE; - - return (buffer); -} - -/* - * LRUHEAD -- Move an LRU list entry to the head of the list. - * - * The head of the list is where the most recently used item goes. - * - * Parameters: - * l -- LRU cache - * lruent -- entry to move to the head of the list - * - * Returns: - * None - * - * Side Effects: - * Updates the cache's head and tail pointers as required. - */ - -void -lruhead(l, lruent) - LRUCACHE *l; - LRU_ENT *lruent; -{ - LRU_ENT *next; - LRU_ENT *prev; - - if (l->lru_head == lruent) - return; - - next = lruent->l_next; - prev = lruent->l_prev; - lruent->l_prev = (LRU_ENT *) NULL; - lruent->l_next = l->lru_head; - l->lru_head->l_prev = lruent; - l->lru_head = lruent; - - prev->l_next = next; - if (next != (LRU_ENT *) NULL) - next->l_prev = prev; - - if (l->lru_tail == lruent) - l->lru_tail = prev; -} - -/* - * LRUGROW -- Grow the LRU cache - * - * This routine is called only if we can't satisfy a user's get() request - * using an existing buffer. We need to rebuild the hash table so that - * subsequent lookups work correctly, since the cache size is used to - * compute hash keys. - * - * Parameters: - * l -- LRU cache to grow - * - * Returns: - * Zero on success, -1 on failure - */ - -int -lrugrow(l) - LRUCACHE *l; -{ - int oldsz, newsz; - CACHE_ENT **new; - CACHE_ENT *ce, *nce; - int h; - int i; - - oldsz = l->lru_csize; - newsz = l->lru_csize + 1; - - /* get a new hash table */ - if ((new = (CACHE_ENT **) malloc((unsigned)newsz * sizeof(CACHE_ENT *))) - == (CACHE_ENT **) NULL) - return (-1); - - /* build the new hash table */ - bzero((char *) new, (size_t) (newsz * sizeof(CACHE_ENT *))); - for (i = oldsz; --i >= 0; ) { - for (ce = l->lru_cache[i]; ce != (CACHE_ENT *) NULL; ) { - nce = ce->c_chain; - h = ce->c_pgno % newsz; - ce->c_chain = new[h]; - new[h] = ce; - ce = nce; - } - } - - /* get rid of the old hash table, and update the cache */ - free ((char *) (l->lru_cache)); - l->lru_cache = new; - l->lru_csize = newsz; - - return (0); -} diff --git a/lib/libc/db/btree/search.c b/lib/libc/db/btree/search.c deleted file mode 100644 index 3006888071..0000000000 --- a/lib/libc/db/btree/search.c +++ /dev/null @@ -1,296 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Mike Olson. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)search.c 5.2 (Berkeley) 4/8/91"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include -#include "btree.h" - -/* - * _BT_FIRST -- Find the first item in the tree that matches the supplied - * key. - * - * This routine supports deletion. When the user supplies a key to - * be deleted, we find the first one, and iteratively delete all the - * matching ones that follow it. - * - * Parameters: - * t -- btree in which to find first occurrence - * key -- key to find - * - * Returns: - * The BTITEM for the matching item. If there's no match, - * this may point to the first item > than the supplied key, - * or off the end of the page. - * - * Warnings: - * The BTITEM returned is in static space and will be overwritten - * by the next search of any kind in any btree. - */ - -BTITEM * -_bt_first(t, key) - BTREE_P t; - DBT *key; -{ - BTHEADER *h; - BTITEM *item; - index_t next; - int r; - - /* find any matching item */ - item = _bt_search(t, key); - h = t->bt_curpage; - next = NEXTINDEX(h); - - /* if we're off the end of the page, search failed and we're done */ - if (item->bti_index >= next) - return (item); - - /* as long as we have an exact match, walk backwards */ - while ((r = _bt_cmp(t, key->data, item->bti_index)) == 0) { - - /* at start of page? */ - if (item->bti_index == 0) { - - /* if no prev page, we're done */ - if (h->h_prevpg == P_NONE) - return (item); - - /* walk backward, skipping empty pages */ - do { - if (_bt_getpage(t, h->h_prevpg) == RET_ERROR) - return ((BTITEM *) NULL); - h = t->bt_curpage; - } while (NEXTINDEX(h) == 0 && h->h_prevpg != P_NONE); - - if (NEXTINDEX(h) != 0) - item->bti_index = NEXTINDEX(h) - 1; - else - item->bti_index = 0; - - item->bti_pgno = h->h_pgno; - } else { - item->bti_index--; - } - } - - /* if we went too far backwards, step forward one entry */ - if (r > 0) { - if (++(item->bti_index) >= NEXTINDEX(h) - && h->h_nextpg != P_NONE) { - - /* walk forward, skipping empty pages */ - do { - if (_bt_getpage(t, h->h_nextpg) == RET_ERROR) - return ((BTITEM *) NULL); - h = t->bt_curpage; - } while (h->h_nextpg != P_NONE && NEXTINDEX(h) == 0); - - item->bti_index = 0; - item->bti_pgno = h->h_pgno; - } - } - - /* got it */ - return (item); -} - -/* - * _BT_SEARCH, _BT_SEARCHR -- Search for a particular key in the tree. - * - * Parameters: - * t -- btree in which to search - * key -- key to find - * - * Returns: - * BTITEM for matching item, if any, or the BTITEM for the - * location of the key, if it were in the tree. - * - * Warnings: - * The BTITEM returned is in static memory, and will be - * overwritten by the next search of any kind in any tree. - */ - -BTITEM * -_bt_search(t, key) - BTREE_P t; - DBT *key; -{ - /* we want to start all of our searches at the root */ - if (_bt_getpage(t, (pgno_t) P_ROOT) == RET_ERROR) - return ((BTITEM *) NULL); - - return (_bt_searchr(t, key)); -} - -BTITEM * -_bt_searchr(t, key) - BTREE_P t; - DBT *key; -{ - BTHEADER *h = t->bt_curpage; - index_t index; - IDATUM *id; - DATUM *d; - static BTITEM item; - - /* do a binary search on the current page */ - index = _bt_binsrch(t, key->data); - - /* - * At this point, the binary search terminated because the endpoints - * got too close together, or we have a match. Figure out which - * case applies and decide what to do based on the page type. - */ - if (h->h_flags & F_LEAF) { - item.bti_pgno = h->h_pgno; - item.bti_index = index; - if (index < NEXTINDEX(h)) - d = (DATUM *) GETDATUM(h,index); - else - d = (DATUM *) NULL; - - item.bti_datum = d; - return(&item); - } else { - id = (IDATUM *) GETDATUM(h, index); - if (_bt_push(t, h->h_pgno) == RET_ERROR) - return ((BTITEM *) NULL); - if (_bt_getpage(t, id->i_pgno) == RET_ERROR) - return ((BTITEM *) NULL); - return (_bt_searchr(t, key)); - } -} - -/* - * _BT_BINSRCH -- Do a binary search for a given key on the current page. - * - * Searches on internal pages are handled slightly differently from - * searches on leaf pages. This is because internal page searches - * find the largest item <= key in the tree, and leaf searches find - * the smallest item >= key. This guarantees that leaf page searches - * leave us pointing at the item's correct position, and internal - * searches descend the tree correctly. - * - * Parameters: - * t -- tree to search - * key -- key we're looking for - * - * Returns: - * Index of the line pointer array entry for the (closest) - * match to key on the current page, with "closest" as defined - * above. - */ - -index_t -_bt_binsrch(t, key) - BTREE_P t; - char *key; -{ - index_t lbound, ubound, cur; - BTHEADER *h = t->bt_curpage; - int match = 0; - int r; - - lbound = 0; - ubound = NEXTINDEX(h); - if (ubound > 0) - --ubound; - - /* do a binary search on the current page */ - while ((ubound - lbound) > 1) { - cur = lbound + ((ubound - lbound) / 2); - r = _bt_cmp(t, key, cur); - - if (r > 0) - lbound = cur + 1; - else if (r < 0) - ubound = cur; - else { - match++; - break; - } - } - - /* - * At this point, the binary search terminated because the endpoints - * got too close together, or we have a match. Figure out which - * case applies, decide what to do based on the page type (leaf or - * internal), and do the right thing. - */ - if (match) { - return (cur); - } else if (ubound != lbound) { - if (h->h_flags & F_LEAF) { - r = _bt_cmp(t, key, lbound); - if (r <= 0) { - return (lbound); - } - } else { - r = _bt_cmp(t, key, ubound); - - /* for internal nodes, move as far left as possible */ - if (r < 0) { - r = _bt_cmp(t, key, lbound); - if (r < 0 && lbound > 0) - --lbound; - return (lbound); - } else { - return (ubound); - } - } - } - - if (h->h_flags & F_LEAF) { - if (ubound < NEXTINDEX(h)) { - r = _bt_cmp(t, key, ubound); - if (r > 0) - ubound++; - } - } else { - /* for internal pages, move as far left as possible */ - if (ubound == NEXTINDEX(h)) - ubound--; - - while (_bt_cmp(t, key, ubound) < 0) - ubound--; - } - return (ubound); -} diff --git a/lib/libc/db/btree/seq.c b/lib/libc/db/btree/seq.c deleted file mode 100644 index 2ce111c2c2..0000000000 --- a/lib/libc/db/btree/seq.c +++ /dev/null @@ -1,318 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Mike Olson. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)seq.c 5.4 (Berkeley) 3/3/91"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include -#include -#include -#include "btree.h" - -/* - * _BT_SEQINIT -- Initialize a sequential scan on the btree. - * - * Sets the tree's notion of the current scan location correctly - * given a key and a direction. - * - * Parameters: - * t -- tree in which to initialize scan - * key -- key for initial scan position - * flags -- R_NEXT, R_PREV - * - * Returns: - * RET_SUCCESS, RET_ERROR, or RET_SPECIAL if there's no data - * in the tree to scan. - * - * Side Effects: - * Changes current scan position for the tree. Almost certainly - * changes current page, as well. Sets BTF_SEQINIT bit in tree - * flags, so that we know we've initialized a scan. - */ - -int -_bt_seqinit(t, key, flags) - BTREE_P t; - DBT *key; - int flags; -{ - BTITEM *item; - BTHEADER *h; - CURSOR *c; - IDATUM *id; - index_t last; - - /* - * Figure out if we really have to search for the key that the - * user supplied. If key is null, then this is an unkeyed scan - * and we can just start from an endpoint. - */ - - c = &(t->bt_cursor); - - if (flags == R_CURSOR) { - if (key->data != (u_char *) NULL) { - - /* key supplied, find first instance of it */ - item = _bt_first(t, key); - c->c_index = item->bti_index; - c->c_pgno = t->bt_curpage->h_pgno; - } else { - errno = EINVAL; - return (RET_ERROR); - } - - } else { - - /* - * Unkeyed scan. For backward scans, find the last item - * in the tree; for forward scans, find the first item. - */ - - if (_bt_getpage(t, (pgno_t) P_ROOT) == RET_ERROR) - return (RET_ERROR); - h = t->bt_curpage; - if (flags == R_LAST || flags == R_PREV) { - - /* backward scan */ - while (!(h->h_flags & F_LEAF)) { - last = NEXTINDEX(h) - 1; - id = (IDATUM *) GETDATUM(h,last); - if (_bt_getpage(t, id->i_pgno) == RET_ERROR) - return (RET_ERROR); - h = t->bt_curpage; - } - - /* skip empty pages */ - while (NEXTINDEX(h) == 0 && h->h_prevpg != P_NONE) { - if (_bt_getpage(t, h->h_prevpg) == RET_ERROR) - return (RET_ERROR); - h = t->bt_curpage; - } - - c->c_pgno = h->h_pgno; - if (NEXTINDEX(h) > 0) - c->c_index = NEXTINDEX(h) - 1; - else - c->c_index = 0; - } else if (flags == R_FIRST || flags == R_NEXT) { - /* forward scan */ - while (!(h->h_flags & F_LEAF)) { - id = (IDATUM *) GETDATUM(h,0); - if (_bt_getpage(t, id->i_pgno) == RET_ERROR) - return (RET_ERROR); - h = t->bt_curpage; - } - - /* skip empty pages */ - while (NEXTINDEX(h) == 0 && h->h_nextpg != P_NONE) { - if (_bt_getpage(t, h->h_nextpg) == RET_ERROR) - return (RET_ERROR); - h = t->bt_curpage; - } - - c->c_pgno = h->h_pgno; - c->c_index = 0; - } else { - /* no flags passed in */ - errno = EINVAL; - return (RET_ERROR); - } - } - - /* okay, scan is initialized */ - t->bt_flags |= BTF_SEQINIT; - - /* don't need the descent stack anymore */ - while (_bt_pop(t) != P_NONE) - continue; - - if (c->c_index == NEXTINDEX(t->bt_curpage)) - return (RET_SPECIAL); - - return (RET_SUCCESS); -} - -/* - * _BT_SEQADVANCE -- Advance the sequential scan on this tree. - * - * Moves the current location pointer for the scan on this tree one - * spot in the requested direction. - * - * Parameters: - * t -- btree being scanned - * flags -- for R_NEXT, R_PREV - * - * Returns: - * RET_SUCCESS, RET_ERROR, or RET_SPECIAL if there is no - * more data in the specified direction. - * - * Side Effects: - * May change current page. - */ - -int -_bt_seqadvance(t, flags) - BTREE_P t; - int flags; -{ - BTHEADER *h; - CURSOR *c; - index_t index; - - c = &(t->bt_cursor); - index = c->c_index; - - if (_bt_getpage(t, c->c_pgno) == RET_ERROR) - return (RET_ERROR); - h = t->bt_curpage; - - /* by the time we get here, don't need the cursor key anymore */ - if (c->c_key != (char *) NULL) - (void) free(c->c_key); - - if (flags == R_NEXT) { - - /* - * This is a forward scan. If the cursor is pointing - * at a virtual record (that is, it was pointing at - * a record that got deleted), then we should return - * the record it's pointing at now. Otherwise, we - * should advance the scan. In either case, we need - * to be careful not to run off the end of the current - * page. - */ - - if (c->c_flags & CRSR_BEFORE) { - - if (index >= NEXTINDEX(h)) { - /* out of items on this page, get next page */ - if (h->h_nextpg == P_NONE) { - /* tell caller we're done... */ - c->c_index = NEXTINDEX(h); - return (RET_SPECIAL); - } - - /* skip empty pages */ - do { - if (_bt_getpage(t, h->h_nextpg) - == RET_ERROR) { - c->c_index = NEXTINDEX(h); - return (RET_ERROR); - } - h = t->bt_curpage; - c->c_pgno = h->h_pgno; - } while (NEXTINDEX(h) == 0 - && h->h_nextpg != P_NONE); - - if (NEXTINDEX(h) == 0) { - /* tell caller we're done */ - c->c_index = NEXTINDEX(h); - return (RET_SPECIAL); - } - index = 0; - } - c->c_flags &= ~CRSR_BEFORE; - - } else if (++index >= NEXTINDEX(h)) { - - /* out of items on this page, get next page */ - if (h->h_nextpg == P_NONE) { - /* tell caller we're done... */ - c->c_index = NEXTINDEX(h); - return (RET_SPECIAL); - } - - /* skip empty pages */ - do { - if (_bt_getpage(t, h->h_nextpg) == RET_ERROR) { - c->c_index = NEXTINDEX(h); - return (RET_ERROR); - } - h = t->bt_curpage; - c->c_pgno = h->h_pgno; - } while (NEXTINDEX(h) == 0 && h->h_nextpg != P_NONE); - - if (NEXTINDEX(h) == 0) { - /* tell caller we're done */ - c->c_index = NEXTINDEX(h); - return (RET_SPECIAL); - } - index = 0; - } - } else if (flags == R_PREV) { - - /* for backward scans, life is substantially easier */ - c->c_flags &= ~CRSR_BEFORE; - if (c->c_key != (char *) NULL) { - (void) free(c->c_key); - c->c_key = (char *) NULL; - } - - if (index == 0) { - - /* we may be done */ - c->c_index = 0; - - /* out of items on this page, get next page */ - if (h->h_prevpg == P_NONE) - return (RET_SPECIAL); - - /* skip empty pages */ - do { - if (_bt_getpage(t, h->h_prevpg) == RET_ERROR) - return (RET_ERROR); - h = t->bt_curpage; - c->c_pgno = h->h_pgno; - } while (NEXTINDEX(h) == 0 && h->h_prevpg != P_NONE); - - if (NEXTINDEX(h) == 0) - return (RET_SPECIAL); - - index = NEXTINDEX(h) - 1; - } else - --index; - } else { - /* must specify a direction */ - errno = EINVAL; - return (RET_ERROR); - } - - c->c_index = index; - return (RET_SUCCESS); -} diff --git a/lib/libc/db/btree/split.c b/lib/libc/db/btree/split.c deleted file mode 100644 index 3dd34868e0..0000000000 --- a/lib/libc/db/btree/split.c +++ /dev/null @@ -1,691 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Mike Olson. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)split.c 5.2 (Berkeley) 2/22/91"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include -#include -#include -#include "btree.h" - -/* - * _BT_SPLIT -- Split a page into two pages. - * - * Splits are caused by insertions, and propogate up the tree in - * the usual way. The root page is always page 1 in the file on - * disk, so root splits are handled specially. On entry to this - * routine, t->bt_curpage is the page to be split. - * - * Parameters: - * t -- btree in which to do split. - * - * Returns: - * RET_SUCCESS, RET_ERROR. - * - * Side Effects: - * Changes the notion of the current page. - */ - -int -_bt_split(t) - BTREE_P t; -{ - BTHEADER *h; - BTHEADER *left, *right; - pgno_t nextpgno, parent; - int nbytes, len; - IDATUM *id; - DATUM *d; - char *src; - IDATUM *new; - pgno_t oldchain; - u_char flags; - - h = (BTHEADER *) t->bt_curpage; - - /* split root page specially, since it must remain page 1 */ - if (h->h_pgno == P_ROOT) { - return (_bt_splitroot(t)); - } - - /* - * This is a little complicated. We go to some trouble to - * figure out which of the three possible cases -- in-memory tree, - * disk tree (no cache), and disk tree (cache) -- we have, in order - * to avoid unnecessary copying. If we have a disk cache, then we - * have to do some extra copying, though, since the cache code - * manages buffers externally to this code. - */ - - if (ISDISK(t) && ISCACHE(t)) { - if ((left = (BTHEADER *) malloc((unsigned) t->bt_psize)) - == (BTHEADER *) NULL) - return (RET_ERROR); - left->h_pgno = left->h_prevpg = left->h_nextpg = P_NONE; - left->h_flags = t->bt_curpage->h_flags; - left->h_lower = (index_t) - (((char *) &(left->h_linp[0])) - ((char *) left)); - left->h_upper = t->bt_psize; - - } else { - if ((left = _bt_allocpg(t)) == (BTHEADER *) NULL) - return (RET_ERROR); - } - left->h_pgno = h->h_pgno; - - if ((right = _bt_allocpg(t)) == (BTHEADER *) NULL) - return (RET_ERROR); - right->h_pgno = ++(t->bt_npages); - - /* now do the split */ - if (_bt_dopsplit(t, left, right) == RET_ERROR) - return (RET_ERROR); - - right->h_prevpg = left->h_pgno; - nextpgno = right->h_nextpg = h->h_nextpg; - left->h_nextpg = right->h_pgno; - left->h_prevpg = h->h_prevpg; - - /* okay, now use the left half of the page as the new page */ - if (ISDISK(t) && ISCACHE(t)) { - (void) bcopy((char *) left, (char *) t->bt_curpage, - (int) t->bt_psize); - (void) free ((char *) left); - left = t->bt_curpage; - } else { - (void) free((char *) t->bt_curpage); - t->bt_curpage = left; - } - - /* - * Write the new pages out. We need them to stay where they are - * until we're done updating the parent pages. - */ - - if (_bt_write(t, left, NORELEASE) == RET_ERROR) - return (RET_ERROR); - if (_bt_write(t, right, NORELEASE) == RET_ERROR) - return (RET_ERROR); - - /* update 'prev' pointer of old neighbor of left */ - if (nextpgno != P_NONE) { - if (_bt_getpage(t, nextpgno) == RET_ERROR) - return (RET_ERROR); - h = t->bt_curpage; - h->h_prevpg = right->h_pgno; - h->h_flags |= F_DIRTY; - } - - if ((parent = _bt_pop(t)) != P_NONE) { - if (right->h_flags & F_LEAF) { - d = (DATUM *) GETDATUM(right, 0); - len = d->d_ksize; - if (d->d_flags & D_BIGKEY) { - bcopy(&(d->d_bytes[0]), - (char *) &oldchain, - sizeof(oldchain)); - if (_bt_markchain(t, oldchain) == RET_ERROR) - return (RET_ERROR); - src = (char *) &oldchain; - flags = D_BIGKEY; - } else { - src = (char *) &(d->d_bytes[0]); - flags = 0; - } - } else { - id = (IDATUM *) GETDATUM(right, 0); - len = id->i_size; - flags = id->i_flags; - src = (char *) &(id->i_bytes[0]); - } - nbytes = len + (sizeof(IDATUM) - sizeof(char)); - new = (IDATUM *) malloc((unsigned) nbytes); - if (new == (IDATUM *) NULL) - return (RET_ERROR); - new->i_size = len; - new->i_pgno = right->h_pgno; - new->i_flags = flags; - if (len > 0) - (void) bcopy(src, (char *) &(new->i_bytes[0]), len); - - nbytes = LONGALIGN(nbytes) + sizeof(index_t); - if (_bt_getpage(t, parent) == RET_ERROR) - return (RET_ERROR); - - h = t->bt_curpage; - - /* - * Split the parent if we need to, then reposition the - * tree's current page pointer for the new datum. - */ - if ((h->h_upper - h->h_lower) < nbytes) { - if (_bt_split(t) == RET_ERROR) - return (RET_ERROR); - if (_bt_reposition(t, new, parent, right->h_prevpg) - == RET_ERROR) - return (RET_ERROR); - } - - /* okay, now insert the new idatum */ - if (_bt_inserti(t, new, right->h_prevpg) == RET_ERROR) - return (RET_ERROR); - } - - /* - * Okay, split is done; don't need right page stapled down anymore. - * The page we call 'left' above is the new version of the old - * (split) page, so we can't release it. - */ - - if (_bt_release(t, right) == RET_ERROR) - return (RET_ERROR); - if (ISDISK(t) && !ISCACHE(t)) - (void) free((char *) right); - - return (RET_SUCCESS); -} - -/* - * _BT_REPOSITION -- Reposition the current page pointer of a btree. - * - * After splitting a node in the tree in order to make room for - * an insertion, we need to figure out which page after the split - * should get the item we want to insert. This routine positions - * the tree's current page pointer appropriately. - * - * Parameters: - * t -- tree to position - * new -- the item we want to insert - * parent -- parent of the node that we just split - * prev -- page number of item directly to the left of - * new's position in the tree. - * - * Returns: - * RET_SUCCESS, RET_ERROR. - * - * Side Effects: - * None. - */ - -int -_bt_reposition(t, new, parent, prev) - BTREE_P t; - IDATUM *new; - pgno_t parent; - pgno_t prev; -{ - index_t i, next; - IDATUM *idx; - - if (parent == P_ROOT) { - - /* - * If we just split the root page, then there are guaranteed - * to be exactly two IDATUMs on it. Look at both of them - * to see if they point to the page that we want. - */ - - if (_bt_getpage(t, parent) == RET_ERROR) - return (RET_ERROR); - - next = NEXTINDEX(t->bt_curpage); - for (i = 0; i < next; i++) { - idx = (IDATUM *) GETDATUM(t->bt_curpage, i); - if (_bt_getpage(t, idx->i_pgno) == RET_ERROR) - return (RET_ERROR); - if (_bt_isonpage(t, new, prev) == RET_SUCCESS) - return (RET_SUCCESS); - if (_bt_getpage(t, parent) == RET_ERROR) - return (RET_ERROR); - } - } else { - - /* - * Get the parent page -- which is where the new item would - * have gone -- and figure out whether the new item now goes - * on the parent, or the page immediately to the right of - * the parent. - */ - - if (_bt_getpage(t, parent) == RET_ERROR) - return (RET_ERROR); - if (_bt_isonpage(t, new, prev) == RET_SUCCESS) - return (RET_SUCCESS); - if (_bt_getpage(t, t->bt_curpage->h_nextpg) == RET_ERROR) - return (RET_ERROR); - if (_bt_isonpage(t, new, prev) == RET_SUCCESS) - return (RET_SUCCESS); - } - return (RET_ERROR); -} - -/* - * _BT_ISONPAGE -- Is the IDATUM for a given page number on the current page? - * - * This routine is used by _bt_reposition to decide whether the current - * page is the correct one on which to insert a new item. - * - * Parameters: - * t -- tree to check - * new -- the item that will be inserted (used for binary search) - * prev -- page number of page whose IDATUM is immediately to - * the left of new's position in the tree. - * - * Returns: - * RET_SUCCESS, RET_ERROR (corresponding to TRUE, FALSE). - */ - -int -_bt_isonpage(t, new, prev) - BTREE_P t; - IDATUM *new; - pgno_t prev; -{ - BTHEADER *h = (BTHEADER *) t->bt_curpage; - index_t i, next; - IDATUM *idx; - - i = _bt_binsrch(t, &(new->i_bytes[0])); - while (i != 0 && _bt_cmp(t, &(new->i_bytes[0]), i) == 0) - --i; - next = NEXTINDEX(h); - idx = (IDATUM *) GETDATUM(h, i); - while (i < next && idx->i_pgno != prev) { - i++; - idx = (IDATUM *) GETDATUM(h, i); - } - if (idx->i_pgno == prev) - return (RET_SUCCESS); - else - return (RET_ERROR); -} - -/* - * _BT_SPLITROOT -- Split the root of a btree. - * - * The root page for a btree is always page one. This means that in - * order to split the root, we need to do extra work. - * - * Parameters: - * t -- tree to split - * - * Returns: - * RET_SUCCESS, RET_ERROR. - * - * Side Effects: - * Splits root upward in the usual way, adding two new pages - * to the tree (rather than just one, as in usual splits). - */ - -int -_bt_splitroot(t) - BTREE_P t; -{ - BTHEADER *h = t->bt_curpage; - BTHEADER *left, *right; - IDATUM *id; - BTHEADER *where_h; - char *src, *dest; - int len, nbytes; - u_long was_leaf; - pgno_t oldchain; - u_char flags; - - /* get two new pages for the split */ - if ((left = _bt_allocpg(t)) == (BTHEADER *) NULL) - return (RET_ERROR); - left->h_pgno = ++(t->bt_npages); - if ((right = _bt_allocpg(t)) == (BTHEADER *) NULL) - return (RET_ERROR); - right->h_pgno = ++(t->bt_npages); - - /* do the split */ - if (_bt_dopsplit(t, left, right) == RET_ERROR) - return (RET_ERROR); - - /* connect the new pages correctly */ - right->h_prevpg = left->h_pgno; - left->h_nextpg = right->h_pgno; - - /* - * Write the child pages out now. We need them to remain - * where they are until we finish updating parent pages, - * however. - */ - - if (_bt_write(t, left, NORELEASE) == RET_ERROR) - return (RET_ERROR); - if (_bt_write(t, right, NORELEASE) == RET_ERROR) - return (RET_ERROR); - - /* now change the root page into an internal page */ - was_leaf = (h->h_flags & F_LEAF); - h->h_flags &= ~F_LEAF; - h->h_lower = (index_t) (((char *) (&(h->h_linp[0]))) - ((char *) h)); - h->h_upper = (index_t) t->bt_psize; - (void) bzero((char *) &(h->h_linp[0]), (int) (h->h_upper - h->h_lower)); - - /* put two new keys on root page */ - where_h = left; - while (where_h) { - DATUM *data; - IDATUM *idata; - - if (was_leaf) { - data = (DATUM *) GETDATUM(where_h, 0); - - if (where_h == left) { - len = 0; /* first key in tree is null */ - } else { - if (data->d_flags & D_BIGKEY) { - bcopy(&(data->d_bytes[0]), - (char *) &oldchain, - sizeof(oldchain)); - if (_bt_markchain(t, oldchain) == RET_ERROR) - return (RET_ERROR); - src = (char *) &oldchain; - flags = D_BIGKEY; - } else { - src = (char *) &(data->d_bytes[0]); - flags = 0; - } - len = data->d_ksize; - } - } else { - idata = (IDATUM *) GETDATUM(where_h, 0); - len = idata->i_size; - flags = idata->i_flags; - src = &(idata->i_bytes[0]); - } - dest = ((char *) h) + h->h_upper; - nbytes = len + (sizeof (IDATUM) - sizeof(char)); - dest -= LONGALIGN(nbytes); - id = (IDATUM *) dest; - id->i_size = len; - id->i_pgno = where_h->h_pgno; - id->i_flags = flags; - if (len > 0) - (void) bcopy((char *) src, (char *) &(id->i_bytes[0]), len); - dest -= ((int) h); - h->h_linp[NEXTINDEX(h)] = (index_t) dest; - h->h_upper = (index_t) dest; - h->h_lower += sizeof(index_t); - - /* next page */ - if (where_h == left) - where_h = right; - else - where_h = (BTHEADER *) NULL; - } - - if (_bt_release(t, left) == RET_ERROR) - return (RET_ERROR); - if (_bt_release(t, right) == RET_ERROR) - return (RET_ERROR); - - /* - * That's it, split is done. If we're doing a non-cached disk - * btree, we can free up the pages we allocated, as they're on - * disk, now. - */ - - if (ISDISK(t) && !ISCACHE(t)) { - (void) free ((char *) left); - (void) free ((char *) right); - } - - h->h_flags |= F_DIRTY; - - return (RET_SUCCESS); -} - -/* - * _BT_DOPSPLIT -- Do the work of splitting a page - * - * This routine takes two page pointers and splits the data on the - * current page of the btree between them. - * - * We do a lot of work here to handle duplicate keys on a page; we - * have to place these keys carefully to guarantee that later searches - * will find them correctly. See comments in the code below for details. - * - * Parameters: - * t -- tree to split - * left -- pointer to page to get left half of the data - * right -- pointer to page to get right half of the data - * - * Returns: - * None. - */ - -int -_bt_dopsplit(t, left, right) - BTREE_P t; - BTHEADER *left; - BTHEADER *right; -{ - BTHEADER *h = t->bt_curpage; - size_t psize; - char *where; - BTHEADER *where_h; - index_t where_i; - int nbytes, dsize, fixedsize, freespc; - index_t i; - index_t save_lower, save_upper, save_i; - index_t switch_i; - char *save_key; - DATUM *d; - CURSOR *c; - index_t top; - int free_save; - pgno_t chain; - int ignore; - - /* - * Our strategy is to put half the bytes on each page. We figure - * out how many bytes we have total, and then move items until - * the last item moved put at least 50% of the data on the left - * page. - */ - save_key = (char *) NULL; - psize = (int) t->bt_psize; - where = ((char *) left) + psize; - where_h = left; - where_i = 0; - nbytes = psize - (int) ((char *) &(h->h_linp[0]) - ((char *) h)); - freespc = nbytes; - - top = NEXTINDEX(h); - if (h->h_flags & F_LEAF) - fixedsize = (sizeof(DATUM) - sizeof(char)); - else - fixedsize = (sizeof(IDATUM) - sizeof(char)); - - save_key = (char *) NULL; - - /* move data */ - for (i = 0; i < top; i++) { - - /* - * Internal and leaf pages have different layouts for - * data items, but in both cases the first entry in the - * data item is a size_t. - */ - d = (DATUM *) GETDATUM(h,i); - if (h->h_flags & F_LEAF) { - dsize = d->d_ksize + d->d_dsize + fixedsize; - } else { - dsize = d->d_ksize + fixedsize; - } - - /* - * If a page contains duplicate keys, we have to be - * careful about splits. A sequence of duplicate keys - * may not begin in the middle of one page and end in - * the middle of another; it must begin on a page boundary, - * in order for searches on the internal nodes to work - * correctly. - */ - if (where_h == left) { - if (save_key == (char *) NULL) { - if (h->h_flags & F_LEAF) { - if (d->d_flags & D_BIGKEY) { - free_save = TRUE; - bcopy(&(d->d_bytes[0]), - (char *) &chain, - sizeof(chain)); - if (_bt_getbig(t, chain, - &save_key, - &ignore) - == RET_ERROR) - return (RET_ERROR); - } else { - free_save = FALSE; - save_key = (char *) &(d->d_bytes[0]); - } - } else { - IDATUM *id = (IDATUM *) d; - - if (id->i_flags & D_BIGKEY) { - free_save = TRUE; - bcopy(&(id->i_bytes[0]), - (char *) &chain, - sizeof(chain)); - if (_bt_getbig(t, chain, - &save_key, - &ignore) - == RET_ERROR) - return (RET_ERROR); - } else { - free_save = FALSE; - save_key = (char *) - &(id->i_bytes[0]); - } - } - save_i = 0; - save_lower = where_h->h_lower; - save_upper = where_h->h_upper; - } else { - if (_bt_cmp(t, save_key, i) != 0) { - save_lower = where_h->h_lower; - save_upper = where_h->h_upper; - save_i = i; - } - if (h->h_flags & F_LEAF) { - if (free_save) - (void) free(save_key); - if (d->d_flags & D_BIGKEY) { - free_save = TRUE; - bcopy(&(d->d_bytes[0]), - (char *) &chain, - sizeof(chain)); - if (_bt_getbig(t, chain, - &save_key, - &ignore) - == RET_ERROR) - return (RET_ERROR); - } else { - free_save = FALSE; - save_key = (char *) &(d->d_bytes[0]); - } - } else { - IDATUM *id = (IDATUM *) d; - - if (id->i_flags & D_BIGKEY) { - free_save = TRUE; - bcopy(&(id->i_bytes[0]), - (char *) &chain, - sizeof(chain)); - if (_bt_getbig(t, chain, - &save_key, - &ignore) - == RET_ERROR) - return (RET_ERROR); - } else { - free_save = FALSE; - save_key = (char *) - &(id->i_bytes[0]); - } - } - } - } - - /* copy data and update page state */ - where -= LONGALIGN(dsize); - (void) bcopy((char *) d, (char *) where, dsize); - where_h->h_upper = where_h->h_linp[where_i] = - (index_t) (where - (int) where_h); - where_h->h_lower += sizeof(index_t); - where_i++; - - /* if we've moved half, switch to the right-hand page */ - nbytes -= LONGALIGN(dsize) + sizeof(index_t); - if ((freespc - nbytes) > nbytes) { - nbytes = 2 * freespc; - - /* identical keys go on the same page */ - if (save_i > 0) { - /* i gets incremented at loop bottom... */ - i = save_i - 1; - where_h->h_lower = save_lower; - where_h->h_upper = save_upper; - } - where = ((char *) right) + psize; - where_h = right; - switch_i = where_i; - where_i = 0; - } - } - - /* - * If there was an active scan on the database, and we just - * split the page that the cursor was on, we may need to - * adjust the cursor to point to the same entry as before the - * split. - */ - - c = &(t->bt_cursor); - if ((t->bt_flags & BTF_SEQINIT) - && (c->c_pgno == h->h_pgno) - && (c->c_index >= switch_i)) { - c->c_pgno = where_h->h_pgno; - c->c_index -= where_i; - } - return (RET_SUCCESS); -} diff --git a/lib/libc/db/btree/storage.c b/lib/libc/db/btree/storage.c deleted file mode 100644 index 963d16c066..0000000000 --- a/lib/libc/db/btree/storage.c +++ /dev/null @@ -1,664 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Mike Olson. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)storage.c 5.2 (Berkeley) 2/22/91"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include -#include -#include -#include -#include -#include "btree.h" - -/* - * BT_GETPAGE -- Make pgno the current page of the btree. - * - * This routine is just a wrapper that decides whether to call the - * memory or disk-based routine to do the work. - * - * Parameters: - * t -- btree in which to get page - * pgno -- page number to get - * - * Returns: - * RET_SUCCESS or RET_ERROR. - */ - -int -_bt_getpage(t, pgno) - BTREE_P t; - pgno_t pgno; -{ -#ifdef DEBUG - if (pgno == P_NONE) - _punt(); -#endif /* DEBUG */ - - /* see if we can get away without doing any work */ - if (t->bt_curpage != (BTHEADER *) NULL) { - if (t->bt_curpage->h_pgno == pgno) - return (RET_SUCCESS); - } - - if (t->bt_fname == (char *) NULL) - return (_bt_getmpage(t, pgno)); - else - return (_bt_getdpage(t, pgno)); -} - -/* - * _BT_GETMPAGE -- Make pgno the current page of the btree. - * - * This routine gets pages for in-memory btrees. - * - * Parameters: - * t -- btree in which to get page - * pgno -- page number to get - * - * Returns: - * RET_SUCCESS or RET_ERROR. - */ - -int -_bt_getmpage(t, pgno) - register BTREE_P t; - pgno_t pgno; -{ - int htindex; - BTHEADER *h; - HTBUCKET *b; - - if (t->bt_curpage == (BTHEADER *) NULL) { - if (pgno != P_ROOT) { - errno = EBADF; - return (RET_ERROR); - } - - t->bt_npages++; - h = (BTHEADER *) malloc((unsigned) t->bt_psize); - if (h == (BTHEADER *) NULL) - return (RET_ERROR); - - h->h_pgno = P_ROOT; - h->h_flags = F_LEAF; - h->h_lower = (index_t) - (((char *) &(h->h_linp[0])) - ((char *) h)); - h->h_upper = t->bt_psize; - h->h_prevpg = h->h_nextpg = P_NONE; - - t->bt_curpage = h; - - /* get the root page into the hash table */ - if (_bt_write(t, h, RELEASE) == RET_ERROR) - return (RET_ERROR); - } - - htindex = HASHKEY(pgno); - - for (b = t->bt_s.bt_ht[htindex]; - b != (HTBUCKET *) NULL; - b = b->ht_next) { - if (b->ht_pgno == pgno) { - t->bt_curpage = b->ht_page; - return (RET_SUCCESS); - } - } - return (RET_ERROR); -} - -/* - * _BT_GETDPAGE -- Make pgno the current page of the btree. - * - * This routine gets pages for disk btrees. - * - * Because disk btree pages must be readable across machine architectures, - * the btree code writes integers out in network format. This routine - * converts them back to host format before returning the page. - * - * Parameters: - * t -- btree in which to get page - * pgno -- page number to get - * - * Returns: - * RET_SUCCESS, RET_ERROR. - */ - -int -_bt_getdpage(t, pgno) - register BTREE_P t; - pgno_t pgno; -{ - BTHEADER *h; - char *cache; - long pos; - int n, nbytes; - - /* if we have an lru cache, let the cache code do the work */ - if (ISCACHE(t)) { - cache = t->bt_s.bt_d.d_cache; - - /* release the old page */ - if (t->bt_curpage != (BTHEADER *) NULL) { - pgno_t opgno = t->bt_curpage->h_pgno; - t->bt_curpage->h_flags &= ~F_DIRTY; - - if (lruwrite(cache, (int) opgno) < 0) - return (RET_ERROR); - - if (lrurelease(cache, (int) opgno) < 0) - return (RET_ERROR); - } - - if (pgno > t->bt_npages) { - if ((h = (BTHEADER *) lrugetnew(cache, (int)pgno, &nbytes)) - == (BTHEADER *) NULL) - return (RET_ERROR); - t->bt_npages = pgno; - } else { - if ((h = (BTHEADER *) lruget(cache, (int)pgno, &nbytes)) - == (BTHEADER *) NULL) - return (RET_ERROR); - } - - /* init this page, if necessary */ - if (nbytes == 0) { - h->h_pgno = pgno; - h->h_flags = F_LEAF; - h->h_lower = (index_t) - (((char *) &(h->h_linp[0])) - ((char *) h)); - h->h_upper = t->bt_psize; - h->h_prevpg = h->h_nextpg = P_NONE; - } - - t->bt_curpage = h; - - return (RET_SUCCESS); - } - - /* sync the current page, if necessary */ - if (t->bt_curpage != (BTHEADER *) NULL) { - if (t->bt_curpage->h_flags & F_DIRTY) - if (_bt_write(t, t->bt_curpage, RELEASE) == RET_ERROR) - return (RET_ERROR); - } else { - if (t->bt_npages == 0) - t->bt_npages = 1; - - /* if no current page, get space for one */ - if ((t->bt_curpage = (BTHEADER *) malloc((unsigned) t->bt_psize)) - == (BTHEADER *) NULL) { - return (RET_ERROR); - } - } - - n = t->bt_psize; - pos = (long) (pgno * n); - - /* seek to correct location in file */ - if (lseek(t->bt_s.bt_d.d_fd, pos, L_SET) != pos) { - return (RET_ERROR); - } - - /* read the page */ - if ((nbytes = read(t->bt_s.bt_d.d_fd, t->bt_curpage, n)) < n) { - - /* - * If we didn't get a full page, we must have gotten no - * data at all -- in which case we're trying to read a - * root page that doesn't exist yet. This is the only - * case in which this is okay. If this happens, construct - * an empty root page by hand. - */ - if (nbytes != 0 || pgno != P_ROOT) { - errno = EBADF; - return (RET_ERROR); - } - - h = (BTHEADER *) t->bt_curpage; - h->h_pgno = pgno; - h->h_flags = F_LEAF; - h->h_lower = (index_t) - (((char *) &(h->h_linp[0])) - ((char *) h)); - h->h_upper = t->bt_psize; - h->h_prevpg = h->h_nextpg = P_NONE; - } else - (void) _bt_pgin(t->bt_curpage, (char *) t->bt_lorder); - - return (RET_SUCCESS); -} - -/* - * _BT_PGOUT, _BT_PGIN -- Convert host-specific number layout to/from - * the host-independent format stored on disk. - * - * Parameters: - * h -- page to convert - * _lorder -- byte order for pages (stored as a char * in the - * cache, and passed around as a magic cookie). - * - * Returns: - * RET_SUCCESS (lru code requires a return value). - * - * Side Effects: - * Layout of tree metadata on the page is changed in place. - * - * Warnings: - * Everywhere else in the code, the types pgno_t and index_t - * are opaque. These two routines know what they really - * are. - */ - -int -_bt_pgout(h, _lorder) - BTHEADER *h; - char *_lorder; -{ - int i; - int top; - int lorder; - DATUM *d; - IDATUM *id; - size_t chain; - - lorder = (int) _lorder; - if (lorder == BYTE_ORDER) - return (RET_SUCCESS); - - if (h->h_flags & F_LEAF) { - if (h->h_flags & F_CONT) { - if (h->h_prevpg == P_NONE) { - size_t longsz; - - (void) bcopy((char *) &(h->h_linp[0]), - (char *) &longsz, - sizeof(longsz)); - BLSWAP(longsz); - (void) bcopy((char *) &longsz, - (char *) &(h->h_linp[0]), - sizeof(longsz)); - } - } else { - top = NEXTINDEX(h); - for (i = 0; i < top; i++) { - d = (DATUM *) GETDATUM(h, i); - if (d->d_flags & D_BIGKEY) { - (void) bcopy((char *) &(d->d_bytes[0]), - (char *) &chain, - sizeof(chain)); - BLSWAP(chain); - (void) bcopy((char *) &chain, - (char *) &(d->d_bytes[0]), - sizeof(chain)); - } - if (d->d_flags & D_BIGDATA) { - (void) bcopy((char *) &(d->d_bytes[d->d_ksize]), - (char *) &chain, - sizeof(chain)); - BLSWAP(chain); - (void) bcopy((char *) &chain, - (char *) &(d->d_bytes[d->d_ksize]), - sizeof(chain)); - } - BLSWAP(d->d_dsize); - BLSWAP(d->d_ksize); - BLSWAP(d->d_flags); - BLSWAP(h->h_linp[i]); - } - } - } else { - top = NEXTINDEX(h); - for (i = 0; i < top; i++) { - id = (IDATUM *) GETDATUM(h, i); - BLSWAP(id->i_size); - BLSWAP(id->i_pgno); - BLSWAP(id->i_flags); - if (id->i_flags & D_BIGKEY) { - (void) bcopy((char *) &(id->i_bytes[0]), - (char *) &chain, - sizeof(chain)); - BLSWAP(chain); - (void) bcopy((char *) &chain, - (char *) &(id->i_bytes[0]), - sizeof(chain)); - } - BLSWAP(h->h_linp[i]); - } - } - BLSWAP(h->h_flags); - BLSWAP(h->h_pgno); - BLSWAP(h->h_prevpg); - BLSWAP(h->h_nextpg); - BLSWAP(h->h_lower); - BLSWAP(h->h_upper); - - return (RET_SUCCESS); -} - -int -_bt_pgin(h, _lorder) - BTHEADER *h; - char *_lorder; -{ - int i; - int top; - int lorder; - DATUM *d; - IDATUM *id; - size_t chain; - - /* - * If btree pages are to be stored in the host byte order, don't - * bother swapping. - */ - lorder = (int) _lorder; - if (lorder == BYTE_ORDER) - return (RET_SUCCESS); - - BLSWAP(h->h_upper); - BLSWAP(h->h_lower); - BLSWAP(h->h_nextpg); - BLSWAP(h->h_prevpg); - BLSWAP(h->h_pgno); - BLSWAP(h->h_flags); - - if (h->h_flags & F_LEAF) { - if (h->h_flags & F_CONT) { - if (h->h_prevpg == P_NONE) { - size_t longsz; - - (void) bcopy((char *) &(h->h_linp[0]), - (char *) &longsz, - sizeof(longsz)); - BLSWAP(longsz); - (void) bcopy((char *) &longsz, - (char *) &(h->h_linp[0]), - sizeof(longsz)); - } - } else { - top = NEXTINDEX(h); - for (i = 0; i < top; i++) { - BLSWAP(h->h_linp[i]); - d = (DATUM *) GETDATUM(h, i); - BLSWAP(d->d_dsize); - BLSWAP(d->d_ksize); - BLSWAP(d->d_flags); - if (d->d_flags & D_BIGKEY) { - (void) bcopy((char *) &(d->d_bytes[0]), - (char *) &chain, - sizeof(chain)); - BLSWAP(chain); - (void) bcopy((char *) &chain, - (char *) &(d->d_bytes[0]), - sizeof(chain)); - } - if (d->d_flags & D_BIGDATA) { - (void) bcopy((char *) &(d->d_bytes[d->d_ksize]), - (char *) &chain, - sizeof(chain)); - BLSWAP(chain); - (void) bcopy((char *) &chain, - (char *) &(d->d_bytes[d->d_ksize]), - sizeof(chain)); - } - } - } - } else { - top = NEXTINDEX(h); - for (i = 0; i < top; i++) { - BLSWAP(h->h_linp[i]); - id = (IDATUM *) GETDATUM(h, i); - BLSWAP(id->i_size); - BLSWAP(id->i_pgno); - BLSWAP(id->i_flags); - if (id->i_flags & D_BIGKEY) { - (void) bcopy((char *) &(id->i_bytes[0]), - (char *) &chain, - sizeof(chain)); - BLSWAP(chain); - (void) bcopy((char *) &chain, - (char *) &(id->i_bytes[0]), - sizeof(chain)); - } - } - } - return (RET_SUCCESS); -} - -/* - * _BT_ALLOCPG -- allocate a new page in the btree. - * - * This is called when we split a page, to get space to do the split. - * For disk btrees, these pages are released when the split is done. - * For memory btrees, they are not. - * - * Parameters: - * t -- tree in which to do split - * - * Returns: - * Pointer to the newly-allocated page - */ - -BTHEADER * -_bt_allocpg(t) - BTREE_P t; -{ - BTHEADER *h = t->bt_curpage; - BTHEADER *nh; - int nbytes; - - /* if we have a cache, let the cache code do the work */ - if (ISDISK(t) && ISCACHE(t)) { - nh = (BTHEADER *) lrugetnew(t->bt_s.bt_d.d_cache, - (int) (t->bt_npages + 1), - &nbytes); - } else { - nh = (BTHEADER *) malloc((unsigned) t->bt_psize); - } - - if (nh != (BTHEADER *) NULL) { - nh->h_pgno = nh->h_prevpg = nh->h_nextpg = P_NONE; - nh->h_flags = h->h_flags; - nh->h_lower = (index_t) - (((char *) &(nh->h_linp[0])) - ((char *) nh)); - nh->h_upper = t->bt_psize; - } - - return (nh); -} - -/* - * _BT_WRITE -- Write a specific page to a btree file. - * - * Parameters: - * t -- btree to get the page - * h -- page to write - * relflag -- (int) this page may/may not be released - * - * Returns: - * RET_SUCCESS, RET_ERROR. - * - * Side Effects: - * Writes a metadata page if none has been written yet. - */ - -int -_bt_write(t, h, relflag) - BTREE_P t; - BTHEADER *h; - int relflag; -{ - long pos; - int htindex; - HTBUCKET *b; - char *cache; - pgno_t pgno; - - h->h_flags &= ~F_DIRTY; - if (ISDISK(t)) { - - /* if we haven't done so yet, write the metadata */ - if (!(t->bt_flags & BTF_METAOK)) { - if (_bt_wrtmeta(t) == RET_ERROR) - return (RET_ERROR); - } - - pgno = h->h_pgno; - - - /* if we have a cache, let the cache code do the work */ - if ((cache = t->bt_s.bt_d.d_cache) != (char *) NULL) { - if (lruwrite(cache, (int) pgno) < 0) - return (RET_ERROR); - if (relflag && lrurelease(cache, (int) pgno) < 0) - return (RET_ERROR); - - } else { - (void) _bt_pgout(h, (char *) t->bt_lorder); - /* now write the current page */ - pos = (long) (pgno * t->bt_psize); - if (lseek(t->bt_s.bt_d.d_fd, pos, L_SET) != pos) - return (RET_ERROR); - if (write(t->bt_s.bt_d.d_fd, (char *) h, (int) t->bt_psize) - < t->bt_psize) - return (RET_ERROR); - if (!relflag) - (void) _bt_pgin(h, (char *) t->bt_lorder); - } - } else { - /* in-memory btree */ - htindex = HASHKEY(h->h_pgno); - - /* see if we need to overwrite existing entry */ - for (b = t->bt_s.bt_ht[htindex]; - b != (HTBUCKET *) NULL; - b = b->ht_next) { - if (b->ht_pgno == h->h_pgno) { - b->ht_page = h; - return (RET_SUCCESS); - } - } - - /* new entry, write it */ - b = (HTBUCKET *) malloc((unsigned) sizeof (HTBUCKET)); - if (b == (HTBUCKET *) NULL) - return (RET_ERROR); - - b->ht_pgno = h->h_pgno; - b->ht_page = h; - b->ht_next = t->bt_s.bt_ht[htindex]; - t->bt_s.bt_ht[htindex] = b; - } - return (RET_SUCCESS); -} - -/* - * _BT_RELEASE -- Release a locked-down cache page - * - * During page splits, we want to force pages out to the cache - * before we actually release them, in some cases. This routine - * releases such a page when it is no longer needed. - * - * Parameters: - * t -- btree in which to release page - * h -- page to release - * - * Returns: - * RET_SUCCESS, RET_ERROR. - * - * Side Effects: - * None. - */ - -int -_bt_release(t, h) - BTREE_P t; - BTHEADER *h; -{ - if (ISDISK(t) && ISCACHE(t)) { - if (lrurelease(t->bt_s.bt_d.d_cache, (int) (h->h_pgno)) < 0) - return (RET_ERROR); - } - return (RET_SUCCESS); -} - -/* - * _BT_WRTMETA -- Write metadata to the btree. - * - * Parameters: - * t -- tree to which to write - * - * Returns: - * RET_SUCCESS, RET_ERROR. - */ - -int -_bt_wrtmeta(t) - BTREE_P t; -{ - BTMETA m; - - if (lseek(t->bt_s.bt_d.d_fd, 0l, L_SET) != 0l) - return (RET_ERROR); - - /* lorder has to be in host-independent format */ - m.m_lorder = (u_long) htonl((long) t->bt_lorder); - - m.m_magic = BTREEMAGIC; - m.m_version = BTREEVERSION; - m.m_psize = t->bt_psize; - m.m_free = t->bt_free; - m.m_flags = t->bt_flags & BTF_NODUPS; - - if (t->bt_lorder != BYTE_ORDER) { - BLSWAP(m.m_magic); - BLSWAP(m.m_version); - BLSWAP(m.m_psize); - BLSWAP(m.m_free); - BLSWAP(m.m_flags); - } - - if (write(t->bt_s.bt_d.d_fd, (char *) &m, sizeof(m)) - != sizeof(m)) { - return (RET_ERROR); - } - - t->bt_flags |= BTF_METAOK; - - return (RET_SUCCESS); -} diff --git a/lib/libc/db/btree/tests/words.c b/lib/libc/db/btree/tests/words.c deleted file mode 100644 index d9c83e2b34..0000000000 --- a/lib/libc/db/btree/tests/words.c +++ /dev/null @@ -1,528 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Mike Olson. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)words.c 5.1 (Berkeley) 4/12/91"; -#endif /* LIBC_SCCS and not lint */ - -/* - * test1.c -- simple btree test program. - */ - -#include -#include -#include -#include -#include -#include -#include - -#define DICTIONARY "/usr/share/dict/words" - -typedef struct cmd_table { - char *cmd; - int nargs; - int (*func)(); - char *descrip; -} cmd_table; - -extern int cursor(), delcur(), delete(), first(), help(), insert(); -extern int last(), lookup(), next(), previous(); - -cmd_table Commands[] = { - "cursor", 2, cursor, - "cursor : point the scan cursor at ", - "delcur", 1, delcur, - "delcur: delete the word under the scan cursor", - "delete", 2, delete, - "delete : delete from the dictionary", - "first", 1, first, - "first: point the scan cursor at the first dictionary entry", - "help", 1, help, - "help: print this command summary", - "insert", 3, insert, - "insert : insert into the dictionary with definition ", - "last", 1, last, - "last: point the scan cursor at the last dictionary entry", - "lookup", 2, lookup, - "lookup : look up in the dictionary", - "next", 1, next, - "next: move the scan cursor forward one word", - "previous", 1, previous, - "previous: move the scan cursor back one word", - (char *) NULL, 0, NULL, (char *) NULL, -}; - -char *Usage = "[-p pagesize] [-c cachesize] [-u] [-l|b|n] [dbname]"; - -main(argc, argv) - int argc; - char **argv; -{ - char *dbname; - int c; - char *progname; - extern int strcmp(); - extern char *optarg; - extern int optind; - DB *t; - BTREEINFO b; - - progname = *argv; - - b.psize = 0; - b.cachesize = 0; - b.lorder = 0; - b.flags = R_DUP; - b.compare = strcmp; - - while ((c = getopt(argc, argv, "p:c:ulb")) != EOF) { - switch (c) { - case 'p': - b.psize = atoi(optarg); - break; - - case 'c': - b.cachesize = atoi(optarg); - break; - - case 'u': - b.flags = 0; - break; - - case 'l': - b.lorder = LITTLE_ENDIAN; - break; - - case 'b': - b.lorder = BIG_ENDIAN; - break; - - default: - fprintf(stderr, "%s: usage: %s\n", progname, Usage); - exit (1); - } - } - - if (argv[optind] != (char *) NULL) - dbname = argv[optind]; - - if ((t = btree_open(dbname, O_CREAT|O_RDWR, 0600, &b)) == (DB *) NULL) { - perror(progname); - exit (1); - } - - load(t); - - user(t); -} - -load(t) - DB *t; -{ - char *lbuf; - int i, l; - int status; - FILE *fp; - DBT key; - DBT data; - char word[64]; - char drow[64]; - - printf("loading %s...\n", DICTIONARY); - fflush(stdout); - if ((fp = fopen(DICTIONARY, "r")) == (FILE *) NULL) { - perror("/usr/dict/words"); - (void) (*(t->close))(t->internal); - exit (1); - } - - key.data = &word[0]; - data.data = &drow[0]; - while ((lbuf = fgets(word, 64, fp)) != (char *) NULL) { - l = strlen(lbuf) - 1; - lbuf[l] = '\0'; - for (i = 0; i < l; i++) - drow[l - (i + 1)] = word[i]; - drow[l] = '\0'; - - key.size = data.size = l + 1; - - status = (*(t->put))(t->internal, &key, &data, R_NOOVERWRITE); - - switch (status) { - case RET_SUCCESS: - break; - - case RET_ERROR: - perror("put"); - break; - - case RET_SPECIAL: - fprintf(stderr, "%s is a duplicate key!\n", lbuf); - fflush(stderr); - break; - } - } - - (void) fclose(fp); - printf("done\n"); - fflush(stdout); -} - -user(t) - DB *t; -{ - char *lbuf; - int argc; - int i; - char *argv[4]; - char buf[512]; - - for (;;) { - printf("> "); - fflush(stdout); - if ((lbuf = fgets(&buf[0], 512, stdin)) == (char *) NULL) - break; - lbuf[strlen(lbuf) - 1] = '\0'; - - if (strcmp(lbuf, "quit") == 0) - break; - - argc = parse(lbuf, &argv[0], 3); - if (argc == 0) - continue; - - for (i = 0; Commands[i].cmd != (char *) NULL; i++) { - if (strcmp(Commands[i].cmd, argv[0]) == 0) - break; - } - - if (Commands[i].cmd == (char *) NULL) { - fprintf(stderr, - "%s: command unknown ('help' for help)\n", - lbuf); - fflush(stderr); - continue; - } - - if (Commands[i].nargs != argc) { - fprintf(stderr, "arg count\n"); - fflush(stderr); - continue; - } - - switch (argc) { - case 1: - (*(Commands[i].func))(t); - break; - case 2: - (*(Commands[i].func))(t, argv[1]); - break; - case 3: - (*(Commands[i].func))(t, argv[1], argv[2]); - break; - case 4: - (*(Commands[i].func))(t, argv[1], argv[2], argv[3]); - break; - } - } - (void) (*(t->close))(t->internal); - exit (0); -} - -int -parse(lbuf, argv, maxargc) - char *lbuf; - char **argv; - int maxargc; -{ - int argc = 0; - char *c; - - c = lbuf; - while (isspace(*c)) - c++; - while (*c != '\0' && argc < maxargc) { - *argv++ = c; - argc++; - while (!isspace(*c) && *c != '\0') { - c++; - } - while (isspace(*c)) - *c++ = '\0'; - } - return (argc); -} - -int -cursor(t, arg) - DB *t; - char *arg; -{ - int status; - DBT key; - DBT data; - - key.data = arg; - key.size = strlen(arg + 1); - status = (*(t->seq))(t->internal, &key, &data, R_CURSOR); - if (status == RET_SUCCESS) - show(&key, &data); - else - perror("cursor"); -} - -int -delcur(t) - DB *t; -{ - int status; - - status = (*(t->delete))(t->internal, (DBT *) NULL, R_CURSOR); - - if (status == RET_ERROR) - perror("delcur"); -} - -int -delete(t, arg) - DB *t; - char *arg; -{ - int status; - DBT key; - - key.data = arg; - key.size = strlen(arg) + 1; - - status = (*(t->delete))(t->internal, &key, 0); - switch (status) { - case RET_SUCCESS: - break; - - case RET_ERROR: - perror("delete"); - break; - - case RET_SPECIAL: - fprintf(stderr, "%s not found\n", arg); - fflush(stderr); - break; - } -} - -int -first(t) - DB *t; -{ - int status; - DBT key; - DBT data; - - status = (*(t->seq))(t->internal, &key, &data, R_FIRST); - - switch (status) { - case RET_ERROR: - perror("first"); - break; - - case RET_SPECIAL: - printf("no more keys"); - break; - - case RET_SUCCESS: - show(&key, &data); - break; - } -} -int -help(t) - DB *t; -{ - int i; - -#ifdef lint - t = t; -#endif /* lint */ - for (i = 0; Commands[i].cmd != (char *) NULL; i++) - printf("%s\n", Commands[i].descrip); - printf("type 'quit' to quit\n"); -} - -int -insert(t, arg, def) - DB *t; - char *arg; - char *def; -{ - int status; - DBT key; - DBT data; - - key.data = arg; - key.size = strlen(arg) + 1; - data.data = def; - data.size = strlen(def) + 1; - - status = (*(t->put))(t->internal, &key, &data, R_NOOVERWRITE); - switch (status) { - case RET_SUCCESS: - break; - - case RET_ERROR: - perror("put"); - break; - - case RET_SPECIAL: - fprintf(stderr, "%s is a duplicate key!\n", arg); - fflush(stderr); - break; - } -} - -int -last(t) - DB *t; -{ - int status; - DBT key; - DBT data; - - status = (*(t->seq))(t->internal, &key, &data, R_LAST); - - switch (status) { - case RET_ERROR: - perror("last"); - break; - - case RET_SPECIAL: - printf("no more keys"); - break; - - case RET_SUCCESS: - show(&key, &data); - break; - } -} - -int -lookup(t, arg) - DB *t; - char *arg; -{ - int status; - DBT key; - DBT data; - - key.data = arg; - key.size = strlen(arg) + 1; - - status = (*(t->get))(t->internal, &key, &data, 0); - - switch (status) { - case RET_SPECIAL: - printf("not found\n"); - break; - case RET_SUCCESS: - show(&key, &data); - break; - case RET_ERROR: - perror("get"); - break; - } -} - -int -next(t) - DB *t; -{ - int status; - DBT key; - DBT data; - - status = (*(t->seq))(t->internal, &key, &data, R_NEXT); - - switch (status) { - case RET_ERROR: - perror("next"); - break; - - case RET_SPECIAL: - printf("no more keys"); - break; - - case RET_SUCCESS: - show(&key, &data); - break; - } -} - -int -previous(t) - DB *t; -{ - int status; - DBT key; - DBT data; - - status = (*(t->seq))(t->internal, &key, &data, R_PREV); - - switch (status) { - case RET_ERROR: - perror("previous"); - break; - - case RET_SPECIAL: - printf("no more keys"); - break; - - case RET_SUCCESS: - show(&key, &data); - break; - } -} - -show(key, data) - DBT *key; - DBT *data; -{ - if (key->size > 0) - printf("%s", key->data); - if (data->size > 0) - printf("/%s", data->data); - printf("\n"); -} diff --git a/lib/libc/db/btree/updutils.c b/lib/libc/db/btree/updutils.c deleted file mode 100644 index 8ed87fbf2e..0000000000 --- a/lib/libc/db/btree/updutils.c +++ /dev/null @@ -1,174 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Mike Olson. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)updutils.c 5.2 (Berkeley) 2/22/91"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include -#include -#include -#include "btree.h" - -/* - * _BT_FIXSCAN -- Adjust a scan to cope with a change in tree structure. - * - * If the user has an active scan on the database, and we delete an - * item from the page the cursor is pointing at, we need to figure - * out what to do about it. Basically, the solution is to point - * "between" keys in the tree, using the CRSR_BEFORE flag. The - * requirement is that the user should not miss any data in the - * tree during a scan, just because he happened to do some deletions - * or insertions while it was active. - * - * In order to guarantee that the scan progresses properly, we need - * to save the key of any deleted item we were pointing at, so that - * we can check later insertions against it. - * - * Parameters: - * t -- tree to adjust - * index -- index of item at which change was made - * newd -- new datum (for insertions only) - * op -- operation (DELETE or INSERT) causing change - * - * Returns: - * RET_SUCCESS, RET_ERROR (errno is set). - * - * Side Effects: - * None. - */ - -int -_bt_fixscan(t, index, newd, op) - BTREE_P t; - index_t index; - DATUM *newd; - int op; -{ - CURSOR *c; - DATUM *d; - - c = &(t->bt_cursor); - - if (op == DELETE) { - if (index < c->c_index) - c->c_index--; - else if (index == c->c_index) { - if (!(c->c_flags & CRSR_BEFORE)) { - if (_bt_crsrkey(t) == RET_ERROR) - return (RET_ERROR); - c->c_flags |= CRSR_BEFORE; - } - } - } else { - /* - * If we previously deleted the object at this location, - * and now we're inserting a new one, we need to do the - * right thing -- the cursor should come either before - * or after the new item, depending on the key that was - * here, and the new one. - */ - - if (c->c_flags & CRSR_BEFORE) { - if (index <= c->c_index) { - char *tmp; - int itmp; - pgno_t chain; - int r; - - d = (DATUM *) GETDATUM(t->bt_curpage, index); - if (d->d_flags & D_BIGKEY) { - bcopy(&(newd->d_bytes[0]), - (char *) &chain, - sizeof(chain)); - if (_bt_getbig(t, chain, &tmp, &itmp) - == RET_ERROR) - return (RET_ERROR); - } else - tmp = &(newd->d_bytes[0]); - - r = (*(t->bt_compare))(tmp, c->c_key); - if (r < 0) - c->c_index++; - - if (d->d_flags & D_BIGKEY) - (void) free (tmp); - } - } else if (index <= c->c_index) - c->c_index++; - } - return (RET_SUCCESS); -} - -/* - * _BT_CRSRKEY -- Save a copy of the key of the record that the cursor - * is pointing to. The record is about to be deleted. - * - * Parameters: - * t -- btree - * - * Returns: - * RET_SUCCESS, RET_ERROR. - * - * Side Effects: - * Allocates memory for the copy which should be released when - * it is no longer needed. - */ - -int -_bt_crsrkey(t) - BTREE_P t; -{ - CURSOR *c; - DATUM *d; - pgno_t pgno; - int ignore; - - c = &(t->bt_cursor); - d = (DATUM *) GETDATUM(t->bt_curpage, c->c_index); - - if (d->d_flags & D_BIGKEY) { - bcopy(&(d->d_bytes[0]), (char *) &pgno, sizeof(pgno)); - return (_bt_getbig(t, pgno, &(c->c_key), &ignore)); - } else { - if ((c->c_key = (char *) malloc(d->d_ksize)) == (char *) NULL) - return (RET_ERROR); - - bcopy(&(d->d_bytes[0]), c->c_key, (size_t) (d->d_ksize)); - } - return (RET_SUCCESS); -} diff --git a/lib/libc/db/btree/utils.c b/lib/libc/db/btree/utils.c deleted file mode 100644 index 58b499995d..0000000000 --- a/lib/libc/db/btree/utils.c +++ /dev/null @@ -1,350 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Mike Olson. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)utils.c 5.3 (Berkeley) 3/3/91"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include -#include -#include -#include "btree.h" - -/* - * _BT_BUILDRET -- Build return key/data pair as a result of search or scan. - * - * This routine manages the statically allocated buffers in which we - * return data to the user. - * - * Parameters: - * t -- btree from which to return datum - * d -- DATUM to be returned to the user. - * data -- data argument supplied by user for return - * key -- key argument supplied by user for return - * - * Returns: - * RET_SUCCESS, RET_ERROR. - * - * Side Effects: - * May free and reallocate static buffers, if the data - * we want to return is bigger than the space we have to - * do so. - */ - -int -_bt_buildret(t, d, data, key) - BTREE_P t; - DATUM *d; - DBT *data; - DBT *key; -{ - static int _data_s = 0; - static int _key_s = 0; - static char *_data = (char *) NULL; - static char *_key = (char *) NULL; - pgno_t chain; - - if (d->d_flags & D_BIGKEY) { - if (_key != (char *) NULL) - (void) free(_key); - (void) bcopy((char *) &(d->d_bytes[0]), - (char *) &chain, - sizeof(chain)); - if (_bt_getbig(t, chain, &_key, &_key_s) == RET_ERROR) - return (RET_ERROR); - key->data = (u_char *)_key; - key->size = _key_s; - } else { - /* need more space for key? */ - if (d->d_ksize > _key_s) { - if (_key != (char *) NULL) - (void) free (_key); - if ((_key = (char *) malloc((unsigned) d->d_ksize)) - == (char *) NULL) - return (RET_ERROR); - _key_s = d->d_ksize; - } - - key->data = (u_char *)_key; - if ((key->size = d->d_ksize) > 0) - (void) bcopy(&(d->d_bytes[0]), - _key, - (int) d->d_ksize); - } - - if (d->d_flags & D_BIGDATA) { - if (_data != (char *) NULL) - (void) free(_data); - (void) bcopy(&(d->d_bytes[d->d_ksize]), - (char *) &chain, - sizeof(chain)); - if (_bt_getbig(t, chain, &_data, &_data_s) == RET_ERROR) - return (RET_ERROR); - data->data = (u_char *)_data; - data->size = _data_s; - } else { - /* need more space for data? */ - if (d->d_dsize > _data_s) { - if (_data != (char *) NULL) - (void) free (_data); - if ((_data = (char *) malloc((unsigned) d->d_dsize)) - == (char *) NULL) - return (RET_ERROR); - _data_s = d->d_dsize; - } - - data->data = (u_char *)_data; - - if ((data->size = d->d_dsize) > 0) - (void) bcopy(&(d->d_bytes[d->d_ksize]), - _data, - (size_t) (d->d_dsize)); - } - - return (RET_SUCCESS); -} - -/* - * _BT_CMP -- Compare a key to a given item on the current page. - * - * This routine is a wrapper for the user's comparison function. - * - * Parameters: - * t -- tree in which to do comparison - * p -- pointer to one argument for the comparison function - * n -- index of item to supply second arg to comparison function - * - * Returns: - * < 0 if p is < item at n - * = 0 if p is = item at n - * > 0 if p is > item at n - */ - -int -_bt_cmp(t, p, n) - BTREE_P t; - char *p; - index_t n; -{ - BTHEADER *h; - IDATUM *id; - DATUM *d; - char *arg; - int ignore; - int free_arg; - pgno_t chain; - int r; - - h = t->bt_curpage; - - /* - * The left-most key at any level of the tree on internal pages - * is guaranteed (artificially, by the code here) to be less than - * any user key. This saves us from having to update the leftmost - * key when the user inserts a new key in the tree smaller than - * anything we've seen yet. - */ - - if (h->h_prevpg == P_NONE && !(h->h_flags & F_LEAF) && n == 0) - return (1); - - if (h->h_flags & F_LEAF) { - d = (DATUM *) GETDATUM(h,n); - if (d->d_flags & D_BIGKEY) { - free_arg = TRUE; - bcopy(&(d->d_bytes[0]), (char *) &chain, sizeof(chain)); - if (_bt_getbig(t, chain, &arg, &ignore) == RET_ERROR) - return (RET_ERROR); - } else { - free_arg = FALSE; - arg = &(d->d_bytes[0]); - } - } else { - id = (IDATUM *) GETDATUM(h,n); - if (id->i_flags & D_BIGKEY) { - free_arg = TRUE; - bcopy(&(id->i_bytes[0]), - (char *) &chain, - sizeof(chain)); - if (_bt_getbig(t, chain, &arg, &ignore) == RET_ERROR) - return (RET_ERROR); - } else { - free_arg = FALSE; - arg = &(id->i_bytes[0]); - } - } - r = (*(t->bt_compare))(p, arg); - - if (free_arg) - (void) free(arg); - - return (r); -} - -/* - * _BT_PUSH/_BT_POP -- Push/pop a parent page number on the parent stack. - * - * When we descend the tree, we keep track of parent pages in order - * to handle splits on insertions. - * - * Parameters: - * t -- tree for which to push parent - * pgno -- page number to push (_bt_push only) - * - * Returns: - * RET_SUCCESS, RET_ERROR. - */ - -int -_bt_push(t, pgno) - BTREE_P t; - pgno_t pgno; -{ - BTSTACK *new; - - if ((new = (BTSTACK *) malloc((unsigned) sizeof(BTSTACK))) - == (BTSTACK *) NULL) - return (RET_ERROR); - new->bts_pgno = pgno; - new->bts_next = t->bt_stack; - t->bt_stack = new; - - return (RET_SUCCESS); -} - -pgno_t -_bt_pop(t) - BTREE_P t; -{ - BTSTACK *s; - pgno_t p = P_NONE; - - if ((s = t->bt_stack) != (BTSTACK *) NULL) { - p = s->bts_pgno; - t->bt_stack = s->bts_next; - (void) free ((char *) s); - } - return (p); -} - -#ifdef DEBUG -void -_btdump(tree) - BTREE tree; -{ - BTREE_P t = (BTREE_P) tree; - DATUM *d; - IDATUM *id; - BTHEADER *h; - pgno_t npages; - pgno_t i; - index_t cur, top; - - npages = t->bt_npages; - (void) printf("\"%s\" fd %d pgsz %d curpg %d @ 0x%lx", - t->bt_fname, t->bt_s.bt_d.d_fd, - t->bt_psize, t->bt_curpage); - (void) printf("npg %d cmp 0x%lx flags=(", npages, t->bt_compare); - if (t->bt_flags & BTF_SEQINIT) - (void) printf("BTF_SEQINIT"); - (void) printf(")\n"); - - for (i = P_ROOT; i <= npages; i++) { - if (_bt_getpage(t, i) == RET_ERROR) - _punt(); - h = t->bt_curpage; - top = NEXTINDEX(h); - (void) printf(" page %d:\n", i); - (void) printf("\tpgno %d prev %d next %d\n", - h->h_pgno, h->h_prevpg, h->h_nextpg); - (void) printf("\tlower %d upper %d nextind %d flags (", - h->h_lower, h->h_upper, top); - if (h->h_flags & F_LEAF) - (void) printf("F_LEAF"); - else - (void) printf(""); - if (h->h_flags & F_DIRTY) - (void) printf("|F_DIRTY"); - if (h->h_flags & F_PRESERVE) - (void) printf("|F_PRESERVE"); - if (h->h_flags & F_CONT) { - (void) printf("|F_CONT)"); - if (h->h_prevpg == P_NONE) { - size_t longsz; - (void) bcopy((char *) &(h->h_linp[0]), - (char *) &longsz, - sizeof(longsz)); - printf("\n\t\t(chain start, data length %ld)", - longsz); - } - printf("\n"); - continue; - } - (void) printf(")\n"); - for (cur = 0; cur < top; cur++) { - (void) printf("\t [%d] off %d ", cur, h->h_linp[cur]); - if (h->h_flags & F_LEAF) { - d = (DATUM *) GETDATUM(h,cur); - (void) printf("ksize %d", d->d_ksize); - if (d->d_flags & D_BIGKEY) - (void) printf(" (indirect)"); - (void) printf("; dsize %d", d->d_dsize); - if (d->d_flags & D_BIGDATA) - (void) printf(" (indirect)"); - } else { - id = (IDATUM *) GETDATUM(h,cur); - (void) printf("size %d pgno %d", - id->i_size, id->i_pgno); - if (id->i_flags & D_BIGKEY) - (void) printf(" (indirect)"); - } - (void) printf("\n"); - } - (void) printf("\n"); - } -} -#endif /* DEBUG */ - -#ifdef DEBUG -_punt() -{ - int pid; - - pid = getpid(); - (void) kill(pid, SIGILL); -} -#endif /* DEBUG */ diff --git a/lib/libc/db/db.3 b/lib/libc/db/db.3 deleted file mode 100644 index 8d93c38ad4..0000000000 --- a/lib/libc/db/db.3 +++ /dev/null @@ -1,721 +0,0 @@ -.\" Copyright (c) 1990 The Regents of the University of California. -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)db.3 5.16 (Berkeley) 4/2/91 -.\" -.TH DB 3 "April 2, 1991" -.UC 7 -.SH NAME -btree_open, hash_open, recno_open \- database access methods -.SH SYNOPSIS -.nf -.ft B -#include -#include - -DB * -btree_open(const char *file, int flags, int mode, -.ti +5 -const BTREEINFO * openinfo); - -DB * -hash_open(const char *file, int flags, int mode, -.ti +5 -const HASHINFO * openinfo); - -DB * -recno_open(const char *file, int flags, int mode, -.ti +5 -const RECNOINFO * openinfo); -.ft R -.fi -.SH DESCRIPTION -.IR Btree_open , -.IR hash_open , -and -.I recno_open -are access method interfaces to database files in btree, hashed, and -flat-file formats, respectively. -The btree format is a representation of a sorted, balanced tree structure. -The hashed format is an extensible, dynamic hashing scheme. -The flat-file format is a UNIX file with fixed or variable length -lines. -These formats are described in more detail below. -.PP -Access to all file types is based on key/data pairs. -.PP -Each routine opens -.I file -for reading and/or writing. -Databases never intended to be preserved on disk may be created by setting -the file parameter to NULL. -The -.I flags -and -.I mode arguments -are as specified to the -.IR open (2) -routine, however, only the O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC -and O_WRONLY flags are meaningful. -The argument -.I openinfo -is a pointer to an access method specific structure described below. -.PP -The open routines return a pointer to a DB structure on success and NULL -on error. -The DB structure contains at least the following fields: -.sp -.nf -typedef struct { -.RS -int (*close)(const DB *db); -int (*sync)(const DB *db); -int (*del)(const DB *db, const DBT *key, u_int flags); -int (*get)(const DB *db, DBT *key, DBT *data, u_int flags); -int (*put)(const DB *db, const DBT *key, const DBT *data, -.ti +5 -u_int flags); -int (*seq)(const DB *db, DBT *key, DBT *data, u_int flags); -int type; -void *openinfo; -.RE -} DB; -.fi -.PP -The elements of this structure consist of a pointer to an access method -specific structure and a set of routines which perform various functions. -All of these routines take a pointer to a structure as returned by -one of the open routines, one or more pointers to key/data structures, -and, optionally, a flag value. -.TP -openinfo -A pointer to an internal structure specific to the access method. -.TP -type -The type of the underlying access method; either DB_BTREE, DB_HASH -or DB_RECNO. -.TP -close -A pointer to a routine to flush any cached information to disk, free any -allocated resources, and close the database file. -Since key/data pairs may be cached in memory, failing to close the -file with a -.I close -routine may result in inconsistent or lost information. -.I Close -routines return -1 on error (setting -.IR errno ) -and 0 on success. -.TP -del -A pointer to a routine to remove key/data pairs from the database. -.I Delete -routines return -1 on error (setting -.IR errno ), -0 on success, and 1 if the specified -.I key -was not in the file. -.TP -get -A pointer to a routine which is the interface for keyed retrieval from -the database. -The address and length of the data associated with the specified -.I key -are returned in the structure referenced by -.IR data . -.I Get -routines return -1 on error (setting -.IR errno ), -0 on success, and 1 if the -.I key -was not in the file. -.TP -put -A pointer to a routine to store key/data pairs in the database. -.IP -The parameter -.I flag -must be set to one of the following values: -.RS -.TP -R_IAFTER -Append the data immediately after the data referenced by -.IR key , -creating a new key/data pair. -(This implies that the access method is able to create new keys, -i.e. the keys are ordered and independent, for example, record numbers. -Applicable only to the -.B RECNO -access method.) -.TP -R_IBEFORE -Insert the data immediately before the data referenced by -.IR key , -creating a new key/data pair. -(This implies that the access method is able to create new keys, -i.e. the keys are ordered and independent, for example, record numbers. -Applicable only to the -.B RECNO -access method.) -.TP -R_NOOVERWRITE -Enter the new key/data pair only if the key does not previously exist. -.TP -R_PUT -Enter the new key/data pair and replace any previously existing key. -.RE -.IP -.I Put -routines return -1 on error (setting -.IR errno ), -0 on success, and 1 if the R_NOOVERWRITE -.I flag -was set and the key already exists in the file. -.TP -seq -A pointer to a routine which is the interface for sequential -retrieval from the database. -The address and length of the key are returned in the structure -referenced by -.IR key , -and the address and length of the data are returned in the -structure referenced -by -.IR data . -.IP -Sequential key/data pair retrieval may begin at any time, and the -position of the ``cursor'' is not affected by calls to the -.IR del , -.IR get , -.IR put , -or -.I sync -routines. -Modifications to the database during a sequential scan will be reflected -in the scan, i.e. records inserted behind the cursor will not be returned -while records inserted in front of the cursor will be returned. -.IP -The flag value must be set to one of the following values: -.RS -.TP -R_CURSOR -The data associated with the specified key is returned. -This differs from the -.I get -routines in that it sets the ``cursor'' to the location of the -key as well. -(This implies that the access method has a implicit order which does -not change. -Applicable only to the -.B BTREE -and -.B RECNO -access methods.) -.TP -R_FIRST -The first key/data pair of the database is returned. -.TP -R_LAST -The last key/data pair of the database is returned. -(This implies that the access method has a implicit order which does -not change. -Applicable only to the -.B BTREE -and -.B RECNO -access methods.) -.TP -R_NEXT -Retrieve the key/data pair immediately after the key/data pair most recently -retrieved using the -.I seq -routine. -The cursor is moved to the returned key/data pair. -If -.I flag -is set to R_NEXT the first time the -.I seq -routine is called, the first key/data pair of the database is returned. -.TP -R_PREV -Retrieve the key/data pair immediately before the key/data pair most recently -retrieved using the -.I seq -routine. -The cursor is moved to the returned key/data pair. -If -.I flag -is set to R_PREV the first time the -.I seq -routine is called, the last key/data pair of the database is returned. -(This implies that the access method has a implicit order which does -not change. -Applicable only to the -.B BTREE -and -.B RECNO -access methods.) -.RE -.IP -.I Seq -routines return -1 on error (setting -.IR errno ), -0 on success, 1 if there are no more key/data pairs available. -If the -.B RECNO -access method is being used, and if the database file is a character special -file and no complete key/data pairs are currently available, the -.I seq -routines return 2. -.TP -sync -A pointer to a routine to flush any cached information to disk. -If the database is in memory only, the -.I sync -routine has no effect and will always succeed. -.I Sync -routines return -1 on error (setting -.IR errno ) -and 0 on success. -.SH "KEY/DATA PAIRS" -Access to all file types is based on key/data pairs. -Both keys and data are represented by the following data structure: -.PP -typedef struct { -.RS -void *data; -.br -size_t size; -.RE -} DBT; -.PP -The elements of the DBT structure are defined as follows: -.TP -data -A pointer to a byte string. -.TP -size -The length of the byte string. -.PP -Key/data strings must fit into available memory. -.SH BTREE -One of the access methods is a btree: a sorted, balanced tree structure -with associated key/data pairs. -.PP -The access method specific data structure provided to -.I btree_open -is as follows: -.PP -typedef struct { -.RS -u_long flags; -.br -u_int psize; -.br -u_int cachesize; -.br -int (*compare)(const void *, const void *); -.br -int lorder; -.RE -} BTREEINFO; -.PP -The elements of this structure are defined as follows: -.TP -flags -The flag value is specified by -.IR or 'ing -any of the following values: -.RS -.TP -R_DUP -On insertion, -if the key to be inserted already exists, -permit insertion anyway. -This flag permits duplicate keys in the tree. -By default, -duplicates are not permitted, -and attempts to insert them will fail. -Note, the order of retrieval of key/data pairs with duplicate keys is -undefined. -.RE -.TP -cachesize -A suggested maximum size, in bytes, of the memory cache. -Setting this value to zero specifies that an appropriate amount of memory -should be used. -Since every search examines the root page of the tree, caching the most -recently used pages substantially improves access time. -In addition, physical writes are delayed as long as possible, so a moderate -cache can reduce the number of I/O operations significantly. -Obviously, using a cache increases the likelihood of corruption or lost data -if the system crashes while a tree is being modified. -However, caching 10 -pages decreases the creation time of a large tree by between two and three -orders of magnitude. -.TP -compare -Compare is a user defined comparison function. -It must return an integer less than, equal to, or greater than zero if the -first argument is considered to be respectively less than, equal to, or -greater than the second. -The same comparison function must be used on a given tree every time it -is opened. -If no comparison function is specified, -.IR strcmp (3) -is used. -.TP -lorder -The byte order for 4-byte integers in the stored database metadata. -The number should represent the order as an integer; for example, -big endian order would be the number 4,321. -If -.I lorder -is 0 (no order is specified) the current host order is used. -If the file already exists, the specified value is ignored and the -value specified when the tree was created is used. -(Obviously, portability of the data forming the key/data pairs is the -concern of the application program.) -.TP -psize -Page size is the size in bytes of the pages used for nodes in the tree. -If the file already exists, the specified value is ignored and the -value specified when the tree was created is used. -If -.I psize -is zero, an appropriate page size is chosen (based on the system memory -and/or file system constraints), but will never be less than 512 bytes. -.PP -If the pointer to the -.I openinfo -data structure is NULL, the -.I btree_open -routine will use appropriate values. -.PP -If the database file already exists, and the O_TRUNC flag is not specified -to -.IR btree_open , -the parameter -.I psize -ignored. -.PP -Key structures may reference byte strings of slightly less than one-half the -tree's page size only (see -.IR psize ). -Data structures may reference byte strings of essentially unlimited length. -.PP -Searches, insertions, and deletions in a btree will all complete in -O lg N. -.PP -Forward sequential scans of a tree are from the least key to the greatest. -.PP -Space freed up by deleting key/data pairs from a btree is never reclaimed, -although it is normally made available for reuse. -The exception to this is that space occupied by large data items (those -greater than one quarter the size of a page) is neither reclaimed nor reused. -This means that the btree storage structure is grow-only. -The only solutions are to avoid excessive deletions, or to create a fresh -tree periodically from a scan of an existing one. -.SH HASH -One of the access methods is hashed access and storage. -The access method specific data structure provided to -.I hash_open -is as follows: -.sp -typedef struct { -.RS -u_long (*hash)(const void *, const size_t); -.br -u_int cachesize; -.br -int bsize; -.br -int ffactor; -.br -int lorder; -.br -int nelem; -.RE -} HASHINFO; -.PP -The elements of this structure are defined as follows: -.TP -bsize -.I Bsize -defines the hash table bucket size, and is, by default, 256 bytes. -It may be preferable to increase the page size for disk-resident tables and -tables with large data items. -.TP -cachesize -A suggested maximum size, in bytes, of the memory cache. -Setting this value to zero specifies that an appropriate amount of memory -should be used. -.TP -ffactor -.I Ffactor -indicates a desired density within the hash table. -It is an approximation of the number of keys allowed to accumulate in any -one bucket, determining when the hash table grows or shrinks. -The default value is 8. -.TP -hash -.I Hash -is a user defined hash function. -Since no hash function performs equally well on all possible data, the -user may find that the built-in hash function does poorly on a particular -data set. -User specified hash functions must take two arguments (a pointer to a byte -string and a length) and return an u_long to be used as the hash value. -.TP -lorder -The byte order for 4-byte integers in the stored database metadata. -The number should represent the order as an integer; for example, -big endian order would be the number 4,321. -If -.I lorder -is 0 (no order is specified) the current host order is used. -If the file already exists, the specified value is ignored and the -value specified when the tree was created is used. -(Obviously, portability of the data forming the key/data pairs is the -concern of the application program.) -.TP -nelem -.I Nelem -is an estimate of the final size of the hash table. -If not set, the default value is 1. -If not set or set too low, hash tables will expand gracefully as keys -are entered, although a slight performance degradation may be noticed. -.PP -If the pointer to the -.I openinfo -data structure is NULL, the -.I hash_open -routine will use appropriate values. -.PP -If the hash table already exists, and the O_TRUNC flag is not -specified to -.IR hash_open , -the parameters -.IR bsize , -.IR ffactor , -and -.I nelem -are ignored. -.PP -If a hash function is specified, -.I hash_open -will attempt to determine if the hash function specified is the same as -the one with which the database was created, and will fail if it is not. -.PP -Both key and data structures may reference byte strings of essentially -unlimited length. -.PP -Backward compatible interfaces to the routines described in -.IR dbm (3), -.IR hsearch (3), -and -.IR ndbm (3) -are provided, however, these interfaces are not compatible with -previous file formats. -.SH RECNO -One of the access methods is either variable or fixed-length records, -the former delimited by a specific byte value. -The access method specific data structure provided to -.I recno_open -is as follows: -.sp -typedef struct { -.RS -u_long flags; -.br -u_int cachesize; -.br -size_t reclen; -.br -u_char bval; -.RE -} RECNOINFO; -.PP -The elements of this structure are defined as follows: -.TP -flags -The flag value is specified by -.IR or 'ing -any of the following values: -.RS -.TP -R_FIXEDLEN -The records are fixed-length, not byte delimited. -The structure element -.I reclen -specifies the length of the record, and the structure element -.I bval -is used as the pad character. -.TP -R_SNAPSHOT -This flag requires that a snapshot of the file be taken when -.I recno_open -is called, instead of permitting any unmodified records to be -read from the original file. -.RE -.TP -cachesize -A suggested maximum size, in bytes, of the memory cache. -Setting this value to zero specifies that an appropriate amount of memory -should be used. -.TP -reclen -The length of a fixed-length record. -.TP -bval -The delimiting byte to be used to mark the end of a record for -variable-length records, and the pad character for fixed-length -records. -.PP -Variable-length and fixed-length data files require -.I key -structures to reference the following structure: -.sp -typedef struct { -.RS -u_long length; -.br -u_long number; -.br -u_long offset; -.br -u_char valid; -.RE -} RECNOKEY; -.PP -The elements of this structure are defined as follows: -.TP -length -The length of the record. -.TP -number -The record number. -.TP -offset -The offset in the file at which the record is located. -.TP -valid -A flag value which indicates the validity of the other fields in the -structure. -The flag value is specified by -.IR or 'ing -one or more of the following values: -.RS -.TP -R_LENGTH -The record length is valid. -.TP -R_NUMBER -The record number is valid. -.TP -R_OFFSET -The byte offset is valid. -.RE -.PP -If the record retrieval is successful, the record number, byte offset and -record length are set in the RECNOKEY structure referenced by the caller's -.I key -structure. -.PP -Data structures may reference byte strings of essentially unlimited length. -.SH ERRORS -The -.I open -routines may fail and set -.I errno -for any of the errors specified for the library routines -.IR open (2) -and -.IR malloc (3) -or the following: -.TP -[EFTYPE] -A file used by one of the -.I open -routines is incorrectly formatted. -.TP -[EINVAL] -A parameter has been specified (hash function, pad byte etc.) that is -incompatible with the current file specification or there is a mismatch -between the version number of file and the software. -.PP -The -.I close -routines may fail and set -.I errno -for any of the errors specified for the library routines -.IR close (2), -.IR read (2), -.IR write (2), -.IR free (3), -or -.IR fsync (2). -.PP -The -.IR del , -.IR get , -.I put -and -.I seq -routines may fail and set -.I errno -for any of the errors specified for the library routines -.IR read (2), -.IR write (2), -.IR free (3) -or -.IR malloc (3). -.PP -The -.I sync -routines may fail and set -.I errno -for any of the errors specified for the library routine -.IR fsync (2). -.SH "SEE ALSO" -.IR "Dynamic Hash Tables" , -Per-Ake Larson, Communications of the ACM, April 1988. -.br -.IR "A New Hash Package for UNIX" , -Margo Seltzer, USENIX Proceedings, Winter 1991. -.SH BUGS -The typedef DBT is a mnemonic for ``data base thang'', and was used -because noone could think of a reasonable name that wasn't already used. -.PP -None of the access methods provide any form of concurrent access, -locking, or transactions. -.PP -Only big and little endian byte order is supported. diff --git a/lib/libc/db/db/Makefile.inc b/lib/libc/db/db/Makefile.inc new file mode 100644 index 0000000000..59478ba198 --- /dev/null +++ b/lib/libc/db/db/Makefile.inc @@ -0,0 +1,5 @@ +# @(#)Makefile.inc 8.1 (Berkeley) 6/4/93 + +.PATH: ${.CURDIR}/db/db + +SRCS+= db.c diff --git a/lib/libc/db/db/db.c b/lib/libc/db/db/db.c new file mode 100644 index 0000000000..e31d48cef4 --- /dev/null +++ b/lib/libc/db/db/db.c @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)db.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +#define __DBINTERFACE_PRIVATE +#include + +DB * +dbopen(fname, flags, mode, type, openinfo) + const char *fname; + int flags, mode; + DBTYPE type; + const void *openinfo; +{ + switch (type) { + case DB_BTREE: + return (__bt_open(fname, flags, mode, openinfo)); + case DB_HASH: + return (__hash_open(fname, flags, mode, openinfo)); + case DB_RECNO: + return (__rec_open(fname, flags, mode, openinfo)); + } + errno = EINVAL; + return (NULL); +} + +static int +__dberr() +{ + return (RET_ERROR); +} + +/* + * __DBPANIC -- Stop. + * + * Parameters: + * dbp: pointer to the DB structure. + */ +void +__dbpanic(dbp) + DB *dbp; +{ + /* The only thing that can succeed is a close. */ + dbp->del = (int (*)())__dberr; + dbp->fd = (int (*)())__dberr; + dbp->get = (int (*)())__dberr; + dbp->put = (int (*)())__dberr; + dbp->seq = (int (*)())__dberr; + dbp->sync = (int (*)())__dberr; +} diff --git a/lib/libc/db/doc/btree.3.ps b/lib/libc/db/doc/btree.3.ps new file mode 100644 index 0000000000..7ae6af965b --- /dev/null +++ b/lib/libc/db/doc/btree.3.ps @@ -0,0 +1,364 @@ +%!PS-Adobe-3.0 +%%Creator: groff version 1.08 +%%DocumentNeededResources: font Times-Roman +%%+ font Times-Bold +%%+ font Times-Italic +%%DocumentSuppliedResources: procset grops 1.08 0 +%%Pages: 2 +%%PageOrder: Ascend +%%Orientation: Portrait +%%EndComments +%%BeginProlog +%%BeginResource: procset grops 1.08 0 +/setpacking where{ +pop +currentpacking +true setpacking +}if +/grops 120 dict dup begin +/SC 32 def +/A/show load def +/B{0 SC 3 -1 roll widthshow}bind def +/C{0 exch ashow}bind def +/D{0 exch 0 SC 5 2 roll awidthshow}bind def +/E{0 rmoveto show}bind def +/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def +/G{0 rmoveto 0 exch ashow}bind def +/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/I{0 exch rmoveto show}bind def +/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def +/K{0 exch rmoveto 0 exch ashow}bind def +/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/M{rmoveto show}bind def +/N{rmoveto 0 SC 3 -1 roll widthshow}bind def +/O{rmoveto 0 exch ashow}bind def +/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/Q{moveto show}bind def +/R{moveto 0 SC 3 -1 roll widthshow}bind def +/S{moveto 0 exch ashow}bind def +/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/SF{ +findfont exch +[exch dup 0 exch 0 exch neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/MF{ +findfont +[5 2 roll +0 3 1 roll +neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/level0 0 def +/RES 0 def +/PL 0 def +/LS 0 def +/PLG{ +gsave newpath clippath pathbbox grestore +exch pop add exch pop +}bind def +/BP{ +/level0 save def +1 setlinecap +1 setlinejoin +72 RES div dup scale +LS{ +90 rotate +}{ +0 PL translate +}ifelse +1 -1 scale +}bind def +/EP{ +level0 restore +showpage +}bind def +/DA{ +newpath arcn stroke +}bind def +/SN{ +transform +.25 sub exch .25 sub exch +round .25 add exch round .25 add exch +itransform +}bind def +/DL{ +SN +moveto +SN +lineto stroke +}bind def +/DC{ +newpath 0 360 arc closepath +}bind def +/TM matrix def +/DE{ +TM currentmatrix pop +translate scale newpath 0 0 .5 0 360 arc closepath +TM setmatrix +}bind def +/RC/rcurveto load def +/RL/rlineto load def +/ST/stroke load def +/MT/moveto load def +/CL/closepath load def +/FL{ +currentgray exch setgray fill setgray +}bind def +/BL/fill load def +/LW/setlinewidth load def +/RE{ +findfont +dup maxlength 1 index/FontName known not{1 add}if dict begin +{ +1 index/FID ne{def}{pop pop}ifelse +}forall +/Encoding exch def +dup/FontName exch def +currentdict end definefont pop +}bind def +/DEFS 0 def +/EBEGIN{ +moveto +DEFS begin +}bind def +/EEND/end load def +/CNT 0 def +/level1 0 def +/PBEGIN{ +/level1 save def +translate +div 3 1 roll div exch scale +neg exch neg exch translate +0 setgray +0 setlinecap +1 setlinewidth +0 setlinejoin +10 setmiterlimit +[]0 setdash +/setstrokeadjust where{ +pop +false setstrokeadjust +}if +/setoverprint where{ +pop +false setoverprint +}if +newpath +/CNT countdictstack def +userdict begin +/showpage{}def +}bind def +/PEND{ +clear +countdictstack CNT sub{end}repeat +level1 restore +}bind def +end def +/setpacking where{ +pop +setpacking +}if +%%EndResource +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Bold +%%IncludeResource: font Times-Italic +grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL +792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron +/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space +/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft +/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four +/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/A/B/C +/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash +/bracketright/circumflex/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q +/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/tilde/.notdef/quotesinglbase +/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger +/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut +/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash +/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar +/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus +/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu +/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright +/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde +/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute +/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis +/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls +/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute +/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve +/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex +/udieresis/yacute/thorn/ydieresis]def/Times-Italic@0 ENC0/Times-Italic RE +/Times-Bold@0 ENC0/Times-Bold RE/Times-Roman@0 ENC0/Times-Roman RE +%%EndProlog +%%Page: 1 1 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 378.84(BTREE\(3\) BTREE\(3\))72 48 R/F1 9/Times-Bold@0 +SF -.18(NA)72 84 S(ME).18 E F0(btree \255 btree database access method)108 96 Q +F1(SYNOPSIS)72 112.8 Q/F2 10/Times-Bold@0 SF(#include )108 124.8 Q +(#include )-.4 E F1(DESCRIPTION)72 153.6 Q F0 .198 +(The routine)108 165.6 R/F3 10/Times-Italic@0 SF(dbopen)2.698 E F0 .198 +(is the library interf)2.698 F .198(ace to database \214les.)-.1 F .198 +(One of the supported \214le formats is btree \214les.)5.198 F .974 +(The general description of the database access methods is in)108 177.6 R F3 +(dbopen)3.475 E F0 .975(\(3\), this manual page describes only).24 F +(the btree speci\214c information.)108 189.6 Q(The btree data structure is a s\ +orted, balanced tree structure storing associated k)108 206.4 Q -.15(ey)-.1 G +(/data pairs.).15 E .504(The btree access method speci\214c data structure pro) +108 223.2 R .504(vided to)-.15 F F3(dbopen)3.004 E F0 .503 +(is de\214ned in the include \214le as)-.4 F(follo)108 +235.2 Q(ws:)-.25 E(typedef struct {)108 252 Q(u_long \215ags;)144 264 Q +(u_int cachesize;)144 276 Q(inde)144 288 Q(x_t psize;)-.15 E(int lorder;)144 +300 Q(int mink)144 312 Q -.15(ey)-.1 G(page;).15 E +(int \(*compare\)\(const DBT *k)144 324 Q -.15(ey)-.1 G(1, const DBT *k).15 E +-.15(ey)-.1 G(2\);).15 E(int \(*pre\214x\)\(const DBT *k)144 336 Q -.15(ey)-.1 +G(1, const DBT *k).15 E -.15(ey)-.1 G(2\);).15 E 2.5(}B)108 348 S(TREEINFO;) +121.97 348 Q(The elements of this structure are as follo)108 364.8 Q(ws:)-.25 E +14.61(\215ags The)108 381.6 R(\215ag v)2.5 E(alue is speci\214ed by)-.25 E F3 +(or)2.5 E F0('ing an).73 E 2.5(yo)-.15 G 2.5(ft)313.2 381.6 S(he follo)321.81 +381.6 Q(wing v)-.25 E(alues:)-.25 E(R_DUP)144 398.4 Q 1.296(Permit duplicate k) +180 410.4 R -.15(ey)-.1 G 3.796(si).15 G 3.796(nt)275.578 410.4 S 1.296 +(he tree, i.e. permit insertion if the k)287.154 410.4 R 1.596 -.15(ey t)-.1 H +3.796(ob).15 G 3.796(ei)466.878 410.4 S 1.296(nserted already)477.894 410.4 R +-.15(ex)180 422.4 S 1.935(ists in the tree.).15 F 1.935(The def)6.935 F 1.935 +(ault beha)-.1 F(vior)-.2 E 4.435(,a)-.4 G 4.435(sd)358.215 422.4 S 1.935 +(escribed in)371.54 422.4 R F3(dbopen)4.435 E F0 1.935(\(3\), is to o).24 F +-.15(ve)-.15 G 1.935(rwrite a).15 F .148(matching k)180 434.4 R .448 -.15(ey w) +-.1 H .148(hen inserting a ne).15 F 2.649(wk)-.25 G .449 -.15(ey o)329.709 +434.4 T 2.649(rt).15 G 2.649(of)355.407 434.4 S .149(ail if the R_NOO)366.286 +434.4 R(VER)-.5 E .149(WRITE \215ag is speci-)-.55 F 5.972(\214ed. The)180 +446.4 R 3.472(R_DUP \215ag is o)5.972 F -.15(ve)-.15 G 3.472 +(rridden by the R_NOO).15 F(VER)-.5 E 3.471(WRITE \215ag, and if the)-.55 F +(R_NOO)180 458.4 Q(VER)-.5 E .781 +(WRITE \215ag is speci\214ed, attempts to insert duplicate k)-.55 F -.15(ey)-.1 +G 3.282(si).15 G .782(nto the tree will)474.604 458.4 R -.1(fa)180 470.4 S(il.) +.1 E 1.13(If the database contains duplicate k)180 487.2 R -.15(ey)-.1 G 1.129 +(s, the order of retrie).15 F -.25(va)-.25 G 3.629(lo).25 G 3.629(fk)439.644 +487.2 S -.15(ey)451.503 487.2 S 1.129(/data pairs is unde-).15 F .837 +(\214ned if the)180 499.2 R F3 -.1(ge)3.337 G(t).1 E F0 .837 +(routine is used, ho)3.337 F(we)-.25 E -.15(ve)-.25 G -.4(r,).15 G F3(seq)3.737 +E F0 .838(routine calls with the R_CURSOR \215ag set)3.337 F(will al)180 511.2 +Q -.1(wa)-.1 G(ys return the logical `).1 E(`\214rst')-.74 E 2.5('o)-.74 G 2.5 +(fa)333.85 511.2 S .3 -.15(ny g)344.12 511.2 T(roup of duplicate k).15 E -.15 +(ey)-.1 G(s.).15 E(cachesize)108 528 Q 3.056(As)144 540 S .556 +(uggested maximum size \(in bytes\) of the memory cache.)158.166 540 R .555 +(This v)5.556 F .555(alue is)-.25 F F2(only)3.055 E F0(advisory)3.055 E 3.055 +(,a)-.65 G .555(nd the)514.725 540 R .759 +(access method will allocate more memory rather than f)144 552 R 3.259 +(ail. Since)-.1 F -2.15 -.25(ev e)3.259 H .76(ry search e).25 F .76 +(xamines the root)-.15 F .055 +(page of the tree, caching the most recently used pages substantially impro)144 +564 R -.15(ve)-.15 G 2.554(sa).15 G .054(ccess time.)459.578 564 R .054 +(In addi-)5.054 F .661(tion, ph)144 576 R .662(ysical writes are delayed as lo\ +ng as possible, so a moderate cache can reduce the number)-.05 F .601 +(of I/O operations signi\214cantly)144 588 R 5.601(.O)-.65 G -.15(bv)280.744 +588 S(iously).15 E 3.101(,u)-.65 G .601(sing a cache increases \(b)324.995 588 +R .6(ut only increases\) the lik)-.2 F(eli-)-.1 E .19(hood of corruption or lo\ +st data if the system crashes while a tree is being modi\214ed.)144 600 R(If) +5.191 E F3(cac)2.691 E(hesize)-.15 E F0(is)2.691 E 2.5(0\()144 612 S +(no size is speci\214ed\) a def)154.83 612 Q(ault cache is used.)-.1 E 12.95 +(psize P)108 628.8 R .45 +(age size is the size \(in bytes\) of the pages used for nodes in the tree.) +-.15 F .449(The minimum page size is)5.449 F .442 +(512 bytes and the maximum page size is 64K.)144 640.8 R(If)5.442 E F3(psize) +2.942 E F0 .442(is 0 \(no page size is speci\214ed\) a page size)2.942 F +(is chosen based on the underlying \214le system I/O block size.)144 652.8 Q +9.62(lorder The)108 669.6 R 1.597(byte order for inte)4.097 F 1.596 +(gers in the stored database metadata.)-.15 F 1.596 +(The number should represent the)6.596 F .688(order as an inte)144 681.6 R .689 +(ger; for e)-.15 F .689(xample, big endian order w)-.15 F .689 +(ould be the number 4,321.)-.1 F(If)5.689 E F3(lor)3.189 E(der)-.37 E F0 .689 +(is 0 \(no)3.189 F(order is speci\214ed\) the current host order is used.)144 +693.6 Q(1)535 768 Q EP +%%Page: 2 2 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 378.84(BTREE\(3\) BTREE\(3\))72 48 R(mink)108 84 Q -.15 +(ey)-.1 G(page).15 E 1.423(The minimum number of k)144 96 R -.15(ey)-.1 G 3.923 +(sw).15 G 1.422(hich will be stored on an)282.245 96 R 3.922(ys)-.15 G 1.422 +(ingle page.)400.618 96 R 1.422(This v)6.422 F 1.422(alue is used to)-.25 F +.257(determine which k)144 108 R -.15(ey)-.1 G 2.757(sw).15 G .257 +(ill be stored on o)242.001 108 R -.15(ve)-.15 G(r\215o).15 E 2.757(wp)-.25 G +.257(ages, i.e. if a k)348.006 108 R .558 -.15(ey o)-.1 H 2.758(rd).15 G .258 +(ata item is longer than the)435.11 108 R 1.102(pagesize di)144 120 R 1.102 +(vided by the mink)-.25 F -.15(ey)-.1 G 1.102(page v).15 F 1.102 +(alue, it will be stored on o)-.25 F -.15(ve)-.15 G(r\215o).15 E 3.602(wp)-.25 +G 1.102(ages instead of in the)451.164 120 R(page itself.)144 132 Q(If)5 E/F1 +10/Times-Italic@0 SF(mink)2.5 E -.3(ey)-.1 G(pa).3 E -.1(ge)-.1 G F0 +(is 0 \(no minimum number of k)2.6 E -.15(ey)-.1 G 2.5(si).15 G 2.5(ss)392.84 +132 S(peci\214ed\) a v)403.12 132 Q(alue of 2 is used.)-.25 E(compare)108 148.8 +Q .751(Compare is the k)144 160.8 R 1.051 -.15(ey c)-.1 H .751 +(omparison function.).15 F .751(It must return an inte)5.751 F .752 +(ger less than, equal to, or greater)-.15 F .913(than zero if the \214rst k)144 +172.8 R 1.213 -.15(ey a)-.1 H -.18(rg).15 G .913 +(ument is considered to be respecti).18 F -.15(ve)-.25 G .913 +(ly less than, equal to, or greater).15 F .352(than the second k)144 184.8 R +.652 -.15(ey a)-.1 H -.18(rg).15 G 2.852(ument. The).18 F .353 +(same comparison function must be used on a gi)2.852 F -.15(ve)-.25 G 2.853(nt) +.15 G .353(ree e)503.127 184.8 R -.15(ve)-.25 G(ry).15 E .817 +(time it is opened.)144 196.8 R(If)5.817 E F1(compar)3.317 E(e)-.37 E F0 .817 +(is NULL \(no comparison function is speci\214ed\), the k)3.317 F -.15(ey)-.1 G +3.316(sa).15 G .816(re com-)508.364 196.8 R(pared le)144 208.8 Q(xically)-.15 E +2.5(,w)-.65 G(ith shorter k)214.57 208.8 Q -.15(ey)-.1 G 2.5(sc).15 G +(onsidered less than longer k)282.92 208.8 Q -.15(ey)-.1 G(s.).15 E 10.17 +(pre\214x Pre\214x)108 225.6 R .291(is the pre\214x comparison function.)2.791 +F .292(If speci\214ed, this routine must return the number of bytes)5.291 F +.937(of the second k)144 237.6 R 1.237 -.15(ey a)-.1 H -.18(rg).15 G .937 +(ument which are necessary to determine that it is greater than the \214rst k) +.18 F -.15(ey)-.1 G(ar)144 249.6 Q 3.477(gument. If)-.18 F .977(the k)3.477 F +-.15(ey)-.1 G 3.477(sa).15 G .977(re equal, the k)241.898 249.6 R 1.277 -.15 +(ey l)-.1 H .978(ength should be returned.).15 F .978 +(Note, the usefulness of this)5.978 F .558(routine is v)144 261.6 R .558 +(ery data dependent, b)-.15 F .558 +(ut, in some data sets can produce signi\214cantly reduced tree sizes)-.2 F +.354(and search times.)144 273.6 R(If)5.354 E F1(pr)2.854 E(e\214x)-.37 E F0 +.354(is NULL \(no pre\214x function is speci\214ed\),)2.854 F/F2 10 +/Times-Bold@0 SF(and)2.854 E F0 .354(no comparison function)2.854 F .193 +(is speci\214ed, a def)144 285.6 R .193(ault le)-.1 F .193 +(xical comparison routine is used.)-.15 F(If)5.192 E F1(pr)2.692 E(e\214x)-.37 +E F0 .192(is NULL and a comparison rou-)2.692 F +(tine is speci\214ed, no pre\214x comparison is done.)144 297.6 Q .79 +(If the \214le already e)108 314.4 R .79(xists \(and the O_TR)-.15 F .79 +(UNC \215ag is not speci\214ed\), the v)-.4 F .79 +(alues speci\214ed for the parameters)-.25 F +(\215ags, lorder and psize are ignored in f)108 326.4 Q -.2(avo)-.1 G 2.5(ro).2 +G 2.5(ft)284.4 326.4 S(he v)293.01 326.4 Q(alues used when the tree w)-.25 E +(as created.)-.1 E -.15(Fo)108 343.2 S(rw).15 E +(ard sequential scans of a tree are from the least k)-.1 E .3 -.15(ey t)-.1 H +2.5(ot).15 G(he greatest.)348.55 343.2 Q 1.043(Space freed up by deleting k)108 +360 R -.15(ey)-.1 G 1.043(/data pairs from the tree is ne).15 F -.15(ve)-.25 G +3.543(rr).15 G 1.043(eclaimed, although it is normally made)378.686 360 R -.2 +(av)108 372 S 1.394(ailable for reuse.)-.05 F 1.394 +(This means that the btree storage structure is gro)6.394 F(w-only)-.25 E 6.395 +(.T)-.65 G 1.395(he only solutions are to)441.09 372 R -.2(avo)108 384 S(id e) +.2 E(xcessi)-.15 E .3 -.15(ve d)-.25 H +(eletions, or to create a fresh tree periodically from a scan of an e).15 E +(xisting one.)-.15 E .344(Searches, insertions, and deletions in a btree will \ +all complete in O lg base N where base is the a)108 400.8 R -.15(ve)-.2 G .343 +(rage \214ll).15 F -.1(fa)108 412.8 S(ctor).1 E 5.798(.O)-.55 G .799 +(ften, inserting ordered data into btrees results in a lo)146.188 412.8 R 3.299 +<778c>-.25 G .799(ll f)377.505 412.8 R(actor)-.1 E 5.799(.T)-.55 G .799 +(his implementation has been)423.443 412.8 R(modi\214ed to mak)108 424.8 Q 2.5 +(eo)-.1 G(rdered insertion the best case, resulting in a much better than norm\ +al page \214ll f)185.4 424.8 Q(actor)-.1 E(.)-.55 E/F3 9/Times-Bold@0 SF +(SEE ALSO)72 441.6 Q F1(dbopen)108 453.6 Q F0(\(3\),).24 E F1(hash)2.5 E F0 +(\(3\),).28 E F1(mpool)2.5 E F0(\(3\),).51 E F1 -.37(re)2.5 G(cno).37 E F0 +(\(3\)).18 E F1(The Ubiquitous B-tr)108 477.6 Q(ee)-.37 E F0 2.5(,D).18 G +(ouglas Comer)209.47 477.6 Q 2.5(,A)-.4 G(CM Comput. Surv)276.72 477.6 Q 2.5 +(.1)-.65 G(1, 2 \(June 1979\), 121-138.)360.25 477.6 Q F1(Pr)108 501.6 Q 1.588 +(e\214x B-tr)-.37 F(ees)-.37 E F0 4.088(,B).27 G 1.587(ayer and Unterauer) +177.636 501.6 R 4.087(,A)-.4 G 1.587(CM T)270.447 501.6 R 1.587 +(ransactions on Database Systems, V)-.35 F 1.587(ol. 2, 1 \(March 1977\),)-1.29 +F(11-26.)108 513.6 Q F1(The Art of Computer Pr)108 537.6 Q -.1(og)-.45 G -.15 +(ra).1 G(mming V).15 E(ol. 3: Sorting and Sear)-1.11 E -.15(ch)-.37 G(ing).15 E +F0 2.5(,D).22 G(.E. Knuth, 1968, pp 471-480.)382 537.6 Q F3 -.09(BU)72 554.4 S +(GS).09 E F0(Only big and little endian byte order is supported.)108 566.4 Q(2) +535 768 Q EP +%%Trailer +end +%%EOF diff --git a/lib/libc/db/doc/dbopen.3.ps b/lib/libc/db/doc/dbopen.3.ps new file mode 100644 index 0000000000..872c82e771 --- /dev/null +++ b/lib/libc/db/doc/dbopen.3.ps @@ -0,0 +1,496 @@ +%!PS-Adobe-3.0 +%%Creator: groff version 1.08 +%%DocumentNeededResources: font Times-Roman +%%+ font Times-Bold +%%+ font Times-Italic +%%DocumentSuppliedResources: procset grops 1.08 0 +%%Pages: 4 +%%PageOrder: Ascend +%%Orientation: Portrait +%%EndComments +%%BeginProlog +%%BeginResource: procset grops 1.08 0 +/setpacking where{ +pop +currentpacking +true setpacking +}if +/grops 120 dict dup begin +/SC 32 def +/A/show load def +/B{0 SC 3 -1 roll widthshow}bind def +/C{0 exch ashow}bind def +/D{0 exch 0 SC 5 2 roll awidthshow}bind def +/E{0 rmoveto show}bind def +/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def +/G{0 rmoveto 0 exch ashow}bind def +/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/I{0 exch rmoveto show}bind def +/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def +/K{0 exch rmoveto 0 exch ashow}bind def +/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/M{rmoveto show}bind def +/N{rmoveto 0 SC 3 -1 roll widthshow}bind def +/O{rmoveto 0 exch ashow}bind def +/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/Q{moveto show}bind def +/R{moveto 0 SC 3 -1 roll widthshow}bind def +/S{moveto 0 exch ashow}bind def +/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/SF{ +findfont exch +[exch dup 0 exch 0 exch neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/MF{ +findfont +[5 2 roll +0 3 1 roll +neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/level0 0 def +/RES 0 def +/PL 0 def +/LS 0 def +/PLG{ +gsave newpath clippath pathbbox grestore +exch pop add exch pop +}bind def +/BP{ +/level0 save def +1 setlinecap +1 setlinejoin +72 RES div dup scale +LS{ +90 rotate +}{ +0 PL translate +}ifelse +1 -1 scale +}bind def +/EP{ +level0 restore +showpage +}bind def +/DA{ +newpath arcn stroke +}bind def +/SN{ +transform +.25 sub exch .25 sub exch +round .25 add exch round .25 add exch +itransform +}bind def +/DL{ +SN +moveto +SN +lineto stroke +}bind def +/DC{ +newpath 0 360 arc closepath +}bind def +/TM matrix def +/DE{ +TM currentmatrix pop +translate scale newpath 0 0 .5 0 360 arc closepath +TM setmatrix +}bind def +/RC/rcurveto load def +/RL/rlineto load def +/ST/stroke load def +/MT/moveto load def +/CL/closepath load def +/FL{ +currentgray exch setgray fill setgray +}bind def +/BL/fill load def +/LW/setlinewidth load def +/RE{ +findfont +dup maxlength 1 index/FontName known not{1 add}if dict begin +{ +1 index/FID ne{def}{pop pop}ifelse +}forall +/Encoding exch def +dup/FontName exch def +currentdict end definefont pop +}bind def +/DEFS 0 def +/EBEGIN{ +moveto +DEFS begin +}bind def +/EEND/end load def +/CNT 0 def +/level1 0 def +/PBEGIN{ +/level1 save def +translate +div 3 1 roll div exch scale +neg exch neg exch translate +0 setgray +0 setlinecap +1 setlinewidth +0 setlinejoin +10 setmiterlimit +[]0 setdash +/setstrokeadjust where{ +pop +false setstrokeadjust +}if +/setoverprint where{ +pop +false setoverprint +}if +newpath +/CNT countdictstack def +userdict begin +/showpage{}def +}bind def +/PEND{ +clear +countdictstack CNT sub{end}repeat +level1 restore +}bind def +end def +/setpacking where{ +pop +setpacking +}if +%%EndResource +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Bold +%%IncludeResource: font Times-Italic +grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL +792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron +/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space +/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft +/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four +/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/A/B/C +/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash +/bracketright/circumflex/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q +/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/tilde/.notdef/quotesinglbase +/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger +/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut +/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash +/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar +/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus +/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu +/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright +/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde +/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute +/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis +/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls +/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute +/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve +/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex +/udieresis/yacute/thorn/ydieresis]def/Times-Italic@0 ENC0/Times-Italic RE +/Times-Bold@0 ENC0/Times-Bold RE/Times-Roman@0 ENC0/Times-Roman RE +%%EndProlog +%%Page: 1 1 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 169.84(DBOPEN\(3\) 1993 DBOPEN\(3\))72 48 R/F1 9 +/Times-Bold@0 SF -.18(NA)72 84 S(ME).18 E F0 +(dbopen \255 database access methods)108 96 Q F1(SYNOPSIS)72 112.8 Q/F2 10 +/Times-Bold@0 SF(#include )108 124.8 Q(#include )108 +136.8 Q(#include )-.4 E(DB *)108 172.8 Q +(dbopen\(const char *\214le, int \215ags, int mode, DBTYPE type,)108 184.8 Q +(const v)158 196.8 Q(oid *openinf)-.1 E(o\);)-.25 E F1(DESCRIPTION)72 213.6 Q +/F3 10/Times-Italic@0 SF(Dbopen)108 225.6 Q F0 .032(is the library interf)2.532 +F .031(ace to database \214les.)-.1 F .031 +(The supported \214le formats are btree, hashed and UNIX \214le)5.031 F 2.82 +(oriented. The)108 237.6 R .32 +(btree format is a representation of a sorted, balanced tree structure.)2.82 F +.321(The hashed format is an)5.321 F -.15(ex)108 249.6 S .424 +(tensible, dynamic hashing scheme.).15 F .423 +(The \215at-\214le format is a byte stream \214le with \214x)5.423 F .423 +(ed or v)-.15 F .423(ariable length)-.25 F 2.906(records. The)108 261.6 R .407 +(formats and \214le format speci\214c information are described in detail in t\ +heir respecti)2.906 F .707 -.15(ve m)-.25 H(anual).15 E(pages)108 273.6 Q F3 +(btr)2.5 E(ee)-.37 E F0(\(3\),).18 E F3(hash)2.5 E F0(\(3\) and).28 E F3 -.37 +(re)2.5 G(cno).37 E F0(\(3\).).18 E .433(Dbopen opens)108 290.4 R F3(\214le) +2.933 E F0 .433(for reading and/or writing.)2.933 F .433(Files ne)5.433 F -.15 +(ve)-.25 G 2.933(ri).15 G .433(ntended to be preserv)346.737 290.4 R .433 +(ed on disk may be created)-.15 F(by setting the \214le parameter to NULL.)108 +302.4 Q(The)108 319.2 Q F3<8d61>4.661 E(gs)-.1 E F0(and)4.661 E F3 2.161 +(mode ar)4.661 F(guments)-.37 E F0 2.161(are as speci\214ed to the)4.661 F F3 +(open)4.661 E F0 2.162(\(2\) routine, ho).24 F(we)-.25 E -.15(ve)-.25 G 2.962 +-.4(r, o).15 H 2.162(nly the O_CREA).4 F -.74(T,)-1.11 G 2.597 +(O_EXCL, O_EXLOCK, O_RDONL)108 331.2 R 5.177 -1.29(Y, O)-1 H(_RD)1.29 E 2.597 +(WR, O_SHLOCK and O_TR)-.3 F 2.596(UNC \215ags are meaningful.)-.4 F +(\(Note, opening a database \214le O_WR)108 343.2 Q(ONL)-.4 E 2.5(Yi)-1 G 2.5 +(sn)289.62 343.2 S(ot possible.\))301.01 343.2 Q(The)108 360 Q F3(type)5.337 E +F0(ar)5.337 E 2.837(gument is of type DBTYPE \(as de\214ned in the include \214le\) and may be set to)-.4 F +(DB_BTREE, DB_HASH or DB_RECNO.)108 372 Q(The)108 388.8 Q F3(openinfo)2.85 E F0 +(ar)2.85 E .349(gument is a pointer to an access method speci\214c structure d\ +escribed in the access method')-.18 F(s)-.55 E .03(manual page.)108 400.8 R(If) +5.03 E F3(openinfo)2.53 E F0 .031(is NULL, each access method will use def)2.53 +F .031(aults appropriate for the system and the)-.1 F(access method.)108 412.8 +Q F3(Dbopen)108 429.6 Q F0 .416 +(returns a pointer to a DB structure on success and NULL on error)2.917 F 5.416 +(.T)-.55 G .416(he DB structure is de\214ned in)423.21 429.6 R(the include \214le, and contains at least the follo)-.4 E +(wing \214elds:)-.25 E(typedef struct {)108 465.6 Q(DBTYPE type;)144 477.6 Q +(int \(*close\)\(const DB *db\);)144 489.6 Q +(int \(*del\)\(const DB *db, const DBT *k)144 501.6 Q -.15(ey)-.1 G 2.5(,u)-.5 +G(_int \215ags\);)318.92 501.6 Q(int \(*fd\)\(const DB *db\);)144 513.6 Q +(int \(*get\)\(const DB *db, DBT *k)144 525.6 Q -.15(ey)-.1 G 2.5(,D)-.5 G +(BT *data, u_int \215ags\);)297.53 525.6 Q(int \(*put\)\(const DB *db, DBT *k) +144 537.6 Q -.15(ey)-.1 G 2.5(,c)-.5 G(onst DBT *data,)295.31 537.6 Q +(u_int \215ags\);)194 549.6 Q(int \(*sync\)\(const DB *db, u_int \215ags\);)144 +561.6 Q(int \(*seq\)\(const DB *db, DBT *k)144 573.6 Q -.15(ey)-.1 G 2.5(,D)-.5 +G(BT *data, u_int \215ags\);)298.64 573.6 Q 2.5(}D)108 585.6 S(B;)122.52 585.6 +Q .101 +(These elements describe a database type and a set of functions performing v) +108 602.4 R .101(arious actions.)-.25 F .101(These functions)5.101 F(tak)108 +614.4 Q 3.039(eap)-.1 G .539(ointer to a structure as returned by)140.078 614.4 +R F3(dbopen)3.038 E F0 3.038(,a).24 G .538 +(nd sometimes one or more pointers to k)323.196 614.4 R -.15(ey)-.1 G .538 +(/data struc-).15 F(tures and a \215ag v)108 626.4 Q(alue.)-.25 E 16.28 +(type The)108 643.2 R +(type of the underlying access method \(and \214le format\).)2.5 E 12.95 +(close A)108 660 R .988(pointer to a routine to \215ush an)3.488 F 3.489(yc) +-.15 G .989(ached information to disk, free an)293.968 660 R 3.489(ya)-.15 G +.989(llocated resources, and)446.662 660 R .112 +(close the underlying \214le\(s\).)144 672 R .111(Since k)5.112 F -.15(ey)-.1 G +.111(/data pairs may be cached in memory).15 F 2.611(,f)-.65 G .111 +(ailing to sync the \214le)455.666 672 R .494(with a)144 684 R F3(close)2.994 E +F0(or)2.994 E F3(sync)2.994 E F0 .495 +(function may result in inconsistent or lost information.)2.994 F F3(Close) +5.495 E F0 .495(routines return)2.995 F(-1 on error \(setting)144 696 Q F3 +(errno)2.5 E F0 2.5(\)a).18 G(nd 0 on success.)254.43 696 Q 21.28(del A)108 +712.8 R(pointer to a routine to remo)2.5 E .3 -.15(ve k)-.15 H -.15(ey).05 G +(/data pairs from the database.).15 E 209.835(24, May)72 768 R(1)535 768 Q EP +%%Page: 2 2 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 169.84(DBOPEN\(3\) 1993 DBOPEN\(3\))72 48 R +(The parameter)144 84 Q/F1 10/Times-Italic@0 SF<8d61>2.5 E(g)-.1 E F0 +(may be set to the follo)2.5 E(wing v)-.25 E(alue:)-.25 E(R_CURSOR)144 100.8 Q +.289(Delete the record referenced by the cursor)180 112.8 R 5.288(.T)-.55 G +.288(he cursor must ha)363.342 112.8 R .588 -.15(ve p)-.2 H(re).15 E .288 +(viously been initial-)-.25 F(ized.)180 124.8 Q F1(Delete)144 141.6 Q F0 .03 +(routines return -1 on error \(setting)2.53 F F1(errno)2.53 E F0 .03 +(\), 0 on success, and 1 if the speci\214ed).18 F F1 -.1(ke)2.53 G(y)-.2 E F0 +-.1(wa)2.53 G 2.53(sn).1 G .03(ot in)521.91 141.6 R(the \214le.)144 153.6 Q +25.17(fd A)108 170.4 R .451 +(pointer to a routine which returns a \214le descriptor representati)2.951 F +.75 -.15(ve o)-.25 H 2.95(ft).15 G .45(he underlying database.)431.73 170.4 R +(A)5.45 E .942(\214le descriptor referencing the same \214le will be returned \ +to all processes which call)144 182.4 R F1(dbopen)3.442 E F0(with)3.442 E 1.629 +(the same)144 194.4 R F1(\214le)4.129 E F0 4.129(name. This)4.129 F 1.628 +(\214le descriptor may be safely used as a ar)4.128 F 1.628(gument to the)-.18 +F F1(fcntl)4.128 E F0 1.628(\(2\) and).51 F F1(\215oc)144 206.4 Q(k)-.2 E F0 +.425(\(2\) locking functions.).67 F .425 +(The \214le descriptor is not necessarily associated with an)5.425 F 2.925(yo) +-.15 G 2.925(ft)492.7 206.4 S .425(he under)501.735 206.4 R(-)-.2 E .198 +(lying \214les used by the access method.)144 218.4 R .198 +(No \214le descriptor is a)5.198 F -.25(va)-.2 G .198 +(ilable for in memory databases.).25 F F1(Fd)5.198 E F0 +(routines return -1 on error \(setting)144 230.4 Q F1(errno)2.5 E F0 +(\), and the \214le descriptor on success.).18 E 21.28(get A)108 247.2 R +(pointer to a routine which is the interf)2.5 E .001(ace for k)-.1 F -.15(ey) +-.1 G .001(ed retrie).15 F -.25(va)-.25 G 2.501(lf).25 G .001 +(rom the database.)399.755 247.2 R .001(The address and)5.001 F .061 +(length of the data associated with the speci\214ed)144 259.2 R F1 -.1(ke)2.561 +G(y)-.2 E F0 .06(are returned in the structure referenced by)2.561 F F1(data) +2.56 E F0(.).26 E F1(Get)144 271.2 Q F0(routines return -1 on error \(setting) +2.5 E F1(errno)2.5 E F0(\), 0 on success, and 1 if the).18 E F1 -.1(ke)2.5 G(y) +-.2 E F0 -.1(wa)2.5 G 2.5(sn).1 G(ot in the \214le.)471.66 271.2 Q 20.72(put A) +108 288 R(pointer to a routine to store k)2.5 E -.15(ey)-.1 G +(/data pairs in the database.).15 E(The parameter)144 304.8 Q F1<8d61>2.5 E(g) +-.1 E F0(may be set to one of the follo)2.5 E(wing v)-.25 E(alues:)-.25 E +(R_CURSOR)144 321.6 Q .051(Replace the k)180 333.6 R -.15(ey)-.1 G .051 +(/data pair referenced by the cursor).15 F 5.052(.T)-.55 G .052 +(he cursor must ha)393.98 333.6 R .352 -.15(ve p)-.2 H(re).15 E .052 +(viously been)-.25 F(initialized.)180 345.6 Q(R_IAFTER)144 362.4 Q 1.165 +(Append the data immediately after the data referenced by)180 374.4 R F1 -.1 +(ke)3.664 G(y)-.2 E F0 3.664(,c).32 G 1.164(reating a ne)446.758 374.4 R 3.664 +(wk)-.25 G -.15(ey)511.27 374.4 S(/data).15 E(pair)180 386.4 Q 6.065(.T)-.55 G +1.065(he record number of the appended k)209.675 386.4 R -.15(ey)-.1 G 1.065 +(/data pair is returned in the).15 F F1 -.1(ke)3.565 G(y)-.2 E F0(structure.) +3.565 E(\(Applicable only to the DB_RECNO access method.\))180 398.4 Q +(R_IBEFORE)144 415.2 Q 1.293 +(Insert the data immediately before the data referenced by)180 427.2 R F1 -.1 +(ke)3.793 G(y)-.2 E F0 3.793(,c).32 G 1.293(reating a ne)446.371 427.2 R 3.793 +(wk)-.25 G -.15(ey)511.27 427.2 S(/data).15 E(pair)180 439.2 Q 6.54(.T)-.55 G +1.54(he record number of the inserted k)210.15 439.2 R -.15(ey)-.1 G 1.541 +(/data pair is returned in the).15 F F1 -.1(ke)4.041 G(y)-.2 E F0(structure.) +4.041 E(\(Applicable only to the DB_RECNO access method.\))180 451.2 Q(R_NOO) +144 468 Q(VER)-.5 E(WRITE)-.55 E(Enter the ne)180 480 Q 2.5(wk)-.25 G -.15(ey) +242.69 480 S(/data pair only if the k).15 E .3 -.15(ey d)-.1 H(oes not pre).15 +E(viously e)-.25 E(xist.)-.15 E(R_SETCURSOR)144 496.8 Q 1.36(Store the k)180 +508.8 R -.15(ey)-.1 G 1.36(/data pair).15 F 3.86(,s)-.4 G 1.359 +(etting or initializing the position of the cursor to reference it.)283.94 +508.8 R(\(Applicable only to the DB_BTREE and DB_RECNO access methods.\))180 +520.8 Q .563(R_SETCURSOR is a)144 537.6 R -.25(va)-.2 G .564 +(ilable only for the DB_BTREE and DB_RECNO access methods because).25 F +(it implies that the k)144 549.6 Q -.15(ey)-.1 G 2.5(sh).15 G -2.25 -.2(av e) +241.81 549.6 T(an inherent order which does not change.)2.7 E .416 +(R_IAFTER and R_IBEFORE are a)144 566.4 R -.25(va)-.2 G .416 +(ilable only for the DB_RECNO access method because the).25 F(y)-.15 E 1.221 +(each imply that the access method is able to create ne)144 578.4 R 3.722(wk) +-.25 G -.15(ey)385.644 578.4 S 3.722(s. This).15 F 1.222(is only true if the k) +3.722 F -.15(ey)-.1 G 3.722(sa).15 G(re)532.23 578.4 Q +(ordered and independent, record numbers for e)144 590.4 Q(xample.)-.15 E .289 +(The def)144 607.2 R .289(ault beha)-.1 F .289(vior of the)-.2 F F1(put)2.789 E +F0 .289(routines is to enter the ne)2.789 F 2.789(wk)-.25 G -.15(ey)388.998 +607.2 S .288(/data pair).15 F 2.788(,r)-.4 G .288(eplacing an)444.284 607.2 R +2.788(yp)-.15 G(re)503.03 607.2 Q(viously)-.25 E -.15(ex)144 619.2 S(isting k) +.15 E -.15(ey)-.1 G(.)-.5 E F1(Put)144 636 Q F0 .37 +(routines return -1 on error \(setting)2.87 F F1(errno)2.87 E F0 .37 +(\), 0 on success, and 1 if the R_NOO).18 F(VER)-.5 E(WRITE)-.55 E F1<8d61>2.87 +E(g)-.1 E F0 -.1(wa)144 648 S 2.5(ss).1 G(et and the k)165.84 648 Q .3 -.15 +(ey a)-.1 H(lready e).15 E(xists in the \214le.)-.15 E 20.17(seq A)108 664.8 R +.002(pointer to a routine which is the interf)2.502 F .002 +(ace for sequential retrie)-.1 F -.25(va)-.25 G 2.502(lf).25 G .002 +(rom the database.)416.694 664.8 R .001(The address)5.001 F .219 +(and length of the k)144 676.8 R .519 -.15(ey a)-.1 H .219 +(re returned in the structure referenced by).15 F F1 -.1(ke)2.72 G(y)-.2 E F0 +2.72(,a).32 G .22(nd the address and length of)426.42 676.8 R +(the data are returned in the structure referenced by)144 688.8 Q F1(data)2.5 E +F0(.).26 E .937(Sequential k)144 705.6 R -.15(ey)-.1 G .937(/data pair retrie) +.15 F -.25(va)-.25 G 3.437(lm).25 G .936(ay be)289.748 705.6 R .936(gin at an) +-.15 F 3.436(yt)-.15 G .936(ime, and the position of the `)359.292 705.6 R +(`cursor')-.74 E 3.436('i)-.74 G 3.436(sn)519.894 705.6 S(ot)532.22 705.6 Q(af) +144 717.6 Q 1.585(fected by calls to the)-.25 F F1(del)4.085 E F0(,).51 E F1 +-.1(ge)4.085 G(t).1 E F0(,).68 E F1(put)4.086 E F0 4.086(,o).68 G(r)308.452 +717.6 Q F1(sync)4.086 E F0 4.086(routines. Modi\214cations)4.086 F 1.586 +(to the database during a)4.086 F 1.404(sequential scan will be re\215ected in\ + the scan, i.e. records inserted behind the cursor will not be)144 729.6 R +209.835(24, May)72 768 R(2)535 768 Q EP +%%Page: 3 3 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 169.84(DBOPEN\(3\) 1993 DBOPEN\(3\))72 48 R +(returned while records inserted in front of the cursor will be returned.)144 +84 Q(The \215ag v)144 100.8 Q(alue)-.25 E/F1 10/Times-Bold@0 SF(must)2.5 E F0 +(be set to one of the follo)2.5 E(wing v)-.25 E(alues:)-.25 E(R_CURSOR)144 +117.6 Q .523(The data associated with the speci\214ed k)180 129.6 R .824 -.15 +(ey i)-.1 H 3.024(sr).15 G 3.024(eturned. This)367.236 129.6 R(dif)3.024 E .524 +(fers from the)-.25 F/F2 10/Times-Italic@0 SF -.1(ge)3.024 G(t).1 E F0 +(routines)3.024 E 1.143 +(in that it sets or initializes the cursor to the location of the k)180 141.6 R +1.443 -.15(ey a)-.1 H 3.642(sw).15 G 3.642(ell. \(Note,)464.924 141.6 R 1.142 +(for the)3.642 F 1.275(DB_BTREE access method, the returned k)180 153.6 R 1.575 +-.15(ey i)-.1 H 3.775(sn).15 G 1.276(ot necessarily an e)386.425 153.6 R 1.276 +(xact match for the)-.15 F .598(speci\214ed k)180 165.6 R -.15(ey)-.1 G 5.598 +(.T)-.5 G .598(he returned k)246.396 165.6 R .898 -.15(ey i)-.1 H 3.098(st).15 +G .598(he smallest k)325.188 165.6 R .898 -.15(ey g)-.1 H .598 +(reater than or equal to the speci\214ed).15 F -.1(ke)180 177.6 S 1.3 -.65 +(y, p)-.05 H(ermitting partial k).65 E .3 -.15(ey m)-.1 H +(atches and range searches.\)).15 E(R_FIRST)144 194.4 Q 1.043(The \214rst k)180 +206.4 R -.15(ey)-.1 G 1.044(/data pair of the database is returned, and the cu\ +rsor is set or initialized to).15 F(reference it.)180 218.4 Q(R_LAST)144 235.2 +Q .085(The last k)180 247.2 R -.15(ey)-.1 G .085(/data pair of the database is\ + returned, and the cursor is set or initialized to ref-).15 F(erence it.)180 +259.2 Q(\(Applicable only to the DB_BTREE and DB_RECNO access methods.\))5 E +(R_NEXT)144 276 Q(Retrie)180 288 Q .604 -.15(ve t)-.25 H .304(he k).15 F -.15 +(ey)-.1 G .304(/data pair immediately after the cursor).15 F 5.304(.I)-.55 G +2.804(ft)410.622 288 S .305(he cursor is not yet set, this is)419.536 288 R +(the same as the R_FIRST \215ag.)180 300 Q(R_PREV)144 316.8 Q(Retrie)180 328.8 +Q .755 -.15(ve t)-.25 H .455(he k).15 F -.15(ey)-.1 G .455 +(/data pair immediately before the cursor).15 F 5.455(.I)-.55 G 2.955(ft)419.05 +328.8 S .454(he cursor is not yet set, this)428.115 328.8 R .62 +(is the same as the R_LAST \215ag.)180 340.8 R .621 +(\(Applicable only to the DB_BTREE and DB_RECNO)5.621 F(access methods.\))180 +352.8 Q .911(R_LAST and R_PREV are a)144 369.6 R -.25(va)-.2 G .911 +(ilable only for the DB_BTREE and DB_RECNO access methods).25 F(because the)144 +381.6 Q 2.5(ye)-.15 G(ach imply that the k)202.16 381.6 Q -.15(ey)-.1 G 2.5(sh) +.15 G -2.25 -.2(av e)302.18 381.6 T(an inherent order which does not change.) +2.7 E F2(Seq)144 398.4 Q F0 .061(routines return -1 on error \(setting)2.561 F +F2(errno)2.561 E F0 .061(\), 0 on success and 1 if there are no k).18 F -.15 +(ey)-.1 G .061(/data pairs less).15 F .35 +(than or greater than the speci\214ed or current k)144 410.4 R -.15(ey)-.1 G +5.349(.I)-.5 G 2.849(ft)346.467 410.4 S .349 +(he DB_RECNO access method is being used,)355.426 410.4 R .025 +(and if the database \214le is a character special \214le and no complete k)144 +422.4 R -.15(ey)-.1 G .025(/data pairs are currently a).15 F -.25(va)-.2 G(il-) +.25 E(able, the)144 434.4 Q F2(seq)2.5 E F0(routines return 2.)2.5 E 15.17 +(sync A)108 451.2 R .458(pointer to a routine to \215ush an)2.958 F 2.957(yc) +-.15 G .457(ached information to disk.)289.72 451.2 R .457 +(If the database is in memory only)5.457 F(,)-.65 E(the)144 463.2 Q F2(sync)2.5 +E F0(routine has no ef)2.5 E(fect and will al)-.25 E -.1(wa)-.1 G(ys succeed.) +.1 E(The \215ag v)144 480 Q(alue may be set to the follo)-.25 E(wing v)-.25 E +(alue:)-.25 E(R_RECNOSYNC)144 496.8 Q .077(If the DB_RECNO access method is be\ +ing used, this \215ag causes the sync routine to apply)180 508.8 R .75(to the \ +btree \214le which underlies the recno \214le, not the recno \214le itself.)180 +520.8 R .75(\(See the)5.75 F F2(bfname)3.25 E F0(\214eld of the)180 532.8 Q F2 +-.37(re)2.5 G(cno).37 E F0(\(3\) manual page for more information.\)).18 E F2 +(Sync)144 549.6 Q F0(routines return -1 on error \(setting)2.5 E F2(errno)2.5 E +F0 2.5(\)a).18 G(nd 0 on success.)336.91 549.6 Q/F3 9/Times-Bold@0 SF(KEY/D)72 +566.4 Q -1.35 -.855(AT A)-.315 H -.666(PA)3.105 G(IRS).666 E F0 .134 +(Access to all \214le types is based on k)108 578.4 R -.15(ey)-.1 G .134 +(/data pairs.).15 F .134(Both k)5.134 F -.15(ey)-.1 G 2.634(sa).15 G .134 +(nd data are represented by the follo)359.078 578.4 R .135(wing data)-.25 F +(structure:)108 590.4 Q(typedef struct {)108 607.2 Q -.2(vo)144 619.2 S +(id *data;).2 E(size_t size;)144 631.2 Q 2.5(}D)108 643.2 S(BT)122.52 643.2 Q +(;)-.55 E(The elements of the DBT structure are de\214ned as follo)108 660 Q +(ws:)-.25 E 16.84(data A)108 676.8 R(pointer to a byte string.)2.5 E 17.95 +(size The)108 693.6 R(length of the byte string.)2.5 E -2.15 -.25(Ke y)108 +710.4 T .829(and data byte strings may reference strings of essentially unlimi\ +ted length although an)3.579 F 3.328(yt)-.15 G 1.028 -.1(wo o)492.894 710.4 T +3.328(ft).1 G(hem)522.78 710.4 Q 1.133(must \214t into a)108 722.4 R -.25(va) +-.2 G 1.134(ilable memory at the same time.).25 F 1.134 +(It should be noted that the access methods pro)6.134 F 1.134(vide no)-.15 F +209.835(24, May)72 768 R(3)535 768 Q EP +%%Page: 4 4 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 169.84(DBOPEN\(3\) 1993 DBOPEN\(3\))72 48 R +(guarantees about byte string alignment.)108 84 Q/F1 9/Times-Bold@0 SF(ERR)72 +100.8 Q(ORS)-.27 E F0(The)108 112.8 Q/F2 10/Times-Italic@0 SF(dbopen)3.389 E F0 +.889(routine may f)3.389 F .889(ail and set)-.1 F F2(errno)3.388 E F0 .888 +(for an)3.388 F 3.388(yo)-.15 G 3.388(ft)324.376 112.8 S .888 +(he errors speci\214ed for the library routines)333.874 112.8 R F2(open)3.388 E +F0(\(2\)).24 E(and)108 124.8 Q F2(malloc)2.5 E F0(\(3\) or the follo).31 E +(wing:)-.25 E([EFTYPE])108 141.6 Q 2.5<418c>144 153.6 S +(le is incorrectly formatted.)159.28 153.6 Q([EINV)108 170.4 Q(AL])-1.35 E +2.812(Ap)144 182.4 S .313(arameter has been speci\214ed \(hash function, pad b\ +yte etc.\) that is incompatible with the current)159.032 182.4 R .406 +(\214le speci\214cation or which is not meaningful for the function \(for e)144 +194.4 R .405(xample, use of the cursor with-)-.15 F .099 +(out prior initialization\) or there is a mismatch between the v)144 206.4 R .1 +(ersion number of \214le and the softw)-.15 F(are.)-.1 E(The)108 223.2 Q F2 +(close)3.469 E F0 .969(routines may f)3.469 F .969(ail and set)-.1 F F2(errno) +3.469 E F0 .969(for an)3.469 F 3.469(yo)-.15 G 3.469(ft)320.18 223.2 S .969 +(he errors speci\214ed for the library routines)329.759 223.2 R F2(close)3.468 +E F0(\(2\),).18 E F2 -.37(re)108 235.2 S(ad).37 E F0(\(2\),).77 E F2(write)2.5 +E F0(\(2\),).18 E F2(fr)2.5 E(ee)-.37 E F0(\(3\), or).18 E F2(fsync)2.5 E F0 +(\(2\).).31 E(The)108 252 Q F2(del)2.969 E F0(,).51 E F2 -.1(ge)2.969 G(t).1 E +F0(,).68 E F2(put)2.969 E F0(and)2.969 E F2(seq)2.969 E F0 .469(routines may f) +2.969 F .469(ail and set)-.1 F F2(errno)2.97 E F0 .47(for an)2.97 F 2.97(yo) +-.15 G 2.97(ft)377.59 252 S .47(he errors speci\214ed for the library rou-) +386.67 252 R(tines)108 264 Q F2 -.37(re)2.5 G(ad).37 E F0(\(2\),).77 E F2 +(write)2.5 E F0(\(2\),).18 E F2(fr)2.5 E(ee)-.37 E F0(\(3\) or).18 E F2(malloc) +2.5 E F0(\(3\).).31 E(The)108 280.8 Q F2(fd)2.5 E F0(routines will f)2.5 E +(ail and set)-.1 E F2(errno)2.5 E F0(to ENOENT for in memory databases.)2.5 E +(The)108 297.6 Q F2(sync)2.5 E F0(routines may f)2.5 E(ail and set)-.1 E F2 +(errno)2.5 E F0(for an)2.5 E 2.5(yo)-.15 G 2.5(ft)307.71 297.6 S +(he errors speci\214ed for the library routine)316.32 297.6 Q F2(fsync)2.5 E F0 +(\(2\).).31 E F1(SEE ALSO)72 314.4 Q F2(btr)108 326.4 Q(ee)-.37 E F0(\(3\),).18 +E F2(hash)2.5 E F0(\(3\),).28 E F2(mpool)2.5 E F0(\(3\),).51 E F2 -.37(re)2.5 G +(cno).37 E F0(\(3\)).18 E F1 -.09(BU)72 343.2 S(GS).09 E F0 .399 +(The typedef DBT is a mnemonic for `)108 355.2 R .399(`data base thang')-.74 F +.399(', and w)-.74 F .399(as used because noone could think of a rea-)-.1 F +(sonable name that w)108 367.2 Q(asn')-.1 E 2.5(ta)-.18 G(lready used.)216.03 +367.2 Q(The \214le descriptor interf)108 384 Q +(ace is a kluge and will be deleted in a future v)-.1 E(ersion of the interf) +-.15 E(ace.)-.1 E(None of the access methods pro)108 400.8 Q(vide an)-.15 E 2.5 +(yf)-.15 G(orm of concurrent access, locking, or transactions.)275.16 400.8 Q +209.835(24, May)72 768 R(4)535 768 Q EP +%%Trailer +end +%%EOF diff --git a/lib/libc/db/doc/hash.3.ps b/lib/libc/db/doc/hash.3.ps new file mode 100644 index 0000000000..8551b86d8f --- /dev/null +++ b/lib/libc/db/doc/hash.3.ps @@ -0,0 +1,289 @@ +%!PS-Adobe-3.0 +%%Creator: groff version 1.08 +%%DocumentNeededResources: font Times-Roman +%%+ font Times-Bold +%%+ font Times-Italic +%%DocumentSuppliedResources: procset grops 1.08 0 +%%Pages: 2 +%%PageOrder: Ascend +%%Orientation: Portrait +%%EndComments +%%BeginProlog +%%BeginResource: procset grops 1.08 0 +/setpacking where{ +pop +currentpacking +true setpacking +}if +/grops 120 dict dup begin +/SC 32 def +/A/show load def +/B{0 SC 3 -1 roll widthshow}bind def +/C{0 exch ashow}bind def +/D{0 exch 0 SC 5 2 roll awidthshow}bind def +/E{0 rmoveto show}bind def +/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def +/G{0 rmoveto 0 exch ashow}bind def +/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/I{0 exch rmoveto show}bind def +/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def +/K{0 exch rmoveto 0 exch ashow}bind def +/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/M{rmoveto show}bind def +/N{rmoveto 0 SC 3 -1 roll widthshow}bind def +/O{rmoveto 0 exch ashow}bind def +/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/Q{moveto show}bind def +/R{moveto 0 SC 3 -1 roll widthshow}bind def +/S{moveto 0 exch ashow}bind def +/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/SF{ +findfont exch +[exch dup 0 exch 0 exch neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/MF{ +findfont +[5 2 roll +0 3 1 roll +neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/level0 0 def +/RES 0 def +/PL 0 def +/LS 0 def +/PLG{ +gsave newpath clippath pathbbox grestore +exch pop add exch pop +}bind def +/BP{ +/level0 save def +1 setlinecap +1 setlinejoin +72 RES div dup scale +LS{ +90 rotate +}{ +0 PL translate +}ifelse +1 -1 scale +}bind def +/EP{ +level0 restore +showpage +}bind def +/DA{ +newpath arcn stroke +}bind def +/SN{ +transform +.25 sub exch .25 sub exch +round .25 add exch round .25 add exch +itransform +}bind def +/DL{ +SN +moveto +SN +lineto stroke +}bind def +/DC{ +newpath 0 360 arc closepath +}bind def +/TM matrix def +/DE{ +TM currentmatrix pop +translate scale newpath 0 0 .5 0 360 arc closepath +TM setmatrix +}bind def +/RC/rcurveto load def +/RL/rlineto load def +/ST/stroke load def +/MT/moveto load def +/CL/closepath load def +/FL{ +currentgray exch setgray fill setgray +}bind def +/BL/fill load def +/LW/setlinewidth load def +/RE{ +findfont +dup maxlength 1 index/FontName known not{1 add}if dict begin +{ +1 index/FID ne{def}{pop pop}ifelse +}forall +/Encoding exch def +dup/FontName exch def +currentdict end definefont pop +}bind def +/DEFS 0 def +/EBEGIN{ +moveto +DEFS begin +}bind def +/EEND/end load def +/CNT 0 def +/level1 0 def +/PBEGIN{ +/level1 save def +translate +div 3 1 roll div exch scale +neg exch neg exch translate +0 setgray +0 setlinecap +1 setlinewidth +0 setlinejoin +10 setmiterlimit +[]0 setdash +/setstrokeadjust where{ +pop +false setstrokeadjust +}if +/setoverprint where{ +pop +false setoverprint +}if +newpath +/CNT countdictstack def +userdict begin +/showpage{}def +}bind def +/PEND{ +clear +countdictstack CNT sub{end}repeat +level1 restore +}bind def +end def +/setpacking where{ +pop +setpacking +}if +%%EndResource +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Bold +%%IncludeResource: font Times-Italic +grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL +792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron +/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space +/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft +/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four +/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/A/B/C +/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash +/bracketright/circumflex/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q +/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/tilde/.notdef/quotesinglbase +/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger +/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut +/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash +/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar +/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus +/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu +/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright +/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde +/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute +/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis +/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls +/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute +/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve +/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex +/udieresis/yacute/thorn/ydieresis]def/Times-Italic@0 ENC0/Times-Italic RE +/Times-Bold@0 ENC0/Times-Bold RE/Times-Roman@0 ENC0/Times-Roman RE +%%EndProlog +%%Page: 1 1 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 182.62(HASH\(3\) 1993 HASH\(3\))72 48 R/F1 9 +/Times-Bold@0 SF -.18(NA)72 84 S(ME).18 E F0 +(hash \255 hash database access method)108 96 Q F1(SYNOPSIS)72 112.8 Q/F2 10 +/Times-Bold@0 SF(#include )108 124.8 Q(#include )-.4 E F1(DESCRIPTION)72 153.6 Q F0 .29(The routine)108 165.6 R/F3 10 +/Times-Italic@0 SF(dbopen)2.79 E F0 .29(is the library interf)2.79 F .29 +(ace to database \214les.)-.1 F .29 +(One of the supported \214le formats is hash \214les.)5.29 F .974 +(The general description of the database access methods is in)108 177.6 R F3 +(dbopen)3.475 E F0 .975(\(3\), this manual page describes only).24 F +(the hash speci\214c information.)108 189.6 Q(The hash data structure is an e) +108 206.4 Q(xtensible, dynamic hashing scheme.)-.15 E .83 +(The access method speci\214c data structure pro)108 223.2 R .83(vided to)-.15 +F F3(dbopen)3.33 E F0 .83(is de\214ned in the include \214le as fol-)-.4 F(lo)108 235.2 Q(ws:)-.25 E(typedef struct {) +108 259.2 Q(int bsize;)144 271.2 Q(int cachesize;)144 283.2 Q(int f)144 295.2 Q +-.1(fa)-.25 G(ctor;).1 E(u_long \(*hash\)\(const v)144 307.2 Q +(oid *, size_t\);)-.2 E(int lorder;)144 319.2 Q(int nelem;)144 331.2 Q 2.5(}H) +108 343.2 S(ASHINFO;)122.52 343.2 Q +(The elements of this structure are as follo)108 360 Q(ws:)-.25 E(bsize)108 +376.8 Q F3(Bsize)144 376.8 Q F0 1.393(de\214nes the hash table b)3.893 F(uck) +-.2 E 1.393(et size, and is, by def)-.1 F 1.394(ault, 256 bytes.)-.1 F 1.394 +(It may be preferable to)6.394 F +(increase the page size for disk-resident tables and tables with lar)144 388.8 +Q(ge data items.)-.18 E(cachesize)108 405.6 Q 3.16(As)144 417.6 S .66 +(uggested maximum size, in bytes, of the memory cache.)158.27 417.6 R .659 +(This v)5.659 F .659(alue is)-.25 F F2(only)3.159 E F0(advisory)3.159 E 3.159 +(,a)-.65 G .659(nd the)514.621 417.6 R +(access method will allocate more memory rather than f)144 429.6 Q(ail.)-.1 E +-2.1 -.25(ff a)108 446.4 T(ctor).25 E F3(Ffactor)9.7 E F0 .482 +(indicates a desired density within the hash table.)2.981 F .482 +(It is an approximation of the number of)5.482 F -.1(ke)144 458.4 S .429 +(ys allo)-.05 F .429(wed to accumulate in an)-.25 F 2.929(yo)-.15 G .429(ne b) +291.454 458.4 R(uck)-.2 E .429(et, determining when the hash table gro)-.1 F +.428(ws or shrinks.)-.25 F(The def)144 470.4 Q(ault v)-.1 E(alue is 8.)-.25 E +(hash)108 487.2 Q F3(Hash)144 487.2 Q F0 .1(is a user de\214ned hash function.) +2.6 F .1(Since no hash function performs equally well on all possible)5.1 F +.924(data, the user may \214nd that the b)144 499.2 R .923 +(uilt-in hash function does poorly on a particular data set.)-.2 F(User)5.923 E +1.408(speci\214ed hash functions must tak)144 511.2 R 3.909(et)-.1 G 1.609 -.1 +(wo a)293.431 511.2 T -.18(rg).1 G 1.409 +(uments \(a pointer to a byte string and a length\) and).18 F +(return an u_long to be used as the hash v)144 523.2 Q(alue.)-.25 E 9.62 +(lorder The)108 540 R 1.597(byte order for inte)4.097 F 1.596 +(gers in the stored database metadata.)-.15 F 1.596 +(The number should represent the)6.596 F .688(order as an inte)144 552 R .689 +(ger; for e)-.15 F .689(xample, big endian order w)-.15 F .689 +(ould be the number 4,321.)-.1 F(If)5.689 E F3(lor)3.189 E(der)-.37 E F0 .689 +(is 0 \(no)3.189 F .822(order is speci\214ed\) the current host order is used.) +144 564 R .822(If the)5.822 F .822(\214le already e)5.822 F .821 +(xists, the speci\214ed v)-.15 F .821(alue is)-.25 F(ignored and the v)144 576 +Q(alue speci\214ed when the tree w)-.25 E(as created is used.)-.1 E(nelem)108 +592.8 Q F3(Nelem)144 592.8 Q F0 .701 +(is an estimate of the \214nal size of the hash table.)3.2 F .701 +(If not set or set too lo)5.701 F 2.001 -.65(w, h)-.25 H .701(ash tables will) +.65 F -.15(ex)144 604.8 S .448(pand gracefully as k).15 F -.15(ey)-.1 G 2.948 +(sa).15 G .448(re entered, although a slight performance de)255.912 604.8 R +.447(gradation may be noticed.)-.15 F(The def)144 616.8 Q(ault v)-.1 E +(alue is 1.)-.25 E .79(If the \214le already e)108 633.6 R .79 +(xists \(and the O_TR)-.15 F .79(UNC \215ag is not speci\214ed\), the v)-.4 F +.79(alues speci\214ed for the parameters)-.25 F(bsize, f)108 645.6 Q -.1(fa) +-.25 G(ctor).1 E 2.5(,l)-.4 G(order and nelem are ignored and the v)167.23 +645.6 Q(alues speci\214ed when the tree w)-.25 E(as created are used.)-.1 E +1.232(If a hash function is speci\214ed,)108 662.4 R F3(hash_open)3.731 E F0 +1.231(will attempt to determine if the hash function speci\214ed is the)3.731 F +(same as the one with which the database w)108 674.4 Q(as created, and will f) +-.1 E(ail if it is not.)-.1 E(Backw)108 691.2 Q .861(ard compatible interf)-.1 +F .861(aces to the routines described in)-.1 F F3(dbm)3.362 E F0 .862 +(\(3\), and).32 F F3(ndbm)3.362 E F0 .862(\(3\) are pro).32 F .862(vided, ho) +-.15 F(we)-.25 E -.15(ve)-.25 G -.4(r,).15 G(these interf)108 703.2 Q +(aces are not compatible with pre)-.1 E(vious \214le formats.)-.25 E 214.835 +(6, June)72 768 R(1)535 768 Q EP +%%Page: 2 2 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 182.62(HASH\(3\) 1993 HASH\(3\))72 48 R/F1 9 +/Times-Bold@0 SF(SEE ALSO)72 84 Q/F2 10/Times-Italic@0 SF(btr)108 96 Q(ee)-.37 +E F0(\(3\),).18 E F2(dbopen)2.5 E F0(\(3\),).24 E F2(mpool)2.5 E F0(\(3\),).51 +E F2 -.37(re)2.5 G(cno).37 E F0(\(3\)).18 E F2(Dynamic Hash T)108 108 Q(ables) +-.92 E F0 2.5(,P).27 G(er)206.79 108 Q(-Ak)-.2 E 2.5(eL)-.1 G +(arson, Communications of the A)242.86 108 Q(CM, April 1988.)-.4 E F2 2.5(AN) +108 120 S .3 -.15(ew H)123.28 120 T(ash P).15 E(ac)-.8 E(ka)-.2 E .2 -.1(ge f) +-.1 H(or UNIX).1 E F0 2.5(,M).94 G(ar)248.41 120 Q(go Seltzer)-.18 E 2.5(,U)-.4 +G(SENIX Proceedings, W)308.09 120 Q(inter 1991.)-.4 E F1 -.09(BU)72 136.8 S(GS) +.09 E F0(Only big and little endian byte order is supported.)108 148.8 Q +214.835(6, June)72 768 R(2)535 768 Q EP +%%Trailer +end +%%EOF diff --git a/lib/libc/db/hash/hash.ps b/lib/libc/db/doc/hash.ps similarity index 100% rename from lib/libc/db/hash/hash.ps rename to lib/libc/db/doc/hash.ps diff --git a/lib/libc/db/doc/mpool.3.ps b/lib/libc/db/doc/mpool.3.ps new file mode 100644 index 0000000000..f595480d6b --- /dev/null +++ b/lib/libc/db/doc/mpool.3.ps @@ -0,0 +1,318 @@ +%!PS-Adobe-3.0 +%%Creator: groff version 1.08 +%%DocumentNeededResources: font Times-Roman +%%+ font Times-Bold +%%+ font Times-Italic +%%DocumentSuppliedResources: procset grops 1.08 0 +%%Pages: 2 +%%PageOrder: Ascend +%%Orientation: Portrait +%%EndComments +%%BeginProlog +%%BeginResource: procset grops 1.08 0 +/setpacking where{ +pop +currentpacking +true setpacking +}if +/grops 120 dict dup begin +/SC 32 def +/A/show load def +/B{0 SC 3 -1 roll widthshow}bind def +/C{0 exch ashow}bind def +/D{0 exch 0 SC 5 2 roll awidthshow}bind def +/E{0 rmoveto show}bind def +/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def +/G{0 rmoveto 0 exch ashow}bind def +/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/I{0 exch rmoveto show}bind def +/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def +/K{0 exch rmoveto 0 exch ashow}bind def +/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/M{rmoveto show}bind def +/N{rmoveto 0 SC 3 -1 roll widthshow}bind def +/O{rmoveto 0 exch ashow}bind def +/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/Q{moveto show}bind def +/R{moveto 0 SC 3 -1 roll widthshow}bind def +/S{moveto 0 exch ashow}bind def +/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/SF{ +findfont exch +[exch dup 0 exch 0 exch neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/MF{ +findfont +[5 2 roll +0 3 1 roll +neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/level0 0 def +/RES 0 def +/PL 0 def +/LS 0 def +/PLG{ +gsave newpath clippath pathbbox grestore +exch pop add exch pop +}bind def +/BP{ +/level0 save def +1 setlinecap +1 setlinejoin +72 RES div dup scale +LS{ +90 rotate +}{ +0 PL translate +}ifelse +1 -1 scale +}bind def +/EP{ +level0 restore +showpage +}bind def +/DA{ +newpath arcn stroke +}bind def +/SN{ +transform +.25 sub exch .25 sub exch +round .25 add exch round .25 add exch +itransform +}bind def +/DL{ +SN +moveto +SN +lineto stroke +}bind def +/DC{ +newpath 0 360 arc closepath +}bind def +/TM matrix def +/DE{ +TM currentmatrix pop +translate scale newpath 0 0 .5 0 360 arc closepath +TM setmatrix +}bind def +/RC/rcurveto load def +/RL/rlineto load def +/ST/stroke load def +/MT/moveto load def +/CL/closepath load def +/FL{ +currentgray exch setgray fill setgray +}bind def +/BL/fill load def +/LW/setlinewidth load def +/RE{ +findfont +dup maxlength 1 index/FontName known not{1 add}if dict begin +{ +1 index/FID ne{def}{pop pop}ifelse +}forall +/Encoding exch def +dup/FontName exch def +currentdict end definefont pop +}bind def +/DEFS 0 def +/EBEGIN{ +moveto +DEFS begin +}bind def +/EEND/end load def +/CNT 0 def +/level1 0 def +/PBEGIN{ +/level1 save def +translate +div 3 1 roll div exch scale +neg exch neg exch translate +0 setgray +0 setlinecap +1 setlinewidth +0 setlinejoin +10 setmiterlimit +[]0 setdash +/setstrokeadjust where{ +pop +false setstrokeadjust +}if +/setoverprint where{ +pop +false setoverprint +}if +newpath +/CNT countdictstack def +userdict begin +/showpage{}def +}bind def +/PEND{ +clear +countdictstack CNT sub{end}repeat +level1 restore +}bind def +end def +/setpacking where{ +pop +setpacking +}if +%%EndResource +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Bold +%%IncludeResource: font Times-Italic +grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL +792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron +/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space +/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft +/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four +/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/A/B/C +/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash +/bracketright/circumflex/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q +/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/tilde/.notdef/quotesinglbase +/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger +/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut +/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash +/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar +/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus +/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu +/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright +/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde +/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute +/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis +/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls +/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute +/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve +/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex +/udieresis/yacute/thorn/ydieresis]def/Times-Italic@0 ENC0/Times-Italic RE +/Times-Bold@0 ENC0/Times-Bold RE/Times-Roman@0 ENC0/Times-Roman RE +%%EndProlog +%%Page: 1 1 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 174.84(MPOOL\(3\) 1991 MPOOL\(3\))72 48 R/F1 9 +/Times-Bold@0 SF -.18(NA)72 84 S(ME).18 E F0(mpool \255 shared memory b)108 96 +Q(uf)-.2 E(fer pool)-.25 E F1(SYNOPSIS)72 112.8 Q/F2 10/Times-Bold@0 SF +(#include )-.4 E(#include )108 136.8 Q(MPOOL *)108 +160.8 Q(mpool_open \(DBT *k)108 172.8 Q(ey)-.1 E 2.5(,i)-.55 G +(nt fd, pgno_t pagesize, pgno_t maxcache\);)216.25 172.8 Q -.1(vo)108 196.8 S +(id).1 E(mpool_\214lter \(MPOOL *mp, v)108 208.8 Q(oid \(*pgin\)\(v)-.1 E +(oid *, pgno_t, v)-.1 E(oid *\),)-.1 E -.1(vo)158 220.8 S(id \(*pgout\)\(v).1 E +(oid *, pgno_t, v)-.1 E(oid *\), v)-.1 E(oid *pgcookie\);)-.1 E -.1(vo)108 +244.8 S(id *).1 E(mpool_new \(MPOOL *mp, pgno_t *pgnoaddr\);)108 256.8 Q -.1 +(vo)108 280.8 S(id *).1 E(mpool_get \(MPOOL *mp, pgno_t pgno, u_int \215ags\);) +108 292.8 Q(int)108 316.8 Q(mpool_put \(MPOOL *mp, v)108 328.8 Q(oid *pgaddr) +-.1 E 2.5(,u)-.92 G(_int \215ags\);)290.62 328.8 Q(int)108 352.8 Q +(mpool_sync \(MPOOL *mp\);)108 364.8 Q(int)108 388.8 Q +(mpool_close \(MPOOL *mp\);)108 400.8 Q F1(DESCRIPTION)72 417.6 Q/F3 10 +/Times-Italic@0 SF(Mpool)108 429.6 Q F0 1.013(is the library interf)3.513 F +1.013(ace intended to pro)-.1 F 1.013(vide page oriented b)-.15 F(uf)-.2 E +1.012(fer management of \214les.)-.25 F 1.012(The b)6.012 F(uf)-.2 E(fers)-.25 +E(may be shared between processes.)108 441.6 Q .416(The function)108 458.4 R F3 +(mpool_open)2.916 E F0 .417(initializes a memory pool.)2.917 F(The)5.417 E F3 +-.1(ke)2.917 G(y)-.2 E F0(ar)2.917 E .417(gument is the byte string used to ne) +-.18 F(gotiate)-.15 E .697(between multiple processes wishing to share b)108 +470.4 R(uf)-.2 E 3.196(fers. If)-.25 F .696(the \214le b)3.196 F(uf)-.2 E .696 +(fers are mapped in shared memory)-.25 F 3.196(,a)-.65 G(ll)534.44 470.4 Q .894 +(processes using the same k)108 482.4 R 1.194 -.15(ey w)-.1 H .894 +(ill share the b).15 F(uf)-.2 E 3.394(fers. If)-.25 F F3 -.1(ke)3.394 G(y)-.2 E +F0 .895(is NULL, the b)3.395 F(uf)-.2 E .895(fers are mapped into pri)-.25 F +-.25(va)-.25 G(te).25 E(memory)108 494.4 Q 5.116(.T)-.65 G(he)154.406 494.4 Q +F3(fd)2.616 E F0(ar)2.616 E .115(gument is a \214le descriptor for the underly\ +ing \214le, which must be seekable.)-.18 F(If)5.115 E F3 -.1(ke)2.615 G(y)-.2 E +F0 .115(is non-)2.615 F(NULL and matches a \214le already being mapped, the)108 +506.4 Q F3(fd)2.5 E F0(ar)2.5 E(gument is ignored.)-.18 E(The)108 523.2 Q F3 +(pa)3.328 E -.1(ge)-.1 G(size).1 E F0(ar)3.329 E .829 +(gument is the size, in bytes, of the pages into which the \214le is brok)-.18 +F .829(en up.)-.1 F(The)5.829 E F3(maxcac)3.329 E(he)-.15 E F0(ar)108 535.2 Q +.153(gument is the maximum number of pages from the underlying \214le to cache\ + at an)-.18 F 2.653(yo)-.15 G .153(ne time.)451.308 535.2 R .153(This v)5.153 F +.153(alue is)-.25 F .099(not relati)108 547.2 R .399 -.15(ve t)-.25 H 2.599(ot) +.15 G .099(he number of processes which share a \214le')168.727 547.2 R 2.6(sb) +-.55 G(uf)350.39 547.2 Q .1(fers, b)-.25 F .1(ut will be the lar)-.2 F .1 +(gest v)-.18 F .1(alue speci\214ed by)-.25 F(an)108 559.2 Q 2.5(yo)-.15 G 2.5 +(ft)129.79 559.2 S(he processes sharing the \214le.)138.4 559.2 Q(The)108 576 Q +F3(mpool_\214lter)3.254 E F0 .754(function is intended to mak)3.254 F 3.254(et) +-.1 G .754(ransparent input and output processing of the pages possi-)301.778 +576 R 3.095(ble. If)108 588 R(the)3.095 E F3(pgin)3.095 E F0 .596 +(function is speci\214ed, it is called each time a b)3.095 F(uf)-.2 E .596 +(fer is read into the memory pool from the)-.25 F .125(backing \214le.)108 600 +R .125(If the)5.125 F F3(pgout)2.625 E F0 .125 +(function is speci\214ed, it is called each time a b)2.625 F(uf)-.2 E .125 +(fer is written into the backing \214le.)-.25 F .276 +(Both functions are are called with the)108 612 R F3(pgcookie)2.777 E F0 +(pointer)2.777 E 2.777(,t)-.4 G .277 +(he page number and a pointer to the page to being)337.27 612 R +(read or written.)108 624 Q .124(The function)108 640.8 R F3(mpool_ne)2.624 E +(w)-.15 E F0(tak)2.624 E .123(es an MPOOL pointer and an address as ar)-.1 F +2.623(guments. If)-.18 F 2.623(an)2.623 G .623 -.25(ew p)457.568 640.8 T .123 +(age can be allo-).25 F .944(cated, a pointer to the page is returned and the \ +page number is stored into the)108 652.8 R F3(pgnoaddr)3.445 E F0 3.445 +(address. Other)3.445 F(-)-.2 E(wise, NULL is returned and errno is set.)108 +664.8 Q 1.167(The function)108 681.6 R F3(mpool_g)3.667 E(et)-.1 E F0(tak)3.667 +E 1.167(es a MPOOL pointer and a page number as ar)-.1 F 3.666(guments. If)-.18 +F 1.166(the page e)3.666 F 1.166(xists, a)-.15 F .686 +(pointer to the page is returned.)108 693.6 R .687 +(Otherwise, NULL is returned and errno is set.)5.686 F .687 +(The \215ags parameter is not)5.687 F(currently used.)108 705.6 Q 1.463 +(The function)108 722.4 R F3(mpool_put)3.963 E F0 1.462 +(unpins the page referenced by)3.962 F F3(pgaddr)3.962 E F0(.).73 E F3(Pgaddr) +6.462 E F0 1.462(must be an address pre)3.962 F(viously)-.25 E 197.615 +(12, September)72 768 R(1)535 768 Q EP +%%Page: 2 2 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 174.84(MPOOL\(3\) 1991 MPOOL\(3\))72 48 R(returned by) +108 84 Q/F1 10/Times-Italic@0 SF(mpool_g)2.5 E(et)-.1 E F0(or)2.5 E F1 +(mpool_ne)2.5 E(w)-.15 E F0 5(.T).31 G(he \215ag v)271.65 84 Q +(alue is speci\214ed by)-.25 E F1(or)2.5 E F0('ing an).73 E 2.5(yo)-.15 G 2.5 +(ft)434.74 84 S(he follo)443.35 84 Q(wing v)-.25 E(alues:)-.25 E(MPOOL_DIR)108 +100.8 Q(TY)-.6 E +(The page has been modi\214ed and needs to be written to the backing \214le.) +144 112.8 Q F1(Mpool_put)108 129.6 Q F0 +(returns 0 on success and -1 if an error occurs.)2.5 E .247(The function)108 +146.4 R F1(mpool_sync)2.747 E F0 .247(writes all modi\214ed pages associated w\ +ith the MPOOL pointer to the backing \214le.)2.747 F F1(Mpool_sync)108 158.4 Q +F0(returns 0 on success and -1 if an error occurs.)2.5 E(The)108 175.2 Q F1 +(mpool_close)2.698 E F0 .198(function free')2.698 F 2.698(su)-.55 G 2.698(pa) +245.432 175.2 S .498 -.15(ny a)257.57 175.2 T .198 +(llocated memory associated with the memory pool cookie.).15 F(Modi-)5.197 E +(\214ed pages are)108 187.2 Q/F2 10/Times-Bold@0 SF(not)2.5 E F0 +(written to the backing \214le.)2.5 E F1(Mpool_close)5 E F0 +(returns 0 on success and -1 if an error occurs.)2.5 E/F3 9/Times-Bold@0 SF +(ERR)72 204 Q(ORS)-.27 E F0(The)108 216 Q F1(mpool_open)2.938 E F0 .438 +(function may f)2.938 F .438(ail and set)-.1 F F1(errno)2.938 E F0 .438(for an) +2.938 F 2.938(yo)-.15 G 2.938(ft)344.87 216 S .439 +(he errors speci\214ed for the library routine)353.918 216 R F1(mal-)2.939 E +(loc)108 228 Q F0(\(3\).).31 E(The)108 244.8 Q F1(mpool_g)2.5 E(et)-.1 E F0 +(function may f)2.5 E(ail and set)-.1 E F1(errno)2.5 E F0(for the follo)2.5 E +(wing:)-.25 E([EINV)108 261.6 Q 29.98(AL] The)-1.35 F(requested record doesn') +2.5 E 2.5(te)-.18 G(xist.)305.96 261.6 Q(The)108 278.4 Q F1(mpool_ne)4.073 E(w) +-.15 E F0(and)4.073 E F1(mpool_g)4.073 E(et)-.1 E F0 1.573(functions may f) +4.073 F 1.573(ail and set)-.1 F F1(errno)4.073 E F0 1.573(for an)4.073 F 4.073 +(yo)-.15 G 4.073(ft)421.336 278.4 S 1.573(he errors speci\214ed for the)431.519 +278.4 R(library routines)108 290.4 Q F1 -.37(re)2.5 G(ad).37 E F0(\(2\)).77 E +F1 2.5(,w).54 G(rite)214.48 290.4 Q F0(\(2\)).18 E F1(,).54 E F0(and)2.5 E F1 +(malloc)2.5 E F0(\(3\).).31 E(The)108 307.2 Q F1(mpool_sync)4.287 E F0 1.787 +(function may f)4.287 F 1.787(ail and set)-.1 F F1(errno)4.288 E F0 1.788 +(for an)4.288 F 4.288(yo)-.15 G 4.288(ft)356.694 307.2 S 1.788 +(he errors speci\214ed for the library routine)367.092 307.2 R F1(write)108 +319.2 Q F0(\(2\).).18 E(The)108 336 Q F1(mpool_close)4.125 E F0 1.624 +(function may f)4.125 F 1.624(ail and set)-.1 F F1(errno)4.124 E F0 1.624 +(for an)4.124 F 4.124(yo)-.15 G 4.124(ft)357.842 336 S 1.624 +(he errors speci\214ed for the library routine)368.076 336 R F1(fr)108 348 Q +(ee)-.37 E F0(\(3\).).18 E F3(SEE ALSO)72 364.8 Q F1(dbopen)108 376.8 Q F0 +(\(3\),).24 E F1(btr)2.5 E(ee)-.37 E F0(\(3\),).18 E F1(hash)2.5 E F0(\(3\),) +.28 E F1 -.37(re)2.5 G(cno).37 E F0(\(3\)).18 E 197.615(12, September)72 768 R +(2)535 768 Q EP +%%Trailer +end +%%EOF diff --git a/lib/libc/db/doc/recno.3.ps b/lib/libc/db/doc/recno.3.ps new file mode 100644 index 0000000000..6ea5344151 --- /dev/null +++ b/lib/libc/db/doc/recno.3.ps @@ -0,0 +1,317 @@ +%!PS-Adobe-3.0 +%%Creator: groff version 1.08 +%%DocumentNeededResources: font Times-Roman +%%+ font Times-Bold +%%+ font Times-Italic +%%DocumentSuppliedResources: procset grops 1.08 0 +%%Pages: 2 +%%PageOrder: Ascend +%%Orientation: Portrait +%%EndComments +%%BeginProlog +%%BeginResource: procset grops 1.08 0 +/setpacking where{ +pop +currentpacking +true setpacking +}if +/grops 120 dict dup begin +/SC 32 def +/A/show load def +/B{0 SC 3 -1 roll widthshow}bind def +/C{0 exch ashow}bind def +/D{0 exch 0 SC 5 2 roll awidthshow}bind def +/E{0 rmoveto show}bind def +/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def +/G{0 rmoveto 0 exch ashow}bind def +/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/I{0 exch rmoveto show}bind def +/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def +/K{0 exch rmoveto 0 exch ashow}bind def +/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/M{rmoveto show}bind def +/N{rmoveto 0 SC 3 -1 roll widthshow}bind def +/O{rmoveto 0 exch ashow}bind def +/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/Q{moveto show}bind def +/R{moveto 0 SC 3 -1 roll widthshow}bind def +/S{moveto 0 exch ashow}bind def +/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/SF{ +findfont exch +[exch dup 0 exch 0 exch neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/MF{ +findfont +[5 2 roll +0 3 1 roll +neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/level0 0 def +/RES 0 def +/PL 0 def +/LS 0 def +/PLG{ +gsave newpath clippath pathbbox grestore +exch pop add exch pop +}bind def +/BP{ +/level0 save def +1 setlinecap +1 setlinejoin +72 RES div dup scale +LS{ +90 rotate +}{ +0 PL translate +}ifelse +1 -1 scale +}bind def +/EP{ +level0 restore +showpage +}bind def +/DA{ +newpath arcn stroke +}bind def +/SN{ +transform +.25 sub exch .25 sub exch +round .25 add exch round .25 add exch +itransform +}bind def +/DL{ +SN +moveto +SN +lineto stroke +}bind def +/DC{ +newpath 0 360 arc closepath +}bind def +/TM matrix def +/DE{ +TM currentmatrix pop +translate scale newpath 0 0 .5 0 360 arc closepath +TM setmatrix +}bind def +/RC/rcurveto load def +/RL/rlineto load def +/ST/stroke load def +/MT/moveto load def +/CL/closepath load def +/FL{ +currentgray exch setgray fill setgray +}bind def +/BL/fill load def +/LW/setlinewidth load def +/RE{ +findfont +dup maxlength 1 index/FontName known not{1 add}if dict begin +{ +1 index/FID ne{def}{pop pop}ifelse +}forall +/Encoding exch def +dup/FontName exch def +currentdict end definefont pop +}bind def +/DEFS 0 def +/EBEGIN{ +moveto +DEFS begin +}bind def +/EEND/end load def +/CNT 0 def +/level1 0 def +/PBEGIN{ +/level1 save def +translate +div 3 1 roll div exch scale +neg exch neg exch translate +0 setgray +0 setlinecap +1 setlinewidth +0 setlinejoin +10 setmiterlimit +[]0 setdash +/setstrokeadjust where{ +pop +false setstrokeadjust +}if +/setoverprint where{ +pop +false setoverprint +}if +newpath +/CNT countdictstack def +userdict begin +/showpage{}def +}bind def +/PEND{ +clear +countdictstack CNT sub{end}repeat +level1 restore +}bind def +end def +/setpacking where{ +pop +setpacking +}if +%%EndResource +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Bold +%%IncludeResource: font Times-Italic +grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL +792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron +/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space +/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft +/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four +/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/A/B/C +/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash +/bracketright/circumflex/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q +/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/tilde/.notdef/quotesinglbase +/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger +/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut +/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash +/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar +/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus +/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu +/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright +/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde +/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute +/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis +/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls +/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute +/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve +/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex +/udieresis/yacute/thorn/ydieresis]def/Times-Italic@0 ENC0/Times-Italic RE +/Times-Bold@0 ENC0/Times-Bold RE/Times-Roman@0 ENC0/Times-Roman RE +%%EndProlog +%%Page: 1 1 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 175.95(RECNO\(3\) 1993 RECNO\(3\))72 48 R/F1 9 +/Times-Bold@0 SF -.18(NA)72 84 S(ME).18 E F0 +(recno \255 record number database access method)108 96 Q F1(SYNOPSIS)72 112.8 +Q/F2 10/Times-Bold@0 SF(#include )108 124.8 Q(#include )-.4 E F1(DESCRIPTION)72 153.6 Q F0 1.158(The routine)108 165.6 R/F3 +10/Times-Italic@0 SF(dbopen)3.658 E F0 1.158(is the library interf)3.658 F +1.158(ace to database \214les.)-.1 F 1.157 +(One of the supported \214le formats is record)6.158 F 1.159(number \214les.) +108 177.6 R 1.159(The general description of the database access methods is in) +6.159 F F3(dbopen)3.66 E F0 1.16(\(3\), this manual page).24 F +(describes only the recno speci\214c information.)108 189.6 Q 1.944 +(The record number data structure is either v)108 206.4 R 1.944 +(ariable or \214x)-.25 F 1.944 +(ed-length records stored in a \215at-\214le format,)-.15 F 2.04 +(accessed by the logical record number)108 218.4 R 7.04(.T)-.55 G 2.04(he e) +286.31 218.4 R 2.04(xistence of record number \214v)-.15 F 4.54(ei)-.15 G 2.04 +(mplies the e)442.1 218.4 R 2.04(xistence of)-.15 F .876 +(records one through four)108 230.4 R 3.376(,a)-.4 G .875 +(nd the deletion of record number one causes record number \214v)219.684 230.4 +R 3.375(et)-.15 G 3.375(ob)489.93 230.4 S 3.375(er)503.305 230.4 S(enum-)514.45 +230.4 Q .282(bered to record number four)108 242.4 R 2.782(,a)-.4 G 2.782(sw) +231.19 242.4 S .283(ell as the cursor)245.082 242.4 R 2.783(,i)-.4 G 2.783(fp) +316.633 242.4 S .283(ositioned after record number one, to shift do)327.746 +242.4 R .283(wn one)-.25 F(record.)108 254.4 Q .373 +(The recno access method speci\214c data structure pro)108 271.2 R .373 +(vided to)-.15 F F3(dbopen)2.873 E F0 .373(is de\214ned in the include \214le as)-.4 F(follo)108 283.2 Q(ws:)-.25 E(typedef struct {)108 +300 Q(u_char b)144 312 Q -.25(va)-.15 G(l;).25 E(u_int cachesize;)144 324 Q +(inde)144 336 Q(x_t psize;)-.15 E(u_long \215ags;)144 348 Q(int lorder;)144 360 +Q(size_t reclen;)144 372 Q(char *bfname;)144 384 Q 2.5(}R)108 396 S(ECNOINFO;) +121.97 396 Q(The elements of this structure are de\214ned as follo)108 412.8 Q +(ws:)-.25 E -.15(bv)108 429.6 S 16.68(al The)-.1 F .182 +(delimiting byte to be used to mark the end of a record for v)2.682 F .183 +(ariable-length records, and the pad)-.25 F .809(character for \214x)144 441.6 +R .809(ed-length records.)-.15 F .809(If no v)5.809 F .809 +(alue is speci\214ed, ne)-.25 F .809(wlines \(`)-.25 F(`\\n')-.74 E .808 +('\) are used to mark the)-.74 F(end of v)144 453.6 Q +(ariable-length records and \214x)-.25 E +(ed-length records are padded with spaces.)-.15 E(cachesize)108 470.4 Q 3.159 +(As)144 482.4 S .659(uggested maximum size, in bytes, of the memory cache.) +158.269 482.4 R .66(This v)5.659 F .66(alue is)-.25 F F2(only)3.16 E F0 +(advisory)3.16 E 3.16(,a)-.65 G .66(nd the)514.62 482.4 R +(access method will allocate more memory rather than f)144 494.4 Q(ail.)-.1 E +12.95(psize The)108 511.2 R .715 +(recno access method stores the in-memory copies of its records in a btree.) +3.216 F .715(This v)5.715 F .715(alue is the)-.25 F +(size \(in bytes\) of the pages used for nodes in that tree.)144 523.2 Q(See)5 +E F3(btr)2.5 E(ee)-.37 E F0(\(3\) for more information.).18 E 3.51(bfname The) +108 540 R .505 +(recno access method stores the in-memory copies of its records in a btree.) +3.005 F .506(If bfname is non-)5.506 F .065(NULL, it speci\214es the name of t\ +he btree \214le, as if speci\214ed as the \214le name for a dbopen of a btree) +144 552 R(\214le.)144 564 Q 14.61(\215ags The)108 580.8 R(\215ag v)2.5 E +(alue is speci\214ed by)-.25 E F3(or)2.5 E F0('ing an).73 E 2.5(yo)-.15 G 2.5 +(ft)313.2 580.8 S(he follo)321.81 580.8 Q(wing v)-.25 E(alues:)-.25 E +(R_FIXEDLEN)144 597.6 Q .962(The records are \214x)180 609.6 R .963 +(ed-length, not byte delimited.)-.15 F .963(The structure element)5.963 F F3 +-.37(re)3.463 G(clen).37 E F0(speci\214es)3.463 E +(the length of the record, and the structure element)180 621.6 Q F3(bval)2.5 E +F0(is used as the pad character)2.5 E(.)-.55 E(R_NOKEY)144 638.4 Q 2.34 +(In the interf)180 650.4 R 2.34(ace speci\214ed by)-.1 F F3(dbopen)4.84 E F0 +4.84(,t).24 G 2.34(he sequential record retrie)344.98 650.4 R -.25(va)-.25 G +4.84<6c8c>.25 G 2.34(lls in both the)478.25 650.4 R(caller')180 662.4 Q 3.556 +(sk)-.55 G 1.357 -.15(ey a)217.336 662.4 T 1.057(nd data structures.).15 F +1.057(If the R_NOKEY \215ag is speci\214ed, the)6.057 F F3(cur)3.557 E(sor)-.1 +E F0(routines)3.557 E .029(are not required to \214ll in the k)180 674.4 R .329 +-.15(ey s)-.1 H 2.529(tructure. This).15 F .028(permits applications to retrie) +2.529 F .328 -.15(ve r)-.25 H .028(ecords at).15 F +(the end of \214les without reading all of the interv)180 686.4 Q +(ening records.)-.15 E(R_SN)144 703.2 Q(APSHO)-.35 E(T)-.4 E .964 +(This \215ag requires that a snapshot of the \214le be tak)180 715.2 R .965 +(en when)-.1 F F3(dbopen)3.465 E F0 .965(is called, instead of)3.465 F +(permitting an)180 727.2 Q 2.5(yu)-.15 G +(nmodi\214ed records to be read from the original \214le.)245.96 727.2 Q +209.835(16, May)72 768 R(1)535 768 Q EP +%%Page: 2 2 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 175.95(RECNO\(3\) 1993 RECNO\(3\))72 48 R 9.62 +(lorder The)108 84 R 1.597(byte order for inte)4.097 F 1.596 +(gers in the stored database metadata.)-.15 F 1.596 +(The number should represent the)6.596 F .688(order as an inte)144 96 R .689 +(ger; for e)-.15 F .689(xample, big endian order w)-.15 F .689 +(ould be the number 4,321.)-.1 F(If)5.689 E/F1 10/Times-Italic@0 SF(lor)3.189 E +(der)-.37 E F0 .689(is 0 \(no)3.189 F +(order is speci\214ed\) the current host order is used.)144 108 Q 9.07 +(reclen The)108 124.8 R(length of a \214x)2.5 E(ed-length record.)-.15 E .972 +(The data part of the k)108 141.6 R -.15(ey)-.1 G .971(/data pair used by the \ +recno access method is the same as other access methods.).15 F .198(The k)108 +153.6 R .498 -.15(ey i)-.1 H 2.698(sd).15 G(if)157.504 153.6 Q 2.698 +(ferent. The)-.25 F F1(data)2.698 E F0 .198(\214eld of the k)2.698 F .499 -.15 +(ey s)-.1 H .199(hould be a pointer to a memory location of type).15 F F1 -.37 +(re)2.699 G(cno_t).37 E F0 2.699(,a).68 G(s)536.11 153.6 Q .506 +(de\214ned in the include \214le.)-.4 F .506 +(This type is normally the lar)5.506 F .506(gest unsigned inte)-.18 F .506 +(gral type a)-.15 F -.25(va)-.2 G .505(ilable to the).25 F 2.5 +(implementation. The)108 177.6 R F1(size)2.5 E F0(\214eld of the k)2.5 E .3 +-.15(ey s)-.1 H(hould be the size of that type.).15 E .064(In the interf)108 +194.4 R .064(ace speci\214ed by)-.1 F F1(dbopen)2.564 E F0 2.564(,u).24 G .064 +(sing the)261.544 194.4 R F1(put)2.564 E F0(interf)2.564 E .064 +(ace to create a ne)-.1 F 2.564(wr)-.25 G .065 +(ecord will cause the creation of)414.436 194.4 R .755(multiple, empty records\ + if the record number is more than one greater than the lar)108 206.4 R .754 +(gest record currently in)-.18 F(the database.)108 218.4 Q/F2 9/Times-Bold@0 SF +(SEE ALSO)72 235.2 Q F1(dbopen)108 247.2 Q F0(\(3\),).24 E F1(hash)2.5 E F0 +(\(3\),).28 E F1(mpool)2.5 E F0(\(3\),).51 E F1 -.37(re)2.5 G(cno).37 E F0 +(\(3\)).18 E F1 2.754(Document Pr)108 271.2 R 2.754 +(ocessing in a Relational Database System)-.45 F F0 5.255(,M).32 G 2.755 +(ichael Stonebrak)362.13 271.2 R(er)-.1 E 5.255(,H)-.4 G 2.755(eidi Stettner) +454.06 271.2 R 5.255(,J)-.4 G(oseph)516.67 271.2 Q +(Kalash, Antonin Guttman, Nadene L)108 283.2 Q +(ynn, Memorandum No. UCB/ERL M82/32, May 1982.)-.55 E F2 -.09(BU)72 300 S(GS) +.09 E F0(Only big and little endian byte order is supported.)108 312 Q 209.835 +(16, May)72 768 R(2)535 768 Q EP +%%Trailer +end +%%EOF diff --git a/lib/libc/db/hash/Makefile.inc b/lib/libc/db/hash/Makefile.inc index 9e74d43a6c..cac96feb02 100644 --- a/lib/libc/db/hash/Makefile.inc +++ b/lib/libc/db/hash/Makefile.inc @@ -1,5 +1,6 @@ -# @(#)Makefile.inc 5.1 (Berkeley) 2/12/91 +# @(#)Makefile.inc 8.1 (Berkeley) 6/4/93 .PATH: ${.CURDIR}/db/hash -SRCS+= bigkey.c buf.c dynahash.c hfunc.c hsearch.c log2.c ndbm.c page.c +SRCS+= hash.c hash_bigkey.c hash_buf.c hash_func.c hash_log2.c \ + hash_page.c hsearch.c ndbm.c diff --git a/lib/libc/db/hash/README b/lib/libc/db/hash/README index 971d037446..f29ccf7e1b 100644 --- a/lib/libc/db/hash/README +++ b/lib/libc/db/hash/README @@ -1,45 +1,7 @@ -# @(#)README 5.3 (Berkeley) 2/22/91 +# @(#)README 8.1 (Berkeley) 6/4/93 This package implements a superset of the hsearch and dbm/ndbm libraries. -Contents: - -Hashing Package: - dynahash.c - page.c - buf.c - big.c - hfunc.c - log2.c - - hash.h - page.h - db.h - -Backward Compatibility Routines: - ndbm.c - ndbm.h - hsearch.c - search.h - -Misc: - byte_order.c - -Compatibility routines: - mkstemp.c - - ansi.h - bsd.h - cdefs.h - endian.h - posix.h - unistd.h - -DIFFS: - These are diffs since the date of the file (i.e. a file labeled - DIFFS.2.12 are the diffs since the 2.12 version on arpa). The - date of the DIFF file indicates when those diffs were installed. - Test Programs: All test programs which need key/data pairs expect them entered with key and data on separate lines @@ -82,30 +44,11 @@ Test Programs: NOTES: -If you are not running a 4.3BSD-Reno or later system, you may need to use -some of the compatibility files provided. The files are as follows: - - mkstemp.c Mkstemp/mktemp library routine. - - ansi.h Map bcopy and friends to memcpy and friends. - bsd.h Map various new BSD things to old things. - cdefs.h Handle the function prototypes in other include files. - endian.h Handle byte ordering. Be sure to set BYTE_ORDER in - endian.h appropriately for your machine. If you don't - know what "endian" your machine is, compile - byte_order.c and run it. It should tell you. - posix.h Map various POSIX 1003.1 things to old-style things. - unistd.h POSIX 1003.1 definitions. - -If you are not running on the current BSD release (4.3BSD-Reno+), you will -need to include bsd.h in hash.h. Depending on what system you are running -on, you will need to add the other compatibility h files in hash.h. - The file search.h is provided for using the hsearch compatible interface on BSD systems. On System V derived systems, search.h should appear in /usr/include. -The man page db.3 explains the interface to the hashing system. +The man page ../man/db.3 explains the interface to the hashing system. The file hash.ps is a postscript copy of a paper explaining the history, implementation, and performance of the hash package. diff --git a/lib/libc/db/hash/bigkey.c b/lib/libc/db/hash/bigkey.c deleted file mode 100644 index 8c4276701c..0000000000 --- a/lib/libc/db/hash/bigkey.c +++ /dev/null @@ -1,672 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Margo Seltzer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)bigkey.c 5.5 (Berkeley) 3/12/91"; -#endif /* LIBC_SCCS and not lint */ - -/****************************************************************************** - -PACKAGE: hash - -DESCRIPTION: - Big key/data handling for the hashing package. - -ROUTINES: - External - __big_keydata - __big_split - __big_insert - __big_return - __big_delete - __find_last_page - Internal - collect_key - collect_data -******************************************************************************/ -/* Includes */ -#include -#include -#include -#include -#include -#include -#include -#include "hash.h" -#include "page.h" - -/* Externals */ -/* buf.c */ -extern BUFHEAD *__get_buf(); - -/* dynahash.c */ -extern u_int call_hash(); - -/* page.c */ -extern BUFHEAD *__add_ovflpage(); - -/* My externals */ -extern int __big_keydata(); -extern int __big_split(); -extern int __big_insert(); -extern int __big_return(); -extern int __big_delete(); -extern u_short __find_last_page(); -extern int __find_bigpair(); - -/* My internals */ -static int collect_key(); -static int collect_data(); - -#ifdef HASH_STATISTICS -extern long hash_accesses, hash_collisions, hash_expansions, hash_overflows; -#endif -/* -Big_insert - -You need to do an insert and the key/data pair is too big -0 ==> OK --1 ==> ERROR -*/ -extern int -__big_insert ( bufp, key, val ) -BUFHEAD *bufp; -DBT *key, *val; -{ - char *cp = bufp->page; /* Character pointer of p */ - register u_short *p = (u_short *)cp; - char *key_data, *val_data; - int key_size, val_size; - int n; - u_short space, move_bytes, off; - - key_data = (char *)key->data; - key_size = key->size; - val_data = (char *)val->data; - val_size = val->size; - - /* First move the Key */ - for ( space = FREESPACE(p) - BIGOVERHEAD; - key_size; - space = FREESPACE(p) - BIGOVERHEAD ) { - move_bytes = MIN(space, key_size); - off = OFFSET(p) - move_bytes; - bcopy (key_data, cp+off, move_bytes ); - key_size -= move_bytes; - key_data += move_bytes; - n = p[0]; - p[++n] = off; - p[0] = ++n; - FREESPACE(p) = off - PAGE_META(n); - OFFSET(p) = off; - p[n] = PARTIAL_KEY; - bufp = __add_ovflpage(bufp); - if ( !bufp ) { - return(-1); - } - n = p[0]; - if ( !key_size ) { - if ( FREESPACE(p) ) { - move_bytes = MIN (FREESPACE(p), val_size); - off = OFFSET(p) - move_bytes; - p[n] = off; - bcopy ( val_data, cp + off, move_bytes ); - val_data += move_bytes; - val_size -= move_bytes; - p[n-2] = FULL_KEY_DATA; - FREESPACE(p) = FREESPACE(p) - move_bytes; - OFFSET(p) = off; - } - else p[n-2] = FULL_KEY; - } - p = (u_short *)bufp->page; - cp = bufp->page; - bufp->flags |= BUF_MOD; - } - - /* Now move the data */ - for ( space = FREESPACE(p) - BIGOVERHEAD; - val_size; - space = FREESPACE(p) - BIGOVERHEAD ) { - move_bytes = MIN(space, val_size); - /* - Here's the hack to make sure that if the data ends - on the same page as the key ends, FREESPACE is - at least one - */ - if ( space == val_size && val_size == val->size ) { - move_bytes--; - } - off = OFFSET(p) - move_bytes; - bcopy (val_data, cp+off, move_bytes ); - val_size -= move_bytes; - val_data += move_bytes; - n = p[0]; - p[++n] = off; - p[0] = ++n; - FREESPACE(p) = off - PAGE_META(n); - OFFSET(p) = off; - if ( val_size ) { - p[n] = FULL_KEY; - bufp = __add_ovflpage (bufp); - if ( !bufp ) { - return(-1); - } - cp = bufp->page; - p = (u_short *)cp; - } else { - p[n] = FULL_KEY_DATA; - } - bufp->flags |= BUF_MOD; - } - return(0); -} - -/* - Called when bufp's page contains a partial key (index should be 1) - - All pages in the big key/data pair except bufp are freed. We cannot - free bufp because the page pointing to it is lost and we can't - get rid of its pointer. - - Returns 0 => OK - -1 => ERROR -*/ -extern int -__big_delete (bufp, ndx) -BUFHEAD *bufp; -int ndx; -{ - register BUFHEAD *rbufp = bufp; - register BUFHEAD *last_bfp = NULL; - char *cp; - u_short *bp = (u_short *)bufp->page; - u_short *xbp; - u_short pageno = 0; - u_short off, free_sp; - int key_done = 0; - int n; - - while (!key_done || (bp[2] != FULL_KEY_DATA)) { - if ( bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA ) key_done = 1; - - /* - If there is freespace left on a FULL_KEY_DATA page, - then the data is short and fits entirely on this - page, and this is the last page. - */ - if ( bp[2] == FULL_KEY_DATA && FREESPACE(bp) ) break; - pageno = bp[bp[0]-1]; - rbufp->flags |= BUF_MOD; - rbufp = __get_buf ( pageno, rbufp, 0 ); - if ( last_bfp ) __free_ovflpage(last_bfp); - last_bfp = rbufp; - if ( !rbufp ) return(-1); /* Error */ - bp = (u_short *)rbufp->page; - } - - /* - If we get here then rbufp points to the last page of - the big key/data pair. Bufp points to the first - one -- it should now be empty pointing to the next - page after this pair. Can't free it because we don't - have the page pointing to it. - */ - - /* This is information from the last page of the pair */ - n = bp[0]; - pageno = bp[n-1]; - - /* Now, bp is the first page of the pair */ - bp = (u_short *)bufp->page; - if ( n > 2 ) { - /* There is an overflow page */ - bp[1] = pageno; - bp[2] = OVFLPAGE; - bufp->ovfl = rbufp->ovfl; - } else { - /* This is the last page */ - bufp->ovfl = NULL; - } - n -= 2; - bp[0] = n; - FREESPACE(bp) = hashp->BSIZE - PAGE_META(n); - OFFSET(bp) = hashp->BSIZE - 1; - - bufp->flags |= BUF_MOD; - if ( rbufp ) __free_ovflpage(rbufp); - if ( last_bfp != rbufp ) __free_ovflpage(last_bfp); - - hashp->NKEYS--; - return(0); -} - -/* - 0 = key not found - -1 = get next overflow page - -2 means key not found and this is big key/data - -3 error -*/ -extern int -__find_bigpair(bufp, ndx, key, size ) -BUFHEAD *bufp; -int ndx; -char *key; -int size; -{ - register u_short *bp = (u_short *)bufp->page; - register char *p = bufp->page; - int ksize = size; - char *kkey = key; - u_short bytes; - - - for ( bytes = hashp->BSIZE - bp[ndx]; - bytes <= size && bp[ndx+1] == PARTIAL_KEY; - bytes = hashp->BSIZE - bp[ndx] ) { - - if ( bcmp ( p+bp[ndx], kkey, bytes ))return(-2); - kkey += bytes; - ksize -= bytes; - bufp = __get_buf ( bp[ndx+2], bufp, 0 ); - if ( !bufp ) { - return(-3); - } - p = bufp->page; - bp = (u_short *)p; - ndx = 1; - } - - if ( (bytes != ksize) || bcmp ( p+bp[ndx], kkey, bytes )) { -#ifdef HASH_STATISTICS - hash_collisions++; -#endif - return(-2); - } - else return (ndx); -} - - -/* - Given the buffer pointer of the first overflow page of a big pair, - find the end of the big pair - - This will set bpp to the buffer header of the last page of the big pair. - It will return the pageno of the overflow page following the last page of - the pair; 0 if there isn't any (i.e. big pair is the last key in the - bucket) -*/ -extern u_short -__find_last_page ( bpp ) -BUFHEAD **bpp; -{ - int n; - u_short pageno; - BUFHEAD *bufp = *bpp; - u_short *bp = (u_short *)bufp->page; - - while ( 1 ) { - n = bp[0]; - - /* - This is the last page if: - the tag is FULL_KEY_DATA and either - only 2 entries - OVFLPAGE marker is explicit - there is freespace on the page - */ - if ( bp[2] == FULL_KEY_DATA && - ((n == 2) || (bp[n] == OVFLPAGE) || (FREESPACE(bp)) ) ) break; - - pageno = bp[n-1]; - bufp = __get_buf ( pageno, bufp, 0 ); - if ( !bufp ) return (0); /* Need to indicate an error! */ - bp = (u_short *)bufp->page; - } - - *bpp = bufp; - if ( bp[0] > 2 ) return ( bp[3] ); - else return(0); -} - - -/* - Return the data for the key/data pair - that begins on this page at this index - (index should always be 1) -*/ -extern int -__big_return ( bufp, ndx, val, set_current ) -BUFHEAD *bufp; -int ndx; -DBT *val; -int set_current; -{ - BUFHEAD *save_p; - u_short save_addr; - u_short *bp = (u_short *)bufp->page; - u_short off, len; - char *cp, *tp; - - while ( bp[ndx+1] == PARTIAL_KEY ) { - bufp = __get_buf ( bp[bp[0]-1], bufp, 0 ); - if ( !bufp ) return(-1); - bp = (u_short *)bufp->page; - ndx = 1; - } - - if ( bp[ndx+1] == FULL_KEY ) { - bufp = __get_buf ( bp[bp[0]-1], bufp, 0 ); - if ( !bufp ) return(-1); - bp = (u_short *)bufp->page; - save_p = bufp; - save_addr = save_p->addr; - off = bp[1]; - len = 0; - } else if (!FREESPACE(bp)) { - /* - This is a hack. We can't distinguish between - FULL_KEY_DATA that contains complete data or - incomplete data, so we require that if the - data is complete, there is at least 1 byte - of free space left. - */ - off = bp[bp[0]]; - len = bp[1] - off; - save_p = bufp; - save_addr = bufp->addr; - bufp = __get_buf ( bp[bp[0]-1], bufp, 0 ); - if ( !bufp ) return(-1); - bp = (u_short *)bufp->page; - } else { - /* The data is all on one page */ - tp = (char *)bp; - off = bp[bp[0]]; - val->data = (u_char *)tp + off; - val->size = bp[1] - off; - if ( set_current ) { - if ( bp[0] == 2 ) { /* No more buckets in chain */ - hashp->cpage = NULL; - hashp->cbucket++; - hashp->cndx=1; - } else { - hashp->cpage = __get_buf ( bp[bp[0]-1], bufp, 0 ); - if ( !hashp->cpage )return(-1); - hashp->cndx = 1; - if ( !((u_short *)hashp->cpage->page)[0] ) { - hashp->cbucket++; - hashp->cpage = NULL; - } - } - } - return(0); - } - - val->size = collect_data ( bufp, len, set_current ); - if ( val->size == -1 ) { - return(-1); - } - if ( save_p->addr != save_addr ) { - /* We are pretty short on buffers */ - errno = EINVAL; /* OUT OF BUFFERS */ - return(-1); - } - bcopy ( (save_p->page)+off, hashp->tmp_buf, len ); - val->data = (u_char *)hashp->tmp_buf; - return(0); -} - -/* - Count how big the total datasize is by - recursing through the pages. Then allocate - a buffer and copy the data as you recurse up. -*/ -static int -collect_data ( bufp, len, set ) -BUFHEAD *bufp; -int len; -int set; -{ - register char *p = bufp->page; - register u_short *bp = (u_short *)p; - u_short save_addr; - int mylen, totlen; - BUFHEAD *xbp; - - mylen = hashp->BSIZE - bp[1]; - save_addr = bufp->addr; - - if ( bp[2] == FULL_KEY_DATA ) { /* End of Data */ - totlen = len + mylen; - if ( hashp->tmp_buf ) free (hashp->tmp_buf); - hashp->tmp_buf = (char *)malloc ( totlen ); - if ( !hashp->tmp_buf ) { - return(-1); - } - if ( set ) { - hashp->cndx = 1; - if ( bp[0] == 2 ) { /* No more buckets in chain */ - hashp->cpage = NULL; - hashp->cbucket++; - } else { - hashp->cpage = __get_buf ( bp[bp[0]-1], bufp, 0 ); - if (!hashp->cpage) { - return(-1); - } else if ( !((u_short *)hashp->cpage->page)[0] ) { - hashp->cbucket++; - hashp->cpage = NULL; - } - } - } - } else { - xbp = __get_buf ( bp[bp[0]-1], bufp, 0 ); - if ( !xbp || ((totlen = collect_data ( xbp, len + mylen, set )) < 1) ) { - return(-1); - } - } - if ( bufp->addr != save_addr ) { - errno = EINVAL; /* Out of buffers */ - return(-1); - } - bcopy ( (bufp->page) + bp[1], &hashp->tmp_buf[len], mylen ); - return ( totlen ); -} - -/* - Fill in the key and data - for this big pair -*/ -extern int -__big_keydata ( bufp, ndx, key, val, set ) -BUFHEAD *bufp; -int ndx; -DBT *key, *val; -int set; -{ - key->size = collect_key ( bufp, 0, val, set ); - if ( key->size == -1 ) { - return (-1); - } - key->data = (u_char *)hashp->tmp_key; - return(0); -} - -/* - Count how big the total key size is by - recursing through the pages. Then collect - the data, allocate a buffer and copy the key as - you recurse up. -*/ -static int -collect_key ( bufp, len, val, set ) -BUFHEAD *bufp; -int len; -DBT *val; -int set; -{ - char *p = bufp->page; - u_short *bp = (u_short *)p; - u_short save_addr; - int mylen, totlen; - BUFHEAD *xbp; - - mylen = hashp->BSIZE - bp[1]; - - save_addr = bufp->addr; - totlen = len + mylen; - if ( bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA ) {/* End of Key */ - if ( hashp->tmp_key ) free (hashp->tmp_key); - hashp->tmp_key = (char *)malloc ( totlen ); - if ( !hashp->tmp_key ) { - return(-1); - } - __big_return ( bufp, 1, val, set ); - } else { - xbp = __get_buf (bp[bp[0]-1], bufp, 0); - if ( !xbp || ((totlen = collect_key (xbp, totlen, val, set)) < 1 ) ) { - return(-1); - } - } - if ( bufp->addr != save_addr ) { - errno = EINVAL; /* MIS -- OUT OF BUFFERS */ - return (-1); - } - bcopy ( (bufp->page) + bp[1], &hashp->tmp_key[len], mylen ); - return ( totlen ); -} - - -/* - return 0 => OK - -1 => error -*/ -extern int -__big_split ( op, np, big_keyp, addr, obucket, ret ) -BUFHEAD *op; /* Pointer to where to put keys that go in old bucket */ -BUFHEAD *np; /* Pointer to new bucket page */ -BUFHEAD *big_keyp; /* Pointer to first page containing the big key/data */ -u_short addr; /* Address of big_keyp */ -u_int obucket; /* Old Bucket */ -SPLIT_RETURN *ret; -{ - register u_short *prev_pagep; - register BUFHEAD *tmpp; - register u_short *tp; - BUFHEAD *bp = big_keyp; - u_short off, free_space; - u_short n; - - DBT key, val; - - u_int change; - - /* Now figure out where the big key/data goes */ - if (__big_keydata ( big_keyp, 1, &key, &val, 0 )) { - return(-1); - } - change = (__call_hash ( key.data, key.size ) != obucket ); - - if ( ret->next_addr = __find_last_page ( &big_keyp ) ) { - if (!(ret->nextp = __get_buf ( ret->next_addr, big_keyp, 0 ))) { - return(-1);; - } - } else { - ret->nextp = NULL; - } - - /* Now make one of np/op point to the big key/data pair */ - assert(np->ovfl == NULL); - if ( change ) tmpp = np; - else tmpp = op; - - tmpp->flags |= BUF_MOD; -#ifdef DEBUG1 - fprintf ( stderr, "BIG_SPLIT: %d->ovfl was %d is now %d\n", tmpp->addr, - (tmpp->ovfl?tmpp->ovfl->addr:0), - (bp?bp->addr:0) ); -#endif - tmpp->ovfl = bp; /* one of op/np point to big_keyp */ - tp = (u_short *)tmpp->page; - assert ( FREESPACE(tp) >= OVFLSIZE); - n = tp[0]; - off = OFFSET(tp); - free_space = FREESPACE(tp); - tp[++n] = addr; - tp[++n] = OVFLPAGE; - tp[0] = n; - OFFSET(tp) = off; - FREESPACE(tp) = free_space - OVFLSIZE; - - /* - Finally, set the new and old return values. - BIG_KEYP contains a pointer to the last page of the big key_data pair. - Make sure that big_keyp has no following page (2 elements) or create - an empty following page. - */ - - ret->newp = np; - ret->oldp = op; - - tp = (u_short *)big_keyp->page; - big_keyp->flags |= BUF_MOD; - if ( tp[0] > 2 ) { - /* - There may be either one or two offsets on this page - If there is one, then the overflow page is linked on - normally and tp[4] is OVFLPAGE. If there are two, tp[4] - contains the second offset and needs to get stuffed in - after the next overflow page is added - */ - n = tp[4]; - free_space = FREESPACE(tp); - off = OFFSET(tp); - tp[0] -= 2; - FREESPACE(tp) = free_space + OVFLSIZE; - OFFSET(tp) = off; - tmpp = __add_ovflpage ( big_keyp ); - if ( !tmpp ) { - return(-1); - } - tp[4] = n; - } else { - tmpp = big_keyp; - } - - if ( change ) ret->newp = tmpp; - else ret->oldp = tmpp; - - return(0); -} diff --git a/lib/libc/db/hash/buf.c b/lib/libc/db/hash/buf.c deleted file mode 100644 index 61bc92f058..0000000000 --- a/lib/libc/db/hash/buf.c +++ /dev/null @@ -1,346 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Margo Seltzer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)buf.c 5.7 (Berkeley) 3/12/91"; -#endif /* LIBC_SCCS and not lint */ - -/****************************************************************************** - -PACKAGE: hash - -DESCRIPTION: - Contains buffer management - -ROUTINES: - External - __buf_init - __get_buf - __buf_free - __reclaim_buf - Internal - newbuf - -******************************************************************************/ -#include -#include -#include -#include -#include -#include "hash.h" - -/* Externals */ -extern HTAB *hashp; - -/* My internals */ -static BUFHEAD *newbuf(); - -/* Unlink B from its place in the lru */ -#define BUF_REMOVE(B) \ -{ \ - B->prev->next = B->next; \ - B->next->prev = B->prev; \ -} - -/* Insert B after P */ -#define BUF_INSERT(B,P) \ -{ \ - B->next = P->next; \ - B->prev = P; \ - P->next = B; \ - B->next->prev = B; \ -} - -#define MRU hashp->bufhead.next -#define LRU hashp->bufhead.prev - -#define MRU_INSERT(B) BUF_INSERT(B,(&hashp->bufhead)) -#define LRU_INSERT(B) BUF_INSERT(B,LRU) - -/* - We are looking for a buffer with address "addr". - If prev_bp is NULL, then address is a bucket index. - If prev_bp is not NULL, then it points to the page previous - to an overflow page that we are trying to find. - - CAVEAT: The buffer header accessed via prev_bp's ovfl field - may no longer be valid. Therefore, you must always verify that - its address matches the address you are seeking. -*/ -extern BUFHEAD * -__get_buf ( addr, prev_bp, newpage ) -u_int addr; -BUFHEAD *prev_bp; -int newpage; /* If prev_bp is set, indicates that this is - a new overflow page */ -{ - register int segment_ndx; - register BUFHEAD *bp; - register unsigned is_disk = 0; - register unsigned is_disk_mask = 0; - SEGMENT segp; - - if ( prev_bp ) { - bp = prev_bp->ovfl; - if ( !bp || (bp->addr != addr) ) bp = NULL; - if ( !newpage ) is_disk = BUF_DISK; - } - else { - /* Grab buffer out of directory */ - segment_ndx = addr & ( hashp->SGSIZE - 1 ); - - /* - * valid segment ensured by __call_hash() - */ - segp = hashp->dir[addr >> hashp->SSHIFT]; -#ifdef DEBUG - assert(segp != NULL); -#endif - bp = PTROF(segp[segment_ndx]); - is_disk_mask = ISDISK(segp[segment_ndx]); - is_disk = is_disk_mask || !hashp->new_file; - } - - if ( !bp ) { - bp = newbuf ( addr, prev_bp ); - if ( !bp || __get_page ( bp->page, addr, !prev_bp, is_disk, 0 )) { - return(NULL); - } - if ( !prev_bp ) { - segp[segment_ndx] = (BUFHEAD *)((unsigned)bp | is_disk_mask ); - } - } else { - BUF_REMOVE ( bp ); - MRU_INSERT ( bp ); - } - return(bp); -} - -/* - We need a buffer for this page. Either allocate one, or - evict a resident one (if we have as many buffers as we're - allowed) and put this one in. - - If newbuf finds an error (returning NULL), it also sets errno -*/ -static BUFHEAD * -newbuf ( addr, prev_bp ) -u_int addr; -BUFHEAD *prev_bp; -{ - register BUFHEAD *bp; /* The buffer we're going to use */ - register BUFHEAD *xbp; /* Temp pointer */ - register BUFHEAD *next_xbp; - int segment_ndx; - u_short *shortp; - u_short oaddr; - SEGMENT segp; - - oaddr = 0; - bp = LRU; - /* - If LRU buffer is pinned, the buffer pool is too small. - We need to allocate more buffers - */ - if ( hashp->nbufs || (bp->flags & BUF_PIN) ) { - /* Allocate a new one */ - bp = (BUFHEAD *)malloc ( sizeof (struct _bufhead) ); - if ( !bp || !(bp->page = (char *)malloc ( hashp->BSIZE )) ) { - return (NULL); - } - hashp->nbufs--; - } else { - /* Kick someone out */ - BUF_REMOVE( bp ); - /* - If this is an overflow page with addr 0, it's already - been flushed back in an overflow chain and initialized - */ - if ( (bp->addr != 0) || (bp->flags & BUF_BUCKET) ) { - /* - Set oaddr before __put_page so that you get it - before bytes are swapped - */ - shortp = (u_short *)bp->page; - if ( shortp[0] ) { - oaddr = shortp[shortp[0]-1]; - } - if ( (bp->flags & BUF_MOD) && - __put_page(bp->page, bp->addr, (int)IS_BUCKET(bp->flags), 0) ) { - return(NULL); - } - /* - Update the pointer to this page (i.e. invalidate it). - - If this is a new file (i.e. we created it at open time), - make sure that we mark pages which have been written to - disk so we retrieve them from disk later, rather than - allocating new pages. - */ - - if ( IS_BUCKET(bp->flags)) { - segment_ndx = bp->addr & ( hashp->SGSIZE - 1 ); - - segp = hashp->dir[bp->addr >> hashp->SSHIFT]; - - assert(segp != NULL); - - if ( hashp->new_file && - ((bp->flags & BUF_MOD) || ISDISK(segp[segment_ndx])) ) { - segp[segment_ndx] = (BUFHEAD *)BUF_DISK; - } else segp[segment_ndx] = NULL; - } - - /* - Since overflow pages can only be access by means of - their bucket, free overflow pages associated with this - bucket. - */ - for ( xbp = bp; xbp->ovfl; ) { - - next_xbp = xbp->ovfl; - xbp->ovfl = 0; - xbp = next_xbp; - - /* Check that ovfl pointer is up date */ - if ( IS_BUCKET(xbp->flags) || (oaddr != xbp->addr) ) break; - - shortp = (u_short *)xbp->page; - if ( shortp[0] ) { - oaddr = shortp[shortp[0]-1]; /* set before __put_page */ - } - if ( (xbp->flags & BUF_MOD) && - __put_page ( xbp->page, xbp->addr, 0, 0 ) ) { - return(NULL); - } - xbp->addr = 0; - xbp->flags = 0; - BUF_REMOVE ( xbp ); - LRU_INSERT ( xbp ); - } - } - } - - /* Now assign this buffer */ - bp->addr = addr; -#ifdef DEBUG1 - fprintf ( stderr, "NEWBUF1: %d->ovfl was %d is now %d\n", bp->addr, - (bp->ovfl?bp->ovfl->addr:0), 0); -#endif - bp->ovfl = NULL; - if ( prev_bp ) { - /* - If prev_bp is set, this is an overflow page, hook it in to the - buffer overflow links - */ -#ifdef DEBUG1 - fprintf ( stderr, "NEWBUF2: %d->ovfl was %d is now %d\n", prev_bp->addr, - (prev_bp->ovfl?bp->ovfl->addr:0), - (bp?bp->addr: 0)); -#endif - prev_bp->ovfl = bp; - bp->flags = 0; - } else bp->flags = BUF_BUCKET; - MRU_INSERT ( bp ); - return ( bp ); -} - -extern void -__buf_init ( nbytes ) -int nbytes; -{ - int npages; - BUFHEAD *bfp = &(hashp->bufhead); - - npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT; - npages = MAX ( npages, MIN_BUFFERS ); - - hashp->nbufs = npages; - bfp->next = bfp; - bfp->prev = bfp; - /* - This space is calloc'd so these are already null - - bfp->ovfl = NULL; - bfp->flags = 0; - bfp->page = NULL; - bfp->addr = 0; - */ -} - -extern int -__buf_free ( do_free, to_disk ) -int do_free; -int to_disk; -{ - BUFHEAD *bp; - - /* Need to make sure that buffer manager has been initialized */ - if ( !LRU ) { - return(0); - } - - for ( bp = LRU; bp != &hashp->bufhead; ) { - /* Check that the buffer is valid */ - if ( bp->addr || IS_BUCKET(bp->flags) ) { - if ( to_disk && (bp->flags & BUF_MOD) && - __put_page (bp->page, bp->addr, IS_BUCKET(bp->flags), 0 )) { - return (-1); - } - } - - /* Check if we are freeing stuff */ - if ( do_free ) { - if ( bp->page ) free ( bp->page ); - BUF_REMOVE(bp); - (void)free ( bp ); - bp = LRU; - } else bp = bp->prev; - } - - return(0); -} - -extern void -__reclaim_buf ( bp ) -BUFHEAD *bp; -{ - bp->ovfl = 0; - bp->addr = 0; - bp->flags = 0; - BUF_REMOVE ( bp ); - LRU_INSERT ( bp ); -} diff --git a/lib/libc/db/hash/dynahash.c b/lib/libc/db/hash/dynahash.c deleted file mode 100644 index 70af919199..0000000000 --- a/lib/libc/db/hash/dynahash.c +++ /dev/null @@ -1,1013 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Margo Seltzer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)dynahash.c 5.14 (Berkeley) 4/2/91"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "hash.h" - -/* Fast arithmetic, relying on powers of 2, */ - -#define MOD(x,y) ((x) & ((y)-1)) -#define RETURN_ERROR(ERR,LOC) { save_errno = ERR; goto LOC; } - -/* Return values */ - -#define SUCCESS 0 -#define ERROR -1 -#define ABNORMAL 1 - -/* page.c */ -extern int __get_page(); -extern int __split_page(); -extern int __addel(); -extern int __delpair(); -extern u_long *__init_bitmap(); - -/* big.c */ -extern int __big_return(); -extern int __big_keydata(); -extern u_short __find_last_page(); - -/* buf.c */ -extern BUFHEAD *__get_buf(); -extern void __buf_init(); -extern int __buf_free(); - -/* hash function */ -extern int (*default_hash)(); - -/* My externals */ -extern int __expand_table(); -extern u_int __call_hash(); - -/* Internal routines */ -static HTAB *init_hash(); -static int hash_access(); -static int flush_meta(); -static int alloc_segs(); -static int init_htab(); -static char *hash_realloc(); -static int hdestroy(); -static int hash_put(); -static int hash_delete(); -static int hash_get(); -static int hash_sync(); -static int hash_close(); -static int hash_seq(); -static void swap_header_copy(); -static void swap_header(); - -/* Local data */ - -HTAB *hashp = NULL; - -#ifdef HASH_STATISTICS -long hash_accesses, hash_collisions, hash_expansions, hash_overflows; -#endif - -/************************** INTERFACE ROUTINES **********************/ -/* OPEN/CLOSE */ - -extern DB * -hash_open ( file, flags, mode, info ) -const char *file; -int flags; -int mode; -const HASHINFO *info; /* Special directives for create */ -{ - int buckets; - int bpages; - int hdrsize; - int i; - int new_table = 0; - int nkey; - int nsegs; - int save_errno; - struct stat statbuf; - DB *dbp; - - - if ( !(hashp = (HTAB *) calloc( 1, sizeof(HTAB) ))) { - /* calloc should set errno */ - return(0); - } - hashp->fp = -1; - /* - Select flags relevant to us. - Even if user wants write only, we need to be able to read - the actual file, so we need to open it read/write. But, the - field in the hashp structure needs to be accurate so that - we can check accesses. - */ - flags = flags & (O_CREAT | O_EXCL | O_RDONLY | O_RDWR | O_TRUNC | O_WRONLY); - hashp->flags = flags; - if ( flags & O_WRONLY ) flags = (flags & ~O_WRONLY) | O_RDWR; - - if ( !file || - (flags & O_TRUNC) || - (stat ( file, &statbuf ) && (errno == ENOENT)) ) { - if ( errno == ENOENT ) { - errno = 0; /* Just in case someone looks at errno */ - } - new_table = 1; - } - - if ( file ) { - if ((hashp->fp = open ( file, flags, mode )) == -1) { - RETURN_ERROR (errno, error0); - } - (void)fcntl(hashp->fp, F_SETFD, 1); - } - - if ( new_table ) { - if ( !(hashp = init_hash( info )) ) { - RETURN_ERROR(errno,error1); - } - } else { - /* Table already exists */ - if ( info && info->hash ) hashp->hash = info->hash; - else hashp->hash = default_hash; - - hdrsize = read ( hashp->fp, &hashp->hdr, sizeof(HASHHDR) ); -#if BYTE_ORDER == LITTLE_ENDIAN - swap_header ( ); -#endif - if ( hdrsize == -1 ) { - RETURN_ERROR(errno, error1); - } - if ( hdrsize != sizeof(HASHHDR) ) { - RETURN_ERROR(EFTYPE, error1); - } - - /* Verify file type, versions and hash function */ - if ( hashp->MAGIC != HASHMAGIC ) - RETURN_ERROR ( EFTYPE, error1 ); - if ( hashp->VERSION != VERSION_NO ) - RETURN_ERROR ( EFTYPE, error1 ); - if (hashp->hash ( CHARKEY, sizeof(CHARKEY) ) != hashp->H_CHARKEY ) { - RETURN_ERROR ( EFTYPE, error1 ); - } - - /* - Figure out how many segments we need. - */ - nsegs = (hashp->MAX_BUCKET + hashp->SGSIZE -1)/ hashp->SGSIZE; - hashp->nsegs = 0; - if (alloc_segs( nsegs )) { - /* - If alloc_segs fails, table will have been destroyed - and errno will have been set. - */ - return( (DB *) NULL ); - } - - /* Read in bitmaps */ - bpages = (hashp->SPARES[__log2(hashp->MAX_BUCKET)] + - (hashp->BSIZE << BYTE_SHIFT) - 1) >> - (hashp->BSHIFT + BYTE_SHIFT); - - hashp->nmaps = bpages; - memset ( &hashp->mapp[0], 0, bpages * sizeof ( u_long *) ); - } - - /* Initialize Buffer Manager */ - if ( info && info->cachesize ) { - __buf_init (info->cachesize); - } else { - __buf_init (DEF_BUFSIZE); - } - - hashp->new_file = new_table; - hashp->save_file = file && (hashp->flags & (O_WRONLY|O_RDWR)); - hashp->cbucket = -1; - if ( !(dbp = (DB *)malloc ( sizeof (DB) )) ) { - save_errno = errno; - hdestroy(); - errno = save_errno; - return ( NULL ); - } - dbp->internal = (char *)hashp; - dbp->close = hash_close; - dbp->del = hash_delete; - dbp->get = hash_get; - dbp->put = hash_put; - dbp->seq = hash_seq; - dbp->sync = hash_sync; - dbp->type = DB_HASH; - -#ifdef DEBUG - (void)fprintf(stderr, "%s\n%s%x\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%x\n%s%x\n%s%d\n%s%d\n", - "init_htab:", - "TABLE POINTER ", hashp, - "BUCKET SIZE ", hashp->BSIZE, - "BUCKET SHIFT ", hashp->BSHIFT, - "DIRECTORY SIZE ", hashp->DSIZE, - "SEGMENT SIZE ", hashp->SGSIZE, - "SEGMENT SHIFT ", hashp->SSHIFT, - "FILL FACTOR ", hashp->FFACTOR, - "MAX BUCKET ", hashp->MAX_BUCKET, - "HIGH MASK ", hashp->HIGH_MASK, - "LOW MASK ", hashp->LOW_MASK, - "NSEGS ", hashp->nsegs, - "NKEYS ", hashp->NKEYS ); -#endif -#ifdef HASH_STATISTICS - hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0; -#endif - return (dbp); - -error2: - (void)hdestroy(); - errno = save_errno; - return ( (DB *)NULL ); - -error1: - (void) close ( hashp->fp ); - -error0: - free ( hashp ); - errno = save_errno; - return ( (DB *) NULL ); -} - -static int -hash_close (dbp) -DB *dbp; -{ - int retval; - - if ( !dbp ) { - return(ERROR); - } - hashp = (HTAB *)dbp->internal; - retval = hdestroy(); - (void)free ( dbp ); - return ( retval ); -} - - -/************************** LOCAL CREATION ROUTINES **********************/ -static HTAB * -init_hash( info ) -HASHINFO *info; -{ - int nelem; - - nelem = 1; - - hashp->LORDER = BYTE_ORDER; - hashp->BSIZE = DEF_BUCKET_SIZE; - hashp->BSHIFT = DEF_BUCKET_SHIFT; - hashp->SGSIZE = DEF_SEGSIZE; - hashp->SSHIFT = DEF_SEGSIZE_SHIFT; - hashp->DSIZE = DEF_DIRSIZE; - hashp->FFACTOR = DEF_FFACTOR; - hashp->hash = default_hash; - bzero (hashp->SPARES, sizeof (hashp->SPARES)); - - if ( info ) { - if ( info->bsize ) { - /* Round pagesize up to power of 2 */ - hashp->BSHIFT = __log2(info->bsize); - hashp->BSIZE = 1 << hashp->BSHIFT; - if ( hashp->BSIZE > MAX_BSIZE ) { - errno = EINVAL; - return(0); - } - } - if ( info->ffactor ) hashp->FFACTOR = info->ffactor; - if ( info->hash ) hashp->hash = info->hash; - if ( info->nelem ) nelem = info->nelem; - if ( info->lorder ) { - if ( info->lorder != BIG_ENDIAN && - info->lorder != LITTLE_ENDIAN) { - errno = EINVAL; - return(0); - } - hashp->LORDER = info->lorder; - } - } - - /* init_htab should destroy the table and set errno if it fails */ - if ( init_htab ( nelem ) ) { - return(0); - } else { - return(hashp); - } -} - -/* - This calls alloc_segs which may run out of memory. - Alloc_segs will destroy the table and set errno, - so we just pass the error information along. - - Returns 0 on No Error - -*/ -static int -init_htab ( nelem ) -int nelem; -{ - register SEGMENT *segp; - register int nbuckets; - register int nsegs; - int l2; - - /* - * Divide number of elements by the fill factor and determine a desired - * number of buckets. Allocate space for the next greater power of - * two number of buckets - */ - nelem = (nelem - 1) / hashp->FFACTOR + 1; - - l2 = __log2(nelem); - nbuckets = 1 << l2; - nbuckets = MAX ( nbuckets, 2 ); - - hashp->SPARES[l2] = l2 + 1; - hashp->SPARES[l2+1] = l2 + 1; - /* - First bitmap page is at: - splitpoint l2 - page offset 1 - */ - __init_bitmap ( OADDR_OF(l2,1), l2+1, 0 ); - - hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1; - hashp->HIGH_MASK = (nbuckets << 1) - 1; - hashp->HDRPAGES = ((MAX(sizeof(HASHHDR),MINHDRSIZE) - 1) >> - hashp->BSHIFT) + 1; - - nsegs = (nbuckets - 1) / hashp->SGSIZE + 1; - nsegs = 1 << __log2(nsegs); - - if ( nsegs > hashp->DSIZE ) { - hashp->DSIZE = nsegs; - } - - return (alloc_segs ( nsegs ) ); -} - -/********************** DESTROY/CLOSE ROUTINES ************************/ - -/* - Flushes any changes to the file if necessary and - destroys the hashp structure, freeing all allocated - space. -*/ -static int -hdestroy() -{ - int save_errno; - int i; - - save_errno = 0; - - if (hashp != NULL) { -#ifdef HASH_STATISTICS - (void) fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n", - hash_accesses, hash_collisions); - (void) fprintf(stderr, "hdestroy: expansions %ld\n", - hash_expansions); - (void) fprintf(stderr, "hdestroy: overflows %ld\n", - hash_overflows); - (void) fprintf(stderr, "keys %ld maxp %d segmentcount %d\n", - hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs); - - for ( i = 0; i < NCACHED; i++ ) { - (void) fprintf ( stderr, "spares[%d] = %d\n", i, hashp->SPARES[i] ); - } -#endif - - /* - Call on buffer manager to free buffers, and if - required, write them to disk - */ - if (__buf_free(1, hashp->save_file)) { - save_errno = errno; - } - if ( hashp->dir ) { - (void)free(*hashp->dir); /* Free initial segments */ - /* Free extra segments */ - while ( hashp->exsegs-- ) { - (void)free ( hashp->dir[--hashp->nsegs] ); - } - (void)free(hashp->dir); - } - if (flush_meta() && !save_errno) { - save_errno = errno; - } - - /* Free Bigmaps */ - for ( i = 0; i < hashp->nmaps; i++ ) { - if ( hashp->mapp[i] ) { - (void) free ( hashp->mapp[i] ); - } - } - - if ( hashp->fp != -1 ) { - (void)close (hashp->fp); - } - (void)free(hashp); - hashp = NULL; - } - if ( save_errno ) { - errno = save_errno; - return(ERROR); - } else { - return(SUCCESS); - } -} - -/* - Write modified pages to disk - 0 == OK - -1 ERROR -*/ -static int -hash_sync(dbp) -DB *dbp; -{ - if ( !dbp ) { - return (ERROR); - } - - hashp = (HTAB *)dbp->internal; - - if (!hashp->save_file) return(0); - if ( __buf_free ( 0, 1 ) || flush_meta()) { - return(ERROR); - } - hashp->new_file = 0; - return(0); -} - -/* -0 == OK --1 indicates that errno should be set -*/ -static int -flush_meta() -{ - int fp; - int hdrsize; - int i; - int wsize; - HASHHDR *whdrp; - HASHHDR whdr; - - if (!hashp->save_file) return(0); - hashp->MAGIC = HASHMAGIC; - hashp->VERSION = VERSION_NO; - hashp->H_CHARKEY = hashp->hash ( CHARKEY, sizeof(CHARKEY)); - - fp = hashp->fp; - whdrp = &hashp->hdr; -#if BYTE_ORDER == LITTLE_ENDIAN - whdrp = &whdr; - swap_header_copy( &hashp->hdr, whdrp ); -#endif - if ( (lseek (fp, 0, SEEK_SET) == -1 ) || - ((wsize = write ( fp, whdrp, sizeof(HASHHDR))) == -1)) { - return(-1); - } else if ( wsize != sizeof(HASHHDR) ) { - errno = EFTYPE; - hashp->errno = errno; - return(-1); - } - for ( i = 0; i < NCACHED; i++ ) { - if ( hashp->mapp[i] ) { - if (!__put_page((char *)hashp->mapp[i], - hashp->BITMAPS[i], 0, 1)){ - return(-1); - } - } - } - return(0); -} -/*******************************SEARCH ROUTINES *****************************/ -/* - All the access routines return - 0 on SUCCESS - 1 to indicate an external ERROR (i.e. key not found, etc) - -1 to indicate an internal ERROR (i.e. out of memory, etc) -*/ -static int -hash_get ( dbp, key, data, flag ) -DB *dbp; -DBT *key, *data; -u_int flag; -{ -#ifdef lint - flag = flag; -#endif - - if ( !dbp ) { - return (ERROR); - } - hashp = (HTAB *)dbp->internal; - if ( hashp->flags & O_WRONLY ) { - errno = EBADF; - hashp->errno = errno; - return ( ERROR ); - } - return ( hash_access ( HASH_GET, key, data ) ); -} - -static int -hash_put ( dbp, key, data, flag ) -DB *dbp; -DBT *key, *data; -u_int flag; -{ - if ( !dbp ) { - return (ERROR); - } - hashp = (HTAB *)dbp->internal; - if ( (hashp->flags & O_ACCMODE) == O_RDONLY ) { - errno = EBADF; - hashp->errno = errno; - return ( ERROR ); - } - if ( flag == R_NOOVERWRITE ) { - return ( hash_access ( HASH_PUTNEW, key, data ) ); - } else { - return ( hash_access ( HASH_PUT, key, data ) ); - } -} - -static int -hash_delete ( dbp, key, flag ) -DB *dbp; -DBT *key; -u_int flag; /* Ignored */ -{ -#ifdef lint - flag = flag; -#endif - if ( !dbp ) { - return (ERROR); - } - hashp = (HTAB *)dbp->internal; - if ( (hashp->flags & O_ACCMODE) == O_RDONLY ) { - errno = EBADF; - hashp->errno = errno; - return ( ERROR ); - } - return ( hash_access ( HASH_DELETE, key, NULL ) ); -} - -/* - Assume that hashp has been set in wrapper routine -*/ -static int -hash_access(action, key, val) -ACTION action; -DBT *key, *val; -{ - register BUFHEAD *rbufp; - register u_short *bp; - register int ndx; - register int n; - register int off = hashp->BSIZE; - register int size; - register char *kp; - BUFHEAD *bufp; - BUFHEAD *save_bufp; - u_short pageno; - -#ifdef HASH_STATISTICS - hash_accesses++; -#endif - - size = key->size; - kp = (char *)key->data; - rbufp = __get_buf ( __call_hash(kp, size), NULL, 0 ); - if ( !rbufp ) return(ERROR); - save_bufp = rbufp; - - /* Pin the bucket chain */ - rbufp->flags |= BUF_PIN; - for ( bp = (u_short *)rbufp->page, n = *bp++, ndx = 1; ndx < n; ) { - if ( bp[1] >= REAL_KEY ) { - /* Real key/data pair */ - if (size == off - *bp && - bcmp(kp, rbufp->page + *bp, size) == 0) { - goto found; - } - off = bp[1]; -#ifdef HASH_STATISTICS - hash_collisions++; -#endif - bp += 2; - ndx += 2; - } else if ( bp[1] == OVFLPAGE ) { - rbufp = __get_buf ( *bp, rbufp, 0 ); - if ( !rbufp ) { - save_bufp->flags &= ~BUF_PIN; - return (ERROR); - } - /* FOR LOOP INIT */ - bp = (u_short *)rbufp->page; - n = *bp++; - ndx = 1; - off = hashp->BSIZE; - } else if ( bp[1] < REAL_KEY ) { - if ( (ndx = __find_bigpair(rbufp, ndx, kp, size )) > 0 ) { - goto found; - } else if ( ndx == -2 ) { - bufp = rbufp; - if ( !(pageno = __find_last_page ( &bufp )) ) { - ndx = 0; - rbufp = bufp; - break; /* FOR */ - } - rbufp = __get_buf ( pageno, bufp, 0 ); - if ( !rbufp ) { - save_bufp->flags &= ~BUF_PIN; - return (ERROR); - } - /* FOR LOOP INIT */ - bp = (u_short *)rbufp->page; - n = *bp++; - ndx = 1; - off = hashp->BSIZE; - } else { - save_bufp->flags &= ~BUF_PIN; - return(ERROR); - } - } - } - - /* Not found */ - switch ( action ) { - case HASH_PUT: - case HASH_PUTNEW: - if (__addel(rbufp, key, val)) { - save_bufp->flags &= ~BUF_PIN; - return(ERROR); - } else { - save_bufp->flags &= ~BUF_PIN; - return(SUCCESS); - } - case HASH_GET: - case HASH_DELETE: - default: - save_bufp->flags &= ~BUF_PIN; - return(ABNORMAL); - } - -found: - switch (action) { - case HASH_PUTNEW: - save_bufp->flags &= ~BUF_PIN; - return(ABNORMAL); - case HASH_GET: - bp = (u_short *)rbufp->page; - if (bp[ndx+1] < REAL_KEY) __big_return(rbufp, ndx, val, 0); - else { - val->data = (u_char *)rbufp->page + (int) bp[ndx + 1]; - val->size = bp[ndx] - bp[ndx + 1]; - } - break; - case HASH_PUT: - if ((__delpair (rbufp, ndx)) || (__addel (rbufp, key, val)) ) { - save_bufp->flags &= ~BUF_PIN; - return(ERROR); - } - break; - case HASH_DELETE: - if (__delpair (rbufp, ndx))return(ERROR); - break; - } - save_bufp->flags &= ~BUF_PIN; - return (SUCCESS); -} - -static int -hash_seq(dbp, key, data, flag) -DB *dbp; -DBT *key, *data; -u_int flag; -{ - register u_int bucket; - register BUFHEAD *bufp; - BUFHEAD *save_bufp; - u_short *bp; - u_short ndx; - u_short pageno; - - if ( !dbp ) { - return (ERROR); - } - - hashp = (HTAB *)dbp->internal; - if ( hashp->flags & O_WRONLY ) { - errno = EBADF; - hashp->errno = errno; - return ( ERROR ); - } -#ifdef HASH_STATISTICS - hash_accesses++; -#endif - - if ( (hashp->cbucket < 0) || (flag == R_FIRST) ) { - hashp->cbucket = 0; - hashp->cndx = 1; - hashp->cpage = NULL; - } - - if ( !(bufp = hashp->cpage) ) { - for (bucket = hashp->cbucket; - bucket <= hashp->MAX_BUCKET; - bucket++, hashp->cndx = 1 ) { - - bufp = __get_buf ( bucket, NULL, 0 ); - if (!bufp) return(ERROR); - hashp->cpage = bufp; - bp = (u_short *)bufp->page; - if (bp[0]) break; - } - hashp->cbucket = bucket; - if ( hashp->cbucket > hashp->MAX_BUCKET ) { - hashp->cbucket = -1; - return ( ABNORMAL ); - } - } else { - bp = (u_short *)hashp->cpage->page; - } - -#ifdef DEBUG - assert (bp); - assert (bufp); -#endif - while ( bp[hashp->cndx+1] == OVFLPAGE ) { - bufp = hashp->cpage = __get_buf ( bp[hashp->cndx], bufp, 0 ); - if (!bufp) return(ERROR); - bp = (u_short *)(bufp->page); - hashp->cndx = 1; - } - ndx = hashp->cndx; - if (bp[ndx+1] < REAL_KEY) { - if (__big_keydata(bufp, ndx, key, data, 1)) { - return(ERROR); - } - } else { - key->data = (u_char *)hashp->cpage->page + bp[ndx]; - key->size = (ndx > 1 ? bp[ndx-1] : hashp->BSIZE) - bp[ndx]; - data->data = (u_char *)hashp->cpage->page + bp[ndx + 1]; - data->size = bp[ndx] - bp[ndx + 1]; - ndx += 2; - if ( ndx > bp[0] ) { - hashp->cpage = NULL; - hashp->cbucket++; - hashp->cndx=1; - } else hashp->cndx = ndx; - } - return (SUCCESS); -} - -/********************************* UTILITIES ************************/ -/* - 0 ==> OK - -1 ==> Error -*/ -extern int -__expand_table() -{ - u_int old_bucket, new_bucket; - int new_segnum; - int dirsize; - int spare_ndx; - register char **old, **new; -#ifdef HASH_STATISTICS - hash_expansions++; -#endif - - new_bucket = ++hashp->MAX_BUCKET; - old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK); - - new_segnum = new_bucket >> hashp->SSHIFT; - - /* Check if we need a new segment */ - if ( new_segnum >= hashp->nsegs ) { - - /* Check if we need to expand directory */ - if ( new_segnum >= hashp->DSIZE ) { - - /* Reallocate directory */ - dirsize = hashp->DSIZE * sizeof ( SEGMENT * ); - if (!hash_realloc ( &hashp->dir, dirsize, (dirsize << 1 ) )) { - return(-1); - } - hashp->DSIZE = dirsize << 1; - } - if (!(hashp->dir[new_segnum] = - (SEGMENT)calloc ( hashp->SGSIZE, sizeof(SEGMENT)))) { - return(-1); - } - hashp->exsegs++; - hashp->nsegs++; - } - - /* - If the split point is increasing (MAX_BUCKET's log - base 2 increases), we need to copy the current contents - of the spare split bucket to the next bucket - */ - spare_ndx = __log2(hashp->MAX_BUCKET); - if ( spare_ndx != (__log2(hashp->MAX_BUCKET - 1))) { - hashp->SPARES[spare_ndx] = hashp->SPARES[spare_ndx-1]; - } - - if ( new_bucket > hashp->HIGH_MASK ) { - /* Starting a new doubling */ - hashp->LOW_MASK = hashp->HIGH_MASK; - hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK; - } - - /* - * Relocate records to the new bucket - */ - return(__split_page ( old_bucket, new_bucket )); -} -/* - If realloc guarantees that the pointer is not destroyed - if the realloc fails, then this routine can go away -*/ -static char * -hash_realloc ( p_ptr, oldsize, newsize ) -char **p_ptr; -int oldsize; -int newsize; -{ - register char *p; - - if (p = (char *)malloc ( newsize ) ) { - bcopy ( *p_ptr, p, oldsize ); - bzero ( *p_ptr + oldsize, newsize-oldsize ); - free(*p_ptr); - *p_ptr = p; - } - return (p); -} - -extern u_int -__call_hash ( k, len ) -char *k; -int len; -{ - int n, bucket; - n = hashp->hash(k, len); - - bucket = n & hashp->HIGH_MASK; - if ( bucket > hashp->MAX_BUCKET ) { - bucket = bucket & hashp->LOW_MASK; - } - - return(bucket); -} - -/* - Allocate segment table. On error, destroy the table - and set errno. - - Returns 0 on success -*/ -static int -alloc_segs ( nsegs ) -int nsegs; -{ - register int i; - register SEGMENT store; - - int save_errno; - - if (!(hashp->dir = (SEGMENT *)calloc(hashp->DSIZE, sizeof(SEGMENT *)))) { - save_errno = errno; - (void)hdestroy(); - errno = save_errno; - return(-1); - } - - /* Allocate segments */ - store = (SEGMENT)calloc ( nsegs << hashp->SSHIFT, sizeof (SEGMENT) ); - if ( !store ) { - save_errno = errno; - (void)hdestroy(); - errno = save_errno; - return(-1); - } - - for ( i=0; i < nsegs; i++, hashp->nsegs++ ) { - hashp->dir[i] = &store[i<SSHIFT]; - } - return(0); -} - -/* - Hashp->hdr needs to be byteswapped. -*/ -static void -swap_header_copy ( srcp, destp ) -HASHHDR *srcp; -HASHHDR *destp; -{ - int i; - - BLSWAP_COPY(srcp->magic, destp->magic); - BLSWAP_COPY(srcp->version, destp->version); - BLSWAP_COPY(srcp->lorder, destp->lorder); - BLSWAP_COPY(srcp->bsize, destp->bsize); - BLSWAP_COPY(srcp->bshift, destp->bshift); - BLSWAP_COPY(srcp->dsize, destp->dsize); - BLSWAP_COPY(srcp->ssize, destp->ssize); - BLSWAP_COPY(srcp->sshift, destp->sshift); - BLSWAP_COPY(srcp->max_bucket, destp->max_bucket); - BLSWAP_COPY(srcp->high_mask, destp->high_mask); - BLSWAP_COPY(srcp->low_mask, destp->low_mask); - BLSWAP_COPY(srcp->ffactor, destp->ffactor); - BLSWAP_COPY(srcp->nkeys, destp->nkeys); - BLSWAP_COPY(srcp->hdrpages, destp->hdrpages); - BLSWAP_COPY(srcp->h_charkey, destp->h_charkey); - for ( i=0; i < NCACHED; i++ ) { - BLSWAP_COPY ( srcp->spares[i] , destp->spares[i]); - BSSWAP_COPY ( srcp->bitmaps[i] , destp->bitmaps[i]); - } - return; -} - -static void -swap_header ( ) -{ - HASHHDR *hdrp; - int i; - - hdrp = &hashp->hdr; - - BLSWAP(hdrp->magic); - BLSWAP(hdrp->version); - BLSWAP(hdrp->lorder); - BLSWAP(hdrp->bsize); - BLSWAP(hdrp->bshift); - BLSWAP(hdrp->dsize); - BLSWAP(hdrp->ssize); - BLSWAP(hdrp->sshift); - BLSWAP(hdrp->max_bucket); - BLSWAP(hdrp->high_mask); - BLSWAP(hdrp->low_mask); - BLSWAP(hdrp->ffactor); - BLSWAP(hdrp->nkeys); - BLSWAP(hdrp->hdrpages); - BLSWAP(hdrp->h_charkey); - for ( i=0; i < NCACHED; i++ ) { - BLSWAP ( hdrp->spares[i] ); - BSSWAP ( hdrp->bitmaps[i] ); - } - return; -} diff --git a/lib/libc/db/hash/extern.h b/lib/libc/db/hash/extern.h new file mode 100644 index 0000000000..5b1b35e437 --- /dev/null +++ b/lib/libc/db/hash/extern.h @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/4/93 + */ + +BUFHEAD *__add_ovflpage __P((HTAB *, BUFHEAD *)); +int __addel __P((HTAB *, BUFHEAD *, const DBT *, const DBT *)); +int __big_delete __P((HTAB *, BUFHEAD *)); +int __big_insert __P((HTAB *, BUFHEAD *, const DBT *, const DBT *)); +int __big_keydata __P((HTAB *, BUFHEAD *, DBT *, DBT *, int)); +int __big_return __P((HTAB *, BUFHEAD *, int, DBT *, int)); +int __big_split __P((HTAB *, BUFHEAD *, BUFHEAD *, BUFHEAD *, + int, u_int, SPLIT_RETURN *)); +int __buf_free __P((HTAB *, int, int)); +void __buf_init __P((HTAB *, int)); +u_int __call_hash __P((HTAB *, char *, int)); +int __delpair __P((HTAB *, BUFHEAD *, int)); +int __expand_table __P((HTAB *)); +int __find_bigpair __P((HTAB *, BUFHEAD *, int, char *, int)); +u_short __find_last_page __P((HTAB *, BUFHEAD **)); +void __free_ovflpage __P((HTAB *, BUFHEAD *)); +BUFHEAD *__get_buf __P((HTAB *, u_int, BUFHEAD *, int)); +int __get_page __P((HTAB *, char *, u_int, int, int, int)); +int __init_bitmap __P((HTAB *, int, int, int)); +u_int __log2 __P((u_int)); +int __put_page __P((HTAB *, char *, u_int, int, int)); +void __reclaim_buf __P((HTAB *, BUFHEAD *)); +int __split_page __P((HTAB *, u_int, u_int)); + +/* Default hash routine. */ +extern int (*__default_hash) __P((u_char *, int)); + +#ifdef HASH_STATISTICS +extern long hash_accesses, hash_collisions, hash_expansions, hash_overflows; +#endif diff --git a/lib/libc/db/hash/hash.c b/lib/libc/db/hash/hash.c new file mode 100644 index 0000000000..aa2e42856e --- /dev/null +++ b/lib/libc/db/hash/hash.c @@ -0,0 +1,988 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)hash.c 8.1 (Berkeley) 6/6/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef DEBUG +#include +#endif + +#include +#include "hash.h" +#include "page.h" +#include "extern.h" + +static int alloc_segs __P((HTAB *, int)); +static int flush_meta __P((HTAB *)); +static int hash_access __P((HTAB *, ACTION, DBT *, DBT *)); +static int hash_close __P((DB *)); +static int hash_delete __P((const DB *, const DBT *, u_int)); +static int hash_fd __P((const DB *)); +static int hash_get __P((const DB *, const DBT *, DBT *, u_int)); +static int hash_put __P((const DB *, DBT *, const DBT *, u_int)); +static void *hash_realloc __P((SEGMENT **, int, int)); +static int hash_seq __P((const DB *, DBT *, DBT *, u_int)); +static int hash_sync __P((const DB *, u_int)); +static int hdestroy __P((HTAB *)); +static HTAB *init_hash __P((HTAB *, const char *, HASHINFO *)); +static int init_htab __P((HTAB *, int)); +#if BYTE_ORDER == LITTLE_ENDIAN +static void swap_header __P((HTAB *)); +static void swap_header_copy __P((HASHHDR *, HASHHDR *)); +#endif + +/* Fast arithmetic, relying on powers of 2, */ +#define MOD(x, y) ((x) & ((y) - 1)) + +#define RETURN_ERROR(ERR, LOC) { save_errno = ERR; goto LOC; } + +/* Return values */ +#define SUCCESS (0) +#define ERROR (-1) +#define ABNORMAL (1) + +#ifdef HASH_STATISTICS +long hash_accesses, hash_collisions, hash_expansions, hash_overflows; +#endif + +/************************** INTERFACE ROUTINES ***************************/ +/* OPEN/CLOSE */ + +extern DB * +__hash_open(file, flags, mode, info) + const char *file; + int flags, mode; + const HASHINFO *info; /* Special directives for create */ +{ + HTAB *hashp; + struct stat statbuf; + DB *dbp; + int bpages, hdrsize, new_table, nsegs, save_errno; + + if ((flags & O_ACCMODE) == O_WRONLY) { + errno = EINVAL; + return (NULL); + } + + if (!(hashp = calloc(1, sizeof(HTAB)))) + return (NULL); + hashp->fp = -1; + /* + * Select flags relevant to us. Even if user wants write only, we need + * to be able to read the actual file, so we need to open it read/write. + * But, the field in the hashp structure needs to be accurate so that + * we can check accesses. + */ + hashp->flags = flags = flags & __USE_OPEN_FLAGS; + + new_table = 0; + if (!file || (flags & O_TRUNC) || + (stat(file, &statbuf) && (errno == ENOENT))) { + if (errno == ENOENT) + errno = 0; /* Just in case someone looks at errno */ + new_table = 1; + } + if (file) { + if ((hashp->fp = open(file, flags, mode)) == -1) + RETURN_ERROR(errno, error0); + (void)fcntl(hashp->fp, F_SETFD, 1); + } + if (new_table) { + if (!(hashp = init_hash(hashp, file, (HASHINFO *)info))) + RETURN_ERROR(errno, error1); + } else { + /* Table already exists */ + if (info && info->hash) + hashp->hash = info->hash; + else + hashp->hash = __default_hash; + + hdrsize = read(hashp->fp, &hashp->hdr, sizeof(HASHHDR)); +#if BYTE_ORDER == LITTLE_ENDIAN + swap_header(hashp); +#endif + if (hdrsize == -1) + RETURN_ERROR(errno, error1); + if (hdrsize != sizeof(HASHHDR)) + RETURN_ERROR(EFTYPE, error1); + /* Verify file type, versions and hash function */ + if (hashp->MAGIC != HASHMAGIC) + RETURN_ERROR(EFTYPE, error1); + if (hashp->VERSION != HASHVERSION) + RETURN_ERROR(EFTYPE, error1); + if (hashp->hash(CHARKEY, sizeof(CHARKEY)) != hashp->H_CHARKEY) + RETURN_ERROR(EFTYPE, error1); + /* + * Figure out how many segments we need. Max_Bucket is the + * maximum bucket number, so the number of buckets is + * max_bucket + 1. + */ + nsegs = (hashp->MAX_BUCKET + 1 + hashp->SGSIZE - 1) / + hashp->SGSIZE; + hashp->nsegs = 0; + if (alloc_segs(hashp, nsegs)) + /* + * If alloc_segs fails, table will have been destroyed + * and errno will have been set. + */ + return (NULL); + /* Read in bitmaps */ + bpages = (hashp->SPARES[hashp->OVFL_POINT] + + (hashp->BSIZE << BYTE_SHIFT) - 1) >> + (hashp->BSHIFT + BYTE_SHIFT); + + hashp->nmaps = bpages; + (void)memset(&hashp->mapp[0], 0, bpages * sizeof(u_long *)); + } + + /* Initialize Buffer Manager */ + if (info && info->cachesize) + __buf_init(hashp, info->cachesize); + else + __buf_init(hashp, DEF_BUFSIZE); + + hashp->new_file = new_table; + hashp->save_file = file && (hashp->flags & O_RDWR); + hashp->cbucket = -1; + if (!(dbp = malloc(sizeof(DB)))) { + save_errno = errno; + hdestroy(hashp); + errno = save_errno; + return (NULL); + } + dbp->internal = hashp; + dbp->close = hash_close; + dbp->del = hash_delete; + dbp->fd = hash_fd; + dbp->get = hash_get; + dbp->put = hash_put; + dbp->seq = hash_seq; + dbp->sync = hash_sync; + dbp->type = DB_HASH; + +#ifdef DEBUG + (void)fprintf(stderr, +"%s\n%s%x\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%x\n%s%x\n%s%d\n%s%d\n", + "init_htab:", + "TABLE POINTER ", hashp, + "BUCKET SIZE ", hashp->BSIZE, + "BUCKET SHIFT ", hashp->BSHIFT, + "DIRECTORY SIZE ", hashp->DSIZE, + "SEGMENT SIZE ", hashp->SGSIZE, + "SEGMENT SHIFT ", hashp->SSHIFT, + "FILL FACTOR ", hashp->FFACTOR, + "MAX BUCKET ", hashp->MAX_BUCKET, + "OVFL POINT ", hashp->OVFL_POINT, + "LAST FREED ", hashp->LAST_FREED, + "HIGH MASK ", hashp->HIGH_MASK, + "LOW MASK ", hashp->LOW_MASK, + "NSEGS ", hashp->nsegs, + "NKEYS ", hashp->NKEYS); +#endif +#ifdef HASH_STATISTICS + hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0; +#endif + return (dbp); + +error1: + if (hashp != NULL) + (void)close(hashp->fp); + +error0: + free(hashp); + errno = save_errno; + return (NULL); +} + +static int +hash_close(dbp) + DB *dbp; +{ + HTAB *hashp; + int retval; + + if (!dbp) + return (ERROR); + + hashp = (HTAB *)dbp->internal; + retval = hdestroy(hashp); + free(dbp); + return (retval); +} + +static int +hash_fd(dbp) + const DB *dbp; +{ + HTAB *hashp; + + if (!dbp) + return (ERROR); + + hashp = (HTAB *)dbp->internal; + if (hashp->fp == -1) { + errno = ENOENT; + return (-1); + } + return (hashp->fp); +} + +/************************** LOCAL CREATION ROUTINES **********************/ +static HTAB * +init_hash(hashp, file, info) + HTAB *hashp; + const char *file; + HASHINFO *info; +{ + struct stat statbuf; + int nelem; + + nelem = 1; + hashp->NKEYS = 0; + hashp->LORDER = BYTE_ORDER; + hashp->BSIZE = DEF_BUCKET_SIZE; + hashp->BSHIFT = DEF_BUCKET_SHIFT; + hashp->SGSIZE = DEF_SEGSIZE; + hashp->SSHIFT = DEF_SEGSIZE_SHIFT; + hashp->DSIZE = DEF_DIRSIZE; + hashp->FFACTOR = DEF_FFACTOR; + hashp->hash = __default_hash; + memset(hashp->SPARES, 0, sizeof(hashp->SPARES)); + memset(hashp->BITMAPS, 0, sizeof (hashp->BITMAPS)); + + /* Fix bucket size to be optimal for file system */ + if (file != NULL) { + if (stat(file, &statbuf)) + return (NULL); + hashp->BSIZE = statbuf.st_blksize; + hashp->BSHIFT = __log2(hashp->BSIZE); + } + + if (info) { + if (info->bsize) { + /* Round pagesize up to power of 2 */ + hashp->BSHIFT = __log2(info->bsize); + hashp->BSIZE = 1 << hashp->BSHIFT; + if (hashp->BSIZE > MAX_BSIZE) { + errno = EINVAL; + return (NULL); + } + } + if (info->ffactor) + hashp->FFACTOR = info->ffactor; + if (info->hash) + hashp->hash = info->hash; + if (info->nelem) + nelem = info->nelem; + if (info->lorder) { + if (info->lorder != BIG_ENDIAN && + info->lorder != LITTLE_ENDIAN) { + errno = EINVAL; + return (NULL); + } + hashp->LORDER = info->lorder; + } + } + /* init_htab should destroy the table and set errno if it fails */ + if (init_htab(hashp, nelem)) + return (NULL); + else + return (hashp); +} +/* + * This calls alloc_segs which may run out of memory. Alloc_segs will destroy + * the table and set errno, so we just pass the error information along. + * + * Returns 0 on No Error + */ +static int +init_htab(hashp, nelem) + HTAB *hashp; + int nelem; +{ + register int nbuckets, nsegs; + int l2; + + /* + * Divide number of elements by the fill factor and determine a + * desired number of buckets. Allocate space for the next greater + * power of two number of buckets. + */ + nelem = (nelem - 1) / hashp->FFACTOR + 1; + + l2 = __log2(MAX(nelem, 2)); + nbuckets = 1 << l2; + + hashp->SPARES[l2] = l2 + 1; + hashp->SPARES[l2 + 1] = l2 + 1; + hashp->OVFL_POINT = l2; + hashp->LAST_FREED = 2; + + /* First bitmap page is at: splitpoint l2 page offset 1 */ + if (__init_bitmap(hashp, OADDR_OF(l2, 1), l2 + 1, 0)) + return (-1); + + hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1; + hashp->HIGH_MASK = (nbuckets << 1) - 1; + hashp->HDRPAGES = ((MAX(sizeof(HASHHDR), MINHDRSIZE) - 1) >> + hashp->BSHIFT) + 1; + + nsegs = (nbuckets - 1) / hashp->SGSIZE + 1; + nsegs = 1 << __log2(nsegs); + + if (nsegs > hashp->DSIZE) + hashp->DSIZE = nsegs; + return (alloc_segs(hashp, nsegs)); +} + +/********************** DESTROY/CLOSE ROUTINES ************************/ + +/* + * Flushes any changes to the file if necessary and destroys the hashp + * structure, freeing all allocated space. + */ +static int +hdestroy(hashp) + HTAB *hashp; +{ + int i, save_errno; + + save_errno = 0; + +#ifdef HASH_STATISTICS + (void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n", + hash_accesses, hash_collisions); + (void)fprintf(stderr, "hdestroy: expansions %ld\n", + hash_expansions); + (void)fprintf(stderr, "hdestroy: overflows %ld\n", + hash_overflows); + (void)fprintf(stderr, "keys %ld maxp %d segmentcount %d\n", + hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs); + + for (i = 0; i < NCACHED; i++) + (void)fprintf(stderr, + "spares[%d] = %d\n", i, hashp->SPARES[i]); +#endif + /* + * Call on buffer manager to free buffers, and if required, + * write them to disk. + */ + if (__buf_free(hashp, 1, hashp->save_file)) + save_errno = errno; + if (hashp->dir) { + free(*hashp->dir); /* Free initial segments */ + /* Free extra segments */ + while (hashp->exsegs--) + free(hashp->dir[--hashp->nsegs]); + free(hashp->dir); + } + if (flush_meta(hashp) && !save_errno) + save_errno = errno; + /* Free Bigmaps */ + for (i = 0; i < hashp->nmaps; i++) + if (hashp->mapp[i]) + free(hashp->mapp[i]); + + if (hashp->fp != -1) + (void)close(hashp->fp); + + if (save_errno) { + errno = save_errno; + return (ERROR); + } + return (SUCCESS); +} +/* + * Write modified pages to disk + * + * Returns: + * 0 == OK + * -1 ERROR + */ +static int +hash_sync(dbp, flags) + const DB *dbp; + u_int flags; +{ + HTAB *hashp; + + if (flags != 0) { + errno = EINVAL; + return (ERROR); + } + + if (!dbp) + return (ERROR); + + hashp = (HTAB *)dbp->internal; + if (!hashp->save_file) + return (0); + if (__buf_free(hashp, 0, 1) || flush_meta(hashp)) + return (ERROR); + hashp->new_file = 0; + return (0); +} + +/* + * Returns: + * 0 == OK + * -1 indicates that errno should be set + */ +static int +flush_meta(hashp) + HTAB *hashp; +{ + HASHHDR *whdrp; +#if BYTE_ORDER == LITTLE_ENDIAN + HASHHDR whdr; +#endif + int fp, i, wsize; + + if (!hashp->save_file) + return (0); + hashp->MAGIC = HASHMAGIC; + hashp->VERSION = HASHVERSION; + hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY)); + + fp = hashp->fp; + whdrp = &hashp->hdr; +#if BYTE_ORDER == LITTLE_ENDIAN + whdrp = &whdr; + swap_header_copy(&hashp->hdr, whdrp); +#endif + if ((lseek(fp, (off_t)0, SEEK_SET) == -1) || + ((wsize = write(fp, whdrp, sizeof(HASHHDR))) == -1)) + return (-1); + else + if (wsize != sizeof(HASHHDR)) { + errno = EFTYPE; + hashp->errno = errno; + return (-1); + } + for (i = 0; i < NCACHED; i++) + if (hashp->mapp[i]) + if (__put_page(hashp, (char *)hashp->mapp[i], + hashp->BITMAPS[i], 0, 1)) + return (-1); + return (0); +} + +/*******************************SEARCH ROUTINES *****************************/ +/* + * All the access routines return + * + * Returns: + * 0 on SUCCESS + * 1 to indicate an external ERROR (i.e. key not found, etc) + * -1 to indicate an internal ERROR (i.e. out of memory, etc) + */ +static int +hash_get(dbp, key, data, flag) + const DB *dbp; + const DBT *key; + DBT *data; + u_int flag; +{ + HTAB *hashp; + + hashp = (HTAB *)dbp->internal; + if (flag) { + hashp->errno = errno = EINVAL; + return (ERROR); + } + return (hash_access(hashp, HASH_GET, (DBT *)key, data)); +} + +static int +hash_put(dbp, key, data, flag) + const DB *dbp; + DBT *key; + const DBT *data; + u_int flag; +{ + HTAB *hashp; + + hashp = (HTAB *)dbp->internal; + if (flag && flag != R_NOOVERWRITE) { + hashp->errno = errno = EINVAL; + return (ERROR); + } + if ((hashp->flags & O_ACCMODE) == O_RDONLY) { + hashp->errno = errno = EPERM; + return (ERROR); + } + return (hash_access(hashp, flag == R_NOOVERWRITE ? + HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data)); +} + +static int +hash_delete(dbp, key, flag) + const DB *dbp; + const DBT *key; + u_int flag; /* Ignored */ +{ + HTAB *hashp; + + hashp = (HTAB *)dbp->internal; + if (flag && flag != R_CURSOR) { + hashp->errno = errno = EINVAL; + return (ERROR); + } + if ((hashp->flags & O_ACCMODE) == O_RDONLY) { + hashp->errno = errno = EPERM; + return (ERROR); + } + return (hash_access(hashp, HASH_DELETE, (DBT *)key, NULL)); +} + +/* + * Assume that hashp has been set in wrapper routine. + */ +static int +hash_access(hashp, action, key, val) + HTAB *hashp; + ACTION action; + DBT *key, *val; +{ + register BUFHEAD *rbufp; + BUFHEAD *bufp, *save_bufp; + register u_short *bp; + register int n, ndx, off, size; + register char *kp; + u_short pageno; + +#ifdef HASH_STATISTICS + hash_accesses++; +#endif + + off = hashp->BSIZE; + size = key->size; + kp = (char *)key->data; + rbufp = __get_buf(hashp, __call_hash(hashp, kp, size), NULL, 0); + if (!rbufp) + return (ERROR); + save_bufp = rbufp; + + /* Pin the bucket chain */ + rbufp->flags |= BUF_PIN; + for (bp = (u_short *)rbufp->page, n = *bp++, ndx = 1; ndx < n;) + if (bp[1] >= REAL_KEY) { + /* Real key/data pair */ + if (size == off - *bp && + memcmp(kp, rbufp->page + *bp, size) == 0) + goto found; + off = bp[1]; +#ifdef HASH_STATISTICS + hash_collisions++; +#endif + bp += 2; + ndx += 2; + } else if (bp[1] == OVFLPAGE) { + rbufp = __get_buf(hashp, *bp, rbufp, 0); + if (!rbufp) { + save_bufp->flags &= ~BUF_PIN; + return (ERROR); + } + /* FOR LOOP INIT */ + bp = (u_short *)rbufp->page; + n = *bp++; + ndx = 1; + off = hashp->BSIZE; + } else if (bp[1] < REAL_KEY) { + if ((ndx = + __find_bigpair(hashp, rbufp, ndx, kp, size)) > 0) + goto found; + if (ndx == -2) { + bufp = rbufp; + if (!(pageno = + __find_last_page(hashp, &bufp))) { + ndx = 0; + rbufp = bufp; + break; /* FOR */ + } + rbufp = __get_buf(hashp, pageno, bufp, 0); + if (!rbufp) { + save_bufp->flags &= ~BUF_PIN; + return (ERROR); + } + /* FOR LOOP INIT */ + bp = (u_short *)rbufp->page; + n = *bp++; + ndx = 1; + off = hashp->BSIZE; + } else { + save_bufp->flags &= ~BUF_PIN; + return (ERROR); + } + } + + /* Not found */ + switch (action) { + case HASH_PUT: + case HASH_PUTNEW: + if (__addel(hashp, rbufp, key, val)) { + save_bufp->flags &= ~BUF_PIN; + return (ERROR); + } else { + save_bufp->flags &= ~BUF_PIN; + return (SUCCESS); + } + case HASH_GET: + case HASH_DELETE: + default: + save_bufp->flags &= ~BUF_PIN; + return (ABNORMAL); + } + +found: + switch (action) { + case HASH_PUTNEW: + save_bufp->flags &= ~BUF_PIN; + return (ABNORMAL); + case HASH_GET: + bp = (u_short *)rbufp->page; + if (bp[ndx + 1] < REAL_KEY) { + if (__big_return(hashp, rbufp, ndx, val, 0)) + return (ERROR); + } else { + val->data = (u_char *)rbufp->page + (int)bp[ndx + 1]; + val->size = bp[ndx] - bp[ndx + 1]; + } + break; + case HASH_PUT: + if ((__delpair(hashp, rbufp, ndx)) || + (__addel(hashp, rbufp, key, val))) { + save_bufp->flags &= ~BUF_PIN; + return (ERROR); + } + break; + case HASH_DELETE: + if (__delpair(hashp, rbufp, ndx)) + return (ERROR); + break; + default: + abort(); + } + save_bufp->flags &= ~BUF_PIN; + return (SUCCESS); +} + +static int +hash_seq(dbp, key, data, flag) + const DB *dbp; + DBT *key, *data; + u_int flag; +{ + register u_int bucket; + register BUFHEAD *bufp; + HTAB *hashp; + u_short *bp, ndx; + + hashp = (HTAB *)dbp->internal; + if (flag && flag != R_FIRST && flag != R_NEXT) { + hashp->errno = errno = EINVAL; + return (ERROR); + } +#ifdef HASH_STATISTICS + hash_accesses++; +#endif + if ((hashp->cbucket < 0) || (flag == R_FIRST)) { + hashp->cbucket = 0; + hashp->cndx = 1; + hashp->cpage = NULL; + } + + for (bp = NULL; !bp || !bp[0]; ) { + if (!(bufp = hashp->cpage)) { + for (bucket = hashp->cbucket; + bucket <= hashp->MAX_BUCKET; + bucket++, hashp->cndx = 1) { + bufp = __get_buf(hashp, bucket, NULL, 0); + if (!bufp) + return (ERROR); + hashp->cpage = bufp; + bp = (u_short *)bufp->page; + if (bp[0]) + break; + } + hashp->cbucket = bucket; + if (hashp->cbucket > hashp->MAX_BUCKET) { + hashp->cbucket = -1; + return (ABNORMAL); + } + } else + bp = (u_short *)hashp->cpage->page; + +#ifdef DEBUG + assert(bp); + assert(bufp); +#endif + while (bp[hashp->cndx + 1] == OVFLPAGE) { + bufp = hashp->cpage = + __get_buf(hashp, bp[hashp->cndx], bufp, 0); + if (!bufp) + return (ERROR); + bp = (u_short *)(bufp->page); + hashp->cndx = 1; + } + if (!bp[0]) { + hashp->cpage = NULL; + ++hashp->cbucket; + } + } + ndx = hashp->cndx; + if (bp[ndx + 1] < REAL_KEY) { + if (__big_keydata(hashp, bufp, key, data, 1)) + return (ERROR); + } else { + key->data = (u_char *)hashp->cpage->page + bp[ndx]; + key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx]; + data->data = (u_char *)hashp->cpage->page + bp[ndx + 1]; + data->size = bp[ndx] - bp[ndx + 1]; + ndx += 2; + if (ndx > bp[0]) { + hashp->cpage = NULL; + hashp->cbucket++; + hashp->cndx = 1; + } else + hashp->cndx = ndx; + } + return (SUCCESS); +} + +/********************************* UTILITIES ************************/ + +/* + * Returns: + * 0 ==> OK + * -1 ==> Error + */ +extern int +__expand_table(hashp) + HTAB *hashp; +{ + u_int old_bucket, new_bucket; + int dirsize, new_segnum, spare_ndx; + +#ifdef HASH_STATISTICS + hash_expansions++; +#endif + new_bucket = ++hashp->MAX_BUCKET; + old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK); + + new_segnum = new_bucket >> hashp->SSHIFT; + + /* Check if we need a new segment */ + if (new_segnum >= hashp->nsegs) { + /* Check if we need to expand directory */ + if (new_segnum >= hashp->DSIZE) { + /* Reallocate directory */ + dirsize = hashp->DSIZE * sizeof(SEGMENT *); + if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1)) + return (-1); + hashp->DSIZE = dirsize << 1; + } + if (!(hashp->dir[new_segnum] = + calloc(hashp->SGSIZE, sizeof(SEGMENT)))) + return (-1); + hashp->exsegs++; + hashp->nsegs++; + } + /* + * If the split point is increasing (MAX_BUCKET's log base 2 + * * increases), we need to copy the current contents of the spare + * split bucket to the next bucket. + */ + spare_ndx = __log2(hashp->MAX_BUCKET + 1); + if (spare_ndx > hashp->OVFL_POINT) { + hashp->SPARES[spare_ndx] = hashp->SPARES[hashp->OVFL_POINT]; + hashp->OVFL_POINT = spare_ndx; + } + + if (new_bucket > hashp->HIGH_MASK) { + /* Starting a new doubling */ + hashp->LOW_MASK = hashp->HIGH_MASK; + hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK; + } + /* Relocate records to the new bucket */ + return (__split_page(hashp, old_bucket, new_bucket)); +} + +/* + * If realloc guarantees that the pointer is not destroyed if the realloc + * fails, then this routine can go away. + */ +static void * +hash_realloc(p_ptr, oldsize, newsize) + SEGMENT **p_ptr; + int oldsize, newsize; +{ + register void *p; + + if (p = malloc(newsize)) { + memmove(p, *p_ptr, oldsize); + memset(p + oldsize, 0, newsize - oldsize); + free(*p_ptr); + *p_ptr = p; + } + return (p); +} + +extern u_int +__call_hash(hashp, k, len) + HTAB *hashp; + char *k; + int len; +{ + int n, bucket; + + n = hashp->hash(k, len); + bucket = n & hashp->HIGH_MASK; + if (bucket > hashp->MAX_BUCKET) + bucket = bucket & hashp->LOW_MASK; + return (bucket); +} + +/* + * Allocate segment table. On error, destroy the table and set errno. + * + * Returns 0 on success + */ +static int +alloc_segs(hashp, nsegs) + HTAB *hashp; + int nsegs; +{ + register int i; + register SEGMENT store; + + int save_errno; + + if (!(hashp->dir = calloc(hashp->DSIZE, sizeof(SEGMENT *)))) { + save_errno = errno; + (void)hdestroy(hashp); + errno = save_errno; + return (-1); + } + /* Allocate segments */ + store = calloc(nsegs << hashp->SSHIFT, sizeof(SEGMENT)); + if (!store) { + save_errno = errno; + (void)hdestroy(hashp); + errno = save_errno; + return (-1); + } + for (i = 0; i < nsegs; i++, hashp->nsegs++) + hashp->dir[i] = &store[i << hashp->SSHIFT]; + return (0); +} + +#if BYTE_ORDER == LITTLE_ENDIAN +/* + * Hashp->hdr needs to be byteswapped. + */ +static void +swap_header_copy(srcp, destp) + HASHHDR *srcp, *destp; +{ + int i; + + BLSWAP_COPY(srcp->magic, destp->magic); + BLSWAP_COPY(srcp->version, destp->version); + BLSWAP_COPY(srcp->lorder, destp->lorder); + BLSWAP_COPY(srcp->bsize, destp->bsize); + BLSWAP_COPY(srcp->bshift, destp->bshift); + BLSWAP_COPY(srcp->dsize, destp->dsize); + BLSWAP_COPY(srcp->ssize, destp->ssize); + BLSWAP_COPY(srcp->sshift, destp->sshift); + BLSWAP_COPY(srcp->ovfl_point, destp->ovfl_point); + BLSWAP_COPY(srcp->last_freed, destp->last_freed); + BLSWAP_COPY(srcp->max_bucket, destp->max_bucket); + BLSWAP_COPY(srcp->high_mask, destp->high_mask); + BLSWAP_COPY(srcp->low_mask, destp->low_mask); + BLSWAP_COPY(srcp->ffactor, destp->ffactor); + BLSWAP_COPY(srcp->nkeys, destp->nkeys); + BLSWAP_COPY(srcp->hdrpages, destp->hdrpages); + BLSWAP_COPY(srcp->h_charkey, destp->h_charkey); + for (i = 0; i < NCACHED; i++) { + BLSWAP_COPY(srcp->spares[i], destp->spares[i]); + BSSWAP_COPY(srcp->bitmaps[i], destp->bitmaps[i]); + } +} + +static void +swap_header(hashp) + HTAB *hashp; +{ + HASHHDR *hdrp; + int i; + + hdrp = &hashp->hdr; + + BLSWAP(hdrp->magic); + BLSWAP(hdrp->version); + BLSWAP(hdrp->lorder); + BLSWAP(hdrp->bsize); + BLSWAP(hdrp->bshift); + BLSWAP(hdrp->dsize); + BLSWAP(hdrp->ssize); + BLSWAP(hdrp->sshift); + BLSWAP(hdrp->ovfl_point); + BLSWAP(hdrp->last_freed); + BLSWAP(hdrp->max_bucket); + BLSWAP(hdrp->high_mask); + BLSWAP(hdrp->low_mask); + BLSWAP(hdrp->ffactor); + BLSWAP(hdrp->nkeys); + BLSWAP(hdrp->hdrpages); + BLSWAP(hdrp->h_charkey); + for (i = 0; i < NCACHED; i++) { + BLSWAP(hdrp->spares[i]); + BSSWAP(hdrp->bitmaps[i]); + } +} +#endif diff --git a/lib/libc/db/hash/hash.h b/lib/libc/db/hash/hash.h index 857c6339d3..c98156d6d4 100644 --- a/lib/libc/db/hash/hash.h +++ b/lib/libc/db/hash/hash.h @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. @@ -33,239 +33,242 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)hash.h 5.4 (Berkeley) 3/12/91 + * @(#)hash.h 8.1 (Berkeley) 6/4/93 */ /* Operations */ -typedef enum { HASH_GET, HASH_PUT, HASH_PUTNEW, HASH_DELETE, - HASH_FIRST, HASH_NEXT } ACTION; +typedef enum { + HASH_GET, HASH_PUT, HASH_PUTNEW, HASH_DELETE, HASH_FIRST, HASH_NEXT +} ACTION; /* Buffer Management structures */ typedef struct _bufhead BUFHEAD; struct _bufhead { - BUFHEAD *prev; /* LRU links */ - BUFHEAD *next; /* LRU links */ - BUFHEAD *ovfl; /* Overflow page buffer header */ - u_int addr; /* Address of this page */ - char *page; /* Actual page data */ - char flags; + BUFHEAD *prev; /* LRU links */ + BUFHEAD *next; /* LRU links */ + BUFHEAD *ovfl; /* Overflow page buffer header */ + u_int addr; /* Address of this page */ + char *page; /* Actual page data */ + char flags; #define BUF_MOD 0x0001 #define BUF_DISK 0x0002 #define BUF_BUCKET 0x0004 #define BUF_PIN 0x0008 }; +#define IS_BUCKET(X) ((X) & BUF_BUCKET) -#define IS_BUCKET(X) (X & BUF_BUCKET) - -typedef BUFHEAD **SEGMENT; +typedef BUFHEAD **SEGMENT; /* Hash Table Information */ typedef struct hashhdr { /* Disk resident portion */ - int magic; /* Magic NO for hash tables */ - int version; /* Version ID */ - long lorder; /* Byte Order */ - int bsize; /* Bucket/Page Size */ - int bshift; /* Bucket shift */ - int dsize; /* Directory Size */ - int ssize; /* Segment Size */ - int sshift; /* Segment shift */ - int max_bucket; /* ID of Maximum bucket in use */ - int high_mask; /* Mask to modulo into entire table */ - int low_mask; /* Mask to modulo into lower half of table */ - int ffactor; /* Fill factor */ - int nkeys; /* Number of keys in hash table */ - int hdrpages; /* Size of table header */ - int h_charkey; /* value of hash(CHARKEY) */ -# define NCACHED 32 /* number of bit maps and spare points*/ - int spares[NCACHED]; /* spare pages for overflow */ - u_short bitmaps[NCACHED]; /* address of overflow page bitmaps */ + int magic; /* Magic NO for hash tables */ + int version; /* Version ID */ + long lorder; /* Byte Order */ + int bsize; /* Bucket/Page Size */ + int bshift; /* Bucket shift */ + int dsize; /* Directory Size */ + int ssize; /* Segment Size */ + int sshift; /* Segment shift */ + int ovfl_point; /* Where overflow pages are being allocated */ + int last_freed; /* Last overflow page freed */ + int max_bucket; /* ID of Maximum bucket in use */ + int high_mask; /* Mask to modulo into entire table */ + int low_mask; /* Mask to modulo into lower half of table */ + int ffactor; /* Fill factor */ + int nkeys; /* Number of keys in hash table */ + int hdrpages; /* Size of table header */ + int h_charkey; /* value of hash(CHARKEY) */ +#define NCACHED 32 /* number of bit maps and spare points */ + int spares[NCACHED];/* spare pages for overflow */ + u_short bitmaps[NCACHED]; /* address of overflow page bitmaps */ } HASHHDR; -typedef struct htab { /* Memory resident data structure */ - HASHHDR hdr; /* Header */ - int nsegs; /* Number of allocated segments */ - int exsegs; /* Number of extra allocated segments */ - int (*hash)(); /* Hash Function */ - int flags; /* Flag values */ - int fp; /* File pointer */ - char *tmp_buf; /* Temporary Buffer for BIG data */ - char *tmp_key; /* Temporary Buffer for BIG keys */ - BUFHEAD *cpage; /* Current page */ - int cbucket; /* Current bucket */ - int cndx; /* Index of next item on cpage */ - int errno; /* Error Number -- for DBM compatability */ - int new_file; /* Indicates whether fd is backing store or no */ - int save_file; /* Indicates whether we need to flush file at exit */ +typedef struct htab { /* Memory resident data structure */ + HASHHDR hdr; /* Header */ + int nsegs; /* Number of allocated segments */ + int exsegs; /* Number of extra allocated segments */ + int (*hash) (); /* Hash Function */ + int flags; /* Flag values */ + int fp; /* File pointer */ + char *tmp_buf; /* Temporary Buffer for BIG data */ + char *tmp_key; /* Temporary Buffer for BIG keys */ + BUFHEAD *cpage; /* Current page */ + int cbucket; /* Current bucket */ + int cndx; /* Index of next item on cpage */ + int errno; /* Error Number -- for DBM compatability */ + int new_file; /* Indicates if fd is backing store or no */ + int save_file; /* Indicates whether we need to flush file at + * exit */ u_long *mapp[NCACHED]; /* Pointers to page maps */ - int nmaps; /* Initial number of bitmaps */ - int nbufs; /* Number of buffers left to allocate */ - BUFHEAD bufhead; /* Header of buffer lru list */ - SEGMENT *dir; /* Hash Bucket directory */ + int nmaps; /* Initial number of bitmaps */ + int nbufs; /* Number of buffers left to allocate */ + BUFHEAD bufhead; /* Header of buffer lru list */ + SEGMENT *dir; /* Hash Bucket directory */ } HTAB; - /* * Constants */ -#define MAX_BSIZE 65536 /* 2^16 */ +#define MAX_BSIZE 65536 /* 2^16 */ #define MIN_BUFFERS 6 #define MINHDRSIZE 512 -#define DEF_BUFSIZE 65536 /* 64 K */ -#define DEF_BUCKET_SIZE 256 -#define DEF_BUCKET_SHIFT 8 /* log2(BUCKET) */ +#define DEF_BUFSIZE 65536 /* 64 K */ +#define DEF_BUCKET_SIZE 4096 +#define DEF_BUCKET_SHIFT 12 /* log2(BUCKET) */ #define DEF_SEGSIZE 256 -#define DEF_SEGSIZE_SHIFT 8 /* log2(SEGSIZE) */ +#define DEF_SEGSIZE_SHIFT 8 /* log2(SEGSIZE) */ #define DEF_DIRSIZE 256 -#define DEF_FFACTOR 5 -#define SPLTMAX 8 -#define CHARKEY "%$sniglet^&" +#define DEF_FFACTOR 65536 +#define MIN_FFACTOR 4 +#define SPLTMAX 8 +#define CHARKEY "%$sniglet^&" #define NUMKEY 1038583 -#define VERSION_NO 3 #define BYTE_SHIFT 3 #define INT_TO_BYTE 2 #define INT_BYTE_SHIFT 5 -#define ALL_SET ((unsigned)0xFFFFFFFF) +#define ALL_SET ((u_int)0xFFFFFFFF) #define ALL_CLEAR 0 +#define PTROF(X) ((BUFHEAD *)((u_int)(X)&~0x3)) +#define ISMOD(X) ((u_int)(X)&0x1) +#define DOMOD(X) ((X) = (char *)((u_int)(X)|0x1)) +#define ISDISK(X) ((u_int)(X)&0x2) +#define DODISK(X) ((X) = (char *)((u_int)(X)|0x2)) -#define PTROF(X) ((BUFHEAD *)((unsigned)(X)&~0x3)) -#define ISMOD(X) ((unsigned)(X)&0x1) -#define DOMOD(X) (X = (char *)( (unsigned)X | 0x1)) -#define ISDISK(X) ((unsigned)(X)&0x2) -#define DODISK(X) (X = (char *)( (unsigned)X | 0x2)) - -#define BITS_PER_MAP 32 +#define BITS_PER_MAP 32 /* Given the address of the beginning of a big map, clear/set the nth bit */ - -#define CLRBIT(A,N) ((A)[N/BITS_PER_MAP] &= ~(1<<(N%BITS_PER_MAP))) -#define SETBIT(A,N) ((A)[N/BITS_PER_MAP] |= (1<<(N%BITS_PER_MAP))) -#define ISSET(A,N) ((A)[N/BITS_PER_MAP] & (1<<(N%BITS_PER_MAP))) +#define CLRBIT(A, N) ((A)[(N)/BITS_PER_MAP] &= ~(1<<((N)%BITS_PER_MAP))) +#define SETBIT(A, N) ((A)[(N)/BITS_PER_MAP] |= (1<<((N)%BITS_PER_MAP))) +#define ISSET(A, N) ((A)[(N)/BITS_PER_MAP] & (1<<((N)%BITS_PER_MAP))) /* Overflow management */ /* - Overflow page numbers are allocated per split point. - At each doubling of the table, we can allocate extra - pages. So, an overflow page number has the top 5 bits - indicate which split point and the lower 11 bits indicate - which page at that split point is indicated (pages within - split points are numberered starting with 1). - - -*/ + * Overflow page numbers are allocated per split point. At each doubling of + * the table, we can allocate extra pages. So, an overflow page number has + * the top 5 bits indicate which split point and the lower 11 bits indicate + * which page at that split point is indicated (pages within split points are + * numberered starting with 1). + */ #define SPLITSHIFT 11 #define SPLITMASK 0x7FF -#define SPLITNUM(N) (((unsigned)N) >> SPLITSHIFT) -#define OPAGENUM(N) (N & SPLITMASK) -#define OADDR_OF(S,O) ((unsigned)((unsigned)S << SPLITSHIFT) + O) +#define SPLITNUM(N) (((u_int)(N)) >> SPLITSHIFT) +#define OPAGENUM(N) ((N) & SPLITMASK) +#define OADDR_OF(S,O) ((u_int)((u_int)(S) << SPLITSHIFT) + (O)) #define BUCKET_TO_PAGE(B) \ - B + hashp->HDRPAGES + (B ? hashp->SPARES[__log2(B+1)-1] : 0) + (B) + hashp->HDRPAGES + ((B) ? hashp->SPARES[__log2((B)+1)-1] : 0) #define OADDR_TO_PAGE(B) \ - BUCKET_TO_PAGE ( (1 << SPLITNUM(B)) -1 ) + OPAGENUM(B); + BUCKET_TO_PAGE ( (1 << SPLITNUM((B))) -1 ) + OPAGENUM((B)); /* - page.h contains a detailed description of the page format. - - Normally, keys and data are accessed from offset tables in the - top of each page which point to the beginning of the key and - data. There are four flag values which may be stored in these - offset tables which indicate the following: - - OVFLPAGE Rather than a key data pair, this pair contains - the address of an overflow page. The format of - the pair is: - OVERFLOW_PAGE_NUMBER OVFLPAGE - - PARTIAL_KEY This must be the first key/data pair on a page - and implies that page contains only a partial key. - That is, the key is too big to fit on a single page - so it starts on this page and continues on the next. - The format of the page is: - KEY_OFF PARTIAL_KEY OVFL_PAGENO OVFLPAGE - - KEY_OFF -- offset of the beginning of the key - PARTIAL_KEY -- 1 - OVFL_PAGENO - page number of the next overflow page - OVFLPAGE -- 0 - FULL_KEY This must be the first key/data pair on the page. It - is used in two cases. - - Case 1: - There is a complete key on the page but no data - (because it wouldn't fit). The next page contains - the data. - - Page format it: - KEY_OFF FULL_KEY OVFL_PAGENO OVFL_PAGE - - KEY_OFF -- offset of the beginning of the key - FULL_KEY -- 2 - OVFL_PAGENO - page number of the next overflow page - OVFLPAGE -- 0 - - Case 2: - This page contains no key, but part of a large - data field, which is continued on the next page. - - Page format it: - DATA_OFF FULL_KEY OVFL_PAGENO OVFL_PAGE - - KEY_OFF -- offset of the beginning of the data on - this page - FULL_KEY -- 2 - OVFL_PAGENO - page number of the next overflow page - OVFLPAGE -- 0 - - FULL_KEY_DATA This must be the first key/data pair on the page. - There are two cases: - - Case 1: - This page contains a key and the beginning of the - data field, but the data field is continued on the - next page. - - Page format is: - KEY_OFF FULL_KEY_DATA OVFL_PAGENO DATA_OFF - - KEY_OFF -- offset of the beginning of the key - FULL_KEY_DATA -- 3 - OVFL_PAGENO - page number of the next overflow page - DATA_OFF -- offset of the beginning of the data - - Case 2: - This page contains the last page of a big data pair. - There is no key, only the tail end of the data - on this page. - - Page format is: - DATA_OFF FULL_KEY_DATA - - DATA_OFF -- offset of the beginning of the data on - this page - FULL_KEY_DATA -- 3 - OVFL_PAGENO - page number of the next overflow page - OVFLPAGE -- 0 - - OVFL_PAGENO and OVFLPAGE are optional (they are - not present if there is no next page). -*/ + * page.h contains a detailed description of the page format. + * + * Normally, keys and data are accessed from offset tables in the top of + * each page which point to the beginning of the key and data. There are + * four flag values which may be stored in these offset tables which indicate + * the following: + * + * + * OVFLPAGE Rather than a key data pair, this pair contains + * the address of an overflow page. The format of + * the pair is: + * OVERFLOW_PAGE_NUMBER OVFLPAGE + * + * PARTIAL_KEY This must be the first key/data pair on a page + * and implies that page contains only a partial key. + * That is, the key is too big to fit on a single page + * so it starts on this page and continues on the next. + * The format of the page is: + * KEY_OFF PARTIAL_KEY OVFL_PAGENO OVFLPAGE + * + * KEY_OFF -- offset of the beginning of the key + * PARTIAL_KEY -- 1 + * OVFL_PAGENO - page number of the next overflow page + * OVFLPAGE -- 0 + * + * FULL_KEY This must be the first key/data pair on the page. It + * is used in two cases. + * + * Case 1: + * There is a complete key on the page but no data + * (because it wouldn't fit). The next page contains + * the data. + * + * Page format it: + * KEY_OFF FULL_KEY OVFL_PAGENO OVFL_PAGE + * + * KEY_OFF -- offset of the beginning of the key + * FULL_KEY -- 2 + * OVFL_PAGENO - page number of the next overflow page + * OVFLPAGE -- 0 + * + * Case 2: + * This page contains no key, but part of a large + * data field, which is continued on the next page. + * + * Page format it: + * DATA_OFF FULL_KEY OVFL_PAGENO OVFL_PAGE + * + * KEY_OFF -- offset of the beginning of the data on + * this page + * FULL_KEY -- 2 + * OVFL_PAGENO - page number of the next overflow page + * OVFLPAGE -- 0 + * + * FULL_KEY_DATA + * This must be the first key/data pair on the page. + * There are two cases: + * + * Case 1: + * This page contains a key and the beginning of the + * data field, but the data field is continued on the + * next page. + * + * Page format is: + * KEY_OFF FULL_KEY_DATA OVFL_PAGENO DATA_OFF + * + * KEY_OFF -- offset of the beginning of the key + * FULL_KEY_DATA -- 3 + * OVFL_PAGENO - page number of the next overflow page + * DATA_OFF -- offset of the beginning of the data + * + * Case 2: + * This page contains the last page of a big data pair. + * There is no key, only the tail end of the data + * on this page. + * + * Page format is: + * DATA_OFF FULL_KEY_DATA + * + * DATA_OFF -- offset of the beginning of the data on + * this page + * FULL_KEY_DATA -- 3 + * OVFL_PAGENO - page number of the next overflow page + * OVFLPAGE -- 0 + * + * OVFL_PAGENO and OVFLPAGE are optional (they are + * not present if there is no next page). + */ #define OVFLPAGE 0 #define PARTIAL_KEY 1 #define FULL_KEY 2 #define FULL_KEY_DATA 3 #define REAL_KEY 4 + /* Short hands for accessing structure */ -#define BSIZE hdr.bsize -#define BSHIFT hdr.bshift -#define DSIZE hdr.dsize -#define SGSIZE hdr.ssize -#define SSHIFT hdr.sshift -#define LORDER hdr.lorder +#define BSIZE hdr.bsize +#define BSHIFT hdr.bshift +#define DSIZE hdr.dsize +#define SGSIZE hdr.ssize +#define SSHIFT hdr.sshift +#define LORDER hdr.lorder +#define OVFL_POINT hdr.ovfl_point +#define LAST_FREED hdr.last_freed #define MAX_BUCKET hdr.max_bucket #define FFACTOR hdr.ffactor #define HIGH_MASK hdr.high_mask diff --git a/lib/libc/db/hash/hash_bigkey.c b/lib/libc/db/hash/hash_bigkey.c new file mode 100644 index 0000000000..f24fcec38c --- /dev/null +++ b/lib/libc/db/hash/hash_bigkey.c @@ -0,0 +1,669 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)hash_bigkey.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +/* + * PACKAGE: hash + * DESCRIPTION: + * Big key/data handling for the hashing package. + * + * ROUTINES: + * External + * __big_keydata + * __big_split + * __big_insert + * __big_return + * __big_delete + * __find_last_page + * Internal + * collect_key + * collect_data + */ + +#include + +#include +#include +#include +#include + +#ifdef DEBUG +#include +#endif + +#include +#include "hash.h" +#include "page.h" +#include "extern.h" + +static int collect_key __P((HTAB *, BUFHEAD *, int, DBT *, int)); +static int collect_data __P((HTAB *, BUFHEAD *, int, int)); + +/* + * Big_insert + * + * You need to do an insert and the key/data pair is too big + * + * Returns: + * 0 ==> OK + *-1 ==> ERROR + */ +extern int +__big_insert(hashp, bufp, key, val) + HTAB *hashp; + BUFHEAD *bufp; + const DBT *key, *val; +{ + register u_short *p; + int key_size, n, val_size; + u_short space, move_bytes, off; + char *cp, *key_data, *val_data; + + cp = bufp->page; /* Character pointer of p. */ + p = (u_short *)cp; + + key_data = (char *)key->data; + key_size = key->size; + val_data = (char *)val->data; + val_size = val->size; + + /* First move the Key */ + for (space = FREESPACE(p) - BIGOVERHEAD; key_size; + space = FREESPACE(p) - BIGOVERHEAD) { + move_bytes = MIN(space, key_size); + off = OFFSET(p) - move_bytes; + memmove(cp + off, key_data, move_bytes); + key_size -= move_bytes; + key_data += move_bytes; + n = p[0]; + p[++n] = off; + p[0] = ++n; + FREESPACE(p) = off - PAGE_META(n); + OFFSET(p) = off; + p[n] = PARTIAL_KEY; + bufp = __add_ovflpage(hashp, bufp); + if (!bufp) + return (-1); + n = p[0]; + if (!key_size) + if (FREESPACE(p)) { + move_bytes = MIN(FREESPACE(p), val_size); + off = OFFSET(p) - move_bytes; + p[n] = off; + memmove(cp + off, val_data, move_bytes); + val_data += move_bytes; + val_size -= move_bytes; + p[n - 2] = FULL_KEY_DATA; + FREESPACE(p) = FREESPACE(p) - move_bytes; + OFFSET(p) = off; + } else + p[n - 2] = FULL_KEY; + p = (u_short *)bufp->page; + cp = bufp->page; + bufp->flags |= BUF_MOD; + } + + /* Now move the data */ + for (space = FREESPACE(p) - BIGOVERHEAD; val_size; + space = FREESPACE(p) - BIGOVERHEAD) { + move_bytes = MIN(space, val_size); + /* + * Here's the hack to make sure that if the data ends on the + * same page as the key ends, FREESPACE is at least one. + */ + if (space == val_size && val_size == val->size) + move_bytes--; + off = OFFSET(p) - move_bytes; + memmove(cp + off, val_data, move_bytes); + val_size -= move_bytes; + val_data += move_bytes; + n = p[0]; + p[++n] = off; + p[0] = ++n; + FREESPACE(p) = off - PAGE_META(n); + OFFSET(p) = off; + if (val_size) { + p[n] = FULL_KEY; + bufp = __add_ovflpage(hashp, bufp); + if (!bufp) + return (-1); + cp = bufp->page; + p = (u_short *)cp; + } else + p[n] = FULL_KEY_DATA; + bufp->flags |= BUF_MOD; + } + return (0); +} + +/* + * Called when bufp's page contains a partial key (index should be 1) + * + * All pages in the big key/data pair except bufp are freed. We cannot + * free bufp because the page pointing to it is lost and we can't get rid + * of its pointer. + * + * Returns: + * 0 => OK + *-1 => ERROR + */ +extern int +__big_delete(hashp, bufp) + HTAB *hashp; + BUFHEAD *bufp; +{ + register BUFHEAD *last_bfp, *rbufp; + u_short *bp, pageno; + int key_done, n; + + rbufp = bufp; + last_bfp = NULL; + bp = (u_short *)bufp->page; + pageno = 0; + key_done = 0; + + while (!key_done || (bp[2] != FULL_KEY_DATA)) { + if (bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA) + key_done = 1; + + /* + * If there is freespace left on a FULL_KEY_DATA page, then + * the data is short and fits entirely on this page, and this + * is the last page. + */ + if (bp[2] == FULL_KEY_DATA && FREESPACE(bp)) + break; + pageno = bp[bp[0] - 1]; + rbufp->flags |= BUF_MOD; + rbufp = __get_buf(hashp, pageno, rbufp, 0); + if (last_bfp) + __free_ovflpage(hashp, last_bfp); + last_bfp = rbufp; + if (!rbufp) + return (-1); /* Error. */ + bp = (u_short *)rbufp->page; + } + + /* + * If we get here then rbufp points to the last page of the big + * key/data pair. Bufp points to the first one -- it should now be + * empty pointing to the next page after this pair. Can't free it + * because we don't have the page pointing to it. + */ + + /* This is information from the last page of the pair. */ + n = bp[0]; + pageno = bp[n - 1]; + + /* Now, bp is the first page of the pair. */ + bp = (u_short *)bufp->page; + if (n > 2) { + /* There is an overflow page. */ + bp[1] = pageno; + bp[2] = OVFLPAGE; + bufp->ovfl = rbufp->ovfl; + } else + /* This is the last page. */ + bufp->ovfl = NULL; + n -= 2; + bp[0] = n; + FREESPACE(bp) = hashp->BSIZE - PAGE_META(n); + OFFSET(bp) = hashp->BSIZE - 1; + + bufp->flags |= BUF_MOD; + if (rbufp) + __free_ovflpage(hashp, rbufp); + if (last_bfp != rbufp) + __free_ovflpage(hashp, last_bfp); + + hashp->NKEYS--; + return (0); +} +/* + * Returns: + * 0 = key not found + * -1 = get next overflow page + * -2 means key not found and this is big key/data + * -3 error + */ +extern int +__find_bigpair(hashp, bufp, ndx, key, size) + HTAB *hashp; + BUFHEAD *bufp; + int ndx; + char *key; + int size; +{ + register u_short *bp; + register char *p; + int ksize; + u_short bytes; + char *kkey; + + bp = (u_short *)bufp->page; + p = bufp->page; + ksize = size; + kkey = key; + + for (bytes = hashp->BSIZE - bp[ndx]; + bytes <= size && bp[ndx + 1] == PARTIAL_KEY; + bytes = hashp->BSIZE - bp[ndx]) { + if (memcmp(p + bp[ndx], kkey, bytes)) + return (-2); + kkey += bytes; + ksize -= bytes; + bufp = __get_buf(hashp, bp[ndx + 2], bufp, 0); + if (!bufp) + return (-3); + p = bufp->page; + bp = (u_short *)p; + ndx = 1; + } + + if (bytes != ksize || memcmp(p + bp[ndx], kkey, bytes)) { +#ifdef HASH_STATISTICS + ++hash_collisions; +#endif + return (-2); + } else + return (ndx); +} + +/* + * Given the buffer pointer of the first overflow page of a big pair, + * find the end of the big pair + * + * This will set bpp to the buffer header of the last page of the big pair. + * It will return the pageno of the overflow page following the last page + * of the pair; 0 if there isn't any (i.e. big pair is the last key in the + * bucket) + */ +extern u_short +__find_last_page(hashp, bpp) + HTAB *hashp; + BUFHEAD **bpp; +{ + BUFHEAD *bufp; + u_short *bp, pageno; + int n; + + bufp = *bpp; + bp = (u_short *)bufp->page; + for (;;) { + n = bp[0]; + + /* + * This is the last page if: the tag is FULL_KEY_DATA and + * either only 2 entries OVFLPAGE marker is explicit there + * is freespace on the page. + */ + if (bp[2] == FULL_KEY_DATA && + ((n == 2) || (bp[n] == OVFLPAGE) || (FREESPACE(bp)))) + break; + + pageno = bp[n - 1]; + bufp = __get_buf(hashp, pageno, bufp, 0); + if (!bufp) + return (0); /* Need to indicate an error! */ + bp = (u_short *)bufp->page; + } + + *bpp = bufp; + if (bp[0] > 2) + return (bp[3]); + else + return (0); +} + +/* + * Return the data for the key/data pair that begins on this page at this + * index (index should always be 1). + */ +extern int +__big_return(hashp, bufp, ndx, val, set_current) + HTAB *hashp; + BUFHEAD *bufp; + int ndx; + DBT *val; + int set_current; +{ + BUFHEAD *save_p; + u_short *bp, len, off, save_addr; + char *tp; + + bp = (u_short *)bufp->page; + while (bp[ndx + 1] == PARTIAL_KEY) { + bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); + if (!bufp) + return (-1); + bp = (u_short *)bufp->page; + ndx = 1; + } + + if (bp[ndx + 1] == FULL_KEY) { + bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); + if (!bufp) + return (-1); + bp = (u_short *)bufp->page; + save_p = bufp; + save_addr = save_p->addr; + off = bp[1]; + len = 0; + } else + if (!FREESPACE(bp)) { + /* + * This is a hack. We can't distinguish between + * FULL_KEY_DATA that contains complete data or + * incomplete data, so we require that if the data + * is complete, there is at least 1 byte of free + * space left. + */ + off = bp[bp[0]]; + len = bp[1] - off; + save_p = bufp; + save_addr = bufp->addr; + bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); + if (!bufp) + return (-1); + bp = (u_short *)bufp->page; + } else { + /* The data is all on one page. */ + tp = (char *)bp; + off = bp[bp[0]]; + val->data = (u_char *)tp + off; + val->size = bp[1] - off; + if (set_current) { + if (bp[0] == 2) { /* No more buckets in + * chain */ + hashp->cpage = NULL; + hashp->cbucket++; + hashp->cndx = 1; + } else { + hashp->cpage = __get_buf(hashp, + bp[bp[0] - 1], bufp, 0); + if (!hashp->cpage) + return (-1); + hashp->cndx = 1; + if (!((u_short *) + hashp->cpage->page)[0]) { + hashp->cbucket++; + hashp->cpage = NULL; + } + } + } + return (0); + } + + val->size = collect_data(hashp, bufp, (int)len, set_current); + if (val->size == -1) + return (-1); + if (save_p->addr != save_addr) { + /* We are pretty short on buffers. */ + errno = EINVAL; /* OUT OF BUFFERS */ + return (-1); + } + memmove(hashp->tmp_buf, (save_p->page) + off, len); + val->data = (u_char *)hashp->tmp_buf; + return (0); +} +/* + * Count how big the total datasize is by recursing through the pages. Then + * allocate a buffer and copy the data as you recurse up. + */ +static int +collect_data(hashp, bufp, len, set) + HTAB *hashp; + BUFHEAD *bufp; + int len, set; +{ + register u_short *bp; + register char *p; + BUFHEAD *xbp; + u_short save_addr; + int mylen, totlen; + + p = bufp->page; + bp = (u_short *)p; + mylen = hashp->BSIZE - bp[1]; + save_addr = bufp->addr; + + if (bp[2] == FULL_KEY_DATA) { /* End of Data */ + totlen = len + mylen; + if (hashp->tmp_buf) + free(hashp->tmp_buf); + hashp->tmp_buf = malloc(totlen); + if (!hashp->tmp_buf) + return (-1); + if (set) { + hashp->cndx = 1; + if (bp[0] == 2) { /* No more buckets in chain */ + hashp->cpage = NULL; + hashp->cbucket++; + } else { + hashp->cpage = + __get_buf(hashp, bp[bp[0] - 1], bufp, 0); + if (!hashp->cpage) + return (-1); + else if (!((u_short *)hashp->cpage->page)[0]) { + hashp->cbucket++; + hashp->cpage = NULL; + } + } + } + } else { + xbp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); + if (!xbp || ((totlen = + collect_data(hashp, xbp, len + mylen, set)) < 1)) + return (-1); + } + if (bufp->addr != save_addr) { + errno = EINVAL; /* Out of buffers. */ + return (-1); + } + memmove(&hashp->tmp_buf[len], (bufp->page) + bp[1], mylen); + return (totlen); +} + +/* + * Fill in the key and data for this big pair. + */ +extern int +__big_keydata(hashp, bufp, key, val, set) + HTAB *hashp; + BUFHEAD *bufp; + DBT *key, *val; + int set; +{ + key->size = collect_key(hashp, bufp, 0, val, set); + if (key->size == -1) + return (-1); + key->data = (u_char *)hashp->tmp_key; + return (0); +} + +/* + * Count how big the total key size is by recursing through the pages. Then + * collect the data, allocate a buffer and copy the key as you recurse up. + */ +static int +collect_key(hashp, bufp, len, val, set) + HTAB *hashp; + BUFHEAD *bufp; + int len; + DBT *val; + int set; +{ + BUFHEAD *xbp; + char *p; + int mylen, totlen; + u_short *bp, save_addr; + + p = bufp->page; + bp = (u_short *)p; + mylen = hashp->BSIZE - bp[1]; + + save_addr = bufp->addr; + totlen = len + mylen; + if (bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA) { /* End of Key. */ + if (hashp->tmp_key) + free(hashp->tmp_key); + hashp->tmp_key = malloc(totlen); + if (!hashp->tmp_key) + return (-1); + if (__big_return(hashp, bufp, 1, val, set)) + return (-1); + } else { + xbp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); + if (!xbp || ((totlen = + collect_key(hashp, xbp, totlen, val, set)) < 1)) + return (-1); + } + if (bufp->addr != save_addr) { + errno = EINVAL; /* MIS -- OUT OF BUFFERS */ + return (-1); + } + memmove(&hashp->tmp_key[len], (bufp->page) + bp[1], mylen); + return (totlen); +} + +/* + * Returns: + * 0 => OK + * -1 => error + */ +extern int +__big_split(hashp, op, np, big_keyp, addr, obucket, ret) + HTAB *hashp; + BUFHEAD *op; /* Pointer to where to put keys that go in old bucket */ + BUFHEAD *np; /* Pointer to new bucket page */ + /* Pointer to first page containing the big key/data */ + BUFHEAD *big_keyp; + int addr; /* Address of big_keyp */ + u_int obucket;/* Old Bucket */ + SPLIT_RETURN *ret; +{ + register BUFHEAD *tmpp; + register u_short *tp; + BUFHEAD *bp; + DBT key, val; + u_int change; + u_short free_space, n, off; + + bp = big_keyp; + + /* Now figure out where the big key/data goes */ + if (__big_keydata(hashp, big_keyp, &key, &val, 0)) + return (-1); + change = (__call_hash(hashp, key.data, key.size) != obucket); + + if (ret->next_addr = __find_last_page(hashp, &big_keyp)) { + if (!(ret->nextp = + __get_buf(hashp, ret->next_addr, big_keyp, 0))) + return (-1);; + } else + ret->nextp = NULL; + + /* Now make one of np/op point to the big key/data pair */ +#ifdef DEBUG + assert(np->ovfl == NULL); +#endif + if (change) + tmpp = np; + else + tmpp = op; + + tmpp->flags |= BUF_MOD; +#ifdef DEBUG1 + (void)fprintf(stderr, + "BIG_SPLIT: %d->ovfl was %d is now %d\n", tmpp->addr, + (tmpp->ovfl ? tmpp->ovfl->addr : 0), (bp ? bp->addr : 0)); +#endif + tmpp->ovfl = bp; /* one of op/np point to big_keyp */ + tp = (u_short *)tmpp->page; +#ifdef DEBUG + assert(FREESPACE(tp) >= OVFLSIZE); +#endif + n = tp[0]; + off = OFFSET(tp); + free_space = FREESPACE(tp); + tp[++n] = (u_short)addr; + tp[++n] = OVFLPAGE; + tp[0] = n; + OFFSET(tp) = off; + FREESPACE(tp) = free_space - OVFLSIZE; + + /* + * Finally, set the new and old return values. BIG_KEYP contains a + * pointer to the last page of the big key_data pair. Make sure that + * big_keyp has no following page (2 elements) or create an empty + * following page. + */ + + ret->newp = np; + ret->oldp = op; + + tp = (u_short *)big_keyp->page; + big_keyp->flags |= BUF_MOD; + if (tp[0] > 2) { + /* + * There may be either one or two offsets on this page. If + * there is one, then the overflow page is linked on normally + * and tp[4] is OVFLPAGE. If there are two, tp[4] contains + * the second offset and needs to get stuffed in after the + * next overflow page is added. + */ + n = tp[4]; + free_space = FREESPACE(tp); + off = OFFSET(tp); + tp[0] -= 2; + FREESPACE(tp) = free_space + OVFLSIZE; + OFFSET(tp) = off; + tmpp = __add_ovflpage(hashp, big_keyp); + if (!tmpp) + return (-1); + tp[4] = n; + } else + tmpp = big_keyp; + + if (change) + ret->newp = tmpp; + else + ret->oldp = tmpp; + return (0); +} diff --git a/lib/libc/db/hash/hash_buf.c b/lib/libc/db/hash/hash_buf.c new file mode 100644 index 0000000000..fc29ee1786 --- /dev/null +++ b/lib/libc/db/hash/hash_buf.c @@ -0,0 +1,344 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)hash_buf.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +/* + * PACKAGE: hash + * + * DESCRIPTION: + * Contains buffer management + * + * ROUTINES: + * External + * __buf_init + * __get_buf + * __buf_free + * __reclaim_buf + * Internal + * newbuf + */ + +#include + +#include +#include +#include +#ifdef DEBUG +#include +#endif + +#include +#include "hash.h" +#include "page.h" +#include "extern.h" + +static BUFHEAD *newbuf __P((HTAB *, u_int, BUFHEAD *)); + +/* Unlink B from its place in the lru */ +#define BUF_REMOVE(B) { \ + (B)->prev->next = (B)->next; \ + (B)->next->prev = (B)->prev; \ +} + +/* Insert B after P */ +#define BUF_INSERT(B, P) { \ + (B)->next = (P)->next; \ + (B)->prev = (P); \ + (P)->next = (B); \ + (B)->next->prev = (B); \ +} + +#define MRU hashp->bufhead.next +#define LRU hashp->bufhead.prev + +#define MRU_INSERT(B) BUF_INSERT((B), &hashp->bufhead) +#define LRU_INSERT(B) BUF_INSERT((B), LRU) + +/* + * We are looking for a buffer with address "addr". If prev_bp is NULL, then + * address is a bucket index. If prev_bp is not NULL, then it points to the + * page previous to an overflow page that we are trying to find. + * + * CAVEAT: The buffer header accessed via prev_bp's ovfl field may no longer + * be valid. Therefore, you must always verify that its address matches the + * address you are seeking. + */ +extern BUFHEAD * +__get_buf(hashp, addr, prev_bp, newpage) + HTAB *hashp; + u_int addr; + BUFHEAD *prev_bp; + int newpage; /* If prev_bp set, indicates a new overflow page. */ +{ + register BUFHEAD *bp; + register u_int is_disk_mask; + register int is_disk, segment_ndx; + SEGMENT segp; + + is_disk = 0; + is_disk_mask = 0; + if (prev_bp) { + bp = prev_bp->ovfl; + if (!bp || (bp->addr != addr)) + bp = NULL; + if (!newpage) + is_disk = BUF_DISK; + } else { + /* Grab buffer out of directory */ + segment_ndx = addr & (hashp->SGSIZE - 1); + + /* valid segment ensured by __call_hash() */ + segp = hashp->dir[addr >> hashp->SSHIFT]; +#ifdef DEBUG + assert(segp != NULL); +#endif + bp = PTROF(segp[segment_ndx]); + is_disk_mask = ISDISK(segp[segment_ndx]); + is_disk = is_disk_mask || !hashp->new_file; + } + + if (!bp) { + bp = newbuf(hashp, addr, prev_bp); + if (!bp || + __get_page(hashp, bp->page, addr, !prev_bp, is_disk, 0)) + return (NULL); + if (!prev_bp) + segp[segment_ndx] = + (BUFHEAD *)((u_int)bp | is_disk_mask); + } else { + BUF_REMOVE(bp); + MRU_INSERT(bp); + } + return (bp); +} + +/* + * We need a buffer for this page. Either allocate one, or evict a resident + * one (if we have as many buffers as we're allowed) and put this one in. + * + * If newbuf finds an error (returning NULL), it also sets errno. + */ +static BUFHEAD * +newbuf(hashp, addr, prev_bp) + HTAB *hashp; + u_int addr; + BUFHEAD *prev_bp; +{ + register BUFHEAD *bp; /* The buffer we're going to use */ + register BUFHEAD *xbp; /* Temp pointer */ + register BUFHEAD *next_xbp; + SEGMENT segp; + int segment_ndx; + u_short oaddr, *shortp; + + oaddr = 0; + bp = LRU; + /* + * If LRU buffer is pinned, the buffer pool is too small. We need to + * allocate more buffers. + */ + if (hashp->nbufs || (bp->flags & BUF_PIN)) { + /* Allocate a new one */ + bp = malloc(sizeof(struct _bufhead)); + if (!bp || !(bp->page = malloc(hashp->BSIZE))) + return (NULL); + if (hashp->nbufs) + hashp->nbufs--; + } else { + /* Kick someone out */ + BUF_REMOVE(bp); + /* + * If this is an overflow page with addr 0, it's already been + * flushed back in an overflow chain and initialized. + */ + if ((bp->addr != 0) || (bp->flags & BUF_BUCKET)) { + /* + * Set oaddr before __put_page so that you get it + * before bytes are swapped. + */ + shortp = (u_short *)bp->page; + if (shortp[0]) + oaddr = shortp[shortp[0] - 1]; + if ((bp->flags & BUF_MOD) && __put_page(hashp, bp->page, + bp->addr, (int)IS_BUCKET(bp->flags), 0)) + return (NULL); + /* + * Update the pointer to this page (i.e. invalidate it). + * + * If this is a new file (i.e. we created it at open + * time), make sure that we mark pages which have been + * written to disk so we retrieve them from disk later, + * rather than allocating new pages. + */ + if (IS_BUCKET(bp->flags)) { + segment_ndx = bp->addr & (hashp->SGSIZE - 1); + segp = hashp->dir[bp->addr >> hashp->SSHIFT]; +#ifdef DEBUG + assert(segp != NULL); +#endif + + if (hashp->new_file && + ((bp->flags & BUF_MOD) || + ISDISK(segp[segment_ndx]))) + segp[segment_ndx] = (BUFHEAD *)BUF_DISK; + else + segp[segment_ndx] = NULL; + } + /* + * Since overflow pages can only be access by means of + * their bucket, free overflow pages associated with + * this bucket. + */ + for (xbp = bp; xbp->ovfl;) { + next_xbp = xbp->ovfl; + xbp->ovfl = 0; + xbp = next_xbp; + + /* Check that ovfl pointer is up date. */ + if (IS_BUCKET(xbp->flags) || + (oaddr != xbp->addr)) + break; + + shortp = (u_short *)xbp->page; + if (shortp[0]) + /* set before __put_page */ + oaddr = shortp[shortp[0] - 1]; + if ((xbp->flags & BUF_MOD) && __put_page(hashp, + xbp->page, xbp->addr, 0, 0)) + return (NULL); + xbp->addr = 0; + xbp->flags = 0; + BUF_REMOVE(xbp); + LRU_INSERT(xbp); + } + } + } + + /* Now assign this buffer */ + bp->addr = addr; +#ifdef DEBUG1 + (void)fprintf(stderr, "NEWBUF1: %d->ovfl was %d is now %d\n", + bp->addr, (bp->ovfl ? bp->ovfl->addr : 0), 0); +#endif + bp->ovfl = NULL; + if (prev_bp) { + /* + * If prev_bp is set, this is an overflow page, hook it in to + * the buffer overflow links. + */ +#ifdef DEBUG1 + (void)fprintf(stderr, "NEWBUF2: %d->ovfl was %d is now %d\n", + prev_bp->addr, (prev_bp->ovfl ? bp->ovfl->addr : 0), + (bp ? bp->addr : 0)); +#endif + prev_bp->ovfl = bp; + bp->flags = 0; + } else + bp->flags = BUF_BUCKET; + MRU_INSERT(bp); + return (bp); +} + +extern void +__buf_init(hashp, nbytes) + HTAB *hashp; + int nbytes; +{ + BUFHEAD *bfp; + int npages; + + bfp = &(hashp->bufhead); + npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT; + npages = MAX(npages, MIN_BUFFERS); + + hashp->nbufs = npages; + bfp->next = bfp; + bfp->prev = bfp; + /* + * This space is calloc'd so these are already null. + * + * bfp->ovfl = NULL; + * bfp->flags = 0; + * bfp->page = NULL; + * bfp->addr = 0; + */ +} + +extern int +__buf_free(hashp, do_free, to_disk) + HTAB *hashp; + int do_free, to_disk; +{ + BUFHEAD *bp; + + /* Need to make sure that buffer manager has been initialized */ + if (!LRU) + return (0); + for (bp = LRU; bp != &hashp->bufhead;) { + /* Check that the buffer is valid */ + if (bp->addr || IS_BUCKET(bp->flags)) { + if (to_disk && (bp->flags & BUF_MOD) && + __put_page(hashp, bp->page, + bp->addr, IS_BUCKET(bp->flags), 0)) + return (-1); + } + /* Check if we are freeing stuff */ + if (do_free) { + if (bp->page) + free(bp->page); + BUF_REMOVE(bp); + free(bp); + bp = LRU; + } else + bp = bp->prev; + } + return (0); +} + +extern void +__reclaim_buf(hashp, bp) + HTAB *hashp; + BUFHEAD *bp; +{ + bp->ovfl = 0; + bp->addr = 0; + bp->flags = 0; + BUF_REMOVE(bp); + LRU_INSERT(bp); +} diff --git a/lib/libc/db/hash/hash_func.c b/lib/libc/db/hash/hash_func.c new file mode 100644 index 0000000000..e4e8fcbe0f --- /dev/null +++ b/lib/libc/db/hash/hash_func.c @@ -0,0 +1,191 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)hash_func.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include "hash.h" +#include "page.h" +#include "extern.h" + +static int hash1 __P((u_char *, int)); +static int hash2 __P((u_char *, int)); +static int hash3 __P((u_char *, int)); +static int hash4 __P((u_char *, int)); + +/* Global default hash function */ +int (*__default_hash) __P((u_char *, int)) = hash4; + +/******************************* HASH FUNCTIONS **************************/ +/* + * Assume that we've already split the bucket to which this key hashes, + * calculate that bucket, and check that in fact we did already split it. + * + * This came from ejb's hsearch. + */ + +#define PRIME1 37 +#define PRIME2 1048583 + +static int +hash1(key, len) + register u_char *key; + register int len; +{ + register int h; + + h = 0; + /* Convert string to integer */ + while (len--) + h = h * PRIME1 ^ (*key++ - ' '); + h %= PRIME2; + return (h); +} + +/* + * Phong's linear congruential hash + */ +#define dcharhash(h, c) ((h) = 0x63c63cd9*(h) + 0x9c39c33d + (c)) + +static int +hash2(key, len) + register u_char *key; + int len; +{ + register u_char *e, c; + register int h; + + e = key + len; + for (h = 0; key != e;) { + c = *key++; + if (!c && key > e) + break; + dcharhash(h, c); + } + return (h); +} + +/* + * This is INCREDIBLY ugly, but fast. We break the string up into 8 byte + * units. On the first time through the loop we get the "leftover bytes" + * (strlen % 8). On every other iteration, we perform 8 HASHC's so we handle + * all 8 bytes. Essentially, this saves us 7 cmp & branch instructions. If + * this routine is heavily used enough, it's worth the ugly coding. + * + * OZ's original sdbm hash + */ +static int +hash3(key, len) + register u_char *key; + register int len; +{ + register int n, loop; + +#define HASHC n = *key++ + 65599 * n + + n = 0; + if (len > 0) { + loop = (len + 8 - 1) >> 3; + + switch (len & (8 - 1)) { + case 0: + do { /* All fall throughs */ + HASHC; + case 7: + HASHC; + case 6: + HASHC; + case 5: + HASHC; + case 4: + HASHC; + case 3: + HASHC; + case 2: + HASHC; + case 1: + HASHC; + } while (--loop); + } + + } + return (n); +} + +/* Hash function from Chris Torek. */ +static int +hash4(key, len) + register u_char *key; + register int len; +{ + register int h, loop; + +#define HASH4a h = (h << 5) - h + *key++; +#define HASH4b h = (h << 5) + h + *key++; +#define HASH4 HASH4b + + h = 0; + if (len > 0) { + loop = (len + 8 - 1) >> 3; + + switch (len & (8 - 1)) { + case 0: + do { /* All fall throughs */ + HASH4; + case 7: + HASH4; + case 6: + HASH4; + case 5: + HASH4; + case 4: + HASH4; + case 3: + HASH4; + case 2: + HASH4; + case 1: + HASH4; + } while (--loop); + } + + } + return (h); +} diff --git a/lib/libc/db/hash/hash_log2.c b/lib/libc/db/hash/hash_log2.c new file mode 100644 index 0000000000..b773707eba --- /dev/null +++ b/lib/libc/db/hash/hash_log2.c @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)hash_log2.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +u_int +__log2(num) + u_int num; +{ + register u_int i, limit; + + limit = 1; + for (i = 0; limit < num; limit = limit << 1, i++); + return (i); +} diff --git a/lib/libc/db/hash/hash_page.c b/lib/libc/db/hash/hash_page.c new file mode 100644 index 0000000000..c2260f7b21 --- /dev/null +++ b/lib/libc/db/hash/hash_page.c @@ -0,0 +1,941 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)hash_page.c 8.1 (Berkeley) 6/6/93"; +#endif /* LIBC_SCCS and not lint */ + +/* + * PACKAGE: hashing + * + * DESCRIPTION: + * Page manipulation for hashing package. + * + * ROUTINES: + * + * External + * __get_page + * __add_ovflpage + * Internal + * overflow_page + * open_temp + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef DEBUG +#include +#endif + +#include +#include "hash.h" +#include "page.h" +#include "extern.h" + +static u_long *fetch_bitmap __P((HTAB *, int)); +static u_long first_free __P((u_long)); +static int open_temp __P((HTAB *)); +static u_short overflow_page __P((HTAB *)); +static void putpair __P((char *, const DBT *, const DBT *)); +static void squeeze_key __P((u_short *, const DBT *, const DBT *)); +static int ugly_split + __P((HTAB *, u_int, BUFHEAD *, BUFHEAD *, int, int)); + +#define PAGE_INIT(P) { \ + ((u_short *)(P))[0] = 0; \ + ((u_short *)(P))[1] = hashp->BSIZE - 3 * sizeof(u_short); \ + ((u_short *)(P))[2] = hashp->BSIZE; \ +} + +/* + * This is called AFTER we have verified that there is room on the page for + * the pair (PAIRFITS has returned true) so we go right ahead and start moving + * stuff on. + */ +static void +putpair(p, key, val) + char *p; + const DBT *key, *val; +{ + register u_short *bp, n, off; + + bp = (u_short *)p; + + /* Enter the key first. */ + n = bp[0]; + + off = OFFSET(bp) - key->size; + memmove(p + off, key->data, key->size); + bp[++n] = off; + + /* Now the data. */ + off -= val->size; + memmove(p + off, val->data, val->size); + bp[++n] = off; + + /* Adjust page info. */ + bp[0] = n; + bp[n + 1] = off - ((n + 3) * sizeof(u_short)); + bp[n + 2] = off; +} + +/* + * Returns: + * 0 OK + * -1 error + */ +extern int +__delpair(hashp, bufp, ndx) + HTAB *hashp; + BUFHEAD *bufp; + register int ndx; +{ + register u_short *bp, newoff; + register int n; + u_short pairlen; + + bp = (u_short *)bufp->page; + n = bp[0]; + + if (bp[ndx + 1] < REAL_KEY) + return (__big_delete(hashp, bufp)); + if (ndx != 1) + newoff = bp[ndx - 1]; + else + newoff = hashp->BSIZE; + pairlen = newoff - bp[ndx + 1]; + + if (ndx != (n - 1)) { + /* Hard Case -- need to shuffle keys */ + register int i; + register char *src = bufp->page + (int)OFFSET(bp); + register char *dst = src + (int)pairlen; + memmove(dst, src, bp[ndx + 1] - OFFSET(bp)); + + /* Now adjust the pointers */ + for (i = ndx + 2; i <= n; i += 2) { + if (bp[i + 1] == OVFLPAGE) { + bp[i - 2] = bp[i]; + bp[i - 1] = bp[i + 1]; + } else { + bp[i - 2] = bp[i] + pairlen; + bp[i - 1] = bp[i + 1] + pairlen; + } + } + } + /* Finally adjust the page data */ + bp[n] = OFFSET(bp) + pairlen; + bp[n - 1] = bp[n + 1] + pairlen + 2 * sizeof(u_short); + bp[0] = n - 2; + hashp->NKEYS--; + + bufp->flags |= BUF_MOD; + return (0); +} +/* + * Returns: + * 0 ==> OK + * -1 ==> Error + */ +extern int +__split_page(hashp, obucket, nbucket) + HTAB *hashp; + u_int obucket, nbucket; +{ + register BUFHEAD *new_bufp, *old_bufp; + register u_short *ino; + register char *np; + DBT key, val; + int n, ndx, retval; + u_short copyto, diff, off, moved; + char *op; + + copyto = (u_short)hashp->BSIZE; + off = (u_short)hashp->BSIZE; + old_bufp = __get_buf(hashp, obucket, NULL, 0); + if (old_bufp == NULL) + return (-1); + new_bufp = __get_buf(hashp, nbucket, NULL, 0); + if (new_bufp == NULL) + return (-1); + + old_bufp->flags |= (BUF_MOD | BUF_PIN); + new_bufp->flags |= (BUF_MOD | BUF_PIN); + + ino = (u_short *)(op = old_bufp->page); + np = new_bufp->page; + + moved = 0; + + for (n = 1, ndx = 1; n < ino[0]; n += 2) { + if (ino[n + 1] < REAL_KEY) { + retval = ugly_split(hashp, obucket, old_bufp, new_bufp, + (int)copyto, (int)moved); + old_bufp->flags &= ~BUF_PIN; + new_bufp->flags &= ~BUF_PIN; + return (retval); + + } + key.data = (u_char *)op + ino[n]; + key.size = off - ino[n]; + + if (__call_hash(hashp, key.data, key.size) == obucket) { + /* Don't switch page */ + diff = copyto - off; + if (diff) { + copyto = ino[n + 1] + diff; + memmove(op + copyto, op + ino[n + 1], + off - ino[n + 1]); + ino[ndx] = copyto + ino[n] - ino[n + 1]; + ino[ndx + 1] = copyto; + } else + copyto = ino[n + 1]; + ndx += 2; + } else { + /* Switch page */ + val.data = (u_char *)op + ino[n + 1]; + val.size = ino[n] - ino[n + 1]; + putpair(np, &key, &val); + moved += 2; + } + + off = ino[n + 1]; + } + + /* Now clean up the page */ + ino[0] -= moved; + FREESPACE(ino) = copyto - sizeof(u_short) * (ino[0] + 3); + OFFSET(ino) = copyto; + +#ifdef DEBUG3 + (void)fprintf(stderr, "split %d/%d\n", + ((u_short *)np)[0] / 2, + ((u_short *)op)[0] / 2); +#endif + /* unpin both pages */ + old_bufp->flags &= ~BUF_PIN; + new_bufp->flags &= ~BUF_PIN; + return (0); +} + +/* + * Called when we encounter an overflow or big key/data page during split + * handling. This is special cased since we have to begin checking whether + * the key/data pairs fit on their respective pages and because we may need + * overflow pages for both the old and new pages. + * + * The first page might be a page with regular key/data pairs in which case + * we have a regular overflow condition and just need to go on to the next + * page or it might be a big key/data pair in which case we need to fix the + * big key/data pair. + * + * Returns: + * 0 ==> success + * -1 ==> failure + */ +static int +ugly_split(hashp, obucket, old_bufp, new_bufp, copyto, moved) + HTAB *hashp; + u_int obucket; /* Same as __split_page. */ + BUFHEAD *old_bufp, *new_bufp; + int copyto; /* First byte on page which contains key/data values. */ + int moved; /* Number of pairs moved to new page. */ +{ + register BUFHEAD *bufp; /* Buffer header for ino */ + register u_short *ino; /* Page keys come off of */ + register u_short *np; /* New page */ + register u_short *op; /* Page keys go on to if they aren't moving */ + + BUFHEAD *last_bfp; /* Last buf header OVFL needing to be freed */ + DBT key, val; + SPLIT_RETURN ret; + u_short n, off, ov_addr, scopyto; + char *cino; /* Character value of ino */ + + bufp = old_bufp; + ino = (u_short *)old_bufp->page; + np = (u_short *)new_bufp->page; + op = (u_short *)old_bufp->page; + last_bfp = NULL; + scopyto = (u_short)copyto; /* ANSI */ + + n = ino[0] - 1; + while (n < ino[0]) { + if (ino[2] < REAL_KEY && ino[2] != OVFLPAGE) { + /* + * Ov_addr gets set before reaching this point; there's + * always an overflow page before a big key/data page. + */ + if (__big_split(hashp, old_bufp, + new_bufp, bufp, ov_addr, obucket, &ret)) + return (-1); + old_bufp = ret.oldp; + if (!old_bufp) + return (-1); + op = (u_short *)old_bufp->page; + new_bufp = ret.newp; + if (!new_bufp) + return (-1); + np = (u_short *)new_bufp->page; + bufp = ret.nextp; + if (!bufp) + return (0); + cino = (char *)bufp->page; + ino = (u_short *)cino; + last_bfp = ret.nextp; + } else if (ino[n + 1] == OVFLPAGE) { + ov_addr = ino[n]; + /* + * Fix up the old page -- the extra 2 are the fields + * which contained the overflow information. + */ + ino[0] -= (moved + 2); + FREESPACE(ino) = + scopyto - sizeof(u_short) * (ino[0] + 3); + OFFSET(ino) = scopyto; + + bufp = __get_buf(hashp, ov_addr, bufp, 0); + if (!bufp) + return (-1); + + ino = (u_short *)bufp->page; + n = 1; + scopyto = hashp->BSIZE; + moved = 0; + + if (last_bfp) + __free_ovflpage(hashp, last_bfp); + last_bfp = bufp; + } + /* Move regular sized pairs of there are any */ + off = hashp->BSIZE; + for (n = 1; (n < ino[0]) && (ino[n + 1] >= REAL_KEY); n += 2) { + cino = (char *)ino; + key.data = (u_char *)cino + ino[n]; + key.size = off - ino[n]; + val.data = (u_char *)cino + ino[n + 1]; + val.size = ino[n] - ino[n + 1]; + off = ino[n + 1]; + + if (__call_hash(hashp, key.data, key.size) == obucket) { + /* Keep on old page */ + if (PAIRFITS(op, (&key), (&val))) + putpair((char *)op, &key, &val); + else { + old_bufp = + __add_ovflpage(hashp, old_bufp); + if (!old_bufp) + return (-1); + op = (u_short *)old_bufp->page; + putpair((char *)op, &key, &val); + } + old_bufp->flags |= BUF_MOD; + } else { + /* Move to new page */ + if (PAIRFITS(np, (&key), (&val))) + putpair((char *)np, &key, &val); + else { + new_bufp = + __add_ovflpage(hashp, new_bufp); + if (!new_bufp) + return (-1); + np = (u_short *)new_bufp->page; + putpair((char *)np, &key, &val); + } + new_bufp->flags |= BUF_MOD; + } + } + } + if (last_bfp) + __free_ovflpage(hashp, last_bfp); + return (0); +} + +/* + * Add the given pair to the page + * + * Returns: + * 0 ==> OK + * 1 ==> failure + */ +extern int +__addel(hashp, bufp, key, val) + HTAB *hashp; + BUFHEAD *bufp; + const DBT *key, *val; +{ + register u_short *bp, *sop; + int do_expand; + + bp = (u_short *)bufp->page; + do_expand = 0; + while (bp[0] && (bp[bp[0]] < REAL_KEY)) + /* Exception case */ + if (bp[2] < REAL_KEY && bp[bp[0]] != OVFLPAGE) { + /* This is a big-keydata pair */ + bufp = __add_ovflpage(hashp, bufp); + if (!bufp) + return (-1); + bp = (u_short *)bufp->page; + } else + /* Try to squeeze key on this page */ + if (FREESPACE(bp) > PAIRSIZE(key, val)) { + squeeze_key(bp, key, val); + return (0); + } else { + bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); + if (!bufp) + return (-1); + bp = (u_short *)bufp->page; + } + + if (PAIRFITS(bp, key, val)) + putpair(bufp->page, key, val); + else { + do_expand = 1; + bufp = __add_ovflpage(hashp, bufp); + if (!bufp) + return (-1); + sop = (u_short *)bufp->page; + + if (PAIRFITS(sop, key, val)) + putpair((char *)sop, key, val); + else + if (__big_insert(hashp, bufp, key, val)) + return (-1); + } + bufp->flags |= BUF_MOD; + /* + * If the average number of keys per bucket exceeds the fill factor, + * expand the table. + */ + hashp->NKEYS++; + if (do_expand || + (hashp->NKEYS / (hashp->MAX_BUCKET + 1) > hashp->FFACTOR)) + return (__expand_table(hashp)); + return (0); +} + +/* + * + * Returns: + * pointer on success + * NULL on error + */ +extern BUFHEAD * +__add_ovflpage(hashp, bufp) + HTAB *hashp; + BUFHEAD *bufp; +{ + register u_short *sp; + u_short ndx, ovfl_num; +#ifdef DEBUG1 + int tmp1, tmp2; +#endif + sp = (u_short *)bufp->page; + + /* Check if we are dynamically determining the fill factor */ + if (hashp->FFACTOR == DEF_FFACTOR) { + hashp->FFACTOR = sp[0] >> 1; + if (hashp->FFACTOR < MIN_FFACTOR) + hashp->FFACTOR = MIN_FFACTOR; + } + bufp->flags |= BUF_MOD; + ovfl_num = overflow_page(hashp); +#ifdef DEBUG1 + tmp1 = bufp->addr; + tmp2 = bufp->ovfl ? bufp->ovfl->addr : 0; +#endif + if (!ovfl_num || !(bufp->ovfl = __get_buf(hashp, ovfl_num, bufp, 1))) + return (NULL); + bufp->ovfl->flags |= BUF_MOD; +#ifdef DEBUG1 + (void)fprintf(stderr, "ADDOVFLPAGE: %d->ovfl was %d is now %d\n", + tmp1, tmp2, bufp->ovfl->addr); +#endif + ndx = sp[0]; + /* + * Since a pair is allocated on a page only if there's room to add + * an overflow page, we know that the OVFL information will fit on + * the page. + */ + sp[ndx + 4] = OFFSET(sp); + sp[ndx + 3] = FREESPACE(sp) - OVFLSIZE; + sp[ndx + 1] = ovfl_num; + sp[ndx + 2] = OVFLPAGE; + sp[0] = ndx + 2; +#ifdef HASH_STATISTICS + hash_overflows++; +#endif + return (bufp->ovfl); +} + +/* + * Returns: + * 0 indicates SUCCESS + * -1 indicates FAILURE + */ +extern int +__get_page(hashp, p, bucket, is_bucket, is_disk, is_bitmap) + HTAB *hashp; + char *p; + u_int bucket; + int is_bucket, is_disk, is_bitmap; +{ + register int fd, page, size; + int rsize; + u_short *bp; + + fd = hashp->fp; + size = hashp->BSIZE; + + if ((fd == -1) || !is_disk) { + PAGE_INIT(p); + return (0); + } + if (is_bucket) + page = BUCKET_TO_PAGE(bucket); + else + page = OADDR_TO_PAGE(bucket); + if ((lseek(fd, (off_t)page << hashp->BSHIFT, SEEK_SET) == -1) || + ((rsize = read(fd, p, size)) == -1)) + return (-1); + bp = (u_short *)p; + if (!rsize) + bp[0] = 0; /* We hit the EOF, so initialize a new page */ + else + if (rsize != size) { + errno = EFTYPE; + return (-1); + } + if (!is_bitmap && !bp[0]) { + PAGE_INIT(p); + } else + if (hashp->LORDER != BYTE_ORDER) { + register int i, max; + + if (is_bitmap) { + max = hashp->BSIZE >> 2; /* divide by 4 */ + for (i = 0; i < max; i++) + BLSWAP(((long *)p)[i]); + } else { + BSSWAP(bp[0]); + max = bp[0] + 2; + for (i = 1; i <= max; i++) + BSSWAP(bp[i]); + } + } + return (0); +} + +/* + * Write page p to disk + * + * Returns: + * 0 ==> OK + * -1 ==>failure + */ +extern int +__put_page(hashp, p, bucket, is_bucket, is_bitmap) + HTAB *hashp; + char *p; + u_int bucket; + int is_bucket, is_bitmap; +{ + register int fd, page, size; + int wsize; + + size = hashp->BSIZE; + if ((hashp->fp == -1) && open_temp(hashp)) + return (-1); + fd = hashp->fp; + + if (hashp->LORDER != BYTE_ORDER) { + register int i; + register int max; + + if (is_bitmap) { + max = hashp->BSIZE >> 2; /* divide by 4 */ + for (i = 0; i < max; i++) + BLSWAP(((long *)p)[i]); + } else { + max = ((u_short *)p)[0] + 2; + for (i = 0; i <= max; i++) + BSSWAP(((u_short *)p)[i]); + } + } + if (is_bucket) + page = BUCKET_TO_PAGE(bucket); + else + page = OADDR_TO_PAGE(bucket); + if ((lseek(fd, (off_t)page << hashp->BSHIFT, SEEK_SET) == -1) || + ((wsize = write(fd, p, size)) == -1)) + /* Errno is set */ + return (-1); + if (wsize != size) { + errno = EFTYPE; + return (-1); + } + return (0); +} + +#define BYTE_MASK ((1 << INT_BYTE_SHIFT) -1) +/* + * Initialize a new bitmap page. Bitmap pages are left in memory + * once they are read in. + */ +extern int +__init_bitmap(hashp, pnum, nbits, ndx) + HTAB *hashp; + int pnum, nbits, ndx; +{ + u_long *ip; + int clearbytes, clearints; + + if (!(ip = malloc(hashp->BSIZE))) + return (1); + hashp->nmaps++; + clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1; + clearbytes = clearints << INT_TO_BYTE; + (void)memset((char *)ip, 0, clearbytes); + (void)memset(((char *)ip) + clearbytes, 0xFF, + hashp->BSIZE - clearbytes); + ip[clearints - 1] = ALL_SET << (nbits & BYTE_MASK); + SETBIT(ip, 0); + hashp->BITMAPS[ndx] = (u_short)pnum; + hashp->mapp[ndx] = ip; + return (0); +} + +static u_long +first_free(map) + u_long map; +{ + register u_long i, mask; + + mask = 0x1; + for (i = 0; i < BITS_PER_MAP; i++) { + if (!(mask & map)) + return (i); + mask = mask << 1; + } + return (i); +} + +static u_short +overflow_page(hashp) + HTAB *hashp; +{ + register u_long *freep; + register int max_free, offset, splitnum; + u_short addr; + int bit, first_page, free_bit, free_page, i, in_use_bits, j; +#ifdef DEBUG2 + int tmp1, tmp2; +#endif + splitnum = hashp->OVFL_POINT; + max_free = hashp->SPARES[splitnum]; + + free_page = (max_free - 1) >> (hashp->BSHIFT + BYTE_SHIFT); + free_bit = (max_free - 1) & ((hashp->BSIZE << BYTE_SHIFT) - 1); + + /* Look through all the free maps to find the first free block */ + first_page = hashp->LAST_FREED >>(hashp->BSHIFT + BYTE_SHIFT); + for ( i = first_page; i <= free_page; i++ ) { + if (!(freep = (u_long *)hashp->mapp[i]) && + !(freep = fetch_bitmap(hashp, i))) + return (NULL); + if (i == free_page) + in_use_bits = free_bit; + else + in_use_bits = (hashp->BSIZE << BYTE_SHIFT) - 1; + + if (i == first_page) { + bit = hashp->LAST_FREED & + ((hashp->BSIZE << BYTE_SHIFT) - 1); + j = bit / BITS_PER_MAP; + bit = bit & ~(BITS_PER_MAP - 1); + } else { + bit = 0; + j = 0; + } + for (; bit <= in_use_bits; j++, bit += BITS_PER_MAP) + if (freep[j] != ALL_SET) + goto found; + } + + /* No Free Page Found */ + hashp->LAST_FREED = hashp->SPARES[splitnum]; + hashp->SPARES[splitnum]++; + offset = hashp->SPARES[splitnum] - + (splitnum ? hashp->SPARES[splitnum - 1] : 0); + +#define OVMSG "HASH: Out of overflow pages. Increase page size\n" + if (offset > SPLITMASK) { + if (++splitnum >= NCACHED) { + (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); + return (NULL); + } + hashp->OVFL_POINT = splitnum; + hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1]; + hashp->SPARES[splitnum-1]--; + offset = 1; + } + + /* Check if we need to allocate a new bitmap page */ + if (free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1) { + free_page++; + if (free_page >= NCACHED) { + (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); + return (NULL); + } + /* + * This is tricky. The 1 indicates that you want the new page + * allocated with 1 clear bit. Actually, you are going to + * allocate 2 pages from this map. The first is going to be + * the map page, the second is the overflow page we were + * looking for. The init_bitmap routine automatically, sets + * the first bit of itself to indicate that the bitmap itself + * is in use. We would explicitly set the second bit, but + * don't have to if we tell init_bitmap not to leave it clear + * in the first place. + */ + if (__init_bitmap(hashp, (int)OADDR_OF(splitnum, offset), + 1, free_page)) + return (NULL); + hashp->SPARES[splitnum]++; +#ifdef DEBUG2 + free_bit = 2; +#endif + offset++; + if (offset > SPLITMASK) { + if (++splitnum >= NCACHED) { + (void)write(STDERR_FILENO, OVMSG, + sizeof(OVMSG) - 1); + return (NULL); + } + hashp->OVFL_POINT = splitnum; + hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1]; + hashp->SPARES[splitnum-1]--; + offset = 0; + } + } else { + /* + * Free_bit addresses the last used bit. Bump it to address + * the first available bit. + */ + free_bit++; + SETBIT(freep, free_bit); + } + + /* Calculate address of the new overflow page */ + addr = OADDR_OF(splitnum, offset); +#ifdef DEBUG2 + (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", + addr, free_bit, free_page); +#endif + return (addr); + +found: + bit = bit + first_free(freep[j]); + SETBIT(freep, bit); +#ifdef DEBUG2 + tmp1 = bit; + tmp2 = i; +#endif + /* + * Bits are addressed starting with 0, but overflow pages are addressed + * beginning at 1. Bit is a bit addressnumber, so we need to increment + * it to convert it to a page number. + */ + bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT)); + if (bit >= hashp->LAST_FREED) + hashp->LAST_FREED = bit - 1; + + /* Calculate the split number for this page */ + for (i = 0; (i < splitnum) && (bit > hashp->SPARES[i]); i++); + offset = (i ? bit - hashp->SPARES[i - 1] : bit); + if (offset >= SPLITMASK) + return (NULL); /* Out of overflow pages */ + addr = OADDR_OF(i, offset); +#ifdef DEBUG2 + (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", + addr, tmp1, tmp2); +#endif + + /* Allocate and return the overflow page */ + return (addr); +} + +/* + * Mark this overflow page as free. + */ +extern void +__free_ovflpage(hashp, obufp) + HTAB *hashp; + BUFHEAD *obufp; +{ + register u_short addr; + u_long *freep; + int bit_address, free_page, free_bit; + u_short ndx; + + addr = obufp->addr; +#ifdef DEBUG1 + (void)fprintf(stderr, "Freeing %d\n", addr); +#endif + ndx = (((u_short)addr) >> SPLITSHIFT); + bit_address = + (ndx ? hashp->SPARES[ndx - 1] : 0) + (addr & SPLITMASK) - 1; + if (bit_address < hashp->LAST_FREED) + hashp->LAST_FREED = bit_address; + free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT)); + free_bit = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1); + + if (!(freep = hashp->mapp[free_page])) + freep = fetch_bitmap(hashp, free_page); +#ifdef DEBUG + /* + * This had better never happen. It means we tried to read a bitmap + * that has already had overflow pages allocated off it, and we + * failed to read it from the file. + */ + if (!freep) + assert(0); +#endif + CLRBIT(freep, free_bit); +#ifdef DEBUG2 + (void)fprintf(stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n", + obufp->addr, free_bit, free_page); +#endif + __reclaim_buf(hashp, obufp); +} + +/* + * Returns: + * 0 success + * -1 failure + */ +static int +open_temp(hashp) + HTAB *hashp; +{ + sigset_t set, oset; + static char namestr[] = "_hashXXXXXX"; + + /* Block signals; make sure file goes away at process exit. */ + (void)sigfillset(&set); + (void)sigprocmask(SIG_BLOCK, &set, &oset); + if ((hashp->fp = mkstemp(namestr)) != -1) { + (void)unlink(namestr); + (void)fcntl(hashp->fp, F_SETFD, 1); + } + (void)sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL); + return (hashp->fp != -1 ? 0 : -1); +} + +/* + * We have to know that the key will fit, but the last entry on the page is + * an overflow pair, so we need to shift things. + */ +static void +squeeze_key(sp, key, val) + u_short *sp; + const DBT *key, *val; +{ + register char *p; + u_short free_space, n, off, pageno; + + p = (char *)sp; + n = sp[0]; + free_space = FREESPACE(sp); + off = OFFSET(sp); + + pageno = sp[n - 1]; + off -= key->size; + sp[n - 1] = off; + memmove(p + off, key->data, key->size); + off -= val->size; + sp[n] = off; + memmove(p + off, val->data, val->size); + sp[0] = n + 2; + sp[n + 1] = pageno; + sp[n + 2] = OVFLPAGE; + FREESPACE(sp) = free_space - PAIRSIZE(key, val); + OFFSET(sp) = off; +} + +static u_long * +fetch_bitmap(hashp, ndx) + HTAB *hashp; + int ndx; +{ + if (ndx >= hashp->nmaps || + !(hashp->mapp[ndx] = malloc(hashp->BSIZE)) || + __get_page(hashp, (char *)hashp->mapp[ndx], + hashp->BITMAPS[ndx], 0, 1, 1)) + return (NULL); + return (hashp->mapp[ndx]); +} + +#ifdef DEBUG4 +int +print_chain(addr) + int addr; +{ + BUFHEAD *bufp; + short *bp, oaddr; + + (void)fprintf(stderr, "%d ", addr); + bufp = __get_buf(hashp, addr, NULL, 0); + bp = (short *)bufp->page; + while (bp[0] && ((bp[bp[0]] == OVFLPAGE) || + ((bp[0] > 2) && bp[2] < REAL_KEY))) { + oaddr = bp[bp[0] - 1]; + (void)fprintf(stderr, "%d ", (int)oaddr); + bufp = __get_buf(hashp, (int)oaddr, bufp, 0); + bp = (short *)bufp->page; + } + (void)fprintf(stderr, "\n"); +} +#endif diff --git a/lib/libc/db/hash/hfunc.c b/lib/libc/db/hash/hfunc.c deleted file mode 100644 index de622f49d4..0000000000 --- a/lib/libc/db/hash/hfunc.c +++ /dev/null @@ -1,179 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Margo Seltzer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)hfunc.c 5.1 (Berkeley) 2/12/91"; -#endif /* LIBC_SCCS and not lint */ - -/* Global default hash function */ -static int hash1(); -static int hash2(); -static int hash3(); -static int hash4(); - -int (*default_hash)() = hash4; - -/******************************* HASH FUNCTIONS **************************/ -/* - Assume that we've already split the bucket to which this - key hashes, calculate that bucket, and check that in fact - we did already split it. - - This came from ejb's hsearch. -*/ - -# define PRIME1 37 -# define PRIME2 1048583 - -static int -hash1(key,len) -char *key; -int len; -{ - register int h; - register int l = len; - register unsigned char *k = (unsigned char *) key; - - h = 0; - /* - * Convert string to integer - */ - while (l--) h = h * PRIME1 ^ (*k++ - ' '); - h %= PRIME2; - - return (h); -} - -/* - Phong's linear congruential hash -*/ -#define dcharhash(h, c) ((h) = 0x63c63cd9*(h) + 0x9c39c33d + (c)) - -static int -hash2(str, n) - register unsigned char *str; - int n; -{ - register unsigned char *e, c; - register int h; - - e = str + n; - for (h = 0; str != e;) { - c = *str++; - if (!c && str > e) - break; - dcharhash(h,c); - } - return(h); -} - -/* - * This is INCREDIBLY ugly, but fast. - * We break the string up into 8 byte units. On the first time - * through the loop we get the "leftover bytes" (strlen % 8). - * On every other iteration, we perform 8 HASHC's so we handle - * all 8 bytes. Essentially, this saves us 7 cmp & branch - * instructions. If this routine is heavily used enough, it's - * worth the ugly coding - * - * OZ's original sdbm hash - */ -static int -hash3(key,nbytes) -char *key; -int nbytes; -{ - register int n = 0; - register char *str = key; - register int loop; - register int len = nbytes; - -#define HASHC n = *str++ + 65599 * n - - if (len > 0) { - loop = (len + 8 - 1) >> 3; - - switch(len & (8 - 1)) { - case 0: do { /* All fall throughs */ - HASHC; - case 7: HASHC; - case 6: HASHC; - case 5: HASHC; - case 4: HASHC; - case 3: HASHC; - case 2: HASHC; - case 1: HASHC; - } while (--loop); - } - - } - return(n); -} - -/* Hash function from Chris Torek */ -static int -hash4(key,nbytes) -char *key; -int nbytes; -{ - register int h = 0; - register char *p = key; - register int loop; - register int len = nbytes; - -#define HASH4a h = (h << 5) - h + *p++; -#define HASH4b h = (h << 5) + h + *p++; -#define HASH4 HASH4b - - if (len > 0) { - loop = (len + 8 - 1) >> 3; - - switch(len & (8 - 1)) { - case 0: do { /* All fall throughs */ - HASH4; - case 7: HASH4; - case 6: HASH4; - case 5: HASH4; - case 4: HASH4; - case 3: HASH4; - case 2: HASH4; - case 1: HASH4; - } while (--loop); - } - - } - return(h); -} diff --git a/lib/libc/db/hash/hsearch.c b/lib/libc/db/hash/hsearch.c index 0c19337c58..30657b2c8a 100644 --- a/lib/libc/db/hash/hsearch.c +++ b/lib/libc/db/hash/hsearch.c @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. @@ -35,81 +35,74 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)hsearch.c 5.5 (Berkeley) 3/12/91"; +static char sccsid[] = "@(#)hsearch.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include + #include #include + +#define __DBINTERFACE_PRIVATE #include #include "search.h" -static DB *dbp = NULL; -static ENTRY retval; +static DB *dbp = NULL; +static ENTRY retval; -extern int -hcreate ( nel ) -unsigned nel; +extern int +hcreate(nel) + u_int nel; { - int status; - HASHINFO info; + HASHINFO info; - info.nelem = nel; - info.bsize = 256; - info.ffactor = 8; - info.cachesize = NULL; - info.hash = NULL; - info.lorder = 0; - dbp = hash_open ( NULL, O_CREAT|O_RDWR, 0600, &info ); - return ( (int) dbp ); + info.nelem = nel; + info.bsize = 256; + info.ffactor = 8; + info.cachesize = NULL; + info.hash = NULL; + info.lorder = 0; + dbp = (DB *)__hash_open(NULL, O_CREAT | O_RDWR, 0600, &info); + return ((int)dbp); } - -extern ENTRY * -hsearch ( item, action ) -ENTRY item; -ACTION action; +extern ENTRY * +hsearch(item, action) + ENTRY item; + ACTION action; { - int status; - DBT key, val; - - if ( !dbp ) { - return(NULL); - } + DBT key, val; + int status; - key.data = (u_char *)item.key; - key.size = strlen(item.key) + 1; + if (!dbp) + return (NULL); + key.data = (u_char *)item.key; + key.size = strlen(item.key) + 1; - if ( action == ENTER ) { - val.data = (u_char *)item.data; - val.size = strlen(item.data) + 1; - status = (dbp->put) ( dbp, &key, &val, R_NOOVERWRITE ); - if ( status ) { - return(NULL); - } - } else { - /* FIND */ - status = (dbp->get) ( dbp, &key, &val, 0 ); - if ( status ) { - return ( NULL ); + if (action == ENTER) { + val.data = (u_char *)item.data; + val.size = strlen(item.data) + 1; + status = (dbp->put)(dbp, &key, &val, R_NOOVERWRITE); + if (status) + return (NULL); } else { - item.data = (char *)val.data; + /* FIND */ + status = (dbp->get)(dbp, &key, &val, 0); + if (status) + return (NULL); + else + item.data = (char *)val.data; } - } - retval.key = item.key; - retval.data = item.data; - return ( &retval ); + retval.key = item.key; + retval.data = item.data; + return (&retval); } - extern void -hdestroy () +hdestroy() { - if (dbp) { - (void)(dbp->close) (dbp); - dbp = NULL; - } - return; + if (dbp) { + (void)(dbp->close)(dbp); + dbp = NULL; + } } - - diff --git a/lib/libc/db/hash/log2.c b/lib/libc/db/hash/log2.c deleted file mode 100644 index 4d2a4c3d02..0000000000 --- a/lib/libc/db/hash/log2.c +++ /dev/null @@ -1,49 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Margo Seltzer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)log2.c 5.1 (Berkeley) 2/12/91"; -#endif /* LIBC_SCCS and not lint */ - -__log2( num ) -int num; -{ - register int i; - register int limit = 1; - - for ( i = 0; limit < num; limit = limit << 1, i++ ); - return (i); -} diff --git a/lib/libc/db/hash/ndbm.c b/lib/libc/db/hash/ndbm.c index 1167a08755..2756074744 100644 --- a/lib/libc/db/hash/ndbm.c +++ b/lib/libc/db/hash/ndbm.c @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. @@ -35,160 +35,168 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)ndbm.c 5.7 (Berkeley) 6/17/91"; +static char sccsid[] = "@(#)ndbm.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ /* - This package provides a dbm compatible interface to the new hashing - package described in db(3) -*/ + * This package provides a dbm compatible interface to the new hashing + * package described in db(3). + */ #include + #include -#include #include +#include + #include "hash.h" /* - return *DBM on success - NULL on failure -*/ + * Returns: + * *DBM on success + * NULL on failure + */ extern DBM * -dbm_open( file, flags, mode ) -const char *file; -int flags; -int mode; +dbm_open(file, flags, mode) + const char *file; + int flags, mode; { - HASHINFO info; - char path[MAXPATHLEN]; - - info.bsize = 1024; - info.ffactor = 5; - info.nelem = 1; - info.cachesize = NULL; - info.hash = NULL; - info.lorder = 0; - (void)strcpy(path, file); - (void)strcat(path, DBM_SUFFIX); - return( hash_open ( path, flags, mode, &info ) ); + HASHINFO info; + char path[MAXPATHLEN]; + + info.bsize = 4096; + info.ffactor = 40; + info.nelem = 1; + info.cachesize = NULL; + info.hash = NULL; + info.lorder = 0; + (void)strcpy(path, file); + (void)strcat(path, DBM_SUFFIX); + return ((DBM *)__hash_open(path, flags, mode, &info)); } -extern void +extern void dbm_close(db) -DBM *db; + DBM *db; { - (void)(db->close) (db); + (void)(db->close)(db); } /* - Returns DATUM on success - NULL on failure -*/ -extern datum -dbm_fetch( db, key ) -DBM *db; -datum key; + * Returns: + * DATUM on success + * NULL on failure + */ +extern datum +dbm_fetch(db, key) + DBM *db; + datum key; { - int status; - datum retval; - - status = (db->get) ( db, (DBT *)&key, (DBT *)&retval, 0 ); - if ( status ) { - retval.dptr = NULL; - retval.dsize = 0; - } - return(retval); + datum retval; + int status; + + status = (db->get)(db, (DBT *)&key, (DBT *)&retval, 0); + if (status) { + retval.dptr = NULL; + retval.dsize = 0; + } + return (retval); } /* - Returns DATUM on success - NULL on failure -*/ -extern datum + * Returns: + * DATUM on success + * NULL on failure + */ +extern datum dbm_firstkey(db) -DBM *db; + DBM *db; { - int status; - datum retkey; - datum retdata; - - status = (db->seq) ( db, (DBT *)&retkey, (DBT *)&retdata, R_FIRST ); - if ( status ) { - retkey.dptr = NULL; - } - return(retkey); + int status; + datum retdata, retkey; + + status = (db->seq)(db, (DBT *)&retkey, (DBT *)&retdata, R_FIRST); + if (status) + retkey.dptr = NULL; + return (retkey); } + /* - Returns DATUM on success - NULL on failure -*/ -extern datum + * Returns: + * DATUM on success + * NULL on failure + */ +extern datum dbm_nextkey(db) -DBM *db; + DBM *db; { - int status; - datum retkey; - datum retdata; - - status = (db->seq) ( db, (DBT *)&retkey, (DBT *)&retdata, R_NEXT ); - if ( status ) { - retkey.dptr = NULL; - } - return(retkey); -} + int status; + datum retdata, retkey; + status = (db->seq)(db, (DBT *)&retkey, (DBT *)&retdata, R_NEXT); + if (status) + retkey.dptr = NULL; + return (retkey); +} /* - 0 on success - <0 failure -*/ -extern int + * Returns: + * 0 on success + * <0 failure + */ +extern int dbm_delete(db, key) -DBM *db; -datum key; + DBM *db; + datum key; { - int status; - - status = (db->del)( db, (DBT *)&key, 0 ); - if ( status ) { - return(-1); - } else { - return(0); - } + int status; + + status = (db->del)(db, (DBT *)&key, 0); + if (status) + return (-1); + else + return (0); } /* - 0 on success - <0 failure - 1 if DBM_INSERT and entry exists -*/ -extern int + * Returns: + * 0 on success + * <0 failure + * 1 if DBM_INSERT and entry exists + */ +extern int dbm_store(db, key, content, flags) -DBM *db; -datum key; -datum content; -int flags; + DBM *db; + datum key, content; + int flags; { - return ((db->put)( db, (DBT *)&key, (DBT *)&content, - (flags == DBM_INSERT) ? R_NOOVERWRITE : 0 )); + return ((db->put)(db, (DBT *)&key, (DBT *)&content, + (flags == DBM_INSERT) ? R_NOOVERWRITE : 0)); } extern int dbm_error(db) -DBM *db; + DBM *db; { - HTAB *hp; + HTAB *hp; - hp = (HTAB *)db->internal; - return ( hp->errno ); + hp = (HTAB *)db->internal; + return (hp->errno); } extern int dbm_clearerr(db) -DBM *db; + DBM *db; { - HTAB *hp; + HTAB *hp; + + hp = (HTAB *)db->internal; + hp->errno = 0; + return (0); +} - hp = (HTAB *)db->internal; - hp->errno = 0; - return ( 0 ); +extern int +dbm_dirfno(db) + DBM *db; +{ + return(((HTAB *)db->internal)->fp); } diff --git a/lib/libc/db/hash/page.c b/lib/libc/db/hash/page.c deleted file mode 100644 index e577d658b5..0000000000 --- a/lib/libc/db/hash/page.c +++ /dev/null @@ -1,905 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Margo Seltzer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)page.c 5.13 (Berkeley) 6/17/91"; -#endif /* LIBC_SCCS and not lint */ - -/****************************************************************************** -PACKAGE: hashing - -DESCRIPTION: - Page manipulation for hashing package. - -ROUTINES: - External - __get_page - __add_ovflpage - Internal - overflow_page - open_temp -******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "hash.h" -#include "page.h" - -/* Externals */ -/* buf.c */ -extern BUFHEAD *__get_buf(); -extern void __reclaim_buf(); - -/* big.c */ -extern int __big_split(); -extern int __big_insert(); -extern int __big_delete(); -extern int __find_bigpair(); - -/* dynahash.c */ -extern u_int __call_hash(); -extern int __expand_table(); - -/* my externals */ -extern int __get_page(); -extern BUFHEAD *__add_ovflpage(); -extern int __split_page(); -extern int __addel(); - -/* my internals */ -static u_short overflow_page(); -static int open_temp(); -static int ugly_split(); -static void squeeze_key(); -static void putpair(); -static u_long *fetch_bitmap(); - -#ifdef HASH_STATISTICS -extern long hash_accesses, hash_collisions, hash_expansions, hash_overflows; -#endif -#define PAGE_INIT(P) \ -{ \ - ((u_short *)P)[0] = 0; \ - ((u_short *)P)[1] = hashp->BSIZE - 3 * sizeof(u_short); \ - ((u_short *)P)[2] = hashp->BSIZE; \ -} - -/* - This is called AFTER we have verified that there is room on the - page for the pair (PAIRFITS has returned true) so we go right - ahead and start moving stuff on. -*/ -static void -putpair(p, key, val) -char *p; -DBT *key; -DBT *val; -{ - register u_short n; - register u_short off; - register u_short *bp = (u_short *) p; - -/* enter the key first */ - n = bp[0]; - - off = OFFSET(bp) - key->size; - bcopy( key->data, p+off, key->size ); - bp[++n] = off; - -/* now the data */ - off -= val->size; - bcopy(val->data, p + off, val->size); - bp[++n] = off; - -/* adjust page info */ - bp[0] = n; - bp[n+1] = off - ((n+3)*sizeof(u_short)); - bp[n+2] = off; - return; -} -/* - 0 OK - -1 error -*/ -extern int -__delpair(bufp, ndx) -BUFHEAD *bufp; -register int ndx; -{ - register u_short *bp = (u_short *) bufp->page; - register int n = bp[0]; - register u_short newoff; - u_short pairlen; - - if ( bp[ndx+1] < REAL_KEY ) return ( __big_delete ( bufp, ndx ) ); - if ( ndx != 1 ) newoff = bp[ndx-1]; - else newoff = hashp->BSIZE; - pairlen = newoff - bp[ndx+1]; - - if ( ndx != (n-1) ) { - /* Hard Case -- need to shuffle keys */ - register int i; - register char *src = bufp->page + (int)OFFSET(bp); - register char *dst = src + (int)pairlen; - bcopy ( src, dst, bp[ndx+1] - OFFSET(bp) ); - - /* Now adjust the pointers */ - for ( i = ndx+2; i <= n; i += 2 ) { - if ( bp[i+1] == OVFLPAGE ) { - bp[i-2] = bp[i]; - bp[i-1] = bp[i+1]; - } else { - bp[i-2] = bp[i] + pairlen; - bp[i-1] = bp[i+1] + pairlen; - } - } - } - - /* Finally adjust the page data */ - bp[n] = OFFSET(bp) + pairlen; - bp[n-1] = bp[n+1] + pairlen + 2 * sizeof(u_short); - bp[0] = n-2; - hashp->NKEYS--; - - bufp->flags |= BUF_MOD; - return (0); -} -/* - -1 ==> Error - 0 ==> OK -*/ -extern int -__split_page(obucket, nbucket) -u_int obucket; -u_int nbucket; -{ - DBT key; - DBT val; - - register BUFHEAD *new_bufp; - register BUFHEAD *old_bufp; - register u_short *ino; - register char *np; - int n; - int ndx; - int retval; - char *op; - - u_short copyto = (u_short)hashp->BSIZE; - u_short diff; - u_short off = (u_short)hashp->BSIZE; - u_short moved; - - old_bufp = __get_buf ( obucket, NULL, 0 ); - new_bufp = __get_buf ( nbucket, NULL, 0 ); - - old_bufp->flags |= (BUF_MOD|BUF_PIN); - new_bufp->flags |= (BUF_MOD|BUF_PIN); - - ino = (u_short *)(op = old_bufp->page); - np = new_bufp->page; - - moved = 0; - - for (n = 1, ndx = 1; n < ino[0]; n+=2) { - if ( ino[n+1] < REAL_KEY ) { - retval = ugly_split( obucket, old_bufp, new_bufp, - copyto, moved ); - old_bufp->flags &= ~BUF_PIN; - new_bufp->flags &= ~BUF_PIN; - return(retval); - - } - key.data = (u_char *)op + ino[n]; - key.size = off - ino[n]; - - if ( __call_hash ( key.data, key.size ) == obucket ) { - /* Don't switch page */ - diff = copyto - off; - if ( diff ) { - copyto = ino[n+1] + diff; - bcopy ( op + ino[n+1], op + copyto, off-ino[n+1]); - ino[ndx] = copyto + ino[n] - ino[n+1]; - ino[ndx+1] = copyto; - } else copyto = ino[n+1]; - ndx += 2; - } else { - /* Switch page */ - val.data = (u_char *)op + ino[n+1]; - val.size = ino[n] - ino[n+1]; - putpair( np, &key, &val); - moved +=2; - } - - off = ino[n+1]; - } - - /* Now clean up the page */ - ino[0] -= moved; - FREESPACE(ino) = copyto - sizeof(u_short) * (ino[0]+3); - OFFSET(ino) = copyto; - -#ifdef DEBUG3 - fprintf(stderr, "split %d/%d\n", - ((u_short *) np)[0] / 2, - ((u_short *) op)[0] / 2); -#endif - /* unpin both pages */ - old_bufp->flags &= ~BUF_PIN; - new_bufp->flags &= ~BUF_PIN; - return(0); -} -/* - 0 ==> success - -1 ==> failure - - Called when we encounter an overflow or big key/data page during - split handling. - This is special cased since we have to begin checking whether - the key/data pairs fit on their respective pages and because - we may need overflow pages for both the old and new pages - - The first page might be a page with regular key/data pairs - in which case we have a regular overflow condition and just - need to go on to the next page or it might be a big key/data - pair in which case we need to fix the big key/data pair. -*/ -static int -ugly_split( obucket, old_bufp, new_bufp, copyto, moved ) -u_int obucket; /* Same as __split_page */ -BUFHEAD *old_bufp; -BUFHEAD *new_bufp; -u_short copyto; /* First byte on page which contains key/data values */ -int moved; /* number of pairs moved to new page */ -{ - register BUFHEAD *bufp = old_bufp; /* Buffer header for ino */ - register u_short *ino = (u_short *)old_bufp->page; - /* Page keys come off of */ - register u_short *np = (u_short *)new_bufp->page; /* New page */ - register u_short *op = (u_short *)old_bufp->page; - /* Page keys go on to if they - aren't moving */ - - char *cino; /* Character value of ino */ - BUFHEAD *last_bfp = NULL; /* Last buffer header OVFL which - needs to be freed */ - u_short ov_addr, last_addr = 0; - u_short n; - u_short off; - - DBT key, val; - SPLIT_RETURN ret; - - n = ino[0]-1; - while ( n < ino[0] ) { - if ( ino[2] < REAL_KEY && ino[2] != OVFLPAGE ) { - if (__big_split (old_bufp, new_bufp, bufp, ov_addr, obucket, &ret)) { - return(-1); - } - old_bufp = ret.oldp; - if ( !old_bufp ) return(-1); - op = (u_short *)old_bufp->page; - new_bufp = ret.newp; - if ( !new_bufp ) return(-1); - np = (u_short *)new_bufp->page; - bufp = ret.nextp; - if ( !bufp ) return(0); - cino = (char *)bufp->page; - ino = (u_short *)cino; - last_bfp = ret.nextp; - } else if ( ino[n+1] == OVFLPAGE ) { - ov_addr = ino[n]; - /* - Fix up the old page -- the extra 2 are the fields which - contained the overflow information - */ - ino[0] -= (moved + 2); - FREESPACE(ino) = copyto - sizeof(u_short) * (ino[0]+3); - OFFSET(ino) = copyto; - - bufp = __get_buf ( ov_addr, bufp, 0 ); - if ( !bufp ) return(-1); - - ino = (u_short *)bufp->page; - n = 1; - copyto = hashp->BSIZE; - moved = 0; - - if ( last_bfp ) { - __free_ovflpage( last_bfp); - } - last_bfp = bufp; - } - - - /* Move regular sized pairs of there are any */ - off = hashp->BSIZE; - for ( n = 1; (n < ino[0]) && (ino[n+1] >= REAL_KEY); n += 2 ) { - cino = (char *)ino; - key.data = (u_char *)cino + ino[n]; - key.size = off - ino[n]; - val.data = (u_char *)cino + ino[n+1]; - val.size = ino[n] - ino[n+1]; - off = ino[n+1]; - - if ( __call_hash ( key.data, key.size ) == obucket ) { - /* Keep on old page */ - if (PAIRFITS(op,(&key),(&val))) putpair((char *)op, &key, &val); - else { - old_bufp = __add_ovflpage ( old_bufp ); - if ( !old_bufp ) return(-1); - op = (u_short *)old_bufp->page; - putpair ((char *)op, &key, &val); - } - old_bufp->flags |= BUF_MOD; - } else { - /* Move to new page */ - if (PAIRFITS(np,(&key),(&val))) putpair((char *)np, &key, &val); - else { - new_bufp = __add_ovflpage ( new_bufp ); - if ( !new_bufp )return(-1); - np = (u_short *)new_bufp->page; - putpair ((char *)np, &key, &val); - } - new_bufp->flags |= BUF_MOD; - } - } - } - if ( last_bfp ) { - __free_ovflpage(last_bfp); - } - - return (0); -} -/* - Add the given pair to the page - 1 ==> failure - 0 ==> OK -*/ -extern int -__addel(bufp, key, val) -BUFHEAD *bufp; -DBT *key; -DBT *val; -{ - register u_short *bp = (u_short *)bufp->page; - register u_short *sop; - int do_expand; - - do_expand = 0; - while ( bp[0] && (bp[bp[0]] < REAL_KEY) ) { - /* Exception case */ - if ( bp[2] < REAL_KEY ) { - /* This is a big-keydata pair */ - bufp = __add_ovflpage(bufp); - if ( !bufp ) { - return(-1); - } - bp = (u_short *)bufp->page; - } else { - /* Try to squeeze key on this page */ - if ( FREESPACE(bp) > PAIRSIZE(key,val) ) { - squeeze_key ( bp, key, val ); - return(0); - } else { - bufp = __get_buf ( bp[bp[0]-1], bufp, 0 ); - if (!bufp) { - return(-1); - } - bp = (u_short *)bufp->page; - } - } - } - - if ( PAIRFITS(bp,key,val) ) putpair (bufp->page, key, val); - else { - do_expand = 1; - bufp = __add_ovflpage ( bufp ); - if (!bufp)return(-1); - sop = (u_short *) bufp->page; - - if ( PAIRFITS(sop, key, val) ) putpair ( (char *)sop, key, val ); - else if ( __big_insert ( bufp, key, val ) ) { - return(-1); - } - } - bufp->flags |= BUF_MOD; - /* - If the average number of keys per bucket exceeds the fill factor, - expand the table - */ - hashp->NKEYS++; - if (do_expand || - (hashp->NKEYS / (hashp->MAX_BUCKET+1) > hashp->FFACTOR) ) { - return(__expand_table()); - } - return(0); -} - -/* - returns a pointer, NULL on error -*/ -extern BUFHEAD * -__add_ovflpage ( bufp ) -BUFHEAD *bufp; -{ - register u_short *sp = (u_short *)bufp->page; - - u_short ovfl_num; - u_short ndx, newoff; - char *op; - DBT okey, oval; -#ifdef DEBUG1 - int tmp1, tmp2; -#endif - - bufp->flags |= BUF_MOD; - ovfl_num = overflow_page (); -#ifdef DEBUG1 - tmp1 = bufp->addr; - tmp2 = bufp->ovfl?bufp->ovfl->addr:0; -#endif - if (!ovfl_num || !(bufp->ovfl = __get_buf ( ovfl_num, bufp, 1 ))) { - return(NULL); - } - bufp->ovfl->flags |= BUF_MOD; -#ifdef DEBUG1 - fprintf ( stderr, "ADDOVFLPAGE: %d->ovfl was %d is now %d\n", tmp1, tmp2, - bufp->ovfl->addr ); -#endif - ndx = sp[0]; - /* - Since a pair is allocated on a page only if there's room - to add an overflow page, we know that the OVFL information - will fit on the page - */ - sp[ndx+4] = OFFSET(sp); - sp[ndx+3] = FREESPACE(sp) - OVFLSIZE; - sp[ndx+1] = ovfl_num; - sp[ndx+2] = OVFLPAGE; - sp[0] = ndx+2; -#ifdef HASH_STATISTICS - hash_overflows++; -#endif - return(bufp->ovfl); -} - -/* - 0 indicates SUCCESS - -1 indicates FAILURE -*/ -extern int -__get_page ( p, bucket, is_bucket, is_disk, is_bitmap ) -char *p; -u_int bucket; -int is_bucket; -int is_disk; -int is_bitmap; -{ - register int size; - register int fd; - register int page; - u_short *bp; - int rsize; - - fd = hashp->fp; - size = hashp->BSIZE; - - if ( (fd == -1) || !is_disk ) { - PAGE_INIT(p); - return(0); - } - - if ( is_bucket) page = BUCKET_TO_PAGE (bucket); - else page = OADDR_TO_PAGE (bucket); - if ((lseek ( fd, page << hashp->BSHIFT, SEEK_SET ) == -1) || - ((rsize = read ( fd, p, size )) == -1 )) { - return(-1); - } - bp = (u_short *)p; - if ( !rsize ) { - bp[0] = 0; /* We hit the EOF, so initialize a new page */ - } else if ( rsize != size ) { - errno = EFTYPE; - return(-1); - } - if (!bp[0]) { - PAGE_INIT(p); - } else if ( hashp->LORDER != BYTE_ORDER ) { - register int i; - register int max; - - if ( is_bitmap ) { - max = hashp->BSIZE >> 2; /* divide by 4 */ - for ( i=0; i < max; i++ ) { - BLSWAP(((long *)p)[i]); - } - } else { - BSSWAP(bp[0]); - max = bp[0] + 2; - for ( i=1; i <= max; i++ ) { - BSSWAP(bp[i]); - } - } - } - return (0); -} - -/* - Write page p to disk - -1==>failure - 0==> OK -*/ -extern int -__put_page ( p, bucket, is_bucket, is_bitmap ) -char *p; -u_int bucket; -int is_bucket; -int is_bitmap; -{ - register int size; - register int fd; - register int page; - int wsize; - - size = hashp->BSIZE; - if ( (hashp->fp == -1) && open_temp() ) return (1); - fd = hashp->fp; - - if ( hashp->LORDER != BYTE_ORDER ) { - register int i; - register int max; - - if ( is_bitmap ) { - max = hashp->BSIZE >> 2; /* divide by 4 */ - for ( i=0; i < max; i++ ) { - BLSWAP(((long *)p)[i]); - } - } else { - max = ((u_short *)p)[0] + 2; - for ( i=0; i <= max; i++ ) { - BSSWAP(((u_short *)p)[i]); - } - } - } - if (is_bucket ) page = BUCKET_TO_PAGE (bucket); - else page = OADDR_TO_PAGE ( bucket ); - if ((lseek ( fd, page << hashp->BSHIFT, SEEK_SET ) == -1) || - ((wsize = write ( fd, p, size )) == -1 )) { - /* Errno is set */ - return(-1); - } - if ( wsize != size ) { - errno = EFTYPE; - return(-1); - } - return(0); -} -#define BYTE_MASK ((1 << INT_BYTE_SHIFT) -1) -/* - Initialize a new bitmap page. Bitmap pages are left in memory - once they are read in. -*/ -extern u_long * -__init_bitmap(pnum, nbits, ndx) -u_short pnum; -int nbits; -int ndx; -{ - u_long *ip; - int clearints; - int clearbytes; - - if ( !(ip = (u_long *)malloc (hashp->BSIZE)) ) return (NULL); - hashp->nmaps++; - clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1; - clearbytes = clearints << INT_TO_BYTE; - memset ((char *)ip, 0, clearbytes ); - memset ( ((char *) ip) + clearbytes, 0xFF, - hashp->BSIZE-clearbytes ); - ip[clearints-1] = ALL_SET << (nbits & BYTE_MASK); - SETBIT(ip, 0); - hashp->BITMAPS[ndx] = pnum; - hashp->mapp[ndx] = ip; - return(ip); -} -static int -first_free ( map ) -u_long map; -{ - register u_long mask; - register u_long i; - - mask = 0x1; - for ( i=0; i < BITS_PER_MAP; i++ ) { - if ( !(mask & map) ) return(i); - mask = mask << 1; - } - return ( i ); -} - -static u_short -overflow_page ( ) -{ - register int max_free; - register int splitnum; - register u_long *freep; - register int offset; - u_short addr; - int in_use_bits; - int free_page, free_bit; - int i, j, bit; -#ifdef DEBUG2 - int tmp1, tmp2; -#endif - - splitnum = __log2(hashp->MAX_BUCKET); - max_free = hashp->SPARES[splitnum]; - - free_page = (max_free-1) >> (hashp->BSHIFT + BYTE_SHIFT); - free_bit = (max_free-1) & ((hashp->BSIZE << BYTE_SHIFT) - 1); - - /* Look through all the free maps to find the first free block */ - for ( i = 0; i <= free_page; i++ ) { - if (!(freep = (u_long *)hashp->mapp[i]) && - !(freep = fetch_bitmap(i)) ) { - return ( NULL ); - } - if ( i == free_page ) in_use_bits = free_bit; - else in_use_bits = (hashp->BSIZE << BYTE_SHIFT) -1; - - for (j = 0, bit = 0; bit <= in_use_bits; j++, bit += BITS_PER_MAP ) { - if ( freep[j] != ALL_SET ) goto found; - } - } - /* No Free Page Found */ - hashp->SPARES[splitnum]++; - offset = hashp->SPARES[splitnum] - - (splitnum ? hashp->SPARES[splitnum-1] : 0); - - /* Check if we need to allocate a new bitmap page */ - if ( free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1 ) { - free_page++; -#define OVMSG "hash: out of overflow pages; increase page size\n" - if ( free_page >= NCACHED ) { - (void) write (STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); - return(NULL); - } - /* - This is tricky. The 1 indicates that you want the - new page allocated with 1 clear bit. Actually, you - are going to allocate 2 pages from this map. The first - is going to be the map page, the second is the overflow - page we were looking for. The init_bitmap routine - automatically, sets the first bit of itself to indicate - that the bitmap itself is in use. We would explicitly - set the second bit, but don't have to if we tell init_bitmap - not to leave it clear in the first place. - */ - __init_bitmap ( OADDR_OF(splitnum, offset), 1, free_page ); - hashp->SPARES[splitnum]++; -#ifdef DEBUG2 - free_bit = 2; -#endif - offset++; - } else { - /* - Free_bit addresses the last used bit. Bump it to - address the first available bit. - */ - free_bit++; - SETBIT ( freep, free_bit ); - } - - /* Calculate address of the new overflow page */ - if ( offset > SPLITMASK ) { - (void) write (STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); - return(NULL); - } - addr = OADDR_OF(splitnum, offset); -#ifdef DEBUG2 - fprintf ( stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", - addr, free_bit, free_page ); -#endif - return(addr); - -found: - bit = bit + first_free(freep[j]); - SETBIT(freep,bit); -#ifdef DEBUG2 - tmp1 = bit; - tmp2 = i; -#endif - /* - Bits are addressed starting with 0, but overflow pages are - addressed beginning at 1. Bit is a bit addressnumber, so we - need to increment it to convert it to a page number. - */ - bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT)); - - /* Calculate the split number for this page */ - for ( i = 0; (i < splitnum) && (bit > hashp->SPARES[i]); i++ ); - offset =(i ? bit - hashp->SPARES[i-1] : bit ); - if ( offset >= SPLITMASK ) return(NULL);/* Out of overflow pages */ - addr = OADDR_OF(i, offset); -#ifdef DEBUG2 - fprintf ( stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", - addr, tmp1, tmp2 ); -#endif - - /* Allocate and return the overflow page */ - return (addr); -} - -/* - Mark this overflow page as free. -*/ -__free_ovflpage ( obufp ) -BUFHEAD *obufp; -{ - register u_short addr = obufp->addr; - int free_page, free_bit; - int bit_address; - u_short ndx; - u_long *freep; - int j; - -#ifdef DEBUG1 - fprintf ( stderr, "Freeing %d\n", addr ); -#endif - ndx = (((u_short)addr) >> SPLITSHIFT); - bit_address = (ndx ? hashp->SPARES[ndx-1] : 0) + (addr & SPLITMASK) - 1; - free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT)); - free_bit = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1); - - if ( !(freep = hashp->mapp[free_page]) && - !(freep = fetch_bitmap( free_page )) ) { - /* - This had better never happen. It means we tried to - read a bitmap that has already had overflow pages allocated - off it, and we failed to read it from the file - */ - assert(0); - } - CLRBIT(freep, free_bit); -#ifdef DEBUG2 - fprintf ( stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n", - obufp->addr, free_bit, free_page ); -#endif - __reclaim_buf ( obufp ); - return; -} - -/* -0 success --1 failure -*/ -static int -open_temp() -{ - sigset_t set, oset; - static char namestr[] = "_hashXXXXXX"; - - /* Block signals; make sure file goes away at process exit. */ - sigemptyset(&set); - sigaddset(&set, SIGHUP); - sigaddset(&set, SIGINT); - sigaddset(&set, SIGQUIT); - sigaddset(&set, SIGTERM); - (void)sigprocmask(SIG_BLOCK, &set, &oset); - if ((hashp->fp = mkstemp ( namestr )) != -1) { - (void)unlink(namestr); - (void)fcntl(hashp->fp, F_SETFD, 1); - } - (void)sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL); - return(hashp->fp != -1 ? 0 : -1); -} - -/* - We have to know that the key will fit, but the - last entry on the page is an overflow pair, so we - need to shift things. -*/ -static void -squeeze_key ( sp, key, val ) -u_short *sp; -DBT *key; -DBT *val; -{ - register char *p = (char *)sp; - u_short free_space, off; - u_short pageno, n; - - n = sp[0]; - free_space = FREESPACE(sp); - off = OFFSET(sp); - - pageno = sp[n-1]; - off -= key->size; - sp[n-1] = off; - bcopy ( key->data, p + off, key->size ); - off -= val->size; - sp[n] = off; - bcopy ( val->data, p + off, val->size ); - sp[0] = n+2; - sp[n+1] = pageno; - sp[n+2] = OVFLPAGE; - FREESPACE(sp) = free_space - PAIRSIZE(key,val); - OFFSET(sp) = off; -} - -static u_long * -fetch_bitmap ( ndx ) -int ndx; -{ - if ( ndx >= hashp->nmaps || - !(hashp->mapp[ndx] = (u_long *)malloc ( hashp->BSIZE )) || - __get_page ((char *)hashp->mapp[ndx], hashp->BITMAPS[ndx], 0, 1, 1)) { - - return(NULL); - } - return ( hashp->mapp[ndx] ); -} -#ifdef DEBUG4 -print_chain ( addr ) -short addr; -{ - BUFHEAD *bufp; - short *bp; - short oaddr; - - fprintf ( stderr, "%d ", addr ); - bufp = __get_buf ( (int)addr, NULL, 0 ); - bp = (short *)bufp->page; - while ( bp[0] && - ((bp[bp[0]] == OVFLPAGE) || - ((bp[0] > 2) && bp[2] < REAL_KEY))) { - oaddr = bp[bp[0]-1]; - fprintf ( stderr, "%d ", (int)oaddr ); - bufp = __get_buf ( (int)oaddr, bufp, 0 ); - bp = (short *)bufp->page; - } - fprintf ( stderr, "\n" ); -} -#endif diff --git a/lib/libc/db/hash/page.h b/lib/libc/db/hash/page.h index 2d6cae1a32..dae82aebdd 100644 --- a/lib/libc/db/hash/page.h +++ b/lib/libc/db/hash/page.h @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. @@ -33,13 +33,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)page.h 5.1 (Berkeley) 2/12/91 + * @(#)page.h 8.1 (Berkeley) 6/6/93 */ /* - Definitions for hashing page file format. -*/ -extern HTAB *hashp; + * Definitions for hashing page file format. + */ + /* * routines dealing with a data page * @@ -61,32 +61,32 @@ extern HTAB *hashp; */ /* - How many bytes required for this pair? - 2 shorts in the table at the top of the page + - room for the key and room for the data - - We prohibit entering a pair on a page unless there is also - room to append an overflow page. The reason for this it that - you can get in a situation where a single key/data pair fits - on a page, but you can't append an overflow page and later - you'd have to split the key/data and handle like a big pair. - You might as well do this up front. + * How many bytes required for this pair? + * 2 shorts in the table at the top of the page + room for the + * key and room for the data + * + * We prohibit entering a pair on a page unless there is also room to append + * an overflow page. The reason for this it that you can get in a situation + * where a single key/data pair fits on a page, but you can't append an + * overflow page and later you'd have to split the key/data and handle like + * a big pair. + * You might as well do this up front. + */ -*/ -#define PAIRSIZE(K,D) (2*sizeof(u_short) + K->size + D->size) +#define PAIRSIZE(K,D) (2*sizeof(u_short) + (K)->size + (D)->size) #define BIGOVERHEAD (4*sizeof(u_short)) -#define KEYSIZE(K) (4*sizeof(u_short) + K->size); +#define KEYSIZE(K) (4*sizeof(u_short) + (K)->size); #define OVFLSIZE (2*sizeof(u_short)) -#define FREESPACE(P) (P[P[0]+1]) -#define OFFSET(P) (P[P[0]+2]) -#define PAIRFITS(P,K,D) ((P[1] >= REAL_KEY) && \ - (PAIRSIZE(K,D) + OVFLSIZE) <= FREESPACE(P)) -#define PAGE_META(N) ((N+3) * sizeof(u_short)) +#define FREESPACE(P) ((P)[(P)[0]+1]) +#define OFFSET(P) ((P)[(P)[0]+2]) +#define PAIRFITS(P,K,D) \ + (((P)[2] >= REAL_KEY) && \ + (PAIRSIZE((K),(D)) + OVFLSIZE) <= FREESPACE((P))) +#define PAGE_META(N) (((N)+3) * sizeof(u_short)) typedef struct { - BUFHEAD *newp; - BUFHEAD *oldp; - BUFHEAD *nextp; - u_short next_addr; -} SPLIT_RETURN; - + BUFHEAD *newp; + BUFHEAD *oldp; + BUFHEAD *nextp; + u_short next_addr; +} SPLIT_RETURN; diff --git a/lib/libc/db/hash/search.h b/lib/libc/db/hash/search.h index ab3e83851e..4d3b9143e7 100644 --- a/lib/libc/db/hash/search.h +++ b/lib/libc/db/hash/search.h @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. @@ -33,13 +33,19 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)search.h 5.1 (Berkeley) 2/12/91 + * @(#)search.h 8.1 (Berkeley) 6/4/93 */ -/* Backward compatibility to hsearch interface */ -typedef struct entry { - char *key; - char *data; +/* Backward compatibility to hsearch interface. */ +typedef struct entry { + char *key; + char *data; } ENTRY; -typedef enum { FIND, ENTER } ACTION; +typedef enum { + FIND, ENTER +} ACTION; + +int hcreate __P((unsigned int)); +void hdestroy __P((void)); +ENTRY *hsearch __P((ENTRY, ACTION)); diff --git a/lib/libc/db/hash/tests/tcreat3.c b/lib/libc/db/hash/tests/tcreat3.c deleted file mode 100644 index 0d27a9d994..0000000000 --- a/lib/libc/db/hash/tests/tcreat3.c +++ /dev/null @@ -1,104 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Margo Seltzer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1990 The Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)tcreat3.c 5.1 (Berkeley) 1/31/91"; -#endif /* not lint */ - -#include -#include -#include -#include - -#define INITIAL 25000 -#define MAXWORDS 25000 /* # of elements in search table */ - -char wp1[8192]; -char wp2[8192]; -main(argc, argv) -char **argv; -{ - DBT item, key; - DB *dbp; - HASHINFO ctl; - FILE *fp; - int trash; - - int i = 0; - - argv++; - ctl.hash = NULL; - ctl.bsize = atoi(*argv++); - ctl.ffactor = atoi(*argv++); - ctl.nelem = atoi(*argv++); - ctl.lorder = 0; - if (!(dbp = hash_open( "hashtest", O_CREAT|O_TRUNC|O_RDWR, 0600, &ctl))){ - /* create table */ - fprintf(stderr, "cannot create: hash table (size %d)\n", - INITIAL); - exit(1); - } - - key.data = wp1; - item.data = wp2; - while ( fgets(wp1, 8192, stdin) && - fgets(wp2, 8192, stdin) && - i++ < MAXWORDS) { -/* -* put info in structure, and structure in the item -*/ - key.size = strlen(wp1); - item.size = strlen(wp2); - -/* - * enter key/data pair into the table - */ - if ((dbp->put)(dbp, &key, &item, R_NOOVERWRITE) != NULL) { - fprintf(stderr, "cannot enter: key %s\n", - item.data); - exit(1); - } - } - - (dbp->close)(dbp); - exit(0); -} diff --git a/lib/libc/db/hash/tests/tdel.c b/lib/libc/db/hash/tests/tdel.c deleted file mode 100644 index 1a3755b8a4..0000000000 --- a/lib/libc/db/hash/tests/tdel.c +++ /dev/null @@ -1,122 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Margo Seltzer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1990 The Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)tdel.c 5.2 (Berkeley) 3/12/91"; -#endif /* not lint */ - -#include -#include -#include -#include - -#define INITIAL 25000 -#define MAXWORDS 25000 /* # of elements in search table */ - -/* Usage: thash pagesize fillfactor file */ -char wp1[8192]; -char wp2[8192]; -main(argc, argv) -char **argv; -{ - DBT item, key; - DB *dbp; - HASHINFO ctl; - FILE *fp; - int stat; - - int i = 0; - - argv++; - ctl.nelem = INITIAL; - ctl.hash = NULL; - ctl.bsize = atoi(*argv++); - ctl.ffactor = atoi(*argv++); - ctl.cachesize = 1024 * 1024; /* 1 MEG */ - ctl.lorder = 0; - argc -= 2; - if (!(dbp = hash_open( NULL, O_CREAT|O_RDWR, 0400, &ctl))) { - /* create table */ - fprintf(stderr, "cannot create: hash table size %d)\n", - INITIAL); - exit(1); - } - - key.data = wp1; - item.data = wp2; - while ( fgets(wp1, 8192, stdin) && - fgets(wp2, 8192, stdin) && - i++ < MAXWORDS) { -/* -* put info in structure, and structure in the item -*/ - key.size = strlen(wp1); - item.size = strlen(wp2); - -/* - * enter key/data pair into the table - */ - if ((dbp->put)(dbp, &key, &item, R_NOOVERWRITE) != NULL) { - fprintf(stderr, "cannot enter: key %s\n", - item.data); - exit(1); - } - } - - if ( --argc ) { - fp = fopen ( argv[0], "r"); - i = 0; - while ( fgets(wp1, 8192, fp) && - fgets(wp2, 8192, fp) && - i++ < MAXWORDS) { - key.size = strlen(wp1); - stat = (dbp->delete)(dbp, &key); - if (stat) { - fprintf ( stderr, "Error retrieving %s\n", key.data ); - exit(1); - } - } - fclose(fp); - } - (dbp->close)(dbp); - exit(0); -} diff --git a/lib/libc/db/hash/tests/thash4.c b/lib/libc/db/hash/tests/thash4.c deleted file mode 100644 index cf24e07cdb..0000000000 --- a/lib/libc/db/hash/tests/thash4.c +++ /dev/null @@ -1,132 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Margo Seltzer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1990 The Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)thash4.c 5.2 (Berkeley) 3/12/91"; -#endif /* not lint */ - -#include -#include -#include -#include -#include -#include - -#define INITIAL 25000 -#define MAXWORDS 25000 /* # of elements in search table */ - -/* Usage: thash pagesize fillfactor file */ -char wp1[256]; -char wp2[8192]; -main(argc, argv) -char **argv; -{ - DBT item, key, res; - DB *dbp; - HASHINFO ctl; - FILE *fp; - int stat; - time_t t; - - int i = 0; - - argv++; - ctl.hash = NULL; - ctl.bsize = atoi(*argv++); - ctl.ffactor = atoi(*argv++); - ctl.nelem = atoi(*argv++); - ctl.cachesize = atoi(*argv++); - ctl.lorder = 0; - if (!(dbp = hash_open( NULL, O_CREAT|O_RDWR, 0400, &ctl))) { - /* create table */ - fprintf(stderr, "cannot create: hash table size %d)\n", - INITIAL); - fprintf(stderr, "\terrno: %d\n", errno); - exit(1); - } - - key.data = wp1; - item.data = wp2; - while ( fgets(wp1, 256, stdin) && - fgets(wp2, 8192, stdin) && - i++ < MAXWORDS) { -/* -* put info in structure, and structure in the item -*/ - key.size = strlen(wp1); - item.size = strlen(wp2); - -/* - * enter key/data pair into the table - */ - if ((dbp->put)(dbp, &key, &item, R_NOOVERWRITE) != NULL) { - fprintf(stderr, "cannot enter: key %s\n", - item.data); - fprintf(stderr, "\terrno: %d\n", errno); - exit(1); - } - } - - if ( --argc ) { - fp = fopen ( argv[0], "r"); - i = 0; - while ( fgets(wp1, 256, fp) && - fgets(wp2, 8192, fp) && - i++ < MAXWORDS) { - - key.size = strlen(wp1); - stat = (dbp->get)(dbp, &key, &res); - if (stat < 0 ) { - fprintf ( stderr, "Error retrieving %s\n", key.data ); - fprintf(stderr, "\terrno: %d\n", errno); - exit(1); - } else if ( stat > 0 ) { - fprintf ( stderr, "%s not found\n", key.data ); - fprintf(stderr, "\terrno: %d\n", errno); - exit(1); - } - } - fclose(fp); - } - dbp->close(dbp); - exit(0); -} diff --git a/lib/libc/db/hash/tests/tread2.c b/lib/libc/db/hash/tests/tread2.c deleted file mode 100644 index 481076abff..0000000000 --- a/lib/libc/db/hash/tests/tread2.c +++ /dev/null @@ -1,105 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Margo Seltzer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1990 The Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)tread2.c 5.2 (Berkeley) 3/12/91"; -#endif /* not lint */ - -#include -#include -#include -#include - -#define INITIAL 25000 -#define MAXWORDS 25000 /* # of elements in search table */ - -typedef struct { /* info to be stored */ - int num, siz; -} info; - -char wp1[256]; -char wp2[256]; -main(argc, argv) -char **argv; -{ - DBT item, key, res; - DB *dbp; - HASHINFO ctl; - int stat; - - int i = 0; - - ctl.nelem = INITIAL; - ctl.hash = NULL; - ctl.bsize = 64; - ctl.ffactor = 1; - ctl.cachesize = atoi(*argv++); - ctl.lorder = 0; - if (!(dbp = hash_open( "hashtest", O_RDONLY, 0400, &ctl))) { - /* create table */ - fprintf(stderr, "cannot open: hash table\n" ); - exit(1); - } - - key.data = wp1; - item.data = wp2; - while ( fgets(wp1, 256, stdin) && - fgets(wp2, 256, stdin) && - i++ < MAXWORDS) { -/* -* put info in structure, and structure in the item -*/ - key.size = strlen(wp1); - item.size = strlen(wp2); - - stat = (dbp->get)(dbp, &key, &res); - if (stat < 0) { - fprintf ( stderr, "Error retrieving %s\n", key.data ); - exit(1); - } else if ( stat > 0 ) { - fprintf ( stderr, "%s not found\n", key.data ); - exit(1); - } - } - (dbp->close)(dbp); - exit(0); -} diff --git a/lib/libc/db/hash/tests/tseq.c b/lib/libc/db/hash/tests/tseq.c deleted file mode 100644 index 9cab86cb0b..0000000000 --- a/lib/libc/db/hash/tests/tseq.c +++ /dev/null @@ -1,88 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Margo Seltzer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1990 The Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)tseq.c 5.1 (Berkeley) 1/31/91"; -#endif /* not lint */ - -#include -#include -#include -#include - -#define INITIAL 25000 -#define MAXWORDS 25000 /* # of elements in search table */ - - -char wp[8192]; -char cp[8192]; -main(argc, argv) -char **argv; -{ - DBT item, key, res; - DB *dbp; - FILE *fp; - int stat; - - if (!(dbp = hash_open( "hashtest", O_RDONLY, 0400, NULL))) { - /* create table */ - fprintf(stderr, "cannot open: hash table\n" ); - exit(1); - } - -/* -* put info in structure, and structure in the item -*/ - for ( stat = (dbp->seq) (dbp, &res, &item, 1 ); - stat == 0; - stat = (dbp->seq) (dbp, &res, &item, 0 ) ) { - - bcopy ( res.data, wp, res.size ); - wp[res.size] = 0; - bcopy ( item.data, cp, item.size ); - cp[item.size] = 0; - - printf ( "%s %s\n", wp, cp ); - } - (dbp->close)(dbp); - exit(0); -} diff --git a/lib/libc/db/hash/tests/tverify.c b/lib/libc/db/hash/tests/tverify.c deleted file mode 100644 index 9f57931fdf..0000000000 --- a/lib/libc/db/hash/tests/tverify.c +++ /dev/null @@ -1,107 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Margo Seltzer. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1990 The Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)tverify.c 5.2 (Berkeley) 3/12/91"; -#endif /* not lint */ - -#include -#include -#include -#include - -#define INITIAL 25000 -#define MAXWORDS 25000 /* # of elements in search table */ - -typedef struct { /* info to be stored */ - int num, siz; -} info; - -char wp1[8192]; -char wp2[8192]; -main(argc, argv) -char **argv; -{ - DBT key, res; - DB *dbp; - HASHINFO ctl; - int trash; - int stat; - - int i = 0; - - ctl.nelem = INITIAL; - ctl.hash = NULL; - ctl.bsize = 64; - ctl.ffactor = 1; - ctl.cachesize = 1024 * 1024; /* 1 MEG */ - ctl.lorder = 0; - if (!(dbp = hash_open( "hashtest", O_RDONLY, 0400, &ctl))) { - /* create table */ - fprintf(stderr, "cannot open: hash table\n" ); - exit(1); - } - - key.data = wp1; - while ( fgets(wp1, 8192, stdin) && - fgets(wp2, 8192, stdin) && - i++ < MAXWORDS) { -/* -* put info in structure, and structure in the item -*/ - key.size = strlen(wp1); - - stat = (dbp->get)(dbp, &key, &res); - if (stat < 0) { - fprintf ( stderr, "Error retrieving %s\n", key.data ); - exit(1); - } else if ( stat > 0 ) { - fprintf ( stderr, "%s not found\n", key.data ); - exit(1); - } - if ( memcmp ( res.data, wp2, res.size ) ) { - fprintf ( stderr, "data for %s is incorrect. Data was %s. Should have been %s\n", key.data, res.data, wp2 ); - } - } - (dbp->close)(dbp); - exit(0); -} diff --git a/lib/libc/db/man/Makefile.inc b/lib/libc/db/man/Makefile.inc new file mode 100644 index 0000000000..f715e0161f --- /dev/null +++ b/lib/libc/db/man/Makefile.inc @@ -0,0 +1,7 @@ +# @(#)Makefile.inc 8.1 (Berkeley) 6/4/93 + +.PATH: ${.CURDIR}/db/man + +# mpool.3 +MAN3+= db/man/btree.3 db/man/dbopen.3 db/man/hash.3 db/man/recno.3 +MLINKS+= dbopen.3 db.3 diff --git a/lib/libc/db/man/btree.3 b/lib/libc/db/man/btree.3 new file mode 100644 index 0000000000..923bc263ca --- /dev/null +++ b/lib/libc/db/man/btree.3 @@ -0,0 +1,224 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)btree.3 8.1 (Berkeley) 6/4/93 +.\" +.TH BTREE 3 +.\".UC 7 +.SH NAME +btree \- btree database access method +.SH SYNOPSIS +.nf +.ft B +#include +#include +.ft R +.fi +.SH DESCRIPTION +The routine +.IR dbopen +is the library interface to database files. +One of the supported file formats is btree files. +The general description of the database access methods is in +.IR dbopen (3), +this manual page describes only the btree specific information. +.PP +The btree data structure is a sorted, balanced tree structure storing +associated key/data pairs. +.PP +The btree access method specific data structure provided to +.I dbopen +is defined in the include file as follows: +.PP +typedef struct { +.RS +u_long flags; +.br +u_int cachesize; +.br +index_t psize; +.br +int lorder; +.\" .br +.\" int maxkeypage; +.br +int minkeypage; +.br +int (*compare)(const DBT *key1, const DBT *key2); +.br +int (*prefix)(const DBT *key1, const DBT *key2); +.RE +} BTREEINFO; +.PP +The elements of this structure are as follows: +.TP +flags +The flag value is specified by +.IR or 'ing +any of the following values: +.RS +.TP +R_DUP +Permit duplicate keys in the tree, i.e. permit insertion if the key to be +inserted already exists in the tree. +The default behavior, as described in +.IR dbopen (3), +is to overwrite a matching key when inserting a new key or to fail if +the R_NOOVERWRITE flag is specified. +The R_DUP flag is overridden by the R_NOOVERWRITE flag, and if the +R_NOOVERWRITE flag is specified, attempts to insert duplicate keys into +the tree will fail. +.IP +If the database contains duplicate keys, the order of retrieval of +key/data pairs is undefined if the +.I get +routine is used, however, +.I seq +routine calls with the R_CURSOR flag set will always return the logical +``first'' of any group of duplicate keys. +.RE +.TP +cachesize +A suggested maximum size (in bytes) of the memory cache. +This value is +.B only +advisory, and the access method will allocate more memory rather than fail. +Since every search examines the root page of the tree, caching the most +recently used pages substantially improves access time. +In addition, physical writes are delayed as long as possible, so a moderate +cache can reduce the number of I/O operations significantly. +Obviously, using a cache increases (but only increases) the likelihood of +corruption or lost data if the system crashes while a tree is being modified. +If +.I cachesize +is 0 (no size is specified) a default cache is used. +.TP +psize +Page size is the size (in bytes) of the pages used for nodes in the tree. +The minimum page size is 512 bytes and the maximum page size is 64K. +If +.I psize +is 0 (no page size is specified) a page size is chosen based on the +underlying file system I/O block size. +.TP +lorder +The byte order for integers in the stored database metadata. +The number should represent the order as an integer; for example, +big endian order would be the number 4,321. +If +.I lorder +is 0 (no order is specified) the current host order is used. +.\" .TP +.\" maxkeypage +.\" The maximum number of keys which will be stored on any single page. +.\" Because of the way the btree data structure works, +.\" .I maxkeypage +.\" must always be greater than or equal to 2. +.\" If +.\" .I maxkeypage +.\" is 0 (no maximum number of keys is specified) the page fill factor is +.\" made as large as possible (which is almost invariably what is wanted). +.TP +minkeypage +The minimum number of keys which will be stored on any single page. +This value is used to determine which keys will be stored on overflow +pages, i.e. if a key or data item is longer than the pagesize divided +by the minkeypage value, it will be stored on overflow pages instead +of in the page itself. +If +.I minkeypage +is 0 (no minimum number of keys is specified) a value of 2 is used. +.TP +compare +Compare is the key comparison function. +It must return an integer less than, equal to, or greater than zero if the +first key argument is considered to be respectively less than, equal to, +or greater than the second key argument. +The same comparison function must be used on a given tree every time it +is opened. +If +.I compare +is NULL (no comparison function is specified), the keys are compared +lexically, with shorter keys considered less than longer keys. +.TP +prefix +Prefix is the prefix comparison function. +If specified, this routine must return the number of bytes of the second key +argument which are necessary to determine that it is greater than the first +key argument. +If the keys are equal, the key length should be returned. +Note, the usefulness of this routine is very data dependent, but, in some +data sets can produce significantly reduced tree sizes and search times. +If +.I prefix +is NULL (no prefix function is specified), +.B and +no comparison function is specified, a default lexical comparison routine +is used. +If +.I prefix +is NULL and a comparison routine is specified, no prefix comparison is +done. +.PP +If the file already exists (and the O_TRUNC flag is not specified), the +values specified for the parameters flags, lorder and psize are ignored +in favor of the values used when the tree was created. +.PP +Forward sequential scans of a tree are from the least key to the greatest. +.PP +Space freed up by deleting key/data pairs from the tree is never reclaimed, +although it is normally made available for reuse. +This means that the btree storage structure is grow-only. +The only solutions are to avoid excessive deletions, or to create a fresh +tree periodically from a scan of an existing one. +.PP +Searches, insertions, and deletions in a btree will all complete in +O lg base N where base is the average fill factor. +Often, inserting ordered data into btrees results in a low fill factor. +This implementation has been modified to make ordered insertion the best +case, resulting in a much better than normal page fill factor. +.SH "SEE ALSO" +.IR dbopen (3), +.IR hash (3), +.IR mpool (3), +.IR recno (3) +.sp +.IR "The Ubiquitous B-tree" , +Douglas Comer, ACM Comput. Surv. 11, 2 (June 1979), 121-138. +.sp +.IR "Prefix B-trees" , +Bayer and Unterauer, ACM Transactions on Database Systems, Vol. 2, 1 +(March 1977), 11-26. +.sp +.IR "The Art of Computer Programming Vol. 3: Sorting and Searching" , +D.E. Knuth, 1968, pp 471-480. +.SH BUGS +Only big and little endian byte order is supported. diff --git a/lib/libc/db/man/dbopen.3 b/lib/libc/db/man/dbopen.3 new file mode 100644 index 0000000000..a2e2bfd14b --- /dev/null +++ b/lib/libc/db/man/dbopen.3 @@ -0,0 +1,453 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)dbopen.3 8.1 (Berkeley) 6/4/93 +.\" +.TH DBOPEN 3 "June 4, 1993" +.UC 7 +.SH NAME +dbopen \- database access methods +.SH SYNOPSIS +.nf +.ft B +#include +#include +#include + +DB * +dbopen(const char *file, int flags, int mode, DBTYPE type, +.ti +5 +const void *openinfo); +.ft R +.fi +.SH DESCRIPTION +.IR Dbopen +is the library interface to database files. +The supported file formats are btree, hashed and UNIX file oriented. +The btree format is a representation of a sorted, balanced tree structure. +The hashed format is an extensible, dynamic hashing scheme. +The flat-file format is a byte stream file with fixed or variable length +records. +The formats and file format specific information are described in detail +in their respective manual pages +.IR btree (3), +.IR hash (3) +and +.IR recno (3). +.PP +Dbopen opens +.I file +for reading and/or writing. +Files never intended to be preserved on disk may be created by setting +the file parameter to NULL. +.PP +The +.I flags +and +.I mode arguments +are as specified to the +.IR open (2) +routine, however, only the O_CREAT, O_EXCL, O_EXLOCK, O_RDONLY, O_RDWR, +O_SHLOCK and O_TRUNC flags are meaningful. +(Note, opening a database file O_WRONLY is not possible.) +.PP +The +.I type +argument is of type DBTYPE (as defined in the include file) and +may be set to DB_BTREE, DB_HASH or DB_RECNO. +.PP +The +.I openinfo +argument is a pointer to an access method specific structure described +in the access method's manual page. +If +.I openinfo +is NULL, each access method will use defaults appropriate for the system +and the access method. +.PP +.I Dbopen +returns a pointer to a DB structure on success and NULL on error. +The DB structure is defined in the include file, and contains at +least the following fields: +.sp +.nf +typedef struct { +.RS +DBTYPE type; +int (*close)(const DB *db); +int (*del)(const DB *db, const DBT *key, u_int flags); +int (*fd)(const DB *db); +int (*get)(const DB *db, DBT *key, DBT *data, u_int flags); +int (*put)(const DB *db, DBT *key, const DBT *data, +.ti +5 +u_int flags); +int (*sync)(const DB *db, u_int flags); +int (*seq)(const DB *db, DBT *key, DBT *data, u_int flags); +.RE +} DB; +.fi +.PP +These elements describe a database type and a set of functions performing +various actions. +These functions take a pointer to a structure as returned by +.IR dbopen , +and sometimes one or more pointers to key/data structures and a flag value. +.TP +type +The type of the underlying access method (and file format). +.TP +close +A pointer to a routine to flush any cached information to disk, free any +allocated resources, and close the underlying file(s). +Since key/data pairs may be cached in memory, failing to sync the file +with a +.I close +or +.I sync +function may result in inconsistent or lost information. +.I Close +routines return -1 on error (setting +.IR errno ) +and 0 on success. +.TP +del +A pointer to a routine to remove key/data pairs from the database. +.IP +The parameter +.I flag +may be set to the following value: +.RS +.TP +R_CURSOR +Delete the record referenced by the cursor. +The cursor must have previously been initialized. +.RE +.IP +.I Delete +routines return -1 on error (setting +.IR errno ), +0 on success, and 1 if the specified +.I key +was not in the file. +.TP +fd +A pointer to a routine which returns a file descriptor representative +of the underlying database. +A file descriptor referencing the same file will be returned to all +processes which call +.I dbopen +with the same +.I file +name. +This file descriptor may be safely used as a argument to the +.IR fcntl (2) +and +.IR flock (2) +locking functions. +The file descriptor is not necessarily associated with any of the +underlying files used by the access method. +No file descriptor is available for in memory databases. +.I Fd +routines return -1 on error (setting +.IR errno ), +and the file descriptor on success. +.TP +get +A pointer to a routine which is the interface for keyed retrieval from +the database. +The address and length of the data associated with the specified +.I key +are returned in the structure referenced by +.IR data . +.I Get +routines return -1 on error (setting +.IR errno ), +0 on success, and 1 if the +.I key +was not in the file. +.TP +put +A pointer to a routine to store key/data pairs in the database. +.IP +The parameter +.I flag +may be set to one of the following values: +.RS +.TP +R_CURSOR +Replace the key/data pair referenced by the cursor. +The cursor must have previously been initialized. +.TP +R_IAFTER +Append the data immediately after the data referenced by +.IR key , +creating a new key/data pair. +The record number of the appended key/data pair is returned in the +.I key +structure. +(Applicable only to the DB_RECNO access method.) +.TP +R_IBEFORE +Insert the data immediately before the data referenced by +.IR key , +creating a new key/data pair. +The record number of the inserted key/data pair is returned in the +.I key +structure. +(Applicable only to the DB_RECNO access method.) +.TP +R_NOOVERWRITE +Enter the new key/data pair only if the key does not previously exist. +.TP +R_SETCURSOR +Store the key/data pair, setting or initializing the position of the +cursor to reference it. +(Applicable only to the DB_BTREE and DB_RECNO access methods.) +.RE +.IP +R_SETCURSOR is available only for the DB_BTREE and DB_RECNO access +methods because it implies that the keys have an inherent order +which does not change. +.IP +R_IAFTER and R_IBEFORE are available only for the DB_RECNO +access method because they each imply that the access method is able to +create new keys. +This is only true if the keys are ordered and independent, record numbers +for example. +.IP +The default behavior of the +.I put +routines is to enter the new key/data pair, replacing any previously +existing key. +.IP +.I Put +routines return -1 on error (setting +.IR errno ), +0 on success, and 1 if the R_NOOVERWRITE +.I flag +was set and the key already exists in the file. +.TP +seq +A pointer to a routine which is the interface for sequential +retrieval from the database. +The address and length of the key are returned in the structure +referenced by +.IR key , +and the address and length of the data are returned in the +structure referenced +by +.IR data . +.IP +Sequential key/data pair retrieval may begin at any time, and the +position of the ``cursor'' is not affected by calls to the +.IR del , +.IR get , +.IR put , +or +.I sync +routines. +Modifications to the database during a sequential scan will be reflected +in the scan, i.e. records inserted behind the cursor will not be returned +while records inserted in front of the cursor will be returned. +.IP +The flag value +.B must +be set to one of the following values: +.RS +.TP +R_CURSOR +The data associated with the specified key is returned. +This differs from the +.I get +routines in that it sets or initializes the cursor to the location of +the key as well. +(Note, for the DB_BTREE access method, the returned key is not necessarily an +exact match for the specified key. +The returned key is the smallest key greater than or equal to the specified +key, permitting partial key matches and range searches.) +.TP +R_FIRST +The first key/data pair of the database is returned, and the cursor +is set or initialized to reference it. +.TP +R_LAST +The last key/data pair of the database is returned, and the cursor +is set or initialized to reference it. +(Applicable only to the DB_BTREE and DB_RECNO access methods.) +.TP +R_NEXT +Retrieve the key/data pair immediately after the cursor. +If the cursor is not yet set, this is the same as the R_FIRST flag. +.TP +R_PREV +Retrieve the key/data pair immediately before the cursor. +If the cursor is not yet set, this is the same as the R_LAST flag. +(Applicable only to the DB_BTREE and DB_RECNO access methods.) +.RE +.IP +R_LAST and R_PREV are available only for the DB_BTREE and DB_RECNO +access methods because they each imply that the keys have an inherent +order which does not change. +.IP +.I Seq +routines return -1 on error (setting +.IR errno ), +0 on success and 1 if there are no key/data pairs less than or greater +than the specified or current key. +If the DB_RECNO access method is being used, and if the database file +is a character special file and no complete key/data pairs are currently +available, the +.I seq +routines return 2. +.TP +sync +A pointer to a routine to flush any cached information to disk. +If the database is in memory only, the +.I sync +routine has no effect and will always succeed. +.IP +The flag value may be set to the following value: +.RS +.TP +R_RECNOSYNC +If the DB_RECNO access method is being used, this flag causes +the sync routine to apply to the btree file which underlies the +recno file, not the recno file itself. +(See the +.I bfname +field of the +.IR recno (3) +manual page for more information.) +.RE +.IP +.I Sync +routines return -1 on error (setting +.IR errno ) +and 0 on success. +.SH "KEY/DATA PAIRS" +Access to all file types is based on key/data pairs. +Both keys and data are represented by the following data structure: +.PP +typedef struct { +.RS +void *data; +.br +size_t size; +.RE +} DBT; +.PP +The elements of the DBT structure are defined as follows: +.TP +data +A pointer to a byte string. +.TP +size +The length of the byte string. +.PP +Key and data byte strings may reference strings of essentially unlimited +length although any two of them must fit into available memory at the same +time. +It should be noted that the access methods provide no guarantees about +byte string alignment. +.SH ERRORS +The +.I dbopen +routine may fail and set +.I errno +for any of the errors specified for the library routines +.IR open (2) +and +.IR malloc (3) +or the following: +.TP +[EFTYPE] +A file is incorrectly formatted. +.TP +[EINVAL] +A parameter has been specified (hash function, pad byte etc.) that is +incompatible with the current file specification or which is not +meaningful for the function (for example, use of the cursor without +prior initialization) or there is a mismatch between the version +number of file and the software. +.PP +The +.I close +routines may fail and set +.I errno +for any of the errors specified for the library routines +.IR close (2), +.IR read (2), +.IR write (2), +.IR free (3), +or +.IR fsync (2). +.PP +The +.IR del , +.IR get , +.I put +and +.I seq +routines may fail and set +.I errno +for any of the errors specified for the library routines +.IR read (2), +.IR write (2), +.IR free (3) +or +.IR malloc (3). +.PP +The +.I fd +routines will fail and set +.I errno +to ENOENT for in memory databases. +.PP +The +.I sync +routines may fail and set +.I errno +for any of the errors specified for the library routine +.IR fsync (2). +.SH "SEE ALSO" +.IR btree (3), +.IR hash (3), +.IR mpool (3), +.IR recno (3) +.SH BUGS +The typedef DBT is a mnemonic for ``data base thang'', and was used +because noone could think of a reasonable name that wasn't already used. +.PP +The file descriptor interface is a kluge and will be deleted in a +future version of the interface. +.PP +None of the access methods provide any form of concurrent access, +locking, or transactions. diff --git a/lib/libc/db/man/hash.3 b/lib/libc/db/man/hash.3 new file mode 100644 index 0000000000..698f10ca18 --- /dev/null +++ b/lib/libc/db/man/hash.3 @@ -0,0 +1,151 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)hash.3 8.1 (Berkeley) 6/6/93 +.\" +.TH HASH 3 "June 6, 1993" +.UC 7 +.SH NAME +hash \- hash database access method +.SH SYNOPSIS +.nf +.ft B +#include +#include +.ft R +.fi +.SH DESCRIPTION +The routine +.IR dbopen +is the library interface to database files. +One of the supported file formats is hash files. +The general description of the database access methods is in +.IR dbopen (3), +this manual page describes only the hash specific information. +.PP +The hash data structure is an extensible, dynamic hashing scheme. +.PP +The access method specific data structure provided to +.I dbopen +is defined in the include file as follows: +.sp +typedef struct { +.RS +int bsize; +.br +int cachesize; +.br +int ffactor; +.br +u_long (*hash)(const void *, size_t); +.br +int lorder; +.br +int nelem; +.RE +} HASHINFO; +.PP +The elements of this structure are as follows: +.TP +bsize +.I Bsize +defines the hash table bucket size, and is, by default, 256 bytes. +It may be preferable to increase the page size for disk-resident tables +and tables with large data items. +.TP +cachesize +A suggested maximum size, in bytes, of the memory cache. +This value is +.B only +advisory, and the access method will allocate more memory rather +than fail. +.TP +ffactor +.I Ffactor +indicates a desired density within the hash table. +It is an approximation of the number of keys allowed to accumulate in any +one bucket, determining when the hash table grows or shrinks. +The default value is 8. +.TP +hash +.I Hash +is a user defined hash function. +Since no hash function performs equally well on all possible data, the +user may find that the built-in hash function does poorly on a particular +data set. +User specified hash functions must take two arguments (a pointer to a byte +string and a length) and return an u_long to be used as the hash value. +.TP +lorder +The byte order for integers in the stored database metadata. +The number should represent the order as an integer; for example, +big endian order would be the number 4,321. +If +.I lorder +is 0 (no order is specified) the current host order is used. +If the file already exists, the specified value is ignored and the +value specified when the tree was created is used. +.TP +nelem +.I Nelem +is an estimate of the final size of the hash table. +If not set or set too low, hash tables will expand gracefully as keys +are entered, although a slight performance degradation may be noticed. +The default value is 1. +.PP +If the file already exists (and the O_TRUNC flag is not specified), the +values specified for the parameters bsize, ffactor, lorder and nelem are +ignored and the values specified when the tree was created are used. +.PP +If a hash function is specified, +.I hash_open +will attempt to determine if the hash function specified is the same as +the one with which the database was created, and will fail if it is not. +.PP +Backward compatible interfaces to the routines described in +.IR dbm (3), +and +.IR ndbm (3) +are provided, however, these interfaces are not compatible with +previous file formats. +.SH "SEE ALSO" +.IR btree (3), +.IR dbopen (3), +.IR mpool (3), +.IR recno (3) +.br +.IR "Dynamic Hash Tables" , +Per-Ake Larson, Communications of the ACM, April 1988. +.br +.IR "A New Hash Package for UNIX" , +Margo Seltzer, USENIX Proceedings, Winter 1991. +.SH BUGS +Only big and little endian byte order is supported. diff --git a/lib/libc/db/man/mpool.3 b/lib/libc/db/man/mpool.3 new file mode 100644 index 0000000000..c17606ebc9 --- /dev/null +++ b/lib/libc/db/man/mpool.3 @@ -0,0 +1,219 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mpool.3 8.1 (Berkeley) 6/4/93 +.\" +.TH MPOOL 3 "June 4, 1993" +.UC 7 +.SH NAME +mpool \- shared memory buffer pool +.SH SYNOPSIS +.nf +.ft B +#include +#include + +MPOOL * +mpool_open (DBT *key, int fd, pgno_t pagesize, pgno_t maxcache); + +void +mpool_filter (MPOOL *mp, void (*pgin)(void *, pgno_t, void *), +.ti +5 +void (*pgout)(void *, pgno_t, void *), void *pgcookie); + +void * +mpool_new (MPOOL *mp, pgno_t *pgnoaddr); + +void * +mpool_get (MPOOL *mp, pgno_t pgno, u_int flags); + +int +mpool_put (MPOOL *mp, void *pgaddr, u_int flags); + +int +mpool_sync (MPOOL *mp); + +int +mpool_close (MPOOL *mp); +.ft R +.fi +.SH DESCRIPTION +.IR Mpool +is the library interface intended to provide page oriented buffer management +of files. +The buffers may be shared between processes. +.PP +The function +.I mpool_open +initializes a memory pool. +The +.I key +argument is the byte string used to negotiate between multiple +processes wishing to share buffers. +If the file buffers are mapped in shared memory, all processes using +the same key will share the buffers. +If +.I key +is NULL, the buffers are mapped into private memory. +The +.I fd +argument is a file descriptor for the underlying file, which must be seekable. +If +.I key +is non-NULL and matches a file already being mapped, the +.I fd +argument is ignored. +.PP +The +.I pagesize +argument is the size, in bytes, of the pages into which the file is broken up. +The +.I maxcache +argument is the maximum number of pages from the underlying file to cache +at any one time. +This value is not relative to the number of processes which share a file's +buffers, but will be the largest value specified by any of the processes +sharing the file. +.PP +The +.I mpool_filter +function is intended to make transparent input and output processing of the +pages possible. +If the +.I pgin +function is specified, it is called each time a buffer is read into the memory +pool from the backing file. +If the +.I pgout +function is specified, it is called each time a buffer is written into the +backing file. +Both functions are are called with the +.I pgcookie +pointer, the page number and a pointer to the page to being read or written. +.PP +The function +.I mpool_new +takes an MPOOL pointer and an address as arguments. +If a new page can be allocated, a pointer to the page is returned and +the page number is stored into the +.I pgnoaddr +address. +Otherwise, NULL is returned and errno is set. +.PP +The function +.I mpool_get +takes a MPOOL pointer and a page number as arguments. +If the page exists, a pointer to the page is returned. +Otherwise, NULL is returned and errno is set. +The flags parameter is not currently used. +.PP +The function +.I mpool_put +unpins the page referenced by +.IR pgaddr . +.I Pgaddr +must be an address previously returned by +.I mpool_get +or +.IR mpool_new . +The flag value is specified by +.IR or 'ing +any of the following values: +.TP +MPOOL_DIRTY +The page has been modified and needs to be written to the backing file. +.PP +.I Mpool_put +returns 0 on success and -1 if an error occurs. +.PP +The function +.I mpool_sync +writes all modified pages associated with the MPOOL pointer to the +backing file. +.I Mpool_sync +returns 0 on success and -1 if an error occurs. +.PP +The +.I mpool_close +function free's up any allocated memory associated with the memory pool +cookie. +Modified pages are +.B not +written to the backing file. +.I Mpool_close +returns 0 on success and -1 if an error occurs. +.SH ERRORS +The +.I mpool_open +function may fail and set +.I errno +for any of the errors specified for the library routine +.IR malloc (3). +.PP +The +.I mpool_get +function may fail and set +.I errno +for the following: +.TP 15 +[EINVAL] +The requested record doesn't exist. +.PP +The +.I mpool_new +and +.I mpool_get +functions may fail and set +.I errno +for any of the errors specified for the library routines +.IR read (2) , +.IR write (2) , +and +.IR malloc (3). +.PP +The +.I mpool_sync +function may fail and set +.I errno +for any of the errors specified for the library routine +.IR write (2). +.PP +The +.I mpool_close +function may fail and set +.I errno +for any of the errors specified for the library routine +.IR free (3). +.SH "SEE ALSO" +.IR dbopen (3), +.IR btree (3), +.IR hash (3), +.IR recno (3) diff --git a/lib/libc/db/man/recno.3 b/lib/libc/db/man/recno.3 new file mode 100644 index 0000000000..476155642a --- /dev/null +++ b/lib/libc/db/man/recno.3 @@ -0,0 +1,196 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)recno.3 8.1 (Berkeley) 6/4/93 +.\" +.TH RECNO 3 "June 4, 1993" +.UC 7 +.SH NAME +recno \- record number database access method +.SH SYNOPSIS +.nf +.ft B +#include +#include +.ft R +.fi +.SH DESCRIPTION +The routine +.IR dbopen +is the library interface to database files. +One of the supported file formats is record number files. +The general description of the database access methods is in +.IR dbopen (3), +this manual page describes only the recno specific information. +.PP +The record number data structure is either variable or fixed-length +records stored in a flat-file format, accessed by the logical record +number. +The existence of record number five implies the existence of records +one through four, and the deletion of record number one causes +record number five to be renumbered to record number four, as well +as the cursor, if positioned after record number one, to shift down +one record. +.PP +The recno access method specific data structure provided to +.I dbopen +is defined in the include file as follows: +.PP +typedef struct { +.RS +u_char bval; +.br +u_int cachesize; +.br +index_t psize; +.br +u_long flags; +.br +int lorder; +.br +size_t reclen; +.br +char *bfname; +.RE +} RECNOINFO; +.PP +The elements of this structure are defined as follows: +.TP +bval +The delimiting byte to be used to mark the end of a record for +variable-length records, and the pad character for fixed-length +records. +If no value is specified, newlines (``\en'') are used to mark the end +of variable-length records and fixed-length records are padded with +spaces. +.TP +cachesize +A suggested maximum size, in bytes, of the memory cache. +This value is +.B only +advisory, and the access method will allocate more memory rather than fail. +If +.I cachesize +is 0 (no size is specified) a default cache is used. +.TP +psize +The recno access method stores the in-memory copies of its records +in a btree. +This value is the size (in bytes) of the pages used for nodes in that tree. +If +.I psize +is 0 (no page size is specified) a page size is chosen based on the +underlying file system I/O block size. +See +.IR btree (3) +for more information. +.TP +bfname +The recno access method stores the in-memory copies of its records +in a btree. +If bfname is non-NULL, it specifies the name of the btree file, +as if specified as the file name for a dbopen of a btree file. +.TP +flags +The flag value is specified by +.IR or 'ing +any of the following values: +.RS +.TP +R_FIXEDLEN +The records are fixed-length, not byte delimited. +The structure element +.I reclen +specifies the length of the record, and the structure element +.I bval +is used as the pad character. +.TP +R_NOKEY +In the interface specified by +.IR dbopen , +the sequential record retrieval fills in both the caller's key and +data structures. +If the R_NOKEY flag is specified, the +.I cursor +routines are not required to fill in the key structure. +This permits applications to retrieve records at the end of files without +reading all of the intervening records. +.TP +R_SNAPSHOT +This flag requires that a snapshot of the file be taken when +.I dbopen +is called, instead of permitting any unmodified records to be read from +the original file. +.RE +.TP +lorder +The byte order for integers in the stored database metadata. +The number should represent the order as an integer; for example, +big endian order would be the number 4,321. +If +.I lorder +is 0 (no order is specified) the current host order is used. +.TP +reclen +The length of a fixed-length record. +.PP +The data part of the key/data pair used by the recno access method +is the same as other access methods. +The key is different. +The +.I data +field of the key should be a pointer to a memory location of type +.IR recno_t , +as defined in the include file. +This type is normally the largest unsigned integral type available to +the implementation. +The +.I size +field of the key should be the size of that type. +.PP +In the interface specified by +.IR dbopen , +using the +.I put +interface to create a new record will cause the creation of multiple, +empty records if the record number is more than one greater than the +largest record currently in the database. +.SH "SEE ALSO" +.IR dbopen (3), +.IR hash (3), +.IR mpool (3), +.IR recno (3) +.sp +.IR "Document Processing in a Relational Database System" , +Michael Stonebraker, Heidi Stettner, Joseph Kalash, Antonin Guttman, +Nadene Lynn, Memorandum No. UCB/ERL M82/32, May 1982. +.SH BUGS +Only big and little endian byte order is supported. diff --git a/lib/libc/db/mpool/Makefile.inc b/lib/libc/db/mpool/Makefile.inc new file mode 100644 index 0000000000..93210c89e2 --- /dev/null +++ b/lib/libc/db/mpool/Makefile.inc @@ -0,0 +1,5 @@ +# @(#)Makefile.inc 8.1 (Berkeley) 6/4/93 + +.PATH: ${.CURDIR}/db/mpool + +SRCS+= mpool.c diff --git a/lib/libc/db/mpool/README b/lib/libc/db/mpool/README new file mode 100644 index 0000000000..0f01fbcdb4 --- /dev/null +++ b/lib/libc/db/mpool/README @@ -0,0 +1,7 @@ +# @(#)README 8.1 (Berkeley) 6/4/93 + +These are the current memory pool routines. +They aren't ready for prime time, yet, and +the interface is expected to change. + +--keith diff --git a/lib/libc/db/mpool/mpool.c b/lib/libc/db/mpool/mpool.c new file mode 100644 index 0000000000..17a66f3b58 --- /dev/null +++ b/lib/libc/db/mpool/mpool.c @@ -0,0 +1,534 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)mpool.c 8.1 (Berkeley) 6/6/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#define __MPOOLINTERFACE_PRIVATE +#include "mpool.h" + +static BKT *mpool_bkt __P((MPOOL *)); +static BKT *mpool_look __P((MPOOL *, pgno_t)); +static int mpool_write __P((MPOOL *, BKT *)); +#ifdef DEBUG +static void __mpoolerr __P((const char *fmt, ...)); +#endif + +/* + * MPOOL_OPEN -- initialize a memory pool. + * + * Parameters: + * key: Shared buffer key. + * fd: File descriptor. + * pagesize: File page size. + * maxcache: Max number of cached pages. + * + * Returns: + * MPOOL pointer, NULL on error. + */ +MPOOL * +mpool_open(key, fd, pagesize, maxcache) + DBT *key; + int fd; + pgno_t pagesize, maxcache; +{ + struct stat sb; + MPOOL *mp; + int entry; + + if (fstat(fd, &sb)) + return (NULL); + /* XXX + * We should only set st_size to 0 for pipes -- 4.4BSD has the fix so + * that stat(2) returns true for ISSOCK on pipes. Until then, this is + * fairly close. + */ + if (!S_ISREG(sb.st_mode)) { + errno = ESPIPE; + return (NULL); + } + + if ((mp = malloc(sizeof(MPOOL))) == NULL) + return (NULL); + mp->free.cnext = mp->free.cprev = (BKT *)&mp->free; + mp->lru.cnext = mp->lru.cprev = (BKT *)&mp->lru; + for (entry = 0; entry < HASHSIZE; ++entry) + mp->hashtable[entry].hnext = mp->hashtable[entry].hprev = + mp->hashtable[entry].cnext = mp->hashtable[entry].cprev = + (BKT *)&mp->hashtable[entry]; + mp->curcache = 0; + mp->maxcache = maxcache; + mp->pagesize = pagesize; + mp->npages = sb.st_size / pagesize; + mp->fd = fd; + mp->pgcookie = NULL; + mp->pgin = mp->pgout = NULL; + +#ifdef STATISTICS + mp->cachehit = mp->cachemiss = mp->pagealloc = mp->pageflush = + mp->pageget = mp->pagenew = mp->pageput = mp->pageread = + mp->pagewrite = 0; +#endif + return (mp); +} + +/* + * MPOOL_FILTER -- initialize input/output filters. + * + * Parameters: + * pgin: Page in conversion routine. + * pgout: Page out conversion routine. + * pgcookie: Cookie for page in/out routines. + */ +void +mpool_filter(mp, pgin, pgout, pgcookie) + MPOOL *mp; + void (*pgin) __P((void *, pgno_t, void *)); + void (*pgout) __P((void *, pgno_t, void *)); + void *pgcookie; +{ + mp->pgin = pgin; + mp->pgout = pgout; + mp->pgcookie = pgcookie; +} + +/* + * MPOOL_NEW -- get a new page + * + * Parameters: + * mp: mpool cookie + * pgnoadddr: place to store new page number + * Returns: + * RET_ERROR, RET_SUCCESS + */ +void * +mpool_new(mp, pgnoaddr) + MPOOL *mp; + pgno_t *pgnoaddr; +{ + BKT *b; + BKTHDR *hp; + +#ifdef STATISTICS + ++mp->pagenew; +#endif + /* + * Get a BKT from the cache. Assign a new page number, attach it to + * the hash and lru chains and return. + */ + if ((b = mpool_bkt(mp)) == NULL) + return (NULL); + *pgnoaddr = b->pgno = mp->npages++; + b->flags = MPOOL_PINNED; + inshash(b, b->pgno); + inschain(b, &mp->lru); + return (b->page); +} + +/* + * MPOOL_GET -- get a page from the pool + * + * Parameters: + * mp: mpool cookie + * pgno: page number + * flags: not used + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +void * +mpool_get(mp, pgno, flags) + MPOOL *mp; + pgno_t pgno; + u_int flags; /* XXX not used? */ +{ + BKT *b; + BKTHDR *hp; + off_t off; + int nr; + + /* + * If asking for a specific page that is already in the cache, find + * it and return it. + */ + if (b = mpool_look(mp, pgno)) { +#ifdef STATISTICS + ++mp->pageget; +#endif +#ifdef DEBUG + if (b->flags & MPOOL_PINNED) + __mpoolerr("mpool_get: page %d already pinned", + b->pgno); +#endif + rmchain(b); + inschain(b, &mp->lru); + b->flags |= MPOOL_PINNED; + return (b->page); + } + + /* Not allowed to retrieve a non-existent page. */ + if (pgno >= mp->npages) { + errno = EINVAL; + return (NULL); + } + + /* Get a page from the cache. */ + if ((b = mpool_bkt(mp)) == NULL) + return (NULL); + b->pgno = pgno; + b->flags = MPOOL_PINNED; + +#ifdef STATISTICS + ++mp->pageread; +#endif + /* Read in the contents. */ + off = mp->pagesize * pgno; + if (lseek(mp->fd, off, SEEK_SET) != off) + return (NULL); + if ((nr = read(mp->fd, b->page, mp->pagesize)) != mp->pagesize) { + if (nr >= 0) + errno = EFTYPE; + return (NULL); + } + if (mp->pgin) + (mp->pgin)(mp->pgcookie, b->pgno, b->page); + + inshash(b, b->pgno); + inschain(b, &mp->lru); +#ifdef STATISTICS + ++mp->pageget; +#endif + return (b->page); +} + +/* + * MPOOL_PUT -- return a page to the pool + * + * Parameters: + * mp: mpool cookie + * page: page pointer + * pgno: page number + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +mpool_put(mp, page, flags) + MPOOL *mp; + void *page; + u_int flags; +{ + BKT *baddr; +#ifdef DEBUG + BKT *b; +#endif + +#ifdef STATISTICS + ++mp->pageput; +#endif + baddr = (BKT *)((char *)page - sizeof(BKT)); +#ifdef DEBUG + if (!(baddr->flags & MPOOL_PINNED)) + __mpoolerr("mpool_put: page %d not pinned", b->pgno); + for (b = mp->lru.cnext; b != (BKT *)&mp->lru; b = b->cnext) { + if (b == (BKT *)&mp->lru) + __mpoolerr("mpool_put: %0x: bad address", baddr); + if (b == baddr) + break; + } +#endif + baddr->flags &= ~MPOOL_PINNED; + baddr->flags |= flags & MPOOL_DIRTY; + return (RET_SUCCESS); +} + +/* + * MPOOL_CLOSE -- close the buffer pool + * + * Parameters: + * mp: mpool cookie + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +mpool_close(mp) + MPOOL *mp; +{ + BKT *b, *next; + + /* Free up any space allocated to the lru pages. */ + for (b = mp->lru.cprev; b != (BKT *)&mp->lru; b = next) { + next = b->cprev; + free(b); + } + free(mp); + return (RET_SUCCESS); +} + +/* + * MPOOL_SYNC -- sync the file to disk. + * + * Parameters: + * mp: mpool cookie + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +mpool_sync(mp) + MPOOL *mp; +{ + BKT *b; + + for (b = mp->lru.cprev; b != (BKT *)&mp->lru; b = b->cprev) + if (b->flags & MPOOL_DIRTY && mpool_write(mp, b) == RET_ERROR) + return (RET_ERROR); + return (fsync(mp->fd) ? RET_ERROR : RET_SUCCESS); +} + +/* + * MPOOL_BKT -- get/create a BKT from the cache + * + * Parameters: + * mp: mpool cookie + * + * Returns: + * NULL on failure and a pointer to the BKT on success + */ +static BKT * +mpool_bkt(mp) + MPOOL *mp; +{ + BKT *b; + + if (mp->curcache < mp->maxcache) + goto new; + + /* + * If the cache is maxxed out, search the lru list for a buffer we + * can flush. If we find one, write it if necessary and take it off + * any lists. If we don't find anything we grow the cache anyway. + * The cache never shrinks. + */ + for (b = mp->lru.cprev; b != (BKT *)&mp->lru; b = b->cprev) + if (!(b->flags & MPOOL_PINNED)) { + if (b->flags & MPOOL_DIRTY && + mpool_write(mp, b) == RET_ERROR) + return (NULL); + rmhash(b); + rmchain(b); +#ifdef STATISTICS + ++mp->pageflush; +#endif +#ifdef DEBUG + { + void *spage; + spage = b->page; + memset(b, 0xff, sizeof(BKT) + mp->pagesize); + b->page = spage; + } +#endif + return (b); + } + +new: if ((b = malloc(sizeof(BKT) + mp->pagesize)) == NULL) + return (NULL); +#ifdef STATISTICS + ++mp->pagealloc; +#endif +#ifdef DEBUG + memset(b, 0xff, sizeof(BKT) + mp->pagesize); +#endif + b->page = (char *)b + sizeof(BKT); + ++mp->curcache; + return (b); +} + +/* + * MPOOL_WRITE -- sync a page to disk + * + * Parameters: + * mp: mpool cookie + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +static int +mpool_write(mp, b) + MPOOL *mp; + BKT *b; +{ + off_t off; + + if (mp->pgout) + (mp->pgout)(mp->pgcookie, b->pgno, b->page); + +#ifdef STATISTICS + ++mp->pagewrite; +#endif + off = mp->pagesize * b->pgno; + if (lseek(mp->fd, off, SEEK_SET) != off) + return (RET_ERROR); + if (write(mp->fd, b->page, mp->pagesize) != mp->pagesize) + return (RET_ERROR); + b->flags &= ~MPOOL_DIRTY; + return (RET_SUCCESS); +} + +/* + * MPOOL_LOOK -- lookup a page + * + * Parameters: + * mp: mpool cookie + * pgno: page number + * + * Returns: + * NULL on failure and a pointer to the BKT on success + */ +static BKT * +mpool_look(mp, pgno) + MPOOL *mp; + pgno_t pgno; +{ + register BKT *b; + register BKTHDR *tb; + + /* XXX + * If find the buffer, put it first on the hash chain so can + * find it again quickly. + */ + tb = &mp->hashtable[HASHKEY(pgno)]; + for (b = tb->hnext; b != (BKT *)tb; b = b->hnext) + if (b->pgno == pgno) { +#ifdef STATISTICS + ++mp->cachehit; +#endif + return (b); + } +#ifdef STATISTICS + ++mp->cachemiss; +#endif + return (NULL); +} + +#ifdef STATISTICS +/* + * MPOOL_STAT -- cache statistics + * + * Parameters: + * mp: mpool cookie + */ +void +mpool_stat(mp) + MPOOL *mp; +{ + BKT *b; + int cnt; + char *sep; + + (void)fprintf(stderr, "%lu pages in the file\n", mp->npages); + (void)fprintf(stderr, + "page size %lu, cacheing %lu pages of %lu page max cache\n", + mp->pagesize, mp->curcache, mp->maxcache); + (void)fprintf(stderr, "%lu page puts, %lu page gets, %lu page new\n", + mp->pageput, mp->pageget, mp->pagenew); + (void)fprintf(stderr, "%lu page allocs, %lu page flushes\n", + mp->pagealloc, mp->pageflush); + if (mp->cachehit + mp->cachemiss) + (void)fprintf(stderr, + "%.0f%% cache hit rate (%lu hits, %lu misses)\n", + ((double)mp->cachehit / (mp->cachehit + mp->cachemiss)) + * 100, mp->cachehit, mp->cachemiss); + (void)fprintf(stderr, "%lu page reads, %lu page writes\n", + mp->pageread, mp->pagewrite); + + sep = ""; + cnt = 0; + for (b = mp->lru.cnext; b != (BKT *)&mp->lru; b = b->cnext) { + (void)fprintf(stderr, "%s%d", sep, b->pgno); + if (b->flags & MPOOL_DIRTY) + (void)fprintf(stderr, "d"); + if (b->flags & MPOOL_PINNED) + (void)fprintf(stderr, "P"); + if (++cnt == 10) { + sep = "\n"; + cnt = 0; + } else + sep = ", "; + + } + (void)fprintf(stderr, "\n"); +} +#endif + +#ifdef DEBUG +#if __STDC__ +#include +#else +#include +#endif + +static void +#if __STDC__ +__mpoolerr(const char *fmt, ...) +#else +__mpoolerr(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + abort(); + /* NOTREACHED */ +} +#endif diff --git a/lib/libc/db/recno/Makefile.inc b/lib/libc/db/recno/Makefile.inc new file mode 100644 index 0000000000..e49e225522 --- /dev/null +++ b/lib/libc/db/recno/Makefile.inc @@ -0,0 +1,6 @@ +# @(#)Makefile.inc 8.1 (Berkeley) 6/4/93 + +.PATH: ${.CURDIR}/db/recno + +SRCS+= rec_close.c rec_delete.c rec_get.c rec_open.c rec_put.c rec_search.c \ + rec_seq.c rec_utils.c diff --git a/lib/libc/db/recno/extern.h b/lib/libc/db/recno/extern.h new file mode 100644 index 0000000000..e567df78de --- /dev/null +++ b/lib/libc/db/recno/extern.h @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/4/93 + */ + +#include "../btree/extern.h" + +int __rec_close __P((DB *)); +int __rec_delete __P((const DB *, const DBT *, u_int)); +int __rec_dleaf __P((BTREE *, PAGE *, int)); +int __rec_fd __P((const DB *)); +int __rec_fmap __P((BTREE *, recno_t)); +int __rec_fout __P((BTREE *)); +int __rec_fpipe __P((BTREE *, recno_t)); +int __rec_get __P((const DB *, const DBT *, DBT *, u_int)); +int __rec_iput __P((BTREE *, recno_t, const DBT *, u_int)); +int __rec_put __P((const DB *dbp, DBT *, const DBT *, u_int)); +int __rec_ret __P((BTREE *, EPG *, recno_t, DBT *, DBT *)); +EPG *__rec_search __P((BTREE *, recno_t, enum SRCHOP)); +int __rec_seq __P((const DB *, DBT *, DBT *, u_int)); +int __rec_sync __P((const DB *, u_int)); +int __rec_vmap __P((BTREE *, recno_t)); +int __rec_vout __P((BTREE *)); +int __rec_vpipe __P((BTREE *, recno_t)); diff --git a/lib/libc/db/recno/rec_close.c b/lib/libc/db/recno/rec_close.c new file mode 100644 index 0000000000..49c5f2ca5e --- /dev/null +++ b/lib/libc/db/recno/rec_close.c @@ -0,0 +1,151 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rec_close.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "recno.h" + +/* + * __REC_CLOSE -- Close a recno tree. + * + * Parameters: + * dbp: pointer to access method + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__rec_close(dbp) + DB *dbp; +{ + BTREE *t; + int rval; + + if (__rec_sync(dbp, 0) == RET_ERROR) + return (RET_ERROR); + + /* Committed to closing. */ + t = dbp->internal; + + rval = RET_SUCCESS; + if (ISSET(t, R_MEMMAPPED) && munmap(t->bt_smap, t->bt_msize)) + rval = RET_ERROR; + + if (!ISSET(t, R_INMEM)) + if (ISSET(t, R_CLOSEFP)) { + if (fclose(t->bt_rfp)) + rval = RET_ERROR; + } else + if (close(t->bt_rfd)) + rval = RET_ERROR; + + if (__bt_close(dbp) == RET_ERROR) + rval = RET_ERROR; + + return (rval); +} + +/* + * __REC_SYNC -- sync the recno tree to disk. + * + * Parameters: + * dbp: pointer to access method + * + * Returns: + * RET_SUCCESS, RET_ERROR. + */ +int +__rec_sync(dbp, flags) + const DB *dbp; + u_int flags; +{ + struct iovec iov[2]; + BTREE *t; + DBT data, key; + off_t off; + recno_t scursor, trec; + int status; + + t = dbp->internal; + + if (flags == R_RECNOSYNC) + return (__bt_sync(dbp, 0)); + + if (ISSET(t, R_RDONLY | R_INMEM) || !ISSET(t, R_MODIFIED)) + return (RET_SUCCESS); + + /* Read any remaining records into the tree. */ + if (!ISSET(t, R_EOF) && t->bt_irec(t, MAX_REC_NUMBER) == RET_ERROR) + return (RET_ERROR); + + /* Rewind the file descriptor. */ + if (lseek(t->bt_rfd, (off_t)0, SEEK_SET) != 0) + return (RET_ERROR); + + iov[1].iov_base = "\n"; + iov[1].iov_len = 1; + scursor = t->bt_rcursor; + + key.size = sizeof(recno_t); + key.data = &trec; + + status = (dbp->seq)(dbp, &key, &data, R_FIRST); + while (status == RET_SUCCESS) { + iov[0].iov_base = data.data; + iov[0].iov_len = data.size; + if (writev(t->bt_rfd, iov, 2) != data.size + 1) + return (RET_ERROR); + status = (dbp->seq)(dbp, &key, &data, R_NEXT); + } + t->bt_rcursor = scursor; + if (status == RET_ERROR) + return (RET_ERROR); + if ((off = lseek(t->bt_rfd, (off_t)0, SEEK_CUR)) == -1) + return (RET_ERROR); + if (ftruncate(t->bt_rfd, off)) + return (RET_ERROR); + CLR(t, R_MODIFIED); + return (RET_SUCCESS); +} diff --git a/lib/libc/db/recno/rec_delete.c b/lib/libc/db/recno/rec_delete.c new file mode 100644 index 0000000000..98d135331d --- /dev/null +++ b/lib/libc/db/recno/rec_delete.c @@ -0,0 +1,193 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rec_delete.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +#include +#include "recno.h" + +static int rec_rdelete __P((BTREE *, recno_t)); + +/* + * __REC_DELETE -- Delete the item(s) referenced by a key. + * + * Parameters: + * dbp: pointer to access method + * key: key to delete + * flags: R_CURSOR if deleting what the cursor references + * + * Returns: + * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found. + */ +int +__rec_delete(dbp, key, flags) + const DB *dbp; + const DBT *key; + u_int flags; +{ + BTREE *t; + recno_t nrec; + int status; + + t = dbp->internal; + switch(flags) { + case 0: + if ((nrec = *(recno_t *)key->data) == 0) + goto einval; + if (nrec > t->bt_nrecs) + return (RET_SPECIAL); + --nrec; + status = rec_rdelete(t, nrec); + break; + case R_CURSOR: + if (!ISSET(t, B_SEQINIT)) + goto einval; + if (t->bt_nrecs == 0) + return (RET_SPECIAL); + status = rec_rdelete(t, t->bt_rcursor - 1); + if (status == RET_SUCCESS) + --t->bt_rcursor; + break; + default: +einval: errno = EINVAL; + return (RET_ERROR); + } + + if (status == RET_SUCCESS) + SET(t, B_MODIFIED | R_MODIFIED); + return (status); +} + +/* + * REC_RDELETE -- Delete the data matching the specified key. + * + * Parameters: + * tree: tree + * nrec: record to delete + * + * Returns: + * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found. + */ +static int +rec_rdelete(t, nrec) + BTREE *t; + recno_t nrec; +{ + EPG *e; + PAGE *h; + int status; + + /* Find the record; __rec_search pins the page. */ + if ((e = __rec_search(t, nrec, SDELETE)) == NULL) { + mpool_put(t->bt_mp, e->page, 0); + return (RET_ERROR); + } + + /* Delete the record. */ + h = e->page; + status = __rec_dleaf(t, h, e->index); + if (status != RET_SUCCESS) { + mpool_put(t->bt_mp, h, 0); + return (status); + } + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + return (RET_SUCCESS); +} + +/* + * __REC_DLEAF -- Delete a single record from a recno leaf page. + * + * Parameters: + * t: tree + * index: index on current page to delete + * + * Returns: + * RET_SUCCESS, RET_ERROR. + */ +int +__rec_dleaf(t, h, index) + BTREE *t; + PAGE *h; + int index; +{ + register RLEAF *rl; + register indx_t *ip, offset; + register size_t nbytes; + register int cnt; + char *from; + void *to; + + /* + * Delete a record from a recno leaf page. Internal records are never + * deleted from internal pages, regardless of the records that caused + * them to be added being deleted. Pages made empty by deletion are + * not reclaimed. They are, however, made available for reuse. + * + * Pack the remaining entries at the end of the page, shift the indices + * down, overwriting the deleted record and its index. If the record + * uses overflow pages, make them available for reuse. + */ + to = rl = GETRLEAF(h, index); + if (rl->flags & P_BIGDATA && __ovfl_delete(t, rl->bytes) == RET_ERROR) + return (RET_ERROR); + nbytes = NRLEAF(rl); + + /* + * Compress the key/data pairs. Compress and adjust the [BR]LEAF + * offsets. Reset the headers. + */ + from = (char *)h + h->upper; + memmove(from + nbytes, from, (char *)to - from); + h->upper += nbytes; + + offset = h->linp[index]; + for (cnt = &h->linp[index] - (ip = &h->linp[0]); cnt--; ++ip) + if (ip[0] < offset) + ip[0] += nbytes; + for (cnt = &h->linp[NEXTINDEX(h)] - ip; --cnt; ++ip) + ip[0] = ip[1] < offset ? ip[1] + nbytes : ip[1]; + h->lower -= sizeof(indx_t); + --t->bt_nrecs; + return (RET_SUCCESS); +} diff --git a/lib/libc/db/recno/rec_get.c b/lib/libc/db/recno/rec_get.c new file mode 100644 index 0000000000..d024512722 --- /dev/null +++ b/lib/libc/db/recno/rec_get.c @@ -0,0 +1,285 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rec_get.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include "recno.h" + +/* + * __REC_GET -- Get a record from the btree. + * + * Parameters: + * dbp: pointer to access method + * key: key to find + * data: data to return + * flag: currently unused + * + * Returns: + * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found. + */ +int +__rec_get(dbp, key, data, flags) + const DB *dbp; + const DBT *key; + DBT *data; + u_int flags; +{ + BTREE *t; + EPG *e; + recno_t nrec; + int status; + + if (flags || (nrec = *(recno_t *)key->data) == 0) { + errno = EINVAL; + return (RET_ERROR); + } + + /* + * If we haven't seen this record yet, try to find it in the + * original file. + */ + t = dbp->internal; + if (nrec > t->bt_nrecs) { + if (ISSET(t, R_EOF | R_INMEM)) + return (RET_SPECIAL); + if ((status = t->bt_irec(t, nrec)) != RET_SUCCESS) + return (status); + } + + --nrec; + if ((e = __rec_search(t, nrec, SEARCH)) == NULL) + return (RET_ERROR); + + status = __rec_ret(t, e, 0, NULL, data); + mpool_put(t->bt_mp, e->page, 0); + return (status); +} + +/* + * __REC_FPIPE -- Get fixed length records from a pipe. + * + * Parameters: + * t: tree + * cnt: records to read + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__rec_fpipe(t, top) + BTREE *t; + recno_t top; +{ + DBT data; + recno_t nrec; + size_t len; + int ch; + char *p; + + data.data = t->bt_dbuf; + data.size = t->bt_reclen; + + if (t->bt_dbufsz < t->bt_reclen) { + if ((t->bt_dbuf = realloc(t->bt_dbuf, t->bt_reclen)) == NULL) + return (RET_ERROR); + t->bt_dbufsz = t->bt_reclen; + } + for (nrec = t->bt_nrecs; nrec < top; ++nrec) { + len = t->bt_reclen; + for (p = t->bt_dbuf;; *p++ = ch) + if ((ch = getc(t->bt_rfp)) == EOF || !len--) { + if (__rec_iput(t, nrec, &data, 0) + != RET_SUCCESS) + return (RET_ERROR); + break; + } + if (ch == EOF) + break; + } + if (nrec < top) { + SET(t, R_EOF); + return (RET_SPECIAL); + } + return (RET_SUCCESS); +} + +/* + * __REC_VPIPE -- Get variable length records from a pipe. + * + * Parameters: + * t: tree + * cnt: records to read + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__rec_vpipe(t, top) + BTREE *t; + recno_t top; +{ + DBT data; + recno_t nrec; + indx_t len; + size_t sz; + int bval, ch; + char *p; + + bval = t->bt_bval; + for (nrec = t->bt_nrecs; nrec < top; ++nrec) { + for (p = t->bt_dbuf, sz = t->bt_dbufsz;; *p++ = ch, --sz) { + if ((ch = getc(t->bt_rfp)) == EOF || ch == bval) { + data.data = t->bt_dbuf; + data.size = p - t->bt_dbuf; + if (ch == EOF && data.size == 0) + break; + if (__rec_iput(t, nrec, &data, 0) + != RET_SUCCESS) + return (RET_ERROR); + break; + } + if (sz == 0) { + len = p - t->bt_dbuf; + t->bt_dbufsz += (sz = 256); + if ((t->bt_dbuf = + realloc(t->bt_dbuf, t->bt_dbufsz)) == NULL) + return (RET_ERROR); + p = t->bt_dbuf + len; + } + } + if (ch == EOF) + break; + } + if (nrec < top) { + SET(t, R_EOF); + return (RET_SPECIAL); + } + return (RET_SUCCESS); +} + +/* + * __REC_FMAP -- Get fixed length records from a file. + * + * Parameters: + * t: tree + * cnt: records to read + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__rec_fmap(t, top) + BTREE *t; + recno_t top; +{ + DBT data; + recno_t nrec; + caddr_t sp, ep; + size_t len; + char *p; + + sp = t->bt_cmap; + ep = t->bt_emap; + data.data = t->bt_dbuf; + data.size = t->bt_reclen; + + if (t->bt_dbufsz < t->bt_reclen) { + if ((t->bt_dbuf = realloc(t->bt_dbuf, t->bt_reclen)) == NULL) + return (RET_ERROR); + t->bt_dbufsz = t->bt_reclen; + } + for (nrec = t->bt_nrecs; nrec < top; ++nrec) { + if (sp >= ep) { + SET(t, R_EOF); + return (RET_SPECIAL); + } + len = t->bt_reclen; + for (p = t->bt_dbuf; sp < ep && len--; *p++ = *sp++); + memset(p, t->bt_bval, len); + if (__rec_iput(t, nrec, &data, 0) != RET_SUCCESS) + return (RET_ERROR); + } + t->bt_cmap = sp; + return (RET_SUCCESS); +} + +/* + * __REC_VMAP -- Get variable length records from a file. + * + * Parameters: + * t: tree + * cnt: records to read + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__rec_vmap(t, top) + BTREE *t; + recno_t top; +{ + DBT data; + caddr_t sp, ep; + recno_t nrec; + int bval; + + sp = t->bt_cmap; + ep = t->bt_emap; + bval = t->bt_bval; + + for (nrec = t->bt_nrecs; nrec < top; ++nrec) { + if (sp >= ep) { + SET(t, R_EOF); + return (RET_SPECIAL); + } + for (data.data = sp; sp < ep && *sp != bval; ++sp); + data.size = sp - (caddr_t)data.data; + if (__rec_iput(t, nrec, &data, 0) != RET_SUCCESS) + return (RET_ERROR); + ++sp; + } + t->bt_cmap = sp; + return (RET_SUCCESS); +} diff --git a/lib/libc/db/recno/rec_open.c b/lib/libc/db/recno/rec_open.c new file mode 100644 index 0000000000..a0e152e476 --- /dev/null +++ b/lib/libc/db/recno/rec_open.c @@ -0,0 +1,221 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rec_open.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include "recno.h" + +DB * +__rec_open(fname, flags, mode, openinfo) + const char *fname; + int flags, mode; + const RECNOINFO *openinfo; +{ + BTREE *t; + BTREEINFO btopeninfo; + DB *dbp; + PAGE *h; + struct stat sb; + int rfd, sverrno; + + /* Open the user's file -- if this fails, we're done. */ + if (fname != NULL && (rfd = open(fname, flags, mode)) < 0) + return (NULL); + + /* Create a btree in memory (backed by disk). */ + dbp = NULL; + if (openinfo) { + if (openinfo->flags & ~(R_FIXEDLEN | R_NOKEY | R_SNAPSHOT)) + goto einval; + btopeninfo.flags = 0; + btopeninfo.cachesize = openinfo->cachesize; + btopeninfo.maxkeypage = 0; + btopeninfo.minkeypage = 0; + btopeninfo.psize = openinfo->psize; + btopeninfo.compare = NULL; + btopeninfo.prefix = NULL; + btopeninfo.lorder = openinfo->lorder; + dbp = __bt_open(openinfo->bfname, + O_RDWR, S_IRUSR | S_IWUSR, &btopeninfo); + } else + dbp = __bt_open(NULL, O_RDWR, S_IRUSR | S_IWUSR, NULL); + if (dbp == NULL) + goto err; + + /* + * Some fields in the tree structure are recno specific. Fill them + * in and make the btree structure look like a recno structure. We + * don't change the bt_ovflsize value, it's close enough and slightly + * bigger. + */ + t = dbp->internal; + if (openinfo) { + if (openinfo->flags & R_FIXEDLEN) { + SET(t, R_FIXLEN); + t->bt_reclen = openinfo->reclen; + if (t->bt_reclen == 0) + goto einval; + } + t->bt_bval = openinfo->bval; + } else + t->bt_bval = '\n'; + + SET(t, R_RECNO); + if (fname == NULL) + SET(t, R_EOF | R_INMEM); + else + t->bt_rfd = rfd; + t->bt_rcursor = 0; + + /* + * In 4.4BSD stat(2) returns true for ISSOCK on pipes. Until + * then, this is fairly close. Pipes are read-only. + */ + if (fname != NULL) { + if (lseek(rfd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) { + switch (flags & O_ACCMODE) { + case O_RDONLY: + SET(t, R_RDONLY); + break; + default: + goto einval; + } +slow: if ((t->bt_rfp = fdopen(rfd, "r")) == NULL) + goto err; + SET(t, R_CLOSEFP); + t->bt_irec = + ISSET(t, R_FIXLEN) ? __rec_fpipe : __rec_vpipe; + } else { + switch (flags & O_ACCMODE) { + case O_RDONLY: + SET(t, R_RDONLY); + break; + case O_RDWR: + break; + default: + goto einval; + } + + if (fstat(rfd, &sb)) + goto err; + /* + * Kluge -- we'd like to test to see if the file is too + * big to mmap. Since, we don't know what size or type + * off_t's or size_t's are, what the largest unsigned + * integral type is, or what random insanity the local + * C compiler will perpetrate, doing the comparison in + * a portable way is flatly impossible. Hope that mmap + * fails if the file is too large. + */ + if (sb.st_size == 0) + SET(t, R_EOF); + else { + t->bt_msize = sb.st_size; + if ((t->bt_smap = + mmap(NULL, t->bt_msize, PROT_READ, 0, rfd, + (off_t)0)) == (caddr_t)-1) + goto slow; + t->bt_cmap = t->bt_smap; + t->bt_emap = t->bt_smap + sb.st_size; + t->bt_irec = ISSET(t, R_FIXLEN) ? + __rec_fmap : __rec_vmap; + SET(t, R_MEMMAPPED); + } + } + } + + /* Use the recno routines. */ + dbp->close = __rec_close; + dbp->del = __rec_delete; + dbp->fd = __rec_fd; + dbp->get = __rec_get; + dbp->put = __rec_put; + dbp->seq = __rec_seq; + dbp->sync = __rec_sync; + + /* If the root page was created, reset the flags. */ + if ((h = mpool_get(t->bt_mp, P_ROOT, 0)) == NULL) + goto err; + if ((h->flags & P_TYPE) == P_BLEAF) { + h->flags = h->flags & ~P_TYPE | P_RLEAF; + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + } else + mpool_put(t->bt_mp, h, 0); + + if (openinfo && openinfo->flags & R_SNAPSHOT && + !ISSET(t, R_EOF | R_INMEM) && + t->bt_irec(t, MAX_REC_NUMBER) == RET_ERROR) + goto err; + return (dbp); + +einval: errno = EINVAL; +err: sverrno = errno; + if (dbp != NULL) + (void)__bt_close(dbp); + if (fname != NULL) + (void)close(rfd); + errno = sverrno; + return (NULL); +} + +int +__rec_fd(dbp) + const DB *dbp; +{ + BTREE *t; + + t = dbp->internal; + + if (ISSET(t, R_INMEM)) { + errno = ENOENT; + return (-1); + } + return (t->bt_rfd); +} diff --git a/lib/libc/db/recno/rec_put.c b/lib/libc/db/recno/rec_put.c new file mode 100644 index 0000000000..9dcdc9c30b --- /dev/null +++ b/lib/libc/db/recno/rec_put.c @@ -0,0 +1,237 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rec_put.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include +#include + +#include +#include "recno.h" + +/* + * __REC_PUT -- Add a recno item to the tree. + * + * Parameters: + * dbp: pointer to access method + * key: key + * data: data + * flag: R_CURSOR, R_IAFTER, R_IBEFORE, R_NOOVERWRITE + * + * Returns: + * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key is + * already in the tree and R_NOOVERWRITE specified. + */ +int +__rec_put(dbp, key, data, flags) + const DB *dbp; + DBT *key; + const DBT *data; + u_int flags; +{ + BTREE *t; + DBT tdata; + recno_t nrec; + int status; + + t = dbp->internal; + + switch (flags) { + case R_CURSOR: + if (!ISSET(t, B_SEQINIT)) + goto einval; + nrec = t->bt_rcursor; + break; + case R_SETCURSOR: + if ((nrec = *(recno_t *)key->data) == 0) + goto einval; + break; + case R_IAFTER: + if ((nrec = *(recno_t *)key->data) == 0) { + nrec = 1; + flags = R_IBEFORE; + } + break; + case 0: + case R_IBEFORE: + if ((nrec = *(recno_t *)key->data) == 0) + goto einval; + break; + case R_NOOVERWRITE: + if ((nrec = *(recno_t *)key->data) == 0) + goto einval; + if (nrec <= t->bt_nrecs) + return (RET_SPECIAL); + break; + default: +einval: errno = EINVAL; + return (RET_ERROR); + } + + /* + * Make sure that records up to and including the put record are + * already in the database. If skipping records, create empty ones. + */ + if (nrec > t->bt_nrecs) { + if (!ISSET(t, R_EOF | R_INMEM) && + t->bt_irec(t, nrec) == RET_ERROR) + return (RET_ERROR); + if (nrec > t->bt_nrecs + 1) { + tdata.data = NULL; + tdata.size = 0; + while (nrec > t->bt_nrecs + 1) + if (__rec_iput(t, + t->bt_nrecs, &tdata, 0) != RET_SUCCESS) + return (RET_ERROR); + } + } + + if ((status = __rec_iput(t, nrec - 1, data, flags)) != RET_SUCCESS) + return (status); + + if (flags == R_SETCURSOR) + t->bt_rcursor = nrec; + + SET(t, R_MODIFIED); + return (__rec_ret(t, NULL, nrec, key, NULL)); +} + +/* + * __REC_IPUT -- Add a recno item to the tree. + * + * Parameters: + * t: tree + * nrec: record number + * data: data + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__rec_iput(t, nrec, data, flags) + BTREE *t; + recno_t nrec; + const DBT *data; + u_int flags; +{ + DBT tdata; + EPG *e; + PAGE *h; + indx_t index, nxtindex; + pgno_t pg; + size_t nbytes; + int dflags, status; + char *dest, db[NOVFLSIZE]; + + /* + * If the data won't fit on a page, store it on indirect pages. + * + * XXX + * If the insert fails later on, these pages aren't recovered. + */ + if (data->size > t->bt_ovflsize) { + if (__ovfl_put(t, data, &pg) == RET_ERROR) + return (RET_ERROR); + tdata.data = db; + tdata.size = NOVFLSIZE; + *(pgno_t *)db = pg; + *(size_t *)(db + sizeof(pgno_t)) = data->size; + dflags = P_BIGDATA; + data = &tdata; + } else + dflags = 0; + + /* __rec_search pins the returned page. */ + if ((e = __rec_search(t, nrec, + nrec > t->bt_nrecs || flags == R_IAFTER || flags == R_IBEFORE ? + SINSERT : SEARCH)) == NULL) + return (RET_ERROR); + + h = e->page; + index = e->index; + + /* + * Add the specified key/data pair to the tree. The R_IAFTER and + * R_IBEFORE flags insert the key after/before the specified key. + * + * Pages are split as required. + */ + switch (flags) { + case R_IAFTER: + ++index; + break; + case R_IBEFORE: + break; + default: + if (nrec < t->bt_nrecs && + __rec_dleaf(t, h, index) == RET_ERROR) { + mpool_put(t->bt_mp, h, 0); + return (RET_ERROR); + } + break; + } + + /* + * If not enough room, split the page. The split code will insert + * the key and data and unpin the current page. If inserting into + * the offset array, shift the pointers up. + */ + nbytes = NRLEAFDBT(data->size); + if (h->upper - h->lower < nbytes + sizeof(indx_t)) { + status = __bt_split(t, h, NULL, data, dflags, nbytes, index); + if (status == RET_SUCCESS) + ++t->bt_nrecs; + return (status); + } + + if (index < (nxtindex = NEXTINDEX(h))) + memmove(h->linp + index + 1, h->linp + index, + (nxtindex - index) * sizeof(indx_t)); + h->lower += sizeof(indx_t); + + h->linp[index] = h->upper -= nbytes; + dest = (char *)h + h->upper; + WR_RLEAF(dest, data, dflags); + + ++t->bt_nrecs; + SET(t, B_MODIFIED); + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + + return (RET_SUCCESS); +} diff --git a/lib/libc/db/recno/rec_search.c b/lib/libc/db/recno/rec_search.c new file mode 100644 index 0000000000..9d50b2f50c --- /dev/null +++ b/lib/libc/db/recno/rec_search.c @@ -0,0 +1,127 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rec_search.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include + +#include +#include "recno.h" + +/* + * __REC_SEARCH -- Search a btree for a key. + * + * Parameters: + * t: tree to search + * recno: key to find + * op: search operation + * + * Returns: + * EPG for matching record, if any, or the EPG for the location of the + * key, if it were inserted into the tree. + * + * Warnings: + * The EPG returned is in static memory, and will be overwritten by the + * next search of any kind in any tree. + */ +EPG * +__rec_search(t, recno, op) + BTREE *t; + recno_t recno; + enum SRCHOP op; +{ + static EPG e; + register indx_t index; + register PAGE *h; + EPGNO *parent; + RINTERNAL *r; + pgno_t pg; + indx_t top; + recno_t total; + int serrno; + + BT_CLR(t); + for (pg = P_ROOT, total = 0;;) { + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + goto err; + if (h->flags & P_RLEAF) { + e.page = h; + e.index = recno - total; + return (&e); + } + for (index = 0, top = NEXTINDEX(h);;) { + r = GETRINTERNAL(h, index); + if (++index == top || total + r->nrecs > recno) + break; + total += r->nrecs; + } + + if (__bt_push(t, pg, index - 1) == RET_ERROR) + return (NULL); + + pg = r->pgno; + switch (op) { + case SDELETE: + --GETRINTERNAL(h, (index - 1))->nrecs; + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + break; + case SINSERT: + ++GETRINTERNAL(h, (index - 1))->nrecs; + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + break; + case SEARCH: + mpool_put(t->bt_mp, h, 0); + break; + } + + } + /* Try and recover the tree. */ +err: serrno = errno; + if (op != SEARCH) + while ((parent = BT_POP(t)) != NULL) { + if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL) + break; + if (op == SINSERT) + --GETRINTERNAL(h, parent->index)->nrecs; + else + ++GETRINTERNAL(h, parent->index)->nrecs; + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + } + errno = serrno; + return (NULL); +} diff --git a/lib/libc/db/recno/rec_seq.c b/lib/libc/db/recno/rec_seq.c new file mode 100644 index 0000000000..29210a7536 --- /dev/null +++ b/lib/libc/db/recno/rec_seq.c @@ -0,0 +1,122 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)rec_seq.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include + +#include +#include "recno.h" + +/* + * __REC_SEQ -- Recno sequential scan interface. + * + * Parameters: + * dbp: pointer to access method + * key: key for positioning and return value + * data: data return value + * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV. + * + * Returns: + * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. + */ +int +__rec_seq(dbp, key, data, flags) + const DB *dbp; + DBT *key, *data; + u_int flags; +{ + BTREE *t; + EPG *e; + recno_t nrec; + int status; + + t = dbp->internal; + switch(flags) { + case R_CURSOR: + if ((nrec = *(recno_t *)key->data) == 0) + goto einval; + break; + case R_NEXT: + if (ISSET(t, B_SEQINIT)) { + nrec = t->bt_rcursor + 1; + break; + } + /* FALLTHROUGH */ + case R_FIRST: + nrec = 1; + break; + case R_PREV: + if (ISSET(t, B_SEQINIT)) { + if ((nrec = t->bt_rcursor - 1) == 0) + return (RET_SPECIAL); + break; + } + /* FALLTHROUGH */ + case R_LAST: + if (!ISSET(t, R_EOF | R_INMEM) && + t->bt_irec(t, MAX_REC_NUMBER) == RET_ERROR) + return (RET_ERROR); + nrec = t->bt_nrecs; + break; + default: +einval: errno = EINVAL; + return (RET_ERROR); + } + + if (t->bt_nrecs == 0 || nrec > t->bt_nrecs) { + if (!ISSET(t, R_EOF | R_INMEM) && + (status = t->bt_irec(t, nrec)) != RET_SUCCESS) + return (status); + if (t->bt_nrecs == 0 || nrec > t->bt_nrecs) + return (RET_SPECIAL); + } + + if ((e = __rec_search(t, nrec - 1, SEARCH)) == NULL) + return (RET_ERROR); + + SET(t, B_SEQINIT); + t->bt_rcursor = nrec; + + status = __rec_ret(t, e, nrec, key, data); + + mpool_put(t->bt_mp, e->page, 0); + return (status); +} diff --git a/lib/libc/db/recno/rec_utils.c b/lib/libc/db/recno/rec_utils.c new file mode 100644 index 0000000000..c55f4df0f8 --- /dev/null +++ b/lib/libc/db/recno/rec_utils.c @@ -0,0 +1,103 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rec_utils.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +#include +#include "recno.h" + +/* + * __REC_RET -- Build return data as a result of search or scan. + * + * Parameters: + * t: tree + * d: LEAF to be returned to the user. + * data: user's data structure + * + * Returns: + * RET_SUCCESS, RET_ERROR. + */ +int +__rec_ret(t, e, nrec, key, data) + BTREE *t; + EPG *e; + recno_t nrec; + DBT *key, *data; +{ + register RLEAF *rl; + register void *p; + + if (data == NULL) + goto retkey; + + rl = GETRLEAF(e->page, e->index); + + if (rl->flags & P_BIGDATA) { + if (__ovfl_get(t, rl->bytes, + &data->size, &t->bt_dbuf, &t->bt_dbufsz)) + return (RET_ERROR); + } else { + /* Use +1 in case the first record retrieved is 0 length. */ + if (rl->dsize + 1 > t->bt_dbufsz) { + if ((p = realloc(t->bt_dbuf, rl->dsize + 1)) == NULL) + return (RET_ERROR); + t->bt_dbuf = p; + t->bt_dbufsz = rl->dsize + 1; + } + memmove(t->bt_dbuf, rl->bytes, rl->dsize); + data->size = rl->dsize; + } + data->data = t->bt_dbuf; + +retkey: if (key == NULL) + return (RET_SUCCESS); + + if (sizeof(recno_t) > t->bt_kbufsz) { + if ((p = realloc(t->bt_kbuf, sizeof(recno_t))) == NULL) + return (RET_ERROR); + t->bt_kbuf = p; + t->bt_kbufsz = sizeof(recno_t); + } + memmove(t->bt_kbuf, &nrec, sizeof(recno_t)); + key->size = sizeof(recno_t); + key->data = t->bt_kbuf; + return (RET_SUCCESS); +} diff --git a/lib/libc/db/recno/recno.h b/lib/libc/db/recno/recno.h new file mode 100644 index 0000000000..bec772c2fa --- /dev/null +++ b/lib/libc/db/recno/recno.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)recno.h 8.1 (Berkeley) 6/4/93 + */ + +enum SRCHOP { SDELETE, SINSERT, SEARCH}; /* Rec_search operation. */ + +#include "../btree/btree.h" +#include "extern.h" diff --git a/lib/libc/db/test/Makefile b/lib/libc/db/test/Makefile new file mode 100644 index 0000000000..f04a780c74 --- /dev/null +++ b/lib/libc/db/test/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= dbtest +SRCS= dbtest.c + +CFLAGS+= -g -DDEBUG -DSTATISTICS +NOMAN= noman + +NOINCLUDE=1 +.include diff --git a/lib/libc/db/test/README b/lib/libc/db/test/README new file mode 100644 index 0000000000..b674c51a19 --- /dev/null +++ b/lib/libc/db/test/README @@ -0,0 +1,45 @@ +# @(#)README 8.1 (Berkeley) 6/4/93 + +The script file consists of lines with a initial character which is +the "command" for that line. Legal characters are as follows: + +c: compare a record + + must be followed by [kK][dD]; the data value in the database + associated with the specified key is compared to the specified + data value. +e: echo a string + + writes out the rest of the line into the output file; if the + last character is not a carriage-return, a newline is appended. +g: do a get command + + must be followed by [kK] + + writes out the retrieved data DBT. +p: do a put command + + must be followed by [kK][dD] +r: do a del command + + must be followed by [kK] +s: do a seq command + + writes out the retrieved data DBT. +f: set the flags for the next command + + no value zero's the flags +D [file]: data file + + set the current data value to the contents of the file +d [data]: + + set the current key value to the contents of the line. +K [file]: key file + + set the current key value to the contents of the file +k [data]: + + set the current key value to the contents of the line. +o [r]: dump [reverse] + + dump the database out, if 'r' is set, in reverse order. + +Options to dbtest are as follows: + + -f: Use the file argument as the database file. + -i: Use the rest of the argument to set elements in the info + structure. If the type is btree, then "-i cachesize=10240" + will set BTREEINFO.cachesize to 10240. + -o: The rest of the argument is the output file instead of + using stdout. + +Dbtest requires two arguments, the type of access "hash", "recno" or +"btree", and the script name. diff --git a/lib/libc/db/test/btree.tests/main.c b/lib/libc/db/test/btree.tests/main.c new file mode 100644 index 0000000000..f26e193562 --- /dev/null +++ b/lib/libc/db/test/btree.tests/main.c @@ -0,0 +1,765 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "btree.h" + +typedef struct cmd_table { + char *cmd; + int nargs; + int rconv; + void (*func) __P((DB *, char **)); + char *usage, *descrip; +} cmd_table; + +int stopstop; +DB *globaldb; + +void append __P((DB *, char **)); +void bstat __P((DB *, char **)); +void cursor __P((DB *, char **)); +void delcur __P((DB *, char **)); +void delete __P((DB *, char **)); +void dump __P((DB *, char **)); +void first __P((DB *, char **)); +void get __P((DB *, char **)); +void help __P((DB *, char **)); +void iafter __P((DB *, char **)); +void ibefore __P((DB *, char **)); +void icursor __P((DB *, char **)); +void insert __P((DB *, char **)); +void keydata __P((DBT *, DBT *)); +void last __P((DB *, char **)); +void list __P((DB *, char **)); +void load __P((DB *, char **)); +void mstat __P((DB *, char **)); +void next __P((DB *, char **)); +int parse __P((char *, char **, int)); +void previous __P((DB *, char **)); +void show __P((DB *, char **)); +void usage __P((void)); +void user __P((DB *)); + +cmd_table commands[] = { + "?", 0, 0, help, "help", NULL, + "a", 2, 1, append, "append key def", "append key with data def", + "b", 0, 0, bstat, "bstat", "stat btree", + "c", 1, 1, cursor, "cursor word", "move cursor to word", + "delc", 0, 0, delcur, "delcur", "delete key the cursor references", + "dele", 1, 1, delete, "delete word", "delete word", + "d", 0, 0, dump, "dump", "dump database", + "f", 0, 0, first, "first", "move cursor to first record", + "g", 1, 1, get, "get key", "locate key", + "h", 0, 0, help, "help", "print command summary", + "ia", 2, 1, iafter, "iafter key data", "insert data after key", + "ib", 2, 1, ibefore, "ibefore key data", "insert data before key", + "ic", 2, 1, icursor, "icursor key data", "replace cursor", + "in", 2, 1, insert, "insert key def", "insert key with data def", + "la", 0, 0, last, "last", "move cursor to last record", + "li", 1, 1, list, "list file", "list to a file", + "loa", 1, 0, load, "load file", NULL, + "loc", 1, 1, get, "get key", NULL, + "m", 0, 0, mstat, "mstat", "stat memory pool", + "n", 0, 0, next, "next", "move cursor forward one record", + "p", 0, 0, previous, "previous", "move cursor back one record", + "q", 0, 0, NULL, "quit", "quit", + "sh", 1, 0, show, "show page", "dump a page", + { NULL }, +}; + +int recno; /* use record numbers */ +char *dict = "words"; /* default dictionary */ +char *progname; + +int +main(argc, argv) + int argc; + char **argv; +{ + int c; + DB *db; + BTREEINFO b; + + progname = *argv; + + b.flags = 0; + b.cachesize = 0; + b.maxkeypage = 0; + b.minkeypage = 0; + b.psize = 0; + b.compare = NULL; + b.prefix = NULL; + b.lorder = 0; + + while ((c = getopt(argc, argv, "bc:di:lp:ru")) != EOF) { + switch (c) { + case 'b': + b.lorder = BIG_ENDIAN; + break; + case 'c': + b.cachesize = atoi(optarg); + break; + case 'd': + b.flags |= R_DUP; + break; + case 'i': + dict = optarg; + break; + case 'l': + b.lorder = LITTLE_ENDIAN; + break; + case 'p': + b.psize = atoi(optarg); + break; + case 'r': + recno = 1; + break; + case 'u': + b.flags = 0; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (recno) + db = dbopen(*argv == NULL ? NULL : *argv, O_RDWR, + 0, DB_RECNO, NULL); + else + db = dbopen(*argv == NULL ? NULL : *argv, O_CREAT|O_RDWR, + 0600, DB_BTREE, &b); + + if (db == NULL) { + (void)fprintf(stderr, "dbopen: %s\n", strerror(errno)); + exit(1); + } + globaldb = db; + user(db); + exit(0); + /* NOTREACHED */ +} + +void +user(db) + DB *db; +{ + FILE *ifp; + int argc, i, last; + char *lbuf, *argv[4], buf[512]; + + if ((ifp = fopen("/dev/tty", "r")) == NULL) { + (void)fprintf(stderr, + "/dev/tty: %s\n", strerror(errno)); + exit(1); + } + for (last = 0;;) { + (void)printf("> "); + (void)fflush(stdout); + if ((lbuf = fgets(&buf[0], 512, ifp)) == NULL) + break; + if (lbuf[0] == '\n') { + i = last; + goto uselast; + } + lbuf[strlen(lbuf) - 1] = '\0'; + + if (lbuf[0] == 'q') + break; + + argc = parse(lbuf, &argv[0], 3); + if (argc == 0) + continue; + + for (i = 0; commands[i].cmd != NULL; i++) + if (strncmp(commands[i].cmd, argv[0], + strlen(commands[i].cmd)) == 0) + break; + + if (commands[i].cmd == NULL) { + (void)fprintf(stderr, + "%s: command unknown ('help' for help)\n", lbuf); + continue; + } + + if (commands[i].nargs != argc - 1) { + (void)fprintf(stderr, "usage: %s\n", commands[i].usage); + continue; + } + + if (recno && commands[i].rconv) { + static recno_t nlong; + nlong = atoi(argv[1]); + argv[1] = (char *)&nlong; + } +uselast: last = i; + (*commands[i].func)(db, argv); + } + if ((db->sync)(db) == RET_ERROR) + perror("dbsync"); + else if ((db->close)(db) == RET_ERROR) + perror("dbclose"); +} + +int +parse(lbuf, argv, maxargc) + char *lbuf, **argv; + int maxargc; +{ + int argc = 0; + char *c; + + c = lbuf; + while (isspace(*c)) + c++; + while (*c != '\0' && argc < maxargc) { + *argv++ = c; + argc++; + while (!isspace(*c) && *c != '\0') { + c++; + } + while (isspace(*c)) + *c++ = '\0'; + } + return (argc); +} + +void +append(db, argv) + DB *db; + char **argv; +{ + DBT key, data; + int status; + + if (!recno) { + (void)fprintf(stderr, + "append only available for recno db's.\n"); + return; + } + key.data = argv[1]; + key.size = sizeof(recno_t); + data.data = argv[2]; + data.size = strlen(data.data); + status = (db->put)(db, &key, &data, R_APPEND); + switch (status) { + case RET_ERROR: + perror("append/put"); + break; + case RET_SPECIAL: + (void)printf("%s (duplicate key)\n", argv[1]); + break; + case RET_SUCCESS: + break; + } +} + +void +cursor(db, argv) + DB *db; + char **argv; +{ + DBT data, key; + int status; + + key.data = argv[1]; + if (recno) + key.size = sizeof(recno_t); + else + key.size = strlen(argv[1]) + 1; + status = (*db->seq)(db, &key, &data, R_CURSOR); + switch (status) { + case RET_ERROR: + perror("cursor/seq"); + break; + case RET_SPECIAL: + (void)printf("key not found\n"); + break; + case RET_SUCCESS: + keydata(&key, &data); + break; + } +} + +void +delcur(db, argv) + DB *db; + char **argv; +{ + int status; + + status = (*db->del)(db, NULL, R_CURSOR); + + if (status == RET_ERROR) + perror("delcur/del"); +} + +void +delete(db, argv) + DB *db; + char **argv; +{ + DBT key; + int status; + + key.data = argv[1]; + if (recno) + key.size = sizeof(recno_t); + else + key.size = strlen(argv[1]) + 1; + + status = (*db->del)(db, &key, 0); + switch (status) { + case RET_ERROR: + perror("delete/del"); + break; + case RET_SPECIAL: + (void)printf("key not found\n"); + break; + case RET_SUCCESS: + break; + } +} + +void +dump(db, argv) + DB *db; + char **argv; +{ + __bt_dump(db); +} + +void +first(db, argv) + DB *db; + char **argv; +{ + DBT data, key; + int status; + + status = (*db->seq)(db, &key, &data, R_FIRST); + + switch (status) { + case RET_ERROR: + perror("first/seq"); + break; + case RET_SPECIAL: + (void)printf("no more keys\n"); + break; + case RET_SUCCESS: + keydata(&key, &data); + break; + } +} + +void +get(db, argv) + DB *db; + char **argv; +{ + DBT data, key; + int status; + + key.data = argv[1]; + if (recno) + key.size = sizeof(recno_t); + else + key.size = strlen(argv[1]) + 1; + + status = (*db->get)(db, &key, &data, 0); + + switch (status) { + case RET_ERROR: + perror("get/get"); + break; + case RET_SPECIAL: + (void)printf("key not found\n"); + break; + case RET_SUCCESS: + keydata(&key, &data); + break; + } +} + +void +help(db, argv) + DB *db; + char **argv; +{ + int i; + + for (i = 0; commands[i].cmd; i++) + if (commands[i].descrip) + (void)printf("%s: %s\n", + commands[i].usage, commands[i].descrip); +} + +void +iafter(db, argv) + DB *db; + char **argv; +{ + DBT key, data; + int status; + + if (!recno) { + (void)fprintf(stderr, + "iafter only available for recno db's.\n"); + return; + } + key.data = argv[1]; + key.size = sizeof(recno_t); + data.data = argv[2]; + data.size = strlen(data.data); + status = (db->put)(db, &key, &data, R_IAFTER); + switch (status) { + case RET_ERROR: + perror("iafter/put"); + break; + case RET_SPECIAL: + (void)printf("%s (duplicate key)\n", argv[1]); + break; + case RET_SUCCESS: + break; + } +} + +void +ibefore(db, argv) + DB *db; + char **argv; +{ + DBT key, data; + int status; + + if (!recno) { + (void)fprintf(stderr, + "ibefore only available for recno db's.\n"); + return; + } + key.data = argv[1]; + key.size = sizeof(recno_t); + data.data = argv[2]; + data.size = strlen(data.data); + status = (db->put)(db, &key, &data, R_IBEFORE); + switch (status) { + case RET_ERROR: + perror("ibefore/put"); + break; + case RET_SPECIAL: + (void)printf("%s (duplicate key)\n", argv[1]); + break; + case RET_SUCCESS: + break; + } +} + +void +icursor(db, argv) + DB *db; + char **argv; +{ + int status; + DBT data, key; + + key.data = argv[1]; + if (recno) + key.size = sizeof(recno_t); + else + key.size = strlen(argv[1]) + 1; + data.data = argv[2]; + data.size = strlen(argv[2]) + 1; + + status = (*db->put)(db, &key, &data, R_CURSOR); + switch (status) { + case RET_ERROR: + perror("icursor/put"); + break; + case RET_SPECIAL: + (void)printf("%s (duplicate key)\n", argv[1]); + break; + case RET_SUCCESS: + break; + } +} + +void +insert(db, argv) + DB *db; + char **argv; +{ + int status; + DBT data, key; + + key.data = argv[1]; + if (recno) + key.size = sizeof(recno_t); + else + key.size = strlen(argv[1]) + 1; + data.data = argv[2]; + data.size = strlen(argv[2]) + 1; + + status = (*db->put)(db, &key, &data, R_NOOVERWRITE); + switch (status) { + case RET_ERROR: + perror("insert/put"); + break; + case RET_SPECIAL: + (void)printf("%s (duplicate key)\n", argv[1]); + break; + case RET_SUCCESS: + break; + } +} + +void +last(db, argv) + DB *db; + char **argv; +{ + DBT data, key; + int status; + + status = (*db->seq)(db, &key, &data, R_LAST); + + switch (status) { + case RET_ERROR: + perror("last/seq"); + break; + case RET_SPECIAL: + (void)printf("no more keys\n"); + break; + case RET_SUCCESS: + keydata(&key, &data); + break; + } +} + +void +list(db, argv) + DB *db; + char **argv; +{ + DBT data, key; + FILE *fp; + int status; + + if ((fp = fopen(argv[1], "w")) == NULL) { + (void)fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); + return; + } + status = (*db->seq)(db, &key, &data, R_FIRST); + while (status == RET_SUCCESS) { + (void)fprintf(fp, "%s\n", key.data); + status = (*db->seq)(db, &key, &data, R_NEXT); + } + if (status == RET_ERROR) + perror("list/seq"); +} + +DB *BUGdb; +void +load(db, argv) + DB *db; + char **argv; +{ + register char *p, *t; + FILE *fp; + DBT data, key; + recno_t cnt; + size_t len; + int status; + char *lp, buf[16 * 1024]; + + BUGdb = db; + if ((fp = fopen(argv[1], "r")) == NULL) { + (void)fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); + return; + } + (void)printf("loading %s...\n", argv[1]); + + for (cnt = 1; (lp = fgetline(fp, &len)) != NULL; ++cnt) { + if (recno) { + key.data = &cnt; + key.size = sizeof(recno_t); + data.data = lp; + data.size = len + 1; + } else { + key.data = lp; + key.size = len + 1; + for (p = lp + len - 1, t = buf; p >= lp; *t++ = *p--); + *t = '\0'; + data.data = buf; + data.size = len + 1; + } + + status = (*db->put)(db, &key, &data, R_NOOVERWRITE); + switch (status) { + case RET_ERROR: + perror("load/put"); + exit(1); + case RET_SPECIAL: + if (recno) + (void)fprintf(stderr, + "duplicate: %ld {%s}\n", cnt, data.data); + else + (void)fprintf(stderr, + "duplicate: %ld {%s}\n", cnt, key.data); + exit(1); + case RET_SUCCESS: + break; + } + } + (void)fclose(fp); +} + +void +next(db, argv) + DB *db; + char **argv; +{ + DBT data, key; + int status; + + status = (*db->seq)(db, &key, &data, R_NEXT); + + switch (status) { + case RET_ERROR: + perror("next/seq"); + break; + case RET_SPECIAL: + (void)printf("no more keys\n"); + break; + case RET_SUCCESS: + keydata(&key, &data); + break; + } +} + +void +previous(db, argv) + DB *db; + char **argv; +{ + DBT data, key; + int status; + + status = (*db->seq)(db, &key, &data, R_PREV); + + switch (status) { + case RET_ERROR: + perror("previous/seq"); + break; + case RET_SPECIAL: + (void)printf("no more keys\n"); + break; + case RET_SUCCESS: + keydata(&key, &data); + break; + } +} + +void +show(db, argv) + DB *db; + char **argv; +{ + BTREE *t; + PAGE *h; + pgno_t pg; + + pg = atoi(argv[1]); + t = db->internal; + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) { + (void)printf("getpage of %ld failed\n", pg); + return; + } + if (pg == 0) + __bt_dmpage(h); + else + __bt_dpage(h); + mpool_put(t->bt_mp, h, 0); +} + +void +bstat(db, argv) + DB *db; + char **argv; +{ + (void)printf("BTREE\n"); + __bt_stat(db); +} + +void +mstat(db, argv) + DB *db; + char **argv; +{ + (void)printf("MPOOL\n"); + mpool_stat(((BTREE *)db->internal)->bt_mp); +} + +void +keydata(key, data) + DBT *key, *data; +{ + if (!recno && key->size > 0) + (void)printf("%s/", key->data); + if (data->size > 0) + (void)printf("%s", data->data); + (void)printf("\n"); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: %s [-bdlu] [-c cache] [-i file] [-p page] [file]\n", + progname); + exit (1); +} diff --git a/lib/libc/db/test/dbtest.c b/lib/libc/db/test/dbtest.c new file mode 100644 index 0000000000..823223088d --- /dev/null +++ b/lib/libc/db/test/dbtest.c @@ -0,0 +1,656 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)dbtest.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +enum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA }; + +void compare __P((DBT *, DBT *)); +DBTYPE dbtype __P((char *)); +void dump __P((DB *, int)); +void err __P((const char *, ...)); +void get __P((DB *, DBT *)); +void getdata __P((DB *, DBT *, DBT *)); +void put __P((DB *, DBT *, DBT *)); +void rem __P((DB *, DBT *)); +void *rfile __P((char *, size_t *)); +void seq __P((DB *, DBT *)); +u_int setflags __P((char *)); +void *setinfo __P((DBTYPE, char *)); +void usage __P((void)); +void *xmalloc __P((char *, size_t)); + +DBTYPE type; +void *infop; +u_long lineno; +u_int flags; +int ofd = STDOUT_FILENO; + +DB *XXdbp; /* Global for gdb. */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + extern char *optarg; + enum S command, state; + DB *dbp; + DBT data, key, keydata; + size_t len; + int ch; + char *fname, *infoarg, *p, buf[8 * 1024]; + + infoarg = NULL; + fname = NULL; + while ((ch = getopt(argc, argv, "f:i:o:")) != EOF) + switch(ch) { + case 'f': + fname = optarg; + break; + case 'i': + infoarg = optarg; + break; + case 'o': + if ((ofd = open(optarg, + O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) + err("%s: %s", optarg, strerror(errno)); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + /* Set the type. */ + type = dbtype(*argv++); + + /* Open the descriptor file. */ + if (freopen(*argv, "r", stdin) == NULL) + err("%s: %s", *argv, strerror(errno)); + + /* Set up the db structure as necessary. */ + if (infoarg == NULL) + infop = NULL; + else + for (p = strtok(infoarg, ",\t "); p != NULL; + p = strtok(0, ",\t ")) + if (*p != '\0') + infop = setinfo(type, p); + + /* Open the DB. */ + if (fname == NULL) { + p = getenv("TMPDIR"); + if (p == NULL) + p = "/var/tmp"; + (void)sprintf(buf, "%s/__dbtest", p); + fname = buf; + (void)unlink(buf); + } + if ((dbp = dbopen(fname, + O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, type, infop)) == NULL) + err("dbopen: %s", strerror(errno)); + XXdbp = dbp; + + state = COMMAND; + for (lineno = 1; + (p = fgets(buf, sizeof(buf), stdin)) != NULL; ++lineno) { + len = strlen(buf); + switch(*p) { + case 'c': /* compare */ + if (state != COMMAND) + err("line %lu: not expecting command", lineno); + state = KEY; + command = COMPARE; + break; + case 'e': /* echo */ + if (state != COMMAND) + err("line %lu: not expecting command", lineno); + /* Don't display the newline, if CR at EOL. */ + if (p[len - 2] == '\r') + --len; + if (write(ofd, p + 1, len - 1) != len - 1) + err("write: %s", strerror(errno)); + break; + case 'g': /* get */ + if (state != COMMAND) + err("line %lu: not expecting command", lineno); + state = KEY; + command = GET; + break; + case 'p': /* put */ + if (state != COMMAND) + err("line %lu: not expecting command", lineno); + state = KEY; + command = PUT; + break; + case 'r': /* remove */ + if (state != COMMAND) + err("line %lu: not expecting command", lineno); + state = KEY; + command = REMOVE; + break; + case 's': /* seq */ + if (state != COMMAND) + err("line %lu: not expecting command", lineno); + if (flags == R_CURSOR) { + state = KEY; + command = SEQ; + } else + seq(dbp, &key); + break; + case 'f': + flags = setflags(p + 1); + break; + case 'D': /* data file */ + if (state != DATA) + err("line %lu: not expecting data", lineno); + data.data = rfile(p + 1, &data.size); + goto ldata; + case 'd': /* data */ + if (state != DATA) + err("line %lu: not expecting data", lineno); + data.data = xmalloc(p + 1, len - 1); + data.size = len - 1; +ldata: switch(command) { + case COMPARE: + compare(&keydata, &data); + break; + case PUT: + put(dbp, &key, &data); + break; + default: + err("line %lu: command doesn't take data", + lineno); + } + if (type != DB_RECNO) + free(key.data); + free(data.data); + state = COMMAND; + break; + case 'K': /* key file */ + if (state != KEY) + err("line %lu: not expecting a key", lineno); + if (type == DB_RECNO) + err("line %lu: 'K' not available for recno", + lineno); + key.data = rfile(p + 1, &key.size); + goto lkey; + case 'k': /* key */ + if (state != KEY) + err("line %lu: not expecting a key", lineno); + if (type == DB_RECNO) { + static recno_t recno; + recno = strtol(p + 1, NULL, 0); + key.data = &recno; + key.size = sizeof(recno); + } else { + key.data = xmalloc(p + 1, len - 1); + key.size = len - 1; + } +lkey: switch(command) { + case COMPARE: + getdata(dbp, &key, &keydata); + state = DATA; + break; + case GET: + get(dbp, &key); + if (type != DB_RECNO) + free(key.data); + state = COMMAND; + break; + case PUT: + state = DATA; + break; + case REMOVE: + rem(dbp, &key); + if (type != DB_RECNO) + free(key.data); + state = COMMAND; + break; + case SEQ: + seq(dbp, &key); + if (type != DB_RECNO) + free(key.data); + state = COMMAND; + break; + default: + err("line %lu: command doesn't take a key", + lineno); + } + break; + case 'o': + dump(dbp, p[1] == 'r'); + break; + default: + err("line %lu: %s: unknown command character", + p, lineno); + } + } + if (dbp->close(dbp)) + err("db->close: %s", strerror(errno)); + (void)close(ofd); + exit(0); +} + +#define NOOVERWRITE "put failed, would overwrite key\n" +#define NOSUCHKEY "get failed, no such key\n" + +void +compare(db1, db2) + DBT *db1, *db2; +{ + register size_t len; + register u_char *p1, *p2; + + if (db1->size != db2->size) + printf("compare failed: key->data len %lu != data len %lu\n", + db1->size, db2->size); + + len = MIN(db1->size, db2->size); + for (p1 = db1->data, p2 = db2->data; len--;) + if (*p1++ != *p2++) { + printf("compare failed at offset %d\n", + p1 - (u_char *)db1->data); + break; + } +} + +void +get(dbp, kp) + DB *dbp; + DBT *kp; +{ + DBT data; + + switch(dbp->get(dbp, kp, &data, flags)) { + case 0: + (void)write(ofd, data.data, data.size); + break; + case -1: + err("line %lu: get: %s", lineno, strerror(errno)); + /* NOTREACHED */ + case 1: + (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); + (void)fprintf(stderr, "%d: %.*s: %s\n", + lineno, kp->size, kp->data, NOSUCHKEY); + break; + } +} + +void +getdata(dbp, kp, dp) + DB *dbp; + DBT *kp, *dp; +{ + switch(dbp->get(dbp, kp, dp, flags)) { + case 0: + return; + case -1: + err("line %lu: getdata: %s", lineno, strerror(errno)); + /* NOTREACHED */ + case 1: + err("line %lu: get failed, no such key", lineno); + /* NOTREACHED */ + } +} + +void +put(dbp, kp, dp) + DB *dbp; + DBT *kp, *dp; +{ + switch(dbp->put(dbp, kp, dp, flags)) { + case 0: + break; + case -1: + err("line %lu: put: %s", lineno, strerror(errno)); + /* NOTREACHED */ + case 1: + (void)write(ofd, NOOVERWRITE, sizeof(NOOVERWRITE) - 1); + break; + } +} + +void +rem(dbp, kp) + DB *dbp; + DBT *kp; +{ + switch(dbp->del(dbp, kp, flags)) { + case 0: + break; + case -1: + err("line %lu: get: %s", lineno, strerror(errno)); + /* NOTREACHED */ + case 1: + (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); + break; + } +} + +void +seq(dbp, kp) + DB *dbp; + DBT *kp; +{ + DBT data; + + switch(dbp->seq(dbp, kp, &data, flags)) { + case 0: + (void)write(ofd, data.data, data.size); + break; + case -1: + err("line %lu: seq: %s", lineno, strerror(errno)); + /* NOTREACHED */ + case 1: + (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); + break; + } +} + +void +dump(dbp, rev) + DB *dbp; + int rev; +{ + DBT key, data; + int flags, nflags; + + if (rev) { + flags = R_LAST; + nflags = R_PREV; + } else { + flags = R_FIRST; + nflags = R_NEXT; + } + for (;; flags = nflags) + switch(dbp->seq(dbp, &key, &data, flags)) { + case 0: + (void)write(ofd, data.data, data.size); + break; + case 1: + goto done; + case -1: + err("line %lu: (dump) seq: %s", + lineno, strerror(errno)); + /* NOTREACHED */ + } +done: return; +} + +u_int +setflags(s) + char *s; +{ + char *p; + + for (; isspace(*s); ++s); + if (*s == '\n') + return (0); + if ((p = index(s, '\n')) != NULL) + *p = '\0'; + if (!strcmp(s, "R_CURSOR")) + return (R_CURSOR); + if (!strcmp(s, "R_FIRST")) + return (R_FIRST); + if (!strcmp(s, "R_IAFTER")) + return (R_IAFTER); + if (!strcmp(s, "R_IBEFORE")) + return (R_IBEFORE); + if (!strcmp(s, "R_LAST")) + return (R_LAST); + if (!strcmp(s, "R_NEXT")) + return (R_NEXT); + if (!strcmp(s, "R_NOOVERWRITE")) + return (R_NOOVERWRITE); + if (!strcmp(s, "R_PREV")) + return (R_PREV); + if (!strcmp(s, "R_SETCURSOR")) + return (R_SETCURSOR); + err("line %lu: %s: unknown flag", lineno, s); + /* NOTREACHED */ +} + +DBTYPE +dbtype(s) + char *s; +{ + if (!strcmp(s, "btree")) + return (DB_BTREE); + if (!strcmp(s, "hash")) + return (DB_HASH); + if (!strcmp(s, "recno")) + return (DB_RECNO); + err("%s: unknown type (use btree, hash or recno)", s); + /* NOTREACHED */ +} + +void * +setinfo(type, s) + DBTYPE type; + char *s; +{ + static BTREEINFO ib; + static HASHINFO ih; + static RECNOINFO rh; + char *eq; + + if ((eq = index(s, '=')) == NULL) + err("%s: illegal structure set statement", s); + *eq++ = '\0'; + if (!isdigit(*eq)) + err("%s: structure set statement must be a number", s); + + switch(type) { + case DB_BTREE: + if (!strcmp("flags", s)) { + ib.flags = strtoul(eq, NULL, 0); + return (&ib); + } + if (!strcmp("cachesize", s)) { + ib.cachesize = strtoul(eq, NULL, 0); + return (&ib); + } + if (!strcmp("maxkeypage", s)) { + ib.maxkeypage = strtoul(eq, NULL, 0); + return (&ib); + } + if (!strcmp("minkeypage", s)) { + ib.minkeypage = strtoul(eq, NULL, 0); + return (&ib); + } + if (!strcmp("lorder", s)) { + ib.lorder = strtoul(eq, NULL, 0); + return (&ib); + } + if (!strcmp("psize", s)) { + ib.psize = strtoul(eq, NULL, 0); + return (&ib); + } + break; + case DB_HASH: + if (!strcmp("bsize", s)) { + ih.bsize = strtoul(eq, NULL, 0); + return (&ih); + } + if (!strcmp("ffactor", s)) { + ih.ffactor = strtoul(eq, NULL, 0); + return (&ih); + } + if (!strcmp("nelem", s)) { + ih.nelem = strtoul(eq, NULL, 0); + return (&ih); + } + if (!strcmp("cachesize", s)) { + ih.cachesize = strtoul(eq, NULL, 0); + return (&ih); + } + if (!strcmp("lorder", s)) { + ih.lorder = strtoul(eq, NULL, 0); + return (&ih); + } + break; + case DB_RECNO: + if (!strcmp("flags", s)) { + rh.flags = strtoul(eq, NULL, 0); + return (&rh); + } + if (!strcmp("cachesize", s)) { + rh.cachesize = strtoul(eq, NULL, 0); + return (&rh); + } + if (!strcmp("lorder", s)) { + rh.lorder = strtoul(eq, NULL, 0); + return (&rh); + } + if (!strcmp("reclen", s)) { + rh.reclen = strtoul(eq, NULL, 0); + return (&rh); + } + if (!strcmp("bval", s)) { + rh.bval = strtoul(eq, NULL, 0); + return (&rh); + } + if (!strcmp("psize", s)) { + rh.psize = strtoul(eq, NULL, 0); + return (&rh); + } + break; + } + err("%s: unknown structure value", s); + /* NOTREACHED */ +} + +void * +rfile(name, lenp) + char *name; + size_t *lenp; +{ + struct stat sb; + void *p; + int fd; + char *np; + + for (; isspace(*name); ++name); + if ((np = index(name, '\n')) != NULL) + *np = '\0'; + if ((fd = open(name, O_RDONLY, 0)) < 0 || + fstat(fd, &sb)) + err("%s: %s\n", name, strerror(errno)); + if (sb.st_size > (off_t)SIZE_T_MAX) + err("%s: %s\n", name, strerror(E2BIG)); + if ((p = malloc((u_int)sb.st_size)) == NULL) + err("%s", strerror(errno)); + (void)read(fd, p, (int)sb.st_size); + *lenp = sb.st_size; + (void)close(fd); + return (p); +} + +void * +xmalloc(text, len) + char *text; + size_t len; +{ + void *p; + + if ((p = malloc(len)) == NULL) + err("%s", strerror(errno)); + memmove(p, text, len); + return (p); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: dbtest [-f file] [-i info] [-o file] type script\n"); + exit(1); +} + +#if __STDC__ +#include +#else +#include +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "dbtest: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/lib/libc/db/test/hash.tests/driver2.c b/lib/libc/db/test/hash.tests/driver2.c new file mode 100644 index 0000000000..2008a2899c --- /dev/null +++ b/lib/libc/db/test/hash.tests/driver2.c @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)driver2.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * Test driver, to try to tackle the large ugly-split problem. + */ + +#include +#include +#include "ndbm.h" + +int my_hash(key, len) + char *key; + int len; +{ + return(17); /* So I'm cruel... */ +} + +main(argc, argv) + int argc; +{ + DB *db; + DBT key, content; + char keybuf[2049]; + char contentbuf[2049]; + char buf[256]; + int i; + HASHINFO info; + + info.bsize = 1024; + info.ffactor = 5; + info.nelem = 1; + info.cachesize = NULL; +#ifdef HASH_ID_PROGRAM_SPECIFIED + info.hash_id = HASH_ID_PROGRAM_SPECIFIED; + info.hash_func = my_hash; +#else + info.hash = my_hash; +#endif + info.lorder = 0; + if (!(db = dbopen("bigtest", O_RDWR | O_CREAT, 0644, DB_HASH, &info))) { + sprintf(buf, "dbopen: failed on file bigtest"); + perror(buf); + exit(1); + } + srandom(17); + key.data = keybuf; + content.data = contentbuf; + bzero(keybuf, sizeof(keybuf)); + bzero(contentbuf, sizeof(contentbuf)); + for (i=1; i <= 500; i++) { + key.size = 128 + (random()&1023); + content.size = 128 + (random()&1023); +/* printf("%d: Key size %d, data size %d\n", i, key.size, + content.size); */ + sprintf(keybuf, "Key #%d", i); + sprintf(contentbuf, "Contents #%d", i); + if ((db->put)(db, &key, &content, R_NOOVERWRITE)) { + sprintf(buf, "dbm_store #%d", i); + perror(buf); + } + } + if ((db->close)(db)) { + perror("closing hash file"); + exit(1); + } + exit(0); +} + + + diff --git a/lib/libc/db/test/hash.tests/makedb.sh b/lib/libc/db/test/hash.tests/makedb.sh new file mode 100644 index 0000000000..f28e281fd1 --- /dev/null +++ b/lib/libc/db/test/hash.tests/makedb.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# +# @(#)makedb.sh 8.1 (Berkeley) 6/4/93 + +awk '{i++; print $0; print i;}' /usr/share/dict/words > WORDS +ls /bin /usr/bin /usr/ucb /etc | egrep '^(...|....|.....|......)$' | \ +sort | uniq | \ +awk '{ + printf "%s\n", $0 + for (i = 0; i < 1000; i++) + printf "%s+", $0 + printf "\n" +}' > LONG.DATA diff --git a/lib/libc/db/test/hash.tests/tcreat3.c b/lib/libc/db/test/hash.tests/tcreat3.c new file mode 100644 index 0000000000..bd125ac6ee --- /dev/null +++ b/lib/libc/db/test/hash.tests/tcreat3.c @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tcreat3.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include +#include +#include +#include + +#define INITIAL 25000 +#define MAXWORDS 25000 /* # of elements in search table */ + +char wp1[8192]; +char wp2[8192]; +main(argc, argv) +char **argv; +{ + DBT item, key; + DB *dbp; + HASHINFO ctl; + FILE *fp; + int trash; + + int i = 0; + + argv++; + ctl.hash = NULL; + ctl.bsize = atoi(*argv++); + ctl.ffactor = atoi(*argv++); + ctl.nelem = atoi(*argv++); + ctl.lorder = 0; + if (!(dbp = dbopen( "hashtest", + O_CREAT|O_TRUNC|O_RDWR, 0600, DB_HASH, &ctl))){ + /* create table */ + fprintf(stderr, "cannot create: hash table (size %d)\n", + INITIAL); + exit(1); + } + + key.data = wp1; + item.data = wp2; + while ( fgets(wp1, 8192, stdin) && + fgets(wp2, 8192, stdin) && + i++ < MAXWORDS) { +/* +* put info in structure, and structure in the item +*/ + key.size = strlen(wp1); + item.size = strlen(wp2); + +/* + * enter key/data pair into the table + */ + if ((dbp->put)(dbp, &key, &item, R_NOOVERWRITE) != NULL) { + fprintf(stderr, "cannot enter: key %s\n", + item.data); + exit(1); + } + } + + (dbp->close)(dbp); + exit(0); +} diff --git a/lib/libc/db/test/hash.tests/tdel.c b/lib/libc/db/test/hash.tests/tdel.c new file mode 100644 index 0000000000..ed3f90ad7f --- /dev/null +++ b/lib/libc/db/test/hash.tests/tdel.c @@ -0,0 +1,122 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tdel.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include +#include +#include +#include + +#define INITIAL 25000 +#define MAXWORDS 25000 /* # of elements in search table */ + +/* Usage: thash pagesize fillfactor file */ +char wp1[8192]; +char wp2[8192]; +main(argc, argv) +char **argv; +{ + DBT item, key; + DB *dbp; + HASHINFO ctl; + FILE *fp; + int stat; + + int i = 0; + + argv++; + ctl.nelem = INITIAL; + ctl.hash = NULL; + ctl.bsize = atoi(*argv++); + ctl.ffactor = atoi(*argv++); + ctl.cachesize = 1024 * 1024; /* 1 MEG */ + ctl.lorder = 0; + argc -= 2; + if (!(dbp = dbopen( NULL, O_CREAT|O_RDWR, 0400, DB_HASH, &ctl))) { + /* create table */ + fprintf(stderr, "cannot create: hash table size %d)\n", + INITIAL); + exit(1); + } + + key.data = wp1; + item.data = wp2; + while ( fgets(wp1, 8192, stdin) && + fgets(wp2, 8192, stdin) && + i++ < MAXWORDS) { +/* +* put info in structure, and structure in the item +*/ + key.size = strlen(wp1); + item.size = strlen(wp2); + +/* + * enter key/data pair into the table + */ + if ((dbp->put)(dbp, &key, &item, R_NOOVERWRITE) != NULL) { + fprintf(stderr, "cannot enter: key %s\n", + item.data); + exit(1); + } + } + + if ( --argc ) { + fp = fopen ( argv[0], "r"); + i = 0; + while ( fgets(wp1, 8192, fp) && + fgets(wp2, 8192, fp) && + i++ < MAXWORDS) { + key.size = strlen(wp1); + stat = (dbp->del)(dbp, &key, 0); + if (stat) { + fprintf ( stderr, "Error retrieving %s\n", key.data ); + exit(1); + } + } + fclose(fp); + } + (dbp->close)(dbp); + exit(0); +} diff --git a/lib/libc/db/test/hash.tests/testit b/lib/libc/db/test/hash.tests/testit new file mode 100644 index 0000000000..039457a9c4 --- /dev/null +++ b/lib/libc/db/test/hash.tests/testit @@ -0,0 +1,147 @@ +#!/bin/csh -f +# +# @(#)testit 8.1 (Berkeley) 6/4/93 +# + +echo "" +echo "PAGE FILL " +set name=WORDS + set i = 256 + foreach j ( 11 14 21 ) + thash4 $i $j 25000 65536 $name < $name + end + set i = 512 + foreach j ( 21 28 43 ) + thash4 $i $j 25000 65536 $name < $name + end + set i = 1024 + foreach j ( 43 57 85 ) + thash4 $i $j 25000 65536 $name < $name + end + set i = 2048 + foreach j ( 85 114 171 ) + thash4 $i $j 25000 65536 $name < $name + end + set i = 4096 + foreach j ( 171 228 341 ) + thash4 $i $j 25000 65536 $name < $name + end + set i = 8192 + foreach j ( 341 455 683 ) + thash4 $i $j 25000 65536 $name < $name + end + echo "PAGE FILL " + set i = 256 + foreach j ( 11 14 21 ) + echo "$i"_"$j" + tcreat3 $i $j 25000 $name < $name + tread2 65536 < $name + tverify $name < $name + tseq > /dev/null + tdel $i $j $name < $name + end + set i = 512 + foreach j ( 21 28 43 ) + echo "$i"_"$j" + tcreat3 $i $j 25000 $name < $name + tread2 65536 < $name + tverify $name < $name + tseq > /dev/null + tdel $i $j $name < $name + end + set i = 1024 + foreach j ( 43 57 85 ) + echo "$i"_"$j" + tcreat3 $i $j 25000 $name < $name + tread2 65536 < $name + tverify $name < $name + tseq > /dev/null + tdel $i $j $name < $name + end + set i = 2048 + foreach j ( 85 114 171 ) + echo "$i"_"$j" + tcreat3 $i $j 25000 $name < $name + tread2 65536 < $name + tverify $name < $name + tseq > /dev/null + tdel $i $j $name < $name + end + set i = 4096 + foreach j ( 171 228 341 ) + echo "$i"_"$j" + tcreat3 $i $j 25000 $name < $name + tread2 65536 < $name + tverify $name < $name + tseq > /dev/null + tdel $i $j $name < $name + end + set i = 8192 + foreach j ( 341 455 683 ) + echo "$i"_"$j" + tcreat3 $i $j 25000 $name < $name + tread2 65536 < $name + tverify $name < $name + tseq > /dev/null + tdel $i $j $name < $name + end +set name=LONG.DATA + set i = 1024 + foreach j ( 1 2 4 ) + echo thash4 $i $j 600 65536 $name + thash4 $i $j 600 65536 $name < $name + end + + set i = 2048 + foreach j ( 1 2 4 ) + echo thash4 $i $j 600 65536 $name + thash4 $i $j 600 65536 $name < $name + end + set i = 4096 + foreach j ( 1 2 4 ) + echo thash4 $i $j 600 65536 $name + thash4 $i $j 600 65536 $name < $name + end + set i = 8192 + foreach j ( 2 4 8 ) + echo thash4 $i $j 600 65536 $name + thash4 $i $j 600 65536 $name < $name + end + echo "PAGE FILL " + set i = 1024 + foreach j ( 1 2 4 ) + echo "$i"_"$j" + tcreat3 $i $j 600 $name < $name + tread2 65536 < $name + tverify $name < $name + tseq > /dev/null + tdel $i $j $name < $name + end + set i = 2048 + foreach j ( 1 2 4 ) + echo "$i"_"$j" + tcreat3 $i $j 600 $name < $name + tread2 65536 < $name + tverify $name < $name + tseq > /dev/null + tdel $i $j $name < $name + end + set i = 4096 + foreach j ( 1 2 4 ) + echo "$i"_"$j" + tcreat3 $i $j 600 $name < $name + tread2 65536 < $name + tverify $name < $name + tseq > /dev/null + tdel $i $j $name < $name + end + set i = 8192 + foreach j ( 2 4 8 ) + echo "$i"_"$j" + tcreat3 $i $j 600 $name < $name + tread2 65536 < $name + tverify $name < $name + tseq > /dev/null + tdel $i $j $name < $name + end +driver2 diff --git a/lib/libc/db/test/hash.tests/thash4.c b/lib/libc/db/test/hash.tests/thash4.c new file mode 100644 index 0000000000..9e344cb833 --- /dev/null +++ b/lib/libc/db/test/hash.tests/thash4.c @@ -0,0 +1,132 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)thash4.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include + +#define INITIAL 25000 +#define MAXWORDS 25000 /* # of elements in search table */ + +/* Usage: thash pagesize fillfactor file */ +char wp1[8192]; +char wp2[8192]; +main(argc, argv) +char **argv; +{ + DBT item, key, res; + DB *dbp; + HASHINFO ctl; + FILE *fp; + int stat; + time_t t; + + int i = 0; + + argv++; + ctl.hash = NULL; + ctl.bsize = atoi(*argv++); + ctl.ffactor = atoi(*argv++); + ctl.nelem = atoi(*argv++); + ctl.cachesize = atoi(*argv++); + ctl.lorder = 0; + if (!(dbp = dbopen( NULL, O_CREAT|O_RDWR, 0400, DB_HASH, &ctl))) { + /* create table */ + fprintf(stderr, "cannot create: hash table size %d)\n", + INITIAL); + fprintf(stderr, "\terrno: %d\n", errno); + exit(1); + } + + key.data = wp1; + item.data = wp2; + while ( fgets(wp1, 8192, stdin) && + fgets(wp2, 8192, stdin) && + i++ < MAXWORDS) { +/* +* put info in structure, and structure in the item +*/ + key.size = strlen(wp1); + item.size = strlen(wp2); + +/* + * enter key/data pair into the table + */ + if ((dbp->put)(dbp, &key, &item, R_NOOVERWRITE) != NULL) { + fprintf(stderr, "cannot enter: key %s\n", + item.data); + fprintf(stderr, "\terrno: %d\n", errno); + exit(1); + } + } + + if ( --argc ) { + fp = fopen ( argv[0], "r"); + i = 0; + while ( fgets(wp1, 256, fp) && + fgets(wp2, 8192, fp) && + i++ < MAXWORDS) { + + key.size = strlen(wp1); + stat = (dbp->get)(dbp, &key, &res, 0); + if (stat < 0 ) { + fprintf ( stderr, "Error retrieving %s\n", key.data ); + fprintf(stderr, "\terrno: %d\n", errno); + exit(1); + } else if ( stat > 0 ) { + fprintf ( stderr, "%s not found\n", key.data ); + fprintf(stderr, "\terrno: %d\n", errno); + exit(1); + } + } + fclose(fp); + } + dbp->close(dbp); + exit(0); +} diff --git a/lib/libc/db/test/hash.tests/tread2.c b/lib/libc/db/test/hash.tests/tread2.c new file mode 100644 index 0000000000..8f0155685f --- /dev/null +++ b/lib/libc/db/test/hash.tests/tread2.c @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tread2.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include +#include +#include +#include + +#define INITIAL 25000 +#define MAXWORDS 25000 /* # of elements in search table */ + +typedef struct { /* info to be stored */ + int num, siz; +} info; + +char wp1[8192]; +char wp2[8192]; +main(argc, argv) +char **argv; +{ + DBT item, key, res; + DB *dbp; + HASHINFO ctl; + int stat; + + int i = 0; + + ctl.nelem = INITIAL; + ctl.hash = NULL; + ctl.bsize = 64; + ctl.ffactor = 1; + ctl.cachesize = atoi(*argv++); + ctl.lorder = 0; + if (!(dbp = dbopen( "hashtest", O_RDONLY, 0400, DB_HASH, &ctl))) { + /* create table */ + fprintf(stderr, "cannot open: hash table\n" ); + exit(1); + } + + key.data = wp1; + item.data = wp2; + while ( fgets(wp1, 8192, stdin) && + fgets(wp2, 8192, stdin) && + i++ < MAXWORDS) { +/* +* put info in structure, and structure in the item +*/ + key.size = strlen(wp1); + item.size = strlen(wp2); + + stat = (dbp->get)(dbp, &key, &res,0); + if (stat < 0) { + fprintf ( stderr, "Error retrieving %s\n", key.data ); + exit(1); + } else if ( stat > 0 ) { + fprintf ( stderr, "%s not found\n", key.data ); + exit(1); + } + } + (dbp->close)(dbp); + exit(0); +} diff --git a/lib/libc/db/test/hash.tests/tseq.c b/lib/libc/db/test/hash.tests/tseq.c new file mode 100644 index 0000000000..f45700e5c0 --- /dev/null +++ b/lib/libc/db/test/hash.tests/tseq.c @@ -0,0 +1,88 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tseq.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include +#include +#include +#include + +#define INITIAL 25000 +#define MAXWORDS 25000 /* # of elements in search table */ + + +char wp[8192]; +char cp[8192]; +main(argc, argv) +char **argv; +{ + DBT item, key, res; + DB *dbp; + FILE *fp; + int stat; + + if (!(dbp = dbopen( "hashtest", O_RDONLY, 0400, DB_HASH, NULL))) { + /* create table */ + fprintf(stderr, "cannot open: hash table\n" ); + exit(1); + } + +/* +* put info in structure, and structure in the item +*/ + for ( stat = (dbp->seq) (dbp, &res, &item, 1 ); + stat == 0; + stat = (dbp->seq) (dbp, &res, &item, 0 ) ) { + + bcopy ( res.data, wp, res.size ); + wp[res.size] = 0; + bcopy ( item.data, cp, item.size ); + cp[item.size] = 0; + + printf ( "%s %s\n", wp, cp ); + } + (dbp->close)(dbp); + exit(0); +} diff --git a/lib/libc/db/test/hash.tests/tverify.c b/lib/libc/db/test/hash.tests/tverify.c new file mode 100644 index 0000000000..ac5d2f9eab --- /dev/null +++ b/lib/libc/db/test/hash.tests/tverify.c @@ -0,0 +1,107 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tverify.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include +#include +#include +#include + +#define INITIAL 25000 +#define MAXWORDS 25000 /* # of elements in search table */ + +typedef struct { /* info to be stored */ + int num, siz; +} info; + +char wp1[8192]; +char wp2[8192]; +main(argc, argv) +char **argv; +{ + DBT key, res; + DB *dbp; + HASHINFO ctl; + int trash; + int stat; + + int i = 0; + + ctl.nelem = INITIAL; + ctl.hash = NULL; + ctl.bsize = 64; + ctl.ffactor = 1; + ctl.cachesize = 1024 * 1024; /* 1 MEG */ + ctl.lorder = 0; + if (!(dbp = dbopen( "hashtest", O_RDONLY, 0400, DB_HASH, &ctl))) { + /* create table */ + fprintf(stderr, "cannot open: hash table\n" ); + exit(1); + } + + key.data = wp1; + while ( fgets(wp1, 8192, stdin) && + fgets(wp2, 8192, stdin) && + i++ < MAXWORDS) { +/* +* put info in structure, and structure in the item +*/ + key.size = strlen(wp1); + + stat = (dbp->get)(dbp, &key, &res,0); + if (stat < 0) { + fprintf ( stderr, "Error retrieving %s\n", key.data ); + exit(1); + } else if ( stat > 0 ) { + fprintf ( stderr, "%s not found\n", key.data ); + exit(1); + } + if ( memcmp ( res.data, wp2, res.size ) ) { + fprintf ( stderr, "data for %s is incorrect. Data was %s. Should have been %s\n", key.data, res.data, wp2 ); + } + } + (dbp->close)(dbp); + exit(0); +} diff --git a/lib/libc/db/test/run.test b/lib/libc/db/test/run.test new file mode 100644 index 0000000000..2e0bd8d196 --- /dev/null +++ b/lib/libc/db/test/run.test @@ -0,0 +1,616 @@ +#!/bin/sh - +# +# @(#)run.test 8.1 (Berkeley) 6/4/93 +# + +# db regression tests + +main() +{ + DICT=/usr/share/dict/words + PROG=obj/dbtest + TMP1=t1 + TMP2=t2 + TMP3=t3 + + test1 + test2 + test3 + test4 + test5 + test6 + test7 + test8 + test9 + test10 + test11 + test12 + test13 + test20 + rm -f $TMP1 $TMP2 $TMP3 + exit 0 +} + +# Take the first hundred entries in the dictionary, and make them +# be key/data pairs. +test1() +{ + printf "Test 1: btree, hash: small key, small data pairs\n" + sed 200q $DICT > $TMP1 + for type in btree hash; do + rm -f $TMP2 $TMP3 + for i in `sed 200q $DICT`; do + printf "p\nk%s\nd%s\ng\nk%s\n" $i $i $i + done > $TMP2 + $PROG -o $TMP3 $type $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test1: type %s: failed\n" $type + exit 1 + fi + done + printf "Test 1: recno: small key, small data pairs\n" + rm -f $TMP2 $TMP3 + sed 200q $DICT | + awk '{ + ++i; + printf("p\nk%d\nd%s\ng\nk%d\n", i, $0, i); + }' > $TMP2 + $PROG -o $TMP3 recno $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test1: type recno: failed\n" + exit 1 + fi +} + +# Take the first 200 entries in the dictionary, and give them +# each a medium size data entry. +test2() +{ + printf "Test 2: btree, hash: small key, medium data pairs\n" + mdata=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz + echo $mdata | + awk '{ for (i = 1; i < 201; ++i) print $0 }' > $TMP1 + for type in hash btree; do + rm -f $TMP2 $TMP3 + for i in `sed 200q $DICT`; do + printf "p\nk%s\nd%s\ng\nk%s\n" $i $mdata $i + done > $TMP2 + $PROG -o $TMP3 $type $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test2: type %s: failed\n" $type + exit 1 + fi + done + printf "Test 2: recno: small key, medium data pairs\n" + rm -f $TMP2 $TMP3 + echo $mdata | + awk '{ for (i = 1; i < 201; ++i) + printf("p\nk%d\nd%s\ng\nk%d\n", i, $0, i); + }' > $TMP2 + $PROG -o $TMP3 recno $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test2: type recno: failed\n" + exit 1 + fi +} + +# Insert the programs in /bin with their paths as their keys. +test3() +{ + printf "Test 3: hash: small key, big data pairs\n" + rm -f $TMP1 + (find /bin -type f -print | xargs cat) > $TMP1 + for type in hash; do + rm -f $TMP2 $TMP3 + for i in `find /bin -type f -print`; do + printf "p\nk%s\nD%s\ng\nk%s\n" $i $i $i + done > $TMP2 + $PROG -o $TMP3 $type $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test3: %s: page size %d: failed\n" \ + $type $psize + exit 1 + fi + done + printf "Test 3: btree: small key, big data pairs\n" + for psize in 512 16384 65536; do + printf "\tpage size %d\n" $psize + for type in btree; do + rm -f $TMP2 $TMP3 + for i in `find /bin -type f -print`; do + printf "p\nk%s\nD%s\ng\nk%s\n" $i $i $i + done > $TMP2 + $PROG -i psize=$psize -o $TMP3 $type $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test3: %s: page size %d: failed\n" \ + $type $psize + exit 1 + fi + done + done + printf "Test 3: recno: big data pairs\n" + rm -f $TMP2 $TMP3 + find /bin -type f -print | + awk '{ + ++i; + printf("p\nk%d\nD%s\ng\nk%d\n", i, $0, i); + }' > $TMP2 + for psize in 512 16384 65536; do + printf "\tpage size %d\n" $psize + $PROG -i psize=$psize -o $TMP3 recno $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test3: recno: page size %d: failed\n" $psize + exit 1 + fi + done +} + +# Do random recno entries. +test4() +{ + printf "Test 4: recno: random entries\n" + echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | + awk '{ + for (i = 37; i <= 37 + 88 * 17; i += 17) + printf("input key %d: %.*s\n", i, i % 41, $0); + for (i = 1; i <= 15; ++i) + printf("input key %d: %.*s\n", i, i % 41, $0); + for (i = 19234; i <= 19234 + 61 * 27; i += 27) + printf("input key %d: %.*s\n", i, i % 41, $0); + exit + }' > $TMP1 + rm -f TMP2 $TMP3 + cat $TMP1 | + awk 'BEGIN { + i = 37; + incr = 17; + } + { + printf("p\nk%d\nd%s\n", i, $0); + if (i == 19234 + 61 * 27) + exit; + if (i == 37 + 88 * 17) { + i = 1; + incr = 1; + } else if (i == 15) { + i = 19234; + incr = 27; + } else + i += incr; + } + END { + for (i = 37; i <= 37 + 88 * 17; i += 17) + printf("g\nk%d\n", i); + for (i = 1; i <= 15; ++i) + printf("g\nk%d\n", i); + for (i = 19234; i <= 19234 + 61 * 27; i += 27) + printf("g\nk%d\n", i); + }' > $TMP2 + $PROG -o $TMP3 recno $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test4: type recno: failed\n" + exit 1 + fi +} + +# Do reverse order recno entries. +test5() +{ + printf "Test 5: recno: reverse order entries\n" + echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | + awk ' { + for (i = 1500; i; --i) + printf("input key %d: %.*s\n", i, i % 34, $0); + exit; + }' > $TMP1 + rm -f TMP2 $TMP3 + cat $TMP1 | + awk 'BEGIN { + i = 1500; + } + { + printf("p\nk%d\nd%s\n", i, $0); + --i; + } + END { + for (i = 1500; i; --i) + printf("g\nk%d\n", i); + }' > $TMP2 + $PROG -o $TMP3 recno $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test5: type recno: failed\n" + exit 1 + fi +} + +# Do alternating order recno entries. +test6() +{ + printf "Test 6: recno: alternating order entries\n" + echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | + awk ' { + for (i = 1; i < 1200; i += 2) + printf("input key %d: %.*s\n", i, i % 34, $0); + for (i = 2; i < 1200; i += 2) + printf("input key %d: %.*s\n", i, i % 34, $0); + exit; + }' > $TMP1 + rm -f TMP2 $TMP3 + cat $TMP1 | + awk 'BEGIN { + i = 1; + even = 0; + } + { + printf("p\nk%d\nd%s\n", i, $0); + i += 2; + if (i >= 1200) { + if (even == 1) + exit; + even = 1; + i = 2; + } + } + END { + for (i = 1; i < 1200; ++i) + printf("g\nk%d\n", i); + }' > $TMP2 + $PROG -o $TMP3 recno $TMP2 + sort -o $TMP1 $TMP1 + sort -o $TMP3 $TMP3 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test6: type recno: failed\n" + exit 1 + fi +} + +# Delete cursor record +test7() +{ + printf "Test 7: btree, recno: delete cursor record\n" + echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | + awk '{ + for (i = 1; i <= 120; ++i) + printf("%05d: input key %d: %s\n", i, i, $0); + printf("%05d: input key %d: %s\n", 120, 120, $0); + printf("get failed, no such key\n"); + printf("%05d: input key %d: %s\n", 1, 1, $0); + printf("%05d: input key %d: %s\n", 2, 2, $0); + exit; + }' > $TMP1 + rm -f TMP2 $TMP3 + + for type in btree recno; do + cat $TMP1 | + awk '{ + if (i == 120) + exit; + printf("p\nk%d\nd%s\n", ++i, $0); + } + END { + printf("fR_NEXT\n"); + for (i = 1; i <= 120; ++i) + printf("s\n"); + printf("fR_CURSOR\ns\nk120\n"); + printf("r\nk120\n"); + printf("fR_NEXT\ns\n"); + printf("fR_CURSOR\ns\nk1\n"); + printf("r\nk1\n"); + printf("fR_FIRST\ns\n"); + }' > $TMP2 + $PROG -o $TMP3 recno $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test7: type $type: failed\n" + exit 1 + fi + done +} + +# Make sure that overflow pages are reused. +test8() +{ + printf "Test 8: btree, hash: repeated small key, big data pairs\n" + rm -f $TMP1 + awk 'BEGIN { + for (i = 1; i <= 10; ++i) { + printf("p\nkkey1\nD/bin/sh\n"); + printf("p\nkkey2\nD/bin/csh\n"); + if (i % 8 == 0) { + printf("c\nkkey2\nD/bin/csh\n"); + printf("c\nkkey1\nD/bin/sh\n"); + printf("e\t%d of 10 (comparison)\r\n", i); + } else + printf("e\t%d of 10 \r\n", i); + printf("r\nkkey1\nr\nkkey2\n"); + } + printf("e\n"); + printf("eend of test8 run\n"); + }' > $TMP1 + $PROG btree $TMP1 + $PROG hash $TMP1 + # No explicit test for success. +} + +# Test btree duplicate keys +test9() +{ + printf "Test 9: btree: duplicate keys\n" + echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | + awk '{ + for (i = 1; i <= 543; ++i) + printf("%05d: input key %d: %s\n", i, i, $0); + exit; + }' > $TMP1 + rm -f TMP2 $TMP3 + + for type in btree; do + cat $TMP1 | + awk '{ + if (i++ % 2) + printf("p\nkduplicatekey\nd%s\n", $0); + else + printf("p\nkunique%dkey\nd%s\n", i, $0); + } + END { + printf("o\n"); + }' > $TMP2 + $PROG -iflags=1 -o $TMP3 $type $TMP2 + sort -o $TMP3 $TMP3 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test9: type $type: failed\n" + exit 1 + fi + done +} + +# Test use of cursor flags without initialization +test10() +{ + printf "Test 10: btree, recno: test cursor flag use\n" + echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | + awk '{ + for (i = 1; i <= 20; ++i) + printf("%05d: input key %d: %s\n", i, i, $0); + exit; + }' > $TMP1 + rm -f TMP2 $TMP3 + + # Test that R_CURSOR doesn't succeed before cursor initialized + for type in btree recno; do + cat $TMP1 | + awk '{ + if (i == 10) + exit; + printf("p\nk%d\nd%s\n", ++i, $0); + } + END { + printf("fR_CURSOR\nr\nk1\n"); + printf("eR_CURSOR SHOULD HAVE FAILED\n"); + }' > $TMP2 + $PROG -o $TMP3 $type $TMP2 > /dev/null 2>&1 + if [ -s $TMP3 ] ; then + printf "Test 10: delete: R_CURSOR SHOULD HAVE FAILED\n" + exit 1 + fi + done + for type in btree recno; do + cat $TMP1 | + awk '{ + if (i == 10) + exit; + printf("p\nk%d\nd%s\n", ++i, $0); + } + END { + printf("fR_CURSOR\np\nk1\ndsome data\n"); + printf("eR_CURSOR SHOULD HAVE FAILED\n"); + }' > $TMP2 + $PROG -o $TMP3 $type $TMP2 > /dev/null 2>&1 + if [ -s $TMP3 ] ; then + printf "Test 10: put: R_CURSOR SHOULD HAVE FAILED\n" + exit 1 + fi + done +} + +# Test insert in reverse order. +test11() +{ + printf "Test 11: recno: reverse order insert\n" + echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | + awk '{ + for (i = 1; i <= 779; ++i) + printf("%05d: input key %d: %s\n", i, i, $0); + exit; + }' > $TMP1 + rm -f TMP2 $TMP3 + + for type in recno; do + cat $TMP1 | + awk '{ + if (i == 0) { + i = 1; + printf("p\nk1\nd%s\n", $0); + printf("%s\n", "fR_IBEFORE"); + } else + printf("p\nk1\nd%s\n", $0); + } + END { + printf("or\n"); + }' > $TMP2 + $PROG -o $TMP3 $type $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test11: type $type: failed\n" + exit 1 + fi + done +} + +# Take the first 20000 entries in the dictionary, reverse them, and give +# them each a small size data entry. Use a small page size to make sure +# the btree split code gets hammered. +test12() +{ + printf "Test 12: btree: lots of keys, small page size\n" + mdata=abcdefghijklmnopqrstuvwxy + echo $mdata | + awk '{ for (i = 1; i < 20001; ++i) print $0 }' > $TMP1 + for type in btree; do + rm -f $TMP2 $TMP3 + for i in `sed 20000q $DICT | rev`; do + printf "p\nk%s\nd%s\ng\nk%s\n" $i $mdata $i + done > $TMP2 + $PROG -i psize=512 -o $TMP3 $type $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test12: type %s: failed\n" $type + exit 1 + fi + done +} + +# Test different byte orders. +test13() +{ + printf "Test 13: btree, hash: differing byte orders\n" + sed 50q $DICT > $TMP1 + for order in 1234 4321; do + for type in btree hash; do + rm -f byte.file $TMP2 $TMP3 + for i in `sed 50q $DICT`; do + printf "p\nk%s\nd%s\ng\nk%s\n" $i $i $i + done > $TMP2 + $PROG -ilorder=$order -f byte.file -o $TMP3 $type $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test13: %s/%s put failed\n" $type $order + exit 1 + fi + for i in `sed 50q $DICT`; do + printf "g\nk%s\n" $i + done > $TMP2 + $PROG -ilorder=$order -f byte.file -o $TMP3 $type $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test13: %s/%s get failed\n" $type $order + exit 1 + fi + done + done + rm -f byte.file +} + +# Try a variety of bucketsizes and fill factors for hashing +test20() +{ + printf\ + "Test 20: hash: bucketsize, fill factor; nelem 25000 cachesize 65536\n" + awk 'BEGIN { + for (i = 1; i <= 10000; ++i) + printf("%.*s\n", i % 34, + "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg"); + }' > $TMP1 + sed 10000q $DICT | + awk '{ + ++i; + printf("p\nk%s\nd%.*s\n", $0, i % 34, + "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg"); + }' > $TMP2 + sed 10000q $DICT | + awk '{ + ++i; + printf("g\nk%s\n", $0); + }' >> $TMP2 + bsize=256 + for ffactor in 11 14 21; do + printf "\tbucketsize %d, fill factor %d\n" $bsize $ffactor + $PROG -o$TMP3 \ + -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ + hash $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test20: type hash:\ +bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed\n" + exit 1 + fi + done + bsize=512 + for ffactor in 21 28 43; do + printf "\tbucketsize %d, fill factor %d\n" $bsize $ffactor + $PROG -o$TMP3 \ + -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ + hash $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test20: type hash:\ +bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed\n" + exit 1 + fi + done + bsize=1024 + for ffactor in 43 57 85; do + printf "\tbucketsize %d, fill factor %d\n" $bsize $ffactor + $PROG -o$TMP3 \ + -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ + hash $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test20: type hash:\ +bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed\n" + exit 1 + fi + done + bsize=2048 + for ffactor in 85 114 171; do + printf "\tbucketsize %d, fill factor %d\n" $bsize $ffactor + $PROG -o$TMP3 \ + -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ + hash $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test20: type hash:\ +bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed\n" + exit 1 + fi + done + bsize=4096 + for ffactor in 171 228 341; do + printf "\tbucketsize %d, fill factor %d\n" $bsize $ffactor + $PROG -o$TMP3 \ + -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ + hash $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test20: type hash:\ +bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed\n" + exit 1 + fi + done + bsize=8192 + for ffactor in 341 455 683; do + printf "\tbucketsize %d, fill factor %d\n" $bsize $ffactor + $PROG -o$TMP3 \ + -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ + hash $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + printf "test20: type hash:\ +bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed\n" + exit 1 + fi + done +} + +main diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc index 27fb71cd95..f7d472e667 100644 --- a/lib/libc/gen/Makefile.inc +++ b/lib/libc/gen/Makefile.inc @@ -3,18 +3,18 @@ # gen sources .PATH: ${.CURDIR}/${MACHINE}/gen ${.CURDIR}/gen ${.CURDIR}/gen/regexp -SRCS+= alarm.c clock.c closedir.c crypt_dummy.c ctermid.c ctime.c ctype_.c \ - difftime.c disklabel.c errlst.c exec.c fnmatch.c frexp.c fstab.c \ - fts.c getcwd.c getgrent.c getlogin.c getmntinfo.c getpass.c \ - getpwent.c getsubopt.c getttyent.c getusershell.c glob.c initgroups.c \ - isatty.c isctype.c isinf.c mktemp.c nice.c nlist.c opendir.c \ - pause.c popen.c psignal.c raise.c readdir.c \ - rewinddir.c scandir.c seekdir.c setjmperr.c \ - setmode.c setrgid.c setruid.c siginterrupt.c \ - siglist.c signal.c sigsetops.c sleep.c syslog.c telldir.c \ - termios.c time.c times.c timezone.c ttyname.c ttyslot.c \ - ualarm.c unvis.c usleep.c utime.c valloc.c vis.c wait.c wait3.c \ - waitpid.c +SRCS+= alarm.c assert.c clock.c crypt.c ctermid.c ctime.c ctype_.c \ + directory.c difftime.c disklabel.c err.c errlst.c exec.c \ + fnmatch.c frexp.c fstab.c fts.c getcap.c getcwd.c getgrent.c \ + getlogin.c getmntinfo.c getpass.c getpwent.c getsubopt.c getttyent.c \ + getusershell.c glob.c infinity.c initgroups.c insque.c isatty.c \ + isctype.c isinf.c mktemp.c nice.c nlist.c pause.c popen.c psignal.c \ + raise.c setjmperr.c setmode.c setrgid.c setruid.c \ + shmat.c shmctl.c shmdt.c shmget.c \ + siginterrupt.c \ + scandir.c siglist.c signal.c sigsetops.c sleep.c syslog.c termios.c \ + time.c times.c timezone.c ttyname.c ttyslot.c ualarm.c unvis.c \ + usleep.c utime.c valloc.c vis.c wait.c wait3.c waitpid.c # gen/regexp sources SRCS+= regerror.c regexp.c regsub.c @@ -27,7 +27,7 @@ SRCS+= adddf3.s addsf3.s ashlsi3.s ashrsi3.s cmpdf2.s cmpsf2.s divdf3.s \ negdf2.s negsf2.s saveregs.c subdf3.s subsf3.s truncdfsf2.s udivsi3.s \ umodsi3.s umulsi3.s .elif (${MACHINE} == "i386") -SRCS+= _setjmp.s alloca.s fabs.s ldexp.c modf.s setjmp.s +SRCS+= _setjmp.s alloca.s fabs.s ldexp.c modf.s setjmp.s sigsetjmp.s SRCS+= divsi3.s fixdfsi.s fixunsdfsi.s udivsi3.s .elif (${MACHINE} == "tahoe") CFLAGS+=-I/sys @@ -38,17 +38,22 @@ SRCS+= _setjmp.s alloca.s fabs.s ldexp.s modf.s setjmp.s SRCS+= udiv.s urem.s .endif -MAN3+= alarm.0 clock.0 crypt.0 ctermid.0 ctime.0 ctype.0 directory.0 \ - exec.0 fnmatch.0 frexp.0 fts.0 getcwd.0 getdiskbyname.0 getfsent.0 \ - getgrent.0 getmntinfo.0 getpass.0 getpwent.0 getsubopt.0 \ - getttyent.0 getusershell.0 glob.0 initgroups.0 isalnum.0 \ - isalpha.0 isascii.0 iscntrl.0 isdigit.0 isgraph.0 isinf.0 \ - islower.0 isprint.0 ispunct.0 isspace.0 isupper.0 isxdigit.0 \ - ldexp.0 modf.0 nice.0 nlist.0 pause.0 popen.0 psignal.0 \ - raise.0 regexp.0 scandir.0 setjmp.0 setmode.0 setuid.0 \ - siginterrupt.0 signal.0 sigsetops.0 sleep.0 syslog.0 time.0 \ - times.0 timezone.0 tolower.0 toupper.0 ttyname.0 tzset.0 \ - ualarm.0 unvis.0 usleep.0 utime.0 valloc.0 vis.0 +MAN3+= gen/alarm.3 gen/clock.3 gen/crypt.3 gen/ctermid.3 gen/ctime.3 \ + gen/ctype.3 gen/directory.3 gen/err.3 gen/exec.3 gen/fnmatch.3 \ + gen/frexp.3 gen/fts.3 gen/getcwd.3 gen/getcap.3 gen/getdiskbyname.3 \ + gen/getfsent.3 gen/getgrent.3 gen/getmntinfo.3 gen/getpass.3 \ + gen/getpwent.3 gen/getsubopt.3 gen/getttyent.3 gen/getusershell.3 \ + gen/glob.3 gen/initgroups.3 gen/insque.3 gen/isalnum.3 gen/isalpha.3 \ + gen/isascii.3 gen/iscntrl.3 gen/isdigit.3 gen/isgraph.3 gen/isinf.3 \ + gen/islower.3 gen/isprint.3 gen/ispunct.3 gen/isspace.3 \ + gen/isupper.3 gen/isxdigit.3 gen/ldexp.3 gen/modf.3 gen/nice.3 \ + gen/nlist.3 gen/pause.3 gen/popen.3 gen/psignal.3 gen/raise.3 \ + gen/regexp.3 gen/scandir.3 gen/setjmp.3 gen/setmode.3 gen/setuid.3 \ + gen/siginterrupt.3 gen/signal.3 gen/sigsetops.3 gen/sleep.3 \ + gen/syslog.3 gen/tcsendbreak.3 gen/time.3 gen/times.3 \ + gen/timezone.3 gen/tolower.3 gen/toupper.3 gen/ttyname.3 \ + gen/tzset.3 gen/ualarm.3 gen/unvis.3 gen/usleep.3 gen/utime.3 \ + gen/valloc.3 gen/vis.3 MLINKS= crypt.3 encrypt.3 crypt.3 setkey.3 MLINKS+=ctime.3 asctime.3 ctime.3 difftime.3 ctime.3 gmtime.3 \ @@ -56,10 +61,15 @@ MLINKS+=ctime.3 asctime.3 ctime.3 difftime.3 ctime.3 gmtime.3 \ MLINKS+=directory.3 closedir.3 directory.3 dirfd.3 directory.3 opendir.3 \ directory.3 readdir.3 directory.3 rewinddir.3 directory.3 seekdir.3 \ directory.3 telldir.3 +MLINKS+=err.3 verr.3 err.3 errx.3 err.3 verrx.3 err.3 warn.3 err.3 vwarn.3 \ + err.3 warnx.3 err.3 vwarnx.3 MLINKS+=exec.3 execl.3 exec.3 execle.3 exec.3 execlp.3 exec.3 execv.3 \ exec.3 execvp.3 -MLINKS+=isinf.3 isnan.3 MLINKS+=getcwd.3 getwd.3 +MLINKS+=getcap.3 cgetent.3 getcap.3 cgetset.3 getcap.3 cgetmatch.3 \ + getcap.3 cgetcap.3 getcap.3 cgetnum.3 getcap.3 cgetstr.3 \ + getcap.3 cgetustr.3 getcap.3 cgetfirst.3 getcap.3 cgetnext.3 \ + getcap.3 cgetclose.3 MLINKS+=getfsent.3 endfsent.3 getfsent.3 getfsfile.3 getfsent.3 getfsspec.3 \ getfsent.3 getfstype.3 getfsent.3 setfsent.3 MLINKS+=getgrent.3 endgrent.3 getgrent.3 setgroupent.3 getgrent.3 getgrgid.3 \ @@ -69,13 +79,15 @@ MLINKS+=getpwent.3 endpwent.3 getpwent.3 setpassent.3 getpwent.3 getpwnam.3 \ MLINKS+=getttyent.3 endttyent.3 getttyent.3 getttynam.3 getttyent.3 setttyent.3 MLINKS+=getusershell.3 endusershell.3 getusershell.3 setusershell.3 MLINKS+=glob.3 globfree.3 +MLINKS+=insque.3 remque.3 +MLINKS+=isinf.3 isnan.3 MLINKS+=popen.3 pclose.3 MLINKS+=psignal.3 sys_siglist.3 MLINKS+=regexp.3 regcomp.3 regexp.3 regexec.3 regexp.3 regsub.3 \ regexp.3 regerror.3 MLINKS+=scandir.3 alphasort.3 MLINKS+=setjmp.3 _longjmp.3 setjmp.3 _setjmp.3 setjmp.3 longjmp.3 \ - setjmp.3 longjmperr.3 setjmp.3 longjmperror.3 + setjmp.3 sigsetjmp.3 setjmp.3 siglongjmp.3 setjmp.3 longjmperror.3 MLINKS+=setmode.3 getmode.3 MLINKS+=setuid.3 setegid.3 setuid.3 seteuid.3 setuid.3 setgid.3 \ setuid.3 setrgid.3 setuid.3 setruid.3 @@ -84,6 +96,9 @@ MLINKS+=sigsetops.3 sigemptyset.3 sigsetops.3 sigfillset.3 \ sigsetops.3 sigismember.3 MLINKS+=syslog.3 closelog.3 syslog.3 openlog.3 syslog.3 setlogmask.3 \ syslog.3 vsyslog.3 +MLINKS+=tcsendbreak.3 tcdrain.3 tcsendbreak.3 tcflush.3 tcsendbreak.3 tcflow.3 + + MLINKS+=ttyname.3 isatty.3 ttyname.3 ttyslot.3 MLINKS+=tzset.3 tzsetwall.3 diff --git a/lib/libc/gen/README.crypt b/lib/libc/gen/README.crypt new file mode 100644 index 0000000000..75d7651ddb --- /dev/null +++ b/lib/libc/gen/README.crypt @@ -0,0 +1,35 @@ +I have implemented the little "scrambler" algorithm I spoke of a few days +ago. Below is a lib/other/crypt.c function (compressed-uuencoded) that does +the trick. As an example, here's a little comparison between Minix 1.5 crypt +and mine. Four crypted values are shown: + +Salt Password Minix crypt() My crypt() + -- ----------- ------------- ------------- + 00 "password" 00|0WFy1B8Tz| 001dcsc5Sh3TQ + 00 "Password" 00|0WFy1B8Tv| 00rTQYCDHmeRY + 00 "password " 00|0WFy1B8Tz| 00pzqMhV9et9M + 03 "password" 03z||meFy1B8T 03nJJegU4/CEg + +You (of course) immediately notice that using upper-lowercase does not +significatively change the password, nor changing the salt value, and that +characters after the 8th are entirely unsignificant. In mine, however, +everything is significant, even the 255th character of the password +(basically, the 637 first characters are significant and 512 more have the +potential of being used!!!). This is because of the CRC property of +"spreading" significantly bits over a large place; every bit in the result +depend of nearly every bit in the source in some complicated manner. + +Thos Sumner pointed out two algorithms for crypting that were posted recently, +but since I do not have the full news (I get comp.os.minix thru the Bitnet +mailing list), I don't know what to do with them. Anyway, my crypt() is +perfectly compatible with Unix/Minix operation, so it's better in that respect. + +He also suggested using a machine-dependant (by machine, I mean SITE-dependant) +info to discourage theft of /etc/passwd files for decryption at leisure. Is +there such an info available on Minix in a standard way? Anyway, you'll notice +that, even using mixs of chars, shorts, longs and 6-bits blocks the results +are the same on 68000 and xxx86, even with the different byte ordering (this +was not intended :-) + +Vincent Archer + diff --git a/lib/libc/gen/assert.c b/lib/libc/gen/assert.c new file mode 100644 index 0000000000..b3ec55b5f2 --- /dev/null +++ b/lib/libc/gen/assert.c @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)assert.c 5.1 (Berkeley) 4/23/92"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include + +void +__assert(file, line, failedexpr) + const char *file, *failedexpr; + int line; +{ + (void)fprintf(stderr, + "assertion \"%s\" failed: file \"%s\", line %d\n", + failedexpr, file, line); + abort(); + /* NOTREACHED */ +} diff --git a/lib/libc/gen/closedir.c b/lib/libc/gen/closedir.c deleted file mode 100644 index d87d816548..0000000000 --- a/lib/libc/gen/closedir.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 1983 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)closedir.c 5.9 (Berkeley) 2/23/91"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include -#include -#include - -/* - * close a directory. - */ -int -closedir(dirp) - register DIR *dirp; -{ - int fd; - - fd = dirp->dd_fd; - dirp->dd_fd = -1; - dirp->dd_loc = 0; - (void)free((void *)dirp->dd_buf); - (void)free((void *)dirp); - return(close(fd)); -} diff --git a/lib/libc/gen/crypt.c b/lib/libc/gen/crypt.c new file mode 100644 index 0000000000..73892ee6ac --- /dev/null +++ b/lib/libc/gen/crypt.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Tom Truscott. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +/* from static char sccsid[] = "@(#)crypt.c 5.11 (Berkeley) 6/25/91"; */ +static char rcsid[] = "$Header: /a/cvs/386BSD/src/lib/libc/gen/crypt.c,v 1.5 1993/08/27 22:04:55 nate Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +/* + * UNIX password, and DES, encryption. + * + * since this is non-exportable, this is just a dummy. if you want real + * encryption, make sure you've got libcrypt.a around. + */ + +#ifndef DES +#define SCRAMBLE /* Don't leave them in plaintext */ +#endif + +#ifndef SCRAMBLE +static char cryptresult[1+4+4+11+1]; /* "encrypted" result */ + +char * +crypt(key, setting) + register const char *key; + register const char *setting; +{ + fprintf(stderr, "WARNING! crypt(3) not present in the system!\n"); + strncpy(cryptresult, key, sizeof cryptresult); + cryptresult[sizeof cryptresult - 1] = '\0'; + return (cryptresult); +} + +#else + +char * +crypt(pw, salt) + register const char *pw; + register const char *salt; +{ + static char password[14]; + long matrix[128], *m, vector[2]; + char a, b, *p; + int i, value; + unsigned short crc; + unsigned long t; + + /* Ugly hack, but I'm too lazy to find the real problem - NW */ + bzero(matrix, 128 * sizeof(long)); + + if (salt[0]) { + a = salt[0]; + if (salt[1]) + b = salt[1]; + else + b = a; + } else + a = b = '0'; + password[0] = a; + password[1] = b; + if (a > 'Z') + a -= 6; + if (a > '9') + a -= 7; + if (b > 'Z') + b -= 6; + if (b > '9') + b -= 7; + a -= '.'; + b -= '.'; + value = (a | (b << 6)) & 07777; + + crc = value; + value += 1000; + b = 0; + p = (char *)pw; + while (value--) { + if (crc & 0x8000) + crc = (crc << 1) ^ 0x1021; + else + crc <<= 1; + if (!b) { + b = 8; + if (!(i = *p++)) { + p = (char *)pw; + i = *p++; + } + } + if (i & 0x80) + crc ^= 1; + i <<= 1; + b--; + } + + m = matrix; + matrix[0] = 0; + a = 32; + for (value = 07777; value >= 0; value--) { + *m <<= 1; + if (crc & 0x8000) { + *m |= 1; + crc = (crc << 1) ^ 0x1021; + } else + crc <<= 1; + if (!b) { + b = 8; + if (!(i = *p++)) { + p = (char *)pw; + i = *p++; + } + } + if (i & 0x80) + crc ^= 1; + i <<= 1; + b--; + if (!(a--)) { + a = 32; + *++m = 0; + } + } + + vector[0] = 0; + vector[1] = 0; + p = (char *) vector; + for (i = 0; i < 7; i++) + if (pw[i]) + *p++ = pw[i]; + else + break; + + p = password + 2; + a = 6; + m = matrix; + *p = 0; + for (i = 077; i >= 0; i--) { + t = *m++; + t = t ^ *m++; + t = t ^ vector[0]; + t = t ^ vector[1]; + b = 0; + while (t) { + if (t & 1) + b = 1 - b; + t >>= 1; + } + a--; + if (b) + *p |= 1 << a; + if (!a) { + a = 6; + *++p = 0; + } + } + + for (i = 2; i < 13; i++) { + password[i] += '.'; + if (password[i] > '9') + password[i] += 7; + if (password[i] > 'Z') + password[i] += 6; + } + password[13] = 0; + + return password; +} +#endif + +des_setkey(key) + register const char *key; +{ + fprintf(stderr, "WARNING! des_setkey(3) not present in the system!\n"); + return (0); +} + +des_cipher(in, out, salt, num_iter) + const char *in; + char *out; + long salt; + int num_iter; +{ + fprintf(stderr, "WARNING! des_cipher(3) not present in the system!\n"); + bcopy(in, out, 8); + return (0); +} + +setkey(key) + register const char *key; +{ + fprintf(stderr, "WARNING! setkey(3) not present in the system!\n"); + return (0); +} + +encrypt(block, flag) + register char *block; + int flag; +{ + fprintf(stderr, "WARNING! encrypt(3) not present in the system!\n"); + return (0); +} diff --git a/lib/libc/gen/crypt_dummy.c b/lib/libc/gen/crypt_dummy.c deleted file mode 100644 index 55bc7412f1..0000000000 --- a/lib/libc/gen/crypt_dummy.c +++ /dev/null @@ -1,5 +0,0 @@ -char * -crypt (k, s) char *k,*s; { - write(2,"Crypt not present in system\n", 29); - return(k); -} diff --git a/lib/libc/gen/directory.c b/lib/libc/gen/directory.c new file mode 100644 index 0000000000..33076bd3d2 --- /dev/null +++ b/lib/libc/gen/directory.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)closedir.c 5.9 (Berkeley) 2/23/91"; +#endif /* LIBC_SCCS and not lint */ + +/* + * One of these structures is malloced to describe the current directory + * position each time telldir is called. It records the current magic + * cookie returned by getdirentries and the offset within the buffer + * associated with that return value. + */ +struct ddloc { + struct ddloc *loc_next;/* next structure in list */ + long loc_index; /* key associated with structure */ + long loc_seek; /* magic cookie returned by getdirentries */ + long loc_loc; /* offset of entry in buffer */ +}; + +static long dd_loccnt = 0; /* Index of entry for sequential telldir's */ + +#include +#include +#include +#include +#include +#include + +/* + * close a directory. + */ +int +closedir(dirp) + register DIR *dirp; +{ + struct ddloc *ptr, *nextptr; + int fd; + + fd = dirp->dd_fd; + dirp->dd_fd = -1; + dirp->dd_loc = 0; + for (ptr = (struct ddloc*)dirp->dd_ddloc; ptr; ptr = nextptr) { + nextptr = ptr->loc_next; + free((void *)ptr); + } + free((void *)dirp->dd_buf); + free((void *)dirp); + return(close(fd)); +} + +/* + * open a directory. + */ +DIR * +opendir(name) + const char *name; +{ + register DIR *dirp; + register int fd; + + if ((fd = open(name, 0)) == -1) + return NULL; + if (fcntl(fd, F_SETFD, 1) == -1 || + (dirp = (DIR *)malloc(sizeof(DIR))) == NULL) { + close (fd); + return NULL; + } + /* + * If CLSIZE is an exact multiple of DIRBLKSIZ, use a CLSIZE + * buffer that it cluster boundary aligned. + * Hopefully this can be a big win someday by allowing page trades + * to user space to be done by getdirentries() + */ + if ((CLSIZE % DIRBLKSIZ) == 0) { + dirp->dd_buf = malloc(CLSIZE); + dirp->dd_len = CLSIZE; + } else { + dirp->dd_buf = malloc(DIRBLKSIZ); + dirp->dd_len = DIRBLKSIZ; + } + if (dirp->dd_buf == NULL) { + close (fd); + free(dirp); + return NULL; + } + dirp->dd_fd = fd; + dirp->dd_loc = 0; + dirp->dd_seek = 0; + dirp->dd_ddloc = NULL; + + return dirp; +} + +/* + * get next entry in a directory. + */ +struct dirent * +readdir(dirp) + register DIR *dirp; +{ + register struct dirent *dp; + + for (;;) { + if (dirp->dd_loc == 0) { + dirp->dd_size = getdirentries(dirp->dd_fd, + dirp->dd_buf, dirp->dd_len, &dirp->dd_seek); + if (dirp->dd_size <= 0) + return NULL; + } + if (dirp->dd_loc >= dirp->dd_size) { + dirp->dd_loc = 0; + continue; + } + dp = (struct dirent *)(dirp->dd_buf + dirp->dd_loc); + if ((int)dp & 03) /* bogus pointer check */ + return NULL; + if (dp->d_reclen <= 0 || + dp->d_reclen > dirp->dd_len + 1 - dirp->dd_loc) + return NULL; + dirp->dd_loc += dp->d_reclen; + if (dp->d_ino == 0) + continue; + return (dp); + } +} + +void +rewinddir(dirp) + DIR *dirp; +{ + (void)lseek(dirp->dd_fd, 0, 0); + dirp->dd_seek = 0; + dirp->dd_loc = 0; +} + +/* + * Seek to an entry in a directory. + * _seekdir is in telldir.c so that it can share opaque data structures. + */ +void +seekdir(dirp, loc) + DIR *dirp; + long loc; +{ + register struct ddloc **prevlp; + register struct ddloc *lp; + struct dirent *dp; + extern long lseek(); + + prevlp = (struct ddloc **)&(dirp->dd_ddloc); + lp = *prevlp; + while (lp != NULL) { + if (lp->loc_index == loc) + break; + prevlp = &lp->loc_next; + lp = lp->loc_next; + } + if (lp) { + if (lp->loc_seek != dirp->dd_seek) { + if (lseek(dirp->dd_fd, lp->loc_seek, 0) == -1) { + *prevlp = lp->loc_next; + free(lp); + return; + } + dirp->dd_seek = lp->loc_seek; + dirp->dd_loc = 0; + while (dirp->dd_loc < lp->loc_loc) { + if (!(dp = readdir(dirp))) { + *prevlp = lp->loc_next; + free(lp); + return; + } + } + } + } +} + +/* + * return a pointer into a directory + */ +long +telldir(dirp) + const DIR *dirp; +{ + register struct ddloc *lp, **fakeout; + + if (lp = (struct ddloc *)malloc(sizeof(struct ddloc))) { + lp->loc_index = dd_loccnt++; + lp->loc_seek = dirp->dd_seek; + lp->loc_loc = dirp->dd_loc; + lp->loc_next = dirp->dd_ddloc; + + /* Compiler won't let us change anything pointed to by db directly */ + /* So we fake to the left and do it anyway */ + /* Wonder if the compile optomizes it to the correct solution */ + fakeout = (struct ddloc **)&(dirp->dd_ddloc); + *fakeout = lp; + + return (lp->loc_index); + } + return (-1); +} + diff --git a/lib/libc/gen/err.3 b/lib/libc/gen/err.3 new file mode 100644 index 0000000000..44ca0942c1 --- /dev/null +++ b/lib/libc/gen/err.3 @@ -0,0 +1,129 @@ +.\" Copyright (c) 1993 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)err.3 5.2 (Berkeley) 3/19/93 +.\" +.Dd "March 19, 1993" +.Dt ERR 3 +.Os BSD 4 +.Sh NAME +.Nm err , +.Nm verr , +.Nm errx , +.Nm verrx , +.Nm warn , +.Nm vwarn , +.Nm warnx , +.Nm vwarnx +.Nd formatted error messages +.Sh SYNOPSIS +.Fd #include +.Ft void +.Fn err "int eval" "const char *fmt" "..." +.Ft void +.Fn verr "int eval" "const char *fmt" "va_list args" +.Ft void +.Fn errx "int eval" "const char *fmt" "..." +.Ft void +.Fn verrx "int eval" "const char *fmt" "va_list args" +.Ft void +.Fn warn "const char *fmt" "..." +.Ft void +.Fn vwarn "const char *fmt" "va_list args" +.Ft void +.Fn warnx "const char *fmt" "..." +.Ft void +.Fn vwarnx "const char *fmt" "va_list args" +.Sh DESCRIPTION +The +.Fn err +and +.Fn warn +family of functions display a formatted error message on the standard +error output. +In all cases, the last component of the program name, a colon character, +and a space are output. +If the +.Va fmt +argument is not NULL, the formatted error message, a colon character, +and a space are output. +In the case of the +.Fn err , +.Fn verr , +.Fn warn , +and +.Fn vwarn +functions, the error message string affiliated with the current value of +the global variable +.Va errno +is output. +In all cases, the output is followed by a newline character. +.Pp +The +.Fn err , +.Fn verr , +.Fn errx , +and +.Fn verrx +functions do not return, but exit with the value of the argument +.Fa eval . +.Sh EXAMPLES +Display the current errno information string and exit: +.Bd -literal -offset indent +if ((p = malloc(size)) == NULL) + err(1, NULL); +if ((fd = open(file_name, O_RDONLY, 0)) == -1) + err(1, "%s", file_name); +.Ed +.Pp +Display an error message and exit: +.Bd -literal -offset indent +if (tm.tm_hour < START_TIME) + errx(1, "too early, wait until %s", start_time_string); +.Ed +.Pp +Warn of an error: +.Bd -literal -offset indent +if ((fd = open(raw_device, O_RDONLY, 0)) == -1) + warnx("%s: %s: trying the block device", + raw_device, strerror(errno)); +if ((fd = open(block_device, O_RDONLY, 0)) == -1) + err(1, "%s", block_device); +.Ed +.Sh SEE ALSO +.Xr strerror 3 +.Sh HISTORY +The +.Fn err +and +.Fn warn +functions are +.Ud . diff --git a/lib/libc/gen/err.c b/lib/libc/gen/err.c new file mode 100644 index 0000000000..3c7d03ae6f --- /dev/null +++ b/lib/libc/gen/err.c @@ -0,0 +1,188 @@ +/*- + * Copyright (c) 1993 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)err.c 5.2 (Berkeley) 3/19/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include +#include + +#ifdef __STDC__ +#include +#else +#include +#endif + +extern char *__progname; /* Program name, from crt0. */ + +volatile void +#ifdef __STDC__ +err(int eval, const char *fmt, ...) +#else +err(eval, fmt, va_alist) + int eval; + const char *fmt; + va_dcl +#endif +{ + _VA_LIST_ ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + verr(eval, fmt, ap); + va_end(ap); +} + +volatile void +verr(eval, fmt, ap) + int eval; + const char *fmt; + _VA_LIST_ ap; +{ + int sverrno; + + sverrno = errno; + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) { + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, ": "); + } + (void)fprintf(stderr, "%s\n", strerror(sverrno)); + exit(eval); +} + +volatile void +#if __STDC__ +errx(int eval, const char *fmt, ...) +#else +errx(eval, fmt, va_alist) + int eval; + const char *fmt; + va_dcl +#endif +{ + _VA_LIST_ ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + verrx(eval, fmt, ap); + va_end(ap); +} + +volatile void +verrx(eval, fmt, ap) + int eval; + const char *fmt; + _VA_LIST_ ap; +{ + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, "\n"); + exit(eval); +} + +void +#if __STDC__ +warn(const char *fmt, ...) +#else +warn(fmt, va_alist) + int eval; + const char *fmt; + va_dcl +#endif +{ + _VA_LIST_ ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vwarn(fmt, ap); + va_end(ap); +} + +void +vwarn(fmt, ap) + const char *fmt; + _VA_LIST_ ap; +{ + int sverrno; + + sverrno = errno; + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) { + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, ": "); + } + (void)fprintf(stderr, "%s\n", strerror(sverrno)); +} + +void +#ifdef __STDC__ +warnx(const char *fmt, ...) +#else +warnx(fmt, va_alist) + int eval; + const char *fmt; + va_dcl +#endif +{ + _VA_LIST_ ap; +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vwarnx(fmt, ap); + va_end(ap); +} + +void +vwarnx(fmt, ap) + const char *fmt; + _VA_LIST_ ap; +{ + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, "\n"); +} diff --git a/lib/libc/gen/fnmatch.3 b/lib/libc/gen/fnmatch.3 index 0b733d2699..2d967d2d1b 100644 --- a/lib/libc/gen/fnmatch.3 +++ b/lib/libc/gen/fnmatch.3 @@ -31,16 +31,16 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)fnmatch.3 5.3 (Berkeley) 4/19/91 +.\" @(#)fnmatch.3 5.4 (Berkeley) 6/28/92 .\" -.Dd April 19, 1991 +.Dd June 28, 1992 .Dt FNMATCH 3 .Os .Sh NAME .Nm fnmatch .Nd match filename or pathname .Sh SYNOPSIS -.Fd #include +.Fd #include .Ft int .Fn fnmatch "const char *pattern" "const char *string" "int flags" .Sh DESCRIPTION @@ -66,33 +66,45 @@ is the bitwise inclusive .Tn OR of any of the following constants, which are defined in the include file -.Pa unistd.h . +.Pa fnmatch.h . .Bl -tag -width FNM_PATHNAME +.It Dv FNM_NOESCAPE +Normally, every occurrence of a backslash +.Pq Ql \e +followed by a character in +.Fa pattern +is replaced by that character. +This is done to negate any special meaning for the character. +If the +.Dv FNM_NOESCAPE +flag is set, a backslash character is treated as an ordinary character. .It Dv FNM_PATHNAME Slash characters in .Fa string must be explicitly matched by slashes in .Fa pattern . If this flag is not set, then slashes are treated as regular characters. -.It Dv FNM_QUOTE -Every occurrence of a backslash -.Pq Ql \e -followed by a character in -.Fa pattern -is replaced by that character. -This is done to negate any special meaning for the character. +.It Dv FNM_PERIOD +Leading periods in strings match periods in patterns. +The definition of ``leading'' is related to the specification of +.Dv FNM_PATHNAME. +A period is always ``leading'' if it is the first character in +.Ar string . +Additionally, if +.Dv FNM_PATHNAME +is set, +a period is ``leading'' if it immediately follows a slash. +.Em "This flag is not currently implemented." .El .Sh RETURN VALUES -If +The +.Fn fnmatch +function returns zero if .Fa string matches the pattern specified by .Fa pattern , -then -.Fn fnmatch -returns zero. -Otherwise, -.Fn fnmatch -returns nonzero. +otherwise, it returns the value +.Dv FNM_NOMATCH . .Sh SEE ALSO .Xr sh 1 , .Xr glob 3 , @@ -104,7 +116,12 @@ The function is .Ud . .Sh BUGS -Quotes and slashes in range patterns are not handled correctly. +Quotes and slashes in range patterns are not handled correctly by +this implementation. +.Pp +The +.Dv FNM_PERIOD +flag is not implemented. .Pp The pattern .Ql * diff --git a/lib/libc/gen/fnmatch.c b/lib/libc/gen/fnmatch.c index 2e0004d71a..7b54c3b725 100644 --- a/lib/libc/gen/fnmatch.c +++ b/lib/libc/gen/fnmatch.c @@ -35,112 +35,116 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)fnmatch.c 5.4 (Berkeley) 2/23/91"; +static char sccsid[] = "@(#)fnmatch.c 5.6 (Berkeley) 6/28/92"; #endif /* LIBC_SCCS and not lint */ /* - * Function fnmatch() as proposed in Posix 1003.2 B.6 (rev. 9). + * Function fnmatch() as proposed in POSIX 1003.2 B.6 (D11.2). * Compares a filename or pathname to a pattern. */ -#include +#include #include #define EOS '\0' -static char * -rangematch(pattern, test) - register char *pattern, test; -{ - register char c, c2; - int negate, ok; - - if (negate = (*pattern == '!')) - ++pattern; - - /* - * TO DO: quoting - */ - - for (ok = 0; (c = *pattern++) != ']';) { - if (c == EOS) - return(NULL); /* illegal pattern */ - if (*pattern == '-' && (c2 = pattern[1]) != EOS && c2 != ']') { - if (c <= test && test <= c2) - ok = 1; - pattern += 2; - } - else if (c == test) - ok = 1; - } - return(ok == negate ? NULL : pattern); -} +static const char *rangematch __P((const char *, int)); fnmatch(pattern, string, flags) - register const char *pattern; - register const char *string; + register const char *pattern, *string; int flags; { register char c; - char test, *rangematch(); + char test; for (;;) switch (c = *pattern++) { case EOS: - return(*string == EOS); + return (*string == EOS ? 0 : FNM_NOMATCH); case '?': if ((test = *string++) == EOS || test == '/' && flags & FNM_PATHNAME) - return(0); + return (FNM_NOMATCH); break; case '*': c = *pattern; - /* collapse multiple stars */ + /* Collapse multiple stars. */ while (c == '*') c = *++pattern; - /* optimize for pattern with * at end or before / */ + /* Optimize for pattern with * at end or before /. */ if (c == EOS) if (flags & FNM_PATHNAME) - return(!index(string, '/')); + return (index(string, '/') == NULL ? + 0 : FNM_NOMATCH); else - return(1); + return (0); else if (c == '/' && flags & FNM_PATHNAME) { if ((string = index(string, '/')) == NULL) - return(0); + return (FNM_NOMATCH); break; } - /* general case, use recursion */ + /* General case, use recursion. */ while ((test = *string) != EOS) { - if (fnmatch(pattern, string, flags)) - return(1); + if (!fnmatch(pattern, string, flags)) + return (0); if (test == '/' && flags & FNM_PATHNAME) break; ++string; } - return(0); + return (FNM_NOMATCH); case '[': if ((test = *string++) == EOS || test == '/' && flags & FNM_PATHNAME) - return(0); + return (FNM_NOMATCH); if ((pattern = rangematch(pattern, test)) == NULL) - return(0); + return (FNM_NOMATCH); break; case '\\': - if (flags & FNM_QUOTE) { + if (!(flags & FNM_NOESCAPE)) { if ((c = *pattern++) == EOS) { c = '\\'; --pattern; } if (c != *string++) - return(0); + return (FNM_NOMATCH); break; } /* FALLTHROUGH */ default: if (c != *string++) - return(0); + return (FNM_NOMATCH); break; } + /* NOTREACHED */ +} + +static const char * +rangematch(pattern, test) + register const char *pattern; + register int test; +{ + register char c, c2; + int negate, ok; + + if (negate = (*pattern == '!')) + ++pattern; + + /* + * XXX + * TO DO: quoting + */ + for (ok = 0; (c = *pattern++) != ']';) { + if (c == EOS) + return (NULL); /* Illegal pattern. */ + if (*pattern == '-' && (c2 = pattern[1]) != EOS && c2 != ']') { + if (c <= test && test <= c2) + ok = 1; + pattern += 2; + } + else if (c == test) + ok = 1; + } + return (ok == negate ? NULL : pattern); } diff --git a/lib/libc/gen/fts.3 b/lib/libc/gen/fts.3 index 17adb13555..746d8e2164 100644 --- a/lib/libc/gen/fts.3 +++ b/lib/libc/gen/fts.3 @@ -29,9 +29,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)fts.3 5.11 (Berkeley) 7/31/91 +.\" @(#)fts.3 5.21 (Berkeley) 3/5/92 .\" -.Dd July 31, 1991 +.Dd March 5, 1992 .Dt FTS 3 .Os .Sh NAME @@ -42,11 +42,11 @@ .Fd #include .Fd #include .Ft FTS * -.Fn fts_open "char * const *path_argv" "int options" "int *compar(const FTSENT *, const FTSENT *)" +.Fn fts_open "char * const *path_argv" "int options" "int *compar(const FTSENT **, const FTSENT **)" .Ft FTSENT * .Fn fts_read "FTS *ftsp" .Ft FTSENT * -.Fn fts_children "FTS *ftsp" +.Fn fts_children "FTS *ftsp" "int options" .Ft int .Fn fts_set "FTS ftsp" "FTSENT *f" "int options" .Ft int @@ -57,13 +57,12 @@ The functions are provided for traversing .Tn UNIX file hierarchies. -.Pp -The simple overview is that the +A simple overview is that the .Fn fts_open -function returns a ``handle'' on a file hierarchy, which is supplied to +function returns a ``handle'' on a file hierarchy, which is then supplied to the other .Nm fts -functions to determine which hierarchy they operate on. +functions. The function .Fn fts_read returns a pointer to a structure describing one of the files in the file @@ -84,7 +83,7 @@ Two structures are defined (and typedef'd) in the include file .Aq Pa fts.h . The first is .Fa FTS , -the structure that represents the file hierarchy stream. +the structure that represents the file hierarchy itself. The second is .Fa FTSENT , the structure that represents a file in the file @@ -110,11 +109,13 @@ typedef struct _ftsent { char *fts_name; /* file name */ short fts_namelen; /* strlen(fts_name) */ short fts_level; /* depth (\-1 to N) */ + int fts_error; /* file errno */ long fts_number; /* local numeric value */ void *fts_pointer; /* local address value */ struct ftsent *fts_parent; /* parent directory */ - struct ftsent *fts_link; /* cycle or next file structure */ - struct stat fts_statb; /* stat(2) information */ + struct ftsent *fts_link; /* next file structure */ + struct ftsent *fts_cycle; /* cycle structure */ + struct stat *fts_statp; /* stat(2) information */ } FTSENT; .Ed .Pp @@ -136,7 +137,7 @@ A directory being visited in pre-order. .It Dv FTS_DC A directory that causes a cycle in the tree. (The -.Fa fts_link +.Fa fts_cycle field of the .Fa FTSENT structure will be filled in as well.) @@ -149,9 +150,9 @@ by one of the other values. .It Dv FTS_DNR A directory which cannot be read. -An error return; the external variable -.Va errno -will be set to indicate the error. +This is an error return, and the +.Fa fts_errno +field will be set to indicate what caused the error. .It Dv FTS_DOT A file named .Ql \&. @@ -171,9 +172,9 @@ it was returned in pre-order, i.e. with the field set to .Dv FTS_D . .It Dv FTS_ERR -An error return; the external variable -.Va errno -will be set to indicate the error. +This is an error return, and the +.Fa fts_errno +field will be set to indicate what caused the error. .It Dv FTS_F A regular file. .It Dv FTS_NS @@ -181,25 +182,25 @@ A file for which no .Xr stat 2 information was available. The contents of the -.Fa fts_statb +.Fa fts_statp field are undefined. -An error return; the external variable -.Va errno -will be set to indicate the error. +This is an error return, and the +.Fa fts_errno +field will be set to indicate what caused the error. .It Dv FTS_NSOK A file for which no .Xr stat 2 information was requested. The contents of the -.Fa fts_statb +.Fa fts_statp field are undefined. .It Dv FTS_SL A symbolic link. .It Dv FTS_SLNONE A symbolic link with a non-existent target. The contents of the -.Fa fts_statb -field contain the file characteristic information for the symbolic link +.Fa fts_statp +field reference the file characteristic information for the symbolic link itself. .El .It Fa fts_accpath @@ -227,17 +228,34 @@ of the traversal is numbered \-1, and the .Fa FTSENT structure for the root itself is numbered 0. +.It Fa fts_errno +Upon return of a +.Fa FTSENT +structure from the +.Fn fts_children +or +.Fn fts_read +functions, with its +.Fa fts_info +field set to +.Dv FTS_DNR , +.Dv FTS_ERR +or +.Dv FTS_NS , +the +.Fa fts_errno +field contains the value of the external variable +.Va errno +specifying the cause of the error. +Otherwise, the contents of the +.Fa fts_errno +field are undefined. .It Fa fts_number This field is provided for the use of the application program and is not modified by the .Nm fts functions. It is initialized to 0. -The fields -.Fa fts_number -and -.Fa fts_pointer -occupy the same physical location; using both may cause undefined results. .It Fa fts_pointer This field is provided for the use of the application program and is not modified by the @@ -245,11 +263,6 @@ not modified by the functions. It is initialized to .Dv NULL . -The fields -.Fa fts_number -and -.Fa fts_pointer -occupy the same physical location; using both may cause undefined results. .It Fa fts_parent A pointer to the .Fa FTSENT @@ -264,33 +277,62 @@ and .Fa fts_pointer fields are guaranteed to be initialized. .It Fa fts_link -The +Upon return from the +.Fn fts_children +function, the .Fa fts_link -field has two separate uses. +field points to the next structure in the NULL-terminated linked list of +directory members. +Otherwise, the contents of the +.Fa fts_link +field are undefined. +.It Fa fts_cycle If a directory causes a cycle in the hierarchy (see .Dv FTS_DC ) , either because of a hard link between two directories, or a symbolic link pointing to a directory, the -.Fa fts_link +.Fa fts_cycle field of the structure will point to the .Fa FTSENT -structure in the hierarchy -that references the same file as the current +structure in the hierarchy that references the same file as the current .Fa FTSENT structure. -Also, upon return from the -.Fn fts_children -function, the -.Fa fts_link -field points to the next structure in the linked list of directory members. Otherwise, the contents of the -.Fa fts_link +.Fa fts_cycle field are undefined. -.It Fa fts_statb -.Xr Stat 2 +.It Fa fts_statp +A pointer to +.Xr stat 2 information for the file. .El +.Pp +A single buffer is used for all of the paths of all of the files in the +file hierarchy. +Therefore, the +.Fa fts_path +and +.Fa fts_accpath +fields are guaranteed to be +.Dv NULL Ns -terminated +.Em only +for the file most recently returned by +.Fn fts_read . +To use these fields to reference any files represented by other +.Fa FTSENT +structures will require that the path buffer be modified using the +information contained in that +.Fa FTSENT +structure's +.Fa fts_pathlen +field. +Any such modifications should be undone before further calls to +.Fn fts_read +are attempted. +The +.Fa fts_name +field is always +.Dv NULL Ns -terminated. .Sh FTS_OPEN The .Fn fts_open @@ -310,6 +352,11 @@ The options are selected by .Em or Ns 'ing the following values: .Bl -tag -width "FTS_PHYSICAL" +.It Dv FTS_COMFOLLOW +This option causes any symbolic link specified as a root path to be +followed immediately whether or not +.Dv FTS_LOGICAL +is also specified. .It Dv FTS_LOGICAL This option causes the .Nm fts @@ -349,9 +396,8 @@ pathnames were provided as arguments to .It Dv FTS_NOSTAT By default, returned .Fa FTSENT -structures contain file characteristic -information (the -.Fa statb +structures reference file characteristic information (the +.Fa statp field) for each file visited. This option relaxes that requirement as a performance optimization, allowing the @@ -361,7 +407,7 @@ functions to set the field to .Dv FTS_NSOK and leave the contents of the -.Fa statb +.Fa statp field undefined. .It Dv FTS_PHYSICAL This option causes the @@ -387,10 +433,9 @@ By default, unless they are specified as path arguments to .Fn fts_open , any files named .Ql \&. -and +or .Ql .. -encountered in the file hierarchy are -ignored. +encountered in the file hierarchy are ignored. This option causes the .Nm fts routines to return @@ -429,17 +474,18 @@ If the field is set to .Dv FTS_NS or -.DV FTS_NSOK , +.Dv FTS_NSOK , the -.Fa fts_stab +.Fa fts_statp field may not either. If the .Fn compar argument is .Dv NULL , -the directory traversal order is unspecified except -for the root paths which are traversed in the order listed in -.Fa path_argv . +the directory traversal order is in the order listed in +.Fa path_argv +for the root paths, and in the order listed in the directory for +everything else. .Sh FTS_READ The .Fn fts_read @@ -496,8 +542,8 @@ The .Fn fts_children function returns a pointer to an .Fa FTSENT -structure describing the first -entry in a linked list of the files in the directory represented by the +structure describing the first entry in a NULL-terminated linked list of +the files in the directory represented by the .Fa FTSENT structure most recently returned by .Fn fts_read . @@ -505,13 +551,20 @@ The list is linked through the .Fa fts_link field of the .Fa FTSENT -structure, and is ordered by the user-specified -comparison function, if any. +structure, and is ordered by the user-specified comparison function, if any. Repeated calls to .Fn fts_children will recreate this linked list. .Pp -If the +As a special case, if +.Fn fts_read +has not yet been called for a hierarchy, +.Fn fts_children +will return a pointer to the files in the logical directory specified to +.Fn fts_open , +i.e. the arguments specified to +.Fn fts_open . +Otherwise, if the .Fa FTSENT structure most recently returned by .Fn fts_read @@ -536,39 +589,24 @@ The structures returned by .Fn fts_children may be overwritten after a call to +.Fn fts_children , .Fn fts_close -on the same file hierarchy stream, or after a call to -.Fn fts_children or .Fn fts_read on the same file hierarchy stream. .Pp -A single buffer is used for all of the paths of all of the files in the -file hierarchy. -Therefore, the -.Fa fts_path -and -.Fa fts_accpath -fields are guaranteed to be -.Dv NULL Ns -terminated -.Em only -for the file most recently returned by -.Fn fts_read . -To use these fields to reference any files represented by other -.Fa FTSENT -structures will require that the path buffer be modified using the -information contained in that -.Fa FTSENT -structure's -.Fa fts_pathlen -field. -Any such modifications should be undone before further calls to -.Fn fts_read -are attempted. -The +.Em Option +may be set to the following value: +.Bl -tag -width FTS_NAMEONLY +.It Dv FTS_NAMEONLY +Only the names of the files are needed. +The contents of all the fields in the returned linked list of structures +are undefined with the exception of the .Fa fts_name -field is always -.Dv NULL Ns -terminated. +and +.Fa fts_namelen +fields. +.El .Sh FTS_SET The function .Fn fts_set @@ -610,7 +648,7 @@ the next call to returns the file with the .Fa fts_info and -.Fa fts_statb +.Fa fts_statp fields reinitialized to reflect the target of the symbolic link instead of the symbolic link itself. If the file is one of those most recently returned by @@ -618,7 +656,7 @@ If the file is one of those most recently returned by the .Fa fts_info and -.Fa fts_statb +.Fa fts_statp fields of the structure, when returned by .Fn fts_read , will reflect the target of the symbolic link instead of the symbolic link @@ -655,32 +693,48 @@ returns 0 on success, and \-1 if an error occurs. .Sh ERRORS The function .Fn fts_open -may fail and set errno for any of the errors specified for the library -functions +may fail and set +.Va errno +for any of the errors specified for the library functions .Xr open 2 and .Xr malloc 3 . .Pp The function .Fn fts_close -may fail and set errno for any of the errors specified for the library -functions +may fail and set +.Va errno +for any of the errors specified for the library functions .Xr chdir 2 and .Xr close 2 . .Pp The functions -.Fn Fts_read +.Fn fts_read and .Fn fts_children -may fail and set errno for any of the errors specified for the library -functions +may fail and set +.Va errno +for any of the errors specified for the library functions .Xr chdir 2 , .Xr malloc 3 , .Xr opendir 3 , .Xr readdir 3 and .Xr stat 2 . +.Pp +In addition, +.Fn fts_children , +.Fn fts_open +and +.Fn fts_set +may fail and set +.Va errno +as follows: +.Bl -tag -width Er +.It Bq Er EINVAL +The options were invalid. +.EL .Sh SEE ALSO .Xr find 1 , .Xr chdir 2 , diff --git a/lib/libc/gen/fts.c b/lib/libc/gen/fts.c index e2958d3cb4..b63884f6c5 100644 --- a/lib/libc/gen/fts.c +++ b/lib/libc/gen/fts.c @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,24 +32,30 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)fts.c 5.19 (Berkeley) 5/9/91"; +static char sccsid[] = "@(#)fts.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ -#include #include #include #include #include #include -#include "fts.h" +#include #include #include #include -static FTSENT *fts_alloc(), *fts_build(), *fts_sort(); -static void fts_load(), fts_lfree(); -static u_short fts_stat(); -static char *fts_path(); +static FTSENT *fts_alloc __P((FTS *, char *, int)); +static FTSENT *fts_build __P((FTS *, int)); +static void fts_lfree __P((FTSENT *)); +static void fts_load __P((FTS *, FTSENT *)); +static size_t fts_maxarglen __P((char * const *)); +static void fts_padjust __P((FTS *, void *)); +static int fts_palloc __P((FTS *, size_t)); +static FTSENT *fts_sort __P((FTS *, FTSENT *, int)); +static u_short fts_stat __P((FTS *, FTSENT *, int)); + +#define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2])) #define ISSET(opt) (sp->fts_options & opt) #define SET(opt) (sp->fts_options |= opt) @@ -58,8 +64,9 @@ static char *fts_path(); #define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) /* fts_build flags */ -#define BCHILD 1 /* from fts_children */ -#define BREAD 2 /* from fts_read */ +#define BCHILD 1 /* fts_children */ +#define BNAMES 2 /* fts_children, names only */ +#define BREAD 3 /* fts_read */ FTS * fts_open(argv, options, compar) @@ -69,13 +76,19 @@ fts_open(argv, options, compar) { register FTS *sp; register FTSENT *p, *root; - register int nitems, maxlen; + register int nitems; FTSENT *parent, *tmp; int len; + /* Options check. */ + if (options & ~FTS_OPTIONMASK) { + errno = EINVAL; + return (NULL); + } + /* Allocate/initialize the stream */ - if (!(sp = (FTS *)malloc((u_int)sizeof(FTS)))) - return(NULL); + if ((sp = malloc((u_int)sizeof(FTS))) == NULL) + return (NULL); bzero(sp, sizeof(FTS)); sp->fts_compar = compar; sp->fts_options = options; @@ -84,23 +97,36 @@ fts_open(argv, options, compar) if (ISSET(FTS_LOGICAL)) SET(FTS_NOCHDIR); - /* Allocate/initialize root's parent. */ - if (!(parent = fts_alloc(sp, "", 0))) + /* + * Start out with 1K of path space, and enough, in any case, + * to hold the user's paths. + */ + if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN))) goto mem1; + + /* Allocate/initialize root's parent. */ + if ((parent = fts_alloc(sp, "", 0)) == NULL) + goto mem2; parent->fts_level = FTS_ROOTPARENTLEVEL; /* Allocate/initialize root(s). */ - maxlen = -1; for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { - if (!(len = strlen(*argv))) { + /* Don't allow zero-length paths. */ + if ((len = strlen(*argv)) == 0) { errno = ENOENT; - goto mem2; + goto mem3; } - if (maxlen < len) - maxlen = len; + p = fts_alloc(sp, *argv, len); p->fts_level = FTS_ROOTLEVEL; p->fts_parent = parent; + p->fts_accpath = p->fts_name; + p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)); + + /* Command-line "." and ".." are real directories. */ + if (p->fts_info == FTS_DOT) + p->fts_info = FTS_D; + /* * If comparison routine supplied, traverse in sorted * order; otherwise traverse in the order specified. @@ -108,12 +134,9 @@ fts_open(argv, options, compar) if (compar) { p->fts_link = root; root = p; - p->fts_accpath = p->fts_name; - if (!(options & FTS_NOSTAT)) - p->fts_info = fts_stat(sp, p, 0); } else { p->fts_link = NULL; - if (!root) + if (root == NULL) tmp = root = p; else { tmp->fts_link = p; @@ -126,17 +149,13 @@ fts_open(argv, options, compar) /* * Allocate a dummy pointer and make fts_read think that we've just - * finished the node before the root(s); set p->fts_info to FTS_NS + * finished the node before the root(s); set p->fts_info to FTS_INIT * so that everything about the "current" node is ignored. */ - if (!(sp->fts_cur = fts_alloc(sp, "", 0))) - goto mem2; - sp->fts_cur->fts_link = root; - sp->fts_cur->fts_info = FTS_NS; - - /* Start out with at least 1K+ of path space. */ - if (!fts_path(sp, MAX(maxlen, MAXPATHLEN))) + if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) goto mem3; + sp->fts_cur->fts_link = root; + sp->fts_cur->fts_info = FTS_INIT; /* * If using chdir(2), grab a file descriptor pointing to dot to insure @@ -148,13 +167,13 @@ fts_open(argv, options, compar) if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0) SET(FTS_NOCHDIR); - return(sp); + return (sp); -mem3: free(sp->fts_cur); -mem2: fts_lfree(root); +mem3: fts_lfree(root); free(parent); +mem2: free(sp->fts_path); mem1: free(sp); - return(NULL); + return (NULL); } static void @@ -169,7 +188,8 @@ fts_load(sp, p) * Load the stream structure for the next traversal. Since we don't * actually enter the directory until after the preorder visit, set * the fts_accpath field specially so the chdir gets done to the right - * place and the user can access the first node. + * place and the user can access the first node. From fts_open it's + * known that the path will fit. */ len = p->fts_pathlen = p->fts_namelen; bcopy(p->fts_name, sp->fts_path, len + 1); @@ -179,24 +199,23 @@ fts_load(sp, p) p->fts_namelen = len; } p->fts_accpath = p->fts_path = sp->fts_path; - - p->fts_info = fts_stat(sp, p, 0); - sp->rdev = p->fts_statb.st_dev; + sp->fts_dev = p->fts_dev; } +int fts_close(sp) FTS *sp; { register FTSENT *freep, *p; int saved_errno; + /* + * This still works if we haven't read anything -- the dummy structure + * points to the root list, so we step through to the end of the root + * list which has a valid parent pointer. + */ if (sp->fts_cur) { - /* - * This still works if we haven't read anything -- the dummy - * structure points to the root list, so we step through to - * the end of the root list which has a valid parent pointer. - */ - for (p = sp->fts_cur; p->fts_level > FTS_ROOTPARENTLEVEL;) { + for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { freep = p; p = p->fts_link ? p->fts_link : p->fts_parent; free(freep); @@ -223,17 +242,17 @@ fts_close(sp) /* Set errno and return. */ if (!ISSET(FTS_NOCHDIR) && saved_errno) { errno = saved_errno; - return(-1); + return (-1); } - return(0); + return (0); } /* - * Special case a root of "/" so that slashes aren't appended causing - * paths to be written as "//foo". + * Special case a root of "/" so that slashes aren't appended which would + * cause paths to be written as "//foo". */ -#define NAPPEND(p) \ - (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \ +#define NAPPEND(p) \ + (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \ p->fts_path[0] == '/' ? 0 : p->fts_pathlen) FTSENT * @@ -243,10 +262,11 @@ fts_read(sp) register FTSENT *p, *tmp; register int instr; register char *t; + int saved_errno; /* If finished or unrecoverable error, return NULL. */ - if (!sp->fts_cur || ISSET(FTS_STOP)) - return(NULL); + if (sp->fts_cur == NULL || ISSET(FTS_STOP)) + return (NULL); /* Set current node pointer. */ p = sp->fts_cur; @@ -255,97 +275,123 @@ fts_read(sp) instr = p->fts_instr; p->fts_instr = FTS_NOINSTR; - /* If used fts_link pointer for cycle detection, restore it. */ - if (sp->fts_savelink) { - p->fts_link = sp->fts_savelink; - sp->fts_savelink = NULL; - } - /* Any type of file may be re-visited; re-stat and re-turn. */ if (instr == FTS_AGAIN) { p->fts_info = fts_stat(sp, p, 0); - return(p); + return (p); } /* * Following a symlink -- SLNONE test allows application to see - * SLNONE and recover. + * SLNONE and recover. If indirecting through a symlink, have + * keep a pointer to current location. If unable to get that + * pointer, follow fails. */ if (instr == FTS_FOLLOW && (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { p->fts_info = fts_stat(sp, p, 1); - return(p); + if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) + if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) { + p->fts_errno = errno; + p->fts_info = FTS_ERR; + } else + p->fts_flags |= FTS_SYMFOLLOW; + return (p); } /* Directory in pre-order. */ if (p->fts_info == FTS_D) { /* If skipped or crossed mount point, do post-order visit. */ if (instr == FTS_SKIP || - ISSET(FTS_XDEV) && p->fts_statb.st_dev != sp->rdev) { + ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev) { + if (p->fts_flags & FTS_SYMFOLLOW) + (void)close(p->fts_symfd); if (sp->fts_child) { fts_lfree(sp->fts_child); sp->fts_child = NULL; } p->fts_info = FTS_DP; - return(p); + return (p); } + /* Rebuild if only read the names and now traversing. */ + if (sp->fts_child && sp->fts_options & FTS_NAMEONLY) { + sp->fts_options &= ~FTS_NAMEONLY; + fts_lfree(sp->fts_child); + sp->fts_child = NULL; + } + /* - * Cd to the subdirectory, reading it if haven't already. If - * the read fails for any reason, or the directory is empty, - * the fts_info field of the current node is set by fts_build. + * Cd to the subdirectory. + * * If have already read and now fail to chdir, whack the list - * to make the names come out right, and set the parent state + * to make the names come out right, and set the parent errno * so the application will eventually get an error condition. - * If haven't read and fail to chdir, check to see if we're - * at the root node -- if so, we have to get back or the root - * node may be inaccessible. + * Set the FTS_DONTCHDIR flag so that when we logically change + * directories back to the parent we don't do a chdir. + * + * If haven't read do so. If the read fails, fts_build sets + * FTS_STOP or the fts_info field of the node. */ if (sp->fts_child) { if (CHDIR(sp, p->fts_accpath)) { - p->fts_parent->fts_cderr = errno; + p->fts_errno = errno; + p->fts_flags |= FTS_DONTCHDIR; for (p = sp->fts_child; p; p = p->fts_link) p->fts_accpath = p->fts_parent->fts_accpath; } - } else if (!(sp->fts_child = fts_build(sp, BREAD))) { - if ISSET(FTS_STOP) - return(NULL); - if (p->fts_level == FTS_ROOTLEVEL && - FCHDIR(sp, sp->fts_rfd)) { - SET(FTS_STOP); - return(NULL); - } - return(p); + } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { + if (ISSET(FTS_STOP)) + return (NULL); + return (p); } p = sp->fts_child; sp->fts_child = NULL; goto name; } - /* Move to next node on this level. */ + /* Move to the next node on this level. */ next: tmp = p; if (p = p->fts_link) { free(tmp); - /* If reached the top, load the paths for the next root. */ + /* + * If reached the top, return to the original directory, and + * load the paths for the next root. + */ if (p->fts_level == FTS_ROOTLEVEL) { + if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { + SET(FTS_STOP); + return (NULL); + } fts_load(sp, p); - return(sp->fts_cur = p); + return (sp->fts_cur = p); } - /* User may have called fts_set on the node. */ + /* + * User may have called fts_set on the node. If skipped, + * ignore. If followed, get a file descriptor so we can + * get back if necessary. + */ if (p->fts_instr == FTS_SKIP) goto next; if (p->fts_instr == FTS_FOLLOW) { p->fts_info = fts_stat(sp, p, 1); + if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) + if ((p->fts_symfd = + open(".", O_RDONLY, 0)) < 0) { + p->fts_errno = errno; + p->fts_info = FTS_ERR; + } else + p->fts_flags |= FTS_SYMFOLLOW; p->fts_instr = FTS_NOINSTR; } name: t = sp->fts_path + NAPPEND(p->fts_parent); *t++ = '/'; bcopy(p->fts_name, t, p->fts_namelen + 1); - return(sp->fts_cur = p); + return (sp->fts_cur = p); } /* Move up to the parent node. */ @@ -359,40 +405,39 @@ name: t = sp->fts_path + NAPPEND(p->fts_parent); */ free(p); errno = 0; - return(sp->fts_cur = NULL); + return (sp->fts_cur = NULL); } + /* Nul terminate the pathname. */ sp->fts_path[p->fts_pathlen] = '\0'; /* - * Cd back up to the parent directory. If at a root node, have to cd - * back to the original place, otherwise may not be able to access the - * original node on post-order. + * Return to the parent directory. If at a root node or came through + * a symlink, go back through the file descriptor. Otherwise, cd up + * one directory. */ if (p->fts_level == FTS_ROOTLEVEL) { - if (FCHDIR(sp, sp->fts_rfd)) { + if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { SET(FTS_STOP); - return(NULL); + return (NULL); + } + } else if (p->fts_flags & FTS_SYMFOLLOW) { + if (FCHDIR(sp, p->fts_symfd)) { + saved_errno = errno; + (void)close(p->fts_symfd); + errno = saved_errno; + SET(FTS_STOP); + return (NULL); + } + (void)close(p->fts_symfd); + } else if (!(p->fts_flags & FTS_DONTCHDIR)) { + if (CHDIR(sp, "..")) { + SET(FTS_STOP); + return (NULL); } } - else if (CHDIR(sp, "..")) { - SET(FTS_STOP); - return(NULL); - } - - /* - * If had a chdir error when trying to get into the directory, set the - * info field to reflect this, and restore errno. The error indicator - * has to be reset to 0 so that if the user does an FTS_AGAIN, it all - * works. - */ - if (p->fts_cderr) { - errno = p->fts_cderr; - p->fts_cderr = 0; - p->fts_info = FTS_ERR; - } else - p->fts_info = FTS_DP; - return(sp->fts_cur = p); + p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; + return (sp->fts_cur = p); } /* @@ -402,39 +447,69 @@ name: t = sp->fts_path + NAPPEND(p->fts_parent); * reasons. */ /* ARGSUSED */ +int fts_set(sp, p, instr) FTS *sp; FTSENT *p; int instr; { + if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && + instr != FTS_NOINSTR && instr != FTS_SKIP) { + errno = EINVAL; + return (1); + } p->fts_instr = instr; - return(0); + return (0); } FTSENT * -fts_children(sp) +fts_children(sp, instr) register FTS *sp; + int instr; { register FTSENT *p; int fd; + if (instr && instr != FTS_NAMEONLY) { + errno = EINVAL; + return (NULL); + } + /* Set current node pointer. */ p = sp->fts_cur; /* - * Set errno to 0 so that user can tell the difference between an - * error and a directory without entries. If not a directory being - * visited in *pre-order*, or we've already had fatal errors, return - * immediately. + * Errno set to 0 so user can distinguish empty directory from + * an error. */ errno = 0; - if (ISSET(FTS_STOP) || p->fts_info != FTS_D && p->fts_info != FTS_DNR) - return(NULL); + + /* Fatal errors stop here. */ + if (ISSET(FTS_STOP)) + return (NULL); + + /* Return logical hierarchy of user's arguments. */ + if (p->fts_info == FTS_INIT) + return (p->fts_link); + + /* + * If not a directory being visited in pre-order, stop here. Could + * allow FTS_DNR, assuming the user has fixed the problem, but the + * same effect is available with FTS_AGAIN. + */ + if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) + return (NULL); /* Free up any previous child list. */ if (sp->fts_child) fts_lfree(sp->fts_child); + if (instr == FTS_NAMEONLY) { + sp->fts_options |= FTS_NAMEONLY; + instr = BNAMES; + } else + instr = BCHILD; + /* * If using chdir on a relative path and called BEFORE fts_read does * its chdir to the root of a traversal, we can lose -- we need to @@ -444,15 +519,15 @@ fts_children(sp) */ if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || ISSET(FTS_NOCHDIR)) - return(sp->fts_child = fts_build(sp, BCHILD)); + return (sp->fts_child = fts_build(sp, instr)); if ((fd = open(".", O_RDONLY, 0)) < 0) - return(NULL); - sp->fts_child = fts_build(sp, BCHILD); + return (NULL); + sp->fts_child = fts_build(sp, instr); if (fchdir(fd)) - return(NULL); + return (NULL); (void)close(fd); - return(sp->fts_child); + return (sp->fts_child); } /* @@ -462,13 +537,13 @@ fts_children(sp) * * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is * set and it's a physical walk (so that symbolic links can't be directories), - * we assume that the number of subdirectories in a node is equal to the number - * of links to the parent. This allows stat calls to be skipped in any leaf - * directories and for any nodes after the directories in the parent node have - * been found. This empirically cuts the stat calls by about 2/3. + * we can do things quickly. First, if it's a 4.4BSD file system, the type + * of the file is in the directory entry. Otherwise, we assume that the number + * of subdirectories in a node is equal to the number of links to the parent. + * The former skips all stat calls. The latter skips stat calls in any leaf + * directories and for any files after the subdirectories in the directory have + * been found, cutting the stat calls by about 2/3. */ -#define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2])) - static FTSENT * fts_build(sp, type) register FTS *sp; @@ -477,9 +552,10 @@ fts_build(sp, type) register struct dirent *dp; register FTSENT *p, *head; register int nitems; - FTSENT *cur; + FTSENT *cur, *tail; DIR *dirp; - int cderr, descend, len, level, maxlen, nlinks, saved_errno; + void *adjaddr; + int cderrno, descend, len, level, maxlen, nlinks, saved_errno; char *cp; /* Set current node pointer. */ @@ -489,10 +565,12 @@ fts_build(sp, type) * Open the directory for reading. If this fails, we're done. * If being called from fts_read, set the fts_info field. */ - if (!(dirp = opendir(cur->fts_accpath))) { - if (type == BREAD) + if ((dirp = opendir(cur->fts_accpath)) == NULL) { + if (type == BREAD) { cur->fts_info = FTS_DNR; - return(NULL); + cur->fts_errno = errno; + } + return (NULL); } /* @@ -500,32 +578,43 @@ fts_build(sp, type) * directory if we're cheating on stat calls, 0 if we're not doing * any stat calls at all, -1 if we're doing stats on everything. */ - nlinks = - ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL) ? - cur->fts_statb.st_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2) : -1; + if (type == BNAMES) + nlinks = 0; + else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) + nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); + else + nlinks = -1; +#ifdef notdef + (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink); + (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", + ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); +#endif /* * If we're going to need to stat anything or we want to descend - * and stay in the directory, chdir. If this fails we keep going. + * and stay in the directory, chdir. If this fails we keep going, + * but set a flag so we don't chdir after the post-order visit. * We won't be able to stat anything, but we can still return the * names themselves. Note, that since fts_read won't be able to * chdir into the directory, it will have to return different path * names than before, i.e. "a/b" instead of "b". Since the node * has already been visited in pre-order, have to wait until the - * post-order visit to return the error. This is all fairly nasty. - * If a program needed sorted entries or stat information, they had - * better be checking FTS_NS on the returned nodes. + * post-order visit to return the error. There is a special case + * here, if there was nothing to stat then it's not an error to + * not be able to stat. This is all fairly nasty. If a program + * needed sorted entries or stat information, they had better be + * checking FTS_NS on the returned nodes. */ + cderrno = 0; if (nlinks || type == BREAD) if (FCHDIR(sp, dirfd(dirp))) { - if (type == BREAD) - cur->fts_cderr = errno; - descend = nlinks = 0; - cderr = 1; - } else { + if (nlinks && type == BREAD) + cur->fts_errno = errno; + cur->fts_flags |= FTS_DONTCHDIR; + descend = 0; + cderrno = errno; + } else descend = 1; - cderr = 0; - } else descend = 0; @@ -536,8 +625,8 @@ fts_build(sp, type) * could do them in fts_read before returning the path, but it's a * lot easier here since the length is part of the dirent structure. * - * If not changing directories set a pointer so that we can just - * append each new name into the path. + * If not changing directories set a pointer so that can just append + * each new name into the path. */ maxlen = sp->fts_pathlen - cur->fts_pathlen - 1; len = NAPPEND(cur); @@ -549,14 +638,15 @@ fts_build(sp, type) level = cur->fts_level + 1; /* Read the directory, attaching each entry to the `link' pointer. */ - for (head = NULL, nitems = 0; dp = readdir(dirp);) { + adjaddr = NULL; + for (head = tail = NULL, nitems = 0; dp = readdir(dirp);) { if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) continue; - if (!(p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen))) + if ((p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen)) == NULL) goto mem1; if (dp->d_namlen > maxlen) { - if (!fts_path(sp, (int)dp->d_namlen)) { + if (fts_palloc(sp, (size_t)dp->d_namlen)) { /* * No more memory for path or structures. Save * errno, free up the current structure and the @@ -570,8 +660,9 @@ mem1: saved_errno = errno; errno = saved_errno; cur->fts_info = FTS_ERR; SET(FTS_STOP); - return(NULL); + return (NULL); } + adjaddr = sp->fts_path; maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; } @@ -579,31 +670,57 @@ mem1: saved_errno = errno; p->fts_parent = sp->fts_cur; p->fts_level = level; - if (nlinks) { + if (cderrno) { + if (nlinks) { + p->fts_info = FTS_NS; + p->fts_errno = cderrno; + } else + p->fts_info = FTS_NSOK; + p->fts_accpath = cur->fts_accpath; + } else if (nlinks == 0 +#ifdef DT_DIR + || nlinks > 0 && + dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN +#endif + ) { + p->fts_accpath = + ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; + p->fts_info = FTS_NSOK; + } else { /* Build a file name for fts_stat to stat. */ if (ISSET(FTS_NOCHDIR)) { p->fts_accpath = p->fts_path; bcopy(p->fts_name, cp, p->fts_namelen + 1); } else p->fts_accpath = p->fts_name; + /* Stat it. */ p->fts_info = fts_stat(sp, p, 0); - if (nlinks > 0 && p->fts_info == FTS_D) + + /* Decrement link count if applicable. */ + if (nlinks > 0 && (p->fts_info == FTS_D || + p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) --nlinks; - } else if (cderr) { - p->fts_info = ISSET(FTS_NOSTAT) ? FTS_NSOK : FTS_NS; - p->fts_accpath = cur->fts_accpath; - } else { - p->fts_accpath = - ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; - p->fts_info = FTS_NSOK; } - p->fts_link = head; - head = p; + /* We walk in directory order so "ls -f" doesn't get upset. */ + p->fts_link = NULL; + if (head == NULL) + head = tail = p; + else { + tail->fts_link = p; + tail = p; + } ++nitems; } (void)closedir(dirp); + /* + * If had to realloc the path, adjust the addresses for the rest + * of the tree. + */ + if (adjaddr) + fts_padjust(sp, adjaddr); + /* * If not changing directories, reset the path back to original * state. @@ -617,25 +734,25 @@ mem1: saved_errno = errno; /* * If descended after called from fts_children or called from * fts_read and didn't find anything, get back. If can't get - * back, we're done. + * back, done. */ if (descend && (!nitems || type == BCHILD) && CHDIR(sp, "..")) { cur->fts_info = FTS_ERR; SET(FTS_STOP); - return(NULL); + return (NULL); } - /* If we didn't find anything, just do the post-order visit */ + /* If didn't find anything, return NULL. */ if (!nitems) { if (type == BREAD) cur->fts_info = FTS_DP; - return(NULL); + return (NULL); } /* Sort the entries. */ if (sp->fts_compar && nitems > 1) head = fts_sort(sp, head, nitems); - return(head); + return (head); } static u_short @@ -644,62 +761,72 @@ fts_stat(sp, p, follow) register FTSENT *p; int follow; { + register FTSENT *t; + register dev_t dev; + register ino_t ino; + struct stat *sbp, sb; int saved_errno; + /* If user needs stat info, stat buffer already allocated. */ + sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; + /* * If doing a logical walk, or application requested FTS_FOLLOW, do * a stat(2). If that fails, check for a non-existent symlink. If - * fail, return the errno from the stat call. + * fail, set the errno from the stat call. */ if (ISSET(FTS_LOGICAL) || follow) { - if (stat(p->fts_accpath, &p->fts_statb)) { + if (stat(p->fts_accpath, sbp)) { saved_errno = errno; - if (!lstat(p->fts_accpath, &p->fts_statb)) { + if (!lstat(p->fts_accpath, sbp)) { errno = 0; - return(FTS_SLNONE); + return (FTS_SLNONE); } - errno = saved_errno; - bzero(&p->fts_statb, sizeof(struct stat)); - return(FTS_NS); + p->fts_errno = saved_errno; + goto err; } - } else if (lstat(p->fts_accpath, &p->fts_statb)) { - bzero(&p->fts_statb, sizeof(struct stat)); - return(FTS_NS); + } else if (lstat(p->fts_accpath, sbp)) { + p->fts_errno = errno; +err: bzero(sbp, sizeof(struct stat)); + return (FTS_NS); } - /* - * Cycle detection is done as soon as we find a directory. Detection - * is by brute force; if the tree gets deep enough or the number of - * symbolic links to directories high enough something faster might - * be worthwhile. - */ - if (S_ISDIR(p->fts_statb.st_mode)) { - register FTSENT *t; - register dev_t dev; - register ino_t ino; - - dev = p->fts_statb.st_dev; - ino = p->fts_statb.st_ino; - for (t = p->fts_parent; t->fts_level > FTS_ROOTLEVEL; - t = t->fts_parent) - if (ino == t->fts_statb.st_ino && - dev == t->fts_statb.st_dev) { - sp->fts_savelink = p->fts_link; - p->fts_link = t; - return(FTS_DC); + if (S_ISDIR(sbp->st_mode)) { + /* + * Set the device/inode. Used to find cycles and check for + * crossing mount points. Also remember the link count, used + * in fts_build to limit the number of stat calls. It is + * understood that these fields are only referenced if fts_info + * is set to FTS_D. + */ + dev = p->fts_dev = sbp->st_dev; + ino = p->fts_ino = sbp->st_ino; + p->fts_nlink = sbp->st_nlink; + + if (ISDOT(p->fts_name)) + return (FTS_DOT); + + /* + * Cycle detection is done by brute force when the directory + * is first encountered. If the tree gets deep enough or the + * number of symbolic links to directories is high enough, + * something faster might be worthwhile. + */ + for (t = p->fts_parent; + t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) + if (ino == t->fts_ino && dev == t->fts_dev) { + p->fts_cycle = t; + return (FTS_DC); } - return(FTS_D); + return (FTS_D); } - if (S_ISLNK(p->fts_statb.st_mode)) - return(FTS_SL); - if (S_ISREG(p->fts_statb.st_mode)) - return(FTS_F); - return(FTS_DEFAULT); + if (S_ISLNK(sbp->st_mode)) + return (FTS_SL); + if (S_ISREG(sbp->st_mode)) + return (FTS_F); + return (FTS_DEFAULT); } -#define R(type, nelem, ptr) \ - (type *)realloc((void *)ptr, (u_int)((nelem) * sizeof(type))) - static FTSENT * fts_sort(sp, head, nitems) FTS *sp; @@ -713,14 +840,14 @@ fts_sort(sp, head, nitems) * Reassemble the array in the order returned by qsort. If unable to * sort for memory reasons, return the directory entries in their * current order. Allocate enough space for the current needs plus - * 40 so we don't realloc one entry at a time. + * 40 so don't realloc one entry at a time. */ if (nitems > sp->fts_nitems) { sp->fts_nitems = nitems + 40; - if (!(sp->fts_array = - R(FTSENT *, sp->fts_nitems, sp->fts_array))) { + if ((sp->fts_array = realloc(sp->fts_array, + (size_t)(sp->fts_nitems * sizeof(FTSENT *)))) == NULL) { sp->fts_nitems = 0; - return(head); + return (head); } } for (ap = sp->fts_array, p = head; p; p = p->fts_link) @@ -729,32 +856,45 @@ fts_sort(sp, head, nitems) for (head = *(ap = sp->fts_array); --nitems; ++ap) ap[0]->fts_link = ap[1]; ap[0]->fts_link = NULL; - return(head); + return (head); } static FTSENT * -fts_alloc(sp, name, len) +fts_alloc(sp, name, namelen) FTS *sp; char *name; - register int len; + register int namelen; { register FTSENT *p; + size_t len; /* - * Variable sized structures; the name is the last element so - * we allocate enough extra space after the structure to store - * it. + * The file name is a variable length array and no stat structure is + * necessary if the user has set the nostat bit. Allocate the FTSENT + * structure, the file name and the stat structure in one chunk, but + * be careful that the stat structure is reasonably aligned. Since the + * fts_name field is declared to be of size 1, the fts_name pointer is + * namelen + 2 before the first possible address of the stat structure. */ - if (!(p = (FTSENT *)malloc((size_t)(sizeof(FTSENT) + len)))) - return(NULL); - bcopy(name, p->fts_name, len + 1); - p->fts_namelen = len; + len = sizeof(FTSENT) + namelen; + if (!ISSET(FTS_NOSTAT)) + len += sizeof(struct stat) + ALIGNBYTES; + if ((p = malloc(len)) == NULL) + return (NULL); + + /* Copy the name plus the trailing NULL. */ + bcopy(name, p->fts_name, namelen + 1); + + if (!ISSET(FTS_NOSTAT)) + p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2); + p->fts_namelen = namelen; p->fts_path = sp->fts_path; + p->fts_errno = 0; + p->fts_flags = 0; p->fts_instr = FTS_NOINSTR; - p->fts_cderr = 0; p->fts_number = 0; p->fts_pointer = NULL; - return(p); + return (p); } static void @@ -771,17 +911,55 @@ fts_lfree(head) } /* - * Allow essentially unlimited paths; certain programs (find, rm, ls) need to - * work on any tree. Most systems will allow creation of paths much longer - * than MAXPATHLEN, even though the kernel won't resolve them. Add an extra - * 128 bytes to the requested size so that we don't realloc the path 2 bytes - * at a time. + * Allow essentially unlimited paths; find, rm, ls should all work on any tree. + * Most systems will allow creation of paths much longer than MAXPATHLEN, even + * though the kernel won't resolve them. Add the size (not just what's needed) + * plus 256 bytes so don't realloc the path 2 bytes at a time. + */ +static int +fts_palloc(sp, more) + FTS *sp; + size_t more; +{ + sp->fts_pathlen += more + 256; + sp->fts_path = realloc(sp->fts_path, (size_t)sp->fts_pathlen); + return (sp->fts_path == NULL); +} + +/* + * When the path is realloc'd, have to fix all of the pointers in structures + * already returned. */ -static char * -fts_path(sp, size) +static void +fts_padjust(sp, addr) FTS *sp; - int size; + void *addr; { - sp->fts_pathlen += size + 128; - return(sp->fts_path = R(char, sp->fts_pathlen, sp->fts_path)); + FTSENT *p; + +#define ADJUST(p) { \ + (p)->fts_accpath = addr + ((p)->fts_accpath - (p)->fts_path); \ + (p)->fts_path = addr; \ +} + /* Adjust the current set of children. */ + for (p = sp->fts_child; p; p = p->fts_link) + ADJUST(p); + + /* Adjust the rest of the tree. */ + for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { + ADJUST(p); + p = p->fts_link ? p->fts_link : p->fts_parent; + } +} + +static size_t +fts_maxarglen(argv) + char * const *argv; +{ + size_t len, max; + + for (max = 0; *argv; ++argv) + if ((len = strlen(*argv)) > max) + max = len; + return (max); } diff --git a/lib/libc/gen/getcap.3 b/lib/libc/gen/getcap.3 new file mode 100644 index 0000000000..4af162f1af --- /dev/null +++ b/lib/libc/gen/getcap.3 @@ -0,0 +1,511 @@ +.\" Copyright (c) 1992 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Casey Leedom of Lawrence Livermore National Laboratory. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)getcap.3 5.4 (Berkeley) 8/11/92 +.\" +.Dd "August 11, 1992" +.Dt GETCAP 3 +.Os +.Sh NAME +.Nm cgetent , +.Nm cgetset , +.Nm cgetmatch , +.Nm cgetcap , +.Nm cgetnum , +.Nm cgetstr , +.Nm cgetustr , +.Nm cgetfirst , +.Nm cgetnext , +.Nm cgetclose +.Nd capability database access routines +.Sh SYNOPSIS +.Fd #include +.Ft int +.Fn cgetent "char **buf" "char **db_array" "char *name" +.Ft int +.Fn cgetset "char *ent" +.Ft int +.Fn cgetmatch "char *buf" "char *name" +.Ft char * +.Fn cgetcap "char *buf" "char *cap" "char type" +.Ft int +.Fn cgetnum "char *buf" "char *cap" "long *num" +.Ft int +.Fn cgetstr "char *buf" "char *cap" "char **str" +.Ft int +.Fn cgetustr "char *buf" "char *cap" "char **str" +.Ft int +.Fn cgetfirst "char **buf" "char **db_array" +.Ft int +.Fn cgetnext "char **buf" "char **db_array" +.Ft int +.Fn cgetclose "void" +.Sh DESCRIPTION +.Fn Cgetent +extracts the capability rec +.Fa name +from the database specified by the +.Dv NULL +terminated file array +.Fa db_array +and returns a pointer to a +.Xr malloc Ns \&'d +copy of it in +.Fa buf . +.Nm Cgetent +will first look for files ending in +.Nm .db +(see +.Xr cap_mkdb 1) +before accessing the ASCII file. +.Fa Buf +must be retained through all subsequent calls to +.Fn cgetmatch , +.Fn cgetcap , +.Fn cgetnum , +.Fn cgetstr , +and +.Fn cgetustr , +but may then be +.Xr free Ns \&'d. +On success 0 is returned, 1 if the returned +record contains an unresolved +.Nm tc +expansion, +\-1 if the requested record couldn't be found, +\-2 if a system error was encountered (couldn't open/read a file, etc.) also +setting +.Va errno , +and \-3 if a potential reference loop is detected (see +.Ic tc= +comments below). +.Pp +.Nm Cgetset +enables the addition of a character buffer containing a single capability +record entry +to the capability database. +Conceptually, the entry is added as the first ``file'' in the database, and +is therefore searched first on the call to +.Nm cgetent . +The entry is passed in +.Fa ent . +If +.Fa ent +is +.Dv NULL , +the current entry is removed from the database. +.Nm Cgetset +must precede the database traversal. It must be called before the +.Nm cgetent +call. If a sequential access is being performed (see below), it must be called +before the first sequential access call ( +.Nm cgetfirst +or +.Nm cgetnext +), or be directly preceded by a +.Nm cgetclose +call. +On success 0 is returned and \-1 on failure. +.Pp +.Nm Cgetmatch +will return 0 if +.Fa name +is one of the names of the capability record +.Fa buf , +\-1 if +not. +.Pp +.Nm Cgetcap +searches the capability record +.Fa buf +for the capability +.Fa cap +with type +.Fa type . +A +.Fa type +is specified using any single character. If a colon (`:') is used, an +untyped capability will be searched for (see below for explanation of +types). A pointer to the value of +.Fa cap +in +.Fa buf +is returned on success, +.Dv NULL +if the requested capability couldn't be +found. The end of the capability value is signaled by a `:' or +.Tn ASCII +.Dv NUL +(see below for capability database syntax). +.Pp +.Nm Cgetnum +retrieves the value of the numeric capability +.Fa cap +from the capability record pointed to by +.Fa buf . +The numeric value is returned in the +.Ft long +pointed to by +.Fa num . +0 is returned on success, \-1 if the requested numeric capability couldn't +be found. +.Pp +.Nm Cgetstr +retrieves the value of the string capability +.Fa cap +from the capability record pointed to by +.Fa buf . +A pointer to a decoded, +.Dv NUL +terminated, +.Xr malloc Ns \&'d +copy of the string is returned in the +.Ft char * +pointed to by +.Fa str . +The number of characters in the decoded string not including the trailing +.Dv NUL +is returned on success, \-1 if the requested string capability couldn't +be found, \-2 if a system error was encountered (storage allocation +failure). +.Pp +.Nm Cgetustr +is identical to +.Nm cgetstr +except that it does not expand special characters, but rather returns each +character of the capability string literally. +.Pp +.Nm Cgetfirst , +.Nm cgetnext , +comprise a function group that provides for sequential +access of the +.Dv NULL +pointer terminated array of file names, +.Fa db_array . +.Nm Cgetfirst +returns the first record in the database and resets the access +to the first record. +.Nm Cgetnext +returns the next record in the database with respect to the +record returned by the previous +.Nm cgetfirst +or +.Nm cgetnext +call. If there is no such previous call, the first record in the database is +returned. +Each record is returned in a +.Xr malloc Ns \&'d +copy pointed to by +.Fa buf . +.Ic Tc +expansion is done (see +.Ic tc= +comments below). +Upon completion of the database 0 is returned, 1 is returned upon successful +return of record with possibly more remaining (we haven't reached the end of +the database yet), 2 is returned if the record contains an unresolved +.Nm tc +expansion, \-1 is returned if an system error occured, and \-2 +is returned if a potential reference loop is detected (see +.Ic tc= +comments below). +Upon completion of database (0 return) the database is closed. +.Pp +.Nm Cgetclose +closes the sequential access and frees any memory and file descriptors +being used. Note that it does not erase the buffer pushed by a call to +.Nm cgetset . +.Sh CAPABILITY DATABASE SYNTAX +Capability databases are normally +.Tn ASCII +and may be edited with standard +text editors. Blank lines and lines beginning with a `#' are comments +and are ignored. Lines ending with a `\|\e' indicate that the next line +is a continuation of the current line; the `\|\e' and following newline +are ignored. Long lines are usually continued onto several physical +lines by ending each line except the last with a `\|\e'. +.Pp +Capability databases consist of a series of records, one per logical +line. Each record contains a variable number of `:'-separated fields +(capabilities). Empty fields consisting entirely of white space +characters (spaces and tabs) are ignored. +.Pp +The first capability of each record specifies its names, separated by `|' +characters. These names are used to reference records in the database. +By convention, the last name is usually a comment and is not intended as +a lookup tag. For example, the +.Em vt100 +record from the +.Nm termcap +database begins: +.Pp +.Dl "d0\||\|vt100\||\|vt100-am\||\|vt100am\||\|dec vt100:" +.Pp +giving four names that can be used to access the record. +.Pp +The remaining non-empty capabilities describe a set of (name, value) +bindings, consisting of a names optionally followed by a typed values: +.Bl -column "nameTvalue" +.It name Ta "typeless [boolean] capability" +.Em name No "is present [true]" +.It name Ns Em \&T Ns value Ta capability +.Pq Em name , \&T +has value +.Em value +.It name@ Ta "no capability" Em name No exists +.It name Ns Em T Ns \&@ Ta capability +.Pq Em name , T +does not exist +.El +.Pp +Names consist of one or more characters. Names may contain any character +except `:', but it's usually best to restrict them to the printable +characters and avoid use of graphics like `#', `=', `%', `@', etc. Types +are single characters used to separate capability names from their +associated typed values. Types may be any character except a `:'. +Typically, graphics like `#', `=', `%', etc. are used. Values may be any +number of characters and may contain any character except `:'. +.Sh CAPABILITY DATABASE SEMANTICS +Capability records describe a set of (name, value) bindings. Names may +have multiple values bound to them. Different values for a name are +distinguished by their +.Fa types . +.Nm Cgetcap +will return a pointer to a value of a name given the capability name and +the type of the value. +.Pp +The types `#' and `=' are conventionally used to denote numeric and +string typed values, but no restriction on those types is enforced. The +functions +.Nm cgetnum +and +.Nm cgetstr +can be used to implement the traditional syntax and semantics of `#' +and `='. +Typeless capabilities are typically used to denote boolean objects with +presence or absence indicating truth and false values respectively. +This interpretation is conveniently represented by: +.Pp +.Dl "(getcap(buf, name, ':') != NULL)" +.Pp +A special capability, +.Ic tc= name , +is used to indicate that the record specified by +.Fa name +should be substituted for the +.Ic tc +capability. +.Ic Tc +capabilities may interpolate records which also contain +.Ic tc +capabilities and more than one +.Ic tc +capability may be used in a record. A +.Ic tc +expansion scope (i.e., where the argument is searched for) contains the +file in which the +.Ic tc +is declared and all subsequent files in the file array. +.Pp +When a database is searched for a capability record, the first matching +record in the search is returned. When an record is scanned for a +capability, the first matching capability is returned; the capability +.Ic :nameT@: +will hide any following definition of a value of type +.Em T +for +.Fa name ; +and the capability +.Ic :name@: +will prevent any following values of +.Fa name +from being seen. +.Pp +These features combined with +.Ic tc +capabilities can be used to generate variations of other databases and +records by either adding new capabilities, overriding definitions with new +definitions, or hiding following definitions via `@' capabilities. +.Sh EXAMPLES +.Bd -unfilled -offset indent +example\||\|an example of binding multiple values to names:\e + :foo%bar:foo^blah:foo@:\e + :abc%xyz:abc^frap:abc$@:\e + :tc=more: +.Ed +.Pp +The capability foo has two values bound to it (bar of type `%' and blah of +type `^') and any other value bindings are hidden. The capability abc +also has two values bound but only a value of type `$' is prevented from +being defined in the capability record more. +.Pp +.Bd -unfilled -offset indent +file1: + new\||\|new_record\||\|a modification of "old":\e + :fript=bar:who-cares@:tc=old:blah:tc=extensions: +file2: + old\||\|old_record\||\|an old database record:\e + :fript=foo:who-cares:glork#200: +.Ed +.Pp +The records are extracted by calling +.Nm cgetent +with file1 preceding file2. +In the capability record new in file1, fript=bar overrides the definition +of fript=foo interpolated from the capability record old in file2, +who-cares@ prevents the definition of any who-cares definitions in old +from being seen, glork#200 is inherited from old, and blah and anything +defined by the record extensions is added to those definitions in old. +Note that the position of the fript=bar and who-cares@ definitions before +tc=old is important here. If they were after, the definitions in old +would take precedence. +.Sh CGETNUM AND CGETSTR SYNTAX AND SEMANTICS +Two types are predefined by +.Nm cgetnum +and +.Nm cgetstr : +.Bl -column "nameXnumber" +.Sm off +.It Em name No \&# Em number Ta numeric +capability +.Em name +has value +.Em number +.It Em name No = Em string Ta "string capability" +.Em name +has value +.Em string +.It Em name No \&#@ Ta "the numeric capability" +.Em name +does not exist +.It Em name No \&=@ Ta "the string capability" +.Em name +does not exist +.El +.Pp +Numeric capability values may be given in one of three numeric bases. +If the number starts with either +.Ql 0x +or +.Ql 0X +it is interpreted as a hexadecimal number (both upper and lower case a-f +may be used to denote the extended hexadecimal digits). +Otherwise, if the number starts with a +.Ql 0 +it is interpreted as an octal number. +Otherwise the number is interpreted as a decimal number. +.Pp +String capability values may contain any character. Non-printable +.Dv ASCII +codes, new lines, and colons may be conveniently represented by the use +of escape sequences: +.Bl -column "\e\|X,X\e\|X" "(ASCII octal nnn)" +^X ('\fIX\fP' & 037) control-\fIX\fP +\e\|b, \e\|B (ASCII 010) backspace +\e\|t, \e\|T (ASCII 011) tab +\e\|n, \e\|N (ASCII 012) line feed (newline) +\e\|f, \e\|F (ASCII 014) form feed +\e\|r, \e\|R (ASCII 015) carriage return +\e\|e, \e\|E (ASCII 027) escape +\e\|c, \e\|C (:) colon +\e\|\e (\e\|) back slash +\e\|^ (^) caret +\e\|\fInnn\fP (ASCII octal \fInnn\fP) +.El +.Pp +A `\|\e' may be followed by up to three octal digits directly specifies +the numeric code for a character. The use of +.Tn ASCII +.Dv NUL Ns s , +while easily +encoded, causes all sorts of problems and must be used with care since +.Dv NUL Ns s +are typically used to denote the end of strings; many applications +use `\e\|200' to represent a +.Dv NUL . +.Sh DIAGNOSTICS +.Nm Cgetent , +.Nm cgetset , +.Nm cgetmatch , +.Nm cgetnum , +.Nm cgetstr , +.Nm cgetustr , +.Nm cgetfirst , +and +.Nm cgetnext +return a a value greater than or equal to 0 on success and a value less +than 0 on failure. +.Nm Cgetcap +returns a character pointer on success and a +.Dv NULL +on failure. +.Pp +.Nm Cgetent , +and +.Nm cgetseq +may fail and set +.Va errno +for any of the errors specified for the library functions: +.Xr fopen 2 , +.Xr fclose 2 , +.Xr open 2 , +and +.Xr close 2 . +.Pp +.Nm Cgetent , +.Nm cgetset , +.Nm cgetstr , +and +.Nm cgetustr +may fail and set +.Va errno +as follows: +.Bl -tag -width Er +.It Bq Er ENOMEM +No memory to allocate. +.El +.Sh SEE ALSO +.Xr cap_mkdb 1 , +.Xr malloc 3 +.Sh BUGS +Colons (`:') can't be used in names, types, or values. +.Pp +There are no checks for +.Ic tc= name +loops in +.Nm cgetent . +.Pp +The buffer added to the database by a call to +.Nm cgetset +is not unique to the database but is rather prepended to any database used. diff --git a/lib/libc/gen/getcap.c b/lib/libc/gen/getcap.c new file mode 100644 index 0000000000..7f70789450 --- /dev/null +++ b/lib/libc/gen/getcap.c @@ -0,0 +1,1038 @@ +/*- + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Casey Leedom of Lawrence Livermore National Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getcap.c 5.15 (Berkeley) 3/19/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BFRAG 1024 +#define BSIZE 1024 +#define ESC ('[' & 037) /* ASCII ESC */ +#define MAX_RECURSION 32 /* maximum getent recursion */ +#define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ + +#define RECOK (char)0 +#define TCERR (char)1 +#define SHADOW (char)2 + +static size_t topreclen; /* toprec length */ +static char *toprec; /* Additional record specified by cgetset() */ +static int gottoprec; /* Flag indicating retrieval of toprecord */ + +static int cdbget __P((DB *, char **, char *)); +static int getent __P((char **, u_int *, char **, int, char *, int, char *)); +static int nfcmp __P((char *, char *)); + +/* + * Cgetset() allows the addition of a user specified buffer to be added + * to the database array, in effect "pushing" the buffer on top of the + * virtual database. 0 is returned on success, -1 on failure. + */ +int +cgetset(ent) + char *ent; +{ + if (ent == NULL) { + if (toprec) + free(toprec); + toprec = NULL; + topreclen = 0; + return (0); + } + topreclen = strlen(ent); + if ((toprec = malloc (topreclen + 1)) == NULL) { + errno = ENOMEM; + return (-1); + } + gottoprec = 0; + (void)strcpy(toprec, ent); + return (0); +} + +/* + * Cgetcap searches the capability record buf for the capability cap with + * type `type'. A pointer to the value of cap is returned on success, NULL + * if the requested capability couldn't be found. + * + * Specifying a type of ':' means that nothing should follow cap (:cap:). + * In this case a pointer to the terminating ':' or NUL will be returned if + * cap is found. + * + * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) + * return NULL. + */ +char * +cgetcap(buf, cap, type) + char *buf, *cap; + int type; +{ + register char *bp, *cp; + + bp = buf; + for (;;) { + /* + * Skip past the current capability field - it's either the + * name field if this is the first time through the loop, or + * the remainder of a field whose name failed to match cap. + */ + for (;;) + if (*bp == '\0') + return (NULL); + else + if (*bp++ == ':') + break; + + /* + * Try to match (cap, type) in buf. + */ + for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) + continue; + if (*cp != '\0') + continue; + if (*bp == '@') + return (NULL); + if (type == ':') { + if (*bp != '\0' && *bp != ':') + continue; + return(bp); + } + if (*bp != type) + continue; + bp++; + return (*bp == '@' ? NULL : bp); + } + /* NOTREACHED */ +} + +/* + * Cgetent extracts the capability record name from the NULL terminated file + * array db_array and returns a pointer to a malloc'd copy of it in buf. + * Buf must be retained through all subsequent calls to cgetcap, cgetnum, + * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success, + * -1 if the requested record couldn't be found, -2 if a system error was + * encountered (couldn't open/read a file, etc.), and -3 if a potential + * reference loop is detected. + */ +int +cgetent(buf, db_array, name) + char **buf, **db_array, *name; +{ + u_int dummy; + + return (getent(buf, &dummy, db_array, -1, name, 0, NULL)); +} + +/* + * Getent implements the functions of cgetent. If fd is non-negative, + * *db_array has already been opened and fd is the open file descriptor. We + * do this to save time and avoid using up file descriptors for tc= + * recursions. + * + * Getent returns the same success/failure codes as cgetent. On success, a + * pointer to a malloc'ed capability record with all tc= capabilities fully + * expanded and its length (not including trailing ASCII NUL) are left in + * *cap and *len. + * + * Basic algorithm: + * + Allocate memory incrementally as needed in chunks of size BFRAG + * for capability buffer. + * + Recurse for each tc=name and interpolate result. Stop when all + * names interpolated, a name can't be found, or depth exceeds + * MAX_RECURSION. + */ +static int +getent(cap, len, db_array, fd, name, depth, nfield) + char **cap, **db_array, *name, *nfield; + u_int *len; + int fd, depth; +{ + DB *capdbp; + DBT key, data; + register char *r_end, *rp, **db_p; + int myfd, eof, foundit, retval; + char *record; + int tc_not_resolved; + char pbuf[_POSIX_PATH_MAX]; + + /* + * Return with ``loop detected'' error if we've recursed more than + * MAX_RECURSION times. + */ + if (depth > MAX_RECURSION) + return (-3); + + /* + * Check if we have a top record from cgetset(). + */ + if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) { + if ((record = malloc (topreclen + BFRAG)) == NULL) { + errno = ENOMEM; + return (-2); + } + (void)strcpy(record, toprec); + myfd = 0; + db_p = db_array; + rp = record + topreclen + 1; + r_end = rp + BFRAG; + goto tc_exp; + } + /* + * Allocate first chunk of memory. + */ + if ((record = malloc(BFRAG)) == NULL) { + errno = ENOMEM; + return (-2); + } + r_end = record + BFRAG; + foundit = 0; + /* + * Loop through database array until finding the record. + */ + + for (db_p = db_array; *db_p != NULL; db_p++) { + eof = 0; + + /* + * Open database if not already open. + */ + + if (fd >= 0) { + (void)lseek(fd, (off_t)0, L_SET); + myfd = 0; + } else { + (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p); + if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0)) + != NULL) { + free(record); + retval = cdbget(capdbp, &record, name); + if (capdbp->close(capdbp) < 0) + return (-2); + *len = strlen(record); + *cap = malloc(*len + 1); + memmove(*cap, record, *len + 1); + return (retval); + } else { + fd = open(*db_p, O_RDONLY, 0); + if (fd < 0) { + /* No error on unfound file. */ + if (errno == ENOENT) + continue; + free(record); + return (-2); + } + myfd = 1; + } + } + /* + * Find the requested capability record ... + */ + { + char buf[BUFSIZ]; + register char *b_end, *bp; + register int c; + + /* + * Loop invariants: + * There is always room for one more character in record. + * R_end always points just past end of record. + * Rp always points just past last character in record. + * B_end always points just past last character in buf. + * Bp always points at next character in buf. + */ + b_end = buf; + bp = buf; + for (;;) { + + /* + * Read in a line implementing (\, newline) + * line continuation. + */ + rp = record; + for (;;) { + if (bp >= b_end) { + int n; + + n = read(fd, buf, sizeof(buf)); + if (n <= 0) { + if (myfd) + (void)close(fd); + if (n < 0) { + free(record); + return (-2); + } else { + fd = -1; + eof = 1; + break; + } + } + b_end = buf+n; + bp = buf; + } + + c = *bp++; + if (c == '\n') { + if (rp > record && *(rp-1) == '\\') { + rp--; + continue; + } else + break; + } + *rp++ = c; + + /* + * Enforce loop invariant: if no room + * left in record buffer, try to get + * some more. + */ + if (rp >= r_end) { + u_int pos; + size_t newsize; + + pos = rp - record; + newsize = r_end - record + BFRAG; + record = realloc(record, newsize); + if (record == NULL) { + errno = ENOMEM; + if (myfd) + (void)close(fd); + return (-2); + } + r_end = record + newsize; + rp = record + pos; + } + } + /* loop invariant let's us do this */ + *rp++ = '\0'; + + /* + * If encountered eof check next file. + */ + if (eof) + break; + + /* + * Toss blank lines and comments. + */ + if (*record == '\0' || *record == '#') + continue; + + /* + * See if this is the record we want ... + */ + if (cgetmatch(record, name) == 0) { + if (nfield == NULL || !nfcmp(nfield, record)) { + foundit = 1; + break; /* found it! */ + } + } + } + } + if (foundit) + break; + } + + if (!foundit) + return (-1); + + /* + * Got the capability record, but now we have to expand all tc=name + * references in it ... + */ +tc_exp: { + register char *newicap, *s; + register int newilen; + u_int ilen; + int diff, iret, tclen; + char *icap, *scan, *tc, *tcstart, *tcend; + + /* + * Loop invariants: + * There is room for one more character in record. + * R_end points just past end of record. + * Rp points just past last character in record. + * Scan points at remainder of record that needs to be + * scanned for tc=name constructs. + */ + scan = record; + tc_not_resolved = 0; + for (;;) { + if ((tc = cgetcap(scan, "tc", '=')) == NULL) + break; + + /* + * Find end of tc=name and stomp on the trailing `:' + * (if present) so we can use it to call ourselves. + */ + s = tc; + for (;;) + if (*s == '\0') + break; + else + if (*s++ == ':') { + *(s - 1) = '\0'; + break; + } + tcstart = tc - 3; + tclen = s - tcstart; + tcend = s; + + iret = getent(&icap, &ilen, db_p, fd, tc, depth+1, + NULL); + newicap = icap; /* Put into a register. */ + newilen = ilen; + if (iret != 0) { + /* an error */ + if (iret < -1) { + if (myfd) + (void)close(fd); + free(record); + return (iret); + } + if (iret == 1) + tc_not_resolved = 1; + /* couldn't resolve tc */ + if (iret == -1) { + *(s - 1) = ':'; + scan = s - 1; + tc_not_resolved = 1; + continue; + + } + } + /* not interested in name field of tc'ed record */ + s = newicap; + for (;;) + if (*s == '\0') + break; + else + if (*s++ == ':') + break; + newilen -= s - newicap; + newicap = s; + + /* make sure interpolated record is `:'-terminated */ + s += newilen; + if (*(s-1) != ':') { + *s = ':'; /* overwrite NUL with : */ + newilen++; + } + + /* + * Make sure there's enough room to insert the + * new record. + */ + diff = newilen - tclen; + if (diff >= r_end - rp) { + u_int pos, tcpos, tcposend; + size_t newsize; + + pos = rp - record; + newsize = r_end - record + diff + BFRAG; + tcpos = tcstart - record; + tcposend = tcend - record; + record = realloc(record, newsize); + if (record == NULL) { + errno = ENOMEM; + if (myfd) + (void)close(fd); + free(icap); + return (-2); + } + r_end = record + newsize; + rp = record + pos; + tcstart = record + tcpos; + tcend = record + tcposend; + } + + /* + * Insert tc'ed record into our record. + */ + s = tcstart + newilen; + bcopy(tcend, s, rp - tcend); + bcopy(newicap, tcstart, newilen); + rp += diff; + free(icap); + + /* + * Start scan on `:' so next cgetcap works properly + * (cgetcap always skips first field). + */ + scan = s-1; + } + + } + /* + * Close file (if we opened it), give back any extra memory, and + * return capability, length and success. + */ + if (myfd) + (void)close(fd); + *len = rp - record - 1; /* don't count NUL */ + if (r_end > rp) + if ((record = + realloc(record, (size_t)(rp - record))) == NULL) { + errno = ENOMEM; + return (-2); + } + + *cap = record; + if (tc_not_resolved) + return (1); + return (0); +} + +static int +cdbget(capdbp, bp, name) + DB *capdbp; + char **bp, *name; +{ + DBT key, data; + char *buf; + int st; + + key.data = name; + key.size = strlen(name); + + for (;;) { + /* Get the reference. */ + switch(capdbp->get(capdbp, &key, &data, 0)) { + case -1: + return (-2); + case 1: + return (-1); + } + + /* If not an index to another record, leave. */ + if (((char *)data.data)[0] != SHADOW) + break; + + key.data = (char *)data.data + 1; + key.size = data.size - 1; + } + + *bp = (char *)data.data + 1; + return (((char *)(data.data))[0] == TCERR ? 1 : 0); +} + +/* + * Cgetmatch will return 0 if name is one of the names of the capability + * record buf, -1 if not. + */ +int +cgetmatch(buf, name) + char *buf, *name; +{ + register char *np, *bp; + + /* + * Start search at beginning of record. + */ + bp = buf; + for (;;) { + /* + * Try to match a record name. + */ + np = name; + for (;;) + if (*np == '\0') + if (*bp == '|' || *bp == ':' || *bp == '\0') + return (0); + else + break; + else + if (*bp++ != *np++) + break; + + /* + * Match failed, skip to next name in record. + */ + bp--; /* a '|' or ':' may have stopped the match */ + for (;;) + if (*bp == '\0' || *bp == ':') + return (-1); /* match failed totally */ + else + if (*bp++ == '|') + break; /* found next name */ + } +} + + + + + +int +cgetfirst(buf, db_array) + char **buf, **db_array; +{ + (void)cgetclose(); + return (cgetnext(buf, db_array)); +} + +static FILE *pfp; +static int slash; +static char **dbp; + +int +cgetclose() +{ + if (pfp != NULL) { + (void)fclose(pfp); + pfp = NULL; + } + dbp = NULL; + gottoprec = 0; + slash = 0; + return(0); +} + +/* + * Cgetnext() gets either the first or next entry in the logical database + * specified by db_array. It returns 0 upon completion of the database, 1 + * upon returning an entry with more remaining, and -1 if an error occurs. + */ +int +cgetnext(bp, db_array) + register char **bp; + char **db_array; +{ + size_t len; + int status, i, done; + char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE]; + u_int dummy; + + if (dbp == NULL) + dbp = db_array; + + if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { + (void)cgetclose(); + return (-1); + } + for(;;) { + if (toprec && !gottoprec) { + gottoprec = 1; + line = toprec; + } else { + line = fgetline(pfp, &len); + if (line == NULL && pfp) { + (void)fclose(pfp); + if (ferror(pfp)) { + (void)cgetclose(); + return (-1); + } else { + if (*++dbp == NULL) { + (void)cgetclose(); + return (0); + } else if ((pfp = + fopen(*dbp, "r")) == NULL) { + (void)cgetclose(); + return (-1); + } else + continue; + } + } else + line[len - 1] = '\0'; + if (len == 1) { + slash = 0; + continue; + } + if (isspace(*line) || + *line == ':' || *line == '#' || slash) { + if (line[len - 2] == '\\') + slash = 1; + else + slash = 0; + continue; + } + if (line[len - 2] == '\\') + slash = 1; + else + slash = 0; + } + + + /* + * Line points to a name line. + */ + i = 0; + done = 0; + np = nbuf; + for (;;) { + for (cp = line; *cp != '\0'; cp++) { + if (*cp == ':') { + *np++ = ':'; + done = 1; + break; + } + if (*cp == '\\') + break; + *np++ = *cp; + } + if (done) { + *np = '\0'; + break; + } else { /* name field extends beyond the line */ + line = fgetline(pfp, &len); + if (line == NULL && pfp) { + (void)fclose(pfp); + if (ferror(pfp)) { + (void)cgetclose(); + return (-1); + } + } else + line[len - 1] = '\0'; + } + } + rp = buf; + for(cp = nbuf; *cp != NULL; cp++) + if (*cp == '|' || *cp == ':') + break; + else + *rp++ = *cp; + + *rp = '\0'; + /* + * XXX + * Last argument of getent here should be nbuf if we want true + * sequential access in the case of duplicates. + * With NULL, getent will return the first entry found + * rather than the duplicate entry record. This is a + * matter of semantics that should be resolved. + */ + status = getent(bp, &dummy, db_array, -1, buf, 0, NULL); + if (status == -2 || status == -3) + (void)cgetclose(); + + return (status + 1); + } + /* NOTREACHED */ +} + +/* + * Cgetstr retrieves the value of the string capability cap from the + * capability record pointed to by buf. A pointer to a decoded, NUL + * terminated, malloc'd copy of the string is returned in the char * + * pointed to by str. The length of the string not including the trailing + * NUL is returned on success, -1 if the requested string capability + * couldn't be found, -2 if a system error was encountered (storage + * allocation failure). + */ +int +cgetstr(buf, cap, str) + char *buf, *cap; + char **str; +{ + register u_int m_room; + register char *bp, *mp; + int len; + char *mem; + + /* + * Find string capability cap + */ + bp = cgetcap(buf, cap, '='); + if (bp == NULL) + return (-1); + + /* + * Conversion / storage allocation loop ... Allocate memory in + * chunks SFRAG in size. + */ + if ((mem = malloc(SFRAG)) == NULL) { + errno = ENOMEM; + return (-2); /* couldn't even allocate the first fragment */ + } + m_room = SFRAG; + mp = mem; + + while (*bp != ':' && *bp != '\0') { + /* + * Loop invariants: + * There is always room for one more character in mem. + * Mp always points just past last character in mem. + * Bp always points at next character in buf. + */ + if (*bp == '^') { + bp++; + if (*bp == ':' || *bp == '\0') + break; /* drop unfinished escape */ + *mp++ = *bp++ & 037; + } else if (*bp == '\\') { + bp++; + if (*bp == ':' || *bp == '\0') + break; /* drop unfinished escape */ + if ('0' <= *bp && *bp <= '7') { + register int n, i; + + n = 0; + i = 3; /* maximum of three octal digits */ + do { + n = n * 8 + (*bp++ - '0'); + } while (--i && '0' <= *bp && *bp <= '7'); + *mp++ = n; + } + else switch (*bp++) { + case 'b': case 'B': + *mp++ = '\b'; + break; + case 't': case 'T': + *mp++ = '\t'; + break; + case 'n': case 'N': + *mp++ = '\n'; + break; + case 'f': case 'F': + *mp++ = '\f'; + break; + case 'r': case 'R': + *mp++ = '\r'; + break; + case 'e': case 'E': + *mp++ = ESC; + break; + case 'c': case 'C': + *mp++ = ':'; + break; + default: + /* + * Catches '\', '^', and + * everything else. + */ + *mp++ = *(bp-1); + break; + } + } else + *mp++ = *bp++; + m_room--; + + /* + * Enforce loop invariant: if no room left in current + * buffer, try to get some more. + */ + if (m_room == 0) { + size_t size = mp - mem; + + if ((mem = realloc(mem, size + SFRAG)) == NULL) + return (-2); + m_room = SFRAG; + mp = mem + size; + } + } + *mp++ = '\0'; /* loop invariant let's us do this */ + m_room--; + len = mp - mem - 1; + + /* + * Give back any extra memory and return value and success. + */ + if (m_room != 0) + if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) + return (-2); + *str = mem; + return (len); +} + +/* + * Cgetustr retrieves the value of the string capability cap from the + * capability record pointed to by buf. The difference between cgetustr() + * and cgetstr() is that cgetustr does not decode escapes but rather treats + * all characters literally. A pointer to a NUL terminated malloc'd + * copy of the string is returned in the char pointed to by str. The + * length of the string not including the trailing NUL is returned on success, + * -1 if the requested string capability couldn't be found, -2 if a system + * error was encountered (storage allocation failure). + */ +int +cgetustr(buf, cap, str) + char *buf, *cap, **str; +{ + register u_int m_room; + register char *bp, *mp; + int len; + char *mem; + + /* + * Find string capability cap + */ + if ((bp = cgetcap(buf, cap, '=')) == NULL) + return (-1); + + /* + * Conversion / storage allocation loop ... Allocate memory in + * chunks SFRAG in size. + */ + if ((mem = malloc(SFRAG)) == NULL) { + errno = ENOMEM; + return (-2); /* couldn't even allocate the first fragment */ + } + m_room = SFRAG; + mp = mem; + + while (*bp != ':' && *bp != '\0') { + /* + * Loop invariants: + * There is always room for one more character in mem. + * Mp always points just past last character in mem. + * Bp always points at next character in buf. + */ + *mp++ = *bp++; + m_room--; + + /* + * Enforce loop invariant: if no room left in current + * buffer, try to get some more. + */ + if (m_room == 0) { + size_t size = mp - mem; + + if ((mem = realloc(mem, size + SFRAG)) == NULL) + return (-2); + m_room = SFRAG; + mp = mem + size; + } + } + *mp++ = '\0'; /* loop invariant let's us do this */ + m_room--; + len = mp - mem - 1; + + /* + * Give back any extra memory and return value and success. + */ + if (m_room != 0) + if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) + return (-2); + *str = mem; + return (len); +} + +/* + * Cgetnum retrieves the value of the numeric capability cap from the + * capability record pointed to by buf. The numeric value is returned in + * the long pointed to by num. 0 is returned on success, -1 if the requested + * numeric capability couldn't be found. + */ +int +cgetnum(buf, cap, num) + char *buf, *cap; + long *num; +{ + register long n; + register int base, digit; + register char *bp; + + /* + * Find numeric capability cap + */ + bp = cgetcap(buf, cap, '#'); + if (bp == NULL) + return (-1); + + /* + * Look at value and determine numeric base: + * 0x... or 0X... hexadecimal, + * else 0... octal, + * else decimal. + */ + if (*bp == '0') { + bp++; + if (*bp == 'x' || *bp == 'X') { + bp++; + base = 16; + } else + base = 8; + } else + base = 10; + + /* + * Conversion loop ... + */ + n = 0; + for (;;) { + if ('0' <= *bp && *bp <= '9') + digit = *bp - '0'; + else if ('a' <= *bp && *bp <= 'f') + digit = 10 + *bp - 'a'; + else if ('A' <= *bp && *bp <= 'F') + digit = 10 + *bp - 'A'; + else + break; + + if (digit >= base) + break; + + n = n * base + digit; + bp++; + } + + /* + * Return value and success. + */ + *num = n; + return (0); +} + + +/* + * Compare name field of record. + */ +static int +nfcmp(nf, rec) + char *nf, *rec; +{ + char *cp, tmp; + int ret; + + for (cp = rec; *cp != ':'; cp++) + ; + + tmp = *(cp + 1); + *(cp + 1) = '\0'; + ret = strcmp(nf, rec); + *(cp + 1) = tmp; + + return (ret); +} diff --git a/lib/libc/gen/getcwd.c b/lib/libc/gen/getcwd.c index 8f2fbcc87e..8d42575f22 100644 --- a/lib/libc/gen/getcwd.c +++ b/lib/libc/gen/getcwd.c @@ -77,6 +77,10 @@ getcwd(pt, size) errno = EINVAL; return((char *)NULL); } + if (size == 1) { + errno = ERANGE; + return((char *)NULL); + } ept = pt + size; } else { if (!(pt = (char *)malloc(ptsize = 1024 - 4))) @@ -185,7 +189,7 @@ getcwd(pt, size) * Check for length of the current name, preceding slash, * leading slash. */ - if (bpt - pt <= dp->d_namlen + (first ? 1 : 2)) { + if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) { size_t len, off; if (!ptsize) { diff --git a/lib/libc/gen/getgrent.3 b/lib/libc/gen/getgrent.3 index 52a498b0ec..45d3bced27 100644 --- a/lib/libc/gen/getgrent.3 +++ b/lib/libc/gen/getgrent.3 @@ -48,7 +48,7 @@ .Ft struct group * .Fn getgrent void .Ft struct group * -.Fn getgrname "const char *name" +.Fn getgrnam "const char *name" .Ft struct group * .Fn getgrgid "gid_t gid" .Ft struct group * diff --git a/lib/libc/gen/getpwent.c b/lib/libc/gen/getpwent.c index 4d65063b00..fe1b19cc25 100644 --- a/lib/libc/gen/getpwent.c +++ b/lib/libc/gen/getpwent.c @@ -158,7 +158,7 @@ __initdb() char *p; p = (geteuid()) ? _PATH_MP_DB : _PATH_SMP_DB; - _pw_db = hash_open(p, O_RDONLY, 0, NULL); + _pw_db = dbopen(p, O_RDONLY, 0, DB_HASH, NULL); if (_pw_db) return(1); if (!warned) diff --git a/lib/libc/gen/getusershell.c b/lib/libc/gen/getusershell.c index 7729cab32f..c599ac37ee 100644 --- a/lib/libc/gen/getusershell.c +++ b/lib/libc/gen/getusershell.c @@ -53,7 +53,7 @@ static char *okshells[] = static char **shells, *strings; static char **curshell = NULL; -extern char **initshells(); +static char **initshells(); /* * Get a list of shells from SHELLS, if it exists. diff --git a/lib/libc/gen/glob.3 b/lib/libc/gen/glob.3 index 5b2a140ee5..d856fc8b05 100644 --- a/lib/libc/gen/glob.3 +++ b/lib/libc/gen/glob.3 @@ -31,9 +31,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)glob.3 5.6 (Berkeley) 7/31/91 +.\" @(#)glob.3 5.7 (Berkeley) 12/2/92 .\" -.Dd July 31, 1991 +.Dd December 2, 1992 .Dt GLOB 3 .Os .Sh NAME @@ -209,6 +209,21 @@ Use the backslash character for quoting: every occurrence of a backslash followed by a character in the pattern is replaced by that character, avoiding any special interpretation of the character. +.It Dv GLOB_ALTDIRFUNC +The following additional fields in the pglob structure have been +initialized with alternate functions for glob to use to open, read, +and close directories and to get stat information on names found +in those directories. +.Bd -literal + void *(*gl_opendir)(); + struct dirent *(*gl_readdir)(); + void (*gl_closedir)(); + int (*gl_lstat)(); + int (*gl_stat)(); +.Ed +.Pp +This non-standard extension is provided to allow programs such +as restore to provide globbing from directories stored on tape. .El .Pp If, during the search, a directory is encountered that cannot be opened @@ -343,7 +358,9 @@ The function is expected to be .St -p1003.2 compatible with the exception -that the flag +that the flags +.Dv GLOB_ALTDIRFUNC +and .Dv GLOB_QUOTE and the fields .Fa gl_matchc diff --git a/lib/libc/gen/glob.c b/lib/libc/gen/glob.c index f53af54f4a..4e48b4b625 100644 --- a/lib/libc/gen/glob.c +++ b/lib/libc/gen/glob.c @@ -35,7 +35,7 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)glob.c 5.12 (Berkeley) 6/24/91"; +static char sccsid[] = "@(#)glob.c 5.18 (Berkeley) 12/4/92"; #endif /* LIBC_SCCS and not lint */ /* @@ -50,11 +50,15 @@ static char sccsid[] = "@(#)glob.c 5.12 (Berkeley) 6/24/91"; * character might have (except \ at end of string is retained). * GLOB_MAGCHAR: * Set in gl_flags if pattern contained a globbing character. + * GLOB_NOMAGIC: + * Same as GLOB_NOCHECK, but it will only append pattern if it did + * not contain any magic characters. [Used in csh style globbing] + * GLOB_ALTDIRFUNC: + * Use alternately specified directory access functions. * gl_matchc: * Number of matches in the current invocation of glob. */ -#include #include #include #include @@ -98,10 +102,10 @@ typedef u_short Char; static int compare __P((const void *, const void *)); static void g_Ctoc __P((Char *, char *)); -static int g_lstat __P((Char *, struct stat *)); -static DIR *g_opendir __P((Char *)); +static int g_lstat __P((Char *, struct stat *, glob_t *)); +static DIR *g_opendir __P((Char *, glob_t *)); static Char *g_strchr __P((Char *, int)); -static int g_stat __P((Char *, struct stat *)); +static int g_stat __P((Char *, struct stat *, glob_t *)); static int glob1 __P((Char *, glob_t *)); static int glob2 __P((Char *, Char *, Char *, glob_t *)); static int glob3 __P((Char *, Char *, Char *, Char *, glob_t *)); @@ -167,7 +171,6 @@ glob(pattern, flags, errfunc, pglob) while ((c = *qpatnext++) != EOS) { switch (c) { case LBRACKET: - pglob->gl_flags |= GLOB_MAGCHAR; c = *qpatnext; if (c == NOT) ++qpatnext; @@ -191,6 +194,7 @@ glob(pattern, flags, errfunc, pglob) qpatnext += 2; } } while ((c = *qpatnext++) != RBRACKET); + pglob->gl_flags |= GLOB_MAGCHAR; *bufnext++ = M_END; break; case QUESTION: @@ -199,7 +203,11 @@ glob(pattern, flags, errfunc, pglob) break; case STAR: pglob->gl_flags |= GLOB_MAGCHAR; - *bufnext++ = M_ALL; + /* collapse adjacent stars to one, + * to avoid exponential behavior + */ + if (bufnext == patbuf || bufnext[-1] != M_ALL) + *bufnext++ = M_ALL; break; default: *bufnext++ = CHAR(c); @@ -214,7 +222,15 @@ glob(pattern, flags, errfunc, pglob) if ((err = glob1(patbuf, pglob)) != 0) return(err); - if (pglob->gl_pathc == oldpathc && flags & GLOB_NOCHECK) { + /* + * If there was no match we are going to append the pattern + * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified + * and the pattern did not contain any magic characters + * GLOB_NOMAGIC is there just for compatibility with csh. + */ + if (pglob->gl_pathc == oldpathc && + ((flags & GLOB_NOCHECK) || + ((flags & GLOB_NOMAGIC) && !(pglob->gl_flags & GLOB_MAGCHAR)))) { if (!(flags & GLOB_QUOTE)) { Char *dp = compilebuf; const u_char *sp = compilepat; @@ -283,13 +299,13 @@ glob2(pathbuf, pathend, pattern, pglob) for (anymeta = 0;;) { if (*pattern == EOS) { /* End of pattern? */ *pathend = EOS; - if (g_stat(pathbuf, &sb)) + if (g_lstat(pathbuf, &sb, pglob)) return(0); if (((pglob->gl_flags & GLOB_MARK) && pathend[-1] != SEP) && (S_ISDIR(sb.st_mode) || (S_ISLNK(sb.st_mode) && - (g_stat(pathbuf, &sb) == 0) && + (g_stat(pathbuf, &sb, pglob) == 0) && S_ISDIR(sb.st_mode)))) { *pathend++ = SEP; *pathend = EOS; @@ -324,25 +340,33 @@ glob3(pathbuf, pathend, pattern, restpattern, pglob) glob_t *pglob; { register struct dirent *dp; + struct dirent *(*readdirfunc)(); DIR *dirp; int len, err; + char buf[MAXPATHLEN]; *pathend = EOS; errno = 0; - if (!(dirp = g_opendir(pathbuf))) + if ((dirp = g_opendir(pathbuf, pglob)) == NULL) { /* TODO: don't call for ENOENT or ENOTDIR? */ - if (pglob->gl_errfunc && - (*pglob->gl_errfunc)(pathbuf, errno) || - (pglob->gl_flags & GLOB_ERR)) - return(GLOB_ABEND); - else - return(0); + if (pglob->gl_errfunc) { + g_Ctoc(pathbuf, buf); + if (pglob->gl_errfunc(buf, errno) || + pglob->gl_flags & GLOB_ERR) + return (GLOB_ABEND); + } + return(0); + } err = 0; /* Search directory for matching names. */ - while ((dp = readdir(dirp))) { + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + readdirfunc = pglob->gl_readdir; + else + readdirfunc = readdir; + while ((dp = (*readdirfunc)(dirp))) { register u_char *sc; register Char *dc; @@ -360,8 +384,10 @@ glob3(pathbuf, pathend, pattern, restpattern, pglob) break; } - /* TODO: check error from readdir? */ - (void)closedir(dirp); + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + (*pglob->gl_closedir)(dirp); + else + closedir(dirp); return(err); } @@ -431,9 +457,10 @@ match(name, pat, patend) case M_ALL: if (pat == patend) return(1); - for (; *name != EOS; ++name) - if (match(name, pat, patend)) - return(1); + do + if (match(name, pat, patend)) + return(1); + while (*name++ != EOS); return(0); case M_ONE: if (*name++ == EOS) @@ -441,7 +468,8 @@ match(name, pat, patend) break; case M_SET: ok = 0; - k = *name++; + if ((k = *name++) == EOS) + return(0); if (negate_range = ((*pat & M_MASK) == M_NOT)) ++pat; while (((c = *pat++) & M_MASK) != M_END) @@ -481,36 +509,47 @@ globfree(pglob) } static DIR * -g_opendir(str) +g_opendir(str, pglob) register Char *str; + glob_t *pglob; { char buf[MAXPATHLEN]; + char *dirname; if (!*str) - return(opendir(".")); - g_Ctoc(str, buf); + strcpy(buf, "."); + else + g_Ctoc(str, buf); + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + return((*pglob->gl_opendir)(buf)); return(opendir(buf)); } static int -g_lstat(fn, sb) +g_lstat(fn, sb, pglob) register Char *fn; struct stat *sb; + glob_t *pglob; { char buf[MAXPATHLEN]; g_Ctoc(fn, buf); + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + return((*pglob->gl_lstat)(buf, sb)); return(lstat(buf, sb)); } static int -g_stat(fn, sb) +g_stat(fn, sb, pglob) register Char *fn; struct stat *sb; + glob_t *pglob; { char buf[MAXPATHLEN]; g_Ctoc(fn, buf); + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + return((*pglob->gl_stat)(buf, sb)); return(stat(buf, sb)); } @@ -544,13 +583,13 @@ qprintf(s) register Char *p; for (p = s; *p; p++) - (void)printf("%c", *p & 0xff); + (void)printf("%c", CHAR(*p)); (void)printf("\n"); for (p = s; *p; p++) (void)printf("%c", *p & M_PROTECT ? '"' : ' '); (void)printf("\n"); for (p = s; *p; p++) - (void)printf("%c", *p & M_META ? '_' : ' '); + (void)printf("%c", ismeta(*p) ? '_' : ' '); (void)printf("\n"); } #endif diff --git a/lib/libc/gen/infinity.c b/lib/libc/gen/infinity.c new file mode 100644 index 0000000000..ae2058064f --- /dev/null +++ b/lib/libc/gen/infinity.c @@ -0,0 +1,6 @@ +/* infinity.c */ + +#include + +/* bytes for +Infinity on a 387 */ +char __infinity[] = { 0, 0, 0, 0, 0, 0, 0xf0, 0x7f }; diff --git a/lib/libc/gen/insque.3 b/lib/libc/gen/insque.3 new file mode 100644 index 0000000000..099bc9348a --- /dev/null +++ b/lib/libc/gen/insque.3 @@ -0,0 +1,70 @@ +.\" Copyright (c) 1993 John Brezak +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: insque.3,v 1.2 1993/08/13 12:54:13 brezak Exp $ +.\" +.Dd August 12, 1993 +.Dt INSQUE 3 +.Sh NAME +.Nm insque , +.Nm remque +.Nd insert/remove element from a circular queue +.Sh SYNOPSIS +.Ft struct qelem { +.br +.Ft struct qelem *q_forw; +.br +.Ft struct qelem *q_back; +.br +.Ft char q_data[]; +.br +.Ft }; +.br +.br +.Ft struct qelem * +.Fn insque "struct qelem *entry" "struct qelem *pred" +.Ft struct qelem * +.Fn remque "struct qelem *entry" +.Sh DESCRIPTION +.Fn insque +and +.Fn remque +manipulate queues built from circular, doubly linked lists. Each element +in the queue must begin with a "struct qelem". +.Fn insque +inserts +.Ar entry +in the queue immediately after +.Ar pred . +.Fn remque +removes +.Ar entry +from the queue. +.Sh DIAGNOSTICS +These functions are not atomic unless that machine architecture allows it. +.Sh HISTORY +These are derived from the insque and remque instructions on a VAX. + diff --git a/lib/libc/gen/insque.c b/lib/libc/gen/insque.c new file mode 100644 index 0000000000..14d782eee9 --- /dev/null +++ b/lib/libc/gen/insque.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1993 John Brezak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char rcsid[] = "$Id: insque.c,v 1.2 1993/08/13 12:54:10 brezak Exp $"; +#endif /* LIBC_SCCS and not lint */ + +struct qelem { + struct qelem *q_forw; + struct qelem *q_back; + char *q_data; +}; + +/* + * Link p before s. + */ +#define reque(p, s) (p)->q_forw = (s), (s)->q_back = (p) + +/* + * Insert entry in circular queue after pred. + */ +struct qelem * +insque(entry, pred) + struct qelem *entry, *pred; +{ + reque(entry, pred->q_forw); + reque(pred, entry); + return (entry); +} + +/* + * Remove entry from circular queue. + */ +struct qelem * +remque(entry) + struct qelem *entry; +{ + reque(entry->q_back, entry->q_forw); + return (entry); +} diff --git a/lib/libc/gen/opendir.c b/lib/libc/gen/opendir.c deleted file mode 100644 index f7f221e472..0000000000 --- a/lib/libc/gen/opendir.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 1983 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)opendir.c 5.11 (Berkeley) 2/23/91"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include -#include -#include -#include - -long _rewinddir; - -/* - * open a directory. - */ -DIR * -opendir(name) - const char *name; -{ - register DIR *dirp; - register int fd; - - if ((fd = open(name, 0)) == -1) - return NULL; - if (fcntl(fd, F_SETFD, 1) == -1 || - (dirp = (DIR *)malloc(sizeof(DIR))) == NULL) { - close (fd); - return NULL; - } - /* - * If CLSIZE is an exact multiple of DIRBLKSIZ, use a CLSIZE - * buffer that it cluster boundary aligned. - * Hopefully this can be a big win someday by allowing page trades - * to user space to be done by getdirentries() - */ - if ((CLSIZE % DIRBLKSIZ) == 0) { - dirp->dd_buf = malloc(CLSIZE); - dirp->dd_len = CLSIZE; - } else { - dirp->dd_buf = malloc(DIRBLKSIZ); - dirp->dd_len = DIRBLKSIZ; - } - if (dirp->dd_buf == NULL) { - close (fd); - return NULL; - } - dirp->dd_fd = fd; - dirp->dd_loc = 0; - dirp->dd_seek = 0; - /* - * Set up seek point for rewinddir. - */ - _rewinddir = telldir(dirp); - return dirp; -} diff --git a/lib/libc/gen/readdir.c b/lib/libc/gen/readdir.c deleted file mode 100644 index 11ed3572d5..0000000000 --- a/lib/libc/gen/readdir.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 1983 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)readdir.c 5.7 (Berkeley) 6/1/90"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include - -/* - * get next entry in a directory. - */ -struct dirent * -readdir(dirp) - register DIR *dirp; -{ - register struct dirent *dp; - - for (;;) { - if (dirp->dd_loc == 0) { - dirp->dd_size = getdirentries(dirp->dd_fd, - dirp->dd_buf, dirp->dd_len, &dirp->dd_seek); - if (dirp->dd_size <= 0) - return NULL; - } - if (dirp->dd_loc >= dirp->dd_size) { - dirp->dd_loc = 0; - continue; - } - dp = (struct dirent *)(dirp->dd_buf + dirp->dd_loc); - if ((int)dp & 03) /* bogus pointer check */ - return NULL; - if (dp->d_reclen <= 0 || - dp->d_reclen > dirp->dd_len + 1 - dirp->dd_loc) - return NULL; - dirp->dd_loc += dp->d_reclen; - if (dp->d_ino == 0) - continue; - return (dp); - } -} diff --git a/lib/libc/gen/rewinddir.c b/lib/libc/gen/rewinddir.c deleted file mode 100644 index d52c013c4d..0000000000 --- a/lib/libc/gen/rewinddir.c +++ /dev/null @@ -1,49 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)rewinddir.c 5.1 (Berkeley) 5/25/90"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include - -void -rewinddir(dirp) - DIR *dirp; -{ - extern long _rewinddir; - - _seekdir((dirp), _rewinddir); - _rewinddir = telldir(dirp); -} diff --git a/lib/libc/gen/seekdir.c b/lib/libc/gen/seekdir.c deleted file mode 100644 index 50331402ab..0000000000 --- a/lib/libc/gen/seekdir.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 1983 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)seekdir.c 5.7 (Berkeley) 6/1/90"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include - -/* - * Seek to an entry in a directory. - * _seekdir is in telldir.c so that it can share opaque data structures. - */ -void -seekdir(dirp, loc) - DIR *dirp; - long loc; -{ - - _seekdir(dirp, loc); -} diff --git a/lib/libc/gen/setjmp.3 b/lib/libc/gen/setjmp.3 index 4d0cd59d25..feb21cb20e 100644 --- a/lib/libc/gen/setjmp.3 +++ b/lib/libc/gen/setjmp.3 @@ -44,7 +44,8 @@ .Nm setjmp , .Nm longjmp , .Nm _setjmp , -.Nm _longjmp longjmperror +.Nm _longjmp , +.Nm longjmperror .Nd non-local jumps .Sh SYNOPSIS .Fd #include diff --git a/lib/libc/gen/shmat.c b/lib/libc/gen/shmat.c new file mode 100644 index 0000000000..27dc0d62d7 --- /dev/null +++ b/lib/libc/gen/shmat.c @@ -0,0 +1,19 @@ +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$Id: shmat.c,v 1.1 1993/09/27 00:57:45 rgrimes Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +#if __STDC__ +void *shmat(int shmid, void *shmaddr, int shmflg) +#else +void *shmat(shmid, shmaddr, shmflg) + int shmid; + void *shmaddr; + int shmflg; +#endif +{ + return ((void *)shmsys(0, shmid, shmaddr, shmflg)); +} diff --git a/lib/libc/gen/shmctl.c b/lib/libc/gen/shmctl.c new file mode 100644 index 0000000000..acbe09d3a6 --- /dev/null +++ b/lib/libc/gen/shmctl.c @@ -0,0 +1,19 @@ +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$Id: shmctl.c,v 1.1 1993/09/27 00:57:47 rgrimes Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +#if __STDC__ +int shmctl(int shmid, int cmd, void *buf) +#else +int shmctl(shmid, cmd, buf) + int shmid; + int cmd; + void *buf; +#endif +{ + return (shmsys(1, shmid, cmd, buf)); +} diff --git a/lib/libc/gen/shmdt.c b/lib/libc/gen/shmdt.c new file mode 100644 index 0000000000..4829070038 --- /dev/null +++ b/lib/libc/gen/shmdt.c @@ -0,0 +1,17 @@ +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$Id: shmdt.c,v 1.1 1993/09/27 00:57:48 rgrimes Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +#if __STDC__ +int shmdt(void *shmaddr) +#else +int shmdt(shmaddr) + void *shmaddr; +#endif +{ + return (shmsys(2, shmaddr)); +} diff --git a/lib/libc/gen/shmget.c b/lib/libc/gen/shmget.c new file mode 100644 index 0000000000..5c91801824 --- /dev/null +++ b/lib/libc/gen/shmget.c @@ -0,0 +1,19 @@ +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$Id: shmget.c,v 1.2 1993/08/26 15:26:21 brezak Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +#if __STDC__ +int shmget(key_t key, int size, int shmflg) +#else +int shmget(key, size, shmflg) + key_t key; + int size; + int shmflg; +#endif +{ + return (shmsys(3, key, size, shmflg)); +} diff --git a/lib/libc/gen/signal.c b/lib/libc/gen/signal.c index 1b5efd7669..bf585b908d 100644 --- a/lib/libc/gen/signal.c +++ b/lib/libc/gen/signal.c @@ -55,6 +55,6 @@ signal(s, a) if (!sigismember(&_sigintr, s)) sa.sa_flags |= SA_RESTART; if (sigaction(s, &sa, &osa) < 0) - return (BADSIG); + return (SIG_ERR); return (osa.sa_handler); } diff --git a/lib/libc/gen/syslog.c b/lib/libc/gen/syslog.c index 59212a7ee0..09d8ae8140 100644 --- a/lib/libc/gen/syslog.c +++ b/lib/libc/gen/syslog.c @@ -32,26 +32,28 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)syslog.c 5.34 (Berkeley) 6/26/91"; +static char sccsid[] = "@(#)syslog.c 5.36 (Berkeley) 10/4/92"; #endif /* LIBC_SCCS and not lint */ #include #include -#include #include #include -#include #include + +#include +#include +#include +#include #include +#include +#include + #if __STDC__ #include #else #include #endif -#include -#include -#include -#include static int LogFile = -1; /* fd for log */ static int connected; /* have done connect */ @@ -93,13 +95,20 @@ vsyslog(pri, fmt, ap) { register int cnt; register char *p; - time_t now, time(); + time_t now; int fd, saved_errno; - char tbuf[2048], fmt_cpy[1024], *stdp, *ctime(); + char *stdp, tbuf[2048], fmt_cpy[1024]; + +#define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID + /* Check for invalid bits. */ + if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) { + syslog(INTERNALLOG, + "syslog: unknown facility/priority: %x", pri); + pri &= LOG_PRIMASK|LOG_FACMASK; + } - /* check for invalid bits or no priority set */ - if (!LOG_PRI(pri) || (pri &~ (LOG_PRIMASK|LOG_FACMASK)) || - !(LOG_MASK(pri) & LogMask)) + /* Check priority against setlogmask values. */ + if (!LOG_MASK(LOG_PRI(pri)) & LogMask) return; saved_errno = errno; @@ -224,6 +233,7 @@ closelog() } /* setlogmask -- set the log mask level */ +int setlogmask(pmask) int pmask; { diff --git a/lib/libc/gen/tcsendbreak.3 b/lib/libc/gen/tcsendbreak.3 new file mode 100644 index 0000000000..99d7d45887 --- /dev/null +++ b/lib/libc/gen/tcsendbreak.3 @@ -0,0 +1,154 @@ +.\" Copyright (c) 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)tcsendbreak.3 5.3 (Berkeley) 3/4/92 +.\" +.Dd "March 4, 1992" +.Dt TCSENDBREAK 3 +.Os +.Sh NAME +.Nm tcsendbreak , +.Nm tcdrain , +.Nm tcflush , +.Nm tcflow +.Nd line control functions +.Sh SYNOPSIS +.Fd #include +.Ft int +.Fn tcdrain "int fd" +.Ft int +.Fn tcflow "int fd" "int action" +.Ft int +.Fn tcflush "int fd" "int action" +.Ft int +.Fn tcsendbreak "int fd" "int len" +.Sh DESCRIPTION +The +.Nm tcdrain +function waits until all output written to the terminal referenced by +.Fa fd +has been transmitted to the terminal. +.Pp +The +.Nm tcflow +function suspends transmission of data to or the reception of data from +the terminal referenced by +.Fa fd +depending on the value of +.Fa action . +The value of +.Fa action +must be one of the following: +.Bl -tag -width "TCIOFF" +.It Fa TCOOFF +Suspend output. +.It Fa TCOON +Restart suspended output. +.It Fa TCIOFF +Transmit a STOP character, which is intended to cause the terminal to stop +transmitting data to the system. +(See the description of IXOFF in the +.Ql Input Modes +section of +.Xr termios 4 ). +.It Fa TCION +Transmit a START character, which is intended to cause the terminal to start +transmitting data to the system. +(See the description of IXOFF in the +.Ql Input Modes +section of +.Xr termios 4 ). +.El +.Pp +The +.Nm tcflush +function discards any data written to the terminal referenced by +.Fa fd +which has not been transmitted to the terminal, or any data received +from the terminal but not yet read, depending on the value of +.Fa action . +The value of +.Fa action +must be one of the following: +.Bl -tag -width "TCIOFLUSH" +.It Fa TCIFLUSH +Flush data received but not read. +.It Fa TCOFLUSH +Flush data written but not transmitted. +.It Fa TCIOFLUSH +Flush both data received but not read and data written but not transmitted. +.El +.Pp +The +.Nm tcsendbreak +function transmits a continuous stream of zero-valued bits for four-tenths +of a second to the terminal referenced by +.Fa fd . +The +.Fa len +parameter is ignored in this implementation. +.Sh RETURN VALUES +Upon successful completion, all of these functions return a value of zero. +.Sh ERRORS +If any error occurs, a value of -1 is returned and the global variable +.Va errno +is set to indicate the error, as follows: +.Bl -tag -width Er +.It Bq Er EBADF +The +.Fa fd +argument is not a valid file descriptor. +.It Bq Er EINVAL +The +.Fa action +argument is not a proper value. +.It Bq Er ENOTTY +The file associated with +.Fa fd +is not a terminal. +.It Bq Er EINTR +A signal interrupted the +.Nm tcdrain +function. +.El +.Sh SEE ALSO +.Xr tcsetattr 3 , +.Xr termios 4 +.Sh STANDARDS +The +.Nm tcsendbreak , +.Nm tcdrain , +.Nm tcflush +and +.Nm tcflow +functions are expected to be compliant with the +.St -p1003.1-88 +specification. diff --git a/lib/libc/gen/telldir.c b/lib/libc/gen/telldir.c deleted file mode 100644 index 37d9de6389..0000000000 --- a/lib/libc/gen/telldir.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 1983 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)telldir.c 5.9 (Berkeley) 2/23/91"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include -#include -#include - -/* - * The option SINGLEUSE may be defined to say that a telldir - * cookie may be used only once before it is freed. This option - * is used to avoid having memory usage grow without bound. - */ -#define SINGLEUSE - -/* - * One of these structures is malloced to describe the current directory - * position each time telldir is called. It records the current magic - * cookie returned by getdirentries and the offset within the buffer - * associated with that return value. - */ -struct ddloc { - struct ddloc *loc_next;/* next structure in list */ - long loc_index; /* key associated with structure */ - long loc_seek; /* magic cookie returned by getdirentries */ - long loc_loc; /* offset of entry in buffer */ -}; - -#define NDIRHASH 32 /* Num of hash lists, must be a power of 2 */ -#define LOCHASH(i) ((i)&(NDIRHASH-1)) - -static long dd_loccnt; /* Index of entry for sequential readdir's */ -static struct ddloc *dd_hash[NDIRHASH]; /* Hash list heads for ddlocs */ - -/* - * return a pointer into a directory - */ -long -telldir(dirp) - const DIR *dirp; -{ - register int index; - register struct ddloc *lp; - - if ((lp = (struct ddloc *)malloc(sizeof(struct ddloc))) == NULL) - return (-1); - index = dd_loccnt++; - lp->loc_index = index; - lp->loc_seek = dirp->dd_seek; - lp->loc_loc = dirp->dd_loc; - lp->loc_next = dd_hash[LOCHASH(index)]; - dd_hash[LOCHASH(index)] = lp; - return (index); -} - -/* - * seek to an entry in a directory. - * Only values returned by "telldir" should be passed to seekdir. - */ -void -_seekdir(dirp, loc) - register DIR *dirp; - long loc; -{ - register struct ddloc *lp; - register struct ddloc **prevlp; - struct dirent *dp; - extern long lseek(); - - prevlp = &dd_hash[LOCHASH(loc)]; - lp = *prevlp; - while (lp != NULL) { - if (lp->loc_index == loc) - break; - prevlp = &lp->loc_next; - lp = lp->loc_next; - } - if (lp == NULL) - return; - if (lp->loc_loc == dirp->dd_loc && lp->loc_seek == dirp->dd_seek) - goto found; - (void) lseek(dirp->dd_fd, lp->loc_seek, 0); - dirp->dd_seek = lp->loc_seek; - dirp->dd_loc = 0; - while (dirp->dd_loc < lp->loc_loc) { - dp = readdir(dirp); - if (dp == NULL) - break; - } -found: -#ifdef SINGLEUSE - *prevlp = lp->loc_next; - free((caddr_t)lp); -#endif -} diff --git a/lib/libc/gen/ttyname.c b/lib/libc/gen/ttyname.c index 80071f3a50..3b308a31fa 100644 --- a/lib/libc/gen/ttyname.c +++ b/lib/libc/gen/ttyname.c @@ -67,7 +67,7 @@ ttyname(fd) if (fstat(fd, &sb) || !S_ISCHR(sb.st_mode)) return(NULL); - if (db = hash_open(_PATH_DEVDB, O_RDONLY, 0, NULL)) { + if (db = dbopen(_PATH_DEVDB, O_RDONLY, 0, DB_HASH, NULL)) { bkey.type = S_IFCHR; bkey.dev = sb.st_rdev; key.data = &bkey; diff --git a/lib/libc/gen/utime.3 b/lib/libc/gen/utime.3 index faf96e2f6b..1162470e73 100644 --- a/lib/libc/gen/utime.3 +++ b/lib/libc/gen/utime.3 @@ -31,7 +31,7 @@ .\" .\" @(#)utime.3 6.3 (Berkeley) 4/19/91 .\" -.Dd April 19, 1991 +.Dd August 13, 1993 .Dt UTIME 3 .Os BSD 4 .Sh NAME @@ -42,25 +42,92 @@ .Ft int .Fn utime "const char *file" "const struct utimbuf *timep" .Sh DESCRIPTION -.Bf -symbolic -This interface is obsoleted by utimes(2) . -It is available from the compatibility library, libcompat. -.Ef -.Pp -.Sh DESCRIPTION The .Fn utime -function sets the access and modification times of the named file from -the structures in the argument array -.Fa timep . +function sets the access and modification times of the named file. +.Pp +If +.Fa timep +is +.Dv NULL , +the access and modification times are set to the current time. +The calling process must be the owner of the file or have permission to +write the file. +.Pp +If +.Fa timep +is +.Pf non- Dv NULL , +.Fa time +is assumed to be a pointer to a utimbuf structure, as defined in +.Aq Pa utime.h : +.Bd -literal -offset indent +struct utimbuf { + time_t actime; /* Access time */ + time_t modtime; /* Modification time */ +} ; +.Ed .Pp -The calling process's effective user ID must match the owner of the -file or must be the super-user. +The access time is set to the value of the actime member, and the modification +time is set to the value of the modtime member. The times are measured in +seconds since 0 hours, 0 minutes, 0 seconds, January 1, 1970, Coordinated +Universal Time. +The calling process must be the owner of the file or be the super-user. +.Pp +In either case, the inode-change-time of the file is set to the current +time. +.Sh RETURN VALUES +Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and +.Va errno +is set to indicate the error. +.Sh ERRORS +.Fn Utime +will fail if: +.Bl -tag -width Er +.It Bq Er EACCES +Search permission is denied for a component of the path prefix; +or the +.Fa times +argument is +.Dv NULL +and the effective user ID of the process does not +match the owner of the file, and is not the super-user, and write +access is denied. +.It Bq Er EFAULT +.Xr File +or +.Fa times +points outside the process's allocated address space. +.It Bq Er EINVAL +The pathname contains a character with the high-order bit set. +.It Bq Er EIO +An I/O error occurred while reading or writing the affected inode. +.It Bq Er ELOOP +Too many symbolic links were encountered in translating the pathname. +.It Bq Er ENAMETOOLONG +A component of a pathname exceeded 255 characters, +or an entire path name exceeded 1023 characters. +.It Bq Er ENOENT +The named file does not exist. +.It Bq Er ENOTDIR +A component of the path prefix is not a directory. +.It Bq Er EPERM +The +.Fa times +argument is not +.Dv NULL +and the calling process's effective user ID +does not match the owner of the file and is not the super-user. +.It Bq Er EROFS +The file system containing the file is mounted read-only. +.El .Sh SEE ALSO -.Xr utimes 2 , -.Xr stat 2 -.Sh HISTORY -A +.Xr stat 2 , +.Xr utimes 2 +.Sh STANDARDS +The .Fn utime -function appeared in -.At v7 . +function +conforms to +.St -p1003.1-88 . diff --git a/lib/libc/gen/utime.c b/lib/libc/gen/utime.c index 99b969b4cd..95057d9dfc 100644 --- a/lib/libc/gen/utime.c +++ b/lib/libc/gen/utime.c @@ -32,7 +32,8 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)utime.c 5.4 (Berkeley) 2/23/91"; +/*static char sccsid[] = "from: @(#)utime.c 5.4 (Berkeley) 2/23/91";*/ +static char rcsid[] = "$Id: utime.c,v 1.3 1993/08/13 23:58:50 jtc Exp $"; #endif /* LIBC_SCCS and not lint */ #include @@ -45,8 +46,12 @@ utime(path, times) { struct timeval tv[2]; - tv[0].tv_sec = times->actime; - tv[1].tv_sec = times->modtime; - tv[0].tv_usec = tv[1].tv_usec = 0; - return(utimes(path, tv)); + if (times == (struct utimbuf *) NULL) { + return(utimes(path, (struct timeval *) NULL)); + } else { + tv[0].tv_sec = times->actime; + tv[1].tv_sec = times->modtime; + tv[0].tv_usec = tv[1].tv_usec = 0; + return(utimes(path, tv)); + } } diff --git a/lib/libc/gen/vis.c b/lib/libc/gen/vis.c index 0609c26653..524db03e10 100644 --- a/lib/libc/gen/vis.c +++ b/lib/libc/gen/vis.c @@ -32,10 +32,11 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)vis.c 5.4 (Berkeley) 2/23/91"; +static char sccsid[] = "@(#)vis.c 5.6 (Berkeley) 2/5/92"; #endif /* LIBC_SCCS and not lint */ #include +#include #include #include @@ -45,16 +46,12 @@ static char sccsid[] = "@(#)vis.c 5.4 (Berkeley) 2/23/91"; * vis - visually encode characters */ char * -#if __STDC__ -vis(register char *dst, register char c, register int flag, char nextc) -#else vis(dst, c, flag, nextc) - register char *dst, c; - char nextc; + register char *dst; + int c, nextc; register int flag; -#endif { - if (isascii(c) && isgraph(c) || + if ((u_int)c <= UCHAR_MAX && isgraph(c) || ((flag & VIS_SP) == 0 && c == ' ') || ((flag & VIS_TAB) == 0 && c == '\t') || ((flag & VIS_NL) == 0 && c == '\n') || @@ -159,11 +156,11 @@ strvis(dst, src, flag) int flag; { register char c; - char *start = dst; - - for (;c = *src; src++) - dst = vis(dst, c, flag, *(src+1)); + char *start; + for (start = dst; c = *src;) + dst = vis(dst, c, flag, *++src); + *dst = '\0'; return (dst - start); } diff --git a/lib/libc/i386/DEFS.h b/lib/libc/i386/DEFS.h index 6b888c470a..ee12074cc8 100644 --- a/lib/libc/i386/DEFS.h +++ b/lib/libc/i386/DEFS.h @@ -33,15 +33,30 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)DEFS.h 5.1 (Berkeley) 4/23/90 + * from: @(#)DEFS.h 5.1 (Berkeley) 4/23/90 + * + * $Id$ */ #ifdef PROF -#define ENTRY(x) .globl _/**/x; _/**/x: \ - .data; 1:; .long 0; .text; lea 1b,%eax ; call mcount -#define ASENTRY(x) .globl x; x: \ - .data; 1:; .long 0; .text; lea 1b,%eax ; call mcount +#define ALTENTRY(x) .align 2,0x90; .globl _/**/x; _/**/x:; \ + .data; .align 2; 1:; .long 0; \ + .text; lea 1b,%eax; call mcount; jmp 2f + +#define ENTRY(x) .align 2,0x90; .globl _/**/x; _/**/x:; \ + .data; .align 2; 1:; .long 0; \ + .text; lea 1b,%eax ; call mcount; 2: + +#define ALTASENTRY(x) .align 2,0x90; .globl x; x:; \ + .data; .align 2; 1:; .long 0; \ + .text; lea 1b,%eax; call mcount; jmp 2f + +#define ASENTRY(x) .align 2,0x90; .globl x; x:; \ + .data; .align 2; 1:; .long 0; \ + .text; lea 1b,%eax; call mcount; 2: #else -#define ENTRY(x) .globl _/**/x; _/**/x: -#define ASENTRY(x) .globl x; x: +#define ALTENTRY(x) .align 2,0x90; .globl _/**/x; _/**/x: +#define ENTRY(x) .align 2,0x90; .globl _/**/x; _/**/x: +#define ALTASENTRY(x) .align 2,0x90; .globl x; x: +#define ASENTRY(x) .align 2,0x90; .globl x; x: #endif diff --git a/lib/libc/i386/SYS.h b/lib/libc/i386/SYS.h index b00c539d06..b7d8ce4d4d 100644 --- a/lib/libc/i386/SYS.h +++ b/lib/libc/i386/SYS.h @@ -33,17 +33,19 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)SYS.h 5.5 (Berkeley) 5/7/91 + * from: @(#)SYS.h 5.5 (Berkeley) 5/7/91 + * + * $Id$ */ #include #ifdef PROF #define ENTRY(x) .globl _/**/x; \ - .data; 1:; .long 0; .text; .align 2; _/**/x: \ + .data; 1:; .long 0; .text; .align 2,0x90; _/**/x: \ movl $1b,%eax; call mcount #else -#define ENTRY(x) .globl _/**/x; .text; .align 2; _/**/x: +#define ENTRY(x) .globl _/**/x; .text; .align 2,0x90; _/**/x: #endif PROF #define SYSCALL(x) 2: jmp cerror; ENTRY(x); lea SYS_/**/x,%eax; LCALL(7,0); jb 2b #define RSYSCALL(x) SYSCALL(x); ret diff --git a/lib/libc/i386/gen/_setjmp.s b/lib/libc/i386/gen/_setjmp.s index fb58d1fa5a..7aa3e69f84 100644 --- a/lib/libc/i386/gen/_setjmp.s +++ b/lib/libc/i386/gen/_setjmp.s @@ -36,6 +36,7 @@ #if defined(LIBC_SCCS) && !defined(lint) .asciz "@(#)_setjmp.s 5.1 (Berkeley) 4/23/90" + .align 2,0x90 #endif /* LIBC_SCCS and not lint */ /* @@ -44,7 +45,7 @@ * _longjmp(a,v) * will generate a "return(v)" from the last call to * _setjmp(a) - * by restoring registers from the stack. + * by restoring registers from the environment 'a'. * The previous signal state is NOT restored. */ @@ -59,7 +60,8 @@ ENTRY(_setjmp) movl %ebp,12(%eax) movl %esi,16(%eax) movl %edi,20(%eax) - movl $0,%eax + fnstcw 28(%eax) + xorl %eax,%eax ret ENTRY(_longjmp) @@ -71,8 +73,10 @@ ENTRY(_longjmp) movl 12(%edx),%ebp movl 16(%edx),%esi movl 20(%edx),%edi - cmpl $0,%eax - jne 1f - movl $1,%eax + fninit + fldcw 28(%edx) + testl %eax,%eax + jnz 1f + incl %eax 1: movl %ecx,0(%esp) ret diff --git a/lib/libc/i386/gen/fixdfsi.s b/lib/libc/i386/gen/fixdfsi.s index 05834665c7..a264e83a37 100644 --- a/lib/libc/i386/gen/fixdfsi.s +++ b/lib/libc/i386/gen/fixdfsi.s @@ -1,46 +1,19 @@ -/*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * William Jolitz. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) - .asciz "@(#)fixdfsi.s 5.4 (Berkeley) 4/12/91" -#endif /* LIBC_SCCS and not lint */ - - .globl ___fixdfsi + .file "__fixdfsi.s" +.text + .align 2 +.globl ___fixdfsi ___fixdfsi: - fldl 4(%esp) - fistpl 4(%esp) - movl 4(%esp),%eax + pushl %ebp + movl %esp,%ebp + subl $12,%esp + fstcw -4(%ebp) + movw -4(%ebp),%ax + orw $0x0c00,%ax + movw %ax,-2(%ebp) + fldcw -2(%ebp) + fldl 8(%ebp) + fistpl -12(%ebp) + fldcw -4(%ebp) + movl -12(%ebp),%eax + leave ret diff --git a/lib/libc/i386/gen/fixunsdfsi.s b/lib/libc/i386/gen/fixunsdfsi.s index d3486c4b8a..9f31df35d2 100644 --- a/lib/libc/i386/gen/fixunsdfsi.s +++ b/lib/libc/i386/gen/fixunsdfsi.s @@ -1,47 +1,36 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * William Jolitz. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ + .file "__fixdfsi.s" +.text + .align 2 +.globl ___fixunsdfsi +___fixunsdfsi: + pushl %ebp + movl %esp,%ebp + subl $12,%esp + fstcw -4(%ebp) + movw -4(%ebp),%ax + orw $0x0c00,%ax + movw %ax,-2(%ebp) + fldcw -2(%ebp) + fldl 8(%ebp) + + fcoml fbiggestsigned /* bigger than biggest signed? */ + fstsw %ax + sahf + jnb 1f + + fistpl -12(%ebp) + movl -12(%ebp),%eax + jmp 2f + +1: fsubl fbiggestsigned /* reduce for proper conversion */ + fistpl -12(%ebp) /* convert */ + movl -12(%ebp),%eax + orl $0x80000000,%eax /* restore bias */ -#if defined(LIBC_SCCS) && !defined(lint) - .asciz "@(#)fixunsdfsi.s 5.1 12/17/90" -#endif /* LIBC_SCCS and not lint */ +2: fldcw -4(%ebp) + leave + ret - .globl ___fixunsdfsi -___fixunsdfsi: - fldl 4(%esp) /* argument double to accum stack */ - frndint /* create integer */ fcoml fbiggestsigned /* bigger than biggest signed? */ fstsw %ax sahf @@ -57,4 +46,5 @@ ___fixunsdfsi: orl $0x80000000,%eax /* restore bias */ ret + .data fbiggestsigned: .double 0r2147483648.0 diff --git a/lib/libc/i386/gen/ldexp.c b/lib/libc/i386/gen/ldexp.c index 4aa99834b0..f54b4392cd 100644 --- a/lib/libc/i386/gen/ldexp.c +++ b/lib/libc/i386/gen/ldexp.c @@ -55,8 +55,18 @@ ldexp (double value, int exp) { double temp, texp, temp2; texp = exp; +#ifdef __GNUC__ +#if __GNUC__ >= 2 + asm ("fscale " + : "=u" (temp2), "=t" (temp) + : "0" (texp), "1" (value)); +#else asm ("fscale ; fxch %%st(1) ; fstp%L1 %1 " : "=f" (temp), "=0" (temp2) : "0" (texp), "f" (value)); +#endif +#else +error unknown asm +#endif return (temp); } diff --git a/lib/libc/i386/gen/setjmp.s b/lib/libc/i386/gen/setjmp.s index 35394bb27b..5482caa245 100644 --- a/lib/libc/i386/gen/setjmp.s +++ b/lib/libc/i386/gen/setjmp.s @@ -36,6 +36,7 @@ #if defined(LIBC_SCCS) && !defined(lint) .asciz "@(#)setjmp.s 5.1 (Berkeley) 4/23/90" + .align 2,0x90 #endif /* LIBC_SCCS and not lint */ /* @@ -44,7 +45,7 @@ * longjmp(a,v) * will generate a "return(v)" from the last call to * setjmp(a) - * by restoring registers from the stack. + * by restoring registers from the environment 'a'. * The previous signal state is restored. */ @@ -63,13 +64,14 @@ ENTRY(setjmp) movl %esi,16(%ecx) movl %edi,20(%ecx) movl %eax,24(%ecx) - movl $0,%eax + fnstcw 28(%ecx) + xorl %eax,%eax ret ENTRY(longjmp) movl 4(%esp),%edx pushl 24(%edx) - call _sigsetmask + call _sigsetmask /* XXX this is not reentrant */ popl %eax movl 4(%esp),%edx movl 8(%esp),%eax @@ -79,8 +81,10 @@ ENTRY(longjmp) movl 12(%edx),%ebp movl 16(%edx),%esi movl 20(%edx),%edi - cmpl $0,%eax - jne 1f - movl $1,%eax + fninit + fldcw 28(%edx) + testl %eax,%eax + jnz 1f + incl %eax 1: movl %ecx,0(%esp) ret diff --git a/lib/libc/i386/gen/sigsetjmp.s b/lib/libc/i386/gen/sigsetjmp.s new file mode 100644 index 0000000000..2f54cd18a1 --- /dev/null +++ b/lib/libc/i386/gen/sigsetjmp.s @@ -0,0 +1,88 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * from: @(#)setjmp.s 5.1 (Berkeley) 4/23/90" + * $Id: sigsetjmp.s,v 1.1 1993/10/19 18:51:21 jtc Exp $ + */ + +#if defined(LIBC_SCCS) && !defined(lint) + .text + .asciz "$Id: sigsetjmp.s,v 1.1 1993/10/19 18:51:21 jtc Exp $" +#endif /* LIBC_SCCS and not lint */ + +#include "DEFS.h" + +ENTRY(sigsetjmp) + movl 8(%esp),%eax + movl 4(%esp),%ecx + movl %eax,24(%ecx) + testl %eax,%eax + jz 1f + pushl $0 + call _sigblock + addl $4,%esp + movl 4(%esp),%ecx + movl %eax,28(%ecx) +1: movl 0(%esp),%edx + movl %edx, 0(%ecx) + movl %ebx, 4(%ecx) + movl %esp, 8(%ecx) + movl %ebp,12(%ecx) + movl %esi,16(%ecx) + movl %edi,20(%ecx) + xorl %eax,%eax + ret + +ENTRY(siglongjmp) + movl 4(%esp),%edx + cmpl $0,24(%edx) + jz 1f + pushl 28(%edx) + call _sigsetmask + addl $4,%esp +1: movl 4(%esp),%edx + movl 8(%esp),%eax + movl 0(%edx),%ecx + movl 4(%edx),%ebx + movl 8(%edx),%esp + movl 12(%edx),%ebp + movl 16(%edx),%esi + movl 20(%edx),%edi + testl %eax,%eax + jnz 2f + incl %eax +2: movl %ecx,0(%esp) + ret diff --git a/lib/libc/i386/stdlib/abs.s b/lib/libc/i386/stdlib/abs.s index a1fbdccd53..3122fcb84f 100644 --- a/lib/libc/i386/stdlib/abs.s +++ b/lib/libc/i386/stdlib/abs.s @@ -35,6 +35,7 @@ */ #if defined(LIBC_SCCS) && !defined(lint) + .text .asciz "@(#)abs.s 5.2 (Berkeley) 12/17/90" #endif /* LIBC_SCCS and not lint */ diff --git a/lib/libc/i386/stdlib/atof.c b/lib/libc/i386/stdlib/atof.c deleted file mode 100644 index 2fba195118..0000000000 --- a/lib/libc/i386/stdlib/atof.c +++ /dev/null @@ -1,181 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * the Systems Programming Group of the University of Utah Computer - * Science Department. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00108 - * -------------------- ----- ---------------------- - * - * 27 Feb 93 Joerg Wunsch Implement strtod, fix atof. - * 28 Mar 93 Jordan Hubbard Fix stdtod bug with endp, remove warnings. - * - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)atof.c 5.2 (Berkeley) 4/12/91"; -#endif /* LIBC_SCCS and not lint */ - -/* - * simple atof() for IEEE 754 architectures - */ - -#include -#include -#include -#include -#include -#include - -static double twoemax = 9007199254740992.; /*2^53*/ - -/* attempt to be as exact as possible */ -static struct { - long low_word; - long high_word; -} exp5[] = { -#if BYTE_ORDER == BIG_ENDIAN - { 0x40140000, 0x00000000 }, /* 5 */ - { 0x40390000, 0x00000000 }, /* 25 */ - { 0x40838800, 0x00000000 }, /* 625 */ - { 0x4117d784, 0x00000000 }, /* 390625 */ - { 0x4241c379, 0x37e08000 }, /* 152587890625 */ - { 0x4493b8b5, 0xb5056e17 }, /* 2.3283064365387e+022 */ - { 0x49384f03, 0xe93ff9f5 }, /* 5.42101086242753e+044 */ - { 0x52827748, 0xf9301d32 }, /* 2.93873587705572e+089 */ - { 0x65154fdd, 0x7f73bf3b } /* 8.63616855509445e+178 */ -#else /* BYTE_ORDER == LITTLE_ENDIAN */ - { 0x00000000, 0x40140000 }, /* 5 */ - { 0x00000000, 0x40390000 }, /* 25 */ - { 0x00000000, 0x40838800 }, /* 625 */ - { 0x00000000, 0x4117d784 }, /* 390625 */ - { 0x37e08000, 0x4241c379 }, /* 152587890625 */ - { 0xb5056e17, 0x4493b8b5 }, /* 2.3283064365387e+022 */ - { 0xe93ff9f5, 0x49384f03 }, /* 5.42101086242753e+044 */ - { 0xf9301d32, 0x52827748 }, /* 2.93873587705572e+089 */ - { 0x7f73bf3b, 0x65154fdd } /* 8.63616855509445e+178 */ -#endif -}; - -double -strtod(p, endp) - register const char *p; - char **endp; -{ - register int c; - register int exp = 0; - register int eexp = 0; - double fl = 0; - double flexp = 1.0; - int bexp; - int neg = 1; - int negexp = 1; - const char *oldp = p; - - while (isspace(*p)) - ++p; - - if ((c = *p++) == '-') - neg = -1; - else if (c == '+') - /* skip it */; - else - --p; - - while ((c = *p++) && isdigit(c)) - if (fl < twoemax) - fl = 10 * fl + (c-'0'); - else - ++exp; - - if (c == '.') - while ((c = *p++) && isdigit(c)) - if (fl < twoemax) { - fl = 10 * fl + (c-'0'); - --exp; - } - - if (c == 'E' || c == 'e') { - if ((c = *p++) == '-') - negexp = -1; - else if (c == '+') - /* skip it */; - else - --p; - while ((c = *p++) && isdigit(c)) - eexp = 10 * eexp + (c-'0'); - if (negexp < 0) - eexp = -eexp; - exp += eexp; - } - - bexp = exp; - if (exp < 0) - exp = -exp; - - for (c = 0; exp && c < sizeof exp5 / sizeof exp5[0]; ++c) { - if (exp & 1) - flexp *= *(double *)&exp5[c]; - exp >>= 1; - } - - /* according to ANSI, check for over-/underflow */ - if(exp > 0) { - if(endp) - *endp = (char *)oldp; - errno = ERANGE; - return neg < 0? -HUGE_VAL: HUGE_VAL; - } - - if (bexp < 0) - fl /= flexp; - else - fl *= flexp; - - fl = ldexp(fl, bexp); - - if(endp) - *endp = (char *)(p - 1); - return neg < 0 ? -fl : fl; -} - - -double -atof(p) - const char *p; -{ - return strtod(p, (char **)NULL); -} - diff --git a/lib/libc/i386/stdlib/div.s b/lib/libc/i386/stdlib/div.s new file mode 100644 index 0000000000..7c640ed012 --- /dev/null +++ b/lib/libc/i386/stdlib/div.s @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1993 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: ffs.s,v 1.1 1993/08/16 18:40:28 jtc Exp $ + */ + +#if defined(LIBC_RCS) && !defined(lint) + .text + .asciz "$Id: ffs.s,v 1.1 1993/08/16 18:40:28 jtc Exp $" +#endif /* LIBC_RCS and not lint */ + +#include "DEFS.h" + +ENTRY(div) + movl 4(%esp),%eax + movl 8(%esp),%ecx + cdq + idiv %ecx + movl %eax,4(%esp) + movl %edx,8(%esp) + ret diff --git a/lib/libc/i386/stdlib/labs.s b/lib/libc/i386/stdlib/labs.s new file mode 100644 index 0000000000..b7a1c5dcee --- /dev/null +++ b/lib/libc/i386/stdlib/labs.s @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) + .text + .asciz "@(#)abs.s 5.2 (Berkeley) 12/17/90" +#endif /* LIBC_SCCS and not lint */ + +#include "DEFS.h" + +ENTRY(labs) + movl 4(%esp),%eax + cmpl $0,%eax + jge 1f + negl %eax +1: ret diff --git a/lib/libc/i386/stdlib/ldiv.s b/lib/libc/i386/stdlib/ldiv.s new file mode 100644 index 0000000000..d79151f756 --- /dev/null +++ b/lib/libc/i386/stdlib/ldiv.s @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1993 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: ffs.s,v 1.1 1993/08/16 18:40:28 jtc Exp $ + */ + +#if defined(LIBC_RCS) && !defined(lint) + .text + .asciz "$Id: ffs.s,v 1.1 1993/08/16 18:40:28 jtc Exp $" +#endif /* LIBC_RCS and not lint */ + +#include "DEFS.h" + +ENTRY(ldiv) + movl 4(%esp),%eax + movl 8(%esp),%ecx + cdq + idiv %ecx + movl %eax,4(%esp) + movl %edx,8(%esp) + ret diff --git a/lib/libc/i386/string/bcmp.s b/lib/libc/i386/string/bcmp.s new file mode 100644 index 0000000000..6861743924 --- /dev/null +++ b/lib/libc/i386/string/bcmp.s @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1993 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: bcmp.s,v 1.1 1993/08/16 18:40:23 jtc Exp $ + */ + +#if defined(LIBC_RCS) && !defined(lint) + .asciz "$Id: bcmp.s,v 1.1 1993/08/16 18:40:23 jtc Exp $" +#endif /* LIBC_RCS and not lint */ + +#include "DEFS.h" + +/* + * bcmp (void *b1, void *b2, size_t len) + * + * Written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + */ + +ENTRY(bcmp) + pushl %edi + pushl %esi + movl 12(%esp),%edi + movl 16(%esp),%esi + movl 20(%esp),%edx + xorl %eax,%eax /* clear return value */ + cld /* set compare direction forward */ + + movl %edx,%ecx /* compare by words */ + shrl $2,%ecx + repe + cmpsl + jne L1 + + movl %edx,%ecx /* compare remainder by bytes */ + andl $3,%ecx + repe + cmpsb + je L2 + +L1: incl %eax +L2: popl %esi + popl %edi + ret diff --git a/lib/libc/i386/string/bcopy.s b/lib/libc/i386/string/bcopy.s index 0ee464bdb6..8f686efa08 100644 --- a/lib/libc/i386/string/bcopy.s +++ b/lib/libc/i386/string/bcopy.s @@ -31,19 +31,22 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id: bcopy.s,v 1.2 1993/08/16 18:40:24 jtc Exp $ */ #if defined(LIBC_RCS) && !defined(lint) - .asciz "$ID$" + .asciz "$Id: bcopy.s,v 1.2 1993/08/16 18:40:24 jtc Exp $" #endif /* LIBC_RCS and not lint */ +#include "DEFS.h" + /* * (ov)bcopy (src,dst,cnt) * ws@tools.de (Wolfgang Solfrank, TooLs GmbH) +49-228-985800 */ - .globl _bcopy -_bcopy: +ENTRY(bcopy) pushl %esi pushl %edi movl 12(%esp),%esi @@ -61,7 +64,6 @@ _bcopy: movsb popl %edi popl %esi - xorl %eax,%eax ret 1: addl %ecx,%edi /* copy backwards. */ @@ -80,6 +82,5 @@ _bcopy: movsl popl %edi popl %esi - xorl %eax,%eax cld ret diff --git a/lib/libc/i386/string/bzero.s b/lib/libc/i386/string/bzero.s index d876e69325..9881d300a3 100644 --- a/lib/libc/i386/string/bzero.s +++ b/lib/libc/i386/string/bzero.s @@ -1,10 +1,7 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. +/* + * Copyright (c) 1993 Winning Strategies, Inc. * All rights reserved. * - * This code is derived from software contributed to Berkeley by - * William Jolitz. - * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -15,39 +12,75 @@ * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * $Id: bzero.s,v 1.6 1993/08/16 17:06:29 jtc Exp $ */ -#if defined(LIBC_SCCS) && !defined(lint) - .asciz "@(#)bzero.s 5.1 (Berkeley) 4/23/90" -#endif /* LIBC_SCCS and not lint */ +#if defined(LIBC_RCS) && !defined(lint) + .asciz "$Id: bzero.s,v 1.6 1993/08/16 17:06:29 jtc Exp $" +#endif /* LIBC_RCS and not lint */ -/* bzero (base,cnt) */ +#include "DEFS.h" - .globl _bzero -_bzero: +/* + * bzero (void *b, size_t len) + * write len zero bytes to the string b. + * + * Written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + */ + +ENTRY(bzero) pushl %edi - movl 8(%esp),%edi - movl 12(%esp),%ecx - movb $0x00,%al - cld + pushl %ebx + movl 12(%esp),%edi + movl 16(%esp),%ecx + + cld /* set fill direction forward */ + xorl %eax,%eax /* set fill data to 0 */ + + /* + * if the string is too short, it's really not worth the overhead + * of aligning to word boundries, etc. So we jump to a plain + * unaligned set. + */ + cmpl $0x0f,%ecx + jle L1 + + movl %edi,%edx /* compute misalignment */ + negl %edx + andl $3,%edx + movl %ecx,%ebx + subl %edx,%ebx + + movl %edx,%ecx /* zero until word aligned */ rep stosb + + movl %ebx,%ecx /* zero by words */ + shrl $2,%ecx + rep + stosl + + movl %ebx,%ecx + andl $3,%ecx /* zero remainder by bytes */ +L1: rep + stosb + + popl %ebx popl %edi ret diff --git a/lib/libc/i386/string/ffs.s b/lib/libc/i386/string/ffs.s new file mode 100644 index 0000000000..cd0322cf21 --- /dev/null +++ b/lib/libc/i386/string/ffs.s @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1993 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: ffs.s,v 1.4 1993/08/16 17:06:30 jtc Exp $ + */ + +#if defined(LIBC_RCS) && !defined(lint) + .asciz "$Id: ffs.s,v 1.4 1993/08/16 17:06:30 jtc Exp $" +#endif /* LIBC_RCS and not lint */ + +#include "DEFS.h" + +/* + * ffs(value) + * finds the first bit set in value and returns the index of + * that bit. Bits are numbered starting from 1, starting at the + * rightmost bit. A return value of 0 means that the argument + * was zero. + * + * Written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + */ + +ENTRY(ffs) + bsfl 4(%esp),%eax + jz L1 /* ZF is set if all bits are 0 */ + incl %eax /* bits numbered from 1, not 0 */ + ret + + .align 2 +L1: xorl %eax,%eax /* clear result */ + ret diff --git a/lib/libc/i386/string/index.s b/lib/libc/i386/string/index.s new file mode 100644 index 0000000000..585e8a1a17 --- /dev/null +++ b/lib/libc/i386/string/index.s @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1993 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: index.s,v 1.4 1993/08/16 17:06:31 jtc Exp $ + */ + +#if defined(LIBC_RCS) && !defined(lint) + .asciz "$Id: index.s,v 1.4 1993/08/16 17:06:31 jtc Exp $" +#endif /* LIBC_RCS and not lint */ + +#include "DEFS.h" + +/* + * index(s, c) + * return a pointer to the first occurance of the character c in + * string s, or NULL if c does not occur in the string. + * + * %edx - pointer iterating through string + * %eax - pointer to first occurance of 'c' + * %cl - character we're comparing against + * %bl - character at %edx + * + * Written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + */ + +ENTRY(index) + pushl %ebx + movl 8(%esp),%eax + movb 12(%esp),%cl + .align 2,0x90 +L1: + movb (%eax),%bl + cmpb %bl,%cl /* found char??? */ + je L2 + incl %eax + testb %bl,%bl /* null terminator??? */ + jne L1 + xorl %eax,%eax +L2: + popl %ebx + ret diff --git a/lib/libc/i386/string/memchr.s b/lib/libc/i386/string/memchr.s new file mode 100644 index 0000000000..0e3b1c4b28 --- /dev/null +++ b/lib/libc/i386/string/memchr.s @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1993 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: memchr.s,v 1.3 1993/08/16 17:06:32 jtc Exp $ + */ + +#if defined(LIBC_RCS) && !defined(lint) + .asciz "$Id: memchr.s,v 1.3 1993/08/16 17:06:32 jtc Exp $" +#endif /* LIBC_RCS and not lint */ + +#include "DEFS.h" + +/* + * memchr (b, c, len) + * locates the first occurance of c in string b. + * + * Written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + */ + +ENTRY(memchr) + pushl %edi + movl 8(%esp),%edi /* string address */ + movl 12(%esp),%eax /* set character to search for */ + movl 16(%esp),%ecx /* set length of search */ + testl %eax,%eax /* clear Z flag, for len == 0 */ + cld /* set search forward */ + repne /* search! */ + scasb + jnz L1 /* scan failed, return null */ + leal -1(%edi),%eax /* adjust result of scan */ + popl %edi + ret + .align 2,0x90 +L1: xorl %eax,%eax + popl %edi + ret diff --git a/lib/libc/i386/string/memcmp.s b/lib/libc/i386/string/memcmp.s new file mode 100644 index 0000000000..187b2e8773 --- /dev/null +++ b/lib/libc/i386/string/memcmp.s @@ -0,0 +1,81 @@ +/* + * Copyright (c) 1993 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: memcmp.s,v 1.1 1993/08/16 18:40:33 jtc Exp $ + */ + +#if defined(LIBC_RCS) && !defined(lint) + .asciz "$Id: memcmp.s,v 1.1 1993/08/16 18:40:33 jtc Exp $" +#endif /* LIBC_RCS and not lint */ + +#include "DEFS.h" + +/* + * memcmp (void *b1, void *b2, size_t len) + * + * Written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + */ + +ENTRY(memcmp) + pushl %edi + pushl %esi + movl 12(%esp),%edi + movl 16(%esp),%esi + movl 20(%esp),%edx + cld /* set compare direction forward */ + + movl %edx,%ecx /* compare by words */ + shrl $2,%ecx + repe + cmpsl + jne L5 /* do we match so far? */ + + movl %edx,%ecx /* compare remainder by bytes */ + andl $3,%ecx + repe + cmpsb + jne L6 /* do we match? */ + + xorl %eax,%eax /* we match, return zero */ + popl %esi + popl %edi + ret + +L5: movl $4,%ecx /* We know that one of the next */ + subl %ecx,%edi /* four pairs of bytes do not */ + subl %ecx,%esi /* match. */ + repe + cmpsb +L6: movsbl -1(%edi),%eax /* Perform unsigned comparison */ + movsbl -1(%esi),%edx + subl %edx,%eax + popl %esi + popl %edi + ret diff --git a/lib/libc/i386/string/memmove.s b/lib/libc/i386/string/memmove.s new file mode 100644 index 0000000000..6f21733402 --- /dev/null +++ b/lib/libc/i386/string/memmove.s @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: memmove.s,v 1.1 1993/08/16 18:40:36 jtc Exp $ + */ + +#if defined(LIBC_RCS) && !defined(lint) + .asciz "$Id: memmove.s,v 1.1 1993/08/16 18:40:36 jtc Exp $" +#endif /* LIBC_RCS and not lint */ + +#include "DEFS.h" + + /* + * (ov)bcopy (src,dst,cnt) + * ws@tools.de (Wolfgang Solfrank, TooLs GmbH) +49-228-985800 + */ + +ALTENTRY(memcpy) +ENTRY(memmove) + pushl %esi + pushl %edi + movl 12(%esp),%edi + pushl %edi + movl 20(%esp),%esi + movl 24(%esp),%ecx + cmpl %esi,%edi /* potentially overlapping? */ + jnb 1f + cld /* nope, copy forwards. */ + shrl $2,%ecx /* copy by words */ + rep + movsl + movl 24(%esp),%ecx + andl $3,%ecx /* any bytes left? */ + rep + movsb + popl %eax + popl %edi + popl %esi + ret +1: + addl %ecx,%edi /* copy backwards. */ + addl %ecx,%esi + std + andl $3,%ecx /* any fractional bytes? */ + decl %edi + decl %esi + rep + movsb + movl 24(%esp),%ecx /* copy remainder by words */ + shrl $2,%ecx + subl $3,%esi + subl $3,%edi + rep + movsl + popl %eax + popl %edi + popl %esi + cld + ret diff --git a/lib/libc/i386/string/memset.s b/lib/libc/i386/string/memset.s new file mode 100644 index 0000000000..9def702174 --- /dev/null +++ b/lib/libc/i386/string/memset.s @@ -0,0 +1,96 @@ +/* + * Copyright (c) 1993 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: memset.s,v 1.3 1993/08/16 17:06:35 jtc Exp $ + */ + +#if defined(LIBC_RCS) && !defined(lint) + .asciz "$Id: memset.s,v 1.3 1993/08/16 17:06:35 jtc Exp $" +#endif /* LIBC_RCS and not lint */ + +#include "DEFS.h" + +/* + * memset(void *b, int c, size_t len) + * write len bytes of value c (converted to an unsigned char) to + * the string b. + * + * Written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + */ + +ENTRY(memset) + pushl %edi + pushl %ebx + movl 12(%esp),%edi + movzbl 16(%esp),%eax /* unsigned char, zero extend */ + movl 20(%esp),%ecx + pushl %edi /* push address of buffer */ + + cld /* set fill direction forward */ + + /* + * if the string is too short, it's really not worth the overhead + * of aligning to word boundries, etc. So we jump to a plain + * unaligned set. + */ + cmpl $0x0f,%ecx + jle L1 + + movl %eax,%edx /* copy value to all bytes in word */ + sall $8,%edx /* XXX is there a better way? */ + orl %edx,%eax + movl %eax,%edx + sall $16,%edx + orl %edx,%eax + + movl %edi,%edx /* compute misalignment */ + negl %edx + andl $3,%edx + movl %ecx,%ebx + subl %edx,%ebx + + movl %edx,%ecx /* set until word aligned */ + rep + stosb + + movl %ebx,%ecx + shrl $2,%ecx /* set by words */ + rep + stosl + + movl %ebx,%ecx /* set remainder by bytes */ + andl $3,%ecx +L1: rep + stosb + + popl %eax /* pop address of buffer */ + popl %ebx + popl %edi + ret diff --git a/lib/libc/i386/string/rindex.s b/lib/libc/i386/string/rindex.s new file mode 100644 index 0000000000..7646fbf0b0 --- /dev/null +++ b/lib/libc/i386/string/rindex.s @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1993 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: rindex.s,v 1.4 1993/08/16 17:06:36 jtc Exp $ + */ + +#if defined(LIBC_RCS) && !defined(lint) + .asciz "$Id: rindex.s,v 1.4 1993/08/16 17:06:36 jtc Exp $" +#endif /* LIBC_RCS and not lint */ + +#include "DEFS.h" + +/* + * rindex(s, c) + * return a pointer to the last occurance of the character c in + * string s, or NULL if c does not occur in the string. + * + * %edx - pointer iterating through string + * %eax - pointer to last occurance of 'c' + * %cl - character we're comparing against + * %bl - character at %edx + * + * Written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + */ + +ENTRY(rindex) + pushl %ebx + movl 8(%esp),%edx + movb 12(%esp),%cl + xorl %eax,%eax /* init pointer to null */ + .align 2,0x90 +L1: + movb (%edx),%bl + cmpb %bl,%cl + jne L2 + movl %edx,%eax +L2: + incl %edx + testb %bl,%bl /* null terminator??? */ + jne L1 + popl %ebx + ret diff --git a/lib/libc/i386/string/strcat.s b/lib/libc/i386/string/strcat.s new file mode 100644 index 0000000000..7966b3da23 --- /dev/null +++ b/lib/libc/i386/string/strcat.s @@ -0,0 +1,105 @@ +/* + * Copyright (c) 1993 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: strcat.s,v 1.5 1993/08/16 17:06:37 jtc Exp $ + */ + +#if defined(LIBC_RCS) && !defined(lint) + .asciz "$Id: strcat.s,v 1.5 1993/08/16 17:06:37 jtc Exp $" +#endif /* LIBC_RCS and not lint */ + +#include "DEFS.h" + +/* + * strcat(s, append) + * append a copy of the null-terminated string "append" to the end + * of the null-terminated string s, then add a terminating `\0'. + * + * Written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + */ + +/* + * I've unrolled the loop eight times: large enough to make a + * significant difference, and small enough not to totally trash the + * cashe. + */ + +ENTRY(strcat) + pushl %edi /* save edi */ + movl 8(%esp),%edi /* dst address */ + movl 12(%esp),%edx /* src address */ + pushl %edi /* push destination address */ + + cld /* set search forward */ + xorl %eax,%eax /* set search for null terminator */ + movl $-1,%ecx /* set search for lots of characters */ + repne /* search! */ + scasb + + leal -1(%edi),%ecx /* correct dst address */ + + .align 2,0x90 +L1: movb (%edx),%al /* unroll loop, but not too much */ + movb %al,(%ecx) + testb %al,%al + je L2 + movb 1(%edx),%al + movb %al,1(%ecx) + testb %al,%al + je L2 + movb 2(%edx),%al + movb %al,2(%ecx) + testb %al,%al + je L2 + movb 3(%edx),%al + movb %al,3(%ecx) + testb %al,%al + je L2 + movb 4(%edx),%al + movb %al,4(%ecx) + testb %al,%al + je L2 + movb 5(%edx),%al + movb %al,5(%ecx) + testb %al,%al + je L2 + movb 6(%edx),%al + movb %al,6(%ecx) + testb %al,%al + je L2 + movb 7(%edx),%al + movb %al,7(%ecx) + addl $8,%edx + addl $8,%ecx + testb %al,%al + jne L1 +L2: popl %eax /* pop destination address */ + popl %edi /* restore edi */ + ret diff --git a/lib/libc/i386/string/strchr.s b/lib/libc/i386/string/strchr.s new file mode 100644 index 0000000000..471e8b8c13 --- /dev/null +++ b/lib/libc/i386/string/strchr.s @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1993 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: strchr.s,v 1.4 1993/08/16 17:06:38 jtc Exp $ + */ + +#if defined(LIBC_RCS) && !defined(lint) + .asciz "$Id: strchr.s,v 1.4 1993/08/16 17:06:38 jtc Exp $" +#endif /* LIBC_RCS and not lint */ + +#include "DEFS.h" + +/* + * strchr(s, c) + * return a pointer to the first occurance of the character c in + * string s, or NULL if c does not occur in the string. + * + * %edx - pointer iterating through string + * %eax - pointer to first occurance of 'c' + * %cl - character we're comparing against + * %bl - character at %edx + * + * Written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + */ + +ENTRY(strchr) + pushl %ebx + movl 8(%esp),%eax + movb 12(%esp),%cl + .align 2,0x90 +L1: + movb (%eax),%bl + cmpb %bl,%cl /* found char??? */ + je L2 + incl %eax + testb %bl,%bl /* null terminator??? */ + jne L1 + xorl %eax,%eax +L2: + popl %ebx + ret diff --git a/lib/libc/i386/string/strcmp.s b/lib/libc/i386/string/strcmp.s new file mode 100644 index 0000000000..9ae6f53071 --- /dev/null +++ b/lib/libc/i386/string/strcmp.s @@ -0,0 +1,124 @@ +/* + * Copyright (c) 1993 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: strcmp.s,v 1.1 1993/08/16 18:40:43 jtc Exp $ + */ + +#if defined(LIBC_RCS) && !defined(lint) + .asciz "$Id: strcmp.s,v 1.1 1993/08/16 18:40:43 jtc Exp $" +#endif /* LIBC_RCS and not lint */ + +#include "DEFS.h" + +/* + * strcmp(s1, s2) + * return an integer greater than, equal to, or less than 0, + * according as string s1 is greater than, equal to, or less + * than the string s2. + * + * %eax - pointer to s1 + * %edx - pointer to s2 + * + * Written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + */ + +/* + * I've unrolled the loop eight times: large enough to make a + * significant difference, and small enough not to totally trash the + * cashe. + */ + +ENTRY(strcmp) + movl 0x04(%esp),%eax + movl 0x08(%esp),%edx + jmp L2 /* Jump into the loop! */ + + .align 2,0x90 +L1: incl %eax + incl %edx +L2: movb (%eax),%cl + testb %cl,%cl + je L3 + cmpb %cl,(%edx) + jne L3 + incl %eax + incl %edx + movb (%eax),%cl + testb %cl,%cl + je L3 + cmpb %cl,(%edx) + jne L3 + incl %eax + incl %edx + movb (%eax),%cl + testb %cl,%cl + je L3 + cmpb %cl,(%edx) + jne L3 + incl %eax + incl %edx + movb (%eax),%cl + testb %cl,%cl + je L3 + cmpb %cl,(%edx) + jne L3 + incl %eax + incl %edx + movb (%eax),%cl + testb %cl,%cl + je L3 + cmpb %cl,(%edx) + jne L3 + incl %eax + incl %edx + movb (%eax),%cl + testb %cl,%cl + je L3 + cmpb %cl,(%edx) + jne L3 + incl %eax + incl %edx + movb (%eax),%cl + testb %cl,%cl + je L3 + cmpb %cl,(%edx) + jne L3 + incl %eax + incl %edx + movb (%eax),%cl + testb %cl,%cl + je L3 + cmpb %cl,(%edx) + je L1 + .align 2, 0x90 +L3: movsbl (%eax),%eax /* unsigned comparison */ + movsbl (%edx),%edx + subl %edx,%eax + ret diff --git a/lib/libc/i386/string/strcpy.s b/lib/libc/i386/string/strcpy.s new file mode 100644 index 0000000000..3bcc90e574 --- /dev/null +++ b/lib/libc/i386/string/strcpy.s @@ -0,0 +1,94 @@ +/* + * Copyright (c) 1993 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: strcpy.s,v 1.4 1993/08/16 17:06:40 jtc Exp $ + */ + +#if defined(LIBC_RCS) && !defined(lint) + .asciz "$Id: strcpy.s,v 1.4 1993/08/16 17:06:40 jtc Exp $" +#endif /* LIBC_RCS and not lint */ + +#include "DEFS.h" + +/* + * strcpy (dst, src) + * copy the string src to dst. + * + * Written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + */ + +/* + * I've unrolled the loop eight times: large enough to make a + * significant difference, and small enough not to totally trash the + * cashe. + */ + +ENTRY(strcpy) + movl 4(%esp),%ecx /* dst address */ + movl 8(%esp),%edx /* src address */ + pushl %ecx /* push dst address */ + + .align 2,0x90 +L1: movb (%edx),%al /* unroll loop, but not too much */ + movb %al,(%ecx) + testb %al,%al + je L2 + movb 1(%edx),%al + movb %al,1(%ecx) + testb %al,%al + je L2 + movb 2(%edx),%al + movb %al,2(%ecx) + testb %al,%al + je L2 + movb 3(%edx),%al + movb %al,3(%ecx) + testb %al,%al + je L2 + movb 4(%edx),%al + movb %al,4(%ecx) + testb %al,%al + je L2 + movb 5(%edx),%al + movb %al,5(%ecx) + testb %al,%al + je L2 + movb 6(%edx),%al + movb %al,6(%ecx) + testb %al,%al + je L2 + movb 7(%edx),%al + movb %al,7(%ecx) + addl $8,%edx + addl $8,%ecx + testb %al,%al + jne L1 +L2: popl %eax /* pop dst address */ + ret diff --git a/lib/libc/i386/string/strlen.s b/lib/libc/i386/string/strlen.s new file mode 100644 index 0000000000..178ce438b3 --- /dev/null +++ b/lib/libc/i386/string/strlen.s @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1993 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: strlen.s,v 1.1 1993/08/16 18:40:46 jtc Exp $ + */ + +#if defined(LIBC_RCS) && !defined(lint) + .asciz "$Id: strlen.s,v 1.1 1993/08/16 18:40:46 jtc Exp $" +#endif /* LIBC_RCS and not lint */ + +#include "DEFS.h" + +/* + * strlen (s) + * compute the length of the string s. + * + * Written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + */ + +ENTRY(strlen) + pushl %edi + movl 8(%esp),%edi /* string address */ + cld /* set search forward */ + xorl %eax,%eax /* set search for null terminator */ + movl $-1,%ecx /* set search for lots of characters */ + repne /* search! */ + scasb + notl %ecx /* get length by taking complement */ + leal -1(%ecx),%eax /* and subtracting one */ + popl %edi + ret diff --git a/lib/libc/i386/string/strncmp.s b/lib/libc/i386/string/strncmp.s new file mode 100644 index 0000000000..3db7767418 --- /dev/null +++ b/lib/libc/i386/string/strncmp.s @@ -0,0 +1,156 @@ +/* + * Copyright (c) 1993 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: strncmp.s,v 1.1 1993/08/16 18:40:48 jtc Exp $ + */ + +#if defined(LIBC_RCS) && !defined(lint) + .asciz "$Id: strncmp.s,v 1.1 1993/08/16 18:40:48 jtc Exp $" +#endif /* LIBC_RCS and not lint */ + +#include "DEFS.h" + +/* + * strncmp(s1, s2, n) + * return an integer greater than, equal to, or less than 0, + * according as the first n characters of string s1 is greater + * than, equal to, or less than the string s2. + * + * %eax - pointer to s1 + * %ecx - pointer to s2 + * %edx - length + * + * Written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + */ + +/* + * I've unrolled the loop eight times: large enough to make a + * significant difference, and small enough not to totally trash the + * cashe. + */ + +ENTRY(strncmp) + pushl %ebx + movl 8(%esp),%eax + movl 12(%esp),%ecx + movl 16(%esp),%edx + jmp L2 /* Jump into the loop! */ + + .align 2,0x90 +L1: incl %eax + incl %ecx + decl %edx +L2: testl %edx,%edx /* Have we compared n chars yet? */ + jle L4 /* Yes, strings are equal */ + movb (%eax),%bl + testb %bl,%bl + je L3 + cmpb %bl,(%ecx) + jne L3 + incl %eax + incl %ecx + decl %edx + testl %edx,%edx + jle L4 + movb (%eax),%bl + testb %bl,%bl + je L3 + cmpb %bl,(%ecx) + jne L3 + incl %eax + incl %ecx + decl %edx + testl %edx,%edx + jle L4 + movb (%eax),%bl + testb %bl,%bl + je L3 + cmpb %bl,(%ecx) + jne L3 + incl %eax + incl %ecx + decl %edx + testl %edx,%edx + jle L4 + movb (%eax),%bl + testb %bl,%bl + je L3 + cmpb %bl,(%ecx) + jne L3 + incl %eax + incl %ecx + decl %edx + testl %edx,%edx + jle L4 + movb (%eax),%bl + testb %bl,%bl + je L3 + cmpb %bl,(%ecx) + jne L3 + incl %eax + incl %ecx + decl %edx + testl %edx,%edx + jle L4 + movb (%eax),%bl + testb %bl,%bl + je L3 + cmpb %bl,(%ecx) + jne L3 + incl %eax + incl %ecx + decl %edx + testl %edx,%edx + jle L4 + movb (%eax),%bl + testb %bl,%bl + je L3 + cmpb %bl,(%ecx) + jne L3 + incl %eax + incl %ecx + decl %edx + testl %edx,%edx + jle L4 + movb (%eax),%bl + testb %bl,%bl + je L3 + cmpb %bl,(%ecx) + je L1 + .align 2,0x90 +L3: movsbl (%eax),%eax /* unsigned comparision */ + movsbl (%ecx),%ecx + subl %ecx,%eax + popl %ebx + ret + .align 2,0x90 +L4: xorl %eax,%eax + popl %ebx + ret diff --git a/lib/libc/i386/string/strrchr.s b/lib/libc/i386/string/strrchr.s new file mode 100644 index 0000000000..bdb9c0eae0 --- /dev/null +++ b/lib/libc/i386/string/strrchr.s @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1993 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: strrchr.s,v 1.4 1993/08/16 17:06:43 jtc Exp $ + */ + +#if defined(LIBC_RCS) && !defined(lint) + .asciz "$Id: strrchr.s,v 1.4 1993/08/16 17:06:43 jtc Exp $" +#endif /* LIBC_RCS and not lint */ + +#include "DEFS.h" + +/* + * strrchr(s, c) + * return a pointer to the last occurance of the character c in + * string s, or NULL if c does not occur in the string. + * + * %edx - pointer iterating through string + * %eax - pointer to last occurance of 'c' + * %cl - character we're comparing against + * %bl - character at %edx + * + * Written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + */ + +ENTRY(strrchr) + pushl %ebx + movl 8(%esp),%edx + movb 12(%esp),%cl + xorl %eax,%eax /* init pointer to null */ + .align 2,0x90 +L1: + movb (%edx),%bl + cmpb %bl,%cl + jne L2 + movl %edx,%eax +L2: + incl %edx + testb %bl,%bl /* null terminator??? */ + jne L1 + popl %ebx + ret diff --git a/lib/libc/i386/sys/syscall.s b/lib/libc/i386/sys/syscall.s index c9868cf8e8..32274930a0 100644 --- a/lib/libc/i386/sys/syscall.s +++ b/lib/libc/i386/sys/syscall.s @@ -45,6 +45,8 @@ ENTRY(syscall) pop %eax /* syscall number */ push %ecx LCALL(7,0) + push %ecx /* need to push a word to keep stack frame intact + upon return; the word must be the return address. */ jb 1f ret 1: diff --git a/lib/libc/net/Makefile.inc b/lib/libc/net/Makefile.inc index dbddcb15d4..23b89e27eb 100644 --- a/lib/libc/net/Makefile.inc +++ b/lib/libc/net/Makefile.inc @@ -7,7 +7,7 @@ SRCS+= gethostnamadr.c getnetbyaddr.c getnetbyname.c getnetent.c \ getproto.c getprotoent.c getprotoname.c getservbyname.c \ getservbyport.c getservent.c herror.c inet_addr.c inet_lnaof.c \ inet_makeaddr.c inet_netof.c inet_network.c inet_ntoa.c \ - linkaddr.c rcmd.c recv.c res_comp.c \ + iso_addr.c linkaddr.c ns_addr.c ns_ntoa.c rcmd.c recv.c res_comp.c \ res_debug.c res_init.c res_mkquery.c res_query.c res_send.c \ send.c sethostent.c @@ -21,8 +21,8 @@ SRCS+= htonl.s htons.s ntohl.s ntohs.s SRCS+= htonl.s htons.s ntohl.s ntohs.s .endif -MAN3+= byteorder.0 gethostbyname.0 getnetent.0 getprotoent.0 getservent.0 \ - inet.0 ns.0 rcmd.0 resolver.0 \ +MAN3+= net/byteorder.3 net/gethostbyname.3 net/getnetent.3 net/getprotoent.3 \ + net/getservent.3 net/inet.3 net/ns.3 net/rcmd.3 net/resolver.3 MLINKS+=byteorder.3 htonl.3 byteorder.3 htons.3 byteorder.3 ntohl.3 \ byteorder.3 ntohs.3 @@ -37,7 +37,8 @@ MLINKS+=getservent.3 endservent.3 getservent.3 getservbyname.3 \ getservent.3 getservbyport.3 getservent.3 setservent.3 MLINKS+=inet.3 addr.3 inet.3 inet_addr.3 inet.3 inet_lnaof.3 \ inet.3 inet_makeaddr.3 inet.3 inet_netof.3 inet.3 inet_network.3 \ - inet.3 inet_ntoa.3 inet.3 network.3 inet.3 ntoa.3 + inet.3 inet_ntoa.3 inet.3 network.3 inet.3 ntoa.3 \ + inet.3 inet_aton.3 MLINKS+=ns.3 ns_addr.3 ns.3 ns_ntoa.3 MLINKS+=rcmd.3 rresvport.3 rcmd.3 ruserok.3 MLINKS+=resolver.3 dn_comp.3 resolver.3 dn_expand.3 resolver.3 res_init.3 \ diff --git a/lib/libc/net/gethostnamadr.c b/lib/libc/net/gethostnamadr.c index a451cb81e5..2b1597a207 100644 --- a/lib/libc/net/gethostnamadr.c +++ b/lib/libc/net/gethostnamadr.c @@ -32,7 +32,7 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)gethostnamadr.c 6.45 (Berkeley) 2/24/91"; +static char sccsid[] = "@(#)gethostnamadr.c 6.48 (Berkeley) 1/10/93"; #endif /* LIBC_SCCS and not lint */ #include @@ -50,6 +50,18 @@ static char sccsid[] = "@(#)gethostnamadr.c 6.45 (Berkeley) 2/24/91"; #define MAXALIASES 35 #define MAXADDRS 35 +#define _PATH_HOSTCONF "/etc/host.conf" + +#define SERVICE_NONE 0 +#define SERVICE_BIND 1 +#define SERVICE_HOSTS 2 +#define SERVICE_NIS 3 +#define SERVICE_MAX 3 + +static int service_order[SERVICE_MAX + 1]; +static int service_done = 0; + + static char *h_addr_ptrs[MAXADDRS + 1]; static struct hostent host; @@ -61,6 +73,7 @@ static char hostaddr[MAXADDRS]; static char *host_addrs[2]; static int stayopen = 0; char *strpbrk(); +struct hostent *_getnishost(); #if PACKETSZ > 1024 #define MAXPACKET PACKETSZ @@ -79,6 +92,37 @@ typedef union { } align; int h_errno; +extern errno; + +static void +init_services() +{ + char *cp, buf[BUFSIZ]; + register int cc = 0; + FILE *fd; + + if ((fd = (FILE *)fopen(_PATH_HOSTCONF, "r")) == NULL) { + /* make some assumptions */ + service_order[0] = SERVICE_BIND; + service_order[1] = SERVICE_HOSTS; + service_order[2] = SERVICE_NONE; + } else { + while (fgets(buf, BUFSIZ, fd) != NULL && cc < SERVICE_MAX) { + if ((cp = rindex(buf, '\n')) != NULL) + *cp = '\0'; + if (buf[0] == '#') + continue; + if (!strcmp(buf, "bind")) + service_order[cc++] = SERVICE_BIND; + else if (!strcmp(buf, "hosts")) + service_order[cc++] = SERVICE_HOSTS; + else if (!strcmp(buf, "nis")) + service_order[cc++] = SERVICE_NIS; + } + service_order[cc] = SERVICE_NONE; + } + service_done = 1; +} static struct hostent * getanswer(answer, anslen, iquery) @@ -162,10 +206,8 @@ getanswer(answer, anslen, iquery) if (iquery && type == T_PTR) { if ((n = dn_expand((u_char *)answer->buf, (u_char *)eom, (u_char *)cp, (u_char *)bp, - buflen)) < 0) { - cp += n; - continue; - } + buflen)) < 0) + break; cp += n; host.h_name = bp; return(&host); @@ -228,10 +270,12 @@ getanswer(answer, anslen, iquery) struct hostent * gethostbyname(name) - char *name; + const char *name; { querybuf buf; - register char *cp; + register int cc; + register const char *cp; + register struct hostent *hp; int n; extern struct hostent *_gethtbyname(); @@ -247,15 +291,13 @@ gethostbyname(name) /* * All-numeric, no dot at the end. * Fake up a hostent as if we'd actually - * done a lookup. What if someone types - * 255.255.255.255? The test below will - * succeed spuriously... ??? + * done a lookup. */ - if ((host_addr.s_addr = inet_addr(name)) == -1) { + if (!inet_aton(name, &host_addr)) { h_errno = HOST_NOT_FOUND; return((struct hostent *) NULL); } - host.h_name = name; + host.h_name = (char *)name; host.h_aliases = host_aliases; host_aliases[0] = NULL; host.h_addrtype = AF_INET; @@ -273,17 +315,37 @@ gethostbyname(name) break; } - if ((n = res_search(name, C_IN, T_A, buf.buf, sizeof(buf))) < 0) { + if (!service_done) + init_services(); + + for (cc = 0; service_order[cc] != SERVICE_NONE && + cc <= SERVICE_MAX; cc++) { + switch (service_order[cc]) { + case SERVICE_BIND: + if ((n = res_search(name, C_IN, T_A, + buf.buf, sizeof(buf))) < 0) { #ifdef DEBUG - if (_res.options & RES_DEBUG) - printf("res_search failed\n"); + if (_res.options & RES_DEBUG) + printf("res_search failed\n"); #endif - if (errno == ECONNREFUSED) - return (_gethtbyname(name)); - else - return ((struct hostent *) NULL); + } + hp = getanswer(&buf, n, 0); + if (hp) + return hp; + break; + case SERVICE_HOSTS: + hp = _gethtbyname(name); + if (hp) + return hp; + break; + case SERVICE_NIS: + hp = _getnishost(name, "hosts.byname"); + if (hp) + return hp; + break; + } } - return (getanswer(&buf, n, 0)); + return ((struct hostent *) NULL); } struct hostent * @@ -292,6 +354,7 @@ gethostbyaddr(addr, len, type) int len, type; { int n; + register int cc; querybuf buf; register struct hostent *hp; char qbuf[MAXDNAME]; @@ -304,28 +367,54 @@ gethostbyaddr(addr, len, type) ((unsigned)addr[2] & 0xff), ((unsigned)addr[1] & 0xff), ((unsigned)addr[0] & 0xff)); - n = res_query(qbuf, C_IN, T_PTR, (char *)&buf, sizeof(buf)); - if (n < 0) { + if (!service_done) + init_services(); + + cc = 0; + while (service_order[cc] != SERVICE_NONE) { + switch (service_order[cc]) { + case SERVICE_BIND: + (void)sprintf(qbuf, "%u.%u.%u.%u.in-addr.arpa", + ((unsigned)addr[3] & 0xff), + ((unsigned)addr[2] & 0xff), + ((unsigned)addr[1] & 0xff), + ((unsigned)addr[0] & 0xff)); + n = res_query(qbuf, C_IN, T_PTR, (char *)&buf, + sizeof(buf)); + if (n < 0) { #ifdef DEBUG - if (_res.options & RES_DEBUG) - printf("res_query failed\n"); + if (_res.options & RES_DEBUG) + printf("res_query failed\n"); #endif - if (errno == ECONNREFUSED) - return (_gethtbyaddr(addr, len, type)); - return ((struct hostent *) NULL); - } - hp = getanswer(&buf, n, 1); - if (hp == NULL) - return ((struct hostent *) NULL); - hp->h_addrtype = type; - hp->h_length = len; - h_addr_ptrs[0] = (char *)&host_addr; - h_addr_ptrs[1] = (char *)0; - host_addr = *(struct in_addr *)addr; + break; + } + hp = getanswer(&buf, n, 1); + if (hp) { + hp->h_addrtype = type; + hp->h_length = len; + h_addr_ptrs[0] = (char *)&host_addr; + h_addr_ptrs[1] = (char *)0; + host_addr = *(struct in_addr *)addr; #if BSD < 43 && !defined(h_addr) /* new-style hostent structure */ - hp->h_addr = h_addr_ptrs[0]; + hp->h_addr = h_addr_ptrs[0]; #endif - return(hp); + return(hp); + } + break; + case SERVICE_HOSTS: + hp = _gethtbyaddr(addr, len, type); + if (hp) + return hp; + break; + case SERVICE_NIS: + hp = _getnishost(addr, "hosts.byaddr"); + if (hp) + return hp; + break; + } + cc++; + } + return ((struct hostent *)NULL); } _sethtent(f) @@ -431,3 +520,58 @@ _gethtbyaddr(addr, len, type) _endhtent(); return (p); } + +struct hostent * +_getnishost(name, map) + char *name, *map; +{ + register char *cp, *dp, **q; + char *result; + int resultlen; + struct hostent h; + static char *domain = (char *)NULL; + + if (domain == (char *)NULL) +#ifdef YP + if (yp_get_default_domain (&domain)) +#endif + return ((struct hostent *)NULL); + +#ifdef YP + if (yp_match(domain, map, name, strlen(name), &result, &resultlen)) + return ((struct hostent *)NULL); +#endif + + if (cp = index(result, '\n')) + *cp = '\0'; + + cp = strpbrk(result, " \t"); + *cp++ = '\0'; +#if BSD >= 43 || defined(h_addr) /* new-style hostent structure */ + h.h_addr_list = host_addrs; +#endif + h.h_addr = hostaddr; + *((u_long *)h.h_addr) = inet_addr(result); + h.h_length = sizeof(u_long); + h.h_addrtype = AF_INET; + while (*cp == ' ' || *cp == '\t') + cp++; + h.h_name = cp; + q = h.h_aliases = host_aliases; + cp = strpbrk(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q < &host_aliases[MAXALIASES - 1]) + *q++ = cp; + cp = strpbrk(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + } + *q = NULL; + return (&h); +} diff --git a/lib/libc/net/inet_addr.c b/lib/libc/net/inet_addr.c index fc7ac1bb2c..b70ce6174d 100644 --- a/lib/libc/net/inet_addr.c +++ b/lib/libc/net/inet_addr.c @@ -32,7 +32,7 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)inet_addr.c 5.10 (Berkeley) 2/24/91"; +static char sccsid[] = "@(#)inet_addr.c 5.11 (Berkeley) 12/9/91"; #endif /* LIBC_SCCS and not lint */ #include @@ -62,9 +62,9 @@ inet_addr(cp) * This replaces inet_addr, the return value from which * cannot distinguish between failure and a local broadcast address. */ - +unsigned long inet_aton(cp, addr) - register char *cp; + register const char *cp; struct in_addr *addr; { register u_long val, base, n; diff --git a/lib/libc/stdio/Makefile.inc b/lib/libc/stdio/Makefile.inc index 591fae355b..ad87006fba 100644 --- a/lib/libc/stdio/Makefile.inc +++ b/lib/libc/stdio/Makefile.inc @@ -14,9 +14,12 @@ SRCS+= clrerr.c fclose.c fdopen.c feof.c ferror.c fflush.c fgetc.c \ vfscanf.c vprintf.c vscanf.c vsnprintf.c vsprintf.c vsscanf.c \ wbuf.c wsetup.c -MAN3+= fclose.0 ferror.0 fflush.0 fgetline.0 fgets.0 fopen.0 fputs.0 \ - fread.0 fseek.0 funopen.0 getc.0 mktemp.0 printf.0 putc.0 remove.0 \ - scanf.0 setbuf.0 stdio.0 strerror.0 tmpnam.0 ungetc.0 +MAN3+= stdio/fclose.3 stdio/ferror.3 stdio/fflush.3 stdio/fgetline.3 \ + stdio/fgets.3 stdio/fopen.3 stdio/fputs.3 stdio/fread.3 \ + stdio/fseek.3 stdio/funopen.3 stdio/getc.3 stdio/mktemp.3 \ + stdio/printf.3 stdio/putc.3 stdio/remove.3 stdio/scanf.3 \ + stdio/setbuf.3 stdio/stdio.3 stdio/strerror.3 stdio/tmpnam.3 \ + stdio/ungetc.3 MLINKS+=ferror.3 clearerr.3 ferror.3 feof.3 ferror.3 fileno.3 MLINKS+=fflush.3 fpurge.3 diff --git a/lib/libc/stdio/fgetpos.c b/lib/libc/stdio/fgetpos.c index fd6d63c17b..c920663643 100644 --- a/lib/libc/stdio/fgetpos.c +++ b/lib/libc/stdio/fgetpos.c @@ -44,5 +44,5 @@ fgetpos(fp, pos) FILE *fp; fpos_t *pos; { - return((*pos = ftell(fp)) != (fpos_t)-1); + return(((*pos = ftell(fp)) != (fpos_t)-1) ? 0:-1); } diff --git a/lib/libc/stdio/fread.c b/lib/libc/stdio/fread.c index affdef9b28..58f0999602 100644 --- a/lib/libc/stdio/fread.c +++ b/lib/libc/stdio/fread.c @@ -52,7 +52,7 @@ fread(buf, size, count, fp) size_t total; if ((resid = count * size) == 0) - return (count); + return (resid); if (fp->_r < 0) fp->_r = 0; total = resid; diff --git a/lib/libc/stdio/local.h b/lib/libc/stdio/local.h index c75c0fc564..21966d704d 100644 --- a/lib/libc/stdio/local.h +++ b/lib/libc/stdio/local.h @@ -33,7 +33,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)local.h 5.2 (Berkeley) 2/5/91 + * @(#)local.h 5.3 (Berkeley) 5/6/93 */ /* @@ -52,6 +52,7 @@ void __sinit __P((void)); void _cleanup __P((void)); void (*__cleanup) __P((void)); void __smakebuf __P((FILE *)); +int __swhatbuf __P((FILE *, size_t *, int *)); int _fwalk __P((int (*)(FILE *))); int __swsetup __P((FILE *)); int __sflags __P((const char *, int *)); diff --git a/lib/libc/stdio/makebuf.c b/lib/libc/stdio/makebuf.c index 7b86aadccc..cb87e6073a 100644 --- a/lib/libc/stdio/makebuf.c +++ b/lib/libc/stdio/makebuf.c @@ -35,7 +35,7 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)makebuf.c 5.2 (Berkeley) 2/24/91"; +static char sccsid[] = "@(#)makebuf.c 5.3 (Berkeley) 5/6/93"; #endif /* LIBC_SCCS and not lint */ #include @@ -56,44 +56,63 @@ void __smakebuf(fp) register FILE *fp; { - register size_t size, couldbetty; register void *p; - struct stat st; + register int flags; + size_t size; + int couldbetty; if (fp->_flags & __SNBF) { fp->_bf._base = fp->_p = fp->_nbuf; fp->_bf._size = 1; return; } - if (fp->_file < 0 || fstat(fp->_file, &st) < 0) { - couldbetty = 0; - size = BUFSIZ; - /* do not try to optimise fseek() */ - fp->_flags |= __SNPT; - } else { - couldbetty = (st.st_mode & S_IFMT) == S_IFCHR; - size = st.st_blksize <= 0 ? BUFSIZ : st.st_blksize; - /* - * Optimise fseek() only if it is a regular file. - * (The test for __sseek is mainly paranoia.) - */ - if ((st.st_mode & S_IFMT) == S_IFREG && - fp->_seek == __sseek) { - fp->_flags |= __SOPT; - fp->_blksize = st.st_blksize; - } else - fp->_flags |= __SNPT; - } + flags = __swhatbuf(fp, &size, &couldbetty); if ((p = malloc(size)) == NULL) { fp->_flags |= __SNBF; fp->_bf._base = fp->_p = fp->_nbuf; fp->_bf._size = 1; - } else { - __cleanup = _cleanup; - fp->_flags |= __SMBF; - fp->_bf._base = fp->_p = p; - fp->_bf._size = size; - if (couldbetty && isatty(fp->_file)) - fp->_flags |= __SLBF; + return; } + __cleanup = _cleanup; + flags |= __SMBF; + fp->_bf._base = fp->_p = p; + fp->_bf._size = size; + if (couldbetty && isatty(fp->_file)) + flags |= __SLBF; + fp->_flags |= flags; +} + +/* + * Internal routine to determine `proper' buffering for a file. + */ +int +__swhatbuf(fp, bufsize, couldbetty) + register FILE *fp; + size_t *bufsize; + int *couldbetty; +{ + struct stat st; + + if (fp->_file < 0 || fstat(fp->_file, &st) < 0) { + *couldbetty = 0; + *bufsize = BUFSIZ; + return (__SNPT); + } + + /* could be a tty iff it is a character device */ + *couldbetty = (st.st_mode & S_IFMT) == S_IFCHR; + if (st.st_blksize <= 0) { + *bufsize = BUFSIZ; + return (__SNPT); + } + + /* + * Optimise fseek() only if it is a regular file. (The test for + * __sseek is mainly paranoia.) It is safe to set _blksize + * unconditionally; it will only be used if __SOPT is also set. + */ + *bufsize = st.st_blksize; + fp->_blksize = st.st_blksize; + return ((st.st_mode & S_IFMT) == S_IFREG && fp->_seek == __sseek ? + __SOPT : __SNPT); } diff --git a/lib/libc/stdio/scanf.3 b/lib/libc/stdio/scanf.3 index ff88e476b0..4eb3877247 100644 --- a/lib/libc/stdio/scanf.3 +++ b/lib/libc/stdio/scanf.3 @@ -33,9 +33,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)scanf.3 6.11 (Berkeley) 6/29/91 +.\" @(#)scanf.3 6.14 (Berkeley) 1/8/93 .\" -.Dd June 29, 1991 +.Dd January 8, 1993 .Dt SCANF 3 .Os .Sh NAME @@ -393,6 +393,7 @@ the number of conversions which were successfully completed is returned. .Sh SEE ALSO .Xr strtol 3 , .Xr strtoul 3 , +.Xr strtod 3 , .Xr getc 3 , .Xr printf 3 .Sh STANDARDS @@ -418,3 +419,12 @@ and conversions is unfortunate. .Pp All of the backwards compatibility formats will be removed in the future. +.Pp +Numerical strings are truncated to 512 characters; for example, +.Cm %f +and +.Cm %d +are implicitly +.Cm %512f +and +.Cm %512d . diff --git a/lib/libc/stdio/setvbuf.c b/lib/libc/stdio/setvbuf.c index 2e53cd278f..1928ae1416 100644 --- a/lib/libc/stdio/setvbuf.c +++ b/lib/libc/stdio/setvbuf.c @@ -35,7 +35,7 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)setvbuf.c 5.2 (Berkeley) 2/1/91"; +static char sccsid[] = "@(#)setvbuf.c 5.5 (Berkeley) 5/6/93"; #endif /* LIBC_SCCS and not lint */ #include @@ -52,59 +52,97 @@ setvbuf(fp, buf, mode, size) register int mode; register size_t size; { + register int ret, flags; + size_t iosize; + int ttyflag; /* * Verify arguments. The `int' limit on `size' is due to this - * particular implementation. + * particular implementation. Note, buf and size are ignored + * when setting _IONBF. */ - if ((mode != _IOFBF && mode != _IOLBF && mode != _IONBF) || - (int)size < 0) - return (EOF); + if (mode != _IONBF) + if ((mode != _IOFBF && mode != _IOLBF) || (int)size < 0) + return (EOF); /* - * Write current buffer, if any; drop read count, if any. - * Make sure putc() will not think fp is line buffered. - * Free old buffer if it was from malloc(). Clear line and - * non buffer flags, and clear malloc flag. + * Write current buffer, if any. Discard unread input, cancel + * line buffering, and free old buffer if malloc()ed. */ - (void) __sflush(fp); - fp->_r = 0; - fp->_lbfsize = 0; - if (fp->_flags & __SMBF) + ret = 0; + (void)__sflush(fp); + fp->_r = fp->_lbfsize = 0; + flags = fp->_flags; + if (flags & __SMBF) free((void *)fp->_bf._base); - fp->_flags &= ~(__SLBF|__SNBF|__SMBF); + flags &= ~(__SLBF | __SNBF | __SMBF | __SOPT | __SNPT); + + /* If setting unbuffered mode, skip all the hard work. */ + if (mode == _IONBF) + goto nbf; /* - * Now put back whichever flag is needed, and fix _lbfsize - * if line buffered. Ensure output flush on exit if the - * stream will be buffered at all. + * Find optimal I/O size for seek optimization. This also returns + * a `tty flag' to suggest that we check isatty(fd), but we do not + * care since our caller told us how to buffer. */ - switch (mode) { - - case _IONBF: - fp->_flags |= __SNBF; - fp->_bf._base = fp->_p = fp->_nbuf; - fp->_bf._size = 1; - break; - - case _IOLBF: - fp->_flags |= __SLBF; - fp->_lbfsize = -size; - /* FALLTHROUGH */ + flags |= __swhatbuf(fp, &iosize, &ttyflag); + if (size == 0) { + buf = NULL; /* force local allocation */ + size = iosize; + } - case _IOFBF: - /* no flag */ - __cleanup = _cleanup; - fp->_bf._base = fp->_p = (unsigned char *)buf; - fp->_bf._size = size; - break; + /* Allocate buffer if needed. */ + if (buf == NULL) { + if ((buf = malloc(size)) == NULL) { + /* + * Unable to honor user's request. We will return + * failure, but try again with file system size. + */ + ret = EOF; + if (size != iosize) { + size = iosize; + buf = malloc(size); + } + } + if (buf == NULL) { + /* No luck; switch to unbuffered I/O. */ +nbf: + fp->_flags = flags | __SNBF; + fp->_w = 0; + fp->_bf._base = fp->_p = fp->_nbuf; + fp->_bf._size = 1; + return (ret); + } + flags |= __SMBF; } /* - * Patch up write count if necessary. + * Kill any seek optimization if the buffer is not the + * right size. + * + * SHOULD WE ALLOW MULTIPLES HERE (i.e., ok iff (size % iosize) == 0)? */ - if (fp->_flags & __SWR) - fp->_w = fp->_flags & (__SLBF|__SNBF) ? 0 : size; + if (size != iosize) + flags |= __SNPT; + + /* + * Fix up the FILE fields, and set __cleanup for output flush on + * exit (since we are buffered in some way). If in r/w mode, go + * to the intermediate state, so that everyone has to call + * __srefill or __swsetup on the first operation -- it is more + * trouble than it is worth to set things up correctly here. + */ + if (mode == _IOLBF) + flags |= __SLBF; + if (flags & __SRW) + flags &= ~(__SRD | __SWR); + fp->_w = 0; + fp->_flags = flags; + fp->_bf._base = fp->_p = (unsigned char *)buf; + fp->_bf._size = size; + fp->_lbfsize = 0; + __cleanup = _cleanup; - return (0); + return (ret); } diff --git a/lib/libc/stdio/sscanf.c b/lib/libc/stdio/sscanf.c index 8eb3390341..752dc91cc9 100644 --- a/lib/libc/stdio/sscanf.c +++ b/lib/libc/stdio/sscanf.c @@ -59,9 +59,9 @@ eofread(cookie, buf, len) } #if __STDC__ -sscanf(char *str, char const *fmt, ...) +sscanf(const char *str, char const *fmt, ...) #else -sscanf(str, fmt, va_alist) +sscanf(const str, fmt, va_alist) char *str; char *fmt; va_dcl diff --git a/lib/libc/stdio/tempnam.c b/lib/libc/stdio/tempnam.c index 0674cfc356..988763aac5 100644 --- a/lib/libc/stdio/tempnam.c +++ b/lib/libc/stdio/tempnam.c @@ -56,24 +56,26 @@ tempnam(dir, pfx) pfx = "tmp."; if (f = getenv("TMPDIR")) { - (void)snprintf(name, MAXPATHLEN, "%s/%sXXXXXX", f, pfx); + (void)snprintf(name, MAXPATHLEN, "%s%s%sXXXXXX", f, + *(f + strlen(f) - 1) == '/'? "": "/", pfx); if (f = mktemp(name)) return(f); } if (f = (char *)dir) { - (void)snprintf(name, MAXPATHLEN, "%s/%sXXXXXX", f, pfx); + (void)snprintf(name, MAXPATHLEN, "%s%s%sXXXXXX", f, + *(f + strlen(f) - 1) == '/'? "": "/", pfx); if (f = mktemp(name)) return(f); } f = P_tmpdir; - (void)snprintf(name, MAXPATHLEN, "%s/%sXXXXXX", f, pfx); + (void)snprintf(name, MAXPATHLEN, "%s%sXXXXXX", f, pfx); if (f = mktemp(name)) return(f); f = _PATH_TMP; - (void)snprintf(name, MAXPATHLEN, "%s/%sXXXXXX", f, pfx); + (void)snprintf(name, MAXPATHLEN, "%s%sXXXXXX", f, pfx); if (f = mktemp(name)) return(f); diff --git a/lib/libc/stdio/tmpnam.c b/lib/libc/stdio/tmpnam.c index 4368b9777c..3746d7e3c5 100644 --- a/lib/libc/stdio/tmpnam.c +++ b/lib/libc/stdio/tmpnam.c @@ -49,6 +49,6 @@ tmpnam(s) if (s == NULL) s = buf; - (void)snprintf(s, L_tmpnam, "%s/tmp.XXXXXX", P_tmpdir); + (void)snprintf(s, L_tmpnam, "%stmp.XXXXXX", P_tmpdir); return(mktemp(s)); } diff --git a/lib/libc/stdio/vfprintf.c b/lib/libc/stdio/vfprintf.c index aada9188d9..8d064c696c 100644 --- a/lib/libc/stdio/vfprintf.c +++ b/lib/libc/stdio/vfprintf.c @@ -35,7 +35,7 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)vfprintf.c 5.47 (Berkeley) 3/22/91"; +static char sccsid[] = "@(#)vfprintf.c 5.50 (Berkeley) 12/16/92"; #endif /* LIBC_SCCS and not lint */ /* @@ -45,100 +45,22 @@ static char sccsid[] = "@(#)vfprintf.c 5.47 (Berkeley) 3/22/91"; */ #include -#include + #include +#include #include + #if __STDC__ #include #else #include #endif + #include "local.h" #include "fvwrite.h" -/* - * Define FLOATING_POINT to get floating point. - * Define CSH to get a csh-specific version (grr). - */ -#ifndef CSH +/* Define FLOATING_POINT to get floating point. */ #define FLOATING_POINT -#endif - -/* end of configuration stuff */ - - -#ifdef CSH -/* - * C shell hacks. Ick, gag. - */ -#undef BUFSIZ -#include "sh.h" - -#if __STDC__ -int -printf(const char *fmt, ...) { - FILE f; - va_list ap; - int ret; - - va_start(ap, fmt); - f._flags = __SWR; - f._write = NULL; - ret = vfprintf(&f, fmt, ap); - va_end(ap); - return ret; -} -#else -int -printf(fmt, args) - char *fmt; -{ - FILE f; - - f._flags = __SWR; - f._write = NULL; - return (vfprintf(&f, fmt, &args)); -} -#endif - -int -__sprint(fp, uio) - FILE *fp; - register struct __suio *uio; -{ - register char *p; - register int n, ch, iovcnt; - register struct __siov *iov; - - /* must allow sprintf to work, might as well allow others too */ - if (fp->_write || fp->_flags & __SSTR) { - if (uio->uio_resid == 0) { - uio->uio_iovcnt = 0; - return (0); - } - n = __sfvwrite(fp, uio); - uio->uio_resid = 0; - uio->uio_iovcnt = 0; - return (n); - } - iov = uio->uio_iov; - for (iovcnt = uio->uio_iovcnt; --iovcnt >= 0; iov++) { - for (p = iov->iov_base, n = iov->iov_len; --n >= 0;) { -#ifdef CSHPUTCHAR - ch = *p++; - CSHPUTCHAR; /* this horrid macro uses `ch' */ -#else -#undef putchar - putchar(*p++); -#endif - } - } - uio->uio_resid = 0; - uio->uio_iovcnt = 0; - return (0); -} - -#else /* CSH */ /* * Flush out all the vectors defined by the given uio, @@ -196,16 +118,16 @@ __sbprintf(fp, fmt, ap) return (ret); } -#endif /* CSH */ - #ifdef FLOATING_POINT +#include #include "floatio.h" #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ #define DEFPREC 6 -static int cvt(); +static char *cvt __P((double, int, int, char *, int *, int, int *)); +static int exponent __P((char *, int, int)); #else /* no FLOATING_POINT */ @@ -224,21 +146,19 @@ static int cvt(); /* * Flags used during conversion. */ -#define LONGINT 0x01 /* long integer */ -#define LONGDBL 0x02 /* long double; unimplemented */ -#define SHORTINT 0x04 /* short integer */ -#define ALT 0x08 /* alternate form */ -#define LADJUST 0x10 /* left adjustment */ -#define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ -#define HEXPREFIX 0x40 /* add 0x or 0X prefix */ - +#define ALT 0x001 /* alternate form */ +#define HEXPREFIX 0x002 /* add 0x or 0X prefix */ +#define LADJUST 0x004 /* left adjustment */ +#define LONGDBL 0x008 /* long double; unimplemented */ +#define LONGINT 0x010 /* long integer */ +#define QUADINT 0x020 /* quad integer */ +#define SHORTINT 0x040 /* short integer */ +#define ZEROPAD 0x080 /* zero (as opposed to blank) pad */ +#define FPT 0x100 /* Floating point number */ int vfprintf(fp, fmt0, ap) FILE *fp; const char *fmt0; -#if tahoe - register /* technically illegal, since we do not know what type va_list is */ -#endif va_list ap; { register char *fmt; /* format string */ @@ -254,9 +174,18 @@ vfprintf(fp, fmt0, ap) #ifdef FLOATING_POINT char softsign; /* temporary negative sign for floats */ double _double; /* double precision arguments %[eEfgG] */ - int fpprec; /* `extra' floating precision in [eEfgG] */ + int expt; /* integer value of exponent */ + int expsize; /* character count for expstr */ + int ndig; /* actual number of digits returned by cvt */ + char expstr[7]; /* buffer for exponent string */ #endif - u_long _ulong; /* integer arguments %[diouxX] */ + +#ifdef __GNUC__ /* gcc has builtin quad type (long long) SOS */ +#define quad_t long long +#define u_quad_t unsigned long long +#endif + + u_quad_t _uquad; /* integer arguments %[diouxX] */ enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */ int dprec; /* a copy of prec if [diouxX], 0 otherwise */ int fieldsz; /* field size expanded by sign, etc */ @@ -270,9 +199,9 @@ vfprintf(fp, fmt0, ap) char ox[2]; /* space for 0x hex-prefix */ /* - * Choose PADSIZE to trade efficiency vs size. If larger - * printf fields occur frequently, increase PADSIZE (and make - * the initialisers below longer). + * Choose PADSIZE to trade efficiency vs. size. If larger printf + * fields occur frequently, increase PADSIZE and make the initialisers + * below longer. */ #define PADSIZE 16 /* pad chunk size */ static char blanks[PADSIZE] = @@ -315,15 +244,16 @@ vfprintf(fp, fmt0, ap) * argument extraction methods. */ #define SARG() \ - (flags&LONGINT ? va_arg(ap, long) : \ + (flags&QUADINT ? va_arg(ap, quad_t) : \ + flags&LONGINT ? va_arg(ap, long) : \ flags&SHORTINT ? (long)(short)va_arg(ap, int) : \ (long)va_arg(ap, int)) #define UARG() \ - (flags&LONGINT ? va_arg(ap, u_long) : \ + (flags&QUADINT ? va_arg(ap, u_quad_t) : \ + flags&LONGINT ? va_arg(ap, u_long) : \ flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \ (u_long)va_arg(ap, u_int)) -#ifndef CSH /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ if (cantwrite(fp)) return (EOF); @@ -332,7 +262,6 @@ vfprintf(fp, fmt0, ap) if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && fp->_file >= 0) return (__sbprintf(fp, fmt0, ap)); -#endif /* CSH */ fmt = (char *)fmt0; uio.uio_iov = iovp = iov; @@ -356,9 +285,6 @@ vfprintf(fp, fmt0, ap) flags = 0; dprec = 0; -#ifdef FLOATING_POINT - fpprec = 0; -#endif width = 0; prec = -1; sign = '\0'; @@ -435,6 +361,9 @@ reswitch: switch (ch) { case 'l': flags |= LONGINT; goto rflag; + case 'q': + flags |= QUADINT; + goto rflag; case 'c': *(cp = buf) = va_arg(ap, int); size = 1; @@ -445,20 +374,28 @@ reswitch: switch (ch) { /*FALLTHROUGH*/ case 'd': case 'i': - _ulong = SARG(); - if ((long)_ulong < 0) { - _ulong = -_ulong; + _uquad = SARG(); + if ((quad_t)_uquad < 0) { + _uquad = -_uquad; sign = '-'; } base = DEC; goto number; #ifdef FLOATING_POINT - case 'e': + case 'e': /* anomalous precision */ case 'E': - case 'f': + prec = (prec == -1) ? + DEFPREC + 1 : prec + 1; + /* FALLTHROUGH */ + goto fp_begin; + case 'f': /* always print trailing zeroes */ + if (prec != 0) + flags |= ALT; case 'g': case 'G': - _double = va_arg(ap, double); + if (prec == -1) + prec = DEFPREC; +fp_begin: _double = va_arg(ap, double); /* do this before tricky precision changes */ if (isinf(_double)) { if (_double < 0) @@ -472,35 +409,44 @@ reswitch: switch (ch) { size = 3; break; } - /* - * don't do unrealistic precision; just pad it with - * zeroes later, so buffer size stays rational. - */ - if (prec > MAXFRACT) { - if (ch != 'g' && ch != 'G' || (flags&ALT)) - fpprec = prec - MAXFRACT; - prec = MAXFRACT; - } else if (prec == -1) - prec = DEFPREC; - /* - * cvt may have to round up before the "start" of - * its buffer, i.e. ``intf("%.2f", (double)9.999);''; - * if the first character is still NUL, it did. - * softsign avoids negative 0 if _double < 0 but - * no significant digits will be shown. - */ - cp = buf; - *cp = '\0'; - size = cvt(_double, prec, flags, &softsign, ch, - cp, buf + sizeof(buf)); + flags |= FPT; + cp = cvt(_double, prec, flags, &softsign, + &expt, ch, &ndig); + if (ch == 'g' || ch == 'G') { + if (expt <= -4 || expt > prec) + ch = (ch == 'g') ? 'e' : 'E'; + else + ch = 'g'; + } + if (ch <= 'e') { /* 'e' or 'E' fmt */ + --expt; + expsize = exponent(expstr, expt, ch); + size = expsize + ndig; + if (ndig > 1 || flags & ALT) + ++size; + } else if (ch == 'f') { /* f fmt */ + if (expt > 0) { + size = expt; + if (prec || flags & ALT) + size += prec + 1; + } else /* "0.X" */ + size = prec + 2; + } else if (expt >= ndig) { /* fixed g fmt */ + size = expt; + if (flags & ALT) + ++size; + } else + size = ndig + (expt > 0 ? + 1 : 2 - expt); + if (softsign) sign = '-'; - if (*cp == '\0') - cp++; break; #endif /* FLOATING_POINT */ case 'n': - if (flags & LONGINT) + if (flags & QUADINT) + *va_arg(ap, quad_t *) = ret; + else if (flags & LONGINT) *va_arg(ap, long *) = ret; else if (flags & SHORTINT) *va_arg(ap, short *) = ret; @@ -511,7 +457,7 @@ reswitch: switch (ch) { flags |= LONGINT; /*FALLTHROUGH*/ case 'o': - _ulong = UARG(); + _uquad = UARG(); base = OCT; goto nosign; case 'p': @@ -523,7 +469,7 @@ reswitch: switch (ch) { * -- ANSI X3J11 */ /* NOSTRICT */ - _ulong = (u_long)va_arg(ap, void *); + _uquad = (u_quad_t)va_arg(ap, void *); base = HEX; xdigs = "0123456789abcdef"; flags |= HEXPREFIX; @@ -554,7 +500,7 @@ reswitch: switch (ch) { flags |= LONGINT; /*FALLTHROUGH*/ case 'u': - _ulong = UARG(); + _uquad = UARG(); base = DEC; goto nosign; case 'X': @@ -562,10 +508,10 @@ reswitch: switch (ch) { goto hex; case 'x': xdigs = "0123456789abcdef"; -hex: _ulong = UARG(); +hex: _uquad = UARG(); base = HEX; /* leading 0x/X only if non-zero */ - if (flags & ALT && _ulong != 0) + if (flags & ALT && _uquad != 0) flags |= HEXPREFIX; /* unsigned conversions */ @@ -584,18 +530,18 @@ number: if ((dprec = prec) >= 0) * -- ANSI X3J11 */ cp = buf + BUF; - if (_ulong != 0 || prec != 0) { + if (_uquad != 0 || prec != 0) { /* - * unsigned mod is hard, and unsigned mod + * Unsigned mod is hard, and unsigned mod * by a constant is easier than that by * a variable; hence this switch. */ switch (base) { case OCT: do { - *--cp = to_char(_ulong & 7); - _ulong >>= 3; - } while (_ulong); + *--cp = to_char(_uquad & 7); + _uquad >>= 3; + } while (_uquad); /* handle octal leading 0 */ if (flags & ALT && *cp != '0') *--cp = '0'; @@ -603,18 +549,18 @@ number: if ((dprec = prec) >= 0) case DEC: /* many numbers are 1 digit */ - while (_ulong >= 10) { - *--cp = to_char(_ulong % 10); - _ulong /= 10; + while (_uquad >= 10) { + *--cp = to_char(_uquad % 10); + _uquad /= 10; } - *--cp = to_char(_ulong); + *--cp = to_char(_uquad); break; case HEX: do { - *--cp = xdigs[_ulong & 15]; - _ulong >>= 4; - } while (_ulong); + *--cp = xdigs[_uquad & 15]; + _uquad >>= 4; + } while (_uquad); break; default: @@ -638,28 +584,20 @@ number: if ((dprec = prec) >= 0) } /* - * All reasonable formats wind up here. At this point, - * `cp' points to a string which (if not flags&LADJUST) - * should be padded out to `width' places. If - * flags&ZEROPAD, it should first be prefixed by any - * sign or other prefix; otherwise, it should be blank - * padded before the prefix is emitted. After any - * left-hand padding and prefixing, emit zeroes - * required by a decimal [diouxX] precision, then print - * the string proper, then emit zeroes required by any - * leftover floating precision; finally, if LADJUST, - * pad with blanks. + * All reasonable formats wind up here. At this point, `cp' + * points to a string which (if not flags&LADJUST) should be + * padded out to `width' places. If flags&ZEROPAD, it should + * first be prefixed by any sign or other prefix; otherwise, + * it should be blank padded before the prefix is emitted. + * After any left-hand padding and prefixing, emit zeroes + * required by a decimal [diouxX] precision, then print the + * string proper, then emit zeroes required by any leftover + * floating precision; finally, if LADJUST, pad with blanks. + * + * Compute actual size, so we know how much to pad. + * fieldsz excludes decimal prec; realsz includes it. */ - - /* - * compute actual size, so we know how much to pad. - * fieldsz excludes decimal prec; realsz includes it - */ -#ifdef FLOATING_POINT - fieldsz = size + fpprec; -#else fieldsz = size; -#endif if (sign) fieldsz++; else if (flags & HEXPREFIX) @@ -687,13 +625,53 @@ number: if ((dprec = prec) >= 0) PAD(dprec - fieldsz, zeroes); /* the string or number proper */ - PRINT(cp, size); - #ifdef FLOATING_POINT - /* trailing f.p. zeroes */ - PAD(fpprec, zeroes); + if ((flags & FPT) == 0) { + PRINT(cp, size); + } else { /* glue together f_p fragments */ + if (ch >= 'f') { /* 'f' or 'g' */ + if (_double == 0) { + /* kludge for __dtoa irregularity */ + if (prec == 0 || + (flags & ALT) == 0) { + PRINT("0", 1); + } else { + PRINT("0.", 2); + PAD(ndig - 1, zeroes); + } + } else if (expt <= 0) { + PRINT("0.", 2); + PAD(-expt, zeroes); + PRINT(cp, ndig); + } else if (expt >= ndig) { + PRINT(cp, ndig); + PAD(expt - ndig, zeroes); + if (flags & ALT) + PRINT(".", 1); + } else { + PRINT(cp, expt); + cp += expt; + PRINT(".", 1); + PRINT(cp, ndig-expt); + } + } else { /* 'e' or 'E' */ + if (ndig > 1 || flags & ALT) { + ox[0] = *cp++; + ox[1] = '.'; + PRINT(ox, 2); + if (_double || flags & ALT == 0) { + PRINT(cp, ndig-1); + } else /* 0.[0..] */ + /* __dtoa irregularity */ + PAD(ndig - 1, zeroes); + } else /* XeYYY */ + PRINT(cp, 1); + PRINT(expstr, expsize); + } + } +#else + PRINT(cp, size); #endif - /* left-adjusting padding (always blank) */ if (flags & LADJUST) PAD(width - realsz, blanks); @@ -711,257 +689,54 @@ error: } #ifdef FLOATING_POINT -#include -static char *exponent(); -static char *round(); +extern char *__dtoa __P((double, int, int, int *, int *, char **)); -static int -cvt(number, prec, flags, signp, fmtch, startp, endp) - double number; - register int prec; - int flags; - char *signp; - int fmtch; - char *startp, *endp; +static char * +cvt(value, ndigits, flags, sign, decpt, ch, length) + double value; + int ndigits, flags, *decpt, ch, *length; + char *sign; { - register char *p, *t; - register double fract; - int dotrim, expcnt, gformat; - double integer, tmp; - - dotrim = expcnt = gformat = 0; - if (number < 0) { - number = -number; - *signp = '-'; - } else - *signp = 0; - - fract = modf(number, &integer); + int mode, dsgn; + char *digits, *bp, *rve; - /* get an extra slot for rounding. */ - t = ++startp; - - /* - * get integer portion of number; put into the end of the buffer; the - * .01 is added for modf(356.0 / 10, &integer) returning .59999999... - */ - for (p = endp - 1; integer; ++expcnt) { - tmp = modf(integer / 10, &integer); - *p-- = to_char((int)((tmp + .01) * 10)); + if (ch == 'f') + mode = 3; + else { + mode = 2; } - switch (fmtch) { - case 'f': - /* reverse integer into beginning of buffer */ - if (expcnt) - for (; ++p < endp; *t++ = *p); - else - *t++ = '0'; - /* - * if precision required or alternate flag set, add in a - * decimal point. - */ - if (prec || flags&ALT) - *t++ = '.'; - /* if requires more precision and some fraction left */ - if (fract) { - if (prec) - do { - fract = modf(fract * 10, &tmp); - *t++ = to_char((int)tmp); - } while (--prec && fract); - if (fract) - startp = round(fract, (int *)NULL, startp, - t - 1, (char)0, signp); - } - for (; prec--; *t++ = '0'); - break; - case 'e': - case 'E': -eformat: if (expcnt) { - *t++ = *++p; - if (prec || flags&ALT) - *t++ = '.'; - /* if requires more precision and some integer left */ - for (; prec && ++p < endp; --prec) - *t++ = *p; - /* - * if done precision and more of the integer component, - * round using it; adjust fract so we don't re-round - * later. - */ - if (!prec && ++p < endp) { - fract = 0; - startp = round((double)0, &expcnt, startp, - t - 1, *p, signp); - } - /* adjust expcnt for digit in front of decimal */ - --expcnt; - } - /* until first fractional digit, decrement exponent */ - else if (fract) { - /* adjust expcnt for digit in front of decimal */ - for (expcnt = -1;; --expcnt) { - fract = modf(fract * 10, &tmp); - if (tmp) - break; - } - *t++ = to_char((int)tmp); - if (prec || flags&ALT) - *t++ = '.'; - } - else { - *t++ = '0'; - if (prec || flags&ALT) - *t++ = '.'; - } - /* if requires more precision and some fraction left */ - if (fract) { - if (prec) - do { - fract = modf(fract * 10, &tmp); - *t++ = to_char((int)tmp); - } while (--prec && fract); - if (fract) - startp = round(fract, &expcnt, startp, - t - 1, (char)0, signp); - } - /* if requires more precision */ - for (; prec--; *t++ = '0'); - - /* unless alternate flag, trim any g/G format trailing 0's */ - if (gformat && !(flags&ALT)) { - while (t > startp && *--t == '0'); - if (*t == '.') - --t; - ++t; - } - t = exponent(t, expcnt, fmtch); - break; - case 'g': - case 'G': - /* a precision of 0 is treated as a precision of 1. */ - if (!prec) - ++prec; - /* - * ``The style used depends on the value converted; style e - * will be used only if the exponent resulting from the - * conversion is less than -4 or greater than the precision.'' - * -- ANSI X3J11 - */ - if (expcnt > prec || !expcnt && fract && fract < .0001) { - /* - * g/G format counts "significant digits, not digits of - * precision; for the e/E format, this just causes an - * off-by-one problem, i.e. g/G considers the digit - * before the decimal point significant and e/E doesn't - * count it as precision. - */ - --prec; - fmtch -= 2; /* G->E, g->e */ - gformat = 1; - goto eformat; - } - /* - * reverse integer into beginning of buffer, - * note, decrement precision - */ - if (expcnt) - for (; ++p < endp; *t++ = *p, --prec); - else - *t++ = '0'; - /* - * if precision required or alternate flag set, add in a - * decimal point. If no digits yet, add in leading 0. - */ - if (prec || flags&ALT) { - dotrim = 1; - *t++ = '.'; - } - else - dotrim = 0; - /* if requires more precision and some fraction left */ - if (fract) { - if (prec) { - do { - fract = modf(fract * 10, &tmp); - *t++ = to_char((int)tmp); - } while(!tmp); - while (--prec && fract) { - fract = modf(fract * 10, &tmp); - *t++ = to_char((int)tmp); - } - } - if (fract) - startp = round(fract, (int *)NULL, startp, - t - 1, (char)0, signp); - } - /* alternate format, adds 0's for precision, else trim 0's */ - if (flags&ALT) - for (; prec--; *t++ = '0'); - else if (dotrim) { - while (t > startp && *--t == '0'); - if (*t != '.') - ++t; + if (value < 0) { + value = -value; + *sign = '-'; + } else + *sign = '\000'; + digits = __dtoa(value, mode, ndigits, decpt, &dsgn, &rve); + if (flags & ALT) { /* Print trailing zeros */ + bp = digits + ndigits; + if (ch == 'f') { + if (*digits == '0' && value) + *decpt = -ndigits + 1; + bp += *decpt; } + if (value == 0) /* kludge for __dtoa irregularity */ + rve = bp; + while (rve < bp) + *rve++ = '0'; } - return (t - startp); -} - -static char * -round(fract, exp, start, end, ch, signp) - double fract; - int *exp; - register char *start, *end; - char ch, *signp; -{ - double tmp; - - if (fract) - (void)modf(fract * 10, &tmp); - else - tmp = to_digit(ch); - if (tmp > 4) - for (;; --end) { - if (*end == '.') - --end; - if (++*end <= '9') - break; - *end = '0'; - if (end == start) { - if (exp) { /* e/E; increment exponent */ - *end = '1'; - ++*exp; - } - else { /* f; add extra digit */ - *--end = '1'; - --start; - } - break; - } - } - /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ - else if (*signp == '-') - for (;; --end) { - if (*end == '.') - --end; - if (*end != '0') - break; - if (end == start) - *signp = 0; - } - return (start); + *length = rve - digits; + return (digits); } -static char * -exponent(p, exp, fmtch) - register char *p; - register int exp; - int fmtch; +static int +exponent(p0, exp, fmtch) + char *p0; + int exp, fmtch; { - register char *t; + register char *p, *t; char expbuf[MAXEXP]; + p = p0; *p++ = fmtch; if (exp < 0) { exp = -exp; @@ -981,6 +756,6 @@ exponent(p, exp, fmtch) *p++ = '0'; *p++ = to_char(exp); } - return (p); + return (p - p0); } #endif /* FLOATING_POINT */ diff --git a/lib/libc/stdio/vfscanf.c b/lib/libc/stdio/vfscanf.c index f8c9f05182..7f9005dfc7 100644 --- a/lib/libc/stdio/vfscanf.c +++ b/lib/libc/stdio/vfscanf.c @@ -35,7 +35,7 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)vfscanf.c 5.6 (Berkeley) 2/24/91"; +static char sccsid[] = "@(#)vfscanf.c 5.7 (Berkeley) 12/14/92"; #endif /* LIBC_SCCS and not lint */ #include @@ -50,12 +50,8 @@ static char sccsid[] = "@(#)vfscanf.c 5.6 (Berkeley) 2/24/91"; #define FLOATING_POINT -#ifdef FLOATING_POINT #include "floatio.h" -#define BUF (MAXEXP+MAXFRACT+3) /* 3 = sign + decimal point + NUL */ -#else -#define BUF 40 -#endif +#define BUF 513 /* Maximum length of numeric string. */ /* * Flags used during conversion. @@ -635,7 +631,7 @@ literal: double res; *p = 0; - res = atof(buf); + res = strtod(buf,(char **) NULL); if (flags & LONG) *va_arg(ap, double *) = res; else diff --git a/lib/libc/stdlib/Makefile.inc b/lib/libc/stdlib/Makefile.inc index 658eba2d99..972887fbd8 100644 --- a/lib/libc/stdlib/Makefile.inc +++ b/lib/libc/stdlib/Makefile.inc @@ -4,26 +4,34 @@ .PATH: ${.CURDIR}/${MACHINE}/stdlib ${.CURDIR}/stdlib SRCS+= abort.c atexit.c atoi.c atol.c bsearch.c calloc.c div.c exit.c \ - getenv.c getopt.c heapsort.c labs.c ldiv.c malloc.c multibyte.c \ - putenv.c qsort.c radixsort.c rand.c random.c setenv.c strtol.c \ - strtoul.c system.c + getenv.c getopt.c heapsort.c malloc.c multibyte.c \ + putenv.c qsort.c radixsort.c rand.c random.c setenv.c strtod.c \ + strtol.c strtoul.c system.c \ + _rand48.c drand48.c erand48.c jrand48.c lcong48.c lrand48.c \ + mrand48.c nrand48.c seed48.c srand48.c .if (${MACHINE} == "hp300") -SRCS+= abs.s atof.c +SRCS+= abs.s div.c labs.c ldiv.c atof.c .elif (${MACHINE} == "i386") -SRCS+= abs.s atof.c +SRCS+= abs.s div.s labs.s ldiv.s atof.c .elif (${MACHINE} == "tahoe") -SRCS+= abs.s atof.s +SRCS+= abs.s div.c labs.c ldiv.c atof.c .elif (${MACHINE} == "vax") -SRCS+= abs.s atof.s +SRCS+= abs.s div.c labs.c ldiv.c atof.c .endif -MAN3+= abort.0 abs.0 alloca.0 atexit.0 atof.0 atoi.0 atol.0 bsearch.0 \ - calloc.0 div.0 exit.0 free.0 getenv.0 getopt.0 labs.0 ldiv.0 \ - malloc.0 memory.0 qsort.0 radixsort.0 rand.0 random.0 realloc.0 \ - strtod.0 strtol.0 strtoul.0 system.0 +MAN3+= stdlib/abort.3 stdlib/abs.3 stdlib/alloca.3 stdlib/atexit.3 \ + stdlib/atof.3 stdlib/atoi.3 stdlib/atol.3 stdlib/bsearch.3 \ + stdlib/calloc.3 stdlib/div.3 stdlib/exit.3 stdlib/free.3 \ + stdlib/getenv.3 stdlib/getopt.3 stdlib/labs.3 stdlib/ldiv.3 \ + stdlib/malloc.3 stdlib/memory.3 stdlib/qsort.3 stdlib/radixsort.3 \ + stdlib/rand.3 stdlib/rand48.3 stdlib/random.3 stdlib/realloc.3 \ + stdlib/strtod.3 stdlib/strtol.3 stdlib/strtoul.3 stdlib/system.3 MLINKS+=getenv.3 setenv.3 getenv.3 unsetenv.3 getenv.3 putenv.3 MLINKS+=qsort.3 heapsort.3 MLINKS+=rand.3 srand.3 MLINKS+=random.3 initstate.3 random.3 setstate.3 random.3 srandom.3 +MLINKS+=rand48.3 drand48.3 rand48.3 erand48.3 rand48.3 lrand48.3 +MLINKS+=rand48.3 mrand48.3 rand48.3 nrand48.3 rand48.3 jrand48.3 +MLINKS+=rand48.3 srand48.3 rand48.3 seed48.3 rand48.3 lcong48.3 diff --git a/lib/libc/stdlib/_rand48.c b/lib/libc/stdlib/_rand48.c new file mode 100644 index 0000000000..990e2c8694 --- /dev/null +++ b/lib/libc/stdlib/_rand48.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 1993 Martin Birgmeier + * All rights reserved. + * + * You may redistribute unmodified or modified versions of this source + * code provided that the above copyright notice and this and the + * following conditions are retained. + * + * This software is provided ``as is'', and comes with no warranties + * of any kind. I shall in no event be liable for anything that happens + * to anyone/anything when using this software. + */ + +#include "rand48.h" + +unsigned short _rand48_seed[3] = { + RAND48_SEED_0, + RAND48_SEED_1, + RAND48_SEED_2 +}; +unsigned short _rand48_mult[3] = { + RAND48_MULT_0, + RAND48_MULT_1, + RAND48_MULT_2 +}; +unsigned short _rand48_add = RAND48_ADD; + +void +_dorand48(unsigned short xseed[3]) +{ + unsigned long accu; + unsigned short temp[2]; + + accu = (unsigned long) _rand48_mult[0] * (unsigned long) xseed[0] + + (unsigned long) _rand48_add; + temp[0] = (unsigned short) accu; /* lower 16 bits */ + accu >>= sizeof(unsigned short) * 8; + accu += (unsigned long) _rand48_mult[0] * (unsigned long) xseed[1] + + (unsigned long) _rand48_mult[1] * (unsigned long) xseed[0]; + temp[1] = (unsigned short) accu; /* middle 16 bits */ + accu >>= sizeof(unsigned short) * 8; + accu += _rand48_mult[0] * xseed[2] + _rand48_mult[1] * xseed[1] + _rand48_mult[2] * xseed[0]; + xseed[0] = temp[0]; + xseed[1] = temp[1]; + xseed[2] = (unsigned short) accu; +} diff --git a/lib/libc/stdlib/atof.c b/lib/libc/stdlib/atof.c index 38fd0a9f09..c3577abfa2 100644 --- a/lib/libc/stdlib/atof.c +++ b/lib/libc/stdlib/atof.c @@ -32,7 +32,7 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)atof.c 5.2 (Berkeley) 6/1/90"; +static char sccsid[] = "@(#)atof.c 5.3 (Berkeley) 1/8/93"; #endif /* LIBC_SCCS and not lint */ #include @@ -40,7 +40,7 @@ static char sccsid[] = "@(#)atof.c 5.2 (Berkeley) 6/1/90"; double atof(ascii) - char *ascii; + const char *ascii; { return(strtod(ascii, (char **)NULL)); } diff --git a/lib/libc/stdlib/drand48.c b/lib/libc/stdlib/drand48.c new file mode 100644 index 0000000000..cec04a6a24 --- /dev/null +++ b/lib/libc/stdlib/drand48.c @@ -0,0 +1,22 @@ +/* + * Copyright (c) 1993 Martin Birgmeier + * All rights reserved. + * + * You may redistribute unmodified or modified versions of this source + * code provided that the above copyright notice and this and the + * following conditions are retained. + * + * This software is provided ``as is'', and comes with no warranties + * of any kind. I shall in no event be liable for anything that happens + * to anyone/anything when using this software. + */ + +#include "rand48.h" + +extern unsigned short _rand48_seed[3]; + +double +drand48(void) +{ + return erand48(_rand48_seed); +} diff --git a/lib/libc/stdlib/erand48.c b/lib/libc/stdlib/erand48.c new file mode 100644 index 0000000000..286904c278 --- /dev/null +++ b/lib/libc/stdlib/erand48.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 1993 Martin Birgmeier + * All rights reserved. + * + * You may redistribute unmodified or modified versions of this source + * code provided that the above copyright notice and this and the + * following conditions are retained. + * + * This software is provided ``as is'', and comes with no warranties + * of any kind. I shall in no event be liable for anything that happens + * to anyone/anything when using this software. + */ + +#include "rand48.h" + +double +erand48(unsigned short xseed[3]) +{ + _dorand48(xseed); + return ldexp((double) xseed[0], -48) + + ldexp((double) xseed[1], -32) + + ldexp((double) xseed[2], -16); +} diff --git a/lib/libc/stdlib/jrand48.c b/lib/libc/stdlib/jrand48.c new file mode 100644 index 0000000000..051d5a69f5 --- /dev/null +++ b/lib/libc/stdlib/jrand48.c @@ -0,0 +1,21 @@ +/* + * Copyright (c) 1993 Martin Birgmeier + * All rights reserved. + * + * You may redistribute unmodified or modified versions of this source + * code provided that the above copyright notice and this and the + * following conditions are retained. + * + * This software is provided ``as is'', and comes with no warranties + * of any kind. I shall in no event be liable for anything that happens + * to anyone/anything when using this software. + */ + +#include "rand48.h" + +long +jrand48(unsigned short xseed[3]) +{ + _dorand48(xseed); + return ((long) xseed[2] << 16) + (long) xseed[1]; +} diff --git a/lib/libc/stdlib/lcong48.c b/lib/libc/stdlib/lcong48.c new file mode 100644 index 0000000000..f13826b3d3 --- /dev/null +++ b/lib/libc/stdlib/lcong48.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 1993 Martin Birgmeier + * All rights reserved. + * + * You may redistribute unmodified or modified versions of this source + * code provided that the above copyright notice and this and the + * following conditions are retained. + * + * This software is provided ``as is'', and comes with no warranties + * of any kind. I shall in no event be liable for anything that happens + * to anyone/anything when using this software. + */ + +#include "rand48.h" + +extern unsigned short _rand48_seed[3]; +extern unsigned short _rand48_mult[3]; +extern unsigned short _rand48_add; + +void +lcong48(unsigned short p[7]) +{ + _rand48_seed[0] = p[0]; + _rand48_seed[1] = p[1]; + _rand48_seed[2] = p[2]; + _rand48_mult[0] = p[3]; + _rand48_mult[1] = p[4]; + _rand48_mult[2] = p[5]; + _rand48_add = p[6]; +} diff --git a/lib/libc/stdlib/lrand48.c b/lib/libc/stdlib/lrand48.c new file mode 100644 index 0000000000..a3d0111cf4 --- /dev/null +++ b/lib/libc/stdlib/lrand48.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 1993 Martin Birgmeier + * All rights reserved. + * + * You may redistribute unmodified or modified versions of this source + * code provided that the above copyright notice and this and the + * following conditions are retained. + * + * This software is provided ``as is'', and comes with no warranties + * of any kind. I shall in no event be liable for anything that happens + * to anyone/anything when using this software. + */ + +#include "rand48.h" + +extern unsigned short _rand48_seed[3]; + +long +lrand48(void) +{ + _dorand48(_rand48_seed); + return ((long) _rand48_seed[2] << 15) + ((long) _rand48_seed[1] >> 1); +} diff --git a/lib/libc/stdlib/mrand48.c b/lib/libc/stdlib/mrand48.c new file mode 100644 index 0000000000..b23db51185 --- /dev/null +++ b/lib/libc/stdlib/mrand48.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 1993 Martin Birgmeier + * All rights reserved. + * + * You may redistribute unmodified or modified versions of this source + * code provided that the above copyright notice and this and the + * following conditions are retained. + * + * This software is provided ``as is'', and comes with no warranties + * of any kind. I shall in no event be liable for anything that happens + * to anyone/anything when using this software. + */ + +#include "rand48.h" + +extern unsigned short _rand48_seed[3]; + +long +mrand48(void) +{ + _dorand48(_rand48_seed); + return ((long) _rand48_seed[2] << 16) + (long) _rand48_seed[1]; +} diff --git a/lib/libc/stdlib/nrand48.c b/lib/libc/stdlib/nrand48.c new file mode 100644 index 0000000000..6c54065e7e --- /dev/null +++ b/lib/libc/stdlib/nrand48.c @@ -0,0 +1,21 @@ +/* + * Copyright (c) 1993 Martin Birgmeier + * All rights reserved. + * + * You may redistribute unmodified or modified versions of this source + * code provided that the above copyright notice and this and the + * following conditions are retained. + * + * This software is provided ``as is'', and comes with no warranties + * of any kind. I shall in no event be liable for anything that happens + * to anyone/anything when using this software. + */ + +#include "rand48.h" + +long +nrand48(unsigned short xseed[3]) +{ + _dorand48(xseed); + return ((long) xseed[2] << 15) + ((long) xseed[1] >> 1); +} diff --git a/lib/libc/stdlib/rand48.3 b/lib/libc/stdlib/rand48.3 new file mode 100644 index 0000000000..fead60db46 --- /dev/null +++ b/lib/libc/stdlib/rand48.3 @@ -0,0 +1,163 @@ +\" Copyright (c) 1993 Martin Birgmeier +.\" All rights reserved. +.\" +.\" You may redistribute unmodified or modified versions of this source +.\" code provided that the above copyright notice and this and the +.\" following conditions are retained. +.\" +.\" This software is provided ``as is'', and comes with no warranties +.\" of any kind. I shall in no event be liable for anything that happens +.\" to anyone/anything when using this software. +.\" +.\" @(#)rand48.3 V1.0 MB 8 Oct 1993 +.\" +.Dd October 8, 1993 +.Dt RAND48 3 +.Os FreeBSD +.Sh NAME +.Nm drand48 , +.Nm erand48 , +.Nm lrand48 , +.Nm nrand48 , +.Nm mrand48 , +.Nm jrand48 , +.Nm srand48 , +.Nm seed48 , +.Nm lcong48 +.Nd pseudo random number generators and initialization routines +.Sh SYNOPSIS +.Fd #include +.Ft double +.Fn drand48 void +.Ft double +.Fn erand48 "unsigned short xseed[3]" +.Ft long +.Fn lrand48 void +.Ft long +.Fn nrand48 "unsigned short xseed[3]" +.Ft long +.Fn mrand48 void +.Ft long +.Fn jrand48 "unsigned short xseed[3]" +.Ft void +.Fn srand48 "long seed" +.Ft "unsigned short *" +.Fn seed48 "unsigned short xseed[3]" +.Ft void +.Fn lcong48 "unsigned short p[7]" +.Sh DESCRIPTION +The +.Fn rand48 +family of functions generates pseudo-random numbers using a linear +congruential algorithm working on integers 48 bits in size. The +particular formula employed is +r(n+1) = (a * r(n) + c) mod m +where the default values are +for the multiplicand a = 0xfdeece66d = 25214903917 and +the addend c = 0xb = 11. The modul is always fixed at m = 2 ** 48. +r(n) is called the seed of the random number generator. +.Pp +For all the six generator routines described next, the first +computational step is to perform a single iteration of the algorithm. +.Pp +.Fn drand48 +and +.Fn erand48 +return values of type double. The full 48 bits of r(n+1) are +loaded into the mantissa of the returned value, with the exponent set +such that the values produced lie in the interval [0.0, 1.0). +.Pp +.Fn lrand48 +and +.Fn nrand48 +return values of type long in the range +[-2**31, 2**31-1]. The high-order (32) bits of +r(n+1) are loaded into the returned value. +.Pp +.Fn mrand48 +and +.Fn jrand48 +return values of type long in the range +[0, 2**31-1]. The high-order (31) bits of +r(n+1) are loaded into the lower bits of the returned value, with +the topmost (sign) bit set to zero. +.Pp +.Fn drand48 , +.Fn lrand48 , +and +.Fn mrand48 +use an internal buffer to store r(n). For these functions +the initial value of r(0) = 0x1234abcd330e = 20017429951246. +.Pp +On the other hand, +.Fn erand48 , +.Fn nrand48 , +and +.Fn jrand48 +use a user-supplied buffer to store the seed r(n), +which consists of an array of 3 shorts, where the zeroth member +holds the least significant bits. +.Pp +All functions share the same multiplicand and addend. +.Pp +.Fn srand48 +is used to initialize the internal buffer r(n) of +.Fn drand48 , +.Fn lrand48 , +and +.Fn mrand48 +such that the 32 bits of the seed value are copied into the upper 32 bits +of r(n), with the lower 16 bits of r(n) arbitrarily being set to 0x330e. +Additionally, the constant multiplicand and addend of the algorithm are +reset to the default values given above. +.Pp +.Fn seed48 +also initializes the internal buffer r(n) of +.Fn drand48 , +.Fn lrand48 , +and +.Fn mrand48 , +but here all 48 bits of the seed can be specified in an array of 3 shorts, +where the zeroth member specifies the lowest bits. Again, +the constant multiplicand and addend of the algorithm are +reset to the default values given above. +.Fn seed48 +returns a pointer to an array of 3 shorts which contains the old seed. +This array is statically allocated, thus its contents are lost after +each new call to +.Fn seed48 . +.Pp +Finally, +.Fn lcong48 +allows full control over the multiplicand and addend used in +.Fn drand48 , +.Fn erand48 , +.Fn lrand48 , +.Fn nrand48 , +.Fn mrand48 , +and +.Fn jrand48 , +and the seed used in +.Fn drand48 , +.Fn lrand48 , +and +.Fn mrand48 . +An array of 7 shorts is passed as parameter; the first three shorts are +used to initialize the seed; the second three are used to initialize the +multiplicand; and the last short is used to initialize the addend. +It is thus not possible to use values greater than 0xffff as the addend. +.Pp +Note that all three methods of seeding the random number generator +always also set the multiplicand and addend for any of the six +generator calls. +.Pp +For a more powerful random number generator, see +.Xr random 3 +.Sh AUTHOR +Martin Birgmeier +.Sh SEE ALSO +.Xr rand 3 , +.Xr random 3 . +============================== cut here ============================== + + diff --git a/lib/libc/stdlib/rand48.h b/lib/libc/stdlib/rand48.h new file mode 100644 index 0000000000..5b9f87d176 --- /dev/null +++ b/lib/libc/stdlib/rand48.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 1993 Martin Birgmeier + * All rights reserved. + * + * You may redistribute unmodified or modified versions of this source + * code provided that the above copyright notice and this and the + * following conditions are retained. + * + * This software is provided ``as is'', and comes with no warranties + * of any kind. I shall in no event be liable for anything that happens + * to anyone/anything when using this software. + */ + +#ifndef _RAND48_H_ +#define _RAND48_H_ + +#include +#include + +void _dorand48 __P((unsigned short[3])); + +#define RAND48_SEED_0 (0x330e) +#define RAND48_SEED_1 (0xabcd) +#define RAND48_SEED_2 (0x1234) +#define RAND48_MULT_0 (0xe66d) +#define RAND48_MULT_1 (0xdeec) +#define RAND48_MULT_2 (0x0005) +#define RAND48_ADD (0x000b) + +#endif /* _RAND48_H_ */ diff --git a/lib/libc/stdlib/seed48.c b/lib/libc/stdlib/seed48.c new file mode 100644 index 0000000000..258c4bac3c --- /dev/null +++ b/lib/libc/stdlib/seed48.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 1993 Martin Birgmeier + * All rights reserved. + * + * You may redistribute unmodified or modified versions of this source + * code provided that the above copyright notice and this and the + * following conditions are retained. + * + * This software is provided ``as is'', and comes with no warranties + * of any kind. I shall in no event be liable for anything that happens + * to anyone/anything when using this software. + */ + +#include "rand48.h" + +extern unsigned short _rand48_seed[3]; +extern unsigned short _rand48_mult[3]; +extern unsigned short _rand48_add; + +unsigned short * +seed48(unsigned short xseed[3]) +{ + static unsigned short sseed[3]; + + sseed[0] = _rand48_seed[0]; + sseed[1] = _rand48_seed[1]; + sseed[2] = _rand48_seed[2]; + _rand48_seed[0] = xseed[0]; + _rand48_seed[1] = xseed[1]; + _rand48_seed[2] = xseed[2]; + _rand48_mult[0] = RAND48_MULT_0; + _rand48_mult[1] = RAND48_MULT_1; + _rand48_mult[2] = RAND48_MULT_2; + _rand48_add = RAND48_ADD; + return sseed; +} diff --git a/lib/libc/stdlib/srand48.c b/lib/libc/stdlib/srand48.c new file mode 100644 index 0000000000..fd369a094c --- /dev/null +++ b/lib/libc/stdlib/srand48.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 1993 Martin Birgmeier + * All rights reserved. + * + * You may redistribute unmodified or modified versions of this source + * code provided that the above copyright notice and this and the + * following conditions are retained. + * + * This software is provided ``as is'', and comes with no warranties + * of any kind. I shall in no event be liable for anything that happens + * to anyone/anything when using this software. + */ + +#include "rand48.h" + +extern unsigned short _rand48_seed[3]; +extern unsigned short _rand48_mult[3]; +extern unsigned short _rand48_add; + +void +srand48(long seed) +{ + _rand48_seed[0] = RAND48_SEED_0; + _rand48_seed[1] = (unsigned short) seed; + _rand48_seed[2] = (unsigned short) (seed >> 16); + _rand48_mult[0] = RAND48_MULT_0; + _rand48_mult[1] = RAND48_MULT_1; + _rand48_mult[2] = RAND48_MULT_2; + _rand48_add = RAND48_ADD; +} diff --git a/lib/libc/stdlib/strtod.c b/lib/libc/stdlib/strtod.c new file mode 100644 index 0000000000..26bd80d2e8 --- /dev/null +++ b/lib/libc/stdlib/strtod.c @@ -0,0 +1,2410 @@ +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strtod.c 5.1 (Berkeley) 11/13/92"; +#endif /* LIBC_SCCS and not lint */ + +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991 by AT&T. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR AT&T MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ + +/* Please send bug reports to + David M. Gay + AT&T Bell Laboratories, Room 2C-463 + 600 Mountain Avenue + Murray Hill, NJ 07974-2070 + U.S.A. + dmg@research.att.com or research!dmg + */ + +/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. + * + * This strtod returns a nearest machine number to the input decimal + * string (or sets errno to ERANGE). With IEEE arithmetic, ties are + * broken by the IEEE round-even rule. Otherwise ties are broken by + * biased rounding (add half and chop). + * + * Inspired loosely by William D. Clinger's paper "How to Read Floating + * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * + * 1. We only require IEEE, IBM, or VAX double-precision + * arithmetic (not IEEE double-extended). + * 2. We get by with floating-point arithmetic in a case that + * Clinger missed -- when we're computing d * 10^n + * for a small integer d and the integer n is not too + * much larger than 22 (the maximum integer k for which + * we can represent 10^k exactly), we may be able to + * compute (d*10^k) * 10^(e-k) with just one roundoff. + * 3. Rather than a bit-at-a-time adjustment of the binary + * result in the hard case, we use floating-point + * arithmetic to determine the adjustment to within + * one bit; only in really hard cases do we need to + * compute a second residual. + * 4. Because of 3., we don't need a large table of powers of 10 + * for ten-to-e (just some small tables, e.g. of 10^k + * for 0 <= k <= 22). + */ + +/* + * #define IEEE_8087 for IEEE-arithmetic machines where the least + * significant byte has the lowest address. + * #define IEEE_MC68k for IEEE-arithmetic machines where the most + * significant byte has the lowest address. + * #define Sudden_Underflow for IEEE-format machines without gradual + * underflow (i.e., that flush to zero on underflow). + * #define IBM for IBM mainframe-style floating-point arithmetic. + * #define VAX for VAX-style floating-point arithmetic. + * #define Unsigned_Shifts if >> does treats its left operand as unsigned. + * #define No_leftright to omit left-right logic in fast floating-point + * computation of dtoa. + * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3. + * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines + * that use extended-precision instructions to compute rounded + * products and quotients) with IBM. + * #define ROUND_BIASED for IEEE-format with biased rounding. + * #define Inaccurate_Divide for IEEE-format with correctly rounded + * products but inaccurate quotients, e.g., for Intel i860. + * #define Just_16 to store 16 bits per 32-bit long when doing high-precision + * integer arithmetic. Whether this speeds things up or slows things + * down depends on the machine and the number being converted. + * #define KR_headers for old-style C function headers. + * #define Bad_float_h if your system lacks a float.h or if it does not + * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, + * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. + */ + +#define IEEE_8087 + +#ifdef DEBUG +#include "stdio.h" +#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} +#endif + +#ifdef __cplusplus +#include "malloc.h" +#include "memory.h" +#else +#ifndef KR_headers +#include "stdlib.h" +#include "string.h" +#else +#include "malloc.h" +#include "memory.h" +#endif +#endif + +#include "errno.h" +#ifdef Bad_float_h +#undef __STDC__ +#ifdef IEEE_MC68k +#define IEEE_ARITHMETIC +#endif +#ifdef IEEE_8087 +#define IEEE_ARITHMETIC +#endif +#ifdef IEEE_ARITHMETIC +#define DBL_DIG 15 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define FLT_RADIX 2 +#define FLT_ROUNDS 1 +#define DBL_MAX 1.7976931348623157e+308 +#endif + +#ifdef IBM +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 75 +#define DBL_MAX_EXP 63 +#define FLT_RADIX 16 +#define FLT_ROUNDS 0 +#define DBL_MAX 7.2370055773322621e+75 +#endif + +#ifdef VAX +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 38 +#define DBL_MAX_EXP 127 +#define FLT_RADIX 2 +#define FLT_ROUNDS 1 +#define DBL_MAX 1.7014118346046923e+38 +#endif + +#ifndef LONG_MAX +#define LONG_MAX 2147483647 +#endif +#else +#include "float.h" +#endif +#ifndef __MATH_H__ +#include "math.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef CONST +#ifdef KR_headers +#define CONST /* blank */ +#else +#define CONST const +#endif +#endif + +#ifdef Unsigned_Shifts +#define Sign_Extend(a,b) if (b < 0) a |= 0xffff0000; +#else +#define Sign_Extend(a,b) /*no-op*/ +#endif + +#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1 +Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. +#endif + +#ifdef IEEE_8087 +#define word0(x) ((unsigned long *)&x)[1] +#define word1(x) ((unsigned long *)&x)[0] +#else +#define word0(x) ((unsigned long *)&x)[0] +#define word1(x) ((unsigned long *)&x)[1] +#endif + +/* The following definition of Storeinc is appropriate for MIPS processors. + * An alternative that might be better on some machines is + * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) + */ +#if defined(IEEE_8087) + defined(VAX) +#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ +((unsigned short *)a)[0] = (unsigned short)c, a++) +#else +#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ +((unsigned short *)a)[1] = (unsigned short)c, a++) +#endif + +/* #define P DBL_MANT_DIG */ +/* Ten_pmax = floor(P*log(2)/log(5)) */ +/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ +/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ +/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ + +#if defined(IEEE_8087) + defined(IEEE_MC68k) +#define Exp_shift 20 +#define Exp_shift1 20 +#define Exp_msk1 0x100000 +#define Exp_msk11 0x100000 +#define Exp_mask 0x7ff00000 +#define P 53 +#define Bias 1023 +#define IEEE_Arith +#define Emin (-1022) +#define Exp_1 0x3ff00000 +#define Exp_11 0x3ff00000 +#define Ebits 11 +#define Frac_mask 0xfffff +#define Frac_mask1 0xfffff +#define Ten_pmax 22 +#define Bletch 0x10 +#define Bndry_mask 0xfffff +#define Bndry_mask1 0xfffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 1 +#define Tiny0 0 +#define Tiny1 1 +#define Quick_max 14 +#define Int_max 14 +#define Infinite(x) (word0(x) == 0x7ff00000) /* sufficient test for here */ +#else +#undef Sudden_Underflow +#define Sudden_Underflow +#ifdef IBM +#define Exp_shift 24 +#define Exp_shift1 24 +#define Exp_msk1 0x1000000 +#define Exp_msk11 0x1000000 +#define Exp_mask 0x7f000000 +#define P 14 +#define Bias 65 +#define Exp_1 0x41000000 +#define Exp_11 0x41000000 +#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ +#define Frac_mask 0xffffff +#define Frac_mask1 0xffffff +#define Bletch 4 +#define Ten_pmax 22 +#define Bndry_mask 0xefffff +#define Bndry_mask1 0xffffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 4 +#define Tiny0 0x100000 +#define Tiny1 0 +#define Quick_max 14 +#define Int_max 15 +#else /* VAX */ +#define Exp_shift 23 +#define Exp_shift1 7 +#define Exp_msk1 0x80 +#define Exp_msk11 0x800000 +#define Exp_mask 0x7f80 +#define P 56 +#define Bias 129 +#define Exp_1 0x40800000 +#define Exp_11 0x4080 +#define Ebits 8 +#define Frac_mask 0x7fffff +#define Frac_mask1 0xffff007f +#define Ten_pmax 24 +#define Bletch 2 +#define Bndry_mask 0xffff007f +#define Bndry_mask1 0xffff007f +#define LSB 0x10000 +#define Sign_bit 0x8000 +#define Log2P 1 +#define Tiny0 0x80 +#define Tiny1 0 +#define Quick_max 15 +#define Int_max 15 +#endif +#endif + +#ifndef IEEE_Arith +#define ROUND_BIASED +#endif + +#ifdef RND_PRODQUOT +#define rounded_product(a,b) a = rnd_prod(a, b) +#define rounded_quotient(a,b) a = rnd_quot(a, b) +#ifdef KR_headers +extern double rnd_prod(), rnd_quot(); +#else +extern double rnd_prod(double, double), rnd_quot(double, double); +#endif +#else +#define rounded_product(a,b) a *= b +#define rounded_quotient(a,b) a /= b +#endif + +#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) +#define Big1 0xffffffff + +#ifndef Just_16 +/* When Pack_32 is not defined, we store 16 bits per 32-bit long. + * This makes some inner loops simpler and sometimes saves work + * during multiplications, but it often seems to make things slightly + * slower. Hence the default is now to store 32 bits per long. + */ +#ifndef Pack_32 +#define Pack_32 +#endif +#endif + +#define Kmax 15 + +#ifdef __cplusplus +extern "C" double strtod(const char *s00, char **se); +extern "C" char *dtoa(double d, int mode, int ndigits, + int *decpt, int *sign, char **rve); +#endif + + struct +Bigint { + struct Bigint *next; + int k, maxwds, sign, wds; + unsigned long x[1]; +}; + + typedef struct Bigint Bigint; + + static Bigint *freelist[Kmax+1]; + + static Bigint * +Balloc +#ifdef KR_headers + (k) int k; +#else + (int k) +#endif +{ + int x; + Bigint *rv; + + if (rv = freelist[k]) { + freelist[k] = rv->next; + } else { + x = 1 << k; + rv = (Bigint *)malloc(sizeof(Bigint) + (x-1)*sizeof(long)); + rv->k = k; + rv->maxwds = x; + } + rv->sign = rv->wds = 0; + return rv; +} + + static void +Bfree +#ifdef KR_headers + (v) Bigint *v; +#else + (Bigint *v) +#endif +{ + if (v) { + v->next = freelist[v->k]; + freelist[v->k] = v; + } +} + +#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ +y->wds*sizeof(long) + 2*sizeof(int)) + + static Bigint * +multadd +#ifdef KR_headers + (b, m, a) Bigint *b; int m, a; +#else + (Bigint *b, int m, int a) /* multiply by m and add a */ +#endif +{ + int i, wds; + unsigned long *x, y; +#ifdef Pack_32 + unsigned long xi, z; +#endif + Bigint *b1; + + wds = b->wds; + x = b->x; + i = 0; + do { +#ifdef Pack_32 + xi = *x; + y = (xi & 0xffff) * m + a; + z = (xi >> 16) * m + (y >> 16); + a = (int)(z >> 16); + *x++ = (z << 16) + (y & 0xffff); +#else + y = *x * m + a; + a = (int)(y >> 16); + *x++ = y & 0xffff; +#endif + } while (++i < wds); + if (a) { + if (wds >= b->maxwds) { + b1 = Balloc(b->k+1); + Bcopy(b1, b); + Bfree(b); + b = b1; + } + b->x[wds++] = a; + b->wds = wds; + } + return b; +} + + static Bigint * +s2b +#ifdef KR_headers + (s, nd0, nd, y9) CONST char *s; int nd0, nd; unsigned long y9; +#else + (CONST char *s, int nd0, int nd, unsigned long y9) +#endif +{ + Bigint *b; + int i, k; + long x, y; + + x = (nd + 8) / 9; + for (k = 0, y = 1; x > y; y <<= 1, k++) ; +#ifdef Pack_32 + b = Balloc(k); + b->x[0] = y9; + b->wds = 1; +#else + b = Balloc(k+1); + b->x[0] = y9 & 0xffff; + b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; +#endif + + i = 9; + if (9 < nd0) { + s += 9; + do + b = multadd(b, 10, *s++ - '0'); + while (++i < nd0); + s++; + } else + s += 10; + for (; i < nd; i++) + b = multadd(b, 10, *s++ - '0'); + return b; +} + + static int +hi0bits +#ifdef KR_headers + (x) register unsigned long x; +#else + (register unsigned long x) +#endif +{ + register int k = 0; + + if (!(x & 0xffff0000)) { + k = 16; + x <<= 16; + } + if (!(x & 0xff000000)) { + k += 8; + x <<= 8; + } + if (!(x & 0xf0000000)) { + k += 4; + x <<= 4; + } + if (!(x & 0xc0000000)) { + k += 2; + x <<= 2; + } + if (!(x & 0x80000000)) { + k++; + if (!(x & 0x40000000)) + return 32; + } + return k; +} + + static int +lo0bits +#ifdef KR_headers + (y) unsigned long *y; +#else + (unsigned long *y) +#endif +{ + register int k; + register unsigned long x = *y; + + if (x & 7) { + if (x & 1) + return 0; + if (x & 2) { + *y = x >> 1; + return 1; + } + *y = x >> 2; + return 2; + } + k = 0; + if (!(x & 0xffff)) { + k = 16; + x >>= 16; + } + if (!(x & 0xff)) { + k += 8; + x >>= 8; + } + if (!(x & 0xf)) { + k += 4; + x >>= 4; + } + if (!(x & 0x3)) { + k += 2; + x >>= 2; + } + if (!(x & 1)) { + k++; + x >>= 1; + if (!x & 1) + return 32; + } + *y = x; + return k; +} + + static Bigint * +i2b +#ifdef KR_headers + (i) int i; +#else + (int i) +#endif +{ + Bigint *b; + + b = Balloc(1); + b->x[0] = i; + b->wds = 1; + return b; + } + + static Bigint * +mult +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int k, wa, wb, wc; + unsigned long carry, y, z; + unsigned long *x, *xa, *xae, *xb, *xbe, *xc, *xc0; +#ifdef Pack_32 + unsigned long z2; +#endif + + if (a->wds < b->wds) { + c = a; + a = b; + b = c; + } + k = a->k; + wa = a->wds; + wb = b->wds; + wc = wa + wb; + if (wc > a->maxwds) + k++; + c = Balloc(k); + for (x = c->x, xa = x + wc; x < xa; x++) + *x = 0; + xa = a->x; + xae = xa + wa; + xb = b->x; + xbe = xb + wb; + xc0 = c->x; +#ifdef Pack_32 + for (; xb < xbe; xb++, xc0++) { + if (y = *xb & 0xffff) { + x = xa; + xc = xc0; + carry = 0; + do { + z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; + carry = z >> 16; + z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; + carry = z2 >> 16; + Storeinc(xc, z2, z); + } while (x < xae); + *xc = carry; + } + if (y = *xb >> 16) { + x = xa; + xc = xc0; + carry = 0; + z2 = *xc; + do { + z = (*x & 0xffff) * y + (*xc >> 16) + carry; + carry = z >> 16; + Storeinc(xc, z, z2); + z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; + carry = z2 >> 16; + } while (x < xae); + *xc = z2; + } + } +#else + for (; xb < xbe; xc0++) { + if (y = *xb++) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * y + *xc + carry; + carry = z >> 16; + *xc++ = z & 0xffff; + } while (x < xae); + *xc = carry; + } + } +#endif + for (xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; + c->wds = wc; + return c; +} + + static Bigint *p5s; + + static Bigint * +pow5mult +#ifdef KR_headers + (b, k) Bigint *b; int k; +#else + (Bigint *b, int k) +#endif +{ + Bigint *b1, *p5, *p51; + int i; + static int p05[3] = { 5, 25, 125 }; + + if (i = k & 3) + b = multadd(b, p05[i-1], 0); + + if (!(k >>= 2)) + return b; + if (!(p5 = p5s)) { + /* first time */ + p5 = p5s = i2b(625); + p5->next = 0; + } + for (;;) { + if (k & 1) { + b1 = mult(b, p5); + Bfree(b); + b = b1; + } + if (!(k >>= 1)) + break; + if (!(p51 = p5->next)) { + p51 = p5->next = mult(p5,p5); + p51->next = 0; + } + p5 = p51; + } + return b; +} + + static Bigint * +lshift +#ifdef KR_headers + (b, k) Bigint *b; int k; +#else + (Bigint *b, int k) +#endif +{ + int i, k1, n, n1; + Bigint *b1; + unsigned long *x, *x1, *xe, z; + +#ifdef Pack_32 + n = k >> 5; +#else + n = k >> 4; +#endif + k1 = b->k; + n1 = n + b->wds + 1; + for (i = b->maxwds; n1 > i; i <<= 1) + k1++; + b1 = Balloc(k1); + x1 = b1->x; + for (i = 0; i < n; i++) + *x1++ = 0; + x = b->x; + xe = x + b->wds; +#ifdef Pack_32 + if (k &= 0x1f) { + k1 = 32 - k; + z = 0; + do { + *x1++ = *x << k | z; + z = *x++ >> k1; + } while (x < xe); + if (*x1 = z) + ++n1; + } +#else + if (k &= 0xf) { + k1 = 16 - k; + z = 0; + do { + *x1++ = *x << k & 0xffff | z; + z = *x++ >> k1; + } while (x < xe); + if (*x1 = z) + ++n1; + } +#endif + else + do + *x1++ = *x++; + while (x < xe); + b1->wds = n1 - 1; + Bfree(b); + return b1; +} + + static int +cmp +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + unsigned long *xa, *xa0, *xb, *xb0; + int i, j; + + i = a->wds; + j = b->wds; +#ifdef DEBUG + if (i > 1 && !a->x[i-1]) + Bug("cmp called with a->x[a->wds-1] == 0"); + if (j > 1 && !b->x[j-1]) + Bug("cmp called with b->x[b->wds-1] == 0"); +#endif + if (i -= j) + return i; + xa0 = a->x; + xa = xa0 + j; + xb0 = b->x; + xb = xb0 + j; + for (;;) { + if (*--xa != *--xb) + return *xa < *xb ? -1 : 1; + if (xa <= xa0) + break; + } + return 0; +} + + static Bigint * +diff +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int i, wa, wb; + long borrow, y; /* We need signed shifts here. */ + unsigned long *xa, *xae, *xb, *xbe, *xc; +#ifdef Pack_32 + long z; +#endif + + i = cmp(a,b); + if (!i) { + c = Balloc(0); + c->wds = 1; + c->x[0] = 0; + return c; + } + if (i < 0) { + c = a; + a = b; + b = c; + i = 1; + } else + i = 0; + c = Balloc(a->k); + c->sign = i; + wa = a->wds; + xa = a->x; + xae = xa + wa; + wb = b->wds; + xb = b->x; + xbe = xb + wb; + xc = c->x; + borrow = 0; +#ifdef Pack_32 + do { + y = (*xa & 0xffff) - (*xb & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + z = (*xa++ >> 16) - (*xb++ >> 16) + borrow; + borrow = z >> 16; + Sign_Extend(borrow, z); + Storeinc(xc, z, y); + } while (xb < xbe); + while (xa < xae) { + y = (*xa & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + z = (*xa++ >> 16) + borrow; + borrow = z >> 16; + Sign_Extend(borrow, z); + Storeinc(xc, z, y); + } +#else + do { + y = *xa++ - *xb++ + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + *xc++ = y & 0xffff; + } while (xb < xbe); + while (xa < xae) { + y = *xa++ + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + *xc++ = y & 0xffff; + } +#endif + while (!*--xc) + wa--; + c->wds = wa; + return c; +} + + static double +ulp +#ifdef KR_headers + (x) double x; +#else + (double x) +#endif +{ + register long L; + double a; + + L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; +#ifndef Sudden_Underflow + if (L > 0) { +#endif +#ifdef IBM + L |= Exp_msk1 >> 4; +#endif + word0(a) = L; + word1(a) = 0; +#ifndef Sudden_Underflow + } else { + L = -L >> Exp_shift; + if (L < Exp_shift) { + word0(a) = 0x80000 >> L; + word1(a) = 0; + } else { + word0(a) = 0; + L -= Exp_shift; + word1(a) = L >= 31 ? 1 : 1 << 31 - L; + } + } +#endif + return a; +} + + static double +b2d +#ifdef KR_headers + (a, e) Bigint *a; int *e; +#else + (Bigint *a, int *e) +#endif +{ + unsigned long *xa, *xa0, w, y, z; + int k; + double d; +#ifdef VAX + unsigned long d0, d1; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + + xa0 = a->x; + xa = xa0 + a->wds; + y = *--xa; +#ifdef DEBUG + if (!y) Bug("zero y in b2d"); +#endif + k = hi0bits(y); + *e = 32 - k; +#ifdef Pack_32 + if (k < Ebits) { + d0 = Exp_1 | y >> Ebits - k; + w = xa > xa0 ? *--xa : 0; + d1 = y << (32-Ebits) + k | w >> Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + if (k -= Ebits) { + d0 = Exp_1 | y << k | z >> 32 - k; + y = xa > xa0 ? *--xa : 0; + d1 = z << k | y >> 32 - k; + } else { + d0 = Exp_1 | y; + d1 = z; + } +#else + if (k < Ebits + 16) { + z = xa > xa0 ? *--xa : 0; + d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k; + w = xa > xa0 ? *--xa : 0; + y = xa > xa0 ? *--xa : 0; + d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + w = xa > xa0 ? *--xa : 0; + k -= Ebits + 16; + d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; + y = xa > xa0 ? *--xa : 0; + d1 = w << k + 16 | y << k; +#endif + ret_d: +#ifdef VAX + word0(d) = d0 >> 16 | d0 << 16; + word1(d) = d1 >> 16 | d1 << 16; +#else +#undef d0 +#undef d1 +#endif + return d; +} + + static Bigint * +d2b +#ifdef KR_headers + (d, e, bits) double d; int *e, *bits; +#else + (double d, int *e, int *bits) +#endif +{ + Bigint *b; + int de, i, k; + unsigned long *x, y, z; +#ifdef VAX + unsigned long d0, d1; + d0 = word0(d) >> 16 | word0(d) << 16; + d1 = word1(d) >> 16 | word1(d) << 16; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + +#ifdef Pack_32 + b = Balloc(1); +#else + b = Balloc(2); +#endif + x = b->x; + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ +#ifdef Sudden_Underflow + de = (int)(d0 >> Exp_shift); +#ifndef IBM + z |= Exp_msk11; +#endif +#else + if (de = (int)(d0 >> Exp_shift)) + z |= Exp_msk1; +#endif +#ifdef Pack_32 + if (y = d1) { + if (k = lo0bits(&y)) { + x[0] = y | z << 32 - k; + z >>= k; + } + else + x[0] = y; + i = b->wds = (x[1] = z) ? 2 : 1; + } else { +#ifdef DEBUG + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + x[0] = z; + i = b->wds = 1; + k += 32; + } +#else + if (y = d1) { + if (k = lo0bits(&y)) + if (k >= 16) { + x[0] = y | z << 32 - k & 0xffff; + x[1] = z >> k - 16 & 0xffff; + x[2] = z >> k; + i = 2; + } else { + x[0] = y & 0xffff; + x[1] = y >> 16 | z << 16 - k & 0xffff; + x[2] = z >> k & 0xffff; + x[3] = z >> k+16; + i = 3; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16; + x[2] = z & 0xffff; + x[3] = z >> 16; + i = 3; + } + } else { +#ifdef DEBUG + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + if (k >= 16) { + x[0] = z; + i = 0; + } else { + x[0] = z & 0xffff; + x[1] = z >> 16; + i = 1; + } + k += 32; + } + while (!x[i]) + --i; + b->wds = i + 1; +#endif +#ifndef Sudden_Underflow + if (de) { +#endif +#ifdef IBM + *e = (de - Bias - (P-1) << 2) + k; + *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask); +#else + *e = de - Bias - (P-1) + k; + *bits = P - k; +#endif +#ifndef Sudden_Underflow + } else { + *e = de - Bias - (P-1) + 1 + k; +#ifdef Pack_32 + *bits = 32*i - hi0bits(x[i-1]); +#else + *bits = (i+2)*16 - hi0bits(x[i]); +#endif + } +#endif + return b; +} +#undef d0 +#undef d1 + + static double +ratio +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + double da, db; + int k, ka, kb; + + da = b2d(a, &ka); + db = b2d(b, &kb); +#ifdef Pack_32 + k = ka - kb + 32*(a->wds - b->wds); +#else + k = ka - kb + 16*(a->wds - b->wds); +#endif +#ifdef IBM + if (k > 0) { + word0(da) += (k >> 2)*Exp_msk1; + if (k &= 3) + da *= 1 << k; + } else { + k = -k; + word0(db) += (k >> 2)*Exp_msk1; + if (k &= 3) + db *= 1 << k; + } +#else + if (k > 0) + word0(da) += k*Exp_msk1; + else { + k = -k; + word0(db) += k*Exp_msk1; + } +#endif + return da / db; +} + + static double +tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 +#ifdef VAX + , 1e23, 1e24 +#endif + }; + + static double +#ifdef IEEE_Arith +bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +static double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 }; +#define n_bigtens 5 +#else +#ifdef IBM +bigtens[] = { 1e16, 1e32, 1e64 }; +static double tinytens[] = { 1e-16, 1e-32, 1e-64 }; +#define n_bigtens 3 +#else +bigtens[] = { 1e16, 1e32 }; +static double tinytens[] = { 1e-16, 1e-32 }; +#define n_bigtens 2 +#endif +#endif + + double +strtod +#ifdef KR_headers + (s00, se) CONST char *s00; char **se; +#else + (CONST char *s00, char **se) +#endif +{ + int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, + e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; + CONST char *s, *s0, *s1; + double aadj, aadj1, adj, rv, rv0; + long L; + unsigned long y, z; + Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; + sign = nz0 = nz = 0; + rv = 0.; + for (s = s00;;s++) switch(*s) { + case '-': + sign = 1; + /* no break */ + case '+': + if (*++s) + goto break2; + /* no break */ + case 0: + s = s00; + goto ret; + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + continue; + default: + goto break2; + } + break2: + if (*s == '0') { + nz0 = 1; + while (*++s == '0') ; + if (!*s) + goto ret; + } + s0 = s; + y = z = 0; + for (nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 9) + y = 10*y + c - '0'; + else if (nd < 16) + z = 10*z + c - '0'; + nd0 = nd; + if (c == '.') { + c = *++s; + if (!nd) { + for (; c == '0'; c = *++s) + nz++; + if (c > '0' && c <= '9') { + s0 = s; + nf += nz; + nz = 0; + goto have_dig; + } + goto dig_done; + } + for (; c >= '0' && c <= '9'; c = *++s) { + have_dig: + nz++; + if (c -= '0') { + nf += nz; + for (i = 1; i < nz; i++) + if (nd++ < 9) + y *= 10; + else if (nd <= DBL_DIG + 1) + z *= 10; + if (nd++ < 9) + y = 10*y + c; + else if (nd <= DBL_DIG + 1) + z = 10*z + c; + nz = 0; + } + } + } + dig_done: + e = 0; + if (c == 'e' || c == 'E') { + if (!nd && !nz && !nz0) { + s = s00; + goto ret; + } + s00 = s; + esign = 0; + switch(c = *++s) { + case '-': + esign = 1; + case '+': + c = *++s; + } + if (c >= '0' && c <= '9') { + while (c == '0') + c = *++s; + if (c > '0' && c <= '9') { + L = c - '0'; + s1 = s; + while ((c = *++s) >= '0' && c <= '9') + L = 10*L + c - '0'; + if (s - s1 > 8 || L > 19999) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + e = 19999; /* safe for 16 bit ints */ + else + e = (int)L; + if (esign) + e = -e; + } else + e = 0; + } else + s = s00; + } + if (!nd) { + if (!nz && !nz0) + s = s00; + goto ret; + } + e1 = e -= nf; + + /* Now we have nd0 digits, starting at s0, followed by a + * decimal point, followed by nd-nd0 digits. The number we're + * after is the integer represented by those digits times + * 10**e */ + + if (!nd0) + nd0 = nd; + k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; + rv = y; + if (k > 9) + rv = tens[k - 9] * rv + z; + if (nd <= DBL_DIG +#ifndef RND_PRODQUOT + && FLT_ROUNDS == 1 +#endif + ) { + if (!e) + goto ret; + if (e > 0) { + if (e <= Ten_pmax) { +#ifdef VAX + goto vax_ovfl_check; +#else + /* rv = */ rounded_product(rv, tens[e]); + goto ret; +#endif + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ + e -= i; + rv *= tens[i]; +#ifdef VAX + /* VAX exponent range is so narrow we must + * worry about overflow here... + */ + vax_ovfl_check: + word0(rv) -= P*Exp_msk1; + /* rv = */ rounded_product(rv, tens[e]); + if ((word0(rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) + goto ovfl; + word0(rv) += P*Exp_msk1; +#else + /* rv = */ rounded_product(rv, tens[e]); +#endif + goto ret; + } + } +#ifndef Inaccurate_Divide + else if (e >= -Ten_pmax) { + /* rv = */ rounded_quotient(rv, tens[-e]); + goto ret; + } +#endif + } + e1 += nd - k; + + /* Get starting approximation = rv * 10**e1 */ + + if (e1 > 0) { + if (i = e1 & 15) + rv *= tens[i]; + if (e1 &= ~15) { + if (e1 > DBL_MAX_10_EXP) { + ovfl: + errno = ERANGE; +#ifdef __STDC__ + rv = HUGE_VAL; +#else + /* Can't trust HUGE_VAL */ +#ifdef IEEE_Arith + word0(rv) = Exp_mask; + word1(rv) = 0; +#else + word0(rv) = Big0; + word1(rv) = Big1; +#endif +#endif + goto ret; + } + if (e1 >>= 4) { + for (j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + rv *= bigtens[j]; + /* The last multiplication could overflow. */ + word0(rv) -= P*Exp_msk1; + rv *= bigtens[j]; + if ((z = word0(rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-P)) + goto ovfl; + if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { + /* set to largest number */ + /* (Can't trust DBL_MAX) */ + word0(rv) = Big0; + word1(rv) = Big1; + } + else + word0(rv) += P*Exp_msk1; + } + } + } else if (e1 < 0) { + e1 = -e1; + if (i = e1 & 15) + rv /= tens[i]; + if (e1 &= ~15) { + e1 >>= 4; + for (j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + rv *= tinytens[j]; + /* The last multiplication could underflow. */ + rv0 = rv; + rv *= tinytens[j]; + if (!rv) { + rv = 2.*rv0; + rv *= tinytens[j]; + if (!rv) { + undfl: + rv = 0.; + errno = ERANGE; + goto ret; + } + word0(rv) = Tiny0; + word1(rv) = Tiny1; + /* The refinement below will clean + * this approximation up. + */ + } + } + } + + /* Now the hard part -- adjusting rv to the correct value.*/ + + /* Put digits into bd: true value = bd * 10^e */ + + bd0 = s2b(s0, nd0, nd, y); + + for (;;) { + bd = Balloc(bd0->k); + Bcopy(bd, bd0); + bb = d2b(rv, &bbe, &bbbits); /* rv = bb * 2^bbe */ + bs = i2b(1); + + if (e >= 0) { + bb2 = bb5 = 0; + bd2 = bd5 = e; + } else { + bb2 = bb5 = -e; + bd2 = bd5 = 0; + } + if (bbe >= 0) + bb2 += bbe; + else + bd2 -= bbe; + bs2 = bb2; +#ifdef Sudden_Underflow +#ifdef IBM + j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); +#else + j = P + 1 - bbbits; +#endif +#else + i = bbe + bbbits - 1; /* logb(rv) */ + if (i < Emin) /* denormal */ + j = bbe + (P-Emin); + else + j = P + 1 - bbbits; +#endif + bb2 += j; + bd2 += j; + i = bb2 < bd2 ? bb2 : bd2; + if (i > bs2) + i = bs2; + if (i > 0) { + bb2 -= i; + bd2 -= i; + bs2 -= i; + } + if (bb5 > 0) { + bs = pow5mult(bs, bb5); + bb1 = mult(bs, bb); + Bfree(bb); + bb = bb1; + } + if (bb2 > 0) + bb = lshift(bb, bb2); + if (bd5 > 0) + bd = pow5mult(bd, bd5); + if (bd2 > 0) + bd = lshift(bd, bd2); + if (bs2 > 0) + bs = lshift(bs, bs2); + delta = diff(bb, bd); + dsign = delta->sign; + delta->sign = 0; + i = cmp(delta, bs); + if (i < 0) { + /* Error is less than half an ulp -- check for + * special case of mantissa a power of two. + */ + if (dsign || word1(rv) || word0(rv) & Bndry_mask) + break; + delta = lshift(delta,Log2P); + if (cmp(delta, bs) > 0) + goto drop_down; + break; + } + if (i == 0) { + /* exactly half-way between */ + if (dsign) { + if ((word0(rv) & Bndry_mask1) == Bndry_mask1 + && word1(rv) == 0xffffffff) { + /*boundary case -- increment exponent*/ + word0(rv) = (word0(rv) & Exp_mask) + + Exp_msk1 +#ifdef IBM + | Exp_msk1 >> 4 +#endif + ; + word1(rv) = 0; + break; + } + } else if (!(word0(rv) & Bndry_mask) && !word1(rv)) { + drop_down: + /* boundary case -- decrement exponent */ +#ifdef Sudden_Underflow + L = word0(rv) & Exp_mask; +#ifdef IBM + if (L < Exp_msk1) +#else + if (L <= Exp_msk1) +#endif + goto undfl; + L -= Exp_msk1; +#else + L = (word0(rv) & Exp_mask) - Exp_msk1; +#endif + word0(rv) = L | Bndry_mask1; + word1(rv) = 0xffffffff; +#ifdef IBM + goto cont; +#else + break; +#endif + } +#ifndef ROUND_BIASED + if (!(word1(rv) & LSB)) + break; +#endif + if (dsign) + rv += ulp(rv); +#ifndef ROUND_BIASED + else { + rv -= ulp(rv); +#ifndef Sudden_Underflow + if (!rv) + goto undfl; +#endif + } +#endif + break; + } + if ((aadj = ratio(delta, bs)) <= 2.) { + if (dsign) + aadj = aadj1 = 1.; + else if (word1(rv) || word0(rv) & Bndry_mask) { +#ifndef Sudden_Underflow + if (word1(rv) == Tiny1 && !word0(rv)) + goto undfl; +#endif + aadj = 1.; + aadj1 = -1.; + } else { + /* special case -- power of FLT_RADIX to be */ + /* rounded down... */ + + if (aadj < 2./FLT_RADIX) + aadj = 1./FLT_RADIX; + else + aadj *= 0.5; + aadj1 = -aadj; + } + } else { + aadj *= 0.5; + aadj1 = dsign ? aadj : -aadj; +#ifdef Check_FLT_ROUNDS + switch(FLT_ROUNDS) { + case 2: /* towards +infinity */ + aadj1 -= 0.5; + break; + case 0: /* towards 0 */ + case 3: /* towards -infinity */ + aadj1 += 0.5; + } +#else + if (FLT_ROUNDS == 0) + aadj1 += 0.5; +#endif + } + y = word0(rv) & Exp_mask; + + /* Check for overflow */ + + if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { + rv0 = rv; + word0(rv) -= P*Exp_msk1; + adj = aadj1 * ulp(rv); + rv += adj; + if ((word0(rv) & Exp_mask) >= + Exp_msk1*(DBL_MAX_EXP+Bias-P)) { + if (word0(rv0) == Big0 && word1(rv0) == Big1) + goto ovfl; + word0(rv) = Big0; + word1(rv) = Big1; + goto cont; + } else + word0(rv) += P*Exp_msk1; + } else { +#ifdef Sudden_Underflow + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { + rv0 = rv; + word0(rv) += P*Exp_msk1; + adj = aadj1 * ulp(rv); + rv += adj; +#ifdef IBM + if ((word0(rv) & Exp_mask) < P*Exp_msk1) +#else + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) +#endif + { + if (word0(rv0) == Tiny0 + && word1(rv0) == Tiny1) + goto undfl; + word0(rv) = Tiny0; + word1(rv) = Tiny1; + goto cont; + } else + word0(rv) -= P*Exp_msk1; + } else { + adj = aadj1 * ulp(rv); + rv += adj; + } +#else + /* Compute adj so that the IEEE rounding rules will + * correctly round rv + adj in some half-way cases. + * If rv * ulp(rv) is denormalized (i.e., + * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid + * trouble from bits lost to denormalization; + * example: 1.2e-307 . + */ + if (y <= (P-1)*Exp_msk1 && aadj >= 1.) { + aadj1 = (double)(int)(aadj + 0.5); + if (!dsign) + aadj1 = -aadj1; + } + adj = aadj1 * ulp(rv); + rv += adj; +#endif + } + z = word0(rv) & Exp_mask; + if (y == z) { + /* Can we stop now? */ + L = aadj; + aadj -= L; + /* The tolerances below are conservative. */ + if (dsign || word1(rv) || word0(rv) & Bndry_mask) { + if (aadj < .4999999 || aadj > .5000001) + break; + } else if (aadj < .4999999/FLT_RADIX) + break; + } + cont: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(delta); + } + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); + ret: + if (se) + *se = (char *)s; + return sign ? -rv : rv; +} + + static int +quorem +#ifdef KR_headers + (b, S) Bigint *b, *S; +#else + (Bigint *b, Bigint *S) +#endif +{ + int n; + long borrow, y; + unsigned long carry, q, ys; + unsigned long *bx, *bxe, *sx, *sxe; +#ifdef Pack_32 + long z; + unsigned long si, zs; +#endif + + n = S->wds; +#ifdef DEBUG + /*debug*/ if (b->wds > n) + /*debug*/ Bug("oversize b in quorem"); +#endif + if (b->wds < n) + return 0; + sx = S->x; + sxe = sx + --n; + bx = b->x; + bxe = bx + n; + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ +#ifdef DEBUG + /*debug*/ if (q > 9) + /*debug*/ Bug("oversized quotient in quorem"); +#endif + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + z = (*bx >> 16) - (zs & 0xffff) + borrow; + borrow = z >> 16; + Sign_Extend(borrow, z); + Storeinc(bx, z, y); +#else + ys = *sx++ * q + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + *bx++ = y & 0xffff; +#endif + } while (sx <= sxe); + if (!*bxe) { + bx = b->x; + while (--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b->x; + sx = S->x; + do { +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + z = (*bx >> 16) - (zs & 0xffff) + borrow; + borrow = z >> 16; + Sign_Extend(borrow, z); + Storeinc(bx, z, y); +#else + ys = *sx++ + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + *bx++ = y & 0xffff; +#endif + } while (sx <= sxe); + bx = b->x; + bxe = bx + n; + if (!*bxe) { + while (--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + return q; +} + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the long + * calculation. + */ + +char * +__dtoa +#ifdef KR_headers + (d, mode, ndigits, decpt, sign, rve) + double d; int mode, ndigits, *decpt, *sign; char **rve; +#else + (double d, int mode, int ndigits, int *decpt, int *sign, char **rve) +#endif +{ + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4-9 should give the same return values as 2-3, i.e., + 4 <= mode <= 9 ==> same return as mode + 2 + (mode & 1). These modes are mainly for + debugging; often they run slower but sometimes + faster than modes 2-3. + 4,5,8,9 ==> left-to-right digit generation. + 6-9 ==> don't try fast floating-point estimate + (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + + int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, + j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, + spec_case, try_quick; + long L; +#ifndef Sudden_Underflow + int denorm; + unsigned long x; +#endif + Bigint *b, *b1, *delta, *mlo, *mhi, *S; + double d2, ds, eps; + char *s, *s0; + static Bigint *result; + static int result_k; + + if (result) { + result->k = result_k; + result->maxwds = 1 << result_k; + Bfree(result); + result = 0; + } + + if (word0(d) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + word0(d) &= ~Sign_bit; /* clear sign bit */ + } + else + *sign = 0; + +#if defined(IEEE_Arith) + defined(VAX) +#ifdef IEEE_Arith + if ((word0(d) & Exp_mask) == Exp_mask) +#else + if (word0(d) == 0x8000) +#endif + { + /* Infinity or NaN */ + *decpt = 9999; + s = +#ifdef IEEE_Arith + !word1(d) && !(word0(d) & 0xfffff) ? "Infinity" : +#endif + "NaN"; + if (rve) + *rve = +#ifdef IEEE_Arith + s[3] ? s + 8 : +#endif + s + 3; + return s; + } +#endif +#ifdef IBM + d += 0; /* normalize */ +#endif + if (!d) { + *decpt = 1; + s = "0"; + if (rve) + *rve = s + 1; + return s; + } + + b = d2b(d, &be, &bbits); +#ifdef Sudden_Underflow + i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#else + if (i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1))) { +#endif + d2 = d; + word0(d2) &= Frac_mask1; + word0(d2) |= Exp_11; +#ifdef IBM + if (j = 11 - hi0bits(word0(d2) & Frac_mask)) + d2 /= 1 << j; +#endif + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + i -= Bias; +#ifdef IBM + i <<= 2; + i += j; +#endif +#ifndef Sudden_Underflow + denorm = 0; + } else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P-1) - 1); + x = i > 32 ? word0(d) << 64 - i | word1(d) >> i - 32 + : word1(d) << 32 - i; + d2 = x; + word0(d2) -= 31*Exp_msk1; /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; + } +#endif + ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + k = (int)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (d < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } else { + b2 -= k; + b5 = -k; + s5 = 0; + } + if (mode < 0 || mode > 9) + mode = 0; + try_quick = 1; + if (mode > 5) { + mode -= 4; + try_quick = 0; + } + leftright = 1; + switch(mode) { + case 0: + case 1: + ilim = ilim1 = -1; + i = 18; + ndigits = 0; + break; + case 2: + leftright = 0; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = 0; + /* no break */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + j = sizeof(unsigned long); + for (result_k = 0; sizeof(Bigint) - sizeof(unsigned long) + j < i; + j <<= 1) result_k++; + result = Balloc(result_k); + s = s0 = (char *)result; + + if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + d2 = d; + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + d /= bigtens[n_bigtens-1]; + ieps++; + } + for (; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + d /= ds; + } else if (j1 = -k) { + d *= tens[j1 & 0xf]; + for (j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + d *= bigtens[i]; + } + } + if (k_check && d < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + d *= 10.; + ieps++; + } + eps = ieps*d + 7.; + word0(eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + d -= 5.; + if (d > eps) + goto one_digit; + if (d < -eps) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + eps = 0.5/tens[ilim-1] - eps; + for (i = 0;;) { + L = d; + d -= L; + *s++ = '0' + (int)L; + if (d < eps) + goto ret1; + if (1. - d < eps) + goto bump_up; + if (++i >= ilim) + break; + eps *= 10.; + d *= 10.; + } + } else { +#endif + /* Generate ilim digits, then fix them up. */ + eps *= tens[ilim-1]; + for (i = 1;; i++, d *= 10.) { + L = d; + d -= L; + *s++ = '0' + (int)L; + if (i == ilim) { + if (d > 0.5 + eps) + goto bump_up; + else if (d < 0.5 - eps) { + while (*--s == '0'); + s++; + goto ret1; + } + break; + } + } +#ifndef No_leftright + } +#endif + fast_failed: + s = s0; + d = d2; + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || d <= 5*ds) + goto no_digits; + goto one_digit; + } + for (i = 1;; i++) { + L = d / ds; + d -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (d < 0) { + L--; + d += ds; + } +#endif + *s++ = '0' + (int)L; + if (i == ilim) { + d += d; + if (d > ds || d == ds && L & 1) { + bump_up: + while (*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + if (!(d *= 10.)) + break; + } + goto ret1; + } + + m2 = b2; + m5 = b5; + mhi = mlo = 0; + if (leftright) { + if (mode < 2) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif +#ifdef IBM + 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); +#else + 1 + P - bbits; +#endif + } else { + j = ilim - 1; + if (m5 >= j) + m5 -= j; + else { + s5 += j -= m5; + b5 += j; + m5 = 0; + } + if ((i = ilim) < 0) { + m2 -= i; + i = 0; + } + } + b2 += i; + s2 += i; + mhi = i2b(1); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + b1 = mult(mhi, b); + Bfree(b); + b = b1; + } + if (j = b5 - m5) + b = pow5mult(b, j); + } else + b = pow5mult(b, b5); + } + S = i2b(1); + if (s5 > 0) + S = pow5mult(S, s5); + + /* Check for special case that d is a normalized power of 2. */ + + if (mode < 2) { + if (!word1(d) && !(word0(d) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(d) & Exp_mask +#endif + ) { + /* The special case */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } else + spec_case = 0; + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ +#ifdef Pack_32 + if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) + i = 32 - i; +#else + if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf) + i = 16 - i; +#endif + if (i > 4) { + i -= 4; + b2 += i; + m2 += i; + s2 += i; + } else if (i < 4) { + i += 28; + b2 += i; + m2 += i; + s2 += i; + } + if (b2 > 0) + b = lshift(b, b2); + if (s2 > 0) + S = lshift(S, s2); + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0); /* we botched the k estimate */ + if (leftright) + mhi = multadd(mhi, 10, 0); + ilim = ilim1; + } + } + if (ilim <= 0 && mode > 2) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { + /* no digits, fcvt style */ + no_digits: + k = -1 - ndigits; + goto ret; + } + one_digit: + *s++ = '1'; + k++; + goto ret; + } + if (leftright) { + if (m2 > 0) + mhi = lshift(mhi, m2); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k); + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P); + } + + for (i = 1;;i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi); + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); +#ifndef ROUND_BIASED + if (j1 == 0 && !mode && !(word1(d) & 1)) { + if (dig == '9') + goto round_9_up; + if (j > 0) + dig++; + *s++ = dig; + goto ret; + } +#endif + if (j < 0 || j == 0 && !mode +#ifndef ROUND_BIASED + && !(word1(d) & 1) +#endif + ) { + if (j1 > 0) { + b = lshift(b, 1); + j1 = cmp(b, S); + if ((j1 > 0 || j1 == 0 && dig & 1) + && dig++ == '9') + goto round_9_up; + } + *s++ = dig; + goto ret; + } + if (j1 > 0) { + if (dig == '9') { /* possible if i == 1 */ + round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } + *s++ = dig; + if (i == ilim) + break; + b = multadd(b, 10, 0); + if (mlo == mhi) + mlo = mhi = multadd(mhi, 10, 0); + else { + mlo = multadd(mlo, 10, 0); + mhi = multadd(mhi, 10, 0); + } + } + } else + for (i = 1;; i++) { + *s++ = dig = quorem(b,S) + '0'; + if (i >= ilim) + break; + b = multadd(b, 10, 0); + } + + /* Round off last digit */ + + b = lshift(b, 1); + j = cmp(b, S); + if (j > 0 || j == 0 && dig & 1) { + roundoff: + while (*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } else { + while (*--s == '0'); + s++; + } + ret: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } + ret1: + Bfree(b); + if (s == s0) { /* don't return empty string */ + *s++ = '0'; + k = 0; + } + *s = 0; + *decpt = k + 1; + if (rve) + *rve = s; + return s0; + } +#ifdef __cplusplus +} +#endif diff --git a/lib/libc/string/Makefile.inc b/lib/libc/string/Makefile.inc index eb9258c27e..edd26209a1 100644 --- a/lib/libc/string/Makefile.inc +++ b/lib/libc/string/Makefile.inc @@ -12,10 +12,11 @@ SRCS+= bcmp.s bcopy.s bzero.s ffs.s index.s memchr.c memcmp.c memset.c \ strncat.c strncmp.s strncpy.s strpbrk.c strsep.c \ strspn.c strstr.c .elif (${MACHINE} == "i386") -SRCS+= bcmp.c bcopy.c bzero.s ffs.c index.c memchr.c memcmp.c memset.c \ - rindex.c strcat.c strcmp.c strcpy.c strcspn.c strlen.c \ - strncat.c strncmp.c strncpy.c strpbrk.c strsep.c \ +SRCS+= bcmp.s bcopy.s bzero.s ffs.s index.s memchr.s memcmp.s memset.s \ + rindex.s strcat.s strcmp.s strcpy.s strcspn.c strlen.s \ + strncat.c strncmp.s strncpy.c strpbrk.c strsep.c \ strspn.c strstr.c +SRCS+= memmove.s strchr.s strrchr.s .elif (${MACHINE} == "tahoe") SRCS+= bcmp.s bcopy.s bzero.s ffs.s index.c memchr.c memcmp.s memset.c \ rindex.c strcat.s strcmp.s strcpy.s strcspn.c strlen.s \ @@ -31,6 +32,7 @@ SRCS+= memmove.s strchr.s strrchr.s .endif # if no machine specific memmove(3), build one out of bcopy(3). +.if empty(SRCS:Mmemcpy.s) .if empty(SRCS:Mmemmove.s) OBJS+= memmove.o memmove.o: bcopy.c @@ -43,19 +45,6 @@ memmove.po: bcopy.c @${LD} -X -r ${.TARGET} @mv a.out ${.TARGET} .endif - -# if no machine specific memcpy(3), build one out of bcopy(3). -.if empty(SRCS:Mmemcpy.s) -OBJS+= memcpy.o -memcpy.o: bcopy.c - ${CC} -DMEMCOPY ${CFLAGS} -c ${.ALLSRC} -o ${.TARGET} - @${LD} -x -r ${.TARGET} - @mv a.out ${.TARGET} - -memcpy.po: bcopy.c - ${CC} -DMEMCOPY ${CFLAGS} -c -p ${.ALLSRC} -o ${.TARGET} - @${LD} -X -r ${.TARGET} - @mv a.out ${.TARGET} .endif # if no machine specific strchr(3), build one out of index(3). @@ -86,11 +75,15 @@ strrchr.po: rindex.c @mv a.out ${.TARGET} .endif -MAN3+= bcmp.0 bcopy.0 bstring.0 bzero.0 ffs.0 index.0 memccpy.0 memchr.0 \ - memcmp.0 memcpy.0 memmove.0 memset.0 rindex.0 strcasecmp.0 strcat.0 \ - strchr.0 strcmp.0 strcoll.0 strcpy.0 strcspn.0 strftime.0 string.0 \ - strlen.0 strmode.0 strdup.0 strpbrk.0 strrchr.0 strsep.0 strspn.0 \ - strstr.0 strtok.0 strxfrm.0 swab.0 +MAN3+= string/bcmp.3 string/bcopy.3 string/bstring.3 string/bzero.3 \ + string/ffs.3 string/index.3 string/memccpy.3 string/memchr.3 \ + string/memcmp.3 string/memcpy.3 string/memmove.3 string/memset.3 \ + string/rindex.3 string/strcasecmp.3 string/strcat.3 \ + string/strchr.3 string/strcmp.3 string/strcoll.3 string/strcpy.3 \ + string/strcspn.3 string/strftime.3 string/string.3 \ + string/strlen.3 string/strmode.3 string/strdup.3 string/strpbrk.3 \ + string/strrchr.3 string/strsep.3 string/strspn.3 \ + string/strstr.3 string/strtok.3 string/strxfrm.3 string/swab.3 MLINKS+=strcasecmp.3 strncasecmp.3 MLINKS+=strcat.3 strncat.3 diff --git a/lib/libc/string/strftime.3 b/lib/libc/string/strftime.3 index 32f74bc724..f67431231a 100644 --- a/lib/libc/string/strftime.3 +++ b/lib/libc/string/strftime.3 @@ -63,7 +63,7 @@ string consists of zero or more conversion specifications and ordinary characters. All ordinary characters are copied directly into the buffer. A conversion specification consists of a percent sign -.Dq Ql % +.Ql % and one other character. .Pp No more than @@ -81,41 +81,36 @@ Each conversion specification is replaced by the characters as follows which are then copied into the buffer. .Bl -tag -width "xxxx" .It Cm \&%A -is replaced by the full weekday name. +is replaced by the locale's full weekday name. .It Cm %a -is replaced by the abbreviated weekday name, where the abbreviation -is the first three characters. +is replaced by the locale's abbreviated weekday name. .It Cm \&%B -is replaced by the full month name. -.It Cm %b or %h -is replaced by the abbreviated month name, where the abbreviation is -the first three characters. +is replaced by the locale's full month name. +.It Cm \&%b No or Cm \&%h +is replaced by the locale's abbreviated month name. .It Cm \&%C -is equivalent to -.Dq Li %a %b %e %H:%M:%S %Y -(the format produced by -.Xr asctime 3 . -.It Cm %c -is equivalent to -.Dq Li %m/%d/%y . +is replaced by the century (a year divided by 100 and truncated to an integer) +as a decimal number (00-99). +.It Cm \&%c +is replaced by the locale's appropriate date and time representation. .It Cm \&%D -is replaced by the date in the format -.Dq Ql mm/dd/yy . -.It Cm %d +is replaced by the date in the format +.Dq Li %m/%d/%y . +.It Cm \&%d is replaced by the day of the month as a decimal number (01-31). -.It Cm %e -is replaced by the day of month as a decimal number (1-31); single -digits are preceded by a blank. +.It Cm \&%e +is replaced by the day of month as a decimal number (1-31); +single digits are preceded by a blank. .It Cm \&%H is replaced by the hour (24-hour clock) as a decimal number (00-23). .It Cm \&%I is replaced by the hour (12-hour clock) as a decimal number (01-12). -.It Cm %j +.It Cm \&%j is replaced by the day of the year as a decimal number (001-366). -.It Cm %k +.It Cm \&%k is replaced by the hour (24-hour clock) as a decimal number (0-23); single digits are preceded by a blank. -.It Cm %l +.It Cm \&%l is replaced by the hour (12-hour clock) as a decimal number (1-12); single digits are preceded by a blank. .It Cm \&%M @@ -125,42 +120,50 @@ is replaced by the month as a decimal number (01-12). .It Cm %n is replaced by a newline. .It Cm %p -is replaced by either +is replaced by the locale's equivalent of either .Dq Tn AM or -.Dq Tn PM -as appropriate. +.Dq Tn PM . .It Cm \&%R -is equivalent to -.Dq Li %H:%M -.It Cm %r -is equivalent to -.Dq Li %I:%M:%S %p . -.It Cm %t +is replaced by the time in the format +.Dq Li %H:%M . +.It Cm \&%r +is replaced by the locale's representation of 12-hour clock time +using AM/PM notation. +.It Cm \&%T +is replaced by the time in the format +.Dq Li %H:%M:%S . +.It Cm \&%t is replaced by a tab. .It Cm \&%S is replaced by the second as a decimal number (00-60). .It Cm %s is replaced by the number of seconds since the Epoch, UCT (see .Xr mktime 3 ) . -.It Cm \&%T No or Cm \&%X -is equivalent to -.Dq Li %H:%M:%S . .It Cm \&%U is replaced by the week number of the year (Sunday as the first day of the week) as a decimal number (00-53). +.It Cm \&%u +is replaced by the weekday (Monday as the first day of the week) +as a decimal number (1-7). +.It Cm \&%V +is replaced by the week number of the year (Monday as the first day of +the week) as a decimal number (01-53). If the week containing January +1 has four or more days in the new year, then it is week 1; otherwise +it is week 53 of the previous year, and the next week is week 1. .It Cm \&%W is replaced by the week number of the year (Monday as the first day of the week) as a decimal number (00-53). -.It Cm %w +.It Cm \&%w is replaced by the weekday (Sunday as the first day of the week) as a decimal number (0-6). -.It Cm %x -is equivalent to -.Dq Li %m/%d/%y %H:%M:%S . +.It Cm \&%X +is replaced by the locale's appropriate date representation. +.It Cm \&%x +is replaced by the locale's appropriate time representation. .It Cm \&%Y is replaced by the year with century as a decimal number. -.It Cm %y +.It Cm \&%y is replaced by the year without century as a decimal number (00-99). .It Cm \&%Z is replaced by the time zone name. @@ -180,7 +183,21 @@ function conforms to .St -ansiC . The -.Ql %s -conversion specification is an extension. +.Ql \&%C , +.Ql \&%D , +.Ql \&%e , +.Ql \&%h , +.Ql \&%k , +.Ql \&%l , +.Ql \&%n , +.Ql \&%r , +.Ql \&%R , +.Ql \&%s . +.Ql \&%t , +.Ql \&%T , +.Ql \&%u , +and +.Ql \&%V +conversion specifications are extensions. .Sh BUGS There is no conversion specification for the phase of the moon. diff --git a/lib/libc/string/strftime.c b/lib/libc/string/strftime.c index f50cd49486..16879d2568 100644 --- a/lib/libc/string/strftime.c +++ b/lib/libc/string/strftime.c @@ -40,25 +40,34 @@ static char sccsid[] = "@(#)strftime.c 5.11 (Berkeley) 2/24/91"; #include #include -static char *afmt[] = { +static char *abday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", }; -static char *Afmt[] = { +static char *day[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", }; -static char *bfmt[] = { +static char *abmon[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; -static char *Bfmt[] = { +static char *mon[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", }; +static char *ampm[] = { + "AM", "PM", +}; + +static const char *d_t_fmt = "%a %b %e %H:%M:%S %Y"; +static const char *d_fmt = "%m/%d/%y"; +static const char *t_fmt = "%H:%M:%S"; +static const char *t_fmt_ampm = "%I:%M:%S %p"; static size_t gsize; static char *pt; static int _add(), _conv(), _secs(); +static size_t _fmt(); size_t strftime(s, maxsize, format, t) @@ -67,8 +76,6 @@ strftime(s, maxsize, format, t) const char *format; const struct tm *t; { - static size_t _fmt(); - pt = s; if ((gsize = maxsize) < 1) return(0); @@ -79,48 +86,61 @@ strftime(s, maxsize, format, t) return(0); } +#define SUN_WEEK(t) (((t)->tm_yday + 7 - \ + ((t)->tm_wday)) / 7) +#define MON_WEEK(t) (((t)->tm_yday + 7 - \ + ((t)->tm_wday ? (t)->tm_wday - 1 : 6)) / 7) static size_t _fmt(format, t) register char *format; struct tm *t; { for (; *format; ++format) { - if (*format == '%') - switch(*++format) { + if (*format == '%') { + ++format; + if (*format == 'E') { + /* Alternate Era */ + ++format; + } else if (*format == 'O') { + /* Alternate numeric symbols */ + ++format; + } + switch(*format) { case '\0': --format; break; case 'A': if (t->tm_wday < 0 || t->tm_wday > 6) return(0); - if (!_add(Afmt[t->tm_wday])) + if (!_add(day[t->tm_wday])) return(0); continue; case 'a': if (t->tm_wday < 0 || t->tm_wday > 6) return(0); - if (!_add(afmt[t->tm_wday])) + if (!_add(abday[t->tm_wday])) return(0); continue; case 'B': if (t->tm_mon < 0 || t->tm_mon > 11) return(0); - if (!_add(Bfmt[t->tm_mon])) + if (!_add(mon[t->tm_mon])) return(0); continue; case 'b': case 'h': if (t->tm_mon < 0 || t->tm_mon > 11) return(0); - if (!_add(bfmt[t->tm_mon])) + if (!_add(abmon[t->tm_mon])) return(0); continue; case 'C': - if (!_fmt("%a %b %e %H:%M:%S %Y", t)) + if (!_conv((t->tm_year + TM_YEAR_BASE) / 100, + 2, '0')) return(0); continue; case 'c': - if (!_fmt("%m/%d/%y %H:%M:%S", t)) + if (!_fmt(d_t_fmt, t)) return(0); continue; case 'D': @@ -154,7 +174,7 @@ _fmt(format, t) continue; case 'l': if (!_conv(t->tm_hour % 12 ? - t->tm_hour % 12 : 12, 2, ' ')) + t->tm_hour % 12: 12, 2, ' ')) return(0); continue; case 'M': @@ -170,15 +190,14 @@ _fmt(format, t) return(0); continue; case 'p': - if (!_add(t->tm_hour >= 12 ? "PM" : "AM")) + if (!_add(ampm[t->tm_hour >= 12])) return(0); continue; case 'R': if (!_fmt("%H:%M", t)) return(0); - continue; case 'r': - if (!_fmt("%I:%M:%S %p", t)) + if (!_fmt(t_fmt_ampm, t)) return(0); continue; case 'S': @@ -190,7 +209,6 @@ _fmt(format, t) return(0); continue; case 'T': - case 'X': if (!_fmt("%H:%M:%S", t)) return(0); continue; @@ -199,14 +217,36 @@ _fmt(format, t) return(0); continue; case 'U': - if (!_conv((t->tm_yday + 7 - t->tm_wday) / 7, - 2, '0')) + if (!_conv(SUN_WEEK(t), 2, '0')) + return(0); + continue; + case 'u': + if (!_conv(t->tm_wday ? t->tm_wday : 7, 2, '0')) return(0); continue; + case 'V': + { + /* ISO 8601 Week Of Year: + If the week (Monday - Sunday) containing + January 1 has four or more days in the new + year, then it is week 1; otherwise it is + week 53 of the previous year and the next + week is week one. */ + + int week = MON_WEEK(t); + + if (((t->tm_yday + 7 - (t->tm_wday + 1)) % 7) >= 4) { + week++; + } else if (week == 0) { + week = 53; + } + + if (!_conv(week, 2, '0')) + return(0); + continue; + } case 'W': - if (!_conv((t->tm_yday + 7 - - (t->tm_wday ? (t->tm_wday - 1) : 6)) - / 7, 2, '0')) + if (!_conv(MON_WEEK(t), 2, '0')) return(0); continue; case 'w': @@ -214,20 +254,24 @@ _fmt(format, t) return(0); continue; case 'x': - if (!_fmt("%m/%d/%y", t)) + if (!_fmt(d_fmt, t)) + return(0); + continue; + case 'X': + if (!_fmt(t_fmt, t)) return(0); continue; case 'y': - if (!_conv((t->tm_year + TM_YEAR_BASE) - % 100, 2, '0')) + if (!_conv((t->tm_year + TM_YEAR_BASE) % 100, + 2, '0')) return(0); continue; case 'Y': - if (!_conv(t->tm_year + TM_YEAR_BASE, 4, '0')) + if (!_conv((t->tm_year + TM_YEAR_BASE), 4, '0')) return(0); continue; case 'Z': - if (!t->tm_zone || !_add(t->tm_zone)) + if (t->tm_zone && !_add(t->tm_zone)) return(0); continue; case '%': @@ -238,6 +282,7 @@ _fmt(format, t) */ default: break; + } } if (!gsize--) return(0); diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc index 41f3557bdd..e138dce490 100644 --- a/lib/libc/sys/Makefile.inc +++ b/lib/libc/sys/Makefile.inc @@ -12,7 +12,7 @@ SRCS+= Ovfork.s brk.s cerror.s exect.s fork.s pipe.s ptrace.s reboot.s \ ASM= accept.o access.o acct.o adjtime.o async_daemon.o bind.o chdir.o \ chflags.o chmod.o chown.o chroot.o close.o connect.o dup.o dup2.o \ execve.o fchdir.o fchflags.o fchmod.o fchown.o fcntl.o flock.o \ - fstat.o fstatfs.o fsync.o ftruncate.o getdirentries.o \ + fstat.o fstatfs.o fsync.o ftruncate.o getdirentries.o getdomainname.o \ getdtablesize.o getegid.o geteuid.o getfh.o getfsstat.o getgid.o \ getgroups.o gethostid.o gethostname.o getitimer.o getkerninfo.o \ getpagesize.o getpeername.o getpgrp.o getpid.o getppid.o \ @@ -21,66 +21,72 @@ ASM= accept.o access.o acct.o adjtime.o async_daemon.o bind.o chdir.o \ lseek.o lstat.o madvise.o mincore.o mkdir.o mkfifo.o mknod.o \ mmap.o mount.o mprotect.o msync.o munmap.o nfssvc.o open.o \ profil.o quotactl.o read.o readlink.o readv.o recvfrom.o recvmsg.o \ - rename.o revoke.o rmdir.o select.o sendmsg.o sendto.o setegid.o \ - seteuid.o setgid.o setgroups.o sethostid.o sethostname.o \ + rename.o revoke.o rmdir.o select.o sendmsg.o sendto.o setdomainname.o \ + setegid.o seteuid.o setgid.o setgroups.o sethostid.o sethostname.o \ setitimer.o setpgid.o setpriority.o setregid.o setreuid.o \ - setrlimit.o setsid.o setsockopt.o settimeofday.o setuid.o \ + setrlimit.o setsid.o setsockopt.o settimeofday.o setuid.o shmsys.o \ shutdown.o sigaction.o sigstack.o socket.o socketpair.o stat.o \ - statfs.o swapon.o symlink.o sync.o truncate.o umask.o unlink.o \ + statfs.o swapon.o symlink.o sync.o truncate.o umask.o uname.o unlink.o \ unmount.o utimes.o vadvise.o wait4.o write.o writev.o PSEUDO= _exit.o _getlogin.o -OBJS+= ${ASM} ${PSEUDO} -POBJS+= ${ASM:.o=.po} ${PSEUDO:.o=.po} -CLEANFILES+=${ASM} ${PSEUDO} ${POBJS} +PASM= ${ASM:.o=.po} +PPSEUDO= ${PSEUDO:.o=.po} + +OBJS+= ${ASM} ${PSEUDO} +POBJS+= ${PASM} ${PPSEUDO} ${ASM}: ${.CURDIR}/${MACHINE}/SYS.h /usr/include/sys/syscall.h -.if !defined(NOPROFILE) - @echo creating ${.PREFIX}.o ${.PREFIX}.po + @echo creating ${.PREFIX}.o @printf '#include "SYS.h"\nRSYSCALL(${.PREFIX})\n' | \ ${CPP} ${CFLAGS:M-[ID]*} ${AINC} | ${AS} -o ${.PREFIX}.o @${LD} -x -r ${.PREFIX}.o @mv a.out ${.PREFIX}.o + +${PASM}: ${.CURDIR}/${MACHINE}/SYS.h /usr/include/sys/syscall.h + @echo creating ${.PREFIX}.po @printf '#include "SYS.h"\nRSYSCALL(${.PREFIX})\n' | \ ${CPP} -DPROF ${CFLAGS:M-[ID]*} ${AINC} | ${AS} -o ${.PREFIX}.po @${LD} -x -r ${.PREFIX}.po @mv a.out ${.PREFIX}.po -.else - @echo creating ${.PREFIX}.o - @printf '#include "SYS.h"\nRSYSCALL(${.PREFIX})\n' | \ - ${CPP} ${CFLAGS:M-[ID]*} ${AINC} | ${AS} -o ${.PREFIX}.o - @${LD} -x -r ${.PREFIX}.o - @mv a.out ${.PREFIX}.o -.endif ${PSEUDO}: ${.CURDIR}/${MACHINE}/SYS.h /usr/include/sys/syscall.h - @echo creating ${.PREFIX}.o ${.PREFIX}.po + @echo creating ${.PREFIX}.o @printf '#include "SYS.h"\nPSEUDO(${.PREFIX},${.PREFIX:S/_//})\n' | \ ${CPP} ${CFLAGS:M-[ID]*} ${AINC} | ${AS} -o ${.PREFIX}.o @${LD} -x -r ${.PREFIX}.o @mv a.out ${.PREFIX}.o + +${PPSEUDO}: ${.CURDIR}/${MACHINE}/SYS.h /usr/include/sys/syscall.h + @echo creating ${.PREFIX}.po @printf '#include "SYS.h"\nPSEUDO(${.PREFIX},${.PREFIX:S/_//})\n' | \ ${CPP} -DPROF ${CFLAGS:M-[ID]*} ${AINC} | ${AS} -o ${.PREFIX}.po @${LD} -x -r ${.PREFIX}.po @mv a.out ${.PREFIX}.po -MAN2+= accept.0 access.0 acct.0 adjtime.0 async_daemon.0 bind.0 brk.0 \ - chdir.0 chflags.0 chmod.0 chown.0 chroot.0 close.0 connect.0 \ - dup.0 execve.0 _exit.0 fcntl.0 flock.0 fork.0 fsync.0 \ - getdirentries.0 getdtablesize.0 getfh.0 getfsstat.0 getgid.0 \ - getgroups.0 gethostid.0 gethostname.0 getitimer.0 getlogin.0 \ - getpagesize.0 getpeername.0 getpgrp.0 getpid.0 getpriority.0 \ - getrlimit.0 getrusage.0 getsockname.0 getsockopt.0 \ - gettimeofday.0 getuid.0 intro.0 ioctl.0 kill.0 link.0 listen.0 \ - lseek.0 mkdir.0 mkfifo.0 mknod.0 madvise.0 mincore.0 mmap.0 mount.0 \ - mprotect.0 msync.0 munmap.0 nfssvc.0 open.0 pipe.0 \ - quotactl.0 read.0 readlink.0 reboot.0 recv.0 rename.0 \ - rmdir.0 select.0 send.0 setgroups.0 setpgid.0 setregid.0 setreuid.0 \ - shutdown.0 sigaction.0 sigprocmask.0 sigreturn.0 sigstack.0 \ - sigsuspend.0 socket.0 socketpair.0 stat.0 statfs.0 swapon.0 \ - symlink.0 sync.0 syscall.0 truncate.0 umask.0 unlink.0 utimes.0 \ - vfork.0 wait.0 write.0 +MAN2+= sys/accept.2 sys/access.2 sys/acct.2 sys/adjtime.2 sys/async_daemon.2 \ + sys/bind.2 sys/brk.2 sys/chdir.2 sys/chflags.2 sys/chmod.2 sys/chown.2 \ + sys/chroot.2 sys/close.2 sys/connect.2 sys/dup.2 sys/execve.2 \ + sys/_exit.2 sys/fcntl.2 sys/flock.2 sys/fork.2 sys/fsync.2 \ + sys/getdirentries.2 sys/getdomainname.2 sys/getdtablesize.2 \ + sys/getfh.2 sys/getfsstat.2 \ + sys/getgid.2 sys/getgroups.2 sys/gethostid.2 sys/gethostname.2 \ + sys/getitimer.2 sys/getlogin.2 sys/getpagesize.2 sys/getpeername.2 \ + sys/getpgrp.2 sys/getpid.2 sys/getpriority.2 sys/getrlimit.2 \ + sys/getrusage.2 sys/getsockname.2 sys/getsockopt.2 sys/gettimeofday.2 \ + sys/getuid.2 sys/intro.2 sys/ioctl.2 sys/kill.2 sys/link.2 \ + sys/listen.2 sys/lseek.2 sys/mkdir.2 sys/mkfifo.2 sys/mknod.2 \ + sys/madvise.2 sys/mincore.2 sys/mmap.2 sys/mount.2 sys/mprotect.2 \ + sys/msync.2 sys/munmap.2 sys/nfssvc.2 sys/open.2 sys/pipe.2 \ + sys/quotactl.2 sys/read.2 sys/readlink.2 sys/reboot.2 sys/recv.2 \ + sys/rename.2 sys/rmdir.2 sys/select.2 sys/send.2 sys/setgroups.2 \ + sys/setpgid.2 sys/setregid.2 sys/setreuid.2 sys/shutdown.2 \ + sys/sigaction.2 sys/sigprocmask.2 sys/sigreturn.2 sys/sigstack.2 \ + sys/sigsuspend.2 sys/socket.2 sys/socketpair.2 sys/stat.2 \ + sys/statfs.2 sys/swapon.2 sys/symlink.2 sys/sync.2 sys/syscall.2 \ + sys/truncate.2 sys/umask.2 sys/uname.2 sys/unlink.2 sys/utimes.2 \ + sys/vfork.2 sys/wait.2 sys/write.2 MLINKS+=brk.2 sbrk.2 MLINKS+=dup.2 dup2.2 @@ -88,6 +94,7 @@ MLINKS+=chdir.2 fchdir.2 MLINKS+=chflags.2 fchflags.2 MLINKS+=chmod.2 fchmod.2 MLINKS+=chown.2 fchown.2 +MLINKS+=getdomainname.2 setdomainname.2 MLINKS+=getgid.2 getegid.2 MLINKS+=gethostid.2 sethostid.2 MLINKS+=gethostname.2 sethostname.2 diff --git a/lib/libc/sys/execve.2 b/lib/libc/sys/execve.2 index 23f5348708..63cda1443b 100644 --- a/lib/libc/sys/execve.2 +++ b/lib/libc/sys/execve.2 @@ -205,6 +205,12 @@ Search permission is denied for a component of the path prefix. The new process file is not an ordinary file. .It Bq Er EACCES The new process file mode denies execute permission. +.It Bq Er EACCES +The new process file is on a filesystem mounted with execution +disabled +.Pf ( Dv MNT_NOEXEC +in +.Ao Pa sys/mount.h Ac ) . .It Bq Er ENOEXEC The new process file has the appropriate access permission, but has an invalid magic number in its header. @@ -221,7 +227,7 @@ is larger than the system-imposed limit. The limit in the system as released is 20480 bytes .Pf ( Dv NCARGS in -.Ao Pa sys/param.h Ac . +.Ao Pa sys/param.h Ac ) . .It Bq Er EFAULT The new process file is not as long as indicated by the size values in its header. diff --git a/lib/libc/sys/getdomainname.2 b/lib/libc/sys/getdomainname.2 new file mode 100644 index 0000000000..e08ed87763 --- /dev/null +++ b/lib/libc/sys/getdomainname.2 @@ -0,0 +1,97 @@ +.\" Copyright (c) 1983, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)getdomainname.2 6.7 (Berkeley) 3/10/91 +.\" $Id$ +.\" +.Dd March 10, 1991 +.Dt GETDOMAINNAME 2 +.Os BSD 4.2 +.Sh NAME +.Nm getdomainname , +.Nm setdomainname +.Nd get/set name of current host +.Sh SYNOPSIS +.Fd #include +.Ft int +.Fn getdomainname "char *name" "int namelen" +.Ft int +.Fn setdomainname "const char *name" "int namelen" +.Sh DESCRIPTION +.Fn Getdomainname +returns the standard host name for the current processor, as +previously set by +.Fn setdomainname . +The parameter +.Fa namelen +specifies the size of the +.Fa name +array. The returned name is null-terminated unless insufficient +space is provided. +.Pp +.Fn Setdomainname +sets the name of the host machine to be +.Fa name , +which has length +.Fa namelen . +This call is restricted to the super-user and +is normally used only when the system is bootstrapped. +.Sh RETURN VALUES +If the call succeeds a value of 0 is returned. If the call +fails, a value of -1 is returned and an error code is +placed in the global location +.Va errno . +.Sh ERRORS +The following errors may be returned by these calls: +.Bl -tag -width Er +.It Bq Er EFAULT +The +.Fa name +or +.Fa namelen +parameter gave an +invalid address. +.It Bq Er EPERM +The caller tried to set the domainname and was not the super-user. +.El +.Sh SEE ALSO +.Xr gethostid 2 +.Sh BUGS +Host names are limited to +.Dv MAXHOSTNAMELEN +(from +.Ao Pa sys/param.h Ac ) +characters, currently 64. +.Sh HISTORY +The +.Nm +function call appeared in +.Bx 4.2 . diff --git a/lib/libc/sys/getpgrp.2 b/lib/libc/sys/getpgrp.2 index fe04ee2a83..64a15de820 100644 --- a/lib/libc/sys/getpgrp.2 +++ b/lib/libc/sys/getpgrp.2 @@ -38,14 +38,12 @@ .Nm getpgrp .Nd get process group .Sh SYNOPSIS +.Fd #include .Ft pid_t -.Fn getpgrp "int pid" +.Fn getpgrp "void" .Sh DESCRIPTION -The process group of the specified process is returned by +The process group of the current process is returned by .Fn getpgrp . -If -.Fa pid -is zero, the call applies to the current process. .Pp Process groups are used for distribution of signals, and by terminals to arbitrate requests for their input: processes @@ -75,3 +73,16 @@ The .Nm function call appeared in .Bx 4.0 . +.Sh STANDARDS +The +.Fn getpgrp +function conforms to IEEE Std 1003.1-1988 +.Pq Dq Tn POSIX . +.Sh COMPATABILITY +This version of +.Fn getpgrp +differs from past Berkeley versions by not taking a +.Fa "pid_t pid" +argument. +This incompatibility is required by +.St -p1003.1-88 . diff --git a/lib/libc/sys/reboot.2 b/lib/libc/sys/reboot.2 index 5eff0adfd5..6089a502a0 100644 --- a/lib/libc/sys/reboot.2 +++ b/lib/libc/sys/reboot.2 @@ -62,7 +62,7 @@ The default, causing the system to reboot in its usual fashion. Interpreted by the bootstrap program itself, causing it to prompt on the console as to what file should be booted. Normally, the system is booted from the file -.Dq Em xx Ns No (0,0)vmunix , +.Dq Em xx Ns No (0,0)386bsd , where .Em xx is the default disk name, @@ -123,7 +123,7 @@ program in the newly booted system. When no options are given (i.e., .Dv RB_AUTOBOOT is used), the system is -rebooted from file ``vmunix'' in the root file system of unit 0 +rebooted from file ``386bsd'' in the root file system of unit 0 of a disk chosen in a processor specific way. An automatic consistency check of the disks is normally performed (see diff --git a/lib/libc/sys/uname.2 b/lib/libc/sys/uname.2 new file mode 100644 index 0000000000..d135b2d116 --- /dev/null +++ b/lib/libc/sys/uname.2 @@ -0,0 +1,100 @@ +.\" Copyright (c) 1983, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)uname.2 6.6 (Berkeley) 3/10/91 +.\" $Id: uname.2,v 1.2 1993/08/01 07:42:04 mycroft Exp $ +.\" +.Dd March 29, 1993 +.Dt UNAME 2 +.Os Posix +.Sh NAME +.Nm uname +.Nd get host information +.Sh SYNOPSIS +.Fd #include +.Ft int +.Fn uname "struct utsname *name" +.Sh DESCRIPTION +.Fn Uname +returns the information identifying the current UNIX system +in the structure pointed to by +.Fa name. +.Pp +.Fn Uname +uses the structure defined in +.Aq Pa sys/utsname.h +whose members are: +.Bd -literal +struct utsname { + char sysname[SYS_NMLN]; + char nodename[SYS_NMLN]; + char release[SYS_NMLN]; + char version[SYS_NMLN]; + char machine[SYS_NMLN]; +}; +.Ed +.Pp +.Fn Uname +returns a null-terminated character string naming the +current UNIX system in the character array +.Fa sysname . +Similarly, +.Fa nodename +contains the name that the system is known by on a +communication network. +.Fa Release +and +.Fa version +further identify the operating system. +.Fa Machine +contains the standard name that identifies the hardware +that the UNIX system is running on. +.Sn ERRORS +.Bl -tag -width ENAMETOOLONGAA +.It Bq Er EFAULT +.Fn Uname +will fail if +.Fa name +points to an invalid address. +.Sh RETURN VALUE +Upon successful completion a value of 0 is returned. +Otherwise, a value of -1 is returned and +.Va errno +is set to indicate the error. +.Sh SEE ALSO +.Xr hostname 1 , +.Xr machine 1 , +.Xr gethostname 2 +.Sh HISTORY +The +.Nm uname +function call appeared in +System V.2 AT&T UNIX. diff --git a/lib/libcrypt/Makefile b/lib/libcrypt/Makefile new file mode 100644 index 0000000000..77a2ea8368 --- /dev/null +++ b/lib/libcrypt/Makefile @@ -0,0 +1,26 @@ +# @(#)Makefile 5.4 (Berkeley) 5/7/91 + +LIB= crypt +SRCS= auth.c crypt.c encrypt.c misc.c + +# Do DES encryption? +CFLAGS+= -DDES_ENCRYPT +SRCS+= enc_des.c + +# Do Kerberos encryption? +#CFLAGS+= -DKRBDES_ENCRYPT +#SRCS+= krb_des.c + +# Kerberos 4? +#CFLAGS+=-DKRB4 +#SRCS+= kerberos.c + +# Kerberos 5? +#CFLAGS+= -DKRB5 +#SRCS+= kerberos5.c + +CFLAGS+= -I/usr/include/kerberosIV -DAUTHENTICATE -DENCRYPT + +.PATH: ${.CURDIR}/../../libexec/getty +.include + diff --git a/lib/libtelnet/auth-proto.h b/lib/libcrypt/auth-proto.h similarity index 100% rename from lib/libtelnet/auth-proto.h rename to lib/libcrypt/auth-proto.h diff --git a/lib/libtelnet/auth.c b/lib/libcrypt/auth.c similarity index 100% rename from lib/libtelnet/auth.c rename to lib/libcrypt/auth.c diff --git a/lib/libtelnet/auth.h b/lib/libcrypt/auth.h similarity index 100% rename from lib/libtelnet/auth.h rename to lib/libcrypt/auth.h diff --git a/lib/libcrypt/crypt.c b/lib/libcrypt/crypt.c new file mode 100644 index 0000000000..8be940185c --- /dev/null +++ b/lib/libcrypt/crypt.c @@ -0,0 +1,966 @@ +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Tom Truscott. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)crypt.c 5.11 (Berkeley) 6/25/91"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +/* + * UNIX password, and DES, encryption. + * By Tom Truscott, trt@rti.rti.org, + * from algorithms by Robert W. Baldwin and James Gillogly. + * + * References: + * "Mathematical Cryptology for Computer Scientists and Mathematicians," + * by Wayne Patterson, 1987, ISBN 0-8476-7438-X. + * + * "Password Security: A Case History," R. Morris and Ken Thompson, + * Communications of the ACM, vol. 22, pp. 594-597, Nov. 1979. + * + * "DES will be Totally Insecure within Ten Years," M.E. Hellman, + * IEEE Spectrum, vol. 16, pp. 32-39, July 1979. + */ + +/* ===== Configuration ==================== */ + +/* + * define "MUST_ALIGN" if your compiler cannot load/store + * long integers at arbitrary (e.g. odd) memory locations. + * (Either that or never pass unaligned addresses to des_cipher!) + */ +#if !defined(vax) +#define MUST_ALIGN +#endif + +#ifdef CHAR_BITS +#if CHAR_BITS != 8 + #error C_block structure assumes 8 bit characters +#endif +#endif + +/* + * define "LONG_IS_32_BITS" only if sizeof(long)==4. + * This avoids use of bit fields (your compiler may be sloppy with them). + */ +#if !defined(cray) +#define LONG_IS_32_BITS +#endif + +/* + * define "B64" to be the declaration for a 64 bit integer. + * XXX this feature is currently unused, see "endian" comment below. + */ +#if defined(cray) +#define B64 long +#endif +#if defined(convex) +#define B64 long long +#endif + +/* + * define "LARGEDATA" to get faster permutations, by using about 72 kilobytes + * of lookup tables. This speeds up des_setkey() and des_cipher(), but has + * little effect on crypt(). + */ +#if defined(notdef) +#define LARGEDATA +#endif + +/* compile with "-DSTATIC=int" when profiling */ +#ifndef STATIC +#define STATIC static +#endif +STATIC init_des(), init_perm(), permute(); +#ifdef DEBUG +STATIC prtab(); +#endif + +/* ==================================== */ + +/* + * Cipher-block representation (Bob Baldwin): + * + * DES operates on groups of 64 bits, numbered 1..64 (sigh). One + * representation is to store one bit per byte in an array of bytes. Bit N of + * the NBS spec is stored as the LSB of the Nth byte (index N-1) in the array. + * Another representation stores the 64 bits in 8 bytes, with bits 1..8 in the + * first byte, 9..16 in the second, and so on. The DES spec apparently has + * bit 1 in the MSB of the first byte, but that is particularly noxious so we + * bit-reverse each byte so that bit 1 is the LSB of the first byte, bit 8 is + * the MSB of the first byte. Specifically, the 64-bit input data and key are + * converted to LSB format, and the output 64-bit block is converted back into + * MSB format. + * + * DES operates internally on groups of 32 bits which are expanded to 48 bits + * by permutation E and shrunk back to 32 bits by the S boxes. To speed up + * the computation, the expansion is applied only once, the expanded + * representation is maintained during the encryption, and a compression + * permutation is applied only at the end. To speed up the S-box lookups, + * the 48 bits are maintained as eight 6 bit groups, one per byte, which + * directly feed the eight S-boxes. Within each byte, the 6 bits are the + * most significant ones. The low two bits of each byte are zero. (Thus, + * bit 1 of the 48 bit E expansion is stored as the "4"-valued bit of the + * first byte in the eight byte representation, bit 2 of the 48 bit value is + * the "8"-valued bit, and so on.) In fact, a combined "SPE"-box lookup is + * used, in which the output is the 64 bit result of an S-box lookup which + * has been permuted by P and expanded by E, and is ready for use in the next + * iteration. Two 32-bit wide tables, SPE[0] and SPE[1], are used for this + * lookup. Since each byte in the 48 bit path is a multiple of four, indexed + * lookup of SPE[0] and SPE[1] is simple and fast. The key schedule and + * "salt" are also converted to this 8*(6+2) format. The SPE table size is + * 8*64*8 = 4K bytes. + * + * To speed up bit-parallel operations (such as XOR), the 8 byte + * representation is "union"ed with 32 bit values "i0" and "i1", and, on + * machines which support it, a 64 bit value "b64". This data structure, + * "C_block", has two problems. First, alignment restrictions must be + * honored. Second, the byte-order (e.g. little-endian or big-endian) of + * the architecture becomes visible. + * + * The byte-order problem is unfortunate, since on the one hand it is good + * to have a machine-independent C_block representation (bits 1..8 in the + * first byte, etc.), and on the other hand it is good for the LSB of the + * first byte to be the LSB of i0. We cannot have both these things, so we + * currently use the "little-endian" representation and avoid any multi-byte + * operations that depend on byte order. This largely precludes use of the + * 64-bit datatype since the relative order of i0 and i1 are unknown. It + * also inhibits grouping the SPE table to look up 12 bits at a time. (The + * 12 bits can be stored in a 16-bit field with 3 low-order zeroes and 1 + * high-order zero, providing fast indexing into a 64-bit wide SPE.) On the + * other hand, 64-bit datatypes are currently rare, and a 12-bit SPE lookup + * requires a 128 kilobyte table, so perhaps this is not a big loss. + * + * Permutation representation (Jim Gillogly): + * + * A transformation is defined by its effect on each of the 8 bytes of the + * 64-bit input. For each byte we give a 64-bit output that has the bits in + * the input distributed appropriately. The transformation is then the OR + * of the 8 sets of 64-bits. This uses 8*256*8 = 16K bytes of storage for + * each transformation. Unless LARGEDATA is defined, however, a more compact + * table is used which looks up 16 4-bit "chunks" rather than 8 8-bit chunks. + * The smaller table uses 16*16*8 = 2K bytes for each transformation. This + * is slower but tolerable, particularly for password encryption in which + * the SPE transformation is iterated many times. The small tables total 9K + * bytes, the large tables total 72K bytes. + * + * The transformations used are: + * IE3264: MSB->LSB conversion, initial permutation, and expansion. + * This is done by collecting the 32 even-numbered bits and applying + * a 32->64 bit transformation, and then collecting the 32 odd-numbered + * bits and applying the same transformation. Since there are only + * 32 input bits, the IE3264 transformation table is half the size of + * the usual table. + * CF6464: Compression, final permutation, and LSB->MSB conversion. + * This is done by two trivial 48->32 bit compressions to obtain + * a 64-bit block (the bit numbering is given in the "CIFP" table) + * followed by a 64->64 bit "cleanup" transformation. (It would + * be possible to group the bits in the 64-bit block so that 2 + * identical 32->32 bit transformations could be used instead, + * saving a factor of 4 in space and possibly 2 in time, but + * byte-ordering and other complications rear their ugly head. + * Similar opportunities/problems arise in the key schedule + * transforms.) + * PC1ROT: MSB->LSB, PC1 permutation, rotate, and PC2 permutation. + * This admittedly baroque 64->64 bit transformation is used to + * produce the first code (in 8*(6+2) format) of the key schedule. + * PC2ROT[0]: Inverse PC2 permutation, rotate, and PC2 permutation. + * It would be possible to define 15 more transformations, each + * with a different rotation, to generate the entire key schedule. + * To save space, however, we instead permute each code into the + * next by using a transformation that "undoes" the PC2 permutation, + * rotates the code, and then applies PC2. Unfortunately, PC2 + * transforms 56 bits into 48 bits, dropping 8 bits, so PC2 is not + * invertible. We get around that problem by using a modified PC2 + * which retains the 8 otherwise-lost bits in the unused low-order + * bits of each byte. The low-order bits are cleared when the + * codes are stored into the key schedule. + * PC2ROT[1]: Same as PC2ROT[0], but with two rotations. + * This is faster than applying PC2ROT[0] twice, + * + * The Bell Labs "salt" (Bob Baldwin): + * + * The salting is a simple permutation applied to the 48-bit result of E. + * Specifically, if bit i (1 <= i <= 24) of the salt is set then bits i and + * i+24 of the result are swapped. The salt is thus a 24 bit number, with + * 16777216 possible values. (The original salt was 12 bits and could not + * swap bits 13..24 with 36..48.) + * + * It is possible, but ugly, to warp the SPE table to account for the salt + * permutation. Fortunately, the conditional bit swapping requires only + * about four machine instructions and can be done on-the-fly with about an + * 8% performance penalty. + */ + +typedef union { + unsigned char b[8]; + struct { +#if defined(LONG_IS_32_BITS) + /* long is often faster than a 32-bit bit field */ + long i0; + long i1; +#else + long i0: 32; + long i1: 32; +#endif + } b32; +#if defined(B64) + B64 b64; +#endif +} C_block; + +/* + * Convert twenty-four-bit long in host-order + * to six bits (and 2 low-order zeroes) per char little-endian format. + */ +#define TO_SIX_BIT(rslt, src) { \ + C_block cvt; \ + cvt.b[0] = src; src >>= 6; \ + cvt.b[1] = src; src >>= 6; \ + cvt.b[2] = src; src >>= 6; \ + cvt.b[3] = src; \ + rslt = (cvt.b32.i0 & 0x3f3f3f3fL) << 2; \ + } + +/* + * These macros may someday permit efficient use of 64-bit integers. + */ +#define ZERO(d,d0,d1) d0 = 0, d1 = 0 +#define LOAD(d,d0,d1,bl) d0 = (bl).b32.i0, d1 = (bl).b32.i1 +#define LOADREG(d,d0,d1,s,s0,s1) d0 = s0, d1 = s1 +#define OR(d,d0,d1,bl) d0 |= (bl).b32.i0, d1 |= (bl).b32.i1 +#define STORE(s,s0,s1,bl) (bl).b32.i0 = s0, (bl).b32.i1 = s1 +#define DCL_BLOCK(d,d0,d1) long d0, d1 + +#if defined(LARGEDATA) + /* Waste memory like crazy. Also, do permutations in line */ +#define LGCHUNKBITS 3 +#define CHUNKBITS (1<>4]; OR(D,D0,D1,*tp); p += (1< 0); + STORE(D,D0,D1,*out); +} +#endif /* LARGEDATA */ + + +/* ===== (mostly) Standard DES Tables ==================== */ + +static unsigned char IP[] = { /* initial permutation */ + 58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, + 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7, +}; + +/* The final permutation is the inverse of IP - no table is necessary */ + +static unsigned char ExpandTr[] = { /* expansion operation */ + 32, 1, 2, 3, 4, 5, + 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, + 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, + 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, + 28, 29, 30, 31, 32, 1, +}; + +static unsigned char PC1[] = { /* permuted choice table 1 */ + 57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4, +}; + +static unsigned char Rotates[] = { /* PC1 rotation schedule */ + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, +}; + +/* note: each "row" of PC2 is left-padded with bits that make it invertible */ +static unsigned char PC2[] = { /* permuted choice table 2 */ + 9, 18, 14, 17, 11, 24, 1, 5, + 22, 25, 3, 28, 15, 6, 21, 10, + 35, 38, 23, 19, 12, 4, 26, 8, + 43, 54, 16, 7, 27, 20, 13, 2, + + 0, 0, 41, 52, 31, 37, 47, 55, + 0, 0, 30, 40, 51, 45, 33, 48, + 0, 0, 44, 49, 39, 56, 34, 53, + 0, 0, 46, 42, 50, 36, 29, 32, +}; + +static unsigned char S[8][64] = { /* 48->32 bit substitution tables */ + /* S[1] */ + 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13, + /* S[2] */ + 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9, + /* S[3] */ + 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12, + /* S[4] */ + 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14, + /* S[5] */ + 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3, + /* S[6] */ + 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13, + /* S[7] */ + 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12, + /* S[8] */ + 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11, +}; + +static unsigned char P32Tr[] = { /* 32-bit permutation function */ + 16, 7, 20, 21, + 29, 12, 28, 17, + 1, 15, 23, 26, + 5, 18, 31, 10, + 2, 8, 24, 14, + 32, 27, 3, 9, + 19, 13, 30, 6, + 22, 11, 4, 25, +}; + +static unsigned char CIFP[] = { /* compressed/interleaved permutation */ + 1, 2, 3, 4, 17, 18, 19, 20, + 5, 6, 7, 8, 21, 22, 23, 24, + 9, 10, 11, 12, 25, 26, 27, 28, + 13, 14, 15, 16, 29, 30, 31, 32, + + 33, 34, 35, 36, 49, 50, 51, 52, + 37, 38, 39, 40, 53, 54, 55, 56, + 41, 42, 43, 44, 57, 58, 59, 60, + 45, 46, 47, 48, 61, 62, 63, 64, +}; + +static unsigned char itoa64[] = /* 0..63 => ascii-64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + +/* ===== Tables that are initialized at run time ==================== */ + + +static unsigned char a64toi[128]; /* ascii-64 => 0..63 */ + +/* Initial key schedule permutation */ +static C_block PC1ROT[64/CHUNKBITS][1< final permutation table */ +static C_block CF6464[64/CHUNKBITS][1<= 0; ) { + if ((t = (unsigned char)setting[i]) == '\0') + t = '.'; + encp[i] = t; + num_iter = (num_iter<<6) | a64toi[t]; + } + setting += 4; + encp += 4; + salt_size = 4; + break; + default: + num_iter = 25; + salt_size = 2; + } + + salt = 0; + for (i = salt_size; --i >= 0; ) { + if ((t = (unsigned char)setting[i]) == '\0') + t = '.'; + encp[i] = t; + salt = (salt<<6) | a64toi[t]; + } + encp += salt_size; + if (des_cipher((char *)&constdatablock, (char *)&rsltblock, + salt, num_iter)) + return (NULL); + + /* + * Encode the 64 cipher bits as 11 ascii characters. + */ + i = ((long)((rsltblock.b[0]<<8) | rsltblock.b[1])<<8) | rsltblock.b[2]; + encp[3] = itoa64[i&0x3f]; i >>= 6; + encp[2] = itoa64[i&0x3f]; i >>= 6; + encp[1] = itoa64[i&0x3f]; i >>= 6; + encp[0] = itoa64[i]; encp += 4; + i = ((long)((rsltblock.b[3]<<8) | rsltblock.b[4])<<8) | rsltblock.b[5]; + encp[3] = itoa64[i&0x3f]; i >>= 6; + encp[2] = itoa64[i&0x3f]; i >>= 6; + encp[1] = itoa64[i&0x3f]; i >>= 6; + encp[0] = itoa64[i]; encp += 4; + i = ((long)((rsltblock.b[6])<<8) | rsltblock.b[7])<<2; + encp[2] = itoa64[i&0x3f]; i >>= 6; + encp[1] = itoa64[i&0x3f]; i >>= 6; + encp[0] = itoa64[i]; + + encp[3] = 0; + + return (cryptresult); +} + + +/* + * The Key Schedule, filled in by des_setkey() or setkey(). + */ +#define KS_SIZE 16 +static C_block KS[KS_SIZE]; + +/* + * Set up the key schedule from the key. + */ +des_setkey(key) + register const char *key; +{ + register DCL_BLOCK(K, K0, K1); + register C_block *ptabp; + register int i; + static int des_ready = 0; + + if (!des_ready) { + init_des(); + des_ready = 1; + } + + PERM6464(K,K0,K1,(unsigned char *)key,(C_block *)PC1ROT); + key = (char *)&KS[0]; + STORE(K&~0x03030303L, K0&~0x03030303L, K1, *(C_block *)key); + for (i = 1; i < 16; i++) { + key += sizeof(C_block); + STORE(K,K0,K1,*(C_block *)key); + ptabp = (C_block *)PC2ROT[Rotates[i]-1]; + PERM6464(K,K0,K1,(unsigned char *)key,ptabp); + STORE(K&~0x03030303L, K0&~0x03030303L, K1, *(C_block *)key); + } + return (0); +} + +/* + * Encrypt (or decrypt if num_iter < 0) the 8 chars at "in" with abs(num_iter) + * iterations of DES, using the the given 24-bit salt and the pre-computed key + * schedule, and store the resulting 8 chars at "out" (in == out is permitted). + * + * NOTE: the performance of this routine is critically dependent on your + * compiler and machine architecture. + */ +des_cipher(in, out, salt, num_iter) + const char *in; + char *out; + long salt; + int num_iter; +{ + /* variables that we want in registers, most important first */ +#if defined(pdp11) + register int j; +#endif + register long L0, L1, R0, R1, k; + register C_block *kp; + register int ks_inc, loop_count; + C_block B; + + L0 = salt; + TO_SIX_BIT(salt, L0); /* convert to 4*(6+2) format */ + +#if defined(vax) || defined(pdp11) + salt = ~salt; /* "x &~ y" is faster than "x & y". */ +#define SALT (~salt) +#else +#define SALT salt +#endif + +#if defined(MUST_ALIGN) + B.b[0] = in[0]; B.b[1] = in[1]; B.b[2] = in[2]; B.b[3] = in[3]; + B.b[4] = in[4]; B.b[5] = in[5]; B.b[6] = in[6]; B.b[7] = in[7]; + LOAD(L,L0,L1,B); +#else + LOAD(L,L0,L1,*(C_block *)in); +#endif + LOADREG(R,R0,R1,L,L0,L1); + L0 &= 0x55555555L; + L1 &= 0x55555555L; + L0 = (L0 << 1) | L1; /* L0 is the even-numbered input bits */ + R0 &= 0xaaaaaaaaL; + R1 = (R1 >> 1) & 0x55555555L; + L1 = R0 | R1; /* L1 is the odd-numbered input bits */ + STORE(L,L0,L1,B); + PERM3264(L,L0,L1,B.b, (C_block *)IE3264); /* even bits */ + PERM3264(R,R0,R1,B.b+4,(C_block *)IE3264); /* odd bits */ + + if (num_iter >= 0) + { /* encryption */ + kp = &KS[0]; + ks_inc = sizeof(*kp); + } + else + { /* decryption */ + num_iter = -num_iter; + kp = &KS[KS_SIZE-1]; + ks_inc = -sizeof(*kp); + } + + while (--num_iter >= 0) { + loop_count = 8; + do { + +#define SPTAB(t, i) (*(long *)((unsigned char *)t + i*(sizeof(long)/4))) +#if defined(gould) + /* use this if B.b[i] is evaluated just once ... */ +#define DOXOR(x,y,i) x^=SPTAB(SPE[0][i],B.b[i]); y^=SPTAB(SPE[1][i],B.b[i]); +#else +#if defined(pdp11) + /* use this if your "long" int indexing is slow */ +#define DOXOR(x,y,i) j=B.b[i]; x^=SPTAB(SPE[0][i],j); y^=SPTAB(SPE[1][i],j); +#else + /* use this if "k" is allocated to a register ... */ +#define DOXOR(x,y,i) k=B.b[i]; x^=SPTAB(SPE[0][i],k); y^=SPTAB(SPE[1][i],k); +#endif +#endif + +#define CRUNCH(p0, p1, q0, q1) \ + k = (q0 ^ q1) & SALT; \ + B.b32.i0 = k ^ q0 ^ kp->b32.i0; \ + B.b32.i1 = k ^ q1 ^ kp->b32.i1; \ + kp = (C_block *)((char *)kp+ks_inc); \ + \ + DOXOR(p0, p1, 0); \ + DOXOR(p0, p1, 1); \ + DOXOR(p0, p1, 2); \ + DOXOR(p0, p1, 3); \ + DOXOR(p0, p1, 4); \ + DOXOR(p0, p1, 5); \ + DOXOR(p0, p1, 6); \ + DOXOR(p0, p1, 7); + + CRUNCH(L0, L1, R0, R1); + CRUNCH(R0, R1, L0, L1); + } while (--loop_count != 0); + kp = (C_block *)((char *)kp-(ks_inc*KS_SIZE)); + + + /* swap L and R */ + L0 ^= R0; L1 ^= R1; + R0 ^= L0; R1 ^= L1; + L0 ^= R0; L1 ^= R1; + } + + /* store the encrypted (or decrypted) result */ + L0 = ((L0 >> 3) & 0x0f0f0f0fL) | ((L1 << 1) & 0xf0f0f0f0L); + L1 = ((R0 >> 3) & 0x0f0f0f0fL) | ((R1 << 1) & 0xf0f0f0f0L); + STORE(L,L0,L1,B); + PERM6464(L,L0,L1,B.b, (C_block *)CF6464); +#if defined(MUST_ALIGN) + STORE(L,L0,L1,B); + out[0] = B.b[0]; out[1] = B.b[1]; out[2] = B.b[2]; out[3] = B.b[3]; + out[4] = B.b[4]; out[5] = B.b[5]; out[6] = B.b[6]; out[7] = B.b[7]; +#else + STORE(L,L0,L1,*(C_block *)out); +#endif + return (0); +} + + +/* + * Initialize various tables. This need only be done once. It could even be + * done at compile time, if the compiler were capable of that sort of thing. + */ +STATIC +init_des() +{ + register int i, j; + register long k; + register int tableno; + static unsigned char perm[64], tmp32[32]; /* "static" for speed */ + + /* + * table that converts chars "./0-9A-Za-z"to integers 0-63. + */ + for (i = 0; i < 64; i++) + a64toi[itoa64[i]] = i; + + /* + * PC1ROT - bit reverse, then PC1, then Rotate, then PC2. + */ + for (i = 0; i < 64; i++) + perm[i] = 0; + for (i = 0; i < 64; i++) { + if ((k = PC2[i]) == 0) + continue; + k += Rotates[0]-1; + if ((k%28) < Rotates[0]) k -= 28; + k = PC1[k]; + if (k > 0) { + k--; + k = (k|07) - (k&07); + k++; + } + perm[i] = k; + } +#ifdef DEBUG + prtab("pc1tab", perm, 8); +#endif + init_perm(PC1ROT, perm, 8, 8); + + /* + * PC2ROT - PC2 inverse, then Rotate (once or twice), then PC2. + */ + for (j = 0; j < 2; j++) { + unsigned char pc2inv[64]; + for (i = 0; i < 64; i++) + perm[i] = pc2inv[i] = 0; + for (i = 0; i < 64; i++) { + if ((k = PC2[i]) == 0) + continue; + pc2inv[k-1] = i+1; + } + for (i = 0; i < 64; i++) { + if ((k = PC2[i]) == 0) + continue; + k += j; + if ((k%28) <= j) k -= 28; + perm[i] = pc2inv[k]; + } +#ifdef DEBUG + prtab("pc2tab", perm, 8); +#endif + init_perm(PC2ROT[j], perm, 8, 8); + } + + /* + * Bit reverse, then initial permutation, then expansion. + */ + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + k = (j < 2)? 0: IP[ExpandTr[i*6+j-2]-1]; + if (k > 32) + k -= 32; + else if (k > 0) + k--; + if (k > 0) { + k--; + k = (k|07) - (k&07); + k++; + } + perm[i*8+j] = k; + } + } +#ifdef DEBUG + prtab("ietab", perm, 8); +#endif + init_perm(IE3264, perm, 4, 8); + + /* + * Compression, then final permutation, then bit reverse. + */ + for (i = 0; i < 64; i++) { + k = IP[CIFP[i]-1]; + if (k > 0) { + k--; + k = (k|07) - (k&07); + k++; + } + perm[k-1] = i+1; + } +#ifdef DEBUG + prtab("cftab", perm, 8); +#endif + init_perm(CF6464, perm, 8, 8); + + /* + * SPE table + */ + for (i = 0; i < 48; i++) + perm[i] = P32Tr[ExpandTr[i]-1]; + for (tableno = 0; tableno < 8; tableno++) { + for (j = 0; j < 64; j++) { + k = (((j >> 0) &01) << 5)| + (((j >> 1) &01) << 3)| + (((j >> 2) &01) << 2)| + (((j >> 3) &01) << 1)| + (((j >> 4) &01) << 0)| + (((j >> 5) &01) << 4); + k = S[tableno][k]; + k = (((k >> 3)&01) << 0)| + (((k >> 2)&01) << 1)| + (((k >> 1)&01) << 2)| + (((k >> 0)&01) << 3); + for (i = 0; i < 32; i++) + tmp32[i] = 0; + for (i = 0; i < 4; i++) + tmp32[4 * tableno + i] = (k >> i) & 01; + k = 0; + for (i = 24; --i >= 0; ) + k = (k<<1) | tmp32[perm[i]-1]; + TO_SIX_BIT(SPE[0][tableno][j], k); + k = 0; + for (i = 24; --i >= 0; ) + k = (k<<1) | tmp32[perm[i+24]-1]; + TO_SIX_BIT(SPE[1][tableno][j], k); + } + } +} + +/* + * Initialize "perm" to represent transformation "p", which rearranges + * (perhaps with expansion and/or contraction) one packed array of bits + * (of size "chars_in" characters) into another array (of size "chars_out" + * characters). + * + * "perm" must be all-zeroes on entry to this routine. + */ +STATIC +init_perm(perm, p, chars_in, chars_out) + C_block perm[64/CHUNKBITS][1<>LGCHUNKBITS; /* which chunk this bit comes from */ + l = 1<<(l&(CHUNKBITS-1)); /* mask for this bit */ + for (j = 0; j < (1<>3] |= 1<<(k&07); + } + } +} + +/* + * "setkey" routine (for backwards compatibility) + */ +setkey(key) + register const char *key; +{ + register int i, j, k; + C_block keyblock; + + for (i = 0; i < 8; i++) { + k = 0; + for (j = 0; j < 8; j++) { + k <<= 1; + k |= (unsigned char)*key++; + } + keyblock.b[i] = k; + } + return (des_setkey((char *)keyblock.b)); +} + +/* + * "encrypt" routine (for backwards compatibility) + */ +encrypt(block, flag) + register char *block; + int flag; +{ + register int i, j, k; + C_block cblock; + + for (i = 0; i < 8; i++) { + k = 0; + for (j = 0; j < 8; j++) { + k <<= 1; + k |= (unsigned char)*block++; + } + cblock.b[i] = k; + } + if (des_cipher((char *)&cblock, (char *)&cblock, 0L, (flag ? -1: 1))) + return (1); + for (i = 7; i >= 0; i--) { + k = cblock.b[i]; + for (j = 7; j >= 0; j--) { + *--block = k&01; + k >>= 1; + } + } + return (0); +} + +#ifdef DEBUG +STATIC +prtab(s, t, num_rows) + char *s; + unsigned char *t; + int num_rows; +{ + register int i, j; + + (void)printf("%s:\n", s); + for (i = 0; i < num_rows; i++) { + for (j = 0; j < 8; j++) { + (void)printf("%3d", t[i*8+j]); + } + (void)printf("\n"); + } + (void)printf("\n"); +} +#endif diff --git a/lib/libtelnet/enc-proto.h b/lib/libcrypt/enc-proto.h similarity index 100% rename from lib/libtelnet/enc-proto.h rename to lib/libcrypt/enc-proto.h diff --git a/lib/libtelnet/enc_des.c b/lib/libcrypt/enc_des.c similarity index 100% rename from lib/libtelnet/enc_des.c rename to lib/libcrypt/enc_des.c diff --git a/lib/libtelnet/encrypt.c b/lib/libcrypt/encrypt.c similarity index 100% rename from lib/libtelnet/encrypt.c rename to lib/libcrypt/encrypt.c diff --git a/lib/libtelnet/encrypt.h b/lib/libcrypt/encrypt.h similarity index 100% rename from lib/libtelnet/encrypt.h rename to lib/libcrypt/encrypt.h diff --git a/lib/libtelnet/kerberos.c b/lib/libcrypt/kerberos.c similarity index 100% rename from lib/libtelnet/kerberos.c rename to lib/libcrypt/kerberos.c diff --git a/lib/libtelnet/kerberos5.c b/lib/libcrypt/kerberos5.c similarity index 100% rename from lib/libtelnet/kerberos5.c rename to lib/libcrypt/kerberos5.c diff --git a/lib/libtelnet/key-proto.h b/lib/libcrypt/key-proto.h similarity index 100% rename from lib/libtelnet/key-proto.h rename to lib/libcrypt/key-proto.h diff --git a/lib/libtelnet/krb_des.c b/lib/libcrypt/krb_des.c similarity index 100% rename from lib/libtelnet/krb_des.c rename to lib/libcrypt/krb_des.c diff --git a/lib/libcrypt/misc-proto.h b/lib/libcrypt/misc-proto.h new file mode 100644 index 0000000000..50469f9574 --- /dev/null +++ b/lib/libcrypt/misc-proto.h @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)misc-proto.h 5.1 (Berkeley) 2/28/91 + */ + +/* + * Copyright (C) 1990 by the Massachusetts Institute of Technology + * + * Export of this software from the United States of America is assumed + * to require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#ifndef __MISC_PROTO__ +#define __MISC_PROTO__ + +#if !defined(P) +#ifdef __STDC__ +#define P(x) x +#else +#define P(x) () +#endif +#endif + +void auth_encrypt_init P((char *, char *, char *, int)); +void auth_encrypt_connect P((int)); +void printd P((unsigned char *, int)); + +/* + * These functions are imported from the application + */ +int net_write P((unsigned char *, int)); +void net_encrypt P((void)); +int telnet_spin P((void)); +char *telnet_getenv P((char *)); +char *telnet_gets P((char *, char *, int, int)); +#endif diff --git a/lib/libcrypt/misc.c b/lib/libcrypt/misc.c new file mode 100644 index 0000000000..e00b88892a --- /dev/null +++ b/lib/libcrypt/misc.c @@ -0,0 +1,113 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)misc.c 5.1 (Berkeley) 2/28/91"; +#endif /* not lint */ + +/* + * Copyright (c) 1988, 1990 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "misc.h" + +char *RemoteHostName; +char *LocalHostName; +char *UserNameRequested = 0; +int ConnectedCount = 0; + + void +auth_encrypt_init(local, remote, name, server) + char *local; + char *remote; + char *name; + int server; +{ + RemoteHostName = remote; + LocalHostName = local; +#if defined(AUTHENTICATE) + auth_init(name, server); +#endif +#if defined(ENCRYPT) + encrypt_init(name, server); +#endif + if (UserNameRequested) { + free(UserNameRequested); + UserNameRequested = 0; + } +} + + void +auth_encrypt_user(name) + char *name; +{ + extern char *strdup(); + + if (UserNameRequested) + free(UserNameRequested); + UserNameRequested = name ? strdup(name) : 0; +} + + void +auth_encrypt_connect(cnt) + int cnt; +{ +} + + void +printd(data, cnt) + unsigned char *data; + int cnt; +{ + if (cnt > 16) + cnt = 16; + while (cnt-- > 0) { + printf(" %02x", *data); + ++data; + } +} diff --git a/lib/libcrypt/misc.h b/lib/libcrypt/misc.h new file mode 100644 index 0000000000..5a23a83d32 --- /dev/null +++ b/lib/libcrypt/misc.h @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)misc.h 5.1 (Berkeley) 2/28/91 + */ + +extern char *UserNameRequested; +extern char *LocalHostName; +extern char *RemoteHostName; +extern int ConnectedCount; +extern int ReservedPort; + +#include "misc-proto.h" diff --git a/lib/libcurses/Makefile b/lib/libcurses/Makefile index 6bddf4ed36..1a1538da63 100644 --- a/lib/libcurses/Makefile +++ b/lib/libcurses/Makefile @@ -7,7 +7,7 @@ SRCS= addbytes.c addch.c addstr.c box.c clear.c clrtobot.c clrtoeol.c \ insch.c insertln.c longname.c move.c mvprintw.c mvscanw.c mvwin.c \ newwin.c overlay.c overwrite.c printw.c putchar.c refresh.c scanw.c \ scroll.c toucholap.c standout.c touchwin.c tstp.c unctrl.c -MAN3= curses.0 +MAN3= curses.3 beforeinstall: -cd ${.CURDIR}; cmp -s curses.h ${DESTDIR}/usr/include/curses.h || \ diff --git a/lib/libcurses/addbytes.c b/lib/libcurses/addbytes.c index ae2d13c157..3d47b326ac 100644 --- a/lib/libcurses/addbytes.c +++ b/lib/libcurses/addbytes.c @@ -37,13 +37,29 @@ static char sccsid[] = "@(#)addbytes.c 5.4 (Berkeley) 6/1/90"; # include "curses.ext" +waddbytes(win, bytes, count) +reg WINDOW *win; +reg char *bytes; +int count; +{ + chtype c; + reg int i; + + for (i = 0; i < count; i++) { + c = (unsigned char) *bytes++; + if (_waddbytes(win, &c, 1) == ERR) + return ERR; + } + return OK; +} + /* * This routine adds the character to the current position * */ -waddbytes(win, bytes, count) +_waddbytes(win, bytes, count) reg WINDOW *win; -reg char *bytes; +reg chtype *bytes; reg int count; { #define SYNCH_OUT() {win->_cury = y; win->_curx = x;} @@ -52,21 +68,18 @@ reg int count; reg int newx; SYNCH_IN(); -# ifdef FULLDEBUG - fprintf(outf, "ADDBYTES('%c') at (%d, %d)\n", c, y, x); -# endif while (count--) { - register int c; - static char blanks[] = " "; + register chtype c; + static chtype blanks[] = {' ',' ',' ',' ',' ',' ',' ',' '}; c = *bytes++; switch (c) { case '\t': - SYNCH_IN(); - if (waddbytes(win, blanks, 8-(x%8)) == ERR) { + SYNCH_OUT(); + if (_waddbytes(win, blanks, 8-(x%8)) == ERR) { return ERR; } - SYNCH_OUT(); + SYNCH_IN(); break; default: @@ -102,10 +115,10 @@ reg int count; newline: if (++y >= win->_maxy) if (win->_scroll) { + --y; SYNCH_OUT(); scroll(win); SYNCH_IN(); - --y; } else return ERR; diff --git a/lib/libcurses/addch.c b/lib/libcurses/addch.c index 897660a984..027c4bf944 100644 --- a/lib/libcurses/addch.c +++ b/lib/libcurses/addch.c @@ -43,7 +43,8 @@ static char sccsid[] = "@(#)addch.c 5.5 (Berkeley) 6/1/90"; */ waddch(win, c) WINDOW *win; -char c; +char c; { - return waddbytes(win, &c, 1); + chtype ch = (unsigned char) c; + return _waddbytes(win, &ch, 1); } diff --git a/lib/libcurses/addstr.c b/lib/libcurses/addstr.c index 0d442672d4..025fd43d4a 100644 --- a/lib/libcurses/addstr.c +++ b/lib/libcurses/addstr.c @@ -43,10 +43,17 @@ static char sccsid[] = "@(#)addstr.c 5.5 (Berkeley) 6/1/90"; */ waddstr(win,str) reg WINDOW *win; -reg char *str; +char *str; { + chtype c; + reg char *s; # ifdef DEBUG fprintf(outf, "WADDSTR(\"%s\")\n", str); # endif - return waddbytes(win, str, strlen(str)); + for (s = str; *s;) { + c = (unsigned char) *s++; + if (_waddbytes(win, &c, 1) == ERR) + return ERR; + } + return OK; } diff --git a/lib/libcurses/box.c b/lib/libcurses/box.c index 7c6532ae4c..983ed66621 100644 --- a/lib/libcurses/box.c +++ b/lib/libcurses/box.c @@ -44,21 +44,21 @@ static char sccsid[] = "@(#)box.c 5.4 (Berkeley) 6/1/90"; */ box(win, vert, hor) reg WINDOW *win; -char vert, hor; { +char vert, hor; { reg int i; reg int endy, endx; - reg char *fp, *lp; + reg chtype *fp, *lp; endx = win->_maxx; endy = win->_maxy - 1; fp = win->_y[0]; lp = win->_y[endy]; for (i = 0; i < endx; i++) - fp[i] = lp[i] = hor; + fp[i] = lp[i] = (unsigned char) hor; endx--; for (i = 0; i <= endy; i++) - win->_y[i][0] = (win->_y[i][endx] = vert); + win->_y[i][0] = (win->_y[i][endx] = (unsigned char) vert); if (!win->_scroll && (win->_flags&_SCROLLWIN)) fp[0] = fp[endx] = lp[0] = lp[endx] = ' '; touchwin(win); diff --git a/lib/libcurses/clrtobot.c b/lib/libcurses/clrtobot.c index e2635ba356..8632438995 100644 --- a/lib/libcurses/clrtobot.c +++ b/lib/libcurses/clrtobot.c @@ -45,7 +45,7 @@ wclrtobot(win) reg WINDOW *win; { reg int y; - reg char *sp, *end, *maxx; + reg chtype *sp, *end, *maxx; reg int startx, minx; startx = win->_curx; diff --git a/lib/libcurses/clrtoeol.c b/lib/libcurses/clrtoeol.c index 472652e4cd..6af2bfcede 100644 --- a/lib/libcurses/clrtoeol.c +++ b/lib/libcurses/clrtoeol.c @@ -44,9 +44,9 @@ static char sccsid[] = "@(#)clrtoeol.c 5.4 (Berkeley) 6/1/90"; wclrtoeol(win) reg WINDOW *win; { - reg char *sp, *end; + reg chtype *sp, *end; reg int y, x; - reg char *maxx; + reg chtype *maxx; reg int minx; y = win->_cury; diff --git a/lib/libcurses/cr_put.c b/lib/libcurses/cr_put.c index 26c1f89384..c9d0e79134 100644 --- a/lib/libcurses/cr_put.c +++ b/lib/libcurses/cr_put.c @@ -180,6 +180,7 @@ plod(cnt) { register int i, j, k; register int soutcol, soutline; + chtype ch; plodcnt = plodflg = cnt; soutcol = outcol; @@ -373,9 +374,9 @@ dontcr: if (plodflg) /* avoid a complex calculation */ plodcnt--; else { - i = curscr->_y[outline][outcol]; - if ((i&_STANDOUT) == (curscr->_flags&_STANDOUT)) - _putchar(i & 0177); + ch = curscr->_y[outline][outcol]; + if ((ch&_STANDOUT) == (curscr->_flags&_STANDOUT)) + _putchar(ch); else goto nondes; } diff --git a/lib/libcurses/curses.h b/lib/libcurses/curses.h index 3c71191029..e845bb2287 100644 --- a/lib/libcurses/curses.h +++ b/lib/libcurses/curses.h @@ -44,6 +44,8 @@ #define bool char #define reg register +typedef unsigned short chtype; + #define TRUE (1) #define FALSE (0) #define ERR (0) @@ -55,7 +57,7 @@ #define _FLUSH 010 #define _FULLLINE 020 #define _IDLINE 040 -#define _STANDOUT 0200 +#define _STANDOUT 0400 #define _NOCHANGE -1 #define _puts(s) tputs(s, 0, _putchar) @@ -92,7 +94,7 @@ struct _win_st { bool _clear; bool _leave; bool _scroll; - char **_y; + chtype **_y; short *_firstch; short *_lastch; struct _win_st *_nextp, *_orig; @@ -127,7 +129,7 @@ int __void__; #define addch(ch) VOID(waddch(stdscr, ch)) #define getch() VOID(wgetch(stdscr)) #define addbytes(da,co) VOID(waddbytes(stdscr, da,co)) -#define addstr(str) VOID(waddbytes(stdscr, str, strlen(str))) +#define addstr(str) VOID(waddstr(stdscr, str)) #define getstr(str) VOID(wgetstr(stdscr, str)) #define move(y, x) VOID(wmove(stdscr, y, x)) #define clear() VOID(wclear(stdscr)) @@ -151,7 +153,7 @@ int __void__; #define mvwaddbytes(win,y,x,da,co) \ VOID(wmove(win,y,x)==ERR?ERR:waddbytes(win,da,co)) #define mvwaddstr(win,y,x,str) \ - VOID(wmove(win,y,x)==ERR?ERR:waddbytes(win,str,strlen(str))) + VOID(wmove(win,y,x)==ERR?ERR:waddstr(win,str)) #define mvwgetstr(win,y,x,str) VOID(wmove(win,y,x)==ERR?ERR:wgetstr(win,str)) #define mvwinch(win,y,x) VOID(wmove(win,y,x) == ERR ? ERR : winch(win)) #define mvwdelch(win,y,x) VOID(wmove(win,y,x) == ERR ? ERR : wdelch(win)) @@ -174,7 +176,7 @@ int __void__; #define scrollok(win,bf) (win->_scroll = bf) #define flushok(win,bf) (bf ? (win->_flags |= _FLUSH):(win->_flags &= ~_FLUSH)) #define getyx(win,y,x) y = win->_cury, x = win->_curx -#define winch(win) (win->_y[win->_cury][win->_curx] & 0177) +#define winch(win) (win->_y[win->_cury][win->_curx] & 0xFF) #define raw() (_tty.sg_flags|=RAW, _pfast=_rawmode=TRUE, \ ioctl(_tty_ch, TIOCSETP, &_tty)) diff --git a/lib/libcurses/delch.c b/lib/libcurses/delch.c index 2347549db3..23499eb554 100644 --- a/lib/libcurses/delch.c +++ b/lib/libcurses/delch.c @@ -45,9 +45,8 @@ static char sccsid[] = "@(#)delch.c 5.4 (Berkeley) 6/1/90"; wdelch(win) reg WINDOW *win; { - reg char *temp1, *temp2; - reg char *end; - reg int lch; + reg chtype *temp1, *temp2; + reg chtype *end; end = &win->_y[win->_cury][win->_maxx - 1]; temp1 = &win->_y[win->_cury][win->_curx]; diff --git a/lib/libcurses/deleteln.c b/lib/libcurses/deleteln.c index d5cb57dd0d..ba5d481974 100644 --- a/lib/libcurses/deleteln.c +++ b/lib/libcurses/deleteln.c @@ -45,9 +45,9 @@ static char sccsid[] = "@(#)deleteln.c 5.4 (Berkeley) 6/1/90"; wdeleteln(win) reg WINDOW *win; { - reg char *temp; + reg chtype *temp; reg int y; - reg char *end; + reg chtype *end; reg int x; # ifdef DEBUG @@ -58,7 +58,7 @@ reg WINDOW *win; if (win->_orig == NULL) win->_y[y] = win->_y[y + 1]; else - bcopy(win->_y[y + 1], win->_y[y], win->_maxx); + bcopy(win->_y[y + 1], win->_y[y], win->_maxx * sizeof(chtype)); touchline(win, y, 0, win->_maxx - 1); } if (win->_orig == NULL) @@ -67,7 +67,7 @@ reg WINDOW *win; temp = win->_y[y]; for (end = &temp[win->_maxx]; temp < end; ) *temp++ = ' '; - touchline(win, win->_cury, 0, win->_maxx - 1); + touchline(win, y, 0, win->_maxx - 1); if (win->_orig == NULL) _id_subwins(win); return OK; diff --git a/lib/libcurses/erase.c b/lib/libcurses/erase.c index c700ef272b..9cbd79e7fd 100644 --- a/lib/libcurses/erase.c +++ b/lib/libcurses/erase.c @@ -45,7 +45,7 @@ werase(win) reg WINDOW *win; { reg int y; - reg char *sp, *end, *start, *maxx; + reg chtype *sp, *end, *start, *maxx; reg int minx; # ifdef DEBUG diff --git a/lib/libcurses/getch.c b/lib/libcurses/getch.c index 95b3875243..d3e04d08f6 100644 --- a/lib/libcurses/getch.c +++ b/lib/libcurses/getch.c @@ -45,7 +45,7 @@ wgetch(win) reg WINDOW *win; { reg bool weset = FALSE; - reg char inp; + reg int inp; if (!win->_scroll && (win->_flags&_FULLWIN) && win->_curx == win->_maxx - 1 && win->_cury == win->_maxy - 1) @@ -58,13 +58,15 @@ reg WINDOW *win; { weset++; } inp = getchar(); + if (inp != EOF) { # ifdef DEBUG fprintf(outf,"WGETCH got '%s'\n",unctrl(inp)); # endif if (_echoit) { mvwaddch(curscr, win->_cury + win->_begy, - win->_curx + win->_begx, inp); - waddch(win, inp); + win->_curx + win->_begx, (unsigned char) inp); + waddch(win, (unsigned char) inp); + } } if (weset) nocbreak(); diff --git a/lib/libcurses/getstr.c b/lib/libcurses/getstr.c index ff09e8dd79..ec3af26fe7 100644 --- a/lib/libcurses/getstr.c +++ b/lib/libcurses/getstr.c @@ -44,13 +44,12 @@ static char sccsid[] = "@(#)getstr.c 5.4 (Berkeley) 6/1/90"; wgetstr(win,str) reg WINDOW *win; reg char *str; { + int c; - while ((*str = wgetch(win)) != ERR && *str != '\n') - str++; - if (*str == ERR) { + while ((c = wgetch(win)) != ERR && c != EOF && c != '\n') + *str++ = c; *str = '\0'; + if (c == ERR) return ERR; - } - *str = '\0'; return OK; } diff --git a/lib/libcurses/insch.c b/lib/libcurses/insch.c index d4e160e33c..5ec1c9a7d8 100644 --- a/lib/libcurses/insch.c +++ b/lib/libcurses/insch.c @@ -44,17 +44,17 @@ static char sccsid[] = "@(#)insch.c 5.4 (Berkeley) 6/1/90"; */ winsch(win, c) reg WINDOW *win; -char c; { +char c; { - reg char *temp1, *temp2; - reg char *end; + reg chtype *temp1, *temp2; + reg chtype *end; end = &win->_y[win->_cury][win->_curx]; temp1 = &win->_y[win->_cury][win->_maxx - 1]; temp2 = temp1 - 1; while (temp1 > end) *temp1-- = *temp2--; - *temp1 = c; + *temp1 = (unsigned char) c; touchline(win, win->_cury, win->_curx, win->_maxx - 1); if (win->_cury == LINES - 1 && win->_y[LINES-1][COLS-1] != ' ') if (win->_scroll) { diff --git a/lib/libcurses/insertln.c b/lib/libcurses/insertln.c index 7c9d528e74..09b8b43530 100644 --- a/lib/libcurses/insertln.c +++ b/lib/libcurses/insertln.c @@ -45,9 +45,9 @@ static char sccsid[] = "@(#)insertln.c 5.4 (Berkeley) 6/1/90"; winsertln(win) reg WINDOW *win; { - reg char *temp; + reg chtype *temp; reg int y; - reg char *end; + reg chtype *end; reg int x; #ifdef DEBUG @@ -59,7 +59,7 @@ reg WINDOW *win; { if (win->_orig == NULL) win->_y[y] = win->_y[y - 1]; else - bcopy(win->_y[y - 1], win->_y[y], win->_maxx); + bcopy(win->_y[y - 1], win->_y[y], win->_maxx * sizeof(chtype)); touchline(win, y, 0, win->_maxx - 1); } if (win->_orig == NULL) diff --git a/lib/libcurses/newwin.c b/lib/libcurses/newwin.c index fc1ae4a247..dfa7e75e63 100644 --- a/lib/libcurses/newwin.c +++ b/lib/libcurses/newwin.c @@ -55,7 +55,7 @@ newwin(num_lines, num_cols, begy, begx) int num_lines, num_cols, begy, begx; { reg WINDOW *win; - reg char *sp; + reg chtype *sp; reg int i, by, bx, nl, nc; reg int j; @@ -87,7 +87,7 @@ int num_lines, num_cols, begy, begx; win->_lastch[i] = _NOCHANGE; } for (i = 0; i < nl; i++) - if ((win->_y[i] = malloc(nc * sizeof win->_y[0])) == NULL) { + if ((win->_y[i] = (chtype *) malloc(nc * sizeof(chtype))) == NULL) { for (j = 0; j < i; j++) free(win->_y[j]); free(win->_firstch); @@ -188,7 +188,7 @@ int num_lines, num_cols, begy, begx; { # ifdef DEBUG fprintf(outf, "MAKENEW: nl = %d\n", nl); # endif - if ((win->_y = (char **) malloc(nl * sizeof win->_y[0])) == NULL) { + if ((win->_y = (chtype **) malloc(nl * sizeof(chtype *))) == NULL) { free(win); return NULL; } diff --git a/lib/libcurses/overlay.c b/lib/libcurses/overlay.c index 0340d9ea89..d272c7ba6d 100644 --- a/lib/libcurses/overlay.c +++ b/lib/libcurses/overlay.c @@ -36,7 +36,6 @@ static char sccsid[] = "@(#)overlay.c 5.6 (Berkeley) 6/1/90"; #endif /* not lint */ # include "curses.ext" -# include # define min(a,b) (a < b ? a : b) # define max(a,b) (a > b ? a : b) @@ -48,7 +47,7 @@ static char sccsid[] = "@(#)overlay.c 5.6 (Berkeley) 6/1/90"; overlay(win1, win2) reg WINDOW *win1, *win2; { - reg char *sp, *end; + reg chtype *sp, *end; reg int x, y, endy, endx, starty, startx; reg int y1,y2; @@ -70,7 +69,7 @@ reg WINDOW *win1, *win2; { end = &win1->_y[y1][endx - win1->_begx]; x = startx - win2->_begx; for (sp = &win1->_y[y1][startx - win1->_begx]; sp < end; sp++) { - if (!isspace(*sp)) + if (*sp != ' ') mvwaddch(win2, y2, x, *sp); x++; } diff --git a/lib/libcurses/overwrite.c b/lib/libcurses/overwrite.c index c885ab3465..655f923ab2 100644 --- a/lib/libcurses/overwrite.c +++ b/lib/libcurses/overwrite.c @@ -48,7 +48,6 @@ static char sccsid[] = "@(#)overwrite.c 5.4 (Berkeley) 6/1/90"; overwrite(win1, win2) reg WINDOW *win1, *win2; { - reg char *sp, *end; reg int x, y, endy, endx, starty, startx; # ifdef DEBUG @@ -66,7 +65,7 @@ reg WINDOW *win1, *win2; { x = endx - startx; for (y = starty; y < endy; y++) { bcopy(&win1->_y[y - win1->_begy][startx - win1->_begx], - &win2->_y[y - win2->_begy][startx - win2->_begx], x); + &win2->_y[y - win2->_begy][startx - win2->_begx], x * sizeof(chtype)); touchline(win2, y, startx - win2->_begx, endx - win2->_begx); } } diff --git a/lib/libcurses/printw.c b/lib/libcurses/printw.c index 069b1c64db..4568c5f4b8 100644 --- a/lib/libcurses/printw.c +++ b/lib/libcurses/printw.c @@ -111,7 +111,7 @@ _winwrite(cookie, buf, n) register int c = n; while (--c >= 0) { - if (waddch(win, *buf++) == ERR) + if (waddch(win, (unsigned char) *buf++) == ERR) return (-1); } return n; diff --git a/lib/libcurses/refresh.c b/lib/libcurses/refresh.c index a4937c56fb..62673d2f87 100644 --- a/lib/libcurses/refresh.c +++ b/lib/libcurses/refresh.c @@ -169,9 +169,11 @@ makech(win, wy) reg WINDOW *win; short wy; { - reg char *nsp, *csp, *ce; + reg chtype *nsp, *csp, *ce; reg short wx, lch, y; reg int nlsp, clsp; /* last space in lines */ + char *ce_tcap; + static chtype blank[] = {' ','\0'}; wx = win->_firstch[wy] - win->_ch_off; if (wx >= win->_maxx) @@ -186,7 +188,7 @@ short wy; y = wy + win->_begy; if (curwin) - csp = " "; + csp = blank; else csp = &curscr->_y[wy + win->_begy][wx + win->_begx]; @@ -199,9 +201,9 @@ short wy; } if (!curwin) - ce = CE; + ce_tcap = CE; else - ce = NULL; + ce_tcap = NULL; while (wx <= lch) { if (*nsp != *csp) { @@ -212,7 +214,7 @@ short wy; ly = y; lx = wx + win->_begx; while (*nsp != *csp && wx <= lch) { - if (ce != NULL && wx >= nlsp && *nsp == ' ') { + if (ce_tcap != NULL && wx >= nlsp && *nsp == ' ') { /* * check for clear to end-of-line */ @@ -235,7 +237,7 @@ short wy; *csp++ = ' '; return OK; } - ce = NULL; + ce_tcap = NULL; } /* * enter/exit standout mode as appropriate @@ -260,9 +262,9 @@ short wy; curscr->_flags &= ~_STANDOUT; } if (!curwin) - _putchar((*csp = *nsp) & 0177); + _putchar((*csp = *nsp)); else - _putchar(*nsp & 0177); + _putchar(*nsp); if (win->_flags&_FULLWIN && !curwin) scroll(curscr); ly = win->_begy+win->_cury; @@ -274,12 +276,12 @@ short wy; return ERR; } if (!curwin) - _putchar((*csp++ = *nsp) & 0177); + _putchar((*csp++ = *nsp)); else - _putchar(*nsp & 0177); + _putchar(*nsp); # ifdef FULLDEBUG fprintf(outf, - "MAKECH:putchar(%c)\n", *nsp & 0177); + "MAKECH:putchar(%c)\n", *nsp); # endif if (UC && (*nsp & _STANDOUT)) { _putchar('\b'); diff --git a/lib/libm/Makefile b/lib/libm/Makefile index c307282f9b..1140ae1663 100644 --- a/lib/libm/Makefile +++ b/lib/libm/Makefile @@ -1,35 +1,138 @@ -# @(#)Makefile 5.7.1.1 (Berkeley) 5/7/91 +# @(#)Makefile 5.11 (Berkeley) 12/2/92 # # ieee - for most IEEE machines, we hope. # mc68881 - the, ahem, mc68881. # national - for those ieee machines whose floating point implementation # has similar byte ordering as the NATIONAL 32016 with 32081. # i386 - i387 NPX, currently the same as "national" +# mips - for MIPS achitecture machines # tahoe - for the tahoe double format. # vax - for the vax D_floating format -# Missing: erf.c, j0.c, j1.c, jn.c, lgamma.c - LIB= m CFLAGS+=-I${.CURDIR}/common_source +.if (${MACHINE} == "ieee") + +HARDWARE=${MACHINE} +.PATH: ${.CURDIR}/common_source ${.CURDIR}/common ${.CURDIR}/ieee +# common_source +SRCS+= acosh.c asincos.c asinh.c atan.c atanh.c cosh.c erf.c exp.c \ + exp__E.c expm1.c floor.c fmod.c gamma.c lgamma.c j0.c j1.c \ + jn.c log.c log10.c log1p.c log__L.c pow.c sinh.c tanh.c +# common +SRCS+= atan2.c sincos.c tan.c +# ieee +SRCS+= cabs.c cbrt.c support.c + +.elif (${MACHINE} == "hp300" || ${MACHINE} == "luna68k") + +HARDWARE=mc68881 +.PATH: ${.CURDIR}/mc68881 ${.CURDIR}/common_source ${.CURDIR}/ieee +# common_source +SRCS+= acosh.c asinh.c erf.c exp__E.c fmod.c gamma.c lgamma.c j0.c j1.c log__L.c \ + pow.c +# mc68881 +SRCS+= asincos.s atan.s atan2.c atanh.s cosh.s exp.s expm1.s floor.s \ + log.s log10.s log1p.s sincos.s sinh.s sqrt.s support.s tan.s tanh.s +# ieee +SRCS+= cabs.c cbrt.c + +.elif (${MACHINE} == "i386") + HARDWARE=i387 -.PATH: ${.CURDIR}/common_source ${.CURDIR}/common \ - ${.CURDIR}/ieee +.PATH: ${.CURDIR}/common_source ${.CURDIR}/common ${.CURDIR}/ieee CFLAGS+= -Dnational # common_source -SRCS+= acosh.c asincos.c asinh.c atan.c atanh.c cosh.c exp.c \ - exp__E.c expm1.c floor.c fmod.c log.c \ +SRCS+= acosh.c asincos.c asinh.c atan.c atanh.c cosh.c erf.c exp.c \ + exp__E.c expm1.c floor.c fmod.c gamma.c lgamma.c j0.c j1.c jn.c log.c \ + log10.c log1p.c log__L.c pow.c sinh.c tanh.c +# common +SRCS+= atan2.c sincos.c tan.c +# ieee +SRCS+= cabs.c cbrt.c support.c + +.elif (${MACHINE} == "mips") + +HARDWARE=${MACHINE} +.PATH: ${.CURDIR}/common_source ${.CURDIR}/common ${.CURDIR}/ieee +CFLAGS+= -Dnational +# common_source +SRCS+= acosh.c asincos.c asinh.c atan.c atanh.c cosh.c erf.c exp.c \ + exp__E.c expm1.c floor.c fmod.c gamma.c lgamma.c j0.c j1.c jn.c log.c \ + log10.c log1p.c log__L.c pow.c sinh.c tanh.c +# common +SRCS+= atan2.c sincos.c tan.c +# ieee +SRCS+= cabs.c cbrt.c support.c + +.elif (${MACHINE} == "national") + +HARDWARE=${MACHINE} +.PATH: ${.CURDIR}/common_source ${.CURDIR}/common ${.CURDIR}/national \ +.elif (${MACHINE} == "national") + +HARDWARE=${MACHINE} +.PATH: ${.CURDIR}/common_source ${.CURDIR}/common ${.CURDIR}/national \ + ${.CURDIR}/ieee +# common_source +SRCS+= acosh.c asincos.c asinh.c atan.c atanh.c cosh.c erf.c exp.c \ + exp__E.c expm1.c floor.c fmod.c gamma.c lgamma.c j0.c j1.c jn.c log.c \ + log10.c log1p.c log__L.c pow.c sinh.c tanh.c +# common +SRCS+= atan2.c sincos.c tan.c +# national +SRCS+= sqrt.s support.s +# ieee +SRCS+= cabs.c cbrt.c + +.elif (${MACHINE} == "sparc") + +HARDWARE=${MACHINE} +.PATH: ${.CURDIR}/common_source ${.CURDIR}/common ${.CURDIR}/ieee +# common_source +SRCS+= acosh.c asincos.c asinh.c atan.c atanh.c cosh.c erf.c exp.c \ + exp__E.c expm1.c floor.c fmod.c gamma.c lgamma.c j0.c j1.c jn.c log.c \ log10.c log1p.c log__L.c pow.c sinh.c tanh.c +# XXX should do sqrt & support functions in assembly # common SRCS+= atan2.c sincos.c tan.c # ieee SRCS+= cabs.c cbrt.c support.c -MAN3+= acos.0 acosh.0 asin.0 asinh.0 atan.0 atan2.0 atanh.0 ceil.0 \ - cos.0 cosh.0 erf.0 exp.0 fabs.0 floor.0 fmod.0 hypot.0 ieee.0 \ - infnan.0 j0.0 lgamma.0 math.0 rint.0 sin.0 sinh.0 sqrt.0 \ - tan.0 tanh.0 +.elif (${MACHINE} == "tahoe") + +HARDWARE=${MACHINE} +.PATH: ${.CURDIR}/common_source ${.CURDIR}/common ${.CURDIR}/tahoe \ +# common_source +SRCS+= acosh.c asincos.c asinh.c atan.c atanh.c cosh.c erf.c exp.c \ + exp__E.c expm1.c floor.c fmod.c gamma.c lgamma.c j0.c j1.c jn.c log.c \ + log10.c log1p.c log__L.c pow.c sinh.c tanh.c +# common +SRCS+= atan2.c sincos.c tan.c +# tahoe +SRCS+= cabs.s cbrt.s sqrt.s support.s infnan.s + +.elif (${MACHINE} == "vax") + +HARDWARE=${MACHINE} +.PATH: ${.CURDIR}/common_source ${.CURDIR}/vax +# common_source +SRCS+= acosh.c asincos.c asinh.c atan.c atanh.c cosh.c erf.c exp.c \ + exp__E.c expm1.c floor.c fmod.c gamma.c lgamma.c j0.c j1.c jn.c log.c \ + log10.c log1p.c log__L.c pow.c sinh.c tanh.c +# vax +SRCS+= atan2.s cabs.s cbrt.s sqrt.s sincos.s tan.s argred.s support.s \ + infnan.s + +.endif + +MANSRC= ${.CURDIR}/common_source + +MAN3+= acos.3 acosh.3 asin.3 asinh.3 atan.3 atan2.3 atanh.3 ceil.3 \ + cos.3 cosh.3 erf.3 exp.3 fabs.3 floor.3 fmod.3 hypot.3 ieee.3 \ + infnan.3 j0.3 lgamma.3 math.3 rint.3 sin.3 sinh.3 sqrt.3 \ + tan.3 tanh.3 MLINKS+=erf.3 erfc.3 MLINKS+=exp.3 expm1.3 exp.3 log.3 exp.3 log10.3 exp.3 log1p.3 exp.3 pow.3 diff --git a/lib/libm/common/sincos.c b/lib/libm/common/sincos.c index 1118a341e1..a687c4e295 100644 --- a/lib/libm/common/sincos.c +++ b/lib/libm/common/sincos.c @@ -63,7 +63,8 @@ double x; big+a; return x; } - return x+x*sin__S(x*x); + a = x*x; + return x+x*sin__S(a); } double @@ -83,7 +84,8 @@ double x; } else { /* ... in [PI/4,3PI/4] */ a = PIo2-a; - return a+a*sin__S(a*a); /* rtn. S(PI/2-|x|) */ + c = a*a; + return a+a*sin__S(c); /* rtn. S(PI/2-|x|) */ } } if (a < small) { diff --git a/lib/libm/common_source/erf.c b/lib/libm/common_source/erf.c new file mode 100644 index 0000000000..ebb765fd99 --- /dev/null +++ b/lib/libm/common_source/erf.c @@ -0,0 +1,396 @@ +/*- + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)erf.c 5.7 (Berkeley) 12/16/92"; +#endif /* not lint */ + +/* Modified Nov 30, 1992 P. McILROY: + * Replaced expansions for x >= 1.25 (error 1.7ulp vs ~6ulp) + * Replaced even+odd with direct calculation for x < .84375, + * to avoid destructive cancellation. + * + * Performance of erfc(x): + * In 300000 trials in the range [.83, .84375] the + * maximum observed error was 3.6ulp. + * + * In [.84735,1.25] the maximum observed error was <2.5ulp in + * 100000 runs in the range [1.2, 1.25]. + * + * In [1.25,26] (Not including subnormal results) + * the error is < 1.7ulp. + */ + +/* double erf(double x) + * double erfc(double x) + * x + * 2 |\ + * erf(x) = --------- | exp(-t*t)dt + * sqrt(pi) \| + * 0 + * + * erfc(x) = 1-erf(x) + * + * Method: + * 1. Reduce x to |x| by erf(-x) = -erf(x) + * 2. For x in [0, 0.84375] + * erf(x) = x + x*P(x^2) + * erfc(x) = 1 - erf(x) if x<=0.25 + * = 0.5 + ((0.5-x)-x*P) if x in [0.25,0.84375] + * where + * 2 2 4 20 + * P = P(x ) = (p0 + p1 * x + p2 * x + ... + p10 * x ) + * is an approximation to (erf(x)-x)/x with precision + * + * -56.45 + * | P - (erf(x)-x)/x | <= 2 + * + * + * Remark. The formula is derived by noting + * erf(x) = (2/sqrt(pi))*(x - x^3/3 + x^5/10 - x^7/42 + ....) + * and that + * 2/sqrt(pi) = 1.128379167095512573896158903121545171688 + * is close to one. The interval is chosen because the fixed + * point of erf(x) is near 0.6174 (i.e., erf(x)=x when x is + * near 0.6174), and by some experiment, 0.84375 is chosen to + * guarantee the error is less than one ulp for erf. + * + * 3. For x in [0.84375,1.25], let s = x - 1, and + * c = 0.84506291151 rounded to single (24 bits) + * erf(x) = c + P1(s)/Q1(s) + * erfc(x) = (1-c) - P1(s)/Q1(s) + * |P1/Q1 - (erf(x)-c)| <= 2**-59.06 + * Remark: here we use the taylor series expansion at x=1. + * erf(1+s) = erf(1) + s*Poly(s) + * = 0.845.. + P1(s)/Q1(s) + * That is, we use rational approximation to approximate + * erf(1+s) - (c = (single)0.84506291151) + * Note that |P1/Q1|< 0.078 for x in [0.84375,1.25] + * where + * P1(s) = degree 6 poly in s + * Q1(s) = degree 6 poly in s + * + * 4. For x in [1.25, 2]; [2, 4] + * erf(x) = 1.0 - tiny + * erfc(x) = (1/x)exp(-x*x-(.5*log(pi) -.5z + R(z)/S(z)) + * + * Where z = 1/(x*x), R is degree 9, and S is degree 3; + * + * 5. For x in [4,28] + * erf(x) = 1.0 - tiny + * erfc(x) = (1/x)exp(-x*x-(.5*log(pi)+eps + zP(z)) + * + * Where P is degree 14 polynomial in 1/(x*x). + * + * Notes: + * Here 4 and 5 make use of the asymptotic series + * exp(-x*x) + * erfc(x) ~ ---------- * ( 1 + Poly(1/x^2) ); + * x*sqrt(pi) + * + * where for z = 1/(x*x) + * P(z) ~ z/2*(-1 + z*3/2*(1 + z*5/2*(-1 + z*7/2*(1 +...)))) + * + * Thus we use rational approximation to approximate + * erfc*x*exp(x*x) ~ 1/sqrt(pi); + * + * The error bound for the target function, G(z) for + * the interval + * [4, 28]: + * |eps + 1/(z)P(z) - G(z)| < 2**(-56.61) + * for [2, 4]: + * |R(z)/S(z) - G(z)| < 2**(-58.24) + * for [1.25, 2]: + * |R(z)/S(z) - G(z)| < 2**(-58.12) + * + * 6. For inf > x >= 28 + * erf(x) = 1 - tiny (raise inexact) + * erfc(x) = tiny*tiny (raise underflow) + * + * 7. Special cases: + * erf(0) = 0, erf(inf) = 1, erf(-inf) = -1, + * erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2, + * erfc/erf(NaN) is NaN + */ + +#if defined(vax) || defined(tahoe) +#define _IEEE 0 +#define TRUNC(x) (double) (float) (x) +#else +#define _IEEE 1 +#define TRUNC(x) *(((int *) &x) + 1) &= 0xf8000000 +#define infnan(x) 0.0 +#endif + +#ifdef _IEEE_LIBM +/* + * redefining "___function" to "function" in _IEEE_LIBM mode + */ +#include "ieee_libm.h" +#endif + +static double +tiny = 1e-300, +half = 0.5, +one = 1.0, +two = 2.0, +c = 8.45062911510467529297e-01, /* (float)0.84506291151 */ +/* + * Coefficients for approximation to erf in [0,0.84375] + */ +p0t8 = 1.02703333676410051049867154944018394163280, +p0 = 1.283791670955125638123339436800229927041e-0001, +p1 = -3.761263890318340796574473028946097022260e-0001, +p2 = 1.128379167093567004871858633779992337238e-0001, +p3 = -2.686617064084433642889526516177508374437e-0002, +p4 = 5.223977576966219409445780927846432273191e-0003, +p5 = -8.548323822001639515038738961618255438422e-0004, +p6 = 1.205520092530505090384383082516403772317e-0004, +p7 = -1.492214100762529635365672665955239554276e-0005, +p8 = 1.640186161764254363152286358441771740838e-0006, +p9 = -1.571599331700515057841960987689515895479e-0007, +p10= 1.073087585213621540635426191486561494058e-0008; +/* + * Coefficients for approximation to erf in [0.84375,1.25] + */ +static double +pa0 = -2.362118560752659485957248365514511540287e-0003, +pa1 = 4.148561186837483359654781492060070469522e-0001, +pa2 = -3.722078760357013107593507594535478633044e-0001, +pa3 = 3.183466199011617316853636418691420262160e-0001, +pa4 = -1.108946942823966771253985510891237782544e-0001, +pa5 = 3.547830432561823343969797140537411825179e-0002, +pa6 = -2.166375594868790886906539848893221184820e-0003, +qa1 = 1.064208804008442270765369280952419863524e-0001, +qa2 = 5.403979177021710663441167681878575087235e-0001, +qa3 = 7.182865441419627066207655332170665812023e-0002, +qa4 = 1.261712198087616469108438860983447773726e-0001, +qa5 = 1.363708391202905087876983523620537833157e-0002, +qa6 = 1.198449984679910764099772682882189711364e-0002; +/* + * log(sqrt(pi)) for large x expansions. + * The tail (lsqrtPI_lo) is included in the rational + * approximations. +*/ +static double + lsqrtPI_hi = .5723649429247000819387380943226; +/* + * lsqrtPI_lo = .000000000000000005132975581353913; + * + * Coefficients for approximation to erfc in [2, 4] +*/ +static double +rb0 = -1.5306508387410807582e-010, /* includes lsqrtPI_lo */ +rb1 = 2.15592846101742183841910806188e-008, +rb2 = 6.24998557732436510470108714799e-001, +rb3 = 8.24849222231141787631258921465e+000, +rb4 = 2.63974967372233173534823436057e+001, +rb5 = 9.86383092541570505318304640241e+000, +rb6 = -7.28024154841991322228977878694e+000, +rb7 = 5.96303287280680116566600190708e+000, +rb8 = -4.40070358507372993983608466806e+000, +rb9 = 2.39923700182518073731330332521e+000, +rb10 = -6.89257464785841156285073338950e-001, +sb1 = 1.56641558965626774835300238919e+001, +sb2 = 7.20522741000949622502957936376e+001, +sb3 = 9.60121069770492994166488642804e+001; +/* + * Coefficients for approximation to erfc in [1.25, 2] +*/ +static double +rc0 = -2.47925334685189288817e-007, /* includes lsqrtPI_lo */ +rc1 = 1.28735722546372485255126993930e-005, +rc2 = 6.24664954087883916855616917019e-001, +rc3 = 4.69798884785807402408863708843e+000, +rc4 = 7.61618295853929705430118701770e+000, +rc5 = 9.15640208659364240872946538730e-001, +rc6 = -3.59753040425048631334448145935e-001, +rc7 = 1.42862267989304403403849619281e-001, +rc8 = -4.74392758811439801958087514322e-002, +rc9 = 1.09964787987580810135757047874e-002, +rc10 = -1.28856240494889325194638463046e-003, +sc1 = 9.97395106984001955652274773456e+000, +sc2 = 2.80952153365721279953959310660e+001, +sc3 = 2.19826478142545234106819407316e+001; +/* + * Coefficients for approximation to erfc in [4,28] + */ +static double +rd0 = -2.1491361969012978677e-016, /* includes lsqrtPI_lo */ +rd1 = -4.99999999999640086151350330820e-001, +rd2 = 6.24999999772906433825880867516e-001, +rd3 = -1.54166659428052432723177389562e+000, +rd4 = 5.51561147405411844601985649206e+000, +rd5 = -2.55046307982949826964613748714e+001, +rd6 = 1.43631424382843846387913799845e+002, +rd7 = -9.45789244999420134263345971704e+002, +rd8 = 6.94834146607051206956384703517e+003, +rd9 = -5.27176414235983393155038356781e+004, +rd10 = 3.68530281128672766499221324921e+005, +rd11 = -2.06466642800404317677021026611e+006, +rd12 = 7.78293889471135381609201431274e+006, +rd13 = -1.42821001129434127360582351685e+007; + +double erf(x) + double x; +{ + double R,S,P,Q,ax,s,y,z,r,fabs(),exp(); + if(!finite(x)) { /* erf(nan)=nan */ + if (isnan(x)) + return(x); + return (x > 0 ? one : -one); /* erf(+/-inf)= +/-1 */ + } + if ((ax = x) < 0) + ax = - ax; + if (ax < .84375) { + if (ax < 3.7e-09) { + if (ax < 1.0e-308) + return 0.125*(8.0*x+p0t8*x); /*avoid underflow */ + return x + p0*x; + } + y = x*x; + r = y*(p1+y*(p2+y*(p3+y*(p4+y*(p5+ + y*(p6+y*(p7+y*(p8+y*(p9+y*p10))))))))); + return x + x*(p0+r); + } + if (ax < 1.25) { /* 0.84375 <= |x| < 1.25 */ + s = fabs(x)-one; + P = pa0+s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6))))); + Q = one+s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6))))); + if (x>=0) + return (c + P/Q); + else + return (-c - P/Q); + } + if (ax >= 6.0) { /* inf>|x|>=6 */ + if (x >= 0.0) + return (one-tiny); + else + return (tiny-one); + } + /* 1.25 <= |x| < 6 */ + z = -ax*ax; + s = -one/z; + if (ax < 2.0) { + R = rc0+s*(rc1+s*(rc2+s*(rc3+s*(rc4+s*(rc5+ + s*(rc6+s*(rc7+s*(rc8+s*(rc9+s*rc10))))))))); + S = one+s*(sc1+s*(sc2+s*sc3)); + } else { + R = rb0+s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*(rb5+ + s*(rb6+s*(rb7+s*(rb8+s*(rb9+s*rb10))))))))); + S = one+s*(sb1+s*(sb2+s*sb3)); + } + y = (R/S -.5*s) - lsqrtPI_hi; + z += y; + z = exp(z)/ax; + if (x >= 0) + return (one-z); + else + return (z-one); +} + +double erfc(x) + double x; +{ + double R,S,P,Q,s,ax,y,z,r,fabs(),exp__D(); + if (!finite(x)) { + if (isnan(x)) /* erfc(NaN) = NaN */ + return(x); + else if (x > 0) /* erfc(+-inf)=0,2 */ + return 0.0; + else + return 2.0; + } + if ((ax = x) < 0) + ax = -ax; + if (ax < .84375) { /* |x|<0.84375 */ + if (ax < 1.38777878078144568e-17) /* |x|<2**-56 */ + return one-x; + y = x*x; + r = y*(p1+y*(p2+y*(p3+y*(p4+y*(p5+ + y*(p6+y*(p7+y*(p8+y*(p9+y*p10))))))))); + if (ax < .0625) { /* |x|<2**-4 */ + return (one-(x+x*(p0+r))); + } else { + r = x*(p0+r); + r += (x-half); + return (half - r); + } + } + if (ax < 1.25) { /* 0.84375 <= |x| < 1.25 */ + s = ax-one; + P = pa0+s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6))))); + Q = one+s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6))))); + if (x>=0) { + z = one-c; return z - P/Q; + } else { + z = c+P/Q; return one+z; + } + } + if (ax >= 28) /* Out of range */ + if (x>0) + return (tiny*tiny); + else + return (two-tiny); + z = ax; + TRUNC(z); + y = z - ax; y *= (ax+z); + z *= -z; /* Here z + y = -x^2 */ + s = one/(-z-y); /* 1/(x*x) */ + if (ax >= 4) { /* 6 <= ax */ + R = s*(rd1+s*(rd2+s*(rd3+s*(rd4+s*(rd5+ + s*(rd6+s*(rd7+s*(rd8+s*(rd9+s*(rd10 + +s*(rd11+s*(rd12+s*rd13)))))))))))); + y += rd0; + } else if (ax >= 2) { + R = rb0+s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*(rb5+ + s*(rb6+s*(rb7+s*(rb8+s*(rb9+s*rb10))))))))); + S = one+s*(sb1+s*(sb2+s*sb3)); + y += R/S; + R = -.5*s; + } else { + R = rc0+s*(rc1+s*(rc2+s*(rc3+s*(rc4+s*(rc5+ + s*(rc6+s*(rc7+s*(rc8+s*(rc9+s*rc10))))))))); + S = one+s*(sc1+s*(sc2+s*sc3)); + y += R/S; + R = -.5*s; + } + /* return exp(-x^2 - lsqrtPI_hi + R + y)/x; */ + s = ((R + y) - lsqrtPI_hi) + z; + y = (((z-s) - lsqrtPI_hi) + R) + y; + r = exp__D(s, y)/x; + if (x>0) + return r; + else + return two-r; +} diff --git a/lib/libm/common_source/exp.c b/lib/libm/common_source/exp.c index dc1b9444da..ec5d11f343 100644 --- a/lib/libm/common_source/exp.c +++ b/lib/libm/common_source/exp.c @@ -32,7 +32,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)exp.c 5.6 (Berkeley) 10/9/90"; +static char sccsid[] = "@(#)exp.c 5.7 (Berkeley) 12/2/92"; #endif /* not lint */ /* EXP(X) @@ -156,3 +156,48 @@ double x; /* exp(INF) is INF, exp(+big#) overflows to INF */ return( finite(x) ? scalb(1.0,5000) : x); } + +/* returns exp(r = x + c) for |c| < |x| with no overlap. */ + +double exp__D(x, c) +double x, c; +{ + double z,hi,lo, t; + int k; + +#if !defined(vax)&&!defined(tahoe) + if (x!=x) return(x); /* x is NaN */ +#endif /* !defined(vax)&&!defined(tahoe) */ + if ( x <= lnhuge ) { + if ( x >= lntiny ) { + + /* argument reduction : x --> x - k*ln2 */ + z = invln2*x; + k = z + copysign(.5, x); + + /* express (x+c)-k*ln2 as hi-lo and let x=hi-lo rounded */ + + hi=(x-k*ln2hi); /* Exact. */ + x= hi - (lo = k*ln2lo-c); + /* return 2^k*[1+x+x*c/(2+c)] */ + z=x*x; + c= x - z*(p1+z*(p2+z*(p3+z*(p4+z*p5)))); + c = (x*c)/(2.0-c); + + return scalb(1.+(hi-(lo - c)), k); + } + /* end of x > lntiny */ + + else + /* exp(-big#) underflows to zero */ + if(finite(x)) return(scalb(1.0,-5000)); + + /* exp(-INF) is zero */ + else return(0.0); + } + /* end of x < lnhuge */ + + else + /* exp(INF) is INF, exp(+big#) overflows to INF */ + return( finite(x) ? scalb(1.0,5000) : x); +} diff --git a/lib/libm/common_source/floor.c b/lib/libm/common_source/floor.c index 6a7eef75ce..a5652c4861 100644 --- a/lib/libm/common_source/floor.c +++ b/lib/libm/common_source/floor.c @@ -29,19 +29,10 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00068 - * -------------------- ----- ---------------------- - * - * 17 Dec 1992 Karl Lehenbauer Removed defines for rint() to - * add it's use in 386BSD. */ - static char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/tty_pty.c,v 1.3 92/01/21 21:31:23 william Exp $"; #ifndef lint -static char sccsid[] = "@(#)floor.c 5.7 (Berkeley) 10/9/90"; +static char sccsid[] = "@(#)floor.c 5.8 (Berkeley) 10/1/92"; #endif /* not lint */ #include "mathimpl.h" @@ -54,10 +45,6 @@ ic(L, 4503599627370496.0E0, 52, 1.0) /* 2**52 */ #define L vccast(L) #endif - -double ceil(); -double floor(); - /* * floor(x) := the largest integer no larger than x; * ceil(x) := -floor(-x), for all real x. @@ -69,7 +56,7 @@ double floor(x) double x; { - double y; + volatile double y; if ( #if !defined(vax)&&!defined(tahoe) @@ -90,7 +77,7 @@ double ceil(x) double x; { - double y; + volatile double y; if ( #if !defined(vax)&&!defined(tahoe) @@ -107,6 +94,7 @@ double x; } } +#ifndef ns32000 /* rint() is in ./NATIONAL/support.s */ /* * algorithm for rint(x) in pseudo-pascal form ... * @@ -132,7 +120,8 @@ double rint(x) double x; { - double s,t; + double s; + volatile double t; const double one = 1.0; #if !defined(vax)&&!defined(tahoe) @@ -145,3 +134,4 @@ double x; t = x + s; /* x+s rounded to integer */ return (t - s); } +#endif /* not national */ diff --git a/lib/libm/common_source/gamma.c b/lib/libm/common_source/gamma.c new file mode 100644 index 0000000000..b9198545ee --- /dev/null +++ b/lib/libm/common_source/gamma.c @@ -0,0 +1,336 @@ +/*- + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)gamma.c 5.3 (Berkeley) 12/16/92"; +#endif /* not lint */ + +/* + * This code by P. McIlroy, Oct 1992; + * + * The financial support of UUNET Communications Services is greatfully + * acknowledged. + */ + +#include +#include "mathimpl.h" +#include + +/* METHOD: + * x < 0: Use reflection formula, G(x) = pi/(sin(pi*x)*x*G(x)) + * At negative integers, return +Inf, and set errno. + * + * x < 6.5: + * Use argument reduction G(x+1) = xG(x) to reach the + * range [1.066124,2.066124]. Use a rational + * approximation centered at the minimum (x0+1) to + * ensure monotonicity. + * + * x >= 6.5: Use the asymptotic approximation (Stirling's formula) + * adjusted for equal-ripples: + * + * log(G(x)) ~= (x-.5)*(log(x)-1) + .5(log(2*pi)-1) + 1/x*P(1/(x*x)) + * + * Keep extra precision in multiplying (x-.5)(log(x)-1), to + * avoid premature round-off. + * + * Special values: + * non-positive integer: Set overflow trap; return +Inf; + * x > 171.63: Set overflow trap; return +Inf; + * NaN: Set invalid trap; return NaN + * + * Accuracy: Gamma(x) is accurate to within + * x > 0: error provably < 0.9ulp. + * Maximum observed in 1,000,000 trials was .87ulp. + * x < 0: + * Maximum observed error < 4ulp in 1,000,000 trials. + */ + +static double neg_gam __P((double)); +static double small_gam __P((double)); +static double smaller_gam __P((double)); +static struct Double large_gam __P((double)); +static struct Double ratfun_gam __P((double, double)); + +/* + * Rational approximation, A0 + x*x*P(x)/Q(x), on the interval + * [1.066.., 2.066..] accurate to 4.25e-19. + */ +#define LEFT -.3955078125 /* left boundary for rat. approx */ +#define x0 .461632144968362356785 /* xmin - 1 */ + +#define a0_hi 0.88560319441088874992 +#define a0_lo -.00000000000000004996427036469019695 +#define P0 6.21389571821820863029017800727e-01 +#define P1 2.65757198651533466104979197553e-01 +#define P2 5.53859446429917461063308081748e-03 +#define P3 1.38456698304096573887145282811e-03 +#define P4 2.40659950032711365819348969808e-03 +#define Q0 1.45019531250000000000000000000e+00 +#define Q1 1.06258521948016171343454061571e+00 +#define Q2 -2.07474561943859936441469926649e-01 +#define Q3 -1.46734131782005422506287573015e-01 +#define Q4 3.07878176156175520361557573779e-02 +#define Q5 5.12449347980666221336054633184e-03 +#define Q6 -1.76012741431666995019222898833e-03 +#define Q7 9.35021023573788935372153030556e-05 +#define Q8 6.13275507472443958924745652239e-06 +/* + * Constants for large x approximation (x in [6, Inf]) + * (Accurate to 2.8*10^-19 absolute) + */ +#define lns2pi_hi 0.418945312500000 +#define lns2pi_lo -.000006779295327258219670263595 +#define Pa0 8.33333333333333148296162562474e-02 +#define Pa1 -2.77777777774548123579378966497e-03 +#define Pa2 7.93650778754435631476282786423e-04 +#define Pa3 -5.95235082566672847950717262222e-04 +#define Pa4 8.41428560346653702135821806252e-04 +#define Pa5 -1.89773526463879200348872089421e-03 +#define Pa6 5.69394463439411649408050664078e-03 +#define Pa7 -1.44705562421428915453880392761e-02 + +static const double zero = 0., one = 1.0, tiny = 1e-300; +static int endian; +/* + * TRUNC sets trailing bits in a floating-point number to zero. + * is a temporary variable. + */ +#if defined(vax) || defined(tahoe) +#define _IEEE 0 +#define TRUNC(x) x = (double) (float) (x) +#else +#define _IEEE 1 +#define TRUNC(x) *(((int *) &x) + endian) &= 0xf8000000 +#define infnan(x) 0.0 +#endif + +double +gamma(x) + double x; +{ + struct Double u; + endian = (*(int *) &one) ? 1 : 0; + + if (x >= 6) { + if(x > 171.63) + return(one/zero); + u = large_gam(x); + return(exp__D(u.a, u.b)); + } else if (x >= 1.0 + LEFT + x0) + return (small_gam(x)); + else if (x > 1.e-17) + return (smaller_gam(x)); + else if (x > -1.e-17) { + if (x == 0.0) + if (!_IEEE) return (infnan(ERANGE)); + else return (one/x); + one+1e-20; /* Raise inexact flag. */ + return (one/x); + } else if (!finite(x)) { + if (_IEEE) /* x = NaN, -Inf */ + return (x*x); + else + return (infnan(EDOM)); + } else + return (neg_gam(x)); +} +/* + * Accurate to max(ulp(1/128) absolute, 2^-66 relative) error. + */ +static struct Double +large_gam(x) + double x; +{ + double z, p; + int i; + struct Double t, u, v; + + z = one/(x*x); + p = Pa0+z*(Pa1+z*(Pa2+z*(Pa3+z*(Pa4+z*(Pa5+z*(Pa6+z*Pa7)))))); + p = p/x; + + u = log__D(x); + u.a -= one; + v.a = (x -= .5); + TRUNC(v.a); + v.b = x - v.a; + t.a = v.a*u.a; /* t = (x-.5)*(log(x)-1) */ + t.b = v.b*u.a + x*u.b; + /* return t.a + t.b + lns2pi_hi + lns2pi_lo + p */ + t.b += lns2pi_lo; t.b += p; + u.a = lns2pi_hi + t.b; u.a += t.a; + u.b = t.a - u.a; + u.b += lns2pi_hi; u.b += t.b; + return (u); +} +/* + * Good to < 1 ulp. (provably .90 ulp; .87 ulp on 1,000,000 runs.) + * It also has correct monotonicity. + */ +static double +small_gam(x) + double x; +{ + double y, ym1, t, x1; + struct Double yy, r; + y = x - one; + ym1 = y - one; + if (y <= 1.0 + (LEFT + x0)) { + yy = ratfun_gam(y - x0, 0); + return (yy.a + yy.b); + } + r.a = y; + TRUNC(r.a); + yy.a = r.a - one; + y = ym1; + yy.b = r.b = y - yy.a; + /* Argument reduction: G(x+1) = x*G(x) */ + for (ym1 = y-one; ym1 > LEFT + x0; y = ym1--, yy.a--) { + t = r.a*yy.a; + r.b = r.a*yy.b + y*r.b; + r.a = t; + TRUNC(r.a); + r.b += (t - r.a); + } + /* Return r*gamma(y). */ + yy = ratfun_gam(y - x0, 0); + y = r.b*(yy.a + yy.b) + r.a*yy.b; + y += yy.a*r.a; + return (y); +} +/* + * Good on (0, 1+x0+LEFT]. Accurate to 1ulp. + */ +static double +smaller_gam(x) + double x; +{ + double t, d; + struct Double r, xx; + if (x < x0 + LEFT) { + t = x, TRUNC(t); + d = (t+x)*(x-t); + t *= t; + xx.a = (t + x), TRUNC(xx.a); + xx.b = x - xx.a; xx.b += t; xx.b += d; + t = (one-x0); t += x; + d = (one-x0); d -= t; d += x; + x = xx.a + xx.b; + } else { + xx.a = x, TRUNC(xx.a); + xx.b = x - xx.a; + t = x - x0; + d = (-x0 -t); d += x; + } + r = ratfun_gam(t, d); + d = r.a/x, TRUNC(d); + r.a -= d*xx.a; r.a -= d*xx.b; r.a += r.b; + return (d + r.a/x); +} +/* + * returns (z+c)^2 * P(z)/Q(z) + a0 + */ +static struct Double +ratfun_gam(z, c) + double z, c; +{ + int i; + double p, q; + struct Double r, t; + + q = Q0 +z*(Q1+z*(Q2+z*(Q3+z*(Q4+z*(Q5+z*(Q6+z*(Q7+z*Q8))))))); + p = P0 + z*(P1 + z*(P2 + z*(P3 + z*P4))); + + /* return r.a + r.b = a0 + (z+c)^2*p/q, with r.a truncated to 26 bits. */ + p = p/q; + t.a = z, TRUNC(t.a); /* t ~= z + c */ + t.b = (z - t.a) + c; + t.b *= (t.a + z); + q = (t.a *= t.a); /* t = (z+c)^2 */ + TRUNC(t.a); + t.b += (q - t.a); + r.a = p, TRUNC(r.a); /* r = P/Q */ + r.b = p - r.a; + t.b = t.b*p + t.a*r.b + a0_lo; + t.a *= r.a; /* t = (z+c)^2*(P/Q) */ + r.a = t.a + a0_hi, TRUNC(r.a); + r.b = ((a0_hi-r.a) + t.a) + t.b; + return (r); /* r = a0 + t */ +} + +static double +neg_gam(x) + double x; +{ + int sgn = 1; + struct Double lg, lsine; + double y, z; + + y = floor(x + .5); + if (y == x) /* Negative integer. */ + if(!_IEEE) + return (infnan(ERANGE)); + else + return (one/zero); + z = fabs(x - y); + y = .5*ceil(x); + if (y == ceil(y)) + sgn = -1; + if (z < .25) + z = sin(M_PI*z); + else + z = cos(M_PI*(0.5-z)); + /* Special case: G(1-x) = Inf; G(x) may be nonzero. */ + if (x < -170) { + if (x < -190) + return ((double)sgn*tiny*tiny); + y = one - x; /* exact: 128 < |x| < 255 */ + lg = large_gam(y); + lsine = log__D(M_PI/z); /* = TRUNC(log(u)) + small */ + lg.a -= lsine.a; /* exact (opposite signs) */ + lg.b -= lsine.b; + y = -(lg.a + lg.b); + z = (y + lg.a) + lg.b; + y = exp__D(y, z); + if (sgn < 0) y = -y; + return (y); + } + y = one-x; + if (one-y == x) + y = gamma(y); + else /* 1-x is inexact */ + y = -x*gamma(-x); + if (sgn < 0) y = -y; + return (M_PI / (y*z)); +} diff --git a/lib/libm/common_source/j0.c b/lib/libm/common_source/j0.c new file mode 100644 index 0000000000..e0335cf046 --- /dev/null +++ b/lib/libm/common_source/j0.c @@ -0,0 +1,439 @@ +/*- + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)j0.c 5.5 (Berkeley) 12/16/92"; +#endif /* not lint */ + +/* + * 16 December 1992 + * Minor modifications by Peter McIlroy to adapt non-IEEE architecture. + */ + +/* + * ==================================================== + * Copyright (C) 1992 by Sun Microsystems, Inc. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + * ******************* WARNING ******************** + * This is an alpha version of SunPro's FDLIBM (Freely + * Distributable Math Library) for IEEE double precision + * arithmetic. FDLIBM is a basic math library written + * in C that runs on machines that conform to IEEE + * Standard 754/854. This alpha version is distributed + * for testing purpose. Those who use this software + * should report any bugs to + * + * fdlibm-comments@sunpro.eng.sun.com + * + * -- K.C. Ng, Oct 12, 1992 + * ************************************************ + */ + +/* double j0(double x), y0(double x) + * Bessel function of the first and second kinds of order zero. + * Method -- j0(x): + * 1. For tiny x, we use j0(x) = 1 - x^2/4 + x^4/64 - ... + * 2. Reduce x to |x| since j0(x)=j0(-x), and + * for x in (0,2) + * j0(x) = 1-z/4+ z^2*R0/S0, where z = x*x; + * (precision: |j0-1+z/4-z^2R0/S0 |<2**-63.67 ) + * for x in (2,inf) + * j0(x) = sqrt(2/(pi*x))*(p0(x)*cos(x0)-q0(x)*sin(x0)) + * where x0 = x-pi/4. It is better to compute sin(x0),cos(x0) + * as follow: + * cos(x0) = cos(x)cos(pi/4)+sin(x)sin(pi/4) + * = 1/sqrt(2) * (cos(x) + sin(x)) + * sin(x0) = sin(x)cos(pi/4)-cos(x)sin(pi/4) + * = 1/sqrt(2) * (sin(x) - cos(x)) + * (To avoid cancellation, use + * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + * to compute the worse one.) + * + * 3 Special cases + * j0(nan)= nan + * j0(0) = 1 + * j0(inf) = 0 + * + * Method -- y0(x): + * 1. For x<2. + * Since + * y0(x) = 2/pi*(j0(x)*(ln(x/2)+Euler) + x^2/4 - ...) + * therefore y0(x)-2/pi*j0(x)*ln(x) is an even function. + * We use the following function to approximate y0, + * y0(x) = U(z)/V(z) + (2/pi)*(j0(x)*ln(x)), z= x^2 + * where + * U(z) = u0 + u1*z + ... + u6*z^6 + * V(z) = 1 + v1*z + ... + v4*z^4 + * with absolute approximation error bounded by 2**-72. + * Note: For tiny x, U/V = u0 and j0(x)~1, hence + * y0(tiny) = u0 + (2/pi)*ln(tiny), (choose tiny<2**-27) + * 2. For x>=2. + * y0(x) = sqrt(2/(pi*x))*(p0(x)*cos(x0)+q0(x)*sin(x0)) + * where x0 = x-pi/4. It is better to compute sin(x0),cos(x0) + * by the method mentioned above. + * 3. Special cases: y0(0)=-inf, y0(x<0)=NaN, y0(inf)=0. + */ + +#include +#include +#if defined(vax) || defined(tahoe) +#define _IEEE 0 +#else +#define _IEEE 1 +#define infnan(x) (0.0) +#endif + +static double pzero __P((double)), qzero __P((double)); + +static double +huge = 1e300, +zero = 0.0, +one = 1.0, +invsqrtpi= 5.641895835477562869480794515607725858441e-0001, +tpi = 0.636619772367581343075535053490057448, + /* R0/S0 on [0, 2.00] */ +r02 = 1.562499999999999408594634421055018003102e-0002, +r03 = -1.899792942388547334476601771991800712355e-0004, +r04 = 1.829540495327006565964161150603950916854e-0006, +r05 = -4.618326885321032060803075217804816988758e-0009, +s01 = 1.561910294648900170180789369288114642057e-0002, +s02 = 1.169267846633374484918570613449245536323e-0004, +s03 = 5.135465502073181376284426245689510134134e-0007, +s04 = 1.166140033337900097836930825478674320464e-0009; + +double +j0(x) + double x; +{ + double z, s,c,ss,cc,r,u,v; + + if (!finite(x)) + if (_IEEE) return one/(x*x); + else return (0); + x = fabs(x); + if (x >= 2.0) { /* |x| >= 2.0 */ + s = sin(x); + c = cos(x); + ss = s-c; + cc = s+c; + if (x < .5 * DBL_MAX) { /* make sure x+x not overflow */ + z = -cos(x+x); + if ((s*c) 6.80564733841876927e+38) /* 2^129 */ + z = (invsqrtpi*cc)/sqrt(x); + else { + u = pzero(x); v = qzero(x); + z = invsqrtpi*(u*cc-v*ss)/sqrt(x); + } + return z; + } + if (x < 1.220703125e-004) { /* |x| < 2**-13 */ + if (huge+x > one) { /* raise inexact if x != 0 */ + if (x < 7.450580596923828125e-009) /* |x|<2**-27 */ + return one; + else return (one - 0.25*x*x); + } + } + z = x*x; + r = z*(r02+z*(r03+z*(r04+z*r05))); + s = one+z*(s01+z*(s02+z*(s03+z*s04))); + if (x < one) { /* |x| < 1.00 */ + return (one + z*(-0.25+(r/s))); + } else { + u = 0.5*x; + return ((one+u)*(one-u)+z*(r/s)); + } +} + +static double +u00 = -7.380429510868722527422411862872999615628e-0002, +u01 = 1.766664525091811069896442906220827182707e-0001, +u02 = -1.381856719455968955440002438182885835344e-0002, +u03 = 3.474534320936836562092566861515617053954e-0004, +u04 = -3.814070537243641752631729276103284491172e-0006, +u05 = 1.955901370350229170025509706510038090009e-0008, +u06 = -3.982051941321034108350630097330144576337e-0011, +v01 = 1.273048348341237002944554656529224780561e-0002, +v02 = 7.600686273503532807462101309675806839635e-0005, +v03 = 2.591508518404578033173189144579208685163e-0007, +v04 = 4.411103113326754838596529339004302243157e-0010; + +double +y0(x) + double x; +{ + double z, s,c,ss,cc,u,v,j0(); + /* Y0(NaN) is NaN, y0(-inf) is Nan, y0(inf) is 0 */ + if (!finite(x)) + if (_IEEE) + return (one/(x+x*x)); + else + return (0); + if (x == 0) + if (_IEEE) return (-one/zero); + else return(infnan(-ERANGE)); + if (x<0) + if (_IEEE) return (zero/zero); + else return (infnan(EDOM)); + if (x >= 2.00) { /* |x| >= 2.0 */ + /* y0(x) = sqrt(2/(pi*x))*(p0(x)*sin(x0)+q0(x)*cos(x0)) + * where x0 = x-pi/4 + * Better formula: + * cos(x0) = cos(x)cos(pi/4)+sin(x)sin(pi/4) + * = 1/sqrt(2) * (sin(x) + cos(x)) + * sin(x0) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) + * = 1/sqrt(2) * (sin(x) - cos(x)) + * To avoid cancellation, use + * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + * to compute the worse one. + */ + s = sin(x); + c = cos(x); + ss = s-c; + cc = s+c; + /* + * j0(x) = 1/sqrt(pi) * (P(0,x)*cc - Q(0,x)*ss) / sqrt(x) + * y0(x) = 1/sqrt(pi) * (P(0,x)*ss + Q(0,x)*cc) / sqrt(x) + */ + if (x < .5 * DBL_MAX) { /* make sure x+x not overflow */ + z = -cos(x+x); + if ((s*c) 6.80564733841876927e+38) /* > 2^129 */ + z = (invsqrtpi*ss)/sqrt(x); + else { + u = pzero(x); v = qzero(x); + z = invsqrtpi*(u*ss+v*cc)/sqrt(x); + } + return z; + } + if (x <= 7.450580596923828125e-009) { /* x < 2**-27 */ + return (u00 + tpi*log(x)); + } + z = x*x; + u = u00+z*(u01+z*(u02+z*(u03+z*(u04+z*(u05+z*u06))))); + v = one+z*(v01+z*(v02+z*(v03+z*v04))); + return (u/v + tpi*(j0(x)*log(x))); +} + +/* The asymptotic expansions of pzero is + * 1 - 9/128 s^2 + 11025/98304 s^4 - ..., where s = 1/x. + * For x >= 2, We approximate pzero by + * pzero(x) = 1 + (R/S) + * where R = pr0 + pr1*s^2 + pr2*s^4 + ... + pr5*s^10 + * S = 1 + ps0*s^2 + ... + ps4*s^10 + * and + * | pzero(x)-1-R/S | <= 2 ** ( -60.26) + */ +static double pr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ + 0.0, + -7.031249999999003994151563066182798210142e-0002, + -8.081670412753498508883963849859423939871e+0000, + -2.570631056797048755890526455854482662510e+0002, + -2.485216410094288379417154382189125598962e+0003, + -5.253043804907295692946647153614119665649e+0003, +}; +static double ps8[5] = { + 1.165343646196681758075176077627332052048e+0002, + 3.833744753641218451213253490882686307027e+0003, + 4.059785726484725470626341023967186966531e+0004, + 1.167529725643759169416844015694440325519e+0005, + 4.762772841467309430100106254805711722972e+0004, +}; + +static double pr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ + -1.141254646918944974922813501362824060117e-0011, + -7.031249408735992804117367183001996028304e-0002, + -4.159610644705877925119684455252125760478e+0000, + -6.767476522651671942610538094335912346253e+0001, + -3.312312996491729755731871867397057689078e+0002, + -3.464333883656048910814187305901796723256e+0002, +}; +static double ps5[5] = { + 6.075393826923003305967637195319271932944e+0001, + 1.051252305957045869801410979087427910437e+0003, + 5.978970943338558182743915287887408780344e+0003, + 9.625445143577745335793221135208591603029e+0003, + 2.406058159229391070820491174867406875471e+0003, +}; + +static double pr3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */ + -2.547046017719519317420607587742992297519e-0009, + -7.031196163814817199050629727406231152464e-0002, + -2.409032215495295917537157371488126555072e+0000, + -2.196597747348830936268718293366935843223e+0001, + -5.807917047017375458527187341817239891940e+0001, + -3.144794705948885090518775074177485744176e+0001, +}; +static double ps3[5] = { + 3.585603380552097167919946472266854507059e+0001, + 3.615139830503038919981567245265266294189e+0002, + 1.193607837921115243628631691509851364715e+0003, + 1.127996798569074250675414186814529958010e+0003, + 1.735809308133357510239737333055228118910e+0002, +}; + +static double pr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ + -8.875343330325263874525704514800809730145e-0008, + -7.030309954836247756556445443331044338352e-0002, + -1.450738467809529910662233622603401167409e+0000, + -7.635696138235277739186371273434739292491e+0000, + -1.119316688603567398846655082201614524650e+0001, + -3.233645793513353260006821113608134669030e+0000, +}; +static double ps2[5] = { + 2.222029975320888079364901247548798910952e+0001, + 1.362067942182152109590340823043813120940e+0002, + 2.704702786580835044524562897256790293238e+0002, + 1.538753942083203315263554770476850028583e+0002, + 1.465761769482561965099880599279699314477e+0001, +}; + +static double pzero(x) + double x; +{ + double *p,*q,z,r,s; + if (x >= 8.00) {p = pr8; q= ps8;} + else if (x >= 4.54545211791992188) {p = pr5; q= ps5;} + else if (x >= 2.85714149475097656) {p = pr3; q= ps3;} + else if (x >= 2.00) {p = pr2; q= ps2;} + z = one/(x*x); + r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))); + s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4])))); + return one+ r/s; +} + + +/* For x >= 8, the asymptotic expansions of qzero is + * -1/8 s + 75/1024 s^3 - ..., where s = 1/x. + * We approximate pzero by + * qzero(x) = s*(-1.25 + (R/S)) + * where R = qr0 + qr1*s^2 + qr2*s^4 + ... + qr5*s^10 + * S = 1 + qs0*s^2 + ... + qs5*s^12 + * and + * | qzero(x)/s +1.25-R/S | <= 2 ** ( -61.22) + */ +static double qr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ + 0.0, + 7.324218749999350414479738504551775297096e-0002, + 1.176820646822526933903301695932765232456e+0001, + 5.576733802564018422407734683549251364365e+0002, + 8.859197207564685717547076568608235802317e+0003, + 3.701462677768878501173055581933725704809e+0004, +}; +static double qs8[6] = { + 1.637760268956898345680262381842235272369e+0002, + 8.098344946564498460163123708054674227492e+0003, + 1.425382914191204905277585267143216379136e+0005, + 8.033092571195144136565231198526081387047e+0005, + 8.405015798190605130722042369969184811488e+0005, + -3.438992935378666373204500729736454421006e+0005, +}; + +static double qr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ + 1.840859635945155400568380711372759921179e-0011, + 7.324217666126847411304688081129741939255e-0002, + 5.835635089620569401157245917610984757296e+0000, + 1.351115772864498375785526599119895942361e+0002, + 1.027243765961641042977177679021711341529e+0003, + 1.989977858646053872589042328678602481924e+0003, +}; +static double qs5[6] = { + 8.277661022365377058749454444343415524509e+0001, + 2.077814164213929827140178285401017305309e+0003, + 1.884728877857180787101956800212453218179e+0004, + 5.675111228949473657576693406600265778689e+0004, + 3.597675384251145011342454247417399490174e+0004, + -5.354342756019447546671440667961399442388e+0003, +}; + +static double qr3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */ + 4.377410140897386263955149197672576223054e-0009, + 7.324111800429115152536250525131924283018e-0002, + 3.344231375161707158666412987337679317358e+0000, + 4.262184407454126175974453269277100206290e+0001, + 1.708080913405656078640701512007621675724e+0002, + 1.667339486966511691019925923456050558293e+0002, +}; +static double qs3[6] = { + 4.875887297245871932865584382810260676713e+0001, + 7.096892210566060535416958362640184894280e+0002, + 3.704148226201113687434290319905207398682e+0003, + 6.460425167525689088321109036469797462086e+0003, + 2.516333689203689683999196167394889715078e+0003, + -1.492474518361563818275130131510339371048e+0002, +}; + +static double qr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ + 1.504444448869832780257436041633206366087e-0007, + 7.322342659630792930894554535717104926902e-0002, + 1.998191740938159956838594407540292600331e+0000, + 1.449560293478857407645853071687125850962e+0001, + 3.166623175047815297062638132537957315395e+0001, + 1.625270757109292688799540258329430963726e+0001, +}; +static double qs2[6] = { + 3.036558483552191922522729838478169383969e+0001, + 2.693481186080498724211751445725708524507e+0002, + 8.447837575953201460013136756723746023736e+0002, + 8.829358451124885811233995083187666981299e+0002, + 2.126663885117988324180482985363624996652e+0002, + -5.310954938826669402431816125780738924463e+0000, +}; + +static double qzero(x) + double x; +{ + double *p,*q, s,r,z; + if (x >= 8.00) {p = qr8; q= qs8;} + else if (x >= 4.54545211791992188) {p = qr5; q= qs5;} + else if (x >= 2.85714149475097656) {p = qr3; q= qs3;} + else if (x >= 2.00) {p = qr2; q= qs2;} + z = one/(x*x); + r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))); + s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5]))))); + return (-.125 + r/s)/x; +} diff --git a/lib/libm/common_source/j1.c b/lib/libm/common_source/j1.c new file mode 100644 index 0000000000..3c2c6fd250 --- /dev/null +++ b/lib/libm/common_source/j1.c @@ -0,0 +1,446 @@ +/*- + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)j1.c 5.5 (Berkeley) 12/16/92"; +#endif /* not lint */ + +/* + * 16 December 1992 + * Minor modifications by Peter McIlroy to adapt non-IEEE architecture. + */ + +/* + * ==================================================== + * Copyright (C) 1992 by Sun Microsystems, Inc. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + * ******************* WARNING ******************** + * This is an alpha version of SunPro's FDLIBM (Freely + * Distributable Math Library) for IEEE double precision + * arithmetic. FDLIBM is a basic math library written + * in C that runs on machines that conform to IEEE + * Standard 754/854. This alpha version is distributed + * for testing purpose. Those who use this software + * should report any bugs to + * + * fdlibm-comments@sunpro.eng.sun.com + * + * -- K.C. Ng, Oct 12, 1992 + * ************************************************ + */ + +/* double j1(double x), y1(double x) + * Bessel function of the first and second kinds of order zero. + * Method -- j1(x): + * 1. For tiny x, we use j1(x) = x/2 - x^3/16 + x^5/384 - ... + * 2. Reduce x to |x| since j1(x)=-j1(-x), and + * for x in (0,2) + * j1(x) = x/2 + x*z*R0/S0, where z = x*x; + * (precision: |j1/x - 1/2 - R0/S0 |<2**-61.51 ) + * for x in (2,inf) + * j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1)) + * y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x1)+q1(x)*cos(x1)) + * where x1 = x-3*pi/4. It is better to compute sin(x1),cos(x1) + * as follows: + * cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4) + * = 1/sqrt(2) * (sin(x) - cos(x)) + * sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) + * = -1/sqrt(2) * (sin(x) + cos(x)) + * (To avoid cancellation, use + * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + * to compute the worse one.) + * + * 3 Special cases + * j1(nan)= nan + * j1(0) = 0 + * j1(inf) = 0 + * + * Method -- y1(x): + * 1. screen out x<=0 cases: y1(0)=-inf, y1(x<0)=NaN + * 2. For x<2. + * Since + * y1(x) = 2/pi*(j1(x)*(ln(x/2)+Euler)-1/x-x/2+5/64*x^3-...) + * therefore y1(x)-2/pi*j1(x)*ln(x)-1/x is an odd function. + * We use the following function to approximate y1, + * y1(x) = x*U(z)/V(z) + (2/pi)*(j1(x)*ln(x)-1/x), z= x^2 + * where for x in [0,2] (abs err less than 2**-65.89) + * U(z) = u0 + u1*z + ... + u4*z^4 + * V(z) = 1 + v1*z + ... + v5*z^5 + * Note: For tiny x, 1/x dominate y1 and hence + * y1(tiny) = -2/pi/tiny, (choose tiny<2**-54) + * 3. For x>=2. + * y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x1)+q1(x)*cos(x1)) + * where x1 = x-3*pi/4. It is better to compute sin(x1),cos(x1) + * by method mentioned above. + */ + +#include +#include + +#if defined(vax) || defined(tahoe) +#define _IEEE 0 +#else +#define _IEEE 1 +#define infnan(x) (0.0) +#endif + +static double pone(), qone(); + +static double +huge = 1e300, +zero = 0.0, +one = 1.0, +invsqrtpi= 5.641895835477562869480794515607725858441e-0001, +tpi = 0.636619772367581343075535053490057448, + + /* R0/S0 on [0,2] */ +r00 = -6.250000000000000020842322918309200910191e-0002, +r01 = 1.407056669551897148204830386691427791200e-0003, +r02 = -1.599556310840356073980727783817809847071e-0005, +r03 = 4.967279996095844750387702652791615403527e-0008, +s01 = 1.915375995383634614394860200531091839635e-0002, +s02 = 1.859467855886309024045655476348872850396e-0004, +s03 = 1.177184640426236767593432585906758230822e-0006, +s04 = 5.046362570762170559046714468225101016915e-0009, +s05 = 1.235422744261379203512624973117299248281e-0011; + +#define two_129 6.80564733841876926e+038 /* 2^129 */ +#define two_m54 5.55111512312578270e-017 /* 2^-54 */ +double j1(x) + double x; +{ + double z, s,c,ss,cc,r,u,v,y; + y = fabs(x); + if (!finite(x)) /* Inf or NaN */ + if (_IEEE && x != x) + return(x); + else + return (copysign(x, zero)); + y = fabs(x); + if (y >= 2) /* |x| >= 2.0 */ + { + s = sin(y); + c = cos(y); + ss = -s-c; + cc = s-c; + if (y < .5*DBL_MAX) { /* make sure y+y not overflow */ + z = cos(y+y); + if ((s*c) two_129) /* x > 2^129 */ + z = (invsqrtpi*cc)/sqrt(y); + else +#endif /* defined(vax) || defined(tahoe) */ + { + u = pone(y); v = qone(y); + z = invsqrtpi*(u*cc-v*ss)/sqrt(y); + } + if (x < 0) return -z; + else return z; + } + if (y < 7.450580596923828125e-009) { /* |x|<2**-27 */ + if(huge+x>one) return 0.5*x;/* inexact if x!=0 necessary */ + } + z = x*x; + r = z*(r00+z*(r01+z*(r02+z*r03))); + s = one+z*(s01+z*(s02+z*(s03+z*(s04+z*s05)))); + r *= x; + return (x*0.5+r/s); +} + +static double u0[5] = { + -1.960570906462389484206891092512047539632e-0001, + 5.044387166398112572026169863174882070274e-0002, + -1.912568958757635383926261729464141209569e-0003, + 2.352526005616105109577368905595045204577e-0005, + -9.190991580398788465315411784276789663849e-0008, +}; +static double v0[5] = { + 1.991673182366499064031901734535479833387e-0002, + 2.025525810251351806268483867032781294682e-0004, + 1.356088010975162198085369545564475416398e-0006, + 6.227414523646214811803898435084697863445e-0009, + 1.665592462079920695971450872592458916421e-0011, +}; + +double y1(x) + double x; +{ + double z, s,c,ss,cc,u,v,j1(); + /* if Y1(NaN) is NaN, Y1(-inf) is NaN, Y1(inf) is 0 */ + if (!finite(x)) + if (!_IEEE) return (infnan(EDOM)); + else if (x < 0) + return(zero/zero); + else if (x > 0) + return (0); + else + return(x); + if (x <= 0) { + if (_IEEE && x == 0) return -one/zero; + else if(x == 0) return(infnan(-ERANGE)); + else if(_IEEE) return (zero/zero); + else return(infnan(EDOM)); + } + if (x >= 2) /* |x| >= 2.0 */ + { + s = sin(x); + c = cos(x); + ss = -s-c; + cc = s-c; + if (x < .5 * DBL_MAX) /* make sure x+x not overflow */ + { + z = cos(x+x); + if ((s*c)>zero) cc = z/ss; + else ss = z/cc; + } + /* y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x0)+q1(x)*cos(x0)) + * where x0 = x-3pi/4 + * Better formula: + * cos(x0) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4) + * = 1/sqrt(2) * (sin(x) - cos(x)) + * sin(x0) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) + * = -1/sqrt(2) * (cos(x) + sin(x)) + * To avoid cancellation, use + * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + * to compute the worse one. + */ + if (_IEEE && x>two_129) + z = (invsqrtpi*ss)/sqrt(x); + else { + u = pone(x); v = qone(x); + z = invsqrtpi*(u*ss+v*cc)/sqrt(x); + } + return z; + } + if (x <= two_m54) { /* x < 2**-54 */ + return (-tpi/x); + } + z = x*x; + u = u0[0]+z*(u0[1]+z*(u0[2]+z*(u0[3]+z*u0[4]))); + v = one+z*(v0[0]+z*(v0[1]+z*(v0[2]+z*(v0[3]+z*v0[4])))); + return (x*(u/v) + tpi*(j1(x)*log(x)-one/x)); +} + +/* For x >= 8, the asymptotic expansions of pone is + * 1 + 15/128 s^2 - 4725/2^15 s^4 - ..., where s = 1/x. + * We approximate pone by + * pone(x) = 1 + (R/S) + * where R = pr0 + pr1*s^2 + pr2*s^4 + ... + pr5*s^10 + * S = 1 + ps0*s^2 + ... + ps4*s^10 + * and + * | pone(x)-1-R/S | <= 2 ** ( -60.06) + */ + +static double pr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ + 0.0, + 1.171874999999886486643746274751925399540e-0001, + 1.323948065930735690925827997575471527252e+0001, + 4.120518543073785433325860184116512799375e+0002, + 3.874745389139605254931106878336700275601e+0003, + 7.914479540318917214253998253147871806507e+0003, +}; +static double ps8[5] = { + 1.142073703756784104235066368252692471887e+0002, + 3.650930834208534511135396060708677099382e+0003, + 3.695620602690334708579444954937638371808e+0004, + 9.760279359349508334916300080109196824151e+0004, + 3.080427206278887984185421142572315054499e+0004, +}; + +static double pr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ + 1.319905195562435287967533851581013807103e-0011, + 1.171874931906140985709584817065144884218e-0001, + 6.802751278684328781830052995333841452280e+0000, + 1.083081829901891089952869437126160568246e+0002, + 5.176361395331997166796512844100442096318e+0002, + 5.287152013633375676874794230748055786553e+0002, +}; +static double ps5[5] = { + 5.928059872211313557747989128353699746120e+0001, + 9.914014187336144114070148769222018425781e+0002, + 5.353266952914879348427003712029704477451e+0003, + 7.844690317495512717451367787640014588422e+0003, + 1.504046888103610723953792002716816255382e+0003, +}; + +static double pr3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */ + 3.025039161373736032825049903408701962756e-0009, + 1.171868655672535980750284752227495879921e-0001, + 3.932977500333156527232725812363183251138e+0000, + 3.511940355916369600741054592597098912682e+0001, + 9.105501107507812029367749771053045219094e+0001, + 4.855906851973649494139275085628195457113e+0001, +}; +static double ps3[5] = { + 3.479130950012515114598605916318694946754e+0001, + 3.367624587478257581844639171605788622549e+0002, + 1.046871399757751279180649307467612538415e+0003, + 8.908113463982564638443204408234739237639e+0002, + 1.037879324396392739952487012284401031859e+0002, +}; + +static double pr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ + 1.077108301068737449490056513753865482831e-0007, + 1.171762194626833490512746348050035171545e-0001, + 2.368514966676087902251125130227221462134e+0000, + 1.224261091482612280835153832574115951447e+0001, + 1.769397112716877301904532320376586509782e+0001, + 5.073523125888185399030700509321145995160e+0000, +}; +static double ps2[5] = { + 2.143648593638214170243114358933327983793e+0001, + 1.252902271684027493309211410842525120355e+0002, + 2.322764690571628159027850677565128301361e+0002, + 1.176793732871470939654351793502076106651e+0002, + 8.364638933716182492500902115164881195742e+0000, +}; + +static double pone(x) + double x; +{ + double *p,*q,z,r,s; + if (x >= 8.0) {p = pr8; q= ps8;} + else if (x >= 4.54545211791992188) {p = pr5; q= ps5;} + else if (x >= 2.85714149475097656) {p = pr3; q= ps3;} + else /* if (x >= 2.0) */ {p = pr2; q= ps2;} + z = one/(x*x); + r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))); + s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4])))); + return (one + r/s); +} + + +/* For x >= 8, the asymptotic expansions of qone is + * 3/8 s - 105/1024 s^3 - ..., where s = 1/x. + * We approximate pone by + * qone(x) = s*(0.375 + (R/S)) + * where R = qr1*s^2 + qr2*s^4 + ... + qr5*s^10 + * S = 1 + qs1*s^2 + ... + qs6*s^12 + * and + * | qone(x)/s -0.375-R/S | <= 2 ** ( -61.13) + */ + +static double qr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ + 0.0, + -1.025390624999927207385863635575804210817e-0001, + -1.627175345445899724355852152103771510209e+0001, + -7.596017225139501519843072766973047217159e+0002, + -1.184980667024295901645301570813228628541e+0004, + -4.843851242857503225866761992518949647041e+0004, +}; +static double qs8[6] = { + 1.613953697007229231029079421446916397904e+0002, + 7.825385999233484705298782500926834217525e+0003, + 1.338753362872495800748094112937868089032e+0005, + 7.196577236832409151461363171617204036929e+0005, + 6.666012326177764020898162762642290294625e+0005, + -2.944902643038346618211973470809456636830e+0005, +}; + +static double qr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ + -2.089799311417640889742251585097264715678e-0011, + -1.025390502413754195402736294609692303708e-0001, + -8.056448281239359746193011295417408828404e+0000, + -1.836696074748883785606784430098756513222e+0002, + -1.373193760655081612991329358017247355921e+0003, + -2.612444404532156676659706427295870995743e+0003, +}; +static double qs5[6] = { + 8.127655013843357670881559763225310973118e+0001, + 1.991798734604859732508048816860471197220e+0003, + 1.746848519249089131627491835267411777366e+0004, + 4.985142709103522808438758919150738000353e+0004, + 2.794807516389181249227113445299675335543e+0004, + -4.719183547951285076111596613593553911065e+0003, +}; + +static double qr3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */ + -5.078312264617665927595954813341838734288e-0009, + -1.025378298208370901410560259001035577681e-0001, + -4.610115811394734131557983832055607679242e+0000, + -5.784722165627836421815348508816936196402e+0001, + -2.282445407376317023842545937526967035712e+0002, + -2.192101284789093123936441805496580237676e+0002, +}; +static double qs3[6] = { + 4.766515503237295155392317984171640809318e+0001, + 6.738651126766996691330687210949984203167e+0002, + 3.380152866795263466426219644231687474174e+0003, + 5.547729097207227642358288160210745890345e+0003, + 1.903119193388108072238947732674639066045e+0003, + -1.352011914443073322978097159157678748982e+0002, +}; + +static double qr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ + -1.783817275109588656126772316921194887979e-0007, + -1.025170426079855506812435356168903694433e-0001, + -2.752205682781874520495702498875020485552e+0000, + -1.966361626437037351076756351268110418862e+0001, + -4.232531333728305108194363846333841480336e+0001, + -2.137192117037040574661406572497288723430e+0001, +}; +static double qs2[6] = { + 2.953336290605238495019307530224241335502e+0001, + 2.529815499821905343698811319455305266409e+0002, + 7.575028348686454070022561120722815892346e+0002, + 7.393932053204672479746835719678434981599e+0002, + 1.559490033366661142496448853793707126179e+0002, + -4.959498988226281813825263003231704397158e+0000, +}; + +static double qone(x) + double x; +{ + double *p,*q, s,r,z; + if (x >= 8.0) {p = qr8; q= qs8;} + else if (x >= 4.54545211791992188) {p = qr5; q= qs5;} + else if (x >= 2.85714149475097656) {p = qr3; q= qs3;} + else /* if (x >= 2.0) */ {p = qr2; q= qs2;} + z = one/(x*x); + r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))); + s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5]))))); + return (.375 + r/s)/x; +} diff --git a/lib/libm/common_source/jn.c b/lib/libm/common_source/jn.c new file mode 100644 index 0000000000..5b31378d6b --- /dev/null +++ b/lib/libm/common_source/jn.c @@ -0,0 +1,312 @@ +/*- + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)jn.c 5.5 (Berkeley) 12/16/92"; +#endif /* not lint */ + +/* + * 16 December 1992 + * Minor modifications by Peter McIlroy to adapt non-IEEE architecture. + */ + +/* + * ==================================================== + * Copyright (C) 1992 by Sun Microsystems, Inc. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + * ******************* WARNING ******************** + * This is an alpha version of SunPro's FDLIBM (Freely + * Distributable Math Library) for IEEE double precision + * arithmetic. FDLIBM is a basic math library written + * in C that runs on machines that conform to IEEE + * Standard 754/854. This alpha version is distributed + * for testing purpose. Those who use this software + * should report any bugs to + * + * fdlibm-comments@sunpro.eng.sun.com + * + * -- K.C. Ng, Oct 12, 1992 + * ************************************************ + */ + +/* + * jn(int n, double x), yn(int n, double x) + * floating point Bessel's function of the 1st and 2nd kind + * of order n + * + * Special cases: + * y0(0)=y1(0)=yn(n,0) = -inf with division by zero signal; + * y0(-ve)=y1(-ve)=yn(n,-ve) are NaN with invalid signal. + * Note 2. About jn(n,x), yn(n,x) + * For n=0, j0(x) is called, + * for n=1, j1(x) is called, + * for nx, a continued fraction approximation to + * j(n,x)/j(n-1,x) is evaluated and then backward + * recursion is used starting from a supposed value + * for j(n,x). The resulting value of j(0,x) is + * compared with the actual value to correct the + * supposed value of j(n,x). + * + * yn(n,x) is similar in all respects, except + * that forward recursion is used for all + * values of n>1. + * + */ + +#include +#include +#include + +#if defined(vax) || defined(tahoe) +#define _IEEE 0 +#else +#define _IEEE 1 +#define infnan(x) (0.0) +#endif + +extern double j0(),j1(),log(),fabs(),sqrt(),cos(),sin(),y0(),y1(); +static double +invsqrtpi= 5.641895835477562869480794515607725858441e-0001, +two = 2.0, +zero = 0.0, +one = 1.0; + +double jn(n,x) + int n; double x; +{ + int i, sgn; + double a, b, temp; + double z, w; + + /* J(-n,x) = (-1)^n * J(n, x), J(n, -x) = (-1)^n * J(n, x) + * Thus, J(-n,x) = J(n,-x) + */ + /* if J(n,NaN) is NaN */ + if (_IEEE && isnan(x)) return x+x; + if (n<0){ + n = -n; + x = -x; + } + if (n==0) return(j0(x)); + if (n==1) return(j1(x)); + sgn = (n&1)&(x < zero); /* even n -- 0, odd n -- sign(x) */ + x = fabs(x); + if (x == 0 || !finite (x)) /* if x is 0 or inf */ + b = zero; + else if ((double) n <= x) { + /* Safe to use J(n+1,x)=2n/x *J(n,x)-J(n-1,x) */ + if (_IEEE && x >= 8.148143905337944345e+090) { + /* x >= 2**302 */ + /* (x >> n**2) + * Jn(x) = cos(x-(2n+1)*pi/4)*sqrt(2/x*pi) + * Yn(x) = sin(x-(2n+1)*pi/4)*sqrt(2/x*pi) + * Let s=sin(x), c=cos(x), + * xn=x-(2n+1)*pi/4, sqt2 = sqrt(2),then + * + * n sin(xn)*sqt2 cos(xn)*sqt2 + * ---------------------------------- + * 0 s-c c+s + * 1 -s-c -c+s + * 2 -s+c -c-s + * 3 s+c c-s + */ + switch(n&3) { + case 0: temp = cos(x)+sin(x); break; + case 1: temp = -cos(x)+sin(x); break; + case 2: temp = -cos(x)-sin(x); break; + case 3: temp = cos(x)-sin(x); break; + } + b = invsqrtpi*temp/sqrt(x); + } else { + a = j0(x); + b = j1(x); + for(i=1;i 33) /* underflow */ + b = zero; + else { + temp = x*0.5; b = temp; + for (a=one,i=2;i<=n;i++) { + a *= (double)i; /* a = n! */ + b *= temp; /* b = (x/2)^n */ + } + b = b/a; + } + } else { + /* use backward recurrence */ + /* x x^2 x^2 + * J(n,x)/J(n-1,x) = ---- ------ ------ ..... + * 2n - 2(n+1) - 2(n+2) + * + * 1 1 1 + * (for large x) = ---- ------ ------ ..... + * 2n 2(n+1) 2(n+2) + * -- - ------ - ------ - + * x x x + * + * Let w = 2n/x and h=2/x, then the above quotient + * is equal to the continued fraction: + * 1 + * = ----------------------- + * 1 + * w - ----------------- + * 1 + * w+h - --------- + * w+2h - ... + * + * To determine how many terms needed, let + * Q(0) = w, Q(1) = w(w+h) - 1, + * Q(k) = (w+k*h)*Q(k-1) - Q(k-2), + * When Q(k) > 1e4 good for single + * When Q(k) > 1e9 good for double + * When Q(k) > 1e17 good for quadruple + */ + /* determine k */ + double t,v; + double q0,q1,h,tmp; int k,m; + w = (n+n)/(double)x; h = 2.0/(double)x; + q0 = w; z = w+h; q1 = w*z - 1.0; k=1; + while (q1<1.0e9) { + k += 1; z += h; + tmp = z*q1 - q0; + q0 = q1; + q1 = tmp; + } + m = n+n; + for(t=zero, i = 2*(n+k); i>=m; i -= 2) t = one/(i/x-t); + a = t; + b = one; + /* estimate log((2/x)^n*n!) = n*log(2/x)+n*ln(n) + * Hence, if n*(log(2n/x)) > ... + * single 8.8722839355e+01 + * double 7.09782712893383973096e+02 + * long double 1.1356523406294143949491931077970765006170e+04 + * then recurrent value may overflow and the result will + * likely underflow to zero + */ + tmp = n; + v = two/x; + tmp = tmp*log(fabs(v*tmp)); + for (i=n-1;i>0;i--){ + temp = b; + b = ((i+i)/x)*b - a; + a = temp; + /* scale b to avoid spurious overflow */ +# if defined(vax) || defined(tahoe) +# define BMAX 1e13 +# else +# define BMAX 1e100 +# endif /* defined(vax) || defined(tahoe) */ + if (b > BMAX) { + a /= b; + t /= b; + b = one; + } + } + b = (t*j0(x)/b); + } + } + return ((sgn == 1) ? -b : b); +} +double yn(n,x) + int n; double x; +{ + int i, sign; + double a, b, temp; + + /* Y(n,NaN), Y(n, x < 0) is NaN */ + if (x <= 0 || (_IEEE && x != x)) + if (_IEEE && x < 0) return zero/zero; + else if (x < 0) return (infnan(EDOM)); + else if (_IEEE) return -one/zero; + else return(infnan(-ERANGE)); + else if (!finite(x)) return(0); + sign = 1; + if (n<0){ + n = -n; + sign = 1 - ((n&1)<<2); + } + if (n == 0) return(y0(x)); + if (n == 1) return(sign*y1(x)); + if(_IEEE && x >= 8.148143905337944345e+090) { /* x > 2**302 */ + /* (x >> n**2) + * Jn(x) = cos(x-(2n+1)*pi/4)*sqrt(2/x*pi) + * Yn(x) = sin(x-(2n+1)*pi/4)*sqrt(2/x*pi) + * Let s=sin(x), c=cos(x), + * xn=x-(2n+1)*pi/4, sqt2 = sqrt(2),then + * + * n sin(xn)*sqt2 cos(xn)*sqt2 + * ---------------------------------- + * 0 s-c c+s + * 1 -s-c -c+s + * 2 -s+c -c-s + * 3 s+c c-s + */ + switch (n&3) { + case 0: temp = sin(x)-cos(x); break; + case 1: temp = -sin(x)-cos(x); break; + case 2: temp = -sin(x)+cos(x); break; + case 3: temp = sin(x)+cos(x); break; + } + b = invsqrtpi*temp/sqrt(x); + } else { + a = y0(x); + b = y1(x); + /* quit if b is -inf */ + for (i = 1; i < n && !finite(b); i++){ + temp = b; + b = ((double)(i+i)/x)*b - a; + a = temp; + } + } + if (!_IEEE && !finite(b)) + return (infnan(-sign * ERANGE)); + return ((sign > 0) ? b : -b); +} diff --git a/lib/libm/common_source/lgamma.3 b/lib/libm/common_source/lgamma.3 index 761769e258..5aaa1dffc0 100644 --- a/lib/libm/common_source/lgamma.3 +++ b/lib/libm/common_source/lgamma.3 @@ -29,41 +29,49 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)lgamma.3 6.4 (Berkeley) 5/6/91 +.\" @(#)lgamma.3 6.6 (Berkeley) 12/3/92 .\" -.Dd May 6, 1991 +.Dd December 3, 1992 .Dt LGAMMA 3 .Os BSD 4.3 .Sh NAME .Nm lgamma -.Nd log gamma function +.Nm gamma +.Nd log gamma function, gamma function .Sh SYNOPSIS .Fd #include +.Ft extern int +.Fa signgam ; +.sp .Ft double .Fn lgamma "double x" +.Ft double +.Fn gamma "double x" .Sh DESCRIPTION -.ta \w'Lgamma returns ln\||\(*G(x)| where'u+1n +1.7i +.Fn Lgamma x .if t \{\ -Lgamma returns ln\||\(*G(x)| where +returns ln\||\(*G(x)| where .Bd -unfilled -offset indent \(*G(x) = \(is\d\s8\z0\s10\u\u\s8\(if\s10\d t\u\s8x\-1\s10\d e\u\s8\-t\s10\d dt for x > 0 and .br -\(*G(x) = \(*p/(\(*G(1\-x)\|sin(\(*px)) for x < 1. \} +\(*G(x) = \(*p/(\(*G(1\-x)\|sin(\(*px)) for x < 1. .Ed +.\} .if n \ -Lgamma returns ln\||\(*G(x)|. -.ta +returns ln\||\(*G(x)|. .Pp The external integer .Fa signgam -returns the sign of -\(*G(x) . +returns the sign of \(*G(x). +.Pp +.Fn Gamma x +returns \(*G(x), with no effect on +.Fa signgam . .Sh IDIOSYNCRASIES -Do -.Em not -use the expression +Do not use the expression .Dq Li signgam\(**exp(lgamma(x)) -to compute g := \(*G(x). Instead use a program like this (in C): +to compute g := \(*G(x). +Instead use a program like this (in C): .Bd -literal -offset indent lg = lgamma(x); g = signgam\(**exp(lg); .Ed @@ -71,86 +79,45 @@ lg = lgamma(x); g = signgam\(**exp(lg); Only after .Fn lgamma has returned can signgam be correct. -Note too that \(*G(x) must overflow when x is large enough, -underflow when \-x is large enough, and spawn a division by zero -when x is a nonpositive integer. .Pp -Only in the -.Tn UNIX -math library for C was the name gamma ever attached -to ln\(*G. Elsewhere, for instance in -.Tn IBM Ns 's -.Tn FORTRAN -library, the name -.Tn GAMMA -belongs to \(*G and the name -.Tn ALGAMA -to ln\(*G in single precision; -in double the names are -.Tn DGAMMA +For arguments in its range, +.Fn gamma +is preferred, as for positive arguments +it is accurate to within one unit in the last place. +Exponentiation of +.Fn lgamma +will lose up to 10 significant bits. +.Sh RETURN VALUES +.Fn Gamma and -.Tn DLGAMA . -Why should C be different? -.Pp -Archaeological records suggest that C's -.Em gamma -originally delivered -ln(\(*G(|x|)). Later, the program gamma was changed to -cope with negative arguments x in a more conventional way, but -the documentation did not reflect that change correctly. The most -recent change corrects inaccurate values when x is almost a -negative integer, and lets \(*G(x) be computed without -conditional expressions. Programmers should not assume that .Fn lgamma -has settled down. -.Pp -At some time in the future, the name -.Em gamma -will be rehabilitated -and used for the gamma function, just as is done in -.Tn FORTRAN . -The reason for this is not so much compatibility with -.Tn FORTRAN -as a -desire to achieve greater speed for smaller values of |x| and greater -accuracy for larger values. -.Pp -Meanwhile, programmers who have to use the name -.Em gamma -in its former -sense, for what is now -.Fn lgamma , -have two choices: -.Bl -enum -width indent -.It -Use the old math library, -.Pa libom . -.It -Add the following program to your others: -.Bd -literal -offset indent -#include -double gamma(x) -double x; -{ - return (lgamma(x)); -} -.Ed -.Sh DIAGNOSTICS -The reserved operand is returned on a -.Tn VAX -for negative integer arguments, +return appropriate values unless an arguent is out of range. +Overflow will occur for sufficiently large positive values, and +non-positive integers. +On the +.Tn VAX, +the reserved operator is returned, +and .Va errno is set to -.Er ERANGE ; -for very large arguments over/underflows will -occur inside the -.Fn lgamma -routine. +.Er ERANGE +For large non-integer negative values, +.Fn gamma +will underflow. .Sh SEE ALSO .Xr math 3 , .Xr infnan 3 .Sh HISTORY The -.Nm +.Nm lgamma function appeared in .Bx 4.3 . +The +.Nm gamma +function appeared in +.Bx 4.4 . +The name +.Fn gamma +was originally dedicated to the +.Fn lgamma +function, so some old code may no longer be compatible. diff --git a/lib/libm/common_source/lgamma.c b/lib/libm/common_source/lgamma.c new file mode 100644 index 0000000000..d02603837c --- /dev/null +++ b/lib/libm/common_source/lgamma.c @@ -0,0 +1,308 @@ +/*- + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)lgamma.c 5.11 (Berkeley) 12/16/92"; +#endif /* not lint */ + +/* + * Coded by Peter McIlroy, Nov 1992; + * + * The financial support of UUNET Communications Services is greatfully + * acknowledged. + */ + +#include +#include + +#include "mathimpl.h" + +/* Log gamma function. + * Error: x > 0 error < 1.3ulp. + * x > 4, error < 1ulp. + * x > 9, error < .6ulp. + * x < 0, all bets are off. (When G(x) ~ 1, log(G(x)) ~ 0) + * Method: + * x > 6: + * Use the asymptotic expansion (Stirling's Formula) + * 0 < x < 6: + * Use gamma(x+1) = x*gamma(x) for argument reduction. + * Use rational approximation in + * the range 1.2, 2.5 + * Two approximations are used, one centered at the + * minimum to ensure monotonicity; one centered at 2 + * to maintain small relative error. + * x < 0: + * Use the reflection formula, + * G(1-x)G(x) = PI/sin(PI*x) + * Special values: + * non-positive integer returns +Inf. + * NaN returns NaN +*/ +static int endian; +#if defined(vax) || defined(tahoe) +#define _IEEE 0 +/* double and float have same size exponent field */ +#define TRUNC(x) x = (double) (float) (x) +#else +#define _IEEE 1 +#define TRUNC(x) *(((int *) &x) + endian) &= 0xf8000000 +#define infnan(x) 0.0 +#endif + +extern double log1p(double); +static double small_lgam(double); +static double large_lgam(double); +static double neg_lgam(double); +static double zero = 0.0, one = 1.0; +int signgam; + +#define UNDERFL (1e-1020 * 1e-1020) + +#define LEFT (1.0 - (x0 + .25)) +#define RIGHT (x0 - .218) +/* +/* Constants for approximation in [1.244,1.712] +*/ +#define x0 0.461632144968362356785 +#define x0_lo -.000000000000000015522348162858676890521 +#define a0_hi -0.12148629128932952880859 +#define a0_lo .0000000007534799204229502 +#define r0 -2.771227512955130520e-002 +#define r1 -2.980729795228150847e-001 +#define r2 -3.257411333183093394e-001 +#define r3 -1.126814387531706041e-001 +#define r4 -1.129130057170225562e-002 +#define r5 -2.259650588213369095e-005 +#define s0 1.714457160001714442e+000 +#define s1 2.786469504618194648e+000 +#define s2 1.564546365519179805e+000 +#define s3 3.485846389981109850e-001 +#define s4 2.467759345363656348e-002 +/* + * Constants for approximation in [1.71, 2.5] +*/ +#define a1_hi 4.227843350984671344505727574870e-01 +#define a1_lo 4.670126436531227189e-18 +#define p0 3.224670334241133695662995251041e-01 +#define p1 3.569659696950364669021382724168e-01 +#define p2 1.342918716072560025853732668111e-01 +#define p3 1.950702176409779831089963408886e-02 +#define p4 8.546740251667538090796227834289e-04 +#define q0 1.000000000000000444089209850062e+00 +#define q1 1.315850076960161985084596381057e+00 +#define q2 6.274644311862156431658377186977e-01 +#define q3 1.304706631926259297049597307705e-01 +#define q4 1.102815279606722369265536798366e-02 +#define q5 2.512690594856678929537585620579e-04 +#define q6 -1.003597548112371003358107325598e-06 +/* + * Stirling's Formula, adjusted for equal-ripple. x in [6,Inf]. +*/ +#define lns2pi .418938533204672741780329736405 +#define pb0 8.33333333333333148296162562474e-02 +#define pb1 -2.77777777774548123579378966497e-03 +#define pb2 7.93650778754435631476282786423e-04 +#define pb3 -5.95235082566672847950717262222e-04 +#define pb4 8.41428560346653702135821806252e-04 +#define pb5 -1.89773526463879200348872089421e-03 +#define pb6 5.69394463439411649408050664078e-03 +#define pb7 -1.44705562421428915453880392761e-02 + +double +lgamma(double x) +{ + double r; + + signgam = 1; + endian = ((*(int *) &one)) ? 1 : 0; + + if (!finite(x)) + if (_IEEE) + return (x+x); + else return (infnan(EDOM)); + + if (x > 6 + RIGHT) { + r = large_lgam(x); + return (r); + } else if (x > 1e-16) + return (small_lgam(x)); + else if (x > -1e-16) { + if (x < 0) + signgam = -1, x = -x; + return (-log(x)); + } else + return (neg_lgam(x)); +} + +static double +large_lgam(double x) +{ + double z, p, x1; + int i; + struct Double t, u, v; + u = log__D(x); + u.a -= 1.0; + if (x > 1e15) { + v.a = x - 0.5; + TRUNC(v.a); + v.b = (x - v.a) - 0.5; + t.a = u.a*v.a; + t.b = x*u.b + v.b*u.a; + if (_IEEE == 0 && !finite(t.a)) + return(infnan(ERANGE)); + return(t.a + t.b); + } + x1 = 1./x; + z = x1*x1; + p = pb0+z*(pb1+z*(pb2+z*(pb3+z*(pb4+z*(pb5+z*(pb6+z*pb7)))))); + /* error in approximation = 2.8e-19 */ + + p = p*x1; /* error < 2.3e-18 absolute */ + /* 0 < p < 1/64 (at x = 5.5) */ + v.a = x = x - 0.5; + TRUNC(v.a); /* truncate v.a to 26 bits. */ + v.b = x - v.a; + t.a = v.a*u.a; /* t = (x-.5)*(log(x)-1) */ + t.b = v.b*u.a + x*u.b; + t.b += p; t.b += lns2pi; /* return t + lns2pi + p */ + return (t.a + t.b); +} + +static double +small_lgam(double x) +{ + int x_int; + double y, z, t, r = 0, p, q, hi, lo; + struct Double rr; + x_int = (x + .5); + y = x - x_int; + if (x_int <= 2 && y > RIGHT) { + t = y - x0; + y--; x_int++; + goto CONTINUE; + } else if (y < -LEFT) { + t = y +(1.0-x0); +CONTINUE: + z = t - x0_lo; + p = r0+z*(r1+z*(r2+z*(r3+z*(r4+z*r5)))); + q = s0+z*(s1+z*(s2+z*(s3+z*s4))); + r = t*(z*(p/q) - x0_lo); + t = .5*t*t; + z = 1.0; + switch (x_int) { + case 6: z = (y + 5); + case 5: z *= (y + 4); + case 4: z *= (y + 3); + case 3: z *= (y + 2); + rr = log__D(z); + rr.b += a0_lo; rr.a += a0_hi; + return(((r+rr.b)+t+rr.a)); + case 2: return(((r+a0_lo)+t)+a0_hi); + case 0: r -= log1p(x); + default: rr = log__D(x); + rr.a -= a0_hi; rr.b -= a0_lo; + return(((r - rr.b) + t) - rr.a); + } + } else { + p = p0+y*(p1+y*(p2+y*(p3+y*p4))); + q = q0+y*(q1+y*(q2+y*(q3+y*(q4+y*(q5+y*q6))))); + p = p*(y/q); + t = (double)(float) y; + z = y-t; + hi = (double)(float) (p+a1_hi); + lo = a1_hi - hi; lo += p; lo += a1_lo; + r = lo*y + z*hi; /* q + r = y*(a0+p/q) */ + q = hi*t; + z = 1.0; + switch (x_int) { + case 6: z = (y + 5); + case 5: z *= (y + 4); + case 4: z *= (y + 3); + case 3: z *= (y + 2); + rr = log__D(z); + r += rr.b; r += q; + return(rr.a + r); + case 2: return (q+ r); + case 0: rr = log__D(x); + r -= rr.b; r -= log1p(x); + r += q; r-= rr.a; + return(r); + default: rr = log__D(x); + r -= rr.b; + q -= rr.a; + return (r+q); + } + } +} + +static double +neg_lgam(double x) +{ + int xi; + double y, z, one = 1.0, zero = 0.0; + extern double gamma(); + + /* avoid destructive cancellation as much as possible */ + if (x > -170) { + xi = x; + if (xi == x) + if (_IEEE) + return(one/zero); + else + return(infnan(ERANGE)); + y = gamma(x); + if (y < 0) + y = -y, signgam = -1; + return (log(y)); + } + z = floor(x + .5); + if (z == x) { /* convention: G(-(integer)) -> +Inf */ + if (_IEEE) + return (one/zero); + else + return (infnan(ERANGE)); + } + y = .5*ceil(x); + if (y == ceil(y)) + signgam = -1; + x = -x; + z = fabs(x + z); /* 0 < z <= .5 */ + if (z < .25) + z = sin(M_PI*z); + else + z = cos(M_PI*(0.5-z)); + z = log(M_PI/(z*x)); + y = large_lgam(x); + return (z - y); +} diff --git a/lib/libm/common_source/log.c b/lib/libm/common_source/log.c index d1dec5b27b..f565dcdd00 100644 --- a/lib/libm/common_source/log.c +++ b/lib/libm/common_source/log.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1985 Regents of the University of California. + * Copyright (c) 1992 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,132 +32,457 @@ */ #ifndef lint -static char sccsid[] = "@(#)log.c 5.6 (Berkeley) 10/9/90"; +static char sccsid[] = "@(#)log.c 5.10 (Berkeley) 1/10/93"; #endif /* not lint */ -/* LOG(X) - * RETURN THE LOGARITHM OF x - * DOUBLE PRECISION (VAX D FORMAT 56 bits or IEEE DOUBLE 53 BITS) - * CODED IN C BY K.C. NG, 1/19/85; - * REVISED BY K.C. NG on 2/7/85, 3/7/85, 3/24/85, 4/16/85. - * - * Required system supported functions: - * scalb(x,n) - * copysign(x,y) - * logb(x) - * finite(x) - * - * Required kernel function: - * log__L(z) +#include +#include + +#include "mathimpl.h" + +/* Table-driven natural logarithm. * - * Method : - * 1. Argument Reduction: find k and f such that - * x = 2^k * (1+f), - * where sqrt(2)/2 < 1+f < sqrt(2) . + * This code was derived, with minor modifications, from: + * Peter Tang, "Table-Driven Implementation of the + * Logarithm in IEEE Floating-Point arithmetic." ACM Trans. + * Math Software, vol 16. no 4, pp 378-400, Dec 1990). * - * 2. Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) - * = 2s + 2/3 s**3 + 2/5 s**5 + ....., - * log(1+f) is computed by + * Calculates log(2^m*F*(1+f/F)), |f/j| <= 1/256, + * where F = j/128 for j an integer in [0, 128]. * - * log(1+f) = 2s + s*log__L(s*s) - * where - * log__L(z) = z*(L1 + z*(L2 + z*(... (L6 + z*L7)...))) + * log(2^m) = log2_hi*m + log2_tail*m + * since m is an integer, the dominant term is exact. + * m has at most 10 digits (for subnormal numbers), + * and log2_hi has 11 trailing zero bits. * - * See log__L() for the values of the coefficients. + * log(F) = logF_hi[j] + logF_lo[j] is in tabular form in log_table.h + * logF_hi[] + 512 is exact. * - * 3. Finally, log(x) = k*ln2 + log(1+f). (Here n*ln2 will be stored - * in two floating point number: n*ln2hi + n*ln2lo, n*ln2hi is exact - * since the last 20 bits of ln2hi is 0.) + * log(1+f/F) = 2*f/(2*F + f) + 1/12 * (2*f/(2*F + f))**3 + ... + * the leading term is calculated to extra precision in two + * parts, the larger of which adds exactly to the dominant + * m and F terms. + * There are two cases: + * 1. when m, j are non-zero (m | j), use absolute + * precision for the leading term. + * 2. when m = j = 0, |1-x| < 1/256, and log(x) ~= (x-1). + * In this case, use a relative precision of 24 bits. + * (This is done differently in the original paper) * * Special cases: - * log(x) is NaN with signal if x < 0 (including -INF) ; - * log(+INF) is +INF; log(0) is -INF with signal; - * log(NaN) is that NaN with no signal. - * - * Accuracy: - * log(x) returns the exact log(x) nearly rounded. In a test run with - * 1,536,000 random arguments on a VAX, the maximum observed error was - * .826 ulps (units in the last place). - * - * Constants: - * The hexadecimal values are the intended ones for the following constants. - * The decimal values may be used, provided that the compiler will convert - * from decimal to binary accurately enough to produce the hexadecimal values - * shown. - */ + * 0 return signalling -Inf + * neg return signalling NaN + * +Inf return +Inf +*/ -#include -#include "mathimpl.h" +#if defined(vax) || defined(tahoe) +#define _IEEE 0 +#define TRUNC(x) x = (double) (float) (x) +#else +#define _IEEE 1 +#define endian (((*(int *) &one)) ? 1 : 0) +#define TRUNC(x) *(((int *) &x) + endian) &= 0xf8000000 +#define infnan(x) 0.0 +#endif -vc(ln2hi, 6.9314718055829871446E-1 ,7217,4031,0000,f7d0, 0, .B17217F7D00000) -vc(ln2lo, 1.6465949582897081279E-12 ,bcd5,2ce7,d9cc,e4f1, -39, .E7BCD5E4F1D9CC) -vc(sqrt2, 1.4142135623730950622E0 ,04f3,40b5,de65,33f9, 1, .B504F333F9DE65) +#define N 128 -ic(ln2hi, 6.9314718036912381649E-1, -1, 1.62E42FEE00000) -ic(ln2lo, 1.9082149292705877000E-10, -33, 1.A39EF35793C76) -ic(sqrt2, 1.4142135623730951455E0, 0, 1.6A09E667F3BCD) +/* Table of log(Fj) = logF_head[j] + logF_tail[j], for Fj = 1+j/128. + * Used for generation of extend precision logarithms. + * The constant 35184372088832 is 2^45, so the divide is exact. + * It ensures correct reading of logF_head, even for inaccurate + * decimal-to-binary conversion routines. (Everybody gets the + * right answer for integers less than 2^53.) + * Values for log(F) were generated using error < 10^-57 absolute + * with the bc -l package. +*/ +double A1 = .08333333333333178827; +double A2 = .01250000000377174923; +double A3 = .002232139987919447809; +double A4 = .0004348877777076145742; -#ifdef vccast -#define ln2hi vccast(ln2hi) -#define ln2lo vccast(ln2lo) -#define sqrt2 vccast(sqrt2) -#endif +double logF_head[N+1] = { + 0., + .007782140442060381246, + .015504186535963526694, + .023167059281547608406, + .030771658666765233647, + .038318864302141264488, + .045809536031242714670, + .053244514518837604555, + .060624621816486978786, + .067950661908525944454, + .075223421237524235039, + .082443669210988446138, + .089612158689760690322, + .096729626458454731618, + .103796793681567578460, + .110814366340264314203, + .117783035656430001836, + .124703478501032805070, + .131576357788617315236, + .138402322859292326029, + .145182009844575077295, + .151916042025732167530, + .158605030176659056451, + .165249572895390883786, + .171850256926518341060, + .178407657472689606947, + .184922338493834104156, + .191394852999565046047, + .197825743329758552135, + .204215541428766300668, + .210564769107350002741, + .216873938300523150246, + .223143551314024080056, + .229374101064877322642, + .235566071312860003672, + .241719936886966024758, + .247836163904594286577, + .253915209980732470285, + .259957524436686071567, + .265963548496984003577, + .271933715484010463114, + .277868451003087102435, + .283768173130738432519, + .289633292582948342896, + .295464212893421063199, + .301261330578199704177, + .307025035294827830512, + .312755710004239517729, + .318453731118097493890, + .324119468654316733591, + .329753286372579168528, + .335355541920762334484, + .340926586970454081892, + .346466767346100823488, + .351976423156884266063, + .357455888922231679316, + .362905493689140712376, + .368325561158599157352, + .373716409793814818840, + .379078352934811846353, + .384411698910298582632, + .389716751140440464951, + .394993808240542421117, + .400243164127459749579, + .405465108107819105498, + .410659924985338875558, + .415827895143593195825, + .420969294644237379543, + .426084395310681429691, + .431173464818130014464, + .436236766774527495726, + .441274560805140936281, + .446287102628048160113, + .451274644139630254358, + .456237433481874177232, + .461175715122408291790, + .466089729924533457960, + .470979715219073113985, + .475845904869856894947, + .480688529345570714212, + .485507815781602403149, + .490303988045525329653, + .495077266798034543171, + .499827869556611403822, + .504556010751912253908, + .509261901790523552335, + .513945751101346104405, + .518607764208354637958, + .523248143765158602036, + .527867089620485785417, + .532464798869114019908, + .537041465897345915436, + .541597282432121573947, + .546132437597407260909, + .550647117952394182793, + .555141507540611200965, + .559615787935399566777, + .564070138285387656651, + .568504735352689749561, + .572919753562018740922, + .577315365035246941260, + .581691739635061821900, + .586049045003164792433, + .590387446602107957005, + .594707107746216934174, + .599008189645246602594, + .603290851438941899687, + .607555250224322662688, + .611801541106615331955, + .616029877215623855590, + .620240409751204424537, + .624433288012369303032, + .628608659422752680256, + .632766669570628437213, + .636907462236194987781, + .641031179420679109171, + .645137961373620782978, + .649227946625615004450, + .653301272011958644725, + .657358072709030238911, + .661398482245203922502, + .665422632544505177065, + .669430653942981734871, + .673422675212350441142, + .677398823590920073911, + .681359224807238206267, + .685304003098281100392, + .689233281238557538017, + .693147180560117703862 +}; +double logF_tail[N+1] = { + 0., + -.00000000000000543229938420049, + .00000000000000172745674997061, + -.00000000000001323017818229233, + -.00000000000001154527628289872, + -.00000000000000466529469958300, + .00000000000005148849572685810, + -.00000000000002532168943117445, + -.00000000000005213620639136504, + -.00000000000001819506003016881, + .00000000000006329065958724544, + .00000000000008614512936087814, + -.00000000000007355770219435028, + .00000000000009638067658552277, + .00000000000007598636597194141, + .00000000000002579999128306990, + -.00000000000004654729747598444, + -.00000000000007556920687451336, + .00000000000010195735223708472, + -.00000000000017319034406422306, + -.00000000000007718001336828098, + .00000000000010980754099855238, + -.00000000000002047235780046195, + -.00000000000008372091099235912, + .00000000000014088127937111135, + .00000000000012869017157588257, + .00000000000017788850778198106, + .00000000000006440856150696891, + .00000000000016132822667240822, + -.00000000000007540916511956188, + -.00000000000000036507188831790, + .00000000000009120937249914984, + .00000000000018567570959796010, + -.00000000000003149265065191483, + -.00000000000009309459495196889, + .00000000000017914338601329117, + -.00000000000001302979717330866, + .00000000000023097385217586939, + .00000000000023999540484211737, + .00000000000015393776174455408, + -.00000000000036870428315837678, + .00000000000036920375082080089, + -.00000000000009383417223663699, + .00000000000009433398189512690, + .00000000000041481318704258568, + -.00000000000003792316480209314, + .00000000000008403156304792424, + -.00000000000034262934348285429, + .00000000000043712191957429145, + -.00000000000010475750058776541, + -.00000000000011118671389559323, + .00000000000037549577257259853, + .00000000000013912841212197565, + .00000000000010775743037572640, + .00000000000029391859187648000, + -.00000000000042790509060060774, + .00000000000022774076114039555, + .00000000000010849569622967912, + -.00000000000023073801945705758, + .00000000000015761203773969435, + .00000000000003345710269544082, + -.00000000000041525158063436123, + .00000000000032655698896907146, + -.00000000000044704265010452446, + .00000000000034527647952039772, + -.00000000000007048962392109746, + .00000000000011776978751369214, + -.00000000000010774341461609578, + .00000000000021863343293215910, + .00000000000024132639491333131, + .00000000000039057462209830700, + -.00000000000026570679203560751, + .00000000000037135141919592021, + -.00000000000017166921336082431, + -.00000000000028658285157914353, + -.00000000000023812542263446809, + .00000000000006576659768580062, + -.00000000000028210143846181267, + .00000000000010701931762114254, + .00000000000018119346366441110, + .00000000000009840465278232627, + -.00000000000033149150282752542, + -.00000000000018302857356041668, + -.00000000000016207400156744949, + .00000000000048303314949553201, + -.00000000000071560553172382115, + .00000000000088821239518571855, + -.00000000000030900580513238244, + -.00000000000061076551972851496, + .00000000000035659969663347830, + .00000000000035782396591276383, + -.00000000000046226087001544578, + .00000000000062279762917225156, + .00000000000072838947272065741, + .00000000000026809646615211673, + -.00000000000010960825046059278, + .00000000000002311949383800537, + -.00000000000058469058005299247, + -.00000000000002103748251144494, + -.00000000000023323182945587408, + -.00000000000042333694288141916, + -.00000000000043933937969737844, + .00000000000041341647073835565, + .00000000000006841763641591466, + .00000000000047585534004430641, + .00000000000083679678674757695, + -.00000000000085763734646658640, + .00000000000021913281229340092, + -.00000000000062242842536431148, + -.00000000000010983594325438430, + .00000000000065310431377633651, + -.00000000000047580199021710769, + -.00000000000037854251265457040, + .00000000000040939233218678664, + .00000000000087424383914858291, + .00000000000025218188456842882, + -.00000000000003608131360422557, + -.00000000000050518555924280902, + .00000000000078699403323355317, + -.00000000000067020876961949060, + .00000000000016108575753932458, + .00000000000058527188436251509, + -.00000000000035246757297904791, + -.00000000000018372084495629058, + .00000000000088606689813494916, + .00000000000066486268071468700, + .00000000000063831615170646519, + .00000000000025144230728376072, + -.00000000000017239444525614834 +}; -double log(x) -double x; +double +#ifdef _ANSI_SOURCE +log(double x) +#else +log(x) double x; +#endif { - const static double zero=0.0, negone= -1.0, half=1.0/2.0; - double s,z,t; - int k,n; - -#if !defined(vax)&&!defined(tahoe) - if(x!=x) return(x); /* x is NaN */ -#endif /* !defined(vax)&&!defined(tahoe) */ - if(finite(x)) { - if( x > zero ) { - - /* argument reduction */ - k=logb(x); x=scalb(x,-k); - if(k == -1022) /* subnormal no. */ - {n=logb(x); x=scalb(x,-n); k+=n;} - if(x >= sqrt2 ) {k += 1; x *= half;} - x += negone ; - - /* compute log(1+x) */ - s=x/(2+x); t=x*x*half; - z=k*ln2lo+s*(t+log__L(s*s)); - x += (z - t) ; - - return(k*ln2hi+x); - } - /* end of if (x > zero) */ - - else { -#if defined(vax)||defined(tahoe) - if ( x == zero ) - return (infnan(-ERANGE)); /* -INF */ + int m, j; + double F, f, g, q, u, u2, v, zero = 0.0, one = 1.0; + double logb(), ldexp(); + volatile double u1; + + /* Catch special cases */ + if (x <= 0) + if (_IEEE && x == zero) /* log(0) = -Inf */ + return (-one/zero); + else if (_IEEE) /* log(neg) = NaN */ + return (zero/zero); + else if (x == zero) /* NOT REACHED IF _IEEE */ + return (infnan(-ERANGE)); + else + return (infnan(EDOM)); + else if (!finite(x)) + if (_IEEE) /* x = NaN, Inf */ + return (x+x); else - return (infnan(EDOM)); /* NaN */ -#else /* defined(vax)||defined(tahoe) */ - /* zero argument, return -INF with signal */ - if ( x == zero ) - return( negone/zero ); - - /* negative argument, return NaN with signal */ - else - return ( zero / zero ); -#endif /* defined(vax)||defined(tahoe) */ - } + return (infnan(ERANGE)); + + /* Argument reduction: 1 <= g < 2; x/2^m = g; */ + /* y = F*(1 + f/F) for |f| <= 2^-8 */ + + m = logb(x); + g = ldexp(x, -m); + if (_IEEE && m == -1022) { + j = logb(g), m += j; + g = ldexp(g, -j); + } + j = N*(g-1) + .5; + F = (1.0/N) * j + 1; /* F*128 is an integer in [128, 512] */ + f = g - F; + + /* Approximate expansion for log(1+f/F) ~= u + q */ + g = 1/(2*F+f); + u = 2*f*g; + v = u*u; + q = u*v*(A1 + v*(A2 + v*(A3 + v*A4))); + + /* case 1: u1 = u rounded to 2^-43 absolute. Since u < 2^-8, + * u1 has at most 35 bits, and F*u1 is exact, as F has < 8 bits. + * It also adds exactly to |m*log2_hi + log_F_head[j] | < 750 + */ + if (m | j) + u1 = u + 513, u1 -= 513; + + /* case 2: |1-x| < 1/256. The m- and j- dependent terms are zero; + * u1 = u to 24 bits. + */ + else + u1 = u, TRUNC(u1); + u2 = (2.0*(f - F*u1) - u1*f) * g; + /* u1 + u2 = 2f/(2F+f) to extra precision. */ + + /* log(x) = log(2^m*F*(1+f/F)) = */ + /* (m*log2_hi+logF_head[j]+u1) + (m*log2_lo+logF_tail[j]+q); */ + /* (exact) + (tiny) */ + + u1 += m*logF_head[N] + logF_head[j]; /* exact */ + u2 = (u2 + logF_tail[j]) + q; /* tiny */ + u2 += logF_tail[N]*m; + return (u1 + u2); +} + +/* + * Extra precision variant, returning struct {double a, b;}; + * log(x) = a+b to 63 bits, with a is rounded to 26 bits. + */ +struct Double +#ifdef _ANSI_SOURCE +log__D(double x) +#else +log__D(x) double x; +#endif +{ + int m, j; + double F, f, g, q, u, v, u2, one = 1.0; + double logb(), ldexp(); + volatile double u1; + struct Double r; + + /* Argument reduction: 1 <= g < 2; x/2^m = g; */ + /* y = F*(1 + f/F) for |f| <= 2^-8 */ + + m = logb(x); + g = ldexp(x, -m); + if (_IEEE && m == -1022) { + j = logb(g), m += j; + g = ldexp(g, -j); } - /* end of if (finite(x)) */ - /* NOTREACHED if defined(vax)||defined(tahoe) */ + j = N*(g-1) + .5; + F = (1.0/N) * j + 1; + f = g - F; - /* log(-INF) is NaN with signal */ - else if (x<0) - return(zero/zero); + g = 1/(2*F+f); + u = 2*f*g; + v = u*u; + q = u*v*(A1 + v*(A2 + v*(A3 + v*A4))); + if (m | j) + u1 = u + 513, u1 -= 513; + else + u1 = u, TRUNC(u1); + u2 = (2.0*(f - F*u1) - u1*f) * g; - /* log(+INF) is +INF */ - else return(x); + u1 += m*logF_head[N] + logF_head[j]; + u2 += logF_tail[j]; u2 += q; + u2 += logF_tail[N]*m; + r.a = u1 + u2; /* Only difference is here */ + TRUNC(r.a); + r.b = (u1 - r.a) + u2; + return (r); } diff --git a/lib/libm/common_source/mathimpl.h b/lib/libm/common_source/mathimpl.h index 37f110039e..1037b99964 100644 --- a/lib/libm/common_source/mathimpl.h +++ b/lib/libm/common_source/mathimpl.h @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)mathimpl.h 5.4 (Berkeley) 3/5/91 + * @(#)mathimpl.h 5.5 (Berkeley) 12/2/92 */ #include @@ -93,3 +93,6 @@ extern double exp__E(); extern double log__L(); +struct Double {double a, b;}; +double exp__D __P((double, double)); +struct Double log__D __P((double)); diff --git a/lib/libm/common_source/pow.c b/lib/libm/common_source/pow.c index 0010447850..a10fcd7e71 100644 --- a/lib/libm/common_source/pow.c +++ b/lib/libm/common_source/pow.c @@ -32,7 +32,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)pow.c 5.7 (Berkeley) 10/9/90"; +static char sccsid[] = "@(#)pow.c 5.9 (Berkeley) 12/16/92"; #endif /* not lint */ /* POW(X,Y) @@ -40,7 +40,7 @@ static char sccsid[] = "@(#)pow.c 5.7 (Berkeley) 10/9/90"; * DOUBLE PRECISION (VAX D format 56 bits, IEEE DOUBLE 53 BITS) * CODED IN C BY K.C. NG, 1/8/85; * REVISED BY K.C. NG on 7/10/85. - * + * KERNEL pow_P() REPLACED BY P. McILROY 7/22/92. * Required system supported functions: * scalb(x,n) * logb(x) @@ -49,9 +49,8 @@ static char sccsid[] = "@(#)pow.c 5.7 (Berkeley) 10/9/90"; * drem(x,y) * * Required kernel functions: - * exp__E(a,c) ...return exp(a+c) - 1 - a*a/2 - * log__L(x) ...return (log(1+x) - 2s)/s, s=x/(2+x) - * pow_p(x,y) ...return +(anything)**(finite non zero) + * exp__D(a,c) exp(a + c) for |a| << |c| + * struct d_double dlog(x) r.a + r.b, |r.b| < |r.a| * * Method * 1. Compute and return log(x) in three pieces: @@ -69,10 +68,12 @@ static char sccsid[] = "@(#)pow.c 5.7 (Berkeley) 10/9/90"; * (anything) ** 1 is itself; * (anything) ** NaN is NaN; * NaN ** (anything except 0) is NaN; - * +-(anything > 1) ** +INF is +INF; + * +(anything > 1) ** +INF is +INF; + * -(anything > 1) ** +INF is NaN; * +-(anything > 1) ** -INF is +0; * +-(anything < 1) ** +INF is +0; - * +-(anything < 1) ** -INF is +INF; + * +(anything < 1) ** -INF is +INF; + * -(anything < 1) ** -INF is NaN; * +-1 ** +-INF is NaN and signal INVALID; * +0 ** +(anything except 0, NaN) is +0; * -0 ** +(anything except 0, NaN, odd integer) is +0; @@ -104,150 +105,99 @@ static char sccsid[] = "@(#)pow.c 5.7 (Berkeley) 10/9/90"; */ #include -#include -#include "mathimpl.h" - -vc(ln2hi, 6.9314718055829871446E-1 ,7217,4031,0000,f7d0, 0, .B17217F7D00000) -vc(ln2lo, 1.6465949582897081279E-12 ,bcd5,2ce7,d9cc,e4f1, -39, .E7BCD5E4F1D9CC) -vc(invln2, 1.4426950408889634148E0 ,aa3b,40b8,17f1,295c, 1, .B8AA3B295C17F1) -vc(sqrt2, 1.4142135623730950622E0 ,04f3,40b5,de65,33f9, 1, .B504F333F9DE65) +#include -ic(ln2hi, 6.9314718036912381649E-1, -1, 1.62E42FEE00000) -ic(ln2lo, 1.9082149292705877000E-10, -33, 1.A39EF35793C76) -ic(invln2, 1.4426950408889633870E0, 0, 1.71547652B82FE) -ic(sqrt2, 1.4142135623730951455E0, 0, 1.6A09E667F3BCD) +#include "mathimpl.h" -#ifdef vccast -#define ln2hi vccast(ln2hi) -#define ln2lo vccast(ln2lo) -#define invln2 vccast(invln2) -#define sqrt2 vccast(sqrt2) -#endif +#if (defined(vax) || defined(tahoe)) +#define TRUNC(x) x = (double) (float) x +#define _IEEE 0 +#else +#define _IEEE 1 +#define endian (((*(int *) &one)) ? 1 : 0) +#define TRUNC(x) *(((int *) &x)+endian) &= 0xf8000000 +#define infnan(x) 0.0 +#endif /* vax or tahoe */ -const static double zero=0.0, half=1.0/2.0, one=1.0, two=2.0, negone= -1.0; +const static double zero=0.0, one=1.0, two=2.0, negone= -1.0; -static double pow_p(double, double); +static double pow_P __P((double, double)); double pow(x,y) double x,y; { double t; - - if (y==zero) return(one); - else if(y==one -#if !defined(vax)&&!defined(tahoe) - ||x!=x -#endif /* !defined(vax)&&!defined(tahoe) */ - ) return( x ); /* if x is NaN or y=1 */ -#if !defined(vax)&&!defined(tahoe) - else if(y!=y) return( y ); /* if y is NaN */ -#endif /* !defined(vax)&&!defined(tahoe) */ - else if(!finite(y)) /* if y is INF */ - if((t=copysign(x,one))==one) return(zero/zero); - else if(t>one) return((y>zero)?y:zero); - else return((yone) + return ((y<0)? zero : ((x0)? zero : ((x<0)? y-y : -y)); + else if (y==two) + return (x*x); + else if (y==negone) + return (one/x); + /* x > 0, x == +0 */ + else if (copysign(one, x) == one) + return (pow_P(x, y)); /* sign(x)= -1 */ /* if y is an even integer */ - else if ( (t=drem(y,two)) == zero) return( pow_p(-x,y) ); + else if ( (t=drem(y,two)) == zero) + return (pow_P(-x, y)); /* if y is an odd integer */ - else if (copysign(t,one) == one) return( -pow_p(-x,y) ); + else if (copysign(t,one) == one) + return (-pow_P(-x, y)); /* Henceforth y is not an integer */ - else if(x==zero) /* x is -0 */ - return((y>zero)?-x:one/(-x)); - else { /* return NaN */ -#if defined(vax)||defined(tahoe) - return (infnan(EDOM)); /* NaN */ -#else /* defined(vax)||defined(tahoe) */ - return(zero/zero); -#endif /* defined(vax)||defined(tahoe) */ - } + else if (x==zero) /* x is -0 */ + return ((y>zero)? -x : one/(-x)); + else if (_IEEE) + return (zero/zero); + else + return (infnan(EDOM)); } - -#ifndef mc68881 -/* pow_p(x,y) return x**y for x with sign=1 and finite y */ -static double pow_p(x,y) -double x,y; +/* kernel function for x >= 0 */ +static double +#ifdef _ANSI_SOURCE +pow_P(double x, double y) +#else +pow_P(x, y) double x, y; +#endif { - double c,s,t,z,tx,ty; -#ifdef tahoe - double tahoe_tmp; -#endif /* tahoe */ - double errtmp; - float sx,sy; - long k=0; - int n,m; - - if(x==zero||!finite(x)) { /* if x is +INF or +0 */ -#if defined(vax)||defined(tahoe) - return((y>zero)?x:infnan(ERANGE)); /* if yzero)?x:one/x); -#endif /* defined(vax)||defined(tahoe) */ - } - if(x==1.0) return(x); /* if x=1.0, return 1 since y is finite */ - - /* reduce x to z in [sqrt(1/2)-1, sqrt(2)-1] */ - z=scalb(x,-(n=logb(x))); -#if !defined(vax)&&!defined(tahoe) /* IEEE double; subnormal number */ - if(n <= -1022) {n += (m=logb(z)); z=scalb(z,-m);} -#endif /* !defined(vax)&&!defined(tahoe) */ - if(z >= sqrt2 ) {n += 1; z *= half;} z -= one ; - - /* log(x) = nlog2+log(1+z) ~ nlog2 + t + tx */ - s=z/(two+z); c=z*z*half; tx=s*(c+log__L(s*s)); - t= z-(c-tx); tx += (z-t)-c; - - /* if y*log(x) is neither too big nor too small */ - if((s=logb(y)+logb(n+t)) < 12.0) - if(s>-60.0) { - - /* compute y*log(x) ~ mlog2 + t + c */ - s=y*(n+invln2*t); - m=s+copysign(half,s); /* m := nint(y*log(x)) */ - k=y; - if(y > (double)LONG_MIN && y < (double)LONG_MAX - && (double)(long)y==y) { /* y is an integer */ - k = m-(long)y*n; - sx=t; tx+=(t-sx); } - else { /* if y is not an integer */ - k =m; - tx+=n*ln2lo; - sx=(c=n*ln2hi)+t; tx+=(c-sx)+t; } - /* end of checking whether k==y */ - - sy=y; ty=y-sy; /* y ~ sy + ty */ -#ifdef tahoe - s = (tahoe_tmp = sx)*sy-k*ln2hi; -#else /* tahoe */ - s=(double)sx*sy-k*ln2hi; /* (sy+ty)*(sx+tx)-kln2 */ -#endif /* tahoe */ - z=(tx*ty-k*ln2lo); - tx=tx*sy; ty=sx*ty; - t=ty+z; t+=tx; t+=s; - c= -((((t-s)-tx)-ty)-z); - - /* return exp(y*log(x)) */ - t += exp__E(t,c); return(scalb(one+t,m)); - } - /* end of if log(y*log(x)) > -60.0 */ - - else - /* exp(+- tiny) = 1 with inexact flag */ - {errtmp=ln2hi+ln2lo; return(one);} - else if(copysign(one,y)*(n+invln2*t) zero)? x : one/x); + if (x == 1) + return (one); + if (y >= 7e18) /* infinity */ + if (x < 1) + return(tiny*tiny); + else if (_IEEE) + return (huge*huge); + else + return (infnan(ERANGE)); + + /* Return exp(y*log(x)), using simulated extended */ + /* precision for the log and the multiply. */ + + s = log__D(x); + t.a = y; + TRUNC(t.a); + t.b = y - t.a; + t.b = s.b*y + t.b*s.a; + t.a *= s.a; + s.a = t.a + t.b; + s.b = (t.a - s.a) + t.b; + return (exp__D(s.a, s.b)); } -#endif /* mc68881 */ diff --git a/lib/librpc/Makefile b/lib/librpc/Makefile index bafff291eb..00a53b7664 100644 --- a/lib/librpc/Makefile +++ b/lib/librpc/Makefile @@ -1,30 +1,3 @@ -# -# @(#)Makefile 2.1 88/08/11 4.0 RPCSRC -# -# Build and install everything. -# -# These directories are presumed to exist in DESTDIR: -# /usr/lib /usr/bin /usr/include -# -DESTDIR= -CFLAGS=-O -MAKE=make - -# These are not used by BSD except portmap which lives in -# /usr/src/usr.sbin/portmap. -# SUBDIR= rpcgen etc rpcsvc - -all install: rpclib ${SUBDIR} - -rpclib: FRC - @echo "Building and installing RPC library" - cd rpc; $(MAKE) ${MFLAGS} all DESTDIR=${DESTDIR}; \ - $(MAKE) ${MFLAGS} install DESTDIR=${DESTDIR} - -${SUBDIR}: FRC - @echo "Building and installing files from: $@" - cd $@; $(MAKE) ${MFLAGS} DESTDIR=${DESTDIR} CFLAGS=${CFLAGS};\ - $(MAKE) ${MFLAGS} install DESTDIR=${DESTDIR} - -FRC: +SUBDIR= man rpc +.include diff --git a/lib/librpc/man/Makefile b/lib/librpc/man/Makefile new file mode 100644 index 0000000000..54b2154778 --- /dev/null +++ b/lib/librpc/man/Makefile @@ -0,0 +1,3 @@ +SUBDIR=man1 man3 man5 man8 + +.include diff --git a/lib/librpc/man/man1/Makefile b/lib/librpc/man/man1/Makefile new file mode 100644 index 0000000000..5f09977a8a --- /dev/null +++ b/lib/librpc/man/man1/Makefile @@ -0,0 +1,3 @@ +MAN1= rpcgen.1 rstat.1 + +.include diff --git a/lib/librpc/man/man3/Makefile b/lib/librpc/man/man3/Makefile new file mode 100644 index 0000000000..85ab536e06 --- /dev/null +++ b/lib/librpc/man/man3/Makefile @@ -0,0 +1,3 @@ +MAN3= bindresvport.3n getrpcent.3n getrpcport.3r rpc.3n xdr.3n + +.include diff --git a/lib/librpc/man/man5/Makefile b/lib/librpc/man/man5/Makefile new file mode 100644 index 0000000000..5d07719edc --- /dev/null +++ b/lib/librpc/man/man5/Makefile @@ -0,0 +1,3 @@ +MAN5= rpc.5 + +.include diff --git a/lib/librpc/man/man8/Makefile b/lib/librpc/man/man8/Makefile new file mode 100644 index 0000000000..bb0dacbd93 --- /dev/null +++ b/lib/librpc/man/man8/Makefile @@ -0,0 +1,3 @@ +MAN8= portmap.8c rpcinfo.8c rstat_svc.8c + +.include diff --git a/lib/librpc/rpc/Makefile b/lib/librpc/rpc/Makefile index 2821b46c09..160cbbe9dd 100644 --- a/lib/librpc/rpc/Makefile +++ b/lib/librpc/rpc/Makefile @@ -19,6 +19,11 @@ HDRS= auth.h auth_unix.h clnt.h pmap_clnt.h \ all: librpc.a beforeinstall: ${HDRS} + @-if [ ! -d ${DESTDIR}/usr/include/rpc ]; then \ + mkdir ${DESTDIR}/usr/include/rpc; \ + chown ${BINOWN}.${BINGRP} ${DESTDIR}/usr/include/rpc; \ + chmod 755 ${DESTDIR}/usr/include/rpc; \ + fi cd ${.CURDIR}; install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${HDRS} \ ${DESTDIR}/usr/include/rpc diff --git a/lib/librpc/rpc/auth.h b/lib/librpc/rpc/auth.h index c94505d45a..e9131c4709 100644 --- a/lib/librpc/rpc/auth.h +++ b/lib/librpc/rpc/auth.h @@ -1,4 +1,3 @@ -/* @(#)auth.h 2.3 88/08/07 4.0 RPCSRC; from 1.17 88/02/08 SMI */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -26,6 +25,10 @@ * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 + * + * from: @(#)auth.h 1.17 88/02/08 SMI + * from: @(#)auth.h 2.3 88/08/07 4.0 RPCSRC + * $Id: auth.h,v 1.3 1993/08/01 18:46:00 mycroft Exp $ */ /* @@ -38,6 +41,9 @@ * "sessions". */ +#ifndef _RPC_AUTH_H +#define _RPC_AUTH_H +#include #define MAX_AUTH_BYTES 400 #define MAXNETNAMELEN 255 /* maximum length of network user's name */ @@ -74,7 +80,9 @@ union des_block { char c[8]; }; typedef union des_block des_block; -extern bool_t xdr_des_block(); +__BEGIN_DECLS +extern bool_t xdr_des_block __P((XDR *, des_block *)); +__END_DECLS /* * Authentication info. Opaque to client. @@ -154,13 +162,17 @@ extern struct opaque_auth _null_auth; * int len; * int *aup_gids; */ -extern AUTH *authunix_create(); -extern AUTH *authunix_create_default(); /* takes no parameters */ -extern AUTH *authnone_create(); /* takes no parameters */ +__BEGIN_DECLS +extern AUTH *authunix_create __P((char *, int, int, int, int *)); +extern AUTH *authunix_create_default __P((void)); +extern AUTH *authnone_create __P((void)); extern AUTH *authdes_create(); +__END_DECLS #define AUTH_NONE 0 /* no authentication */ #define AUTH_NULL 0 /* backward compatibility */ #define AUTH_UNIX 1 /* unix style (uid, gids) */ #define AUTH_SHORT 2 /* short hand unix style */ #define AUTH_DES 3 /* des style (encrypted timestamps) */ + +#endif /* !_RPC_AUTH_H */ diff --git a/lib/librpc/rpc/auth_none.c b/lib/librpc/rpc/auth_none.c index 630037fb47..b07bec5338 100644 --- a/lib/librpc/rpc/auth_none.c +++ b/lib/librpc/rpc/auth_none.c @@ -1,4 +1,3 @@ -/* @(#)auth_none.c 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)auth_none.c 1.19 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)auth_none.c 1.19 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)auth_none.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: auth_none.c,v 1.3 1993/08/26 00:53:10 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/auth_unix.c b/lib/librpc/rpc/auth_unix.c index 87ff2b648b..59156b7d33 100644 --- a/lib/librpc/rpc/auth_unix.c +++ b/lib/librpc/rpc/auth_unix.c @@ -1,4 +1,3 @@ -/* @(#)auth_unix.c 2.2 88/08/01 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)auth_unix.c 1.19 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)auth_unix.c 1.19 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)auth_unix.c 2.2 88/08/01 4.0 RPCSRC";*/ +static char *rcsid = "$Id: auth_unix.c,v 1.4 1993/08/26 00:53:11 jtc Exp $"; #endif /* @@ -44,6 +46,7 @@ static char sccsid[] = "@(#)auth_unix.c 1.19 87/08/11 Copyr 1984 Sun Micro"; */ #include +#include #include #include diff --git a/lib/librpc/rpc/auth_unix.h b/lib/librpc/rpc/auth_unix.h index 705741e139..ed15ed8111 100644 --- a/lib/librpc/rpc/auth_unix.h +++ b/lib/librpc/rpc/auth_unix.h @@ -1,4 +1,3 @@ -/* @(#)auth_unix.h 2.2 88/07/29 4.0 RPCSRC; from 1.8 88/02/08 SMI */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -26,8 +25,11 @@ * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 + * + * from: @(#)auth_unix.h 1.8 88/02/08 SMI + * from: @(#)auth_unix.h 2.2 88/07/29 4.0 RPCSRC + * $Id: auth_unix.h,v 1.3 1993/08/01 18:46:00 mycroft Exp $ */ -/* @(#)auth_unix.h 1.5 86/07/16 SMI */ /* * auth_unix.h, Protocol for UNIX style authentication parameters for RPC @@ -42,6 +44,10 @@ * for the credentials. */ +#ifndef _RPC_AUTH_UNIX_H +#define _RPC_AUTH_UNIX_H +#include + /* The machine name is part of a credential; it may not exceed 255 bytes */ #define MAX_MACHINE_NAME 255 @@ -60,7 +66,9 @@ struct authunix_parms { int *aup_gids; }; -extern bool_t xdr_authunix_parms(); +__BEGIN_DECLS +extern bool_t xdr_authunix_parms __P((XDR *, struct authunix_parms *)); +__END_DECLS /* * If a response verifier has flavor AUTH_SHORT, @@ -70,3 +78,5 @@ extern bool_t xdr_authunix_parms(); struct short_hand_verf { struct opaque_auth new_cred; }; + +#endif /* !_RPC_AUTH_UNIX_H */ diff --git a/lib/librpc/rpc/authunix_prot.c b/lib/librpc/rpc/authunix_prot.c index a60d99a57b..d25d6b14b5 100644 --- a/lib/librpc/rpc/authunix_prot.c +++ b/lib/librpc/rpc/authunix_prot.c @@ -1,4 +1,3 @@ -/* @(#)authunix_prot.c 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)authunix_prot.c 1.15 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)authunix_prot.c 1.15 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)authunix_prot.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: authunix_prot.c,v 1.3 1993/08/26 00:53:13 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/bindresvport.c b/lib/librpc/rpc/bindresvport.c index 63a68d36e1..f3093a8e8f 100644 --- a/lib/librpc/rpc/bindresvport.c +++ b/lib/librpc/rpc/bindresvport.c @@ -1,4 +1,3 @@ -static char sccsid[] = "@(#)bindresvport.c 2.2 88/07/29 4.0 RPCSRC 1.8 88/02/08 SMI"; /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -28,6 +27,12 @@ static char sccsid[] = "@(#)bindresvport.c 2.2 88/07/29 4.0 RPCSRC 1.8 88/02/08 * Mountain View, California 94043 */ +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)bindresvport.c 1.8 88/02/08 SMI";*/ +/*static char *sccsid = "from: @(#)bindresvport.c 2.2 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: bindresvport.c,v 1.3 1993/08/26 00:53:14 jtc Exp $"; +#endif + /* * Copyright (c) 1987 by Sun Microsystems, Inc. */ diff --git a/lib/librpc/rpc/clnt.h b/lib/librpc/rpc/clnt.h index 8c002a19fa..fef717c243 100644 --- a/lib/librpc/rpc/clnt.h +++ b/lib/librpc/rpc/clnt.h @@ -1,4 +1,3 @@ -/* @(#)clnt.h 2.1 88/07/29 4.0 RPCSRC; from 1.31 88/02/08 SMI*/ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -26,6 +25,10 @@ * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 + * + * from: @(#)clnt.h 1.31 88/02/08 SMI + * from: @(#)clnt.h 2.1 88/07/29 4.0 RPCSRC + * $Id: clnt.h,v 1.3 1993/08/01 18:45:58 mycroft Exp $ */ /* @@ -34,8 +37,9 @@ * Copyright (C) 1984, Sun Microsystems, Inc. */ -#ifndef _CLNT_ -#define _CLNT_ +#ifndef _RPC_CLNT_H_ +#define _RPC_CLNT_H_ +#include /* * Rpc calls return an enum clnt_stat. This should be looked at more, @@ -235,21 +239,23 @@ typedef struct { * u_long prog; * u_long vers; */ -extern CLIENT *clntraw_create(); +__BEGIN_DECLS +extern CLIENT *clntraw_create __P((u_long, u_long)); +__END_DECLS /* * Generic client creation routine. Supported protocols are "udp" and "tcp" + * CLIENT * + * clnt_create(host, prog, vers, prot); + * char *host; -- hostname + * u_long prog; -- program number + * u_long vers; -- version number + * char *prot; -- protocol */ -extern CLIENT * -clnt_create(/*host, prog, vers, prot*/); /* - char *host; -- hostname - u_long prog; -- program number - u_long vers; -- version number - char *prot; -- protocol -*/ - - +__BEGIN_DECLS +extern CLIENT *clnt_create __P((char *, u_long, u_long, char *)); +__END_DECLS /* @@ -263,7 +269,15 @@ clnt_create(/*host, prog, vers, prot*/); /* * u_int sendsz; * u_int recvsz; */ -extern CLIENT *clnttcp_create(); +__BEGIN_DECLS +extern CLIENT *clnttcp_create __P((struct sockaddr_in *, + u_long, + u_long, + int *, + u_int, + u_int)); +__END_DECLS + /* * UDP based rpc. @@ -286,25 +300,46 @@ extern CLIENT *clnttcp_create(); * u_int sendsz; * u_int recvsz; */ -extern CLIENT *clntudp_create(); -extern CLIENT *clntudp_bufcreate(); +__BEGIN_DECLS +extern CLIENT *clntudp_create __P((struct sockaddr_in *, + u_long, + u_long, + struct timeval, + int *)); +extern CLIENT *clntudp_bufcreate __P((struct sockaddr_in *, + u_long, + u_long, + struct timeval, + int *, + u_int, + u_int)); +__END_DECLS + /* * Print why creation failed */ -void clnt_pcreateerror(/* char *msg */); /* stderr */ -char *clnt_spcreateerror(/* char *msg */); /* string */ +__BEGIN_DECLS +extern void clnt_pcreateerror __P((char *)); /* stderr */ +extern char *clnt_spcreateerror __P((char *)); /* string */ +__END_DECLS /* * Like clnt_perror(), but is more verbose in its output */ -void clnt_perrno(/* enum clnt_stat num */); /* stderr */ +__BEGIN_DECLS +extern void clnt_perrno __P((enum clnt_stat)); /* stderr */ +extern char *clnt_sperrno __P((enum clnt_stat)); /* string */ +__END_DECLS /* * Print an English error message, given the client error code */ -void clnt_perror(/* CLIENT *clnt, char *msg */); /* stderr */ -char *clnt_sperror(/* CLIENT *clnt, char *msg */); /* string */ +__BEGIN_DECLS +extern void clnt_perror __P((CLIENT *, char *)); /* stderr */ +extern char *clnt_sperror __P((CLIENT *, char *)); /* string */ +__END_DECLS + /* * If a creation fails, the following allows the user to figure out why. @@ -317,15 +352,7 @@ struct rpc_createerr { extern struct rpc_createerr rpc_createerr; - -/* - * Copy error message to buffer. - */ -char *clnt_sperrno(/* enum clnt_stat num */); /* string */ - - - #define UDPMSGSIZE 8800 /* rpc imposed limit on udp msg size */ #define RPCSMALLMSGSIZE 400 /* a more reasonable packet size */ -#endif /*!_CLNT_*/ +#endif /* !_RPC_CLNT_H */ diff --git a/lib/librpc/rpc/clnt_generic.c b/lib/librpc/rpc/clnt_generic.c index e54e77828b..3689350c6e 100644 --- a/lib/librpc/rpc/clnt_generic.c +++ b/lib/librpc/rpc/clnt_generic.c @@ -1,4 +1,3 @@ -/* @(#)clnt_generic.c 2.2 88/08/01 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,9 +26,13 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)clnt_generic.c 1.4 87/08/11 (C) 1987 SMI"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)clnt_generic.c 1.4 87/08/11 (C) 1987 SMI";*/ +/*static char *sccsid = "from: @(#)clnt_generic.c 2.2 88/08/01 4.0 RPCSRC";*/ +static char *rcsid = "$Id: clnt_generic.c,v 1.4 1993/08/26 00:53:15 jtc Exp $"; #endif + /* * Copyright (C) 1987, Sun Microsystems, Inc. */ @@ -46,8 +49,8 @@ static char sccsid[] = "@(#)clnt_generic.c 1.4 87/08/11 (C) 1987 SMI"; CLIENT * clnt_create(hostname, prog, vers, proto) char *hostname; - unsigned prog; - unsigned vers; + u_long prog; + u_long vers; char *proto; { struct hostent *h; diff --git a/lib/librpc/rpc/clnt_perror.c b/lib/librpc/rpc/clnt_perror.c index c618c5f3e9..303923ae14 100644 --- a/lib/librpc/rpc/clnt_perror.c +++ b/lib/librpc/rpc/clnt_perror.c @@ -1,4 +1,3 @@ -/* @(#)clnt_perror.c 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)clnt_perror.c 1.15 87/10/07 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)clnt_perror.c 1.15 87/10/07 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)clnt_perror.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: clnt_perror.c,v 1.4 1993/08/26 00:53:16 jtc Exp $"; #endif /* @@ -38,15 +40,14 @@ static char sccsid[] = "@(#)clnt_perror.c 1.15 87/10/07 Copyr 1984 Sun Micro"; * */ #include - +#include +#include #include #include #include static char *auth_errmsg(); -extern char *strcpy(); - static char *buf; static char * diff --git a/lib/librpc/rpc/clnt_raw.c b/lib/librpc/rpc/clnt_raw.c index 89059ae2da..bb042e25ee 100644 --- a/lib/librpc/rpc/clnt_raw.c +++ b/lib/librpc/rpc/clnt_raw.c @@ -1,4 +1,3 @@ -/* @(#)clnt_raw.c 2.2 88/08/01 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)clnt_raw.c 1.22 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)clnt_raw.c 1.22 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)clnt_raw.c 2.2 88/08/01 4.0 RPCSRC";*/ +static char *rcsid = "$Id: clnt_raw.c,v 1.3 1993/08/26 00:53:17 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/clnt_simple.c b/lib/librpc/rpc/clnt_simple.c index 043ce0a3eb..baff3727be 100644 --- a/lib/librpc/rpc/clnt_simple.c +++ b/lib/librpc/rpc/clnt_simple.c @@ -1,4 +1,3 @@ -/* @(#)clnt_simple.c 2.2 88/08/01 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)clnt_simple.c 1.35 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)clnt_simple.c 1.35 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)clnt_simple.c 2.2 88/08/01 4.0 RPCSRC";*/ +static char *rcsid = "$Id: clnt_simple.c,v 1.4 1993/08/26 00:53:19 jtc Exp $"; #endif /* @@ -39,10 +41,11 @@ static char sccsid[] = "@(#)clnt_simple.c 1.35 87/08/11 Copyr 1984 Sun Micro"; */ #include +#include +#include #include #include #include -#include static struct callrpc_private { CLIENT *client; diff --git a/lib/librpc/rpc/clnt_tcp.c b/lib/librpc/rpc/clnt_tcp.c index 2222bc6577..602a37223c 100644 --- a/lib/librpc/rpc/clnt_tcp.c +++ b/lib/librpc/rpc/clnt_tcp.c @@ -1,4 +1,3 @@ -/* @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC";*/ +static char *rcsid = "$Id: clnt_tcp.c,v 1.3 1993/08/26 00:53:20 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/clnt_udp.c b/lib/librpc/rpc/clnt_udp.c index 815cbb4ed2..9bbaa711de 100644 --- a/lib/librpc/rpc/clnt_udp.c +++ b/lib/librpc/rpc/clnt_udp.c @@ -1,4 +1,3 @@ -/* @(#)clnt_udp.c 2.2 88/08/01 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)clnt_udp.c 2.2 88/08/01 4.0 RPCSRC";*/ +static char *rcsid = "$Id: clnt_udp.c,v 1.3 1993/08/26 00:53:21 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/get_myaddress.c b/lib/librpc/rpc/get_myaddress.c index 60b12272c4..409191430f 100644 --- a/lib/librpc/rpc/get_myaddress.c +++ b/lib/librpc/rpc/get_myaddress.c @@ -1,4 +1,3 @@ -/* @(#)get_myaddress.c 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)get_myaddress.c 1.4 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)get_myaddress.c 1.4 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)get_myaddress.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: get_myaddress.c,v 1.4 1993/08/26 00:53:22 jtc Exp $"; #endif /* @@ -39,6 +41,7 @@ static char sccsid[] = "@(#)get_myaddress.c 1.4 87/08/11 Copyr 1984 Sun Micro"; */ #include +#include #include #include #include diff --git a/lib/librpc/rpc/getrpcent.c b/lib/librpc/rpc/getrpcent.c index a69dc1f24d..5252f2a8c1 100644 --- a/lib/librpc/rpc/getrpcent.c +++ b/lib/librpc/rpc/getrpcent.c @@ -1,66 +1,75 @@ -/* @(#)getrpcent.c 2.2 88/07/29 4.0 RPCSRC */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)getrpcent.c 1.9 87/08/11 Copyr 1984 Sun Micro"; -#endif - /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or - * program developed by the user. - * + * program developed by the user or with the express written consent of + * Sun Microsystems, Inc. + * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. - * + * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. - * + * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. - * + * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. - * + * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)getrpcent.c 1.14 91/03/11 Copyr 1984 Sun Micro";*/ +static char *rcsid = "$Id: getrpcent.c,v 1.7 1993/08/26 00:53:23 jtc Exp $"; +#endif + /* - * Copyright (c) 1985 by Sun Microsystems, Inc. + * Copyright (c) 1984 by Sun Microsystems, Inc. */ #include #include +#include #include -#include -#include +#ifdef YP +#include +#include +#endif /* * Internet version. */ struct rpcdata { FILE *rpcf; - char *current; - int currentlen; int stayopen; #define MAXALIASES 35 char *rpc_aliases[MAXALIASES]; struct rpcent rpc; char line[BUFSIZ+1]; +#ifdef YP char *domain; -} *rpcdata, *_rpcdata(); + char *current; + int currentlen; +#endif +} *rpcdata; + +#ifdef YP +static int __yp_nomap = 0; +#endif /* YP */ static struct rpcent *interpret(); struct hostent *gethostent(); char *inet_ntoa(); -static char *index(); static char RPCDB[] = "/etc/rpc"; @@ -82,12 +91,36 @@ getrpcbynumber(number) { register struct rpcdata *d = _rpcdata(); register struct rpcent *p; +#ifdef YP int reason; - char adrstr[16], *val = NULL; - int vallen; + char adrstr[16]; +#endif if (d == 0) return (0); +#ifdef YP + if (!__yp_nomap && _yp_check(&d->domain)) { + sprintf(adrstr, "%d", number); + reason = yp_match(d->domain, "rpc.bynumber", adrstr, strlen(adrstr), + &d->current, &d->currentlen); + switch(reason) { + case 0: + break; + case YPERR_MAP: + __yp_nomap = 1; + goto no_yp; + break; + default: + return(0); + break; + } + d->current[d->currentlen] = '\0'; + p = interpret(d->current, d->currentlen); + (void) free(d->current); + return p; + } +no_yp: +#endif /* YP */ setrpcent(0); while (p = getrpcent()) { if (p->r_number == number) @@ -105,7 +138,7 @@ getrpcbyname(name) char **rp; setrpcent(0); - while(rpc = getrpcent()) { + while (rpc = getrpcent()) { if (strcmp(rpc->r_name, name) == 0) return (rpc); for (rp = rpc->r_aliases; *rp != NULL; rp++) { @@ -117,6 +150,7 @@ getrpcbyname(name) return (NULL); } +void setrpcent(f) int f; { @@ -124,26 +158,40 @@ setrpcent(f) if (d == 0) return; +#ifdef YP + if (!__yp_nomap && _yp_check(NULL)) { + if (d->current) + free(d->current); + d->current = NULL; + d->currentlen = 0; + return; + } + __yp_nomap = 0; +#endif /* YP */ if (d->rpcf == NULL) d->rpcf = fopen(RPCDB, "r"); else rewind(d->rpcf); - if (d->current) - free(d->current); - d->current = NULL; d->stayopen |= f; } +void endrpcent() { register struct rpcdata *d = _rpcdata(); if (d == 0) return; - if (d->current && !d->stayopen) { - free(d->current); - d->current = NULL; - } +#ifdef YP + if (!__yp_nomap && _yp_check(NULL)) { + if (d->current && !d->stayopen) + free(d->current); + d->current = NULL; + d->currentlen = 0; + return; + } + __yp_nomap = 0; +#endif /* YP */ if (d->rpcf && !d->stayopen) { fclose(d->rpcf); d->rpcf = NULL; @@ -155,48 +203,74 @@ getrpcent() { struct rpcent *hp; int reason; + register struct rpcdata *d = _rpcdata(); +#ifdef YP char *key = NULL, *val = NULL; int keylen, vallen; - register struct rpcdata *d = _rpcdata(); +#endif if (d == 0) return(NULL); +#ifdef YP + if (!__yp_nomap && _yp_check(&d->domain)) { + if (d->current == NULL && d->currentlen == 0) { + reason = yp_first(d->domain, "rpc.bynumber", + &d->current, &d->currentlen, + &val, &vallen); + } else { + reason = yp_next(d->domain, "rpc.bynumber", + d->current, d->currentlen, + &d->current, &d->currentlen, + &val, &vallen); + } + switch(reason) { + case 0: + break; + case YPERR_MAP: + __yp_nomap = 1; + goto no_yp; + break; + default: + return(0); + break; + } + val[vallen] = '\0'; + hp = interpret(val, vallen); + (void) free(val); + return hp; + } +no_yp: +#endif /* YP */ if (d->rpcf == NULL && (d->rpcf = fopen(RPCDB, "r")) == NULL) return (NULL); - if (fgets(d->line, BUFSIZ, d->rpcf) == NULL) + if (fgets(d->line, BUFSIZ, d->rpcf) == NULL) return (NULL); - return interpret(d->line, strlen(d->line)); + return (interpret(d->line, strlen(d->line))); } static struct rpcent * interpret(val, len) + char *val; + int len; { register struct rpcdata *d = _rpcdata(); char *p; register char *cp, **q; if (d == 0) - return; - strncpy(d->line, val, len); + return (0); + (void) strncpy(d->line, val, len); p = d->line; d->line[len] = '\n'; if (*p == '#') return (getrpcent()); - cp = index(p, '#'); + cp = strpbrk(p, "#\n"); if (cp == NULL) - { - cp = index(p, '\n'); - if (cp == NULL) - return (getrpcent()); - } + return (getrpcent()); *cp = '\0'; - cp = index(p, ' '); + cp = strpbrk(p, " \t"); if (cp == NULL) - { - cp = index(p, '\t'); - if (cp == NULL) - return (getrpcent()); - } + return (getrpcent()); *cp++ = '\0'; /* THIS STUFF IS INTERNET SPECIFIC */ d->rpc.r_name = d->line; @@ -204,15 +278,9 @@ interpret(val, len) cp++; d->rpc.r_number = atoi(cp); q = d->rpc.r_aliases = d->rpc_aliases; - cp = index(p, ' '); - if (cp != NULL) + cp = strpbrk(cp, " \t"); + if (cp != NULL) *cp++ = '\0'; - else - { - cp = index(p, '\t'); - if (cp != NULL) - *cp++ = '\0'; - } while (cp && *cp) { if (*cp == ' ' || *cp == '\t') { cp++; @@ -220,16 +288,11 @@ interpret(val, len) } if (q < &(d->rpc_aliases[MAXALIASES - 1])) *q++ = cp; - cp = index(p, ' '); + cp = strpbrk(cp, " \t"); if (cp != NULL) *cp++ = '\0'; - else - { - cp = index(p, '\t'); - if (cp != NULL) - *cp++ = '\0'; - } } *q = NULL; return (&d->rpc); } + diff --git a/lib/librpc/rpc/getrpcport.c b/lib/librpc/rpc/getrpcport.c index 9b13bac6b0..755c61747c 100644 --- a/lib/librpc/rpc/getrpcport.c +++ b/lib/librpc/rpc/getrpcport.c @@ -1,7 +1,3 @@ -/* @(#)getrpcport.c 2.1 88/07/29 4.0 RPCSRC */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)getrpcport.c 1.3 87/08/11 SMI"; -#endif /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -31,6 +27,12 @@ static char sccsid[] = "@(#)getrpcport.c 1.3 87/08/11 SMI"; * Mountain View, California 94043 */ +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)getrpcport.c 1.3 87/08/11 SMI";*/ +/*static char *sccsid = "from: @(#)getrpcport.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: getrpcport.c,v 1.3 1993/08/26 00:53:24 jtc Exp $"; +#endif + /* * Copyright (c) 1985 by Sun Microsystems, Inc. */ diff --git a/lib/librpc/rpc/pmap_clnt.c b/lib/librpc/rpc/pmap_clnt.c index 09220e77b1..de36b21785 100644 --- a/lib/librpc/rpc/pmap_clnt.c +++ b/lib/librpc/rpc/pmap_clnt.c @@ -1,4 +1,3 @@ -/* @(#)pmap_clnt.c 2.2 88/08/01 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)pmap_clnt.c 1.37 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)pmap_clnt.c 1.37 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)pmap_clnt.c 2.2 88/08/01 4.0 RPCSRC";*/ +static char *rcsid = "$Id: pmap_clnt.c,v 1.3 1993/08/26 00:53:25 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/pmap_clnt.h b/lib/librpc/rpc/pmap_clnt.h index d2ea2a88e9..896412dca7 100644 --- a/lib/librpc/rpc/pmap_clnt.h +++ b/lib/librpc/rpc/pmap_clnt.h @@ -1,4 +1,3 @@ -/* @(#)pmap_clnt.h 2.1 88/07/29 4.0 RPCSRC; from 1.11 88/02/08 SMI */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -26,6 +25,10 @@ * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 + * + * from: @(#)pmap_clnt.h 1.11 88/02/08 SMI + * from: @(#)pmap_clnt.h 2.1 88/07/29 4.0 RPCSRC + * $Id: pmap_clnt.h,v 1.3 1993/08/01 18:45:57 mycroft Exp $ */ /* @@ -57,9 +60,25 @@ * address if the responder to the broadcast. */ -extern bool_t pmap_set(); -extern bool_t pmap_unset(); -extern struct pmaplist *pmap_getmaps(); -enum clnt_stat pmap_rmtcall(); -enum clnt_stat clnt_broadcast(); -extern u_short pmap_getport(); +#ifndef _RPC_PMAPCLNT_H +#define _RPC_PMAPCLNT_H +#include + +__BEGIN_DECLS +extern bool_t pmap_set __P((u_long, u_long, int, int)); +extern bool_t pmap_unset __P((u_long, u_long)); +extern struct pmaplist *pmap_getmaps __P((struct sockaddr_in *)); +extern enum clnt_stat pmap_rmtcall __P((struct sockaddr_in *, + u_long, u_long, u_long, + xdrproc_t, caddr_t, + xdrproc_t, caddr_t, + struct timeval, u_long *)); +extern enum clnt_stat clnt_broadcast __P((u_long, u_long, u_long, + xdrproc_t, char *, + xdrproc_t, char *, + bool_t (*)())); +extern u_short pmap_getport __P((struct sockaddr_in *, + u_long, u_long, u_int)); +__END_DECLS + +#endif /* !_RPC_PMAPCLNT_H */ diff --git a/lib/librpc/rpc/pmap_getmaps.c b/lib/librpc/rpc/pmap_getmaps.c index e4a9c49361..4c172e62a6 100644 --- a/lib/librpc/rpc/pmap_getmaps.c +++ b/lib/librpc/rpc/pmap_getmaps.c @@ -1,4 +1,3 @@ -/* @(#)pmap_getmaps.c 2.2 88/08/01 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)pmap_getmaps.c 1.10 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)pmap_getmaps.c 1.10 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)pmap_getmaps.c 2.2 88/08/01 4.0 RPCSRC";*/ +static char *rcsid = "$Id: pmap_getmaps.c,v 1.3 1993/08/26 00:53:27 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/pmap_getport.c b/lib/librpc/rpc/pmap_getport.c index 77b9cf743a..c8b3d166ca 100644 --- a/lib/librpc/rpc/pmap_getport.c +++ b/lib/librpc/rpc/pmap_getport.c @@ -1,4 +1,3 @@ -/* @(#)pmap_getport.c 2.2 88/08/01 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)pmap_getport.c 1.9 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)pmap_getport.c 1.9 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)pmap_getport.c 2.2 88/08/01 4.0 RPCSRC";*/ +static char *rcsid = "$Id: pmap_getport.c,v 1.3 1993/08/26 00:53:28 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/pmap_prot.c b/lib/librpc/rpc/pmap_prot.c index 643c2ff6a2..e38d35a685 100644 --- a/lib/librpc/rpc/pmap_prot.c +++ b/lib/librpc/rpc/pmap_prot.c @@ -1,4 +1,3 @@ -/* @(#)pmap_prot.c 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)pmap_prot.c 1.17 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)pmap_prot.c 1.17 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)pmap_prot.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: pmap_prot.c,v 1.3 1993/08/26 00:53:29 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/pmap_prot.h b/lib/librpc/rpc/pmap_prot.h index ccf7a77b41..5b84a4ba0b 100644 --- a/lib/librpc/rpc/pmap_prot.h +++ b/lib/librpc/rpc/pmap_prot.h @@ -1,4 +1,3 @@ -/* @(#)pmap_prot.h 2.1 88/07/29 4.0 RPCSRC; from 1.14 88/02/08 SMI */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -26,6 +25,10 @@ * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 + * + * from: @(#)pmap_prot.h 1.14 88/02/08 SMI + * from: @(#)pmap_prot.h 2.1 88/07/29 4.0 RPCSRC + * $Id: pmap_prot.h,v 1.3 1993/08/01 18:45:55 mycroft Exp $ */ /* @@ -65,6 +68,10 @@ * The service supports remote procedure calls on udp/ip or tcp/ip socket 111. */ +#ifndef _RPC_PMAPPROT_H +#define _RPC_PMAPPROT_H +#include + #define PMAPPORT ((u_short)111) #define PMAPPROG ((u_long)100000) #define PMAPVERS ((u_long)2) @@ -84,11 +91,14 @@ struct pmap { long unsigned pm_port; }; -extern bool_t xdr_pmap(); - struct pmaplist { struct pmap pml_map; struct pmaplist *pml_next; }; -extern bool_t xdr_pmaplist(); +__BEGIN_DECLS +extern bool_t xdr_pmap __P((XDR *, struct pmap *)); +extern bool_t xdr_pmaplist __P((XDR *, struct pmaplist **)); +__END_DECLS + +#endif /* !_RPC_PMAPPROT_H */ diff --git a/lib/librpc/rpc/pmap_prot2.c b/lib/librpc/rpc/pmap_prot2.c index e2a8214d48..dcdce8e1a2 100644 --- a/lib/librpc/rpc/pmap_prot2.c +++ b/lib/librpc/rpc/pmap_prot2.c @@ -1,4 +1,3 @@ -/* @(#)pmap_prot2.c 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)pmap_prot2.c 1.3 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)pmap_prot2.c 1.3 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)pmap_prot2.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: pmap_prot2.c,v 1.3 1993/08/26 00:53:30 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/pmap_rmt.c b/lib/librpc/rpc/pmap_rmt.c index 8945b2fb01..599dc8e0b3 100644 --- a/lib/librpc/rpc/pmap_rmt.c +++ b/lib/librpc/rpc/pmap_rmt.c @@ -1,4 +1,3 @@ -/* @(#)pmap_rmt.c 2.2 88/08/01 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)pmap_rmt.c 1.21 87/08/27 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)pmap_rmt.c 1.21 87/08/27 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)pmap_rmt.c 2.2 88/08/01 4.0 RPCSRC";*/ +static char *rcsid = "$Id: pmap_rmt.c,v 1.3 1993/08/26 00:53:32 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/pmap_rmt.h b/lib/librpc/rpc/pmap_rmt.h index ee68cebec2..d377d29374 100644 --- a/lib/librpc/rpc/pmap_rmt.h +++ b/lib/librpc/rpc/pmap_rmt.h @@ -1,4 +1,3 @@ -/* @(#)pmap_rmt.h 2.1 88/07/29 4.0 RPCSRC; from 1.2 88/02/08 SMI */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -26,6 +25,10 @@ * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 + * + * from: @(#)pmap_rmt.h 1.2 88/02/08 SMI + * from: @(#)pmap_rmt.h 2.1 88/07/29 4.0 RPCSRC + * $Id: pmap_rmt.h,v 1.3 1993/08/01 18:45:59 mycroft Exp $ */ /* @@ -35,14 +38,16 @@ * Copyright (C) 1986, Sun Microsystems, Inc. */ +#ifndef _RPC_PMAPRMT_H +#define _RPC_PMAPRMT_H +#include + struct rmtcallargs { u_long prog, vers, proc, arglen; caddr_t args_ptr; xdrproc_t xdr_args; }; -bool_t xdr_rmtcall_args(); - struct rmtcallres { u_long *port_ptr; u_long resultslen; @@ -50,4 +55,9 @@ struct rmtcallres { xdrproc_t xdr_results; }; -bool_t xdr_rmtcallres(); +__BEGIN_DECLS +extern bool_t xdr_rmtcall_args __P((XDR *, struct rmtcallargs *)); +extern bool_t xdr_rmtcallres __P((XDR *, struct rmtcallres *)); +__END_DECLS + +#endif /* !_RPC_PMAPRMT_H */ diff --git a/lib/librpc/rpc/rpc.h b/lib/librpc/rpc/rpc.h index cd403b7351..9efed3573b 100644 --- a/lib/librpc/rpc/rpc.h +++ b/lib/librpc/rpc/rpc.h @@ -1,4 +1,3 @@ -/* @(#)rpc.h 2.4 89/07/11 4.0 RPCSRC; from 1.9 88/02/08 SMI */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -26,6 +25,10 @@ * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 + * + * from: @(#)rpc.h 1.9 88/02/08 SMI + * from: @(#)rpc.h 2.4 89/07/11 4.0 RPCSRC + * $Id: rpc.h,v 1.3 1993/08/01 18:45:54 mycroft Exp $ */ /* @@ -34,8 +37,8 @@ * * Copyright (C) 1984, Sun Microsystems, Inc. */ -#ifndef __RPC_HEADER__ -#define __RPC_HEADER__ +#ifndef _RPC_RPC_H +#define _RPC_RPC_H #include /* some typedefs */ #include @@ -75,6 +78,12 @@ struct rpcent { int r_number; /* rpc program number */ }; -struct rpcent *getrpcbyname(), *getrpcbynumber(), *getrpcent(); +__BEGIN_DECLS +extern struct rpcent *getrpcbyname __P((char *)); +extern struct rpcent *getrpcbynumber __P((int)); +extern struct rpcent *getrpcent __P((void)); +extern void setrpcent __P((int)); +extern void endrpcent __P((void)); +__END_DECLS -#endif /* ndef __RPC_HEADER__ */ +#endif /* !_RPC_RPC_H */ diff --git a/lib/librpc/rpc/rpc.order b/lib/librpc/rpc/rpc.order deleted file mode 100644 index 9e31cd988a..0000000000 --- a/lib/librpc/rpc/rpc.order +++ /dev/null @@ -1,37 +0,0 @@ -xdr_stdio.o -svc_tcp.o -svc_simple.o -svc_run.o -svc_raw.o -rpc_commondata.o -pmap_rmt.o -pmap_getmaps.o -getrpcport.o -getrpcent.o -clnt_simple.o -clnt_raw.o -clnt_generic.o -auth_unix.o -svc_udp.o -svc.o -rpc_callmsg.o -pmap_prot2.o -pmap_clnt.o -get_myaddress.o -clnt_tcp.o -clnt_perror.o -xdr_reference.o -xdr_rec.o -svc_auth.o -svc_auth_unix.o -authunix_prot.o -xdr_array.o -pmap_getport.o -pmap_prot.o -clnt_udp.o -bindresvport.o -auth_none.o -xdr_mem.o -rpc_prot.o -rpc_dtablesize.o -xdr.o diff --git a/lib/librpc/rpc/rpc_callmsg.c b/lib/librpc/rpc/rpc_callmsg.c index d9d815a6fb..509a32e0ff 100644 --- a/lib/librpc/rpc/rpc_callmsg.c +++ b/lib/librpc/rpc/rpc_callmsg.c @@ -1,4 +1,3 @@ -/* @(#)rpc_callmsg.c 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)rpc_callmsg.c 1.4 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)rpc_callmsg.c 1.4 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)rpc_callmsg.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: rpc_callmsg.c,v 1.3 1993/08/26 00:53:33 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/rpc_commondata.c b/lib/librpc/rpc/rpc_commondata.c index 75cead0875..212b7f7a02 100644 --- a/lib/librpc/rpc/rpc_commondata.c +++ b/lib/librpc/rpc/rpc_commondata.c @@ -1,4 +1,3 @@ -/* @(#)rpc_commondata.c 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,6 +26,12 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)rpc_commondata.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: rpc_commondata.c,v 1.3 1993/08/26 00:53:34 jtc Exp $"; +#endif + #include /* * This file should only contain common data (global data) that is exported diff --git a/lib/librpc/rpc/rpc_dtablesize.c b/lib/librpc/rpc/rpc_dtablesize.c index a8488172e4..982f2e39e9 100644 --- a/lib/librpc/rpc/rpc_dtablesize.c +++ b/lib/librpc/rpc/rpc_dtablesize.c @@ -1,4 +1,3 @@ -/* @(#)rpc_dtablesize.c 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)rpc_dtablesize.c 1.2 87/08/11 Copyr 1987 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)rpc_dtablesize.c 1.2 87/08/11 Copyr 1987 Sun Micro";*/ +/*static char *sccsid = "from: @(#)rpc_dtablesize.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: rpc_dtablesize.c,v 1.3 1993/08/26 00:53:35 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/rpc_msg.h b/lib/librpc/rpc/rpc_msg.h index b78872b6a8..9a789a17cc 100644 --- a/lib/librpc/rpc/rpc_msg.h +++ b/lib/librpc/rpc/rpc_msg.h @@ -1,4 +1,3 @@ -/* @(#)rpc_msg.h 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -26,8 +25,11 @@ * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 + * + * from: @(#)rpc_msg.h 1.7 86/07/16 SMI + * from: @(#)rpc_msg.h 2.1 88/07/29 4.0 RPCSRC + * $Id: rpc_msg.h,v 1.3 1993/08/01 18:45:53 mycroft Exp $ */ -/* @(#)rpc_msg.h 1.7 86/07/16 SMI */ /* * rpc_msg.h @@ -36,6 +38,9 @@ * Copyright (C) 1984, Sun Microsystems, Inc. */ +#ifndef _RPC_RPCMSG_H +#define _RPC_RPCMSG_H + #define RPC_MSG_VERSION ((u_long) 2) #define RPC_SERVICE_PORT ((u_short) 2048) @@ -153,14 +158,14 @@ struct rpc_msg { #define acpted_rply ru.RM_rmb.ru.RP_ar #define rjcted_rply ru.RM_rmb.ru.RP_dr - +__BEGIN_DECLS /* * XDR routine to handle a rpc message. * xdr_callmsg(xdrs, cmsg) * XDR *xdrs; * struct rpc_msg *cmsg; */ -extern bool_t xdr_callmsg(); +extern bool_t xdr_callmsg __P((XDR *, struct rpc_msg *)); /* * XDR routine to pre-serialize the static part of a rpc message. @@ -168,7 +173,7 @@ extern bool_t xdr_callmsg(); * XDR *xdrs; * struct rpc_msg *cmsg; */ -extern bool_t xdr_callhdr(); +extern bool_t xdr_callhdr __P((XDR *, struct rpc_msg *)); /* * XDR routine to handle a rpc reply. @@ -176,7 +181,7 @@ extern bool_t xdr_callhdr(); * XDR *xdrs; * struct rpc_msg *rmsg; */ -extern bool_t xdr_replymsg(); +extern bool_t xdr_replymsg __P((XDR *, struct rpc_msg *)); /* * Fills in the error part of a reply message. @@ -184,4 +189,7 @@ extern bool_t xdr_replymsg(); * struct rpc_msg *msg; * struct rpc_err *error; */ -extern void _seterr_reply(); +extern void _seterr_reply __P((struct rpc_msg *, struct rpc_err *)); +__END_DECLS + +#endif /* !_RPC_RPCMSG_H */ diff --git a/lib/librpc/rpc/rpc_prot.c b/lib/librpc/rpc/rpc_prot.c index 4b1319ad56..646d638c99 100644 --- a/lib/librpc/rpc/rpc_prot.c +++ b/lib/librpc/rpc/rpc_prot.c @@ -1,4 +1,3 @@ -/* @(#)rpc_prot.c 2.3 88/08/07 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)rpc_prot.c 1.36 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)rpc_prot.c 1.36 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)rpc_prot.c 2.3 88/08/07 4.0 RPCSRC";*/ +static char *rcsid = "$Id: rpc_prot.c,v 1.3 1993/08/26 00:53:36 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/svc.c b/lib/librpc/rpc/svc.c index 3327ee5bdd..1a2cb79664 100644 --- a/lib/librpc/rpc/svc.c +++ b/lib/librpc/rpc/svc.c @@ -1,4 +1,3 @@ -/* @(#)svc.c 2.4 88/08/11 4.0 RPCSRC; from 1.44 88/02/08 SMI */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)svc.c 1.41 87/10/13 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)svc.c 1.44 88/02/08 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)svc.c 2.4 88/08/11 4.0 RPCSRC";*/ +static char *rcsid = "$Id: svc.c,v 1.3 1993/08/26 00:53:37 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/svc.h b/lib/librpc/rpc/svc.h index 3cb07ef3f3..243d49cf6f 100644 --- a/lib/librpc/rpc/svc.h +++ b/lib/librpc/rpc/svc.h @@ -1,4 +1,3 @@ -/* @(#)svc.h 2.2 88/07/29 4.0 RPCSRC; from 1.20 88/02/08 SMI */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -26,6 +25,10 @@ * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 + * + * from: @(#)svc.h 1.20 88/02/08 SMI + * from: @(#)svc.h 2.2 88/07/29 4.0 RPCSRC + * $Id: svc.h,v 1.3 1993/08/01 18:45:52 mycroft Exp $ */ /* @@ -34,8 +37,9 @@ * Copyright (C) 1984, Sun Microsystems, Inc. */ -#ifndef __SVC_HEADER__ -#define __SVC_HEADER__ +#ifndef _RPC_SVC_H +#define _RPC_SVC_H +#include /* * This interface must manage two items concerning remote procedure calling: @@ -153,7 +157,9 @@ struct svc_req { * void (*dispatch)(); * int protocol; /* like TCP or UDP, zero means do not register */ -extern bool_t svc_register(); +__BEGIN_DECLS +extern bool_t svc_register __P((SVCXPRT *, u_long, u_long, void (*)(), int)); +__END_DECLS /* * Service un-registration @@ -162,7 +168,9 @@ extern bool_t svc_register(); * u_long prog; * u_long vers; */ -extern void svc_unregister(); +__BEGIN_DECLS +extern void svc_unregister __P((u_long, u_long)); +__END_DECLS /* * Transport registration. @@ -170,7 +178,9 @@ extern void svc_unregister(); * xprt_register(xprt) * SVCXPRT *xprt; */ -extern void xprt_register(); +__BEGIN_DECLS +extern void xprt_register __P((SVCXPRT *)); +__END_DECLS /* * Transport un-register @@ -178,7 +188,9 @@ extern void xprt_register(); * xprt_unregister(xprt) * SVCXPRT *xprt; */ -extern void xprt_unregister(); +__BEGIN_DECLS +extern void xprt_unregister __P((SVCXPRT *)); +__END_DECLS @@ -209,14 +221,16 @@ extern void xprt_unregister(); * deadlock the caller and server processes! */ -extern bool_t svc_sendreply(); -extern void svcerr_decode(); -extern void svcerr_weakauth(); -extern void svcerr_noproc(); -extern void svcerr_progvers(); -extern void svcerr_auth(); -extern void svcerr_noprog(); -extern void svcerr_systemerr(); +__BEGIN_DECLS +extern bool_t svc_sendreply __P((SVCXPRT *, xdrproc_t, char *)); +extern void svcerr_decode __P((SVCXPRT *)); +extern void svcerr_weakauth __P((SVCXPRT *)); +extern void svcerr_noproc __P((SVCXPRT *)); +extern void svcerr_progvers __P((SVCXPRT *, u_long, u_long)); +extern void svcerr_auth __P((SVCXPRT *, enum auth_stat)); +extern void svcerr_noprog __P((SVCXPRT *)); +extern void svcerr_systemerr __P((SVCXPRT *)); +__END_DECLS /* * Lowest level dispatching -OR- who owns this process anyway. @@ -246,9 +260,11 @@ extern int svc_fds; */ extern void rpctest_service(); -extern void svc_getreq(); -extern void svc_getreqset(); /* takes fdset instead of int */ -extern void svc_run(); /* never returns */ +__BEGIN_DECLS +extern void svc_getreq __P((int)); +extern void svc_getreqset __P((fd_set *)); +extern void svc_run __P((void)); +__END_DECLS /* * Socket to use on svcxxx_create call to get default socket @@ -262,19 +278,25 @@ extern void svc_run(); /* never returns */ /* * Memory based rpc for testing and timing. */ -extern SVCXPRT *svcraw_create(); +__BEGIN_DECLS +extern SVCXPRT *svcraw_create __P((void)); +__END_DECLS + /* * Udp based rpc. */ -extern SVCXPRT *svcudp_create(); -extern SVCXPRT *svcudp_bufcreate(); +__BEGIN_DECLS +extern SVCXPRT *svcudp_create __P((int)); +extern SVCXPRT *svcudp_bufcreate __P((int, u_int, u_int)); +__END_DECLS + /* * Tcp based rpc. */ -extern SVCXPRT *svctcp_create(); - - +__BEGIN_DECLS +extern SVCXPRT *svctcp_create __P((int, u_int, u_int)); +__END_DECLS -#endif !__SVC_HEADER__ +#endif /* !_RPC_SVC_H */ diff --git a/lib/librpc/rpc/svc_auth.c b/lib/librpc/rpc/svc_auth.c index ab7ab69421..833dd9f06f 100644 --- a/lib/librpc/rpc/svc_auth.c +++ b/lib/librpc/rpc/svc_auth.c @@ -1,6 +1,3 @@ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)svc_auth.c 2.1 88/08/07 4.0 RPCSRC; from 1.19 87/08/11 Copyr 1984 Sun Micro"; -#endif /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -30,6 +27,12 @@ static char sccsid[] = "@(#)svc_auth.c 2.1 88/08/07 4.0 RPCSRC; from 1.19 87/08/ * Mountain View, California 94043 */ +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)svc_auth.c 1.19 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)svc_auth.c 2.1 88/08/07 4.0 RPCSRC";*/ +static char *rcsid = "$Id: svc_auth.c,v 1.3 1993/08/26 00:53:38 jtc Exp $"; +#endif + /* * svc_auth_nodes.c, Server-side rpc authenticator interface, * *WITHOUT* DES authentication. diff --git a/lib/librpc/rpc/svc_auth.h b/lib/librpc/rpc/svc_auth.h index a36a01aba8..485b18bfe5 100644 --- a/lib/librpc/rpc/svc_auth.h +++ b/lib/librpc/rpc/svc_auth.h @@ -1,4 +1,3 @@ -/* @(#)svc_auth.h 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -26,8 +25,11 @@ * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 + * + * from: @(#)svc_auth.h 1.6 86/07/16 SMI + * from: @(#)svc_auth.h 2.1 88/07/29 4.0 RPCSRC + * $Id: svc_auth.h,v 1.3 1993/08/01 18:45:50 mycroft Exp $ */ -/* @(#)svc_auth.h 1.6 86/07/16 SMI */ /* * svc_auth.h, Service side of rpc authentication. @@ -35,8 +37,14 @@ * Copyright (C) 1984, Sun Microsystems, Inc. */ +#ifndef _RPC_SVCAUTH_H +#define _RPC_SVCAUTH_H /* * Server side authenticator */ -extern enum auth_stat _authenticate(); +__BEGIN_DECLS +extern enum auth_stat _authenticate __P((struct svc_req *, struct rpc_msg *)); +__END_DECLS + +#endif /* !_RPC_SVCAUTH_H */ diff --git a/lib/librpc/rpc/svc_auth_unix.c b/lib/librpc/rpc/svc_auth_unix.c index ea00b7895f..c2f7066e9d 100644 --- a/lib/librpc/rpc/svc_auth_unix.c +++ b/lib/librpc/rpc/svc_auth_unix.c @@ -1,4 +1,3 @@ -/* @(#)svc_auth_unix.c 2.3 88/08/01 4.0 RPCSRC; from 1.28 88/02/08 SMI */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)svc_auth_unix.c 1.28 88/02/08 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)svc_auth_unix.c 1.28 88/02/08 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)svc_auth_unix.c 2.3 88/08/01 4.0 RPCSRC";*/ +static char *rcsid = "$Id: svc_auth_unix.c,v 1.3 1993/08/26 00:53:39 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/svc_raw.c b/lib/librpc/rpc/svc_raw.c index 1170ecec83..62a45133bf 100644 --- a/lib/librpc/rpc/svc_raw.c +++ b/lib/librpc/rpc/svc_raw.c @@ -1,4 +1,3 @@ -/* @(#)svc_raw.c 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)svc_raw.c 1.15 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)svc_raw.c 1.15 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)svc_raw.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: svc_raw.c,v 1.3 1993/08/26 00:53:41 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/svc_run.c b/lib/librpc/rpc/svc_run.c index c1c3e04781..160a307c12 100644 --- a/lib/librpc/rpc/svc_run.c +++ b/lib/librpc/rpc/svc_run.c @@ -1,8 +1,3 @@ -/* @(#)svc_run.c 2.1 88/07/29 4.0 RPCSRC */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)svc_run.c 1.1 87/10/13 Copyr 1984 Sun Micro"; -#endif - /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -32,6 +27,12 @@ static char sccsid[] = "@(#)svc_run.c 1.1 87/10/13 Copyr 1984 Sun Micro"; * Mountain View, California 94043 */ +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)svc_run.c 1.1 87/10/13 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)svc_run.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: svc_run.c,v 1.3 1993/08/26 00:53:42 jtc Exp $"; +#endif + /* * This is the rpc server side idle loop * Wait for input, call server program. diff --git a/lib/librpc/rpc/svc_simple.c b/lib/librpc/rpc/svc_simple.c index d6bcbd3c04..25585177be 100644 --- a/lib/librpc/rpc/svc_simple.c +++ b/lib/librpc/rpc/svc_simple.c @@ -1,4 +1,3 @@ -/* @(#)svc_simple.c 2.2 88/08/01 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)svc_simple.c 1.18 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)svc_simple.c 1.18 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)svc_simple.c 2.2 88/08/01 4.0 RPCSRC";*/ +static char *rcsid = "$Id: svc_simple.c,v 1.3 1993/08/26 00:53:43 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/svc_tcp.c b/lib/librpc/rpc/svc_tcp.c index 587e0f0d9b..df1c68c23d 100644 --- a/lib/librpc/rpc/svc_tcp.c +++ b/lib/librpc/rpc/svc_tcp.c @@ -1,4 +1,3 @@ -/* @(#)svc_tcp.c 2.2 88/08/01 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)svc_tcp.c 2.2 88/08/01 4.0 RPCSRC";*/ +static char *rcsid = "$Id: svc_tcp.c,v 1.3 1993/08/26 00:53:44 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/svc_udp.c b/lib/librpc/rpc/svc_udp.c index 429b01b6de..826e638a18 100644 --- a/lib/librpc/rpc/svc_udp.c +++ b/lib/librpc/rpc/svc_udp.c @@ -1,4 +1,3 @@ -/* @(#)svc_udp.c 2.2 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)svc_udp.c 2.2 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: svc_udp.c,v 1.4 1993/08/26 00:53:45 jtc Exp $"; #endif /* @@ -40,6 +42,7 @@ static char sccsid[] = "@(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro"; */ #include +#include #include #include #include diff --git a/lib/librpc/rpc/types.h b/lib/librpc/rpc/types.h index 06d22bf800..af90e87e3a 100644 --- a/lib/librpc/rpc/types.h +++ b/lib/librpc/rpc/types.h @@ -1,4 +1,3 @@ -/* @(#)types.h 2.3 88/08/15 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -26,25 +25,32 @@ * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 + * + * from: @(#)types.h 1.18 87/07/24 SMI + * from: @(#)types.h 2.3 88/08/15 4.0 RPCSRC + * $Id: types.h,v 1.5 1993/08/01 18:45:51 mycroft Exp $ */ -/* @(#)types.h 1.18 87/07/24 SMI */ /* * Rpc additions to */ -#ifndef __TYPES_RPC_HEADER__ -#define __TYPES_RPC_HEADER__ +#ifndef _RPC_TYPES_H +#define _RPC_TYPES_H #define bool_t int #define enum_t int -#define FALSE (0) -#define TRUE (1) #define __dontcare__ -1 + +#ifndef FALSE +# define FALSE (0) +#endif +#ifndef TRUE +# define TRUE (1) +#endif #ifndef NULL -# define NULL 0 +# define NULL 0 #endif -void *malloc(); #define mem_alloc(bsize) malloc(bsize) #define mem_free(ptr, bsize) free(ptr) @@ -54,10 +60,7 @@ void *malloc(); #include #ifndef INADDR_LOOPBACK -#define INADDR_LOOPBACK (u_long)0x7F000001 -#endif -#ifndef MAXHOSTNAMELEN -#define MAXHOSTNAMELEN 64 +#define INADDR_LOOPBACK (u_long)0x7F000001 #endif -#endif /* ndef __TYPES_RPC_HEADER__ */ +#endif /* !_RPC_TYPES_H */ diff --git a/lib/librpc/rpc/xdr.c b/lib/librpc/rpc/xdr.c index 6c379c9e1b..736e621935 100644 --- a/lib/librpc/rpc/xdr.c +++ b/lib/librpc/rpc/xdr.c @@ -1,4 +1,3 @@ -/* @(#)xdr.c 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)xdr.c 1.35 87/08/12"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)xdr.c 1.35 87/08/12";*/ +/*static char *sccsid = "from: @(#)xdr.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: xdr.c,v 1.3 1993/08/26 00:53:46 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/xdr.h b/lib/librpc/rpc/xdr.h index 6cd3e6fe03..1a551f066f 100644 --- a/lib/librpc/rpc/xdr.h +++ b/lib/librpc/rpc/xdr.h @@ -1,4 +1,3 @@ -/* @(#)xdr.h 2.2 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -26,8 +25,11 @@ * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 + * + * from: @(#)xdr.h 1.19 87/04/22 SMI + * from: @(#)xdr.h 2.2 88/07/29 4.0 RPCSRC + * $Id: xdr.h,v 1.3 1993/08/01 18:45:49 mycroft Exp $ */ -/* @(#)xdr.h 1.19 87/04/22 SMI */ /* * xdr.h, External Data Representation Serialization Routines. @@ -35,8 +37,9 @@ * Copyright (C) 1984, Sun Microsystems, Inc. */ -#ifndef __XDR_HEADER__ -#define __XDR_HEADER__ +#ifndef _RPC_XDR_H +#define _RPC_XDR_H +#include /* * XDR provides a conventional way for converting between C data @@ -221,28 +224,31 @@ struct xdr_discrim { /* * These are the "generic" xdr routines. */ -extern bool_t xdr_void(); -extern bool_t xdr_int(); -extern bool_t xdr_u_int(); -extern bool_t xdr_long(); -extern bool_t xdr_u_long(); -extern bool_t xdr_short(); -extern bool_t xdr_u_short(); -extern bool_t xdr_bool(); -extern bool_t xdr_enum(); -extern bool_t xdr_array(); -extern bool_t xdr_bytes(); -extern bool_t xdr_opaque(); -extern bool_t xdr_string(); -extern bool_t xdr_union(); -extern bool_t xdr_char(); -extern bool_t xdr_u_char(); -extern bool_t xdr_vector(); -extern bool_t xdr_float(); -extern bool_t xdr_double(); -extern bool_t xdr_reference(); -extern bool_t xdr_pointer(); -extern bool_t xdr_wrapstring(); +__BEGIN_DECLS +extern bool_t xdr_void __P((void)); +extern bool_t xdr_int __P((XDR *, int *)); +extern bool_t xdr_u_int __P((XDR *, u_int *)); +extern bool_t xdr_long __P((XDR *, long *)); +extern bool_t xdr_u_long __P((XDR *, u_long *)); +extern bool_t xdr_short __P((XDR *, short *)); +extern bool_t xdr_u_short __P((XDR *, u_short *)); +extern bool_t xdr_bool __P((XDR *, bool_t *)); +extern bool_t xdr_enum __P((XDR *, enum_t *)); +extern bool_t xdr_array __P((XDR *, char **, u_int *, u_int, u_int, xdrproc_t)); +extern bool_t xdr_bytes __P((XDR *, char **, u_int *, u_int)); +extern bool_t xdr_opaque __P((XDR *, caddr_t, u_int)); +extern bool_t xdr_string __P((XDR *, char **, u_int)); +extern bool_t xdr_union __P((XDR *, enum_t *, char *, struct xdr_discrim *, xdrproc_t)); +extern bool_t xdr_char __P((XDR *, char *)); +extern bool_t xdr_u_char __P((XDR *, char *)); +extern bool_t xdr_vector __P((XDR *, char *, u_int, u_int, xdrproc_t)); +extern bool_t xdr_float __P((XDR *, float *)); +extern bool_t xdr_double __P((XDR *, double *)); +extern bool_t xdr_reference __P((XDR *, caddr_t *, u_int, xdrproc_t)); +extern bool_t xdr_pointer __P((XDR *, caddr_t *, u_int, xdrproc_t)); +extern bool_t xdr_wrapstring __P((XDR *, char **)); +extern void xdr_free __P((xdrproc_t, char *)); +__END_DECLS /* * Common opaque bytes objects used by many rpc protocols; @@ -260,11 +266,26 @@ extern bool_t xdr_netobj(); * These are the public routines for the various implementations of * xdr streams. */ -extern void xdrmem_create(); /* XDR using memory buffers */ -extern void xdrstdio_create(); /* XDR using stdio library */ -extern void xdrrec_create(); /* XDR pseudo records for tcp */ -extern bool_t xdrrec_endofrecord(); /* make end of xdr record */ -extern bool_t xdrrec_skiprecord(); /* move to beginning of next record */ -extern bool_t xdrrec_eof(); /* true if no more input */ +__BEGIN_DECLS +/* XDR using memory buffers */ +extern void xdrmem_create __P((XDR *, char *, u_int, enum xdr_op)); + +#ifdef _STDIO_H_ +/* XDR using stdio library */ +extern void xdrstdio_create __P((XDR *, FILE *, enum xdr_op)); +#endif + +/* XDR pseudo records for tcp */ +extern void xdrrec_create __P((XDR *, u_int, u_int, char *, int (*)(), int (*)())); + +/* make end of xdr record */ +extern bool_t xdrrec_endofrecord __P((XDR *, int)); + +/* move to beginning of next record */ +extern bool_t xdrrec_skiprecord __P((XDR *)); + +/* true if no more input */ +extern bool_t xdrrec_eof __P((XDR *)); +__END_DECLS -#endif !__XDR_HEADER__ +#endif /* !_RPC_XDR_H */ diff --git a/lib/librpc/rpc/xdr_array.c b/lib/librpc/rpc/xdr_array.c index 7c2831ccd2..0e90d8fc71 100644 --- a/lib/librpc/rpc/xdr_array.c +++ b/lib/librpc/rpc/xdr_array.c @@ -1,4 +1,3 @@ -/* @(#)xdr_array.c 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)xdr_array.c 1.10 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)xdr_array.c 1.10 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)xdr_array.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: xdr_array.c,v 1.4 1993/08/26 00:53:47 jtc Exp $"; #endif /* @@ -41,7 +43,7 @@ static char sccsid[] = "@(#)xdr_array.c 1.10 87/08/11 Copyr 1984 Sun Micro"; */ #include - +#include #include #include diff --git a/lib/librpc/rpc/xdr_float.c b/lib/librpc/rpc/xdr_float.c index 4b5b697bcf..71dc31145b 100644 --- a/lib/librpc/rpc/xdr_float.c +++ b/lib/librpc/rpc/xdr_float.c @@ -1,4 +1,3 @@ -/* @(#)xdr_float.c 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)xdr_float.c 1.12 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)xdr_float.c 1.12 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)xdr_float.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: xdr_float.c,v 1.6 1993/08/26 00:53:49 jtc Exp $"; #endif /* @@ -42,15 +44,20 @@ static char sccsid[] = "@(#)xdr_float.c 1.12 87/08/11 Copyr 1984 Sun Micro"; */ #include - +#include +#include #include #include /* * NB: Not portable. - * This routine works on Suns (Sky / 68000's) and Vaxen. + * This routine works on Suns (Sky / 68000's), i386's, MIPS, NS32k and Vaxen. */ +#if defined(mc68000)||defined(sparc)||defined(i386)||defined(mips)||defined(ns32000) +#define IEEEFP +#endif + #ifdef vax /* What IEEE single precision floating point looks like on a Vax */ @@ -87,7 +94,7 @@ xdr_float(xdrs, fp) register XDR *xdrs; register float *fp; { -#if !defined(mc68000) && !defined(sparc) +#ifndef IEEEFP struct ieee_single is; struct vax_single vs, *vsp; struct sgl_limits *lim; @@ -96,7 +103,7 @@ xdr_float(xdrs, fp) switch (xdrs->x_op) { case XDR_ENCODE: -#if defined(mc68000) || defined(sparc) +#ifdef IEEEFP return (XDR_PUTLONG(xdrs, (long *)fp)); #else vs = *((struct vax_single *)fp); @@ -118,7 +125,7 @@ xdr_float(xdrs, fp) #endif case XDR_DECODE: -#if defined(mc68000) || defined(sparc) +#ifdef IEEEFP return (XDR_GETLONG(xdrs, (long *)fp)); #else vsp = (struct vax_single *)fp; @@ -148,7 +155,7 @@ xdr_float(xdrs, fp) } /* - * This routine works on Suns (Sky / 68000's) and Vaxen. + * This routine works on Suns (Sky / 68000's), i386's, MIPS and Vaxen. */ #ifdef vax @@ -193,7 +200,7 @@ xdr_double(xdrs, dp) double *dp; { register long *lp; -#if !defined(mc68000) && !defined(sparc) +#ifndef IEEEFP struct ieee_double id; struct vax_double vd; register struct dbl_limits *lim; @@ -203,8 +210,13 @@ xdr_double(xdrs, dp) switch (xdrs->x_op) { case XDR_ENCODE: -#if defined(mc68000) || defined(sparc) +#ifdef IEEEFP lp = (long *)dp; +#if BYTE_ORDER == BIG_ENDIAN + return (XDR_PUTLONG(xdrs, lp++) && XDR_PUTLONG(xdrs, lp)); +#else + return (XDR_PUTLONG(xdrs, lp+1) && XDR_PUTLONG(xdrs, lp)); +#endif #else vd = *((struct vax_double *)dp); for (i = 0, lim = dbl_limits; @@ -227,13 +239,17 @@ xdr_double(xdrs, dp) shipit: id.sign = vd.sign; lp = (long *)&id; -#endif return (XDR_PUTLONG(xdrs, lp++) && XDR_PUTLONG(xdrs, lp)); +#endif case XDR_DECODE: -#if defined(mc68000) || defined(sparc) +#ifdef IEEEFP lp = (long *)dp; +#if BYTE_ORDER == BIG_ENDIAN return (XDR_GETLONG(xdrs, lp++) && XDR_GETLONG(xdrs, lp)); +#else + return (XDR_GETLONG(xdrs, lp+1) && XDR_GETLONG(xdrs, lp)); +#endif #else lp = (long *)&id; if (!XDR_GETLONG(xdrs, lp++) || !XDR_GETLONG(xdrs, lp)) diff --git a/lib/librpc/rpc/xdr_mem.c b/lib/librpc/rpc/xdr_mem.c index 558d369227..9c8376aba5 100644 --- a/lib/librpc/rpc/xdr_mem.c +++ b/lib/librpc/rpc/xdr_mem.c @@ -1,4 +1,3 @@ -/* @(#)xdr_mem.c 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)xdr_mem.c 1.19 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)xdr_mem.c 1.19 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)xdr_mem.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: xdr_mem.c,v 1.3 1993/08/26 00:53:50 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/xdr_rec.c b/lib/librpc/rpc/xdr_rec.c index 9269f12f54..057c6cc51b 100644 --- a/lib/librpc/rpc/xdr_rec.c +++ b/lib/librpc/rpc/xdr_rec.c @@ -1,4 +1,3 @@ -/* @(#)xdr_rec.c 2.2 88/08/01 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,10 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)xdr_rec.c 1.21 87/08/11 Copyr 1984 Sun Micro"; +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)xdr_rec.c 1.21 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)xdr_rec.c 2.2 88/08/01 4.0 RPCSRC";*/ +static char *rcsid = "$Id: xdr_rec.c,v 1.4 1993/08/26 00:53:51 jtc Exp $"; #endif /* @@ -49,6 +50,7 @@ static char sccsid[] = "@(#)xdr_rec.c 1.21 87/08/11 Copyr 1984 Sun Micro"; */ #include +#include #include #include #include diff --git a/lib/librpc/rpc/xdr_reference.c b/lib/librpc/rpc/xdr_reference.c index 32d91d9999..b78719411f 100644 --- a/lib/librpc/rpc/xdr_reference.c +++ b/lib/librpc/rpc/xdr_reference.c @@ -1,4 +1,3 @@ -/* @(#)xdr_reference.c 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)xdr_reference.c 1.11 87/08/11 SMI"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)xdr_reference.c 1.11 87/08/11 SMI";*/ +/*static char *sccsid = "from: @(#)xdr_reference.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: xdr_reference.c,v 1.3 1993/08/26 00:53:53 jtc Exp $"; #endif /* diff --git a/lib/librpc/rpc/xdr_stdio.c b/lib/librpc/rpc/xdr_stdio.c index 694774f6f6..6bdebe7270 100644 --- a/lib/librpc/rpc/xdr_stdio.c +++ b/lib/librpc/rpc/xdr_stdio.c @@ -1,4 +1,3 @@ -/* @(#)xdr_stdio.c 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape @@ -27,8 +26,11 @@ * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -static char sccsid[] = "@(#)xdr_stdio.c 1.16 87/08/11 Copyr 1984 Sun Micro"; + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)xdr_stdio.c 1.16 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)xdr_stdio.c 2.1 88/07/29 4.0 RPCSRC";*/ +static char *rcsid = "$Id: xdr_stdio.c,v 1.3 1993/08/26 00:53:54 jtc Exp $"; #endif /* diff --git a/lib/librpcsvc/Makefile b/lib/librpcsvc/Makefile new file mode 100644 index 0000000000..42701830b5 --- /dev/null +++ b/lib/librpcsvc/Makefile @@ -0,0 +1,31 @@ +# from: @(#)Makefile 5.10 (Berkeley) 6/24/90 +# $Id: Makefile,v 1.6 1993/08/01 05:40:01 mycroft Exp $ + +.PATH: ${.CURDIR}/../../include/rpcsvc /usr/src/include/rpcsvc + +LIB= rpcsvc + +RPCSRCS= klm_prot.x mount.x nfs_prot.x nlm_prot.x rex.x rnusers.x \ + rquota.x rstat.x rwall.x sm_inter.x spray.x yppasswd.x + +RPCCOM = rpcgen + +INCDIRS= -I/usr/include/rpcsvc +CFLAGS+= ${INCDIRS} + +SRCS= ${RPCSRCS:R:S/$/_xdr.c/g} + +CLEANFILES+= ${SRCS} ${RPCSRCS} + +NOMAN= noman + +.include + +.SUFFIXES: .x _xdr.c + +.x_xdr.c: + @echo generating $@... + @PWD=`pwd` ; cd ${.CURDIR} ; if cmp -s ${.IMPSRC} ${*F}.x > /dev/null; then :; else cp -f ${.IMPSRC} $$PWD/${*F}.x ; fi + @${RPCCOM} -c ${*F}.x -o ${.TARGET} + +OBJS+= ${RPCSRCS:R:S/$/_xdr.o/g} diff --git a/lib/libtelnet/Makefile b/lib/libtelnet/Makefile index bf7e490c10..e28bc48a74 100644 --- a/lib/libtelnet/Makefile +++ b/lib/libtelnet/Makefile @@ -1,9 +1,7 @@ # @(#)Makefile 5.4 (Berkeley) 5/7/91 LIB= telnet -SRCS= auth.c encrypt.c genget.c getent.c gettytab.c misc.c -SRCS+= kerberos.c enc_des.c -CFLAGS+= -I/usr/include/kerberosIV +SRCS= genget.c getent.c gettytab.c misc.c .PATH: ${.CURDIR}/../../libexec/getty .include diff --git a/lib/libterm/Makefile b/lib/libterm/Makefile index aff06332b1..4c7b0ba012 100644 --- a/lib/libterm/Makefile +++ b/lib/libterm/Makefile @@ -12,7 +12,7 @@ LIB= termcap CFLAGS+=-DCM_N -DCM_GT -DCM_B -DCM_D SRCS= termcap.c tgoto.c tputs.c -MAN3= termcap.0 +MAN3= termcap.3 MLINKS= termcap.3 tgetent.3 termcap.3 tgetflag.3 termcap.3 tgetnum.3 \ termcap.3 tgetstr.3 termcap.3 tgoto.3 termcap.3 tputs.3 LINKS= ${LIBDIR}/libtermcap.a ${LIBDIR}/libtermlib.a \ diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile index 476f4a148d..8f30077d9d 100644 --- a/lib/libutil/Makefile +++ b/lib/libutil/Makefile @@ -5,7 +5,7 @@ LIB= util CFLAGS+=-DLIBC_SCCS -I/sys SRCS= daemon.c getloadavg.c kvm.c login.c login_tty.c logout.c logwtmp.c \ pty.c pwcache.c -MAN3= getloadavg.0 pwcache.0 +MAN3= getloadavg.3 pwcache.3 MLINKS+=pwcache.3 user_from_uid.3 pwcache.3 group_from_gid.3 diff --git a/libexec/Makefile b/libexec/Makefile index c680f686e4..602bf063e6 100644 --- a/libexec/Makefile +++ b/libexec/Makefile @@ -1,18 +1,11 @@ -# @(#)Makefile 5.7 (Berkeley) 4/1/91 -# -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 1 00116 -# -------------------- ----- ---------------------- -# -# 04 Apr 93 Rodney W. Grimes Add bugfiler, uucpd, uucp comment, -# kpasswdd comment +# $Id: Makefile,v 1.3 1993/08/27 02:13:13 rgrimes Exp $ +# From: @(#)Makefile 5.7 (Berkeley) 4/1/91 # SUBDIR= bugfiler comsat crond elvispreserve fingerd ftpd getNAME getty \ - mail.local makekey rexecd rlogind rshd talkd telnetd tftpd uucpd + mail.local makekey pppd rexecd rlogind rpc.rstatd rpc.rusersd \ + rpc.rwalld rshd talkd telnetd tftpd uucpd -# uucp has old style Makefiles, needs to be updated. # kpasswdd not ported, it is old kerberosIV .include diff --git a/libexec/bugfiler/Makefile b/libexec/bugfiler/Makefile index d24fc7030f..540c71ecc3 100644 --- a/libexec/bugfiler/Makefile +++ b/libexec/bugfiler/Makefile @@ -1,13 +1,13 @@ -# @(#)Makefile 5.17 (Berkeley) 5/24/90 -# Fixed to use bsd.coe.montana.edu as a bugs host - rgrimes 4/04/93 +# from: @(#)Makefile 5.17 (Berkeley) 5/24/90 +# $Id$ PROG= bugfiler -CFLAGS+=-I${.CURDIR} -D"BUGS_HOME \"owner-386bsd_bugs@bsd.coe.montana.edu\"" +CFLAGS+=-I${.CURDIR} -DBUGS_HOME=\"owner-FreeBSD-bugfiler@freefall.cdrom.com\" SRCS= bugfiler.c error.c gethead.c process.c redist.c reply.c BINOWN= root BINMODE=4555 -MAN1= sendbug.0 -MAN8= bugfiler.0 +MAN1= sendbug.1 +MAN8= bugfiler.8 beforeinstall: install -c -o bin -g ${BINGRP} -m 555 \ diff --git a/libexec/bugfiler/bug.h b/libexec/bugfiler/bug.h index 91a4c57bf9..2dfd18501e 100644 --- a/libexec/bugfiler/bug.h +++ b/libexec/bugfiler/bug.h @@ -30,9 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)bug.h 5.10 (Berkeley) 2/25/91 - * - * 4/01/93 Fixed BUGS_HOME so it can be defined from the Makefile - rgrimes + * from: @(#)bug.h 5.10 (Berkeley) 2/25/91 + * $Id$ */ #ifndef BUGS_HOME diff --git a/libexec/bugfiler/bugformat b/libexec/bugfiler/bugformat index 9d9c3ca1db..939f4188f4 100644 --- a/libexec/bugfiler/bugformat +++ b/libexec/bugfiler/bugformat @@ -1,5 +1,5 @@ Subject: Short summary of the problem (please make this meaningful!) -Index: folder 386BSD-0.1.2.3 +Index: folder FreeBSD-current Description: Detailed description of the problem, suggestion, or complaint. @@ -19,7 +19,7 @@ you must replace "folder" (on the line above starting with "Index:") with one of the following values: folder ::= bin | doc | etc | games | ideas | include | lib - | libexec | man | misc | sbin | share | sys.386bsd + | libexec | man | misc | sbin | share | sys | usr.bin | usr.sbin This value should match the part of directory that the bug is in @@ -28,14 +28,13 @@ first one on the list. If the place that it appears is not on the list use the "misc" value. If it is a suggestion use the "ideas" value. If it is a manual page problem use the "man" value. -If you're not running 386BSD-0.1.2.3, you should also replace -"386BSD-0.1.2.3" on the same line with one of the following values. +If you're not running FreeBSD-current, you should also replace +"FreeBSD-current" on the same line with one of the following values. - version ::= 386BSD-0.1 | 386BSD-0.1.1 | 386BSD-0.1.2 | 386BSD-0.1.2.1 - | 386BSD-0.1.2.2 | 386BSD-0.1.2.3 + version ::= FreeBSD-current | FreeBSD-1.0 For example, if your bug concerns the program "/usr/bin/file" and -you're currently running 386BSD-0.1.2.4, you should replace "folder" -with "usr.bin/file", and "386BSD-0.1.2.3" with "386BSD-0.1.2.4". +you're currently running FreeBSD-1.1, you should replace "folder" +with "usr.bin/file", and "FreeBSD-current" with "FreeBSD-1.1". Thus, your Index line would look like this: -Index: usr.bin/file 386BSD-0.1.2.4 +Index: usr.bin/file FreeBSD-1.1 diff --git a/libexec/bugfiler/pathnames.h b/libexec/bugfiler/pathnames.h index 406aa0ee3b..a90bb8d7d9 100644 --- a/libexec/bugfiler/pathnames.h +++ b/libexec/bugfiler/pathnames.h @@ -30,9 +30,10 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pathnames.h 5.4 (Berkeley) 3/7/91 + * from: @(#)pathnames.h 5.4 (Berkeley) 3/7/91 + * $Id$ */ -#define MAIL_CMD "/usr/sbin/sendmail -i -t -F \"Bugs Bunny\" -f owner-bugs" +#define MAIL_CMD "/usr/sbin/sendmail -i -t -F \"Bugs Bunny\" -f BUGS_HOME" #undef _PATH_TMP #define _PATH_TMP "/tmp/BUG_XXXXXX" diff --git a/libexec/bugfiler/sendbug.1 b/libexec/bugfiler/sendbug.1 index aad2cb7699..3f2ca07ffa 100644 --- a/libexec/bugfiler/sendbug.1 +++ b/libexec/bugfiler/sendbug.1 @@ -29,19 +29,20 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)sendbug.1 6.8 (Berkeley) 5/9/91 +.\" from: @(#)sendbug.1 6.8 (Berkeley) 5/9/91 +.\" $Id$ .\" .Dd May 9, 1991 .Dt SENDBUG 1 -.Os BSD 4.2 +.Os FreeBSD 1.0 .Sh NAME .Nm sendbug -.Nd mail a system bug report to 386bsd_bugs +.Nd mail a system bug report to FreeBSD-bugfiler .Sh SYNOPSIS .Nm sendbug .Op Ar address .Sh DESCRIPTION -Bug reports sent to `386bsd_bugs@bsd.coe.montana.edu' are intercepted +Bug reports sent to `FreeBSD-bugfiler@freefall.cdrom.com' are intercepted by a program which expects bug reports to conform to a standard format. .Nm Sendbug is a shell script to help the user compose and mail bug reports @@ -52,7 +53,7 @@ works by invoking the editor specified by the environment variable on a temporary copy of the bug report format outline. The user must fill in the appropriate fields and exit the editor. .Nm Sendbug -then mails the completed report to `386bsd_bugs@bsd.coe.montana.edu' or the +then mails the completed report to `FreeBSD-bugfiler@freefall.cdrom.com' or the .Ar address specified on the command line. .Sh ENVIRONMENT diff --git a/libexec/bugfiler/sendbug.sh b/libexec/bugfiler/sendbug.sh index 395cc3ec73..1b8deebd28 100644 --- a/libexec/bugfiler/sendbug.sh +++ b/libexec/bugfiler/sendbug.sh @@ -31,10 +31,8 @@ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # -# @(#)sendbug.sh 5.11 (Berkeley) 7/25/90 -# -# Modified to support 386bsd bugfiler running at bsd.coe.montana.edu -# by Rodney W. Grimes on April 4, 1993. +# form: @(#)sendbug.sh 5.11 (Berkeley) 7/25/90 +# $Id$ # # create a bug report and mail it to 'bugs'. @@ -46,8 +44,8 @@ TEMP=/tmp/bug$$ FORMAT=/usr/share/misc/bugformat # uucp sites should use: -# ": ${BUGADDR=bsd.coe.montana.edu!386bsd_bugs}" with a suitable path. -: ${BUGADDR=386bsd_bugs@bsd.coe.montana.edu} +# ": ${BUGADDR=freefall.cdrom.com!FreeBSD-bugfiler}" with a suitable path. +: ${BUGADDR=FreeBSD-bugfiler@freefall.cdrom.com} : ${EDITOR=vi} trap 'rm -f $TEMP ; exit 1' 1 2 3 13 15 diff --git a/libexec/comsat/Makefile b/libexec/comsat/Makefile index ced9120ceb..eaebc9421e 100644 --- a/libexec/comsat/Makefile +++ b/libexec/comsat/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= comsat -MAN8= comsat.0 +MAN8= comsat.8 .include diff --git a/libexec/crond/Makefile b/libexec/crond/Makefile index 755d6a092e..f722dae121 100644 --- a/libexec/crond/Makefile +++ b/libexec/crond/Makefile @@ -12,6 +12,6 @@ PROG= crond SRCS= crond.c database.c user.c entry.c misc.c job.c do_command.c env.c CFLAGS+=-I${.CURDIR} -DDEBUGGING=1 -DBSD -DCRONDIR='"/var/cron"' -fstrength-reduce -MAN8= crond.0 +MAN8= crond.8 .include diff --git a/libexec/elvispreserve/Makefile b/libexec/elvispreserve/Makefile index 67e1ab03ee..7566625528 100644 --- a/libexec/elvispreserve/Makefile +++ b/libexec/elvispreserve/Makefile @@ -1,7 +1,9 @@ PROG= elvispreserve -CFLAGS= -I${.CURDIR}/../../usr.bin/elvis -MAN8=elvispreserve.0 +CFLAGS+=-I${.CURDIR}/../../usr.bin/elvis +BINOWN= root +BINMODE=4755 +MAN8=elvispreserve.8 .include .PATH: ${.CURDIR}/../../usr.bin/elvis diff --git a/libexec/elvispreserve/elvispreserve.8 b/libexec/elvispreserve/elvispreserve.8 index 9c7e74f974..ae4cdfbe5b 100644 --- a/libexec/elvispreserve/elvispreserve.8 +++ b/libexec/elvispreserve/elvispreserve.8 @@ -33,14 +33,14 @@ if your operating system normally supports mail. .SH FILES .IP /tmp/elv* The temporary file that \fIelvis\fP was using when it died. -.IP /usr/preserve/p* +.IP /var/preserve/p* The text that is preserved by \fIelvispreserve\fP. -.IP /usr/preserve/Index +.IP /var/preserve/Index A text file which lists the names of all preserved files, and the names -of the /usr/preserve/p* files which contain their preserved text. +of the /var/preserve/p* files which contain their preserved text. .SH BUGS .PP -Due to the permissions on the /usr/preserve directory, on UNIX systems +Due to the permissions on the /var/preserve directory, on UNIX systems \fIelvispreserve\fP must be run as superuser. This is accomplished by making the \fIelvispreserve\fP executable be owned by "root" and turning on its "set user id" bit. diff --git a/libexec/elvispreserve/elvispreserve.c b/libexec/elvispreserve/elvispreserve.c index b9e72725e1..f5a76e40b6 100644 --- a/libexec/elvispreserve/elvispreserve.c +++ b/libexec/elvispreserve/elvispreserve.c @@ -53,6 +53,17 @@ #include "config.h" #include "vi.h" +/* We include ctype.c here (instead of including just ctype.h and linking + * with ctype.o) because on some systems ctype.o will have been compiled in + * "large model" and the elvprsv program is to be compiled in "small model" + * You can't mix models. By including ctype.c here, we can avoid linking + * with ctype.o. + */ +#include "ctype.c" + +void preserve P_((char *, char *)); +void main P_((int, char **)); + #if AMIGA BLK tmpblk; # include "amiwild.c" @@ -121,7 +132,7 @@ void preserve(tname, when) || read(infd, name.c, BLKSIZE) != BLKSIZE) { /* something wrong with the file - sorry */ - fprintf(stderr, "%s: trucated header blocks\n", tname); + fprintf(stderr, "%s: truncated header blocks\n", tname); close(infd); return; } @@ -138,6 +149,16 @@ void preserve(tname, when) return; } + /* If there are no text blocks in the file, then we must've never + * really started editing. Discard the file. + */ + if (hdr.n[1] == 0) + { + close(infd); + unlink(tname); + return; + } + if (rewrite_now) { /* we don't need to open the index file */ @@ -147,7 +168,8 @@ void preserve(tname, when) for (i = 1; i < MAXBLKS && hdr.n[i]; i++) { lseek(infd, (long)hdr.n[i] * (long)BLKSIZE, 0); - if (read(infd, buf.c, BLKSIZE) != BLKSIZE) + if (read(infd, buf.c, BLKSIZE) != BLKSIZE + || buf.c[0] == '\0') { /* messed up header */ fprintf(stderr, "%s: unrecoverable -- header trashed\n", name.c); @@ -172,9 +194,12 @@ void preserve(tname, when) if (!index) { perror(PRSVINDEX); - exit(1); + exit(2); } + /* should be at the end of the file already, but MAKE SURE */ + fseek(index, 0L, 2); + /* create the recovery file in the PRESVDIR directory */ #if AMIGA prsvdir = &PRSVDIR[strlen(PRSVDIR) - 1]; @@ -199,7 +224,8 @@ void preserve(tname, when) for (i = 1; i < MAXBLKS && hdr.n[i]; i++) { lseek(infd, (long)hdr.n[i] * (long)BLKSIZE, 0); - if (read(infd, buf.c, BLKSIZE) != BLKSIZE) + if (read(infd, buf.c, BLKSIZE) != BLKSIZE + || buf.c[0] == '\0') { /* messed up header */ fprintf(stderr, "%s: unrecoverable -- header trashed\n", name.c); @@ -242,7 +268,7 @@ void preserve(tname, when) } } -main(argc, argv) +void main(argc, argv) int argc; char **argv; { @@ -251,10 +277,11 @@ main(argc, argv) #if MSDOS || TOS /* expand any wildcards in the command line */ + _ct_init(""); argv = wildexpand(&argc, argv); #endif - /* do we have a "when" argument? */ + /* do we have a "-c", "-R", or "-when elvis died" argument? */ i = 1; if (argc >= i + 1 && !strcmp(argv[i], "-R")) { diff --git a/libexec/elvispreserve/prsvunix.c b/libexec/elvispreserve/prsvunix.c new file mode 100644 index 0000000000..be954c2715 --- /dev/null +++ b/libexec/elvispreserve/prsvunix.c @@ -0,0 +1,226 @@ +/* prsvunix.c */ + +/* This file contains the UNIX-specific parts of the "elvprsv" program. */ + +#if OSK +#define ELVPRSV +#include "osk.c" +#else +#include +#include +#endif +#ifndef __STDC__ +/* some older systems don't declare this in pwd.h, I guess. */ +extern struct passwd *getpwuid(); +#endif + +#if ANY_UNIX /* { */ +/* Since elvprsv runs as SUID-root, we need a *secure* version of popen() */ +#define popen safe_popen +#define pclose safe_pclose + +/* This function is similar to the standard popen() function, except for... + * 1) It doesn't use the shell, for security reasons. + * 2) Shell services are not supported, including quoting. + * 3) The mode can only be "w". "r" is not supported. + * 4) No more than 9 arguments can be given, including the command. + */ +/*ARGSUSED*/ +static FILE *safe_popen(cmd, mode) + char *cmd; /* the filename of the program to be run */ + char *mode; /* "w", ignored */ +{ + char path[100];/* full pathname of argv[0] */ + char *argv[10];/* the arguments */ + int r0w1[2];/* the pipe fd's */ + int i; + FILE *fp; + + /* parse the arguments */ + for (i = 0; i < 9 && *cmd; i++) + { + /* remember where this arg starts */ + argv[i] = cmd; + + /* move to the end of the argument */ + do + { + cmd++; + } while (*cmd && *cmd != ' '); + + /* then mark end of arg & skip to next */ + while (*cmd && *cmd == ' ') + { + *cmd++ = '\0'; + } +printf("argv[%d]=\"%s\"\n", i, argv[i]); + } + argv[i] = (char *)0; + + /* make the pipe */ + if (pipe(r0w1) < 0) + { +perror("pipe()"); + return (FILE *)0; /* pipe failed */ + } + + switch (fork()) + { + case -1: /* error */ +perror("fork()"); + return (FILE *)0; + + case 0: /* child */ + /* close the "write" end of the pipe */ + close(r0w1[1]); + + /* redirect stdin to come from the "read" end of the pipe */ + close(0); + dup(r0w1[0]); + close(r0w1[0]); + + /* exec the shell to run the command */ + if (*argv[0] != '/') + { + /* no path, try "/bin/argv[0]" */ + strcpy(path, "/bin/"); + strcat(path, argv[0]); + execv(path, argv); +perror(path); + + /* if that failed, then try "/usr/bin/argv[0]" */ + strcpy(path, "/usr/bin/"); + strcat(path, argv[0]); + execv(path, argv); +perror(path); + } + else + { + /* full pathname given, so use it */ + execv(argv[0], argv); +perror(argv[0]); + } + + /* if we get here, exec failed */ + exit(1); + + default: /* parent */ + /* close the "read" end of the pipe */ + close(r0w1[0]); + + /* convert the "write" fd into a (FILE *) */ + fp = fdopen(r0w1[1], "w"); + return fp; + } + /*NOTREACHED*/ +} + + +/* This function closes the pipe opened by popen(), and returns 0 for success */ +static int safe_pclose(fp) + FILE *fp; /* value returned by popen() */ +{ + int status; + + /* close the file, and return the defunct child's exit status */ + fclose(fp); + wait(&status); + return status; +} +#endif /* } ANY UNIX */ + + +/* This variable is used to add extra error messages for mail sent to root */ +char *ps; + +/* This function returns the login name of the owner of a file */ +char *ownername(filename) + char *filename; /* name of a file */ +{ + struct stat st; + struct passwd *pw; + + /* stat the file, to get its uid */ + if (stat(filename, &st) < 0) + { + ps = "stat() failed"; + return "root"; + } + + /* get the /etc/passwd entry for that user */ + pw = getpwuid(st.st_uid); + if (!pw) + { + ps = "uid not found in password file"; + return "root"; + } + + /* return the user's name */ + return pw->pw_name; +} + + +/* This function sends a mail message to a given user, saying that a file + * has been preserved. + */ +void mail(user, file, when) + char *user; /* name of user who should receive the mail */ + char *file; /* name of original text file that was preserved */ + char *when; /* description of why the file was preserved */ +{ + char cmd[80];/* buffer used for constructing a "mail" command */ + FILE *m; /* stream used for giving text to the "mail" program */ + char *base; /* basename of the file */ + + /* separate the directory name from the basename. */ + for (base = file + strlen(file); --base > file && *base != SLASH; ) + { + } + if (*base == SLASH) + { + *base++ = '\0'; + } + + /* for anonymous buffers, pretend the name was "foo" */ + if (!strcmp(base, "*")) + { + base = "foo"; + } + + /* open a pipe to the "mail" program */ +#if OSK + sprintf(cmd, "mail \"-s=%s preserved!\" %s", base, user); +#else /* ANY_UNIX */ + sprintf(cmd, "mail -s Graceland %s", user); +#endif + m = popen(cmd, "w"); + if (!m) + { + perror(cmd); + /* Can't send mail! Hope the user figures it out. */ + return; + } + + /* Tell the user that the file was preserved */ + fprintf(m, "A version of your file \"%s%c%s\"\n", file, SLASH, base); + fprintf(m, "was preserved when %s.\n", when); + fprintf(m, "To recover this file, do the following:\n"); + fprintf(m, "\n"); +#if OSK + fprintf(m, " chd %s\n", file); +#else /* ANY_UNIX */ + fprintf(m, " cd %s\n", file); +#endif + fprintf(m, " elvisrecover %s\n", base); + fprintf(m, "\n"); + fprintf(m, "With fond wishes for a speedy recovery,\n"); + fprintf(m, " Elvis\n"); + if (ps) + { + fprintf(m, "\nP.S. %s\n", ps); + ps = (char *)0; + } + + /* close the stream */ + pclose(m); +} diff --git a/libexec/fingerd/Makefile b/libexec/fingerd/Makefile index 15a2cf3448..42d5f638e3 100644 --- a/libexec/fingerd/Makefile +++ b/libexec/fingerd/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= fingerd -MAN8= fingerd.0 +MAN8= fingerd.8 .include diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile index e00045b6ce..fe7ffd1a2c 100644 --- a/libexec/ftpd/Makefile +++ b/libexec/ftpd/Makefile @@ -1,10 +1,17 @@ # @(#)Makefile 5.15 (Berkeley) 7/1/90 + PROG= ftpd CFLAGS+=-I${.CURDIR}/../../usr.bin/ftp -DSETPROCTITLE SRCS= ftpd.c ftpcmd.c glob.c logwtmp.c popen.c vers.c -MAN8= ftpd.0 +MAN8= ftpd.8 CLEANFILES+=ftpcmd.c y.tab.h .PATH: ${.CURDIR}/../../usr.bin/ftp +.if exists(/usr/lib/libcrypt.a) +DPADD+= ${LIBCRYPT} +LDADD+= -lcrypt +.endif + + .include diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c index 6e8d270818..2638e8dd8b 100644 --- a/libexec/ftpd/ftpd.c +++ b/libexec/ftpd/ftpd.c @@ -448,11 +448,7 @@ pass(passwd) salt = "xx"; else salt = pw->pw_passwd; -#ifdef DES xpasswd = crypt(passwd, salt); -#else - xpasswd = passwd; -#endif /* The strcmp does not catch null passwords! */ if (pw == NULL || *pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { diff --git a/libexec/getty/Makefile b/libexec/getty/Makefile index 11726de631..8113441f47 100644 --- a/libexec/getty/Makefile +++ b/libexec/getty/Makefile @@ -1,10 +1,10 @@ # @(#)Makefile 5.12 (Berkeley) 1/21/91 PROG= getty -SRCS= main.c init.c subr.c gettytab.c ttydefaults.c +SRCS= main.c init.c subr.c gettytab.c DPADD= ${LIBUTIL} LDADD= -lutil -MAN5= gettytab.0 ttys.0 -MAN8= getty.0 +MAN5= gettytab.5 ttys.5 +MAN8= getty.8 .include diff --git a/libexec/getty/gettytab.c b/libexec/getty/gettytab.c index cfdddc0534..a864529cdb 100644 --- a/libexec/getty/gettytab.c +++ b/libexec/getty/gettytab.c @@ -45,9 +45,9 @@ static char sccsid[] = "@(#)gettytab.c 5.5 (Berkeley) 2/25/91"; static char *tbuf; int hopcount; /* detect infinite loops in termcap, init 0 */ -char *skip(); +static char *skip(); char *getstr(); -char *decode(); +static char *decode(); /* * Get an entry for terminal name in buffer bp, diff --git a/libexec/getty/main.c b/libexec/getty/main.c index e95bdb125d..18b35b8c58 100644 --- a/libexec/getty/main.c +++ b/libexec/getty/main.c @@ -140,7 +140,6 @@ main(argc, argv) { extern char **environ; char *tname; - long allflags; int repcnt = 0; signal(SIGINT, SIG_IGN); @@ -191,16 +190,18 @@ main(argc, argv) if (argc > 1) tname = argv[1]; for (;;) { - int ldisp = OTTYDISC; int off = 0; + int flushboth = 0; + struct sgttyb fake; gettable(tname, tabent, tabstrs); if (OPset || EPset || APset) APset++, OPset++, EPset++; setdefaults(); - ioctl(0, TIOCFLUSH, 0); /* clear out the crap */ + ioctl(0, TIOCFLUSH, &flushboth); /* clear out the crap */ ioctl(0, FIONBIO, &off); /* turn off non-blocking mode */ ioctl(0, FIOASYNC, &off); /* ditto for async mode */ + ioctl(0, TIOCGETP, &fake); /* initialize kernel termios */ if (IS) tmode.sg_ispeed = speed(IS); else if (SP) @@ -209,8 +210,7 @@ main(argc, argv) tmode.sg_ospeed = speed(OS); else if (SP) tmode.sg_ospeed = speed(SP); - tmode.sg_flags = setflags(0); - ioctl(0, TIOCSETP, &tmode); + set_tmode(0); setchars(); ioctl(0, TIOCSETC, &tc); if (HC) @@ -251,28 +251,13 @@ main(argc, argv) } if (!(upper || lower || digit)) continue; - allflags = setflags(2); - tmode.sg_flags = allflags & 0xffff; - allflags >>= 16; - if (crmod || NL) - tmode.sg_flags |= CRMOD; - if (upper || UC) - tmode.sg_flags |= LCASE; - if (lower || LC) - tmode.sg_flags &= ~LCASE; - ioctl(0, TIOCSETP, &tmode); + set_tmode(2); ioctl(0, TIOCSLTC, <c); - ioctl(0, TIOCLSET, &allflags); signal(SIGINT, SIG_DFL); for (i = 0; environ[i] != (char *)0; i++) env[i] = environ[i]; makeenv(&env[i]); - /* - * this is what login was doing anyway. - * soon we rewrite getty completely. - */ - set_ttydefaults(0); execle(LO, "login", "-p", name, (char *) 0, env); syslog(LOG_ERR, "%s: %m", LO); exit(1); @@ -290,6 +275,7 @@ getname() register int c; register char *np; char cs; + int flushin = 1 /*FREAD*/; /* * Interrupt may happen if we use CBREAK mode @@ -299,16 +285,14 @@ getname() return (0); } signal(SIGINT, interrupt); - tmode.sg_flags = setflags(0); - ioctl(0, TIOCSETP, &tmode); - tmode.sg_flags = setflags(1); + ioctl(0, TIOCFLUSH, &flushin); /* purge any input */ prompt(); + oflush(); if (PF > 0) { - oflush(); sleep(PF); PF = 0; } - ioctl(0, TIOCSETP, &tmode); + set_tmode(1); crmod = digit = lower = upper = 0; np = name; for (;;) { @@ -317,7 +301,7 @@ getname() exit(0); if ((c = cs&0177) == 0) return (0); - if (c == EOT) + if (c == EOT || c == 4 /*^D*/) exit(1); if (c == '\r' || c == '\n' || np >= &name[sizeof name]) { putf("\r\n"); @@ -327,7 +311,7 @@ getname() lower = 1; else if (isupper(c)) upper = 1; - else if (c == ERASE || c == '#' || c == '\b') { + else if (c == ERASE || c == '\b' || c == 0177) { if (np > name) { np--; if (tmode.sg_ospeed >= B1200) @@ -336,8 +320,7 @@ getname() putchr(cs); } continue; - } else if (c == KILL || c == '@') { - putchr(cs); + } else if (c == KILL || c == 025 /*^U*/) { putchr('\r'); if (tmode.sg_ospeed < B1200) putchr('\n'); @@ -497,3 +480,29 @@ putf(cp) cp++; } } + +/* + * The conversions from sgttyb to termios make LITOUT and PASS8 affect + * the parity. So every TIOCSETP ioctl has to be paired with a TIOCLSET + * ioctl (at least if LITOUT or PASS8 has changed, and PASS8 may vary + * with 'n'). + */ +set_tmode(n) + int n; +{ + long allflags; + + allflags = setflags(n); + tmode.sg_flags = allflags & 0xffff; + allflags >>= 16; + if (n == 2) { + if (crmod || NL) + tmode.sg_flags |= CRMOD; + if (upper || UC) + tmode.sg_flags |= LCASE; + if (lower || LC) + tmode.sg_flags &= ~LCASE; + } + ioctl(0, TIOCSETP, &tmode); + ioctl(0, TIOCLSET, &allflags); +} diff --git a/libexec/getty/subr.c b/libexec/getty/subr.c index c67f0374f8..571970e978 100644 --- a/libexec/getty/subr.c +++ b/libexec/getty/subr.c @@ -186,6 +186,8 @@ setflags(n) f |= ODDP; else if (EP) f |= EVENP; + if (NP) + f |= PASS8; if (UC) f |= LCASE; diff --git a/libexec/getty/ttydefaults.c b/libexec/getty/ttydefaults.c deleted file mode 100644 index cb0fbc14b3..0000000000 --- a/libexec/getty/ttydefaults.c +++ /dev/null @@ -1,51 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)ttydefaults.c 5.1 (Berkeley) 1/19/91"; -#endif /* not lint */ - -#include - -set_ttydefaults(fd) - int fd; -{ - struct termios term; - - tcgetattr(fd, &term); - term.c_iflag = TTYDEF_IFLAG; - term.c_oflag = TTYDEF_OFLAG; - term.c_lflag = TTYDEF_LFLAG; - term.c_cflag = TTYDEF_CFLAG; - tcsetattr(fd, TCSAFLUSH, &term); -} diff --git a/libexec/getty/ttys.5 b/libexec/getty/ttys.5 index c829b619c6..8d31b23e56 100644 --- a/libexec/getty/ttys.5 +++ b/libexec/getty/ttys.5 @@ -45,7 +45,6 @@ and control the use of terminal special files. This information is read with the .Xr getttyent 3 library routines. -The There is one line in the .Nm ttys file per special device file. diff --git a/libexec/mail.local/Makefile b/libexec/mail.local/Makefile index 90635a87c1..2c3fd5b97f 100644 --- a/libexec/mail.local/Makefile +++ b/libexec/mail.local/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.3 (Berkeley) 1/17/91 PROG= mail.local -MAN8= mail.local.0 +MAN8= mail.local.8 BINOWN= root BINMODE=4555 diff --git a/libexec/makekey/Makefile b/libexec/makekey/Makefile index b982beef60..5f61f4f9cb 100644 --- a/libexec/makekey/Makefile +++ b/libexec/makekey/Makefile @@ -1,6 +1,11 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= makekey -MAN8= makekey.0 +MAN8= makekey.8 + +.if exists(/usr/lib/libcrypt.a) +DPADD+= ${LIBCRYPT} +LDADD+= -lcrypt +.endif .include diff --git a/libexec/pppd/Makefile b/libexec/pppd/Makefile new file mode 100644 index 0000000000..7f5baae5eb --- /dev/null +++ b/libexec/pppd/Makefile @@ -0,0 +1,24 @@ +# +# $Id: Makefile,v 1.1 1993/08/27 02:11:39 rgrimes Exp $ +# +PROG= pppd +MAN8= pppd.8 +SRCS= main.c magic.c fsm.c lcp.c ipcp.c upap.c logwtmp.c chap.c md5.c +MD5SRCS = md5driver.c + +DEBUG_FLAGS = -DDEBUGFSM -DDEBUGLCP -DDEBUGIPCP -DDEBUGUPAP -DDEBUGCHAP \ + -DDEBUGMAIN + +CFLAGS= -DKVMLIB -DPPP ${DEBUG_FLAGS} + +DPADD+= ${LIBUTIL} +LDADD+= -lutil + +BINOWN= root +BINGRP= daemon +BINMODE= 4555 + +md5driver: md5.h md5.o md5driver.o + $(CC) $(CFLAGS) -o md5driver md5driver.o md5.o + +.include diff --git a/libexec/pppd/args.h b/libexec/pppd/args.h new file mode 100644 index 0000000000..80f5526d2d --- /dev/null +++ b/libexec/pppd/args.h @@ -0,0 +1,11 @@ +/* + * neat macro from ka9q to "do the right thing" with ansi prototypes + */ + +#ifndef __ARGS +#ifdef __STDC__ +#define __ARGS(x) x +#else +#define __ARGS(x) () +#endif +#endif diff --git a/libexec/pppd/callout.h b/libexec/pppd/callout.h new file mode 100644 index 0000000000..cb265f1ea0 --- /dev/null +++ b/libexec/pppd/callout.h @@ -0,0 +1,16 @@ +/* Note: This is a copy of /usr/include/sys/callout.h with the c_func */ +/* member of struct callout changed from a pointer to a function of type int*/ +/* to a pointer to a function of type void (generic pointer) as per */ +/* ANSI C */ + +#ifndef _ppp_callout_h +#define _ppp_callout_h + +struct callout { + int c_time; /* incremental time */ + caddr_t c_arg; /* argument to routine */ + void (*c_func)(); /* routine (changed to void (*)() */ + struct callout *c_next; +}; + +#endif /*!_ppp_callout_h*/ diff --git a/libexec/pppd/chap.c b/libexec/pppd/chap.c new file mode 100644 index 0000000000..6643db16c0 --- /dev/null +++ b/libexec/pppd/chap.c @@ -0,0 +1,678 @@ +/* + * chap.c - Crytographic Handshake Authentication Protocol. + * + * Copyright (c) 1991 Gregory M. Christy. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Gregory M. Christy. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * TODO: + */ + +#include +#include +#include +#include + +#ifdef STREAMS +#include +#include +#include +#endif + +#include +#include "pppd.h" +#include "fsm.h" +#include "lcp.h" +#include "chap.h" +#include "upap.h" +#include "ipcp.h" +#include "md5.h" + +chap_state chap[NPPP]; /* CHAP state; one for each unit */ + +static void ChapTimeout __ARGS((caddr_t)); +static void ChapReceiveChallenge __ARGS((chap_state *, u_char *, int, int)); +static void ChapReceiveResponse __ARGS((chap_state *, u_char *, int, int)); +static void ChapReceiveSuccess __ARGS((chap_state *, u_char *, int, int)); +static void ChapReceiveFailure __ARGS((chap_state *, u_char *, int, int)); +static void ChapSendStatus __ARGS((chap_state *, int, int, + u_char *, int)); +static void ChapSendChallenge __ARGS((chap_state *)); +static void ChapSendResponse __ARGS((chap_state *, int, u_char *, int)); +static void ChapGenChallenge __ARGS((int, u_char *)); + +extern double drand48 __ARGS((void)); +extern void srand48 __ARGS((long)); + +/* + * ChapInit - Initialize a CHAP unit. + */ +void + ChapInit(unit) +int unit; +{ + chap_state *cstate = &chap[unit]; + + cstate->unit = unit; + cstate->chal_str[0] = '\000'; + cstate->chal_len = 0; + cstate->clientstate = CHAPCS_CLOSED; + cstate->serverstate = CHAPSS_CLOSED; + cstate->flags = 0; + cstate->id = 0; + cstate->timeouttime = CHAP_DEFTIMEOUT; + cstate->retransmits = 0; + srand48((long) time(NULL)); /* joggle random number generator */ +} + + +/* + * ChapAuthWithPeer - Authenticate us with our peer (start client). + * + */ +void + ChapAuthWithPeer(unit) +int unit; +{ + chap_state *cstate = &chap[unit]; + + cstate->flags &= ~CHAPF_AWPPENDING; /* Clear pending flag */ + + /* Protect against programming errors that compromise security */ + if (cstate->serverstate != CHAPSS_CLOSED || + cstate->flags & CHAPF_APPENDING) { + CHAPDEBUG((LOG_INFO, + "ChapAuthWithPeer: we were called already!")) + return; + } + + if (cstate->clientstate == CHAPCS_CHALLENGE_SENT || /* should we be here? */ + cstate->clientstate == CHAPCS_OPEN) + return; + + /* Lower layer up? */ + if (!(cstate->flags & CHAPF_LOWERUP)) { + cstate->flags |= CHAPF_AWPPENDING; /* Nah, Wait */ + return; + } + ChapSendChallenge(cstate); /* crank it up dude! */ + TIMEOUT(ChapTimeout, (caddr_t) cstate, cstate->timeouttime); + /* set-up timeout */ + cstate->clientstate = CHAPCS_CHALLENGE_SENT; /* update state */ + cstate->retransmits = 0; +} + + +/* + * ChapAuthPeer - Authenticate our peer (start server). + */ +void + ChapAuthPeer(unit) +int unit; +{ + chap_state *cstate = &chap[unit]; + + cstate->flags &= ~CHAPF_APPENDING; /* Clear pending flag */ + + /* Already authenticat{ed,ing}? */ + if (cstate->serverstate == CHAPSS_LISTEN || + cstate->serverstate == CHAPSS_OPEN) + return; + + /* Lower layer up? */ + if (!(cstate->flags & CHAPF_LOWERUP)) { + cstate->flags |= CHAPF_APPENDING; /* Wait for desired event */ + return; + } + cstate->serverstate = CHAPSS_LISTEN; +} + + +/* + * ChapTimeout - Timeout expired. + */ +static void + ChapTimeout(arg) +caddr_t arg; +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending challenges, don't worry. then again we */ + /* probably shouldn't be here either */ + if (cstate->clientstate != CHAPCS_CHALLENGE_SENT) + return; + + ChapSendChallenge(cstate); /* Send challenge */ + TIMEOUT(ChapTimeout, (caddr_t) cstate, cstate->timeouttime); + ++cstate->retransmits; +} + + +/* + * ChapLowerUp - The lower layer is up. + * + * Start up if we have pending requests. + */ +void + ChapLowerUp(unit) +int unit; +{ + chap_state *cstate = &chap[unit]; + + cstate->flags |= CHAPF_LOWERUP; + if (cstate->flags & CHAPF_AWPPENDING) /* were we attempting authwithpeer? */ + ChapAuthWithPeer(unit); /* Try it now */ + if (cstate->flags & CHAPF_APPENDING) /* or authpeer? */ + ChapAuthPeer(unit); +} + + +/* + * ChapLowerDown - The lower layer is down. + * + * Cancel all timeouts. + */ +void + ChapLowerDown(unit) +int unit; +{ + chap_state *cstate = &chap[unit]; + + cstate->flags &= ~CHAPF_LOWERUP; + + if (cstate->clientstate == CHAPCS_CHALLENGE_SENT) /* Timeout pending? */ + UNTIMEOUT(ChapTimeout, (caddr_t) cstate); /* Cancel timeout */ + + if (cstate->serverstate == CHAPSS_OPEN) /* have we successfully authed? */ + LOGOUT(unit); + cstate->clientstate = CHAPCS_CLOSED; + cstate->serverstate = CHAPSS_CLOSED; +} + + +/* + * ChapProtocolReject - Peer doesn't grok CHAP. + */ +void + ChapProtocolReject(unit) +int unit; +{ + ChapLowerDown(unit); /* shutdown chap */ + + +/* Note: should we bail here if chap is required? */ +} + + +/* + * ChapInput - Input CHAP packet. + */ +void + ChapInput(unit, inpacket, packet_len) +int unit; +u_char *inpacket; +int packet_len; +{ + chap_state *cstate = &chap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (packet_len < CHAP_HEADERLEN) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short header.")) + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < CHAP_HEADERLEN) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd illegal length.")) + return; + } + if (len > packet_len) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short packet.")) + return; + } + len -= CHAP_HEADERLEN; + + /* + * Action depends on code. + */ + switch (code) { + case CHAP_CHALLENGE: + ChapReceiveChallenge(cstate, inp, id, len); + break; + + case CHAP_RESPONSE: + ChapReceiveResponse(cstate, inp, id, len); + break; + + case CHAP_FAILURE: + ChapReceiveFailure(cstate, inp, id, len); + break; + + case CHAP_SUCCESS: + ChapReceiveSuccess(cstate, inp, id, len); + break; + + default: /* Need code reject? */ + syslog(LOG_WARNING, "Unknown CHAP code (%d) received.", code); + break; + } +} + + +/* + * ChapReceiveChallenge - Receive Challenge. + */ +static void + ChapReceiveChallenge(cstate, inp, id, len) +chap_state *cstate; +u_char *inp; +int id; +int len; +{ + u_char rchallenge_len; + u_char *rchallenge; + u_char secret[MAX_SECRET_LEN]; + int secret_len; + u_char rhostname[256]; + u_char buf[256]; + MD5_CTX mdContext; + + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: Rcvd id %d.", id)) + if (cstate->serverstate != CHAPSS_LISTEN) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received challenge but not in listen state")) + return; + } + + if (len < 2) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet.")) + return; + } + GETCHAR(rchallenge_len, inp); + len -= sizeof (u_char) + rchallenge_len ; + if (len < 0) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet.")) + return; + } + rchallenge = inp; + INCPTR(rchallenge_len, inp); + + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received name field: %s", + rhostname)) + GETSECRET(rhostname, secret, &secret_len);/* get secret for specified host */ + + BCOPY(rchallenge, buf, rchallenge_len); /* copy challenge into buffer */ + BCOPY(secret, buf + rchallenge_len, secret_len); /* append secret */ + + /* generate MD based on negotiated type */ + + switch (lcp_hisoptions[cstate->unit].chap_mdtype) { + + case CHAP_DIGEST_MD5: /* only MD5 is defined for now */ + MD5Init(&mdContext); + MD5Update(&mdContext, buf, rchallenge_len + secret_len); + MD5Final(&mdContext); + ChapSendResponse(cstate, id, &mdContext.digest[0], MD5_SIGNATURE_SIZE); + break; + + default: + CHAPDEBUG((LOG_INFO, "unknown digest type %d", + lcp_hisoptions[cstate->unit].chap_mdtype)) + } + +} + + +/* + * ChapReceiveResponse - Receive and process response. + */ +static void + ChapReceiveResponse(cstate, inp, id, len) +chap_state *cstate; +u_char *inp; +int id; +int len; +{ + u_char *remmd, remmd_len; + u_char secret[MAX_SECRET_LEN]; + int secret_len; + u_char chal_len = cstate->chal_len; + u_char code; + u_char rhostname[256]; + u_char buf[256]; + MD5_CTX mdContext; + u_char msg[256], msglen; + + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: Rcvd id %d.", id)) + +/* sanity check */ + if (cstate->clientstate != CHAPCS_CHALLENGE_SENT) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received response but did not send a challenge")) + return; + } + + if (len < 2) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet.")) + return; + } + GETCHAR(remmd_len, inp); /* get length of MD */ + len -= sizeof (u_char) + remmd_len ; + + if (len < 0) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet.")) + return; + } + + remmd = inp; /* get pointer to MD */ + INCPTR(remmd_len, inp); + + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received name field: %s", + rhostname)) + + GETSECRET(rhostname, secret, &secret_len);/* get secret for specified host */ + + BCOPY(cstate->chal_str, buf, chal_len); /* copy challenge */ + /* into buffer */ + BCOPY(secret, buf + chal_len, secret_len); /* append secret */ + + /* generate MD based on negotiated type */ + + switch (lcp_gotoptions[cstate->unit].chap_mdtype) { + + case CHAP_DIGEST_MD5: /* only MD5 is defined for now */ + MD5Init(&mdContext); + MD5Update(&mdContext, buf, chal_len + secret_len); + MD5Final(&mdContext); + + /* compare local and remote MDs and send the appropriate status */ + + if (bcmp (&mdContext.digest[0], remmd, MD5_SIGNATURE_SIZE)) + code = CHAP_FAILURE; /* they ain't the same */ + else + code = CHAP_SUCCESS; /* they are the same! */ + break; + + default: + CHAPDEBUG((LOG_INFO, "unknown digest type %d", + lcp_gotoptions[cstate->unit].chap_mdtype)) + } + if (code == CHAP_SUCCESS) + sprintf((char *)msg, "Welcome to %s.", hostname); + else + sprintf((char *)msg, "I don't like you. Go 'way."); + msglen = strlen(msg); + ChapSendStatus(cstate, code, id, msg, msglen); + + /* only crank up IPCP when either we aren't doing PAP, or if we are, */ + /* that it is in open state */ + + if (code == CHAP_SUCCESS) { + cstate->serverstate = CHAPSS_OPEN; + if (!lcp_hisoptions[cstate->unit].neg_upap || + (lcp_hisoptions[cstate->unit].neg_upap && + upap[cstate->unit].us_serverstate == UPAPSS_OPEN )) + ipcp_activeopen(cstate->unit); /* Start IPCP */ + } +} +/* + * ChapReceiveSuccess - Receive Success + */ +/* ARGSUSED */ +static void + ChapReceiveSuccess(cstate, inp, id, len) +chap_state *cstate; +u_char *inp; +u_char id; +int len; +{ + u_char msglen; + u_char *msg; + + CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: Rcvd id %d.", id)) + + if (cstate->clientstate != CHAPCS_CHALLENGE_SENT) { + CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: received success, but did not send a challenge.")) + return; + } + + /* + * Parse message. + */ + if (len < sizeof (u_char)) { + CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: rcvd short packet.")) + return; + } + GETCHAR(msglen, inp); + len -= sizeof (u_char); + if (len < msglen) { + CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: rcvd short packet.")) + return; + } + msg = inp; + PRINTMSG(msg, msglen); + + cstate->clientstate = CHAPCS_OPEN; + + /* only crank up IPCP when either we aren't doing PAP, or if we are, */ + /* that it is in open state */ + + if (!lcp_gotoptions[cstate->unit].neg_chap || + (lcp_gotoptions[cstate->unit].neg_chap && + upap[cstate->unit].us_serverstate == UPAPCS_OPEN )) + ipcp_activeopen(cstate->unit); /* Start IPCP */ +} + + +/* + * ChapReceiveFailure - Receive failure. + */ +/* ARGSUSED */ +static void + ChapReceiveFailure(cstate, inp, id, len) +chap_state *cstate; +u_char *inp; +u_char id; +int len; +{ + u_char msglen; + u_char *msg; + + CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: Rcvd id %d.", id)) + if (cstate->clientstate != CHAPCS_CHALLENGE_SENT) /* XXX */ + return; + + /* + * Parse message. + */ + if (len < sizeof (u_char)) { + CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: rcvd short packet.")) + return; + } + GETCHAR(msglen, inp); + len -= sizeof (u_char); + if (len < msglen) { + CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: rcvd short packet.")) + return; + } + msg = inp; + PRINTMSG(msg, msglen); + + cstate->flags &= ~CHAPF_UPVALID; /* Clear valid flag */ + cstate->clientstate = CHAPCS_CLOSED; /* Pretend for a moment */ + ChapAuthWithPeer(cstate->unit); /* Restart */ +} + + +/* + * ChapSendChallenge - Send an Authenticate challenge. + */ +static void + ChapSendChallenge(cstate) +chap_state *cstate; +{ + u_char *outp; + u_char chal_len; + int outlen; + +/* pick a random challenge length between MIN_CHALLENGE_LENGTH and + MAX_CHALLENGE_LENGTH */ + cstate->chal_len = (unsigned) ((drand48() * + (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) + + MIN_CHALLENGE_LENGTH); + chal_len = cstate->chal_len; + + outlen = CHAP_HEADERLEN + 2 * sizeof (u_char) + chal_len + hostname_len; + outp = outpacket_buf; + + MAKEHEADER(outp, CHAP); /* paste in a CHAP header */ + + PUTCHAR(CHAP_CHALLENGE, outp); + PUTCHAR(++cstate->id, outp); + PUTSHORT(outlen, outp); + + PUTCHAR(chal_len, outp); /* put length of challenge */ + + ChapGenChallenge(chal_len, cstate->chal_str); /* generate a challenge string */ + + BCOPY(cstate->chal_str, outp, chal_len); /* copy it the the output buffer */ + INCPTR(chal_len, outp); + + BCOPY(hostname, outp, hostname_len); /* append hostname */ + INCPTR(hostname_len, outp); + + output(cstate->unit, outpacket_buf, outlen + DLLHEADERLEN); + + CHAPDEBUG((LOG_INFO, "ChapSendChallenge: Sent id %d.", cstate->id)) + cstate->clientstate |= CHAPCS_CHALLENGE_SENT; +} + + +/* + * ChapSendStatus - Send a status response (ack or nak). + */ +static void + ChapSendStatus(cstate, code, id, msg, msglen) +chap_state *cstate; +u_char code, id; +u_char *msg; +int msglen; +{ + u_char *outp; + int outlen; + + outlen = CHAP_HEADERLEN + msglen; + outp = outpacket_buf; + + MAKEHEADER(outp, CHAP); /* paste in a header */ + + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + BCOPY(msg, outp, msglen); + output(cstate->unit, outpacket_buf, outlen + DLLHEADERLEN); + + CHAPDEBUG((LOG_INFO, "ChapSendStatus: Sent code %d, id %d.", code, id)) +} + +/* + * ChapGenChallenge is used to generate a pseudo-random challenge string of + * a pseudo-random length between min_len and max_len and return the + * challenge string, and the message digest of the secret appended to + * the challenge string. the message digest type is specified by mdtype. + * + * It returns with the string in the caller-supplied buffer str (which + * should be instantiated with a length of max_len + 1), and the + * length of the generated string into chal_len. + * + */ + +static void + ChapGenChallenge(chal_len, str) +u_char chal_len; +u_char * str; +{ + u_char * ptr = str; + unsigned int i; + + /* generate a random string */ + + for (i = 0; i < chal_len; i++ ) + *ptr++ = (char) (drand48() * 0xff); + + *ptr = 0; /* null terminate it so we can printf it */ +} +/* + * ChapSendResponse - send a response packet with the message + * digest specified by md and md_len + */ +/* ARGSUSED */ +static void + ChapSendResponse(cstate, id, md, md_len) +chap_state *cstate; +u_char id; +u_char *md; +int md_len; +{ + u_char *outp; + int outlen; + + outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + hostname_len; + outp = outpacket_buf; + MAKEHEADER(outp, CHAP); + + PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */ + PUTCHAR(id, outp); /* copy id from challenge packet */ + PUTSHORT(outlen, outp); /* packet length */ + + PUTCHAR(md_len, outp); /* length of MD */ + + BCOPY(md, outp, md_len); /* copy MD to buffer */ + INCPTR(md_len, outp); + + BCOPY(hostname, outp, hostname_len); /* append hostname */ + INCPTR(hostname_len, outp); + + output(cstate->unit, outpacket_buf, outlen + DLLHEADERLEN); /* bomb's away! */ +} + +#ifdef NO_DRAND48 + +double drand48() +{ + return (double)random() / (double)0x7fffffffL; /* 2**31-1 */ +} + +void srand48(seedval) +long seedval; +{ + srand((int)seedval); +} + +#endif diff --git a/libexec/pppd/chap.h b/libexec/pppd/chap.h new file mode 100644 index 0000000000..2d5c51e348 --- /dev/null +++ b/libexec/pppd/chap.h @@ -0,0 +1,106 @@ +/* + * chap.h - Cryptographic Handshake Authentication Protocol definitions. + * based on November 1991 draft of PPP Authentication RFC + * + * Copyright (c) 1991 Gregory M. Christy + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the author. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef __CHAP_INCLUDE__ + +/* Code + ID + length */ +#define CHAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + +/* + * CHAP codes. + */ + +#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */ + +#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */ + +#define CHAP_NOCALLBACK 0 /* don't call back after successful auth */ +#define CHAP_CALLBACK 1 /* do call back */ + +#define CHAP_CHALLENGE 1 +#define CHAP_RESPONSE 2 +#define CHAP_SUCCESS 3 +#define CHAP_FAILURE 4 + +/* + * Challenge lengths + */ + +#define MIN_CHALLENGE_LENGTH 64 +#define MAX_CHALLENGE_LENGTH 128 + +#define MAX_SECRET_LEN 128 +/* + * Each interface is described by chap structure. + */ + +typedef struct chap_state { + int unit; /* Interface unit number */ + u_char chal_str[MAX_CHALLENGE_LENGTH + 1]; /* challenge string */ + u_char chal_len; /* challenge length */ + int clientstate; /* Client state */ + int serverstate; /* Server state */ + int flags; /* Flags */ + unsigned char id; /* Current id */ + int timeouttime; /* Timeout time in milliseconds */ + int retransmits; /* Number of retransmissions */ +} chap_state; + + +/* + * Client states. + */ +#define CHAPCS_CLOSED 1 /* Connection down */ +#define CHAPCS_CHALLENGE_SENT 2 /* We've sent a challenge */ +#define CHAPCS_OPEN 3 /* We've received an Ack */ + +/* + * Server states. + */ +#define CHAPSS_CLOSED 1 /* Connection down */ +#define CHAPSS_LISTEN 2 /* Listening for a challenge */ +#define CHAPSS_OPEN 3 /* We've sent an Ack */ + +/* + * Flags. + */ +#define CHAPF_LOWERUP 0x01 /* The lower level is UP */ +#define CHAPF_AWPPENDING 0x02 /* Auth with peer pending */ +#define CHAPF_APPENDING 0x04 /* Auth peer pending */ +#define CHAPF_UPVALID 0x08 /* values valid */ +#define CHAPF_UPPENDING 0x10 /* values pending */ + + +/* + * Timeouts. + */ +#define CHAP_DEFTIMEOUT 3 /* Timeout time in seconds */ + +extern chap_state chap[]; + +void ChapInit __ARGS((int)); +void ChapAuthWithPeer __ARGS((int)); +void ChapAuthPeer __ARGS((int)); +void ChapLowerUp __ARGS((int)); +void ChapLowerDown __ARGS((int)); +void ChapInput __ARGS((int, u_char *, int)); +void ChapProtocolReject __ARGS((int)); + +#define __CHAP_INCLUDE__ +#endif /* __CHAP_INCLUDE__ */ diff --git a/libexec/pppd/fsm.c b/libexec/pppd/fsm.c new file mode 100644 index 0000000000..19bfb97595 --- /dev/null +++ b/libexec/pppd/fsm.c @@ -0,0 +1,765 @@ +/* + * fsm.c - {Link, IP} Control Protocol Finite State Machine. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * TODO: + * Mechanism to exit() and/or drop DTR. + * Hold-down on open? + * Randomize fsm id on link/init. + * Deal with variable outgoing MTU. + */ + +#include +#include +/*#include */ +#include + +#ifdef STREAMS +#include +#include +#include +#endif + +#include +#include "pppd.h" +#include "fsm.h" + +extern char *proto_name(); + +static void fsm_timeout __ARGS((caddr_t)); +static void fsm_rconfack __ARGS((fsm *, u_char *, int, int)); +static void fsm_rconfnak __ARGS((fsm *, u_char *, int, int)); +static void fsm_rconfrej __ARGS((fsm *, u_char *, int, int)); +static void fsm_rtermreq __ARGS((fsm *, int)); +static void fsm_rtermack __ARGS((fsm *)); +static void fsm_rcoderej __ARGS((fsm *, u_char *, int)); +static void fsm_rprotrej __ARGS((fsm *, u_char *, int)); +static void fsm_sconfreq __ARGS((fsm *)); + + +/* + * fsm_init - Initialize fsm. + * + * Initialize fsm state. + */ +void + fsm_init(f) +fsm *f; +{ + f->state = CLOSED; + f->flags = 0; + f->id = 0; /* XXX Start with random id? */ +} + + +/* + * fsm_activeopen - Actively open connection. + * + * Set new state, reset desired options and send requests. + */ +void + fsm_activeopen(f) +fsm *f; +{ + f->flags &= ~(AOPENDING|POPENDING); /* Clear pending flags */ + if (f->state == REQSENT || /* Already actively open(ing)? */ + f->state == ACKRCVD || + f->state == ACKSENT || + f->state == OPEN) + return; + if (f->state == TERMSENT || /* Closing or */ + !(f->flags & LOWERUP)) { /* lower layer down? */ + f->flags |= AOPENDING; /* Wait for desired event */ + return; + } + if (f->callbacks->resetci) + (*f->callbacks->resetci)(f); /* Reset options */ + fsm_sconfreq(f); /* Send Configure-Request */ + TIMEOUT(fsm_timeout, (caddr_t) f, f->timeouttime); + f->state = REQSENT; + f->retransmits = 0; /* Reset retransmits count */ + f->nakloops = 0; /* Reset nakloops count */ +} + + +/* + * fsm_passiveopen - Passively open connection. + * + * Set new state and reset desired options. + */ +void + fsm_passiveopen(f) +fsm *f; +{ + f->flags &= ~(AOPENDING|POPENDING); /* Clear pending flags */ + if (f->state == LISTEN || /* Already passively open(ing)? */ + f->state == OPEN) + return; + if (f->state == REQSENT || /* Active-Opening or */ + f->state == ACKRCVD || + f->state == ACKSENT || + f->state == TERMSENT || /* closing or */ + !(f->flags & LOWERUP)) { /* lower layer down? */ + f->flags |= POPENDING; /* Wait for desired event */ + return; + } + if (f->callbacks->resetci) + (*f->callbacks->resetci)(f); /* Reset options */ + f->state = LISTEN; + f->retransmits = 0; /* Reset retransmits count */ + f->nakloops = 0; /* Reset nakloops count */ +} + + +/* + * fsm_close - Start closing connection. + * + * Cancel timeouts and either initiate close or possibly go directly to + * the CLOSED state. + */ +void + fsm_close(f) +fsm *f; +{ + f->flags &= ~(AOPENDING|POPENDING); /* Clear pending flags */ + if (f->state == CLOSED || /* Already CLOSED or Closing? */ + f->state == TERMSENT) + return; + if (f->state == REQSENT || /* Timeout pending for Open? */ + f->state == ACKRCVD || + f->state == ACKSENT) + UNTIMEOUT(fsm_timeout, (caddr_t) f); /* Cancel timeout */ + if (f->state == OPEN && /* Open? */ + f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers we're down */ + if (f->state == ACKSENT || /* Could peer be OPEN? */ + f->state == OPEN) { + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, NULL, 0); + /* Send Terminate-Request */ + TIMEOUT(fsm_timeout, (caddr_t) f, f->timeouttime); + f->state = TERMSENT; + f->retransmits = 0; /* Reset retransmits count */ + } + else { + f->state = CLOSED; + if (f->callbacks->closed) + (*f->callbacks->closed)(f); /* Exit/restart/etc. */ + } +} + + +/* + * fsm_timeout - Timeout expired. + */ +static void + fsm_timeout(arg) +caddr_t arg; +{ + fsm *f = (fsm *) arg; + switch (f->state) { + case REQSENT: + case ACKRCVD: + case ACKSENT: + if (f->flags & POPENDING) { /* Go passive? */ + f->state = CLOSED; /* Pretend for a moment... */ + fsm_passiveopen(f); + return; + } + if (f->retransmits > f->maxconfreqtransmits) { + if (f->nakloops > f->maxnakloops) { + syslog(LOG_INFO, "%s: timeout sending Config-Requests", + proto_name(f->protocol)); + } else + syslog(LOG_INFO, "%s: timed out. Config-Requests not accepted", + proto_name(f->protocol)); + + /* timeout sending config-requests */ + fsm_close(f); + + return; + } + if (f->callbacks->retransmit) /* If there is a retransmit rtn? */ + (*f->callbacks->retransmit)(f); + fsm_sconfreq(f); /* Send Configure-Request */ + TIMEOUT(fsm_timeout, (caddr_t) f, f->timeouttime); + f->state = REQSENT; + ++f->retransmits; + f->nakloops = 0; + break; + + case TERMSENT: + if (f->flags & POPENDING) { /* Go passive? */ + f->state = CLOSED; /* Pretend for a moment... */ + fsm_passiveopen(f); + return; + } + if (++f->retransmits > f->maxtermtransmits) { + /* + * We've waited for an ack long enough. Peer probably heard us. + */ + f->state = CLOSED; + if (f->callbacks->closed) + (*f->callbacks->closed)(f); /* Exit/restart/etc. */ + return; + } + if (f->callbacks->retransmit) /* If there is a retransmit rtn? */ + (*f->callbacks->retransmit)(f); + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, NULL, 0); + /* Send Terminate-Request */ + TIMEOUT(fsm_timeout, (caddr_t) f, f->timeouttime); + ++f->retransmits; + } +} + + +/* + * fsm_lowerup - The lower layer is up. + * + * Start Active or Passive Open if pending. + */ +void + fsm_lowerup(f) +fsm *f; +{ + f->flags |= LOWERUP; + if (f->flags & AOPENDING) /* Attempting Active-Open? */ + fsm_activeopen(f); /* Try it now */ + else if (f->flags & POPENDING) /* Attempting Passive-Open? */ + fsm_passiveopen(f); /* Try it now */ +} + + +/* + * fsm_lowerdown - The lower layer is down. + * + * Cancel all timeouts and inform upper layers. + */ +void + fsm_lowerdown(f) +fsm *f; +{ + f->flags &= ~LOWERUP; + if (f->state == REQSENT || /* Timeout pending? */ + f->state == ACKRCVD || + f->state == ACKSENT || + f->state == TERMSENT) + UNTIMEOUT(fsm_timeout, (caddr_t) f); /* Cancel timeout */ + if (f->state == OPEN && /* OPEN? */ + f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + f->state = CLOSED; + if (f->callbacks->closed) + (*f->callbacks->closed)(f); /* Exit/restart/etc. */ +} + + +/* + * fsm_protreject - Peer doesn't speak this protocol. + * + * Pretend that the lower layer went down. + */ +void + fsm_protreject(f) +fsm *f; +{ + fsm_lowerdown(f); +} + + +/* + * fsm_input - Input packet. + */ +void + fsm_input(f, inpacket, l) +fsm *f; +u_char *inpacket; +int l; +{ + u_char *inp, *outp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < HEADERLEN) { + FSMDEBUG((LOG_WARNING, "fsm_input(%x): Rcvd short header.", f->protocol)) + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < HEADERLEN) { + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd illegal length.", + f->protocol)) + return; + } + if (len > l) { + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd short packet.", + f->protocol)) + return; + } + len -= HEADERLEN; /* subtract header length */ + + /* + * Action depends on code. + */ + switch (code) { + case CONFREQ: + FSMDEBUG((LOG_INFO, "fsm_rconfreq(%x): Rcvd id %d.", + f->protocol, id)) + + if (f->state == TERMSENT) + return; + if (f->state == CLOSED) { + fsm_sdata(f, TERMACK, id, NULL, 0); + return; + } + if (f->state == OPEN && f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + if (f->state == OPEN || f->state == LISTEN) { + /* XXX Possibly need hold-down on OPEN? */ + fsm_sconfreq(f); /* Send Configure-Request */ + TIMEOUT(fsm_timeout, (caddr_t) f, f->timeouttime); + } + + if (f->callbacks->reqci) /* Check CI */ + code = (*f->callbacks->reqci)(f, inp, &len); + else if (len) + code = CONFREJ; /* Reject all CI */ + + len += HEADERLEN; /* add header length back on */ + + inp = inpacket; /* Reset to header */ + outp = outpacket_buf; /* get pointer to output buffer */ + MAKEHEADER(outp, f->protocol); /* paste in DLL header */ + BCOPY(inp, outp, len); /* copy input packet */ + PUTCHAR(code, outp); /* put in the code, id, and length*/ + PUTCHAR(id, outp); + PUTSHORT(len, outp); + output(f->unit, outpacket_buf, len + DLLHEADERLEN); /* send it out */ + + if (code == CONFACK) { + if (f->state == ACKRCVD) { + UNTIMEOUT(fsm_timeout, (caddr_t) f); /* Cancel timeout */ + if (f->callbacks->up) + (*f->callbacks->up)(f); /* Inform upper layers */ + f->state = OPEN; + } + else + f->state = ACKSENT; + } + else { + if (f->state != ACKRCVD) + f->state = REQSENT; + } + return; + + case CONFACK: + fsm_rconfack(f, inp, id, len); + break; + + case CONFNAK: + fsm_rconfnak(f, inp, id, len); + break; + + case CONFREJ: + fsm_rconfrej(f, inp, id, len); + break; + + case TERMREQ: + fsm_rtermreq(f, id); + break; + + case TERMACK: + fsm_rtermack(f); + break; + + case CODEREJ: + fsm_rcoderej(f, inp, len); + break; + + case PROTREJ: + fsm_rprotrej(f, inp, len); + break; + + case ECHOREQ: + FSMDEBUG((LOG_INFO, "lcp: Echo-Request, Rcvd id %d", id)); + + switch (f->state) { + case CLOSED: + case LISTEN: + fsm_sdata(f, TERMACK, id, NULL, 0); + break; + + case OPEN: + inp = inpacket; /* Reset to header */ + outp = outpacket_buf; /* get pointer to output buffer */ + MAKEHEADER(outp, f->protocol); /* add DLL header */ + len += HEADERLEN; /* add header length */ + BCOPY(inp, outp, len); /* copy input packet to output buffer */ + PUTCHAR(ECHOREP, outp); /* set code to echo reply */ + PUTCHAR(id, outp); /* add in id */ + PUTSHORT(len, outp); /* and length */ + output(f->unit, outpacket_buf, len + DLLHEADERLEN); /* send it */ + return; + } + break; + + case ECHOREP: + case DISCREQ: + /* XXX Deliver to ECHOREQ sender? */ + break; + + default: + fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN); + break; + } + +} + + +/* + * fsm_rconfack - Receive Configure-Ack. + */ +static void + fsm_rconfack(f, inp, id, len) +fsm *f; +u_char *inp; +u_char id; +int len; +{ + FSMDEBUG((LOG_INFO, "fsm_rconfack(%x): Rcvd id %d.", + f->protocol, id)) + + switch (f->state) { + case LISTEN: + case CLOSED: + fsm_sdata(f, TERMACK, id, NULL, 0); + break; + + case ACKRCVD: + case REQSENT: + if (id != f->reqid) /* Expected id? */ + break; /* Nope, toss... */ + if (f->callbacks->ackci && + (*f->callbacks->ackci)(f, inp, len)) /* Good ack? */ + f->state = ACKRCVD; + else + f->state = REQSENT; /* Wait for timeout to retransmit */ + break; + + case ACKSENT: + if (id != f->reqid) /* Expected id? */ + break; /* Nope, toss... */ + if (f->callbacks->ackci && + (*f->callbacks->ackci)(f, inp, len)) { /* Good ack? */ + UNTIMEOUT(fsm_timeout, (caddr_t) f); /* Cancel timeout */ + if (f->callbacks->up) + (*f->callbacks->up)(f); /* Inform upper layers */ + f->state = OPEN; + } + else + f->state = REQSENT; /* Wait for timeout to retransmit */ + break; + + case OPEN: + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + f->state = CLOSED; /* Only for a moment... */ + fsm_activeopen(f); /* Restart */ + break; + } +} + + +/* + * fsm_rconfnak - Receive Configure-Nak. + */ +static void + fsm_rconfnak(f, inp, id, len) +fsm *f; +u_char *inp; +u_char id; +int len; +{ + FSMDEBUG((LOG_INFO, "fsm_rconfnak(%x): Rcvd id %d.", + f->protocol, id)) + + switch (f->state) { + case LISTEN: + case CLOSED: + fsm_sdata(f, TERMACK, id, NULL, 0); + break; + + case REQSENT: + case ACKSENT: + if (id != f->reqid) /* Expected id? */ + break; /* Nope, toss... */ + if (++f->nakloops > f->maxnakloops) { + FSMDEBUG((LOG_INFO, + "fsm_rconfnak(%x): Possible CONFNAK loop!", + f->protocol)) + break; /* Break the loop */ + } + UNTIMEOUT(fsm_timeout, (caddr_t) f); /* Cancel timeout */ + if (f->callbacks->nakci) + (*f->callbacks->nakci)(f, inp, len); + fsm_sconfreq(f); /* Send Configure-Request */ + TIMEOUT(fsm_timeout, (caddr_t) f, f->timeouttime); + ++f->retransmits; + break; + + case ACKRCVD: + f->state = REQSENT; /* Wait for timeout to retransmit */ + break; + + case OPEN: + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + f->state = CLOSED; /* Only for a moment... */ + fsm_activeopen(f); /* Restart */ + break; + } +} + + +/* + * fsm_rconfrej - Receive Configure-Rej. + */ +static void + fsm_rconfrej(f, inp, id, len) +fsm *f; +u_char *inp; +u_char id; +int len; +{ + FSMDEBUG((LOG_INFO, "fsm_rconfrej(%x): Rcvd id %d.", + f->protocol, id)) + + switch (f->state) { + case LISTEN: + case CLOSED: + fsm_sdata(f, TERMACK, id, NULL, 0); + break; + + case REQSENT: + case ACKSENT: + if (id != f->reqid) /* Expected id? */ + break; /* Nope, toss... */ + if (++f->nakloops > f->maxnakloops) + break; /* Break the loop */ + UNTIMEOUT(fsm_timeout, (caddr_t) f); /* Cancel timeout */ + if (f->callbacks->rejci) + (*f->callbacks->rejci)(f, inp, len); + fsm_sconfreq(f); /* Send Configure-Request */ + TIMEOUT(fsm_timeout, (caddr_t) f, f->timeouttime); + ++f->retransmits; + break; + + case ACKRCVD: + f->state = REQSENT; /* Wait for timeout to retransmit */ + break; + + case OPEN: + f->state = CLOSED; /* Only for a moment... */ + fsm_activeopen(f); /* Restart */ + break; + } +} + + +/* + * fsm_rtermreq - Receive Terminate-Req. + */ +static void + fsm_rtermreq(f, id) +fsm *f; +u_char id; +{ + FSMDEBUG((LOG_INFO, "fsm_rtermreq(%x): Rcvd id %d.", + f->protocol, id)) + + fsm_sdata(f, TERMACK, id, NULL, 0); + switch (f->state) { + case ACKRCVD: + case ACKSENT: + f->state = REQSENT; /* Start over but keep trying */ + break; + + case OPEN: + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + f->state = CLOSED; + if (f->callbacks->closed) + (*f->callbacks->closed)(f); /* Exit/restart/etc. */ + break; + } +} + + +/* + * fsm_rtermack - Receive Terminate-Ack. + */ +static void + fsm_rtermack(f) +fsm *f; +{ + FSMDEBUG((LOG_INFO, "fsm_rtermack(%x).", f->protocol)) + + switch (f->state) { + case OPEN: + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + f->state = CLOSED; + if (f->callbacks->closed) + (*f->callbacks->closed)(f); /* Exit/restart/etc. */ + break; + + case TERMSENT: + UNTIMEOUT(fsm_timeout, (caddr_t) f); /* Cancel timeout */ + f->state = CLOSED; + if (f->callbacks->closed) + (*f->callbacks->closed)(f); /* Exit/restart/etc. */ + break; + } +} + + +/* + * fsm_rcoderej - Receive an Code-Reject. + */ +static void + fsm_rcoderej(f, inp, len) +fsm *f; +u_char *inp; +int len; +{ + u_char code; + + FSMDEBUG((LOG_INFO, "fsm_rcoderej(%x).", f->protocol)) + + if (len < sizeof (u_char)) { + FSMDEBUG((LOG_INFO, + "fsm_rcoderej: Rcvd short Code-Reject packet!")) + return; + } + GETCHAR(code, inp); + FSMDEBUG((LOG_INFO, + "fsm_rcoderej: Rcvd Code-Reject for code %d!", + code)) +} + + +/* + * fsm_rprotrej - Receive an Protocol-Reject. + * + * Figure out which protocol is rejected and inform it. + */ +static void + fsm_rprotrej(f, inp, len) +fsm *f; +u_char *inp; +int len; +{ + u_short prot; + + FSMDEBUG((LOG_INFO, "fsm_rprotrej.")) + + if (len < sizeof (u_short)) { + FSMDEBUG((LOG_INFO, + "fsm_rprotrej: Rcvd short Protocol-Reject packet!")) + return; + } + if (f->protocol != LCP) { /* Only valid for LCP */ + FSMDEBUG((LOG_INFO, + "fsm_rprotrej: Rcvd non-LCP Protocol-Reject!")) + return; + } + + GETSHORT(prot, inp); + + FSMDEBUG((LOG_INFO, + "fsm_rprotrej: Rcvd Protocol-Reject packet for %x!", + prot)) + DEMUXPROTREJ(f->unit, prot); /* Inform protocol */ +} + + +/* + * fsm_sconfreq - Send a Configure-Request. + */ +static void + fsm_sconfreq(f) +fsm *f; +{ + u_char *outp; + int outlen; + + outlen = HEADERLEN + (f->callbacks->cilen ? (*f->callbacks->cilen)(f) : 0); + /* XXX Adjust outlen to MTU */ + outp = outpacket_buf; + MAKEHEADER(outp, f->protocol); + + PUTCHAR(CONFREQ, outp); + PUTCHAR(f->reqid = ++f->id, outp); + PUTSHORT(outlen, outp); + if (f->callbacks->cilen && f->callbacks->addci) + (*f->callbacks->addci)(f, outp); + output(f->unit, outpacket_buf, outlen + DLLHEADERLEN); + + FSMDEBUG((LOG_INFO, "%s: sending Configure-Request, id %d", + proto_name(f->protocol), f->reqid)) +} + + +/* + * fsm_sdata - Send some data. + * + * Used for Terminate-Request, Terminate-Ack, Code-Reject, Protocol-Reject, + * Echo-Request, and Discard-Request. + */ +void + fsm_sdata(f, code, id, data, datalen) +fsm *f; +u_char code, id; +u_char *data; +int datalen; +{ + u_char *outp; + int outlen; + + /* Adjust length to be smaller than MTU */ + if (datalen > MTU - HEADERLEN) + datalen = MTU - HEADERLEN; + outlen = datalen + HEADERLEN; + outp = outpacket_buf; + MAKEHEADER(outp, f->protocol); + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + if (datalen) + BCOPY(data, outp, datalen); + output(f->unit, outpacket_buf, outlen + DLLHEADERLEN); + + FSMDEBUG((LOG_INFO, "fsm_sdata(%x): Sent code %d, id %d.", + f->protocol, code, id)) +} diff --git a/libexec/pppd/fsm.h b/libexec/pppd/fsm.h new file mode 100644 index 0000000000..82af3039ca --- /dev/null +++ b/libexec/pppd/fsm.h @@ -0,0 +1,118 @@ +/* + * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * Packet header = Code, id, length. + */ +#define HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * CP (LCP, IPCP, etc.) codes. + */ +#define CONFREQ 1 /* Configuration Request */ +#define CONFACK 2 /* Configuration Ack */ +#define CONFNAK 3 /* Configuration Nak */ +#define CONFREJ 4 /* Configuration Reject */ +#define TERMREQ 5 /* Termination Request */ +#define TERMACK 6 /* Termination Ack */ +#define CODEREJ 7 /* Code Reject */ +#define PROTREJ 8 /* Protocol Reject */ +#define ECHOREQ 9 /* Echo Request */ +#define ECHOREP 10 /* Echo Reply */ +#define DISCREQ 11 /* Discard Request */ +#define KEEPALIVE 12 /* Keepalive */ + + +/* + * Each FSM is described by a fsm_callbacks and a fsm structure. + */ +typedef struct fsm_callbacks { + void (*resetci)(); /* Reset our Configuration Information */ + int (*cilen)(); /* Length of our Configuration Information */ + void (*addci)(); /* Add our Configuration Information */ + int (*ackci)(); /* ACK our Configuration Information */ + void (*nakci)(); /* NAK our Configuration Information */ + void (*rejci)(); /* Reject our Configuration Information */ + u_char (*reqci)(); /* Request peer's Configuration Information */ + void (*up)(); /* Called when fsm reaches OPEN state */ + void (*down)(); /* Called when fsm leaves OPEN state */ + void (*closed)(); /* Called when fsm reaches CLOSED state */ + void (*protreject)(); /* Called when Protocol-Reject received */ + void (*retransmit)(); /* Retransmission is necessary */ +} fsm_callbacks; + + +typedef struct fsm { + int unit; /* Interface unit number */ + u_short protocol; /* Data Link Layer Protocol field value */ + int state; /* State */ + int flags; /* Flags */ + u_char id; /* Current id */ + u_char reqid; /* Current request id */ + int timeouttime; /* Timeout time in milliseconds */ + int maxconfreqtransmits; /* Maximum Configure-Request transmissions */ + int retransmits; /* Number of retransmissions */ + int maxtermtransmits; /* Maximum Terminate-Request transmissions */ + int nakloops; /* Number of nak loops since last timeout */ + int maxnakloops; /* Maximum number of nak loops tolerated */ + fsm_callbacks *callbacks; /* Callback routines */ +} fsm; + + +/* + * Link states. + */ +#define CLOSED 1 /* Connection closed */ +#define LISTEN 2 /* Listening for a Config Request */ +#define REQSENT 3 /* We've sent a Config Request */ +#define ACKSENT 4 /* We've sent a Config Ack */ +#define ACKRCVD 5 /* We've received a Config Ack */ +#define OPEN 6 /* Connection open */ +#define TERMSENT 7 /* We've sent a Terminate Request */ + + +/* + * Flags. + */ +#define LOWERUP 1 /* The lower level is UP */ +#define AOPENDING 2 /* Active Open pending timeout of request */ +#define POPENDING 4 /* Passive Open pending timeout of request */ + + +/* + * Timeouts. + */ +#define DEFTIMEOUT 3 /* Timeout time in seconds */ +#define DEFMAXTERMTRANSMITS 10 /* Maximum Terminate-Request transmissions */ +#define DEFMAXCONFIGREQS 10 /* Maximum Configure-Request transmissions */ + + +#define DEFMAXNAKLOOPS 10 /* Maximum number of nak loops */ + + +void fsm_init __ARGS((fsm *)); +void fsm_activeopen __ARGS((fsm *)); +void fsm_passiveopen __ARGS((fsm *)); +void fsm_close __ARGS((fsm *)); +void fsm_lowerup __ARGS((fsm *)); +void fsm_lowerdown __ARGS((fsm *)); +void fsm_protreject __ARGS((fsm *)); +void fsm_input __ARGS((fsm *, u_char *, int)); +void fsm_sdata __ARGS((fsm *, int, int, u_char *, int)); diff --git a/libexec/pppd/ipcp.c b/libexec/pppd/ipcp.c new file mode 100644 index 0000000000..a805a3b612 --- /dev/null +++ b/libexec/pppd/ipcp.c @@ -0,0 +1,968 @@ +/* + * ipcp.c - PPP IP Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * TODO: + * Fix IP address negotiation (wantoptions or hisoptions). + * Don't set zero IP addresses. + * Send NAKs for unsent CIs. + * VJ compression. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#ifndef BSD +#ifndef sun +#define BSD 44 +#endif +#endif /*BSD*/ + +#ifdef STREAMS +#include +#include "ppp_str.h" +#endif + +#include "pppd.h" +#include + +#include +#include "fsm.h" +#include "ipcp.h" + + +/* global vars */ +ipcp_options ipcp_wantoptions[NPPP]; /* Options that we want to request */ +ipcp_options ipcp_gotoptions[NPPP]; /* Options that peer ack'd */ +ipcp_options ipcp_allowoptions[NPPP]; /* Options that we allow peer to + request */ +ipcp_options ipcp_hisoptions[NPPP]; /* Options that we ack'd */ + +/* local vars */ + +/* + * VJ compression protocol mode for negotiation. See ipcp.h for a + * description of each mode. + */ +static int vj_mode = IPCP_VJMODE_RFC1332; + +static int vj_opt_len = 6; /* holds length in octets for valid vj */ + /* compression frame depending on mode */ + +static int vj_opt_val = IPCP_VJ_COMP; + /* compression negotiation frames */ + /* depending on vj_mode */ + +static void ipcp_resetci __ARGS((fsm *)); /* Reset our Configuration Information */ +static int ipcp_cilen __ARGS((fsm *)); /* Return length of our CI */ +static void ipcp_addci __ARGS((fsm *, u_char *)); /* Add our CIs */ +static int ipcp_ackci __ARGS((fsm *, u_char *, int)); /* Ack some CIs */ +static void ipcp_nakci __ARGS((fsm *, u_char *, int)); /* Nak some CIs */ +static void ipcp_rejci __ARGS((fsm *, u_char *, int)); /* Reject some CIs */ +static u_char ipcp_reqci __ARGS((fsm *, u_char *, int *)); /* Check the requested CIs */ +static void ipcp_up __ARGS((fsm *)); /* We're UP */ +static void ipcp_down __ARGS((fsm *)); /* We're DOWN */ + + +static fsm ipcp_fsm[NPPP]; /* IPCP fsm structure */ + +static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */ + ipcp_resetci, /* Reset our Configuration Information */ + ipcp_cilen, /* Length of our Configuration Information */ + ipcp_addci, /* Add our Configuration Information */ + ipcp_ackci, /* ACK our Configuration Information */ + ipcp_nakci, /* NAK our Configuration Information */ + ipcp_rejci, /* Reject our Configuration Information */ + ipcp_reqci, /* Request peer's Configuration Information */ + ipcp_up, /* Called when fsm reaches OPEN state */ + ipcp_down, /* Called when fsm leaves OPEN state */ + NULL, /* Called when fsm reaches CLOSED state */ + NULL, /* Called when Protocol-Reject received */ + NULL /* Retransmission is necessary */ +}; + +char * +ip_ntoa(ipaddr) +u_long ipaddr; +{ + static char b1[64], b2[64], w = 0; + char *b = (w++&1) ? b1 : b2; + + ipaddr = ntohl(ipaddr); + + sprintf(b, "%d.%d.%d.%d", + (u_char)(ipaddr >> 24), + (u_char)(ipaddr >> 16), + (u_char)(ipaddr >> 8), + (u_char)(ipaddr)); + return b; +} + +/* + * ipcp_init - Initialize IPCP. + */ +void + ipcp_init(unit) +int unit; +{ + fsm *f = &ipcp_fsm[unit]; + ipcp_options *wo = &ipcp_wantoptions[unit]; + ipcp_options *ao = &ipcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = IPCP; + f->timeouttime = DEFTIMEOUT; + f->maxconfreqtransmits = DEFMAXCONFIGREQS; + f->maxtermtransmits = DEFMAXTERMTRANSMITS; + f->maxnakloops = DEFMAXNAKLOOPS; + f->callbacks = &ipcp_callbacks; + + wo->neg_addrs = 1; + wo->ouraddr = 0; + wo->hisaddr = 0; + + wo->neg_vj = 1; + wo->maxslotindex = MAX_STATES - 1; /* really max index */ + wo->cflag = 1; + + /* max slots and slot-id compression are currently hardwired in */ + /* ppp_if.c to 16 and 1, this needs to be changed (among other */ + /* things) gmc */ + + ao->neg_addrs = 1; /* accept old style dual addr */ + ao->neg_addr = 1; /* accept new style single addr */ + ao->neg_vj = 1; + ao->maxslotindex = MAX_STATES - 1; + ao->cflag = 1; + fsm_init(&ipcp_fsm[unit]); +} + +/* + * ipcp_vj_setmode - set option length and option value for vj + * compression negotiation frames depending on mode + */ + +void + ipcp_vj_setmode(mode) +int mode; +{ + vj_mode = mode; + + switch (vj_mode) { + + case IPCP_VJMODE_OLD: /* with wrong code (0x0037) */ + vj_opt_len = 4; + vj_opt_val = IPCP_VJ_COMP_OLD; + break; + + case IPCP_VJMODE_RFC1172: /* as per rfc1172 */ + vj_opt_len = 4; + vj_opt_val = IPCP_VJ_COMP; + break; + + case IPCP_VJMODE_RFC1332: /* draft mode vj compression */ + vj_opt_len = 6; /* negotiation includes values for */ + /* maxslot and slot number compression */ + vj_opt_val = IPCP_VJ_COMP; + break; + + default: + IPCPDEBUG((LOG_WARNING, "Unknown vj compression mode %d. Please report \ +this error.", vj_mode)) + break; + } + +} +/* + * ipcp_activeopen - Actively open IPCP. + */ +void + ipcp_activeopen(unit) +int unit; +{ + fsm_activeopen(&ipcp_fsm[unit]); +} + + +/* + * ipcp_passiveopen - Passively open IPCP. + */ +void ipcp_passiveopen(unit) + int unit; +{ + fsm_passiveopen(&ipcp_fsm[unit]); +} + + +/* + * ipcp_close - Close IPCP. + */ +void + ipcp_close(unit) +int unit; +{ + fsm_close(&ipcp_fsm[unit]); +} + + +/* + * ipcp_lowerup - The lower layer is up. + */ +void + ipcp_lowerup(unit) +int unit; +{ + fsm_lowerup(&ipcp_fsm[unit]); +} + + +/* + * ipcp_lowerdown - The lower layer is down. + */ +void + ipcp_lowerdown(unit) +int unit; +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_input - Input IPCP packet. + */ +void + ipcp_input(unit, p, len) +int unit; +u_char *p; +int len; +{ + fsm_input(&ipcp_fsm[unit], p, len); +} + + +/* + * ipcp_protrej - A Protocol-Reject was received for IPCP. + * + * Simply pretend that LCP went down. + */ +void + ipcp_protrej(unit) +int unit; +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_resetci - Reset our CI. + */ +static void + ipcp_resetci(f) +fsm *f; +{ + ipcp_gotoptions[f->unit] = ipcp_wantoptions[f->unit]; +} + + +/* + * ipcp_cilen - Return length of our CI. + */ +static int + ipcp_cilen(f) +fsm *f; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + + +#define LENCISHORT(neg) (neg ? vj_opt_len : 0) + +#define LENCIADDRS(neg) (neg ? 10 : 0) + +#define LENCIADDR(neg) (neg ? 6 : 0) + + return (LENCIADDRS(go->neg_addrs) + + LENCIADDR(go->neg_addr) + + LENCISHORT(go->neg_vj)); +} + + +/* + * ipcp_addci - Add our desired CIs to a packet. + */ +static void + ipcp_addci(f, ucp) +fsm *f; +u_char *ucp; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + + +#define ADDCISHORT(opt, neg, val, maxslotindex, cflag) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(vj_opt_len, ucp); \ + PUTSHORT(val, ucp); \ + if (vj_mode == IPCP_VJMODE_RFC1332) { \ + PUTCHAR(maxslotindex, ucp); \ + PUTCHAR(cflag, ucp); \ + } \ + } + +#define ADDCIADDRS(opt, neg, val1, val2) \ + if (neg) { \ + u_long l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(2 + 2 * sizeof (long), ucp); \ + l = ntohl(val1); \ + PUTLONG(l, ucp); \ + l = ntohl(val2); \ + PUTLONG(l, ucp); \ + } + +#define ADDCIADDR(opt, neg, val) \ + if (neg) { \ + u_long l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(2 + sizeof (long), ucp); \ + l = ntohl(val); \ + PUTLONG(l, ucp); \ + } + + ADDCIADDRS(CI_ADDRS, go->neg_addrs, go->ouraddr, go->hisaddr) + + ADDCIADDR(CI_ADDR, go->neg_addr, go->ouraddr) + + ADDCISHORT(CI_COMPRESSTYPE, go->neg_vj, vj_opt_val, + go->maxslotindex, go->cflag) +} + + +/* + * ipcp_ackci - Ack our CIs. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int + ipcp_ackci(f, p, len) +fsm *f; +u_char *p; +int len; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_short cilen, citype, cishort; + u_long cilong; + u_char cimaxslotindex, cicflag; + /* + * CIs must be in exactly the same order that we sent... + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define ACKCISHORT(opt, neg, val, maxslotindex, cflag) \ + if (neg) { \ + if ((len -= vj_opt_len) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != vj_opt_len || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + if (vj_mode == IPCP_VJMODE_RFC1332) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex > maxslotindex) \ + goto bad; \ + GETCHAR(cicflag, p); \ + if (cicflag != cflag) \ + goto bad; \ + } \ + } + +#define ACKCIADDRS(opt, neg, val1, val2) \ + if (neg) { \ + u_long l; \ + if ((len -= 2 + 2 * sizeof (long)) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != 2 + 2 * sizeof (long) || \ + citype != opt) \ + goto bad; \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val1) { \ + if (val1 != cilong) \ + goto bad; \ + } \ + else \ + val1 = cilong; \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val2) { \ + if (val2 != cilong) \ + goto bad; \ + } \ + else \ + val2 = cilong; \ + } + +#define ACKCIADDR(opt, neg, val) \ + if (neg) { \ + u_long l; \ + if ((len -= 2 + sizeof (long)) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != 2 + sizeof (long) || \ + citype != opt) \ + goto bad; \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val) { \ + if (val != cilong) \ + goto bad; \ + } \ + else \ + val = cilong; \ + } + + ACKCIADDRS(CI_ADDRS, go->neg_addrs, go->ouraddr, go->hisaddr) + ACKCIADDR(CI_ADDR, go->neg_addr, go->ouraddr) + ACKCISHORT(CI_COMPRESSTYPE, go->neg_vj, vj_opt_val, go->maxslotindex, go->cflag) + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + return (1); + +bad: + IPCPDEBUG((LOG_INFO, "ipcp_ackci: received bad Ack!")); + + if (vj_mode == IPCP_VJMODE_RFC1332 ) + IPCPDEBUG((LOG_INFO, "ipcp_ackci: citype %d, cilen %l", + citype, cilen)); + + if (citype == CI_COMPRESSTYPE) { + IPCPDEBUG((LOG_INFO, "ipcp_ackci: compress_type %d", cishort)); + if (vj_mode == IPCP_VJMODE_RFC1332) + IPCPDEBUG((LOG_INFO, ", maxslotindex %d, cflag %d", + cishort, cimaxslotindex, cicflag)); + } + return (0); +} + +/* + * ipcp_nakci - NAK some of our CIs. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static void + ipcp_nakci(f, p, len) +fsm *f; +u_char *p; +int len; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, cicflag; + u_short cishort; + u_long ciaddr1, ciaddr2; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCISHORT(opt, neg, code) \ + if (neg && \ + len >= vj_opt_len && \ + p[1] == vj_opt_len && \ + p[0] == opt) { \ + len -= vj_opt_len; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + if (vj_mode == IPCP_VJMODE_RFC1332) { \ + GETCHAR(cimaxslotindex, p); \ + GETCHAR(cicflag, p); \ + } \ + code \ + } + +#define NAKCIADDRS(opt, neg, code) \ + if (neg && \ + len >= 2 + 2 * sizeof (long) && \ + p[1] == 2 + 2 * sizeof (long) && \ + p[0] == opt) { \ + u_long l; \ + len -= 2 + 2 * sizeof (long); \ + INCPTR(2, p); \ + GETLONG(l, p); \ + ciaddr1 = htonl(l); \ + GETLONG(l, p); \ + ciaddr2 = htonl(l); \ + code \ + } + +#define NAKCIADDR(opt, neg, code) \ + if (neg && \ + len >= 2 + sizeof (long) && \ + p[1] == 2 + sizeof (long) && \ + p[0] == opt) { \ + u_long l; \ + len -= 2 + sizeof (long); \ + INCPTR(2, p); \ + GETLONG(l, p); \ + ciaddr1 = htonl(l); \ + code \ + } + + NAKCIADDRS(CI_ADDRS, go->neg_addrs, + if (!go->ouraddr) { /* Didn't know our address? */ + syslog(LOG_INFO, "local IP address %s", ip_ntoa(ciaddr1)); + go->ouraddr = ciaddr1; + } + if (ciaddr2) { /* Does he know his? */ + go->hisaddr = ciaddr2; + syslog(LOG_INFO, "remote IP address %s", ip_ntoa(ciaddr2)); + } + ) + + NAKCIADDR(CI_ADDR, go->neg_addr, + logf(LOG_INFO, "acquired IP address %s", ip_ntoa(ciaddr1)); + if (!go->ouraddr) { /* Didn't know our address? */ + go->ouraddr = ciaddr1; + syslog(LOG_INFO, "remote IP address %s", ip_ntoa(ciaddr1)); + } + ) + + NAKCISHORT(CI_COMPRESSTYPE, go->neg_vj, + if (cishort != vj_opt_val) + goto bad; + go->maxslotindex = cimaxslotindex; /* this is what it */ + go->cflag = cicflag; /* wants */ + + ) + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len == 0) + return; +bad: + IPCPDEBUG((LOG_INFO, "ipcp_nakci: received bad Nak!")); +} + + +/* + * ipcp_rejci - Reject some of our CIs. + */ +static void + ipcp_rejci(f, p, len) +fsm *f; +u_char *p; +int len; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, ciflag; + u_short cishort; + u_long cilong; + + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCISHORT(opt, neg, val, maxslot, cflag) \ + if (neg && \ + len >= vj_opt_len && \ + p[1] == vj_opt_len && \ + p[0] == opt) { \ + len -= vj_opt_len; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) \ + goto bad; \ + if (vj_mode == IPCP_VJMODE_RFC1332) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslot) \ + goto bad; \ + GETCHAR(ciflag, p); \ + if (ciflag != cflag) \ + goto bad; \ + } \ + neg = 0; \ + } + +#define REJCIADDRS(opt, neg, val1, val2) \ + if (neg && \ + len >= 2 + 2 * sizeof (long) && \ + p[1] == 2 + 2 * sizeof (long) && \ + p[0] == opt) { \ + u_long l; \ + len -= 2 + 2 * sizeof (long); \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val2) \ + goto bad; \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val1) \ + goto bad; \ + neg = 0; \ + } + +#define REJCIADDR(opt, neg, val) \ + if (neg && \ + len >= 2 + sizeof (long) && \ + p[1] == 2 + sizeof (long) && \ + p[0] == opt) { \ + u_long l; \ + len -= 2 + sizeof (long); \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val) \ + goto bad; \ + neg = 0; \ + } + + REJCIADDRS(CI_ADDRS, go->neg_addrs, go->ouraddr, go->hisaddr) + + REJCIADDR(CI_ADDR, go->neg_addr, go->ouraddr) + + REJCISHORT(CI_COMPRESSTYPE, go->neg_vj, vj_opt_val, go->maxslotindex, go->cflag) + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len == 0) + return; + +bad: + IPCPDEBUG((LOG_INFO, "ipcp_rejci: received bad Reject!")); +} + + +/* + * ipcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. + */ +static u_char + ipcp_reqci(f, inp, len) +fsm *f; +u_char *inp; /* Requested CIs */ +int *len; /* Length of requested CIs */ +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *ao = &ipcp_allowoptions[f->unit]; + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char *cip; /* Pointer to Current CI */ + u_short cilen, citype; /* Parsed len, type */ + u_short cishort; /* Parsed short value */ + u_long tl, ciaddr1, ciaddr2; /* Parsed address values */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p = inp; /* Pointer to next char to parse */ + u_char *ucp = inp; /* Pointer to current output char */ + int l = *len; /* Length left */ + u_char maxslotindex, cflag; + + /* + * Reset all his options. + */ + ho->neg_addrs = 0; + ho->neg_vj = 0; + ho->maxslotindex = 0; + ho->cflag = 0; + + /* + * Process all his options. + */ + while (l) { + orc = CONFACK; /* Assume success */ + cip = p; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: bad CI length!")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + cilen -= 2; /* Adjust cilen to just data */ + + switch (citype) { /* Check CI type */ + case CI_ADDRS: + logf(LOG_INFO, "ipcp: received ADDRS "); + if (!ao->neg_addrs || + cilen != 2 * sizeof (long)) + { /* Check CI length */ + INCPTR(cilen, p); /* Skip rest of CI */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + if (!ciaddr1 || + (wo->neg_addrs && wo->hisaddr && ciaddr1 != wo->hisaddr)) + { + orc = CONFNAK; + DECPTR(sizeof (long), p); + tl = wo->neg_addrs ? ntohl(wo->hisaddr) : 0; + PUTLONG(tl, p); + } + + /* + * If he doesn't know our address, or if we both have our address + * but disagree about it, then NAK it with our idea. + */ + GETLONG(tl, p); /* Parse desination address (ours) */ + ciaddr2 = htonl(tl); + logf(LOG_INFO, "(%s:%s)", ip_ntoa(ciaddr1), ip_ntoa(ciaddr2)); + if (!ciaddr2 || + (wo->neg_addrs && wo->ouraddr && ciaddr2 != wo->ouraddr)) + { + orc = CONFNAK; + DECPTR(sizeof (long), p); + tl = ntohl(wo->ouraddr); + PUTLONG(tl, p); + } + if (orc == CONFNAK) + break; + + /* XXX ho or go? */ + ho->neg_addrs = 1; + ho->hisaddr = ciaddr1; + ho->ouraddr = ciaddr2; + break; + + case CI_ADDR: + logf(LOG_INFO, "ipcp: received ADDR "); + go->got_addr = 1; + go->neg_addrs = 0; + go->neg_addr = 1; + + if (!ao->neg_addr || + cilen != sizeof (long)) { /* Check CI length */ + INCPTR(cilen, p); /* Skip rest of CI */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + logf(LOG_INFO, "(%s)", ip_ntoa(ciaddr1)); + if (!ciaddr1 || + (wo->neg_addr && wo->hisaddr && ciaddr1 != wo->hisaddr)) { + orc = CONFNAK; + DECPTR(sizeof (long), p); + tl = wo->neg_addr ? ntohl(wo->hisaddr) : 0; + PUTLONG(tl, p); + } + + if (orc == CONFNAK) + break; + + /* XXX ho or go? */ + ho->neg_addr = 1; + ho->hisaddr = ciaddr1; + break; + + case CI_COMPRESSTYPE: + logf(LOG_INFO, "ipcp: received COMPRESSTYPE "); + if (!ao->neg_vj || + cilen != (vj_opt_len - 2)) { + INCPTR(cilen, p); + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + logf(LOG_INFO, "(%d)", cishort); + + /* + * Compresstype must be vj_opt_val. + */ + if (cishort != vj_opt_val) { + DECPTR(sizeof (short), p); + orc = CONFNAK; + PUTSHORT(vj_opt_val, p); + break; + } + ho->neg_vj = 1; + if (vj_mode == IPCP_VJMODE_RFC1332) { + GETCHAR(maxslotindex, p); + if (maxslotindex > wo->maxslotindex) { + DECPTR(1, p); + orc = CONFNAK; + PUTCHAR(wo->maxslotindex, p); + break; + } + ho->maxslotindex = maxslotindex; + + GETCHAR(cflag, p); + if (cflag != wo->cflag) { + DECPTR(1, p); + orc = CONFNAK; + PUTCHAR(wo->cflag, p); + break; + } + ho->cflag = wo->cflag; + } + break; + + default: + INCPTR(cilen, p); + orc = CONFREJ; + break; + } + cilen += 2; /* Adjust cilen whole CI */ + +endswitch: + logf(LOG_INFO, " (%s)\n", + orc == CONFACK ? "ACK" : (orc == CONFNAK ? "NAK" : "Reject")); + + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) /* but prior CI wasnt? */ + continue; /* Don't send this one */ + + if (orc == CONFNAK) { /* Nak this CI? */ + if (rc == CONFREJ) /* Rejecting prior CI? */ + continue; /* Don't send this one */ + if (rc == CONFACK) { /* Ack'd all prior CIs? */ + rc = CONFNAK; /* Not anymore... */ + ucp = inp; /* Backup */ + } + } + + if (orc == CONFREJ && /* Reject this CI */ + rc != CONFREJ) { /* but no prior ones? */ + rc = CONFREJ; + ucp = inp; /* Backup */ + } + + /* Need to move CI? */ + if (ucp != cip) + /* Move it */ + memcpy(ucp, cip, (size_t)cilen); + + /* Update output pointer */ + INCPTR(cilen, ucp); + } + + /* + * XXX If we wanted to send additional NAKs (for unsent CIs), the + * code would go here. This must be done with care since it might + * require a longer packet than we received. + */ + + *len = ucp - inp; /* Compute output length */ + + syslog(LOG_INFO, "ipcp: returning Configure-%s", + rc == CONFACK ? "ACK" : + rc == CONFNAK ? "NAK" : "Reject"); + + return (rc); /* Return final code */ +} + + +/* + * ipcp_up - IPCP has come UP. + */ +static void + ipcp_up(f) +fsm *f; +{ + u_long mask; + + syslog(LOG_INFO, "ipcp: up"); + + if (ipcp_hisoptions[f->unit].hisaddr == 0) + ipcp_hisoptions[f->unit].hisaddr = ipcp_wantoptions[f->unit].hisaddr; + + syslog(LOG_INFO, "local IP address %s", + ip_ntoa(ipcp_gotoptions[f->unit].ouraddr)); + syslog(LOG_INFO, "remote IP address %s", + ip_ntoa(ipcp_hisoptions[f->unit].hisaddr)); + + SIFADDR(f->unit, ipcp_gotoptions[f->unit].ouraddr, + ipcp_hisoptions[f->unit].hisaddr); + + /* set new netmask if specified */ + mask = GetMask(ipcp_gotoptions[f->unit].ouraddr); + if (mask) + SIFMASK(f->unit, mask); + + /* set tcp compression */ + SIFVJCOMP(f->unit, ipcp_hisoptions[f->unit].neg_vj); +} + + +/* + * ipcp_down - IPCP has gone DOWN. + * + * Alert other protocols. + */ +static void + ipcp_down(f) +fsm *f; +{ + syslog(LOG_INFO, "ipcp: down"); + + CIFADDR(f->unit, ipcp_gotoptions[f->unit].ouraddr, + ipcp_hisoptions[f->unit].hisaddr); +} diff --git a/libexec/pppd/ipcp.h b/libexec/pppd/ipcp.h new file mode 100644 index 0000000000..3bbec12442 --- /dev/null +++ b/libexec/pppd/ipcp.h @@ -0,0 +1,63 @@ +/* + * ipcp.h - IP Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * Options. + */ +#define CI_ADDRS 1 /* IP Addresses */ +#define CI_COMPRESSTYPE 2 /* Compression Type */ +#define CI_ADDR 3 + +#define MAX_STATES 16 /* from slcompress.h */ + +#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */ +#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */ +#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */ + /* maxslot and slot number */ + /* compression from Aug. 1991 */ + /* ipcp draft RFC) */ + +#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option*/ +#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */ + /* compression option*/ + +typedef struct ipcp_options { + int neg_addrs : 1; /* Negotiate IP Addresses? */ + int neg_addr : 1; /* Negotiate IP Address? */ + int got_addr : 1; /* Got IP Address? */ + u_long ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */ + int neg_vj : 1; /* Van Jacobson Compression? */ + u_char maxslotindex, cflag; /* fields for Aug. 1991 Draft VJ */ + /* compression negotiation */ +} ipcp_options; + +extern ipcp_options ipcp_wantoptions[]; +extern ipcp_options ipcp_gotoptions[]; +extern ipcp_options ipcp_allowoptions[]; +extern ipcp_options ipcp_hisoptions[]; + +void ipcp_init __ARGS((int)); +void ipcp_vj_setmode __ARGS((int)); +void ipcp_activeopen __ARGS((int)); +void ipcp_passiveopen __ARGS((int)); +void ipcp_close __ARGS((int)); +void ipcp_lowerup __ARGS((int)); +void ipcp_lowerdown __ARGS((int)); +void ipcp_input __ARGS((int, u_char *, int)); +void ipcp_protrej __ARGS((int)); diff --git a/libexec/pppd/lcp.c b/libexec/pppd/lcp.c new file mode 100644 index 0000000000..d95a8faf57 --- /dev/null +++ b/libexec/pppd/lcp.c @@ -0,0 +1,1016 @@ +/* + * lcp.c - PPP Link Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * TODO: + * Keepalive. + * Send NAKs for unsent CIs. + * Keep separate MTU, MRU. + * Option tracing. + * Extra data on authtype option. + * Test restart. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#ifdef STREAMS +#include +#include "ppp_str.h" +#endif + +#include +#include "pppd.h" + +#include +#include "fsm.h" +#include "lcp.h" +#include "magic.h" +#include "chap.h" +#include "upap.h" +#include "ipcp.h" + +/* global vars */ +fsm lcp_fsm[NPPP]; /* LCP fsm structure (global)*/ +lcp_options lcp_wantoptions[NPPP]; /* Options that we want to request */ +lcp_options lcp_gotoptions[NPPP]; /* Options that peer ack'd */ +lcp_options lcp_allowoptions[NPPP]; /* Options that we allow peer to request */ +lcp_options lcp_hisoptions[NPPP]; /* Options that we ack'd */ + +/* local vars */ +static void lcp_resetci __ARGS((fsm *)); + /* Reset our Configuration Information */ +static int lcp_cilen __ARGS((fsm *)); /* Return length of our CI */ +static void lcp_addci __ARGS((fsm *, u_char *)); /* Add our CIs */ +static int lcp_ackci __ARGS((fsm *, u_char *, int)); /* Ack some CIs */ +static void lcp_nakci __ARGS((fsm *, u_char *, int)); /* Nak some CIs */ +static void lcp_rejci __ARGS((fsm *, u_char *, int)); + /* Reject some CIs */ +static u_char lcp_reqci __ARGS((fsm *, u_char *, int *)); + /* Check the requested CIs */ +static void lcp_up __ARGS((fsm *)); /* We're UP */ +static void lcp_down __ARGS((fsm *)); /* We're DOWN */ +static void lcp_closed __ARGS((fsm *)); /* We're CLOSED */ + +static fsm_callbacks lcp_callbacks = { /* LCP callback routines */ + lcp_resetci, /* Reset our Configuration Information */ + lcp_cilen, /* Length of our Configuration Information */ + lcp_addci, /* Add our Configuration Information */ + lcp_ackci, /* ACK our Configuration Information */ + lcp_nakci, /* NAK our Configuration Information */ + lcp_rejci, /* Reject our Configuration Information */ + lcp_reqci, /* Request peer's Configuration Information */ + lcp_up, /* Called when fsm reaches OPEN state */ + lcp_down, /* Called when fsm leaves OPEN state */ + lcp_closed, /* Called when fsm reaches CLOSED state */ + NULL, /* Called when Protocol-Reject received */ + NULL /* Retransmission is necessary */ +}; + + + + +#define DEFWARNLOOPS 10 /* XXX Move to lcp.h */ +static int lcp_warnloops = DEFWARNLOOPS; /* Warn about a loopback this often */ + + +/* + * lcp_init - Initialize LCP. + */ +void + lcp_init(unit) +int unit; +{ + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *ao = &lcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = LCP; + f->timeouttime = DEFTIMEOUT; + f->maxconfreqtransmits = DEFMAXCONFIGREQS; + f->maxtermtransmits = DEFMAXTERMTRANSMITS; + f->maxnakloops = DEFMAXNAKLOOPS; + f->callbacks = &lcp_callbacks; + + wo->passive = 0; + wo->restart = 0; /* Set to 1 in kernels or multi-line + implementations */ + + wo->neg_mru = 1; + wo->mru = DEFMRU; + wo->neg_asyncmap = 1; + wo->asyncmap = 0; + wo->neg_chap = 0; /* Set to 1 on server */ + wo->neg_upap = 0; /* Set to 1 on server */ + wo->neg_magicnumber = 1; + wo->neg_pcompression = 1; + wo->neg_accompression = 1; + + ao->neg_mru = 1; + ao->neg_asyncmap = 1; + ao->neg_chap = 0; /* Set to 1 on client */ + ao->chap_mdtype = CHAP_DIGEST_MD5; + ao->chap_callback = CHAP_NOCALLBACK; + ao->neg_upap = 0; /* Set to 1 on client */ + + ao->neg_magicnumber = 1; + ao->neg_pcompression = 1; + ao->neg_accompression = 1; + + fsm_init(f); +} + + +/* + * lcp_activeopen - Actively open LCP. + */ +void + lcp_activeopen(unit) +int unit; +{ + fsm_activeopen(&lcp_fsm[unit]); +} + + +/* + * lcp_passiveopen - Passively open LCP. + */ +void + lcp_passiveopen(unit) +int unit; +{ + fsm_passiveopen(&lcp_fsm[unit]); +} + + +/* + * lcp_close - Close LCP. + */ +void + lcp_close(unit) +int unit; +{ + fsm_close(&lcp_fsm[unit]); +} + + +/* + * lcp_lowerup - The lower layer is up. + */ +void + lcp_lowerup(unit) +int unit; +{ + SIFDOWN(unit); + SIFMTU(unit, MTU); + SIFASYNCMAP(unit, 0xffffffff); + CIFPCOMPRESSION(unit); + CIFACCOMPRESSION(unit); + + fsm_lowerup(&lcp_fsm[unit]); +} + + +/* + * lcp_lowerdown - The lower layer is down. + */ +void + lcp_lowerdown(unit) +int unit; +{ + fsm_lowerdown(&lcp_fsm[unit]); +} + + +/* + * lcp_input - Input LCP packet. + */ +void + lcp_input(unit, p, len) +int unit; +u_char *p; +int len; +{ + fsm_input(&lcp_fsm[unit], p, len); +} + + +/* + * lcp_protrej - A Protocol-Reject was received. + */ +/*ARGSUSED*/ +void + lcp_protrej(unit) +int unit; +{ + /* + * Can't reject LCP! + */ + LCPDEBUG((LOG_WARNING, + "lcp_protrej: Received Protocol-Reject for LCP!")) +} + + +/* + * lcp_sprotrej - Send a Protocol-Reject for some protocol. + */ +void + lcp_sprotrej(unit, p, len) +int unit; +u_char *p; +int len; +{ + /* this is marginal, as rejected-info should be full frame, + * but at least we return the rejected-protocol + */ + p += 2; + len -= 2; + + fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id, + p, len); +} + + +/* + * lcp_resetci - Reset our CI. + */ +static void + lcp_resetci(f) +fsm *f; +{ + lcp_wantoptions[f->unit].magicnumber = magic(); + lcp_wantoptions[f->unit].numloops = 0; + lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit]; +} + + +/* + * lcp_cilen - Return length of our CI. + */ +static int + lcp_cilen(f) +fsm *f; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + +#define LENCIVOID(neg) (neg ? 2 : 0) +#define LENCICHAP(neg) (neg ? 6 : 0) +#define LENCISHORT(neg) (neg ? 4 : 0) +#define LENCILONG(neg) (neg ? 6 : 0) + + return (LENCISHORT(go->neg_mru) + + LENCILONG(go->neg_asyncmap) + + LENCICHAP(go->neg_chap) + + LENCISHORT(go->neg_upap) + + LENCILONG(go->neg_magicnumber) + + LENCIVOID(go->neg_pcompression) + + LENCIVOID(go->neg_accompression)); +} + + +/* + * lcp_addci - Add our desired CIs to a packet. + */ +static void + lcp_addci(f, ucp) +fsm *f; +u_char *ucp; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + +#define ADDCIVOID(opt, neg) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(2, ucp); \ + } +#define ADDCISHORT(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(2 + sizeof (short), ucp); \ + PUTSHORT(val, ucp); \ + } +#define ADDCICHAP(opt, neg, val, digest, callback) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(6, ucp); \ + PUTSHORT(val, ucp); \ + PUTCHAR(digest, ucp); \ + PUTCHAR(callback, ucp); \ + } +#define ADDCILONG(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(2 + sizeof (long), ucp); \ + PUTLONG(val, ucp); \ + } + + ADDCISHORT(CI_MRU, go->neg_mru, go->mru) + ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap, go->asyncmap) + ADDCICHAP(CI_AUTHTYPE, go->neg_chap, CHAP, go->chap_mdtype, go->chap_callback) + ADDCISHORT(CI_AUTHTYPE, go->neg_upap, UPAP) + ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber) + ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression) + ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression) +} + + +/* + * lcp_ackci - Ack our CIs. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int + lcp_ackci(f, p, len) +fsm *f; +u_char *p; +int len; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char cilen, citype, cichar; + u_short cishort; + u_long cilong; + + /* + * CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define ACKCIVOID(opt, neg) \ + if (neg) { \ + if ((len -= 2) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != 2 || \ + citype != opt) \ + goto bad; \ + } +#define ACKCISHORT(opt, neg, val) \ + if (neg) { \ + if ((len -= 2 + sizeof (short)) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != 2 + sizeof (short) || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + } +#define ACKCICHAP(opt, neg, val, digest, callback) \ + if (neg) { \ + if ((len -= 4 + sizeof (short)) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != 4 + sizeof (short) || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != digest) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != callback) \ + goto bad; \ + } +#define ACKCILONG(opt, neg, val) \ + if (neg) { \ + if ((len -= 2 + sizeof (long)) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != 2 + sizeof (long) || \ + citype != opt) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } + + ACKCISHORT(CI_MRU, go->neg_mru, go->mru) + ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap, go->asyncmap) + ACKCICHAP(CI_AUTHTYPE, go->neg_chap, CHAP, go->chap_mdtype, go->chap_callback) + ACKCISHORT(CI_AUTHTYPE, go->neg_upap, UPAP) + ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber) + ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression) + ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression) + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + return (1); +bad: + LCPDEBUG((LOG_WARNING, "lcp_acki: received bad Ack!")) + return (0); +} + + +/* + * lcp_nakci - NAK some of our CIs. + */ +static void + lcp_nakci(f, p, len) +fsm *f; +u_char *p; +int len; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *wo = &lcp_wantoptions[f->unit]; + u_short cishort; + u_long cilong; + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIVOID(opt, neg, code) \ + if (neg && \ + len >= 2 && \ + p[1] == 2 && \ + p[0] == opt) { \ + len -= 2; \ + INCPTR(2, p); \ + code \ + } +#define NAKCICHAP(opt, neg, digest, callback, code) \ + if (neg && \ + len >= 4 + sizeof (short) && \ + p[1] == 4 + sizeof (short) && \ + p[0] == opt) { \ + len -= 4 + sizeof (short); \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + INCPTR(2, p); \ + code \ + } +#define NAKCISHORT(opt, neg, code) \ + if (neg && \ + len >= 2 + sizeof (short) && \ + p[1] == 2 + sizeof (short) && \ + p[0] == opt) { \ + len -= 2 + sizeof (short); \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + code \ + } +#define NAKCILONG(opt, neg, code) \ + if (neg && \ + len >= 2 + sizeof (long) && \ + p[1] == 2 + sizeof (long) && \ + p[0] == opt) { \ + len -= 2 + sizeof (long); \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + code \ + } + + /* + * We don't care if they want to send us smaller packets than + * we want. Therefore, accept any MRU less than what we asked for, + * but then ignore the new value when setting the MRU in the kernel. + * If they send us a bigger MRU than what we asked, reject it and + * let him decide to accept our value. + */ + NAKCISHORT(CI_MRU, go->neg_mru, + if (cishort <= wo->mru) + go->mru = cishort; + else + goto bad; + ) + NAKCILONG(CI_ASYNCMAP, go->neg_asyncmap, + go->asyncmap |= cilong; + ) + NAKCICHAP(CI_AUTHTYPE, go->neg_chap, go->chap_mdtype, go->chap_callback, + LCPDEBUG((LOG_WARNING, "Peer refuses to authenticate chap!")) + ) + NAKCISHORT(CI_AUTHTYPE, go->neg_upap, + LCPDEBUG((LOG_WARNING, "Peer refuses to authenticate pap!")) + ) + NAKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, + go->magicnumber = magic(); + if (++go->numloops % lcp_warnloops == 0) + LCPDEBUG((LOG_INFO, "The line appears to be looped back.")) + ) + NAKCIVOID(CI_PCOMPRESSION, go->neg_pcompression, + go->neg_pcompression = 0; + ) + NAKCIVOID(CI_ACCOMPRESSION, go->neg_accompression, + go->neg_accompression = 0; + ) + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len == 0) + return; +bad: + LCPDEBUG((LOG_WARNING, "lcp_nakci: received bad Nak!")) +} + + +/* + * lcp_rejci - Reject some of our CIs. + */ +static void + lcp_rejci(f, p, len) +fsm *f; +u_char *p; +int len; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_short cishort; + u_long cilong; + u_char *start = p; + int myopt, myval, xval, plen = len; + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIVOID(opt, neg) \ + myopt = opt; \ + if (neg && \ + len >= 2 && \ + p[1] == 2 && \ + p[0] == opt) { \ + len -= 2; \ + INCPTR(2, p); \ + neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected void opt %d",opt)) \ + } +#define REJCISHORT(opt, neg, val) \ + myopt = opt; myval = val; \ + if (neg && \ + len >= 2 + sizeof (short) && \ + p[1] == 2 + sizeof (short) && \ + p[0] == opt) { \ + len -= 2 + sizeof (short); \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + xval = cishort; \ + if (cishort != val) \ + goto bad; \ + neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected short opt %d", opt)) \ + } +#define REJCICHAP(opt, neg, val, digest, callback) \ + myopt = opt; myval = val; \ + if (neg && \ + len >= 4 + sizeof (short) && \ + p[1] == 4 + sizeof (short) && \ + p[0] == opt) { \ + len -= 4 + sizeof (short); \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + xval = cishort; \ + if (cishort != val) \ + goto bad; \ + neg = 0; \ + INCPTR(2, p); \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected chap opt %d", opt)) \ + } +#define REJCILONG(opt, neg, val) \ + myopt = opt; myval = val; \ + if (neg && \ + len >= 2 + sizeof (long) && \ + p[1] == 2 + sizeof (long) && \ + p[0] == opt) { \ + len -= 2 + sizeof (long); \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + xval = cilong; \ + /* Check rejected value. */ \ + if (cilong != val) \ + goto bad; \ + neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected long opt %d", opt)) \ + } + + REJCISHORT(CI_MRU, go->neg_mru, go->mru) + REJCILONG(CI_ASYNCMAP, go->neg_asyncmap, go->asyncmap) + REJCICHAP(CI_AUTHTYPE, go->neg_chap, CHAP, go->chap_mdtype, go->callback) + REJCISHORT(CI_AUTHTYPE, go->neg_upap, UPAP) + REJCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber) + REJCIVOID(CI_PCOMPRESSION, go->neg_pcompression) + REJCIVOID(CI_ACCOMPRESSION, go->neg_accompression) + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len == 0) + return; +bad: + LCPDEBUG((LOG_WARNING, "lcp_rejci: received bad Reject!")) + LCPDEBUG((LOG_WARNING, "lcp_rejci: plen %d len %d off %d, exp opt %d, found %d, val %d fval %d ", + plen, len, p - start, myopt, p[0] &0xff, myval, xval )) +} + + +/* + * lcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. + */ +static u_char + lcp_reqci(f, inp, len) +fsm *f; +u_char *inp; /* Requested CIs */ +int *len; /* Length of requested CIs */ +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *ao = &lcp_allowoptions[f->unit]; + u_char *cip; /* Pointer to Current CI */ + u_char cilen, citype, cichar;/* Parsed len, type, char value */ + u_short cishort; /* Parsed short value */ + u_long cilong; /* Parse long value */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p = inp; /* Pointer to next char to parse */ + u_char *ucp = inp; /* Pointer to current output char */ + int l = *len; /* Length left */ + + /* + * Reset all his options. + */ + ho->neg_mru = 0; + ho->neg_asyncmap = 0; + ho->neg_chap = 0; + ho->neg_upap = 0; + ho->neg_magicnumber = 0; + ho->neg_pcompression = 0; + ho->neg_accompression = 0; + + /* + * Process all his options. + */ + while (l) { + orc = CONFACK; /* Assume success */ + cip = p; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: bad CI length!")) + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + cilen -= 2; /* Adjust cilen to just data */ + + switch (citype) { /* Check CI type */ + case CI_MRU: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd MRU")) + if (!ao->neg_mru || /* Allow option? */ + cilen != sizeof (short)) { /* Check CI length */ + INCPTR(cilen, p); /* Skip rest of CI */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETSHORT(cishort, p); /* Parse MRU */ + LCPDEBUG((LOG_INFO, "(%d)", cishort)) + + /* + * He must be able to receive at least our minimum. + * No need to check a maximum. If he sends a large number, + * we'll just ignore it. + */ + if (cishort < MINMRU) { + orc = CONFNAK; /* Nak CI */ + DECPTR(sizeof (short), p); /* Backup */ + PUTSHORT(MINMRU, p); /* Give him a hint */ + break; + } + ho->neg_mru = 1; /* Remember he sent and MRU */ + ho->mru = cishort; /* And remember value */ + break; + + case CI_ASYNCMAP: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd ASYNCMAP")) + if (!ao->neg_asyncmap || + cilen != sizeof (long)) { + INCPTR(cilen, p); + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + LCPDEBUG((LOG_INFO, "(%lx)", cilong)) + + /* XXX Accept anything he says */ +#if 0 + /* + * Asyncmap must be OR of two maps. + */ + if ((lcp_wantoptions[f->unit].neg_asyncmap && + cilong != (lcp_wantoptions[f->unit].asyncmap | cilong)) || + (!lcp_wantoptions[f->unit].neg_asyncmap && + cilong != 0xffffffff)) { + orc = CONFNAK; + DECPTR(sizeof (long), p); + PUTLONG(lcp_wantoptions[f->unit].neg_asyncmap ? + lcp_wantoptions[f->unit].asyncmap | cilong : + 0xffffffff, p); + break; + } +#endif + ho->neg_asyncmap = 1; + ho->asyncmap = cilong; + break; + + case CI_AUTHTYPE: + if (cilen < sizeof (short) || + (!ao->neg_upap && !ao->neg_chap)) { + LCPDEBUG((LOG_WARNING, + "lcp_reqci: rcvd AUTHTYPE, rejecting ...!")) + INCPTR(cilen, p); + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd AUTHTYPE (%x)", + cishort)) + + /* + * Authtype must be UPAP or CHAP. + */ + if (cishort == UPAP) { + INCPTR(cilen - sizeof (u_short), p); + if (!ao->neg_upap) { /* we don't want to do PAP */ + LCPDEBUG((LOG_INFO, + "lcp_reqci: rcvd AUTHTYPE PAP, rejecting...")) + orc = CONFREJ; + break; + } + ho->neg_upap = 1; + break; + } + else if (cishort == CHAP) { + INCPTR(cilen - sizeof (u_short), p); + if (!ao->neg_chap) { /* we don't want to do CHAP */ + LCPDEBUG((LOG_INFO, + "lcp_reqci: rcvd AUTHTYPE CHAP, rejecting...")) + orc = CONFREJ; + break; + } + GETCHAR(cichar, p); /* get digest type*/ + if (cichar != ao->chap_mdtype) { + DECPTR(sizeof (u_char), p); + orc = CONFNAK; + PUTCHAR(ao->chap_mdtype, p); + INCPTR(cilen - sizeof(u_char), p); + break; + } + ho->chap_mdtype = cichar; /* save md type */ + GETCHAR(cichar, p); /* get callback type*/ + if (cichar != ao->chap_callback) { /* we don't callback yet */ + DECPTR(sizeof (u_char), p); + orc = CONFNAK; + PUTCHAR(CHAP_NOCALLBACK, p); + INCPTR(cilen - sizeof(u_char), p); + break; + } + ho->chap_callback = cichar; /* save callback */ + ho->neg_chap = 1; + break; + } + else { + DECPTR(sizeof (short), p); + orc = CONFNAK; + if (ao->neg_chap) { /* We prefer CHAP */ + PUTSHORT(CHAP, p); + } + else + if (ao->neg_upap) { + PUTSHORT(CHAP, p); + } + else { + syslog(LOG_ERR, "Coding botch in lcp_reqci authnak. This shouldn't happen."); + exit(1); + } + INCPTR(cilen - sizeof (u_short), p); + break; + } + + + case CI_MAGICNUMBER: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd MAGICNUMBER")) + if (!ao->neg_magicnumber || + cilen != sizeof (long)) { + INCPTR(cilen, p); + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + LCPDEBUG((LOG_INFO, "(%lx)", cilong)) + + /* + * He must have a different magic number. + */ + if (go->neg_magicnumber && + cilong == go->magicnumber) { + orc = CONFNAK; + DECPTR(sizeof (long), p); + cilong = magic(); /* Don't put magic() inside macro! */ + PUTLONG(cilong, p); + break; + } + ho->neg_magicnumber = 1; + ho->magicnumber = cilong; + break; + + + case CI_PCOMPRESSION: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd PCOMPRESSION")) + if (!ao->neg_pcompression || + cilen != 0) { + INCPTR(cilen, p); + orc = CONFREJ; + break; + } + ho->neg_pcompression = 1; + break; + + case CI_ACCOMPRESSION: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd ACCOMPRESSION")) + if (!ao->neg_accompression || + cilen != 0) { + INCPTR(cilen, p); + orc = CONFREJ; + break; + } + ho->neg_accompression = 1; + break; + + default: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd unknown option %d", + citype)) + INCPTR(cilen, p); + orc = CONFREJ; + break; + } + cilen += 2; /* Adjust cilen whole CI */ + +endswitch: + LCPDEBUG((LOG_INFO, " (%s)", + orc == CONFACK ? "ACK" : (orc == CONFNAK ? "NAK" : "REJ"))) + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) /* but prior CI wasnt? */ + continue; /* Don't send this one */ + + if (orc == CONFNAK) { /* Nak this CI? */ + if (rc == CONFREJ) /* Rejecting prior CI? */ + continue; /* Don't send this one */ + if (rc == CONFACK) { /* Ack'd all prior CIs? */ + rc = CONFNAK; /* Not anymore... */ + ucp = inp; /* Backup */ + } + } + if (orc == CONFREJ && /* Reject this CI */ + rc != CONFREJ) { /* but no prior ones? */ + rc = CONFREJ; + ucp = inp; /* Backup */ + } + if (ucp != cip) /* Need to move CI? */ + BCOPY(cip, ucp, cilen); /* Move it */ + INCPTR(cilen, ucp); /* Update output pointer */ + } + + /* + * XXX If we wanted to send additional NAKs (for unsent CIs), the + * code would go here. This must be done with care since it might + * require a longer packet than we received. + */ + + *len = ucp - inp; /* Compute output length */ + LCPDEBUG((LOG_INFO, "lcp_reqci: returning %s.", + rc == CONFACK ? "CONFACK" : + rc == CONFNAK ? "CONFNAK" : "CONFREJ")) + return (rc); /* Return final code */ +} + + +/* + * lcp_up - LCP has come UP. + * + * Start UPAP, IPCP, etc. + */ +static void + lcp_up(f) +fsm *f; +{ + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *go = &lcp_gotoptions[f->unit]; + int auth = 0; + + if (ho->neg_mru) + SIFMTU(f->unit, ho->mru); + if (ho->neg_asyncmap) + SIFASYNCMAP(f->unit, ho->asyncmap); + if (ho->neg_pcompression) + SIFPCOMPRESSION(f->unit); + if (ho->neg_accompression) + SIFACCOMPRESSION(f->unit); + SIFUP(f->unit); /* Bring the interface up (set IFF_UP) */ + ChapLowerUp(f->unit); /* Enable CHAP */ + upap_lowerup(f->unit); /* Enable UPAP */ + ipcp_lowerup(f->unit); /* Enable IPCP */ + if (go->neg_chap) { + ChapAuthPeer(f->unit); + auth = 1; + } + if (ho->neg_chap) { + ChapAuthWithPeer(f->unit); + auth = 1; + } + if (go->neg_upap) { + upap_authpeer(f->unit); + auth = 1; + } + if (ho->neg_upap) { + upap_authwithpeer(f->unit); + auth = 1; + } + if (!auth) + ipcp_activeopen(f->unit); +} + + +/* + * lcp_down - LCP has gone DOWN. + * + * Alert other protocols. + */ +static void + lcp_down(f) +fsm *f; +{ + ipcp_lowerdown(f->unit); + SIFDOWN(f->unit); + SIFMTU(f->unit, MTU); + SIFASYNCMAP(f->unit, 0xffffffff); + CIFPCOMPRESSION(f->unit); + CIFACCOMPRESSION(f->unit); + ChapLowerDown(f->unit); + upap_lowerdown(f->unit); +} + + +/* + * lcp_closed - LCP has CLOSED. + * + * Alert other protocols. + */ +static void + lcp_closed(f) +fsm *f; +{ + if (lcp_wantoptions[f->unit].restart) { + if (lcp_wantoptions[f->unit].passive) + lcp_passiveopen(f->unit); /* Start protocol in passive mode */ + else + lcp_activeopen(f->unit); /* Start protocol in active mode */ + } + else { + EXIT(f->unit); + } +} diff --git a/libexec/pppd/lcp.h b/libexec/pppd/lcp.h new file mode 100644 index 0000000000..d4db5344a9 --- /dev/null +++ b/libexec/pppd/lcp.h @@ -0,0 +1,71 @@ +/* + * lcp.h - Link Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * Options. + */ +#define CI_MRU 1 /* Maximum Receive Unit */ +#define CI_ASYNCMAP 2 /* Async Control Character Map */ +#define CI_AUTHTYPE 3 /* Authentication Type */ +#define CI_NOTDEFINED 4 /* not defined (used to be Encryption Type) */ +#define CI_MAGICNUMBER 5 /* Magic Number */ +#define CI_KEEPALIVE 6 /* Keep Alive Parameters */ +#define CI_PCOMPRESSION 7 /* Protocol Field Compression */ +#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */ + + +/* + * The state of options is described by an lcp_options structure. + */ +typedef struct lcp_options { + int passive : 1; /* Passives vs. active open */ + int restart : 1; /* Restart vs. exit after close */ + int neg_mru : 1; /* Negotiate the MRU? */ + u_short mru; /* Value of MRU */ + int neg_asyncmap : 1; /* Async map? */ + u_long asyncmap; + int neg_upap : 1; /* UPAP authentication? */ + int neg_chap : 1; /* CHAP authentication? */ + char chap_mdtype; /* which MD type */ + char chap_callback; /* callback ? */ + int neg_magicnumber : 1; /* Magic number? */ + u_long magicnumber; + int numloops; /* Number loops during magic number negot. */ + int neg_pcompression : 1; /* HDLC Protocol Field Compression? */ + int neg_accompression : 1; /* HDLC Address/Control Field Compression? */ +} lcp_options; + +extern fsm lcp_fsm[]; +extern lcp_options lcp_wantoptions[]; +extern lcp_options lcp_gotoptions[]; +extern lcp_options lcp_allowoptions[]; +extern lcp_options lcp_hisoptions[]; + +#define DEFMRU 1500 /* Try for this */ +#define MINMRU 128 /* No MRUs below this */ + +void lcp_init __ARGS((int)); +void lcp_activeopen __ARGS((int)); +void lcp_passiveopen __ARGS((int)); +void lcp_close __ARGS((int)); +void lcp_lowerup __ARGS((int)); +void lcp_lowerdown __ARGS((int)); +void lcp_input __ARGS((int, u_char *, int)); +void lcp_protrej __ARGS((int)); +void lcp_sprotrej __ARGS((int, u_char *, int)); diff --git a/libexec/pppd/logwtmp.c b/libexec/pppd/logwtmp.c new file mode 100644 index 0000000000..62cd0dc653 --- /dev/null +++ b/libexec/pppd/logwtmp.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1988 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * static char sccsid[] = "@(#)logwtmp.c 5.2 (Berkeley) 9/20/88"; + */ + +#if !defined(lint) && !defined(SABER) +static char sccsid[] = "@(#)logwtmp.c 5.2 (Berkeley) 9/22/88"; +#endif /* not lint && not SABER*/ + +#include +#include +#include +#include +#include +#include + +#define WTMPFILE "/usr/adm/wtmp" + +static int fd; + +logwtmp(line, name, host) + char *line, *name, *host; +{ + struct utmp ut; + struct stat buf; + time_t time(); + char *strncpy(); + + if (!fd && (fd = open(WTMPFILE, O_WRONLY|O_APPEND, 0)) < 0) + return; + if (!fstat(fd, &buf)) { + (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line)); + (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name)); + (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host)); + (void)time(&ut.ut_time); + if (write(fd, (char *)&ut, sizeof(struct utmp)) != + sizeof(struct utmp)) + (void)ftruncate(fd, buf.st_size); + } +} diff --git a/libexec/pppd/magic.c b/libexec/pppd/magic.c new file mode 100644 index 0000000000..87f2a7ac4c --- /dev/null +++ b/libexec/pppd/magic.c @@ -0,0 +1,66 @@ +/* + * magic.c - PPP Magic Number routines. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include +#include +#include + +#include "magic.h" + + +static u_long next; /* Next value to return */ + +extern u_long gethostid __ARGS((void)); +extern long random __ARGS((void)); +extern void srandom __ARGS((int)); + + +/* + * magic_init - Initialize the magic number generator. + * + * Computes first magic number and seed for random number generator. + * Attempts to compute a random number seed which will not repeat. + * The current method uses the current hostid and current time. + */ +void magic_init() +{ + struct timeval tv; + + next = gethostid(); + if (gettimeofday(&tv, NULL)) { + perror("gettimeofday"); + exit(1); + } + next ^= (u_long) tv.tv_sec ^ (u_long) tv.tv_usec; + + srandom((int) next); +} + + +/* + * magic - Returns the next magic number. + */ +u_long magic() +{ + u_long m; + + m = next; + next = (u_long) random(); + return (m); +} diff --git a/libexec/pppd/magic.h b/libexec/pppd/magic.h new file mode 100644 index 0000000000..a0479b2438 --- /dev/null +++ b/libexec/pppd/magic.h @@ -0,0 +1,22 @@ +/* + * magic.h - PPP Magic Number definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#include "args.h" + +void magic_init __ARGS((void)); /* Initialize the magic number generator */ +u_long magic __ARGS((void)); /* Returns the next magic number */ diff --git a/libexec/pppd/main.c b/libexec/pppd/main.c new file mode 100644 index 0000000000..557bc555e2 --- /dev/null +++ b/libexec/pppd/main.c @@ -0,0 +1,2298 @@ +/* + * main.c - Point-to-Point Protocol main module + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * There are three scenarios: + * 1. pppd used as daemon started from /etc/rc or perhaps /etc/ttys. + * a. server + * b. authentication necessary + * c. want to use constant local ip addr + * d. want to use constant remote ip addr, constant ip addr based on + * authenticated user, or request ip addr + * 2. pppd used on /dev/tty after remote login. + * a. server + * b. no authentication necessary or allowed + * c. want to use constant local ip addr + * d. want to use constant remote ip addr, constant ip addr based on + * authenticated user, or request ip addr + * 3. pppd used on line after tip'ing out. + * a. client + * b. remote end may request authentication + * c. want to use constant local ip addr or request ip addr + * d. want to use constant remote ip addr based on tip'd host, or + * request remote ip addr + */ + +#ifdef __386BSD__ +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef sparc +#include +#endif + +#ifdef STREAMS +#include +#include +#include +#else +#ifdef SGTTY +#include +#else +#include +#include +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "callout.h" + +#include +#include + +#ifdef STREAMS +#include "ppp_str.h" +#endif + +#define DEVNAME_SIZE 128 /* Buffer size for /dev filenames */ + +#include + +#ifndef BSD +#define BSD 43 +#endif /*BSD*/ + +#include +#include "magic.h" +#include "fsm.h" +#include "lcp.h" +#include "ipcp.h" +#include "upap.h" +#include "chap.h" + +#include "pppd.h" +#include "pathnames.h" +#include "patchlevel.h" + + +#ifndef TRUE +#define TRUE (1) +#endif /*TRUE*/ + +#ifndef FALSE +#define FALSE (0) +#endif /*FALSE*/ + +#ifdef PIDPATH +static char *pidpath = PIDPATH; /* filename in which pid will be */ + /* stored */ +#else +static char *pidpath = _PATH_PIDFILE; +#endif /* PIDFILE */ + +static char uinfopath[DEVNAME_SIZE]; + +/* interface vars */ + +char ifname[IFNAMSIZ]; /* Interface name */ +int ifunit; /* Interface unit number */ + +char *progname; /* Name of this program */ +char hostname[MAX_HOSTNAME_LEN]; /* hostname */ +u_char hostname_len; /* hostname length */ + +static pid_t pid; /* Our pid */ +static pid_t pgrpid; /* Process Group ID */ +static char pidfilename[DEVNAME_SIZE]; + +static char devname[DEVNAME_SIZE] = "/dev/tty"; /* Device name */ +static int default_device = TRUE; /* use default device (stdin/out) */ +int fd; /* Device file descriptor */ +int s; /* Socket file descriptor */ +static int initdisc; /* Initial TTY discipline */ +#ifndef STREAMS +#ifdef SGTTY +static struct sgttyb initsgttyb; /* Initial TTY sgttyb */ +#else +static struct termios inittermios; /* Initial TTY TIOCGETA */ +#endif +#endif + +static int initfdflags; /* Initial file descriptor flags */ + +u_char outpacket_buf[MTU+DLLHEADERLEN]; /* buffer for outgoing packet */ +static u_char inpacket_buf[MTU+DLLHEADERLEN]; /* buffer for incoming packet */ + +/* configured variables */ + +int debug = 0; /* Debug flag */ +static char user[80]; /* User name */ +static char passwd[80]; /* password */ +static char *connector = NULL; /* "connect" command */ +static int inspeed = 0; /* Input/Output speed */ +static u_long netmask = 0; /* netmask to use on ppp interface */ +static int crtscts = 0; /* use h/w flow control */ +static int nodetach = 0; /* don't fork */ + +/* prototypes */ +static void hup __ARGS((int, int, struct sigcontext *, char *)); +static void intr __ARGS((int, int, struct sigcontext *, char *)); +static void term __ARGS((int, int, struct sigcontext *, char *)); +static void alrm __ARGS((int, int, struct sigcontext *, char *)); +static void io __ARGS((int, int, struct sigcontext *, char *)); +static void incdebug __ARGS((int, int, struct sigcontext *, char *)); +static void nodebug __ARGS((int, int, struct sigcontext *, char *)); +static void getuserpasswd __ARGS((void)); + +static int setdebug __ARGS((int *, char ***)); +static int setpassive __ARGS((int *, char ***)); +static int noopt __ARGS((int *, char ***)); +static int setnovj __ARGS((int *, char ***)); +static int noupap __ARGS((int *, char ***)); +static int requpap __ARGS((int *, char ***)); +static int nochap __ARGS((int *, char ***)); +static int reqchap __ARGS((int *, char ***)); +static int setspeed __ARGS((int *, char ***)); +static int noaccomp __ARGS((int *, char ***)); +static int noasyncmap __ARGS((int *, char ***)); +static int noipaddr __ARGS((int *, char ***)); +static int nomagicnumber __ARGS((int *, char ***)); +static int setasyncmap __ARGS((int *, char ***)); +static int setvjmode __ARGS((int *, char ***)); +static int setmru __ARGS((int *, char ***)); +static int nomru __ARGS((int *, char ***)); +static int nopcomp __ARGS((int *, char ***)); +static int setconnector __ARGS((int *, char ***)); +static int setdomain __ARGS((int *, char ***)); +static int setnetmask __ARGS((int *, char ***)); +static int setcrtscts __ARGS((int *, char ***)); +static int setnodetach __ARGS((int *, char ***)); +static void cleanup __ARGS((int, caddr_t)); + +#ifdef STREAMS +static void str_restore __ARGS((void)); +extern char *ttyname __ARGS((int)); +#define MAXMODULES 10 /* max number of module names that we can save */ +static struct modlist { + char modname[FMNAMESZ+1]; +} str_modules[MAXMODULES]; +static int str_module_count = 0; +#endif + +/* + * Valid arguments. + */ +static struct cmd { + char *cmd_name; + int (*cmd_func)(); +} cmds[] = { + "-all", noopt, /* Don't request/allow any options */ + "-ac", noaccomp, /* Disable Address/Control compress */ + "-am", noasyncmap, /* Disable asyncmap negotiation */ + "-as", setasyncmap, /* set the desired async map */ + "-d", setdebug, /* Increase debugging level */ + "-detach", setnodetach, /* don't fork */ + "-ip", noipaddr, /* Disable IP address negotiation */ + "-mn", nomagicnumber, /* Disable magic number negotiation */ + "-mru", nomru, /* Disable mru negotiation */ + "-p", setpassive, /* Set passive mode */ + "-pc", nopcomp, /* Disable protocol field compress */ + "+ua", requpap, /* Require UPAP authentication */ + "-ua", noupap, /* Don't allow UPAP authentication */ + "+chap", reqchap, /* Require CHAP authentication */ + "-chap", nochap, /* Don't allow CHAP authentication */ + "-vj", setnovj, /* disable VJ compression */ + "asyncmap", setasyncmap, /* set the desired async map */ + "connect", setconnector, /* A program to set up a connection */ + "crtscts", setcrtscts, /* set h/w flow control */ + "debug", setdebug, /* Increase debugging level */ + "domain", setdomain, /* Add given domain name to hostname*/ + "mru", setmru, /* Set MRU value for negotiation */ + "netmask", setnetmask, /* set netmask */ + "passive", setpassive, /* Set passive mode */ + "vjmode", setvjmode, /* set VJ compression mode */ + NULL + }; + + +/* + * PPP Data Link Layer "protocol" table. + * One entry per supported protocol. + */ +static struct protent { + u_short protocol; + void (*init)(); + void (*input)(); + void (*protrej)(); +} prottbl[] = { + { LCP, lcp_init, lcp_input, lcp_protrej }, + { IPCP, ipcp_init, ipcp_input, ipcp_protrej }, + { UPAP, upap_init, upap_input, upap_protrej }, + { CHAP, ChapInit, ChapInput, ChapProtocolReject }, +}; + + +static char *usage = "pppd version %s patch level %d\n\ +Usage: %s [ arguments ], where arguments are:\n\ + -all Don't request/allow any options\n\ + -ac Disable Address/Control compression\n\ + -am Disable asyncmap negotiation\n\ + -as Set the desired async map to hex \n\ + -d Increase debugging level\n\ + -detach Don't fork to background\n\ + -ip Disable IP address negotiation\n\ + -mn Disable magic number negotiation\n\ + -mru Disable mru negotiation\n\ + -p Set passive mode\n\ + -pc Disable protocol field compression\n\ + +ua

Require UPAP authentication and use file

for\n\ + remote login data\n\ + -ua Don't allow UPAP authentication\n\ + +chap Require CHAP authentication\n\ + -chap Don't allow CHAP authentication\n\ + -vj disable VJ compression\n\ + connect

Invoke shell command

to set up the serial line\n\ + crtscts Use hardware RTS/CTS flow control\n\ + debug Increase debugging level\n\ + domain Append domain name to hostname for authentication\n\ + mru Set MRU value to for negotiation\n\ + netmask Set interface netmask to \n\ + passive Set passive mode\n\ + vjmode VJ compression mode {old, rfc1172, rfc1132 (default)}\n\ + Communicate over the named device\n\ + Set the baud rate to \n\ + : Set the local and/or remote interface IP\n\ + addresses. Either one may be omitted.\n"; + + +main(argc, argv) + int argc; + char *argv[]; +{ + int mask, i; + struct sigvec sv; + struct cmd *cmdp; + FILE *pidfile; +#ifndef STREAMS + int pppdisc = PPPDISC; +#endif + + /* + * Initialize syslog system and magic number package. + */ +#if BSD >= 43 || defined(sun) + openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP); + setlogmask(LOG_UPTO(LOG_INFO)); +#else + openlog("pppd", LOG_PID); +#define LOG_UPTO(x) (x) +#define setlogmask(x) (x) +#endif + +#ifdef STREAMS + if (ttyname(fileno(stdin))) + strcpy(devname, ttyname(fileno(stdin))); +#endif + + magic_init(); + + if (gethostname(hostname, MAX_HOSTNAME_LEN) < 0 ) { + syslog(LOG_ERR, "couldn't get hostname: %m"); + exit(1); + } + + /* + * Initialize to the standard option set and then parse the command + * line arguments. + */ + for (i = 0; i < sizeof (prottbl) / sizeof (struct protent); i++) + (*prottbl[i].init)(0); + + progname = *argv; + for (argc--, argv++; argc; ) { + /* + * First see if it's a command. + */ + for (cmdp = cmds; cmdp->cmd_name; cmdp++) + if (!strcmp(*argv, cmdp->cmd_name) && + (*cmdp->cmd_func)(&argc, &argv)) + break; + + /* + * Maybe a tty name, speed or IP address? + */ + if (cmdp->cmd_name == NULL && + !setdevname(&argc, &argv) && + !setspeed(&argc, &argv) && + !setipaddr(&argc, &argv)) { + fprintf(stderr, usage, VERSION, PATCHLEVEL, progname); + exit(1); + } + } + + syslog(LOG_INFO, "Starting pppd %s patch level %d", + VERSION, PATCHLEVEL); + + /* + * Initialize state. + */ + + +#define SETSID +#ifdef SETSID + if (default_device) { + /* No device name was specified... inherit the old controlling + terminal */ + + if ((pgrpid = getpgrp(0)) < 0) { + syslog(LOG_ERR, "getpgrp(0): %m"); + exit(1); + } + if (pgrpid != pid) + syslog(LOG_WARNING, "warning... not a process group leader"); + } + else /*default_device*/ + { + /* become session leader... */ + + if (!nodetach) { + /* fork so we're not a process group leader */ + if (pid = fork()) { + exit(0); + } + } +#ifdef xxx + else + /* bag controlling terminal */ + if (ioctl(0, TIOCNOTTY) < 0) { + syslog(LOG_ERR, "ioctl(TIOCNOTTY): %m"); + exit(1); + } +#endif + + /* create new session */ + if ((pgrpid = setsid()) < 0) { + syslog(LOG_ERR, "setsid(): %m"); + exit(1); + } + } +#endif + + /* open i/o device */ + if ((fd = open(devname, O_RDWR /*| O_NDELAY*/)) < 0) { + syslog(LOG_ERR, "open(%s): %m", devname); + exit(1); + } + + /* drop dtr to hang up incase modem is off hook */ + if (!default_device) { + setdtr(fd, FALSE); + sleep(1); + setdtr(fd, TRUE); + } + + /* set device to be controlling tty */ + if (ioctl(fd, TIOCSCTTY) < 0) { + syslog(LOG_ERR, "ioctl(TIOCSCTTY): %m"); + exit(1); + } + + /* run connection script */ + if (connector) { + syslog(LOG_NOTICE, "Connecting with <%s>", connector); + /* set line speed */ + set_up_tty(fd, 0); + if (set_up_connection(connector, fd, fd) < 0) { + syslog(LOG_ERR, "could not set up connection"); + setdtr(fd, FALSE); + exit(1); + } + syslog(LOG_NOTICE, "Connected..."); + } + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "socket : %m"); + exit(1); + } + + /* if we exit, then try and restore the stream */ +#ifdef sun + on_exit(cleanup, NULL); +#endif + +#ifdef STREAMS + /* go through and save the name of all the modules, then pop em */ + while(1) { + if(!ioctl(fd, I_LOOK, str_modules[str_module_count].modname)) + MAINDEBUG((LOG_DEBUG, "popped stream module : %s", + str_modules[str_module_count].modname)) + if(!ioctl(fd, I_POP, 0)) + str_module_count++; + else + break; + } + + /* set line speed */ + set_up_tty(fd, 1); + + syslog(LOG_ERR, "about to push modules..."); + + /* now push the async/fcs module */ + if(ioctl(fd, I_PUSH, "pppasync") < 0) { + syslog(LOG_ERR, "ioctl(I_PUSH, ppp_async): %m"); + exit(1); + } + /* finally, push the ppp_if module that actually handles the */ + /* network interface */ + if(ioctl(fd, I_PUSH, "pppif") < 0) { + syslog(LOG_ERR, "ioctl(I_PUSH, ppp_if): %m"); + exit(1); + } + if(ioctl(fd, I_SETSIG, S_INPUT) < 0) { + syslog(LOG_ERR, "ioctl(I_SETSIG, S_INPUT): %m"); + exit(1); + } + /* read mode, message non-discard mode */ + if(ioctl(fd, I_SRDOPT, RMSGN) < 0) { + syslog(LOG_ERR, "ioctl(I_SRDOPT, RMSGN): %m"); + exit(1); + } + /* Flush any waiting messages, or we'll never get SIGPOLL */ + if(ioctl(fd, I_FLUSH, FLUSHRW) < 0) { + syslog(LOG_ERR, "ioctl(I_FLUSH, FLUSHRW): %m"); + exit(1); + } + /* + * Find out which interface we were given. + * (ppp_if handles this ioctl) + */ + if (ioctl(fd, SIOCGETU, &ifunit) < 0) { + syslog(LOG_ERR, "ioctl(SIOCGETU): %m"); + exit(1); + } + + /* if debug, set debug flags in driver */ + { + int flags = debug ? 0x3 : 0; +syslog(LOG_INFO, "debug 0x%x, flags 0x%x", debug, flags); + if (ioctl(fd, SIOCSIFDEBUG, &flags) < 0) { + syslog(LOG_ERR, "ioctl(SIOCSIFDEBUG): %m"); + } + } + + syslog(LOG_ERR, "done pushing modules, ifunit %d", ifunit); +#else + /* set line speed */ + set_up_tty(fd, 1); + + if (ioctl(fd, TIOCGETD, &initdisc) < 0) { + syslog(LOG_ERR, "ioctl(TIOCGETD): %m"); + exit(1); + } + if (ioctl(fd, TIOCSETD, &pppdisc) < 0) { + syslog(LOG_ERR, "ioctl(TIOCSETD): %m"); + exit(1); + } + + /* + * Find out which interface we were given. + */ + if (ioctl(fd, PPPIOCGUNIT, &ifunit) < 0) { + syslog(LOG_ERR, "ioctl(PPPIOCGUNIT): %m"); + exit(1); + } +#endif + + syslog(LOG_NOTICE, "Using interface ppp%d", ifunit); + (void) sprintf(ifname, "ppp%d", ifunit); + pid = getpid(); + + (void) sprintf(pidfilename, "%s/%s.pid", pidpath, ifname); + + /* write pid to file */ + + if ((pidfile = fopen(pidfilename, "w")) != NULL) { + fprintf(pidfile, "%d\n", pid); + (void) fclose(pidfile); + } + + hostname_len = (u_char) strlen(hostname); + + MAINDEBUG((LOG_DEBUG, "hostname = %s", hostname)) + +#ifdef SETSID + if (default_device) { + int id = tcgetpgrp(fd); + if (id != pgrpid) { + syslog(LOG_WARNING, + "warning: pppd is not the leader of a forground process group"); + } + } + else + if (tcsetpgrp(fd, pgrpid) < 0) { + syslog(LOG_ERR, "tcsetpgrp(): %m"); + exit(1); + } +#else + /* set process group on tty so we get SIGIO's */ + if (ioctl(fd, TIOCSPGRP, &pgrpid) < 0) { + syslog(LOG_ERR, "ioctl(TIOCSPGRP): %m"); + exit(1); + } +#endif + + /* + * Compute mask of all interesting signals and install signal handlers + * for each. Only one signal handler may be active at a time. Therefore, + * all other signals should be masked when any handler is executing. + */ + mask = sigmask(SIGHUP) | sigmask(SIGINT) | sigmask(SIGALRM) | + sigmask(SIGIO); +#ifdef STREAMS + mask |= sigmask(SIGPOLL); +#endif + + sv.sv_handler = hup; /* Hangup */ + sv.sv_mask = mask; + sv.sv_flags = 0; + if (sigvec(SIGHUP, &sv, NULL)) { + syslog(LOG_ERR, "sigvec(SIGHUP)"); + exit(1); + } + sv.sv_handler = intr; /* Interrupt */ + sv.sv_mask = mask; + sv.sv_flags = 0; + if (sigvec(SIGINT, &sv, NULL)) { + syslog(LOG_ERR, "sigvec(SIGINT)"); + exit(1); + } + sv.sv_handler = term; /* Terminate */ + sv.sv_mask = mask; + sv.sv_flags = 0; + if (sigvec(SIGTERM, &sv, NULL)) { + syslog(LOG_ERR, "sigvec(SIGTERM)"); + exit(1); + } + sv.sv_handler = alrm; /* Timeout */ + sv.sv_mask = mask; + sv.sv_flags = 0; + if (sigvec(SIGALRM, &sv, NULL)) { + syslog(LOG_ERR, "sigvec(SIGALRM)"); + exit(1); + } + sv.sv_handler = io; /* Input available */ + sv.sv_mask = mask; + sv.sv_flags = 0; + if (sigvec(SIGIO, &sv, NULL)) { + syslog(LOG_ERR, "sigvec(SIGIO)"); + exit(1); + } +#ifdef STREAMS + sv.sv_handler = io; /* Input available */ + sv.sv_mask = mask; + sv.sv_flags = 0; + if (sigvec(SIGPOLL, &sv, NULL)) { + syslog(LOG_ERR, "sigvec(SIGPOLL)"); + exit(1); + } +#endif + +#ifdef __STDC__ + /* Increment debug flag */ + (void) signal(SIGUSR1, (void (*)(int))incdebug); + /* Reset debug flag */ + (void) signal(SIGUSR2, (void (*)(int))nodebug); +#else + /* Increment debug flag */ + (void) signal(SIGUSR1, (void (*)())incdebug); + /* Reset debug flag */ + (void) signal(SIGUSR2, (void (*)())nodebug); +#endif + + /* + * Record initial device flags, then set device to cause SIGIO + * signals to be generated. + */ + if ((initfdflags = fcntl(fd, F_GETFL)) == -1) { + syslog(LOG_ERR, "fcntl(F_GETFL): %m"); + exit(1); + } + if (fcntl(fd, F_SETFL, FNDELAY | FASYNC) == -1) { + syslog(LOG_ERR, "fcntl(F_SETFL, FNDELAY | FASYNC): %m"); + exit(1); + } + + /* + * Block all signals, start opening the connection, and wait for + * incoming signals (reply, timeout, etc.). + */ + syslog(LOG_INFO, "Connect: %s <--> %s", ifname, devname); + sigblock(mask); /* Block signals now */ + lcp_lowerup(0); /* XXX Well, sort of... */ + if (lcp_wantoptions[0].passive) + lcp_passiveopen(0); /* Start protocol in passive mode */ + else + lcp_activeopen(0); /* Start protocol in active mode */ + for (;;) { + sigpause(0); /* Wait for next signal */ + + /* Need to read user/passwd? */ + if (upap[0].us_flags & UPAPF_UPPENDING) { + sigsetmask(0); /* Allow other signals to occur */ + getuserpasswd(); /* Get user and passwd */ + upap[0].us_flags &= ~UPAPF_UPPENDING; + upap[0].us_flags |= UPAPF_UPVALID; + sigsetmask(mask); /* Disallow signals */ + upap_authwithpeer(0); + } + } +} + +set_up_tty(fd, flow) +int fd; +int flow; +{ +#ifdef STREAMS + int new_cflag; + struct termios tios; + + if(ioctl(fd, TCGETS, (caddr_t) &tios) < 0) { + syslog(LOG_ERR, "ioctl(TCGETS): %m"); + exit(1); + } + + new_cflag = CS8 | CREAD | HUPCL; + new_cflag |= inspeed ? inspeed : (tios.c_cflag & CBAUD); + if (flow) + new_cflag |= crtscts ? CRTSCTS : 0; + + tios.c_cflag = new_cflag; + tios.c_iflag = IGNBRK | IGNPAR; + tios.c_oflag = 0; + tios.c_lflag = 0; + + if(ioctl(fd, TCSETS, (caddr_t) &tios) < 0) { + syslog(LOG_ERR, "ioctl(TCSETS): %m"); + exit(1); + } +#else +#ifdef SGTTY + struct sgttyb sgttyb; + + /* + * Put the tty in raw mode and set the discipline to PPP. + */ + if (ioctl(fd, TIOCGETP, &initsgttyb) < 0) { + syslog(LOG_ERR, "ioctl(TIOCGETP): %m"); + exit(1); + } + + sgttyb = initsgttyb; + sgttyb.sg_flags = RAW | ANYP; + if (inspeed) + sgttyb.sg_ispeed = inspeed; + + if (ioctl(fd, TIOCSETP, &sgttyb) < 0) { + syslog(LOG_ERR, "ioctl(TIOCSETP): %m"); + exit(1); + } +#else + struct termios tios; + + if (ioctl(fd, TIOCGETA, &tios) < 0) { + syslog(LOG_ERR, "ioctl(TIOCGETA): %m"); + exit(1); + } + + inittermios = tios; + + tios.c_cflag = CREAD | CS8 | HUPCL; + if (flow) + tios.c_cflag |= crtscts ? CRTSCTS : 0; + tios.c_iflag = IGNBRK | IGNPAR; + tios.c_oflag = 0; + tios.c_lflag = 0; + tios.c_cc[VERASE] = tios.c_cc[VKILL] = 0; + tios.c_cc[VMIN] = 1; + tios.c_cc[VTIME] = 0; + if (inspeed) { + tios.c_ispeed = inspeed; + tios.c_ospeed = inspeed; + } + + if (ioctl(fd, TIOCSETA, &tios) < 0) { + syslog(LOG_ERR, "ioctl(TIOCSETA): %m"); + exit(1); + } +#endif +#endif +} + +/* + * quit - Clean up state and exit. + */ +void + quit() +{ + syslog(LOG_NOTICE, "Quitting"); + + if (fd == 0) + return; + + if (fcntl(fd, F_SETFL, initfdflags) == -1) { + syslog(LOG_ERR, "fcntl(F_SETFL, fdflags): %m"); + exit(1); + } + +#ifdef STREAMS + str_restore(); +#else +#ifdef SGTTY + if (ioctl(fd, TIOCSETP, &initsgttyb) < 0) { + syslog(LOG_ERR, "ioctl(TIOCSETP)"); + exit(1); + } +#else + if (ioctl(fd, TIOCSETA, &inittermios) < 0) { + syslog(LOG_ERR, "ioctl(TIOCSETA)"); + exit(1); + } + +#endif + if (ioctl(fd, TIOCSETD, &initdisc) < 0) { + syslog(LOG_ERR, "ioctl(TIOCSETD)"); + exit(1); + } +#endif + + /* drop dtr to hang up */ + setdtr(fd, FALSE); + + close(fd); + fd = 0; + + exit(0); +} + + +static struct callout *callout = NULL; /* Callout list */ +static struct timeval schedtime; /* Time last timeout was set */ + + +/* + * timeout - Schedule a timeout. + * + * Note that this timeout takes the number of seconds, NOT hz (as in + * the kernel). + */ +void timeout(func, arg, time) + void (*func)(); + caddr_t arg; + int time; +{ + struct itimerval itv; + struct callout *newp, **oldpp; + + MAINDEBUG((LOG_DEBUG, "Timeout %x:%x in %d seconds.", + (int) func, (int) arg, time)) + + /* + * Allocate timeout. + */ + if ((newp = (struct callout *) malloc(sizeof(struct callout))) == NULL) { + syslog(LOG_ERR, "Out of memory in timeout()!"); + exit(1); + } + newp->c_arg = arg; + newp->c_func = func; + + /* + * Find correct place to link it in and decrement its time by the + * amount of time used by preceding timeouts. + */ + for (oldpp = &callout; + *oldpp && (*oldpp)->c_time <= time; + oldpp = &(*oldpp)->c_next) + time -= (*oldpp)->c_time; + newp->c_time = time; + newp->c_next = *oldpp; + if (*oldpp) + (*oldpp)->c_time -= time; + *oldpp = newp; + + /* + * If this is now the first callout then we have to set a new + * itimer. + */ + if (callout == newp) { + itv.it_interval.tv_sec = itv.it_interval.tv_usec = + itv.it_value.tv_usec = 0; + itv.it_value.tv_sec = callout->c_time; + MAINDEBUG((LOG_DEBUG, "Setting itimer for %d seconds.", + itv.it_value.tv_sec)) + if (setitimer(ITIMER_REAL, &itv, NULL)) { + syslog(LOG_ERR, "setitimer(ITIMER_REAL)"); + exit(1); + } + if (gettimeofday(&schedtime, NULL)) { + syslog(LOG_ERR, "gettimeofday"); + exit(1); + } + } +} + + +/* + * untimeout - Unschedule a timeout. + */ +void untimeout(func, arg) + void (*func)(); + caddr_t arg; +{ + + struct itimerval itv; + struct callout **copp, *freep; + int reschedule = 0; + + MAINDEBUG((LOG_DEBUG, "Untimeout %x:%x.", (int) func, (int) arg)) + + /* + * If the first callout is unscheduled then we have to set a new + * itimer. + */ + if (callout && + callout->c_func == func && + callout->c_arg == arg) + reschedule = 1; + + /* + * Find first matching timeout. Add its time to the next timeouts + * time. + */ + for (copp = &callout; *copp; copp = &(*copp)->c_next) + if ((*copp)->c_func == func && + (*copp)->c_arg == arg) { + freep = *copp; + *copp = freep->c_next; + if (*copp) + (*copp)->c_time += freep->c_time; + (void) free((char *) freep); + break; + } + + if (reschedule) { + itv.it_interval.tv_sec = itv.it_interval.tv_usec = + itv.it_value.tv_usec = 0; + itv.it_value.tv_sec = callout ? callout->c_time : 0; + MAINDEBUG((LOG_DEBUG, "Setting itimer for %d seconds.", + itv.it_value.tv_sec)) + if (setitimer(ITIMER_REAL, &itv, NULL)) { + syslog(LOG_ERR, "setitimer(ITIMER_REAL)"); + exit(1); + } + if (gettimeofday(&schedtime, NULL)) { + syslog(LOG_ERR, "gettimeofday"); + exit(1); + } + } +} + + +/* + * adjtimeout - Decrement the first timeout by the amount of time since + * it was scheduled. + */ +void adjtimeout() +{ + struct timeval tv; + int timediff; + + if (callout == NULL) + return; + /* + * Make sure that the clock hasn't been warped dramatically. + * Account for recently expired, but blocked timer by adding + * small fudge factor. + */ + if (gettimeofday(&tv, NULL)) { + syslog(LOG_ERR, "gettimeofday"); + exit(1); + } + timediff = tv.tv_sec - schedtime.tv_sec; + if (timediff < 0 || + timediff > callout->c_time + 1) + return; + + callout->c_time -= timediff; /* OK, Adjust time */ +} + + +/* + * output - Output PPP packet. + */ +void + output(unit, p, len) +int unit; +u_char *p; +int len; +{ +#ifdef STREAMS + struct strbuf str; + + str.len = len; + str.buf = (caddr_t) p; + if(putmsg(fd, NULL, &str, 0) < 0) { + syslog(LOG_ERR, "putmsg"); + exit(1); + } +#else + if (unit != 0) { + MAINDEBUG((LOG_WARNING, "output: unit != 0!")) + abort(); + } + + if (write(fd, p, len) < 0) { + syslog(LOG_ERR, "write"); + exit(1); + } +#endif +} + + +/* + * hup - Catch SIGHUP signal. + * + * Indicates that the physical layer has been disconnected. + */ +/*ARGSUSED*/ +static void + hup(sig, code, scp, addr) +int sig, code; +struct sigcontext *scp; +char *addr; +{ + syslog(LOG_NOTICE, "Hangup (SIGHUP)"); + adjtimeout(); /* Adjust timeouts */ + lcp_lowerdown(0); /* Reset connection */ +} + + +/* + * term - Catch SIGTERM signal. + * + * Indicates that we should initiate a graceful disconnect and exit. + */ +/*ARGSUSED*/ +static void + term(sig, code, scp, addr) +int sig, code; +struct sigcontext *scp; +char *addr; +{ + syslog(LOG_NOTICE, "Terminate signal received."); + adjtimeout(); /* Adjust timeouts */ + lcp_close(0); /* Close connection */ +} + + +/* + * intr - Catch SIGINT signal (DEL/^C). + * + * Indicates that we should initiate a graceful disconnect and exit. + */ +/*ARGSUSED*/ +static void + intr(sig, code, scp, addr) +int sig, code; +struct sigcontext *scp; +char *addr; +{ + syslog(LOG_NOTICE, "Interrupt received. Exiting."); + adjtimeout(); /* Adjust timeouts */ + lcp_close(0); /* Close connection */ +} + + +/* + * alrm - Catch SIGALRM signal. + * + * Indicates a timeout. + */ +/*ARGSUSED*/ +static void + alrm(sig, code, scp, addr) +int sig, code; +struct sigcontext *scp; +char *addr; +{ + struct itimerval itv; + struct callout *freep; + + MAINDEBUG((LOG_DEBUG, "Alarm")) + + /* + * Call and free first scheduled timeout and any that were scheduled + * for the same time. + */ + while (callout) { + freep = callout; /* Remove entry before calling */ + callout = freep->c_next; + (*freep->c_func)(freep->c_arg); + (void) free((char *) freep); + if (callout && callout->c_time) + break; + } + + /* + * Set a new itimer if there are more timeouts scheduled. + */ + if (callout) { + itv.it_interval.tv_sec = itv.it_interval.tv_usec = + itv.it_value.tv_usec = 0; + itv.it_value.tv_sec = callout->c_time; + MAINDEBUG((LOG_DEBUG, "Setting itimer for %d seconds.", + itv.it_value.tv_sec)) + if (setitimer(ITIMER_REAL, &itv, NULL)) { + syslog(LOG_ERR, "setitimer(ITIMER_REAL)"); + exit(1); + } + if (gettimeofday(&schedtime, NULL)) { + syslog(LOG_ERR, "gettimeofday"); + exit(1); + } + } +} + + +/* + * io - Catch SIGIO signal. + * + * Indicates that incoming data is available. + */ +/*ARGSUSED*/ +static void + io(sig, code, scp, addr) +int sig, code; +struct sigcontext *scp; +char *addr; +{ + int len, i; + u_char *p; + u_short protocol; + fd_set fdset; + struct timeval notime; + int ready; +#ifdef STREAMS + struct strbuf str; +#endif + + + MAINDEBUG((LOG_DEBUG, "IO signal received")) + adjtimeout(); /* Adjust timeouts */ + + /* we do this to see if the SIGIO handler is being invoked for input */ + /* ready, or for the socket buffer hitting the low-water mark. */ + + notime.tv_sec = 0; + notime.tv_usec = 0; + + FD_ZERO(&fdset); + FD_SET(fd, &fdset); + + if ((ready = select(32, &fdset, (fd_set *) NULL, (fd_set *) NULL, + ¬ime)) == -1) { + syslog(LOG_ERR, "Error in io() select: %m"); + exit(1); + } + + if (ready == 0) { + MAINDEBUG((LOG_DEBUG, "IO non-input ready SIGIO occured.")); + return; + } + + /* Yup, this is for real */ + for (;;) { /* Read all available packets */ + p = inpacket_buf; /* point to beggining of packet buffer */ + +#ifdef STREAMS + str.maxlen = MTU+DLLHEADERLEN; + str.buf = (caddr_t) p; + i = 0; + len = getmsg(fd, NULL, &str, &i); + if(len < 0) { + if(errno == EAGAIN || errno == EWOULDBLOCK) { + return; + } + syslog(LOG_ERR, "getmsg(fd) %m"); + exit(1); + } + else if(len) + MAINDEBUG((LOG_DEBUG, "getmsg returns with length 0x%x",len)) + + if(str.len < 0) { + MAINDEBUG((LOG_DEBUG, "getmsg short return length %d", + str.len)) + return; + } + + len = str.len; +#else + if ((len = read(fd, p, MTU + DLLHEADERLEN)) < 0) { + if (errno == EWOULDBLOCK) { + MAINDEBUG((LOG_DEBUG, "read(fd): EWOULDBLOCK")) + return; + } + else { + syslog(LOG_ERR, "read(fd): %m"); + exit(1); + } + } + else +#endif + if (len == 0) { + syslog(LOG_ERR, "End of file on fd!"); + exit(1); + } + + if (len < DLLHEADERLEN) { + MAINDEBUG((LOG_INFO, "io(): Received short packet.")) + return; + } + + p += 2; /* Skip address and control */ + GETSHORT(protocol, p); + len -= DLLHEADERLEN; + + /* + * Toss all non-LCP packets unless LCP is OPEN. + */ + if (protocol != LCP && lcp_fsm[0].state != OPEN) { + MAINDEBUG((LOG_INFO, "io(): Received non-LCP packet and LCP is not in open state.")) + dumpbuffer(inpacket_buf, len + DLLHEADERLEN, LOG_ERR); + return; + } + + /* + * Upcall the proper protocol input routine. + */ + for (i = 0; i < sizeof (prottbl) / sizeof (struct protent); i++) + if (prottbl[i].protocol == protocol) { + (*prottbl[i].input)(0, p, len); + break; + } + + if (i == sizeof (prottbl) / sizeof (struct protent)) { + syslog(LOG_WARNING, "input: Unknown protocol (%x) received!", + protocol); + p -= DLLHEADERLEN; + len += DLLHEADERLEN; + lcp_sprotrej(0, p, len); + } + } +} + +/* + * cleanup - clean_up before we exit + */ +/* ARGSUSED */ +static void + cleanup(status, arg) +int status; +caddr_t arg; +{ + adjtimeout(); + lcp_lowerdown(0); + if (unlink(pidfilename) < 0) + syslog(LOG_WARNING, "unable to unlink pid file: %m"); +} + + +/* + * demuxprotrej - Demultiplex a Protocol-Reject. + */ +void + demuxprotrej(unit, protocol) +int unit; +u_short protocol; +{ + int i; + + /* + * Upcall the proper Protocol-Reject routine. + */ + for (i = 0; i < sizeof (prottbl) / sizeof (struct protent); i++) + if (prottbl[i].protocol == protocol) { + (*prottbl[i].protrej)(unit); + return; + } + syslog(LOG_WARNING, "demuxprotrej: Unrecognized Protocol-Reject for protocol %d!", protocol); +} + + +/* + * incdebug - Catch SIGUSR1 signal. + * + * Increment debug flag. + */ +/*ARGSUSED*/ +static void + incdebug(sig, code, scp, addr) +int sig, code; +struct sigcontext *scp; +char *addr; +{ + syslog(LOG_NOTICE, "Debug turned ON, Level %d", debug); + setlogmask(LOG_UPTO(LOG_DEBUG)); + debug++; +} + + +/* + * nodebug - Catch SIGUSR2 signal. + * + * Turn off debugging. + */ +/*ARGSUSED*/ +static void + nodebug(sig, code, scp, addr) +int sig, code; +struct sigcontext *scp; +char *addr; +{ + setlogmask(LOG_UPTO(LOG_WARNING)); + debug = 0; +} + + +/* + * setdebug - Set debug (command line argument). + */ +static int + setdebug(argcp, argvp) +int *argcp; +char ***argvp; +{ + debug++; + setlogmask(LOG_UPTO(LOG_DEBUG)); + --*argcp, ++*argvp; + return (1); +} + +/* + * noopt - Disable all options. + */ +static int + noopt(argcp, argvp) +int *argcp; +char ***argvp; +{ + bzero((char *) &lcp_wantoptions[0], sizeof (struct lcp_options)); + bzero((char *) &lcp_allowoptions[0], sizeof (struct lcp_options)); + bzero((char *) &ipcp_wantoptions[0], sizeof (struct ipcp_options)); + bzero((char *) &ipcp_allowoptions[0], sizeof (struct ipcp_options)); + --*argcp, ++*argvp; + return (1); +} + + +/* + * setconnector - Set a program to connect to a serial line + */ +static int + setconnector(argcp, argvp) +int *argcp; +char ***argvp; +{ + + --*argcp, ++*argvp; + + connector = strdup(**argvp); + if (connector == NULL) { + syslog(LOG_ERR, "cannot allocate space for connector string"); + exit(1); + } + + --*argcp, ++*argvp; + return (1); +} + + +/* + * set_up_connection - run a program to initialize the serial connector + */ +int set_up_connection(program, in, out) + char *program; + int in, out; +{ + int pid; + int flags; + int status; + + flags = sigblock(sigmask(SIGINT)|sigmask(SIGHUP)); + pid = fork(); + + if (pid < 0) { + syslog(LOG_ERR, "fork"); + exit(1); + } + + if (pid == 0) { + (void) setreuid(getuid(), getuid()); + (void) setregid(getgid(), getgid()); + (void) sigsetmask(flags); + (void) dup2(in, 0); + (void) dup2(out, 1); + (void) execl("/bin/sh", "sh", "-c", program, (char *)0); + syslog(LOG_ERR, "could not exec /bin/sh"); + _exit(99); + } + else { + while (waitpid(pid, &status, 0) != pid) { + if (errno == EINTR) + continue; + syslog(LOG_ERR, "waiting for connection process"); + exit(1); + } + (void) sigsetmask(flags); + } + return (status == 0 ? 0 : -1); +} + +/* + * noaccomp - Disable Address/Control field compression negotiation. + */ +static int + noaccomp(argcp, argvp) +int *argcp; +char ***argvp; +{ + lcp_wantoptions[0].neg_accompression = 0; + lcp_allowoptions[0].neg_accompression = 0; + --*argcp, ++*argvp; + return (1); +} + + +/* + * noasyncmap - Disable async map negotiation. + */ +static int + noasyncmap(argcp, argvp) +int *argcp; +char ***argvp; +{ + lcp_wantoptions[0].neg_asyncmap = 0; + lcp_allowoptions[0].neg_asyncmap = 0; + --*argcp, ++*argvp; + return (1); +} + + +/* + * noipaddr - Disable IP address negotiation. + */ +static int + noipaddr(argcp, argvp) +int *argcp; +char ***argvp; +{ + ipcp_wantoptions[0].neg_addrs = 0; + ipcp_allowoptions[0].neg_addrs = 0; + --*argcp, ++*argvp; + return (1); +} + + +/* + * nomagicnumber - Disable magic number negotiation. + */ +static int + nomagicnumber(argcp, argvp) +int *argcp; +char ***argvp; +{ + lcp_wantoptions[0].neg_magicnumber = 0; + lcp_allowoptions[0].neg_magicnumber = 0; + --*argcp, ++*argvp; + return (1); +} + + +/* + * nomru - Disable mru negotiation. + */ +static int + nomru(argcp, argvp) +int *argcp; +char ***argvp; +{ + lcp_wantoptions[0].neg_mru = 0; + lcp_allowoptions[0].neg_mru = 0; + --*argcp, ++*argvp; + return (1); +} + + +/* + * setmru - Set MRU for negotiation. + */ +static int + setmru(argcp, argvp) +int *argcp; +char ***argvp; +{ + --*argcp, ++*argvp; + lcp_wantoptions[0].mru = atoi(**argvp); + --*argcp, ++*argvp; + return (1); +} + + +/* + * nopcomp - Disable Protocol field compression negotiation. + */ +static int + nopcomp(argcp, argvp) +int *argcp; +char ***argvp; +{ + lcp_wantoptions[0].neg_pcompression = 0; + lcp_allowoptions[0].neg_pcompression = 0; + --*argcp, ++*argvp; + return (1); +} + + +/* + * setpassive - Set passive mode. + */ +static int + setpassive(argcp, argvp) +int *argcp; +char ***argvp; +{ + lcp_wantoptions[0].passive = 1; + --*argcp, ++*argvp; + return (1); +} + + +/* + * noupap - Disable UPAP authentication. + */ +static int + noupap(argcp, argvp) +int *argcp; +char ***argvp; +{ + lcp_allowoptions[0].neg_upap = 0; + --*argcp, ++*argvp; + return (1); +} + + +/* + * requpap - Require UPAP authentication. + */ +static int + requpap(argcp, argvp) +int *argcp; +char ***argvp; +{ + FILE * ufile; + struct stat sbuf; + + lcp_wantoptions[0].neg_upap = 1; + lcp_allowoptions[0].neg_upap = 0; + --*argcp, ++*argvp; + strcpy(uinfopath, **argvp); + --*argcp, ++*argvp; + + /* open user info file */ + + if ((ufile = fopen(uinfopath, "r")) == NULL) { + fprintf(stderr, "unable to open user login data file %s\n", uinfopath); + exit(1); + }; + + if (fstat(fileno(ufile), &sbuf) < 0) { + perror("cannot stat user login data file!"); + exit(1); + } + if ((sbuf.st_mode & 077) != 0) + syslog(LOG_WARNING, "Warning - user info file has world and/or group access!\n"); + + /* get username */ + fgets(user, sizeof (user) - 1, ufile); + if (strlen(user) == 0) { + fprintf(stderr, "Unable to get user name from user login data file.\n"); + exit(2); + } + /* get rid of newline */ + user[strlen(user) - 1] = '\000'; + + fgets(passwd, sizeof(passwd) - 1, ufile); + + if (strlen(passwd) == 0) { + fprintf(stderr, "Unable to get password from user login data file.\n"); + exit(2); + } + + passwd[strlen(passwd) - 1] = '\000'; + + return (1); +} + + +/* + * nochap - Disable CHAP authentication. + */ +static int + nochap(argcp, argvp) +int *argcp; +char ***argvp; +{ + lcp_allowoptions[0].neg_chap = 0; + --*argcp, ++*argvp; + return (1); +} + + +/* + * reqchap - Require CHAP authentication. + */ +static int + reqchap(argcp, argvp) +int *argcp; +char ***argvp; +{ + lcp_wantoptions[0].neg_chap = 1; + lcp_allowoptions[0].neg_chap = 0; + --*argcp, ++*argvp; + return (1); +} + + +/* + * setvjmode - Set vj compression mode + */ + +static int + setvjmode(argcp, argvp) +int *argcp; +char ***argvp; +{ + extern int ipcp_vj_mode; + + --*argcp, ++*argvp; + + if (!strcmp(**argvp, "old")) { /* "old" mode */ + ipcp_vj_setmode(IPCP_VJMODE_OLD); + } + + else if (!strcmp(**argvp, "rfc1172")) { /* "rfc1172" mode*/ + ipcp_vj_setmode(IPCP_VJMODE_RFC1172); + } + + else if (!strcmp(**argvp, "rfc1332")) { /* "rfc1332" default mode */ + ipcp_vj_setmode(IPCP_VJMODE_RFC1332); + } + else { + syslog(LOG_WARNING, + "Unknown vj compression mode %s. Defaulting to RFC1332", **argvp); + ipcp_vj_setmode(IPCP_VJMODE_RFC1332); + } + --*argcp, ++*argvp; + + return (1); +} +/* + * setnovj - diable vj compression + */ + +static int + setnovj(argcp, argvp) +int *argcp; +char ***argvp; +{ + extern int ipcp_vj_mode; + + --*argcp, ++*argvp; + ipcp_wantoptions[0].neg_vj = 0; + ipcp_allowoptions[0].neg_vj = 0; + + return (1); +} + +/* + * setdomain - Set domain name to append to hostname + */ +static int + setdomain(argcp, argvp) +int *argcp; +char ***argvp; +{ + + --*argcp, ++*argvp; + + strcat(hostname, **argvp); + hostname_len = strlen(hostname); + + --*argcp, ++*argvp; + + return (1); +} + +/* + * Valid speeds. + */ +struct speed { + int speed_int, speed_val; +} speeds[] = { +#ifdef B50 + { 50, B50 }, +#endif +#ifdef B75 + { 75, B75 }, +#endif +#ifdef B110 + { 110, B110 }, +#endif +#ifdef B150 + { 150, B150 }, +#endif +#ifdef B200 + { 200, B200 }, +#endif +#ifdef B300 + { 300, B300 }, +#endif +#ifdef B600 + { 600, B600 }, +#endif +#ifdef B1200 + { 1200, B1200 }, +#endif +#ifdef B1800 + { 1800, B1800 }, +#endif +#ifdef B2000 + { 2000, B2000 }, +#endif +#ifdef B2400 + { 2400, B2400 }, +#endif +#ifdef B3600 + { 3600, B3600 }, +#endif +#ifdef B4800 + { 4800, B4800 }, +#endif +#ifdef B7200 + { 7200, B7200 }, +#endif +#ifdef B9600 + { 9600, B9600 }, +#endif +#ifdef EXTA + { 19200, EXTA }, +#endif +#ifdef EXTB + { 38400, EXTB }, +#endif +#ifdef B57600 + { 57600, B57600 }, +#endif +#ifdef B115200 + { 115200, B115200 }, +#endif + { 0, 0 } +}; + +static int + setasyncmap(argcp, argvp) +int *argcp; +char ***argvp; +{ + unsigned long asyncmap; + + asyncmap = 0xffffffff; + ++*argvp; + sscanf(**argvp,"%lx",&asyncmap); + ++*argvp; + lcp_wantoptions[0].asyncmap = asyncmap; + *argcp -= 2; + return(1); +} + +/* + * setspeed - Set the speed. + */ +static int + setspeed(argcp, argvp) +int *argcp; +char ***argvp; +{ + int speed; + struct speed *speedp; + + speed = atoi(**argvp); + for (speedp = speeds; speedp->speed_int; speedp++) + if (speed == speedp->speed_int) { + inspeed = speedp->speed_val; + --*argcp, ++*argvp; + return (1); + } + return (0); +} + + +/* + * setdevname - Set the device name. + */ +int setdevname(argcp, argvp) + int *argcp; + char ***argvp; +{ + char dev[DEVNAME_SIZE]; + char *cp = **argvp; + struct stat statbuf; + char *tty, *ttyname(); + + if (strncmp("/dev/", cp, sizeof ("/dev/") - 1)) { + (void) sprintf(dev, "/dev/%s", cp); + cp = dev; + } + + /* + * Check if there is a device by this name. + */ + if (stat(cp, &statbuf) < 0) { + if (errno == ENOENT) + return (0); + syslog(LOG_ERR, cp); + exit(1); + } + + (void) strcpy(devname, cp); + default_device = FALSE; + --*argcp, ++*argvp; + + /* + * If we haven't already decided to require authentication, + * or we are running ppp on the control terminal, then we can + * allow authentication to be requested. + */ + if ((tty = ttyname(fileno(stdin))) == NULL) + tty = ""; /* running from init means no stdin. Null kills strcmp -KWK */ + if (lcp_wantoptions[0].neg_upap == 0 && + strcmp(devname, "/dev/tty") && + strcmp(devname, tty)) { + lcp_wantoptions[0].neg_upap = 0; + lcp_allowoptions[0].neg_upap = 1; + } + return (1); +} + + +/* + * setipaddr - Set the IP address + */ +int setipaddr(argcp, argvp) + int *argcp; + char ***argvp; +{ + u_long local, remote; + struct hostent *hp; + char *colon, *index(); + + /* + * IP address pair separated by ":". + */ + if ((colon = index(**argvp, ':')) == NULL) + return (0); + + /* + * If colon first character, then no local addr. + */ + if (colon == **argvp) { + local = 0l; + ++colon; + } + else { + *colon++ = '\0'; + if ((local = inet_addr(**argvp)) == -1) { + if ((hp = gethostbyname(**argvp)) == NULL) { + syslog(LOG_WARNING, "unknown host: %s", **argvp); + goto ret; + } + bcopy(hp->h_addr, (char *) &local, hp->h_length); + } + } + + /* + * If colon last character, then no remote addr. + */ + if (*colon == '\0') + remote = 0l; + else { + if ((remote = inet_addr(colon)) == -1) { + if ((hp = gethostbyname(colon)) == NULL) { + syslog(LOG_WARNING,"unknown host: %s", colon); + goto ret; + } + bcopy(hp->h_addr, (char *) &remote, hp->h_length); + } + } + + ipcp_wantoptions[0].neg_addrs = 1; + ipcp_wantoptions[0].ouraddr = local; + ipcp_wantoptions[0].hisaddr = remote; + + ret: + --*argcp, ++*argvp; + return (1); +} + +static int + setnetmask(argcp, argvp) +int *argcp; +char ***argvp; +{ + u_long mask; + + --*argcp, ++*argvp; + if ((mask = inet_addr(**argvp)) == -1) { + fprintf(stderr, "Invalid netmask %s\n", **argvp); + exit(1); + } + + netmask = mask; + --*argcp, ++*argvp; + return (1); +} + +static int + setcrtscts(argcp, argvp) +int *argcp; +char ***argvp; +{ + crtscts = 1; + --*argcp, ++*argvp; + return (1); +} + +static int + setnodetach(argcp, argvp) +int *argcp; +char ***argvp; +{ + nodetach = 1; + --*argcp, ++*argvp; + return (1); +} + +/* + * getuserpasswd - Get the user name and passwd. + */ +static void + getuserpasswd() +{ + + upap[0].us_user = user; + upap[0].us_userlen = strlen(upap[0].us_user); + + upap[0].us_passwd = passwd; + upap[0].us_passwdlen = strlen(upap[0].us_passwd); +} + + +/* + * login - Check the user name and passwd and login the user. + * + * returns: + * UPAP_AUTHNAK: Login failed. + * UPAP_AUTHACK: Login succeeded. + * In either case, msg points to an appropriate message. + */ +u_char + login(user, userlen, passwd, passwdlen, msg, msglen) +char *user; +int userlen; +char *passwd; +int passwdlen; +char **msg; +int *msglen; +{ + struct passwd *pw; + char *epasswd, *crypt(); + static int attempts = 0; + char *tty, *rindex(); + char *tmp_passwd, *tmp_user; + + /* why alloca.h doesn't define what alloca() returns is a mystery */ + /* seems to be defined in stdlib.h for FreeBSD, rgrimes */ + +#ifdef sparc + char *__builtin_alloca __ARGS((int)); +#else +#ifndef __386BSD__ + char *alloca __ARGS((int)); +#endif /* !__386BSD__ */ +#endif /*sparc*/ + tmp_passwd = alloca(passwdlen + 1); /* we best make copies before */ + /* null terminating the string */ + if (tmp_passwd == NULL) { + syslog(LOG_ERR, "alloca failed"); + exit(1); + } + bcopy(passwd, tmp_passwd, passwdlen); + tmp_passwd[passwdlen] = '\0'; + + tmp_user = alloca(userlen + 1); + if (tmp_user == NULL) { + syslog(LOG_ERR, "alloca failed"); + exit(1); + } + bcopy(user, tmp_user, userlen); + tmp_user[userlen] = '\0'; + + if ((pw = getpwnam(tmp_user)) == NULL) { + *msg = "Login incorrect"; + *msglen = strlen(*msg); + syslog(LOG_WARNING, "upap login userid '%s' incorrect",tmp_user); + return (UPAP_AUTHNAK); + } + + /* + * XXX If no passwd, let them login without one. + */ + if (pw->pw_passwd == '\0') { + *msg = "Login ok"; + *msglen = strlen(*msg); + return (UPAP_AUTHACK); + } + + epasswd = crypt(tmp_passwd, pw->pw_passwd); + if (strcmp(epasswd, pw->pw_passwd)) { + *msg = "Login incorrect"; + *msglen = strlen(*msg); + syslog(LOG_WARNING, "upap login password '%s' incorrect", tmp_passwd); + /* + * Frustrate passwd stealer programs. + * Allow 10 tries, but start backing off after 3 (stolen from login). + * On 10'th, drop the connection. + */ + if (attempts++ >= 10) { + syslog(LOG_WARNING, "%d LOGIN FAILURES ON %s, %s", + attempts, devname, tmp_user); + lcp_close(0); /* Drop DTR? */ + } + if (attempts > 3) + sleep((u_int) (attempts - 3) * 5); + return (UPAP_AUTHNAK); + } + + attempts = 0; /* Reset count */ + *msg = "Login ok"; + *msglen = strlen(*msg); + syslog(LOG_NOTICE, "user %s logged in", tmp_user); + tty = rindex(devname, '/'); + if (tty == NULL) + tty = devname; + else + tty++; + logwtmp(tty, tmp_user, ""); /* Add wtmp login entry */ + + return (UPAP_AUTHACK); +} + + +/* + * logout - Logout the user. + */ +void logout() +{ + char *tty; + + tty = rindex(devname, '/'); + if (tty == NULL) + tty = devname; + else + tty++; + logwtmp(tty, "", ""); /* Add wtmp logout entry */ +} + + +/* + * getuseropt - Get the options from /etc/hosts.ppp for this user. + */ +int getuseropt(user) + char *user; +{ + char buf[1024], *s; + FILE *fp; + int rc = 0; + + if ((fp = fopen(PPPHOSTS, "r")) == NULL) + return (0);; + + /* + * Loop till we find an entry for this user. + */ + for (;;) { + if (fgets(buf, sizeof (buf), fp) == NULL) { + if (feof(fp)) + break; + else { + syslog(LOG_ERR, "fgets"); + exit(1); + } + } + if ((s = index(buf, ' ')) == NULL) + continue; + *s++ = '\0'; + if (!strcmp(user, buf)) { + rc = 1; + break; + } + } + fclose(fp); + return (rc); +} +/* + * open "secret" file and return the secret matching the given name. + * If no secret for a given name is found, use the one for "default". + */ + +void + get_secret(name, secret, secret_len) +u_char * name; +u_char * secret; +int * secret_len; +{ + FILE * sfile; + struct stat sbuf; + u_char fname[256]; + int match_found, default_found; + + match_found = FALSE; + default_found = FALSE; + + if ((sfile = fopen(_PATH_CHAPFILE, "r")) == NULL) { + syslog(LOG_ERR, "unable to open secret file %s", _PATH_CHAPFILE); + exit(1); + }; + + if (fstat(fileno(sfile), &sbuf) < 0) { + syslog(LOG_ERR, "cannot stat secret file!: %m"); + exit(1); + } + if ((sbuf.st_mode & 077) != 0) + syslog(LOG_WARNING, "Warning - secret file has world and/or group access!"); + + while (!feof(sfile) && !match_found) { + if (fscanf(sfile, "%s %s", fname, secret) == EOF) + break; + if (!strcasecmp((char *)fname, (char *)name)) { + match_found = TRUE; + } + if (!strcasecmp("default", (char *)name)) { + default_found = TRUE; + } + } + + if (!match_found && !default_found) { + syslog(LOG_ERR, "No match or default entry found for %s in CHAP secret file! Aborting...", name); + cleanup(0, NULL); /* shut us down */ + } +#ifdef UNSECURE +/* while this is useful for debugging, it is a security hole as well */ + + syslog(LOG_DEBUG, "get_secret: found secret %s", secret); +#endif /*UNSECURE*/ + fclose(sfile); + *secret_len = strlen((char *)secret); + if (*secret_len > MAX_SECRET_LEN) { /* don't let it overflow the buffer */ + syslog(LOG_ERR, "Length of secret for host %s is greater than the maximum %d characters! ", name, MAX_SECRET_LEN); + cleanup(0, NULL); /* scream and die */ + } + return; +} +/* + * Return user specified netmask. A value of zero means no netmask has + * been set. + */ +/* ARGSUSED */ +u_long + GetMask(addr) +u_long addr; +{ + return(netmask); +} + +#ifdef STREAMS +/* + * this module will attempt to reconstruct the stream with the + * previously popped modules + */ + +/*ARGSUSED*/ +static void + str_restore() +{ + /*EMPTY*/ + while(ioctl(fd, I_POP, 0) == 0); /* pop any we pushed */ + + for(; str_module_count > 0; str_module_count--) { + if(ioctl(fd, I_PUSH, str_modules[str_module_count-1].modname)) { + syslog(LOG_ERR, "str_restore: couldn't push module %s: %m", + str_modules[str_module_count-1].modname); + } + else { + MAINDEBUG((LOG_INFO, "str_restore: pushed module %s", + str_modules[str_module_count-1].modname)) + } + } +} +#endif + +dumpbuffer(buffer, size, level) +unsigned char *buffer; +int size; +int level; +{ + register int i; + char line[256], *p; + + printf("%d bytes:\n", size); + while (size > 0) + { + p = line; + sprintf(p, "%08lx: ", buffer); + p += 10; + + for (i = 0; i < 8; i++, p += 3) + if (size - i <= 0) + sprintf(p, "xx "); + else + sprintf(p, "%02x ", buffer[i]); + + for (i = 0; i < 8; i++) + if (size - i <= 0) + *p++ = 'x'; + else + *p++ = (' ' <= buffer[i] && buffer[i] <= '~') ? + buffer[i] : '.'; + + *p++ = 0; + buffer += 8; + size -= 8; + +/* syslog(level, "%s\n", line); */ + printf("%s\n", line); + fflush(stdout); + } +} + +#ifdef sun +setdtr(fd, on) +int fd, on; +{ + int linestate; + + ioctl(fd, TIOCMGET, &linestate); + + if (on) + linestate |= TIOCM_DTR; + else + linestate &= ~TIOCM_DTR; + + ioctl(fd, TIOCMSET, &linestate); +} +#endif +#ifdef __386BSD__ +setdtr(fd, on) +int fd, on; +{ + int modembits = TIOCM_DTR; + + if (on) + ioctl(fd, TIOCMBIS, &modembits); + else + ioctl(fd, TIOCMBIC, &modembits); +} +#endif + +char * +proto_name(proto) +u_short proto; +{ + switch (proto) { + case LCP: return "lcp"; + case UPAP: return "pap"; + case CHAP: return "chap"; + case IPCP: return "ipcp"; +#define LQM 0xc025 + case LQM: return "lqm"; + } + return ""; +} + +#include + +char line[256]; +char *p; + +logf(level, fmt, va_alist) +int level; +char *fmt; +va_dcl +{ + va_list pvar; + char buf[256]; + + va_start(pvar); + vsprintf(buf, fmt, pvar); + va_end(pvar); + + p = line + strlen(line); + strcat(p, buf); + + if (buf[strlen(buf)-1] == '\n') { + syslog(level, "%s", line); + line[0] = 0; + } +} diff --git a/libexec/pppd/md5.c b/libexec/pppd/md5.c new file mode 100644 index 0000000000..d9ef47b76f --- /dev/null +++ b/libexec/pppd/md5.c @@ -0,0 +1,298 @@ + + +/* + *********************************************************************** + ** md5.c -- the source code for MD5 routines ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#include "md5.h" + +/* + *********************************************************************** + ** Message-digest routines: ** + ** To form the message digest for a message M ** + ** (1) Initialize a context buffer mdContext using MD5Init ** + ** (2) Call MD5Update on mdContext and M ** + ** (3) Call MD5Final on mdContext ** + ** The message digest is now in mdContext->digest[0...15] ** + *********************************************************************** + */ + +/* forward declaration */ +static void Transform (); + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G, H and I are basic MD5 functions */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* The routine MD5Init initializes the message-digest context + mdContext. All fields are set to zero. + */ +void MD5Init (mdContext) +MD5_CTX *mdContext; +{ + mdContext->i[0] = mdContext->i[1] = (UINT4)0; + + /* Load magic initialization constants. + */ + mdContext->buf[0] = (UINT4)0x67452301; + mdContext->buf[1] = (UINT4)0xefcdab89; + mdContext->buf[2] = (UINT4)0x98badcfe; + mdContext->buf[3] = (UINT4)0x10325476; +} + +/* The routine MD5Update updates the message-digest context to + account for the presence of each of the characters inBuf[0..inLen-1] + in the message whose digest is being computed. + */ +void MD5Update (mdContext, inBuf, inLen) +MD5_CTX *mdContext; +unsigned char *inBuf; +unsigned int inLen; +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0]) + mdContext->i[1]++; + mdContext->i[0] += ((UINT4)inLen << 3); + mdContext->i[1] += ((UINT4)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +/* The routine MD5Final terminates the message-digest computation and + ends with the desired message digest in mdContext->digest[0...15]. + */ +void MD5Final (mdContext) +MD5_CTX *mdContext; +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } +} + +/* Basic MD5 step. Transforms buf based on in. + */ +static void Transform (buf, in) +UINT4 *buf; +UINT4 *in; +{ + UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF ( a, b, c, d, in[ 0], S11, 3614090360UL); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, 3905402710UL); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, 606105819UL); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, 3250441966UL); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, 4118548399UL); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, 1200080426UL); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, 2821735955UL); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, 4249261313UL); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, 1770035416UL); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, 2336552879UL); /* 10 */ + FF ( c, d, a, b, in[10], S13, 4294925233UL); /* 11 */ + FF ( b, c, d, a, in[11], S14, 2304563134UL); /* 12 */ + FF ( a, b, c, d, in[12], S11, 1804603682UL); /* 13 */ + FF ( d, a, b, c, in[13], S12, 4254626195UL); /* 14 */ + FF ( c, d, a, b, in[14], S13, 2792965006UL); /* 15 */ + FF ( b, c, d, a, in[15], S14, 1236535329UL); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, 4129170786UL); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, 3225465664UL); /* 18 */ + GG ( c, d, a, b, in[11], S23, 643717713UL); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, 3921069994UL); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, 3593408605UL); /* 21 */ + GG ( d, a, b, c, in[10], S22, 38016083UL); /* 22 */ + GG ( c, d, a, b, in[15], S23, 3634488961UL); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, 3889429448UL); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, 568446438UL); /* 25 */ + GG ( d, a, b, c, in[14], S22, 3275163606UL); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, 4107603335UL); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, 1163531501UL); /* 28 */ + GG ( a, b, c, d, in[13], S21, 2850285829UL); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, 4243563512UL); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, 1735328473UL); /* 31 */ + GG ( b, c, d, a, in[12], S24, 2368359562UL); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, 4294588738UL); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, 2272392833UL); /* 34 */ + HH ( c, d, a, b, in[11], S33, 1839030562UL); /* 35 */ + HH ( b, c, d, a, in[14], S34, 4259657740UL); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, 2763975236UL); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, 1272893353UL); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, 4139469664UL); /* 39 */ + HH ( b, c, d, a, in[10], S34, 3200236656UL); /* 40 */ + HH ( a, b, c, d, in[13], S31, 681279174UL); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, 3936430074UL); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, 3572445317UL); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, 76029189UL); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, 3654602809UL); /* 45 */ + HH ( d, a, b, c, in[12], S32, 3873151461UL); /* 46 */ + HH ( c, d, a, b, in[15], S33, 530742520UL); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, 3299628645UL); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, 4096336452UL); /* 49 */ + II ( d, a, b, c, in[ 7], S42, 1126891415UL); /* 50 */ + II ( c, d, a, b, in[14], S43, 2878612391UL); /* 51 */ + II ( b, c, d, a, in[ 5], S44, 4237533241UL); /* 52 */ + II ( a, b, c, d, in[12], S41, 1700485571UL); /* 53 */ + II ( d, a, b, c, in[ 3], S42, 2399980690UL); /* 54 */ + II ( c, d, a, b, in[10], S43, 4293915773UL); /* 55 */ + II ( b, c, d, a, in[ 1], S44, 2240044497UL); /* 56 */ + II ( a, b, c, d, in[ 8], S41, 1873313359UL); /* 57 */ + II ( d, a, b, c, in[15], S42, 4264355552UL); /* 58 */ + II ( c, d, a, b, in[ 6], S43, 2734768916UL); /* 59 */ + II ( b, c, d, a, in[13], S44, 1309151649UL); /* 60 */ + II ( a, b, c, d, in[ 4], S41, 4149444226UL); /* 61 */ + II ( d, a, b, c, in[11], S42, 3174756917UL); /* 62 */ + II ( c, d, a, b, in[ 2], S43, 718787259UL); /* 63 */ + II ( b, c, d, a, in[ 9], S44, 3951481745UL); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/* + *********************************************************************** + ** End of md5.c ** + ******************************** (cut) ******************************** + */ diff --git a/libexec/pppd/md5.h b/libexec/pppd/md5.h new file mode 100644 index 0000000000..535b18d4b7 --- /dev/null +++ b/libexec/pppd/md5.h @@ -0,0 +1,58 @@ +/* + *********************************************************************** + ** md5.h -- header file for implementation of MD5 ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ** Revised (for MD5): RLR 4/27/91 ** + ** -- G modified to have y&~z instead of y&z ** + ** -- FF, GG, HH modified to add in last register done ** + ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** + ** -- distinct additive constant for each step ** + ** -- round 4 added, working mod 7 ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#ifndef __MD5_INCLUDE__ + +/* typedef a 32-bit type */ +typedef unsigned long int UINT4; + +/* Data structure for MD5 (Message-Digest) computation */ +typedef struct { + UINT4 i[2]; /* number of _bits_ handled mod 2^64 */ + UINT4 buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +void MD5Init (); +void MD5Update (); +void MD5Final (); + +#define __MD5_INCLUDE__ +#endif /* __MD5_INCLUDE__ */ diff --git a/libexec/pppd/md5driver.c b/libexec/pppd/md5driver.c new file mode 100644 index 0000000000..cbb8f38285 --- /dev/null +++ b/libexec/pppd/md5driver.c @@ -0,0 +1,196 @@ + + +/* + *********************************************************************** + ** md5driver.c -- sample test routines ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/16/90 RLR ** + ** Updated: 1/91 SRD ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#include +#include +#include +#include +#include "md5.h" + +/* Prints message digest buffer in mdContext as 32 hexadecimal digits. + Order is from low-order byte to high-order byte of digest. + Each byte is printed with high-order hexadecimal digit first. + */ +static void MDPrint (mdContext) +MD5_CTX *mdContext; +{ + int i; + + for (i = 0; i < 16; i++) + printf ("%02x", mdContext->digest[i]); +} + +/* size of test block */ +#define TEST_BLOCK_SIZE 1000 + +/* number of blocks to process */ +#define TEST_BLOCKS 10000 + +/* number of test bytes = TEST_BLOCK_SIZE * TEST_BLOCKS */ +static long TEST_BYTES = (long)TEST_BLOCK_SIZE * (long)TEST_BLOCKS; + +/* A time trial routine, to measure the speed of MD5. + Measures wall time required to digest TEST_BLOCKS * TEST_BLOCK_SIZE + characters. + */ +static void MDTimeTrial () +{ + MD5_CTX mdContext; + time_t endTime, startTime; + unsigned char data[TEST_BLOCK_SIZE]; + unsigned int i; + + /* initialize test data */ + for (i = 0; i < TEST_BLOCK_SIZE; i++) + data[i] = (unsigned char)(i & 0xFF); + + /* start timer */ + printf ("MD5 time trial. Processing %ld characters...\n", TEST_BYTES); + time (&startTime); + + /* digest data in TEST_BLOCK_SIZE byte blocks */ + MD5Init (&mdContext); + for (i = TEST_BLOCKS; i > 0; i--) + MD5Update (&mdContext, data, TEST_BLOCK_SIZE); + MD5Final (&mdContext); + + /* stop timer, get time difference */ + time (&endTime); + MDPrint (&mdContext); + printf (" is digest of test input.\n"); + printf + ("Seconds to process test input: %ld\n", (long)(endTime-startTime)); + printf + ("Characters processed per second: %ld\n", + TEST_BYTES/(endTime-startTime)); +} + +/* Computes the message digest for string inString. + Prints out message digest, a space, the string (in quotes) and a + carriage return. + */ +static void MDString (inString) +char *inString; +{ + MD5_CTX mdContext; + unsigned int len = strlen (inString); + + MD5Init (&mdContext); + MD5Update (&mdContext, inString, len); + MD5Final (&mdContext); + MDPrint (&mdContext); + printf (" \"%s\"\n", inString); +} + +static void MDFile (filename) +char *filename; +{ + FILE *inFile = fopen (filename, "rb"); + MD5_CTX mdContext; + int bytes; + unsigned char data[1024]; + + if (inFile == NULL) { + printf ("%s can't be opened.\n", filename); + return; + } + + MD5Init (&mdContext); + while ((bytes = fread (data, 1, 1024, inFile)) != 0) + MD5Update (&mdContext, data, bytes); + MD5Final (&mdContext); + MDPrint (&mdContext); + printf (" %s\n", filename); + fclose (inFile); +} + +/* Writes the message digest of the data from stdin onto stdout, + followed by a carriage return. + */ +static void MDFilter () +{ + MD5_CTX mdContext; + int bytes; + unsigned char data[16]; + + MD5Init (&mdContext); + while ((bytes = fread (data, 1, 16, stdin)) != 0) + MD5Update (&mdContext, data, bytes); + MD5Final (&mdContext); + MDPrint (&mdContext); + printf ("\n"); +} + +/* Runs a standard suite of test data. + */ +static void MDTestSuite () +{ + printf ("MD5 test suite results:\n"); + MDString (""); + MDString ("a"); + MDString ("abc"); + MDString ("message digest"); + MDString ("abcdefghijklmnopqrstuvwxyz"); + MDString + ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + MDString + ("1234567890123456789012345678901234567890\ +1234567890123456789012345678901234567890"); + /* Contents of file foo are "abc" */ + MDFile ("foo"); +} + +void main (argc, argv) +int argc; +char *argv[]; +{ + int i; + + /* For each command line argument in turn: + ** filename -- prints message digest and name of file + ** -sstring -- prints message digest and contents of string + ** -t -- prints time trial statistics for 10M + characters + ** -x -- execute a standard suite of test data + ** (no args) -- writes messages digest of stdin onto stdout + */ + if (argc == 1) + MDFilter (); + else + for (i = 1; i < argc; i++) + if (argv[i][0] == '-' && argv[i][1] == 's') + MDString (argv[i] + 2); + else if (strcmp (argv[i], "-t") == 0) + MDTimeTrial (); + else if (strcmp (argv[i], "-x") == 0) + MDTestSuite (); + else MDFile (argv[i]); +} + +/* + *********************************************************************** + ** End of md5driver.c ** + ******************************** (cut) ******************************** + */ diff --git a/libexec/pppd/patchlevel.h b/libexec/pppd/patchlevel.h new file mode 100644 index 0000000000..3c62d52860 --- /dev/null +++ b/libexec/pppd/patchlevel.h @@ -0,0 +1,4 @@ +#define PATCHLEVEL 0 + +#define VERSION "1.3" +#define DATE "17 Jun 93" diff --git a/libexec/pppd/pathnames.h b/libexec/pppd/pathnames.h new file mode 100644 index 0000000000..dac7cd94c5 --- /dev/null +++ b/libexec/pppd/pathnames.h @@ -0,0 +1,11 @@ + +/* + * define path names + */ + +#define PPPHOSTS "/etc/ppp/hosts" + +#define _PATH_DEBUG "/usr/tmp/" +#define _PATH_PIDFILE "/etc/ppp" +#define _PATH_UPAPFILE "/etc/ppp/upap" +#define _PATH_CHAPFILE "/etc/ppp/chap" diff --git a/libexec/pppd/pppd.8 b/libexec/pppd/pppd.8 new file mode 100644 index 0000000000..ab57223caa --- /dev/null +++ b/libexec/pppd/pppd.8 @@ -0,0 +1,281 @@ +.\" manual page v0.4 [3/3/93] for pppd 1.2beta +.\" SH section heading +.\" SS subsection heading +.\" LP paragraph +.\" IP indented paragraph +.\" TP hanging label +.TH PPPD 8 +.SH NAME +pppd \- Point to Point Protocol daemon +.SH SYNOPSIS +.B pppd +[ +.I option +] +.B tty_name speed +[ +.I local_IP_address +]:[ +.I remote_IP_address +] +.SH DESCRIPTION +.LP +The Point-to-Point Protocol (PPP) provides a method for transmitting +datagrams over serial point-to-point links. +.I pppd +is composed of three parts: +.TP +1. A method for encapsulating datagrams over serial links. +.TP +2. An extensible Link Control Protocol (LCP). +.TP +3. A family of Network Control Protocols (NCP) for establishing +and configuring different network-layer protocols. +.LP +.I pppd +currently supports the encapsulation scheme, the basic LCP, and an +NCP for establishing and configuring the Internet Protocol (IP) +(called the IP Control Protocol, IPCP). +.SH OPTIONS +.TP +.B -all +Don't request/allow any options +.TP +.B -ac +Disable Address/Control compression negotiation +.TP +.B -am +Disable asyncmap negotiation +.TP +.B -as +Set the desired async map to hex . The default async map is 0xffffffff. +.TP +.B -d +Increase debugging level +.TP +.B -detach +Don't fork to become a background process +.TP +.B -ip +Disable IP address negotiation +.TP +.B -mn +Disable magic number negotiation +.TP +.B -mru +Disable MRU [Maximum Receive Unit] negotiation +.TP +.B -p +Set passive mode +.TP +.B -pc +Disable protocol field compression negotiation +.TP +.B +ua

+Require UPAP [User/Password Authentication Protocol] authentication. +Use the data in file

for the user and password to send to the +peer. The file contains the remote user name, followed by a newline, +followed by the remote password. +.TP +.B -ua +Don't allow UPAP authentication +.TP +.B +chap +Require CHAP [Cryptographic Handshake Authentication Protocol] authentication. +Use the data in file /usr/local/etc/ppp/chap, which contains +host name/secret pairs separated by newlines, to authenticate the peer. +In the data file, a host name of "default" will match any host name +not already specified in the file. +.TP +.B -chap +Don't allow CHAP authentication +.TP +.B asyncmap +Set the async character map to . +This map describes which control characters to "escape" in the serial data +stream. +The argument is a 32 bit hex number represented as 8 hex characters, +with each bit representing a character to escape. +The lowest bit (i.e. 00000001) represents the character 0x00 +The highest bit (i.e. 80000000) represents the character 0x1f or ^_. +.TP +.B connect

+Use the executable or shell command specified by

to set-up the +serial line. This script would typically use the "chat" program to +dial the modem and start the remote ppp session. +.TP +.B crtscts +Use hardware flow control (i.e. RTS/CTS) to control the flow of data on +the serial port. +.TP +.B debug +Increase debugging level +.TP +.B domain +Append the domain name to the local host name for authentication +purposes. I.e., if gethostname() returns the name porsche, but the +fully qualified domain name is porsche.Quotron.COM, you would use the +domain option to set the domain name to Quotron.COM. +.TP +.B mru +Set MRU value to for negotiation. The minimum MRU value is 128. +The default MRU value is 1500. +.TP +.B netmask +Set the interface netmask to , a 32 bit netmask in "decimal dot notation" +(i.e. 255.255.255.0). +.TP +.B passive +Set passive mode; Don't send LCP configure requests. Sets initial state to +"listen". +.TP +.B vjmode +Specifies which version of IPCP Van Jacobson Compression negotiation +to use. Specify +.I old +for to have backward compatibility with early versions of +.I pppd +which operated "incorrectly" when negotiating Compression-Type due to a +typographical error in the RFC. +.I pppd +(4.1, patch level 4 and later) +have this problem corrected but to allow interoperability with older versions +you can force this "incorrect" behavior. Specify +.I rfc1172 +for to use the RFC1172 value for negotiation. To use the +new version of negotiation specified in RFC1132 for +IPCP (the default), specify +.I rfc1132 +for . +.TP +.B -vj +This disables the use of Van Jacobson style IP header compression +altogether. +.TP +.B +Communicate over the named device +.TP +.B +Set the baud rate to +.TP +.B : +Set the local and/or remote interface IP addresses. Either one may be omitted. +\".SH EXAMPLES +\".SH FILES +.SH SEE ALSO +.TP +.B RFC1144 +Jacobson, V. +.I Compressing TCP/IP headers for low-speed serial links. +1990 February. +.TP +.B RFC1171 +Perkins, D. +.I Point\-to\-Point Protocol for the transmission of multi\-protocol +.I datagrams over Point\-to\-Point links. +1990 July. +.TP +.B RFC1172 +Perkins, D.; Hobby, R. +.I Point\-to\-Point Protocol (PPP) initial configuration options. +1990 July. +.TP +.B RFC1331 +Simpson, W. +.I The Point\-to\-Point Protocol (PPP) for the Transmission of Multi-protocol +.I Datagrams over Point\-to\-Point Links +1992 May. +.TP +.B RFC1332 +McGregor, G. +.I The PPP Internet Protocol Control Protocol (IPCP). +1991 August. +.TP +.B RFC1333 +Simpson, W. +.I PPP Link Quality Monitoring +1992 May. +.TP +.B Internet Draft +Lloyd, B.; Simpson, W. +.I PPP Authentication Protocols. +1991 November. +.TP +.B Internet Draft +Rivest, R. +.I The MD5 Message-Digest Algorithm. +1991 July. +.SH DIAGNOSTICS +.LP +There is, currently, only one level of debugging available. Debugging is +enabled by setting the -d or debug flag on the command line. Debugging may +also be enabled after +.I pppd +is running by sending a SIGUSR1 to the +.I pppd +process. +Debugging may be disabled by sending a SIGUSR2 to the +.I pppd +process. +.LP +Error and warning messages are sent to the syslog daemon using facility +LOG_DAEMON unless +.I pppd +has been compiled with debugging code. In this case the logging +facility used will be LOG_LOCAL2 in order to allow separation of the debug +output from the other daemons using the LOG_DAEMON facility. You can +override this by defining the macro LOG_PPP to the desired facility +and recompiling. In order to see the error and debug messages, you +will need to edit your /etc/syslog.conf file to direct the messages to +the desired output device or file. +.LP +Debugging is currently available for +.I fsm.c +\- the {Link, IP} Control Protocol Finite State Machine module; +.I lcp.c +\- the PPP Link Control Protocol module; +.I ipcp.c +\- the PPP IP Control Protocol module; +.I upap.c +\- the User/Password Authentication Protocol module; +and +.I chap.c +\- the Challenge Handshake Authentication Protocol module. +.SH NOTES +The following signals have the specified effect when sent to the +.I pppd +process. +.TP +.B SIGINT +This signal is normally generated by a Ctrl-C or DEL. +Causes +.I pppd +to initiate a graceful disconnect and exit. +.I pppd +will adjust the timeouts and close the connection. +.TP +.B SIGTERM +Causes +.I pppd +to initiate a graceful disconnect and exit. +.I pppd +will adjust the timeouts and close the connection. +.TP +.B SIGHUP +Indicates that the physical layer has been disconnected. You will +probably see "Bad file number" errors in the log output due to the way +the STREAMS-based tty driver handles a hangup. You can ignore +these errors in this case. +.I pppd +will adjust the timeouts and reset the connection. +.SH BUGS +Note that sections the current implementation of +.I pppd + are based on older RFCs and draft RFCs and may not be fully +compatible with the latest draft RFC versions. +.SH AUTHORS +Drew Perkins, +Brad Clements, +Karl Fox, +Greg Christy, +Brad Parker (brad@fcr.com) diff --git a/libexec/pppd/pppd.h b/libexec/pppd/pppd.h new file mode 100644 index 0000000000..2a114390e2 --- /dev/null +++ b/libexec/pppd/pppd.h @@ -0,0 +1,436 @@ +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * TODO: + */ + +#ifndef __PPPD_H__ +#define __PPPD_H__ +#include "args.h" +#define NPPP 1 /* One PPP interface supported (per process) */ + +extern int debug; /* Debug flag */ +extern char ifname[]; /* Interface name */ +extern int fd; /* Device file descriptor */ +extern int s; /* socket descriptor */ +extern char hostname[]; /* hostname */ +extern u_char hostname_len; /* and its length */ +extern u_char outpacket_buf[]; /* buffer for outgoing packets */ + +#define MAX_HOSTNAME_LEN 128 /* should be 255 - MAX_CHALLENGE_LEN + 1 */ + +void quit __ARGS((void)); /* Cleanup and exit */ +void timeout __ARGS((void (*)(), caddr_t, int)); + /* Look-alike of kernel's timeout() */ +void untimeout __ARGS((void (*)(), caddr_t)); + /* Look-alike of kernel's untimeout() */ +void output __ARGS((int, u_char *, int)); /* Output a PPP packet */ +void demuxprotrej __ARGS((int, int)); /* Demultiplex a Protocol-Reject */ +u_char login __ARGS((char *, int, char *, int, char **, int *)); /* Login user */ +void logout __ARGS((void)); /* Logout user */ +void get_secret __ARGS((u_char *, u_char *, int *)); /* get "secret" for chap */ +u_long GetMask __ARGS((u_long)); /* get netmask for address */ +extern int errno; + + +/* + * Inline versions of get/put char/short/long. + * Pointer is advanced; we assume that both arguments + * are lvalues and will already be in registers. + * cp MUST be u_char *. + */ +#define GETCHAR(c, cp) { \ + (c) = *(cp)++; \ +} +#define PUTCHAR(c, cp) { \ + *(cp)++ = (c); \ +} + + +#define GETSHORT(s, cp) { \ + (s) = *(cp)++ << 8; \ + (s) |= *(cp)++; \ +} +#define PUTSHORT(s, cp) { \ + *(cp)++ = (s) >> 8; \ + *(cp)++ = (s); \ +} + +#define GETLONG(l, cp) { \ + (l) = *(cp)++ << 8; \ + (l) |= *(cp)++; (l) <<= 8; \ + (l) |= *(cp)++; (l) <<= 8; \ + (l) |= *(cp)++; \ +} +#define PUTLONG(l, cp) { \ + *(cp)++ = (l) >> 24; \ + *(cp)++ = (l) >> 16; \ + *(cp)++ = (l) >> 8; \ + *(cp)++ = (l); \ +} + +#define INCPTR(n, cp) ((cp) += (n)) +#define DECPTR(n, cp) ((cp) -= (n)) + +/* + * System dependent definitions for user-level 4.3BSD UNIX implementation. + */ + +#define DEMUXPROTREJ(u, p) demuxprotrej(u, p) + +#define TIMEOUT(r, f, t) timeout((r), (f), (t)) +#define UNTIMEOUT(r, f) untimeout((r), (f)) + +#define BCOPY(s, d, l) bcopy(s, d, l) +#define EXIT(u) quit() + +#define GETUSERPASSWD(u) +#define LOGIN(n, u, ul, p, pl, m, ml) login(u, ul, p, pl, m, ml); +#define LOGOUT(n) logout() +#define GETSECRET(n, s, sl) get_secret(n, s, sl) +#define PRINTMSG(m, l) { m[l] = '\0'; syslog(LOG_INFO, "Remote message: %s", m); } + +/* + * return a pointer to the beginning of the data part of a packet. + */ + +#define PACKET_DATA(p) (p + DLLHEADERLEN) + +/* + * MAKEHEADER - Add Header fields to a packet. (Should we do + * AC compression here?) + */ +#define MAKEHEADER(p, t) { \ + PUTCHAR(ALLSTATIONS, p); \ + PUTCHAR(UI, p); \ + PUTSHORT(t, p); } + +/* + * SIFASYNCMAP - Config the interface async map. + */ +#ifdef STREAMS +#define SIFASYNCMAP(u, a) { \ + u_long x = a; \ + if(ioctl(fd, SIOCSIFASYNCMAP, (caddr_t) &x) < 0) { \ + syslog(LOG_ERR, "ioctl(SIOCSIFASYNCMAP): %m"); \ + } } +#else +#define SIFASYNCMAP(u, a) { \ + u_long x = a; \ + if (ioctl(fd, PPPIOCSASYNCMAP, (caddr_t) &x) < 0) { \ + syslog(LOG_ERR, "ioctl(PPPIOCSASYNCMAP): %m"); \ + quit(); \ + } } +#endif + +/* + * SIFPCOMPRESSION - Config the interface for protocol compression. + */ +#ifdef STREAMS +#define SIFPCOMPRESSION(u) { \ + char c = 1; \ + if(ioctl(fd, SIOCSIFCOMPPROT, &c) < 0) { \ + syslog(LOG_ERR, "ioctl(SIOCSIFCOMPPROT): %m"); \ + }} +#else +#define SIFPCOMPRESSION(u) { \ + u_int x; \ + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { \ + syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m"); \ + quit(); \ + } \ + x |= SC_COMP_PROT; \ + if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) { \ + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m"); \ + quit(); \ + } } +#endif + +/* + * CIFPCOMPRESSION - Config the interface for no protocol compression. + */ +#ifdef STREAMS +#define CIFPCOMPRESSION(u) { \ + char c = 0; \ + if(ioctl(fd, SIOCSIFCOMPPROT, &c) < 0) { \ + syslog(LOG_ERR, "ioctl(SIOCSIFCOMPPROT): %m"); \ + quit(); \ + }} +#else +#define CIFPCOMPRESSION(u) { \ + u_int x; \ + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { \ + syslog(LOG_ERR, "ioctl(PPPIOCGFLAGS): %m"); \ + quit(); \ + } \ + x &= ~SC_COMP_PROT; \ + if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) { \ + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m"); \ + quit(); \ + } } +#endif + +/* + * SIFACCOMPRESSION - Config the interface for address/control compression. + */ +#ifdef STREAMS +#define SIFACCOMPRESSION(u) { \ + char c = 1; \ + if(ioctl(fd, SIOCSIFCOMPAC, &c) < 0) { \ + syslog(LOG_ERR, "ioctl(SIOCSIFCOMPAC): %m"); \ + quit(); \ + }} +#else +#define SIFACCOMPRESSION(u) { \ + u_int x; \ + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { \ + syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m"); \ + quit(); \ + } \ + x |= SC_COMP_AC; \ + if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) { \ + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m"); \ + quit(); \ + } } +#endif + +/* + * CIFACCOMPRESSION - Config the interface for no address/control compression. + */ +#ifdef STREAMS +#define CIFACCOMPRESSION(u) { \ + char c = 0; \ + if(ioctl(fd, SIOCSIFCOMPAC, &c) < 0) { \ + syslog(LOG_ERR, "ioctl(SIOCSIFCOMPAC): %m"); \ + quit(); \ + }} +#else +#define CIFACCOMPRESSION(u) { \ + u_int x; \ + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { \ + syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m"); \ + quit(); \ + } \ + x &= ~SC_COMP_AC; \ + if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) { \ + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m"); \ + quit(); \ + } } +#endif + +/* + * SIFVJCOMP - config tcp header compression + */ +#ifdef STREAMS +#define SIFVJCOMP(u, a) { \ + char x = a; \ + if (debug) syslog(LOG_DEBUG, "SIFVJCOMP unit %d to value %d\n",u,x); \ + if(ioctl(fd, SIOCSIFVJCOMP, (caddr_t) &x) < 0) { \ + syslog(LOG_ERR, "ioctl(SIOCSIFVJCOMP): %m"); \ + quit(); \ + } \ +} +#else +#define SIFVJCOMP(u, a) { \ + u_int x; \ + if (debug) \ + syslog(LOG_DEBUG, "PPPIOCSFLAGS unit %d set %s\n",u,a?"on":"off"); \ + if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { \ + syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m"); \ + quit(); \ + } \ + x = (x & ~SC_COMP_TCP) | ((a) ? SC_COMP_TCP : 0); \ + if(ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) { \ + syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m"); \ + quit(); \ + } } +#endif + +/* + * SIFUP - Config the interface up. + */ +#define SIFUP(u) { \ + struct ifreq ifr; \ + strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); \ + if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { \ + syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m"); \ + quit(); \ + } \ + ifr.ifr_flags |= IFF_UP; \ + if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { \ + syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m"); \ + quit(); \ + } } + +/* + * SIFDOWN - Config the interface down. + */ +#define SIFDOWN(u) { \ + struct ifreq ifr; \ + strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); \ + if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { \ + syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m"); \ + quit(); \ + } \ + ifr.ifr_flags &= ~IFF_UP; \ + if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { \ + syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m"); \ + quit(); \ + } } + +/* + * SIFMTU - Config the interface MTU. + */ +#define SIFMTU(u, m) { \ + struct ifreq ifr; \ + strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); \ + ifr.ifr_mtu = m; \ + if (ioctl(s, SIOCSIFMTU, (caddr_t) &ifr) < 0) { \ + syslog(LOG_ERR, "ioctl(SIOCSIFMTU): %m"); \ + quit(); \ + } } + + +#ifdef __386BSD__ /* BSD >= 44 ? */ +#define SET_SA_FAMILY(addr, family) \ + bzero((char *) &(addr), sizeof(addr)); \ + addr.sa_family = (family); \ + addr.sa_len = sizeof(addr); +#else +#define SET_SA_FAMILY(addr, family) \ + bzero((char *) &(addr), sizeof(addr)); \ + addr.sa_family = (family); +#endif + +/* + * SIFADDR - Config the interface IP addresses. + */ +#define SIFADDR(u, o, h) { \ + struct ifreq ifr; \ + strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); \ + SET_SA_FAMILY(ifr.ifr_addr, AF_INET); \ + ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = o; \ + if (ioctl(s, SIOCSIFADDR, (caddr_t) &ifr) < 0) { \ + syslog(LOG_ERR, "ioctl(SIOCSIFADDR): %m"); \ + } \ + ((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_addr.s_addr = h; \ + if (ioctl(s, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) { \ + syslog(LOG_ERR, "ioctl(SIOCSIFDSTADDR): %m"); \ + quit(); \ + } } + +/* + * CIFADDR - Clear the interface IP addresses. + */ +#if BSD > 43 +#define CIFADDR(u, o, h) { \ + struct ortentry rt; \ + SET_SA_FAMILY(rt.rt_dst, AF_INET); \ + ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr = h; \ + SET_SA_FAMILY(rt.rt_gateway, AF_INET); \ + ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = o; \ + rt.rt_flags |= RTF_HOST; \ + syslog(LOG_INFO, "Deleting host route from %s to %s\n", \ + ip_ntoa(h), ip_ntoa(o)); \ + if (ioctl(s, SIOCDELRT, (caddr_t) &rt) < 0) { \ + syslog(LOG_ERR, "ioctl(SIOCDELRT): %m"); \ + } } +#else +#define CIFADDR(u, o, h) { \ + struct rtentry rt; \ + SET_SA_FAMILY(rt.rt_dst, AF_INET); \ + ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr = h; \ + SET_SA_FAMILY(rt.rt_gateway, AF_INET); \ + ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = o; \ + rt.rt_flags |= RTF_HOST; \ + syslog(LOG_INFO, "Deleting host route from %s to %s\n", \ + ip_ntoa(h), ip_ntoa(o)); \ + if (ioctl(s, SIOCDELRT, (caddr_t) &rt) < 0) { \ + syslog(LOG_ERR, "ioctl(SIOCDELRT): %m"); \ + } } +#endif + +/* + * SIFMASK - Config the interface net mask + */ +#define SIFMASK(u, m) { \ + struct ifreq ifr; \ + strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); \ + SET_SA_FAMILY(ifr.ifr_addr, AF_INET); \ + ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = m; \ + syslog(LOG_INFO, "Setting interface mask to %s\n", ip_ntoa(m)); \ + if (ioctl(s, SIOCSIFNETMASK, (caddr_t) &ifr) < 0) { \ + syslog(LOG_ERR, "ioctl(SIOCSIFADDR): %m"); \ + } } + +#ifndef LOG_PPP /* we use LOG_LOCAL2 for syslog by default */ +#if defined(DEBUGMAIN) || defined(DEBUGFSM) || defined(DEBUG) \ + || defined(DEBUGLCP) || defined(DEBUGIPCP) || defined(DEBUGUPAP) \ + || defined(DEBUGCHAP) +#define LOG_PPP LOG_LOCAL2 +#else +#define LOG_PPP LOG_DAEMON +#endif +#endif /* LOG_PPP */ + +#ifdef DEBUGMAIN +#define MAINDEBUG(x) if (debug) syslog x; +#else +#define MAINDEBUG(x) +#endif + +#ifdef DEBUGFSM +#define FSMDEBUG(x) if (debug) syslog x; +#else +#define FSMDEBUG(x) +#endif + +#ifdef DEBUGLCP +#define LCPDEBUG(x) if (debug) syslog x; +#else +#define LCPDEBUG(x) +#endif + +#ifdef DEBUGIPCP +#define IPCPDEBUG(x) if (debug) syslog x; +#else +#define IPCPDEBUG(x) +#endif + +#ifdef DEBUGUPAP +#define UPAPDEBUG(x) if (debug) syslog x; +#else +#define UPAPDEBUG(x) +#endif + +#ifdef DEBUGCHAP +#define CHAPDEBUG(x) if (debug) syslog x; +#else +#define CHAPDEBUG(x) +#endif + +#ifndef SIGTYPE +#if defined(sun) || defined(SYSV) || defined(POSIX_SOURCE) +#define SIGTYPE void +#else +#define SIGTYPE int +#endif /* defined(sun) || defined(SYSV) || defined(POSIX_SOURCE) */ +#endif /* SIGTYPE */ +#endif /* __PPP_H__ */ diff --git a/libexec/pppd/upap.c b/libexec/pppd/upap.c new file mode 100644 index 0000000000..a58a018388 --- /dev/null +++ b/libexec/pppd/upap.c @@ -0,0 +1,484 @@ +/* + * upap.c - User/Password Authentication Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * TODO: + */ + +#include +#include +#include +#include + +#ifdef STREAMS +#include +#include +#include +#endif + +#include +#include "pppd.h" +#include "fsm.h" +#include "lcp.h" +#include "upap.h" +#include "chap.h" +#include "ipcp.h" + + +upap_state upap[NPPP]; /* UPAP state; one for each unit */ + + +static void upap_timeout __ARGS((caddr_t)); +static void upap_rauth __ARGS((upap_state *, u_char *, int, int)); +static void upap_rauthack __ARGS((upap_state *, u_char *, int, int)); +static void upap_rauthnak __ARGS((upap_state *, u_char *, int, int)); +static void upap_sauth __ARGS((upap_state *)); +static void upap_sresp __ARGS((upap_state *, int, int, u_char *, int)); + + +/* + * upap_init - Initialize a UPAP unit. + */ +void + upap_init(unit) +int unit; +{ + upap_state *u = &upap[unit]; + + u->us_unit = unit; + u->us_user = NULL; + u->us_userlen = 0; + u->us_passwd = NULL; + u->us_passwdlen = 0; + u->us_clientstate = UPAPCS_CLOSED; + u->us_serverstate = UPAPSS_CLOSED; + u->us_flags = 0; + u->us_id = 0; + u->us_timeouttime = UPAP_DEFTIMEOUT; +} + + +/* + * upap_authwithpeer - Authenticate us with our peer (start client). + * + * Set new state and send authenticate's. + */ +void + upap_authwithpeer(unit) +int unit; +{ + upap_state *u = &upap[unit]; + + u->us_flags &= ~UPAPF_AWPPENDING; /* Clear pending flag */ + + /* Protect against programming errors that compromise security */ + if (u->us_serverstate != UPAPSS_CLOSED || + u->us_flags & UPAPF_APPENDING) { + UPAPDEBUG((LOG_WARNING, + "upap_authwithpeer: upap_authpeer already called!")) + return; + } + + /* Already authenticat{ed,ing}? */ + if (u->us_clientstate == UPAPCS_AUTHSENT || + u->us_clientstate == UPAPCS_OPEN) + return; + + /* Lower layer up? */ + if (!(u->us_flags & UPAPF_LOWERUP)) { + u->us_flags |= UPAPF_AWPPENDING; /* Wait */ + return; + } + + /* User/passwd values valid? */ + if (!(u->us_flags & UPAPF_UPVALID)) { + GETUSERPASSWD(unit); /* Start getting user and passwd */ + if (!(u->us_flags & UPAPF_UPVALID)) { + u->us_flags |= UPAPF_UPPENDING; /* Wait */ + return; + } + } + + upap_sauth(u); /* Start protocol */ +/* TIMEOUT(upap_timeout, (caddr_t) u, u->us_timeouttime);*/ + u->us_clientstate = UPAPCS_AUTHSENT; + u->us_retransmits = 0; +} + + +/* + * upap_authpeer - Authenticate our peer (start server). + * + * Set new state. + */ +void + upap_authpeer(unit) +int unit; +{ + upap_state *u = &upap[unit]; + + u->us_flags &= ~UPAPF_APPENDING; /* Clear pending flag */ + + /* Already authenticat{ed,ing}? */ + if (u->us_serverstate == UPAPSS_LISTEN || + u->us_serverstate == UPAPSS_OPEN) + return; + + /* Lower layer up? */ + if (!(u->us_flags & UPAPF_LOWERUP)) { + u->us_flags |= UPAPF_APPENDING; /* Wait for desired event */ + return; + } + u->us_serverstate = UPAPSS_LISTEN; +} + + +/* + * upap_timeout - Timeout expired. + */ +static void + upap_timeout(arg) +caddr_t arg; +{ + upap_state *u = (upap_state *) arg; + + if (u->us_clientstate != UPAPCS_AUTHSENT) + return; + + /* XXX Print warning after many retransmits? */ + + upap_sauth(u); /* Send Configure-Request */ + TIMEOUT(upap_timeout, (caddr_t) u, u->us_timeouttime); + ++u->us_retransmits; +} + + +/* + * upap_lowerup - The lower layer is up. + * + * Start authenticating if pending. + */ +void + upap_lowerup(unit) +int unit; +{ + upap_state *u = &upap[unit]; + + u->us_flags |= UPAPF_LOWERUP; + if (u->us_flags & UPAPF_AWPPENDING) /* Attempting authwithpeer? */ + upap_authwithpeer(unit); /* Try it now */ + if (u->us_flags & UPAPF_APPENDING) /* Attempting authpeer? */ + upap_authpeer(unit); /* Try it now */ +} + + +/* + * upap_lowerdown - The lower layer is down. + * + * Cancel all timeouts. + */ +void + upap_lowerdown(unit) +int unit; +{ + upap_state *u = &upap[unit]; + + u->us_flags &= ~UPAPF_LOWERUP; /* XXX UPAP_UPVALID? */ + + if (u->us_clientstate == UPAPCS_AUTHSENT) /* Timeout pending? */ + UNTIMEOUT(upap_timeout, (caddr_t) u); /* Cancel timeout */ + + if (u->us_serverstate == UPAPSS_OPEN) /* User logged in? */ + LOGOUT(unit); + u->us_clientstate = UPAPCS_CLOSED; + u->us_serverstate = UPAPSS_CLOSED; +} + + +/* + * upap_protrej - Peer doesn't speak this protocol. + * + * This shouldn't happen. In any case, pretend lower layer went down. + */ +void + upap_protrej(unit) +int unit; +{ + upap_lowerdown(unit); +} + + +/* + * upap_input - Input UPAP packet. + */ +void + upap_input(unit, inpacket, l) +int unit; +u_char *inpacket; +int l; +{ + upap_state *u = &upap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < UPAP_HEADERLEN) { + UPAPDEBUG((LOG_INFO, "upap_input: rcvd short header.")) + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < UPAP_HEADERLEN) { + UPAPDEBUG((LOG_INFO, "upap_input: rcvd illegal length.")) + return; + } + if (len > l) { + UPAPDEBUG((LOG_INFO, "upap_input: rcvd short packet.")) + return; + } + len -= UPAP_HEADERLEN; + + /* + * Action depends on code. + */ + switch (code) { + case UPAP_AUTH: + upap_rauth(u, inp, id, len); + break; + + case UPAP_AUTHACK: + upap_rauthack(u, inp, id, len); + break; + + case UPAP_AUTHNAK: + upap_rauthnak(u, inp, id, len); + break; + + default: /* XXX Need code reject */ + break; + } +} + + +/* + * upap_rauth - Receive Authenticate. + */ +static void + upap_rauth(u, inp, id, len) +upap_state *u; +u_char *inp; +u_char id; +int len; +{ + u_char ruserlen, rpasswdlen; + u_char *ruser, *rpasswd; + u_char retcode; + u_char *msg; + int msglen; + + UPAPDEBUG((LOG_INFO, "upap_rauth: Rcvd id %d.", id)) + if (u->us_serverstate != UPAPSS_LISTEN) /* XXX Reset connection? */ + return; + + /* + * Parse user/passwd. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "upap_rauth: rcvd short packet.")) + return; + } + GETCHAR(ruserlen, inp); + len -= sizeof (u_char) + ruserlen + sizeof (u_char);; + if (len < 0) { + UPAPDEBUG((LOG_INFO, "upap_rauth: rcvd short packet.")) + return; + } + ruser = inp; + INCPTR(ruserlen, inp); + GETCHAR(rpasswdlen, inp); + if (len < rpasswdlen) { + UPAPDEBUG((LOG_INFO, "upap_rauth: rcvd short packet.")) + return; + } + rpasswd = inp; + + retcode = LOGIN(u->us_unit, (char *) ruser, (int) ruserlen, (char *) rpasswd, + (int) rpasswdlen, (char **) &msg, &msglen); + + upap_sresp(u, retcode, id, msg, msglen); + + /* only crank up IPCP when either we aren't doing CHAP, or if we are, */ + /* that it is in open state */ + + if (retcode == UPAP_AUTHACK) { + u->us_serverstate = UPAPSS_OPEN; + if (!lcp_hisoptions[u->us_unit].neg_chap || + (lcp_hisoptions[u->us_unit].neg_chap && + chap[u->us_unit].serverstate == CHAPSS_OPEN)) + ipcp_activeopen(u->us_unit); /* Start IPCP */ + } +} + + +/* + * upap_rauthack - Receive Authenticate-Ack. + */ +static void + upap_rauthack(u, inp, id, len) +upap_state *u; +u_char *inp; +u_char id; +int len; +{ + u_char msglen; + u_char *msg; + + UPAPDEBUG((LOG_INFO, "upap_rauthack: Rcvd id %d.", id)) + if (u->us_clientstate != UPAPCS_AUTHSENT) /* XXX */ + return; + + /* + * Parse message. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "upap_rauthack: rcvd short packet.")) + return; + } + GETCHAR(msglen, inp); + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG((LOG_INFO, "upap_rauthack: rcvd short packet.")) + return; + } + msg = inp; + PRINTMSG(msg, msglen); + + u->us_clientstate = UPAPCS_OPEN; + + /* only crank up IPCP when either we aren't doing CHAP, or if we are, */ + /* that it is in open state */ + + if (!lcp_gotoptions[u->us_unit].neg_chap || + (lcp_gotoptions[u->us_unit].neg_chap && + chap[u->us_unit].clientstate == CHAPCS_OPEN)) + ipcp_activeopen(u->us_unit); /* Start IPCP */ +} + + +/* + * upap_rauthnak - Receive Authenticate-Nakk. + */ +static void + upap_rauthnak(u, inp, id, len) +upap_state *u; +u_char *inp; +u_char id; +int len; +{ + u_char msglen; + u_char *msg; + + UPAPDEBUG((LOG_INFO, "upap_rauthnak: Rcvd id %d.", id)) + if (u->us_clientstate != UPAPCS_AUTHSENT) /* XXX */ + return; + + /* + * Parse message. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "upap_rauthnak: rcvd short packet.")) + return; + } + GETCHAR(msglen, inp); + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG((LOG_INFO, "upap_rauthnak: rcvd short packet.")) + return; + } + msg = inp; + PRINTMSG(msg, msglen); + + u->us_flags &= ~UPAPF_UPVALID; /* Clear valid flag */ + u->us_clientstate = UPAPCS_CLOSED; /* Pretend for a moment */ + upap_authwithpeer(u->us_unit); /* Restart */ +} + + +/* + * upap_sauth - Send an Authenticate. + */ +static void + upap_sauth(u) +upap_state *u; +{ + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) + + u->us_userlen + u->us_passwdlen; + outp = outpacket_buf; + + MAKEHEADER(outp, UPAP); + + PUTCHAR(UPAP_AUTH, outp); + PUTCHAR(++u->us_id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(u->us_userlen, outp); + BCOPY(u->us_user, outp, u->us_userlen); + INCPTR(u->us_userlen, outp); + PUTCHAR(u->us_passwdlen, outp); + BCOPY(u->us_passwd, outp, u->us_passwdlen); + output(u->us_unit, outpacket_buf, outlen + DLLHEADERLEN); + + UPAPDEBUG((LOG_INFO, "upap_sauth: Sent id %d.", u->us_id)) +} + + +/* + * upap_sresp - Send a response (ack or nak). + */ +static void + upap_sresp(u, code, id, msg, msglen) +upap_state *u; +u_char code, id; +u_char *msg; +int msglen; +{ + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen; + outp = outpacket_buf; + MAKEHEADER(outp, UPAP); + + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(msglen, outp); + BCOPY(msg, outp, msglen); + output(u->us_unit, outpacket_buf, outlen + DLLHEADERLEN); + + UPAPDEBUG((LOG_INFO, "upap_sresp: Sent code %d, id %d.", code, id)) +} diff --git a/libexec/pppd/upap.h b/libexec/pppd/upap.h new file mode 100644 index 0000000000..f25b9487ec --- /dev/null +++ b/libexec/pppd/upap.h @@ -0,0 +1,90 @@ +/* + * upap.h - User/Password Authentication Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * Packet header = Code, id, length. + */ +#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * UPAP codes. + */ +#define UPAP_AUTH 1 /* Authenticate */ +#define UPAP_AUTHACK 2 /* Authenticate Ack */ +#define UPAP_AUTHNAK 3 /* Authenticate Nak */ + + +/* + * Each interface is described by upap structure. + */ +typedef struct upap_state { + int us_unit; /* Interface unit number */ + char *us_user; /* User */ + int us_userlen; /* User length */ + char *us_passwd; /* Password */ + int us_passwdlen; /* Password length */ + int us_clientstate; /* Client state */ + int us_serverstate; /* Server state */ + int us_flags; /* Flags */ + u_char us_id; /* Current id */ + int us_timeouttime; /* Timeout time in milliseconds */ + int us_retransmits; /* Number of retransmissions */ +} upap_state; + + +/* + * Client states. + */ +#define UPAPCS_CLOSED 1 /* Connection down */ +#define UPAPCS_AUTHSENT 2 /* We've sent an Authenticate */ +#define UPAPCS_OPEN 3 /* We've received an Ack */ + +/* + * Server states. + */ +#define UPAPSS_CLOSED 1 /* Connection down */ +#define UPAPSS_LISTEN 2 /* Listening for an Authenticate */ +#define UPAPSS_OPEN 3 /* We've sent an Ack */ + +/* + * Flags. + */ +#define UPAPF_LOWERUP 1 /* The lower level is UP */ +#define UPAPF_AWPPENDING 2 /* Auth with peer pending */ +#define UPAPF_APPENDING 4 /* Auth peer pending */ +#define UPAPF_UPVALID 8 /* User/passwd values valid */ +#define UPAPF_UPPENDING 0x10 /* User/passwd values pending */ + + +/* + * Timeouts. + */ +#define UPAP_DEFTIMEOUT 3 /* Timeout time in seconds */ + + +extern upap_state upap[]; + +void upap_init __ARGS((int)); +void upap_authwithpeer __ARGS((int)); +void upap_authpeer __ARGS((int)); +void upap_lowerup __ARGS((int)); +void upap_lowerdown __ARGS((int)); +void upap_input __ARGS((int, u_char *, int)); +void upap_protrej __ARGS((int)); diff --git a/libexec/rexecd/Makefile b/libexec/rexecd/Makefile index 09b7316150..093e9a6444 100644 --- a/libexec/rexecd/Makefile +++ b/libexec/rexecd/Makefile @@ -1,6 +1,11 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= rexecd -MAN8= rexecd.0 +MAN8= rexecd.8 + +.if exists(/usr/lib/libcrypt.a) +DPADD+= ${LIBCRYPT} +LDADD+= -lcrypt +.endif .include diff --git a/libexec/rlogind/Makefile b/libexec/rlogind/Makefile index e7224145ae..90254a1064 100644 --- a/libexec/rlogind/Makefile +++ b/libexec/rlogind/Makefile @@ -2,9 +2,15 @@ PROG= rlogind SRCS= rlogind.c -MAN8= rlogind.0 +MAN8= rlogind.8 DPADD= ${LIBUTIL} LDADD= -lutil .PATH: ${.CURDIR}/../../usr.bin/rlogin +.if exists(/usr/lib/libcrypt.a) +#CFLAGS+=-DCRYPT -DKERBEROS +DPADD+= ${LIBCRYPT} #${LIBKRB} +LDADD+= -lcrypt #-lkrb +.endif + .include diff --git a/libexec/rpc.rstatd/Makefile b/libexec/rpc.rstatd/Makefile new file mode 100644 index 0000000000..5134a55bb5 --- /dev/null +++ b/libexec/rpc.rstatd/Makefile @@ -0,0 +1,10 @@ +# $Id: Makefile,v 1.4 1993/08/02 17:50:37 mycroft Exp $ + +PROG = rpc.rstatd +SRCS = rstatd.c rstat_proc.c +MAN8 = rpc.rstatd.8 + +DPADD= ${LIBRPCSVC} ${LIBRPC} ${LIBUTIL} +LDADD= -lrpcsvc -lrpc -lutil + +.include diff --git a/libexec/rpc.rstatd/rpc.rstatd.8 b/libexec/rpc.rstatd/rpc.rstatd.8 new file mode 100644 index 0000000000..d0636ef42e --- /dev/null +++ b/libexec/rpc.rstatd/rpc.rstatd.8 @@ -0,0 +1,61 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 1985, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: rpc.rstatd.8,v 1.5 1993/08/16 16:04:53 mycroft Exp $ +.\" +.Dd June 7, 1993 +.Dt RPC.RSTATD 8 +.Os BSD 4.3 +.Sh NAME +.Nm rpc.rstatd +.Nd kernel statistics server +.Sh SYNOPSIS +.Nm /usr/libexec/rpc.rstatd +.Sh DESCRIPTION +.Nm rpc.rstatd +is a server which returns performance statistics obtained from the kernel. +These statistics are read using the +.Xr rup 1 +command. +The +.Nm rpc.rstatd +daemon is normally invoked by +.Xr inetd 8 . +.Pp +.Nm rpc.rstatd +uses an RPC protocol defined in +.Pa /usr/include/rpcsvc/rstat.x . +.Sh SEE ALSO +.Xr rup 1 , +.Xr inetd 8 + diff --git a/libexec/rpc.rstatd/rstat_proc.c b/libexec/rpc.rstatd/rstat_proc.c new file mode 100644 index 0000000000..5eda69f643 --- /dev/null +++ b/libexec/rpc.rstatd/rstat_proc.c @@ -0,0 +1,422 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#ifndef lint +/*static char sccsid[] = "from: @(#)rpc.rstatd.c 1.1 86/09/25 Copyr 1984 Sun Micro";*/ +/*static char sccsid[] = "from: @(#)rstat_proc.c 2.2 88/08/01 4.0 RPCSRC";*/ +static char rcsid[] = "$Id: rstat_proc.c,v 1.4 1993/09/23 18:42:39 jtc Exp $"; +#endif + +/* + * rstat service: built with rstat.x and derived from rpc.rstatd.c + * + * Copyright (c) 1984 by Sun Microsystems, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef BSD +#include +#include +#else +#include +#endif +#include + +#undef FSHIFT /* Use protocol's shift and scale values */ +#undef FSCALE +#include + +struct nlist nl[] = { +#define X_CPTIME 0 + { "_cp_time" }, +#define X_SUM 1 + { "_sum" }, +#define X_IFNET 2 + { "_ifnet" }, +#define X_DKXFER 3 + { "_dk_xfer" }, +#define X_BOOTTIME 4 + { "_boottime" }, +#define X_HZ 5 + { "_hz" }, +#ifdef vax +#define X_AVENRUN 6 + { "_avenrun" }, +#endif + "", +}; +int firstifnet, numintfs; /* chain of ethernet interfaces */ +int stats_service(); + +extern int from_inetd; +int sincelastreq = 0; /* number of alarms since last request */ +extern int closedown; + +union { + struct stats s1; + struct statsswtch s2; + struct statstime s3; +} stats_all; + +void updatestat(); +static stat_is_init = 0; +extern int errno; + +#ifndef FSCALE +#define FSCALE (1 << 8) +#endif + +#ifndef BSD +/* + * BSD has the kvm facility for getting info from the + * kernel. If you aren't on BSD, this surfices. + */ +int kmem; + +kvm_read(off, addr, size) + unsigned long off, size; + char *addr; +{ + int len; + if (lseek(kmem, (long)off, 0) == -1) + return(-1); + return(read(kmem, addr, size)); +} + +kvm_nlist(nl) + struct nlist *nl; +{ + int n = nlist("/vmunix", nl); + if (nl[0].n_value == 0) + return(n); + + if ((kmem = open("/dev/kmem", 0)) < 0) + return(-1); + return(0); +} +#endif + +stat_init() +{ + stat_is_init = 1; + setup(); + updatestat(); + (void) signal(SIGALRM, updatestat); + alarm(1); +} + +statstime * +rstatproc_stats_3() +{ + if (! stat_is_init) + stat_init(); + sincelastreq = 0; + return(&stats_all.s3); +} + +statsswtch * +rstatproc_stats_2() +{ + if (! stat_is_init) + stat_init(); + sincelastreq = 0; + return(&stats_all.s2); +} + +stats * +rstatproc_stats_1() +{ + if (! stat_is_init) + stat_init(); + sincelastreq = 0; + return(&stats_all.s1); +} + +u_int * +rstatproc_havedisk_3() +{ + static u_int have; + + if (! stat_is_init) + stat_init(); + sincelastreq = 0; + have = havedisk(); + return(&have); +} + +u_int * +rstatproc_havedisk_2() +{ + return(rstatproc_havedisk_3()); +} + +u_int * +rstatproc_havedisk_1() +{ + return(rstatproc_havedisk_3()); +} + +void +updatestat() +{ + int off, i, hz; + struct vmmeter sum; + struct ifnet ifnet; + double avrun[3]; + struct timeval tm, btm; + +#ifdef DEBUG + fprintf(stderr, "entering updatestat\n"); +#endif + if (sincelastreq >= closedown) { +#ifdef DEBUG + fprintf(stderr, "about to closedown\n"); +#endif + if (from_inetd) + exit(0); + else { + stat_is_init = 0; + return; + } + } + sincelastreq++; + + if (kvm_read((long)nl[X_HZ].n_value, (char *)&hz, sizeof hz) != sizeof hz) { + syslog(LOG_ERR, "rstat: can't read hz from kmem\n"); + exit(1); + } + if (kvm_read((long)nl[X_CPTIME].n_value, (char *)stats_all.s1.cp_time, sizeof (stats_all.s1.cp_time)) + != sizeof (stats_all.s1.cp_time)) { + syslog(LOG_ERR, "rstat: can't read cp_time from kmem\n"); + exit(1); + } +#ifdef vax + if (kvm_read((long)nl[X_AVENRUN].n_value, (char *)avrun, sizeof (avrun)) != sizeof (avrun)) { + syslog(LOG_ERR, "rstat: can't read avenrun from kmem\n"); + exit(1); + } +#endif +#ifdef BSD + (void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0])); +#endif + stats_all.s2.avenrun[0] = avrun[0] * FSCALE; + stats_all.s2.avenrun[1] = avrun[1] * FSCALE; + stats_all.s2.avenrun[2] = avrun[2] * FSCALE; + if (kvm_read((long)nl[X_BOOTTIME].n_value, (char *)&btm, sizeof (stats_all.s2.boottime)) + != sizeof (stats_all.s2.boottime)) { + syslog(LOG_ERR, "rstat: can't read boottime from kmem\n"); + exit(1); + } + stats_all.s2.boottime.tv_sec = btm.tv_sec; + stats_all.s2.boottime.tv_usec = btm.tv_usec; + + +#ifdef DEBUG + fprintf(stderr, "%d %d %d %d\n", stats_all.s1.cp_time[0], + stats_all.s1.cp_time[1], stats_all.s1.cp_time[2], stats_all.s1.cp_time[3]); +#endif + + if (kvm_read((long)nl[X_SUM].n_value, (char *)&sum, sizeof sum) != sizeof sum) { + syslog(LOG_ERR, "rstat: can't read sum from kmem\n"); + exit(1); + } + stats_all.s1.v_pgpgin = sum.v_pgpgin; + stats_all.s1.v_pgpgout = sum.v_pgpgout; + stats_all.s1.v_pswpin = sum.v_pswpin; + stats_all.s1.v_pswpout = sum.v_pswpout; + stats_all.s1.v_intr = sum.v_intr; + gettimeofday(&tm, (struct timezone *) 0); + stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) + + hz*(tm.tv_usec - btm.tv_usec)/1000000; + stats_all.s2.v_swtch = sum.v_swtch; + + if (kvm_read((long)nl[X_DKXFER].n_value, (char *)stats_all.s1.dk_xfer, sizeof (stats_all.s1.dk_xfer)) + != sizeof (stats_all.s1.dk_xfer)) { + syslog(LOG_ERR, "rstat: can't read dk_xfer from kmem\n"); + exit(1); + } + + stats_all.s1.if_ipackets = 0; + stats_all.s1.if_opackets = 0; + stats_all.s1.if_ierrors = 0; + stats_all.s1.if_oerrors = 0; + stats_all.s1.if_collisions = 0; + for (off = firstifnet, i = 0; off && i < numintfs; i++) { + if (kvm_read(off, (char *)&ifnet, sizeof ifnet) != sizeof ifnet) { + syslog(LOG_ERR, "rstat: can't read ifnet from kmem\n"); + exit(1); + } + stats_all.s1.if_ipackets += ifnet.if_ipackets; + stats_all.s1.if_opackets += ifnet.if_opackets; + stats_all.s1.if_ierrors += ifnet.if_ierrors; + stats_all.s1.if_oerrors += ifnet.if_oerrors; + stats_all.s1.if_collisions += ifnet.if_collisions; + off = (int) ifnet.if_next; + } + gettimeofday((struct timeval *)&stats_all.s3.curtime, + (struct timezone *) 0); + alarm(1); +} + +setup() +{ + struct ifnet ifnet; + int off; + + if (kvm_nlist(nl) != 0) { + syslog(LOG_ERR, "rstatd: Can't get namelist."); + exit (1); + } + + if (kvm_read((long)nl[X_IFNET].n_value, &firstifnet, + sizeof(int)) != sizeof(int)) { + syslog(LOG_ERR, "rstat: can't read firstifnet from kmem\n"); + exit(1); + } + + numintfs = 0; + for (off = firstifnet; off;) { + if (kvm_read(off, (char *)&ifnet, sizeof ifnet) != sizeof ifnet) { + syslog(LOG_ERR, "rstat: can't read ifnet from kmem\n"); + exit(1); + } + numintfs++; + off = (int) ifnet.if_next; + } +} + +/* + * returns true if have a disk + */ +havedisk() +{ + int i, cnt; + long xfer[DK_NDRIVE]; + + if (kvm_nlist(nl) != 0) { + syslog(LOG_ERR, "rstatd: Can't get namelist."); + exit (1); + } + + if (kvm_read((long)nl[X_DKXFER].n_value, (char *)xfer, sizeof xfer)!= sizeof xfer) { + syslog(LOG_ERR, "rstat: can't read kmem\n"); + exit(1); + } + cnt = 0; + for (i=0; i < DK_NDRIVE; i++) + cnt += xfer[i]; + return (cnt != 0); +} + +void +rstat_service(rqstp, transp) + struct svc_req *rqstp; + SVCXPRT *transp; +{ + union { + int fill; + } argument; + char *result; + bool_t (*xdr_argument)(), (*xdr_result)(); + char *(*local)(); + + switch (rqstp->rq_proc) { + case NULLPROC: + (void)svc_sendreply(transp, xdr_void, (char *)NULL); + goto leave; + + case RSTATPROC_STATS: + xdr_argument = xdr_void; + xdr_result = xdr_statstime; + switch (rqstp->rq_vers) { + case RSTATVERS_ORIG: + local = (char *(*)()) rstatproc_stats_1; + break; + case RSTATVERS_SWTCH: + local = (char *(*)()) rstatproc_stats_2; + break; + case RSTATVERS_TIME: + local = (char *(*)()) rstatproc_stats_3; + break; + default: + svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); + goto leave; + /*NOTREACHED*/ + } + break; + + case RSTATPROC_HAVEDISK: + xdr_argument = xdr_void; + xdr_result = xdr_u_int; + switch (rqstp->rq_vers) { + case RSTATVERS_ORIG: + local = (char *(*)()) rstatproc_havedisk_1; + break; + case RSTATVERS_SWTCH: + local = (char *(*)()) rstatproc_havedisk_2; + break; + case RSTATVERS_TIME: + local = (char *(*)()) rstatproc_havedisk_3; + break; + default: + svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); + goto leave; + /*NOTREACHED*/ + } + break; + + default: + svcerr_noproc(transp); + goto leave; + } + bzero((char *)&argument, sizeof(argument)); + if (!svc_getargs(transp, xdr_argument, &argument)) { + svcerr_decode(transp); + goto leave; + } + result = (*local)(&argument, rqstp); + if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, &argument)) { + (void)fprintf(stderr, "unable to free arguments\n"); + exit(1); + } +leave: + if (from_inetd) + exit(0); +} diff --git a/libexec/rpc.rstatd/rstatd.c b/libexec/rpc.rstatd/rstatd.c new file mode 100644 index 0000000000..fbdb183b8f --- /dev/null +++ b/libexec/rpc.rstatd/rstatd.c @@ -0,0 +1,117 @@ +/*- + * Copyright (c) 1993, John Brezak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$Id: rstatd.c,v 1.4 1993/08/02 17:50:40 mycroft Exp $"; +#endif /* not lint */ + +#include +#include +#include +#include +#include + +extern void rstat_service(); + +int from_inetd = 1; /* started from inetd ? */ +int closedown = 20; /* how long to wait before going dormant */ + +void +cleanup() +{ + (void) pmap_unset(RSTATPROG, RSTATVERS_TIME); + (void) pmap_unset(RSTATPROG, RSTATVERS_SWTCH); + (void) pmap_unset(RSTATPROG, RSTATVERS_ORIG); + exit(0); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + SVCXPRT *transp; + int sock = 0; + int proto = 0; + struct sockaddr_in from; + int fromlen; + + if (argc == 2) + closedown = atoi(argv[1]); + if (closedown <= 0) + closedown = 20; + + /* + * See if inetd started us + */ + if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { + from_inetd = 0; + sock = RPC_ANYSOCK; + proto = IPPROTO_UDP; + } + + if (!from_inetd) { + daemon(0, 0); + + (void)pmap_unset(RSTATPROG, RSTATVERS_TIME); + (void)pmap_unset(RSTATPROG, RSTATVERS_SWTCH); + (void)pmap_unset(RSTATPROG, RSTATVERS_ORIG); + + (void) signal(SIGINT, cleanup); + (void) signal(SIGTERM, cleanup); + (void) signal(SIGHUP, cleanup); + } + + openlog("rpc.rusersd", LOG_CONS|LOG_PID, LOG_DAEMON); + + transp = svcudp_create(sock); + if (transp == NULL) { + syslog(LOG_ERR, "cannot create udp service."); + exit(1); + } + if (!svc_register(transp, RSTATPROG, RSTATVERS_TIME, rstat_service, proto)) { + syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_TIME, udp)."); + exit(1); + } + if (!svc_register(transp, RSTATPROG, RSTATVERS_SWTCH, rstat_service, proto)) { + syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_SWTCH, udp)."); + exit(1); + } + if (!svc_register(transp, RSTATPROG, RSTATVERS_ORIG, rstat_service, proto)) { + syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_ORIG, udp)."); + exit(1); + } + + svc_run(); + syslog(LOG_ERR, "svc_run returned"); + exit(1); +} diff --git a/libexec/rpc.rusersd/Makefile b/libexec/rpc.rusersd/Makefile new file mode 100644 index 0000000000..8031be7f9f --- /dev/null +++ b/libexec/rpc.rusersd/Makefile @@ -0,0 +1,16 @@ +# $Id: Makefile,v 1.5 1993/08/02 17:50:44 mycroft Exp $ + +PROG = rpc.rusersd +SRCS = rusersd.c rusers_proc.c +MAN8 = rpc.rusersd.8 + +DPADD= ${LIBRPCSVC} ${LIBRPC} ${LIBUTIL} +LDADD= -lrpcsvc -lrpc -lutil + +.if exists(/usr/include/X11/extensions/xidle.h) +CFLAGS+= -DXIDLE +LDADD+= -L/usr/X386/lib -lXext -lX11 +.endif + + +.include diff --git a/libexec/rpc.rusersd/rpc.rusersd.8 b/libexec/rpc.rusersd/rpc.rusersd.8 new file mode 100644 index 0000000000..88adeee139 --- /dev/null +++ b/libexec/rpc.rusersd/rpc.rusersd.8 @@ -0,0 +1,64 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 1985, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: rpc.rusersd.8,v 1.3 1993/08/16 15:57:53 jtc Exp $ +.\" +.Dd June 7, 1993 +.Dt RPC.RUSERSD 8 +.Os BSD 4.3 +.Sh NAME +.Nm rpc.rusersd +.Nd logged in users server +.Sh SYNOPSIS +.Nm /usr/libexec/rpc.rusersd +.Sh DESCRIPTION +.Nm rpc.rusersd +is a server which returns information about users +currently logged in to the system. +.Pp +The currently logged in users are queried using the +.Xr rusers 1 +command. +The +.Nm rpc.rusersd +daemon is normally invoked by +.Xr inetd 8 . +.Pp +.Nm rpc.rusersd +uses an RPC protocol defined in +.Pa /usr/include/rpcsvc/rnusers.x . +.Sh SEE ALSO +.Xr rusers 1 , +.Xr who 1 , +.Xr w 1 , +.Xr inetd 8 diff --git a/libexec/rpc.rusersd/rusers_proc.c b/libexec/rpc.rusersd/rusers_proc.c new file mode 100644 index 0000000000..c64c78cd2f --- /dev/null +++ b/libexec/rpc.rusersd/rusers_proc.c @@ -0,0 +1,385 @@ +/*- + * Copyright (c) 1993, John Brezak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$Id: rusers_proc.c,v 1.5 1993/08/02 17:50:46 mycroft Exp $"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef XIDLE +#include +#include +#include +#endif +#define utmp rutmp +#include +#undef utmp + +#define IGNOREUSER "sleeper" + +#ifdef OSF +#define _PATH_UTMP UTMP_FILE +#endif + +#ifndef _PATH_UTMP +#define _PATH_UTMP "/etc/utmp" +#endif + +#ifndef _PATH_DEV +#define _PATH_DEV "/dev" +#endif + +#ifndef UT_LINESIZE +#define UT_LINESIZE sizeof(((struct utmp *)0)->ut_line) +#endif +#ifndef UT_NAMESIZE +#define UT_NAMESIZE sizeof(((struct utmp *)0)->ut_name) +#endif +#ifndef UT_HOSTSIZE +#define UT_HOSTSIZE sizeof(((struct utmp *)0)->ut_host) +#endif + +typedef char ut_line_t[UT_LINESIZE]; +typedef char ut_name_t[UT_NAMESIZE]; +typedef char ut_host_t[UT_HOSTSIZE]; + +utmpidle utmp_idle[MAXUSERS]; +rutmp old_utmp[MAXUSERS]; +ut_line_t line[MAXUSERS]; +ut_name_t name[MAXUSERS]; +ut_host_t host[MAXUSERS]; + +extern int from_inetd; + +FILE *ufp; + +#ifdef XIDLE +Display *dpy; + +static jmp_buf openAbort; + +static void +abortOpen () +{ + longjmp (openAbort, 1); +} + +XqueryIdle(char *display) +{ + int first_event, first_error; + Time IdleTime; + + (void) signal (SIGALRM, abortOpen); + (void) alarm ((unsigned) 10); + if (!setjmp (openAbort)) { + if (!(dpy= XOpenDisplay(display))) { + syslog(LOG_ERR, "Cannot open display %s", display); + return(-1); + } + if (XidleQueryExtension(dpy, &first_event, &first_error)) { + if (!XGetIdleTime(dpy, &IdleTime)) { + syslog(LOG_ERR, "%s: Unable to get idle time.", display); + return(-1); + } + } + else { + syslog(LOG_ERR, "%s: Xidle extension not loaded.", display); + return(-1); + } + XCloseDisplay(dpy); + } + else { + syslog(LOG_ERR, "%s: Server grabbed for over 10 seconds.", display); + return(-1); + } + (void) signal (SIGALRM, SIG_DFL); + (void) alarm ((unsigned) 0); + + IdleTime /= 1000; + return((IdleTime + 30) / 60); +} +#endif + +static u_int +getidle(char *tty, char *display) +{ + struct stat st; + char devname[PATH_MAX]; + time_t now; + u_long idle; + + /* + * If this is an X terminal or console, then try the + * XIdle extension + */ +#ifdef XIDLE + if (display && *display && (idle = XqueryIdle(display)) >= 0) + return(idle); +#endif + idle = 0; + if (*tty == 'X') { + u_long kbd_idle, mouse_idle; +#if !defined(__386BSD__) + kbd_idle = getidle("kbd", NULL); +#else + kbd_idle = getidle("vga", NULL); +#endif + mouse_idle = getidle("mouse", NULL); + idle = (kbd_idle < mouse_idle)?kbd_idle:mouse_idle; + } + else { + sprintf(devname, "%s/%s", _PATH_DEV, tty); + if (stat(devname, &st) < 0) { +#ifdef DEBUG + printf("%s: %s\n", devname, strerror(errno)); +#endif + return(-1); + } + time(&now); +#ifdef DEBUG + printf("%s: now=%d atime=%d\n", devname, now, + st.st_atime); +#endif + idle = now - st.st_atime; + idle = (idle + 30) / 60; /* secs->mins */ + } + if (idle < 0) idle = 0; + + return(idle); +} + +static utmpidlearr * +do_names_2(int all) +{ + static utmpidlearr ut; + struct utmp usr; + int nusers = 0; + + bzero((char *)&ut, sizeof(ut)); + ut.utmpidlearr_val = &utmp_idle[0]; + + ufp = fopen(_PATH_UTMP, "r"); + if (!ufp) { + syslog(LOG_ERR, "%m"); + return(&ut); + } + + /* only entries with both name and line fields */ + while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1 && + nusers < MAXUSERS) + if (*usr.ut_name && *usr.ut_line && + strncmp(usr.ut_name, IGNOREUSER, + sizeof(usr.ut_name)) +#ifdef OSF + && usr.ut_type == USER_PROCESS +#endif + ) { + utmp_idle[nusers].ui_utmp.ut_time = + usr.ut_time; + utmp_idle[nusers].ui_idle = + getidle(usr.ut_line, usr.ut_host); + utmp_idle[nusers].ui_utmp.ut_line = line[nusers]; + strncpy(line[nusers], usr.ut_line, sizeof(line[nusers])); + utmp_idle[nusers].ui_utmp.ut_name = name[nusers]; + strncpy(name[nusers], usr.ut_name, sizeof(name[nusers])); + utmp_idle[nusers].ui_utmp.ut_host = host[nusers]; + strncpy(host[nusers], usr.ut_host, sizeof(host[nusers])); + nusers++; + } + + ut.utmpidlearr_len = nusers; + fclose(ufp); + return(&ut); +} + +int * +rusers_num() +{ + int num_users = 0; + struct utmp usr; + + ufp = fopen(_PATH_UTMP, "r"); + if (!ufp) { + syslog(LOG_ERR, "%m"); + return(0); + } + + /* only entries with both name and line fields */ + while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1) + if (*usr.ut_name && *usr.ut_line && + strncmp(usr.ut_name, IGNOREUSER, + sizeof(usr.ut_name)) +#ifdef OSF + && usr.ut_type == USER_PROCESS +#endif + ) { + num_users++; + } + + fclose(ufp); + return(&num_users); +} + +static utmparr * +do_names_1(int all) +{ + utmpidlearr *utidle; + utmparr ut; + int i; + + bzero((char *)&ut, sizeof(ut)); + + utidle = do_names_2(all); + if (utidle) { + ut.utmparr_len = utidle->utmpidlearr_len; + ut.utmparr_val = &old_utmp[0]; + for (i = 0; i < ut.utmparr_len; i++) + bcopy(&utmp_idle[i].ui_utmp, &old_utmp[i], + sizeof(old_utmp[0])); + + } + + return(&ut); +} + +utmpidlearr * +rusersproc_names_2() +{ + return(do_names_2(0)); +} + +utmpidlearr * +rusersproc_allnames_2() +{ + return(do_names_2(1)); +} + +utmparr * +rusersproc_names_1() +{ + return(do_names_1(0)); +} + +utmparr * +rusersproc_allnames_1() +{ + return(do_names_1(1)); +} + +void +rusers_service(rqstp, transp) + struct svc_req *rqstp; + SVCXPRT *transp; +{ + union { + int fill; + } argument; + char *result; + bool_t (*xdr_argument)(), (*xdr_result)(); + char *(*local)(); + + switch (rqstp->rq_proc) { + case NULLPROC: + (void)svc_sendreply(transp, xdr_void, (char *)NULL); + goto leave; + + case RUSERSPROC_NUM: + xdr_argument = xdr_void; + xdr_result = xdr_int; + local = (char *(*)()) rusers_num; + break; + + case RUSERSPROC_NAMES: + xdr_argument = xdr_void; + xdr_result = xdr_utmpidlearr; + switch (rqstp->rq_vers) { + case RUSERSVERS_ORIG: + local = (char *(*)()) rusersproc_names_1; + break; + case RUSERSVERS_IDLE: + local = (char *(*)()) rusersproc_names_2; + break; + default: + svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE); + goto leave; + /*NOTREACHED*/ + } + break; + + case RUSERSPROC_ALLNAMES: + xdr_argument = xdr_void; + xdr_result = xdr_utmpidlearr; + switch (rqstp->rq_vers) { + case RUSERSVERS_ORIG: + local = (char *(*)()) rusersproc_allnames_1; + break; + case RUSERSVERS_IDLE: + local = (char *(*)()) rusersproc_allnames_2; + break; + default: + svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE); + goto leave; + /*NOTREACHED*/ + } + break; + + default: + svcerr_noproc(transp); + goto leave; + } + bzero((char *)&argument, sizeof(argument)); + if (!svc_getargs(transp, xdr_argument, &argument)) { + svcerr_decode(transp); + goto leave; + } + result = (*local)(&argument, rqstp); + if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, &argument)) { + (void)fprintf(stderr, "unable to free arguments\n"); + exit(1); + } +leave: + if (from_inetd) + exit(0); +} diff --git a/libexec/rpc.rusersd/rusersd.c b/libexec/rpc.rusersd/rusersd.c new file mode 100644 index 0000000000..79d6c22a04 --- /dev/null +++ b/libexec/rpc.rusersd/rusersd.c @@ -0,0 +1,108 @@ +/*- + * Copyright (c) 1993, John Brezak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$Id: rusersd.c,v 1.4 1993/08/02 17:50:47 mycroft Exp $"; +#endif /* not lint */ + +#include +#include +#include +#include +#define utmp rutmp +#include +#undef utmp + +extern void rusers_service(); + +int from_inetd = 1; + +void +cleanup() +{ + (void) pmap_unset(RUSERSPROG, RUSERSVERS_IDLE); + (void) pmap_unset(RUSERSPROG, RUSERSVERS_ORIG); + exit(0); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + SVCXPRT *transp; + int sock = 0; + int proto = 0; + struct sockaddr_in from; + int fromlen; + + /* + * See if inetd started us + */ + if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { + from_inetd = 0; + sock = RPC_ANYSOCK; + proto = IPPROTO_UDP; + } + + if (!from_inetd) { + daemon(0, 0); + + (void) pmap_unset(RUSERSPROG, RUSERSVERS_IDLE); + (void) pmap_unset(RUSERSPROG, RUSERSVERS_ORIG); + + (void) signal(SIGINT, cleanup); + (void) signal(SIGTERM, cleanup); + (void) signal(SIGHUP, cleanup); + } + + openlog("rpc.rusersd", LOG_CONS|LOG_PID, LOG_DAEMON); + + transp = svcudp_create(sock); + if (transp == NULL) { + syslog(LOG_ERR, "cannot create udp service."); + exit(1); + } + if (!svc_register(transp, RUSERSPROG, RUSERSVERS_IDLE, rusers_service, proto)) { + syslog(LOG_ERR, "unable to register (RUSERSPROG, RUSERSVERS_IDLE, %s).", proto?"udp":"(inetd)"); + exit(1); + } + + if (!svc_register(transp, RUSERSPROG, RUSERSVERS_ORIG, rusers_service, proto)) { + syslog(LOG_ERR, "unable to register (RUSERSPROG, RUSERSVERS_ORIG, %s).", proto?"udp":"(inetd)"); + exit(1); + } + + svc_run(); + syslog(LOG_ERR, "svc_run returned"); + exit(1); +} diff --git a/libexec/rpc.rwalld/Makefile b/libexec/rpc.rwalld/Makefile new file mode 100644 index 0000000000..7efd954b81 --- /dev/null +++ b/libexec/rpc.rwalld/Makefile @@ -0,0 +1,10 @@ +# $Id: Makefile,v 1.4 1993/08/02 17:50:53 mycroft Exp $ + +PROG = rpc.rwalld +SRCS = rwalld.c +MAN8 = rpc.rwalld.8 + +DPADD= ${LIBRPCSVC} ${LIBRPC} ${LIBUTIL} +LDADD= -lrpcsvc -lrpc -lutil + +.include diff --git a/libexec/rpc.rwalld/rpc.rwalld.8 b/libexec/rpc.rwalld/rpc.rwalld.8 new file mode 100644 index 0000000000..14a621f11a --- /dev/null +++ b/libexec/rpc.rwalld/rpc.rwalld.8 @@ -0,0 +1,67 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 1985, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: rpc.rwalld.8,v 1.3 1993/08/16 15:57:58 jtc Exp $ +.\" +.Dd June 7, 1993 +.Dt RPC.RWALLD 8 +.Os BSD 4.3 +.Sh NAME +.Nm rpc.rwalld +.Nd write messages to users currently logged in server +.Sh SYNOPSIS +.Nm /usr/libexec/rpc.rwalld +.Sh DESCRIPTION +.Nm rpc.rwalld +is a server which will send a message to users +currently logged in to the system. This server +invokes the +.Xr wall 1 +command to actually write the messages to the +system. +.Pp +Messages are sent to this server by the +.Xr rwall 1 +command. +The +.Nm rpc.rwalld +daemon is normally invoked by +.Xr inetd 8 . +.Pp +.Nm rpc.rwalld +uses an RPC protocol defined in +.Pa /usr/include/rpcsvc/rwall.x . +.Sh SEE ALSO +.Xr rwall 1 , +.Xr wall 1 , +.Xr inetd 8 diff --git a/libexec/rpc.rwalld/rwalld.c b/libexec/rpc.rwalld/rwalld.c new file mode 100644 index 0000000000..5bdf6e8da9 --- /dev/null +++ b/libexec/rpc.rwalld/rwalld.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 1993 Christopher G. Demetriou + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$Id: rwalld.c,v 1.2 1993/08/02 17:50:55 mycroft Exp $"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef OSF +#define WALL_CMD "/usr/sbin/wall" +#else +#define WALL_CMD "/usr/bin/wall -n" +#endif + +void wallprog_1(); +void possess(); +void killkids(); + +int nodaemon = 0; +int from_inetd = 1; + +main(argc, argv) + int argc; + char *argv[]; +{ + SVCXPRT *transp; + int s, salen; + struct sockaddr_in sa; + int sock = 0; + int proto = 0; + + if (argc == 2 && !strcmp(argv[1], "-n")) + nodaemon = 1; + if (argc != 1 && !nodaemon) { + printf("usage: %s [-n]\n", argv[0]); + exit(1); + } + + if (geteuid() == 0) { + struct passwd *pep = getpwnam("nobody"); + if (pep) + setuid(pep->pw_uid); + else + setuid(getuid()); + } + + /* + * See if inetd started us + */ + if (getsockname(0, (struct sockaddr *)&sa, &salen) < 0) { + from_inetd = 0; + sock = RPC_ANYSOCK; + proto = IPPROTO_UDP; + } + + if (!from_inetd) { + if (!nodaemon) + possess(); + + (void)pmap_unset(WALLPROG, WALLVERS); + if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + perror("socket"); + exit(1); + } + bzero((char *)&sa, sizeof sa); + if (bind(s, (struct sockaddr *)&sa, sizeof sa) < 0) { + perror("bind"); + exit(1); + } + + salen = sizeof sa; + if (getsockname(s, (struct sockaddr *)&sa, &salen)) { + perror("getsockname"); + exit(1); + } + + pmap_set(WALLPROG, WALLVERS, IPPROTO_UDP, ntohs(sa.sin_port)); + if (dup2(s, 0) < 0) { + perror("dup2"); + exit(1); + } + (void)pmap_unset(WALLPROG, WALLVERS); + } + + (void)signal(SIGCHLD, killkids); + + transp = svcudp_create(sock); + if (transp == NULL) { + (void)fprintf(stderr, "cannot create udp service.\n"); + exit(1); + } + if (!svc_register(transp, WALLPROG, WALLVERS, wallprog_1, proto)) { + (void)fprintf(stderr, "unable to register (WALLPROG, WALLVERS, udp).\n"); + exit(1); + } + svc_run(); + (void)fprintf(stderr, "svc_run returned\n"); + exit(1); + +} + +void possess() +{ + daemon(0, 0); +} + +void killkids() +{ + while(wait4(-1, NULL, WNOHANG, NULL) > 0) + ; +} + +void *wallproc_wall_1(s) + char **s; +{ + /* fork, popen wall with special option, and send the message */ + if (fork() == 0) { + FILE *pfp; + + pfp = popen(WALL_CMD, "w"); + if (pfp != NULL) { + fprintf(pfp, "\007\007%s", *s); + pclose(pfp); + exit(0); + } + } +} + +void +wallprog_1(rqstp, transp) + struct svc_req *rqstp; + SVCXPRT *transp; +{ + union { + char *wallproc_wall_1_arg; + } argument; + char *result; + bool_t (*xdr_argument)(), (*xdr_result)(); + char *(*local)(); + + switch (rqstp->rq_proc) { + case NULLPROC: + (void)svc_sendreply(transp, xdr_void, (char *)NULL); + goto leave; + + case WALLPROC_WALL: + xdr_argument = xdr_wrapstring; + xdr_result = xdr_void; + local = (char *(*)()) wallproc_wall_1; + break; + + default: + svcerr_noproc(transp); + goto leave; + } + bzero((char *)&argument, sizeof(argument)); + if (!svc_getargs(transp, xdr_argument, &argument)) { + svcerr_decode(transp); + goto leave; + } + result = (*local)(&argument, rqstp); + if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, &argument)) { + (void)fprintf(stderr, "unable to free arguments\n"); + exit(1); + } +leave: + if (from_inetd) + exit(0); +} diff --git a/libexec/rshd/Makefile b/libexec/rshd/Makefile index 3da669c4e6..bf4b594e99 100644 --- a/libexec/rshd/Makefile +++ b/libexec/rshd/Makefile @@ -2,9 +2,16 @@ PROG= rshd SRCS= rshd.c -MAN8= rshd.0 +MAN8= rshd.8 DPADD= ${LIBUTIL} LDADD= -lutil .PATH: ${.CURDIR}/../../usr.bin/rlogin +.if exists(/usr/lib/libcrypt.a) +#CFLAGS+=-DCRYPT -DKERBEROS +DPADD+= ${LIBCRYPT} #${LIBKRB} +LDADD+= -lcrypt #-lkrb +.endif + + .include diff --git a/libexec/talkd/Makefile b/libexec/talkd/Makefile index dc5ef0c6d6..bc0f2d2874 100644 --- a/libexec/talkd/Makefile +++ b/libexec/talkd/Makefile @@ -2,6 +2,6 @@ PROG= ntalkd SRCS= talkd.c announce.c process.c table.c print.c -MAN8= talkd.0 +MAN8= talkd.8 .include diff --git a/libexec/telnetd/Makefile b/libexec/telnetd/Makefile index 9de255f043..a094651731 100644 --- a/libexec/telnetd/Makefile +++ b/libexec/telnetd/Makefile @@ -1,12 +1,5 @@ # @(#)Makefile 5.16 (Berkeley) 5/13/91 -# -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 1 00035 -# -------------------- ----- ---------------------- -# -# 27 Nov 1992 Felix Gaehtgens Removed KLUDGELINEMODE -# + PROG= telnetd CFLAGS+=-DLINEMODE -DUSE_TERMIO -DDIAGNOSTICS CFLAGS+=-I${.CURDIR}/../../lib @@ -14,6 +7,13 @@ SRCS= global.c slc.c state.c sys_term.c telnetd.c \ termstat.c utility.c DPADD= ${LIBUTIL} ${LIBTERM} ${LIBTELNET} LDADD= -lutil -ltermcap -ltelnet -MAN8= telnetd.0 +MAN8= telnetd.8 + +.if exists(/usr/lib/libcrypt.a) +#CFLAGS+=-DENCRYPT +DPADD+= ${LIBCRYPT} +LDADD+= -lcrypt +.endif + .include diff --git a/libexec/tftpd/Makefile b/libexec/tftpd/Makefile index 4314b0b46b..397460b485 100644 --- a/libexec/tftpd/Makefile +++ b/libexec/tftpd/Makefile @@ -2,7 +2,7 @@ PROG= tftpd SRCS= tftpd.c tftpsubs.c -MAN8= tftpd.0 +MAN8= tftpd.8 .PATH: ${.CURDIR}/../../usr.bin/tftp .include diff --git a/libexec/tftpd/tftpd.c b/libexec/tftpd/tftpd.c index 29d4748f6b..62e7f8a28b 100644 --- a/libexec/tftpd/tftpd.c +++ b/libexec/tftpd/tftpd.c @@ -69,7 +69,7 @@ static char sccsid[] = "@(#)tftpd.c 5.13 (Berkeley) 2/26/91"; #define TIMEOUT 5 extern int errno; -struct sockaddr_in sin = { AF_INET }; +struct sockaddr_in s_in = { AF_INET }; int peer; int rexmtval = TIMEOUT; int maxtimeout = 5*TIMEOUT; @@ -164,7 +164,7 @@ main(ac, av) syslog(LOG_ERR, "socket: %m\n"); exit(1); } - if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) { + if (bind(peer, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) { syslog(LOG_ERR, "bind: %m\n"); exit(1); } diff --git a/libexec/uucpd/Makefile b/libexec/uucpd/Makefile index 3f3e1faad0..474992ec92 100644 --- a/libexec/uucpd/Makefile +++ b/libexec/uucpd/Makefile @@ -3,4 +3,9 @@ PROG= uucpd NOMAN= noman +.if exists(/usr/lib/libcrypt.a) +DPADD+= ${LIBCRYPT} +LDADD+= -lcrypt +.endif + .include diff --git a/sbin/Makefile b/sbin/Makefile index 0d32975f18..86ebd705b7 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -1,22 +1,21 @@ # @(#)Makefile 5.4.1.1 (Berkeley) 5/7/91 # -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 2 00163 -# -------------------- ----- ---------------------- -# -# 04 Apr 93 Rodney W. Grimes Add fdisk, mount_pcfs, quotacheck -# Comments about not ported and missing -# 27 May 93 Guido van Rooij Added comcontrol -# -SUBDIR= badsect comcontrol clri disklabel dmesg dump dumpfs \ - fastboot fdisk fsck \ - halt ifconfig init mknod mount mount_isofs mount_pcfs mountd newfs \ - nfsd nfsiod ping quotacheck reboot restore route routed savecore \ - shutdown slattach swapon tunefs umount +SUBDIR= XNSrouted badsect comcontrol clri disklabel dmesg dump dumpfs fastboot fdisk \ + fsck halt ifconfig mknod mount mount_isofs mount_pcfs mountd \ + newfs nfsd nfsiod ping quotacheck reboot restore route routed \ + savecore shutdown slattach st swapon tunefs umount + +.if defined(INIT_BSDI) +SUBDIR+= init.bsdi +.elif defined(INIT_CHMR) +SUBDIR+= init.chmr +.else +SUBDIR+= init +.endif + -# Not ported: XNSrouted enpload +# Not ported: enpload # Missing: icheck ncheck .include diff --git a/sbin/XNSrouted/Makefile b/sbin/XNSrouted/Makefile new file mode 100644 index 0000000000..1722c6fed4 --- /dev/null +++ b/sbin/XNSrouted/Makefile @@ -0,0 +1,10 @@ +# From: @(#)Makefile 5.14 (Berkeley) 2/26/91 +# $Id$ + +PROG= XNSrouted +MAN8= XNSrouted.8 +SRCS= af.c if.c input.c main.c output.c startup.c tables.c timer.c trace.c +DPADD= ${LIBUTIL} +LDADD= -lutil + +.include diff --git a/sbin/XNSrouted/XNSrouted.8 b/sbin/XNSrouted/XNSrouted.8 new file mode 100644 index 0000000000..144cb1d404 --- /dev/null +++ b/sbin/XNSrouted/XNSrouted.8 @@ -0,0 +1,186 @@ +.\" Copyright (c) 1986, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)XNSrouted.8 6.4 (Berkeley) 3/16/91 +.\" +.Dd March 16, 1991 +.Dt XNSROUTED 8 +.Os BSD 4.3 +.Sh NAME +.Nm XNSrouted +.Nd NS Routing Information Protocol daemon +.Sh SYNOPSIS +.Nm XNSrouted +.Op Fl q +.Op Fl s +.Op Fl t +.Op Ar logfile +.Sh DESCRIPTION +.Nm XNSrouted +is invoked at boot time to manage the Xerox NS routing tables. +The NS routing daemon uses the Xerox NS Routing +Information Protocol in maintaining up to date kernel routing +table entries. +.Pp +Available options: +.Bl -tag -width logfile +.It Fl q +Do not supply routing information (opposite of +.Fl s +option below). +.It Fl s +Forces +.Nm XNSrouted +to supply routing information whether it is acting as an internetwork +router or not. +.It Fl t +All packets sent or received are +printed on the standard output. In addition, +.Nm XNSrouted +will not divorce itself from the controlling terminal +so that interrupts from the keyboard will kill the process. +.It Ar logfile +Name of file in which +.Nm XNSrouted Ns 's +actions should be logged. This log contains information +about any changes to the routing tables and a history of +recent messages sent and received which are related to +the changed route. +.El +.Pp +In normal operation +.Nm XNSrouted +listens +for routing information packets. If the host is connected to +multiple NS networks, it periodically supplies copies +of its routing tables to any directly connected hosts +and networks. +.Pp +When +.Nm XNSrouted +is started, it uses the +.Dv SIOCGIFCONF +.Xr ioctl 2 +to find those +directly connected interfaces configured into the +system and marked +.Dq up +(the software loopback interface +is ignored). If multiple interfaces +are present, it is assumed the host will forward packets +between networks. +.Nm XNSrouted +then transmits a +.Em request +packet on each interface (using a broadcast packet if +the interface supports it) and enters a loop, listening +for +.Em request +and +.Em response +packets from other hosts. +.Pp +When a +.Em request +packet is received, +.Nm XNSrouted +formulates a reply based on the information maintained in its +internal tables. The +.Em response +packet generated contains a list of known routes, each marked +with a +.Dq hop count +metric (a count of 16, or greater, is +considered +.Dq infinite ) . +The metric associated with each +route returned provides a metric +.Em relative to the sender . +.Pp +.Em Response +packets received by +.Nm XNSrouted +are used to update the routing tables if one of the following +conditions is satisfied: +.Bl -bullet +.It +No routing table entry exists for the destination network +or host, and the metric indicates the destination is ``reachable'' +(i.e. the hop count is not infinite). +.It +The source host of the packet is the same as the router in the +existing routing table entry. That is, updated information is +being received from the very internetwork router through which +packets for the destination are being routed. +.It +The existing entry in the routing table has not been updated for +some time (defined to be 90 seconds) and the route is at least +as cost effective as the current route. +.It +The new route describes a shorter route to the destination than +the one currently stored in the routing tables; the metric of +the new route is compared against the one stored in the table +to decide this. +.El +.Pp +When an update is applied, +.Nm XNSrouted +records the change in its internal tables and generates a +.Em response +packet to all directly connected hosts and networks. +.Xr Routed 8 +waits a short period +of time (no more than 30 seconds) before modifying the kernel's +routing tables to allow possible unstable situations to settle. +.Pp +In addition to processing incoming packets, +.Nm XNSrouted +also periodically checks the routing table entries. +If an entry has not been updated for 3 minutes, the entry's metric +is set to infinity and marked for deletion. Deletions are delayed +an additional 60 seconds to insure the invalidation is propagated +to other routers. +.Pp +Hosts acting as internetwork routers gratuitously supply their +routing tables every 30 seconds to all directly connected hosts +and networks. +.Sh SEE ALSO +.Xr idp 4 +.Rs +.%T "Internet Transport Protocols" +.%R "XSIS 028112" +.%Q "Xerox System Integration Standard" +.Re +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/sbin/XNSrouted/af.c b/sbin/XNSrouted/af.c new file mode 100644 index 0000000000..d131e87739 --- /dev/null +++ b/sbin/XNSrouted/af.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)af.c 5.11 (Berkeley) 2/26/91"; +#endif /* not lint */ + +#include "defs.h" + +/* + * Address family support routines + */ +int null_hash(), null_netmatch(), null_output(), + null_portmatch(), null_portcheck(), + null_checkhost(), null_ishost(), null_canon(); +int xnnet_hash(), xnnet_netmatch(), xnnet_output(), + xnnet_portmatch(), + xnnet_checkhost(), xnnet_ishost(), xnnet_canon(); +#define NIL \ + { null_hash, null_netmatch, null_output, \ + null_portmatch, null_portcheck, null_checkhost, \ + null_ishost, null_canon } +#define XNSNET \ + { xnnet_hash, xnnet_netmatch, xnnet_output, \ + xnnet_portmatch, xnnet_portmatch, xnnet_checkhost, \ + xnnet_ishost, xnnet_canon } + +struct afswitch afswitch[AF_MAX] = + { NIL, NIL, NIL, NIL, NIL, NIL, XNSNET, NIL, NIL, NIL, NIL }; + +struct sockaddr_ns xnnet_default = { sizeof(struct sockaddr_ns), AF_NS }; + +union ns_net ns_anynet; +union ns_net ns_zeronet; + +xnnet_hash(sns, hp) + register struct sockaddr_ns *sns; + struct afhash *hp; +{ + register long hash = 0; + register u_short *s = sns->sns_addr.x_host.s_host; + union ns_net_u net; + + net.net_e = sns->sns_addr.x_net; + hp->afh_nethash = net.long_e; + hash = *s++; hash <<= 8; hash += *s++; hash <<= 8; hash += *s; + hp->afh_hosthash = hash; +} + +xnnet_netmatch(sxn1, sxn2) + struct sockaddr_ns *sxn1, *sxn2; +{ + return (ns_neteq(sxn1->sns_addr, sxn2->sns_addr)); +} + +/* + * Verify the message is from the right port. + */ +xnnet_portmatch(sns) + register struct sockaddr_ns *sns; +{ + + return (ntohs(sns->sns_addr.x_port) == IDPPORT_RIF ); +} + + +/* + * xns output routine. + */ +#ifdef DEBUG +int do_output = 0; +#endif +xnnet_output(flags, sns, size) + int flags; + struct sockaddr_ns *sns; + int size; +{ + struct sockaddr_ns dst; + + dst = *sns; + sns = &dst; + if (sns->sns_addr.x_port == 0) + sns->sns_addr.x_port = htons(IDPPORT_RIF); +#ifdef DEBUG + if(do_output || ntohs(msg->rip_cmd) == RIPCMD_REQUEST) +#endif + /* + * Kludge to allow us to get routes out to machines that + * don't know their addresses yet; send to that address on + * ALL connected nets + */ + if (ns_neteqnn(sns->sns_addr.x_net, ns_zeronet)) { + extern struct interface *ifnet; + register struct interface *ifp; + + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + sns->sns_addr.x_net = + satons_addr(ifp->int_addr).x_net; + (void) sendto(s, msg, size, flags, + (struct sockaddr *)sns, sizeof (*sns)); + } + return; + } + + (void) sendto(s, msg, size, flags, + (struct sockaddr *)sns, sizeof (*sns)); +} + +/* + * Return 1 if we want this route. + * We use this to disallow route net G entries for one for multiple + * point to point links. + */ +xnnet_checkhost(sns) + struct sockaddr_ns *sns; +{ + register struct interface *ifp = if_ifwithnet(sns); + /* + * We want this route if there is no more than one + * point to point interface with this network. + */ + if (ifp == 0 || (ifp->int_flags & IFF_POINTOPOINT)==0) return (1); + return (ifp->int_sq.n == ifp->int_sq.p); +} + +/* + * Return 1 if the address is + * for a host, 0 for a network. + */ +xnnet_ishost(sns) +struct sockaddr_ns *sns; +{ + register u_short *s = sns->sns_addr.x_host.s_host; + + if ((s[0]==0xffff) && (s[1]==0xffff) && (s[2]==0xffff)) + return (0); + else + return (1); +} + +xnnet_canon(sns) + struct sockaddr_ns *sns; +{ + + sns->sns_addr.x_port = 0; +} + +/*ARGSUSED*/ +null_hash(addr, hp) + struct sockaddr *addr; + struct afhash *hp; +{ + + hp->afh_nethash = hp->afh_hosthash = 0; +} + +/*ARGSUSED*/ +null_netmatch(a1, a2) + struct sockaddr *a1, *a2; +{ + + return (0); +} + +/*ARGSUSED*/ +null_output(s, f, a1, n) + int s, f; + struct sockaddr *a1; + int n; +{ + + ; +} + +/*ARGSUSED*/ +null_portmatch(a1) + struct sockaddr *a1; +{ + + return (0); +} + +/*ARGSUSED*/ +null_portcheck(a1) + struct sockaddr *a1; +{ + + return (0); +} + +/*ARGSUSED*/ +null_ishost(a1) + struct sockaddr *a1; +{ + + return (0); +} + +/*ARGSUSED*/ +null_checkhost(a1) + struct sockaddr *a1; +{ + + return (0); +} + +/*ARGSUSED*/ +null_canon(a1) + struct sockaddr *a1; +{ + + ; +} diff --git a/sbin/XNSrouted/af.h b/sbin/XNSrouted/af.h new file mode 100644 index 0000000000..d2b27c5313 --- /dev/null +++ b/sbin/XNSrouted/af.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)af.h 5.1 (Berkeley) 6/4/85 (routed/af.h) + * + * @(#)af.h 5.2 (Berkeley) 6/1/90 + */ + +/* + * Routing table management daemon. + */ + +/* + * Per address family routines. + */ +struct afswitch { + int (*af_hash)(); /* returns keys based on address */ + int (*af_netmatch)(); /* verifies net # matching */ + int (*af_output)(); /* interprets address for sending */ + int (*af_portmatch)(); /* packet from some other router? */ + int (*af_portcheck)(); /* packet from privileged peer? */ + int (*af_checkhost)(); /* tells if address for host or net */ + int (*af_ishost)(); /* tells if address is valid */ + int (*af_canon)(); /* canonicalize address for compares */ +}; + +/* + * Structure returned by af_hash routines. + */ +struct afhash { + u_int afh_hosthash; /* host based hash */ + u_int afh_nethash; /* network based hash */ +}; + +struct afswitch afswitch[AF_MAX]; /* table proper */ diff --git a/sbin/XNSrouted/defs.h b/sbin/XNSrouted/defs.h new file mode 100644 index 0000000000..5414d8c08c --- /dev/null +++ b/sbin/XNSrouted/defs.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)defs.h 5.9 (Berkeley) 2/26/91 + */ + +#include +#include + +#include +#include +#include +#if defined(vax) || defined(pdp11) +#define xnnet(x) ((u_long) (x)->rip_dst[1] << 16 | (u_long) (x)->rip_dst[0] ) +#else +#define xnnet(x) ((u_long) (x)->rip_dst[0] << 16 | (u_long) (x)->rip_dst[1] ) +#endif +#define IDPPORT_RIF 1 + +#include +#include + +#include "protocol.h" +#include "trace.h" +#include "interface.h" +#include "table.h" +#include "af.h" + + +/* + * When we find any interfaces marked down we rescan the + * kernel every CHECK_INTERVAL seconds to see if they've + * come up. + */ +#define CHECK_INTERVAL (5*60) + +#define equal(a1, a2) \ + (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0) +#define min(a,b) ((a)>(b)?(b):(a)) + +struct sockaddr_ns addr; /* Daemon's Address */ +int s; /* Socket to listen on */ +int kmem; +int supplier; /* process should supply updates */ +int install; /* if 1 call kernel */ +int lookforinterfaces; /* if 1 probe kernel for new up interfaces */ +int performnlist; /* if 1 check if /vmunix has changed */ +int externalinterfaces; /* # of remote and local interfaces */ +int timeval; /* local idea of time */ +int noteremoterequests; /* squawk on requests from non-local nets */ + +char packet[MAXPACKETSIZE+sizeof(struct idp)+1]; +struct rip *msg; + +char **argv0; + +int sndmsg(); +int supply(); +int cleanup(); diff --git a/sbin/XNSrouted/if.c b/sbin/XNSrouted/if.c new file mode 100644 index 0000000000..0f597a66fa --- /dev/null +++ b/sbin/XNSrouted/if.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * static char sccsid[] = "@(#)if.c 5.1 (Berkeley) 6/4/85"; (routed/if.c) + */ + +#ifndef lint +static char sccsid[] = "@(#)if.c 5.2 (Berkeley) 6/1/90"; +#endif /* not lint */ + +/* + * Routing Table Management Daemon + */ +#include "defs.h" + +extern struct interface *ifnet; + +/* + * Find the interface with address addr. + */ +struct interface * +if_ifwithaddr(addr) + struct sockaddr *addr; +{ + register struct interface *ifp; + +#define same(a1, a2) \ + (bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0) + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (ifp->int_flags & IFF_REMOTE) + continue; + if (ifp->int_addr.sa_family != addr->sa_family) + continue; + if (same(&ifp->int_addr, addr)) + break; + if ((ifp->int_flags & IFF_BROADCAST) && + same(&ifp->int_broadaddr, addr)) + break; + } + return (ifp); +} + +/* + * Find the point-to-point interface with destination address addr. + */ +struct interface * +if_ifwithdstaddr(addr) + struct sockaddr *addr; +{ + register struct interface *ifp; + + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if ((ifp->int_flags & IFF_POINTOPOINT) == 0) + continue; + if (same(&ifp->int_dstaddr, addr)) + break; + } + return (ifp); +} + +/* + * Find the interface on the network + * of the specified address. + */ +struct interface * +if_ifwithnet(addr) + register struct sockaddr *addr; +{ + register struct interface *ifp; + register int af = addr->sa_family; + register int (*netmatch)(); + + if (af >= AF_MAX) + return (0); + netmatch = afswitch[af].af_netmatch; + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (ifp->int_flags & IFF_REMOTE) + continue; + if (af != ifp->int_addr.sa_family) + continue; + if ((*netmatch)(addr, &ifp->int_addr)) + break; + } + return (ifp); +} + +/* + * Find an interface from which the specified address + * should have come from. Used for figuring out which + * interface a packet came in on -- for tracing. + */ +struct interface * +if_iflookup(addr) + struct sockaddr *addr; +{ + register struct interface *ifp, *maybe; + register int af = addr->sa_family; + register int (*netmatch)(); + + if (af >= AF_MAX) + return (0); + maybe = 0; + netmatch = afswitch[af].af_netmatch; + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (ifp->int_addr.sa_family != af) + continue; + if (same(&ifp->int_addr, addr)) + break; + if ((ifp->int_flags & IFF_BROADCAST) && + same(&ifp->int_broadaddr, addr)) + break; + if (maybe == 0 && (*netmatch)(addr, &ifp->int_addr)) + maybe = ifp; + } + if (ifp == 0) + ifp = maybe; + return (ifp); +} diff --git a/sbin/XNSrouted/input.c b/sbin/XNSrouted/input.c new file mode 100644 index 0000000000..7dc64fa5cc --- /dev/null +++ b/sbin/XNSrouted/input.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)input.c 5.9 (Berkeley) 6/1/90"; +#endif /* not lint */ + +/* + * XNS Routing Table Management Daemon + */ +#include "defs.h" + +struct sockaddr * +xns_nettosa(net) +union ns_net net; +{ + static struct sockaddr_ns sxn; + extern char ether_broadcast_addr[6]; + + bzero(&sxn, sizeof (struct sockaddr_ns)); + sxn.sns_family = AF_NS; + sxn.sns_len = sizeof (sxn); + sxn.sns_addr.x_net = net; + sxn.sns_addr.x_host = *(union ns_host *)ether_broadcast_addr; + return( (struct sockaddr *)&sxn); + +} + +/* + * Process a newly received packet. + */ +rip_input(from, size) + struct sockaddr *from; + int size; +{ + struct rt_entry *rt; + struct netinfo *n; + struct interface *ifp; + int newsize; + struct afswitch *afp; + + + ifp = 0; + TRACE_INPUT(ifp, from, size); + if (from->sa_family >= AF_MAX) + return; + afp = &afswitch[from->sa_family]; + + size -= sizeof (u_short) /* command */; + n = msg->rip_nets; + + switch (ntohs(msg->rip_cmd)) { + + case RIPCMD_REQUEST: + newsize = 0; + while (size > 0) { + if (size < sizeof (struct netinfo)) + break; + size -= sizeof (struct netinfo); + + /* + * A single entry with rip_dst == DSTNETS_ALL and + * metric ``infinity'' means ``all routes''. + */ + if (ns_neteqnn(n->rip_dst, ns_anynet) && + ntohs(n->rip_metric) == HOPCNT_INFINITY && + size == 0) { + ifp = if_ifwithnet(from); + supply(from, 0, ifp); + return; + } + /* + * request for specific nets + */ + rt = rtlookup(xns_nettosa(n->rip_dst)); + if (ftrace) { + fprintf(ftrace, + "specific request for %s", + xns_nettoa(n->rip_dst)); + fprintf(ftrace, + " yields route %x\n", + rt); + } + n->rip_metric = htons( rt == 0 ? HOPCNT_INFINITY : + min(rt->rt_metric+1, HOPCNT_INFINITY)); + n++; + newsize += sizeof (struct netinfo); + } + if (newsize > 0) { + msg->rip_cmd = htons(RIPCMD_RESPONSE); + newsize += sizeof (u_short); + /* should check for if with dstaddr(from) first */ + (*afp->af_output)(0, from, newsize); + ifp = if_ifwithnet(from); + TRACE_OUTPUT(ifp, from, newsize); + if (ftrace) { + fprintf(ftrace, + "request arrived on interface %s\n", + ifp->int_name); + } + } + return; + + case RIPCMD_RESPONSE: + /* verify message came from a router */ + if ((*afp->af_portmatch)(from) == 0) + return; + (*afp->af_canon)(from); + /* are we talking to ourselves? */ + if (ifp = if_ifwithaddr(from)) { + rt = rtfind(from); + if (rt == 0 || (rt->rt_state & RTS_INTERFACE) == 0) + addrouteforif(ifp); + else + rt->rt_timer = 0; + return; + } + /* Update timer for interface on which the packet arrived. + * If from other end of a point-to-point link that isn't + * in the routing tables, (re-)add the route. + */ + if ((rt = rtfind(from)) && (rt->rt_state & RTS_INTERFACE)) { + if(ftrace) fprintf(ftrace, "Got route\n"); + rt->rt_timer = 0; + } else if (ifp = if_ifwithdstaddr(from)) { + if(ftrace) fprintf(ftrace, "Got partner\n"); + addrouteforif(ifp); + } + for (; size > 0; size -= sizeof (struct netinfo), n++) { + struct sockaddr *sa; + if (size < sizeof (struct netinfo)) + break; + if ((unsigned) ntohs(n->rip_metric) >= HOPCNT_INFINITY) + continue; + rt = rtfind(sa = xns_nettosa(n->rip_dst)); + if (rt == 0) { + rtadd(sa, from, ntohs(n->rip_metric), 0); + continue; + } + + /* + * Update if from gateway and different, + * from anywhere and shorter, or getting stale and equivalent. + */ + if ((equal(from, &rt->rt_router) && + ntohs(n->rip_metric) != rt->rt_metric ) || + (unsigned) ntohs(n->rip_metric) < rt->rt_metric || + (rt->rt_timer > (EXPIRE_TIME/2) && + rt->rt_metric == ntohs(n->rip_metric))) { + rtchange(rt, from, ntohs(n->rip_metric)); + rt->rt_timer = 0; + } + } + return; + } +} diff --git a/sbin/XNSrouted/interface.h b/sbin/XNSrouted/interface.h new file mode 100644 index 0000000000..7cb416699a --- /dev/null +++ b/sbin/XNSrouted/interface.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)interface.h 5.5 (Berkeley) 6/1/90 + */ + +/* + * Routing table management daemon. + */ + +/* + * An ``interface'' is similar to an ifnet structure, + * except it doesn't contain q'ing info, and it also + * handles ``logical'' interfaces (remote gateways + * that we want to keep polling even if they go down). + * The list of interfaces which we maintain is used + * in supplying the gratuitous routing table updates. + * We list only one address for each interface, the AF_XNS one. + */ +#define NIFADDR 3 +struct interface { + struct interface *int_next; + struct sockaddr int_addr; /* address on this host */ + union { + struct sockaddr intu_broadaddr; + struct sockaddr intu_dstaddr; + } int_intu; +#define int_broadaddr int_intu.intu_broadaddr /* broadcast address */ +#define int_dstaddr int_intu.intu_dstaddr /* other end of p-to-p link */ + int int_metric; /* init's routing entry */ + int int_flags; /* see below */ + struct ifdebug int_input, int_output; /* packet tracing stuff */ + int int_ipackets; /* input packets received */ + int int_opackets; /* output packets sent */ + char *int_name; /* from kernel if structure */ + u_short int_transitions; /* times gone up-down */ +/*XNS Specific entry */ + struct sameq { + struct sameq *n; /* q of other pt-to-pt links */ + struct sameq *p; /* with same net # */ + } int_sq; +}; + +/* + * 0x1 to 0x10 are reused from the kernel's ifnet definitions, + * the others agree with the RTS_ flags defined elsewhere. + */ +#define IFF_UP 0x1 /* interface is up */ +#define IFF_BROADCAST 0x2 /* broadcast address valid */ +#define IFF_DEBUG 0x4 /* turn on debugging */ +#define IFF_ROUTE 0x8 /* routing entry installed */ +#define IFF_POINTOPOINT 0x10 /* interface is point-to-point link */ + +#define IFF_PASSIVE 0x2000 /* can't tell if up/down */ +#define IFF_INTERFACE 0x4000 /* hardware interface */ +#define IFF_REMOTE 0x8000 /* interface isn't on this machine */ + +struct interface *if_ifwithaddr(); +struct interface *if_ifwithdstaddr(); +struct interface *if_ifwithnet(); +struct interface *if_iflookup(); diff --git a/sbin/XNSrouted/main.c b/sbin/XNSrouted/main.c new file mode 100644 index 0000000000..f4ddf28583 --- /dev/null +++ b/sbin/XNSrouted/main.c @@ -0,0 +1,245 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1985 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 5.11 (Berkeley) 2/26/91"; +#endif /* not lint */ + +/* + * XNS Routing Information Protocol Daemon + */ +#include "defs.h" +#include + +#include + +#include +#include +#include +#include + +int supplier = -1; /* process should supply updates */ +extern int gateway; + +struct rip *msg = (struct rip *) &packet[sizeof (struct idp)]; +void hup(), fkexit(), timer(); + +main(argc, argv) + int argc; + char *argv[]; +{ + int cc; + struct sockaddr from; + u_char retry; + + argv0 = argv; + argv++, argc--; + while (argc > 0 && **argv == '-') { + if (strcmp(*argv, "-s") == 0) { + supplier = 1; + argv++, argc--; + continue; + } + if (strcmp(*argv, "-q") == 0) { + supplier = 0; + argv++, argc--; + continue; + } + if (strcmp(*argv, "-R") == 0) { + noteremoterequests++; + argv++, argc--; + continue; + } + if (strcmp(*argv, "-t") == 0) { + tracepackets++; + argv++, argc--; + ftrace = stderr; + tracing = 1; + continue; + } + if (strcmp(*argv, "-g") == 0) { + gateway = 1; + argv++, argc--; + continue; + } + if (strcmp(*argv, "-l") == 0) { + gateway = -1; + argv++, argc--; + continue; + } + fprintf(stderr, + "usage: xnsrouted [ -s ] [ -q ] [ -t ] [ -g ] [ -l ]\n"); + exit(1); + } + + +#ifndef DEBUG + if (!tracepackets) + daemon(0, 0); +#endif + openlog("XNSrouted", LOG_PID, LOG_DAEMON); + + ns_anynet.s_net[0] = -1; ns_anynet.s_net[1] = -1; + addr.sns_family = AF_NS; + addr.sns_len = sizeof(addr); + addr.sns_port = htons(IDPPORT_RIF); + s = getsocket(SOCK_DGRAM, 0, &addr); + if (s < 0) + exit(1); + /* + * Any extra argument is considered + * a tracing log file. + */ + if (argc > 0) + traceon(*argv); + /* + * Collect an initial view of the world by + * snooping in the kernel. Then, send a request packet on all + * directly connected networks to find out what + * everyone else thinks. + */ + rtinit(); + ifinit(); + if (supplier < 0) + supplier = 0; + /* request the state of the world */ + msg->rip_cmd = htons(RIPCMD_REQUEST); + msg->rip_nets[0].rip_dst = ns_anynet; + msg->rip_nets[0].rip_metric = htons(HOPCNT_INFINITY); + toall(sndmsg); + signal(SIGALRM, timer); + signal(SIGHUP, hup); + signal(SIGINT, hup); + signal(SIGEMT, fkexit); + timer(); + + + for (;;) + process(s); + +} + +process(fd) + int fd; +{ + struct sockaddr from; + int fromlen = sizeof (from), cc, omask; + struct idp *idp = (struct idp *)packet; + + cc = recvfrom(fd, packet, sizeof (packet), 0, &from, &fromlen); + if (cc <= 0) { + if (cc < 0 && errno != EINTR) + syslog(LOG_ERR, "recvfrom: %m"); + return; + } + if (tracepackets > 1 && ftrace) { + fprintf(ftrace,"rcv %d bytes on %s ", cc, xns_ntoa(&idp->idp_dna)); + fprintf(ftrace," from %s\n", xns_ntoa(&idp->idp_sna)); + } + + if (noteremoterequests && !ns_neteqnn(idp->idp_sna.x_net, ns_zeronet) + && !ns_neteq(idp->idp_sna, idp->idp_dna)) + { + syslog(LOG_ERR, + "net of interface (%s) != net on ether (%s)!\n", + xns_nettoa(idp->idp_dna.x_net), + xns_nettoa(idp->idp_sna.x_net)); + } + + /* We get the IDP header in front of the RIF packet*/ + cc -= sizeof (struct idp); +#define mask(s) (1<<((s)-1)) + omask = sigblock(mask(SIGALRM)); + rip_input(&from, cc); + sigsetmask(omask); +} + +getsocket(type, proto, sns) + int type, proto; + struct sockaddr_ns *sns; +{ + int domain = sns->sns_family; + int retry, s, on = 1; + + retry = 1; + while ((s = socket(domain, type, proto)) < 0 && retry) { + syslog(LOG_ERR, "socket: %m"); + sleep(5 * retry); + retry <<= 1; + } + if (retry == 0) + return (-1); + while (bind(s, (struct sockaddr *)sns, sizeof (*sns)) < 0 && retry) { + syslog(LOG_ERR, "bind: %m"); + sleep(5 * retry); + retry <<= 1; + } + if (retry == 0) + return (-1); + if (domain==AF_NS) { + struct idp idp; + if (setsockopt(s, 0, SO_HEADERS_ON_INPUT, &on, sizeof(on))) { + syslog(LOG_ERR, "setsockopt SEE HEADERS: %m"); + exit(1); + } + idp.idp_pt = NSPROTO_RI; + if (setsockopt(s, 0, SO_DEFAULT_HEADERS, &idp, sizeof(idp))) { + syslog(LOG_ERR, "setsockopt SET HEADER: %m"); + exit(1); + } + } + if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) { + syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); + exit(1); + } + return (s); +} + +/* + * Fork and exit on EMT-- for profiling. + */ +void +fkexit() +{ + if (fork() == 0) + exit(0); +} diff --git a/sbin/XNSrouted/output.c b/sbin/XNSrouted/output.c new file mode 100644 index 0000000000..cdea11f2b7 --- /dev/null +++ b/sbin/XNSrouted/output.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)output.c 5.8 (Berkeley) 2/26/91"; +#endif /* not lint */ + +/* + * Routing Table Management Daemon + */ +#include "defs.h" + +/* + * Apply the function "f" to all non-passive + * interfaces. If the interface supports the + * use of broadcasting use it, otherwise address + * the output to the known router. + */ +toall(f) + int (*f)(); +{ + register struct interface *ifp; + register struct sockaddr *dst; + register int flags; + extern struct interface *ifnet; + + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (ifp->int_flags & IFF_PASSIVE) + continue; + dst = ifp->int_flags & IFF_BROADCAST ? &ifp->int_broadaddr : + ifp->int_flags & IFF_POINTOPOINT ? &ifp->int_dstaddr : + &ifp->int_addr; + flags = ifp->int_flags & IFF_INTERFACE ? MSG_DONTROUTE : 0; + (*f)(dst, flags, ifp); + } +} + +/* + * Output a preformed packet. + */ +/*ARGSUSED*/ +sndmsg(dst, flags, ifp) + struct sockaddr *dst; + int flags; + struct interface *ifp; +{ + + (*afswitch[dst->sa_family].af_output) + (flags, dst, sizeof (struct rip)); + TRACE_OUTPUT(ifp, dst, sizeof (struct rip)); +} + +/* + * Supply dst with the contents of the routing tables. + * If this won't fit in one packet, chop it up into several. + */ +supply(dst, flags, ifp) + struct sockaddr *dst; + int flags; + struct interface *ifp; +{ + register struct rt_entry *rt; + register struct rthash *rh; + register struct netinfo *nn; + register struct netinfo *n = msg->rip_nets; + struct rthash *base = hosthash; + struct sockaddr_ns *sns = (struct sockaddr_ns *) dst; + int (*output)() = afswitch[dst->sa_family].af_output; + int doinghost = 1, size, metric; + union ns_net net; + + msg->rip_cmd = ntohs(RIPCMD_RESPONSE); +again: + for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) + for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { + size = (char *)n - (char *)msg; + if (size > MAXPACKETSIZE - sizeof (struct netinfo)) { + (*output)(flags, dst, size); + TRACE_OUTPUT(ifp, dst, size); + n = msg->rip_nets; + } + sns = (struct sockaddr_ns *)&rt->rt_dst; + if ((rt->rt_flags & (RTF_HOST|RTF_GATEWAY)) == RTF_HOST) + sns = (struct sockaddr_ns *)&rt->rt_router; + metric = min(rt->rt_metric + 1, HOPCNT_INFINITY); + net = sns->sns_addr.x_net; + /* + * Make sure that we don't put out a two net entries + * for a pt to pt link (one for the G route, one for the if) + * This is a kludge, and won't work if there are lots of nets. + */ + for (nn = msg->rip_nets; nn < n; nn++) { + if (ns_neteqnn(net, nn->rip_dst)) { + if (metric < ntohs(nn->rip_metric)) + nn->rip_metric = htons(metric); + goto next; + } + } + n->rip_dst = net; + n->rip_metric = htons(metric); + n++; + next:; + } + if (doinghost) { + doinghost = 0; + base = nethash; + goto again; + } + if (n != msg->rip_nets) { + size = (char *)n - (char *)msg; + (*output)(flags, dst, size); + TRACE_OUTPUT(ifp, dst, size); + } +} diff --git a/sbin/XNSrouted/protocol.h b/sbin/XNSrouted/protocol.h new file mode 100644 index 0000000000..9bf422b606 --- /dev/null +++ b/sbin/XNSrouted/protocol.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)protocol.h 5.6 (Berkeley) 6/1/90 + */ + +/* + * Xerox Routing Information Protocol + * + */ + +struct netinfo { + union ns_net rip_dst; /* destination net */ + u_short rip_metric; /* cost of route */ +}; + +struct rip { + u_short rip_cmd; /* request/response */ + struct netinfo rip_nets[1]; /* variable length */ +}; + +/* + * Packet types. + */ +#define RIPCMD_REQUEST 1 /* want info */ +#define RIPCMD_RESPONSE 2 /* responding to request */ + +#define RIPCMD_MAX 3 +#ifdef RIPCMDS +char *ripcmds[RIPCMD_MAX] = + { "#0", "REQUEST", "RESPONSE" }; +#endif + +#define HOPCNT_INFINITY 16 /* per Xerox NS */ +#define DSTNETS_ALL 0xffffffff /* per Xerox NS */ +#define MAXPACKETSIZE 512 /* max broadcast size */ + +extern union ns_net ns_anynet; +extern union ns_net ns_zeronet; + +/* + * Timer values used in managing the routing table. + * Every update forces an entry's timer to be reset. After + * EXPIRE_TIME without updates, the entry is marked invalid, + * but held onto until GARBAGE_TIME so that others may + * see it "be deleted". + */ +#define TIMER_RATE 30 /* alarm clocks every 30 seconds */ + +#define SUPPLY_INTERVAL 30 /* time to supply tables */ + +#define EXPIRE_TIME 180 /* time to mark entry invalid */ +#define GARBAGE_TIME 240 /* time to garbage collect */ diff --git a/sbin/XNSrouted/startup.c b/sbin/XNSrouted/startup.c new file mode 100644 index 0000000000..8887c1fc47 --- /dev/null +++ b/sbin/XNSrouted/startup.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)startup.c 5.11 (Berkeley) 2/26/91"; +#endif /* not lint */ + +/* + * Routing Table Management Daemon + */ +#include "defs.h" +#include +#include +#include +#include + +struct interface *ifnet; +int lookforinterfaces = 1; +int performnlist = 1; +int gateway = 0; +int externalinterfaces = 0; /* # of remote and local interfaces */ +char ether_broadcast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + +/* + * Find the network interfaces which have configured themselves. + * If the interface is present but not yet up (for example an + * ARPANET IMP), set the lookforinterfaces flag so we'll + * come back later and look again. + */ +ifinit() +{ + struct interface ifs, *ifp; + int s; + struct ifconf ifc; + char buf[BUFSIZ], *cp, *cplim; + struct ifreq ifreq, *ifr; + u_long i; + + if ((s = socket(AF_NS, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "socket: %m"); + exit(1); + } + ifc.ifc_len = sizeof (buf); + ifc.ifc_buf = buf; + if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) { + syslog(LOG_ERR, "ioctl (get interface configuration)"); + close(s); + exit(1); + } + ifr = ifc.ifc_req; + lookforinterfaces = 0; +#ifdef RTM_ADD +#define max(a, b) (a > b ? a : b) +#define size(p) max((p).sa_len, sizeof(p)) +#else +#define size(p) (sizeof (p)) +#endif + cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */ + for (cp = buf; cp < cplim; + cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) { + ifr = (struct ifreq *)cp; + bzero((char *)&ifs, sizeof(ifs)); + ifs.int_addr = ifr->ifr_addr; + ifreq = *ifr; + if (ioctl(s, SIOCGIFFLAGS, (char *)&ifreq) < 0) { + syslog(LOG_ERR, "ioctl (get interface flags)"); + continue; + } + ifs.int_flags = ifreq.ifr_flags | IFF_INTERFACE; + if ((ifs.int_flags & IFF_UP) == 0 || + ifr->ifr_addr.sa_family == AF_UNSPEC) { + lookforinterfaces = 1; + continue; + } + if (ifs.int_addr.sa_family != AF_NS) + continue; + if (ifs.int_flags & IFF_POINTOPOINT) { + if (ioctl(s, SIOCGIFDSTADDR, (char *)&ifreq) < 0) { + syslog(LOG_ERR, "ioctl (get dstaddr): %m"); + continue; + } + ifs.int_dstaddr = ifreq.ifr_dstaddr; + } + if (ifs.int_flags & IFF_BROADCAST) { + if (ioctl(s, SIOCGIFBRDADDR, (char *)&ifreq) < 0) { + syslog(LOG_ERR, "ioctl (get broadaddr: %m"); + continue; + } + ifs.int_broadaddr = ifreq.ifr_broadaddr; + } + /* + * already known to us? + * what makes a POINTOPOINT if unique is its dst addr, + * NOT its source address + */ + if ( ((ifs.int_flags & IFF_POINTOPOINT) && + if_ifwithdstaddr(&ifs.int_dstaddr)) || + ( ((ifs.int_flags & IFF_POINTOPOINT) == 0) && + if_ifwithaddr(&ifs.int_addr))) + continue; + /* no one cares about software loopback interfaces */ + if (strncmp(ifr->ifr_name,"lo", 2)==0) + continue; + ifp = (struct interface *)malloc(sizeof (struct interface)); + if (ifp == 0) { + syslog(LOG_ERR,"XNSrouted: out of memory\n"); + break; + } + *ifp = ifs; + /* + * Count the # of directly connected networks + * and point to point links which aren't looped + * back to ourself. This is used below to + * decide if we should be a routing ``supplier''. + */ + if ((ifs.int_flags & IFF_POINTOPOINT) == 0 || + if_ifwithaddr(&ifs.int_dstaddr) == 0) + externalinterfaces++; + /* + * If we have a point-to-point link, we want to act + * as a supplier even if it's our only interface, + * as that's the only way our peer on the other end + * can tell that the link is up. + */ + if ((ifs.int_flags & IFF_POINTOPOINT) && supplier < 0) + supplier = 1; + ifp->int_name = malloc(strlen(ifr->ifr_name) + 1); + if (ifp->int_name == 0) { + syslog(LOG_ERR,"XNSrouted: out of memory\n"); + exit(1); + } + strcpy(ifp->int_name, ifr->ifr_name); + ifp->int_metric = 0; + ifp->int_next = ifnet; + ifnet = ifp; + traceinit(ifp); + addrouteforif(ifp); + } + if (externalinterfaces > 1 && supplier < 0) + supplier = 1; + close(s); +} + +addrouteforif(ifp) + struct interface *ifp; +{ + struct sockaddr_ns net; + struct sockaddr *dst; + int state, metric; + struct rt_entry *rt; + + if (ifp->int_flags & IFF_POINTOPOINT) { + int (*match)(); + register struct interface *ifp2 = ifnet; + register struct interface *ifprev = ifnet; + + dst = &ifp->int_dstaddr; + + /* Search for interfaces with the same net */ + ifp->int_sq.n = ifp->int_sq.p = &(ifp->int_sq); + match = afswitch[dst->sa_family].af_netmatch; + if (match) + for (ifp2 = ifnet; ifp2; ifp2 =ifp2->int_next) { + if (ifp->int_flags & IFF_POINTOPOINT == 0) + continue; + if ((*match)(&ifp2->int_dstaddr,&ifp->int_dstaddr)) { + insque(&ifp2->int_sq,&ifp->int_sq); + break; + } + } + } else { + dst = &ifp->int_broadaddr; + } + rt = rtlookup(dst); + if (rt) + rtdelete(rt); + if (tracing) + fprintf(stderr, "Adding route to interface %s\n", ifp->int_name); + if (ifp->int_transitions++ > 0) + syslog(LOG_ERR, "re-installing interface %s", ifp->int_name); + rtadd(dst, &ifp->int_addr, ifp->int_metric, + ifp->int_flags & (IFF_INTERFACE|IFF_PASSIVE|IFF_REMOTE)); +} + diff --git a/sbin/XNSrouted/table.h b/sbin/XNSrouted/table.h new file mode 100644 index 0000000000..159dc4e642 --- /dev/null +++ b/sbin/XNSrouted/table.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)table.h 5.1 (Berkeley) 6/4/85 (routed/table.h) + * + * @(#)table.h 5.3 (Berkeley) 6/1/90 + */ + +/* + * Routing table management daemon. + */ + +/* + * Routing table structure; differs a bit from kernel tables. + * + * Note: the union below must agree in the first 4 members + * so the ioctl's will work. + */ +struct rthash { + struct rt_entry *rt_forw; + struct rt_entry *rt_back; +}; + +#ifdef RTM_ADD +#define rtentry ortentry +#endif + +struct rt_entry { + struct rt_entry *rt_forw; + struct rt_entry *rt_back; + union { + struct rtentry rtu_rt; + struct { + u_long rtu_hash; + struct sockaddr rtu_dst; + struct sockaddr rtu_router; + short rtu_flags; + short rtu_state; + int rtu_timer; + int rtu_metric; + struct interface *rtu_ifp; + } rtu_entry; + } rt_rtu; +}; + +#define rt_rt rt_rtu.rtu_rt /* pass to ioctl */ +#define rt_hash rt_rtu.rtu_entry.rtu_hash /* for net or host */ +#define rt_dst rt_rtu.rtu_entry.rtu_dst /* match value */ +#define rt_router rt_rtu.rtu_entry.rtu_router /* who to forward to */ +#define rt_flags rt_rtu.rtu_entry.rtu_flags /* kernel flags */ +#define rt_timer rt_rtu.rtu_entry.rtu_timer /* for invalidation */ +#define rt_state rt_rtu.rtu_entry.rtu_state /* see below */ +#define rt_metric rt_rtu.rtu_entry.rtu_metric /* cost of route */ +#define rt_ifp rt_rtu.rtu_entry.rtu_ifp /* interface to take */ + +#define ROUTEHASHSIZ 32 /* must be a power of 2 */ +#define ROUTEHASHMASK (ROUTEHASHSIZ - 1) + +/* + * "State" of routing table entry. + */ +#define RTS_CHANGED 0x1 /* route has been altered recently */ +#define RTS_PASSIVE IFF_PASSIVE /* don't time out route */ +#define RTS_INTERFACE IFF_INTERFACE /* route is for network interface */ +#define RTS_REMOTE IFF_REMOTE /* route is for ``remote'' entity */ + +struct rthash nethash[ROUTEHASHSIZ]; +struct rthash hosthash[ROUTEHASHSIZ]; +struct rt_entry *rtlookup(); +struct rt_entry *rtfind(); diff --git a/sbin/XNSrouted/tables.c b/sbin/XNSrouted/tables.c new file mode 100644 index 0000000000..38ce4386fe --- /dev/null +++ b/sbin/XNSrouted/tables.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)tables.c 5.9 (Berkeley) 6/1/90"; +#endif /* not lint */ + +/* + * Routing Table Management Daemon + */ +#include "defs.h" +#include +#include + +#ifndef DEBUG +#define DEBUG 0 +#endif + +extern char *xns_ntoa(); +#define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));} + +int install = !DEBUG; /* if 1 call kernel */ +int delete = 1; +/* + * Lookup dst in the tables for an exact match. + */ +struct rt_entry * +rtlookup(dst) + struct sockaddr *dst; +{ + register struct rt_entry *rt; + register struct rthash *rh; + register u_int hash; + struct afhash h; + int doinghost = 1; + + if (dst->sa_family >= AF_MAX) + return (0); + (*afswitch[dst->sa_family].af_hash)(dst, &h); + hash = h.afh_hosthash; + rh = &hosthash[hash & ROUTEHASHMASK]; +again: + for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { + if (rt->rt_hash != hash) + continue; + if (equal(&rt->rt_dst, dst)) + return (rt); + } + if (doinghost) { + doinghost = 0; + hash = h.afh_nethash; + rh = &nethash[hash & ROUTEHASHMASK]; + goto again; + } + return (0); +} + +/* + * Find a route to dst as the kernel would. + */ +struct rt_entry * +rtfind(dst) + struct sockaddr *dst; +{ + register struct rt_entry *rt; + register struct rthash *rh; + register u_int hash; + struct afhash h; + int af = dst->sa_family; + int doinghost = 1, (*match)(); + + if (af >= AF_MAX) + return (0); + (*afswitch[af].af_hash)(dst, &h); + hash = h.afh_hosthash; + rh = &hosthash[hash & ROUTEHASHMASK]; + +again: + for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { + if (rt->rt_hash != hash) + continue; + if (doinghost) { + if (equal(&rt->rt_dst, dst)) + return (rt); + } else { + if (rt->rt_dst.sa_family == af && + (*match)(&rt->rt_dst, dst)) + return (rt); + } + } + if (doinghost) { + doinghost = 0; + hash = h.afh_nethash; + rh = &nethash[hash & ROUTEHASHMASK]; + match = afswitch[af].af_netmatch; + goto again; + } + return (0); +} + +rtadd(dst, gate, metric, state) + struct sockaddr *dst, *gate; + int metric, state; +{ + struct afhash h; + register struct rt_entry *rt; + struct rthash *rh; + int af = dst->sa_family, flags; + u_int hash; + + FIXLEN(dst); + FIXLEN(gate); + if (af >= AF_MAX) + return; + (*afswitch[af].af_hash)(dst, &h); + flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0; + if (flags & RTF_HOST) { + hash = h.afh_hosthash; + rh = &hosthash[hash & ROUTEHASHMASK]; + } else { + hash = h.afh_nethash; + rh = &nethash[hash & ROUTEHASHMASK]; + } + rt = (struct rt_entry *)malloc(sizeof (*rt)); + if (rt == 0) + return; + rt->rt_hash = hash; + rt->rt_dst = *dst; + rt->rt_router = *gate; + rt->rt_metric = metric; + rt->rt_timer = 0; + rt->rt_flags = RTF_UP | flags; + rt->rt_state = state | RTS_CHANGED; + rt->rt_ifp = if_ifwithnet(&rt->rt_router); + if (metric) + rt->rt_flags |= RTF_GATEWAY; + insque(rt, rh); + TRACE_ACTION(ADD, rt); + /* + * If the ioctl fails because the gateway is unreachable + * from this host, discard the entry. This should only + * occur because of an incorrect entry in /etc/gateways. + */ + if (install && ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) { + if (errno != EEXIST) + perror("SIOCADDRT"); + if (errno == ENETUNREACH) { + TRACE_ACTION(DELETE, rt); + remque(rt); + free((char *)rt); + } + } +} + +rtchange(rt, gate, metric) + struct rt_entry *rt; + struct sockaddr *gate; + short metric; +{ + int doioctl = 0, metricchanged = 0; + struct rtentry oldroute; + + FIXLEN(gate); + if (!equal(&rt->rt_router, gate)) + doioctl++; + if (metric != rt->rt_metric) + metricchanged++; + if (doioctl || metricchanged) { + TRACE_ACTION(CHANGE FROM, rt); + if (doioctl) { + oldroute = rt->rt_rt; + rt->rt_router = *gate; + } + rt->rt_metric = metric; + if ((rt->rt_state & RTS_INTERFACE) && metric) { + rt->rt_state &= ~RTS_INTERFACE; + syslog(LOG_ERR, + "changing route from interface %s (timed out)", + rt->rt_ifp->int_name); + } + if (metric) + rt->rt_flags |= RTF_GATEWAY; + else + rt->rt_flags &= ~RTF_GATEWAY; + rt->rt_state |= RTS_CHANGED; + TRACE_ACTION(CHANGE TO, rt); + } + if (doioctl && install) { +#ifndef RTM_ADD + if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) + syslog(LOG_ERR, "SIOCADDRT dst %s, gw %s: %m", + xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr), + xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr)); + if (delete && ioctl(s, SIOCDELRT, (char *)&oldroute) < 0) + perror("SIOCDELRT"); +#else + if (delete && ioctl(s, SIOCDELRT, (char *)&oldroute) < 0) + perror("SIOCDELRT"); + if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) + syslog(LOG_ERR, "SIOCADDRT dst %s, gw %s: %m", + xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr), + xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr)); +#endif + } +} + +rtdelete(rt) + struct rt_entry *rt; +{ + + struct sockaddr *sa = &(rt->rt_rt.rt_gateway); + FIXLEN(sa); +#undef rt_dst + sa = &(rt->rt_rt.rt_dst); + FIXLEN(sa); + if (rt->rt_state & RTS_INTERFACE) { + syslog(LOG_ERR, "deleting route to interface %s (timed out)", + rt->rt_ifp->int_name); + } + TRACE_ACTION(DELETE, rt); + if (install && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt)) + perror("SIOCDELRT"); + remque(rt); + free((char *)rt); +} + +rtinit() +{ + register struct rthash *rh; + + for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) + rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; + for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++) + rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; +} diff --git a/sbin/XNSrouted/timer.c b/sbin/XNSrouted/timer.c new file mode 100644 index 0000000000..f420bc074e --- /dev/null +++ b/sbin/XNSrouted/timer.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)timer.c 5.7 (Berkeley) 2/26/91"; +#endif /* not lint */ + +/* + * Routing Table Management Daemon + */ +#include "defs.h" + +int timeval = -TIMER_RATE; + +/* + * Timer routine. Performs routing information supply + * duties and manages timers on routing table entries. + */ +void +timer() +{ + register struct rthash *rh; + register struct rt_entry *rt; + struct rthash *base = hosthash; + int doinghost = 1, timetobroadcast; + + timeval += TIMER_RATE; + if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0) + ifinit(); + timetobroadcast = supplier && (timeval % SUPPLY_INTERVAL) == 0; +again: + for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) { + rt = rh->rt_forw; + for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { + /* + * We don't advance time on a routing entry for + * a passive gateway or that for our only interface. + * The latter is excused because we don't act as + * a routing information supplier and hence would + * time it out. This is fair as if it's down + * we're cut off from the world anyway and it's + * not likely we'll grow any new hardware in + * the mean time. + */ + if (!(rt->rt_state & RTS_PASSIVE) && + (supplier || !(rt->rt_state & RTS_INTERFACE))) + rt->rt_timer += TIMER_RATE; + if (rt->rt_timer >= EXPIRE_TIME) + rt->rt_metric = HOPCNT_INFINITY; + if (rt->rt_timer >= GARBAGE_TIME) { + rt = rt->rt_back; + /* Perhaps we should send a REQUEST for this route? */ + rtdelete(rt->rt_forw); + continue; + } + if (rt->rt_state & RTS_CHANGED) { + rt->rt_state &= ~RTS_CHANGED; + /* don't send extraneous packets */ + if (!supplier || timetobroadcast) + continue; + msg->rip_cmd = htons(RIPCMD_RESPONSE); + msg->rip_nets[0].rip_dst = + (satons_addr(rt->rt_dst)).x_net; + msg->rip_nets[0].rip_metric = + htons(min(rt->rt_metric+1, HOPCNT_INFINITY)); + toall(sndmsg); + } + } + } + if (doinghost) { + doinghost = 0; + base = nethash; + goto again; + } + if (timetobroadcast) + toall(supply); + alarm(TIMER_RATE); +} + +/* + * On hangup, let everyone know we're going away. + */ +void +hup() +{ + register struct rthash *rh; + register struct rt_entry *rt; + struct rthash *base = hosthash; + int doinghost = 1; + + if (supplier) { +again: + for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) { + rt = rh->rt_forw; + for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) + rt->rt_metric = HOPCNT_INFINITY; + } + if (doinghost) { + doinghost = 0; + base = nethash; + goto again; + } + toall(supply); + } + exit(1); +} diff --git a/sbin/XNSrouted/tools/query.c b/sbin/XNSrouted/tools/query.c new file mode 100644 index 0000000000..2e6374d1f6 --- /dev/null +++ b/sbin/XNSrouted/tools/query.c @@ -0,0 +1,232 @@ +/*- + * Copyright (c) 1983, 1986 The Regents of the University of California. + * All rights reserved. + * + * This code includes software contributed to Berkeley by + * Bill Nesheim at Cornell University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1983, 1986 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)query.c 5.8 (Berkeley) 4/16/91"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../protocol.h" +#define IDPPORT_RIF 1 + +#define WTIME 5 /* Time to wait for responses */ + +int s; +int timedout, timeout(); +char packet[MAXPACKETSIZE]; +extern int errno; +struct sockaddr_ns myaddr = {sizeof(myaddr), AF_NS}; +char *ns_ntoa(); +struct ns_addr ns_addr(); +main(argc, argv) +int argc; +char *argv[]; +{ + int cc, count, bits; + struct sockaddr from; + int fromlen = sizeof(from); + struct timeval notime; + + if (argc < 2) { + printf("usage: query hosts...\n"); + exit(1); + } + s = getsocket(SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + exit(2); + } + + argv++, argc--; + query(argv,argc); + + /* + * Listen for returning packets; + * may be more than one packet per host. + */ + bits = 1 << s; + bzero(¬ime, sizeof(notime)); + signal(SIGALRM, timeout); + alarm(WTIME); + while (!timedout || + select(20, &bits, 0, 0, ¬ime) > 0) { + struct nspacket { + struct idp hdr; + char data[512]; + } response; + cc = recvfrom(s, &response, sizeof (response), 0, + &from, &fromlen); + if (cc <= 0) { + if (cc < 0) { + if (errno == EINTR) + continue; + perror("recvfrom"); + (void) close(s); + exit(1); + } + continue; + } + rip_input(&from, response.data, cc); + count--; + } +} +static struct sockaddr_ns router = {sizeof(myaddr), AF_NS}; +static struct ns_addr zero_addr; +static short allones[] = {-1, -1, -1}; + +query(argv,argc) +char **argv; +{ + register struct rip *msg = (struct rip *)packet; + char *host = *argv; + int flags = 0; + struct ns_addr specific; + + if (bcmp(*argv, "-r", 3) == 0) { + flags = MSG_DONTROUTE; argv++; argc--; + } + host = *argv; + router.sns_addr = ns_addr(host); + router.sns_addr.x_port = htons(IDPPORT_RIF); + if (ns_hosteq(zero_addr, router.sns_addr)) { + router.sns_addr.x_host = *(union ns_host *) allones; + } + msg->rip_cmd = htons(RIPCMD_REQUEST); + msg->rip_nets[0].rip_dst = *(union ns_net *) allones; + msg->rip_nets[0].rip_metric = htons(HOPCNT_INFINITY); + if (argc > 0) { + specific = ns_addr(*argv); + msg->rip_nets[0].rip_dst = specific.x_net; + specific.x_host = zero_addr.x_host; + specific.x_port = zero_addr.x_port; + printf("Net asked for was %s\n", ns_ntoa(specific)); + } + if (sendto(s, packet, sizeof (struct rip), flags, + &router, sizeof(router)) < 0) + perror(host); +} + +/* + * Handle an incoming routing packet. + */ +rip_input(from, msg, size) + struct sockaddr_ns *from; + register struct rip *msg; + int size; +{ + struct netinfo *n; + char *name; + int lna, net, subnet; + struct hostent *hp; + struct netent *np; + static struct ns_addr work; + + if (htons(msg->rip_cmd) != RIPCMD_RESPONSE) + return; + printf("from %s\n", ns_ntoa(from->sns_addr)); + size -= sizeof (struct idp); + size -= sizeof (short); + n = msg->rip_nets; + while (size > 0) { + union ns_net_u net; + if (size < sizeof (struct netinfo)) + break; + net.net_e = n->rip_dst; + printf("\t%d, metric %d\n", ntohl(net.long_e), + ntohs(n->rip_metric)); + size -= sizeof (struct netinfo), n++; + } +} + +timeout() +{ + timedout = 1; +} +getsocket(type, proto) + int type, proto; +{ + struct sockaddr_ns *sns = &myaddr; + int domain = sns->sns_family; + int retry, s, on = 1; + + retry = 1; + while ((s = socket(domain, type, proto)) < 0 && retry) { + perror("socket"); + sleep(5 * retry); + retry <<= 1; + } + if (retry == 0) + return (-1); + while (bind(s, sns, sizeof (*sns), 0) < 0 && retry) { + perror("bind"); + sleep(5 * retry); + retry <<= 1; + } + if (retry == 0) + return (-1); + if (domain==AF_NS) { + struct idp idp; + if (setsockopt(s, 0, SO_HEADERS_ON_INPUT, &on, sizeof(on))) { + perror("setsockopt SEE HEADERS"); + exit(1); + } + idp.idp_pt = NSPROTO_RI; + if (setsockopt(s, 0, SO_DEFAULT_HEADERS, &idp, sizeof(idp))) { + perror("setsockopt SET HEADERS"); + exit(1); + } + } + if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) { + perror("setsockopt SO_BROADCAST"); + exit(1); + } + return (s); +} diff --git a/sbin/XNSrouted/trace.c b/sbin/XNSrouted/trace.c new file mode 100644 index 0000000000..51728733a3 --- /dev/null +++ b/sbin/XNSrouted/trace.c @@ -0,0 +1,313 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)trace.c 5.11 (Berkeley) 2/26/91"; +#endif /* not lint */ + +/* + * Routing Table Management Daemon + */ +#define RIPCMDS +#include +#include "defs.h" + +#define NRECORDS 50 /* size of circular trace buffer */ +#ifdef DEBUG +FILE *ftrace = stdout; +int tracing = 1; +#else DEBUG +FILE *ftrace = NULL; +int tracing = 0; +#endif + +char *xns_ntoa(); + +traceinit(ifp) + register struct interface *ifp; +{ + static int iftraceinit(); + + if (iftraceinit(ifp, &ifp->int_input) && + iftraceinit(ifp, &ifp->int_output)) + return; + tracing = 0; + syslog(LOG_ERR, "traceinit: can't init %s\n", ifp->int_name); +} + +static +iftraceinit(ifp, ifd) + struct interface *ifp; + register struct ifdebug *ifd; +{ + register struct iftrace *t; + + ifd->ifd_records = + (struct iftrace *)malloc(NRECORDS * sizeof (struct iftrace)); + if (ifd->ifd_records == 0) + return (0); + ifd->ifd_front = ifd->ifd_records; + ifd->ifd_count = 0; + for (t = ifd->ifd_records; t < ifd->ifd_records + NRECORDS; t++) { + t->ift_size = 0; + t->ift_packet = 0; + } + ifd->ifd_if = ifp; + return (1); +} + +traceon(file) + char *file; +{ + + if (ftrace != NULL) + return; + ftrace = fopen(file, "a"); + if (ftrace == NULL) + return; + dup2(fileno(ftrace), 1); + dup2(fileno(ftrace), 2); + tracing = 1; +} + +traceoff() +{ + if (!tracing) + return; + if (ftrace != NULL) + fclose(ftrace); + ftrace = NULL; + tracing = 0; +} + +trace(ifd, who, p, len, m) + register struct ifdebug *ifd; + struct sockaddr *who; + char *p; + int len, m; +{ + register struct iftrace *t; + + if (ifd->ifd_records == 0) + return; + t = ifd->ifd_front++; + if (ifd->ifd_front >= ifd->ifd_records + NRECORDS) + ifd->ifd_front = ifd->ifd_records; + if (ifd->ifd_count < NRECORDS) + ifd->ifd_count++; + if (t->ift_size > 0 && t->ift_packet) + free(t->ift_packet); + t->ift_packet = 0; + t->ift_stamp = time(0); + t->ift_who = *who; + if (len > 0) { + t->ift_packet = malloc(len); + if (t->ift_packet) + bcopy(p, t->ift_packet, len); + else + len = 0; + } + t->ift_size = len; + t->ift_metric = m; +} + +traceaction(fd, action, rt) + FILE *fd; + char *action; + struct rt_entry *rt; +{ + struct sockaddr_ns *dst, *gate; + static struct bits { + int t_bits; + char *t_name; + } flagbits[] = { + { RTF_UP, "UP" }, + { RTF_GATEWAY, "GATEWAY" }, + { RTF_HOST, "HOST" }, + { 0 } + }, statebits[] = { + { RTS_PASSIVE, "PASSIVE" }, + { RTS_REMOTE, "REMOTE" }, + { RTS_INTERFACE,"INTERFACE" }, + { RTS_CHANGED, "CHANGED" }, + { 0 } + }; + register struct bits *p; + register int first; + char *cp; + struct interface *ifp; + + if (fd == NULL) + return; + fprintf(fd, "%s ", action); + dst = (struct sockaddr_ns *)&rt->rt_dst; + gate = (struct sockaddr_ns *)&rt->rt_router; + fprintf(fd, "dst %s, ", xns_ntoa(&dst->sns_addr)); + fprintf(fd, "router %s, metric %d, flags", + xns_ntoa(&gate->sns_addr), rt->rt_metric); + cp = " %s"; + for (first = 1, p = flagbits; p->t_bits > 0; p++) { + if ((rt->rt_flags & p->t_bits) == 0) + continue; + fprintf(fd, cp, p->t_name); + if (first) { + cp = "|%s"; + first = 0; + } + } + fprintf(fd, " state"); + cp = " %s"; + for (first = 1, p = statebits; p->t_bits > 0; p++) { + if ((rt->rt_state & p->t_bits) == 0) + continue; + fprintf(fd, cp, p->t_name); + if (first) { + cp = "|%s"; + first = 0; + } + } + putc('\n', fd); + if (!tracepackets && (rt->rt_state & RTS_PASSIVE) == 0 && rt->rt_ifp) + dumpif(fd, rt->rt_ifp); + fflush(fd); +} + +dumpif(fd, ifp) + register struct interface *ifp; + FILE *fd; +{ + if (ifp->int_input.ifd_count || ifp->int_output.ifd_count) { + fprintf(fd, "*** Packet history for interface %s ***\n", + ifp->int_name); + dumptrace(fd, "to", &ifp->int_output); + dumptrace(fd, "from", &ifp->int_input); + fprintf(fd, "*** end packet history ***\n"); + } +} + +dumptrace(fd, dir, ifd) + FILE *fd; + char *dir; + register struct ifdebug *ifd; +{ + register struct iftrace *t; + char *cp = !strcmp(dir, "to") ? "Output" : "Input"; + + if (ifd->ifd_front == ifd->ifd_records && + ifd->ifd_front->ift_size == 0) { + fprintf(fd, "%s: no packets.\n", cp); + return; + } + fprintf(fd, "%s trace:\n", cp); + t = ifd->ifd_front - ifd->ifd_count; + if (t < ifd->ifd_records) + t += NRECORDS; + for ( ; ifd->ifd_count; ifd->ifd_count--, t++) { + if (t >= ifd->ifd_records + NRECORDS) + t = ifd->ifd_records; + if (t->ift_size == 0) + continue; + fprintf(fd, "%.24s: metric=%d\n", ctime(&t->ift_stamp), + t->ift_metric); + dumppacket(fd, dir, &t->ift_who, t->ift_packet, t->ift_size); + } +} + +dumppacket(fd, dir, who, cp, size) + FILE *fd; + struct sockaddr_ns *who; /* should be sockaddr */ + char *dir, *cp; + register int size; +{ + register struct rip *msg = (struct rip *)cp; + register struct netinfo *n; + char *xns_nettoa(); + + if (msg->rip_cmd && ntohs(msg->rip_cmd) < RIPCMD_MAX) + fprintf(fd, "%s %s %s#%x", ripcmds[ntohs(msg->rip_cmd)], + dir, xns_ntoa(&who->sns_addr), ntohs(who->sns_addr.x_port)); + else { + fprintf(fd, "Bad cmd 0x%x %s %s#%x\n", ntohs(msg->rip_cmd), + dir, xns_ntoa(&who->sns_addr), ntohs(who->sns_addr.x_port)); + fprintf(fd, "size=%d cp=%x packet=%x\n", size, cp, packet); + return; + } + switch (ntohs(msg->rip_cmd)) { + + case RIPCMD_REQUEST: + case RIPCMD_RESPONSE: + fprintf(fd, ":\n"); + size -= sizeof (u_short); + n = msg->rip_nets; + for (; size > 0; n++, size -= sizeof (struct netinfo)) { + if (size < sizeof (struct netinfo)) + break; + fprintf(fd, "\tnet %s metric %d\n", + xns_nettoa(n->rip_dst), + ntohs(n->rip_metric)); + } + break; + + } +} + +union ns_net_u net; + +char * +xns_nettoa(val) +union ns_net val; +{ + static char buf[100]; + net.net_e = val; + (void)sprintf(buf, "%lx", ntohl(net.long_e)); + return (buf); +} + + +char * +xns_ntoa(addr) +struct ns_addr *addr; +{ + static char buf[100]; + + (void)sprintf(buf, "%s#%x:%x:%x:%x:%x:%x", + xns_nettoa(addr->x_net), + addr->x_host.c_host[0], addr->x_host.c_host[1], + addr->x_host.c_host[2], addr->x_host.c_host[3], + addr->x_host.c_host[4], addr->x_host.c_host[5]); + + return(buf); +} diff --git a/sbin/XNSrouted/trace.h b/sbin/XNSrouted/trace.h new file mode 100644 index 0000000000..be48553d6c --- /dev/null +++ b/sbin/XNSrouted/trace.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)trace.h 5.6 (Berkeley) 6/1/90 + */ + +/* + * Xerox Routing Information Protocol. + */ + +/* + * Trace record format. + */ +struct iftrace { + time_t ift_stamp; /* time stamp */ + struct sockaddr ift_who; /* from/to */ + char *ift_packet; /* pointer to packet */ + short ift_size; /* size of packet */ + short ift_metric; /* metric */ +}; + +/* + * Per interface packet tracing buffers. An incoming and + * outgoing circular buffer of packets is maintained, per + * interface, for debugging. Buffers are dumped whenever + * an interface is marked down. + */ +struct ifdebug { + struct iftrace *ifd_records; /* array of trace records */ + struct iftrace *ifd_front; /* next empty trace record */ + int ifd_count; /* number of unprinted records */ + struct interface *ifd_if; /* for locating stuff */ +}; + +/* + * Packet tracing stuff. + */ +int tracepackets; /* watch packets as they go by */ +int tracing; /* on/off */ +FILE *ftrace; /* output trace file */ + +#define TRACE_ACTION(action, route) { \ + if (tracing) \ + traceaction(ftrace, "action", route); \ + } +#define TRACE_INPUT(ifp, src, size) { \ + if (tracing) { \ + ifp = if_iflookup(src); \ + if (ifp) \ + trace(&ifp->int_input, src, &packet[sizeof(struct idp)], size, \ + ntohl(ifp->int_metric)); \ + } \ + if (tracepackets && ftrace) \ + dumppacket(ftrace, "from", src, &packet[sizeof(struct idp)], size); \ + } +#define TRACE_OUTPUT(ifp, dst, size) { \ + if (tracing) { \ + ifp = if_iflookup(dst); \ + if (ifp) \ + trace(&ifp->int_output, dst, &packet[sizeof(struct idp)], size, ifp->int_metric); \ + } \ + if (tracepackets && ftrace) \ + dumppacket(ftrace, "to", dst, &packet[sizeof(struct idp)], size); \ + } diff --git a/sbin/badsect/Makefile b/sbin/badsect/Makefile index f58b9f7e12..0f738854cb 100644 --- a/sbin/badsect/Makefile +++ b/sbin/badsect/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= badsect -MAN8= badsect.0 +MAN8= badsect.8 .include diff --git a/sbin/disklabel/Makefile b/sbin/disklabel/Makefile index f38e583b6d..13dfceed10 100644 --- a/sbin/disklabel/Makefile +++ b/sbin/disklabel/Makefile @@ -2,15 +2,9 @@ PROG= disklabel SRCS= disklabel.c dkcksum.c -MAN8= disklabel.0 BINGRP=operator BINMODE=2550 -CLEANFILES=disklabel.5.0 - -all: disklabel.5.0 - -beforeinstall: - install -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} disklabel.5.0 \ - ${DESTDIR}${MANDIR}5/disklabel.0 +MAN5= disklabel.5 +MAN8= disklabel.8 .include diff --git a/sbin/disklabel/disklabel.5.5 b/sbin/disklabel/disklabel.5 similarity index 100% rename from sbin/disklabel/disklabel.5.5 rename to sbin/disklabel/disklabel.5 diff --git a/sbin/disklabel/disklabel.c b/sbin/disklabel/disklabel.c index af840b5049..801dbc399a 100644 --- a/sbin/disklabel/disklabel.c +++ b/sbin/disklabel/disklabel.c @@ -521,6 +521,7 @@ readmbr(f) */ dp = dos_partitions; npart = njunk = nboot = 0; + bsdp = NULL; for (i = 0; i < NDOSPART; i++, dp++) { if (dp->dp_flag != 0x80 && dp->dp_flag != 0) njunk++; else diff --git a/sbin/dmesg/Makefile b/sbin/dmesg/Makefile index ba29d5626a..3b0736a638 100644 --- a/sbin/dmesg/Makefile +++ b/sbin/dmesg/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.6 (Berkeley) 5/2/91 PROG= dmesg -MAN8= dmesg.0 +MAN8= dmesg.8 BINGRP= kmem BINMODE=2555 LDADD= -lutil diff --git a/sbin/dmesg/dmesg.8 b/sbin/dmesg/dmesg.8 index b90ec05e19..219d966fe2 100644 --- a/sbin/dmesg/dmesg.8 +++ b/sbin/dmesg/dmesg.8 @@ -52,7 +52,7 @@ Extract values associated with the name list from the specified core instead of the default ``/dev/kmem''. .It Fl N Extract the name list from the specified system instead of the default -``/vmunix''. +``/386bsd''. .El .Sh SEE ALSO .Xr syslogd 8 diff --git a/sbin/dump/Makefile b/sbin/dump/Makefile index 1f7c162730..e20aa904e3 100644 --- a/sbin/dump/Makefile +++ b/sbin/dump/Makefile @@ -1,4 +1,6 @@ # @(#)Makefile 5.12 (Berkeley) 3/7/91 +# +# $Header$ # dump.h header file # dumpitime.c reads /etc/dumpdates @@ -17,13 +19,13 @@ SRCS= dumpitime.c dumpmain.c dumpoptr.c dumptape.c dumptraverse.c \ ROBJS= dumpitime.o dumprmain.o dumpoptr.o dumprmt.o dumprtape.o \ dumptraverse.o unctime.o BINGRP= tty -MAN8= dump.0 rdump.0 +MAN8= dump.8 rdump.8 CLEANFILES=dumprmain.o dumprtape.o dumprmt.o rdump all: rdump rdump: ${ROBJS} ${LIBC} - ${CC} -o ${.TARGET} ${CFLAGS} ${ROBJS} + ${CC} ${LDFLAGS} -o ${.TARGET} ${ROBJS} ${LDADD} dumprmain.o: dumpmain.o ${CC} -c -DRDUMP ${CFLAGS} ${.CURDIR}/dumpmain.c -o ${.TARGET} @@ -32,7 +34,7 @@ dumprtape.o: dumptape.o ${CC} -c -DRDUMP ${CFLAGS} ${.CURDIR}/dumptape.c -o ${.TARGET} afterinstall: - install ${STRIP} -o root -g ${BINGRP} -m 6555 rdump \ + install ${COPY} ${STRIP} -o root -g ${BINGRP} -m 6555 rdump \ ${DESTDIR}${BINDIR} .include diff --git a/sbin/dump/dump.8 b/sbin/dump/dump.8 index 976008a171..638f4819cd 100644 --- a/sbin/dump/dump.8 +++ b/sbin/dump/dump.8 @@ -31,6 +31,8 @@ .\" .\" @(#)dump.8 6.8 (Berkeley) 6/17/91 .\" +.\" $Header: /b/source/CVS/src/sbin/dump/dump.8,v 1.3 1993/03/23 00:27:01 cgd Exp $ +.\" .Dd June 17, 1991 .Dt DUMP 8 .Os BSD 4 diff --git a/sbin/dump/dump.h b/sbin/dump/dump.h index 6186030e66..dfd9c18cdb 100644 --- a/sbin/dump/dump.h +++ b/sbin/dump/dump.h @@ -31,6 +31,8 @@ * SUCH DAMAGE. * * @(#)dump.h 5.16 (Berkeley) 5/29/91 + * + * $Header: /b/source/CVS/src/sbin/dump/dump.h,v 1.3 1993/03/23 00:27:03 cgd Exp $ */ #define MAXINOPB (MAXBSIZE / sizeof(struct dinode)) diff --git a/sbin/dump/dumpitime.c b/sbin/dump/dumpitime.c index dc038613f9..e38c5ebacb 100644 --- a/sbin/dump/dumpitime.c +++ b/sbin/dump/dumpitime.c @@ -33,6 +33,7 @@ #ifndef lint static char sccsid[] = "@(#)dumpitime.c 5.7 (Berkeley) 3/7/91"; +static char rcsid[] = "$Header: /b/source/CVS/src/sbin/dump/dumpitime.c,v 1.3 1993/03/23 00:27:05 cgd Exp $"; #endif /* not lint */ #include diff --git a/sbin/dump/dumpmain.c b/sbin/dump/dumpmain.c index 402a540ba6..eb43e684fd 100644 --- a/sbin/dump/dumpmain.c +++ b/sbin/dump/dumpmain.c @@ -39,6 +39,7 @@ char copyright[] = #ifndef lint static char sccsid[] = "@(#)dumpmain.c 5.16 (Berkeley) 4/24/91"; +static char rcsid[] = "$Header: /a/cvs/386BSD/src/sbin/dump/dumpmain.c,v 1.2 1993/07/22 16:49:16 jkh Exp $"; #endif /* not lint */ #include @@ -71,6 +72,7 @@ long dev_bsize = 1; /* recalculated below */ long blocksperfile; /* output blocks per file */ #ifdef RDUMP char *host; +char *user; int rmthost(); #endif @@ -99,7 +101,7 @@ main(argc, argv) level = '0'; argv++; argc -= 2; - for (cp = *argv++; *cp; cp++) { + for (cp = *argv++; cp && *cp; cp++) { switch (*cp) { case '-': continue; @@ -244,14 +246,21 @@ main(argc, argv) #ifdef RDUMP { char *index(); + user = tape; + host = index(user, '@'); + if(host == 0) { + user = NULL; host = tape; + } + else + *host++ = 0; tape = index(host, ':'); if (tape == 0) { msg("need keyletter ``f'' and device ``host:tape''\n"); exit(1); } *tape++ = 0; - if (rmthost(host) == 0) + if (rmthost(host,user) == 0) exit(X_ABORT); } setuid(getuid()); /* rmthost() is the only reason to be setuid */ diff --git a/sbin/dump/dumpoptr.c b/sbin/dump/dumpoptr.c index b9a0739842..7731b815bb 100644 --- a/sbin/dump/dumpoptr.c +++ b/sbin/dump/dumpoptr.c @@ -33,6 +33,7 @@ #ifndef lint static char sccsid[] = "@(#)dumpoptr.c 5.8 (Berkeley) 3/7/91"; +static char rcsid[] = "$Header: /b/source/CVS/src/sbin/dump/dumpoptr.c,v 1.4 1993/04/20 09:30:45 mycroft Exp $"; #endif /* not lint */ #include @@ -468,10 +469,12 @@ fstabsearch(key) char *rawname(); for (pf = table; pf != NULL; pf = pf->pf_next) { + char *disk; fs = pf->pf_fstab; if (strcmp(fs->fs_file, key) == 0 || strcmp(fs->fs_spec, key) == 0 || - strcmp(rawname(fs->fs_spec), key) == 0) + ((disk = rawname(fs->fs_spec)) && + strcmp(disk, key) == 0)) return (fs); if (key[0] != '/') { if (*fs->fs_spec == '/' && diff --git a/sbin/dump/dumprmt.c b/sbin/dump/dumprmt.c index 316813f482..6cdd03e56a 100644 --- a/sbin/dump/dumprmt.c +++ b/sbin/dump/dumprmt.c @@ -33,6 +33,7 @@ #ifndef lint static char sccsid[] = "@(#)dumprmt.c 5.11 (Berkeley) 3/7/91"; +static char rcsid[] = "$Header: /a/cvs/386BSD/src/sbin/dump/dumprmt.c,v 1.2 1993/07/22 16:49:18 jkh Exp $"; #endif /* not lint */ #include @@ -67,16 +68,19 @@ int rmtgetb(); void rmtgets(); int rmtcall(); char *rmtpeer; +char *rmtuser; extern int ntrec; /* blocking factor on tape */ extern void msg(); int -rmthost(host) +rmthost(host, user) char *host; + char *user; { rmtpeer = host; + rmtuser = user; signal(SIGPIPE, rmtconnaborted); rmtgetconn(); if (rmtape < 0) @@ -110,7 +114,7 @@ rmtgetconn() pw = getpwuid(getuid()); if (pw && pw->pw_name) name = pw->pw_name; - rmtape = rcmd(&rmtpeer, sp->s_port, name, name, _PATH_RMT, 0); + rmtape = rcmd(&rmtpeer, sp->s_port, name, rmtuser ? rmtuser : name, _PATH_RMT, 0); size = ntrec * TP_BSIZE; while (size > TP_BSIZE && setsockopt(rmtape, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) < 0) diff --git a/sbin/dump/dumptape.c b/sbin/dump/dumptape.c index 6a8144a583..338fa16a10 100644 --- a/sbin/dump/dumptape.c +++ b/sbin/dump/dumptape.c @@ -33,6 +33,7 @@ #ifndef lint static char sccsid[] = "@(#)dumptape.c 5.18 (Berkeley) 4/24/91"; +static char rcsid[] = "$Header: /b/source/CVS/src/sbin/dump/dumptape.c,v 1.3 1993/03/23 00:27:15 cgd Exp $"; #endif /* not lint */ #include diff --git a/sbin/dump/dumptraverse.c b/sbin/dump/dumptraverse.c index e181a73775..cb522f42ce 100644 --- a/sbin/dump/dumptraverse.c +++ b/sbin/dump/dumptraverse.c @@ -29,18 +29,11 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00110 - * -------------------- ----- ---------------------- - * - * 28 Mar 93 Keith Bostic Fix bug where max inode not mapped. - * */ #ifndef lint static char sccsid[] = "@(#)dumptraverse.c 5.11 (Berkeley) 3/7/91"; +static char rcsid[] = "$Header: /b/source/CVS/src/sbin/dump/dumptraverse.c,v 1.5 1993/06/13 21:08:27 mycroft Exp $"; #endif /* not lint */ #include @@ -114,7 +107,7 @@ mapfiles(maxino, tapesize) register struct dinode *dp; int anydirskipped = 0; - for (ino = 0; ino <= maxino; ino++) { /* PATCH 110 */ + for (ino = 0; ino <= maxino; ino++) { dp = getino(ino); if ((mode = (dp->di_mode & IFMT)) == 0) continue; @@ -316,6 +309,13 @@ dumpino(dp, ino) writeheader(ino); return; } + if (DFASTLINK(*dp)) { + spcl.c_addr[0] = 1; + spcl.c_count = 1; + writeheader(ino); + writerec(dp->di_symlink); + return; + } if (dp->di_size > NDADDR * sblock->fs_bsize) cnt = NDADDR * sblock->fs_frag; else diff --git a/sbin/dump/pathnames.h b/sbin/dump/pathnames.h index 5ebfd89e0e..3c90b77047 100644 --- a/sbin/dump/pathnames.h +++ b/sbin/dump/pathnames.h @@ -31,6 +31,8 @@ * SUCH DAMAGE. * * @(#)pathnames.h 5.6 (Berkeley) 2/28/91 + * + * $Header: /b/source/CVS/src/sbin/dump/pathnames.h,v 1.3 1993/03/23 00:27:20 cgd Exp $ */ #include diff --git a/sbin/dump/rdump.8 b/sbin/dump/rdump.8 index df3471bce9..bf19028aa3 100644 --- a/sbin/dump/rdump.8 +++ b/sbin/dump/rdump.8 @@ -31,6 +31,8 @@ .\" .\" @(#)rdump.8 6.3 (Berkeley) 3/16/91 .\" +.\" $Header: /a/cvs/386BSD/src/sbin/dump/rdump.8,v 1.2 1993/07/22 16:49:22 jkh Exp $ +.\" .Dd March 16, 1991 .Dt RDUMP 8 .Os BSD 4.2 @@ -56,7 +58,7 @@ except the key must be specified and the file .Ar argument supplied should be of the form -.Pa machine:device . +.Pa [user@]machine:device . .Pp .Nm Rdump creates a remote server, diff --git a/sbin/dump/unctime.c b/sbin/dump/unctime.c index 298366129e..d906fd7923 100644 --- a/sbin/dump/unctime.c +++ b/sbin/dump/unctime.c @@ -33,6 +33,7 @@ #ifndef lint static char sccsid[] = "@(#)unctime.c 5.4 (Berkeley) 3/7/91"; +static char rcsid[] = "$Header: /b/source/CVS/src/sbin/dump/unctime.c,v 1.3 1993/03/23 00:27:23 cgd Exp $"; #endif /* not lint */ #include diff --git a/sbin/dumpfs/Makefile b/sbin/dumpfs/Makefile index 5b261bf7d4..d4a3c06bdf 100644 --- a/sbin/dumpfs/Makefile +++ b/sbin/dumpfs/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= dumpfs -MAN8= dumpfs.0 +MAN8= dumpfs.8 .include diff --git a/sbin/fastboot/Makefile b/sbin/fastboot/Makefile index 385b3fe2c7..83222db040 100644 --- a/sbin/fastboot/Makefile +++ b/sbin/fastboot/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.6 (Berkeley) 6/23/90 -MAN8= fastboot.0 +MAN8= fastboot.8 MLINKS= fastboot.8 fasthalt.8 beforeinstall: diff --git a/sbin/fdisk/Makefile b/sbin/fdisk/Makefile index 38e3faddaa..78b1c9ac3d 100644 --- a/sbin/fdisk/Makefile +++ b/sbin/fdisk/Makefile @@ -4,6 +4,6 @@ PROG= fdisk SRCS= fdisk.c -MAN8= fdisk.0 +MAN8= fdisk.8 .include diff --git a/sbin/fdisk/fdisk.8 b/sbin/fdisk/fdisk.8 index 078ae5ea38..0c9fd82cc3 100644 --- a/sbin/fdisk/fdisk.8 +++ b/sbin/fdisk/fdisk.8 @@ -8,6 +8,7 @@ .Nm .Op Fl i .Op Fl u +.Op disk .Bl -tag -width time .It Fl i Initializes sector 0 of the disk. diff --git a/sbin/fsck/Makefile b/sbin/fsck/Makefile index 67fda56896..a9ca80c881 100644 --- a/sbin/fsck/Makefile +++ b/sbin/fsck/Makefile @@ -1,9 +1,11 @@ # @(#)Makefile 5.15 (Berkeley) 6/29/90 +# +# $Header: /a/cvs/386BSD/src/sbin/fsck/Makefile,v 1.3 1993/07/22 16:51:45 jkh Exp $ PROG= fsck -MAN8= fsck.0 +MAN8= fsck.8 SRCS= dir.c inode.c main.c pass1.c pass1b.c pass2.c pass3.c pass4.c \ pass5.c preen.c setup.c utilities.c ufs_subr.c ufs_tables.c -.PATH: ${.CURDIR}/../../sys.386bsd/ufs +.PATH: ${.CURDIR}/../../sys/ufs .include diff --git a/sbin/fsck/dir.c b/sbin/fsck/dir.c index 8bb4972b10..80b35bb238 100644 --- a/sbin/fsck/dir.c +++ b/sbin/fsck/dir.c @@ -32,7 +32,8 @@ */ #ifndef lint -static char sccsid[] = "@(#)dir.c 5.19 (Berkeley) 7/26/91"; +/*static char sccsid[] = "from: @(#)dir.c 5.19 (Berkeley) 7/26/91";*/ +static char rcsid[] = "$Id$"; #endif /* not lint */ #include @@ -41,6 +42,7 @@ static char sccsid[] = "@(#)dir.c 5.19 (Berkeley) 7/26/91"; #define KERNEL #include #undef KERNEL +#include #include #include #include "fsck.h" @@ -127,20 +129,27 @@ fsck_readdir(idesc) { register struct direct *dp, *ndp; register struct bufarea *bp; - long size, blksiz, fix; + long blksiz, new_id_filesize; + int fix, new_id_loc, new_reclen, orig_id_loc, size; blksiz = idesc->id_numfrags * sblock.fs_fsize; bp = getdirblk(idesc->id_blkno, blksiz); + orig_id_loc = idesc->id_loc; if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 && idesc->id_loc < blksiz) { dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); if (dircheck(idesc, dp)) goto dpok; - idesc->id_loc += DIRBLKSIZ; - idesc->id_filesize -= DIRBLKSIZ; + /* + * See below about recursion. + */ + new_id_loc = idesc->id_loc + DIRBLKSIZ; + new_id_filesize = idesc->id_filesize - DIRBLKSIZ; fix = dofix(idesc, "DIRECTORY CORRUPTED"); + idesc->id_loc = new_id_loc; + idesc->id_filesize = new_id_filesize; bp = getdirblk(idesc->id_blkno, blksiz); - dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); + dp = (struct direct *)(bp->b_un.b_buf + orig_id_loc); dp->d_reclen = DIRBLKSIZ; dp->d_ino = 0; dp->d_namlen = 0; @@ -161,12 +170,23 @@ dpok: if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && dircheck(idesc, ndp) == 0) { size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); - idesc->id_loc += size; - idesc->id_filesize -= size; + /* + * dofix() may recurse here. Don't let it cause multiple + * fixups. + */ + new_id_loc = idesc->id_loc + size; + new_id_filesize = idesc->id_filesize - size; + new_reclen = dp->d_reclen + size; fix = dofix(idesc, "DIRECTORY CORRUPTED"); + idesc->id_loc = new_id_loc; + idesc->id_filesize = new_id_filesize; + /* + * dofix() calls fsck_readdir() and getdirblk() discards + * the lock on bp so bp may now be invalid. + */ bp = getdirblk(idesc->id_blkno, blksiz); - dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); - dp->d_reclen += size; + dp = (struct direct *)(bp->b_un.b_buf + orig_id_loc); + dp->d_reclen = new_reclen; if (fix) dirty(bp); } @@ -185,8 +205,10 @@ dircheck(idesc, dp) register char *cp; int spaceleft; - size = DIRSIZ(dp); spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); + if (spaceleft < offsetof(struct direct, d_name)) + return (0); /* dp is bad; don't use it */ + size = DIRSIZ(dp); if (dp->d_ino < maxino && dp->d_reclen != 0 && dp->d_reclen <= spaceleft && diff --git a/sbin/fsck/fsck.8 b/sbin/fsck/fsck.8 index a7dae66fa7..0ccbf4b4ef 100644 --- a/sbin/fsck/fsck.8 +++ b/sbin/fsck/fsck.8 @@ -31,6 +31,8 @@ .\" .\" @(#)fsck.8 6.9 (Berkeley) 4/20/91 .\" +.\" $Header: /b/source/CVS/src/sbin/fsck/fsck.8,v 1.3 1993/03/23 00:27:48 cgd Exp $ +.\" .TH FSCK 8 "April 20, 1991" .UC 4 .de us diff --git a/sbin/fsck/fsck.h b/sbin/fsck/fsck.h index 310bd219b1..3c858f3ce9 100644 --- a/sbin/fsck/fsck.h +++ b/sbin/fsck/fsck.h @@ -31,6 +31,8 @@ * SUCH DAMAGE. * * @(#)fsck.h 5.17 (Berkeley) 7/27/90 + * + * $Header: /b/source/CVS/src/sbin/fsck/fsck.h,v 1.3 1993/03/23 00:27:51 cgd Exp $ */ #define MAXDUP 10 /* limit on dup blks (per inode) */ diff --git a/sbin/fsck/inode.c b/sbin/fsck/inode.c index c87eb8f4be..b6a81e7e20 100644 --- a/sbin/fsck/inode.c +++ b/sbin/fsck/inode.c @@ -33,6 +33,7 @@ #ifndef lint static char sccsid[] = "@(#)inode.c 5.18 (Berkeley) 3/19/91"; +static char rcsid[] = "$Header: /b/source/CVS/src/sbin/fsck/inode.c,v 1.4 1993/06/13 21:09:16 mycroft Exp $"; #endif /* not lint */ #include @@ -58,7 +59,8 @@ ckinode(dp, idesc) idesc->id_fix = DONTKNOW; idesc->id_entryno = 0; idesc->id_filesize = dp->di_size; - if ((dp->di_mode & IFMT) == IFBLK || (dp->di_mode & IFMT) == IFCHR) + if ((dp->di_mode & IFMT) == IFBLK || (dp->di_mode & IFMT) == IFCHR || + DFASTLINK(*dp)) return (KEEPON); dino = *dp; ndb = howmany(dino.di_size, sblock.fs_bsize); diff --git a/sbin/fsck/main.c b/sbin/fsck/main.c index 8c2838fbc7..3e2b728980 100644 --- a/sbin/fsck/main.c +++ b/sbin/fsck/main.c @@ -39,6 +39,7 @@ char copyright[] = #ifndef lint static char sccsid[] = "@(#)main.c 5.27 (Berkeley) 8/7/90"; +static char rcsid[] = "$Header: /b/source/CVS/src/sbin/fsck/main.c,v 1.3 1993/03/23 00:27:55 cgd Exp $"; #endif /* not lint */ #include diff --git a/sbin/fsck/pass1.c b/sbin/fsck/pass1.c index ffea6cb574..152af39778 100644 --- a/sbin/fsck/pass1.c +++ b/sbin/fsck/pass1.c @@ -33,6 +33,7 @@ #ifndef lint static char sccsid[] = "@(#)pass1.c 5.16 (Berkeley) 3/19/91"; +static char rcsid[] = "$Header: /b/source/CVS/src/sbin/fsck/pass1.c,v 1.4 1993/06/13 21:10:50 mycroft Exp $"; #endif /* not lint */ #include @@ -101,6 +102,13 @@ pass1() continue; } lastino = inumber; + /* is fast symlink? */ + if (DFASTLINK(*dp)) { + lncntp[inumber] = dp->di_nlink; + statemap[inumber] = FSTATE; + n_files++; + continue; + } if (/* dp->di_size < 0 || */ dp->di_size + sblock.fs_bsize - 1 < dp->di_size) { if (debug) diff --git a/sbin/fsck/pass1b.c b/sbin/fsck/pass1b.c index 8537162e6f..76c1e8c154 100644 --- a/sbin/fsck/pass1b.c +++ b/sbin/fsck/pass1b.c @@ -33,6 +33,7 @@ #ifndef lint static char sccsid[] = "@(#)pass1b.c 5.8 (Berkeley) 7/20/90"; +static char rcsid[] = "$Header: /b/source/CVS/src/sbin/fsck/pass1b.c,v 1.3 1993/03/23 00:28:01 cgd Exp $"; #endif /* not lint */ #include diff --git a/sbin/fsck/pass2.c b/sbin/fsck/pass2.c index 70bc60535b..0347a5b325 100644 --- a/sbin/fsck/pass2.c +++ b/sbin/fsck/pass2.c @@ -33,6 +33,7 @@ #ifndef lint static char sccsid[] = "@(#)pass2.c 5.17 (Berkeley) 12/28/90"; +static char rcsid[] = "$Header: /b/source/CVS/src/sbin/fsck/pass2.c,v 1.3 1993/03/23 00:28:03 cgd Exp $"; #endif /* not lint */ #include diff --git a/sbin/fsck/pass3.c b/sbin/fsck/pass3.c index 0d372ae875..b0b22bccd4 100644 --- a/sbin/fsck/pass3.c +++ b/sbin/fsck/pass3.c @@ -33,6 +33,7 @@ #ifndef lint static char sccsid[] = "@(#)pass3.c 5.10 (Berkeley) 6/1/90"; +static char rcsid[] = "$Header: /b/source/CVS/src/sbin/fsck/pass3.c,v 1.3 1993/03/23 00:28:05 cgd Exp $"; #endif /* not lint */ #include diff --git a/sbin/fsck/pass4.c b/sbin/fsck/pass4.c index 4a01583100..0611de5c28 100644 --- a/sbin/fsck/pass4.c +++ b/sbin/fsck/pass4.c @@ -33,6 +33,7 @@ #ifndef lint static char sccsid[] = "@(#)pass4.c 5.10 (Berkeley) 7/20/90"; +static char rcsid[] = "$Header: /b/source/CVS/src/sbin/fsck/pass4.c,v 1.3 1993/03/23 00:28:07 cgd Exp $"; #endif /* not lint */ #include diff --git a/sbin/fsck/pass5.c b/sbin/fsck/pass5.c index 8c4d2aa29b..9ea12c8e6d 100644 --- a/sbin/fsck/pass5.c +++ b/sbin/fsck/pass5.c @@ -33,6 +33,7 @@ #ifndef lint static char sccsid[] = "@(#)pass5.c 5.13 (Berkeley) 7/20/90"; +static char rcsid[] = "$Header: /b/source/CVS/src/sbin/fsck/pass5.c,v 1.3 1993/03/23 00:28:09 cgd Exp $"; #endif /* not lint */ #include diff --git a/sbin/fsck/preen.c b/sbin/fsck/preen.c index 29f2d28d27..35dfac8434 100644 --- a/sbin/fsck/preen.c +++ b/sbin/fsck/preen.c @@ -33,6 +33,7 @@ #ifndef lint static char sccsid[] = "@(#)preen.c 5.7 (Berkeley) 3/19/91"; +static char rcsid[] = "$Header: /b/source/CVS/src/sbin/fsck/preen.c,v 1.3 1993/03/23 00:28:11 cgd Exp $"; #endif /* not lint */ #include diff --git a/sbin/fsck/setup.c b/sbin/fsck/setup.c index e29d8e586e..ea4f6b5fe8 100644 --- a/sbin/fsck/setup.c +++ b/sbin/fsck/setup.c @@ -33,6 +33,7 @@ #ifndef lint static char sccsid[] = "@(#)setup.c 5.33 (Berkeley) 2/22/91"; +static char rcsid[] = "$Header: /b/source/CVS/src/sbin/fsck/setup.c,v 1.3 1993/03/23 00:28:14 cgd Exp $"; #endif /* not lint */ #define DKTYPENAMES diff --git a/sbin/fsck/utilities.c b/sbin/fsck/utilities.c index 9f5f7cf138..c9186efbe2 100644 --- a/sbin/fsck/utilities.c +++ b/sbin/fsck/utilities.c @@ -33,6 +33,7 @@ #ifndef lint static char sccsid[] = "@(#)utilities.c 5.30 (Berkeley) 7/26/91"; +static char rcsid[] = "$Header: /b/source/CVS/src/sbin/fsck/utilities.c,v 1.3 1993/03/23 00:28:16 cgd Exp $"; #endif /* not lint */ #include diff --git a/sbin/halt/Makefile b/sbin/halt/Makefile index 36fd4fb7c2..d8c21ab5ec 100644 --- a/sbin/halt/Makefile +++ b/sbin/halt/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= halt -MAN8= halt.0 +MAN8= halt.8 DPADD= ${LIBUTIL} LDADD= -lutil diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile index 07d9c22248..cbfb991d7a 100644 --- a/sbin/ifconfig/Makefile +++ b/sbin/ifconfig/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= ifconfig -MAN8= ifconfig.0 +MAN8= ifconfig.8 .include diff --git a/sbin/init.bsdi/Makefile b/sbin/init.bsdi/Makefile new file mode 100644 index 0000000000..cce9456b5b --- /dev/null +++ b/sbin/init.bsdi/Makefile @@ -0,0 +1,17 @@ +# BSDI $Id: Makefile,v 1.2 1993/08/02 23:18:35 nate Exp $ +# @(#)Makefile 5.3 (Berkeley) 5/11/90 + +PROG= init +MAN8= init.8 +# XXX fix this when we have KERN_SECURELVL +CFLAGS+=-DSECURE -DNOSYSCTL +DPADD= ${LIBUTIL} +LDADD= -lutil +BINMODE=500 + +.if exists(/usr/lib/libcrypt.a) +DPADD+= ${LIBCRYPT} +LDADD+= -lcrypt +.endif + +.include diff --git a/sbin/init.bsdi/README b/sbin/init.bsdi/README new file mode 100644 index 0000000000..ede363db84 --- /dev/null +++ b/sbin/init.bsdi/README @@ -0,0 +1,35 @@ +This is the source for a version of /sbin/init that runs on 4.4 +BSD and BSD/386 1.0 systems. I wrote it for BSDI about two years +ago and BSDI donated the source to UC Berkeley CSRG, but CSRG didn't +get it working in time to include it with the Networking 2 release, +although we had hoped it would appear there. Since that time both +BSDI and CSRG have worked on it to make it more useful and reliable, +and recently the two groups have merged our efforts and produced +a common version once more. Since the source is freely redistributable +under the terms of the standard BSD copyright, I'm posting it to +the net on behalf of BSDI so other folks might get some use from +it. Keep in mind that I'm only paid to support this code if you +bought a system from BSDI, so I probably won't be able to make very +timely responses to random bug reports, but I am interested in +seeing them. (More formally: Neither BSDI nor I personally will +make any guarantees about the reliability or usefulness of this +code, and it's unsupported unless you have a support agreement.) + +I also make no promises that this code will just drop in and work +on any particular system. Please don't call me at home to tell me +that you compiled and installed init, and now your system won't +boot. With a program as sensitive as init, you should always be +prepared to fall back to an earlier working version if the new one +fails. ALWAYS save your old version before installing, and ALWAYS +be prepared to boot from an alternate root (e.g. a floppy) if the +new init fails. I strongly advise you to read the man page carefully +before you install. One advance warning: I don't think anyone has +ever tested the window system start-up code. One of these days... +This code is running on my systems at home, and an earlier version +is running on hundreds of BSDI systems, so it can't be too broken. + +Enjoy, + +Donn Seeley +Berkeley Software Design, Inc. (Salt Lake City) +June 14, 1993 diff --git a/sbin/init.bsdi/init.8 b/sbin/init.bsdi/init.8 new file mode 100644 index 0000000000..78298024b9 --- /dev/null +++ b/sbin/init.bsdi/init.8 @@ -0,0 +1,291 @@ +.\" Copyright (c) 1980, 1991 Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Donn Seeley at Berkeley Software Design, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)init.8 6.7 (Berkeley) 6/2/93 +.\" +.Dd June 2, 1993 +.Dt INIT 8 +.Os BSD 4 +.Sh NAME +.Nm init +.Nd process control initialization +.Sh SYNOPSIS +.Nm init +.Sh DESCRIPTION +The +.Nm init +program +is the last stage of the boot process. +It normally runs the automatic reboot sequence as described in +.Xr reboot 8 , +and if this succeeds, begins multi-user operation. +If the reboot scripts fail, +.Nm init +commences single user operation by giving +the super-user a shell on the console. +The +.Nm init +program may be passed parameters +from the boot program to +prevent the system from going multi-user and to instead execute +a single user shell without starting the normal daemons. +The system is then quiescent for maintenance work and may +later be made to go to multi-user by exiting the +single-user shell (with ^D). +This +causes +.Nm init +to run the +.Pa /etc/rc +start up command file in fastboot mode (skipping disk checks). +.Pp +If the +.Nm console +entry in the +.Xr ttys 5 +file is marked ``insecure'', +then +.Nm init +will require that the superuser password be +entered before the system will start a single-user shell. +The password check is skipped if the +.Nm console +is marked as ``secure''. +.Pp +The kernel runs with four different levels of security. +Any superuser process can raise the security level, but only +.Nm init +can lower it. +Security levels are defined as follows: +.Bl -tag -width flag +.It Ic -1 +Permanently insecure mode \- always run system in level 0 mode. +.It Ic 0 +Insecure mode \- immutable and append-only flags may be turned off. +All devices may be read or written subject to their permissions. +.It Ic 1 +Secure mode \- immutable and append-only flags may not be changed; +disks for mounted filesystems, +.Pa /dev/mem , +and +.Pa /dev/kmem +are read-only. +.It Ic 2 +Highly secure mode \- same as secure mode, plus disks are always +read-only whether mounted or not. +This level precludes tampering with filesystems by unmounting them, +but also inhibits running +.Xr newfs 8 +while the system is multi-user. +.El +.Pp +Normally, the system runs in level 0 mode while single user +and in level 1 mode while multiuser. +If the level 2 mode is desired while running multiuser, +it can be set in the startup script +.Pa /etc/rc +using +.Xr sysctl 1 . +If it is desired to run the system in level 0 mode while multiuser, +the administrator must build a kernel with the variable +.Nm securelevel +in the kernel source file +.Pa /sys/kern/kern_sysctl.c +initialized to -1. +.Pp +In multi-user operation, +.Nm init +maintains +processes for the terminal ports found in the file +.Xr ttys 5 . +.Nm Init +reads this file, and executes the command found in the second field. +This command is usually +.Xr getty 8 ; +.Xr getty +opens and initializes the tty line +and +executes the +.Xr login +program. +The +.Xr login +program, when a valid user logs in, +executes a shell for that user. When this shell +dies, either because the user logged out +or an abnormal termination occurred (a signal), +the +.Nm init +program wakes up, deletes the user +from the +.Xr utmp 5 +file of current users and records the logout in the +.Xr wtmp +file. +The cycle is +then restarted by +.Nm init +executing a new +.Xr getty +for the line. +.Pp +Line status (on, off, secure, getty, or window information) +may be changed in the +.Xr ttys +file without a reboot by sending the signal +.Dv SIGHUP +to +.Nm init +with the command +.Dq Li "kill -HUP 1" . +On receipt of this signal, +.Nm init +re-reads the +.Xr ttys +file. +When a line is turned off in +.Xr ttys , +.Nm init +will send a SIGHUP signal to the controlling process +for the session associated with the line. +For any lines that were previously turned off in the +.Xr ttys +file and are now on, +.Nm init +executes a new +.Xr getty +to enable a new login. +If the getty or window field for a line is changed, +the change takes effect at the end of the current +login session (e.g., the next time +.Nm init +starts a process on the line). +If a line is commented out or deleted from +.Xr ttys , +.Nm init +will not do anything at all to that line. +However, it will complain that the relationship between lines +in the +.Xr ttys +file and records in the +.Xr utmp +file is out of sync, +so this practice is not recommended. +.Pp +.Nm Init +will terminate multi-user operations and resume single-user mode +if sent a terminate +.Pq Dv TERM +signal, for example, +.Dq Li "kill \-TERM 1" . +If there are processes outstanding that are deadlocked (because of +hardware or software failure), +.Xr init +will not wait for them all to die (which might take forever), but +will time out after 30 seconds and print a warning message. +.Pp +.Nm Init +will cease creating new +.Xr getty Ns 's +and allow the system to slowly die away, if it is sent a terminal stop +.Pq Dv TSTP +signal, i.e. +.Dq Li "kill \-TSTP 1" . +A later hangup will resume full +multi-user operations, or a terminate will start a single user shell. +This hook is used by +.Xr reboot 8 +and +.Xr halt 8 . +.Pp +The role of +.Nm init +is so critical that if it dies, the system will reboot itself +automatically. +If, at bootstrap time, the +.Xr init +process cannot be located, the system will panic with the message +``panic: "init died (signal %d, exit %d)''. +.Sh DIAGNOSTICS +.Bl -diag +.It "getty repeating too quickly on port %s, sleeping" +A process being started to service a line is exiting quickly +each time it is started. +This is often caused by a ringing or noisy terminal line. +.Em "Init will sleep for 10 seconds" , +.Em "then continue trying to start the process" . +.Pp +.It "some processes would not die; ps axl advised." +A process +is hung and could not be killed when the system was shutting down. +This condition is usually caused by a process +that is stuck in a device driver because of +a persistent device error condition. +.El +.Sh FILES +.Bl -tag -width /var/log/wtmp -compact +.It Pa /dev/console +System console device. +.It Pa /dev/tty* +Terminal ports found in +.Xr ttys . +.It Pa /var/run/utmp +Record of Current users on the system. +.It Pa /var/log/wtmp +Record of all logins and logouts. +.It Pa /etc/ttys +The terminal initialization information file. +.It Pa /etc/rc +System startup commands. +.El +.Sh SEE ALSO +.Xr login 1 , +.Xr kill 1 , +.Xr sh 1 , +.Xr ttys 5 , +.Xr crash 8 , +.Xr getty 8 , +.Xr rc 8 , +.Xr reboot 8 , +.Xr halt 8 , +.Xr shutdown 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . +.Sh BUGS +Systems without +.Xr sysctl +behave as though they have security level \-1. diff --git a/sbin/init.bsdi/init.c b/sbin/init.bsdi/init.c new file mode 100644 index 0000000000..0ba1b5a6aa --- /dev/null +++ b/sbin/init.bsdi/init.c @@ -0,0 +1,1292 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Donn Seeley at Berkeley Software Design, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1991 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)init.c 6.22 (Berkeley) 6/2/93"; +#endif /* not lint */ + +#include +#ifndef NOSYSCTL +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __STDC__ +#include +#else +#include +#endif + +#ifdef SECURE +#include +#endif + +#include "pathnames.h" + +/* + * Until the mythical util.h arrives... + */ +extern int login_tty __P((int)); +extern int logout __P((const char *)); +extern void logwtmp __P((const char *, const char *, const char *)); + +/* + * Sleep times; used to prevent thrashing. + */ +#define GETTY_SPACING 5 /* N secs minimum getty spacing */ +#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ +#define WINDOW_WAIT 3 /* wait N secs after starting window */ +#define STALL_TIMEOUT 30 /* wait N secs after warning */ +#define DEATH_WATCH 10 /* wait N secs for procs to die */ + +void handle __P((sig_t, ...)); +void delset __P((sigset_t *, ...)); + +void stall __P((char *, ...)); +void warning __P((char *, ...)); +void emergency __P((char *, ...)); +void disaster __P((int)); +void badsys __P((int)); + +/* + * We really need a recursive typedef... + * The following at least guarantees that the return type of (*state_t)() + * is sufficiently wide to hold a function pointer. + */ +typedef long (*state_func_t) __P((void)); +typedef state_func_t (*state_t) __P((void)); + +state_func_t single_user __P((void)); +state_func_t runcom __P((void)); +state_func_t read_ttys __P((void)); +state_func_t multi_user __P((void)); +state_func_t clean_ttys __P((void)); +state_func_t catatonia __P((void)); +state_func_t death __P((void)); + +enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; + +void transition __P((state_t)); +state_t requested_transition = runcom; + +void setctty __P((char *)); + +typedef struct init_session { + int se_index; /* index of entry in ttys file */ + pid_t se_process; /* controlling process */ + time_t se_started; /* used to avoid thrashing */ + int se_flags; /* status of session */ +#define SE_SHUTDOWN 0x1 /* session won't be restarted */ + char *se_device; /* filename of port */ + char *se_getty; /* what to run on that port */ + char **se_getty_argv; /* pre-parsed argument array */ + char *se_window; /* window system (started only once) */ + char **se_window_argv; /* pre-parsed argument array */ + struct init_session *se_prev; + struct init_session *se_next; +} session_t; + +void free_session __P((session_t *)); +session_t *new_session __P((session_t *, int, struct ttyent *)); +session_t *sessions; + +char **construct_argv __P((char *)); +void start_window_system __P((session_t *)); +void collect_child __P((pid_t)); +pid_t start_getty __P((session_t *)); +void transition_handler __P((int)); +void alrm_handler __P((int)); +void setsecuritylevel __P((int)); +int getsecuritylevel __P((void)); +int setupargv __P((session_t *, struct ttyent *)); +int clang; + +void clear_session_logs __P((session_t *)); + +int start_session_db __P((void)); +void add_session __P((session_t *)); +void del_session __P((session_t *)); +session_t *find_session __P((pid_t)); +DB *session_db; + +/* + * The mother of all processes. + */ +int +main(argc, argv) + int argc; + char **argv; +{ + int c; + struct sigaction sa; + sigset_t mask; + + + /* Dispose of random users. */ + if (getuid() != 0) { + (void)fprintf(stderr, "init: %s\n", strerror(EPERM)); + exit (1); + } + + /* System V users like to reexec init. */ + if (getpid() != 1) { + (void)fprintf(stderr, "init: already running\n"); + exit (1); + } + + /* + * Note that this does NOT open a file... + * Does 'init' deserve its own facility number? + */ + openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); + + /* + * Create an initial session. + */ + if (setsid() < 0) + warning("initial setsid() failed: %m"); + + /* + * This code assumes that we always get arguments through flags, + * never through bits set in some random machine register. + */ + while ((c = getopt(argc, argv, "sf")) != -1) + switch (c) { + case 's': + requested_transition = single_user; + break; + case 'f': + runcom_mode = FASTBOOT; + break; + default: + warning("unrecognized flag '-%c'", c); + break; + } + + if (optind != argc) + warning("ignoring excess arguments"); + + /* + * We catch or block signals rather than ignore them, + * so that they get reset on exec. + */ + handle(badsys, SIGSYS, 0); + handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, + SIGBUS, SIGXCPU, SIGXFSZ, 0); + handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0); + handle(alrm_handler, SIGALRM, 0); + sigfillset(&mask); + delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, + SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); + (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); + + /* + * Paranoia. + */ + close(0); + close(1); + close(2); + + /* + * Start the state machine. + */ + transition(requested_transition); + + /* + * Should never reach here. + */ + return 1; +} + +/* + * Associate a function with a signal handler. + */ +void +#ifdef __STDC__ +handle(sig_t handler, ...) +#else +handle(va_alist) + va_dcl +#endif +{ + int sig; + struct sigaction sa; + int mask_everything; + va_list ap; +#ifndef __STDC__ + sig_t handler; + + va_start(ap); + handler = va_arg(ap, sig_t); +#else + va_start(ap, handler); +#endif + + sa.sa_handler = handler; + sigfillset(&mask_everything); + + while (sig = va_arg(ap, int)) { + sa.sa_mask = mask_everything; + /* XXX SA_RESTART? */ + sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; + sigaction(sig, &sa, (struct sigaction *) 0); + } + va_end(ap); +} + +/* + * Delete a set of signals from a mask. + */ +void +#ifdef __STDC__ +delset(sigset_t *maskp, ...) +#else +delset(va_alist) + va_dcl +#endif +{ + int sig; + va_list ap; +#ifndef __STDC__ + sigset_t *maskp; + + va_start(ap); + maskp = va_arg(ap, sigset_t *); +#else + va_start(ap, maskp); +#endif + + while (sig = va_arg(ap, int)) + sigdelset(maskp, sig); + va_end(ap); +} + +/* + * Log a message and sleep for a while (to give someone an opportunity + * to read it and to save log or hardcopy output if the problem is chronic). + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +stall(char *message, ...) +#else +stall(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_ALERT, message, ap); + va_end(ap); + sleep(STALL_TIMEOUT); +} + +/* + * Like stall(), but doesn't sleep. + * If cpp had variadic macros, the two functions could be #defines for another. + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +warning(char *message, ...) +#else +warning(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_ALERT, message, ap); + va_end(ap); +} + +/* + * Log an emergency message. + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +emergency(char *message, ...) +#else +emergency(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_EMERG, message, ap); + va_end(ap); +} + +/* + * Catch a SIGSYS signal. + * + * These may arise if a system does not support sysctl. + * We tolerate up to 25 of these, then throw in the towel. + */ +void +badsys(sig) + int sig; +{ + static int badcount = 0; + + if (badcount++ < 25) + return; + disaster(sig); +} + +/* + * Catch an unexpected signal. + */ +void +disaster(sig) + int sig; +{ + emergency("fatal signal: %s", + sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal"); + + sleep(STALL_TIMEOUT); + _exit(sig); /* reboot */ +} + +/* + * Get the security level of the kernel. + */ +int +getsecuritylevel() +{ +#ifdef KERN_SECURELVL + int name[2], curlevel; + size_t len; + extern int errno; + + name[0] = CTL_KERN; + name[1] = KERN_SECURELVL; + len = sizeof curlevel; + if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { + emergency("cannot get kernel security level: %s", + strerror(errno)); + return (-1); + } + return (curlevel); +#else + return (-1); +#endif +} + +/* + * Set the security level of the kernel. + */ +void +setsecuritylevel(newlevel) + int newlevel; +{ +#ifdef KERN_SECURELVL + int name[2], curlevel; + extern int errno; + + curlevel = getsecuritylevel(); + if (newlevel == curlevel) + return; + name[0] = CTL_KERN; + name[1] = KERN_SECURELVL; + if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { + emergency( + "cannot change kernel security level from %d to %d: %s", + curlevel, newlevel, strerror(errno)); + return; + } +#ifdef SECURE + warning("kernel security level changed from %d to %d", + curlevel, newlevel); +#endif +#endif +} + +/* + * Change states in the finite state machine. + * The initial state is passed as an argument. + */ +void +transition(s) + state_t s; +{ + for (;;) + s = (state_t) (*s)(); +} + +/* + * Close out the accounting files for a login session. + * NB: should send a message to the session logger to avoid blocking. + */ +void +clear_session_logs(sp) + session_t *sp; +{ + char *line = sp->se_device + sizeof(_PATH_DEV) - 1; + + if (logout(line)) + logwtmp(line, "", ""); +} + +/* + * Start a session and allocate a controlling terminal. + * Only called by children of init after forking. + */ +void +setctty(name) + char *name; +{ + int fd; + + (void) revoke(name); + sleep (2); /* leave DTR low */ + if ((fd = open(name, O_RDWR)) == -1) { + stall("can't open %s: %m", name); + _exit(1); + } + if (login_tty(fd) == -1) { + stall("can't get %s for controlling terminal: %m", name); + _exit(1); + } +} + +/* + * Bring the system up single user. + */ +state_func_t +single_user() +{ + pid_t pid, wpid; + int status; + sigset_t mask; + char *shell = _PATH_BSHELL; + char *argv[2]; +#ifdef SECURE + struct ttyent *typ; + struct passwd *pp; + static const char banner[] = + "Enter root password, or ^D to go multi-user\n"; + char *clear, *password; +#endif + + /* + * If the kernel is in secure mode, downgrade it to insecure mode. + */ + if (getsecuritylevel() > 0) + setsecuritylevel(0); + + if ((pid = fork()) == 0) { + /* + * Start the single user session. + */ + setctty(_PATH_CONSOLE); + +#ifdef SECURE + /* + * Check the root password. + * We don't care if the console is 'on' by default; + * it's the only tty that can be 'off' and 'secure'. + */ + typ = getttynam("console"); + pp = getpwnam("root"); + if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) { + write(2, banner, sizeof banner - 1); + for (;;) { + clear = getpass("Password:"); + if (clear == 0 || *clear == '\0') + _exit(0); + password = crypt(clear, pp->pw_passwd); + bzero(clear, _PASSWORD_LEN); + if (strcmp(password, pp->pw_passwd) == 0) + break; + warning("single-user login failed\n"); + } + } + endttyent(); + endpwent(); +#endif /* SECURE */ + +#ifdef DEBUGSHELL + { + char altshell[128], *cp = altshell; + int num; + +#define SHREQUEST \ + "Enter pathname of shell or RETURN for sh: " + (void)write(STDERR_FILENO, + SHREQUEST, sizeof(SHREQUEST) - 1); + while ((num = read(STDIN_FILENO, cp, 1)) != -1 && + num != 0 && *cp != '\n' && cp < &altshell[127]) + cp++; + *cp = '\0'; + if (altshell[0] != '\0') + shell = altshell; + } +#endif /* DEBUGSHELL */ + + /* + * Unblock signals. + * We catch all the interesting ones, + * and those are reset to SIG_DFL on exec. + */ + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + /* + * Fire off a shell. + * If the default one doesn't work, try the Bourne shell. + */ + argv[0] = "-sh"; + argv[1] = 0; + execv(shell, argv); + emergency("can't exec %s for single user: %m", shell); + execv(_PATH_BSHELL, argv); + emergency("can't exec %s for single user: %m", _PATH_BSHELL); + sleep(STALL_TIMEOUT); + _exit(1); + } + + if (pid == -1) { + /* + * We are seriously hosed. Do our best. + */ + emergency("can't fork single-user shell, trying again"); + while (waitpid(-1, (int *) 0, WNOHANG) > 0) + continue; + return (state_func_t) single_user; + } + + requested_transition = 0; + do { + if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) + collect_child(wpid); + if (wpid == -1) { + if (errno == EINTR) + continue; + warning("wait for single-user shell failed: %m; restarting"); + return (state_func_t) single_user; + } + if (wpid == pid && WIFSTOPPED(status)) { + warning("init: shell stopped, restarting\n"); + kill(pid, SIGCONT); + wpid = -1; + } + } while (wpid != pid && !requested_transition); + + if (requested_transition) + return (state_func_t) requested_transition; + + if (!WIFEXITED(status)) { + if (WTERMSIG(status) == SIGKILL) { + /* + * reboot(8) killed shell? + */ + warning("single user shell terminated."); + sleep(STALL_TIMEOUT); + _exit(0); + } else { + warning("single user shell terminated, restarting"); + return (state_func_t) single_user; + } + } + + runcom_mode = FASTBOOT; + return (state_func_t) runcom; +} + +/* + * Run the system startup script. + */ +state_func_t +runcom() +{ + pid_t pid, wpid; + int status; + char *argv[4]; + struct sigaction sa; + + if ((pid = fork()) == 0) { + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); + (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); + + setctty(_PATH_CONSOLE); + + argv[0] = "sh"; + argv[1] = _PATH_RUNCOM; + argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0; + argv[3] = 0; + + sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); + + execv(_PATH_BSHELL, argv); + stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); + _exit(1); /* force single user mode */ + } + + if (pid == -1) { + emergency("can't fork for %s on %s: %m", + _PATH_BSHELL, _PATH_RUNCOM); + while (waitpid(-1, (int *) 0, WNOHANG) > 0) + continue; + sleep(STALL_TIMEOUT); + return (state_func_t) single_user; + } + + /* + * Copied from single_user(). This is a bit paranoid. + */ + do { + if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) + collect_child(wpid); + if (wpid == -1) { + if (errno == EINTR) + continue; + warning("wait for %s on %s failed: %m; going to single user mode", + _PATH_BSHELL, _PATH_RUNCOM); + return (state_func_t) single_user; + } + if (wpid == pid && WIFSTOPPED(status)) { + warning("init: %s on %s stopped, restarting\n", + _PATH_BSHELL, _PATH_RUNCOM); + kill(pid, SIGCONT); + wpid = -1; + } + } while (wpid != pid); + + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && + requested_transition == catatonia) { + /* /etc/rc executed /sbin/reboot; wait for the end quietly */ + sigset_t s; + + sigfillset(&s); + for (;;) + sigsuspend(&s); + } + + if (!WIFEXITED(status)) { + warning("%s on %s terminated abnormally, going to single user mode", + _PATH_BSHELL, _PATH_RUNCOM); + return (state_func_t) single_user; + } + + if (WEXITSTATUS(status)) + return (state_func_t) single_user; + + runcom_mode = AUTOBOOT; /* the default */ + /* NB: should send a message to the session logger to avoid blocking. */ + logwtmp("~", "reboot", ""); + return (state_func_t) read_ttys; +} + +/* + * Open the session database. + * + * NB: We could pass in the size here; is it necessary? + */ +int +start_session_db() +{ + if (session_db && (*session_db->close)(session_db)) + emergency("session database close: %s", strerror(errno)); + if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { + emergency("session database open: %s", strerror(errno)); + return (1); + } + return (0); + +} + +/* + * Add a new login session. + */ +void +add_session(sp) + session_t *sp; +{ + DBT key; + DBT data; + + key.data = &sp->se_process; + key.size = sizeof sp->se_process; + data.data = &sp; + data.size = sizeof sp; + + if ((*session_db->put)(session_db, &key, &data, 0)) + emergency("insert %d: %s", sp->se_process, strerror(errno)); +} + +/* + * Delete an old login session. + */ +void +del_session(sp) + session_t *sp; +{ + DBT key; + + key.data = &sp->se_process; + key.size = sizeof sp->se_process; + + if ((*session_db->del)(session_db, &key, 0)) + emergency("delete %d: %s", sp->se_process, strerror(errno)); +} + +/* + * Look up a login session by pid. + */ +session_t * +#ifdef __STDC__ +find_session(pid_t pid) +#else +find_session(pid) + pid_t pid; +#endif +{ + DBT key; + DBT data; + session_t *ret; + + key.data = &pid; + key.size = sizeof pid; + if ((*session_db->get)(session_db, &key, &data, 0) != 0) + return 0; + bcopy(data.data, (char *)&ret, sizeof(ret)); + return ret; +} + +/* + * Construct an argument vector from a command line. + */ +char ** +construct_argv(command) + char *command; +{ + register int argc = 0; + register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) + * sizeof (char *)); + static const char separators[] = " \t"; + + if ((argv[argc++] = strtok(command, separators)) == 0) + return 0; + while (argv[argc++] = strtok((char *) 0, separators)) + continue; + return argv; +} + +/* + * Deallocate a session descriptor. + */ +void +free_session(sp) + register session_t *sp; +{ + free(sp->se_device); + if (sp->se_getty) { + free(sp->se_getty); + free(sp->se_getty_argv); + } + if (sp->se_window) { + free(sp->se_window); + free(sp->se_window_argv); + } + free(sp); +} + +/* + * Allocate a new session descriptor. + */ +session_t * +new_session(sprev, session_index, typ) + session_t *sprev; + int session_index; + register struct ttyent *typ; +{ + register session_t *sp; + + if ((typ->ty_status & TTY_ON) == 0 || + typ->ty_name == 0 || + typ->ty_getty == 0) + return 0; + + sp = (session_t *) malloc(sizeof (session_t)); + bzero(sp, sizeof *sp); + + sp->se_index = session_index; + + sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); + (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); + + if (setupargv(sp, typ) == 0) { + free_session(sp); + return (0); + } + + sp->se_next = 0; + if (sprev == 0) { + sessions = sp; + sp->se_prev = 0; + } else { + sprev->se_next = sp; + sp->se_prev = sprev; + } + + return sp; +} + +/* + * Calculate getty and if useful window argv vectors. + */ +int +setupargv(sp, typ) + session_t *sp; + struct ttyent *typ; +{ + + if (sp->se_getty) { + free(sp->se_getty); + free(sp->se_getty_argv); + } + sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2); + (void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name); + sp->se_getty_argv = construct_argv(sp->se_getty); + if (sp->se_getty_argv == 0) { + warning("can't parse getty for port %s", sp->se_device); + free(sp->se_getty); + sp->se_getty = 0; + return (0); + } + if (typ->ty_window) { + if (sp->se_window) + free(sp->se_window); + sp->se_window = strdup(typ->ty_window); + sp->se_window_argv = construct_argv(sp->se_window); + if (sp->se_window_argv == 0) { + warning("can't parse window for port %s", + sp->se_device); + free(sp->se_window); + sp->se_window = 0; + return (0); + } + } + return (1); +} + +/* + * Walk the list of ttys and create sessions for each active line. + */ +state_func_t +read_ttys() +{ + int session_index = 0; + register session_t *sp, *snext; + register struct ttyent *typ; + + /* + * Destroy any previous session state. + * There shouldn't be any, but just in case... + */ + for (sp = sessions; sp; sp = snext) { + if (sp->se_process) + clear_session_logs(sp); + snext = sp->se_next; + free_session(sp); + } + sessions = 0; + if (start_session_db()) + return (state_func_t) single_user; + + /* + * Allocate a session entry for each active port. + * Note that sp starts at 0. + */ + while (typ = getttyent()) + if (snext = new_session(sp, ++session_index, typ)) + sp = snext; + + endttyent(); + + return (state_func_t) multi_user; +} + +/* + * Start a window system running. + */ +void +start_window_system(sp) + session_t *sp; +{ + pid_t pid; + sigset_t mask; + + if ((pid = fork()) == -1) { + emergency("can't fork for window system on port %s: %m", + sp->se_device); + /* hope that getty fails and we can try again */ + return; + } + + if (pid) + return; + + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + if (setsid() < 0) + emergency("setsid failed (window) %m"); + + execv(sp->se_window_argv[0], sp->se_window_argv); + stall("can't exec window system '%s' for port %s: %m", + sp->se_window_argv[0], sp->se_device); + _exit(1); +} + +/* + * Start a login session running. + */ +pid_t +start_getty(sp) + session_t *sp; +{ + pid_t pid; + sigset_t mask; + time_t current_time = time((time_t *) 0); + + /* + * fork(), not vfork() -- we can't afford to block. + */ + if ((pid = fork()) == -1) { + emergency("can't fork for getty on port %s: %m", sp->se_device); + return -1; + } + + if (pid) + return pid; + + if (current_time > sp->se_started && + current_time - sp->se_started < GETTY_SPACING) { + warning("getty repeating too quickly on port %s, sleeping", + sp->se_device); + sleep((unsigned) GETTY_SLEEP); + } + + if (sp->se_window) { + start_window_system(sp); + sleep(WINDOW_WAIT); + } + + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + execv(sp->se_getty_argv[0], sp->se_getty_argv); + stall("can't exec getty '%s' for port %s: %m", + sp->se_getty_argv[0], sp->se_device); + _exit(1); +} + +/* + * Collect exit status for a child. + * If an exiting login, start a new login running. + */ +void +#ifdef __STDC__ +collect_child(pid_t pid) +#else +collect_child(pid) + pid_t pid; +#endif +{ + register session_t *sp, *sprev, *snext; + + if (! sessions) + return; + + if (! (sp = find_session(pid))) + return; + + clear_session_logs(sp); + del_session(sp); + sp->se_process = 0; + + if (sp->se_flags & SE_SHUTDOWN) { + if (sprev = sp->se_prev) + sprev->se_next = sp->se_next; + else + sessions = sp->se_next; + if (snext = sp->se_next) + snext->se_prev = sp->se_prev; + free_session(sp); + return; + } + + if ((pid = start_getty(sp)) == -1) { + /* serious trouble */ + requested_transition = clean_ttys; + return; + } + + sp->se_process = pid; + sp->se_started = time((time_t *) 0); + add_session(sp); +} + +/* + * Catch a signal and request a state transition. + */ +void +transition_handler(sig) + int sig; +{ + + switch (sig) { + case SIGHUP: + requested_transition = clean_ttys; + break; + case SIGTERM: + requested_transition = death; + break; + case SIGTSTP: + requested_transition = catatonia; + break; + default: + requested_transition = 0; + break; + } +} + +/* + * Take the system multiuser. + */ +state_func_t +multi_user() +{ + pid_t pid; + register session_t *sp; + + requested_transition = 0; + + /* + * If the administrator has not set the security level to -1 + * to indicate that the kernel should not run multiuser in secure + * mode, and the run script has not set a higher level of security + * than level 1, then put the kernel into secure mode. + */ + if (getsecuritylevel() == 0) + setsecuritylevel(1); + + for (sp = sessions; sp; sp = sp->se_next) { + if (sp->se_process) + continue; + if ((pid = start_getty(sp)) == -1) { + /* serious trouble */ + requested_transition = clean_ttys; + break; + } + sp->se_process = pid; + sp->se_started = time((time_t *) 0); + add_session(sp); + } + + while (!requested_transition) + if ((pid = waitpid(-1, (int *) 0, 0)) != -1) + collect_child(pid); + + return (state_func_t) requested_transition; +} + +/* + * This is an n-squared algorithm. We hope it isn't run often... + */ +state_func_t +clean_ttys() +{ + register session_t *sp, *sprev; + register struct ttyent *typ; + register int session_index = 0; + register int devlen; + + if (! sessions) + return (state_func_t) multi_user; + + devlen = sizeof(_PATH_DEV) - 1; + while (typ = getttyent()) { + ++session_index; + + for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) + if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) + break; + + if (sp) { + if (sp->se_index != session_index) { + warning("port %s changed utmp index from %d to %d", + sp->se_device, sp->se_index, + session_index); + sp->se_index = session_index; + } + if ((typ->ty_status & TTY_ON) == 0 || + typ->ty_getty == 0) { + sp->se_flags |= SE_SHUTDOWN; + kill(sp->se_process, SIGHUP); + continue; + } + sp->se_flags &= ~SE_SHUTDOWN; + if (setupargv(sp, typ) == 0) { + warning("can't parse getty for port %s", + sp->se_device); + sp->se_flags |= SE_SHUTDOWN; + kill(sp->se_process, SIGHUP); + } + continue; + } + + new_session(sprev, session_index, typ); + } + + endttyent(); + + return (state_func_t) multi_user; +} + +/* + * Block further logins. + */ +state_func_t +catatonia() +{ + register session_t *sp; + + for (sp = sessions; sp; sp = sp->se_next) + sp->se_flags |= SE_SHUTDOWN; + + return (state_func_t) multi_user; +} + +/* + * Note SIGALRM. + */ +void +alrm_handler(sig) + int sig; +{ + clang = 1; +} + +/* + * Bring the system down to single user. + */ +state_func_t +death() +{ + register session_t *sp; + register int i; + pid_t pid; + static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; + + for (sp = sessions; sp; sp = sp->se_next) + sp->se_flags |= SE_SHUTDOWN; + + /* NB: should send a message to the session logger to avoid blocking. */ + logwtmp("~", "shutdown", ""); + + for (i = 0; i < 3; ++i) { + if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) + return (state_func_t) single_user; + + clang = 0; + alarm(DEATH_WATCH); + do + if ((pid = waitpid(-1, (int *)0, 0)) != -1) + collect_child(pid); + while (clang == 0 && errno != ECHILD); + + if (errno == ECHILD) + return (state_func_t) single_user; + } + + warning("some processes would not die; ps axl advised"); + + return (state_func_t) single_user; +} diff --git a/sbin/init.bsdi/pathnames.h b/sbin/init.bsdi/pathnames.h new file mode 100644 index 0000000000..947cae0b50 --- /dev/null +++ b/sbin/init.bsdi/pathnames.h @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Donn Seeley at UUNET Technologies, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 6.1 (Berkeley) 7/2/91 + */ + +#include + +#define _PATH_SLOGGER "/sbin/session_logger" +#define _PATH_RUNCOM "/etc/rc" diff --git a/sbin/init.chmr/Makefile b/sbin/init.chmr/Makefile new file mode 100644 index 0000000000..2f52ec020a --- /dev/null +++ b/sbin/init.chmr/Makefile @@ -0,0 +1,8 @@ +PROG= init +MAN8= init.8 +SRCS= init.c ttytab.c configure.c cf_table.c utils.c fake_syslog.c +DPADD= ${LIBUTIL} +LDADD= -lutil +LDFLAGS= + +.include diff --git a/sbin/init.chmr/README b/sbin/init.chmr/README new file mode 100644 index 0000000000..e71f9b20d8 --- /dev/null +++ b/sbin/init.chmr/README @@ -0,0 +1,44 @@ +This is a new version of /sbin/init, with many features: +already implemented and working: + o modular design (separate function for each state) + o error logging via syslog() + o extensive debugging facilities (compile time option) + o detection of failing getty processes (exit status and runtime) + o if a getty process is failing repeatedly, retry it after + configurable time intervals. + o recovery from non-fatal errors such as out of memory or + fork failed. + o a manual page + o most parameters can be changed from a configuration file (default + startup state, retry time etc) + o some system parameters can also be set from the configuration file + (default process limits, initial environment variables) + o fully compatible with old init (if config file does not exist) +planned but not yet implemented: + o redirecting error messages to stderr instead of syslog if init + is executed by the user (Syntaxcheck) + o support for ty_window (for window systems) + o maybe sometime support for /etc/inittab + +What you can define (Add "CFLAGS += -D..." to the Makefile): + -DDEBUG enables debugging facilities: + you can increment debug level with debug keyword in + config file, -d option, or by sending SIGUSR1 to init. + SIGUSR2 sets debug level to 0. + -DUNTRUSTED debugging code: on SIGINT it execs /sbin/init.ori + (do this only in singleuser mode !) + -DCONFIGURE on startup (and when requested), read /etc/init.conf + and set parameters in it. + -DFAKE_SYSLOG do not use syslog, but write to /dev/console. + This makes it MUCH smaller ! + -DSMALL do not use perror, but write error codes. + Makes it even smaller, but requires -DFAKE_SYSLOG. +For the floppy version, compile this with -DFAKE_SYSLOG -DSMALL. If compiled +with gcc1, it is not bigger than the original init (20k). + +If you want to use CONFIGURE, you also have to copy init.conf to /etc . + +I'm interested in any feedback you have ! + + Christoph Robitschko + chmr@edvz.tu-graz.ac.at diff --git a/sbin/init.chmr/cf_defs.h b/sbin/init.chmr/cf_defs.h new file mode 100644 index 0000000000..98648fb877 --- /dev/null +++ b/sbin/init.chmr/cf_defs.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 1993 Christoph M. Robitschko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christoph M. Robitschko + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * cf_defs.h + * typedefs and defines for the configuration subsystem. + */ + +struct Command { + char *name; /* command name / value description */ + short level; /* level of command nesting */ + u_char type; /* type of entry (exact, string, int) */ + u_char flags; /* additional flags */ + void *var; /* Pointer to variable to assign to */ + union { + int intval; /* Integer value to assign to *var */ + char *chval; /* String ... */ + } val; + }; + + +/* defines for the level field */ +#define SUB 0x100 /* There must be another argument */ +#define OPT 0x200 /* There can be another argument (not yet impl) */ +#define LEVEL 0x0ff /* Flags masked off */ + +/* Valid types */ +#define T_EX 0x00 /* Must match exact */ +#define T_STR 0x01 /* Matches any string */ +#define T_INT 0x02 /* Matches an integer */ +#define T_BYTE 0x03 /* Matches a byte expression (1m = 1024k == 1024 == 1048576b) */ +#define T_TIME 0x04 /* Matches a time expression (1:00:00 == 1h == 60m == 3600s == 3600) */ + +/* values for the flags field */ +#define NRAISE 0x01 /* Variable can be lowered but not raised */ +#define MAXVAL 0x02 /* intval contains the maximum allowed value */ +#define CFUNC 0x04 /* Call the function *(void (*)())var(argc, argv) */ + + +#define NOVAR NULL, {0} diff --git a/sbin/init.chmr/cf_table.c b/sbin/init.chmr/cf_table.c new file mode 100644 index 0000000000..94a31e49fd --- /dev/null +++ b/sbin/init.chmr/cf_table.c @@ -0,0 +1,324 @@ +/* + * Copyright (c) 1993 Christoph M. Robitschko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christoph M. Robitschko + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * cf_table.c + * The table of words that the configuration subsystem understands, + * along with local support functions. + */ + +#ifdef CONFIGURE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "init.h" +#include "prototypes.h" +#include "cf_defs.h" + + +/* Prototypes for static functions */ +static int conf_env(char *, int, int, char **, int); +static int conf_include(char *, int, int, char **, int); +static int conf_waittimes(char *, int, int, char **, int); + + +/* static variables */ +static struct rlimit proclimits[RLIM_NLIMITS]; + + +/* extern variables, referenced inb Commands table */ +extern int startup_single; +extern struct ttyent RCent_auto; +extern struct ttyent RCent_fast; +extern struct ttyent Single_ent; +extern int retrytime; +extern int timeout_m2s_KILL; +extern int timeout_m2s_TERM; +extern int checkstatus; +extern int checktime; + + + +#ifdef __STDC__ +# define CURLIM(_lim_) &proclimits[RLIMIT_##_lim_].rlim_cur +# define MAXLIM(_lim_) &proclimits[RLIMIT_##_lim_].rlim_max +#else +# define CURLIM(_lim_) &proclimits[RLIMIT_/**/_lim_].rlim_cur +# define MAXLIM(_lim_) &proclimits[RLIMIT_/**/_lim_].rlim_max +#endif + + +/* + * The table of all known words and actions + */ + +const struct Command Commands[] = { + +#ifdef DEBUG + {"debug_level", 0+SUB, T_EX, 0, NOVAR}, + {"LEVEL", 1, T_INT, MAXVAL, &debug, {5}}, +#endif + + {"startup_state", 0+SUB, T_EX, 0, NOVAR}, + {"singleuser_mode", 1, T_EX, 0, &startup_single, {1}}, + {"multiuser_mode", 1, T_EX, 0, &startup_single, {0}}, + + {"setenv", 0+SUB, T_EX, 0, NOVAR}, + {"VAR", 1+SUB, T_STR, 0, NOVAR}, + {"STRING", 2, T_STR, CFUNC, conf_env, {0}}, + + {"include", 0+SUB, T_EX, 0, NOVAR}, + {"FILE", 1, T_STR, CFUNC, conf_include, {0}}, + + {"singleusershell", 0+SUB, T_EX, 0, NOVAR}, + {"COMMAND", 1, T_STR, 0, &Single_ent.ty_getty, {0}}, + {"singleuserterminal", 0+SUB, T_EX, 0, NOVAR}, + {"TYPE", 1, T_STR, 0, &Single_ent.ty_type, {0}}, + {"singleuserdevice", 0+SUB, T_EX, 0, NOVAR}, + {"DEVICE", 1, T_STR, 0, &Single_ent.ty_name, {0}}, + + {"autobootcommand", 0+SUB, T_EX, 0, NOVAR}, + {"COMMAND", 1, T_STR, 0, &RCent_auto.ty_getty, {0}}, + {"fastbootcommand", 0+SUB, T_EX, 0, NOVAR}, + {"COMMAND", 1, T_STR, 0, &RCent_fast.ty_getty, {0}}, + + {"timeout", 0+SUB, T_EX, 0, NOVAR}, + {"shutdown", 1+SUB, T_EX, 0, NOVAR}, + {"sigterm", 2+SUB, T_EX, 0, NOVAR}, + {"TIMEOUT", 3, T_INT, MAXVAL, &timeout_m2s_TERM, {300}}, + {"sigkill", 2+SUB, T_EX, 0, NOVAR}, + {"TIMEOUT", 3, T_INT, MAXVAL, &timeout_m2s_KILL, {300}}, + {"error-retry", 1+SUB, T_EX, 0, NOVAR}, + {"TIME", 2, T_TIME, 0, &retrytime, {0}}, + + {"respawn", 0+SUB, T_EX, 0, NOVAR}, + {"checkstatus", 1+SUB, T_EX, 0, NOVAR}, + {"yes", 2, T_EX, 0, &checkstatus, {1}}, + {"on", 2, T_EX, 0, &checkstatus, {1}}, + {"no", 2, T_EX, 0, &checkstatus, {0}}, + {"off", 2, T_EX, 0, &checkstatus, {0}}, + {"checktime", 1+SUB, T_EX, 0, NOVAR}, + {"yes", 2, T_EX, 0, &checktime, {DEF_CHECKTIME}}, + {"on", 2, T_EX, 0, &checktime, {DEF_CHECKTIME}}, + {"no", 2, T_EX, 0, &checktime, {0}}, + {"off", 2, T_EX, 0, &checktime, {0}}, + {"TIMEOUT", 2, T_TIME, MAXVAL, &checktime, {300}}, + {"waittimes", 1+SUB, T_EX, 0, NOVAR}, + {"delete_list", 2, T_EX, CFUNC, conf_waittimes, {1}}, + {"add_list", 2+SUB, T_EX, 0, NOVAR}, + {"TIME", 3, T_TIME, CFUNC, conf_waittimes, {2}}, + + {"limit", 0+SUB, T_EX, 0, NOVAR}, + {"cputime", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, CURLIM(CPU), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_TIME, 0, CURLIM(CPU), {0}}, + {"filesize", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, CURLIM(FSIZE), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_BYTE, 0, CURLIM(FSIZE), {0}}, + {"datasize", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, CURLIM(DATA), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_BYTE, 0, CURLIM(DATA), {0}}, + {"stacksize", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, CURLIM(STACK), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_BYTE, 0, CURLIM(STACK), {0}}, + {"coredumpsize", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, CURLIM(CORE), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_BYTE, 0, CURLIM(CORE), {0}}, + {"memoryuse", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, CURLIM(RSS), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_BYTE, 0, CURLIM(RSS), {0}}, + {"memorylocked", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, CURLIM(MEMLOCK), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_BYTE, 0, CURLIM(MEMLOCK), {0}}, + {"maxprocesses", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, CURLIM(NPROC), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_INT, 0, CURLIM(NPROC), {0}}, + {"openfiles", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, CURLIM(OFILE), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_INT, 0, CURLIM(OFILE), {0}}, + + {"hardlimit", 0+SUB, T_EX, 0, NOVAR}, + {"cputime", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, MAXLIM(CPU), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_TIME, NRAISE, MAXLIM(CPU), {0}}, + {"filesize", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, MAXLIM(FSIZE), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_BYTE, NRAISE, MAXLIM(FSIZE), {0}}, + {"datasize", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, MAXLIM(DATA), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_BYTE, NRAISE, MAXLIM(DATA), {0}}, + {"stacksize", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, MAXLIM(STACK), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_BYTE, NRAISE, MAXLIM(STACK), {0}}, + {"coredumpsize", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, MAXLIM(CORE), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_BYTE, NRAISE, MAXLIM(CORE), {0}}, + {"memoryuse", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, MAXLIM(RSS), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_BYTE, NRAISE, MAXLIM(RSS), {0}}, + {"memorylocked", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, MAXLIM(MEMLOCK), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_BYTE, NRAISE, MAXLIM(MEMLOCK), {0}}, + {"maxprocesses", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, MAXLIM(NPROC), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_INT, NRAISE, MAXLIM(NPROC), {0}}, + {"openfiles", 1+SUB, T_EX, 0, NOVAR}, + {"unlimited", 2, T_EX, 0, MAXLIM(OFILE), {RLIM_INFINITY}}, + {"default", 2, T_EX, 0, NOVAR}, + {"LIMIT", 2, T_INT, NRAISE, MAXLIM(OFILE), {0}}, +}; +const int NCommands = (sizeof(Commands) / sizeof(struct Command)); + + +/* + * INIT-specific functions for configuration + */ + + +/* + * SETCONF + * called in each new process to set up configured things + */ +void +setconf(void) +{ +int f; + + Debug (4, "Setting configuration"); + for (f=0; f< RLIM_NLIMITS; f++) + (void) setrlimit(f, &proclimits[f]); + Debug(4, "Setting configuration completed"); +} + + + +/* + * GETCONF + * prepare for configuration + * called once before configure(). + */ +void +getconf(void) +{ +int f; + + for (f=0; f< RLIM_NLIMITS; f++) + (void) getrlimit(f, &proclimits[f]); +} + + + +/* + * CHECKCONF + * check configured things etc. + * called once after configure(). + */ +void +checkconf(void) +{ +int f; + + /* Do not allow soft limits to be higher than the hard limits */ + for (f=0; f proclimits[f].rlim_max) + proclimits[f].rlim_cur = proclimits[f].rlim_max; +} + + +/* + * Support functions, referenced in Commands table + */ + +/* Set an environment variable from the configuration file */ +static int +conf_env(configfile, lineno, argc, argv, what) +char *configfile; +int lineno; +int argc; +char **argv; +int what; +{ + iputenv(argv[1], argv[2]); + return(0); +} + +/* Include another configuration file */ +static int +conf_include(configfile, lineno, argc, argv, what) +char *configfile; +int lineno; +int argc; +char **argv; +int what; +{ + configure(argv[1]); + return(0); +} + + +/* Configure the times to wait before restarting a getty that has failed */ +static int +conf_waittimes(configfile, lineno, argc, argv, what) +char *configfile; +int lineno; +int argc; +char **argv; +int what; +{ + Debug(0, "conf_waittimes not yet implemented."); + return(0); +} + +#endif diff --git a/sbin/init.chmr/configure.c b/sbin/init.chmr/configure.c new file mode 100644 index 0000000000..367c4f0310 --- /dev/null +++ b/sbin/init.chmr/configure.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 1993 Christoph M. Robitschko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christoph M. Robitschko + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * configure.c + * the main part of the configuration subsystem. + */ + +#ifdef CONFIGURE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "init.h" +#include "prototypes.h" +#include "cf_defs.h" + + + +/* Prototypes for local functions: */ +static int evaluate_line(char *, int, int, char **, int); +static int parseline(char *, int, int, char **); + + +char *fgetline(FILE *, size_t *); /* XXX */ + + +extern const struct Command Commands[]; +extern const int NCommands; + + + +/* + * CONFIGURE + * read a configuration file + */ +void +configure(filename) +char *filename; +{ +static int ncalled = 0; +FILE *cf; +char *line, *s, *errmsg; +int lineno; +char **cline; +int argc; + + + Debug (1, "Configuring from file %s", filename); + if (++ncalled > 50) { + syslog(LOG_ERR, "include loop in configuration files !"); + ncalled --; + return; + } + + cf = fopen(filename, "r"); + if (!cf) { + Debug(1, "Could not open config file %s: %m", filename); + ncalled --; + return; + } + Debug(1, "Config file %s opened.", filename); + + lineno = 0; + while ((line = fgetline(cf, (size_t *)0))) { + lineno ++; + if (*line == '#') + continue; /* Skip comment line */ + for (s = line; *s; s++) + if ((*s != ' ') && (*s != '\t')) + break; + if (!*s) + continue; /* Skip empty line */ + cline = string_to_argv(line, &argc, &errmsg); + if (errmsg) + syslog(LOG_ERR, "%s line %d: %s", filename, lineno, errmsg); + if (cline && argc > 0) + (void)parseline(filename, lineno, argc, cline); + + } + fclose(cf); + ncalled --; +} + + +/* + * Local support functions + */ + +/* PARSELINE -- the obvious */ +static int +parseline(configfile, linenumber, argc, argv) +char *configfile; +int linenumber; +int argc; +char **argv; +{ +int f, level; +int nmatch, wmatch; +char comline[1024]; /* XXX should test for overflow */ + + + comline[0] = '\0'; + /* Search through the Commands list; + * Each argv[level] is matched against a list of Commands of the same level, + * which must be subcommands of argv[level-1] if level > 0. + + * There are two types of Matches: T_EX 'Exact match' means that argv[level] + * is an abbreviation of Commands[f].name, case ignored. + * T_STR, T_NUM etc match always (the validity of argv[level] is checked + * when it is evaluated, later), unless there has already been an exact + * match for the same argv[level]. + + * Note that the search is not aborted after a match is found, but + * continues up to the end of the list or the end of the scope of the + * previous (sub)command (i.e. if Commands[f].level < level). + * At that point, exactly one match must have been found. The search + * is then restarted for the next level, beginning at the previous match. + */ + + for (f=nmatch=wmatch=level=0; f= argc) { + syslog(LOG_ERR, "%s line %d: required parameter missing to command \"%s\"", configfile, linenumber, comline); + return (-1); + } + f = wmatch; + nmatch = 0; + } else { /* XXX OPT not yet implemented */ + if (level +1 != argc) { + syslog(LOG_ERR, "%s line %d: too many arguments to command \"%s\"", configfile, linenumber, comline); + return(-1); + } + Debug(3, "%s line %d: OK \"%s\"", configfile, linenumber, comline); + return(evaluate_line(configfile, linenumber, argc, argv, wmatch)); + } + break; + } + /* NOTREACHED */ + } + return(-1); /* to stop gcc from complaining */ +} + + + +/* + * Evaluate_line + * Execute the action (set a variable, call a function) specified in the + * Commands entry of the *last* argument on the config line. + */ +/* XXX no support for string values; only last argument evaluated */ +static int +evaluate_line(cfgfile, lineno, argc, argv, wmatch) +char *cfgfile; +int lineno, argc, wmatch; +char **argv; +{ +char *s, *arg; +long tmp, tmp2; + + + arg = argv[argc-1]; + if (Commands[wmatch].flags & CFUNC) { /* Call the function */ + int (* func)() = Commands[wmatch].var; + return(func(cfgfile, lineno, argc, argv, Commands[wmatch].val)); + } + + switch (Commands[wmatch].type) { + case T_STR: /* set var to String */ + s = newstring(arg); + if (s) /* XXX ohne Geld ka Musi */ + *(char **)Commands[wmatch].var = s; + return(0); /* XXX Flags handling */ + case T_EX: /* Copy val to *var */ + tmp = Commands[wmatch].val.intval; + break; + case T_INT: /* Arg is an integer */ + tmp = strtol(arg, &s, 0); + if ((!*arg) || (*s)) { + syslog(LOG_ERR, "%s line %d: \"%s\" is not an integer", cfgfile, lineno, arg); + return(-1); + } + break; + case T_BYTE: /* Arg is a byte limit */ + tmp = strtol(arg, &s, 0); + if ((tmp < 0) || (*arg > '9') || (*arg < '0')) + goto notbyte; + switch (*s) { + case '\0': + tmp *= 1024; + break; + case 'm': case 'M': + tmp *= 1024; + /* no break */ + case 'k': case 'K': + tmp *= 1024; + /* no break */ + case 'b': case 'B': + if (!*++s) + break; + default: +notbyte: + syslog(LOG_ERR, "%s line %d: \"%s\" is not a valid bytecount", cfgfile, lineno, arg); + return(-1); + } + break; + case T_TIME: /* Arg is a time limit */ + tmp = strtol(arg, &s, 0); + if ((tmp < 0) || (*arg > '9') || (*arg < '0')) + goto nottime; + switch (*s) { + case '\0': + break; + case 'h': case 'H': + tmp *= 60; + /* no break */ + case 'm': case 'M': + tmp *= 60; + /* no break */ + case 's': case 'S': + if (!*++s) + break; + case ':': + if ((tmp < 0) || (tmp > 59)) goto nottime; + tmp *= 60; + if ((*++s < '0') || (*s > '9')) goto nottime; + tmp2 = strtol(s, &s, 0); + if ((tmp2 < 0) || (tmp2 > 59)) goto nottime; + tmp += tmp2; + if (!*s) break; + if (*s != ':') goto nottime; + tmp *= 60; + if ((*++s < '0') || (*s > '9')) goto nottime; + tmp2 = strtol(s, &s, 0); + if ((tmp2 < 0) || (tmp2 > 59)) goto nottime; + tmp += tmp2; + if (!*s) break; + /* no break */ + default: +nottime: + syslog(LOG_ERR, "%s line %d: \"%s\" is not a valid timespan", cfgfile, lineno, arg); + return(-1); + } + break; + default: + syslog(LOG_ERR, "internal error: unknown argument type.", cfgfile, lineno); + return(-1); + } + + if (Commands[wmatch].flags & MAXVAL) + if (tmp > Commands[wmatch].val.intval) { + syslog(LOG_ERR, "%s line %d: \"%s\" (%d) is bigger than the allowed maximum (%d)", cfgfile, lineno, arg, tmp, Commands[wmatch].val.intval); + return(-1); + } + if (Commands[wmatch].flags & NRAISE) + if (tmp > *(int *)(Commands[wmatch].var)) { + syslog(LOG_ERR, "%s line %d: Unable to raise limit beyond the current maximum (%d)", cfgfile, lineno, *(int *)Commands[wmatch].var); + return(-1); + } + + if (Commands[wmatch].var) + *(int *)Commands[wmatch].var = tmp; + return(0); +} + + +#endif /* CONFIGURE */ diff --git a/sbin/init.chmr/fake_syslog.c b/sbin/init.chmr/fake_syslog.c new file mode 100644 index 0000000000..c25b23670b --- /dev/null +++ b/sbin/init.chmr/fake_syslog.c @@ -0,0 +1,125 @@ +/* + * Warning: extremely XXX + * fake syslog to write to stderr, for standalone test version of init + */ + + +#ifdef FAKE_SYSLOG + + +#include +#include +#include +#include +#include + + +#ifdef TESTRUN +# define FAKE_LOGFILE "/dev/tty" +#else +# define FAKE_LOGFILE "/dev/console" +#endif + + +static char * +itoa(i) + int i; +{ + static char buf[12]; + char *s; + int minflg = 0; + + s = &buf[11]; + *s = '\0'; + if (i < 0) + minflg = 1, i = -i; + do { + *(--s) = (i % 10) + '0'; + i /= 10; + } while (i); + if (minflg) + *(--s) = '-'; + return(s); +} + + +static int _log_fd = 2; +static const char *_log_id = ""; + + +void +openlog(ident, logopt, facility) +const char *ident; +int logopt, facility; +{ + if (ident) + _log_id = ident; + if (_log_fd >= 0) + close(_log_fd); + _log_fd = open(FAKE_LOGFILE, O_WRONLY | O_NONBLOCK, 0); +} + + +void +vsyslog(int pri, const char *fmt, va_list ap) +{ + const char *s, *t; + int saved_errno = errno; + + if (write(_log_fd, "", 0) < 0) + openlog(NULL, 0, 0); + write(_log_fd, _log_id, strlen(_log_id)); + if (*_log_id) write(_log_fd, ": ", 2); + for (s=fmt; *s; s++) + if (*s == '%') + switch (*(++s)) { + case '\0': + s--; break; + case '%': + write(_log_fd, s, 1); break; + case 's': + t = va_arg(ap, char *); + write(_log_fd, t, strlen(t)); + break; + case 'm': +#ifdef SMALL + write(_log_fd, "Error ", 6); + t = itoa(saved_errno); +#else + t = strerror(saved_errno); +#endif + write(_log_fd, t, strlen(t)); + break; + case 'd': + t = itoa(va_arg(ap, int)); + write(_log_fd, t, strlen(t)); + break; + default: + write(_log_fd, s-1, 2); + } + else + write(_log_fd, s, 1); + write(_log_fd, "\n", 1); +} + + +void +syslog(int pri, const char *fmt, ...) +{ +va_list ap; + + va_start(ap, fmt); + vsyslog(pri, fmt, ap); + va_end(ap); +} + + +void +closelog(void) +{ + close(_log_fd); + _log_fd = -1; +} + + +#endif diff --git a/sbin/init.chmr/init.8 b/sbin/init.chmr/init.8 new file mode 100644 index 0000000000..6aea2f0966 --- /dev/null +++ b/sbin/init.chmr/init.8 @@ -0,0 +1,359 @@ +.\" Copyright (c) 1993 Christoph M. Robitschko +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Christoph M. Robitschko +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software withough specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.Dd May 20, 1993 +.Dt INIT 8 +.Os 386BSD +.Sh NAME +.Nm init +.Nd ancestor of all user-level processes +.Sh SYNOPSIS +.Nm init +.Op Fl s +.Op Fl f +.Op Fl d Ar level +.Op Fl C Ar file +.Op Fl S +.Sh DESCRIPTION +.Nm init +is started by the Operating System kernel as the first user-level process +(PID 1). +It creates more processes to execute the system startup script +.Pa /etc/rc +and enable users to login. If the system startup fails for any reason, +it gives the system administrator a shell on the console. +Note that you cannot execute the +.Nm init +program directly (except if you are a UNIX kernel, which seems unlikely). +.Pp +.Em Options +.Bl -tag -width Ds -offset indent +.It Fl s +Starts a singleuser shell instead of booting the system to multiuser mode +.It Fl f +Fast boot. Calls the +.Pa /etc/rc +script without the +.Sy autoboot +argument, which causes it to skip disk checks +.It Fl d Ar level +Sets the debug level to the specified number. +.It Fl C Ar file +Causes +.Nm init +to read its configuration from file +.Ar file +instead of the default /etc/init.conf . +.It Fl S +Only checks the configuration file for syntax errors and exits immediately +afterwards. +.El +.Pp +.Em Singleuser and multiuser modes +.Bd -filled -offset indent +.Nm +knows of two states: +.Em singleuser +and +.Em multiuser +\&. The normal state is multiuser, where system daemons such as +.Xr crond 8 +or +.Xr inetd 8 +are active and the system accepts logins from direct connect ttys (with +.Xr getty 8 +) and from the network. In singleuser mode, all daemons and user processes +terminated, and +.Nm +starts a shell on the system console. This shell can be used by the +system manager for administrative purposes. Because no proceses are running +that have files open, all file systems except the root file system can be +unmounted. +.Ed +.Pp +.Em System startup and shutdown +.Bd -filled -offset indent +When the system is booted, it creates process 1 and executes the file +.Pa /sbin/init +in it. The arguments that are given to the kernel are passed on to +the +.NM +program. Unless the +.Fl s +flag is specified, +.Nm +starts a shell to execute the +.Pa /etc/rc +script with the argument +.Sy "autoboot" +(Unless the +.Fl f +flag is specified, in which case the script is called without arguments). +This script checks and mounts file systems and starts +system daemons. If the script fails (exits with a status unequal to zero), +a shell is started on the console to enable the system administrator to +cure the problem. When this shell terminates, +.Nm +starts over by executing +.Pa /etc/rc +again (without arguments). When finally this script terminates with +an exit status of zero (signalling success), +.Nm +starts multiuser mode by reading the file +.Pa /etc/ttys +and starting getty processes for the terminals that are marked active +in this file. +.Pp +To bring the system from multiuser to singleuser mode, you can use +the +.Xr shutdown 8 +program. +.Ed +.Pp +.Em Signals +.Bd -filled -offset indent +The following list of signals is only here for completeness; You should +not send any signals to +.Nm init +directly, but instead use the +.Xr shutdown 8 +command to perform a state change. +.Bl -tag -width SIGTERM +.It SIGTERM +go to singleuser mode immediately. All processes are terminated, and then +a singleuser shell is started on the console. +.It SIGHUP +Reread the +.Pa /etc/ttys +file. +.It SIGTSTP +Drain the system. No new processes are created. The only way out of +this pseudo-state is another SIGHUP or SIGTERM signal. +.It SIGTTIN +Re-read the configuration file. Only valid when in Singleuser mode. +.It SIGUSR1 +Increase debugging level (if debugging is compiled in) +.It SIGUSR2 +Turn off debugging +.El +.Ed +.Pp +.Em Configuration +.Bd -filled -offset indent +Upon startup, +.Nm +reads the file +.Pa /etc/init.conf +or the file specified with the +.Fl C +option. +Each line in this file specifies a thing to configure. +Empty lines and lines with a hash character +.Sy # +as the first character are ignored. +.Bd -literal +limit LIMIT VALUE +hlimit LIMIT VALUE +.Ed +.Bd -filled -offset indent +set a soft or hard limit, respectively. LIMIT can be one of +.Sy cputime , +.Sy filesize , +.Sy datasize , +.Sy stacksize , +.Sy coredumpsize , +.Sy memoryuse , +.Sy memorylocked , +.Sy maxproc , +.Sy openfiles . +The VALUE is specified in bytes or seconds. See +.Xr setrlimit 2 +for more information. +.Pp +Note that you cannot raise hard limits. +Note also that the specified limits are not used for +.Nm init +itself, but for all processes it creates. +.Ed +.Bd -literal +setenv VARIABLE "VALUE" +.Ed +.Bd -filled -offset indent +Sets the specified environment-variable VAR with the specified VALUE. +VALUE can be ommitted. This setting is valid for all processes +on the system, except +.Nm init +itself. +.Ed +.Bd -literal +debug LEVEL +.Ed +.Bd -filled -offset indent +Sets the debug level of init to the specified value. Note that a level +specified with the +.Fl d +option takes precedence over this. +.Ed +.Bd -literal +startup_state { singleuser | multiuser } +.Ed +.Bd -filled -offset indent +Sets the default runlevel to singleuser or multiuser mode, respectively. +If +.Fl s +is specified, it always goes to singleuser mode. +.Ed +.Bd -literal +include "FILE" +.Ed +.Bd -filled -offset indent +Reads further configuration instructions from file "FILE" and continues in the +current file afterwards. Include commands can be nested up to a level of 50. +.Ed +.Bd -literal +singleusershell "COMMAND" +.Ed +.Bd -filled -offset indent +Lets you specify a command to execute as a singleuser shell. Note that the +first argument you specify is the 0th argument passed to the program. +Default is "/bin/sh -". +.Ed +.Bd -literal +singleuserterminal "TYPE" +.Ed +.Bd -filled -offset indent +Lets you specify a terminal type that is passed in the TERM environment +variable to the singleusershell. Default is "pc3". +.Ed +.Bd -literal +singleuserdevice "DEVICE" +.Ed +.Bd -filled -offset indent +Lets you specify a terminal device where the singleusershell will be started. +Default is "/dev/console". +.Ed +.Bd -literal +autobootcommand "COMMAND" +fastbootcommand "COMMAND" +.Ed +.Bd -filled -offset indent +Lets you specify a command that is executed when the system goes to multiuser +mode. The difference between the two is that autobootcommand is only executed +when the system starts multiuser mode immediately after boot, and only if +.Fl f +has not been specified. +If the system has already been in singleuser mode or +.Fl f +was used, fastbootcommand is used instead. The defaults are +"/bin/sh sh /etc/rc autoboot" for autobootcommand, and "/bin/sh sh /etc/rc" +for fastbootcommand. +.Ed +.Bd -literal +timeout shutdown sigterm TIME +timeout shutdown sigkill TIME +.Ed +.Bd -filled -offset indent +Normally, if you shutdown to singleuser mode, +.Nm init +sends a SIGTERM signal to all processes to give them a chance to +terminate gracefully, waits 10 seconds, then sends a SIGKILL to all +remaining processes and waits up to 30 seconds. You can change these timeouts +if you like. +.Ed +.Bd -literal +timeout error-retry TIME +.Ed +.Bd -filled -offset indent +If +.Nm init +encouters a serious problem (such as "fork failed" or "out of memory"), it +does not terminate, but instead continues its work and retries the operation +at a later time. The default is to retry after 300 seconds. +.Ed +.Bd -literal +respawn checkstatus { yes | no } +.Ed +.Bd -filled -offset indent +Specifies whether +.Nm init +should check the termination status of its children, in order to see if these +children fail (e.g. a getty on a non-existent device). The default is yes, +but you may want to switch it off if you have programs that don't return any +useful exit status. +.Ed +.Bd -literal +respawn checktime { yes | no | TIME } +.Ed +.Bd -filled -offset indent +Specifies whether or not +.Nm init +should assume that children that did not live longer than TIME seconds +were failing. The default is yes, which is equivalent to a time of 5 seconds. +0 is equivalent to no. +.Ed +.Sh ENVIRONMENT +The UNIX kernel sets up the environment for +.Nm init +as part of its initialisation. +Specifically, +.Nm +is called without any files open. +.Sh FILES +.Bl -tag -width "/etc/init.conf" -compact +.It Pa /etc/init.conf +Configuration file for +.Nm init +(See +.Sx DESCRIPTION +above) +.It Pa /etc/rc +System Startup Script +.It Pa /etc/ttys +specifies on which terminals to create login processes +.El +.Sh DIAGNOSTICS +.Nm init +logs all messages via +.Xr syslogd 8 +with the +.Sy LOG_DAEMON +facility. +If it cannot open a connectoin to a syslogd, it prints the message +to the console. Fatal errors (such as a segmentaion fault) cause +the kernel to panic with the message "init died". +.Sh SEE ALSO +.Xr rc 8 , +.Xr getty 8 , +.Xr syslogd 8 , +.Xr ttys 5 , +.Xr shutdown 8 +.Sh AUTHOR +This incarnation of +.Nm init +was written by Christoph Robitschko for the 386BSD project. +.Sh BUGS +\&... should be reported to diff --git a/sbin/init.chmr/init.c b/sbin/init.chmr/init.c new file mode 100644 index 0000000000..5e20400c46 --- /dev/null +++ b/sbin/init.chmr/init.c @@ -0,0 +1,828 @@ +/* + * Copyright (c) 1993 Christoph M. Robitschko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christoph M. Robitschko + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * init.c + * Main program for init. + * Also contains definitions for global variables etc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "init.h" +#include "prototypes.h" +#include "libutil.h" + + +/* global variables, preset to their defaults */ +int timeout_m2s_TERM = INIT_M2S_TERMTO; +int timeout_m2s_KILL = INIT_M2S_KILLTO; +int retrytime = RETRYTIME; +int startup_single = 0; +int checkonly = 0; +int force_single = -1; +#ifdef DEBUG +int force_debug = -1; +int debug = DEBUG_LEVEL; +#endif +#ifdef CONFIGURE +char *config_file = INIT_CONFIG; +#endif + +static ttytab_t *ttytab = (ttytab_t *)0; +static callout_t *callout_tab = (callout_t *)0; +static callout_t *callout_free = (callout_t *)0; +static int callout_nfree = 0; +sigset_t block_set; +jmp_buf boing_singleuser, + boing_single2multi, + boing_multiuser, + boing_multi2single, + boing_waitforboot, + *boing_m2stimeout; + +static enum { SINGLEUSER, MULTIUSER, SINGLE2MULTI, MULTI2SINGLE } + State; + +struct ttyent RCent_auto = { + "console", + "/bin/sh sh /etc/rc autoboot", + "dumb", + TTY_ON | TTY_SECURE, + 0, + 0 + }; +struct ttyent RCent_fast = { + "console", + "/bin/sh sh /etc/rc", + "dumb", + TTY_ON | TTY_SECURE, + 0, + 0 + }; +struct ttyent Single_ent = { + "console", + "/bin/sh -", + "pc3", + TTY_ON | TTY_SECURE, + 0, + 0 + }; +static struct ttyent *RCent = &RCent_auto; +static struct ttyent *Singlesh = &Single_ent; + + + + +/********************************************************** + * Main * + **********************************************************/ +void +main(argc, argv) +int argc; +char **argv; +{ + + + /* make it a session leader */ + (void) setsid(); + + /* initialize syslog */ + openlog("init", LOG_CONS | LOG_PID, LOG_DAEMON); + + + /* parse command line */ + while(argc > 1) { + if(!strcmp(argv[1], "-s")) /* Singleuser */ + force_single = startup_single = 1; + + else if(!strcmp(argv[1], "-f")) /* Fastboot */ + RCent = &RCent_fast; + +#ifdef DEBUG + else if (!strcmp(argv[1], "-d")) /* Debug level */ + if (argc > 2) { + if ((force_debug = str2u(argv[2])) >= 0) { + debug = force_debug; + argc --; argv++; + } else + syslog(LOG_ERR, "option -d needs positive integer argument"); + } else + syslog(LOG_ERR, "option -d needs an argument"); +#endif +#ifdef CONFIGURE + else if (!strcmp(argv[1], "-C")) /* Configuration file */ + if (argc > 2) { + config_file = argv[2]; + argc--; argv++; + } else + syslog(LOG_ERR, "option -C needs an argument"); + + else if (!strcmp(argv[1], "-S")) /* Syntaxcheck only */ + checkonly = 1; +#endif + else if (!strcmp(argv[1], "-")) /* ignore this */ + ; + else if (!strcmp(argv[1], "--")) /* ... and this */ + ; + else + syslog(LOG_ERR, "unknown option \"%s\"", argv[1]); + argc--; argv++; + } + + +#ifndef TESTRUN + /* did some idiot try to run init ? */ + if((getpid() != 1) && !checkonly) { + const char errmsg[] = "init: system daemon, not runnable by user\r\n"; + write(2, errmsg, strlen(errmsg)); + exit(0xff); + } +#endif /* ! TESTRUN */ + + +#ifdef CONFIGURE + /* read the default configuration (limits etc) */ + getconf(); + /* read configuration file */ + configure(config_file); + /* set global configuration parameters */ + checkconf(); + if (checkonly) + exit(0); + + /* values configured by command-line arguments take precedence */ + /* over values in the config file */ +# ifdef DEBUG + if (force_debug >= 0) + debug = force_debug; +# endif + if (force_single >= 0) + startup_single = force_single; +#endif + + /* + * initialize callout table + */ + allocate_callout(); + + /* + * initialize the longjmp buffers; + * after a longjmp(), the appropriate function is called and + * does not return. + */ + if (setjmp(boing_singleuser)) + singleuser(); + if (setjmp(boing_single2multi)) + single2multi(); + if (setjmp(boing_multiuser)) + multiuser(); + if (setjmp(boing_multi2single)) + multi2single(); + if (setjmp(boing_waitforboot)) + waitforboot(); + + /* install signal handlers for catched signals */ + signal(SIGTSTP, sig_tstp); + signal(SIGTERM, sig_term); + signal(SIGHUP, sig_hup); + signal(SIGALRM, sig_alrm); +#ifdef DEBUG + signal(SIGUSR1, sig_usr1); + signal(SIGUSR2, sig_usr2); +#endif +#ifdef CONFIGURE + signal(SIGTTIN, sig_ttin); +#endif +#if defined (UNTRUSTED) && !defined (TESTRUN) + signal(SIGINT, sig_int); +#endif + + /* define Set of signals to be blocked for critical parts */ + (void) sigemptyset (&block_set); + (void) sigaddset (&block_set, SIGTSTP); + (void) sigaddset (&block_set, SIGTERM); + (void) sigaddset (&block_set, SIGHUP); + (void) sigaddset (&block_set, SIGUSR1); + (void) sigaddset (&block_set, SIGUSR2); + (void) sigaddset (&block_set, SIGALRM); + + /* Action ! */ + if (startup_single) + longjmp(boing_singleuser, 1); + else + longjmp(boing_single2multi, 1); + + /* NOTREACHED */ +} + + + +/********************************************************** + * Signal Handlers * + **********************************************************/ + +/* TSTP -- wait for children, but don't spawn new ones */ +void +sig_tstp(sig) +int sig; +{ + Debug(3, "TSTP Signal received"); + longjmp(boing_waitforboot, 2); +} + + +/* TERM -- Go to singleuser mode */ +void +sig_term(sig) +int sig; +{ + Debug(3, "Terminate Signal received"); + longjmp(boing_multi2single, 2); +} + + +/* HUP -- Reread /etc/ttys file */ +void +sig_hup(sig) +int sig; +{ + Debug(3, "Hangup Signal received"); + if (State == MULTIUSER) + longjmp(boing_multiuser, 2); +} + + +/* ALRM -- Timeout Signal */ +void +sig_alrm(sig) +int sig; +{ + Debug(3, "Alarm Signal received"); + if (callout_tab) + do_callout(); +} + + +#ifdef DEBUG +/* USR1 -- Increment debugging level */ +void +sig_usr1(sig) +int sig; +{ + debug++; + if (debug == 1) + Debug(0, "I will chat like a gossip"); + else + Debug(0, "I will chat like %d gossips", debug); +} + +/* USR2 -- switch off debugging */ +void +sig_usr2(sig) +int sig; +{ + Debug(0, "OK, I will shut up now."); + debug = 0; +} +#endif + +#if defined (UNTRUSTED) && !defined (TESTRUN) +/* INT -- execute original init (Signal can be generated from the kernel + debugger with 'call pfind(1)' and then 'call psignal(XXXXX, 2)' + where XXXXX is the return value of the pfind call). + This isn't very pretty, but it saved me from booting from floppy + disk many times. */ +void +sig_int(sig) +int sig; +{ + Debug(0, "Interrupt signal received; trying to execute /sbin/init.ori"); + Debug(0, "(Are you not satisfied with me ?)"); + kill (-1, SIGKILL); + execl("/sbin/init.ori", "init", "-s", 0); + Debug(0, "Could not execute /sbin/init.ori (%m)"); + longjmp(boing_multi2single, 1); +} +#endif /* UNTRUSTED */ + + +#ifdef CONFIGURE +/* TTIN -- reread configuration file; only valid when in singleuser mode */ +void +sig_ttin(sig) +int sig; +{ + if (State == SINGLEUSER) { + blocksig(); + Debug(0, "TTIN signal received, re-reading configuration file"); + setconf(); + configure(config_file); + checkconf(); + unblocksig(); + } else + syslog(LOG_NOTICE, "TTIN signal received, but not in singleuser mode"); +} +#endif + + + +/********************************************************** + * SingleUserMode * + **********************************************************/ + +void +singleuser(void) +{ +int status; + + + State = SINGLEUSER; + clear_callout(); + Debug(1, "Entered State singleuser"); + + if (ttytab) { + syslog(LOG_ERR, "internal error: multiple users in singleusermode"); + longjmp(boing_multi2single, 1); + } + + + RCent = &RCent_fast; + blocksig(); + ttytab = ent_to_tab(Singlesh, (ttytab_t *)0, ttytab, INIT_NODEV | INIT_OPEN | INIT_ARG0); + unblocksig(); + if (do_getty(ttytab, 0) < 0) { + syslog(LOG_EMERG, "Unable to start singleuser shell"); + sync(); sleep(1); + _exit(1); /* What else should we do about this ? */ + } + +#ifndef TESTRUN + while(wait(&status) != ttytab->pid); +#else + scanf("%d\n", &status); /* XXX */ +#endif + Debug(1, "Singleusershell exited with status %d", status); + + + blocksig(); + ttytab = free_tty(ttytab, ttytab); + unblocksig(); + + longjmp(boing_single2multi, 1); + /* NOTREACHED */ +} + + + +/********************************************************** + * Single 2 Multi * + **********************************************************/ + +void +single2multi(void) +{ +int status; + + + State = SINGLE2MULTI; + clear_callout(); + Debug(1, "Entered State single2multi"); + + if (ttytab) { + syslog(LOG_ERR, "internal error: users in single2multi"); + longjmp(boing_multi2single, 1); + } + + + blocksig(); + ttytab = ent_to_tab(RCent, (ttytab_t *)0, ttytab, INIT_NODEV | INIT_OPEN | INIT_ARG0); + unblocksig(); + if (do_getty(ttytab, 0) < 0) { + syslog(LOG_ERR, "Unable to execute /etc/rc"); + ttytab = free_tty(ttytab, ttytab); + longjmp(boing_singleuser, 2); + } + +#ifndef TESTRUN + while(wait(&status) != ttytab->pid); +#else + scanf("%d\n", &status); +#endif + Debug(1, "/etc/rc exited with status %d", status); + + + blocksig(); + ttytab = free_tty(ttytab, ttytab); + unblocksig(); + + if (status) + longjmp(boing_singleuser, 1); + else { + logwtmp("~", "reboot", ""); + longjmp(boing_multiuser, 1); + } + /* NOTREACHED */ +} + + + +/********************************************************** + * WaitForBoot * + **********************************************************/ + +void +waitforboot(void) +{ +int status; +pid_t pid; +ttytab_t *tt; + + + + /* Note that the State variable is not set here */ + clear_callout(); + Debug(1, "Entered State waitforboot"); + + while (1) { + pid = wait(&status); + if (pid < 0) + pause(); + else { + Debug(4, "Process %d exited with status %d", pid, status); + for (tt=ttytab; tt; tt = tt->next) + if (tt->pid == pid) { + blocksig(); + ttytab = free_tty(ttytab, tt); + unblocksig(); + break; + } + } + } + /* NOTREACHED */ +} + + + +/********************************************************** + * MultiUser * + **********************************************************/ + +void +multiuser(void) +{ +ttytab_t *tt; +struct ttyent *tent; +int pid; +int status; + + + + State = MULTIUSER; + clear_callout(); + Debug(1, "Entered State multiuser"); + + /* First, (re)build ttytab based on what is in /etc/ttys */ + blocksig(); + setttyent(); + + for (tt = ttytab; tt; tt = tt->next) + tt->intflags &= ~(INIT_SEEN | INIT_CHANGED | INIT_NEW); + + while ((tent = getttyent())) + if (tent->ty_status & TTY_ON) { + for (tt = ttytab; tt; tt = tt->next) + if (!strcmp(tent->ty_name, tt->name)) + break; + ttytab = ent_to_tab(tent, tt, ttytab, 0); + } + + unblocksig(); + + /* Kill the processes whose entries are deleted or changed */ + /* Also start the getty process on the lines that were just added */ + for (tt = ttytab; tt; tt = tt->next) + if (!(tt->intflags & INIT_SEEN)) { + Debug(5, "killing %s (PID %d): Not seen", tt->name, tt->pid); + kill (tt->pid, SIGKILL); + tt->intflags |= INIT_DONTSPAWN; + } + else if (tt->intflags & INIT_NEW) + (void)do_getty(tt, 0); + else if (tt->intflags & INIT_CHANGED) { + Debug(5, "killing %s (PID %d): Changed", tt->name, tt->pid); + kill (tt->pid, SIGKILL); + } + else if (tt->intflags & INIT_FAILSLEEP) { + Debug(5, "continuing %s (PID %d)", tt->name, tt->pid); +#define UNSLEEP(a) + UNSLEEP(tt->pid); + tt->intflags &= ~INIT_FAILED | INIT_FAILSLEEP; + do_getty(tt, 0); + } + + + /* Now handle terminating children and respawn gettys for lines */ + while (1) { + pid = wait(&status); + if (pid < 0) { + switch (errno) { + case EINTR: break; + case ECHILD: + syslog(LOG_ERR, "wait() found no child processes -- going singleuser."); + longjmp(boing_multi2single, 2); + default: + syslog(LOG_ERR, "wait() failed: %m"); + sleep(5); + } + } else { + Debug(4, "Process %d terminated with status %d", pid, status); + for (tt = ttytab; tt; tt = tt->next) + if (pid == tt->pid) + if (tt->intflags & INIT_DONTSPAWN) { + blocksig(); + ttytab = free_tty(ttytab, tt); + unblocksig(); + } + else + (void)do_getty(tt, 0); + } + } + /* NOTREACHED */ +} + + + +/********************************************************** + * Multi2Single * + **********************************************************/ + +void +multi2single(void) +{ +static jmp_buf boing_timeout; +pid_t pid; +int status; +volatile int round; + + + State = MULTI2SINGLE; + clear_callout(); + Debug(1, "Entering State multi2single"); + + /* forget about the gettys */ + blocksig(); + while (ttytab) + ttytab = free_tty(ttytab, ttytab); + unblocksig(); + + /* + * round = 1: TERMinate children, then wait for them (default 10 seconds) + * round = 2: KILL children, then wait for them (default 30 seconds) + * round = 3: timeout expired; go to singleuser mode + */ + round = 0; + setjmp(boing_timeout); + boing_m2stimeout = &boing_timeout; + round ++; + if (round < 3) { + if (round == 1){ + Debug(3, "TERMinating processes"); + kill (-1, SIGTERM); + kill (-1, SIGCONT); + callout (timeout_m2s_TERM, CO_MUL2SIN, (void *)0); + } + else { + Debug(3, "KILLing processes"); + kill (-1, SIGKILL); + kill (-1, SIGCONT); + callout (timeout_m2s_KILL, CO_MUL2SIN, (void *)0); + } + while ((pid = wait(&status)) >= 0) + Debug(4, "Process %d exited with status %d", pid, status); + Debug(2, "Wait returned error: %m"); + } + else + syslog(LOG_NOTICE, "There are still some (hung) processes."); + + /* We don't need no steenkin timeout any more... */ + boing_m2stimeout = (jmp_buf *)0; + + /* Jump ! (Rein ins Vergnuegen) */ + longjmp(boing_singleuser, 2); + /* NOTREACHED */ +} + + + +/********************************************************** + * Callout * + * Schedule a retry operation for a later time * + **********************************************************/ +void +callout(when, type, arg) +unsigned int when; +retr_t type; +void *arg; +{ +callout_t *ntp, + *ctp, + *octp; + + + Debug(3, "Scheduling callout in %d seconds.", when); + blocksig(); + + /* find a free callout entry */ + if (callout_nfree <= CALLOUT_MINFREE) + allocate_callout(); + ntp = callout_free; + if (!ntp) { + syslog(LOG_WARNING, "Callout table is full !"); + return; + } + callout_free = ntp->next; + callout_nfree --; + + /* look at which point we put it in the callout list */ + when += (unsigned int)time(NULL); + for (octp = NULL, ctp = callout_tab; ctp; octp = ctp, ctp = ctp->next) + if (when < ctp->sleept) + break; + else + when -= ctp->sleept; + + + if (octp) + octp->next = ntp; + else + callout_tab = ntp; + ntp->next = ctp; + ntp->sleept = when; + if (ctp) ctp->sleept -= when; + ntp->what = type; + ntp->arg = arg; + + /* schedule alarm */ + when = callout_tab->sleept - (unsigned int)time(NULL); + if (when <= 0) { + Debug(4, "Next callout: NOW !"); + alarm(0); + kill (getpid(), SIGALRM); + } else + Debug(4, "Next callout in %d seconds.", when); + alarm(when); + + unblocksig(); +} + + +/********************************************************** + * Allocate_Callout * + * allocate (further) elements to the callout table * + *********************************************************/ +void +allocate_callout(void) +{ +callout_t *ntp; +int i; + + + ntp = malloc(sizeof(callout_t) * CALLOUT_CHUNK); + if (ntp) { + for (i=1; i< CALLOUT_CHUNK; i++) + ntp[i].next = &ntp[i-1]; + ntp->next = callout_free; + callout_free = &ntp[i-1]; + callout_nfree += CALLOUT_CHUNK; + } +} + + +/********************************************************** + * Clear_Callout * + * Removes all callout entries * + *********************************************************/ +void +clear_callout(void) +{ +callout_t *ctp, + *nctp; + + + if (callout_tab) + Debug(4, "All callouts for today cancelled."); + blocksig(); + alarm(0); + for (ctp = callout_tab; ctp; ctp = nctp) { + nctp = ctp->next; + ctp->next = callout_free; + callout_free = ctp; + callout_nfree ++; + } + callout_tab = (callout_t *)0; + unblocksig(); +} + + + +/********************************************************** + * Do_Callout * + * calls the callback routines when the time has expired * + *********************************************************/ +void +do_callout(void) +{ +callout_t *ctp; +unsigned int now; + + + now = (unsigned int) time(NULL); + for (ctp = callout_tab; ctp;) { + if (ctp->sleept > now) + break; + ctp = ctp->next; + if (ctp) + ctp->sleept += callout_tab->sleept; + callout_tab->next = callout_free; + callout_free = callout_tab; + callout_tab = ctp; + callout_nfree ++; + + switch (callout_free->what) { + case CO_ENT2TAB: + Debug(3, "Callout -> Multiuser"); + longjmp(boing_multiuser, 2); + case CO_FORK: + case CO_GETTY: + Debug(3, "Callout -> do_getty()"); + (void)do_getty((ttytab_t *)callout_free->arg, 0); + break; + case CO_MUL2SIN: + Debug(3, "Callout -> M2S timeout"); + if (boing_m2stimeout) + longjmp(*boing_m2stimeout, 2); + } + } + + /* schedule next alarm */ + if (callout_tab) { + Debug(4, "Next callout in %d seconds.", callout_tab->sleept - now); + alarm(callout_tab->sleept - now); + } +} + + +/********************************************************** + * SignalsForChile * + * Set up signals for the child processes * + *********************************************************/ +void +signalsforchile(void) +{ + signal(SIGTSTP, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGHUP , SIG_DFL); + signal(SIGUSR1, SIG_DFL); + signal(SIGUSR2, SIG_DFL); + signal(SIGALRM, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGTTIN, SIG_DFL); + unblocksig(); +} diff --git a/sbin/init.chmr/init.conf b/sbin/init.chmr/init.conf new file mode 100644 index 0000000000..4fd8cb4fa2 --- /dev/null +++ b/sbin/init.chmr/init.conf @@ -0,0 +1,125 @@ +# This is the /etc/init.conf file. +# It is read on startup by the chmr-init program, before /etc/rc or +# singleuser shell is executed. +# +# WARNING: You can easily make your system unbootable if you specify +# wrong values in this file. Make sure you know exactly what you +# are doing, and always have a boot floppy prepared ! +# +# Blank lines and lines beginning with '#' are ignored. +# All keywords can be abbreviated to their shortest unambiguous form. +# ("st s" is the same as "startup_state singleuser"). +# Case is ignored in keywords, but retained in string values. +# +# +# Set the debug level high, so that you can see the configuration process +# Note that you also have to edit /etc/syslog.conf so that messages of type +# daemon.debug are written somewhere. Otherwise they go to /dev/null. +# +# This keyword is only available if init was compiled with -DDEBUG . +# Note that it produces *a lot* of noise ! +#debug 5 +# +# +# You can also include another config file here: +#include "/etc/init.conf2" +# +# +# To which mode the machine boots: "singleuser" or "multiuser" +startup_state multiuser +# +# +# The command to be executed for a singleuser shell. +# Note that this is specified as "command arg0 arg1", i.e. the first argument +# give here is passed as argv[0] to the command. +singleusershell "/bin/sh -" +# +# Terminal type for singleusershell +singleuserterm "pc3" +# +# Device on which the singleusershell is started, relative to /dev. +singleuserdevice "console" +# +# +# autobootcommand is started to execute /etc/rc if the system is started +# directly to multiuser mode; In all other cases (transition from singleuser +# to multiuser) fastbootcommand isn't started. +autobootcommand "/bin/sh sh /etc/rc autoboot" +fastbootcommand "/bin/sh sh /etc/rc" +# +# +# Timeout values, in seconds for shutdown to singleuser mode: +# how long to wait for child processes after sending SIGTERM: +timeout shutdown sigterm 10 +# how long to wait after sending SIGKILL: +timeout shutdown sigkill 30 +# +# +# If an error occurs (out of memory, fork failed,...) retry the same operation +# after this many seconds (0 disables): +timeout error-retry 300 +# +# +# Parameters for a failing getty process: +# check the exit status of the process to see if it is failing: +respawn checkstatus yes +# if the process terminates within this many seconds, assume it is failing: +respawn checktime 3 +## don't check the time the process is alive +#respawn checktime no +# +# +# ************************************************************* +# configuration parameters that are applicable to ALL processes +# ************************************************************* +# +# Set an environment variable that is accessible from all processes on this +# system (not very useful, but it was easy to implement 8-) +setenv HOSTTYPE "386bsd" +# +# +# Configure default resource limits for *all* processes on this system, except +# init itself. Soft limits are quietly truncated to the corresponding +# hard limit. A limit is left alone if it is no specified or specified as +# 'default'. +# +# Cpu time limit; 1:00:00 == 1h == 60m == 3600s == 3600 +hardlimit cputime unlimited +limit cputime unlimited +# +# Maximum file size; 1m == 1024k == 1048576b == 1024 +hardlimit filesize unlimited +limit filesize 512m +# +# Maximum size of DATA segment +hardlimit datasize 32m +limit datasize 16m +# +# Stack segment... +hardlimit stacksize 32m +limit stacksize 512k +# +# Maximum size of a core dump +hardlimit coredumpsize unlimited +limit coredumpsize 0 +# +# Maximum use of physical memory per process +hardlimit memoryuse default +limit memoryuse default +# +# Maximum number of bytes lockable in-core +hardlimit memorylocked default +limit memorylocked default +# +# Maximum number of processes per user +hardlimit maxprocesses 64 +limit maxprocesses 40 +# +# Maximum number of open files, per process +hardlimit openfiles default +limit openfiles 128 +# +# +# +# End of configuration -- set debug level back to 0 +#debug 0 diff --git a/sbin/init.chmr/init.h b/sbin/init.chmr/init.h new file mode 100644 index 0000000000..8f7df80591 --- /dev/null +++ b/sbin/init.chmr/init.h @@ -0,0 +1,76 @@ +/* + * Defaults + */ +#define RETRYTIME 5*60 +#define INIT_M2S_TERMTO 10 +#define INIT_M2S_KILLTO 30 +#define DEF_CHECKTIME 5 +#define DEF_CHECKSTATUS 1 +#define ALLOC_ARGV 4 +#define CALLOUT_MINFREE 5 +#define CALLOUT_CHUNK 10 + + +#ifndef TESTRUN +# define DEBUG_LEVEL 0 +# define INIT_CONFIG "/etc/init.conf" +#else /* TESTRUN */ +# define DEBUG_LEVEL 5 +# define INIT_CONFIG "./init.conf" +#endif /* TESTRUN */ + + +#ifdef DEBUG +extern int debug; +#endif +extern int retrytime; +extern char **ienviron; +extern sigset_t block_set; + + +#define blocksig() sigprocmask(SIG_BLOCK, &block_set, 0) +#define unblocksig() sigprocmask(SIG_UNBLOCK, &block_set, 0) + + + +/* internal representation of getty table */ +typedef struct ttytab { + struct ttytab *next; + struct ttytab *prev; + char *name; /* device name */ + char **argv; /* argv for execve() */ + char *type; /* terminal type */ + int intflags; /* internal flags, see below */ + pid_t pid; /* PID of spawned process */ + int failcount; /* how often getty exited with error */ + time_t starttime; /* when it was started */ + } ttytab_t; + + +/* Values for intflags: */ +#define INIT_SEEN 0x001 +#define INIT_CHANGED 0x002 +#define INIT_NEW 0x004 +#define INIT_FAILED 0x008 /* process exited with error code last time */ +#define INIT_OPEN 0x010 /* Init has to do the open() */ +#define INIT_NODEV 0x020 /* do not append device to argv */ +#define INIT_DONTSPAWN 0x040 /* do not respawn a process on this line */ +#define INIT_ARG0 0x080 /* don't pass command as argv[0] */ +#define INIT_FAILSLEEP 0x100 /* getty is asleep before it is retried */ + + +/* type field for callout table */ +typedef enum { + CO_ENT2TAB, /* retry multiuser() */ + CO_FORK, /* retry do_getty(tt) */ + CO_GETTY, /* retry do_getty(tt) */ + CO_MUL2SIN, /* timeout in multi2single */ + } retr_t; + +/* format of callout table */ +typedef struct callout { + struct callout *next; + unsigned int sleept; + retr_t what; + void *arg; + } callout_t; diff --git a/sbin/init.chmr/libutil.h b/sbin/init.chmr/libutil.h new file mode 100644 index 0000000000..8d0d659c66 --- /dev/null +++ b/sbin/init.chmr/libutil.h @@ -0,0 +1,7 @@ +/* + * missing prototypes for the functions in libutil. + * These should be conatined in a system header file, but aren't. + */ +int logout(char *); +int logwtmp(char *, char *, char *); +int login_tty(int); diff --git a/sbin/init.chmr/prototypes.h b/sbin/init.chmr/prototypes.h new file mode 100644 index 0000000000..85fbd6da1d --- /dev/null +++ b/sbin/init.chmr/prototypes.h @@ -0,0 +1,70 @@ +/* + * prototypes + */ + +/* from init.c: */ +void main(int, char **); + +void sig_tstp(int); +void sig_term(int); +void sig_hup(int); +void sig_alrm(int); +#ifdef DEBUG +void sig_usr1(int); +void sig_usr2(int); +#endif +#if defined (UNTRUSTED) && !defined (TESTRUN) +void sig_int(int); +#endif +#ifdef CONFIGURE +void sig_ttin(int); +#endif + +void singleuser(void); +void single2multi(void); +void waitforboot(void); +void multiuser(void); +void multi2single(void); + +void callout(unsigned int, retr_t, void *); +void allocate_callout(void); +void clear_callout(void); +void do_callout(void); +void signalsforchile(void); +void no_autoboot(void); + + +/* from ttytab.c: */ +ttytab_t *free_tty(ttytab_t *, ttytab_t *); +#ifdef _TTYENT_H_ +ttytab_t *ent_to_tab(const struct ttyent *, ttytab_t *, ttytab_t *, int); +#endif +int argv_changed(const char *, const char **); +char **string_to_argv(const char *, int *, char **); +int do_getty(ttytab_t *, int); + + +/* from configure.c: */ +#ifdef CONFIGURE +void configure(char *); +void getconf(void); +void setconf(void); +void checkconf(void); +#endif + + +/* from utils.c: */ +void iputenv(const char *, const char *); +void Debug(int, const char *, ...); +int strCcmp(char *, char *); +char *newstring(const char *); +long str2u(const char *); + + +#ifdef TESTRUN +/* from fake_syslog.c: */ +void openlog(); +void syslog(int, const char *, ...); +void vsyslog(int, const char *, va_list); +void closelog(void); +#endif /* TESTRUN */ diff --git a/sbin/init.chmr/ttytab.c b/sbin/init.chmr/ttytab.c new file mode 100644 index 0000000000..4c54db1a37 --- /dev/null +++ b/sbin/init.chmr/ttytab.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) 1993 Christoph M. Robitschko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christoph M. Robitschko + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ttytab.c + * Everything that has to do with the getty table. + * This includes starting child precesses. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "init.h" +#include "prototypes.h" +#include "libutil.h" + + +#ifdef __gcc__ +# define alloca __builtin_alloca +#endif + +int checkstatus = DEF_CHECKSTATUS; +int checktime = DEF_CHECKTIME; +int def_waittimes[] = {0,0,0,30,60,300,1800}; +int *waittimes = def_waittimes; +int nwaittimes = 7; + + +/* + * FREE_TTY + * remove a ttytab entry from the list + */ +ttytab_t * +free_tty(tab, tt) +ttytab_t *tab, *tt; +{ +char **s; + + + /* Delete the entry from utmp, make log to wtmp */ + if (logout(tt->name)) + logwtmp(tt->name, "", ""); + + /* unlink it from the list first, in case of unexpected signal */ + if (tt->next) + tt->next->prev = tt->prev; + if (tt->prev) + tt->prev->next = tt->next; + else + tab = tt->next; + + /* free the associated memory */ + if (tt->name) free(tt->name); + if (tt->type) free(tt->type); + if (tt->argv) { + for (s = tt->argv; *s; s++) + free(*s); + free(tt->argv); + } + free(tt); + + return(tab); +} + + +/* + * ENT_TO_TAB + * create or change a ttytab entry based on the information in a ttyent struct + */ +ttytab_t * +ent_to_tab(ent, tt, tab, flags) +const struct ttyent *ent; +ttytab_t *tab, *tt; +int flags; +{ +char *argstr; + + + /* Do we really need this entry ? */ + if (ent->ty_status & TTY_ON == 0) + return(tab); + + Debug(4, "Processing tty entry %s", ent->ty_name); + /* Allocate a ttytab entry, if not already there */ + if (!tt) { + tt = malloc(sizeof(*tt)); + if (!tt) + goto kaaplotz; + tt->name = (char *)0; + tt->argv = (char **)0; + tt->type = (char *)0; + tt->intflags = INIT_NEW; + tt->pid = 0; + tt->failcount = 0; + tt->starttime = (time_t)0; + tt->next = tab; + tt->prev = (ttytab_t *)0; + if (tab) + tab->prev = tt; + tab = tt; + Debug(5, "Creating new ttytab entry"); + } else + Debug(5, "Reusing existing ttytab entry"); + + + tt->intflags |= (flags | INIT_SEEN); + + + /* fill each field in from the ttyent, if it has changed */ + if (strcmp(ent->ty_name, tt->name)) { + Debug(5, "name differs: old=\"%s\" new=\"%s\"", tt->name, ent->ty_name); + if (tt->name) free(tt->name); + tt->name = newstring(ent->ty_name); + tt->intflags |= INIT_CHANGED; + } + if (flags & INIT_NODEV) + argstr = ent->ty_getty; + else { + if (!(argstr = alloca(strlen(ent->ty_getty) +strlen(tt->name) +2))) + goto kaaplotz; +/* sprintf(argstr, "%s %s", ent->ty_getty, tt->name); */ + strcpy(argstr, ent->ty_getty); + strcat(argstr, " "); + strcat(argstr, tt->name); + } + if (argv_changed(argstr, (const char **)tt->argv)) { + if (tt->argv) { + char **s; + + for (s = tt->argv; *s; s++) + free(*s); + free(tt->argv); + } + tt->argv = string_to_argv(argstr, (int *)0, (char **)0); + tt->intflags |= INIT_CHANGED; + Debug(5, "argv differs."); + } + if (strcmp(ent->ty_type, tt->type)) { + if (tt->type) free(tt->type); + tt->type = newstring(ent->ty_type); + tt->intflags |= INIT_CHANGED; + Debug(5, "type differs."); + } + + if (!(tt->intflags & INIT_CHANGED)) { + Debug(5, "entry unchanged."); + } else + Debug(5, "entry has been changed."); + return(tab); + + +kaaplotz: + syslog(LOG_ERR, "Out of memory in ent_to_tab"); + callout(retrytime, CO_ENT2TAB, NULL); + return(tab); +} + + + +/* + * ARGV_CHANGED + * compares a string and an argv + */ +int +argv_changed(string, argv) +const char *string; +const char **argv; +{ +const char *ss; +const char **sa; + + + if (!argv) + return(1); + for (ss = string, sa = argv; *sa; sa++) { + register int len = strlen(*sa); + if (strncmp(*sa, ss, len)) + return(1); + ss += len; + if ((*ss != ' ') && (*ss != '\t') && (*ss)) + return(1); + while ((*ss == ' ') || (*ss == '\t')) ss++; + } + if (*ss) /* String longer than argv ? */ + return(1); + else + return(0); +} + + +/* + * STRING_TO_ARGV + * breaks up a string into words. Speration characters are SPACE and TAB, + * unless prepended by a backslash \ or within double quotes ". + * Quotes and backslash can be escaped by an additional \. + * If errtext is non-NULL, it is set to point to an error message. + */ +char ** +string_to_argv(string, rargc, errtext) +const char *string; +int *rargc; +char **errtext; +{ +const char *s; +char *buf, *t; +int backslash, inquote; +int argc, alloc_argc; +char **argv; +char **ra; + + + Debug(4, "string_to_argv(\"%s\")", string); + /* + * argv is allocated in chunks of ALLOC_ARGV pointers; if it runs + * out of space, it is realloc'ed with ALLOC_ARGV more pointers. + */ + argc = 0; + argv = (char **)malloc(ALLOC_ARGV * sizeof(char *)); + if (!argv) return(argv); + alloc_argc = ALLOC_ARGV; + + if (errtext) + *errtext = (char *)0; + + for (s = string; *s;) { + backslash = inquote = 0; + for (; (*s == ' ') || (*s == '\t'); s++); /* Skip blanks */ + if (!(*s)) break; + if (!(buf = (char *)malloc(strlen(s)+1))) goto malloc_fail; + for (t=buf;; s++) { + if (backslash) { + backslash = 0; + *t++ = *s; + } else switch (*s) { + case '\\': + backslash = 1; + break; + case '\"': + inquote ^= 1; + break; + case '\0': + if (inquote && errtext) + *errtext = "Unmatched \"."; + goto end_of_word; + case ' ': case '\t': + if (!inquote) + goto end_of_word; + /* no break */ + default: + *t++ = *s; + } + } + +end_of_word: + *t = '\0'; + if (!(argv[argc] = (char *)realloc(buf, strlen(buf)+1))) goto malloc_fail; + + if (++argc >= alloc_argc) { + alloc_argc += ALLOC_ARGV; + if (!(ra = realloc(argv, alloc_argc * sizeof(char **)))) + goto malloc_fail; + argv = ra; + } + } + + /* Terminate argv with a NULL pointer and return */ + argv[argc] = (char *)0; + if (rargc) + *rargc = argc; +#ifdef DEBUG + if (debug > 5) { + for (ra = argv; *ra; ra++) + Debug(5, " \"%s\"", *ra); + Debug(5, " "); + } +#endif + return(argv); + +malloc_fail: + /* free all so-far allocated memory and return a NULL pointer */ + for (; *argv; argv++) + free(*argv); + free(argv); + return((char **)0); +} + + + +/* + * DO_GETTY + * start a getty process for a ttytab entry + */ +int +do_getty(tt, status) +ttytab_t *tt; +int status; +{ +int pid; + + + if (!tt) + return(-1); + Debug(2, "do_getty for %s", tt->name); + + if (tt->intflags & INIT_FAILSLEEP) + tt->intflags &= ~INIT_FAILSLEEP; + else { + /* Delete old entry from utmp, make log to wtmp */ + if (logout(tt->name)) + logwtmp(tt->name, "", ""); + + if ((checkstatus && status) || (checktime && (time(0) - tt->starttime <= checktime))) { + if (tt->intflags & INIT_FAILED) { + if (++tt->failcount >= nwaittimes) + tt->failcount = nwaittimes; + } else { + tt->intflags |= INIT_FAILED; + tt->failcount = 1; + } + if (waittimes[tt->failcount]) { + tt->intflags |= INIT_FAILSLEEP; + tt->pid = 0; + syslog(LOG_WARNING, "getty \"%s\" for %s failed, sleeping", tt->argv[0], tt->name); + callout (waittimes[tt->failcount], CO_GETTY, (void *)tt); + return(0); + } + } else + tt->intflags &= ~INIT_FAILED; + } + + +#ifdef TESTRUN + return (0); +#endif /* TESTRUN */ + + tt->starttime = time(0); + blocksig(); + switch ((pid=fork())) { + case -1: + syslog(LOG_ERR, "fork failed for %s: %m", tt->name); + unblocksig(); + callout(retrytime, CO_FORK, (void *)tt); + return (-1); + case 0: + signalsforchile(); + iputenv("TERM", tt->type); + if (tt->intflags & INIT_OPEN) { + int fd; + char *device = alloca(strlen(tt->name) + sizeof("/dev/")); +/* sprintf(device, "/dev/%s", tt->name); */ + strcpy(device, "/dev/"); + strcat(device, tt->name); + (void)revoke (device); + closelog(); + fd = open(device, O_RDWR); + if (fd < 0) + syslog(LOG_ERR, "open %s failed: %m", device); + if (login_tty(fd) < 0) + syslog(LOG_ERR, "login_tty for %s failed: %m", device); + } +#ifdef CONFIGURE + setconf(); +#endif + closelog(); /* Necessary, because dup2 fails otherwise (?) */ + if (tt->intflags & INIT_ARG0) + execve(tt->argv[0], tt->argv +1, ienviron); + else + execve(tt->argv[0], tt->argv, ienviron); + syslog(LOG_ERR, "Exec \"%s\" failed for %s: %m", tt->argv[0], tt->name); + _exit(1); + default: + tt->pid = pid; + unblocksig(); + return(0); + } + + +} diff --git a/sbin/init.chmr/utils.c b/sbin/init.chmr/utils.c new file mode 100644 index 0000000000..69dd9b6f67 --- /dev/null +++ b/sbin/init.chmr/utils.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 1993 Christoph M. Robitschko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christoph M. Robitschko + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * utils.c + * Misc. utility functions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "init.h" +#include "prototypes.h" + + +/* global variable */ +char **ienviron; + + +/* + * IPUTENV + * put an environment variable in a separate space + */ +void +iputenv(var, val) +const char *var, *val; +{ +static int ienvalloc = 0; +char *plaza; +char **ienv; +int ienvcount; +int varlen; + + + varlen = strlen(var) + 1; /* VAR= */ + plaza = malloc(varlen + strlen(val) + 1); + if (!plaza) + return; /* fail miserably */ +/* sprintf(plaza, "%s=%s", var, val); */ + strcpy(plaza, var); + strcat(plaza, "="); + strcat(plaza, val); + + + if (!ienvalloc) { + ienviron = malloc(4*sizeof(char *)); + if (!ienviron) return; + ienvalloc = 4; + *ienviron = (char *)0; + } + + /* Search ienviron if variable is already defined */ + for (ienvcount = 0, ienv = ienviron; *ienv; ienv++, ienvcount++) + if (!strncmp(*ienv, plaza, varlen)) { + free(*ienv); + *ienv = plaza; + return; + } + + /* Not found, create new environ entry */ + if (ienvcount >= ienvalloc) + if (( ienviron = realloc (ienviron, (ienvalloc + 4)* sizeof (char *)))) + ienvalloc += 4; + else + return; /* kaaplotz */ + + for (ienv = ienviron; *ienv; ienv++); + *ienv = plaza; + *(++ienv) = (char *)0; + return; +} + + + +/* + * dEBUG + * print a message if current debug level > log level + */ +void +Debug(int level, const char *format, ...) +{ +#ifdef DEBUG + va_list args; + + + if (level > debug) + return; + va_start(args, format); + vsyslog(LOG_DEBUG, format, args); + va_end(args); +#endif +} + + +#undef toupper +/* + * STRcCMP + * see if s2 is the beginning of s1, ignoring case + */ +int +strCcmp(s1, s2) /* strncasecmp(s1, s2, strlen(s1)) */ +char *s1, *s2; +{ + + + for (;*s1; s1++, s2++) + if (toupper(*s1) != toupper(*s2)) + return(1); + return(0); +} + + + +/* + * NEWSTRING + * allocate memory for a string and copy it. + */ +char * +newstring(string) +const char *string; +{ +char *s; + + s = (char *)malloc(strlen(string)+1); + if (s) + strcpy(s, string); + return(s); +} + + + +/* + * STR2U + * atoi with error handling. errors are signalled with a negative return value + */ +long +str2u(str) +const char *str; +{ +char *s; +long res; + + res = strtol(str, &s, 0); + if ((s != str) && !*s) + return(res); + return(-1); +} diff --git a/sbin/init/Makefile b/sbin/init/Makefile index 04b96363e1..4e85ee8c1f 100644 --- a/sbin/init/Makefile +++ b/sbin/init/Makefile @@ -6,4 +6,8 @@ LDADD= -lutil NOMAN=noman LDFLAGS= +beforeinstall: + rm -f ${DESTDIR}${BINDIR}/init.old + - ln ${DESTDIR}${BINDIR}/init ${DESTDIR}${BINDIR}/init.old + .include diff --git a/sbin/init/init.c b/sbin/init/init.c index fd36c16ef2..a8bbd45756 100644 --- a/sbin/init/init.c +++ b/sbin/init/init.c @@ -211,6 +211,7 @@ top: /* if we are about to be rebooted, then wait for it */ if (WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) pause(); + logwtmp("~", "reboot", ""); } else { status = 1; sflag = 1; goto top; } } signal(SIGTERM, sterm); diff --git a/sbin/mknod/Makefile b/sbin/mknod/Makefile index 6ba48e8b6a..efa8cd1338 100644 --- a/sbin/mknod/Makefile +++ b/sbin/mknod/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= mknod -MAN8= mknod.0 +MAN8= mknod.8 .include diff --git a/sbin/mount/Makefile b/sbin/mount/Makefile index 15f5fe67b3..50c19ec36d 100644 --- a/sbin/mount/Makefile +++ b/sbin/mount/Makefile @@ -2,7 +2,7 @@ PROG= mount CFLAGS+=-DNFS -MAN8= mount.0 +MAN8= mount.8 DPADD= ${LIBRPC} LDADD= -lrpc MLINKS= mount.8 umount.8 diff --git a/sbin/mount/mount.8 b/sbin/mount/mount.8 index a92b34806d..ef8a024060 100644 --- a/sbin/mount/mount.8 +++ b/sbin/mount/mount.8 @@ -120,6 +120,8 @@ Do not allow set-user-identifier or set-group-identifier bits to take effect. Do not interpret character or block special devices on the file system. This options is useful for a server that has file systems containing special devices for architectures other than its own. +.It nocore +Do not create core files on the file system. .It synchronous All .Tn I/O @@ -271,9 +273,8 @@ Active special devices continue to work, but all other files return errors if further accesses are attempted. The root file system cannot be forcibly unmounted. .It Fl a -All of the file systems described in -.Xr fstab -are unmounted. +All of the file systems currently mounted (except the root file +system) are unmouted. .It Fl t Is used to indicate the actions should only be taken on filesystems of the specified type. diff --git a/sbin/mount/mount.c b/sbin/mount/mount.c index 14e40cd363..0d468233e7 100644 --- a/sbin/mount/mount.c +++ b/sbin/mount/mount.c @@ -419,6 +419,8 @@ prmount(spec, name, flags) PR("nosuid"); if (flags & MNT_NODEV) PR("nodev"); + if (flags & MNT_NOCORE) + PR("nocore"); if (flags & MNT_SYNCHRONOUS) PR("synchronous"); if (flags & MNT_QUOTA) @@ -504,6 +506,13 @@ getstdopts(options, flagp) *flagp &= ~MNT_NODEV; continue; } + if (!strcasecmp(opt, "core")) { + if (negative) + *flagp |= MNT_NOCORE; + else + *flagp &= ~MNT_NOCORE; + continue; + } if (!strcasecmp(opt, "synchronous")) { if (!negative) *flagp |= MNT_SYNCHRONOUS; diff --git a/sbin/mount_isofs/Makefile b/sbin/mount_isofs/Makefile index 4f0c8ac73a..9fac800808 100644 --- a/sbin/mount_isofs/Makefile +++ b/sbin/mount_isofs/Makefile @@ -1,4 +1,6 @@ +# $Id: Makefile,v 1.2 1993/07/21 07:51:49 cgd Exp $ + PROG = mount_isofs -NOMAN = noman +MAN8 = mount_isofs.8 .include diff --git a/sbin/mount_isofs/mount_isofs.8 b/sbin/mount_isofs/mount_isofs.8 new file mode 100644 index 0000000000..c10bcdbec1 --- /dev/null +++ b/sbin/mount_isofs/mount_isofs.8 @@ -0,0 +1,98 @@ +.\" +.\" Copyright (c) 1993 Christopher G. Demetriou +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Christopher G. Demetriou. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software withough specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $Id: mount_isofs.8,v 1.1.2.3 1993/07/21 07:47:26 cgd Exp $ +.\" +.Dd July 19, 1993 +.Dt MOUNT_MSDOS 8 +.Os NetBSD 0.9 +.Sh NAME +.Nm mount_isofs +.Nd mount an ISO-9660 filesystem +.Sh SYNOPSIS +.Nm mount_isofs +.Op Fl F Ar fsoptions +.Op Fl norrip +.Pa special +.Pa node +.Sh DESCRIPTION +The +.Nm mount_isofs +command attaches the ISO-9660 filesystem residing on +the device +.Pa special +to the global filesystem namespace at the location +indicated by +.Pa node . +This command is normally executed by +.Xr mount 8 . +at boot time. +.Pp +If the filesystem includes Rockridge extensions, they are +used unless the +.Fl norrip +flag is used. If that option is given to +.Nm +then the Rockridge extensions will be ignored. +.Sh EXAMPLES +.Bd -literal -offset indent -compact +mount_isofs /dev/cd0d /cdrom +mount_isofs \-norrip /dev/cd0d /cdrom +mount \-t isofs /dev/cd0d /cdrom +mount \-t isofs \-o \-norrip /dev/cd0d /cdrom +.Ed +.Sh SEE ALSO +.Xr mount 2 , +.Xr unmount 2 , +.Xr fstab 5 +.Sh BUGS +The isofs filesystem should support the original "High Sierra" +("CDROM001") format; +it does not. +.Pp +POSIX device nodes are currently not supported. +.Pp +The filesystem name might need some rethinking, and some would +say it should run as a user process. +.Pp +There is no ECMA support. +.Sh HISTORY +The +.Nm mount_isofs +utility first appeared in NetBSD 0.8. +Handling of Rockridge extensions was added for NetBSD 0.9. +.Sh AUTHORS +.Bl -tag +Pace Willisson , wrote the +original version of the ISO-9660 filesystem. +.Pp +Atsushi Murai wrote the Rockridge support. +.Pp +Chris Demetriou wrote the man page. +.El diff --git a/sbin/mount_isofs/mount_isofs.c b/sbin/mount_isofs/mount_isofs.c index 3c25385749..27303c8018 100644 --- a/sbin/mount_isofs/mount_isofs.c +++ b/sbin/mount_isofs/mount_isofs.c @@ -1,3 +1,7 @@ +#ifndef lint +static char rcsid[] = "$Header: /a/cvs/386BSD/src/sbin/mount_isofs/mount_isofs.c,v 1.2 1993/07/20 03:33:49 jkh Exp $"; +#endif + #include #include #include @@ -18,30 +22,29 @@ char **argv; char *dir; struct ufs_args args; int c; - extern char *optarg; - extern int optind; int opts; opts = MNT_RDONLY; - while ((c = getopt (argc, argv, "F:")) != EOF) { - switch (c) { - case 'F': - opts |= atoi (optarg); - break; - default: - usage (); - } + argc--; + argv++; + while (argc > 2) { + if (!strcmp("-F", argv[0])) { + argc--; argv++; + opts |= atoi(argv[0]); + argc--; argv++; + } else if (!strcmp(argv[0], "-norrip")) { + opts |= MNT_NORRIP; + argc--; argv++; + } else + usage(); } - if (optind + 2 != argc) - usage (); - - dev = argv[optind]; - dir = argv[optind + 1]; + dev = argv[0]; + dir = argv[1]; args.fspec = dev; - args.exflags = MNT_EXRDONLY; + args.exflags = MNT_EXRDONLY | opts; args.exroot = 0; if (mount (MOUNT_ISOFS, dir, MNT_RDONLY, &args) < 0) { diff --git a/sbin/mountd/Makefile b/sbin/mountd/Makefile index 01c4b2ee17..d40dd727b3 100644 --- a/sbin/mountd/Makefile +++ b/sbin/mountd/Makefile @@ -2,8 +2,8 @@ PROG= mountd CFLAGS+=-DNFS -MAN5= exports.0 -MAN8= mountd.0 +MAN5= exports.5 +MAN8= mountd.8 DPADD= ${LIBRPC} LDADD= -lrpc -lutil diff --git a/sbin/newfs/Makefile b/sbin/newfs/Makefile index b79c15eb4c..07201cdfdf 100644 --- a/sbin/newfs/Makefile +++ b/sbin/newfs/Makefile @@ -3,7 +3,7 @@ PROG= newfs CFLAGS+=-DMFS SRCS= dkcksum.c newfs.c mkfs.c -MAN8= newfs.0 +MAN8= newfs.8 LINKS= ${BINDIR}/newfs ${BINDIR}/mount_mfs MLINKS= newfs.8 mfs.8 .PATH: ${.CURDIR}/../disklabel diff --git a/sbin/newfs/newfs.c b/sbin/newfs/newfs.c index 44b0d7de6d..59d6446c2c 100644 --- a/sbin/newfs/newfs.c +++ b/sbin/newfs/newfs.c @@ -61,6 +61,8 @@ char copyright[] = #include #include +int fatal(char *fmt, ...); + #define COMPAT /* allow non-labeled disks */ /* @@ -556,8 +558,7 @@ rewritelabel(s, fd, lp) } /*VARARGS*/ -fatal(fmt) - char *fmt; +fatal(char *fmt, ...) { va_list ap; diff --git a/sbin/nfsd/Makefile b/sbin/nfsd/Makefile index bdd1d771fe..675e4b2c4b 100644 --- a/sbin/nfsd/Makefile +++ b/sbin/nfsd/Makefile @@ -2,7 +2,7 @@ PROG= nfsd CFLAGS+=-DNFS -MAN8= nfsd.0 +MAN8= nfsd.8 DPADD= ${LIBRPC} LDADD= -lrpc -lutil diff --git a/sbin/nfsiod/Makefile b/sbin/nfsiod/Makefile index 335b4f50c8..77da72c0d7 100644 --- a/sbin/nfsiod/Makefile +++ b/sbin/nfsiod/Makefile @@ -2,7 +2,7 @@ PROG= nfsiod CFLAGS+=-DNFS -MAN8= nfsiod.0 +MAN8= nfsiod.8 LDADD= -lutil .include diff --git a/sbin/ping/Makefile b/sbin/ping/Makefile index d5caaaeb64..bb67205ae1 100644 --- a/sbin/ping/Makefile +++ b/sbin/ping/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= ping -MAN8= ping.0 +MAN8= ping.8 BINOWN= root BINMODE=4555 diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c index bfa1447e27..9ba6c6af5c 100644 --- a/sbin/ping/ping.c +++ b/sbin/ping/ping.c @@ -132,9 +132,9 @@ int interval = 1; /* interval between packets */ /* timing */ int timing; /* flag to do timing */ -long tmin = LONG_MAX; /* minimum round trip time */ -long tmax; /* maximum round trip time */ -u_long tsum; /* sum of all times, for doing average */ +double tmin = 1000.0*(double)LONG_MAX; /* minimum round trip time */ +double tmax; /* maximum round trip time */ +double tsum; /* sum of all times, for doing average */ char *pr_addr(); void catcher(), finish(); @@ -389,7 +389,7 @@ catcher() alarm((u_int)interval); else { if (nreceived) { - waittime = 2 * tmax / 1000; + waittime = 2 * tmax / 1000000.0; if (!waittime) waittime = 1; } else @@ -464,7 +464,7 @@ pr_pack(buf, cc, from) static char old_rr[MAX_IPOPTLEN]; struct ip *ip; struct timeval tv, *tp; - long triptime; + double triptime; int hlen, dupflag; (void)gettimeofday(&tv, (struct timezone *)NULL); @@ -494,7 +494,7 @@ pr_pack(buf, cc, from) tp = (struct timeval *)icp->icmp_data; #endif tvsub(&tv, tp); - triptime = tv.tv_sec * 1000 + (tv.tv_usec / 1000); + triptime = (double) (tv.tv_sec * 1000000 + tv.tv_usec); tsum += triptime; if (triptime < tmin) tmin = triptime; @@ -522,7 +522,7 @@ pr_pack(buf, cc, from) icp->icmp_seq); (void)printf(" ttl=%d", ip->ip_ttl); if (timing) - (void)printf(" time=%ld ms", triptime); + (void)printf(" time=%.3f ms", triptime/1000.0); if (dupflag) (void)printf(" (DUP!)"); /* check the data */ @@ -707,8 +707,8 @@ finish() ntransmitted)); (void)putchar('\n'); if (nreceived && timing) - (void)printf("round-trip min/avg/max = %ld/%lu/%ld ms\n", - tmin, tsum / (nreceived + nrepeats), tmax); + (void)printf("round-trip min/avg/max = %.3f/%.3f/%.3f ms\n", + tmin/1000.0, tsum/(nreceived + nrepeats)/1000.0, tmax/1000.0); exit(0); } diff --git a/sbin/quotacheck/Makefile b/sbin/quotacheck/Makefile index fa74932251..f89385191f 100644 --- a/sbin/quotacheck/Makefile +++ b/sbin/quotacheck/Makefile @@ -2,7 +2,7 @@ PROG= quotacheck SRCS= quotacheck.c preen.c -MAN8= quotacheck.0 +MAN8= quotacheck.8 .PATH: ${.CURDIR}/../fsck .include diff --git a/sbin/reboot/Makefile b/sbin/reboot/Makefile index f3065b103a..91fc85cc73 100644 --- a/sbin/reboot/Makefile +++ b/sbin/reboot/Makefile @@ -4,8 +4,8 @@ PROG= reboot DPADD= ${LIBUTIL} LDADD= -lutil -MAN8= reboot_${MACHINE}.0 -MLINKS= reboot_${MACHINE}.8 reboot.8 +MAN8= reboot_${MACHINE}.8 +MLINKS= reboot_${MACHINE}.8 ../reboot.8 MANSUBDIR=/${MACHINE} .include diff --git a/sbin/reboot/reboot_i386.8 b/sbin/reboot/reboot_i386.8 index b8eb89c185..b444887b3e 100644 --- a/sbin/reboot/reboot_i386.8 +++ b/sbin/reboot/reboot_i386.8 @@ -110,7 +110,7 @@ clones attempt to boot the floppy disk drive A (otherwise known as drive 0) first, and failing that, attempt to boot the hard disk C (otherwise known as hard disk controller 1, drive 0). The automatic boot will attempt to load -.Pa vmunix +.Pa 386bsd from partition A of either the floppy or the hard disk. This boot may be aborted by typing any character on the keyboard repeatedly (four or five times at least) @@ -156,9 +156,9 @@ fd 5 1/4" or 3 1/2" High density floppies For example, to boot from a file system which starts at cylinder 0 of unit 0 of an IDE disk, type -.Dq Li wd0a:vmunix +.Dq Li wd0a:386bsd to the boot prompt; -.Dq Li fd0a:vmunix +.Dq Li fd0a:386bsd would specify a 3 1/2" floppy drive 0 . .Pp In an emergency, the bootstrap methods described in the paper @@ -166,8 +166,8 @@ In an emergency, the bootstrap methods described in the paper can be used to boot from a distribution tape. .Sh FILES -.Bl -tag -width /vmunixxx -compact -.It Pa /vmunix +.Bl -tag -width /386bsdxx -compact +.It Pa /386bsd system code .It Pa /boot system bootstrap diff --git a/sbin/restore/Makefile b/sbin/restore/Makefile index 4791fa89c3..782f8d1794 100644 --- a/sbin/restore/Makefile +++ b/sbin/restore/Makefile @@ -4,7 +4,7 @@ PROG= restore SRCS= main.c interactive.c restore.c dirs.c symtab.c tape.c utilities.c ROBJS= main.o interactive.o restore.o dirs.o symtab.o rtape.o utilities.o \ dumprmt.o -MAN8= restore.0 rrestore.0 +MAN8= restore.8 rrestore.8 .PATH: ${.CURDIR}/../dump CLEANFILES+=dumprmt.o rtape.o rrestore @@ -17,7 +17,7 @@ rtape.o: tape.c ${LIBC} ${CC} ${CFLAGS} -c -DRRESTORE ${.CURDIR}/tape.c -o ${.TARGET} afterinstall: - install ${STRIP} -o root -g ${BINGRP} -m 4755 rrestore \ + install ${COPY} ${STRIP} -o root -g ${BINGRP} -m 4755 rrestore \ ${DESTDIR}${BINDIR} .include diff --git a/sbin/route/Makefile b/sbin/route/Makefile index 99ef23a005..c904a0bb62 100644 --- a/sbin/route/Makefile +++ b/sbin/route/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.6 (Berkeley) 6/27/91 PROG= route -MAN8= route.0 +MAN8= route.8 SRCS= route.c #SRCS= route.c ccitt_addr.c CFLAGS+=-I. diff --git a/sbin/route/route.c b/sbin/route/route.c index 67f3b061bd..44dad38897 100644 --- a/sbin/route/route.c +++ b/sbin/route/route.c @@ -78,7 +78,7 @@ struct keytab { struct ortentry route; union sockunion { struct sockaddr sa; - struct sockaddr_in sin; + struct sockaddr_in s_in; #ifdef notdef struct sockaddr_ns sns; struct sockaddr_iso siso; @@ -98,7 +98,7 @@ int s; int forcehost, forcenet, doflush, nflag, af, qflag, tflag, Cflag, keyword(); int iflag, verbose, aflen = sizeof (struct sockaddr_in); int locking, lockrest, debugonly; -struct sockaddr_in sin = { sizeof(sin), AF_INET }; +struct sockaddr_in s_in = { sizeof(s_in), AF_INET }; struct rt_metrics rt_metrics; u_long rtm_inits; struct in_addr inet_makeaddr(); @@ -671,7 +671,7 @@ newroute(argc, argv) break; if (af == AF_INET && hp && hp->h_addr_list[1]) { hp->h_addr_list++; - bcopy(hp->h_addr_list[0], (caddr_t)&so_dst.sin.sin_addr, + bcopy(hp->h_addr_list[0], (caddr_t)&so_dst.s_in.sin_addr, hp->h_length); } else break; @@ -706,9 +706,9 @@ newroute(argc, argv) } void -inet_makenetandmask(net, sin) +inet_makenetandmask(net, s_in) u_long net; - register struct sockaddr_in *sin; + register struct sockaddr_in *s_in; { u_long addr, mask = 0; register char *cp; @@ -736,15 +736,15 @@ inet_makenetandmask(net, sin) else mask = -1; } - sin->sin_addr.s_addr = htonl(addr); - sin = &so_mask.sin; - sin->sin_addr.s_addr = htonl(mask); - sin->sin_len = 0; - sin->sin_family = 0; - cp = (char *)(&sin->sin_addr + 1); - while (*--cp == 0 && cp > (char *)sin) + s_in->sin_addr.s_addr = htonl(addr); + s_in = &so_mask.s_in; + s_in->sin_addr.s_addr = htonl(mask); + s_in->sin_len = 0; + s_in->sin_family = 0; + cp = (char *)(&s_in->sin_addr + 1); + while (*--cp == 0 && cp > (char *)s_in) ; - sin->sin_len = 1 + cp - (char *)sin; + s_in->sin_len = 1 + cp - (char *)s_in; } /* @@ -812,13 +812,13 @@ getaddr(which, s, hpp) *hpp = NULL; if (((val = inet_addr(s)) != -1) && (which != RTA_DST || forcenet == 0)) { - su->sin.sin_addr.s_addr = val; - if (inet_lnaof(su->sin.sin_addr) != INADDR_ANY) + su->s_in.sin_addr.s_addr = val; + if (inet_lnaof(su->s_in.sin_addr) != INADDR_ANY) return (1); else { val = ntohl(val); out: if (which == RTA_DST) - inet_makenetandmask(val, &su->sin); + inet_makenetandmask(val, &su->s_in); return (0); } } @@ -834,8 +834,8 @@ getaddr(which, s, hpp) hp = gethostbyname(s); if (hp) { *hpp = hp; - su->sin.sin_family = hp->h_addrtype; - bcopy(hp->h_addr, (char *)&su->sin.sin_addr, hp->h_length); + su->s_in.sin_family = hp->h_addrtype; + bcopy(hp->h_addr, (char *)&su->s_in.sin_addr, hp->h_length); return (1); } (void) fprintf(stderr, "%s: bad value\n", s); @@ -1198,7 +1198,7 @@ sodump(su, which) #endif case AF_INET: (void) printf("%s: inet %s; ", - which, inet_ntoa(su->sin.sin_addr)); + which, inet_ntoa(su->s_in.sin_addr)); break; #ifdef notdef case AF_NS: diff --git a/sbin/routed/Makefile b/sbin/routed/Makefile index 553b68b241..0fc8bc839c 100644 --- a/sbin/routed/Makefile +++ b/sbin/routed/Makefile @@ -3,7 +3,7 @@ PROG= routed SRCS= af.c if.c input.c main.c output.c startup.c tables.c timer.c \ trace.c inet.c -MAN8= routed.0 +MAN8= routed.8 SUBDIR= query trace DPADD= ${LIBUTIL} ${LIBCOMPAT} LDADD= -lutil diff --git a/sbin/savecore/Makefile b/sbin/savecore/Makefile index 5bd92274aa..6b125202fa 100644 --- a/sbin/savecore/Makefile +++ b/sbin/savecore/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= savecore -MAN8= savecore.0 +MAN8= savecore.8 .include diff --git a/sbin/shutdown/Makefile b/sbin/shutdown/Makefile index 95391ed738..56c4013618 100644 --- a/sbin/shutdown/Makefile +++ b/sbin/shutdown/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= shutdown -MAN8= shutdown.0 +MAN8= shutdown.8 BINOWN= root BINGRP= operator BINMODE=4550 diff --git a/sbin/slattach/Makefile b/sbin/slattach/Makefile index 24a0a579f3..c02f44bcb6 100644 --- a/sbin/slattach/Makefile +++ b/sbin/slattach/Makefile @@ -1,7 +1,10 @@ -# @(#)Makefile 5.4 (Berkeley) 5/11/90 +# from: @(#)Makefile 5.4 (Berkeley) 5/11/90 +# $Id$ PROG= slattach -MAN8= slattach.0 +MAN8= slattach.8 MLINKS= slattach.8 slip.8 +LDADD= -lutil +DPADD= ${LIBUTIL} .include diff --git a/sbin/slattach/slattach.8 b/sbin/slattach/slattach.8 index 16c688a9ec..ab4b2e0d8e 100644 --- a/sbin/slattach/slattach.8 +++ b/sbin/slattach/slattach.8 @@ -29,9 +29,11 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)slattach.8 6.4 (Berkeley) 3/16/91 +.\" from: @(#)slattach.8 6.4 (Berkeley) 3/16/91 .\" -.Dd March 16, 1991 +.\" $Id$ +.\" +.Dd April 4, 1993 .Dt SLATTACH 8 .Os BSD 4.3 .Sh NAME @@ -39,7 +41,18 @@ .Nd attach serial lines as network interfaces .Sh SYOPNSIS .Nm Slattach -.Ar ttyname Op Ar baudrate +.Op \-a +.Op \-c +.Op \-e exit-command +.Op \-f +.Op \-h +.Op \-l +.Op \-n +.Op \-r redial-command +.Op \-s baudrate +.Op \-u unit-command +.Op \-z +.Ar ttyname .Sh DESCRIPTION .Nm Slattach is used to assign a tty line to a network interface, @@ -47,16 +60,53 @@ and to define the network source and destination addresses. The following operands are supported by .Nm slattach : .Bl -tag -width Ar +.It Ar \-a +Autoenable the VJ header compression option, if the other end of the link +is capable of VJ header compression then it will be used otherwise normal +headers will be used. +.It Ar \-c +Enables the VJ header compression option. Note that both ends of the link +must be able to use VJ header compression for this to work. +.It Ar \-e exit-command +Specifies a command to be invoked within a shell (sh -c exit-command) +before slattach exits. +.It Ar \-f +Disables the invocation of daemon() to run slattach in the background. +.It Ar \-h +Turn on cts/rts style flow control on the slip port, by default no flow +control is done. +.It Ar \-l +disable modem control (CLOCAL) and ignore carrier detect on the slip +port. By default the redial-command is invoked upon carrier drop and +slattach aborts if no redial-command is specified. +.It Ar \-n +Throw away ICMP packets. The slip interface will ignore ICMP packets +to prevent slow lines being saturated by ICMP responses. +.It Ar \-r redial-command +Specifies a command to be invoked within a shell (sh -c +redial-command) whenever carrier is lost on the line. +.It Ar \-s baudrate +Specifies the speed of the connection. If not specified, the +default of 9600 is used. +.It Ar \-u unit-command +When the line is switched to slip discipline, run +.Ql sh \-c unit-command +where and are the slip unit numbers when the line was +last opened and the unit number of the current slip connection +respecitvely. The unit number can change after redialing if you are +using more than one slip line. Slattach will abort if the unit number +changes and +.Ql \-u unit-command +was not specified. +.It Ar \-z +forces redial +redial-cmd upon startup irrespective of carrier. .It Ar ttyname Specifies the name of the tty device. .Ar Ttyname -should be a string of the form -.Ql ttyXX , -or -.Ql /dev/ttyXX . -.It Ar baudrate -Specifies the speed of the connection. If not specified, the -default of 9600 is used. +should be a string of the form +.Ql ttyXX or +.Ql /dev/ttyXX. .El .Pp Only the super-user may attach a network interface. @@ -65,19 +115,58 @@ To detach the interface, use .Dq Li ifconfig interface-name down after killing off the .Nm slattach -process. +process using +.Ql kill -INT . .Ar Interface-name is the name that is shown by .Xr netstat 1 +.Pp +To setup slattach to redial the phone when carrier is lost, use the +.Ql \-r redial-cmd +option to specify a script or executable that will reconnect the +serial line to the slip server. For example, the script could redial +the server and log in, etc. +.Pp +To reconfigure the network interface in case the slip unit number +changes, use the +.Ql \-u unit-cmd +option to specify a script or executable that will be invoked as +.Ql sh \-c unit-cmd old new, +where old and new are the slip unit numbers before and after +reconnecting the line. The unit number can change if you have more +than one line disconnect at the same time. The first to succeed in +reconnecting will get the lowest unit number. +.Pp +To kill slattach use +.Ql kill -INT +(SIGINT) which causes it to close the tty and exit. +.Pp +To force a redial, use +.Ql kill -HUP +(SIGHUP) which causes slattach to think carrier was lost and thus invoke +.Ql sh -c redial-command +to reconnect to the server. +.Pp +If you use a hard-wired connection rather than a modem, invoke +slattach with the +.Ql \-l +option in order to ignore carrier on the slip line. .Sh EXAMPLES .Bd -literal -offset indent -compact slattach ttyh8 -slattach /dev/tty01 4800 +slattach \-s 4800 /dev/tty01 +slattach \-c \-s 38400 /dev/sio01 +slattach \-r 'kermit -y dial.script >kermit.log 2>&1' .Ed .Sh DIAGNOSTICS +Look for error messages in /var/log/messages (slattach is a daemon). Messages indicating the specified interface does not exit, the -requested address is unknown, the user is not privileged and -tried to alter an interface's configuration. +requested address is unknown, the user is not privileged and tried to +alter an interface's configuration are logged there. Slattach also +logs failure to set the controlling terminal or failure to install +signal handlers. Upon connection and redial the ttyname and baud rate +are logged and on shutdown the ttyname is logged. +.Pp .Sh SEE ALSO .Xr netstat 1 , .Xr netintro 4 , diff --git a/sbin/slattach/slattach.c b/sbin/slattach/slattach.c index cd34ccb45e..b8cffd091f 100644 --- a/sbin/slattach/slattach.c +++ b/sbin/slattach/slattach.c @@ -34,6 +34,55 @@ * SUCH DAMAGE. */ +/* + * Hacks to support "-a|c|n" flags on the command line which enable VJ + * header compresion and disable ICMP. + * If this is good all rights go to B & L Jolitz, otherwise send your + * comments to Reagan (/dev/null). + * + * nerd@percival.rain.com (Michael Galassi) 92.09.03 + * + * Hacked to change from sgtty to POSIX termio style serial line control + * and added flag to enable cts/rts style flow control. + * + * blymn@awadi.com.au (Brett Lymn) 93.04.04 + * + * Put slattach in it's own process group so it can't be killed + * accidentally. Close the connection on SIGHUP and SIGINT. Write a + * syslog entry upon opening and closing the connection. Rich Murphey + * and Brad Huntting. + * + * Add '-r command' option: runs 'command' upon recieving SIGHUP + * resulting from loss of carrier. Log any errors after forking. + * Rich 8/13/93 + * + * This version of slattach includes many changes by David Greenman, Brian + * Smith, Chris Bradley, and me (Michael Galassi). None of them are + * represented as functional anywhere outside of RAINet though they do work + * for us. Documentation is limited to the usage message for now. If you + * make improovments please pass them back. + * + * Added '-u UCMD' which runs 'UCMD ' whenever the slip + * unit number changes where and are the old and new unit + * numbers, respectively. Also added the '-z' option which forces + * invocation of the redial command (-r CMD) upon startup regardless + * of whether the com driver claims (sometimes mistakenly) that + * carrier is present. Also added '-e ECMD' which runs ECMD before + * exiting. + * + * marc@escargot.rain.com (Marc Frajola) 93/09/10 + * + * Minor fixes to allow passive SLIP links to work (connections with + * modem control that do not have an associated dial command). Added + * code to re-check for carrier after dial command has been executed. + * Added SIGTERM handler to properly handle normal kill signals. Fixed + * bug in logic that caused message about no -u command to be logged + * even when -u was specified and the sl number changes. Tried to get + * rid of redundant syslog()'s to minimize console log output. Improved + * logging of improper command line options or number of command + * arguments. Removed spurious newline characters from syslog() calls. + */ + #ifndef lint char copyright[] = "@(#) Copyright (c) 1988 Regents of the University of California.\n\ @@ -41,132 +90,352 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)slattach.c 4.6 (Berkeley) 6/1/90"; +/*static char sccsid[] = "from: @(#)slattach.c 4.6 (Berkeley) 6/1/90";*/ +static char rcsid[] = "$Id"; #endif /* not lint */ #include -#include +#include +#include #include #include #include +#include #include #include +#include #include +#include #include +#include +#include +#include + +extern int errno; +extern char *sys_errlist[]; #define DEFAULT_BAUD 9600 + +void sighup_handler(); /* SIGHUP handler */ +void sigint_handler(); /* SIGINT handler */ +void sigterm_handler(); /* SIGTERM handler */ +void exit_handler(int ret); /* run exit_cmd iff specified upon exit. */ +void setup_line(); /* configure slip line */ +void attach_line(); /* switch to slip line discipline */ + +int fd = -1; +char *dev = (char *)0; int slipdisc = SLIPDISC; +int ttydisc = TTYDISC; +int flow_control = 0; /* non-zero to enable hardware flow control. */ +int modem_control = 0; /* non-zero iff we watch carrier. */ +int comstate; /* TIOCMGET current state of serial driver */ +int redial_on_startup = 0; /* iff non-zero execute redial_cmd on startup */ +int speed = DEFAULT_BAUD; +int slflags = 0; /* compression flags */ +int unit = -1; /* slip device unit number */ +int foreground = 0; +FILE *console; char devname[32]; char hostname[MAXHOSTNAMELEN]; +char *redial_cmd = 0; /* command to exec upon shutdown. */ +char *config_cmd = 0; /* command to exec if slip unit changes. */ +char *exit_cmd = 0; /* command to exec before exiting. */ +char string[100]; + +static char usage_str[] = "\ +usage: %s [-acfhlnz] [-e command] [-r command] [-s speed] [-u command] device\n\ + -a -- autoenable VJ compression\n\ + -c -- enable VJ compression\n\ + -e ECMD -- run ECMD before exiting\n\ + -f -- run in foreground (don't detach from controlling tty)\n\ + -h -- turn on cts/rts style flow control\n\ + -l -- disable modem control (CLOCAL) and ignore carrier detect\n\ + -n -- throw out ICMP packets\n\ + -r RCMD -- run RCMD upon loss of carrier\n\ + -s # -- set baud rate (default 9600)\n\ + -u UCMD -- run 'UCMD ' before switch to slip discipline\n\ + -z -- run RCMD upon startup irrespective of carrier\n"; -main(argc, argv) - int argc; - char *argv[]; +int main(int argc, char **argv) { - register int fd; - register char *dev = argv[1]; - struct sgttyb sgtty; - int speed; - - if (argc < 2 || argc > 3) { - fprintf(stderr, "usage: %s ttyname [baudrate]\n", argv[0]); - exit(1); + int option; + char name[32]; + extern char *optarg; + extern int optind; + + while ((option = getopt(argc, argv, "ace:fhlnr:s:u:z")) != EOF) { + switch (option) { + case 'a': + slflags |= SC_AUTOCOMP; + slflags &= ~SC_COMPRESS; + break; + case 'c': + slflags |= SC_COMPRESS; + slflags &= ~SC_AUTOCOMP; + break; + case 'e': + exit_cmd = (char*) strdup (optarg); + break; + case 'f': + foreground = 1; + break; + case 'h': + flow_control |= CRTSCTS; + break; + case 'l': + modem_control |= CLOCAL; + break; + case 'n': + slflags |= SC_NOICMP; + break; + case 'r': + redial_cmd = (char*) strdup (optarg); + break; + case 's': + speed = atoi(optarg); + break; + case 'u': + config_cmd = (char*) strdup (optarg); + break; + case 'z': + redial_on_startup = 1; + break; + default: + fprintf(stderr, "%s: Invalid option -- '%c'\n", + option); + case '?': + fprintf(stderr, usage_str, argv[0]); + exit_handler(1); + } + } + + if (optind == argc - 1) + dev = argv[optind]; + + if (optind < (argc - 1)) { + fprintf(stderr, "%s: Too many args, first='%s'\n", + argv[0], argv[optind]); + } + if (optind > (argc - 1)) { + fprintf(stderr, "%s: Not enough args\n", argv[0]); } - speed = argc == 3 ? findspeed(atoi(argv[2])) : findspeed(DEFAULT_BAUD); - if (speed == 0) { - fprintf(stderr, "unknown speed %s", argv[2]); - exit(1); + if (dev == (char *)0) { + fprintf(stderr, usage_str, argv[0]); + exit_handler(2); } if (strncmp(_PATH_DEV, dev, sizeof(_PATH_DEV) - 1)) { - (void)sprintf(devname, "%s/%s", _PATH_DEV, dev); + strcpy(devname, _PATH_DEV); + strcat(devname, "/"); + strncat(devname, dev, 10); dev = devname; } - if ((fd = open(dev, O_RDWR | O_NDELAY)) < 0) { - perror(dev); - exit(1); + + if (!foreground) + daemon(0,0); /* fork, setsid, chdir /, and close std*. */ + + /* Note: daemon() closes stderr, so log errors from here on. */ + (void)sprintf(name,"slattach[%d]", getpid()); + openlog(name,LOG_CONS,LOG_DAEMON); + + if ((fd = open(dev, O_RDWR | O_NONBLOCK)) < 0) { + syslog(LOG_ERR, "open(%s): %m", dev); + exit_handler(1); + } + /* acquire the serial line as a controling terminal. */ + if (ioctl(fd, TIOCSCTTY, 0) < 0) + syslog(LOG_NOTICE,"ioctl(TIOCSCTTY) failed: %s: %m"); + /* Make us the foreground process group associated with the + slip line which is our controlling terminal. */ + if (tcsetpgrp(fd, getpid()) < 0) + syslog(LOG_NOTICE,"tcsetpgrp failed: %s: %m"); + /* upon INT log a timestamp and exit. */ + if ((int)signal(SIGINT,sigint_handler) < 0) + syslog(LOG_NOTICE,"cannot install SIGINT handler: %s: %m"); + /* upon TERM log a timestamp and exit. */ + if ((int)signal(SIGTERM,sigterm_handler) < 0) + syslog(LOG_NOTICE,"cannot install SIGTERM handler: %s: %m"); + /* upon HUP redial and reconnect. */ + if ((int)signal(SIGHUP,sighup_handler) < 0) + syslog(LOG_NOTICE,"cannot install SIGHUP handler: %s: %m"); + + setup_line(); + + if (redial_on_startup) + sighup_handler(); + else + attach_line(); + if (!(modem_control & CLOCAL)) { + ioctl(fd, TIOCMGET, &comstate); + if (!(comstate & TIOCM_CD)) { /* check for carrier */ + /* force a redial if no carrier */ + kill (getpid(), SIGHUP); + } } - sgtty.sg_flags = RAW | ANYP; - sgtty.sg_ispeed = sgtty.sg_ospeed = speed; - if (ioctl(fd, TIOCSETP, &sgtty) < 0) { - perror("ioctl(TIOCSETP)"); - exit(1); + for (;;) { + sigset_t mask = 0; + sigsuspend(&mask); + } +} + +void setup_line() +{ + struct termios tty; + + tty.c_lflag = tty.c_iflag = tty.c_oflag = 0; + tty.c_cflag = CREAD | CS8 | flow_control | modem_control; + tty.c_ispeed = tty.c_ospeed = speed; + /* set the line speed and flow control */ + if (tcsetattr(fd, TCSAFLUSH, &tty) < 0) { + syslog(LOG_ERR, "tcsetattr(TCSAFLUSH): %m"); + exit_handler(1); } + /* set data terminal ready */ + if (ioctl(fd, TIOCSDTR) < 0) { + syslog(LOG_ERR, "ioctl(TIOCSDTR): %m"); + exit_handler(1); + } + /* Switch to slip line discipline. */ if (ioctl(fd, TIOCSETD, &slipdisc) < 0) { - perror("ioctl(TIOCSETD)"); - exit(1); + syslog(LOG_ERR, "ioctl(TIOCSETD): %m"); + exit_handler(1); } + /* Assert any compression or no-icmp flags. */ + if (ioctl(fd, SLIOCSFLAGS, &slflags) < 0) { + syslog(LOG_ERR, "ioctl(SLIOCSFLAGS): %m"); + exit_handler(1); + } +} + +/* switch to slip line discipline and configure the network. */ +void attach_line() +{ + int new_unit; - if (fork() > 0) - exit(0); - for (;;) - sigpause(0L); + /* find out what unit number we were assigned */ + if (ioctl(fd, SLIOCGUNIT, (caddr_t)&new_unit) < 0) { + syslog(LOG_ERR, "ioctl(SLIOCGUNIT): %m"); + exit_handler(1); + } + /* don't compare unit numbers if this is the first time to attach. */ + if (unit < 0) + unit = new_unit; + /* iff the unit number changes either invoke config_cmd or punt. */ + if (config_cmd) { + char *s; + s = (char*) malloc(strlen(config_cmd) + 32); + sprintf (s, "%s %d %d", config_cmd, unit, new_unit); + syslog(LOG_NOTICE, "Configuring %s (sl%d):", dev, unit); + syslog(LOG_NOTICE, " '%s'", s); + system(s); + free (s); + unit = new_unit; + } else { + if (new_unit != unit) { + syslog(LOG_ERR, "slip unit changed from sl%d to sl%d, but no -u CMD was specified!"); + exit_handler(1); + } + syslog(LOG_NOTICE,"sl%d connected to %s at %d baud",unit,dev,speed); + } } -struct sg_spds { - int sp_val, sp_name; -} spds[] = { -#ifdef B50 - { 50, B50 }, -#endif -#ifdef B75 - { 75, B75 }, -#endif -#ifdef B110 - { 110, B110 }, -#endif -#ifdef B150 - { 150, B150 }, -#endif -#ifdef B200 - { 200, B200 }, -#endif -#ifdef B300 - { 300, B300 }, -#endif -#ifdef B600 - { 600, B600 }, -#endif -#ifdef B1200 - { 1200, B1200 }, -#endif -#ifdef B1800 - { 1800, B1800 }, -#endif -#ifdef B2000 - { 2000, B2000 }, -#endif -#ifdef B2400 - { 2400, B2400 }, -#endif -#ifdef B3600 - { 3600, B3600 }, -#endif -#ifdef B4800 - { 4800, B4800 }, -#endif -#ifdef B7200 - { 7200, B7200 }, -#endif -#ifdef B9600 - { 9600, B9600 }, -#endif -#ifdef EXTA - { 19200, EXTA }, -#endif -#ifdef EXTB - { 38400, EXTB }, -#endif - { 0, 0 } -}; - -findspeed(speed) - register int speed; +/* Signal handler for SIGHUP when carrier is dropped. */ +void sighup_handler() { - register struct sg_spds *sp; +again: + /* reset discipline */ + if (ioctl(fd, TIOCSETD, &ttydisc) < 0) { + syslog(LOG_ERR, "ioctl(TIOCSETD): %m"); + exit_handler(1); + } + /* invoke a shell for redial_cmd or punt. */ + if (redial_cmd) { + syslog(LOG_NOTICE,"SIGHUP on %s (sl%d); running %s", + dev,unit,redial_cmd); + system(redial_cmd); + /* Now check again for carrier (dial command is done): */ + if (!(modem_control & CLOCAL)) { + ioctl(fd, TIOCMGET, &comstate); + if (!(comstate & TIOCM_CD)) { /* check for carrier */ + /* force a redial if no carrier */ + goto again; + } + } + } else { + /* + * No redial command. + * + * If modem control, just wait for carrier before + * falling through to setup_line() and attach_line(). + * If no modem control, just fall through immediately. + */ + if (!(modem_control & CLOCAL)) { + int carrier = 0; - sp = spds; - while (sp->sp_val && sp->sp_val != speed) - sp++; - return (sp->sp_name); + syslog(LOG_NOTICE, "Waiting for carrier on %s (sl%d)", + dev, unit); + + /* Now wait for carrier before attaching line: */ + while (! carrier) { + /* + * Don't burn the CPU checking for carrier; + * carrier must be polled since there is no + * way to have a signal sent when carrier + * goes high (SIGHUP can only be sent when + * carrier is dropped); so add space between + * checks for carrier: + */ + sleep(2); + + /* Check for carrier present on tty port: */ + ioctl(fd, TIOCMGET, &comstate); + if (comstate & TIOCM_CD) { + carrier = 1; + } + } + + syslog(LOG_NOTICE, "Carrier now present on %s (sl%d)", + dev, unit); + } + } + setup_line(); + attach_line(); +} +/* Signal handler for SIGINT. We just log and exit. */ +void sigint_handler() +{ + syslog(LOG_NOTICE,"sl%d on %s caught SIGINT, exiting.",unit,dev); + exit_handler(0); } +/* Signal handler for SIGTERM. We just log and exit. */ +void sigterm_handler() +{ + syslog(LOG_NOTICE,"SIGTERM on %s (sl%d); exiting",dev,unit); + exit_handler(0); +} +/* Run config_cmd if specified before exiting. */ +void exit_handler(int ret) +{ + /* + * First close the slip line in case exit_cmd wants it (like to hang + * up a modem or something). + */ + if (fd != -1) + close(fd); + /* invoke a shell for exit_cmd. */ + if (exit_cmd) { + syslog(LOG_NOTICE,"exiting after running %s", exit_cmd); + system(exit_cmd); + } + exit(ret); +} + +/* local variables: */ +/* c-indent-level: 8 */ +/* c-argdecl-indent: 0 */ +/* c-label-offset: -8 */ +/* c-continued-statement-offset: 8 */ +/* c-brace-offset: 0 */ +/* comment-column: 32 */ +/* end: */ diff --git a/sbin/st/Makefile b/sbin/st/Makefile new file mode 100644 index 0000000000..e6e31fb906 --- /dev/null +++ b/sbin/st/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 5.4 (Berkeley) 6/5/91 + +PROG= st +MAN8= st.8 + +.include diff --git a/sbin/st/st.8 b/sbin/st/st.8 new file mode 100644 index 0000000000..246afd5dd4 --- /dev/null +++ b/sbin/st/st.8 @@ -0,0 +1,232 @@ +.\" Copyright (c) 1981, 1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mt.1 6.7 (Berkeley) 7/28/91 +.\" +.Dd July 28, 1991 +.Dt ST 1 +.Os BSD 4 +.Sh NAME +.Nm st +.Nd magnetic tape manipulating program +.Sh SYNOPSIS +.Nm st +.Op Fl f Ar tapename +.Ar command +.Op Ar count +.Sh DESCRIPTION +.Nm St +is used to give commands to a magnetic tape drive. +By default +.Nm st +performs the requested operation once. Operations +may be performed multiple times by specifying +.Ar count . +Note +that +.Ar tapename +must reference a raw (not block) tape device. +.Pp +The available commands are listed below. Only as many +characters as are required to uniquely identify a command +need be specified. +.Bl -tag -width "eof, weof" +.It Cm eof , weof +Write +.Ar count +end-of-file marks at the current position on the tape. +.It Cm fsf +Forward space +.Ar count +files. +.It Cm fsr +Forward space +.Ar count +records. +.It Cm bsf +Back space +.Ar count +files. +.It Cm bsr +Back space +.Ar count +records. +.It Cm rewind +Rewind the tape +(Count is ignored). +.It Cm offline , rewoffl +Rewind the tape and place the tape unit off-line (possibly eject) +(Count is ignored). +.It Cm blocksize +Sets the block size characteristics of the device to value specified in +.Ar count. +A 0 means variable sized blocks, and anything else means fixed block, +with blocksize as that of +.Ar count. +.It Cm density +Set the density for the openned device (bits 2,3 of minor number) +.Ar count +density code as specified by the SCSI II specification. Valid values +are from 0 to 0x17. +.It The different density codes are as follows: +0x0 default for device +.br +0xE reserved for ECMA +.br + +Value Tracks Density(bpi) Code Type Reference Note +.br +0x1 9 800 NRZI R X3.22-1983 2 +.br +0x2 9 1600 PE R X3.39-1986 2 +.br +0x3 9 6250 GCR R X3.54-1986 2 +.br +0x5 4/9 8000 GCR C X3.136-1986 1 +.br +0x6 9 3200 PE R X3.157-1987 2 +.br +0x7 4 6400 IMFM C X3.116-1986 1 +.br +0x8 4 8000 GCR CS X3.158-1986 1 +.br +0x9 18 37871 GCR C X3B5/87-099 2 +.br +0xA 22 6667 MFM C X3B5/86-199 1 +.br +0xB 4 1600 PE C X3.56-1986 1 +.br +0xC 24 12690 GCR C HI-TC1 1,5 +.br +0xD 24 25380 GCR C HI-TC2 1,5 +.br +0xF 15 10000 GCR C QIC-120 1,5 +.br +0x10 18 10000 GCR C QIC-150 1,5 +.br +0x11 26 16000 GCR C QIC-320(525?) 1,5 +.br +0x12 30 51667 RLL C QIC-1350 1,5 +.br +0x13 1 61000 DDS CS X3B5/88-185A 4 +.br +0x14 1 43245 RLL CS X3.202-1991 4 +.br +0x15 1 45434 RLL CS ECMA TC17 4 +.br +0x16 48 10000 MFM C X3.193-1990 1 +.br +0x17 48 42500 MFM C X3B5/91-174 1 +.br + +where Code means: +.br +NRZI Non Return to Zero, change on ones +.br +GCR Group Code Recording +.br +PE Phase Encoded +.br +IMFM Inverted Modified Frequency Modulation +.br +MFM Modified Frequency Modulation +.br +DDS Dat Data Storage +.br +RLL Run Length Encoding +.br + +where Type means: +.br +R Real-to-Real +.br +C Cartridge +.br +CS cassette +.br + +where Notes means: +.br +1 Serial Recorded +.br +2 Parallel Recorded +.br +3 Old format know as QIC-11 +.br +4 Helical Scan +.br +5 Not ANSI standard, rather industry standard. +.br + +.It Cm status +Print status information about the tape unit. +.El +.Pp +If a tape name is not specified, and the environment variable +.Ev TAPE +does not exist; +.Nm st +uses the device +.Pa /dev/rst0 . +.Pp +.Nm St +returns a 0 exit status when the operation(s) were successful, +1 if the command was unrecognized, and 2 if an operation failed. +.Sh ENVIRONMENT +If the following environment variable exists, it is utilized by +.Nm st . +.Bl -tag -width Fl +.It Ev TAPE +.Nm St +checks the +.Ev TAPE +environment variable if the +argument +.Ar tapename +is not given. +.Sh FILES +.Bl -tag -width /dev/rmt* -compact +.It Pa /dev/rst* +Raw magnetic tape interface +.El +.Sh SEE ALSO +.\".Xr mtio 4 , +.Xr st 4 , +.Xr dd 1 , +.Xr ioctl 2 , +.Xr environ 7 +.Sh HISTORY +The +.Nm st +command appeared in 386BSD 0.1. + +.\" mt.1: mtio(4) missing diff --git a/sbin/st/st.c b/sbin/st/st.c new file mode 100644 index 0000000000..f630b763fe --- /dev/null +++ b/sbin/st/st.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 1980 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1980 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mt.c 5.6 (Berkeley) 6/6/91"; +#endif /* not lint */ + +/* + * mt -- + * magnetic tape manipulation program + */ +#include +#include +#include +#include +#include +#include + +#define equal(s1,s2) (strcmp(s1, s2) == 0) + +struct commands { + char *c_name; + int c_code; + int c_ronly; +} com[] = { + { "weof", MTWEOF, 0 }, + { "eof", MTWEOF, 0 }, + { "fsf", MTFSF, 1 }, + { "bsf", MTBSF, 1 }, + { "fsr", MTFSR, 1 }, + { "bsr", MTBSR, 1 }, + { "rewind", MTREW, 1 }, + { "offline", MTOFFL, 1 }, + { "rewoffl", MTOFFL, 1 }, + { "status", MTNOP, 1 }, + { "blocksize", MTSETBSIZ, 0 }, + { "density", MTSETDNSTY, 0 }, + { 0 } +}; + +int mtfd; +struct mtop mt_com; +struct mtget mt_status; +char *tape; + +main(argc, argv) + char **argv; +{ + void usage(); + char line[80], *getenv(); + register char *cp; + register struct commands *comp; + + if (argc > 2 && (equal(argv[1], "-t") || equal(argv[1], "-f"))) { + argc -= 2; + tape = argv[2]; + argv += 2; + } else + if ((tape = getenv("TAPE")) == NULL) + tape = DEFTAPE; + if (argc < 2) { + usage(); + exit(1); + } + cp = argv[1]; + if ((strncmp(cp, "blocksize", strlen(cp)) == 0) && argc < 3 ) { + usage(); + exit(1); + } + if ((strncmp(cp, "density", strlen(cp)) == 0) && argc < 3 ) { + usage(); + exit(1); + } + for (comp = com; comp->c_name != NULL; comp++) + if (strncmp(cp, comp->c_name, strlen(cp)) == 0) + break; + if (comp->c_name == NULL) { + fprintf(stderr, "mt: don't grok \"%s\"\n", cp); + usage(); + exit(1); + } + if ((mtfd = open(tape, comp->c_ronly ? O_RDONLY : O_RDWR)) < 0) { + perror(tape); + exit(1); + } + if (comp->c_code != MTNOP) { + mt_com.mt_op = comp->c_code; + mt_com.mt_count = (argc > 2 ? atoi(argv[2]) : 1); + if (mt_com.mt_count < 0) { + fprintf(stderr, "mt: negative repeat count\n"); + exit(1); + } + if (ioctl(mtfd, MTIOCTOP, &mt_com) < 0) { + fprintf(stderr, "%s %s %d ", tape, comp->c_name, + mt_com.mt_count); + perror("failed"); + exit(2); + } + } else { + if (ioctl(mtfd, MTIOCGET, (char *)&mt_status) < 0) { + perror("mt"); + exit(2); + } + status(&mt_status); + } +} + +#ifdef vax +#include +#include + +#include +#include +#undef b_repcnt /* argh */ +#include +#endif + +#ifdef sun +#include +#include +#endif + +#ifdef tahoe +#include +#endif + +struct tape_desc { + short t_type; /* type of magtape device */ + char *t_name; /* printing name */ + char *t_dsbits; /* "drive status" register */ + char *t_erbits; /* "error" register */ +} tapes[] = { +#ifdef vax + { MT_ISTS, "ts11", 0, TSXS0_BITS }, + { MT_ISHT, "tm03", HTDS_BITS, HTER_BITS }, + { MT_ISTM, "tm11", 0, TMER_BITS }, + { MT_ISMT, "tu78", MTDS_BITS, 0 }, + { MT_ISUT, "tu45", UTDS_BITS, UTER_BITS }, +#endif +#if defined(sun) + { MT_ISCPC, "TapeMaster", TMS_BITS, 0 }, + { MT_ISAR, "Archive", ARCH_CTRL_BITS, ARCH_BITS }, +#endif +#ifdef tahoe + { MT_ISCY, "cipher", CYS_BITS, CYCW_BITS }, +#endif +#if defined (__386BSD__) + { MT_ISAR, "Archive/Tandberg?", 0, 0 }, +#endif + { 0 } +}; + +/* + * Interpret the status buffer returned + */ +status(bp) + register struct mtget *bp; +{ + register struct tape_desc *mt; + + for (mt = tapes; mt->t_type; mt++) + if (mt->t_type == bp->mt_type) + break; + if (mt->t_type == 0) { + printf("unknown tape drive type (%d)\n", bp->mt_type); + return; + } + printf("%s tape drive, residual=%d, blocksize=%d\n", + mt->t_name, bp->mt_resid, bp->mt_bsiz); + printf("Density: dflt(0) = %d(0x%x) 1 = %d(0x%x), 2 = %d(0x%x), 3 = %d(0x%x)\n", + bp->mt_dns_dflt, bp->mt_dns_dflt, + bp->mt_dns_dsty1, bp->mt_dns_dsty1, + bp->mt_dns_dsty2, bp->mt_dns_dsty2, + bp->mt_dns_dsty3, bp->mt_dns_dsty3); + printreg("ds", bp->mt_dsreg, mt->t_dsbits); + printreg("\ner", bp->mt_erreg, mt->t_erbits); + putchar('\n'); +} + +/* + * Print a register a la the %b format of the kernel's printf + */ +printreg(s, v, bits) + char *s; + register char *bits; + register unsigned short v; +{ + register int i, any = 0; + register char c; + + if (bits && *bits == 8) + printf("%s=%o", s, v); + else + printf("%s=%x", s, v); + bits++; + if (v && bits) { + putchar('<'); + while (i = *bits++) { + if (v & (1 << (i-1))) { + if (any) + putchar(','); + any = 1; + for (; (c = *bits) > 32; bits++) + putchar(c); + } else + for (; *bits > 32; bits++) + ; + } + putchar('>'); + } +} +void usage() { + register struct commands *comp; + + fprintf(stderr, "Usage: st [ -f tape ] command [ count ] \n"); + fprintf(stderr, " Where command is one of:\n"); + for (comp=com; comp->c_name != NULL; comp++) { + fprintf(stderr, " %s\n", comp->c_name); + } + fprintf(stderr,"Note that the count argument is required\n"); + fprintf(stderr, "with the \"blocksize\" , and the density setting commands.\n"); + fprintf(stderr, "Note that the count argument is a base 10 number\n"); +} diff --git a/sbin/swapon/Makefile b/sbin/swapon/Makefile index f47aba18c9..610f920e8b 100644 --- a/sbin/swapon/Makefile +++ b/sbin/swapon/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= swapon -MAN8= swapon.0 +MAN8= swapon.8 .include diff --git a/sbin/tunefs/Makefile b/sbin/tunefs/Makefile index bc54f4b6de..657423dfbf 100644 --- a/sbin/tunefs/Makefile +++ b/sbin/tunefs/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= tunefs -MAN8= tunefs.0 +MAN8= tunefs.8 .include diff --git a/sbin/umount/umount.c b/sbin/umount/umount.c index 55b0d5284e..8f3a6a2512 100644 --- a/sbin/umount/umount.c +++ b/sbin/umount/umount.c @@ -56,7 +56,6 @@ static char sccsid[] = "@(#)umount.c 5.16 (Berkeley) 6/3/91"; #include #endif -#include #include #include @@ -123,12 +122,9 @@ main(argc, argv) if (all) { if (argc > 0) usage(); - if (setfsent() == 0) - perror(FSTAB), exit(1); - umountall(typelist); - exit(0); - } else - setfsent(); + errs += umountall(typelist); + exit(errs); + } while (argc > 0) { if (umountfs(*argv++, 0) == 0) errs++; @@ -154,60 +150,26 @@ usage() umountall(typelist) char **typelist; { - register struct fstab *fs; - struct fstab *allocfsent(); + int i, mntsize, errors; + struct statfs *mntbuf; - if ((fs = getfsent()) == (struct fstab *)0) - return; - fs = allocfsent(fs); - umountall(typelist); - if (strcmp(fs->fs_file, "/") == 0) { - freefsent(fs); - return; + if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) { + fprintf(stderr, "umount: cannot get mount information.\n"); + exit(1); } - if (strcmp(fs->fs_type, FSTAB_RW) && - strcmp(fs->fs_type, FSTAB_RO) && - strcmp(fs->fs_type, FSTAB_RQ)) { - freefsent(fs); - return; + for (errors = 0, i = mntsize - 1; i >= 0; i--) { + if (badtype(mntbuf[i].f_type, typelist) || + (strcmp(mntbuf[i].f_mntonname, "/") == 0)) + continue; + if (!fake && unmount(mntbuf[i].f_mntonname, fflag) < 0) { + perror(mntbuf[i].f_mntonname); + errors ++; + } + if (vflag) + fprintf(stderr, "%s: Unmounted from %s\n", + mntbuf[i].f_mntfromname, mntbuf[i].f_mntonname); } - (void) umountfs(fs->fs_file, typelist); - freefsent(fs); -} - -struct fstab * -allocfsent(fs) - register struct fstab *fs; -{ - register struct fstab *new; - register char *cp; - - new = (struct fstab *)malloc((unsigned)sizeof (*fs)); - cp = (char *)malloc((unsigned)strlen(fs->fs_file) + 1); - strcpy(cp, fs->fs_file); - new->fs_file = cp; - cp = (char *)malloc((unsigned)strlen(fs->fs_type) + 1); - strcpy(cp, fs->fs_type); - new->fs_type = cp; - cp = (char *)malloc((unsigned)strlen(fs->fs_spec) + 1); - strcpy(cp, fs->fs_spec); - new->fs_spec = cp; - new->fs_passno = fs->fs_passno; - new->fs_freq = fs->fs_freq; - return (new); -} - -freefsent(fs) - register struct fstab *fs; -{ - - if (fs->fs_file) - free(fs->fs_file); - if (fs->fs_spec) - free(fs->fs_spec); - if (fs->fs_type) - free(fs->fs_type); - free((char *)fs); + return errors; } umountfs(name, typelist) diff --git a/share/Makefile b/share/Makefile index c54c969d91..f2ac4ff2ac 100644 --- a/share/Makefile +++ b/share/Makefile @@ -1,16 +1,10 @@ -# @(#)Makefile 5.8.1.1 (Berkeley) 5/7/91 -# -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 2 90001 -# -------------------- ----- ---------------------- -# -# 02 Jun 93 Rodney W. Grimes Add misc -# 02 Jun 93 Rodney W. Grimes Add dict and doc +# From: @(#)Makefile 5.8.1.1 (Berkeley) 5/7/91 +# $Id$ # Missing: ms # Broken: doc -SUBDIR= dict man me misc mk skel tabset termcap tmac zoneinfo +SUBDIR= dict man me misc mk skel syscons tabset \ + termcap tmac zoneinfo .include diff --git a/share/doc/Makefile b/share/doc/Makefile index 03f5c78fe2..3ed3a2a56a 100644 --- a/share/doc/Makefile +++ b/share/doc/Makefile @@ -1,8 +1,12 @@ -# @(#)Makefile 5.1.1.1 (Berkeley) 5/7/91 +# from: @(#)Makefile 5.1.1.1 (Berkeley) 5/7/91 +# Makefile,v 1.3 1993/07/31 15:29:22 mycroft Exp #Missing: index iso papers ps2 ucs + SUBDIR= ps1 smm usd -all depend lint tags: +all paper print clean: _SUBDIRUSE + +depend lint tags: .include diff --git a/share/doc/ps1/04.pascal/Makefile b/share/doc/ps1/04.pascal/Makefile index a119109172..ae228c28b9 100644 --- a/share/doc/ps1/04.pascal/Makefile +++ b/share/doc/ps1/04.pascal/Makefile @@ -1,6 +1,9 @@ -# @(#)Makefile 6.2 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.2 (Berkeley) 6/30/90 +# Makefile,v 1.3 1993/07/31 15:34:01 mycroft Exp DIR= ps1/04.pascal +DOC= 04.pascal + SRCS= puman0.n puman1.n puman2.n puman3.n puman4.n puman5.n pumanA.n FIGURES=firstout firstobjout firstobjout2 bigger2.p bigger3.p bigout1 \ bigger6.p bigout2 bigout3 bigout4 primeout1 primeout2 digitsout \ @@ -9,11 +12,11 @@ FIGURES=firstout firstobjout firstobjout2 bigger2.p bigger3.p bigout1 \ xxxxqqqout xxxxqqqout2 primes primes-d comments1.p commentsout CLEANFILES+=csfix ${FIGURES} -paper.${PRINTER}: ${FIGURES} - ${SOELIM} ${SRCS} | ${TBL} | ${ROFF} > ${.TARGET} - .include +${DOC}.${PRINTER}: ${FIGURES} + ${SOELIM} ${SRCS} | ${TBL} | ${ROFF} > ${.TARGET} + csfix: ${CC} -O ${.CURDIR}/csfix.c -o ${.TARGET} diff --git a/share/doc/ps1/04.pascal/csfix.c b/share/doc/ps1/04.pascal/csfix.c index ed42557632..3174d6a642 100644 --- a/share/doc/ps1/04.pascal/csfix.c +++ b/share/doc/ps1/04.pascal/csfix.c @@ -38,7 +38,8 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)csfix.c 6.3 (Berkeley) 4/17/91"; +/*static char sccsid[] = "from: @(#)csfix.c 6.3 (Berkeley) 4/17/91";*/ +static char rcsid[] = "csfix.c,v 1.2 1993/08/01 18:23:36 mycroft Exp"; #endif /* not lint */ #include diff --git a/share/doc/ps1/06.sysman/Makefile b/share/doc/ps1/06.sysman/Makefile index 4e5dd65cda..f177ab9d98 100644 --- a/share/doc/ps1/06.sysman/Makefile +++ b/share/doc/ps1/06.sysman/Makefile @@ -1,11 +1,14 @@ -# @(#)Makefile 6.2 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.2 (Berkeley) 6/30/90 +# Makefile,v 1.5 1993/07/31 15:33:55 mycroft Exp DIR= ps1/06.sysman +DOC= 06.sysman + SRCS= 0.t 1.0.t 1.1.t 1.2.t 1.3.t 1.4.t 1.5.t 1.6.t 1.7.t \ 2.0.t 2.1.t 2.2.t 2.3.t 2.4.t 2.5.t a.t -MACROS= -msU - -paper.${PRINTER}: ${SRCS} - ${TBL} ${SRCS} | ${ROFF} > ${.TARGET} +MACROS= -ms .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${TBL} ${SRCS} ) | ${ROFF} > ${.TARGET} diff --git a/share/doc/ps1/06.sysman/a.t b/share/doc/ps1/06.sysman/a.t index c6e482a562..4b1223176e 100644 --- a/share/doc/ps1/06.sysman/a.t +++ b/share/doc/ps1/06.sysman/a.t @@ -77,9 +77,6 @@ lw(1.6i) aw(3i). memory management definitions sbrk change data section size sstk\(dg change stack section size -.FS -\(dg Not supported in 4.3BSD. -.FE getpagesize get memory page size mmap\(dg map pages of memory msync\(dg flush modified mapped pages to filesystem @@ -89,6 +86,9 @@ madvise\(dg give memory management advice mincore\(dg determine core residency of pages msleep\(dg sleep on a lock mwakeup\(dg wakeup process sleeping on a lock +.FS +\(dg Not supported in 4.3BSD. +.FE .TE .in -5 .h 1.3 "Signals diff --git a/share/doc/ps1/07.ipctut/Makefile b/share/doc/ps1/07.ipctut/Makefile index 822b6fe284..08d070fd1f 100644 --- a/share/doc/ps1/07.ipctut/Makefile +++ b/share/doc/ps1/07.ipctut/Makefile @@ -1,16 +1,16 @@ -# @(#)Makefile 6.5 (Berkeley) 1/2/91 +# from: @(#)Makefile 6.5 (Berkeley) 1/2/91 +# Makefile,v 1.5 1993/07/31 15:33:50 mycroft Exp DIR= ps1/07.ipctut +DOC= 07.ipctut + SRCS= tutor.me MACROS= -me EXTRA= accept.grn dgramread.c dgramsend.c gremlins pipe.c pipe.grn \ socketpair.c socketpair.grn strchkread.c streamread.c streamwrite.c \ udgramread.c udgramsend.c ustreamread.c ustreamwrite.c -print: paper.${PRINTER} - lpr -P${PRINTER} -n paper.${PRINTER} - -paper.${PRINTER}: ${SRCS} ${EXTRA} - ${TBL} ${SRCS} | ${GREMLIN} | ${ROFF} > ${.TARGET} - .include + +${DOC}.${PRINTER}: ${SRCS} ${EXTRA} + ( cd ${.CURDIR} ; ${TBL} ${SRCS} ) | ${GREMLIN} | ${ROFF} > ${.TARGET} diff --git a/share/doc/ps1/07.ipctut/dgramread.c b/share/doc/ps1/07.ipctut/dgramread.c index e197f09903..87f15adf42 100644 --- a/share/doc/ps1/07.ipctut/dgramread.c +++ b/share/doc/ps1/07.ipctut/dgramread.c @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)dgramread.c 6.4 (Berkeley) 4/17/91 +.\" from: @(#)dgramread.c 6.4 (Berkeley) 4/17/91 +.\" dgramread.c,v 1.2 1993/08/01 18:23:31 mycroft Exp .\" #include #include diff --git a/share/doc/ps1/07.ipctut/dgramsend.c b/share/doc/ps1/07.ipctut/dgramsend.c index 2632114658..9e8f8fe8eb 100644 --- a/share/doc/ps1/07.ipctut/dgramsend.c +++ b/share/doc/ps1/07.ipctut/dgramsend.c @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)dgramsend.c 6.4 (Berkeley) 4/17/91 +.\" from: @(#)dgramsend.c 6.4 (Berkeley) 4/17/91 +.\" dgramsend.c,v 1.2 1993/08/01 18:23:30 mycroft Exp .\" #include #include diff --git a/share/doc/ps1/07.ipctut/pipe.c b/share/doc/ps1/07.ipctut/pipe.c index dd2910bf4b..f002e7c612 100644 --- a/share/doc/ps1/07.ipctut/pipe.c +++ b/share/doc/ps1/07.ipctut/pipe.c @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)pipe.c 6.4 (Berkeley) 4/17/91 +.\" from: @(#)pipe.c 6.4 (Berkeley) 4/17/91 +.\" pipe.c,v 1.2 1993/08/01 18:23:32 mycroft Exp .\" #include diff --git a/share/doc/ps1/07.ipctut/socketpair.c b/share/doc/ps1/07.ipctut/socketpair.c index 8c8d78f109..a895e80201 100644 --- a/share/doc/ps1/07.ipctut/socketpair.c +++ b/share/doc/ps1/07.ipctut/socketpair.c @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)socketpair.c 6.4 (Berkeley) 4/17/91 +.\" from: @(#)socketpair.c 6.4 (Berkeley) 4/17/91 +.\" socketpair.c,v 1.2 1993/08/01 18:23:29 mycroft Exp .\" #include #include diff --git a/share/doc/ps1/07.ipctut/strchkread.c b/share/doc/ps1/07.ipctut/strchkread.c index ff6e9bebed..005463d7e6 100644 --- a/share/doc/ps1/07.ipctut/strchkread.c +++ b/share/doc/ps1/07.ipctut/strchkread.c @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)strchkread.c 6.4 (Berkeley) 4/17/91 +.\" from: @(#)strchkread.c 6.4 (Berkeley) 4/17/91 +.\" strchkread.c,v 1.2 1993/08/01 18:23:29 mycroft Exp .\" #include #include diff --git a/share/doc/ps1/07.ipctut/streamread.c b/share/doc/ps1/07.ipctut/streamread.c index e14eaa9a6c..d985287167 100644 --- a/share/doc/ps1/07.ipctut/streamread.c +++ b/share/doc/ps1/07.ipctut/streamread.c @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)streamread.c 6.4 (Berkeley) 4/17/91 +.\" from: @(#)streamread.c 6.4 (Berkeley) 4/17/91 +.\" streamread.c,v 1.2 1993/08/01 18:23:27 mycroft Exp .\" #include #include diff --git a/share/doc/ps1/07.ipctut/streamwrite.c b/share/doc/ps1/07.ipctut/streamwrite.c index 490e1fbaae..6595bcf23e 100644 --- a/share/doc/ps1/07.ipctut/streamwrite.c +++ b/share/doc/ps1/07.ipctut/streamwrite.c @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)streamwrite.c 6.4 (Berkeley) 4/17/91 +.\" from: @(#)streamwrite.c 6.4 (Berkeley) 4/17/91 +.\" streamwrite.c,v 1.2 1993/08/01 18:23:28 mycroft Exp .\" #include #include diff --git a/share/doc/ps1/07.ipctut/udgramread.c b/share/doc/ps1/07.ipctut/udgramread.c index 7ac2925190..7084ca45ed 100644 --- a/share/doc/ps1/07.ipctut/udgramread.c +++ b/share/doc/ps1/07.ipctut/udgramread.c @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)udgramread.c 6.5 (Berkeley) 4/17/91 +.\" from: @(#)udgramread.c 6.5 (Berkeley) 4/17/91 +.\" udgramread.c,v 1.2 1993/08/01 18:23:26 mycroft Exp .\" #include #include diff --git a/share/doc/ps1/07.ipctut/udgramsend.c b/share/doc/ps1/07.ipctut/udgramsend.c index 32f3401833..b960cf3edd 100644 --- a/share/doc/ps1/07.ipctut/udgramsend.c +++ b/share/doc/ps1/07.ipctut/udgramsend.c @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)udgramsend.c 6.4 (Berkeley) 4/17/91 +.\" from: @(#)udgramsend.c 6.4 (Berkeley) 4/17/91 +.\" udgramsend.c,v 1.2 1993/08/01 18:23:25 mycroft Exp .\" #include #include diff --git a/share/doc/ps1/07.ipctut/ustreamread.c b/share/doc/ps1/07.ipctut/ustreamread.c index b64e9490df..4209481ecd 100644 --- a/share/doc/ps1/07.ipctut/ustreamread.c +++ b/share/doc/ps1/07.ipctut/ustreamread.c @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)ustreamread.c 6.5 (Berkeley) 4/17/91 +.\" from: @(#)ustreamread.c 6.5 (Berkeley) 4/17/91 +.\" ustreamread.c,v 1.2 1993/08/01 18:23:24 mycroft Exp .\" #include #include diff --git a/share/doc/ps1/07.ipctut/ustreamwrite.c b/share/doc/ps1/07.ipctut/ustreamwrite.c index 4833d247ff..fcb3d3af99 100644 --- a/share/doc/ps1/07.ipctut/ustreamwrite.c +++ b/share/doc/ps1/07.ipctut/ustreamwrite.c @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)ustreamwrite.c 6.5 (Berkeley) 4/17/91 +.\" from: @(#)ustreamwrite.c 6.5 (Berkeley) 4/17/91 +.\" ustreamwrite.c,v 1.2 1993/08/01 18:23:22 mycroft Exp .\" #include #include diff --git a/share/doc/ps1/08.ipc/5.t b/share/doc/ps1/08.ipc/5.t index ce2ee46e88..11b195d141 100644 --- a/share/doc/ps1/08.ipc/5.t +++ b/share/doc/ps1/08.ipc/5.t @@ -552,7 +552,7 @@ to return a stream socket in with a privileged port number: .DS int lport = IPPORT_RESERVED \- 1; int s; -... +\&... s = rresvport(&lport); if (s < 0) { if (errno == EAGAIN) diff --git a/share/doc/ps1/08.ipc/Makefile b/share/doc/ps1/08.ipc/Makefile index 9b94057541..3bdacaefa0 100644 --- a/share/doc/ps1/08.ipc/Makefile +++ b/share/doc/ps1/08.ipc/Makefile @@ -1,10 +1,13 @@ -# @(#)Makefile 1.4 (Berkeley) 6/30/90 +# from: @(#)Makefile 1.4 (Berkeley) 6/30/90 +# Makefile,v 1.5 1993/07/31 15:33:44 mycroft Exp DIR= ps1/08.ipc +DOC= 08.ipc + SRCS= 0.t 1.t 2.t 3.t 4.t 5.t MACROS= -ms -ipc.${PRINTER}: ${SRCS} - ${TBL} ${SRCS} | ${ROFF} > ${.TARGET} - .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${TBL} ${SRCS} ) | ${ROFF} > ${.TARGET} diff --git a/share/doc/ps1/13.rcs/Makefile b/share/doc/ps1/13.rcs/Makefile index af0e1e68ae..c8fddff126 100644 --- a/share/doc/ps1/13.rcs/Makefile +++ b/share/doc/ps1/13.rcs/Makefile @@ -1,10 +1,18 @@ -# @(#)Makefile 6.2 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.2 (Berkeley) 6/30/90 +# Makefile,v 1.5 1993/07/31 15:33:38 mycroft Exp DIR= ps1/13.rcs -SRCS= rcs.ms -MACROS= -msU +DOC= 13.rcs -paper.${PRINTER}: ${SRCS} - ${ROFF} ${SRCS} > ${.TARGET} +MSRC= rcs.man +SRCS= rcs.ms +MACS= -ms +MMACS= -mandoc .include + +${DOC}.${PRINTER}: ${SRCS} ${DOC}.man.${PRINTER} + ( cd ${.CURDIR} ; ${ROFF} ${MACS} ${SRCS} ) > ${.TARGET} + +${DOC}.man.${PRINTER}: ${MSRC} + ( cd ${.CURDIR} ; ${ROFF} ${MMACS} ${MSRC} ) > ${.TARGET} diff --git a/share/doc/ps1/13.rcs/man/Makefile b/share/doc/ps1/13.rcs/man/Makefile index 0ecf3c2144..296a909ed5 100644 --- a/share/doc/ps1/13.rcs/man/Makefile +++ b/share/doc/ps1/13.rcs/man/Makefile @@ -1,3 +1,5 @@ +# Makefile,v 1.2 1993/08/02 17:51:17 mycroft Exp + DEST = $(DESTDIR)/usr/man/mann MAKEFILE = Makefile diff --git a/share/doc/ps1/13.rcs/man/ci.1 b/share/doc/ps1/13.rcs/man/ci.1 index 75e3822839..b3527e3454 100644 --- a/share/doc/ps1/13.rcs/man/ci.1 +++ b/share/doc/ps1/13.rcs/man/ci.1 @@ -1,3 +1,4 @@ +.\" ci.1,v 1.2 1993/08/02 17:51:19 mycroft Exp -*- nroff -*- .TH CI 1 6/29/83 "Purdue University" .SH NAME ci \- check in RCS revisions @@ -181,9 +182,9 @@ Author: Walter F. Tichy, Purdue University, West Lafayette, IN, 47907. .sp 0 Revision Number: -.VL $Revision: 3.1 $ +.VL 1.2 ; Release Date: -.VL $Date: 83/04/04 15:52:35 $ +.VL 1993/08/02 17:51:19 \&. .sp 0 Copyright \(co 1982 by Walter F. Tichy. diff --git a/share/doc/ps1/13.rcs/man/co.1 b/share/doc/ps1/13.rcs/man/co.1 index 486527aa37..1b44c4770d 100644 --- a/share/doc/ps1/13.rcs/man/co.1 +++ b/share/doc/ps1/13.rcs/man/co.1 @@ -1,3 +1,4 @@ +.\" co.1,v 1.2 1993/08/02 17:51:20 mycroft Exp -*- nroff -*- .TH CO 1 6/29/83 "Purdue University" .SH NAME co \- check out RCS revisions @@ -170,10 +171,10 @@ Keywords and their corresponding values: .TP 13 $\&Author$ The login name of the user who checked in the revision. -. \.TP -. \$\&Class$ -. \Prog, Def, Doc, or Test, depending on the class assigned to the file -. \with the \fB-c\fR option of the \fIrcs\fR command. +.\".TP +.\"$\&Class$ +.\"Prog, Def, Doc, or Test, depending on the class assigned to the file +.\"with the \fB-c\fR option of the \fIrcs\fR command. .TP $\&Date$ The date and time the revision was checked in. @@ -244,9 +245,9 @@ Author: Walter F. Tichy, Purdue University, West Lafayette, IN, 47907. .sp 0 Revision Number: -.VL $Revision: 3.1 $ +.VL 1.2 ; Release Date: -.VL $Date: 83/04/04 15:53:40 $ +.VL 1993/08/02 17:51:20 \&. .sp 0 Copyright \(co 1982 by Walter F. Tichy. diff --git a/share/doc/ps1/13.rcs/man/ident.1 b/share/doc/ps1/13.rcs/man/ident.1 index d747e65aa4..a61c02cf16 100644 --- a/share/doc/ps1/13.rcs/man/ident.1 +++ b/share/doc/ps1/13.rcs/man/ident.1 @@ -1,3 +1,4 @@ +.\" ident.1,v 1.2 1993/08/02 17:51:21 mycroft Exp -*- nroff -*- .TH IDENT 1 6/29/83 "Purdue University" .SH NAME ident \- identify files @@ -50,9 +51,9 @@ Author: Walter F. Tichy, Purdue University, West Lafayette, IN, 47907. .sp 0 Revision Number: -.VL $Revision: 3.0 $ +.VL 1.2 ; Release Date: -.VL $Date: 82/12/04 12:09:59 $ +.VL 1993/08/02 17:51:21 \&. .sp 0 Copyright \(co 1982 by Walter F. Tichy. diff --git a/share/doc/ps1/13.rcs/man/merge.1 b/share/doc/ps1/13.rcs/man/merge.1 index 6352672809..c9320a2411 100644 --- a/share/doc/ps1/13.rcs/man/merge.1 +++ b/share/doc/ps1/13.rcs/man/merge.1 @@ -1,3 +1,4 @@ +.\" merge.1,v 1.2 1993/08/02 17:51:22 mycroft Exp -*- nroff -*- .TH MERGE 1 6/29/83 "Purdue University" .SH NAME merge \- three-way file merge @@ -35,9 +36,9 @@ Author: Walter F. Tichy, Purdue University, West Lafayette, IN, 47907. .sp 0 Revision Number: -.VL $Revision: 3.0 $ +.VL 1.2 ; Release Date: -.VL $Date: 82/11/25 11:43:41 $ +.VL 1993/08/02 17:51:22 \&. .sp 0 Copyright \(co 1982 by Walter F. Tichy. diff --git a/share/doc/ps1/13.rcs/man/rcs.1 b/share/doc/ps1/13.rcs/man/rcs.1 index 65011c9b21..26f6fdc023 100644 --- a/share/doc/ps1/13.rcs/man/rcs.1 +++ b/share/doc/ps1/13.rcs/man/rcs.1 @@ -1,3 +1,4 @@ +.\" rcs.1,v 1.2 1993/08/02 17:51:23 mycroft Exp -*- nroff -*- .TH RCS 1 6/29/83 "Purdue University" .SH NAME rcs \- change RCS file attributes @@ -146,9 +147,9 @@ Author: Walter F. Tichy, Purdue University, West Lafayette, IN, 47907. .sp 0 Revision Number: -.VL $Revision: 3.1 $ +.VL 1.2 ; Release Date: -.VL $Date: 83/04/04 15:58:23 $ +.VL 1993/08/02 17:51:23 \&. .sp 0 Copyright \(co 1982 by Walter F. Tichy. diff --git a/share/doc/ps1/13.rcs/man/rcsdiff.1 b/share/doc/ps1/13.rcs/man/rcsdiff.1 index 11e778aec9..000a4f0a7f 100644 --- a/share/doc/ps1/13.rcs/man/rcsdiff.1 +++ b/share/doc/ps1/13.rcs/man/rcsdiff.1 @@ -1,3 +1,4 @@ +.\" rcsdiff.1,v 1.2 1993/08/02 17:51:24 mycroft Exp -*- nroff -*- .TH RCSDIFF 1 5/19/86 "Purdue University" .SH NAME rcsdiff \- compare RCS revisions @@ -46,9 +47,9 @@ Author: Walter F. Tichy, Purdue University, West Lafayette, IN, 47907. .sp 0 Revision Number: -.VL $Revision: 1.2 $ +.VL 1.2 ; Release Date: -.VL $Date: 86/05/19 02:27:17 $ +.VL 1993/08/02 17:51:24 \&. .sp 0 Copyright \(co 1982 by Walter F. Tichy. diff --git a/share/doc/ps1/13.rcs/man/rcsfile.5 b/share/doc/ps1/13.rcs/man/rcsfile.5 index fb56758826..182c47656a 100644 --- a/share/doc/ps1/13.rcs/man/rcsfile.5 +++ b/share/doc/ps1/13.rcs/man/rcsfile.5 @@ -1,3 +1,4 @@ +.\" rcsfile.5,v 1.2 1993/08/02 17:51:25 mycroft Exp -*- nroff -*- .TH RCSFILE 5 6/29/83 "Purdue University" .SH NAME rcsfile \- format of RCS file @@ -133,9 +134,9 @@ Author: Walter F. Tichy, Purdue University, West Lafayette, IN, 47907. .sp 0 Revision Number: -.VL $Revision: 3.0 $ +.VL 1.2 ; Release Date: -.VL $Date: 82/11/18 16:36:39 $ +.VL 1993/08/02 17:51:25 \&. .sp 0 Copyright \(co 1982 by Walter F. Tichy. diff --git a/share/doc/ps1/13.rcs/man/rcsintro.1 b/share/doc/ps1/13.rcs/man/rcsintro.1 index 3752ccca5a..b0f310a059 100644 --- a/share/doc/ps1/13.rcs/man/rcsintro.1 +++ b/share/doc/ps1/13.rcs/man/rcsintro.1 @@ -1,3 +1,4 @@ +.\" rcsintro.1,v 1.2 1993/08/02 17:51:26 mycroft Exp -*- nroff -*- .TH RCSINTRO 1 "June 29, 1983" "Purdue University" .SH NAME rcsintro \- introduction to RCS commands diff --git a/share/doc/ps1/13.rcs/man/rcsmerge.1 b/share/doc/ps1/13.rcs/man/rcsmerge.1 index 078380e10e..bb0850bc65 100644 --- a/share/doc/ps1/13.rcs/man/rcsmerge.1 +++ b/share/doc/ps1/13.rcs/man/rcsmerge.1 @@ -1,3 +1,4 @@ +.\" rcsmerge.1,v 1.2 1993/08/02 17:51:27 mycroft Exp -*- nroff -*- .TH RCSMERGE 1 6/29/83 "Purdue University" .SH NAME rcsmerge \- merge RCS revisions @@ -54,9 +55,9 @@ Author: Walter F. Tichy, Purdue University, West Lafayette, IN, 47907. .sp 0 Revision Number: -.VL $Revision: 3.0 $ +.VL 1.2 ; Release Date: -.VL $Date: 83/01/15 18:55:16 $ +.VL 1993/08/02 17:51:27 \&. .sp 0 Copyright \(co 1982 by Walter F. Tichy. diff --git a/share/doc/ps1/13.rcs/man/rlog.1 b/share/doc/ps1/13.rcs/man/rlog.1 index 09495ae341..caa62f6643 100644 --- a/share/doc/ps1/13.rcs/man/rlog.1 +++ b/share/doc/ps1/13.rcs/man/rlog.1 @@ -1,3 +1,4 @@ +.\" rlog.1,v 1.2 1993/08/02 17:51:28 mycroft Exp -*- nroff -*- .TH RLOG 1 6/29/83 "Purdue University" .SH NAME rlog \- print log messages and other information about RCS files @@ -112,9 +113,9 @@ Author: Walter F. Tichy, Purdue University, West Lafayette, IN, 47907. .sp 0 Revision Number: -.VL $Revision: 3.2 $ +.VL 1.2 ; Release Date: -.VL $Date: 83/05/11 11:11:22 $ +.VL 1993/08/02 17:51:28 \&. .sp 0 Copyright \(co 1982 by Walter F. Tichy. diff --git a/share/doc/ps1/13.rcs/man/sccstorcs.1 b/share/doc/ps1/13.rcs/man/sccstorcs.1 index a9754919e3..453e4e6cbf 100644 --- a/share/doc/ps1/13.rcs/man/sccstorcs.1 +++ b/share/doc/ps1/13.rcs/man/sccstorcs.1 @@ -1,3 +1,4 @@ +.\" sccstorcs.1,v 1.2 1993/08/02 17:51:29 mycroft Exp -*- nroff -*- .TH SCCSTORCS 8 "29 June 1983" .UC 4 .SH NAME diff --git a/share/doc/ps1/13.rcs/rcs.ms b/share/doc/ps1/13.rcs/rcs.ms index f0bedea890..ebc54e6cc7 100644 --- a/share/doc/ps1/13.rcs/rcs.ms +++ b/share/doc/ps1/13.rcs/rcs.ms @@ -265,19 +265,19 @@ Automatic Identification RCS can put special strings for identification into your source and object code. To obtain such identification, place the marker .DS -$Header$ +/b/source/CVS/src/share/doc/ps1/13.rcs/rcs.ms,v 1.1.1.1 1993/03/21 09:48:22 cgd Exp .DE into your text, for instance inside a comment. RCS will replace this marker with a string of the form .DS -$Header: filename revisionnumber date time author state $ +/b/source/CVS/src/share/doc/ps1/13.rcs/rcs.ms,v 1.1.1.1 1993/03/21 09:48:22 cgd Exp .DE You never need to touch this string, because RCS keeps it up to date automatically. To propagate the marker into your object code, simply put it into a literal character string. In C, this is done as follows: .DS -static char rcsid[] = "$Header$"; +static char rcsid[] = "/b/source/CVS/src/share/doc/ps1/13.rcs/rcs.ms,v 1.1.1.1 1993/03/21 09:48:22 cgd Exp"; .DE The command \fIident\fR extracts such markers from any file, even object code. Thus, \fIident\fR helps you to find out @@ -285,7 +285,10 @@ which revisions of which modules were used in a given program. .PP You may also find it useful to put the marker .DS -$Log$ +rcs.ms,v +.\" Revision 1.1.1.1 1993/03/21 09:48:22 cgd +.\" initial import of 386bsd-0.1 sources +.\" .DE into your text, inside a comment. This marker accumulates the log messages that are requested during checkin. diff --git a/share/doc/ps1/14.sccs/Makefile b/share/doc/ps1/14.sccs/Makefile index 5ef961bee1..d41fca6b76 100644 --- a/share/doc/ps1/14.sccs/Makefile +++ b/share/doc/ps1/14.sccs/Makefile @@ -1,10 +1,13 @@ -# @(#)Makefile 6.2 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.2 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:33:32 mycroft Exp DIR= ps1/14.sccs +DOC= 14.sccs + SRCS= sccs.me MACROS= -me -paper.${PRINTER}: ${SRCS} - ${ROFF} ${SRCS} > ${.TARGET} - .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${ROFF} ${COMPAT} ${SRCS} ) > ${.TARGET} diff --git a/share/doc/ps1/18.curses/Makefile b/share/doc/ps1/18.curses/Makefile index bd641fc495..4bcd1a52b5 100644 --- a/share/doc/ps1/18.curses/Makefile +++ b/share/doc/ps1/18.curses/Makefile @@ -1,14 +1,19 @@ -# @(#)Makefile 6.4 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.4 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:33:26 mycroft Exp DIR= ps1/18.curses -SRCS= Master macros appen.A appen.B appen.C intro.0 intro.1 \ - intro.2 intro.3 intro.4 intro.5 +DOC= 18.curses + +SRCS= Master MACROS= -me CLEANFILES+=win_st.gr twinkle1.gr twinkle2.gr life.gr intro.2.tbl appen.A.tbl -paper.${PRINTER}: ${SRCS} - ${ROFF} ${SRCS} > ${.TARGET} +.include + +${DOC}.${PRINTER}: ${SRCS} win_st.gr twinkle1.gr twinkle2.gr life.gr \ + intro.2.tbl appen.A.tbl + ( cd ${.CURDIR} ; ${ROFF} ${COMPAT} ${SRCS} ) > ${.TARGET} .SUFFIXES: .SUFFIXES: .c .gr @@ -22,16 +27,16 @@ paper.${PRINTER}: ${SRCS} # Don't re-run vgrind unless you want to patch the output files. VFONT= /usr/libexec/vfontedpr .c.gr: - ${VFONT} $*.c | grep -v "^'wh" > $*.gr + ( cd ${.CURDIR} ; ${VFONT} $*.c ) | grep -v "^'wh" > $*.gr + +.VPATH: ${CURDIR}/obj intro.5: doc.I doc.II doc.III doc.IV appen.B: win_st.gr appen.C: twinkle1.gr life.gr twinkle2.gr intro.2.tbl: intro.2 - ${TBL} intro.2 > intro.2.tbl + ( cd ${.CURDIR} ; ${TBL} intro.2 ) > intro.2.tbl appen.A.tbl: appen.A - ${TBL} appen.A > appen.A.tbl - -.include + ( cd ${.CURDIR} ; ${TBL} appen.A ) > appen.A.tbl diff --git a/share/doc/ps1/18.curses/appen.B b/share/doc/ps1/18.curses/appen.B index df0b687c2c..8db25a5034 100644 --- a/share/doc/ps1/18.curses/appen.B +++ b/share/doc/ps1/18.curses/appen.B @@ -47,7 +47,7 @@ The WINDOW structure is defined as follows: .so win_st.gr .)l .pp -.Vn \*_cury \\* +.Vn \*_cury \** .(f \** All variables not normally accessed directly by the user diff --git a/share/doc/ps1/18.curses/appen.C b/share/doc/ps1/18.curses/appen.C index 45408f43d1..07a35f4829 100644 --- a/share/doc/ps1/18.curses/appen.C +++ b/share/doc/ps1/18.curses/appen.C @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)appen.C 6.3 (Berkeley) 4/17/91 +.\" from: @(#)appen.C 6.3 (Berkeley) 4/17/91 +.\" appen.C,v 1.2 1993/08/01 18:23:16 mycroft Exp .\" .ie t .oh '\*(Ln Appendix C''PS1:18-%' .eh 'PS1:18-%''\*(Ln Appendix C' diff --git a/share/doc/ps1/18.curses/doc.III b/share/doc/ps1/18.curses/doc.III index c2daa5a31c..530f9f9437 100644 --- a/share/doc/ps1/18.curses/doc.III +++ b/share/doc/ps1/18.curses/doc.III @@ -114,7 +114,7 @@ and .Fd winch win \*m WINDOW *win; .De -Returns the character at the current \*(y +Returns the character at the current \*y on the given window. This does not make any changes to the window. .Ds diff --git a/share/doc/ps1/18.curses/intro.0 b/share/doc/ps1/18.curses/intro.0 index 5aebba029d..be5785f01a 100644 --- a/share/doc/ps1/18.curses/intro.0 +++ b/share/doc/ps1/18.curses/intro.0 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)intro.0 6.3 (Berkeley) 4/17/91 +.\" from: @(#)intro.0 6.3 (Berkeley) 4/17/91 +.\" intro.0,v 1.2 1993/08/01 07:37:57 mycroft Exp .\" .tp .(l C diff --git a/share/doc/ps1/18.curses/intro.1 b/share/doc/ps1/18.curses/intro.1 index 474795099e..1ba8f259b7 100644 --- a/share/doc/ps1/18.curses/intro.1 +++ b/share/doc/ps1/18.curses/intro.1 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)intro.1 6.3 (Berkeley) 4/17/91 +.\" from: @(#)intro.1 6.3 (Berkeley) 4/17/91 +.\" intro.1,v 1.2 1993/08/01 07:37:56 mycroft Exp .\" .bp .sh 1 Overview diff --git a/share/doc/ps1/18.curses/intro.2 b/share/doc/ps1/18.curses/intro.2 index 939b3df379..919a7e462f 100644 --- a/share/doc/ps1/18.curses/intro.2 +++ b/share/doc/ps1/18.curses/intro.2 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)intro.2 6.3 (Berkeley) 4/17/91 +.\" from: @(#)intro.2 6.3 (Berkeley) 4/17/91 +.\" intro.2,v 1.2 1993/08/01 07:37:55 mycroft Exp .\" .sh 1 Variables .pp diff --git a/share/doc/ps1/18.curses/intro.3 b/share/doc/ps1/18.curses/intro.3 index 82c9beed9b..35b875a92b 100644 --- a/share/doc/ps1/18.curses/intro.3 +++ b/share/doc/ps1/18.curses/intro.3 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)intro.3 6.3 (Berkeley) 4/17/91 +.\" from: @(#)intro.3 6.3 (Berkeley) 4/17/91 +.\" intro.3,v 1.2 1993/08/01 07:37:54 mycroft Exp .\" .sh 1 Usage .pp diff --git a/share/doc/ps1/18.curses/intro.4 b/share/doc/ps1/18.curses/intro.4 index fb379d0448..8b98663af6 100644 --- a/share/doc/ps1/18.curses/intro.4 +++ b/share/doc/ps1/18.curses/intro.4 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)intro.4 6.3 (Berkeley) 4/17/91 +.\" from: @(#)intro.4 6.3 (Berkeley) 4/17/91 +.\" intro.4,v 1.2 1993/08/01 07:37:53 mycroft Exp .\" .sh 1 "Cursor Motion Optimization: Standing Alone" .pp diff --git a/share/doc/ps1/18.curses/intro.5 b/share/doc/ps1/18.curses/intro.5 index 2c589514ae..2d487a0a53 100644 --- a/share/doc/ps1/18.curses/intro.5 +++ b/share/doc/ps1/18.curses/intro.5 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)intro.5 6.3 (Berkeley) 4/17/91 +.\" from: @(#)intro.5 6.3 (Berkeley) 4/17/91 +.\" intro.5,v 1.2 1993/08/01 07:37:51 mycroft Exp .\" .sh 1 "The Functions" .pp @@ -49,7 +50,7 @@ counterpart. The arguments are given to show the order and type of each. Their names are not mandatory, just suggestive. -.ta 11m,17m,25m,33m,41m,49m,57m,65m,73m +.ta 11m 17m 25m 33m 41m 49m 57m 65m 73m .sh 2 "Output Functions" .so doc.I .sh 2 "Input Functions" diff --git a/share/doc/ps1/18.curses/life.c b/share/doc/ps1/18.curses/life.c index f11522892b..8ae43e2c8c 100644 --- a/share/doc/ps1/18.curses/life.c +++ b/share/doc/ps1/18.curses/life.c @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)life.c 6.3 (Berkeley) 4/17/91 +.\" from: @(#)life.c 6.3 (Berkeley) 4/17/91 +.\" life.c,v 1.2 1993/08/01 18:23:17 mycroft Exp .\" # include # include diff --git a/share/doc/ps1/18.curses/macros b/share/doc/ps1/18.curses/macros index 2634451095..287d7e8dcc 100644 --- a/share/doc/ps1/18.curses/macros +++ b/share/doc/ps1/18.curses/macros @@ -44,7 +44,7 @@ .ds Nm This has no associated \*(lq\fBmv\fP\*(rq command. .ie t .ds m \fB\s-2\(dg\s+2\fP .el .ds m [*] -.hy WINDOW +.\".hy WINDOW .\".he ''\*(Ln'' .\".fo ''\- % \-'' .oh '\*(Ln''PS1:18-%' diff --git a/share/doc/ps1/18.curses/twinkle1.c b/share/doc/ps1/18.curses/twinkle1.c index ed35e51082..56118520bc 100644 --- a/share/doc/ps1/18.curses/twinkle1.c +++ b/share/doc/ps1/18.curses/twinkle1.c @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)twinkle1.c 6.3 (Berkeley) 4/17/91 +.\" from: @(#)twinkle1.c 6.3 (Berkeley) 4/17/91 +.\" twinkle1.c,v 1.2 1993/08/01 18:23:15 mycroft Exp .\" # include # include diff --git a/share/doc/ps1/18.curses/twinkle2.c b/share/doc/ps1/18.curses/twinkle2.c index fc1ca860ca..1ea8295894 100644 --- a/share/doc/ps1/18.curses/twinkle2.c +++ b/share/doc/ps1/18.curses/twinkle2.c @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)twinkle2.c 6.3 (Berkeley) 4/17/91 +.\" from: @(#)twinkle2.c 6.3 (Berkeley) 4/17/91 +.\" twinkle2.c,v 1.2 1993/08/01 18:23:14 mycroft Exp .\" extern int _putchar(); diff --git a/share/doc/ps1/18.curses/win_st.c b/share/doc/ps1/18.curses/win_st.c index ce5ab66c45..e12f366fb3 100644 --- a/share/doc/ps1/18.curses/win_st.c +++ b/share/doc/ps1/18.curses/win_st.c @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)win_st.c 6.3 (Berkeley) 4/17/91 +.\" from: @(#)win_st.c 6.3 (Berkeley) 4/17/91 +.\" win_st.c,v 1.2 1993/08/01 18:23:13 mycroft Exp .\" # define WINDOW struct _win_st diff --git a/share/doc/ps1/Makefile b/share/doc/ps1/Makefile index b6ccb78f6d..a251394782 100644 --- a/share/doc/ps1/Makefile +++ b/share/doc/ps1/Makefile @@ -1,15 +1,26 @@ -# @(#)Makefile 5.1.1.1 (Berkeley) 5/7/91 +# from: @(#)Makefile 5.1.1.1 (Berkeley) 5/7/91 +# Makefile,v 1.5 1993/07/31 15:33:20 mycroft Exp -# Missing: 00.contents 01.Clang 02.f77 03.f77io 05.as 09.lint 10.adb 11.dbx +# Missing: 01.Clang 02.f77 03.f77io 05.as 09.lint 10.adb 11.dbx # 12.make 15.yacc 16.lex 17.m4 +# +# Broken: 04.pascal 07.ipctut BINDIR= /usr/share/doc/ps1 -FILES= Makefile Title -SUBDIR= 04.pascal 06.sysman 07.ipctut \ +FILES= 00.contents Makefile Title +SUBDIR= 06.sysman \ 08.ipc 13.rcs 14.sccs \ 18.curses beforeinstall: + @if [ ! -d ${DESTDIR}${BINDIR} ]; then \ + /bin/rm -f ${DESTDIR}${BINDIR} ; \ + mkdir -p ${DESTDIR}${BINDIR} ; \ + chown root.wheel ${DESTDIR}${BINDIR} ; \ + chmod 755 ${DESTDIR}${BINDIR} ; \ + else \ + true ; \ + fi install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${FILES} \ ${DESTDIR}${BINDIR} diff --git a/share/doc/smm/01.setup/Makefile b/share/doc/smm/01.setup/Makefile index e6927bccaf..7dc41c92a7 100644 --- a/share/doc/smm/01.setup/Makefile +++ b/share/doc/smm/01.setup/Makefile @@ -1,13 +1,10 @@ -# @(#)Makefile 5.2 (Berkeley) 6/30/90 +# from: @(#)Makefile 5.2 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:33:01 mycroft Exp -DIR= smm/01.setup -SRCS= 0.t 1.t 2.t 3.t 4.t 5.t 6.t a.t b.t .if ${MACHINE} == "vax" -SRCS+= c.t +SUBDIR= vax +.elif ${MACHINE} == "tahoe" +SUBDIR= tahoe .endif -.PATH: ${.CURDIR}/${MACHINE} ${.CURDIR}/common -paper.${PRINTER}: ${SRCS} - ${EQN} ${.ALLSRC} | ${TBL} | ${ROFF} > ${.TARGET} - -.include +.include diff --git a/share/doc/smm/01.setup/common/4.t b/share/doc/smm/01.setup/common/4.t index e8a6bc55b2..334a882d2d 100644 --- a/share/doc/smm/01.setup/common/4.t +++ b/share/doc/smm/01.setup/common/4.t @@ -845,11 +845,11 @@ so that it will be checked and mounted when the system is bootstrapped. .PP As an example, consider a system with \*(Dn's. On the first \*(Dn, \*(Dk0, we will put the root file system in \*(Dk0a, and the /usr -file system in \*(Dk0\*(pa, which has enough space to hold it and then some. +file system in \*(Dk0\*(Pa, which has enough space to hold it and then some. The /tmp directory will be part of the root file system, as no file system will be mounted on /tmp. If we had only one \*(Dn, we would put user files -in the \*(Dk0\*(pa partition with the system source and binaries. +in the \*(Dk0\*(Pa partition with the system source and binaries. .PP If we had a second \*(Dn, we would place \fI/usr\fP in \*(Dk1\*(Pa. We would put user files in \*(Dk0g, calling the file system /a. diff --git a/share/doc/smm/01.setup/common/renohints.t b/share/doc/smm/01.setup/common/renohints.t index c747d7e41c..e805d7890a 100644 --- a/share/doc/smm/01.setup/common/renohints.t +++ b/share/doc/smm/01.setup/common/renohints.t @@ -503,7 +503,7 @@ including the standard \*(Bs rules in its Makefile makes the .Pn obj links in the current directory and recursively in the normal subdirectories.) We have one -.PN /usr/obj +.Pn /usr/obj hierarchy on the local system, and another on each system that shares the source filesystem. .NH 2 @@ -596,8 +596,8 @@ is used to create a job-control session consisting of a single process group with one member, the caller, which becomes a session leader. Only a session leader may acquire a controlling terminal. This is done explicitly via a -.Sm TIOCSCTTY -.FN ioctl +.SM TIOCSCTTY +.Fn ioctl call, not implicitly by an .Fn open call. @@ -727,7 +727,7 @@ structures also require modification; a definition of an .I osockaddr structure is provided for this purpose. Finally, programs that use the -.Sm SIOCGIFCONF +.SM SIOCGIFCONF ioctl to get a complete list of interface addresses need to check the .I sa_len diff --git a/share/doc/smm/01.setup/tahoe/1.t b/share/doc/smm/01.setup/tahoe/1.t index bf3db47c1c..5db9464739 100644 --- a/share/doc/smm/01.setup/tahoe/1.t +++ b/share/doc/smm/01.setup/tahoe/1.t @@ -161,7 +161,7 @@ The Power 6/32 (and most related machines being shipped) use a \*(Vs for all I/O peripherals. The console processor used for bootstrap and diagnostic purposes is also located on the \*(Vs. -The Harris HCX-9 uses a \*(Vm instead of a \(*Vs; however, the architecture +The Harris HCX-9 uses a \*(Vm instead of a \*(Vs; however, the architecture is completely analogous, and the following discussion applies with the exception of the name of the bus and the name of the disk controller. The device naming diff --git a/share/doc/smm/01.setup/tahoe/Makefile b/share/doc/smm/01.setup/tahoe/Makefile index 8cebdfb6b5..9ecdc69c32 100644 --- a/share/doc/smm/01.setup/tahoe/Makefile +++ b/share/doc/smm/01.setup/tahoe/Makefile @@ -1,24 +1,18 @@ -# @(#)Makefile 1.3 (Berkeley) 5/7/91 +# from: @(#)Makefile 1.3 (Berkeley) 5/7/91 +# Makefile,v 1.2 1993/07/31 15:33:14 mycroft Exp -PRINTER=-Plz -TROFF= ditroff ${PRINTER} -EQN= deqn ${PRINTER} -TBL= dtbl ${PRINTER} -LPR= lpr ${PRINTER} +DIR= smm/01.setup/tahoe +DOC= 01.setup +RSRC= ../common/renohints.t CSRC= ../common/3.t ../common/4.t ../common/5.t ../common/6.t SRC= 0.t 1.t 2.t ${CSRC} a.t b.t +MACROS= -ms -setup.out: ${SRC} - ${EQN} ${SRC} | ${TBL} | ${TROFF} -t -ms ${PAGES} > setup.out +.include -setup: setup.out - ${LPR} setup.out +${DOC}.${PRINTER}: ${SRC} ${DOC}.renohints.${PRINTER} + ( cd ${.CURDIR} ; ${EQN} ${SRC} ) | ${TBL} | ${ROFF} > ${.TARGET} -clean: - rm -f setup.out ?.t.spell errs Errs - -spell: ${SRC} - @for i in ${SRC}; do \ - echo $$i; spell $$i | sort | comm -23 - spell.ok > $$i.spell; \ - done +${DOC}.renohints.${PRINTER}: ${RSRC} + ( cd ${.CURDIR} ; ${TBL} ${RSRC} ) | ${ROFF} > ${.TARGET} diff --git a/share/doc/smm/01.setup/vax/Makefile b/share/doc/smm/01.setup/vax/Makefile index 3983dd401e..272dc56921 100644 --- a/share/doc/smm/01.setup/vax/Makefile +++ b/share/doc/smm/01.setup/vax/Makefile @@ -1,30 +1,22 @@ -# @(#)Makefile 6.3 (Berkeley) 5/7/91 +# from: @(#)Makefile 6.3 (Berkeley) 5/7/91 +# Makefile,v 1.2 1993/07/31 15:33:08 mycroft Exp -PRINTER=-Plz -TROFF= ditroff ${PRINTER} -EQN= deqn ${PRINTER} -TBL= dtbl ${PRINTER} -LPR= lpr -n ${PRINTER} +DIR= smm/01.setup/vax +DOC= 01.setup +RSRC= ../common/renohints.t CSRC= ../common/3.t ../common/4.t ../common/5.t ../common/6.t -SRC= 0.t 1.t 2.t ${CSRC} a.t b.t +SRC= 0.t 1.t 2.t ${CSRC} a.t b.t c.t +MACROS= -ms -setup.out: ${SRC} - ${EQN} ${SRC} | ${TBL} | ${TROFF} -t -ms > setup.out +.include -setup: setup.out - ${LPR} setup.out +${DOC}.${PRINTER}: ${SRC} ${DOC}.vaxhints.${PRINTER} \ + ${DOC}.renohints.${PRINTER} + ( cd ${.CURDIR} ; ${EQN} ${SRC} ) | ${TBL} | ${ROFF} > ${.TARGET} -vaxhints.out: vaxhints.t - ${TROFF} -t -ms vaxhints.t > vaxhints.out +${DOC}.vaxhints.${PRINTER}: vaxhints.t + ( cd ${.CURDIR} ; ${ROFF} vaxhints.t ) > ${.TARGET} -vaxhints: vaxhints.out - ${LPR} vaxhints.out - -clean: - rm -f setup.out vaxhints.out ?.t.spell errs Errs - -spell: ${SRC} - @for i in ${SRC}; do \ - echo $$i; spell $$i | sort | comm -23 - spell.ok > $$i.spell; \ - done +${DOC}.renohints.${PRINTER}: ${RSRC} + ( cd ${.CURDIR} ; ${TBL} ${RSRC} ) | ${ROFF} > ${.TARGET} diff --git a/share/doc/smm/02.config/6.t b/share/doc/smm/02.config/6.t index b391411da5..ac667bf097 100644 --- a/share/doc/smm/02.config/6.t +++ b/share/doc/smm/02.config/6.t @@ -143,7 +143,7 @@ The is one of .B standard or -.BR optional . +.B optional . Files marked as standard are included in all system configurations. Optional file specifications include a list of one or more system options that together require the inclusion of this module. diff --git a/share/doc/smm/02.config/Makefile b/share/doc/smm/02.config/Makefile index 0a7d1f7edd..59b81650be 100644 --- a/share/doc/smm/02.config/Makefile +++ b/share/doc/smm/02.config/Makefile @@ -1,10 +1,13 @@ -# @(#)Makefile 6.3 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.3 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:32:49 mycroft Exp DIR= smm/02.config -SRCS= 0.t 1.t 2.t 3.t 4.t 5.t 6.t a.t b.t c.t d.t e.t -MACROS= -msU +DOC= 02.config -paper.${PRINTER}: ${SRCS} - ${TBL} ${SRCS} | ${ROFF} > ${.TARGET} +SRCS= 0.t 1.t 2.t 3.t 4.t 5.t 6.t a.t b.t c.t d.t e.t +MACROS= -ms .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${TBL} ${SRCS} ) | ${ROFF} > ${.TARGET} diff --git a/share/doc/smm/04.quotas/Makefile b/share/doc/smm/04.quotas/Makefile index f79bd30e4d..c832258d3e 100644 --- a/share/doc/smm/04.quotas/Makefile +++ b/share/doc/smm/04.quotas/Makefile @@ -1,10 +1,13 @@ -# @(#)Makefile 6.2 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.2 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:32:41 mycroft Exp DIR= smm/04.quotas -SRCS= quotas.ms -MACROS= -msU +DOC= 04.quotas -paper.${PRINTER}: ${SRCS} - ${ROFF} ${SRCS} > ${.TARGET} +SRCS= quotas.ms +MACROS= -ms .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${ROFF} ${SRCS} ) > ${.TARGET} diff --git a/share/doc/smm/05.fsck/Makefile b/share/doc/smm/05.fsck/Makefile index 59a5f06296..a08844477d 100644 --- a/share/doc/smm/05.fsck/Makefile +++ b/share/doc/smm/05.fsck/Makefile @@ -1,10 +1,13 @@ -# @(#)Makefile 4.3 (Berkeley) 6/30/90 +# from: @(#)Makefile 4.3 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:32:32 mycroft Exp DIR= smm/05.fsck +DOC= 05.fsck + SRCS= 0.t 1.t 2.t 3.t 4.t TROFF= ditroff -paper.${PRINTER}: ${SRCS} - ${ROFF} ${SRCS} > ${.TARGET} - .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${ROFF} ${SRCS} ) > ${.TARGET} diff --git a/share/doc/smm/06.lpd/Makefile b/share/doc/smm/06.lpd/Makefile index fa132254c5..7408218482 100644 --- a/share/doc/smm/06.lpd/Makefile +++ b/share/doc/smm/06.lpd/Makefile @@ -1,10 +1,13 @@ -# @(#)Makefile 6.3 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.3 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:32:23 mycroft Exp DIR= smm/06.lpd -SRCS= 0.t 1.t 2.t 3.t 4.t 5.t 6.t 7.t -MACROS= -msU +DOC= 06.lpd -paper.${PRINTER}: ${SRCS} - ${TBL} ${SRCS} | ${ROFF} > ${.TARGET} +SRCS= 0.t 1.t 2.t 3.t 4.t 5.t 6.t 7.t +MACROS= -ms .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${TBL} ${SRCS} ) | ${ROFF} > ${.TARGET} diff --git a/share/doc/smm/07.sendmailop/Makefile b/share/doc/smm/07.sendmailop/Makefile index 23974261b7..c1a5bd6e73 100644 --- a/share/doc/smm/07.sendmailop/Makefile +++ b/share/doc/smm/07.sendmailop/Makefile @@ -1,10 +1,14 @@ -# @(#)Makefile 5.2 (Berkeley) 6/30/90 +# from: @(#)Makefile 5.2 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:32:13 mycroft Exp DIR= smm/07.sendmailop +DOC= 07.sendmailop + SRCS= op.me MACROS= -me -paper.${PRINTER}: ${SRCS} - ${PIC} ${SRCS} | ${EQN} | ${ROFF} > ${.TARGET} - .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${PIC} ${SRCS} ) | ${EQN} | \ + ${ROFF} ${COMPAT} > ${.TARGET} diff --git a/share/doc/smm/08.timedop/Makefile b/share/doc/smm/08.timedop/Makefile index bf5341803f..a6ec7131df 100644 --- a/share/doc/smm/08.timedop/Makefile +++ b/share/doc/smm/08.timedop/Makefile @@ -1,10 +1,13 @@ -# @(#)Makefile 6.3 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.3 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:32:03 mycroft Exp DIR= smm/08.timedop -SRCS= timed.ms -MACROS= -msU +DOC= 08.timedop -paper.${PRINTER}: ${SRCS} - ${ROFF} ${SRCS} > ${.TARGET} +SRCS= timed.ms +MACROS= -ms .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${ROFF} ${SRCS} ) > ${.TARGET} diff --git a/share/doc/smm/10.newsop/Makefile b/share/doc/smm/10.newsop/Makefile index a4b19d65c2..04cd3b602a 100644 --- a/share/doc/smm/10.newsop/Makefile +++ b/share/doc/smm/10.newsop/Makefile @@ -1,9 +1,16 @@ -# @(#)Makefile 5.2 (Berkeley) 6/30/90 +# from: @(#)Makefile 5.2 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:31:54 mycroft Exp DIR= smm/10.newsop -SRCS= tmac.n install.mn +DOC= 10.newsop -paper.${PRINTER}: ${SRCS} - ${ROFF} ${SRCS} > ${.TARGET} +SSRC= tmac.n standard.mn +SRCS= tmac.n install.mn .include + +${DOC}.${PRINTER}: ${SRCS} ${DOC}.standard.${PRINTER} + ( cd ${.CURDIR} ; ${ROFF} ${SRCS} ) > ${.TARGET} + +${DOC}.standard.${PRINTER}: ${SSRC} + ( cd ${.CURDIR} ; ${ROFF} ${SSRC} ) > ${.TARGET} diff --git a/share/doc/smm/11.named/Makefile b/share/doc/smm/11.named/Makefile index 80d67bbacf..ef461a5629 100644 --- a/share/doc/smm/11.named/Makefile +++ b/share/doc/smm/11.named/Makefile @@ -1,13 +1,15 @@ -# @(#)Makefile 6.4 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.4 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:31:45 mycroft Exp DIR= smm/11.named +DOC= 11.named + SRCS= 00macs.me 00title.me intro.me build.me types.me setup.me \ files.me named.boot.primary named.boot.secondary named.boot.cache \ resolv.conf root.cache named.local ucbhosts ucbhosts.rev manage.me MACROS= -me -NROFF= nroff -rb3 - -paper.${PRINTER}: ${SRCS} - ${TBL} ${SRCS} | ${ROFF} > ${.TARGET} .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${TBL} ${SRCS} ) | ${ROFF} ${COMPAT} > ${.TARGET} diff --git a/share/doc/smm/14.fastfs/Makefile b/share/doc/smm/14.fastfs/Makefile index bdb2342bd6..ba3c93ddca 100644 --- a/share/doc/smm/14.fastfs/Makefile +++ b/share/doc/smm/14.fastfs/Makefile @@ -1,10 +1,13 @@ -# @(#)Makefile 5.4 (Berkeley) 6/30/90 +# from: @(#)Makefile 5.4 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:31:35 mycroft Exp DIR= smm/14.fastfs -SRCS= 0.t 1.t 2.t 3.t 4.t 5.t 6.t -MACROS= -msU +DOC= 14.fastfs -paper.${PRINTER}: ${SRCS} - ${TBL} ${SRCS} | ${EQN} | ${TROFF} > ${.TARGET} +SRCS= 0.t 1.t 2.t 3.t 4.t 5.t 6.t +MACROS= -ms .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${TBL} ${SRCS} ) | ${EQN} | ${ROFF} > ${.TARGET} diff --git a/share/doc/smm/15.net/7.t b/share/doc/smm/15.net/7.t index e0af0699ec..a7b6757662 100644 --- a/share/doc/smm/15.net/7.t +++ b/share/doc/smm/15.net/7.t @@ -226,7 +226,7 @@ The protocol module is supplied two sockets and requested to establish a connection between the two without binding any addresses, if possible. This call is used in implementing the -.IR socketpair (2) +.I socketpair (2) system call. .PP The following requests are used internally by the protocol modules diff --git a/share/doc/smm/15.net/Makefile b/share/doc/smm/15.net/Makefile index 99c74690bd..af996f4d0b 100644 --- a/share/doc/smm/15.net/Makefile +++ b/share/doc/smm/15.net/Makefile @@ -1,10 +1,13 @@ -# @(#)Makefile 6.3 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.3 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:31:26 mycroft Exp DIR= smm/15.net -SRCS= 0.t 1.t 2.t 3.t 4.t 5.t 6.t 7.t 8.t 9.t a.t b.t c.t d.t e.t f.t -MACROS= -msU +DOC= 15.net -paper.${PRINTER}: ${SRCS} - ${TBL} ${SRCS} | ${ROFF} > ${.TARGET} +SRCS= 0.t 1.t 2.t 3.t 4.t 5.t 6.t 7.t 8.t 9.t a.t b.t c.t d.t e.t f.t +MACROS= -ms .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${TBL} ${SRCS} ) | ${ROFF} > ${.TARGET} diff --git a/share/doc/smm/15.net/a.t b/share/doc/smm/15.net/a.t index baf4ee1b42..481d6183c8 100644 --- a/share/doc/smm/15.net/a.t +++ b/share/doc/smm/15.net/a.t @@ -154,7 +154,7 @@ by means of their \fIpr_ctlinput\fP entries. Statistics are kept by the routing table routines on the use of routing redirect messages and their affect on the routing tables. These statistics may be viewed using -.IR netstat (1). +.I netstat (1). .PP Status information other than routing redirect control messages may be used in the future, but at present they are ignored. diff --git a/share/doc/smm/16.sendmail/Makefile b/share/doc/smm/16.sendmail/Makefile index 0f41b660de..a50c4a679c 100644 --- a/share/doc/smm/16.sendmail/Makefile +++ b/share/doc/smm/16.sendmail/Makefile @@ -1,10 +1,13 @@ -# @(#)Makefile 5.3 (Berkeley) 6/30/90 +# from: @(#)Makefile 5.3 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:31:17 mycroft Exp DIR= smm/16.sendmail +DOC= 16.sendmail + SRCS= intro.me MACROS= -me -paper.${PRINTER}: ${SRCS} - ${PIC} ${SRCS} | ${ROFF} > ${.TARGET} - .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${PIC} ${SRCS} ) | ${ROFF} ${COMPAT} > ${.TARGET} diff --git a/share/doc/smm/20.termdesc/Makefile b/share/doc/smm/20.termdesc/Makefile index 788def19aa..f0f41d5b16 100644 --- a/share/doc/smm/20.termdesc/Makefile +++ b/share/doc/smm/20.termdesc/Makefile @@ -1,10 +1,13 @@ -# @(#)Makefile 6.2 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.2 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:31:08 mycroft Exp DIR= smm/20.termdesc +DOC= 20.termdesc + SRCS= termdesc.me MACROS= -me -paper.${PRINTER}: ${SRCS} - ${ROFF} ${SRCS} > ${.TARGET} - .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${ROFF} ${COMPAT} ${SRCS} ) > ${.TARGET} diff --git a/share/doc/smm/22.timed/Makefile b/share/doc/smm/22.timed/Makefile index eba4955db6..6c127d1855 100644 --- a/share/doc/smm/22.timed/Makefile +++ b/share/doc/smm/22.timed/Makefile @@ -1,10 +1,13 @@ -# @(#)Makefile 6.3 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.3 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:30:59 mycroft Exp DIR= smm/22.timed -SRCS= timed.ms -MACROS= -msU +DOC= 22.timed -paper.${PRINTER}: ${SRCS} - ${SOELIM} ${SRCS} | ${TBL} | ${ROFF} > ${.TARGET} +SRCS= timed.ms +MACROS= -ms .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${SOELIM} ${SRCS} ) | ${TBL} | ${ROFF} > ${.TARGET} diff --git a/share/doc/smm/Makefile b/share/doc/smm/Makefile index 2523b1d162..9cd9988372 100644 --- a/share/doc/smm/Makefile +++ b/share/doc/smm/Makefile @@ -1,4 +1,5 @@ -# @(#)Makefile 5.2.1.2 (Berkeley) 5/7/91 +# from: @(#)Makefile 5.2.1.2 (Berkeley) 5/7/91 +# Makefile,v 1.3 1993/07/31 15:32:55 mycroft Exp # Missing: 03.kdebug 09.uucpimpl 12.uchanges 13.kchanges 17.security # 18.password 19.porttour 21.uucpnet @@ -16,6 +17,14 @@ SUBDIR+=01.setup .endif beforeinstall: + @if [ ! -d ${DESTDIR}${BINDIR} ]; then \ + /bin/rm -f ${DESTDIR}${BINDIR} ; \ + mkdir -p ${DESTDIR}${BINDIR} ; \ + chown root.wheel ${DESTDIR}${BINDIR} ; \ + chmod 755 ${DESTDIR}${BINDIR} ; \ + else \ + true ; \ + fi install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${FILES} \ ${DESTDIR}${BINDIR} diff --git a/share/doc/usd/04.csh/Makefile b/share/doc/usd/04.csh/Makefile index c34af0515e..c1ad7261eb 100644 --- a/share/doc/usd/04.csh/Makefile +++ b/share/doc/usd/04.csh/Makefile @@ -1,9 +1,12 @@ -# @(#)Makefile 5.1 (Berkeley) 6/30/90 +# from: @(#)Makefile 5.1 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:30:49 mycroft Exp DIR= usd/04.csh -SRCS= tabs csh.1 csh.2 csh.3 csh.4 csh.a csh.g +DOC= 04.csh -paper.${PRINTER}: ${SRCS} - ${ROFF} > ${.TARGET} +SRCS= tabs csh.1 csh.2 csh.3 csh.4 csh.a csh.g .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${ROFF} ${SRCS} ) > ${.TARGET} diff --git a/share/doc/usd/04.csh/csh.1 b/share/doc/usd/04.csh/csh.1 index 68be1583c5..edf13462ed 100644 --- a/share/doc/usd/04.csh/csh.1 +++ b/share/doc/usd/04.csh/csh.1 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)csh.1 6.2 (Berkeley) 4/17/91 +.\" from: @(#)csh.1 6.2 (Berkeley) 4/17/91 +.\" csh.1,v 1.2 1993/08/01 07:37:46 mycroft Exp .\" .EH 'USD:4-%''An Introduction to the C shell' .OH 'An Introduction to the C shell''USD:4-%' diff --git a/share/doc/usd/04.csh/csh.2 b/share/doc/usd/04.csh/csh.2 index 3b543f94bc..c0edb2be48 100644 --- a/share/doc/usd/04.csh/csh.2 +++ b/share/doc/usd/04.csh/csh.2 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)csh.2 6.2 (Berkeley) 4/17/91 +.\" from: @(#)csh.2 6.2 (Berkeley) 4/17/91 +.\" csh.2,v 1.2 1993/08/01 07:37:45 mycroft Exp .\" .nr H1 1 .NH diff --git a/share/doc/usd/04.csh/csh.3 b/share/doc/usd/04.csh/csh.3 index 8b2cf8b5bf..d9f5d0a157 100644 --- a/share/doc/usd/04.csh/csh.3 +++ b/share/doc/usd/04.csh/csh.3 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)csh.3 6.2 (Berkeley) 4/17/91 +.\" from: @(#)csh.3 6.2 (Berkeley) 4/17/91 +.\" csh.3,v 1.2 1993/08/01 07:37:43 mycroft Exp .\" .nr H1 2 .NH diff --git a/share/doc/usd/04.csh/csh.4 b/share/doc/usd/04.csh/csh.4 index ce08807011..83fbecda83 100644 --- a/share/doc/usd/04.csh/csh.4 +++ b/share/doc/usd/04.csh/csh.4 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)csh.4 6.2 (Berkeley) 4/17/91 +.\" from: @(#)csh.4 6.2 (Berkeley) 4/17/91 +.\" csh.4,v 1.2 1993/08/01 07:37:42 mycroft Exp .\" .nr H1 3 .NH diff --git a/share/doc/usd/04.csh/csh.a b/share/doc/usd/04.csh/csh.a new file mode 100644 index 0000000000..8482b6d074 --- /dev/null +++ b/share/doc/usd/04.csh/csh.a @@ -0,0 +1,95 @@ +.\" Copyright (c) 1980 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)csh.a 6.2 (Berkeley) 4/17/91 +.\" +.SH +Appendix \- Special characters +.LP +The following table lists the special characters of +.I csh +and the \s-2UNIX\s0 system, giving for each the section(s) in which it +is discussed. +A number of these characters also have special meaning in expressions. +See the +.I csh +manual section +for a complete list. +.ta .75i 1.5i 2.25i +.LP +Syntactic metacharacters +.DS +; 2.4 separates commands to be executed sequentially +| 1.5 separates commands in a pipeline +( ) 2.2,3.6 brackets expressions and variable values +& 2.5 follows commands to be executed without waiting for completion +.DE +.LP +Filename metacharacters +.DS +/ 1.6 separates components of a file's pathname +\. 1.6 separates root parts of a file name from extensions +? 1.6 expansion character matching any single character +* 1.6 expansion character matching any sequence of characters +[ ] 1.6 expansion sequence matching any single character from a set +~ 1.6 used at the beginning of a filename to indicate home directories +{ } 4.2 used to specify groups of arguments with common parts +.DE +.LP +Quotation metacharacters +.DS +\e 1.7 prevents meta-meaning of following single character +\' 1.7 prevents meta-meaning of a group of characters +" 4.3 like \', but allows variable and command expansion +.DE +.LP +Input/output metacharacters +.DS +< 1.5 indicates redirected input +> 1.3 indicates redirected output +.DE +.LP +Expansion/substitution metacharacters +.DS +$ 3.4 indicates variable substitution +! 2.3 indicates history substitution +: 3.6 precedes substitution modifiers +^ 2.3 used in special forms of history substitution +\` 4.3 indicates command substitution +.DE +.LP +Other metacharacters +.DS +# 1.3,3.6 begins scratch file names; indicates shell comments +\- 1.2 prefixes option (flag) arguments to commands +% 2.6 prefixes job name specifications +.DE +.bp diff --git a/share/doc/usd/07.Mail/Makefile b/share/doc/usd/07.Mail/Makefile index 4c2c8ccca8..84b79b019d 100644 --- a/share/doc/usd/07.Mail/Makefile +++ b/share/doc/usd/07.Mail/Makefile @@ -1,11 +1,14 @@ -# @(#)Makefile 6.2 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.2 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:30:41 mycroft Exp DIR= usd/07.Mail +DOC= 07.Mail + SRCS= mail0.nr mail1.nr mail2.nr mail3.nr mail4.nr mail5.nr mail6.nr \ mail7.nr mail8.nr mail9.nr maila.nr MACROS= -me -paper.${PRINTER}: ${SRCS} - ${TBL} ${SRCS} | ${ROFF} > ${.TARGET} - .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${TBL} ${SRCS} ) | ${ROFF} ${COMPAT} > ${.TARGET} diff --git a/share/doc/usd/09.newsread/Makefile b/share/doc/usd/09.newsread/Makefile index 677f3e5110..b860a67fea 100644 --- a/share/doc/usd/09.newsread/Makefile +++ b/share/doc/usd/09.newsread/Makefile @@ -1,9 +1,16 @@ -# @(#)Makefile 6.2 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.2 (Berkeley) 6/30/90 +# Makefile,v 1.5 1993/07/31 15:30:35 mycroft Exp DIR= usd/09.newsread -SRCS= tmac.n howto.mn +DOC= 09.newsread -paper.${PRINTER}: ${SRCS} - ${ROFF} ${SRCS} > ${.TARGET} +CSRC= ../../smm/10.newsop/tmac.n copyright.mn +SRCS= ../../smm/10.newsop/tmac.n howto.mn .include + +${DOC}.${PRINTER}: ${SRCS} ${DOC}.copyright.${PRINTER} + ( cd ${.CURDIR} ; ${ROFF} ${SRCS} ) > ${.TARGET} + +${DOC}.copyright.${PRINTER}: ${CSRC} + ( cd ${.CURDIR} ; ${ROFF} ${CSRC} ) > ${.TARGET} diff --git a/share/doc/usd/10.etiq/Makefile b/share/doc/usd/10.etiq/Makefile index 68941c61fb..028cb1c43a 100644 --- a/share/doc/usd/10.etiq/Makefile +++ b/share/doc/usd/10.etiq/Makefile @@ -1,9 +1,12 @@ -# @(#)Makefile 6.2 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.2 (Berkeley) 6/30/90 +# Makefile,v 1.5 1993/07/31 15:30:28 mycroft Exp DIR= usd/10.etiq -SRCS= tmac.n manner.mn +DOC= 10.etiq -paper.${PRINTER}: ${SRCS} - ${ROFF} > ${.TARGET} +SRCS= ../../smm/10.newsop/tmac.n manner.mn .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${ROFF} ${SRCS} ) > ${.TARGET} diff --git a/share/doc/usd/11.notes/0.long b/share/doc/usd/11.notes/0.long index 7eccc2b8a8..b027402c19 100644 --- a/share/doc/usd/11.notes/0.long +++ b/share/doc/usd/11.notes/0.long @@ -35,7 +35,7 @@ .sp |1.5i .ce Report No. UIUCDCS-R-82-1081 -.ce999 +.ce 999 .sp |3.75i .ft B diff --git a/share/doc/usd/11.notes/2.1 b/share/doc/usd/11.notes/2.1 index 28dce53392..92e59fac3f 100644 --- a/share/doc/usd/11.notes/2.1 +++ b/share/doc/usd/11.notes/2.1 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)2.1 6.2 (Berkeley) 4/17/91 +.\" from: @(#)2.1 6.2 (Berkeley) 4/17/91 +.\" 2.1,v 1.2 1993/08/01 07:37:36 mycroft Exp .\" .ls 1 .ch "Using Notesfiles" diff --git a/share/doc/usd/11.notes/2.2 b/share/doc/usd/11.notes/2.2 index a769c754d7..a5082f5f3d 100644 --- a/share/doc/usd/11.notes/2.2 +++ b/share/doc/usd/11.notes/2.2 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)2.2 6.2 (Berkeley) 4/17/91 +.\" from: @(#)2.2 6.2 (Berkeley) 4/17/91 +.\" 2.2,v 1.2 1993/08/01 07:37:34 mycroft Exp .\" .ls 1 .se "Other Commands" diff --git a/share/doc/usd/11.notes/3.1 b/share/doc/usd/11.notes/3.1 index d06a8976ca..f3c889c16b 100644 --- a/share/doc/usd/11.notes/3.1 +++ b/share/doc/usd/11.notes/3.1 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)3.1 6.2 (Berkeley) 4/17/91 +.\" from: @(#)3.1 6.2 (Berkeley) 4/17/91 +.\" 3.1,v 1.2 1993/08/01 07:37:33 mycroft Exp .\" .ls 1 .ch "Managing Notesfiles" diff --git a/share/doc/usd/11.notes/3.2 b/share/doc/usd/11.notes/3.2 index 0eecee851f..4bc612c6ab 100644 --- a/share/doc/usd/11.notes/3.2 +++ b/share/doc/usd/11.notes/3.2 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)3.2 6.2 (Berkeley) 4/17/91 +.\" from: @(#)3.2 6.2 (Berkeley) 4/17/91 +.\" 3.2,v 1.2 1993/08/01 07:37:32 mycroft Exp .\" .ls 1 .se "Initial Installation & Parameters" diff --git a/share/doc/usd/11.notes/4.0 b/share/doc/usd/11.notes/4.0 index 1edca940c8..551b054ed6 100644 --- a/share/doc/usd/11.notes/4.0 +++ b/share/doc/usd/11.notes/4.0 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)4.0 6.2 (Berkeley) 4/17/91 +.\" from: @(#)4.0 6.2 (Berkeley) 4/17/91 +.\" 4.0,v 1.2 1993/08/01 07:37:30 mycroft Exp .\" .ls 1 .ch "Other Notesfile Utilities" diff --git a/share/doc/usd/11.notes/4.1 b/share/doc/usd/11.notes/4.1 index ad8ad7e730..f0afdec38a 100644 --- a/share/doc/usd/11.notes/4.1 +++ b/share/doc/usd/11.notes/4.1 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)4.1 6.2 (Berkeley) 4/17/91 +.\" from: @(#)4.1 6.2 (Berkeley) 4/17/91 +.\" 4.1,v 1.2 1993/08/01 07:37:30 mycroft Exp .\" .se "Hard Copy Output" diff --git a/share/doc/usd/11.notes/4.2 b/share/doc/usd/11.notes/4.2 index 7d22e552d8..fc2519b29a 100644 --- a/share/doc/usd/11.notes/4.2 +++ b/share/doc/usd/11.notes/4.2 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)4.2 6.2 (Berkeley) 4/17/91 +.\" from: @(#)4.2 6.2 (Berkeley) 4/17/91 +.\" 4.2,v 1.2 1993/08/01 07:37:29 mycroft Exp .\" .se "Piped Insertion of Notes" diff --git a/share/doc/usd/11.notes/4.3 b/share/doc/usd/11.notes/4.3 index e726e00769..5df631c38b 100644 --- a/share/doc/usd/11.notes/4.3 +++ b/share/doc/usd/11.notes/4.3 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)4.3 6.2 (Berkeley) 4/17/91 +.\" from: @(#)4.3 6.2 (Berkeley) 4/17/91 +.\" 4.3,v 1.2 1993/08/01 07:37:28 mycroft Exp .\" .se "User Subroutines" diff --git a/share/doc/usd/11.notes/4.4 b/share/doc/usd/11.notes/4.4 index d52aed0054..cdb5735865 100644 --- a/share/doc/usd/11.notes/4.4 +++ b/share/doc/usd/11.notes/4.4 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)4.4 6.2 (Berkeley) 4/17/91 +.\" from: @(#)4.4 6.2 (Berkeley) 4/17/91 +.\" 4.4,v 1.2 1993/08/01 07:37:27 mycroft Exp .\" .se "Statistics" diff --git a/share/doc/usd/11.notes/4.5 b/share/doc/usd/11.notes/4.5 index 8d6a89da91..a63db5774d 100644 --- a/share/doc/usd/11.notes/4.5 +++ b/share/doc/usd/11.notes/4.5 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)4.5 6.2 (Berkeley) 4/17/91 +.\" from: @(#)4.5 6.2 (Berkeley) 4/17/91 +.\" 4.5,v 1.2 1993/08/01 07:37:26 mycroft Exp .\" .se "Checking for New Notes" diff --git a/share/doc/usd/11.notes/4.6 b/share/doc/usd/11.notes/4.6 index 9783bb65c8..41a38b86d8 100644 --- a/share/doc/usd/11.notes/4.6 +++ b/share/doc/usd/11.notes/4.6 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)4.6 6.2 (Berkeley) 4/17/91 +.\" from: @(#)4.6 6.2 (Berkeley) 4/17/91 +.\" 4.6,v 1.2 1993/08/01 07:37:25 mycroft Exp .\" .se "Mail to Notesfiles" diff --git a/share/doc/usd/11.notes/4.7 b/share/doc/usd/11.notes/4.7 index 8fdfbba620..df24e7ecc0 100644 --- a/share/doc/usd/11.notes/4.7 +++ b/share/doc/usd/11.notes/4.7 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)4.7 6.2 (Berkeley) 4/17/91 +.\" from: @(#)4.7 6.2 (Berkeley) 4/17/91 +.\" 4.7,v 1.2 1993/08/01 07:37:23 mycroft Exp .\" .se "Modifying Access Rights for Many Notesfiles" diff --git a/share/doc/usd/11.notes/Makefile b/share/doc/usd/11.notes/Makefile index 10ecd7532c..ac00ef3cb9 100644 --- a/share/doc/usd/11.notes/Makefile +++ b/share/doc/usd/11.notes/Makefile @@ -1,4 +1,5 @@ -# @(#)Makefile 6.1 (Berkeley) 5/26/86 +# from: @(#)Makefile 6.1 (Berkeley) 5/26/86 +# Makefile,v 1.2 1993/07/31 15:30:23 mycroft Exp # # Makefile for the notesfile reference manual # March 29, 1982 @@ -13,76 +14,15 @@ # This copy of everything has been modified to run with troff # and a typesetter. Hopefully the only changes to make it # run again under nroff will be in page length/width statements. -# -# - -DISTDIR = p:/usr/src/uiuc/notesfiles -DCP= rcp -NROPTS = -TROPTS = -PRINTER = -Pdp -# ditroff actually prints the mother -TROFF = ditroff -NROFF = nroff -NOUT = qprx -FILES= 0.long 0.short \ - 1 2.1 2.2 3.1 3.2 \ - 4.0 4.1 4.2 4.3 4.4 4.5 4.6 4.7 \ - 5 \ - a.checklist b.interface c.changes -LONGFILES= 0.long \ - 1 2.1 2.2 3.1 3.2 4.0 4.1 4.2 4.3 4.4 4.5 4.6 4.7 \ - 5 a.checklist b.interface c.changes -SHORTFILES= 0.short \ - 1 2.1 2.2 4.0 4.1 4.2 4.3 4.4 4.5 -OTHERS = macros vtoc Makefile acks - -man: shortman -all: longman shortman - -longman: $(LONGFILES) macros vtoc acks - $(TROFF) $(TROPTS) $(PRINTER) macros $(LONGFILES) vtoc acks - -shortman: $(SHORTFILES) macros vtoc - $(TROFF) $(TROPTS) $(PRINTER) macros $(SHORTFILES) vtoc - -longout: $(LONGFILES) macros vtoc acks - $(NROFF) $(NROPTS) macros $(LONGFILES) vtoc acks > longout - -shortout: $(SHORTFILES) macros vtoc - $(NROFF) $(NROPTS) macros $(SHORTFILES) vtoc > shortout -print: longout - $(NOUT) longout +DIR= usd/11.notes +DOC= 11.notes -printsh: shortout - $(NOUT) shortout - -help: - @echo "make longman/shortman for long/abridged user man via ditroff" - @echo "make longout/shortout for long/abridged user man via nroff" - @echo "make print prints long nroff output" - -# -# Maintainence. -# - -spell: $(FILES) - sp $? - touch spell - -diction: out - diction out > diction - -style: out - style out > style - -dist: $(FILES) $(OTHERS) - ${DCP} $? $(DISTDIR)/doc/. - touch dist +SRCS= macros 0.long \ + 1 2.1 2.2 3.1 3.2 4.0 4.1 4.2 4.3 4.4 4.5 4.6 4.7 \ + 5 a.checklist b.interface c.changes vtoc acks -clean: - rm -f man out Make.errs - rm -f diction style spell sp.* +.include -install: +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${ROFF} ${SRCS} ) > ${.TARGET} diff --git a/share/doc/usd/14.edit/Makefile b/share/doc/usd/14.edit/Makefile index 4257d3fea2..358e9fe9bf 100644 --- a/share/doc/usd/14.edit/Makefile +++ b/share/doc/usd/14.edit/Makefile @@ -1,11 +1,16 @@ -# @(#)Makefile 6.2 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.2 (Berkeley) 6/30/90 +# Makefile,v 1.5 1993/07/31 15:30:17 mycroft Exp DIR= usd/14.edit +DOC= 14.edit + SRCS= edittut.ms -MACROS= -msU +MACROS= -ms + +.include -paper.${PRINTER}: ${SRCS} - ${TBL} ${SRCS} | ${ROFF} > ${.TARGET} +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${TBL} ${SRCS} ) | ${ROFF} > ${.TARGET} # index for versatec is different from the one in edit.tut # because the fonts are different and entries reference page @@ -13,6 +18,4 @@ paper.${PRINTER}: ${SRCS} # you should just use the index in edit.tut, and ignore editvindex. editvindex: - ${TROFF} ${MACROS} -n22 edit.vindex - -.include + ( cd ${.CURDIR} ; ${ROFF} -n22 edit.vindex ) > ${DOC}.${.TARGET}.${PRINTER} diff --git a/share/doc/usd/14.edit/edittut.ms b/share/doc/usd/14.edit/edittut.ms index 3f35ccb12d..94858f67f1 100644 --- a/share/doc/usd/14.edit/edittut.ms +++ b/share/doc/usd/14.edit/edittut.ms @@ -89,19 +89,19 @@ September 1981 .nf Introduction\ \ \ 3 .sp -Session 1\ \ \4 +Session 1\ \ \ 4 .in +.5i Making contact with \s-2UNIX\s+2\ \ \ 4 -Logging in\ \ \4 +Logging in\ \ \ 4 Asking for \fIedit\fR\ \ \ 4 The ``Command not found'' message\ \ \ 5 -A summary\ \ \5 +A summary\ \ \ 5 Entering text\ \ \ 5 Messages from \fIedit\fR\ \ \ 5 Text input mode\ \ \ 6 Making corrections\ \ \ 6 Writing text to disk\ \ \ 7 -Signing off\ \ \7 +Signing off\ \ \ 7 .in -.5i .sp Session 2\ \ \ 8 diff --git a/share/doc/usd/15.vi/Makefile b/share/doc/usd/15.vi/Makefile index 75e2e04f9a..9ff48b80ed 100644 --- a/share/doc/usd/15.vi/Makefile +++ b/share/doc/usd/15.vi/Makefile @@ -1,17 +1,19 @@ -# @(#)Makefile 6.3 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.3 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:30:11 mycroft Exp DIR= usd/15.vi +DOC= 15.vi + SRCS= vi.in vi.chars -MACROS= -msU -CLEANFILES+=summary.* viapwh.* +MACROS= -ms -paper.${PRINTER}: ${SRCS} summary.${PRINTER} viapwh.${PRINTER} - ${TBL} ${SRCS} | ${ROFF} > ${.TARGET} +.include -summary.${PRINTER}: vi.summary - ${TBL} vi.summary | ${ROFF} > ${.TARGET} +${DOC}.${PRINTER}: ${SRCS} ${DOC}.summary.${PRINTER} ${DOC}.apwh.${PRINTER} + ( cd ${.CURDIR} ; ${TBL} ${SRCS} ) | ${ROFF} > ${.TARGET} -viapwh.${PRINTER}: vi.apwh.ms - ${ROFF} vi.viapwh > ${.TARGET} +${DOC}.summary.${PRINTER}: vi.summary + ( cd ${.CURDIR} ; ${TBL} vi.summary ) | ${ROFF} > ${.TARGET} -.include +${DOC}.apwh.${PRINTER}: vi.apwh.ms + ( cd ${.CURDIR} ; ${ROFF} vi.apwh.ms ) > ${.TARGET} diff --git a/share/doc/usd/15.vi/vi.chars b/share/doc/usd/15.vi/vi.chars index 85f2fab505..0180f80b33 100644 --- a/share/doc/usd/15.vi/vi.chars +++ b/share/doc/usd/15.vi/vi.chars @@ -32,7 +32,7 @@ .\" @(#)vi.chars 6.3 (Berkeley) 4/17/91 .\" .bd S 3 -..pn 21 +.pn 21 .de iP .IP "\fB\\$1\fR" \\$2 .. diff --git a/share/doc/usd/15.vi/vi.in b/share/doc/usd/15.vi/vi.in index 8b73ccc070..e18d0809c5 100644 --- a/share/doc/usd/15.vi/vi.in +++ b/share/doc/usd/15.vi/vi.in @@ -1487,7 +1487,7 @@ command from .I EXINIT ) with a command of the form: .DS -:map \fIlhs\fR \fIrhs\fR\s-2CR\f0 +:map \fIlhs\fR \fIrhs\fR\s-2CR\s0 .DE mapping .I lhs @@ -1810,7 +1810,7 @@ of the string. If you are using an operator such as \fBd\fR, \fBc\fR or \fBy\fR, then you may well wish to affect lines up to the line before the line containing the pattern. You can give a search of the form \fB/\fR\fIpat\fR\fB/\-\fR\fIn\fR to refer to the \fIn\fR'th line -before the next line containing \fIpat\fR, or you can use \fB\+\fR instead +before the next line containing \fIpat\fR, or you can use \fB+\fR instead of \fB\-\fR to refer to the lines after the one containing \fIpat\fR. If you don't give a line offset, then the editor will affect characters up to the match place, rather than whole lines; thus use ``+0'' to affect @@ -1836,7 +1836,7 @@ It is also necessary to use a \e before a The following table gives the extended forms when \fBmagic\fR is set. .DS .TS -bl l. +lb l. \(ua at beginning of pattern, matches beginning of line $ at end of pattern, matches end of line \fB\&.\fR matches any character diff --git a/share/doc/usd/15.vi/vi.summary b/share/doc/usd/15.vi/vi.summary index 51aefaa88f..02ccebd68a 100644 --- a/share/doc/usd/15.vi/vi.summary +++ b/share/doc/usd/15.vi/vi.summary @@ -434,7 +434,7 @@ c change < left shift > right shift ! filter through command -\&\= indent for \s-2LISP\s0 +\&= indent for \s-2LISP\s0 y yank lines to buffer .TE .h "Miscellaneous operations" diff --git a/share/doc/usd/16.ex/Makefile b/share/doc/usd/16.ex/Makefile index daf48a27c0..0e16269843 100644 --- a/share/doc/usd/16.ex/Makefile +++ b/share/doc/usd/16.ex/Makefile @@ -1,14 +1,16 @@ -# @(#)Makefile 6.2 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.2 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:30:05 mycroft Exp DIR= usd/16.ex +DOC= 16.ex + SRCS= ex.rm -MACROS= -msU -CLEANFILES=summary.* +MACROS= -ms -paper.${PRINTER}: ${SRCS} summary.${PRINTER} - ${ROFF} ${SRCS} > ${.TARGET} +.include -summary.${PRINTER}: ex.summary - ${TBL} ex.summary | ${ROFF} > ${.TARGET} +${DOC}.${PRINTER}: ${SRCS} ${DOC}.summary.${PRINTER} + ( cd ${.CURDIR} ; ${ROFF} ${SRCS} ) > ${.TARGET} -.include +${DOC}.summary.${PRINTER}: ex.summary + ( cd ${.CURDIR} ; ${TBL} ex.summary ) | ${ROFF} > ${.TARGET} diff --git a/share/doc/usd/21.msdiffs/Makefile b/share/doc/usd/21.msdiffs/Makefile index 1abdcc9883..1e0edbe686 100644 --- a/share/doc/usd/21.msdiffs/Makefile +++ b/share/doc/usd/21.msdiffs/Makefile @@ -1,10 +1,13 @@ -# @(#)Makefile 6.2 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.2 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:29:58 mycroft Exp DIR= usd/21.msdiffs -SRCS= ms.diffs -MACROS= -msU +DOC= 21.msdiffs -paper.${PRINTER}: ${SRCS} - ${ROFF} ${SRCS} > ${.TARGET} +SRCS= ms.diffs +MACROS= -ms .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${ROFF} ${SRCS} ) > ${.TARGET} diff --git a/share/doc/usd/22.memacros/Makefile b/share/doc/usd/22.memacros/Makefile index 23e8ab950d..94c9401e2d 100644 --- a/share/doc/usd/22.memacros/Makefile +++ b/share/doc/usd/22.memacros/Makefile @@ -1,10 +1,13 @@ -# @(#)Makefile 6.4 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.4 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:29:52 mycroft Exp DIR= usd/22.memacros +DOC= 22.memacros + SRCS= intro.me MACROS= -me -paper.${PRINTER}: ${SRCS} - ${ROFF} ${SRCS} > ${.TARGET} - .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${ROFF} ${COMPAT} ${SRCS} ) > ${.TARGET} diff --git a/share/doc/usd/23.meref/Makefile b/share/doc/usd/23.meref/Makefile index f597a7284a..72a3b03548 100644 --- a/share/doc/usd/23.meref/Makefile +++ b/share/doc/usd/23.meref/Makefile @@ -1,10 +1,13 @@ -# @(#)Makefile 6.4 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.4 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:29:46 mycroft Exp DIR= usd/23.meref +DOC= 23.meref + SRCS= ref.me MACROS= -me -paper.${PRINTER}: ${SRCS} - ${ROFF} ${SRCS} > ${.TARGET} - .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${ROFF} ${COMPAT} ${SRCS} ) > ${.TARGET} diff --git a/share/doc/usd/33.rogue/Makefile b/share/doc/usd/33.rogue/Makefile index 47dccb0c84..61951092d1 100644 --- a/share/doc/usd/33.rogue/Makefile +++ b/share/doc/usd/33.rogue/Makefile @@ -1,10 +1,13 @@ -# @(#)Makefile 6.3 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.3 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:29:41 mycroft Exp DIR= usd/33.rogue +DOC= 33.rogue + SRCS= rogue.me MACROS= -me -paper.${PRINTER}: ${SRCS} - ${TBL} ${SRCS} | ${ROFF} > ${.TARGET} - .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${TBL} ${SRCS} ) | ${ROFF} ${COMPAT} > ${.TARGET} diff --git a/share/doc/usd/34.trek/Makefile b/share/doc/usd/34.trek/Makefile index 58829b7cc1..48584e730d 100644 --- a/share/doc/usd/34.trek/Makefile +++ b/share/doc/usd/34.trek/Makefile @@ -1,10 +1,13 @@ -# @(#)Makefile 6.2 (Berkeley) 6/30/90 +# from: @(#)Makefile 6.2 (Berkeley) 6/30/90 +# Makefile,v 1.4 1993/07/31 15:29:34 mycroft Exp DIR= usd/34.trek +DOC= 34.trek + SRCS= trek.me MACROS= -me -paper.${PRINTER}: ${SRCS} - ${TBL} ${SRCS} | ${ROFF} > ${.TARGET} - .include + +${DOC}.${PRINTER}: ${SRCS} + ( cd ${.CURDIR} ; ${TBL} ${SRCS} ) | ${ROFF} ${COMPAT} > ${.TARGET} diff --git a/share/doc/usd/Makefile b/share/doc/usd/Makefile index 1097a247e1..dad7234f55 100644 --- a/share/doc/usd/Makefile +++ b/share/doc/usd/Makefile @@ -1,17 +1,27 @@ -# @(#)Makefile 5.1.1.1 (Berkeley) 5/7/91 +# from: @(#)Makefile 5.1.1.1 (Berkeley) 5/7/91 +# Makefile,v 1.4 1993/07/31 15:29:28 mycroft Exp #Missing: 01.begin 02.learn 03.shell 05.dc 06.bc 08.mh 12.edtut 13.edadv # 17.jove 18.sed 19.awk 20.msmacros 24.troff 25.trofftut 26.eqn # 27.eqnguide 28.tbl 29.refer 30.invert 31.bib 32.diction + BINDIR= /usr/share/doc/usd FILES= 00.contents Makefile Title SUBDIR= 04.csh 07.Mail \ - 09.newsread 10.etiq 14.edit 15.vi \ + 09.newsread 10.etiq 11.notes 14.edit 15.vi \ 16.ex 21.msdiffs 22.memacros \ 23.meref \ 33.rogue 34.trek beforeinstall: + @if [ ! -d ${DESTDIR}${BINDIR} ]; then \ + /bin/rm -f ${DESTDIR}${BINDIR} ; \ + mkdir -p ${DESTDIR}${BINDIR} ; \ + chown root.wheel ${DESTDIR}${BINDIR} ; \ + chmod 755 ${DESTDIR}${BINDIR} ; \ + else \ + true ; \ + fi install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${FILES} \ ${DESTDIR}${BINDIR} diff --git a/share/man/Makefile b/share/man/Makefile index ed2d0b9047..a5c8e472da 100644 --- a/share/man/Makefile +++ b/share/man/Makefile @@ -1,18 +1,14 @@ -# @(#)Makefile 5.9 (Berkeley) 7/1/91 +# from: @(#)Makefile 5.9 (Berkeley) 7/1/91 +# $Id: Makefile,v 1.6 1993/10/19 19:57:35 rgrimes Exp $ SUBDIR= man1 man3 man4 man5 man7 man8 afterinstall: - install -c -o ${BINOWN} -g ${BINGRP} -m 444 makewhatis.sed \ - ${DESTDIR}/usr/share/man/makewhatis.sed install -c -o ${BINOWN} -g ${BINGRP} -m 444 man0/COPYRIGHT \ ${DESTDIR}/usr/share/man/COPYRIGHT makedb: - for file in `find /usr/share/man -type f -name '*.0' -print`; do \ - sed -n -f /usr/share/man/makewhatis.sed $$file; \ - done | sort -u > whatis.db - install -o ${BINOWN} -g ${BINGRP} -m 444 whatis.db \ - ${DESTDIR}/usr/share/man + makewhatis ${DESTDIR}${BINDIR}/man .include +.include "${.CURDIR}/../Makefile.inc" diff --git a/share/man/makewhatis.sed b/share/man/makewhatis.sed deleted file mode 100644 index 263f757360..0000000000 --- a/share/man/makewhatis.sed +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/sh - -# -# Copyright (c) 1988 The Regents of the University of California. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)makewhatis.sed 5.5 (Berkeley) 4/17/91 -# - -/^[A-Z0-9]*([0-9])[ ].*[ ][A-Z0-9]*([0-9])$/ { - s;.*(\([a-zA-Z0-9]*\).*;\1; - h - d -} - -/^NNAAMMEE/!d - -:name - s;.*;; - N - s;\n;; - s;.;;g - /^[^ ]/b print - H - b name - -:print - x - s;\n;;g - /-/!d - s;\([a-z][A-z]\)-[ ][ ]*;\1; - s;\([a-zA-Z0-9,]\)[ ][ ]*;\1 ;g - s;[^a-zA-Z0-9]*\([a-zA-Z0-9]*\)[^a-zA-Z0-9]*\(.*\) - \(.*\);\2 (\1) - \3; - p - d - q diff --git a/share/man/man1/Makefile b/share/man/man1/Makefile index 7ce2c71103..f3866279aa 100644 --- a/share/man/man1/Makefile +++ b/share/man/man1/Makefile @@ -1,5 +1,5 @@ # @(#)Makefile 5.1 (Berkeley) 4/30/91 -MAN1= intro.0 +MAN1= intro.1 .include diff --git a/share/man/man3/Makefile b/share/man/man3/Makefile index 4ce7dbf3ad..9c8ed802a4 100644 --- a/share/man/man3/Makefile +++ b/share/man/man3/Makefile @@ -1,6 +1,10 @@ # @(#)Makefile 0.1 (RWGrimes) 3/25/93 -MAN3= assert.0 bitstring.0 end.0 intro.0 stdarg.0 +MAN3= assert.3 bitstring.3 end.3 fpgetround.3 intro.3 stdarg.3 + MLINKS+=end.3 edata.3 end.3 etext.3 stdarg.3 varargs.3 +MLINKS+=fpgetround.3 fpsetround.3 fpgetround.3 fpgetmask.3 \ + fpgetround.3 fpsetmask.3 fpgetround.3 fpgetsticky.3 \ + fpgetround.3 fpsetsticky.3 fpgetround.3 fpresetsticky.3 .include diff --git a/share/man/man3/fpgetround.3 b/share/man/man3/fpgetround.3 new file mode 100644 index 0000000000..30365afa35 --- /dev/null +++ b/share/man/man3/fpgetround.3 @@ -0,0 +1,138 @@ +.\" Copyright (c) 1993 Andrew Moore, Talke Studio +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)fpgetround.3 1.0 (Berkeley) 9/23/93 +.\" +.Dd August 23, 1993 +.Dt FPGETROUND 3 +.Os +.Sh NAME +.Nm fpgetround , +.Nm fpsetround , +.Nm fpgetmask , +.Nm fpsetmask , +.Nm fpgetsticky , +.Nm fpresetsticky +.Nd IEEE floating point interface +.Sh SYNOPSIS +.Fd #include +.Ft typedef enum { +.br +.Ft FP_RN, +.Li /* round to nearest */ +.br +.Ft FP_RM, +.Li /* round to minus infinity */ +.br +.Ft FP_RP, +.Li /* round to plus inifinity */ +.br +.Ft FP_RZ, +.Li /* truncate */ +.br +.Ft } fp_rnd; +.Pp +.Ft fp_rnd +.Fn fpgetround "" +.Ft fp_rnd +.Fn fpsetround "fp_rnd direction" +.Fd #define fp_except int +.Fd #define FP_X_INV 0x01 /* invalid */ +.Fd #define FP_X_OFL 0x08 /* overflow */ +.Fd #define FP_X_UFL 0x10 /* underflow */ +.Fd #define FP_X_DZ 0x04 /* divide-by-zero */ +.Fd #define FP_X_IMP 0x20 /* loss of precision */ +.Fd #define FP_X_DNML 0x02 /* denormal */ +.Ft fp_except +.Fn fpgetmask "" +.Ft fp_except +.Fn fpsetmask "fp_except mask" +.Ft fp_except +.Fn fpgetsticky "" +.Ft fp_except +.Fn fpresetsticky "fp_except sticky" +.Sh DESCRIPTION +When a floating point exception is detected, the exception sticky flag is +set and the exception mask is tested. If the mask is set, then a trap +occurs. These routines allow both setting the floating point exception +masks, and resetting the exception sticky flags after an exception is +detected. In addition, they allow setting the floating point rounding mode. +.Pp +The +.Fn fpgetround +function +returns the current floating point rounding mode. +.Pp +The +.Fn fpsetround +function +sets the floating point rounding mode and returns +the previous mode. +.Pp +The +.Fn fpgetmask +function +returns the current floating point exception masks. +.Pp +The +.Fn fpsetmask +function +sets the floating point exception masks and returns the +previous masks. +.Pp +The +.Fn fpgetsticky +function +returns the current floating point sticky flags. +.Pp +The +.Fn fpresetsticky +function +clears the floating point sticky flags and returns +the previous flags. +.Pp +Sample code which prevents a trap on divide-by-zero: +.Bd -literal -offset indent +fpsetmask(~FP_X_DZ); +a = 1.0; +b = 0; +c = a / b; +fpresetsticky(FP_X_DZ); +fpsetmask(FP_X_DZ); +.Ed +.Sh SEE ALSO +.Xr isnan 3 +.Sh CAVEAT +After a floating point exception and before a mask is set, the sticky +flags must be reset. If another exception occurs before the sticky +flags are reset, then a wrong exception type may be signaled. +.Sh HISTORY +These routines are based on SysV/386 routines of the same name. diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 1e85eb41ee..73bd6da828 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -2,9 +2,11 @@ # # Not for 386bsd: man4.hp300 man4.tahoe man4.vax -MAN4= bpf.0 clnp.0 cltp.0 drum.0 esis.0 fd.0 icmp.0 idp.0 imp.0 \ - inet.0 ip.0 iso.0 kadb.0 lo.0 netintro.0 ns.0 nsip.0 null.0 \ - pty.0 route.0 spp.0 tcp.0 tp.0 udp.0 unix.0 +MAN4= bpf.4 clnp.4 cltp.4 drum.4 esis.4 fd.4 icmp.4 idp.4 imp.4 inet.4 ip.4 \ + iso.4 kadb.4 lo.4 netintro.4 ns.4 nsip.4 null.4 pty.4 route.4 \ + sd.4 spp.4 st.4 \ + tcp.4 termios.4 tp.4 udp.4 unix.4 + MLINKS= netintro.4 networking.4 SUBDIR= man4.i386 diff --git a/share/man/man4/ddb.4 b/share/man/man4/ddb.4 new file mode 100644 index 0000000000..c2ff64f606 --- /dev/null +++ b/share/man/man4/ddb.4 @@ -0,0 +1,401 @@ +.\" +.\" Mach Operating System +.\" Copyright (c) 1991,1990 Carnegie Mellon University +.\" All Rights Reserved. +.\" +.\" Permission to use, copy, modify and distribute this software and its +.\" documentation is hereby granted, provided that both the copyright +.\" notice and this permission notice appear in all copies of the +.\" software, derivative works or modified versions, and any portions +.\" thereof, and that both notices appear in supporting documentation. +.\" +.\" CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" +.\" CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR +.\" ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. +.\" +.\" Carnegie Mellon requests users of this software to return to +.\" +.\" Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU +.\" School of Computer Science +.\" Carnegie Mellon University +.\" Pittsburgh PA 15213-3890 +.\" +.\" any improvements or extensions that they make and grant Carnegie Mellon +.\" the rights to redistribute these changes. +.\" +.\" +.\" HISTORY +.\" ddb.4,v +# Revision 1.1 1993/07/15 18:41:02 brezak +# Man page for DDB +# +.\" Revision 2.6 92/04/08 08:52:57 rpd +.\" Changes from OSF. +.\" [92/01/17 14:19:22 jsb] +.\" Changes for OSF debugger modifications. +.\" [91/12/12 tak] +.\" +.\" Revision 2.5 91/06/25 13:50:22 rpd +.\" Added some watchpoint explanation. +.\" [91/06/25 rpd] +.\" +.\" Revision 2.4 91/06/17 15:47:31 jsb +.\" Added documentation for continue/c, match, search, and watchpoints. +.\" I've not actually explained what a watchpoint is; maybe Rich can +.\" do that (hint, hint). +.\" [91/06/17 10:58:08 jsb] +.\" +.\" Revision 2.3 91/05/14 17:04:23 mrt +.\" Correcting copyright +.\" +.\" Revision 2.2 91/02/14 14:10:06 mrt +.\" Changed to new Mach copyright +.\" [91/02/12 18:10:12 mrt] +.\" +.\" Revision 2.2 90/08/30 14:23:15 dbg +.\" Created. +.\" [90/08/30 dbg] +.\" +.TH ddb 4 +.SH NAME +ddb \- kernel debugger +.de XX +.sp +.ti -4n +\\$1 +.br +.sp +.. +.de XS +.nr )R +\\$1 +.. +.de XE +.nr )R -\\$1 +.. +.SH DESCRIPTION +.br +.sp +The kernel debugger has most of the features of the old kdb, +but with a more rational (gdb-like) syntax. +.sp +The current location is called 'dot'. The 'dot' is displayed with +a hexadecimal format at a prompt. +Examine and write commands update 'dot' to the address of the last line +examined or the last location modified, and set 'next' to the address of +the next location to be examined or changed. +Other commands don't change 'dot', and set 'next' to be the same as 'dot'. +.sp +The general command syntax is: +.sp +.ti +4n +\fIcommand[/modifier] address [,count]\fR +.sp +A blank line repeats from the address 'next' with count 1 and no modifiers. +Specifying 'address' sets 'dot' to the address. +Omitting 'address' uses 'dot'. +A missing 'count' is taken to be 1 for printing commands or infinity +for stack traces. +.sp +"\fBddb\fR" has a feature like a command "\fBmore\fR" +for the output. If an output line exceeds the number set in the $lines +variable, it displays "\fI--db_more--\fR" +and waits for a response. +The valid responses for it are: +.XS 4n +.IP \fI\\fR 10n +one more page +.IP \fI\\fR 10n +one more line +.IP \fB\q\fR 10n +abort the current command, and return to the command input mode. +.LP +.sp +.XE 4n +.LP +.B COMMANDS +.sp +.XS 4n +.LP +.XX "\fBexamine(x) \fI[/] [,]\fR" +Display the addressed locations according to the formats in the modifier. +Multiple modifier formats display multiple locations. +If no format is specified, the last formats specified for this command +is used. +.br +The format characters are +.sp +.LP +.XS 2n +.IP b 5n +look at by bytes(8 bits) +.IP h 5n +look at by half words(16 bits) +.IP l 5n +look at by long words(32 bits) +.IP a 5n +print the location being displayed +.IP A 5n +print the location with a line number if possible +.IP x 5n +display in unsigned hex +.IP z 5n +display in signed hex +.IP o 5n +display in unsigned octal +.IP d 5n +display in signed decimal +.IP u 5n +display in unsigned decimal +.IP r 5n +display in current radix, signed +.IP c 5n +display low 8 bits as a character. +Non-printing characters are displayed as an octal escape code (e.g. '\\000'). +.IP s 5n +display the null-terminated string at the location. +Non-printing characters are displayed as octal escapes. +.IP m 5n +display in unsigned hex with character dump at the end of each line. +The location is also displayed in hex at the beginning of each line. +.IP i 5n +display as an instruction +.IP I 5n +display as an instruction with possible alternate formats depending on the +machine: +.XE 2n +.LP +.XS 5n +.LP +.IP vax 6n +don't assume that each external label is a procedure entry mask +.IP i386 6n +don't round to the next long word boundary +.IP mips 6n +print register contents +.LP +.XE 5n +.LP +.XX xf +Examine forward. +It executes an examine command with the last specified parameters to it +except that the next address displayed by it is used as the start address. +.XX xb +Examine backward. +It executes an examine command with the last specified parameters to it +except that the last start address subtracted by the size displayed by it +is used as the start address. +.XX "\fBprint[/axzodurc] \fI [ ... ]\fR" +Print 'addr's according to the modifier character. +Valid formats are: a x z o d u r c. +If no modifier is specified, the last one specified to it is used. 'addr' +can be a string, and it is printed as it is. For example, +.br +.sp +.ti +4n +print/x "eax = " $eax "\\necx = " $ecx "\\n" +.br +.sp +will print like +.sp +.in +4n +eax = xxxxxx +.br +ecx = yyyyyy +.in -4n +.sp +.br +.XX "\fBwrite[/bhl] \fI [ ... ]\fR" +Write the expressions at succeeding locations. +The write unit size can be specified in the modifier with a letter +b (byte), h (half word) or l(long word) respectively. If omitted, +long word is assumed. +.br +Warning: since there is no delimiter between expressions, strange +things may happen. +It's best to enclose each expression in parentheses. +.XX "\fBset \fI$ [=] \fR" +Set the named variable or register with the value of 'expr'. +Valid variable names are described below. +.XX "\fBbreak[/u] \fI[,]\fR" +Set a break point at 'addr'. +If count is supplied, continues (count-1) times before stopping at the +break point. If the break point is set, a break point number is +printed with '#'. This number can be used in deleting the break point +or adding conditions to it. +.LP +.XS 2n +.IP u 5n +Set a break point in user space address. Without 'u' option, +the address is considered in the kernel space, and wrong space address +is rejected with an error message. +This option can be used only if it is supported by machine dependent +routines. +.LP +.XE 2n +Warning: if a user text is shadowed by a normal user space debugger, +user space break points may not work correctly. Setting a break +point at the low-level code paths may also cause strange behavior. +.XX "\fBdelete \fI|#\fR" +Delete the break point. The target break point can be specified by a +break point number with '#', or by 'addr' like specified in 'break' +command. +.XX "\fBstep[/p] \fI[,]\fR" +Single step 'count' times. +If 'p' option is specified, print each instruction at each step. +Otherwise, only print the last instruction. +.br +.sp +Warning: depending on machine type, it may not be possible to +single-step through some low-level code paths or user space code. +On machines with software-emulated single-stepping (e.g., pmax), +stepping through code executed by interrupt handlers will probably +do the wrong thing. +.XX "\fBcontinue[/c]\fR" +Continue execution until a breakpoint or watchpoint. +If /c, count instructions while executing. +Some machines (e.g., pmax) also count loads and stores. +.br +.sp +Warning: when counting, the debugger is really silently single-stepping. +This means that single-stepping on low-level code may cause strange +behavior. +.XX "\fBuntil[/p]\fR" +Stop at the next call or return instruction. +If 'p' option is specified, print the call nesting depth and the +cumulative instruction count at each call or return. Otherwise, +only print when the matching return is hit. +.XX "\fBnext[/p]\fR" +Stop at the matching return instruction. +If 'p' option is specified, print the call nesting depth and the +cumulative instruction count at each call or return. Otherwise, +only print when the matching return is hit. +.XX "\fBmatch[/p]\fR" +A synonym for 'next'. +.XX "\fBtrace[/u] \fI[ ][,]\fR" +Stack trace. 'u' option traces user space; if omitted, only traces +kernel space. 'count' is the number of frames to be traced. +If the 'count' is omitted, all frames are printed. +.br +.sp +Warning: User space stack trace is valid +only if the machine dependent code supports it. +.XX "\fBsearch[/bhl] \fI [] [,]\fR" +Search memory for a value. This command might fail in interesting +ways if it doesn't find the searched-for value. This is because +ddb doesn't always recover from touching bad memory. The optional +count argument limits the search. +.XX "\fBshow all procs[/m]\fR" +Display all process information. +This version of "\fBddb\fR" +prints more information than previous one. +It shows UNIX process information like "ps". +The UNIX process information may not be shown if it is not +supported in the machine, or the bottom of the stack of the +target process is not in the main memory at that time. +The 'm' options will alter the 'ps' display to show vm_map +addresses for the process and not show other info. +.br +.XX "\fBps[/m]\fR" +A synonym for 'show all procs'. +.XX "\fBshow registers\fR" +Display the register set. +If 'u' option is specified, it displays user registers instead of +kernel or currently saved one. +.br +.sp +Warning: The support of 'u' option depends on the machine. If +not supported, incorrect information will be displayed. +.XX "\fBshow map[/f] \fI\fR" +Prints the vm_map at 'addr'. If the 'f' option is specified the +complete map is printed. +.XX "\fBshow object[/f] \fI\fR" +Prints the vm_object at 'addr'. If the 'f' option is specified the +complete object is printed. +.XX "\fBshow watches\fR" +Displays all watchpoints. +.XX "\fBwatch \fI,\fR" +Set a watchpoint for a region. Execution stops +when an attempt to modify the region occurs. +The 'size' argument defaults to 4. +.br +If you specify a wrong space address, the request is rejected +with an error message. +.br +.sp +Warning: Attempts to watch wired kernel memory +may cause unrecoverable error in some systems such as i386. +Watchpoints on user addresses work best. +.br +.LP +.XE 4n +.LP +.sp +.B VARIABLES +.sp +The debugger accesses registers and variables as +.I $. +Register names are as in the "\fBshow registers\fR" +command. +Some variables are suffixed with numbers, and may have some modifier +following a colon immediately after the variable name. +For example, register variables can have 'u' modifier to indicate +user register (e.g. $eax:u). +.br +.sp +Built-in variables currently supported are: +.sp +.IP radix 12n +Input and output radix +.IP maxoff 12n +Addresses are printed as 'symbol'+offset unless offset is greater than maxoff. +.IP maxwidth 12n +The width of the displayed line. +.IP lines 12n +The number of lines. It is used by "more" feature. +.IP tabstops 12n +Tab stop width. +.IP work\fIxx\fR +Work variable. +.I 'xx' +can be 0 to 31. +.LP +.LP +.sp +.B EXPRESSIONS +.sp +Almost all expression operators in C are supported except '~', '^', +and unary '&'. +Special rules in "\fBddb\fR" +are: +.br +.IP "" 15n +name of a symbol. It is translated to the address(or value) of it. '.' +and ':' can be used in the identifier. If supported by an object format +dependent routine, +[\fI\fR:]\fI\fR[:\fI\fR] +[\fI\fR:]\fI\fR, and +\fI\fR[:\fI\fR] +can be accepted as a symbol. +The symbol may be prefixed with '\fI\fR::' +like 'emulator::mach_msg_trap' to specify other than kernel symbols. +.IP "" 15n +radix is determined by the first two letters: +0x: hex, 0o: octal, 0t: decimal, otherwise, follow current radix. +.IP \. 15n +\'dot' +.IP \+ 15n +\'next' +.IP \.. 15n +address of the start of the last line examined. +Unlike 'dot' or 'next', this is only changed by "examine" or +"write" command. +.IP \' 15n +last address explicitly specified. +.IP "$" 15n +register name or variable. It is translated to the value of it. +It may be followed by a ':' and modifiers as described above. +.IP \# 15n +a binary operator which rounds up the left hand side to the next +multiple of right hand side. +.IP "*" 15n +indirection. It may be followed by a ':' and modifiers as described above. diff --git a/share/man/man4/man4.i386/Makefile b/share/man/man4/man4.i386/Makefile index b675c4b994..a4a6059992 100644 --- a/share/man/man4/man4.i386/Makefile +++ b/share/man/man4/man4.i386/Makefile @@ -1,7 +1,19 @@ # @(#)Makefile 0.1 (RWGrimes) 3/25/93 -MAN4= mem.0 spkr.0 -MLINKS= mem.4 kmem.4 +MAN4= com.4 keyboard.4 lpa.4 lpt.4 mem.4 mse.4 npx.4 screen.4 sio.4 spkr.4 + +MLINKS= com.4 ../com.4 +MLINKS+= keyboard.4 ../keyboard.4 +MLINKS+= lpa.4 ../lpa.4 +MLINKS+= lpt.4 ../lpt.4 +MLINKS+= mem.4 ../mem.4 +MLINKS+= mem.4 ../kmem.4 +MLINKS+= mse.4 ../mse.4 +MLINKS+= npx.4 ../npx.4 +MLINKS+= screen.4 ../screen.4 +MLINKS+= sio.4 ../sio.4 +MLINKS+= spkr.4 ../spkr.4 + MANSUBDIR=/i386 .include diff --git a/share/man/man4/man4.i386/com.4 b/share/man/man4/man4.i386/com.4 new file mode 100644 index 0000000000..4e45bab861 --- /dev/null +++ b/share/man/man4/man4.i386/com.4 @@ -0,0 +1,102 @@ +.\" Copyright (c) 1990, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Systems Programming Group of the University of Utah Computer +.\" Science Department. +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)dca.4 5.2 (Berkeley) 3/27/91 +.\" from: com.4,v 1.1 1993/08/06 11:19:07 cgd Exp +.\" $Id$ +.\" +.Dd August 28, 1993 +.Dt COM 4 i386 +.Os FreeBSD +.Sh NAME +.Nm com +.Nd +serial communications interface +.Sh SYNOPSIS +.Cd "device com0 at isa? port" \&"IO_COM1\&" tty irq 4 vector comintr +.Cd "device com1 at isa? port" \&"IO_COM2\&" tty irq 3 vector comintr +.Cd "device com2 at isa? port" \&"IO_COM3\&" tty irq 5 vector comintr +.Cd "device com3 at isa? port" \&"IO_COM4\&" tty irq 9 vector comintr +.Sh DESCRIPTION +The +.Nm com +driver provides support for NS8250-, NS16450-, and NS16550-based +.Tn EIA +.Tn RS-232C +.Pf ( Tn CCITT +.Tn V.28 ) +communications interfaces. The NS8250 and NS16450 have single character +buffers, and the NS16550 has a 16 character buffer. +.Pp +Input and output for each line may set to one of following baud rates; +50, 75, 110, 134.5, 150, 300, 600, 1200, 1800, 2400, 4800, 9600, +19200, 38400, 57600, or 115200. +.Sh FILES +.Bl -tag -width Pa +.It Pa /dev/tty00 +.It Pa /dev/tty01 +.El +.Sh DIAGNOSTICS +.Bl -diag +.It com%d: silo overflow. +The single-character input +.Dq silo +has overflowed and incoming data has been lost. +.\".It com%d: weird interrupt: %x. +.\"The device has generated an unexpected interrupt +.\"with the code listed. +.El +.Sh SEE ALSO +.Xr tty 4 +.Xr sio 4 +.Sh HISTORY +The +.Nm +driver is derived from the +.Nm HP9000/300 +.Nm dca +driver and is +.Ud +.Sh BUGS +Data loss is possible on busy systems with baud rates greater than 300. +The +.Nm com +driver is slow, buggy, and not properly tested. +.Pp +The name of this driver and the constants which define the locations +of the various seiral ports are holdovers from +.Nm DOS . +.Pp +As usual, you get what you pay for; cheap NS16550 clones +generally don't work. diff --git a/share/man/man4/man4.i386/keyboard.4 b/share/man/man4/man4.i386/keyboard.4 new file mode 100644 index 0000000000..0a8b0d5141 --- /dev/null +++ b/share/man/man4/man4.i386/keyboard.4 @@ -0,0 +1,134 @@ +.Dd April 7, 1993 +.Dt KEYBOARD 4 +.Os FreeBSD +.Sh NAME +.Nm keyboard +.Nd pc keyboard interface +.Sh DESCRIPTION + +The PC keyboard is use as the console character input device. The keyboard +is owned by the current virtual console. +To switch between the virtual consoles use the sequence +.Ar ALT+Fn +, which means hold down ALT and press one of the function keys. The +virtual console with the same number as the function key is then +selected as the current virtual console, and given exclusive use of +the keyboard and display. + +The console allows entering values that are not physically +present on the keyboard via a special keysequence. +To use this facility press and hold down ALT, +then enter a decimal number from 0-255 via the numerical keypad, then +release ALT. The entered value is then used as the ASCII value for one +character. This way it is possible to enter any ASCII value. +The keyboard is configurable to suit the individual user and the different +national layout. + +The keys on the keyboard can have any of the following functions: + + Normal key - Enter the ASCII value associated with the key. + + Function key - Enter a string of ASCII values. + + Switch Key - Switch virtual console. + + Modifier Key - Change the meaning of another key. + + +The keyboard is seen as a number of keys numbered from 1 to n. This +number is often referred to as the "scancode" for a given key. The number +of the key is transmitted as an 8 bit char with bit 7 as 0 when a key is +pressed, and the number with bit 7 as 1 when released. This makes it +possible to make the mapping of the keys fully configurable. + +The meaning of every key is programmable via the PIO_KEYMAP ioctl call, that +takes a structure keymap_t as argument. The layout of this structure is as +follows: +.Pp +.Bd -literal -offset indent + struct keymap { + u_short n_keys; + struct key_t { + u_char map[NUM_STATES]; + u_char spcl; + u_char flgs; + } key[NUM_KEYS]; + }; +.Ed +.Pp +The field n_keys tells the system how many keydefinitions (scancodes) +follows. Each scancode is then specified in the key_t substructure. + +Each scancode can be translated to any of 8 different values, depending +on the shift, control, and alt state. These eight possibilities is +represented by the map array, as shown below: + + alt + scan cntrl alt alt cntrl + code base shift cntrl shift alt shift cntrl shift + map[n] 0 1 2 3 4 5 6 7 + ---- ------------------------------------------------------ + 0x1E 'a' 'A' 0x01 0x01 'a' 'A' 0x01 0x01 + +This is the default mapping for the key labelled 'A' wich normally has +scancode 0x1E. The eight states is as shown, giving the 'A' key its +normal behavior. +The spcl field is used to give the key "special" treatment, and is +interpreted as follows. +Each bit correspond to one of the states above. If the bit is 0 the +key emits the number defined in the corresponding map[] entry. +If the bit is 1 the key is "special". This means it does not emit +anything, instead it changes the "state". That means it is a shift, +control, alt, lock, switch-screen, function-key or no-op key. +The bitmap is backwards ie. 7 for base, 6 for shift etc. + +The flgs field defines if the key should react on caps-lock (1), +num-lock (2), both (3) or ignore both (0). + +The mapkbd utility is used to load such a description into/outof +the kernel at runtime. This make it possible to change the key +assignments at runtime, or more important to get (GIO_KEYMAP ioctl) +the exact key meanings from the kernel (fx. used by the X server). + +The function keys can be programmed using the PIO_STRMAP ioctl call. + +This ioctl takes a argument of the type fkeyarg_t: +.Bd -literal -offset indent + struct fkeyarg { + u_short keynum; + char keydef[MAXFK]; + char flen; + }; +.Ed +.Pp +The field keynum defines which function key that is programmed. +The array keydef should contain the new string to be used (MAXFK long), +and the length should be entered in flen. + +The GIO_STRMAP ioctl call works in a semilar manner, execpt it returns +the current setting of keynum. + +The function keys are numbered like this: +.Bd -literal -offset indent + F1-F12 key 1 - 12 + Shift F1-F12 key 13 - 24 + Ctrl F1-F12 key 25 - 36 + Ctrl+shift F1-F12 key 37 - 48 + + Home key 49 + Up arrow key 50 + Page Up key 51 + (keypad) - key 52 + Left arrow key 53 + (keypad) 5 key 54 + Right arrow key 55 + (keypad) + key 56 + End key 57 + Down arrow key 58 + Page down key 59 + Insert key 60 +.Ed +.Pp +.Sh AUTHOR + S_ren Schmidt + Email: (baukno@login.dkuug.dk -or- sos@kmd-ac.dk) diff --git a/share/man/man4/man4.i386/lpa.4 b/share/man/man4/man4.i386/lpa.4 new file mode 100644 index 0000000000..659db55493 --- /dev/null +++ b/share/man/man4/man4.i386/lpa.4 @@ -0,0 +1,62 @@ +.\" +.\" Copyright (c) 1993 Christopher G. Demetriou +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Christopher G. Demetriou. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software withough specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" from: lpa.4,v 1.1 1993/08/06 10:34:11 cgd Exp +.\" $Id$ +.\" +.Dd August 28, 1993 +.Dt LPA 4 i386 +.Os FreeBSD +.Sh NAME +.Nm lpa +.Nd +Interruptless parallel port driver +.Sh SYNOPSIS +.\" XXX this is awful hackery to get it to work right... -- cgd +.Cd "device lpa0 at isa? port" \&"IO_LPT1\&" tty +.Cd "device lpa1 at isa? port" \&"IO_LPT2\&" tty +.Cd "device lpa2 at isa? port" \&"IO_LPT3\&" tty +.Sh DESCRIPTION +This driver provides access to parallel ports. It assumes that +the parallel port controller will not cause an interrupt, and +therefore must poll the controller. +.Sh FILES +.Bl -tag -width Pa -compact +.It Pa /dev/lpa0 +first interruptless parallel port driver +.El +.Sh SEE ALSO +.Xr lpt 4 +.Sh BUGS +This driver only exists to support broken parallel port implementations. +Systems with properly working parallel ports should use the +.Nm lpt +driver instead, as it is less resource-hungry. +.Pp +This driver could stand a rewrite. diff --git a/share/man/man4/man4.i386/lpt.4 b/share/man/man4/man4.i386/lpt.4 new file mode 100644 index 0000000000..070a53e0a0 --- /dev/null +++ b/share/man/man4/man4.i386/lpt.4 @@ -0,0 +1,59 @@ +.\" +.\" Copyright (c) 1993 Christopher G. Demetriou +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Christopher G. Demetriou. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software withough specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" from: lpt.4,v 1.1 1993/08/06 10:34:12 cgd Exp +.\" +.Dd August 28, 1993 +.Dt LPT 4 i386 +.Os FreeBSD +.Sh NAME +.Nm lpt +.Nd +Parallel port driver +.Sh SYNOPSIS +.Cd "device lpt0 at isa? port" \&"IO_LPT1\&" tty irq 7 vector lptintr +.Cd "device lpt1 at isa? port" \&"IO_LPT2\&" tty irq 7 vector lptintr +.Cd "device lpt2 at isa? port" \&"IO_LPT3\&" tty irq 7 vector lptintr +.Sh DESCRIPTION +This driver provides access to parallel ports. It assumes that +the parallel port controller interrupts the CPU properly. If +this is not the case, the +.Nm lpa +driver should be used instead. +.Sh FILES +.Bl -tag -width Pa -compact +.It Pa /dev/lpt0 +first parallel port driver +.El +.Sh SEE ALSO +.Xr lpa 4 +.Sh BUGS +There are lots of them, especially in cheap parallel port implementations. +.Pp +This driver could stand a rewrite. diff --git a/share/man/man4/man4.i386/mse.4 b/share/man/man4/man4.i386/mse.4 new file mode 100644 index 0000000000..f76f3530af --- /dev/null +++ b/share/man/man4/man4.i386/mse.4 @@ -0,0 +1,33 @@ +.\" Copyright 1992 by the University of Guelph +.\" +.\" Permission to use, copy and modify this +.\" software and its documentation for any purpose and without +.\" fee is hereby granted, provided that the above copyright +.\" notice appear in all copies and that both that copyright +.\" notice and this permission notice appear in supporting +.\" documentation. +.\" University of Guelph makes no representations about the suitability of +.\" this software for any purpose. It is provided "as is" +.\" without express or implied warranty. +.\" +.Dd Aug 16, 1992 +.Dt MSE 4 +.Os +.Sh NAME +.Nm mse +.Nd bus mouse driver +.Sh DESCRIPTION +This is a simple driver for the Logitech and ATI Inport bus mouse interfaces +designed to be used with the X386 X11R5 X server. The minor device number is +made up of: +.Bd -literal -offset indent +minor = ('unit' << 1) | 'non-blocking' +.Ed +.Pp +where 'unit' is the device number (usually 0) and the 'non-blocking' bit +is set to indicate "don't block waiting for mouse input, return 0 instead". +The 'non-blocking' bit should be set for X386, therefore the minor device +number usually used for X386 is 1. +.Sh Caveats +Most bus mice generate N interrupts/second when enabled, whether or not the +mouse state is changing. diff --git a/share/man/man4/man4.i386/npx.4 b/share/man/man4/man4.i386/npx.4 new file mode 100644 index 0000000000..f904bd19fd --- /dev/null +++ b/share/man/man4/man4.i386/npx.4 @@ -0,0 +1,77 @@ +.\" +.\" Copyright (c) 1993 Christopher G. Demetriou +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Christopher G. Demetriou. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software withough specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" from: npx.4,v 1.1 1993/08/06 10:58:03 cgd Exp +.\" $Id$ +.\" +.Dd August 28, 1993 +.Dt NPX 4 i386 +.Os FreeBSD +.Sh NAME +.Nm npx +.Nd +Numeric Processing Extension coprocessor and emulator +.Sh SYNOPSIS +.Cd "options MATH_EMULATE" +.\" XXX this is awful hackery to get it to work right... -- cgd +.Cd "device npx0 at isa? port" \&"IO_NPX0\&" tty irq 13 vector npxintr +.Sh DESCRIPTION +The +.Nm npx +driver enables the use of the system's Numeric Processing Extension +coprocessor, +if one is present. Numeric processing extensions are present in +systems with +.Nm 486DX +CPUs and in systems with +.Nm 387 +or +.Nm 487SX +coprocessors. The +.Nm npx +driver is required for proper system functioning regardless +of whether or not a NPX is present. +.Pp +If there is no NPX present in the system, the "MATH_EMULATE" +option must be defined in the kernel configuration file. +It will provide support for the instructions normally executed +by the NPX. If there is no NPX in the system and the kernel +is not built with math emulation, the system will not boot. +.Sh CAVEATS +The emulator is much slower than the NPX coprocessor. +This will result in poor floating-point math performance +if the emulator must be used. +.Sh BUGS +There are lots of them, especially on cheap motherboards. In particular, +some motherboards do not have the interrupt lines from the NPX to +the CPU wired properly. If this is the case, the emulator must be used +if consistent system operation is desired. +.Pp +Emulation of the transcendental functions instructions is incorrect. +Emulation of other instructions is suspect. diff --git a/share/man/man4/man4.i386/screen.4 b/share/man/man4/man4.i386/screen.4 new file mode 100644 index 0000000000..a0df69cc3c --- /dev/null +++ b/share/man/man4/man4.i386/screen.4 @@ -0,0 +1,157 @@ +.Dd April 7, 1993 +.Dt SCREEN 4 +.Os FreeBSD +.Sh NAME +.Nm screen +.Nd pc display interface +.Sh DESCRIPTION + +Access to the +.Ar virtual consoles +are obtained through the device files +tty01 - ttynn in /dev. +Each of these files correspond to a seperate +virtual console. +All virtual console devices can be open at once, but only one is +active at a time. The active virtual console "owns" the keyboard and +display screen. +.Pp +Output to a virtual console that not currently is on the display is +saved in a buffer that holds a "screenfull" (normally 25) lines. +Any output written to +.Ar /dev/console +(the original console device) is echoed to /dev/tty01. +.Pp +To switch between the virtual consoles one uses the sequence +.Ar ALT+Fn +, which means hold down ALT and press one of the function keys. The +virtual console with the same number as the function key is then +selected as the current virtual console, and given exclusive use of +the keyboard and display. This switch sequence can be changed via +the keyboard mapping ioctl call (see keyboard.man) +.Pp +The number of virtual consoles is changeable in the system config +file, and need recompilation of the kernel to take any effect. The +default is 12. + +The console allows entering values that are not physically +present on the keyboard via a special keysequence. +To use this facility press and hold down ALT, +then enter a decimal number from 0-255 via the numerical keypad, then +release ALT. The entered value is then used as the ASCII value for one +character. This way it is possible to enter any ASCII value. + +The console understands a subset of the ANSI x3.64 character +sequences. For compatibility with the old pccons, the PC3 character +sequences is also supported. +.Pp +.Bd -literal +ANSI Seq Function Termcap entry +======= ======= ===================================== ============== + +CUU E[nA move cursor up n lines up (ku) + +CUD E[nB move cursor down n lines do (kd) + +CUF E[nC move cursor right n characters nd (kr) + +CUB E[nD move cursor left n characters bs (kl) + +HPA E[n` move cursor character position n -- + +HPR E[na move cursor right n characters -- + +VPA E[nd move cursor to line n -- + +VPR E[ne move cursor down n lines -- + +CPL E[nF move cursor to start of line, -- + n lines up + +CNL E[nF move cursor to start of line, -- + n lines down + +CUP E[y;xH Move cursor to x, y cm + +HVP E[y;xf Move cursor to x, y -- + +CBT E[nZ Move cursor back n tab stops bt + +IL E[nL Insert n blank lines al + +ICH E[n@ Insert n blank characters ic + +DL E[nM Delete n lines dl + +DCH E[nP Delete n characters dc + +ED E[nJ Erase part or all of display: cd + n=0 from cursor to end of display, + n=1 from begin of display to cursor, + n=2 entire display. + +EL E[nK Erase part or all of line: ce + n=0 from cursor to end of line, + n=1 from begin of line to cursor, + n=2 entire line. + +ECH E[nX Erase n characters -- + +SU E[nS Scroll display n lines up (forward) sf + +SD E[nT Scroll display n lines down (reverse) sr + + +SGR E[nm Set character attributes: -- + n= 0 normal attribute (all off) + n= 1 bold (highlight) + n= 4 underscore (if supported by HW) + n= 5 blink (if supported by HW) + n= 7 reverse + n=3X set foreground color (see table) + n=4X set background color (see table) + + X=0 black X=1 red + X=2 green X=3 brown + X=4 blue X=5 magenta + X=6 cyan X=7 white + + +-- E[=p;dB Set bell pitch (p) and duration (d), -- + picth is in units of 840 nS, + duration is units of 0,1 S. + +-- E[=s:eC Set cursor start and end scanline, -- + start on line s, end on line e. + +-- E[=nA Set the border color to color n + (see table) (if supported by HW) + +-- E[=cF set normal foreground color to n -- + (see table) + +-- E[=cG set normal background color to n -- + (see table) + +-- E[=cH set reverse foreground color to n -- + (see table) + +-- E[=cI set reverse background color to n -- + (see table) + + n= 0 black n= 8 grey + n= 1 blue n= 9 light blue + n= 2 green n=10 light green + n= 3 cyan n=11 light cyan + n= 4 red n=12 light red + n= 5 magenta n=13 light magenta + n= 6 brown n=14 yellow + n= 7 white n=15 light white + +note: the first E in the sequences stands for ESC (0x1b) + +.Ed +.Pp +.Sh AUTHOR + S_ren Schmidt + Email: (baukno@login.dkuug.dk -or- sos@kmd-ac.dk) diff --git a/share/man/man4/man4.i386/sio.4 b/share/man/man4/man4.i386/sio.4 new file mode 100644 index 0000000000..53efb1c22b --- /dev/null +++ b/share/man/man4/man4.i386/sio.4 @@ -0,0 +1,101 @@ +.\" Copyright (c) 1990, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Systems Programming Group of the University of Utah Computer +.\" Science Department. +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)dca.4 5.2 (Berkeley) 3/27/91 +.\" from: com.4,v 1.1 1993/08/06 11:19:07 cgd Exp +.\" $Id$ +.\" +.Dd August 28, 1993 +.Dt SIO 4 i386 +.Os FreeBSD +.Sh NAME +.Nm sio +.Nd +fast interrupt serial communications interface +.Sh SYNOPSIS +.Cd "device sio0 at isa? port" \&"IO_COM1\&" tty irq 4 vector siointr +.Cd "device sio1 at isa? port" \&"IO_COM2\&" tty irq 3 vector siointr +.Cd "device sio2 at isa? port" \&"IO_COM3\&" tty irq 5 vector siointr +.Cd "device sio3 at isa? port" \&"IO_COM4\&" tty irq 9 vector siointr +.Sh DESCRIPTION +The +.Nm sio +driver provides support for NS8250-, NS16450-, and NS16550-based +.Tn EIA +.Tn RS-232C +.Pf ( Tn CCITT +.Tn V.28 ) +communications interfaces. The NS8250 and NS16450 have single character +buffers, and the NS16550 has a 16 character buffer. +.Pp +Input and output for each line may set to one of following baud rates; +50, 75, 110, 134.5, 150, 300, 600, 1200, 1800, 2400, 4800, 9600, +19200, 38400, 57600, or 115200. +.Sh FILES +.Bl -tag -width Pa +.It Pa /dev/tty00 +.It Pa /dev/tty01 +.El +.Sh DIAGNOSTICS +.Bl -diag +.It sio%d: silo overflow. +The single-character input +.Dq silo +has overflowed and incoming data has been lost. +.\".It com%d: weird interrupt: %x. +.\"The device has generated an unexpected interrupt +.\"with the code listed. +.El +.Sh SEE ALSO +.Xr tty 4 +.Xr com 4 +.Sh HISTORY +The +.Nm +driver is derived from the +.Nm HP9000/300 +.Nm dca +driver and is +.Ud +.Sh BUGS +Data loss is not near as likely on busy systems as they are with the +.Xr com 4 +driver but they still can occur at very high baud rates on slow systems. +.Pp +The constants which define the locations +of the various seiral ports are holdovers from +.Nm DOS . +.Pp +As usual, you get what you pay for; cheap NS16550 clones +generally don't work. diff --git a/share/man/man4/sd.4 b/share/man/man4/sd.4 new file mode 100644 index 0000000000..3bb2973840 --- /dev/null +++ b/share/man/man4/sd.4 @@ -0,0 +1,192 @@ +.Dd August 27, 1993 +.Dt SD 4 +.Os 386BSD/NetBSD +.Sh NAME +.Nm sd +.Nd scsi disk driver +.Sh SYNOPSIS +.Nm device-driver sd +.Op Ar count +.Sh DESCRIPTION +The +.Xr sd +driver provides support for a +.Em scsi +disk. It allows the disk +to be divided up into a set of pseudo devices called +.Em partitions. +A Partition can have both a +.Em raw +interface +and a +.Em Block mode +interface. +In general the interfaces are similar to those described by +.Xr wd 4 +or +.Xr dk 4 . + +.Pp +Where the +.Xr wd 4 +device has a fairly low level interface to the system, +.Em SCSI +devices have a much higher level interface and talk to the system via +a +.Em SCSI Adapter +and a +.Em Scsi Adapter driver +e.g. +.Xr AHA1542 . +A scsi adapter must also be separatly configured into the system +before a scsi disk can be configured. +.Pp +As the scsi adapter is probed during boot, the +.Em SCSI +bus is scanned for devices. Any devices found which answer as 'Direct' +type devices will be 'attached' to the +.Nm +driver. The first found will be attached as +.Em sd0 +and the next, +.Em sd1 +etc. +.Pp +The +.Nm +driver allows the disk to have two levels of partitioning. +One which allows it to have +partitions for different Operating systems, (one of which is BSD unix), +(see also for the 386 port, +.Xr fdisk 1 +), and within a BSD partition, further partitions which are individually +addressable as separate entries in the +.Em /dev +directory. The second level of partitioning is controlled by the program +.Xr disklabel 1 +and is common in format across most BSD operating systems. In most of +the original BSD ports, what is the +BSD part here, is the entire disk, and the outer layer of partitionning +does not exist. +.Nm +will also run in this manner if +.Xr disklabel 1 +is run with a blank disk, without first partitioning it +with +.Xr fdisk 1 +(or similar). + +.Pp +Apologies for the two conflicting usages of the word Partition, but +it's a historical artifact, and the meaning must be judged from context +in each case. The next paragraph will discuss partitions exclusively +in the context of WITHIN a BSD partition on the disk. +.Pp +The first few blocks of the BSD section (maybe all) of the disk contain +some boot code, and a structure, known as the +.Xr disklabel 5 +which describes the disk's characteristics and partitioning for BSD. +It is set up by the +.Xr disklabel 1 +program, and read in by the kernel when the device is first initialised +during boot. It describes how the drive is further divided. The +.Xr disklabel 5 +structure contains room for 8 (usually) partitions. Usually these +partitions are calculated so as to fall evenly on cylinder boundaries, +however on a +.Em SCSI +disk this is sometimes not possible. The reason for doing this is historically +to get better performance, however modern +.Em SCSI +disks often have a variable format, so that it is hard to know at any point +in the disk, where the cylinder or track boundaries are. Added to this, the +fact that +.Em SCSI +disk blocks are addressed soley by their 'block number' and not by +any geometry, leads to the common occurance on +.Em SCSI +disks, of laying out partitions on arbitrary boundaries. Because +modern disks often have large track caches, this often leads to only small +degadations of performance, and is in fact sometimes unavoidable. The +boot messages will suggest a geometry similar in heads and cylinders +to the real geometry, but the disklable need not agree with this for the +system to be able to successfully work with the disk. +.Pp +During booting +with an uninitialised disk, the +.Nm +driver will initialise the 'in-core' copy of the disklabel to the suggested +values, however they are not written to the disk. +.Pp +The fourth partition is special. No matter what the disklabel +says, the fourth partition (partition d) reflectls the entire disk, including +those areas OUTSIDE the BSD partitions. At some times it is suggested that +the c partition might be used to represent the entire BSD partition, so these +two partitions should be avoided when laying out filesystems. +.Pp +While partitions are only theoretically valid within the BSD partition, they +are specified in terms of absolute block numbers, so it is possible to +specify a partition that lies outside of the BSD partition. This is useful +if one wants to have a /dev entry that points to a partition belonging +to another OS (e.g. DOS). +.Pp +In configuring, if an optional +.Ar count +is given in +the specification, that number of scsi disks are configured; +Most storage for them is allocated only when found so a large number +of configured devices is cheap. (once the first has included the driver). + +.Pp +The following +.Xr ioctl 2 +calls apply to scsi disks as well as to other disks. They are defined +in the header file +.Em disklabel.h. + +.Bl -tag -width DIOCSDINFO + +.It Dv DIOCSBAD +Usually used to set up a bad-block mapping system on the disk. Scsi +drive incorporate their own bad-block mapping so this is not implimented, +however it MAY be implimented in the future as a 'kludged' interface to the +scsi bad-block mapping. +.It Dv DIOCGDINFO +Read, from the kernel, the in-core copy of the disklabel for the +drive. This may be a ficticious disklabel if the drive has never +been initialised, in which case it will contain information read +from the scsi inquiry commands, and should be the same as +the information printed at boot. +.It Dv DIOCSDINFO +Give the driver a new disklabel to use. The driver will NOT try write the new +disklabel to the disk. +.It Dv DIOCWLABEL +Enable or Disable the driver's software +write protect of the disklabel on the disk. +.It Dv DIOCWDINFO +Give the driver a new disklabel to use. The driver WILL try write the new +disklabel to the disk. +.Pp +.El + +.Sh FILES +.Bl -tag -width /dev/rsd[0-9][a-h] -compact +.It Pa /dev/sd[0-9][a-h] +block mode scsi disks +.It Pa /dev/rsd[0-9][a-h] +raw scsi disks +.El +.Sh DIAGNOSTICS +None. +.Sh SEE ALSO +.Xr disklabel 1 +.Xr disklabel 5 +.Xr fdisk 1 +.Xr wd 4 +.Xr dk 4 +(on other systems) +.Sh HISTORY +The +.Nm +driver appeared in MACH 2.5 . + diff --git a/share/man/man4/st.4 b/share/man/man4/st.4 new file mode 100644 index 0000000000..5223121880 --- /dev/null +++ b/share/man/man4/st.4 @@ -0,0 +1,244 @@ +.Dd August 27, 1993 +.Dt ST 4 +.Os 386BSD/NetBSD +.Sh NAME +.Nm st +.Nd scsi tape driver +.Sh SYNOPSIS +.Nm device-driver st +.Op Ar count +.Sh DESCRIPTION +The +.Xr st +driver provides support for a +.Em scsi +tape. It allows the tape +to be run in upto four different modes depending on minor numbers +and supports several different 'close' modes. +The device can have both a +.Em raw +interface +and a +.Em Block mode +interface however only the raw interface is usually used (or recommended). +In general the interfaces are similar to those described by +.Xr wt 4 +or +.Xr mt 4 . + +.Pp +Where the +.Xr wt 4 +device has a fairly low level interface to the system, +.Em SCSI +devices have a much higher level interface and talk to the system via +a +.Em SCSI Adapter +and a +.Em Scsi Adapter driver +e.g. +.Xr AHA1542 . +A scsi adapter must also be separatly configured into the system +before a scsi tape can be configured. +.Pp +As the scsi adapter is probed during boot, the +.Em SCSI +bus is scanned for devices. Any devices found which answer as 'Sequential' +type devices will be attached to the +.Nm +driver. The first found will be attached as +.Em st0 +and the next, +.Em st1 +etc. +.Pp +The +.Nm +driver allows the tape to have four different modes of operation. +They are controlled by bits 2 and 3 of the minor number. In addition, bits +0 and 1 of the minor number are interpretted as being 'close modes'. +The following close modes are supported (1 free, suggestions accepted) +.Bl -tag -width ABOUT_THIS_BIG +.It Pa 00 +A close will rewind the device. If the tape has been +written, then a Filemark will be written before the rewind is requested. +.It Pa 01 +A close will leave the tape loaded. +If the tape was written to a filemark will be written. +No other head positioning takes place. +Any further reads or writes will occur directly after the +last read, or the written filemark. +.It Pa 10 +A close will rewind the device. If the tape has been +written, then a Filemark will be written before the rewind is requested. +On completion of the rewind an UNLOAD command will be issued. +.It Pa 11 +Not yet defined.. your suggestions welcome? +Possibly like 01 but if the filemark has not already been encountered +on a read, skip forward until it is found. +.El + +Scsi Tapes may run in either 'variable' or 'fixed block' modes. +Most QIC type devices run in Fixed block mode, where most 'reel to reel' tapes and +many new cartridge formats, allow variable blocksize. The difference between +the two is as follows: +.Bl -tag -width variable-blocksize +.It Pa Variable Blocksize +Each write made to the device results in a single logical record written to +the tape. You can never read or write PART of a record from tape, (though you +may request a larger block and read a smaller record). You cannot read +multiple blocks either. +Data from a single write +is therefore read by a single read. The block size used may be any value +supported by the device, the scsi adapter and the system. +(often variable between 1 byte and 64k (sometimes more)). +.Pp +When reading a variable record/block from the tape, +the head is logically considered to be immediatly after the last item read, +and before the next item after that. If the next item is a Filemark, but +you never read it because you have all the data, then the next process to read +will immediatly read the filemark and return EOF. (assuming you were +in non-rewind mode). +.It Pa fixed Blocksize +Data written by the user is passed to the tape as a succession of +fixed size blocks. It may be contiguouse in ram and read in a single DMA +pass, however it is considered to be a series of independent blocks. You may +never write an amount of data that is not an exact multiple of the blocksize. +You may read and write the same data as a different set of records, +In other words, blocks that were written together may be read separatly, and +visa versa. +.Pp + If you ask for more blocks than there are left in the file, +then the drive will encounter the filemark. Because there is some data to +return to you (unless there were no records before the filemark) the driver +will return the data to you (less than you requested), but hide from +you the discovery of the Filemark. The NEXT read will be returned +immediatly with an EOF. If you never Make the next read, but close the device, +the next process to read +will immediatly read the filemark and return EOF. (assuming you were +in non-rewind mode). +.El + +There are four Operation modes. These are controlled by bits 2 and 3 of +the minor number and are designed to allow people to easily read +and write different formats of tape on devices that allow multiple formats. +The formats are: 00-default, 01-high-density, 10-medium-density, and +11-low-density, though the actual densities in high, medium and low +can be set by hand with the +.Xr st 1 +variant of the +.Xr mt 1 +command. +The exact mechanism for setting the densities may change in the future. +.Pp +Because different tape drives behave differently, there is a mechanism +within the source to st, to quickly and conveniently recognise and deal +with brands and models of drive that have special requirements. +.Pp +There is a table (called the rogues gallery) in which the indentification +strings of known errant drives can be stored. Along with each is +a set of flags that allows the setting of densities for each +of the 4 modes, along with a set of 'QUIRK' flags that can be +used to enable or disable sections of code within the driver if a particular +drive is recognised. +.Pp +The handling of filemarks on write is pretty much automatic. If you +have written to the tape, and not done a read since, then a filemark will +be written to the tape when you close the device. If a rewind is requested +after a write, then the driver assumes that you have written the last file +on the tape and ensures that there are two filemarks written to the tape. +It takes into account any filemarks already written (whether by close +or by explicit ioctl). The exception to this is that there seems to be +a standard (which we follow, but don't understand why) that certain +types of tape do not actually write two filemarks to tape, +but when read, report a 'phantom' filemark when the last file is read. +These devices include the QIC family of devices. It might be that this +set of devices is the same set as that of fixed block devices. This has not +been detirmined yet, and they are treated as separate behaviours by the +driver at this time. +.Pp +In configuring, if an optional +.Ar count +is given in +the specification, that number of scsi tapes are configured; +Most storage for them is allocated only when found so a large number +of configured devices is cheap. (once the first has included the driver). +.Pp +The following +.Xr ioctl 2 +calls apply to scsi tapes. Some also apply to other tapes. They are defined +in the header file +.Em /sys/mtio.h. + +.Bl -tag -width MTIOCEEOT +.It Pa MTIOCGET +Get the mt control structure filled out by the driver, showing +all the present settings. +.It Pa MTIOCTOP +Perform one of the following operations. These operations all have a +single argument, which is either a boolean, or a signed integer, depending +on the operation. +.Bl -tag -width MTSELDNSTY +.It Pa MTWEOF +Write N end of file marks at the present head position. +.It Pa MTFSF +Skip over N Filemarks. Leave the head on the EOM side of the last skipped +Filemark. +.It Pa MTBSF +Skip BACKWARDS over N Filemarks. Leave the head on the BOM (beginning of media) +side of the last skipped Filemark. +.It Pa MTFSR +Skip forwards over N records. +.It Pa MTBSR +Skip backwards over N records. +.It Pa MTREW +Rewind the device to the beginning of the media. +.It Pa MTOFFL +Rewind the media (and if possible eject). Even if the device cannot +eject the media it will often no longer respond to normal requests. +.It Pa MTNOP +No Op, set status only.. +.It Pa MTCACHE +Enable controller Buffering. +.It Pa MTNOCACHE +Disable controller Buffering. +.It Pa MTSETBSIZ +Set the blocksize to use for the device. If the device is capable of +variable blocksize operation, and the blocksize is set to 0, then the drive +will be driven in variable mode. +.It Pa MTSETDNSTY +Set the Density value (see +.Xr st 1 +) to use when running in the mode openned (minor bits 2,3). +.El +.It Pa MTIOCIEOT +?Set END of TAPE processing... not yet supported. +.It Pa MTIOCEEOT +?Set END of TAPE processing... not yet supported. +.El + +.Sh FILES +.Bl -tag -width /dev/[hml][n][e]rst[0-9] -compact +.It Pa /dev/[hml][n][e]rst[0-9] +general form: +.It Pa /dev/hrst0 +High density, rewind on close +.It Pa /dev/mnrst0 +Medium density, No rewind on close +.It Pa /dev/lerst0 +Low density, Eject on close (if capable) +.It Pa /dev/rst0 +default density, rewind on close +.It Pa /dev/nrst0 +default density, No rewind on close +.El +.Sh DIAGNOSTICS +None. +.Sh SEE ALSO +.Xr mt 1 +.Xr st 1 +.Sh HISTORY +This +.Nm +driver appeared in MACH 2.5 . + diff --git a/share/man/man4/termios.4 b/share/man/man4/termios.4 new file mode 100644 index 0000000000..e1cd6f33e4 --- /dev/null +++ b/share/man/man4/termios.4 @@ -0,0 +1,1407 @@ +.\" Copyright (c) 1991, 1992 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)termios.4 5.4 (Berkeley) 1/19/93 +.\" +.Dd January 19, 1993 +.Dt TERMIOS 4 +.Os BSD 4 +.Sh NAME +.Nm termios +.Nd general terminal line discipline +.Sh SYNOPSIS +.Fd #include +.Sh DESCRIPTION +This describes a general terminal line discipline that is +supported on tty asynchronous communication ports. +.Ss Opening a Terminal Device File +When a terminal file is opened, it normally causes the process to wait +until a connection is established. For most hardware, the presence +of a connection is indicated by the assertion of the hardware +.Dv CARRIER line. +If the termios structure associated with the terminal file has the +.Dv CLOCAL +flag set in the cflag, or if the +.Dv O_NONBLOCK +flag is set +in the +.Xr open 2 +call, then the open will succeed even without +a connection being present. +In practice, applications +seldom open these files; they are opened by special programs, such +as +.Xr getty 2 +or +.Xr rlogind 2 , +and become +an application's standard input, output, and error files. +.Ss Job Control in a Nutshell +Every process is associated with a particular process group and session. +The grouping is hierarchical: every member of a particular process group is a +member of the same session. This structuring is used in managing groups +of related processes for purposes of +.\" .Gw "job control" ; +.Em "job control" ; +that is, the +ability from the keyboard (or from program control) to simultaneously +stop or restart +a complex command (a command composed of one or more related +processes). The grouping into process groups allows delivering +of signals that stop or start the group as a whole, along with +arbitrating which process group has access to the single controlling +terminal. The grouping at a higher layer into sessions is to restrict +the job control related signals and system calls to within processes +resulting from a particular instance of a "login". Typically, a session +is created when a user logs in, and the login terminal is setup +to be the controlling terminal; all processes spawned from that +login shell are in the same session, and inherit the controlling +terminal. +A job control shell +operating interactively (that is, reading commands from a terminal) +normally groups related processes together by placing them into the +same process group. A set of processes in the same process group +is collectively refered to as a "job". When the foreground process +group of the terminal is the same as the process group of a particular +job, that job is said to be in the "foreground". When the process +group of the terminal is different than the process group of +a job (but is still the controlling terminal), that job is said +to be in the "background". Normally the +shell reads a command and starts the job that implements that +command. If the command is to be started in the foreground (typical), it +sets the process group of the terminal to the process group +of the started job, waits for the job to complete, and then +sets the process group of the terminal back to its own process +group (it puts itself into the foreground). If the job is to +be started in the background (as denoted by the shell operator "&"), +it never changes the process group of the terminal and doesn't +wait for the job to complete (that is, it immediately attempts to read the next +command). If the job is started in the foreground, the user may +type a key (ususally +.Ql \&^Z ) +which generates the terminal stop signal +.Pq Dv SIGTSTP +and has the affect of stopping the entire job. +The shell will notice that the job stopped, and will resume running after +placing itself in the foreground. +The shell also has commands for placing stopped jobs in the background, +and for placing stopped or background jobs into to the foreground. +.Ss Orphaned Process Groups +An orphaned process group is a process group that has no process +whose parent is in a different process group, yet is in the same +session. Conceptually it means a process group that doesn't have +a parent that could do anything if it were to be stopped. For example, +the initial login shell is typically in an orphaned process group. +Orphaned process groups are immune to keyboard generated stop +signals and job control signals resulting from reads or writes to the +controlling terminal. +.Ss The Controlling Terminal +A terminal may belong to a process as its controlling terminal. Each +process of a session that has a controlling terminal has the same +controlling terminal. A terminal may be the controlling terminal for at +most one session. The controlling terminal for a session is allocated by +the session leader by issuing the +.Dv TIOCSCTTY +ioctl. A controlling terminal +is never acquired by mereley opening a terminal device file. +When a controlling terminal becomes +associated with a session, its foreground process group is set to +the process group of the session leader. +.Pp +The controlling terminal is inherited by a child process during a +.Xr fork 2 +function call. A process relinquishes its controlling terminal when it +creates a new session with the +.Xd setsid 2 +function; other processes +remaining in the old session that had this terminal as their controlling +terminal continue to have it. +A process does not relinquish its +controlling terminal simply by closing all of its file descriptors +associated with the controlling terminal if other processes continue to +have it open. +.Pp +When a controlling process terminates, the controlling terminal is +disassociated from the current session, allowing it to be acquired by a +new session leader. Subsequent access to the terminal by other processes +in the earlier session will be denied, with attempts to access the +terminal treated as if modem disconnect had been sensed. +.Ss Terminal Access Control +If a process is in the foreground process group of its controlling +terminal, read operations are allowed. +Any attempts by a process +in a background process group to read from its controlling terminal +causes a +.Dv SIGTTIN +signal to be sent to +the process's group +unless one of the +following special cases apply: If the reading process is ignoring or +blocking the +.Dv SIGTTIN signal, or if the process group of the reading +process is orphaned, the +.Xr read 2 +returns -1 with +.Va errno set to +.Er Dv EIO +and no +signal is sent. The default action of the +.Dv SIGTTIN +signal is to stop the +process to which it is sent. +.Pp +If a process is in the foreground process group of its controlling +terminal, write operations are allowed. +Attempts by a process in a background process group to write to its +controlling terminal will cause the process group to be sent a +.Dv SIGTTOU +signal unless one of the following special cases apply: If +.Dv TOSTOP +is not +set, or if +.Dv TOSTOP +is set and the process is ignoring or blocking the +.Dv SIGTTOU +signal, the process is allowed to write to the terminal and the +.Dv SIGTTOU +signal is not sent. If +.Dv TOSTOP +is set, and the process group of +the writing process is orphaned, and the writing process is not ignoring +or blocking +.Dv SIGTTOU , +the +.Xr write +returns -1 with +errno set to +.Er Dv EIO +and no signal is sent. +.Pp +Certain calls that set terminal parameters are treated in the same +fashion as write, except that +.Dv TOSTOP +is ignored; that is, the effect is +identical to that of terminal writes when +.Dv TOSTOP +is set. +.Ss Input Processing and Reading Data +A terminal device associated with a terminal device file may operate in +full-duplex mode, so that data may arrive even while output is occurring. +Each terminal device file has associated with it an input queue, into +which incoming data is stored by the system before being read by a +process. The system imposes a limit, +.Pf \&{ Dv MAX_INPUT Ns \&} , +on the number of +bytes that may be stored in the input queue. The behavior of the system +when this limit is exceeded depends on the setting of the +.Dv IMAXBEL +flag in the termios +.Fa c_iflag . +If this flag is set, the terminal +is a sent an +.Tn ASCII +.Dv BEL +character each time a character is recieved +while the input queue is full. Otherwise, the input queue is flushed +upon receiving the character. +.Pp +Two general kinds of input processing are available, determined by +whether the terminal device file is in canonical mode or noncanonical +mode. Additionally, +input characters are processed according to the +.Fa c_iflag +and +.Fa c_lflag +fields. Such processing can include echoing, which +in general means transmitting input characters immediately back to the +terminal when they are received from the terminal. This is useful for +terminals that can operate in full-duplex mode. +.Pp +The manner in which data is provided to a process reading from a terminal +device file is dependent on whether the terminal device file is in +canonical or noncanonical mode. +.Pp +Another dependency is whether the +.Dv O_NONBLOCK +flag is set by +.Xr open() +or +.Xr fcntl() . +If the +.Dv O_NONBLOCK +flag is clear, then the read request is +blocked until data is available or a signal has been received. If the +.Dv O_NONBLOCK +flag is set, then the read request is completed, without +blocking, in one of three ways: +.Bl -enum -offset indent +.It +If there is enough data available to satisfy the entire request, +and the read completes successfully the number of +bytes read is returned. +.It +If there is not enough data available to satisfy the entire +request, and the read completes successfully, having read as +much data as possible, the number of bytes read is returned. +.It +If there is no data available, the read returns -1, with +errno set to +.Br EAGAIN . +.El +.Pp +When data is available depends on whether the input processing mode is +canonical or noncanonical. +.Ss Canonical Mode Input Processing +In canonical mode input processing, terminal input is processed in units +of lines. A line is delimited by a newline +.Ql \&\en +character, an end-of-file +.Pq Dv EOF +character, or an end-of-line +.Pq Dv EOL +character. See the +.Sx "Special Characters" +section for +more information on +.Dv EOF +and +.Dv EOL . +This means that a read request will +not return until an entire line has been typed, or a signal has been +received. Also, no matter how many bytes are requested in the read call, +at most one line is returned. It is not, however, necessary to +read a whole line at once; any number of bytes, even one, may be +requested in a read without losing information. +.Pp +.Pf \&{ Dv MAX_CANON Ns \&} +is a limit on the +number of bytes in a line. +The behavior of the system when this limit is +exceeded is the same as when the input queue limit +.Pf \&{ Dv MAX_INPUT Ns \&} , +is exceeded. +.Pp +Erase and kill processing occur when either of two special characters, +the +.Dv ERASE +and +.Dv KILL +characters (see the +.Sx "Special Characters section" ) , +is received. +This processing affects data in the input queue that has not yet been +delimited by a newline +.Dv NL, +.Dv EOF , +or +.Dv EOL +character. This un-delimited +data makes up the current line. The +.Dv ERASE +character deletes the last +character in the current line, if there is any. The +.Dv KILL +character +deletes all data in the current line, if there is any. The +.Dv ERASE +and +.Dv KILL +characters have no effect if there is no data in the current line. +The +.Dv ERASE +and +.Dv KILL +characters themselves are not placed in the input +queue. +.Ss Noncanonical Mode Input Processing +In noncanonical mode input processing, input bytes are not assembled into +lines, and erase and kill processing does not occur. The values of the +.Dv MIN +and +.Dv TIME +members of the +.Fa c_cc +array are used to determine how to +process the bytes received. +.Pp +.Dv MIN +represents the minimum number of bytes that should be received when +the +.Xr read +function successfully returns. +.Dv TIME +is a timer of 0.1 second +granularity that is used to time out bursty and short term data +transmissions. If +.Dv MIN +is greater than +.Dv \&{ Dv MAX_INPUT Ns \&} , +the response to the +request is undefined. The four possible values for +.Dv MIN +and +.Dv TIME +and +their interactions are described below. +.Ss "Case A: MIN > 0, TIME > 0" +In this case +.Dv TIME +serves as an inter-byte timer and is activated after +the first byte is received. Since it is an inter-byte timer, it is reset +after a byte is received. The interaction between +.Dv MIN +and +.Dv TIME +is as +follows: as soon as one byte is received, the inter-byte timer is +started. If +.Dv MIN +bytes are received before the inter-byte timer expires +(remember that the timer is reset upon receipt of each byte), the read is +satisfied. If the timer expires before +.Dv MIN +bytes are received, the +characters received to that point are returned to the user. Note that if +.Dv TIME +expires at least one byte is returned because the timer would +not have been enabled unless a byte was received. In this case +.Pf \&( Dv MIN +> 0, +.Dv TIME +> 0) the read blocks until the +.Dv MIN +and +.Dv TIME +mechanisms are +activated by the receipt of the first byte, or a signal is received. If +data is in the buffer at the time of the read(), the result is as +if data had been received immediately after the read(). +.Ss "Case B: MIN > 0, TIME = 0" +In this case, since the value of +.Dv TIME +is zero, the timer plays no role +and only +.Dv MIN +is significant. A pending read is not satisfied until +.Dv MIN +bytes are received (i.e., the pending read blocks until +.Dv MIN +bytes +are received), or a signal is received. A program that uses this case to +read record-based terminal +.Dv I/O +may block indefinitely in the read +operation. +.Ss "Case C: MIN = 0, TIME > 0" +In this case, since +.Dv MIN += 0, +.Dv TIME +no longer represents an inter-byte +timer. It now serves as a read timer that is activated as soon as the +read function is processed. A read is satisfied as soon as a single +byte is received or the read timer expires. Note that in this case if +the timer expires, no bytes are returned. If the timer does not +expire, the only way the read can be satisfied is if a byte is received. +In this case the read will not block indefinitely waiting for a byte; if +no byte is received within +.Dv TIME Ns *0.1 +seconds after the read is initiated, +the read returns a value of zero, having read no data. If data is +in the buffer at the time of the read, the timer is started as if +data had been received immediately after the read. +.Ss Case D: MIN = 0, TIME = 0 +The minimum of either the number of bytes requested or the number of +bytes currently available is returned without waiting for more +bytes to be input. If no characters are available, read returns a +value of zero, having read no data. +.Ss Writing Data and Output Processing +When a process writes one or more bytes to a terminal device file, they +are processed according to the +.Fa c_oflag +field (see the +.Sx "Output Modes +section). The +implementation may provide a buffering mechanism; as such, when a call to +write() completes, all of the bytes written have been scheduled for +transmission to the device, but the transmission will not necessarily +have completed. +.\" See also .Sx "6.4.2" for the effects of +.\" .Dv O_NONBLOCK +.\" on write. +.Ss Special Characters +Certain characters have special functions on input or output or both. +These functions are summarized as follows: +.Bl -tag -width indent +.It Dv INTR +Special character on input and is recognized if the +.Dv ISIG +flag (see the +.Sx "Local Modes" +section) is enabled. Generates a +.Dv SIGINT +signal which is sent to all processes in the foreground +process group for which the terminal is the controlling +terminal. If +.Dv ISIG +is set, the +.Dv INTR +character is +discarded when processed. +.It Dv QUIT +Special character on input and is recognized if the +.Dv ISIG +flag is enabled. Generates a +.Dv SIGQUIT +signal which is +sent to all processes in the foreground process group +for which the terminal is the controlling terminal. If +.Dv ISIG +is set, the +.Dv QUIT +character is discarded when +processed. +.It Dv ERASE +Special character on input and is recognized if the +.Dv ICANON +flag is set. Erases the last character in the +current line; see +.Sx "Canonical Mode Input Processing" . +It does not erase beyond +the start of a line, as delimited by an +.Dv NL , +.Dv EOF , +or +.Dv EOL +character. If +.Dv ICANON +is set, the +.Dv ERASE +character is +discarded when processed. +.It Dv KILL +Special character on input and is recognized if the +.Dv ICANON +flag is set. Deletes the entire line, as +delimited by a +.Dv NL , +.Dv EOF , +or +.Dv EOL +character. If +.Dv ICANON +is set, the +.Dv KILL +character is discarded when processed. +.It Dv EOF +Special character on input and is recognized if the +.Dv ICANON +flag is set. When received, all the bytes +waiting to be read are immediately passed to the +process, without waiting for a newline, and the +.Dv EOF +is discarded. Thus, if there are no bytes waiting (that +is, the +.Dv EOF +occurred at the beginning of a line), a byte +count of zero is returned from the read(), +representing an end-of-file indication. If +.Dv ICANON +is +set, the +.Dv EOF +character is discarded when processed. +.Dv NL +Special character on input and is recognized if the +.Dv ICANON +flag is set. It is the line delimiter +.Ql \&\en . +.It Dv EOL +Special character on input and is recognized if the +.Dv ICANON +flag is set. Is an additional line delimiter, +like +.Dv NL . +.It Dv SUSP +If the +.Dv ISIG +flag is enabled, receipt of the +.Dv SUSP +character causes a +.Dv SIGTSTP +signal to be sent to all processes in the +foreground process group for which the terminal is the +controlling terminal, and the +.Dv SUSP +character is +discarded when processed. +.It Dv STOP +Special character on both input and output and is +recognized if the +.Dv IXON +(output control) or +.Dv IXOFF +(input +control) flag is set. Can be used to temporarily +suspend output. It is useful with fast terminals to +prevent output from disappearing before it can be read. +If +.Dv IXON +is set, the +.Dv STOP +character is discarded when +processed. +.It Dv START +Special character on both input and output and is +recognized if the +.Dv IXON +(output control) or +.Dv IXOFF +(input +control) flag is set. Can be used to resume output that +has been suspended by a +.Dv STOP +character. If +.Dv IXON +is set, the +.Dv START +character is discarded when processed. +.Dv CR +Special character on input and is recognized if the +.Dv ICANON +flag is set; it is the +.Ql \&\er , +as denoted in the +.Tn \&C +Standard {2}. When +.Dv ICANON +and +.Dv ICRNL +are set and +.Dv IGNCR +is not set, this character is translated into a +.Dv NL , +and +has the same effect as a +.Dv NL +character. +.El +.Pp +The following special characters are extensions defined by this +system and are not a part of 1003.1 termios. +.Bl -tag -width indent +.It Dv EOL2 +Secondary +.Dv EOL +character. Same function as +.Dv EOL. +.It Dv WERASE +Special character on input and is recognized if the +.Dv ICANON +flag is set. Erases the last word in the current +line according to one of two algorithms. If the +.Dv ALTWERASE +flag is not set, first any preceding whitespace is +erased, and then the maximal sequence of non-whitespace +characters. If +.Dv ALTWERASE +is set, first any preceding +whitespace is erased, and then the maximal sequence +of alphabetic/underscores or non alphabetic/underscores. +As a special case in this second algorithm, the first previous +non-whitespace character is skippied in determining +whether the preceding word is a sequence of +alphabetic/undercores. This sounds confusing but turns +out to be quite practical. +.It Dv REPRINT +Special character on input and is recognized if the +.Dv ICANON +flag is set. Causes the current input edit line +to be retyped. +.It Dv DSUSP +Has similar actions to the +.Dv SUSP +character, except that +the +.Dv SIGTSTP +signal is delivered when one of the processes +in the foreground process group issues a read() to the +controlling terminal. +.It Dv LNEXT +Special character on input and is recognized if the +.Dv IEXTEN +flag is set. Receipt of this character causes the next +character to be taken literally. +.It Dv DISCARD +Special character on input and is recognized if the +.Dv IEXTEN +flag is set. Receipt of this character toggles the flushing +of terminal output. +.It Dv STATUS +Special character on input and is recognized if the +.Dv ICANON +flag is set. Receipt of this character causes a +.Dv SIGINFO +signal to be sent to the forground process group of the +terminal. Also, if the +.Dv NOKERNINFO +flag is not set, it +causes the kernel to write a status message to the terminal +that displays the current load average, the name of the +command in the foreground, its process ID, the symbolic +wait channel, the number of user and system seconds used, +the percentage of cpu the process is getting, and the resident +set size of the process. +.El +.Pp +The +.Dv NL +and +.Dv CR +characters cannot be changed. +The values for all the remaining characters can be set and are +described later in the document under +Special Control Characters. +.Pp +Special +character functions associated with changeable special control characters +can be disabled individually by setting their value to +.Dv {_POSIX_VDISABLE}; +see +.Sx "Special Control Characters" . +.Pp +If two or more special characters have the same value, the function +performed when that character is received is undefined. +.Ss Modem Disconnect +If a modem disconnect is detected by the terminal interface for a +controlling terminal, and if +.Dv CLOCAL +is not set in the +.Fa c_cflag +field for +the terminal, the +.Dv SIGHUP +signal is sent to the controlling +process associated with the terminal. Unless other arrangements have +been made, this causes the controlling process to terminate. +Any subsequent call to the read() function return the value zero, +indicating end of file. Thus, processes that read a terminal +file and test for end-of-file can terminate appropriately after a +disconnect. +.\" If the +.\" .Er EIO +.\" condition specified in 6.1.1.4 that applies +.\" when the implementation supports job control also exists, it is +.\" unspecified whether the +.\" .Dv EOF +.\" condition or the +.\" .Pf [ Dv EIO +.\" ] is returned. +Any +subsequent write() to the terminal device returns -1, with +.Va errno +set to +.Er EIO , +until the device is closed. +.Sh General Terminal Interface +.Pp +.Ss Closing a Terminal Device File +The last process to close a terminal device file causes any output +to be sent to the device and any input to be discarded. Then, if +.Dv HUPCL +is set in the control modes, and the communications port supports a +disconnect function, the terminal device performs a disconnect. +.Ss Parameters That Can Be Set +Routines that need to control certain terminal +.Tn I/O +characteristics +do so by using the termios structure as defined in the header +.Aq Pa termios.h . +This structure contains minimally four scalar elements of bit flags +and one array of special characters. The scalar flag elements are +named: +.Fa c_iflag , +.Fa c_oflag , +.Fa c_cflag , +and +.Fa c_lflag . The character array +is named +.Fa c_cc , +and its maximum index is +.Dv NCCS . +.Ss Input Modes +Values of the +.Fa c_iflag +field describe the basic +terminal input control, and are composed of +following masks: +.Pp +.Bl -tag -width IMAXBEL -offset indent -compact +.It Dv IGNBRK +/* ignore BREAK condition */ +.It Dv BRKINT +/* map BREAK to SIGINTR */ +.It Dv IGNPAR +/* ignore (discard) parity errors */ +.It Dv PARMRK +/* mark parity and framing errors */ +.It Dv INPCK +/* enable checking of parity errors */ +.It Dv ISTRIP +/* strip 8th bit off chars */ +.It Dv INLCR +/* map NL into CR */ +.It Dv IGNCR +/* ignore CR */ +.It Dv ICRNL +/* map CR to NL (ala CRMOD) */ +.It Dv IXON +/* enable output flow control */ +.It Dv IXOFF +/* enable input flow control */ +.It Dv IXANY +/* any char will restart after stop */ +.It Dv IMAXBEL +/* ring bell on input queue full */ +.El +.Pp +In the context of asynchronous serial data transmission, a break +condition is defined as a sequence of zero-valued bits that continues for +more than the time to send one byte. The entire sequence of zero-valued +bits is interpreted as a single break condition, even if it continues for +a time equivalent to more than one byte. In contexts other than +asynchronous serial data transmission the definition of a break condition +is implementation defined. +.Pp +If +.Dv IGNBRK +is set, a break condition detected on input is ignored, that +is, not put on the input queue and therefore not read by any process. If +.Dv IGNBRK +is not set and +.Dv BRKINT +is set, the break condition flushes the +input and output queues and if the terminal is the controlling terminal +of a foreground process group, the break condition generates a +single +.Dv SIGINT +signal to that foreground process group. If neither +.Dv IGNBRK +nor +.Dv BRKINT +is set, a break condition is read as a single +.Ql \&\e0 , +or if +.Dv PARMRK +is set, as +.Ql \&\e377 , +.Ql \&\e0 , +.Ql \&\e0 . +.Pp +If +.Dv IGNPAR +is set, a byte with a framing or parity error (other than +break) is ignored. +.Pp +If +.Dv PARMRK +is set, and +.Dv IGNPAR +is not set, a byte with a framing or parity +error (other than break) is given to the application as the three- +character sequence +.Ql \&\e377 , +.Ql \&\e0 , +X, where +.Ql \&\e377 , +.Ql \&\e0 +is a two-character +flag preceding each sequence and X is the data of the character received +in error. To avoid ambiguity in this case, if +.Dv ISTRIP +is not set, a valid +character of +.Ql \&\e377 +is given to the application as +.Ql \&\e377 , +.Ql \&\e377 . +If +neither +.Dv PARMRK +nor +.Dv IGNPAR +is set, a framing or parity error (other than +break) is given to the application as a single character +.Ql \&\e0 . +.Pp +If +.Dv INPCK +is set, input parity checking is enabled. If +.Dv INPCK +is not set, +input parity checking is disabled, allowing output parity generation +without input parity errors. Note that whether input parity checking is +enabled or disabled is independent of whether parity detection is enabled +or disabled (see +.Sx "Control Modes" ) . +If parity detection is enabled but input +parity checking is disabled, the hardware to which the terminal is +connected recognizes the parity bit, but the terminal special file +does not check whether this bit is set correctly or not. +.Pp +If +.Dv ISTRIP +is set, valid input bytes are first stripped to seven bits, +otherwise all eight bits are processed. +.Pp +If +.Dv INLCR +is set, a received +.Dv NL character is translated into a +.Dv CR +character. If +.Dv IGNCR +is set, a received +.Dv CR +character is ignored (not +read). If +.Dv IGNCR +is not set and +.Dv ICRNL +is set, a received +.Dv CR +character is +translated into a +.Dv NL +character. +.Pp +If +.Dv IXON +is set, start/stop output control is enabled. A received +.Dv STOP +character suspends output and a received +.Dv START +character +restarts output. If +.Dv IXANY +is also set, then any character may +restart output. When +.Dv IXON +is set, +.Dv START +and +.Dv STOP +characters are not +read, but merely perform flow control functions. When +.Dv IXON +is not set, +the +.Dv START +and +.Dv STOP +characters are read. +.Pp +If +.Dv IXOFF +is set, start/stop input control is enabled. The system shall +transmit one or more +.Dv STOP +characters, which are intended to cause the +terminal device to stop transmitting data, as needed to prevent the input +queue from overflowing and causing the undefined behavior described in +.Sx "Input Processing and Reading Data" , +and shall transmit one or more +.Dv START +characters, which are +intended to cause the terminal device to resume transmitting data, as +soon as the device can continue transmitting data without risk of +overflowing the input queue. The precise conditions under which +.Dv STOP +and +START +characters are transmitted are implementation defined. +.Pp +If +.Dv IMAXBEL +is set and the input queue is full, subsequent input +shall causes an +.Tn ASCII +.Dv BEL +character to be transmitted to the +the output queue. +.Pp +The initial input control value after open() is implementation defined. +.Ss Output Modes +Values of the +.Fa c_oflag +field describe the basic terminal output control, +and are composed of the following masks: +.Pp +.Bl -tag -width OXTABS -offset indent -compact +.It Dv OPOST +/* enable following output processing */ +.It Dv ONLCR +/* map NL to CR-NL (ala +.Dv CRMOD) +*/ +.It Dv OXTABS +/* expand tabs to spaces */ +.It Dv ONOEOT +/* discard +.Dv EOT Ns 's +.Ql \&^D +on output) */ +.El +.Pp +If +.Dv OPOST +is set, the remaining flag masks are interpreted as follows; +otherwise characters are transmitted without change. +.Pp +If +.Dv ONLCR +is set, newlines are translated to carriage return, linefeeds. +.Pp +If +.Dv OXTABS +is set, tabs are expanded to the appropiate number of +spaces (assuming 8 column tab stops). +.Pp +If +.Dv ONOEOT +is set, +.Tn ASCII +.Dv EOT NS 's +are discarded on output. +.Ss Control Modes +Values of the +.Fa c_cflag +field describe the basic +terminal hardware control, and are composed of the +the following masks. +Not all values +specified are supported by all hardware. +.Pp +.Bl -tag -width CRTSXIFLOW -offset indent -compact +.It Dv CSIZE +/* character size mask */ +.It Dv CS5 +/* 5 bits (pseudo) */ +.It Dv CS6 +/* 6 bits */ +.It Dv CS7 +/* 7 bits */ +.It Dv CS8 +/* 8 bits */ +.It Dv CSTOPB +/* send 2 stop bits */ +.It Dv CREAD +/* enable receiver */ +.It Dv PARENB +/* parity enable */ +.It Dv PARODD +/* odd parity, else even */ +.It Dv HUPCL +/* hang up on last close */ +.It Dv CLOCAL +/* ignore modem status lines */ +.It Dv CCTS_OFLOW +/* +.Dv CTS +flow control of output */ +.It Dv CRTSCTS +/* same as +.Dv CCTS_OFLOW +*/ +.It Dv CRTS_IFLOW +/* RTS flow control of input */ +.It Dv MDMBUF +/* flow control output via Carrier */ +.El +.Pp +The +.Dv CSIZE +bits specify the byte size in bits for both transmission and +reception. The +.Fa c_cflag +is masked with +.Dv CSIZE +and compared with the +values +.Dv CS5 , +.Dv CS6 , +.Dv CS7 , +or +.Dv CS8 . +This size does not include the parity bit, if any. If +.Dv CSTOPB +is set, two stop bits are used, otherwise one stop bit. For example, at +110 baud, two stop bits are normally used. +.Pp +If +.Dv CREAD +is set, the receiver is enabled. Otherwise, no characters shall +be received. Not all hardware supports this bit. In fact, this flag +is pretty silly and if it were not part of the +.Nm termios +specification +it would be ommitted. +.Pp +If +.Dv PARENB +is set, parity generation and detection is enabled and a parity +bit is added to each character. If parity is enabled, +.Dv PARODD +specifies +odd parity if set, otherwise even parity is used. +.Pp +If +.Dv HUPCL +is set, the modem control lines for the port shall be lowered +when the last process with the port open closes the port or the process +terminates. The modem connection shall be broken. +.Pp +If +.Dv CLOCAL +is set, a connection does not depend on the state of the modem +status lines. If +.Dv CLOCAL +is clear, the modem status lines shall be +monitored. +.Pp +Under normal circumstances, a call to the open() function shall wait for +the modem connection to complete. However, if the +.Dv O_NONBLOCK +flag is set +or if +.Dv CLOCAL +has been set, the open() function shall return +immediately without waiting for the connection. +.Pp +The +.Dv CCTS_OFLOW +.Pf ( Dv CRTSCTS ) +flag is currently unused. +.Pp +If +.Dv MDMBUF +is set then output flow control is controlled by the state +of Carrier Detect. +.Pp +If the object for which the control modes are set is not an asynchronous +serial connection, some of the modes may be ignored; for example, if an +attempt is made to set the baud rate on a network connection to a +terminal on another host, the baud rate may or may not be set on the +connection between that terminal and the machine it is directly connected +to. +.Ss Local Modes +Values of the c_lflag field describe the control of +various functions, and are composed of the following +masks. +.Pp +.Bl -tag -width NOKERNINFO -offset indent -compact +.It Dv ECHOKE +/* visual erase for line kill */ +.It Dv ECHOE +/* visually erase chars */ +.It Dv ECHO +/* enable echoing */ +.It Dv ECHONL +/* echo +.Dv NL +even if +.Dv ECHO +is off */ +.It Dv ECHOPRT +/* visual erase mode for hardcopy */ +.It Dv ECHOCTL +/* echo control chars as ^(Char) */ +.It Dv ISIG +/* enable signals +.Dv INTR , +.Dv QUIT , +.Dv [D]SUSP +*/ +.It Dv ICANON +/* canonicalize input lines */ +.It Dv ALTWERASE +/* use alternate +.Dv WERASE +algorithm */ +.It Dv IEXTEN +/* enable +.Dv DISCARD +and +.Dv LNEXT +*/ +.It Dv EXTPROC +/* external processing */ +.It Dv TOSTOP +/* stop background jobs from output */ +.It Dv FLUSHO +/* output being flushed (state) */ +.It Dv NOKERNINFO +/* no kernel output from +.Dv VSTATUS +*/ +.It Dv PENDIN +/* XXX retype pending input (state) */ +.It Dv NOFLSH +/* don't flush after interrupt */ +.El +.Pp +If +.Dv ECHO +is set, input characters are echoed back to the terminal. If +.Dv ECHO +is not set, input characters are not echoed. +.Pp +If +.Dv ECHOE +and +.Dv ICANON +are set, the +.Dv ERASE +character shall cause the terminal +to erase the last character in the current line from the display, if +possible. If there is no character to erase, an implementation may echo +an indication that this was the case or do nothing. +.Pp +If +.Dv ECHOK +and +.Dv ICANON +are set, the +.Dv KILL +character shall +cause the current line to be discarded and the system shall +echo the +.Ql \&\en +character after the +.Dv KILL +character. +.Pp +If +.Dv ECHOKE +and +.Dv ICANON +are set, the +.Dv KILL +character shall cause +the current line to be discarded and the system shall cause +the terminal +to erase the line from the display. +.Pp +If +.Dv ECHOPRT +and +.Dv ICANON +are set, the system shall assume +that the display is a printing device and shall print a +backslash and the erased characters when processing +.Dv ERASE +characters, followed by a forward slash. +.Pp +If +.Dv ECHOCTL +is set, the system shall echo control characters +in a visible fashion using a carrot followed by the control character. +.Pp +If +.Dv ALTWERASE +is set, the system will use an alternative algorithm +for determining what constitutes a word when processing +.Dv WERASE +characters (see +.Dv WERASE ) . +.Pp +If +.Dv ECHONL +and +.Dv ICANON +are set, the +.Ql \&\en +character shall be echoed even if +.Dv ECHO +is not set. +.Pp +If +.Dv ICANON +is set, canonical processing is enabled. This enables the +erase and kill edit functions, and the assembly of input characters into +lines delimited by +.Dv NL, +.Dv EOF , +and +.Dv EOL, +as described in +.Sx "Canonical Mode Input Processing" . +.Pp +If +.Dv ICANON +is not set, read requests are satisfied directly from the input +queue. A read shall not be satisfied until at least +.Dv MIN +bytes have been +received or the timeout value +.Dv TIME +expired between bytes. The time value +represents tenths of seconds. See +.Sx "Noncanonical Mode Input Processing" +for more details. +.Pp +If +.Dv ISIG +is set, each input character is checked against the special +control characters +.Dv INTR , +.Dv QUIT , +and +.Dv SUSP +(job control only). If an input +character matches one of these control characters, the function +associated with that character is performed. If +.Dv ISIG +is not set, no +checking is done. Thus these special input functions are possible only +if +.Dv ISIG +is set. +.Pp +If +.Dv IEXTEN +is set, implementation-defined functions shall be recognized +from the input data. It is implementation defined how +.Dv IEXTEN +being set +interacts with +.Dv ICANON , +.Dv ISIG , +.Dv IXON , +or +.Dv IXOFF . +If +.Dv IEXTEN +is not set, then +implementation-defined functions shall not be recognized, and the +corresponding input characters shall be processed as described for +.Dv ICANON , +.Dv ISIG , +.Dv IXON , +and +.Dv IXOFF . +.Pp +If +.Dv NOFLSH +is set, the normal flush of the input and output queues +associated with the +.Dv INTR , +.Dv QUIT , +and +.Dv SUSP +characters +shall not be done. +.Pp +If +.Dv TOSTOP +is set, the signal +.Dv SIGTTOU +is sent to the process group of a process that tries to write to +its controlling terminal if it is not in the foreground process group for +that terminal. This signal, by default, stops the members of the process +group. Otherwise, the output generated by that process is output to the +current output stream. Processes that are blocking or ignoring +.Dv SIGTTOU +signals are excepted and allowed to produce output and the +.Dv SIGTTOU +signal +is not sent. +.Pp +If +.Dv NOKERNINFO +is set, the kernel shall not produce a status message +when processing +.Dv STATUS +characters (see +.Dv STATUS ) . +.Ss Special Control Characters +The special control characters values are defined by the array c_cc. +This table lists the array index, the corresponding special character, +and the system default value. For an accurate list of +the system defaults, consult the header file +.Aq Pa ttydefaults.h . +.Pp +.Bl -column "Index Name" "Special Character" -offset indent -compact +.It Em "Index Name Special Character Default Value" +.It Dv VEOF Ta EOF Ta \&^D +.It Dv VEOL Ta EOL Ta _POSIX_VDISABLE +.It Dv VEOL2 Ta EOL2 Ta _POSIX_VDISABLE +.It Dv VERASE Ta ERASE Ta \&^? Ql \&\e177 +.It Dv VWERASE Ta WERASE Ta \&^W +.It Dv VKILL Ta KILL Ta \&^U +.It Dv VREPRINT Ta REPRINT Ta \&^R +.It Dv VINTR Ta INTR Ta \&^C +.It Dv VQUIT Ta QUIT Ta \&^\e\e Ql \&\e34 +.It Dv VSUSP Ta SUSP Ta \&^Z +.It Dv VDSUSP Ta DSUSP Ta \&^Y +.It Dv VSTART Ta START Ta \&^Q +.It Dv VSTOP Ta STOP Ta \&^S +.It Dv VLNEXT Ta LNEXT Ta \&^V +.It Dv VDISCARD Ta DISCARD Ta \&^O +.It Dv VMIN Ta --- Ta \&1 +.It Dv VTIME Ta --- Ta \&0 +.It Dv VSTATUS Ta STATUS Ta \&^T +.El +.Pp +If the +value of one of the changeable special control characters (see +.Sx "Special Characters" ) +is +.Dv {_POSIX_VDISABLE} , +that function shall be disabled; that is, no input +data shall be recognized as the disabled special character. +If +.Dv ICANON +is +not set, the value of +.Dv {_POSIX_VDISABLE} +has no special meaning for the +.Dv VMIN +and +.Dv VTIME +entries of the +.Fa c_cc +array. +.Pp +The initial values of the flags and control characters +after open() is set according to +the values in the header +.Aq Pa sys/ttydefaults.h . diff --git a/share/man/man5/Makefile b/share/man/man5/Makefile index 1b89b4c391..4fe2321078 100644 --- a/share/man/man5/Makefile +++ b/share/man/man5/Makefile @@ -1,9 +1,12 @@ # @(#)Makefile 0.1 (RWGrimes) 3/25/93 # Clean up and added pcfs, humm should pcfs be a subdir i386? -MAN5= a.out.0 acct.0 core.0 dir.0 disktab.0 fs.0 fstab.0 group.0 \ - hosts.0 networks.0 passwd.0 pcfs.0 phones.0 printcap.0 protocols.0 \ - remote.0 resolver.0 services.0 shells.0 stab.0 types.0 utmp.0 -MLINKS= fs.5 inode.5 utmp.5 wtmp.5 +MAN5= a.out.5 acct.5 core.5 dir.5 disktab.5 \ + fs.5 fstab.5 group.5 hosts.5 networks.5 \ + passwd.5 pcfs.5 phones.5 printcap.5 \ + protocols.5 remote.5 resolver.5 services.5 \ + shells.5 stab.5 types.5 utmp.5 + +MLINKS= fs.5 inode.5 utmp.5 wtmp.5 utmp.5 lastlog.5 .include diff --git a/share/man/man5/core.5 b/share/man/man5/core.5 index 6df18aaf8c..20b1eb91f2 100644 --- a/share/man/man5/core.5 +++ b/share/man/man5/core.5 @@ -46,11 +46,15 @@ to disk for later examination by one of the aviailable debuggers. (See .Xr sigaction 2 . ) This memory image is written to a file named -.Nm core -in the working directory; +.Nm progname.core +in the working directory, where progname is replaced by the name of the program; provided the terminated process had write permission in the directory, -and provided the abnormality did not caused -a system crash. +the filesystem on which the working directory is on was not mounted +with the option +.Em nocore +(See +.Xr mount 8 ), +and provided the abnormality did not cause a system crash. (In this event, the decision to save the core file is arbitrary, see .Xr savecore 8 . ) .Pp @@ -59,6 +63,9 @@ The maximum size of a file is limited by .Xr setrlimit 2 . Files which would be larger than the limit are not created. +Also, the kernel tries hard to not overwrite an existing file unless +it is in standard coredump format and was produced from the same +program. .Pp The .Nm core diff --git a/share/man/man7/Makefile b/share/man/man7/Makefile index a62fa261a3..4d7a89accd 100644 --- a/share/man/man7/Makefile +++ b/share/man/man7/Makefile @@ -1,9 +1,10 @@ # @(#)Makefile 0.1 (RWGrimes) 3/25/93 # -# Missing: term.0 +# Missing: term.7 + +MAN7= ascii.7 environ.7 hier.7 hostname.7 intro.7 \ + mailaddr.7 mdoc.samples.7 operator.7 -MAN7= ascii.0 environ.0 hier.0 hostname.0 intro.0 mailaddr.0 \ - mdoc.samples.0 operator.0 MLINKS= intro.7 miscellaneous.7 .include diff --git a/share/man/man7/hier.7 b/share/man/man7/hier.7 index d855af965d..81e57a40f6 100644 --- a/share/man/man7/hier.7 +++ b/share/man/man7/hier.7 @@ -368,7 +368,7 @@ commonly ~uucp; public uucp temporary directory .It Li tmp/ temporary files that are kept between system reboots .El -.It Li /vmunix +.It Li /386bsd pure kernel executable (the operating system loaded into memory at boot time). .El diff --git a/share/man/man7/mdoc.samples.7 b/share/man/man7/mdoc.samples.7 index cf5b0bdc83..6239139300 100644 --- a/share/man/man7/mdoc.samples.7 +++ b/share/man/man7/mdoc.samples.7 @@ -40,12 +40,12 @@ .Os BSD 4.4 .Dt MDOC.SAMPLES 7 .Sh NAME -.Nm mdoc.sample +.Nm mdoc.samples .Nd writing manual pages with .Nm -mdoc macro package .Sh SYNOPSIS -.Nm man mdoc.sample +.Nm man mdoc.samples .Sh DESCRIPTION A tutorial sampler for writing .Bx @@ -1172,9 +1172,9 @@ to it can not recall the first argument it was invoked with. .Pp .Dl Usage: .Nm argument ... \*(Pu -.Bl -tag -width ".Nm mdoc.sample" -compact -offset 14n -.It Li \&.Nm mdoc.sample -.Nm mdoc.sample +.Bl -tag -width ".Nm mdoc.samples" -compact -offset 14n +.It Li \&.Nm mdoc.samples +.Nm mdoc.samples .It Li \&.Nm \-mdoc .Nm \-mdoc . .It Li \&.Nm foo\ )\ )\ , diff --git a/share/man/man8/Makefile b/share/man/man8/Makefile index a669694ef8..1eba015b03 100644 --- a/share/man/man8/Makefile +++ b/share/man/man8/Makefile @@ -2,7 +2,7 @@ # # Not for 386bsd: man4.hp300 man4.tahoe man4.vax -MAN8= adduser.0 intro.0 makedev.0 rc.0 sticky.0 +MAN8= adduser.8 intro.8 makedev.8 rc.8 sticky.8 SUBDIR= man8.i386 .include diff --git a/share/man/man8/man8.i386/Makefile b/share/man/man8/man8.i386/Makefile index de13225587..89c8c4a4a0 100644 --- a/share/man/man8/man8.i386/Makefile +++ b/share/man/man8/man8.i386/Makefile @@ -1,6 +1,8 @@ # @(#)Makefile 0.1 (RWGrimes) 3/25/93 -MAN8= MAKEDEV.0 +MAN8= MAKEDEV.8 MANSUBDIR=/i386 +MLINKS= MAKEDEV.8 ../MAKEDEV.8 + .include diff --git a/share/me/Makefile b/share/me/Makefile index 9e237e8ef1..965b36555d 100644 --- a/share/me/Makefile +++ b/share/me/Makefile @@ -6,7 +6,7 @@ MESRCS= acm.me chars.me deltext.me eqn.me float.me footnote.me \ index.me letterhead.me local.me null.me refer.me sh.me \ tbl.me thesis.me TMSRCS= tmac.e -MAN7= me.0 +MAN7= me.7 beforeinstall: cd ${.CURDIR}; for i in ${MESRCS}; do \ diff --git a/share/me/footnote.me b/share/me/footnote.me index 9e5a8a3c31..02c0b2dc3e 100644 --- a/share/me/footnote.me +++ b/share/me/footnote.me @@ -101,7 +101,7 @@ . ch @f -\\n(_bu . if \\n(.p-\\n(_b<=\\n(nl \ . ch @f \\n(nlu+\n(.Vu -. nr dn _D +. nr dn \\n(_D . rr _D .\} .el \ diff --git a/share/me/me.7 b/share/me/me.7 index 12b561df22..a9ec5bc9a3 100644 --- a/share/me/me.7 +++ b/share/me/me.7 @@ -85,9 +85,9 @@ and preprocessors for equations and tables is acceptable as input. .SH FILES -/usr/lib/tmac/tmac.e +/usr/share/tmac/tmac.e .br -/usr/lib/me/* +/usr/share/me/* .SH "SEE ALSO" eqn(1), troff(1), refer(1), tbl(1) .br diff --git a/share/misc/zipcodes b/share/misc/zipcodes index 0db5be5761..44fbd70cb7 100644 --- a/share/misc/zipcodes +++ b/share/misc/zipcodes @@ -40555,7 +40555,7 @@ 96328:APO San Francisco, CA 96331:APO San Francisco, CA 96333:APO San Francisco, CA -96334:APO San Francisco, GU +96334:APO San Francisco, CA 96335:APO San Francisco, CA 96336:APO San Francisco, CA 96343:APO San Francisco, CA diff --git a/share/mk/bsd.README b/share/mk/bsd.README index e8752d90bc..cfe2fdc776 100644 --- a/share/mk/bsd.README +++ b/share/mk/bsd.README @@ -2,8 +2,7 @@ This is the README file for the new make "include" files for the BSD source tree. The files are installed in /usr/share/mk, and are, by -convention, named with the suffix ".mk". Each ".mk" file has a -corresponding ".rd" file which is an explanation of the ".mk" file. +convention, named with the suffix ".mk". Note, this file is not intended to replace reading through the .mk files for anything tricky. @@ -66,7 +65,8 @@ environment or otherwise. You probably don't want to touch this file. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= The include file handles installing manual pages and their -links. +links. If you do not wish to install man pages, set the NOMAN environment +variable. It has a single target: @@ -86,7 +86,7 @@ MANMODE Manual mode. MANSUBDIR Subdirectory under the manual page section, i.e. "/vax" or "/tahoe" for machine specific manual pages. -MAN1 ... MAN8 The manual pages to be installed (use a .0 suffix). +MAN1 ... MAN8 The manual pages to be installed (use a .1 - .8 suffix). MLINKS List of manual page links (using a .1 - .8 suffix). The linked-to file must come first, the linked file second, @@ -113,13 +113,13 @@ BINMODE Binary mode. STRIP The flag passed to the install program to cause the binary to be stripped. This is to be used when building your own install script so that the entire system can be made - stripped/not-stripped using a single nob. + stripped/not-stripped using a single global variable COPY The flag passed to the install program to cause the binary to be copied rather than moved. This is to be used when - building our own install script so that the entire system - can either be installed with copies, or with moves using - a single nob. + building your own install script so that the entire system + can either be installed with copies or moves using a single + global variable. MANDIR Base path for manual installation. @@ -147,7 +147,7 @@ It has seven targets: Errs, errs, mklog, and core. cleandir: remove all of the files removed by the target clean, as - well as .depend, tags, and any manual pages. + well as .depend, tags, obj, and any manual pages. depend: make the dependencies for the source files, and store them in the file .depend. @@ -193,8 +193,8 @@ LINKS The list of binary links; should be full pathnames, the LINKS= ${DESTDIR}/bin/test ${DESTDIR}/bin/[ -MAN1...MAN8 Manual pages (should end in .0). If no MAN variable is - defined, "MAN1=${PROG}.0" is assumed. +MAN1...MAN8 Manual pages (should end in .[1-8]). If no MAN variable is + defined, "MAN1=${PROG}.1" is assumed. PROG The name of the program to build. If not supplied, nothing is built. @@ -202,6 +202,10 @@ PROG The name of the program to build. If not supplied, nothing SRCS List of source files to build the program. If PROG is not defined, it's assumed to be ${PROG}.c. +DPSRCS List of source files that must exist before compiling the + program source files. Usually for a building a configuration + file that is required by all sources. + DPADD Additional dependencies for the program. Usually used for libraries. For example, to depend on the compatibility and utility libraries use: @@ -213,6 +217,7 @@ DPADD Additional dependencies for the program. Usually used for LIBC /lib/libc.a LIBCOMPAT /usr/lib/libcompat.a LIBCURSES /usr/lib/libcurses.a + LIBCRYPT /usr/lib/libcrypt.a LIBDBM /usr/lib/libdbm.a LIBDES /usr/lib/libdes.a LIBL /usr/lib/libl.a @@ -223,6 +228,7 @@ DPADD Additional dependencies for the program. Usually used for LIBPC /usr/lib/libpc.a LIBPLOT /usr/lib/libplot.a LIBRPC /usr/lib/sunrpc.a + LIBTELNET /usr/lib/libtelnet.a LIBTERM /usr/lib/libterm.a LIBUTIL /usr/lib/libutil.a @@ -249,7 +255,7 @@ To build foo from foo.c with a manual page foo.1, use: To build foo from foo.c with a manual page foo.2, add the line: - MAN2= foo.0 + MAN2= foo.2 If foo does not have a manual page at all, add the line: @@ -290,7 +296,7 @@ LIBMODE Library mode. LDADD Additional loader objects. -MAN1 ... MAN8 The manual pages to be installed (use a .0 suffix). +MAN1 ... MAN8 The manual pages to be installed (use a .[1-8] suffix). SRCS List of source files to build the library. Suffix types .s, .c, and .f are supported. Note, .s files are preferred @@ -301,6 +307,6 @@ The include file includes the file named "../Makefile.inc" if it exists, as well as the include file . It has rules for building profiled objects; profiled libraries are -built by default. +built by default, unless the NOPROFILE environment variable is set. Libraries are ranlib'd before installation. diff --git a/share/mk/bsd.doc.mk b/share/mk/bsd.doc.mk index f41cc17684..66d3891039 100644 --- a/share/mk/bsd.doc.mk +++ b/share/mk/bsd.doc.mk @@ -1,36 +1,80 @@ -# @(#)bsd.doc.mk 5.3 (Berkeley) 1/2/91 +# from: @(#)bsd.doc.mk 5.3 (Berkeley) 1/2/91 +# $Id: bsd.doc.mk,v 1.4 1993/10/10 18:50:58 rgrimes Exp $ -PRINTER=psc +PRINTER?= ps BIB?= bib -EQN?= deqn -P${PRINTER} -GREMLIN?= grn -P${PRINTER} +EQN?= eqn -T${PRINTER} +GREMLIN?= grn GRIND?= vgrind -f INDXBIB?= indxbib -PIC?= pic -P${PRINTER} +PIC?= pic REFER?= refer -ROFF?= ditroff -t ${MACROS} ${PAGES} -P${PRINTER} +ROFF?= groff -T${PRINTER} ${MACROS} -o${PAGES} SOELIM?= soelim -TBL?= dtbl -P${PRINTER} +TBL?= tbl + +PAGES?= 1- + +# Compatibility mode flag for groff. Use this when formatting documents with +# Berkeley me macros. +COMPAT?= -C .PATH: ${.CURDIR} +all: ${DOC}.${PRINTER} + .if !target(print) -print: paper.${PRINTER} - lpr -P${PRINTER} paper.${PRINTER} +print: ${DOC}.${PRINTER} + lpr -P${PRINTER} ${DOC}.${PRINTER} +.endif + +.if !target(obj) +.if defined(NOOBJ) +obj: +.else +obj: + @cd ${.CURDIR}; rm -f obj > /dev/null 2>&1 || true; \ + here=`pwd`; subdir=`echo $$here | sed 's,^/usr/src/,,'`; \ + if test $$here != $$subdir ; then \ + dest=/usr/obj/$$subdir ; \ + echo "$$here -> $$dest"; ln -s $$dest obj; \ + if test -d /usr/obj -a ! -d $$dest; then \ + mkdir -p $$dest; \ + else \ + true; \ + fi; \ + else \ + true ; \ + dest=$$here/obj ; \ + echo "making $$here/obj" ; \ + if test ! -d obj ; then \ + mkdir $$here/obj; \ + fi ; \ + fi; +.endif .endif clean cleandir: - rm -f paper.* [eE]rrs mklog ${CLEANFILES} + rm -f ${DOC}.* [eE]rrs mklog ${CLEANFILES} rm -rf obj FILES?= ${SRCS} install: - install ${COPY} -o ${BINOWN} -g ${BINGRP} -m 444 \ - Makefile ${FILES} ${EXTRA} ${DESTDIR}${BINDIR}/${DIR} + @if [ ! -d "${DESTDIR}${BINDIR}/${DIR}" ]; then \ + /bin/rm -f ${DESTDIR}${BINDIR}/${DIR} ; \ + mkdir -p ${DESTDIR}${BINDIR}/${DIR} ; \ + chown root.wheel ${DESTDIR}${BINDIR}/${DIR} ; \ + chmod 755 ${DESTDIR}${BINDIR}/${DIR} ; \ + else \ + true ; \ + fi + ( cd ${.CURDIR} ; install ${COPY} -o ${BINOWN} -g ${BINGRP} -m 444 \ + Makefile ${FILES} ${EXTRA} ${DESTDIR}${BINDIR}/${DIR} ) spell: ${SRCS} - spell ${SRCS} | sort | comm -23 - spell.ok > paper.spell + (cd ${.CURDIR}; spell ${SRCS} ) | sort | \ + comm -23 - ${.CURDIR}/spell.ok > ${DOC}.spell BINDIR?= /usr/share/doc BINGRP?= bin diff --git a/share/mk/bsd.lib.mk b/share/mk/bsd.lib.mk index 44dbeed194..e20871af40 100644 --- a/share/mk/bsd.lib.mk +++ b/share/mk/bsd.lib.mk @@ -1,12 +1,58 @@ # @(#)bsd.lib.mk 5.26 (Berkeley) 5/2/91 # -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 1 00157 -# -------------------- ----- ---------------------- +# $Log: bsd.lib.mk,v $ +# Revision 1.11 1993/08/15 01:27:28 nate +# 1) Finishedup my DPSRCS fixes from way back. Now any .h files will +# automatically be depended and stripped out of the SRCS line +# (I also left the external definition as well, in case of non-src +# dependencies) +# +# 2) Cleaned up some of the clean/cleandirs to have a more pleasing format +# (rm -f on every line rather than line-continuations) +# +# 3) Added Charles Hannum's dependency fixes for a cleaner make depend that +# works for both c/c++ files +# +# 4) Added default targets for (file.cc|cxx|C) -> file.o in the all +# affected make macros. However, these as well as Charles c++ +# dependency fixes are commented out so that groff won't be broken. +# I'll uncomment them after further testing on my box and seeing if +# groff should be modified rather than relying on gcc2 doing the right +# thing (subject to group vote) +# +# Revision 1.10 1993/08/11 03:15:20 alm +# added rules .f.po (and .f.o) from Jonas. +# +# Revision 1.9 1993/08/05 18:45:53 nate +# Removed the ranlib statements from before the install (since it's done +# after the install as well), and changed ranlib -> ${RANLIB} +# +# Revision 1.8 1993/08/03 20:57:34 nate +# Fixed macros so that you can do a +# make maninstall at all times and have it not blow up +# +# Revision 1.7 1993/07/23 20:44:38 nate +# Fixed a boo-boo and made the NOMAN environment variable work correctly with +# both programs and libraries. +# +# Revision 1.6 1993/07/09 00:38:35 jkh +# Removed $History$ line from hell (no leading #). +# +# Revision 1.5 1993/07/08 12:17:07 paul +# Removed the core.* before disaster strikes. +# I removed core as well since it's pretty redundant. +# +# Revision 1.4 1993/07/07 21:42:45 nate +# Cleaned up header files and added core.* to clean directives +# +# Revision 1.3 1993/07/02 06:44:30 root +# New manual page system +# +# Revision 1.2 1993/06/17 02:01:11 rgrimes +# Make clean in src/lib/libc failed due to too many arguments to /bin/sh, +# this was fixed for make cleandir in the patchkit, this fixes it for +# make clean. # -# 27 Apr 93 Rodney W. Grimes Break up cleandir so that we do not -# overflow shell args .if exists(${.CURDIR}/../Makefile.inc) .include "${.CURDIR}/../Makefile.inc" @@ -27,11 +73,9 @@ BINMODE?= 555 .MAIN: all # prefer .s to a .c, add .po, remove stuff not used in the BSD libraries +#.SUFFIXES: .out .o .po .s .c .cc .cxx .C .f .y .l .SUFFIXES: -.SUFFIXES: .out .o .po .s .c .f .y .l .8 .7 .6 .5 .4 .3 .2 .1 .0 - -.8.0 .7.0 .6.0 .5.0 .4.0 .3.0 .2.0 .1.0: - nroff -mandoc ${.IMPSRC} > ${.TARGET} +.SUFFIXES: .out .o .po .s .c .f .y .l .c.o: ${CC} ${CFLAGS} -c ${.IMPSRC} @@ -43,6 +87,26 @@ BINMODE?= 555 @${LD} -X -r ${.TARGET} @mv a.out ${.TARGET} +#.cc.o .cxx.o .C.o: +# ${CXX} ${CXXFLAGS} -c ${.IMPSRC} +# @${LD} -x -r ${.TARGET} +# @mv a.out ${.TARGET} + +#.cc.po .C.po .cxx.o: +# ${CXX} -p ${CXXFLAGS} -c ${.IMPSRC} -o ${.TARGET} +# @${LD} -X -r ${.TARGET} +# @mv a.out ${.TARGET} + +.f.o: + ${FC} ${RFLAGS} -o ${.TARGET} -c ${.IMPSRC} + @${LD} -x -r ${.TARGET} + @mv a.out ${.TARGET} + +.f.po: + ${FC} -p ${RFLAGS} -o ${.TARGET} -c ${.IMPSRC} + @${LD} -X -r ${.TARGET} + @mv a.out ${.TARGET} + .s.o: ${CPP} -E ${CFLAGS:M-[ID]*} ${AINC} ${.IMPSRC} | \ ${AS} -o ${.TARGET} @@ -55,45 +119,44 @@ BINMODE?= 555 @${LD} -X -r ${.TARGET} @mv a.out ${.TARGET} -MANALL= ${MAN1} ${MAN2} ${MAN3} ${MAN4} ${MAN5} ${MAN6} ${MAN7} ${MAN8} - .if !defined(NOPROFILE) _LIBS=lib${LIB}.a lib${LIB}_p.a .else _LIBS=lib${LIB}.a .endif -all: ${_LIBS} ${MANALL}# llib-l${LIB}.ln +all: ${_LIBS} # llib-l${LIB}.ln -OBJS+= ${SRCS:R:S/$/.o/g} +OBJS+= ${SRCS:N*.h:R:S/$/.o/g} lib${LIB}.a:: ${OBJS} @echo building standard ${LIB} library @rm -f lib${LIB}.a @${AR} cTq lib${LIB}.a `lorder ${OBJS} | tsort` ${LDADD} - ranlib lib${LIB}.a + ${RANLIB} lib${LIB}.a POBJS+= ${OBJS:.o=.po} lib${LIB}_p.a:: ${POBJS} @echo building profiled ${LIB} library @rm -f lib${LIB}_p.a @${AR} cTq lib${LIB}_p.a `lorder ${POBJS} | tsort` ${LDADD} - ranlib lib${LIB}_p.a + ${RANLIB} lib${LIB}_p.a llib-l${LIB}.ln: ${SRCS} ${LINT} -C${LIB} ${CFLAGS} ${.ALLSRC:M*.c} .if !target(clean) clean: - rm -f a.out Errs errs mklog core ${CLEANFILES} ${OBJS} ${POBJS} \ - ${MANALL} profiled/*.o lib${LIB}.a lib${LIB}_p.a llib-l${LIB}.ln + rm -f a.out Errs errs mklog ${CLEANFILES} ${OBJS} + rm -f lib${LIB}.a llib-l${LIB}.ln + rm -f ${POBJS} profiled/*.o lib${LIB}_p.a .endif .if !target(cleandir) cleandir: - rm -f a.out Errs errs mklog core ${CLEANFILES} ${OBJS} \ - lib${LIB}.a llib-l${LIB}.ln \ - ${MANALL} ${.CURDIR}/tags .depend + rm -f a.out Errs errs mklog ${CLEANFILES} ${OBJS} + rm -f lib${LIB}.a llib-l${LIB}.ln + rm -f ${.CURDIR}/tags .depend rm -f ${POBJS} profiled/*.o lib${LIB}_p.a cd ${.CURDIR}; rm -rf obj; .endif @@ -101,9 +164,17 @@ cleandir: .if !target(depend) depend: .depend .depend: ${SRCS} - mkdep ${CFLAGS:M-[ID+]*} ${AINC} ${.ALLSRC} + rm -f .depend + files="${.ALLSRC:M*.c}"; \ + if [ "$$files" != "" ]; then \ + mkdep -a ${MKDEP} ${CFLAGS:M-[ID]*} $$files; \ + fi +# files="${.ALLSRC:M*.cc} ${.ALLSRC:M*.C} ${.ALLSRC:M*.cxx}"; \ +# if [ "$$files" != " " ]; then \ +# mkdep -a ${MKDEP} -+ ${CXXFLAGS:M-[ID]*} $$files; \ +# fi @(TMP=/tmp/_depend$$$$; \ - sed -e 's/^\([^\.]*\).o:/\1.o \1.po:/' < .depend > $$TMP; \ + sed -e 's/^\([^\.]*\).o[ ]*:/\1.o \1.po:/' < .depend > $$TMP; \ mv $$TMP .depend) .endif @@ -113,12 +184,10 @@ beforeinstall: .endif realinstall: beforeinstall - ranlib lib${LIB}.a install ${COPY} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} lib${LIB}.a \ ${DESTDIR}${LIBDIR} ${RANLIB} -t ${DESTDIR}${LIBDIR}/lib${LIB}.a .if !defined(NOPROFILE) - ranlib lib${LIB}_p.a install ${COPY} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ lib${LIB}_p.a ${DESTDIR}${LIBDIR} ${RANLIB} -t ${DESTDIR}${LIBDIR}/lib${LIB}_p.a @@ -139,7 +208,11 @@ realinstall: beforeinstall .endif install: afterinstall +.if !defined(NOMAN) afterinstall: realinstall maninstall +.else +afterinstall: realinstall +.endif .endif .if !target(lint) @@ -152,7 +225,12 @@ tags: ${SRCS} sed "s;\${.CURDIR}/;;" > tags .endif +.if !defined(NOMAN) .include +.elif !target(maninstall) +maninstall: +.endif + .if !target(obj) .if defined(NOOBJ) obj: diff --git a/share/mk/bsd.man.mk b/share/mk/bsd.man.mk index a68fbcd616..8abe4d3c2d 100644 --- a/share/mk/bsd.man.mk +++ b/share/mk/bsd.man.mk @@ -8,49 +8,51 @@ MANGRP?= bin MANOWN?= bin MANMODE?= 444 -MANDIR?= /usr/share/man/cat - +MANDIR?= /usr/share/man/man +MANSRC?= ${.CURDIR} MINSTALL= install ${COPY} -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} maninstall: .if defined(MAN1) && !empty(MAN1) - ${MINSTALL} ${MAN1} ${DESTDIR}${MANDIR}1${MANSUBDIR} + (cd ${MANSRC}; ${MINSTALL} ${MAN1} ${DESTDIR}${MANDIR}1${MANSUBDIR}) .endif .if defined(MAN2) && !empty(MAN2) - ${MINSTALL} ${MAN2} ${DESTDIR}${MANDIR}2${MANSUBDIR} + (cd ${MANSRC}; ${MINSTALL} ${MAN2} ${DESTDIR}${MANDIR}2${MANSUBDIR}) .endif .if defined(MAN3) && !empty(MAN3) - ${MINSTALL} ${MAN3} ${DESTDIR}${MANDIR}3${MANSUBDIR} + (cd ${MANSRC}; ${MINSTALL} ${MAN3} ${DESTDIR}${MANDIR}3${MANSUBDIR}) .endif .if defined(MAN3F) && !empty(MAN3F) - ${MINSTALL} ${MAN3F} ${DESTDIR}${MANDIR}3f${MANSUBDIR} + (cd ${MANSRC}; ${MINSTALL} ${MAN3F} ${DESTDIR}${MANDIR}3f${MANSUBDIR}) .endif .if defined(MAN4) && !empty(MAN4) - ${MINSTALL} ${MAN4} ${DESTDIR}${MANDIR}4${MANSUBDIR} + (cd ${MANSRC}; ${MINSTALL} ${MAN4} ${DESTDIR}${MANDIR}4${MANSUBDIR}) .endif .if defined(MAN5) && !empty(MAN5) - ${MINSTALL} ${MAN5} ${DESTDIR}${MANDIR}5${MANSUBDIR} + (cd ${MANSRC}; ${MINSTALL} ${MAN5} ${DESTDIR}${MANDIR}5${MANSUBDIR}) .endif .if defined(MAN6) && !empty(MAN6) - ${MINSTALL} ${MAN6} ${DESTDIR}${MANDIR}6${MANSUBDIR} + (cd ${MANSRC}; ${MINSTALL} ${MAN6} ${DESTDIR}${MANDIR}6${MANSUBDIR}) .endif .if defined(MAN7) && !empty(MAN7) - ${MINSTALL} ${MAN7} ${DESTDIR}${MANDIR}7${MANSUBDIR} + (cd ${MANSRC}; ${MINSTALL} ${MAN7} ${DESTDIR}${MANDIR}7${MANSUBDIR}) .endif .if defined(MAN8) && !empty(MAN8) - ${MINSTALL} ${MAN8} ${DESTDIR}${MANDIR}8${MANSUBDIR} + (cd ${MANSRC}; ${MINSTALL} ${MAN8} ${DESTDIR}${MANDIR}8${MANSUBDIR}) .endif .if defined(MLINKS) && !empty(MLINKS) @set ${MLINKS}; \ while test $$# -ge 2; do \ name=$$1; \ shift; \ - dir=${DESTDIR}${MANDIR}`expr $$name : '[^\.]*\.\(.*\)'`; \ - l=$${dir}${MANSUBDIR}/`expr $$name : '\([^\.]*\)'`.0; \ + sect=`expr $$name : '.*\.\([^.]*\)'`; \ + dir=${DESTDIR}${MANDIR}$$sect; \ + l=$${dir}${MANSUBDIR}/$$name; \ name=$$1; \ shift; \ - dir=${DESTDIR}${MANDIR}`expr $$name : '[^\.]*\.\(.*\)'`; \ - t=$${dir}${MANSUBDIR}/`expr $$name : '\([^\.]*\)'`.0; \ + sect=`expr $$name : '.*\.\([^.]*\)'`; \ + dir=${DESTDIR}${MANDIR}$$sect; \ + t=$${dir}${MANSUBDIR}/$$name; \ echo $$t -\> $$l; \ rm -f $$t; \ ln $$l $$t; \ diff --git a/share/mk/bsd.own.mk b/share/mk/bsd.own.mk index 7ebe01496b..78b9a468b3 100644 --- a/share/mk/bsd.own.mk +++ b/share/mk/bsd.own.mk @@ -8,7 +8,7 @@ STRIP?= -s COPY?= -c -MANDIR?= /usr/share/man/cat +MANDIR?= /usr/share/man/man MANGRP?= bin MANOWN?= bin MANMODE?= 444 diff --git a/share/mk/bsd.prog.mk b/share/mk/bsd.prog.mk index 41a1c62d33..0c569a83f9 100644 --- a/share/mk/bsd.prog.mk +++ b/share/mk/bsd.prog.mk @@ -4,10 +4,8 @@ .include "${.CURDIR}/../Makefile.inc" .endif -.SUFFIXES: .out .o .c .y .l .s .8 .7 .6 .5 .4 .3 .2 .1 .0 - -.8.0 .7.0 .6.0 .5.0 .4.0 .3.0 .2.0 .1.0: - nroff -mandoc ${.IMPSRC} > ${.TARGET} +#.SUFFIXES: .out .o .c .cc .cxx .C .y .l .s +.SUFFIXES: .out .o .c .y .l .s CFLAGS+=${COPTS} @@ -20,9 +18,12 @@ BINMODE?= 555 LIBCRT0?= /usr/lib/crt0.o LIBC?= /usr/lib/libc.a LIBCOMPAT?= /usr/lib/libcompat.a +LIBCRYPT?= /usr/lib/libcrypt.a LIBCURSES?= /usr/lib/libcurses.a LIBDBM?= /usr/lib/libdbm.a LIBDES?= /usr/lib/libdes.a +LIBGNUMALLOC?= /usr/lib/libgnumalloc.a +LIBGNUREGEX?= /usr/lib/libgnuregex.a LIBL?= /usr/lib/libl.a LIBKDB?= /usr/lib/libkdb.a LIBKRB?= /usr/lib/libkrb.a @@ -31,7 +32,9 @@ LIBMP?= /usr/lib/libmp.a LIBPC?= /usr/lib/libpc.a LIBPLOT?= /usr/lib/libplot.a LIBRESOLV?= /usr/lib/libresolv.a -LIBRPC?= /usr/lib/sunrpc.a +LIBRPC?= /usr/lib/librpc.a +LIBRPCSVC?= /usr/lib/librpcsvc.a +LIBTELNET?= /usr/lib/libtelnet.a LIBTERM?= /usr/lib/libterm.a LIBUTIL?= /usr/lib/libutil.a @@ -41,21 +44,28 @@ CLEANFILES+=strings ${CC} -E ${CFLAGS} ${.IMPSRC} | xstr -c - @${CC} ${CFLAGS} -c x.c -o ${.TARGET} @rm -f x.c + +#.cc.o .cxx.o .C.o: +# ${CXX} -E ${CXXFLAGS} ${.IMPSRC} | xstr -c - +# @mv -f x.c x.cc +# @${CXX} ${CXXFLAGS} -c x.cc -o ${.TARGET} + .endif .if defined(PROG) .if defined(SRCS) -OBJS+= ${SRCS:R:S/$/.o/g} +DPSRCS+= ${SRCS:M*.h} +OBJS+= ${SRCS:N*.h:R:S/$/.o/g} .if defined(LDONLY) -${PROG}: ${LIBCRT0} ${LIBC} ${OBJS} ${DPADD} +${PROG}: ${LIBCRT0} ${LIBC} ${DPSRCS} ${OBJS} ${DPADD} ${LD} ${LDFLAGS} -o ${.TARGET} ${LIBCRT0} ${OBJS} ${LIBC} ${LDADD} .else defined(LDONLY) -${PROG}: ${OBJS} ${LIBC} ${DPADD} +${PROG}: ${DPSRCS} ${OBJS} ${LIBC} ${DPADD} ${CC} ${LDFLAGS} -o ${.TARGET} ${OBJS} ${LDADD} .endif @@ -64,8 +74,8 @@ ${PROG}: ${OBJS} ${LIBC} ${DPADD} SRCS= ${PROG}.c -${PROG}: ${SRCS} ${LIBC} ${DPADD} - ${CC} ${CFLAGS} -o ${.TARGET} ${.CURDIR}/${SRCS} ${LDADD} +${PROG}: ${DPSRCS} ${SRCS} ${LIBC} ${DPADD} + ${CC} ${LDFLAGS} ${CFLAGS} -o ${.TARGET} ${.CURDIR}/${SRCS} ${LDADD} MKDEP= -p @@ -74,10 +84,9 @@ MKDEP= -p .if !defined(MAN1) && !defined(MAN2) && !defined(MAN3) && \ !defined(MAN4) && !defined(MAN5) && !defined(MAN6) && \ !defined(MAN7) && !defined(MAN8) && !defined(NOMAN) -MAN1= ${PROG}.0 +MAN1= ${PROG}.1 .endif .endif -MANALL= ${MAN1} ${MAN2} ${MAN3} ${MAN4} ${MAN5} ${MAN6} ${MAN7} ${MAN8} _PROGSUBDIR: .USE .if defined(SUBDIR) && !empty(SUBDIR) @@ -93,26 +102,34 @@ _PROGSUBDIR: .USE .endif .MAIN: all -all: ${PROG} ${MANALL} _PROGSUBDIR +all: ${PROG} _PROGSUBDIR .if !target(clean) clean: _PROGSUBDIR - rm -f a.out [Ee]rrs mklog core ${PROG} ${OBJS} ${CLEANFILES} ${MANALL} + rm -f a.out [Ee]rrs mklog ${PROG} ${OBJS} ${CLEANFILES} .endif .if !target(cleandir) cleandir: _PROGSUBDIR - rm -f a.out [Ee]rrs mklog core ${PROG} ${OBJS} ${CLEANFILES} \ - ${MANALL} ${.CURDIR}/tags .depend + rm -f a.out [Ee]rrs mklog ${PROG} ${OBJS} ${CLEANFILES} + rm -f ${.CURDIR}/tags .depend cd ${.CURDIR}; rm -rf obj; .endif # some of the rules involve .h sources, so remove them from mkdep line .if !target(depend) depend: .depend _PROGSUBDIR -.depend: ${SRCS} +.depend: ${DPSRCS} ${SRCS} .if defined(PROG) - mkdep ${MKDEP} ${CFLAGS:M-[ID]*} ${.ALLSRC:M*.c} + rm -f .depend + files="${.ALLSRC:M*.c}"; \ + if [ "$$files" != "" ]; then \ + mkdep -a ${MKDEP} ${CFLAGS:M-[ID]*} $$files; \ + fi +# files="${.ALLSRC:M*.cc} ${.ALLSRC:M*.C} ${.ALLSRC:M*.cxx}"; \ +# if [ "$$files" != " " ]; then \ +# mkdep -a ${MKDEP} -+ ${CXXFLAGS:M-[ID]*} $$files; \ +# fi .endif .endif @@ -146,9 +163,12 @@ realinstall: _PROGSUBDIR done; true .endif -install: maninstall -maninstall: afterinstall +install: afterinstall +.if !defined(NOMAN) +afterinstall: realinstall maninstall +.else afterinstall: realinstall +.endif realinstall: beforeinstall .endif @@ -185,4 +205,6 @@ tags: ${SRCS} _PROGSUBDIR .if !defined(NOMAN) .include +.elif !target(maninstall) +maninstall: .endif diff --git a/share/mk/bsd.subdir.mk b/share/mk/bsd.subdir.mk index 81ba0ee594..d5e35a19d4 100644 --- a/share/mk/bsd.subdir.mk +++ b/share/mk/bsd.subdir.mk @@ -11,13 +11,15 @@ BINMODE?= 555 _SUBDIRUSE: .USE @for entry in ${SUBDIR}; do \ (if test -d ${.CURDIR}/$${entry}.${MACHINE}; then \ - echo "===> $${entry}.${MACHINE}"; \ - cd ${.CURDIR}/$${entry}.${MACHINE}; \ + echo "===> ${DIRPRFX}$${entry}.${MACHINE}"; \ + edir=$${entry}.${MACHINE}; \ + cd ${.CURDIR}/$${edir}; \ else \ - echo "===> $$entry"; \ - cd ${.CURDIR}/$${entry}; \ + echo "===> ${DIRPRFX}$$entry"; \ + edir=$${entry}; \ + cd ${.CURDIR}/$${edir}; \ fi; \ - ${MAKE} ${.TARGET:realinstall=install}); \ + ${MAKE} ${.TARGET:realinstall=install} DIRPRFX=${DIRPRFX}$$edir/); \ done ${SUBDIR}:: @@ -44,6 +46,10 @@ cleandir: _SUBDIRUSE depend: _SUBDIRUSE .endif +.if !target (maninstall) +maninstall: _SUBDIRUSE +.endif + .if !target(install) .if !target(beforeinstall) beforeinstall: diff --git a/share/mk/sys.mk b/share/mk/sys.mk index dd938daeb6..41710fb8e1 100644 --- a/share/mk/sys.mk +++ b/share/mk/sys.mk @@ -2,6 +2,7 @@ unix= We run UNIX. +#.SUFFIXES: .out .a .ln .o .c .cc .cxx .C .F .f .e .r .y .l .s .cl .p .h .SUFFIXES: .out .a .ln .o .c .F .f .e .r .y .l .s .cl .p .h .LIBS: .a @@ -16,6 +17,9 @@ AFLAGS= CC= cc CFLAGS= -O +CXX= g++ +CXXFLAGS= ${CFLAGS} + CPP= cpp FC= f77 @@ -47,6 +51,9 @@ YFLAGS=-d .c.o: ${CC} ${CFLAGS} -c ${.IMPSRC} +#.cc.o .cxx.o .C.o: +# ${CXX} ${CXXFLAGS} -c ${.IMPSRC} + .p.o: ${PC} ${PFLAGS} -c ${.IMPSRC} diff --git a/share/syscons/Makefile b/share/syscons/Makefile new file mode 100644 index 0000000000..2ea8f3f318 --- /dev/null +++ b/share/syscons/Makefile @@ -0,0 +1,5 @@ +# $Id: Makefile,v 1.1 1993/09/29 02:16:53 rgrimes Exp $ + +SUBDIR= fonts keymaps scrnmaps + +.include diff --git a/share/syscons/fonts/Makefile b/share/syscons/fonts/Makefile new file mode 100644 index 0000000000..73b8698c6a --- /dev/null +++ b/share/syscons/fonts/Makefile @@ -0,0 +1,10 @@ +FONTS = cp850-8x14 cp850-8x16 cp850-8x8 cp865-8x14 cp865-8x16 \ + cp865-8x8 iso-8x14 iso-8x16 iso-8x8 +FONTDIR = /usr/share/syscons/fonts + +install: + cd ${.CURDIR}; \ + install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${FONTS} \ + ${DESTDIR}${FONTDIR} + +.include diff --git a/share/syscons/fonts/alt-8x14 b/share/syscons/fonts/alt-8x14 new file mode 100644 index 0000000000000000000000000000000000000000..92327841cbc04c21e2dd9073d3b2c8c3e7e80be6 GIT binary patch literal 3584 zcmZWrKa1Q}5Eoad66;O(h?jGd!lg)HErPfMlaG)zse~^mE>v7~S;#~7U6CqZz~IW@ z#vdSs!LZ!HBgNu!IdYffs@=vMTdlWPp7NVNPtwM7w~~G{^XAQ;HzP&SKGo-M&(Gh! zI3g2vqB ztNcbaUrz6~etS24NuuZ=9vu0jL43dnei~zH^$i<UL3tBG@dH zA*je5iO#XL?2c%@>4_^?TASv1%rxyqftWIb`D=x!tw0tN?Psre`aNi8U?pTNypoah%8*l)Sbf@k0oRJwZC+ z5Gq_0FQcd+J_QwaNU&7RV28zGaXXcBHKb$au_vU|PSr{<-HFEhCaF$#bPG zKxjVqwh~{kaYimMq7+e#5}$W}q6n9IUUELjX(}?ziPLNpCPU7dND`7!P1x8Mmj=zz zb+d{miPg@D?uueUWfHF;w{AMs7)CH&VY+dT(NgPtkY&{`1?5|E2H6=DZNiffHmft( zl$_xZWt1j~A&#R#3_{FU1kDl!R83ZEH6i}w=ks|R*ey)zx- zS=mY`hSQP8H%!wV5f%LE>ku-|qZ=EROR+_8`z`Nznkcub6P%u*unE`Uk4 zMs3A2B=$#(%bYbamqr*p-Px6aMzZ7|r{j$^-Rye3zMkpXJsg+VAH=c?=kD*R=R^~1 za#~PLPayG{`2<;0!hzq6taEq_zJwl=GfYd6Y4wx+pW9)^>!_W%uNiSEaq$N+u&#^g zIijeUI1>TPMf2{iuvB=;r7-PIBveX3QgDs<-@I*lIV*mD?k^sZ=$?x}N4Eqnhf=li)Y* zPtxPodK4X}ec9)zzk3sX-dm@dyt)aRtnoQ$vJd;R@p#{+MigS`?JypzXKV#A^!#ii zQr_|2x<-)fZuWDwpLxv-ePeeNd=buij~?uiuIV|39s}({U-jN`P?EQJ(B@-Z$CmMs zM@*P7|C{31G|;DMuP@bF>pILhPowY-n$+pi8EfY=W2r|^oRPjB+I rNBT+&9HS2p_!H$*m5-QVFW$d<^XA?A3kv@9IxYV0m)$Z26W#qkUHi{O literal 0 HcmV?d00001 diff --git a/share/syscons/fonts/alt-8x16 b/share/syscons/fonts/alt-8x16 new file mode 100644 index 0000000000000000000000000000000000000000..1ffba0625fc6fb3655ff2f5926817a99afd0632b GIT binary patch literal 4096 zcmZu!KZ_ej6d#VT)gmk15(~LW;c_8xUczz*CLbYbQZ07axY**Viy(vSbK&n`aAk1g z50JuO5O=V+5H5v?F5)VwF^8(N3Ngy>{h8Ta;U{}{-@M;@^Zw1-J!1}OeDUt$;@#_u zi?K+?`^A30Snv1B8&f%u&H7%mpV>B>ZNj`>h!($ncM5KGB_0<(NAmY`$ zeEhf+bzZ!B{qf`LE0qtU-VdWGXe1W$5|k&{K={+2PE*j?=0^DEPoF;p{kdLEWFbiw z`8r=H{>9{Gvvu3e&Ey4(kD}i2#GMR#(UHP)m`=vtVME)2a2NUW#bWXIeIn*eno5^IN)_L_|<*J8`yq{&( z`t7ld;@PYzUjyVP)F#LNM*TskKXwrntn+_bP7+&RPHYsts;!UUvW}Cqs9%X%R#n;g zHR^TFJW5lDK(D|uC9y1|Nz9Zq5tjKr*K9Ke+3|Y?Q*gOaFzsw8q|rEv$Um*Op=!-$ zR51Z2*$B2kS|3{ryjGjwqdbZtU*33-S5pUQ>bO**ao_VkTy_sxi#*=Ib9J7U3X)O8B8UAL(fRrsgN5aCc|B48`jiky4hepT z4zSemJK^*45)n==^?;gfMZ&VIwjy`slV_u6#gkI=rF>CrTKdbnE`JuDcH-@HKWaXv zT;xOUhUUDlu+?aUO;;k{6ii{i`qnv=6QuT6cW(K);W^jDYP=jDnNa^!K3m$jM;2I4 zG3tJh7g-zy{qgwsj=yA@N$o3F`2$D%Y&LVZmHJm#IE@xb7oKv-InelB{fdfr>H9C2 z*o1&m>qX~iqTcZJd{s!2o z0$k5GfozRK;N@BMN5M7H{6c^Ti>-vnK9qGVkX#`$t7+;t{zGE#Uq0NBa7_Z2E)s(E(n+pQbx1$?*UZAr7yV z58gezK#Y+F(E39KvnOlUiM*pB5c?xFrFp97sfSR{C_&tvG=cdr)0K&2gh|eXKfjiH zbLA86&6$%Dd$rCx&oA14CmK6?Z1IkY&m{63F9Rl5zoZqCZy@&@pc21Ys>e+5G(QIz zpFV$N`t$N`rTriO+vQC?oo_btDON>)cwFL{Ncg(Gr4x@M@A>XL84EbXBeSN_=h*5U zJQjVNZ;CGX7tXilSImtg@4v)0v;EBPk80vHdw--EaCJw~cYv@y4fsg#oEo}leH&iz zH)wcE9i;tA`m29HE72!s+w~buqi8scBL98#2GBdgzY#p=V@ydD9#@1+9T0fO2DScJ zg?akd$nnYt1zuV}>yK5~+lBKF9wAYd0v;_e2>HXRHh{|Gq{~A-oo~?rLH~pgRKsKB z)5H&Q38&$u1={`tA^7iq>HdCu-~IjfSNBH;>l?J~tZkFP9}C^-{aAf;9sUr17X`e> z`8(^o1NmK~y&US3DgY_KpXHf09S#R z0oa^x%$K2ejzAcJn1F8r9`k?(Ao2Jra5cZM{QIh^4(6ABK8H_g`WnNU=X>|>`(|m~ z=kvL~f9VYktbaYzrg$ZXw=(^CL~C7Yqgj%8`91!;`%+c;K$h&w5AWZ;ef$2yOHR<) PJS{$T+s6ud3!VG_^&vic literal 0 HcmV?d00001 diff --git a/share/syscons/fonts/alt-8x8 b/share/syscons/fonts/alt-8x8 new file mode 100644 index 0000000000000000000000000000000000000000..e274ec23b4c26a8cf847d5c278568e5f465002b5 GIT binary patch literal 2048 zcmYLKKWpqp5FhNIG{U}g3qI@={s2o7^i(WoFt5lLc+IPX9X2jRxazVHMYel?fL!^C zG^x`X1O`>&O7lR@ah2UYT%83GtNdnGM@F1xXLj~Ce^*BosJ#92_I`O=mP0!{ybNXT zy)V)UP0~VmTBNKfCP7gUDPt7TFuXj#ejsDd&#j^1VR?VQd>9VDKK#9D8fUDvMwRO9 z`!lWQubb=3>&wfZpR46Z^YUUoE^juS?>0BIcQEs_gFT>@N|i?8n6+f-x@~_uIy$1+ zWt!({<2c%~EX59HgV-xY3IrKp#Xbfjo%u|&byZa!?9>kS6dVT-Ee9^PLz9 zD2icS-B3}l*4yWT^jw2_a!s6OP2e%ootnJy@iFyP7qgsZRfn=t-v9tO4g$+~6|tHN zHsg=KjJgVlx1)k=0UFWIz%YX;> z&6Aso%;#LQnNK;-zXL9|K|T8eye6D}^k0DYK2L$gdW7J}*LUhW_;c`^WsW%cINBq} z1EL(}8OHz9prtmfaBmRs(e2n7&L;e`ZNAG!7 zQAJ6Kw{d{_KlD#MO|{-=J>`2LKNg^(#S@M&aUp6?$BDHi;ztOQH@`UllFNs%aO*(6 zh76OE+O{-h+oJ!`G{SsX`%AgNc{6fkJxAjOmv8W{?^weP+c6np0mRu$`o_W=>!nW$ zxOT?)@YO;G*(jW4J?MHLuUQmDh=*y|@%_!12j*kDUT+Dgl8?KKX3?FaRonjCwkw)3 zK*AT5`@SGD8Q=}%0tMhYO9jA<DO3P<_1O1NXFeDF zNCoJd_u={QS9t#UH9VmV^6tjB@FYpL_$2`L!aEi8+eq}RVj(`UR-XFAAl!5;6oabNV11t)n6dUy+R zk_hMhoj*C3B!UZXCxSovWl!V&z#LKS;@#yXaStQGURJLo9gYO0z>fD1YfPZ@Qzj5H z4;c3w6IgN?WB^6a^B49b$YK6Q4*doL$MeGjxCQ3{WVyfqGGAc;SKu?5KZ`jlaIa2x z^TnSl{xtJf8ta3jBQ8sz&!6XL1mWLL@kN%q%zukOW8x@5Da-qN(O1n?Ix*+VPoI|O SM*leZ*2#7Ok_FLuN&f+l7^wRI literal 0 HcmV?d00001 diff --git a/share/syscons/fonts/alt8x16.fnt b/share/syscons/fonts/alt8x16.fnt new file mode 100644 index 0000000000000000000000000000000000000000..2e8400196a29549c5f15572643994e21eec6efc5 GIT binary patch literal 4096 zcmZu!Piq@j5MLtL=Ag=g9=g=VzJ#2Y7@UP}Wf1)cIrX&hhIJ0xA_xRQmQCDVa_ATE zF^BftFJKT^*hAwuHxa6!8Uj)eAs7O(pklYbnR)Z{Cmq}G?Y#NTym@ct&3kqnOW&KX z{N%Tjle5#4lUxS!dQ{iNw5|=G)Sx^ML(e0C=Y?UOuMR*0OyY&Cz;eQ<-DrqmxE_4c5b+>PE~bU?hWGMwWpMm*EAawz6kJi zMD%2vi1@6Gg0w8tpjl?b8>)!tTMfb(?GM;m?XUMWPe&>@o2|Olyh)!-ZNFFd{I;@= zGXRNo8M~F#$t^2xw}T+dmh!5~vLI-$=v%1729d?)$o|A}xgvF(G)=2Y8%Vr-H2S9> z?tVQQaRes6e`tE1??*rVc|nQ+utD(S>#}_HhG|vW%jPcGs}PFCq_FZZDTxOOzAB9aSTW(renO3r^Jj5<*oe&u z07g8QmUNV71MFPS_nK~lWJFl~asvht$!Gxsl97E<(f9&g6n~FKepx<>=1cuISnu(7#M4|n89J8h z?xSuHzvT)+%>KQPlO)a31k=7J`Bh}d%twU;=Mnacg5+g6napP6@x_I)Pg~7o;=%6c zWaNtQvj0W#>P;THUzqjBIVMc5Nsiqb404Kp!TC{-PSs=f7k(Hu`+_xmS#KuVYk}^Qz(L#7~S>%(Yyu>jOInHflV#NDPm?whIT|mfw z#`QFrus$*~kR4_JabO*qg9wFy@yLKa0>XHMB@MyD$i!p-+e7nC>?g|8nFnRck0phX z1mdFg&)s6k_)!cmjSwSPe{YAJCu;ltg2QpJ!#o->U)ud3{-cRMVI%`;>kkR(*lpUQ&9$ z)cTwMz?*CHi>J!1|G38aJmY<1{3%lkr#a7#Lr)c^3aJcIec>F3wW;BHxym=VqR;>E z{qO*kiG8XNp9ud+csgCH@#&wtw>!68SKmh$fc{H(zK`)fLby6~z^9^*w%^R);bZ1+ zxS_FU)`L-O5oR#)2cO@$gOu+xo|@eQ%7I!cF8FMD{uzT5pakG=YQn^Ay1KvM5%S?g zpyRQ`Rl9I zKa&cdb@R26xu-nEjcCU0!GXF|t?`@qnX7mMvz_Qy8}gZJ&YGh@<;5-7*NIK9{;Au0 zW?Xrbi&ks%jk;%m3Q)Y&*AMRZJjJVjclCq&yZ)gX;{MQImtQ+-UtqO!y`isol`CFg z*A_2s)z+SxQM}bR{tJ(1jRH-t{t681Qy}-t_3H59hQ8V}{&<`FyBROtKmI3ZrBVAj zbhSZS8>BeZll~o7@XTIue8Fpd21H8WvL0v&GZ0e(@g_GA{P%+w&GP)M)j!xHeN<5E z1nvfq<}6(q%uD?P3MH_!zn@z?)bXU7Sso>Dtv+>*8+(o8j+LVXq5_)whtd4}e370D zpQgC6BCK8SE4aN{zwGnbk@@*0O@mIx_FLQACRf(Gw{y9F`MHkgxIEff1y}O&R_1?@ wtf%H<1uP@D;)S-%`|d*;?#M{kJ$>=wv}?A$_q-iFU(cRQNG+JY+okpV2ig2s*Z=?k literal 0 HcmV?d00001 diff --git a/share/syscons/fonts/altc-8x16 b/share/syscons/fonts/altc-8x16 new file mode 100644 index 0000000000000000000000000000000000000000..734b76eb1b16038cd1a61455dee150361162d865 GIT binary patch literal 4096 zcmZu!y^9-16d#VTNwLUEx5PqjT)12am?bP{Flh`XRnnwd&SB%g60W)kGRVFZ#{Yte zTSJJOROt-M9SjK(CY2D;MO-DNFh-S8Ay)alkC|N^ezI43pYy(F&zP$)K7Da|`r_H? z=~y`9-F&y3uXa1djj0-d&H7%mpV=;&hi-Qc2M7TSK)%iINAYf`< z-o0CjIup;Iy?XWRT=~PO_sM7i7>R|v1mp=05d6-kcT&Ld`cm*m4<0=Le7joKLP(N% zzRKq+|G2(fZ<@{evVP3$gQzz=Yz~LL=s?ADm`=vtVME6O@GbmD^ZES8>jaEjS7eoQ zRTeud0{o})2p3U0wwSxQ$waIb5QGXEX&3%aq*7-k?lf=5S+D6e6XMF@`E>6 ziiu#7jbIC)^|4OGo!SH*Afih6w939Z<8Wh*_4^M)*y6^lSOz2O59lo>1}5eE;P# zE@`TKgAI0^4Z^<4Wqjh8`BY7@`<^GZZJKr$i^_~*8|Kwq76oM+O%NX^s6H$o22HzimN_=Ul9#fI0{W-w; z^!X#xuNUu@+W+xC`?ztF*?K*jfGYaK+a-Pz2=3flns|)7$M^TiScF47vTIs>j?Hc1 zvFKyIDZ1cam~YLmm>Wmle~E2s`L9wqgE(BP7aFkVgv)LjFM222g&S?Bn21W*c-s&_CpXYH_T5 z+W3Jl;k3B4K-Ygri2TQ&?|;7fb^r745Bnb)toOhs(E7OVuq*)KzN_p1{;q3(U*5iH zM!dpD;&0O5$C13f`vuEW_hS^NP>DY>{)+sq0Dd!(mcqW!mOP*l4)uZktNAmg^;aCf zA->2q0ZHD&J<*t5h)?A`t|CER=|P_PoNd?ibwoaV`q#@}UjF=4bm?;7{$7UTaKBg| z3|$?}E?30Id$5l_;qZ$M_~ z{CSMCj%cVpDEa;SU);N=IP%@_c+@OQkNl|K)JLp;5NUC=K1tt&1xKQqzYYEFzzv^o z{v)XKP?QVek8Y*~@*( literal 0 HcmV?d00001 diff --git a/share/syscons/fonts/cp850-8x14 b/share/syscons/fonts/cp850-8x14 new file mode 100644 index 0000000000000000000000000000000000000000..cc9d30e743eb60fc5792955ffb0a10a002967f2e GIT binary patch literal 3584 zcmaJ^ziT5$6dp|2Y7tI1+!xKace z4S9XIo~+l`%e5e78j#JmWzTkQTcC?#xm;>pFi#i5^?K+LmcZ7;Su-uKp%cWiMfK)Q zMcbHleg5&|`L*zNJ1_PIJrv(m#e^T`olYi`UtfX<*}BY4U7I|uEpzD$ z$>8Uph0`PmYK4b~)&hJ>n^ZW&&dR3DSdPC&qmgJN^x8D$wdZZ;xwXz58MZd}W$+^M zi$oVN&DIglS25Q>BY&`{wXLs4Huc|0)3?`n4-MjQtDYq=L5+oBU`g*Et-ImB1v3@ z(w)MLn>5{J$p*Q74!pbss%t0NuolGRbYea5=xLZF>415X*ANmvXcrLsT>88~e8~TJ zA1$+|-a?A4P7N%~X0z!?&eb-B+rTJGKBI&dpJzrzb%iLdxLK5189uIN#k{6^`)=^= zZ6%oyRF)6zzFA#;#|XOFfLOp`VWI|9YL|=YJ8n@KGccxFFz=xx@)qiU8x#e^yn6B2 z%x*lQoDoSx90PWWFT%T>*$!=F9^w!S;FV}Fw{JN7G4f0_dY&NTT7b~)EzY+10->DD zFrpk$j1pf{|BjP&<<3jN2RX}xhdFVQ@A%13a3<0eJSqw6`uuCHqI!GwAhpstO3ll1 zKxL9lK)}|ck&AB!$_=I)^%#?_o51VtWftDKQ>RoPf;Qzz2%V-6osu&gqMXtsF(irV zBp}2Lm5{7ZKqtv!K@#ly<`yKjm6}4On|xT}G6k=RXMF{oFRriDv|j}d?K|0V@oi_> zGeYR@cnb(2iWUX(^@D&E&`W|7<($ep3?0~vF{R{`cM#19K75qWPL5a$uVdi^s6X|J z{jYSHpwLy^<71^-Khe^N$X5lMR8(7>i58*3Ba~xUwLCH-btem?rPZmRrA4HrMXFL! zZ8gSfP7zXN6-fqE^AUAZ1GXhP?f~31o09>1@4Nl!-7#rV2WC8O5bq5E@eJFf?(CUI z;+aR;U>s$Mcx}!oBFIU_YJ?+1I%{SS77JLiH?zkDqul&d(ui3+lP_P`n;RU?#ln71 z5PRDr(Rqn1(7k;}#ibgxxh53y!o2n*6EE@;r^26kJZ?;|!k7aZ-$Ha5s^GE0HIZed zk0C$$>+;Xb%cI;&s|NkYpq%$AqqF1dBz=X75qi-*{`-*GJpbvgcKf>K&UAwgJexa0 zUmnGOKU~It|NIjFAs?U9N|tb$MrQ)86(L+q5rWD5=)=Xu#RrP0OZ1pL^6w{K>}#{Z zdrYJ+!J^lT5vG~D4Yfj-o3wk=bwJ{bVgqaZ`$%lrmh+GEwZ99B9zQa-ptdRl*Um~4B zgtzd3Y&#qy5gm3nu4~Lu9&~{RE%7YisqA|bHQMAsH{C|KbVhi@J!S>bJH!J6OYak~ zF8a9}VJ||k7a^Gww1Asrg$$BDii)ZdS6l?KxIy7uQ%d+4hv~p_bQb1WuY0dUgRB4l w|AWSS(Hl0P8utSao3Jh?C_sMjyL9f{=hZ=)r+k4iDymfPZMZmO+SCN}AF6z$r2qf` literal 0 HcmV?d00001 diff --git a/share/syscons/fonts/cp850-8x16 b/share/syscons/fonts/cp850-8x16 new file mode 100644 index 0000000000000000000000000000000000000000..a671c011bdb4039cf197b5c80d1cc9fcbd332d20 GIT binary patch literal 4096 zcma)9y=xpt6dz33YB`i{iG|#xaFJqh!f6SM6iogFH>nmmY+Nism2!~bY`O6NV3&q8 zD%~3v3AQ>^9s((cs=LM*Rgwy^m*4xC*%{eI-r1}B&3p5{=k1&^_i1|g&*9<6w}*#Q z;Y`=(>-EWMy;f{Y(}QfW;dX8mHiTJOu2!ooE0v#>Q*0hEmLZY}cg?gqAp=CbTGmgW z*0P-!Pu_m~`1VBkTchuuOlF{oXeet?nZbbI-5+;z(Ein>;Fr%|J_r4=TDG!~W#{Fp zJXiTw?d8>Ne|vS=zGC)+WVE%@?`(~d2P$4dbvAVl8!`vNU*W$zKR^HbArWKSRYlWv zO_6q11o%lQ6Nq$(KFz0KEUEwW4Kx@;y3#&XF6+pvzRCmTrfpl*&a01`zIi^(st;vUt$({ORRuT z->2B0PSkO!#NfWeE?jmGSxcO}f#>QxEmg#$h=mXRDWY@rH3kcd#p0q>|28NgJvk)u zcjy319e*ddtd9|4d#nf4+&09l>*iMYeZBW$@}kMGd3=(_r6!DNZesXb~w zrc(G@+zrEdU&7R60;5aeUsX)rUiZ20QBIKRUtaf%(*bj?32M9?AE{9Pls{kCbVM4M zPcf=J@T($C{Ql|mx5!^o&7}GjHSWL>p3mp~=SKbO8k|Opqzk58at<{9zI#K(J9hn7 z%5lkay0aOiIiNWbP6tyzTiPZBf2aR4DNP!4%%0F@VE z>~Q%o=2MRZ&xg1#NWGia>>m|jh)4L=w}A7{9qEgMdgu?{j}CDAH}d?B!W(abBgEka z`5LEN1*fgOo);*m_Y&-ma%mV+ms zaHq};=UH~lj0ycm??v^*W)t-XU6X61LHRO{Y+COZM^hN!GvW1xRh1k?4>W>U> zhJwP?`jdQ!JHz49gdx*#C&GBI%7F6o145t1$Yhj8RS8Z0mhYR&|I`)mApb`*d9{VV<;abKfbw^+5ly|~|O7vnUEF^c*6-TU|N-)X#K zqU+?E|9<7cUibI-*NM&s;?j+*#q{+T2Du%_Hzx0|`u#|<;irk*!`-4WYZsrBcUVQd zysmqBW6%*ne*s1J3l4ER-yKZh+it!cg??kaewJICn~1+X$SdE=v)s)fPkg;@aea*W zDf=PWq1Qvnd-*Kn`lkNkOt z63Q;_4^`Cy;QehKrYqN#Kr8nz_@m=b<{Rus81^Ggup8nE#}xGL3PgS?nQ2G?oRq{+q(lOt^5N?DT0*fQZrks_^& zE3R;Je?hJ=EEuL*T#5rbjsih$LU1Lk5VQH-n~kr?TS?!(w{PC>eIiQaCl`~;^NIJ} zO?P$Q`6|Zfigj8qoM7RK7^mw6^UA@v z$<56V8yg!m`U;lJE634#??n%4o#-n?3IrZu6I(O*ntVH_u7BUwa_}hfEg`D}=IK)*hIbNc1+q!%?E32_Zyow5m zGovia5X-FO0?b9qaS{C;r_(9$7WR2SIAb1X@Qw|Ny)E$LWZyR~Tanf)t%sY?Ll_bV zjv;>xq~s6e0x-`LutJkRQ`?>fN*yaTz!(S-GZ4}E7OG5Kh`@8!AkDmPd;7UcdAhSy zk3b2v>QN_zpQiD7;m(pica-a+`}4N1hdV0}fPtI^h63DTU@&jc2Pz$#rM#g2WFu50dkI(Mf<_4PxEdETqQR=+)! zYp|h=dVdW6DIe?%iI4MVKdQHndU30{Xw#)Qfo-4&`k*P~!^&BWXeptlv z@vX~z@Tf=fKOFAreO97Y)K!$*;mtgS%W^9xq}moac8kv zVIBfD9-~$XQE_-T*tfiC44H$3mJ&j!mJ$aM$KCbBBEE%4lFw%e_>1@mlC?{;-W7y{c8XUDX?fKCw07S;3T6-{H- z#k=?K-(3jrVD#webUvR?Rj#TE3#E*XzdO#d?DXcE(VLSuCnqPrY&SE4)-H=}u~f;o zv+KL&?t1o?Smr@8I%D0xi+OLB%jNHnL4U>K~O6^d~_DzTiT?;A$C?aWyW&+n9XLQk) zD$AR0-a1s@GlFh5AQrG#m}mnUbHK%nJ8n}sH+8PsFz==$iWb7h21NleFKlj`^<_kq zGa`wIW57)DMR-?hKcb1uLmXlOyb>Es=hgL3JK)nB)XUB>=DIAWR3^y; z1bmpyG`@W(cj#`k$C!L*THg3+=HZ>LI%6gf^eJ~j=yVh4l$>D^<&-9gAxX?A0U>6n zgk*&Rsw9iGl3;&mu0Uejm=#oN=fe<}sr8z8K2}hD(S2oBld9#=ywiP+Z!gR45yE&s zT0jWBXi=b8KL|(xqa--JoKt!0b_X_dbSYWoBSdqGKVC}MPD-qW$1!jUG+9N(@h!Vd zP`j&mkI#+i{KS?5!>RueKI# zHKz!vvW6sE6nKeh*MMz_j&}ggnqgsB)HdVXS6#5Ir4K?f_GJEHb2M2DeiJvKNd zvaIaIVCZa#!Z@6<{}`3mCzZ3=>x(2kM8ya_8o&PMnAzO_>8#d?W4CbQR*4)y$mBOa z_J3ad-T(RbxBd_E@g?2$hVHDQvCyr&)MnE|L&@7jB}F1Wd0sQ%-6paxjmjuuAE2|1 zjz0RHS&RG-skd@3lp$OTbyQx5@=~Ts#(6!-L#vYN%d_Y=-p{h*s2uO(tk3&w`qp>m z^I)Dz^6XudWQosFl6@HR#^a$*jVQ*@voId3XRHM=^!#ii(y`;gyqzGiZihJ=W?u8c zx9#>3eUX8i0X-a$PU$&D4R*0u$vH;%^~F2{^idElGT0j+0z`BH)pNQDXdKu`x=L_a zT7YOvGx2fc%Nc{oiUZ^C8l8~;6JVs*CA}RI3NY}Ic?US0k4Tmg9`$k=clB2Zz^7XE zA7o`&mt}S!lCfzCf`jtQP_MM2N3V6-E6`vNQYvWlnA+5kdf{0}#l6oblpa#tY{YK+ j)Qh6xIP0O@P(HX~9~{T^k5h^&wNJ%G9jcikzkUA)n)`hW literal 0 HcmV?d00001 diff --git a/share/syscons/fonts/cp865-8x16 b/share/syscons/fonts/cp865-8x16 new file mode 100644 index 0000000000000000000000000000000000000000..53e16afbff1d5274ec5b217ff0f7300e0dbf96de GIT binary patch literal 4096 zcmZu!&1>976d%%}li0X7(HQ&ELyjVajVQt{q5ncIJqf3%ISj?8?LvhdU;IC$_g?xx z1QxsKwD`Czc)R$tdojUKaQ0xd{k@MFjW&5}OM3IZKjvv<%r4aDf1RIycz=Fg3#H!P zY`0hI?N)JPYzwHeJtw)BRUw+>`Fg!hl3eLYUPHLUu?&_(s9T2h6&WDl)x3Q5suXcv zyn6rP!}}|x9}K>IF`fd(q9HE)81AAf(G0#5I51wVUp_6G3ldNq-SB)Q4g`Hk|w zo7~=S+Rgp#`JyG@?s*~C|Y{(n{ccGu%+}!-}jDYc~DKgtMHjA4| z82Cyl6BOwXeVR|fv84Xf6QDDQbj59~RMwFeZIK4b&15oB?Y#Qjw)S~P-^;S9a^|s& z;#FB!zK)TfP@5d~jrxO7zw2{YXy<=gPLis*oK#VC(Nr#i%O+0JqPY;U%-U?UK)ptc zqcnvO^a@^vB$kCVi5ZeE!ZP3Hnr-GmcAPD*3NCj7Cd`JibP`V@@=xn+s9LibRZJL@ zYz$ohtxwemd!ssmM|l)QuDtPnZl^6=J`nj9#dr+pSpmEOXgjo!mzagWB2qx7=Nj_y zSRI#2bnZLch0E?CYmwtO@LZjzrGjV_vCyGEMs%*e#$aK&TrMW+-yS8TC5MFn5gkxb z$3F_5mzRiea;Y2CY%CGW(r$#_mM3q=Z;O*s^QCxE+z0Z@rYV0AoGh_9wMWgzkPH2Q zyP@0fE0`LOVRR+*`+~vSYd*Fu$_Y^YtA}=Z-Qk>TLN#8Fk5s6CN}nyOct9GMPBE%I z(2FdNynJ2%-1C=IGpK&WtlMye&t|jsqgDSJi``fu>4H-(IR_el+gwobE?xiSvR%?t z=>`+*G~4&{DwpjO$4sYcirz0=Y1*PWKBJ7|%I{ZmTNIRSG=b-<8Z>~*-##o^u39k5-uOc z+_gyXtc!a=>fO9%|ELIEJVLL03poGWmR^j?u0L2F9pL2m()5wS8}5T5z~Ke?AZvcU zfJGWW>rc4Nvan9fk0B7dEj6Wis{3hxP){jA+?_Om9f)zF5l?c;_-G-^!IgKoQ)jyU zED}>=LjTcwy?R2}^!fw*A&zO&!!yj)kWPMR|FAvM*XtLQEyPJ*h?~;%`Xj-cA)|1$ z{=^^Rjp1-z1F2bk`L!+Jjdjrff@veRix8M$Uu{G|$M|FFLuF8+3S zI-d6ON%Szk2J*yBL!U-|gJ=arf1^}y4_;Kw^=zf~Gy6-yBXIS|{(W6tUsLz5R@INR zu$fLb|4f%K$?==PGo*@6Aam&tdM||ap?Zh;Bf5Ab{t({t9}O1lFJO%uxa^O~tvw1~ zM|2)Anmgi1Q~VW^Fg`=?(20oiwWaMZcyW7DS~#)5R6N3x;^p<9$HKy|Ha^671w%Wx zLysAK-Euy@`KkYV{dfQO@9+H|689z8^cFXX$A1;4Y`8>{kL4OxLKY9Pu??XpdT+ZQc(U`5SyW63AP!`YciY4Hyw@lGB)j?Dt(VUqe*^^==bu};-+`w)pnN@d&1f;7&lhq&H}q{apJD`IuoGZ% z(ryG0#z5zPfFcb>8aAYVq2TZdb#t9B#3Zj@yk93QGFnJ>88I~`H~?R8g1h6VJC76I zkhzaSKfsCQh$RHawZ{?N6fj++`^xX;EAM|=!)B2`Wzf$Hr%{j3 zeuiN}BI4NLgJ(2RKZ7s<33wA#PB#om{0ZGAQ`t47Vu{k@s1v*kLimuIv!7^m@I3#dV_BLA3&qMQmg z}^xaX) zync}R8~d8DM%JNvAVYg71Gb%3)=iyV(L-I=N606BK>Gkct{(4hz!5LVi@zFpBH#D{ z`{#AWB2f4b;Y-04bJz<wp@tJKw#Xo^vNY&t z7eDR;x&X7iI4wQ)by*{QyMWHe2L5)TZJ}p5H6C%*YmS#_(zd=_FKaikh*wc1aTZh* z1!7s&T!58mIWD62X*QdIFM!Vt!WnZrL-q_Pczcj189y|>*pSv6tw+1CLl_bVmLY#M zq~s6I1z?#wV1rHmT)hl5Q0hdf0op)_Sb&Nq3z#x-AtLu#gEY%&+vAri=jktUJtAeU zRnK+i@Y6J*to%i`=N;wx=zhKJ>)|gd1Yn?Ng`o(yXc+Vx?7@{z%~}p<_%!{RXpDAm z+Z$qBu21fijLY)$M-Fs?KF;+(M@im{%XJZ%>$>G{W-EVIxvw5-^z&;6(fvW|w*4>u z9se8*l~K1R@SpR+ogwjY{_IEf_^20e7}t$EmyB~`K1Y>v#_5Zt>S*LesXrZDWi31NWY6ByNA)|0&kG#yLU1-`=8MaKVFo zgJoP23d+bIoU}DsLfX)M!1*i3nBaBuc_7^P%*0p|qQ zGq_ya;Dh0i{bb?**odoyhUBv>eFzbhiwDlaHg3E3&g?p_733)Z(XJo^l7W2w2LlhY AO#lD@ literal 0 HcmV?d00001 diff --git a/share/syscons/fonts/iso-8x14 b/share/syscons/fonts/iso-8x14 new file mode 100644 index 0000000000000000000000000000000000000000..8a8d2f9fbd518d4b899e2e4fb28cec7d933d9b52 GIT binary patch literal 3584 zcmaJ@Pm9}D5PzwI;-uQgN5R3ee5OB99jsdC};~d-X#l(VU5Io zjDCP#`W*s=y$1RLeCchkJ(b+t-*4tUJ-J)@5^MFFnK%F5ycrp@znZIGudY7)a@7dA z*=={b>zkb-leJhyJk%}2xbPs#^P8KSD9VKw<;`x_I0O>dk#KfgH`ma~MN)oza&j!& zNV(pA_^`cJ-uT`ZU#&lW{J55#d0p3e1f_z)2lwyae=wY^*9q|WXnb^Z^yu09w@-kg zXq(^UTPry$&o}Mnyqtk!%$>o#aXY>@xFZODMp4vQ-%?p7O@-HPx7*9dSVTpY#938k zaZnY)(=F=5aKp>X(!k~UE}q7uuh1Uf$1og9;7CY@6E(J~9E;-eerG^e?13Tmr)QTJmCbt?=VI`= zNEVfdR(cLVgApSVUT`qV>NW@-3TlwsrsKs4P~~*Cq6MB=ouGF-zC9ldf>?Og#V#g! zu@Q;SNJn5L8J&x;B%Lt{4?2S|H2BX2bar;OD%I;D%dCY_;Wenp0zcI=N?9RgIuhO{ zlXA^Ag4#5BnmAigzaN(YbFulNhj9eNo<`h=T#N3J-Cp1!N+@1+(YZ{Mh$QG~%II zQk2Y7wF8U(5X2?5yiX^ zS`!Y1(la5M;sC9rh;@?CS+@%;=h;fC$sv}#^%ErWiq0GL#ojCM`hFJGt}bH;bHS+CMeDlUWg0b z2U6F8RoFklz-4z|8+Odu8TT(OC-OycqI*$e{J3bedg!+#lFa?tZ#!ORv3T+N<;&ME z3KAxF^LcJI9nU<@mYIA%SW-J+*K@0NFR=0A^{XFVsg^ECVNd?2dM3*#dDEHOgp<%$ z8pe%RFK@hl^|bsQVDH<8%>T$|7-Hxp=pnufO;Dhdetd||omcs1RWGCT^p7!PRFtf) zgK%ujUrF`1=g}BuruG+e)+5FzH$vy; zno|q|z|Zh{d0H)2r8VIvo+PH5e>CBF45EjarcROv=n;H1?>?WdetyO`E}ffFk!T-& zQZgiuXwM^tRFivR!s literal 0 HcmV?d00001 diff --git a/share/syscons/fonts/iso-8x16 b/share/syscons/fonts/iso-8x16 new file mode 100644 index 0000000000000000000000000000000000000000..571df318bf0bc9ed68794f45022cfd7c4140d31a GIT binary patch literal 4096 zcmZ`+&1)M+6d!z8dRUB)OJf`na_Dhs!7S~fK>mVWdS8;V&0#5`2UpN#E&CAk7nGXb z0)>!cj{$>f!IFc}(Fi1jPtqk6oq`T#x4-u>yCcPYPAvWAy?NjBW^Bwp6&HV9TwGsW zTol47>Umv{*LAJfn5+Wj(UIMG6y+`q;&{DY2SKd-ATF@E#aM<&Al#a1JthN0yc(y^ zpQo~&7sprE*H>fZpB#Sqb$DqhN%@-i#za{b zoh?wWGlnQl!e8t!$T9`Ktb~Ej6toehaUE;6Sq9m$wyYL7;%qQ(HdLhDao~}ET5l7l z@Wm%@*3MNZz zPVG_iF~!0^;cjTp`wFJ|eHdK{f0HmZ_GprzoFLV|`cy5aZ9JT7f*LQ!M=I1m<=-xI z|By5=pJG&d;3tvqHT&!N)sDZUno0GuE7V?kf*H@}wVfZ#3aAUkU+^Id()jD~YbxG9 zW0{R6mg5qJ$~Tx`z38}^SFs!)-)BBmQ}jOY8ydq<-E!G|KX1;fxl0ntHkzOj$xAeV z&EGLBWf0f%O+tBzLtyz)_G7{|()@ydM&gs)=MfJ5*%|5A{Gv4@PN>G`dFBwu0faG+ z&5)M{PFv}|?VHuGbP z_4BcnR9suW9xBV`&j;2XSTYZ;S$kH$es1^h{@%u)JvoYpPCrI?GtzaOH~66Vi2FFp zCK`n2<900UxNnZ%>fQ;`xlNo^{>oPaKLQjMW1RM}y>UO+cd6G`dC427nQQ!56BLm& zDYN(AN1sinyu5t>=FNL(W%*vv|IjzP^*+sJ*(r1NS4HE&U68%JLi*nQ{m$ysQ`89m ze?Jy1)0>6K2gm$22!oj(EeN1CjVk+vhfVP^8S%p2`%r{za_km-;*yT>kzl^rtBAP0 z*%0O&2e5|dvl%f#xQR=QhjjXTms6lPnf~edkHO!Bznp$__?YH({?j64AW|K)p6LCs>gZCD_AMkweJyeMryGj$xUU@H2 zK3z=qT?~7Gtvv157XxLF{dY0u>JHyD(B7|nxqYHX1?@+8rF}3Eru^uOfP0Ga<6`p9 z#n`V<`?uV~(1|||C}*VJ9C-P3y1c#pv|8o#A|-q7`+@AanC!V2`A{hKNM0WhoR3YC zqaBgnrm0|gx|mJM+XcUX$)J1RQvqB|23<@U+QAq*E~W->F$MYacaMtM;#c}g6r+Gt zes)k2+`bni!7Y{qw-^BR(4qXBks7DbsnV7UI=0q%Q2{G%Lm z{rSL4g$0X$EuZq^^2J|=Pd7GZcRK;71(s+4u0J<``+lH$xH#-jcCbPIT|UW^HTok$ W-Vf3O+ywLl!#n6-o={!SkIa8^W#)_k literal 0 HcmV?d00001 diff --git a/share/syscons/fonts/iso-8x8 b/share/syscons/fonts/iso-8x8 new file mode 100644 index 0000000000000000000000000000000000000000..62e50a8c95972355665ea0c34d032c24d9bb1c1a GIT binary patch literal 2048 zcmZ8iJ!m6G6n^;3298CRNs0jrDTFIh4(DLDSY&gkaFOO&48MIeo&)*7s4LS(7H zF|A3{(e?FrTU%SyKTM2CO3Tq^S?2Yi)_8p(NPxg2thkN=Bvq{>Yv*}h1D@zcKLO$Z zqUFH7I#!;fwNjEKV~ppGsqtq>y4=P~7%EC3bwPXcO66WpNTrk%gil~iq|+wV z*pjk>!9bEXsJiEPkD?gb`7ycTa(1_HB!?1IkuBpyl>uX<+euAX$6c!OWz2k{@+Hbj zRS5&IW5FVs$Mby7mh)kU^ZX;&#Wtv7e=sizr?2Xdum>Zs#d3sTsV;sL zPvOtPuTqvc`8Zl5%MV0Yyk{ssP$PY9SYTs^dz~wV{HM&9IsA8Gh#_abLY)$$kkT#q z5m$UUS7xLUucV>RKc-NCx#9vWyp-c2D!Bl_C1>F6ah-^!s5l$+_PXFl+S~UJkJ{2n z`4!(EALBRx$R7{#s;>F1q5wW6$Po|jc`RR6)RA&7WrzF1f6PEcBTkKp3sG=mtO$LP z2|9oN{G7i5sJFhp?)LWfdTn(5V32e-Kl>D{^E~b@5Z5)M!OO1)Q0ol_U$*%iOA(Bx zCDy#?u@Kb5nz6y>75?|b?HTA{#rmuBOJ+c;UAD6#ilq{9hW8&wLIUYge`km5w4SxI zJ>tShFQ@xoe}*aM5U(Ns0kB;j&wF^gs2?a#y&hqSa$^Z{Gj<{*9wf z)cvBj3p3lg)Z4&1!p{Ri@7+1{R+Yy*37c*BbO>)J5`9RzYpt~|5KVed{{Hjq!-wqg zkQ8KY#0am89~3 z_B5a#2P}CqU|;SB?A!6r%keMw^8v*a^?}7)5Hesg2&geBSgs^b2Kc#0dm8vKF)+7` zzC)*w9{a&SQaH4>gMPqd5KxZ;7MNJP&(EIUedI2GdbhA>bFNQ?a{=vXz|!|kkXyu| ztl`msZvJyx6w_Ra(D#VX_dnwEsK)^jr|0E<;*sdDr`hk{qQI%nHRm7Z0?zySfb$-4 z&LjQ@4MY9`hd%_w)p$9YH#WEjJq~jL`bd!T510%B>Ty84H^0x1r!sU{{c?pXICLbYbQY}{4xY$CKa*$zdsYsPCAaG@H z;}4+1U|1v=TpTVB5h;hNyT+KS&Q~0B`Mp0gw`b=`x4UnCZ{FXTHxos>)SSIOJ9~X` z*6?t%x!r88=NsXo+JQ{omnr?+Y?-tu=JR=)7L27uv)MGB!w}$sRqWP zRy2W&i#S%vWKh&&mm~+Q+#$E?faN7P>vcNTf|yv+(0IV;tBK=8)}Z7ygv1{{Aoc|5 zh<&JVQM`GZCZb=9Y3=3ta1*QlVuZ_Qj$T_K7q*(}kj22fS? ziZNF`eKvabw318+SC)(Iys52zWDdI7fLOp{Vd4!K9dI%Ij+<3=J*w+!#`6{>k+)z! zIX59yqI0%%(+{4b5I}X#7{`E_;tS=|PNpX55y%+g5DUOcY%r}~)BM`;jHVGumgKwE zEkN94;!G{Rz;I43Fh?n(7$rVu|3(q6biEY3kh4rM^of&v;5$RXnMe}Cs3xrJtH%bL zW4oIb9wkP1PIOox`RL_`u?EUmQMH10D+b+;wNRiH4>YH9m?=|~?oXI@#DB6gm zVxNU$i7&IOOOho%`^<}Esyb!%P<5)L>cqM_fBWXut2b}YjoN;BdAYoNsSIjF=ac6g z9n@>jaSw5^+&!WK*O}Vcb3u1%+c#)#6VuNHb98dHUdtE#h3^-FC8^vtg5-F0GY-BS zQqu|?jzFA%L2}3IhMHFJn$GjBWMiA*Rtutor1!0JK0vXYfE^3e2sLR44gq>>ZM#|G z%T2Iu?d+(BkY&29$`$Zg)SC+!%$AIhr}iS*bW!j@y%j^@>!NV zK+>6*JGb`Jwth6uMPiJLhY`u36c6kc3-$BGYZ)_^9QAh;e}73CBMV5NW**`F%rP=X uTKLBtRESzX57qPa!wq+UORQ+&9MMDS2khW~FwQj@r}9bt?zwtWzyAO~`p-lF literal 0 HcmV?d00001 diff --git a/share/syscons/fonts/koi8-8x16 b/share/syscons/fonts/koi8-8x16 new file mode 100644 index 0000000000000000000000000000000000000000..59bb441a1093eb07ee5e25c29554732dcdace442 GIT binary patch literal 4096 zcmZu!zl$436dsPS)gmk15+8Dt!sSBXZVAgBnEVMzlWMWU#>Ey_T?AQVp9}vF23H0* z{sUAP4B`%k6vCxAqKmjnYRsWZr$Vgqy&p5PlKo_B_09V+Z{9cacFvf68C<--xOo5O z;$k40!FIacP8Zv)Vq>ZXQfFP~+0E)UO!9oOSR_fV`XnE~IbaOKBoXbFWpQ=1TRwsPjXA4CzaRyoBTlCJ=o3(`gDhTiyzO@$AJj$e)Y(NCuK* znlJLH+P@s#F4xU^c{_T^>Vv4$J84dOo#;T#bA(O?E@4B{LGTp)i|KUw_d^oKwJoyB z+A51}Ee8BeB@>8pNIb2lU<_&g^a~OuM7iQ7RxQU!i>63h!Odti(&#+;xN54$LEp`? zx^~lJ806Jipf4EmFL0A%exvzdsXy*oREW<1X*fx0dpWA3=(Vlg3NCG&q=kJgZdp}j z=QrT%oH3{=M4{iXWl3TgNRyZ)2`en~ZLZa38&t>5^-&1TPxX^o9d{@K z>uHTTAM`~QN8W!h_Hv0@-E%} z%jLMFsp<_n*jaYy`&BN-CyrT9Y)ag3e52c@X}htgO+T*vc{R61LDj}4@DXZ@4dCi` zh?y#w>-i?5+Tsv6eOCQZFh^Qn=+DV~T*utP5x+X8_*!3V&8*FT6qyc=0|a$}Gwfvp zsJ$4&!_~u>FAWMj3G2S0@UCBTendi8kLYW^1zdjaNZ*{7VSlhcc7XHmrs;+#IXZ+! zgqPQ<2hZ+bAjZfBQ2yb9-jlU?qU@*%#O_Fqsh{e3>R_p7R3P4+)Peah(>Dw0$R;@x ze10R(@XaUOg_B`<@8UN(wne=jxiQ6ShxPeL$DO|>?&EoXwfFw&x7~}kpO4q`B|xtU zZcaRP`0K}VPaf>a_rL6Ze*3Wd`R}jYk1?Hx4_nDn4z)-tozPs(13r#lswxlr1C+E_ zq0@~P?OV3CYmeJ;U44`5>ucecUaxJ7v+K0@$Cux0baODqP8qZ^MD_4dIocB*ew29r zaQmz1jj{3Pn=MYAo1pY~2d|TTzMCC=6RjNP(qcYM$LRR!4n9sjW_!fn(H|Y@Sux_j ziad7l`H3%$@AGAR9P)SZojtlpgYLH+jpP8S|4N_oScUa)UZ}70*F3k*UiJ45v)$Kx z?DBIT_!9fX$JaPMKI41i<72nJ`uO49yLTTxzVZ$5?Qpmn4&S;O62J6H_PF5-%*JEs zZ%w2(Hk0E%h#R4Or51bpqgzuup48n;^$o6!5Fd~FpMU>h)Y{Lp)!fY=C%YK>@%8$8 z?DWQ*aDxpPR{**Jg!pp9@OVJ&)rR%R^&{9rKR7A_-iW^gXnsOIz8i@z{(CrEB=~%OX>UjgTMbJVLmE% zi~L`u2BN2U98>3~U~LRF)9?7Fzc&u?=SC}@R8;xzkE5v9iz4^;uqbJN2tO2$*pDKa M{}vZ0qF?y`AGpmvdjJ3c literal 0 HcmV?d00001 diff --git a/share/syscons/fonts/koi8-8x8 b/share/syscons/fonts/koi8-8x8 new file mode 100644 index 0000000000000000000000000000000000000000..8821986e32bac6d8a13ef8b0cbd769f768e60df8 GIT binary patch literal 2048 zcmYjSziZ@16n@x2X@u=`0x!N4{sV?2?5PlIFjwRsxaO+C4igt5Y_%DPBHO+HfNXh1 zn$&3x0)r~Cr8$tZY-M+ctF<6P%J)6VNuI2Idh>pMZ{CbW2FkC0zrI~w=XqcEcTatu zc3qdn2XYW+%Hu5NLUu4LWFk?LL?V6vbO-xh5_fv)66x=jx3|l?e*eq;U#qI}iE}QA z^62E-6JzGjo6Gad^Yb4ci{*#p=_&cJyxKHfv$>L;y;(Qg+d=V}MDUhShuA`oPh6?P$*7~5Gx>!K(+uw&P|V{ka2S{AP6_);?7 z8zV;2G*vlGJ3OhFtngujKxGvfS4y15i-&3<))s0P8EdVH1aE|I5tsCdgUXl|0S7Wv zNIOc@Nf5%ixRR_~t+$Vvn7IM9;;S&W)xbl*og``1g-6mBO-MPmMT53VTOk1AcnFm7 zlZDY-V>ABvT4rH=njRJ4o0*v>BHpZAuh)eag8*$+N$XtZT#M~Q->{!eihW2M>CP_v z#aTCS`>gtvn+f(9Zk3>WATLKD%0xYnF7>HlE+jUhKhjt4hUfhs!}HhA!?SmIs6o*ekj4=v+uaGi zs4j3Z0f=JZG-Hc}+su(Bo#K8Vefl)VhGEKZ9KJvJ+N)Ax$}kXG0ln5e9SP_a-!UdU zw+SszsFD-&R(bO;?oQ<{*pkCb`-IOEI!)gi<*;s4o=&yd(`}%f_AC+h8W;A;Y0q^h zxy@`Y&~NeLcgVwfsYlG7%aC8l(fwB(1AiH0+>mMy6Sbed4ED+C^5e(lDFCkOzv~sk zasC$~iA`{0L*@m1xr+r{i5&*_wI6w1=S-jTfYC0A^ulvLy<6g}}{d=x*>LX`{913qSl=z*XA-GBs~@Exii zIMB~=0S@S~{(AobD*?lfl!&5-JcC61NUqlb(Ble$p6HQ}>Hspb1Aa|A{t2v<<38N3 cfj@Di`Vxs2mf<#9U1=7z^IRAP~Cn3iW} zIDm*()BNt;T(%NOMpc;{f6Be14}7I$F(zfArwd1JL)& zMJWq$JWZGBRP`U1^VPauujb`r7Vp_k@1Q>Db?lz1r!XB4y~Bo%1L0HTkEYY<&)12V zcU6`YRaGQWlrmwF4zZ{C6dp_JKYar=2O?cj9jTOU^s_qaw~U*zEY&)%-YV+i zRwM5wiF5vVECW23H1IWw{0-QoINzv02=(T+Mgi;mN6T^Ss?*Y0`=oL{g3~IB`&sow zwk1W89A5yhW99)(CIWpzmnn{9p&v&~aT8&lZc@!Q>mWP+tY8W*Hycbl8>-Um*t6uH z;%x}6$%GIS#W)$j7D(}Nm8e&06TD5WwI1GRKP|>JT<*#K8em+tp0ykCO_hYk!lHtIz~qo)q6@jFILS@?%mTyNE|P>=(ymU-OI7OxpNcYdUBL5b}b} z(3b*GeNn~^pATc+)=2QAiTi@g`+3d&5eQ8@B6pz#ynnu>FOKu3K6pPW!0+$&`&R_X z?R`juIJ{Oq`1J4sF-8hN@y8a-o}`Tvc}GJa@+~!{d8+-?L8wQRAg)fDz}(FAWFi^q z#7Dv(U&v?kGj&yI6CiVe}?o2i53U*sFY#jq0C+?I4LkR ztM;Rf`p}QNrsj!Xim{@gH^x=yZ>*+=<+?mfMUE%tgR{Oruz$cKYasI5H@Erd6XQqz zADO76_wRpo@7^0>@I*h=uU{)q!^BZX`FYVb)%bjUJ`Vl9N3URz?7tbK--w36CE@FC zS2FkU)4yK+_VU-Kte>r6p6Ah$__&Mr7n9;CN-#}?zmjC@-mi~RX{eHl` z%}2-iLG)XEOCC%34wtJRe*kH`G)xe^kDo>lxA8UyJYc;(wbqjeN*CY2wiq2>Or@Z|9Bv zmFNeT>HT-=>l?fD1`zhMdaK?jc>D!9?9p%F<$~DoRkaw6MvEZFm2*E7FIFj=xIwh# zy${Iu>_?(P{@_X(^ygebPI!P%=m-1{a%gi#M9%kFvY`G6_*9f)U%+qh*Y*hgA>cRY zlU&F diff --git a/share/syscons/keymaps/danish.cp865 b/share/syscons/keymaps/danish.cp865 new file mode 100644 index 0000000000..9666557cfb --- /dev/null +++ b/share/syscons/keymaps/danish.cp865 @@ -0,0 +1,127 @@ +/* + * This program is free software; you can do with it as you see fit, + * as long as you leave this header intact. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Soren Schmidt Email: sos@kmd-ac.dk + * Tritonvej 36 UUCP: ...uunet!dkuug!kmd-ac!sos + * DK9210 Aalborg SO Phone: +45 9814 8076 + */ + +keymap_t keymap = { 107, /* DK codepage 865 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, NOP, NOP, 0x1B, 0x1B, NOP, NOP, 0x33, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '"', 0x00, 0x00, '@', '"', 0x00, 0x00, 0x00, 0x00, +/* sc=04 */ '3', '#', NOP, NOP, 0x9C, '#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', 0xAF, NOP, NOP, '$', 0xAF, NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '&', NOP, NOP, '6', '&', NOP, NOP, 0x33, 0x00, +/* sc=08 */ '7', '/', NOP, NOP, '{', '/', NOP, NOP, 0x33, 0x00, +/* sc=09 */ '8', '(', 0x1B, 0x1B, '[', '(', 0x1B, 0x1B, 0x00, 0x00, +/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ')', 0x1D, 0x1D, 0x00, 0x00, +/* sc=0b */ '0', '=', NOP, NOP, '}', '=', NOP, NOP, 0x33, 0x00, +/* sc=0c */ '+', '?', NOP, NOP, '+', '?', NOP, NOP, 0x33, 0x00, +/* sc=0d */ '\'', '`', NOP, NOP, '|', '`', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x33, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ 0x86, 0x8F, NOP, NOP, 0xE5, 0xC5, NOP, NOP, 0x33, 0x01, +/* sc=1b */ '"', '^', 0x1E, 0x1E, '~', '^', 0x1E, 0x1E, 0x00, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ 0x91, 0x92, NOP, NOP, 0xE6, 0xC6, NOP, NOP, 0x33, 0x01, +/* sc=28 */ 0x9B, 0x9D, NOP, NOP, 0xF8, 0xD8, NOP, NOP, 0x33, 0x01, +/* sc=29 */ 0xAB, '~', NOP, NOP, 0xAB, '~', NOP, NOP, 0x33, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '\'', '*', NOP, NOP, '\'', '*', NOP, NOP, 0x33, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', '*', '*', '*', '*', '*', '*', 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00, +/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '/', '/', '/', '/', '/', '/', '/', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', '.', '.', '.', '.', '.', '.', 0x00, 0x02, +/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ '<', '>', 0x1C, 0x1C, '\\', '>', 0x1C, 0x1C, 0x00, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02, +/* sc=5c */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x02, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=5f */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=60 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=61 */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=62 */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=63 */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=64 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=65 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=66 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=67 */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=69 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +}; diff --git a/share/syscons/keymaps/danish.iso b/share/syscons/keymaps/danish.iso new file mode 100644 index 0000000000..95eaeef5bc --- /dev/null +++ b/share/syscons/keymaps/danish.iso @@ -0,0 +1,127 @@ +/* + * This program is free software; you can do with it as you see fit, + * as long as you leave this header intact. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Soren Schmidt Email: sos@kmd-ac.dk + * Tritonvej 36 UUCP: ...uunet!dkuug!kmd-ac!sos + * DK9210 Aalborg SO Phone: +45 9814 8076 + */ + +keymap_t keymap = { 107, /* DK iso8859 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, NOP, NOP, 0x1B, 0x1B, NOP, NOP, 0x33, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '"', 0x00, 0x00, '@', '"', 0x00, 0x00, 0x00, 0x00, +/* sc=04 */ '3', '#', NOP, NOP, 0x9E, '#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', 0xA4, NOP, NOP, '$', 0xA4, NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '&', NOP, NOP, '6', '&', NOP, NOP, 0x33, 0x00, +/* sc=08 */ '7', '/', NOP, NOP, '{', '/', NOP, NOP, 0x33, 0x00, +/* sc=09 */ '8', '(', 0x1B, 0x1B, '[', '(', 0x1B, 0x1B, 0x00, 0x00, +/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ')', 0x1D, 0x1D, 0x00, 0x00, +/* sc=0b */ '0', '=', NOP, NOP, '}', '=', NOP, NOP, 0x33, 0x00, +/* sc=0c */ '+', '?', NOP, NOP, '+', '?', NOP, NOP, 0x33, 0x00, +/* sc=0d */ '\'', '`', NOP, NOP, '|', '`', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x33, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ 0xE5, 0xC5, NOP, NOP, 0x86, 0x8F, NOP, NOP, 0x33, 0x01, +/* sc=1b */ '"', '^', 0x1E, 0x1E, '~', '^', 0x1E, 0x1E, 0x00, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ 0xE6, 0xC6, NOP, NOP, 0x91, 0x92, NOP, NOP, 0x33, 0x01, +/* sc=28 */ 0xF8, 0xD8, NOP, NOP, 0x9B, 0x9D, NOP, NOP, 0x33, 0x01, +/* sc=29 */ 0xBD, 0xA7, NOP, NOP, 0xBD, 0xA7, NOP, NOP, 0x33, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '\'', '*', NOP, NOP, '\'', '*', NOP, NOP, 0x33, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', '*', '*', '*', '*', '*', '*', 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00, +/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '/', '/', '/', '/', '/', '/', '/', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', '.', '.', '.', '.', '.', '.', 0x00, 0x02, +/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ '<', '>', 0x1C, 0x1C, '\\', '>', 0x1C, 0x1C, 0x00, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02, +/* sc=5c */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x02, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=5f */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=60 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=61 */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=62 */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=63 */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=64 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=65 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=66 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=67 */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=69 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +}; diff --git a/share/syscons/keymaps/fff b/share/syscons/keymaps/fff new file mode 100644 index 0000000000..c8e12fdf8b --- /dev/null +++ b/share/syscons/keymaps/fff @@ -0,0 +1,127 @@ +/* + * This program is free software; you can do with it as you see fit, + * as long as you leave this header intact. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Soren Schmidt Email: sos@kmd-ac.dk + * Tritonvej 36 UUCP: ...uunet!dkuug!kmd-ac!sos + * DK9210 Aalborg SO Phone: +45 9814 8076 + */ + +keymap_t keymap = { 107, /* DK iso8859 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, NOP, NOP, 0x1B, 0x1B, NOP, NOP, 0x33, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '"', 0x00, 0x00, '@', '"', 0x00, 0x00, 0x00, 0x00, +/* sc=04 */ '3', '#', NOP, NOP, 0x9E, '#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', 0xA4, NOP, NOP, '$', 0xA4, NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '&', NOP, NOP, '6', '&', NOP, NOP, 0x33, 0x00, +/* sc=08 */ '7', '/', NOP, NOP, '{', '/', NOP, NOP, 0x33, 0x00, +/* sc=09 */ '8', '(', 0x1B, 0x1B, '[', '(', 0x1B, 0x1B, 0x00, 0x00, +/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ')', 0x1D, 0x1D, 0x00, 0x00, +/* sc=0b */ '0', '=', NOP, NOP, '}', '=', NOP, NOP, 0x33, 0x00, +/* sc=0c */ '+', '?', NOP, NOP, '+', '?', NOP, NOP, 0x33, 0x00, +/* sc=0d */ '\'', '`', NOP, NOP, '|', '`', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x33, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ 0xE5, 0xC5, NOP, NOP, 0x86, 0x8F, NOP, NOP, 0x33, 0x01, +/* sc=1b */ '"', '^', 0x1E, 0x1E, '~', '^', 0x1E, 0x1E, 0x00, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ 0xE6, 0xC6, NOP, NOP, 0x91, 0x92, NOP, NOP, 0x33, 0x01, +/* sc=28 */ 0xF8, 0xD8, NOP, NOP, 0x9B, 0x9D, NOP, NOP, 0x33, 0x01, +/* sc=29 */ 0xBD, 0xA7, NOP, NOP, 0xBD, 0xA7, NOP, NOP, 0x33, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '\'', '*', NOP, NOP, '\'', '*', NOP, NOP, 0x33, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', '*', '*', '*', '*', '*', '*', 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00, +/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '/', '/', '/', '/', '/', '/', '/', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', '.', '.', '.', '.', '.', '.', 0x00, 0x02, +/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ '<', '>', 0x1C, 0x1C, '\\', '>', 0x1C, 0x1C, 0x00, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '#', '¤', '/', '/', '/', '/', '/', '/', 0x00, 0x02, +/* sc=5c */ '1', '2', '*', '*', '*', '*', '*', '*', 0x00, 0x02, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=5f */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=60 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=61 */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=62 */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=63 */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=64 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=65 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=66 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=67 */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=69 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +}; diff --git a/share/syscons/keymaps/german.cp850 b/share/syscons/keymaps/german.cp850 new file mode 100644 index 0000000000..608cbc1ed1 --- /dev/null +++ b/share/syscons/keymaps/german.cp850 @@ -0,0 +1,127 @@ +/* + * This program is free software; you can do with it as you see fit, + * as long as you leave this header intact. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Soren Schmidt Email: sos@kmd-ac.dk + * Tritonvej 36 UUCP: ...uunet!dkuug!kmd-ac!sos + * DK9210 Aalborg SO Phone: +45 9814 8076 + */ + +keymap_t keymap = { 107, /* german codepage 850 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '"', NOP, NOP, 0xFD, 0xFD, NOP, NOP, 0x33, 0x00, +/* sc=04 */ '3', 0xF5, NOP, NOP, 0xFC, 0xFC, NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '&', NOP, NOP, '6', '&', NOP, NOP, 0x33, 0x00, +/* sc=08 */ '7', '/', NOP, NOP, '{', '{', NOP, NOP, 0x33, 0x00, +/* sc=09 */ '8', '(', 0x1B, 0x1B, '[', '[', 0x1B, 0x1B, 0x00, 0x00, +/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ']', 0x1D, 0x1D, 0x00, 0x00, +/* sc=0b */ '0', '=', NOP, NOP, '}', '}', NOP, NOP, 0x33, 0x00, +/* sc=0c */ 0xE1, '?', 0x1C, 0x1C, '\\', '\\', 0x1C, 0x1C, 0x00, 0x00, +/* sc=0d */ 0xB3, 0xB4, NOP, NOP, 0xB3, 0xB4, NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, '@', '@', 0x00, 0x00, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ 0x81, 0x9A, NOP, NOP, 0x81, 0x9A, NOP, NOP, 0x33, 0x01, +/* sc=1b */ '+', '*', NOP, NOP, '~', '~', NOP, NOP, 0x33, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ 0x94, 0x99, NOP, NOP, 0x94, 0x99, NOP, NOP, 0x33, 0x01, +/* sc=28 */ 0x84, 0x8E, NOP, NOP, 0x84, 0x8E, NOP, NOP, 0x33, 0x01, +/* sc=29 */ '^', 0xF8, 0x1E, 0x1E, '^', 0xF8, 0x1E, 0x1E, 0x00, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '#', '\'', NOP, NOP, '#', '\'', NOP, NOP, 0x33, 0x00, +/* sc=2c */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 0xE6, 0xE6, NOP, NOP, 0x03, 0x01, +/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00, +/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '-', '-', '-', '-', '-', '-', '-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, ',', 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x02, +/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ '<', '>', NOP, NOP, '|', '|', NOP, NOP, 0x33, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02, +/* sc=5c */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x02, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=5f */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=60 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=61 */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=62 */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=63 */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=64 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=65 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=66 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=67 */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=69 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +}; diff --git a/share/syscons/keymaps/german.iso b/share/syscons/keymaps/german.iso new file mode 100644 index 0000000000..2636674d06 --- /dev/null +++ b/share/syscons/keymaps/german.iso @@ -0,0 +1,127 @@ +/* + * This program is free software; you can do with it as you see fit, + * as long as you leave this header intact. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Soren Schmidt Email: sos@kmd-ac.dk + * Tritonvej 36 UUCP: ...uunet!dkuug!kmd-ac!sos + * DK9210 Aalborg SO Phone: +45 9814 8076 + */ + +keymap_t keymap = { 107, /* german iso8859 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, '`', '`', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '"', 0x00, 0x00, '@', '@', 0x00, 0x00, 0x00, 0x00, +/* sc=04 */ '3', 0xA7, NOP, NOP, '#', '#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '&', 0x1E, 0x1E, '^', '^', 0x1E, 0x1E, 0x00, 0x00, +/* sc=08 */ '7', '/', 0x1B, 0x1B, '[', '[', 0x1B, 0x1B, 0x00, 0x00, +/* sc=09 */ '8', '(', NOP, NOP, '8', '(', NOP, NOP, 0x33, 0x00, +/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ']', 0x1D, 0x1D, 0x00, 0x00, +/* sc=0b */ '0', '=', NOP, NOP, '{', '{', NOP, NOP, 0x33, 0x00, +/* sc=0c */ 0xDF, '?', NOP, NOP, '|', '|', NOP, NOP, 0x33, 0x00, +/* sc=0d */ 0x92, 0x93, NOP, NOP, '\'', '`', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ 0xFC, 0xDC, 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x01, +/* sc=1b */ '+', '*', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ 0xF6, 0xD6, NOP, NOP, 0xF6, 0xD6, NOP, NOP, 0x33, 0x01, +/* sc=28 */ 0xE4, 0xC4, NOP, NOP, 0xE4, 0xC4, NOP, NOP, 0x33, 0x01, +/* sc=29 */ '<', '>', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '#', '^', 0x1E, 0x1E, '`', '~', 0x1E, 0x1E, 0x00, 0x00, +/* sc=2c */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00, +/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x02, +/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02, +/* sc=5c */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x02, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=5f */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=60 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=61 */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=62 */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=63 */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=64 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=65 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=66 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=67 */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=69 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +}; diff --git a/share/syscons/keymaps/mkkbdfil.c b/share/syscons/keymaps/mkkbdfil.c new file mode 100644 index 0000000000..d96056c8df --- /dev/null +++ b/share/syscons/keymaps/mkkbdfil.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 1992, 1993 Søren Schmidt + * + * This program is free software; you may redistribute it and/or + * modify it, provided that it retain the above copyright notice + * and the following disclaimer. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Søren Schmidt Email: sos@kmd-ac.dk + * Tritonvej 36 UUCP: ...uunet!dkuug!kmd-ac!sos + * DK9210 Aalborg SO Phone: +45 9814 8076 + */ + +#include +#include +#include +#include + +#include FIL + +main(int argc, char **argv) +{ + FILE *fd; + + if (argc == 2) { + fd = fopen(argv[1], "w"); + if (fd == NULL) { + printf("%s: cannot create file\n", argv[0]); + exit(1); + } + fwrite(&keymap, sizeof(keymap_t), 1, fd); + fclose(fd); + exit(0); + } + else + printf("usage: mkkbdfil \n"); + exit(1); +} diff --git a/share/syscons/keymaps/ru.koi8.map b/share/syscons/keymaps/ru.koi8.map new file mode 100644 index 0000000000000000000000000000000000000000..19d49d8c30188b8b6870c5a2fb54e019714b7e9f GIT binary patch literal 2562 zcmeIy^;4Wj9LMp`KEMG7hu`5)hyZsSoC}1w5(p5a-qhVKZ4<&1ha|*Z+onpIMu=94 zySuv(cTYC^47&|KF#Q8EdC%P3cXsBU=XPi3RSOYCbTlw&5@3Zn0hsfhGFe&zb8BRo z&5eNS>B^d|F);53WwJK`=KrWnj+f%%795;96>v5M7XGBHx%9xIDrIsf1B-uACQ}M9 z`&VT$c8iand1%@+Aj<&!JwsXZ@hK}SFQ*jnWxuUppYHgiAUOE)mJuUBx<4HreskBj zaUgHB^YS{eqJMvoPrHVMT-iEuB*^DQVPV&{j~)&3^|O(YxA#n#0P;n#R(pNNm@y#l zbn^ABT~kp3@=;!3;KfbDhJk#X9~5+H^YGyy&-aLq-gj5o`-`Qqu?Ox?nF44uYISAh zph4`v@F-qsMaYpQy?cXvRu~$3b=#;>Ag{Oe^gO=2Uq6sH+pAP3R}L5e@>U0L?^CM= z4g`6-qmR$&)#c?N?`EsjXV$Xkk@q_L`JG)icreKOIsX3V)(;s1l9>Vg`G;}_Nr}ME z-z(G0y@10Hlu6=IywEcyX8*m(lYwuZi;B9lcj82lubzpBxV3xyc#zlHcz7IJ*0(Rn zmnAygjh$o1g1p*Vl8!Fz(+A|kE&%}-HVz#M@ zQ8YF+>lntO@ZwRZcodzTPUk|FEakXdu0 zWyw-0k6m6~{i%sLtCN;R(bUpBTgRgHZdeZZK-v%Cxm|+F9`ou zof7`-yDR)Va9{Yh_m1#y^=a-OIYhaCq#^%So)rG=xh?$LbyN7a(2@QmLC`X?YzPLBm3`h|H$-e?jLE$zb%)Ae+M53|F&Hf{%trf{998i{M&v_ h__zC(@Nel+?jNZ+%>5%z{Cnb`O7%!@Vcz5U{SZAhxikO( literal 0 HcmV?d00001 diff --git a/share/syscons/keymaps/rus.koi8.map b/share/syscons/keymaps/rus.koi8.map new file mode 100644 index 0000000000000000000000000000000000000000..dfe7fd9e7adf20841882b0627db5fc83b465942d GIT binary patch literal 2562 zcmeH_<#O9V6ot=SQyg1nJ75Ykn}cOY15E>EW@b*?hv`54?F*EdnVFfn-AdJFrmxT$ ze`j`d=8m*`_gpQ<5FCC1P+t$^=K~QmGyubzly@i+0Seoc8bzZ(QGx1WF`&3nb@6y8 zG^;8U0woeaNsU@dCV>j&+X$snKv|jMP*(?(m#eP2Ivk!oI~)dDT7b$*wYEzDm=%Hw zz}n+ZXU}t|6SVn2R@USPSy`Zkmkfr{p9}`jx)Uzfq!%t1Xu~0or|*%+1KN5zCui#G zoE*@`BVKR+6R#Jv^oH3y{)gENT7AoI@BU@CgVtPkI3|8~I6%{n`u*FV`u(7pcY?tk zTsfB{DKk@DS}$rp9CHs74S!4oT7S*$?)~O=gBD!T>qmXj>p_e58I5D^8I7RDXHBND zZ%ro9lJglE%f%xr(7>_^K_S}YS@S}dRy2d&nQhgK`7=`zsxSAIk(YU?Kvbjrz5P*dP)9MkCHD6B1XH;rv_a}?DTd78&HdpHaN z4x@l$LPC?wI+B`X)|t{Iv#x4QGV6|NlG((VCYklbHOXvJr6!s6)@zbkUxOx@^+zi*aVq!UOd@V_VU2Lf8` AbpQYW literal 0 HcmV?d00001 diff --git a/share/syscons/keymaps/swedish.cp850 b/share/syscons/keymaps/swedish.cp850 new file mode 100644 index 0000000000..7e3f38ba79 --- /dev/null +++ b/share/syscons/keymaps/swedish.cp850 @@ -0,0 +1,127 @@ +/* + * This program is free software; you can do with it as you see fit, + * as long as you leave this header intact. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Soren Schmidt Email: sos@kmd-ac.dk + * Tritonvej 36 UUCP: ...uunet!dkuug!kmd-ac!sos + * DK9210 Aalborg SO Phone: +45 9814 8076 + */ + +keymap_t keymap = { 107, /* swedish codepage 850 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, NOP, NOP, NOP, NOP, 0x0F, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '"', 0x00, 0x00, '@', '@', 0x00, 0x00, 0x00, 0x00, +/* sc=04 */ '3', '#', NOP, NOP, 0x9C, 0x9C, NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', 0xCF, NOP, NOP, '$', '$', NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '&', NOP, NOP, '6', '&', NOP, NOP, 0x33, 0x00, +/* sc=08 */ '7', '/', NOP, NOP, '{', '{', NOP, NOP, 0x33, 0x00, +/* sc=09 */ '8', '(', 0x1B, 0x1B, '[', '[', 0x1B, 0x1B, 0x00, 0x00, +/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ']', 0x1D, 0x1D, 0x00, 0x00, +/* sc=0b */ '0', '=', NOP, NOP, '}', '}', NOP, NOP, 0x33, 0x00, +/* sc=0c */ '+', '?', 0x1C, 0x1C, '\\', '\\', 0x1C, 0x1C, 0x00, 0x00, +/* sc=0d */ 0xB3, 0xB4, NOP, NOP, 0xB3, 0xB4, NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ 0x86, 0x8F, NOP, NOP, 0x86, 0x8F, NOP, NOP, 0x33, 0x01, +/* sc=1b */ 0xB1, 0xB0, 0x1E, 0x1E, 0xB2, 0xB2, NOP, NOP, 0x03, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ 0x94, 0x99, NOP, NOP, 0x94, 0x99, NOP, NOP, 0x33, 0x01, +/* sc=28 */ 0x84, 0x8E, NOP, NOP, 0x84, 0x8E, NOP, NOP, 0x33, 0x01, +/* sc=29 */ 0xF5, 0xAB, NOP, NOP, 0xF5, 0xAB, NOP, NOP, 0x33, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '\'', '*', NOP, NOP, '\'', '*', NOP, NOP, 0x33, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00, +/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '-', '-', '-', '-', '-', '-', '-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x02, +/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ '<', '>', NOP, NOP, '|', '|', NOP, NOP, 0x33, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02, +/* sc=5c */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x02, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=5f */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=60 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=61 */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=62 */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=63 */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=64 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=65 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=66 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=67 */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=69 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +}; diff --git a/share/syscons/keymaps/swedish.iso b/share/syscons/keymaps/swedish.iso new file mode 100644 index 0000000000..c6e36107b0 --- /dev/null +++ b/share/syscons/keymaps/swedish.iso @@ -0,0 +1,127 @@ +/* + * This program is free software; you can do with it as you see fit, + * as long as you leave this header intact. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Soren Schmidt Email: sos@kmd-ac.dk + * Tritonvej 36 UUCP: ...uunet!dkuug!kmd-ac!sos + * DK9210 Aalborg SO Phone: +45 9814 8076 + */ + +keymap_t keymap = { 107, /* swedish iso8859 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, +/* sc=03 */ '2', '"', NOP, NOP, NOP, '@', NOP, NOP, 0x3B, 0x00, +/* sc=04 */ '3', 0xA3, NOP, NOP, NOP, '#', NOP, NOP, 0x3B, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, +/* sc=07 */ '6', '&', 0x1E, NOP, NOP, '^', 0x1E, NOP, 0x19, 0x00, +/* sc=08 */ '7', '/', NOP, NOP, NOP, '&', NOP, NOP, 0x3B, 0x00, +/* sc=09 */ '8', '(', NOP, NOP, NOP, '*', NOP, NOP, 0x3B, 0x00, +/* sc=0a */ '9', ')', NOP, NOP, NOP, '(', NOP, NOP, 0x3B, 0x00, +/* sc=0b */ '0', '=', NOP, NOP, NOP, ')', NOP, NOP, 0x3B, 0x00, +/* sc=0c */ '+', '?', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, +/* sc=0d */ 0xB4, '`', NOP, NOP, '=', '+', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ 0xE5, 0xC5, NOP, NOP, '[', '{', 0x1B, NOP, 0x31, 0x01, +/* sc=1b */ 0xA8, '^', NOP, NOP, ']', '}', 0x1D, NOP, 0x31, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ 0xF8, 0xD8, NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x01, +/* sc=28 */ 0xE6, 0xC6, NOP, NOP, '\'', '"', NOP, NOP, 0x33, 0x01, +/* sc=29 */ '<', '>', NOP, NOP, '\\', '|', 0x1C, NOP, 0x31, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '\'', '*', NOP, NOP, '`', '~', NOP, NOP, 0x33, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', ';', NOP, NOP, NOP, '<', NOP, NOP, 0x3B, 0x00, +/* sc=34 */ '.', ':', NOP, NOP, NOP, '>', NOP, NOP, 0x3B, 0x00, +/* sc=35 */ '-', '_', 0x1F, NOP, '/', '?', NOP, NOP, 0x13, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00, +/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x02, +/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02, +/* sc=5c */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x02, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=5f */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=60 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=61 */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=62 */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=63 */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=64 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=65 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=66 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=67 */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=69 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +}; diff --git a/share/syscons/keymaps/uk.cp850 b/share/syscons/keymaps/uk.cp850 new file mode 100644 index 0000000000..46529cafaf --- /dev/null +++ b/share/syscons/keymaps/uk.cp850 @@ -0,0 +1,127 @@ +/* + * This program is free software; you can do with it as you see fit, + * as long as you leave this header intact. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Soren Schmidt Email: sos@kmd-ac.dk + * Tritonvej 36 UUCP: ...uunet!dkuug!kmd-ac!sos + * DK9210 Aalborg SO Phone: +45 9814 8076 + */ + +keymap_t keymap = { 107, /* uk codepage 850 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '"', NOP, NOP, '2', '"', NOP, NOP, 0x33, 0x00, +/* sc=04 */ '3', 0x9C, NOP, NOP, '3', 0x9C, NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '^', 0x1E, 0x1E, '6', '^', 0x1E, 0x1E, 0x00, 0x00, +/* sc=08 */ '7', '&', NOP, NOP, '7', '&', NOP, NOP, 0x33, 0x00, +/* sc=09 */ '8', '*', NOP, NOP, '8', '*', NOP, NOP, 0x33, 0x00, +/* sc=0a */ '9', '(', NOP, NOP, '9', '(', NOP, NOP, 0x33, 0x00, +/* sc=0b */ '0', ')', NOP, NOP, '0', ')', NOP, NOP, 0x33, 0x00, +/* sc=0c */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, +/* sc=0d */ '=', '+', NOP, NOP, '=', '+', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x00, +/* sc=1b */ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ ';', ':', NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x00, +/* sc=28 */ '\'', '@', 0x00, 0x00, '\'', '@', 0x00, 0x00, 0x00, 0x00, +/* sc=29 */ '`', 0xAA, NOP, NOP, '|', '|', NOP, NOP, 0x33, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '#', '~', NOP, NOP, '#', '~', NOP, NOP, 0x33, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', '<', NOP, NOP, ',', '<', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', '>', NOP, NOP, '.', '>', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '/', '?', NOP, NOP, '/', '?', NOP, NOP, 0x33, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00, +/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '-', '-', '-', '-', '-', '-', '-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x02, +/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02, +/* sc=5c */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x02, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=5f */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=60 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=61 */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=62 */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=63 */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=64 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=65 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=66 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=67 */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=69 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +}; diff --git a/share/syscons/keymaps/uk.iso b/share/syscons/keymaps/uk.iso new file mode 100644 index 0000000000..3c4c4d3ecf --- /dev/null +++ b/share/syscons/keymaps/uk.iso @@ -0,0 +1,127 @@ +/* + * This program is free software; you can do with it as you see fit, + * as long as you leave this header intact. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Soren Schmidt Email: sos@kmd-ac.dk + * Tritonvej 36 UUCP: ...uunet!dkuug!kmd-ac!sos + * DK9210 Aalborg SO Phone: +45 9814 8076 + */ + +keymap_t keymap = { 107, /* uk iso8859 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, '`', '`', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '"', 0x00, 0x00, '@', '@', 0x00, 0x00, 0x00, 0x00, +/* sc=04 */ '3', 0xA3, NOP, NOP, '#', '#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '^', 0x1E, 0x1E, '^', '^', 0x1E, 0x1E, 0x00, 0x00, +/* sc=08 */ '7', '&', NOP, NOP, '[', '[', 0x1B, 0x1B, 0x30, 0x00, +/* sc=09 */ '8', '*', NOP, NOP, '8', '*', NOP, NOP, 0x33, 0x00, +/* sc=0a */ '9', '(', NOP, NOP, ']', ']', 0x1D, 0x1D, 0x30, 0x00, +/* sc=0b */ '0', ')', NOP, NOP, '{', '{', NOP, NOP, 0x33, 0x00, +/* sc=0c */ '-', '_', 0x1F, 0x1F, '|', '|', 0x1F, 0x1F, 0x00, 0x00, +/* sc=0d */ '=', '+', NOP, NOP, '}', '}', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x00, +/* sc=1b */ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ ';', ':', NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x00, +/* sc=28 */ '\'', '@', 0x00, 0x00, '\'', '@', 0x00, 0x00, 0x00, 0x00, +/* sc=29 */ '\\', '|', 0x1C, 0x1C, '\\', '\\', 0x1C, 0x1C, 0x00, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '#', '~', NOP, NOP, '~', '~', NOP, NOP, 0x33, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', '<', NOP, NOP, ',', '<', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', '>', NOP, NOP, '.', '>', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '/', '?', NOP, NOP, '/', '?', NOP, NOP, 0x33, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00, +/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x02, +/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02, +/* sc=5c */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x02, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=5f */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=60 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=61 */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=62 */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=63 */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=64 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=65 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=66 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=67 */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=69 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +}; diff --git a/share/syscons/keymaps/us.iso b/share/syscons/keymaps/us.iso new file mode 100644 index 0000000000..8411d74486 --- /dev/null +++ b/share/syscons/keymaps/us.iso @@ -0,0 +1,127 @@ +/* + * This program is free software; you can do with it as you see fit, + * as long as you leave this header intact. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Soren Schmidt Email: sos@kmd-ac.dk + * Tritonvej 36 UUCP: ...uunet!dkuug!kmd-ac!sos + * DK9210 Aalborg SO Phone: +45 9814 8076 + */ + +keymap_t keymap = { 107, /* US iso8859 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, NOP, NOP, 0x1B, 0x1B, NOP, NOP, 0x33, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '@', 0x00, 0x00, '2', '@', 0x00, 0x00, 0x00, 0x00, +/* sc=04 */ '3', '#', NOP, NOP, '3', '#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '^', 0x1E, 0x1E, '6', '^', 0x1E, 0x1E, 0x00, 0x00, +/* sc=08 */ '7', '&', NOP, NOP, '7', '&', NOP, NOP, 0x33, 0x00, +/* sc=09 */ '8', '*', NOP, NOP, '8', '*', NOP, NOP, 0x33, 0x00, +/* sc=0a */ '9', '(', NOP, NOP, '9', '(', NOP, NOP, 0x33, 0x00, +/* sc=0b */ '0', ')', NOP, NOP, '0', ')', NOP, NOP, 0x33, 0x00, +/* sc=0c */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, +/* sc=0d */ '=', '+', NOP, NOP, '=', '+', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x33, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x00, +/* sc=1b */ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ ';', ':', NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x00, +/* sc=28 */ '\'', '"', NOP, NOP, '\'', '"', NOP, NOP, 0x33, 0x00, +/* sc=29 */ '`', '~', NOP, NOP, '`', '~', NOP, NOP, 0x33, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', '<', NOP, NOP, ',', '<', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', '>', NOP, NOP, '.', '>', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '/', '?', NOP, NOP, '/', '?', NOP, NOP, 0x33, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00, +/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '-', '-', '-', '-', '-', '-', '-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x02, +/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02, +/* sc=5c */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x02, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=5f */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=60 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=61 */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=62 */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=63 */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=64 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=65 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=66 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=67 */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=69 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +}; diff --git a/share/syscons/scrnmaps/Makefile b/share/syscons/scrnmaps/Makefile new file mode 100644 index 0000000000..3c35575244 --- /dev/null +++ b/share/syscons/scrnmaps/Makefile @@ -0,0 +1,10 @@ +SCRMAPS = koi82alt.scr + +SCRDIR = /usr/share/syscons/scrnmaps + +install: + cd ${.CURDIR}; \ + install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${SCRMAPS} \ + ${DESTDIR}${SCRDIR} + +.include diff --git a/share/syscons/scrnmaps/koi82alt.scr b/share/syscons/scrnmaps/koi82alt.scr new file mode 100644 index 0000000000000000000000000000000000000000..6c6256d260d778f1e74641f30b6c9279b5bf7d2d GIT binary patch literal 256 zcmV+b0ssC00RjUA1qKHQ2?`4g4Gs?w5fT#=6&4p585$cL9UdPbAtECrB_<~*DJm;0 zEiNxGF)}kWH8wXmIXXK$Jw87`K|(`BMMg(RNlHshO-@fxQBqS>RaRG6Sz23MU0z>c zVPa!sWoBn+X=-b1ZEkOHadLBXb#`}nd3t+%eSUw$v)aGF*~7HL!NuR)+uh!)YHhmy}ZW9%(d{gxzW!;}L>F4U6fPt2Tg_MJph>41ejE# /dev/null - -clean: - rm -f termcap - -cleandir: clean - rm -f ${MAN5} + ed - ${.CURDIR}/termcap.src < ${.CURDIR}/reorder > /dev/null beforeinstall: - install -o ${BINOWN} -g ${BINGRP} -m 444 termcap \ + install ${COPY} -o ${BINOWN} -g ${BINGRP} -m 444 termcap \ ${DESTDIR}${BINDIR}/misc install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/map3270 \ ${DESTDIR}${BINDIR}/misc + rm -f ${DESTDIR}/etc/termcap + ln -s ${BINDIR}/misc/termcap ${DESTDIR}/etc/termcap .include diff --git a/share/termcap/reorder b/share/termcap/reorder index dbc1b43cad..dbc5f7537e 100644 --- a/share/termcap/reorder +++ b/share/termcap/reorder @@ -1,4 +1,4 @@ -/# << EOH -/k a +/# << EOH -/ka /|adm12|/;.,/^[^ ]/-m'a /|pcplot/;.,/^[^ ]/-m'a /|c100|/;.,/^[^ ]/-m'a @@ -58,5 +58,5 @@ /|5620|/;.,/^[^ ]/-m'a /^s/;.,/^#/-m'a 'ad -w! termcap +w termcap q diff --git a/share/termcap/termcap.src b/share/termcap/termcap.src index f46aa6ec30..b8d7f4e866 100644 --- a/share/termcap/termcap.src +++ b/share/termcap/termcap.src @@ -1895,6 +1895,26 @@ ce|c100-s|concept-s|concept100-s|slow concept 100:\ :vb=\Ek\200\EK:pt:dC@:dN@:tc=c100: cf|c100-rv-s|concept-rv-s|concept100-rv-s|c100rvs|slow reverse concept 100:\ :vb=\EK\200\Ek:pt:dC@:dN@:tc=c100-rv: +# for syscons +cons25|ansi|ansi80x25:\ + :al=\E[L:am:bs:cd=\E[J:ce=\E[K:cl=\E[2J\E[H:cm=\E[%i%d;%dH:co#80:\ + :dc=\E[P:dl=\E[M:do=\E[B:bt=\E[Z:ei=:ho=\E[H:ic=\E[@:im=:li#25:\ + :nd=\E[C:pt:so=\E[7m:se=\E[m:us=\E[4m:ue=\E[m:up=\E[A:\ + :k1=\E[M:k2=\E[N:k3=\E[O:k4=\E[P:k5=\E[Q:k6=\E[R:\ + :k7=\E[S:k8=\E[T:k9=\E[U:k0=\E[V:k10=\E[V:k11=\E[W:k12=\E[X:\ + :kb=^h:ku=\E[A:kd=\E[B:kl=\E[D:kr=\E[C:eo:sf=\E[S:sr=\E[T:\ + :mb=\E[5m:md=\E[1m:me=\E[m: +cons50|ansil|ansi80x50:\ + :li#50:tc=cons25: +dosansi|ANSI.SYS standard crt:\ + :am:bs:ce=\E[K:cl=\E[2J:cm=\E[%i%d;%dH:co#80:\ + :do=\E[B:li#25:mi:nd=\E[C:\ + :se=\E[m:so=\E[7m:up=\E[A:us=\E[4m:ue=\E[m:\ + :md=\E[1m:mh=\E[m:mb=\E[5m:me=\E[m:\ + :kh=\EG:kb=^h:ku=\EH:kd=\EP:kl=\EK:kr=\EM:\ + :k1=\E;:k2=\E<:k3=\E=:k4=\E>:k5=\E?:\ + :k6=\E@:k7=\EA:k8=\EB:k9=\EC:k0=\ED: + # # -------------------------------- # # d: DEC (DIGITAL EQUIPMENT CORPORATION) @@ -3226,6 +3246,8 @@ pc|ibmpc|ibm pc PC/IX:\ :li#24:co#80:am:bs:bw:eo:\ :cd=\E[J:ce=\E[K:cl=\Ec:cm=\E[%i%2;%2H:do=\E[B:ho=\E[;H:\ :nd=\E[C:up=\E[A:so=\E[7m:se=\E[0m:us=\E[4m:ue=\E[0m: +pc3mono|IBM PC 386BSD Console with monochrome monitor:\ + :so=\E[0;1r\E[m:tc=pc3: pc3|ibmpc3|IBM PC 386BSD Console:\ :DO=\E[%dB:\ :K1=\E[H:\ @@ -3250,15 +3272,15 @@ pc3|ibmpc3|IBM PC 386BSD Console:\ :ho=\E[H:\ :is=\E[m:\ :k0=\E[V:\ - :k1:\E[M:\ - :k2:\E[N:\ - :k3:\E[O:\ - :k4:\E[P:\ - :k5:\E[Q:\ - :k6:\E[R:\ - :k7:\E[S:\ - :k8:\E[T:\ - :k9:\E[U:\ + :k1=\E[M:\ + :k2=\E[N:\ + :k3=\E[O:\ + :k4=\E[P:\ + :k5=\E[Q:\ + :k6=\E[R:\ + :k7=\E[S:\ + :k8=\E[T:\ + :k9=\E[U:\ :kD=\177:\ :kH=\E[F:\ :kN=\E[G:\ diff --git a/share/tmac/Makefile b/share/tmac/Makefile index 920d59c56c..4f588dc1ce 100644 --- a/share/tmac/Makefile +++ b/share/tmac/Makefile @@ -1,17 +1,20 @@ # @(#)Makefile 6.13 (Berkeley) 3/20/91 BINDIR= /usr/share -FILES= tmac.andoc tmac.doc.old +FILES= tmac.andoc MSRCS= doc doc-ditroff doc-common doc-nroff doc-syms LINKS= ${BINDIR}/tmac/tmac.andoc ${BINDIR}/tmac/tmac.an NOOBJ= noobj # old macros not currently installed... -OFILES= tmac.an.v6compat tmac.an6n tmac.an6t tmac.a tmac.cp +OFILES= tmac.an.v6compat tmac.an6n tmac.an6t tmac.a tmac.cp tmac.doc.old all clean cleandir depend lint tags: -beforeinstall: +strip.sed: + ln -s ../me/strip.sed strip.sed + +beforeinstall: strip.sed for i in ${MSRCS}; do \ sed -f strip.sed < $$i > ${DESTDIR}${BINDIR}/tmac/tmac.$$i; \ chown ${BINOWN}.${BINGRP} ${DESTDIR}${BINDIR}/tmac/tmac.$$i; \ diff --git a/share/tmac/doc-syms b/share/tmac/doc-syms index a9504e9bd8..e8ae1846c3 100644 --- a/share/tmac/doc-syms +++ b/share/tmac/doc-syms @@ -197,21 +197,21 @@ . \} . if "\\*(A\\n(aP"-ansiC" \{\ . ie \\n(sT==1 \{\ -. ds b1 \&\\*(tNANSI C\\*(aa3.159-1989\\*(sV +. ds b1 \&\\*(tNANSI \\*(aaX3.159-1989\\*(sV . as b1 (``\\*(tNANSI C\\*(aa'') . \} . el \{\ -. ds b1 \&\\*(tNANSI C\\*(aa3.159-1989\\*(sV +. ds b1 \&\\*(tNANSI \\*(aaX3.159-1989\\*(sV . as b1 (``\\*(tNANSI C\\*(aa'') . \} . \} . if "\\*(A\\n(aP"-ansiC-89" \{\ . ie \\n(sT==1 \{\ -. ds b1 \&\\*(tNANSI C\\*(aa3.159-1989\\*(sV +. ds b1 \&\\*(tNANSI \\*(aaX3.159-1989\\*(sV . as b1 (``\\*(tNANSI C\\*(aa'') . \} . el \{\ -. ds b1 \&\\*(tNANSI C\\*(aa3.159-1989\\*(sV +. ds b1 \&\\*(tNANSI \\*(aaX3.159-1989\\*(sV . as b1 (``\\*(tNANSI C\\*(aa'') . \} . \} diff --git a/share/tmac/tmac.andoc b/share/tmac/tmac.andoc index 7e4fa035cb..2ca57802ad 100644 --- a/share/tmac/tmac.andoc +++ b/share/tmac/tmac.andoc @@ -32,24 +32,15 @@ .\" .\" @(#)tmac.andoc 5.6 (Berkeley) 8/5/91 .\" -.\" 12 section one files still have not ben upgraded from tmac.doc.old -.\" once those are done this `.de Vx' can go away. -.nr Vx 0 -.de Vx -.nr Vx 1 -.so /usr/share/tmac/tmac.doc.old -.. .de Dd -.if \\n(vX==0 \{\ -. rn Dd xX -. so /usr/share/tmac/tmac.doc -. Dd \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 -. rm xX -.\} +.rn Dd xX +.so /usr/share/tmac/tmac.doc +.Dd "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" +.rm xX .. .de TH .rn TH xX .so /usr/share/tmac/tmac.an.old -.TH \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 +.TH "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" .rm xX .. diff --git a/share/zoneinfo/Makefile b/share/zoneinfo/Makefile index f396a7f999..844abd0422 100644 --- a/share/zoneinfo/Makefile +++ b/share/zoneinfo/Makefile @@ -95,15 +95,21 @@ REDO= right_only # -DCMUCS # to the end of the "CFLAGS=" line +.if exists(${.CURDIR}/obj) +OBJ=obj +.else +OBJ=. +.endif + .PATH: ${.CURDIR}/datfiles CFLAGS= -DTM_GMTOFF=tm_gmtoff -DTM_ZONE=tm_zone PROG= zic -MAN5= tzfile.0 +MAN5= tzfile.5 SRCS= zic.c scheck.c ialloc.c YDATA= africa antarctica asia australasia europe northamerica \ - southamerica pacificnew etcetera factory + southamerica pacificnew russia etcetera factory NDATA= systemv #SDATA= solar87 solar88 solar89 TDATA= ${YDATA} ${NDATA} ${SDATA} @@ -112,17 +118,17 @@ USNO= usno1988 usno1989 posix_only: ${TDATA} (cd ${.CURDIR}/datfiles; \ - ../obj/zic -d ${TZDIR} -L /dev/null ${TDATA}) + ../${OBJ}/zic -d ${TZDIR} -L /dev/null ${TDATA}) right_only: leapseconds ${TDATA} (cd ${.CURDIR}/datfiles; \ - ../obj/zic -d ${TZDIR} -L leapseconds ${TDATA}) + ../${OBJ}/zic -d ${TZDIR} -L leapseconds ${TDATA}) other_two: leapseconds ${TDATA} - (cd ${.CURDIR}/datfiles; - ../obj/zic -d ${TZDIR}/posix -L /dev/null ${TDATA}) (cd ${.CURDIR}/datfiles; \ - ../obj/zic -d ${TZDIR}/right -L leapseconds ${TDATA}) + ../${OBJ}/zic -d ${TZDIR}/posix -L /dev/null ${TDATA}) + (cd ${.CURDIR}/datfiles; \ + ../${OBJ}/zic -d ${TZDIR}/right -L leapseconds ${TDATA}) posix_right: posix_only other_two @@ -130,9 +136,10 @@ right_posix: right_only other_two install: maninstall ${DATA} ${REDO} (cd ${.CURDIR}/datfiles; \ - ../obj/zic -d ${TZDIR} -p ${POSIXRULES}) - install -c -o ${BINOWN} -g ${BINGRP} -m 444 \ - ${TZDIR}/${LOCALTIME} ${DESTDIR}/etc/localtime + ../${OBJ}/zic -d ${TZDIR} -p ${POSIXRULES}) + if [ ! -f ${DESTDIR}/etc/localtime ]; then \ + ln -s /usr/share/zoneinfo/${LOCALTIME} ${DESTDIR}/etc/localtime ; \ + fi chown -R ${BINOWN}.${BINGRP} ${TZDIR} chmod -R a-w ${TZDIR} diff --git a/share/zoneinfo/datfiles/russia b/share/zoneinfo/datfiles/russia new file mode 100644 index 0000000000..e96dbc71b8 --- /dev/null +++ b/share/zoneinfo/datfiles/russia @@ -0,0 +1,18 @@ +# Change Time Rules for Moscow +# +# Rule NAME FROM TO TYPE IN ON AT SAVE LETER/S +# Historic rules +Rule Moscow 1930 1980 - Jun 21 0:00 0 MSK +Rule Moscow 1981 1984 - Apr 1 0:00 1 MSD +Rule Moscow 1981 1984 - Oct 1 0:00 0 MSK +Rule Moscow 1984 1990 - Mar lastSun 2:00 1 MSD +Rule Moscow 1985 1990 - Sep lastSun 3:00 0 MSK +Rule Moscow 1991 only - Mar lastSun 2:00 0 "EET DST" +Rule Moscow 1991 only - Sep lastSun 3:00 -1 EET +Rule Moscow 1992 only - Jan 19 2:00 0 MSK +# Current rules +Rule Moscow 1992 max - Mar lastSun 2:00 1 MSD +Rule Moscow 1992 max - Sep lastSun 3:00 0 MSK + +# Zone NAME GMTOFF RULES/SAVE FORMAT [UNTIL] +Zone Moscow 3:00 Moscow %s diff --git a/sys/ROADMAP b/sys/ROADMAP index 4684c38380..f979875799 100644 --- a/sys/ROADMAP +++ b/sys/ROADMAP @@ -46,3 +46,5 @@ wish to examine. To look at a running system, "gdb -k /386bsd /dev/mem". For in-kernel debugging, one can compile in the "ddb" debugger and enter it by typing CTRL, ALT, and ESC simulatineously. Finally, printfs can be scattered in the kernel for debugging, just like user programs. + +$Id$ diff --git a/sys/compile/.keep_me b/sys/compile/.keep_me new file mode 100644 index 0000000000..aedb72a50a --- /dev/null +++ b/sys/compile/.keep_me @@ -0,0 +1 @@ +This normally empty directory needs to be kept in the distribution. diff --git a/sys/conf/files b/sys/conf/files index 27de96ec29..5047c4324f 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,11 +1,4 @@ -# -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 1 00129 -# -------------------- ----- ---------------------- -# -# 02 Apr 93 Paul Popelka Added PCFS support -# 02 Apr 93 Rodney W. Grimes Sorted file +# $Id$ # ddb/db_access.c optional ddb ddb/db_aout.c optional ddb @@ -29,8 +22,9 @@ isofs/isofs_node.c optional isofs isofs/isofs_util.c optional isofs isofs/isofs_vfsops.c optional isofs isofs/isofs_vnops.c optional isofs +isofs/isofs_rrip.c optional isofs kern/dead_vnops.c standard -kern/fifo_vnops.c optional fifo +kern/fifo_vnops.c standard kern/init_main.c standard kern/init_sysent.c standard kern/kern__physio.c standard @@ -88,6 +82,7 @@ net/bpf_filter.c optional bpfilter net/if.c standard net/if_ethersubr.c optional ether net/if_loop.c optional loop +net/if_ppp.c optional ppp net/if_sl.c optional sl net/if_tun.c optional tun device-driver net/radix.c standard @@ -96,6 +91,7 @@ net/raw_usrreq.c standard net/route.c standard net/rtsock.c standard net/slcompress.c optional sl +net/slcompress.c optional ppp netccitt/ccitt_proto.c optional ccitt netccitt/hd_debug.c optional ccitt netccitt/hd_input.c optional ccitt @@ -198,6 +194,12 @@ pcfs/pcfs_fat.c optional pcfs pcfs/pcfs_lookup.c optional pcfs pcfs/pcfs_vfsops.c optional pcfs pcfs/pcfs_vnops.c optional pcfs +scsi/cd.c optional cd +scsi/ch.c optional ch +scsi/scsiconf.c optional scbus +scsi/sd.c optional sd +scsi/sg.c optional sg +scsi/st.c optional st ufs/mfs_vfsops.c optional mfs ufs/mfs_vnops.c optional mfs ufs/ufs_alloc.c standard diff --git a/sys/conf/newvers.sh b/sys/conf/newvers.sh index 01d5e8adbf..94fd7e0370 100644 --- a/sys/conf/newvers.sh +++ b/sys/conf/newvers.sh @@ -31,16 +31,9 @@ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # -# @(#)newvers.sh 7.4 (Berkeley) 12/7/90 +# from: @(#)newvers.sh 7.4 (Berkeley) 12/7/90 +# $Id$ # -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 1 00105 -# -------------------- ----- ---------------------- -# -# 28 Mar 93 Kent Talarico & Charles Hannum -# Don't update version number twice and -# include kernel name in version string. if [ ! -r version ] then @@ -50,7 +43,13 @@ else fi touch version + +kernvers="FreeBSD 1.0e" v=`cat version` t=`date "+ %m/%d/%y %H:%M"` +t=`date` +user=${USER-root} +host=`hostname` +dir=`pwd` ( - echo "char version[] = \"version: ${v} ($1) ${t}\";" + echo "char version[] = \"${kernvers} ($1) #${v}: ${t}\\n ${user}@${host}:${dir}\\n\";" ) > vers.c diff --git a/sys/conf/nfsswapvmunix.c b/sys/conf/nfsswapvmunix.c index 3ca040a517..dec28d37e7 100644 --- a/sys/conf/nfsswapvmunix.c +++ b/sys/conf/nfsswapvmunix.c @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfsswapvmunix.c 7.1 (Berkeley) 3/4/91 + * from: @(#)nfsswapvmunix.c 7.1 (Berkeley) 3/4/91 + * $Id$ */ /* diff --git a/sys/conf/param.c b/sys/conf/param.c index 6444f9608d..45fb17f313 100644 --- a/sys/conf/param.c +++ b/sys/conf/param.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)param.c 7.20 (Berkeley) 6/27/91 + * from: @(#)param.c 7.20 (Berkeley) 6/27/91 + * $Id$ */ #include "sys/param.h" @@ -56,7 +57,7 @@ * the kernel; it should be modified there to suit local taste * if necessary. * - * Compiled with -DHZ=xx -DTIMEZONE=x -DDST=x -DMAXUSERS=xx + * Compiled with -DHZ=xx -DTIMEZONE=x -DDST=x -DMAXUSERS=xx -DMAXFDESCS=xx */ #ifndef HZ @@ -71,6 +72,7 @@ int maxproc = NPROC; #define NTEXT (80 + NPROC / 8) /* actually the object cache */ #define NVNODE (NPROC + NTEXT + 100) long desiredvnodes = NVNODE; +int maxfdescs = MAXFDESCS; int maxfiles = 3 * (NPROC + MAXUSERS) + 80; int ncallout = 16 + NPROC; int nclist = 60 + 12 * MAXUSERS; diff --git a/sys/ddb/db_access.c b/sys/ddb/db_access.c index 63368c0cbc..cbb25dd371 100644 --- a/sys/ddb/db_access.c +++ b/sys/ddb/db_access.c @@ -22,24 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_access.c,v $ - * Revision 1.1 1992/03/25 21:44:50 pace - * Initial revision * - * Revision 2.3 91/02/05 17:05:44 mrt - * Changed to new Mach copyright - * [91/01/31 16:16:22 mrt] - * - * Revision 2.2 90/08/27 21:48:20 dbg - * Fix type declarations. - * [90/08/07 dbg] - * Created. - * [90/07/25 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 diff --git a/sys/ddb/db_access.h b/sys/ddb/db_access.h index ddc5349148..2fe5e1053f 100644 --- a/sys/ddb/db_access.h +++ b/sys/ddb/db_access.h @@ -22,22 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_access.h,v $ - * Revision 1.1 1992/03/25 21:44:53 pace - * Initial revision * - * Revision 2.3 91/02/05 17:05:49 mrt - * Changed to new Mach copyright - * [91/01/31 16:16:37 mrt] - * - * Revision 2.2 90/08/27 21:48:27 dbg - * Created. - * [90/08/07 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 diff --git a/sys/ddb/db_aout.c b/sys/ddb/db_aout.c index 88f4b03e18..d1c848f07d 100644 --- a/sys/ddb/db_aout.c +++ b/sys/ddb/db_aout.c @@ -22,22 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_aout.c,v $ - * Revision 1.1 1992/03/25 21:44:55 pace - * Initial revision * - * Revision 2.3 91/02/05 17:05:55 mrt - * Changed to new Mach copyright - * [91/01/31 16:16:44 mrt] - * - * Revision 2.2 90/08/27 21:48:35 dbg - * Created. - * [90/08/17 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 @@ -77,7 +65,10 @@ (sp = (struct nlist *)((symtab) + 1), \ ep = (struct nlist *)((char *)sp + *(symtab))) +#ifndef SYMTAB_SPACE #define SYMTAB_SPACE 63000 +#endif /*SYMTAB_SPACE*/ + int db_symtabsize = SYMTAB_SPACE; char db_symtab[SYMTAB_SPACE] = { 1 }; diff --git a/sys/ddb/db_break.c b/sys/ddb/db_break.c index 9db7a041a1..b4d0bbcb3e 100644 --- a/sys/ddb/db_break.c +++ b/sys/ddb/db_break.c @@ -22,45 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_break.c,v $ - * Revision 1.1 1992/03/25 21:44:57 pace - * Initial revision * - * Revision 2.7 91/02/05 17:06:00 mrt - * Changed to new Mach copyright - * [91/01/31 16:17:01 mrt] - * - * Revision 2.6 91/01/08 15:09:03 rpd - * Added db_map_equal, db_map_current, db_map_addr. - * [90/11/10 rpd] - * - * Revision 2.5 90/11/05 14:26:32 rpd - * Initialize db_breakpoints_inserted to TRUE. - * [90/11/04 rpd] - * - * Revision 2.4 90/10/25 14:43:33 rwd - * Added map field to breakpoints. - * Added map argument to db_set_breakpoint, db_delete_breakpoint, - * db_find_breakpoint. Added db_find_breakpoint_here. - * [90/10/18 rpd] - * - * Revision 2.3 90/09/28 16:57:07 jsb - * Fixed db_breakpoint_free. - * [90/09/18 rpd] - * - * Revision 2.2 90/08/27 21:49:53 dbg - * Reflected changes in db_printsym()'s calling seq. - * [90/08/20 af] - * Clear breakpoints only if inserted. - * Reduce lint. - * [90/08/07 dbg] - * Created. - * [90/07/25 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 diff --git a/sys/ddb/db_break.h b/sys/ddb/db_break.h index 874cbcb5c3..dc66dcab75 100644 --- a/sys/ddb/db_break.h +++ b/sys/ddb/db_break.h @@ -22,30 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_break.h,v $ - * Revision 1.1 1992/03/25 21:44:59 pace - * Initial revision * - * Revision 2.4 91/02/05 17:06:06 mrt - * Changed to new Mach copyright - * [91/01/31 16:17:10 mrt] - * - * Revision 2.3 90/10/25 14:43:40 rwd - * Added map field to breakpoints. - * [90/10/18 rpd] - * - * Revision 2.2 90/08/27 21:50:00 dbg - * Modularized typedef names. - * [90/08/20 af] - * Add external defintions. - * [90/08/07 dbg] - * Created. - * [90/07/25 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 diff --git a/sys/ddb/db_command.c b/sys/ddb/db_command.c index e660e183bd..8c2555dcce 100644 --- a/sys/ddb/db_command.c +++ b/sys/ddb/db_command.c @@ -22,62 +22,15 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_command.c,v $ - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00081 - * -------------------- ----- ---------------------- - * - * 01 Feb 93 Julian Elischer move strcmp to a more general - * part of the kernel. * - * Revision 1.1 1992/03/25 21:45:02 pace - * Initial revision - * - * Revision 2.6 91/02/05 17:06:10 mrt - * Changed to new Mach copyright - * [91/01/31 16:17:18 mrt] - * - * Revision 2.5 91/01/08 17:31:54 rpd - * Forward reference for db_fncall(); - * [91/01/04 12:35:17 rvb] - * - * Add call as a synonym for ! and match for next - * [91/01/04 12:14:48 rvb] - * - * Revision 2.4 90/11/07 16:49:15 rpd - * Added search. - * [90/11/06 rpd] - * - * Revision 2.3 90/10/25 14:43:45 rwd - * Changed db_fncall to print the result unsigned. - * [90/10/19 rpd] - * - * Added CS_MORE to db_watchpoint_cmd. - * [90/10/17 rpd] - * Added watchpoint commands: watch, dwatch, show watches. - * [90/10/16 rpd] - * - * Revision 2.2 90/08/27 21:50:10 dbg - * Remove 'listbreaks' - use 'show breaks' instead. Change 'show - * threads' to 'show all threads' to avoid clash with 'show thread'. - * Set 'dot' here from db_prev or db_next, depending on 'db_ed_style' - * flag and syntax table. - * [90/08/22 dbg] - * Reduce lint. - * [90/08/07 dbg] - * Created. - * [90/07/25 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 */ + /* * Command dispatcher. */ diff --git a/sys/ddb/db_command.h b/sys/ddb/db_command.h index 938a3d6a28..191d7336a9 100644 --- a/sys/ddb/db_command.h +++ b/sys/ddb/db_command.h @@ -22,24 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_command.h,v $ - * Revision 1.1 1992/03/25 21:45:05 pace - * Initial revision * - * Revision 2.3 91/02/05 17:06:15 mrt - * Changed to new Mach copyright - * [91/01/31 16:17:28 mrt] - * - * Revision 2.2 90/08/27 21:50:19 dbg - * Replace db_last_address_examined with db_prev, db_next. - * [90/08/22 dbg] - * Created. - * [90/08/07 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 diff --git a/sys/ddb/db_examine.c b/sys/ddb/db_examine.c index 66f5f6858c..7a83692df1 100644 --- a/sys/ddb/db_examine.c +++ b/sys/ddb/db_examine.c @@ -22,35 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_examine.c,v $ - * Revision 1.1 1992/03/25 21:45:07 pace - * Initial revision * - * Revision 2.4 91/02/05 17:06:20 mrt - * Changed to new Mach copyright - * [91/01/31 16:17:37 mrt] - * - * Revision 2.3 90/11/07 16:49:23 rpd - * Added db_search_cmd, db_search. - * [90/11/06 rpd] - * - * Revision 2.2 90/08/27 21:50:38 dbg - * Add 'r', 'z' to print and examine formats. - * Change calling sequence of db_disasm. - * db_examine sets db_prev and db_next instead of explicitly - * advancing dot. - * [90/08/20 dbg] - * Reflected changes in db_printsym()'s calling seq. - * [90/08/20 af] - * Reduce lint. - * [90/08/07 dbg] - * Created. - * [90/07/25 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 diff --git a/sys/ddb/db_expr.c b/sys/ddb/db_expr.c index 062662c48e..b02f225825 100644 --- a/sys/ddb/db_expr.c +++ b/sys/ddb/db_expr.c @@ -22,33 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_expr.c,v $ - * Revision 1.1 1992/03/25 21:45:09 pace - * Initial revision * - * Revision 2.3 91/02/05 17:06:25 mrt - * Changed to new Mach copyright - * [91/01/31 16:17:46 mrt] - * - * Revision 2.2 90/08/27 21:50:57 dbg - * Use '..' instead of '$$' for db_prev. - * Use '+' for db_next. - * [90/08/22 dbg] - * - * Allow repeated unary operators. - * [90/08/20 dbg] - * - * Reflected back rename of db_symbol_value->db_value_of_name - * [90/08/20 af] - * Reduce lint. - * [90/08/07 dbg] - * Created. - * [90/07/25 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 diff --git a/sys/ddb/db_input.c b/sys/ddb/db_input.c index 5ec7824c51..3cf0b0562b 100644 --- a/sys/ddb/db_input.c +++ b/sys/ddb/db_input.c @@ -22,28 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_input.c,v $ - * Revision 1.1 1992/03/25 21:45:10 pace - * Initial revision * - * Revision 2.4 91/02/14 14:41:53 mrt - * Add input line editing. - * [90/11/11 dbg] - * - * Revision 2.3 91/02/05 17:06:32 mrt - * Changed to new Mach copyright - * [91/01/31 16:18:13 mrt] - * - * Revision 2.2 90/08/27 21:51:03 dbg - * Reduce lint. - * [90/08/07 dbg] - * Created. - * [90/07/25 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 diff --git a/sys/ddb/db_lex.c b/sys/ddb/db_lex.c index dcfd90038a..84176383d1 100644 --- a/sys/ddb/db_lex.c +++ b/sys/ddb/db_lex.c @@ -22,30 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_lex.c,v $ - * Revision 1.1 1992/03/25 21:45:13 pace - * Initial revision * - * Revision 2.3 91/02/05 17:06:36 mrt - * Changed to new Mach copyright - * [91/01/31 16:18:20 mrt] - * - * Revision 2.2 90/08/27 21:51:10 dbg - * Add 'dotdot' token. - * [90/08/22 dbg] - * - * Allow backslash to quote any character into an identifier. - * Allow colon in identifier for symbol table qualification. - * [90/08/16 dbg] - * Reduce lint. - * [90/08/07 dbg] - * Created. - * [90/07/25 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 diff --git a/sys/ddb/db_lex.h b/sys/ddb/db_lex.h index 56c5569499..98c37478fe 100644 --- a/sys/ddb/db_lex.h +++ b/sys/ddb/db_lex.h @@ -22,26 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_lex.h,v $ - * Revision 1.1 1992/03/25 21:45:15 pace - * Initial revision * - * Revision 2.3 91/02/05 17:06:41 mrt - * Changed to new Mach copyright - * [91/01/31 16:18:28 mrt] - * - * Revision 2.2 90/08/27 21:51:16 dbg - * Add 'dotdot' token. - * [90/08/22 dbg] - * Export db_flush_lex. - * [90/08/07 dbg] - * Created. - * [90/07/25 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 diff --git a/sys/ddb/db_output.c b/sys/ddb/db_output.c index fc8fca7766..442546b9b2 100644 --- a/sys/ddb/db_output.c +++ b/sys/ddb/db_output.c @@ -23,33 +23,9 @@ * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 4 00083 - * -------------------- ----- ---------------------- - * - * 14 Mar 93 Chris G. Demetriou Fixed so that tab is not output, - * use spaces instead. - */ -/* - * HISTORY - * $Log: db_output.c,v $ - * Revision 1.1 1992/03/25 21:45:18 pace - * Initial revision - * - * Revision 2.3 91/02/05 17:06:45 mrt - * Changed to new Mach copyright - * [91/01/31 16:18:41 mrt] - * - * Revision 2.2 90/08/27 21:51:25 dbg - * Put extra features of db_doprnt in _doprnt. - * [90/08/20 dbg] - * Reduce lint. - * [90/08/07 dbg] - * Created. - * [90/07/25 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 @@ -60,6 +36,7 @@ */ #include "param.h" +#include "systm.h" #include /* @@ -156,16 +133,6 @@ db_print_position() return (db_output_position); } -/* - * End line if too long. - */ -void -db_end_line() -{ - if (db_output_position >= db_max_width) - db_printf("\n"); -} - /* * Printing */ @@ -191,6 +158,16 @@ kdbprintf(char *fmt, ...) va_end(listp); } +/* + * End line if too long. + */ +void +db_end_line() +{ + if (db_output_position >= db_max_width) + db_printf("\n"); +} + /* * Put a number (base <= 16) in a buffer in reverse order; return an * optional length and a pointer to the NULL terminated (preceded?) diff --git a/sys/ddb/db_output.h b/sys/ddb/db_output.h index 3ad599d8cc..e0e345558d 100644 --- a/sys/ddb/db_output.h +++ b/sys/ddb/db_output.h @@ -22,22 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_output.h,v $ - * Revision 1.1 1992/03/25 21:45:20 pace - * Initial revision * - * Revision 2.3 91/02/05 17:06:49 mrt - * Changed to new Mach copyright - * [91/01/31 16:18:48 mrt] - * - * Revision 2.2 90/08/27 21:51:32 dbg - * Created. - * [90/08/07 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 8/90 diff --git a/sys/ddb/db_print.c b/sys/ddb/db_print.c index 48eef38c6a..25e4c66600 100644 --- a/sys/ddb/db_print.c +++ b/sys/ddb/db_print.c @@ -22,45 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_print.c,v $ - * Revision 1.1 1992/03/25 21:45:22 pace - * Initial revision * - * Revision 2.5 91/02/05 17:06:53 mrt - * Changed to new Mach copyright - * [91/01/31 16:18:56 mrt] - * - * Revision 2.4 90/10/25 14:43:54 rwd - * Changed db_show_regs to print unsigned. - * [90/10/19 rpd] - * Generalized the watchpoint support. - * [90/10/16 rwd] - * - * Revision 2.3 90/09/09 23:19:52 rpd - * Avoid totally incorrect guesses of symbol names for small values. - * [90/08/30 17:39:08 af] - * - * Revision 2.2 90/08/27 21:51:49 dbg - * Insist that 'show thread' be called with an explicit address. - * [90/08/22 dbg] - * - * Fix type for db_maxoff. - * [90/08/20 dbg] - * - * Do not dereference the "valuep" field of a variable directly, - * call the new db_read/write_variable functions instead. - * Reflected changes in symbol lookup functions. - * [90/08/20 af] - * Reduce lint. - * [90/08/10 14:33:44 dbg] - * - * Created. - * [90/07/25 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 diff --git a/sys/ddb/db_run.c b/sys/ddb/db_run.c index b8e33aa75e..78c0d4c1d4 100644 --- a/sys/ddb/db_run.c +++ b/sys/ddb/db_run.c @@ -22,46 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_run.c,v $ - * Revision 1.1 1992/03/25 21:45:24 pace - * Initial revision * - * Revision 2.5 91/02/05 17:06:58 mrt - * Changed to new Mach copyright - * [91/01/31 16:19:05 mrt] - * - * Revision 2.4 91/01/08 15:09:10 rpd - * Fixed bug in db_restart_at_pc. - * [90/12/07 rpd] - * Added STEP_COUNT and count option to db_continue_cmd. - * Changed db_stop_at_pc to return (modified) is_breakpoint. - * Fixed db_stop_at_pc to print newlines in the right places. - * [90/11/27 rpd] - * - * Revision 2.3 90/10/25 14:43:59 rwd - * Changed db_find_breakpoint to db_find_breakpoint_here. - * [90/10/18 rpd] - * - * Fixed db_set_single_step to pass regs to branch_taken. - * Added watchpoint argument to db_restart_at_pc. - * [90/10/17 rpd] - * Generalized the watchpoint support. - * [90/10/16 rwd] - * Added watchpoint support. - * [90/10/16 rpd] - * - * Revision 2.2 90/08/27 21:51:59 dbg - * Fixed names for single-step functions. - * [90/08/20 af] - * Reduce lint. - * [90/08/07 dbg] - * Created. - * [90/07/25 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 diff --git a/sys/ddb/db_sym.c b/sys/ddb/db_sym.c index 0517c68521..fd02ae455c 100644 --- a/sys/ddb/db_sym.c +++ b/sys/ddb/db_sym.c @@ -22,52 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_sym.c,v $ - * Revision 1.1 1992/03/25 21:45:27 pace - * Initial revision * - * Revision 2.5 91/02/05 17:07:07 mrt - * Changed to new Mach copyright - * [91/01/31 16:19:17 mrt] - * - * Revision 2.4 90/10/25 14:44:05 rwd - * Changed db_printsym to print unsigned. - * [90/10/19 rpd] - * - * Revision 2.3 90/09/09 23:19:56 rpd - * Avoid totally incorrect guesses of symbol names for small values. - * [90/08/30 17:39:48 af] - * - * Revision 2.2 90/08/27 21:52:18 dbg - * Removed nlist.h. Fixed some type declarations. - * Qualifier character is ':'. - * [90/08/20 dbg] - * Modularized symtab info into a new db_symtab_t type. - * Modified db_add_symbol_table and others accordingly. - * Defined db_sym_t, a new (opaque) type used to represent - * symbols. This should support all sort of future symtable - * formats. Functions like db_qualify take a db_sym_t now. - * New db_symbol_values() function to explode the content - * of a db_sym_t. - * db_search_symbol() replaces db_find_sym_and_offset(), which is - * now a macro defined in our (new) header file. This new - * function accepts more restrictive searches, which are - * entirely delegated to the symtab-specific code. - * Accordingly, db_printsym() accepts a strategy parameter. - * New db_line_at_pc() function. - * Renamed misleading db_eqsym into db_eqname. - * [90/08/20 10:47:06 af] - * - * Created. - * [90/07/25 dbg] - * - * Revision 2.1 90/07/26 16:43:52 dbg - * Created. - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 diff --git a/sys/ddb/db_sym.h b/sys/ddb/db_sym.h index 663f1cd420..bc17bd0256 100644 --- a/sys/ddb/db_sym.h +++ b/sys/ddb/db_sym.h @@ -22,26 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_sym.h,v $ - * Revision 1.1 1992/03/25 21:45:29 pace - * Initial revision * - * Revision 2.3 91/02/05 17:07:12 mrt - * Changed to new Mach copyright - * [91/01/31 16:19:27 mrt] - * - * Revision 2.2 90/08/27 21:52:39 dbg - * Changed type of db_sym_t to char * - it's a better type for an - * opaque pointer. - * [90/08/22 dbg] - * - * Created. - * [90/08/19 af] - * + * $Id$ */ + /* * Author: Alessandro Forin, Carnegie Mellon University * Date: 8/90 diff --git a/sys/ddb/db_trap.c b/sys/ddb/db_trap.c index 099fab1439..bcdc4f47f4 100644 --- a/sys/ddb/db_trap.c +++ b/sys/ddb/db_trap.c @@ -22,40 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_trap.c,v $ - * Revision 1.1 1992/03/25 21:45:31 pace - * Initial revision * - * Revision 2.5 91/02/05 17:07:16 mrt - * Changed to new Mach copyright - * [91/01/31 16:19:35 mrt] - * - * Revision 2.4 91/01/08 15:09:17 rpd - * Changed db_stop_at_pc's arguments. - * Print db_inst_count, db_load_count, db_store_count. - * [90/11/27 rpd] - * - * Revision 2.3 90/10/25 14:44:11 rwd - * From rpd. - * [90/10/19 17:03:17 rwd] - * - * Generalized the watchpoint support. - * [90/10/16 rwd] - * Added watchpoint support. - * [90/10/16 rpd] - * - * Revision 2.2 90/08/27 21:52:52 dbg - * Assign to db_dot before calling the print function. - * [90/08/20 af] - * Reduce lint. - * [90/08/07 dbg] - * Created. - * [90/07/25 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 diff --git a/sys/ddb/db_variables.c b/sys/ddb/db_variables.c index ed9512df52..cd59bb2d57 100644 --- a/sys/ddb/db_variables.c +++ b/sys/ddb/db_variables.c @@ -22,31 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_variables.c,v $ - * Revision 1.1 1992/03/25 21:45:33 pace - * Initial revision * - * Revision 2.3 91/02/05 17:07:19 mrt - * Changed to new Mach copyright - * [91/01/31 16:19:46 mrt] - * - * Revision 2.2 90/08/27 21:53:24 dbg - * New db_read/write_variable functions. Should be used instead - * of dereferencing valuep directly, which might not be a true - * pointer if there is an fcn() access function. - * [90/08/20 af] - * - * Fix declarations. - * Check for trailing garbage after last expression on command line. - * [90/08/10 14:34:54 dbg] - * - * Created. - * [90/07/25 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 diff --git a/sys/ddb/db_variables.h b/sys/ddb/db_variables.h index f958d2c383..25558bc28b 100644 --- a/sys/ddb/db_variables.h +++ b/sys/ddb/db_variables.h @@ -22,27 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_variables.h,v $ - * Revision 1.1 1992/03/25 21:45:35 pace - * Initial revision * - * Revision 2.3 91/02/05 17:07:23 mrt - * Changed to new Mach copyright - * [91/01/31 16:19:54 mrt] - * - * Revision 2.2 90/08/27 21:53:40 dbg - * Modularized typedef name. Documented the calling sequence of - * the (optional) access function of a variable. Now the valuep - * field can be made opaque, eg be an offset that fcn() resolves. - * [90/08/20 af] - * - * Created. - * [90/07/25 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 diff --git a/sys/ddb/db_watch.c b/sys/ddb/db_watch.c index e69d906d93..b027732b9d 100644 --- a/sys/ddb/db_watch.c +++ b/sys/ddb/db_watch.c @@ -22,34 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_watch.c,v $ - * Revision 1.1 1992/03/25 21:45:37 pace - * Initial revision * - * Revision 2.5 91/02/05 17:07:27 mrt - * Changed to new Mach copyright - * [91/01/31 16:20:02 mrt] - * - * Revision 2.4 91/01/08 15:09:24 rpd - * Use db_map_equal, db_map_current, db_map_addr. - * [90/11/10 rpd] - * - * Revision 2.3 90/11/05 14:26:39 rpd - * Initialize db_watchpoints_inserted to TRUE. - * [90/11/04 rpd] - * - * Revision 2.2 90/10/25 14:44:16 rwd - * Made db_watchpoint_cmd parse a size argument. - * [90/10/17 rpd] - * Generalized the watchpoint support. - * [90/10/16 rwd] - * Created. - * [90/10/16 rpd] - * + * $Id$ */ + /* * Author: Richard P. Draves, Carnegie Mellon University * Date: 10/90 diff --git a/sys/ddb/db_watch.h b/sys/ddb/db_watch.h index 9795755bfd..f8932de39d 100644 --- a/sys/ddb/db_watch.h +++ b/sys/ddb/db_watch.h @@ -22,24 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_watch.h,v $ - * Revision 1.1 1992/03/25 21:45:40 pace - * Initial revision * - * Revision 2.3 91/02/05 17:07:31 mrt - * Changed to new Mach copyright - * [91/01/31 16:20:09 mrt] - * - * Revision 2.2 90/10/25 14:44:21 rwd - * Generalized the watchpoint support. - * [90/10/16 rwd] - * Created. - * [90/10/16 rpd] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 10/90 diff --git a/sys/ddb/db_write_cmd.c b/sys/ddb/db_write_cmd.c index dce22a915c..0113958c9b 100644 --- a/sys/ddb/db_write_cmd.c +++ b/sys/ddb/db_write_cmd.c @@ -22,32 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_write_cmd.c,v $ - * Revision 1.1 1992/03/25 21:45:42 pace - * Initial revision * - * Revision 2.4 91/02/05 17:07:35 mrt - * Changed to new Mach copyright - * [91/01/31 16:20:19 mrt] - * - * Revision 2.3 90/10/25 14:44:26 rwd - * Changed db_write_cmd to print unsigned. - * [90/10/19 rpd] - * - * Revision 2.2 90/08/27 21:53:54 dbg - * Set db_prev and db_next instead of explicitly advancing dot. - * [90/08/22 dbg] - * Reflected changes in db_printsym()'s calling seq. - * [90/08/20 af] - * Warn user if nothing was written. - * [90/08/07 dbg] - * Created. - * [90/07/25 dbg] - * + * $Id$ */ + /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 diff --git a/sys/i386/Makefile b/sys/i386/Makefile index 0662e28ce8..4ad5a34556 100644 --- a/sys/i386/Makefile +++ b/sys/i386/Makefile @@ -1,4 +1,5 @@ -# @(#)Makefile 7.3 (Berkeley) 6/9/91 +# from: @(#)Makefile 7.3 (Berkeley) 6/9/91 +# $Id$ # Makefile for i386 tags file diff --git a/sys/i386/boot/Makefile b/sys/i386/boot/Makefile index bebd8bcc03..2e4f162d9a 100644 --- a/sys/i386/boot/Makefile +++ b/sys/i386/boot/Makefile @@ -1,7 +1,4 @@ # -# Ported to boot 386BSD by Julian Elischer (julian@tfs.com) -# September 1992 -# # Permission to use, copy, modify and distribute this software and its # documentation is hereby granted, provided that both the copyright # notice and this permission notice appear in all copies of the @@ -22,54 +19,39 @@ # any improvements or extensions that they make and grant Carnegie Mellon # the rights to redistribute these changes. # +# from: Mach, Revision 2.2 92/04/04 11:33:46 rpd +# $Id$ # -# HISTORY -# $Log: Makefile,v $ -# Revision 2.2 92/04/04 11:33:46 rpd -# Fabricated for MACH 3.0 -# [92/03/30 mg32] -# -# Thu Sep 10 22:40:23 PDT 1992 -# Ported to 386BSD by Julian Elischer -# julian@tfs.com -# -CFLAGS = -O -DDO_BAD144 +wd0: + dd if=boot of=biosboot count=1 + dd if=boot of=bootbios skip=1 + disklabel -r -w wd0 nec5655 newboot biosboot bootbios + rm biosboot bootbios + +NOPROG= noprog +NOMAN= noman + +CFLAGS = -O -DDO_BAD144 -I${.CURDIR} LIBS= -lc -INC= -I../.. +INC= -I${.CURDIR}/../.. -# start.o should be first, table.o should be second +# start.o should be first OBJS = start.o table.o boot2.o boot.o asm.o bios.o io.o disk.o sys.o -.SUFFIXES: .s .c .o +.SUFFIXES: .S .c .o -# These are wierd because we don't want separate code and data segments.. ok? .c.o: - @echo $(CC) -c $(CFLAGS) $(INC) $*.c - -@trap "/bin/rm -f $*.i $*.s; exit 0" 0 1 2 3 10 15; \ - $(CC) $(CFLAGS) $(INC) -S $<; \ - if [ $$? != 0 ]; then :; else \ - sed -f boot.sed $*.s > $*.i; \ - $(AS) $*.i -o $@; \ - fi - -.s.o: - @echo $(AS) -o $*.o $< - -@trap "/bin/rm -f $*.i X$*.c; exit 0" 0 1 2 3 10 15; \ - /bin/rm -f X$*.c; \ - ln $*.s X$*.c; \ - $(CC) -E $(CFLAGS) X$*.c > $*.i; \ - if [ $$? != 0 ]; then :; \ - else \ - $(AS) $*.i -o $@; \ - fi - - -boot: boot.sed $(OBJS) + $(CC) $(CFLAGS) $(INC) -c $< + +.S.o: + $(CC) $(CFLAGS) -c $< + +boot: $(OBJS) $(LD) -N -T 0 -o boot $(OBJS) $(LIBS) cp boot boot.sym @strip boot - @./rmaouthdr boot boot.tmp + @sh ${.CURDIR}/rmaouthdr boot boot.tmp @mv -f boot.tmp boot @ls -l boot @@ -105,27 +87,17 @@ sd: /usr/mdec/bootsd /usr/mdec/sdboot wd: /usr/mdec/bootwd /usr/mdec/wdboot fd: /usr/mdec/bootfd /usr/mdec/fdboot -#wd0: /usr/mdec/bootwd /usr/mdec/wdboot -# disklabel -r -w wd0 julian julian5 /usr/mdec/wdboot /usr/mdec/bootwd -# - -sd0: /usr/mdec/bootsd /usr/mdec/sdboot - disklabel -r -w sd0 XT-8760 scsitest /usr/mdec/sdboot /usr/mdec/bootsd - +all: biosboot bootbios -#fd0: /usr/mdec/bootfd /usr/mdec/fdboot -# disklabel -r -w fd0 floppy5 bootflpy /usr/mdec/fdboot /usr/mdec/bootfd -# +fd0: + dd if=boot of=biosboot count=1 + dd if=boot of=bootbios skip=1 + disklabel -r -w fd0 floppy bootflpy biosboot bootbios + rm biosboot bootbios -install: wd sd -# you should use the old floppy booter, it's MUCH faster -# This one works but it's too slow. -# besides how many kernels can you fit on a floppy? -# only use this one if you want to boot a kernel -# from a hard disk 2 when you've trashed hard disk 1 -#install: wd sd fd +install: wd sd fd -clean: +clean cleandir: /bin/rm -f *.o *.d boot bootbios biosboot boot.sym - +.include diff --git a/sys/i386/boot/README.386BSD b/sys/i386/boot/README.386BSD index ae61346800..cc5cb8fe2f 100644 --- a/sys/i386/boot/README.386BSD +++ b/sys/i386/boot/README.386BSD @@ -147,3 +147,5 @@ these should install painlessly. Before you do this ensure you have a booting floppy with correct disktab and bootblock files on it so that if it doesn't work, you can re-disklabel from the floppy. + +$Id$ diff --git a/sys/i386/boot/README.MACH b/sys/i386/boot/README.MACH index d11f3e4203..cb6232936e 100644 --- a/sys/i386/boot/README.MACH +++ b/sys/i386/boot/README.MACH @@ -1,6 +1,4 @@ /* - * Ported to boot 386BSD by Julian Elischer, September 1992 - * * Mach Operating System * Copyright (c) 1992, 1991 Carnegie Mellon University * All Rights Reserved. @@ -24,15 +22,9 @@ * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. - */ - -/* - * HISTORY - * $Log: README,v $ - * Revision 2.2 92/04/04 11:33:55 rpd - * From 2.5 - * [92/03/30 mg32] - * + * + * from: Mach, Revision 2.2 92/04/04 11:33:55 rpd + * $Id$ */ ********NOTE: This is not all relevant to the 386BSD version********** diff --git a/sys/i386/boot/asm.S b/sys/i386/boot/asm.S new file mode 100644 index 0000000000..b2914285a4 --- /dev/null +++ b/sys/i386/boot/asm.S @@ -0,0 +1,260 @@ +/* + * Mach Operating System + * Copyright (c) 1992, 1991 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + * + * from: Mach, Revision 2.2 92/04/04 11:34:13 rpd + * $Id$ + */ + + +/* + Copyright 1988, 1989, 1990, 1991, 1992 + by Intel Corporation, Santa Clara, California. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Intel +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + +INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + + .file "asm.s" + +#include "asm.h" + + +CR0_PE_ON = 0x1 +CR0_PE_OFF = 0xfffffffe + +.globl _ouraddr + .text + +/* +# +# real_to_prot() +# transfer from real mode to protected mode. +*/ + +ENTRY(real_to_prot) + # guarantee that interrupt is disabled when in prot mode + cli + + # load the gdtr + addr32 + data32 + lgdt EXT(Gdtr) + + # set the PE bit of CR0 + mov %cr0, %eax + + data32 + or $CR0_PE_ON, %eax + mov %eax, %cr0 + + # make intrasegment jump to flush the processor pipeline and + # reload CS register + data32 + ljmp $0x18, $xprot + +xprot: + # we are in USE32 mode now + # set up the protected mode segment registers : DS, SS, ES + mov $0x20, %eax + movw %ax, %ds + movw %ax, %ss + movw %ax, %es + + # load idtr so we can debug + lidt EXT(Idtr_prot) + + ret + +/* +# +# prot_to_real() +# transfer from protected mode to real mode +# +*/ + +ENTRY(prot_to_real) + + # set up a dummy stack frame for the second seg change. + movl _ouraddr, %eax + sarl $4, %eax + pushw %ax + movw $xreal, %ax # gas botches pushw $xreal - extra bytes 0, 0 + pushw %ax # decode to add %al, (%eax) (%al usually 0) + + # Change to use16 mode. + ljmp $0x28, $x16 + +x16: + # clear the PE bit of CR0 + mov %cr0, %eax + data32 + and $CR0_PE_OFF, %eax + mov %eax, %cr0 + + # make intersegment jmp to flush the processor pipeline + # using the fake stack frame set up earlier + # and reload CS register + lret + +xreal: + # we are in real mode now + # set up the real mode segment registers : DS, SS, ES + movw %cs, %ax + movw %ax, %ds + movw %ax, %ss + movw %ax, %es + + # load idtr so we can debug + addr32 + data32 + lidt EXT(Idtr_real) + + data32 + ret + +/* +# +# startprog(phyaddr) +# start the program on protected mode where phyaddr is the entry point +# +*/ + +ENTRY(startprog) + push %ebp + mov %esp, %ebp + + # get things we need into registers + movl 0x8(%ebp), %ecx # entry offset + movl 0x0c(%ebp), %eax # &argv + + # make a new stack at 0:0xa0000 (big segs) + mov $0x10, %ebx + movw %bx, %ss + movl $0xa0000, %ebx + movl %ebx, %esp + + # push some number of args onto the stack + pushl $0 # nominally a cyl offset in the boot. + pushl 0x8(%eax) # argv[2] = bootdev + pushl 0x4(%eax) # argv[1] = howto + pushl $0 # dummy 'return' address + + # push on our entry address + mov $0x08, %ebx # segment + pushl %ebx + pushl %ecx + + # convert over the other data segs + mov $0x10, %ebx + movw %bx, %ds + movw %bx, %es + + # convert the PC (and code seg) + lret +/* +# +# pbzero( dst, cnt) +# where src is a virtual address and dst is a physical address +*/ + +ENTRY(pbzero) + push %ebp + mov %esp, %ebp + push %es + push %esi + push %edi + push %ecx + + cld + + # set %es to point at the flat segment + mov $0x10, %eax + movw %ax, %es + + mov 0x8(%ebp), %edi # destination + mov 0xc(%ebp), %ecx # count + mov $0x0, %eax # value + + rep + stosb + + pop %ecx + pop %edi + pop %esi + pop %es + pop %ebp + + ret +/* +# +# pcpy(src, dst, cnt) +# where src is a virtual address and dst is a physical address +# +*/ + +ENTRY(pcpy) + push %ebp + mov %esp, %ebp + push %es + push %esi + push %edi + push %ecx + + cld + + # set %es to point at the flat segment + mov $0x10, %eax + movw %ax, %es + + mov 0x8(%ebp), %esi # source + mov 0xc(%ebp), %edi # destination + mov 0x10(%ebp), %ecx # count + + rep + movsb + + pop %ecx + pop %edi + pop %esi + pop %es + pop %ebp + + ret + diff --git a/sys/i386/boot/asm.h b/sys/i386/boot/asm.h index 9b86637f4a..43242bdec0 100644 --- a/sys/i386/boot/asm.h +++ b/sys/i386/boot/asm.h @@ -1,6 +1,4 @@ /* - * Ported to Boot 386BSD by Julian Elsicher (julian@tfs.com) Sept. 1992 - * * Mach Operating System * Copyright (c) 1991,1990,1989 Carnegie Mellon University * All Rights Reserved. @@ -24,54 +22,11 @@ * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: asm.h,v $ - * Revision 2.7 92/02/29 15:33:41 rpd - * Added ENTRY2. - * [92/02/28 rpd] - * - * Revision 2.6 92/02/19 15:07:52 elf - * Changed #if __STDC__ to #ifdef __STDC__ - * [92/01/16 jvh] - * - * Revision 2.5 91/05/14 16:02:45 mrt - * Correcting copyright - * - * Revision 2.4 91/02/05 17:10:42 mrt - * Changed to new Mach copyright - * [91/02/01 17:30:29 mrt] - * - * Revision 2.3 90/12/20 16:35:27 jeffreyh - * changes for __STDC__ - * [90/12/06 jeffreyh] - * - * Revision 2.2 90/05/03 15:24:12 dbg - * First checkin. - * - * - * Typo on ENTRY if gprof - * [90/03/29 rvb] - * - * fix SVC for "ifdef wheeze" [kupfer] - * Fix the GPROF definitions. - * ENTRY(x) gets profiled iffdef GPROF. - * Entry(x) (and DATA(x)) is NEVER profiled. - * MCOUNT can be used by asm that intends to build a frame, - * after the frame is built. - * [90/02/26 rvb] * - * Add #define addr16 .byte 0x67 - * [90/02/09 rvb] - * Added LBi, SVC and ENTRY - * [89/11/10 09:51:33 rvb] - * - * New a.out and coff compatible .s files. - * [89/10/16 rvb] + * from: Mach, Revision 2.7 92/02/29 15:33:41 rpd + * $Id$ */ - #define S_ARG0 4(%esp) #define S_ARG1 8(%esp) #define S_ARG2 12(%esp) @@ -139,11 +94,8 @@ #endif wheeze +#define addr32 .byte 0x67 #define data32 .byte 0x66 -#define data16 .byte 0x66 -#define addr16 .byte 0x67 - - #ifdef GPROF #ifdef __STDC__ diff --git a/sys/i386/boot/asm.s b/sys/i386/boot/asm.s deleted file mode 100644 index 8802b13f6d..0000000000 --- a/sys/i386/boot/asm.s +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 - * - * Mach Operating System - * Copyright (c) 1992, 1991 Carnegie Mellon University - * All Rights Reserved. - * - * Permission to use, copy, modify and distribute this software and its - * documentation is hereby granted, provided that both the copyright - * notice and this permission notice appear in all copies of the - * software, derivative works or modified versions, and any portions - * thereof, and that both notices appear in supporting documentation. - * - * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" - * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR - * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. - * - * Carnegie Mellon requests users of this software to return to - * - * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU - * School of Computer Science - * Carnegie Mellon University - * Pittsburgh PA 15213-3890 - * - * any improvements or extensions that they make and grant Carnegie Mellon - * the rights to redistribute these changes. - */ - -/* - * HISTORY - * $Log: asm.s,v $ - * Revision 2.2 92/04/04 11:34:13 rpd - * Fix Intel Copyright as per B. Davies authorization. - * [92/04/03 rvb] - * From 2.5 boot: pruned inb(), outb(), and pzero(). - * [92/03/30 rvb] - * - * Revision 2.2 91/04/02 14:35:10 mbj - * Added _sp() => where is the stack at. [kupfer] - * Add Intel copyright - * [90/02/09 rvb] - * - */ - -/* - Copyright 1988, 1989, 1990, 1991, 1992 - by Intel Corporation, Santa Clara, California. - - All Rights Reserved - -Permission to use, copy, modify, and distribute this software and -its documentation for any purpose and without fee is hereby -granted, provided that the above copyright notice appears in all -copies and that both the copyright notice and this permission notice -appear in supporting documentation, and that the name of Intel -not be used in advertising or publicity pertaining to distribution -of the software without specific, written prior permission. - -INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE -INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, -IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR -CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, -NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - - .file "asm.s" - -#include "asm.h" - - -CR0_PE_ON = 0x1 -CR0_PE_OFF = 0xfffffffe - -.globl _ouraddr - .text - -/* -# -# real_to_prot() -# transfer from real mode to protected mode. -*/ - -ENTRY(real_to_prot) - # guarantee that interrupt is disabled when in prot mode - cli - - # load the gdtr - addr16 - data32 - lgdt EXT(Gdtr) - - # set the PE bit of CR0 - mov %cr0, %eax - - data32 - or $CR0_PE_ON, %eax - mov %eax, %cr0 - - # make intrasegment jump to flush the processor pipeline and - # reload CS register - data32 - ljmp $0x18, $xprot - -xprot: - # we are in USE32 mode now - # set up the protective mode segment registers : DS, SS, ES - mov $0x20, %eax - movw %ax, %ds - movw %ax, %ss - movw %ax, %es - - ret - -/* -# -# prot_to_real() -# transfer from protected mode to real mode -# -*/ - -ENTRY(prot_to_real) - - # set up a dummy stack frame for the second seg change. - movl _ouraddr, %eax - sarl $4, %eax - pushw %ax - pushw $xreal - - # Change to use16 mode. - ljmp $0x28, $x16 - -x16: - # clear the PE bit of CR0 - mov %cr0, %eax - data32 - and $CR0_PE_OFF, %eax - mov %eax, %cr0 - - - # make intersegment jmp to flush the processor pipeline - # using the fake stack frame set up earlier - # and reload CS register - lret - - -xreal: - # we are in real mode now - # set up the real mode segment registers : DS, SS, ES - movw %cs, %ax - movw %ax, %ds - movw %ax, %ss - movw %ax, %es - - data32 - ret - -/* -# -# startprog(phyaddr) -# start the program on protected mode where phyaddr is the entry point -# -*/ - -ENTRY(startprog) - push %ebp - mov %esp, %ebp - - - - # get things we need into registers - movl 0x8(%ebp), %ecx # entry offset - movl 0x0c(%ebp), %eax # &argv - - # make a new stack at 0:0xa0000 (big segs) - mov $0x10, %ebx - movw %bx, %ss - movl $0xa0000,%ebx - movl %ebx,%esp - - - # push some number of args onto the stack - pushl $0 # nominally a cyl offset in the boot. - pushl 0x8(%eax) # argv[2] = bootdev - pushl 0x4(%eax) # argv[1] = howto - pushl $0 # dummy 'return' address - - # push on our entry address - mov $0x08, %ebx # segment - pushl %ebx - pushl %ecx - - # convert over the other data segs - mov $0x10, %ebx - movw %bx, %ds - movw %bx, %es - - # convert the PC (and code seg) - lret -/* -# -# pbzero( dst, cnt) -# where src is a virtual address and dst is a physical address -*/ - -ENTRY(pbzero) - push %ebp - mov %esp, %ebp - push %es - push %esi - push %edi - push %ecx - - cld - - # set %es to point at the flat segment - mov $0x10, %eax - movw %ax, %es - - mov 0x8(%ebp), %edi # destination - mov 0xc(%ebp), %ecx # count - mov $0x0,%eax # value - - rep - stosb - - pop %ecx - pop %edi - pop %esi - pop %es - pop %ebp - - ret -/* -# -# pcpy(src, dst, cnt) -# where src is a virtual address and dst is a physical address -# -*/ - -ENTRY(pcpy) - push %ebp - mov %esp, %ebp - push %es - push %esi - push %edi - push %ecx - - cld - - # set %es to point at the flat segment - mov $0x10, %eax - movw %ax, %es - - mov 0x8(%ebp), %esi # source - mov 0xc(%ebp), %edi # destination - mov 0x10(%ebp), %ecx # count - - rep - movsb - - pop %ecx - pop %edi - pop %esi - pop %es - pop %ebp - - ret - diff --git a/sys/i386/boot/bios.S b/sys/i386/boot/bios.S new file mode 100644 index 0000000000..687ca18c7d --- /dev/null +++ b/sys/i386/boot/bios.S @@ -0,0 +1,329 @@ +/* + * Mach Operating System + * Copyright (c) 1992, 1991 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + * + * from: Mach, Revision 2.2 92/04/04 11:34:26 rpd + * $Id$ + */ + +/* + Copyright 1988, 1989, 1990, 1991, 1992 + by Intel Corporation, Santa Clara, California. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Intel +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + +INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + + .file "bios.s" + +#include "asm.h" + .text + +/* +# biosread(dev, cyl, head, sec, nsec, offset) +# Read "nsec" sectors from disk to offset "offset" in boot segment +# BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory +# Call with %ah = 0x2 +# %al = number of sectors +# %ch = cylinder +# %cl = sector +# %dh = head +# %dl = drive (0x80 for hard disk, 0x0 for floppy disk) +# %es:%bx = segment:offset of buffer +# Return: +# %al = 0x0 on success; err code on failure +*/ + +ENTRY(biosread) + push %ebp + mov %esp, %ebp + + push %ebx + push %ecx + push %edx + push %es + + movb 0x10(%ebp), %dh + movw 0x0c(%ebp), %cx + xchgb %ch, %cl # cylinder; the highest 2 bits of cyl is in %cl + rorb $2, %cl + movb 0x14(%ebp), %al + orb %al, %cl + incb %cl # sector; sec starts from 1, not 0 + movb 0x8(%ebp), %dl # device + movl 0x1c(%ebp), %ebx # offset + # prot_to_real will set %es to BOOTSEG + + call EXT(prot_to_real) # enter real mode + movb $0x2, %ah # subfunction + addr32 + movb 0x18(%ebp), %al # number of sectors + + sti + int $0x13 + cli + + mov %eax, %ebx # save return value (actually movw %ax, %bx) + + data32 + call EXT(real_to_prot) # back to protected mode + + xor %eax, %eax + movb %bh, %al # return value in %ax + + pop %es + pop %edx + pop %ecx + pop %ebx + pop %ebp + + ret + + +/* +# putc(ch) +# BIOS call "INT 10H Function 0Eh" to write character to console +# Call with %ah = 0x0e +# %al = character +# %bh = page +# %bl = foreground color ( graphics modes) +*/ + + +ENTRY(putc) + push %ebp + mov %esp, %ebp + push %ebx + push %ecx + + movb 0x8(%ebp), %cl + + call EXT(prot_to_real) + + data32 + mov $0x1, %ebx # %bh=0, %bl=1 (blue) + movb $0xe, %ah + movb %cl, %al + sti + int $0x10 # display a byte + cli + + data32 + call EXT(real_to_prot) + + pop %ecx + pop %ebx + pop %ebp + ret + + +/* +# getc() +# BIOS call "INT 16H Function 00H" to read character from keyboard +# Call with %ah = 0x0 +# Return: %ah = keyboard scan code +# %al = ASCII character +*/ + +ENTRY(getc) + push %ebp + mov %esp, %ebp + push %ebx # save %ebx + + call EXT(prot_to_real) + + movb $0x0, %ah + sti + int $0x16 + cli + + movb %al, %bl # real_to_prot uses %eax + + data32 + call EXT(real_to_prot) + + xor %eax, %eax + movb %bl, %al + + pop %ebx + pop %ebp + ret +/* +# ischar() +# if there is a character pending, return it; otherwise return 0 +# BIOS call "INT 16H Function 01H" to check whether a character is pending +# Call with %ah = 0x1 +# Return: +# If key waiting to be input: +# %ah = keyboard scan code +# %al = ASCII character +# Zero flag = clear +# else +# Zero flag = set +*/ +ENTRY(ischar) + push %ebp + mov %esp, %ebp + push %ebx + + call EXT(prot_to_real) # enter real mode + + xor %ebx, %ebx + movb $0x1, %ah + sti + int $0x16 + cli + data32 + jz nochar + movb %al, %bl + +nochar: + data32 + call EXT(real_to_prot) + + xor %eax, %eax + movb %bl, %al + + pop %ebx + pop %ebp + ret + +/* +# +# get_diskinfo(): return a word that represents the +# max number of sectors and heads and drives for this device +# +*/ + +ENTRY(get_diskinfo) + push %ebp + mov %esp, %ebp + push %es + push %ebx + push %ecx + push %edx + + movb 0x8(%ebp), %dl # diskinfo(drive #) + call EXT(prot_to_real) # enter real mode + + movb $0x8, %ah # ask for disk info + + sti + int $0x13 + cli + + jnc ok + /* + * Urk. Call failed. It is not supported for floppies by old BIOS's. + * Guess it's a 15-sector floppy. Initialize all the registers for + * documentation, although we only need head and sector counts. + */ + subb %ah, %ah # %ax = 0 + movb %al, %al + movb %ah, %bh # %bh = 0 + movb $2, %bl # %bl bits 0-3 = drive type, 2 = 1.2M + movb $79, %ch # max track + movb $15, %cl # max sector + movb $1, %dh # max head + movb $1, %dl # # floppy drives installed + # es:di = parameter table + # carry = 0 +ok: + + data32 + call EXT(real_to_prot) # back to protected mode + + xor %eax, %eax + + /*form a longword representing all this gunk*/ + movb %dh, %ah # max head + andb $0x3f, %cl # mask of cylinder gunk + movb %cl, %al # max sector (and # sectors) + + pop %edx + pop %ecx + pop %ebx + pop %es + pop %ebp + ret + +/* +# +# memsize(i) : return the memory size in KB. i == 0 for conventional memory, +# i == 1 for extended memory +# BIOS call "INT 12H" to get conventional memory size +# BIOS call "INT 15H, AH=88H" to get extended memory size +# Both have the return value in AX. +# +*/ + +ENTRY(memsize) + push %ebp + mov %esp, %ebp + push %ebx + + mov 8(%ebp), %ebx + + call EXT(prot_to_real) # enter real mode + + cmpb $0x1, %bl + data32 + je xext + + sti + int $0x12 + cli + data32 + jmp xdone + +xext: movb $0x88, %ah + sti + int $0x15 + cli + +xdone: + mov %eax, %ebx + + data32 + call EXT(real_to_prot) + + mov %ebx, %eax + pop %ebx + pop %ebp + ret diff --git a/sys/i386/boot/bios.s b/sys/i386/boot/bios.s deleted file mode 100644 index d1673a5600..0000000000 --- a/sys/i386/boot/bios.s +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 - * - * Mach Operating System - * Copyright (c) 1992, 1991 Carnegie Mellon University - * All Rights Reserved. - * - * Permission to use, copy, modify and distribute this software and its - * documentation is hereby granted, provided that both the copyright - * notice and this permission notice appear in all copies of the - * software, derivative works or modified versions, and any portions - * thereof, and that both notices appear in supporting documentation. - * - * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" - * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR - * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. - * - * Carnegie Mellon requests users of this software to return to - * - * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU - * School of Computer Science - * Carnegie Mellon University - * Pittsburgh PA 15213-3890 - * - * any improvements or extensions that they make and grant Carnegie Mellon - * the rights to redistribute these changes. - */ - -/* - * HISTORY - * $Log: bios.s,v $ - * Revision 2.2 92/04/04 11:34:26 rpd - * Fix Intel Copyright as per B. Davies authorization. - * [92/04/03 rvb] - * From 2.5 version - * [92/03/30 mg32] - * - * Revision 2.2 91/04/02 14:35:21 mbj - * Add Intel copyright - * [90/02/09 rvb] - * - */ - - -/* - Copyright 1988, 1989, 1990, 1991, 1992 - by Intel Corporation, Santa Clara, California. - - All Rights Reserved - -Permission to use, copy, modify, and distribute this software and -its documentation for any purpose and without fee is hereby -granted, provided that the above copyright notice appears in all -copies and that both the copyright notice and this permission notice -appear in supporting documentation, and that the name of Intel -not be used in advertising or publicity pertaining to distribution -of the software without specific, written prior permission. - -INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE -INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, -IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR -CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, -NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - - .file "bios.s" - -#include "asm.h" - .text - -/* -# biosread(dev, cyl, head, sec) -# Read one sector from disk into the internal buffer "intbuf" which -# is the first 512 bytes of the boot loader. -# BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory -# Call with %ah = 0x2 -# %al = number of sectors -# %ch = cylinder -# %cl = sector -# %dh = head -# %dl = drive (0x80 for hard disk, 0x0 for floppy disk) -# %es:%bx = segment:offset of buffer -# Return: -# %al = 0x0 on success; err code on failure -*/ - -ENTRY(biosread) - push %ebp - mov %esp, %ebp - - push %ebx - push %ecx - push %edx - push %es - - movb 0x10(%ebp), %dh - movw 0x0c(%ebp), %cx - xchgb %ch, %cl # cylinder; the highest 2 bits of cyl is in %cl - rorb $2, %cl - movb 0x14(%ebp), %al - orb %al, %cl - incb %cl # sector; sec starts from 1, not 0 - movb 0x8(%ebp), %dl # device - xor %ebx, %ebx # offset -- 0 - # prot_to_real will set %es to BOOTSEG - - call EXT(prot_to_real) # enter real mode - movb $0x2, %ah # subfunction - movb $0x1, %al # number of sectors -- one - - sti - int $0x13 - cli - - mov %eax, %ebx # save return value - - data16 - call EXT(real_to_prot) # back to protected mode - - xor %eax, %eax - movb %bh, %al # return value in %ax - - pop %es - pop %edx - pop %ecx - pop %ebx - pop %ebp - - ret - - -/* -# putc(ch) -# BIOS call "INT 10H Function 0Eh" to write character to console -# Call with %ah = 0x0e -# %al = character -# %bh = page -# %bl = foreground color ( graphics modes) -*/ - - -ENTRY(putc) - push %ebp - mov %esp, %ebp - push %ebx - push %ecx - - movb 0x8(%ebp), %cl - - call EXT(prot_to_real) - - data16 - mov $0x1, %ebx # %bh=0, %bl=1 (blue) - movb $0xe, %ah - movb %cl, %al - sti - int $0x10 # display a byte - cli - - data16 - call EXT(real_to_prot) - - pop %ecx - pop %ebx - pop %ebp - ret - - -/* -# getc() -# BIOS call "INT 16H Function 00H" to read character from keyboard -# Call with %ah = 0x0 -# Return: %ah = keyboard scan code -# %al = ASCII character -*/ - -ENTRY(getc) - push %ebp - mov %esp, %ebp - push %ebx # save %ebx - - call EXT(prot_to_real) - - movb $0x0, %ah - sti - int $0x16 - cli - - movb %al, %bl # real_to_prot uses %eax - - data16 - call EXT(real_to_prot) - - xor %eax, %eax - movb %bl, %al - - pop %ebx - pop %ebp - ret -/* -# ischar() -# if there is a character pending, return it; otherwise return 0 -# BIOS call "INT 16H Function 01H" to check whether a character is pending -# Call with %ah = 0x1 -# Return: -# If key waiting to be input: -# %ah = keyboard scan code -# %al = ASCII character -# Zero flag = clear -# else -# Zero flag = set -*/ -ENTRY(ischar) - push %ebp - mov %esp, %ebp - push %ebx - - call EXT(prot_to_real) # enter real mode - - xor %ebx, %ebx - movb $0x1, %ah - sti - int $0x16 - cli - data16 - jz nochar - movb %al, %bl - -nochar: - data16 - call EXT(real_to_prot) - - xor %eax, %eax - movb %bl, %al - - pop %ebx - pop %ebp - ret - -/* -# -# get_diskinfo(): return a word that represents the -# max number of sectors and heads and drives for this device -# -*/ - -ENTRY(get_diskinfo) - push %ebp - mov %esp, %ebp - push %es - push %ebx - push %ecx - push %edx - - movb 0x8(%ebp), %dl # diskinfo(drive #) - call EXT(prot_to_real) # enter real mode - - movb $0x8, %ah # ask for disk info - - sti - int $0x13 - cli - - data16 - call EXT(real_to_prot) # back to protected mode - - xor %eax, %eax - - /*form a longword representing all this gunk*/ - movb %dh, %ah # # heads - andb $0x3f, %cl # mask of cylinder gunk - movb %cl, %al # # sectors - - pop %edx - pop %ecx - pop %ebx - pop %es - pop %ebp - ret - -/* -# -# memsize(i) : return the memory size in KB. i == 0 for conventional memory, -# i == 1 for extended memory -# BIOS call "INT 12H" to get conventional memory size -# BIOS call "INT 15H, AH=88H" to get extended memory size -# Both have the return value in AX. -# -*/ - -ENTRY(memsize) - push %ebp - mov %esp, %ebp - push %ebx - - mov 8(%ebp), %ebx - - call EXT(prot_to_real) # enter real mode - - cmpb $0x1, %bl - data16 - je xext - - sti - int $0x12 - cli - data16 - jmp xdone - -xext: movb $0x88, %ah - sti - int $0x15 - cli - -xdone: - mov %eax, %ebx - - data16 - call EXT(real_to_prot) - - mov %ebx, %eax - pop %ebx - pop %ebp - ret diff --git a/sys/i386/boot/boot.c b/sys/i386/boot/boot.c index 805303e6da..1e15ab7073 100644 --- a/sys/i386/boot/boot.c +++ b/sys/i386/boot/boot.c @@ -1,6 +1,4 @@ /* - * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 - * * Mach Operating System * Copyright (c) 1992, 1991 Carnegie Mellon University * All Rights Reserved. @@ -25,28 +23,10 @@ * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00159 - * -------------------- ----- ---------------------- - * - * 23 May 93 Rodney W. Grimes Added pad to kernel size for structs - * allocated by locore.s + * from: Mach, [92/04/03 16:51:14 rvb] + * $Id: boot.c,v 1.7 1993/10/15 12:33:03 rgrimes Exp $ */ -/* - * HISTORY - * $Log: boot.c,v $ - * Revision 2.2 92/04/04 11:34:37 rpd - * Change date in banner. - * [92/04/03 16:51:14 rvb] - * - * Fix Intel Copyright as per B. Davies authorization. - * [92/04/03 rvb] - * From 2.5 version. - * [92/03/30 mg32] - * - */ /* Copyright 1988, 1989, 1990, 1991, 1992 @@ -76,13 +56,12 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include - struct exec head; int argv[10], esym; char *name; char *names[] = { - "/386bsd", "/386bsd.old", - "/vmunix", "/vmunix.old" + "/386bsd", "/o386bsd", "/386bsd.old", + "/vmunix", "/ovmunix", "/vmunix.old" }; #define NUMNAMES (sizeof(names)/sizeof(char *)) @@ -91,11 +70,14 @@ boot(drive) int drive; { int loadflags, currname = 0; - printf("\n>> 386bsd BOOT @ 0x%x: %d/%d k of memory [20/9/92]\n", + char *t; + + printf("\n>> FreeBSD BOOT @ 0x%x: %d/%d k of memory [%s]\n", ouraddr, argv[7] = memsize(0), - argv[8] = memsize(1)); - printf("use options hd(1,...... to boot sd0 when wd0 is also installed\n"); + argv[8] = memsize(1), + "$Revision: 1.7 $"); + printf("use hd(1,a)/386bsd to boot sd0 when wd0 is also installed\n"); gateA20(); loadstart: /***************************************************************\ @@ -126,6 +108,7 @@ loadprog(howto) { long int startaddr; long int addr; /* physical address.. not directly useable */ + long int addr0; int i; static int (*x_entry)() = 0; unsigned char tmpbuf[4096]; /* we need to load the first 4k here */ @@ -133,18 +116,18 @@ loadprog(howto) argv[3] = 0; argv[4] = 0; read(&head, sizeof(head)); - if (head.a_magic == 0413 ) - { - poff = 4096; - } - else - { + if ( N_BADMAG(head)) { printf("Invalid format!\n"); return; } + poff = N_TXTOFF(head); + /*if(poff==0) + poff = 32;*/ + startaddr = (int)head.a_entry; - addr = (startaddr & 0x00f00000); /* some MEG boundary */ + addr = (startaddr & 0x00ffffff); /* some MEG boundary */ + addr0 = addr; printf("Booting %s(%d,%c)%s @ 0x%x\n" , devs[maj] , unit @@ -158,22 +141,14 @@ loadprog(howto) printf("kernel will not fit below loader\n"); return; } - /* - * The +28672 is for memory allocated by locore.s that must - * fit in the bss! - */ - if((addr + head.a_text + head.a_data + head.a_bss + 28672) > 0xa0000) + if((addr + head.a_text + head.a_data + head.a_bss) > 0xa0000) { printf("kernel too big, won't fit in 640K with bss\n"); printf("Only hope is to link the kernel for > 1MB\n"); return; } - if((addr + head.a_text + head.a_data + head.a_bss) > ouraddr) - { - printf("loader overlaps bss, kernel must bzero\n"); - } } - printf("text=0x%x", head.a_text); + printf("text=0x%x ", head.a_text); /********************************************************/ /* LOAD THE TEXT SEGMENT */ /* don't clobber the first 4k yet (BIOS NEEDS IT) */ @@ -189,7 +164,7 @@ loadprog(howto) while (addr & CLOFSET) *(char *)addr++ = 0; - printf(" data=0x%x", head.a_data); + printf("data=0x%x ", head.a_data); xread(addr, head.a_data); addr += head.a_data; @@ -197,7 +172,7 @@ loadprog(howto) /* Skip over the uninitialised data */ /* (but clear it) */ /********************************************************/ - printf(" bss=0x%x", head.a_bss); + printf("bss=0x%x ", head.a_bss); if( (addr < ouraddr) && ((addr + head.a_bss) > ouraddr)) { pbzero(addr,ouraddr - (int)addr); @@ -220,7 +195,7 @@ loadprog(howto) /********************************************************/ /* READ in the symbol table */ /********************************************************/ - printf(" symbols=[+0x%x", head.a_syms); + printf("symbols=[+0x%x", head.a_syms); xread(addr, head.a_syms); addr += head.a_syms; @@ -237,7 +212,7 @@ loadprog(howto) /********************************************************/ /* and that many bytes of (debug symbols?) */ /********************************************************/ - printf("+0x%x]", i); + printf("+0x%x] ", i); xread(addr, i); addr += i; } @@ -247,7 +222,7 @@ loadprog(howto) /********************************************************/ argv[4] = ((addr+sizeof(int)-1))&~(sizeof(int)-1); - printf(" total=0x%x",argv[4]); + printf("total=0x%x ",argv[4]); /* @@ -264,8 +239,19 @@ loadprog(howto) switch(maj) { case 2: - printf("\n\nInsert file system floppy \n"); - getchar(); + printf("\n\nInsert file system floppy in drive A or B\n"); + printf("Press 'A', 'B' or any other key for the default "); + printf("%c: ", unit+'A'); + i = getchar(); + switch (i) { + case '0': case 'A': case 'a': + unit = 0; + break; + case '1': case 'B': case 'b': + unit = 1; + break; + } + printf("\n"); break; case 4: break; @@ -278,8 +264,10 @@ loadprog(howto) /****************************************************************/ /* copy that first page and overwrite any BIOS variables */ /****************************************************************/ - printf(" entry point=0x%x \n" ,((int)startaddr) & 0xffffff); - pcpy(tmpbuf, 0, 4096); + printf("entry point=0x%x\n" ,((int)startaddr) & 0xffffff); + /* Under no circumstances overwrite precious BIOS variables! */ + pcpy(tmpbuf, addr0, 0x400); + pcpy(tmpbuf + 0x500, addr0 + 0x500, 4096 - 0x500); startprog(((int)startaddr & 0xffffff),argv); } @@ -318,6 +306,7 @@ getbootdev(howto) *ptr++ = 0; } } - } + } else + printf("\n"); } diff --git a/sys/i386/boot/boot.h b/sys/i386/boot/boot.h index 28a7a220e5..f77f8823fb 100644 --- a/sys/i386/boot/boot.h +++ b/sys/i386/boot/boot.h @@ -1,6 +1,4 @@ /* - * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 - * * Mach Operating System * Copyright (c) 1992, 1991 Carnegie Mellon University * All Rights Reserved. @@ -24,15 +22,9 @@ * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. - */ - -/* - * HISTORY - * $Log: boot.h,v $ - * Revision 2.2 92/04/04 11:35:03 rpd - * Fabricated from 3.0 bootstrap. But too many things are global. - * [92/03/30 mg32] - * + * + * from: Mach, Revision 2.2 92/04/04 11:35:03 rpd + * $Id$ */ #include diff --git a/sys/i386/boot/boot2.S b/sys/i386/boot/boot2.S new file mode 100644 index 0000000000..2569dd2538 --- /dev/null +++ b/sys/i386/boot/boot2.S @@ -0,0 +1,167 @@ +/* + * Mach Operating System + * Copyright (c) 1992, 1991 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + * + * from: Mach, Revision 2.2 92/04/04 11:35:26 rpd + * $Id$ + */ + +#include "asm.h" + +/* Conventional GDT indexes. */ +#define BOOT_CS_INDEX 3 +#define BOOT_CS16_INDEX 5 +#define BOOT_DS_INDEX 4 +#define DB_CS_INDEX 14 +#define DB_CS16_INDEX 15 +#define DB_DS_INDEX 16 +#define GDT_INDEX 17 + +/* Vector numbers. */ +#define BREAKPOINT_VECTOR 3 +#define DEBUG_VECTOR 1 + +/* + * boot2() -- second stage boot + */ + +.globl EXT(ouraddr) + +ENTRY(boot2) + data32 + subl %eax, %eax + mov %cs, %ax + mov %ax, %ds + mov %ax, %es + data32 + shll $4, %eax + addr32 + data32 + movl %eax, EXT(ouraddr) + + /* fix up GDT entries for bootstrap */ +#define FIXUP(gdt_index) \ + addr32; \ + movl %eax, EXT(Gdt)+(8*gdt_index)+2; /* actually movw %ax */ \ + addr32; \ + movb %bl, EXT(Gdt)+(8*gdt_index)+4 + + data32 + shld $16, %eax, %ebx + FIXUP(BOOT_CS_INDEX) + FIXUP(BOOT_CS16_INDEX) + FIXUP(BOOT_DS_INDEX) + + /* fix up GDT entry for GDT, and GDT and IDT pointers */ + data32 + movl %eax, %ecx + data32 + addl $ EXT(Gdt), %eax + data32 + shld $16, %eax, %ebx + FIXUP(GDT_INDEX) + addr32 + data32 + movl %eax, EXT(Gdtr)+2 + data32 + addl $ EXT(Idt), %ecx + addr32 + data32 + movl %ecx, EXT(Idtr_prot)+2 + + /* %es = vector table segment for a while */ + push %es + data32 + subl %eax, %eax + mov %ax, %es + + /* fix up GDT entries for bdb */ + data32 + movl $4*DEBUG_VECTOR, %esi + addr32 + movl %es: 2(%esi), %eax /* actually movw to %ax */ + data32 + shll $4, %eax + data32 + shld $16, %eax, %ebx + FIXUP(DB_CS_INDEX) + FIXUP(DB_CS16_INDEX) + FIXUP(DB_DS_INDEX) + + /* Fetch entry points of bdb's protected mode trap handlers. These + * are stored at 2 before the corresponding entry points for real mode. + */ + data32 + subl %ebx, %ebx + addr32 + movl %es: (%esi), %ebx /* actually movw to %bx */ + data32 + subl %ecx, %ecx + addr32 + movl %es: 4*(BREAKPOINT_VECTOR-DEBUG_VECTOR)(%esi), %ecx + /* actually movw to %cx */ + + /* %es = bdb segment for a while */ + data32 + shrl $4, %eax + mov %ax, %es + + /* fix up IDT entries for bdb */ + movl %es: -2(%ebx), %eax /* actually movw to %ax */ + addr32 + movl %eax, EXT(Idt)+8*DEBUG_VECTOR /* actually movw %ax */ + movl %es: -2(%ecx), %eax /* actually movw to %ax */ + addr32 + movl %eax, EXT(Idt)+8*BREAKPOINT_VECTOR /* actually movw %ax */ + + /* finished with groping in real mode segments */ + pop %es + + /* change to protected mode */ + data32 + call EXT(real_to_prot) + + /* clear the bss */ + movl $ EXT(edata), %edi /* no EXT(_edata) - krufty ld */ + movl $ EXT(end), %ecx /* or EXT(_end) */ + subl %edi, %ecx + subb %al, %al + rep + stosb + + movzbl %dl, %edx /* discard head (%dh) and random high bits */ + pushl %edx + call EXT(boot) +oops: + hlt + jmp oops + + .data + .align 2 +#if 0 /* XXX this would give losing "_ouraddr :". Better declared in C */ +EXT(ouraddr): +#else +_ouraddr: +#endif + .long 0 diff --git a/sys/i386/boot/boot2.s b/sys/i386/boot/boot2.s deleted file mode 100644 index d319edf6d2..0000000000 --- a/sys/i386/boot/boot2.s +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 - * - * Mach Operating System - * Copyright (c) 1992, 1991 Carnegie Mellon University - * All Rights Reserved. - * - * Permission to use, copy, modify and distribute this software and its - * documentation is hereby granted, provided that both the copyright - * notice and this permission notice appear in all copies of the - * software, derivative works or modified versions, and any portions - * thereof, and that both notices appear in supporting documentation. - * - * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" - * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR - * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. - * - * Carnegie Mellon requests users of this software to return to - * - * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU - * School of Computer Science - * Carnegie Mellon University - * Pittsburgh PA 15213-3890 - * - * any improvements or extensions that they make and grant Carnegie Mellon - * the rights to redistribute these changes. - */ - -/* - * HISTORY - * $Log: boot2.s,v $ - * Revision 2.2 92/04/04 11:35:26 rpd - * From 2.5 - * [92/03/30 rvb] - * - * Revision 2.2 91/04/02 14:39:21 mbj - * Put into rcs tree - * [90/02/09 rvb] - * - */ - -#include "asm.h" -#define LOADMSG 1 -/* - * boot2() -- second stage boot - */ - -.globl _ouraddr - -ENTRY(boot2) - movl %cs, %ax - movl %ax, %ds - movl %ax, %es - data32 - sall $4, %eax - data32 - movl %eax, _ouraddr - /* save the drive type and ID */ - data32 - pushl %edx - /* change to protected mode */ - data32 - call _real_to_prot - - call _boot - ret - - .data - .align 2 -_ouraddr: - .long 0 - - diff --git a/sys/i386/boot/disk.c b/sys/i386/boot/disk.c index 87b30b27fc..a8d1602a4a 100644 --- a/sys/i386/boot/disk.c +++ b/sys/i386/boot/disk.c @@ -1,6 +1,4 @@ /* - * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 - * * Mach Operating System * Copyright (c) 1992, 1991 Carnegie Mellon University * All Rights Reserved. @@ -24,23 +22,9 @@ * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. - */ - -/* - * HISTORY - * $Log: disk.c,v $ - * Revision 2.2 92/04/04 11:35:49 rpd - * Fabricated from 3.0 bootstrap and 2.5 boot disk.c: - * with support for scsi - * [92/03/30 mg32] - * - */ - -/* - * 9/20/92 - * Peng-Toh Sim. sim@cory.berkeley.edu - * Added bad144 support under 386bsd for wd's - * So, bad block remapping is done when loading the kernel. + * + * from: Mach, Revision 2.2 92/04/04 11:35:49 rpd + * $Id$ */ #include "boot.h" @@ -174,14 +158,44 @@ devread() } } +#define I_ADDR ((void *) 0) /* XXX where all reads go */ + +/* Read ahead buffer large enough for one track on a 1440K floppy. For + * reading from floppies, the bootstrap has to be loaded on a 64K boundary + * to ensure that this buffer doesn't cross a 64K DMA boundary. + */ +#define RA_SECTORS 18 +static char ra_buf[RA_SECTORS * BPS]; +static int ra_end; +static int ra_first; + Bread(dosdev,sector) int dosdev,sector; { - int cyl = sector/spc, head = (sector%spc)/spt, secnum = sector%spt; - while (biosread(dosdev, cyl,head,secnum)) + if (sector < ra_first || sector >= ra_end) { - printf("Error: C:%d H:%d S:%d\n",cyl,head,secnum); + int cyl, head, sec, nsec; + + cyl = sector/spc; + head = (sector % spc) / spt; + sec = sector % spt; + nsec = spt - sec; + if (nsec > RA_SECTORS) + nsec = RA_SECTORS; + twiddle(); + if (biosread(dosdev, cyl, head, sec, nsec, ra_buf) != 0) + { + nsec = 1; + twiddle(); + while (biosread(dosdev, cyl, head, sec, nsec, ra_buf) != 0) { + printf("Error: C:%d H:%d S:%d\n", cyl, head, sec); + twiddle(); + } + } + ra_first = sector; + ra_end = sector + nsec; } + bcopy(ra_buf + (sector - ra_first) * BPS, I_ADDR, BPS); } badsect(dosdev, sector) diff --git a/sys/i386/boot/io.c b/sys/i386/boot/io.c index 237b21cc2c..6a5a58a3e3 100644 --- a/sys/i386/boot/io.c +++ b/sys/i386/boot/io.c @@ -1,6 +1,4 @@ /* - * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 - * * Mach Operating System * Copyright (c) 1992, 1991 Carnegie Mellon University * All Rights Reserved. @@ -24,18 +22,9 @@ * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. - */ - -/* - * HISTORY - * $Log: io.c,v $ - * Revision 2.2 92/04/04 11:35:57 rpd - * Fixed for IBM L40's A20 initialization. - * [92/03/30 rvb] - * - * Created. - * [92/03/30 mg32] - * + * + * from: Mach, Revision 2.2 92/04/04 11:35:57 rpd + * $Id$ */ #include @@ -82,6 +71,8 @@ printf(format,data) { int *dataptr = &data; char c; + + reset_twiddle(); while (c = *format++) if (c != '%') putchar(c); @@ -187,3 +178,25 @@ int len; while (len-- > 0) *to++ = *from++; } + +static int tw_on; +static int tw_pos; +static char tw_chars[] = "|/-\\"; + +reset_twiddle() +{ + if (tw_on) + putchar('\b'); + tw_on = 0; + tw_pos = 0; +} + +twiddle() +{ + if (tw_on) + putchar('\b'); + else + tw_on = 1; + putchar(tw_chars[tw_pos++]); + tw_pos %= (sizeof(tw_chars) - 1); +} diff --git a/sys/i386/boot/rmaouthdr b/sys/i386/boot/rmaouthdr index d69d3917c1..4bb9c84924 100644 --- a/sys/i386/boot/rmaouthdr +++ b/sys/i386/boot/rmaouthdr @@ -1,12 +1,6 @@ #!/bin/csh -f -# Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 -# # -# HISTORY -# $Log: rmaouthdr,v $ -# Revision 2.2 92/04/04 11:36:01 rpd -# From 2.5 boot -# [92/03/30 mg32] -# +# from: Mach, Revision 2.2 92/04/04 11:36:01 rpd +# $Id$ # dd if=$1 of=$2 ibs=32 skip=1 obs=1024b diff --git a/sys/i386/boot/start.S b/sys/i386/boot/start.S new file mode 100644 index 0000000000..62b93ac50e --- /dev/null +++ b/sys/i386/boot/start.S @@ -0,0 +1,291 @@ +/* + * Mach Operating System + * Copyright (c) 1992, 1991 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + * + * from: Mach, Revision 2.2 92/04/04 11:36:29 rpd + * $Id$ + */ + +/* + Copyright 1988, 1989, 1990, 1991, 1992 + by Intel Corporation, Santa Clara, California. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Intel +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + +INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +#include "asm.h" + + .file "start.s" + +BOOTSEG = 0x9000 # boot will be loaded here (below 640K) +BOOTSTACK = 0xe000 # boot stack +SIGNATURE = 0xaa55 +LOADSZ = 15 # size of unix boot +PARTSTART = 0x1be # starting address of partition table +NUMPART = 4 # number of partitions in partition table +PARTSZ = 16 # each partition table entry is 16 bytes +BSDPART = 0xA5 # value of boot_ind, means bootable partition +BOOTABLE = 0x80 # value of boot_ind, means bootable partition + + .text + +ENTRY(boot1) + # start (aka boot1) is loaded at 0x0:0x7c00 but we want 0x7c0:0 + # ljmp to the next instruction to adjust %cs + data32 + ljmp $0x7c0, $start + +start: + # set up %ds + mov %cs, %ax + mov %ax, %ds + + # set up %ss and %esp + data32 + mov $BOOTSEG, %eax + mov %ax, %ss + data32 + mov $BOOTSTACK, %esp + + /*** set up %es, (where we will load boot2 to) ***/ + mov %ax, %es + +#ifdef DEBUG + data32 + mov $one, %esi + data32 + call message +#endif + + # bootstrap passes us drive number in %dl + cmpb $0x80, %dl + data32 + jae hd + +fd: +# reset the disk system +#ifdef DEBUG + data32 + mov $two, %esi + data32 + call message +#endif + movb $0x0, %ah + int $0x13 + data32 + mov $0x0001, %ecx # cyl 0, sector 1 + movb $0, %dh # head +#ifdef DEBUG + data32 + mov $three, %esi + data32 + call message +#endif + data32 + jmp load + +hd: /**** load sector 0 into the BOOTSEG ****/ +#ifdef DEBUG + data32 + mov $four, %esi + data32 + call message +#endif + data32 + mov $0x0201, %eax + xor %ebx, %ebx # %bx = 0 + data32 + mov $0x0001, %ecx +#ifdef DEBUG + data32 + mov $five, %esi + data32 + call message +#endif + data32 + andl $0xff, %edx + /*mov $0x0080, %edx*/ + int $0x13 + data32 + jb read_error + + /***# find the first 386BSD partition *****/ + data32 + mov $PARTSTART, %ebx + data32 + mov $NUMPART, %ecx +again: + addr32 + movb %es:4(%ebx), %al + cmpb $BSDPART, %al + data32 + je found + data32 + add $PARTSZ, %ebx + data32 + loop again + data32 + mov $enoboot, %esi + data32 + jmp err_stop + + +/* +# BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory +# Call with %ah = 0x2 +# %al = number of sectors +# %ch = cylinder +# %cl = sector +# %dh = head +# %dl = drive (0x80 for hard disk, 0x0 for floppy disk) +# %es:%bx = segment:offset of buffer +# Return: +# %al = 0x0 on success; err code on failure +*/ + +found: + addr32 + movb %es:1(%ebx), %dh /* head */ + addr32 + movl %es:2(%ebx), %ecx /*sect, cyl (+ 2 bytes junk in top word) */ + +load: + movb $0x2, %ah /* function 2 */ + movb $LOADSZ, %al /* number of blocks */ + xor %ebx, %ebx /* %bx = 0, put it at 0 in the BOOTSEG */ + int $0x13 + data32 + jb read_error + + # ljmp to the second stage boot loader (boot2). + # After ljmp, %cs is BOOTSEG and boot1 (512 bytes) will be used + # as an internal buffer "intbuf". + +#ifdef DEBUG + data32 + mov $six, %esi + data32 + call message +#endif + data32 + ljmp $BOOTSEG, $ EXT(boot2) + +# +# read_error +# + +read_error: + data32 + mov $eread, %esi +err_stop: + data32 + call message + data32 + jmp stop + +# +# message: write the error message in %ds:%esi to console +# + +message: +/* + # Use BIOS "int 10H Function 0Eh" to write character in teletype mode + # %ah = 0xe %al = character + # %bh = page %bl = foreground color (graphics modes) +*/ + + data32 + push %eax + data32 + push %ebx + data32 + mov $0x0001, %ebx + cld + +nextb: + lodsb # load a byte into %al + cmpb $0x0, %al + data32 + je done + movb $0xe, %ah + int $0x10 # display a byte + data32 + jmp nextb +done: + data32 + pop %ebx + data32 + pop %eax + data32 + ret + +stop: hlt + data32 + jmp stop # halt doesnt actually halt forever + +/* error messages */ + +#ifdef DEBUG +one: String "1\r\n\0" +two: String "2\r\n\0" +three: String "3\r\n\0" +four: String "4\r\n\0" +five: String "5\r\n\0" +six: String "6\r\n\0" +seven: String "7\r\n\0" +#endif DEBUG +eread: String "Read error\r\n\0" +enoboot: String "No bootable partition\r\n\0" +endofcode: +/* throw in a partition in case we are block0 as well */ +/* flag, head, sec, cyl, typ, ehead, esect, ecyl, start, len */ + . = EXT(boot1) + PARTSTART + .byte 0x0,0,0,0,0,0,0,0 + .long 0,0 + .byte 0x0,0,0,0,0,0,0,0 + .long 0,0 + .byte 0x0,0,0,0,0,0,0,0 + .long 0,0 + .byte BOOTABLE,0,1,0,BSDPART,255,255,255 + .long 0,50000 +/* the last 2 bytes in the sector 0 contain the signature */ + . = EXT(boot1) + 0x1fe + .value SIGNATURE +ENTRY(disklabel) + . = EXT(boot1) + 0x400 diff --git a/sys/i386/boot/start.s b/sys/i386/boot/start.s deleted file mode 100644 index f6f4bf0e7d..0000000000 --- a/sys/i386/boot/start.s +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 - * - * Mach Operating System - * Copyright (c) 1992, 1991 Carnegie Mellon University - * All Rights Reserved. - * - * Permission to use, copy, modify and distribute this software and its - * documentation is hereby granted, provided that both the copyright - * notice and this permission notice appear in all copies of the - * software, derivative works or modified versions, and any portions - * thereof, and that both notices appear in supporting documentation. - * - * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" - * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR - * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. - * - * Carnegie Mellon requests users of this software to return to - * - * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU - * School of Computer Science - * Carnegie Mellon University - * Pittsburgh PA 15213-3890 - * - * any improvements or extensions that they make and grant Carnegie Mellon - * the rights to redistribute these changes. - */ - -/* - * HISTORY - * $Log: start.s,v $ - * Revision 2.2 92/04/04 11:36:29 rpd - * Fix Intel Copyright as per B. Davies authorization. - * [92/04/03 rvb] - * Need to zero dh on hd path; at least for an adaptec card. - * [92/01/14 rvb] - * - * From 2.5 boot: - * Flush digit printing. - * Fuse floppy and hd boot by using Int 21 to tell - * boot type (slightly dubious since Int 21 is DOS - * not BIOS) - * [92/03/30 mg32] - * - * Revision 2.2 91/04/02 14:42:04 mbj - * Fix the BIG boot bug. We had missed a necessary data - * before a xor that was clearing a register used later - * as an index register. - * [91/03/01 rvb] - * Remember floppy type for swapgeneric - * Add Intel copyright - * [90/02/09 rvb] - * - */ - - -/* - Copyright 1988, 1989, 1990, 1991, 1992 - by Intel Corporation, Santa Clara, California. - - All Rights Reserved - -Permission to use, copy, modify, and distribute this software and -its documentation for any purpose and without fee is hereby -granted, provided that the above copyright notice appears in all -copies and that both the copyright notice and this permission notice -appear in supporting documentation, and that the name of Intel -not be used in advertising or publicity pertaining to distribution -of the software without specific, written prior permission. - -INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE -INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, -IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR -CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, -NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -#include "asm.h" - - .file "start.s" - -BOOTSEG = 0x9000 # boot will be loaded at 640k-64k -BOOTSTACK = 0xe000 # boot stack -SIGNATURE = 0xaa55 -LOADSZ = 14 # size of unix boot -PARTSTART = 0x1be # starting address of partition table -NUMPART = 4 # number of partitions in partition table -PARTSZ = 16 # each partition table entry is 16 bytes -BSDPART = 0xA5 # value of boot_ind, means bootable partition -BOOTABLE = 0x80 # value of boot_ind, means bootable partition - - .text - -ENTRY(boot1) - - # boot1 is loaded at 0x0:0x7c00 - # ljmp to the next instruction to set up %cs - data32 - ljmp $0x7c0, $start - -start: - # set up %ds - mov %cs, %ax - mov %ax, %ds - - - # set up %ss and %esp - data32 - mov $BOOTSEG, %eax - mov %ax, %ss - data32 - mov $BOOTSTACK, %esp - - /*** set up %es, (where we will load boot2 to) ***/ - mov %ax, %es - -#ifdef DEBUG - data32 - mov $one, %esi - data32 - call message -#endif - # get the boot drive id - movb $0x33, %ah - movb $0x05, %al - int $0x21 - - cmpb $0x80, %dl - data32 - jge hd - -fd: -# reset the disk system -#ifdef DEBUG - data32 - mov $two, %esi - data32 - call message -#endif - movb $0x0, %ah - int $0x13 - data32 - mov $0x0001, %ecx # cyl 0, sector 1 - data32 -#ifdef DEBUG - data32 - mov $three, %esi - data32 - call message -#endif - jmp load - -hd: /**** load sector 0 into the BOOTSEG ****/ -#ifdef DEBUG - data32 - mov $four, %esi - data32 - call message -#endif - data32 - mov $0x0201, %eax - xor %ebx, %ebx # %bx = 0 - data32 - mov $0x0001, %ecx -#ifdef DEBUG - data32 - mov $five, %esi - data32 - call message -#endif - data32 - andl $0xff, %edx - /*mov $0x0080, %edx*/ - int $0x13 - data32 - jb read_error - - /***# find the bootable partition *****/ - data32 - mov $PARTSTART, %ebx - data32 - mov $NUMPART, %ecx -again: - addr16 - movb %es:4(%ebx), %al - cmpb $BSDPART, %al - data32 - je found - data32 - add $PARTSZ, %ebx - data32 - loop again - data32 - mov $enoboot, %esi - data32 - jmp err_stop - - -/* -# BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory -# Call with %ah = 0x2 -# %al = number of sectors -# %ch = cylinder -# %cl = sector -# %dh = head -# %dl = drive (0x80 for hard disk, 0x0 for floppy disk) -# %es:%bx = segment:offset of buffer -# Return: -# %al = 0x0 on success; err code on failure -*/ - -found: - addr16 - movb %es:1(%ebx), %dh /* head */ - addr16 - xor %ecx, %ecx - addr16 - movw %es:2(%ebx), %ecx /*sect,cyl (+ 2 bytes junk in top word )*/ - -load: - movb $0x2, %ah /* function 2 */ - movb $LOADSZ, %al /* number of blocks */ - xor %ebx, %ebx /* %bx = 0, put it at 0 in the BOOTSEG */ - int $0x13 - data32 - jb read_error - - # ljmp to the second stage boot loader (boot2). - # After ljmp, %cs is BOOTSEG and boot1 (512 bytes) will be used - # as an internal buffer "intbuf". - -#ifdef DEBUG - data32 - mov $six, %esi - data32 - call message -#endif - data32 - ljmp $BOOTSEG, $EXT(boot2) - -# -# read_error -# - -read_error: - - data32 - mov $eread, %esi -err_stop: - data32 - call message - data32 - jmp stop - -# -# message: write the error message in %ds:%esi to console -# - -message: - # Use BIOS "int 10H Function 0Eh" to write character in teletype mode - # %ah = 0xe %al = character - # %bh = page %bl = foreground color (graphics modes) - - data32 - push %eax - data32 - push %ebx - data32 - mov $0x0001, %ebx - cld - -nextb: - lodsb # load a byte into %al - cmpb $0x0, %al - data32 - je done - movb $0xe, %ah - int $0x10 # display a byte - data32 - jmp nextb -done: - data32 - pop %ebx - data32 - pop %eax - data32 - ret - -stop: hlt - data32 - jmp stop # halt doesnt actually halt forever - -/* error messages */ - -#ifdef DEBUG -one: String "1\r\n\0" -two: String "2\r\n\0" -three: String "3\r\n\0" -four: String "4\r\n\0" -five: String "5\r\n\0" -six: String "6\r\n\0" -seven: String "7\r\n\0" -#endif DEBUG -eread: String "Read error\r\n\0" -enoboot: String "No bootable partition\r\n\0" -endofcode: -/* throw in a partition in case we are block0 as well */ -/* flag,head,sec,cyl,typ,ehead,esect,ecyl,start,len */ - . = EXT(boot1) + PARTSTART - .byte 0x0,0,0,0,0,0,0,0 - .long 0,0 - .byte 0x0,0,0,0,0,0,0,0 - .long 0,0 - .byte 0x0,0,0,0,0,0,0,0 - .long 0,0 - .byte BOOTABLE,0,1,0,BSDPART,255,255,255 - .long 0,50000 -/* the last 2 bytes in the sector 0 contain the signature */ - . = EXT(boot1) + 0x1fe - .value SIGNATURE -ENTRY(disklabel) - . = EXT(boot1) + 0x400 diff --git a/sys/i386/boot/sys.c b/sys/i386/boot/sys.c index bb34340da4..4ffe17194d 100644 --- a/sys/i386/boot/sys.c +++ b/sys/i386/boot/sys.c @@ -1,6 +1,4 @@ /* - * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 - * * Mach Operating System * Copyright (c) 1992, 1991 Carnegie Mellon University * All Rights Reserved. @@ -24,15 +22,9 @@ * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. - */ - -/* - * HISTORY - * $Log: sys.c,v $ - * Revision 2.2 92/04/04 11:36:34 rpd - * Fabricated from 3.0 bootstrap and scratch. - * [92/03/30 mg32] - * + * + * from: Mach, Revision 2.2 92/04/04 11:36:34 rpd + * $Id$ */ #include "boot.h" diff --git a/sys/i386/boot/table.c b/sys/i386/boot/table.c index c26490ad06..c53173d5aa 100644 --- a/sys/i386/boot/table.c +++ b/sys/i386/boot/table.c @@ -1,6 +1,4 @@ /* - * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 - * * Mach Operating System * Copyright (c) 1992, 1991 Carnegie Mellon University * All Rights Reserved. @@ -24,21 +22,9 @@ * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. - */ - -/* - * HISTORY - * $Log: table.c,v $ - * Revision 2.2 92/04/04 11:36:43 rpd - * Fix Intel Copyright as per B. Davies authorization. - * [92/04/03 rvb] - * Taken from 2.5 bootstrap. - * [92/03/30 rvb] - * - * Revision 2.2 91/04/02 14:42:22 mbj - * Add Intel copyright - * [90/02/09 rvb] - * + * + * from: Mach, Revision 2.2 92/04/04 11:36:43 rpd + * $Id$ */ /* @@ -64,9 +50,6 @@ NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define NGDTENT 6 -#define GDTLIMIT 48 /* NGDTENT * 8 */ - /* Segment Descriptor * * 31 24 19 16 7 0 @@ -85,21 +68,51 @@ struct seg_desc { unsigned short limit_15_0; unsigned short base_15_0; unsigned char base_23_16; - unsigned char bit_15_8; - unsigned char bit_23_16; + unsigned char p_dpl_type; + unsigned char g_b_a_limit; unsigned char base_31_24; }; +#define RUN 0 /* not really 0, but filled in at boot time */ -struct seg_desc Gdt[NGDTENT] = { +struct seg_desc Gdt[] = { {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, /* 0x0 : null */ {0xFFFF, 0x0, 0x0, 0x9F, 0xCF, 0x0}, /* 0x08 : kernel code */ + /* 0x9E? */ {0xFFFF, 0x0, 0x0, 0x93, 0xCF, 0x0}, /* 0x10 : kernel data */ - {0xFFFF, 0x0000, 0x9, 0x9E, 0x40, 0x0}, /* 0x18 : boot code */ - {0xFFFF, 0x0000, 0x9, 0x92, 0x40, 0x0}, /* 0x20 : boot data */ - {0xFFFF, 0x0000, 0x9, 0x9E, 0x0, 0x0} /* 0x28 : boot code, 16 bits */ - }; + /* 0x92? */ + {0xFFFF, RUN, RUN, 0x9E, 0x40, 0x0}, /* 0x18 : boot code */ + {0xFFFF, RUN, RUN, 0x92, 0x40, 0x0}, /* 0x20 : boot data */ + {0xFFFF, RUN, RUN, 0x9E, 0x0, 0x0}, /* 0x28 : boot code, 16 bits */ + /* More for bdb. */ + {}, /* BIOS_CS_INDEX = 6 : null */ + {}, /* BIOS_TMP_INDEX = 7 : null */ + {}, /* TSS_INDEX = 8 : null */ + {0xFFFF, 0x0, 0x0, 0xB2, 0x40, 0x0}, /* DS_286_INDEX = 9 */ + {0xFFFF, 0x0, 0x0, 0xB2, 0x40, 0x0}, /* ES_286_INDEX = 10 */ + {}, /* Unused = 11 : null */ + {0x7FFF, 0x8000, 0xB, 0xB2, 0x40, 0x0}, /* COLOR_INDEX = 12 */ + {0x7FFF, 0x0, 0xB, 0xB2, 0x40, 0x0}, /* MONO_INDEX = 13 */ + {0xFFFF, RUN, RUN, 0x9A, 0x40, 0x0}, /* DB_CS_INDEX = 14 */ + {0xFFFF, RUN, RUN, 0x9A, 0x0, 0x0}, /* DB_CS16_INDEX = 15 */ + {0xFFFF, RUN, RUN, 0x92, 0x40, 0x0}, /* DB_DS_INDEX = 16 */ + {8*18-1, RUN, RUN, 0x92, 0x40, 0x0}, /* GDT_INDEX = 17 */ +}; + +struct idt_desc { + unsigned short entry_15_0; + unsigned short selector; + unsigned char padding; + unsigned char p_dpl_type; + unsigned short entry_31_16; +}; +struct idt_desc Idt[] = { + {}, /* Null (int 0) */ + {RUN, 0x70, 0, 0x8E, 0}, /* DEBUG_VECTOR = 1 */ + {}, /* Null (int 2) */ + {RUN, 0x70, 0, 0xEE, 0}, /* BREAKPOINT_VECTOR = 3 */ +}; struct pseudo_desc { unsigned short limit; @@ -107,6 +120,6 @@ struct pseudo_desc { unsigned short base_high; }; -struct pseudo_desc Gdtr = { GDTLIMIT, 0x0400, 9 }; -struct pseudo_desc Gdtr2 = { GDTLIMIT, 0xfe00, 9 }; - /* boot is loaded at 0x90000, Gdt is at boot+1024 */ +struct pseudo_desc Gdtr = { sizeof Gdt - 1, RUN, RUN }; +struct pseudo_desc Idtr_prot = { sizeof Idt - 1, RUN, RUN }; +struct pseudo_desc Idtr_real = { 0x400 - 1, 0x0, 0x0 }; diff --git a/sys/i386/conf/AHATEST b/sys/i386/conf/AHATEST deleted file mode 100644 index 1195df4e70..0000000000 --- a/sys/i386/conf/AHATEST +++ /dev/null @@ -1,59 +0,0 @@ -# -# SCSITEST -- Generic ISA machine -- scsi test kernel -# -machine "i386" -cpu "i386" -ident SCSITEST -timezone 8 dst -maxusers 10 -options INET,ISOFS,NFS -options "COMPAT_43" -options "TCP_COMPAT_42" - -config "386bsd" root on wd0 swap on wd0 and sd0 - -controller isa0 -controller wd0 at isa? port "IO_WD1" bio irq 14 vector wdintr -disk wd0 at wd0 drive 0 -disk wd0 at wd0 drive 1 - -controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr -disk fd0 at fd0 drive 0 -disk fd1 at fd0 drive 1 - -device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint -device npx0 at isa? port "IO_NPX" irq 13 vector npxintr -device com1 at isa? port "IO_COM1" tty irq 4 vector comintr -device com2 at isa? port "IO_COM2" tty irq 3 vector comintr - -controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr -controller aha1 at isa? port "IO_AHA1" bio irq 12 drq 7 vector ahaintr -#controller bt0 at isa? port "IO_BT0" bio irq 12 vector btintr -controller scbus0 - -device sd0 -device sd1 -device sd2 -device sd3 - -device st0 -device st1 -device st2 -device st3 - -device cd0 -device cd1 - -device we0 at isa? port 0x300 net irq 10 iomem 0xd0000 iosiz 8192 vector weintr - - -pseudo-device loop -pseudo-device ether -pseudo-device sl 2 -pseudo-device log -pseudo-device ddb -pseudo-device pty 4 - -pseudo-device swappager -pseudo-device vnodepager -pseudo-device devpager diff --git a/sys/i386/conf/AHBTEST b/sys/i386/conf/AHBTEST deleted file mode 100644 index 06780c78e6..0000000000 --- a/sys/i386/conf/AHBTEST +++ /dev/null @@ -1,60 +0,0 @@ -# -# SCSITEST -- Generic ISA machine -- scsi test kernel -# -machine "i386" -cpu "i386" -ident SCSITEST -timezone 8 dst -maxusers 10 -options INET,ISOFS,NFS -options "COMPAT_43" -options "TCP_COMPAT_42" - -config "386bsd" root on sd0 swap on sd0 - -controller isa0 -controller wd0 at isa? port "IO_WD1" bio irq 14 vector wdintr -disk wd0 at wd0 drive 0 -disk wd0 at wd0 drive 1 - -controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr -disk fd0 at fd0 drive 0 -disk fd1 at fd0 drive 1 - -device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint -device npx0 at isa? port "IO_NPX" irq 13 vector npxintr -device com1 at isa? port "IO_COM1" tty irq 4 vector comintr -device com2 at isa? port "IO_COM2" tty irq 3 vector comintr - -#controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr -#controller aha1 at isa? port "IO_AHA1" bio irq 12 drq 7 vector ahaintr -#controller bt0 at isa? port "IO_BT0" bio irq 12 vector btintr -controller ahb0 at isa? bio irq 12 vector ahbintr -controller scbus0 - -device sd0 -device sd1 -device sd2 -device sd3 - -device st0 -device st1 -device st2 -device st3 - -device cd0 -device cd1 - -device we0 at isa? port 0x280 net irq 2 iomem 0xd0000 iosiz 8192 vector weintr - - -pseudo-device loop -pseudo-device ether -pseudo-device sl 2 -pseudo-device log -pseudo-device ddb -pseudo-device pty 4 - -pseudo-device swappager -pseudo-device vnodepager -pseudo-device devpager diff --git a/sys/i386/conf/BTTEST b/sys/i386/conf/BTTEST deleted file mode 100644 index ebc46c5205..0000000000 --- a/sys/i386/conf/BTTEST +++ /dev/null @@ -1,59 +0,0 @@ -# -# SCSITEST -- Generic ISA machine -- scsi test kernel -# -machine "i386" -cpu "i386" -ident SCSITEST -timezone 8 dst -maxusers 10 -options INET,ISOFS,NFS -options "COMPAT_43" -options "TCP_COMPAT_42" - -config "386bsd" root on sd0 swap on sd0 - -controller isa0 -controller wd0 at isa? port "IO_WD1" bio irq 14 vector wdintr -disk wd0 at wd0 drive 0 -disk wd0 at wd0 drive 1 - -controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr -disk fd0 at fd0 drive 0 -disk fd1 at fd0 drive 1 - -device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint -device npx0 at isa? port "IO_NPX" irq 13 vector npxintr -device com1 at isa? port "IO_COM1" tty irq 4 vector comintr -device com2 at isa? port "IO_COM2" tty irq 3 vector comintr - -#controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr -#controller aha1 at isa? port "IO_AHA1" bio irq 12 drq 7 vector ahaintr -controller bt0 at isa? port "IO_BT0" bio irq 12 vector btintr -controller scbus0 - -device sd0 -device sd1 -device sd2 -device sd3 - -device st0 -device st1 -device st2 -device st3 - -device cd0 -device cd1 - -device we0 at isa? port 0x280 net irq 2 iomem 0xd0000 iosiz 8192 vector weintr - - -pseudo-device loop -pseudo-device ether -pseudo-device sl 2 -pseudo-device log -pseudo-device ddb -pseudo-device pty 4 - -pseudo-device swappager -pseudo-device vnodepager -pseudo-device devpager diff --git a/sys/i386/conf/CODRV b/sys/i386/conf/CODRV deleted file mode 100644 index 62aa488af9..0000000000 --- a/sys/i386/conf/CODRV +++ /dev/null @@ -1,68 +0,0 @@ -# -# HOLGI -- Generic ISA kernel with X386 and CO device support -# -# for use with codrv-0.1.2 -# -# -machine "i386" -cpu "i386" -ident CODRV -timezone -1 -maxusers 16 -options INET,NFS -options "COMPAT_43" -options "TCP_COMPAT_42" -options "NO_COM_FIFO" - -# the following line enables PC3 terminal emulation -# this is downward compatible to pccons -options "PC3" - -config "386bsd" root on wd0 swap on wd0 and as0 - -controller isa0 -controller wd0 at isa? port "IO_WD1" bio irq 14 vector wdintr -disk wd0 at wd0 drive 0 -disk wd0 at wd0 drive 1 - -controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr -disk fd0 at fd0 drive 0 -disk fd1 at fd0 drive 1 - -controller as0 at isa? port 0x330 bio irq 11 drq 5 vector asintr -disk as0 at as0 drive 0 -disk as1 at as0 drive 1 - -# The following line enables codrv -device co0 at isa? port "IO_KBD" tty irq 1 vector cointr -#device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint -device npx0 at isa? port "IO_NPX" irq 13 vector npxintr -device com0 at isa? port "IO_COM1" tty irq 4 vector comintr -device com1 at isa? port "IO_COM2" tty irq 3 flags 0x01 vector comintr -device lpt0 at isa? port "IO_LPT1" tty irq 7 vector lptintr - -device we0 at isa? port 0x280 net irq 9 iomem 0xd0000 iosiz 8192 vector weintr -#device ne0 at isa? port 0x300 net irq 9 vector neintr -#device ec0 at isa? port 0x250 net irq 9 iomem 0xd8000 iosiz 8192 vector ecintr -#device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr -#device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr - -pseudo-device loop -pseudo-device ether -pseudo-device sl 2 -pseudo-device log -pseudo-device pty 8 - -# the following line sets the number of available virtual consoles -# a maximum of 12 is possible, but 4 or 8 should be sufficient -# note this does not relate to the number of ptys -pseudo-device vty 4 - -# This line must be present with codrv for some reasons -pseudo-device vtemul - -#pseudo-device ddb - -pseudo-device swappager -pseudo-device vnodepager -pseudo-device devpager diff --git a/sys/i386/conf/DEVEL b/sys/i386/conf/DEVEL deleted file mode 100644 index a325ce263b..0000000000 --- a/sys/i386/conf/DEVEL +++ /dev/null @@ -1,35 +0,0 @@ -# -# Developement config -# -machine "i386" -cpu "i386" -ident DEVEL -timezone 8 dst -maxusers 2 -options INET,DDB -options "COMPAT_43" -options "TCP_COMPAT_42" - -config "386bsd" root on dk0 swap on dk0 - -controller isa0 -controller wd0 at isa? port "IO_WD1" bio irq 14 vector wdintr -disk dk0 at wd0 drive 0 -controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr -disk dk4 at fd0 drive 0 -disk dk5 at fd0 drive 1 - -device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint -device npx0 at isa? port "IO_NPX" irq 13 vector npxintr -device com0 at isa? port "IO_COM1" tty irq 4 vector comintr -# device com1 at isa? port "IO_COM2" tty irq 3 vector comintr -device we0 at isa? port 0x280 net irq 3 iomem 0xd0000 iosiz 8192 vector weintr - -pseudo-device loop -pseudo-device ether -pseudo-device sl 1 -pseudo-device pty 2 - -pseudo-device swappager -pseudo-device vnodepager -pseudo-device devpager diff --git a/sys/i386/conf/GENERICAH b/sys/i386/conf/GENERICAH index 5f8ed03c7e..b30211fdae 100644 --- a/sys/i386/conf/GENERICAH +++ b/sys/i386/conf/GENERICAH @@ -1,39 +1,26 @@ # -# GENERICISA -- Generic ISA machine -- distribution floppy +# GENERICAH -- Generic machine with WD/AHx family disks # -# -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 3 00158 -# -------------------- ----- ---------------------- -# -# 09 Feb 93 Nate Williams Added options for X code to -# compiled in by default -# 29 Mar 93 Rodney W. Grimes Fixed com ports to be com0, com1 -# Added com2, com3, lpt, lpa -# Replaced as0 with Julian SCSI -# Fixed secound wd0 to be wd1 -# Cleaned up file, added comments -# Added ahb, order is important! -# Added pseudo-device speaker -# Note: All IO_xxx names are 1 based -# 17 May 93 Rodney W. Grimes Updated IRQ's 2 to IRQ 9's for -# intr-0.1 code. Changed ahb0 from -# irq 12 to irq 11 the real default +# $Id: GENERICAH,v 1.15 1993/10/16 12:28:32 rgrimes Exp $ # machine "i386" -cpu "i386" -ident GENERICISA +cpu "I386_CPU" +cpu "I486_CPU" +ident GENERICAH timezone 8 dst maxusers 10 +maxfdescs 2048 #Max file descriptors per process +options MATH_EMULATE #Support for x87 emulation options INET #InterNETworking options ISOFS #ISO File System options NFS #Network File System +options PCFS #MSDOS File System options "COMPAT_43" #Compatible with BSD 4.3 options "TCP_COMPAT_42" #TCP/IP compatible with 4.2 options XSERVER #Xserver options UCONSOLE #X Console support +#options GATEWAY #Host is a Gateway (forwards packets) config "386bsd" root on wd0 swap on wd0 and sd0 @@ -58,36 +45,41 @@ device sd3 device st0 device st1 -device st2 -device st3 -device cd0 -device cd1 +device cd0 #Only need one of these, the code dynamically grows + +device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr +device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint device npx0 at isa? port "IO_NPX" irq 13 vector npxintr -device com0 at isa? port "IO_COM1" tty irq 4 vector comintr -device com1 at isa? port "IO_COM2" tty irq 3 vector comintr -device com2 at isa? port "IO_COM3" tty irq 5 vector comintr -device com3 at isa? port "IO_COM4" tty irq 9 vector comintr +#The old drivers are rarely needed anymore... +#device com0 at isa? port "IO_COM1" tty irq 4 vector comintr +#device com1 at isa? port "IO_COM2" tty irq 3 vector comintr +#device com2 at isa? port "IO_COM3" tty irq 5 vector comintr +#device com3 at isa? port "IO_COM4" tty irq 9 vector comintr + +#For high speed serial lines. +device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr +device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr +device sio2 at isa? port "IO_COM3" tty irq 5 vector siointr +device sio3 at isa? port "IO_COM4" tty irq 9 vector siointr device lpt0 at isa? port "IO_LPT3" tty irq 7 vector lptintr device lpa0 at isa? port "IO_LPT1" tty device lpa1 at isa? port "IO_LPT2" tty -device we0 at isa? port 0x280 net irq 9 iomem 0xd0000 iosiz 8192 vector weintr -device ne0 at isa? port 0x300 net irq 9 vector neintr -device ec0 at isa? port 0x250 net irq 9 iomem 0xd8000 iosiz 8192 vector ecintr +device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr +device ed1 at isa? port 0x300 net irq 5 iomem 0xd8000 vector edintr +device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr -device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr - pseudo-device loop pseudo-device ether pseudo-device log pseudo-device sl 2 -pseudo-device pty 4 +pseudo-device pty 12 pseudo-device speaker pseudo-device swappager diff --git a/sys/i386/conf/GENERICBT b/sys/i386/conf/GENERICBT new file mode 100644 index 0000000000..7c91d1d924 --- /dev/null +++ b/sys/i386/conf/GENERICBT @@ -0,0 +1,87 @@ +# +# GENERICBT -- Generic machine with WD/BTx family disks +# +# $Id: GENERICBT,v 1.14 1993/10/16 12:28:33 rgrimes Exp $ +# + +machine "i386" +cpu "I386_CPU" +cpu "I486_CPU" +ident GENERICBT +timezone 8 dst +maxusers 10 +maxfdescs 2048 #Max file descriptors per process +options MATH_EMULATE #Support for x87 emulation +options INET #InterNETworking +options ISOFS #ISO File System +options NFS #Network File System +options PCFS #MSDOS File System +options "COMPAT_43" #Compatible with BSD 4.3 +options "TCP_COMPAT_42" #TCP/IP compatible with 4.2 +options XSERVER #Xserver +options UCONSOLE #X Console support +#options GATEWAY #Host is a Gateway (forwards packets) + +config "386bsd" root on wd0 swap on wd0 and sd0 + +controller isa0 + +controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr +disk fd0 at fd0 drive 0 +disk fd1 at fd0 drive 1 + +controller wd0 at isa? port "IO_WD1" bio irq 14 vector wdintr +disk wd0 at wd0 drive 0 +disk wd1 at wd0 drive 1 + +controller bt0 at isa? port "IO_BT0" bio irq 12 vector btintr +controller uha0 at isa? port "IO_UHA0" bio irq 14 drq 5 vector uhaintr +controller scbus0 + +device sd0 +device sd1 +device sd2 +device sd3 + +device st0 +device st1 + +device cd0 #Only need one of these, the code dynamically grows + +device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr +device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr + +device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint +device npx0 at isa? port "IO_NPX" irq 13 vector npxintr + +#The old drivers are rarely needed anymore... +#device com0 at isa? port "IO_COM1" tty irq 4 vector comintr +#device com1 at isa? port "IO_COM2" tty irq 3 vector comintr +#device com2 at isa? port "IO_COM3" tty irq 5 vector comintr +#device com3 at isa? port "IO_COM4" tty irq 9 vector comintr + +#For high speed serial lines. +device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr +device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr +device sio2 at isa? port "IO_COM3" tty irq 5 vector siointr +device sio3 at isa? port "IO_COM4" tty irq 9 vector siointr + +device lpt0 at isa? port "IO_LPT3" tty irq 7 vector lptintr +device lpa0 at isa? port "IO_LPT1" tty +device lpa1 at isa? port "IO_LPT2" tty + +device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr +device ed1 at isa? port 0x300 net irq 5 iomem 0xd8000 vector edintr +device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr +device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr + +pseudo-device loop +pseudo-device ether +pseudo-device log +pseudo-device sl 2 +pseudo-device pty 12 +pseudo-device speaker + +pseudo-device swappager +pseudo-device vnodepager +pseudo-device devpager diff --git a/sys/i386/conf/GENERICISA b/sys/i386/conf/GENERICISA deleted file mode 100644 index 5f8ed03c7e..0000000000 --- a/sys/i386/conf/GENERICISA +++ /dev/null @@ -1,95 +0,0 @@ -# -# GENERICISA -- Generic ISA machine -- distribution floppy -# -# -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 3 00158 -# -------------------- ----- ---------------------- -# -# 09 Feb 93 Nate Williams Added options for X code to -# compiled in by default -# 29 Mar 93 Rodney W. Grimes Fixed com ports to be com0, com1 -# Added com2, com3, lpt, lpa -# Replaced as0 with Julian SCSI -# Fixed secound wd0 to be wd1 -# Cleaned up file, added comments -# Added ahb, order is important! -# Added pseudo-device speaker -# Note: All IO_xxx names are 1 based -# 17 May 93 Rodney W. Grimes Updated IRQ's 2 to IRQ 9's for -# intr-0.1 code. Changed ahb0 from -# irq 12 to irq 11 the real default -# - -machine "i386" -cpu "i386" -ident GENERICISA -timezone 8 dst -maxusers 10 -options INET #InterNETworking -options ISOFS #ISO File System -options NFS #Network File System -options "COMPAT_43" #Compatible with BSD 4.3 -options "TCP_COMPAT_42" #TCP/IP compatible with 4.2 -options XSERVER #Xserver -options UCONSOLE #X Console support - -config "386bsd" root on wd0 swap on wd0 and sd0 - -controller isa0 - -controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr -disk fd0 at fd0 drive 0 -disk fd1 at fd0 drive 1 - -controller wd0 at isa? port "IO_WD1" bio irq 14 vector wdintr -disk wd0 at wd0 drive 0 -disk wd1 at wd0 drive 1 - -controller ahb0 at isa? bio irq 11 vector ahbintr -controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr -controller scbus0 - -device sd0 -device sd1 -device sd2 -device sd3 - -device st0 -device st1 -device st2 -device st3 - -device cd0 -device cd1 - -device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint -device npx0 at isa? port "IO_NPX" irq 13 vector npxintr - -device com0 at isa? port "IO_COM1" tty irq 4 vector comintr -device com1 at isa? port "IO_COM2" tty irq 3 vector comintr -device com2 at isa? port "IO_COM3" tty irq 5 vector comintr -device com3 at isa? port "IO_COM4" tty irq 9 vector comintr - -device lpt0 at isa? port "IO_LPT3" tty irq 7 vector lptintr -device lpa0 at isa? port "IO_LPT1" tty -device lpa1 at isa? port "IO_LPT2" tty - -device we0 at isa? port 0x280 net irq 9 iomem 0xd0000 iosiz 8192 vector weintr -device ne0 at isa? port 0x300 net irq 9 vector neintr -device ec0 at isa? port 0x250 net irq 9 iomem 0xd8000 iosiz 8192 vector ecintr -device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr - -device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr - -pseudo-device loop -pseudo-device ether -pseudo-device log -pseudo-device sl 2 -pseudo-device pty 4 -pseudo-device speaker - -pseudo-device swappager -pseudo-device vnodepager -pseudo-device devpager diff --git a/sys/i386/conf/LARGE b/sys/i386/conf/LARGE deleted file mode 100644 index 7ccb090da6..0000000000 --- a/sys/i386/conf/LARGE +++ /dev/null @@ -1,46 +0,0 @@ -# -# Everthing sans OSI (need to get an ACCUNET connection -wfj) -# -machine "i386" -cpu "i386" -ident LARGE -timezone 8 dst -maxusers 30 -options INET,NFS,MFS,HSFS,DIAGNOSTIC # OSI real soon -options "COMPAT_43","TCP_COMPAT_42" - -config "386bsd" root on dk0 swap on dk0 - -controller isa0 -controller wd0 at isa? port "IO_WD1" bio irq 14 vector wdintr -disk dk0 at wd0 drive 0 -disk dk1 at wd0 drive 1 - -controller as0 at isa? port 0x330 bio irq 11 drq 5 vector asintr -disk dk2 at as0 drive 0 -disk dk3 at as0 drive 1 - -controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr -disk dk4 at fd0 drive 0 -disk dk5 at fd0 drive 1 - -device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint -device npx0 at isa? port "IO_NPX" irq 13 vector npxintr -device com0 at isa? port "IO_COM1" tty irq 4 vector comintr - -device we0 at isa? port 0x280 net irq 3 iomem 0xd0000 iosiz 8192 vector weintr -device ne0 at isa? port 0x300 net irq 9 vector neintr - -device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr - -pseudo-device loop -pseudo-device pty -pseudo-device ether -pseudo-device sl 2 -pseudo-device log -pseudo-device ddb - - -pseudo-device swappager -pseudo-device vnodepager -pseudo-device devpager diff --git a/sys/i386/conf/LINT b/sys/i386/conf/LINT new file mode 100644 index 0000000000..290975b3c9 --- /dev/null +++ b/sys/i386/conf/LINT @@ -0,0 +1,130 @@ +# +# LINT -- config file for checking all the sources, tries to pull in +# as much of the source tree as it can. +# +# This kernel is NOT MEANT to be runnable! +# +# $Id: LINT,v 1.20 1993/10/23 22:20:47 jkh Exp $ +# + +machine "i386" +cpu "I386_CPU" +cpu "I486_CPU" +ident LINT +timezone 8 dst +maxusers 10 +maxfdescs 2048 #Max file descriptors per process +options MATH_EMULATE #Support for x87 emulation + +config "386bsd" at 0xFE100000 root on wd0 swap on wd0 and sd0 + +# +# options that appear as inline #ifdef's +# +options "COM_BIDIR" #Bidirectional support in sys/isa/sio.c +options "COM_MULTIPORT" #Multiport support in sys/isa/sio.c +options "COMPAT_43" #compatible with BSD 4.3 +options "SYMTAB_SPACE=102452" #This kernel needs LOTS of symtable +options GATEWAY #internetwork gateway +options KTRACE #kernel tracing +options "NCONS=8" #number of syscons virtual consoles +options "TCP_COMPAT_42" #tcp/ip compatible with 4.2 +options UCONSOLE #x console support +options XSERVER #xserver + +# +# options that are in sys/conf/files +# +pseudo-device bpfilter 4 #berkeley packet filter +options CCITT +device cd0 #Only need one of these, the code dynamically grows +device ch0 +pseudo-device ddb +pseudo-device devpager +options EON +pseudo-device ether +options FIFO +#pseudo-device imp +options INET #Internet communications protocols +options ISO +options ISOFS #ISO 9660 File System +pseudo-device loop +options MFS #Memroy File System +options NFS #Network File System +options NS #Xerox NS communications protocols +options PCFS #PC (MSDOS) File System +pseudo-device ppp 2 +pseudo-device pty 4 +options QUOTA #enable disk quotas +controller scbus0 +device sd0 +device sd1 +device sd2 +device sd3 +pseudo-device sl 2 +device st0 +device st1 +pseudo-device swappager +options SYSVSHM +options "SHMMAXPGS=64" # 256Kb of sharable memory +#pseudo-device tb #tablet line discipline. +pseudo-device tpip +#pseudo-device tun +pseudo-device vnodepager + +# +# options that are in sys/i386/conf/files.i386 +# +#This is needed here so the isa? below will work +controller isa0 + +controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr +controller ahb0 at isa? bio irq 11 vector ahbintr +controller bt0 at isa? port "IO_BT0" bio irq 12 vector btintr +device com0 at isa? port "IO_COM1" tty irq 4 vector comintr +device com1 at isa? port "IO_COM2" tty irq 3 vector comintr +device com2 at isa? port "IO_COM3" tty irq 5 vector comintr +device com3 at isa? port "IO_COM4" tty irq 9 vector comintr +#dcfclk device-driver +controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr +disk fd0 at fd0 drive 0 +disk fd1 at fd0 drive 1 +device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr +device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr +device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr +#device ix0 at isa? port 0x320 net irq 10 iomem 0xd0000 iosiz 32768 vector ixintr +#special cased above: +#controller isa0 +device lpa0 at isa? port "IO_LPT1" tty +device lpa1 at isa? port "IO_LPT2" tty +device lpt0 at isa? port "IO_LPT3" tty irq 7 vector lptintr +device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr +device mse0 at isa? port 0x23c tty irq 5 vector mseintr +device npx0 at isa? port "IO_NPX" irq 13 vector npxintr +device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint +#only one of pc0 or sc0 allowed +#device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr +device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr +device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr +device sio2 at isa? port "IO_COM3" tty irq 5 vector siointr +device sio3 at isa? port "IO_COM4" tty irq 9 vector siointr +pseudo-device speaker +#tw device-driver +controller uha0 at isa? port "IO_UHA0" bio irq 14 drq 5 vector uhaintr +controller wd0 at isa? port "IO_WD1" bio irq 14 vector wdintr +disk wd0 at wd0 drive 0 +disk wd1 at wd0 drive 1 +device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr + +# Various sound card drivers. +# See /sys/i386/doc/sound.doc for more information. +device snd5 at isa? port 0x330 irq 6 drq 0 vector mpuintr +device snd4 at isa? port 0x220 irq 15 drq 6 vector gusintr +device snd3 at isa? port 0x388 irq 12 drq 3 vector pasintr +device snd2 at isa? port 0x220 irq 7 drq 1 vector sbintr +device snd1 at isa? port 0x388 irq 0 drq 0 vector sbintr +# +# +# options that have not been resolved yet +# +pseudo-device log diff --git a/sys/i386/conf/MINITERM b/sys/i386/conf/MINITERM deleted file mode 100644 index ffa26fc975..0000000000 --- a/sys/i386/conf/MINITERM +++ /dev/null @@ -1,76 +0,0 @@ -# -# MINITERM -- Reduced size generic ISA kernel with CO device support -# -# for use with codrv-0.1.2 -# -machine "i386" -cpu "i386" -ident MINITERM -timezone -1 -maxusers 16 -options INET,NFS -options "COMPAT_43" -options "TCP_COMPAT_42" -options "NO_COM_FIFO" - -# the following line enables PC3 terminal emulation -# this is downward compatible to pccons -#options "PC3" - -# the following line removes some code from codrv in order to build -# a slim kernel for the installation disk -# Note: some more things have to be done in addition to this -# see options below -options "MINITERM" - -config "386bsd" root on wd0 swap on wd0 and as0 - -controller isa0 -controller wd0 at isa? port "IO_WD1" bio irq 14 vector wdintr -disk wd0 at wd0 drive 0 -disk wd0 at wd0 drive 1 - -controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr -disk fd0 at fd0 drive 0 -disk fd1 at fd0 drive 1 - -controller as0 at isa? port 0x330 bio irq 11 drq 5 vector asintr -disk as0 at as0 drive 0 -disk as1 at as0 drive 1 - -# The following line enables codrv -device co0 at isa? port "IO_KBD" tty irq 1 vector cointr - -#device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint -device npx0 at isa? port "IO_NPX" irq 13 vector npxintr -device com0 at isa? port "IO_COM1" tty irq 4 vector comintr -device com1 at isa? port "IO_COM2" tty irq 3 flags 0x01 vector comintr -device lpa0 at isa? port "IO_LPT1" tty - -device we0 at isa? port 0x280 net irq 2 iomem 0xd0000 iosiz 8192 vector weintr -#device ne0 at isa? port 0x300 net irq 2 vector neintr -#device ec0 at isa? port 0x250 net irq 2 iomem 0xd8000 iosiz 8192 vector ecintr -#device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr -#device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr - -pseudo-device loop -pseudo-device ether -pseudo-device sl 2 -pseudo-device log -# you should reduce this for MINITERM as well -pseudo-device pty 8 - -# the following line sets the number of available virtual consoles -# a maximum of 12 is possible, but 4 or 8 should be sufficient -# this line is irrelevant if MINITERM is active -#pseudo-device vty 4 - -# This line must be present with codrv for some reasons -pseudo-device vtemul - -# you should exclude this as well for MINITERM -#pseudo-device ddb - -pseudo-device swappager -pseudo-device vnodepager -pseudo-device devpager diff --git a/sys/i386/conf/Makefile.i386 b/sys/i386/conf/Makefile.i386 index 7eedfb83b9..3b649d4358 100644 --- a/sys/i386/conf/Makefile.i386 +++ b/sys/i386/conf/Makefile.i386 @@ -1,6 +1,8 @@ # Copyright 1990 W. Jolitz -# @(#)Makefile.i386 7.1 5/10/91 -# Makefile for 4.3 BSD-Reno +# from: @(#)Makefile.i386 7.1 5/10/91 +# $Id: Makefile.i386,v 1.9 1993/10/23 06:46:24 nate Exp $ +# +# Makefile for FreeBSD # # This makefile is constructed from a machine description: # config machineid @@ -18,28 +20,12 @@ # -DTRACE compile in kernel tracing hooks # -DQUOTA compile in file system quotas # -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 3 00158 -# -------------------- ----- ---------------------- -# -# 29 Jun 92 Chris G. Demetriou Fix vers.o for kernel profiling and -# plain old link -# 25 Mar 93 Sean Eric Fagan Add support for assembler source -# 25 Apr 93 Bruce Evans Support for intr-0.0, and some fixes -# Rodney W. Grimes Added depedencies for conf.o due to -# all the new drivers. And to param.c -# because there were missing. -# 26 May 97 Rodney W. Grimes Remove extra SYSTEM_LD_TAIL -# Redirect stderr from dbsym to null, -# this is bad, but atleast I won't get -# 100's of bug reports about the silly -# warning from dbsym. -# TOUCH= touch -f -c LD= /usr/bin/ld CC= cc CPP= cpp +STRIP= strip +DBSYM= /usr/sbin/dbsym S= ../.. I386= ../../i386 @@ -47,23 +33,27 @@ I386= ../../i386 INCLUDES= -I. -I$S -I$S/sys COPTS= ${INCLUDES} ${IDENT} -DKERNEL -Di386 -DNPX ASFLAGS= -CFLAGS= -O ${COPTS} +CFLAGS= -O ${DEBUG} ${COPTS} +LOAD_ADDRESS?= FE000000 NORMAL_C= ${CC} -c ${CFLAGS} ${PROF} $< NORMAL_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $< NORMAL_S= ${CPP} -I. -DLOCORE ${COPTS} $< | ${AS} ${ASFLAGS} -o $*.o DRIVER_C= ${CC} -c ${CFLAGS} ${PROF} $< DRIVER_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $< -SYSTEM_OBJS=locore.o ${OBJS} param.o ioconf.o conf.o +SYSTEM_OBJS=locore.o ${OBJS} param.o ioconf.o conf.o machdep.o SYSTEM_DEP=Makefile symbols.sort ${SYSTEM_OBJS} SYSTEM_LD_HEAD= @echo loading $@; rm -f $@ -SYSTEM_LD= @${LD} -z -T FE000000 -o $@ -X vers.o ${SYSTEM_OBJS} +SYSTEM_LD= @${LD} -z -T ${LOAD_ADDRESS} -o $@ -X vers.o ${SYSTEM_OBJS} SYSTEM_LD_TAIL= @echo rearranging symbols; symorder symbols.sort $@; \ - dbsym $@ 2>/dev/null || true; size $@; chmod 755 $@ + ${DBSYM} -fT ${LOAD_ADDRESS} $@; ${STRIP} -x $@; size $@; chmod 755 $@ +# (XXX) ok, this is weird. but we've got a working ed, and a broken ex, and +# the script is identical for either... -- cgd +# GPROF.EX= /usr/src/lib/csu.i386/gprof.ex PROFILE_C= ${CC} -S -c ${CFLAGS} $< ; \ - ex - $*.s < ${GPROF.EX} ; \ + ed - $*.s < ${GPROF.EX} ; \ ${AS} -o $@ $*.s ; \ rm -f $*.s @@ -94,6 +84,9 @@ locore.o: assym.s ${I386}/i386/locore.s machine/trap.h machine/psl.h \ ${CPP} -I. -DLOCORE ${COPTS} ${I386}/i386/locore.s | \ ${AS} ${ASFLAGS} -o locore.o +machdep.o: ${I386}/i386/machdep.c Makefile + ${CC} -c ${CFLAGS} -DLOAD_ADDRESS=0x${LOAD_ADDRESS} ${PROF} $< + # the following is necessary because autoconf.o depends on #if GENERIC autoconf.o: Makefile @@ -101,15 +94,11 @@ autoconf.o: Makefile af.o uipc_proto.o locore.o: Makefile # depend on maxusers -assym.s machdep.o: Makefile +assym.s: Makefile # depends on KDB (cons.o also depends on GENERIC) trap.o cons.o: Makefile -assym.s: $S/sys/param.h machine/pte.h $S/sys/buf.h \ - $S/sys/vmmeter.h \ - $S/sys/proc.h $S/sys/msgbuf.h machine/vmparam.h - assym.s: genassym ./genassym >assym.s @@ -118,7 +107,7 @@ genassym: ${I386}/i386/genassym.c -o genassym depend: assym.s param.c - sh /usr/bin/mkdep ${COPTS} ${CFILES} ioconf.c + sh /usr/bin/mkdep -DLOAD_ADDRESS=0x${LOAD_ADDRESS} ${COPTS} ${CFILES} ioconf.c param.c ${I386}/i386/conf.c sh /usr/bin/mkdep -a -p ${INCLUDES} ${IDENT} ${PARAM} ${I386}/i386/genassym.c links: @@ -136,25 +125,17 @@ ioconf.o: ioconf.c $S/sys/param.h machine/pte.h $S/sys/buf.h \ ${I386}/isa/isa_device.h ${I386}/isa/isa.h ${I386}/isa/icu.h ${CC} -c ${CFLAGS} ioconf.c -conf.o: $S/sys/param.h $S/sys/systm.h $S/sys/buf.h $S/sys/ioctl.h \ - $S/sys/tty.h $S/sys/conf.h \ - as.h bpfilter.h cd.h ch.h com.h dcfclk.h fd.h lpa.h \ - lpt.h pty.h sd.h speaker.h st.h wd.h wt.h \ - ${I386}/i386/conf.c +conf.o: ${I386}/i386/conf.c ${CC} -traditional -c ${CFLAGS} ${I386}/i386/conf.c -param.c: $S/conf/param.c \ - $S/sys/param.h $S/sys/systm.h $S/sys/socket.h $S/sys/proc.h \ - $S/sys/vnode.h $S/sys/file.h $S/sys/callout.h $S/sys/clist.h \ - $S/sys/mbuf.h $S/ufs/quota.h $S/sys/kernel.h machine/vmparam.h \ - $S/sys/shm.h +param.c: $S/conf/param.c -rm -f param.c cp $S/conf/param.c . param.o: param.c Makefile ${CC} -c ${CFLAGS} ${PARAM} param.c -newvers: +vers.o: ${SYSTEM_DEP} ${SYSTEM_SWAP_DEP} sh $S/conf/newvers.sh ${KERN_IDENT} ${IDENT} ${CC} ${CFLAGS} -c vers.c diff --git a/sys/i386/conf/ODYSSEUS b/sys/i386/conf/ODYSSEUS deleted file mode 100644 index 74ddd5ae99..0000000000 --- a/sys/i386/conf/ODYSSEUS +++ /dev/null @@ -1,42 +0,0 @@ -# -# Laptop 386SX Odysseus -# -machine "i386" -cpu "i386" -ident ODYSSEUS -timezone 8 dst -maxusers 2 -options INET -options NFS -options MFS -options "COMPAT_43" -options "TCP_COMPAT_42" -options DEBUG -options DIAGNOSTIC -options KGDB -options "KGDBDEV=0x800" - -config "386bsd" root on dk0 swap on dk0 - -controller isa0 -controller wd0 at isa? port "IO_WD1" bio irq 14 vector wdintr -disk dk0 at wd0 drive 0 -controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr -disk dk4 at fd0 drive 0 -disk dk5 at fd0 drive 1 - -device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint -device npx0 at isa? port "IO_NPX" irq 13 vector npxintr -device com0 at isa? port "IO_COM1" tty irq 4 vector comintr -# device com1 at isa? port "IO_COM2" tty irq 3 vector comintr -device we0 at isa? port 0x280 net irq 3 iomem 0xd0000 iosiz 8192 vector weintr - -pseudo-device loop -# pseudo-device pty -pseudo-device ether -pseudo-device sl 2 -pseudo-device log - -pseudo-device swappager -pseudo-device vnodepager -pseudo-device devpager diff --git a/sys/i386/conf/PHOENIX b/sys/i386/conf/PHOENIX deleted file mode 100644 index d7e65d98d1..0000000000 --- a/sys/i386/conf/PHOENIX +++ /dev/null @@ -1,45 +0,0 @@ -# -# Compaq DeskPro/20 PHOENIX -# -machine "i386" -cpu "i386" -ident PHOENIX -timezone 8 dst -maxusers 4 -options INET -options NFS -options MFS -options ISOFS -options "COMPAT_43" -options "TCP_COMPAT_42" -#options DEBUG -options DIAGNOSTIC - -config "386bsd" root on wd0 swap on wd0 and as0 - -controller isa0 -controller wd0 at isa? port "IO_WD1" bio irq 14 vector wdintr -disk wd0 at wd0 drive 0 -controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr -disk fd0 at fd0 drive 0 -disk fd1 at fd0 drive 1 -controller as0 at isa? port 0x330 bio irq 11 drq 5 vector asintr -disk as0 at as0 drive 0 - -device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint -device npx0 at isa? port "IO_NPX" irq 13 vector npxintr -device com0 at isa? port "IO_COM1" tty irq 4 vector comintr -device we0 at isa? port 0x380 net irq 9 iomem 0xd0000 iosiz 8192 vector weintr -device ec0 at isa? port 0x250 net irq 9 iomem 0xd8000 iosiz 8192 vector ecintr -device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr - -pseudo-device loop -pseudo-device pty 10 -pseudo-device ether -pseudo-device sl 1 -pseudo-device log -pseudo-device ddb - -pseudo-device swappager -pseudo-device vnodepager -pseudo-device devpager diff --git a/sys/i386/conf/SMALL b/sys/i386/conf/SMALL deleted file mode 100644 index d93a300fb0..0000000000 --- a/sys/i386/conf/SMALL +++ /dev/null @@ -1,35 +0,0 @@ -# -# Distribution Floppy -# -machine "i386" -cpu "i386" -ident SMALL -timezone 8 dst -maxusers 2 -options INET -options "COMPAT_43" -options "TCP_COMPAT_42" - -config "386bsd" root on dk0 swap on dk0 - -controller isa0 -controller wd0 at isa? port "IO_WD1" bio irq 14 vector wdintr -disk dk0 at wd0 drive 0 -controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr -disk dk4 at fd0 drive 0 -disk dk5 at fd0 drive 1 - -device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint -device npx0 at isa? port "IO_NPX" irq 13 vector npxintr -device com0 at isa? port "IO_COM1" tty irq 4 vector comintr -# device com1 at isa? port "IO_COM2" tty irq 3 vector comintr -device we0 at isa? port 0x280 net irq 3 iomem 0xd0000 iosiz 8192 vector weintr - -pseudo-device loop -pseudo-device ether -pseudo-device sl 1 -pseudo-device pty 2 - -pseudo-device swappager -pseudo-device vnodepager -pseudo-device devpager diff --git a/sys/i386/conf/SPACE_HEATER b/sys/i386/conf/SPACE_HEATER deleted file mode 100644 index dd63244fcd..0000000000 --- a/sys/i386/conf/SPACE_HEATER +++ /dev/null @@ -1,101 +0,0 @@ -# -# This config file is purely fictional. Any correspondance to -# real config files is purely coincidental. -# -# Modified on May 31, 1993 by Rodney W. Grimes to use new sio driver name -# -machine "i386" -cpu "i386" -ident SPACE_HEATER -timezone 8 dst -maxusers 10 -options INET,NFS -options "COMPAT_43" -options "TCP_COMPAT_42" -options UCONSOLE, XSERVER - -# comment this out if you don't want to enable multiport board handling. -# also effects the way incoming chars are checked for, and i'd leave it -# uncommented-out. -# -options COM_MULTIPORT - -# enable the ability to use the bidirectional port features of -# the serial drivers. to actually use the ports bidirectionally, -# you have to say "/sbin/comcontrol bidir" -# commenting it out won't hurt, if you're sure you don't want the -# bidir. capabilities -# -options COM_BIDIR - -config "386bsd" root on sd0 swap on sd0 - -controller isa0 -controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr -disk fd0 at fd0 drive 0 -disk fd1 at fd0 drive 1 - -device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint -device npx0 at isa? port "IO_NPX" irq 13 vector npxintr - -# -# the "com" devices follow; start numbering of "active" ports at 0... -# - -# this is for the standard COM1 port, but can't be used, because there's -# a card which does interrupt sharing on irq 4, which this wants... -# -#device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr - -# this is for the standard COM2 port -- it's my mouse -# -device sio0 at isa? port "IO_COM2" tty irq 3 vector siointr - -# these 4 devices are on a type 1 multiport board (see README). -# -# Note: -# flags = (minor number of "master" multiport serial port << 8) + 1 -# where the "master" port is the serial port with the highest "port" -# address. - -device sio1 at isa? port 0x2a0 tty irq 4 flags 0x0401 vector siointr -device sio2 at isa? port 0x2a8 tty irq 4 flags 0x0401 vector siointr -device sio3 at isa? port 0x2b0 tty irq 4 flags 0x0401 vector siointr -device sio4 at isa? port 0x2b8 tty irq 4 flags 0x0401 vector siointr - -# these 4 devices are on a type 2 multiport board (see README). -# -# Note: Here no flags are defined! - -device sio5 at isa? port 0x1a0 tty irq 4 vector siointr -device sio6 at isa? port 0x1a8 tty irq 4 vector siointr -device sio7 at isa? port 0x1b0 tty irq 4 vector siointr -device sio8 at isa? port 0x1b8 tty irq 4 vector siointr - -controller ahb0 at isa? bio irq 11 drq 5 vector ahbintr -controller scbus0 - -device sd0 -device sd1 -device sd2 -device sd3 - -device st0 -device st1 -device st2 -device st3 - -device cd0 -device cd1 - -device we0 at isa? port 0x300 net irq 5 iomem 0xca000 iosiz 8192 vector weintr - -pseudo-device loop -pseudo-device ether -pseudo-device sl -pseudo-device log -pseudo-device pty - -pseudo-device swappager -pseudo-device vnodepager -pseudo-device devpager diff --git a/sys/i386/conf/SUMNER b/sys/i386/conf/SUMNER deleted file mode 100644 index fb95281ea0..0000000000 --- a/sys/i386/conf/SUMNER +++ /dev/null @@ -1,39 +0,0 @@ -# -# Thos Sumner's 50Mhz 486 -# -machine "i386" -cpu "i486" -ident SUMNER -timezone 8 dst -maxusers 4 -options INET, NFS, MFS, ISOFS -options "COMPAT_43" -options "TCP_COMPAT_42" - -config "386bsd" root on wd0 swap on wd0 and as0 - -controller isa0 -controller wd0 at isa? port "IO_WD1" bio irq 14 vector wdintr -disk wd0 at wd0 drive 0 -controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr -disk fd0 at fd0 drive 0 -disk fd1 at fd0 drive 1 -controller as0 at isa? port 0x330 bio irq 11 drq 5 vector asintr -disk as0 at as0 drive 0 - -device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint -device npx0 at isa? port "IO_NPX" irq 13 vector npxintr -device com1 at isa? port "IO_COM1" tty irq 4 vector comintr -device com2 at isa? port "IO_COM2" tty irq 3 vector comintr -device we0 at isa? port 0x280 net irq 9 iomem 0xd0000 iosiz 8192 vector weintr - -pseudo-device loop -pseudo-device pty 3 -pseudo-device ether -pseudo-device sl 1 -pseudo-device log -pseudo-device ddb - -pseudo-device swappager -pseudo-device vnodepager -pseudo-device devpager diff --git a/sys/i386/conf/SYSCONS b/sys/i386/conf/SYSCONS new file mode 100644 index 0000000000..0b5055cf0d --- /dev/null +++ b/sys/i386/conf/SYSCONS @@ -0,0 +1,87 @@ +# +# SYSCONS -- Generic machine with WD/AHx family disks and syscons +# +# $Id: SYSCONS,v 1.11 1993/10/16 12:28:35 rgrimes Exp $ +# + +machine "i386" +cpu "I386_CPU" +cpu "I486_CPU" +ident SYSCONS +timezone 8 dst +maxusers 10 +maxfdescs 2048 #Max file descriptors per process +options MATH_EMULATE #Support for x87 emulation +options INET #InterNETworking +options ISOFS #ISO File System +options NFS #Network File System +options PCFS #MSDOS File System +options "COMPAT_43" #Compatible with BSD 4.3 +options "TCP_COMPAT_42" #TCP/IP compatible with 4.2 +options XSERVER #Xserver +options UCONSOLE #X Console support +options "NCONS=8" #8 virtual consoles +#options GATEWAY #Host is a Gateway (forwards packets) + +config "386bsd" root on wd0 swap on wd0 and sd0 + +controller isa0 + +controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr +disk fd0 at fd0 drive 0 +disk fd1 at fd0 drive 1 + +controller wd0 at isa? port "IO_WD1" bio irq 14 vector wdintr +disk wd0 at wd0 drive 0 +disk wd1 at wd0 drive 1 + +controller ahb0 at isa? bio irq 11 vector ahbintr +controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr +controller scbus0 + +device sd0 +device sd1 +device sd2 +device sd3 + +device st0 +device st1 + +device cd0 #Only need one of these, the code dynamically grows + +device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr +device npx0 at isa? port "IO_NPX" irq 13 vector npxintr + +#The old drivers are rarely needed anymore... +#device com0 at isa? port "IO_COM1" tty irq 4 vector comintr +#device com1 at isa? port "IO_COM2" tty irq 3 vector comintr +#device com2 at isa? port "IO_COM3" tty irq 5 vector comintr +#device com3 at isa? port "IO_COM4" tty irq 9 vector comintr + +#For high speed serial lines. +device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr +device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr +device sio2 at isa? port "IO_COM3" tty irq 5 vector siointr +device sio3 at isa? port "IO_COM4" tty irq 9 vector siointr + +device lpt0 at isa? port "IO_LPT3" tty irq 7 vector lptintr +device lpa0 at isa? port "IO_LPT1" tty +device lpa1 at isa? port "IO_LPT2" tty + +device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr +device ed1 at isa? port 0x300 net irq 5 iomem 0xd8000 vector edintr +device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr +device is0 at isa? port 0x280 net irq 10 drq 7 vector isintr + +device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr + +pseudo-device loop +pseudo-device ether +pseudo-device log +pseudo-device sl 2 +pseudo-device pty 4 +pseudo-device speaker + +pseudo-device swappager +pseudo-device vnodepager +pseudo-device devpager diff --git a/sys/i386/conf/UHATEST b/sys/i386/conf/UHATEST deleted file mode 100644 index ff9ef37f5b..0000000000 --- a/sys/i386/conf/UHATEST +++ /dev/null @@ -1,59 +0,0 @@ -# -# SCSITEST -- Generic ISA machine -- scsi test kernel -ultrastore 14f adapter -# -machine "i386" -cpu "i386" -ident UHATEST -timezone 8 dst -maxusers 10 -options INET,ISOFS,NFS -options "COMPAT_43" -options "TCP_COMPAT_42" - -config "386bsd" root on wd0 swap on wd0 and sd0 - -controller isa0 -controller wd0 at isa? port "IO_WD1" bio irq 14 vector wdintr -disk wd0 at wd0 drive 0 -disk wd0 at wd0 drive 1 - -controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr -disk fd0 at fd0 drive 0 -disk fd1 at fd0 drive 1 - -device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint -device npx0 at isa? port "IO_NPX" irq 13 vector npxintr -device com1 at isa? port "IO_COM1" tty irq 4 vector comintr -device com2 at isa? port "IO_COM2" tty irq 3 vector comintr - -controller uha0 at isa? port "IO_UHA0" bio irq 11 drq 5 vector uhaintr -#controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr -#controller bt0 at isa? port "IO_BT0" bio irq 12 vector btintr -controller scbus0 - -device sd0 -device sd1 -device sd2 -device sd3 - -device st0 -device st1 -device st2 -device st3 - -device cd0 -device cd1 - -device we0 at isa? port 0x300 net irq 10 iomem 0xd0000 iosiz 8192 vector weintr - - -pseudo-device loop -pseudo-device ether -pseudo-device sl 2 -pseudo-device log -pseudo-device ddb -pseudo-device pty 4 - -pseudo-device swappager -pseudo-device vnodepager -pseudo-device devpager diff --git a/sys/i386/conf/devices.i386 b/sys/i386/conf/devices.i386 index 84f7affeb9..b69698ce00 100644 --- a/sys/i386/conf/devices.i386 +++ b/sys/i386/conf/devices.i386 @@ -1,18 +1,12 @@ # This file tells what major numbers the various possible swap devices have. # -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 1 00097 -# -------------------- ----- ---------------------- -# -# 17 Feb 93 Julian Elischer Add devices for new scsi disks -# 10 Mar 93 Rodney W. Grimes Add more devices, st and cd +# $Id: devices.i386,v 1.2 1993/08/21 22:20:50 rgrimes Exp $ # wd 0 dk 1 fd 2 wt 3 sd 4 -as 4 st 5 cd 6 +mcd 7 diff --git a/sys/i386/conf/files.i386 b/sys/i386/conf/files.i386 index 578472cc73..a697fcc8ed 100644 --- a/sys/i386/conf/files.i386 +++ b/sys/i386/conf/files.i386 @@ -1,19 +1,7 @@ +# This file tells config what files go into building a kernel, +# files marked standard are always included. # -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 3 00160 -# -------------------- ----- ---------------------- -# -# 17 Feb 93 Julian Elischer Added files for scsi -# 10 Mar 93 Rodney W. Grimes Added files for lpt and lpa -# 25 Mar 93 Sean Eric Fagan Added microtime.s routine -# 08 Apr 93 Rodney W. Grimes Cleaned up the tabs, sorted file names -# Added ix, speaker, dcfclock -# 23 Apr 93 Holger Veit Added codrv -# 26 May 93 Rodney W. Grimes Rename of Bruce Evans com driver to sio -# Gene Stark Add xten power controler driver (tw) -# David Greenman Add ethernet driver (SMC/WD/3COM) (ed) -# Rick Macklem Add bus mouse driver (mse) +# $Id: files.i386,v 1.17 1993/10/23 10:49:01 jkh Exp $ # i386/i386/autoconf.c standard device-driver i386/i386/cons.c standard @@ -21,8 +9,7 @@ i386/i386/db_disasm.c optional ddb i386/i386/db_interface.c optional ddb i386/i386/db_trace.c optional ddb i386/i386/in_cksum.c optional inet -i386/i386/machdep.c standard config-dependent -i386/i386/math_emulate.c standard +i386/i386/math_emulate.c optional math_emulate i386/i386/mem.c standard i386/i386/microtime.s standard i386/i386/ns_cksum.c optional ns @@ -32,40 +19,50 @@ i386/i386/trap.c standard i386/i386/vm_machdep.c standard i386/isa/aha1542.c optional aha device-driver i386/isa/aha1742.c optional ahb device-driver -i386/isa/as.c optional as device-driver i386/isa/bt742a.c optional bt device-driver i386/isa/clock.c standard -i386/isa/codrv/co_cons.c optional co device-driver -i386/isa/codrv/co_kbd.c optional co device-driver -i386/isa/codrv/co_vga.c optional co device-driver -i386/isa/codrv/co_codrv1.c optional co device-driver -i386/isa/codrv/co_vty.c optional vty -i386/isa/codrv/co_pc3.c optional vtemul -i386/isa/codrv/co_mini.c optional vtemul i386/isa/com.c optional com device-driver i386/isa/dcfclk.c optional dcfclk device-driver i386/isa/fd.c optional fd device-driver -i386/isa/if_ec.c optional ec device-driver i386/isa/if_ed.c optional ed device-driver +i386/isa/if_ie.c optional ie device-driver i386/isa/if_is.c optional is device-driver i386/isa/if_ix.c optional ix device-driver -i386/isa/if_ne.c optional ne device-driver -i386/isa/if_we.c optional we device-driver i386/isa/isa.c optional isa device-driver i386/isa/lpa.c optional lpa device-driver i386/isa/lpt.c optional lpt device-driver +i386/isa/mcd.c optional mcd device-driver i386/isa/mse.c optional mse device-driver i386/isa/npx.c optional npx device-driver +i386/isa/syscons.c optional sc device-driver i386/isa/pccons.c optional pc device-driver +i386/isa/sb.c optional sb device-driver i386/isa/sio.c optional sio device-driver +i386/isa/sound/adlib_card.c optional snd device-driver +i386/isa/sound/audio.c optional snd device-driver +i386/isa/sound/dev_table.c optional snd device-driver +i386/isa/sound/dmabuf.c optional snd device-driver +i386/isa/sound/dsp.c optional snd device-driver +i386/isa/sound/gus_card.c optional snd device-driver +i386/isa/sound/gus_midi.c optional snd device-driver +i386/isa/sound/gus_vol.c optional snd device-driver +i386/isa/sound/gus_wave.c optional snd device-driver +i386/isa/sound/midi.c optional snd device-driver +i386/isa/sound/midibuf.c optional snd device-driver +i386/isa/sound/mpu401.c optional snd device-driver +i386/isa/sound/opl3.c optional snd device-driver +i386/isa/sound/pas2_card.c optional snd device-driver +i386/isa/sound/pas2_midi.c optional snd device-driver +i386/isa/sound/pas2_mixer.c optional snd device-driver +i386/isa/sound/pas2_pcm.c optional snd device-driver +i386/isa/sound/patmgr.c optional snd device-driver +i386/isa/sound/pro_midi.c optional snd device-driver +i386/isa/sound/sb_card.c optional snd device-driver +i386/isa/sound/sb_dsp.c optional snd device-driver +i386/isa/sound/sequencer.c optional snd device-driver +i386/isa/sound/soundcard.c optional snd device-driver i386/isa/spkr.c optional speaker i386/isa/tw.c optional tw device-driver i386/isa/ultra14f.c optional uha device-driver i386/isa/wd.c optional wd device-driver i386/isa/wt.c optional wt device-driver -scsi/cd.c optional cd -scsi/ch.c optional ch -scsi/scsiconf.c optional scbus -scsi/sd.c optional sd -scsi/st.c optional st - diff --git a/sys/i386/doc/config_options.doc b/sys/i386/doc/config_options.doc new file mode 100644 index 0000000000..67c57bc2a0 --- /dev/null +++ b/sys/i386/doc/config_options.doc @@ -0,0 +1,34 @@ +This file desribes what options do in a kernel config file, these options +change kernl code mostly by #ifdef/#endif pairs in the source. + +Note that options which have an underscore ( _ ) in them must be enclosed +in quotes. This also applies to options that take a numeric value. + +options CCITT X.25 networking support +options "COMPAT_43" Compatible with BSD 4.3 +options "COM_BIDIR" Bidirectional support in sys/isa/sio.c +options "COM_MULTIPORT" Multiport support in sys/isa/sio.c +options EON +options FIFO Named pipes (fifos) support +options GATEWAY Internetwork gateway +options INET Internet communications protocols +options ISO ISO networking (TP4?) +options ISOFS ISO 9660 File System +options KTRACE Kernel tracing +options MATH_EMULATE Support for x87 emulation +options MFS Memroy File System +options "NCONS=8" Number of syscons virtual consoles +options NFS Network File System +options NS Xerox NS communications protocols +options PANICDELAY Kernel waits 20 secounds after a panic +options PANICWAIT Kernel waits for a key stroke after a panic +options PCFS PC (MSDOS) File System +options QUOTA Enable disk quotas +options "SHMMAXPGS=64" 256Kb of sharable memory +options "SYMTAB_SPACE=89000" Amount of DDB symbol table space +options SYSVSHM System V shared memory support +options "TCP_COMPAT_42" Tcp/Ip compatible with 4.2 +options UCONSOLE Xconsole support +options XSERVER Xserver support + +$Id$ diff --git a/sys/i386/doc/ed.relnotes b/sys/i386/doc/ed.relnotes new file mode 100644 index 0000000000..09d0339d84 --- /dev/null +++ b/sys/i386/doc/ed.relnotes @@ -0,0 +1,173 @@ + + Release Notes for 'ed' Device Driver + David Greenman, 24-May-1993 + ------------------------------------ + +Last updated: 16-October-1993 + +INTRODUCTION +------------ + The 'ed' device driver is a new, high performance device driver supporting +the Western Digital/SMC 80x3 series (including 'EtherCard PLUS' 'Elite16'), the +3Com 3c503 (both 8 and 16 bit versions), and the Novell NE1000/2000. All of the +ethernet controllers use the DS8390 or 83C690 Network Interface Controller +(NIC). The differences between the boards are in their memory capacity, bus +width (8/16 bits), special logic (asic) used to configure the board, and the +host access method to the NIC memory (shared or programmed I/O). Every effort +has been made to conform to the manufactures' specifications for the NIC and +asic. This includes both normal operation and error recovery. + +PERFORMANCE +----------- + +transmit +-------- + The 8390 doesn't provide a mechanism for chained write buffers, so it is +very important for maximum performance to queue the next packet for +transmission as soon as the current one has completed. On boards with 16k or +more of memory, the NIC memory is divided in a way that allows enough space +for two full size packets to be buffered for transmission. When sufficient +data is available for transmission, a packet is copied into the NIC memory, +the transmission is started, and then an additional packet is copied into the +NIC memory (to a different memory area). As soon as the first packet has +completed, transmission of a second packet can then be started immediately. +This results in nearly the highest performance possible from ethernet. + +Packets go out on the 'wire' with the following format: + +preamble dest-addr src-addr type data FCS intr-frame +64bits 48bits 48bits 16bits 1500bytes 32bits 96bits + + With 10Mbits/sec, each bit is 100ns in duration. All of the above fields, +except for data are of fixed length. With full sized packets (1500 bytes), the +maximum unidirectional data rate can be calculated as: 6.4us + 4.8us + 4.8us + +1.6us + 1200us + 3.2us + 9.6us = 1230.4us/packet = 812.74382 packets/second = +1219115.7 (1190k) bytes/second. With TCP, there is a 40 byte overhead for the +IP and TCP headers, so there is only 1460 bytes of data per packet. This +reduces the maximum data rate to 1186606 bytes/second. With TCP, there will +also be periodic acknowledgments which will reduce this figure somewhat both +because of the additional traffic in the reverse direction and because of the +occasional collisions that this will cause. Despite this, the data rate has +still been consistantly measured at 1125000 (~1100k) bytes/second through a TCP +socket. In these tests, the TCP window was set to 16384 bytes. With UDP, there +is less overhead for the headers, and with 1472 bytes of data per packet, a +data rate of 1196358.9 (1168k) bytes/sec is possible. UDP performance hasn't +been precisely measured with this device driver, but less precise tests show +this to be true (measured at around 1135k/second). + +receive +------- + The 8390 implements an NIC memory ring-buffer to store incoming packets. +The 8bit boards (8003, 3c503, and NE1000) usually have only 8k bytes of shared +memory. This is only enough room for about 4 full size (1500 byte) packets. +This can sometimes be a problem, especially on the original WD8003E and 3c503. +This is because these boards' NIC memory access speed is also quite slow +compared to newer 16bit boards - typically less than 1MB/second. The additional +overhead of this slow memory access, and the fact that there is only room for 4 +full-sized packets means that the ring-buffer will occassionally overflow. When +this happens, the board must be reset to avoid a lockup problem in early +revision 8390's. Resetting the board will cause all of the data in the ring- +buffer to be lost - requiring it to be re-transmitted/received...slowing things +even further. Because of these problems, maximum throughput on boards of this +type is only about 400-600k per second. The 16bit boards, however, have 16k of +memory as well as much faster memory access speed. Typical memory access speed +on these boards is about 1.7-4MB/second (with the Novell NE2000 being the +slowest and the SMC 8013 being the fastest). These boards generally have no +problems keeping up with full ethernet speed. The only problem I've seen with +these boards is related to the (slow) performance of the BSD Net/2 malloc code +when additional mbufs must be added to the pool. This can sometimes increase +the total time to remove a packet enough for a ring-buffer overflow to occur. +This tends to be highly transient, and quite rare on fast machines. I've only +seen this problem when doing tests with large amounts of UDP traffic without +any acknowledgments (uni-directional). Again, this has been very rare. + + All of the above tests were done using a 486DX2/66, 486DX/33, 386DX/40, +8-9Mhz ISA bus, using FreeBSD 1.0. TCP tests were done with the 'ttcp' +performance test utility, and also with FTP client/server. UDP tests were done +with a modified version of ttcp (to work around a bug in the BSD Net/2 UDP +code related to queue depth), and also with NFS. + +KERNEL INSTALLATION +------------------- + (Note that this driver comes standard in FreeBSD 1.0, NetBSD 0.9, and 386BSD +0.2, and therefore doesn't require installation) + To 'install' this driver, the files if_ed.c and if_edreg.h must be copied +into the i386/isa kernel source directory and the following line must be +added into the file i386/conf/files.i386: + +i386/isa/if_ed.c optional ed device-driver + + To build a kernel that includes this driver, first comment out any 'we', +'ec', or 'ne' devices in your kernel config file. Then, add a line similar to +the following (modify to match your cards configuration): + +device ed0 at isa? port 0x300 net irq 10 iomem 0xcc000 vector edintr + + Note that the 'iosiz' option is not needed because the driver automatically +figures this out. However, if you have problems with this, it can be specified +to force the use of the size you specify. + The iomem 0xcc000 option is not need for NE1000/NE2000 boards because they +use programmed I/O rather than shared memory to access the NIC's memory. + On 3Com boards, the tranceiver must be enabled in software (there is no +hardware default). In this driver, this is controlled using the "LLC0" link- +level control flag. The default for this flag can be set in your kernel config +file by specifying 'flags 0x01' in the 'ed' device specification line (this +is necessary for diskless support). Otherwise, the tranceiver is easily enabled +and disabled with a command like "ifconfig ed0 -llc0" to enable the tranceiver +or "ifconfig ed0 llc0" to disable it; this assumes that you have the modified +ifconfig(8) that originally appeared in the 386BSD patchkit. To specify the +'flags' option, use a line similar to: + +device ed0 at isa? port 0x300 net irq 10 flags 0x01 iomem 0xcc000 vector edintr + + Flags can be similarly specified to force 8 or 16bit mode, and disabling +the use of transmitter double buffering. The various supported flag values +are: + + Disable tranceiver 0x01 + Force 8 bit mode 0x02 + Force 16 bit mode 0x04 + Disable multi TX buffering 0x08 + + To use multiple flags, simply add the values together. Note that these +numbers are in hexadecimal. If the 'Force 8 bit' and 'Force 16 bit' flags are +both specified, the 8 bit flag has precedence. + The use of the above flags should only be necessary under exceptional +conditions where the probe routine incorrectly determines the board type (such +is the case with Compex boards, which require the 16bit flag and an iosiz +16384), or where the high performance of the transmitter causes problems with +other vendors hardware. + + +KNOWN PROBLEMS +-------------- + +1) Early revision DS8390B chips have problems. They lock-up whenever the + receive ring-buffer overflows. They occassionally switch the byte order + of the length field in the packet ring header (several different causes + of this related to an off-by-one byte alignment) - resulting in "NIC + memory corrupt - invalid length NNNNN" messages. The board is reset + whenever these problems occur, but otherwise there is no problem with + recovering from these conditions. +2) 16bit boards that use shared memory can conflict with 8bit BIOSes, BIOS + extensions (like the VGA), and 8bit devices with shared memory (again + like the VGA, or perhaps a second ethernet board). There is a work- + around for this in the driver, however. The problem is that the + ethernet board stays in 16bit mode, asserting its '16bit' signal on + the ISA bus. This signal is shared by other devices/ROMs in the same + 128K memory segment as the ethernet card - causing the CPU to read the + 8bit ROMs as if they were 16bit wide. The work-around involves setting + the host access to the shared memory to 16bits only when the memory is + actually accessed, and setting it back to 8bit mode all other times. + Without this work-around, the machine will hang whenever a reboot is + attempted. This work-around also allows the board to co-exist with + other 8bit devices that have shared memory. This has only been + implemented for SMC/WD boards, but I haven't seen this problem with + 3Com boards (i.e. if you have a 3Com board, you might experiance the + above problem - I haven't specifically tested for it). +3) The NIC memory access to 3Com and Novell boards is much slower than it is on + SMC boards; it's less than 1MB on 8bit boards and less than 2MB/second + on the 16bit boards. This can lead to ring-buffer overruns resulting in + additional lost data during heavy network traffic. + +$Id$ diff --git a/sys/i386/doc/sound.doc b/sys/i386/doc/sound.doc new file mode 100644 index 0000000000..b65667b1a1 --- /dev/null +++ b/sys/i386/doc/sound.doc @@ -0,0 +1,33 @@ +To enable sound card support, you need to add one or more of the following +lines to your kernel configuration file: + +device snd5 at isa? port 0x330 irq 6 drq 0 vector mpuintr +device snd4 at isa? port 0x220 irq 15 drq 6 vector gusintr +device snd3 at isa? port 0x388 irq 12 drq 3 vector pasintr +device snd2 at isa? port 0x220 irq 7 drq 1 vector sbintr +device snd1 at isa? port 0x388 irq 0 drq 0 vector sbintr + + Unit numbers are: + 1 for Yamaha FM synth + 2 for SB/SB Pro DSP + 3 for PAS PCM and Midi + 4 for GUS + 5 for MPU-401 + + If you have ProAudioSpectrum, uncomment units 3, 2 and 1 + If you have SoundBlaster, uncomment 2 and 1. + If you have GravisUltrasound, uncomment 4 + If you have MPU-401, uncomment 5 + +NOTE: The MPU-401 driver may or may not work, and is unfortunately +unverifiable since no one I know has one. If you can test this, +please let me know! + +Also: Some systems with the OPTI chipset will require you to #define +BROKEN_BUS_CLOCK in /sys/i386/sound/pas2_card.c. Symptoms are that +you will hear a lot of clicking and popping sounds, like a geiger counter, +coming out of the PAS even when is not playing anything. + + + + - Jordan Hubbard (jkh@freefall.cdrom.com) diff --git a/sys/i386/doc/vm_layout.doc b/sys/i386/doc/vm_layout.doc new file mode 100644 index 0000000000..feb6c20012 --- /dev/null +++ b/sys/i386/doc/vm_layout.doc @@ -0,0 +1,32 @@ +Physical Memory Layout: + +NOT YET DONE + + + +Virtual Memory Layout: + +Page Table Directories, and how they relate to the vm address space +Note: PTDI stands for Page Table Directory Index. + +PTDI Address pmap.h/param.h Calculation to locate it in vm space +-------------------------------------------------------------------------------- + FFFFF000 APTD APTmap + (APTDPTDI * NBPG) + FFC00000 APTmap APTDPTDI << PDRSHIFT +3FF FFC00000 APTDPTDI #define (NPTEPG-1) + FFBFFFFF KERNEND ((KPTDI+NKPDE) << PDRSHIFT) - 1 +3FD FF400000 . +3FC FF000000 . +3FB FEC00000 . +3FA FE800000 . +3F9 FE400000 . + FE000000 KERNBASE KPTDI << PDRSHIFT +3F8 FE000000 KPTDI #define (APTDPTDI-NKPDE) + FDFF8000 Sysmap PTmap + (KPTDI * NBPG) + FDFF7FF8 APTpde PTD + (APTDPTDI * sizeof(pde)) + FDFF7FDC PTDpde PTD + (PTDPTDI * sizeof(pde)) + FDFF7000 PTD PTmap + (PTDPTDI * NBPG) + FDC00000 PTmap PTDPTDI << PDRSHIFT +3F7 FDC00000 PTDPTDI #define (KPTDI-1) + +$Id: vm_layout.doc,v 1.5 1993/10/15 06:35:46 rgrimes Exp $ diff --git a/sys/i386/i386/autoconf.c b/sys/i386/i386/autoconf.c index 7eee991a94..9f06f758d8 100644 --- a/sys/i386/i386/autoconf.c +++ b/sys/i386/i386/autoconf.c @@ -33,18 +33,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)autoconf.c 7.1 (Berkeley) 5/9/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00117 - * -------------------- ----- ---------------------- - * - * 09 Apr 93 ???(From sun-lamp) Fix to report sd when Julians - * scsi code is used, allow you to swap - * root floppies during a boot + * from: @(#)autoconf.c 7.1 (Berkeley) 5/9/91 + * $Id$ */ -static char rcsid[] = "$Header: /b/source/CVS/src/sys.386bsd/i386/i386/autoconf.c,v 1.3 1993/04/10 21:58:52 cgd Exp $"; /* * Setup the system to run on the current machine. @@ -133,17 +124,12 @@ extern int Maxmem; #define DOSWAP /* change swdevt and dumpdev */ u_long bootdev = 0; /* should be dev_t, but not until 32 bits */ -#include "sd.h" static char devname[][2] = { 'w','d', /* 0 = wd */ 's','w', /* 1 = sw */ 'f','d', /* 2 = fd */ 'w','t', /* 3 = wt */ -#if NSD < 1 - 'a','s', /* 4 = as */ -#else 's','d', /* 4 = sd -- new SCSI system */ -#endif }; #define PARTITIONMASK 0x7 @@ -179,13 +165,6 @@ setroot() */ if (rootdev == orootdev) return; - if (devname[majdev][0] == 'f' && devname[majdev][1] == 'd') { - printf(""); - printf("* insert the floppy you want to have mounted as\n"); - printf("* root, and hit any key to continue booting:\n"); - cngetc(); - printf(""); - } printf("changing root device to %c%c%d%c\n", devname[majdev][0], devname[majdev][1], mindev >> PARTITIONSHIFT, part + 'a'); diff --git a/sys/i386/i386/conf.c b/sys/i386/i386/conf.c index 0189092fc0..32295a1ac9 100644 --- a/sys/i386/i386/conf.c +++ b/sys/i386/i386/conf.c @@ -33,29 +33,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)conf.c 5.8 (Berkeley) 5/12/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 5 00160 - * -------------------- ----- ---------------------- - * - * 10 Feb 93 Jordan K. Hubbard Added select entry for com driver - * 10 Feb 93 Julian Elischer Add empty table entries - * so we can allocate numbers - * 15 Feb 93 Julian Elischer Add basic SCSI device entries - * 16 Feb 93 Julian Elischer add entries for scsi media changer - * 01 Mar 93 Jordan K. Hubbard Reserve major numbers for codrv, fd, bpf - * 10 Mar 83 Rodney W. Grimes General clean up of the above patches - * 06 Apr 93 Rodney W. Grimes Fixed NLPT for LPA driver case, added - * spkr, dcfclock - * 23 Apr 93 Holger Veit added codrv - * 25 May 93 Bruce Evans New fast interrupt serial driver (sio) - * Gene Stark Xten power controller info added (tw) - * Rick Macklem Bus mouse driver (mse) - * + * from: @(#)conf.c 5.8 (Berkeley) 5/12/91 + * $Id: conf.c,v 1.11 1993/10/23 10:49:24 jkh Exp $ */ -static char rcsid[] = "$Header: /usr/src/sys.386bsd/i386/i386/RCS/conf.c,v 1.2 92/01/21 14:21:57 william Exp Locker: toor $"; #include "param.h" #include "systm.h" @@ -80,25 +60,10 @@ int wddump(),wdsize(); #define wdsize NULL #endif -#include "as.h" -#if NAS > 0 -int asopen(),asclose(),asstrategy(),asioctl(); -int /*asdump(),*/assize(); -#define asdump enxio -#else -#define asopen enxio -#define asclose enxio -#define asstrategy enxio -#define asioctl enxio -#define asdump enxio -#define assize NULL -#endif - #include "sd.h" #if NSD > 0 int sdopen(),sdclose(),sdstrategy(),sdioctl(); -int /*sddump(),*/sdsize(); -#define sddump enxio +int sddump(),sdsize(); #else #define sdopen enxio #define sdclose enxio @@ -137,6 +102,20 @@ int /*cddump(),*/cdsize(); #define cdsize NULL #endif +#include "mcd.h" +#if NMCD > 0 +int mcdopen(),mcdclose(),mcdstrategy(),mcdioctl(); +int /*mcddump(),*/mcdsize(); +#define mcddump enxio +#else +#define mcdopen enxio +#define mcdclose enxio +#define mcdstrategy enxio +#define mcdioctl enxio +#define mcddump enxio +#define mcdsize NULL +#endif + #include "ch.h" #if NCH > 0 int chopen(),chclose(),chioctl(); @@ -146,6 +125,20 @@ int chopen(),chclose(),chioctl(); #define chioctl enxio #endif +#include "sg.h" +#if NSG > 0 +int sgopen(),sgclose(),sgioctl(),sgstrategy(); +#define sgdump enxio +#define sgsize NULL +#else +#define sgopen enxio +#define sgclose enxio +#define sgstrategy enxio +#define sgioctl enxio +#define sgdump enxio +#define sgsize NULL +#endif + #include "wt.h" #if NWT > 0 int wtopen(),wtclose(),wtstrategy(),wtioctl(); @@ -161,8 +154,7 @@ int wtdump(),wtsize(); #include "fd.h" #if NFD > 0 -int Fdopen(),fdclose(),fdstrategy(); -#define fdioctl enxio +int Fdopen(),fdclose(),fdstrategy(),fdioctl(); #define fddump enxio #define fdsize NULL #else @@ -186,17 +178,14 @@ struct bdevsw bdevsw[] = fddump, fdsize, NULL }, { wtopen, wtclose, wtstrategy, wtioctl, /*3*/ wtdump, wtsize, B_TAPE }, -#if NSD > 0 { sdopen, sdclose, sdstrategy, sdioctl, /*4*/ sddump, sdsize, NULL }, -#else NSD > 0 - { asopen, asclose, asstrategy, asioctl, /*4*/ - asdump, assize, NULL }, -#endif NSD > 0 { stopen, stclose, ststrategy, stioctl, /*5*/ stdump, stsize, NULL }, { cdopen, cdclose, cdstrategy, cdioctl, /*6*/ cddump, cdsize, NULL }, + { mcdopen, mcdclose, mcdstrategy, mcdioctl, /*7*/ + mcddump, mcdsize, NULL }, /* * If you need a bdev major number, please contact the 386bsd patchkit * coordinator by sending mail to "patches@cs.montana.edu". @@ -212,7 +201,7 @@ extern struct tty pccons; int cttyopen(), cttyread(), cttywrite(), cttyioctl(), cttyselect(); -int mmrw(); +int mmopen(), mmclose(), mmrw(); #define mmselect seltrue #include "pty.h" @@ -266,19 +255,6 @@ int lptopen(),lptclose(),lptwrite(),lptioctl(); #define lptioctl enxio #endif -#include "co.h" -#if NCO > 0 -int coopen(),coclose(),coread(),coioctl(),coselect(),comap(); -#define pcmmap comap -#else -#define coopen enxio -#define coclose enxio -#define coread enxio -#define coioctl enxio -#define coselect enxio -#define comap enxio -#endif - #include "tw.h" #if NTW > 0 int twopen(),twclose(),twread(),twwrite(),twselect(); @@ -290,6 +266,32 @@ int twopen(),twclose(),twread(),twwrite(),twselect(); #define twselect enxio #endif +#include "sb.h" /* Sound Blaster */ +#if NSB > 0 +int sbopen(), sbclose(), sbioctl(), sbread(), sbwrite(); +int sbselect(); +#else +#define sbopen enxio +#define sbclose enxio +#define sbioctl enxio +#define sbread enxio +#define sbwrite enxio +#define sbselect seltrue +#endif + +#include "snd.h" /* General Sound Driver */ +#if NSND > 0 +int sndopen(), sndclose(), sndioctl(), sndread(), sndwrite(); +int sndselect(); +#else +#define sndopen enxio +#define sndclose enxio +#define sndioctl enxio +#define sndread enxio +#define sndwrite enxio +#define sndselect seltrue +#endif + int fdopen(); #include "bpfilter.h" @@ -371,9 +373,9 @@ struct cdevsw cdevsw[] = { cttyopen, nullop, cttyread, cttywrite, /*1*/ cttyioctl, nullop, nullop, NULL, /* tty */ cttyselect, enodev, NULL }, - { nullop, nullop, mmrw, mmrw, /*2*/ - enodev, nullop, nullop, NULL, /* memory */ - mmselect, enodev, NULL }, + { mmopen, mmclose, mmrw, mmrw, /*2*/ + enodev, nullop, nullop, NULL, /* memory */ + mmselect, enodev, NULL }, { wdopen, wdclose, rawread, rawwrite, /*3*/ wdioctl, enodev, nullop, NULL, /* wd */ seltrue, enodev, wdstrategy }, @@ -404,15 +406,9 @@ struct cdevsw cdevsw[] = { pcopen, pcclose, pcread, pcwrite, /*12*/ pcioctl, nullop, nullop, &pccons, /* pc */ ttselect, pcmmap, NULL }, -#if NSD > 0 { sdopen, sdclose, rawread, rawwrite, /*13*/ sdioctl, enodev, nullop, NULL, /* sd */ seltrue, enodev, sdstrategy }, -#else NSD > 0 - { asopen, asclose, rawread, rawwrite, /*13*/ - asioctl, enodev, nullop, NULL, /* as */ - seltrue, enodev, asstrategy }, -#endif NSD > 0 { stopen, stclose, rawread, rawwrite, /*14*/ stioctl, enodev, nullop, NULL, /* st */ seltrue, enodev, ststrategy }, @@ -425,18 +421,18 @@ struct cdevsw cdevsw[] = { chopen, chclose, enxio, enxio, /*17*/ chioctl, enxio, enxio, NULL, /* ch */ enxio, enxio, enxio }, - { enxio, enxio, enxio, enxio, /*18*/ - enxio, enxio, enxio, NULL, /* scsi generic */ - enxio, enxio, enxio }, + { sgopen, sgclose, enodev, enodev, /*18*/ + sgioctl, enodev, nullop, NULL, /* scsi 'generic' */ + seltrue, enodev, sgstrategy }, { twopen, twclose, twread, twwrite, /*19*/ enodev, nullop, nullop, NULL, /* tw */ twselect, enodev, enodev }, - { enxio, enxio, enxio, enxio, /*20*/ - enxio, enxio, enxio, NULL, /* soundblaster?*/ - enxio, enxio, enxio }, - { coopen, coclose, coread, enxio, /*21*/ - coioctl, nullop, nullop, NULL, /* co */ - coselect, comap, NULL }, + { sbopen, sbclose, sbread, sbwrite, /*20*/ + sbioctl, enodev, enodev, NULL, /* soundblaster*/ + sbselect, enodev, NULL }, + { enodev, enodev, enodev, enodev, /*21*/ + enodev, enodev, nullop, NULL, /* psm */ + enodev, enodev, enodev }, { fdopen, enxio, enxio, enxio, /*22*/ enxio, enxio, enxio, NULL, /* fd (!=Fd) */ enxio, enxio, enxio }, @@ -458,9 +454,15 @@ struct cdevsw cdevsw[] = { sioopen, sioclose, sioread, siowrite, /*28*/ sioioctl, siostop, sioreset, sio_tty, /* sio */ sioselect, enodev, NULL }, + { mcdopen, mcdclose, rawread, enodev, /*29*/ + mcdioctl, enodev, nullop, NULL, /* mitsumi cd */ + seltrue, enodev, mcdstrategy }, + { sndopen, sndclose, sndread, sndwrite, /*30*/ + sndioctl, enodev, enodev, NULL, /* sound driver */ + sndselect, enodev, NULL }, /* - * If you need a cdev major number, please contact the 386bsd patchkit - * coordinator by sending mail to "patches@cs.montana.edu". + * If you need a cdev major number, please contact the FreeBSD team + * by sending mail to `freebsd-hackers@freefall.cdrom.com'. * If you assign one yourself it may then conflict with someone else. */ }; diff --git a/sys/i386/i386/cons.c b/sys/i386/i386/cons.c index 6189d72111..2e92a9c107 100644 --- a/sys/i386/i386/cons.c +++ b/sys/i386/i386/cons.c @@ -35,15 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)cons.c 7.2 (Berkeley) 5/9/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00083 - * -------------------- ----- ---------------------- - * - * 16 Aug 92 Pace Willisson /dev/console redirect (xterm -C, etc.) - * 14 Mar 93 Chris G. Demetriou Moved pg() here from isa/pccons.c + * from: @(#)cons.c 7.2 (Berkeley) 5/9/91 + * $Id: cons.c,v 1.3 1993/10/16 14:14:49 rgrimes Exp $ */ diff --git a/sys/i386/i386/cons.h b/sys/i386/i386/cons.h index b3c706433a..e8a9812325 100644 --- a/sys/i386/i386/cons.h +++ b/sys/i386/i386/cons.h @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)cons.h 7.2 (Berkeley) 5/9/91 + * from: @(#)cons.h 7.2 (Berkeley) 5/9/91 + * $Id$ */ diff --git a/sys/i386/i386/db_disasm.c b/sys/i386/i386/db_disasm.c index 20430b6741..a8b98da4a3 100644 --- a/sys/i386/i386/db_disasm.c +++ b/sys/i386/i386/db_disasm.c @@ -22,31 +22,8 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_disasm.c,v $ - * Revision 1.1 1992/03/25 21:42:01 pace - * Initial revision * - * Revision 2.3 91/02/05 17:11:03 mrt - * Changed to new Mach copyright - * [91/02/01 17:31:03 mrt] - * - * Revision 2.2 90/08/27 21:55:56 dbg - * Fix register operand for move to/from control/test/debug - * register instructions. Add i486 instructions. - * [90/08/27 dbg] - * - * Import db_sym.h. Print instruction displacements in - * current radix (signed). Change calling sequence of - * db_disasm. - * [90/08/21 dbg] - * Fix includes. - * [90/08/08 dbg] - * Created. - * [90/07/25 dbg] - * + * $Id$ */ /* @@ -719,8 +696,8 @@ struct inst db_inst_table[256] = { /*a9*/ { "test", FALSE, LONG, op2(I, A), 0 }, /*aa*/ { "stos", FALSE, BYTE, op1(DI), 0 }, /*ab*/ { "stos", FALSE, LONG, op1(DI), 0 }, -/*ac*/ { "ldos", FALSE, BYTE, op1(SI), 0 }, -/*ad*/ { "ldos", FALSE, LONG, op1(SI), 0 }, +/*ac*/ { "lods", FALSE, BYTE, op1(SI), 0 }, +/*ad*/ { "lods", FALSE, LONG, op1(SI), 0 }, /*ae*/ { "scas", FALSE, BYTE, op1(SI), 0 }, /*af*/ { "scas", FALSE, LONG, op1(SI), 0 }, diff --git a/sys/i386/i386/db_interface.c b/sys/i386/i386/db_interface.c index 31e7849e01..74c09dce7a 100644 --- a/sys/i386/i386/db_interface.c +++ b/sys/i386/i386/db_interface.c @@ -22,29 +22,8 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_interface.c,v $ - * Revision 1.1 1992/03/25 21:42:03 pace - * Initial revision - * - * Revision 2.4 91/02/05 17:11:13 mrt - * Changed to new Mach copyright - * [91/02/01 17:31:17 mrt] - * - * Revision 2.3 90/12/04 14:45:55 jsb - * Changes for merged intel/pmap.{c,h}. - * [90/12/04 11:14:41 jsb] - * - * Revision 2.2 90/10/25 14:44:43 rwd - * Added watchpoint support. - * [90/10/18 rpd] - * - * Created. - * [90/07/25 dbg] - * * + * $Id$ */ /* diff --git a/sys/i386/i386/db_trace.c b/sys/i386/i386/db_trace.c index cbffbbc067..ffed283014 100644 --- a/sys/i386/i386/db_trace.c +++ b/sys/i386/i386/db_trace.c @@ -22,38 +22,10 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_trace.c,v $ - * Revision 1.1 1992/03/25 21:42:05 pace - * Initial revision * - * Revision 2.6 91/02/05 17:11:21 mrt - * Changed to new Mach copyright - * [91/02/01 17:31:32 mrt] - * - * Revision 2.5 91/01/09 19:55:27 rpd - * Fixed stack tracing for threads without kernel stacks. - * [91/01/09 rpd] - * - * Revision 2.4 91/01/08 15:10:22 rpd - * Reorganized the pcb. - * [90/12/11 rpd] - * - * Revision 2.3 90/11/05 14:27:07 rpd - * If we can not guess the number of args to a function, use 5 vs 0. - * [90/11/02 rvb] - * - * Revision 2.2 90/08/27 21:56:20 dbg - * Import db_sym.h. - * [90/08/21 dbg] - * Fix includes. - * [90/08/08 dbg] - * Created from rvb's code for new debugger. - * [90/07/11 dbg] - * + * $Id$ */ + #include "param.h" #include "proc.h" #include diff --git a/sys/i386/i386/dkbad.c b/sys/i386/i386/dkbad.c index 6974af1be2..0e9aca3c69 100644 --- a/sys/i386/i386/dkbad.c +++ b/sys/i386/i386/dkbad.c @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)dkbad.c 5.4 (Berkeley) 1/19/91 + * from: @(#)dkbad.c 5.4 (Berkeley) 1/19/91 + * $Id$ */ diff --git a/sys/i386/i386/genassym.c b/sys/i386/i386/genassym.c index 18ec37b289..381f3df9f6 100644 --- a/sys/i386/i386/genassym.c +++ b/sys/i386/i386/genassym.c @@ -33,21 +33,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)genassym.c 5.11 (Berkeley) 5/10/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00154 - * -------------------- ----- ---------------------- - * - * 24 Apr 93 Bruce Evans/Dave Rivers Npx-0.5 support - * + * from: @(#)genassym.c 5.11 (Berkeley) 5/10/91 + * $Id: genassym.c,v 1.4 1993/10/12 15:33:18 rgrimes Exp $ */ -static char rcsid[] = "$Header: /usr/bill/working/sys/i386/i386/RCS/genassym.c,v 1.2 92/01/21 14:22:02 william Exp $"; - -#ifndef lint -static char sccsid[] = "@(#)genassym.c 5.11 (Berkeley) 5/10/91"; -#endif /* not lint */ #include "sys/param.h" #include "sys/buf.h" @@ -60,7 +48,6 @@ static char sccsid[] = "@(#)genassym.c 5.11 (Berkeley) 5/10/91"; #include "machine/cpu.h" #include "machine/trap.h" #include "machine/psl.h" -#include "machine/reg.h" #include "sys/syscall.h" #include "vm/vm_param.h" #include "vm/vm_map.h" @@ -77,6 +64,8 @@ main() vm_map_t map = (vm_map_t)0; pmap_t pmap = (pmap_t)0; struct pcb *pcb = (struct pcb *)0; + struct trapframe *tf = (struct trapframe *)0; + struct sigframe *sigf = (struct sigframe *)0; register unsigned i; printf("#define\tI386_CR3PAT %d\n", I386_CR3PAT); @@ -107,7 +96,12 @@ main() printf("#define\tCLSIZE %d\n", CLSIZE); printf("#define\tNBPG %d\n", NBPG); printf("#define\tNPTEPG %d\n", NPTEPG); + printf("#define\tNKPDE %d\n", NKPDE); + printf("#define\tKPTDI %d\n", KPTDI); + printf("#define\tPTDPTDI %d\n", PTDPTDI); + printf("#define\tAPTDPTDI %d\n", APTDPTDI); printf("#define\tPGSHIFT %d\n", PGSHIFT); + printf("#define\tPDRSHIFT %d\n", PDRSHIFT); printf("#define\tSYSPTSIZE %d\n", SYSPTSIZE); printf("#define\tUSRPTSIZE %d\n", USRPTSIZE); printf("#define\tUSRIOSIZE %d\n", USRIOSIZE); @@ -115,6 +109,8 @@ main() printf("#define\tSHMMAXPGS %d\n", SHMMAXPGS); #endif printf("#define\tUSRSTACK %d\n", USRSTACK); + printf("#define\tKERNBASE %d\n", KERNBASE); + printf("#define\tKERNSIZE %d\n", KERNSIZE); printf("#define\tMSGBUFPTECNT %d\n", btoc(sizeof (struct msgbuf))); printf("#define\tNMBCLUSTERS %d\n", NMBCLUSTERS); printf("#define\tMCLBYTES %d\n", MCLBYTES); @@ -143,8 +139,8 @@ main() printf("#define\tPCB_FS %d\n", &pcb->pcb_tss.tss_fs); printf("#define\tPCB_GS %d\n", &pcb->pcb_tss.tss_gs); printf("#define\tPCB_LDT %d\n", &pcb->pcb_tss.tss_ldt); + printf("#define\tPCB_USERLDT %d\n", &pcb->pcb_ldt); printf("#define\tPCB_IOOPT %d\n", &pcb->pcb_tss.tss_ioopt); - printf("#define\tNKMEMCLUSTERS %d\n", NKMEMCLUSTERS); printf("#define\tU_PROF %d\n", &up->u_stats.p_prof); printf("#define\tU_PROFSCALE %d\n", &up->u_stats.p_prof.pr_scale); printf("#define\tPR_BASE %d\n", &uprof->pr_base); @@ -154,18 +150,36 @@ main() printf("#define\tRU_MINFLT %d\n", &rup->ru_minflt); printf("#define\tPCB_FLAGS %d\n", &pcb->pcb_flags); printf("#define\tPCB_SAVEFPU %d\n", &pcb->pcb_savefpu); -#ifdef notused - printf("#define\tFP_WASUSED %d\n", FP_WASUSED); - printf("#define\tFP_NEEDSSAVE %d\n", FP_NEEDSSAVE); - printf("#define\tFP_NEEDSRESTORE %d\n", FP_NEEDSRESTORE); -#endif printf("#define\tFP_USESEMC %d\n", FP_USESEMC); printf("#define\tPCB_SAVEEMC %d\n", &pcb->pcb_saveemc); printf("#define\tPCB_CMAP2 %d\n", &pcb->pcb_cmap2); - printf("#define\tPCB_SIGC %d\n", pcb->pcb_sigc); printf("#define\tPCB_IML %d\n", &pcb->pcb_iml); printf("#define\tPCB_ONFAULT %d\n", &pcb->pcb_onfault); + printf("#define\tTF_ES %d\n", &tf->tf_es); + printf("#define\tTF_DS %d\n", &tf->tf_ds); + printf("#define\tTF_EDI %d\n", &tf->tf_edi); + printf("#define\tTF_ESI %d\n", &tf->tf_esi); + printf("#define\tTF_EBP %d\n", &tf->tf_ebp); + printf("#define\tTF_ISP %d\n", &tf->tf_isp); + printf("#define\tTF_EBX %d\n", &tf->tf_ebx); + printf("#define\tTF_EDX %d\n", &tf->tf_edx); + printf("#define\tTF_ECX %d\n", &tf->tf_ecx); + printf("#define\tTF_EAX %d\n", &tf->tf_eax); + printf("#define\tTF_TRAPNO %d\n", &tf->tf_trapno); + printf("#define\tTF_ERR %d\n", &tf->tf_err); + printf("#define\tTF_EIP %d\n", &tf->tf_eip); + printf("#define\tTF_CS %d\n", &tf->tf_cs); + printf("#define\tTF_EFLAGS %d\n", &tf->tf_eflags); + printf("#define\tTF_ESP %d\n", &tf->tf_esp); + printf("#define\tTF_SS %d\n", &tf->tf_ss); + + printf("#define\tSIGF_SIGNUM %d\n", &sigf->sf_signum); + printf("#define\tSIGF_CODE %d\n", &sigf->sf_code); + printf("#define\tSIGF_SCP %d\n", &sigf->sf_scp); + printf("#define\tSIGF_HANDLER %d\n", &sigf->sf_handler); + printf("#define\tSIGF_SC %d\n", &sigf->sf_sc); + printf("#define\tB_READ %d\n", B_READ); printf("#define\tENOENT %d\n", ENOENT); printf("#define\tEFAULT %d\n", EFAULT); diff --git a/sys/i386/i386/in_cksum.c b/sys/i386/i386/in_cksum.c index 335dd358a7..3bea1c1da5 100644 --- a/sys/i386/i386/in_cksum.c +++ b/sys/i386/i386/in_cksum.c @@ -30,17 +30,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * from tahoe: in_cksum.c 1.2 86/01/05 - * @(#)in_cksum.c 1.3 (Berkeley) 1/19/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00104 - * -------------------- ----- ---------------------- - * - * 24 Jul 92 Bakul Shah Optimized some more - * - * 920724 i386 changes by Bakul Shah + * from tahoe: in_cksum.c 1.2 86/01/05 + * from: @(#)in_cksum.c 1.3 (Berkeley) 1/19/91 + * $Id$ */ #include "param.h" diff --git a/sys/i386/i386/locore.s b/sys/i386/i386/locore.s index d558dba24a..37818859bb 100644 --- a/sys/i386/i386/locore.s +++ b/sys/i386/i386/locore.s @@ -33,19 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)locore.s 7.3 (Berkeley) 5/13/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 5 00158 - * -------------------- ----- ---------------------- - * - * 06 Aug 92 Pace Willisson Allow VGA memory to be mapped - * 28 Nov 92 Frank MacLachlan Aligned addresses and data - * on 32bit boundaries. - * 25 Mar 93 Kevin Lahey Add syscall counter for vmstat - * 20 Apr 93 Bruce Evans New npx-0.5 code - * 25 Apr 93 Bruce Evans Support new interrupt code (intr-0.1) + * from: @(#)locore.s 7.3 (Berkeley) 5/13/91 + * $Id: locore.s,v 1.7 1993/10/13 07:11:11 rgrimes Exp $ */ @@ -55,6 +44,8 @@ * Written by William F. Jolitz, 386BSD Project */ +#include "npx.h" + #include "assym.s" #include "machine/psl.h" #include "machine/pte.h" @@ -65,6 +56,7 @@ #include "machine/specialreg.h" #include "i386/isa/debug.h" +#include "machine/cputypes.h" #define KDSEL 0x10 #define SEL_RPL_MASK 0x0003 @@ -77,94 +69,160 @@ */ .set IDXSHIFT,10 - .set SYSTEM,0xFE000000 # virtual address of system start - /*note: gas copys sign bit (e.g. arithmetic >>), can't do SYSTEM>>22! */ - .set SYSPDROFF,0x3F8 # Page dir index of System Base #define ALIGN_DATA .align 2 #define ALIGN_TEXT .align 2,0x90 /* 4-byte boundaries, NOP-filled */ #define SUPERALIGN_TEXT .align 4,0x90 /* 16-byte boundaries better for 486 */ +#define GEN_ENTRY(name) ALIGN_TEXT; .globl name; name: +#define NON_GPROF_ENTRY(name) GEN_ENTRY(_/**/name) + +#ifdef GPROF +/* + * ALTENTRY() must be before a corresponding ENTRY() so that it can jump + * over the mcounting. + */ +#define ALTENTRY(name) GEN_ENTRY(_/**/name); MCOUNT; jmp 2f +#define ENTRY(name) GEN_ENTRY(_/**/name); MCOUNT; 2: +/* + * The call to mcount supports the usual (bad) conventions. We allocate + * some data and pass a pointer to it although the 386BSD doesn't use + * the data. We set up a frame before calling mcount because that is + * the standard convention although it makes work for both mcount and + * callers. + */ +#define MCOUNT .data; ALIGN_DATA; 1:; .long 0; .text; \ + pushl %ebp; movl %esp,%ebp; \ + movl $1b,%eax; call mcount; popl %ebp +#else +/* + * ALTENTRY() has to align because it is before a corresponding ENTRY(). + * ENTRY() has to align to because there may be no ALTENTRY() before it. + * If there is a previous ALTENTRY() then the alignment code is empty. + */ +#define ALTENTRY(name) GEN_ENTRY(_/**/name) +#define ENTRY(name) GEN_ENTRY(_/**/name) +#endif + /* NB: NOP now preserves registers so NOPs can be inserted anywhere */ /* XXX: NOP and FASTER_NOP are misleadingly named */ -#ifdef BROKEN_HARDWARE_AND_OR_SOFTWARE /* XXX - rarely necessary */ -#define FASTER_NOP pushl %eax ; inb $0x84,%al ; popl %eax -#define NOP pushl %eax ; inb $0x84,%al ; inb $0x84,%al ; popl %eax -#else +#ifdef DUMMY_NOPS /* this will break some older machines */ #define FASTER_NOP #define NOP +#else +#define FASTER_NOP pushl %eax ; inb $0x84,%al ; popl %eax +#define NOP pushl %eax ; inb $0x84,%al ; inb $0x84,%al ; popl %eax #endif /* * PTmap is recursive pagemap at top of virtual address space. * Within PTmap, the page directory can be found (third indirection). */ - .set PDRPDROFF,0x3F7 # Page dir index of Page dir - .globl _PTmap, _PTD, _PTDpde, _Sysmap - .set _PTmap,0xFDC00000 - .set _PTD,0xFDFF7000 - .set _Sysmap,0xFDFF8000 - .set _PTDpde,0xFDFF7000+4*PDRPDROFF + .globl _PTmap,_PTD,_PTDpde,_Sysmap + .set _PTmap,PTDPTDI << PDRSHIFT + .set _PTD,_PTmap + (PTDPTDI * NBPG) + .set _PTDpde,_PTD + (PTDPTDI * 4) /* XXX 4=sizeof pde */ + + .set _Sysmap,_PTmap + (KPTDI * NBPG) /* * APTmap, APTD is the alternate recursive pagemap. * It's used when modifying another process's page tables. */ - .set APDRPDROFF,0x3FE # Page dir index of Page dir - .globl _APTmap, _APTD, _APTDpde - .set _APTmap,0xFF800000 - .set _APTD,0xFFBFE000 - .set _APTDpde,0xFDFF7000+4*APDRPDROFF + .globl _APTmap,_APTD,_APTDpde + .set _APTmap,APTDPTDI << PDRSHIFT + .set _APTD,_APTmap + (APTDPTDI * NBPG) + .set _APTDpde,_PTD + (APTDPTDI * 4) /* XXX 4=sizeof pde */ /* * Access to each processes kernel stack is via a region of * per-process address space (at the beginning), immediatly above * the user process stack. */ - .set _kstack, USRSTACK + .set _kstack,USRSTACK .globl _kstack .set PPDROFF,0x3F6 - .set PPTEOFF,0x400-UPAGES # 0x3FE + .set PPTEOFF,0x400-UPAGES /* 0x3FE */ -#define ENTRY(name) \ - .globl _/**/name; ALIGN_TEXT; _/**/name: -#define ALTENTRY(name) ENTRY(name) /* - * Initialization + * Globals */ .data - .globl _cpu,_cold,_boothowto,_bootdev,_cyloffset,_atdevbase,_atdevphys -_cpu: .long 0 # are we 386, 386sx, or 486 -_cold: .long 1 # cold till we are not -_atdevbase: .long 0 # location of start of iomem in virtual -_atdevphys: .long 0 # location of device mapping ptes (phys) + .globl _esym +_esym: .long 0 /* ptr to end of syms */ + + .globl _boothowto,_bootdev,_curpcb + + .globl _cpu,_cold,_atdevbase +_cpu: .long 0 /* are we 386, 386sx, or 486 */ +_cold: .long 1 /* cold till we are not */ +_atdevbase: .long 0 /* location of start of iomem in virtual */ +_atdevphys: .long 0 /* location of device mapping ptes (phys) */ - .globl _IdlePTD, _KPTphys + .globl _IdlePTD,_KPTphys _IdlePTD: .long 0 _KPTphys: .long 0 + .globl _cyloffset,_proc0paddr +_cyloffset: .long 0 +_proc0paddr: .long 0 + .space 512 tmpstk: + + +/* + * System Initialization + */ .text - .globl start -start: movw $0x1234,%ax - movw %ax,0x472 # warm boot + +/* + * btext: beginning of text section. + * Also the entry point (jumped to directly from the boot blocks). + */ +ENTRY(btext) + movw $0x1234,0x472 /* warm boot */ jmp 1f - .space 0x500 # skip over warm boot shit + .space 0x500 /* skip over warm boot shit */ /* - * pass parameters on stack (howto, bootdev, unit, cyloffset) + * pass parameters on stack (howto, bootdev, unit, cyloffset, esym) * note: (%esp) is return address of boot * ( if we want to hold onto /boot, it's physical %esp up to _end) */ 1: movl 4(%esp),%eax - movl %eax,_boothowto-SYSTEM + movl %eax,_boothowto-KERNBASE movl 8(%esp),%eax - movl %eax,_bootdev-SYSTEM + movl %eax,_bootdev-KERNBASE movl 12(%esp),%eax - movl %eax, _cyloffset-SYSTEM + movl %eax,_cyloffset-KERNBASE + movl 16(%esp),%eax + addl $KERNBASE,%eax + movl %eax,_esym-KERNBASE + + /* find out our CPU type. */ + pushfl + popl %eax + movl %eax,%ecx + xorl $0x40000,%eax + pushl %eax + popfl + pushfl + popl %eax + xorl %ecx,%eax + shrl $18,%eax + andl $1,%eax + push %ecx + popfl + + cmpl $0,%eax + jne 1f + movl $CPU_386,_cpu-KERNBASE + jmp 2f +1: movl $CPU_486,_cpu-KERNBASE +2: /* * Finished with old stack; load new %esp now instead of later so @@ -180,65 +238,64 @@ start: movw $0x1234,%ax * Oops, the gdt is in the carcass of the boot program so clearing * the rest of memory is still not possible. */ - movl $ tmpstk-SYSTEM,%esp # bootstrap stack end location - -#ifdef garbage - /* count up memory */ - - xorl %eax,%eax # start with base memory at 0x0 - #movl $ 0xA0000/NBPG,%ecx # look every 4K up to 640K - movl $ 0xA0,%ecx # look every 4K up to 640K -1: movl (%eax),%ebx # save location to check - movl $0xa55a5aa5,(%eax) # write test pattern - /* flush stupid cache here! (with bcopy (0,0,512*1024) ) */ - cmpl $0xa55a5aa5,(%eax) # does not check yet for rollover - jne 2f - movl %ebx,(%eax) # restore memory - addl $ NBPG,%eax - loop 1b -2: shrl $12,%eax - movl %eax,_Maxmem-SYSTEM - - movl $0x100000,%eax # next, talley remaining memory - #movl $((0xFFF000-0x100000)/NBPG),%ecx - movl $(0xFFF-0x100),%ecx -1: movl (%eax),%ebx # save location to check - movl $0xa55a5aa5,(%eax) # write test pattern - cmpl $0xa55a5aa5,(%eax) # does not check yet for rollover - jne 2f - movl %ebx,(%eax) # restore memory - addl $ NBPG,%eax - loop 1b -2: shrl $12,%eax - movl %eax,_Maxmem-SYSTEM -#endif + movl $tmpstk-KERNBASE,%esp /* bootstrap stack end location */ + +/* + * Virtual address space of kernel: + * + * text | data | bss | [syms] | page dir | proc0 kernel stack | usr stk map | Sysmap + * 0 1 2 3 4 + */ /* find end of kernel image */ - movl $_end-SYSTEM,%ecx - addl $ NBPG-1,%ecx + movl $_end-KERNBASE,%ecx + addl $NBPG-1,%ecx /* page align up */ andl $~(NBPG-1),%ecx - movl %ecx,%esi + movl %ecx,%esi /* esi=start of tables */ /* clear bss and memory for bootstrap pagetables. */ - movl $_edata-SYSTEM,%edi + movl $_edata-KERNBASE,%edi subl %edi,%ecx - addl $(UPAGES+5)*NBPG,%ecx -/* - * Virtual address space of kernel: - * - * text | data | bss | page dir | proc0 kernel stack | usr stk map | Sysmap - * 0 1 2 3 4 - */ - xorl %eax,%eax # pattern + addl $(UPAGES+5)*NBPG,%ecx /* size of tables */ + + xorl %eax,%eax /* pattern */ cld rep stosb - movl %esi,_IdlePTD-SYSTEM /*physical address of Idle Address space */ +/* + * If we are loaded at 0x0 check to see if we have space for the + * page tables pages after the kernel and before the 640K ISA memory + * hole. If we do not have space relocate the page table pages and + * the kernel stack to start at 1MB. The value that ends up in esi + * is used by the rest of locore to build the tables. Locore adjusts + * esi each time it allocates a structure and then passes the final + * value to init386(first) as the value first. esi should ALWAYS + * be page aligned!! + */ + movl %esi,%ecx /* Get current first availiable address */ + cmpl $0x100000,%ecx /* Lets see if we are already above 1MB */ + jge 1f /* yep, don't need to check for room */ + addl $(NKPDE + 4) * NBPG,%ecx /* XXX the 4 is for kstack */ + /* space for kstack, PTD and PTE's */ + cmpl $(640*1024),%ecx + /* see if it fits in low memory */ + jle 1f /* yep, don't need to relocate it */ + movl $0x100000,%esi /* won't fit, so start it at 1MB */ +1: + +/* physical address of Idle Address space */ + movl %esi,_IdlePTD-KERNBASE +/* + * fillkpt + * eax = (page frame address | control | status) == pte + * ebx = address of page table + * ecx = how many pages to map + */ #define fillkpt \ 1: movl %eax,(%ebx) ; \ - addl $ NBPG,%eax ; /* increment physical address */ \ + addl $NBPG,%eax ; /* increment physical address */ \ addl $4,%ebx ; /* next pte */ \ loop 1b ; @@ -249,30 +306,34 @@ start: movw $0x1234,%ax * * First step - build page tables */ - movl %esi,%ecx # this much memory, - shrl $ PGSHIFT,%ecx # for this many pte s - addl $ UPAGES+4,%ecx # including our early context - movl $0xa0,%ecx # XXX - cover debugger pages - movl $PG_V|PG_KW,%eax # having these bits set, - lea (4*NBPG)(%esi),%ebx # physical address of KPT in proc 0, - movl %ebx,_KPTphys-SYSTEM # in the kernel page table, + movl %esi,%ecx /* this much memory, */ + shrl $PGSHIFT,%ecx /* for this many pte s */ + addl $UPAGES+4,%ecx /* including our early context */ + cmpl $0xa0,%ecx /* XXX - cover debugger pages */ + jae 1f + movl $0xa0,%ecx +1: + movl $PG_V|PG_KW,%eax /* having these bits set, */ + lea (4*NBPG)(%esi),%ebx /* physical address of KPT in proc 0, */ + movl %ebx,_KPTphys-KERNBASE /* in the kernel page table, */ fillkpt /* map I/O memory map */ - movl $0x100-0xa0,%ecx # for this many pte s, - movl $(0xa0000|PG_V|PG_UW),%eax # having these bits set,(perhaps URW?) XXX 06 Aug 92 - movl %ebx,_atdevphys-SYSTEM # remember phys addr of ptes + movl $0x100-0xa0,%ecx /* for this many pte s, */ + movl $(0xa0000|PG_V|PG_UW),%eax /* having these bits set,(perhaps URW?) XXX 06 Aug 92 */ + movl %ebx,_atdevphys-KERNBASE /* remember phys addr of ptes */ fillkpt /* map proc 0's kernel stack into user page table page */ - movl $ UPAGES,%ecx # for this many pte s, - lea (1*NBPG)(%esi),%eax # physical address in proc 0 - lea (SYSTEM)(%eax),%edx - movl %edx,_proc0paddr-SYSTEM # remember VA for 0th process init - orl $PG_V|PG_KW,%eax # having these bits set, - lea (3*NBPG)(%esi),%ebx # physical address of stack pt in proc 0 + movl $UPAGES,%ecx /* for this many pte s, */ + lea (1*NBPG)(%esi),%eax /* physical address in proc 0 */ + lea (KERNBASE)(%eax),%edx + movl %edx,_proc0paddr-KERNBASE + /* remember VA for 0th process init */ + orl $PG_V|PG_KW,%eax /* having these bits set, */ + lea (3*NBPG)(%esi),%ebx /* physical address of stack pt in proc 0 */ addl $(PPTEOFF*4),%ebx fillkpt @@ -281,61 +342,61 @@ start: movw $0x1234,%ax * (of page directory elements - pde's) */ /* install a pde for temporary double map of bottom of VA */ - lea (4*NBPG)(%esi),%eax # physical address of kernel page table - orl $ PG_V|PG_UW,%eax # pde entry is valid XXX 06 Aug 92 - movl %eax,(%esi) # which is where temp maps! + lea (4*NBPG)(%esi),%eax /* physical address of kernel page table */ + orl $PG_V|PG_UW,%eax /* pde entry is valid XXX 06 Aug 92 */ + movl %eax,(%esi) /* which is where temp maps! */ /* kernel pde's */ - movl $ 3,%ecx # for this many pde s, - lea (SYSPDROFF*4)(%esi), %ebx # offset of pde for kernel + movl $(NKPDE),%ecx /* for this many pde s, */ + lea (KPTDI*4)(%esi),%ebx /* offset of pde for kernel */ fillkpt /* install a pde recursively mapping page directory as a page table! */ - movl %esi,%eax # phys address of ptd in proc 0 - orl $ PG_V|PG_UW,%eax # pde entry is valid XXX 06 Aug 92 - movl %eax, PDRPDROFF*4(%esi) # which is where PTmap maps! + movl %esi,%eax /* phys address of ptd in proc 0 */ + orl $PG_V|PG_UW,%eax /* pde entry is valid XXX 06 Aug 92 */ + movl %eax,PTDPTDI*4(%esi) /* which is where PTmap maps! */ /* install a pde to map kernel stack for proc 0 */ - lea (3*NBPG)(%esi),%eax # physical address of pt in proc 0 - orl $PG_V|PG_KW,%eax # pde entry is valid - movl %eax,PPDROFF*4(%esi) # which is where kernel stack maps! + lea (3*NBPG)(%esi),%eax /* physical address of pt in proc 0 */ + orl $PG_V|PG_KW,%eax /* pde entry is valid */ + movl %eax,PPDROFF*4(%esi) /* which is where kernel stack maps! */ /* copy and convert stuff from old gdt and idt for debugger */ - cmpl $0x0375c339,0x96104 # XXX - debugger signature + cmpl $0x0375c339,0x96104 /* XXX - debugger signature */ jne 1f - movb $1,_bdb_exists-SYSTEM + movb $1,_bdb_exists-KERNBASE 1: pushal subl $2*6,%esp sgdt (%esp) - movl 2(%esp),%esi # base address of current gdt - movl $_gdt-SYSTEM,%edi + movl 2(%esp),%esi /* base address of current gdt */ + movl $_gdt-KERNBASE,%edi movl %edi,2(%esp) movl $8*18/4,%ecx - rep # copy gdt + rep /* copy gdt */ movsl - movl $_gdt-SYSTEM,-8+2(%edi) # adjust gdt self-ptr + movl $_gdt-KERNBASE,-8+2(%edi) /* adjust gdt self-ptr */ movb $0x92,-8+5(%edi) sidt 6(%esp) - movl 6+2(%esp),%esi # base address of current idt - movl 8+4(%esi),%eax # convert dbg descriptor to ... + movl 6+2(%esp),%esi /* base address of current idt */ + movl 8+4(%esi),%eax /* convert dbg descriptor to ... */ movw 8(%esi),%ax - movl %eax,bdb_dbg_ljmp+1-SYSTEM # ... immediate offset ... + movl %eax,bdb_dbg_ljmp+1-KERNBASE /* ... immediate offset ... */ movl 8+2(%esi),%eax - movw %ax,bdb_dbg_ljmp+5-SYSTEM # ... and selector for ljmp - movl 24+4(%esi),%eax # same for bpt descriptor + movw %ax,bdb_dbg_ljmp+5-KERNBASE /* ... and selector for ljmp */ + movl 24+4(%esi),%eax /* same for bpt descriptor */ movw 24(%esi),%ax - movl %eax,bdb_bpt_ljmp+1-SYSTEM + movl %eax,bdb_bpt_ljmp+1-KERNBASE movl 24+2(%esi),%eax - movw %ax,bdb_bpt_ljmp+5-SYSTEM + movw %ax,bdb_bpt_ljmp+5-KERNBASE - movl $_idt-SYSTEM,%edi + movl $_idt-KERNBASE,%edi movl %edi,6+2(%esp) movl $8*4/4,%ecx - rep # copy idt + rep /* copy idt */ movsl lgdt (%esp) @@ -344,51 +405,54 @@ start: movw $0x1234,%ax addl $2*6,%esp popal - /* load base of page directory, and enable mapping */ - movl %esi,%eax # phys address of ptd in proc 0 - orl $ I386_CR3PAT,%eax - movl %eax,%cr3 # load ptd addr into mmu - movl %cr0,%eax # get control word + /* load base of page directory and enable mapping */ + movl %esi,%eax /* phys address of ptd in proc 0 */ + orl $I386_CR3PAT,%eax + movl %eax,%cr3 /* load ptd addr into mmu */ + movl %cr0,%eax /* get control word */ +/* + * XXX it is now safe to always (attempt to) set CR0_WP and to set up + * the page tables assuming it works, so USE_486_WRITE_PROTECT will go + * away. The special 386 PTE checking needs to be conditional on + * whatever distingiushes 486-only kernels from 386-486 kernels. + */ #ifdef USE_486_WRITE_PROTECT - orl $CR0_PE|CR0_PG|CR0_WP,%eax # and let s page! + orl $CR0_PE|CR0_PG|CR0_WP,%eax /* enable paging */ #else - orl $CR0_PE|CR0_PG,%eax # and let s page! + orl $CR0_PE|CR0_PG,%eax /* enable paging */ #endif - movl %eax,%cr0 # NOW! + movl %eax,%cr0 /* and let's page NOW! */ - pushl $begin # jump to high mem! + pushl $begin /* jump to high mem */ ret -begin: /* now running relocated at SYSTEM where the system is linked to run */ +begin: /* now running relocated at KERNBASE where the system is linked to run */ - .globl _Crtat - movl _Crtat,%eax - subl $0xfe0a0000,%eax - movl _atdevphys,%edx # get pte PA - subl _KPTphys,%edx # remove base of ptes, now have phys offset - shll $ PGSHIFT-2,%edx # corresponding to virt offset - addl $ SYSTEM,%edx # add virtual base - movl %edx, _atdevbase + .globl _Crtat /* XXX - locore should not know about */ + movl _Crtat,%eax /* variables of device drivers (pccons)! */ + subl $(KERNBASE+0xA0000),%eax + movl _atdevphys,%edx /* get pte PA */ + subl _KPTphys,%edx /* remove base of ptes, now have phys offset */ + shll $PGSHIFT-2,%edx /* corresponding to virt offset */ + addl $KERNBASE,%edx /* add virtual base */ + movl %edx,_atdevbase addl %eax,%edx movl %edx,_Crtat /* set up bootstrap stack */ - movl $ _kstack+UPAGES*NBPG-4*12,%esp # bootstrap stack end location - xorl %eax,%eax # mark end of frames + movl $_kstack+UPAGES*NBPG-4*12,%esp /* bootstrap stack end location */ + xorl %eax,%eax /* mark end of frames */ movl %eax,%ebp - movl _proc0paddr, %eax - movl %esi, PCB_CR3(%eax) + movl _proc0paddr,%eax + movl %esi,PCB_CR3(%eax) - lea 7*NBPG(%esi),%esi # skip past stack. - pushl %esi - /* relocate debugger gdt entries */ - movl $_gdt+8*9,%eax # adjust slots 9-17 + movl $_gdt+8*9,%eax /* adjust slots 9-17 */ movl $9,%ecx reloc_gdt: - movb $0xfe,7(%eax) # top byte of base addresses, was 0, - addl $8,%eax # now SYSTEM>>24 + movb $0xfe,7(%eax) /* top byte of base addresses, was 0, */ + addl $8,%eax /* now KERNBASE>>24 */ loop reloc_gdt cmpl $0,_bdb_exists @@ -396,25 +460,39 @@ reloc_gdt: int $3 1: - call _init386 # wire 386 chip for unix operation - + /* + * Skip over the page tables and the kernel stack + * XXX 4 is kstack size + */ + lea (NKPDE + 4) * NBPG(%esi),%esi + + pushl %esi /* value of first for init386(first) */ + call _init386 /* wire 386 chip for unix operation */ + movl $0,_PTD - call _main + call _main /* autoconfiguration, mountroot etc */ popl %esi + /* + * now we've run main() and determined what cpu-type we are, we can + * enable WP mode on i486 cpus and above. + * on return from main(), we are process 1 + * set up address space and stack so that we can 'return' to user mode + */ + .globl __ucodesel,__udatasel movl __ucodesel,%eax movl __udatasel,%ecx - # build outer stack frame - pushl %ecx # user ss - pushl $ USRSTACK # user esp - pushl %eax # user cs - pushl $0 # user ip + /* build outer stack frame */ + pushl %ecx /* user ss */ + pushl $USRSTACK /* user esp */ + pushl %eax /* user cs */ + pushl $0 /* user ip */ movl %cx,%ds movl %cx,%es - movl %ax,%fs # double map cs to fs - movl %cx,%gs # and ds to gs - lret # goto user! + movl %ax,%fs /* double map cs to fs */ + movl %cx,%gs /* and ds to gs */ + lret /* goto user! */ pushl $lretmsg1 /* "should never get here!" */ call _panic @@ -427,35 +505,41 @@ lretmsg1: #define LCALL(x,y) .byte 0x9a ; .long y; .word x /* - * Icode is copied out to process 1 to exec /etc/init. - * If the exec fails, process 1 exits. + * Icode is copied out to process 1 and executed in user mode: + * execve("/sbin/init", argv, envp); exit(0); + * If the execve fails, process 1 exits and the system panics. */ -ENTRY(icode) - # pushl $argv-_icode # gas fucks up again +NON_GPROF_ENTRY(icode) + pushl $0 /* envp for execve() */ + +# pushl $argv-_icode /* can't do this 'cos gas 1.38 is broken */ movl $argv,%eax subl $_icode,%eax - pushl %eax + pushl %eax /* argp for execve() */ - # pushl $init-_icode +# pushl $init-_icode movl $init,%eax subl $_icode,%eax - pushl %eax - pushl %eax # dummy out rta + pushl %eax /* fname for execve() */ + + pushl %eax /* dummy return address */ - movl %esp,%ebp movl $exec,%eax LCALL(0x7,0x0) - pushl %eax + + /* exit if something botches up in the above execve() */ + pushl %eax /* execve failed, the errno will do for an */ + /* exit code because errnos are < 128 */ + pushl %eax /* dummy return address */ movl $exit,%eax - pushl %eax # dummy out rta LCALL(0x7,0x0) init: .asciz "/sbin/init" ALIGN_DATA argv: - .long init+6-_icode # argv[0] = "init" ("/sbin/init" + 6) - .long eicode-_icode # argv[1] follows icode after copyout + .long init+6-_icode /* argv[0] = "init" ("/sbin/init" + 6) */ + .long eicode-_icode /* argv[1] follows icode after copyout */ .long 0 eicode: @@ -463,24 +547,23 @@ eicode: _szicode: .long _szicode-_icode -ENTRY(sigcode) - call 12(%esp) - lea 28(%esp),%eax # scp (the call may have clobbered the - # copy at 8(%esp)) - # XXX - use genassym +NON_GPROF_ENTRY(sigcode) + call SIGF_HANDLER(%esp) + lea SIGF_SC(%esp),%eax /* scp (the call may have clobbered the */ + /* copy at 8(%esp)) */ pushl %eax - pushl %eax # junk to fake return address - movl $103,%eax # sigreturn() - LCALL(0x7,0) # enter kernel with args on stack - hlt # never gets here + pushl %eax /* junk to fake return address */ + movl $103,%eax /* XXX sigreturn() */ + LCALL(0x7,0) /* enter kernel with args on stack */ + hlt /* never gets here */ .globl _szsigcode _szsigcode: .long _szsigcode-_sigcode - /* - * Support routines for GCC - */ +/* + * Support routines for GCC, general C-callable functions + */ ENTRY(__udivsi3) movl 4(%esp),%eax xorl %edx,%edx @@ -496,30 +579,56 @@ ENTRY(__divsi3) /* * I/O bus instructions via C */ -ENTRY(inb) +ENTRY(inb) /* val = inb(port) */ movl 4(%esp),%edx - subl %eax,%eax # clr eax + subl %eax,%eax NOP inb %dx,%al ret - -ENTRY(inw) +ENTRY(inw) /* val = inw(port) */ movl 4(%esp),%edx - subl %eax,%eax # clr eax + subl %eax,%eax NOP inw %dx,%ax ret +ENTRY(insb) /* insb(port, addr, cnt) */ + pushl %edi + movw 8(%esp),%dx + movl 12(%esp),%edi + movl 16(%esp),%ecx + cld + NOP + rep + insb + NOP + movl %edi,%eax + popl %edi + ret + +ENTRY(insw) /* insw(port, addr, cnt) */ + pushl %edi + movw 8(%esp),%dx + movl 12(%esp),%edi + movl 16(%esp),%ecx + cld + NOP + rep + insw + NOP + movl %edi,%eax + popl %edi + ret -ENTRY(rtcin) +ENTRY(rtcin) /* rtcin(val) */ movl 4(%esp),%eax outb %al,$0x70 - subl %eax,%eax # clr eax + subl %eax,%eax inb $0x71,%al ret -ENTRY(outb) +ENTRY(outb) /* outb(port, val) */ movl 4(%esp),%edx NOP movl 8(%esp),%eax @@ -527,7 +636,7 @@ ENTRY(outb) NOP ret -ENTRY(outw) +ENTRY(outw) /* outw(port, val) */ movl 4(%esp),%edx NOP movl 8(%esp),%eax @@ -535,16 +644,43 @@ ENTRY(outw) NOP ret +ENTRY(outsb) /* outsb(port, addr, cnt) */ + pushl %esi + movw 8(%esp),%dx + movl 12(%esp),%esi + movl 16(%esp),%ecx + cld + NOP + rep + outsb + NOP + movl %esi,%eax + popl %esi + ret + +ENTRY(outsw) /* outsw(port, addr, cnt) */ + pushl %esi + movw 8(%esp),%dx + movl 12(%esp),%esi + movl 16(%esp),%ecx + cld + NOP + rep + outsw + NOP + movl %esi,%eax + popl %esi + ret + /* - * void bzero(void *base, u_int cnt) + * bcopy family */ - -ENTRY(bzero) +ENTRY(bzero) /* void bzero(void *base, u_int cnt) */ pushl %edi movl 8(%esp),%edi movl 12(%esp),%ecx xorl %eax,%eax - shrl $2,%ecx + shrl $2,%ecx cld rep stosl @@ -555,11 +691,7 @@ ENTRY(bzero) popl %edi ret - /* - * fillw (pat,base,cnt) - */ - -ENTRY(fillw) +ENTRY(fillw) /* fillw(pat, base, cnt) */ pushl %edi movl 8(%esp),%eax movl 12(%esp),%edi @@ -571,6 +703,7 @@ ENTRY(fillw) ret ENTRY(bcopyb) +bcopyb: pushl %esi pushl %edi movl 12(%esp),%esi @@ -600,6 +733,7 @@ ENTRY(bcopyb) ret ENTRY(bcopyw) +bcopyw: pushl %esi pushl %edi movl 12(%esp),%esi @@ -642,21 +776,18 @@ ENTRY(bcopyw) ENTRY(bcopyx) movl 16(%esp),%eax cmpl $2,%eax - je _bcopyw + je bcopyw /* not _bcopyw, to avoid multiple mcounts */ cmpl $4,%eax - jne _bcopyb - /* - * Fall through to bcopy. ENTRY() provides harmless fill bytes. - */ + je bcopy + jmp bcopyb /* - * (ov)bcopy (src,dst,cnt) + * (ov)bcopy(src, dst, cnt) * ws@tools.de (Wolfgang Solfrank, TooLs GmbH) +49-228-985800 - * Changed by bde to not bother returning %eax = 0. */ - -ENTRY(ovbcopy) +ALTENTRY(ovbcopy) ENTRY(bcopy) +bcopy: pushl %esi pushl %edi movl 12(%esp),%esi @@ -697,89 +828,141 @@ ENTRY(bcopy) cld ret -#ifdef notdef -ENTRY(copyout) - movl _curpcb, %eax - movl $cpyflt, PCB_ONFAULT(%eax) # in case we page/protection violate - pushl %esi - pushl %edi - pushl %ebx - movl 16(%esp), %esi - movl 20(%esp), %edi - movl 24(%esp), %ebx - - /* first, check to see if "write fault" */ -1: movl %edi, %eax -#ifdef notyet - shrl $IDXSHIFT, %eax /* fetch pte associated with address */ - andb $0xfc, %al - movl _PTmap(%eax), %eax - - andb $7, %al /* if we are the one case that won't trap... */ - cmpb $5, %al - jne 2f - /* ... then simulate the trap! */ - pushl %edi - call _trapwrite /* trapwrite(addr) */ - popl %edx - - cmpl $0, %eax /* if not ok, return */ - jne cpyflt - /* otherwise, continue with reference */ -2: - movl %edi, %eax /* calculate remainder this pass */ - andl $0xfffff000, %eax - movl $NBPG, %ecx - subl %eax, %ecx - cmpl %ecx, %ebx - jle 3f - movl %ebx, %ecx -3: subl %ecx, %ebx - movl %ecx, %edx +ALTENTRY(ntohl) +ENTRY(htonl) + movl 4(%esp),%eax +#ifdef i486 + /* XXX */ + /* Since Gas 1.38 does not grok bswap this has been coded as the + * equivalent bytes. This can be changed back to bswap when we + * upgrade to a newer version of Gas */ + /* bswap %eax */ + .byte 0x0f + .byte 0xc8 #else - movl %ebx, %ecx - movl %ebx, %edx + xchgb %al,%ah + roll $16,%eax + xchgb %al,%ah #endif + ret - shrl $2,%ecx /* movem */ - cld - rep - movsl - movl %edx, %ecx /* don't depend on ecx here! */ - andl $3, %ecx - rep - movsb +ALTENTRY(ntohs) +ENTRY(htons) + movzwl 4(%esp),%eax + xchgb %al,%ah + ret -#ifdef notyet - cmpl $0, %ebx - jl 1b -#endif +/*****************************************************************************/ +/* copyout and fubyte family */ +/*****************************************************************************/ +/* + * Access user memory from inside the kernel. These routines and possibly + * the math- and DOS emulators should be the only places that do this. + * + * We have to access the memory with user's permissions, so use a segment + * selector with RPL 3. For writes to user space we have to additionally + * check the PTE for write permission, because the 386 does not check + * write permissions when we are executing with EPL 0. The 486 does check + * this if the WP bit is set in CR0, so we can use a simpler version here. + * + * These routines set curpcb->onfault for the time they execute. When a + * protection violation occurs inside the functions, the trap handler + * returns to *curpcb->onfault instead of the function. + */ - popl %ebx - popl %edi - popl %esi - xorl %eax,%eax - movl _curpcb,%edx - movl %eax,PCB_ONFAULT(%edx) - ret -ENTRY(copyin) +ENTRY(copyout) /* copyout(from_kernel, to_user, len) */ movl _curpcb,%eax - movl $cpyflt,PCB_ONFAULT(%eax) # in case we page/protection violate + movl $copyout_fault,PCB_ONFAULT(%eax) pushl %esi pushl %edi - pushl %ebx # XXX - not used, but affects stack offsets - movl 12(%esp),%esi - movl 16(%esp),%edi - movl 20(%esp),%ecx - shrl $2,%ecx + pushl %ebx + movl 16(%esp),%esi + movl 20(%esp),%edi + movl 24(%esp),%ebx + orl %ebx,%ebx /* anything to do? */ + jz done_copyout + + /* + * Check explicitly for non-user addresses. If 486 write protection + * is being used, this check is essential because we are in kernel + * mode so the h/w does not provide any protection against writing + * kernel addresses. + * + * Otherwise, it saves having to load and restore %es to get the + * usual segment-based protection (the destination segment for movs + * is always %es). The other explicit checks for user-writablility + * are not quite sufficient. They fail for the user area because + * we mapped the user area read/write to avoid having an #ifdef in + * vm_machdep.c. They fail for user PTEs and/or PTDs! (107 + * addresses including 0xff800000 and 0xfc000000). I'm not sure if + * this can be fixed. Marking the PTEs supervisor mode and the + * PDE's user mode would almost work, but there may be a problem + * with the self-referential PDE. + */ + movl %edi,%eax + addl %ebx,%eax + jc copyout_fault +#define VM_END_USER_ADDRESS 0xFDBFE000 /* XXX */ + cmpl $VM_END_USER_ADDRESS,%eax + ja copyout_fault + +#ifndef USE_486_WRITE_PROTECT + /* + * We have to check each PTE for user write permission. + * The checking may cause a page fault, so it is important to set + * up everything for return via copyout_fault before here. + */ + /* compute number of pages */ + movl %edi,%ecx + andl $NBPG-1,%ecx + addl %ebx,%ecx + decl %ecx + shrl $IDXSHIFT+2,%ecx + incl %ecx + + /* compute PTE offset for start address */ + movl %edi,%edx + shrl $IDXSHIFT,%edx + andb $0xfc,%dl + +1: /* check PTE for each page */ + movb _PTmap(%edx),%al + andb $0x07,%al /* Pages must be VALID + USERACC + WRITABLE */ + cmpb $0x07,%al + je 2f + + /* simulate a trap */ + pushl %edx + pushl %ecx + shll $IDXSHIFT,%edx + pushl %edx + call _trapwrite /* trapwrite(addr) */ + popl %edx + popl %ecx + popl %edx + + orl %eax,%eax /* if not ok, return EFAULT */ + jnz copyout_fault + +2: + addl $4,%edx + decl %ecx + jnz 1b /* check next page */ +#endif /* ndef USE_486_WRITE_PROTECT */ + + /* bcopy(%esi, %edi, %ebx) */ cld + movl %ebx,%ecx + shrl $2,%ecx rep movsl - movl 20(%esp),%ecx - andl $3,%ecx + movb %bl,%cl + andb $3,%cl /* XXX can we trust the rest of %ecx on clones? */ rep movsb + +done_copyout: popl %ebx popl %edi popl %esi @@ -789,31 +972,36 @@ ENTRY(copyin) ret ALIGN_TEXT -cpyflt: +copyout_fault: popl %ebx popl %edi popl %esi movl _curpcb,%edx movl $0,PCB_ONFAULT(%edx) - movl $ EFAULT,%eax + movl $EFAULT,%eax ret -#else -ENTRY(copyout) + +ENTRY(copyin) /* copyin(from_user, to_kernel, len) */ movl _curpcb,%eax - movl $cpyflt,PCB_ONFAULT(%eax) # in case we page/protection violate + movl $copyin_fault,PCB_ONFAULT(%eax) pushl %esi pushl %edi - movl 12(%esp),%esi - movl 16(%esp),%edi - movl 20(%esp),%ecx - shrl $2,%ecx + movl 12(%esp),%esi /* caddr_t from */ + movl 16(%esp),%edi /* caddr_t to */ + movl 20(%esp),%ecx /* size_t len */ + + movb %cl,%al + shrl $2,%ecx /* copy longword-wise */ cld + gs rep movsl - movl 20(%esp),%ecx - andl $3,%ecx + movb %al,%cl + andb $3,%cl /* copy remaining bytes */ + gs rep movsb + popl %edi popl %esi xorl %eax,%eax @@ -821,101 +1009,398 @@ ENTRY(copyout) movl %eax,PCB_ONFAULT(%edx) ret -ENTRY(copyin) - movl _curpcb,%eax - movl $cpyflt,PCB_ONFAULT(%eax) # in case we page/protection violate - pushl %esi - pushl %edi - movl 12(%esp),%esi - movl 16(%esp),%edi - movl 20(%esp),%ecx - shrl $2,%ecx - cld - rep - movsl - movl 20(%esp),%ecx - andl $3,%ecx - rep - movsb + ALIGN_TEXT +copyin_fault: popl %edi popl %esi + movl _curpcb,%edx + movl $0,PCB_ONFAULT(%edx) + movl $EFAULT,%eax + ret + + /* + * fu{byte,sword,word} : fetch a byte(sword, word) from user memory + */ +ALTENTRY(fuiword) +ENTRY(fuword) + movl _curpcb,%ecx + movl $fusufault,PCB_ONFAULT(%ecx) + movl 4(%esp),%edx + gs + movl (%edx),%eax + movl $0,PCB_ONFAULT(%ecx) + ret + +ENTRY(fusword) + movl _curpcb,%ecx + movl $fusufault,PCB_ONFAULT(%ecx) + movl 4(%esp),%edx + gs + movzwl (%edx),%eax + movl $0,PCB_ONFAULT(%ecx) + ret + +ALTENTRY(fuibyte) +ENTRY(fubyte) + movl _curpcb,%ecx + movl $fusufault,PCB_ONFAULT(%ecx) + movl 4(%esp),%edx + gs + movzbl (%edx),%eax + movl $0,PCB_ONFAULT(%ecx) + ret + + ALIGN_TEXT +fusufault: + movl _curpcb,%ecx + xorl %eax,%eax + movl %eax,PCB_ONFAULT(%ecx) + decl %eax + ret + + /* + * su{byte,sword,word}: write a byte(word, longword) to user memory + */ +#ifdef USE_486_WRITE_PROTECT + /* + * we only have to set the right segment selector. + */ +ALTENTRY(suiword) +ENTRY(suword) + movl _curpcb,%ecx + movl $fusufault,PCB_ONFAULT(%ecx) + movl 4(%esp),%edx + movl 8(%esp),%eax + gs + movl %eax,(%edx) + xorl %eax,%eax + movl %eax,PCB_ONFAULT(%ecx) + ret + +ENTRY(susword) + movl _curpcb,%ecx + movl $fusufault,PCB_ONFAULT(%ecx) + movl 4(%esp),%edx + movw 8(%esp),%ax + gs + movw %ax,(%edx) + xorl %eax,%eax + movl %eax,PCB_ONFAULT(%ecx) + ret + +ALTENTRY(suibyte) +ENTRY(subyte) + movl _curpcb,%ecx + movl $fusufault,PCB_ONFAULT(%ecx) + movl 4(%esp),%edx + movb 8(%esp),%al + gs + movb %al,(%edx) + xorl %eax,%eax + movl %eax,PCB_ONFAULT(%ecx) + ret + + +#else /* USE_486_WRITE_PROTECT */ + /* + * here starts the trouble again: check PTE, twice if word crosses + * a page boundary. + */ + /* XXX - page boundary crossing is not handled yet */ + +ALTENTRY(suibyte) +ENTRY(subyte) + movl _curpcb,%ecx + movl $fusufault,PCB_ONFAULT(%ecx) + movl 4(%esp),%edx + movl %edx,%eax + shrl $IDXSHIFT,%edx + andb $0xfc,%dl + movb _PTmap(%edx),%dl + andb $0x7,%dl /* must be VALID + USERACC + WRITE */ + cmpb $0x7,%dl + je 1f + /* simulate a trap */ + pushl %eax + call _trapwrite + popl %edx + orl %eax,%eax + jnz fusufault +1: + movl 4(%esp),%edx + movl 8(%esp),%eax + gs + movb %al,(%edx) + xorl %eax,%eax + movl _curpcb,%ecx + movl %eax,PCB_ONFAULT(%ecx) + ret + +ENTRY(susword) + movl _curpcb,%ecx + movl $fusufault,PCB_ONFAULT(%ecx) + movl 4(%esp),%edx + movl %edx,%eax + shrl $IDXSHIFT,%edx + andb $0xfc,%dl + movb _PTmap(%edx),%dl + andb $0x7,%dl /* must be VALID + USERACC + WRITE */ + cmpb $0x7,%dl + je 1f + /* simulate a trap */ + pushl %eax + call _trapwrite + popl %edx + orl %eax,%eax + jnz fusufault +1: + movl 4(%esp),%edx + movl 8(%esp),%eax + gs + movw %ax,(%edx) + xorl %eax,%eax + movl _curpcb,%ecx + movl %eax,PCB_ONFAULT(%ecx) + ret + +ALTENTRY(suiword) +ENTRY(suword) + movl _curpcb,%ecx + movl $fusufault,PCB_ONFAULT(%ecx) + movl 4(%esp),%edx + movl %edx,%eax + shrl $IDXSHIFT,%edx + andb $0xfc,%dl + movb _PTmap(%edx),%dl + andb $0x7,%dl /* must be VALID + USERACC + WRITE */ + cmpb $0x7,%dl + je 1f + /* simulate a trap */ + pushl %eax + call _trapwrite + popl %edx + orl %eax,%eax + jnz fusufault +1: + movl 4(%esp),%edx + movl 8(%esp),%eax + gs + movl %eax,0(%edx) xorl %eax,%eax - movl _curpcb,%edx - movl %eax,PCB_ONFAULT(%edx) + movl _curpcb,%ecx + movl %eax,PCB_ONFAULT(%ecx) ret - ALIGN_TEXT -cpyflt: popl %edi - popl %esi - movl _curpcb,%edx - movl $0,PCB_ONFAULT(%edx) - movl $ EFAULT,%eax - ret +#endif /* USE_486_WRITE_PROTECT */ -#endif +/* + * copyoutstr(from, to, maxlen, int *lencopied) + * copy a string from from to to, stop when a 0 character is reached. + * return ENAMETOOLONG if string is longer than maxlen, and + * EFAULT on protection violations. If lencopied is non-zero, + * return the actual length in *lencopied. + */ +#ifdef USE_486_WRITE_PROTECT - # insb(port,addr,cnt) -ENTRY(insb) +ENTRY(copyoutstr) + pushl %esi pushl %edi - movw 8(%esp),%dx - movl 12(%esp),%edi - movl 16(%esp),%ecx - cld - NOP - rep - insb - NOP - movl %edi,%eax - popl %edi - ret + movl _curpcb,%ecx + movl $cpystrflt,PCB_ONFAULT(%ecx) + + movl 12(%esp),%esi /* %esi = from */ + movl 16(%esp),%edi /* %edi = to */ + movl 20(%esp),%edx /* %edx = maxlen */ + incl %edx + +1: + decl %edx + jz 4f + /* + * gs override doesn't work for stosb. Use the same explicit check + * as in copyout(). It's much slower now because it is per-char. + * XXX - however, it would be faster to rewrite this function to use + * strlen() and copyout(). + */ + cmpl $VM_END_USER_ADDRESS,%edi + jae cpystrflt + lodsb + gs + stosb + orb %al,%al + jnz 1b + /* Success -- 0 byte reached */ + decl %edx + xorl %eax,%eax + jmp 6f +4: + /* edx is zero -- return ENAMETOOLONG */ + movl $ENAMETOOLONG,%eax + jmp 6f - # insw(port,addr,cnt) -ENTRY(insw) +#else /* ndef USE_486_WRITE_PROTECT */ + +ENTRY(copyoutstr) + pushl %esi pushl %edi - movw 8(%esp),%dx - movl 12(%esp),%edi - movl 16(%esp),%ecx - cld - NOP - .byte 0x66,0xf2,0x6d # rep insw - NOP + movl _curpcb,%ecx + movl $cpystrflt,PCB_ONFAULT(%ecx) + + movl 12(%esp),%esi /* %esi = from */ + movl 16(%esp),%edi /* %edi = to */ + movl 20(%esp),%edx /* %edx = maxlen */ +1: + /* + * It suffices to check that the first byte is in user space, because + * we look at a page at a time and the end address is on a page + * boundary. + */ + cmpl $VM_END_USER_ADDRESS,%edi + jae cpystrflt movl %edi,%eax + shrl $IDXSHIFT,%eax + andb $0xfc,%al + movb _PTmap(%eax),%al + andb $7,%al + cmpb $7,%al + je 2f + + /* simulate trap */ + pushl %edx + pushl %edi + call _trapwrite popl %edi - ret + popl %edx + orl %eax,%eax + jnz cpystrflt + +2: /* copy up to end of this page */ + movl %edi,%eax + andl $NBPG-1,%eax + movl $NBPG,%ecx + subl %eax,%ecx /* ecx = NBPG - (src % NBPG) */ + cmpl %ecx,%edx + jge 3f + movl %edx,%ecx /* ecx = min(ecx, edx) */ +3: + orl %ecx,%ecx + jz 4f + decl %ecx + decl %edx + lodsb + stosb + orb %al,%al + jnz 3b + + /* Success -- 0 byte reached */ + decl %edx + xorl %eax,%eax + jmp 6f + +4: /* next page */ + orl %edx,%edx + jnz 1b + /* edx is zero -- return ENAMETOOLONG */ + movl $ENAMETOOLONG,%eax + jmp 6f - # outsw(port,addr,cnt) -ENTRY(outsw) +#endif /* USE_486_WRITE_PROTECT */ + +/* + * copyinstr(from, to, maxlen, int *lencopied) + * copy a string from from to to, stop when a 0 character is reached. + * return ENAMETOOLONG if string is longer than maxlen, and + * EFAULT on protection violations. If lencopied is non-zero, + * return the actual length in *lencopied. + */ +ENTRY(copyinstr) pushl %esi - movw 8(%esp),%dx - movl 12(%esp),%esi - movl 16(%esp),%ecx - cld - NOP - .byte 0x66,0xf2,0x6f # rep outsw - NOP - movl %esi,%eax + pushl %edi + movl _curpcb,%ecx + movl $cpystrflt,PCB_ONFAULT(%ecx) + + movl 12(%esp),%esi /* %esi = from */ + movl 16(%esp),%edi /* %edi = to */ + movl 20(%esp),%edx /* %edx = maxlen */ + incl %edx + +1: + decl %edx + jz 4f + gs + lodsb + stosb + orb %al,%al + jnz 1b + /* Success -- 0 byte reached */ + decl %edx + xorl %eax,%eax + jmp 6f +4: + /* edx is zero -- return ENAMETOOLONG */ + movl $ENAMETOOLONG,%eax + jmp 6f + +cpystrflt: + movl $EFAULT,%eax +6: /* set *lencopied and return %eax */ + movl _curpcb,%ecx + movl $0,PCB_ONFAULT(%ecx) + movl 20(%esp),%ecx + subl %edx,%ecx + movl 24(%esp),%edx + orl %edx,%edx + jz 7f + movl %ecx,(%edx) +7: + popl %edi popl %esi ret - # outsb(port,addr,cnt) -ENTRY(outsb) + +/* + * copystr(from, to, maxlen, int *lencopied) + */ +ENTRY(copystr) pushl %esi - movw 8(%esp),%dx - movl 12(%esp),%esi - movl 16(%esp),%ecx - cld - NOP - rep - outsb - NOP - movl %esi,%eax + pushl %edi + + movl 12(%esp),%esi /* %esi = from */ + movl 16(%esp),%edi /* %edi = to */ + movl 20(%esp),%edx /* %edx = maxlen */ + incl %edx + +1: + decl %edx + jz 4f + lodsb + stosb + orb %al,%al + jnz 1b + /* Success -- 0 byte reached */ + decl %edx + xorl %eax,%eax + jmp 6f +4: + /* edx is zero -- return ENAMETOOLONG */ + movl $ENAMETOOLONG,%eax + +6: /* set *lencopied and return %eax */ + movl 20(%esp),%ecx + subl %edx,%ecx + movl 24(%esp),%edx + orl %edx,%edx + jz 7f + movl %ecx,(%edx) +7: + popl %edi popl %esi ret - /* - * void lgdt(struct region_descriptor *rdp); - */ -ENTRY(lgdt) +/* + * Handling of special 386 registers and descriptor tables etc + */ +ENTRY(lgdt) /* void lgdt(struct region_descriptor *rdp); */ /* reload the descriptor table */ movl 4(%esp),%eax lgdt (%eax) @@ -932,7 +1417,7 @@ ENTRY(lgdt) /* reload code selector by turning return into intersegmental return */ movl (%esp),%eax pushl %eax - # movl $KCSEL,4(%esp) +# movl $KCSEL,4(%esp) movl $8,4(%esp) lret @@ -958,49 +1443,7 @@ ENTRY(ltr) ltr 4(%esp) ret - /* - * void lcr3(caddr_t cr3) - */ - ALIGN_TEXT -ENTRY(load_cr3) -ALTENTRY(lcr3) - movl 4(%esp),%eax - orl $ I386_CR3PAT,%eax - movl %eax,%cr3 - ret - - # tlbflush() -ENTRY(tlbflush) - movl %cr3,%eax - orl $ I386_CR3PAT,%eax - movl %eax,%cr3 - ret - - # lcr0(cr0) -ENTRY(lcr0) -ALTENTRY(load_cr0) - movl 4(%esp),%eax - movl %eax,%cr0 - ret - - # rcr0() -ENTRY(rcr0) - movl %cr0,%eax - ret - - # rcr2() -ENTRY(rcr2) - movl %cr2,%eax - ret - - # rcr3() -ENTRY(_cr3) -ALTENTRY(rcr3) - movl %cr3,%eax - ret - - # ssdtosd(*ssdp,*sdp) -ENTRY(ssdtosd) +ENTRY(ssdtosd) /* ssdtosd(*ssdp,*sdp) */ pushl %ebx movl 8(%esp),%ecx movl 8(%ecx),%ebx @@ -1020,158 +1463,76 @@ ENTRY(ssdtosd) popl %ebx ret -/* - * {fu,su},{byte,word} - */ -ALTENTRY(fuiword) -ENTRY(fuword) - movl _curpcb,%ecx - movl $fusufault,PCB_ONFAULT(%ecx) - movl 4(%esp),%edx - .byte 0x65 # use gs - movl (%edx),%eax - movl $0,PCB_ONFAULT(%ecx) - ret - -ENTRY(fusword) - movl _curpcb,%ecx - movl $fusufault,PCB_ONFAULT(%ecx) #in case we page/protection violate - movl 4(%esp),%edx - .byte 0x65 # use gs - movzwl (%edx),%eax - movl $0,PCB_ONFAULT(%ecx) - ret - -ALTENTRY(fuibyte) -ENTRY(fubyte) - movl _curpcb,%ecx - movl $fusufault,PCB_ONFAULT(%ecx) #in case we page/protection violate - movl 4(%esp),%edx - .byte 0x65 # use gs - movzbl (%edx),%eax - movl $0,PCB_ONFAULT(%ecx) + +ENTRY(tlbflush) /* tlbflush() */ + movl %cr3,%eax + orl $I386_CR3PAT,%eax + movl %eax,%cr3 ret - ALIGN_TEXT -fusufault: - movl _curpcb,%ecx - xorl %eax,%eax - movl %eax,PCB_ONFAULT(%ecx) #in case we page/protection violate - decl %eax + +ENTRY(load_cr0) /* load_cr0(cr0) */ + movl 4(%esp),%eax + movl %eax,%cr0 ret -ALTENTRY(suiword) -ENTRY(suword) - movl _curpcb,%ecx - movl $fusufault,PCB_ONFAULT(%ecx) #in case we page/protection violate - movl 4(%esp),%edx - movl 8(%esp),%eax -#ifdef notdef - shrl $IDXSHIFT, %edx /* fetch pte associated with address */ - andb $0xfc, %dl - movl _PTmap(%edx), %edx +ENTRY(rcr0) /* rcr0() */ + movl %cr0,%eax + ret - andb $7, %dl /* if we are the one case that won't trap... */ - cmpb $5 , %edx - jne 1f - /* ... then simulate the trap! */ - pushl %edi - call _trapwrite /* trapwrite(addr) */ - popl %edx - cmpl $0, %eax /* if not ok, return */ - jne fusufault - movl 8(%esp),%eax /* otherwise, continue with reference */ -1: - movl 4(%esp),%edx -#endif - .byte 0x65 # use gs - movl %eax,(%edx) - xorl %eax,%eax - movl %eax,PCB_ONFAULT(%ecx) #in case we page/protection violate + +ENTRY(rcr2) /* rcr2() */ + movl %cr2,%eax ret - -ENTRY(susword) - movl _curpcb,%ecx - movl $fusufault,PCB_ONFAULT(%ecx) #in case we page/protection violate - movl 4(%esp),%edx - movl 8(%esp),%eax -#ifdef notdef -shrl $IDXSHIFT, %edx /* calculate pte address */ -andb $0xfc, %dl -movl _PTmap(%edx), %edx -andb $7, %edx /* if we are the one case that won't trap... */ -cmpb $5 , %edx -jne 1f -/* ..., then simulate the trap! */ - pushl %edi - call _trapwrite /* trapwrite(addr) */ - popl %edx -movl _curpcb, %ecx # restore trashed registers -cmpl $0, %eax /* if not ok, return */ -jne fusufault -movl 8(%esp),%eax -1: movl 4(%esp),%edx -#endif - .byte 0x65 # use gs - movw %ax,(%edx) - xorl %eax,%eax - movl %eax,PCB_ONFAULT(%ecx) #in case we page/protection violate + + +ENTRY(rcr3) /* rcr3() */ + movl %cr3,%eax ret -ALTENTRY(suibyte) -ENTRY(subyte) - movl _curpcb,%ecx - movl $fusufault,PCB_ONFAULT(%ecx) #in case we page/protection violate - movl 4(%esp),%edx - movl 8(%esp),%eax -#ifdef notdef -shrl $IDXSHIFT, %edx /* calculate pte address */ -andb $0xfc, %dl -movl _PTmap(%edx), %edx -andb $7, %edx /* if we are the one case that won't trap... */ -cmpb $5 , %edx -jne 1f -/* ..., then simulate the trap! */ - pushl %edi - call _trapwrite /* trapwrite(addr) */ - popl %edx -movl _curpcb, %ecx # restore trashed registers -cmpl $0, %eax /* if not ok, return */ -jne fusufault -movl 8(%esp),%eax -1: movl 4(%esp),%edx -#endif - .byte 0x65 # use gs - movb %eax,(%edx) - xorl %eax,%eax - movl %eax,PCB_ONFAULT(%ecx) #in case we page/protection violate + +ENTRY(load_cr3) /* void load_cr3(caddr_t cr3) */ + movl 4(%esp),%eax + orl $I386_CR3PAT,%eax + movl %eax,%cr3 ret + +/*****************************************************************************/ +/* setjump, longjump */ +/*****************************************************************************/ + ENTRY(setjmp) movl 4(%esp),%eax - movl %ebx, (%eax) # save ebx - movl %esp, 4(%eax) # save esp - movl %ebp, 8(%eax) # save ebp - movl %esi,12(%eax) # save esi - movl %edi,16(%eax) # save edi - movl (%esp),%edx # get rta - movl %edx,20(%eax) # save eip - xorl %eax,%eax # return (0); + movl %ebx,(%eax) /* save ebx */ + movl %esp,4(%eax) /* save esp */ + movl %ebp,8(%eax) /* save ebp */ + movl %esi,12(%eax) /* save esi */ + movl %edi,16(%eax) /* save edi */ + movl (%esp),%edx /* get rta */ + movl %edx,20(%eax) /* save eip */ + xorl %eax,%eax /* return(0); */ ret ENTRY(longjmp) movl 4(%esp),%eax - movl (%eax),%ebx # restore ebx - movl 4(%eax),%esp # restore esp - movl 8(%eax),%ebp # restore ebp - movl 12(%eax),%esi # restore esi - movl 16(%eax),%edi # restore edi - movl 20(%eax),%edx # get rta - movl %edx,(%esp) # put in return frame - xorl %eax,%eax # return (1); + movl (%eax),%ebx /* restore ebx */ + movl 4(%eax),%esp /* restore esp */ + movl 8(%eax),%ebp /* restore ebp */ + movl 12(%eax),%esi /* restore esi */ + movl 16(%eax),%edi /* restore edi */ + movl 20(%eax),%edx /* get rta */ + movl %edx,(%esp) /* put in return frame */ + xorl %eax,%eax /* return(1); */ incl %eax ret + + +/*****************************************************************************/ +/* Scheduling */ +/*****************************************************************************/ + /* * The following primitives manipulate the run queues. * _whichqs tells which of the 32 queues _qs @@ -1193,17 +1554,17 @@ ENTRY(longjmp) */ ENTRY(setrq) movl 4(%esp),%eax - cmpl $0,P_RLINK(%eax) # should not be on q already + cmpl $0,P_RLINK(%eax) /* should not be on q already */ je set1 pushl $set2 call _panic set1: movzbl P_PRI(%eax),%edx shrl $2,%edx - btsl %edx,_whichqs # set q full bit + btsl %edx,_whichqs /* set q full bit */ shll $3,%edx - addl $_qs,%edx # locate q hdr - movl %edx,P_LINK(%eax) # link process on tail of q + addl $_qs,%edx /* locate q hdr */ + movl %edx,P_LINK(%eax) /* link process on tail of q */ movl P_RLINK(%edx),%ecx movl %ecx,P_RLINK(%eax) movl %eax,P_RLINK(%edx) @@ -1221,13 +1582,13 @@ ENTRY(remrq) movl 4(%esp),%eax movzbl P_PRI(%eax),%edx shrl $2,%edx - btrl %edx,_whichqs # clear full bit, panic if clear already + btrl %edx,_whichqs /* clear full bit, panic if clear already */ jb rem1 pushl $rem3 call _panic rem1: pushl %edx - movl P_LINK(%eax),%ecx # unlink process + movl P_LINK(%eax),%ecx /* unlink process */ movl P_RLINK(%eax),%edx movl %edx,P_RLINK(%ecx) movl P_RLINK(%eax),%ecx @@ -1237,12 +1598,12 @@ rem1: movl $_qs,%ecx shll $3,%edx addl %edx,%ecx - cmpl P_LINK(%ecx),%ecx # q still has something? + cmpl P_LINK(%ecx),%ecx /* q still has something? */ je rem2 - shrl $3,%edx # yes, set bit as still full + shrl $3,%edx /* yes, set bit as still full */ btsl %edx,_whichqs rem2: - movl $0,P_RLINK(%eax) # zap reverse link to indicate off list + movl $0,P_RLINK(%eax) /* zap reverse link to indicate off list */ ret rem3: .asciz "remrq" @@ -1252,21 +1613,19 @@ sw0: .asciz "swtch" * When no processes are on the runq, Swtch branches to idle * to wait for something to come ready. */ - .globl Idle ALIGN_TEXT Idle: -sti_for_idle: sti SHOW_STI + ALIGN_TEXT -idle: +idle_loop: call _spl0 cmpl $0,_whichqs jne sw1 - hlt # wait for interrupt - jmp idle + hlt /* wait for interrupt */ + jmp idle_loop - SUPERALIGN_TEXT /* so profiling doesn't lump Idle with swtch().. */ badsw: pushl $sw0 call _panic @@ -1275,6 +1634,7 @@ badsw: /* * Swtch() */ + SUPERALIGN_TEXT /* so profiling doesn't lump Idle with swtch().. */ ENTRY(swtch) incl _cnt+V_SWTCH @@ -1289,15 +1649,15 @@ ENTRY(swtch) movl P_ADDR(%ecx),%ecx - movl (%esp),%eax # Hardware registers - movl %eax, PCB_EIP(%ecx) - movl %ebx, PCB_EBX(%ecx) - movl %esp, PCB_ESP(%ecx) - movl %ebp, PCB_EBP(%ecx) - movl %esi, PCB_ESI(%ecx) - movl %edi, PCB_EDI(%ecx) + movl (%esp),%eax /* Hardware registers */ + movl %eax,PCB_EIP(%ecx) + movl %ebx,PCB_EBX(%ecx) + movl %esp,PCB_ESP(%ecx) + movl %ebp,PCB_EBP(%ecx) + movl %esi,PCB_ESI(%ecx) + movl %edi,PCB_EDI(%ecx) -#ifdef NPX +#if NNPX > 0 /* have we used fp, and need a save? */ mov _curproc,%eax cmp %eax,_npxproc @@ -1309,14 +1669,14 @@ ENTRY(swtch) popl %eax popl %ecx 1: -#endif +#endif /* NNPX > 0 */ - movl _CMAP2,%eax # save temporary map PTE - movl %eax,PCB_CMAP2(%ecx) # in our context - movl $0,_curproc # out of process + movl _CMAP2,%eax /* save temporary map PTE */ + movl %eax,PCB_CMAP2(%ecx) /* in our context */ + movl $0,_curproc /* out of process */ - # movw _cpl, %ax - # movw %ax, PCB_IML(%ecx) # save ipl +# movw _cpl,%ax +# movw %ax,PCB_IML(%ecx) /* save ipl */ /* save is done, now choose a new process or idle */ sw1: @@ -1324,35 +1684,35 @@ sw1: SHOW_CLI movl _whichqs,%edi 2: - # XXX - bsf is sloow - bsfl %edi,%eax # find a full q - je sti_for_idle # if none, idle - # XX update whichqs? + /* XXX - bsf is sloow */ + bsfl %edi,%eax /* find a full q */ + je Idle /* if none, idle */ + /* XX update whichqs? */ swfnd: - btrl %eax,%edi # clear q full status - jnb 2b # if it was clear, look for another - movl %eax,%ebx # save which one we are using + btrl %eax,%edi /* clear q full status */ + jnb 2b /* if it was clear, look for another */ + movl %eax,%ebx /* save which one we are using */ shll $3,%eax - addl $_qs,%eax # select q + addl $_qs,%eax /* select q */ movl %eax,%esi #ifdef DIAGNOSTIC - cmpl P_LINK(%eax),%eax # linked to self? (e.g. not on list) - je badsw # not possible + cmpl P_LINK(%eax),%eax /* linked to self? (e.g. not on list) */ + je badsw /* not possible */ #endif - movl P_LINK(%eax),%ecx # unlink from front of process q + movl P_LINK(%eax),%ecx /* unlink from front of process q */ movl P_LINK(%ecx),%edx movl %edx,P_LINK(%eax) movl P_RLINK(%ecx),%eax movl %eax,P_RLINK(%edx) - cmpl P_LINK(%ecx),%esi # q empty + cmpl P_LINK(%ecx),%esi /* q empty */ je 3f - btsl %ebx,%edi # nope, set to indicate full + btsl %ebx,%edi /* nope, set to indicate full */ 3: - movl %edi,_whichqs # update q status + movl %edi,_whichqs /* update q status */ movl $0,%eax movl %eax,_want_resched @@ -1360,7 +1720,7 @@ swfnd: #ifdef DIAGNOSTIC cmpl %eax,P_WCHAN(%ecx) jne badsw - cmpb $ SRUN,P_STAT(%ecx) + cmpb $SRUN,P_STAT(%ecx) jne badsw #endif @@ -1372,21 +1732,21 @@ swfnd: movl %ebx,%cr3 /* restore context */ - movl PCB_EBX(%edx), %ebx - movl PCB_ESP(%edx), %esp - movl PCB_EBP(%edx), %ebp - movl PCB_ESI(%edx), %esi - movl PCB_EDI(%edx), %edi - movl PCB_EIP(%edx), %eax - movl %eax, (%esp) - - movl PCB_CMAP2(%edx),%eax # get temporary map - movl %eax,_CMAP2 # reload temporary map PTE - - movl %ecx,_curproc # into next process + movl PCB_EBX(%edx),%ebx + movl PCB_ESP(%edx),%esp + movl PCB_EBP(%edx),%ebp + movl PCB_ESI(%edx),%esi + movl PCB_EDI(%edx),%edi + movl PCB_EIP(%edx),%eax + movl %eax,(%esp) + + movl PCB_CMAP2(%edx),%eax /* get temporary map */ + movl %eax,_CMAP2 /* reload temporary map PTE */ + + movl %ecx,_curproc /* into next process */ movl %edx,_curpcb - pushl %edx # save p to return + pushl %edx /* save p to return */ /* * XXX - 0.0 forgot to save it - is that why this was commented out in 0.1? * I think restoring the cpl is unnecessary, but we must turn off the cli @@ -1403,7 +1763,7 @@ swfnd: * XXX - 0.0 gets here via swtch_to_inactive(). I think 0.1 gets here in the * same way. Better return a value. */ - popl %eax # return (p); + popl %eax /* return(p); */ ret ENTRY(mvesp) @@ -1419,13 +1779,13 @@ ENTRY(mvesp) * pass it back as a return value. */ ENTRY(swtch_to_inactive) - popl %edx # old pc - popl %eax # arg, our return value + popl %edx /* old pc */ + popl %eax /* arg, our return value */ movl _IdlePTD,%ecx - movl %ecx,%cr3 # good bye address space + movl %ecx,%cr3 /* good bye address space */ #write buffer? - movl $tmpstk-4,%esp # temporary stack, compensated for call - jmp %edx # return, execute remainder of cleanup + movl $tmpstk-4,%esp /* temporary stack, compensated for call */ + jmp %edx /* return, execute remainder of cleanup */ /* * savectx(pcb, altreturn) @@ -1433,18 +1793,18 @@ ENTRY(swtch_to_inactive) * for alternate return ala longjmp in swtch if altreturn is true. */ ENTRY(savectx) - movl 4(%esp), %ecx - movw _cpl, %ax - movw %ax, PCB_IML(%ecx) - movl (%esp), %eax - movl %eax, PCB_EIP(%ecx) - movl %ebx, PCB_EBX(%ecx) - movl %esp, PCB_ESP(%ecx) - movl %ebp, PCB_EBP(%ecx) - movl %esi, PCB_ESI(%ecx) - movl %edi, PCB_EDI(%ecx) - -#ifdef NPX + movl 4(%esp),%ecx + movw _cpl,%ax + movw %ax,PCB_IML(%ecx) + movl (%esp),%eax + movl %eax,PCB_EIP(%ecx) + movl %ebx,PCB_EBX(%ecx) + movl %esp,PCB_ESP(%ecx) + movl %ebp,PCB_EBP(%ecx) + movl %esi,PCB_ESI(%ecx) + movl %edi,PCB_EDI(%ecx) + +#if NNPX > 0 /* * If npxproc == NULL, then the npx h/w state is irrelevant and the * state had better already be in the pcb. This is true for forks @@ -1480,18 +1840,18 @@ ENTRY(savectx) addl $12,%esp popl %ecx 1: -#endif +#endif /* NNPX > 0 */ - movl _CMAP2, %edx # save temporary map PTE - movl %edx, PCB_CMAP2(%ecx) # in our context + movl _CMAP2,%edx /* save temporary map PTE */ + movl %edx,PCB_CMAP2(%ecx) /* in our context */ - cmpl $0, 8(%esp) + cmpl $0,8(%esp) je 1f - movl %esp, %edx # relocate current sp relative to pcb - subl $_kstack, %edx # (sp is relative to kstack): - addl %edx, %ecx # pcb += sp - kstack; - movl %eax, (%ecx) # write return pc at (relocated) sp@ - # this mess deals with replicating register state gcc hides + movl %esp,%edx /* relocate current sp relative to pcb */ + subl $_kstack,%edx /* (sp is relative to kstack): */ + addl %edx,%ecx /* pcb += sp - kstack; */ + movl %eax,(%ecx) /* write return pc at (relocated) sp@ */ + /* this mess deals with replicating register state gcc hides */ movl 12(%esp),%eax movl %eax,12(%ecx) movl 16(%esp),%eax @@ -1501,14 +1861,13 @@ ENTRY(savectx) movl 24(%esp),%eax movl %eax,24(%ecx) 1: - xorl %eax, %eax # return 0 + xorl %eax,%eax /* return 0 */ ret /* * addupc(int pc, struct uprof *up, int ticks): * update profiling information for the user process. */ - ENTRY(addupc) pushl %ebp movl %esp,%ebp @@ -1542,47 +1901,33 @@ L1: proffault: /* if we get a fault, then kill profiling all together */ movl $0,PCB_ONFAULT(%edx) /* squish the fault handler */ - movl 12(%ebp),%ecx + movl 12(%ebp),%ecx movl $0,PR_SCALE(%ecx) /* up->pr_scale = 0 */ leave ret - # To be done: - ENTRY(astoff) +/* To be done: */ +ENTRY(astoff) ret - .data - ALIGN_DATA - .globl _cyloffset, _curpcb -_cyloffset: .long 0 - .globl _proc0paddr -_proc0paddr: .long 0 -LF: .asciz "swtch %x" - ALIGN_DATA - -#if 0 -#define PANIC(msg) xorl %eax,%eax; movl %eax,_waittime; pushl 1f; \ - call _panic; MSG(msg) -#define PRINTF(n,msg) pushal ; nop ; pushl 1f; call _printf; MSG(msg) ; \ - popl %eax ; popal -#define MSG(msg) .data; 1: .asciz msg; ALIGN_DATA; .text -#endif /* 0 */ +/*****************************************************************************/ +/* Trap handling */ +/*****************************************************************************/ /* * Trap and fault vector routines * * XXX - debugger traps are now interrupt gates so at least bdb doesn't lose * control. The sti's give the standard losing behaviour for ddb and kgdb. - */ + */ #define IDTVEC(name) ALIGN_TEXT; .globl _X/**/name; _X/**/name: #define TRAP(a) pushl $(a) ; jmp alltraps #ifdef KGDB -#define BPTTRAP(a) sti; pushl $(a) ; jmp bpttraps +# define BPTTRAP(a) sti; pushl $(a) ; jmp bpttraps #else -#define BPTTRAP(a) sti; TRAP(a) +# define BPTTRAP(a) sti; TRAP(a) #endif - .text IDTVEC(div) pushl $0; TRAP(T_DIVIDE) IDTVEC(dbg) @@ -1624,7 +1969,7 @@ IDTVEC(page) IDTVEC(rsvd) pushl $0; TRAP(T_RESERVED) IDTVEC(fpu) -#ifdef NPX +#if NNPX > 0 /* * Handle like an interrupt so that we can call npxintr to clear the * error. It would be better to handle npx interrupts as traps but @@ -1646,9 +1991,9 @@ IDTVEC(fpu) incl _cnt+V_TRAP call _npxintr jmp doreti -#else +#else /* NNPX > 0 */ pushl $0; TRAP(T_ARITHTRAP) -#endif +#endif /* NNPX > 0 */ /* 17 - 31 reserved for future exp */ IDTVEC(rsvd0) pushl $0; TRAP(17) @@ -1707,7 +2052,7 @@ calltrap: * This code checks for a kgdb trap, then falls through * to the regular trap code. */ - ALIGN_TEXT + SUPERALIGN_TEXT bpttraps: pushal nop @@ -1717,26 +2062,25 @@ bpttraps: movl %ax,%ds movl %ax,%es testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp) - # non-kernel mode? - jne calltrap # yes - call _kgdb_trap_glue + /* non-kernel mode? */ + jne calltrap /* yes */ + call _kgdb_trap_glue jmp calltrap #endif /* * Call gate entry for syscall */ - SUPERALIGN_TEXT IDTVEC(syscall) - pushfl # only for stupid carry bit and more stupid wait3 cc kludge - # XXX - also for direction flag (bzero, etc. clear it) - pushal # only need eax,ecx,edx - trap resaves others + pushfl /* only for stupid carry bit and more stupid wait3 cc kludge */ + /* XXX - also for direction flag (bzero, etc. clear it) */ + pushal /* only need eax,ecx,edx - trap resaves others */ nop - movl $KDSEL,%eax # switch to kernel segments + movl $KDSEL,%eax /* switch to kernel segments */ movl %ax,%ds movl %ax,%es - incl _cnt+V_SYSCALL # kml 3/25/93 + incl _cnt+V_SYSCALL /* kml 3/25/93 */ call _syscall /* * Return through doreti to handle ASTs. Have to change syscall frame @@ -1759,44 +2103,18 @@ IDTVEC(syscall) pushal nop movl __udatasel,%eax /* switch back to user segments */ - push %eax /* XXX - better to preserve originals? */ - push %eax + pushl %eax /* XXX - better to preserve originals? */ + pushl %eax pushl _cpl pushl $0 jmp doreti -ENTRY(htonl) -ENTRY(ntohl) - movl 4(%esp),%eax -#ifdef i486 - /* XXX */ - /* Since Gas 1.38 does not grok bswap this has been coded as the - * equivalent bytes. This can be changed back to bswap when we - * upgrade to a newer version of Gas */ - /* bswap %eax */ - .byte 0x0f - .byte 0xc8 -#else - xchgb %al,%ah - roll $16,%eax - xchgb %al,%ah -#endif - ret - -ENTRY(htons) -ENTRY(ntohs) - movzwl 4(%esp),%eax - xchgb %al,%ah - ret - #ifdef SHOW_A_LOT - /* * 'show_bits' was too big when defined as a macro. The line length for some * enclosing macro was too big for gas. Perhaps the code would have blown * the cache anyway. */ - ALIGN_TEXT show_bits: pushl %eax @@ -1826,5 +2144,9 @@ bit_colors: #endif /* SHOW_A_LOT */ + +/* + * include generated interrupt vectors and ISA intr code + */ #include "i386/isa/vector.s" #include "i386/isa/icu.s" diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c index f0351a7ddf..49b93045d6 100644 --- a/sys/i386/i386/machdep.c +++ b/sys/i386/i386/machdep.c @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1982, 1987, 1990 The Regents of the University of California. * Copyright (c) 1992 Terrence R. Lambert. + * Copyright (c) 1982, 1987, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by @@ -34,31 +34,22 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)machdep.c 7.4 (Berkeley) 6/3/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 5 00158 - * -------------------- ----- ---------------------- - * - * 15 Aug 92 William Jolitz Large memory bug - * 15 Aug 92 Terry Lambert Fixed CMOS RAM size bug - * 25 Mar 93 Sean Eric Fagan Added #ifdef HZ around microtime for - * the new microtime.s routine - * 08 Apr 93 Andrew Herbert Fixes for kmem_alloc panics - * 20 Apr 93 Bruce Evans New npx-0.5 code - * 25 Apr 93 Bruce Evans New intr-0.1 code + * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 + * $Id: machdep.c,v 1.11 1993/10/14 18:15:35 rgrimes Exp $ */ -static char rcsid[] = "$Header: /usr/src/sys.386bsd/i386/i386/RCS/machdep.c,v 1.2 92/01/21 14:22:09 william Exp Locker: root $"; +#include "npx.h" +#include "isa.h" #include #include "param.h" #include "systm.h" #include "signalvar.h" #include "kernel.h" +#include "map.h" #include "proc.h" #include "user.h" +#include "exec.h" /* for PS_STRINGS */ #include "buf.h" #include "reboot.h" #include "conf.h" @@ -69,16 +60,33 @@ static char rcsid[] = "$Header: /usr/src/sys.386bsd/i386/i386/RCS/machdep.c,v 1. #include "msgbuf.h" #include "net/netisr.h" +#ifdef SYSVSHM +#include "sys/shm.h" +#endif + #include "vm/vm.h" #include "vm/vm_kern.h" #include "vm/vm_page.h" +#include "sys/exec.h" +#include "sys/vnode.h" + +#ifndef MACHINE_NONCONTIG extern vm_offset_t avail_end; +#else +extern vm_offset_t avail_start, avail_end; +static vm_offset_t hole_start, hole_end; +static vm_offset_t avail_next; +static unsigned int avail_remaining; +#endif /* MACHINE_NONCONTIG */ #include "machine/cpu.h" #include "machine/reg.h" #include "machine/psl.h" #include "machine/specialreg.h" +#include "machine/sysarch.h" + +#include "i386/isa/isa.h" #include "i386/isa/rtc.h" @@ -99,9 +107,10 @@ int bufpages = BUFPAGES; #else int bufpages = 0; #endif -int msgbufmapped; /* set when safe to use msgbuf */ extern int freebufspace; +int _udatasel, _ucodesel; + /* * Machine-dependent startup code */ @@ -116,6 +125,11 @@ int biosmem; extern cyloffset; +int cpu_class; + +void dumpsys __P((void)); + +void cpu_startup() { register int unixsize; @@ -135,18 +149,21 @@ cpu_startup() /* avail_end was pre-decremented in pmap_bootstrap to compensate */ for (i = 0; i < btoc(sizeof (struct msgbuf)); i++) +#ifndef MACHINE_NONCONTIG pmap_enter(pmap_kernel(), msgbufp, avail_end + i * NBPG, VM_PROT_ALL, TRUE); +#else + pmap_enter(pmap_kernel(), (caddr_t)msgbufp + i * NBPG, + avail_end + i * NBPG, VM_PROT_ALL, TRUE); +#endif msgbufmapped = 1; -#ifdef KDB - kdb_init(); /* startup kernel debugger */ -#endif /* * Good {morning,afternoon,evening,night}. */ - /*printf(version); - printf("real mem = %d\n", ctob(physmem));*/ + printf(version); + identifycpu(); + printf("real mem = %d\n", ctob(physmem)); /* * Allocate space for system data structures. @@ -171,26 +188,31 @@ again: (name) = (type *)v; v = (caddr_t)((name)+(num)) #define valloclim(name, type, num, lim) \ (name) = (type *)v; v = (caddr_t)((lim) = ((name)+(num))) +/* valloc(cfree, struct cblock, nclist); no clists any more!!! - cgd */ valloc(callout, struct callout, ncallout); +#ifdef NetBSD + valloc(swapmap, struct map, nswapmap = maxproc * 2); +#endif #ifdef SYSVSHM valloc(shmsegs, struct shmid_ds, shminfo.shmmni); #endif /* * Determine how many buffers to allocate. - * Use 10% of memory for the first 2 Meg, 5% of the remaining - * memory. Insure a minimum of 16 buffers. + * Use 20% of memory of memory beyond the first 2MB + * Insure a minimum of 16 fs buffers. * We allocate 1/2 as many swap buffer headers as file i/o buffers. */ if (bufpages == 0) - if (physmem < (2 * 1024 * 1024)) - bufpages = physmem / 10 / CLSIZE; - else - bufpages = ((2 * 1024 * 1024 + physmem) / 20) / CLSIZE; + bufpages = (ctob(physmem) - 2048*1024) / NBPG / 5; + if (bufpages < 32) + bufpages = 32; + /* - * 15 Aug 92 William Jolitz bufpages fix for too large + * We must still limit the maximum number of buffers to be no + * more than 2/5's of the size of the kernal malloc region, this + * will only take effect for machines with lots of memory */ - bufpages = min( NKMEMCLUSTERS*2/5, bufpages); - + bufpages = min(bufpages, (VM_KMEM_SIZE / NBPG) * 2 / 5); if (nbuf == 0) { nbuf = bufpages / 2; if (nbuf < 16) @@ -220,11 +242,23 @@ again: */ if ((vm_size_t)(v - firstaddr) != size) panic("startup: table size inconsistency"); + /* * Allocate a submap for buffer space allocations. + * XXX we are NOT using buffer_map, but due to + * the references to it we will just allocate 1 page of + * vm (not real memory) to make things happy... */ buffer_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, - bufpages*NBPG, TRUE); + /* bufpages * */NBPG, TRUE); + /* + * Allocate a submap for exec arguments. This map effectively + * limits the number of processes exec'ing at any time. + */ +/* exec_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, + * 16*NCARGS, TRUE); + * NOT CURRENTLY USED -- cgd + */ /* * Allocate a submap for physio */ @@ -247,7 +281,9 @@ again: for (i = 1; i < ncallout; i++) callout[i-1].c_next = &callout[i]; - /*printf("avail mem = %d\n", ptoa(vm_page_free_count));*/ + printf("avail mem = %d\n", ptoa(vm_page_free_count)); + printf("using %d buffers containing %d bytes of memory\n", + nbuf, bufpages * CLBYTES); /* * Set up CPU-specific registers, cache, etc. @@ -265,6 +301,70 @@ again: configure(); } + +struct cpu_nameclass i386_cpus[] = { + { "Intel 80286", CPUCLASS_286 }, /* CPU_286 */ + { "i386SX", CPUCLASS_386 }, /* CPU_386SX */ + { "i386DX", CPUCLASS_386 }, /* CPU_386 */ + { "i486SX", CPUCLASS_486 }, /* CPU_486SX */ + { "i486DX", CPUCLASS_486 }, /* CPU_486 */ + { "i586", CPUCLASS_586 }, /* CPU_586 */ +}; + +identifycpu() /* translated from hp300 -- cgd */ +{ + printf("CPU: "); + if (cpu >= 0 && cpu < (sizeof i386_cpus/sizeof(struct cpu_nameclass))) { + printf("%s", i386_cpus[cpu].cpu_name); + cpu_class = i386_cpus[cpu].cpu_class; + } else { + printf("unknown cpu type %d\n", cpu); + panic("startup: bad cpu id"); + } + printf(" ("); + switch(cpu_class) { + case CPUCLASS_286: + printf("286"); + break; + case CPUCLASS_386: + printf("386"); + break; + case CPUCLASS_486: + printf("486"); + break; + case CPUCLASS_586: + printf("586"); + break; + default: + printf("unknown"); /* will panic below... */ + } + printf("-class CPU)"); + printf("\n"); /* cpu speed would be nice, but how? */ + + /* + * Now that we have told the user what they have, + * let them know if that machine type isn't configured. + */ + switch (cpu_class) { + case CPUCLASS_286: /* a 286 should not make it this far, anyway */ +#if !defined(I386_CPU) && !defined(I486_CPU) && !defined(I586_CPU) +#error This kernel is not configured for one of the supported CPUs +#endif +#if !defined(I386_CPU) + case CPUCLASS_386: +#endif +#if !defined(I486_CPU) + case CPUCLASS_486: +#endif +#if !defined(I586_CPU) + case CPUCLASS_586: +#endif + panic("CPU class not configured"); + default: + break; + } +} + #ifdef PGINPROF /* * Return the difference (in microseconds) @@ -283,17 +383,6 @@ vmtime(otime, olbolt, oicr) } #endif -struct sigframe { - int sf_signum; - int sf_code; - struct sigcontext *sf_scp; - sig_t sf_handler; - int sf_eax; - int sf_edx; - int sf_ecx; - struct sigcontext sf_sc; -} ; - extern int kstack[]; /* @@ -408,18 +497,19 @@ sendsig(catcher, sig, mask, code) * psl to gain improper priviledges or to cause * a machine fault. */ +struct sigreturn_args { + struct sigcontext *sigcntxp; +}; + sigreturn(p, uap, retval) struct proc *p; - struct args { - struct sigcontext *sigcntxp; - } *uap; + struct sigreturn_args *uap; int *retval; { register struct sigcontext *scp; register struct sigframe *fp; register int *regs = p->p_regs; - /* * (XXX old comment) regs[sESP] points to the return address. * The user scp pointer is above that. @@ -455,16 +545,25 @@ sigreturn(p, uap, retval) return(EJUSTRETURN); } +/* + * a simple function to make the system panic (and dump a vmcore) + * in a predictable fashion + */ +void diediedie() +{ + panic("because you said to!"); +} + int waittime = -1; struct pcb dumppcb; +void boot(arghowto) int arghowto; { register long dummy; /* r12 is reserved */ register int howto; /* r11 == how to boot */ register int devtype; /* r10 == major of root dev */ - extern char *panicstr; extern int cold; int nomsg = 1; @@ -479,12 +578,20 @@ boot(arghowto) waittime = 0; (void) splnet(); + printf("syncing disks... "); /* * Release inodes held by texts before update. */ if (panicstr == 0) vnode_pager_umount(NULL); sync((struct sigcontext *)0); + /* + * Unmount filesystems + */ +#if 0 + if (panicstr == 0) + vfs_unmountall(); +#endif for (iter = 0; iter < 20; iter++) { nbusy = 0; @@ -497,19 +604,22 @@ boot(arghowto) printf("updating disks before rebooting... "); nomsg = 0; } - /* printf("%d ", nbusy); */ + printf("%d ", nbusy); DELAY(40000 * iter); } if (nbusy) - printf(" failed!\n"); - else if (nomsg == 0) - printf("succeded.\n"); + printf("giving up\n"); + else + printf("done\n"); DELAY(10000); /* wait for printf to finish */ } splhigh(); devtype = major(rootdev); if (howto&RB_HALT) { - pg("\nThe operating system has halted. Please press any key to reboot.\n\n"); + printf("\n"); + printf("The operating system has halted.\n"); + printf("Please press any key to reboot.\n\n"); + cngetc(); } else { if (howto & RB_DUMP) { savectx(&dumppcb, 0); @@ -527,13 +637,14 @@ boot(arghowto) /*NOTREACHED*/ } -int dumpmag = 0x8fca0101; /* magic number for savecore */ -int dumpsize = 0; /* also for savecore */ +unsigned dumpmag = 0x8fca0101; /* magic number for savecore */ +int dumpsize = 0; /* also for savecore */ /* * Doadump comes here after turning off memory management and * getting on the dump stack, either when called above, or by * the auto-restart code. */ +void dumpsys() { @@ -541,37 +652,37 @@ dumpsys() return; if ((minor(dumpdev)&07) != 1) return; - printf("\nThe operating system is saving a copy of RAM memory to device %x, offset %d\n\ -(hit any key to abort): [ amount left to save (MB) ] ", dumpdev, dumplo); dumpsize = physmem; + printf("\ndumping to dev %x, offset %d\n", dumpdev, dumplo); + printf("dump "); switch ((*bdevsw[major(dumpdev)].d_dump)(dumpdev)) { case ENXIO: - printf("-- device bad\n"); + printf("device bad\n"); break; case EFAULT: - printf("-- device not ready\n"); + printf("device not ready\n"); break; case EINVAL: - printf("-- area improper\n"); + printf("area improper\n"); break; case EIO: - printf("-- i/o error\n"); + printf("i/o error\n"); break; case EINTR: - printf("-- aborted from console\n"); + printf("aborted from console\n"); break; default: - printf(" succeeded\n"); + printf("succeeded\n"); break; } - printf("system rebooting.\n\n"); - DELAY(10000); + printf("\n\n"); + DELAY(1000); } #ifdef HZ @@ -631,6 +742,7 @@ initcpu() /* * Clear registers on exec */ +void setregs(p, entry) struct proc *p; u_long entry; @@ -641,9 +753,9 @@ setregs(p, entry) p->p_addr->u_pcb.pcb_flags = 0; /* no fp at all */ load_cr0(rcr0() | CR0_TS); /* start emulating */ -#ifdef NPX +#if NNPX > 0 npxinit(__INITIAL_NPXCW__); -#endif +#endif /* NNPX > 0 */ } /* @@ -653,7 +765,7 @@ setregs(p, entry) /* * Initialize segments & interrupt table */ - +#define DESCRIPTOR_SIZE 8 #define GNULL_SEL 0 /* Null Descriptor */ #define GCODE_SEL 1 /* Kernel Code Descriptor */ @@ -664,13 +776,13 @@ setregs(p, entry) #define GPROC0_SEL 6 /* Task state process slot zero and up */ #define NGDT GPROC0_SEL+1 -union descriptor gdt[GPROC0_SEL+1]; +unsigned char gdt[GPROC0_SEL+1][DESCRIPTOR_SIZE]; /* interrupt descriptor table */ struct gate_descriptor idt[NIDT]; /* local descriptor table */ -union descriptor ldt[5]; +unsigned char ldt[5][DESCRIPTOR_SIZE]; #define LSYS5CALLS_SEL 0 /* forced by intel BCS */ #define LSYS5SIGR_SEL 1 @@ -684,15 +796,15 @@ struct i386tss tss, panic_tss; extern struct user *proc0paddr; -/* software prototypes -- in more palitable form */ +/* software prototypes -- in more palatable form */ struct soft_segment_descriptor gdt_segs[] = { /* Null Descriptor */ { 0x0, /* segment base address */ - 0x0, /* length - all address space */ + 0x0, /* length */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ - 0,0, + 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Code Descriptor for kernel */ @@ -701,7 +813,7 @@ struct soft_segment_descriptor gdt_segs[] = { SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ - 0,0, + 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* Data Descriptor for kernel */ @@ -710,7 +822,7 @@ struct soft_segment_descriptor gdt_segs[] = { SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ - 0,0, + 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* LDT Descriptor */ @@ -719,7 +831,7 @@ struct soft_segment_descriptor gdt_segs[] = { SDT_SYSLDT, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ - 0,0, + 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - Placeholder */ @@ -728,7 +840,7 @@ struct soft_segment_descriptor gdt_segs[] = { 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ - 0,0, + 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Panic Tss Descriptor */ @@ -737,7 +849,7 @@ struct soft_segment_descriptor gdt_segs[] = { SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ - 0,0, + 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Proc 0 Tss Descriptor */ @@ -746,7 +858,7 @@ struct soft_segment_descriptor gdt_segs[] = { SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ - 0,0, + 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }}; @@ -757,7 +869,7 @@ struct soft_segment_descriptor ldt_segs[] = { 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ - 0,0, + 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ @@ -766,7 +878,7 @@ struct soft_segment_descriptor ldt_segs[] = { 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ - 0,0, + 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ @@ -775,7 +887,7 @@ struct soft_segment_descriptor ldt_segs[] = { 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ - 0,0, + 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Code Descriptor for user */ @@ -784,7 +896,7 @@ struct soft_segment_descriptor ldt_segs[] = { SDT_MEMERA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ - 0,0, + 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* Data Descriptor for user */ @@ -793,7 +905,7 @@ struct soft_segment_descriptor ldt_segs[] = { SDT_MEMRWA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ - 0,0, + 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ } }; @@ -821,7 +933,7 @@ extern IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl), IDTVEC(rsvd13), IDTVEC(rsvd14), IDTVEC(rsvd14), IDTVEC(syscall); int lcr0(), lcr3(), rcr0(), rcr2(); -int _udatasel, _ucodesel, _gsel_tss; +int _gsel_tss; init386(first) { @@ -831,7 +943,7 @@ init386(first) struct gate_descriptor *gdp; extern int sigcode,szsigcode; /* table descriptors - used to load tables by microp */ - struct region_descriptor r_gdt, r_idt; + unsigned short r_gdt[3], r_idt[3]; int pagesinbase, pagesinext; @@ -841,14 +953,37 @@ init386(first) * Initialize the console before we print anything out. */ - cninit (KERNBASE+0xa0000); + cninit (); - /* make gdt memory segments */ - gdt_segs[GCODE_SEL].ssd_limit = btoc((int) &etext + NBPG); + /* + * make gdt memory segments, the code segment goes up to end of the + * page with etext in it, the data segment goes to the end of + * the address space + */ + gdt_segs[GCODE_SEL].ssd_limit = i386_btop(i386_round_page(&etext)) - 1; + gdt_segs[GDATA_SEL].ssd_limit = 0xffffffff; /* XXX constant? */ for (x=0; x < NGDT; x++) ssdtosd(gdt_segs+x, gdt+x); /* make ldt memory segments */ - ldt_segs[LUCODE_SEL].ssd_limit = btoc(UPT_MIN_ADDRESS); - ldt_segs[LUDATA_SEL].ssd_limit = btoc(UPT_MIN_ADDRESS); + /* + * The data segment limit must not cover the user area because we + * don't want the user area to be writable in copyout() etc. (page + * level protection is lost in kernel mode on 386's). Also, we + * don't want the user area to be writable directly (page level + * protection of the user area is not available on 486's with + * CR0_WP set, because there is no user-read/kernel-write mode). + * + * XXX - VM_MAXUSER_ADDRESS is an end address, not a max. And it + * should be spelled ...MAX_USER... + */ +#define VM_END_USER_RW_ADDRESS VM_MAXUSER_ADDRESS + /* + * The code segment limit has to cover the user area until we move + * the signal trampoline out of the user area. This is safe because + * the code segment cannot be written to directly. + */ +#define VM_END_USER_R_ADDRESS (VM_END_USER_RW_ADDRESS + UPAGES * NBPG) + ldt_segs[LUCODE_SEL].ssd_limit = i386_btop(VM_END_USER_R_ADDRESS) - 1; + ldt_segs[LUDATA_SEL].ssd_limit = i386_btop(VM_END_USER_RW_ADDRESS) - 1; /* Note. eventually want private ldts per process */ for (x=0; x < 5; x++) ssdtosd(ldt_segs+x, ldt+x); @@ -891,11 +1026,13 @@ init386(first) isa_defaultirq(); #endif - r_gdt.rd_limit = sizeof(gdt)-1; - r_gdt.rd_base = (int) gdt; + r_gdt[0] = (unsigned short) (sizeof(gdt) - 1); + r_gdt[1] = (unsigned short) ((int) gdt & 0xffff); + r_gdt[2] = (unsigned short) ((int) gdt >> 16); lgdt(&r_gdt); - r_idt.rd_limit = sizeof(idt)-1; - r_idt.rd_base = (int) idt; + r_idt[0] = (unsigned short) (sizeof(idt) - 1); + r_idt[1] = (unsigned short) ((int) idt & 0xfffff); + r_idt[2] = (unsigned short) ((int) idt >> 16); lidt(&r_idt); lldt(GSEL(GLDT_SEL, SEL_KPL)); @@ -941,7 +1078,8 @@ init386(first) Maxmem = 640/4; else { Maxmem = pagesinext + 0x100000/NBPG; - first = 0x100000; /* skip hole */ + if (first < 0x100000) + first = 0x100000; /* skip hole */ } /* This used to explode, since Maxmem used to be 0 for bas CMOS*/ @@ -957,25 +1095,31 @@ init386(first) * If they get working well enough to recompile, they can unset * the flag; otherwise, it's a toy and they have to lump it. */ - getchar(); /* kernel getchar in /sys/i386/isa/pccons.c*/ + cngetc(); #endif /* !INFORM_WAIT*/ } - /* - * End of CMOS bux fix - */ /* call pmap initialization to make new kernel address space */ +#ifndef MACHINCE_NONCONTIG pmap_bootstrap (first, 0); +#else + pmap_bootstrap ((vm_offset_t)atdevbase + IOM_SIZE); + +#endif /* MACHINE_NONCONTIG */ /* now running on new page tables, configured,and u/iom is accessible */ /* make a initial tss so microp can get interrupt stack on syscall! */ proc0.p_addr->u_pcb.pcb_tss.tss_esp0 = (int) kstack + UPAGES*NBPG; proc0.p_addr->u_pcb.pcb_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL) ; _gsel_tss = GSEL(GPROC0_SEL, SEL_KPL); + + ((struct i386tss *)gdt_segs[GPROC0_SEL].ssd_base)->tss_ioopt = + (sizeof(tss))<<16; + ltr(_gsel_tss); /* make a call gate to reenter kernel with */ - gdp = &ldt[LSYS5CALLS_SEL].gd; + gdp = (struct gate_descriptor *) &ldt[LSYS5CALLS_SEL][0]; x = (int) &IDTVEC(syscall); gdp->gd_looffset = x++; @@ -1008,13 +1152,16 @@ clearseg(n) { *(int *)CMAP2 = PG_V | PG_KW | ctob(n); load_cr3(rcr3()); bzero(CADDR2,NBPG); +#ifndef MACHINE_NONCONTIG *(int *) CADDR2 = 0; +#endif /* MACHINE_NONCONTIG */ } /* * copy a page of physical memory * specified in relocation units (NBPG bytes) */ +void copyseg(frm, n) { *(int *)CMAP2 = PG_V | PG_KW | ctob(n); @@ -1026,6 +1173,7 @@ copyseg(frm, n) { * copy a page of physical memory * specified in relocation units (NBPG bytes) */ +void physcopyseg(frm, to) { *(int *)CMAP1 = PG_V | PG_KW | ctob(frm); @@ -1038,6 +1186,7 @@ physcopyseg(frm, to) { schednetisr(NETISR_AST); }*/ +void setsoftclock() { schednetisr(NETISR_SCLK); } @@ -1067,8 +1216,10 @@ _remque(element) element->ph_rlink = (struct proc *)0; } +#ifdef SLOW_OLD_COPYSTRS vmunaccess() {} +#if 0 /* assembler versions now in locore.s */ /* * Below written in C to allow access to debugging code */ @@ -1113,6 +1264,8 @@ copyoutstr(fromaddr, toaddr, maxlength, lencopied) u_int *lencopied, maxlength; return(ENAMETOOLONG); } +#endif /* SLOW_OLD_COPYSTRS */ + copystr(fromaddr, toaddr, maxlength, lencopied) u_int *lencopied, maxlength; void *fromaddr, *toaddr; { u_int tally; @@ -1129,3 +1282,4 @@ copystr(fromaddr, toaddr, maxlength, lencopied) u_int *lencopied, maxlength; if(lencopied) *lencopied = tally; return(ENAMETOOLONG); } +#endif diff --git a/sys/i386/i386/math_emu.h b/sys/i386/i386/math_emu.h index 537fba5641..453a903554 100644 --- a/sys/i386/i386/math_emu.h +++ b/sys/i386/i386/math_emu.h @@ -2,6 +2,8 @@ * linux/include/linux/math_emu.h * * (C) 1991 Linus Torvalds + * + * $Id$ */ #ifndef _LINUX_MATH_EMU_H #define _LINUX_MATH_EMU_H diff --git a/sys/i386/i386/math_emulate.c b/sys/i386/i386/math_emulate.c index faf55a4521..f7fa88ed94 100644 --- a/sys/i386/i386/math_emulate.c +++ b/sys/i386/i386/math_emulate.c @@ -1,8 +1,12 @@ -/* [expediant "port" of linux 8087 emulator to 386BSD, with apologies -wfj] */ /* * linux/kernel/math/math_emulate.c * * (C) 1991 Linus Torvalds + * + * [expediant "port" of linux 8087 emulator to 386BSD, with apologies -wfj] + * + * from: 386BSD 0.1 + * $Id$ */ /* @@ -30,15 +34,6 @@ * to 80-bit temporary reals, and do with them as they please. I wanted to * hide most of the 387-specific things here. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00060 - * -------------------- ----- ---------------------- - * - * 19 Sep 92 Ishii Masahiro Fix 0x1fd instruction - * kym@bingsuns.cc.binghamton.edu Fix fscale - * 28 Nov 92 Poul-Henning Kamp Reduce kernel size if you have - * a 387 or 486 chip */ #include "machine/cpu.h" @@ -78,10 +73,6 @@ put_fs_long(unsigned long val, unsigned long *adr) { (void)suword(adr,val); } math_emulate(struct trapframe * info) { -#if defined(i486) || defined(i387) - panic("math_emulate(), shouldn't happen with -Di486 or -Di387"); -} -#else unsigned short code; temp_real tmp; char * address; @@ -625,7 +616,7 @@ char * ea(struct trapframe * info, unsigned short code) I387.fos = 0x17; return (char *) offset; } - tmp = & (long)REG(rm); + tmp = (long *) ®(rm); switch (mod) { case 0: offset = 0; break; case 1: @@ -964,8 +955,6 @@ void fmul(const temp_real * src1, const temp_real * src2, temp_real * result) * temporary real division routine. */ -#include "i386/i386/math_emu.h" - static void shift_left(int * c) { __asm__ __volatile__("movl (%0),%%eax ; addl %%eax,(%0)\n\t" @@ -1484,4 +1473,3 @@ void int_to_real(const temp_int * a, temp_real * b) :"0" (b->a),"1" (b->b)); } } -#endif /* defined(i486) || defined(i387) */ diff --git a/sys/i386/i386/mem.c b/sys/i386/i386/mem.c index 650e21eb50..51c62cacf1 100644 --- a/sys/i386/i386/mem.c +++ b/sys/i386/i386/mem.c @@ -36,8 +36,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * from: Utah $Hdr: mem.c 1.13 89/10/08$ - * @(#)mem.c 7.2 (Berkeley) 5/9/91 + * from: Utah $Hdr: mem.c 1.13 89/10/08$ + * from: @(#)mem.c 7.2 (Berkeley) 5/9/91 + * $Id$ */ /* @@ -50,8 +51,10 @@ #include "systm.h" #include "uio.h" #include "malloc.h" +#include "proc.h" #include "machine/cpu.h" +#include "machine/psl.h" #include "vm/vm_param.h" #include "vm/lock.h" @@ -61,6 +64,42 @@ extern char *vmmap; /* poor name! */ /*ARGSUSED*/ +mmclose(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + struct syscframe *fp; + + switch (minor(dev)) { + case 14: + fp = (struct syscframe *)curproc->p_regs; + fp->sf_eflags &= ~PSL_IOPL; + break; + default: + break; + } + return(0); +} +/*ARGSUSED*/ +mmopen(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + struct syscframe *fp; + + switch (minor(dev)) { + case 14: + fp = (struct syscframe *)curproc->p_regs; + fp->sf_eflags |= PSL_IOPL; + break; + default: + break; + } + return(0); +} +/*ARGSUSED*/ mmrw(dev, uio, flags) dev_t dev; struct uio *uio; diff --git a/sys/i386/i386/microtime.s b/sys/i386/i386/microtime.s index 4af938b917..99f8601ed9 100644 --- a/sys/i386/i386/microtime.s +++ b/sys/i386/i386/microtime.s @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Header$ + * from: Steve McCanne's microtime code + * $Id$ */ #include "asm.h" diff --git a/sys/i386/i386/ns_cksum.c b/sys/i386/i386/ns_cksum.c new file mode 100644 index 0000000000..1473f85550 --- /dev/null +++ b/sys/i386/i386/ns_cksum.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 1982, 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)ns_cksum.c 7.7 (Berkeley) 4/29/91 + * from NetBSD: ns_cksum.c,v 1.2 1993/05/22 07:59:55 cgd Exp + * $Id$ + */ + +#include "sys/param.h" +#include "sys/mbuf.h" + +/* + * Checksum routine for Network Systems Protocol Packets (Big-Endian). + * + * This routine is very heavily used in the network + * code and should be modified for each CPU to be as fast as possible. + */ + +#define ADDCARRY(x) { if ((x) > 65535) (x) -= 65535; } +#define FOLD(x) {l_util.l = (x); (x) = l_util.s[0] + l_util.s[1]; ADDCARRY(x);} + +u_short +ns_cksum(m, len) + register struct mbuf *m; + register int len; +{ + register u_short *w; + register int sum = 0; + register int mlen = 0; + register int sum2; + + union { + u_short s[2]; + long l; + } l_util; + + for (;m && len; m = m->m_next) { + if (m->m_len == 0) + continue; + /* + * Each trip around loop adds in + * word from one mbuf segment. + */ + w = mtod(m, u_short *); + if (mlen == -1) { + /* + * There is a byte left from the last segment; + * ones-complement add it into the checksum. + */ +#if BYTE_ORDER == BIG_ENDIAN + sum += *(u_char *)w; +#else + sum += *(u_char *)w << 8; +#endif + sum += sum; + w = (u_short *)(1 + (char *)w); + mlen = m->m_len - 1; + len--; + FOLD(sum); + } else + mlen = m->m_len; + if (len < mlen) + mlen = len; + len -= mlen; + /* + * We can do a 16 bit ones complement sum using + * 32 bit arithmetic registers for adding, + * with carries from the low added + * into the high (by normal carry-chaining) + * so long as we fold back before 16 carries have occured. + */ + if (1 & (int) w) + goto uuuuglyy; +#ifndef TINY +/* -DTINY reduces the size from 1250 to 550, but slows it down by 22% */ + while ((mlen -= 32) >= 0) { + sum += w[0]; sum += sum; sum += w[1]; sum += sum; + sum += w[2]; sum += sum; sum += w[3]; sum += sum; + sum += w[4]; sum += sum; sum += w[5]; sum += sum; + sum += w[6]; sum += sum; sum += w[7]; sum += sum; + FOLD(sum); + sum += w[8]; sum += sum; sum += w[9]; sum += sum; + sum += w[10]; sum += sum; sum += w[11]; sum += sum; + sum += w[12]; sum += sum; sum += w[13]; sum += sum; + sum += w[14]; sum += sum; sum += w[15]; sum += sum; + FOLD(sum); + w += 16; + } + mlen += 32; +#endif + while ((mlen -= 8) >= 0) { + sum += w[0]; sum += sum; sum += w[1]; sum += sum; + sum += w[2]; sum += sum; sum += w[3]; sum += sum; + FOLD(sum); + w += 4; + } + mlen += 8; + while ((mlen -= 2) >= 0) { + sum += *w++; sum += sum; + } + goto commoncase; +uuuuglyy: +#if BYTE_ORDER == BIG_ENDIAN +#define ww(n) (((u_char *)w)[n + n + 1]) +#define vv(n) (((u_char *)w)[n + n]) +#else +#if BYTE_ORDER == LITTLE_ENDIAN +#define vv(n) (((u_char *)w)[n + n + 1]) +#define ww(n) (((u_char *)w)[n + n]) +#endif +#endif + sum2 = 0; +#ifndef TINY + while ((mlen -= 32) >= 0) { + sum += ww(0); sum += sum; sum += ww(1); sum += sum; + sum += ww(2); sum += sum; sum += ww(3); sum += sum; + sum += ww(4); sum += sum; sum += ww(5); sum += sum; + sum += ww(6); sum += sum; sum += ww(7); sum += sum; + FOLD(sum); + sum += ww(8); sum += sum; sum += ww(9); sum += sum; + sum += ww(10); sum += sum; sum += ww(11); sum += sum; + sum += ww(12); sum += sum; sum += ww(13); sum += sum; + sum += ww(14); sum += sum; sum += ww(15); sum += sum; + FOLD(sum); + sum2 += vv(0); sum2 += sum2; sum2 += vv(1); sum2 += sum2; + sum2 += vv(2); sum2 += sum2; sum2 += vv(3); sum2 += sum2; + sum2 += vv(4); sum2 += sum2; sum2 += vv(5); sum2 += sum2; + sum2 += vv(6); sum2 += sum2; sum2 += vv(7); sum2 += sum2; + FOLD(sum2); + sum2 += vv(8); sum2 += sum2; sum2 += vv(9); sum2 += sum2; + sum2 += vv(10); sum2 += sum2; sum2 += vv(11); sum2 += sum2; + sum2 += vv(12); sum2 += sum2; sum2 += vv(13); sum2 += sum2; + sum2 += vv(14); sum2 += sum2; sum2 += vv(15); sum2 += sum2; + FOLD(sum2); + w += 16; + } + mlen += 32; +#endif + while ((mlen -= 8) >= 0) { + sum += ww(0); sum += sum; sum += ww(1); sum += sum; + sum += ww(2); sum += sum; sum += ww(3); sum += sum; + FOLD(sum); + sum2 += vv(0); sum2 += sum2; sum2 += vv(1); sum2 += sum2; + sum2 += vv(2); sum2 += sum2; sum2 += vv(3); sum2 += sum2; + FOLD(sum2); + w += 4; + } + mlen += 8; + while ((mlen -= 2) >= 0) { + sum += ww(0); sum += sum; + sum2 += vv(0); sum2 += sum2; + w++; + } + sum += (sum2 << 8); +commoncase: + if (mlen == -1) { +#if BYTE_ORDER == BIG_ENDIAN + sum += *(u_char *)w << 8; +#else + sum += *(u_char *)w; +#endif + } + FOLD(sum); + } + if (mlen == -1) { + /* We had an odd number of bytes to sum; assume a garbage + byte of zero and clean up */ + sum += sum; + FOLD(sum); + } + /* + * sum has already been kept to low sixteen bits. + * just examine result and exit. + */ + if(sum==0xffff) sum = 0; + return (sum); +} diff --git a/sys/i386/i386/pmap.c b/sys/i386/i386/pmap.c index 66c7fec59a..f35dd0f70d 100644 --- a/sys/i386/i386/pmap.c +++ b/sys/i386/i386/pmap.c @@ -34,16 +34,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pmap.c 7.7 (Berkeley) 5/12/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00063 - * -------------------- ----- ---------------------- - * - * 28 Nov 1991 Poul-Henning Kamp Speedup processing. + * from: @(#)pmap.c 7.7 (Berkeley) 5/12/91 + * $Id: pmap.c,v 1.6 1993/10/12 15:09:37 rgrimes Exp $ */ -static char rcsid[] = "$Header: /usr/src/sys.386bsd/i386/i386/RCS/pmap.c,v 1.3 92/01/21 14:26:44 william Exp Locker: root $"; /* * Derived from hp300 version by Mike Hibler, this version by William @@ -185,7 +178,7 @@ vm_offset_t virtual_avail; /* VA of first avail page (after kernel bss)*/ vm_offset_t virtual_end; /* VA of last avail page (end of kernel AS) */ vm_offset_t vm_first_phys; /* PA of first managed page */ vm_offset_t vm_last_phys; /* PA just past last managed page */ -int i386pagesperpage; /* PAGE_SIZE / I386_PAGE_SIZE */ +int i386pagesperpage; /* PAGE_SIZE / NBPG */ boolean_t pmap_initialized = FALSE; /* Has pmap_init completed? */ char *pmap_attributes; /* reference and modify bits */ @@ -212,7 +205,7 @@ struct msgbuf *msgbufp; * and just syncs the pmap module with what has already been done. * [We can't call it easily with mapping off since the kernel is not * mapped with PA == VA, hence we would have to relocate every address - * from the linked base (virtual) address 0xFE000000 to the actual + * from the linked base (virtual) address KERNBASE to the actual * (physical) address starting relative to 0] */ struct pte *pmap_pte(); @@ -229,7 +222,7 @@ pmap_bootstrap(firstaddr, loadaddr) extern vm_offset_t maxmem, physmem; extern int IdlePTD; - avail_start = firstaddr; + avail_start = firstaddr + 8 * NBPG; avail_end = maxmem << PG_SHIFT; /* XXX: allow for msgbuf */ @@ -238,7 +231,7 @@ extern int IdlePTD; mem_size = physmem << PG_SHIFT; virtual_avail = (vm_offset_t)atdevbase + 0x100000 - 0xa0000 + 10*NBPG; virtual_end = VM_MAX_KERNEL_ADDRESS; - i386pagesperpage = PAGE_SIZE / I386_PAGE_SIZE; + i386pagesperpage = PAGE_SIZE / NBPG; /* * Initialize protection array. @@ -256,6 +249,8 @@ extern int IdlePTD; /* * Create Kernel page directory table and page maps. * [ currently done in locore. i have wild and crazy ideas -wfj ] + * XXX IF THIS IS EVER USED, IT MUST BE MOVED TO THE TOP + * OF THIS ROUTINE -- cgd */ bzero(firstaddr, 4*NBPG); kernel_pmap->pm_pdir = firstaddr + VM_MIN_KERNEL_ADDRESS; @@ -269,7 +264,7 @@ extern int IdlePTD; *(int *)pde = firstaddr + x*NBPG | PG_V | PG_KW; } #else - kernel_pmap->pm_pdir = (pd_entry_t *)(0xfe000000 + IdlePTD); + kernel_pmap->pm_pdir = (pd_entry_t *)(KERNBASE + IdlePTD); #endif @@ -281,7 +276,7 @@ extern int IdlePTD; * Allocate all the submaps we need */ #define SYSMAP(c, p, v, n) \ - v = (c)va; va += ((n)*I386_PAGE_SIZE); p = pte; pte += (n); + v = (c)va; va += ((n)*NBPG); p = pte; pte += (n); va = virtual_avail; pte = pmap_pte(kernel_pmap, va); @@ -294,15 +289,16 @@ extern int IdlePTD; #endif /* * reserve special hunk of memory for use by bus dma as a bounce - * buffer (contiguous virtual *and* physical memory). for now, - * assume vm does not use memory beneath hole, and we know that - * the bootstrap uses top 32k of base memory. -wfj + * buffer (contiguous virtual *and* physical memory). + * do it from firstaddr -> firstaddr+8 pages. note that + * avail_start was bumped up 8 pages, above, to accomodate this. */ { extern vm_offset_t isaphysmem; - isaphysmem = va; - virtual_avail = pmap_map(va, 0xa0000 - 32*1024, 0xa0000, VM_PROT_ALL); + isaphysmem = va; + virtual_avail = pmap_map(va, firstaddr, firstaddr + 8*NBPG, + VM_PROT_ALL); } *(int *)PTD = 0; @@ -336,7 +332,7 @@ pmap_init(phys_start, phys_end) (void) vm_map_find(kernel_map, NULL, (vm_offset_t) 0, &addr, (0x100000-0xa0000), FALSE); - addr = (vm_offset_t) 0xfe000000+KPTphys/* *NBPG */; + addr = (vm_offset_t) KERNBASE + KPTphys/* *NBPG */; vm_object_reference(kernel_object); (void) vm_map_find(kernel_map, kernel_object, addr, &addr, 2*NBPG, FALSE); @@ -456,12 +452,11 @@ pmap_pinit(pmap) pmap->pm_pdir = (pd_entry_t *) kmem_alloc(kernel_map, NBPG); /* wire in kernel global address entries */ - bcopy(PTD+KPTDI_FIRST, pmap->pm_pdir+KPTDI_FIRST, - (KPTDI_LAST-KPTDI_FIRST+1)*4); + bcopy(PTD+KPTDI, pmap->pm_pdir+KPTDI, NKPDE*4); /* install self-referential address mapping entry */ *(int *)(pmap->pm_pdir+PTDPTDI) = - (int)pmap_extract(kernel_pmap, pmap->pm_pdir) | PG_V | PG_URKW; + (int)pmap_extract(kernel_pmap, pmap->pm_pdir) | PG_V | PG_KW; pmap->pm_count = 1; simple_lock_init(&pmap->pm_lock); @@ -643,7 +638,7 @@ pmap_remove(pmap, sva, eva) do { bits |= *(int *)pte & (PG_U|PG_M); *(int *)pte++ = 0; - /*TBIS(va + ix * I386_PAGE_SIZE);*/ + /*TBIS(va + ix * NBPG);*/ } while (++ix != i386pagesperpage); if (curproc && pmap == &curproc->p_vmspace->vm_pmap) pmap_activate(pmap, (struct pcb *)curproc->p_addr); @@ -846,7 +841,7 @@ pmap_protect(pmap, sva, eva, prot) do { /* clear VAC here if PG_RO? */ pmap_pte_set_prot(pte++, i386prot); - /*TBIS(va + ix * I386_PAGE_SIZE);*/ + /*TBIS(va + ix * NBPG);*/ } while (++ix != i386pagesperpage); } if (curproc && pmap == &curproc->p_vmspace->vm_pmap) @@ -901,7 +896,8 @@ pmap_enter(pmap, va, pa, prot, wired) * Page Directory table entry not valid, we need a new PT page */ if (!pmap_pde_v(pmap_pde(pmap, va))) { - pg("ptdi %x", pmap->pm_pdir[PTDPTDI]); + printf("ptdi %x\n", pmap->pm_pdir[PTDPTDI]); + panic("Page Table Directory Invalid (ptdi)"); } pte = pmap_pte(pmap, va); @@ -1050,13 +1046,10 @@ validate: do { *(int *)pte++ = npte; /*TBIS(va);*/ - npte += I386_PAGE_SIZE; - va += I386_PAGE_SIZE; + npte += NBPG; + va += NBPG; } while (++ix != i386pagesperpage); pte--; -#ifdef DEBUGx -cache, tlb flushes -#endif /*pads(pmap);*/ /*load_cr3(((struct pcb *)curproc->p_addr)->pcb_ptd);*/ tlbflush(); @@ -1162,6 +1155,7 @@ struct pte *pmap_pte(pmap, va) if (pmapdebug & PDB_FOLLOW) printf("pmap_pte(%x, %x) ->\n", pmap, va); #endif + if (pmap && pmap_pde_v(pmap_pde(pmap, va))) { /* are we current address space or kernel? */ @@ -1642,7 +1636,7 @@ pmap_changebit(pa, bit, setem) *pte = npte; /*TBIS(va);*/ } - va += I386_PAGE_SIZE; + va += NBPG; pte++; } while (++ix != i386pagesperpage); @@ -1714,8 +1708,8 @@ pads(pm) pmap_t pm; { for (i = 0; i < 1024; i++) if(pm->pm_pdir[i].pd_v) for (j = 0; j < 1024 ; j++) { - va = (i<<22)+(j<<12); - if (pm == kernel_pmap && va < 0xfe000000) + va = (i< UPT_MAX_ADDRESS) continue; diff --git a/sys/i386/i386/swapgeneric.c b/sys/i386/i386/swapgeneric.c index 0d76e7280c..8aa78a6676 100644 --- a/sys/i386/i386/swapgeneric.c +++ b/sys/i386/i386/swapgeneric.c @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)swapgeneric.c 5.5 (Berkeley) 5/9/91 + * from: @(#)swapgeneric.c 5.5 (Berkeley) 5/9/91 + * $Id$ */ #include "machine/pte.h" diff --git a/sys/i386/i386/symbols.raw b/sys/i386/i386/symbols.raw index 56e5ab4bc5..0f5bafd838 100644 --- a/sys/i386/i386/symbols.raw +++ b/sys/i386/i386/symbols.raw @@ -1,12 +1,6 @@ # @(#)symbols.raw 7.6 (Berkeley) 5/8/91 # -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 1 00066 -# -------------------- ----- ---------------------- -# -# 28 Nov 1991 Warren Toomey Let symorder -t keep some needed -# symbols used by common programs +# $Id$ # @@ -73,6 +67,7 @@ _filehead _nfiles _rthashsize + _radix_node_head #routed _ifnet #rwho diff --git a/sys/i386/i386/sys_machdep.c b/sys/i386/i386/sys_machdep.c index 10b9ac274b..2b63cee6fb 100644 --- a/sys/i386/i386/sys_machdep.c +++ b/sys/i386/i386/sys_machdep.c @@ -1,4 +1,3 @@ - /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. @@ -34,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)sys_machdep.c 5.5 (Berkeley) 1/19/91 + * from: @(#)sys_machdep.c 5.5 (Berkeley) 1/19/91 + * $Id$ */ #include "param.h" @@ -52,12 +52,14 @@ #ifdef TRACE int nvualarm; +struct vtrace_args { + int request; + int value; +}; + vtrace(p, uap, retval) struct proc *p; - register struct args { - int request; - int value; - } *uap; + register struct vtrace_args *uap; int *retval; { int vdoualarm(); diff --git a/sys/i386/i386/trap.c b/sys/i386/i386/trap.c index 57195f32cb..989a73273a 100644 --- a/sys/i386/i386/trap.c +++ b/sys/i386/i386/trap.c @@ -33,22 +33,15 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)trap.c 7.4 (Berkeley) 5/13/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00137 - * -------------------- ----- ---------------------- - * - * 08 Apr 93 Bruce Evans Several VM system fixes - * Paul Kranenburg Add counter for vmstat + * from: @(#)trap.c 7.4 (Berkeley) 5/13/91 + * $Id$ */ -static char rcsid[] = "$Header: /usr/bill/working/sys/i386/i386/RCS/trap.c,v 1.2 92/01/21 14:22:13 william Exp $"; /* * 386 Trap and System call handleing */ +#include "npx.h" #include "machine/cpu.h" #include "machine/psl.h" #include "machine/reg.h" @@ -70,6 +63,21 @@ static char rcsid[] = "$Header: /usr/bill/working/sys/i386/i386/RCS/trap.c,v 1.2 #include "machine/trap.h" +#ifdef __GNUC__ + +/* + * The "r" contraint could be "rm" except for fatal bugs in gas. As usual, + * we omit the size from the mov instruction to avoid nonfatal bugs in gas. + */ +#define read_gs() ({ u_short gs; __asm("mov %%gs,%0" : "=r" (gs)); gs; }) +#define write_gs(gs) __asm("mov %0,%%gs" : : "r" ((u_short) gs)) + +#else /* not __GNUC__ */ + +u_short read_gs __P((void)); +void write_gs __P((/* promoted u_short */ int gs)); + +#endif /* __GNUC__ */ struct sysent sysent[]; int nsysent; @@ -112,9 +120,25 @@ trap(frame) frame.tf_trapno, frame.tf_err, frame.tf_eip, frame.tf_cs, rcr2(), frame.tf_esp);*/ if(curpcb == 0 || curproc == 0) goto we_re_toast; - if (curpcb->pcb_onfault && frame.tf_trapno != 0xc) { + if (curpcb->pcb_onfault && frame.tf_trapno != T_PAGEFLT) { + extern int _udatasel; + + if (read_gs() != (u_short) _udatasel) + /* + * Some user has corrupted %gs but we depend on it in + * copyout() etc. Fix it up and retry. + * + * (We don't preserve %fs or %gs, so users can change + * them to either _ucodesel, _udatasel or a not-present + * selector, possibly ORed with 0 to 3, making them + * volatile for other users. Not preserving them saves + * time and doesn't lose functionality or open security + * holes.) + */ + write_gs(_udatasel); + else copyfault: - frame.tf_eip = (int)curpcb->pcb_onfault; + frame.tf_eip = (int)curpcb->pcb_onfault; return; } @@ -175,12 +199,16 @@ copyfault: goto out; case T_DNA|T_USER: -#ifdef NPX +#if NNPX > 0 /* if a transparent fault (due to context switch "late") */ if (npxdna()) return; -#endif +#endif /* NNPX > 0 */ +#ifdef MATH_EMULATE i = math_emulate(&frame); if (i == 0) return; +#else /* MATH_EMULTATE */ + panic("trap: math emulation necessary!"); +#endif /* MATH_EMULTATE */ ucode = FPE_FPU_NP_TRAP; break; @@ -222,27 +250,6 @@ copyfault: unsigned nss,v; va = trunc_page((vm_offset_t)eva); - /* - * Avoid even looking at pde_v(va) for high va's. va's - * above VM_MAX_KERNEL_ADDRESS don't correspond to normal - * PDE's (half of them correspond to APDEpde and half to - * an unmapped kernel PDE). va's betweeen 0xFEC00000 and - * VM_MAX_KERNEL_ADDRESS correspond to unmapped kernel PDE's - * (XXX - why are only 3 initialized when 6 are required to - * reach VM_MAX_KERNEL_ADDRESS?). Faulting in an unmapped - * kernel page table would give inconsistent PTD's. - * - * XXX - faulting in unmapped page tables wastes a page if - * va turns out to be invalid. - * - * XXX - should "kernel address space" cover the kernel page - * tables? Might have same problem with PDEpde as with - * APDEpde (or there may be no problem with APDEpde). - */ - if (va > 0xFEBFF000) { - rv = KERN_FAILURE; /* becomes SIGBUS */ - goto nogo; - } /* * It is only a kernel address space fault iff: * 1. (type & T_USER) == 0 and @@ -396,18 +403,49 @@ out: } /* - * Compensate for 386 brain damage (missing URKR) + * Compensate for 386 brain damage (missing URKR). + * This is a little simpler than the pagefault handler in trap() because + * it the page tables have already been faulted in and high addresses + * are thrown out early for other reasons. */ -int trapwrite(unsigned addr) { - int rv; +int trapwrite(addr) + unsigned addr; +{ + unsigned nss; + struct proc *p; vm_offset_t va; + struct vmspace *vm; va = trunc_page((vm_offset_t)addr); - if (va > VM_MAXUSER_ADDRESS) return(1); - rv = vm_fault(&curproc->p_vmspace->vm_map, va, - VM_PROT_READ | VM_PROT_WRITE, FALSE); - if (rv == KERN_SUCCESS) return(0); - else return(1); + /* + * XXX - MAX is END. Changed > to >= for temp. fix. + */ + if (va >= VM_MAXUSER_ADDRESS) + return (1); + /* + * XXX: rude stack hack adapted from trap(). + */ + nss = 0; + p = curproc; + vm = p->p_vmspace; + if ((caddr_t)va >= vm->vm_maxsaddr && dostacklimits) { + nss = clrnd(btoc((unsigned)vm->vm_maxsaddr + MAXSSIZ + - (unsigned)va)); + if (nss > btoc(p->p_rlimit[RLIMIT_STACK].rlim_cur)) + return (1); + } + + if (vm_fault(&vm->vm_map, va, VM_PROT_READ | VM_PROT_WRITE, FALSE) + != KERN_SUCCESS) + return (1); + + /* + * XXX: continuation of rude stack hack + */ + if (nss > vm->vm_ssize) + vm->vm_ssize = nss; + + return (0); } /* diff --git a/sys/i386/i386/vm_machdep.c b/sys/i386/i386/vm_machdep.c index 27ef912e85..38077213ed 100644 --- a/sys/i386/i386/vm_machdep.c +++ b/sys/i386/i386/vm_machdep.c @@ -35,22 +35,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_machdep.c 7.3 (Berkeley) 5/13/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00154 - * -------------------- ----- ---------------------- - * - * 20 Apr 93 Bruce Evans New npx-0.5 code - * - */ - -/* + * from: @(#)vm_machdep.c 7.3 (Berkeley) 5/13/91 * Utah $Hdr: vm_machdep.c 1.16.1.1 89/06/23$ + * $Id$ */ -static char rcsid[] = "$Header: /usr/bill/working/sys/i386/i386/RCS/vm_machdep.c,v 1.2 92/01/21 14:22:17 william Exp $"; +#include "npx.h" #include "param.h" #include "systm.h" #include "proc.h" @@ -105,8 +95,15 @@ cpu_fork(p1, p2) vm_map_pageable(&p2->p_vmspace->vm_map, addr, addr+NBPG, FALSE); for (i=0; i < UPAGES; i++) pmap_enter(&p2->p_vmspace->vm_pmap, kstack+i*NBPG, - pmap_extract(kernel_pmap, ((int)p2->p_addr)+i*NBPG), VM_PROT_READ, 1); - + pmap_extract(kernel_pmap, ((int)p2->p_addr)+i*NBPG), + /* + * The user area has to be mapped writable because + * it contains the kernel stack (when CR0_WP is on + * on a 486 there is no user-read/kernel-write + * mode). It is protected from user mode access + * by the segment limits. + */ + VM_PROT_READ|VM_PROT_WRITE, TRUE); pmap_activate(&p2->p_vmspace->vm_pmap, &up->u_pcb); /* @@ -139,14 +136,15 @@ cpu_fork(p1, p2) * a special case]. */ struct proc *swtch_to_inactive(); +volatile void cpu_exit(p) register struct proc *p; { static struct pcb nullpcb; /* pcb to overwrite on last swtch */ -#ifdef NPX +#if NNPX > 0 npxexit(p); -#endif +#endif /* NNPX */ /* move to inactive space and stack, passing arg accross */ p = swtch_to_inactive(p); @@ -161,15 +159,22 @@ cpu_exit(p) /* NOTREACHED */ } #else +void cpu_exit(p) register struct proc *p; { -#ifdef NPX +#if NNPX > 0 npxexit(p); -#endif +#endif /* NNPX */ splclock(); swtch(); + /* + * This is to shutup the compiler, and if swtch() failed I suppose + * this would be a good thing. This keeps gcc happy because panic + * is a volatile void function as well. + */ + panic("cpu_exit"); } cpu_wait(p) struct proc *p; { @@ -293,8 +298,8 @@ kernacc(addr, count, rw) for (pde += ix; cnt; cnt--, pde++) if (pde->pd_v == 0) return(0); - ix = btop(addr-0xfe000000); - cnt = btop(addr-0xfe000000+count+NBPG-1); + ix = btop(addr-KERNBASE); + cnt = btop(addr-KERNBASE+count+NBPG-1); if (cnt > (int)&Syssize) return(0); cnt -= ix; diff --git a/sys/i386/include/ansi.h b/sys/i386/include/ansi.h index ca677487ee..cdb3af871b 100644 --- a/sys/i386/include/ansi.h +++ b/sys/i386/include/ansi.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ansi.h 7.1 (Berkeley) 3/9/91 + * from: @(#)ansi.h 7.1 (Berkeley) 3/9/91 + * $Id$ */ #ifndef _ANSI_H_ diff --git a/sys/i386/include/console.h b/sys/i386/include/console.h new file mode 100644 index 0000000000..23cdb50d3f --- /dev/null +++ b/sys/i386/include/console.h @@ -0,0 +1,205 @@ +/* + * Copyright (C) 1992, 1993 Søren Schmidt + * + * This program is free software; you may redistribute it and/or + * modify it, provided that it retain the above copyright notice + * and the following disclaimer. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Søren Schmidt Email: sos@kmd-ac.dk + * Tritonvej 36 UUCP: ...uunet!dkuug!kmd-ac!sos + * DK9210 Aalborg SO Phone: +45 9814 8076 + * + * $Id$ + */ + +#ifndef _CONSOLE_H_ +#define _CONSOLE_H_ + +#include +#include + +#define KDGKBMODE _IOR('K', 6, int) +#define KDSKBMODE _IO('K', 7) +#define KDMKTONE _IO('K', 8) +#define KDGETMODE _IOR('K', 9, int) +#define KDSETMODE _IO('K', 10) +#define KDSBORDER _IO('K', 13) +#define KDGKBSTATE _IOR('K', 19, int) +#define KDSKBSTATE _IO('K', 20) +#define KDENABIO _IO('K', 60) +#define KDDISABIO _IO('K', 61) +#define KIOCSOUND _IO('k', 63) +#define KDGKBTYPE _IOR('K', 64, int) +#define KDGETLED _IOR('K', 65, int) +#define KDSETLED _IO('K', 66) +#define KDSETRAD _IO('K', 67) + +#define GETFKEY _IOWR('k', 0, fkeyarg_t) +#define SETFKEY _IOWR('k', 1, fkeyarg_t) +#define GIO_SCRNMAP _IOR('k', 2, scrmap_t) +#define PIO_SCRNMAP _IOW('k', 3, scrmap_t) +#define GIO_KEYMAP _IOR('k', 6, keymap_t) +#define PIO_KEYMAP _IOW('k', 7, keymap_t) + +#define CONS_BLANKTIME _IOW('c', 4, long) +#define PIO_FONT8x8 _IOW('c', 64, fnt8_t) +#define GIO_FONT8x8 _IOR('c', 65, fnt8_t) +#define PIO_FONT8x14 _IOW('c', 66, fnt14_t) +#define GIO_FONT8x14 _IOR('c', 67, fnt14_t) +#define PIO_FONT8x16 _IOW('c', 68, fnt16_t) +#define GIO_FONT8x16 _IOR('c', 69, fnt16_t) +#define CONS_GETINFO _IOR('c', 73, vid_info_t) +#define CONS_GETVERS _IOR('c', 74, long) +#define CONS_80x25TEXT _IO('c', 102) +#define CONS_80x50TEXT _IO('c', 103) + +#define VT_OPENQRY _IOR('v', 1, int) +#define VT_SETMODE _IOW('v', 2, vtmode_t) +#define VT_GETMODE _IOR('v', 3, vtmode_t) +#define VT_RELDISP _IO('v', 4) +#define VT_ACTIVATE _IO('v', 5) +#define VT_WAITACTIVE _IO('v', 6) +#define VT_GETACTIVE _IOR('v', 7, int) + +#define VT_FALSE 0 +#define VT_TRUE 1 +#define VT_ACKACQ 2 + +#define VT_AUTO 0 /* switching is automatic */ +#define VT_PROCESS 1 /* switching controlled by prog */ + +/* compatibility to old pccons & X386 */ +#define CONSOLE_X_MODE_ON _IO('t', 121) +#define CONSOLE_X_MODE_OFF _IO('t', 122) +#define CONSOLE_X_BELL _IOW('t',123,int[2]) + +struct vt_mode { + char mode; + char waitv; /* not implemented yet SOS */ + short relsig; + short acqsig; + short frsig; /* not implemented yet SOS */ +}; + +typedef struct vt_mode vtmode_t; + +#define KD_MONO 1 /* monochrome adapter */ +#define KD_HERCULES 2 /* hercules adapter */ +#define KD_CGA 3 /* color graphics adapter */ +#define KD_EGA 4 /* enhanced graphics adapter */ +#define KD_VGA 5 /* video graohics adapter */ + +#define KD_TEXT 0 /* set text mode restore fonts */ +#define KD_TEXT0 0 /* ditto */ +#define KD_TEXT1 2 /* set text mode !restore fonts */ +#define KD_GRAPHICS 1 /* set graphics mode */ + +#define K_RAW 0 /* keyboard returns scancodes */ +#define K_XLATE 1 /* keyboard returns ascii */ + +#define KB_84 1 +#define KB_101 2 +#define KB_OTHER 3 + +#define CLKED 1 +#define NLKED 2 +#define SLKED 4 +#define ALKED 8 +#define LED_CAP 1 +#define LED_NUM 2 +#define LED_SCR 4 + +/* possible flag values */ +#define FLAG_LOCK_O 0 +#define FLAG_LOCK_C 1 +#define FLAG_LOCK_N 2 + +#define NUM_KEYS 256 +#define NUM_STATES 8 +#define ALTGR_OFFSET 128 + +struct keymap { + u_short n_keys; + struct key_t { + u_char map[NUM_STATES]; + u_char spcl; + u_char flgs; + } key[NUM_KEYS]; +}; + +#define MAXFK 16 + +struct fkeytab { + u_char str[MAXFK]; + u_char len; +}; + +struct fkeyarg { + u_short keynum; + char keydef[MAXFK]; + char flen; +}; + +struct colors { + char fore; + char back; +}; + +struct vid_info { + short size; + short m_num; + u_short mv_row, mv_col; + u_short mv_rsz, mv_csz; + struct colors mv_norm, + mv_rev, + mv_grfc; + u_char mv_ovscan; + u_char mk_keylock; +}; + +typedef struct keymap keymap_t; +typedef struct fkeytab fkeytab_t; +typedef struct fkeyarg fkeyarg_t; +typedef struct vid_info vid_info_t; +typedef struct {char scrmap[256];} scrmap_t; +typedef struct {char fnt8x8[8*256];} fnt8_t; +typedef struct {char fnt8x14[14*256];} fnt14_t; +typedef struct {char fnt8x16[16*256];} fnt16_t; + +#define F(x) ((x)+F_FN-1) +#define S(x) ((x)+F_SCR-1) +#define NOP 0x00 +#define LSH 0x02 +#define RSH 0x03 +#define CLK 0x04 +#define NLK 0x05 +#define SLK 0x06 +#define LALT 0x07 +#define LCTR 0x09 +#define RCTR 0x7b +#define RALT 0x7c +#define ALK 0x7d +#define ASH 0x7e + +#define F_SCR 11 /* switch to first screen */ +#define L_SCR 26 /* switch to last screen */ +#define F_FN 27 /* first function key */ +#define L_FN 122 /* last function key */ +#define FKEY 0x200 /* funtion key marker */ + +#define KB_DATA 0x60 /* kbd data port */ +#define KB_STAT 0x64 /* kbd status port */ +#define KB_BUF_FULL 0x01 /* kbd has char pending */ +#define KB_READY 0x02 /* kbd ready for command */ +#define KB_WRITE 0x60 /* kbd write command */ +#define KB_SETLEDS 0xed /* kbd set leds */ +#define KB_SETRAD 0xf3 /* kbd set repeat&delay command */ +#define KB_ACK 0xfa /* kbd acknowledge answer */ +#define KB_RESET_CPU 0xfe /* kbd reset main cpu command */ +#define KB_RESET 0xff /* kbd reset */ + +#endif diff --git a/sys/i386/include/cpu.h b/sys/i386/include/cpu.h index 583d76c985..43a6becdbc 100644 --- a/sys/i386/include/cpu.h +++ b/sys/i386/include/cpu.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)cpu.h 5.4 (Berkeley) 5/9/91 + * from: @(#)cpu.h 5.4 (Berkeley) 5/9/91 + * $Id$ */ /* @@ -48,16 +49,6 @@ */ #undef COPY_SIGCODE /* don't copy sigcode above user stack in exec */ -/* - * function vs. inline configuration; - * these are defined to get generic functions - * rather than inline or machine-dependent implementations - */ -#define NEED_MINMAX /* need {,i,l,ul}{min,max} functions */ -#define NEED_FFS /* need ffs function */ -#define NEED_BCMP /* need bcmp function */ -#define NEED_STRLEN /* need strlen function */ - #define cpu_exec(p) /* nothing */ /* @@ -71,8 +62,6 @@ typedef struct intrframe clockframe; #define CLKF_BASEPRI(framep) ((framep)->if_ppl == 0) #define CLKF_PC(framep) ((framep)->if_eip) -#define resettodr() /* no todr to set */ - /* * Preempt the current process if in interrupt from user mode, * or after the current trap/syscall if in system mode. @@ -98,11 +87,17 @@ int astpending; /* need to trap before returning to user mode */ int want_resched; /* resched() was called */ /* - * Kinds of processor + * pull in #defines for kinds of processors */ +#include "machine/cputypes.h" + +struct cpu_nameclass { + char *cpu_name; + int cpu_class; +}; -#define CPU_386SX 0 -#define CPU_386 1 -#define CPU_486SX 2 -#define CPU_486 3 -#define CPU_586 4 +#ifdef KERNEL +extern int cpu; +extern int cpu_class; +extern struct cpu_nameclass i386_cpus[]; +#endif diff --git a/sys/i386/include/cpufunc.h b/sys/i386/include/cpufunc.h index e3b4a8c9c0..db5cf95ff7 100644 --- a/sys/i386/include/cpufunc.h +++ b/sys/i386/include/cpufunc.h @@ -1,6 +1,8 @@ /* * Functions to provide access to special i386 instructions. * XXX - bezillions more are defined in locore.s but are not declared anywhere. + * + * $Id$ */ #include @@ -58,6 +60,116 @@ outb(u_int port, u_char data) __asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port)); } +static __inline__ +imin(a, b) + int a, b; +{ + + return (a < b ? a : b); +} + +static __inline__ +imax(a, b) + int a, b; +{ + + return (a > b ? a : b); +} + +static __inline__ +unsigned int +min(a, b) + unsigned int a, b; +{ + + return (a < b ? a : b); +} + +static __inline__ +unsigned int +max(a, b) + unsigned int a, b; +{ + + return (a > b ? a : b); +} + +static __inline__ +long +lmin(a, b) + long a, b; +{ + + return (a < b ? a : b); +} + +static __inline__ +long +lmax(a, b) + long a, b; +{ + + return (a > b ? a : b); +} + +static __inline__ +unsigned long +ulmin(a, b) + unsigned long a, b; +{ + + return (a < b ? a : b); +} + +static __inline__ +unsigned long +ulmax(a, b) + unsigned long a, b; +{ + + return (a > b ? a : b); +} + +static __inline__ +ffs(mask) + register long mask; +{ + register int bit; + + if (!mask) + return(0); + for (bit = 1;; ++bit) { + if (mask&0x01) + return(bit); + mask >>= 1; + } +} + +static __inline__ +bcmp(v1, v2, len) + void *v1, *v2; + register unsigned len; +{ + register u_char *s1 = v1, *s2 = v2; + + while (len--) + if (*s1++ != *s2++) + return (1); + return (0); +} + +static __inline__ +size_t +strlen(s1) + register __const__ char *s1; +{ + register size_t len; + + for (len = 0; *s1++ != '\0'; len++) + ; + return (len); +} + #else /* not __GNUC__ */ int bdb __P((void)); @@ -80,3 +192,4 @@ really_void setidt __P((int idx, /*XXX*/caddr_t func, int typ, int dpl)); #undef really_u_int #undef really_void + diff --git a/sys/i386/include/cputypes.h b/sys/i386/include/cputypes.h new file mode 100644 index 0000000000..b922494be4 --- /dev/null +++ b/sys/i386/include/cputypes.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1993 Christopher G. Demetriou + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id$ + */ + +/* + * Classes of Processor + */ + +#define CPUCLASS_286 0 +#define CPUCLASS_386 1 +#define CPUCLASS_486 2 +#define CPUCLASS_586 3 + +/* + * Kinds of Processor + */ + +#define CPU_286 0 /* Intel 80286 */ +#define CPU_386SX 1 /* Intel 80386SX */ +#define CPU_386 2 /* Intel 80386DX */ +#define CPU_486SX 3 /* Intel 80486SX */ +#define CPU_486 4 /* Intel 80486DX */ +#define CPU_586 5 /* Intel P.....m (I hate lawyers; it's TM) */ + diff --git a/sys/i386/include/db_machdep.h b/sys/i386/include/db_machdep.h index 8e37eec43f..aa0f006ee3 100644 --- a/sys/i386/include/db_machdep.h +++ b/sys/i386/include/db_machdep.h @@ -22,42 +22,8 @@ * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: db_machdep.h,v $ - * Revision 2.8 92/02/19 15:07:56 elf - * Added db_thread_fp_used. - * [92/02/19 rpd] - * - * Revision 2.7 91/10/09 16:06:28 af - * Revision 2.6.3.1 91/10/05 13:10:32 jeffreyh - * Added access and task name macros. - * [91/08/29 tak] - * - * Revision 2.6.3.1 91/10/05 13:10:32 jeffreyh - * Added access and task name macros. - * [91/08/29 tak] - * - * Revision 2.6 91/05/14 16:05:58 mrt - * Correcting copyright * - * Revision 2.5 91/02/05 17:11:17 mrt - * Changed to new Mach copyright - * [91/02/01 17:31:24 mrt] - * - * Revision 2.4 91/01/08 15:10:16 rpd - * Added dummy inst_load/inst_store macros. - * [90/12/11 rpd] - * - * Revision 2.3 90/10/25 14:44:49 rwd - * Added watchpoint support. - * [90/10/18 rpd] - * - * Revision 2.2 90/08/27 21:56:15 dbg - * Created. - * [90/07/25 dbg] - * + * $Id$ */ #ifndef _I386_DB_MACHDEP_H_ diff --git a/sys/i386/include/dkio.h b/sys/i386/include/dkio.h index 1bb06c99bd..90f0721b61 100644 --- a/sys/i386/include/dkio.h +++ b/sys/i386/include/dkio.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)dkio.h 5.2 (Berkeley) 1/18/91 + * from: @(#)dkio.h 5.2 (Berkeley) 1/18/91 + * $Id$ */ /* diff --git a/sys/i386/include/eflags.h b/sys/i386/include/eflags.h index 84f32757ec..663b31783a 100644 --- a/sys/i386/include/eflags.h +++ b/sys/i386/include/eflags.h @@ -22,21 +22,8 @@ * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. - */ -/* - * HISTORY - * $Log: eflags.h,v $ - * Revision 2.4 91/05/14 16:06:35 mrt - * Correcting copyright - * - * Revision 2.3 91/02/05 17:11:26 mrt - * Changed to new Mach copyright - * [91/02/01 17:31:41 mrt] - * - * Revision 2.2 90/05/03 15:25:03 dbg - * Created. - * [90/02/08 dbg] - * + * + * $Id$ */ #ifndef _I386_EFLAGS_H_ diff --git a/sys/i386/include/endian.h b/sys/i386/include/endian.h index 404bf4932e..9abd47eb52 100644 --- a/sys/i386/include/endian.h +++ b/sys/i386/include/endian.h @@ -30,15 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)endian.h 7.8 (Berkeley) 4/3/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00093 - * -------------------- ----- ---------------------- - * - * 27 Feb 93 Charles Hannum Better byte-swapping macros for - * i386/i486. + * from: @(#)endian.h 7.8 (Berkeley) 4/3/91 + * $Id$ */ /* diff --git a/sys/i386/include/float.h b/sys/i386/include/float.h index edfe0d9291..d6232a0d38 100644 --- a/sys/i386/include/float.h +++ b/sys/i386/include/float.h @@ -30,14 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)float.h 7.1 (Berkeley) 5/8/90 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00086 - * -------------------- ----- ---------------------- - * - * 27 Feb 93 Handel/da Silva/Poirot Adjust value for MAX_DOUBLE + * from: @(#)float.h 7.1 (Berkeley) 5/8/90 + * $Id$ */ #define FLT_RADIX 2 /* b */ @@ -46,9 +40,9 @@ #define FLT_MANT_DIG 24 /* p */ #define FLT_EPSILON 1.19209290E-07F /* b**(1-p) */ #define FLT_DIG 6 /* floor((p-1)*log10(b))+(b == 10) */ -#define FLT_MIN_EXP -125 /* emin */ +#define FLT_MIN_EXP (-125) /* emin */ #define FLT_MIN 1.17549435E-38F /* b**(emin-1) */ -#define FLT_MIN_10_EXP -37 /* ceil(log10(b**(emin-1))) */ +#define FLT_MIN_10_EXP (-37) /* ceil(log10(b**(emin-1))) */ #define FLT_MAX_EXP 128 /* emax */ #define FLT_MAX 3.40282347E+38F /* (1-b**(-p))*b**emax */ #define FLT_MAX_10_EXP 38 /* floor(log10((1-b**(-p))*b**emax)) */ @@ -56,11 +50,11 @@ #define DBL_MANT_DIG 53 #define DBL_EPSILON 2.2204460492503131E-16 #define DBL_DIG 15 -#define DBL_MIN_EXP -1021 -#define DBL_MIN 2.225073858507201E-308 -#define DBL_MIN_10_EXP -307 +#define DBL_MIN_EXP (-1021) +#define DBL_MIN 2.2250738585072014E-308 +#define DBL_MIN_10_EXP (-307) #define DBL_MAX_EXP 1024 -#define DBL_MAX 1.797693134862315E+308 +#define DBL_MAX 1.7976931348623157E+308 #define DBL_MAX_10_EXP 308 #define LDBL_MANT_DIG DBL_MANT_DIG diff --git a/sys/i386/include/floatingpoint.h b/sys/i386/include/floatingpoint.h new file mode 100644 index 0000000000..c17bd2c60f --- /dev/null +++ b/sys/i386/include/floatingpoint.h @@ -0,0 +1,108 @@ +/*- + * Copyright (c) 1993 Andrew Moore, Talke Studio + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#) floatingpoint.h 1.0 (Berkeley) 9/23/93 + * $Id$ + */ + +/* + * IEEE floating point structure and function definitions + */ + +#ifndef _FLOATINGPOINT_H_ +#define _FLOATINGPOINT_H_ + +#include +#include + +#ifdef __GNUC__ + +#define fnstcw(addr) __asm("fnstcw %0" : "=m" (*addr) : "0" (*addr)) +#define fnstsw(addr) __asm("fnstsw %0" : "=m" (*addr) : "0" (*addr)) +#define fnstenv(addr) __asm("fnstenv %0" : "=m" (*addr) : "0" (*addr)) +#define fldenv(addr) __asm("fldenv %0" : : "m" (*addr)) + +#ifdef __i386__ + +/* + * return the contents of a FP register + */ +static __inline__ int +__fpgetreg(int _reg) +{ + unsigned short _mem; + + switch(_reg) { + default: + fnstcw(&_mem); + break; + case FP_STKY_REG: + fnstsw(&_mem); + break; + } + return _mem; +} + +/* + * set a FP mode; return previous mode + */ +static __inline__ int +__fpsetreg(int _m, int _reg, int _fld, int _off) +{ + unsigned _env[7]; + unsigned _p; + + fnstenv(_env); + _p = (_env[_reg] & _fld) >> _off; + _env[_reg] = (_env[_reg] & ~_fld) | (_m << _off & _fld); + fldenv(_env); + return _p; +} + +#endif /* __i386__ */ + +#endif /* __GNUC__ */ + +/* + * SysV/386 FP control interface + */ +#define fpgetround() ((__fpgetreg(FP_RND_REG) & FP_RND_FLD) >> FP_RND_OFF) +#define fpsetround(m) __fpsetreg((m), FP_RND_REG, FP_RND_FLD, FP_RND_OFF) +#define fpgetprec() ((__fpgetreg(FP_PRC_REG) & FP_PRC_FLD) >> FP_PRC_OFF) +#define fpsetprec(m) __fpsetreg((m), FP_PRC_REG, FP_PRC_FLD, FP_PRC_OFF) +#define fpgetmask() ((~__fpgetreg(FP_MSKS_REG) & FP_MSKS_FLD) >> FP_MSKS_OFF) +#define fpsetmask(m) __fpsetreg(~(m), FP_MSKS_REG, FP_MSKS_FLD, FP_MSKS_OFF) +#define fpgetsticky() ((__fpgetreg(FP_STKY_REG) & FP_STKY_FLD) >> FP_STKY_OFF) +#define fpresetsticky(m) __fpsetreg(0, FP_STKY_REG, (m), FP_STKY_OFF) +#define fpsetsticky(m) fpresetsticky(m) + +#endif /* !_FLOATINGPOINT_H_ */ diff --git a/sys/i386/include/frame.h b/sys/i386/include/frame.h index 4dbabd1935..250e5c5e40 100644 --- a/sys/i386/include/frame.h +++ b/sys/i386/include/frame.h @@ -33,9 +33,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)frame.h 5.2 (Berkeley) 1/18/91 + * from: @(#)frame.h 5.2 (Berkeley) 1/18/91 + * $Id$ */ +#include + /* * System stack frames. */ @@ -92,6 +95,20 @@ struct intrframe { int if_ss; }; +/* + * Signal frame + */ +struct sigframe { + int sf_signum; + int sf_code; + struct sigcontext *sf_scp; + sig_t sf_handler; + int sf_eax; + int sf_edx; + int sf_ecx; + struct sigcontext sf_sc; +} ; + /* * Call Gate/System Call Stack Frame */ diff --git a/sys/i386/include/ioctl_pc.h b/sys/i386/include/ioctl_pc.h new file mode 100644 index 0000000000..bc6a255c8b --- /dev/null +++ b/sys/i386/include/ioctl_pc.h @@ -0,0 +1,797 @@ +/* Copyright 1992,1993 by Holger Veit + * May be freely used with Bill Jolitz's port of + * 386bsd and may be included in a 386bsd collection + * as long as binary and source are available and reproduce the above + * copyright. + * + * You may freely modify this code and contribute improvements based + * on this code as long as you don't claim to be the original author. + * Commercial use of this source requires permittance of the copyright + * holder. A general license for 386bsd will override this restriction. + * + * Use at your own risk. The copyright holder or any person who makes + * this code available for the public (administrators of public archives + * for instance) are not responsible for any harm to hardware or software + * that might happen due to wrong application or program faults. + * + * Addendum: The XFree86 developers and maintainers are hereby granted the + * right to distribute this file together with their source distributions + * and patchkits of XFree86 without further explicit permission of the + * above copyright holder. + * This and another file is a necessary include file for the unified + * pccons/codrv implementation of XFree86. This file is needed if + * someone wants to compile an Xserver on a system which does not have, + * for some reasons, the codrv console driver which comes with this file. The + * availability of this file avoids a large number of #ifdef's and + * allows to make the xserver code easier runtime-configurable. + * To make use of this file, it must be installed in /usr/include/sys. + * This file is not the complete console device driver, so it is possible + * that properties described in this file do not work without having the + * complete driver distribution. This is not a fault of the Xserver that + * was built with this file. + * + * + * + * From: @(#)$RCSfile: ioctl_pc.h,v + * Revision: 1.1.1.1 (Contributed to 386bsd) + * Date: 1993/06/12 14:58:11 + * + * Important notice: #defined values are subject to be changed!!! + * Don't use the constant, use the name instead! + * + * codrv1-style uses ioctls 'K': 1-33,255 + * 'V': 100-109 + * + * -hv- Holger Veit, Holger.Veit@gmd.de + * -hm Hellmuth Michaelis, hm@hcshh.hcs.de + * -vak- Sergey Vakulenko, vak@kiae.su + * + * 25-07-92 -hv- First version + * 16-08-92 -hm adding vga ioctl for cursor shape + * 25-10-92 -hv- X11 + video related ioctls + * 01/12/92 -vak- 8x16 font loading, beep ioctl, + * LED reassignment ioctl. + * 22-04-93 -hv- unified most CODRV1/CODRV2 codes + * 24-04-93 -hv- revised parts of keymap structures + * + * $Id$ + */ + +#ifndef _IOCTL_PC_H_ +#define _IOCTL_PC_H_ + +#ifdef NOTDEF +#if __GNUC__ >= 2 +#pragma pack(1) +#endif +#endif + +#ifndef KERNEL +#include +#ifndef _TYPES_H_ +#include +#endif +#else +#include "ioctl.h" +#endif + + +/*************************************************************************** + * Basic definitions + ***************************************************************************/ + +/* Use this data type when manipulating characters, don't use 'char' or 'u_char' + * some day this will be changed to 'u_short' or 'u_long' size to allow + * characters > 255 + */ +typedef u_char XCHAR; + +/*************************************************************************** + * driver identification + ***************************************************************************/ + +/* + * This defines the CONSOLE INFORMATION data structure, used to + * describe console capabilities, to distinguish between different + * versions. If this ioctl fail, you probably have an old style "pccons" + * driver (or an "improved" console driver, whose writer is not interested + * in providing compatibility for anything). + * In this case, a considerable number of features may not work as expected, + * or do not work at all. + */ + +#define MAXINFOSIZE 16 +struct consinfo { + u_long info1; + u_long __reserved1__; + u_long __reserved2__; + u_long __reserved3__; + XCHAR drv_name[MAXINFOSIZE+1]; + XCHAR emul_name[MAXINFOSIZE+1]; + XCHAR __reserved1_name__[MAXINFOSIZE+1]; + XCHAR __reserved2_name__[MAXINFOSIZE+1]; +}; + +struct oldconsinfo { + u_long info1; + u_long __reserved__; +}; + +#define CONSGINFO _IOR('K',255,struct consinfo) /* Get console capabilities */ +#define OLDCONSGINFO _IOR('K',255,struct oldconsinfo) /* compatibility */ +#define CONS_ISPC 0x00000001 /* is derived from old PCCONS */ +#define CONS_ISCO 0x00000002 /* is derived from CO driver */ +#define CONS_reserved1 0x00000004 /* reserved for other console drivers */ +#define CONS_reserved2 0x00000008 /* reserved for other console drivers */ +#define CONS_HASKBD 0x00000010 /* has /dev/kbd */ +#define CONS_HASSCAN 0x00000020 /* uses Scan codes */ +#define CONS_HASKEYNUM 0x00000040 /* uses KEYNUMS */ +#define CONS_HASVTY 0x00000080 /* has /dev/vty* */ +#define CONS_HASPC3 0x00000100 /* unused, historical */ +#define CONS_HASVTHP 0x00000200 /* unused, historical */ +#define CONS_reserved3 0x00000400 /* reserved */ +#define CONS_reserved4 0x00000800 /* reserved */ +#define CONS_HASPX386 0x00001000 /* has X386 probing support +new CONSOLE_X_MODE */ +#define CONS_HASOX386 0x00002000 /* has old X386 support CONSOLE_X_MODE_ON/OFF */ +#define CONS_reserved5 0x00004000 /* reserved */ +#define CONS_reserved6 0x00008000 /* reserved */ +#define CONS_HASKCAP 0x00010000 /* has ioctl keycap support */ +#define CONS_HASFNT 0x00020000 /* has ioctl font support */ +#define CONS_reserved7 0x00040000 /* reserved */ +#define CONS_reserved8 0x00080000 /* reserved */ +#define CONS_USE7BIT 0x00100000 /* does not support 8bit characters */ +#define CONS_USEPC8 0x00200000 /* uses PC8 8-bit mapping */ +#define CONS_USELATIN1 0x00400000 /* uses ISO LATIN1 mapping */ +#define CONS_HAS10646 0x00800000 /* has /dev/unicode */ +#define CONS_PCCONS2 0x01000000 /* modified pccons */ +#define CONS_CODRV1 0x02000000 /* old codrv ioctls */ +#define CONS_CODRV2 0x04000000 /* codrv ioctls 0.1.2 */ +#define CONS_reserved9 0x08000000 /* reserved */ +#define CONS_reserved10 0x10000000 /* reserved */ +#define CONS_reserved11 0x20000000 /* reserved */ +#define CONS_reserved12 0x40000000 /* reserved */ +#define CONS_reserved13 0x80000000 /* reserved */ + + +/*************************************************************************** + * IOCTLs for AT Keyboard + ***************************************************************************/ + +/**** initializing the keyboard ****/ + +/* reset keyboard, run selftests and set default values: + * default keymap, no overloaded keys, default typematic rate + * KBD_TPD500|KBD_TPM100, repetition on + */ +#define KBDCOLDRESET _IO('K', 1) /* reset keyboard and set default + * values: + * default keymap, no overloaded + * keys, default typematic rate + * KBD_TPD500|KBD_TPM100 + */ +/* resets the mode in keyboard controller only */ +#define KBDWARMRESET _IO('K', 23) + + + +/**** key repetition (typematic) feature ****/ + +/* get (G) / set (S) key repetition rate and delay + * see below for a definition of rate and delay and the necessary + * argument + */ +#define KBDGTPMAT _IOR('K', 2, int) +#define KBDSTPMAT _IOW('K', 3, int) + +/* Typematic rates: + * Rate = 1 / Period, with + * Period = (8+ (Val&7)) * 2^((Val>>3)&3) * 0.00417 seconds, + * and Val the typematic value below + * + * The typematic delay is determined by + * Delay = (1+((Val>>5)&3)) * 250 msec +/- 20 % + * + * Source IBM/AT reference manual, 1987 + * + * Note that you have to pass one TPD* and one TPM* value to the KBDSTPMAT + * ioctl: they are different flags of the same data word. Also note that + * 0x00 is a valid value: KBD_TPD250|KBD_TPM300 which is really fast, instead + * of turning off key repetition entirely. You can turn off key repetition + * with the ioctls KBDGREPSW/KBDSREPSW. +*/ + +#define KBD_TPD250 0x0000 /* 250 ms */ +#define KBD_TPD500 0x0020 /* 500 ms */ +#define KBD_TPD750 0x0040 /* 750 ms */ +#define KBD_TPD1000 0x0060 /* 1000 ms */ + +#define KBD_TPM300 0x0000 /* 30.0 rate */ +#define KBD_TPM267 0x0001 /* 26.7 rate */ +#define KBD_TPM240 0x0002 /* 24.0 rate */ +#define KBD_TPM218 0x0003 /* 21.8 rate */ +#define KBD_TPM200 0x0004 /* 20.0 rate */ +#define KBD_TPM185 0x0005 /* 18.5 rate */ +#define KBD_TPM171 0x0006 /* 17.1 rate */ +#define KBD_TPM160 0x0007 /* 16.0 rate */ +#define KBD_TPM150 0x0008 /* 15.0 rate */ +#define KBD_TPM133 0x0009 /* 13.3 rate */ +#define KBD_TPM120 0x000a /* 12.0 rate */ +#define KBD_TPM109 0x000b /* 10.9 rate */ +#define KBD_TPM100 0x000c /* 10.0 rate */ +#define KBD_TPM92 0x000d /* 9.2 rate */ +#define KBD_TPM86 0x000e /* 8.6 rate */ +#define KBD_TPM80 0x000f /* 8.0 rate */ +#define KBD_TPM75 0x0010 /* 7.5 rate */ +#define KBD_TPM67 0x0011 /* 6.7 rate */ +#define KBD_TPM60 0x0012 /* 6.0 rate */ +#define KBD_TPM55 0x0013 /* 5.5 rate */ +#define KBD_TPM50 0x0014 /* 5.0 rate */ +#define KBD_TPM46 0x0015 /* 4.6 rate */ +#define KBD_TPM43 0x0016 /* 4.3 rate */ +#define KBD_TPM40 0x0017 /* 4.0 rate */ +#define KBD_TPM37 0x0018 /* 3.7 rate */ +#define KBD_TPM33 0x0019 /* 3.3 rate */ +#define KBD_TPM30 0x001a /* 3.0 rate */ +#define KBD_TPM27 0x001b /* 2.7 rate */ +#define KBD_TPM25 0x001c /* 2.5 rate */ +#define KBD_TPM23 0x001d /* 2.3 rate */ +#define KBD_TPM21 0x001e /* 2.1 rate */ +#define KBD_TPM20 0x001f /* 2.0 rate */ + + +/* get (G) / set (S) the key repetition switch */ +#define KBD_REPEATOFF 0 +#define KBD_REPEATON 1 +#define KBDGREPSW _IOR('K', 4, int) +#define KBDSREPSW _IOW('K', 5, int) + + + +/**** handling keyboard LEDS and Lock keys ****/ + +/* get (G) / set (S) the keyboard LEDs, + * does not influence the state of the lock keys. + * Note: if keyboard serves tty console mode (VTYs have keyboard focus), + * the lock keys will still modify the state when used + */ +#define KBDGLEDS _IOR('K', 6, int) +#define KBDSLEDS _IOW('K', 7, int) + +/* get (G) / set (S) the SCROLL, NUM, CAPS ALTGRLOCK keys + * (note: ALTGRLOCK or SHIFTLOCK are not necessarily accessible + * on your keyboard) + */ +#define KBD_LOCKSCROLL 0x0001 +#define KBD_LOCKNUM 0x0002 +#define KBD_LOCKCAPS 0x0004 +#define KBD_LOCKALTGR 0x0008 +#define KBD_LOCKSHIFT 0x0010 +#define KBDGLOCK _IOR('K', 8, int) +#define KBDSLOCK _IOW('K', 9, int) + + + +/**** making noise ****/ + +/* get (G) / set (S) the beeper frequency and tone duration + * the nr param determines the VTY which parameters are changed + * VTY# = 0...n, n < max_vtys + * nr = -1: actual vty + * nr = -2: Set the system default beep frequency + * + * in some emulations, you can also set pitch and duration by an ESC code + */ +#define KBD_ACTVTY -1 +#define KBD_DEFLT -2 +struct kbd_bell { + int pitch; + int duration; + int nr; +}; + +#define KBDGETBEEP _IOWR('K',28, struct kbd_bell) +#define KBDSETBEEP _IOW('K',29, struct kbd_bell) + +/* do a beep of specified frequency and duration + * the argument nr is unused + * a NULL arg performs a default system beep + */ +#define KBDBELL _IOW('K',30, struct kbd_bell) + + + +/**** I/O access ****/ + +/* This call allows programs to access I/O ports. + * The ioctl is intended to perform several tasks for the XFree86 Xserver, + * but currently has other interesting applications. This is why it is + * priviledged and can only be executed by root (or with setuid-root). + * In future the ioctl might be restricted to allow access to video ports + * only. + */ +#define X_MODE_ON 1 +#define X_MODE_OFF 0 +#define CONSOLE_X_MODE _IOW('K',22,int) + + +/**** keyboard overloading ****/ + +/* Codrv allows loading of strings to keys in six layers. + * Any string may have a length of up to KBDMAXOVLKEYSIZE XCHARS. + * !!! Warning: This ioctl uses the type XCHAR. In future, this may + * !!! no longer be a char type, so str*** functions might not work any more + * !!! some day. + * The available layers are: + * + * - unshifted + * - with shift key + * - with ctrl key + * - with meta key (usually ALT-left) + * - with altgr key (usually ALT-right) + * - with shift+altgr key + * + * There are no combinations: shift-ctrl, ctrl-alt, shift-meta. + * The combination ctrl-altleft-somekey is reserved for system purposes. + * These keys are usually processed before the above keys. To gain control + * over these keys, you must run the keyboard in raw mode (/dev/kbd) and + * do ALL the processing yourself. The Xserver for instance does it this way. + * The following special keys are currently defined: + * + * CTRL-ALTLEFT-DELETE: Reboot + * CTRL-ALTLEFT-ESCAPE: Call the debugger (if compiled into the kernel) + * CTRL-ALTLEFT-KP+: Switch to next resolution (Xserver only) + * CTRL-ALTLEFT-KP-: Switch to previous resolution (Xserver only) + */ + +/* values for type field of various kbd_overload ioctls */ +#define KBD_NONE 0 /* no function, key is disabled */ +#define KBD_SHIFT 1 /* keyboard shift */ +#define KBD_META 2 /* (ALT) alternate shift, sets bit8 to ASCII code */ +#define KBD_NUM 3 /* numeric shift cursors vs. numeric */ +#define KBD_CTL 4 /* control shift -- allows ctl function */ +#define KBD_CAPS 5 /* caps shift -- swaps case of letter */ +#define KBD_ASCII 6 /* ascii code for this key */ +#define KBD_SCROLL 7 /* stop output */ +#define KBD_FUNC 8 /* function key */ +#define KBD_KP 9 /* Keypad keys */ +#define KBD_BREAK 10 /* The damned BREAK key, ignored in ioctl */ +#define KBD_ALTGR 11 /* AltGr Translation feature */ +#define KBD_SHFTLOCK 12 /* some people are accustomed to this nonsense */ +#define KBD_ALTGRLOCK 13 /* Useful for 8-bit national kbds (cyrillic) */ +#define KBD_DOALTCAPS 0x0400 /* change by altgr + caps shift */ +#define KBD_DOCAPS 0x0800 /* change by caps shift */ +#define KBD_DIACPFX 0x4000 /* Key carries a diacritical prefix */ +#define KBD_OVERLOAD 0x8000 /* Key is overloaded, ignored in ioctl */ +#define KBD_MASK 0x001f /* mask for type */ + +#define KBDMAXOVLKEYSIZE 15 /* excl. zero byte */ +struct kbd_ovlkey { + u_short keynum; + u_short type; + XCHAR unshift[KBDMAXOVLKEYSIZE+1]; + XCHAR shift[KBDMAXOVLKEYSIZE+1]; + XCHAR ctrl[KBDMAXOVLKEYSIZE+1]; + XCHAR meta[KBDMAXOVLKEYSIZE+1]; + XCHAR altgr[KBDMAXOVLKEYSIZE+1]; + XCHAR shiftaltgr[KBDMAXOVLKEYSIZE+1]; +}; + + +/* Get (G) / Set (S) a key assignment. This will influence the current + * key value only + */ +#define KBDGCKEY _IOWR('K',16, struct kbd_ovlkey) +#define KBDSCKEY _IOW('K',17, struct kbd_ovlkey) + +/* Get (G) the default (old) key assignment. You cannot overwrite the + * default setting, so this ioctl is unpaired + */ +#define KBDGOKEY _IOWR('K',18, struct kbd_ovlkey) + + + +/* Remove a key assignment for a key, i.e. restore default setting for key + * arg = keynum + */ +#define KBDRMKEY _IOW('K', 19, int) + +/* Restore the default key setting */ +#define KBDDEFAULT _IO('K',20) + + + +/* Set behavior of unassigned key layers + * Note that there is a hack from further versions which uses + * the flags KBD_C0 and KBD_A0 for this. This is still supported, but + * is not recommended way to do. It may disappear in future + * (what means that it won't :-)) + */ +#define KBD_CLEARCTRL 2 +#define KBD_CLEARMETA 4 +#define KBD_CLEARALT 1 +#ifdef notyet + #define KBD_CLEARNORM 8 + #define KBD_CLEARSHIFT 16 + #define KBD_CLEARSHALT 32 +#endif +#define KBDSCLRLYR _IOW('K',31,int) + +/* get (G) / set (S) CAPSLOCK LED behaviour. + * Not all of this keys may be accessible at your keyboard + * Note: For compatibility, the S ioctl returns the old state in arg + */ +#define KBD_CAPSCAPS 0 /* LED follows CAPSLOCK state */ +#define KBD_CAPSSHIFT 1 /* LED follows SHIFTLOCK state */ +#define KBD_CAPSALTGR 2 /* LED follows ALTGRLOCK state */ +#define KBD_CAPSINIT 0x04 /* bit to set to set a default for all VTYs */ +#define KBDGCAPSLED _IOR('K',27,int) +#define KBDSCAPSLED _IOWR('K',25,int) + +/* extended functions: functions that are triggered by a keypress + * before key is converted to ASCII + * + * use function KBD_HOTKEYDELETE to remove a hotkey from a key + */ +struct kbd_hotkey { + u_short key; + u_short modifier; + u_short function; +}; +#define KBDGSPECF _IOWR('K',32,struct kbd_hotkey) +#define KBDSSPECF _IOW('K',33,struct kbd_hotkey) + +/* extended function prefixes (in modifier field) + * bit set triggers a special function on the key layer + */ +#define KBD_NOEXT 0x00 /* trigger never */ +#define KBD_EXT_N 0x01 /* on normal key (normal layer) */ +#define KBD_EXT_S 0x02 /* on shift key (shift layer) */ +#define KBD_EXT_C 0x04 /* on ctrl key (ctrl layer) */ +#define KBD_EXT_A 0x08 /* on alt key (alt layer) */ +#define KBD_EXT_SK 0x10 /* on syskey (PRINTSCREEN) (Meta Layer) */ +#define KBD_EXT_CA 0x20 /* on ctrl-alt (shift alt layer) */ + +/* extended functions (in function field) */ +#define KBD_VTY0 0 /* select vty 0 */ +#define KBD_VTY1 1 /* select vty 1 */ +#define KBD_VTY2 2 /* select vty 2 */ +#define KBD_VTY3 3 /* select vty 3 */ +#define KBD_VTY4 4 /* select vty 4 */ +#define KBD_VTY5 5 /* select vty 5 */ +#define KBD_VTY6 6 /* select vty 6 */ +#define KBD_VTY7 7 /* select vty 7 */ +#define KBD_VTY8 8 /* select vty 8 */ +#define KBD_VTY9 9 /* select vty 9 */ +#define KBD_VTY10 10 /* select vty 10 */ +#define KBD_VTY11 11 /* select vty 11 */ +#define KBD_VTYUP 0x80 /* select next vty */ +#define KBD_VTYDOWN 0x81 /* select previous vty */ +#define KBD_RESETKEY 0x82 /* the CTRL-ALT-DEL key (movable) */ +#define KBD_DEBUGKEY 0x83 /* the CTRL-ALT-ESC key (debugger) */ + +#define KBD_HOTKEYDELETE 0xff /* use to delete a hotkey KBDSSPECF */ + + + +/* These are names used in older versions of keycap/codrv */ +/* do not use the following functions any longer in future */ +#ifdef COMPAT_CO011 +#define KBDRESET KBDCOLDRESET +#define KBDRESET8042 KBDWARMRESET +#define KBDFORCEASCII _IOW('K', 24, int) /* no op in codrv-0.1.2 */ +#define KBD_SCROLLLOCK KBD_LOCKSCROLL +#define KBD_NUMLOCK KBD_LOCKNUM +#define KBD_CAPSLOCK KBD_LOCKCAPS +#define KBDASGNLEDS KBDSCAPSLED +#ifndef KERNEL +struct kbd_sound { + int pitch; /* Frequency in Hz */ + int duration; /* Time in msec */ +}; +#endif +#define KBDSETBELL _IOW('K',21, struct kbd_sound) /* do some music */ +#define OLDKBDSETBEEP _IOW('K',26, struct kbd_sound) /* change beep settings */ + +struct oldkbd_ovlkey { + u_short keynum; + u_short type; + char unshift[KBDMAXOVLKEYSIZE+1]; + char shift[KBDMAXOVLKEYSIZE+1]; + char ctrl[KBDMAXOVLKEYSIZE+1]; + char altgr[KBDMAXOVLKEYSIZE+1]; +}; +#define OLDKBDGCKEY _IOWR('K',16, struct oldkbd_ovlkey) /* get current key values */ + + + +#endif /*COMPAT_CO011*/ + +/*************************************************************************** + * IOCTLs for Video Adapter + ***************************************************************************/ + +/* to define the cursor shape for ioctl */ +struct cursorshape { + int start; /* topmost scanline, range 0...31 */ + int end; /* bottom scanline, range 0...31 */ +}; + +#define VGAGCURSOR _IOR('V',100, struct cursorshape) /* get cursor shape */ +#define VGASCURSOR _IOW('V',101, struct cursorshape) /* set cursor shape */ + + + +/**** information ****/ + +/* the video information structure for ioctl */ +struct videoinfo { + char name[20]; /* ASCIZ name of detected card */ + short type; /* Adapter type, see below */ + short subtype; /* Adapter specific subtype */ + short ram; /* in KBytes */ + short iobase; /* Address of 6845: 0x3b0 / 0x3d0 */ +}; + +/* Get information about the videoboard */ +#define VGAGINFO _IOR('V',102, struct videoinfo) + +/* recognized Adapter types */ +#define VG_UNKNOWN 0 +#define VG_MONO 1 +#define VG_CGA 2 +#define VG_EGA 3 +#define VG_VGA 4 +#define VG_CHIPS 5 +/* CHIPS & TECHNOLOGIES has subtypes: + * 0x10 82c451 + * 0x11 82c452 + * 0x20 82c455 + * 0x30 82c453 + * 0x50 82c455 + */ +#define VG_GENOA 6 +/* GENOA has subtypes: + * 0x33/0x55 5100-5400, ET3000 based + * 0x22 6100 + * 0x00 6200,6300 + * 0x11 6400,6600 + */ +#define VG_PARADISE 7 +/* PARADISE has subtypes: + * 01 PVGA1A,WD90C90 + * 02 WD90C00 + * 03 WD90C10 + * 04 WD90C11 + */ +#define VG_TVGA 8 +/* TVGA has subtypes: + * 00-02 8800 + * 03 8900B + * 04 8900C + * 13 8900C + * 23 9000 + */ +#define VG_ET3000 9 +#define VG_ET4000 10 +#define VG_VIDEO7 11 +/* VIDEO7 has subtypes: + * 0x80-0xfe VEGA VGA + * 0x70-0x7e V7VGA FASTWRITE/VRAM + * 0x50-0x59 V7VGA version 5 + * 0x41-0x49 1024i + */ +#define VG_ATI 12 +/* ATI has subtypes: + * 0x01nn 18800 + * 0x02nn 18800-1 + * 0x03nn 28800-2 + * 0x04nn-05nn + * with nn: + * 0x01 VGA WONDER + * 0x02 EGA WONDER800+ + * 0x03 VGA BASIC 16+ + */ + + + +/**** Screen blanking ****/ + +/* Get (G) / Set (S) screen blanker timeout (seconds), + * time=0 disables blanking + * + * The blanking state is coded in bits 31 and 30 of word returned by get + */ +#define VGA_BLANKOFF 0x00000000 /* display is on, no blanking */ +#define VGA_BLANKON 0x40000000 /* display is on, wait for blank */ +#define VGA_BLANKED 0x80000000 /* display is dark */ +#define VGAGBLANK _IOR('V',2,int) +#define VGASBLANK _IOW('V',3,int) + + + +/**** Text/Attribute direct access, block move ****/ + +struct vga_block { + short mode; + short pagenum; + short x0,y0; /* upper left coordinates 0..x-1, 0..y-1 */ + short x1,y1; /* lower right coordinates >= x0,y0 */ + u_char *map; /* must be allocated by user process ! */ +}; + +/* mode word */ +#define VGA_SCREEN 0x01 /* entire screen, ignore x,y */ +#define VGA_WINDOW 0x02 /* use x,y for a rectangular window */ +#define VGA_TEXT 0x10 /* copy text information only */ +#define VGA_ATTR 0x20 /* copy attribute information only */ +#define VGA_BOTH 0x30 /* copy text and attribute */ +#define VGA_ALL 0x31 /* copy complete screen */ + +/* Get (G) / Set (S) a rectangular block of screen + * The virtual screen need not be visible. + * The buffer must be provided by the user process and must be large enough + * use VGAGVRES to find out how many bytes + * pagenum: 0..n, n < max_vty, VTY number + * -1, actual VTY + */ +#define VGAGBLOCK _IOWR('V',4,struct vga_block) +#define VGASBLOCK _IOW('V',5,struct vga_block) + + + +#define VGA_TXTPAGE0 0 +#define VGA_TXTPAGE1 1 +#ifdef notyet +#define VGA_GFXPAGE 2 +#endif +#define VGA_PC8CODING 0x80 /* obsolete ! */ + +/* maximum dimension of pixels + * Note: this is the space reserved in the fontchar map, but + * does not mean, that this resolution is accepted in the current release + * codrv-0.1.2 accepts 8x16 / "9x16" fonts only + */ +#define VGA_MAXX 16 +#define VGA_MAXY 16 + +struct fchar { + XCHAR encoding; /* encoding of character */ + char _f1_,_f2_,_f3_; /* filler */ + u_char map[VGA_MAXX/8*VGA_MAXY]; +}; + +struct fmap { + short page; /* page to load */ + short nr; /* nr of characters to load */ + char x,y; /* x,y pixel width */ + XCHAR start; /* first character in sequence (get only) */ + struct fchar *fntmap; /* allocated by user process */ +}; + +/* get (G) / set (S) font map. Must provide page,nr,start for get */ +#define VGAGFONTMAP _IOWR('V',6,struct fmap) +#define VGASFONTMAP _IOW('V',7,struct fmap) + + + +/* do not use the following functions any longer in future */ +#ifdef COMPAT_CO011 +/* miscellaneous functions: */ +#define VGA_DIS1 1 /* disable font 1 */ +#define VGA_GTENC 2 /* get current encoding */ +#define VGA_SBLANK 3 /* set screen blanking timeout (use VGASBLANK!) */ +#define VGA_GBLANK 4 /* get screen blanking timeout (use VGAGBLANK!) */ + +struct miscfcns { + u_char cmd; + union { + short enc[2]; + int timeout; + } u; +}; +#define VGAMISCFCNS _IOWR('V',107,struct miscfcns) /* misc functions */ + + +/* Font mapping this needs at least an EGA card (else EINVAL) */ +#define VGAFNTLATIN1 0x00 +#define VGAFNTEXTEND1 0x01 +#define VGAFNTEXTEND2 0x02 +#define VGAFNTGREEK 0x03 +#define VGAFNTCYRILLIC 0x04 +#define VGAFNTHEBREW 0x05 +#define VGAFNTARABIAN 0x06 + +#define VGA_FNTNCHARS 256 +#define VGA_FNTCSIZE 15 + +struct fontchar { + u_char page; /* which font page */ + u_char idx; /* which char in font page */ + u_char cmap[VGA_FNTCSIZE]; /* character bitmap */ +}; + +#define OLDVGAGCHAR _IOWR('V',105,struct fontchar) /* get character of font */ +#define OLDVGASCHAR _IOW('V',106,struct fontchar) /* set character in font */ + +struct fontmap { + u_char page; /* page to load */ + u_short encoding; /* font encoding */ + u_char map[VGA_FNTNCHARS*VGA_FNTCSIZE]; +}; + +#define OLDVGAGFNTMAP _IOWR('V',103,struct fontmap) /* get font */ +#define VGAGFNTMAP OLDVGAGFNTMAP +#define OLDVGASFNTMAP _IOW('V',104,struct fontmap) /* set font */ +#define VGASFNTMAP OLDVGASFNTMAP + +#endif + + + + +struct textpage { + u_char pagenum; /* note: only page 0 used by vtys */ +#define VGA_TEXTATTR 0 +#define VGA_TEXTDATA 1 + u_char ad; +#define VGA_LINES 50 /* only 25 used for now */ +#define VGA_COLUMNS 80 + u_char map[VGA_LINES*VGA_COLUMNS]; +}; + +#define VGAGPAGE _IOWR('V',108,struct textpage) /* get a data page */ +#define VGASPAGE _IOW('V',109,struct textpage) /* set a data page */ + +/**** Signalling access ****/ + +/* Use "take control" in an application program to signal the kernel + * that the program wants to use video memory (such as Xserver) + * before the program switches modes + * + * Use "give control" to return the control to the kernel. The application + * should have restored the original state before giving back control. + * Close /dev/vga also returns control. + * + * However, the kernel remains the master in the house, and reserves the right + * to grab control back at any time. (It usually doesn't). + * + */ +#define VGATAKECTRL _IO('V',8) +#define VGAGIVECTRL _IO('V',9) + +/*************************************************************************** + * Pandora's box, don't even think of using the following ioctl's + * (if you happen to find some; codrv_experimental might not be + * available at your system) + ***************************************************************************/ + +#ifdef PANDORA +#include "codrv_experimental.h" +#endif + + + +/*************************************************************************** + * XFree86 pccons support + ***************************************************************************/ + +#ifdef COMPAT_PCCONS +/* The following calls are special to the old pccons driver and are + * not understood or supported by codrv. + * This file serves as a central definition base for these calls + * in order to avoid defining them in applications that want to + * use them. + * + * One word of warning: There are different purpose tty ioctls + * with the same encoding, see + * TIOCSDTR = _IO('t', 121) + * TIOCCBRK = _IO('t', 122) + * + */ +#define CONSOLE_X_MODE_ON _IO('t',121) +#define CONSOLE_X_MODE_OFF _IO('t',122) +#define CONSOLE_X_BELL _IOW('t',123,int[2]) +#endif /* COMPAT_PCCONS */ + +#endif /* _IOCTL_PC_H_ */ + diff --git a/sys/i386/include/limits.h b/sys/i386/include/limits.h index 73474608f7..82fac5f353 100644 --- a/sys/i386/include/limits.h +++ b/sys/i386/include/limits.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)limits.h 7.2 (Berkeley) 6/28/90 + * from: @(#)limits.h 7.2 (Berkeley) 6/28/90 + * $Id$ */ #define CHAR_BIT 8 /* number of bits in a char */ diff --git a/sys/i386/include/mtpr.h b/sys/i386/include/mtpr.h index e69de29bb2..e8347e6775 100644 --- a/sys/i386/include/mtpr.h +++ b/sys/i386/include/mtpr.h @@ -0,0 +1,4 @@ +/* + * Unused in 386BSD port + * $Id$ + */ diff --git a/sys/i386/include/npx.h b/sys/i386/include/npx.h index 134e0c12ae..eab64570a6 100644 --- a/sys/i386/include/npx.h +++ b/sys/i386/include/npx.h @@ -33,15 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)npx.h 5.3 (Berkeley) 1/18/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00154 - * -------------------- ----- ---------------------- - * - * 20 Apr 93 Bruce Evans New npx-0.5 code - * + * from: @(#)npx.h 5.3 (Berkeley) 1/18/91 + * $Id$ */ /* diff --git a/sys/i386/include/param.h b/sys/i386/include/param.h index 1ee217a6ee..546a8f31bc 100644 --- a/sys/i386/include/param.h +++ b/sys/i386/include/param.h @@ -33,57 +33,60 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)param.h 5.8 (Berkeley) 6/28/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00166 - * -------------------- ----- ---------------------- - * - * 08 Apr 93 Andrew Herbert Fixes for kmem_alloc panics - * Rodney W. Grimes Tuneable mbuf sizes - * 04 Jun 93 Rodney W. Grimes Change default mbuf size to 2048 via - * MCLSHIFT. + * from: @(#)param.h 5.8 (Berkeley) 6/28/91 + * $Id: param.h,v 1.6 1993/10/12 12:08:16 rgrimes Exp $ */ /* * Machine dependent constants for Intel 386. */ -#define MACHINE "i386" +#define MACHINE "i386" +#define MID_MACHINE MID_I386 /* * Round p (pointer or byte index) up to a correctly-aligned value * for all data types (int, long, ...). The result is u_int and * must be cast to any desired pointer type. */ -#define ALIGN(p) (((u_int)(p) + (sizeof(int) - 1)) &~ (sizeof(int) - 1)) +#define ALIGNBYTES (sizeof(int) - 1) +#define ALIGN(p) (((u_int)(p) + ALIGNBYTES) &~ ALIGNBYTES) + +/* XXX PGSHIFT and PG_SHIFT are two names for the same thing */ +#define PGSHIFT 12 /* LOG2(NBPG) */ +#define NBPG (1 << PGSHIFT) /* bytes/page */ +#define PGOFSET (NBPG-1) /* byte offset into page */ +#define NPTEPG (NBPG/(sizeof (struct pte))) -#define NBPG 4096 /* bytes/page */ -#define PGOFSET (NBPG-1) /* byte offset into page */ -#define PGSHIFT 12 /* LOG2(NBPG) */ -#define NPTEPG (NBPG/(sizeof (struct pte))) +/* XXX PDRSHIFT and PD_SHIFT are two names for the same thing */ +#define PDRSHIFT 22 /* LOG2(NBPDR) */ +#define NBPDR (1 << PDRSHIFT) /* bytes/page dir */ +#define PDROFSET (NBPDR-1) /* byte offset into page dir */ -#define NBPDR (1024*NBPG) /* bytes/page dir */ -#define PDROFSET (NBPDR-1) /* byte offset into page dir */ -#define PDRSHIFT 22 /* LOG2(NBPDR) */ +/* + * XXX This should really be KPTDPTDI << PDRSHIFT, but since KPTDPTDI is + * defined in pmap.h which is included after this we can't do that + * (YET!) + */ +#define KERNBASE 0xFE000000 /* start of kernel virtual */ +#define BTOPKERNBASE ((u_long)KERNBASE >> PGSHIFT) -#define KERNBASE 0xFE000000 /* start of kernel virtual */ -#define BTOPKERNBASE ((u_long)KERNBASE >> PGSHIFT) +#define KERNSIZE 0x00C00000 /* size of kernel virtual */ + +#define DEV_BSHIFT 9 /* log2(DEV_BSIZE) */ +#define DEV_BSIZE (1 << DEV_BSHIFT) -#define DEV_BSIZE 512 -#define DEV_BSHIFT 9 /* log2(DEV_BSIZE) */ #define BLKDEV_IOSIZE 2048 -#define MAXPHYS (64 * 1024) /* max raw I/O transfer size */ +#define MAXPHYS (64 * 1024) /* max raw I/O transfer size */ -#define CLSIZE 1 -#define CLSIZELOG2 0 +#define CLSIZELOG2 0 +#define CLSIZE (1 << CLSIZELOG2) /* NOTE: SSIZE, SINCR and UPAGES must be multiples of CLSIZE */ -#define SSIZE 1 /* initial stack size/NBPG */ -#define SINCR 1 /* increment of stack/NBPG */ +#define SSIZE 1 /* initial stack size/NBPG */ +#define SINCR 1 /* increment of stack/NBPG */ -#define UPAGES 2 /* pages of u-area */ +#define UPAGES 2 /* pages of u-area */ /* * Constants related to network buffer management. @@ -93,50 +96,44 @@ * of the hardware page size. */ #ifndef MSIZE -#define MSIZE 128 /* size of an mbuf */ +#define MSIZE 128 /* size of an mbuf */ #endif /* MSIZE */ #ifndef MCLSHIFT -#define MCLSHIFT 11 /* convert bytes to m_buf clusters */ +#define MCLSHIFT 11 /* convert bytes to m_buf clusters */ #endif /* MCLSHIFT */ -#define MCLBYTES (1 << MCLSHIFT) /* size of an m_buf cluster */ -#define MCLOFSET (MCLBYTES - 1) /* offset within an m_buf cluster */ +#define MCLBYTES (1 << MCLSHIFT) /* size of an m_buf cluster */ +#define MCLOFSET (MCLBYTES - 1) /* offset within an m_buf cluster */ #ifndef NMBCLUSTERS #ifdef GATEWAY -#define NMBCLUSTERS 512 /* map size, max cluster allocation */ +#define NMBCLUSTERS 512 /* map size, max cluster allocation */ #else -#define NMBCLUSTERS 256 /* map size, max cluster allocation */ +#define NMBCLUSTERS 256 /* map size, max cluster allocation */ #endif /* GATEWAY */ #endif /* NMBCLUSTERS */ -/* - * Size of kernel malloc arena in CLBYTES-sized logical pages - */ -#ifndef NKMEMCLUSTERS -#define NKMEMCLUSTERS (3072*1024/CLBYTES) -#endif /* * Some macros for units conversion */ /* Core clicks (4096 bytes) to segments and vice versa */ -#define ctos(x) (x) -#define stoc(x) (x) +#define ctos(x) (x) +#define stoc(x) (x) /* Core clicks (4096 bytes) to disk blocks */ -#define ctod(x) ((x)<<(PGSHIFT-DEV_BSHIFT)) -#define dtoc(x) ((x)>>(PGSHIFT-DEV_BSHIFT)) -#define dtob(x) ((x)<>(PGSHIFT-DEV_BSHIFT)) +#define dtob(x) ((x)<>PGSHIFT) +#define btoc(x) (((unsigned)(x)+(NBPG-1))>>PGSHIFT) -#define btodb(bytes) /* calculates (bytes / DEV_BSIZE) */ \ +#define btodb(bytes) /* calculates (bytes / DEV_BSIZE) */ \ ((unsigned)(bytes) >> DEV_BSHIFT) -#define dbtob(db) /* calculates (db * DEV_BSIZE) */ \ +#define dbtob(db) /* calculates (db * DEV_BSIZE) */ \ ((unsigned)(db) << DEV_BSHIFT) /* @@ -145,7 +142,7 @@ * add an entry to cdevsw/bdevsw for that purpose. * For now though just use DEV_BSIZE. */ -#define bdbtofsb(bn) ((bn) / (BLKDEV_IOSIZE/DEV_BSIZE)) +#define bdbtofsb(bn) ((bn) / (BLKDEV_IOSIZE/DEV_BSIZE)) /* * Mach derived conversion macros @@ -159,6 +156,7 @@ #define i386_btop(x) ((unsigned)(x) >> PGSHIFT) #define i386_ptob(x) ((unsigned)(x) << PGSHIFT) -#ifndef KERNEL -#define DELAY(n) { volatile int N = (n); while (--N > 0); } -#endif +/* + * phystokv stolen from SCSI device drivers and fixed to use KERNBASE + */ +#define PHYSTOKV(x) (x | KERNBASE) diff --git a/sys/i386/include/pc/display.h b/sys/i386/include/pc/display.h index cab46e44e8..9e64a3f133 100644 --- a/sys/i386/include/pc/display.h +++ b/sys/i386/include/pc/display.h @@ -1,5 +1,7 @@ /* * IBM PC display definitions + * + * $Id$ */ /* Color attributes for foreground text */ diff --git a/sys/i386/include/pc/msdos.h b/sys/i386/include/pc/msdos.h index 8d1eb7bfd5..ea221c7954 100644 --- a/sys/i386/include/pc/msdos.h +++ b/sys/i386/include/pc/msdos.h @@ -2,6 +2,8 @@ * msdos common header file * [obtained from mtools -wfj] * how to decipher DOS disk structures in coexisting with DOS + * + * $Id$ */ #define MSECTOR_SIZE 512 /* MSDOS sector size in bytes */ diff --git a/sys/i386/include/pcb.h b/sys/i386/include/pcb.h index 92bd810ca0..96485b44cd 100644 --- a/sys/i386/include/pcb.h +++ b/sys/i386/include/pcb.h @@ -33,17 +33,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pcb.h 5.10 (Berkeley) 5/12/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00154 - * -------------------- ----- ---------------------- - * - * 20 Apr 93 Bruce Evans New npx-0.5 code - * + * from: @(#)pcb.h 5.10 (Berkeley) 5/12/91 + * $Id$ */ +#ifndef _I386_PCB_H_ +#define _I386_PCB_H_ + /* * Intel 386 process control block */ @@ -62,6 +58,8 @@ struct pcb { #ifdef notyet u_char pcb_iomap[NPORT/sizeof(u_char)]; /* i/o port bitmap */ #endif + caddr_t pcb_ldt; /* per process (user) LDT */ + int pcb_ldt_len; /* number of LDT entries */ struct save87 pcb_savefpu; /* floating point state for 287/387 */ struct emcsts pcb_saveemc; /* Cyrix EMC state */ /* @@ -85,3 +83,5 @@ struct pcb { #ifdef KERNEL struct pcb *curpcb; /* our current running pcb */ #endif + +#endif /* _I386_PCB_H_ */ diff --git a/sys/i386/include/pio.h b/sys/i386/include/pio.h index 553a97a453..2cb714c1c7 100644 --- a/sys/i386/include/pio.h +++ b/sys/i386/include/pio.h @@ -3,42 +3,11 @@ * Copyright (c) 1990 Carnegie-Mellon University * All rights reserved. The CMU software License Agreement specifies * the terms and conditions for use and redistribution. - */ -/* - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00158 - * -------------------- ----- ---------------------- - * - * 27 May 93 Rodney W. Grimes Added #ifndef around inb so that the - * clash with cpufunc.h does not cause - * a compile warning.. this is a hack. - * All of this needs cleaned up at 0.1.5 * - * HISTORY - * $Log: pio.h,v $ - * Revision 1.1 1992/05/27 00:48:30 balsup - * machkern/cor merge - * - * Revision 1.1 1991/10/10 20:11:39 balsup - * Initial revision - * - * Revision 2.2 91/04/02 11:52:29 mbj - * [90/08/14 mg32] - * - * Now we know how types are factor in. - * Cleaned up a bunch: eliminated ({ for output and flushed unused - * output variables. - * [90/08/14 rvb] - * - * This is how its done in gcc: - * Created. - * [90/03/26 rvb] - * + * from: Mach, unknown, 386BSD patch kit + * $Id$ */ - #define inl(y) \ ({ unsigned long _tmp__; \ asm volatile("inl %1, %0" : "=a" (_tmp__) : "d" ((unsigned short)(y))); \ diff --git a/sys/i386/include/pmap.h b/sys/i386/include/pmap.h index 2eff22fa22..3fe1248e14 100644 --- a/sys/i386/include/pmap.h +++ b/sys/i386/include/pmap.h @@ -34,17 +34,15 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pmap.h 7.4 (Berkeley) 5/12/91 - */ - -/* * Derived from hp300 version by Mike Hibler, this version by William * Jolitz uses a recursive map [a pde points to the page directory] to * map the page tables using the pagetables themselves. This is done to * reduce the impact on kernel virtual memory for lots of sparse address * space, and to reduce the cost of memory to each process. * - * from hp300: @(#)pmap.h 7.2 (Berkeley) 12/16/90 + * from: hp300: @(#)pmap.h 7.2 (Berkeley) 12/16/90 + * from: @(#)pmap.h 7.4 (Berkeley) 5/12/91 + * $Id$ */ #ifndef _PMAP_MACHINE_ @@ -54,7 +52,6 @@ * 386 page table entry and page table directory * W.Jolitz, 8/89 */ - struct pde { unsigned int @@ -94,9 +91,9 @@ unsigned int #define PG_u 0x00000004 #define PG_PROT 0x00000006 /* all protection bits . */ #define PG_W 0x00000200 -#define PG_N 0x00000800 /* Non-cacheable */ +#define PG_N 0x00000800 /* Non-cacheable */ #define PG_M 0x00000040 -#define PG_U 0x00000020 +#define PG_U 0x00000020 #define PG_FRAME 0xfffff000 #define PG_NOACC 0 @@ -109,31 +106,33 @@ unsigned int /* Garbage for current bastardized pager that assumes a hp300 */ #define PG_NV 0 #define PG_CI 0 + /* * Page Protection Exception bits */ - -#define PGEX_P 0x01 /* Protection violation vs. not present */ -#define PGEX_W 0x02 /* during a Write cycle */ -#define PGEX_U 0x04 /* access from User mode (UPL) */ +#define PGEX_P 0x01 /* Protection violation vs. not present */ +#define PGEX_W 0x02 /* during a Write cycle */ +#define PGEX_U 0x04 /* access from User mode (UPL) */ typedef struct pde pd_entry_t; /* page directory entry */ typedef struct pte pt_entry_t; /* Mach page table entry */ /* - * One page directory, shared between - * kernel and user modes. + * NKPDE controls the virtual space of the kernel, what ever is left is + * given to the user (NUPDE) */ -#define I386_PAGE_SIZE NBPG -#define I386_PDR_SIZE NBPDR - -#define I386_KPDES 8 /* KPT page directory size */ -#define I386_UPDES NBPDR/sizeof(struct pde)-8 /* UPT page directory size */ - -#define UPTDI 0x3f6 /* ptd entry for u./kernel&user stack */ -#define PTDPTDI 0x3f7 /* ptd entry that points to ptd! */ -#define KPTDI_FIRST 0x3f8 /* start of kernel virtual pde's */ -#define KPTDI_LAST 0x3fA /* last of kernel virtual pde's */ +#define NKPDE 7 /* number of kernel pde's */ +#define NUPDE (NPTEPG-NKPDE) /* number of user pde's */ +/* + * The *PTDI values control the layout of virtual memory + * + * XXX This works for now, but I am not real happy with it, I'll fix it + * right after I fix locore.s and the magic 28K hole + */ +#define APTDPTDI (NPTEPG-1) /* alt ptd entry that points to APTD */ +#define KPTDI (APTDPTDI-NKPDE)/* start of kernel virtual pde's */ +#define PTDPTDI (KPTDI-1) /* ptd entry that points to ptd! */ +#define UPTDI (PTDPTDI-1) /* ptd entry for u./kernel&user stack */ /* * Address of current and alternate address space page table maps @@ -142,7 +141,7 @@ typedef struct pte pt_entry_t; /* Mach page table entry */ #ifdef KERNEL extern struct pte PTmap[], APTmap[], Upte; extern struct pde PTD[], APTD[], PTDpde, APTDpde, Upde; -extern pt_entry_t *Sysmap; +extern pt_entry_t *Sysmap; extern int IdlePTD; /* physical address of "Idle" state directory */ #endif @@ -156,12 +155,12 @@ extern int IdlePTD; /* physical address of "Idle" state directory */ #define vtopte(va) (PTmap + i386_btop(va)) #define kvtopte(va) vtopte(va) #define ptetov(pt) (i386_ptob(pt - PTmap)) -#define vtophys(va) (i386_ptob(vtopte(va)->pg_pfnum) | ((int)(va) & PGOFSET)) -#define ispt(va) ((va) >= UPT_MIN_ADDRESS && (va) <= KPT_MAX_ADDRESS) +#define vtophys(va) (i386_ptob(vtopte(va)->pg_pfnum) | ((int)(va) & PGOFSET)) +#define ispt(va) ((va) >= UPT_MIN_ADDRESS && (va) <= KPT_MAX_ADDRESS) #define avtopte(va) (APTmap + i386_btop(va)) #define ptetoav(pt) (i386_ptob(pt - APTmap)) -#define avtophys(va) (i386_ptob(avtopte(va)->pg_pfnum) | ((int)(va) & PGOFSET)) +#define avtophys(va) (i386_ptob(avtopte(va)->pg_pfnum) | ((int)(va) & PGOFSET)) /* * macros to generate page directory/table indicies @@ -193,7 +192,7 @@ extern pmap_t kernel_pmap; /* * Macros for speed */ -#define PMAP_ACTIVATE(pmapp, pcbp) \ +#define PMAP_ACTIVATE(pmapp, pcbp) \ if ((pmapp) != NULL /*&& (pmapp)->pm_pdchanged */) { \ (pcbp)->pcb_cr3 = \ pmap_extract(kernel_pmap, (pmapp)->pm_pdir); \ @@ -202,7 +201,7 @@ extern pmap_t kernel_pmap; (pmapp)->pm_pdchanged = FALSE; \ } -#define PMAP_DEACTIVATE(pmapp, pcbp) +#define PMAP_DEACTIVATE(pmapp, pcbp) /* * For each vm_page_t, there is a list of all currently valid virtual @@ -218,14 +217,14 @@ typedef struct pv_entry { #define PV_ENTRY_NULL ((pv_entry_t) 0) #define PV_CI 0x01 /* all entries must be cache inhibited */ -#define PV_PTPAGE 0x02 /* entry maps a page table page */ +#define PV_PTPAGE 0x02 /* entry maps a page table page */ #ifdef KERNEL pv_entry_t pv_table; /* array of entries, one per page */ -#define pa_index(pa) atop(pa - vm_first_phys) -#define pa_to_pvh(pa) (&pv_table[pa_index(pa)]) +#define pa_index(pa) atop(pa - vm_first_phys) +#define pa_to_pvh(pa) (&pv_table[pa_index(pa)]) #define pmap_resident_count(pmap) ((pmap)->pm_stats.resident_count) diff --git a/sys/i386/include/proc.h b/sys/i386/include/proc.h index 02f3c01ac3..0e171645da 100644 --- a/sys/i386/include/proc.h +++ b/sys/i386/include/proc.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)proc.h 7.1 (Berkeley) 5/15/91 + * from: @(#)proc.h 7.1 (Berkeley) 5/15/91 + * $Id$ */ /* diff --git a/sys/i386/include/psl.h b/sys/i386/include/psl.h index aee73ed25c..21de5f7c3c 100644 --- a/sys/i386/include/psl.h +++ b/sys/i386/include/psl.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)psl.h 5.2 (Berkeley) 1/18/91 + * from: @(#)psl.h 5.2 (Berkeley) 1/18/91 + * $Id$ */ /* diff --git a/sys/i386/include/pte.h b/sys/i386/include/pte.h index 4b98413479..a98a357beb 100644 --- a/sys/i386/include/pte.h +++ b/sys/i386/include/pte.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pte.h 5.5 (Berkeley) 5/9/91 + * from: @(#)pte.h 5.5 (Berkeley) 5/9/91 + * $Id$ */ /* diff --git a/sys/i386/include/reg.h b/sys/i386/include/reg.h index bc2f05cf0c..11d930da82 100644 --- a/sys/i386/include/reg.h +++ b/sys/i386/include/reg.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)reg.h 5.5 (Berkeley) 1/18/91 + * from: @(#)reg.h 5.5 (Berkeley) 1/18/91 + * $Id$ */ /* diff --git a/sys/i386/include/segments.h b/sys/i386/include/segments.h index 0456e59ac8..bc77569335 100644 --- a/sys/i386/include/segments.h +++ b/sys/i386/include/segments.h @@ -34,7 +34,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)segments.h 7.1 (Berkeley) 5/9/91 + * from: @(#)segments.h 7.1 (Berkeley) 5/9/91 + * $Id$ */ /* diff --git a/sys/i386/include/soundcard.h b/sys/i386/include/soundcard.h new file mode 100644 index 0000000000..f853de470d --- /dev/null +++ b/sys/i386/include/soundcard.h @@ -0,0 +1,737 @@ +#ifndef SOUNDCARD_H +#define SOUNDCARD_H +/* + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + */ + + /* + * If you make modifications to this file, please contact me before + * distributing the modified version. There is already enough + * divercity in the world. + * + * Regards, + * Hannu Savolainen + * hsavolai@cs.helsinki.fi + */ + +#define SOUND_VERSION 200 + +#include + +/* + * Supported card ID numbers (Should be somewhere else?) + */ + +#define SNDCARD_ADLIB 1 +#define SNDCARD_SB 2 +#define SNDCARD_PAS 3 +#define SNDCARD_GUS 4 +#define SNDCARD_MPU401 5 + +/*********************************** + * IOCTL Commands for /dev/sequencer + */ + +#ifndef _IOWR +/* @(#)ioctlp.h */ + +/* Ioctl's have the command encoded in the lower word, + * and the size of any in or out parameters in the upper + * word. The high 2 bits of the upper word are used + * to encode the in/out status of the parameter; for now + * we restrict parameters to at most 128 bytes. + */ +/* #define IOCTYPE (0xff<<8) */ +#define IOCPARM_MASK 0x7f /* parameters must be < 128 bytes */ +#define IOC_VOID 0x20000000 /* no parameters */ +#define IOC_OUT 0x40000000 /* copy out parameters */ +#define IOC_IN 0x80000000 /* copy in parameters */ +#define IOC_INOUT (IOC_IN|IOC_OUT) +/* the 0x20000000 is so we can distinguish new ioctl's from old */ +#define _IO(x,y) ((int)(IOC_VOID|(x<<8)|y)) +#define _IOR(x,y,t) ((int)(IOC_OUT|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y)) +#define _IOW(x,y,t) ((int)(IOC_IN|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y)) +/* this should be _IORW, but stdio got there first */ +#define _IOWR(x,y,t) ((int)(IOC_INOUT|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y)) +#endif /* !_IOWR */ + +#define SNDCTL_SEQ_RESET _IO ('Q', 0) +#define SNDCTL_SEQ_SYNC _IO ('Q', 1) +#define SNDCTL_SYNTH_INFO _IOWR('Q', 2, struct synth_info) +#define SNDCTL_SEQ_CTRLRATE _IOWR('Q', 3, int) /* Set/get timer resolution (HZ) */ +#define SNDCTL_SEQ_GETOUTCOUNT _IOR ('Q', 4, int) +#define SNDCTL_SEQ_GETINCOUNT _IOR ('Q', 5, int) +#define SNDCTL_SEQ_PERCMODE _IOW ('Q', 6, int) +#define SNDCTL_FM_LOAD_INSTR _IOW ('Q', 7, struct sbi_instrument) /* Valid for FM only */ +#define SNDCTL_SEQ_TESTMIDI _IOW ('Q', 8, int) +#define SNDCTL_SEQ_RESETSAMPLES _IOW ('Q', 9, int) +#define SNDCTL_SEQ_NRSYNTHS _IOR ('Q',10, int) +#define SNDCTL_SEQ_NRMIDIS _IOR ('Q',11, int) +#define SNDCTL_MIDI_INFO _IOWR('Q',12, struct midi_info) +#define SNDCTL_SEQ_TRESHOLD _IOW ('Q',13, int) +#define SNDCTL_SYNTH_MEMAVL _IOWR('Q',14, int) /* in=dev#, out=memsize */ +#define SNDCTL_FM_4OP_ENABLE _IOW ('Q',15, int) /* in=dev# */ +#define SNDCTL_PMGR_ACCESS _IOWR('Q',16, struct patmgr_info) + +/* + * Sample loading mechanism for internal synthesizers (/dev/sequencer) + * The following patch_info structure has been designed to support + * Gravis UltraSound. It tries to be universal format for uploading + * sample based patches but is propably too limited. + */ + +struct patch_info { + short key; /* Use GUS_PATCH here */ +#define GUS_PATCH 0x04fd +#define OBSOLETE_GUS_PATCH 0x02fd + short device_no; /* Synthesizer number */ + short instr_no; /* Midi pgm# */ + + unsigned long mode; +/* + * The least significant byte has the same format than the GUS .PAT + * files + */ +#define WAVE_16_BITS 0x01 /* bit 0 = 8 or 16 bit wave data. */ +#define WAVE_UNSIGNED 0x02 /* bit 1 = Signed - Unsigned data. */ +#define WAVE_LOOPING 0x04 /* bit 2 = looping enabled-1. */ +#define WAVE_BIDIR_LOOP 0x08 /* bit 3 = Set is bidirectional looping. */ +#define WAVE_LOOP_BACK 0x10 /* bit 4 = Set is looping backward. */ +#define WAVE_SUSTAIN_ON 0x20 /* bit 5 = Turn sustaining on. (Env. pts. 3)*/ +#define WAVE_ENVELOPES 0x40 /* bit 6 = Enable envelopes - 1 */ + /* (use the env_rate/env_offs fields). */ +/* Linux specific bits */ +#define WAVE_VIBRATO 0x00010000 /* The vibrato info is valid */ +#define WAVE_TREMOLO 0x00020000 /* The tremolo info is valid */ +#define WAVE_SCALE 0x00040000 /* The scaling info is valid */ +/* Other bits must be zeroed */ + + long len; /* Size of the wave data in bytes */ + long loop_start, loop_end; /* Byte offsets from the beginning */ + +/* + * The base_freq and base_note fields are used when computing the + * playback speed for a note. The base_note defines the tone frequency + * which is heard if the sample is played using the base_freq as the + * playback speed. + * + * The low_note and high_note fields define the minimum and maximum note + * frequencies for which this sample is valid. It is possible to define + * more than one samples for a instrument number at the same time. The + * low_note and high_note fields are used to select the most suitable one. + * + * The fields base_note, high_note and low_note should contain + * the note frequency multiplied by 1000. For example value for the + * middle A is 440*1000. + */ + + unsigned int base_freq; + unsigned long base_note; + unsigned long high_note; + unsigned long low_note; + int panning; /* -128=left, 127=right */ + int detuning; + +/* New fields introduced in version 1.99.5 */ + + /* Envelope. Enabled by mode bit WAVE_ENVELOPES */ + unsigned char env_rate[ 6 ]; /* GUS HW ramping rate */ + unsigned char env_offset[ 6 ]; /* 255 == 100% */ + + /* + * The tremolo, vibrato and scale info are not supported yet. + * Enable by setting the mode bits WAVE_TREMOLO, WAVE_VIBRATO or + * WAVE_SCALE + */ + + unsigned char tremolo_sweep; + unsigned char tremolo_rate; + unsigned char tremolo_depth; + + unsigned char vibrato_sweep; + unsigned char vibrato_rate; + unsigned char vibrato_depth; + + int scale_frequency; + unsigned int scale_factor; /* from 0 to 2048 or 0 to 2 */ + + int volume; + int spare[4]; + char data[0]; /* The waveform data starts here */ + }; + + +/* + * Patch management interface (/dev/sequencer, /dev/patmgr#) + * Don't use these calls if you want to maintain compatibility with + * the future versions of the driver. + */ + +#define PS_NO_PATCHES 0 /* No patch support on device */ +#define PS_MGR_NOT_OK 1 /* Plain patch support (no mgr) */ +#define PS_MGR_OK 2 /* Patch manager supported */ +#define PS_MANAGED 3 /* Patch manager running */ + +#define SNDCTL_PMGR_IFACE _IOWR('P', 1, struct patmgr_info) + +/* + * The patmgr_info is a fixed size structure which is used for two + * different purposes. The intended use is for communication between + * the application using /dev/sequencer and the patch manager daemon + * associated with a synthesizer device (ioctl(SNDCTL_PMGR_ACCESS)). + * + * This structure is also used with ioctl(SNDCTL_PGMR_IFACE) which allows + * a patch manager daemon to read and write device parameters. This + * ioctl available through /dev/sequencer also. Avoid using it since it's + * extremely hardware dependent. In addition access trough /dev/sequencer + * may confuse the patch manager daemon. + */ + +struct patmgr_info { /* Note! size must be < 4k since kmalloc() is used */ + unsigned long key; /* Don't worry. Reserved for communication + between the patch manager and the driver. */ +#define PM_K_EVENT 1 /* Event from the /dev/sequencer driver */ +#define PM_K_COMMAND 2 /* Request from a application */ +#define PM_K_RESPONSE 3 /* From patmgr to application */ +#define PM_ERROR 4 /* Error returned by the patmgr */ + int device; + int command; + +/* + * Commands 0x000 to 0xfff reserved for patch manager programs + */ +#define PM_GET_DEVTYPE 1 /* Returns type of the patch mgr interface of dev */ +#define PMTYPE_FM2 1 /* 2 OP fm */ +#define PMTYPE_FM4 2 /* Mixed 4 or 2 op FM (OPL-3) */ +#define PMTYPE_WAVE 3 /* Wave table synthesizer (GUS) */ +#define PM_GET_NRPGM 2 /* Returns max # of midi programs in parm1 */ +#define PM_GET_PGMMAP 3 /* Returns map of loaded midi programs in data8 */ +#define PM_GET_PGM_PATCHES 4 /* Return list of patches of a program (parm1) */ +#define PM_GET_PATCH 5 /* Return patch header of patch parm1 */ +#define PM_SET_PATCH 6 /* Set patch header of patch parm1 */ +#define PM_READ_PATCH 7 /* Read patch (wave) data */ +#define PM_WRITE_PATCH 8 /* Write patch (wave) data */ + +/* + * Commands 0x1000 to 0xffff are for communication between the patch manager + * and the client + */ +#define _PM_LOAD_PATCH 0x100 + +/* + * Commands above 0xffff reserved for device specific use + */ + + long parm1; + long parm2; + long parm3; + + union { + unsigned char data8[4000]; + unsigned short data16[2000]; + unsigned long data32[1000]; + struct patch_info patch; + } data; + }; + +/* + * When a patch manager daemon is present, it will be informed by the + * driver when something important happens. For example when the + * /dev/sequencer is opened or closed. A record with key == PM_K_EVENT is + * returned. The command field contains the event type: + */ +#define PM_E_OPENED 1 /* /dev/sequencer opened */ +#define PM_E_CLOSED 2 /* /dev/sequencer closed */ +#define PM_E_PATCH_RESET 3 /* SNDCTL_RESETSAMPLES called */ +#define PM_E_PATCH_LOADED 4 /* A patch has been loaded by appl */ + +/* + * /dev/sequencer input events. + * + * The data written to the /dev/sequencer is a stream of events. Events + * are records of 4 or 8 bytes. The first byte defines the size. + * Any number of events can be written with a write call. There + * is a set of macros for sending these events. Use these macros if you + * want to maximize portability of your program. + * + * Events SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO. Are also input events. + * (All input events are currently 4 bytes long. Be prepared to support + * 8 byte events also. If you receive any event having first byte >= 0xf0, + * it's a 8 byte event. + * + * The events are documented at the end of this file. + * + * Normal events (4 bytes) + * There is also a 8 byte version of most of the 4 byte events. The + * 8 byte one is recommended. + */ +#define SEQ_NOTEOFF 0 +#define SEQ_FMNOTEOFF SEQ_NOTEOFF /* Just old name */ +#define SEQ_NOTEON 1 +#define SEQ_FMNOTEON SEQ_NOTEON +#define SEQ_WAIT 2 +#define SEQ_PGMCHANGE 3 +#define SEQ_FMPGMCHANGE SEQ_PGMCHANGE +#define SEQ_SYNCTIMER 4 +#define SEQ_MIDIPUTC 5 +#define SEQ_DRUMON 6 /*** OBSOLETE ***/ +#define SEQ_DRUMOFF 7 /*** OBSOLETE ***/ +#define SEQ_ECHO 8 /* For synching programs with output */ +#define SEQ_AFTERTOUCH 9 +#define SEQ_CONTROLLER 10 +#define CTRL_PITCH_BENDER 255 +#define CTRL_PITCH_BENDER_RANGE 254 +#define CTRL_EXPRESSION 253 +#define CTRL_MAIN_VOLUME 252 +#define SEQ_BALANCE 11 + +/* + * Note! SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO are used also as + * input events. + */ + +/* + * Event codes 0xf0 to 0xfc are reserved for future extensions. + */ + +#define SEQ_FULLSIZE 0xfd /* Long events */ +/* + * SEQ_FULLSIZE events are used for loading patches/samples to the + * synthesizer devices. These events are passed directly to the driver + * of the associated synthesizer device. There is no limit to the size + * of the extended events. These events are not queued but executed + * immediately when the write() is called (execution can take several + * seconds of time). + * + * When a SEQ_FULLSIZE message is written to the device, it must + * be written using exactly one write() call. Other events cannot + * be mixed to the same write. + * + * For FM synths (YM3812/OPL3) use struct sbi_instrument and write it to the + * /dev/sequencer. Don't write other data together with the instrument structure + * Set the key field of the structure to FM_PATCH. The device field is used to + * route the patch to the corresponding device. + * + * For Gravis UltraSound use struct patch_info. Initialize the key field + * to GUS_PATCH. + */ +#define SEQ_PRIVATE 0xfe /* Low level HW dependent events (8 bytes) */ +#define SEQ_EXTENDED 0xff /* Extended events (8 bytes) */ + +/* + * Extended events for synthesizers (8 bytes) + * + * Format: + * + * b0 = SEQ_EXTENDED + * b1 = command + * b2 = device + * b3-b7 = parameters + * + * Command b3 b4 b5 b6 b7 + * ---------------------------------------------------------------------------- + * SEQ_NOTEON voice note volume 0 0 + * SEQ_NOTEOFF voice note volume 0 0 + * SEQ_PGMCHANGE voice pgm 0 0 0 + * SEQ_DRUMON (voice) drum# volume 0 0 + * SEQ_DRUMOFF (voice) drum# volume 0 0 + */ + +/* + * Record for FM patches + */ + +typedef unsigned char sbi_instr_data[32]; + +struct sbi_instrument { + unsigned short key; /* Initialize to FM_PATCH or OPL3_PATCH */ +#define FM_PATCH 0x01fd +#define OPL3_PATCH 0x03fd + short device; /* Synth# (0-4) */ + int channel; /* Program# to be initialized */ + sbi_instr_data operators; /* Register settings for operator cells (.SBI format) */ + }; + +struct synth_info { /* Read only */ + char name[30]; + int device; /* 0-N. INITIALIZE BEFORE CALLING */ + int synth_type; +#define SYNTH_TYPE_FM 0 +#define SYNTH_TYPE_SAMPLE 1 + + int synth_subtype; +#define FM_TYPE_ADLIB 0x00 +#define FM_TYPE_OPL3 0x01 + +#define SAMPLE_TYPE_GUS 0x10 + + int perc_mode; /* No longer supported */ + int nr_voices; + int nr_drums; /* Obsolete field */ + int instr_bank_size; + unsigned long capabilities; +#define SYNTH_CAP_PERCMODE 0x00000001 /* No longer used */ +#define SYNTH_CAP_OPL3 0x00000002 /* Set if OPL3 supported */ + int dummies[19]; /* Reserve space */ + }; + +struct midi_info { + char name[30]; + int device; /* 0-N. INITIALIZE BEFORE CALLING */ + unsigned long capabilities; /* To be defined later */ + int dummies[19]; /* Reserve space */ + }; + +/******************************************** + * IOCTL commands for /dev/dsp and /dev/audio + */ + +#define SNDCTL_DSP_RESET _IO ('P', 0) +#define SNDCTL_DSP_SYNC _IO ('P', 1) +#define SNDCTL_DSP_SPEED _IOWR('P', 2, int) +#define SNDCTL_DSP_STEREO _IOWR('P', 3, int) +#define SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int) +#define SNDCTL_DSP_SAMPLESIZE _IOWR('P', 5, int) /* 8, 12 or 16 */ +#define SOUND_PCM_WRITE_CHANNELS _IOWR('P', 6, int) +#define SOUND_PCM_WRITE_FILTER _IOWR('P', 7, int) +#define SNDCTL_DSP_POST _IO ('P', 8) + +#define SOUND_PCM_READ_RATE _IOR ('P', 2, int) +#define SOUND_PCM_READ_CHANNELS _IOR ('P', 6, int) +#define SOUND_PCM_READ_BITS _IOR ('P', 5, int) +#define SOUND_PCM_READ_FILTER _IOR ('P', 7, int) + +/* Some alias names */ +#define SOUND_PCM_WRITE_BITS SNDCTL_DSP_SAMPLESIZE +#define SOUND_PCM_WRITE_RATE SNDCTL_DSP_SPEED +#define SOUND_PCM_POST SNDCTL_DSP_POST +#define SOUND_PCM_RESET SNDCTL_DSP_RESET +#define SOUND_PCM_SYNC SNDCTL_DSP_SYNC + +/********************************************* + * IOCTL commands for /dev/mixer + */ + +/* + * Mixer devices + * + * There can be up to 20 different analog mixer channels. The + * SOUND_MIXER_NRDEVICES gives the currently supported maximum. + * The SOUND_MIXER_READ_DEVMASK returns a bitmask which tells + * the devices supported by the particular mixer. + */ + +#define SOUND_MIXER_NRDEVICES 12 +#define SOUND_MIXER_VOLUME 0 +#define SOUND_MIXER_BASS 1 +#define SOUND_MIXER_TREBLE 2 +#define SOUND_MIXER_SYNTH 3 +#define SOUND_MIXER_PCM 4 +#define SOUND_MIXER_SPEAKER 5 +#define SOUND_MIXER_LINE 6 +#define SOUND_MIXER_MIC 7 +#define SOUND_MIXER_CD 8 +#define SOUND_MIXER_IMIX 9 /* Recording monitor */ +#define SOUND_MIXER_ALTPCM 10 +#define SOUND_MIXER_RECLEV 11 /* Recording level */ + +/* Some on/off settings (SOUND_SPECIAL_MIN - SOUND_SPECIAL_MAX) */ +/* Not counted to SOUND_MIXER_NRDEVICES, but use the same number space */ +#define SOUND_ONOFF_MIN 28 +#define SOUND_ONOFF_MAX 30 +#define SOUND_MIXER_MUTE 28 /* 0 or 1 */ +#define SOUND_MIXER_ENHANCE 29 /* Enhanced stereo (0, 40, 60 or 80) */ +#define SOUND_MIXER_LOUD 30 /* 0 or 1 */ + +/* Note! Number 31 cannot be used since the sign bit is reserved */ + +#define SOUND_DEVICE_LABELS {"Vol ", "Bass ", "Trebl", "Synth", "Pcm ", "Spkr ", "Line ", \ + "Mic ", "CD ", "Mix ", "Pcm2 ", "rec"} + +#define SOUND_DEVICE_NAMES {"vol", "bass", "treble", "synth", "pcm", "speaker", "line", \ + "mic", "cd", "mix", "pcm2", "rec"} + +/* Device bitmask identifiers */ + +#define SOUND_MIXER_RECSRC 0xff /* Arg contains a bit for each recording source */ +#define SOUND_MIXER_DEVMASK 0xfe /* Arg contains a bit for each supported device */ +#define SOUND_MIXER_RECMASK 0xfd /* Arg contains a bit for each supported recording source */ +#define SOUND_MIXER_CAPS 0xfc + #define SOUND_CAP_EXCL_INPUT 0x00000001 /* Only one recording source at a time */ +#define SOUND_MIXER_STEREODEVS 0xfb /* Mixer channels supporting stereo */ + +/* Device mask bits */ + +#define SOUND_MASK_VOLUME (1 << SOUND_MIXER_VOLUME) +#define SOUND_MASK_BASS (1 << SOUND_MIXER_BASS) +#define SOUND_MASK_TREBLE (1 << SOUND_MIXER_TREBLE) +#define SOUND_MASK_SYNTH (1 << SOUND_MIXER_SYNTH) +#define SOUND_MASK_PCM (1 << SOUND_MIXER_PCM) +#define SOUND_MASK_SPEAKER (1 << SOUND_MIXER_SPEAKER) +#define SOUND_MASK_LINE (1 << SOUND_MIXER_LINE) +#define SOUND_MASK_MIC (1 << SOUND_MIXER_MIC) +#define SOUND_MASK_CD (1 << SOUND_MIXER_CD) +#define SOUND_MASK_IMIX (1 << SOUND_MIXER_IMIX) +#define SOUND_MASK_ALTPCM (1 << SOUND_MIXER_ALTPCM) +#define SOUND_MASK_RECLEV (1 << SOUND_MIXER_RECLEV) + +#define SOUND_MASK_MUTE (1 << SOUND_MIXER_MUTE) +#define SOUND_MASK_ENHANCE (1 << SOUND_MIXER_ENHANCE) +#define SOUND_MASK_LOUD (1 << SOUND_MIXER_LOUD) + +#define MIXER_READ(dev) _IOR('M', dev, int) +#define SOUND_MIXER_READ_VOLUME MIXER_READ(SOUND_MIXER_VOLUME) +#define SOUND_MIXER_READ_BASS MIXER_READ(SOUND_MIXER_BASS) +#define SOUND_MIXER_READ_TREBLE MIXER_READ(SOUND_MIXER_TREBLE) +#define SOUND_MIXER_READ_SYNTH MIXER_READ(SOUND_MIXER_SYNTH) +#define SOUND_MIXER_READ_PCM MIXER_READ(SOUND_MIXER_PCM) +#define SOUND_MIXER_READ_SPEAKER MIXER_READ(SOUND_MIXER_SPEAKER) +#define SOUND_MIXER_READ_LINE MIXER_READ(SOUND_MIXER_LINE) +#define SOUND_MIXER_READ_MIC MIXER_READ(SOUND_MIXER_MIC) +#define SOUND_MIXER_READ_CD MIXER_READ(SOUND_MIXER_CD) +#define SOUND_MIXER_READ_IMIX MIXER_READ(SOUND_MIXER_IMIX) +#define SOUND_MIXER_READ_ALTPCM MIXER_READ(SOUND_MIXER_ALTPCM) +#define SOUND_MIXER_READ_RECLEV MIXER_READ(SOUND_MIXER_RECLEV) +#define SOUND_MIXER_READ_MUTE MIXER_READ(SOUND_MIXER_MUTE) +#define SOUND_MIXER_READ_ENHANCE MIXER_READ(SOUND_MIXER_ENHANCE) +#define SOUND_MIXER_READ_LOUD MIXER_READ(SOUND_MIXER_LOUD) + +#define SOUND_MIXER_READ_RECSRC MIXER_READ(SOUND_MIXER_RECSRC) +#define SOUND_MIXER_READ_DEVMASK MIXER_READ(SOUND_MIXER_DEVMASK) +#define SOUND_MIXER_READ_RECMASK MIXER_READ(SOUND_MIXER_RECMASK) +#define SOUND_MIXER_READ_STEREODEVS MIXER_READ(SOUND_MIXER_STEREODEVS) +#define SOUND_MIXER_READ_CAPS MIXER_READ(SOUND_MIXER_CAPS) + +#define MIXER_WRITE(dev) _IOWR('M', dev, int) +#define SOUND_MIXER_WRITE_VOLUME MIXER_WRITE(SOUND_MIXER_VOLUME) +#define SOUND_MIXER_WRITE_BASS MIXER_WRITE(SOUND_MIXER_BASS) +#define SOUND_MIXER_WRITE_TREBLE MIXER_WRITE(SOUND_MIXER_TREBLE) +#define SOUND_MIXER_WRITE_SYNTH MIXER_WRITE(SOUND_MIXER_SYNTH) +#define SOUND_MIXER_WRITE_PCM MIXER_WRITE(SOUND_MIXER_PCM) +#define SOUND_MIXER_WRITE_SPEAKER MIXER_WRITE(SOUND_MIXER_SPEAKER) +#define SOUND_MIXER_WRITE_LINE MIXER_WRITE(SOUND_MIXER_LINE) +#define SOUND_MIXER_WRITE_MIC MIXER_WRITE(SOUND_MIXER_MIC) +#define SOUND_MIXER_WRITE_CD MIXER_WRITE(SOUND_MIXER_CD) +#define SOUND_MIXER_WRITE_IMIX MIXER_WRITE(SOUND_MIXER_IMIX) +#define SOUND_MIXER_WRITE_ALTPCM MIXER_WRITE(SOUND_MIXER_ALTPCM) +#define SOUND_MIXER_WRITE_RECLEV MIXER_WRITE(SOUND_MIXER_RECLEV) +#define SOUND_MIXER_WRITE_MUTE MIXER_WRITE(SOUND_MIXER_MUTE) +#define SOUND_MIXER_WRITE_ENHANCE MIXER_WRITE(SOUND_MIXER_ENHANCE) +#define SOUND_MIXER_WRITE_LOUD MIXER_WRITE(SOUND_MIXER_LOUD) + +#define SOUND_MIXER_WRITE_RECSRC MIXER_WRITE(SOUND_MIXER_RECSRC) + +/* + * The following mixer ioctl calls are compatible with the BSD driver by + * Steve Haehnichen + * + * Since this interface is entirely SB specific, it will be dropped in the + * near future. + */ + +typedef unsigned char S_BYTE; +typedef unsigned char S_FLAG; +struct stereo_vol +{ + S_BYTE l; /* Left volume */ + S_BYTE r; /* Right volume */ +}; + +#define MIXER_IOCTL_SET_LEVELS _IOW ('s', 20, struct sb_mixer_levels) +#define MIXER_IOCTL_SET_PARAMS _IOW ('s', 21, struct sb_mixer_params) +#define MIXER_IOCTL_READ_LEVELS _IOR ('s', 22, struct sb_mixer_levels) +#define MIXER_IOCTL_READ_PARAMS _IOR ('s', 23, struct sb_mixer_params) +#define MIXER_IOCTL_RESET _IO ('s', 24) + +/* + * Mixer volume levels for MIXER_IOCTL_SET_VOL & MIXER_IOCTL_READ_VOL + */ +struct sb_mixer_levels +{ + struct stereo_vol master; /* Master volume */ + struct stereo_vol voc; /* DSP Voice volume */ + struct stereo_vol fm; /* FM volume */ + struct stereo_vol line; /* Line-in volume */ + struct stereo_vol cd; /* CD audio */ + S_BYTE mic; /* Microphone level */ +}; + +/* + * Mixer parameters for MIXER_IOCTL_SET_PARAMS & MIXER_IOCTL_READ_PARAMS + */ +struct sb_mixer_params +{ + S_BYTE record_source; /* Recording source (See SRC_xxx below) */ + S_FLAG hifreq_filter; /* Filter frequency (hi/low) */ + S_FLAG filter_input; /* ANFI input filter */ + S_FLAG filter_output; /* DNFI output filter */ + S_FLAG dsp_stereo; /* 1 if DSP is in Stereo mode */ +}; + +#define SRC_MIC 1 /* Select Microphone recording source */ +#define SRC_CD 3 /* Select CD recording source */ +#define SRC_LINE 7 /* Use Line-in for recording source */ + +#if !defined(KERNEL) && !defined(INKERNEL) +/* + * Some convenience macros to simplify programming of the + * /dev/sequencer interface + * + * These macros define the API which should be used when possible. + */ + +void seqbuf_dump(); /* This function must be provided by programs */ + +/* Sample seqbuf_dump() implementation: + * + * SEQ_DEFINEBUF (2048); -- Defines a buffer for 2048 bytes + * + * int seqfd; -- The file descriptor for /dev/sequencer. + * + * void + * seqbuf_dump () + * { + * if (_seqbufptr) + * if (write (seqfd, _seqbuf, _seqbufptr) == -1) + * { + * perror ("write /dev/sequencer"); + * exit (-1); + * } + * _seqbufptr = 0; + * } + */ + +#define SEQ_DEFINEBUF(len) unsigned char _seqbuf[len]; int _seqbuflen = len, _seqbufptr = 0 +#define SEQ_PM_DEFINES struct patmgr_info _pm_info +#define _SEQ_NEEDBUF(len) if ((_seqbufptr+(len)) > _seqbuflen) seqbuf_dump() +#define _SEQ_ADVBUF(len) _seqbufptr += len +#define SEQ_DUMPBUF seqbuf_dump +#define PM_LOAD_PATCH(dev, bank, pgm) (SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \ + _pm_info.device=dev, _pm_info.data.data8[0]=pgm, \ + _pm_info.parm1 = bank, _pm_info.parm2 = 1, \ + ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info)) +#define PM_LOAD_PATCHES(dev, bank, pgm) (SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \ + _pm_info.device=dev, memcpy(_pm_info.data.data8, pgm, 128), \ + _pm_info.parm1 = bank, _pm_info.parm2 = 128, \ + ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info)) + +#define SEQ_START_NOTE(dev, voice, note, vol) {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr] = SEQ_EXTENDED;\ + _seqbuf[_seqbufptr+1] = SEQ_NOTEON;\ + _seqbuf[_seqbufptr+2] = (dev);\ + _seqbuf[_seqbufptr+3] = (voice);\ + _seqbuf[_seqbufptr+4] = (note);\ + _seqbuf[_seqbufptr+5] = (vol);\ + _seqbuf[_seqbufptr+6] = 0;\ + _seqbuf[_seqbufptr+7] = 0;\ + _SEQ_ADVBUF(8);} + +#define SEQ_STOP_NOTE(dev, voice, note, vol) {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr] = SEQ_EXTENDED;\ + _seqbuf[_seqbufptr+1] = SEQ_NOTEOFF;\ + _seqbuf[_seqbufptr+2] = (dev);\ + _seqbuf[_seqbufptr+3] = (voice);\ + _seqbuf[_seqbufptr+4] = (note);\ + _seqbuf[_seqbufptr+5] = (vol);\ + _seqbuf[_seqbufptr+6] = 0;\ + _seqbuf[_seqbufptr+7] = 0;\ + _SEQ_ADVBUF(8);} + +#define SEQ_CHN_PRESSURE(dev, voice, pressure) {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr] = SEQ_EXTENDED;\ + _seqbuf[_seqbufptr+1] = SEQ_AFTERTOUCH;\ + _seqbuf[_seqbufptr+2] = (dev);\ + _seqbuf[_seqbufptr+3] = (voice);\ + _seqbuf[_seqbufptr+4] = (pressure);\ + _seqbuf[_seqbufptr+5] = 0;\ + _seqbuf[_seqbufptr+6] = 0;\ + _seqbuf[_seqbufptr+7] = 0;\ + _SEQ_ADVBUF(8);} + +#define SEQ_PANNING(dev, voice, pos) {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr] = SEQ_EXTENDED;\ + _seqbuf[_seqbufptr+1] = SEQ_BALANCE;\ + _seqbuf[_seqbufptr+2] = (dev);\ + _seqbuf[_seqbufptr+3] = (voice);\ + (char)_seqbuf[_seqbufptr+4] = (pos);\ + _seqbuf[_seqbufptr+5] = 0;\ + _seqbuf[_seqbufptr+6] = 0;\ + _seqbuf[_seqbufptr+7] = 0;\ + _SEQ_ADVBUF(8);} + +#define SEQ_CONTROL(dev, voice, controller, value) {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr] = SEQ_EXTENDED;\ + _seqbuf[_seqbufptr+1] = SEQ_CONTROLLER;\ + _seqbuf[_seqbufptr+2] = (dev);\ + _seqbuf[_seqbufptr+3] = (voice);\ + _seqbuf[_seqbufptr+4] = (controller);\ + *(short *)&_seqbuf[_seqbufptr+5] = (value);\ + _seqbuf[_seqbufptr+7] = 0;\ + _SEQ_ADVBUF(8);} + +#define SEQ_PITCHBEND(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_PITCH_BENDER, value) +#define SEQ_BENDER_RANGE(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_PITCH_BENDER_RANGE, value) +#define SEQ_EXPRESSION(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_EXPRESSION, value) +#define SEQ_MAIN_VOLUME(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_MAIN_VOLUME, value) + +#define SEQ_START_TIMER() {_SEQ_NEEDBUF(4);\ + _seqbuf[_seqbufptr] = SEQ_SYNCTIMER;\ + _seqbuf[_seqbufptr+1] = 0;\ + _seqbuf[_seqbufptr+2] = 0;\ + _seqbuf[_seqbufptr+3] = 0;\ + _SEQ_ADVBUF(4);} +#define SEQ_SET_PATCH(dev, voice, patch) {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr] = SEQ_EXTENDED;\ + _seqbuf[_seqbufptr+1] = SEQ_PGMCHANGE;\ + _seqbuf[_seqbufptr+2] = (dev);\ + _seqbuf[_seqbufptr+3] = (voice);\ + _seqbuf[_seqbufptr+4] = (patch);\ + _seqbuf[_seqbufptr+5] = 0;\ + _seqbuf[_seqbufptr+6] = 0;\ + _seqbuf[_seqbufptr+7] = 0;\ + _SEQ_ADVBUF(8);} + +#define SEQ_WAIT_TIME(ticks) {_SEQ_NEEDBUF(4);\ + *(unsigned long *)&_seqbuf[_seqbufptr] = SEQ_WAIT | ((ticks) << 8);\ + _SEQ_ADVBUF(4);} + +#define SEQ_ECHO_BACK(key) {_SEQ_NEEDBUF(4);\ + *(unsigned long *)&_seqbuf[_seqbufptr] = SEQ_ECHO | ((key) << 8);\ + _SEQ_ADVBUF(4);} + +#define SEQ_MIDIOUT(device, byte) {_SEQ_NEEDBUF(4);\ + _seqbuf[_seqbufptr] = SEQ_MIDIPUTC;\ + _seqbuf[_seqbufptr+1] = (byte);\ + _seqbuf[_seqbufptr+2] = (device);\ + _seqbuf[_seqbufptr+3] = 0;\ + _SEQ_ADVBUF(4);} +#define SEQ_WRPATCH(patch, len) {if (_seqbufptr) seqbuf_dump();\ + if (write(seqfd, (char*)(patch), len)==-1) \ + perror("Write patch: /dev/sequencer");} + +#endif +long soundcard_init(long mem_start); +#endif diff --git a/sys/i386/include/specialreg.h b/sys/i386/include/specialreg.h index d1908c9371..75382594b8 100644 --- a/sys/i386/include/specialreg.h +++ b/sys/i386/include/specialreg.h @@ -30,15 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)specialreg.h 7.1 (Berkeley) 5/9/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00154 - * -------------------- ----- ---------------------- - * - * 20 Apr 93 Bruce Evans New npx-0.5 code - * + * from: @(#)specialreg.h 7.1 (Berkeley) 5/9/91 + * $Id$ */ /* diff --git a/sys/i386/include/stdarg.h b/sys/i386/include/stdarg.h index f4992539ba..3fdd382ec1 100644 --- a/sys/i386/include/stdarg.h +++ b/sys/i386/include/stdarg.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)stdarg.h 7.2 (Berkeley) 5/4/91 + * from: @(#)stdarg.h 7.2 (Berkeley) 5/4/91 + * $Id$ */ typedef char *va_list; diff --git a/sys/i386/include/sysarch.h b/sys/i386/include/sysarch.h new file mode 100644 index 0000000000..92abe9c058 --- /dev/null +++ b/sys/i386/include/sysarch.h @@ -0,0 +1,12 @@ +/* + * Architecture specific syscalls (i386) + * + * $Id$ + */ +#define I386_GET_LDT 0 +#define I386_SET_LDT 1 + +#ifndef KERNEL +int i386_get_ldt __P((int, union descriptor *, int)); +int i386_set_ldt __P((int, union descriptor *, int)); +#endif diff --git a/sys/i386/include/trap.h b/sys/i386/include/trap.h index 601560523d..7b19bc7b4b 100644 --- a/sys/i386/include/trap.h +++ b/sys/i386/include/trap.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)trap.h 5.4 (Berkeley) 5/9/91 + * from: @(#)trap.h 5.4 (Berkeley) 5/9/91 + * $Id$ */ /* diff --git a/sys/i386/include/tss.h b/sys/i386/include/tss.h index 8ba140d6c7..fec3db5108 100644 --- a/sys/i386/include/tss.h +++ b/sys/i386/include/tss.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tss.h 5.4 (Berkeley) 1/18/91 + * from: @(#)tss.h 5.4 (Berkeley) 1/18/91 + * $Id$ */ /* diff --git a/sys/i386/include/types.h b/sys/i386/include/types.h index 9eeaef3cd7..118290cc23 100644 --- a/sys/i386/include/types.h +++ b/sys/i386/include/types.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)types.h 7.5 (Berkeley) 3/9/91 + * from: @(#)types.h 7.5 (Berkeley) 3/9/91 + * $Id$ */ #ifndef _MACHTYPES_H_ diff --git a/sys/i386/include/vmparam.h b/sys/i386/include/vmparam.h index 39403d6aba..700b697841 100644 --- a/sys/i386/include/vmparam.h +++ b/sys/i386/include/vmparam.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vmparam.h 5.9 (Berkeley) 5/12/91 + * from: @(#)vmparam.h 5.9 (Berkeley) 5/12/91 + * $Id$ */ @@ -63,7 +64,7 @@ */ #define MAXTSIZ (6*1024*1024) /* max text size */ #ifndef DFLDSIZ -#define DFLDSIZ (6*1024*1024) /* initial data size limit */ +#define DFLDSIZ (16*1024*1024) /* initial data size limit */ #endif #ifndef MAXDSIZ #define MAXDSIZ (32*1024*1024) /* max data size */ @@ -72,7 +73,7 @@ #define DFLSSIZ (512*1024) /* initial stack size limit */ #endif #ifndef MAXSSIZ -#define MAXSSIZ MAXDSIZ /* max stack size */ +#define MAXSSIZ (8*1024*1024) /* max stack size */ #endif /* @@ -90,6 +91,13 @@ #define SYSPTSIZE (2*NPTEPG) #define USRPTSIZE (2*NPTEPG) +/* + * Size of the Shared Memory Pages page table. + */ +#ifndef SHMMAXPGS +#define SHMMAXPGS 512 /* XXX until we have more kmap space */ +#endif + /* * Size of User Raw I/O map */ @@ -212,12 +220,9 @@ /* virtual sizes (bytes) for various kernel submaps */ #define VM_MBUF_SIZE (NMBCLUSTERS*MCLBYTES) -#define VM_KMEM_SIZE (NKMEMCLUSTERS*CLBYTES) +#define VM_KMEM_SIZE (16 * 1024 * 1024) #define VM_PHYS_SIZE (USRIOSIZE*CLBYTES) -/* # of kernel PT pages (initial only, can grow dynamically) */ -#define VM_KERNEL_PT_PAGES ((vm_size_t)2) /* XXX: SYSPTSIZE */ - /* pcb base */ #define pcbb(p) ((u_int)(p)->p_addr) diff --git a/sys/i386/isa/TODO b/sys/i386/isa/TODO index b42eba4e64..8cbe259b06 100644 --- a/sys/i386/isa/TODO +++ b/sys/i386/isa/TODO @@ -1,3 +1,5 @@ +This is file $Id$ + 1. add support for "soft" configuration network devices 2. split console driver into kbd/vga/other drivers 3. implement additional link layer support items (bpf et al) diff --git a/sys/i386/isa/aha1542.c b/sys/i386/isa/aha1542.c index 85271fcf97..5aedbc3439 100644 --- a/sys/i386/isa/aha1542.c +++ b/sys/i386/isa/aha1542.c @@ -12,45 +12,13 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- - * - * 16 Feb 93 Julian Elischer ADDED for SCSI system + * $Id: aha1542.c,v 1.12 1993/10/16 12:27:20 rgrimes Exp $ */ /* * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 */ -/* - * HISTORY - * $Log: aha1542.c,v $ - * Revision 1.6 1992/08/24 21:01:58 jason - * many changes and bugfixes for osf1 - * - * Revision 1.5 1992/07/31 01:22:03 julian - * support improved scsi.h layout - * - * Revision 1.4 1992/07/25 03:11:26 julian - * check each request fro sane flags. - * - * Revision 1.3 1992/07/24 00:52:45 julian - * improved timeout handling. - * added support for two arguments to the sd_done (or equiv) call so that - * they can pre-queue several arguments. - * slightly clean up error handling - * - * Revision 1.2 1992/07/17 22:03:54 julian - * upgraded the timeout code. - * added support for UIO-based i/o (as used for pmem operations) - * - * Revision 1.1 1992/05/27 00:51:12 balsup - * machkern/cor merge - */ - /* * a FEW lines in this driver come from a MACH adaptec-disk driver * so the copyright below is included: @@ -126,6 +94,7 @@ int Debugger(); #define Debugger() panic("should call debugger here (adaptec.c)") #endif NDDB #endif __386BSD__ +extern int hz; extern int delaycount; /* from clock setup code */ /************************** board definitions *******************************/ @@ -182,6 +151,8 @@ extern int delaycount; /* from clock setup code */ #define AHA_WRITE_FIFO 0x1c /* write fifo buffer */ #define AHA_READ_FIFO 0x1d /* read fifo buffer */ #define AHA_ECHO 0x1e /* Echo command data */ +#define AHA_EXT_BIOS 0x28 /* return extended bios info */ +#define AHA_MBX_ENABLE 0x29 /* enable mail box interface */ struct aha_cmd_buf { u_char byte[16]; @@ -258,8 +229,6 @@ struct aha_ccb { struct aha_ccb *next; struct scsi_xfer *xfer; /* the scsi_xfer for this cmd */ struct aha_mbx_out *mbx; /* pointer to mail box */ - long int delta; /* difference from previous*/ - struct aha_ccb *later,*sooner; int flags; #define CCB_FREE 0 #define CCB_ACTIVE 1 @@ -267,9 +236,6 @@ struct aha_ccb { }; -struct aha_ccb *aha_soonest = (struct aha_ccb *)0; -struct aha_ccb *aha_latest = (struct aha_ccb *)0; -long int aha_furtherest = 0; /* longest time in the timeout queue */ /* * opcode fields @@ -330,6 +296,29 @@ struct aha_config u_char :5; }; +struct aha_inquire +{ + u_char boardid; /* type of board */ + /* 0x20 = BusLogic 545, but it gets + the command wrong, only returns + one byte */ + /* 0x31 = AHA-1540 */ + /* 0x41 = AHA-1540A/1542A/1542B */ + /* 0x42 = AHA-1640 */ + /* 0x43 = AHA-1542C */ + /* 0x44 = AHA-1542CF */ + u_char spec_opts; /* special options ID */ + /* 0x41 = Board is standard model */ + u_char revision_1; /* firmware revision [0-9A-Z] */ + u_char revision_2; /* firmware revision [0-9A-Z] */ +}; + +struct aha_extbios +{ + u_char flags; /* Bit 3 == 1 extended bios enabled */ + u_char mailboxlock; /* mail box lock code to unlock it */ +}; + #define INT9 0x01 #define INT10 0x02 #define INT11 0x04 @@ -351,7 +340,6 @@ struct aha_config #define KVTOPHYS(x) kvtophys(x) #else MACH #ifdef __386BSD__ -#define PHYSTOKV(x) (x | 0xFE000000) #define KVTOPHYS(x) vtophys(x) #else __386BSD__ #endif __386BSD__ @@ -381,7 +369,9 @@ int aha_initialized[NAHA]; #ifdef OSF int aha_attached[NAHA]; #endif OSF -int aha_debug = 0; +#ifdef AHADEBUG +int aha_debug = 1; +#endif /*AHADEBUG*/ int ahaprobe(), ahaattach(), ahaintr(); #ifdef MACH @@ -415,7 +405,8 @@ struct scsi_switch aha_switch = 0, 0, aha_adapter_info, - 0,0,0 + "aha", + 0,0 }; #define AHA_CMD_TIMEOUT_FUDGE 200 /* multiplied to get Secs */ #define AHA_RESET_TIMEOUT 1000000 /* time to wait for reset */ @@ -474,7 +465,8 @@ u_char args; } if (!i) { - printf("aha_cmd: aha1542 host not idle(0x%x)\n",sts); + printf("aha%d: aha_cmd, host not idle(0x%x)\n", + unit,sts); return(ENXIO); } } @@ -504,7 +496,7 @@ u_char args; } if (i >= wait) { - printf("aha_cmd: aha1542 cmd/data port full\n"); + printf("aha%d: aha_cmd, cmd/data port full\n",unit); outb(AHA_CTRL_STAT_PORT, AHA_SRST); return(ENXIO); } @@ -525,7 +517,8 @@ u_char args; } if (i >= wait) { - printf("aha_cmd: aha1542 cmd/data port empty %d\n",ocnt); + printf("aha%d: aha_cmd, cmd/data port empty %d\n", + unit,ocnt); return(ENXIO); } oc = inb(AHA_CMD_DATA_PORT); @@ -546,7 +539,7 @@ u_char args; } if (!i) { - printf("aha_cmd: aha1542 host not finished(0x%x)\n",sts); + printf("aha%d: aha_cmd, host not finished(0x%x)\n",unit,sts); return(ENXIO); } outb(AHA_CTRL_STAT_PORT, AHA_IRST); @@ -577,7 +570,7 @@ struct isa_dev *dev; aha_base[unit] = dev->dev_addr; if(unit >= NAHA) { - printf("aha: unit number (%d) too high\n",unit); + printf("aha%d: unit number too high\n",unit); return(0); } /***********************************************\ @@ -607,6 +600,7 @@ struct isa_dev *dev; #endif #else /* !defined(OSF) */ + dev->dev_pic = aha_dma[unit]; chp->ih_level = dev->dev_pic; chp->ih_handler = dev->dev_intr[0]; chp->ih_resolver = i386_resolver; @@ -619,11 +613,8 @@ struct isa_dev *dev; else panic("Unable to add aha interrupt handler"); #endif /* !defined(OSF) */ -#ifdef __386BSD__ - printf("\n **"); -#else __386BSD__ - printf("port=%x spl=%d\n", - dev->dev_addr, dev->dev_spl); +#ifndef __386BSD__ + printf("port=%x spl=%d\n", dev->dev_addr, dev->dev_spl); #endif __386BSD__ ahaunit ++; return(1); @@ -637,9 +628,6 @@ struct isa_dev *dev; { int unit = dev->dev_unit; -#ifdef __386BSD__ - printf(" probing for scsi devices**\n"); -#endif __386BSD__ /***********************************************\ * ask the adapter what subunits are present * \***********************************************/ @@ -647,18 +635,9 @@ struct isa_dev *dev; #if defined(OSF) aha_attached[unit]=1; #endif /* defined(OSF) */ - if(!unit) /* only one for all boards */ - { - aha_timeout(0); - } -#ifdef __386BSD__ - printf("aha%d",unit); -#endif __386BSD__ return; - } - /***********************************************\ * Return some information to the caller about * * the adapter and it's capabilities * @@ -678,8 +657,10 @@ ahaintr(unit) unsigned char stat; register i; +#ifdef AHADEBUG if(scsi_debug & PRINTROUTINES) printf("ahaintr "); +#endif /*AHADEBUG*/ /***********************************************\ * First acknowlege the interrupt, Then if it's * * not telling about a completed operation * @@ -687,12 +668,16 @@ ahaintr(unit) \***********************************************/ stat = inb(AHA_INTR_PORT); outb(AHA_CTRL_STAT_PORT, AHA_IRST); +#ifdef AHADEBUG if(scsi_debug & TRACEINTERRUPTS) printf("int "); +#endif /*AHADEBUG*/ if (! (stat & AHA_MBIF)) return(1); +#ifdef AHADEBUG if(scsi_debug & TRACEINTERRUPTS) printf("b "); +#endif /*AHADEBUG*/ #if defined(OSF) if (!aha_attached[unit]) { @@ -714,15 +699,19 @@ ahaintr(unit) switch(stat) { case AHA_MBI_ABORT: +#ifdef AHADEBUG if(aha_debug) printf("abort"); +#endif /*AHADEBUG*/ ccb->host_stat = AHA_ABORTED; break; case AHA_MBI_UNKNOWN: ccb = (struct aha_ccb *)0; +#ifdef AHADEBUG if(aha_debug) printf("unknown ccb for abort "); +#endif /*AHADEBUG*/ /* may have missed it */ /* no such ccb known for abort */ @@ -733,6 +722,7 @@ ahaintr(unit) panic("Impossible mbxi status"); } +#ifdef AHADEBUG if( aha_debug && ccb ) { u_char *cp; @@ -744,10 +734,11 @@ ahaintr(unit) , aha_mbx[unit].mbi[i].stat, i); printf("addr = 0x%x\n", ccb); } +#endif /*AHADEBUG*/ } if(ccb) { - aha_remove_timeout(ccb); + untimeout(aha_timeout,ccb); aha_done(unit,ccb); } aha_mbx[unit].mbi[i].stat = AHA_MBI_FREE; @@ -765,19 +756,16 @@ struct aha_ccb *ccb; { unsigned int opri; +#ifdef AHADEBUG if(scsi_debug & PRINTROUTINES) printf("ccb%d(0x%x)> ",unit,flags); +#endif /*AHADEBUG*/ if (!(flags & SCSI_NOMASK)) opri = splbio(); ccb->next = aha_ccb_free[unit]; aha_ccb_free[unit] = ccb; ccb->flags = CCB_FREE; - if(ccb->sooner || ccb->later) - { - printf("yikes, still in timeout queue\n"); - aha_remove_timeout(ccb); - } /***********************************************\ * If there were none, wake abybody waiting for * * one to come free, starting with queued entries* @@ -798,8 +786,10 @@ aha_get_ccb(unit,flags) unsigned opri; struct aha_ccb *rc; +#ifdef AHADEBUG if(scsi_debug & PRINTROUTINES) printf("xfer; +#ifdef AHADEBUG if(scsi_debug & PRINTROUTINES ) printf("aha_done "); +#endif /*AHADEBUG*/ /***********************************************\ * Otherwise, put the results of the operation * * into the xfer and call whoever started it * \***********************************************/ if(!(xs->flags & INUSE)) { - printf("exiting but not in use! "); + printf("aha%d: exiting but not in use!\n",unit); Debugger(); } if ( ( ccb->host_stat != AHA_OK @@ -861,11 +853,13 @@ struct aha_ccb *ccb; break; default: /* Other scsi protocol messes */ xs->error = XS_DRIVER_STUFFUP; +#ifdef AHADEBUG if (aha_debug > 1) { printf("host_stat%x\n", ccb->host_stat); } +#endif /*AHADEBUG*/ } } @@ -882,11 +876,13 @@ struct aha_ccb *ccb; xs->error = XS_BUSY; break; default: +#ifdef AHADEBUG if (aha_debug > 1) { printf("target_stat%x\n", ccb->target_stat); } +#endif /*AHADEBUG*/ xs->error = XS_DRIVER_STUFFUP; } } @@ -911,6 +907,8 @@ int unit; unsigned char ad[3]; volatile int i,sts; struct aha_config conf; + struct aha_inquire inquire; + struct aha_extbios extbios; /***********************************************\ * reset board, If it doesn't respond, assume * @@ -927,23 +925,61 @@ int unit; } if (i >= AHA_RESET_TIMEOUT) { +#ifdef AHADEBUG if (aha_debug) printf("aha_init: No answer from adaptec board\n"); +#endif /*AHADEBUG*/ return(ENXIO); } - + /* + * Assume we have a board at this stage, do an adapter inquire + * to find out what type of controller it is + */ + aha_cmd(unit, 0, sizeof(inquire), 1 ,&inquire, AHA_INQUIRE); +#ifdef AHADEBUG + printf("aha%d: inquire %x, %x, %x, %x\n", + unit, + inquire.boardid, inquire.spec_opts, + inquire.revision_1, inquire.revision_2); +#endif /* AHADEBUG */ + /* + * XXX The Buslogic 545S gets the AHA_INQUIRE command wrong, + * they only return one byte which causes us to print an error, + * so if the boardid comes back as 0x20, tell the user why they + * get the "cmd/data port empty" message + */ + if (inquire.boardid == 0x20) { + /* looks like a Buslogic 545 */ + printf ("aha%d: above cmd/data port empty do to Buslogic 545\n", + unit); + } + /* + * If we are a 1542C or 1542CF disable the extended bios so that the + * mailbox interface is unlocked. + * No need to check the extended bios flags as some of the + * extensions that cause us problems are not flagged in that byte. + */ + if ((inquire.boardid == 0x43) || (inquire.boardid == 0x44)) { + aha_cmd(unit, 0, sizeof(extbios), 0, &extbios, AHA_EXT_BIOS); +#ifdef AHADEBUG + printf("aha%d: extended bios flags %x\n", unit, extbios.flags); +#endif /* AHADEBUG */ + printf("aha%d: 1542C/CF detected, unlocking mailbox\n"); + aha_cmd(unit, 2, 0, 0, 0, AHA_MBX_ENABLE, + 0, extbios.mailboxlock); + } /***********************************************\ - * Assume we have a board at this stage * - * setup dma channel from jumpers and save int * + * Setup dma channel from jumpers and save int * * level * \***********************************************/ #ifdef __386BSD__ - printf("aha%d reading board settings, ",unit); -#define PRNT(x) + printf("aha%d: reading board settings, ",unit); +#define PRNT(x) printf(x) #else __386BSD__ printf("aha%d:",unit); #define PRNT(x) printf(x) #endif __386BSD__ + DELAY(10000); /* for Bustek 545 */ aha_cmd(unit,0, sizeof(conf), 0 ,&conf, AHA_CONF_GET); switch(conf.chan) { @@ -1012,10 +1048,18 @@ int unit; /***********************************************\ * Initialize memory transfer speed * \***********************************************/ +/* + * XXX This code seems to BREAK more boards than it makes + * work right, we are just going to NOP this here... + */ +#if 0 if(!(aha_set_bus_speed(unit))) { return(EIO); } +#else + printf ("\n"); +#endif /***********************************************\ @@ -1091,8 +1135,10 @@ struct scsi_xfer *xs; struct iovec *iovp; int s; +#ifdef AHADEBUG if(scsi_debug & PRINTROUTINES) printf("aha_scsi_cmd "); +#endif /*AHADEBUG*/ /***********************************************\ * get a ccb (mbox-out) to use. If the transfer * * is from a buf (possibly from interrupt time) * @@ -1101,13 +1147,13 @@ struct scsi_xfer *xs; flags = xs->flags; if(!(flags & INUSE)) { - printf("not in use!"); + printf("aha%d: not in use!\n",unit); Debugger(); xs->flags |= INUSE; } if(flags & ITSDONE) { - printf("Already done! check device retry code "); + printf("aha%d: Already done! check device retry code\n",unit); Debugger(); xs->flags &= ~ITSDONE; } @@ -1119,7 +1165,7 @@ struct scsi_xfer *xs; } if (ccb->mbx->cmd != AHA_MBO_FREE) - printf("MBO not free\n"); + printf("aha%d: MBO not free\n",unit); /***********************************************\ * Put all the arguments for the xfer in the ccb * @@ -1156,10 +1202,12 @@ struct scsi_xfer *xs; { lto3b(iovp->iov_base,&(sg->seg_addr)); lto3b(iovp->iov_len,&(sg->seg_len)); +#ifdef AHADEBUG if(scsi_debug & SHOWSCATGATH) printf("(0x%x@0x%x)" ,iovp->iov_len ,iovp->iov_base); +#endif /*AHADEBUG*/ sg++; iovp++; seg++; @@ -1172,8 +1220,10 @@ struct scsi_xfer *xs; * Set up the scatter gather block * \***********************************************/ +#ifdef AHADEBUG if(scsi_debug & SHOWSCATGATH) printf("%d @0x%x:- ",xs->datalen,xs->data); +#endif /*AHADEBUG*/ datalen = xs->datalen; thiskv = (int)xs->data; thisphys = KVTOPHYS(thiskv); @@ -1185,8 +1235,10 @@ struct scsi_xfer *xs; /* put in the base address */ lto3b(thisphys,&(sg->seg_addr)); +#ifdef AHADEBUG if(scsi_debug & SHOWSCATGATH) printf("0x%x",thisphys); +#endif /*AHADEBUG*/ /* do it at least once */ nextphys = thisphys; @@ -1216,19 +1268,23 @@ struct scsi_xfer *xs; /***************************************\ * next page isn't contiguous, finish the seg* \***************************************/ +#ifdef AHADEBUG if(scsi_debug & SHOWSCATGATH) printf("(0x%x)",bytes_this_seg); +#endif /*AHADEBUG*/ lto3b(bytes_this_seg,&(sg->seg_len)); sg++; seg++; } } lto3b(seg * sizeof(struct aha_scat_gath),ccb->data_length); +#ifdef AHADEBUG if(scsi_debug & SHOWSCATGATH) printf("\n"); +#endif /*AHADEBUG*/ if (datalen) { /* there's still data, must have run out of segs! */ - printf("aha_scsi_cmd%d: more than %d DMA segs\n", + printf("aha%d: aha_scsi_cmd, more than %d DMA segs\n", unit,AHA_NSEG); xs->error = XS_DRIVER_STUFFUP; aha_free_ccb(unit,ccb,flags); @@ -1247,6 +1303,7 @@ struct scsi_xfer *xs; \***********************************************/ if(!(flags & SCSI_RESET)) bcopy(xs->cmd, &ccb->scsi_cmd, ccb->scsi_cmd_length); +#ifdef AHADEBUG if(scsi_debug & SHOWCOMMANDS) { u_char *b = (u_char *)&ccb->scsi_cmd; @@ -1272,22 +1329,27 @@ struct scsi_xfer *xs; ); } } +#endif /*AHADEBUG*/ if (!(flags & SCSI_NOMASK)) { s= splbio(); /* stop instant timeouts */ - aha_add_timeout(ccb,xs->timeout); + timeout(aha_timeout,ccb,(xs->timeout * hz) / 1000); aha_startmbx(ccb->mbx); /***********************************************\ * Usually return SUCCESSFULLY QUEUED * \***********************************************/ splx(s); +#ifdef AHADEBUG if(scsi_debug & TRACEINTERRUPTS) printf("sent "); +#endif /*AHADEBUG*/ return(SUCCESSFULLY_QUEUED); } aha_startmbx(ccb->mbx); +#ifdef AHADEBUG if(scsi_debug & TRACEINTERRUPTS) printf("cmd_sent, waiting "); +#endif /*AHADEBUG*/ /***********************************************\ * If we can't use interrupts, poll on completion* \***********************************************/ @@ -1314,7 +1376,7 @@ struct scsi_xfer *xs; if (!count) { if (!(xs->flags & SCSI_SILENT)) - printf("cmd fail\n"); + printf("aha%d: cmd fail\n",unit); aha_abortmbx(ccb->mbx); count = delaycount * 2000 / AHA_SCSI_TIMEOUT_FUDGE; while((!done) && count) @@ -1336,7 +1398,7 @@ struct scsi_xfer *xs; } if(!count) { - printf("abort failed in wait\n"); + printf("aha%d: abort failed in wait\n",unit); ccb->mbx->cmd = AHA_MBO_FREE; } aha_free_ccb(unit,ccb,flags); @@ -1387,7 +1449,7 @@ int unit; { speed++; } - printf("%d nSEC ok, use ",retval); + printf("%d nSEC ok, using ",retval); retval2 = aha_bus_speed_check(unit,speed); if(retval2 == HAD_ERROR) /* retval is slowest already */ { @@ -1396,12 +1458,12 @@ int unit; } if(retval2) { - printf("%d nSEC ",retval2); + printf("%d nSEC\n",retval2); return(retval2); } else { - printf(".. slower failed, abort.\n",retval); + printf(".. slower failed, abort\n",retval); return(0); } @@ -1479,166 +1541,43 @@ int unit,speed; } -/* - * +----------+ +----------+ +----------+ - * aha_soonest--->| later |--->| later|--->| later|-->0 - * | [Delta] | | [Delta] | | [Delta] | - * 0<---|sooner |<---|sooner |<---|sooner |<---aha_latest - * +----------+ +----------+ +----------+ - * - * aha_furtherest = sum(Delta[1..n]) - */ -aha_add_timeout(ccb,time) -struct aha_ccb *ccb; -int time; -{ - int timeprev; - struct aha_ccb *prev; - int s = splbio(); - if(prev = aha_latest) /* yes, an assign */ - { - timeprev = aha_furtherest; - } - else - { - timeprev = 0; - } - while(prev && (timeprev > time)) - { - timeprev -= prev->delta; - prev = prev->sooner; - } - if(prev) - { - ccb->delta = time - timeprev; - if( ccb->later = prev->later) /* yes an assign */ - { - ccb->later->sooner = ccb; - ccb->later->delta -= ccb->delta; - } - else - { - aha_furtherest = time; - aha_latest = ccb; - } - ccb->sooner = prev; - prev->later = ccb; - } - else - { - if( ccb->later = aha_soonest) /* yes, an assign*/ - { - ccb->later->sooner = ccb; - ccb->later->delta -= time; - } - else - { - aha_furtherest = time; - aha_latest = ccb; - } - ccb->delta = time; - ccb->sooner = (struct aha_ccb *)0; - aha_soonest = ccb; - } - splx(s); -} - -aha_remove_timeout(ccb) -struct aha_ccb *ccb; +aha_timeout(struct aha_ccb *ccb) { - int s = splbio(); + int unit; + int s = splbio(); - if(ccb->sooner) - { - ccb->sooner->later = ccb->later; - } - else - { - aha_soonest = ccb->later; - } - if(ccb->later) + unit = ccb->xfer->adapter; + printf("aha%d: device %d timed out ",unit ,ccb->xfer->targ); + + /***************************************\ + * If The ccb's mbx is not free, then * + * the board has gone south * + \***************************************/ + if(ccb->mbx->cmd != AHA_MBO_FREE) { - ccb->later->sooner = ccb->sooner; - ccb->later->delta += ccb->delta; + printf("aha%d: not taking commands!\n",unit); + Debugger(); } - else + /***************************************\ + * If it has been through before, then * + * a previous abort has failed, don't * + * try abort again * + \***************************************/ + if(ccb->flags == CCB_ABORTED) /* abort timed out */ { - aha_latest = ccb->sooner; - aha_furtherest -= ccb->delta; + printf(" AGAIN\n"); + ccb->xfer->retries = 0; /* I MEAN IT ! */ + ccb->host_stat = AHA_ABORTED; + aha_done(unit,ccb); } - ccb->sooner = ccb->later = (struct aha_ccb *)0; - splx(s); -} - - -extern int hz; -#define ONETICK 500 /* milliseconds */ -#define SLEEPTIME ((hz * 1000) / ONETICK) -aha_timeout(arg) -int arg; -{ - struct aha_ccb *ccb; - int unit; - int s = splbio(); - - while( ccb = aha_soonest ) + else /* abort the operation that has timed out */ { - if(ccb->delta <= ONETICK) - /***********************************************\ - * It has timed out, we need to do some work * - \***********************************************/ - { - unit = ccb->xfer->adapter; - printf("aha%d: device %d timed out ",unit - ,ccb->xfer->targ); - - /***************************************\ - * Unlink it from the queue * - \***************************************/ - aha_remove_timeout(ccb); - - /***************************************\ - * If The ccb's mbx is not free, then * - * the board has gone south * - \***************************************/ - if(ccb->mbx->cmd != AHA_MBO_FREE) - { - printf("aha%d not taking commands!\n" - ,unit); - Debugger(); - } - /***************************************\ - * If it has been through before, then * - * a previous abort has failed, don't * - * try abort again * - \***************************************/ - if(ccb->flags == CCB_ABORTED) /* abort timed out */ - { - printf(" AGAIN\n"); - ccb->xfer->retries = 0; /* I MEAN IT ! */ - ccb->host_stat = AHA_ABORTED; - aha_done(unit,ccb); - } - else /* abort the operation that has timed out */ - { - printf("\n"); - aha_abortmbx(ccb->mbx); - /* 2 secs for the abort */ - aha_add_timeout(ccb,2000 + ONETICK); - ccb->flags = CCB_ABORTED; - } - } - else - /***********************************************\ - * It has not timed out, adjust and leave * - \***********************************************/ - { - ccb->delta -= ONETICK; - aha_furtherest -= ONETICK; - break; - } + printf("\n"); + aha_abortmbx(ccb->mbx); + /* 2 secs for the abort */ + timeout(aha_timeout,ccb,2 * hz); + ccb->flags = CCB_ABORTED; } splx(s); - timeout(aha_timeout,arg,SLEEPTIME); } diff --git a/sys/i386/isa/aha1742.c b/sys/i386/isa/aha1742.c index 721f6f74b8..1e3e154f43 100644 --- a/sys/i386/isa/aha1742.c +++ b/sys/i386/isa/aha1742.c @@ -12,14 +12,9 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- - * - * 16 Feb 93 Julian Elischer ADDED for SCSI system * commenced: Sun Sep 27 18:14:01 PDT 1992 + * + * $Id: aha1742.c,v 1.9 1993/08/28 03:07:40 rgrimes Exp $ */ #include @@ -77,6 +72,7 @@ int Debugger(); #endif MACH typedef unsigned long int physaddr; +extern int hz; #ifdef MACH extern physaddr kvtophys(); @@ -85,7 +81,6 @@ extern physaddr kvtophys(); #endif MACH #ifdef __386BSD__ -#define PHYSTOKV(x) (x | 0xFE000000) #define KVTOPHYS(x) vtophys(x) #endif __386BSD__ @@ -271,8 +266,6 @@ struct ecb /*-----------------end of hardware supported fields----------------*/ struct ecb *next; /* in free list */ struct scsi_xfer *xs; /* the scsi_xfer for this cmd */ - long int delta; /* difference from previous*/ - struct ecb *later,*sooner; int flags; #define ECB_FREE 0 #define ECB_ACTIVE 1 @@ -284,9 +277,6 @@ struct ecb struct scsi_sense_data ecb_sense; }; -struct ecb *ahb_soonest = (struct ecb *)0; -struct ecb *ahb_latest = (struct ecb *)0; -long int ahb_furtherest = 0; /* longest time in the timeout queue */ /* */ struct ahb_data @@ -338,7 +328,8 @@ struct scsi_switch ahb_switch = 0, 0, ahb_adapter_info, - 0,0,0 + "ahb", + 0,0 }; /* */ @@ -499,6 +490,7 @@ struct isa_dev *dev; * If it's there, put in it's interrupt vectors * \***********************************************/ #ifdef MACH + dev->dev_pic = ahb_data[unit].vect; #if defined(OSF) /* OSF */ chp->ih_level = dev->dev_pic; chp->ih_handler = dev->dev_intr[0]; @@ -512,7 +504,6 @@ struct isa_dev *dev; else panic("Unable to add ahb interrupt handler"); #else /* CMU */ - dev->dev_pic = ahb_data[unit].vect; take_dev_irq(dev); #endif /* !defined(OSF) */ printf("port=%x spl=%d\n", dev->dev_addr, dev->dev_spl); @@ -520,7 +511,6 @@ struct isa_dev *dev; #ifdef __386BSD__ /* 386BSD */ dev->id_irq = (1 << ahb_data[unit].vect); dev->id_drq = -1; /* use EISA dma */ - printf("\n **"); #endif __386BSD__ ahb_unit++; @@ -535,11 +525,6 @@ struct isa_dev *dev; { int unit = dev->dev_unit; - -#ifdef __386BSD__ - printf(" probing for scsi devices**\n"); -#endif __386BSD__ - /***********************************************\ * ask the adapter what subunits are present * \***********************************************/ @@ -547,13 +532,6 @@ struct isa_dev *dev; #if defined(OSF) ahb_attached[unit]=1; #endif /* defined(OSF) */ - if(!unit) /* only one for all boards */ - { - ahb_timeout(0); - } -#ifdef __386BSD__ - printf("ahb%d",unit); -#endif __386BSD__ return; } @@ -581,8 +559,10 @@ ahbintr(unit) int port = ahb_data[unit].baseport; +#ifdef AHBDEBUG if(scsi_debug & PRINTROUTINES) printf("ahbintr "); +#endif /*AHBDEBUG*/ #if defined(OSF) if (!ahb_attached[unit]) @@ -601,8 +581,10 @@ ahbintr(unit) stat = ahbstat & G2INTST_INT_STAT; mboxval = inl(port + MBOXIN0);/* don't know this will work */ outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT); +#ifdef AHBDEBUG if(scsi_debug & TRACEINTERRUPTS) printf("status = 0x%x ",stat); +#endif /*AHBDEBUG*/ /***********************************************\ * Process the completed operation * \***********************************************/ @@ -625,9 +607,13 @@ ahbintr(unit) ahb_data[unit].immed_ecb = 0; break; case AHB_ASN: /* for target mode */ + printf("ahb%d: Unexpected ASN interrupt(%x)\n", + unit, mboxval); ecb = 0; break; case AHB_HW_ERR: + printf("ahb%d: Hardware error interrupt(%x)\n", + unit, mboxval); ecb = 0; break; case AHB_ECB_RECOVERED: @@ -643,13 +629,15 @@ ahbintr(unit) } if(ecb) { +#ifdef AHBDEBUG if(ahb_debug & AHB_SHOWCMDS ) { ahb_show_scsi_cmd(ecb->xs); } if((ahb_debug & AHB_SHOWECBS) && ecb) printf("",ecb); - ahb_remove_timeout(ecb); +#endif /*AHBDEBUG*/ + untimeout(ahb_timeout,ecb); ahb_done(unit,ecb,((stat == AHB_ECB_OK)?SUCCESS:FAIL)); } } @@ -669,8 +657,10 @@ struct ecb *ecb; struct scsi_sense_data *s1,*s2; struct scsi_xfer *xs = ecb->xs; +#ifdef AHBDEBUG if(scsi_debug & (PRINTROUTINES | TRACEINTERRUPTS)) printf("ahb_done "); +#endif /*AHBDEBUG*/ /***********************************************\ * Otherwise, put the results of the operation * * into the xfer and call whoever started it * @@ -706,19 +696,23 @@ struct ecb *ecb; case HS_CMD_ABORTED_ADAPTER: /* No response */ break; case HS_TIMED_OUT: /* No response */ +#ifdef AHBDEBUG if (ahb_debug & AHB_SHOWMISC) { printf("timeout reported back\n"); } +#endif /*AHBDEBUG*/ xs->error = XS_TIMEOUT; break; default: /* Other scsi protocol messes */ xs->error = XS_DRIVER_STUFFUP; +#ifdef AHBDEBUG if (ahb_debug & AHB_SHOWMISC) { printf("unexpected ha_status: %x\n", stat->ha_status); } +#endif /*AHBDEBUG*/ } } @@ -735,11 +729,13 @@ struct ecb *ecb; xs->error = XS_BUSY; break; default: +#ifdef AHBDEBUG if (ahb_debug & AHB_SHOWMISC) { printf("unexpected targ_status: %x\n", stat->targ_status); } +#endif /*AHBDEBUG*/ xs->error = XS_DRIVER_STUFFUP; } } @@ -759,8 +755,10 @@ struct ecb *ecb; { unsigned int opri; +#ifdef AHBDEBUG if(scsi_debug & PRINTROUTINES) printf("ecb%d(0x%x)> ",unit,flags); +#endif /*AHBDEBUG*/ if (!(flags & SCSI_NOMASK)) opri = splbio(); @@ -787,8 +785,10 @@ ahb_get_ecb(unit,flags) unsigned opri; struct ecb *rc; +#ifdef AHBDEBUG if(scsi_debug & PRINTROUTINES) printf("bp) flags |= (SCSI_NOSLEEP); /* just to be sure */ if(flags & ITSDONE) { - printf("Already done?"); + printf("ahb%d: Already done?",unit); xs->flags &= ~ITSDONE; } if(!(flags & INUSE)) { - printf("Not in use?"); + printf("ahb%d: Not in use?",unit); xs->flags |= INUSE; } if (!(ecb = ahb_get_ecb(unit,flags))) @@ -989,12 +991,14 @@ struct scsi_xfer *xs; } cheat = ecb; +#ifdef AHBDEBUG if(ahb_debug & AHB_SHOWECBS) printf("",ecb); if(scsi_debug & SHOWCOMMANDS) { ahb_show_scsi_cmd(xs); } +#endif /*AHBDEBUG*/ ecb->xs = xs; /***********************************************\ * If it's a reset, we need to do an 'immediate' * @@ -1014,7 +1018,7 @@ cheat = ecb; { s = splbio(); ahb_send_immed(unit,xs->targ,AHB_TARG_RESET); - ahb_add_timeout(ecb,xs->timeout); + timeout(ahb_timeout,ecb,(xs->timeout * hz)/1000); splx(s); return(SUCCESSFULLY_QUEUED); } @@ -1024,8 +1028,10 @@ cheat = ecb; /***********************************************\ * If we can't use interrupts, poll on completion* \***********************************************/ +#ifdef AHBDEBUG if(scsi_debug & TRACEINTERRUPTS) printf("wait "); +#endif /*AHBDEBUG*/ if( ahb_poll(unit,xs->timeout)) { ahb_free_ecb(unit,ecb,flags); @@ -1064,10 +1070,12 @@ cheat = ecb; { sg->addr = (physaddr)iovp->iov_base; xs->datalen += sg->len = iovp->iov_len; +#ifdef AHBDEBUG if(scsi_debug & SHOWSCATGATH) printf("(0x%x@0x%x)" ,iovp->iov_len ,iovp->iov_base); +#endif /*AHBDEBUG*/ sg++; iovp++; seg++; @@ -1080,8 +1088,10 @@ cheat = ecb; * Set up the scatter gather block * \***********************************************/ +#ifdef AHBDEBUG if(scsi_debug & SHOWSCATGATH) printf("%d @0x%x:- ",xs->datalen,xs->data); +#endif /*AHBDEBUG*/ datalen = xs->datalen; thiskv = (int)xs->data; thisphys = KVTOPHYS(thiskv); @@ -1093,8 +1103,10 @@ cheat = ecb; /* put in the base address */ sg->addr = thisphys; +#ifdef AHBDEBUG if(scsi_debug & SHOWSCATGATH) printf("0x%x",thisphys); +#endif /*AHBDEBUG*/ /* do it at least once */ nextphys = thisphys; @@ -1123,16 +1135,20 @@ cheat = ecb; /********************************************\ * next page isn't contiguous, finish the seg * \********************************************/ +#ifdef AHBDEBUG if(scsi_debug & SHOWSCATGATH) printf("(0x%x)",bytes_this_seg); +#endif /*AHBDEBUG*/ sg->len = bytes_this_seg; sg++; seg++; } } /*end of iov/kv decision */ ecb->datalen = seg * sizeof(struct ahb_dma_seg); +#ifdef AHBDEBUG if(scsi_debug & SHOWSCATGATH) printf("\n"); +#endif /*AHBDEBUG*/ if (datalen) { /* there's still data, must have run out of segs! */ printf("ahb_scsi_cmd%d: more than %d DMA segs\n", @@ -1160,18 +1176,22 @@ cheat = ecb; { s = splbio(); ahb_send_mbox(unit,OP_START_ECB,xs->targ,ecb); - ahb_add_timeout(ecb,xs->timeout); + timeout(ahb_timeout,ecb,(xs->timeout * hz)/1000); splx(s); +#ifdef AHBDEBUG if(scsi_debug & TRACEINTERRUPTS) printf("cmd_sent "); +#endif /*AHBDEBUG*/ return(SUCCESSFULLY_QUEUED); } /***********************************************\ * If we can't use interrupts, poll on completion* \***********************************************/ ahb_send_mbox(unit,OP_START_ECB,xs->targ,ecb); +#ifdef AHBDEBUG if(scsi_debug & TRACEINTERRUPTS) printf("cmd_wait "); +#endif /*AHBDEBUG*/ do { if(ahb_poll(unit,xs->timeout)) @@ -1184,12 +1204,9 @@ cheat = ecb; ahb_free_ecb(unit,ecb,flags); } xs->error = XS_DRIVER_STUFFUP; - splx(s); return(HAD_ERROR); } } while (!(xs->flags & ITSDONE));/* something (?) else finished */ - splx(s); -scsi_debug = 0;ahb_debug = 0; if(xs->error) { return(HAD_ERROR); @@ -1197,172 +1214,55 @@ scsi_debug = 0;ahb_debug = 0; return(COMPLETE); } -/* - * +----------+ +----------+ +----------+ - * ahb_soonest--->| later |--->| later|--->| later|--->0 - * | [Delta] | | [Delta] | | [Delta] | - * 0<---|sooner |<---|sooner |<---|sooner |<---ahb_latest - * +----------+ +----------+ +----------+ - * - * ahb_furtherest = sum(Delta[1..n]) - */ -ahb_add_timeout(ecb,time) -struct ecb *ecb; -int time; -{ - int timeprev; - struct ecb *prev; - int s = splbio(); - - if(prev = ahb_latest) /* yes, an assign */ - { - timeprev = ahb_furtherest; - } - else - { - timeprev = 0; - } - while(prev && (timeprev > time)) - { - timeprev -= prev->delta; - prev = prev->sooner; - } - if(prev) - { - ecb->delta = time - timeprev; - if( ecb->later = prev->later) /* yes an assign */ - { - ecb->later->sooner = ecb; - ecb->later->delta -= ecb->delta; - } - else - { - ahb_furtherest = time; - ahb_latest = ecb; - } - ecb->sooner = prev; - prev->later = ecb; - } - else - { - if( ecb->later = ahb_soonest) /* yes, an assign*/ - { - ecb->later->sooner = ecb; - ecb->later->delta -= time; - } - else - { - ahb_furtherest = time; - ahb_latest = ecb; - } - ecb->delta = time; - ecb->sooner = (struct ecb *)0; - ahb_soonest = ecb; - } - splx(s); -} -ahb_remove_timeout(ecb) -struct ecb *ecb; +ahb_timeout(struct ecb *ecb) { - int s = splbio(); + int unit; + int s = splbio(); - if(ecb->sooner) - { - ecb->sooner->later = ecb->later; - } - else + unit = ecb->xs->adapter; + printf("ahb%d:%d device timed out\n",unit + ,ecb->xs->targ); +#ifdef AHBDEBUG + if(ahb_debug & AHB_SHOWECBS) + ahb_print_active_ecb(unit); +#endif /*AHBDEBUG*/ + + /***************************************\ + * If it's immediate, don't try abort it * + \***************************************/ + if(ecb->flags & ECB_IMMED) { - ahb_soonest = ecb->later; + ecb->xs->retries = 0; /* I MEAN IT ! */ + ecb->flags |= ECB_IMMED_FAIL; + ahb_done(unit,ecb,FAIL); + splx(s); + return; } - if(ecb->later) + /***************************************\ + * If it has been through before, then * + * a previous abort has failed, don't * + * try abort again * + \***************************************/ + if(ecb->flags == ECB_ABORTED) /* abort timed out */ { - ecb->later->sooner = ecb->sooner; - ecb->later->delta += ecb->delta; + printf("AGAIN"); + ecb->xs->retries = 0; /* I MEAN IT ! */ + ecb->ecb_status.ha_status = HS_CMD_ABORTED_HOST; + ahb_done(unit,ecb,FAIL); } - else + else /* abort the operation that has timed out */ { - ahb_latest = ecb->sooner; - ahb_furtherest -= ecb->delta; + printf("\n"); + ahb_send_mbox(unit,OP_ABORT_ECB,ecb->xs->targ,ecb); + /* 2 secs for the abort */ + timeout(ahb_timeout,ecb,2 * hz); + ecb->flags = ECB_ABORTED; } - ecb->sooner = ecb->later = (struct ecb *)0; splx(s); } - -extern int hz; -#define ONETICK 500 /* milliseconds */ -#define SLEEPTIME ((hz * 1000) / ONETICK) -ahb_timeout(arg) -int arg; -{ - struct ecb *ecb; - int unit; - int s = splbio(); - - while( ecb = ahb_soonest ) - { - if(ecb->delta <= ONETICK) - /***********************************************\ - * It has timed out, we need to do some work * - \***********************************************/ - { - unit = ecb->xs->adapter; - printf("ahb%d:%d device timed out\n",unit - ,ecb->xs->targ); - if(ahb_debug & AHB_SHOWECBS) - ahb_print_active_ecb(); - - /***************************************\ - * Unlink it from the queue * - \***************************************/ - ahb_remove_timeout(ecb); - - /***************************************\ - * If it's immediate, don't try abort it * - \***************************************/ - if(ecb->flags & ECB_IMMED) - { - ecb->xs->retries = 0; /* I MEAN IT ! */ - ecb->flags |= ECB_IMMED_FAIL; - ahb_done(unit,ecb,FAIL); - continue; - } - /***************************************\ - * If it has been through before, then * - * a previous abort has failed, don't * - * try abort again * - \***************************************/ - if(ecb->flags == ECB_ABORTED) /* abort timed out */ - { - printf("AGAIN"); - ecb->xs->retries = 0; /* I MEAN IT ! */ - ecb->ecb_status.ha_status = HS_CMD_ABORTED_HOST; - ahb_done(unit,ecb,FAIL); - } - else /* abort the operation that has timed out */ - { - printf("\n"); - ahb_send_mbox(unit,OP_ABORT_ECB,ecb->xs->targ,ecb); - /* 2 secs for the abort */ - ahb_add_timeout(ecb,2000 + ONETICK); - ecb->flags = ECB_ABORTED; - } - } - else - /***********************************************\ - * It has not timed out, adjust and leave * - \***********************************************/ - { - ecb->delta -= ONETICK; - ahb_furtherest -= ONETICK; - break; - } - } - splx(s); - timeout(ahb_timeout,arg,SLEEPTIME); -} - +#ifdef AHBDEBUG ahb_show_scsi_cmd(struct scsi_xfer *xs) { u_char *b = (u_char *)xs->cmd; @@ -1397,24 +1297,26 @@ struct ecb *ecb; ,ecb->opcode ,ecb->cdblen ,ecb->senselen); - printf(" datlen:%d hstat:%x tstat:%x delta:%d flags:%x\n" + printf(" datlen:%d hstat:%x tstat:%x flags:%x\n" ,ecb->datalen ,ecb->ecb_status.ha_status ,ecb->ecb_status.targ_status - ,ecb->delta ,ecb->flags); ahb_show_scsi_cmd(ecb->xs); } -ahb_print_active_ecb() +ahb_print_active_ecb(int unit) { - struct ecb *ecb; - ecb = ahb_soonest; + struct ecb *ecb = ahb_data[unit].ecbs; + int i = NUM_CONCURRENT; - while(ecb) + while(i--) { - ahb_print_ecb(ecb); - ecb = ecb->later; + if(ecb->flags != ECB_FREE) + { + ahb_print_ecb(ecb); + } + ecb++; } - printf("Furtherest = %d\n",ahb_furtherest); } +#endif /*AHBDEBUG */ diff --git a/sys/i386/isa/as.c b/sys/i386/isa/as.c deleted file mode 100644 index 48240e2365..0000000000 --- a/sys/i386/isa/as.c +++ /dev/null @@ -1,1334 +0,0 @@ -/* - * Adaptech 1542 SCSI driver for 386bsd - * - * Pace Willisson pace@blitz.com March 28, 1992 - * - * Placed in the public domain with NO WARRANTIES, not even the - * implied warranties for MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. - * - * - * This is a very early version - use with care. - * - * Here is the config info: - * - * controller as0 at isa? port 0x330 bio irq 11 drq 5 vector asintr - * disk dk6 at as0 drive 0 - * - * Also, don't forget to update sys/i386/conf/files.i386. - * - * So far, used with: - * - * CDC WREN 5 600 Megabyte magnetic disk - * EXABYTE EXB-8200 8mm tape drive - * SONY CDU-541 cdrom - * - * The the tape stuff still needs a lot of working concerning - * file marks, end of tape handling and rewinding, but I have - * extracted tar tapes to a file system mounted on the CDC disk. - * - * minor number bits: - * - * 7 6 5 4 3 2 1 0 - * +-----+ partition number - * +-----+ scsi target number - * +--+ unused (should be 0) - * - * For tape drives, set the partition number to 0 for regular, - * 1 for no rewind. - * - * Only supports LUN 0. - * - * To use with a read-write disk, first use diskpart to create - * a disktab entry, then use disklabel. Since I don't have - * the boot programs done yet, I faked it with: - * - * # cd /usr/mdec - * # cp wdboot asboot - * # cp bootwd bootas - * - * Now you can run disklabel, newfs, etc. - * - * Please send patches and names other perpherials that work to - * pace@blitz.com. If you have trouble that you can't fix, please - * wait for the next release before contacting me. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00088 - * -------------------- ----- ---------------------- - * - * 23 Oct 92 Joerg Lohse changed ccb opcode for compatibility - * with Adaptec AHA-1542A - * 27 Feb 93 James da Silva Tapedrive fixes. - */ - -#include "as.h" -#if NAS > 0 - -#include "param.h" -#include "dkbad.h" -#include "systm.h" -#include "conf.h" -#include "file.h" -#include "stat.h" -#include "ioctl.h" -#include "disklabel.h" -#include "buf.h" -#include "uio.h" -#include "i386/isa/isa_device.h" -#include "i386/isa/icu.h" -#include "syslog.h" -#include "vm/vm.h" -#include "kernel.h" - -#include "asreg.h" - -int asstrategy (); -int asabort (); -extern int hz; - -int asverbose = 0; - - -/* target id 7 is the controller itself */ -#define NTARGETS 7 - -struct mailbox_entry mailboxes[NTARGETS * 2] = {0}; - -#define b_cylin b_resid /* fake cylinder number for disksort */ - -/* maximum scatter list size for Adaptech controller */ -#define NSCATTER 17 - -/* this array must reside in contiguous physical memory */ -struct asinfo { - dev_t dev; - struct buf requests; - struct mailbox_entry *mailbox; - int active; - struct ccb ccb; - unsigned int ccb_phys; - char scatter_list[NSCATTER * 6]; - - struct disklabel label; - struct dos_partition dospart[NDOSPART]; - int have_label; - - int scsi_lock; - struct buf *scsi_bp; - int scsi_cdb_len; - unsigned char scsi_cdb[MAXCDB]; - - int tape; /* sequential */ - int disk; /* nonsequential */ - int read_only; /* CDROM */ - int removable; /* unsupported and tested */ - char vendor[9]; - char model[17]; - char revision[5]; - int bs; /* device block size */ - - int open_lock; - int open; - int units_open; - - int wlabel; - - int retry_count; - int start_time; - int restart_pending; - -} asinfo[NTARGETS] = {0}; - -#define dev_part(dev) (minor (dev) & 7) -#define dev_target(dev) ((minor (dev) >> 3) & 7) -#define dev_rewind(dev) ((minor (dev) & 1) == 0) - -#define makeasdev(major, target, part) \ - makedev ((major), ((target) << 3) | (part)) - -int as_port; - -int asprobe(struct isa_device *), asattach(struct isa_device *), - asintr(dev_t); - -struct isa_driver asdriver = { - asprobe, asattach, "as", -}; - -int -asprobe (struct isa_device *dvp) -{ - int val; - - as_port = dvp->id_iobase; - - outb (as_port + AS_CONTROL, AS_CONTROL_SRST); - DELAY (30000); - val = inb (as_port + AS_STATUS); - - if (val == (AS_STATUS_INIT | AS_STATUS_IDLE)) - return (1); - as_port = 0; - return (0); -} - -asattach (struct isa_device *dvp) -{ - int i; - unsigned int physaddr; - int val; - int s; - - for (i = 0; i < NTARGETS; i++) { - asinfo[i].mailbox = &mailboxes[i]; - asinfo[i].ccb_phys = vtophys (&asinfo[i].ccb); - } - - isa_dmacascade(dvp->id_drq); - - physaddr = vtophys (mailboxes); - - s = splbio (); - if (as_put_byte (AS_CMD_MAILBOX_INIT) < 0 - || as_put_byte (NTARGETS) < 0 - || as_put_byte (physaddr >> 16) < 0 - || as_put_byte (physaddr >> 8) < 0 - || as_put_byte (physaddr) < 0) { - splx (s); - return (EIO); - } - splx (s); - DELAY (300); - val = inb (as_port + AS_STATUS); - - if (val & AS_STATUS_INIT) - printf ("as: mailbox init error: 0x%x\n", val); -} - -int -ascmd (as, bp, direction, count, retrycount) -struct asinfo *as; -struct buf *bp; -int direction; -int count; -int retrycount; -{ - int err; - - do { - if (asverbose) - printf ("ascmd "); - bp->b_bcount = count; - bp->b_error = 0; - bp->b_flags &= ~(B_READ | B_ERROR | B_DONE); - if (direction == B_READ) - bp->b_flags |= B_READ; - - bp->b_dev = as->dev; - bp->b_blkno = 0; - - as->scsi_bp = bp; - /* scsi_cdb, scsi_cdb_len set up by caller */ - - asstrategy (bp); - err = biowait (bp); - as->scsi_bp = NULL; - - } while (err && --retrycount); - - return (err); -} - -asstring (dest, src, size) -char *dest; -char *src; -int size; -{ - size--; - bcopy (src, dest, size); - while (size > 0 && dest[size - 1] == ' ') - size--; - dest[size] = 0; -} - -asopen (dev, flag) -dev_t dev; -int flag; -{ - struct asinfo *as; - unsigned int physaddr; - struct buf *bp = NULL; - int retry; - unsigned char *cdb; - char *p, *q; - int n; - int error; - char vendor[9]; - char model[17]; - int disksize; - - if (as_port == 0 || dev_target (dev) >= NTARGETS) - return (ENXIO); - - as = &asinfo[dev_target (dev)]; - as->dev = dev; - - while (as->open_lock) - if (error = tsleep ((caddr_t)as, PZERO|PCATCH, "scsiopen", 0)) - return (error); - - if (as->open) { - if (as->tape) - return (EBUSY); - - if (as->have_label == 0 && dev_part (dev) != 3) - return (ENXIO); - - as->units_open |= 1 << dev_part (dev); - return (0); - } - - as->open_lock = 1; - - /* it seems like we might have to block here in case someone - * opens the device just after someone else closes - */ - while (as->scsi_lock) - if (error = tsleep ((caddr_t)as, PZERO|PCATCH, "scsicmd", 0)) - return (error); - - as->scsi_lock = 1; - - error = EIO; - - as->have_label = 0; - as->tape = 0; - as->disk = 0; - as->read_only = 0; - as->removable = 0; - bcopy(as->vendor, vendor, sizeof(vendor)); - bcopy(as->model, model, sizeof(model)); - as->vendor[0] = 0; - as->model[0] = 0; - as->revision[0] = 0; - - bp = geteblk (DEV_BSIZE); - - if (asverbose) { - printf ("openbuf = 0x%x phys 0x%x\n", - bp->b_un.b_addr, vtophys (bp->b_un.b_addr)); - printf ("mailboxes = 0x%x\n", mailboxes); - } - - /* first, find out if a device is present, and just what it is */ - as->scsi_cdb_len = 6; - cdb = as->scsi_cdb; - bzero (cdb, 6); - cdb[0] = 0x12; /* INQUIRY */ - cdb[4] = 255; /* allocation length */ - if (error = ascmd (as, bp, B_READ, DEV_BSIZE, 2)) - /* does not respond to inquiry, obviously not CCS, give up */ - goto done; - - - /* blather on console about it */ - p = bp->b_un.b_addr; - if (asverbose) { - printf ("inquiry: "); - for (n = 0; n < 20; n++) - printf ("%x ", p[n] & 0xff); - printf ("\n"); - for (n = 0; n < 40; n++) { - if (p[n] >= ' ' && p[n] < 0177) - printf ("%c", p[n]); - else - printf ("."); - } - printf ("\n"); - } - - switch (p[0]) { - case 0: /* normal disk */ - case 4: /* write once disk */ - as->disk = 1; - break; - case 5: /* read only disk */ - as->read_only = 1; - as->disk = 1; - break; - case 1: /* tape */ - as->tape = 1; - break; - case 0x7f: - printf ("logical unit not present\n"); - goto done; - default: - printf ("unknown peripheral device type: 0x%x\n", p[0]); - goto done; - } - - as->removable = (p[1] & 0x80) ? 1 : 0; - - n = p[4] & 0xff; - if (n >= 31) { - asstring (as->vendor, p + 8, sizeof as->vendor); - asstring (as->model, p + 16, sizeof as->model); - asstring (as->revision, p + 32, sizeof as->revision); - } - - if(bcmp(as->vendor,vendor, sizeof(vendor)) != 0 || - bcmp(as->model,model, sizeof(model)) != 0) { - printf("as%d: attached tgt %d <%s %s %s> ", 0, dev_target(dev), - as->vendor, as->model, as->revision); - if (as->read_only) printf("readonly "); - if (!as->removable) printf("winchester "); - if (as->tape) printf("tape "); - if (as->disk) printf("disk "); - printf("\n"); - } - - /* probe for desired block size */ - - /* assume default of 512, except if CDROM (2048) */ - if (as->read_only) - as->bs = 2048; - else - as->bs = 512; - - bzero(cdb, 6); - cdb[0] = 0x1A; /* SCSI_MDSENSE */ - cdb[4] = 255; - if (as->tape && ascmd (as, bp, B_READ, 12, 2) == 0) { - int minblk, maxblk; - -#ifdef notdef - /* blather about device more */ - if(bcmp(as->vendor,vendor, sizeof(vendor)) != 0 || - bcmp(as->model,model, sizeof(model)) != 0) { - p = bp->b_un.b_addr; - printf("as%d: data len %d medium %d speed/bufmode 0x%x desc len %d\n", - dev_target(dev), p[0], p[1], p[2], p[3]); - printf("as%d: density %d nblocks %d block len %d\n", - dev_target(dev), p[4], - (long)p[5]*65536+p[6]*256+p[7], - (long)p[9]*65536+p[10]*256+p[11]); - } -#endif - - /* obtain possible block sizes */ - bzero(cdb, 6); - cdb[0] = 0x05; /* SCSI_RDLIMITS; */ - if (ascmd (as, bp, B_READ, 12, 2) == 0) { - p = bp->b_un.b_addr; - minblk = p[4]*256+p[5]; - maxblk = p[1]*65536+p[2]*256+p[3]; -#ifdef notdef - if(bcmp(as->vendor,vendor, sizeof(vendor)) != 0 || - bcmp(as->model,model, sizeof(model)) != 0) { - printf("as%d: limits: min block len %ld max block len %ld\n", - dev_target(dev), minblk, maxblk); - } -#endif - if ( minblk == maxblk ) - as->bs = minblk; - else if (as->tape) - as->bs = 1; - } - } - - as->scsi_cdb_len = 10; - bzero(cdb, 10); - cdb[0] = 0x25; /* SCSI_READCAPACITY */ - disksize = 0; - if (as->disk && ascmd (as, bp, B_READ, 12, 2) == 0) { - p = bp->b_un.b_addr; - disksize = ntohl(*(long *)p); - as->bs = ntohl(*(long *)(p+4)); - - } - -if(asverbose) - printf("block size %d disksize %d ", as->bs, disksize); - - - /* for standard disk, negotiate block size */ - if (as->read_only == 0 && as->disk) { - /* do mode select to set the logical block size */ - as->scsi_cdb_len = 6; - cdb = as->scsi_cdb; - bzero (cdb, 6); - cdb[0] = 0x15; /* MODE SELECT */ - cdb[4] = 12; /* parameter list length */ - - p = bp->b_un.b_addr; - bzero (p, 12); - p[3] = 8; /* block descriptor length */ - n = as->bs == 1 ? 0 : as->bs; - p[9] = n >> 16; - p[10] = n >> 8; - p[11] = n; - - (void) ascmd (as, bp, B_WRITE, 12, 2); - } - - /* device online and ready? */ - as->scsi_cdb_len = 6; - bzero(cdb, 6); - cdb[0] = 0x00; /* SCSI_UNITRDY */ - if (error = ascmd (as, bp, B_READ, 12, 2)) { - printf("as%d: drive not online\n", dev_target(dev)); - goto done; - } - - if (as->disk && as->read_only == 0) { - /* read disk label */ - bzero ((caddr_t)&as->label, sizeof as->label); - as->label.d_secsize = as->bs; - as->label.d_secpercyl = 64*32; - as->label.d_type = DTYPE_SCSI; - - - /* read label using "d" partition */ - if ((p = readdisklabel ( - makeasdev (major (dev), dev_target (dev), 3), - asstrategy, &as->label, as->dospart, 0, 0)) == NULL){ - as->have_label = 1; - } else { - if (disksize) { - as->label.d_subtype = DSTYPE_GEOMETRY; - as->label.d_npartitions = 3; - /* partition 0 holds bios, partition 1 ESDI */ - as->label.d_partitions[2].p_size = disksize; - as->label.d_partitions[2].p_offset = 0; - } - if (asverbose || dev_part (dev) != 3) - printf ("error reading label: %s\n", p); - if (dev_part (dev) != 3) { - error = EINVAL; - goto done; - } - } - } - - /* may want to set logical block size here ? */ - error = 0; - - done: - if (bp) { - bp->b_flags |= B_INVAL | B_AGE; - brelse (bp); - } - - if (error == 0) - as->open = 1; - - as->open_lock = 0; - as->scsi_lock = 0; - wakeup (as); - - return (error); -} - -asclose (dev, flag) -dev_t dev; -{ - struct asinfo *as; - int error = 0; - unsigned char *cdb; - struct buf *bp; - int n; - - as = &asinfo[dev_target (dev)]; - - while (as->open_lock) - if (error = tsleep ((caddr_t)as, PZERO|PCATCH, "scsiclose", 0)) - return (error); - - as->open_lock = 1; - - if (as->tape) { - while (as->scsi_lock) - if (error = tsleep ((caddr_t)as, PZERO|PCATCH, - "scsicmd", 0)) - return (error); - - as->scsi_lock = 1; - - bp = geteblk (DEV_BSIZE); - - if ((flag & FWRITE) != 0) { - /* presume user will use tape again */ - as->scsi_cdb_len = 6; - cdb = as->scsi_cdb; - bzero (cdb, 6); - cdb[0] = 0x10; /* write filemarks */ - cdb[4] = 1; /* one of them */ - error = ascmd (as, bp, B_READ, 0, 1); - } - if (dev_rewind (dev) || error) { - if ( error == 0 && (flag & FWRITE) != 0) { - /* presumption error correction */ - as->scsi_cdb_len = 6; - cdb = as->scsi_cdb; - bzero (cdb, 6); - cdb[0] = 0x10; /* write filemarks */ - cdb[4] = 1; /* one of them */ - error |= ascmd (as, bp, B_READ, 0, 1); - } - as->scsi_cdb_len = 6; - cdb = as->scsi_cdb; - bzero (cdb, 6); - cdb[0] = 0x1; /* rewind */ - cdb[1] = 1; /* don't wait until done */ - error |= ascmd (as, bp, B_READ, 0, 1); - } -#ifdef notdef - } else { - cdb[0] = 0x11; /* backspace */ - cdb[1] = 1; /* look at filemarks (instead of blocks) */ - n = -1; - cdb[2] = n >> 16; - cdb[3] = n >> 8; - cdb[4] = n; - error = ascmd (as, bp, B_READ, 0, 1); - } -#endif - - bp->b_flags |= B_INVAL | B_AGE; - brelse (bp); - - as->scsi_lock = 0; - } - - as->units_open &= ~(1 << dev_part (dev)); - - if (as->units_open == 0) - as->open = 0; - - as->open_lock = 0; - - wakeup (as); - - return (error); -} - -int -asioctl (dev, cmd, addr, flag) -dev_t dev; -int cmd; -caddr_t addr; -int flag; -{ - struct scsicmd *cmdp; - struct asinfo *as; - int ccblen; - struct buf *bp; - int error = 0; - int direction; - struct disklabel *dl; - int old_wlabel; - - as = &asinfo[dev_target (dev)]; - - switch (cmd) { - case DIOCGDINFO: - *(struct disklabel *)addr = as->label; - break; - - case DIOCSDINFO: - if ((flag & FWRITE) == 0) { - error = EBADF; - break; - } - dl = (struct disklabel *)addr; - if (error = setdisklabel(&as->label, dl, 0, as->dospart)) - break; - as->have_label = 1; - break; - - case DIOCWLABEL: - if ((flag & FWRITE) == 0) { - error = EBADF; - break; - } - as->wlabel = *(int *)addr; - break; - - case DIOCWDINFO: - if ((flag & FWRITE) == 0) { - error = EBADF; - break; - } - - dl = (struct disklabel *)addr; - - if (error = setdisklabel (&as->label, dl, 0, as->dospart)) - break; - - as->have_label = 1; - - old_wlabel = as->wlabel; - as->wlabel = 1; - error = writedisklabel(dev, asstrategy, &as->label, - as->dospart); - as->wlabel = old_wlabel; - break; - - case SCSICMD: - cmdp = (struct scsicmd *)addr; - - /* limited by max sizeof of geteblk */ - if (cmdp->datalen >= 8192 - || cmdp->cdblen >= MAXCDB) { - error = EINVAL; - break; - } - - ccblen = cmdp->ccblen; - if (ccblen > sizeof (struct ccb)) - ccblen = sizeof (struct ccb); - - while (as->scsi_lock) - if (error = tsleep ((caddr_t)as, PZERO|PCATCH, - "scsicmd", 0)) - break; - - as->scsi_lock = 1; - - bp = geteblk (cmdp->datalen); - - as->scsi_cdb_len = cmdp->cdblen; - if (error = copyin (cmdp->cdb, as->scsi_cdb, cmdp->cdblen)) - goto done; - - direction = cmdp->readflag ? B_READ : B_WRITE; - - if (direction == B_WRITE) - if (error = copyin (cmdp->data, - bp->b_un.b_addr, cmdp->datalen)) - goto done; - - ascmd (as, bp, direction, cmdp->datalen, 1); - - copyout (&as->ccb, cmdp->ccb, ccblen); - if (direction == B_READ) - copyout (bp->b_un.b_addr, cmdp->data, cmdp->datalen); - done: - bp->b_flags |= B_INVAL | B_AGE; - brelse (bp); - as->scsi_lock = 0; - wakeup (as); - break; - default: - error = ENOTTY; - break; - } - return (error); -} - -int -asstrategy (bp) -struct buf *bp; -{ - struct asinfo *as; - int s; - - if (asverbose) - printf ("asstrategy %d %d ", bp->b_blkno, bp->b_bcount); - s = splbio (); - - as = &asinfo[dev_target (bp->b_dev)]; - - if (as->tape) { - bp->av_forw = NULL; - if (as->requests.b_actf) - as->requests.b_actl->av_forw = bp; - else - as->requests.b_actf = bp; - as->requests.b_actl = bp; - } else { - if (bp != as->scsi_bp - && as->have_label == 0 - && dev_part (bp->b_dev) != 3) - goto bad; - - bp->b_cylin = bp->b_blkno; - disksort (&as->requests, bp); - } - - if (as->active == 0) - asstart (as); - - splx (s); - return; - - bad: - bp->b_flags |= B_ERROR; - biodone (bp); -} - -asrestart (as) -struct asinfo *as; -{ - int s; - s = splbio (); - as->restart_pending = 0; - as->retry_count++; - asstart (as); - splx (s); -} - -asstart (as) -struct asinfo *as; -{ - struct buf *bp; - int blknum; - unsigned int physaddr; - struct ccb *ccb; - unsigned char *cdb; - int target; - char *p; - int n; - char *sp; - int nscatter; - int thistime; - int nbytes; - struct partition *part; - int blkno; - int nblocks; - int total; - int bs = as->bs; - - - if (as->restart_pending) { - as->restart_pending = 0; - untimeout (asrestart, as); - } - - again: - if ((bp = as->requests.b_actf) == NULL) - return; - - bp->b_error = 0; - - if (asverbose) - printf ("asstart %x ", bp); - - if (as->mailbox->cmd != 0) { - /* this can't happen, unless the card flakes */ - printf ("asstart: mailbox not available\n"); - bp->b_error = EIO; - goto bad; - } - - if (as->retry_count == 0) { - as->start_time = time.tv_sec; - } else { - if (time.tv_sec - as->start_time > 60) { - printf ("as: command timed out\n"); - bp->b_error = EIO; - goto done; - } - } - - if (bp != as->scsi_bp) { - if (bp->b_bcount == 0) - goto done; - - if ((bp->b_bcount % bs) != 0) { - printf("as: partial block read\n"); - bp->b_error = EIO; - goto bad; - } - } - - if (bp != as->scsi_bp) { - - blkno = bp->b_blkno; - nblocks = bp->b_bcount / bs; - - if (as->have_label && dev_part(bp->b_dev) != 3) { - part = &as->label.d_partitions[dev_part (bp->b_dev)]; - - if (blkno > part->p_size) { - bp->b_error = EINVAL; - goto bad; - } - if (blkno == part->p_size) { - bp->b_resid = bp->b_bcount; - goto done; - } - - if (blkno + nblocks >= part->p_size) - nblocks = part->p_size - blkno; - - blkno += part->p_offset; - } else - blkno = (blkno * DEV_BSIZE)/bs; -if(asverbose) - printf("trans %d ", blkno); - if (nblocks > 255) - nblocks = 255; - total = nblocks * bs; -if(asverbose) -printf("total %d nblocks %d ", total, nblocks); - /*bp->b_bcount = total; /* XXX partial tape block read - wrong */ - } else { -#ifdef nomore - if (as->fixed == 0) { - total = bp->b_bcount; - } else { - total = bp->b_bcount; - blkno = bp->b_blkno; - nblocks = bp->b_bcount / as->fixed; - } -#else - total = bp->b_bcount; -#endif - } - - p = bp->b_un.b_addr; - n = 0; - sp = as->scatter_list; - nscatter = 0; - if (as->tape && as->bs == 1) - total = bp->b_bcount; - while (n < total && nscatter < NSCATTER) { - thistime = page_size - ((vm_offset_t)p - trunc_page (p)); - - if (n + thistime > total) - thistime = total - n; - - physaddr = vtophys (p); - - if (asverbose) - printf ("%d bytes to %x (%x)\n", - thistime, p, physaddr); - sp[0] = thistime >> 16; - sp[1] = thistime >> 8; - sp[2] = thistime; - sp[3] = physaddr >> 16; - sp[4] = physaddr >> 8; - sp[5] = physaddr; - - p += thistime; - n += thistime; - sp += 6; - nscatter++; - } - - if (nscatter == NSCATTER) { - printf("out of range, cannot happen?"); - bp->b_error = ENXIO; - goto bad; - } - - ccb = &as->ccb; - - /* this only needed to make debugging easier */ - bzero ((caddr_t)ccb, sizeof *ccb); - - if (nscatter) - ccb->ccb_opcode = 2; /* scatter cmd, return resid */ - else - ccb->ccb_opcode = 0; - target = dev_target (bp->b_dev); - ccb->ccb_addr_and_control = target << 5; - if (bp->b_bcount != 0) - ccb->ccb_addr_and_control |= (bp->b_flags & B_READ) ? 8 : 0x10; - else - ccb->ccb_addr_and_control |= 0x18; - - nbytes = nscatter * 6; - ccb->ccb_data_len_msb = nbytes >> 16; - ccb->ccb_data_len_mid = nbytes >> 8; - ccb->ccb_data_len_lsb = nbytes; - - ccb->ccb_requst_sense_allocation_len = MAXSENSE; - - physaddr = vtophys (as->scatter_list); - ccb->ccb_data_ptr_msb = physaddr >> 16; - ccb->ccb_data_ptr_mid = physaddr >> 8; - ccb->ccb_data_ptr_lsb = physaddr; - - ccb->ccb_link_msb = 0; - ccb->ccb_link_mid = 0; - ccb->ccb_link_lsb = 0; - ccb->ccb_link_id = 0; - ccb->ccb_host_status = 0; - ccb->ccb_target_status = 0; - ccb->ccb_zero1 = 0; - ccb->ccb_zero2 = 0; - - cdb = ccb->ccb_cdb; - if (bp == as->scsi_bp) { - ccb->ccb_scsi_command_len = as->scsi_cdb_len; - bcopy (as->scsi_cdb, cdb, as->scsi_cdb_len); - } else if (as->tape) { - ccb->ccb_scsi_command_len = 6; - cdb[0] = (bp->b_flags & B_READ) ? 8 : 0xa; - if (as->bs == 1) { - cdb[1] = 0; /* logical unit 0, variable block size */ - cdb[2] = bp->b_bcount >> 16; - cdb[3] = bp->b_bcount >> 8; - cdb[4] = bp->b_bcount; - } else { - cdb[1] = 1; /* fixed block size */ - cdb[2] = nblocks >> 16; - cdb[3] = nblocks >> 8; - cdb[4] = nblocks; - } - cdb[5] = 0; /* control byte (used in linking) */ - } else { - ccb->ccb_scsi_command_len = 10; - cdb[0] = (bp->b_flags & B_READ) ? 0x28 : 0x2a; - cdb[1] = 0; - *(long *) (cdb+2) = htonl(blkno); - *(short *) (cdb+7) = htons(nblocks); - cdb[9] = 0; /* control byte (used in linking) */ - } - -#ifdef notdef - if (asverbose) { - printf ("ccb: "); - for (n = 0; n < 48; n++) - printf ("%02x ", ((unsigned char *)ccb)[n]); - printf ("\n"); - } -#endif - - physaddr = vtophys (ccb); - as->mailbox->msb = physaddr >> 16; - as->mailbox->mid = physaddr >> 8; - as->mailbox->lsb = physaddr; - as->mailbox->cmd = 1; - - /* tell controller to look in its mailbox */ - as_put_byte (AS_CMD_START_SCSI_COMMAND); - as->active = 1; - timeout (asabort, as, hz * 60 * 2); - return; - - bad: - bp->b_flags |= B_ERROR; - done: - asdone (as, 0); - goto again; -} - -asabort (as) -struct asinfo *as; -{ - int s; - int physaddr; - struct buf *bp; - - s = splbio (); - if (as->active) { - printf ("asabort %d\n", as - asinfo); - physaddr = vtophys (&as->ccb); - as->mailbox->msb = physaddr >> 16; - as->mailbox->mid = physaddr >> 8; - as->mailbox->lsb = physaddr; - as->mailbox->cmd = 2; - as_put_byte (AS_CMD_START_SCSI_COMMAND); - - as->active = 0; - bp = as->requests.b_actf; - if (bp) { - bp->b_flags |= B_ERROR; - asdone (as, 1); - } - } - splx (s); -} - -asintr (dev_t dev) -{ - int didwork; - int i, j; - struct mailbox_entry *mp; - unsigned int physaddr; - int val; - - outb (as_port + AS_CONTROL, AS_CONTROL_IRST); -#ifdef notdef - if (asverbose) - printf ("asintr %x ", cpl); -#endif - again: - didwork = 0; - for (i = NTARGETS; i < NTARGETS * 2; i++) { - mp = &mailboxes[i]; - - if ((val = mp->cmd) == 0) - continue; - - didwork = 1; - - physaddr = (mp->msb << 16) - | (mp->mid << 8) - | mp->lsb; - - for (j = 0; j < NTARGETS; j++) { - if (asinfo[j].ccb_phys == physaddr) { - mp->cmd = 0; - asintr1 (&asinfo[j], val); - break; - } - } - if (j == NTARGETS) { - printf ("as: unknown mailbox paddr 0x%x\n", physaddr); - mp->cmd = 0; - } - } - - if (didwork) - goto again; -} - -asintr1 (as, val) -struct asinfo *as; -int val; -{ - struct buf *bp; - struct ccb *ccb; - int n; - int bad; - char *msg; - char msgbuf[100]; - unsigned char *sp; - int i,key; - - if (asverbose) - printf ("asintr1 %x ", val); - if (as->active == 0) { - printf ("as: stray intr 0x%x\n", as->dev); - return; - } - - as->active = 0; - untimeout (asabort, as); - - bp = as->requests.b_actf; - ccb = &as->ccb; - - if (bp == as->scsi_bp) { - /* no fancy error recovery in this case */ - if (asverbose) - printf ("asintr1:scsicmd "); -#if 0 - if (val != 1) - bp->b_flags |= B_ERROR; - goto next; -#endif - } - - bad = 0; - msg = NULL; - - if (val != 1 && val != 4) { - bad = 1; - sprintf (msgbuf, "funny mailbox message 0x%x\n", val); - msg = msgbuf; - goto wrapup; - } - - if (ccb->ccb_host_status != 0) { - bad = 1; - sprintf (msgbuf, "controller error 0x%x", - ccb->ccb_host_status); - msg = msgbuf; - goto wrapup; - } - - if (ccb->ccb_target_status == 0) - /* good transfer */ - goto wrapup; - - if (ccb->ccb_target_status == 8) { - /* target rejected command because it is busy - * and wants us to try again later. We'll wait 1 second - */ - as->restart_pending = 1; - timeout (asrestart, as, hz); - return; - } - - if (ccb->ccb_target_status != 2) { - bad = 1; - sprintf (msgbuf, "target error 0x%x", - ccb->ccb_target_status); - msg = msgbuf; - goto wrapup; - } - - /* normal path for errors */ - - sp = ccb_sense (ccb); - /* check for extended sense information */ - if ((sp[0] & 0x7f) != 0x70) { - /* none */ - bad = 1; - sprintf (msgbuf, "scsi error 0x%x", sp[0] & 0x7f); - msg = msgbuf; - goto wrapup; - } - - if (as->tape && (sp[2] & 0xf) == 0) { - if (sp[2] & 0xe0) { - /* either we read a file mark, the early warning EOT, - * or the block size did not match. In any case, the - * normal residue handling will work (I think) - */ - goto wrapup; - } - } - - bad = 1; - - switch (key = sp[2] & 0xf) { - case 1: - msg = "soft error"; - bad = 0; - break; - case 2: - msg = "not ready"; - break; - case 3: - msg = "hard error"; - break; - case 4: - msg = "target hardware error"; - break; - case 5: - msg = "illegal request"; - break; - case 6: - msg = "unit attention error"; - break; - case 7: - msg = "write protect error"; - break; - case 0xd: - msg = "volume overflow"; - break; - default: - sprintf (msgbuf, "scsi extended error 0x%x", sp[2] & 0xf); - msg = msgbuf; - break; - } - - wrapup: - - if (bad && msg == NULL) - msg = "unknown error"; - - if (msg && key != 6) { - diskerr (bp, "as", msg, - LOG_PRINTF, - -1, /* number of successful blks */ - as->have_label ? &as->label : NULL); - printf ("\n"); - } - - if (bad && key != 6) { - bp->b_flags |= B_ERROR; - printf ("scsi sense: "); - sp = ccb_sense (ccb); - for (i = 0; i < 30; i++) - printf ("%x ", sp[i] & 0xff); - printf ("\n"); - } - - /* this assignment mixed sizes of controller commands - and data to read/write. - bp->b_resid = (ccb->ccb_data_len_msb << 16) - | (ccb->ccb_data_len_mid << 8) - | ccb->ccb_data_len_lsb; - */ - bp->b_resid = 0; - - next: - asdone (as, 1); -} - -asdone (as, restart) -struct asinfo *as; -int restart; -{ - struct buf *bp; - - bp = as->requests.b_actf; - as->requests.b_actf = bp->av_forw; - biodone (bp); - as->retry_count = 0; - if (restart && as->requests.b_actf) - asstart (as); -} - -int -assize (dev) -dev_t dev; -{ - struct asinfo *as; - struct disklabel *lp; - int val; - - if (as_port == 0 || dev_target (dev) >= NTARGETS) - return (ENXIO); - - as = &asinfo[dev_target (dev)]; - - if (as->open == 0 - && asopen (dev, FREAD, S_IFBLK, NULL) != 0) - return (0); - - if (as->have_label == 0) - return (0); - - lp = &as->label; - val = lp->d_partitions[dev_part (dev)].p_size - * lp->d_secsize / DEV_BSIZE; - (void) asclose(dev, FREAD, S_IFBLK, NULL); - return (val); -} - -int -as_put_byte (val) -int val; -{ - int i; - - for (i = 100; i > 0; i--) { - if ((inb (as_port + AS_STATUS) & AS_STATUS_CDF) == 0) - break; - DELAY (100); - } - if (i == 0) { - printf ("as: put byte timed out\n"); - return (-1); - } - outb (as_port + AS_DATA_OUT, val); - return (0); -} - -int -as_get_byte (as) -{ - int i; - - for (i = 100; i > 0; i--) { - if ((inb (as_port + AS_STATUS) & AS_STATUS_DF) != 0) - break; - DELAY (100); - } - if (i == 0) { - printf ("as_get_byte timed out\n"); - return (-1); - } - return (inb (as_port + AS_DATA_OUT) & 0xff); -} -#endif /* NAS */ diff --git a/sys/i386/isa/asreg.h b/sys/i386/isa/asreg.h deleted file mode 100644 index 24f4645911..0000000000 --- a/sys/i386/isa/asreg.h +++ /dev/null @@ -1,104 +0,0 @@ -struct scsicmd { - int lun; - int readflag; /* 1=read, 0=write */ - int blkno; /* just used in seek sorting */ - - unsigned int cdblen; - caddr_t cdb; - - unsigned int datalen; - caddr_t data; - - unsigned int ccblen; - caddr_t ccb; -}; -#define SCSICMD _IOWR('a', 150, struct scsicmd) - -/* write regs */ -#define AS_CONTROL 0 -#define AS_DATA_OUT 1 - -/* read regs */ -#define AS_STATUS 0 -#define AS_DATA_IN 1 -#define AS_INTR 2 - -/* control port bits */ -#define AS_CONTROL_HRST 0x80 /* hard reset */ -#define AS_CONTROL_SRST 0x40 /* soft reset */ -#define AS_CONTROL_IRST 0x20 /* interrupt reset */ -#define AS_CONTROL_SCRST 0x10 /* scsi bus reset */ - -/* status port bits */ -#define AS_STATUS_STST 0x80 /* self test in progress */ -#define AS_STATUS_DIAGF 0x40 /* internal diagnostic failure */ -#define AS_STATUS_INIT 0x20 /* mailbox initialization required */ -#define AS_STATUS_IDLE 0x10 /* scsi host adapter idle */ -#define AS_STATUS_CDF 0x08 /* command/data out port full */ -#define AS_STATUS_DF 0x04 /* data in port full */ -/* 0x02 reserved */ -#define AS_STATUS_INVDCMD 0x01 /* invalid host adapter command */ - -/* interrupt port bits */ -#define AS_INTR_ANY 0x80 -#define AS_INTR_SCRD 0x08 /* scsi reset detected */ -#define AS_INTR_HACC 0x04 /* host adapter command complete */ -#define AS_INTR_MBOA 0x02 /* mailbox out available */ -#define AS_INTR_MBIF 0x01 /* mailbox in available */ - -/* command bytes */ -#define AS_CMD_NOP 0x00 -#define AS_CMD_MAILBOX_INIT 0x01 -#define AS_CMD_START_SCSI_COMMAND 0x02 -#define AS_CMD_START_BIOS_COMMAND 0x03 -#define AS_CMD_ADAPTER_INQUIRY 0x04 -#define AS_CMD_ENABLE_MBOA_INTR 0x05 -#define AS_CMD_SET_SELECTION_TIMEOUT 0x06 -#define AS_CMD_SET_BUS_ON_TIME 0x07 -#define AS_CMD_SET_BUS_OFF_TIME 0x08 -#define AS_CMD_SET_TRANSFER_SPEED 0x09 -#define AS_CMD_RETURN_INSTALLED_DEVICES 0x0a -#define AS_CMD_RETURN_CONFIGURATION_DATA 0x0b -#define AS_CMD_ENABLE_TARGET_MODE 0x0c -#define AS_CMD_RETURN_SETUP_DATA 0x0d -#define AS_CMD_WRITE_ADAPTER_CHANNEL_2_BUFFER 0x1a -#define AS_CMD_READ_ADAPTER_CHANNEL_2_BUFGFER 0x1b -#define AS_CMD_WRITE_ADAPTER_FIFO_BUFFER 0x1c -#define AS_CMD_READ_ADAPTER_FIFO_BUFFER 0x1d -#define AS_CMD_ECHO 0x1f -#define AS_CMD_ADAPTER_DIAG 0x20 -#define AS_CMD_SET_ADAPTER_OPTIONS 0x21 - -struct mailbox_entry { - unsigned char cmd; - unsigned char msb; - unsigned char mid; - unsigned char lsb; -}; - -#define MAXCDB 50 -#define MAXSENSE 100 - -struct ccb { - unsigned char ccb_opcode; - unsigned char ccb_addr_and_control; - unsigned char ccb_scsi_command_len; - unsigned char ccb_requst_sense_allocation_len; - unsigned char ccb_data_len_msb; - unsigned char ccb_data_len_mid; - unsigned char ccb_data_len_lsb; - unsigned char ccb_data_ptr_msb; - unsigned char ccb_data_ptr_mid; - unsigned char ccb_data_ptr_lsb; - unsigned char ccb_link_msb; - unsigned char ccb_link_mid; - unsigned char ccb_link_lsb; - unsigned char ccb_link_id; - unsigned char ccb_host_status; - unsigned char ccb_target_status; - unsigned char ccb_zero1; - unsigned char ccb_zero2; - unsigned char ccb_cdb[MAXCDB + MAXSENSE]; -}; -#define ccb_sense(ccb) ((ccb)->ccb_cdb + (ccb)->ccb_scsi_command_len) - diff --git a/sys/i386/isa/bt742a.c b/sys/i386/isa/bt742a.c index dcdba5b54e..0e25091811 100644 --- a/sys/i386/isa/bt742a.c +++ b/sys/i386/isa/bt742a.c @@ -12,47 +12,11 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- - * - * 16 Feb 93 Julian Elischer ADDED for SCSI system + * $Id: bt742a.c,v 1.7 1993/08/28 03:07:42 rgrimes Exp $ */ /* - * HISTORY - * $Log: bt742a.c,v $ - * Revision 1.7 1992/08/24 22:40:16 jason - * BIG_DMA ifdef for 512 dma segments instead of 128 segments - * - * Revision 1.6 1992/08/24 21:01:58 jason - * many changes and bugfixes for osf1 - * - * Revision 1.5 1992/07/31 01:22:03 julian - * support improved scsi.h layout - * - * Revision 1.4 1992/07/25 03:11:26 julian - * check each request fro sane flags. - * - * Revision 1.3 1992/07/24 00:52:45 julian - * improved timeout handling. - * added support for two arguments to the sd_done (or equiv) call so that - * they can pre-queue several arguments. - * slightly clean up error handling - * - * Revision 1.2 1992/07/17 22:03:54 julian - * upgraded the timeout code. - * added support for UIO-based i/o (as used for pmem operations) - * - * Revision 1.1 1992/05/27 00:51:12 balsup - * machkern/cor merge - * - */ - -/* - * bt742a BT-1542A SCSI driver + * bt742a SCSI driver */ #include @@ -99,7 +63,7 @@ #if NDDB > 0 int Debugger(); #else NDDB -#define Debugger() panic("should call debugger here (adaptec.c)") +#define Debugger() panic("should call debugger here (bt742a.c)") #endif NDDB #endif __386BSD__ @@ -107,6 +71,7 @@ int Debugger(); int Debugger(); #endif MACH +extern int hz; extern int delaycount; /* from clock setup code */ typedef unsigned long int physaddr; @@ -166,6 +131,11 @@ typedef unsigned long int physaddr; #define BT_MBX_INIT_EXTENDED 0x81 /* Mbx initialization */ #define BT_INQUIRE_EXTENDED 0x8D /* Adapter Setup Inquiry */ +/* Follows command appeared at FirmWare 3.31 */ +#define BT_ROUND_ROBIN 0x8f /* Enable/Disable(default) round robin */ +#define BT_DISABLE 0x00 /* Parameter value for Disable */ +#define BT_ENABLE 0x01 /* Parameter value for Enable */ + struct bt_cmd_buf { u_char byte[16]; }; @@ -174,7 +144,7 @@ struct bt_cmd_buf { * BT_INTR_PORT bits (read) */ -#define BT_ANY_INTR 0x80 /* Any interrupt */ +#define BT_ANY_INTR 0x80 /* Any interrupt */ #define BT_SCRD 0x08 /* SCSI reset detected */ #define BT_HACC 0x04 /* Command complete */ #define BT_MBOA 0x02 /* MBX out empty */ @@ -184,22 +154,38 @@ struct bt_cmd_buf { * Mail box defs */ -#define BT_MBX_SIZE 16 /* mail box size */ +#define BT_MBX_SIZE 255 /* mail box size (MAX 255 MBxs) */ +#define BT_CCB_SIZE 32 /* store up to 32CCBs at any one time */ + /* in bt742a H/W ( Not MAX ? ) */ + +#define bt_nextmbx( wmb, mbx, mbio ) \ + if ( (wmb) == &((mbx)->mbio[BT_MBX_SIZE - 1 ]) ) { \ + (wmb) = &((mbx)->mbio[0]); \ + } else { \ + (wmb)++; \ + } + + +typedef struct bt_mbx_out { + physaddr ccb_addr; + unsigned char dummy[3]; + unsigned char cmd; +} BT_MBO; + +typedef struct bt_mbx_in{ + physaddr ccb_addr; + unsigned char btstat; + unsigned char sdstat; + unsigned char dummy; + unsigned char stat; +} BT_MBI; struct bt_mbx { - struct bt_mbx_out { - physaddr ccb_addr; - unsigned char dummy[3]; - unsigned char cmd; - } mbo [BT_MBX_SIZE]; - struct bt_mbx_in{ - physaddr ccb_addr; - unsigned char btstat; - unsigned char sdstat; - unsigned char dummy; - unsigned char stat; - } mbi[BT_MBX_SIZE]; + BT_MBO mbo[BT_MBX_SIZE]; + BT_MBI mbi[BT_MBX_SIZE]; + BT_MBO *tmbo; /* Target Mail Box out */ + BT_MBI *tmbi; /* Target Mail Box in */ }; /* @@ -264,17 +250,12 @@ struct bt_ccb { /*------------------------------------longword boundary */ struct bt_mbx_out *mbx; /* pointer to mail box */ /*------------------------------------longword boundary */ - long int delta; /* difference from previous*/ - struct bt_ccb *later,*sooner; int flags; #define CCB_FREE 0 #define CCB_ACTIVE 1 #define CCB_ABORTED 2 }; -struct bt_ccb *bt_soonest = (struct bt_ccb *)0; -struct bt_ccb *bt_latest = (struct bt_ccb *)0; -long int bt_furtherest = 0; /* longest time in the timeout queue */ /* * opcode fields */ @@ -304,7 +285,13 @@ long int bt_furtherest = 0; /* longest time in the timeout queue */ #define BT_INV_CCB 0x1a /* Invalid CCB or segment list */ #define BT_ABORTED 42 /* pseudo value from driver */ - +struct bt_boardID +{ + u_char board_type; + u_char custom_feture; + char firm_revision; + u_char firm_version; +}; struct bt_setup { @@ -315,9 +302,8 @@ struct bt_setup u_char bus_on; u_char bus_off; u_char num_mbx; - u_char mbx[4]; - struct - { + u_char mbx[3]; + struct { u_char offset:4; u_char period:3; u_char valid:1; @@ -356,7 +342,6 @@ extern physaddr kvtophys(); #endif MACH #ifdef __386BSD__ -#define PHYSTOKV(x) (x | 0xFE000000) #define KVTOPHYS(x) vtophys(x) #endif __386BSD__ @@ -374,7 +359,7 @@ short bt_base[NBT]; /* base port for each board */ #endif MACH struct bt_mbx bt_mbx[NBT]; struct bt_ccb *bt_ccb_free[NBT]; -struct bt_ccb bt_ccb[NBT][BT_MBX_SIZE]; +struct bt_ccb bt_ccb[NBT][BT_CCB_SIZE]; struct scsi_xfer bt_scsi_xfer[NBT]; struct isa_dev *btinfo[NBT]; struct bt_ccb *bt_get_ccb(); @@ -408,15 +393,6 @@ struct isa_driver btdriver = { btprobe, btattach, "bt"}; static int btunit = 0; -#define bt_abortmbx(mbx) \ - (mbx)->cmd = BT_MBO_ABORT; \ - outb(BT_CMD_DATA_PORT, BT_START_SCSI); -#define bt_startmbx(mbx) \ - (mbx)->cmd = BT_MBO_START; \ - outb(BT_CMD_DATA_PORT, BT_START_SCSI); - - - int bt_scsi_cmd(); int bt_timeout(); void btminphys(); @@ -429,7 +405,8 @@ struct scsi_switch bt_switch = 0, 0, bt_adapter_info, - 0,0,0 + "bt", + 0,0 }; #define BT_CMD_TIMEOUT_FUDGE 200 /* multiplied to get Secs */ #define BT_RESET_TIMEOUT 1000000 @@ -486,7 +463,7 @@ u_char args; } if (!i) { - printf("bt_cmd: bt742a host not idle(0x%x)\n",sts); + printf("bt%d: bt_cmd, host not idle(0x%x)\n",unit,sts); return(ENXIO); } } @@ -516,7 +493,7 @@ u_char args; } if (i >= wait) { - printf("bt_cmd: bt742a cmd/data port full\n"); + printf("bt%d: bt_cmd, cmd/data port full\n",unit); outb(BT_CTRL_STAT_PORT, BT_SRST); return(ENXIO); } @@ -537,7 +514,8 @@ u_char args; } if (i >= wait) { - printf("bt_cmd: bt742a cmd/data port empty %d\n",ocnt); + printf("bt%d: bt_cmd, cmd/data port empty %d\n", + unit,ocnt); return(ENXIO); } oc = inb(BT_CMD_DATA_PORT); @@ -558,7 +536,7 @@ u_char args; } if (!i) { - printf("bt_cmd: bt742a host not finished(0x%x)\n",sts); + printf("bt%d: bt_cmd, host not finished(0x%x)\n",unit,sts); return(ENXIO); } outb(BT_CTRL_STAT_PORT, BT_IRST); @@ -589,7 +567,7 @@ struct isa_dev *dev; bt_base[unit] = dev->dev_addr; if(unit >= NBT) { - printf("bt: unit number (%d) too high\n",unit); + printf("bt%d: unit number too high\n",unit); return(0); } /***********************************************\ @@ -605,6 +583,7 @@ struct isa_dev *dev; * If it's there, put in it's interrupt vectors * \***********************************************/ #ifdef MACH + dev->dev_pic = bt_int[unit]; #if defined(OSF) /* OSF */ chp->ih_level = dev->dev_pic; chp->ih_handler = dev->dev_intr[0]; @@ -618,7 +597,6 @@ struct isa_dev *dev; else panic("Unable to add bt interrupt handler"); #else /* CMU */ - dev->dev_pic = bt_int[unit]; take_dev_irq(dev); #endif /* !defined(OSF) */ printf("port=%x spl=%d\n", dev->dev_addr, dev->dev_spl); @@ -626,7 +604,6 @@ struct isa_dev *dev; #ifdef __386BSD__ /* 386BSD */ dev->id_irq = (1 << bt_int[unit]); dev->id_drq = bt_dma[unit]; - printf("\n **"); #endif __386BSD__ btunit++; @@ -642,10 +619,6 @@ struct isa_dev *dev; int unit = dev->dev_unit; -#ifdef __386BSD__ - printf(" probing for scsi devices**\n"); -#endif __386BSD__ - /***********************************************\ * ask the adapter what subunits are present * \***********************************************/ @@ -653,13 +626,6 @@ struct isa_dev *dev; #if defined(OSF) bt_attached[unit]=1; #endif /* defined(OSF) */ - if(!unit) /* only one for all boards */ - { - bt_timeout(0); - } -#ifdef __386BSD__ - printf("bt%d",unit); -#endif __386BSD__ return; } @@ -678,25 +644,50 @@ int unit; \***********************************************/ btintr(unit) { + BT_MBI *wmbi; + struct bt_mbx *wmbx; struct bt_ccb *ccb; unsigned char stat; - register i; + int i,wait; + int found = 0; +#ifdef UTEST if(scsi_debug & PRINTROUTINES) printf("btintr "); +#endif /***********************************************\ * First acknowlege the interrupt, Then if it's * * not telling about a completed operation * * just return. * \***********************************************/ stat = inb(BT_INTR_PORT); - outb(BT_CTRL_STAT_PORT, BT_IRST); - if(scsi_debug & TRACEINTERRUPTS) - printf("int = 0x%x ",stat); - if (! (stat & BT_MBIF)) + + /* Mail Box out empty ? */ + if ( stat & BT_MBOA ) { + printf("bt%d: Available Free mbo post\n",unit); + /* Disable MBO available interrupt */ + outb(BT_CMD_DATA_PORT,BT_MBO_INTR_EN); + wait = BT_CMD_TIMEOUT_FUDGE * delaycount; + for (i=0; i< wait; i++) + { + if (!(inb(BT_CTRL_STAT_PORT) & BT_CDF)) + break; + } + if (i >= wait) + { + printf("bt%d: bt_intr, cmd/data port full\n",unit); + outb(BT_CTRL_STAT_PORT, BT_SRST); + return 1; + } + outb(BT_CMD_DATA_PORT, 0x00); /* Disable */ + wakeup(&bt_mbx[unit]); + outb(BT_CTRL_STAT_PORT, BT_IRST); return 1; - if(scsi_debug & TRACEINTERRUPTS) - printf("mbxi "); + } + if (! (stat & BT_MBIF)) { + outb(BT_CTRL_STAT_PORT, BT_IRST); + return 1; + } #if defined(OSF) if (!bt_attached[unit]) { @@ -706,57 +697,81 @@ btintr(unit) /***********************************************\ * If it IS then process the competed operation * \***********************************************/ - for (i = 0; i < BT_MBX_SIZE; i++) - { - if (bt_mbx[unit].mbi[i].stat != BT_MBI_FREE) + wmbx = &bt_mbx[unit]; + wmbi = wmbx->tmbi; +AGAIN: + while ( wmbi->stat != BT_MBI_FREE ) { + found++; + ccb = (struct bt_ccb *)PHYSTOKV((wmbi->ccb_addr)); + if((stat = wmbi->stat) != BT_MBI_OK) { - ccb = (struct bt_ccb *)PHYSTOKV( - (bt_mbx[unit].mbi[i].ccb_addr)); - if((bt_debug & BT_SHOWCCBS) && ccb) - printf("",ccb); - if((stat = bt_mbx[unit].mbi[i].stat) != BT_MBI_OK) + switch(stat) { - switch(stat) - { - case BT_MBI_ABORT: - if(bt_debug & BT_SHOWMISC) - printf("abort "); - ccb->host_stat = BT_ABORTED; - break; + case BT_MBI_ABORT: +#ifdef UTEST + if(bt_debug & BT_SHOWMISC) + printf("abort "); +#endif + ccb->host_stat = BT_ABORTED; + break; - case BT_MBI_UNKNOWN: - ccb = (struct bt_ccb *)0; - if(bt_debug & BT_SHOWMISC) - printf("unknown ccb for abort"); - break; + case BT_MBI_UNKNOWN: + ccb = (struct bt_ccb *)0; +#ifdef UTEST + if(bt_debug & BT_SHOWMISC) + printf("unknown ccb for abort"); +#endif + break; - case BT_MBI_ERROR: - break; + case BT_MBI_ERROR: + break; - default: - panic("Impossible mbxi status"); + default: + panic("Impossible mbxi status"); - } - if((bt_debug & BT_SHOWCMDS ) && ccb) - { - u_char *cp; - cp = ccb->scsi_cmd; - printf("op=%x %x %x %x %x %x\n", - cp[0], cp[1], cp[2], - cp[3], cp[4], cp[5]); - printf("stat %x for mbi[%d]\n" - , bt_mbx[unit].mbi[i].stat, i); - printf("addr = 0x%x\n", ccb); - } } - if(ccb) +#ifdef UTEST + if((bt_debug & BT_SHOWCMDS ) && ccb) { - bt_remove_timeout(ccb); - bt_done(unit,ccb); + u_char *cp; + cp = ccb->scsi_cmd; + printf("op=%x %x %x %x %x %x\n", + cp[0], cp[1], cp[2], + cp[3], cp[4], cp[5]); + printf("stat %x for mbi addr = 0x%08x\n" + , wmbi->stat, wmbi ); + printf("addr = 0x%x\n", ccb); + } +#endif + } + wmbi->stat = BT_MBI_FREE; + if(ccb) + { + untimeout(bt_timeout,ccb); + bt_done(unit,ccb); + } + + /* Set the IN mail Box pointer for next */ + bt_nextmbx( wmbi, wmbx, mbi ); + } + if ( !found ) { + for ( i = 0; i < BT_MBX_SIZE; i++) { + if ( wmbi->stat != BT_MBI_FREE ) { + found ++; + break; } - bt_mbx[unit].mbi[i].stat = BT_MBI_FREE; + bt_nextmbx( wmbi, wmbx, mbi ); + } + if ( !found ) { + printf("bt%d: mbi at 0x%08x should be found, stat=%02x..resync\n", + unit, wmbi, stat ); + } else { + found = 0; + goto AGAIN; } } + wmbx->tmbi = wmbi; + outb(BT_CTRL_STAT_PORT, BT_IRST); return(1); } @@ -768,9 +783,11 @@ bt_free_ccb(unit,ccb, flags) struct bt_ccb *ccb; { unsigned int opri; - + +#ifdef UTEST if(scsi_debug & PRINTROUTINES) printf("ccb%d(0x%x)> ",unit,flags); +#endif if (!(flags & SCSI_NOMASK)) opri = splbio(); @@ -789,16 +806,20 @@ struct bt_ccb *ccb; } /***********************************************\ -* Get a free ccb (and hence mbox-out entry) * +* Get a free ccb * \***********************************************/ struct bt_ccb * bt_get_ccb(unit,flags) { - unsigned opri; + unsigned opri; struct bt_ccb *rc; + struct bt_mbx *wmbx; /* Mail Box pointer specified unit */ + BT_MBO *wmbo; /* Out Mail Box pointer */ +#ifdef UTEST if(scsi_debug & PRINTROUTINES) printf("next; rc->flags = CCB_ACTIVE; +#ifdef HE + /* Get the Target OUT mail Box pointer */ + wmbx = &bt_mbx[unit]; + wmbo = wmbx->tmbo; + while ( wmbo->cmd != BT_MBO_FREE ) { + /* Enable MBO available interrupt */ + outb(BT_CMD_DATA_PORT,BT_MBO_INTR_EN); + printf("Wait free mbo.."); /* AMURAI */ + sleep( wmbx, PRIBIO); + printf("Got free mbo\n"); /* AMURAI */ + } + + /* Link CCB to the Mail Box */ + rc->mbx = wmbo; + wmbo->ccb_addr = KVTOPHYS(rc); + + /* Set the OUT mail Box pointer for next */ + bt_nextmbx( wmbo, wmbx, mbo ); + wmbx->tmbo = wmbo; +#endif } if (!(flags & SCSI_NOMASK)) splx(opri); + return(rc); } - +/***********************************************\ +* Get a MBO and then Send it * +\***********************************************/ +BT_MBO *bt_send_mbo( int unit, + int flags, + int cmd, + struct bt_ccb *ccb ) +{ + unsigned opri; + BT_MBO *wmbo; /* Mail Box Out pointer */ + struct bt_mbx *wmbx; /* Mail Box pointer specified unit */ + int i, wait; + wmbx = &bt_mbx[unit]; + + if (!(flags & SCSI_NOMASK)) + opri = splbio(); + + /* Get the Target OUT mail Box pointer and move to Next */ + wmbo = wmbx->tmbo; + wmbx->tmbo = ( wmbo == &( wmbx->mbo[BT_MBX_SIZE - 1 ] ) ? + &(wmbx->mbo[0]) : wmbo + 1 ); + + /* + * Check the outmail box is free or not + * Note: Under the normal operation, it shuld NOT happen to wait. + */ + while ( wmbo->cmd != BT_MBO_FREE ) { + + wait = BT_CMD_TIMEOUT_FUDGE * delaycount; + /* Enable MBO available interrupt */ + outb(BT_CMD_DATA_PORT,BT_MBO_INTR_EN); + for (i=0; i< wait; i++) + { + if (!(inb(BT_CTRL_STAT_PORT) & BT_CDF)) + break; + } + if (i >= wait) + { + printf("bt%d: bt_send_mbo, cmd/data port full\n",unit); + outb(BT_CTRL_STAT_PORT, BT_SRST); + return( (BT_MBO *)0 ); + } + outb(BT_CMD_DATA_PORT, 0x01); /* Enable */ + sleep( wmbx, PRIBIO); + } + + /* Link CCB to the Mail Box */ + wmbo->ccb_addr = KVTOPHYS(ccb); + ccb->mbx = wmbo; + wmbo->cmd = cmd; + + /* Send it ! */ + outb(BT_CMD_DATA_PORT, BT_START_SCSI); + + if (!(flags & SCSI_NOMASK)) + splx(opri); + + return(wmbo); +} /***********************************************\ * We have a ccb which has been processed by the * * adaptor, now we look to see how the operation * @@ -831,8 +932,10 @@ struct bt_ccb *ccb; struct scsi_sense_data *s1,*s2; struct scsi_xfer *xs = ccb->xfer; +#ifdef UTEST if(scsi_debug & (PRINTROUTINES | TRACEINTERRUPTS)) printf("bt_done "); +#endif /***********************************************\ * Otherwise, put the results of the operation * * into the xfer and call whoever started it * @@ -851,19 +954,23 @@ struct bt_ccb *ccb; { case BT_ABORTED: /* No response */ case BT_SEL_TIMEOUT: /* No response */ +#ifdef UTEST if (bt_debug & BT_SHOWMISC) { printf("timeout reported back\n"); } +#endif xs->error = XS_TIMEOUT; break; default: /* Other scsi protocol messes */ xs->error = XS_DRIVER_STUFFUP; +#ifdef UTEST if (bt_debug & BT_SHOWMISC) { printf("unexpected host_stat: %x\n", ccb->host_stat); } +#endif } } @@ -880,11 +987,13 @@ struct bt_ccb *ccb; xs->error = XS_BUSY; break; default: +#ifdef UTEST if (bt_debug & BT_SHOWMISC) { printf("unexpected target_stat: %x\n", ccb->target_stat); } +#endif xs->error = XS_DRIVER_STUFFUP; } } @@ -924,8 +1033,10 @@ int unit; } if (i >= BT_RESET_TIMEOUT) { +#ifdef UTEST if (bt_debug & BT_SHOWMISC) printf("bt_init: No answer from bt742a board\n"); +#endif return(ENXIO); } @@ -935,11 +1046,9 @@ int unit; * level * \***********************************************/ #ifdef __386BSD__ - printf("bt%d reading board settings, ",unit); -#define PRNT(x) + printf("bt%d: reading board settings, ",unit); #else __386BSD__ printf("bt%d:",unit); -#define PRNT(x) printf(x) #endif __386BSD__ bt_cmd(unit,0, sizeof(conf), 0 ,&conf, BT_CONF_GET); @@ -947,72 +1056,71 @@ int unit; { case EISADMA: bt_dma[unit] = -1; - PRNT("eisa dma "); break; case CHAN0: outb(0x0b, 0x0c); outb(0x0a, 0x00); bt_dma[unit] = 0; - PRNT("dma=0 "); break; case CHAN5: outb(0xd6, 0xc1); outb(0xd4, 0x01); bt_dma[unit] = 5; - PRNT("dma=5 "); break; case CHAN6: outb(0xd6, 0xc2); outb(0xd4, 0x02); bt_dma[unit] = 6; - PRNT("dma=6 "); break; case CHAN7: outb(0xd6, 0xc3); outb(0xd4, 0x03); bt_dma[unit] = 7; - PRNT("dma=7 "); break; default: printf("illegal dma setting %x\n",conf.chan); return(EIO); } + if (bt_dma[unit] == -1) + printf("eisa dma, "); + else + printf("dma=%d, ",bt_dma[unit]); + switch(conf.intr) { case INT9: bt_int[unit] = 9; - PRNT("int=9 "); break; case INT10: bt_int[unit] = 10; - PRNT("int=10 "); break; case INT11: bt_int[unit] = 11; - PRNT("int=11 "); break; case INT12: bt_int[unit] = 12; - PRNT("int=12 "); break; case INT14: bt_int[unit] = 14; - PRNT("int=14 "); break; case INT15: bt_int[unit] = 15; - PRNT("int=15 "); break; default: printf("illegal int setting\n"); return(EIO); } +#ifdef __386BSD__ + printf("int=%d\n",bt_int[unit]); +#else + printf("int=%d ",bt_int[unit]); +#endif __386BSD__ + /* who are we on the scsi bus */ bt_scsi_dev[unit] = conf.scsi_dev; /***********************************************\ * Initialize mail box * \***********************************************/ - *((physaddr *)ad) = KVTOPHYS(&bt_mbx[unit]); bt_cmd(unit,5, 0, 0, 0, BT_MBX_INIT_EXTENDED , BT_MBX_SIZE @@ -1022,16 +1130,34 @@ int unit; , ad[3]); /***********************************************\ - * link the ccb's with the mbox-out entries and * - * into a free-list * + * Set Pointer chain null for just in case * + * Link the ccb's into a free-list W/O mbox * + * Initilize Mail Box stat to Free * \***********************************************/ - for (i=0; i < BT_MBX_SIZE; i++) { - bt_ccb[unit][i].next = bt_ccb_free[unit]; - bt_ccb_free[unit] = &bt_ccb[unit][i]; + if ( bt_ccb_free[unit] != (struct bt_ccb *)0 ) { + printf("bt%d: bt_ccb_free is NOT initialized but init here\n", + unit); + bt_ccb_free[unit] = (struct bt_ccb *)0; + } + for (i=0; i < BT_CCB_SIZE; i++) { + bt_ccb[unit][i].next = bt_ccb_free[unit]; + bt_ccb_free[unit] = &bt_ccb[unit][i]; bt_ccb_free[unit]->flags = CCB_FREE; - bt_ccb_free[unit]->mbx = &bt_mbx[unit].mbo[i]; - bt_mbx[unit].mbo[i].ccb_addr = KVTOPHYS(bt_ccb_free[unit]) ; } + for (i=0; i < BT_MBX_SIZE; i++) { + bt_mbx[unit].mbo[i].cmd = BT_MBO_FREE; + bt_mbx[unit].mbi[i].stat = BT_MBI_FREE; + } + + /***********************************************\ + * Set up Initial mail box for round-robin * + \***********************************************/ + bt_mbx[unit].tmbo = &bt_mbx[unit].mbo[0]; + bt_mbx[unit].tmbi = &bt_mbx[unit].mbi[0]; + bt_inquire_setup_information( unit ); + + /* Enable round-robin scheme - appeared at FirmWare 3.31 */ + bt_cmd(unit, 1, 0, 0, 0, BT_ROUND_ROBIN, BT_ENABLE ); /***********************************************\ * Note that we are going and return (to probe) * @@ -1039,6 +1165,49 @@ int unit; bt_initialized[unit]++; return( 0 ); } +bt_inquire_setup_information( unit ) +int unit; +{ + struct bt_setup setup; + struct bt_boardID bID; + int i; + + /* Inquire Board ID to Bt742 for FirmWare Version */ + bt_cmd(unit, 0, sizeof(bID), 0, &bID, BT_INQUIRE ); + printf("bt%d: version %c.%c, ", + unit, bID.firm_revision, bID.firm_version ); + + /* Ask setup information to Bt742 */ + bt_cmd(unit, 1, sizeof(setup), 0, &setup, BT_SETUP_GET, sizeof(setup) ); + + if ( setup.sync_neg ) { + printf("sync, "); + } else { + printf("async, "); + } + + if ( setup.parity ) { + printf("parity, "); + } else { + printf("no parity, "); + } + + printf("%d mbxs, %d ccbs\n", setup.num_mbx, + sizeof(bt_ccb)/(sizeof(struct bt_ccb) * NBT) ); + + for ( i = 0; i < 8; i++ ) { + if( !setup.sync[i].offset && + !setup.sync[i].period && + !setup.sync[i].valid ) + continue; + + printf("bt%d: dev%02d Offset=%d,Transfer period=%d, Synchronous? %s", + unit, i, + setup.sync[i].offset, setup.sync[i].period, + setup.sync[i].valid ? "Yes" : "No" ); + } + +} #ifndef min @@ -1079,9 +1248,12 @@ struct scsi_xfer *xs; int unit =xs->adapter; int bytes_this_seg,bytes_this_page,datalen,flags; struct iovec *iovp; + BT_MBO *mbo; +#ifdef UTEST if(scsi_debug & PRINTROUTINES) printf("bt_scsi_cmd "); +#endif /***********************************************\ * get a ccb (mbox-out) to use. If the transfer * * is from a buf (possibly from interrupt time) * @@ -1091,12 +1263,12 @@ struct scsi_xfer *xs; if(xs->bp) flags |= (SCSI_NOSLEEP); /* just to be sure */ if(flags & ITSDONE) { - printf("Already done?"); + printf("bt%d: Already done?\n",unit); xs->flags &= ~ITSDONE; } if(!(flags & INUSE)) { - printf("Not in use?"); + printf("bt%d: Not in use?\n",unit); xs->flags |= INUSE; } if (!(ccb = bt_get_ccb(unit,flags))) @@ -1104,12 +1276,10 @@ struct scsi_xfer *xs; xs->error = XS_DRIVER_STUFFUP; return(TRY_AGAIN_LATER); } - +#ifdef UTEST if(bt_debug & BT_SHOWCCBS) printf("",ccb); - if (ccb->mbx->cmd != BT_MBO_FREE) - printf("MBO not free\n"); - +#endif /***********************************************\ * Put all the arguments for the xfer in the ccb * \***********************************************/ @@ -1147,10 +1317,12 @@ struct scsi_xfer *xs; { sg->seg_addr = (physaddr)iovp->iov_base; xs->datalen += sg->seg_len = iovp->iov_len; +#ifdef UTEST if(scsi_debug & SHOWSCATGATH) printf("(0x%x@0x%x)" ,iovp->iov_len ,iovp->iov_base); +#endif sg++; iovp++; seg++; @@ -1163,8 +1335,10 @@ struct scsi_xfer *xs; * Set up the scatter gather block * \***********************************************/ +#ifdef UTEST if(scsi_debug & SHOWSCATGATH) printf("%d @0x%x:- ",xs->datalen,xs->data); +#endif datalen = xs->datalen; thiskv = (int)xs->data; thisphys = KVTOPHYS(thiskv); @@ -1176,8 +1350,10 @@ struct scsi_xfer *xs; /* put in the base address */ sg->seg_addr = thisphys; +#ifdef UTEST if(scsi_debug & SHOWSCATGATH) printf("0x%x",thisphys); +#endif /* do it at least once */ nextphys = thisphys; @@ -1206,19 +1382,23 @@ struct scsi_xfer *xs; /********************************************\ * next page isn't contiguous, finish the seg * \********************************************/ +#ifdef UTEST if(scsi_debug & SHOWSCATGATH) printf("(0x%x)",bytes_this_seg); +#endif sg->seg_len = bytes_this_seg; sg++; seg++; } } /*end of iov/kv decision */ ccb->data_length = seg * sizeof(struct bt_scat_gath); +#ifdef UTEST if(scsi_debug & SHOWSCATGATH) printf("\n"); +#endif if (datalen) { /* there's still data, must have run out of segs! */ - printf("bt_scsi_cmd%d: more than %d DMA segs\n", + printf("bt%d: bt_scsi_cmd, more than %d DMA segs\n", unit,BT_NSEG); xs->error = XS_DRIVER_STUFFUP; bt_free_ccb(unit,ccb,flags); @@ -1240,6 +1420,7 @@ struct scsi_xfer *xs; { bcopy(xs->cmd, ccb->scsi_cmd, ccb->scsi_cmd_length); } +#ifdef UTEST if(scsi_debug & SHOWCOMMANDS) { u_char *b = ccb->scsi_cmd; @@ -1266,248 +1447,146 @@ struct scsi_xfer *xs; ); } } - bt_startmbx(ccb->mbx); +#endif + if ( bt_send_mbo( unit, flags, BT_MBO_START, ccb ) == (BT_MBO *)0 ) + { + xs->error = XS_DRIVER_STUFFUP; + bt_free_ccb(unit,ccb,flags); + return(TRY_AGAIN_LATER); + } /***********************************************\ * Usually return SUCCESSFULLY QUEUED * \***********************************************/ +#ifdef UTEST if(scsi_debug & TRACEINTERRUPTS) printf("cmd_sent "); +#endif if (!(flags & SCSI_NOMASK)) { - bt_add_timeout(ccb,xs->timeout); + timeout(bt_timeout,ccb,(xs->timeout * hz) / 1000); return(SUCCESSFULLY_QUEUED); - } + } else /***********************************************\ * If we can't use interrupts, poll on completion* \***********************************************/ { int done = 0; int count = delaycount * xs->timeout / BT_SCSI_TIMEOUT_FUDGE; + struct bt_mbx *wmbx = &bt_mbx[unit]; + BT_MBI *wmbi = wmbx->tmbi; + unsigned char stat; +#ifdef UTEST if(scsi_debug & TRACEINTERRUPTS) printf("wait "); +#endif while((!done) && count) { - i=0; - while ( (!done) && istat == BT_MBI_FREE )|| + (PHYSTOKV(wmbi->ccb_addr) + != (int)ccb ) ) { + count--; + continue; } - count--; + wmbi->stat = BT_MBI_FREE; + bt_done(unit,ccb); + done ++; + outb(BT_CTRL_STAT_PORT, BT_IRST); + /* Set the IN mail Box pointer for next */ + bt_nextmbx( wmbi, wmbx, mbi ); + wmbx->tmbi = wmbi; } - if (!count) + if (!count && !done) { +#ifdef UTEST if (!(xs->flags & SCSI_SILENT)) printf("cmd fail\n"); - bt_abortmbx(ccb->mbx); +#endif + bt_send_mbo( unit, flags, BT_MBO_ABORT, ccb ); count = delaycount * 2000 / BT_SCSI_TIMEOUT_FUDGE; while((!done) && count) { - i=0; - while ( (!done) && istat == BT_MBI_FREE )|| + ( PHYSTOKV(wmbi->ccb_addr ) + != (int)ccb ) ) { + count--; + continue; } - count--; + wmbi->stat = BT_MBI_FREE; + bt_done(unit,ccb); + done ++; + outb(BT_CTRL_STAT_PORT, BT_IRST); + /* Set the IN mail Box pointer for next */ + bt_nextmbx( wmbi, wmbx, mbi ); + wmbx->tmbi = wmbi; } - if(!count) + if(!count && !done) { - printf("abort failed in wait\n"); + printf("bt%d: abort failed in wait\n", unit); ccb->mbx->cmd = BT_MBO_FREE; } bt_free_ccb(unit,ccb,flags); - btintr(unit); xs->error = XS_DRIVER_STUFFUP; return(HAD_ERROR); } - btintr(unit); if(xs->error) return(HAD_ERROR); return(COMPLETE); - } -} - -/* - * +----------+ +----------+ +----------+ - * bt_soonest--->| later |---->| later|---->| later|--->0 - * | [Delta] | | [Delta] | | [Delta] | - * 0<-----|sooner |<----|sooner |<----|sooner |<----bt_latest - * +----------+ +----------+ +----------+ - * - * bt_furtherest = sum(Delta[1..n]) - */ -bt_add_timeout(ccb,time) -struct bt_ccb *ccb; -int time; -{ - int timeprev; - struct bt_ccb *prev; - int s = splbio(); - - if(prev = bt_latest) /* yes, an assign */ - { - timeprev = bt_furtherest; - } - else - { - timeprev = 0; } - while(prev && (timeprev > time)) - { - timeprev -= prev->delta; - prev = prev->sooner; - } - if(prev) - { - ccb->delta = time - timeprev; - if( ccb->later = prev->later) /* yes an assign */ - { - ccb->later->sooner = ccb; - ccb->later->delta -= ccb->delta; - } - else - { - bt_furtherest = time; - bt_latest = ccb; - } - ccb->sooner = prev; - prev->later = ccb; - } - else - { - if( ccb->later = bt_soonest) /* yes, an assign*/ - { - ccb->later->sooner = ccb; - ccb->later->delta -= time; - } - else - { - bt_furtherest = time; - bt_latest = ccb; - } - ccb->delta = time; - ccb->sooner = (struct bt_ccb *)0; - bt_soonest = ccb; - } - splx(s); -} - -bt_remove_timeout(ccb) -struct bt_ccb *ccb; -{ - int s = splbio(); - - if(ccb->sooner) - { - ccb->sooner->later = ccb->later; - } - else - { - bt_soonest = ccb->later; - } - if(ccb->later) - { - ccb->later->sooner = ccb->sooner; - ccb->later->delta += ccb->delta; - } - else - { - bt_latest = ccb->sooner; - bt_furtherest -= ccb->delta; - } - ccb->sooner = ccb->later = (struct bt_ccb *)0; - splx(s); } -extern int hz; -#define ONETICK 500 /* milliseconds */ -#define SLEEPTIME ((hz * 1000) / ONETICK) -bt_timeout(arg) -int arg; +bt_timeout(struct bt_ccb *ccb) { - struct bt_ccb *ccb; int unit; int s = splbio(); - while( ccb = bt_soonest ) + unit = ccb->xfer->adapter; + printf("bt%d: %d device timed out\n",unit + ,ccb->xfer->targ); +#ifdef UTEST + if(bt_debug & BT_SHOWCCBS) + bt_print_active_ccbs(unit); +#endif + + /***************************************\ + * If The ccb's mbx is not free, then * + * the board has gone Far East ? * + \***************************************/ + if((struct bt_ccb *)PHYSTOKV(ccb->mbx->ccb_addr)==ccb && + ccb->mbx->cmd != BT_MBO_FREE ) { - if(ccb->delta <= ONETICK) - /***********************************************\ - * It has timed out, we need to do some work * - \***********************************************/ - { - unit = ccb->xfer->adapter; - printf("bt%d:%d device timed out\n",unit - ,ccb->xfer->targ); - if(bt_debug & BT_SHOWCCBS) - tfs_print_active_ccbs(); - - /***************************************\ - * Unlink it from the queue * - \***************************************/ - bt_remove_timeout(ccb); - - /***************************************\ - * If The ccb's mbx is not free, then * - * the board has gone south * - \***************************************/ - if(ccb->mbx->cmd != BT_MBO_FREE) - { - printf("bt%d not taking commands!\n" - ,unit); - Debugger(); - } - /***************************************\ - * If it has been through before, then * - * a previous abort has failed, don't * - * try abort again * - \***************************************/ - if(ccb->flags == CCB_ABORTED) /* abort timed out */ - { - printf("AGAIN"); - ccb->xfer->retries = 0; /* I MEAN IT ! */ - ccb->host_stat = BT_ABORTED; - bt_done(unit,ccb); - } - else /* abort the operation that has timed out */ - { - printf("\n"); - bt_abortmbx(ccb->mbx); - /* 2 secs for the abort */ - bt_add_timeout(ccb,2000 + ONETICK); - ccb->flags = CCB_ABORTED; - } - } - else - /***********************************************\ - * It has not timed out, adjust and leave * - \***********************************************/ - { - ccb->delta -= ONETICK; - bt_furtherest -= ONETICK; - break; - } + printf("bt%d: not taking commands!\n" + ,unit); + Debugger(); + } + /***************************************\ + * If it has been through before, then * + * a previous abort has failed, don't * + * try abort again * + \***************************************/ + if(ccb->flags == CCB_ABORTED) /* abort timed out */ + { + printf("bt%d: Abort Operation has timed out\n",unit); + ccb->xfer->retries = 0; /* I MEAN IT ! */ + ccb->host_stat = BT_ABORTED; + bt_done(unit,ccb); + } + else /* abort the operation that has timed out */ + { + printf("bt%d: Try to abort\n",unit); + bt_send_mbo( unit, ~SCSI_NOMASK, + BT_MBO_ABORT, ccb ); + /* 2 secs for the abort */ + timeout(bt_timeout,ccb,2 * hz); + ccb->flags = CCB_ABORTED; } splx(s); - timeout(bt_timeout,arg,SLEEPTIME); } -tfs_print_ccb(ccb) +#ifdef UTEST +bt_print_ccb(ccb) struct bt_ccb *ccb; { printf("ccb:%x op:%x cmdlen:%d senlen:%d\n" @@ -1515,23 +1594,24 @@ struct bt_ccb *ccb; ,ccb->opcode ,ccb->scsi_cmd_length ,ccb->req_sense_length); - printf(" datlen:%d hstat:%x tstat:%x delta:%d flags:%x\n" + printf(" datlen:%d hstat:%x tstat:%x flags:%x\n" ,ccb->data_length ,ccb->host_stat ,ccb->target_stat - ,ccb->delta ,ccb->flags); } -tfs_print_active_ccbs() +bt_print_active_ccbs(int unit) { struct bt_ccb *ccb; - ccb = bt_soonest; + ccb = &(bt_ccb[unit][0]); + int i = BT_CCB_SIZE; - while(ccb) + while(i--) { - tfs_print_ccb(ccb); - ccb = ccb->later; + if(ccb->flags != CCB_FREE) + bt_print_ccb(ccb); + ccb++; } - printf("Furtherest = %d\n",bt_furtherest); } +#endif /*UTEST*/ diff --git a/sys/i386/isa/clock.c b/sys/i386/isa/clock.c index 0fb77012d0..53497c8487 100644 --- a/sys/i386/isa/clock.c +++ b/sys/i386/isa/clock.c @@ -33,27 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)clock.c 7.2 (Berkeley) 5/12/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 5 00158 - * -------------------- ----- ---------------------- - * - * 14 Aug 92 Arne Henrik Juul Added code in the kernel to - * allow for DST in the BIOS. - * 17 Jan 93 Bruce Evans Fixed leap year and second - * calculations - * 01 Feb 93 Julian Elischer Added code to for the cpu - * speed independent spinwait() - * function, (used by scsi and others) - * 25 Mar 93 Sean Eric Fagan Add microtimer support using timer 1 - * 08 Apr 93 Poul-Henning Kamp/P-HK Fixes, and support for dcfclock - * 26 Apr 93 Bruce Evans Eliminate findspeed, new spinwait - * 26 Apr 93 Rodney W. Grimes I merged in Bruce changes and hope I - * still kept the other fixes... Had to - * add back in findcpuspeed that Bruce - * had removed. + * from: @(#)clock.c 7.2 (Berkeley) 5/12/91 + * $Id$ */ /* diff --git a/sys/i386/isa/codrv/DOC/CHANGES.vak b/sys/i386/isa/codrv/DOC/CHANGES.vak deleted file mode 100644 index d196f19bd2..0000000000 --- a/sys/i386/isa/codrv/DOC/CHANGES.vak +++ /dev/null @@ -1,142 +0,0 @@ -This file is a reference of the modifications to codrv-0.1.1 -proposed by vak (Serge Vakulenko). Most of this has been integrated -into this version, with the exceptions marked with !!!! by me. -The file was named READMEvak before. Good work! - - - ------------------------------------------------------------ -Here is the patch for codrv, version 0.1.1. - -To apply it, - 1) unpack the original codrv distribution to directory keycap-0.1.1; - 2) apply this patch by command: - - cd keycap-0.1.1; patch -p1 < ../keycap011v.pch - - 3) Move cyrillic font to font directory: - - mv ../koi8x16.bdf font - - 4) Install codrv, follow instructions in keycap-0.1.1/README. - 5) Create directory /usr/share/font and copy all fonts to it. - 6) After rebooting with new kernel, try cyrillics with - - keymap -m koi8 - - Run VI, press CapsLock and enter cyrillic text! - Right Alt will work as old CapsLock key. - ------------------------------------------------------------ -Here is the brief description of my changes in keycap-0.1.1. - -!!! 1) Maximum character size on loaded font enlarged to 8.16 instead of -!!! 8x15. Tis caused changing the system limit on size of ioctl() argument -!!! (parameter IOCPARM_MAX in sys/ioctl.h) to 2*NBPG instead of NBPG. -!!! VGA_FNTCSIZE set to 16. -!!!REPLACED BY DIFFERENT PARAMETER PASSING MECHANISM - -2) Makefile for demo/ directory. - -!!!3) /dev/vga changed to /dev/kbd in dumpfont.c -!!!REMAINS /dev/vga, BUT CHECK WHETHER actvty POINTS TO Crtat, ELSE -!!!REJECT SOME IOCTLS. - -4) Verbose printing of video card subtype in info.c. - -5) New cyrillic KOI8 font with Russian, Ukrainian, Belorussian, - Serbian characters. Endoding is the widely used in exSU - so named KOI8. - -6) Analysing e0000 in addition to c0000 when trying to locate EGA/VGA - BIOS. My Intel 386 maps Paradise BIOS it to e0000 by default. - -7) New escape sequence for changing bell pitch and duration: - - ESC [ pitch ; duration ! - -!!! New ioctl(fd,KBDSETBEEP,struct kbd_sound*) to do the same. -!!!AVAILABLE, BUT DIFFERENT CALLING CONVENTION - -8) No ovltbl. Key2ascii now has pointer to kbd_ovlkey structure, - allocated by malloc. Never free'd, reused an need. This requires - much less memory, proportional to the number of keys remapped. - -9) New key ALTGRLOCK. It allows to have two alternate keyboard - mappings, for example, russian and english simultaneosly. - -10) New ioctl(fd,KBDASGNLEDS,int*) to map CapsLock LED to either - CapsLock (default), or ShiftLock, or AltGrLock. - -11) ATTENSION: SERIOUS BUG! Uninitialized pointer in coprobe(): - - register struct consoftc *p; - p->cs_flags = 0; - - I don't know, why it worked, obviously stack had some garbage, - which pointed to some secure place. But after several - recompilations of kernel I got trap 12 at this line. - -12) What is the reason to map Shift-Tab (BackTab) to \177\t? This sequence - makes no sense to all text editors I know. Why not use, say, - ESC[z? There also exist termcap extension kB for escape sequence, - generated by BackTab. I did not change this in driver, - and keep it now in keycap entry. -!!! WAS INHERITED FROM pccons - -13) Processing of KBD_META changed. Now ovl_tbl has the entry - for META keys, which is initially filled with unshift[] - with 8 bit xor'ed, but can be remapped by ioctl (KBDSCKEY), - just like ctrl[]. - -14) New entry in ovl_tbl, shiftaltgr[]. Now ALTGR and ALTGRLOCK - can work as switch to an alternative keyboard layout. - It it used for cyrillic layout, for example. - -15) Processing of CAPS changed. Every key has the flag KBD_DOCAPS, - whether it could be shifted by CAPS. By default this flag - is set for 'a'...'z', and can be changed by ioctl (KBDSCKEY). - Another flag KBD_DOALTCAPS means applying CAPS to keys in ALTGR - mode (altgr[] to shiftaltgr[]). - -16) Angle brackets removed when printing video card name in attach(). - All other drivers don't use brackets, so why should we do? - Printing of RAM size added. -!!!SOME WD PATCHES TO ALLOW 2 DRIVES USE THIS CONVENTION. BUT OK. - -17) When parsing an escape sequence, the driver tested the final - character to be in range 'A'...'Z' or 'a'...'z'. - I changed it to be ' '...'~'. - -18) Keynum and keyset incorporated into keymap. They are called - as ``keymap -T'' and ``keymap -a'' now. The main adea was to - have only one utility to handle all aspects of keyboard and - video driver. - -19) List() in keymap splitted into two functions: loadkmap() - and list(), because in old version the command - - keymap -l | more - - locked keyboard forever. - -20) Output format of ``keymap -l'' changed. Key types are printed - in lowercase, remapped keys tagged by '*'. New columns meta and - shiftaltgr added. Keys, affected by CapsLock marked with ~ in - unshift and altgr columns. Control characters in key strings - printed as ^X. - -21) New entries 'M123=' and 'X123=' in keycap, for Meta and ShiftAltgr keys - accordingly. - -22) New entry 'ag=' (alternate graphics) for AltgrLock in keycap. - -23) New entry 'la=' (LED assignments) in keycap. The values are: - 0 (default) Caps Lock LED reflects the state of CapsLock key; - 1 --//-- ShiftLock key; - 2 --//-- AltgrLock key. - -24) If the string value for unshifted 'K123=' or altgr 'A123=' key in - keycap begins with '~', then this key is affected by CapsLock - (KBD_DOCAPS or KBD_DOALTCAPS flag is set). - diff --git a/sys/i386/isa/codrv/DOC/CO_HISTORY b/sys/i386/isa/codrv/DOC/CO_HISTORY deleted file mode 100644 index ad043a6f9c..0000000000 --- a/sys/i386/isa/codrv/DOC/CO_HISTORY +++ /dev/null @@ -1,60 +0,0 @@ -RCS HISTORY OF CO DRIVER: - --hv- = Holger Veit veit@du9ds3.fb9dv.uni-duisburg.de --vak- = Sergey Vakulenko vak@kiae.su --hm = Helmut Michaelis hm@hcshh.hcs.de - -$Log: CO_HISTORY,v $ -# Revision 1.11 93/04/27 21:19:16 -hv- -# keymap debugged, hotkey programmable, HASVTY flag fixed -# several minor things I forgot -# -# Revision 1.10 93/04/22 22:06:21 -hv- -# most CODRV2 ioctls moved into CODRV1, now "the interface" -# made scroll, lock keys and LEDS depending from the current -# vty, fixed EMACS META-SPACE bug, fixed ISOLATIN bug (again) -# -# Revision 1.9 93/03/29 19:45:38 -hv- -# Lots of fixes -# -# Revision 1.8 93/01/23 23:13:05 -hv- -# kbd low level routines revised, now runs on laptop and pc, -# Christoph's snake screen saver included -# -# Revision 1.7 93/01/11 01:14:44 -hv- -# Bug fixes -# -# Revision 1.6 93/01/10 14:55:47 -hv- -# "virtual" emulator calls, codrv2 ioctl interface -# -# Revision 1.5 93/01/07 11:40:47 -hv- -# split ioctl_pc.h into public and common (to kernel) part -# to allow for kernel independent from ioctl_pc.h, for different -# ioctl sets -# -# Revision 1.4 93/01/04 22:04:18 -hv- -# Keyboard low level code entirely rewritten, XTKBDMODE -# -# Revision 1.3 93/01/02 23:10:49 -hv- -# Split IOCTL services into co_codrv1/co_codrv2 -# -# Revision 1.2 92/12/27 12:41:11 -hv- -# VTYs, many, many bug fixes -# and modifications -# -# Revision 1.1 92/12/25 12:33:47 -hv- -# Initial revision -# - - -PRE RCS History: - --vak- 01/12/92 meta and shiftaltgr mapping, beep ioctl --vak- 01/12/92 8x16 font loading, beep ioctl, - LED reassignment ioctl. --hv- 04/09/92 kbd (co) device, bug fixes X11 support - many more ioctls (codrv1-type) --hm 16-08-92 adding vga ioctl for cursor shape --hv- 25/07/92 split into kbd.c vga.c pccons.c --hv- 23/07/92 use at your own risk! - national keyboard support diff --git a/sys/i386/isa/codrv/DOC/FEATURES b/sys/i386/isa/codrv/DOC/FEATURES deleted file mode 100644 index 7af123f5d7..0000000000 --- a/sys/i386/isa/codrv/DOC/FEATURES +++ /dev/null @@ -1,16 +0,0 @@ -New features: - -1. Virtual consoles -2. Modified ioctl interface (mostly video functions) -3. Terminal emulation moved out of co_vga.c. Another terminal emulator - may be easily integrated (see co_pc3.c for more info) -4. Ioctl interface moved out of co_cons.c, co_kbd.c and co_vga.c. Other - ioctl call sets (SYSV) may be easily integrated. -6. Preparation for multibyte characters (not fully finished) -7. Preparation for graphics console support (not finished) -8. New keyboard layer with support, allows to switch between two - sets of key layouts. -9. Low level keyboard support totally rewritten, should be more stable now. -10. Many bug fixes -11. Many new bugs :-) -12. Hotkeys are now programmable diff --git a/sys/i386/isa/codrv/DOC/README b/sys/i386/isa/codrv/DOC/README deleted file mode 100644 index eeb637cb1e..0000000000 --- a/sys/i386/isa/codrv/DOC/README +++ /dev/null @@ -1,8 +0,0 @@ -This directory is going to be a documentation. - -Not yet. - -Real Programmers Don't Document Their Code. If it was -hard to write, it should be hard to understand. :-) - -(wanted to get the stuff finished before pk-0.2.4, doc follows) diff --git a/sys/i386/isa/codrv/ETC/etc.ttys b/sys/i386/isa/codrv/ETC/etc.ttys deleted file mode 100644 index 6bf1f54e60..0000000000 --- a/sys/i386/isa/codrv/ETC/etc.ttys +++ /dev/null @@ -1,57 +0,0 @@ -# -# @(#)ttys 5.1 (Berkeley) 4/17/89 -# -# name getty type status comments -# -# Console (=ttyv0) -# don't do a getty on /dev/console, and don't pass -# /dev/console as the video device to the xserver -ttyv0 "/usr/libexec/getty Pc" pc3 on secure -# -# Serial lines -# maybe your lines are called different -ttyd0 "/usr/libexec/getty std.9600" esprit off secure -ttyd1 "/usr/libexec/getty std.9600" esprit on secure -# -# VTYs -ttyv1 "/usr/libexec/getty Pc" pc3 on secure -ttyv2 "/usr/libexec/getty Pc" pc3 on secure -ttyv3 "/usr/libexec/getty Pc" pc3 on secure -#ttyv4 "/usr/libexec/getty Pc" pc3 on secure -#ttyv5 "/usr/libexec/getty Pc" pc3 on secure -#ttyv6 "/usr/libexec/getty Pc" pc3 on secure -#ttyv7 "/usr/libexec/getty Pc" pc3 on secure -# -# PTYs -ttyp0 none network -ttyp1 none network -ttyp2 none network -ttyp3 none network -ttyp4 none network -ttyp5 none network -ttyp6 none network -ttyp7 none network -ttyp8 none network -ttyp9 none network -ttypa none network -ttypb none network -ttypc none network -ttypd none network -ttype none network -ttypf none network -ttyq0 none network -ttyq1 none network -ttyq2 none network -ttyq3 none network -ttyq4 none network -ttyq5 none network -ttyq6 none network -ttyq7 none network -ttyq8 none network -ttyq9 none network -ttyqa none network -ttyqb none network -ttyqc none network -ttyqd none network -ttyqe none network -ttyqf none network diff --git a/sys/i386/isa/codrv/co_codrv1.c b/sys/i386/isa/codrv/co_codrv1.c deleted file mode 100644 index b56007dfef..0000000000 --- a/sys/i386/isa/codrv/co_codrv1.c +++ /dev/null @@ -1,771 +0,0 @@ -/* Copyright 1992 by Holger Veit - * May be freely used with Bill Jolitz's port of - * 386bsd and may be included in a 386bsd collection - * as long as binary and source are available and reproduce the above - * copyright. This includes special software collections which - * are derived from 386bsd, such as the so-called NetBSD. - * - * You may freely modify this code and contribute improvements based - * on this code as long as you don't claim to be the original author. - * Commercial use of this source requires permittance of the copyright - * holder. A general license for 386bsd will override this restriction. - * - * Use at your own risk. The copyright holder or any person who makes - * this code available for the public (administrators of public archives - * for instance) are not responsible for any harm to hardware or software - * that might happen due to wrong application or program faults. - * - * @(#) $RCSfile: co_codrv1.c,v $ $Revision: 1.6 $ (Contributed to 386bsd) $Date: 93/01/23 23:14:27 $ - * - * This file processes the vga/kbdioctls - * - * History: see CO_HISTORY - * - */ -static char *rcsid = "$Header: /usr/src/sys.386bsd/i386/isa/codrv/RCS/co_codrv1.c,v 1.6 93/01/23 23:14:27 root Exp Locker: root $"; - -#define COMPAT_CO011 -#include "co_hdr.h" - -/************************************************************************** - * - * This file serves as a plug-in interface for IOCTL processing. - * - * This file has been modified to understand most of the CODRV1 and - * CODRV2 ioctls which have been tested long in codrv-0.1.2-ALPHA. - * For some reasons, the CODRV2 font interface will be revised - * again. It is not integrated here. The other ioctls are available. - * - * This file has four public entry points: - * - * coioctl_init() (called by coattach, initialization) - * consioctl(dev,cmd,addr,flag) (serves /dev/console specific things) - * kbdioctl(dev,cmd,addr,flags) (serves /dev/kbd specific things) - * vgaioctl(dev,cmd,addr,flags) (serves /dev/vga specific things) - * - * In CODRV1 these functions were available with any console device, - * be it /dev/console, /dev/kbd, /dev/vga - * - * This ioctl package contains its own set of service functions, that - * were in co_kbd.c and co_vga.c before, with the exception of the - * keyboard overload subsystem, which is still in co_kbd.c. - * - * Programmers are invited to provide different interfaces, such as - * SVR3/SVR4/SUN-OS. There will be hopefully fully runtime-replacable - * kernel modules, which can make use of this. - * - ***********************************************************************/ - -#ifdef CODRV1 -#ifndef MINITERM - -/* - * initialize ioctl system - */ -void coioctl_init() -{ - cons_capabilities.info1 |= (CONS_CODRV1|CONS_CODRV2); -} - -/* - * process /dev/console ioctls - */ -int consioctl(dev_t dev, int cmd, caddr_t data, int flag) -{ - register error; - register struct tty *tp = dev2tty(dev); - - if (!tp) return ENXIO; - - /* mandatory */ - if (cmd==CONSGINFO) { - *((struct consinfo*)data) = cons_capabilities; - return 0; - } else if (cmd==OLDCONSGINFO) { - ((struct oldconsinfo*)data)->info1 = cons_capabilities.info1; - return 0; - } - - error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag); - if (error >= 0) - return (error); - error = ttioctl(tp, cmd, data, flag); - if (error >= 0) - return (error); - - /* must be executed for backward compatibility - * must be executed *after* the tty ioctls, - * because it shares an essential ioctl with them. - */ - error = kbdioctl(dev, cmd, data, flag); - return error==0 ? 0 : ENOTTY; -} - -/********************************************************************** - * utility functions for kbdioctl - *********************************************************************/ - -static struct vty *n2vty(int n) -{ - if (n == KBD_ACTVTY) return actvty; - if (n >= 0 && n < nvty) return &vtys[n]; - return 0; -} - -static void kioc_setlockkeys(dev_t dev,int snc) -{ - register struct vty *vp = dev2vty(dev); - if (!vp) { - register int i; - for (i=0; iscroll = snc & 1; - vp->num = (snc & 2) ? 1 : 0; - vp->caps = (snc & 4) ? 1 : 0; - } - vp = actvty; - } - vp->scroll = snc & 1; - vp->num = (snc & 2) ? 1 : 0; - vp->caps = (snc & 4) ? 1 : 0; - kbd_setleds (leds (vp)); -} - -static int kioc_assignleds(dev_t dev,int param) -{ - u_char ostate; - struct vty *vp; - register int i; - - if (param&4) { - /* used for initialization */ - for (i=0; ialtgrled = (param&3)==2; - vp->shiftled = (param&3)==1; - } - } else - { - vp = dev2vty(dev); - if (!vp) vp=actvty; - vp->altgrled = param==2; - vp->shiftled = param==1; - } - - vp = actvty; - kbd_setleds (leds (vp)); - ostate = vp->altgrled ? 2 : vp->shiftled ? 1 : 0; - return (ostate); -} - -static int kioc_dobeep(struct kbd_bell *data) -{ - if (data) { - int p, d; - - kbd_cvtsound(data->pitch, &p, data->duration, &d); - sysbeep (p, d); - } else - sysbeep (kbs.pitch, kbs.duration); - return 0; -} - -static int kioc_getbeep2(struct kbd_bell *data) -{ - struct vty *vp; - - if (data->nr == KBD_DEFLT) { - data->pitch = 1193180 / kbs.pitch; - data->duration = kbs.duration / hz * 1000; - return 0; - } - - vp = n2vty(data->nr); - if (!vp) return EINVAL; - - data->pitch = 1193180 / vp->pitch; - data->duration = vp->duration / hz * 1000; - return 0; -} - -static int kioc_setbeep2(struct kbd_bell *data) -{ - struct vty *vp; - - if (data->nr == KBD_DEFLT) { - kbd_cvtsound(data->pitch, &kbs.pitch, - data->duration, &kbs.duration); - return 0; - } - - vp = n2vty(data->nr); - if (!vp) return EINVAL; - - kbd_cvtsound(data->pitch, &vp->pitch, data->duration, &vp->duration); - return 0; -} - -/* - * process keyboard ioctls - */ - -static int ofl = 0; - -int kbdioctl(dev_t dev, int cmd, caddr_t data, int flag) -{ - int s,n,error; - struct vty *vp = dev2vty(dev); - - switch (cmd) { - /* CONSGINFO is mandatory ! */ - case CONSGINFO: - *((struct consinfo*)data) = cons_capabilities; - return 0; - case OLDCONSGINFO: - ((struct oldconsinfo*)data)->info1 = cons_capabilities.info1; - return 0; - case KBDFORCEASCII: - /* this is a relic from my youth mistakes */ - return 0; - case KBDCOLDRESET: - kbd_coldreset(); - kbd_ovlinit(); - kbd_settpmrate(KBD_TPD500|KBD_TPM100); - kioc_setlockkeys(dev,0); - return 0; - case KBDWARMRESET: - kbd_warmreset(); - return 0; - case KBDGTPMAT: - *(int *)data = kbs.tpmrate; - return 0; - case KBDSTPMAT: - kbd_settpmrate(*(int *)data); - return 0; - case KBDGREPSW: - *(int *)data = kbs.repeat; - return 0; - case KBDSREPSW: - kbs.repeat = (*(int *)data) & 1; - return 0; - case KBDGLEDS: - *(int *)data = kbs.ledstate; - return 0; - case KBDSLEDS: - kbd_setleds(*(int *)data); - return 0; - case KBDGLOCK: - if (!vp) vp=actvty; - *(int *)data = vp->scroll | (vp->num<<1) | - (vp->caps<<2) | (vp->altgrlock<<3) | - (vp->shiftlock<<4); - return 0; - case KBDSLOCK: - kioc_setlockkeys (dev,*(int *) data); - return 0; - case KBDSCAPSLED: /* assign CapsLock LED to AltgrLock or ShiftLock */ - *(int *) data = kioc_assignleds (dev, *(int *)data); - return 0; - case KBDGCAPSLED: - if (!vp) vp = actvty; - *(int *) data = vp->altgrled ? 2 : - vp->shiftled ? 1 : 0; - return 0; - case OLDKBDSETBEEP: - return kbd_cvtsound(((struct kbd_sound*)data)->pitch, &kbs.pitch, - ((struct kbd_sound*)data)->duration, &kbs.duration); - case KBDGETBEEP: - return kioc_getbeep2((struct kbd_bell*)data); - case KBDSETBEEP: - return kioc_setbeep2((struct kbd_bell*)data); - case KBDBELL: - case KBDSETBELL: /* compatibility */ - return kioc_dobeep((struct kbd_bell*)data); - case KBDGCKEY: - return kbd_getckeydef (((Ovl_tbl *)data)->keynum, (Ovl_tbl *) data); - case OLDKBDGCKEY: - { - int stat; - Ovl_tbl ok; - stat = kbd_getckeydef (ok.keynum, &ok); -#if XCHAR != u_char -ERROR! FIX ME! -#else - if (!stat) - bcopy(&ok,data,sizeof(struct oldkbd_ovlkey)); -#endif - return stat; - } - case KBDSCKEY: - return kbd_setkeydef((Ovl_tbl *)data); - case KBDGOKEY: - return kbd_getokeydef (((Ovl_tbl *)data)->keynum, (Ovl_tbl *) data); - case KBDRMKEY: - return kbd_rmkeydef (*(int *) data); - case KBDDEFAULT: - kbd_ovlinit(); - return 0; - case KBDSCLRLYR: - kbs.a0flag = (*(int*)data & 1) != 0; - kbs.c0flag = (*(int*)data & 2) != 0; - kbs.m0flag = (*(int*)data & 4) != 0; /*not used yet*/ - return 0; - case KBDGSPECF: - return kbd_gethotkey((struct kbd_hotkey *)data); - case KBDSSPECF: - return kbd_sethotkey((struct kbd_hotkey *)data); - - /* and now some special features which are cheap */ - case FIONREAD: - s = spltty(); - n = RB_LEN(&co_buf); - splx(s); - *(off_t*)data = n; - return 0; - - case TIOCSPGRP: - consoftc.cs_pgid = *(int*)data; - return 0; - - case TIOCGPGRP: - *(int*)data = consoftc.cs_pgid; - return 0; - - default: - /* backward compatibility */ - error = vgaioctl(dev, cmd, data, flag); - if (!error) return 0; - else return ENOTTY; - } - /*NOTREACHED*/ -} - -/********************************************************************** - * utility functions for vgaioctl - *********************************************************************/ - -static int chklim(struct vty *vp,int x0,int x1, int y0, int y1) -{ - if (x0 < 0 || x0 >= vp->ncol || - y0 < 0 || y0 >= vp->nrow || - x1 < 0 || x1 >= vp->ncol || - y1 < 0 || y1 >= vp->nrow || - x0 > x1 || y0 > y1) - return 1; - return 0; -} - -static int cpyblk(u_char *from, u_char *to, int mode, - int x0, int x1, int y0, int y1, int ncol) -{ - register x,y,n; - u_char *ofs,*ofs2; - u_short *ofs1; - - /* copy the requested data, could be optimized */ - - ofs = from; - switch (mode) { - case VGA_ATTR: - ofs++; - /*FALLTHRU*/ - - case VGA_TEXT: - ofs2 = ofs + y0*ncol*2; - for (n=0, y=y0; y<=y1; y++, ofs += (ncol*2) ) { - for (ofs2=ofs,x=x0; x<=x1; x++,n++,ofs2+=2) - *(to+n) = *ofs2; - } - break; - - case VGA_BOTH: - ofs1 = ((u_short*)from) + x0; - for (y=y0; y<=y1; y++, ofs1 += ncol) - bcopy(ofs1, to, (x1-x0+1)*2); - break; - - default: - return EINVAL; - } - return 0; -} - -static int getsz(struct vty *vp,struct vga_block *p) -{ - int sz; - - /* how much */ - if ((p->mode & 0x03) == VGA_SCREEN) { - p->x0 = 0; - p->y0 = 0; - p->x1 = vp->ncol-1; - p->y1 = vp->nrow-1; - sz = vp->size; - } - else { - if (chklim(vp,p->x0,p->x1,p->y0,p->y1)) - return 0; - sz = (p->x1-p->x0+1) * (p->y1-p->y0+1); - } - - if ((p->mode & 0x30) == VGA_BOTH) - sz *= 2; - - return sz; -} - -static int vioc_vgagetblock(struct vga_block *p) -{ - u_char *data = 0; - struct vty *vp; - int sz,x0,y0,x1,y1; - int error; - - /* check vty */ - vp = n2vty(p->pagenum); - if (!vp) return EINVAL; - - /* get size */ - if (!(sz=getsz(vp, p))) return EINVAL; - - data = (u_char*)malloc(sz, M_IOCTLOPS, M_WAITOK); - - /* copy the data into buffer */ - error = cpyblk( (u_char*)vp->Crtat, data, p->mode&0x30, - x0, x1, y0, y1, vp->ncol); - - /* to user process */ - if (!error) error = copyout(data, p->map, sz); - - /* clean up */ - free(data,M_IOCTLOPS); - return error; -} - -static int vioc_vgasetblock(struct vga_block *p) -{ - u_char *data = 0; - struct vty *vp; - int sz,x0,y0,x1,y1; - int error; - - /* check vty */ - vp = n2vty(p->pagenum); - if (!vp) return EINVAL; - - /* get size */ - if (!(sz=getsz(vp, p))) return EINVAL; - - data = (u_char*)malloc(sz, M_IOCTLOPS, M_WAITOK); - - error = copyin(p->map,data,sz); - - /* copy the requested data */ - error = cpyblk (data, (u_char*)vp->Crtat, p->mode&0x30, - x0, x1, y0, y1, vp->ncol); - - /* clean up */ - free(data,M_IOCTLOPS); - return error; -} - -static int vioc_gettextpage(struct textpage *tp) -{ - int i; - u_char *p = ((u_char*)Crtat) + tp->ad; - - if (tp->ad > 1 || tp->pagenum > 7) return EINVAL; - - for (i=0; isize; i++, p+=2) - tp->map[i] = *p; - return 0; -} - -static int vioc_settextpage(struct textpage *tp) -{ - int i; - u_char *p = ((u_char*)Crtat) + tp->ad; - - if (tp->ad > 1 || tp->pagenum > 7) return EINVAL; - - for (i=0; isize; i++,p+=2) - *p = tp->map[i]; - return 0; -} - -static int vioc_oldgetfontchar(struct fontchar *fc) -{ - int pg = fc->page; - u_char *ofs = (u_char*)Crtat + pg*0x4000; - - if (vds.cardtype < VG_EGA || pg < 0 || pg > 7) return EINVAL; - - vga_enablecg(pg); - bcopy(ofs+(fc->idx<<5),fc->cmap,VGA_FNTCSIZE); - vga_disablecg(); - - return 0; -} - -static int vioc_oldsetfontchar(struct fontchar *fc) -{ - int pg = fc->page; - u_char *ofs = (u_char*)Crtat + pg*0x4000; - - if (vds.cardtype < VG_EGA || pg < 0 || pg > 7) return EINVAL; - - vga_enablecg(pg); - bcopy(fc->cmap,ofs+(fc->idx<<5),VGA_FNTCSIZE); - vga_disablecg(); - - return 0; -} - -static int vioc_miscfunctions(struct miscfcns *f) -{ - switch(f->cmd) { - case 1: - vds.encoding[1] = NOFONT; - outw(0x3c4,0x0003); /* SA=0,SB=0 */ - break; - case 2: - f->u.enc[0] = vds.encoding[0]; - f->u.enc[1] = vds.encoding[1]; - break; - case 3: - vds.scrtimeout = f->u.timeout; /* seconds */ - vga_doblanking(BLANKSTART); - break; - case 4: - f->u.timeout = (vds.scrtimeout&0x3FFFFFFF) | - ((u_long)(vds.blanking&3)<<30); - break; - default: - return EINVAL; - } - return 0; -} - -static int vioc_oldsetfontmap(struct fontmap *data) -{ - int i,k; - int pg = data->page; - u_char *ofs = (u_char*)Crtat+pg*0x4000; - - if (vds.cardtype < VG_EGA || pg < 0 || pg > 7) return EINVAL; - vds.encoding[pg] = data->encoding; - - vga_enablecg(); - bzero(ofs,8192); - for(i=k=0; i<(VGA_FNTCSIZE*VGA_FNTNCHARS); k+=32,i+=VGA_FNTCSIZE) { - bcopy(&data->map[i],ofs+k,VGA_FNTCSIZE); - } - vga_disablecg(); - - /* enable SB/SBH when font 1 is loaded */ - if (pg==1 && data->encoding != NOFONT) outw(0x3c4,0x0403); /* SA=1,SB=0 */ - - return 0; -} - -static int vioc_oldgetfontmap(struct fontmap *data) -{ - int i,k; - int pg = data->page; - u_char *ofs = (u_char*)Crtat + pg*0x4000; - if (vds.cardtype < VG_EGA || pg < 0 || pg > 7) return EINVAL; - - data->encoding = vds.encoding[pg]; - - vga_enablecg(pg); - for(i=k=0; i<(VGA_FNTCSIZE*VGA_FNTNCHARS); k+=32,i+=VGA_FNTCSIZE) - bcopy(ofs+k,&data->map[i],VGA_FNTCSIZE); - vga_disablecg(); - - return 0; -} - -#define FSPACE 256*32 -#define FCHUNKS 32 - -static int vioc_setfontmap(struct fmap *f) -{ - u_char *ofs = (u_char*)Crtat + f->page*0x4000; - u_char *buf; - struct fchar *cibuf,fc[FCHUNKS]; - int error = 0; - int i,j,k; - int ec; - - /* XXX graphics */ - if (vds.cardtype < VG_EGA || f->page < 0 || f->page > 1 || - f->x > 9 || f->y > 16 || f->nr > 256) return EINVAL; - - if (f->nr > 0) { - vds.f89bit = vds.cardtype >= VG_EGA ? 9 : 8; - buf = (u_char*)malloc(FSPACE, M_IOCTLOPS, M_WAITOK); - bzero(buf, FSPACE); - - /* fill the buffer */ - cibuf = f->fntmap; - for (i = f->nr; i > 0; i -= FCHUNKS, cibuf+=FCHUNKS) { - j = i >= FCHUNKS ? FCHUNKS : i; - error = copyin(cibuf, fc, j*sizeof(struct fchar)); - if (error) goto erexit; - for (k=0; ky); - } - } - - /* set the extension bit (this is really switched in - * vga_disablecg() - */ - if (f->x < 9) - vds.f89bit = 8; - - /* move the data into CG space */ - vga_enablecg(); - bcopy(buf, ofs, FSPACE); - vga_disablecg(); - } - - /* this is a hack, as long as XCHAR == u_char */ - switch (f->start) { - case 0: - i = XLAT2PC8; break; - case 1: - i = NOFONT; break; - default: - case 2: - i = 0; break; - } - vds.encoding[f->page] = (u_short)i; - - /* enable SB/SBH when font 1 is loaded */ - if (f->page==1) { - if (f->nr > 0) outw(0x3c4,0x0403); /* SA=1,SB=0 */ - else outw(0x3c4,0x0003); - } - -erexit: - /* clean up */ - free(buf,M_IOCTLOPS); - - return error; -} - -static int vioc_getfontmap(struct fmap *f) -{ - u_char *ofs = (u_char*)Crtat + f->page*0x4000; - u_char *buf; - struct fchar *cobuf,fc[FCHUNKS]; - int error = 0; - int i,j,k; - - /* XXX graphics */ - if (vds.cardtype < VG_EGA || f->page < 0 || f->page > 1 || - f->nr > 0 && (f->x > 9 || f->y > 16) || (f->nr+f->start) > 256) return EINVAL; - - if (f->nr > 0) { - buf = (u_char*)malloc(FSPACE, M_IOCTLOPS, M_WAITOK); - - /* move the data from CG space */ - vga_enablecg(); - bcopy(ofs, buf, FSPACE); - vga_disablecg(); - - /* fill the buffer */ - cobuf = f->fntmap; - for (k = i = 0; inr; i++,k++) { - if (k==FCHUNKS) { - error = copyout(fc,cobuf,FCHUNKS*sizeof(struct fchar)); - if (error) goto erexit; - k = 0; - cobuf += FCHUNKS; - } - - fc[k].encoding = i+f->start; - bzero(fc[k].map,VGA_MAXX/8*VGA_MAXY); - bcopy(buf+32*(i+f->start),fc[k].map,f->y); - } - - /* copy the rest */ - error = copyout(fc,cobuf,k*sizeof(struct fchar)); - -erexit: - /* clean up */ - free(buf,M_IOCTLOPS); - } - - f->start = vds.encoding[f->page]; - - return error; -} - -/* - * execute my own vga ioctls - */ -/*ARGSUSED*/ -int vgaioctl(dev_t dev, int cmd, caddr_t data, int flag) -{ - switch(cmd) { - /* CONSGINFO is mandatory ! */ - case CONSGINFO: - *((struct consinfo*)data) = cons_capabilities; - return 0; - case OLDCONSGINFO: - ((struct oldconsinfo*)data)->info1 = cons_capabilities.info1; - return 0; - case CONSOLE_X_MODE: - return kbd_setxserveriopl(*(int*)data); - case VGASCURSOR: - return vga_setcshape((struct cursorshape*)data); - case VGAGCURSOR: - return vga_getcshape((struct cursorshape*)data); - case VGAGINFO: - return vga_getvideoinfo((struct videoinfo*)data); - case VGAGBLANK: - *(int*)data = (vds.scrtimeout&0x3FFFFFFF) | - ((u_long)(vds.blanking&3)<<30); - return 0; - case VGASBLANK: - vds.scrtimeout = *(int*)data; /* seconds */ - vga_doblanking(BLANKSTART); - return 0; - case VGAGBLOCK: - return vioc_vgagetblock((struct vga_block*)data); - case VGASBLOCK: - return vioc_vgasetblock((struct vga_block*)data); - case OLDVGASFNTMAP: - return vioc_oldsetfontmap((struct fontmap*)data); - case OLDVGAGFNTMAP: - return vioc_oldgetfontmap((struct fontmap*)data); - case VGASFONTMAP: - return vioc_setfontmap((struct fmap*)data); - case VGAGFONTMAP: - return vioc_getfontmap((struct fmap*)data); - case OLDVGAGCHAR: - return vioc_oldgetfontchar((struct fontchar*)data); - case OLDVGASCHAR: - return vioc_oldsetfontchar((struct fontchar*)data); - case VGAGPAGE: - return vioc_gettextpage((struct textpage*)data); - case VGASPAGE: - return vioc_settextpage((struct textpage*)data); - case VGAMISCFCNS: - return vioc_miscfunctions((struct miscfcns*)data); - case VGATAKECTRL: - /* No op for now */ - return 0; - case VGAGIVECTRL: - /* No op for now */ - return 0; - default: - return ENOTTY; - } - /*NOTREACHED*/ -} - -#endif /*!MINITERM*/ -#endif /*CODRV1*/ diff --git a/sys/i386/isa/codrv/co_cons.c b/sys/i386/isa/codrv/co_cons.c deleted file mode 100644 index a7db6f14bc..0000000000 --- a/sys/i386/isa/codrv/co_cons.c +++ /dev/null @@ -1,441 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * Copyright (c) 1992 by Holger Veit - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * William Jolitz and Don Ahn. - * Significant parts are added and rewritten by Holger Veit. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)co_cons.c $Revision: 1.11 $ (Contributed to 386bsd) $Date: 93/01/23 23:14:33 $ - */ -static char *rcsid = "$Header: /usr/src/sys.386bsd/i386/isa/codrv/RCS/co_cons.c,v 1.11 93/01/23 23:14:33 root Exp Locker: root $"; - -/* - * History: See CO_HISTORY - */ - -#include "co.h" -#include "pc.h" -#if NCO == 1 -#if NPC == 0 - -/* - * code to work keyboard & display for PC-style console - */ -#include "co_hdr.h" - -unsigned __debug = 0; /* 0xffe, exported to elsewhere */ -static unsigned __color; - -struct tty *dev2tty(dev_t dev) -{ - register n = minor(dev); - - /* also checks valid minor # */ - if (n < nvty) - return &pccons[n]; - else - return 0; -} - -static int tty2vty(struct tty *tp) -{ - int i; - for (i=0; it_oproc = pcstart; - tp->t_param = pcparam; - tp->t_dev = dev; - - consoftc.cs_flags |= CO_OPEN|CO_INITTTY; - consoftc.cs_opencnt++; - - if ((tp->t_state & TS_ISOPEN) == 0) { - tp->t_state |= TS_WOPEN; - ttychars(tp); - tp->t_iflag = TTYDEF_IFLAG; - tp->t_oflag = TTYDEF_OFLAG; - tp->t_cflag = TTYDEF_CFLAG; - tp->t_lflag = TTYDEF_LFLAG; - tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; - tp->t_iflag &= ~ISTRIP; - pcparam(tp, &tp->t_termios); - ttsetwater(tp); - } else if (tp->t_state&TS_XCLUDE && p->p_ucred->cr_uid != 0) - return (EBUSY); - tp->t_state |= TS_CARR_ON; - return ((*linesw[tp->t_line].l_open)(dev, tp)); -} - -/* - * Close a vty - */ -/*ARGSUSED*/ -int pcclose(dev_t dev, int flag, int mode, struct proc *p) -{ - register struct tty *tp = dev2tty(dev); - struct vty *vp = &vtys[minor(dev)]; - - if (!tp) return ENXIO; - - /* decrement vty reference count */ - if (vp->ttycnt > 0) vp->ttycnt--; - - if (consoftc.cs_opencnt==0) { - consoftc.cs_flags &= ~CO_OPEN; - - /* reset the keyboard state */ - reset_kbd_flags(); - initrb(&co_buf); - } - else - consoftc.cs_opencnt--; - - (*linesw[tp->t_line].l_close)(tp, flag); - ttyclose(tp); - - return(0); -} - -/* - * read from vty - */ -/*ARGSUSED*/ -int pcread(dev_t dev, struct uio *uio, int flag) -{ - register struct tty *tp = dev2tty(dev); - if (!tp) return ENXIO; - - /* this does not belong to here, but anybody always wants to - strip the 8th bit, very likely the shell */ - tp->t_iflag &= ~ISTRIP; - return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); -} - -/* - * write to vty - */ -/*ARGSUSED*/ -int pcwrite(dev_t dev, struct uio *uio, int flag) -{ - register struct tty *tp = dev2tty(dev); - if (!tp) return ENXIO; - - /* we allow writing, but we don't know where it goes to */ - /*if (consoftc.cs_flags & CO_OPENRAW) return EBUSY; */ - - return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); -} - -/* - * execute vty ioctl - * This does no longer accept keyboard and vga oriented ioctls - */ -int pcioctl(dev_t dev, int cmd, caddr_t data, int flag) -{ - /* call the ioctl handler */ - return consioctl(dev, cmd, data, flag); -} - -int pcconsintr = 1; -/* - * Got a console transmission interrupt - - * the console processor wants another character. - * - * -hv- is this really used? - */ -pcxint(dev) - dev_t dev; -{ - register struct tty *tp = dev2tty(dev); - register int unit; - - if (!tp) return; - - if (!pcconsintr) - return; - tp->t_state &= ~TS_BUSY; - consoftc.cs_timo = 0; - if (tp->t_line) - (*linesw[tp->t_line].l_start)(tp); - else - pcstart(tp); -} - -int pcstart(register struct tty *tp) -{ - int c, s, vp; - - s = spltty(); - if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) - goto out; - do { - if (RB_LEN(&tp->t_out) <= tp->t_lowat) { - if (tp->t_state&TS_ASLEEP) { - tp->t_state &= ~TS_ASLEEP; - wakeup((caddr_t)&tp->t_out); - } - if (tp->t_wsel) { - selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); - tp->t_wsel = 0; - tp->t_state &= ~TS_WCOLL; - } - } - if (RB_LEN(&tp->t_out) == 0) - goto out; - c = getc(&tp->t_out); - tp->t_state |= TS_BUSY; - splx(s); - vp = tty2vty(tp); - if (vp < 0) panic("pcstart: unknown vty"); - sput(vp, c, 0); - tp->t_state &= ~TS_BUSY; - (void)spltty(); - } while(1); -out: - splx(s); -} - -/* interface for console device */ -void pccnprobe(struct consdev *cp) -{ - int maj; - int dev = 0; - - /* locate the major number */ - for (maj = 0; maj < nchrdev; maj++) - if (cdevsw[maj].d_open == pcopen) - break; - -maj = 12; - /* initialize required fields */ - cp->cn_dev = makedev(maj, 0); - cp->cn_tp = &pccons[0]; - cp->cn_pri = CN_INTERNAL; -} - -/* interface for console device */ -/* ARGSUSED */ -void pccninit(struct consdev *cp) -{ - /* - * For now, don't screw with it. - */ - /* crtat = 0; */ -} - -/* interface for console device */ -/* ARGSUSED */ -void pccnputc(dev_t dev, int c) -{ - int clr = __color; - - if (!dev2tty(dev)) return; /* ignore if invalid */ - - if (clr == 0) - clr = 0x30; - else - clr |= 0x60; - - if (c == '\n') - sput(minor(dev), '\r', clr); - sput(minor(dev), c, clr); -} - -#ifdef notyetused -/* - * Print a character on console. - */ -pcputchar(c, tp) - char c; - register struct tty *tp; -{ - sput(0, c, 0x2); - if (c=='\n') getchar(); -} -#endif - -/* interface for console device */ -/* ARGSUSED */ -int pccngetc(dev_t dev) -{ - register int s; - register XCHAR *cp; - - s = spltty(); /* block cointr while we poll */ - while ((cp = kbd_sgetc(0))==NULL); - splx(s); - if (*cp == '\r') return('\n'); - return (char)*cp; -} - -#ifdef notyetused -pcgetchar(tp) - register struct tty *tp; -{ - XCHAR *cp; - - cp = kbd_sgetc(0); /* this is surely ASCII */ - return (char)(*cp&0xff); -} -#endif - -/* - * Set line parameters - */ -int pcparam(struct tty *tp, struct termios *t) -{ - register int cflag = t->c_cflag; - /* and copy to tty */ - tp->t_ispeed = t->c_ispeed; - tp->t_ospeed = t->c_ospeed; - tp->t_cflag = cflag; - - return(0); -} - -#ifdef KDB -/* - * Turn input polling on/off (used by debugger). - */ -/*ARGSUSED*/ -int pcpoll(int onoff) -{ -} -#endif - -#ifdef OLD_PATCHKIT -int pg(char *p,int q,int r,int s,int t,int u,int v,int w,int x,int y,int z) -{ - printf(p,q,r,s,t,u,v,w,x,y,z); - printf("\n"); - return(getchar()); -} -#endif - -/* special characters */ -#define bs 8 -#define lf 10 -#define cr 13 -#define cntlc 3 -#define del 0177 -#define cntld 4 - -int getchar() -{ - XCHAR thechar,*c; - register delay; - int x; - - consoftc.cs_flags |= CO_POLLING; - x = splhigh(); - sput(0, '>', 1); - /*while (1) {*/ - while ((c=kbd_sgetc(0))==NULL); - thechar = *c; - consoftc.cs_flags &= ~CO_POLLING; - splx(x); - switch (thechar) { - default: if (thechar >= ' ') - sput(0, thechar, 1); - return(thechar); - case cr: - case lf: sput(0, '\r', 1); - sput(0, '\n', 1); - return(lf); - case bs: - case del: - sput(0, '\b', 1); - sput(0, ' ', 1); - sput(0, '\b', 1); - return(thechar); - case cntlc: - sput(0, '^', 1) ; sput(0, 'C', 1) ; sput(0, '\r', 1) ; sput(0, '\n', 1) ; - cpu_reset(); - case cntld: - sput(0, '^', 1) ; sput(0, 'D', 1) ; sput(0, '\r', 1) ; sput(0, '\n', 1) ; - return(0); - } - /*}*/ -} - -#ifdef notyetused -#include "machine/stdarg.h" -static nrow; - -#define DPAUSE 1 -void -#ifdef __STDC__ -dprintf(unsigned flgs, const char *fmt, ...) -#else -dprintf(flgs, fmt /*, va_alist */) - char *fmt; - unsigned flgs; -#endif -{ extern unsigned __debug; - va_list ap; - - if((flgs&__debug) > DPAUSE) { - __color = ffs(flgs&__debug)+1; - va_start(ap,fmt); - kprintf(fmt, 1, (struct tty *)0, ap); - va_end(ap); - if (flgs&DPAUSE || nrow%24 == 23) { - int x; - x = splhigh(); - if (nrow%24 == 23) nrow = 0; - (void)kbd_sgetc(0); - splx(x); - } - } - __color = 0; -} -#endif - -#endif /* NPC=0 */ -#endif /* NCO=1 */ diff --git a/sys/i386/isa/codrv/co_hdr.h b/sys/i386/isa/codrv/co_hdr.h deleted file mode 100644 index dbae645547..0000000000 --- a/sys/i386/isa/codrv/co_hdr.h +++ /dev/null @@ -1,490 +0,0 @@ -/* - * General declarations for CO driver - * - * Copyright 1992 by Holger Veit - * May be freely used with Bill Jolitz's port of - * 386bsd and may be included in a 386bsd collection - * as long as binary and source are available and reproduce the above - * copyright. - * - * You may freely modify this code and contribute improvements based - * on this code as long as you don't claim to be the original author. - * Commercial use of this source requires permittance of the copyright - * holder. A general license for 386bsd will override this restriction. - * - * Use at your own risk. The copyright holder or any person who makes - * this code available for the public (administrators of public archives - * for instance) are not responsible for any harm to hardware or software - * that might happen due to wrong application or program faults. - * - * @(#) $RCSfile: co_hdr.h,v $ $Revision: 1.11 $ (Contributed to 386bsd) $Date: 93/01/23 23:14:39 $ - * - * History: see CO_HISTORY - */ - -#ifndef _CO_HDR_H_ -#define _CO_HDR_H_ -#ifndef GLOBAL -#define GLOBAL extern -#endif - -#include "param.h" -#include "malloc.h" -#include "conf.h" -#include "ioctl.h" -#include "proc.h" -#include "user.h" -#include "tty.h" -#include "uio.h" -#include "fcntl.h" -#include "i386/isa/isa_device.h" -#include "callout.h" -#include "systm.h" -#include "kernel.h" -#include "vnode.h" -#include "syslog.h" -#include "i386/isa/icu.h" -#include "i386/i386/cons.h" -#include "i386/isa/isa.h" -#include "i386/isa/ic/i8042.h" -#include "i386/isa/kbd.h" -#include "machine/pc/display.h" -#include "machine/psl.h" -#include "machine/frame.h" -#include "ioctl_pc.h" - -/* - * user adjustable constants - */ - -/*#define NETBSD*/ /* define this if you have NetBSD */ - -#define SCRSAVER 2 /* 1=standard moving block */ - /* 2=moving snake (thanks: Christoph Robitschko) */ - -#define BLANKTIMEOUT 10*60 /* Default Screen blanking after 10*60 seconds */ -#define KBDTIMEOUT 0x15000l /* timeout parameter for a keyboard response - * adjust for slow keyboard at a fast computer - * (e.g. 486DX laptop) - */ -#define KBDRETRIES 5 /* max. no. of tries to reset the keyboard in - * probing loop, avoids hanging forever if - * keyboard does not reply like expected - */ - -#define FAT_CURSOR /* full size cursor, undef if to leave unchanged */ - - -/* default screen attributes */ -#define DEF_STD_BGAT BG_BLACK /* standard background */ -#define DEF_STD_M_FGAT FG_LIGHTGREY /* standard mono foreground */ -#define DEF_STD_C_FGAT FG_LIGHTGREY /* standard color foreground */ -#define DEF_KERN_BGAT BG_BLACK /* kernelmode background */ -#define DEF_KERN_M_FGAT FG_UNDERLINE /* kernelmode mono foreground */ -#define DEF_KERN_C_FGAT FG_LIGHTBLUE /* XXX (to distinguish) kernelmode color foreground */ -#define DEF_SO_M_AT FG_INTENSE|BG_LIGHTGREY /* standout mono mode */ -#define DEF_SO_C_AT FG_YELLOW|BG_BLACK /* standout color mode */ - -/*****************NO USER DEFINABLE DATA BELOW THIS LINE*******************/ - -/* this is an anachronism in the moment: there is no ioctl other than CODRV1 - * public available. If you want to write a different ioctl set - * based on the core system, read co_codrv1.c as a template. - */ -#define CODRV1 1 - -/* I dunno why this had to be changed! */ -#ifndef NETBSD -#include "ddb.h" -#else /* is NetBSD */ -#undef NDDB -#ifdef DDB -#define NDDB 1 -#else -#define NDDB 0 -#endif -#endif /* NetBSD */ - -#define DEFAULTCOL 80 -#define DEFAULTROW 25 -#define CHR 2 - /* Don't think these locations are true virtual addrs! */ -#define MONO_BUF 0xfe0b0000 -#define CGA_BUF 0xfe0b8000 -#define EGA_BIOS 0xfe0c0000 -#define ALTEGA_BIOS 0xfe0e0000 /* alternate video BIOS location */ -#define IOPHYSMEM 0xa0000 - -#define MONO_BASE 0x3b4 -#define CGA_BASE 0x3d4 - -/* 6845 register definitions */ -#define M6845_ADDR 0 -#define M6845_HTOTAL 0 -#define M6845_HDISPL 1 -#define M6845_HSYNC 2 -#define M6845_SYNCWIDTH 3 -#define M6845_VTOTAL 4 -#define M6845_VTOTADJ 5 -#define M6845_VDISPL 6 -#define M6845_VSYNC 7 -#define M6845_IMODE 8 -#define M6845_MAXSCAN 9 -#define M6845_CURSTART 0x0A -#define M6845_CUREND 0x0B -#define M6845_STARTADRH 0x0C -#define M6845_STARTADRL 0x0D -#define M6845_CURSORH 0x0E -#define M6845_CURSORL 0x0F -#define M6845_LPENH 0x10 -#define M6845_LPENL 0x11 - -/* for screentimeout */ -#define BLANKSTART 0 /* for reset and change */ -#define BLANKSTOP 1 /* temporarily suspend */ - -#define MAXESCPARAM 10 /* max # of ESC parameters */ - -/* for fonts */ -#define XLAT2PC8 0xFFFE -#define NOFONT 0xFFFD -#define ISOLATIN1 0 - -/* abbreviation for LED control */ -#define leds(vp) ((vp->altgrled ? \ - vp->altgrlock : vp->shiftled ? \ - vp->shiftlock : vp->caps) << 2 | \ - vp->num<<1 | vp->scroll) - -/* - * local types - */ - - -#define KBDMAXOVLKEYSIZE 15 /* excl. zero byte */ -#define KBDDEFOVLKEYSIZE 4 /* excl. zero byte */ - -/* This defines the actually used table for key assignments, and is - * dynamically assigned. - */ -typedef struct kbd_ovlkey Ovl_tbl; - -/* This defines the default table for key assignments. This table does - * not have entries for META, ALTGR, SHIFTALTGR like the overloaded key - * table - */ -typedef struct { - u_short type; /* type of key */ - Ovl_tbl *ovlptr; /* -vak pointer into overload table */ - XCHAR unshift[KBDDEFOVLKEYSIZE+1]; /* default codes */ - XCHAR shift[KBDDEFOVLKEYSIZE+1]; - XCHAR ctrl[KBDDEFOVLKEYSIZE+1]; -} Keycap_def; - -#define XC0 { 0,1,1,1 } -#define XC1(a) { a,0,1,1 } -#define XC2(a,b) { a,b,0,1 } -#define XC3(a,b,c) { a,b,c,0 } -#define XC4(a,b,c,d) { a,b,c,d } -#define XE3(a,b) { '\033',a,b,0 } -#if XCHAR == u_char -#define xc_bcopy(src,dst,sz) bcopyb(src,dst,sz) -#define xc_char2xc(X) (X) -#else -ERROR! CHANGE xc_bcopy!!!!!! -#endif - -/* This is the special functions lookup table (local version of - * struct kbd_special - */ - -#define MAXNROFSPEC 30 /* so many keys may have special functions */ -typedef struct { - u_short key; - u_short modifier; - u_short scan; - u_short function; -} Spec_tbl; - -/* noise, noise, noise (local structure) */ -struct kbd_sound { - int pitch; /* Frequency in Hz */ - int duration; /* Time in msec */ -}; - -/* - * global variables - */ -GLOBAL struct consinfo cons_capabilities; -GLOBAL volatile char vtswsema; -extern u_short *Crtat; /* only absolute address in video area */ -extern Keycap_def kbd_keytab[]; - -GLOBAL struct consoftc { - int cs_mymajor; - int cs_flags; -#define CO_INITRB 0x001 -#define CO_INITTTY 0x002 -#define CO_OPEN 0x004 /* cs_opencnt != 0 */ -#define CO_OPENRAW 0x008 -#define CO_POLLING 0x010 /* polling for input */ -#define CO_ACTIVE 0x020 /* timeout active (unused) */ -#define CO_ASYNC 0x040 /* Async I/O mode */ -#define CO_RDWAIT 0x080 /* blocked on read */ - - int cs_timo; /* timeouts since interrupt (unused) */ - u_long cs_wedgecnt; /* times restarted (unused)*/ - u_long cs_ovfl; /* buffer overflows */ -#ifdef OLDPATCHKIT - struct proc *cs_selp; /* Process waiting for select call */ -#else - pid_t cs_rsel; /* Process waiting for select call */ -#endif - int cs_pgid; /* Process group for ASYNC I/O */ -/*XXX*/ struct tty *cs_constty; /* used to restore constty if */ - /* anyone dares to steal CONSOLE during raw open */ - struct vty *cs_actvty; /* active vty which owns the /dev/kbd */ - int cs_opencnt; /* counter for opens from vtys */ -} consoftc; - -GLOBAL struct kbdstate { - - int pitch; /* default pitch of beep */ - int duration; /* default duration of beep */ - -/* XXX could be bitfields. But why? */ - u_char m0flag; /* clear META-map flag */ - u_char c0flag; /* clear CRTL-map flag */ - u_char a0flag; /* clear ALT-map flag */ - u_char ledstate; /* current state of kbd LEDS */ - - u_char shift_down; /* shift pressed */ - u_char ctrl_down; /* ctrl pressed */ - u_char meta_down; /* meta pressed */ - u_char altgr_down; /* altgr pressed */ - u_char repeat; /* allow key repetition */ - u_char id; /* keyboard id */ - int tpmrate; /* repetition rate/delay */ -} kbs; /* kbd state */ - -GLOBAL struct videostate { - char blanking; - char color; - char f89bit; /* 8/9 bit flag */ - short cardtype; /* set by device probe */ - short cardsubtype; /* chipset version */ - short ram; /* set by whoami */ - int iobase; - int scrtimeout; /* Timeout for screenblanker */ - u_short encoding[2]; /* font encoding */ - short _atiext; /* ATI VGA special regs */ -} vds; - -/* poor man's C++. The usage of these attributes is NOT FOR NONSENSE */ -#define PRIVATE -#define PUBLIC - -/* VT central data structure */ -struct vty { -PRIVATE u_short *Crtat; /* ptr to virtual video page */ -PRIVATE u_short *crtat; /* ptr to virtual cursor position */ -PRIVATE u_short *vbuf; /* video buffer */ - -PUBLIC char so; /* in standout mode? */ -PUBLIC char vtynum; /* to get vt# from vtyptr */ -PUBLIC u_short ttycnt; /* open reference counter */ -PUBLIC struct tty *ttyp; /* pointer to virtual tty information */ - -PUBLIC u_short so_at; /* standout attribute */ - -PUBLIC struct outmode { - u_short fg_at,bg_at; /* kernel attributes */ - u_short def_at; /* default attribute */ - char f2; /* select second font */ - char escstate; /* escape state */ - char parcnt; /* param count */ - int param[MAXESCPARAM]; /* ESC parameters */ - } om[2]; /* 0 = std, 1 = kernel */ -PUBLIC struct outmode *op; /* pointer to actual set */ - -PUBLIC short row, col; /* current cursor position */ -PUBLIC short nrow, ncol; /* current screen geometry */ -PUBLIC short size; /* size of video space */ -PUBLIC u_char visible; /* =1 is visible */ -PUBLIC u_char scroll; /* =1, scrolllock active */ -PUBLIC u_char caps; /* caps lock active */ -PUBLIC u_char num; /* num lock active */ -PUBLIC u_char shiftlock; /* shift lock active */ -PUBLIC u_char altgrlock; /* altgr lock active */ -PUBLIC u_char altgrled; /* -vak- CAPS led is ALTGR-LOCK */ -PUBLIC u_char shiftled; /* -vak- CAPS led is CAPS-LOCK */ - -PUBLIC int pitch; /* vty dependent sound */ -PUBLIC int duration; -}; - -/* TTY structure for virtual terminals */ -extern struct vty vtys[]; /* vty data */ -extern struct tty pccons[]; /* vty-tty buffers */ -extern int nvty; /* # of available vtys */ -extern struct tty *constty; /* console tty */ -GLOBAL struct vty *actvty; /* pointer to actual vty */ - -/* Ring buffer of the raw co device */ -GLOBAL struct ringb co_buf; - -/* - * prototypes - */ - -/* in co_cons.c */ -extern struct tty *dev2tty(dev_t dev); -extern int pcopen(dev_t dev, int flag, int mode, struct proc*); -extern int pcclose(dev_t dev, int flag, int mode, struct proc*); -extern int pcread(dev_t dev, struct uio *uio, int flag); -extern int pcwrite(dev_t dev, struct uio *uio, int flag); -extern int pcioctl(dev_t dev, int cmd, caddr_t data, int flag); -extern int pcstart(struct tty *tp); -extern void pccnprobe(struct consdev *cp); -extern void pccninit(struct consdev *cp); -extern void pccnputc(dev_t dev, int c); -extern int pccngetc(dev_t dev); -extern int pcparam(struct tty*,struct termios*); -extern int pcpoll(int onoff); -extern int pg(char *p,int q,int r,int s,int t,int u,int v,int w,int x,int y,int z); -extern int getchar(); - -/* in co_kbd.c */ -extern void reset_kbd_flags(); -extern int coopen(dev_t dev, int flag, int mode, struct proc *p); -extern int coclose(dev_t dev, int flag, int mode, struct proc *p); -extern int coread(dev_t dev, struct uio *uio, int flag); -extern void cointr(dev_t dev, int irq, int cpl); -extern void cowakeup(); -extern int coselect(dev_t dev, int rw, struct proc *p); -extern int coioctl(dev_t dev, int cmd, caddr_t data, int flag); -extern int comap(dev_t dev, int offset, int nprot); -extern int coprobe(struct isa_device *dev); -extern void kbd_setleds(int ledval); -extern void kbd_ovlinit(); -extern int kbd_getokeydef(u_int key,Ovl_tbl *thisdef); -extern int kbd_getckeydef(u_int key,Ovl_tbl *thisdef); -extern int kbd_getspecial(struct kbd_hotkey *data); -extern int kbd_setspecial(struct kbd_hotkey *data); -extern int sgetc(int noblock); /* compatibility */ -extern XCHAR *kbd_sgetc(int noblock); -extern void kbd_settpmrate(int rate); -extern int kbd_rmkeydef(u_int key); -extern int kbd_setkeydef(Ovl_tbl *data); -extern int kbd_cvtsound(int ipitch, int *opitch, int idur, int *odur); - -/* in co_vga.c */ -extern void cons_highlight(); -extern void cons_normal(); -extern int coattach(struct isa_device *dev); -extern void vga_setcursorpos(int pos); -extern void vga_cursor(int a); -extern void vga_doblanking(int fct); -extern void sput(int vtynum, XCHAR c, int ka); -extern void consinit(); -extern void vga_whoami(); -extern int vga_setcshape(struct cursorshape *data); -extern int vga_getcshape(struct cursorshape *data); -extern int vga_getvideoinfo(struct videoinfo *data); -extern void vga_enablecg(); -extern void vga_disablecg(); -extern int vga_xlatiso646(struct vty *vp,u_short *at,u_short *sat,int c); -/* emulator support */ -extern void vga_cursorup(struct vty *vp, int n); -extern void vga_cursordown(struct vty *vp, int n); -extern void vga_cursorleft(struct vty *vp, int n); -extern void vga_cursorright(struct vty *vp, int n, int wrap); -extern void vga_scrollup(struct vty *vp,int n, int cm); -extern void vga_scrolldown(struct vty *vp, int n); -extern void vga_cursormove(struct vty *vp, int x, int y); -extern void vga_cursorrelative(struct vty *vp, int dx, int dy); -extern void vga_clearcursor(struct vty *vp, int mode); -extern void vga_clearline(struct vty *vp, int mode); -extern void vga_deleteline(struct vty *vp, int n); -extern void vga_insertline(struct vty *vp, int n); -extern void vga_deletechars(struct vty *vp, int n); -extern void vga_insertchars(struct vty *vp, int n); -extern void vga_setattributes(struct vty *vp, int mode, int attr); -extern void vga_selectfont(struct vty *vp,int fontnr); -extern void vga_wrtchar(struct vty *vp, u_int c, u_int at); -extern int vga_checkcursor(struct vty *vp); -extern void vga_sendchar(struct vty *vp, XCHAR c); -extern void vga_initvideo(); - -/* in co_pc3.c */ -extern void vtemul_init(); /* initialize terminal emulator */ -extern void vtemul_exec(struct vty*,XCHAR); /* process data */ - -/* in co_vty.c */ -extern struct vty *dev2vty(dev_t dev); -extern void vty_init(int first); -extern void vty_setactive(int vtyno,int sw); -extern void vty_next(); -extern void vty_previous(); -extern void vty_broadcast(const char *fmt,...); - -/* in co_codrv1.c/co_codrv2.c, etc. */ -extern void coioctl_init(); -extern int consioctl(dev_t dev, int cmd, caddr_t data, int flag); -extern int kbdioctl(dev_t dev, int cmd, caddr_t data, int flag); -extern int vgaioctl(dev_t dev, int cmd, caddr_t data, int flag); - -/******************************************************************* - * The video console multiplexer (not yet) - ******************************************************************/ - -/* in future this will be a struct of pointers to functions, and - * will be filled by a "module_init call - */ - -#ifdef GFX_CONSOLE -#define emul_cursorup gfx_cursorup -#define emul_cursordown gfx_cursordown -#define emul_cursorleft gfx_cursorleft -#define emul_cursorright gfx_cursorright -#define emul_scrollup gfx_scrollup -#define emul_scrolldown gfx_scrolldown -#define emul_cursormove gfx_cursormove -#define emul_cursorrelative gfx_cursorrelative -#define emul_clearcursor gfx_clearcursor -#define emul_clearline gfx_clearline -#define emul_deleteline gfx_deleteline -#define emul_insertline gfx_insertline -#define emul_deletechars gfx_deletechars -#define emul_insertchars gfx_insertchars -#define emul_setattributes gfx_setattributes -#define emul_selectfont gfx_selectfont -#define emul_wrtchar gfx_wrtchar -#define emul_checkcursor gfx_checkcursor -#define emul_sendchar gfx_sendchar -#define emul_initvideo gfx_initvideo -#else -#define emul_cursorup vga_cursorup -#define emul_cursordown vga_cursordown -#define emul_cursorleft vga_cursorleft -#define emul_cursorright vga_cursorright -#define emul_scrollup vga_scrollup -#define emul_scrolldown vga_scrolldown -#define emul_cursormove vga_cursormove -#define emul_cursorrelative vga_cursorrelative -#define emul_clearcursor vga_clearcursor -#define emul_clearline vga_clearline -#define emul_deleteline vga_deleteline -#define emul_insertline vga_insertline -#define emul_deletechars vga_deletechars -#define emul_insertchars vga_insertchars -#define emul_setattributes vga_setattributes -#define emul_selectfont vga_selectfont -#define emul_wrtchar vga_wrtchar -#define emul_checkcursor vga_checkcursor -#define emul_sendchar vga_sendchar -#define emul_initvideo vga_initvideo -#endif - -#endif /* _CO_HDR_H_ - diff --git a/sys/i386/isa/codrv/co_kbd.c b/sys/i386/isa/codrv/co_kbd.c deleted file mode 100644 index 6189afa467..0000000000 --- a/sys/i386/isa/codrv/co_kbd.c +++ /dev/null @@ -1,1466 +0,0 @@ -/*- - * Copyright 1992,1993 by Holger Veit - * Some parts of this code may still resemble code - * derived from software contributed to Berkeley by William Jolitz - * and Don Ahn. - * May be freely used with Bill Jolitz's port of - * 386bsd and may be included in a 386bsd collection - * as long as binary and source are available and reproduce the above - * copyright. - * - * You may freely modify this code and contribute improvements based - * on this code as long as you don't claim to be the original author. - * Commercial use of this source requires permittance of the copyright - * holder. A general license for 386bsd will override this restriction. - * - * Use at your own risk. The copyright holder or any person who makes - * this code available for the public (administrators of public archives - * for instance) are not responsible for any harm to hardware or software - * that might happen due to wrong application or program faults. - * - * - * @(#) $RCSfile: co_kbd.c,v $ $Revision: 1.12 $ (Contributed to 386bsd) $Date: 93/01/23 23:14:42 $ - */ -static char rcsid[] = "$Header: /usr/src/sys.386bsd/i386/isa/codrv/RCS/co_kbd.c,v 1.12 93/01/23 23:14:42 root Exp Locker: root $"; - -/* - * History: see CO_HISTORY - */ - -/* - * code to work keyboard & display for PC-style console - */ -#include "co.h" -#include "pc.h" -#if NCO == 1 -#if NPC == 0 -#include "vty.h" - -#define GLOBAL -#include "co_hdr.h" -#undef GLOBAL - -struct consinfo cons_capabilities = -{ - CONS_ISCO | CONS_HASKBD | CONS_HASKEYNUM - | CONS_USEPC8 | CONS_HASKCAP - | CONS_HASFNT - | CONS_HASPX386 -#if NVTY > 1 - | CONS_HASVTY -#endif -#ifdef PC3 - | CONS_HASPC3 -#endif - ,0l, 0l, 0l, - - /* dont write this as a "string", this is XCHAR data! */ - { 'c', 'o', 'd', 'r', 'v', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - - /* filled in by vtemul_init */ - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - - /* reserved */ - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } -}; - -/* - * static variables for co_kbd - */ -static XCHAR *more_chars; -static u_char lastkey = 0; /* save for last key */ -static u_char extended = 0; /* extended scan code */ -static char resetdone = 0; /* reset flag */ -static u_char altkpflag = 0; /* in ALT-KP mode */ -static u_short altkpval = 0; /* accumulated value of ALT-KP */ - -static XCHAR *kbd_xlatkey2ascii(u_int key); -static u_int kbd_sgetk(int); - -/* CO device support */ -struct isa_driver codriver = { - coprobe, coattach, "co", -}; - -#define CO_RDPRI (PZERO+1) - -void reset_kbd_flags() -{ - /* when closing pccons, it may be that a ctrl key is still pending */ - kbs.shift_down = - kbs.ctrl_down = - kbs.meta_down = - kbs.altgr_down = - extended = - altkpflag = 0; -} - -/* - * CO is the raw keyboard device == /dev/kbd - * PC is the TTY-type keyboard device == /dev/console - * CO has precedence over PC, so if the XSERVER opens /dev/kbd, - * no word will ever be heard from /dev/console, until X closes it again - * - */ - -/* ARGSUSED */ -int coopen(dev_t dev, int flag, int mode, struct proc *p) -{ - consoftc.cs_mymajor = major(dev); - - /* minor 128 is /dev/vga */ - if (minor(dev)==0x80) return 0; - - /* exclusive open only */ - if (consoftc.cs_flags & CO_OPENRAW) - return EBUSY; - - /* someone wants to write to the keyboard? */ - if (flag & (FWRITE|FAPPEND)) - return ENODEV; - - consoftc.cs_flags |= CO_OPENRAW; - - /* save constty for later */ - consoftc.cs_constty = constty; - - /* save active vty */ - consoftc.cs_actvty = actvty; - - /* initialize the buffer */ - consoftc.cs_flags |= CO_INITRB; - initrb(&co_buf); - - /* signal opening process only */ -#ifdef OLDPATCHKIT - consoftc.cs_selp = 0; -#else - consoftc.cs_rsel = 0; -#endif - consoftc.cs_pgid = p->p_pid; - - /* lex xserver */ - if (minor(dev)==1) { - kbd_setxserveriopl(1); - vty_setactive(-1,0); - } - - return 0; -} - -/* ARGSUSED */ -int coclose(dev_t dev, int flag, int mode, struct proc *p) -{ - register struct tty *tp = actvty->ttyp; - - /* minor 128 is /dev/vga */ - if (minor(dev)==0x80) return 0; - - consoftc.cs_flags &= ~(CO_OPENRAW|CO_ASYNC); -#ifdef OLDPATCHKIT - consoftc.cs_selp = 0; -#else - consoftc.cs_rsel = 0; -#endif - consoftc.cs_pgid = 0; - - /* do we need to restore console? - * check whether we had a console and now have no longer one: - * then someone grabbed it (xterm -C) and died, - * of course the old constty must still be open - */ - if (consoftc.cs_constty && !constty && consoftc.cs_constty->t_state) { - constty = consoftc.cs_constty; - constty->t_state |= TS_CARR_ON; - ttwakeup(constty); - } - - if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_CARR_ON)==0) { - /* usually console hangs in ttopen (hack) */ - tp->t_state |= TS_CARR_ON; - ttwakeup(tp); - } - - if (minor(dev)==1) { - kbd_setxserveriopl(0); - vty_setactive(actvty->vtynum, 3); - } - - return(0); -} - -/* ARGSUSED */ -int coread(dev_t dev, struct uio *uio, int flag) -{ - register c, n = 0; - int error,s; - - /* minor 128 is /dev/vga */ - if (minor(dev)==0x80) return ENXIO; - - s = spltty(); - while ( (n=RB_LEN(&co_buf))<=0 ) { - if (flag & IO_NDELAY) { - splx(s); - return EWOULDBLOCK; - } - consoftc.cs_flags |= CO_RDWAIT; - if (error= tsleep((caddr_t)&co_buf,CO_RDPRI|PCATCH,"kbd",0)) { - splx(s); - return error; - } - } - splx(s); - consoftc.cs_flags &= ~CO_RDWAIT; - - while ((c = getc(&co_buf)) > 0) { - error = ureadc(c,uio); - if (error || uio->uio_resid == 0) - break; - } - return error; -} - -/* - * Got a console receive interrupt - - * the console processor wants to give us a character. - * Catch the character in a buffer - */ -void cointr(dev_t dev, int irq, int cpl) -{ - int c, error; - XCHAR *cp; - register struct tty *tp; - - /* open buffer ? */ - if ((consoftc.cs_flags & CO_INITRB) == 0) { - initrb(&co_buf); - consoftc.cs_flags |= CO_INITRB; - } - - /* vt switch in progress? */ - if (vtswsema) return; - - /* get a keynum */ - c = kbd_sgetk(1); - - /* spurious? */ - if ((c&0x7F)==0) return; - - /* must restore display? */ - if (vds.blanking) vga_doblanking(BLANKSTART); - - /* in poll mode ? */ - if (consoftc.cs_flags & CO_POLLING) return; - - /* no open device */ - if ((consoftc.cs_flags & (CO_OPEN|CO_OPENRAW)) == 0) - return; - - if (consoftc.cs_flags & CO_OPENRAW) { - - /* has the vty, which was active when /dev/kbd was opened - * still control? If yes, move data to /dev/kbd, else - * to the active vty - */ - if (actvty==consoftc.cs_actvty) { - /* save byte */ - error = putc(c,&co_buf); - if (error) { /* buffer full */ - printf("Keyboard buffer overflow"); - initrb(&co_buf); /* flushing all */ - consoftc.cs_ovfl++; - } - /* if anyone is interested, tell it that there is data */ - cowakeup(); - return; - } - } - - /* convert to ASCII and give to tty driver */ - cp = kbd_xlatkey2ascii(c); - if (cp==0) return; - - tp = actvty->ttyp; - if (actvty->ttycnt==0) return; /* ignore if not open */ - - /* notice: a \000 Byte is an ASCIZ string \000\000 actually */ - if (*cp==0 && *(cp+1)==0) - (*linesw[tp->t_line].l_rint)((*cp++ & 0xff), tp); - else while (*cp) - (*linesw[tp->t_line].l_rint)((*cp++ & 0xff), tp); - return; -} - -void cowakeup() -{ - struct proc *p; - - if (!(consoftc.cs_flags & CO_OPENRAW)) - return; -#ifdef OLDPATCHKIT - if (consoftc.cs_selp) { - selwakeup(consoftc.cs_selp,0); - consoftc.cs_selp = 0; - } -#else - if (consoftc.cs_rsel) { - selwakeup(consoftc.cs_rsel,0); - consoftc.cs_rsel = 0; - } -#endif - if (consoftc.cs_flags & CO_ASYNC) { - if (consoftc.cs_pgid < 0) - gsignal(-consoftc.cs_pgid,SIGIO); - else if (p=pfind(consoftc.cs_pgid)) - psignal(p,SIGIO); - } - if (consoftc.cs_flags & CO_RDWAIT) { - wakeup((caddr_t)&co_buf); - consoftc.cs_flags &= ~CO_RDWAIT; - } -} - -int coselect(dev_t dev, int rw, struct proc *p) -{ - register nread; - int s = spltty(); - if (rw==FREAD) { - nread = RB_LEN(&co_buf); - if (nread > 0) { - splx(s); - return 1; - } -#ifdef OLDPATCHKIT - consoftc.cs_selp = p; -#else - consoftc.cs_rsel = p->p_pid; -#endif - } - splx(s); - return 0; -} - -int coioctl(dev_t dev, int cmd, caddr_t data, int flag) -{ - register error; - - switch (minor(dev)) { - case 0: - case 1: - /* /dev/kbd */ - return kbdioctl(dev,cmd,data,flag); - case 128: - /* /dev/vga */ - return vgaioctl(dev,cmd,data,flag); - default: - return ENOTTY; - } - /*NOTREACHED*/ -} - -/* might be put into vga.c, because it is its memory */ -int comap(dev_t dev, int offset, int nprot) -{ - if (offset > 0x20000) - return -1; - return i386_btop((0xa0000 + offset)); -} - -void kbd_wait_ibf() -{ - int i; - - for (i=0; i 0 && (kbs.id=inb(KBDATAP)) == KBR_RSTDONE); -/* kbd_wait_obf();*/ - if (!i) kbs.id = 0xFF; -/* kbs.id = inb(KBDATAP);*/ - - resetdone = 1; -} - -/* set the LEDs */ -void kbd_setleds(int ledval) -{ - kbs.ledstate = ledval; - - kbd_cmd(KBC_STSIND); - outb(KBOUTP,ledval); -} - -#ifndef MINITERM -/* set the typematic rate */ -void kbd_settpmrate(int rate) -{ - kbs.tpmrate = rate & 0x7f; - - kbd_cmd(KBC_SETTPM); - outb(KBOUTP, kbs.tpmrate); -} -#endif /*!MINITERM*/ - -int coprobe(struct isa_device *dev) -{ - register struct consoftc *p = &consoftc; - register struct kbdstate *k = &kbs; - - /* reset consoftc structure */ - p->cs_flags = 0; - p->cs_timo = 0; - p->cs_wedgecnt = 0l; - p->cs_ovfl = 0l; - p->cs_opencnt = 0; - - k->pitch = 0x31b; - k->duration = hz/4; - k->ledstate = 0; - k->repeat = 1; - - extended = - vds.blanking = 0; - - /* VT switch semaphore */ - vtswsema = 0; - - /* init keyboard ring buffer */ - p->cs_flags |= CO_INITRB; - initrb(&co_buf); - - /* reset keyboard */ - kbd_coldreset(); - - kbd_setleds(leds(actvty)); - -#ifndef MINITERM - k->tpmrate = /*KBD_TPD500|KBD_TPM100*/ 0x20|0x0c; - kbd_settpmrate(k->tpmrate); -#endif - - /* clear the overlay table */ - kbd_ovlinit(); - - return 1; -} - -#define MAXKEYNUM 127 -#ifndef XTKBDMODE - -#define KMASK 0xFF -#define BREAKKEY 0x80 -#define PRINTKEY 0x7C - -/* This one is for AT scan codes (preferred) */ -static char scantokey[] = { -/* -0- -1- -2- -3- -4- -5- -6- -7- This layout is valid for US only */ -/*00*/ 0, 120, 0, 116, 114, 112, 113, 123, /* ?? F9 ?? F5 F3 F1 F2 F12 */ -/*08*/ 0, 121, 119, 117, 115, 16, 1, 0, /* ?? F10 F8 F6 F4 TAB ` ?? */ -/*10*/ 0, 60, 44, 0, 58, 17, 2, 0, /* ?? ALl SHl ?? CTl Q 1 ?? */ -/*18*/ 0, 0, 46, 32, 31, 18, 3, 0, /* ?? Z S A W 2 ?? ?? */ -/*20*/ 0, 48, 47, 33, 19, 5, 4, 0, /* ?? C X D E 4 3 ?? */ -/*28*/ 0, 61, 49, 34, 21, 20, 6, 0, /* ?? SP V F T R 5 ?? */ -/*30*/ 0, 51, 50, 36, 35, 22, 7, 0, /* ?? N B H G Y 6 ?? */ -/*38*/ 0, 0, 52, 37, 23, 8, 9, 0, /* ?? ?? M J U 7 8 ?? */ -/*40*/ 0, 53, 38, 24, 25, 11, 10, 0, /* ?? , K I O 0 9 ?? */ -/*48*/ 0, 54, 55, 39, 40, 26, 12, 0, /* ?? . / L ; P - ?? */ -/*50*/ 0, 0, 41, 0, 27, 13, 0, 0, /* ?? ?? " ?? [ = ?? ?? */ -/*58*/ 30, 57, 43, 28, 0, 29, 0, 0, /* CAP SHr ENT ] ?? \ ?? ?? */ -/*60*/ 0, 45, 0, 0, 0, 0, 15, 0, /* ?? NL1 ?? ?? ?? ?? BS ?? */ -/*68*/ 0, 93, 0, 92, 91, 0, 0, 0, /* ?? KP1 ?? KP4 KP7 ?? ?? ?? */ -/*70*/ 99, 104, 98, 97, 102, 96, 110, 90, /* KP0 KP. KP2 KP5 KP6 KP8 ESC NUM */ -/*78*/ 122, 106, 103, 105, 100, 101, 125, 0, /* F11 KP+ KP3 KP- KP* KP9 LOC ?? */ -/*80*/ 126, 0, 0, 118, /* BREAK ?? ?? F7 */ -/* ^^^ - * -hv- we use 0x80 for a pseudo scan code for this shit break key - */ -}; - -static char extscantokey[] = { -/* -0- -1- -2- -3- -4- -5- -6- -7- This layout is valid for US only */ -/*00*/ 0, 120, 0, 116, 114, 112, 113, 123, /* ?? F9 ?? F5 F3 F1 F2 F12 */ -/*08*/ 0, 121, 119, 117, 115, 16, 1, 0, /* ?? F10 F8 F6 F4 TAB ` ?? */ -/*10*/ 0, 62, 124, 0, 64, 17, 2, 0, /* ?? ALr PSc ?? CTr Q 1 ?? */ -/*18*/ 0, 0, 46, 32, 31, 18, 3, 0, /* ?? Z S A W 2 ?? ?? */ -/*20*/ 0, 48, 47, 33, 19, 5, 4, 0, /* ?? C X D E 4 3 ?? */ -/*28*/ 0, 61, 49, 34, 21, 20, 6, 0, /* ?? SP V F T R 5 ?? */ -/*30*/ 0, 51, 50, 36, 35, 22, 7, 0, /* ?? N B H G Y 6 ?? */ -/*38*/ 0, 0, 52, 37, 23, 8, 9, 0, /* ?? ?? M J U 7 8 ?? */ -/*40*/ 0, 53, 38, 24, 25, 11, 10, 0, /* ?? , K I O 0 9 ?? */ -/*48*/ 0, 54, 95, 39, 40, 26, 12, 0, /* ?? . KP/ L ; P - ?? */ -/*50*/ 0, 0, 41, 0, 27, 13, 0, 0, /* ?? ?? " ?? [ = ?? ?? */ -/*58*/ 30, 57, 108, 28, 0, 29, 0, 0, /* CAP SHr KPE ] ?? \ ?? ?? */ -/*60*/ 0, 45, 0, 0, 0, 0, 15, 0, /* ?? NL1 ?? ?? ?? ?? BS ?? */ -/*68*/ 0, 81, 0, 79, 80, 0, 0, 0, /* ?? END ?? LA HOM ?? ?? ?? */ -/*70*/ 75, 76, 84, 97, 89, 83, 110, 90, /* INS DEL DA KP5 RA UA ESC NUM */ -/*78*/ 122, 106, 86, 105, 124, 85, 125, 0, /* F11 KP+ PD KP- PSc PU LOC ?? */ -/*80*/ 126, 0, 0, 118, /* BREAK ?? ?? F7 */ -}; -#else - -/* in SNAFU mode, try this ones (XTKBDMODE) */ - -#define KMASK 0x7F -#define BREAKKEY 0x7F -#define PRINTKEY 0x7E - -static char scantokey[] = { -/* -0- -1- -2- -3- -4- -5- -6- -7- This layout is valid for US only */ -/*00*/ 0, 110, 2, 3, 4, 5, 6, 7, /* ?? F9 1 2 3 4 5 6 */ -/*08*/ 8, 9, 10, 11, 12, 13, 15, 16, /* 7 8 9 0 - = BS TAB */ -/*10*/ 17, 18, 19, 20, 21, 22, 23, 24, /* Q W E R T Y U I */ -/*18*/ 25, 26, 27, 28, 43, 58, 31, 32, /* O P [ ] ENT CTl A S */ -/*20*/ 33, 34, 35, 36, 37, 38, 39, 40, /* D F G H J K L ; */ -/*28*/ 41, 1, 44, 29, 46, 47, 48, 49, /* ' ' SHl NL1 Z X C V */ -/*30*/ 50, 51, 52, 53, 54, 55, 57, 100, /* B N M , . / SHr KP* */ -/*38*/ 60, 61, 30, 112, 113, 114, 115, 116, /* ALl SP CAP F1 F2 F3 F4 F5 */ -/*40*/ 117, 118, 119, 120, 121, 90, 125, 91, /* F6 F7 F8 F9 F10 NUM LOC KP7 */ -/*48*/ 96, 101, 105, 92, 97, 102, 106, 93, /* KP8 KP9 KP- KP4 KP5 KP6 KP+ KP1 */ -/*50*/ 98, 103, 99, 104, 0, 0, 56, 122, /* KP2 KP3 KP0 KP. ?? ?? NL2 F11 */ -/*58*/ 123, 0, 0, 0, 0, 0, 0, 0, /* F12 ?? ?? ?? ?? ?? ?? ?? */ -/*60*/ 0, 0, 0, 0, 0, 0, 0, 0, /* ?? ?? ?? ?? ?? ?? ?? ?? */ -/*68*/ 0, 0, 0, 0, 0, 0, 0, 0, /* ?? ?? ?? ?? ?? ?? ?? ?? */ -/*70*/ 0, 0, 0, 0, 0, 0, 0, 0, /* ?? ?? ?? ?? ?? ?? ?? ?? */ -/*78*/ 0, 0, 0, 0, 0, 0, 124, 126, /* ?? ?? ?? ?? ?? ?? ?? ?? */ -/* ^^^ ^^^ - * -hv- we use 0x7E for a pseudo scan code for the printscreen kludge - * -hv- we use 0x7f for a pseudo scan code for this shit break key - */ -}; - -static char extscantokey[] = { -/* -0- -1- -2- -3- -4- -5- -6- -7- This layout is valid for US only */ -/*00*/ 0, 110, 2, 3, 4, 5, 6, 7, /* ?? F9 1 2 3 4 5 6 */ -/*08*/ 8, 9, 10, 11, 12, 13, 15, 16, /* 7 8 9 0 - = BS TAB */ -/*10*/ 17, 18, 19, 20, 21, 22, 23, 24, /* Q W E R T Y U I */ -/*18*/ 25, 26, 27, 28, 108, 64, 31, 32, /* O P [ ] KPE CTr A S */ -/*20*/ 33, 34, 35, 36, 37, 38, 39, 40, /* D F G H J K L ; */ -/*28*/ 41, 1, 44, 29, 46, 47, 48, 49, /* ' ' SHl NL1 Z X C V */ -/*30*/ 50, 51, 52, 53, 54, 95, 57, 100, /* B N M , . KP/ SHr KP* */ -/*38*/ 62, 61, 30, 112, 113, 114, 115, 116, /* ALr SP CAP F1 F2 F3 F4 F5 */ -/*40*/ 117, 118, 119, 120, 121, 90, 125, 80, /* F6 F7 F8 F9 F10 NUM LOC HOM */ -/*48*/ 83, 85, 105, 79, 97, 89, 106, 81, /* UA PU KP- LA KP5 RA KP+ END */ -/*50*/ 84, 86, 75, 76, 0, 0, 56, 122, /* DA PD INS DEL ?? ?? NL2 F11 */ -/*58*/ 123, 0, 0, 0, 0, 0, 0, 0, /* F12 ?? ?? ?? ?? ?? ?? ?? */ -/*60*/ 0, 0, 0, 0, 0, 0, 0, 0, /* ?? ?? ?? ?? ?? ?? ?? ?? */ -/*68*/ 0, 0, 0, 0, 0, 0, 0, 0, /* ?? ?? ?? ?? ?? ?? ?? ?? */ -/*70*/ 0, 0, 0, 0, 0, 0, 0, 0, /* ?? ?? ?? ?? ?? ?? ?? ?? */ -/*78*/ 0, 0, 0, 0, 0, 0, 124, 126, /* ?? ?? ?? ?? ?? ?? ?? ?? */ -/* ^^^ ^^^ - * -hv- we use 0x7E for a pseudo scan code for the printscreen kludge - * -hv- we use 0x7f for a pseudo scan code for this shit break key - */ -}; -#endif - -static int key2scan(int keynum) -{ - int i = 0; - while (i<=MAXKEYNUM && keynum != scantokey[i]) i++; - return i==MAXKEYNUM ? -1 : i; -} - -#define KBD_ALPHA KBD_ASCII|KBD_DOCAPS -Keycap_def kbd_keytab[] = -{ -/* type ptr unshift shift ctrl */ -/* DONT EVER OVERLOAD KEY 0, THIS IS A KEY THAT MUSTN'T EXIST */ -/* 0*/ KBD_NONE, 0, XC2('d','f'), XC0, XC0, -/* 1*/ KBD_ASCII, 0, XC1('`'), XC1('~'), XC1('`'), -/* 2*/ KBD_ASCII, 0, XC1('1'), XC1('!'), XC1('!'), -/* 3*/ KBD_ASCII, 0, XC1('2'), XC1('@'), XC1(0), -/* 4*/ KBD_ASCII, 0, XC1('3'), XC1('#'), XC1('#'), -/* 5*/ KBD_ASCII, 0, XC1('4'), XC1('$'), XC1('$'), -/* 6*/ KBD_ASCII, 0, XC1('5'), XC1('%'), XC1('%'), -/* 7*/ KBD_ASCII, 0, XC1('6'), XC1('^'), XC1('\036'), -/* 8*/ KBD_ASCII, 0, XC1('7'), XC1('&'), XC1('&'), -/* 9*/ KBD_ASCII, 0, XC1('8'), XC1('*'), XC1('\010'), -/* 10*/ KBD_ASCII, 0, XC1('9'), XC1('('), XC1('('), -/* 11*/ KBD_ASCII, 0, XC1('0'), XC1(')'), XC1(')'), -/* 12*/ KBD_ASCII, 0, XC1('-'), XC1('_'), XC1('\037'), -/* 13*/ KBD_ASCII, 0, XC1('='), XC1('+'), XC1('+'), -/* 14*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 15*/ KBD_ASCII, 0, XC1('\177'), XC1('\177'), XC1('\010'), -/* 16*/ KBD_ASCII, 0, XC1('\t'), XC2('\177','\t'), XC1('\t'), -/* 17*/ KBD_ALPHA, 0, XC1('q'), XC1('Q'), XC1('\021'), -/* 18*/ KBD_ALPHA, 0, XC1('w'), XC1('W'), XC1('\027'), -/* 19*/ KBD_ALPHA, 0, XC1('e'), XC1('E'), XC1('\005'), -/* 20*/ KBD_ALPHA, 0, XC1('r'), XC1('R'), XC1('\022'), -/* 21*/ KBD_ALPHA, 0, XC1('t'), XC1('T'), XC1('\024'), -/* 22*/ KBD_ALPHA, 0, XC1('y'), XC1('Y'), XC1('\031'), -/* 23*/ KBD_ALPHA, 0, XC1('u'), XC1('U'), XC1('\025'), -/* 24*/ KBD_ALPHA, 0, XC1('i'), XC1('I'), XC1('\011'), -/* 25*/ KBD_ALPHA, 0, XC1('o'), XC1('O'), XC1('\017'), -/* 26*/ KBD_ALPHA, 0, XC1('p'), XC1('P'), XC1('\020'), -/* 27*/ KBD_ASCII, 0, XC1('['), XC1('{'), XC1('\033'), -/* 28*/ KBD_ASCII, 0, XC1(']'), XC1('}'), XC1('\035'), -/* 29*/ KBD_ASCII, 0, XC1('\\'), XC1('|'), XC1('\034'), -/* 30*/ KBD_CAPS, 0, XC0, XC0, XC0, -/* 31*/ KBD_ALPHA, 0, XC1('a'), XC1('A'), XC1('\001'), -/* 32*/ KBD_ALPHA, 0, XC1('s'), XC1('S'), XC1('\023'), -/* 33*/ KBD_ALPHA, 0, XC1('d'), XC1('D'), XC1('\004'), -/* 34*/ KBD_ALPHA, 0, XC1('f'), XC1('F'), XC1('\006'), -/* 35*/ KBD_ALPHA, 0, XC1('g'), XC1('G'), XC1('\007'), -/* 36*/ KBD_ALPHA, 0, XC1('h'), XC1('H'), XC1('\010'), -/* 37*/ KBD_ALPHA, 0, XC1('j'), XC1('J'), XC1('\n'), -/* 38*/ KBD_ALPHA, 0, XC1('k'), XC1('K'), XC1('\013'), -/* 39*/ KBD_ALPHA, 0, XC1('l'), XC1('L'), XC1('\014'), -/* 40*/ KBD_ASCII, 0, XC1(';'), XC1(':'), XC1(';'), -/* 41*/ KBD_ASCII, 0, XC1('\''), XC1('"'), XC1('\''), -/* 42*/ KBD_ASCII, 0, XC1('\\'), XC1('|'), XC1('\034'), /* special */ -/* 43*/ KBD_ASCII, 0, XC1('\r'), XC1('\r'), XC1('\n'), /* RETURN */ -/* 44*/ KBD_SHIFT, 0, XC0, XC0, XC0, /* SHIFT left */ -/* 45*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 46*/ KBD_ALPHA, 0, XC1('z'), XC1('Z'), XC1('\032'), -/* 47*/ KBD_ALPHA, 0, XC1('x'), XC1('X'), XC1('\030'), -/* 48*/ KBD_ALPHA, 0, XC1('c'), XC1('C'), XC1('\003'), -/* 49*/ KBD_ALPHA, 0, XC1('v'), XC1('V'), XC1('\026'), -/* 50*/ KBD_ALPHA, 0, XC1('b'), XC1('B'), XC1('\002'), -/* 51*/ KBD_ALPHA, 0, XC1('n'), XC1('N'), XC1('\016'), -/* 52*/ KBD_ALPHA, 0, XC1('m'), XC1('M'), XC1('\r'), -/* 53*/ KBD_ASCII, 0, XC1(','), XC1('<'), XC1('<'), -/* 54*/ KBD_ASCII, 0, XC1('.'), XC1('>'), XC1('>'), -/* 55*/ KBD_ASCII, 0, XC1('/'), XC1('?'), XC1('\177'), -/* 56*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 57*/ KBD_SHIFT, 0, XC0, XC0, XC0, /* SHIFT right */ -/* 58*/ KBD_CTL, 0, XC0, XC0, XC0, /* CTL left */ -/* 59*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 60*/ KBD_META, 0, XC0, XC0, XC0, /* ALT left */ -/* 61*/ KBD_ASCII, 0, XC1(' '), XC1(' '), XC1(' '), /* SPACE */ -/* 62*/ KBD_META, 0, XC0, XC0, XC0, /* ALT right */ -/* 63*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 64*/ KBD_CTL, 0, XC0, XC0, XC0, /* CTL right */ -/* 65*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 66*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 67*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 68*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 69*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 70*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 71*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 72*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 73*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 74*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 75*/ KBD_FUNC, 0, XE3('[','L'), XE3('|','a'), XE3('|','b'), /* INS */ -/* 76*/ KBD_FUNC, 0, XC1('\177'), XE3('|','c'), XE3('|','d'), /* DEL */ -/* 77*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 78*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 79*/ KBD_FUNC, 0, XE3('[','D'), XE3('|','e'), XE3('|','f'), /* CU <- */ -/* 80*/ KBD_FUNC, 0, XE3('[','H'), XE3('|','g'), XE3('|','h'), /* HOME */ -/* 81*/ KBD_FUNC, 0, XE3('[','F'), XE3('|','i'), XE3('|','j'), /* END */ -/* 82*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 83*/ KBD_FUNC, 0, XE3('[','A'), XE3('|','k'), XE3('|','l'), /* CU ^ */ -/* 84*/ KBD_FUNC, 0, XE3('[','B'), XE3('|','m'), XE3('|','n'), /* CU v */ -/* 85*/ KBD_FUNC, 0, XE3('[','I'), XE3('|','o'), XE3('|','p'), /* PG UP */ -/* 86*/ KBD_FUNC, 0, XE3('[','G'), XE3('|','q'), XE3('|','r'), /* PG DN */ -/* 87*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 88*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 89*/ KBD_FUNC, 0, XE3('[','C'), XE3('|','s'), XE3('|','t'), /* CU -> */ -/* 90*/ KBD_NUM, 0, XC0, XC0, XC0, -/* 91*/ KBD_KP, 0, XC1('7'), XE3('[','H'), XC1('7'), -/* 92*/ KBD_KP, 0, XC1('4'), XE3('[','D'), XC1('4'), -/* 93*/ KBD_KP, 0, XC1('1'), XE3('[','F'), XC1('1'), -/* 94*/ KBD_NONE, 0, XC0, XC0, XC0, -/* 95*/ KBD_KP, 0, XC1('/'), XC1('/'), XC1('/'), -/* 96*/ KBD_KP, 0, XC1('8'), XE3('[','A'), XC1('8'), -/* 97*/ KBD_KP, 0, XC1('5'), XE3('[','E'), XC1('5'), -/* 98*/ KBD_KP, 0, XC1('2'), XE3('[','B'), XC1('2'), -/* 99*/ KBD_KP, 0, XC1('0'), XE3('[','L'), XC1('0'), -/*100*/ KBD_KP, 0, XC1('*'), XC1('*'), XC1('*'), -/*101*/ KBD_KP, 0, XC1('9'), XE3('[','I'), XC1('9'), -/*102*/ KBD_KP, 0, XC1('6'), XE3('[','C'), XC1('6'), -/*103*/ KBD_KP, 0, XC1('3'), XE3('[','G'), XC1('3'), -/*104*/ KBD_KP, 0, XC1('.'), XC1('\177'), XC1('.'), -/*105*/ KBD_KP, 0, XC1('-'), XC1('-'), XC1('-'), -/*106*/ KBD_KP, 0, XC1('+'), XC1('+'), XC1('+'), -/*107*/ KBD_NONE, 0, XC0, XC0, XC0, -/*108*/ KBD_ASCII, 0, XC1('\r'), XC1('\r'), XC1('\n'), /* RETURN */ -/*109*/ KBD_NONE, 0, XC0, XC0, XC0, -/*110*/ KBD_ASCII, 0, XC1('\033'), XC1('\033'), XC1('\033'), -/*111*/ KBD_NONE, 0, XC0, XC0, XC0, -/*112*/ KBD_FUNC, 0, XE3('[','M'), XE3('[','Y'), XE3('[','k'), /* F1 */ -/*113*/ KBD_FUNC, 0, XE3('[','N'), XE3('[','Z'), XE3('[','l'), /* F2 */ -/*114*/ KBD_FUNC, 0, XE3('[','O'), XE3('[','a'), XE3('[','m'), /* F3 */ -/*115*/ KBD_FUNC, 0, XE3('[','P'), XE3('[','b'), XE3('[','n'), /* F4 */ -/*116*/ KBD_FUNC, 0, XE3('[','Q'), XE3('[','c'), XE3('[','o'), /* F5 */ -/*117*/ KBD_FUNC, 0, XE3('[','R'), XE3('[','d'), XE3('[','p'), /* F6 */ -/*118*/ KBD_FUNC, 0, XE3('[','S'), XE3('[','e'), XE3('[','q'), /* F7 */ -/*119*/ KBD_FUNC, 0, XE3('[','T'), XE3('[','f'), XE3('[','r'), /* F8 */ -/*120*/ KBD_FUNC, 0, XE3('[','U'), XE3('[','g'), XE3('[','s'), /* F9 */ -/*121*/ KBD_FUNC, 0, XE3('[','V'), XE3('[','h'), XE3('[','t'), /* F10 */ -/*122*/ KBD_FUNC, 0, XE3('[','W'), XE3('[','i'), XE3('[','u'), /* F11 */ -/*123*/ KBD_FUNC, 0, XE3('[','X'), XE3('[','j'), XE3('[','v'), /* F12 */ -/*124*/ KBD_KP, 0, XE3('[','w'), XE3('[','x'), XE3('[','y'), -/*125*/ KBD_SCROLL, 0, XC0, XC0, XC0, -/*126*/ KBD_BREAK, 0, XC0, XC0, XC0, -/*127*/ KBD_NONE, 0, XC0, XC0, XC0, -}; - -static char keypad2num[] = { - 7, 4, 1, -1, -1, 8, 5, 2, 0, -1, 9, 6, 3, -1, -1, -1, -1 -}; - -/* - * Overloaded definitions are stored in Ovl_tbl structure, - * allocated via malloc() and pointed by kbd_keytab[].ovlptr. -vak- - * A table entry consists of - * a short, holding the new type attribute and 4 entries for a new - * keydef. - * Note that as long there is enough space, you may overload any key: - * You may assign "rm -rf /\n" to the ENTER key, so you have it directly, - * when you need it, but you can also make a big SHIFT key by overloading - * the SPACE bar. But these are the more uncommon cases. - */ - -void kbd_ovlinit() -{ - register i; - - for (i=0; i<=MAXKEYNUM; i++) - kbd_keytab[i].type &= (KBD_MASK|KBD_DOCAPS); - - kbs.m0flag = kbs.c0flag = kbs.a0flag = 0; -} - -/* get original key def */ -int kbd_getokeydef(u_int key,Ovl_tbl *thisdef) -{ - if (key==0 || key>MAXKEYNUM) return EINVAL; - - thisdef->keynum = key; - thisdef->type = kbd_keytab[key].type; - -#define copydef(src,dst)\ - xc_bcopy(src,dst,KBDDEFOVLKEYSIZE) - - copydef(kbd_keytab[key].unshift,thisdef->unshift); - copydef(kbd_keytab[key].shift,thisdef->shift); - copydef(kbd_keytab[key].ctrl,thisdef->ctrl); - - copydef(kbd_keytab[key].unshift,thisdef->altgr); /* deliver at least anything */ - copydef(kbd_keytab[key].shift,thisdef->shiftaltgr); - copydef(kbd_keytab[key].unshift,thisdef->meta); -/* this defeated EMACS META-SPACE */ -/* if (thisdef->meta [0]) */ - thisdef->meta[0] ^= 0x80; -#undef copydef - - return 0; -} - -/* get current key def */ -int kbd_getckeydef(u_int key,Ovl_tbl *thisdef) -{ - if (key>MAXKEYNUM) - return EINVAL; - - if (kbd_keytab[key].type & KBD_OVERLOAD) - *thisdef = *kbd_keytab[key].ovlptr; - else - kbd_getokeydef(key,thisdef); - - return 0; -} - -Spec_tbl *spec_tbl = NULL; -static int kbd_nrspec = 0; - -#define SPECINVALID ((Spec_tbl*)0xFFFFFFFF) - -static Spec_tbl *kbd_findspec(u_short key,u_short modifier) -{ - int i; - - if (!spec_tbl) spec_tbl = malloc ( - MAXNROFSPEC*sizeof(Spec_tbl), M_DEVBUF, M_WAITOK); - /* hv must check whether space is really there */ - if (!spec_tbl) return SPECINVALID; - - for (i=0; ikey==0 || data->key > MAXKEYNUM) - return EINVAL; - p = kbd_findspec(data->key,data->modifier); - if (p==SPECINVALID) return ENOMEM; - if (p) { - data->key = p->key; - data->modifier = p->modifier; - data->function = p->function; - } else - /* not found, clear all */ - data->key = data->modifier = data->function = 0; - - return 0; -} - -int kbd_sethotkey(struct kbd_hotkey *data) -{ - int k; - Spec_tbl *p; - - if (data->key > MAXKEYNUM) - return EINVAL; - p = kbd_findspec(data->key,data->modifier); - if (p==SPECINVALID) return ENOMEM; - if (!p) { - /* not found: enter. Find empty slot */ - for (k=0; k=MAXNROFSPEC) return ENOMEM; - p = &spec_tbl[k]; - } else k = -1; - - if (data->modifier == KBD_HOTKEYDELETE) { - p->key = 0; - p->modifier = 0; - p->function = 0; - p->scan = 0; - } else - { - p->key = data->key; - p->modifier = data->modifier; - p->function = data->function; - p->scan = key2scan(p->key); - if (k==kbd_nrspec) kbd_nrspec++; - } - return 0; -} - -#ifndef MINITERM -/* - * management of diacritical characters - * in the future this table will be editable - */ -static char *diacriticals[] = -{ - 0, - "` ```A\300a\340E\310e\350I\314i\354O\322o\362U\331u\371", - "' '''A\301a\341E\311e\351I\315i\355O\323o\363U\332u\372Y\335", - "^ ^^^A\302a\342E\312e\312I\316i\356O\324o\364U\333u\373", - "~ ~~~A\303a\343N\321n\361O\325o\365", - "\250A\304a\344\E\313e\353I\316i\357O\326o\366U\334u\374y\377", - "\270C\307c\347", - "\260\260\260A\305a\345", - 0 -}; -static char diacflag = 0; - -static XCHAR *kbd_xlatdiac(u_short type,XCHAR *ch) -{ - int i; - char *s; - - /* may by NULL pointer, a rich source for crashes */ - if (!ch) return ch; - - if (diacflag) { - for (s=diacriticals[diacflag]+1, diacflag=0; *s; s+=2) - if (*s==(char)*ch) { - *ch= (XCHAR)*(s+1); - return ch; - } - return ch; - } else - { - if (type & KBD_DIACPFX) { - for (i=1; diacriticals[i]; i++) - if (*ch== *(diacriticals[i])) { - diacflag = i; - return 0; - } - } - return ch; - } -} -#endif /*!MINITERM*/ - -/* - * kbd_xlatkey2ascii: takes a keynum and the current shift/ctrl/etc. state and - * delivers an appropriate ASCII string - */ -static XCHAR *kbd_xlatkey2ascii(u_int key) -{ - struct vty *vp = actvty; - struct consoftc *cs = &consoftc; - static XCHAR metachar[2]; - static Ovl_tbl thisdef; - u_short n,type; - char isreleased = key & 0x80; - int mask; - int keycode= key & 0x7f; - - /* ignore the NON-KEY */ - if (keycode==0) - return 0; - - /* get the current ASCII value */ - kbd_getckeydef(keycode, &thisdef); - type = thisdef.type & KBD_MASK; - - switch (type) { - case KBD_SHFTLOCK: - if (!isreleased) { - vp->shiftlock ^= 1; - kbd_setleds (leds (vp)); - } - return 0; - case KBD_ALTGRLOCK: - if (!isreleased) { - vp->altgrlock ^= 1; - kbd_setleds (leds (vp)); - } - return 0; - case KBD_NUM: - if (!isreleased) { - vp->num ^= 1; - kbd_setleds (leds (vp)); - } - return 0; - case KBD_CAPS: - if (!isreleased) { - vp->caps ^= 1; - kbd_setleds (leds (vp)); - } - return 0; - case KBD_SCROLL: - if (!isreleased) { - vp->scroll ^= 1; - kbd_setleds (leds (vp)); - } - return 0; - case KBD_SHIFT: - kbs.shift_down = isreleased ? 0 : 1; - return 0; - case KBD_META: - kbs.meta_down = isreleased ? 0 : 0x80; - goto altnumpad; - case KBD_ALTGR: - kbs.altgr_down = isreleased ? 0 : 1; -altnumpad: - /* special handling of ALT-KEYPAD */ - /* have we been in altkp mode? */ - if (isreleased) { - if (altkpflag) { - metachar[0] = altkpval; - metachar[1] = 0; - altkpflag = 0; - altkpval = 0; - kbs.altgr_down = 0; /* -hv- was: hang in ALT-KP */ - return metachar; - } - } - return 0; - case KBD_CTL: - kbs.ctrl_down = isreleased ? 0 : 1; - /*fall thru*/ - default: - case KBD_NONE: - return 0; - - case KBD_BREAK: - case KBD_ASCII: - case KBD_FUNC: - if (isreleased) return 0; - more_chars = NULL; - - if (kbs.ctrl_down) { - if (!kbs.c0flag || thisdef.type&KBD_OVERLOAD) - more_chars = thisdef.ctrl; - } else if (kbs.meta_down) { - if (thisdef.type&KBD_OVERLOAD) - more_chars = thisdef.meta; - else { - metachar[0] = thisdef.unshift[0] ^ 0x80; - metachar[1] = 0; - more_chars = metachar; - } - } else if (kbs.altgr_down || vp->altgrlock) { - if (!kbs.a0flag || thisdef.type&KBD_OVERLOAD) - if (kbs.shift_down || vp->shiftlock || - vp->caps && (thisdef.type & KBD_DOALTCAPS)) - more_chars = thisdef.shiftaltgr; - else - more_chars = thisdef.altgr; - } else if (kbs.shift_down || vp->shiftlock || vp->caps && - (thisdef.type & KBD_DOCAPS)) - more_chars = thisdef.shift; - else - more_chars = thisdef.unshift; -#ifndef MINITERM - return kbd_xlatdiac(thisdef.type,more_chars); -#else - return more_chars; -#endif - - case KBD_KP: - if (isreleased) return 0; - - more_chars = NULL; - - /* there is special if used with keypad */ - if (kbs.meta_down || kbs.altgr_down) { - n = keypad2num[keycode-91]; - if (n>=0) { - if (!altkpflag) { - /* start ALT-KP mode */ - altkpflag = 1; - altkpval = 0; - } - altkpval *= 10; - altkpval += n; - - /* discard ALT-keypad mode if over 255 */ - if (altkpval > 255) { - altkpflag = 0; - altkpval = 0; - } - } else altkpflag = 0; - /* will be emitted when ALT released */ - return 0; - } - - if (kbs.shift_down || vp->shiftlock || kbs.ctrl_down || !vp->num) - more_chars = (kbs.altgr_down || vp->altgrlock) ? - thisdef.shiftaltgr : thisdef.shift; - else - more_chars = (kbs.altgr_down || vp->altgrlock) ? - thisdef.altgr : thisdef.unshift; - - return(more_chars); - } -} - -/* - * kbd_sgetc (noblock): get ASCII strings from the keyboard. If - * noblock == 0 wait until a key is gotten. Otherwise return a NULL - * if no characters are present. - * - */ -XCHAR *kbd_sgetc(int noblock) -{ - u_int key; - - if (!resetdone) kbd_warmreset(); /* allows proper operation of ddb when - * kernel dies before probing pc0 - */ - key = kbd_sgetk(noblock); - return kbd_xlatkey2ascii(key); -} - -/* compatibility */ -int sgetc(int noblock) { - return (int)*kbd_sgetc(noblock); -} - -/* - * kbd_sgetk: does most of the work and returns keynums - */ -static u_char breakseen = 0; -static u_char breakshit = 0; /* -hv- for the polite one: - read as "break's hit" :-) - Why couldn't IBM use the normal - convention for the break key? */ -static u_char altseen = 0; -static u_char ctrlseen = 0; -static u_char shiftseen = 0; -static u_char syskeyseen = 0; -static u_char resetcnt = 0; -static u_char in_Debugger = 0; -static u_char lockhotkey = 0xff; -#ifdef XTKBDMODE -static u_char lsh = 0; -#endif - -static Spec_tbl *kbd_ishotkey(int scan) -{ - int i; - - for (i=0; imodifier & MASK) { - /* has triggered a hotkey function, now which */ - /* block function if repeated */ - if (lockhotkey==(func=ptr->function)) return 0; - - /* now perform the function */ - switch (func) { - default: -#ifndef MINITERM - if (func>=KBD_VTY0 && func 0 - if (!in_Debugger) { - in_Debugger = 1; - Debugger(); - in_Debugger = 0; - return 1; - } -#endif - break; - } - lockhotkey = func; - return 1; /* hotkey was done */ - } - return 0; /* was not done */ -} - -static u_int kbd_sgetk(int noblock) -{ - u_char dt; - u_int key; - u_short type; - int delay; - Spec_tbl *sp; - static char keybuf[2]; - -loop: - /* - * First see if there is something in the keyboard port - * Some keyboards may raise interrupt before putting characters - * in the buffer (cf. Mach kd.c), so allow a little delay - * and don't time out immediately - * (sgetk is called from cointr!) - */ - delay = 1000; - while (!(inb(KBSTATP) & KBS_DIB)) { - if (noblock && delay-- ==0) return 0; - } - - /* get the byte */ - dt = inb(KBDATAP); - - /* look what we got */ - switch (dt) { - case KBR_DIAGFAIL: - printf("Keyboard irq: keyboard sent DIAGNOSTIC FAILURE\n"); - kbd_coldreset(); - break; - case KBR_RESEND: - case KBR_ACK: - case KBR_RSTDONE: - case KBR_OVERRUN: - case KBR_ECHO: /* this is junk we do not want to see */ - break; - case KBR_E0: /* extended code? */ - extended = 1; - break; - case KBR_F0: /* break code? */ - breakseen = 1; - if (breakshit) /* =5: wait for 14 */ - breakshit++; /* =7: wait for 77 */ - break; - case KBR_E1: /* -hv- this shit BREAK key */ - /* we have to collect a sequence E1 14 77 E1 for make - and F0 14 F0 77 for break */ - breakshit++; /* state=1: wait for 14, state=4: have make */ - if (breakshit==4) { - dt = 0x80; /* build replacement code */ - goto regular; - } - break; - /* I don't know why the keyboard sends additional E0 F0 12 (make!) - * and E0 12 (break!), when an extended key is used with - * shift, but this is certainly junk, which we avoid here - */ - case 0x12: - if (extended) { - extended = 0; - breakseen= 0; - break; - } else - goto regular; - case 0x14: - if (breakshit) - breakshit++; /* =2: wait for 77 */ - else /* =6: needs second break; */ - goto regular; - break; - case 0x77: - if (breakshit) - breakshit++; /* =3: wait for E1 */ - else /* =7: needs second break; */ - goto regular; - if (breakshit==8) { - dt = BREAKKEY; /* build replacement code */ - breakshit = 0; - goto regular; - } - break; - default: - goto regular; /* regular key */ - } - -rescan: - if (noblock) - return 0; - else - goto loop; - - /* got a normal scan key */ -regular: - - /* process special non-movable key combinations */ - switch (dt) { - case 0x14: /* SCAN LEFT CTRL */ - ctrlseen = breakseen ? 0 : 1; - break; - case 0x11: /* SCAN LEFT ALT */ - altseen = breakseen ? 0 : 1; - break; - case 0x12: /* SCAN LEFT SHIFT */ - shiftseen = breakseen ? 0 : 1; - break; - case 0x7C: /* Print/SysRq */ - if (breakseen) - syskeyseen ^= 1; - resetcnt = 0; - break; - default: - /* check and process hotkey functions */ - if ((sp=kbd_ishotkey(dt))) { - if (kbd_dohotkey(sp)) - goto rescan; - } else - lockhotkey = 0xff; - } - if (!breakseen) resetcnt = 0; - - /* - * make a keycode from scan code - */ - key = extended ? extscantokey[dt&KMASK] : scantokey[dt&KMASK]; - extended = 0; - - /* in NOREPEAT MODE ignore the key if it was the same as before */ - if (!kbs.repeat && key == lastkey && !breakseen) { - if (noblock) - return 0; - else - goto loop; - } - - /* -hv- processing of special keys moved to kbd_xlatkey2ascii */ - if (breakseen) { - key |= 0x80; - breakseen = 0; - lastkey = 0; /* -hv- I know this is a bug with */ - } else /* N-Key-Rollover, but I ignore that */ - { /* because avoidance is too complicated */ - lastkey = key; - } - - /* have a key */ - return key; -} - -/* - * Utility functions for IOCTL - */ -int kbd_rmkeydef(u_int key) -{ - register Ovl_tbl *ref; - - if (key==0 || key>MAXKEYNUM) return EINVAL; - - if (kbd_keytab[key].type & KBD_OVERLOAD) - kbd_keytab[key].type &= (KBD_MASK|KBD_DOCAPS); - - return 0; -} - -int kbd_setkeydef(Ovl_tbl *data) -{ - /* valid */ - if (data->keynum>MAXKEYNUM || - (data->type&KBD_MASK)==KBD_BREAK || - (data->type&KBD_MASK) > KBD_ALTGRLOCK) - return EINVAL; - - /* limit junk */ - data->unshift[KBDMAXOVLKEYSIZE] = - data->shift[KBDMAXOVLKEYSIZE] = - data->ctrl[KBDMAXOVLKEYSIZE] = - data->meta[KBDMAXOVLKEYSIZE] = - data->shiftaltgr[KBDMAXOVLKEYSIZE] = - data->altgr[KBDMAXOVLKEYSIZE] = 0; - - /* mark overload */ - data->type |= KBD_OVERLOAD; - - /* if key already overloaded, use this slot */ - if (! kbd_keytab[data->keynum].ovlptr) { - /* allocate new slot */ - kbd_keytab[data->keynum].ovlptr = - (Ovl_tbl *) malloc (sizeof (Ovl_tbl), M_DEVBUF, M_WAITOK); - if (! kbd_keytab[data->keynum].ovlptr) - return ENOSPC; /* no space, abuse of ENOSPC(!) */ - } - - /* copy */ - *kbd_keytab[data->keynum].ovlptr = *data; - - /* mark key */ - kbd_keytab[data->keynum].type |= KBD_OVERLOAD; - return 0; -} - -int kbd_cvtsound(int ipitch, int *opitch, int idur, int *odur) -{ - if (ipitch <= 0) - ipitch = 1500, idur = 250; - else if (idur<=0 || idur>=10000) - idur = 250; - *opitch = 1193180 / ipitch; - *odur = idur * hz / 1000; - return 0; -} - -int pc_xmode = 0; /* someone looks for this in the symbol table */ - -int kbd_setxserveriopl(int mode) -{ - struct syscframe *fp; - int error; - - /* do we have the necessary privilege? */ - if (error=suser(curproc->p_ucred, &curproc->p_acflag)) - return error; - - /* we have it, caller knows what he does :-) */ - fp = (struct syscframe *)curproc->p_regs; - - if (mode) { /* switch on */ - if (pc_xmode) return 0; - pc_xmode = 1; - fp->sf_eflags |= PSL_IOPL; - vga_doblanking(BLANKSTOP); /* does not really belong here */ - } else - { /* switch off */ - if (!pc_xmode) return 0; - pc_xmode = 0; - fp->sf_eflags &= ~PSL_IOPL; - vga_doblanking(BLANKSTART); /* does not really belong here */ - } - return 0; -} - -#endif /* NPC=0 */ -#endif /* NCO=1 */ diff --git a/sys/i386/isa/codrv/co_mini.c b/sys/i386/isa/codrv/co_mini.c deleted file mode 100644 index 245604cd00..0000000000 --- a/sys/i386/isa/codrv/co_mini.c +++ /dev/null @@ -1,359 +0,0 @@ -/* Copyright 1992 by Holger Veit - * May be freely used with Bill Jolitz's port of - * 386bsd and may be included in a 386bsd collection - * as long as binary and source are available and reproduce the above - * copyright. - * - * You may freely modify this code and contribute improvements based - * on this code as long as you don't claim to be the original author. - * Commercial use of this source requires permittance of the copyright - * holder. A general license for 386bsd will override this restriction. - * - * Use at your own risk. The copyright holder or any person who makes - * this code available for the public (administrators of public archives - * for instance) are not responsible for any harm to hardware or software - * that might happen due to wrong application or program faults. - * - * @(#) $RCSfile$ $Revision$ (Contributed to 386bsd) $Date$ - * - * History: see CO_HISTORY - */ -static char *rcsid = "$Header$"; - -/* This file provides a real mini terminal emulator, basically one that - * has only the cursor functions, screen clear/home, DEL, TAB, BEL, - * but no attributes etc. - * This interface is used for a installation kernel which must be - * small enough to fit on a diskette. - */ - -/* check my optional symbol to avoid multiple inclusions */ -#ifdef MINITERM - -#define COMPAT_CO011 -#include "co_hdr.h" -#include "vty.h" - -#define ESC_NONE 0 /* No esc in progress */ -#define ESC_WBRAC 1 /* got esc, wait for '[' or 'c' */ -#define ESC_WPARAM 2 /* got esc [, wait for param, ';' or letter */ - -/* - * Do the local initialisations. Notice that many things are already done - * in vty_init - */ -void vtemul_init() -{ - char *c,*id = "coinst"; - XCHAR *xc; - Keycap_def *kp; - struct Keycap2 *k2; - - /* fill the cons_capabilities structure - * Don't use strcpy here! - */ - c = id; - xc = cons_capabilities.emul_name; - while (*xc++ = xc_char2xc(*c++)) ; - -#define copydef(src,dst)\ - xc_bcopy(src,dst,KBDDEFOVLKEYSIZE) -} - -/* - * this routine does all the ESC and character processing stuff - */ -void vtemul_exec(struct vty *vp, XCHAR ch) -{ - int inccol, par1, par2; - int sc = 1; /* do scroll check */ - u_short at,sat; - struct outmode *sk = vp->op; - - /* which attributes do we use? */ - at = sk->fg_at|sk->bg_at; - sat= vp->so_at; - - /* translate to proper font */ - ch = vga_xlatiso646(vp,&at,&sat,ch); - - switch(ch) { - case 0x1B: - if(sk->escstate != ESC_NONE) - emul_wrtchar(vp,ch,sat); - else - sk->escstate = ESC_WBRAC; - break; - - case '\t': - inccol = (8 - vp->col % 8); /* non-destructive tab */ - emul_cursorrelative(vp,inccol,0); - break; - - case '\010': - emul_cursorleft(vp, 1); - break; - - case '\r': - emul_cursorrelative(vp,-vp->col,0); - break; - - case '\n': - emul_cursorrelative(vp,0,1); - break; - - case 0x07: - /* different sounds for different vtys possible */ - sysbeep (vp->pitch, vp->duration); - break; - default: - /* ESC Processing */ - switch (sk->escstate) { - - default: - case ESC_NONE: - /* NO ESC, normal processing */ - emul_wrtchar(vp,ch,vp->so ? sat : at); - if (vp->col >= vp->ncol) vp->col = 0; - break; - - case ESC_WBRAC: - /* has seen ESC, wait for [ or 'c' */ - if (ch=='[') { - sk->escstate = ESC_WPARAM; - sk->parcnt = 0; - sk->param[0] = 0; - sk->param[1] = 0; - } - else if (ch == 'c') { - /* Clear screen & home */ - emul_clearcursor(vp, 2); - emul_cursormove(vp, 0, 0); - sk->escstate = ESC_NONE; - } - else { - /* error */ - sk->escstate = ESC_NONE; - emul_wrtchar(vp, ch, sat); - } - break; - - case ESC_WPARAM: /* has seen ESC [ wait for digit, ';' or letter */ - if (ch>='0' && ch<='9') { - sk->param[sk->parcnt] *= 10; - sk->param[sk->parcnt] += ch-'0'; - } - else if (ch==';') { - if (sk->parcnt>=2) { - sk->escstate = ESC_NONE; /* error */ - emul_wrtchar(vp,ch,sat); - } - else { - sk->parcnt++; - sk->param[sk->parcnt] = 0; - } - } - else if (ch>=' ' && ch<='~') { - par1 = sk->param[0]; - par2 = sk->param[1]; - sk->parcnt++; - sk->param[sk->parcnt] = 0; - switch (ch) { - case 'A': /* back cx rows */ - emul_cursorup(vp, par1); - sc = 0; - break; - case 'B': /* down cx rows */ - emul_cursordown(vp, par1); - sc = 0; - break; - case 'C': /* right cursor */ - emul_cursorright(vp, par1, 1); - sc = 0; - break; - case 'D': /* left cursor */ - emul_cursorleft(vp, par1); - sc = 0; - break; - case 'H': /* Cursor move */ - emul_cursormove(vp, par1, par2); - break; - default: - emul_wrtchar(vp,ch,sat); - } - sk->escstate = ESC_NONE; - } - break; - } - } - - if (sc && emul_checkcursor(vp) > 0) { - if (consoftc.cs_flags&CO_OPEN) - do - (void)kbd_sgetc(1); - while (vp->scroll); - - emul_scrollup(vp, 1, 1); - } -} - -/* - * The following part provides a basic set of ioctls - */ -void coioctl_init() {} - -int consioctl(dev_t dev, int cmd, caddr_t data, int flag) -{ - register error; - register struct tty *tp = dev2tty(dev); - - if (!tp) return ENXIO; - - /* mandatory */ - if (cmd==CONSGINFO) { - *((struct consinfo*)data) = cons_capabilities; - return 0; - } else if (cmd==OLDCONSGINFO) { - ((struct oldconsinfo*)data)->info1 = cons_capabilities.info1; - return 0; - } - - error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag); - if (error >= 0) - return (error); - error = ttioctl(tp, cmd, data, flag); - if (error >= 0) - return (error); - - /* must be executed for backward compatibility - * must be executed *after* the tty ioctls, - * because it shares an essential ioctl with them. - */ - error = kbdioctl(dev, cmd, data, flag); - return error==0 ? 0 : ENOTTY; -} - -/* - * process keyboard ioctls - */ - -static int ofl = 0; - -int kbdioctl(dev_t dev, int cmd, caddr_t data, int flag) -{ - int s,n,error; - struct vty *vp = dev2vty(dev); - - switch (cmd) { - /* CONSGINFO is mandatory ! */ - case CONSGINFO: - *((struct consinfo*)data) = cons_capabilities; - return 0; - case OLDCONSGINFO: - ((struct oldconsinfo*)data)->info1 = cons_capabilities.info1; - return 0; - /* this is a relic from my youth mistakes */ - return 0; - case KBDCOLDRESET: - kbd_coldreset(); - kbd_ovlinit(); - return 0; - case KBDGCKEY: - return kbd_getckeydef (((Ovl_tbl *)data)->keynum, (Ovl_tbl *) data); - case KBDSCKEY: - return kbd_setkeydef((Ovl_tbl *)data); - case KBDGOKEY: - return kbd_getokeydef (((Ovl_tbl *)data)->keynum, (Ovl_tbl *) data); - case KBDRMKEY: - return kbd_rmkeydef (*(int *) data); - case KBDDEFAULT: - kbd_ovlinit(); - return 0; - case KBDSCLRLYR: - kbs.a0flag = (*(int*)data & 1) != 0; - kbs.c0flag = (*(int*)data & 2) != 0; - kbs.m0flag = (*(int*)data & 4) != 0; /*not used yet*/ - return 0; - default: - /* backward compatibility */ - error = vgaioctl(dev, cmd, data, flag); - if (!error) return 0; - else return ENOTTY; - } - /*NOTREACHED*/ -} - -static int vioc_setfontmap(struct fontmap *data) -{ - int i,k; - int pg = data->page; - u_char *ofs = (u_char*)Crtat+pg*0x4000; - - if (vds.cardtype < VG_EGA || pg < 0 || pg > 7) return EINVAL; - vds.encoding[pg] = data->encoding; - - vga_enablecg(); - bzero(ofs,8192); - for(i=k=0; i<(VGA_FNTCSIZE*VGA_FNTNCHARS); k+=32,i+=VGA_FNTCSIZE) { - bcopy(&data->map[i],ofs+k,VGA_FNTCSIZE); - } - vga_disablecg(); - - /* enable SB/SBH when font 1 is loaded */ - if (pg==1 && data->encoding != NOFONT) outw(0x3c4,0x0403); /* SA=1,SB=0 */ - - return 0; -} - -static int vioc_getfontmap(struct fontmap *data) -{ - int i,k; - int pg = data->page; - u_char *ofs = (u_char*)Crtat + pg*0x4000; - if (vds.cardtype < VG_EGA || pg < 0 || pg > 1) return EINVAL; - - data->encoding = vds.encoding[pg]; - - vga_enablecg(pg); - for(i=k=0; i<(VGA_FNTCSIZE*VGA_FNTNCHARS); k+=32,i+=VGA_FNTCSIZE) - bcopy(ofs+k,&data->map[i],VGA_FNTCSIZE); - vga_disablecg(); - - return 0; -} - -/* - * execute my own vga ioctls - */ -/*ARGSUSED*/ -int vgaioctl(dev_t dev, int cmd, caddr_t data, int flag) -{ - switch(cmd) { - /* CONSGINFO is mandatory ! */ - case CONSGINFO: - *((struct consinfo*)data) = cons_capabilities; - return 0; - case OLDCONSGINFO: - ((struct oldconsinfo*)data)->info1 = cons_capabilities.info1; - return 0; - case CONSOLE_X_MODE: - return kbd_setxserveriopl(*(int*)data); - case VGAGINFO: - return vga_getvideoinfo((struct videoinfo*)data); - case VGASFNTMAP: - return vioc_setfontmap((struct fontmap*)data); - case VGAGFNTMAP: - return vioc_getfontmap((struct fontmap*)data); - case VGATAKECTRL: - /* No op for now */ - return 0; - case VGAGIVECTRL: - /* No op for now */ - return 0; - default: - return ENOTTY; - } - /*NOTREACHED*/ -} - -#endif /* MINITERM */ diff --git a/sys/i386/isa/codrv/co_pc3.c b/sys/i386/isa/codrv/co_pc3.c deleted file mode 100644 index e8f77df511..0000000000 --- a/sys/i386/isa/codrv/co_pc3.c +++ /dev/null @@ -1,350 +0,0 @@ -/* Copyright 1992 by Holger Veit - * May be freely used with Bill Jolitz's port of - * 386bsd and may be included in a 386bsd collection - * as long as binary and source are available and reproduce the above - * copyright. - * - * You may freely modify this code and contribute improvements based - * on this code as long as you don't claim to be the original author. - * Commercial use of this source requires permittance of the copyright - * holder. A general license for 386bsd will override this restriction. - * - * Use at your own risk. The copyright holder or any person who makes - * this code available for the public (administrators of public archives - * for instance) are not responsible for any harm to hardware or software - * that might happen due to wrong application or program faults. - * - * @(#) $RCSfile: co_pc3.c,v $ $Revision: 1.6 $ (Contributed to 386bsd) $Date: 93/01/23 23:14:46 $ - * - * History: see CO_HISTORY - */ -static char *rcsid = "$Header: /usr/src/sys.386bsd/i386/isa/codrv/RCS/co_pc3.c,v 1.6 93/01/23 23:14:46 root Exp Locker: root $"; - -/* This file provides a plug-in interface for different terminal emulations. - * As a reference, the pc3/pc3x/pc3n/pc3nc interface is shown. - * - * Programmers are invited to provide different emulations, e.g. - * vt100/vt220/vt320, adm3a/adm31, hp262x, etc. - * - * The following conventions have to be met: - * - * The video subsystem provides a number of support functions to - * manipulate screen data, such as writing to a VTY screen, or moving the - * cursor, etc. - * - * All these routines are named "emul_*". See co_hdr.h for a list of available - * facilities. You will see that these are defines to either a set of - * "vga_*" or "gfx_*" routines (not yet available). To make a migration to - * the future graphics console as smooth as possible, avoid using other - * routines to access the screen, even if you know tricky code - * that runs 10 times as fast. There is time enough to do optimizations - * when the graphics subsystem is running. - * - *************************************************************************** - * - * This file contains one central routine, 'vtemul_exec', which is called - * by the console driver, with the following arguments: - * - * vp: Pointer to the actual vty - * ch: Character to be processed (note that this is not a "u_char"!) - * - *************************************************************************** - * - * A second call is used during coattach: 'vtemul_init', which - * can be used for local initialisation. vtemul_init also MUST fill an - * identifier into the field 'emul_name' of struct consinfo cons_capabilities. - * - *************************************************************************** - * - * Only use the PUBLIC fields in struct vty! - * Don't rely on the PRIVATE members of the structure, even if you - * can access them (PRIVATE and PUBLIC are #defines only). - * The reason is, that a future version may run in graphics mode, and - * will interpret these fields in a different way you may think they - * might be used. This holds in particular for Crtat and Crtat!!!! - * - * If you need more special variables, use a local structure - * use #include "vty.h" to find out the number of vtys available. - * But even in the local structure, don't think that there is - * direct accessible video memory, even if using this this speeds up - * everything extremely! - */ - -/* example code follows: */ - -/* check my optional symbol to avoid multiple inclusions */ -#ifdef PC3 -#ifndef MINITERM - -#include "co_hdr.h" -#include "vty.h" - -#define ESC_NONE 0 /* No esc in progress */ -#define ESC_WBRAC 1 /* got esc, wait for '[' or 'c' */ -#define ESC_WPARAM 2 /* got esc [, wait for param, ';' or letter */ - -/* !!! this may look like a Keycap_def, like in co_kbd.c, but isn't ! :-) */ -static struct Keycap2 { - short key; - u_short type; /* type of key */ - XCHAR unshift[KBDDEFOVLKEYSIZE+1]; /* default codes */ - XCHAR shift[KBDDEFOVLKEYSIZE+1]; - XCHAR ctrl[KBDDEFOVLKEYSIZE+1]; -} pc3keys[] = { -75, KBD_FUNC, XE3('[','L'), XE3('|','a'), XE3('|','b'), /* INS */ -76, KBD_FUNC, XC1('\177'), XE3('|','c'), XE3('|','d'), /* DEL */ -79, KBD_FUNC, XE3('[','D'), XE3('|','e'), XE3('|','f'), /* CU <- */ -80, KBD_FUNC, XE3('[','H'), XE3('|','g'), XE3('|','h'), /* HOME */ -81, KBD_FUNC, XE3('[','F'), XE3('|','i'), XE3('|','j'), /* END */ -83, KBD_FUNC, XE3('[','A'), XE3('|','k'), XE3('|','l'), /* CU ^ */ -84, KBD_FUNC, XE3('[','B'), XE3('|','m'), XE3('|','n'), /* CU v */ -85, KBD_FUNC, XE3('[','I'), XE3('|','o'), XE3('|','p'), /* PG UP */ -86, KBD_FUNC, XE3('[','G'), XE3('|','q'), XE3('|','r'), /* PG DN */ -89, KBD_FUNC, XE3('[','C'), XE3('|','s'), XE3('|','t'), /* CU -> */ -91, KBD_KP, XC1('7'), XE3('[','H'), XC1('7'), -92, KBD_KP, XC1('4'), XE3('[','D'), XC1('4'), -93, KBD_KP, XC1('1'), XE3('[','F'), XC1('1'), -95, KBD_KP, XC1('/'), XC1('/'), XC1('/'), -96, KBD_KP, XC1('8'), XE3('[','A'), XC1('8'), -97, KBD_KP, XC1('5'), XE3('[','E'), XC1('5'), -98, KBD_KP, XC1('2'), XE3('[','B'), XC1('2'), -99, KBD_KP, XC1('0'), XE3('[','L'), XC1('0'), -100, KBD_KP, XC1('*'), XC1('*'), XC1('*'), -101, KBD_KP, XC1('9'), XE3('[','I'), XC1('9'), -102, KBD_KP, XC1('6'), XE3('[','C'), XC1('6'), -103, KBD_KP, XC1('3'), XE3('[','G'), XC1('3'), -104, KBD_KP, XC1('.'), XC1('\177'), XC1('.'), -105, KBD_KP, XC1('-'), XC1('-'), XC1('-'), -106, KBD_KP, XC1('+'), XC1('+'), XC1('+'), -112, KBD_FUNC, XE3('[','M'), XE3('[','Y'), XE3('[','k'), /* F1 */ -113, KBD_FUNC, XE3('[','N'), XE3('[','Z'), XE3('[','l'), /* F2 */ -114, KBD_FUNC, XE3('[','O'), XE3('[','a'), XE3('[','m'), /* F3 */ -115, KBD_FUNC, XE3('[','P'), XE3('[','b'), XE3('[','n'), /* F4 */ -116, KBD_FUNC, XE3('[','Q'), XE3('[','c'), XE3('[','o'), /* F5 */ -117, KBD_FUNC, XE3('[','R'), XE3('[','d'), XE3('[','p'), /* F6 */ -118, KBD_FUNC, XE3('[','S'), XE3('[','e'), XE3('[','q'), /* F7 */ -119, KBD_FUNC, XE3('[','T'), XE3('[','f'), XE3('[','r'), /* F8 */ -120, KBD_FUNC, XE3('[','U'), XE3('[','g'), XE3('[','s'), /* F9 */ -121, KBD_FUNC, XE3('[','V'), XE3('[','h'), XE3('[','t'), /* F10 */ -122, KBD_FUNC, XE3('[','W'), XE3('[','i'), XE3('[','u'), /* F11 */ -123, KBD_FUNC, XE3('[','X'), XE3('[','j'), XE3('[','v'), /* F12 */ -124, KBD_KP, XE3('[','w'), XE3('[','x'), XE3('[','y'), -0, KBD_NONE, XC0, XC0, XC0 -}; - -/* - * Do the local initialisations. Notice that many things are already done - * in vty_init - */ -void vtemul_init() -{ - char *c,*id = "pc3n"; - XCHAR *xc; - Keycap_def *kp; - struct Keycap2 *k2; - - /* fill the cons_capabilities structure - * Don't use strcpy here! - */ - c = id; - xc = cons_capabilities.emul_name; - while (*xc++ = xc_char2xc(*c++)) ; - -#define copydef(src,dst)\ - xc_bcopy(src,dst,KBDDEFOVLKEYSIZE) - - /* set the function keys new */ - for (k2 = pc3keys; k2->key; k2++) { - kp = &kbd_keytab[k2->key]; - kp->type = k2->type; - kp->ovlptr = 0; - copydef(k2->unshift,kp->unshift); - copydef(k2->shift,kp->shift); - copydef(k2->ctrl,kp->ctrl); - } -} - -/* - * this routine does all the ESC and character processing stuff - */ -void vtemul_exec(struct vty *vp, XCHAR ch) -{ - int inccol, par1, par2; - int sc = 1; /* do scroll check */ - u_short at,sat; - struct outmode *sk = vp->op; - - /* which attributes do we use? */ - at = sk->fg_at|sk->bg_at; - sat= vp->so_at; - - /* translate to proper font */ - ch = vga_xlatiso646(vp,&at,&sat,ch); - - switch(ch) { - case 016: /* SO, select font 2 */ - emul_selectfont(vp,2); - break; - case 017: /* SI, select font 1 */ - emul_selectfont(vp,1); - break; - case 0x1B: - if(sk->escstate != ESC_NONE) - emul_wrtchar(vp,ch,sat); - else - sk->escstate = ESC_WBRAC; - break; - - case '\t': - inccol = (8 - vp->col % 8); /* non-destructive tab */ - emul_cursorrelative(vp,inccol,0); - break; - - case '\010': - emul_cursorleft(vp, 1); - break; - - case '\r': - emul_cursorrelative(vp,-vp->col,0); - break; - - case '\n': - emul_cursorrelative(vp,0,1); - break; - - case 0x07: - /* different sounds for different vtys possible */ - sysbeep (vp->pitch, vp->duration); - break; - default: - /* ESC Processing */ - switch (sk->escstate) { - - default: - case ESC_NONE: - /* NO ESC, normal processing */ - emul_wrtchar(vp,ch,vp->so ? sat : at); - if (vp->col >= vp->ncol) vp->col = 0; - break; - - case ESC_WBRAC: - /* has seen ESC, wait for [ or 'c' */ - if (ch=='[') { - sk->escstate = ESC_WPARAM; - sk->parcnt = 0; - sk->param[0] = 0; - sk->param[1] = 0; - } - else if (ch == 'c') { - /* Clear screen & home */ - emul_clearcursor(vp, 2); - emul_cursormove(vp, 0, 0); - sk->escstate = ESC_NONE; - } - else { - /* error */ - sk->escstate = ESC_NONE; - emul_wrtchar(vp, ch, sat); - } - break; - - case ESC_WPARAM: /* has seen ESC [ wait for digit, ';' or letter */ - if (ch>='0' && ch<='9') { - sk->param[sk->parcnt] *= 10; - sk->param[sk->parcnt] += ch-'0'; - } - else if (ch==';') { - if (sk->parcnt>=2) { - sk->escstate = ESC_NONE; /* error */ - emul_wrtchar(vp,ch,sat); - } - else { - sk->parcnt++; - sk->param[sk->parcnt] = 0; - } - } - else if (ch>=' ' && ch<='~') { - par1 = sk->param[0]; - par2 = sk->param[1]; - sk->parcnt++; - sk->param[sk->parcnt] = 0; - switch (ch) { - case 'A': /* back cx rows */ - emul_cursorup(vp, par1); - sc = 0; - break; - case 'B': /* down cx rows */ - emul_cursordown(vp, par1); - sc = 0; - break; - case 'C': /* right cursor */ - emul_cursorright(vp, par1, 1); - sc = 0; - break; - case 'D': /* left cursor */ - emul_cursorleft(vp, par1); - sc = 0; - break; - case 'f': /* in system V consoles */ - case 'H': /* Cursor move */ - emul_cursormove(vp, par1, par2); - break; - case 'J': /* Clear ... */ - emul_clearcursor(vp, par1); - break; - case 'K': /* Clear line ... */ - emul_clearline(vp, par1); - break; - case 'L': /* Insert Line */ - emul_insertline(vp, par1); - break; - case 'M': /* Delete Line */ - emul_deleteline(vp, par1); - break; - case 'P': /* delete spaces */ - emul_deletechars(vp, par1); - break; - case 'S': /* scroll up cx lines */ - emul_scrollup(vp, par1, 0); - break; - case 'T': /* scroll down cx lines */ - emul_scrolldown(vp, par1); - break; - case 'm': /* standout mode */ - if (par1) vp->so = 1; - else vp->so = 0; - break; - case 'r': - vp->so_at = (par1 & 0x0f) | ((par2 & 0x0f) << 4); - break; - case 'x': /* set attributes */ - emul_setattributes(vp, par1, par2); - break; - case '@': /* insert spaces */ - emul_insertchars(vp, par1); - break; - case '!': /* set beep pitch and duration */ - kbd_cvtsound(par1, &vp->pitch, - par2, &vp->duration); - break; - default: - emul_wrtchar(vp,ch,sat); - } - sk->escstate = ESC_NONE; - } - break; - } - } - - if (sc && emul_checkcursor(vp) > 0) { - if (consoftc.cs_flags&CO_OPEN) - do - (void)kbd_sgetc(1); - while (vp->scroll); - - emul_scrollup(vp, 1, 1); - } -} - -#endif /* !MINITERM */ -#endif /* PC3 */ diff --git a/sys/i386/isa/codrv/co_vga.c b/sys/i386/isa/codrv/co_vga.c deleted file mode 100644 index 21f272e988..0000000000 --- a/sys/i386/isa/codrv/co_vga.c +++ /dev/null @@ -1,1297 +0,0 @@ - /*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * William Jolitz and Don Ahn. - * Many improvements based on this source are made by Holger Veit. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#) $RCSfile: co_vga.c,v $ $Revision: 1.11 $ (Berkeley) $Date: 93/01/23 23:14:55 $ - */ -static char rcsid[] = "$Header: /usr/src/sys.386bsd/i386/isa/codrv/RCS/co_vga.c,v 1.11 93/01/23 23:14:55 root Exp Locker: root $"; - -/* - * History: see CO_HISTORY - */ - -#include "co.h" -#include "pc.h" -#if NCO == 1 -#if NPC == 0 - -#include "co_hdr.h" - -static char isinitialized = 0; /* =1: console/vtys initialized */ -u_short *Crtat = (u_short *)MONO_BUF; /* The only absolute location that is - * known from video memory, everything - * else derived from it, - * modified by device probe */ -static int scrblanksave; /* save position cursor */ -static u_short *scrbuf = 0; /* buffer for screen saver */ -static char *card2name(int id); - -/* find video config */ -static void probe_video() -{ - vga_whoami(); - if (vds.color) { - Crtat += (CGA_BUF-MONO_BUF)/CHR; - vds.iobase = 0x3D0; /* color */ - } else - vds.iobase = 0x3B0; /* mono */ - - vds.encoding[0] = XLAT2PC8; - vds.encoding[1] = NOFONT; - vds.f89bit = vds.cardtype >= VG_EGA ? 9 : 8; -} - -int coattach(struct isa_device *dev) -{ - struct cursorshape cs; - struct kbd_hotkey spec; - int i; - - /* re-initialize ALL vtys */ - vty_init(0); - - /* initialize the ioctl subsystem */ - coioctl_init(); - - printf(" %s", card2name(vds.cardtype)); - if (vds.cardsubtype>=0) - printf("/0x%02x", vds.cardsubtype); - - printf(" ram %dk io 0x%x kbd 0x%x", vds.ram, vds.iobase, kbs.id); - - if (nvty>1) printf(" vtys %d",nvty); - - /* create a save buffer for screen saver */ - if (!scrbuf) /* XXX modify with different screen sizes */ - scrbuf = (u_short*)malloc(vtys[0].size*CHR, M_TEMP, M_NOWAIT); - if (!scrbuf) - panic("coattach: no screen buffer"); - -#ifdef FAT_CURSOR - cs.start = 0; - cs.end = 18; - vga_setcshape(&cs); -#endif FAT_CURSOR - - vds.blanking = 0; - vds.scrtimeout = BLANKTIMEOUT; - vga_doblanking(BLANKSTART); - vga_cursor(0); - - /* define the basic special function keys - * CTRL-ALT-DEL (Reset) - * CTRL-ALT-ESC (Debugger) - * CTRL-ALT-F1 (VTY-1) - * CTRL-ALT-F2 (VTY-2) - * CTRL-ALT-F3 (VTY-3) - * CTRL-ALT-F4 (VTY-4) - * CTRL-ALT-F5 (VTY-5) - * CTRL-ALT-F6 (VTY-6) - * CTRL-ALT-F7 (VTY-7) - * CTRL-ALT-F8 (VTY-8) - * CTRL-ALT-F9 (VTY-9) - * CTRL-ALT-F10 (VTY-10) - * CTRL-ALT-F11 (VTY-11) - * CTRL-ALT-F12 (VTY-12) - */ - spec.key = 104; /*DEL*/ - spec.modifier = KBD_EXT_CA; /*CTRL-ALT*/ - spec.function = KBD_RESETKEY; - kbd_sethotkey(&spec); -#if NDDB > 0 - spec.key = 110; /*ESC*/ - spec.function = KBD_DEBUGKEY; - kbd_sethotkey(&spec); -#endif - for (i=0; i<12; i++) { - spec.key = 112+i; - spec.function = i; - kbd_sethotkey(&spec); - } -} - -/* - * vga_cursor(): - * reassigns cursor position, updated by the rescheduling clock - * which is a index (0-1999) into the text area. Note that the - * cursor is a "foreground" character, it's color determined by - * the fg_at attribute. Thus if fg_at is left as 0, (FG_BLACK), - * as when a portion of screen memory is 0, the cursor may dissappear. - */ -void vga_setcursorpos(int pos) -{ - outb(vds.iobase+4, M6845_CURSORH); - outb(vds.iobase+5, pos>> 8); - outb(vds.iobase+4, M6845_CURSORL); - outb(vds.iobase+5, pos); -} - -void vga_cursor(int a) -{ - vga_setcursorpos(actvty->crtat-actvty->Crtat); - - if (a == 0 && actvty->visible == 1) - timeout(vga_cursor, 0, hz/10); -} - -#ifndef MINITERM -/* Screen saver function: - * XXX to be changed for graphics console - * - * if screenblanking is enabled, the display is switched off after delay - * given in scrtimeout (in sec.) - * Any key hit restarts display again and restarts the timer. - * scrtimeout = 0 disables blanking. - * Also, blanking is suspended if console_x_mode is active, because it - * is supposed that X11 has its own facilities to turn off display. - */ -static struct cursorshape playsave = { -1,-1 }; - -#if SCRSAVER == 1 -/*ARGSUSED*/ -static void display_off(int arg,int ctime) -{ - static int play1,play2,play3,play4; - int dir[] = { -79,-81,79,81 }; - register s; - int size = actvty->size; - - if (!actvty->visible) return; /* no show on invisible screen */ - - if (vds.blanking==1) { - - /* This idea was in parts borrowed from Linux, since I - * was too lazy to play around with the video registers - * (although there are suited bits to switch) - * screenplay originally by me (holgi) - */ - bcopy(actvty->Crtat,scrbuf,size*CHR); - fillw(0x0820,actvty->Crtat, actvty->size); - scrblanksave = actvty->crtat-actvty->Crtat; - vds.blanking = 2; - play1 = actvty->crtat-actvty->Crtat; - play2 = time.tv_sec; - play3 = 7; - play4 = 1; - untimeout(vga_cursor, 0); - } - - /* some animation */ - s = spltty(); - vga_setcursorpos(play1); - if (!play3) { - play2 = (play2 % 1237) + 997; - play3 = 4 + (play2 % 9); - if (((play2>>3)&3)==play4) - play4=(play4+1)%4; - else - play4=(play2>>3)&3; - } else play3--; - play1 += dir[play4]; - if (play1<0) play1 += size; - else if (play1 >= size) play1 -= size; - timeout(display_off,0,hz/4); - splx(s); -} - -#else -#if SCRSAVER == 2 /* moving snake by Chr. Robitschko */ - -/* XXX This code could be merged with the standard saver, because there are - not many modifications, but I need things to do for the next version */ - -static void display_off(int arg,int ctime) -{ - int dir[] = { -79,-81,79,81 }; - register s, f; -#ifdef NETBSD - const char snake[] = { "NetBSD" }; -#else - const char snake[] = { "386BSD" }; -#endif - const int snlen = sizeof(snake) - 1; - static char *snpos[snlen]; - static int play1,play2,play3,play4; - struct cursorshape cs; - - int size = actvty->size; - - if (!actvty->visible) return; /* no show on invisible screen */ - - if (vds.blanking==1) { - - /* disable cursor -hv- */ - vga_getcshape(&playsave); - cs.start = 31; - cs.end = 30; - vga_setcshape(&cs); - - bcopy(actvty->Crtat,scrbuf,size*CHR); - fillw(0x0820,actvty->Crtat, actvty->size); - scrblanksave = actvty->crtat-actvty->Crtat; - vds.blanking = 2; - play1 = actvty->crtat-actvty->Crtat; - play2 = time.tv_sec; - play3 = 7; - play4 = 1; - for (f=0; f0; f--) - snpos[f] = snpos[f-1]; - snpos[0] = (char *)(Crtat + play1); - for (f=snlen-1; f>=0; f--) - if (snpos[f]) *snpos[f] = snake[f]; - - if (!play3) { - play2 = (play2 % 1237) + 997; - play3 = 4 + (play2 % 9); - if (((play2>>3)&1)==play4%2) - play4=(play4+1)%4; - else - play4=(play2>>3)&3; - } else play3--; - play1 += dir[play4]; - if (play1<0) play1 += size; - else if (play1 >= size) play1 -= size; - timeout(display_off,0,hz/4); - splx(s); -} - -#else -ERROR! NEED SCRSAVER=1 OR 2 -#endif /* SCRSAVER=2 */ -#endif /* SCRSAVER=1 */ - -/* XXX graphics */ -static void display_on() -{ - /* reload old content */ - if (vds.blanking==2) { - bcopy(scrbuf,actvty->Crtat,actvty->size*CHR); - if (playsave.start != -1) vga_setcshape(&playsave); - vga_setcursorpos(scrblanksave); - vga_cursor(0); - } -} - -/* XXX graphics */ -void vga_doblanking(int fct) -{ - /* restore display, just in case */ - display_on(); - - /* timer was started, stop */ - if (vds.blanking<3) - untimeout(display_off,0); - - vds.blanking = 0; - - if (fct==BLANKSTART) { - if (vds.scrtimeout) { - /* start it */ - timeout(display_off,0,vds.scrtimeout*hz); - vds.blanking = 1; - } - } - /*else if(fct==BLANKSTOP) vds.blanking = 0;*/ -} - -#else /* MINITERM */ -/*ARGSUSED*/ -static void display_off(int arg,int ctime) {} -static void display_on() {} -void vga_doblanking(int fct) {} -#endif - -/* normally called by init_main to do local initialisation */ -/* called by sput, if anyone is faster than init_main, e.g. ddb */ -void consinit () -{ - if (isinitialized == 0) - { - /* find out video card */ - probe_video(); - - /* initialize video device */ - emul_initvideo(); - - /* disable screensaver */ - vds.blanking = 3; - - /* initialize a single terminal */ - vty_init(1); - - isinitialized = 1; - - /* NetBSD would like to have this, - * and for 386bsd it doesn't make problems */ - cons_normal(); - } -} - -/* - * sput calls vtemul_exec for terminal emulation - * if ka, use kernel attributes. - */ -void sput(int vtynum, XCHAR c, int ka) -{ - register struct vty *act; - - if (!isinitialized) - consinit(); - - /* necessary: turn on display again */ - if (vds.blanking==2) vga_doblanking(BLANKSTART); - - /* select vty */ - act = &vtys[vtynum]; - - /* select attribute set */ - act->op = ka ? &act->om[1] : &act->om[0]; - - /* process input */ - vtemul_exec(act, c); - - /* re-set cursor position */ - if (ka) vga_cursor(1); -} - -/* both routines used by init_main for more portability */ -void cons_highlight() -{ - register struct vty *p = &vtys[0]; - - p->op = &p->om[1]; - emul_setattributes(p,3,15); -} - -void cons_normal() -{ - register struct vty *p = &vtys[0]; - p->op = &p->om[1]; - emul_setattributes(p,0,0); -} - -/* - * vga_whoami: try to detect whether a SVGA is present - * see Ferraro, Programmer's guide to the EGA/VGA cards for more info - */ -/* - * Try to identify, which video card is in the system - */ -void vga_whoami() -{ - register int iobase; - u_char *cp; - u_char *videobios; - u_short *vp; - u_short vsave; - volatile u_char lock,save1,save2,save3,save4; - - vds.color = 1; /* default */ - vds.cardtype = VG_UNKNOWN; - vds.cardsubtype = -1; - vds.ram = 64; - - /* first look whether there is RAM in the COLOR region, if this is - * a mono card, there is none - */ - vp = Crtat + (CGA_BUF-MONO_BUF)/CHR; - vsave = *vp; - *vp = 0xCC33; - DELAY(100); /* wait 100 us, doesn't help if you buffer your isa - * bus lines with large capacitors :-) - */ - if (*vp == 0xCC33) goto not_a_mono; - else { - /* we are paranoid */ - *vp = 0xA55A; - DELAY(100); - if (*vp == 0xA55A) goto not_a_mono; - } - - /* oh, that poor owner has only a MDA/Hercules card :-) */ - *vp = vsave; - vds.ram = 16; - vds.color = 0; - vds.cardtype = VG_MONO; - return; - -not_a_mono: - *vp = vsave; - - /* there is RAM in the ???B8000 region, look which color card we have - * Hmmm... the ancient CGA was supported by the ROMBIOS, and had no ROM - * on board, but the EGA's and VGA's have - */ - videobios = cp = (u_char*)Crtat + (EGA_BIOS-MONO_BUF); - - /* Some machines map video BIOS to e0000 instead of c0000. - * Check it now. - */ - if (*cp != 0x55 || *(cp+1) != 0xAA) - videobios = cp = (u_char*)Crtat + (ALTEGA_BIOS-MONO_BUF); - - if (*cp != 0x55 || *(cp+1) != 0xAA) { - /* There is no ROM ID. - * you do not plan to run X11 with this card, do you? - */ - vds.ram = 16; - vds.cardtype = VG_CGA; - return; - } - - /* this is an EGA or a VGA or a SVGA or something else quite strange - * so lets look whether this is a VGA. We know that several EGA - * registers are readonly, which is really a design fault. - * so just look, whether we can write and read something into - * such a register: this will hold for the CRT address register at - * iobase+4 and the graphics control address - */ - - if (inb(0x3cc) & 1) { - iobase = vds.iobase = 0x3D0; - } else - { - iobase = vds.iobase = 0x3B0; - vds.color = 0; - } - - outb(iobase+4, 7); - DELAY(100); - if (inb(iobase+4) != 7) vds.cardtype = VG_EGA; - else { - outb(0x3ce,2); - DELAY(100); - if (inb(0x3ce) != 2) vds.cardtype = VG_EGA; - } - if (vds.cardtype==VG_EGA) { - vds.ram = 64; /* for simplicity assume the worst */ - return; - } - - /* There is a problem now: 8514/A adapters also have ROM, unfortunately - * I don't have sufficient information about the 8514/A and the XGA, - * so just don't try 386BSD with it ! - */ - - /* I don't know whether this already qualifies for a VGA, so - * someone may add some code here, but for now I am persuaded that I - * have a VGA at this point. This might be a SVGA, however, so let's - * look if this assumption is true. Hard stuff follows... - * refer to Ferraro: Programmer's Guide to the EGA and VGA Cards - * 2nd ed, Addison-Wesley, 1990 - */ - vds.cardtype = VG_VGA; - - /* unlock paradise registers */ - outb(iobase+4, 0x11); lock = inb(iobase+5); outb(iobase+5, lock & 0x7f); - - /* now check for a PVGA1 */ - cp = videobios + 0x007d; - if (*cp=='V' && *(cp+1)=='G' && *(cp+2)=='A' && *(cp+3)=='=') { - /* this is a paradise, now check which one */ - vds.cardtype = VG_PARADISE; - outw(0x3CE, 0x050F); - outw(iobase+4, 0x8529); - outb(iobase+4,0x2b); save1 = inb(iobase+5); - outb(iobase+5,0xaa); save2 = inb(iobase+5); - outb(iobase+5,save1); - if (save2 != 0xaa) { vds.cardsubtype = 0x01; goto foundp2; } - outb(0x3c4,0x12); save1 = inb(0x3c5); - outb(0x3c5,save1 & 0xbf); save2 = inb(0x3c5) & 0x40; - if (save2) { vds.cardsubtype = 0x01; goto foundp2; } - outb(0x3c5,save1 | 0x40); save2 = inb(0x3c5) & 0x40; - if (!save2) { vds.cardsubtype = 0x02; goto foundp; } - outb(0x3c5,save1); - save3 = 0; - outb(0x3c4,0x10); save1 = inb(0x3c5); - outb(0x3c5, save1 & 0xfb); save2 = inb(0x3c5) & 0x04; - if (save2) save3 = 1; - outb(0x3c5, save1 | 0x04); save2 = inb(0x3c5) & 0x04; - if (!save2) save3 = 1; - vds.cardsubtype = save3 ? 0x03 : 0x04; -foundp: outb(0x3c5,save1); -foundp2: if (vds.cardsubtype != -1) { - /* find ram */ - outb(0x3ce,0x0b); - switch(inb(0x3cf) & 0xc0) { - case 0x80: vds.ram = 512; break; - case 0xc0: vds.ram = 1024; break; - default: vds.ram = 256; - } - return; - } - } - -/* Strategy: The ET4000 uses the reg 3x5/33, the ET3000 uses the 3x5/23 - * uniquely, the WD paradises also use 3x5/33. So check for Paradise - * first, and if it's not, check for 3000/4000 with these registers - */ - /* Is this a TSENG? */ - outb(0x3bf, 3); - outb(iobase+8,0xa0); - - save1 = inb(iobase+0x0A); - outb(0x3C0, 0x20|0x16); save2 = inb(0x3C1); - outb(0x3C0, save2 ^ 0x10); - outb(0x3C0, 0x20|0x16); save3 = inb(0x3C1); - outb(0x3C0, save2); - if (save3 == (save2 ^ 0x10)) { - /* assume it is a Tseng, but which one */ - outb(iobase+4, 0x23); save2 = inb(iobase+5); - outb(iobase+5, save2 ^ 0x07); save3 = inb(iobase+5); - outb(iobase+5, save2); - if (save3 == (save2 ^ 0x07)) { - vds.ram = 512; - vds.cardtype = VG_ET3000; - } else - { - /* same experiment for reg 33 */ - outb(iobase+4, 0x33); save2 = inb(iobase+5); - outb(iobase+5, save2 ^ 0x0f); save3 = inb(iobase+5); - outb(iobase+5, save2); - if (save3 == (save2 ^ 0x0f)) { - vds.cardtype = VG_ET4000; - outb(iobase+4,0x37); - switch (inb(iobase+5) & 03) { - case 1: vds.ram = 256; break; - case 2: vds.ram = 512; break; - case 3: vds.ram = 1024; break; - } - } - } - } - /* cleanup for TSENG */ - outb(0x3bf, 1); - outb(iobase+8, 0xa0); - - if (vds.cardtype != VG_VGA) return; - - /* now look for the GENOAs */ - cp = videobios + 0x37; - cp += (u_char)*cp; /* at 0x37 is the offset to the real signature */ - if (*cp==0x77 && *(cp+2)==0x66 && *(cp+3)==0x99) { - /* this is a GENOA. Note that the GENOAs 5xxx are - * really TSENG ET3000, so they should have fallen - * through the sieve above already, if not, something's - * quite strange here, so I don't believe its a GENOA - */ - vds.cardtype = VG_GENOA; - vds.cardsubtype = *(cp+1); - switch (vds.cardsubtype) { - default: - case 0x33: /* shouldn't reach this */ - case 0x55: vds.cardtype = VG_VGA; return; - case 0x22: - case 0x00: vds.ram = 256; return; - case 0x11: vds.ram = 512; return; - } - /*NOTREACHED*/ - } - - /* - * Look for the Tridents - */ - outb(0x3c4, 0x0e); save1 = inb(0x3c5); - outb(0x3c5, 0); save2 = inb(0x3c5) & 0x0f; - outb(0x3c5, save1 ^ 0x02); - if (save2==0x02) { - /* is a Trident */ - vds.cardtype = VG_TVGA; - outb(0x3c4, 0x0b); - outb(0x3c5, 0); - save1 = inb(0x3c5); - /* restore "old mode", recommended by - * chmr@edvz.tu-graz.ac.at (Christoph Robitschko) - */ - outb(0x3c5, 0); - vds.cardsubtype = save1; - if (save1== 0xfd) /*TVGA 9000*/ - vds.ram = 1024; - else - vds.ram = 512; /* There is 3c4/1f, but I don't know the encoding */ - - return; - } - - /* for the moment X11 does not know any more chips, but - * I found some more in Ferraro's book - * - * Video 7 - */ - outw(0x3c4,0xea06); - outb(iobase+4,0x0c); save1 = inb(iobase+5); - outb(iobase+5,0x55); save2 = inb(iobase+5); - outb(iobase+4,0x1f); save3 = inb(iobase+5); - outb(iobase+4,0x0c); outb(iobase+5,save1); - if (save3 == (0x55^0xea)) { - /* this is a video 7 */ - vds.cardtype = VG_VIDEO7; - outb(0x3c4, 0x8e); - vds.cardsubtype = inb(0x3c5); - if (vds.cardsubtype > 0x40 && vds.cardsubtype < 0x4a) - vds.ram = 1024; /* 1024i */ - else - vds.ram = 512; - return; - } - - /* C&T Chips, I'm not sure about that, in particular the - * strange 46E8 port, I somewhere heard that I/O space is - * 0000-03FF only, but I don't know whether any chipset - * clips this range. Anyway... - */ - outb(0x46e8,0x1e); - if (inb(0x104)==0xa5) { - outb(0x103, 0x80); outb(0x46e8, 0x0e); - outb(0x3d6, 0); - vds.cardtype = VG_CHIPS; - vds.cardsubtype = inb(0x3d7) & 0xf0; - switch(vds.cardsubtype) { - case 0x10: - outb(0x3d6, 0x3a); save1=inb(0x3d7); - outb(0x3d7, 0xaa); save2=inb(0x3d7); - outb(0x3d7, save1); - if (save2==0xaa) { - vds.cardsubtype = 0x11; - vds.ram = 1024; - } else - vds.ram = 256; - break; - case 0x20: vds.ram = 256; break; - case 0x30: vds.ram = 1024; break; - case 0x50: vds.ram = 256; - } - outb(0x46e8, 0x1e); outb(0x103, 0); - } - outb(0x46e8, 0x0e); - - /* the two ATI's */ - cp = videobios + 0x0031; - if (!bcmp(cp,"761295520",9)) { - /* is an ATI, also unsure about the numbers */ - vds.cardtype = VG_ATI; - cp = videobios + 0x0042; - vds.cardsubtype = *cp | (*(cp+1)<<8); - vp = (u_short *) videobios + 0010; - vds._atiext = *vp; - outb(*vp,0xbb); save1 = inb((*vp)+1); - vds.ram = (save1 & 0x20) ? 512 : 256; - - return; - } - - /* I don't know more types, so assume - * it is a VGA (at least looks as if) - */ - vds.ram = 256; - vds.cardtype = VG_VGA; - return; - -} - -static char *card2name(int id) -{ - static char *nametable[] = { - /* 0 */ "UNKNOWN", - /* 1 */ "MDA/Hercules", - /* 2 */ "CGA", - /* 3 */ "EGA", - /* 4 */ "VGA", - /* 5 */ "C&T", - /* 6 */ "Genoa VGA", - /* 7 */ "Paradise VGA", - /* 8 */ "Trident VGA", - /* 9 */ "Tseng ET3000", - /* 10 */ "Tseng ET4000", - /* 11 */ "Video7 VGA", - /* 12 */ "ATI VGA", - 0, - }; - - return nametable[id]; -} - -/* - * set cursor shape - */ -int vga_setcshape(struct cursorshape *data) -{ - register s,e; - - s = data->start; - e = data->end; - - /* I don't do error correction here, the user should pass - * correct and checked data! Note: The registers allow 5 Bits == 31 - */ - if (s<0 || s>31 || e<0 || e>31) return EINVAL; - - /* the Tridents seem to have the feature to switch off - * cursor when start is 0 (not verified) - */ - if (vds.cardtype==VG_TVGA && s==0) s=1; - - outb(vds.iobase+4, M6845_CURSTART); - outb(vds.iobase+5, s); - outb(vds.iobase+4, M6845_CUREND); - outb(vds.iobase+5, e); -} - -#ifndef MINITERM -int vga_getcshape(struct cursorshape *data) -{ - /* update from registers */ - outb(vds.iobase+4, M6845_CURSTART); - data->start = inb(vds.iobase+5) & 0x1F; - outb(vds.iobase+4, M6845_CUREND); - data->end = inb(vds.iobase+5) & 0x1F; - - return 0; -} -#endif - -int vga_getvideoinfo(struct videoinfo *data) -{ - int i; - - bcopy(card2name(vds.cardtype),data->name,20); - data->type = vds.cardtype; - data->subtype = vds.cardsubtype; - data->ram = vds.ram; - data->iobase = vds.iobase; - return 0; -} - -static char modesave; - -void vga_enablecg() -{ - register iobase = vds.iobase; - - while((inb(iobase+10)&0x08)==0); /* wait for vertical blanking */ - inb(iobase+10); - outb(0x3C0,0x30); modesave = inb(0x3C1); - inb(iobase+10); - outb(0x3C0,0x30); outb(0x3C0, 0x01); /* graphics mode */ -/* outw(0x3c4,0x0100);*/ /* hold sequencer synch reset */ - outw(0x3c4,0x0402); /* write enable map "page" */ - outw(0x3c4,0x0604); /* set sequential addressing */ -/* outw(0x3c4,0x0300);*/ /* release sequencer synch reset */ - outw(0x3ce,0x0204); /* select map "page" for cpu reading */ - outw(0x3ce,0x0005); /* RM=0,O/E=0,WM=00 */ - outw(0x3ce,0x0d06); /* map at "B8000" */ -} - -void vga_disablecg() -{ - register iobase10 = vds.iobase+10; - char seq; - - inb(iobase10); - outb(0x3C0,0x30); outb(0x3C0, modesave); /* alpha mode */ -/* outw(0x3c4,0x0100);*/ /* hold sequencer synch reset */ - outw(0x3c4,0x0302); /* write enable text pages */ - outw(0x3c4,0x0304); /* set O/E addressing */ -/* outw(0x3c4,0x0300);*/ /* release sequencer synch reset */ - outw(0x3ce,0x0004); /* select map 0 for cpu reading */ - outw(0x3ce,0x1005); /* RM=0,O/E=0,WM=00 */ - outw(0x3ce,0x0e06); /* map at "B8000", Chain O/E */ - - /* set the 8/9 bit according to the state of vds.f89 - * The little problem is that with a EGA we have to guess - * the old value (but it is usually 0x03) - */ - if (vds.cardtype != VG_EGA) { - inb(iobase10); - outb(0x3c0,0x20|0x10); - seq = inb(0x3c1); - } - else - seq = 0x03; - inb(iobase10); - outb(0x3c0,0x20|0x10); - if (vds.f89bit == 9) outb(0x3c0, seq | 0x04); - else outb(0x3c0, seq & ~0x04); - - while(inb(iobase10)&0x08); /* wait for end of vertical blanking */ -} - -static u_char iso2pc8[] = { -/*A0*/ 0xff,0xad,0x9b,0x9c,0xfe,0x9d,0x7c,0x15, -/*A8*/ 0xfe,0xfe,0xa6,0xae,0xaa,0x2d,0xfe,0xfe, -/*B0*/ 0xf8,0xf1,0xfd,0xfe,0xfe,0xe6,0x14,0xf9, -/*B8*/ 0xfe,0xfe,0xa7,0xaf,0xac,0xfd,0xfe,0xa8, -/*C0*/ 0xfe,0xfe,0xfe,0xfe,0x8e,0x8f,0x92,0x80, -/*C8*/ 0xfe,0x90,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe, -/*D0*/ 0xfe,0xa5,0xfe,0xfe,0xfe,0xfe,0x99,0xfe, -/*D8*/ 0xfe,0xfe,0xfe,0xfe,0x9a,0xfe,0xfe,0xe1, -/*E0*/ 0x85,0xa0,0x83,0xfe,0x84,0x86,0x91,0x87, -/*E8*/ 0x8a,0x82,0x88,0x89,0x8d,0xa1,0x8c,0x8b, -/*F0*/ 0xfe,0xa4,0x95,0xa2,0x93,0xfe,0x94,0xf6, -/*F8*/ 0xfe,0x97,0xa3,0x96,0x81,0xfe,0xfe,0x98 -}; - -/* needs attribute byte to select font */ -int vga_xlatiso646(struct vty *vp,u_short *at,u_short *sat,int c) -{ - int f2flag = vp->op->f2; - - if (vds.encoding[1] != NOFONT) { /* second font loaded? */ - if (f2flag) { - *at |= 0x08; /* select font 2 */ - *sat |= 0x08; - } else - { - *at &= 0xF7; - *sat &= 0xF7; - } - } else f2flag = 0; /* ignore font switch */ - - /* need to translate character? */ - if (f2flag==0 && vds.encoding[0]==XLAT2PC8) - return (c<0xa0) ? c : iso2pc8[c-0xa0]; - else - return c; -} - -/*********************************************************************** - * Support functions for the terminal emulator - **********************************************************************/ -#ifndef GFX_CONSOLE -/* - * moves the cursor up n lines - */ -void vga_cursorup(struct vty *vp, int n) -{ - register pos; - - if (n <= 0) n = 1; - - pos = vp->crtat - vp->Crtat; - pos -= vp->ncol * n; - vp->row--; - if (pos < 0) { - pos += vp->size; - vp->row = vp->nrow-1; - } - vp->crtat = vp->Crtat + pos; -} - -/* - * moves the cursor down n lines - */ -void vga_cursordown(struct vty *vp, int n) -{ - register pos; - - if (n <= 0) n = 1; - pos = vp->crtat - vp->Crtat; - pos += vp->ncol * n; - vp->row++; - if (pos >= vp->size) { - pos -= vp->size; - vp->row = 0; - } - vp->crtat = vp->Crtat + pos; -} - -/* - * moves the cursor left n columns - */ -void vga_cursorleft(struct vty *vp, int n) -{ - register pos; - - if (n <= 0) n = 1; - pos = vp->crtat - vp->Crtat; - pos -= n; vp->col -= n; - if (vp->col < 0) { - vp->col += vp->ncol; - pos += vp->ncol; /* cursor stays on same line */ - } - vp->crtat = vp->Crtat + pos; -} - -/* - * moves the cursor right n columns - * wrap=1: cursor stays on same line - */ -void vga_cursorright(struct vty *vp, int n, int wrap) -{ - register pos; - - if (n <= 0) n = 1; - pos = vp->crtat - vp->Crtat; - pos += n; vp->col += n; - if (wrap && vp->col >= vp->ncol) { - vp->col -= vp->ncol; - pos -= vp->ncol; /* cursor stays on same line */ - } - vp->crtat = vp->Crtat + (pos % vp->size); -} - -/* - * scrollup n lines and fill free space with default attribute - * cm = 1: also move cursor - */ -void vga_scrollup(struct vty *vp,int n, int cm) -{ - u_short attr = vp->op->def_at; - int blkx,blky; - - if (n <= 0) n = 1; - blkx = vp->ncol*n; - blky = vp->size - blkx; - - bcopy(vp->Crtat+blkx, vp->Crtat, blky*CHR); - fillw((attr <<8)+' ', vp->Crtat+blky, blkx); - if (cm) { - vp->crtat -= blkx; - vp->row-= n; - } -} - -#ifndef MINITERM -/* - * scroll down n lines and fill free space with default attribute - */ -void vga_scrolldown(struct vty *vp, int n) -{ - u_short attr = vp->op->def_at; - int blkx; - - if (n <= 0) n = 1; - blkx = vp->ncol*n; - - bcopy(vp->Crtat, vp->Crtat+blkx, (vp->size-blkx)*CHR); - fillw((attr <<8)+' ', vp->Crtat, blkx); - /* XXX vp->crtat += blkx; */ -} -#endif /*!MINITERM*/ - -/* - * set the absolute cursor position (row,col = 0..max) - */ -void vga_cursormove(struct vty *vp, int row, int col) -{ - if (row==0 || col==0) { - vp->crtat = vp->Crtat; - vp->row = 0; - vp->col = 0; - } else if (col <= vp->ncol && row <= vp->nrow) { - vp->crtat = vp->Crtat + (row - 1) * vp->ncol + col - 1; - vp->col = col - 1; - vp->row = row - 1; - } -} - -/* - * cursorrelative: move cursor relative, don't check for outside of screen - */ -void vga_cursorrelative(struct vty *vp, int dx, int dy) -{ - int incr = dy*vp->ncol + dx; - int pos = vp->crtat - vp->Crtat; - -/* if ((pos+incr) < 0 || (pos+incr) >= vp->size) - return; -*/ vp->crtat += incr; - vp->col = (vp->col+dx) % vp->ncol; - vp->row = (vp->row+dy) % vp->nrow; -} - -/* - * Clear screen with default attr - * mode = 0: from cursor to end of screen - * = 1: cursor position only - * = 2: whole display - */ -void vga_clearcursor(struct vty *vp, int mode) -{ - u_short attr = (vp->op->def_at << 8) | ' '; - - /* clear ... */ - switch (mode) { - case 0: /* ... to end of display */ - fillw(attr, vp->crtat, vp->Crtat + vp->size - vp->crtat); - break; - case 1: /* ... to next location */ - fillw(attr, vp->Crtat, vp->crtat - vp->Crtat + 1); - break; - case 2: /* ... whole display */ - fillw(attr, vp->Crtat, vp->size); - } -} - -/* - * Clear line and fill with default attr - * mode = 0: from cursor to end of line - * = 1: from beginning to cursor - * = 2: whole line - */ -#ifndef MINITERM -void vga_clearline(struct vty *vp, int mode) -{ - u_short attr = (vp->op->def_at << 8) | ' '; - - /* clear ... */ - switch (mode) { - case 0: /* ... current to EOL */ - fillw(attr, vp->crtat, - vp->ncol - (vp->crtat - vp->Crtat) % vp->ncol); - break; - case 1: /* ... beginning to next */ - fillw(attr, - vp->crtat - (vp->crtat - vp->Crtat) % vp->ncol, - ((vp->crtat - vp->Crtat) % vp->ncol) + 1); - break; - case 2: /* ... entire line */ - fillw(attr, vp->crtat - (vp->crtat - vp->Crtat) % vp->ncol, - vp->ncol); - } -} - -/* - * delete n lines from cursor position to end, pull up lines with default attr - */ -void vga_deleteline(struct vty *vp, int n) -{ - u_short *crtend,*pp; - u_short attr = (vp->op->def_at <<8) | ' '; - - if (n <= 0) n = 1; - - /* Work from beginning of line */ - vp->crtat -= (vp->crtat - vp->Crtat) % vp->ncol; - vp->col = 0; - crtend = vp->Crtat + vp->size; - - /* Cap affected area at bottom of screen */ - if ((pp = (vp->crtat + n*vp->ncol)) > crtend) { - n = (crtend-vp->crtat) / vp->ncol; - pp = crtend; - } - - /* If there's visible lines left, move them up */ - if (pp < crtend) { - bcopy(pp, vp->crtat, (crtend-pp)*CHR); - } - fillw(attr, crtend - n*vp->ncol, n*vp->ncol); -} - -/* - * insert n lines from cursor position to end, fill with default attr - */ -void vga_insertline(struct vty *vp, int n) -{ - u_short *crtend,*pp; - u_short attr = (vp->op->def_at << 8) | ' '; - - if (n <= 0) n = 1; - - /* Work from beginning of line */ - vp->crtat -= (vp->crtat - vp->Crtat) % vp->ncol; - vp->col = 0; - crtend = vp->Crtat + vp->size; - - /* Cap affected area at bottom of screen */ - if ((pp = (vp->crtat + n*vp->ncol)) > crtend) { - n = (crtend-vp->crtat) / vp->ncol; - pp = crtend; - } - - /* If there's visible lines left, move them down */ - if (pp < crtend) { - bcopy(vp->crtat, pp, (crtend-pp)*CHR); - } - fillw(attr, vp->crtat, n*vp->ncol); -} - -/* - * Delete n chars in line, fill free space with default attr - */ -void vga_deletechars(struct vty *vp, int n) -{ - u_short *crtend,*pp; - u_short attr = (vp->op->def_at << 8) | ' '; - - if (n <= 0) n = 1; - - /* Point to end of current line */ - pp = vp->crtat + (vp->ncol - (vp->crtat-vp->Crtat) % vp->ncol); - - /* Cap delete to end of current line */ - if ((vp->crtat+n) > pp) - n = pp-vp->crtat; - - /* If visible characters, move in */ - if ((vp->crtat+n) < pp) - bcopy(vp->crtat+n, vp->crtat, ((pp-vp->crtat)-n)*CHR); - - /* Blank out space at end of line */ - fillw(attr, pp-n, n); -} - -/* - * Delete n chars in line, fill free space with default attr - */ -void vga_insertchars(struct vty *vp, int n) -{ - u_short *crtend,*pp; - u_short attr = (vp->op->def_at << 8) | ' '; - - if (n <= 0) n = 1; - - /* Point to end of current line */ - pp = vp->crtat + (vp-> ncol - (vp->crtat-vp->Crtat) % vp->ncol); - - /* Cap insert to end of current line */ - if ((vp->crtat+n) > pp) - n = pp-vp->crtat; - - /* If visible characters, move out */ - if ((vp->crtat) < pp) - bcopy(vp->crtat, vp->crtat+n, ((pp-vp->crtat)-n)*CHR); - - /* Blank out space at new positions */ - fillw(attr, vp->crtat, n); -} -#endif /*!MINITERM*/ - -static u_short fgansitopc[] = -{ FG_BLACK, FG_RED, FG_GREEN, FG_BROWN, FG_BLUE, - FG_MAGENTA, FG_CYAN, FG_LIGHTGREY -}; - -static u_short bgansitopc[] = -{ BG_BLACK, BG_RED, BG_GREEN, BG_BROWN, BG_BLUE, - BG_MAGENTA, BG_CYAN, BG_LIGHTGREY -}; - -/* - * set attributes - * mode = 0: reset normal attributes, attr=0: std, attr=1: kernel - * mode = 1: set ansi color background - * mode = 2: set ansi color foreground - * mode = 3: set PC attribute - * mode = 4: reset standout attributes (extension!) - */ -void vga_setattributes(struct vty *vp, int mode, int attr) -{ - struct outmode *o = vp->op; - - switch (mode) { - case 0: /* reset to normal attributes */ - if (o == &vp->om[0]) { /* std */ - o->fg_at = vds.color ? DEF_STD_C_FGAT : DEF_STD_M_FGAT; - o->bg_at = DEF_STD_BGAT; - } else - { /* kernel */ - o->fg_at = vds.color ? DEF_KERN_C_FGAT : DEF_KERN_M_FGAT; - o->bg_at = DEF_KERN_BGAT; - } - /* asa@kiae.su: fix to allow non-black background */ - /*o->def_at = o->fg_at | o->bg_at;*/ - break; - - case 1: /* ansi background */ - if (vds.color) - o->bg_at = bgansitopc[attr & 7]; - break; - - case 2: /* ansi foreground */ - if (vds.color) - o->fg_at = fgansitopc[attr & 7]; - break; - - case 3: /* pc text attribute */ - o->fg_at = attr & 0x8f; - o->bg_at = attr & 0x70; - break; - - case 4: /* set so attribute to default */ - vp->so_at = vds.color ? DEF_SO_C_AT : DEF_SO_M_AT; - } - o->def_at = o->fg_at | o->bg_at; /* asa@kiae.su */ -} - -void vga_selectfont(struct vty *vp,int fontnr) -{ - vp->op->f2 = (fontnr==2); -} - -void vga_wrtchar(struct vty *vp, u_int c, u_int at) -{ - *(vp->crtat++) = (u_short)(at<<8)|c; - vp->col++; -} - -/* - * check whether crtat points to outside the screen - * = 0: no, = -1: less than top of screen, =1: below bottom of screen - */ -int vga_checkcursor(struct vty *vp) -{ - if (vp->crtat < vp->Crtat) return -1; - if (vp->crtat >= vp->Crtat+vp->size) return 1; - return 0; -} - -/* - * return a char to the vty, i.e. simulate an (ASCII) keypress, - * used to return status messages (not for pc3) - */ -void vga_sendchar(struct vty *vp, XCHAR c) -{ - /* XXX modify for real (=char) XCHAR type */ - - /* is it opened? */ - if (vp->ttycnt) - (*linesw[vp->ttyp->t_line].l_rint)(c & 0xFF, vp->ttyp); -} - -void vga_initvideo() {} - -#endif -#endif /* NPC=0 */ -#endif /* NCO=1 */ diff --git a/sys/i386/isa/codrv/co_vty.c b/sys/i386/isa/codrv/co_vty.c deleted file mode 100644 index e01e64ee84..0000000000 --- a/sys/i386/isa/codrv/co_vty.c +++ /dev/null @@ -1,267 +0,0 @@ -/* Copyright 1992 by Holger Veit - * May be freely used with Bill Jolitz's port of - * 386bsd and may be included in a 386bsd collection - * as long as binary and source are available and reproduce the above - * copyright. - * - * You may freely modify this code and contribute improvements based - * on this code as long as you don't claim to be the original author. - * Commercial use of this source requires permittance of the copyright - * holder. A general license for 386bsd will override this restriction. - * - * Use at your own risk. The copyright holder or any person who makes - * this code available for the public (administrators of public archives - * for instance) are not responsible for any harm to hardware or software - * that might happen due to wrong application or program faults. - * - * @(#) $RCSfile: co_vty.c,v $ $Revision: 1.6 $ (Contributed to 386bsd) $Date: 93/01/23 23:14:58 $ - * - * This file includes the data table for virtual terminals - */ -static char *rcsid = "$Header: /usr/src/sys.386bsd/i386/isa/codrv/RCS/co_vty.c,v 1.6 93/01/23 23:14:58 root Exp Locker: root $"; - -/* - * History: see CO_HISTORY - */ - -#include -#include "co_hdr.h" - -#ifdef MINITERM -#define NVTY 1 -#else -#include "vty.h" -#endif - -#if NVTY > 0 - -#if NVTY > 12 -#undef NVTY -#define NVTY 12 -#endif - -#else /* ! NVTY > 0 */ -#undef NVTY -#define NVTY 1 -#endif /* ! NVTY > 0 */ - -struct vty vtys[NVTY]; -struct tty pccons[NVTY]; -int nvty = NVTY; - -/* - * returns a pointer to a vty, when called with a - * vty devmajor/minor, 0 when called with /dev/kbd - */ -struct vty *dev2vty(dev_t dev) -{ - register m = minor(dev); - - /* check whether called from a vty or kbd: - * kbd has global scope - */ - if (major(dev)==consoftc.cs_mymajor) - return 0; - - return &vtys[m>=nvty?0:m]; -} - -/* - * initialize the vty data structure - */ -void vty_init(int first) -{ - int i,adr; - struct vty *vp; - struct outmode *std,*kern; - - /* first mode: only 1 vty for startup, must be extended - * when remainder of system is up - */ - nvty = first ? 1 : NVTY; - - for (i=0; iom[0]; - kern= &vp->om[1]; - vp->op = std; - - vp->vtynum = i; - - std->escstate = - kern->escstate = 0; /* "ESC_NONE" */ - std->parcnt = - kern->parcnt = 0; - - emul_setattributes(vp, 0, 0); /* set std attributes */ - emul_setattributes(vp, 0, 1); /* set kernel attributes */ - emul_setattributes(vp, 4, 0); /* set so attributes */ - std->f2 = - kern->f2 = 0; - vp->so = 0; - - vp->pitch = 0x31b; /* default 1.500 kHz */ - vp->duration = hz/4; - - /* initialize vty tty structure */ - vp->ttyp = &pccons[i]; - vp->ttycnt = 0; - - /* set default # of rows and columns */ - vp->ncol = DEFAULTCOL; - vp->nrow = DEFAULTROW; - vp->size = vp->ncol * vp->nrow; - vp->visible = - vp->altgrlock = - vp->shiftlock = - vp->scroll = - vp->num = - vp->caps = 0; - - /* set Screen */ - if (first) { - /* set console to screen */ - vp->Crtat = vp->crtat = Crtat; - vp->col = vp->row = 0; - vp->vbuf = 0; - actvty = vp; - emul_clearcursor(vp,2); - } - else { - /* now also vm is initialized */ - vp->vbuf = (u_short*)malloc(vp->size*CHR, M_TEMP, M_NOWAIT); - if (i > 0) { - vp->crtat = vp->Crtat = vp->vbuf; - vp->col = vp->row = 0; - actvty = vp; - emul_clearcursor(vp,2); - } - } - } - - /* execute the local vt_emulator initializations */ - vtemul_init(); - - /* set active vty */ - vty_setactive(0,2); -} - -/* - * set the active vty, which writes to screen (-1: suspend writing to screen) - * sw=0: only set page for writing - * sw=1: do a physical switch (save/restore pages first) - * sw=2: do a logical switch (don't save/restore pages) - * sw=3: restore page without saving old page - */ -void vty_setactive(int vtyno,int sw) -{ - struct vty *oldvty; - register struct vty *vp; - int pos,s; - - /* protect switch */ - vtswsema = 1; - - /* to disable writing to screen entirely */ - if (vtyno == -1) { - pos = actvty->crtat - actvty->Crtat; - actvty->Crtat = actvty->vbuf; - actvty->crtat = actvty->Crtat + pos; - return; - } - - if (vtyno < nvty) { - - /* save old vty, and set new one */ - oldvty = actvty; - vp = actvty = &vtys[vtyno]; - - switch (sw) { - case 0: - break; - case 1: - /* save screen */ - bcopy(Crtat,oldvty->vbuf,oldvty->size*CHR); - /*FALLTHRU*/ - case 3: - /* restore videopage of new vty */ - bcopy(vp->vbuf,Crtat,vp->size*CHR); - /*FALLTHRU*/ - case 2: - /* adjust old pointers */ - pos = oldvty->crtat - oldvty->Crtat; - oldvty->Crtat = oldvty->vbuf; - oldvty->crtat = oldvty->vbuf + pos; - oldvty->visible = 0; - - /* set the write pointers - * these normally point to the vbuf, but on a visible - * active screen they point to video memory - */ - pos = vp->crtat - vp->Crtat; - vp->Crtat = Crtat; - vp->crtat = vp->Crtat + pos; - - /* set cursor */ - vga_setcursorpos(pos); - - /* restore LEDS */ - kbd_setleds(leds(vp)); - - actvty->visible = 1; - } - } - - vtswsema = 0; -} - -#ifndef MINITERM -/* - * switch to next vty - */ -void vty_next() -{ - int n = actvty->vtynum; - if (nvty==1) return; - - n = (n+1) % nvty; - - vty_setactive(n,1); -} - -/* - * switch to previous vty - */ -void vty_previous() -{ - int n = actvty->vtynum; - if (nvty==1) return; - - n = (n==0) ? nvty-1 : n-1; - - vty_setactive(n,1); -} -#endif /*!MINITERM*/ - -/* - * vty_broadcast: Send a message to all vtys - */ -void -#ifdef __STDC__ -vty_broadcast(const char *fmt, ...) -#else -vty_broadcast(fmt /*, va_alist */) - char *fmt; -#endif -{ - int i; - va_list ap; - -#define TOCONS 0x01 /* inherited from /sys/kern/subr_prf.c */ -#define TOTTY 0x02 /* inherited from /sys/kern/subr_prf.c */ - va_start(ap, fmt); - for (i=0; i 0 @@ -62,6 +52,7 @@ static char rcsid[] = "$Header: /usr/bill/working/sys/i386/isa/RCS/com.c,v 1.2 9 #include "kernel.h" #include "syslog.h" +#include "i386/isa/isa.h" #include "i386/isa/isa_device.h" #include "i386/isa/comreg.h" #include "i386/isa/ic/ns16550.h" @@ -128,7 +119,7 @@ struct isa_device *dev; outb(dev->id_iobase+com_iir, 0); DELAY(100); if ((inb(dev->id_iobase+com_iir) & 0x38) == 0) - return(1); + return(IO_COMSIZE); return(0); } @@ -153,7 +144,7 @@ struct isa_device *isdp; DELAY(100); if ((inb(port+com_iir) & IIR_FIFO_MASK) == IIR_FIFO_MASK) { com_hasfifo |= 1 << unit; - printf(" fifo"); + printf("com%d: fifo\n", unit); } outb(port+com_ier, 0); @@ -401,7 +392,7 @@ commint(unit, com) outb(com+com_mcr, inb(com+com_mcr) & ~(MCR_DTR | MCR_RTS) | MCR_IENABLE); } else if ((stat & MSR_DCTS) && (tp->t_state & TS_ISOPEN) && - (tp->t_flags & CRTSCTS)) { + (tp->t_cflag & CRTSCTS)) { /* the line is up and we want to do rts/cts flow control */ if (stat & MSR_CTS) { tp->t_state &=~ TS_TTSTOP; diff --git a/sys/i386/isa/comreg.h b/sys/i386/isa/comreg.h index 2a626832e8..4b0f1b6fa5 100644 --- a/sys/i386/isa/comreg.h +++ b/sys/i386/isa/comreg.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)comreg.h 7.2 (Berkeley) 5/9/91 + * from: @(#)comreg.h 7.2 (Berkeley) 5/9/91 + * $Id$ */ diff --git a/sys/i386/isa/debug.h b/sys/i386/isa/debug.h index e00bc81bb8..5e72cb7fe4 100644 --- a/sys/i386/isa/debug.h +++ b/sys/i386/isa/debug.h @@ -1,4 +1,7 @@ -/* debug.h */ +/* + * from: debug.h, part of Bruce Evans interrupt code + * $Id$ + */ #define SHOW_A_LOT_NOT diff --git a/sys/i386/isa/fd.c b/sys/i386/isa/fd.c index a461e8153d..c287ef1850 100644 --- a/sys/i386/isa/fd.c +++ b/sys/i386/isa/fd.c @@ -34,48 +34,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)fd.c 7.4 (Berkeley) 5/25/91 + * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 + * $Id: fd.c,v 1.5 1993/09/15 23:27:45 rgrimes Exp $ * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00153 - * -------------------- ----- ---------------------- - * - * 20 Apr 93 Julian Elischer Heavily re worked, see notes below - * - * Largely rewritten to handle multiple controllers and drives - * By Julian Elischer, Sun Apr 4 16:34:33 WST 1993 - */ -char rev[] = "$Revision: 1.10 $"; -/* - * $Header: /usr/src/sys.386bsd/i386/isa/RCS/fd.c,v 1.10 93/04/13 16:53:29 root Exp $ - */ -/* - * $Log: fd.c,v $ - * Revision 1.10 93/04/13 16:53:29 root - * make sure turning off a drive motor doesn't deselect another - * drive active at the time. - * Also added a pointer from the fd_data to it's fd_type. - * - * Revision 1.9 93/04/13 15:31:02 root - * make all seeks go through DOSEEK state so are sure of being done right. - * - * Revision 1.8 93/04/12 21:20:13 root - * only check if old fd is the one we are working on if there IS - * an old fd pointer. (in fdstate()) - * - * Revision 1.7 93/04/11 17:05:35 root - * cleanup timeouts etc. - * also fix bug to select teh correct drive when running > 1 drive - * at a time. - * - * Revision 1.6 93/04/05 00:48:45 root - * change a timeout and add version to banner message - * - * Revision 1.5 93/04/04 16:39:08 root - * first working version.. some floppy controllers don't seem to - * like 2 int. status inquiries in a row. - * */ #include "fd.h" @@ -87,8 +48,10 @@ char rev[] = "$Revision: 1.10 $"; #include "conf.h" #include "file.h" #include "ioctl.h" +#include "disklabel.h" #include "buf.h" #include "uio.h" +#include "syslog.h" #include "i386/isa/isa.h" #include "i386/isa/isa_device.h" #include "i386/isa/fdreg.h" @@ -282,8 +245,11 @@ struct isa_device *dev; fdu++,fdsu++) { /* is there a unit? */ - if ((fdt & 0xf0) == RTCFDT_NONE) + if ((fdt & 0xf0) == RTCFDT_NONE) { +#define NO_TYPE NUMTYPES + fd_data[fdu].type = NO_TYPE; continue; + } #ifdef notyet /* select it */ @@ -304,22 +270,16 @@ struct isa_device *dev; fd_data[fdu].track = -2; fd_data[fdu].fdc = fdc; fd_data[fdu].fdsu = fdsu; - /* yes, announce it */ - if (!hdr) - printf(" drives "); - else - printf(", "); - printf("%d: ", fdu); - + printf("fd%d: unit %d type ", fdcu, fdu); if ((fdt & 0xf0) == RTCFDT_12M) { - printf("1.2M"); + printf("1.2MB 5.25in\n"); fd_data[fdu].type = 1; fd_data[fdu].ft = fd_types + 1; } if ((fdt & 0xf0) == RTCFDT_144M) { - printf("1.44M"); + printf("1.44MB 3.5in\n"); fd_data[fdu].type = 0; fd_data[fdu].ft = fd_types + 0; } @@ -329,7 +289,6 @@ struct isa_device *dev; hdr = 1; } - printf(" %s ",rev); /* Set transfer to 500kbps */ outb(fdc->baseport+fdctl,0); /*XXX*/ } @@ -400,7 +359,10 @@ bad: /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ -set_motor(fdcu_t fdcu, fdu_t fdu, int reset) +set_motor(fdcu, fdu, reset) + fdcu_t fdcu; + fdu_t fdu; + int reset; { int m0,m1; int selunit; @@ -427,24 +389,35 @@ set_motor(fdcu_t fdcu, fdu_t fdu, int reset) | (m1 ? FDO_MOEN1 : 0))); } -fd_turnoff(fdu_t fdu) +fd_turnoff(fdu) + fdu_t fdu; { + int s; + fd_p fd = fd_data + fdu; + s = splbio(); fd->flags &= ~FD_MOTOR; set_motor(fd->fdc->fdcu,fd->fdsu,0); + splx(s); } -fd_motor_on(fdu_t fdu) +fd_motor_on(fdu) + fdu_t fdu; { + int s; + fd_p fd = fd_data + fdu; + s = splbio(); fd->flags &= ~FD_MOTOR_WAIT; if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) { - fd_pseudointr(fd->fdc->fdcu); + fdintr(fd->fdc->fdcu); } + splx(s); } -fd_turnon(fdu_t fdu) +fd_turnon(fdu) + fdu_t fdu; { fd_p fd = fd_data + fdu; if(!(fd->flags & FD_MOTOR)) @@ -455,7 +428,8 @@ fd_turnon(fdu_t fdu) } } -fd_turnon1(fdu_t fdu) +fd_turnon1(fdu) + fdu_t fdu; { fd_p fd = fd_data + fdu; fd->flags |= FD_MOTOR; @@ -466,7 +440,8 @@ fd_turnon1(fdu_t fdu) /* fdc in/out */ /****************************************************************************/ int -in_fdc(fdcu_t fdcu) +in_fdc(fdcu) + fdcu_t fdcu; { int baseport = fdc_data[fdcu].baseport; int i, j = 100000; @@ -484,20 +459,29 @@ in_fdc(fdcu_t fdcu) #endif } -out_fdc(fdcu_t fdcu,int x) +out_fdc(fdcu, x) + fdcu_t fdcu; + int x; { int baseport = fdc_data[fdcu].baseport; - int i = 100000; + int i; + /* Check that the direction bit is set */ + i = 100000; while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0); + if (i <= 0) return (-1); /* Floppy timed out */ + + /* Check that the floppy controller is ready for a command */ + i = 100000; while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0); - if (i <= 0) return (-1); + if (i <= 0) return (-1); /* Floppy timed out */ + + /* Send the command and return */ outb(baseport+fddata,x); TRACE1("[0x%x->fddata]",x); return (0); } -static fdopenf; /****************************************************************************/ /* fdopen/fdclose */ /****************************************************************************/ @@ -510,7 +494,7 @@ Fdopen(dev, flags) int s; /* check bounds */ - if (fdu >= NFD) return(ENXIO); + if (fdu >= NFD || fd_data[fdu].type == NO_TYPE) return(ENXIO); /*if (type >= NUMTYPES) return(ENXIO);*/ fd_data[fdu].flags |= FD_OPEN; @@ -535,7 +519,8 @@ fdclose(dev, flags) * If the controller is already busy, we need do nothing, as it * * will pick up our work when the present work completes * \***************************************************************/ -fdstart(fdcu_t fdcu) +fdstart(fdcu) + fdcu_t fdcu; { register struct buf *dp,*bp; int s; @@ -549,13 +534,16 @@ fdstart(fdcu_t fdcu) splx(s); } -fd_timeout(fdcu_t fdcu) +fd_timeout(fdcu) + fdcu_t fdcu; { fdu_t fdu = fdc_data[fdcu].fdu; int st0, st3, cyl; struct buf *dp,*bp; + int s; dp = &fdc_data[fdcu].head; + s = splbio(); bp = dp->b_actf; out_fdc(fdcu,NE7CMD_SENSED); @@ -587,11 +575,13 @@ fd_timeout(fdcu_t fdcu) fdc_data[fdcu].fdu = -1; fdc_data[fdcu].state = DEVIDLE; } - fd_pseudointr(fdcu); + fdintr(fdcu); + splx(s); } /* just ensure it has the right spl */ -fd_pseudointr(fdcu_t fdcu) +fd_pseudointr(fdcu) + fdcu_t fdcu; { int s; s = splbio(); @@ -604,7 +594,8 @@ fd_pseudointr(fdcu_t fdcu) * keep calling the state machine until it returns a 0 * * ALWAYS called at SPLBIO * \***********************************************************************/ -fdintr(fdcu_t fdcu) +fdintr(fdcu) + fdcu_t fdcu; { fdc_p fdc = fdc_data + fdcu; while(fdstate(fdcu, fdc)); @@ -614,7 +605,9 @@ fdintr(fdcu_t fdcu) * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ -int fdstate(fdcu_t fdcu, fdc_p fdc) +int fdstate(fdcu, fdc) + fdcu_t fdcu; + fdc_p fdc; { int read,head,trac,sec,i,s,sectrac,cyl,st0; unsigned long blknum; @@ -696,8 +689,10 @@ int fdstate(fdcu_t fdcu, fdc_p fdc) out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac); fd->track = -2; fdc->state = SEEKWAIT; + timeout(fd_timeout,fdcu,2 * hz); return(0); /* will return later */ case SEEKWAIT: + untimeout(fd_timeout,fdcu); /* allow heads to settle */ timeout(fd_pseudointr,fdcu,hz/50); fdc->state = SEEKCOMPLETE; @@ -853,7 +848,8 @@ int fdstate(fdcu_t fdcu, fdc_p fdc) return(1); /* Come back immediatly to new state */ } -retrier(fdcu_t fdcu) +retrier(fdcu) + fdcu_t fdcu; { fdc_p fdc = fdc_data + fdcu; register struct buf *dp,*bp; @@ -876,13 +872,13 @@ retrier(fdcu_t fdcu) break; default: { - printf("fd%d: hard error (ST0 %b ", - fdc->fdu, fdc->status[0], NE7_ST0BITS); + diskerr(bp, "fd", "hard error", LOG_PRINTF, + fdc->fd->skip, (struct disklabel *)NULL); + printf(" (ST0 %b ", fdc->status[0], NE7_ST0BITS); printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS); printf(" ST2 %b ", fdc->status[2], NE7_ST2BITS); - printf(" ST3 %b ", fdc->status[3], NE7_ST3BITS); printf("cyl %d hd %d sec %d)\n", - fdc->status[4], fdc->status[5], fdc->status[6]); + fdc->status[3], fdc->status[4], fdc->status[5]); } bp->b_flags |= B_ERROR; bp->b_error = EIO; @@ -893,11 +889,89 @@ retrier(fdcu_t fdcu) fdc->state = FINDWORK; fdc->fd = (fd_p) 0; fdc->fdu = -1; + /* XXX abort current command, if any. */ return(1); } fdc->retry++; return(1); } -#endif +/* + * fdioctl() from jc@irbs.UUCP (John Capo) + * i386/i386/conf.c needs to have fdioctl() declared and remove the line that + * defines fdioctl to be enxio. + * + * TODO: Reformat. + * Think about allocating buffer off stack. + * Don't pass uncast 0's and NULL's to read/write/setdisklabel(). + * Watch out for NetBSD's different *disklabel() interface. + */ + +int +fdioctl (dev, cmd, addr, flag) +dev_t dev; +int cmd; +caddr_t addr; +int flag; +{ + struct fd_type *fdt; + struct disklabel *dl; + char buffer[DEV_BSIZE]; + int error; + + error = 0; + + switch (cmd) + { + case DIOCGDINFO: + bzero(buffer, sizeof (buffer)); + dl = (struct disklabel *)buffer; + dl->d_secsize = FDBLK; + fdt = fd_data[FDUNIT(minor(dev))].ft; + dl->d_secpercyl = fdt->size / fdt->tracks; + dl->d_type = DTYPE_FLOPPY; + + if (readdisklabel(dev, fdstrategy, dl, NULL, 0, 0) == NULL) + error = 0; + else + error = EINVAL; + + *(struct disklabel *)addr = *dl; + break; + + case DIOCSDINFO: + if ((flag & FWRITE) == 0) + error = EBADF; + + break; + + case DIOCWLABEL: + if ((flag & FWRITE) == 0) + error = EBADF; + + break; + + case DIOCWDINFO: + if ((flag & FWRITE) == 0) + { + error = EBADF; + break; + } + + dl = (struct disklabel *)addr; + + if (error = setdisklabel ((struct disklabel *)buffer, dl, 0, NULL)) + break; + + error = writedisklabel(dev, fdstrategy, (struct disklabel *)buffer, NULL); + break; + + default: + error = EINVAL; + break; + } + return (error); +} + +#endif diff --git a/sys/i386/isa/fdreg.h b/sys/i386/isa/fdreg.h index 948f566dfd..84dbf75055 100644 --- a/sys/i386/isa/fdreg.h +++ b/sys/i386/isa/fdreg.h @@ -30,14 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)fdreg.h 7.1 (Berkeley) 5/9/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00153 - * -------------------- ----- ---------------------- - * - * 20 Apr 93 Julian Elischer Heavily re worked, see notes below + * from: @(#)fdreg.h 7.1 (Berkeley) 5/9/91 + * $Id$ */ /* diff --git a/sys/i386/isa/ic/ds8390.h b/sys/i386/isa/ic/ds8390.h deleted file mode 100644 index 000b453663..0000000000 --- a/sys/i386/isa/ic/ds8390.h +++ /dev/null @@ -1,146 +0,0 @@ -/*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)ds8390.h 7.1 (Berkeley) 5/9/91 - */ - -/* - * Nominal Semidestructor DS8390 Ethernet Chip - * Register and bit definitions - */ - -/* - * Page register offset values - */ -#define ds_cmd 0x00 /* Command register: */ -#define DSCM_STOP 0x01 /* Stop controller */ -#define DSCM_START 0x02 /* Start controller */ -#define DSCM_TRANS 0x04 /* Transmit packet */ -#define DSCM_RREAD 0x08 /* Remote read */ -#define DSCM_RWRITE 0x10 /* Remote write */ -#define DSCM_NODMA 0x20 /* No Remote DMA present */ -#define DSCM_PG0 0x00 /* Select Page 0 */ -#define DSCM_PG1 0x40 /* Select Page 1 */ -#define DSCM_PG2 0x80 /* Select Page 2? */ - -#define ds0_pstart 0x01 /* Page Start register */ -#define ds0_pstop 0x02 /* Page Stop register */ -#define ds0_bnry 0x03 /* Boundary Pointer */ - -#define ds0_tsr 0x04 /* Transmit Status (read-only) */ -#define DSTS_PTX 0x01 /* Successful packet transmit */ -#define DSTS_COLL 0x04 /* Packet transmit w/ collision*/ -#define DSTS_COLL16 0x04 /* Packet had >16 collisions & fail */ -#define DSTS_UND 0x20 /* FIFO Underrun on transmission*/ - -#define ds0_tpsr ds0_tsr /* Transmit Page (write-only) */ -#define ds0_tbcr0 0x05 /* Transmit Byte count, low WO */ -#define ds0_tbcr1 0x06 /* Transmit Byte count, high WO */ - -#define ds0_isr 0x07 /* Interrupt status register */ -#define DSIS_RX 0x01 /* Successful packet reception */ -#define DSIS_TX 0x02 /* Successful packet transmission */ -#define DSIS_RXE 0x04 /* Packet reception w/error */ -#define DSIS_TXE 0x08 /* Packet transmission w/error*/ -#define DSIS_ROVRN 0x10 /* Receiver overrun in the ring*/ -#define DSIS_CTRS 0x20 /* Diagnostic counters need attn */ -#define DSIS_RDC 0x40 /* Remote DMA Complete */ -#define DSIS_RESET 0x80 /* Reset Complete */ - -#define ds0_rsar0 0x08 /* Remote start address low WO */ -#define ds0_rsar1 0x09 /* Remote start address high WO */ -#define ds0_rbcr0 0x0A /* Remote byte count low WO */ -#define ds0_rbcr1 0x0B /* Remote byte count high WO */ - -#define ds0_rsr 0x0C /* Receive status RO */ -#define DSRS_RPC 0x01 /* Received Packet Complete */ - -#define ds0_rcr ds0_rsr /* Receive configuration WO */ -#define DSRC_SEP 0x01 /* Save error packets */ -#define DSRC_AR 0x02 /* Accept Runt packets */ -#define DSRC_AB 0x04 /* Accept Broadcast packets */ -#define DSRC_AM 0x08 /* Accept Multicast packets */ -#define DSRC_PRO 0x10 /* Promiscuous physical */ -#define DSRC_MON 0x20 /* Monitor mode */ - -#define ds0_tcr 0x0D /* Transmit configuration WO */ -#define DSTC_CRC 0x01 /* Inhibit CRC */ -#define DSTC_LB0 0x02 /* Encoded Loopback Control */ -#define DSTC_LB1 0x04 /* Encoded Loopback Control */ -#define DSTC_ATD 0x08 /* Auto Transmit Disable */ -#define DSTC_OFST 0x10 /* Collision Offset Enable */ - -#define ds0_rcvalctr ds0_tcr /* Receive alignment err ctr RO */ - -#define ds0_dcr 0x0E /* Data configuration WO */ -#define DSDC_WTS 0x01 /* Word Transfer Select */ -#define DSDC_BOS 0x02 /* Byte Order Select */ -#define DSDC_LAS 0x04 /* Long Address Select */ -#define DSDC_BMS 0x08 /* Burst Mode Select */ -#define DSDC_AR 0x10 /* Autoinitialize Remote */ -#define DSDC_FT0 0x20 /* Fifo Threshold Select */ -#define DSDC_FT1 0x40 /* Fifo Threshold Select */ - -#define ds0_rcvcrcctr ds0_dcr /* Receive CRC error counter RO */ - -#define ds0_imr 0x0F /* Interrupt mask register WO */ -#define DSIM_PRXE 0x01 /* Packet received enable */ -#define DSIM_PTXE 0x02 /* Packet transmitted enable */ -#define DSIM_RXEE 0x04 /* Receive error enable */ -#define DSIM_TXEE 0x08 /* Transmit error enable */ -#define DSIM_OVWE 0x10 /* Overwrite warning enable */ -#define DSIM_CNTE 0x20 /* Counter overflow enable */ -#define DSIM_RDCE 0x40 /* Dma complete enable */ - -#define ds0_rcvfrmctr ds0_imr /* Receive Frame error cntr RO */ - - -#define ds1_par0 ds0_pstart /* Physical address register 0 */ - /* Physical address registers 1-4 */ -#define ds1_par5 ds0_tbcr1 /* Physical address register 5 */ -#define ds1_curr ds0_isr /* Current page (receive unit) */ -#define ds1_mar0 ds0_rsar0 /* Multicast address register 0 */ - /* Multicast address registers 1-6 */ -#define ds1_mar7 ds0_imr /* Multicast address register 7 */ -#define ds1_curr ds0_isr /* Current page (receive unit) */ - -#define DS_PGSIZE 256 /* Size of RAM pages in bytes */ - -/* - * Packet receive header, 1 per each buffer page used in receive packet - */ -struct prhdr { - u_char pr_status; /* is this a good packet, same as ds0_rsr */ - u_char pr_nxtpg; /* next page of packet or next packet */ - u_char pr_sz0; - u_char pr_sz1; -}; diff --git a/sys/i386/isa/ic/i8042.h b/sys/i386/isa/ic/i8042.h index 4d04ce5b1d..84ee90f08e 100644 --- a/sys/i386/isa/ic/i8042.h +++ b/sys/i386/isa/ic/i8042.h @@ -1,3 +1,7 @@ +/* + * $Id$ + */ + #define KBSTATP 0x64 /* kbd controller status port (I) */ #define KBS_DIB 0x01 /* kbd data in buffer */ #define KBS_IBF 0x02 /* kbd input buffer low */ diff --git a/sys/i386/isa/ic/i8237.h b/sys/i386/isa/ic/i8237.h index dc269f2cfc..2199e732a0 100644 --- a/sys/i386/isa/ic/i8237.h +++ b/sys/i386/isa/ic/i8237.h @@ -1,5 +1,7 @@ /* * Intel 8237 DMA Controller + * + * $Id$ */ #define DMA37MD_SINGLE 0x40 /* single pass mode */ diff --git a/sys/i386/isa/ic/i82586.h b/sys/i386/isa/ic/i82586.h new file mode 100644 index 0000000000..577313d81e --- /dev/null +++ b/sys/i386/isa/ic/i82586.h @@ -0,0 +1,325 @@ +/*- + * Copyright (c) 1992, University of Vermont and State Agricultural College. + * Copyright (c) 1992, Garrett A. Wollman. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * Vermont and State Agricultural College and Garrett A. Wollman. + * 4. Neither the name of the University nor the name of the author + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR AUTHOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +/* + * Intel 82586 Ethernet chip + * Register, bit, and structure definitions. + * + * Written by GAW with reference to the Clarkson Packet Driver code for this + * chip written by Russ Nelson and others. + */ + +struct ie_en_addr { + u_char data[6]; +}; + +/* + * This is the master configuration block. It tells the hardware where all + * the rest of the stuff is. + */ +struct ie_sys_conf_ptr { + u_short mbz; /* must be zero */ + u_char ie_bus_use; /* true if 8-bit only */ + u_char mbz2[5]; /* must be zero */ + caddr_t ie_iscp_ptr; /* 24-bit physaddr of ISCP */ +}; + +/* + * Note that this is wired in hardware; the SCP is always located here, no + * matter what. + */ +#define IE_SCP_ADDR 0xfffff4 + +/* + * The tells the hardware where all the rest of the stuff is, too. + * FIXME: some of these should be re-commented after we figure out their + * REAL function. + */ +struct ie_int_sys_conf_ptr { + u_char ie_busy; /* zeroed after init */ + u_char mbz; + u_short ie_scb_offset; /* 16-bit physaddr of next struct */ + caddr_t ie_base; /* 24-bit physaddr for all 16-bit vars */ +}; + +/* + * This FINALLY tells the hardware what to do and where to put it. + */ +struct ie_sys_ctl_block { + u_short ie_status; /* status word */ + u_short ie_command; /* command word */ + u_short ie_command_list; /* 16-pointer to command block list */ + u_short ie_recv_list; /* 16-pointer to receive frame list */ + u_short ie_err_crc; /* CRC errors */ + u_short ie_err_align; /* Alignment errors */ + u_short ie_err_resource; /* Resource errors */ + u_short ie_err_overrun; /* Overrun errors */ +}; + +/* Command values */ +#define IE_RU_COMMAND 0x0070 /* mask for RU command */ +#define IE_RU_NOP 0 /* for completeness */ +#define IE_RU_START 0x0010 /* start receive unit command */ +#define IE_RU_ENABLE 0x0020 /* enable receiver command */ +#define IE_RU_DISABLE 0x0030 /* disable receiver command */ +#define IE_RU_ABORT 0x0040 /* abort current receive operation */ + +#define IE_CU_COMMAND 0x0700 /* mask for CU command */ +#define IE_CU_NOP 0 /* included for completeness */ +#define IE_CU_START 0x0100 /* do-command command */ +#define IE_CU_RESUME 0x0200 /* resume a suspended cmd list */ +#define IE_CU_STOP 0x0300 /* SUSPEND was already taken */ +#define IE_CU_ABORT 0x0400 /* abort current command */ + +#define IE_ACK_COMMAND 0xf000 /* mask for ACK command */ +#define IE_ACK_CX 0x8000 /* ack IE_ST_DONE */ +#define IE_ACK_FR 0x4000 /* ack IE_ST_RECV */ +#define IE_ACK_CNA 0x2000 /* ack IE_ST_ALLDONE */ +#define IE_ACK_RNR 0x1000 /* ack IE_ST_RNR */ + +#define IE_ACTION_COMMAND(x) (((x) & IE_CU_COMMAND) == IE_CU_START) + /* is this command an action command? */ + +/* Status values */ +#define IE_ST_WHENCE 0xf000 /* mask for cause of interrupt */ +#define IE_ST_DONE 0x8000 /* command with I bit completed */ +#define IE_ST_RECV 0x4000 /* frame received */ +#define IE_ST_ALLDONE 0x2000 /* all commands completed */ +#define IE_ST_RNR 0x1000 /* receive not ready */ + +#define IE_CU_STATUS 0x700 /* mask for command unit status */ +#define IE_CU_ACTIVE 0x200 /* command unit is active */ +#define IE_CU_SUSPEND 0x100 /* command unit is suspended */ + +#define IE_RU_STATUS 0x70 /* mask for receiver unit status */ +#define IE_RU_SUSPEND 0x10 /* receiver is suspended */ +#define IE_RU_NOSPACE 0x20 /* receiver has no resources */ +#define IE_RU_READY 0x40 /* reveiver is ready */ + +/* + * This is filled in partially by the chip, partially by us. + */ +struct ie_recv_frame_desc { + u_short ie_fd_status; /* status for this frame */ + u_short ie_fd_last; /* end of frame list flag */ + u_short ie_fd_next; /* 16-pointer to next RFD */ + u_short ie_fd_buf_desc; /* 16-pointer to list of buffer desc's */ + struct ie_en_addr dest; /* destination ether */ + struct ie_en_addr src; /* source ether */ + u_short ie_length; /* 802 length/Ether type */ + u_short mbz; /* must be zero */ +}; + +#define IE_FD_LAST 0x8000 /* last rfd in list */ +#define IE_FD_SUSP 0x4000 /* suspend RU after receipt */ + +#define IE_FD_COMPLETE 0x8000 /* frame is complete */ +#define IE_FD_BUSY 0x4000 /* frame is busy */ +#define IE_FD_OK 0x2000 /* frame is bad */ +#define IE_FD_RNR 0x0200 /* receiver out of resources here */ + +/* + * linked list of buffers... + */ +struct ie_recv_buf_desc { + u_short ie_rbd_actual; /* status for this buffer */ + u_short ie_rbd_next; /* 16-pointer to next RBD */ + caddr_t ie_rbd_buffer; /* 24-pointer to buffer for this RBD */ + u_short ie_rbd_length; /* length of the buffer */ + u_short mbz; /* must be zero */ +}; + +#define IE_RBD_LAST 0x8000 /* last buffer */ +#define IE_RBD_USED 0x4000 /* this buffer has data */ +/* + * All commands share this in common. + */ +struct ie_cmd_common { + u_short ie_cmd_status; /* status of this command */ + u_short ie_cmd_cmd; /* command word */ + u_short ie_cmd_link; /* link to next command */ +}; + +#define IE_STAT_COMPL 0x8000 /* command is completed */ +#define IE_STAT_BUSY 0x4000 /* command is running now */ +#define IE_STAT_OK 0x2000 /* command completed successfully */ + +#define IE_CMD_NOP 0x0000 /* NOP */ +#define IE_CMD_IASETUP 0x0001 /* initial address setup */ +#define IE_CMD_CONFIG 0x0002 /* configure command */ +#define IE_CMD_MCAST 0x0003 /* multicast setup command */ +#define IE_CMD_XMIT 0x0004 /* transmit command */ +#define IE_CMD_TDR 0x0005 /* time-domain reflectometer command */ +#define IE_CMD_DUMP 0x0006 /* dump command */ +#define IE_CMD_DIAGNOSE 0x0007 /* diagnostics command */ + +#define IE_CMD_LAST 0x8000 /* this is the last command in the list */ +#define IE_CMD_SUSPEND 0x4000 /* suspend CU after this command */ +#define IE_CMD_INTR 0x2000 /* post an interrupt after completion */ + +/* + * This is the command to transmit a frame. + */ +struct ie_xmit_cmd { + struct ie_cmd_common com; /* common part */ +#define ie_xmit_status com.ie_cmd_status + + u_short ie_xmit_desc; /* 16-pointer to buffer descriptor */ + struct ie_en_addr ie_xmit_addr; /* destination address */ + + u_short ie_xmit_length; /* 802.3 length/Ether type field */ +}; + +#define IE_XS_MAXCOLL 0x000f /* number of collisions during transmit */ +#define IE_XS_EXCMAX 0x0020 /* exceeded maximum number of collisions */ +#define IE_XS_SQE 0x0040 /* SQE positive */ +#define IE_XS_DEFERRED 0x0080 /* transmission deferred */ +#define IE_XS_UNDERRUN 0x0100 /* DMA underrun */ +#define IE_XS_LOSTCTS 0x0200 /* Lost CTS */ +#define IE_XS_NOCARRIER 0x0400 /* No Carrier */ +#define IE_XS_LATECOLL 0x0800 /* Late collision */ + +/* + * This is a buffer descriptor for a frame to be transmitted. + */ + +struct ie_xmit_buf { + u_short ie_xmit_flags; /* see below */ + u_short ie_xmit_next; /* 16-pointer to next desc. */ + caddr_t ie_xmit_buf; /* 24-pointer to the actual buffer */ +}; + +#define IE_XMIT_LAST 0x8000 /* this TBD is the last one */ +/* The rest of the `flags' word is actually the length. */ + +/* + * Multicast setup command. + */ + +#define MAXMCAST 50 /* must fit in transmit buffer */ + +struct ie_mcast_cmd { + struct ie_cmd_common com; /* common part */ +#define ie_mcast_status com.ie_cmd_status + + u_short ie_mcast_bytes; /* size (in bytes) of multicast addresses */ + struct ie_en_addr ie_mcast_addrs[MAXMCAST + 1]; /* space for them */ +}; + +/* + * Time Domain Reflectometer command. + */ + +struct ie_tdr_cmd { + struct ie_cmd_common com; /* common part */ +#define ie_tdr_status com.ie_cmd_status + + u_short ie_tdr_time; /* error bits and time */ +}; + +#define IE_TDR_SUCCESS 0x8000 /* TDR succeeded without error */ +#define IE_TDR_XCVR 0x4000 /* detected a transceiver problem */ +#define IE_TDR_OPEN 0x2000 /* detected an open */ +#define IE_TDR_SHORT 0x1000 /* TDR detected a short */ +#define IE_TDR_TIME 0x07ff /* mask for reflection time */ + +/* + * Initial Address Setup command + */ +struct ie_iasetup_cmd { + struct ie_cmd_common com; +#define ie_iasetup_status com.ie_cmd_status + + struct ie_en_addr ie_address; +}; + +/* + * Configuration command + */ +struct ie_config_cmd { + struct ie_cmd_common com; /* common part */ +#define ie_config_status com.ie_cmd_status + + u_char ie_config_count; /* byte count (0x0c) */ + u_char ie_fifo; /* fifo (8) */ + u_char ie_save_bad; /* save bad frames (0x40) */ + u_char ie_addr_len; /* address length (0x2e) (AL-LOC == 1) */ + u_char ie_priority; /* priority and backoff (0x0) */ + u_char ie_ifs; /* inter-frame spacing (0x60) */ + u_char ie_slot_low; /* slot time, LSB (0x0) */ + u_char ie_slot_high; /* slot time, MSN, and retries (0xf2) */ + u_char ie_promisc; /* 1 if promiscuous, else 0 */ + u_char ie_crs_cdt; /* CSMA/CD parameters (0x0) */ + u_char ie_min_len; /* min frame length (0x40) */ + u_char ie_junk; /* stuff for 82596 (0xff) */ +}; + +/* + * Here are a few useful functions. We could have done these as macros, + * but since we have the inline facility, it makes sense to use that + * instead. + */ +inline void +ie_setup_config(volatile struct ie_config_cmd *cmd, + int promiscuous, int manchester) { + cmd->ie_config_count = 0x0c; + cmd->ie_fifo = 8; + cmd->ie_save_bad = 0x40; + cmd->ie_addr_len = 0x2e; + cmd->ie_priority = 0; + cmd->ie_ifs = 0x60; + cmd->ie_slot_low = 0; + cmd->ie_slot_high = 0xf2; + cmd->ie_promisc = !!promiscuous | manchester << 2; + cmd->ie_crs_cdt = 0; + cmd->ie_min_len = 64; + cmd->ie_junk = 0xff; +} + +inline caddr_t +Align(caddr_t ptr) { + unsigned long l = (unsigned long)ptr; + l = (l + 3) & ~3L; + return (caddr_t)l; +} + +inline void +ie_ack(volatile struct ie_sys_ctl_block *scb, + u_int mask, int unit, + void (*ca)(int)) { + scb->ie_command = scb->ie_status & mask; + (*ca)(unit); +} diff --git a/sys/i386/isa/ic/nec765.h b/sys/i386/isa/ic/nec765.h index b84b46e799..1895db7603 100644 --- a/sys/i386/isa/ic/nec765.h +++ b/sys/i386/isa/ic/nec765.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nec765.h 7.1 (Berkeley) 5/9/91 + * from: @(#)nec765.h 7.1 (Berkeley) 5/9/91 + * $Id$ */ /* diff --git a/sys/i386/isa/ic/ns16450.h b/sys/i386/isa/ic/ns16450.h index f059035776..aa6280df57 100644 --- a/sys/i386/isa/ic/ns16450.h +++ b/sys/i386/isa/ic/ns16450.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ns16450.h 7.1 (Berkeley) 5/9/91 + * from: @(#)ns16450.h 7.1 (Berkeley) 5/9/91 + * $Id$ */ /* diff --git a/sys/i386/isa/ic/ns16550.h b/sys/i386/isa/ic/ns16550.h index e20e9aa58a..ff59757a5f 100644 --- a/sys/i386/isa/ic/ns16550.h +++ b/sys/i386/isa/ic/ns16550.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ns16550.h 7.1 (Berkeley) 5/9/91 + * from: @(#)ns16550.h 7.1 (Berkeley) 5/9/91 + * $Id$ */ /* diff --git a/sys/i386/isa/icu.h b/sys/i386/isa/icu.h index 4866d8d0a8..488ad3ef8d 100644 --- a/sys/i386/isa/icu.h +++ b/sys/i386/isa/icu.h @@ -33,14 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)icu.h 5.6 (Berkeley) 5/9/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00158 - * -------------------- ----- ---------------------- - * - * 25 Apr 93 Bruce Evans New fast interrupt code (intr-0.1) + * from: @(#)icu.h 5.6 (Berkeley) 5/9/91 + * $Id$ */ /* diff --git a/sys/i386/isa/icu.s b/sys/i386/isa/icu.s index 1b93c65ae4..22cec5866f 100644 --- a/sys/i386/isa/icu.s +++ b/sys/i386/isa/icu.s @@ -36,28 +36,7 @@ * * @(#)icu.s 7.2 (Berkeley) 5/21/91 * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 5 00167 - * -------------------- ----- ---------------------- - * - * 28 Nov 92 Frank MacLachlan Aligned addresses and data - * on 32bit boundaries. - * 24 Mar 93 Rodney W. Grimes Added interrupt counters for vmstat - * also stray and false intr counters added - * 20 Apr 93 Bruce Evans New npx-0.5 code - * 25 Apr 93 Bruce Evans Support new interrupt code (intr-0.1) - * Rodney W. Grimes Reimplement above patches.. - * 17 May 93 Rodney W. Grimes Redid the interrupt counter stuff - * moved the counters to vectors.s so - * they are next to the name tables. - * 04 Jun 93 Bruce Evans Fixed irq_num vs id_num for multiple - * devices configed on the same irq with - * respect to ipending. Restructured - * not to use BUILD_VECTORS. - * Rodney W. Grimes softsio1 only works if you have sio - * serial driver, added #include sio.h - * and #ifdef NSIO > 0 to protect it. + * $Id$ */ /* @@ -80,7 +59,7 @@ _cpl: .long 0xffff # current priority (all off) .globl _imen _imen: .long 0xffff # interrupt mask enable (all off) -# .globl _highmask +/* .globl _highmask */ _highmask: .long HIGHMASK .globl _ttymask _ttymask: .long 0 @@ -205,20 +184,28 @@ none_to_unpend: je test_ASTs # no net stuff, just temporary AST's FASTSPL_VARMASK(_netmask) DONET(NETISR_RAW, _rawintr, 5) -#ifdef INET + +#ifdef INET DONET(NETISR_IP, _ipintr, 6) -#endif -#ifdef IMP +#endif /* INET */ + +#ifdef IMP DONET(NETISR_IMP, _impintr, 7) -#endif -#ifdef NS +#endif /* IMP */ + +#ifdef NS DONET(NETISR_NS, _nsintr, 8) -#endif +#endif /* NS */ + +#ifdef ISO + DONET(NETISR_ISO, _clnlintr, 9) +#endif /* ISO */ + FASTSPL($0) test_ASTs: btrl $NETISR_SCLK,_netisr jnc test_resched - COUNT_EVENT(_intrcnt_spl, 9) + COUNT_EVENT(_intrcnt_spl, 10) FASTSPL($SOFTCLOCKMASK) /* * Back to an interrupt frame for a moment. @@ -242,11 +229,11 @@ test_resched: testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp) # to non-kernel (i.e., user)? je 2f # nope, leave - COUNT_EVENT(_intrcnt_spl, 10) + COUNT_EVENT(_intrcnt_spl, 11) movl $0,_astpending call _trap 2: - COUNT_EVENT(_intrcnt_spl, 11) + COUNT_EVENT(_intrcnt_spl, 12) popl %es popl %ds popal @@ -260,20 +247,20 @@ test_resched: * -- ipending = active interrupts currently masked by cpl */ - GENSPL(bio, _biomask, 12) - GENSPL(clock, $HIGHMASK, 13) /* splclock == splhigh ex for count */ - GENSPL(high, $HIGHMASK, 14) - GENSPL(imp, _netmask, 15) /* splimp == splnet except for count */ - GENSPL(net, _netmask, 16) - GENSPL(softclock, $SOFTCLOCKMASK, 17) - GENSPL(tty, _ttymask, 18) + GENSPL(bio, _biomask, 13) + GENSPL(clock, $HIGHMASK, 14) /* splclock == splhigh ex for count */ + GENSPL(high, $HIGHMASK, 15) + GENSPL(imp, _netmask, 16) /* splimp == splnet except for count */ + GENSPL(net, _netmask, 17) + GENSPL(softclock, $SOFTCLOCKMASK, 18) + GENSPL(tty, _ttymask, 19) .globl _splnone .globl _spl0 ALIGN_TEXT _splnone: _spl0: - COUNT_EVENT(_intrcnt_spl, 19) + COUNT_EVENT(_intrcnt_spl, 20) in_spl0: movl _cpl,%eax pushl %eax # save old priority @@ -285,10 +272,24 @@ in_spl0: /* * XXX - what about other net intrs? */ - DONET(NETISR_RAW, _rawintr, 20) -#ifdef INET - DONET(NETISR_IP, _ipintr, 21) -#endif + DONET(NETISR_RAW, _rawintr, 21) + +#ifdef INET + DONET(NETISR_IP, _ipintr, 22) +#endif /* INET */ + +#ifdef IMP + DONET(NETISR_IMP, _impintr, 23) +#endif /* IMP */ + +#ifdef NS + DONET(NETISR_NS, _nsintr, 24) +#endif /* NS */ + +#ifdef ISO + DONET(NETISR_ISO, _clnlintr, 25) +#endif /* ISO */ + over_net_stuff_for_spl0: movl $0,_cpl # set new priority SHOW_CPL @@ -301,11 +302,11 @@ over_net_stuff_for_spl0: .globl _splx ALIGN_TEXT _splx: - COUNT_EVENT(_intrcnt_spl, 22) + COUNT_EVENT(_intrcnt_spl, 26) movl 4(%esp),%eax # new priority testl %eax,%eax je in_spl0 # going to "zero level" is special - COUNT_EVENT(_intrcnt_spl, 23) + COUNT_EVENT(_intrcnt_spl, 27) movl _cpl,%edx # save old priority movl %eax,_cpl # set new priority SHOW_CPL @@ -319,7 +320,7 @@ _splx: unpend_V_result_edx: pushl %edx unpend_V: - COUNT_EVENT(_intrcnt_spl, 24) + COUNT_EVENT(_intrcnt_spl, 28) bsfl %eax,%eax btrl %eax,_ipending jnc unpend_V_next diff --git a/sys/i386/isa/if_ec.c b/sys/i386/isa/if_ec.c deleted file mode 100644 index a491529f43..0000000000 --- a/sys/i386/isa/if_ec.c +++ /dev/null @@ -1,777 +0,0 @@ -#include "ec.h" -#if NEC > 0 -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Tim L. Tucker. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - */ -/* - * A driver for the 3Com 3C503 (Etherlink II) ethernet adaptor. - * - * Written by Herb Peyerl (hpeyerl@novatel.cuc.ab.ca) on 04/25/92. - * (This is my first ever device driver and I couldn't have done - * it without the consumption of many "Brock Gummy Bears" so a - * big thanx to the "Brock Candy Company" of Chattanooga TN) - * - * This driver uses the Western Digital 8003 driver for a template - * since the two cards use the DP8390 chip by National. Everything - * is fairly similar except that the nic on the wd8003 appears to - * to see shared memory at 0x0000 and on the 3com, the nic sees it - * at 0x2000. Also, the 3c503 has it's own ASIC for controlling - * things like the IRQ level in software and whether to use the - * onboard xceiver or not. Since the Clarkson drivers do a very - * good rendition of a 3c503, I also scavenged a lot of ideas from - * there. - * - * Kludges: - * Since I couldn't really think of any non-creative way (other than - * using a #define) of configuring the board to use the onboard xceiver, - * I kludged the isa_device->unit to contain this information. Simply - * put, if bit-7 of isa_device->unit is set (>127) then the driver - * configures that unit to onboard-xceiver (BNC) and if <128 it assumes - * AUI. ec_attach informs the user of this on bootup. Also, ec_probe - * repairs this bit after obtaining it's information since I didn't know - * what else within the depths of the kernel would freak out if I left it. - */ -#include "param.h" -#include "mbuf.h" -#include "socket.h" -#include "ioctl.h" -#include "errno.h" -#include "syslog.h" -#include "net/if.h" -#include "net/netisr.h" -#ifdef INET -#include "netinet/in.h" -#include "netinet/in_systm.h" -#include "netinet/in_var.h" -#include "netinet/ip.h" -#include "netinet/if_ether.h" -#endif -#ifdef NS -#include "netns/ns.h" -#include "netns/ns_if.h" -#endif -#include "i386/isa/isa_device.h" -#include "i386/isa/if_ec.h" - -/* - * Ethernet software status per interface. - * - * Each interface is referenced by a network interface structure, - * qe_if, which the routing code uses to locate the interface. - * This structure contains the output queue for the interface, its address, ... - */ -struct ec_softc { - struct arpcom ec_ac; /* Ethernet common part */ -#define ec_if ec_ac.ac_if /* network-visible interface */ -#define ec_addr ec_ac.ac_enaddr /* hardware Ethernet address */ - - u_char ec_flags; /* software state */ -#define EC_RUNNING 0x01 -#define EC_TXBUSY 0x02 - - u_char ec_type; /* interface type code */ - u_short ec_vector; /* interrupt vector */ - short ec_io_ctl_addr; /* i/o bus address, control */ - short ec_io_nic_addr; /* i/o bus address, DS8390 */ - short thick_or_thin; /* thick=0;thin=2 */ - - caddr_t ec_vmem_addr; /* card RAM virtual memory base */ - u_long ec_vmem_size; /* card RAM bytes */ - caddr_t ec_vmem_ring; /* receive ring RAM vaddress */ - caddr_t ec_vmem_end; /* receive ring RAM end */ -} ec_softc[NEC]; - -#define PAGE0 outb(sc->ec_io_nic_addr + EN_CCMD, ENC_NODMA|ENC_PAGE0); -#define PAGE1 outb(sc->ec_io_nic_addr + EN_CCMD, ENC_NODMA|ENC_PAGE1); -static Bdry; - -int ether_output(), - ecprobe(), - ecattach(), - ecintr(), - ec_init(), - ec_ioctl(), - ec_reset(), - ec_watchdog(), - ec_start_output(); - -struct isa_driver ecdriver = { - ecprobe, - ecattach, - "ec", -}; - -ecprobe(is) -struct isa_device *is; -{ - register struct ec_softc *sc = &ec_softc[is->id_unit&127]; - int i, sum; -/* - * Set up the softc structure with card specific info. - * The 3Com asic is at base+0x400 - */ - sc->ec_io_ctl_addr = is->id_iobase + 0x400; - sc->ec_io_nic_addr = is->id_iobase; - sc->ec_vector = is->id_irq; - sc->ec_vmem_addr = (caddr_t)is->id_maddr; - sc->ec_vmem_size = is->id_msize; - sc->ec_vmem_ring = sc->ec_vmem_addr + (EC_PAGE_SIZE * EC_TXBUF_SIZE); - sc->ec_vmem_end = sc->ec_vmem_addr + is->id_msize; -/* - * Now we get the MAC address. Assume thin ethernet unless told otherwise later. - */ - outb(sc->ec_io_ctl_addr + E33G_CNTRL, ECNTRL_RESET|2); /* Toggle reset bit on*/ - DELAY(100); - outb(sc->ec_io_ctl_addr + E33G_CNTRL, 2); /* Toggle reset bit off */ - DELAY(100); - outb(sc->ec_io_ctl_addr + E33G_CNTRL, ECNTRL_SAPROM|2); /* Map SA_PROM */ - for (i=0;iec_addr[i] = inb(sc->ec_io_nic_addr + i); - outb(sc->ec_io_ctl_addr + E33G_CNTRL, 2); /* Disable SA_PROM */ - outb(sc->ec_io_ctl_addr + E33G_GACFR, EGACFR_IRQOFF); /* tcm, rsel, mbs0, nim */ -/* - * Stop the chip just in case. - */ - DELAY(1000); - PAGE0 - outb(sc->ec_io_nic_addr + EN_CCMD, ENC_NODMA|ENC_STOP); - DELAY(1000); - - /* Check cmd reg and fail if not right */ - if ((i=inb(sc->ec_io_nic_addr + EN_CCMD)) != (ENC_NODMA|ENC_STOP)) - return(0); - -/* - * Test the shared memory. - */ - for(i = 0 ; i < sc->ec_vmem_size ; ++i) - sc->ec_vmem_addr[i] = 0x0; - for(sum=0, i=0; iec_vmem_size; ++i) - sum += sc->ec_vmem_addr[i]; - if(sum) - { - printf("ec%d: shared memory error (device conflict?)\n", - is->id_unit); - return(0); - } -/* - * All done. - */ - return(1); -} - -ecattach(is) -struct isa_device *is; -{ - register struct ec_softc *sc = &ec_softc[is->id_unit]; - register struct ifnet *ifp = &sc->ec_if; - -/** - ** Initialize the ASIC in same order as Clarkson driver. - **/ - - -/* - * Point vector pointer registers off into boonies. - */ - outb(sc->ec_io_ctl_addr + E33G_VP2, 0xff); - outb(sc->ec_io_ctl_addr + E33G_VP1, 0xff); - outb(sc->ec_io_ctl_addr + E33G_VP0, 0x0); -/* - * Set up control of shared memory, buffer ring, etc. - */ - outb(sc->ec_io_ctl_addr + E33G_STARTPG, EC_RXBUF_OFFSET); - outb(sc->ec_io_ctl_addr + E33G_STOPPG, EC_RXBUF_END); -/* - * Set up the IRQ and NBURST on the board. - * ( Not sure why we set up NBURST since we don't use DMA.) - * - * Normally we would is->id_irq<<2 but IRQ2 is defined as 0x200 - * in icu.h so it's a special case. - */ - if(is->id_irq == 0x200) - outb(sc->ec_io_ctl_addr + E33G_IDCFR, 0x10); - else - outb(sc->ec_io_ctl_addr + E33G_IDCFR, is->id_irq << 2); - outb(sc->ec_io_ctl_addr + E33G_NBURST, 0x08); /* Set Burst to 8 */ - outb(sc->ec_io_ctl_addr + E33G_DMAAH, 0x20); - outb(sc->ec_io_ctl_addr + E33G_DMAAL, 0x0); -/* - * Fill in the ifnet structure. - */ - ifp->if_unit = is->id_unit; - ifp->if_name = "ec" ; - ifp->if_mtu = ETHERMTU; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS; - ifp->if_init = ec_init; - ifp->if_output = ether_output; - ifp->if_start = ec_start_output; - ifp->if_ioctl = ec_ioctl; - ifp->if_reset = ec_reset; - ifp->if_watchdog = ec_watchdog; -/* - * Attach the interface to something. Have to figure this out later. - */ - if_attach(ifp); -/* - * Weeee.. We get to tell people we exist... - */ - printf(" address %s", ether_sprintf(sc->ec_addr)); -} - -ec_init(unit) -int unit; -{ - register struct ec_softc *sc = &ec_softc[unit]; - register struct ifnet *ifp = &sc->ec_if; - int i, s; - u_short ax, cx; - - Bdry=0; -printf("ecinit"); -/* - * Address not known. - */ - if(ifp->if_addrlist == (struct ifaddr *) 0) - return; - - /* - * XXX (untested) - * select thick (e.g. AUI connector) if LLC0 bit is set - */ - if (ifp->if_flags & IFF_LLC0) - outb(sc->ec_io_ctl_addr + E33G_CNTRL, 0); - else - outb(sc->ec_io_ctl_addr + E33G_CNTRL, 2); - -/* - * Set up the 8390 chip. - * (Use sequence recommended by 3Com. ) - */ - s=splhigh(); - PAGE0 - outb(sc->ec_io_nic_addr + EN_CCMD, ENC_NODMA|ENC_PAGE0|ENC_STOP); - outb(sc->ec_io_nic_addr + EN0_DCFG, ENDCFG_BM8); - outb(sc->ec_io_nic_addr + EN0_RCNTLO, 0x0); - outb(sc->ec_io_nic_addr + EN0_RCNTHI, 0x0); - outb(sc->ec_io_nic_addr + EN0_RXCR, ENRXCR_MON ); - outb(sc->ec_io_nic_addr + EN0_TXCR, 0x02); - outb(sc->ec_io_nic_addr + EN0_BOUNDARY, EC_RXBUF_OFFSET); - outb(sc->ec_io_nic_addr + EN0_TPSR, 0x20); - outb(sc->ec_io_nic_addr + EN0_STARTPG, EC_RXBUF_OFFSET); - outb(sc->ec_io_nic_addr + EN0_STOPPG, EC_RXBUF_END); - outb(sc->ec_io_nic_addr + EN0_ISR, 0xff); - outb(sc->ec_io_nic_addr + EN0_IMR, 0x3f); -/* - * Copy Ethernet address from SA_PROM into 8390 chip registers. - */ - PAGE1 - for(i=0;i<6;i++) - outb(sc->ec_io_nic_addr + EN1_PHYS+i, sc->ec_addr[i]); -/* - * Set multicast filter mask bits in case promiscuous rcv wanted (???) - * (set to 0xff as in if_we.c) - */ - for(i=0;i<8;i++) - outb(sc->ec_io_nic_addr + EN1_MULT+i, 0xff); -/* - * Set current shared page for RX to work on. - */ - outb(sc->ec_io_nic_addr + EN1_CURPAG, EC_RXBUF_OFFSET); -/* - * Start the 8390. Clear Interrupt Status reg, and accept Broadcast - * packets. - */ - outb(sc->ec_io_nic_addr + EN_CCMD, ENC_START|ENC_PAGE0|ENC_NODMA); - outb(sc->ec_io_nic_addr + EN0_ISR, 0xff); - outb(sc->ec_io_nic_addr + EN0_TXCR, 0x0); - outb(sc->ec_io_nic_addr + EN0_RXCR, ENRXCR_BCST); -/* - * Take interface out of reset, program the vector, - * enable interrupts, and tell the world we are up. - */ - ifp->if_flags |= IFF_RUNNING; - outb(sc->ec_io_ctl_addr + E33G_GACFR, EGACFR_NORM); /* tcm, rsel, mbs0 */ - (void) splx(s); - sc->ec_flags &= ~EC_TXBUSY; - ec_start_output(ifp); -} - -ec_start_output(ifp) -struct ifnet *ifp; -{ - register struct ec_softc *sc = &ec_softc[ifp->if_unit]; - struct mbuf *m0, *m; - register caddr_t buffer; - int len, s; - int ec_cmd_reg; - -/* - * The DS8390 only has one transmit buffer, if it is busy we - * must wait until the transmit interrupt completes. - */ - s=splhigh(); - if(sc->ec_flags & EC_TXBUSY) - { - (void) splx(s); - return; - } - IF_DEQUEUE(&sc->ec_if.if_snd, m); - if(m == 0) - { - (void) splx(s); - return; - } - sc->ec_flags |= EC_TXBUSY; - (void) splx(s); - -/* - * Copy the mbuf chain into the transmit buffer - */ - buffer = sc->ec_vmem_addr; - len = 0; - for(m0 = m; m!= 0; m = m->m_next) - { - bcopy(mtod(m, caddr_t), buffer, m->m_len); - buffer += m->m_len; - len += m->m_len; - } - m_freem(m0); - -/* - * Init transmit length registers and set transmit start flag. - */ - s=splhigh(); - len = MAX(len, ETHER_MIN_LEN); - PAGE0 - outb(sc->ec_io_nic_addr + EN0_TCNTLO, len & 0xff); - outb(sc->ec_io_nic_addr + EN0_TCNTHI, len >> 8); - ec_cmd_reg = inb(sc->ec_io_nic_addr + EN_CCMD); - outb(sc->ec_io_nic_addr + EN_CCMD, ec_cmd_reg|ENC_TRANS); - (void) splx(s); -} - - int ec_cmd_reg, ec_sts_reg; -/* - * Interrupt handler. - */ -ecintr(unit) -int unit; -{ - register struct ec_softc *sc = &ec_softc[unit]; - - unit = 0; - -/* - * Get current command register and interrupt status. - * Turn off interrupts while we take care of things. - */ - ec_cmd_reg = inb(sc->ec_io_nic_addr + EN_CCMD); - PAGE0 - ec_sts_reg = inb(sc->ec_io_nic_addr + EN0_ISR); - outb(sc->ec_io_nic_addr + EN0_IMR, 0x0); -loop: - outb(sc->ec_io_nic_addr + EN0_ISR, ec_sts_reg); - /* - * have we lost ourselves somewhere? - */ - if (ec_sts_reg == 0xff) - ec_watchdog(unit); -/* - * Transmit error - */ - if(ec_sts_reg & ENISR_TX_ERR) - { - sc->ec_if.if_collisions += inb(sc->ec_io_nic_addr + EN0_TCNTLO); - ++sc->ec_if.if_oerrors; - } -/* - * Receive Error - */ - if(ec_sts_reg & ENISR_RX_ERR) - { - (void) inb(sc->ec_io_nic_addr + 0xD); - (void) inb(sc->ec_io_nic_addr + 0xE); - (void) inb(sc->ec_io_nic_addr + 0xF); - ++sc->ec_if.if_ierrors; - } -/* - * Normal transmit complete. - */ - if(ec_sts_reg&ENISR_TX || ec_sts_reg&ENISR_TX_ERR) - ectint(unit); -/* - * Normal receive notification - */ - if(ec_sts_reg&(ENISR_RX|ENISR_RX_ERR)) - ecrint(unit); - -/* - * Try to start transmit - */ - ec_start_output(&sc->ec_if); -/* - * Reenable onboard interrupts. - */ - /*PAGE0*/ - outb(sc->ec_io_nic_addr + EN_CCMD, ec_cmd_reg); - outb(sc->ec_io_nic_addr + EN0_IMR, 0x3f); - if(ec_sts_reg=inb(sc->ec_io_nic_addr + EN0_ISR)) - goto loop; -} - -/* - * Transmit interrupt - */ -ectint(unit) -int unit; -{ - register struct ec_softc *sc = &ec_softc[unit]; -/* - * Do some statistics. - */ - PAGE0 - sc->ec_flags &= ~EC_TXBUSY; - sc->ec_if.if_timer = 0; - ++sc->ec_if.if_opackets; - sc->ec_if.if_collisions += inb(sc->ec_io_nic_addr + EN0_TCNTLO); -} - -/* - * Receive interrupt. - * (This gave me the most trouble so excuse the mess.) - */ -ecrint(unit) -int unit; -{ - register struct ec_softc *sc = &ec_softc[unit]; - u_char bnry, curr; - int len; - struct ec_ring *ecr; - - /* - * Traverse the receive ring looking for packets to pass back. - * The search is complete when we find a descriptor not in use. - */ - PAGE0 - bnry = inb(sc->ec_io_nic_addr + EN0_BOUNDARY); - PAGE1 - curr = inb(sc->ec_io_nic_addr + EN1_CURPAG); -if(Bdry) - bnry =Bdry; - - while (bnry != curr) - { - /* get pointer to this buffer header structure */ - ecr = (struct ec_ring *)(sc->ec_vmem_addr + ((bnry-EC_VMEM_OFFSET) << 8)); - - /* count includes CRC */ - len = ecr->ec_count - 4; - /*if (len > 30 && len <= ETHERMTU+100) */ - ecread(sc, (caddr_t)(ecr + 1), len); - /*else printf("reject:%x bnry:%x curr:%x", len, bnry, curr);*/ -outofbufs: - PAGE0 - /* advance on chip Boundry register */ - if((caddr_t) ecr + EC_PAGE_SIZE - 1 > sc->ec_vmem_end) { - bnry = EC_RXBUF_OFFSET; - outb(sc->ec_io_nic_addr + EN0_BOUNDARY, - (sc->ec_vmem_size / EC_PAGE_SIZE) - 1 - EC_VMEM_OFFSET); - } else { - if (len > 30 && len <= ETHERMTU+100) - bnry = ecr->ec_next_packet; - else bnry = curr; - - /* watch out for NIC overflow, reset Boundry if invalid */ - if ((bnry - 1) < EC_RXBUF_OFFSET) { - outb(sc->ec_io_nic_addr + EN0_BOUNDARY, - (sc->ec_vmem_size / EC_PAGE_SIZE) - 1 - EC_VMEM_OFFSET); - bnry = EC_RXBUF_OFFSET; - } else - outb(sc->ec_io_nic_addr + EN0_BOUNDARY, bnry-1); - } - - /* refresh our copy of CURR */ - PAGE1 - curr = inb(sc->ec_io_nic_addr + EN1_CURPAG); - } - Bdry = bnry; - PAGE0 -} - -#define ecdataaddr(sc, eh, off, type) \ - ((type) ((caddr_t)((eh)+1)+(off) >= (sc)->ec_vmem_end) ? \ - (((caddr_t)((eh)+1)+(off))) - (sc)->ec_vmem_end \ - + (sc)->ec_vmem_ring: \ - ((caddr_t)((eh)+1)+(off))) - -ecread(sc, buf, len) -register struct ec_softc *sc; -char *buf; -int len; -{ - register struct ether_header *eh; - struct mbuf *m, *ecget(); - int off, resid; - - /* - * Deal with trailer protocol: if type is trailer type - * get true type from first 16-bit word past data. - * Remember that type was trailer by setting off. - */ - eh = (struct ether_header *)buf; - eh->ether_type = ntohs((u_short)eh->ether_type); - if (eh->ether_type >= ETHERTYPE_TRAIL && - eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { - off = (eh->ether_type - ETHERTYPE_TRAIL) * 512; - if (off >= ETHERMTU) return; /* sanity */ - eh->ether_type = ntohs(*ecdataaddr(sc, eh, off, u_short *)); - resid = ntohs(*(ecdataaddr(sc, eh, off+2, u_short *))); - if (off + resid > len) return; /* sanity */ - len = off + resid; - } else off = 0; - - len -= sizeof(struct ether_header); - if (len <= 0) return; - - /* - * Pull packet off interface. Off is nonzero if packet - * has trailing header; neget will then force this header - * information to be at the front, but we still have to drop - * the type and length which are at the front of any trailer data. - */ - m = ecget(buf, len, off, &sc->ec_if, sc); - if (m == 0) return; - ether_input(&sc->ec_if, eh, m); -} -ec_ioctl(ifp, cmd, data) -register struct ifnet *ifp; -int cmd; -caddr_t data; -{ - register struct ifaddr *ifa = (struct ifaddr *)data; - struct ec_softc *sc = &ec_softc[ifp->if_unit]; - struct ifreq *ifr = (struct ifreq *)data; - int s=splimp(), error=0; - switch(cmd){ - case SIOCSIFADDR: - ifp->if_flags |= IFF_UP; - switch (ifa->ifa_addr->sa_family) { -#ifdef INET - case AF_INET: - ec_init(ifp->if_unit); /* before arpwhohas */ - ((struct arpcom *)ifp)->ac_ipaddr = - IA_SIN(ifa)->sin_addr; - arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); - break; -#endif -#ifdef NS - case AF_NS: - { - register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); - - if (ns_nullhost(*ina)) - ina->x_host = *(union ns_host *)(sc->ns_addr); - else { - /* - * The manual says we can't change the address - * while the receiver is armed, - * so reset everything - */ - ifp->if_flags &= ~IFF_RUNNING; - bcopy((caddr_t)ina->x_host.c_host, - (caddr_t)sc->ns_addr, sizeof(sc->ns_addr)); - } - ec_init(ifp->if_unit); /* does ne_setaddr() */ - break; - } -#endif - default: - ec_init(ifp->if_unit); - break; - } - break; - - case SIOCSIFFLAGS: - if ((ifp->if_flags & IFF_UP) == 0 && - ifp->if_flags & IFF_RUNNING) { - ifp->if_flags &= ~IFF_RUNNING; - ec_stop(ifp->if_unit); - } else if (ifp->if_flags & IFF_UP && - (ifp->if_flags & IFF_RUNNING) == 0) - ec_init(ifp->if_unit); - break; - -#ifdef notdef - case SIOCGHWADDR: - bcopy((caddr_t)sc->sc_addr, (caddr_t) &ifr->ifr_data, - sizeof(sc->sc_addr)); - break; -#endif - - default: - error = EINVAL; - } - splx(s); - return (error); -} - -ec_reset(unit) -int unit; -{ - if(unit >= NEC) - return; - printf("ec%d: reset\n", unit); - ec_init(unit); -} - -ec_watchdog(unit) -int unit; -{ - log(LOG_WARNING, "ec%d: soft reset\n", unit); - ec_stop(unit); - ec_init(unit); -} - -ec_stop(unit) -int unit; -{ - register struct ec_softc *sc = &ec_softc[unit]; - int s; - - s=splimp(); - PAGE0 - outb(sc->ec_io_nic_addr + EN_CCMD, ENC_NODMA|ENC_STOP); - outb(sc->ec_io_nic_addr + EN0_IMR, 0x0); - sc->ec_flags &= ~EC_RUNNING; -/* - * Shutdown the 8390. - */ - (void) splx(s); -} - -/* - * Pull read data off a interface. - * Len is length of data, with local net header stripped. - * Off is non-zero if a trailer protocol was used, and - * gives the offset of the trailer information. - * We copy the trailer information and then all the normal - * data into mbufs. When full cluster sized units are present - * we copy into clusters. - */ -struct mbuf * -ecget(buf, totlen, off0, ifp, sc) - caddr_t buf; - int totlen, off0; - struct ifnet *ifp; - struct ec_softc *sc; -{ - struct mbuf *top, **mp, *m, *p; - int off = off0, len; - register caddr_t cp = buf; - char *epkt; - int tc =totlen; - - buf += sizeof(struct ether_header); - cp = buf; - epkt = cp + totlen; - - if (off) { - cp += off + 2 * sizeof(u_short); - totlen -= 2 * sizeof(u_short); - } - - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == 0) - return (0); - m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.len = totlen; - m->m_len = MHLEN; - - top = 0; - mp = ⊤ - while (totlen > 0) { - if (top) { - MGET(m, M_DONTWAIT, MT_DATA); - if (m == 0) { - m_freem(top); - return (0); - } - m->m_len = MLEN; - } - len = min(totlen, epkt - cp); - if (len >= MINCLSIZE) { - MCLGET(m, M_DONTWAIT); - if (m->m_flags & M_EXT) - m->m_len = len = min(len, MCLBYTES); - else - len = m->m_len; - } else { - /* - * Place initial small packet/header at end of mbuf. - */ - if (len < m->m_len) { - if (top == 0 && len + max_linkhdr <= m->m_len) - m->m_data += max_linkhdr; - m->m_len = len; - } else - len = m->m_len; - } - - totlen -= len; - /* only do up to end of buffer */ - if (cp+len > sc->ec_vmem_end) { - unsigned toend = sc->ec_vmem_end - cp; - - bcopy(cp, mtod(m, caddr_t), toend); - cp = sc->ec_vmem_ring; - bcopy(cp, mtod(m, caddr_t)+toend, len - toend); - cp += len - toend; - epkt = cp + totlen; - } else { - bcopy(cp, mtod(m, caddr_t), (unsigned)len); - cp += len; - } - *mp = m; - mp = &m->m_next; - if (cp == epkt) { - cp = buf; - epkt = cp + tc; - } - } - return (top); -} -#endif /* NEC > 0 */ diff --git a/sys/i386/isa/if_ec.h b/sys/i386/isa/if_ec.h deleted file mode 100644 index 4123bc9011..0000000000 --- a/sys/i386/isa/if_ec.h +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Tim L. Tucker. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * A lot of this was derived from if_wereg.h and 3c503.asm. - */ -/* - * receive ring discriptor - * - * The National Semiconductor DS8390 Network interface controller uses - * the following receive ring headers. The way this works is that the - * memory on the interface card is chopped up into 256 bytes blocks. - * A contiguous portion of those blocks are marked for receive packets - * by setting start and end block #'s in the NIC. For each packet that - * is put into the receive ring, one of these headers (4 bytes each) is - * tacked onto the front. - */ -struct ec_ring { - struct ecr_status { /* received packet status */ - u_char rs_prx:1, /* packet received intack */ - rs_crc:1, /* crc error */ - rs_fae:1, /* frame alignment error */ - rs_fo:1, /* fifo overrun */ - rs_mpa:1, /* packet received intack */ - rs_phy:1, /* packet received intack */ - rs_dis:1, /* packet received intack */ - rs_dfr:1; /* packet received intack */ - } ec_rcv_status; /* received packet status */ - u_char ec_next_packet; /* pointer to next packet */ - u_short ec_count; /* bytes in packet (length + 4) */ -}; - -#define EC_PAGE_SIZE 256 -#define EC_TXBUF_SIZE 0x06 -#define EC_VMEM_OFFSET 0x20 -#define EC_RXBUF_OFFSET 0x26 -#define EC_RXBUF_END 0x40 -#define EC_ROM_OFFSET 8 -#define ETHER_ADDR_LEN 6 -#define ETHER_MIN_LEN 64 -#define ETHER_HDR_SIZE 14 -/* - * Share memory management parameters. - */ -#define XMIT_MTU 0x600 -#define SM_TSTART_PG 0x020 -#define SM_RSTART_PG 0x026 -#define SM_RSTOP_PG 0x040 -/* - * Description of header of each packet in receive area of shared memory. - */ -#define EN_RBUF_STAT 0x0 /* Received frame status. */ -#define EN_RBUF_NXT_PG 0x1 /* Page after this frame */ -#define EN_RBUF_SIZE_LO 0x2 /* Length of this frame */ -#define EN_RBUF_SIZE_HI 0x3 /* Length of this frame */ -#define EN_RBUF_NHDR 0x4 /* Length of above header area */ -/* - * E33 Control registers. (base + 40x) - */ -#define E33G 0x0 -#define E33G_STARTPG 0x0 -#define E33G_STOPPG 0x1 -#define E33G_NBURST 0x2 -#define E33G_IOBASE 0x3 -#define E33G_ROMBASE 0x4 -#define E33G_GACFR 0x5 -#define E33G_CNTRL 0x6 -#define E33G_STATUS 0x7 -#define E33G_IDCFR 0x8 -#define E33G_DMAAH 0x9 -#define E33G_DMAAL 0xa -#define E33G_VP2 0xb -#define E33G_VP1 0xc -#define E33G_VP0 0xd -#define E33G_FIFOH 0xe -#define E33G_FIFOL 0xf -/* - * Bits in E33G_GACFR register. - */ -#define EGACFR_NORM 0x49 -#define EGACFR_IRQOFF 0xc9 -/* - * Control bits for E33G_CNTRL - */ -#define ECNTRL_RESET 0x01 /* Software reset of ASIC and 8390. */ -#define ECNTRL_THIN 0x02 /* Enable thinnet interface. */ -#define ECNTRL_SAPROM 0x04 /* Map Address Prom. */ -#define ECNTRL_DBLBFR 0x20 /* FIFO Configuration bit */ -#define ECNTRL_OUTPUT 0x40 /* PC->3c503 direction if set*/ -#define ECNTRL_START 0x80 /* Start DMA Logic. */ -/* - * Bits in E33G status register. - */ -#define ESTAT_DPRDY 0x80 /* Data port of FIFO ready */ -#define ESTAT_UFLW 0x40 /* Tried to read FIFO when it was empty. */ -#define ESTAT_OFLW 0x20 /* Tried to write FIFO when it was full */ -#define ESTAT_DTC 0x10 /* Terminal count from PC bus DMA Logic */ -#define ESTAT_DIP 0x8 /* DMA in progress */ -/* - * 8390 chip registers. - */ -#define EN_CCMD 0x0 /* Chip's command register. */ -#define EN0_STARTPG 0x1 /* Starting page of ring buffer. */ -#define EN0_STOPPG 0x2 /* Ending page + 1 of ring buffer */ -#define EN0_BOUNDARY 0x3 /* Boundary page of ring buffer */ -#define EN0_TSR 0x4 /* Transmit status register. */ -#define EN0_TPSR 0x4 /* Transmit starting page. */ -#define EN0_TCNTLO 0x5 /* Low byte of tx byte count */ -#define EN0_TCNTHI 0x6 /* High byte of tx byte count */ -#define EN0_ISR 0x7 /* Interrupt status register. */ -#define EN0_RSARLO 0x8 /* Remote start address reg 0 */ -#define EN0_RSARHI 0x9 /* Remote start address reg 1 */ -#define EN0_RCNTLO 0xa /* Remote byte count reg */ -#define EN0_RCNTHI 0xb /* Remote byte count reg */ -#define EN0_RXCR 0xc /* RX Control reg */ -#define EN0_TXCR 0xd /* TX Control reg */ -#define EN0_COUNTER0 0xd /* Rcv alignment error counter */ -#define EN0_DCFG 0xe /* Data configuration reg */ -#define EN0_COUNTER1 0xe /* rcv CRC error counter */ -#define EN0_IMR 0xf /* Interrupt mask reg */ -#define EN0_COUNTER2 0xf /* rcv missed frame error counter */ -#define EN1_PHYS 0x1 /* boards physical enet addr. */ -#define EN1_CURPAG 0x7 /* current memory page. */ -#define EN1_MULT 0x8 /* multicast filter mask array (8 bytes) */ -/* - * Chip commands in EN_CCMD - */ -#define ENC_STOP 0x1 /* Stop the chip. */ -#define ENC_START 0x2 /* Start the chip */ -#define ENC_TRANS 0x4 /* Transmit a frame. */ -#define ENC_RREAD 0x8 /* Remote read. */ -#define ENC_RWRITE 0x10 /* Remote write */ -#define ENC_NODMA 0x20 /* No remote DMA used on this card */ -#define ENC_PAGE0 0x0 /* Select page 0 of chip regs */ -#define ENC_PAGE1 0x40 /* Select page 1 of chip regs */ -/* - * Commands for RX control reg - */ -#define ENRXCR_MON 0x20 /* Monitor mode (no packets rcvd) */ -#define ENRXCR_PROMP 0x10 /* Promiscuous phys addresses. */ -#define ENRXCR_MULTI 0x8 /* Multicast (if pass filter) */ -#define ENRXCR_BCST 0x4 /* Accept broadcasts */ -#define ENRXCR_BAD 0x3 /* Accept runts and bad CRC frames */ -/* - * Commands for TX control reg - */ -#define ENTXCR_LOOP 0x2 /* Set loopback mode */ -/* - * bits on the EN0_DCFG config register. - */ -#define ENDCFG_BM8 0x48 /* Set bust mode, 8 deep FIFO */ -/* - * Bits in the EN0_ISR Interrup Status Register - */ -#define ENISR_RX 0x1 /* receiver, no error */ -#define ENISR_TX 0x2 /* transmitter, no error */ -#define ENISR_RX_ERR 0x4 /* Receiver with error */ -#define ENISR_TX_ERR 0x8 /* Transmitter with error */ -#define ENISR_OVER 0x10 /* receiver overwrote the ring */ -#define ENISR_COUNTERS 0x20 /* Counters need emptying. */ -#define ENISR_RDC 0x40 /* Remote DMA complete. */ -#define ENISR_RESET 0x80 /* Reset completed */ -#define ENISR_ALL 0x3f /* Interrupts we will enable */ -/* - * Bits in received packet status byte and EN0_RSR - */ -#define ENPS_RXOK 0x1 /* received a good packet */ -/* - * Bits in TX status reg. - */ -#define ENTSR_PTX 0x1 /* Packet transmitted without error */ -#define ENTSR_COLL 0x4 /* Collided at least once */ -#define ENTSR_COLL16 0x8 /* Collided 16 times and was dropped */ -#define ENTSR_FU 0x20 /* TX FIFO Underrun */ - diff --git a/sys/i386/isa/if_ed.c b/sys/i386/isa/if_ed.c new file mode 100644 index 0000000000..91eb929231 --- /dev/null +++ b/sys/i386/isa/if_ed.c @@ -0,0 +1,2375 @@ +/* + * Device driver for National Semiconductor DS8390/WD83C690 based ethernet + * adapters. By David Greenman, 29-April-1993 + * + * Copyright (C) 1993, David Greenman. This software may be used, modified, + * copied, distributed, and sold, in both source and binary form provided + * that the above copyright and these terms are retained. Under no + * circumstances is the author responsible for the proper functioning + * of this software, nor does the author assume any responsibility + * for damages incurred with its use. + * + * Currently supports the Western Digital/SMC 8003 and 8013 series, + * the 3Com 3c503, the NE1000 and NE2000, and a variety of similar + * clones. + * + * Thanks to Charles Hannum for proving to me with example code that the + * NE1000/2000 support could be added with minimal impact. Without + * this, I wouldn't have proceeded in this direction. + * + */ + +/* + * $Id: if_ed.c,v 2.11 1993/10/23 04:21:03 davidg Exp davidg $ + */ + +/* + * Modification history + * + * Revision 2.11 1993/10/23 04:21:03 davidg + * Novell probe changed to be invasive because of too many complaints + * about some clone boards not being reset properly and thus not + * found on a warmboot. Yuck. + * + * Revision 2.10 1993/10/23 04:07:12 davidg + * increment output errors if the device times out (done via watchdog) + * + * Revision 2.9 1993/10/23 04:01:45 davidg + * increment input error counter if a packet with a bad length is + * detected. + * + * Revision 2.8 1993/10/15 10:59:56 davidg + * increase maximum time to wait for transmit DMA to complete to 120us. + * call ed_reset() if the time limit is reached instead of trying + * to abort the remote DMA. + * + * Revision 2.7 1993/10/15 10:49:10 davidg + * minor change to way the mbuf pointer temp variable is assigned in + * ed_start (slightly improves code readability) + * + * Revision 2.6 93/10/02 01:12:20 davidg + * use ETHER_ADDR_LEN in NE probe rather than '6'. + * + * Revision 2.5 93/09/30 17:44:14 davidg + * patch from vak@zebub.msk.su (Serge V.Vakulenko) to work around + * a hardware bug in cheap WD clone boards where the PROM checksum + * byte is always zero + * + * Revision 2.4 93/09/29 21:24:30 davidg + * Added software NIC reset in NE probe to work around a problem + * with some NE boards where the 8390 doesn't reset properly on + * power-up. Remove initialization of IMR/ISR in the NE probe + * because this is inherent in the reset. + * + * Revision 2.3 93/09/29 15:10:16 davidg + * credit Charles Hannum + * + * Revision 2.2 93/09/29 13:23:25 davidg + * added no multi-buffer override for 3c503 + * + * Revision 2.1 93/09/29 12:32:12 davidg + * changed multi-buffer count for 16bit 3c503's from 5 to 2 after + * noticing that the transmitter becomes idle because of so many + * packets to load. + * + * Revision 2.0 93/09/29 00:00:19 davidg + * many changes, rewrites, additions, etc. Now supports the + * NE1000, NE2000, WD8003, WD8013, 3C503, 16bit 3C503, and + * a variety of similar clones. 16bit 3c503 now does multi + * transmit buffers. Nearly every part of the driver has + * changed in some way since rev 1.30. + * + * Revision 1.1 93/06/14 22:21:24 davidg + * Beta release of device driver for SMC/WD80x3 and 3C503 ethernet boards. + * + */ + +#include "ed.h" +#if NED > 0 +/* bpfilter included here in case it is needed in future net includes */ +#include "bpfilter.h" + +#include "param.h" +#include "systm.h" +#include "errno.h" +#include "ioctl.h" +#include "mbuf.h" +#include "socket.h" +#include "syslog.h" + +#include "net/if.h" +#include "net/if_dl.h" +#include "net/if_types.h" +#include "net/netisr.h" + +#ifdef INET +#include "netinet/in.h" +#include "netinet/in_systm.h" +#include "netinet/in_var.h" +#include "netinet/ip.h" +#include "netinet/if_ether.h" +#endif + +#ifdef NS +#include "netns/ns.h" +#include "netns/ns_if.h" +#endif + +#if NBPFILTER > 0 +#include "net/bpf.h" +#include "net/bpfdesc.h" +#endif + +#include "i386/isa/isa.h" +#include "i386/isa/isa_device.h" +#include "i386/isa/icu.h" +#include "i386/isa/if_edreg.h" + +#include "i386/include/pio.h" + +/* For backwards compatibility */ +#ifndef IFF_ALTPHYS +#define IFF_ALTPHYS IFF_LLC0 +#endif + +/* + * ed_softc: per line info and status + */ +struct ed_softc { + struct arpcom arpcom; /* ethernet common */ + + char *type_str; /* pointer to type string */ + u_char vendor; /* interface vendor */ + u_char type; /* interface type code */ + + u_short asic_addr; /* ASIC I/O bus address */ + u_short nic_addr; /* NIC (DS8390) I/O bus address */ + +/* + * The following 'proto' variable is part of a work-around for 8013EBT asics + * being write-only. It's sort of a prototype/shadow of the real thing. + */ + u_char wd_laar_proto; + u_char isa16bit; /* width of access to card 0=8 or 1=16 */ + + caddr_t bpf; /* BPF "magic cookie" */ + caddr_t mem_start; /* NIC memory start address */ + caddr_t mem_end; /* NIC memory end address */ + u_long mem_size; /* total NIC memory size */ + caddr_t mem_ring; /* start of RX ring-buffer (in NIC mem) */ + + u_char mem_shared; /* NIC memory is shared with host */ + u_char xmit_busy; /* transmitter is busy */ + u_char txb_cnt; /* number of transmit buffers */ + u_char txb_inuse; /* number of TX buffers currently in-use*/ + + u_char txb_new; /* pointer to where new buffer will be added */ + u_char txb_next_tx; /* pointer to next buffer ready to xmit */ + u_short txb_len[8]; /* buffered xmit buffer lengths */ + u_char tx_page_start; /* first page of TX buffer area */ + u_char rec_page_start; /* first page of RX ring-buffer */ + u_char rec_page_stop; /* last page of RX ring-buffer */ + u_char next_packet; /* pointer to next unread RX packet */ +} ed_softc[NED]; + +int ed_attach(), ed_init(), edintr(), ed_ioctl(), ed_probe(), + ed_start(), ed_reset(), ed_watchdog(); + +static void ed_stop(); + +static inline void ed_rint(); +static inline void ed_xmit(); +static inline char *ed_ring_copy(); + +void ed_pio_readmem(), ed_pio_writemem(); +u_short ed_pio_write_mbufs(); + +extern int ether_output(); + +struct trailer_header { + u_short ether_type; + u_short ether_residual; +}; + +struct isa_driver eddriver = { + ed_probe, + ed_attach, + "ed" +}; +/* + * Interrupt conversion table for WD/SMC ASIC + * (IRQ* are defined in icu.h) + */ +static unsigned short ed_intr_mask[] = { + IRQ9, + IRQ3, + IRQ5, + IRQ7, + IRQ10, + IRQ11, + IRQ15, + IRQ4 +}; + +#define ETHER_MIN_LEN 64 +#define ETHER_MAX_LEN 1518 +#define ETHER_ADDR_LEN 6 +#define ETHER_HDR_SIZE 14 + +/* + * Determine if the device is present + * + * on entry: + * a pointer to an isa_device struct + * on exit: + * NULL if device not found + * or # of i/o addresses used (if found) + */ +int +ed_probe(isa_dev) + struct isa_device *isa_dev; +{ + struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; + int nports; + + if (nports = ed_probe_WD80x3(isa_dev)) + return (nports); + + if (nports = ed_probe_3Com(isa_dev)) + return (nports); + + if (nports = ed_probe_Novell(isa_dev)) + return (nports); +} + +/* + * Generic probe routine for testing for the existance of a DS8390. + * Must be called after the NIC has just been reset. This routine + * works by looking at certain register values that are gauranteed + * to be initialized a certain way after power-up or reset. Seems + * not to currently work on the 83C690. + * + * Specifically: + * + * Register reset bits set bits + * Command Register (CR) TXP, STA RD2, STP + * Interrupt Status (ISR) RST + * Interrupt Mask (IMR) All bits + * Data Control (DCR) LAS + * Transmit Config. (TCR) LB1, LB0 + * + * We only look at the CR and ISR registers, however, because looking at + * the others would require changing register pages (which would be + * intrusive if this isn't an 8390). + * + * Return 1 if 8390 was found, 0 if not. + */ + +int +ed_probe_generic8390(sc) + struct ed_softc *sc; +{ + if ((inb(sc->nic_addr + ED_P0_CR) & + (ED_CR_RD2|ED_CR_TXP|ED_CR_STA|ED_CR_STP)) != + (ED_CR_RD2|ED_CR_STP)) + return (0); + if ((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) != ED_ISR_RST) + return (0); + + return(1); +} + +/* + * Probe and vendor-specific initialization routine for SMC/WD80x3 boards + */ +int +ed_probe_WD80x3(isa_dev) + struct isa_device *isa_dev; +{ + struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; + int i; + u_int memsize; + u_char iptr, isa16bit, sum; + + sc->asic_addr = isa_dev->id_iobase; + sc->nic_addr = sc->asic_addr + ED_WD_NIC_OFFSET; + + /* + * Attempt to do a checksum over the station address PROM. + * If it fails, it's probably not a SMC/WD board. There + * is a problem with this, though: some clone WD boards + * don't pass the checksum test. Danpex boards for one. + */ + for (sum = 0, i = 0; i < 8; ++i) + sum += inb(sc->asic_addr + ED_WD_PROM + i); + + if (sum != ED_WD_ROM_CHECKSUM_TOTAL) { + /* + * Checksum is invalid. This often happens with cheap + * WD8003E clones. In this case, the checksum byte + * (the eighth byte) seems to always be zero. + */ + if (inb(sc->asic_addr + ED_WD_CARD_ID) != ED_TYPE_WD8003E || + inb(sc->asic_addr + ED_WD_PROM + 7) != 0) + return(0); + } + + /* reset card to force it into a known state. */ + outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_RST); + DELAY(100); + outb(sc->asic_addr + ED_WD_MSR, inb(sc->asic_addr + ED_WD_MSR) & ~ED_WD_MSR_RST); + /* wait in the case this card is reading it's EEROM */ + DELAY(5000); + + sc->vendor = ED_VENDOR_WD_SMC; + sc->type = inb(sc->asic_addr + ED_WD_CARD_ID); + + /* + * Set initial values for width/size. + */ + switch (sc->type) { + case ED_TYPE_WD8003S: + sc->type_str = "WD8003S"; + memsize = 8192; + isa16bit = 0; + break; + case ED_TYPE_WD8003E: + sc->type_str = "WD8003E"; + memsize = 8192; + isa16bit = 0; + break; + case ED_TYPE_WD8013EBT: + sc->type_str = "WD8013EBT"; + memsize = 16384; + isa16bit = 1; + break; + case ED_TYPE_WD8013EP: /* also WD8003EP */ + if (inb(sc->asic_addr + ED_WD_ICR) + & ED_WD_ICR_16BIT) { + isa16bit = 1; + memsize = 16384; + sc->type_str = "WD8013EP"; + } else { + isa16bit = 0; + memsize = 8192; + sc->type_str = "WD8003EP"; + } + break; + case ED_TYPE_WD8013WC: + sc->type_str = "WD8013WC"; + memsize = 16384; + isa16bit = 1; + break; + case ED_TYPE_WD8013EBP: + sc->type_str = "WD8013EBP"; + memsize = 16384; + isa16bit = 1; + break; + case ED_TYPE_WD8013EPC: + sc->type_str = "WD8013EPC"; + memsize = 16384; + isa16bit = 1; + break; + default: + sc->type_str = ""; + memsize = 8192; + isa16bit = 0; + break; + } + /* + * Make some adjustments to initial values depending on what is + * found in the ICR. + */ + if (isa16bit && (sc->type != ED_TYPE_WD8013EBT) + && ((inb(sc->asic_addr + ED_WD_ICR) & ED_WD_ICR_16BIT) == 0)) { + isa16bit = 0; + memsize = 8192; + } + +#if ED_DEBUG + printf("type=%s isa16bit=%d memsize=%d id_msize=%d\n", + sc->type_str,isa16bit,memsize,isa_dev->id_msize); + for (i=0; i<8; i++) + printf("%x -> %x\n", i, inb(sc->asic_addr + i)); +#endif + /* + * Allow the user to override the autoconfiguration + */ + if (isa_dev->id_msize) + memsize = isa_dev->id_msize; + /* + * (note that if the user specifies both of the following flags + * that '8bit' mode intentionally has precedence) + */ + if (isa_dev->id_flags & ED_FLAGS_FORCE_16BIT_MODE) + isa16bit = 1; + if (isa_dev->id_flags & ED_FLAGS_FORCE_8BIT_MODE) + isa16bit = 0; + + /* + * Check 83C584 interrupt configuration register if this board has one + * XXX - we could also check the IO address register. But why + * bother...if we get past this, it *has* to be correct. + */ + if (sc->type & ED_WD_SOFTCONFIG) { + /* + * Assemble together the encoded interrupt number. + */ + iptr = (inb(isa_dev->id_iobase + ED_WD_ICR) & ED_WD_ICR_IR2) | + ((inb(isa_dev->id_iobase + ED_WD_IRR) & + (ED_WD_IRR_IR0 | ED_WD_IRR_IR1)) >> 5); + /* + * Translate it using translation table, and check for correctness. + */ + if (ed_intr_mask[iptr] != isa_dev->id_irq) { + printf("ed%d: kernel configured irq %d doesn't match board configured irq %d\n", + isa_dev->id_unit, ffs(isa_dev->id_irq) - 1, ffs(ed_intr_mask[iptr]) - 1); + return(0); + } + /* + * Enable the interrupt. + */ + outb(isa_dev->id_iobase + ED_WD_IRR, + inb(isa_dev->id_iobase + ED_WD_IRR) | ED_WD_IRR_IEN); + } + + sc->isa16bit = isa16bit; + +#ifdef notyet /* XXX - I'm not sure if PIO mode is even possible on WD/SMC boards */ + /* + * The following allows the WD/SMC boards to be used in Programmed I/O + * mode - without mapping the NIC memory shared. ...Not the prefered + * way, but it might be the only way. + */ + if (isa_dev->id_flags & ED_FLAGS_FORCE_PIO) { + sc->mem_shared = 0; + isa_dev->id_maddr = 0; + } else { + sc->mem_shared = 1; + } +#else + sc->mem_shared = 1; +#endif + isa_dev->id_msize = memsize; + + sc->mem_start = (caddr_t)isa_dev->id_maddr; + + /* + * allocate one xmit buffer if < 16k, two buffers otherwise + */ + if ((memsize < 16384) || (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)) { + sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE); + sc->txb_cnt = 1; + sc->rec_page_start = ED_TXBUF_SIZE; + } else { + sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE * 2); + sc->txb_cnt = 2; + sc->rec_page_start = ED_TXBUF_SIZE * 2; + } + sc->mem_size = memsize; + sc->mem_end = sc->mem_start + memsize; + sc->rec_page_stop = memsize / ED_PAGE_SIZE; + sc->tx_page_start = ED_WD_PAGE_OFFSET; + + /* + * Get station address from on-board ROM + */ + for (i = 0; i < ETHER_ADDR_LEN; ++i) + sc->arpcom.ac_enaddr[i] = inb(sc->asic_addr + ED_WD_PROM + i); + + if (sc->mem_shared) { + /* + * Set address and enable interface shared memory. + */ + outb(sc->asic_addr + ED_WD_MSR, ((kvtop(sc->mem_start) >> 13) & + ED_WD_MSR_ADDR) | ED_WD_MSR_MENB); + + /* + * Set upper address bits and 8/16 bit access to shared memory + */ + if (isa16bit) { + outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto = + ED_WD_LAAR_L16EN | ED_WD_LAAR_M16EN | + ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI))); + } else { + if ((sc->type & ED_WD_SOFTCONFIG) || (sc->type == ED_TYPE_WD8013EBT)) { + outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto = + ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI))); + } + } + + /* + * Now zero memory and verify that it is clear + */ + bzero(sc->mem_start, memsize); + + for (i = 0; i < memsize; ++i) + if (sc->mem_start[i]) { + printf("ed%d: failed to clear shared memory at %x - check configuration\n", + isa_dev->id_unit, kvtop(sc->mem_start + i)); + + /* + * Disable 16 bit access to shared memory + */ + if (isa16bit) + outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &= + ~ED_WD_LAAR_M16EN)); + + return(0); + } + + /* + * Disable 16bit access to shared memory - we leave it disabled so + * that 1) machines reboot properly when the board is set + * 16 bit mode and there are conflicting 8bit devices/ROMS + * in the same 128k address space as this boards shared + * memory. and 2) so that other 8 bit devices with shared + * memory can be used in this 128k region, too. + */ + if (isa16bit) + outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &= + ~ED_WD_LAAR_M16EN)); + + } + + return (ED_WD_IO_PORTS); +} + +/* + * Probe and vendor-specific initialization routine for 3Com 3c503 boards + */ +int +ed_probe_3Com(isa_dev) + struct isa_device *isa_dev; +{ + struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; + int i; + u_int memsize; + u_char isa16bit, sum; + + sc->asic_addr = isa_dev->id_iobase + ED_3COM_ASIC_OFFSET; + sc->nic_addr = isa_dev->id_iobase + ED_3COM_NIC_OFFSET; + + /* + * Verify that the kernel configured I/O address matches the board + * configured address + */ + switch (inb(sc->asic_addr + ED_3COM_BCFR)) { + case ED_3COM_BCFR_300: + if (isa_dev->id_iobase != 0x300) + return(0); + break; + case ED_3COM_BCFR_310: + if (isa_dev->id_iobase != 0x310) + return(0); + break; + case ED_3COM_BCFR_330: + if (isa_dev->id_iobase != 0x330) + return(0); + break; + case ED_3COM_BCFR_350: + if (isa_dev->id_iobase != 0x350) + return(0); + break; + case ED_3COM_BCFR_250: + if (isa_dev->id_iobase != 0x250) + return(0); + break; + case ED_3COM_BCFR_280: + if (isa_dev->id_iobase != 0x280) + return(0); + break; + case ED_3COM_BCFR_2A0: + if (isa_dev->id_iobase != 0x2a0) + return(0); + break; + case ED_3COM_BCFR_2E0: + if (isa_dev->id_iobase != 0x2e0) + return(0); + break; + default: + return(0); + } + + /* + * Verify that the kernel shared memory address matches the + * board configured address. + */ + switch (inb(sc->asic_addr + ED_3COM_PCFR)) { + case ED_3COM_PCFR_DC000: + if (kvtop(isa_dev->id_maddr) != 0xdc000) + return(0); + break; + case ED_3COM_PCFR_D8000: + if (kvtop(isa_dev->id_maddr) != 0xd8000) + return(0); + break; + case ED_3COM_PCFR_CC000: + if (kvtop(isa_dev->id_maddr) != 0xcc000) + return(0); + break; + case ED_3COM_PCFR_C8000: + if (kvtop(isa_dev->id_maddr) != 0xc8000) + return(0); + break; + default: + return(0); + } + + + /* + * Reset NIC and ASIC. Enable on-board transceiver throughout reset + * sequence because it'll lock up if the cable isn't connected + * if we don't. + */ + outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_RST | ED_3COM_CR_XSEL); + + /* + * Wait for a while, then un-reset it + */ + DELAY(50); + /* + * The 3Com ASIC defaults to rather strange settings for the CR after + * a reset - it's important to set it again after the following + * outb (this is done when we map the PROM below). + */ + outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); + + /* + * Wait a bit for the NIC to recover from the reset + */ + DELAY(5000); + + sc->vendor = ED_VENDOR_3COM; + sc->type_str = "3c503"; + + sc->mem_shared = 1; + + /* + * Hmmm...a 16bit 3Com board has 16k of memory, but only an 8k + * window to it. + */ + memsize = 8192; + + /* + * Get station address from on-board ROM + */ + /* + * First, map ethernet address PROM over the top of where the NIC + * registers normally appear. + */ + outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_EALO | ED_3COM_CR_XSEL); + + for (i = 0; i < ETHER_ADDR_LEN; ++i) + sc->arpcom.ac_enaddr[i] = inb(sc->nic_addr + i); + + /* + * Unmap PROM - select NIC registers. The proper setting of the + * tranceiver is set in ed_init so that the attach code + * is given a chance to set the default based on a compile-time + * config option + */ + outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); + + /* + * Determine if this is an 8bit or 16bit board + */ + + /* + * select page 0 registers + */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); + + /* + * Attempt to clear WTS bit. If it doesn't clear, then this is a + * 16bit board. + */ + outb(sc->nic_addr + ED_P0_DCR, 0); + + /* + * select page 2 registers + */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_2|ED_CR_RD2|ED_CR_STP); + + /* + * The 3c503 forces the WTS bit to a one if this is a 16bit board + */ + if (inb(sc->nic_addr + ED_P2_DCR) & ED_DCR_WTS) + isa16bit = 1; + else + isa16bit = 0; + + /* + * select page 0 registers + */ + outb(sc->nic_addr + ED_P2_CR, ED_CR_RD2|ED_CR_STP); + + sc->mem_start = (caddr_t)isa_dev->id_maddr; + sc->mem_size = memsize; + sc->mem_end = sc->mem_start + memsize; + + /* + * We have an entire 8k window to put the transmit buffers on the + * 16bit boards. But since the 16bit 3c503's shared memory + * is only fast enough to overlap the loading of one full-size + * packet, trying to load more than 2 buffers can actually + * leave the transmitter idle during the load. So 2 seems + * the best value. (Although a mix of variable-sized packets + * might change this assumption. Nonetheless, we optimize for + * linear transfers of same-size packets.) + */ + if (isa16bit) { + if (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING) + sc->txb_cnt = 1; + else + sc->txb_cnt = 2; + + sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_16BIT; + sc->rec_page_start = ED_3COM_RX_PAGE_OFFSET_16BIT; + sc->rec_page_stop = memsize / ED_PAGE_SIZE + + ED_3COM_RX_PAGE_OFFSET_16BIT; + sc->mem_ring = sc->mem_start; + } else { + sc->txb_cnt = 1; + sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_8BIT; + sc->rec_page_start = ED_TXBUF_SIZE + ED_3COM_TX_PAGE_OFFSET_8BIT; + sc->rec_page_stop = memsize / ED_PAGE_SIZE + + ED_3COM_TX_PAGE_OFFSET_8BIT; + sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE); + } + + sc->isa16bit = isa16bit; + + /* + * Initialize GA page start/stop registers. Probably only needed + * if doing DMA, but what the hell. + */ + outb(sc->asic_addr + ED_3COM_PSTR, sc->rec_page_start); + outb(sc->asic_addr + ED_3COM_PSPR, sc->rec_page_stop); + + /* + * Set IRQ. 3c503 only allows a choice of irq 2-5. + */ + switch (isa_dev->id_irq) { + case IRQ2: + outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ2); + break; + case IRQ3: + outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ3); + break; + case IRQ4: + outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ4); + break; + case IRQ5: + outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ5); + break; + default: + printf("ed%d: Invalid irq configuration (%d) must be 2-5 for 3c503\n", + isa_dev->id_unit, ffs(isa_dev->id_irq) - 1); + return(0); + } + + /* + * Initialize GA configuration register. Set bank and enable shared mem. + */ + outb(sc->asic_addr + ED_3COM_GACFR, ED_3COM_GACFR_RSEL | + ED_3COM_GACFR_MBS0); + + /* + * Initialize "Vector Pointer" registers. These gawd-awful things + * are compared to 20 bits of the address on ISA, and if they + * match, the shared memory is disabled. We set them to + * 0xffff0...allegedly the reset vector. + */ + outb(sc->asic_addr + ED_3COM_VPTR2, 0xff); + outb(sc->asic_addr + ED_3COM_VPTR1, 0xff); + outb(sc->asic_addr + ED_3COM_VPTR0, 0x00); + + /* + * Zero memory and verify that it is clear + */ + bzero(sc->mem_start, memsize); + + for (i = 0; i < memsize; ++i) + if (sc->mem_start[i]) { + printf("ed%d: failed to clear shared memory at %x - check configuration\n", + isa_dev->id_unit, kvtop(sc->mem_start + i)); + return(0); + } + + isa_dev->id_msize = memsize; + return(ED_3COM_IO_PORTS); +} + +/* + * Probe and vendor-specific initialization routine for NE1000/2000 boards + */ +int +ed_probe_Novell(isa_dev) + struct isa_device *isa_dev; +{ + struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; + u_int memsize, n; + u_char romdata[16], isa16bit = 0, tmp; + static char test_pattern[32] = "THIS is A memory TEST pattern"; + char test_buffer[32]; + + sc->asic_addr = isa_dev->id_iobase + ED_NOVELL_ASIC_OFFSET; + sc->nic_addr = isa_dev->id_iobase + ED_NOVELL_NIC_OFFSET; + + /* XXX - do Novell-specific probe here */ + + /* Reset the board */ + tmp = inb(sc->asic_addr + ED_NOVELL_RESET); + +#if 0 + /* + * This total and completely screwy thing is to work around braindamage + * in some NE compatible boards. Why it works, I have *no* idea. + * It appears that the boards watch the ISA bus for an outb, and + * will lock up the ISA bus if they see an inb first. Weird. + */ + outb(0x84, 0); +#endif + + /* + * I don't know if this is necessary; probably cruft leftover from + * Clarkson packet driver code. Doesn't do a thing on the boards + * I've tested. -DG [note that a outb(0x84, 0) seems to work + * here, and is non-invasive...but some boards don't seem to reset + * and I don't have complete documentation on what the 'right' + * thing to do is...so we do the invasive thing for now. Yuck.] + */ + outb(sc->asic_addr + ED_NOVELL_RESET, tmp); + DELAY(5000); + + /* + * This is needed because some NE clones apparently don't reset the + * NIC properly (or the NIC chip doesn't reset fully on power-up) + * XXX - this makes the probe invasive! ...Done against my better + * judgement. -DLG + */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); + + DELAY(5000); + + /* Make sure that we really have an 8390 based board */ + if (!ed_probe_generic8390(sc)) + return(0); + + sc->vendor = ED_VENDOR_NOVELL; + sc->mem_shared = 0; + isa_dev->id_maddr = 0; + + /* + * Test the ability to read and write to the NIC memory. This has + * the side affect of determining if this is an NE1000 or an NE2000. + */ + + /* + * This prevents packets from being stored in the NIC memory when + * the readmem routine turns on the start bit in the CR. + */ + outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON); + + /* Temporarily initialize DCR for byte operations */ + outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_LS); + + outb(sc->nic_addr + ED_P0_PSTART, 8192 / ED_PAGE_SIZE); + outb(sc->nic_addr + ED_P0_PSTOP, 16384 / ED_PAGE_SIZE); + + sc->isa16bit = 0; + + /* + * Write a test pattern in byte mode. If this fails, then there + * probably isn't any memory at 8k - which likely means + * that the board is an NE2000. + */ + ed_pio_writemem(sc, test_pattern, 8192, sizeof(test_pattern)); + ed_pio_readmem(sc, 8192, test_buffer, sizeof(test_pattern)); + + if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) { + /* not an NE1000 - try NE2000 */ + + outb(sc->nic_addr + ED_P0_DCR, + ED_DCR_WTS|ED_DCR_FT1|ED_DCR_LS); + outb(sc->nic_addr + ED_P0_PSTART, 16384 / ED_PAGE_SIZE); + outb(sc->nic_addr + ED_P0_PSTOP, 32768 / ED_PAGE_SIZE); + + sc->isa16bit = 1; + /* + * Write a test pattern in word mode. If this also fails, then + * we don't know what this board is. + */ + ed_pio_writemem(sc, test_pattern, 16384, sizeof(test_pattern)); + ed_pio_readmem(sc, 16384, test_buffer, sizeof(test_pattern)); + + if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) + return(0); /* not an NE2000 either */ + + sc->type = ED_TYPE_NE2000; + sc->type_str = "NE2000"; + } else { + sc->type = ED_TYPE_NE1000; + sc->type_str = "NE1000"; + } + + /* 8k of memory plus an additional 8k if 16bit */ + memsize = 8192 + sc->isa16bit * 8192; + +#if 0 /* probably not useful - NE boards only come two ways */ + /* allow kernel config file overrides */ + if (isa_dev->id_msize) + memsize = isa_dev->id_msize; +#endif + + sc->mem_size = memsize; + + /* NIC memory doesn't start at zero on an NE board */ + /* The start address is tied to the bus width */ + sc->mem_start = (char *) 8192 + sc->isa16bit * 8192; + sc->mem_end = sc->mem_start + memsize; + sc->tx_page_start = memsize / ED_PAGE_SIZE; + + /* + * Use one xmit buffer if < 16k, two buffers otherwise (if not told + * otherwise). + */ + if ((memsize < 16384) || (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)) + sc->txb_cnt = 1; + else + sc->txb_cnt = 2; + + sc->rec_page_start = sc->tx_page_start + sc->txb_cnt * ED_TXBUF_SIZE; + sc->rec_page_stop = sc->tx_page_start + memsize / ED_PAGE_SIZE; + + sc->mem_ring = sc->mem_start + sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE; + + ed_pio_readmem(sc, 0, romdata, 16); + for (n = 0; n < ETHER_ADDR_LEN; n++) + sc->arpcom.ac_enaddr[n] = romdata[n*(sc->isa16bit+1)]; + + /* clear any pending interrupts that might have occurred above */ + outb(sc->nic_addr + ED_P0_ISR, 0xff); + + return(ED_NOVELL_IO_PORTS); +} + +/* + * Install interface into kernel networking data structures + */ +int +ed_attach(isa_dev) + struct isa_device *isa_dev; +{ + struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct ifaddr *ifa; + struct sockaddr_dl *sdl; + + /* + * Set interface to stopped condition (reset) + */ + ed_stop(isa_dev->id_unit); + + /* + * Initialize ifnet structure + */ + ifp->if_unit = isa_dev->id_unit; + ifp->if_name = "ed" ; + ifp->if_mtu = ETHERMTU; + ifp->if_init = ed_init; + ifp->if_output = ether_output; + ifp->if_start = ed_start; + ifp->if_ioctl = ed_ioctl; + ifp->if_reset = ed_reset; + ifp->if_watchdog = ed_watchdog; + + /* + * Set default state for ALTPHYS flag (used to disable the tranceiver + * for AUI operation), based on compile-time config option. + */ + if (isa_dev->id_flags & ED_FLAGS_DISABLE_TRANCEIVER) + ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS + | IFF_ALTPHYS); + else + ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS); + + /* + * Attach the interface + */ + if_attach(ifp); + + /* + * Search down the ifa address list looking for the AF_LINK type entry + */ + ifa = ifp->if_addrlist; + while ((ifa != 0) && (ifa->ifa_addr != 0) && + (ifa->ifa_addr->sa_family != AF_LINK)) + ifa = ifa->ifa_next; + /* + * If we find an AF_LINK type entry we fill in the hardware address. + * This is useful for netstat(1) to keep track of which interface + * is which. + */ + if ((ifa != 0) && (ifa->ifa_addr != 0)) { + /* + * Fill in the link-level address for this interface + */ + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_alen = ETHER_ADDR_LEN; + sdl->sdl_slen = 0; + bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN); + } + + /* + * Print additional info when attached + */ + printf("ed%d: address %s, ", isa_dev->id_unit, + ether_sprintf(sc->arpcom.ac_enaddr)); + + if (sc->type_str && (*sc->type_str != 0)) + printf("type %s ", sc->type_str); + else + printf("type unknown (0x%x) ", sc->type); + + printf("%s ",sc->isa16bit ? "(16 bit)" : "(8 bit)"); + + printf("%s\n", ((sc->vendor == ED_VENDOR_3COM) && + (ifp->if_flags & IFF_ALTPHYS)) ? "tranceiver disabled" : ""); + + /* + * If BPF is in the kernel, call the attach for it + */ +#if NBPFILTER > 0 + bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif + +} + +/* + * Reset interface. + */ +int +ed_reset(unit) + int unit; +{ + int s; + + s = splnet(); + + /* + * Stop interface and re-initialize. + */ + ed_stop(unit); + ed_init(unit); + + (void) splx(s); +} + +/* + * Take interface offline. + */ +void +ed_stop(unit) + int unit; +{ + struct ed_softc *sc = &ed_softc[unit]; + int n = 5000; + + /* + * Stop everything on the interface, and select page 0 registers. + */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); + + /* + * Wait for interface to enter stopped state, but limit # of checks + * to 'n' (about 5ms). It shouldn't even take 5us on modern + * DS8390's, but just in case it's an old one. + */ + while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) == 0) && --n); + +} + +/* + * Device timeout/watchdog routine. Entered if the device neglects to + * generate an interrupt after a transmit has been started on it. + */ +int +ed_watchdog(unit) + int unit; +{ + struct ed_softc *sc = &ed_softc[unit]; + + log(LOG_ERR, "ed%d: device timeout\n", unit); + ++sc->arpcom.ac_if.if_oerrors; + + ed_reset(unit); +} + +/* + * Initialize device. + */ +ed_init(unit) + int unit; +{ + struct ed_softc *sc = &ed_softc[unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; + int i, s; + u_char command; + + + /* address not known */ + if (ifp->if_addrlist == (struct ifaddr *)0) return; + + /* + * Initialize the NIC in the exact order outlined in the NS manual. + * This init procedure is "mandatory"...don't change what or when + * things happen. + */ + s = splnet(); + + /* reset transmitter flags */ + sc->xmit_busy = 0; + sc->arpcom.ac_if.if_timer = 0; + + sc->txb_inuse = 0; + sc->txb_new = 0; + sc->txb_next_tx = 0; + + /* This variable is used below - don't move this assignment */ + sc->next_packet = sc->rec_page_start + 1; + + /* + * Set interface for page 0, Remote DMA complete, Stopped + */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); + + if (sc->isa16bit) { + /* + * Set FIFO threshold to 8, No auto-init Remote DMA, + * byte order=80x86, word-wide DMA xfers, + */ + outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_WTS|ED_DCR_LS); + } else { + /* + * Same as above, but byte-wide DMA xfers + */ + outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_LS); + } + + /* + * Clear Remote Byte Count Registers + */ + outb(sc->nic_addr + ED_P0_RBCR0, 0); + outb(sc->nic_addr + ED_P0_RBCR1, 0); + + /* + * Enable reception of broadcast packets + */ + outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB); + + /* + * Place NIC in internal loopback mode + */ + outb(sc->nic_addr + ED_P0_TCR, ED_TCR_LB0); + + /* + * Initialize transmit/receive (ring-buffer) Page Start + */ + outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start); + outb(sc->nic_addr + ED_P0_PSTART, sc->rec_page_start); + + /* + * Initialize Receiver (ring-buffer) Page Stop and Boundry + */ + outb(sc->nic_addr + ED_P0_PSTOP, sc->rec_page_stop); + outb(sc->nic_addr + ED_P0_BNRY, sc->rec_page_start); + + /* + * Clear all interrupts. A '1' in each bit position clears the + * corresponding flag. + */ + outb(sc->nic_addr + ED_P0_ISR, 0xff); + + /* + * Enable the following interrupts: receive/transmit complete, + * receive/transmit error, and Receiver OverWrite. + * + * Counter overflow and Remote DMA complete are *not* enabled. + */ + outb(sc->nic_addr + ED_P0_IMR, + ED_IMR_PRXE|ED_IMR_PTXE|ED_IMR_RXEE|ED_IMR_TXEE|ED_IMR_OVWE); + + /* + * Program Command Register for page 1 + */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STP); + + /* + * Copy out our station address + */ + for (i = 0; i < ETHER_ADDR_LEN; ++i) + outb(sc->nic_addr + ED_P1_PAR0 + i, sc->arpcom.ac_enaddr[i]); + +#if NBPFILTER > 0 + /* + * Initialize multicast address hashing registers to accept + * all multicasts (only used when in promiscuous mode) + */ + for (i = 0; i < 8; ++i) + outb(sc->nic_addr + ED_P1_MAR0 + i, 0xff); +#endif + + /* + * Set Current Page pointer to next_packet (initialized above) + */ + outb(sc->nic_addr + ED_P1_CURR, sc->next_packet); + + /* + * Set Command Register for page 0, Remote DMA complete, + * and interface Start. + */ + outb(sc->nic_addr + ED_P1_CR, ED_CR_RD2|ED_CR_STA); + + /* + * Take interface out of loopback + */ + outb(sc->nic_addr + ED_P0_TCR, 0); + + /* + * If this is a 3Com board, the tranceiver must be software enabled + * (there is no settable hardware default). + */ + if (sc->vendor == ED_VENDOR_3COM) { + if (ifp->if_flags & IFF_ALTPHYS) { + outb(sc->asic_addr + ED_3COM_CR, 0); + } else { + outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); + } + } + + /* + * Set 'running' flag, and clear output active flag. + */ + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + /* + * ...and attempt to start output + */ + ed_start(ifp); + + (void) splx(s); +} + +/* + * This routine actually starts the transmission on the interface + */ +static inline void ed_xmit(ifp) + struct ifnet *ifp; +{ + struct ed_softc *sc = &ed_softc[ifp->if_unit]; + unsigned short len; + + len = sc->txb_len[sc->txb_next_tx]; + + /* + * Set NIC for page 0 register access + */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + + /* + * Set TX buffer start page + */ + outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start + + sc->txb_next_tx * ED_TXBUF_SIZE); + + /* + * Set TX length + */ + outb(sc->nic_addr + ED_P0_TBCR0, len); + outb(sc->nic_addr + ED_P0_TBCR1, len >> 8); + + /* + * Set page 0, Remote DMA complete, Transmit Packet, and *Start* + */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_TXP|ED_CR_STA); + + sc->xmit_busy = 1; + + /* + * Point to next transmit buffer slot and wrap if necessary. + */ + sc->txb_next_tx++; + if (sc->txb_next_tx == sc->txb_cnt) + sc->txb_next_tx = 0; + + /* + * Set a timer just in case we never hear from the board again + */ + ifp->if_timer = 2; +} + +/* + * Start output on interface. + * We make two assumptions here: + * 1) that the current priority is set to splnet _before_ this code + * is called *and* is returned to the appropriate priority after + * return + * 2) that the IFF_OACTIVE flag is checked before this code is called + * (i.e. that the output part of the interface is idle) + */ +int +ed_start(ifp) + struct ifnet *ifp; +{ + struct ed_softc *sc = &ed_softc[ifp->if_unit]; + struct mbuf *m0, *m; + caddr_t buffer; + int len; + +outloop: + /* + * First, see if there are buffered packets and an idle + * transmitter - should never happen at this point. + */ + if (sc->txb_inuse && (sc->xmit_busy == 0)) { + printf("ed: packets buffers, but transmitter idle\n"); + ed_xmit(ifp); + } + + /* + * See if there is room to put another packet in the buffer. + */ + if (sc->txb_inuse == sc->txb_cnt) { + /* + * No room. Indicate this to the outside world + * and exit. + */ + ifp->if_flags |= IFF_OACTIVE; + return; + } + + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); + if (m == 0) { + /* + * We are using the !OACTIVE flag to indicate to the outside + * world that we can accept an additional packet rather than + * that the transmitter is _actually_ active. Indeed, the + * transmitter may be active, but if we haven't filled all + * the buffers with data then we still want to accept more. + */ + ifp->if_flags &= ~IFF_OACTIVE; + return; + } + + /* + * Copy the mbuf chain into the transmit buffer + */ + + m0 = m; + + /* txb_new points to next open buffer slot */ + buffer = sc->mem_start + (sc->txb_new * ED_TXBUF_SIZE * ED_PAGE_SIZE); + + if (sc->mem_shared) { + /* + * Special case setup for 16 bit boards... + */ + if (sc->isa16bit) { + switch (sc->vendor) { + /* + * For 16bit 3Com boards (which have 16k of memory), + * we have the xmit buffers in a different page + * of memory ('page 0') - so change pages. + */ + case ED_VENDOR_3COM: + outb(sc->asic_addr + ED_3COM_GACFR, + ED_3COM_GACFR_RSEL); + break; + /* + * Enable 16bit access to shared memory on WD/SMC boards + * Don't update wd_laar_proto because we want to restore the + * previous state (because an arp reply in the input code + * may cause a call-back to ed_start) + * XXX - the call-back to 'start' is a bug, IMHO. + */ + case ED_VENDOR_WD_SMC: + outb(sc->asic_addr + ED_WD_LAAR, + (sc->wd_laar_proto | ED_WD_LAAR_M16EN)); + } + } + + for (len = 0; m != 0; m = m->m_next) { + bcopy(mtod(m, caddr_t), buffer, m->m_len); + buffer += m->m_len; + len += m->m_len; + } + + /* + * Restore previous shared memory access + */ + if (sc->isa16bit) { + switch (sc->vendor) { + case ED_VENDOR_3COM: + outb(sc->asic_addr + ED_3COM_GACFR, + ED_3COM_GACFR_RSEL | ED_3COM_GACFR_MBS0); + break; + case ED_VENDOR_WD_SMC: + outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto); + break; + } + } + } else { + len = ed_pio_write_mbufs(sc, m, buffer); + } + + sc->txb_len[sc->txb_new] = MAX(len, ETHER_MIN_LEN); + + sc->txb_inuse++; + + /* + * Point to next buffer slot and wrap if necessary. + */ + sc->txb_new++; + if (sc->txb_new == sc->txb_cnt) + sc->txb_new = 0; + + if (sc->xmit_busy == 0) + ed_xmit(ifp); + /* + * If there is BPF support in the configuration, tap off here. + * The following has support for converting trailer packets + * back to normal. + * XXX - support for trailer packets in BPF should be moved into + * the bpf code proper to avoid code duplication in all of + * the drivers. + */ +#if NBPFILTER > 0 + if (sc->bpf) { + u_short etype; + int off, datasize, resid; + struct ether_header *eh; + struct trailer_header trailer_header; + char ether_packet[ETHER_MAX_LEN]; + char *ep; + + ep = ether_packet; + + /* + * We handle trailers below: + * Copy ether header first, then residual data, + * then data. Put all this in a temporary buffer + * 'ether_packet' and send off to bpf. Since the + * system has generated this packet, we assume + * that all of the offsets in the packet are + * correct; if they're not, the system will almost + * certainly crash in m_copydata. + * We make no assumptions about how the data is + * arranged in the mbuf chain (i.e. how much + * data is in each mbuf, if mbuf clusters are + * used, etc.), which is why we use m_copydata + * to get the ether header rather than assume + * that this is located in the first mbuf. + */ + /* copy ether header */ + m_copydata(m0, 0, sizeof(struct ether_header), ep); + eh = (struct ether_header *) ep; + ep += sizeof(struct ether_header); + etype = ntohs(eh->ether_type); + if (etype >= ETHERTYPE_TRAIL && + etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { + datasize = ((etype - ETHERTYPE_TRAIL) << 9); + off = datasize + sizeof(struct ether_header); + + /* copy trailer_header into a data structure */ + m_copydata(m0, off, sizeof(struct trailer_header), + &trailer_header.ether_type); + + /* copy residual data */ + m_copydata(m0, off+sizeof(struct trailer_header), + resid = ntohs(trailer_header.ether_residual) - + sizeof(struct trailer_header), ep); + ep += resid; + + /* copy data */ + m_copydata(m0, sizeof(struct ether_header), + datasize, ep); + ep += datasize; + + /* restore original ether packet type */ + eh->ether_type = trailer_header.ether_type; + + bpf_tap(sc->bpf, ether_packet, ep - ether_packet); + } else + bpf_mtap(sc->bpf, m0); + } +#endif + + m_freem(m0); + + /* + * Loop back to the top to possibly buffer more packets + */ + goto outloop; +} + +/* + * Ethernet interface receiver interrupt. + */ +static inline void +ed_rint(unit) + int unit; +{ + register struct ed_softc *sc = &ed_softc[unit]; + u_char boundry, current; + u_short len; + struct ed_ring packet_hdr; + char *packet_ptr; + + /* + * Set NIC to page 1 registers to get 'current' pointer + */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STA); + + /* + * 'sc->next_packet' is the logical beginning of the ring-buffer - i.e. + * it points to where new data has been buffered. The 'CURR' + * (current) register points to the logical end of the ring-buffer + * - i.e. it points to where additional new data will be added. + * We loop here until the logical beginning equals the logical + * end (or in other words, until the ring-buffer is empty). + */ + while (sc->next_packet != inb(sc->nic_addr + ED_P1_CURR)) { + + /* get pointer to this buffer's header structure */ + packet_ptr = sc->mem_ring + + (sc->next_packet - sc->rec_page_start) * ED_PAGE_SIZE; + + /* + * The byte count includes the FCS - Frame Check Sequence (a + * 32 bit CRC). + */ + if (sc->mem_shared) + packet_hdr = *(struct ed_ring *)packet_ptr; + else + ed_pio_readmem(sc, packet_ptr, (char *) &packet_hdr, + sizeof(packet_hdr)); + len = packet_hdr.count; + if ((len >= ETHER_MIN_LEN) && (len <= ETHER_MAX_LEN)) { + /* + * Go get packet. len - 4 removes CRC from length. + */ + ed_get_packet(sc, packet_ptr + 4, len - 4); + ++sc->arpcom.ac_if.if_ipackets; + } else { + /* + * Really BAD...probably indicates that the ring pointers + * are corrupted. Also seen on early rev chips under + * high load - the byte order of the length gets switched. + */ + log(LOG_ERR, + "ed%d: NIC memory corrupt - invalid packet length %d\n", + unit, len); + ++sc->arpcom.ac_if.if_ierrors; + ed_reset(unit); + return; + } + + /* + * Update next packet pointer + */ + sc->next_packet = packet_hdr.next_packet; + + /* + * Update NIC boundry pointer - being careful to keep it + * one buffer behind. (as recommended by NS databook) + */ + boundry = sc->next_packet - 1; + if (boundry < sc->rec_page_start) + boundry = sc->rec_page_stop - 1; + + /* + * Set NIC to page 0 registers to update boundry register + */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + + outb(sc->nic_addr + ED_P0_BNRY, boundry); + + /* + * Set NIC to page 1 registers before looping to top (prepare to + * get 'CURR' current pointer) + */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STA); + } +} + +/* + * Ethernet interface interrupt processor + */ +int +edintr(unit) + int unit; +{ + struct ed_softc *sc = &ed_softc[unit]; + u_char isr; + + /* + * Set NIC to page 0 registers + */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + + /* + * loop until there are no more new interrupts + */ + while (isr = inb(sc->nic_addr + ED_P0_ISR)) { + + /* + * reset all the bits that we are 'acknowledging' + * by writing a '1' to each bit position that was set + * (writing a '1' *clears* the bit) + */ + outb(sc->nic_addr + ED_P0_ISR, isr); + + /* + * Handle transmitter interrupts. Handle these first + * because the receiver will reset the board under + * some conditions. + */ + if (isr & (ED_ISR_PTX|ED_ISR_TXE)) { + u_char collisions = inb(sc->nic_addr + ED_P0_NCR); + + /* + * Check for transmit error. If a TX completed with an + * error, we end up throwing the packet away. Really + * the only error that is possible is excessive + * collisions, and in this case it is best to allow the + * automatic mechanisms of TCP to backoff the flow. Of + * course, with UDP we're screwed, but this is expected + * when a network is heavily loaded. + */ + if (isr & ED_ISR_TXE) { + + /* + * Excessive collisions (16) + */ + if ((inb(sc->nic_addr + ED_P0_TSR) & ED_TSR_ABT) + && (collisions == 0)) { + /* + * When collisions total 16, the + * P0_NCR will indicate 0, and the + * TSR_ABT is set. + */ + collisions = 16; + } + + /* + * update output errors counter + */ + ++sc->arpcom.ac_if.if_oerrors; + } else { + /* + * Update total number of successfully + * transmitted packets. + */ + ++sc->arpcom.ac_if.if_opackets; + } + + /* + * reset tx busy and output active flags + */ + sc->xmit_busy = 0; + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + + /* + * clear watchdog timer + */ + sc->arpcom.ac_if.if_timer = 0; + + /* + * Add in total number of collisions on last + * transmission. + */ + sc->arpcom.ac_if.if_collisions += collisions; + + /* + * Decrement buffer in-use count if not zero (can only + * be zero if a transmitter interrupt occured while + * not actually transmitting). + * If data is ready to transmit, start it transmitting, + * otherwise defer until after handling receiver + */ + if (sc->txb_inuse && --sc->txb_inuse) + ed_xmit(&sc->arpcom.ac_if); + } + + /* + * Handle receiver interrupts + */ + if (isr & (ED_ISR_PRX|ED_ISR_RXE|ED_ISR_OVW)) { + /* + * Overwrite warning. In order to make sure that a lockup + * of the local DMA hasn't occurred, we reset and + * re-init the NIC. The NSC manual suggests only a + * partial reset/re-init is necessary - but some + * chips seem to want more. The DMA lockup has been + * seen only with early rev chips - Methinks this + * bug was fixed in later revs. -DG + */ + if (isr & ED_ISR_OVW) { + ++sc->arpcom.ac_if.if_ierrors; +#ifdef DIAGNOSTIC + log(LOG_WARNING, + "ed%d: warning - receiver ring buffer overrun\n", + unit); +#endif + /* + * Stop/reset/re-init NIC + */ + ed_reset(unit); + } else { + + /* + * Receiver Error. One or more of: CRC error, frame + * alignment error FIFO overrun, or missed packet. + */ + if (isr & ED_ISR_RXE) { + ++sc->arpcom.ac_if.if_ierrors; +#ifdef ED_DEBUG + printf("ed%d: receive error %x\n", unit, + inb(sc->nic_addr + ED_P0_RSR)); +#endif + } + + /* + * Go get the packet(s) + * XXX - Doing this on an error is dubious + * because there shouldn't be any data to + * get (we've configured the interface to + * not accept packets with errors). + */ + + /* + * Enable 16bit access to shared memory first + * on WD/SMC boards. + */ + if (sc->isa16bit && + (sc->vendor == ED_VENDOR_WD_SMC)) { + + outb(sc->asic_addr + ED_WD_LAAR, + (sc->wd_laar_proto |= + ED_WD_LAAR_M16EN)); + } + + ed_rint (unit); + + /* disable 16bit access */ + if (sc->isa16bit && + (sc->vendor == ED_VENDOR_WD_SMC)) { + + outb(sc->asic_addr + ED_WD_LAAR, + (sc->wd_laar_proto &= + ~ED_WD_LAAR_M16EN)); + } + } + } + + /* + * If it looks like the transmitter can take more data, + * attempt to start output on the interface. + * This is done after handling the receiver to + * give the receiver priority. + */ + if ((sc->arpcom.ac_if.if_flags & IFF_OACTIVE) == 0) + ed_start(&sc->arpcom.ac_if); + + /* + * return NIC CR to standard state: page 0, remote DMA complete, + * start (toggling the TXP bit off, even if was just set + * in the transmit routine, is *okay* - it is 'edge' + * triggered from low to high) + */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + + /* + * If the Network Talley Counters overflow, read them to + * reset them. It appears that old 8390's won't + * clear the ISR flag otherwise - resulting in an + * infinite loop. + */ + if (isr & ED_ISR_CNT) { + (void) inb(sc->nic_addr + ED_P0_CNTR0); + (void) inb(sc->nic_addr + ED_P0_CNTR1); + (void) inb(sc->nic_addr + ED_P0_CNTR2); + } + } +} + +/* + * Process an ioctl request. This code needs some work - it looks + * pretty ugly. + */ +int +ed_ioctl(ifp, command, data) + register struct ifnet *ifp; + int command; + caddr_t data; +{ + register struct ifaddr *ifa = (struct ifaddr *)data; + struct ed_softc *sc = &ed_softc[ifp->if_unit]; + struct ifreq *ifr = (struct ifreq *)data; + int s, error = 0; + + s = splnet(); + + switch (command) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + ed_init(ifp->if_unit); /* before arpwhohas */ + /* + * See if another station has *our* IP address. + * i.e.: There is an address conflict! If a + * conflict exists, a message is sent to the + * console. + */ + ((struct arpcom *)ifp)->ac_ipaddr = + IA_SIN(ifa)->sin_addr; + arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); + break; +#endif +#ifdef NS + /* + * XXX - This code is probably wrong + */ + case AF_NS: + { + register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + + if (ns_nullhost(*ina)) + ina->x_host = + *(union ns_host *)(sc->arpcom.ac_enaddr); + else { + /* + * + */ + bcopy((caddr_t)ina->x_host.c_host, + (caddr_t)sc->arpcom.ac_enaddr, + sizeof(sc->arpcom.ac_enaddr)); + } + /* + * Set new address + */ + ed_init(ifp->if_unit); + break; + } +#endif + default: + ed_init(ifp->if_unit); + break; + } + break; + + case SIOCSIFFLAGS: + /* + * If interface is marked down and it is running, then stop it + */ + if (((ifp->if_flags & IFF_UP) == 0) && + (ifp->if_flags & IFF_RUNNING)) { + ed_stop(ifp->if_unit); + ifp->if_flags &= ~IFF_RUNNING; + } else { + /* + * If interface is marked up and it is stopped, then start it + */ + if ((ifp->if_flags & IFF_UP) && + ((ifp->if_flags & IFF_RUNNING) == 0)) + ed_init(ifp->if_unit); + } +#if NBPFILTER > 0 + if (ifp->if_flags & IFF_PROMISC) { + /* + * Set promiscuous mode on interface. + * XXX - for multicasts to work, we would need to + * write 1's in all bits of multicast + * hashing array. For now we assume that + * this was done in ed_init(). + */ + outb(sc->nic_addr + ED_P0_RCR, + ED_RCR_PRO|ED_RCR_AM|ED_RCR_AB); + } else { + /* + * XXX - for multicasts to work, we would need to + * rewrite the multicast hashing array with the + * proper hash (would have been destroyed above). + */ + outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB); + } +#endif + /* + * An unfortunate hack to provide the (required) software control + * of the tranceiver for 3Com boards. The ALTPHYS flag disables + * the tranceiver if set. + */ + if (sc->vendor == ED_VENDOR_3COM) { + if (ifp->if_flags & IFF_ALTPHYS) { + outb(sc->asic_addr + ED_3COM_CR, 0); + } else { + outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); + } + } + + break; + + default: + error = EINVAL; + } + (void) splx(s); + return (error); +} + +/* + * Macro to calculate a new address within shared memory when given an offset + * from an address, taking into account ring-wrap. + */ +#define ringoffset(sc, start, off, type) \ + ((type)( ((caddr_t)(start)+(off) >= (sc)->mem_end) ? \ + (((caddr_t)(start)+(off))) - (sc)->mem_end \ + + (sc)->mem_ring: \ + ((caddr_t)(start)+(off)) )) + +/* + * Retreive packet from shared memory and send to the next level up via + * ether_input(). If there is a BPF listener, give a copy to BPF, too. + */ +ed_get_packet(sc, buf, len) + struct ed_softc *sc; + char *buf; + u_short len; +{ + struct ether_header *eh; + struct mbuf *m, *head, *ed_ring_to_mbuf(); + u_short off; + int resid; + u_short etype; + struct trailer_header trailer_header; + + /* Allocate a header mbuf */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + goto bad; + m->m_pkthdr.rcvif = &sc->arpcom.ac_if; + m->m_pkthdr.len = len; + m->m_len = 0; + head = m; + + /* The following sillines is to make NFS happy */ +#define EROUND ((sizeof(struct ether_header) + 3) & ~3) +#define EOFF (EROUND - sizeof(struct ether_header)) + + /* + * The following assumes there is room for + * the ether header in the header mbuf + */ + head->m_data += EOFF; + eh = mtod(head, struct ether_header *); + + if (sc->mem_shared) + bcopy(buf, mtod(head, caddr_t), sizeof(struct ether_header)); + else + ed_pio_readmem(sc, buf, mtod(head, caddr_t), + sizeof(struct ether_header)); + buf += sizeof(struct ether_header); + head->m_len += sizeof(struct ether_header); + len -= sizeof(struct ether_header); + + etype = ntohs((u_short)eh->ether_type); + + /* + * Deal with trailer protocol: + * If trailer protocol, calculate the datasize as 'off', + * which is also the offset to the trailer header. + * Set resid to the amount of packet data following the + * trailer header. + * Finally, copy residual data into mbuf chain. + */ + if (etype >= ETHERTYPE_TRAIL && + etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { + + off = (etype - ETHERTYPE_TRAIL) << 9; + if ((off + sizeof(struct trailer_header)) > len) + goto bad; /* insanity */ + + /* + * If we have shared memory, we can get info directly from the + * stored packet, otherwise we must get a local copy + * of the trailer header using PIO. + */ + if (sc->mem_shared) { + eh->ether_type = *ringoffset(sc, buf, off, u_short *); + resid = ntohs(*ringoffset(sc, buf, off+2, u_short *)); + } else { + struct trailer_header trailer_header; + ed_pio_readmem(sc, + ringoffset(sc, buf, off, caddr_t), + (char *) &trailer_header, + sizeof(trailer_header)); + eh->ether_type = trailer_header.ether_type; + resid = trailer_header.ether_residual; + } + + if ((off + resid) > len) goto bad; /* insanity */ + + resid -= sizeof(struct trailer_header); + if (resid < 0) goto bad; /* insanity */ + + m = ed_ring_to_mbuf(sc, ringoffset(sc, buf, off+4, char *), head, resid); + if (m == 0) goto bad; + + len = off; + head->m_pkthdr.len -= 4; /* subtract trailer header */ + } + + /* + * Pull packet off interface. Or if this was a trailer packet, + * the data portion is appended. + */ + m = ed_ring_to_mbuf(sc, buf, m, len); + if (m == 0) goto bad; + +#if NBPFILTER > 0 + /* + * Check if there's a BPF listener on this interface. + * If so, hand off the raw packet to bpf. + */ + if (sc->bpf) { + bpf_mtap(sc->bpf, head); + + /* + * Note that the interface cannot be in promiscuous mode if + * there are no BPF listeners. And if we are in promiscuous + * mode, we have to check if this packet is really ours. + * + * XXX This test does not support multicasts. + */ + if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && + bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, + sizeof(eh->ether_dhost)) != 0 && + bcmp(eh->ether_dhost, etherbroadcastaddr, + sizeof(eh->ether_dhost)) != 0) { + + m_freem(head); + return; + } + } +#endif + + /* + * Fix up data start offset in mbuf to point past ether header + */ + m_adj(head, sizeof(struct ether_header)); + + /* + * silly ether_input routine needs 'type' in host byte order + */ + eh->ether_type = ntohs(eh->ether_type); + + ether_input(&sc->arpcom.ac_if, eh, head); + return; + +bad: if (head) + m_freem(head); + return; +} + +/* + * Supporting routines + */ + +/* + * Given a NIC memory source address and a host memory destination + * address, copy 'amount' from NIC to host using Programmed I/O. + * The 'amount' is rounded up to a word - okay as long as mbufs + * are word sized. + * This routine is currently Novell-specific. + */ +void +ed_pio_readmem(sc,src,dst,amount) + struct ed_softc *sc; + unsigned short src; + unsigned char *dst; + unsigned short amount; +{ + unsigned short tmp_amount; + + /* select page 0 registers */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + + /* round up to a word */ + tmp_amount = amount; + if (amount & 1) ++amount; + + /* set up DMA byte count */ + outb(sc->nic_addr + ED_P0_RBCR0, amount); + outb(sc->nic_addr + ED_P0_RBCR1, amount>>8); + + /* set up source address in NIC mem */ + outb(sc->nic_addr + ED_P0_RSAR0, src); + outb(sc->nic_addr + ED_P0_RSAR1, src>>8); + + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD0 | ED_CR_STA); + + if (sc->isa16bit) { + insw(sc->asic_addr + ED_NOVELL_DATA, dst, amount/2); + } else + insb(sc->asic_addr + ED_NOVELL_DATA, dst, amount); + +} + +/* + * Stripped down routine for writing a linear buffer to NIC memory. + * Only used in the probe routine to test the memory. 'len' must + * be even. + */ +void +ed_pio_writemem(sc,src,dst,len) + struct ed_softc *sc; + char *src; + unsigned short dst; + unsigned short len; +{ + int maxwait=100; /* about 120us */ + + /* select page 0 registers */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + + /* reset remote DMA complete flag */ + outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); + + /* set up DMA byte count */ + outb(sc->nic_addr + ED_P0_RBCR0, len); + outb(sc->nic_addr + ED_P0_RBCR1, len>>8); + + /* set up destination address in NIC mem */ + outb(sc->nic_addr + ED_P0_RSAR0, dst); + outb(sc->nic_addr + ED_P0_RSAR1, dst>>8); + + /* set remote DMA write */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); + + if (sc->isa16bit) + outsw(sc->asic_addr + ED_NOVELL_DATA, src, len/2); + else + outsb(sc->asic_addr + ED_NOVELL_DATA, src, len); + /* + * Wait for remote DMA complete. This is necessary because on the + * transmit side, data is handled internally by the NIC in bursts + * and we can't start another remote DMA until this one completes. + * Not waiting causes really bad things to happen - like the NIC + * irrecoverably jamming the ISA bus. + */ + while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); +} + +/* + * Write an mbuf chain to the destination NIC memory address using + * programmed I/O. + */ +u_short +ed_pio_write_mbufs(sc,m,dst) + struct ed_softc *sc; + struct mbuf *m; + unsigned short dst; +{ + unsigned short len, mb_offset; + struct mbuf *mp; + unsigned char residual[2]; + int maxwait=100; /* about 120us */ + + /* First, count up the total number of bytes to copy */ + for (len = 0, mp = m; mp; mp = mp->m_next) + len += mp->m_len; + + /* select page 0 registers */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + + /* reset remote DMA complete flag */ + outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); + + /* set up DMA byte count */ + outb(sc->nic_addr + ED_P0_RBCR0, len); + outb(sc->nic_addr + ED_P0_RBCR1, len>>8); + + /* set up destination address in NIC mem */ + outb(sc->nic_addr + ED_P0_RSAR0, dst); + outb(sc->nic_addr + ED_P0_RSAR1, dst>>8); + + /* set remote DMA write */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); + + mb_offset = 0; + /* + * Transfer the mbuf chain to the NIC memory. + * The following code isn't too pretty. The problem is that we can only + * transfer words to the board, and if an mbuf has an odd number + * of bytes in it, this is a problem. It's not a simple matter of + * just removing a byte from the next mbuf (adjusting data++ and + * len--) because this will hose-over the mbuf chain which might + * be needed later for BPF. Instead, we maintain an offset + * (mb_offset) which let's us skip over the first byte in the + * following mbuf. + */ + while (m) { + if (m->m_len - mb_offset) { + if (sc->isa16bit) { + if ((m->m_len - mb_offset) > 1) + outsw(sc->asic_addr + ED_NOVELL_DATA, + mtod(m, caddr_t) + mb_offset, + (m->m_len - mb_offset) / 2); + + /* + * if odd number of bytes, get the odd byte from + * the next mbuf with data + */ + if ((m->m_len - mb_offset) & 1) { + /* first the last byte in current mbuf */ + residual[0] = *(mtod(m, caddr_t) + + m->m_len - 1); + + /* advance past any empty mbufs */ + while (m->m_next && (m->m_next->m_len == 0)) + m = m->m_next; + + if (m->m_next) { + /* remove first byte in next mbuf */ + residual[1] = *(mtod(m->m_next, caddr_t)); + mb_offset = 1; + } + + outw(sc->asic_addr + ED_NOVELL_DATA, + *((unsigned short *) residual)); + } else + mb_offset = 0; + } else + outsb(sc->asic_addr + ED_NOVELL_DATA, m->m_data, m->m_len); + + } + m = m->m_next; + } + + /* + * Wait for remote DMA complete. This is necessary because on the + * transmit side, data is handled internally by the NIC in bursts + * and we can't start another remote DMA until this one completes. + * Not waiting causes really bad things to happen - like the NIC + * irrecoverably jamming the ISA bus. + */ + while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); + + if (!maxwait) { + log(LOG_WARNING, "ed%d: remote transmit DMA failed to complete\n", + sc->arpcom.ac_if.if_unit); + ed_reset(sc->arpcom.ac_if.if_unit); + } + + return(len); +} + +/* + * Given a source and destination address, copy 'amount' of a packet from + * the ring buffer into a linear destination buffer. Takes into account + * ring-wrap. + */ +static inline char * +ed_ring_copy(sc,src,dst,amount) + struct ed_softc *sc; + char *src; + char *dst; + u_short amount; +{ + u_short tmp_amount; + + /* does copy wrap to lower addr in ring buffer? */ + if (src + amount > sc->mem_end) { + tmp_amount = sc->mem_end - src; + + /* copy amount up to end of NIC memory */ + if (sc->mem_shared) + bcopy(src,dst,tmp_amount); + else + ed_pio_readmem(sc,src,dst,tmp_amount); + + amount -= tmp_amount; + src = sc->mem_ring; + dst += tmp_amount; + } + + if (sc->mem_shared) + bcopy(src, dst, amount); + else + ed_pio_readmem(sc, src, dst, amount); + + return(src + amount); +} + +/* + * Copy data from receive buffer to end of mbuf chain + * allocate additional mbufs as needed. return pointer + * to last mbuf in chain. + * sc = ed info (softc) + * src = pointer in ed ring buffer + * dst = pointer to last mbuf in mbuf chain to copy to + * amount = amount of data to copy + */ +struct mbuf * +ed_ring_to_mbuf(sc,src,dst,total_len) + struct ed_softc *sc; + char *src; + struct mbuf *dst; + u_short total_len; +{ + register struct mbuf *m = dst; + + while (total_len) { + register u_short amount = min(total_len, M_TRAILINGSPACE(m)); + + if (amount == 0) { /* no more data in this mbuf, alloc another */ + /* + * If there is enough data for an mbuf cluster, attempt + * to allocate one of those, otherwise, a regular + * mbuf will do. + * Note that a regular mbuf is always required, even if + * we get a cluster - getting a cluster does not + * allocate any mbufs, and one is needed to assign + * the cluster to. The mbuf that has a cluster + * extension can not be used to contain data - only + * the cluster can contain data. + */ + dst = m; + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) + return (0); + + if (total_len >= MINCLSIZE) + MCLGET(m, M_DONTWAIT); + + m->m_len = 0; + dst->m_next = m; + amount = min(total_len, M_TRAILINGSPACE(m)); + } + + src = ed_ring_copy(sc, src, mtod(m, caddr_t) + m->m_len, amount); + + m->m_len += amount; + total_len -= amount; + + } + return (m); +} +#endif diff --git a/sys/i386/isa/if_edreg.h b/sys/i386/isa/if_edreg.h new file mode 100644 index 0000000000..9b7a43f431 --- /dev/null +++ b/sys/i386/isa/if_edreg.h @@ -0,0 +1,903 @@ +/* + * National Semiconductor DS8390 NIC register definitions + * + * $Id: if_edreg.h,v 2.0 93/09/29 00:37:15 davidg Exp Locker: davidg $ + * + * Modification history + * + * $Log: if_edreg.h,v $ + * Revision 2.0 93/09/29 00:37:15 davidg + * changed double buffering flag to multi buffering + * made changes/additions for 3c503 multi-buffering + * ...companion to Rev. 2.0 of 'ed' driver. + * + * Revision 1.6 93/09/28 17:20:03 davidg + * first cut at PIO (e.g. NE1000/2000) support + * + * Revision 1.5 93/08/25 20:38:34 davidg + * added define for card type WD8013WC (10BaseT) + * + * Revision 1.4 93/08/14 20:07:55 davidg + * fix board type definition for 8013EP + * + * Revision 1.3 93/07/20 15:25:25 davidg + * added config flags for forcing 8/16bit mode and disabling double + * xmit buffers. + * + * Revision 1.2 93/06/23 03:03:05 davidg + * added some additional definitions for the 83C584 bus interface + * chip (SMC/WD boards) + * + * Revision 1.1 93/06/23 03:01:07 davidg + * Initial revision + * + */ + +/* + * Page 0 register offsets + */ +#define ED_P0_CR 0x00 /* Command Register */ + +#define ED_P0_CLDA0 0x01 /* Current Local DMA Addr low (read) */ +#define ED_P0_PSTART 0x01 /* Page Start register (write) */ + +#define ED_P0_CLDA1 0x02 /* Current Local DMA Addr high (read) */ +#define ED_P0_PSTOP 0x02 /* Page Stop register (write) */ + +#define ED_P0_BNRY 0x03 /* Boundary Pointer */ + +#define ED_P0_TSR 0x04 /* Transmit Status Register (read) */ +#define ED_P0_TPSR 0x04 /* Transmit Page Start (write) */ + +#define ED_P0_NCR 0x05 /* Number of Collisions Reg (read) */ +#define ED_P0_TBCR0 0x05 /* Transmit Byte count, low (write) */ + +#define ED_P0_FIFO 0x06 /* FIFO register (read) */ +#define ED_P0_TBCR1 0x06 /* Transmit Byte count, high (write) */ + +#define ED_P0_ISR 0x07 /* Interrupt Status Register */ + +#define ED_P0_CRDA0 0x08 /* Current Remote DMA Addr low (read) */ +#define ED_P0_RSAR0 0x08 /* Remote Start Address low (write) */ + +#define ED_P0_CRDA1 0x09 /* Current Remote DMA Addr high (read) */ +#define ED_P0_RSAR1 0x09 /* Remote Start Address high (write) */ + +#define ED_P0_RBCR0 0x0a /* Remote Byte Count low (write) */ + +#define ED_P0_RBCR1 0x0b /* Remote Byte Count high (write) */ + +#define ED_P0_RSR 0x0c /* Receive Status (read) */ +#define ED_P0_RCR 0x0c /* Receive Configuration Reg (write) */ + +#define ED_P0_CNTR0 0x0d /* frame alignment error counter (read) */ +#define ED_P0_TCR 0x0d /* Transmit Configuration Reg (write) */ + +#define ED_P0_CNTR1 0x0e /* CRC error counter (read) */ +#define ED_P0_DCR 0x0e /* Data Configuration Reg (write) */ + +#define ED_P0_CNTR2 0x0f /* missed packet counter (read) */ +#define ED_P0_IMR 0x0f /* Interrupt Mask Register (write) */ + +/* + * Page 1 register offsets + */ +#define ED_P1_CR 0x00 /* Command Register */ +#define ED_P1_PAR0 0x01 /* Physical Address Register 0 */ +#define ED_P1_PAR1 0x02 /* Physical Address Register 1 */ +#define ED_P1_PAR2 0x03 /* Physical Address Register 2 */ +#define ED_P1_PAR3 0x04 /* Physical Address Register 3 */ +#define ED_P1_PAR4 0x05 /* Physical Address Register 4 */ +#define ED_P1_PAR5 0x06 /* Physical Address Register 5 */ +#define ED_P1_CURR 0x07 /* Current RX ring-buffer page */ +#define ED_P1_MAR0 0x08 /* Multicast Address Register 0 */ +#define ED_P1_MAR1 0x09 /* Multicast Address Register 1 */ +#define ED_P1_MAR2 0x0a /* Multicast Address Register 2 */ +#define ED_P1_MAR3 0x0b /* Multicast Address Register 3 */ +#define ED_P1_MAR4 0x0c /* Multicast Address Register 4 */ +#define ED_P1_MAR5 0x0d /* Multicast Address Register 5 */ +#define ED_P1_MAR6 0x0e /* Multicast Address Register 6 */ +#define ED_P1_MAR7 0x0f /* Multicast Address Register 7 */ + +/* + * Page 2 register offsets + */ +#define ED_P2_CR 0x00 /* Command Register */ +#define ED_P2_PSTART 0x01 /* Page Start (read) */ +#define ED_P2_CLDA0 0x01 /* Current Local DMA Addr 0 (write) */ +#define ED_P2_PSTOP 0x02 /* Page Stop (read) */ +#define ED_P2_CLDA1 0x02 /* Current Local DMA Addr 1 (write) */ +#define ED_P2_RNPP 0x03 /* Remote Next Packet Pointer */ +#define ED_P2_TPSR 0x04 /* Transmit Page Start (read) */ +#define ED_P2_LNPP 0x05 /* Local Next Packet Pointer */ +#define ED_P2_ACU 0x06 /* Address Counter Upper */ +#define ED_P2_ACL 0x07 /* Address Counter Lower */ +#define ED_P2_RCR 0x0c /* Receive Configuration Register (read) */ +#define ED_P2_TCR 0x0d /* Transmit Configuration Register (read) */ +#define ED_P2_DCR 0x0e /* Data Configuration Register (read) */ +#define ED_P2_IMR 0x0f /* Interrupt Mask Register (read) */ + +/* + * Command Register (CR) definitions + */ + +/* + * STP: SToP. Software reset command. Takes the controller offline. No + * packets will be received or transmitted. Any reception or + * transmission in progress will continue to completion before + * entering reset state. To exit this state, the STP bit must + * reset and the STA bit must be set. The software reset has + * executed only when indicated by the RST bit in the ISR being + * set. + */ +#define ED_CR_STP 0x01 + +/* + * STA: STArt. This bit is used to activate the NIC after either power-up, + * or when the NIC has been put in reset mode by software command + * or error. + */ +#define ED_CR_STA 0x02 + +/* + * TXP: Transmit Packet. This bit must be set to indicate transmission of + * a packet. TXP is internally reset either after the transmission is + * completed or aborted. This bit should be set only after the Transmit + * Byte Count and Transmit Page Start register have been programmed. + */ +#define ED_CR_TXP 0x04 + +/* + * RD0, RD1, RD2: Remote DMA Command. These three bits control the operation + * of the remote DMA channel. RD2 can be set to abort any remote DMA + * command in progress. The Remote Byte Count registers should be cleared + * when a remote DMA has been aborted. The Remote Start Addresses are not + * restored to the starting address if the remote DMA is aborted. + * + * RD2 RD1 RD0 function + * 0 0 0 not allowed + * 0 0 1 remote read + * 0 1 0 remote write + * 0 1 1 send packet + * 1 X X abort + */ +#define ED_CR_RD0 0x08 +#define ED_CR_RD1 0x10 +#define ED_CR_RD2 0x20 + +/* + * PS0, PS1: Page Select. The two bits select which register set or 'page' to + * access. + * + * PS1 PS0 page + * 0 0 0 + * 0 1 1 + * 1 0 2 + * 1 1 reserved + */ +#define ED_CR_PS0 0x40 +#define ED_CR_PS1 0x80 +/* bit encoded aliases */ +#define ED_CR_PAGE_0 0x00 /* (for consistency) */ +#define ED_CR_PAGE_1 0x40 +#define ED_CR_PAGE_2 0x80 + +/* + * Interrupt Status Register (ISR) definitions + */ + +/* + * PRX: Packet Received. Indicates packet received with no errors. + */ +#define ED_ISR_PRX 0x01 + +/* + * PTX: Packet Transmitted. Indicates packet transmitted with no errors. + */ +#define ED_ISR_PTX 0x02 + +/* + * RXE: Receive Error. Indicates that a packet was received with one or more + * the following errors: CRC error, frame alignment error, FIFO overrun, + * missed packet. + */ +#define ED_ISR_RXE 0x04 + +/* + * TXE: Transmission Error. Indicates that an attempt to transmit a packet + * resulted in one or more of the following errors: excessive + * collisions, FIFO underrun. + */ +#define ED_ISR_TXE 0x08 + +/* + * OVW: OverWrite. Indicates a receive ring-buffer overrun. Incoming network + * would exceed (has exceeded?) the boundry pointer, resulting in data + * that was previously received and not yet read from the buffer to be + * overwritten. + */ +#define ED_ISR_OVW 0x10 + +/* + * CNT: Counter Overflow. Set when the MSB of one or more of the Network Talley + * Counters has been set. + */ +#define ED_ISR_CNT 0x20 + +/* + * RDC: Remote Data Complete. Indicates that a Remote DMA operation has completed. + */ +#define ED_ISR_RDC 0x40 + +/* + * RST: Reset status. Set when the NIC enters the reset state and cleared when a + * Start Command is issued to the CR. This bit is also set when a receive + * ring-buffer overrun (OverWrite) occurs and is cleared when one or more + * packets have been removed from the ring. This is a read-only bit. + */ +#define ED_ISR_RST 0x80 + +/* + * Interrupt Mask Register (IMR) definitions + */ + +/* + * PRXE: Packet Received interrupt Enable. If set, a received packet will cause + * an interrupt. + */ +#define ED_IMR_PRXE 0x01 + +/* + * PTXE: Packet Transmit interrupt Enable. If set, an interrupt is generated when + * a packet transmission completes. + */ +#define ED_IMR_PTXE 0x02 + +/* + * RXEE: Receive Error interrupt Enable. If set, an interrupt will occur whenever a + * packet is received with an error. + */ +#define ED_IMR_RXEE 0x04 + +/* + * TXEE: Transmit Error interrupt Enable. If set, an interrupt will occur whenever + * a transmission results in an error. + */ +#define ED_IMR_TXEE 0x08 + +/* + * OVWE: OverWrite error interrupt Enable. If set, an interrupt is generated whenever + * the receive ring-buffer is overrun. i.e. when the boundry pointer is exceeded. + */ +#define ED_IMR_OVWE 0x10 + +/* + * CNTE: Counter overflow interrupt Enable. If set, an interrupt is generated whenever + * the MSB of one or more of the Network Statistics counters has been set. + */ +#define ED_IMR_CNTE 0x20 + +/* + * RDCE: Remote DMA Complete interrupt Enable. If set, an interrupt is generated + * when a remote DMA transfer has completed. + */ +#define ED_IMR_RDCE 0x40 + +/* + * bit 7 is unused/reserved + */ + +/* + * Data Configuration Register (DCR) definitions + */ + +/* + * WTS: Word Transfer Select. WTS establishes byte or word transfers for + * both remote and local DMA transfers + */ +#define ED_DCR_WTS 0x01 + +/* + * BOS: Byte Order Select. BOS sets the byte order for the host. + * Should be 0 for 80x86, and 1 for 68000 series processors + */ +#define ED_DCR_BOS 0x02 + +/* + * LAS: Long Address Select. When LAS is 1, the contents of the remote + * DMA registers RSAR0 and RSAR1 are used to provide A16-A31 + */ +#define ED_DCR_LAS 0x04 + +/* + * LS: Loopback Select. When 0, loopback mode is selected. Bits D1 and D2 + * of the TCR must also be programmed for loopback operation. + * When 1, normal operation is selected. + */ +#define ED_DCR_LS 0x08 + +/* + * AR: Auto-initialize Remote. When 0, data must be removed from ring-buffer + * under program control. When 1, remote DMA is automatically initiated + * and the boundry pointer is automatically updated + */ +#define ED_DCR_AR 0x10 + +/* + * FT0, FT1: Fifo Threshold select. + * FT1 FT0 Word-width Byte-width + * 0 0 1 word 2 bytes + * 0 1 2 words 4 bytes + * 1 0 4 words 8 bytes + * 1 1 8 words 12 bytes + * + * During transmission, the FIFO threshold indicates the number of bytes + * or words that the FIFO has filled from the local DMA before BREQ is + * asserted. The transmission threshold is 16 bytes minus the receiver + * threshold. + */ +#define ED_DCR_FT0 0x20 +#define ED_DCR_FT1 0x40 + +/* + * bit 7 (0x80) is unused/reserved + */ + +/* + * Transmit Configuration Register (TCR) definitions + */ + +/* + * CRC: Inhibit CRC. If 0, CRC will be appended by the transmitter, if 0, CRC + * is not appended by the transmitter. + */ +#define ED_TCR_CRC 0x01 + +/* + * LB0, LB1: Loopback control. These two bits set the type of loopback that is + * to be performed. + * + * LB1 LB0 mode + * 0 0 0 - normal operation (DCR_LS = 0) + * 0 1 1 - internal loopback (DCR_LS = 0) + * 1 0 2 - external loopback (DCR_LS = 1) + * 1 1 3 - external loopback (DCR_LS = 0) + */ +#define ED_TCR_LB0 0x02 +#define ED_TCR_LB1 0x04 + +/* + * ATD: Auto Transmit Disable. Clear for normal operation. When set, allows + * another station to disable the NIC's transmitter by transmitting to + * a multicast address hashing to bit 62. Reception of a multicast address + * hashing to bit 63 enables the transmitter. + */ +#define ED_TCR_ATD 0x08 + +/* + * OFST: Collision Offset enable. This bit when set modifies the backoff + * algorithm to allow prioritization of nodes. + */ +#define ED_TCR_OFST 0x10 + +/* + * bits 5, 6, and 7 are unused/reserved + */ + +/* + * Transmit Status Register (TSR) definitions + */ + +/* + * PTX: Packet Transmitted. Indicates successful transmission of packet. + */ +#define ED_TSR_PTX 0x01 + +/* + * bit 1 (0x02) is unused/reserved + */ + +/* + * COL: Transmit Collided. Indicates that the transmission collided at least + * once with another station on the network. + */ +#define ED_TSR_COL 0x04 + +/* + * ABT: Transmit aborted. Indicates that the transmission was aborted due to + * excessive collisions. + */ +#define ED_TSR_ABT 0x08 + +/* + * CRS: Carrier Sense Lost. Indicates that carrier was lost during the + * transmission of the packet. (Transmission is not aborted because + * of a loss of carrier) + */ +#define ED_TSR_CRS 0x10 + +/* + * FU: FIFO Underrun. Indicates that the NIC wasn't able to access bus/ + * transmission memory before the FIFO emptied. Transmission of the + * packet was aborted. + */ +#define ED_TSR_FU 0x20 + +/* + * CDH: CD Heartbeat. Indicates that the collision detection circuitry + * isn't working correctly during a collision heartbeat test. + */ +#define ED_TSR_CDH 0x40 + +/* + * OWC: Out of Window Collision: Indicates that a collision occurred after + * a slot time (51.2us). The transmission is rescheduled just as in + * normal collisions. + */ +#define ED_TSR_OWC 0x80 + +/* + * Receiver Configuration Register (RCR) definitions + */ + +/* + * SEP: Save Errored Packets. If 0, error packets are discarded. If set to 1, + * packets with CRC and frame errors are not discarded. + */ +#define ED_RCR_SEP 0x01 + +/* + * AR: Accept Runt packet. If 0, packet with less than 64 byte are discarded. + * If set to 1, packets with less than 64 byte are not discarded. + */ +#define ED_RCR_AR 0x02 + +/* + * AB: Accept Broadcast. If set, packets sent to the broadcast address will be + * accepted. + */ +#define ED_RCR_AB 0x04 + +/* + * AM: Accept Multicast. If set, packets sent to a multicast address are checked + * for a match in the hashing array. If clear, multicast packets are ignored. + */ +#define ED_RCR_AM 0x08 + +/* + * PRO: Promiscuous Physical. If set, all packets with a physical addresses are + * accepted. If clear, a physical destination address must match this + * station's address. Note: for full promiscuous mode, RCR_AB and RCR_AM + * must also be set. In addition, the multicast hashing array must be set + * to all 1's so that all multicast addresses are accepted. + */ +#define ED_RCR_PRO 0x10 + +/* + * MON: Monitor Mode. If set, packets will be checked for good CRC and framing, + * but are not stored in the ring-buffer. If clear, packets are stored (normal + * operation). + */ +#define ED_RCR_MON 0x20 + +/* + * bits 6 and 7 are unused/reserved. + */ + +/* + * Receiver Status Register (RSR) definitions + */ + +/* + * PRX: Packet Received without error. + */ +#define ED_RSR_PRX 0x01 + +/* + * CRC: CRC error. Indicates that a packet has a CRC error. Also set for frame + * alignment errors. + */ +#define ED_RSR_CRC 0x02 + +/* + * FAE: Frame Alignment Error. Indicates that the incoming packet did not end on + * a byte boundry and the CRC did not match at the last byte boundry. + */ +#define ED_RSR_FAE 0x04 + +/* + * FO: FIFO Overrun. Indicates that the FIFO was not serviced (during local DMA) + * causing it to overrun. Reception of the packet is aborted. + */ +#define ED_RSR_FO 0x08 + +/* + * MPA: Missed Packet. Indicates that the received packet couldn't be stored in + * the ring-buffer because of insufficient buffer space (exceeding the + * boundry pointer), or because the transfer to the ring-buffer was inhibited + * by RCR_MON - monitor mode. + */ +#define ED_RSR_MPA 0x10 + +/* + * PHY: Physical address. If 0, the packet received was sent to a physical address. + * If 1, the packet was accepted because of a multicast/broadcast address + * match. + */ +#define ED_RSR_PHY 0x20 + +/* + * DIS: Receiver Disabled. Set to indicate that the receiver has enetered monitor + * mode. Cleared when the receiver exits monitor mode. + */ +#define ED_RSR_DIS 0x40 + +/* + * DFR: Deferring. Set to indicate a 'jabber' condition. The CRS and COL inputs + * are active, and the transceiver has set the CD line as a result of the + * jabber. + */ +#define ED_RSR_DFR 0x80 + +/* + * receive ring discriptor + * + * The National Semiconductor DS8390 Network interface controller uses + * the following receive ring headers. The way this works is that the + * memory on the interface card is chopped up into 256 bytes blocks. + * A contiguous portion of those blocks are marked for receive packets + * by setting start and end block #'s in the NIC. For each packet that + * is put into the receive ring, one of these headers (4 bytes each) is + * tacked onto the front. + */ +struct ed_ring { + struct edr_status { /* received packet status */ + u_char rs_prx:1, /* packet received intack */ + rs_crc:1, /* crc error */ + rs_fae:1, /* frame alignment error */ + rs_fo:1, /* fifo overrun */ + rs_mpa:1, /* packet received intack */ + rs_phy:1, /* packet received intack */ + rs_dis:1, /* packet received intack */ + rs_dfr:1; /* packet received intack */ + } ed_rcv_status; /* received packet status */ + u_char next_packet; /* pointer to next packet */ + u_short count; /* bytes in packet (length + 4) */ +}; + +/* + * Common constants + */ +#define ED_PAGE_SIZE 256 /* Size of RAM pages in bytes */ +#define ED_TXBUF_SIZE 6 /* Size of TX buffer in pages */ + +/* + * Vendor types + */ +#define ED_VENDOR_WD_SMC 0x00 /* Western Digital/SMC */ +#define ED_VENDOR_3COM 0x01 /* 3Com */ +#define ED_VENDOR_NOVELL 0x02 /* Novell */ + +/* + * Compile-time config flags + */ +/* + * this sets the default for enabling/disablng the tranceiver + */ +#define ED_FLAGS_DISABLE_TRANCEIVER 0x0001 + +/* + * This forces the board to be used in 8/16bit mode even if it + * autoconfigs differently + */ +#define ED_FLAGS_FORCE_8BIT_MODE 0x0002 +#define ED_FLAGS_FORCE_16BIT_MODE 0x0004 + +/* + * This disables the use of double transmit buffers. + */ +#define ED_FLAGS_NO_MULTI_BUFFERING 0x0008 + +/* + * This forces all operations with the NIC memory to use Programmed + * I/O (i.e. not via shared memory) + */ +#define ED_FLAGS_FORCE_PIO 0x0010 + +/* + * Definitions for Western digital/SMC WD80x3 series ASIC + */ +/* + * Memory Select Register (MSR) + */ +#define ED_WD_MSR 0 + +#define ED_WD_MSR_ADDR 0x3f /* Memory decode bits 18-13 */ +#define ED_WD_MSR_MENB 0x40 /* Memory enable */ +#define ED_WD_MSR_RST 0x80 /* Reset board */ + +/* + * Interface Configuration Register (ICR) + */ +#define ED_WD_ICR 1 + +#define ED_WD_ICR_16BIT 0x01 /* 16-bit interface */ +#define ED_WD_ICR_OAR 0x02 /* select register. 0=BIO 1=EAR */ +#define ED_WD_ICR_IR2 0x04 /* high order bit of encoded IRQ */ +#define ED_WD_ICR_MSZ 0x08 /* memory size (0=8k 1=32k) */ +#define ED_WD_ICR_RLA 0x10 /* recall LAN address */ +#define ED_WD_ICR_RX7 0x20 /* recall all but i/o and LAN address */ +#define ED_WD_ICR_RIO 0x40 /* recall i/o address */ +#define ED_WD_ICR_STO 0x80 /* store to non-volatile memory */ + +/* + * IO Address Register (IAR) + */ +#define ED_WD_IAR 2 + +/* + * EEROM Address Register + */ +#define ED_WD_EAR 3 + +/* + * Interrupt Request Register (IRR) + */ +#define ED_WD_IRR 4 + +#define ED_WD_IRR_0WS 0x01 /* use 0 wait-states on 8 bit bus */ +#define ED_WD_IRR_OUT1 0x02 /* WD83C584 pin 1 output */ +#define ED_WD_IRR_OUT2 0x04 /* WD83C584 pin 2 output */ +#define ED_WD_IRR_OUT3 0x08 /* WD83C584 pin 3 output */ +#define ED_WD_IRR_FLASH 0x10 /* Flash RAM is in the ROM socket */ + +/* + * The three bit of the encoded IRQ are decoded as follows: + * + * IR2 IR1 IR0 IRQ + * 0 0 0 2/9 + * 0 0 1 3 + * 0 1 0 5 + * 0 1 1 7 + * 1 0 0 10 + * 1 0 1 11 + * 1 1 0 15 + * 1 1 1 4 + */ +#define ED_WD_IRR_IR0 0x20 /* bit 0 of encoded IRQ */ +#define ED_WD_IRR_IR1 0x40 /* bit 1 of encoded IRQ */ +#define ED_WD_IRR_IEN 0x80 /* Interrupt enable */ + +/* + * LA Address Register (LAAR) + */ +#define ED_WD_LAAR 5 + +#define ED_WD_LAAR_ADDRHI 0x1f /* bits 23-19 of RAM address */ +#define ED_WD_LAAR_0WS16 0x20 /* enable 0 wait-states on 16 bit bus */ +#define ED_WD_LAAR_L16EN 0x40 /* enable 16-bit operation */ +#define ED_WD_LAAR_M16EN 0x80 /* enable 16-bit memory access */ + +/* i/o base offset to station address/card-ID PROM */ +#define ED_WD_PROM 8 + +/* i/o base offset to CARD ID */ +#define ED_WD_CARD_ID ED_WD_PROM+6 + +/* Board type codes in card ID */ +#define ED_TYPE_WD8003S 0x02 +#define ED_TYPE_WD8003E 0x03 +#define ED_TYPE_WD8013EBT 0x05 +#define ED_TYPE_WD8013EP 0x27 +#define ED_TYPE_WD8013WC 0x28 +#define ED_TYPE_WD8013EBP 0x2c +#define ED_TYPE_WD8013EPC 0x29 + +/* Bit definitions in card ID */ +#define ED_WD_REV_MASK 0x1f /* Revision mask */ +#define ED_WD_SOFTCONFIG 0x20 /* Soft config */ +#define ED_WD_LARGERAM 0x40 /* Large RAM */ +#define ED_MICROCHANEL 0x80 /* Microchannel bus (vs. isa) */ + +/* + * Checksum total. All 8 bytes in station address PROM will add up to this + */ +#define ED_WD_ROM_CHECKSUM_TOTAL 0xFF + +#define ED_WD_NIC_OFFSET 0x10 /* I/O base offset to NIC */ +#define ED_WD_ASIC_OFFSET 0 /* I/O base offset to ASIC */ +#define ED_WD_IO_PORTS 32 /* # of i/o addresses used */ + +#define ED_WD_PAGE_OFFSET 0 /* page offset for NIC access to mem */ + +/* + * Definitions for 3Com 3c503 + */ +#define ED_3COM_NIC_OFFSET 0 +#define ED_3COM_ASIC_OFFSET 0x400 /* offset to nic i/o regs */ + +/* + * XXX - The I/O address range is fragmented in the 3c503; this is the + * number of regs at iobase. + */ +#define ED_3COM_IO_PORTS 16 /* # of i/o addresses used */ + +/* tx memory starts in second bank on 8bit cards */ +#define ED_3COM_TX_PAGE_OFFSET_8BIT 0x20 + +/* tx memory starts in first bank on 16bit cards */ +#define ED_3COM_TX_PAGE_OFFSET_16BIT 0x0 + +/* ...and rx memory starts in second bank */ +#define ED_3COM_RX_PAGE_OFFSET_16BIT 0x20 + + +/* + * Page Start Register. Must match PSTART in NIC + */ +#define ED_3COM_PSTR 0 + +/* + * Page Stop Register. Must match PSTOP in NIC + */ +#define ED_3COM_PSPR 1 + +/* + * Drq Timer Register. Determines number of bytes to be transfered during + * a DMA burst. + */ +#define ED_3COM_DQTR 2 + +/* + * Base Configuration Register. Read-only register which contains the + * board-configured I/O base address of the adapter. Bit encoded. + */ +#define ED_3COM_BCFR 3 + +#define ED_3COM_BCFR_2E0 0x01 +#define ED_3COM_BCFR_2A0 0x02 +#define ED_3COM_BCFR_280 0x04 +#define ED_3COM_BCFR_250 0x08 +#define ED_3COM_BCFR_350 0x10 +#define ED_3COM_BCFR_330 0x20 +#define ED_3COM_BCFR_310 0x40 +#define ED_3COM_BCFR_300 0x80 + +/* + * EPROM Configuration Register. Read-only register which contains the + * board-configured memory base address. Bit encoded. + */ +#define ED_3COM_PCFR 4 + +#define ED_3COM_PCFR_C8000 0x10 +#define ED_3COM_PCFR_CC000 0x20 +#define ED_3COM_PCFR_D8000 0x40 +#define ED_3COM_PCFR_DC000 0x80 + +/* + * GA Configuration Register. Gate-Array Configuration Register. + */ +#define ED_3COM_GACFR 5 + +/* + * mbs2 mbs1 mbs0 start address + * 0 0 0 0x0000 + * 0 0 1 0x2000 + * 0 1 0 0x4000 + * 0 1 1 0x6000 + * + * Note that with adapters with only 8K, the setting for 0x2000 must + * always be used. + */ +#define ED_3COM_GACFR_MBS0 0x01 +#define ED_3COM_GACFR_MBS1 0x02 +#define ED_3COM_GACFR_MBS2 0x04 + +#define ED_3COM_GACFR_RSEL 0x08 /* enable shared memory */ +#define ED_3COM_GACFR_TEST 0x10 /* for GA testing */ +#define ED_3COM_GACFR_OWS 0x20 /* select 0WS access to GA */ +#define ED_3COM_GACFR_TCM 0x40 /* Mask DMA interrupts */ +#define ED_3COM_GACFR_NIM 0x80 /* Mask NIC interrupts */ + +/* + * Control Register. Miscellaneous control functions. + */ +#define ED_3COM_CR 6 + +#define ED_3COM_CR_RST 0x01 /* Reset GA and NIC */ +#define ED_3COM_CR_XSEL 0x02 /* Transceiver select. BNC=1(def) AUI=0 */ +#define ED_3COM_CR_EALO 0x04 /* window EA PROM 0-15 to I/O base */ +#define ED_3COM_CR_EAHI 0x08 /* window EA PROM 16-31 to I/O base */ +#define ED_3COM_CR_SHARE 0x10 /* select interrupt sharing option */ +#define ED_3COM_CR_DBSEL 0x20 /* Double buffer select */ +#define ED_3COM_CR_DDIR 0x40 /* DMA direction select */ +#define ED_3COM_CR_START 0x80 /* Start DMA controller */ + +/* + * Status Register. Miscellaneous status information. + */ +#define ED_3COM_STREG 7 + +#define ED_3COM_STREG_REV 0x07 /* GA revision */ +#define ED_3COM_STREG_DIP 0x08 /* DMA in progress */ +#define ED_3COM_STREG_DTC 0x10 /* DMA terminal count */ +#define ED_3COM_STREG_OFLW 0x20 /* Overflow */ +#define ED_3COM_STREG_UFLW 0x40 /* Underflow */ +#define ED_3COM_STREG_DPRDY 0x80 /* Data port ready */ + +/* + * Interrupt/DMA Configuration Register + */ +#define ED_3COM_IDCFR 8 + +#define ED_3COM_IDCFR_DRQ0 0x01 /* DMA request 1 select */ +#define ED_3COM_IDCFR_DRQ1 0x02 /* DMA request 2 select */ +#define ED_3COM_IDCFR_DRQ2 0x04 /* DMA request 3 select */ +#define ED_3COM_IDCFR_UNUSED 0x08 /* not used */ +#define ED_3COM_IDCFR_IRQ2 0x10 /* Interrupt request 2 select */ +#define ED_3COM_IDCFR_IRQ3 0x20 /* Interrupt request 3 select */ +#define ED_3COM_IDCFR_IRQ4 0x40 /* Interrupt request 4 select */ +#define ED_3COM_IDCFR_IRQ5 0x80 /* Interrupt request 5 select */ + +/* + * DMA Address Register MSB + */ +#define ED_3COM_DAMSB 9 + +/* + * DMA Address Register LSB + */ +#define ED_3COM_DALSB 0x0a + +/* + * Vector Pointer Register 2 + */ +#define ED_3COM_VPTR2 0x0b + +/* + * Vector Pointer Register 1 + */ +#define ED_3COM_VPTR1 0x0c + +/* + * Vector Pointer Register 0 + */ +#define ED_3COM_VPTR0 0x0d + +/* + * Register File Access MSB + */ +#define ED_3COM_RFMSB 0x0e + +/* + * Register File Access LSB + */ +#define ED_3COM_RFLSB 0x0f + +/* + * Definitions for Novell NE1000/2000 boards + */ + +/* + * Board type codes + */ +#define ED_TYPE_NE1000 0x01 +#define ED_TYPE_NE2000 0x02 + +/* + * Register offsets/total + */ +#define ED_NOVELL_NIC_OFFSET 0x00 +#define ED_NOVELL_ASIC_OFFSET 0x10 +#define ED_NOVELL_IO_PORTS 32 + +/* + * Remote DMA data register; for reading or writing to the NIC mem + * via programmed I/O (offset from ASIC base) + */ +#define ED_NOVELL_DATA 0x00 + +/* + * Reset register; reading from this register causes a board reset + */ +#define ED_NOVELL_RESET 0x0f diff --git a/sys/i386/isa/if_ie.c b/sys/i386/isa/if_ie.c new file mode 100644 index 0000000000..2a68b1c6fe --- /dev/null +++ b/sys/i386/isa/if_ie.c @@ -0,0 +1,1799 @@ +/*- + * Copyright (c) 1992, 1993, University of Vermont and State + * Agricultural College. + * Copyright (c) 1992, 1993, Garrett A. Wollman. + * + * Portions: + * Copyright (c) 1990, 1991, William F. Jolitz + * Copyright (c) 1990, The Regents of the University of California + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * Vermont and State Agricultural College and Garrett A. Wollman, + * by William F. Jolitz, by the University of California, + * Berkeley, by Larwence Berkeley Laboratory, and its contributors. + * 4. Neither the names of the Universities nor the names of the authors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR AUTHORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +/* + * Intel 82586 Ethernet chip + * Register, bit, and structure definitions. + * + * Written by GAW with reference to the Clarkson Packet Driver code for this + * chip written by Russ Nelson and others. + * + * BPF support code stolen directly from hpdev/if_le.c, supplied with + * tcpdump. + */ + +/* + * The i82586 is a very versatile chip, found in many implementations. + * Programming this chip is mostly the same, but certain details differ + * from card to card. This driver is written so that different cards + * can be automatically detected at run-time. Currently, only the + * AT&T EN100/StarLAN 10 series are supported. + */ + +/* +Mode of operation: + +We run the 82586 in a standard Ethernet mode. We keep NFRAMES received +frame descriptors around for the receiver to use, and NBUFFS associated +receive buffer descriptors, both in a circular list. Whenever a frame is +received, we rotate both lists as necessary. (The 586 treats both lists +as a simple queue.) We also keep a transmit command around so that packets +can be sent off quickly. + +We configure the adapter in AL-LOC = 1 mode, which means that the +Ethernet/802.3 MAC header is placed at the beginning of the receive buffer +rather than being split off into various fields in the RFD. This also +means that we must include this header in the transmit buffer as well. + +By convention, all transmit commands, and only transmit commands, shall +have the I (IE_CMD_INTR) bit set in the command. This way, when an +interrupt arrives at ieintr(), it is immediately possible to tell +what precisely caused it. ANY OTHER command-sending routines should +run at splimp(), and should post an acknowledgement to every interrupt +they generate. + +The 82586 has a 24-bit address space internally, and the adaptor's +memory is located at the top of this region. However, the value we are +given in configuration is normally the *bottom* of the adaptor RAM. So, +we must go through a few gyrations to come up with a kernel virtual address +which represents the actual beginning of the 586 address space. First, +we autosize the RAM by running through several possible sizes and trying +to initialize the adapter under the assumption that the selected size +is correct. Then, knowing the correct RAM size, we set up our pointers +in ie_softc[unit]. `iomem' represents the computed base of the 586 +address space. `iomembot' represents the actual configured base +of adapter RAM. Finally, `iosize' represents the calculated size +of 586 RAM. Then, when laying out commands, we use the interval +[iomembot, iomembot + iosize); to make 24-pointers, we subtract +iomem, and to make 16-pointers, we subtract iomem and and with 0xffff. + +*/ + +#include "ie.h" +#if NIE > 0 + +#include "param.h" +#include "systm.h" +#include "mbuf.h" +#include "buf.h" +#include "protosw.h" +#include "socket.h" +#include "ioctl.h" +#include "errno.h" +#include "syslog.h" + +#include "net/if.h" +#include "net/if_types.h" +#include "net/if_dl.h" +#include "net/netisr.h" +#include "net/route.h" + +#include "bpfilter.h" + +#ifdef INET +#include "netinet/in.h" +#include "netinet/in_systm.h" +#include "netinet/in_var.h" +#include "netinet/ip.h" +#include "netinet/if_ether.h" +#endif + +#ifdef NS +#include "netns/ns.h" +#include "netns/ns_if.h" +#endif + +#include "i386/isa/isa.h" +/*#include "machine/cpufunc.h"*/ +#include "i386/isa/isa_device.h" +#include "i386/isa/ic/i82586.h" +#include "i386/isa/if_iereg.h" +#include "i386/isa/icu.h" + +#include "vm/vm.h" + +#if NBPFILTER > 0 +#include "net/bpf.h" +#include "net/bpfdesc.h" +#endif + +#if (NBPFILTER > 0) || defined(MULTICAST) +#define FILTER +static struct mbuf *last_not_for_us; +#endif + +#ifdef DEBUG +#define IED_RINT 1 +#define IED_TINT 2 +#define IED_RNR 4 +#define IED_CNA 8 +#define IED_READFRAME 16 +int ie_debug = IED_RNR; +#endif + +#ifndef ETHERMINLEN +#define ETHERMINLEN 60 +#endif + +#define IE_BUF_LEN 1512 /* length of transmit buffer */ + +/* Forward declaration */ +struct ie_softc; + +int ieprobe(struct isa_device *dvp); +int ieattach(struct isa_device *dvp); +int ieinit(int unit); +int ieioctl(struct ifnet *ifp, int command, void *data); +int iestart(struct ifnet *ifp); +static void sl_reset_586(int unit); +static void sl_chan_attn(int unit); +int iereset(int unit, int dummy); +static void ie_readframe(int unit, struct ie_softc *ie, int bufno); +static void ie_drop_packet_buffer(int unit, struct ie_softc *ie); +static void sl_read_ether(int unit, unsigned char addr[6]); +static void find_ie_mem_size(int unit); +static int command_and_wait(int unit, int command, void volatile *pcmd, int); +static int ierint(int unit, struct ie_softc *ie); +static int ietint(int unit, struct ie_softc *ie); +static int iernr(int unit, struct ie_softc *ie); +static void start_receiver(int unit); +static int ieget(int, struct ie_softc *, struct mbuf **, + struct ether_header *, int *); +static caddr_t setup_rfa(caddr_t ptr, struct ie_softc *ie); +static int mc_setup(int, caddr_t, volatile struct ie_sys_ctl_block *); +#ifdef MULTICAST +static void ie_mc_reset(int unit); +#endif + +#ifdef DEBUG +void print_rbd(volatile struct ie_recv_buf_desc *rbd); + +int in_ierint = 0; +int in_ietint = 0; +#endif + +/* + * This tells the autoconf code how to set us up. + */ +struct isa_driver iedriver = { + ieprobe, ieattach, "ie", +}; + +enum ie_hardware { + IE_STARLAN10, + IE_EN100, + IE_SLFIBER, + IE_UNKNOWN +}; + +const char *ie_hardware_names[] = { + "StarLAN 10", + "EN100", + "StarLAN Fiber", + "Unknown" +}; + +/* +sizeof(iscp) == 1+1+2+4 == 8 +sizeof(scb) == 2+2+2+2+2+2+2+2 == 16 +NFRAMES * sizeof(rfd) == NFRAMES*(2+2+2+2+6+6+2+2) == NFRAMES*24 == 384 +sizeof(xmit_cmd) == 2+2+2+2+6+2 == 18 +sizeof(transmit buffer) == 1512 +sizeof(transmit buffer desc) == 8 +----- +1946 + +NBUFFS * sizeof(rbd) == NBUFFS*(2+2+4+2+2) == NBUFFS*12 +NBUFFS * IE_RBUF_SIZE == NBUFFS*256 + +NBUFFS should be (16384 - 1946) / (256 + 12) == 14438 / 268 == 53 + +With NBUFFS == 48, this leaves us 1574 bytes for another command or +more buffers. Another transmit command would be 18+8+1512 == 1538 +---just barely fits! + +Obviously all these would have to be reduced for smaller memory sizes. +With a larger memory, it would be possible to roughly double the number of +both transmit and receive buffers. +*/ + +#define NFRAMES 16 /* number of frames to allow for receive */ +#define NBUFFS 48 /* number of buffers to allocate */ +#define IE_RBUF_SIZE 256 /* size of each buffer, MUST BE POWER OF TWO */ + +/* + * Ethernet status, per interface. + */ +struct ie_softc { + struct arpcom arpcom; + void (*ie_reset_586)(int); + void (*ie_chan_attn)(int); + enum ie_hardware hard_type; + int hard_vers; + + u_short port; + caddr_t iomem; + caddr_t iomembot; + unsigned iosize; + + int want_mcsetup; + int promisc; + volatile struct ie_int_sys_conf_ptr *iscp; + volatile struct ie_sys_ctl_block *scb; + volatile struct ie_recv_frame_desc *rframes[NFRAMES]; + volatile struct ie_recv_buf_desc *rbuffs[NBUFFS]; + volatile char *cbuffs[NBUFFS]; + int rfhead, rftail, rbhead, rbtail; + + volatile struct ie_xmit_cmd *xmit_cmds[2]; + volatile struct ie_xmit_buf *xmit_buffs[2]; + int xmit_count; + u_char *xmit_cbuffs[2]; + + struct ie_en_addr mcast_addrs[MAXMCAST + 1]; + int mcast_count; + +#if NBPFILTER > 0 + caddr_t ie_bpf; +#endif + +} ie_softc[NIE]; + +#define MK_24(base, ptr) ((caddr_t)((u_long)ptr - (u_long)base)) +#define MK_16(base, ptr) ((u_short)(u_long)MK_24(base, ptr)) + +#define PORT ie_softc[unit].port +#define MEM ie_softc[unit].iomem + + +int ieprobe(dvp) + struct isa_device *dvp; +{ + int unit = dvp->id_unit; + u_char c; + + ie_softc[unit].port = dvp->id_iobase; + ie_softc[unit].iomembot = dvp->id_maddr; + ie_softc[unit].iomem = 0; + + c = inb(PORT + IEATT_REVISION); + switch(SL_BOARD(c)) { + case SL10_BOARD: + ie_softc[unit].hard_type = IE_STARLAN10; + ie_softc[unit].ie_reset_586 = sl_reset_586; + ie_softc[unit].ie_chan_attn = sl_chan_attn; + break; + case EN100_BOARD: + ie_softc[unit].hard_type = IE_EN100; + ie_softc[unit].ie_reset_586 = sl_reset_586; + ie_softc[unit].ie_chan_attn = sl_chan_attn; + break; + case SLFIBER_BOARD: + ie_softc[unit].hard_type = IE_SLFIBER; + ie_softc[unit].ie_reset_586 = sl_reset_586; + ie_softc[unit].ie_chan_attn = sl_chan_attn; + break; + + /* + * Anything else is not recognized or cannot be used. + */ + default: + return 0; + } + + ie_softc[unit].hard_vers = SL_REV(c); + + /* + * Divine memory size on-board the card. Ususally 16k. + */ + find_ie_mem_size(unit); + + if(!ie_softc[unit].iosize) { + return 0; + } + + dvp->id_msize = ie_softc[unit].iosize; + + switch(ie_softc[unit].hard_type) { + case IE_EN100: + case IE_STARLAN10: + case IE_SLFIBER: + sl_read_ether(unit, ie_softc[unit].arpcom.ac_enaddr); + break; + + default: + printf("ie%d: unknown AT&T board type code %d\n", unit, + ie_softc[unit].hard_type); + return 0; + } + + return 1; +} + +/* + * Taken almost exactly from Bill's if_is.c, then modified beyond recognition. + */ +int +ieattach(dvp) + struct isa_device *dvp; +{ + int unit = dvp->id_unit; + struct ie_softc *ie = &ie_softc[unit]; + struct ifnet *ifp = &ie->arpcom.ac_if; + + ifp->if_unit = unit; + ifp->if_name = iedriver.name; + ifp->if_mtu = ETHERMTU; + printf("<%s R%d> ethernet address %s", + ie_hardware_names[ie_softc[unit].hard_type], + ie_softc[unit].hard_vers + 1, + ether_sprintf(ie->arpcom.ac_enaddr)); + + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS; +#ifdef MULTICAST + ifp->if_flags |= IFF_MULTICAST; +#endif /* MULTICAST */ + + ifp->if_init = ieinit; + ifp->if_output = ether_output; + ifp->if_start = iestart; + ifp->if_ioctl = ieioctl; + ifp->if_reset = iereset; + ifp->if_type = IFT_ETHER; + ifp->if_addrlen = 6; + ifp->if_hdrlen = 14; + +#if NBPFILTER > 0 + printf("\n"); + bpfattach(&ie_softc[unit].ie_bpf, ifp, DLT_EN10MB, + sizeof(struct ether_header)); +#endif + + if_attach(ifp); + { + struct ifaddr *ifa = ifp->if_addrlist; + struct sockaddr_dl *sdl; + while(ifa && ifa->ifa_addr && ifa->ifa_addr->sa_family != AF_LINK) + ifa = ifa->ifa_next; + + if(!ifa || !ifa->ifa_addr) return; + + /* Provide our ether address to the higher layers */ + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_alen = 6; + sdl->sdl_slen = 0; + bcopy(ie->arpcom.ac_enaddr, LLADDR(sdl), 6); + } +} + +/* + * What to do upon receipt of an interrupt. + */ +int ieintr(unit) + int unit; +{ + register struct ie_softc *ie = &ie_softc[unit]; + register u_short status; + + status = ie->scb->ie_status; + +loop: + if(status & (IE_ST_RECV | IE_ST_RNR)) { +#ifdef DEBUG + in_ierint++; + if(ie_debug & IED_RINT) + printf("ie%d: rint\n", unit); +#endif + ierint(unit, ie); +#ifdef DEBUG + in_ierint--; +#endif + } + + if(status & IE_ST_DONE) { +#ifdef DEBUG + in_ietint++; + if(ie_debug & IED_TINT) + printf("ie%d: tint\n", unit); +#endif + ietint(unit, ie); +#ifdef DEBUG + in_ietint--; +#endif + } + + if(status & IE_ST_RNR) { +#ifdef DEBUG + if(ie_debug & IED_RNR) + printf("ie%d: rnr\n", unit); +#endif + iernr(unit, ie); + } + +#ifdef DEBUG + if((status & IE_ST_ALLDONE) + && (ie_debug & IED_CNA)) + printf("ie%d: cna\n", unit); +#endif + + /* Don't ack interrupts which we didn't receive */ + ie_ack(ie->scb, IE_ST_WHENCE & status, unit, ie->ie_chan_attn); + + if((status = ie->scb->ie_status) & IE_ST_WHENCE) + goto loop; + + return unit; +} + +/* + * Process a received-frame interrupt. + */ +static int ierint(unit, ie) + int unit; + struct ie_softc *ie; +{ + int i, status; + static int timesthru = 1024; + + i = ie->rfhead; + while(1) { + status = ie->rframes[i]->ie_fd_status; + + if((status & IE_FD_COMPLETE) && (status & IE_FD_OK)) { + ie->arpcom.ac_if.if_ipackets++; + if(!--timesthru) { + ie->arpcom.ac_if.if_ierrors += ie->scb->ie_err_crc + ie->scb->ie_err_align + + ie->scb->ie_err_resource + ie->scb->ie_err_overrun; + ie->scb->ie_err_crc = 0; + ie->scb->ie_err_align = 0; + ie->scb->ie_err_resource = 0; + ie->scb->ie_err_overrun = 0; + timesthru = 1024; + } + ie_readframe(unit, ie, i); + } else { + if(status & IE_FD_RNR) { + if(!(ie->scb->ie_status & IE_RU_READY)) { + ie->rframes[0]->ie_fd_next = MK_16(MEM, ie->rbuffs[0]); + ie->scb->ie_recv_list = MK_16(MEM, ie->rframes[0]); + command_and_wait(unit, IE_RU_START, 0, 0); + } + } + break; + } + i = (i + 1) % NFRAMES; + } + return 0; +} + +/* + * Process a command-complete interrupt. These are only generated by + * the transmission of frames. This routine is deceptively simple, since + * most of the real work is done by iestart(). + */ +static int ietint(unit, ie) + int unit; + struct ie_softc *ie; +{ + int status; + int i; + + ie->arpcom.ac_if.if_timer = 0; + ie->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + + for(i = 0; i < ie->xmit_count; i++) { + status = ie->xmit_cmds[i]->ie_xmit_status; + + if(status & IE_XS_LATECOLL) { + printf("ie%d: late collision\n", unit); + ie->arpcom.ac_if.if_collisions++; + ie->arpcom.ac_if.if_oerrors++; + } else if(status & IE_XS_NOCARRIER) { + printf("ie%d: no carrier\n", unit); + ie->arpcom.ac_if.if_oerrors++; + } else if(status & IE_XS_LOSTCTS) { + printf("ie%d: lost CTS\n", unit); + ie->arpcom.ac_if.if_oerrors++; + } else if(status & IE_XS_UNDERRUN) { + printf("ie%d: DMA underrun\n", unit); + ie->arpcom.ac_if.if_oerrors++; + } else if(status & IE_XS_EXCMAX) { + printf("ie%d: too many collisions\n", unit); + ie->arpcom.ac_if.if_collisions += 16; + ie->arpcom.ac_if.if_oerrors++; + } else { + ie->arpcom.ac_if.if_opackets++; + ie->arpcom.ac_if.if_collisions += status & IE_XS_MAXCOLL; + } + } + ie->xmit_count = 0; + + /* + * If multicast addresses were added or deleted while we were transmitting, + * ie_mc_reset() set the want_mcsetup flag indicating that we should do it. + */ + if(ie->want_mcsetup) { + mc_setup(unit, (caddr_t)ie->xmit_cbuffs[0], ie->scb); + ie->want_mcsetup = 0; + } + + /* Wish I knew why this seems to be necessary... */ + ie->xmit_cmds[0]->ie_xmit_status |= IE_STAT_COMPL; + + iestart(&ie->arpcom.ac_if); + return 0; /* shouldn't be necessary */ +} + +/* + * Process a receiver-not-ready interrupt. I believe that we get these + * when there aren't enough buffers to go around. For now (FIXME), we + * just restart the receiver, and hope everything's ok. + */ +static int iernr(unit, ie) + int unit; + struct ie_softc *ie; +{ +#ifdef doesnt_work + setup_rfa((caddr_t)ie->rframes[0], ie); + + ie->scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]); + command_and_wait(unit, IE_RU_START, 0, 0); +#else + /* This doesn't work either, but it doesn't hang either. */ + command_and_wait(unit, IE_RU_DISABLE, 0, 0); /* just in case */ + setup_rfa((caddr_t)ie->rframes[0], ie); /* ignore cast-qual */ + + ie->scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]); + command_and_wait(unit, IE_RU_START, 0, 0); /* was ENABLE */ + +#endif + ie_ack(ie->scb, IE_ST_WHENCE, unit, ie->ie_chan_attn); + + ie->arpcom.ac_if.if_ierrors++; + return 0; +} + +#ifdef FILTER +/* + * Compare two Ether/802 addresses for equality, inlined and + * unrolled for speed. I'd love to have an inline assembler + * version of this... + */ +static inline int ether_equal(u_char *one, u_char *two) { + if(one[0] != two[0]) return 0; + if(one[1] != two[1]) return 0; + if(one[2] != two[2]) return 0; + if(one[3] != two[3]) return 0; + if(one[4] != two[4]) return 0; + if(one[5] != two[5]) return 0; + return 1; +} + +/* + * Check for a valid address. to_bpf is filled in with one of the following: + * 0 -> BPF doesn't get this packet + * 1 -> BPF does get this packet + * 2 -> BPF does get this packet, but we don't + * Return value is true if the packet is for us, and false otherwise. + * + * This routine is a mess, but it's also critical that it be as fast + * as possible. It could be made cleaner if we can assume that the + * only client which will fiddle with IFF_PROMISC is BPF. This is + * probably a good assumption, but we do not make it here. (Yet.) + */ +static inline int check_eh(struct ie_softc *ie, + struct ether_header *eh, + int *to_bpf) { + int i; + + switch(ie->promisc) { + case IFF_ALLMULTI: + /* + * Receiving all multicasts, but no unicasts except those destined for us. + */ +#if NBPFILTER > 0 + *to_bpf = (ie->ie_bpf != 0); /* BPF gets this packet if anybody cares */ +#endif + if(eh->ether_dhost[0] & 1) { + return 1; + } + if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1; + return 0; + + case IFF_PROMISC: + /* + * Receiving all packets. These need to be passed on to BPF. + */ +#if NBPFILTER > 0 + *to_bpf = (ie->ie_bpf != 0); +#endif + /* If for us, accept and hand up to BPF */ + if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1; + +#if NBPFILTER > 0 + if(*to_bpf) *to_bpf = 2; /* we don't need to see it */ +#endif + +#ifdef MULTICAST + /* + * Not a multicast, so BPF wants to see it but we don't. + */ + if(!(eh->ether_dhost[0] & 1)) return 1; + + /* + * If it's one of our multicast groups, accept it and pass it + * up. + */ + for(i = 0; i < ie->mcast_count; i++) { + if(ether_equal(eh->ether_dhost, (u_char *)&ie->mcast_addrs[i])) { +#if NBPFILTER > 0 + if(*to_bpf) *to_bpf = 1; +#endif + return 1; + } + } +#endif /* MULTICAST */ + return 1; + + case IFF_ALLMULTI | IFF_PROMISC: + /* + * Acting as a multicast router, and BPF running at the same time. + * Whew! (Hope this is a fast machine...) + */ +#if NBPFILTER > 0 + *to_bpf = (ie->ie_bpf != 0); +#endif + /* We want to see multicasts. */ + if(eh->ether_dhost[0] & 1) return 1; + + /* We want to see our own packets */ + if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1; + + /* Anything else goes to BPF but nothing else. */ +#if NBPFILTER > 0 + if(*to_bpf) *to_bpf = 2; +#endif + return 1; + + default: + /* + * Only accept unicast packets destined for us, or multicasts + * for groups that we belong to. For now, we assume that the + * '586 will only return packets that we asked it for. This + * isn't strictly true (it uses hashing for the multicast filter), + * but it will do in this case, and we want to get out of here + * as quickly as possible. + */ +#if NBPFILTER > 0 + *to_bpf = (ie->ie_bpf != 0); +#endif + return 1; + } + return 0; +} +#endif /* FILTER */ + +/* + * We want to isolate the bits that have meaning... This assumes that + * IE_RBUF_SIZE is an even power of two. If somehow the act_len exceeds + * the size of the buffer, then we are screwed anyway. + */ +static inline int ie_buflen(struct ie_softc *ie, int head) { + return (ie->rbuffs[head]->ie_rbd_actual + & (IE_RBUF_SIZE | (IE_RBUF_SIZE - 1))); +} + +static inline int ie_packet_len(int unit, struct ie_softc *ie) { + int i; + int head = ie->rbhead; + int acc = 0; + + do { + if(!(ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_USED)) { +#ifdef DEBUG + print_rbd(ie->rbuffs[ie->rbhead]); +#endif + log(LOG_ERR, "ie%d: receive descriptors out of sync at %d\n", + unit, ie->rbhead); + iereset(unit, 0); + return -1; + } + + i = ie->rbuffs[head]->ie_rbd_actual & IE_RBD_LAST; + + acc += ie_buflen(ie, head); + head = (head + 1) % NBUFFS; + } while(!i); + + return acc; +} + +/* + * Read data off the interface, and turn it into an mbuf chain. + * + * This code is DRAMATICALLY different from the previous version; this + * version tries to allocate the entire mbuf chain up front, given the + * length of the data available. This enables us to allocate mbuf + * clusters in many situations where before we would have had a long + * chain of partially-full mbufs. This should help to speed up the + * operation considerably. (Provided that it works, of course.) + */ +static inline int ieget(unit, ie, mp, ehp, to_bpf) + int unit; + struct ie_softc *ie; + struct mbuf **mp; + struct ether_header *ehp; + int *to_bpf; +{ + struct mbuf *m, *top, **mymp; + int i; + int offset; + int totlen, resid; + int thismboff; + int head; + + totlen = ie_packet_len(unit, ie); + if(totlen <= 0) return -1; + + i = ie->rbhead; + + /* + * Snarf the Ethernet header. + */ + bcopy((caddr_t)ie->cbuffs[i], (caddr_t)ehp, sizeof *ehp); + /* ignore cast-qual warning here */ + + /* + * As quickly as possible, check if this packet is for us. + * If not, don't waste a single cycle copying the rest of the + * packet in. + * This is only a consideration when FILTER is defined; i.e., when + * we are either running BPF or doing multicasting. + */ +#ifdef FILTER + if(!check_eh(ie, ehp, to_bpf)) { + ie_drop_packet_buffer(unit, ie); + ie->arpcom.ac_if.if_ierrors--; /* just this case, it's not an error */ + return -1; + } +#endif + totlen -= (offset = sizeof *ehp); + + MGETHDR(*mp, M_DONTWAIT, MT_DATA); + if(!*mp) { + ie_drop_packet_buffer(unit, ie); + return -1; + } + + m = *mp; + m->m_pkthdr.rcvif = &ie->arpcom.ac_if; + m->m_len = MHLEN; + resid = m->m_pkthdr.len = totlen; + top = 0; + mymp = ⊤ + + /* + * This loop goes through and allocates mbufs for all the data we will + * be copying in. It does not actually do the copying yet. + */ + do { /* while(resid > 0) */ + /* + * Try to allocate an mbuf to hold the data that we have. If we + * already allocated one, just get another one and stick it on the + * end (eventually). If we don't already have one, try to allocate + * an mbuf cluster big enough to hold the whole packet, if we think it's + * reasonable, or a single mbuf which may or may not be big enough. + * Got that? + */ + if(top) { + MGET(m, M_DONTWAIT, MT_DATA); + if(!m) { + m_freem(top); + ie_drop_packet_buffer(unit, ie); + return -1; + } + m->m_len = MLEN; + } + + if(resid >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if(m->m_flags & M_EXT) + m->m_len = min(resid, MCLBYTES); + } else { + if(resid < m->m_len) { + if(!top && resid + max_linkhdr <= m->m_len) + m->m_data += max_linkhdr; + m->m_len = resid; + } + } + resid -= m->m_len; + *mymp = m; + mymp = &m->m_next; + } while(resid > 0); + + resid = totlen; + m = top; + thismboff = 0; + head = ie->rbhead; + + /* + * Now we take the mbuf chain (hopefully only one mbuf most of the + * time) and stuff the data into it. There are no possible failures + * at or after this point. + */ + while(resid > 0) { /* while there's stuff left */ + int thislen = ie_buflen(ie, head) - offset; + + /* + * If too much data for the current mbuf, then fill the current one + * up, go to the next one, and try again. + */ + if(thislen > m->m_len - thismboff) { + int newlen = m->m_len - thismboff; + bcopy((caddr_t)(ie->cbuffs[head] + offset), + mtod(m, caddr_t) + thismboff, (unsigned)newlen); + /* ignore cast-qual warning */ + m = m->m_next; + thismboff = 0; /* new mbuf, so no offset */ + offset += newlen; /* we are now this far into the packet */ + resid -= newlen; /* so there is this much left to get */ + continue; + } + + /* + * If there is more than enough space in the mbuf to hold the + * contents of this buffer, copy everything in, advance pointers, + * and so on. + */ + if(thislen < m->m_len - thismboff) { + bcopy((caddr_t)(ie->cbuffs[head] + offset), /* ignore warning */ + mtod(m, caddr_t) + thismboff, (unsigned)thislen); + thismboff += thislen; /* we are this far into the mbuf */ + resid -= thislen; /* and this much is left */ + goto nextbuf; + } + + /* + * Otherwise, there is exactly enough space to put this buffer's + * contents into the current mbuf. Do the combination of the above + * actions. + */ + bcopy((caddr_t)(ie->cbuffs[head] + offset), /* ignore warning */ + mtod(m, caddr_t) + thismboff, (unsigned)thislen); + m = m->m_next; + thismboff = 0; /* new mbuf, start at the beginning */ + resid -= thislen; /* and we are this far through */ + + /* + * Advance all the pointers. We can get here from either of the + * last two cases, but never the first. + */ +nextbuf: + offset = 0; + ie->rbuffs[head]->ie_rbd_actual = 0; + ie->rbuffs[head]->ie_rbd_length |= IE_RBD_LAST; + ie->rbhead = head = (head + 1) % NBUFFS; + ie->rbuffs[ie->rbtail]->ie_rbd_length &= ~IE_RBD_LAST; + ie->rbtail = (ie->rbtail + 1) % NBUFFS; + } + + /* + * Unless something changed strangely while we were doing the copy, + * we have now copied everything in from the shared memory. + * This means that we are done. + */ + return 0; +} + +/* + * Read frame NUM from unit UNIT (pre-cached as IE). + * + * This routine reads the RFD at NUM, and copies in the buffers from + * the list of RBD, then rotates the RBD and RFD lists so that the receiver + * doesn't start complaining. Trailers are DROPPED---there's no point + * in wasting time on confusing code to deal with them. Hopefully, + * this machine will never ARP for trailers anyway. + */ +static void ie_readframe(unit, ie, num) + int unit; + struct ie_softc *ie; + int num; /* frame number to read */ +{ + struct ie_recv_frame_desc rfd; + struct mbuf *m = 0; + struct ether_header eh; +#if NBPFILTER > 0 + int bpf_gets_it = 0; +#endif + + bcopy((caddr_t)(ie->rframes[num]), &rfd, sizeof(struct ie_recv_frame_desc)); + + /* Immediately advance the RFD list, since we we have copied ours now. */ + ie->rframes[num]->ie_fd_status = 0; + ie->rframes[num]->ie_fd_last |= IE_FD_LAST; + ie->rframes[ie->rftail]->ie_fd_last &= ~IE_FD_LAST; + ie->rftail = (ie->rftail + 1) % NFRAMES; + ie->rfhead = (ie->rfhead + 1) % NFRAMES; + + if(rfd.ie_fd_status & IE_FD_OK) { + if( +#if NBPFILTER > 0 + ieget(unit, ie, &m, &eh, &bpf_gets_it) +#else + ieget(unit, ie, &m, &eh, (int *)0) +#endif + ) { + ie->arpcom.ac_if.if_ierrors++; /* this counts as an error */ + return; + } + } + +#ifdef DEBUG + if(ie_debug & IED_READFRAME) { + printf("ie%d: frame from ether %s type %x\n", unit, + ether_sprintf(eh.ether_shost), (unsigned)eh.ether_type); + } + if(ntohs(eh.ether_type) > ETHERTYPE_TRAIL + && ntohs(eh.ether_type) < (ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER)) + printf("received trailer!\n"); +#endif + + if(!m) return; + +#ifdef FILTER + if(last_not_for_us) { + m_freem(last_not_for_us); + last_not_for_us = 0; + } + +#if NBPFILTER > 0 + /* + * Check for a BPF filter; if so, hand it up. + * Note that we have to stick an extra mbuf up front, because + * bpf_mtap expects to have the ether header at the front. + * It doesn't matter that this results in an ill-formatted mbuf chain, + * since BPF just looks at the data. (It doesn't try to free the mbuf, + * tho' it will make a copy for tcpdump.) + */ + if(bpf_gets_it) { + struct mbuf m0; + m0.m_len = sizeof eh; + m0.m_data = (caddr_t)&eh; + m0.m_next = m; + + /* Pass it up */ + bpf_mtap(ie->ie_bpf, &m0); + } + /* + * A signal passed up from the filtering code indicating that the + * packet is intended for BPF but not for the protocol machinery. + * We can save a few cycles by not handing it off to them. + */ + if(bpf_gets_it == 2) { + last_not_for_us = m; + return; + } +#endif /* NBPFILTER > 0 */ + /* + * In here there used to be code to check destination addresses upon + * receipt of a packet. We have deleted that code, and replaced it + * with code to check the address much earlier in the cycle, before + * copying the data in; this saves us valuable cycles when operating + * as a multicast router or when using BPF. + */ +#endif /* FILTER */ + + eh.ether_type = ntohs(eh.ether_type); + + /* + * Finally pass this packet up to higher layers. + */ + ether_input(&ie->arpcom.ac_if, &eh, m); +} + +static void ie_drop_packet_buffer(int unit, struct ie_softc *ie) { + int i; + + do { + /* + * This means we are somehow out of sync. So, we reset the + * adapter. + */ + if(!(ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_USED)) { +#ifdef DEBUG + print_rbd(ie->rbuffs[ie->rbhead]); +#endif + log(LOG_ERR, "ie%d: receive descriptors out of sync at %d\n", + unit, ie->rbhead); + iereset(unit, 0); + return; + } + + i = ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_LAST; + + ie->rbuffs[ie->rbhead]->ie_rbd_length |= IE_RBD_LAST; + ie->rbuffs[ie->rbhead]->ie_rbd_actual = 0; + ie->rbhead = (ie->rbhead + 1) % NBUFFS; + ie->rbuffs[ie->rbtail]->ie_rbd_length &= ~IE_RBD_LAST; + ie->rbtail = (ie->rbtail + 1) % NBUFFS; + } while(!i); +} + + +/* + * Start transmission on an interface. + */ +int iestart(ifp) + struct ifnet *ifp; +{ + struct ie_softc *ie = &ie_softc[ifp->if_unit]; + struct mbuf *m0, *m; + unsigned char *buffer; + u_short len; + /* This is not really volatile, in this routine, but it makes gcc happy. */ + volatile u_short *bptr = &ie->scb->ie_command_list; + + if(!(ifp->if_flags & IFF_RUNNING)) + return 0; + if(ifp->if_flags & IFF_OACTIVE) + return 0; + + do { + IF_DEQUEUE(&ie->arpcom.ac_if.if_snd, m); + if(!m) + break; + + buffer = ie->xmit_cbuffs[ie->xmit_count]; + len = 0; + + for(m0 = m; m && len < IE_BUF_LEN; m = m->m_next) { + bcopy(mtod(m, caddr_t), buffer, m->m_len); + buffer += m->m_len; + len += m->m_len; + } + + m_freem(m0); + len = MAX(len, ETHERMINLEN); + +#if NBPFILTER > 0 + /* + * See if bpf is listening on this interface, let it see the packet + * before we commit it to the wire. + */ + if(ie->ie_bpf) + bpf_tap(ie->ie_bpf, ie->xmit_cbuffs[ie->xmit_count], len); +#endif + + ie->xmit_buffs[ie->xmit_count]->ie_xmit_flags = IE_XMIT_LAST | len; + ie->xmit_buffs[ie->xmit_count]->ie_xmit_next = 0xffff; + ie->xmit_buffs[ie->xmit_count]->ie_xmit_buf = + MK_24(ie->iomem, ie->xmit_cbuffs[ie->xmit_count]); + + ie->xmit_cmds[ie->xmit_count]->com.ie_cmd_cmd = IE_CMD_XMIT; + ie->xmit_cmds[ie->xmit_count]->ie_xmit_status = 0; + ie->xmit_cmds[ie->xmit_count]->ie_xmit_desc = + MK_16(ie->iomem, ie->xmit_buffs[ie->xmit_count]); + + *bptr = MK_16(ie->iomem, ie->xmit_cmds[ie->xmit_count]); + bptr = &ie->xmit_cmds[ie->xmit_count]->com.ie_cmd_link; + ie->xmit_count++; + } while(ie->xmit_count < 2); + + /* + * If we queued up anything for transmission, send it. + */ + if(ie->xmit_count) { + ie->xmit_cmds[ie->xmit_count - 1]->com.ie_cmd_cmd |= + IE_CMD_LAST | IE_CMD_INTR; + + /* + * By passing the command pointer as a null, we tell + * command_and_wait() to pretend that this isn't an action + * command. I wish I understood what was happening here. + */ + command_and_wait(ifp->if_unit, IE_CU_START, 0, 0); + ifp->if_flags |= IFF_OACTIVE; + } + + return 0; +} + +/* + * Check to see if there's an 82586 out there. + */ +int check_ie_present(unit, where, size) + int unit; + caddr_t where; + unsigned size; +{ + volatile struct ie_sys_conf_ptr *scp; + volatile struct ie_int_sys_conf_ptr *iscp; + volatile struct ie_sys_ctl_block *scb; + u_long realbase; + int s; + + s = splimp(); + + realbase = (u_long)where + size - (1 << 24); + + scp = (volatile struct ie_sys_conf_ptr *)(realbase + IE_SCP_ADDR); + bzero((char *)scp, sizeof *scp); /* ignore cast-qual */ + + /* + * First we put the ISCP at the bottom of memory; this tests to make + * sure that our idea of the size of memory is the same as the controller's. + * This is NOT where the ISCP will be in normal operation. + */ + iscp = (volatile struct ie_int_sys_conf_ptr *)where; + bzero((char *)iscp, sizeof *iscp); /* ignore cast-qual */ + + scb = (volatile struct ie_sys_ctl_block *)where; + bzero((char *)scb, sizeof *scb); /* ignore cast-qual */ + + scp->ie_bus_use = 0; /* 16-bit */ + scp->ie_iscp_ptr = (caddr_t)((volatile caddr_t)iscp - /* ignore cast-qual */ + (volatile caddr_t)realbase); + + iscp->ie_busy = 1; + iscp->ie_scb_offset = MK_16(realbase, scb) + 256; + + (*ie_softc[unit].ie_reset_586)(unit); + (*ie_softc[unit].ie_chan_attn)(unit); + + DELAY(100); /* wait a while... */ + + if(iscp->ie_busy) { + splx(s); + return 0; + } + + /* + * Now relocate the ISCP to its real home, and reset the controller + * again. + */ + iscp = (void *)Align((caddr_t)(realbase + IE_SCP_ADDR - + sizeof(struct ie_int_sys_conf_ptr))); + bzero((char *)iscp, sizeof *iscp); /* ignore cast-qual */ + + scp->ie_iscp_ptr = (caddr_t)((caddr_t)iscp - (caddr_t)realbase); + /* ignore cast-qual */ + + iscp->ie_busy = 1; + iscp->ie_scb_offset = MK_16(realbase, scb); + + (*ie_softc[unit].ie_reset_586)(unit); + (*ie_softc[unit].ie_chan_attn)(unit); + + DELAY(100); + + if(iscp->ie_busy) { + splx(s); + return 0; + } + + ie_softc[unit].iosize = size; + ie_softc[unit].iomem = (caddr_t)realbase; + + ie_softc[unit].iscp = iscp; + ie_softc[unit].scb = scb; + + /* + * Acknowledge any interrupts we may have caused... + */ + ie_ack(scb, IE_ST_WHENCE, unit, ie_softc[unit].ie_chan_attn); + splx(s); + + return 1; +} + +/* + * Divine the memory size of ie board UNIT. + * Better hope there's nothing important hiding just below the ie card... + */ +static void find_ie_mem_size(unit) + int unit; +{ + unsigned size; + + ie_softc[unit].iosize = 0; + + for(size = 65536; size >= 16384; size -= 16384) { + if(check_ie_present(unit, ie_softc[unit].iomembot, size)) { + return; + } + } + + return; +} + +void sl_reset_586(unit) + int unit; +{ + outb(PORT + IEATT_RESET, 0); +} + +void sl_chan_attn(unit) + int unit; +{ + outb(PORT + IEATT_ATTN, 0); +} + +void sl_read_ether(unit, addr) + int unit; + unsigned char addr[6]; +{ + int i; + + for(i = 0; i < 6; i++) + addr[i] = inb(PORT + i); +} + + +int iereset(unit, dummy) + int unit, dummy; +{ + int s = splimp(); + + if(unit >= NIE) { + splx(s); + return -1; + } + + printf("ie%d: reset\n", unit); + ie_softc[unit].arpcom.ac_if.if_flags &= ~IFF_UP; + ieioctl(&ie_softc[unit].arpcom.ac_if, SIOCSIFFLAGS, 0); + + /* + * Stop i82586 dead in its tracks. + */ + if(command_and_wait(unit, IE_RU_ABORT | IE_CU_ABORT, 0, 0)) + printf("ie%d: abort commands timed out\n", unit); + + if(command_and_wait(unit, IE_RU_DISABLE | IE_CU_STOP, 0, 0)) + printf("ie%d: disable commands timed out\n", unit); + +#ifdef notdef + if(!check_ie_present(unit, ie_softc[unit].iomembot, ie_softc[unit].iosize)) + panic("ie disappeared!\n"); +#endif + + ie_softc[unit].arpcom.ac_if.if_flags |= IFF_UP; + ieioctl(&ie_softc[unit].arpcom.ac_if, SIOCSIFFLAGS, 0); + + splx(s); + return 0; +} + +/* + * This is called if we time out. + */ +static int chan_attn_timeout(rock) + caddr_t rock; +{ + *(int *)rock = 1; + return 0; +} + +/* + * Send a command to the controller and wait for it to either + * complete or be accepted, depending on the command. If the + * command pointer is null, then pretend that the command is + * not an action command. If the command pointer is not null, + * and the command is an action command, wait for + * ((volatile struct ie_cmd_common *)pcmd)->ie_cmd_status & MASK + * to become true. + */ +static int command_and_wait(unit, cmd, pcmd, mask) + int unit; + int cmd; + volatile void *pcmd; + int mask; +{ + volatile struct ie_cmd_common *cc = pcmd; + volatile int timedout = 0; + extern int hz; + + ie_softc[unit].scb->ie_command = (u_short)cmd; + + if(IE_ACTION_COMMAND(cmd) && pcmd) { + (*ie_softc[unit].ie_chan_attn)(unit); + + /* + * According to the packet driver, the minimum timeout should be + * .369 seconds, which we round up to .37. + */ + timeout(chan_attn_timeout, (caddr_t)&timedout, 37 * hz / 100); + /* ignore cast-qual */ + + /* + * Now spin-lock waiting for status. This is not a very nice + * thing to do, but I haven't figured out how, or indeed if, we + * can put the process waiting for action to sleep. (We may + * be getting called through some other timeout running in the + * kernel.) + */ + while(1) { + if((cc->ie_cmd_status & mask) || timedout) + break; + } + + untimeout(chan_attn_timeout, (caddr_t)&timedout); + /* ignore cast-qual */ + + return timedout; + } else { + + /* + * Otherwise, just wait for the command to be accepted. + */ + (*ie_softc[unit].ie_chan_attn)(unit); + + while(ie_softc[unit].scb->ie_command) + ; /* spin lock */ + + return 0; + } +} + +/* + * Run the time-domain reflectometer... + */ +static void run_tdr(unit, cmd) + int unit; + struct ie_tdr_cmd *cmd; +{ + int result; + + cmd->com.ie_cmd_status = 0; + cmd->com.ie_cmd_cmd = IE_CMD_TDR | IE_CMD_LAST; + cmd->com.ie_cmd_link = 0xffff; + cmd->ie_tdr_time = 0; + + ie_softc[unit].scb->ie_command_list = MK_16(MEM, cmd); + cmd->ie_tdr_time = 0; + + if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL)) + result = 0x2000; + else + result = cmd->ie_tdr_time; + + ie_ack(ie_softc[unit].scb, IE_ST_WHENCE, unit, + ie_softc[unit].ie_chan_attn); + + if(result & IE_TDR_SUCCESS) + return; + + if(result & IE_TDR_XCVR) { + printf("ie%d: transceiver problem\n", unit); + } else if(result & IE_TDR_OPEN) { + printf("ie%d: TDR detected an open %d clocks away\n", unit, + result & IE_TDR_TIME); + } else if(result & IE_TDR_SHORT) { + printf("ie%d: TDR detected a short %d clocks away\n", unit, + result & IE_TDR_TIME); + } else { + printf("ie%d: TDR returned unknown status %x\n", result); + } +} + +static void start_receiver(unit) + int unit; +{ + int s = splimp(); + + ie_softc[unit].scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]); + command_and_wait(unit, IE_RU_START, 0, 0); + + ie_ack(ie_softc[unit].scb, IE_ST_WHENCE, unit, ie_softc[unit].ie_chan_attn); + + splx(s); +} + +/* + * Here is a helper routine for iernr() and ieinit(). This sets up + * the RFA. + */ +static caddr_t setup_rfa(caddr_t ptr, struct ie_softc *ie) { + volatile struct ie_recv_frame_desc *rfd = (void *)ptr; + volatile struct ie_recv_buf_desc *rbd; + int i; + int unit = ie - &ie_softc[0]; + + /* First lay them out */ + for(i = 0; i < NFRAMES; i++) { + ie->rframes[i] = rfd; + bzero((char *)rfd, sizeof *rfd); /* ignore cast-qual */ + rfd++; + } + + ptr = (caddr_t)Align((caddr_t)rfd); /* ignore cast-qual */ + + /* Now link them together */ + for(i = 0; i < NFRAMES; i++) { + ie->rframes[i]->ie_fd_next = + MK_16(MEM, ie->rframes[(i + 1) % NFRAMES]); + } + + /* Finally, set the EOL bit on the last one. */ + ie->rframes[NFRAMES - 1]->ie_fd_last |= IE_FD_LAST; + + /* + * Now lay out some buffers for the incoming frames. Note that + * we set aside a bit of slop in each buffer, to make sure that + * we have enough space to hold a single frame in every buffer. + */ + rbd = (void *)ptr; + + for(i = 0; i < NBUFFS; i++) { + ie->rbuffs[i] = rbd; + bzero((char *)rbd, sizeof *rbd); /* ignore cast-qual */ + ptr = (caddr_t)Align(ptr + sizeof *rbd); + rbd->ie_rbd_length = IE_RBUF_SIZE; + rbd->ie_rbd_buffer = MK_24(MEM, ptr); + ie->cbuffs[i] = (void *)ptr; + ptr += IE_RBUF_SIZE; + rbd = (void *)ptr; + } + + /* Now link them together */ + for(i = 0; i < NBUFFS; i++) { + ie->rbuffs[i]->ie_rbd_next = MK_16(MEM, ie->rbuffs[(i + 1) % NBUFFS]); + } + + /* Tag EOF on the last one */ + ie->rbuffs[NBUFFS - 1]->ie_rbd_length |= IE_RBD_LAST; + + /* We use the head and tail pointers on receive to keep track of + * the order in which RFDs and RBDs are used. */ + ie->rfhead = 0; + ie->rftail = NFRAMES - 1; + ie->rbhead = 0; + ie->rbtail = NBUFFS - 1; + + ie->scb->ie_recv_list = MK_16(MEM, ie->rframes[0]); + ie->rframes[0]->ie_fd_buf_desc = MK_16(MEM, ie->rbuffs[0]); + + ptr = Align(ptr); + return ptr; +} + +/* + * Run the multicast setup command. + * Call at splimp(). + */ +static int mc_setup(int unit, caddr_t ptr, + volatile struct ie_sys_ctl_block *scb) { + struct ie_softc *ie = &ie_softc[unit]; + volatile struct ie_mcast_cmd *cmd = (void *)ptr; + + cmd->com.ie_cmd_status = 0; + cmd->com.ie_cmd_cmd = IE_CMD_MCAST | IE_CMD_LAST; + cmd->com.ie_cmd_link = 0xffff; + + /* ignore cast-qual */ + bcopy((caddr_t)ie->mcast_addrs, (caddr_t)cmd->ie_mcast_addrs, + ie->mcast_count * sizeof *ie->mcast_addrs); + + cmd->ie_mcast_bytes = ie->mcast_count * 6; /* grrr... */ + + scb->ie_command_list = MK_16(MEM, cmd); + if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL) + || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { + printf("ie%d: multicast address setup command failed\n", unit); + return 0; + } + return 1; +} + +/* + * This routine takes the environment generated by check_ie_present() + * and adds to it all the other structures we need to operate the adapter. + * This includes executing the CONFIGURE, IA-SETUP, and MC-SETUP commands, + * starting the receiver unit, and clearing interrupts. + * + * THIS ROUTINE MUST BE CALLED AT splimp() OR HIGHER. + */ +int ieinit(unit) + int unit; +{ + struct ie_softc *ie = &ie_softc[unit]; + volatile struct ie_sys_ctl_block *scb = ie->scb; + caddr_t ptr; + + ptr = (caddr_t)Align((caddr_t)scb + sizeof *scb); /* ignore cast-qual */ + + /* + * Send the configure command first. + */ + { + volatile struct ie_config_cmd *cmd = (void *)ptr; + + ie_setup_config(cmd, ie->promisc, ie->hard_type == IE_STARLAN10); + cmd->com.ie_cmd_status = 0; + cmd->com.ie_cmd_cmd = IE_CMD_CONFIG | IE_CMD_LAST; + cmd->com.ie_cmd_link = 0xffff; + + scb->ie_command_list = MK_16(MEM, cmd); + + if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL) + || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { + printf("ie%d: configure command failed\n", unit); + return 0; + } + } + /* + * Now send the Individual Address Setup command. + */ + { + volatile struct ie_iasetup_cmd *cmd = (void *)ptr; + + cmd->com.ie_cmd_status = 0; + cmd->com.ie_cmd_cmd = IE_CMD_IASETUP | IE_CMD_LAST; + cmd->com.ie_cmd_link = 0xffff; + + bcopy((char *)ie_softc[unit].arpcom.ac_enaddr, (char *)&cmd->ie_address, + sizeof cmd->ie_address); /* ignore cast-qual */ + + scb->ie_command_list = MK_16(MEM, cmd); + if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL) + || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { + printf("ie%d: individual address setup command failed\n", unit); + return 0; + } + } + + /* + * Now run the time-domain reflectometer. + */ + run_tdr(unit, (void *)ptr); + + /* + * Acknowledge any interrupts we have generated thus far. + */ + ie_ack(ie->scb, IE_ST_WHENCE, unit, ie->ie_chan_attn); + + /* + * Set up the RFA. + */ + ptr = setup_rfa(ptr, ie); + + /* + * Finally, the transmit command and buffer are the last little bit of work. + */ + ie->xmit_cmds[0] = (void *)ptr; + ptr += sizeof *ie->xmit_cmds[0]; + ptr = Align(ptr); + ie->xmit_buffs[0] = (void *)ptr; + ptr += sizeof *ie->xmit_buffs[0]; + ptr = Align(ptr); + + /* Second transmit command */ + ie->xmit_cmds[1] = (void *)ptr; + ptr += sizeof *ie->xmit_cmds[1]; + ptr = Align(ptr); + ie->xmit_buffs[1] = (void *)ptr; + ptr += sizeof *ie->xmit_buffs[1]; + ptr = Align(ptr); + + /* Both transmit buffers */ + ie->xmit_cbuffs[0] = (void *)ptr; + ptr += IE_BUF_LEN; + ptr = Align(ptr); + ie->xmit_cbuffs[1] = (void *)ptr; + + bzero((caddr_t)ie->xmit_cmds[0], sizeof *ie->xmit_cmds[0]); /* ignore */ + bzero((caddr_t)ie->xmit_buffs[0], sizeof *ie->xmit_buffs[0]); /* cast-qual */ + bzero((caddr_t)ie->xmit_cmds[1], sizeof *ie->xmit_cmds[0]); /* warnings */ + bzero((caddr_t)ie->xmit_buffs[1], sizeof *ie->xmit_buffs[0]); /* here */ + + /* + * This must be coordinated with iestart() and ietint(). + */ + ie->xmit_cmds[0]->ie_xmit_status = IE_STAT_COMPL; + + ie->arpcom.ac_if.if_flags |= IFF_RUNNING; /* tell higher levels that we are here */ + start_receiver(unit); + return 0; +} + +static void ie_stop(unit) + int unit; +{ + command_and_wait(unit, IE_RU_DISABLE, 0, 0); +} + +int ieioctl(ifp, command, data) + struct ifnet *ifp; + int command; + void *data; +{ + struct ifaddr *ifa = (struct ifaddr *)data; + struct ie_softc *ie = &ie_softc[ifp->if_unit]; + int s, error = 0; + + s = splimp(); + + switch(command) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch(ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + ieinit(ifp->if_unit); + ((struct arpcom *)ifp)->ac_ipaddr = + IA_SIN(ifa)->sin_addr; + arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); + break; +#endif /* INET */ + +#ifdef NS + /* This magic copied from if_is.c; I don't use XNS, so I have no + * way of telling if this actually works or not. + */ + case AF_NS: + { + struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + + if(ns_nullhost(*ina)) { + ina->x_host = *(union ns_host *)(ie->arpcom.ac_enaddr); + } else { + ifp->if_flags &= ~IFF_RUNNING; + bcopy((caddr_t)ina->x_host.c_host, + (caddr_t)ie->arpcom.ac_enaddr, + sizeof ie->arpcom.ac_enaddr); + } + + ieinit(ifp->if_unit); + } + break; +#endif /* NS */ + + default: + ieinit(ifp->if_unit); + break; + } + break; + + case SIOCSIFFLAGS: + /* + * Note that this device doesn't have an "all multicast" mode, so we + * must turn on promiscuous mode and do the filtering manually. + */ + if((ifp->if_flags & IFF_UP) == 0 && + (ifp->if_flags & IFF_RUNNING)) { + ifp->if_flags &= ~IFF_RUNNING; + ie_stop(ifp->if_unit); + } else if((ifp->if_flags & IFF_UP) && + (ifp->if_flags & IFF_RUNNING) == 0) { + ie_softc[ifp->if_unit].promisc = + ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI); + ieinit(ifp->if_unit); + } else if(ie_softc[ifp->if_unit].promisc ^ + (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI))) { + ie_softc[ifp->if_unit].promisc = + ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI); + ieinit(ifp->if_unit); + } + break; + +#ifdef MULTICAST + case SIOCADDMULTI: + case SIOCDELMULTI: + /* + * Update multicast listeners + */ + error = ((command == SIOCADDMULTI) + ? ether_addmulti((struct ifreq *)data, &ie->arpcom) + : ether_delmulti((struct ifreq *)data, &ie->arpcom)); + + if(error == ENETRESET) { + /* reset multicast filtering */ + ie_mc_reset(ifp->if_unit); + error = 0; + } + break; +#endif /* MULTICAST */ + + default: + error = EINVAL; + } + + splx(s); + return error; +} + +#ifdef MULTICAST +static void ie_mc_reset(int unit) { + struct ie_softc *ie = &ie_softc[unit]; + struct ether_multi *enm; + struct ether_multistep step; + + /* + * Step through the list of addresses. + */ + ie->mcast_count = 0; + ETHER_FIRST_MULTI(step, &ie->arpcom, enm); + while(enm) { + if(ie->mcast_count >= MAXMCAST + || bcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) { + ie->arpcom.ac_if.if_flags |= IFF_ALLMULTI; + ieioctl(&ie->arpcom.ac_if, SIOCSIFFLAGS, (void *)0); + goto setflag; + } + + bcopy(enm->enm_addrlo, &(ie->mcast_addrs[ie->mcast_count]), 6); + ie->mcast_count++; + ETHER_NEXT_MULTI(step, enm); + } + +setflag: + ie->want_mcsetup = 1; +} + +#endif + +#ifdef DEBUG +void print_rbd(volatile struct ie_recv_buf_desc *rbd) { + printf("RBD at %08lx:\n" + "actual %04x, next %04x, buffer %08x\n" + "length %04x, mbz %04x\n", + (unsigned long)rbd, + rbd->ie_rbd_actual, rbd->ie_rbd_next, rbd->ie_rbd_buffer, + rbd->ie_rbd_length, rbd->mbz); +} +#endif /* DEBUG */ +#endif /* NIE > 0 */ + diff --git a/sys/i386/isa/if_iereg.h b/sys/i386/isa/if_iereg.h new file mode 100644 index 0000000000..3588b84140 --- /dev/null +++ b/sys/i386/isa/if_iereg.h @@ -0,0 +1,24 @@ +/* + * $Id$ + * definitions for AT&T StarLAN 10 etc... + */ + +#define IEATT_RESET 0 /* any write here resets the 586 */ +#define IEATT_ATTN 1 /* any write here sends a Chan attn */ +#define IEATT_REVISION 6 /* read here to figure out this board */ +#define IEATT_ATTRIB 7 /* more information about this board */ + +#define SL_BOARD(x) ((x) & 0x0f) +#define SL_REV(x) ((x) >> 4) + +#define SL1_BOARD 0 +#define SL10_BOARD 1 +#define EN100_BOARD 2 +#define SLFIBER_BOARD 3 + +#define SL_ATTR_WIDTH 0x04 /* bus width: clear -> 8-bit */ +#define SL_ATTR_SPEED 0x08 /* medium speed: clear -> 10 Mbps */ +#define SL_ATTR_CODING 0x10 /* encoding: clear -> Manchester */ +#define SL_ATTR_HBW 0x20 /* host bus width: clear -> 16-bit */ +#define SL_ATTR_TYPE 0x40 /* medium type: clear -> Ethernet */ +#define SL_ATTR_BOOTROM 0x80 /* set -> boot ROM present */ diff --git a/sys/i386/isa/if_is.c b/sys/i386/isa/if_is.c index df164d6260..becfe17487 100644 --- a/sys/i386/isa/if_is.c +++ b/sys/i386/isa/if_is.c @@ -1,59 +1,43 @@ -/*- - * Copyright (c) 1990, 1991 William F. Jolitz. - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. +/* + * Isolan AT 4141-0 Ethernet driver + * Isolink 4110 * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. + * By Paul Richards * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * Copyright (C) 1993, Paul Richards. This software may be used, modified, + * copied, distributed, and sold, in both source and binary form provided + * that the above copyright and these terms are retained. Under no + * circumstances is the author responsible for the proper functioning + * of this software, nor does the author assume any responsibility + * for damages incurred with its use. * - * @(#)if_ne.c 7.4 (Berkeley) 5/21/91 - */ +*/ -/* - * Isolink 4110-2 Ethernet driver - */ +/* TODO + +1) Add working multicast support +2) Use better allocation of memory to card +3) Advertise for more packets until all transmit buffers are full +4) Add more of the timers/counters e.g. arpcom.opackets etc. +*/ #include "is.h" #if NIS > 0 +#include "bpfilter.h" + #include "param.h" #include "systm.h" +#include "errno.h" +#include "ioctl.h" #include "mbuf.h" -#include "buf.h" -#include "protosw.h" #include "socket.h" -#include "ioctl.h" -#include "errno.h" #include "syslog.h" #include "net/if.h" +#include "net/if_dl.h" +#include "net/if_types.h" #include "net/netisr.h" -#include "net/route.h" #ifdef INET #include "netinet/in.h" @@ -68,87 +52,109 @@ #include "netns/ns_if.h" #endif +#if NBPFILTER > 0 +#include "net/bpf.h" +#include "net/bpfdesc.h" +#endif + #include "i386/isa/isa_device.h" #include "i386/isa/if_isreg.h" #include "i386/isa/icu.h" #include "vm/vm.h" -/* Function prototypes */ -int isprobe(), isattach(); -int isioctl(),isinit(),isstart(); - -struct isa_driver isdriver = { - isprobe, isattach, "is", -}; - - -struct mbuf *isget(); - #define ETHER_MIN_LEN 64 +#define ETHER_MAX_LEN 1518 +#define ETHER_ADDR_LEN 6 + /* * Ethernet software status per interface. * * Each interface is referenced by a network interface structure, - * ns_if, which the routing code uses to locate the interface. + * arpcom.ac_if, which the routing code uses to locate the interface. * This structure contains the output queue for the interface, its address, ... */ struct is_softc { - struct arpcom ns_ac; /* Ethernet common part */ -#define ns_if ns_ac.ac_if /* network-visible interface */ -#define ns_addr ns_ac.ac_enaddr /* hardware Ethernet address */ + struct arpcom arpcom; /* Ethernet common part */ + int iobase; /* IO base address of card */ + struct init_block *init_block; /* Lance initialisation block */ + struct mds *rd; + struct mds *td; + unsigned char *rbuf; + unsigned char *tbuf; int last_rd; int last_td; int no_td; + caddr_t bpf; /* BPF "magic cookie" */ + } is_softc[NIS] ; -struct init_block init_block; -struct mds *td, *rd; -unsigned char *rbuf,*tbuf; +int is_debug; -int isc; +/* Function prototypes */ +int is_probe(),is_attach(),is_watchdog(); +int is_ioctl(),is_init(),is_start(); -iswrcsr(port,val) +static inline void is_rint(int unit); +static inline void isread(struct is_softc*, unsigned char*, int); + +struct mbuf *isget(); + +struct isa_driver isdriver = { + is_probe, + is_attach, + "is" +}; + +iswrcsr(unit,port,val) + int unit; u_short port; u_short val; { - outw(isc+RAP,port); - outw(isc+RDP,val); + int iobase; + + iobase = is_softc[unit].iobase; + outw(iobase+RAP,port); + outw(iobase+RDP,val); } -u_short isrdcsr(port) +u_short isrdcsr(unit,port) + int unit; u_short port; { - outw(isc+RAP,port); - return(inw(isc+RDP)); + int iobase; + + iobase = is_softc[unit].iobase; + outw(iobase+RAP,port); + return(inw(iobase+RDP)); } -isprobe(dvp) - struct isa_device *dvp; +is_probe(isa_dev) + struct isa_device *isa_dev; { int val,i,s; - register struct is_softc *ns = &is_softc[0]; + int unit = isa_dev->id_unit ; + register struct is_softc *is = &is_softc[unit]; - isc = dvp->id_iobase; - s = splimp(); + is->iobase = isa_dev->id_iobase; - /* Stop the lance chip, put it known state */ - iswrcsr(0,STOP); + /* Stop the lance chip, put it in known state */ + iswrcsr(unit,0,STOP); DELAY(100); /* is there a lance? */ - iswrcsr(3, 0xffff); - if (isrdcsr(3) != 7) { - isc = 0; + iswrcsr(unit,3, 0xffff); + if (isrdcsr(unit,3) != 7) { + is->iobase = 0; return (0); } - iswrcsr(3, 0); + iswrcsr(unit,3, 0); /* Extract board address */ - for(i=0; i < 6; i++) ns->ns_addr[i] = inb(isc+(i*2)); + for(i=0;iarpcom.ac_enaddr[i]=inb(is->iobase+(i*2)); - splx(s); return (1); } @@ -157,13 +163,16 @@ isprobe(dvp) /* * Reset of interface. */ -isreset(unit, uban) - int unit, uban; +int +is_reset(int unit) { + int s; + struct is_softc *is = &is_softc[unit]; + if (unit >= NIS) return; printf("is%d: reset\n", unit); - isinit(unit); + is_init(unit); } /* @@ -171,87 +180,169 @@ isreset(unit, uban) * record. System will initialize the interface when it is ready * to accept packets. We get the ethernet address here. */ -isattach(dvp) - struct isa_device *dvp; +int +is_attach(isa_dev) + struct isa_device *isa_dev; { - int unit = dvp->id_unit; - register struct is_softc *is = &is_softc[unit]; - register struct ifnet *ifp = &is->ns_if; - - /* Set up DMA */ - isa_dmacascade(dvp->id_drq); + int unit = isa_dev->id_unit; + struct is_softc *is = &is_softc[unit]; + struct ifnet *ifp = &is->arpcom.ac_if; + struct ifaddr *ifa; + struct sockaddr_dl *sdl; ifp->if_unit = unit; ifp->if_name = isdriver.name ; ifp->if_mtu = ETHERMTU; - printf (" ethernet address %s", ether_sprintf(is->ns_addr)) ; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS; - ifp->if_init = isinit; + ifp->if_init = is_init; ifp->if_output = ether_output; - ifp->if_start = isstart; - ifp->if_ioctl = isioctl; - ifp->if_reset = isreset; - ifp->if_watchdog = 0; + ifp->if_start = is_start; + ifp->if_ioctl = is_ioctl; + ifp->if_reset = is_reset; + ifp->if_watchdog = is_watchdog; + + /* + * XXX -- not sure this is right place to do this + * Allocate memory for use by Lance + * Memory allocated for: + * initialisation block, + * ring descriptors, + * transmit and receive buffers. + */ + + /* + * XXX - hopefully have better way to get dma'able memory later, + * this code assumes that the physical memory address returned + * from malloc will be below 16Mb. The Lance's address registers + * are only 16 bits wide! + */ + +#define MAXMEM ((NRBUF+NTBUF)*(BUFSIZE) + (NRBUF+NTBUF)*sizeof(struct mds) \ + + sizeof(struct init_block) + 8) + is->init_block = (struct init_block *)malloc(MAXMEM,M_TEMP,M_NOWAIT); + if (!is->init_block) { + printf("is%d : Couldn't allocate memory for card\n",unit); + } + /* + * XXX -- should take corrective action if not + * quadword alilgned, the 8 byte slew factor in MAXMEM + * allows for this. + */ + + if ((u_long)is->init_block & 0x3) + printf("is%d: memory allocated not quadword aligned\n"); + + /* Set up DMA */ + isa_dmacascade(isa_dev->id_drq); + if_attach(ifp); + + /* + * Search down the ifa address list looking + * for the AF_LINK type entry + */ + + ifa = ifp->if_addrlist; + while ((ifa != 0) && (ifa->ifa_addr != 0) && + (ifa->ifa_addr->sa_family != AF_LINK)) + ifa = ifa->ifa_next; + + /* + * If we find an AF_LINK type entry, we will fill + * in the hardware address for this interface. + */ + + if ((ifa != 0) && (ifa->ifa_addr != 0)) { + + /* + * Fill in the link level address for this interface + */ + + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_alen = ETHER_ADDR_LEN; + sdl->sdl_slen = 0; + bcopy(is->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN); + } + + printf ("is%d: address %s\n", unit, + ether_sprintf(is->arpcom.ac_enaddr)) ; + +#if NBPFILTER > 0 + bpfattach(&is->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif +} + +int +is_watchdog(unit) + int unit; +{ + log(LOG_ERR, "is%d: device timeout\n", unit); + is_reset(unit); } /* Lance initialisation block set up */ -init_mem() +init_mem(unit) + int unit; { int i; - u_long temp; + void *temp; + struct is_softc *is = &is_softc[unit]; - /* Allocate memory */ -/* Temporary hack, will use kmem_alloc in future */ -#define MAXMEM ((NRBUF+NTBUF)*(BUFSIZE) + (NRBUF+NTBUF)*sizeof(struct mds) + 8) -static u_char lance_mem[MAXMEM]; + /* + * At this point we assume that the + * memory allocated to the Lance is + * quadword aligned. If it isn't + * then the initialisation is going + * fail later on. + */ - /* Align message descriptors on quad word boundary - (this is essential) */ + /* + * Set up lance initialisation block + */ - temp = (u_long) &lance_mem; - temp = (temp+8) - (temp%8); - rd = (struct mds *) temp; - td = (struct mds *) (temp + (NRBUF*sizeof(struct mds))); + temp = (void *)is->init_block; + temp += sizeof(struct init_block); + is->rd = (struct mds *) temp; + is->td = (struct mds *) (temp + (NRBUF*sizeof(struct mds))); temp += (NRBUF+NTBUF) * sizeof(struct mds); - init_block.mode = 0; - - /* Get ethernet address */ - for (i=0; i<6; i++) - init_block.padr[i] = inb(isc+(i*2)); + is->init_block->mode = 0; + for (i=0; iinit_block->padr[i] = is->arpcom.ac_enaddr[i]; + for (i = 0; i < 8; ++i) + is->init_block->ladrf[i] = MULTI_INIT_ADDR; + is->init_block->rdra = kvtop(is->rd); + is->init_block->rlen = ((kvtop(is->rd) >> 16) & 0xff) | (RLEN<<13); + is->init_block->tdra = kvtop(is->td); + is->init_block->tlen = ((kvtop(is->td) >> 16) & 0xff) | (TLEN<<13); - /* Clear multicast address for now */ - for (i=0; i<8; i++) - init_block.ladrf[i] = 0; - init_block.rdra = kvtop(rd); - init_block.rlen = ((kvtop(rd) >> 16) & 0xff) | (RLEN<<13); - init_block.tdra = kvtop(td); - init_block.tlen = ((kvtop(td) >> 16) & 0xff) | (TLEN<<13); + /* + * Set up receive ring descriptors + */ - /* Set up receive ring descriptors */ - rbuf = (unsigned char *)temp; + is->rbuf = (unsigned char *)temp; for (i=0; iaddr = kvtop(temp); - (rd+i)->flags= ((kvtop(temp) >> 16) & 0xff) | OWN; - (rd+i)->bcnt = -BUFSIZE; - (rd+i)->mcnt = 0; + (is->rd+i)->addr = kvtop(temp); + (is->rd+i)->flags= ((kvtop(temp) >> 16) & 0xff) | OWN; + (is->rd+i)->bcnt = -BUFSIZE; + (is->rd+i)->mcnt = 0; temp += BUFSIZE; } - /* Set up transmit ring descriptors */ - tbuf = (unsigned char *)temp; -#ifdef ISDEBUG - printf("rd = %x,td = %x, rbuf = %x, tbuf = %x,td+1=%x\n",rd,td,rbuf,tbuf,td+1); -#endif + /* + * Set up transmit ring descriptors + */ + + is->tbuf = (unsigned char *)temp; for (i=0; iaddr = kvtop(temp); - (td+i)->flags= ((kvtop(temp) >> 16) & 0xff); - (td+i)->bcnt = 0; - (td+i)->mcnt = 0; + (is->td+i)->addr = kvtop(temp); + (is->td+i)->flags= ((kvtop(temp) >> 16) & 0xff); + (is->td+i)->bcnt = 0; + (is->td+i)->mcnt = 0; temp += BUFSIZE; } @@ -261,50 +352,59 @@ static u_char lance_mem[MAXMEM]; * Initialization of interface; set up initialization block * and transmit/receive descriptor rings. */ -isinit(unit) + +is_init(unit) int unit; { - register struct is_softc *ns = &is_softc[unit]; - struct ifnet *ifp = &ns->ns_if; + register struct is_softc *is = &is_softc[unit]; + struct ifnet *ifp = &is->arpcom.ac_if; int s; register i; + /* Address not known */ if (ifp->if_addrlist == (struct ifaddr *)0) return; - ns->last_rd = ns->last_td = ns->no_td = 0; - s = splimp(); + s = splnet(); - /* Set up lance's memory area */ - init_mem(); + /* + * Lance must be stopped + * to access registers. + */ + + iswrcsr(unit,0,STOP); - /* Stop Lance to get access to other registers */ - iswrcsr(0,STOP); + is->last_rd = is->last_td = is->no_td = 0; - /* I wish I knew what this was */ - iswrcsr(3,0); + /* Set up lance's memory area */ + init_mem(unit); + + /* No byte swapping etc */ + iswrcsr(unit,3,0); /* Give lance the physical address of its memory area */ - iswrcsr(1,kvtop(&init_block)); - iswrcsr(2,(kvtop(&init_block) >> 16) & 0xff); + iswrcsr(unit,1,kvtop(is->init_block)); + iswrcsr(unit,2,(kvtop(is->init_block) >> 16) & 0xff); /* OK, let's try and initialise the Lance */ - iswrcsr(0,INIT); + iswrcsr(unit,0,INIT); /* Wait for initialisation to finish */ for(i=0; i<1000; i++){ - if (isrdcsr(0)&IDON) + if (isrdcsr(unit,0)&IDON) break; } - if (isrdcsr(0)&IDON) { + if (isrdcsr(unit,0)&IDON) { /* Start lance */ - iswrcsr(0,STRT|IDON|INEA); - ns->ns_if.if_flags |= IFF_RUNNING; - isstart(ifp); + iswrcsr(unit,0,STRT|IDON|INEA); + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + is_start(ifp); } else - printf("Isolink card failed to initialise\n"); + printf("is%d: card failed to initialise\n", unit); - splx(s); + (void) splx(s); } /* @@ -313,10 +413,11 @@ isinit(unit) * and map it to the interface before starting the output. * called only at splimp or interrupt level. */ -isstart(ifp) +is_start(ifp) struct ifnet *ifp; { - register struct is_softc *ns = &is_softc[ifp->if_unit]; + int unit = ifp->if_unit; + register struct is_softc *is = &is_softc[unit]; struct mbuf *m0, *m; unsigned char *buffer; u_short len; @@ -324,15 +425,15 @@ isstart(ifp) struct mds *cdm; - if ((ns->ns_if.if_flags & IFF_RUNNING) == 0) + if ((is->arpcom.ac_if.if_flags & IFF_RUNNING) == 0) return; do { - cdm = (td + ns->last_td); + cdm = (is->td + is->last_td); if (cdm->flags&OWN) return; - IF_DEQUEUE(&ns->ns_if.if_snd, m); + IF_DEQUEUE(&is->arpcom.ac_if.if_snd, m); if (m == 0) return; @@ -341,13 +442,79 @@ isstart(ifp) * Copy the mbuf chain into the transmit buffer */ - buffer = tbuf+(BUFSIZE*ns->last_td); + buffer = is->tbuf+(BUFSIZE*is->last_td); len=0; for (m0=m; m != 0; m=m->m_next) { bcopy(mtod(m,caddr_t),buffer,m->m_len); buffer += m->m_len; len += m->m_len; } +#if NBPFILTER > 0 + if (is->bpf) { + u_short etype; + int off, datasize, resid; + struct ether_header *eh; + struct trailer_header { + u_short ether_type; + u_short ether_residual; + } trailer_header; + char ether_packet[ETHER_MAX_LEN]; + char *ep; + + ep = ether_packet; + + /* + * We handle trailers below: + * Copy ether header first, then residual data, + * then data. Put all this in a temporary buffer + * 'ether_packet' and send off to bpf. Since the + * system has generated this packet, we assume + * that all of the offsets in the packet are + * correct; if they're not, the system will almost + * certainly crash in m_copydata. + * We make no assumptions about how the data is + * arranged in the mbuf chain (i.e. how much + * data is in each mbuf, if mbuf clusters are + * used, etc.), which is why we use m_copydata + * to get the ether header rather than assume + * that this is located in the first mbuf. + */ + /* copy ether header */ + m_copydata(m0, 0, sizeof(struct ether_header), ep); + eh = (struct ether_header *) ep; + ep += sizeof(struct ether_header); + etype = ntohs(eh->ether_type); + if (etype >= ETHERTYPE_TRAIL && + etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { + datasize = ((etype - ETHERTYPE_TRAIL) << 9); + off = datasize + sizeof(struct ether_header); + + /* copy trailer_header into a data structure */ + m_copydata(m0, off, sizeof(struct trailer_header), + &trailer_header.ether_type); + + /* copy residual data */ + resid = trailer_header.ether_residual - + sizeof(struct trailer_header); + resid = ntohs(resid); + m_copydata(m0, off+sizeof(struct trailer_header), + resid, ep); + ep += resid; + + /* copy data */ + m_copydata(m0, sizeof(struct ether_header), + datasize, ep); + ep += datasize; + + /* restore original ether packet type */ + eh->ether_type = trailer_header.ether_type; + + bpf_tap(is->bpf, ether_packet, ep - ether_packet); + } else + bpf_mtap(is->bpf, m0); + } +#endif + m_freem(m0); len = MAX(len,ETHER_MIN_LEN); @@ -360,17 +527,19 @@ isstart(ifp) cdm->bcnt = -len; cdm->mcnt = 0; #ifdef ISDEBUG - xmit_print(ns->last_td); + if (is_debug) + xmit_print(unit,is->last_td); #endif - iswrcsr(0,TDMD|INEA); - if (++ns->last_td >= NTBUF) - ns->last_td=0; - }while(++ns->no_td < NTBUF); - ns->no_td = NTBUF; - ns->ns_if.if_flags |= IFF_OACTIVE; -#ifdef ISDEBUG - printf("no_td = %x, last_td = %x\n",ns->no_td, ns->last_td); + iswrcsr(unit,0,TDMD|INEA); + if (++is->last_td >= NTBUF) + is->last_td=0; + }while(++is->no_td < NTBUF); + is->no_td = NTBUF; + is->arpcom.ac_if.if_flags |= IFF_OACTIVE; +#ifdef ISDEBUG + if (is_debug) + printf("no_td = %x, last_td = %x\n",is->no_td, is->last_td); #endif return(0); } @@ -381,34 +550,49 @@ isstart(ifp) */ isintr(unit) { - register struct is_softc *ns = &is_softc[unit]; + register struct is_softc *is = &is_softc[unit]; u_short isr; - while((isr=isrdcsr(0))&INTR) { + while((isr=isrdcsr(unit,0))&INTR) { if (isr&ERR) { - if (isr&BABL) - printf("BABL\n"); - if (isr&CERR) - printf("CERR\n"); - if (isr&MISS) - printf("MISS\n"); + if (isr&BABL){ + printf("is%d: BABL\n",unit); + is->arpcom.ac_if.if_oerrors++; + } + if (isr&CERR) { + printf("is%d: CERR\n",unit); + is->arpcom.ac_if.if_collisions++; + } + if (isr&MISS) { + printf("is%d: MISS\n",unit); + is->arpcom.ac_if.if_ierrors++; + } if (isr&MERR) - printf("MERR\n"); - iswrcsr(0,BABL|CERR|MISS|MERR|INEA); + printf("is%d: MERR\n",unit); + iswrcsr(unit,0,BABL|CERR|MISS|MERR|INEA); } - if (!(isr&TXON)) { - isreset(unit); + if (!(isr&RXON)) { + printf("is%d: !(isr&RXON)\n", unit); + is->arpcom.ac_if.if_ierrors++; + is_reset(unit); return(1); } - if (!(isr&RXON)) { - isreset(unit); + if (!(isr&TXON)) { + printf("is%d: !(isr&TXON)\n", unit); + is->arpcom.ac_if.if_oerrors++; + is_reset(unit); return(1); } + if (isr&RINT) { - isrint(unit); + /* reset watchdog timer */ + is->arpcom.ac_if.if_timer = 0; + is_rint(unit); } if (isr&TINT) { - iswrcsr(0,TINT|INEA); + /* reset watchdog timer */ + is->arpcom.ac_if.if_timer = 0; + iswrcsr(unit,0,TINT|INEA); istint(unit); } } @@ -418,16 +602,18 @@ istint(unit) int unit; { struct is_softc *is = &is_softc[unit]; - register struct ifnet *ifp = &is->ns_if; + register struct ifnet *ifp = &is->arpcom.ac_if; int i,loopcount=0; struct mds *cdm; + is->arpcom.ac_if.if_opackets++; do { if ((i=is->last_td - is->no_td) < 0) i+=NTBUF; - cdm = (td+i); + cdm = (is->td+i); #ifdef ISDEBUG - printf("Trans cdm = %x\n",cdm); + if (is_debug) + printf("Trans cdm = %x\n",cdm); #endif if (cdm->flags&OWN) { if (loopcount) @@ -435,120 +621,150 @@ istint(unit) return; } loopcount++; - is->ns_if.if_flags &= ~IFF_OACTIVE; + is->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; }while(--is->no_td > 0); - isstart(ifp); + is_start(ifp); } #define NEXTRDS \ - if (++rmd == NRBUF) rmd=0, cdm=rd; else ++cdm + if (++rmd == NRBUF) rmd=0, cdm=is->rd; else ++cdm -isrint(unit) - int unit; +/* only called from one place, so may as well integrate */ +static inline void is_rint(int unit) { register struct is_softc *is=&is_softc[unit]; register int rmd = is->last_rd; - struct mds *cdm = (rd + rmd); + struct mds *cdm = (is->rd + rmd); /* Out of sync with hardware, should never happen */ if (cdm->flags & OWN) { - printf("is0 error: out of sync\n"); - iswrcsr(0,RINT|INEA); + printf("is%d: error: out of sync\n",unit); + iswrcsr(unit,0,RINT|INEA); return; } /* Process all buffers with valid data */ while (!(cdm->flags&OWN)) { /* Clear interrupt to avoid race condition */ - iswrcsr(0,RINT|INEA); + iswrcsr(unit,0,RINT|INEA); if (cdm->flags&ERR) { if (cdm->flags&FRAM) - printf("FRAM\n"); + printf("is%d: FRAM\n",unit); if (cdm->flags&OFLO) - printf("OFLO\n"); + printf("is%d: OFLO\n",unit); if (cdm->flags&CRC) - printf("CRC\n"); + printf("is%d: CRC\n",unit); if (cdm->flags&RBUFF) - printf("RBUFF\n"); + printf("is%d: RBUFF\n",unit); }else if (cdm->flags&(STP|ENP) != (STP|ENP)) { do { - iswrcsr(0,RINT|INEA); + iswrcsr(unit,0,RINT|INEA); cdm->mcnt = 0; cdm->flags |= OWN; NEXTRDS; }while (!(cdm->flags&(OWN|ERR|STP|ENP))); is->last_rd = rmd; - printf("Chained buffer\n"); + printf("is%d: Chained buffer\n",unit); if ((cdm->flags & (OWN|ERR|STP|ENP)) != ENP) { - isreset(unit); + is_reset(unit); return; } }else { -#ifdef ISDEBUG - recv_print(is->last_rd); +#ifdef ISDEBUG + if (is_debug) + recv_print(unit,is->last_rd); #endif - isread(is,rbuf+(BUFSIZE*rmd),cdm->mcnt); + isread(is,is->rbuf+(BUFSIZE*rmd),(int)cdm->mcnt); + is->arpcom.ac_if.if_ipackets++; } cdm->flags |= OWN; cdm->mcnt = 0; NEXTRDS; #ifdef ISDEBUG - printf("is->last_rd = %x, cdm = %x\n",is->last_rd,cdm); + if (is_debug) + printf("is->last_rd = %x, cdm = %x\n",is->last_rd,cdm); #endif } /* while */ is->last_rd = rmd; -} /* isrint */ +} /* is_rint */ + /* * Pass a packet to the higher levels. * We deal with the trailer protocol here. */ -isread(ns, buf, len) - register struct is_softc *ns; - char *buf; - int len; +static inline void +isread(struct is_softc *is, unsigned char *buf, int len) { - register struct ether_header *eh; - struct mbuf *m; - int off, resid; - register struct ifqueue *inq; + register struct ether_header *eh; + struct mbuf *m; + int off, resid; + register struct ifqueue *inq; + + /* + * Deal with trailer protocol: if type is trailer type + * get true type from first 16-bit word past data. + * Remember that type was trailer by setting off. + */ + eh = (struct ether_header *)buf; + eh->ether_type = ntohs((u_short)eh->ether_type); + len = len - sizeof(struct ether_header) - 4; +#define nedataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off)))) + if (eh->ether_type >= ETHERTYPE_TRAIL && + eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { + off = (eh->ether_type - ETHERTYPE_TRAIL) * 512; + if (off >= ETHERMTU) return; /* sanity */ + eh->ether_type = ntohs(*nedataaddr(eh, off, u_short *)); + resid = ntohs(*(nedataaddr(eh, off+2, u_short *))); + if (off + resid > len) return; /* sanity */ + len = off + resid; + } else off = 0; + + if (len == 0) return; + + /* + * Pull packet off interface. Off is nonzero if packet + * has trailing header; neget will then force this header + * information to be at the front, but we still have to drop + * the type and length which are at the front of any trailer data. + */ + is->arpcom.ac_if.if_ipackets++; + m = isget(buf, len, off, &is->arpcom.ac_if); + if (m == 0) return; +#if NBPFILTER > 0 + /* + * Check if there's a BPF listener on this interface. + * If so, hand off the raw packet to bpf. + */ + if (is->bpf) { + bpf_mtap(is->bpf, m); + + /* + * Note that the interface cannot be in promiscuous mode if + * there are no BPF listeners. And if we are in promiscuous + * mode, we have to check if this packet is really ours. + * + * XXX This test does not support multicasts. + */ + if ((is->arpcom.ac_if.if_flags & IFF_PROMISC) && + bcmp(eh->ether_dhost, is->arpcom.ac_enaddr, + sizeof(eh->ether_dhost)) != 0 && + bcmp(eh->ether_dhost, etherbroadcastaddr, + sizeof(eh->ether_dhost)) != 0) { + + m_freem(m); + return; + } + } +#endif - /* - * Deal with trailer protocol: if type is trailer type - * get true type from first 16-bit word past data. - * Remember that type was trailer by setting off. - */ - eh = (struct ether_header *)buf; - eh->ether_type = ntohs((u_short)eh->ether_type); - len = len - sizeof(struct ether_header) - 4; -#define nedataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off)))) - if (eh->ether_type >= ETHERTYPE_TRAIL && - eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { - off = (eh->ether_type - ETHERTYPE_TRAIL) * 512; - if (off >= ETHERMTU) return; /* sanity */ - eh->ether_type = ntohs(*nedataaddr(eh, off, u_short *)); - resid = ntohs(*(nedataaddr(eh, off+2, u_short *))); - if (off + resid > len) return; /* sanity */ - len = off + resid; - } else off = 0; - - if (len == 0) return; - /* - * Pull packet off interface. Off is nonzero if packet - * has trailing header; neget will then force this header - * information to be at the front, but we still have to drop - * the type and length which are at the front of any trailer data. - */ - m = isget(buf, len, off, &ns->ns_if); - if (m == 0) return; - - ether_input(&ns->ns_if, eh, m); + ether_input(&is->arpcom.ac_if, eh, m); } /* @@ -566,85 +782,87 @@ isread(ns, buf, len) */ struct mbuf * isget(buf, totlen, off0, ifp) - caddr_t buf; - int totlen, off0; - struct ifnet *ifp; + caddr_t buf; + int totlen, off0; + struct ifnet *ifp; { - struct mbuf *top, **mp, *m, *p; - int off = off0, len; - register caddr_t cp = buf; - char *epkt; - - buf += sizeof(struct ether_header); - cp = buf; - epkt = cp + totlen; - - - if (off) { - cp += off + 2 * sizeof(u_short); - totlen -= 2 * sizeof(u_short); - } - - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == 0) - return (0); - m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.len = totlen; - m->m_len = MHLEN; - - top = 0; - mp = ⊤ - while (totlen > 0) { - if (top) { - MGET(m, M_DONTWAIT, MT_DATA); - if (m == 0) { - m_freem(top); - return (0); - } - m->m_len = MLEN; - } - len = min(totlen, epkt - cp); - if (len >= MINCLSIZE) { - MCLGET(m, M_DONTWAIT); - if (m->m_flags & M_EXT) - m->m_len = len = min(len, MCLBYTES); - else - len = m->m_len; - } else { - /* - * Place initial small packet/header at end of mbuf. - */ - if (len < m->m_len) { - if (top == 0 && len + max_linkhdr <= m->m_len) - m->m_data += max_linkhdr; - m->m_len = len; - } else - len = m->m_len; - } - bcopy(cp, mtod(m, caddr_t), (unsigned)len); - cp += len; - *mp = m; - mp = &m->m_next; - totlen -= len; - if (cp == epkt) - cp = buf; - } - return (top); + struct mbuf *top, **mp, *m, *p; + int off = off0, len; + register caddr_t cp = buf; + char *epkt; + + buf += sizeof(struct ether_header); + cp = buf; + epkt = cp + totlen; + + + if (off) { + cp += off + 2 * sizeof(u_short); + totlen -= 2 * sizeof(u_short); + } + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + return (0); + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = totlen; + m->m_len = MHLEN; + top = 0; + mp = ⊤ + while (totlen > 0) { + if (top) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) { + m_freem(top); + return (0); + } + m->m_len = MLEN; + } + len = min(totlen, epkt - cp); + if (len >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if (m->m_flags & M_EXT) + m->m_len = len = min(len, MCLBYTES); + else + len = m->m_len; + } else { + /* + * Place initial small packet/header at end of mbuf. + */ + if (len < m->m_len) { + if (top == 0 && len + max_linkhdr <= m->m_len) + m->m_data += max_linkhdr; + m->m_len = len; + } else + len = m->m_len; + } + bcopy(cp, mtod(m, caddr_t), (unsigned)len); + cp += len; + *mp = m; + mp = &m->m_next; + totlen -= len; + if (cp == epkt) + cp = buf; + } + return (top); } + /* * Process an ioctl request. */ -isioctl(ifp, cmd, data) +is_ioctl(ifp, cmd, data) register struct ifnet *ifp; int cmd; caddr_t data; { register struct ifaddr *ifa = (struct ifaddr *)data; - struct is_softc *ns = &is_softc[ifp->if_unit]; + int unit = ifp->if_unit; + struct is_softc *is = &is_softc[unit]; struct ifreq *ifr = (struct ifreq *)data; - int s = splimp(), error = 0; + int s, error = 0; + s = splnet(); switch (cmd) { @@ -654,94 +872,153 @@ isioctl(ifp, cmd, data) switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: - isinit(ifp->if_unit); /* before arpwhohas */ + is_init(ifp->if_unit); /* before arpwhohas */ + /* + * See if another station has *our* IP address. + * i.e.: There is an address conflict! If a + * conflict exists, a message is sent to the + * console. + */ ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); break; #endif #ifdef NS + /* + * XXX - This code is probably wrong + */ case AF_NS: { register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); if (ns_nullhost(*ina)) - ina->x_host = *(union ns_host *)(ns->ns_addr); + ina->x_host = + *(union ns_host *)(is->arpcom.ac_enaddr); else { /* - * The manual says we can't change the address - * while the receiver is armed, - * so reset everything + * */ - ifp->if_flags &= ~IFF_RUNNING; bcopy((caddr_t)ina->x_host.c_host, - (caddr_t)ns->ns_addr, sizeof(ns->ns_addr)); + (caddr_t)is->arpcom.ac_enaddr, + sizeof(is->arpcom.ac_enaddr)); } - isinit(ifp->if_unit); /* does ne_setaddr() */ + /* + * Set new address + */ + is_init(ifp->if_unit); break; } #endif default: - isinit(ifp->if_unit); + is_init(ifp->if_unit); break; } break; case SIOCSIFFLAGS: + /* + * If interface is marked down and it is running, then stop it + */ if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) { + iswrcsr(unit,0,STOP); ifp->if_flags &= ~IFF_RUNNING; - iswrcsr(0,STOP); - } else if (ifp->if_flags & IFF_UP && - (ifp->if_flags & IFF_RUNNING) == 0) - isinit(ifp->if_unit); + } else { + /* + * If interface is marked up and it is stopped, then start it + */ + if ((ifp->if_flags & IFF_UP) && + (ifp->if_flags & IFF_RUNNING) == 0) + is_init(ifp->if_unit); + } +#ifdef ISDEBUG + if (ifp->if_flags & IFF_DEBUG) + is_debug = 1; + else + is_debug = 0; +#endif +#if NBPFILTER > 0 + if (ifp->if_flags & IFF_PROMISC) { + /* + * Set promiscuous mode on interface. + * XXX - for multicasts to work, we would need to + * write 1's in all bits of multicast + * hashing array. For now we assume that + * this was done in is_init(). + */ + is->init_block->mode = PROM; + } else + /* + * XXX - for multicasts to work, we would need to + * rewrite the multicast hashing array with the + * proper hash (would have been destroyed above). + */ + { /* Don't know about this */}; +#endif break; #ifdef notdef case SIOCGHWADDR: - bcopy((caddr_t)ns->ns_addr, (caddr_t) &ifr->ifr_data, - sizeof(ns->ns_addr)); + bcopy((caddr_t)is->arpcom.ac_enaddr, (caddr_t) &ifr->ifr_data, + sizeof(is->arpcom.ac_enaddr)); break; #endif default: error = EINVAL; } - splx(s); + (void) splx(s); return (error); } -recv_print(no) - int no; +#ifdef ISDEBUG +recv_print(unit,no) + int unit,no; { + register struct is_softc *is=&is_softc[unit]; struct mds *rmd; - int len,i; + int len,i,printed=0; - rmd = (rd+no); + rmd = (is->rd+no); len = rmd->mcnt; - printf("Receive buffer %d, len = %d\n",no,len); - printf("Status %x\n",isrdcsr(0)); - for (i=0; irbuf+(BUFSIZE*no)+i)); + } + if (printed) + printf("\n"); } -xmit_print(no) - int no; +xmit_print(unit,no) + int unit,no; { + register struct is_softc *is=&is_softc[unit]; struct mds *rmd; - int i; + int i, printed=0; u_short len; - rmd = (td+no); + rmd = (is->td+no); len = -(rmd->bcnt); - printf("Transmit buffer %d, len = %d\n",no,len); - printf("Status %x\n",isrdcsr(0)); - printf("addr %x, flags %x, bcnt %x, mcnt %x\n", - rmd->addr,rmd->flags,rmd->bcnt,rmd->mcnt); - for (i=0; iaddr,rmd->flags,rmd->bcnt,rmd->mcnt); + for (i=0; itbuf+(BUFSIZE*no)+i)); + } + if (printed) + printf("\n"); } +#endif /* ISDEBUG */ -#endif +#endif /* NIS > 0 */ diff --git a/sys/i386/isa/if_isreg.h b/sys/i386/isa/if_isreg.h index 790313d6d9..4dbca17e44 100644 --- a/sys/i386/isa/if_isreg.h +++ b/sys/i386/isa/if_isreg.h @@ -1,3 +1,29 @@ +/* + * Isolan AT 4141-0 Ethernet driver header file + * Isolink 4110 + * + * By Paul Richards + * + * Copyright (C) 1993, Paul Richards. This software may be used, modified, + * copied, distributed, and sold, in both source and binary form provided + * that the above copyright and these terms are retained. Under no + * circumstances is the author responsible for the proper functioning + * of this software, nor does the author assume any responsibility + * for damages incurred with its use. + * + * $Id: if_isreg.h,v 1.2 1993/10/16 13:45:55 rgrimes Exp $ + */ + +/* + * Initialize multicast address hashing registers to accept + * all multicasts (only used when in promiscuous mode) + */ +#if NBPFILTER > 0 +#define MULTI_INIT_ADDR 0xff +#else +#define MULTI_INIT_ADDR 0 +#endif + /* Declarations specific to this driver */ #define NTBUF 2 #define TLEN 1 diff --git a/sys/i386/isa/if_ne.c b/sys/i386/isa/if_ne.c deleted file mode 100644 index e1a6c78f9a..0000000000 --- a/sys/i386/isa/if_ne.c +++ /dev/null @@ -1,855 +0,0 @@ -/*- - * Copyright (c) 1990, 1991 William F. Jolitz. - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)if_ne.c 7.4 (Berkeley) 5/21/91 - */ - -/* - * NE2000 Ethernet driver - * - * Parts inspired from Tim Tucker's if_wd driver for the wd8003, - * insight on the ne2000 gained from Robert Clements PC/FTP driver. - */ - -#include "ne.h" -#if NNE > 0 - -#include "param.h" -#include "systm.h" -#include "mbuf.h" -#include "buf.h" -#include "protosw.h" -#include "socket.h" -#include "ioctl.h" -#include "errno.h" -#include "syslog.h" - -#include "net/if.h" -#include "net/netisr.h" -#include "net/route.h" - -#ifdef INET -#include "netinet/in.h" -#include "netinet/in_systm.h" -#include "netinet/in_var.h" -#include "netinet/ip.h" -#include "netinet/if_ether.h" -#endif - -#ifdef NS -#include "netns/ns.h" -#include "netns/ns_if.h" -#endif - -#include "i386/isa/isa_device.h" -#include "i386/isa/if_nereg.h" -#include "i386/isa/icu.h" - -int neprobe(), neattach(), neintr(); -int nestart(),neinit(), ether_output(), neioctl(); - -struct isa_driver nedriver = { - neprobe, neattach, "ne", -}; - -struct mbuf *neget(); - -#define ETHER_MIN_LEN 64 -#define ETHER_MAX_LEN 1536 - -/* - * Ethernet software status per interface. - * - * Each interface is referenced by a network interface structure, - * ns_if, which the routing code uses to locate the interface. - * This structure contains the output queue for the interface, its address, ... - */ -struct ne_softc { - struct arpcom ns_ac; /* Ethernet common part */ -#define ns_if ns_ac.ac_if /* network-visible interface */ -#define ns_addr ns_ac.ac_enaddr /* hardware Ethernet address */ - int ns_flags; -#define DSF_LOCK 1 /* block re-entering enstart */ - int ns_oactive ; - int ns_mask ; - int ns_ba; /* byte addr in buffer ram of inc pkt */ - int ns_cur; /* current page being filled */ - struct prhdr ns_ph; /* hardware header of incoming packet*/ - struct ether_header ns_eh; /* header of incoming packet */ - u_char ns_pb[2048 /*ETHERMTU+sizeof(long)*/]; - short ns_txstart; /* transmitter buffer start */ - short ns_rxend; /* recevier buffer end */ - short ns_rxbndry; /* recevier buffer boundary */ - short ns_port; /* i/o port base */ - short ns_mode; /* word/byte mode */ -} ne_softc[NNE] ; -#define ENBUFSIZE (sizeof(struct ether_header) + ETHERMTU + 2 + ETHER_MIN_LEN) - -#define PAT(n) (0xa55a + 37*(n)) - -u_short boarddata[16]; - -neprobe(dvp) - struct isa_device *dvp; -{ - int val, i, s, sum, bytemode = 1, pat; - register struct ne_softc *ns = &ne_softc[0]; - register nec; - -#ifdef lint - neintr(0); -#endif - - nec = ns->ns_port = dvp->id_iobase; - s = splimp(); - - if (bytemode) { - /* Byte Transfers, Burst Mode Select, Fifo at 8 bytes */ - ns->ns_mode = DSDC_BMS|DSDC_FT1; - ns->ns_txstart = TBUF8; - ns->ns_rxend = RBUFEND8; - } else { -word: - /* Word Transfers, Burst Mode Select, Fifo at 8 bytes */ - ns->ns_mode = DSDC_WTS|DSDC_BMS|DSDC_FT1; - ns->ns_txstart = TBUF16; - ns->ns_rxend = RBUFEND16; - bytemode = 0; - } - - /* Reset the bastard */ - val = inb(nec + ne_reset); - DELAY(200); - outb(nec + ne_reset, val); - DELAY(200); - - outb(nec + ds_cmd, DSCM_STOP|DSCM_NODMA); - - i = 10000; - while ((inb(nec + ds0_isr) & DSIS_RESET) == 0 && i-- > 0); - if (i < 0) return (0); - - outb(nec + ds0_isr, 0xff); - outb(nec + ds0_dcr, ns->ns_mode); - outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP); - DELAY(1000); - - /* Check cmd reg and fail if not right */ - if ((i = inb(nec + ds_cmd)) != (DSCM_NODMA|DSCM_PG0|DSCM_STOP)) - return(0); - - outb(nec + ds0_tcr, 0); - outb(nec + ds0_rcr, DSRC_MON); - outb(nec + ds0_pstart, (ns->ns_txstart+PKTSZ)/DS_PGSIZE); - outb(nec + ds0_pstop, ns->ns_rxend/DS_PGSIZE); - outb(nec + ds0_bnry, ns->ns_rxend/DS_PGSIZE); - outb(nec + ds0_imr, 0); - outb(nec + ds0_isr, 0); - outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP); - outb(nec + ds1_curr, (ns->ns_txstart+PKTSZ)/DS_PGSIZE); - outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP); - - -#ifdef NEDEBUG -#define RCON 37 - { int i, rom; - - rom=1; - printf("ne ram "); - - for (i = 0; i < 0xfff0; i+=4) { - pat = PAT(i); - neput(ns, &pat,i,4); - nefetch(ns, &pat,i,4); - if (pat == PAT(i)) { - if (rom) { - rom=0; - printf(" %x", i); - } - } else { - if (!rom) { - rom=1; - printf("..%x ", i); - } - } - pat=0; - neput(ns, &pat,i,4); - } - printf("\n"); - } -#endif - - /* - * detect difference between units - * solely by where the RAM is decoded. - */ - pat = PAT(0); - neput(ns, &pat, ns->ns_txstart, 4); - nefetch(ns, &pat, ns->ns_txstart, 4); - if (pat != PAT(0)) { - if (bytemode) - goto word; - else return (0); - } - - - /* Extract board address */ - nefetch (ns, (caddr_t)boarddata, 0, sizeof(boarddata)); - - for(i=0; i < 6; i++) - ns->ns_addr[i] = boarddata[i]; - splx(s); - return (1); -} - -/* - * Fetch from onboard ROM/RAM - */ -nefetch (ns, up, ad, len) struct ne_softc *ns; caddr_t up; { - u_char cmd; - register nec = ns->ns_port; - int counter = 100000; - - cmd = inb (nec + ds_cmd); - outb (nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START); - - /* Setup remote dma */ - outb (nec + ds0_isr, DSIS_RDC); - - if ((ns->ns_mode & DSDC_WTS) && len&1) - len++; /* roundup to words */ - - outb (nec+ds0_rbcr0, len); - outb (nec+ds0_rbcr1, len>>8); - outb (nec+ds0_rsar0, ad); - outb (nec+ds0_rsar1, ad>>8); - - /* Execute & extract from card */ - outb (nec+ds_cmd, DSCM_RREAD|DSCM_PG0|DSCM_START); - - if (ns->ns_mode & DSDC_WTS) - insw (nec+ne_data, up, len/2); - else - insb (nec+ne_data, up, len); - - /* Wait till done, then shutdown feature */ - while ((inb (nec+ds0_isr) & DSIS_RDC) == 0 && counter-- > 0) - ; - outb (nec+ds0_isr, DSIS_RDC); - outb (nec+ds_cmd, cmd); -} - -/* - * Put to onboard RAM - */ -neput (ns, up, ad, len) struct ne_softc *ns; caddr_t up; { - u_char cmd; - register nec = ns->ns_port; - int counter = 100000; - - cmd = inb(nec+ds_cmd); - outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START); - - /* Setup for remote dma */ - outb (nec+ds0_isr, DSIS_RDC); - - if ((ns->ns_mode & DSDC_WTS) && len&1) - len++; /* roundup to words */ - - outb (nec+ds0_rbcr0, len); - outb (nec+ds0_rbcr1, len>>8); - outb (nec+ds0_rsar0, ad); - outb (nec+ds0_rsar1, ad>>8); - - /* Execute & stuff to card */ - outb (nec+ds_cmd, DSCM_RWRITE|DSCM_PG0|DSCM_START); - if (ns->ns_mode & DSDC_WTS) - outsw (nec+ne_data, up, len/2); - else - outsb (nec+ne_data, up, len); - - /* Wait till done, then shutdown feature */ - while ((inb (nec+ds0_isr) & DSIS_RDC) == 0 && counter-- > 0) - ; - outb (nec+ds0_isr, DSIS_RDC); - outb (nec+ds_cmd, cmd); -} - -/* - * Reset of interface. - */ -nereset(unit, uban) - int unit, uban; -{ - if (unit >= NNE) - return; - printf("ne%d: reset\n", unit); - ne_softc[unit].ns_flags &= ~DSF_LOCK; - neinit(unit); -} - -/* - * Interface exists: make available by filling in network interface - * record. System will initialize the interface when it is ready - * to accept packets. We get the ethernet address here. - */ -neattach(dvp) - struct isa_device *dvp; -{ - int unit = dvp->id_unit; - register struct ne_softc *ns = &ne_softc[unit]; - register struct ifnet *ifp = &ns->ns_if; - - ifp->if_unit = unit; - ifp->if_name = nedriver.name ; - ifp->if_mtu = ETHERMTU; - printf (" ethernet address %s", ether_sprintf(ns->ns_addr)) ; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS; - ifp->if_init = neinit; - ifp->if_output = ether_output; - ifp->if_start = nestart; - ifp->if_ioctl = neioctl; - ifp->if_reset = nereset; - ifp->if_watchdog = 0; - if_attach(ifp); -} - -/* - * Initialization of interface; set up initialization block - * and transmit/receive descriptor rings. - */ -neinit(unit) - int unit; -{ - register struct ne_softc *ns = &ne_softc[unit]; - struct ifnet *ifp = &ns->ns_if; - int s; - int i; char *cp; - register nec = ns->ns_port; - - if (ifp->if_addrlist == (struct ifaddr *)0) return; - if (ifp->if_flags & IFF_RUNNING) return; - - s = splimp(); - - /* set physical address on ethernet */ - outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP); - for (i=0 ; i < 6 ; i++) outb(nec+ds1_par0+i,ns->ns_addr[i]); - - /* clr logical address hash filter for now */ - for (i=0 ; i < 8 ; i++) outb(nec+ds1_mar0+i,0xff); - - /* init regs */ - outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP); - outb (nec+ds0_rbcr0, 0); - outb (nec+ds0_rbcr1, 0); - outb (nec+ds0_imr, 0); - outb (nec+ds0_isr, 0xff); - - /* Word Transfer select, Burst Mode Select, Fifo at 8 bytes */ - outb(nec+ds0_dcr, ns->ns_mode); - - outb(nec+ds0_tcr, 0); - outb (nec+ds0_rcr, DSRC_MON); - outb (nec+ds0_tpsr, 0); - outb(nec+ds0_pstart, (ns->ns_txstart+PKTSZ)/DS_PGSIZE); - outb(nec+ds0_pstop, ns->ns_rxend/DS_PGSIZE); - outb(nec+ds0_bnry, (ns->ns_txstart+PKTSZ)/DS_PGSIZE); - outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP); - outb(nec+ds1_curr, (ns->ns_txstart+PKTSZ)/DS_PGSIZE); - ns->ns_cur = (ns->ns_txstart+PKTSZ)/DS_PGSIZE; - outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START); - outb (nec+ds0_rcr, DSRC_AB); - outb(nec+ds0_dcr, ns->ns_mode); - outb (nec+ds0_imr, 0xff); - - ns->ns_if.if_flags |= IFF_RUNNING; - ns->ns_flags &= ~DSF_LOCK; - ns->ns_oactive = 0; ns->ns_mask = ~0; - nestart(ifp); - splx(s); -} - -/* - * Setup output on interface. - * Get another datagram to send off of the interface queue, - * and map it to the interface before starting the output. - * called only at splimp or interrupt level. - */ -nestart(ifp) - struct ifnet *ifp; -{ - register struct ne_softc *ns = &ne_softc[ifp->if_unit]; - struct mbuf *m0, *m; - int buffer; - int len = 0, i, total,t; - register nec = ns->ns_port; - - /* - * The DS8390 has only one transmit buffer, if it is busy we - * must wait until the transmit interrupt completes. - */ - outb(nec+ds_cmd,DSCM_NODMA|DSCM_START); - - if (ns->ns_flags & DSF_LOCK) - return; - - if (inb(nec+ds_cmd) & DSCM_TRANS) - return; - - if ((ns->ns_if.if_flags & IFF_RUNNING) == 0) - return; - - IF_DEQUEUE(&ns->ns_if.if_snd, m); - - if (m == 0) - return; - - /* - * Copy the mbuf chain into the transmit buffer - */ - - ns->ns_flags |= DSF_LOCK; /* prevent entering nestart */ - buffer = ns->ns_txstart; len = i = 0; - t = 0; - for (m0 = m; m != 0; m = m->m_next) - t += m->m_len; - - m = m0; - total = t; - for (m0 = m; m != 0; ) { - - if (m->m_len&1 && t > m->m_len) { - neput(ns, mtod(m, caddr_t), buffer, m->m_len - 1); - t -= m->m_len - 1; - buffer += m->m_len - 1; - m->m_data += m->m_len - 1; - m->m_len = 1; - m = m_pullup(m, 2); - } else { - neput(ns, mtod(m, caddr_t), buffer, m->m_len); - buffer += m->m_len; - t -= m->m_len; - MFREE(m, m0); - m = m0; - } - } - - /* - * Init transmit length registers, and set transmit start flag. - */ - - len = total; - if (len < ETHER_MIN_LEN) len = ETHER_MIN_LEN; - outb(nec+ds0_tbcr0,len&0xff); - outb(nec+ds0_tbcr1,(len>>8)&0xff); - outb(nec+ds0_tpsr, ns->ns_txstart/DS_PGSIZE); - outb(nec+ds_cmd, DSCM_TRANS|DSCM_NODMA|DSCM_START); -} - -/* buffer successor/predecessor in ring? */ -#define succ(n) (((n)+1 >= ns->ns_rxend/DS_PGSIZE) ? (ns->ns_txstart+PKTSZ)/DS_PGSIZE : (n)+1) -#define pred(n) (((n)-1 < (ns->ns_txstart+PKTSZ)/DS_PGSIZE) ? ns->ns_rxend/DS_PGSIZE-1 : (n)-1) - -/* - * Controller interrupt. - */ -neintr(unit) -{ - register struct ne_softc *ns = &ne_softc[unit]; - u_char cmd,isr; - register nec = ns->ns_port; - - /* Save cmd, clear interrupt */ - cmd = inb (nec+ds_cmd); -loop: - isr = inb (nec+ds0_isr); - outb(nec+ds_cmd,DSCM_NODMA|DSCM_START); - outb(nec+ds0_isr, isr); - - /* Receiver error */ - if (isr & DSIS_RXE) { - /* need to read these registers to clear status */ - (void) inb(nec+ ds0_rsr); - (void) inb(nec+ 0xD); - (void) inb(nec + 0xE); - (void) inb(nec + 0xF); - ns->ns_if.if_ierrors++; - } - - /* We received something; rummage thru tiny ring buffer */ - if (isr & (DSIS_RX|DSIS_RXE|DSIS_ROVRN)) { - u_char pend,lastfree; - - outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1); - pend = inb(nec+ds1_curr); - outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0); - lastfree = inb(nec+ds0_bnry); - - /* Have we wrapped? */ - if (lastfree >= ns->ns_rxend/DS_PGSIZE) - lastfree = (ns->ns_txstart+PKTSZ)/DS_PGSIZE; - if (pend < lastfree && ns->ns_cur < pend) - lastfree = ns->ns_cur; - else if (ns->ns_cur > lastfree) - lastfree = ns->ns_cur; - - /* Something in the buffer? */ - while (pend != lastfree) { - u_char nxt; - - /* Extract header from microcephalic board */ - nefetch(ns, &ns->ns_ph,lastfree*DS_PGSIZE, - sizeof(ns->ns_ph)); - ns->ns_ba = lastfree*DS_PGSIZE+sizeof(ns->ns_ph); - - /* Incipient paranoia */ - if (ns->ns_ph.pr_status == DSRS_RPC || - /* for dequna's */ - ns->ns_ph.pr_status == 0x21) - nerecv (ns); -#ifdef NEDEBUG - else { - printf("cur %x pnd %x lfr %x ", - ns->ns_cur, pend, lastfree); - printf("nxt %x len %x ", ns->ns_ph.pr_nxtpg, - (ns->ns_ph.pr_sz1<<8)+ ns->ns_ph.pr_sz0); - printf("Bogus Sts %x\n", ns->ns_ph.pr_status); - } -#endif - - nxt = ns->ns_ph.pr_nxtpg ; - - /* Sanity check */ - if ( nxt >= (ns->ns_txstart+PKTSZ)/DS_PGSIZE - && nxt <= ns->ns_rxend/DS_PGSIZE && nxt <= pend) - ns->ns_cur = nxt; - else ns->ns_cur = nxt = pend; - - /* Set the boundaries */ - lastfree = nxt; - outb(nec+ds0_bnry, pred(nxt)); - outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1); - pend = inb(nec+ds1_curr); - outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0); - } - outb(nec+ds_cmd, DSCM_START|DSCM_NODMA); - } - - /* Transmit error */ - if (isr & DSIS_TXE) { - ns->ns_flags &= ~DSF_LOCK; - /* Need to read these registers to clear status */ - ns->ns_if.if_collisions += inb(nec+ds0_tbcr0); - ns->ns_if.if_oerrors++; - } - - /* Packet Transmitted */ - if (isr & DSIS_TX) { - ns->ns_flags &= ~DSF_LOCK; - ++ns->ns_if.if_opackets; - ns->ns_if.if_collisions += inb(nec+ds0_tbcr0); - } - - /* Receiver ovverun? */ - if (isr & DSIS_ROVRN) { - log(LOG_ERR, "ne%d: error: isr %x\n", ns-ne_softc, isr - /*, DSIS_BITS*/); - outb(nec+ds0_rbcr0, 0); - outb(nec+ds0_rbcr1, 0); - outb(nec+ds0_tcr, DSTC_LB0); - outb(nec+ds0_rcr, DSRC_MON); - outb(nec+ds_cmd, DSCM_START|DSCM_NODMA); - outb(nec+ds0_rcr, DSRC_AB); - outb(nec+ds0_tcr, 0); - } - - /* Any more to send? */ - outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START); - nestart(&ns->ns_if); - outb (nec+ds_cmd, cmd); - outb (nec+ds0_imr, 0xff); - - /* Still more to do? */ - isr = inb (nec+ds0_isr); - if(isr) goto loop; -} - -/* - * Ethernet interface receiver interface. - * If input error just drop packet. - * Otherwise examine packet to determine type. If can't determine length - * from type, then have to drop packet. Othewise decapsulate - * packet based on type and pass to type specific higher-level - * input routine. - */ -nerecv(ns) - register struct ne_softc *ns; -{ - int len,i; - - ns->ns_if.if_ipackets++; - len = ns->ns_ph.pr_sz0 + (ns->ns_ph.pr_sz1<<8); - if(len < ETHER_MIN_LEN || len > ETHER_MAX_LEN) - return; - - /* this need not be so torturous - one/two bcopys at most into mbufs */ - nefetch(ns, ns->ns_pb, ns->ns_ba, min(len,DS_PGSIZE-sizeof(ns->ns_ph))); - if (len > DS_PGSIZE-sizeof(ns->ns_ph)) { - int l = len - (DS_PGSIZE-sizeof(ns->ns_ph)), b ; - u_char *p = ns->ns_pb + (DS_PGSIZE-sizeof(ns->ns_ph)); - - if(++ns->ns_cur > 0x7f) ns->ns_cur = 0x46; - b = ns->ns_cur*DS_PGSIZE; - - while (l >= DS_PGSIZE) { - nefetch(ns, p, b, DS_PGSIZE); - p += DS_PGSIZE; l -= DS_PGSIZE; - if(++ns->ns_cur > 0x7f) ns->ns_cur = 0x46; - b = ns->ns_cur*DS_PGSIZE; - } - if (l > 0) - nefetch(ns, p, b, l); - } - /* don't forget checksum! */ - len -= sizeof(struct ether_header) + sizeof(long); - - neread(ns,(caddr_t)(ns->ns_pb), len); -} - -/* - * Pass a packet to the higher levels. - * We deal with the trailer protocol here. - */ -neread(ns, buf, len) - register struct ne_softc *ns; - char *buf; - int len; -{ - register struct ether_header *eh; - struct mbuf *m; - int off, resid; - register struct ifqueue *inq; - - /* - * Deal with trailer protocol: if type is trailer type - * get true type from first 16-bit word past data. - * Remember that type was trailer by setting off. - */ - eh = (struct ether_header *)buf; - eh->ether_type = ntohs((u_short)eh->ether_type); -#define nedataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off)))) - if (eh->ether_type >= ETHERTYPE_TRAIL && - eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { - off = (eh->ether_type - ETHERTYPE_TRAIL) * 512; - if (off >= ETHERMTU) return; /* sanity */ - eh->ether_type = ntohs(*nedataaddr(eh, off, u_short *)); - resid = ntohs(*(nedataaddr(eh, off+2, u_short *))); - if (off + resid > len) return; /* sanity */ - len = off + resid; - } else off = 0; - - if (len == 0) return; - - /* - * Pull packet off interface. Off is nonzero if packet - * has trailing header; neget will then force this header - * information to be at the front, but we still have to drop - * the type and length which are at the front of any trailer data. - */ - m = neget(buf, len, off, &ns->ns_if); - if (m == 0) return; - - ether_input(&ns->ns_if, eh, m); -} - -/* - * Supporting routines - */ - -/* - * Pull read data off a interface. - * Len is length of data, with local net header stripped. - * Off is non-zero if a trailer protocol was used, and - * gives the offset of the trailer information. - * We copy the trailer information and then all the normal - * data into mbufs. When full cluster sized units are present - * we copy into clusters. - */ -struct mbuf * -neget(buf, totlen, off0, ifp) - caddr_t buf; - int totlen, off0; - struct ifnet *ifp; -{ - struct mbuf *top, **mp, *m, *p; - int off = off0, len; - register caddr_t cp = buf; - char *epkt; - - buf += sizeof(struct ether_header); - cp = buf; - epkt = cp + totlen; - - - if (off) { - cp += off + 2 * sizeof(u_short); - totlen -= 2 * sizeof(u_short); - } - - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == 0) - return (0); - m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.len = totlen; - m->m_len = MHLEN; - - top = 0; - mp = ⊤ - while (totlen > 0) { - if (top) { - MGET(m, M_DONTWAIT, MT_DATA); - if (m == 0) { - m_freem(top); - return (0); - } - m->m_len = MLEN; - } - len = min(totlen, epkt - cp); - if (len >= MINCLSIZE) { - MCLGET(m, M_DONTWAIT); - if (m->m_flags & M_EXT) - m->m_len = len = min(len, MCLBYTES); - else - len = m->m_len; - } else { - /* - * Place initial small packet/header at end of mbuf. - */ - if (len < m->m_len) { - if (top == 0 && len + max_linkhdr <= m->m_len) - m->m_data += max_linkhdr; - m->m_len = len; - } else - len = m->m_len; - } - bcopy(cp, mtod(m, caddr_t), (unsigned)len); - cp += len; - *mp = m; - mp = &m->m_next; - totlen -= len; - if (cp == epkt) - cp = buf; - } - return (top); -} - -/* - * Process an ioctl request. - */ -neioctl(ifp, cmd, data) - register struct ifnet *ifp; - int cmd; - caddr_t data; -{ - register struct ifaddr *ifa = (struct ifaddr *)data; - struct ne_softc *ns = &ne_softc[ifp->if_unit]; - struct ifreq *ifr = (struct ifreq *)data; - int s = splimp(), error = 0; - - - switch (cmd) { - - case SIOCSIFADDR: - ifp->if_flags |= IFF_UP; - - switch (ifa->ifa_addr->sa_family) { -#ifdef INET - case AF_INET: - neinit(ifp->if_unit); /* before arpwhohas */ - ((struct arpcom *)ifp)->ac_ipaddr = - IA_SIN(ifa)->sin_addr; - arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); - break; -#endif -#ifdef NS - case AF_NS: - { - register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); - - if (ns_nullhost(*ina)) - ina->x_host = *(union ns_host *)(ns->ns_addr); - else { - /* - * The manual says we can't change the address - * while the receiver is armed, - * so reset everything - */ - ifp->if_flags &= ~IFF_RUNNING; - bcopy((caddr_t)ina->x_host.c_host, - (caddr_t)ns->ns_addr, sizeof(ns->ns_addr)); - } - neinit(ifp->if_unit); /* does ne_setaddr() */ - break; - } -#endif - default: - neinit(ifp->if_unit); - break; - } - break; - - case SIOCSIFFLAGS: - if ((ifp->if_flags & IFF_UP) == 0 && - ifp->if_flags & IFF_RUNNING) { - ifp->if_flags &= ~IFF_RUNNING; - outb(ns->ns_port + ds_cmd, DSCM_STOP|DSCM_NODMA); - } else if (ifp->if_flags & IFF_UP && - (ifp->if_flags & IFF_RUNNING) == 0) - neinit(ifp->if_unit); - break; - -#ifdef notdef - case SIOCGHWADDR: - bcopy((caddr_t)ns->ns_addr, (caddr_t) &ifr->ifr_data, - sizeof(ns->ns_addr)); - break; -#endif - - default: - error = EINVAL; - } - splx(s); - return (error); -} -#endif diff --git a/sys/i386/isa/if_nereg.h b/sys/i386/isa/if_nereg.h deleted file mode 100644 index 29520b4bb6..0000000000 --- a/sys/i386/isa/if_nereg.h +++ /dev/null @@ -1,55 +0,0 @@ -/*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)if_nereg.h 7.1 (Berkeley) 5/9/91 - */ - -/* - * NE1000/2000 Ethernet Card registers - */ - -/* This card uses a DS8390 Ethernet controller in at the beginning of - its i/o space */ -#include "ic/ds8390.h" - -#define ne_data 0x10 /* Data Transfer port */ -#define ne_reset 0x1f /* Card Reset port */ - -#define PKTSZ 3*512 /* Size of transmit buffer */ - -/* Span of memory on an NE2000 */ -#define TBUF16 (16*1024) /* Starting location of Transmit Buffer */ -#define RBUFEND16 (32*1024) /* Ending location of Receive Buffer */ - -/* Span of memory on an NE1000 */ -#define TBUF8 (8*1024) /* Starting location of Transmit Buffer */ -#define RBUFEND8 (16*1024) /* Ending location of Receive Buffer */ diff --git a/sys/i386/isa/if_we.c b/sys/i386/isa/if_we.c deleted file mode 100644 index 0f6c3bff38..0000000000 --- a/sys/i386/isa/if_we.c +++ /dev/null @@ -1,1057 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Tim L. Tucker. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)if_we.c 7.3 (Berkeley) 5/21/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00112 - * -------------------- ----- ---------------------- - * - * 09 Sep 92 Mike Durkin Fix Danpex EW-2016 & other 8013 clones - * enable with "options WECOMPAT" - * 19 Sep 92 Michael Galassi Fixed multiboard routing - * 20 Sep 92 Barry Lustig WD8013 16 bit mode -- enable - * with "options WD8013". - * 14 Mar 93 Marc Frajola BPF packet filter support - * 14 Mar 93 David Greenman Input and other routines re-written - * 14 Mar 93 Rodney W. Grimes Added link level address to we_attach - */ - -/* - * Modification history - * - * 8/28/89 - Initial version(if_wd.c), Tim L Tucker - * - * 92.09.19 - Changes to allow multiple we interfaces in one box. - * Allowed interupt handler to look at unit other than 0 - * Bdry was static, made it into an array w/ one entry per - * interface. nerd@percival.rain.com (Michael Galassi) - * - * BPF Packet Filter Support added by Marc Frajola, 12/30/92 - * Input & other routines re-written by David Greenman, 1/2/93 - * BPF trailer support added by David Greenman, 1/7/93 - * we_attach enhanced with link level address by Rodney W. Grimes, 1/30/93 - * - * $Log: if_we.c,v $ - * Revision 1.2 93/02/18 17:21:57 davidg - * Bugs in mbuf cluster allocation fixed - * Problem with nfs wanting mbufs aligned on longword boundries fixed - * - */ - -#include "we.h" -#if NWE > 0 -/* - * Western Digital 8003 ethernet/starlan adapter - * - * Supports the following interface cards: - * WD8003E, WD8003EBT, WD8003S, WD8003SBT, WD8013EBT - * - * The Western Digital card is one of many AT/MCA ethernet interfaces - * based on the National DS8390 Network Interface chip set. - */ -#include "param.h" -#include "mbuf.h" -#include "socket.h" -#include "ioctl.h" -#include "errno.h" -#include "syslog.h" - -#include "net/if.h" -#include "net/if_types.h" -#include "net/if_dl.h" -#include "net/netisr.h" - -#ifdef INET -#include "netinet/in.h" -#include "netinet/in_systm.h" -#include "netinet/in_var.h" -#include "netinet/ip.h" -#include "netinet/if_ether.h" -#endif - -#ifdef NS -#include "netns/ns.h" -#include "netns/ns_if.h" -#endif - -#include "bpfilter.h" -#if NBPFILTER > 0 -#include "net/bpf.h" -#include "net/bpfdesc.h" -#endif - -#include "i386/isa/isa.h" -#include "i386/isa/if_wereg.h" -#include "i386/isa/isa_device.h" -#include "i386/isa/icu.h" -#include "i386/include/pio.h" - -static inline char *we_ring_copy(); - -/* - * This constant should really be 60 because the we adds 4 bytes of crc. - * However when set to 60 our packets are ignored by deunas , 3coms are - * okay ?????????????????????????????????????????? - */ -#define ETHER_MIN_LEN 64 -#define ETHER_ADDR_LEN 6 -#define ETHER_HDR_SIZE 14 - -/* - * Ethernet software status per interface. - * - * Each interface is referenced by a network interface structure, - * qe_if, which the routing code uses to locate the interface. - * This structure contains the output queue for the interface, its address, ... - */ -struct we_softc { - struct arpcom we_ac; /* Ethernet common part */ -#define we_if we_ac.ac_if /* network-visible interface */ -#define we_addr we_ac.ac_enaddr /* hardware Ethernet address */ - - u_char we_flags; /* software state */ -#define WDF_RUNNING 0x01 -#define WDF_TXBUSY 0x02 -#if NBPFILTER > 0 -#define WDF_ATTACHED 0x80 -#endif - - u_char we_type; /* interface type code */ - u_short we_vector; /* interrupt vector */ - short we_io_ctl_addr; /* i/o bus address, control */ - short we_io_nic_addr; /* i/o bus address, DS8390 */ - - caddr_t we_vmem_addr; /* card RAM virtual memory base */ - u_long we_vmem_size; /* card RAM bytes */ - caddr_t we_vmem_ring; /* receive ring RAM vaddress */ - caddr_t we_vmem_end; /* receive ring RAM end */ - caddr_t we_bpf; /* Magic Cookie for BPF */ -} we_softc[NWE]; - -int weprobe(), weattach(), weintr(), westart(); -int weinit(), ether_output(), weioctl(), wereset(), wewatchdog(); - -struct isa_driver wedriver = { - weprobe, weattach, "we", -}; - -static unsigned short wemask[] = - { IRQ9, IRQ3, IRQ5, IRQ7, IRQ10, IRQ11, IRQ15, IRQ4 }; - -/* - * Probe the WD8003 to see if its there - */ -weprobe(is) - struct isa_device *is; -{ - register int i; - register struct we_softc *sc = &we_softc[is->id_unit]; - union we_mem_sel wem; - u_char sum; -#ifdef WD8013 /* 20 Sep 92*/ - union we_laar laar; - - laar.laar_byte = 0; -#endif /* WD8013*/ - - wem.ms_byte = 0; /* 20 Sep 92*/ - - /* reset card to force it into a known state. */ - outb(is->id_iobase, 0x80); - DELAY(100); - outb(is->id_iobase, 0x00); - /* wait in the case this card is reading it's EEROM */ - DELAY(5000); - -#ifdef WD8013 /* 20 Sep 92*/ - /* allow the NIC to access the shared RAM 16 bits at a time */ - - laar.addr_l19 = 1; - laar.lan_16_en = 1; - laar.mem_16_en = 1; - outb(is->id_iobase+5, laar.laar_byte); /* Write a 0xc1 */ -#endif /* WD8013*/ - - /* - * Here we check the card ROM, if the checksum passes, and the - * type code and ethernet address check out, then we know we have - * a wd8003 card. - * - * Autoconfiguration: No warning message is printed on error. - */ - for (sum = 0, i = 0; i < 8; ++i) - sum += inb(is->id_iobase + WD_ROM_OFFSET + i); - if (sum != WD_CHECKSUM) { /* 09 Sep 92*/ -#ifdef WECOMPAT - printf( "we: probe: checksum failed... installing anyway\n"); - printf( "we: Danpex EW-2016 or other 8013 clone card?\n"); -#else /* !WECOMPAT*/ - return (0); -#endif /* !WECOMPAT*/ - } - sc->we_type = inb(is->id_iobase + WD_ROM_OFFSET + 6); -#ifdef nope - if ((sc->we_type & WD_REVMASK) != 2 /* WD8003E or WD8003S */ - && (sc->we_type & WD_REVMASK) != 4 /* WD8003EBT */ - && (sc->we_type & WD_REVMASK) != 6) /* WD8003ELB? */ - return (0); -#endif -/*printf("type %x ", sc->we_type);*/ - if (sc->we_type & WD_SOFTCONFIG) { - int iv = inb(is->id_iobase + 1) & 4 | - ((inb(is->id_iobase+4) & 0x60) >> 5); -/*printf("iv %d ", iv);*/ - if (wemask[iv] != is->id_irq) - return(0); - outb(is->id_iobase+4, inb(is->id_iobase+4) | 0x80); - } - - /* - * Setup card RAM area and i/o addresses - * Kernel Virtual to segment C0000-DFFFF????? - */ - sc->we_io_ctl_addr = is->id_iobase; - sc->we_io_nic_addr = sc->we_io_ctl_addr + WD_NIC_OFFSET; - sc->we_vector = is->id_irq; - sc->we_vmem_addr = (caddr_t)is->id_maddr; - sc->we_vmem_size = is->id_msize; - sc->we_vmem_ring = sc->we_vmem_addr + (WD_PAGE_SIZE * WD_TXBUF_SIZE); - sc->we_vmem_end = sc->we_vmem_addr + is->id_msize; - - /* - * Save board ROM station address - */ - for (i = 0; i < ETHER_ADDR_LEN; ++i) - sc->we_addr[i] = inb(sc->we_io_ctl_addr + WD_ROM_OFFSET + i); - - /* - * Mapin interface memory, setup memory select register - */ - wem.ms_addr = kvtop(sc->we_vmem_addr) >> 13; - wem.ms_enable = 1; - wem.ms_reset = 0; - outb(sc->we_io_ctl_addr, wem.ms_byte); - - /* - * clear interface memory, then sum to make sure its valid - */ - for (i = 0; i < sc->we_vmem_size; ++i) - sc->we_vmem_addr[i] = 0x0; - for (sum = 0, i = 0; i < sc->we_vmem_size; ++i) - sum += sc->we_vmem_addr[i]; - if (sum != 0x0) { - printf("we%d: wd8003 dual port RAM address error\n", is->id_unit); - return (0); - } - - return (WD_IO_PORTS); -} - -/* - * Interface exists: make available by filling in network interface - * record. System will initialize the interface when it is ready - * to accept packets. - */ -weattach(is) - struct isa_device *is; -{ - register struct we_softc *sc = &we_softc[is->id_unit]; - register struct ifnet *ifp = &sc->we_if; - union we_command wecmd; - struct ifaddr *ifa; - struct sockaddr_dl *sdl; - - wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND); - wecmd.cs_stp = 1; - wecmd.cs_sta = 0; - wecmd.cs_ps = 0; - outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); - /* - * Initialize ifnet structure - */ - ifp->if_unit = is->id_unit; - ifp->if_name = "we" ; - ifp->if_mtu = ETHERMTU; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS ; - ifp->if_init = weinit; - ifp->if_output = ether_output; - ifp->if_start = westart; - ifp->if_ioctl = weioctl; - ifp->if_reset = wereset; - ifp->if_watchdog = wewatchdog; - if_attach(ifp); - /* Search down the ifa address list looking for the AF_LINK type entry */ - ifa = ifp->if_addrlist; - while ((ifa != 0) && - (ifa->ifa_addr != 0) && - (ifa->ifa_addr->sa_family != AF_LINK)) - { - ifa = ifa->ifa_next; - } - /* If we find an AF_LINK type entry, we well fill in the hardware addr */ - if ((ifa != 0) && - (ifa->ifa_addr != 0)) - { - /* Fill in the link level address for this interface */ - sdl = (struct sockaddr_dl *)ifa->ifa_addr; - sdl->sdl_type = IFT_ETHER; - sdl->sdl_alen = ETHER_ADDR_LEN; - sdl->sdl_slen = 0; - bcopy(sc->we_addr, LLADDR(sdl), ETHER_ADDR_LEN); - } - -#if NBPFILTER > 0 - sc->we_flags &= ~WDF_ATTACHED; /* Make sure BPF attach flag clear */ -#endif - - /* - * Banner... - */ - printf(" %saddr %s", - (sc->we_type & WD_ETHERNET) ? "enet" : "slan", - ether_sprintf(sc->we_addr)); -} - -/* - * Reset of interface. - */ -wereset(unit, uban) - int unit, uban; -{ - if (unit >= NWE) - return; - printf("we%d: reset\n", unit); -/* we_softc[unit].we_flags &= ~WDF_RUNNING; */ - weinit(unit); -} - -/* - * Take interface offline. - */ -westop(unit) - int unit; -{ - register struct we_softc *sc = &we_softc[unit]; - union we_command wecmd; - int s; - - /* - * Shutdown DS8390 - */ - s = splimp(); - wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND); - wecmd.cs_stp = 1; - wecmd.cs_sta = 0; - wecmd.cs_ps = 0; - outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); - (void) splx(s); -} - -wewatchdog(unit) { - - weintr(unit); - /*log(LOG_WARNING,"we%d: soft reset\n", unit); - westop(unit); - weinit(unit);*/ -} - -static Bdry[NWE]; /* 19 Sep 92*/ -/* - * Initialization of interface (really just DS8390). - */ -weinit(unit) - int unit; -{ - register struct we_softc *sc = &we_softc[unit]; - register struct ifnet *ifp = &sc->we_if; - union we_command wecmd; - int i, s; - -#if NBPFILTER > 0 - if ((sc->we_flags & WDF_ATTACHED) == 0) { - bpfattach(&sc->we_bpf, ifp, DLT_EN10MB, - sizeof(struct ether_header)); - sc->we_flags |= WDF_ATTACHED; - } -#endif - - /* address not known */ - if (ifp->if_addrlist == (struct ifaddr *)0) - return; - - /* already running */ - /*if (ifp->if_flags & IFF_RUNNING) return; */ - - /* - * Initialize DS8390 in order given in NSC NIC manual. - * this is stock code...please see the National manual for details. - */ - s = splhigh(); - Bdry[unit] = 0; /* 19 Sep 92*/ - wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND); - wecmd.cs_stp = 1; - wecmd.cs_sta = 0; - wecmd.cs_ps = 0; - outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); -#ifdef WD8013 /* 20 Sep 92*/ - /* enable 16 bit access if 8013 card */ - outb(sc->we_io_nic_addr + WD_P0_DCR, WD_D_CONFIG16); -#else /* !WD8013*/ - outb(sc->we_io_nic_addr + WD_P0_DCR, WD_D_CONFIG); -#endif /* !WD8013*/ - outb(sc->we_io_nic_addr + WD_P0_RBCR0, 0); - outb(sc->we_io_nic_addr + WD_P0_RBCR1, 0); - outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_MON); - outb(sc->we_io_nic_addr + WD_P0_TCR, WD_T_CONFIG); - outb(sc->we_io_nic_addr + WD_P0_TPSR, 0); - outb(sc->we_io_nic_addr + WD_P0_PSTART, WD_TXBUF_SIZE); - outb(sc->we_io_nic_addr + WD_P0_PSTOP, - sc->we_vmem_size / WD_PAGE_SIZE); - outb(sc->we_io_nic_addr + WD_P0_BNRY, WD_TXBUF_SIZE); - outb(sc->we_io_nic_addr + WD_P0_ISR, 0xff); - outb(sc->we_io_nic_addr + WD_P0_IMR, WD_I_CONFIG); - wecmd.cs_ps = 1; - outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); - for (i = 0; i < ETHER_ADDR_LEN; ++i) - outb(sc->we_io_nic_addr + WD_P1_PAR0 + i, sc->we_addr[i]); - for (i = 0; i < ETHER_ADDR_LEN; ++i) /* == broadcast addr */ - outb(sc->we_io_nic_addr + WD_P1_MAR0 + i, 0xff); - outb(sc->we_io_nic_addr + WD_P1_CURR, WD_TXBUF_SIZE); - wecmd.cs_ps = 0; - wecmd.cs_stp = 0; - wecmd.cs_sta = 1; - wecmd.cs_rd = 0x4; - outb(sc->we_io_nic_addr + WD_P1_COMMAND, wecmd.cs_byte); -#if NBPFILTER > 0 - if (sc->we_if.if_flags & IFF_PROMISC) { - outb(sc->we_io_nic_addr + WD_P0_RCR, - WD_R_PRO | WD_R_SEP | WD_R_AR | WD_R_CONFIG); - } else -#endif - outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_CONFIG); - - /* - * Take the interface out of reset, program the vector, - * enable interrupts, and tell the world we are up. - */ - ifp->if_flags |= IFF_RUNNING; - sc->we_flags &= ~WDF_TXBUSY; - (void) splx(s); - westart(ifp); -} - -/* - * Start output on interface. - */ -westart(ifp) - struct ifnet *ifp; -{ - register struct we_softc *sc = &we_softc[ifp->if_unit]; - struct mbuf *m0, *m; - register caddr_t buffer; - int len, s; - union we_command wecmd; - - /* - * The DS8390 has only one transmit buffer, if it is busy we - * must wait until the transmit interrupt completes. - */ - s = splhigh(); - - if (sc->we_flags & WDF_TXBUSY) { - (void) splx(s); - return; - } - IF_DEQUEUE(&sc->we_if.if_snd, m); - if (m == 0) { - (void) splx(s); - return; - } - sc->we_flags |= WDF_TXBUSY; - - (void) splx(s); - -#if NBPFILTER > 0 - if (sc->we_bpf) { - u_short etype; - int off, datasize, resid; - struct ether_header *eh; - struct trailer_header { - u_short ether_type; - u_short ether_residual; - } trailer_header; - char ether_packet[ETHERMTU+100]; - char *ep; - - ep = ether_packet; - - /* - * We handle trailers below: - * Copy ether header first, then residual data, - * then data. Put all this in a temporary buffer - * 'ether_packet' and send off to bpf. Since the - * system has generated this packet, we assume - * that all of the offsets in the packet are - * correct; if they're not, the system will almost - * certainly crash in m_copydata. - * We make no assumptions about how the data is - * arranged in the mbuf chain (i.e. how much - * data is in each mbuf, if mbuf clusters are - * used, etc.), which is why we use m_copydata - * to get the ether header rather than assume - * that this is located in the first mbuf. - */ - /* copy ether header */ - m_copydata(m, 0, sizeof(struct ether_header), ep); - eh = (struct ether_header *) ep; - ep += sizeof(struct ether_header); - etype = ntohs(eh->ether_type); - if (etype >= ETHERTYPE_TRAIL && - etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { - datasize = ((etype - ETHERTYPE_TRAIL) << 9); - off = datasize + sizeof(struct ether_header); - - /* copy trailer_header into a data structure */ - m_copydata(m, off, sizeof(struct trailer_header), - &trailer_header.ether_type); - - /* copy residual data */ - m_copydata(m, off+sizeof(struct trailer_header), - resid = ntohs(trailer_header.ether_residual) - - sizeof(struct trailer_header), ep); - ep += resid; - - /* copy data */ - m_copydata(m, sizeof(struct ether_header), datasize, ep); - ep += datasize; - - /* restore original ether packet type */ - eh->ether_type = trailer_header.ether_type; - - bpf_tap(sc->we_bpf, ether_packet, ep - ether_packet); - } else - bpf_mtap(sc->we_bpf, m); - } -#endif - - /* - * Copy the mbuf chain into the transmit buffer - */ - buffer = sc->we_vmem_addr; - len = 0; - for (m0 = m; m != 0; m = m->m_next) { - bcopy(mtod(m, caddr_t), buffer, m->m_len); - buffer += m->m_len; - len += m->m_len; - } - - m_freem(m0); - - /* - * Init transmit length registers, and set transmit start flag. - */ - s = splhigh(); - len = MAX(len, ETHER_MIN_LEN); - wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND); - wecmd.cs_ps = 0; - outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); - outb(sc->we_io_nic_addr + WD_P0_TBCR0, len & 0xff); - outb(sc->we_io_nic_addr + WD_P0_TBCR1, len >> 8); - wecmd.cs_txp = 1; - outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); - sc->we_if.if_timer = 3; - (void) splx(s); -} - -/* - * Ethernet interface interrupt processor - */ -weintr(unit) - int unit; -{ - register struct we_softc *sc = &we_softc[unit]; - union we_command wecmd; - union we_interrupt weisr; - - - /* disable onboard interrupts, then get interrupt status */ - wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND); - wecmd.cs_ps = 0; - outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); - weisr.is_byte = inb(sc->we_io_nic_addr + WD_P0_ISR); -loop: - outb(sc->we_io_nic_addr + WD_P0_ISR, weisr.is_byte); - - /* transmit error */ - if (weisr.is_txe) { - /* need to read these registers to clear status */ - sc->we_if.if_collisions += - inb(sc->we_io_nic_addr + WD_P0_TBCR0); - ++sc->we_if.if_oerrors; - } - - /* receiver error */ - if (weisr.is_rxe) { - /* need to read these registers to clear status */ - (void) inb(sc->we_io_nic_addr + 0xD); - (void) inb(sc->we_io_nic_addr + 0xE); - (void) inb(sc->we_io_nic_addr + 0xF); - ++sc->we_if.if_ierrors; - } - - /* normal transmit complete */ - if (weisr.is_ptx || weisr.is_txe) - wetint (unit); - - /* normal receive notification */ - if (weisr.is_prx || weisr.is_rxe) - werint (unit); - - /* try to start transmit */ - westart(&sc->we_if); - - /* re-enable onboard interrupts */ - wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND); - wecmd.cs_ps = 0; - outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); - outb(sc->we_io_nic_addr + WD_P0_IMR, 0xff/*WD_I_CONFIG*/); - weisr.is_byte = inb(sc->we_io_nic_addr + WD_P0_ISR); - if (weisr.is_byte) goto loop; -} - -/* - * Ethernet interface transmit interrupt. - */ -wetint(unit) - int unit; -{ - register struct we_softc *sc = &we_softc[unit]; - - /* - * Do some statistics (assume page zero of NIC mapped in) - */ - sc->we_flags &= ~WDF_TXBUSY; - sc->we_if.if_timer = 0; - ++sc->we_if.if_opackets; - sc->we_if.if_collisions += inb(sc->we_io_nic_addr + WD_P0_TBCR0); -} - -/* - * Ethernet interface receiver interrupt. - */ -werint(unit) - int unit; -{ - register struct we_softc *sc = &we_softc[unit]; - u_char bnry, curr; - long len; - union we_command wecmd; - struct we_ring *wer; - - /* - * Traverse the receive ring looking for packets to pass back. - * The search is complete when we find a descriptor not in use. - */ - wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND); - wecmd.cs_ps = 0; - outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); - bnry = inb(sc->we_io_nic_addr + WD_P0_BNRY); - wecmd.cs_ps = 1; - outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); - curr = inb(sc->we_io_nic_addr + WD_P1_CURR); - if(Bdry[unit]) /* 19 Sep 92*/ - bnry = Bdry[unit]; - - while (bnry != curr) - { - /* get pointer to this buffer header structure */ - wer = (struct we_ring *)(sc->we_vmem_addr + (bnry << 8)); - - /* count includes CRC */ - len = wer->we_count - 4; - if (len > 30 && len <= ETHERMTU+100) - weread(sc, (caddr_t)(wer + 1), len); - else printf("we%d: reject - bad length %d", unit, len); - -outofbufs: - wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND); - wecmd.cs_ps = 0; - outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); - - /* advance on chip Boundry register */ - if((caddr_t) wer + WD_PAGE_SIZE - 1 > sc->we_vmem_end) { - bnry = WD_TXBUF_SIZE; - outb(sc->we_io_nic_addr + WD_P0_BNRY, - sc->we_vmem_size / WD_PAGE_SIZE-1); - - } else { - if (len > 30 && len <= ETHERMTU+100) - bnry = wer->we_next_packet; - else bnry = curr; - - /* watch out for NIC overflow, reset Boundry if invalid */ - if ((bnry - 1) < WD_TXBUF_SIZE) { - outb(sc->we_io_nic_addr + WD_P0_BNRY, - (sc->we_vmem_size / WD_PAGE_SIZE) - 1); - bnry = WD_TXBUF_SIZE; - } else - outb(sc->we_io_nic_addr + WD_P0_BNRY, bnry-1); - } - - /* refresh our copy of CURR */ - wecmd.cs_ps = 1; - outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); - curr = inb(sc->we_io_nic_addr + WD_P1_CURR); - } - Bdry[unit] = bnry; /* 19 Sep 92*/ -} - -/* - * Process an ioctl request. - */ -weioctl(ifp, cmd, data) - register struct ifnet *ifp; - int cmd; - caddr_t data; -{ - register struct ifaddr *ifa = (struct ifaddr *)data; - struct we_softc *sc = &we_softc[ifp->if_unit]; - struct ifreq *ifr = (struct ifreq *)data; - int s = splimp(), error = 0; - - - switch (cmd) { - - case SIOCSIFADDR: - ifp->if_flags |= IFF_UP; - - switch (ifa->ifa_addr->sa_family) { -#ifdef INET - case AF_INET: - weinit(ifp->if_unit); /* before arpwhohas */ - ((struct arpcom *)ifp)->ac_ipaddr = - IA_SIN(ifa)->sin_addr; - arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); - break; -#endif -#ifdef NS - case AF_NS: - { - register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); - - if (ns_nullhost(*ina)) - ina->x_host = *(union ns_host *)(sc->ns_addr); - else { - /* - * The manual says we cant change the address - * while the receiver is armed, - * so reset everything - */ - ifp->if_flags &= ~IFF_RUNNING; - bcopy((caddr_t)ina->x_host.c_host, - (caddr_t)sc->ns_addr, sizeof(sc->ns_addr)); - } - weinit(ifp->if_unit); /* does ne_setaddr() */ - break; - } -#endif - default: - weinit(ifp->if_unit); - break; - } - break; - - case SIOCSIFFLAGS: - if ((ifp->if_flags & IFF_UP) == 0 && - ifp->if_flags & IFF_RUNNING) { - ifp->if_flags &= ~IFF_RUNNING; - westop(ifp->if_unit); - } else if (ifp->if_flags & IFF_UP && - (ifp->if_flags & IFF_RUNNING) == 0) - weinit(ifp->if_unit); -#if NBPFILTER > 0 - if (sc->we_if.if_flags & IFF_PROMISC) { - outb(sc->we_io_nic_addr + WD_P0_RCR, - WD_R_PRO | WD_R_SEP | WD_R_AR | WD_R_CONFIG); - } else -#endif - outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_CONFIG); - break; - -#ifdef notdef - case SIOCGHWADDR: - bcopy((caddr_t)sc->sc_addr, (caddr_t) &ifr->ifr_data, - sizeof(sc->sc_addr)); - break; -#endif - - default: - error = EINVAL; - } - splx(s); - return (error); -} -/* - * set ethernet address for unit - */ -wesetaddr(physaddr, unit) - u_char *physaddr; - int unit; -{ - register struct we_softc *sc = &we_softc[unit]; - register int i; - - /* - * Rewrite ethernet address, and then force restart of NIC - */ - for (i = 0; i < ETHER_ADDR_LEN; i++) - sc->we_addr[i] = physaddr[i]; - sc->we_flags &= ~WDF_RUNNING; - weinit(unit); -} - -#define ringoffset(sc, eh, off, type) \ - ((type)( ((caddr_t)(eh)+(off) >= (sc)->we_vmem_end) ? \ - (((caddr_t)(eh)+(off))) - (sc)->we_vmem_end \ - + (sc)->we_vmem_ring: \ - ((caddr_t)(eh)+(off)) )) -/* - * Pass a packet to the higher levels. - * We deal with the trailer protocol here. - */ -weread(sc, buf, len) - register struct we_softc *sc; - char *buf; - int len; -{ - caddr_t we_ring_copy(); - struct ether_header *eh; - struct mbuf *m, *head, *we_ring_to_mbuf(); - int off, resid; - u_short etype; - struct trailer_header { - u_short trail_type; - u_short trail_residual; - } trailer_header; - - ++sc->we_if.if_ipackets; - - /* Allocate a header mbuf */ - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == 0) - goto bad; - m->m_pkthdr.rcvif = &sc->we_if; - m->m_pkthdr.len = len; - m->m_len = 0; - head = m; - - eh = (struct ether_header *)buf; - -#define EROUND ((sizeof(struct ether_header) + 3) & ~3) -#define EOFF (EROUND - sizeof(struct ether_header)) - - /* - * The following assumes there is room for - * the ether header in the header mbuf - */ - head->m_data += EOFF; - bcopy(buf, mtod(head, caddr_t), sizeof(struct ether_header)); - buf += sizeof(struct ether_header); - head->m_len += sizeof(struct ether_header); - len -= sizeof(struct ether_header); - - etype = ntohs((u_short)eh->ether_type); - - /* - * Deal with trailer protocol: - * If trailer protocol, calculate the datasize as 'off', - * which is also the offset to the trailer header. - * Set resid to the amount of packet data following the - * trailer header. - * Finally, copy residual data into mbuf chain. - */ - if (etype >= ETHERTYPE_TRAIL && - etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { - - off = (etype - ETHERTYPE_TRAIL) << 9; - if ((off + sizeof(struct trailer_header)) > len) - goto bad; /* insanity */ - - eh->ether_type = *ringoffset(sc, buf, off, u_short *); - resid = ntohs(*ringoffset(sc, buf, off+2, u_short *)); - - if ((off + resid) > len) goto bad; /* insanity */ - - resid -= sizeof(struct trailer_header); - if (resid < 0) goto bad; /* insanity */ - - m = we_ring_to_mbuf(sc, ringoffset(sc, buf, off+4, char *), head, resid); - if (m == 0) goto bad; - - len = off; - head->m_pkthdr.len -= 4; /* subtract trailer header */ - } - - /* - * Pull packet off interface. Or if this was a trailer packet, - * the data portion is appended. - */ - m = we_ring_to_mbuf(sc, buf, m, len); - if (m == 0) goto bad; - -#if NBPFILTER > 0 - /* - * Check if there's a bpf filter listening on this interface. - * If so, hand off the raw packet to bpf. - */ - if (sc->we_bpf) { - bpf_mtap(sc->we_bpf, head); - } - - /* - * Note that the interface cannot be in promiscuous mode if - * there are no bpf listeners. And if we are in promiscuous - * mode, we have to check if this packet is really ours. - * - * XXX This test does not support multicasts. - */ - if ((sc->we_if.if_flags & IFF_PROMISC) && - bcmp(eh->ether_dhost, sc->we_addr, - sizeof(eh->ether_dhost)) != 0 && - bcmp(eh->ether_dhost, etherbroadcastaddr, - sizeof(eh->ether_dhost)) != 0) { - - m_freem(head); - return; - } -#endif - - /* - * Fix up data start offset in mbuf to point past ether header - */ - m_adj(head, sizeof(struct ether_header)); - - /* - * silly ether_input routine needs 'type' in host byte order - */ - eh->ether_type = ntohs(eh->ether_type); - - ether_input(&sc->we_if, eh, head); - return; - -bad: if (head) - m_freem(head); - return; -} - -/* - * Supporting routines - */ - -/* - * Copy data from receive buffer to end of mbuf chain - * allocate additional mbufs as needed. return pointer - * to last mbuf in chain. - * sc = we info - * src = pointer in we ring buffer - * dst = pointer to last mbuf in mbuf chain to copy to - * amount = amount of data to copy - */ -struct mbuf * -we_ring_to_mbuf(sc,src,dst,total_len) - struct we_softc *sc; - char *src; - struct mbuf *dst; - int total_len; -{ - register struct mbuf *m = dst; - - while (total_len > 0) { - register int amount = min(total_len, M_TRAILINGSPACE(m)); - - if (amount == 0) { /* no more data in this mbuf, alloc another */ - /* - * if there is enough data for an mbuf cluster, attempt - * to allocate one of those, otherwise, a regular mbuf - * will do. - */ - dst = m; - MGET(m, M_DONTWAIT, MT_DATA); - if (m == 0) - return (0); - - if (total_len >= MINCLSIZE) - MCLGET(m, M_DONTWAIT); - - m->m_len = 0; - dst->m_next = m; - amount = min(total_len, M_TRAILINGSPACE(m)); - } - - src = we_ring_copy(sc, src, mtod(m, caddr_t) + m->m_len, amount); - - m->m_len += amount; - total_len -= amount; - - } - return (m); -} - -static inline char * -we_ring_copy(sc,src,dst,amount) - struct we_softc *sc; - char *src; - char *dst; - int amount; -{ - int tmp_amount; - - /* does copy wrap to lower addr in ring buffer? */ - if (src + amount > sc->we_vmem_end) { - tmp_amount = sc->we_vmem_end - src; - bcopy(src,dst,tmp_amount); /* copy amount up to end */ - amount -= tmp_amount; - src = sc->we_vmem_ring; - dst += tmp_amount; - } - - bcopy(src, dst, amount); - - return(src + amount); -} -#endif - diff --git a/sys/i386/isa/if_wereg.h b/sys/i386/isa/if_wereg.h deleted file mode 100644 index ae6a0cbee7..0000000000 --- a/sys/i386/isa/if_wereg.h +++ /dev/null @@ -1,277 +0,0 @@ -/*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Tim L. Tucker. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)if_wereg.h 7.1 (Berkeley) 5/9/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00100 - * -------------------- ----- ---------------------- - * - * 20 Sep 92 Barry Lustig WD8013 16 bit mode -- enable - * with "options WD8013". - */ - -/* - * Western Digital 8003 ethernet/starlan adapter - */ - -/* - * Memory Select Register (MSR) - */ -union we_mem_sel { - struct memory_decode { - u_char msd_addr:6, /* Memory decode bits */ - msd_enable:1, /* Memory (RAM) enable */ - msd_reset:1; /* Software reset */ - } msd_decode; -#define ms_addr msd_decode.msd_addr -#define ms_enable msd_decode.msd_enable -#define ms_reset msd_decode.msd_reset - u_char ms_byte; /* entire byte */ -}; - -/* 20 Sep 92*/ -/* - * LA Address Register (LAAR) - */ -union we_laar { - struct lan_addr_reg { - u_char addr_l19_b:1, /* Address Line 19 for enabling */ - /* 16 bit NIC access to shared RAM */ - unused_b:5, /* unused (or unknown) bits */ - lan_16_en_b:1, /* Enables 16bit shrd RAM for LAN */ - mem_16_en_b:1; /* Enables 16bit shrd RAM for host */ - } laar_decode; -#define addr_l19 laar_decode.addr_l19_b -#define lan_16_en laar_decode.lan_16_en_b -#define mem_16_en laar_decode.mem_16_en_b - u_char laar_byte; /* entire byte */ -}; - -/* - * receive ring discriptor - * - * The National Semiconductor DS8390 Network interface controller uses - * the following receive ring headers. The way this works is that the - * memory on the interface card is chopped up into 256 bytes blocks. - * A contiguous portion of those blocks are marked for receive packets - * by setting start and end block #'s in the NIC. For each packet that - * is put into the receive ring, one of these headers (4 bytes each) is - * tacked onto the front. - */ -struct we_ring { - struct wer_status { /* received packet status */ - u_char rs_prx:1, /* packet received intack */ - rs_crc:1, /* crc error */ - rs_fae:1, /* frame alignment error */ - rs_fo:1, /* fifo overrun */ - rs_mpa:1, /* packet received intack */ - rs_phy:1, /* packet received intack */ - rs_dis:1, /* packet received intack */ - rs_dfr:1; /* packet received intack */ - } we_rcv_status; /* received packet status */ - u_char we_next_packet; /* pointer to next packet */ - u_short we_count; /* bytes in packet (length + 4) */ -}; - -/* - * Command word definition - */ -union we_command { - struct command_decode { - u_char csd_stp:1, /* STOP! */ - csd_sta:1, /* START! */ - csd_txp:1, /* Transmit packet */ - csd_rd:3, /* Remote DMA command */ - csd_ps:2; /* Page select */ - } csd_decode; -#define cs_stp csd_decode.csd_stp -#define cs_sta csd_decode.csd_sta -#define cs_txp csd_decode.csd_txp -#define cs_rd csd_decode.csd_rd -#define cs_ps csd_decode.csd_ps - u_char cs_byte; /* entire command byte */ -}; - -/* - * Interrupt status definition - */ -union we_interrupt { - struct interrupt_decode { - u_char isd_prx:1, /* Packet received */ - isd_ptx:1, /* Packet transmitted */ - isd_rxe:1, /* Receive error */ - isd_txe:1, /* Transmit error */ - isd_ovw:1, /* Overwrite warning */ - isd_cnt:1, /* Counter overflow */ - isd_rdc:1, /* Remote DMA complete */ - isd_rst:1; /* Reset status */ - } isd_decode; -#define is_prx isd_decode.isd_prx -#define is_ptx isd_decode.isd_ptx -#define is_rxe isd_decode.isd_rxe -#define is_txe isd_decode.isd_txe -#define is_ovw isd_decode.isd_ovw -#define is_cnt isd_decode.isd_cnt -#define is_rdc isd_decode.isd_rdc -#define is_rst isd_decode.isd_rst - u_char is_byte; /* entire interrupt byte */ -}; - -/* - * Status word definition (transmit) - */ -union wet_status { - struct tstat { - u_char tsd_ptx:1, /* Packet transmitted intack */ - tsd_dfr:1, /* Non deferred transmition */ - tsd_col:1, /* Transmit Collided */ - tsd_abt:1, /* Transmit Aborted (coll > 16) */ - tsd_crs:1, /* Carrier Sense Lost */ - tsd_fu:1, /* Fifo Underrun */ - tsd_chd:1, /* CD Heartbeat */ - tsd_owc:1; /* Out of Window Collision */ - } tsd_decode; -#define ts_ptx tsd_decode.tsd_ptx -#define ts_dfr tsd_decode.tsd_dfr -#define ts_col tsd_decode.tsd_col -#define ts_abt tsd_decode.tsd_abt -#define ts_crs tsd_decode.tsd_crs -#define ts_fu tsd_decode.tsd_fu -#define ts_chd tsd_decode.tsd_chd -#define ts_owc tsd_decode.tsd_owc - u_char ts_byte; /* entire transmit byte */ -}; - -/* - * General constant definitions - */ - -/* Bits in the REGE register */ -#define WD_MICROCHANEL 0x80 /* Microchannel bus (vs. isa) */ -#define WD_LARGERAM 0x40 /* Large RAM */ -#define WD_SOFTCONFIG 0x20 /* Soft config */ -#define WD_REVMASK 0x1e /* Revision mask */ -#define WD_ETHERNET 0x01 /* Ethernet (vs. Starlan) */ - -#define WD_CHECKSUM 0xFF /* Checksum byte */ -#define WD_PAGE_SIZE 256 /* Size of RAM pages in bytes */ -#define WD_TXBUF_SIZE 6 /* Size of TX buffer in pages */ -#define WD_ROM_OFFSET 8 /* i/o base offset to ROM */ -#define WD_IO_PORTS 32 /* # of i/o addresses used */ -#define WD_NIC_OFFSET 16 /* i/o base offset to NIC */ - -/* - * Page register offset values - */ -#define WD_P0_COMMAND 0x00 /* Command register */ -#define WD_P0_PSTART 0x01 /* Page Start register */ -#define WD_P0_PSTOP 0x02 /* Page Stop register */ -#define WD_P0_BNRY 0x03 /* Boundary Pointer */ -#define WD_P0_TSR 0x04 /* Transmit Status (read-only) */ -#define WD_P0_TPSR WD_P0_TSR /* Transmit Page (write-only) */ -#define WD_P0_TBCR0 0x05 /* Transmit Byte count, low WO */ -#define WD_P0_TBCR1 0x06 /* Transmit Byte count, high WO */ -#define WD_P0_ISR 0x07 /* Interrupt status register */ -#define WD_P0_RBCR0 0x0A /* Remote byte count low WO */ -#define WD_P0_RBCR1 0x0B /* Remote byte count high WO */ -#define WD_P0_RSR 0x0C /* Receive status RO */ -#define WD_P0_RCR WD_P0_RSR /* Receive configuration WO */ -#define WD_P0_TCR 0x0D /* Transmit configuration WO */ -#define WD_P0_DCR 0x0E /* Data configuration WO */ -#define WD_P0_IMR 0x0F /* Interrupt masks WO */ -#define WD_P1_COMMAND 0x00 /* Command register */ -#define WD_P1_PAR0 0x01 /* Physical address register 0 */ -#define WD_P1_PAR1 0x02 /* Physical address register 1 */ -#define WD_P1_PAR2 0x03 /* Physical address register 2 */ -#define WD_P1_PAR3 0x04 /* Physical address register 3 */ -#define WD_P1_PAR4 0x05 /* Physical address register 4 */ -#define WD_P1_PAR5 0x06 /* Physical address register 5 */ -#define WD_P1_CURR 0x07 /* Current page (receive unit) */ -#define WD_P1_MAR0 0x08 /* Multicast address register 0 */ - -/* - * Configuration constants (receive unit) - */ -#define WD_R_SEP 0x01 /* Save error packets */ -#define WD_R_AR 0x02 /* Accept Runt packets */ -#define WD_R_AB 0x04 /* Accept Broadcast packets */ -#define WD_R_AM 0x08 /* Accept Multicast packets */ -#define WD_R_PRO 0x10 /* Promiscuous physical */ -#define WD_R_MON 0x20 /* Monitor mode */ -#define WD_R_RES1 0x40 /* reserved... */ -#define WD_R_RES2 0x80 /* reserved... */ -#define WD_R_CONFIG (WD_R_AB) - -/* - * Configuration constants (transmit unit) - */ -#define WD_T_CRC 0x01 /* Inhibit CRC */ -#define WD_T_LB0 0x02 /* Encoded Loopback Control */ -#define WD_T_LB1 0x04 /* Encoded Loopback Control */ -#define WD_T_ATD 0x08 /* Auto Transmit Disable */ -#define WD_T_OFST 0x10 /* Collision Offset Enable */ -#define WD_T_RES1 0x20 /* reserved... */ -#define WD_T_RES2 0x40 /* reserved... */ -#define WD_T_RES3 0x80 /* reserved... */ -#define WD_T_CONFIG (0) - -/* - * Configuration constants (data unit) - */ -#define WD_D_WTS 0x01 /* Word Transfer Select */ -#define WD_D_BOS 0x02 /* Byte Order Select */ -#define WD_D_LAS 0x04 /* Long Address Select */ -#define WD_D_BMS 0x08 /* Burst Mode Select */ -#define WD_D_AR 0x10 /* Autoinitialize Remote */ -#define WD_D_FT0 0x20 /* Fifo Threshold Select */ -#define WD_D_FT1 0x40 /* Fifo Threshold Select */ -#define WD_D_RES 0x80 /* reserved... */ -#define WD_D_CONFIG (WD_D_FT1|WD_D_BMS) -#define WD_D_CONFIG16 (WD_D_FT1|WD_D_BMS|WD_D_LAS|WD_D_WTS) /* 20 Sep 92*/ - -/* - * Configuration constants (interrupt mask register) - */ -#define WD_I_PRXE 0x01 /* Packet received enable */ -#define WD_I_PTXE 0x02 /* Packet transmitted enable */ -#define WD_I_RXEE 0x04 /* Receive error enable */ -#define WD_I_TXEE 0x08 /* Transmit error enable */ -#define WD_I_OVWE 0x10 /* Overwrite warning enable */ -#define WD_I_CNTE 0x20 /* Counter overflow enable */ -#define WD_I_RDCE 0x40 /* Dma complete enable */ -#define WD_I_RES 0x80 /* reserved... */ -#define WD_I_CONFIG (WD_I_PRXE|WD_I_PTXE|WD_I_RXEE|WD_I_TXEE) diff --git a/sys/i386/isa/isa.c b/sys/i386/isa/isa.c index 8707b43d4c..ec083129b6 100644 --- a/sys/i386/isa/isa.c +++ b/sys/i386/isa/isa.c @@ -33,27 +33,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)isa.c 7.2 (Berkeley) 5/13/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 4 00163 - * -------------------- ----- ---------------------- - * - * 18 Aug 92 Frank Maclachlan *See comments below - * 25 Mar 93 Rodney W. Grimes Added counter for stray interrupt, - * turned on logging of stray interrupts, - * Now prints maddr, msize, and flags - * after finding a device. - * 26 Apr 93 Bruce Evans New intr-0.1 code - * Rodney W. Grimes Only print io address if id_alive != -1 - * 17 May 93 Rodney W. Grimes renamed stray interrupt counters to - * work with new intr-0.1 code. - * Enabled printf for interrupt masks to - * aid in bug reports. - * 27 May 93 Guido van Rooij New routine add find_isa_dev + * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 + * $Id: isa.c,v 1.4 1993/10/13 15:59:28 rgrimes Exp $ */ -static char rcsid[] = "$Header: /usr/src/sys.386bsd/i386/isa/RCS/isa.c,v 1.2 92/01/21 14:34:23 william Exp Locker: root $"; /* * code to manage AT bus @@ -93,119 +75,161 @@ static char rcsid[] = "$Header: /usr/src/sys.386bsd/i386/isa/RCS/isa.c,v 1.2 92/ /* ** Register definitions for DMA controller 2 (channels 4..7): */ -#define DMA2_CHN(c) (IO_DMA1 + 2*(2*(c))) /* addr reg for channel c */ +#define DMA2_CHN(c) (IO_DMA2 + 2*(2*(c))) /* addr reg for channel c */ #define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */ #define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */ #define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */ int config_isadev __P((struct isa_device *, u_int *)); -#ifdef notyet -struct rlist *isa_iomem; - /* - * Configure all ISA devices + * print a conflict message */ -isa_configure() { - struct isa_device *dvp; - struct isa_driver *dp; - - splhigh(); - INTREN(IRQ_SLAVE); - /*rlist_free(&isa_iomem, 0xa0000, 0xfffff);*/ - for (dvp = isa_devtab_tty; dvp; dvp++) - (void) config_isadev(dvp, &ttymask); - for (dvp = isa_devtab_bio; dvp; dvp++) - (void) config_isadev(dvp, &biomask); - for (dvp = isa_devtab_net; dvp; dvp++) - (void) config_isadev(dvp, &netmask); - for (dvp = isa_devtab_null; dvp; dvp++) - (void) config_isadev(dvp, (u_int *) NULL); -#include "sl.h" -#if NSL > 0 - netmask |= ttymask; - ttymask |= netmask; -#endif -/* printf("biomask %x ttymask %x netmask %x\n", biomask, ttymask, netmask); */ - splnone(); +void +conflict(dvp, tmpdvp, item, reason, format) + struct isa_device *dvp, *tmpdvp; + int item; + char *reason; + char *format; +{ + printf("%s%d not probed due to %s conflict with %s%d at ", + dvp->id_driver->name, dvp->id_unit, reason, + tmpdvp->id_driver->name, tmpdvp->id_unit); + printf(format, item); + printf("\n"); } /* - * Configure an ISA device. + * Check to see if things are alread in use, like IRQ's, I/O addresses + * and Memory addresses. */ -config_isadev(isdp, mp) - struct isa_device *isdp; - u_int *mp; +int +haveseen(dvp, tmpdvp) + struct isa_device *dvp, *tmpdvp; { - struct isa_driver *dp; - static short drqseen, irqseen; - - if (dp = isdp->id_driver) { - /* if a device with i/o memory, convert to virtual address */ - if (isdp->id_maddr) { - extern unsigned int atdevbase; + int status = 0; - isdp->id_maddr -= IOM_BEGIN; - isdp->id_maddr += atdevbase; - } - isdp->id_alive = (*dp->probe)(isdp); - if (isdp->id_alive) { - - printf("%s%d at port 0x%x ", dp->name, - isdp->id_unit, isdp->id_iobase); - - /* check for conflicts */ - if (irqseen & isdp->id_irq) { - printf("INTERRUPT CONFLICT - irq%d\n", - ffs(isdp->id_irq) - 1); - return (0); + /* + * Only check against devices that have already been found + */ + if (tmpdvp->id_alive) { + /* + * Check for I/O address conflict. We can only check the + * starting address of the device against the range of the + * device that has already been probed since we do not + * know how many I/O addresses this device uses. + */ + if (tmpdvp->id_alive != -1) { + if ((dvp->id_iobase >= tmpdvp->id_iobase) && + (dvp->id_iobase <= + (tmpdvp->id_iobase + tmpdvp->id_alive - 1))) { + conflict(dvp, tmpdvp, dvp->id_iobase, + "I/O address", "0x%x"); + status = 1; } - if (isdp->id_drq != -1 - && (drqseen & (1<id_drq))) { - printf("DMA CONFLICT - drq%d\n", isdp->id_drq); - return (0); + } + /* + * Check for Memory address conflict. We can check for + * range overlap, but it will not catch all cases since the + * driver may adjust the msize paramater during probe, for + * now we just check that the starting address does not + * fall within any allocated region. + * XXX could add a second check after the probe for overlap, + * since at that time we would know the full range. + * XXX KERNBASE is a hack, we should have vaddr in the table! + */ + if(tmpdvp->id_maddr) { + if((KERNBASE + dvp->id_maddr >= tmpdvp->id_maddr) && + (KERNBASE + dvp->id_maddr <= + (tmpdvp->id_maddr + tmpdvp->id_msize - 1))) { + conflict(dvp, tmpdvp, dvp->id_maddr, "maddr", + "0x%x"); + status = 1; } - /* NEED TO CHECK IOMEM CONFLICT HERE */ - - /* allocate and wire in device */ - if(isdp->id_irq) { - int intrno; - - intrno = ffs(isdp->id_irq)-1; - printf("irq %d ", intrno); - INTREN(isdp->id_irq); - if(mp)INTRMASK(*mp,isdp->id_irq); - setidt(NRSVIDT + intrno, isdp->id_intr, - SDT_SYS386IGT, SEL_KPL); - irqseen |= isdp->id_irq; + } + /* + * Check for IRQ conflicts. + */ + if(tmpdvp->id_irq) { + if (tmpdvp->id_irq == dvp->id_irq) { + conflict(dvp, tmpdvp, ffs(dvp->id_irq) - 1, + "irq", "%d"); + status = 1; } - if (isdp->id_drq != -1) { - printf("drq %d ", isdp->id_drq); - drqseen |= 1 << isdp->id_drq; + } + /* + * Check for DRQ conflicts. + */ + if(tmpdvp->id_drq != -1) { + if (tmpdvp->id_drq == dvp->id_drq) { + conflict(dvp, tmpdvp, dvp->id_drq, + "drq", "%d"); + status = 1; } + } + } + return (status); +} - (*dp->attach)(isdp); +/* + * Search through all the isa_devtab_* tables looking for anything that + * conflicts with the current device. + */ +int +haveseen_isadev(dvp) + struct isa_device *dvp; +{ + struct isa_device *tmpdvp; + int status = 0; - printf("on isa\n"); - } - return (1); - } else return(0); + for (tmpdvp = isa_devtab_tty; tmpdvp->id_driver; tmpdvp++) { + status |= haveseen(dvp, tmpdvp); + } + for (tmpdvp = isa_devtab_bio; tmpdvp->id_driver; tmpdvp++) { + status |= haveseen(dvp, tmpdvp); + } + for (tmpdvp = isa_devtab_net; tmpdvp->id_driver; tmpdvp++) { + status |= haveseen(dvp, tmpdvp); + } + for (tmpdvp = isa_devtab_null; tmpdvp->id_driver; tmpdvp++) { + status |= haveseen(dvp, tmpdvp); + } + return(status); } -#else /* notyet */ + /* * Configure all ISA devices */ +void isa_configure() { struct isa_device *dvp; - struct isa_driver *dp; enable_intr(); splhigh(); INTREN(IRQ_SLAVE); - for (dvp = isa_devtab_tty; config_isadev(dvp,&ttymask); dvp++); - for (dvp = isa_devtab_bio; config_isadev(dvp,&biomask); dvp++); - for (dvp = isa_devtab_net; config_isadev(dvp,&netmask); dvp++); - for (dvp = isa_devtab_null; config_isadev(dvp,(u_int *) NULL); dvp++); + printf("Probing for devices on the ISA bus:\n"); + for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) { + if (!haveseen_isadev(dvp)) + config_isadev(dvp,&ttymask); + } + for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) { + if (!haveseen_isadev(dvp)) + config_isadev(dvp,&biomask); + } + for (dvp = isa_devtab_net; dvp->id_driver; dvp++) { + if (!haveseen_isadev(dvp)) + config_isadev(dvp,&netmask); + } + for (dvp = isa_devtab_null; dvp->id_driver; dvp++) { + if (!haveseen_isadev(dvp)) + config_isadev(dvp,(u_int *) NULL); + } +/* + * XXX We should really add the tty device to netmask when the line is + * switched to SLIPDISC, and then remove it when it is switched away from + * SLIPDISC. No need to block out ALL ttys during a splnet when only one + * of them is running slip. + */ #include "sl.h" #if NSL > 0 netmask |= ttymask; @@ -223,68 +247,69 @@ config_isadev(isdp, mp) struct isa_device *isdp; u_int *mp; { - struct isa_driver *dp; + struct isa_driver *dp = isdp->id_driver; - if (dp = isdp->id_driver) { - if (isdp->id_maddr) { - extern u_int atdevbase; + if (isdp->id_maddr) { + extern u_int atdevbase; - isdp->id_maddr -= 0xa0000; /* XXX should be a define */ - isdp->id_maddr += atdevbase; - } - isdp->id_alive = (*dp->probe)(isdp); - if (isdp->id_alive) { - printf("%s%d", dp->name, isdp->id_unit); - /* - * The attach should really be after all the printf's - * but until all the drivers are fixed do it here. - * There is a comment below that shows where this - * really belongs. Rod Grimes 04/10/93 - */ - (*dp->attach)(isdp); - /* - * Only print the I/O address range if id_alive != -1 - * Right now this is a temporary fix just for the new - * NPX code so that if it finds a 486 that can use trap - * 16 it will not report I/O addresses. - * Rod Grimes 04/26/94 - */ - if (isdp->id_alive != -1) { - printf(" at 0x%x", isdp->id_iobase); - if ((isdp->id_iobase + isdp->id_alive - 1) != - isdp->id_iobase) - printf("-0x%x", - isdp->id_iobase + - isdp->id_alive - 1); + isdp->id_maddr -= 0xa0000; /* XXX should be a define */ + isdp->id_maddr += atdevbase; + } + isdp->id_alive = (*dp->probe)(isdp); + if (isdp->id_alive) { + /* + * Only print the I/O address range if id_alive != -1 + * Right now this is a temporary fix just for the new + * NPX code so that if it finds a 486 that can use trap + * 16 it will not report I/O addresses. + * Rod Grimes 04/26/94 + */ + printf("%s%d", dp->name, isdp->id_unit); + if (isdp->id_alive != -1) { + printf(" at 0x%x", isdp->id_iobase); + if ((isdp->id_iobase + isdp->id_alive - 1) != + isdp->id_iobase) { + printf("-0x%x", + isdp->id_iobase + + isdp->id_alive - 1); } - if(isdp->id_irq) - printf(" irq %d", ffs(isdp->id_irq)-1); - if (isdp->id_drq != -1) - printf(" drq %d", isdp->id_drq); - if (isdp->id_maddr != 0) - printf(" maddr 0x%x", kvtop(isdp->id_maddr)); - if (isdp->id_msize != 0) - printf(" msize %d", isdp->id_msize); - if (isdp->id_flags != 0) - printf(" flags 0x%x", isdp->id_flags); + } + if(isdp->id_irq) + printf(" irq %d", ffs(isdp->id_irq) - 1); + if (isdp->id_drq != -1) + printf(" drq %d", isdp->id_drq); + if (isdp->id_maddr) + printf(" maddr 0x%x", kvtop(isdp->id_maddr)); + if (isdp->id_msize) + printf(" msize %d", isdp->id_msize); + if (isdp->id_flags) + printf(" flags 0x%x", isdp->id_flags); + if (isdp->id_iobase < 0x100) + printf(" on motherboard\n"); + else printf(" on isa\n"); - /* This is the place the attach should be done! */ - if(isdp->id_irq) { - int intrno; + (*dp->attach)(isdp); - intrno = ffs(isdp->id_irq)-1; - setidt(ICU_OFFSET+intrno, isdp->id_intr, - SDT_SYS386IGT, SEL_KPL); - if(mp) - INTRMASK(*mp,isdp->id_irq); - INTREN(isdp->id_irq); + if(isdp->id_irq) { + int intrno; + + intrno = ffs(isdp->id_irq)-1; + setidt(ICU_OFFSET+intrno, isdp->id_intr, + SDT_SYS386IGT, SEL_KPL); + if(mp) { + INTRMASK(*mp,isdp->id_irq); } + INTREN(isdp->id_irq); + } + } else { + printf("%s%d not found", dp->name, isdp->id_unit); + if (isdp->id_iobase) { + printf(" at 0x%x", isdp->id_iobase); } - return (1); - } else return(0); + printf("\n"); + } } -#endif /* (!) notyet */ #define IDTVEC(name) __CONCAT(X,name) /* default interrupt vector table entries */ @@ -576,12 +601,12 @@ isa_strayintr(d) { /* * Wait "n" microseconds. * Relies on timer 1 counting down from (TIMER_FREQ / hz) at - * (2 * TIMER_FREQ) Hz. + * (1 * TIMER_FREQ) Hz. * Note: timer had better have been programmed before this is first used! * (The standard programming causes the timer to generate a square wave and * the counter is decremented twice every cycle.) */ -#define CF (2 * TIMER_FREQ) +#define CF (1 * TIMER_FREQ) #define TIMER_FREQ 1193182 /* XXX - should be elsewhere */ extern int hz; /* XXX - should be elsewhere */ diff --git a/sys/i386/isa/isa.h b/sys/i386/isa/isa.h index a9c042d4e5..24dc5cd3f1 100644 --- a/sys/i386/isa/isa.h +++ b/sys/i386/isa/isa.h @@ -33,16 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)isa.h 5.7 (Berkeley) 5/9/91 - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 3 00158 - * -------------------- ----- ---------------------- - * - * 15 Feb 93 Julian Elischer Added entries for some scsi adapters - * 06 Apr 93 Rodney W. Grimes Added com3 and com4, added IO_ISASIZES - * section - * 26 Apr 93 Bruce Evans Support for intr-0.1 + * from: @(#)isa.h 5.7 (Berkeley) 5/9/91 + * $Id$ */ /* diff --git a/sys/i386/isa/isa_device.h b/sys/i386/isa/isa_device.h index eea71ecf82..4405c70522 100644 --- a/sys/i386/isa/isa_device.h +++ b/sys/i386/isa/isa_device.h @@ -30,19 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)isa_device.h 7.1 (Berkeley) 5/9/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00163 - * -------------------- ----- ---------------------- - * - * 27 Feb 93 Chris Demetriou Add proper flag handling. - * 10 Mar 93 Rodney W. Grimes Fixed isa_device->id_irq to be - * the u_short instead of short. This - * enables us to use irq15! - * 27 May 93 Guido van Rooij Add prototype find_isadev() - * + * from: @(#)isa_device.h 7.1 (Berkeley) 5/9/91 + * $Id$ */ /* diff --git a/sys/i386/isa/iso8859.font b/sys/i386/isa/iso8859.font new file mode 100644 index 0000000000..4c5e972fc5 --- /dev/null +++ b/sys/i386/isa/iso8859.font @@ -0,0 +1,1230 @@ +/* + * ISO 8859-1 font file + * + * $Id$ + */ + +char font_8x8[256*8] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x7E, 0x81, 0xA5, 0x81, 0xBD, 0x99, 0x81, 0x7E, +0x7E, 0xFF, 0xDB, 0xFF, 0xC3, 0xE7, 0xFF, 0x7E, +0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, +0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, 0x00, +0x38, 0x7C, 0x38, 0xFE, 0xFE, 0x7C, 0x38, 0x7C, +0x10, 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x7C, +0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x00, 0x00, +0xFF, 0xFF, 0xE7, 0xC3, 0xC3, 0xE7, 0xFF, 0xFF, +0x00, 0x3C, 0x66, 0x42, 0x42, 0x66, 0x3C, 0x00, +0xFF, 0xC3, 0x99, 0xBD, 0xBD, 0x99, 0xC3, 0xFF, +0x0F, 0x07, 0x0F, 0x7D, 0xCC, 0xCC, 0xCC, 0x78, +0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, +0x3F, 0x33, 0x3F, 0x30, 0x30, 0x70, 0xF0, 0xE0, +0x7F, 0x63, 0x7F, 0x63, 0x63, 0x67, 0xE6, 0xC0, +0x99, 0x5A, 0x3C, 0xE7, 0xE7, 0x3C, 0x5A, 0x99, +0x80, 0xE0, 0xF8, 0xFE, 0xF8, 0xE0, 0x80, 0x00, +0x02, 0x0E, 0x3E, 0xFE, 0x3E, 0x0E, 0x02, 0x00, +0x18, 0x3C, 0x7E, 0x18, 0x18, 0x7E, 0x3C, 0x18, +0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, +0x7F, 0xDB, 0xDB, 0x7B, 0x1B, 0x1B, 0x1B, 0x00, +0x3E, 0x63, 0x38, 0x6C, 0x6C, 0x38, 0xCC, 0x78, +0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x7E, 0x00, +0x18, 0x3C, 0x7E, 0x18, 0x7E, 0x3C, 0x18, 0xFF, +0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x00, +0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00, +0x00, 0x18, 0x0C, 0xFE, 0x0C, 0x18, 0x00, 0x00, +0x00, 0x30, 0x60, 0xFE, 0x60, 0x30, 0x00, 0x00, +0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xFE, 0x00, 0x00, +0x00, 0x24, 0x66, 0xFF, 0x66, 0x24, 0x00, 0x00, +0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x00, 0x00, +0x00, 0xFF, 0xFF, 0x7E, 0x3C, 0x18, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00, +0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, +0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00, +0x30, 0x7C, 0xC0, 0x78, 0x0C, 0xF8, 0x30, 0x00, +0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00, +0x38, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0x76, 0x00, +0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, +0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, +0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, +0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, +0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60, +0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, +0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, +0x7C, 0xC6, 0xCE, 0xDE, 0xF6, 0xE6, 0x7C, 0x00, +0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00, +0x78, 0xCC, 0x0C, 0x38, 0x60, 0xCC, 0xFC, 0x00, +0x78, 0xCC, 0x0C, 0x38, 0x0C, 0xCC, 0x78, 0x00, +0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x1E, 0x00, +0xFC, 0xC0, 0xF8, 0x0C, 0x0C, 0xCC, 0x78, 0x00, +0x38, 0x60, 0xC0, 0xF8, 0xCC, 0xCC, 0x78, 0x00, +0xFC, 0xCC, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00, +0x78, 0xCC, 0xCC, 0x78, 0xCC, 0xCC, 0x78, 0x00, +0x78, 0xCC, 0xCC, 0x7C, 0x0C, 0x18, 0x70, 0x00, +0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, +0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60, +0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x00, +0x00, 0x00, 0xFC, 0x00, 0x00, 0xFC, 0x00, 0x00, +0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0x00, +0x78, 0xCC, 0x0C, 0x18, 0x30, 0x00, 0x30, 0x00, +0x7C, 0xC6, 0xDE, 0xDE, 0xDE, 0xC0, 0x78, 0x00, +0x30, 0x78, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0x00, +0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xFC, 0x00, +0x3C, 0x66, 0xC0, 0xC0, 0xC0, 0x66, 0x3C, 0x00, +0xF8, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, +0xFE, 0x62, 0x68, 0x78, 0x68, 0x62, 0xFE, 0x00, +0xFE, 0x62, 0x68, 0x78, 0x68, 0x60, 0xF0, 0x00, +0x3C, 0x66, 0xC0, 0xC0, 0xCE, 0x66, 0x3E, 0x00, +0xCC, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0xCC, 0x00, +0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, +0x1E, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, +0xE6, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0xE6, 0x00, +0xF0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00, +0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00, +0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00, +0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00, +0xFC, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, +0x78, 0xCC, 0xCC, 0xCC, 0xDC, 0x78, 0x1C, 0x00, +0xFC, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0xE6, 0x00, +0x78, 0xCC, 0xE0, 0x70, 0x1C, 0xCC, 0x78, 0x00, +0xFC, 0xB4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, +0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xFC, 0x00, +0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, +0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00, +0xC6, 0xC6, 0x6C, 0x38, 0x38, 0x6C, 0xC6, 0x00, +0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x30, 0x78, 0x00, +0xFE, 0xC6, 0x8C, 0x18, 0x32, 0x66, 0xFE, 0x00, +0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, +0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00, +0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, +0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, +0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, +0xE0, 0x60, 0x60, 0x7C, 0x66, 0x66, 0xDC, 0x00, +0x00, 0x00, 0x78, 0xCC, 0xC0, 0xCC, 0x78, 0x00, +0x1C, 0x0C, 0x0C, 0x7C, 0xCC, 0xCC, 0x76, 0x00, +0x00, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, +0x38, 0x6C, 0x60, 0xF0, 0x60, 0x60, 0xF0, 0x00, +0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, +0xE0, 0x60, 0x6C, 0x76, 0x66, 0x66, 0xE6, 0x00, +0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, +0x0C, 0x00, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, +0xE0, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0xE6, 0x00, +0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, +0x00, 0x00, 0xCC, 0xFE, 0xFE, 0xD6, 0xC6, 0x00, +0x00, 0x00, 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, +0x00, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00, +0x00, 0x00, 0xDC, 0x66, 0x66, 0x7C, 0x60, 0xF0, +0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0x1E, +0x00, 0x00, 0xDC, 0x76, 0x66, 0x60, 0xF0, 0x00, +0x00, 0x00, 0x7C, 0xC0, 0x78, 0x0C, 0xF8, 0x00, +0x10, 0x30, 0x7C, 0x30, 0x30, 0x34, 0x18, 0x00, +0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, +0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, +0x00, 0x00, 0xC6, 0xD6, 0xFE, 0xFE, 0x6C, 0x00, +0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00, +0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, +0x00, 0x00, 0xFC, 0x98, 0x30, 0x64, 0xFC, 0x00, +0x1C, 0x30, 0x30, 0xE0, 0x30, 0x30, 0x1C, 0x00, +0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, +0xE0, 0x30, 0x30, 0x1C, 0x30, 0x30, 0xE0, 0x00, +0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3C, 0x7E, 0xFF, 0x7E, 0x18, 0x00, +0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, 0xAA, +0x44, 0x44, 0x44, 0x44, 0x1F, 0x04, 0x04, 0x04, +0x7C, 0x40, 0x40, 0x40, 0x1F, 0x10, 0x10, 0x10, +0x38, 0x44, 0x44, 0x38, 0x1E, 0x11, 0x14, 0x13, +0x40, 0x40, 0x40, 0x7C, 0x1F, 0x10, 0x10, 0x10, +0x38, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, +0x44, 0x64, 0x4C, 0x44, 0x10, 0x10, 0x10, 0x1F, +0x44, 0x44, 0x28, 0x10, 0x1F, 0x04, 0x04, 0x04, +0x18, 0x18, 0x18, 0x18, 0xF8, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xF8, 0x18, 0x18, 0x18, +0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, 0x00, 0x00, +0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0xFF, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xFF, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x00, 0x0C, 0x30, 0x60, 0x18, 0x0C, 0x7E, 0x00, +0x00, 0x30, 0x0C, 0x06, 0x18, 0x30, 0x7E, 0x00, +0x00, 0x00, 0x03, 0x3E, 0x36, 0x36, 0x6C, 0x00, +0x00, 0x00, 0x04, 0x7E, 0x10, 0x7E, 0x40, 0x00, +0x00, 0x1C, 0x30, 0x30, 0x30, 0x30, 0x7E, 0x00, +0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x30, 0x00, 0x30, 0x30, 0x78, 0x78, 0x30, 0x00, +0x00, 0x00, 0x10, 0x7C, 0xC0, 0xC0, 0x7C, 0x10, +0x00, 0x38, 0x60, 0x60, 0xF0, 0x60, 0xFC, 0x00, +0x00, 0xC3, 0x3C, 0x66, 0x66, 0x3C, 0xC3, 0x00, +0x00, 0xCC, 0xCC, 0x78, 0x30, 0xFC, 0x30, 0x00, +0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, +0x7E, 0xC0, 0x7C, 0xC6, 0x7C, 0x06, 0xFC, 0x00, +0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x7C, 0x82, 0xBA, 0xA2, 0xBA, 0x82, 0x7C, 0x00, +0x1C, 0x06, 0x1E, 0x22, 0x1F, 0x3F, 0x00, 0x00, +0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00, +0x00, 0xFE, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x7C, 0x82, 0xBA, 0xB2, 0xAA, 0x82, 0x7C, 0x00, +0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x38, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x10, 0x7C, 0x10, 0x00, 0x7C, 0x00, 0x00, +0x1C, 0x36, 0x06, 0x18, 0x3E, 0x00, 0x00, 0x00, +0x1E, 0x02, 0x0E, 0x02, 0x1E, 0x00, 0x00, 0x00, +0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0xC0, +0x7E, 0xCA, 0xCA, 0x7E, 0x0A, 0x0A, 0x0A, 0x00, +0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, +0x06, 0x0E, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, +0x0E, 0x11, 0x11, 0x11, 0x0E, 0x1F, 0x00, 0x00, +0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00, +0x60, 0xE0, 0x66, 0x6C, 0x33, 0x67, 0x0F, 0x03, +0x60, 0xE0, 0x66, 0x6C, 0x36, 0x6A, 0x04, 0x0E, +0xF0, 0x20, 0x96, 0x6C, 0x33, 0x67, 0x0F, 0x03, +0x30, 0x00, 0x30, 0x60, 0xC0, 0xCC, 0x78, 0x00, +0x18, 0x0C, 0x30, 0x78, 0xCC, 0xFC, 0xCC, 0x00, +0x60, 0xC0, 0x30, 0x78, 0xCC, 0xFC, 0xCC, 0x00, +0x78, 0x84, 0x30, 0x78, 0xCC, 0xFC, 0xCC, 0x00, +0x66, 0x98, 0x30, 0x78, 0xCC, 0xFC, 0xCC, 0x00, +0xCC, 0x00, 0x30, 0x78, 0xCC, 0xFC, 0xCC, 0x00, +0x30, 0x48, 0x30, 0x78, 0xCC, 0xFC, 0xCC, 0x00, +0x3E, 0x78, 0x98, 0x9C, 0xF8, 0x98, 0x9E, 0x00, +0x3C, 0x66, 0xC0, 0xC0, 0xC0, 0x66, 0x1C, 0x30, +0x30, 0x18, 0xFE, 0x62, 0x78, 0x62, 0xFE, 0x00, +0x18, 0x30, 0xFE, 0x62, 0x78, 0x62, 0xFE, 0x00, +0x38, 0x44, 0xFE, 0x62, 0x78, 0x62, 0xFE, 0x00, +0x66, 0x00, 0xFE, 0x62, 0x78, 0x62, 0xFE, 0x00, +0x60, 0x30, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, +0x18, 0x30, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, +0x78, 0x84, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, +0xCC, 0x00, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, +0x78, 0x6C, 0x66, 0xF6, 0x66, 0x6C, 0x78, 0x00, +0x66, 0x98, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0x00, +0x30, 0x18, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, +0x18, 0x30, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, +0x38, 0x44, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, +0x66, 0x98, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, +0xC6, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, +0x00, 0xC6, 0x6C, 0x38, 0x38, 0x6C, 0xC6, 0x00, +0x06, 0x7C, 0xCE, 0x9A, 0xB2, 0xE6, 0x78, 0xC0, +0x60, 0x30, 0xCC, 0xCC, 0xCC, 0xCC, 0xFC, 0x00, +0x18, 0x30, 0xCC, 0xCC, 0xCC, 0xCC, 0xFC, 0x00, +0x78, 0x84, 0xCC, 0xCC, 0xCC, 0xCC, 0xFC, 0x00, +0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xFC, 0x00, +0x18, 0x30, 0xCC, 0xCC, 0x78, 0x30, 0x78, 0x00, +0x60, 0x78, 0x6C, 0x78, 0x60, 0x60, 0x60, 0x00, +0x78, 0xCC, 0xC4, 0xDC, 0xC6, 0xC6, 0xDC, 0xC0, +0x30, 0x18, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, +0x18, 0x30, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, +0x78, 0x84, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, +0x66, 0x98, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, +0xCC, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, +0x30, 0x48, 0x38, 0x0C, 0x7C, 0xCC, 0x76, 0x00, +0x00, 0x00, 0xEC, 0x32, 0x7E, 0xB0, 0x6E, 0x00, +0x00, 0x00, 0x3C, 0x66, 0xC0, 0x66, 0x1C, 0x30, +0x30, 0x18, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, +0x18, 0x30, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, +0x78, 0x84, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, +0xCC, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, +0x60, 0x30, 0x00, 0x70, 0x30, 0x30, 0x78, 0x00, +0x18, 0x30, 0x00, 0x70, 0x30, 0x30, 0x78, 0x00, +0x70, 0x88, 0x00, 0x70, 0x30, 0x30, 0x78, 0x00, +0xCC, 0x00, 0x00, 0x70, 0x30, 0x30, 0x78, 0x00, +0x6C, 0x38, 0x6C, 0x0C, 0x6C, 0xCC, 0x78, 0x00, +0x66, 0x98, 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, +0x60, 0x30, 0x00, 0x7C, 0xC6, 0xC6, 0x7C, 0x00, +0x18, 0x30, 0x00, 0x7C, 0xC6, 0xC6, 0x7C, 0x00, +0x38, 0x44, 0x00, 0x7C, 0xC6, 0xC6, 0x7C, 0x00, +0x66, 0x98, 0x00, 0x7C, 0xC6, 0xC6, 0x7C, 0x00, +0xC6, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x7C, 0x00, +0x00, 0x00, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x00, +0x00, 0x00, 0x06, 0x7C, 0xDE, 0xF6, 0x7C, 0xC0, +0x60, 0x30, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, +0x18, 0x30, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, +0x30, 0x48, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, +0xCC, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, +0x18, 0x30, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, +0xE0, 0x78, 0x6C, 0x66, 0x6C, 0x78, 0xE0, 0x00, +0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8 +}; + +char font_8x14[256*14] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x7E, 0x81, 0xA5, 0x81, 0x81, 0xBD, 0x99, 0x81, +0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xFF, +0xDB, 0xFF, 0xFF, 0xC3, 0xE7, 0xFF, 0x7E, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x7F, 0x7F, +0x7F, 0x7F, 0x3E, 0x1C, 0x08, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x08, 0x1C, 0x3E, 0x7F, 0x3E, +0x1C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x18, 0x3C, 0x3C, 0xE7, 0xE7, 0xE7, 0x18, 0x18, +0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, +0x7E, 0xFF, 0xFF, 0x7E, 0x18, 0x18, 0x3C, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, +0x3C, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xC3, 0xC3, +0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, +0x00, 0x00, 0x3C, 0x24, 0x42, 0x42, 0x24, 0x3C, +0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, +0xC3, 0xDB, 0xBD, 0xBD, 0xDB, 0xC3, 0xFF, 0xFF, +0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x07, 0x0D, 0x19, +0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, +0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, +0x0C, 0x0A, 0x09, 0x09, 0x09, 0x0A, 0x08, 0x38, +0x78, 0x78, 0x30, 0x00, 0x00, 0x00, 0x1F, 0x11, +0x1F, 0x11, 0x11, 0x11, 0x13, 0x37, 0x77, 0x72, +0x20, 0x00, 0x00, 0x00, 0x18, 0x18, 0xDB, 0x3C, +0xE7, 0x3C, 0xDB, 0x18, 0x18, 0x00, 0x00, 0x00, +0x00, 0x00, 0x40, 0x60, 0x70, 0x7C, 0x7F, 0x7C, +0x70, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x03, 0x07, 0x1F, 0x7F, 0x1F, 0x07, 0x03, +0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, +0x7E, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00, +0x00, 0x00, 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, +0x33, 0x33, 0x00, 0x33, 0x33, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7F, 0xDB, 0xDB, 0xDB, 0x7B, 0x1B, +0x1B, 0x1B, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x3E, +0x63, 0x30, 0x1C, 0x36, 0x63, 0x63, 0x36, 0x1C, +0x06, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x7F, 0x00, +0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, +0x18, 0x18, 0x7E, 0x3C, 0x18, 0x7E, 0x00, 0x00, +0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, +0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x0C, 0x06, 0x7F, 0x06, 0x0C, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, +0x7F, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x60, 0x60, +0x7F, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x24, 0x42, 0xFF, 0x42, 0x24, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x18, 0x3C, 0x7E, 0xFF, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, +0x7E, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x18, 0x3C, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, +0x18, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, +0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x7F, 0x36, +0x36, 0x36, 0x7F, 0x36, 0x36, 0x00, 0x00, 0x00, +0x08, 0x08, 0x3E, 0x63, 0x60, 0x60, 0x3E, 0x03, +0x03, 0x63, 0x3E, 0x08, 0x08, 0x00, 0x00, 0x00, +0x00, 0x61, 0x63, 0x06, 0x0C, 0x18, 0x30, 0x63, +0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x36, +0x36, 0x1C, 0x3B, 0x6E, 0x66, 0x66, 0x3B, 0x00, +0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, +0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, +0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x18, +0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x66, 0x3C, 0x7E, 0x3C, 0x66, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, +0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x06, 0x0C, +0x18, 0x30, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3E, 0x63, 0x67, 0x6F, 0x7B, 0x73, +0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, +0x0C, 0x1C, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, +0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x63, +0x03, 0x06, 0x0C, 0x18, 0x30, 0x63, 0x7F, 0x00, +0x00, 0x00, 0x00, 0x00, 0x3E, 0x63, 0x03, 0x03, +0x1E, 0x03, 0x03, 0x63, 0x3E, 0x00, 0x00, 0x00, +0x00, 0x00, 0x06, 0x0E, 0x1E, 0x36, 0x66, 0x7F, +0x06, 0x06, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, +0x7E, 0x60, 0x60, 0x60, 0x7E, 0x03, 0x03, 0x63, +0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x30, +0x60, 0x60, 0x7E, 0x63, 0x63, 0x63, 0x3E, 0x00, +0x00, 0x00, 0x00, 0x00, 0x7F, 0x63, 0x03, 0x06, +0x0C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3E, 0x63, 0x63, 0x63, 0x3E, 0x63, +0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, +0x3E, 0x63, 0x63, 0x63, 0x3F, 0x03, 0x03, 0x06, +0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, +0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, +0x00, 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x30, +0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x7E, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, +0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, +0x00, 0x00, 0x00, 0x00, 0x3E, 0x63, 0x63, 0x06, +0x0C, 0x0C, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3E, 0x63, 0x63, 0x6F, 0x6F, 0x6F, +0x6E, 0x60, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, +0x08, 0x1C, 0x36, 0x63, 0x63, 0x7F, 0x63, 0x63, +0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x33, +0x33, 0x33, 0x3E, 0x33, 0x33, 0x33, 0x7E, 0x00, +0x00, 0x00, 0x00, 0x00, 0x1E, 0x33, 0x60, 0x60, +0x60, 0x60, 0x60, 0x33, 0x1E, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7C, 0x36, 0x33, 0x33, 0x33, 0x33, +0x33, 0x36, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, +0x7F, 0x33, 0x30, 0x34, 0x3C, 0x34, 0x30, 0x33, +0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x33, +0x30, 0x34, 0x3C, 0x34, 0x30, 0x30, 0x78, 0x00, +0x00, 0x00, 0x00, 0x00, 0x1E, 0x33, 0x60, 0x60, +0x60, 0x6F, 0x63, 0x33, 0x1D, 0x00, 0x00, 0x00, +0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x7F, 0x63, +0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, +0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x06, +0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3C, 0x00, +0x00, 0x00, 0x00, 0x00, 0x73, 0x33, 0x36, 0x36, +0x3C, 0x36, 0x36, 0x33, 0x73, 0x00, 0x00, 0x00, +0x00, 0x00, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, +0x30, 0x33, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, +0x63, 0x77, 0x7F, 0x6B, 0x63, 0x63, 0x63, 0x63, +0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x73, +0x7B, 0x7F, 0x6F, 0x67, 0x63, 0x63, 0x63, 0x00, +0x00, 0x00, 0x00, 0x00, 0x1C, 0x36, 0x63, 0x63, +0x63, 0x63, 0x63, 0x36, 0x1C, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7E, 0x33, 0x33, 0x33, 0x3E, 0x30, +0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, +0x1C, 0x36, 0x63, 0x63, 0x63, 0x63, 0x6B, 0x3E, +0x1C, 0x06, 0x03, 0x00, 0x00, 0x00, 0x7E, 0x33, +0x33, 0x33, 0x3E, 0x36, 0x33, 0x33, 0x73, 0x00, +0x00, 0x00, 0x00, 0x00, 0x3E, 0x63, 0x63, 0x30, +0x1C, 0x06, 0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7E, 0x5A, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, +0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, +0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, +0x63, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x08, 0x00, +0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, +0x63, 0x6B, 0x6B, 0x7F, 0x36, 0x00, 0x00, 0x00, +0x00, 0x00, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x36, +0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, +0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, +0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x63, +0x06, 0x0C, 0x18, 0x30, 0x60, 0x63, 0x7F, 0x00, +0x00, 0x00, 0x00, 0x00, 0x3C, 0x30, 0x30, 0x30, +0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, 0x00, 0x00, +0x00, 0x00, 0x40, 0x60, 0x30, 0x18, 0x0C, 0x06, +0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, +0x3C, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x36, 0x63, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, +0x18, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, +0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x30, +0x30, 0x3E, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, +0x63, 0x60, 0x60, 0x63, 0x3E, 0x00, 0x00, 0x00, +0x00, 0x00, 0x0E, 0x06, 0x06, 0x3E, 0x66, 0x66, +0x66, 0x66, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x3E, 0x63, 0x7F, 0x60, 0x63, +0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x33, +0x30, 0x7C, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, +0x66, 0x66, 0x66, 0x3E, 0x06, 0x66, 0x3C, 0x00, +0x00, 0x00, 0x70, 0x30, 0x30, 0x36, 0x3B, 0x33, +0x33, 0x33, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, +0x0C, 0x0C, 0x00, 0x1C, 0x0C, 0x0C, 0x0C, 0x0C, +0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, +0x00, 0x1C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, +0x78, 0x00, 0x00, 0x00, 0x70, 0x30, 0x30, 0x33, +0x36, 0x3C, 0x36, 0x33, 0x73, 0x00, 0x00, 0x00, +0x00, 0x00, 0x1C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, +0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x66, 0x7F, 0x6B, 0x6B, 0x6B, +0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x6E, 0x33, 0x33, 0x33, 0x33, 0x33, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, +0x63, 0x63, 0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x33, 0x33, +0x33, 0x3E, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x3B, 0x66, 0x66, 0x66, 0x3E, +0x06, 0x06, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x6E, 0x33, 0x30, 0x30, 0x30, 0x78, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, +0x63, 0x38, 0x0E, 0x63, 0x3E, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x08, 0x18, 0x7E, 0x18, 0x18, +0x18, 0x1B, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, +0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x08, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, +0x63, 0x6B, 0x6B, 0x7F, 0x36, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x36, 0x1C, +0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x63, 0x66, 0x66, 0x66, 0x3E, +0x06, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x7F, 0x66, 0x0C, 0x18, 0x33, 0x7F, 0x00, +0x00, 0x00, 0x00, 0x00, 0x0E, 0x18, 0x18, 0x18, +0x70, 0x18, 0x18, 0x18, 0x0E, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, +0x70, 0x18, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x18, +0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x6E, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, +0x36, 0x63, 0x63, 0x7F, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x7E, 0x3C, +0x18, 0x00, 0x00, 0x00, 0x55, 0xAA, 0x55, 0xAA, +0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, +0x55, 0xAA, 0x00, 0x44, 0x44, 0x7C, 0x44, 0x44, +0x00, 0x1F, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, +0x00, 0x7C, 0x40, 0x78, 0x40, 0x40, 0x00, 0x1F, +0x10, 0x1E, 0x10, 0x10, 0x00, 0x00, 0x00, 0x38, +0x44, 0x40, 0x44, 0x38, 0x00, 0x1E, 0x11, 0x1E, +0x14, 0x13, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, +0x40, 0x7C, 0x00, 0x1F, 0x10, 0x1E, 0x10, 0x10, +0x00, 0x00, 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, +0x18, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x44, +0x64, 0x54, 0x4C, 0x44, 0x00, 0x10, 0x10, 0x10, +0x10, 0x1F, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, +0x28, 0x10, 0x00, 0x1F, 0x04, 0x04, 0x04, 0x04, +0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x1F, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x0C, 0x18, +0x30, 0x60, 0x30, 0x18, 0x0C, 0x00, 0x7E, 0x00, +0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0C, 0x06, +0x0C, 0x18, 0x30, 0x00, 0x7E, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x03, 0x3E, 0x76, 0x36, +0x36, 0x36, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x02, 0x04, 0x7E, 0x08, 0x10, 0x7E, 0x20, +0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x36, +0x30, 0x30, 0x7C, 0x30, 0x30, 0x73, 0x7E, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, +0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x18, 0x18, 0x00, 0x18, 0x18, 0x3C, 0x3C, 0x3C, +0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, +0x08, 0x3E, 0x63, 0x60, 0x60, 0x63, 0x3E, 0x08, +0x08, 0x00, 0x00, 0x00, 0x1C, 0x36, 0x30, 0x30, +0x7C, 0x30, 0x30, 0x73, 0x7E, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x42, 0x3C, 0x66, 0x66, 0x66, +0x3C, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, 0x7E, +0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, +0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, +0x00, 0x00, 0x00, 0x3E, 0x63, 0x30, 0x1C, 0x36, +0x63, 0x63, 0x36, 0x1C, 0x06, 0x63, 0x3E, 0x00, +0x00, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x3E, 0x41, 0x5D, 0x51, 0x51, 0x5D, 0x41, +0x3E, 0x00, 0x00, 0x00, 0x00, 0x38, 0x04, 0x3C, +0x44, 0x3C, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x36, +0x6C, 0x36, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x03, +0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, +0x41, 0x5D, 0x55, 0x59, 0x55, 0x41, 0x3E, 0x00, +0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, +0x7E, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6C, 0x18, +0x30, 0x64, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x7C, 0x0C, 0x38, 0x0C, 0x6C, +0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x06, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, +0x3B, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x7F, 0xDB, +0xDB, 0xDB, 0x7B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, +0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x0C, 0x0C, 0x06, 0x1C, 0x00, 0x30, +0x70, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x44, +0x44, 0x38, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, 0x36, +0x1B, 0x36, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x60, 0xE0, 0x63, 0x66, 0x6C, 0x18, 0x33, +0x67, 0xCF, 0x1F, 0x03, 0x03, 0x00, 0x00, 0x60, +0xE0, 0x63, 0x66, 0x6C, 0x18, 0x30, 0x6E, 0xC3, +0x06, 0x0C, 0x1F, 0x00, 0x00, 0xF0, 0x30, 0x63, +0xF6, 0x6C, 0x18, 0x33, 0x67, 0xCF, 0x1F, 0x03, +0x03, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, +0x18, 0x30, 0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, +0x60, 0x30, 0x18, 0x00, 0x1C, 0x36, 0x63, 0x63, +0x7F, 0x63, 0x63, 0x00, 0x00, 0x00, 0x03, 0x06, +0x0C, 0x00, 0x1C, 0x36, 0x63, 0x63, 0x7F, 0x63, +0x63, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x36, 0x00, +0x1C, 0x36, 0x63, 0x63, 0x7F, 0x63, 0x63, 0x00, +0x00, 0x00, 0x3B, 0x6E, 0x00, 0x08, 0x1C, 0x36, +0x63, 0x63, 0x7F, 0x63, 0x63, 0x00, 0x00, 0x00, +0x00, 0x63, 0x63, 0x08, 0x1C, 0x36, 0x63, 0x63, +0x7F, 0x63, 0x63, 0x00, 0x00, 0x00, 0x1C, 0x36, +0x1C, 0x00, 0x1C, 0x36, 0x63, 0x63, 0x7F, 0x63, +0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x36, +0x66, 0x66, 0x67, 0x7E, 0x66, 0x66, 0x67, 0x00, +0x00, 0x00, 0x00, 0x00, 0x1E, 0x33, 0x60, 0x60, +0x60, 0x60, 0x60, 0x33, 0x1E, 0x0C, 0x06, 0x1C, +0x60, 0x30, 0x18, 0x00, 0x7F, 0x33, 0x30, 0x3E, +0x30, 0x33, 0x7F, 0x00, 0x00, 0x00, 0x06, 0x0C, +0x18, 0x00, 0x7F, 0x33, 0x30, 0x3E, 0x30, 0x33, +0x7F, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x36, 0x00, +0x7F, 0x33, 0x30, 0x3E, 0x30, 0x33, 0x7F, 0x00, +0x00, 0x00, 0x00, 0x63, 0x63, 0x00, 0x7F, 0x33, +0x30, 0x3E, 0x30, 0x33, 0x7F, 0x00, 0x00, 0x00, +0x60, 0x30, 0x18, 0x00, 0x3C, 0x18, 0x18, 0x18, +0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x06, 0x0C, +0x18, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, +0x3C, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x66, 0x00, +0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, +0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x3C, 0x18, +0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3C, 0x36, 0x33, 0x33, 0x7B, 0x33, +0x33, 0x36, 0x3C, 0x00, 0x00, 0x00, 0x3B, 0x6E, +0x00, 0x63, 0x73, 0x7B, 0x7F, 0x6F, 0x67, 0x63, +0x63, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, +0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00, +0x00, 0x00, 0x03, 0x06, 0x0C, 0x00, 0x1C, 0x36, +0x63, 0x63, 0x63, 0x36, 0x1C, 0x00, 0x00, 0x00, +0x08, 0x1C, 0x36, 0x00, 0x1C, 0x36, 0x63, 0x63, +0x63, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x3B, +0x6E, 0x00, 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, +0x1C, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x00, +0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x36, 0x1C, +0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00, +0x00, 0x03, 0x1E, 0x36, 0x67, 0x6F, 0x6B, 0x7B, +0x73, 0x36, 0x3C, 0x60, 0x00, 0x00, 0x60, 0x30, +0x18, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, +0x3E, 0x00, 0x00, 0x00, 0x03, 0x06, 0x0C, 0x00, +0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3E, 0x00, +0x00, 0x00, 0x08, 0x1C, 0x36, 0x00, 0x63, 0x63, +0x63, 0x63, 0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, +0x00, 0x63, 0x63, 0x00, 0x63, 0x63, 0x63, 0x63, +0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x06, 0x0C, +0x18, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, +0x3C, 0x00, 0x00, 0x00, 0x00, 0x78, 0x30, 0x3E, +0x33, 0x33, 0x3E, 0x30, 0x30, 0x30, 0x78, 0x00, +0x00, 0x00, 0x00, 0x00, 0x1C, 0x36, 0x63, 0x66, +0x6C, 0x67, 0x63, 0x6B, 0x6E, 0x60, 0x60, 0x00, +0x00, 0x60, 0x30, 0x18, 0x00, 0x3C, 0x06, 0x3E, +0x66, 0x66, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x06, +0x0C, 0x18, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, +0x3B, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x36, +0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00, +0x00, 0x00, 0x00, 0x00, 0x3B, 0x6E, 0x00, 0x3C, +0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00, 0x00, 0x00, +0x00, 0x00, 0x66, 0x66, 0x00, 0x3C, 0x06, 0x3E, +0x66, 0x66, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x1C, +0x36, 0x1C, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, +0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x76, 0x0B, 0x3B, 0x6E, 0x68, 0x37, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, +0x63, 0x60, 0x60, 0x63, 0x3E, 0x0C, 0x06, 0x1C, +0x00, 0x30, 0x18, 0x0C, 0x00, 0x3E, 0x63, 0x7F, +0x60, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x03, +0x06, 0x0C, 0x00, 0x3E, 0x63, 0x7F, 0x60, 0x63, +0x3E, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x36, +0x00, 0x3E, 0x63, 0x7F, 0x60, 0x63, 0x3E, 0x00, +0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x00, 0x3E, +0x63, 0x7F, 0x60, 0x63, 0x3E, 0x00, 0x00, 0x00, +0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, +0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x06, +0x0C, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, +0x3C, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x66, +0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, +0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x38, +0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, +0x00, 0x00, 0x36, 0x1C, 0x36, 0x06, 0x3E, 0x66, +0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, +0x3B, 0x6E, 0x00, 0x6E, 0x33, 0x33, 0x33, 0x33, +0x33, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, +0x00, 0x3E, 0x63, 0x63, 0x63, 0x63, 0x3E, 0x00, +0x00, 0x00, 0x00, 0x06, 0x0C, 0x18, 0x00, 0x3E, +0x63, 0x63, 0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, +0x00, 0x08, 0x1C, 0x36, 0x00, 0x3E, 0x63, 0x63, +0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, +0x3B, 0x6E, 0x00, 0x3E, 0x63, 0x63, 0x63, 0x63, +0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, +0x00, 0x3E, 0x63, 0x63, 0x63, 0x63, 0x3E, 0x00, +0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, +0x7E, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x03, 0x3E, 0x67, 0x6F, +0x7B, 0x73, 0x3E, 0x60, 0x00, 0x00, 0x00, 0x60, +0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, +0x3B, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0C, 0x18, +0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3B, 0x00, +0x00, 0x00, 0x00, 0x18, 0x3C, 0x66, 0x00, 0x66, +0x66, 0x66, 0x66, 0x66, 0x3B, 0x00, 0x00, 0x00, +0x00, 0x00, 0x66, 0x66, 0x00, 0x66, 0x66, 0x66, +0x66, 0x66, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x06, +0x0C, 0x18, 0x00, 0x63, 0x66, 0x66, 0x66, 0x3E, +0x06, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x78, +0x30, 0x3E, 0x33, 0x33, 0x33, 0x33, 0x3E, 0x30, +0x78, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x66, +0x66, 0x66, 0x66, 0x3E, 0x06, 0x66, 0x3C, 0x00 +}; + +char font_8x16[256*16] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7E, 0x81, 0xA5, 0x81, 0x81, 0xBD, +0x99, 0x81, 0x81, 0x7E, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7E, 0xFF, 0xDB, 0xFF, 0xFF, 0xC3, +0xE7, 0xFF, 0xFF, 0x7E, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x6C, 0xFE, 0xFE, 0xFE, +0xFE, 0x7C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7C, 0xFE, +0x7C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x18, 0x3C, 0x3C, 0xE7, 0xE7, +0xE7, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, +0x7E, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, +0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xC3, +0xC3, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x42, +0x42, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x99, 0xBD, +0xBD, 0x99, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x00, 0x00, 0x1E, 0x0E, 0x1A, 0x32, 0x78, 0xCC, +0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, +0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3F, 0x33, 0x3F, 0x30, 0x30, 0x30, +0x30, 0x70, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7F, 0x63, 0x7F, 0x63, 0x63, 0x63, +0x63, 0x67, 0xE7, 0xE6, 0xC0, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x18, 0x18, 0xDB, 0x3C, 0xE7, +0x3C, 0xDB, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFE, 0xF8, +0xF0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, +0x00, 0x02, 0x06, 0x0E, 0x1E, 0x3E, 0xFE, 0x3E, +0x1E, 0x0E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, +0x7E, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, +0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7F, 0xDB, 0xDB, 0xDB, 0x7B, 0x1B, +0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00, 0x00, 0x00, +0x00, 0x7C, 0xC6, 0x60, 0x38, 0x6C, 0xC6, 0xC6, +0x6C, 0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xFE, 0xFE, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, +0x7E, 0x3C, 0x18, 0x7E, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x7E, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0C, 0xFE, +0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xFE, +0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, +0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xFF, +0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7C, +0x7C, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x7C, 0x7C, +0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x3C, 0x3C, 0x3C, 0x18, 0x18, +0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, +0x6C, 0xFE, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, +0x18, 0x18, 0x7C, 0xC6, 0xC2, 0xC0, 0x7C, 0x06, +0x06, 0x86, 0xC6, 0x7C, 0x18, 0x18, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xC2, 0xC6, 0x0C, 0x18, +0x30, 0x60, 0xC6, 0x86, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x76, 0xDC, +0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, +0x30, 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x0C, +0x0C, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3C, 0xFF, +0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, +0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0C, 0x18, +0x30, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xDB, 0xDB, +0xC3, 0xC3, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x7E, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7C, 0xC6, 0x06, 0x0C, 0x18, 0x30, +0x60, 0xC0, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7C, 0xC6, 0x06, 0x06, 0x3C, 0x06, +0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x0C, 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, +0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xFE, 0xC0, 0xC0, 0xC0, 0xFC, 0x06, +0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x60, 0xC0, 0xC0, 0xFC, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xFE, 0xC6, 0x06, 0x06, 0x0C, 0x18, +0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, +0x06, 0x06, 0x0C, 0x78, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, +0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, +0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, +0x30, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, +0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0C, 0x06, +0x0C, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x0C, 0x18, 0x18, +0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xDE, 0xDE, +0xDE, 0xDC, 0xC0, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, +0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x66, +0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xC0, +0xC0, 0xC2, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x66, +0x66, 0x66, 0x6C, 0xF8, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68, +0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68, +0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xDE, +0xC6, 0xC6, 0x66, 0x3A, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xFE, 0xC6, +0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, +0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xE6, 0x66, 0x66, 0x6C, 0x78, 0x78, +0x6C, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, +0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xC3, 0xE7, 0xFF, 0xFF, 0xDB, 0xC3, +0xC3, 0xC3, 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xC6, 0xE6, 0xF6, 0xFE, 0xDE, 0xCE, +0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x60, +0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, +0xC6, 0xD6, 0xDE, 0x7C, 0x0C, 0x0E, 0x00, 0x00, +0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x6C, +0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x60, 0x38, 0x0C, +0x06, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xFF, 0xDB, 0x99, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, +0xC3, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xDB, +0xDB, 0xFF, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xC3, 0xC3, 0x66, 0x3C, 0x18, 0x18, +0x3C, 0x66, 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, 0x18, +0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xFF, 0xC3, 0x86, 0x0C, 0x18, 0x30, +0x60, 0xC1, 0xC3, 0xFF, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, +0x30, 0x30, 0x30, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0x70, 0x38, +0x1C, 0x0E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, +0x0C, 0x0C, 0x0C, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, +0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, 0x7C, +0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xE0, 0x60, 0x60, 0x78, 0x6C, 0x66, +0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC0, +0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x1C, 0x0C, 0x0C, 0x3C, 0x6C, 0xCC, +0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xFE, +0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60, +0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xCC, 0xCC, +0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xCC, 0x78, 0x00, +0x00, 0x00, 0xE0, 0x60, 0x60, 0x6C, 0x76, 0x66, +0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, +0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x06, 0x06, 0x00, 0x0E, 0x06, 0x06, +0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3C, 0x00, +0x00, 0x00, 0xE0, 0x60, 0x60, 0x66, 0x6C, 0x78, +0x78, 0x6C, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xFF, 0xDB, +0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66, +0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66, +0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xCC, 0xCC, +0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0x0C, 0x1E, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x76, 0x66, +0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0x60, +0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x10, 0x30, 0x30, 0xFC, 0x30, 0x30, +0x30, 0x30, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, +0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xC3, 0xC3, +0xC3, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xC3, 0xC3, +0xDB, 0xDB, 0xFF, 0x66, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x66, 0x3C, +0x18, 0x3C, 0x66, 0xC3, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x0C, 0xF8, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xCC, 0x18, +0x30, 0x60, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x0E, 0x18, 0x18, 0x18, 0x70, 0x18, +0x18, 0x18, 0x18, 0x0E, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0E, 0x18, +0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6, +0xC6, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x7E, 0xFF, +0xFF, 0x7E, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, +0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, +0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, +0x00, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x88, 0x00, +0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, +0x00, 0xF8, 0x80, 0xF0, 0x80, 0x80, 0x80, 0x00, +0x3E, 0x20, 0x3C, 0x20, 0x20, 0x20, 0x00, 0x00, +0x00, 0x70, 0x88, 0x80, 0x80, 0x88, 0x70, 0x00, +0x3C, 0x22, 0x3C, 0x28, 0x24, 0x22, 0x00, 0x00, +0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF8, 0x00, +0x3E, 0x20, 0x3C, 0x20, 0x20, 0x20, 0x00, 0x00, +0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, +0x18, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, +0x00, 0x88, 0xC8, 0xA8, 0xA8, 0x98, 0x88, 0x00, +0x20, 0x20, 0x20, 0x20, 0x20, 0x3E, 0x00, 0x00, +0x00, 0x88, 0x88, 0x88, 0x88, 0x50, 0x20, 0x00, +0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x00, 0x00, 0x00, 0x0C, 0x18, 0x30, 0x60, 0x30, +0x18, 0x0C, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x30, 0x18, 0x0C, 0x06, 0x0C, +0x18, 0x30, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x02, 0x7E, 0xEC, 0x6C, 0x6C, +0x6C, 0x6C, 0x6C, 0xCC, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x06, 0x0C, 0x7E, 0x18, 0x18, +0x7E, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60, 0x60, +0x60, 0x60, 0xE6, 0xFC, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, +0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, +0x3C, 0x3C, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x18, 0x18, 0x7E, 0xC3, 0xC0, 0xC0, 0xC0, +0xC3, 0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60, 0x60, +0x60, 0x60, 0xE6, 0xFC, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x82, 0xC6, 0x7C, 0xC6, 0xC6, +0xC6, 0xC6, 0x7C, 0xC6, 0x82, 0x00, 0x00, 0x00, +0x00, 0x00, 0xC3, 0x66, 0x3C, 0x18, 0xFF, 0x18, +0xFF, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, +0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x7C, 0xC6, 0x60, 0x38, 0x6C, 0xC6, 0xC6, +0x6C, 0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00, +0x00, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7C, 0xC6, 0x82, 0xBA, 0xA2, 0xA2, +0xBA, 0x82, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x70, 0x18, 0x78, 0xC8, 0x78, 0x00, 0xF8, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6C, 0xD8, +0x6C, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x06, +0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7C, 0xC6, 0x82, 0xBA, 0xAA, 0xB2, +0xAA, 0x82, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, +0x18, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, +0x00, 0x70, 0xD8, 0x30, 0x60, 0xC8, 0xF8, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xF8, 0x18, 0x30, 0x18, 0xD8, 0x70, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, +0x66, 0x7E, 0x63, 0x60, 0xC0, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7F, 0xDB, 0xDB, 0xDB, 0x7B, 0x1B, +0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, +0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x0C, 0x06, 0x3C, 0x00, 0x00, +0x00, 0x60, 0xE0, 0x60, 0x60, 0x60, 0xF0, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x70, 0xD8, 0x88, 0xD8, 0x70, 0x00, 0xF8, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x6C, 0x36, +0x6C, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xC0, 0xC0, 0xC2, 0xC6, 0xCC, 0x18, 0x30, +0x66, 0xCE, 0x96, 0x3E, 0x06, 0x06, 0x00, 0x00, +0x00, 0xC0, 0xC0, 0xC2, 0xC6, 0xCC, 0x18, 0x30, +0x60, 0xCE, 0x9B, 0x06, 0x0C, 0x1F, 0x00, 0x00, +0x00, 0xF0, 0x30, 0x72, 0x36, 0xEC, 0x18, 0x30, +0x66, 0xCE, 0x96, 0x3E, 0x06, 0x06, 0x00, 0x00, +0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, +0xC0, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x60, 0x30, 0x18, 0x00, 0x38, 0x6C, 0xC6, 0xC6, +0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, +0x0C, 0x18, 0x30, 0x00, 0x38, 0x6C, 0xC6, 0xC6, +0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, +0x10, 0x38, 0x6C, 0x00, 0x38, 0x6C, 0xC6, 0xC6, +0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x76, 0xDC, 0x00, 0x38, 0x6C, 0xC6, 0xC6, +0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, +0x00, 0xC6, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, +0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, +0x38, 0x6C, 0x38, 0x00, 0x38, 0x6C, 0xC6, 0xC6, +0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3E, 0x6C, 0xCC, 0xCC, 0xFE, 0xCC, +0xCC, 0xCC, 0xCC, 0xCE, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xC0, +0xC2, 0x66, 0x3C, 0x0C, 0x06, 0x7C, 0x00, 0x00, +0x30, 0x18, 0x0C, 0x00, 0xFE, 0x66, 0x60, 0x7C, +0x60, 0x60, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, +0x18, 0x30, 0x60, 0x00, 0xFE, 0x66, 0x60, 0x7C, +0x60, 0x60, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, +0x10, 0x38, 0x6C, 0x00, 0xFE, 0x66, 0x60, 0x7C, +0x60, 0x60, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, +0x00, 0xC6, 0x00, 0xFE, 0x66, 0x60, 0x60, 0x7C, +0x60, 0x60, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, +0x30, 0x18, 0x0C, 0x00, 0x3C, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x0C, 0x18, 0x30, 0x00, 0x3C, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x18, 0x3C, 0x66, 0x00, 0x3C, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x66, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x78, 0x6C, 0x66, 0x66, 0x66, 0xF6, +0x66, 0x66, 0x6C, 0x78, 0x00, 0x00, 0x00, 0x00, +0x76, 0xDC, 0x00, 0xC6, 0xE6, 0xF6, 0xFE, 0xDE, +0xCE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, +0x60, 0x30, 0x18, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x0C, 0x18, 0x30, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x10, 0x38, 0x6C, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x76, 0xDC, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0xC6, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x10, +0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, +0x02, 0x06, 0x7C, 0xCE, 0xCE, 0xDE, 0xD6, 0xD6, +0xF6, 0xE6, 0xE6, 0x7C, 0xC0, 0x80, 0x00, 0x00, +0x60, 0x30, 0x18, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x0C, 0x18, 0x30, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x10, 0x38, 0x6C, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0xC6, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x0C, 0x18, 0x30, 0x00, 0xC3, 0xC3, 0x66, 0x3C, +0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, +0xF0, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x60, +0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x6C, 0xC6, 0xCE, 0xDC, 0xD8, +0xCC, 0xC6, 0xD6, 0xDC, 0xC0, 0x80, 0x00, 0x00, +0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0C, 0x7C, +0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0C, 0x7C, +0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x10, 0x38, 0x6C, 0x00, 0x78, 0x0C, 0x7C, +0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x76, 0xDC, 0x00, 0x78, 0x0C, 0x7C, +0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xCC, 0x00, 0x00, 0x78, 0x0C, 0x7C, +0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x38, 0x6C, 0x38, 0x00, 0x78, 0x0C, 0x7C, +0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x3B, 0x1B, +0x7E, 0xD8, 0xDC, 0x77, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x60, 0x60, +0x66, 0x3C, 0x0C, 0x06, 0x3C, 0x00, 0x00, 0x00, +0x00, 0x60, 0x30, 0x18, 0x00, 0x7C, 0xC6, 0xFE, +0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x0C, 0x18, 0x30, 0x00, 0x7C, 0xC6, 0xFE, +0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x10, 0x38, 0x6C, 0x00, 0x7C, 0xC6, 0xFE, +0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xC6, 0x00, 0x00, 0x7C, 0xC6, 0xFE, +0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, +0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x06, 0x0C, 0x18, 0x00, 0x38, 0x18, 0x18, +0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x18, 0x3C, 0x66, 0x00, 0x38, 0x18, 0x18, +0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, +0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x6C, 0x38, 0x38, 0x6C, 0x0C, 0x3C, 0x6C, +0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x76, 0xDC, 0x00, 0xDC, 0x66, 0x66, +0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, +0x00, 0x60, 0x30, 0x18, 0x00, 0x7C, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x18, 0x30, 0x60, 0x00, 0x7C, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x10, 0x38, 0x6C, 0x00, 0x7C, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x76, 0xDC, 0x00, 0x7C, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xC6, 0x00, 0x00, 0x7C, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7E, +0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x02, 0x06, 0x7C, 0xCE, 0xDE, +0xD6, 0xF6, 0xE6, 0x7C, 0xC0, 0x80, 0x00, 0x00, +0x00, 0x60, 0x30, 0x18, 0x00, 0xCC, 0xCC, 0xCC, +0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x18, 0x30, 0x60, 0x00, 0xCC, 0xCC, 0xCC, +0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x30, 0x78, 0xCC, 0x00, 0xCC, 0xCC, 0xCC, +0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xCC, 0x00, 0x00, 0xCC, 0xCC, 0xCC, +0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x18, 0x30, 0x60, 0x00, 0xC6, 0xC6, 0xC6, +0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x0C, 0xF8, 0x00, +0x00, 0x00, 0x00, 0xF0, 0x60, 0x7C, 0x66, 0x66, +0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, +0x00, 0x00, 0x66, 0x66, 0x00, 0xEE, 0x66, 0x66, +0x66, 0x66, 0x66, 0x3E, 0x06, 0x0C, 0x38, 0x00 +}; diff --git a/sys/i386/isa/kbd.h b/sys/i386/isa/kbd.h index f60e8c2321..66e5224134 100644 --- a/sys/i386/isa/kbd.h +++ b/sys/i386/isa/kbd.h @@ -1,13 +1,7 @@ /* - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00162 - * -------------------- ----- ---------------------- - * - * 26 May 93 Holger Veit added more 8042 defines - * * Keyboard definitions + * from: unknown origin, 386BSD 0.1 + * $Id$ */ /* Reference: IBM AT Technical Reference Manual, diff --git a/sys/i386/isa/kbdtables.h b/sys/i386/isa/kbdtables.h new file mode 100644 index 0000000000..5acb9af221 --- /dev/null +++ b/sys/i386/isa/kbdtables.h @@ -0,0 +1,858 @@ +/* + * Copyright (C) 1992, 1993 Søren Schmidt + * + * This program is free software; you may redistribute it and/or + * modify it, provided that it retain the above copyright notice + * and the following disclaimer. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Søren Schmidt Email: sos@kmd-ac.dk + * Tritonvej 36 UUCP: ...uunet!dkuug!kmd-ac!sos + * DK9210 Aalborg SO Phone: +45 9814 8076 + * + * $Id$ + */ + +#define META 0x80 /* eight bit for emacs META-key */ + +#ifdef DKKEYMAP +keymap_t key_map = { 0x69, /* DK iso8859 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, NOP, NOP, 0x1B, 0x1B, NOP, NOP, 0x33, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '"', 0x00, 0x00, '@', '"', 0x00, 0x00, 0x00, 0x00, +/* sc=04 */ '3', '#', NOP, NOP, 0x9E, '#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', 0xA4, NOP, NOP, '$', 0xA4, NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '&', NOP, NOP, '6', '&', NOP, NOP, 0x33, 0x00, +/* sc=08 */ '7', '/', NOP, NOP, '{', '/', NOP, NOP, 0x33, 0x00, +/* sc=09 */ '8', '(', 0x1B, 0x1B, '[', '(', 0x1B, 0x1B, 0x00, 0x00, +/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ')', 0x1D, 0x1D, 0x00, 0x00, +/* sc=0b */ '0', '=', NOP, NOP, '}', '=', NOP, NOP, 0x33, 0x00, +/* sc=0c */ '+', '?', NOP, NOP, '+', '?', NOP, NOP, 0x33, 0x00, +/* sc=0d */ '\'', '`', NOP, NOP, '|', '`', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x33, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ 0xE5, 0xC5, NOP, NOP, 0x86, 0x8F, NOP, NOP, 0x33, 0x01, +/* sc=1b */ '"', '^', 0x1E, 0x1E, '~', '^', 0x1E, 0x1E, 0x00, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ 0xE6, 0xC6, NOP, NOP, 0x91, 0x92, NOP, NOP, 0x33, 0x01, +/* sc=28 */ 0xF8, 0xD8, NOP, NOP, 0x9B, 0x9D, NOP, NOP, 0x33, 0x01, +/* sc=29 */ 0xBD, 0xA7, NOP, NOP, 0xBD, 0xA7, NOP, NOP, 0x33, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '\'', '*', NOP, NOP, '\'', '*', NOP, NOP, 0x33, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', '*', '*', '*', '*', '*', '*', 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00, +/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '-', '-', '-', '-', '-', '-', '-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ NOP, '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', '.', '.', '.', '.', '.', '.', 0x00, 0x02, +/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ '<', '>', 0x1C, 0x1C, '\\', '>', 0x1C, 0x1C, 0x00, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x00, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x00, +/* sc=5c */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x00, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00, +/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00, +/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00, +/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00, +/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00, +/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00, +/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00, +/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00, +/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00, +/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), F(54), F(54), 0xFF, 0x00, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +}; +#endif + +#ifdef UKKEYMAP +keymap_t key_map = { 0x69, /* uk iso8859 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, '`', '`', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '"', 0x00, 0x00, '@', '@', 0x00, 0x00, 0x00, 0x00, +/* sc=04 */ '3', 0xA3, NOP, NOP, '#', '#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '^', 0x1E, 0x1E, '^', '^', 0x1E, 0x1E, 0x00, 0x00, +/* sc=08 */ '7', '&', NOP, NOP, '[', '[', 0x1B, 0x1B, 0x30, 0x00, +/* sc=09 */ '8', '*', NOP, NOP, '8', '*', NOP, NOP, 0x33, 0x00, +/* sc=0a */ '9', '(', NOP, NOP, ']', ']', 0x1D, 0x1D, 0x30, 0x00, +/* sc=0b */ '0', ')', NOP, NOP, '{', '{', NOP, NOP, 0x33, 0x00, +/* sc=0c */ '-', '_', 0x1F, 0x1F, '|', '|', 0x1F, 0x1F, 0x00, 0x00, +/* sc=0d */ '=', '+', NOP, NOP, '}', '}', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x00, +/* sc=1b */ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ ';', ':', NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x00, +/* sc=28 */ '\'', '@', 0x00, 0x00, '\'', '@', 0x00, 0x00, 0x00, 0x00, +/* sc=29 */ '\\', '|', 0x1C, 0x1C, '\\', '\\', 0x1C, 0x1C, 0x00, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '#', '~', NOP, NOP, '~', '~', NOP, NOP, 0x33, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', '<', NOP, NOP, ',', '<', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', '>', NOP, NOP, '.', '>', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '/', '?', NOP, NOP, '/', '?', NOP, NOP, 0x33, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00, +/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x02, +/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02, +/* sc=5c */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x02, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00, +/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00, +/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00, +/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00, +/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00, +/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00, +/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00, +/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00, +/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00, +/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), F(54), F(54), 0xFF, 0x00, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +}; +#endif + +#ifdef GRKEYMAP +keymap_t key_map = { 0x69, /* german iso8859 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, '`', '`', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '"', 0x00, 0x00, '@', '@', 0x00, 0x00, 0x00, 0x00, +/* sc=04 */ '3', 0xA7, NOP, NOP, '#', '#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '&', 0x1E, 0x1E, '^', '^', 0x1E, 0x1E, 0x00, 0x00, +/* sc=08 */ '7', '/', 0x1B, 0x1B, '[', '[', 0x1B, 0x1B, 0x00, 0x00, +/* sc=09 */ '8', '(', NOP, NOP, '8', '(', NOP, NOP, 0x33, 0x00, +/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ']', 0x1D, 0x1D, 0x00, 0x00, +/* sc=0b */ '0', '=', NOP, NOP, '{', '{', NOP, NOP, 0x33, 0x00, +/* sc=0c */ 0xDF, '?', NOP, NOP, '|', '|', NOP, NOP, 0x33, 0x00, +/* sc=0d */ 0x92, 0x93, NOP, NOP, '\'', '`', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ 0xFC, 0xDC, 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x01, +/* sc=1b */ '+', '*', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ 0xF6, 0xD6, NOP, NOP, 0xF6, 0xD6, NOP, NOP, 0x33, 0x01, +/* sc=28 */ 0xE4, 0xC4, NOP, NOP, 0xE4, 0xC4, NOP, NOP, 0x33, 0x01, +/* sc=29 */ '<', '>', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '#', '^', 0x1E, 0x1E, '`', '~', 0x1E, 0x1E, 0x00, 0x00, +/* sc=2c */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00, +/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x02, +/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02, +/* sc=5c */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x02, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00, +/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00, +/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00, +/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00, +/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00, +/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00, +/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00, +/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00, +/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00, +/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), F(54), F(54), 0xFF, 0x00, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +}; +#endif + +#ifdef SWKEYMAP +keymap_t key_map = { 0x69, /* swedish iso8859 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, +/* sc=03 */ '2', '"', NOP, NOP, NOP, '@', NOP, NOP, 0x3B, 0x00, +/* sc=04 */ '3', 0xA3, NOP, NOP, NOP, '#', NOP, NOP, 0x3B, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, +/* sc=07 */ '6', '&', 0x1E, NOP, NOP, '^', 0x1E, NOP, 0x19, 0x00, +/* sc=08 */ '7', '/', NOP, NOP, NOP, '&', NOP, NOP, 0x3B, 0x00, +/* sc=09 */ '8', '(', NOP, NOP, NOP, '*', NOP, NOP, 0x3B, 0x00, +/* sc=0a */ '9', ')', NOP, NOP, NOP, '(', NOP, NOP, 0x3B, 0x00, +/* sc=0b */ '0', '=', NOP, NOP, NOP, ')', NOP, NOP, 0x3B, 0x00, +/* sc=0c */ '+', '?', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, +/* sc=0d */ 0xB4, '`', NOP, NOP, '=', '+', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ 0xE5, 0xC5, NOP, NOP, '[', '{', 0x1B, NOP, 0x31, 0x01, +/* sc=1b */ 0xA8, '^', NOP, NOP, ']', '}', 0x1D, NOP, 0x31, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ 0xF8, 0xD8, NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x01, +/* sc=28 */ 0xE6, 0xC6, NOP, NOP, '\'', '"', NOP, NOP, 0x33, 0x01, +/* sc=29 */ '<', '>', NOP, NOP, '\\', '|', 0x1C, NOP, 0x31, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '\'', '*', NOP, NOP, '`', '~', NOP, NOP, 0x33, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', ';', NOP, NOP, NOP, '<', NOP, NOP, 0x3B, 0x00, +/* sc=34 */ '.', ':', NOP, NOP, NOP, '>', NOP, NOP, 0x3B, 0x00, +/* sc=35 */ '-', '_', 0x1F, NOP, '/', '?', NOP, NOP, 0x13, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00, +/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x02, +/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02, +/* sc=5c */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x02, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00, +/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00, +/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00, +/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00, +/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00, +/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00, +/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00, +/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00, +/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00, +/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), F(54), F(54), 0xFF, 0x00, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +}; +#endif + +#ifdef RUKEYMAP +keymap_t key_map = { 0xe9, /* keys number */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * ------------------------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, NOP, NOP, META|0x1B, META|0x1B, NOP, NOP, 0x33, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, META|'1', META|'!', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '@', 0x00, 0x00, META|'2', META|'@', META|0x00, META|0x00, 0x00, 0x00, +/* sc=04 */ '3', '#', NOP, NOP, META|'3', META|'#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, META|'4', META|'$', NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, META|'5', META|'%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '^', 0x1E, 0x1E, META|'6', META|'^', META|0x1E, META|0x1E, 0x00, 0x00, +/* sc=08 */ '7', '&', NOP, NOP, META|'7', META|'&', NOP, NOP, 0x33, 0x00, +/* sc=09 */ '8', '*', NOP, NOP, META|'8', META|'*', NOP, NOP, 0x33, 0x00, +/* sc=0a */ '9', '(', NOP, NOP, META|'9', META|'(', NOP, NOP, 0x33, 0x00, +/* sc=0b */ '0', ')', NOP, NOP, META|'0', META|')', NOP, NOP, 0x33, 0x00, +/* sc=0c */ '-', '_', 0x1F, 0x1F, META|'-', META|'_', META|0x1F, META|0x1F, 0x00, 0x00, +/* sc=0d */ '=', '+', NOP, NOP, META|'=', META|'+', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, META|0x08, META|0x08, META|0x7F, META|0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, F(16), NOP, NOP, META|0x09, F(16), NOP, NOP, 0x77, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, META|'q', META|'Q', META|0x11, META|0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, META|'w', META|'W', META|0x17, META|0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, META|'e', META|'E', META|0x05, META|0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, META|'r', META|'R', META|0x12, META|0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, META|'t', META|'T', META|0x14, META|0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, META|'y', META|'Y', META|0x19, META|0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, META|'u', META|'U', META|0x15, META|0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, META|'i', META|'I', META|0x09, META|0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, META|'o', META|'O', META|0x0F, META|0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, META|'p', META|'P', META|0x10, META|0x10, 0x00, 0x01, +/* sc=1a */ '[', '{', 0x1B, 0x1B, META|'[', META|'{', META|0x1B, META|0x1B, 0x00, 0x00, +/* sc=1b */ ']', '}', 0x1D, 0x1D, META|']', META|'}', META|0x1D, META|0x1D, 0x00, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, META|0x0D, META|0x0D, META|0x0A, META|0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, META|'a', META|'A', META|0x01, META|0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, META|'s', META|'S', META|0x13, META|0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, META|'d', META|'D', META|0x04, META|0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, META|'f', META|'F', META|0x06, META|0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, META|'g', META|'G', META|0x07, META|0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, META|'h', META|'H', META|0x08, META|0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, META|'j', META|'J', META|0x0A, META|0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, META|'k', META|'K', META|0x0B, META|0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, META|'l', META|'L', META|0x0C, META|0x0C, 0x00, 0x01, +/* sc=27 */ ';', ':', NOP, NOP, META|';', META|':', NOP, NOP, 0x33, 0x00, +/* sc=28 */ '\'', '"', NOP, NOP, META|'\'', META|'"', NOP, NOP, 0x33, 0x00, +/* sc=29 */ '`', '~', NOP, NOP, META|'`', META|'~', NOP, NOP, 0x33, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '\\', '|', 0x1C, 0x1C, META|'\\', META|'|', META|0x1C, META|0x1C, 0x00, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, META|'z', META|'Z', META|0x1A, META|0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, META|'x', META|'X', META|0x18, META|0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, META|'c', META|'C', META|0x03, META|0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, META|'v', META|'V', META|0x16, META|0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, META|'b', META|'B', META|0x02, META|0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, META|'n', META|'N', META|0x0E, META|0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, META|'m', META|'M', META|0x0D, META|0x0D, 0x00, 0x01, +/* sc=33 */ ',', '<', NOP, NOP, META|',', META|'<', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', '>', NOP, NOP, META|'.', META|'>', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '/', '?', NOP, NOP, META|'/', META|'?', NOP, NOP, 0x33, 0x00, +/* sc=36 */ ASH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, META|'*', META|'*', META|0x0A, META|0x0A, 0x00, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', META|' ', META|' ', META|' ', META|' ', 0x00, 0x00, +/* sc=3a */ ALK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00, +/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=47 */ F(49), '7', '7', '7', META|'7', META|'7', META|'7', META|'7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', META|'8', META|'8', META|'8', META|'8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', META|'9', META|'9', META|'9', META|'9', 0x80, 0x02, +/* sc=4a */ F(52), '-', '-', '-', META|'-', META|'-', META|'-', META|'-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', META|'4', META|'4', META|'4', META|'4', 0x80, 0x02, +/* sc=4c */ F(48), '5', '5', '5', META|'5', META|'5', META|'5', META|'5', 0x80, 0x02, +/* sc=4d */ F(55), '6', '6', '6', META|'6', META|'6', META|'6', META|'6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', META|'+', META|'+', META|'+', META|'+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', META|'1', META|'1', META|'1', META|'1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', META|'2', META|'2', META|'2', META|'2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', META|'3', META|'3', META|'3', META|'3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', META|'0', META|'0', META|'0', META|'0', 0x80, 0x02, +/* sc=53 */ F(54), '.', 0x7F, 0x7F, META|0x7F, META|0x7F, META|0x7F, META|0x7F, 0x80, 0x02, +/* sc=54 */ ALK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0A, 0x0A, META|0x0D, META|0x0D, META|0x0A, META|0x0A, 0x00, 0x00, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', NOP, NOP, META|'/', META|'/', NOP, NOP, 0x33, 0x00, +/* sc=5c */ CLK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00, +/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00, +/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00, +/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00, +/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00, +/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00, +/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00, +/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00, +/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00, +/* sc=67 */ F(54), F(54), 0x7F, 0x7F, META|0x7F, META|0x7F, META|0x7F, META|0x7F, 0xC0, 0x00, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=69 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6b */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6c */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6d */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6e */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=6f */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=70 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=71 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=72 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=73 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=74 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=75 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=76 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=77 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=78 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=79 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=7a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=7b */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=7c */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=7d */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=7e */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=7f */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* extended (ALTGR LOCK keys) */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, NOP, NOP, META|0x1B, META|0x1B, NOP, NOP, 0x33, 0x00, +/* sc=02 */ '!', '1', NOP, NOP, META|'1', META|'!', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '"', '2', 0x00, 0x00, META|'2', META|'@', META|0x00, META|0x00, 0x00, 0x00, +/* sc=04 */ '\'', '3', NOP, NOP, META|'3', META|'#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ ';', '4', NOP, NOP, META|'4', META|'$', NOP, NOP, 0x33, 0x00, +/* sc=06 */ ':', '5', NOP, NOP, META|'5', META|'%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ ',', '6', 0x1E, 0x1E, META|'6', META|'^', META|0x1E, META|0x1E, 0x00, 0x00, +/* sc=08 */ '.', '7', NOP, NOP, META|'7', META|'&', NOP, NOP, 0x33, 0x00, +/* sc=09 */ '*', '8', NOP, NOP, META|'8', META|'*', NOP, NOP, 0x33, 0x00, +/* sc=0a */ '(', '9', NOP, NOP, META|'9', META|'(', NOP, NOP, 0x33, 0x00, +/* sc=0b */ ')', '0', NOP, NOP, META|'0', META|')', NOP, NOP, 0x33, 0x00, +/* sc=0c */ '_', '-', 0x1F, 0x1F, META|'-', META|'_', META|0x1F, META|0x1F, 0x00, 0x00, +/* sc=0d */ '+', '=', NOP, NOP, META|'=', META|'+', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, META|0x08, META|0x08, META|0x7F, META|0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, F(16), NOP, NOP, META|0x09, F(16), NOP, NOP, 0x77, 0x00, +/* sc=10 */ 0xca, 0xea, 0x11, 0x11, META|'q', META|'Q', META|0x11, META|0x11, 0x00, 0x01, +/* sc=11 */ 0xc3, 0xe3, 0x17, 0x17, META|'w', META|'W', META|0x17, META|0x17, 0x00, 0x01, +/* sc=12 */ 0xd5, 0xf5, 0x05, 0x05, META|'e', META|'E', META|0x05, META|0x05, 0x00, 0x01, +/* sc=13 */ 0xcb, 0xeb, 0x12, 0x12, META|'r', META|'R', META|0x12, META|0x12, 0x00, 0x01, +/* sc=14 */ 0xc5, 0xe5, 0x14, 0x14, META|'t', META|'T', META|0x14, META|0x14, 0x00, 0x01, +/* sc=15 */ 0xce, 0xee, 0x19, 0x19, META|'y', META|'Y', META|0x19, META|0x19, 0x00, 0x01, +/* sc=16 */ 0xc7, 0xe7, 0x15, 0x15, META|'u', META|'U', META|0x15, META|0x15, 0x00, 0x01, +/* sc=17 */ 0xdb, 0xfb, 0x09, 0x09, META|'i', META|'I', META|0x09, META|0x09, 0x00, 0x01, +/* sc=18 */ 0xdd, 0xfd, 0x0F, 0x0F, META|'o', META|'O', META|0x0F, META|0x0F, 0x00, 0x01, +/* sc=19 */ 0xda, 0xfa, 0x10, 0x10, META|'p', META|'P', META|0x10, META|0x10, 0x00, 0x01, +/* sc=1a */ 0xc8, 0xe8, 0x1B, 0x1B, META|'[', META|'{', META|0x1B, META|0x1B, 0x00, 0x01, +/* sc=1b */ 0xdf, 0xff, 0x1D, 0x1D, META|']', META|'}', META|0x1D, META|0x1D, 0x00, 0x01, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, META|0x0D, META|0x0D, META|0x0A, META|0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 0xc6, 0xe6, 0x01, 0x01, META|'a', META|'A', META|0x01, META|0x01, 0x00, 0x01, +/* sc=1f */ 0xd9, 0xf9, 0x13, 0x13, META|'s', META|'S', META|0x13, META|0x13, 0x00, 0x01, +/* sc=20 */ 0xd7, 0xf7, 0x04, 0x04, META|'d', META|'D', META|0x04, META|0x04, 0x00, 0x01, +/* sc=21 */ 0xc1, 0xe1, 0x06, 0x06, META|'f', META|'F', META|0x06, META|0x06, 0x00, 0x01, +/* sc=22 */ 0xd0, 0xf0, 0x07, 0x07, META|'g', META|'G', META|0x07, META|0x07, 0x00, 0x01, +/* sc=23 */ 0xd2, 0xf2, 0x08, 0x08, META|'h', META|'H', META|0x08, META|0x08, 0x00, 0x01, +/* sc=24 */ 0xcf, 0xef, 0x0A, 0x0A, META|'j', META|'J', META|0x0A, META|0x0A, 0x00, 0x01, +/* sc=25 */ 0xcc, 0xec, 0x0B, 0x0B, META|'k', META|'K', META|0x0B, META|0x0B, 0x00, 0x01, +/* sc=26 */ 0xc4, 0xe4, 0x0C, 0x0C, META|'l', META|'L', META|0x0C, META|0x0C, 0x00, 0x01, +/* sc=27 */ 0xd6, 0xf6, NOP, NOP, META|';', META|':', NOP, NOP, 0x33, 0x01, +/* sc=28 */ 0xdc, 0xfc, NOP, NOP, META|'\'', META|'"', NOP, NOP, 0x33, 0x01, +/* sc=29 */ 0xa3, 0xb3, NOP, NOP, META|'`', META|'~', NOP, NOP, 0x33, 0x01, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '\\', '|', 0x1C, 0x1C, META|'\\', META|'|', META|0x1C, META|0x1C, 0x00, 0x00, +/* sc=2c */ 0xd1, 0xf1, 0x1A, 0x1A, META|'z', META|'Z', META|0x1A, META|0x1A, 0x00, 0x01, +/* sc=2d */ 0xde, 0xfe, 0x18, 0x18, META|'x', META|'X', META|0x18, META|0x18, 0x00, 0x01, +/* sc=2e */ 0xd3, 0xf3, 0x03, 0x03, META|'c', META|'C', META|0x03, META|0x03, 0x00, 0x01, +/* sc=2f */ 0xcd, 0xed, 0x16, 0x16, META|'v', META|'V', META|0x16, META|0x16, 0x00, 0x01, +/* sc=30 */ 0xc9, 0xe9, 0x02, 0x02, META|'b', META|'B', META|0x02, META|0x02, 0x00, 0x01, +/* sc=31 */ 0xd4, 0xf4, 0x0E, 0x0E, META|'n', META|'N', META|0x0E, META|0x0E, 0x00, 0x01, +/* sc=32 */ 0xd8, 0xf8, 0x0D, 0x0D, META|'m', META|'M', META|0x0D, META|0x0D, 0x00, 0x01, +/* sc=33 */ 0xc2, 0xe2, NOP, NOP, META|',', META|'<', NOP, NOP, 0x33, 0x01, +/* sc=34 */ 0xc0, 0xe0, NOP, NOP, META|'.', META|'>', NOP, NOP, 0x33, 0x01, +/* sc=35 */ '/', '?', NOP, NOP, META|'/', META|'?', NOP, NOP, 0x33, 0x00, +/* sc=36 */ ASH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, META|'*', META|'*', META|0x0A, META|0x0A, 0x00, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', META|' ', META|' ', META|' ', META|' ', 0x00, 0x00, +/* sc=3a */ ALK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00, +/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=47 */ F(49), '7', '7', '7', META|'7', META|'7', META|'7', META|'7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', META|'8', META|'8', META|'8', META|'8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', META|'9', META|'9', META|'9', META|'9', 0x80, 0x02, +/* sc=4a */ F(52), '-', '-', '-', META|'-', META|'-', META|'-', META|'-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', META|'4', META|'4', META|'4', META|'4', 0x80, 0x02, +/* sc=4c */ F(48), '5', '5', '5', META|'5', META|'5', META|'5', META|'5', 0x80, 0x02, +/* sc=4d */ F(55), '6', '6', '6', META|'6', META|'6', META|'6', META|'6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', META|'+', META|'+', META|'+', META|'+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', META|'1', META|'1', META|'1', META|'1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', META|'2', META|'2', META|'2', META|'2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', META|'3', META|'3', META|'3', META|'3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', META|'0', META|'0', META|'0', META|'0', 0x80, 0x02, +/* sc=53 */ F(54), '.', 0x7F, 0x7F, META|0x7F, META|0x7F, META|0x7F, META|0x7F, 0x80, 0x02, +/* sc=54 */ ALK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0A, 0x0A, META|0x0D, META|0x0D, META|0x0A, META|0x0A, 0x00, 0x00, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', NOP, NOP, META|'/', META|'/', NOP, NOP, 0x33, 0x00, +/* sc=5c */ CLK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00, +/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00, +/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00, +/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00, +/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00, +/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00, +/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00, +/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00, +/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00, +/* sc=67 */ F(54), F(54), 0x7F, 0x7F, META|0x7F, META|0x7F, META|0x7F, META|0x7F, 0xC0, 0x00, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +}; + +#endif + +#if !defined(DKKEYMAP) && !defined(UKKEYMAP) && !defined(GRKEYMAP) && !defined(SWKEYMAP) && !defined(RUKEYMAP) +keymap_t key_map = { 0x69, /* US iso8859 keymap */ +/* alt + * scan cntrl alt alt cntrl + * code base shift cntrl shift alt shift cntrl shift spcl flgs + * --------------------------------------------------------------------------- + */ +/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=01 */ 0x1B, 0x1B, NOP, NOP, 0x1B, 0x1B, NOP, NOP, 0x33, 0x00, +/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00, +/* sc=03 */ '2', '@', 0x00, 0x00, '2', '@', 0x00, 0x00, 0x00, 0x00, +/* sc=04 */ '3', '#', NOP, NOP, '3', '#', NOP, NOP, 0x33, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00, +/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '^', 0x1E, 0x1E, '6', '^', 0x1E, 0x1E, 0x00, 0x00, +/* sc=08 */ '7', '&', NOP, NOP, '7', '&', NOP, NOP, 0x33, 0x00, +/* sc=09 */ '8', '*', NOP, NOP, '8', '*', NOP, NOP, 0x33, 0x00, +/* sc=0a */ '9', '(', NOP, NOP, '9', '(', NOP, NOP, 0x33, 0x00, +/* sc=0b */ '0', ')', NOP, NOP, '0', ')', NOP, NOP, 0x33, 0x00, +/* sc=0c */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, +/* sc=0d */ '=', '+', NOP, NOP, '=', '+', NOP, NOP, 0x33, 0x00, +/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00, +/* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x33, 0x00, +/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, +/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01, +/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01, +/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01, +/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01, +/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01, +/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01, +/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, +/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, +/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, +/* sc=1a */ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x00, +/* sc=1b */ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00, +/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, +/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, +/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, +/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01, +/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01, +/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01, +/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01, +/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01, +/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, +/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, +/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, +/* sc=27 */ ';', ':', NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x00, +/* sc=28 */ '\'', '"', NOP, NOP, '\'', '"', NOP, NOP, 0x33, 0x00, +/* sc=29 */ '`', '~', NOP, NOP, '`', '~', NOP, NOP, 0x33, 0x00, +/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, +/* sc=2b */ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00, +/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, +/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, +/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, +/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01, +/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01, +/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01, +/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01, +/* sc=33 */ ',', '<', NOP, NOP, ',', '<', NOP, NOP, 0x33, 0x00, +/* sc=34 */ '.', '>', NOP, NOP, '.', '>', NOP, NOP, 0x33, 0x00, +/* sc=35 */ '/', '?', NOP, NOP, '/', '?', NOP, NOP, 0x33, 0x00, +/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00, +/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00, +/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00, +/* sc=39 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0x00, 0x00, +/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00, +/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00, +/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00, +/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00, +/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00, +/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00, +/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00, +/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00, +/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00, +/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00, +/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00, +/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00, +/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02, +/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02, +/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02, +/* sc=4a */ F(52), '-', '-', '-', '-', '-', '-', '-', 0x80, 0x02, +/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02, +/* sc=4c */ NOP, '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02, +/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02, +/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02, +/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02, +/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02, +/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02, +/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02, +/* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x02, +/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, +/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, +/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x00, 0x00, +/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00, +/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x00, +/* sc=5c */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x00, +/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00, +/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00, +/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00, +/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00, +/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00, +/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00, +/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00, +/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00, +/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00, +/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00, +/* sc=67 */ F(54), F(54), F(54), F(54), F(54), F(54), F(54), F(54), 0xFF, 0x00, +/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00, +}; + +#endif + +fkeytab_t fkey_tab[60] = { +/* 00-03 */ {"\033[M", 3}, {"\033[N", 3}, {"\033[O", 3}, {"\033[P", 3}, +/* 04-07 */ {"\033[Q", 3}, {"\033[R", 3}, {"\033[S", 3}, {"\033[T", 3}, +/* 08-0B */ {"\033[U", 3}, {"\033[V", 3}, {"\033[W", 3}, {"\033[X", 3}, +/* 0C-0F */ {"\033[W", 3}, {"\033[X", 3}, {"\033[Y", 3}, {"\033[Z", 3}, +/* 10-13 */ {"\033[a", 3}, {"\033[b", 3}, {"\033[c", 3}, {"\033[d", 3}, +/* 14-17 */ {"\033[e", 3}, {"\033[f", 3}, {"\033[g", 3}, {"\033[h", 3}, +/* 18-1B */ {"\033[g", 3}, {"\033[h", 3}, {"\033[i", 3}, {"\033[j", 3}, +/* 1C-1F */ {"\033[k", 3}, {"\033[l", 3}, {"\033[m", 3}, {"\033[n", 3}, +/* 20-23 */ {"\033[o", 3}, {"\033[p", 3}, {"\033[q", 3}, {"\033[r", 3}, +/* 24-27 */ {"\033[g", 3}, {"\033[h", 3}, {"\033[i", 3}, {"\033[j", 3}, +/* 28-2B */ {"\033[k", 3}, {"\033[l", 3}, {"\033[m", 3}, {"\033[n", 3}, +/* 2C-2F */ {"\033[o", 3}, {"\033[p", 3}, {"\033[q", 3}, {"\033[r", 3}, +/* 30-33 */ {"\033[H", 3}, {"\033[A", 3}, {"\033[I", 3}, {"-" , 1}, +/* 34-37 */ {"\033[D", 3}, {"\177" , 1}, {"\033[C", 3}, {"+" , 1}, +/* 38-3B */ {"\033[F", 3}, {"\033[B", 3}, {"\033[G", 3}, {"\033[L", 3} +}; diff --git a/sys/i386/isa/lpa.c b/sys/i386/isa/lpa.c index f639efd427..96c2a9b288 100644 --- a/sys/i386/isa/lpa.c +++ b/sys/i386/isa/lpa.c @@ -45,30 +45,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00164 - * -------------------- ----- ---------------------- - * - * 01 June 93 Rodney W. Grimes Made lpflag uniq now is lpaflag - * Added timeout loop to lpa_port_test. - * lpa_port_test should move to a common - * routine.. - * + * $Id$ */ /* - * Device Driver for AT parallel printer port - * Written by William Jolitz 12/18/90 - * Modified to run without interrupts - * 92-08-19 Wolfgang Stanglmeier - * Slight cleanup and reorganization, try to handle restarted syscalls - * 92-09-08 Andy Valencia - * 93-04-08 Rodney W. Grimes - * Converted to driver name lpa, shares lptreg.h with lpt.c, fixed probe - * so that it returns IO_LPTSIZE. Added dummy lpaioctl. Added my new - * probe code from lpt.c, as the one in here was crud. + * Device Driver for AT parallel printer port, without using interrupts */ #include "lpa.h" @@ -129,8 +110,8 @@ struct isa_driver lpadriver = {lpaprobe, lpaattach, "lpa"}; */ #define TIMEOUT (hz*16) /* Timeout while open device */ #define LONG (hz* 1) /* Timesteps while open */ - -#define MAX_SPIN 255 /* max loop counter for busy wait */ +#define MAX_SLEEP (hz*5) /* Timeout while waiting for device ready */ +#define MAX_SPIN 20 /* Max delay for device ready in usecs */ struct lpa_softc { char *sc_cp; /* current data to print */ @@ -140,7 +121,6 @@ struct lpa_softc { short sc_ctrl; /* printer status port */ u_char sc_flags; /* flags (open and internal) */ u_char sc_unit; /* unit-number */ - u_char sc_smax; /* current max busy loop cnt */ char /* buffer for data */ *sc_inbuf; } lpa_sc[NLPA]; @@ -306,7 +286,7 @@ lpaopen(dev, flag) } /* sleep a moment */ - if ((err = tsleep (sc, LPPRI, "lpa: open", LONG)) != + if ((err = tsleep (sc, LPPRI, "lpaopen", LONG)) != EWOULDBLOCK) { sc->sc_flags = 0; return (EBUSY); @@ -337,36 +317,40 @@ pushbytes(sc) ch = *(sc->sc_cp); sc->sc_cp += 1; sc->sc_count -= 1; - outb(sc->sc_data, ch); - /* Busy wait for printer ready .. */ - spin = tic = 0; - while (NOT_READY()) { - if (++spin >= sc->sc_smax) { + /* + * Wait for printer ready. + * Loop 20 usecs testing BUSY bit, then sleep + * for exponentially increasing timeout. (vak) + */ + for (spin=0; NOT_READY() && spin= MAX_SPIN) { + tic = 0; + while (NOT_READY()) { /* * Now sleep, every cycle a * little longer .. */ tic = tic + tic + 1; - err = tsleep(sc, LPPRI, "lpa: write", tic); + /* + * But no more than 10 seconds. (vak) + */ + if (tic > MAX_SLEEP) + tic = MAX_SLEEP; + err = tsleep(sc, LPPRI, "lpawrite", tic); if (err != EWOULDBLOCK) { return (err); } } } + /* output data */ + outb(sc->sc_data, ch); /* strobe */ outb(sc->sc_ctrl, LPC_NINIT|LPC_SEL|LPC_STB); outb(sc->sc_ctrl, LPC_NINIT|LPC_SEL); - /* Adapt busy-wait length... */ - if (spin >= sc->sc_smax) { /* was sleep wait */ - if (sc->sc_smaxsc_smax++; - } - if (spin*2 < sc->sc_smax) { - sc->sc_smax--; - } } return(0); } diff --git a/sys/i386/isa/lpt.c b/sys/i386/isa/lpt.c index 46d1e47c4a..6d65d8a8d1 100644 --- a/sys/i386/isa/lpt.c +++ b/sys/i386/isa/lpt.c @@ -45,22 +45,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00164 - * -------------------- ----- ---------------------- - * - * 06 Apr 93 Eric Haug Fixed comments and includes. [Ed: I did - * not include the unit-1 thing, that is a - * DOSism, fixed the config file instead] - * 06 Apr 93 Rodney W. Grimes A real probe routine, may even cause on - * interrupt if a printer is attached. - * - * 01 Jun 93 Rodney W. Grimes Made lpflag uniq now is lptflag - * Added timeout loop to lpt_port_test. - * lpt_port_test should move to a common - * routine.. - * + * from: unknown origin, 386BSD 0.1 + * $Id$ */ /* @@ -97,12 +83,13 @@ int lptflag = 1; #endif -int lptout(); +void lptout(); #ifdef DEBUG int lptflag = 1; #endif -int lptprobe(), lptattach(), lptintr(); +int lptprobe(), lptattach(); +void lptintr(); struct isa_driver lptdriver = { lptprobe, lptattach, "lpt" @@ -143,7 +130,10 @@ struct lpt_softc { * Internal routine to lptprobe to do port tests of one byte value */ int -lpt_port_test(short port, u_char data, u_char mask) +lpt_port_test(port, data, mask) + short port; + u_char data; + u_char mask; { int temp, timeout; @@ -182,8 +172,9 @@ lpt_port_test(short port, u_char data, u_char mask) */ int -lptprobe(struct isa_device *dvp) - { +lptprobe(dvp) + struct isa_device *dvp; +{ int status; short port; u_char data; @@ -227,6 +218,7 @@ lptprobe(struct isa_device *dvp) return (status); } +int lptattach(isdp) struct isa_device *isdp; { @@ -242,17 +234,23 @@ lptattach(isdp) * lptopen -- reset the printer, then wait until it's selected and not busy. */ +int lptopen(dev, flag) dev_t dev; int flag; { - struct lpt_softc *sc = lpt_sc + LPTUNIT(minor(dev)); + struct lpt_softc *sc; int s; int trys, port; + u_int unit = LPTUNIT(minor(dev)); + + if (unit >= NLPT) + return (ENXIO); + sc = lpt_sc + unit; if (sc->sc_state) { lprintf("lp: still open\n") ; -printf("still open %x\n", sc->sc_state); +lprintf("still open %x\n", sc->sc_state); return(EBUSY); } else sc->sc_state |= INIT; @@ -278,7 +276,7 @@ lprintf("lp flags 0x%x\n", sc->sc_flags); if (trys++ >= LPINITRDY*4) { splx(s); sc->sc_state = 0; -printf ("status %x\n", inb(port+lpt_status) ); +lprintf ("status %x\n", inb(port+lpt_status) ); return (EBUSY); } @@ -310,6 +308,7 @@ lprintf("opened.\n"); return(0); } +void lptout (sc) struct lpt_softc *sc; { int pl; @@ -339,7 +338,9 @@ lprintf ("T %x ", inb(sc->sc_port+lpt_status)); * lptclose -- close the device, free the local line buffer. */ +int lptclose(dev, flag) + dev_t dev; int flag; { struct lpt_softc *sc = lpt_sc + LPTUNIT(minor(dev)); @@ -365,6 +366,7 @@ lprintf("closed.\n"); * putc to get the chars moved to the output queue. */ +int lptwrite(dev, uio) dev_t dev; struct uio *uio; @@ -398,7 +400,9 @@ lprintf("W "); * ready to accept another char. */ +void lptintr(unit) + int unit; { struct lpt_softc *sc = lpt_sc + unit; int port = sc->sc_port,sts; @@ -433,7 +437,11 @@ lprintf("sts %x ", sts); } int -lptioctl(dev_t dev, int cmd, caddr_t data, int flag) +lptioctl(dev, cmd, data, flag) + dev_t dev; + int cmd; + caddr_t data; + int flag; { int error; diff --git a/sys/i386/isa/lptreg.h b/sys/i386/isa/lptreg.h index 2605fa0ec1..9e10ba91a5 100644 --- a/sys/i386/isa/lptreg.h +++ b/sys/i386/isa/lptreg.h @@ -5,9 +5,8 @@ * This code is derived from software contributed to Berkeley by * William Jolitz. * - * %sccs.include.noredist.c% - * - * @(#)lptreg.h 1.1 (Berkeley) 12/19/90 + * form: @(#)lptreg.h 1.1 (Berkeley) 12/19/90 + * $Id$ */ /* diff --git a/sys/i386/isa/mcd.c b/sys/i386/isa/mcd.c new file mode 100644 index 0000000000..3064d08ed3 --- /dev/null +++ b/sys/i386/isa/mcd.c @@ -0,0 +1,1260 @@ +/* + * Copyright 1993 by Holger Veit (data part) + * Copyright 1993 by Brian Moore (audio part) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This software was developed by Holger Veit and Brian Moore + * for use with "386BSD" and similar operating systems. + * "Similar operating systems" includes mainly non-profit oriented + * systems for research and education, including but not restricted to + * "NetBSD", "FreeBSD", "Mach" (by CMU). + * 4. Neither the name of the developer(s) nor the name "386BSD" + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER(S) BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: mcd.c,v 1.1 1993/10/12 06:08:29 rgrimes Exp $ + */ +static char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore"; + +#include "mcd.h" +#if NMCD > 0 +#include "types.h" +#include "param.h" +#include "systm.h" +#include "conf.h" +#include "file.h" +#include "buf.h" +#include "stat.h" +#include "uio.h" +#include "ioctl.h" +#include "cdio.h" +#include "errno.h" +#include "dkbad.h" +#include "disklabel.h" +#include "i386/isa/isa.h" +#include "i386/isa/isa_device.h" +#include "mcdreg.h" + +/* user definable options */ +/*#define MCD_TO_WARNING_ON*/ /* define to get timeout messages */ +/*#define MCDMINI*/ /* define for a mini configuration for boot kernel */ + + +#ifdef MCDMINI +#define MCD_TRACE(fmt,a,b,c,d) +#ifdef MCD_TO_WARNING_ON +#undef MCD_TO_WARNING_ON +#endif +#else +#define MCD_TRACE(fmt,a,b,c,d) {if (mcd_data[unit].debug) {printf("mcd%d st=%02x: ",unit,mcd_data[unit].status); printf(fmt,a,b,c,d);}} +#endif + +#define mcd_part(dev) ((minor(dev)) & 7) +#define mcd_unit(dev) (((minor(dev)) & 0x38) >> 3) +#define mcd_phys(dev) (((minor(dev)) & 0x40) >> 6) +#define RAW_PART 3 + +/* flags */ +#define MCDOPEN 0x0001 /* device opened */ +#define MCDVALID 0x0002 /* parameters loaded */ +#define MCDINIT 0x0004 /* device is init'd */ +#define MCDWAIT 0x0008 /* waiting for something */ +#define MCDLABEL 0x0010 /* label is read */ +#define MCDPROBING 0x0020 /* probing */ +#define MCDREADRAW 0x0040 /* read raw mode (2352 bytes) */ +#define MCDVOLINFO 0x0080 /* already read volinfo */ +#define MCDTOC 0x0100 /* already read toc */ +#define MCDMBXBSY 0x0200 /* local mbx is busy */ + +/* status */ +#define MCDAUDIOBSY MCD_ST_AUDIOBSY /* playing audio */ +#define MCDDSKCHNG MCD_ST_DSKCHNG /* sensed change of disk */ +#define MCDDSKIN MCD_ST_DSKIN /* sensed disk in drive */ +#define MCDDOOROPEN MCD_ST_DOOROPEN /* sensed door open */ + +/* toc */ +#define MCD_MAXTOCS 104 /* from the Linux driver */ +#define MCD_LASTPLUS1 170 /* special toc entry */ + +struct mcd_mbx { + short unit; + short port; + short retry; + short nblk; + int sz; + u_long skip; + struct buf *bp; + int p_offset; + short count; +}; + +struct mcd_data { + short config; + short flags; + short status; + int blksize; + u_long disksize; + int iobase; + struct disklabel dlabel; + int partflags[MAXPARTITIONS]; + int openflags; + struct mcd_volinfo volinfo; +#ifndef MCDMINI + struct mcd_qchninfo toc[MCD_MAXTOCS]; + short audio_status; + struct mcd_read2 lastpb; +#endif + short debug; + struct buf head; /* head of buf queue */ + struct mcd_mbx mbx; +} mcd_data[NMCD]; + +/* reader state machine */ +#define MCD_S_BEGIN 0 +#define MCD_S_BEGIN1 1 +#define MCD_S_WAITSTAT 2 +#define MCD_S_WAITMODE 3 +#define MCD_S_WAITREAD 4 + +/* prototypes */ +int mcdopen(dev_t dev); +int mcdclose(dev_t dev); +int mcdstrategy(struct buf *bp); +int mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags); +int mcdsize(dev_t dev); +static void mcd_done(struct mcd_mbx *mbx); +static void mcd_start(int unit); +static int mcd_getdisklabel(int unit); +static void mcd_configure(struct mcd_data *cd); +static int mcd_get(int unit, char *buf, int nmax); +static void mcd_setflags(int unit,struct mcd_data *cd); +static int mcd_getstat(int unit,int sflg); +static int mcd_send(int unit, int cmd,int nretrys); +static int bcd2bin(bcd_t b); +static bcd_t bin2bcd(int b); +static void hsg2msf(int hsg, bcd_t *msf); +static int msf2hsg(bcd_t *msf); +static int mcd_volinfo(int unit); +static int mcd_waitrdy(int port,int dly); +static void mcd_doread(int state, struct mcd_mbx *mbxin); +#ifndef MCDMINI +static int mcd_setmode(int unit, int mode); +static int mcd_getqchan(int unit, struct mcd_qchninfo *q); +static int mcd_subchan(int unit, struct ioc_read_subchannel *sc); +static int mcd_toc_header(int unit, struct ioc_toc_header *th); +static int mcd_read_toc(int unit); +static int mcd_toc_entry(int unit, struct ioc_read_toc_entry *te); +static int mcd_stop(int unit); +static int mcd_playtracks(int unit, struct ioc_play_track *pt); +static int mcd_play(int unit, struct mcd_read2 *pb); +static int mcd_pause(int unit); +static int mcd_resume(int unit); +#endif + +extern int hz; +extern int mcd_probe(struct isa_device *dev); +extern int mcd_attach(struct isa_device *dev); +struct isa_driver mcddriver = { mcd_probe, mcd_attach, "mcd" }; + +#define mcd_put(port,byte) outb(port,byte) + +#define MCD_RETRYS 5 +#define MCD_RDRETRYS 8 + +#define MCDBLK 2048 /* for cooked mode */ +#define MCDRBLK 2352 /* for raw mode */ + +/* several delays */ +#define RDELAY_WAITSTAT 300 +#define RDELAY_WAITMODE 300 +#define RDELAY_WAITREAD 800 + +#define DELAY_STATUS 10000l /* 10000 * 1us */ +#define DELAY_GETREPLY 200000l /* 200000 * 2us */ +#define DELAY_SEEKREAD 20000l /* 20000 * 1us */ +#define mcd_delay DELAY + +int mcd_attach(struct isa_device *dev) +{ + struct mcd_data *cd = mcd_data + dev->id_unit; + int i; + + cd->iobase = dev->id_iobase; + cd->flags |= MCDINIT; + cd->openflags = 0; + for (i=0; ipartflags[i] = 0; + +#ifdef NOTYET + /* wire controller for interrupts and dma */ + mcd_configure(cd); +#endif + + return 1; +} + +int mcdopen(dev_t dev) +{ + int unit,part,phys; + struct mcd_data *cd; + + unit = mcd_unit(dev); + if (unit >= NMCD) + return ENXIO; + + cd = mcd_data + unit; + part = mcd_part(dev); + phys = mcd_phys(dev); + + /* not initialized*/ + if (!(cd->flags & MCDINIT)) + return ENXIO; + + /* invalidated in the meantime? mark all open part's invalid */ + if (!(cd->flags & MCDVALID) && cd->openflags) + return ENXIO; + + if (mcd_getstat(unit,1) < 0) + return ENXIO; + + /* XXX get a default disklabel */ + mcd_getdisklabel(unit); + + if (mcdsize(dev) < 0) { + printf("mcd%d: failed to get disk size\n",unit); + return ENXIO; + } else + cd->flags |= MCDVALID; + +MCD_TRACE("open: partition=%d, disksize = %d, blksize=%d\n", + part,cd->disksize,cd->blksize,0); + + if (part == RAW_PART || + (part < cd->dlabel.d_npartitions && + cd->dlabel.d_partitions[part].p_fstype != FS_UNUSED)) { + cd->partflags[part] |= MCDOPEN; + cd->openflags |= (1<partflags[part] |= MCDREADRAW; + return 0; + } + + return ENXIO; +} + +int mcdclose(dev_t dev) +{ + int unit,part,phys; + struct mcd_data *cd; + + unit = mcd_unit(dev); + if (unit >= NMCD) + return ENXIO; + + cd = mcd_data + unit; + part = mcd_part(dev); + phys = mcd_phys(dev); + + if (!(cd->flags & MCDINIT)) + return ENXIO; + + mcd_getstat(unit,1); /* get status */ + + /* close channel */ + cd->partflags[part] &= ~(MCDOPEN|MCDREADRAW); + cd->openflags &= ~(1<b_dev); + + cd = mcd_data + unit; + + /* test validity */ +/*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n", + bp,unit,bp->b_blkno,bp->b_bcount);*/ + if (unit >= NMCD || bp->b_blkno < 0) { + printf("mcdstrategy: unit = %d, blkno = %d, bcount = %d\n", + unit, bp->b_blkno, bp->b_bcount); + pg("mcd: mcdstratregy failure"); + bp->b_error = EINVAL; + bp->b_flags |= B_ERROR; + goto bad; + } + + /* if device invalidated (e.g. media change, door open), error */ + if (!(cd->flags & MCDVALID)) { +MCD_TRACE("strategy: drive not valid\n",0,0,0,0); + bp->b_error = EIO; + goto bad; + } + + /* read only */ + if (!(bp->b_flags & B_READ)) { + bp->b_error = EROFS; + goto bad; + } + + /* no data to read */ + if (bp->b_bcount == 0) + goto done; + + /* for non raw access, check partition limits */ + if (mcd_part(bp->b_dev) != RAW_PART) { + if (!(cd->flags & MCDLABEL)) { + bp->b_error = EIO; + goto bad; + } + /* adjust transfer if necessary */ + if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) { + goto done; + } + } + + /* queue it */ + qp = &cd->head; + s = splbio(); + disksort(qp,bp); + splx(s); + + /* now check whether we can perform processing */ + mcd_start(unit); + return; + +bad: + bp->b_flags |= B_ERROR; +done: + bp->b_resid = bp->b_bcount; + biodone(bp); + return; +} + +static void mcd_start(int unit) +{ + struct mcd_data *cd = mcd_data + unit; + struct buf *bp, *qp = &cd->head; + struct partition *p; + int part; + register s = splbio(); + + if (cd->flags & MCDMBXBSY) + return; + + if ((bp = qp->b_actf) != 0) { + /* block found to process, dequeue */ + /*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/ + qp->b_actf = bp->av_forw; + splx(s); + } else { + /* nothing to do */ + splx(s); + return; + } + + /* changed media? */ + if (!(cd->flags & MCDVALID)) { + MCD_TRACE("mcd_start: drive not valid\n",0,0,0,0); + return; + } + + p = cd->dlabel.d_partitions + mcd_part(bp->b_dev); + + cd->flags |= MCDMBXBSY; + cd->mbx.unit = unit; + cd->mbx.port = cd->iobase; + cd->mbx.retry = MCD_RETRYS; + cd->mbx.bp = bp; + cd->mbx.p_offset = p->p_offset; + + /* calling the read routine */ + mcd_doread(MCD_S_BEGIN,&(cd->mbx)); + /* triggers mcd_start, when successful finished */ + return; +} + +int mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags) +{ + struct mcd_data *cd; + int unit,part; + + unit = mcd_unit(dev); + part = mcd_part(dev); + cd = mcd_data + unit; + +#ifdef MCDMINI + return ENOTTY; +#else + if (!(cd->flags & MCDVALID)) + return EIO; +MCD_TRACE("ioctl called 0x%x\n",cmd,0,0,0); + + switch (cmd) { + case DIOCSBAD: + return EINVAL; + case DIOCGDINFO: + case DIOCGPART: + case DIOCWDINFO: + case DIOCSDINFO: + case DIOCWLABEL: + return ENOTTY; + case CDIOCPLAYTRACKS: + return mcd_playtracks(unit, (struct ioc_play_track *) addr); + case CDIOCPLAYBLOCKS: + return mcd_play(unit, (struct mcd_read2 *) addr); + case CDIOCREADSUBCHANNEL: + return mcd_subchan(unit, (struct ioc_read_subchannel *) addr); + case CDIOREADTOCHEADER: + return mcd_toc_header(unit, (struct ioc_toc_header *) addr); + case CDIOREADTOCENTRYS: + return mcd_toc_entry(unit, (struct ioc_read_toc_entry *) addr); + case CDIOCSETPATCH: + case CDIOCGETVOL: + case CDIOCSETVOL: + case CDIOCSETMONO: + case CDIOCSETSTERIO: + case CDIOCSETMUTE: + case CDIOCSETLEFT: + case CDIOCSETRIGHT: + return EINVAL; + case CDIOCRESUME: + return mcd_resume(unit); + case CDIOCPAUSE: + return mcd_pause(unit); + case CDIOCSTART: + return EINVAL; + case CDIOCSTOP: + return mcd_stop(unit); + case CDIOCEJECT: + return EINVAL; + case CDIOCSETDEBUG: + cd->debug = 1; + return 0; + case CDIOCCLRDEBUG: + cd->debug = 0; + return 0; + case CDIOCRESET: + return EINVAL; + default: + return ENOTTY; + } + /*NOTREACHED*/ +#endif /*!MCDMINI*/ +} + +/* this could have been taken from scsi/cd.c, but it is not clear + * whether the scsi cd driver is linked in + */ +static int mcd_getdisklabel(int unit) +{ + struct mcd_data *cd = mcd_data + unit; + + if (cd->flags & MCDLABEL) + return -1; + + bzero(&cd->dlabel,sizeof(struct disklabel)); + strncpy(cd->dlabel.d_typename,"Mitsumi CD ROM ",16); + strncpy(cd->dlabel.d_packname,"unknown ",16); + cd->dlabel.d_secsize = cd->blksize; + cd->dlabel.d_nsectors = 100; + cd->dlabel.d_ntracks = 1; + cd->dlabel.d_ncylinders = (cd->disksize/100)+1; + cd->dlabel.d_secpercyl = 100; + cd->dlabel.d_secperunit = cd->disksize; + cd->dlabel.d_rpm = 300; + cd->dlabel.d_interleave = 1; + cd->dlabel.d_flags = D_REMOVABLE; + cd->dlabel.d_npartitions= 1; + cd->dlabel.d_partitions[0].p_offset = 0; + cd->dlabel.d_partitions[0].p_size = cd->disksize; + cd->dlabel.d_partitions[0].p_fstype = 9; + cd->dlabel.d_magic = DISKMAGIC; + cd->dlabel.d_magic2 = DISKMAGIC; + cd->dlabel.d_checksum = dkcksum(&cd->dlabel); + + cd->flags |= MCDLABEL; + return 0; +} + +int mcdsize(dev_t dev) +{ + int size; + int unit = mcd_unit(dev); + struct mcd_data *cd = mcd_data + unit; + + if (mcd_volinfo(unit) >= 0) { + cd->blksize = MCDBLK; + size = msf2hsg(cd->volinfo.vol_msf); + cd->disksize = size * (MCDBLK/DEV_BSIZE); + return 0; + } + return -1; +} + +/*************************************************************** + * lower level of driver starts here + **************************************************************/ + +#ifdef NOTDEF +static char irqs[] = { + 0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00, + 0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00 +}; + +static char drqs[] = { + 0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07, +}; +#endif + +static void mcd_configure(struct mcd_data *cd) +{ + outb(cd->iobase+mcd_config,cd->config); +} + +/* check if there is a cdrom */ +/* Heavly hacked by gclarkii@sugar.neosoft.com */ + +int mcd_probe(struct isa_device *dev) +{ + int port = dev->id_iobase; + int unit = dev->id_unit; + int i; + int st; + int check; + int junk; + + mcd_data[unit].flags = MCDPROBING; + +#ifdef NOTDEF + /* get irq/drq configuration word */ + mcd_data[unit].config = irqs[dev->id_irq]; /* | drqs[dev->id_drq];*/ +#else + mcd_data[unit].config = 0; +#endif + + /* send a reset */ + outb(port+MCD_FLAGS,0); + DELAY(100000); + /* get any pending status and throw away...*/ + for (i=10; i != 0; i--) { + inb(port+MCD_DATA); + } + DELAY(1000); + + outb(port+MCD_DATA,MCD_CMDGETSTAT); /* Send get status command */ + + /* Loop looking for avail of status */ + /* XXX May have to increase for fast machinces */ + for (i = 1000; i != 0; i--) { + if ((inb(port+MCD_FLAGS) & 0xF ) == STATUS_AVAIL) { + break; + } + DELAY(10); + } + /* get status */ + + if (i == 0) { +#ifdef DEBUG + printf ("Mitsumi drive NOT detected\n"); +#endif + return 0; + } + +/* + * The following code uses the 0xDC command, it returns a M from the + * second byte and a number in the third. Does anyone know what the + * number is for? Better yet, how about someone thats REAL good in + * i80x86 asm looking at the Dos driver... Most of this info came + * from a friend of mine spending a whole weekend..... + */ + + DELAY (2000); + outb(port+MCD_DATA,MCD_CMDCONTINFO); + for (i = 0; i < 100000; i++) { + if ((inb(port+MCD_FLAGS) & 0xF) == STATUS_AVAIL) + break; + } + if (i > 100000) { +#ifdef DEBUG + printf ("Mitsumi drive error\n"); +#endif + return 0; + } + DELAY (40000); + st = inb(port+MCD_DATA); + DELAY (500); + check = inb(port+MCD_DATA); + DELAY (500); + junk = inb(port+MCD_DATA); /* What is byte used for?!?!? */ + + if (check = 'M') { +#ifdef DEBUG + printf("Mitsumi drive detected\n"); +#endif + return 4; + } else { + printf("Mitsumi drive NOT detected\n"); + printf("Mitsumi drive error\n"); + return 0; + } +} + +static int mcd_waitrdy(int port,int dly) +{ + int i; + + /* wait until xfer port senses data ready */ + for (i=0; iiobase; + + /* wait data to become ready */ + if (mcd_waitrdy(port,dly)<0) { +#ifdef MCD_TO_WARNING_ON + printf("mcd%d: timeout getreply\n",unit); +#endif + return -1; + } + + /* get the data */ + return inb(port+mcd_status) & 0xFF; +} + +static int mcd_getstat(int unit,int sflg) +{ + int i; + struct mcd_data *cd = mcd_data + unit; + int port = cd->iobase; + + /* get the status */ + if (sflg) + outb(port+mcd_command, MCD_CMDGETSTAT); + i = mcd_getreply(unit,DELAY_GETREPLY); + if (i<0) return -1; + + cd->status = i; + + mcd_setflags(unit,cd); + return cd->status; +} + +static void mcd_setflags(int unit, struct mcd_data *cd) +{ + /* check flags */ + if (cd->status & (MCDDSKCHNG|MCDDOOROPEN)) { + MCD_TRACE("getstat: sensed DSKCHNG or DOOROPEN\n",0,0,0,0); + cd->flags &= ~MCDVALID; + } + +#ifndef MCDMINI + if (cd->status & MCDAUDIOBSY) + cd->audio_status = CD_AS_PLAY_IN_PROGRESS; + else if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS) + cd->audio_status = CD_AS_PLAY_COMPLETED; +#endif +} + +static int mcd_get(int unit, char *buf, int nmax) +{ + int port = mcd_data[unit].iobase; + int i,k; + + for (i=0; i> 4) * 10 + (b & 15); +} + +static bcd_t bin2bcd(int b) +{ + return ((b / 10) << 4) | (b % 10); +} + +static void hsg2msf(int hsg, bcd_t *msf) +{ + hsg += 150; + M_msf(msf) = bin2bcd(hsg / 4500); + hsg %= 4500; + S_msf(msf) = bin2bcd(hsg / 75); + F_msf(msf) = bin2bcd(hsg % 75); +} + +static int msf2hsg(bcd_t *msf) +{ + return (bcd2bin(M_msf(msf)) * 60 + + bcd2bin(S_msf(msf))) * 75 + + bcd2bin(F_msf(msf)) - 150; +} + +static int mcd_volinfo(int unit) +{ + struct mcd_data *cd = mcd_data + unit; + int i; + +/*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/ + + /* Get the status, in case the disc has been changed */ + if (mcd_getstat(unit, 1) < 0) return EIO; + + /* Just return if we already have it */ + if (cd->flags & MCDVOLINFO) return 0; + + /* send volume info command */ + if (mcd_send(unit,MCD_CMDGETVOLINFO,MCD_RETRYS) < 0) + return -1; + + /* get data */ + if (mcd_get(unit,(char*) &cd->volinfo,sizeof(struct mcd_volinfo)) < 0) { + printf("mcd%d: mcd_volinfo: error read data\n",unit); + return -1; + } + + if (cd->volinfo.trk_low != 0 || cd->volinfo.trk_high != 0) { + cd->flags |= MCDVOLINFO; /* volinfo is OK */ + return 0; + } + + return -1; +} + +int mcdintr(unit) +{ + int port = mcd_data[unit].iobase; + u_int i; + + MCD_TRACE("stray interrupt xfer=0x%x\n",inb(port+mcd_xfer),0,0,0); + + /* just read out status and ignore the rest */ + if ((inb(port+mcd_xfer)&0xFF) != 0xFF) { + i = inb(port+mcd_status); + } +} + +/* state machine to process read requests + * initialize with MCD_S_BEGIN: calculate sizes, and read status + * MCD_S_WAITSTAT: wait for status reply, set mode + * MCD_S_WAITMODE: waits for status reply from set mode, set read command + * MCD_S_WAITREAD: wait for read ready, read data + */ +static struct mcd_mbx *mbxsave; + +static void mcd_doread(int state, struct mcd_mbx *mbxin) +{ + struct mcd_mbx *mbx = (state!=MCD_S_BEGIN) ? mbxsave : mbxin; + int unit = mbx->unit; + int port = mbx->port; + struct buf *bp = mbx->bp; + struct mcd_data *cd = mcd_data + unit; + + int rm,i,k; + struct mcd_read2 rbuf; + int blknum; + caddr_t addr; + +loop: + switch (state) { + case MCD_S_BEGIN: + mbx = mbxsave = mbxin; + + case MCD_S_BEGIN1: + /* get status */ + outb(port+mcd_command, MCD_CMDGETSTAT); + mbx->count = RDELAY_WAITSTAT; + timeout(mcd_doread,MCD_S_WAITSTAT,hz/100); + return; + case MCD_S_WAITSTAT: + untimeout(mcd_doread,MCD_S_WAITSTAT); + if (mbx->count-- >= 0) { + if (inb(port+mcd_xfer) & MCD_ST_BUSY) { + timeout(mcd_doread,MCD_S_WAITSTAT,hz/100); + return; + } + mcd_setflags(unit,cd); + MCD_TRACE("got WAITSTAT delay=%d\n",RDELAY_WAITSTAT-mbx->count,0,0,0); + /* reject, if audio active */ + if (cd->status & MCDAUDIOBSY) { + printf("mcd%d: audio is active\n",unit); + goto readerr; + } + + /* to check for raw/cooked mode */ + if (cd->flags & MCDREADRAW) { + rm = MCD_MD_RAW; + mbx->sz = MCDRBLK; + } else { + rm = MCD_MD_COOKED; + mbx->sz = cd->blksize; + } + + mbx->count = RDELAY_WAITMODE; + + mcd_put(port+mcd_command, MCD_CMDSETMODE); + mcd_put(port+mcd_command, rm); + timeout(mcd_doread,MCD_S_WAITMODE,hz/100); + return; + } else { +#ifdef MCD_TO_WARNING_ON + printf("mcd%d: timeout getstatus\n",unit); +#endif + goto readerr; + } + + case MCD_S_WAITMODE: + untimeout(mcd_doread,MCD_S_WAITMODE); + if (mbx->count-- < 0) { +#ifdef MCD_TO_WARNING_ON + printf("mcd%d: timeout set mode\n",unit); +#endif + goto readerr; + } + if (inb(port+mcd_xfer) & MCD_ST_BUSY) { + timeout(mcd_doread,MCD_S_WAITMODE,hz/100); + return; + } + mcd_setflags(unit,cd); + MCD_TRACE("got WAITMODE delay=%d\n",RDELAY_WAITMODE-mbx->count,0,0,0); + /* for first block */ + mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz; + mbx->skip = 0; + +nextblock: + blknum = (bp->b_blkno / (mbx->sz/DEV_BSIZE)) + + mbx->p_offset + mbx->skip/mbx->sz; + + MCD_TRACE("mcd_doread: read blknum=%d for bp=0x%x\n",blknum,bp,0,0); + + /* build parameter block */ + hsg2msf(blknum,rbuf.start_msf); + + /* send the read command */ + mcd_put(port+mcd_command,MCD_CMDREAD2); + mcd_put(port+mcd_command,rbuf.start_msf[0]); + mcd_put(port+mcd_command,rbuf.start_msf[1]); + mcd_put(port+mcd_command,rbuf.start_msf[2]); + mcd_put(port+mcd_command,0); + mcd_put(port+mcd_command,0); + mcd_put(port+mcd_command,1); + mbx->count = RDELAY_WAITREAD; + timeout(mcd_doread,MCD_S_WAITREAD,hz/100); + return; + case MCD_S_WAITREAD: + untimeout(mcd_doread,MCD_S_WAITREAD); + if (mbx->count-- > 0) { + k = inb(port+mcd_xfer); + if ((k & 2)==0) { + MCD_TRACE("got data delay=%d\n",RDELAY_WAITREAD-mbx->count,0,0,0); + /* data is ready */ + addr = bp->b_un.b_addr + mbx->skip; + outb(port+mcd_ctl2,0x04); /* XXX */ + for (i=0; isz; i++) + *addr++ = inb(port+mcd_rdata); + outb(port+mcd_ctl2,0x0c); /* XXX */ + + if (--mbx->nblk > 0) { + mbx->skip += mbx->sz; + goto nextblock; + } + + /* return buffer */ + bp->b_resid = 0; + biodone(bp); + + cd->flags &= ~MCDMBXBSY; + mcd_start(mbx->unit); + return; + } + if ((k & 4)==0) + mcd_getstat(unit,0); + timeout(mcd_doread,MCD_S_WAITREAD,hz/100); + return; + } else { +#ifdef MCD_TO_WARNING_ON + printf("mcd%d: timeout read data\n",unit); +#endif + goto readerr; + } + } + +readerr: + if (mbx->retry-- > 0) { +#ifdef MCD_TO_WARNING_ON + printf("mcd%d: retrying\n",unit); +#endif + state = MCD_S_BEGIN1; + goto loop; + } + + /* invalidate the buffer */ + bp->b_flags |= B_ERROR; + bp->b_resid = bp->b_bcount; + biodone(bp); + mcd_start(mbx->unit); + return; + +#ifdef NOTDEF + printf("mcd%d: unit timeout, resetting\n",mbx->unit); + outb(mbx->port+mcd_reset,MCD_CMDRESET); + DELAY(300000); + (void)mcd_getstat(mbx->unit,1); + (void)mcd_getstat(mbx->unit,1); + /*cd->status &= ~MCDDSKCHNG; */ + cd->debug = 1; /* preventive set debug mode */ + +#endif + +} + +#ifndef MCDMINI +static int mcd_setmode(int unit, int mode) +{ + struct mcd_data *cd = mcd_data + unit; + int port = cd->iobase; + int retry; + + printf("mcd%d: setting mode to %d\n", unit, mode); + for(retry=0; retrylen = msf2hsg(cd->volinfo.vol_msf); + th->starting_track = bcd2bin(cd->volinfo.trk_low); + th->ending_track = bcd2bin(cd->volinfo.trk_high); + + return 0; +} + +static int mcd_read_toc(int unit) +{ + struct mcd_data *cd = mcd_data + unit; + struct ioc_toc_header th; + struct mcd_qchninfo q; + int rc, trk, idx, retry; + + /* Only read TOC if needed */ + if (cd->flags & MCDTOC) return 0; + + printf("mcd%d: reading toc header\n", unit); + if (mcd_toc_header(unit, &th) != 0) + return ENXIO; + + printf("mcd%d: stopping play\n", unit); + if ((rc=mcd_stop(unit)) != 0) + return rc; + + /* try setting the mode twice */ + if (mcd_setmode(unit, MCD_MD_TOC) != 0) + return EIO; + if (mcd_setmode(unit, MCD_MD_TOC) != 0) + return EIO; + + printf("mcd%d: get_toc reading qchannel info\n",unit); + for(trk=th.starting_track; trk<=th.ending_track; trk++) + cd->toc[trk].idx_no = 0; + trk = th.ending_track - th.starting_track + 1; + for(retry=0; retry<300 && trk>0; retry++) + { + if (mcd_getqchan(unit, &q) < 0) break; + idx = bcd2bin(q.idx_no); + if (idx>0 && idx < MCD_MAXTOCS && q.trk_no==0) + if (cd->toc[idx].idx_no == 0) + { + cd->toc[idx] = q; + trk--; + } + } + + if (mcd_setmode(unit, MCD_MD_COOKED) != 0) + return EIO; + + if (trk != 0) return ENXIO; + + /* add a fake last+1 */ + idx = th.ending_track + 1; + cd->toc[idx].ctrl_adr = cd->toc[idx-1].ctrl_adr; + cd->toc[idx].trk_no = 0; + cd->toc[idx].idx_no = 0xAA; + cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0]; + cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1]; + cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2]; + + cd->flags |= MCDTOC; + + return 0; +} + +static int mcd_toc_entry(int unit, struct ioc_read_toc_entry *te) +{ + struct mcd_data *cd = mcd_data + unit; + struct ret_toc + { + struct ioc_toc_header th; + struct cd_toc_entry rt; + } ret_toc; + struct ioc_toc_header th; + int rc, i; + + /* Make sure we have a valid toc */ + if ((rc=mcd_read_toc(unit)) != 0) + return rc; + + /* find the toc to copy*/ + i = te->starting_track; + if (i == MCD_LASTPLUS1) + i = bcd2bin(cd->volinfo.trk_high) + 1; + + /* verify starting track */ + if (i < bcd2bin(cd->volinfo.trk_low) || + i > bcd2bin(cd->volinfo.trk_high)+1) + return EINVAL; + + /* do we have room */ + if (te->data_len < sizeof(struct ioc_toc_header) + + sizeof(struct cd_toc_entry)) return EINVAL; + + /* Copy the toc header */ + if (mcd_toc_header(unit, &th) < 0) return EIO; + ret_toc.th = th; + + /* copy the toc data */ + ret_toc.rt.control = cd->toc[i].ctrl_adr; + ret_toc.rt.addr_type = te->address_format; + ret_toc.rt.track = i; + if (te->address_format == CD_MSF_FORMAT) + { + ret_toc.rt.addr[1] = cd->toc[i].hd_pos_msf[0]; + ret_toc.rt.addr[2] = cd->toc[i].hd_pos_msf[1]; + ret_toc.rt.addr[3] = cd->toc[i].hd_pos_msf[2]; + } + + /* copy the data back */ + copyout(&ret_toc, te->data, sizeof(struct cd_toc_entry) + + sizeof(struct ioc_toc_header)); + + return 0; +} + +static int mcd_stop(int unit) +{ + struct mcd_data *cd = mcd_data + unit; + + if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0) + return ENXIO; + cd->audio_status = CD_AS_PLAY_COMPLETED; + return 0; +} + +static int mcd_getqchan(int unit, struct mcd_qchninfo *q) +{ + struct mcd_data *cd = mcd_data + unit; + + if (mcd_send(unit, MCD_CMDGETQCHN, MCD_RETRYS) < 0) + return -1; + if (mcd_get(unit, (char *) q, sizeof(struct mcd_qchninfo)) < 0) + return -1; + if (cd->debug) + printf("mcd%d: qchannel ctl=%d, t=%d, i=%d, ttm=%d:%d.%d dtm=%d:%d.%d\n", + unit, + q->ctrl_adr, q->trk_no, q->idx_no, + q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2], + q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2]); + return 0; +} + +static int mcd_subchan(int unit, struct ioc_read_subchannel *sc) +{ + struct mcd_data *cd = mcd_data + unit; + struct mcd_qchninfo q; + struct cd_sub_channel_info data; + + printf("mcd%d: subchan af=%d, df=%d\n", unit, + sc->address_format, + sc->data_format); + if (sc->address_format != CD_MSF_FORMAT) return EIO; + if (sc->data_format != CD_CURRENT_POSITION) return EIO; + + if (mcd_getqchan(unit, &q) < 0) return EIO; + + data.header.audio_status = cd->audio_status; + data.what.position.data_format = CD_MSF_FORMAT; + data.what.position.track_number = bcd2bin(q.trk_no); + + if (copyout(&data, sc->data, sizeof(struct cd_sub_channel_info))!=0) + return EFAULT; + return 0; +} + +static int mcd_playtracks(int unit, struct ioc_play_track *pt) +{ + struct mcd_data *cd = mcd_data + unit; + struct mcd_read2 pb; + int a = pt->start_track; + int z = pt->end_track; + int rc; + + if ((rc = mcd_read_toc(unit)) != 0) return rc; + + printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit, + a, pt->start_index, z, pt->end_index); + + if (a < cd->volinfo.trk_low || a > cd->volinfo.trk_high || a > z || + z < cd->volinfo.trk_low || z > cd->volinfo.trk_high) + return EINVAL; + + pb.start_msf[0] = cd->toc[a].hd_pos_msf[0]; + pb.start_msf[1] = cd->toc[a].hd_pos_msf[1]; + pb.start_msf[2] = cd->toc[a].hd_pos_msf[2]; + pb.end_msf[0] = cd->toc[z+1].hd_pos_msf[0]; + pb.end_msf[1] = cd->toc[z+1].hd_pos_msf[1]; + pb.end_msf[2] = cd->toc[z+1].hd_pos_msf[2]; + + return mcd_play(unit, &pb); +} + +static int mcd_play(int unit, struct mcd_read2 *pb) +{ + struct mcd_data *cd = mcd_data + unit; + int port = cd->iobase; + int retry, st; + + cd->lastpb = *pb; + for(retry=0; retrystart_msf[0]); + outb(port+mcd_command, pb->start_msf[1]); + outb(port+mcd_command, pb->start_msf[2]); + outb(port+mcd_command, pb->end_msf[0]); + outb(port+mcd_command, pb->end_msf[1]); + outb(port+mcd_command, pb->end_msf[2]); + if ((st=mcd_getstat(unit, 0)) != -1) break; + } + + if (cd->debug) + printf("mcd%d: mcd_play retry=%d, status=%d\n", unit, retry, st); + if (st == -1) return ENXIO; + cd->audio_status = CD_AS_PLAY_IN_PROGRESS; + return 0; +} + +static int mcd_pause(int unit) +{ + struct mcd_data *cd = mcd_data + unit; + struct mcd_qchninfo q; + int rc; + + /* Verify current status */ + if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) + { + printf("mcd%d: pause attempted when not playing\n", unit); + return EINVAL; + } + + /* Get the current position */ + if (mcd_getqchan(unit, &q) < 0) return EIO; + + /* Copy it into lastpb */ + cd->lastpb.start_msf[0] = q.hd_pos_msf[0]; + cd->lastpb.start_msf[1] = q.hd_pos_msf[1]; + cd->lastpb.start_msf[2] = q.hd_pos_msf[2]; + + /* Stop playing */ + if ((rc=mcd_stop(unit)) != 0) return rc; + + /* Set the proper status and exit */ + cd->audio_status = CD_AS_PLAY_PAUSED; + return 0; +} + +static int mcd_resume(int unit) +{ + struct mcd_data *cd = mcd_data + unit; + + if (cd->audio_status != CD_AS_PLAY_PAUSED) return EINVAL; + return mcd_play(unit, &cd->lastpb); +} +#endif /*!MCDMINI*/ + +#endif /* NMCD > 0 */ diff --git a/sys/i386/isa/mcdreg.h b/sys/i386/isa/mcdreg.h new file mode 100644 index 0000000000..83664a7674 --- /dev/null +++ b/sys/i386/isa/mcdreg.h @@ -0,0 +1,150 @@ +/* + * Copyright 1993 by Holger Veit (data part) + * Copyright 1993 by Brian Moore (audio part) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This software was developed by Holger Veit and Brian Moore + * for use with "386BSD" and similar operating systems. + * "Similar operating systems" includes mainly non-profit oriented + * systems for research and education, including but not restricted to + * "NetBSD", "FreeBSD", "Mach" (by CMU). + * 4. Neither the name of the developer(s) nor the name "386BSD" + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER(S) BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file contains definitions for some cdrom control commands + * and status codes. This info was "inherited" from the DOS MTMCDE.SYS + * driver, and is thus not complete (and may even be wrong). Some day + * the manufacturer or anyone else might provide better documentation, + * so this file (and the driver) will then have a better quality. + * + * $Id$ + */ + +#ifndef MCD_H +#define MCD_H + +#ifdef __GNUC__ +#if __GNUC__ >= 2 +#pragma pack(1) +#endif +#endif + +typedef unsigned char bcd_t; +#define M_msf(msf) msf[0] +#define S_msf(msf) msf[1] +#define F_msf(msf) msf[2] + +/* io lines used */ +#define MCD_IO_BASE 0x300 + +#define mcd_command 0 +#define mcd_status 0 +#define mcd_rdata 0 + +#define mcd_reset 1 +#define mcd_xfer 1 +#define mcd_ctl2 2 /* XXX Is this right? */ + +#define mcd_config 3 +#define MCD_MASK_DMA 0x07 /* bits 2-0 = DMA channel */ +#define MCD_MASK_IRQ 0x70 /* bits 6-4 = INT number */ + /* 001 = int 2,9 */ + /* 010 = int 3 */ + /* 011 = int 5 */ + /* 100 = int 10 */ + /* 101 = int 11 */ +/* flags */ +#define STATUS_AVAIL 0xb +#define DATA_AVAIL 0xf + +/* ports */ +#define MCD_DATA 0 +#define MCD_FLAGS 1 +#define MCD_DONT_KNOW 2 /* What are these two ports for??? */ +#define CHANNEL 3 + +/* Status bits */ +#define MCD_ST_DOOROPEN 0x80 +#define MCD_ST_DSKIN 0x40 +#define MCD_ST_DSKCHNG 0x20 +#define MCD_ST_BUSY 0x04 +#define MCD_ST_AUDIOBSY 0x02 + +/* commands known by the controller */ +#define MCD_CMDRESET 0x00 +#define MCD_CMDGETVOLINFO 0x10 /* gets mcd_volinfo */ +#define MCD_CMDGETQCHN 0x20 /* gets mcd_qchninfo */ +#define MCD_CMDGETSTAT 0x40 /* gets a byte of status */ +#define MCD_CMDSETMODE 0x50 /* set transmission mode, needs byte */ +#define MCD_MD_RAW 0x60 +#define MCD_MD_COOKED 0x01 +#define MCD_MD_TOC 0x05 +#define MCD_CMDSTOPAUDIO 0x70 +#define MCD_CMDGETVOLUME 0x8E /* gets mcd_volume */ +#define MCD_CMDSETVOLUME 0xAE /* sets mcd_volume */ +#define MCD_CMDREAD1 0xB0 /* read n sectors */ +#define MCD_CMDREAD2 0xC0 /* read from-to */ +#define MCD_CMDCONTINFO 0xDC /* Get controller info */ +#define MCD_CMDEJECTDISK 0xF6 +#define MCD_CMDCLOSETRAY 0xF8 +#define MCD_CMDLOCKDRV 0xFE /* needs byte */ +#define MCD_LK_UNLOCK 0x00 +#define MCD_LK_LOCK 0x01 +#define MCD_LK_TEST 0x02 + +struct mcd_volinfo { + bcd_t trk_low; + bcd_t trk_high; + bcd_t vol_msf[3]; + bcd_t trk1_msf[3]; +}; + +struct mcd_qchninfo { + u_char ctrl_adr; + u_char trk_no; + u_char idx_no; + bcd_t trk_size_msf[3]; + u_char :8; + bcd_t hd_pos_msf[3]; +}; + +struct mcd_volume { + u_char v0l; + u_char v0rs; + u_char v0r; + u_char v0ls; +}; + +struct mcd_read1 { + bcd_t start_msf[3]; + u_char nsec[3]; +}; + +struct mcd_read2 { + bcd_t start_msf[3]; + bcd_t end_msf[3]; +}; +#endif /* MCD_H */ diff --git a/sys/i386/isa/mse.c b/sys/i386/isa/mse.c new file mode 100644 index 0000000000..60621ccba8 --- /dev/null +++ b/sys/i386/isa/mse.c @@ -0,0 +1,493 @@ +/* + * Copyright 1992 by the University of Guelph + * + * Permission to use, copy and modify this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation. + * University of Guelph makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ +/* + * Driver for the Logitech and ATI Inport Bus mice for use with 386bsd and + * the X386 port, courtesy of + * Rick Macklem, rick@snowhite.cis.uoguelph.ca + * Caveats: The driver currently uses spltty(), but doesn't use any + * generic tty code. It could use splmse() (that only masks off the + * bus mouse interrupt, but that would require hacking in i386/isa/icu.s. + * (This may be worth the effort, since the Logitech generates 30/60 + * interrupts/sec continuously while it is open.) + * NB: The ATI has NOT been tested yet! + */ + +/* + * Modification history: + * + * Oct 19, 1992 -- E. Stark (stark@cs.sunysb.edu) + * fixes to make it work with Microsoft InPort busmouse + * + * Jan, 1993 -- E. Stark (stark@cs.sunysb.edu) + * added patches for new "select" interface + * + * May 4, 1993 -- E. Stark (stark@cs.sunysb.edu) + * changed position of some spl()'s in mseread + * + * October 8, 1993 -- E. Stark (stark@cs.sunysb.edu) + * limit maximum negative x/y value to -127 to work around XFree problem + * that causes spurious button pushes. + */ + +#include "mse.h" +#if NMSE > 0 +#include "param.h" +#include "proc.h" +#include "user.h" +#include "buf.h" +#include "systm.h" +#include "kernel.h" +#include "ioctl.h" +#include "tty.h" +#include "uio.h" + +#include "i386/isa/isa_device.h" +#include "i386/isa/icu.h" + +int mseprobe(), mseattach(), mseintr(); + +struct isa_driver msedriver = { + mseprobe, mseattach, "mse" +}; + +/* + * Software control structure for mouse. The sc_enablemouse(), + * sc_disablemouse() and sc_getmouse() routines must be called spl'd(). + */ +#define PROTOBYTES 5 +struct mse_softc { + int sc_flags; + int sc_mousetype; + pid_t sc_selp; + u_int sc_port; + void (*sc_enablemouse)(); + void (*sc_disablemouse)(); + void (*sc_getmouse)(); + int sc_deltax; + int sc_deltay; + int sc_obuttons; + int sc_buttons; + int sc_bytesread; + u_char sc_bytes[PROTOBYTES]; +} mse_sc[NMSE]; + +/* Flags */ +#define MSESC_OPEN 0x1 +#define MSESC_WANT 0x2 + +/* and Mouse Types */ +#define MSE_LOGITECH 0x1 +#define MSE_ATIINPORT 0x2 + +#define MSE_PORTA 0 +#define MSE_PORTB 1 +#define MSE_PORTC 2 +#define MSE_PORTD 3 + +#define MSE_UNIT(dev) (minor(dev) >> 1) +#define MSE_NBLOCKIO(dev) (minor(dev) & 0x1) + +/* + * Logitech bus mouse definitions + */ +#define MSE_SETUP 0x91 /* What does this mean? */ +#define MSE_HOLD 0x80 +#define MSE_RXLOW 0x00 +#define MSE_RXHIGH 0x20 +#define MSE_RYLOW 0x40 +#define MSE_RYHIGH 0x60 +#define MSE_DISINTR 0x10 +#define MSE_INTREN 0x00 + +static int mse_probelogi(); +static void mse_enablelogi(), mse_disablelogi(), mse_getlogi(); + +/* + * ATI Inport mouse definitions + */ +#define MSE_INPORT_RESET 0x80 +#define MSE_INPORT_STATUS 0x00 +#define MSE_INPORT_DX 0x01 +#define MSE_INPORT_DY 0x02 +#define MSE_INPORT_MODE 0x07 +#define MSE_INPORT_HOLD 0x20 +#define MSE_INPORT_INTREN 0x09 + +static int mse_probeati(); +static void mse_enableati(), mse_disableati(), mse_getati(); + +#define MSEPRI (PZERO + 3) + +/* + * Table of mouse types. + * Keep the Logitech last, since I haven't figured out how to probe it + * properly yet. (Someday I'll have the documentation.) + */ +struct mse_types { + int m_type; /* Type of bus mouse */ + int (*m_probe)(); /* Probe routine to test for it */ + void (*m_enable)(); /* Start routine */ + void (*m_disable)(); /* Disable interrupts routine */ + void (*m_get)(); /* and get mouse status */ +} mse_types[] = { + { MSE_ATIINPORT, mse_probeati, mse_enableati, mse_disableati, mse_getati }, + { MSE_LOGITECH, mse_probelogi, mse_enablelogi, mse_disablelogi, mse_getlogi }, + { 0, }, +}; + +mseprobe(idp) + register struct isa_device *idp; +{ + register struct mse_softc *sc = &mse_sc[idp->id_unit]; + register int i; + + /* + * Check for each mouse type in the table. + */ + i = 0; + while (mse_types[i].m_type) { + if ((*mse_types[i].m_probe)(idp)) { + sc->sc_mousetype = mse_types[i].m_type; + sc->sc_enablemouse = mse_types[i].m_enable; + sc->sc_disablemouse = mse_types[i].m_disable; + sc->sc_getmouse = mse_types[i].m_get; + return (1); + } + i++; + } + return (0); +} + +mseattach(idp) + struct isa_device *idp; +{ + struct mse_softc *sc = &mse_sc[idp->id_unit]; + + sc->sc_port = idp->id_iobase; + return (1); +} + +/* + * Exclusive open the mouse, initialize it and enable interrupts. + */ +mseopen(dev, flag) + dev_t dev; + int flag; +{ + register struct mse_softc *sc; + int s; + + if (MSE_UNIT(dev) >= NMSE) + return (ENXIO); + sc = &mse_sc[MSE_UNIT(dev)]; + if (sc->sc_flags & MSESC_OPEN) + return (EBUSY); + sc->sc_flags |= MSESC_OPEN; + sc->sc_obuttons = sc->sc_buttons = 0x7; + sc->sc_deltax = sc->sc_deltay = 0; + sc->sc_bytesread = PROTOBYTES; + + /* + * Initialize mouse interface and enable interrupts. + */ + s = spltty(); + (*sc->sc_enablemouse)(sc->sc_port); + splx(s); + return (0); +} + +/* + * mseclose: just turn off mouse innterrupts. + */ +mseclose(dev, flag) + int flag; +{ + struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)]; + int s; + + s = spltty(); + (*sc->sc_disablemouse)(sc->sc_port); + sc->sc_flags &= ~MSESC_OPEN; + splx(s); + return(0); +} + +/* + * mseread: return mouse info using the MSC serial protocol, but without + * using bytes 4 and 5. + * (Yes this is cheesy, but it makes the X386 server happy, so...) + */ +mseread(dev, uio) + dev_t dev; + struct uio *uio; +{ + register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)]; + int xfer, s, error; + + /* + * If there are no protocol bytes to be read, set up a new protocol + * packet. + */ + s = spltty(); /* XXX Should be its own spl, but where is imlXX() */ + if (sc->sc_bytesread >= PROTOBYTES) { + while (sc->sc_deltax == 0 && sc->sc_deltay == 0 && + (sc->sc_obuttons ^ sc->sc_buttons) == 0) { + if (MSE_NBLOCKIO(dev)) { + splx(s); + return (0); + } + sc->sc_flags |= MSESC_WANT; + if (error = tsleep((caddr_t)sc, MSEPRI | PCATCH, + "mseread", 0)) { + splx(s); + return (error); + } + } + + /* + * Generate protocol bytes. + * For some reason X386 expects 5 bytes but never uses + * the fourth or fifth? + */ + sc->sc_bytes[0] = 0x80 | (sc->sc_buttons & ~0xf8); + if (sc->sc_deltax > 127) + sc->sc_deltax = 127; + if (sc->sc_deltax < -127) + sc->sc_deltax = -127; + sc->sc_deltay = -sc->sc_deltay; /* Otherwise mousey goes wrong way */ + if (sc->sc_deltay > 127) + sc->sc_deltay = 127; + if (sc->sc_deltay < -127) + sc->sc_deltay = -127; + sc->sc_bytes[1] = sc->sc_deltax; + sc->sc_bytes[2] = sc->sc_deltay; + sc->sc_bytes[3] = sc->sc_bytes[4] = 0; + sc->sc_obuttons = sc->sc_buttons; + sc->sc_deltax = sc->sc_deltay = 0; + sc->sc_bytesread = 0; + } + splx(s); + xfer = MIN(uio->uio_resid, PROTOBYTES - sc->sc_bytesread); + if (error = uiomove(&sc->sc_bytes[sc->sc_bytesread], xfer, uio)) + return (error); + sc->sc_bytesread += xfer; + return(0); +} + +/* + * mseselect: check for mouse input to be processed. + */ +mseselect(dev, rw, p) + dev_t dev; + int rw; + struct proc *p; +{ + register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)]; + int s; + + s = spltty(); + if (sc->sc_bytesread != PROTOBYTES || sc->sc_deltax != 0 || + sc->sc_deltay != 0 || (sc->sc_obuttons ^ sc->sc_buttons) != 0) { + splx(s); + return (1); + } + + /* + * Since this is an exclusive open device, any previous proc. + * pointer is trash now, so we can just assign it. + */ + sc->sc_selp = p->p_pid; + splx(s); + return (0); +} + +/* + * mseintr: update mouse status. sc_deltax and sc_deltay are accumulative. + */ +mseintr(unit) + int unit; +{ + register struct mse_softc *sc = &mse_sc[unit]; + pid_t p; + +#ifdef DEBUG + static int mse_intrcnt = 0; + if((mse_intrcnt++ % 10000) == 0) + printf("mseintr\n"); +#endif /* DEBUG */ + if ((sc->sc_flags & MSESC_OPEN) == 0) + return; + + (*sc->sc_getmouse)(sc->sc_port, &sc->sc_deltax, &sc->sc_deltay, &sc->sc_buttons); + + /* + * If mouse state has changed, wake up anyone wanting to know. + */ + if (sc->sc_deltax != 0 || sc->sc_deltay != 0 || + (sc->sc_obuttons ^ sc->sc_buttons) != 0) { + if (sc->sc_flags & MSESC_WANT) { + sc->sc_flags &= ~MSESC_WANT; + wakeup((caddr_t)sc); + } + if (sc->sc_selp) { + p = sc->sc_selp; + sc->sc_selp = (pid_t)0; + selwakeup(p, 0); + } + } +} + +/* + * Routines for the Logitech mouse. + */ +/* + * Test for a Logitech bus mouse and return 1 if it is. + * (until I know how to use the signature port properly, just disable + * interrupts and return 1) + */ +static int +mse_probelogi(idp) + register struct isa_device *idp; +{ + + outb(idp->id_iobase + MSE_PORTB, 0x55); + if (inb(idp->id_iobase + MSE_PORTB) == 0x55) { + outb(idp->id_iobase + MSE_PORTB, 0xaa); + if (inb(idp->id_iobase + MSE_PORTB) == 0xaa) + return (1); + } + return (0); +} + +/* + * Initialize Logitech mouse and enable interrupts. + */ +static void +mse_enablelogi(port) + register u_int port; +{ + int dx, dy, but; + + outb(port + MSE_PORTD, MSE_SETUP); + mse_getlogi(port, &dx, &dy, &but); +} + +/* + * Disable interrupts for Logitech mouse. + */ +static void +mse_disablelogi(port) + register u_int port; +{ + + outb(port + MSE_PORTC, MSE_DISINTR); +} + +/* + * Get the current dx, dy and button up/down state. + */ +static void +mse_getlogi(port, dx, dy, but) + register u_int port; + int *dx; + int *dy; + int *but; +{ + register char x, y; + + outb(port + MSE_PORTC, MSE_HOLD | MSE_RXLOW); + x = inb(port + MSE_PORTA); + *but = (x >> 5) & 0x7; + x &= 0xf; + outb(port + MSE_PORTC, MSE_HOLD | MSE_RXHIGH); + x |= (inb(port + MSE_PORTA) << 4); + outb(port + MSE_PORTC, MSE_HOLD | MSE_RYLOW); + y = (inb(port + MSE_PORTA) & 0xf); + outb(port + MSE_PORTC, MSE_HOLD | MSE_RYHIGH); + y |= (inb(port + MSE_PORTA) << 4); + *dx += x; + *dy += y; + outb(port + MSE_PORTC, MSE_INTREN); +} + +/* + * Routines for the ATI Inport bus mouse. + */ +/* + * Test for a ATI Inport bus mouse and return 1 if it is. + * (do not enable interrupts) + */ +static int +mse_probeati(idp) + register struct isa_device *idp; +{ + int i; + + for (i = 0; i < 2; i++) + if (inb(idp->id_iobase + MSE_PORTC) == 0xde) + return (1); + return (0); +} + +/* + * Initialize ATI Inport mouse and enable interrupts. + */ +static void +mse_enableati(port) + register u_int port; +{ + + outb(port + MSE_PORTA, MSE_INPORT_RESET); + outb(port + MSE_PORTA, MSE_INPORT_MODE); + outb(port + MSE_PORTB, MSE_INPORT_INTREN); +} + +/* + * Disable interrupts for ATI Inport mouse. + */ +static void +mse_disableati(port) + register u_int port; +{ + + outb(port + MSE_PORTA, MSE_INPORT_MODE); + outb(port + MSE_PORTB, 0); +} + +/* + * Get current dx, dy and up/down button state. + */ +static void +mse_getati(port, dx, dy, but) + register u_int port; + int *dx; + int *dy; + int *but; +{ + register char byte; + + outb(port + MSE_PORTA, MSE_INPORT_MODE); + outb(port + MSE_PORTB, MSE_INPORT_HOLD); + outb(port + MSE_PORTA, MSE_INPORT_STATUS); + *but = ~(inb(port + MSE_PORTB) & 0x7); + outb(port + MSE_PORTA, MSE_INPORT_DX); + byte = inb(port + MSE_PORTB); + *dx += byte; + outb(port + MSE_PORTA, MSE_INPORT_DY); + byte = inb(port + MSE_PORTB); + *dy += byte; + outb(port + MSE_PORTA, MSE_INPORT_MODE); + outb(port + MSE_PORTB, MSE_INPORT_INTREN); +} +#endif /* NMSE */ diff --git a/sys/i386/isa/npx.c b/sys/i386/isa/npx.c index 73392fabfc..08256a6614 100644 --- a/sys/i386/isa/npx.c +++ b/sys/i386/isa/npx.c @@ -31,21 +31,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)npx.c 7.2 (Berkeley) 5/12/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00154 - * -------------------- ----- ---------------------- - * - * 20 Apr 93 Bruce Evans New npx-0.5 code - * 23 May 93 Rodney W. Grimes Return a special value of -1 from - * the probe code to keep isa_config from - * printing out the I/O address when we - * are using trap 16 handling. - * + * from: @(#)npx.c 7.2 (Berkeley) 5/12/91 + * $Id$ */ -static char rcsid[] = "$Header: /usr/bill/working/sys/i386/isa/RCS/npx.c,v 1.2 92/01/21 14:34:27 william Exp $"; #include "npx.h" #if NNPX > 0 @@ -318,7 +306,11 @@ npxprobe1(dvp) * that aren't really devices better. */ dvp->id_irq = 0; - return (IO_NPXSIZE); + /* + * special return value to flag that we do not + * actually use any I/O registers + */ + return (-1); } /* @@ -328,14 +320,12 @@ int npxattach(dvp) struct isa_device *dvp; { - if (npx_ex16) - printf(" "); - else if (npx_irq13) - printf(" "); - else if (npx_exists) - printf(" "); - else - printf(" <387 Emulator>"); + if (!npx_ex16 && !npx_irq13) { + if (npx_exists) + printf("npx%d: Error reporting broken, using 387 emulator\n",dvp->id_unit); + else + printf("npx%d: 387 Emulator\n",dvp->id_unit); + } npxinit(__INITIAL_NPXCW__); return (1); /* XXX unused */ } diff --git a/sys/i386/isa/pccons.c b/sys/i386/isa/pccons.c index 29c47e9ebe..f68c34b269 100644 --- a/sys/i386/isa/pccons.c +++ b/sys/i386/isa/pccons.c @@ -33,28 +33,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pccons.c 5.11 (Berkeley) 5/21/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 6 00162 - * -------------------- ----- ---------------------- - * - * 15 Aug 92 Pace Willisson Patches for X server - * 21 Aug 92 Frank Maclachlan Fixed back-scroll system crash - * 28 Nov 92 Terry Lee Fixed LED's in X mode - * 09 Feb 93 Rich Murphey Added 'BELL' mode in X - * 14 Mar 93 Bruce Evans Added keyboard timeout in pcprobe - * Fixed color/mono test and mono - * kernel color. Added check for - * minor. - * 14 Mar 93 Chris G. Demetriou Moved pg() to i386/cons.c, code - * cleanup, removed ctl-alt-del. - * 22 Apr 93 Holger Veit Added cons_highlight/cons_normal - * to eliminate ESC sequence in - * init_main.c + * from: @(#)pccons.c 5.11 (Berkeley) 5/21/91 + * $Id$ */ -static char rcsid[] = "$Header: /usr/bill/working/sys/i386/isa/RCS/pccons.c,v 1.2 92/01/21 14:35:28 william Exp $"; /* * code to work keyboard & display for PC-style console @@ -130,9 +111,9 @@ struct isa_driver pcdriver = { #define ROW 25 #define CHR 2 #define MONO_BASE 0x3B4 -#define MONO_BUF 0xfe0B0000 +#define MONO_BUF (KERNBASE + 0xB0000) #define CGA_BASE 0x3D4 -#define CGA_BUF 0xfe0B8000 +#define CGA_BUF (KERNBASE + 0xB8000) #define IOPHYSMEM 0xA0000 static unsigned int addr_6845 = MONO_BASE; @@ -274,8 +255,7 @@ struct isa_device *dev; break; } } - - return (1); + return (IO_KBDSIZE); } pcattach(dev) @@ -285,8 +265,8 @@ struct isa_device *dev; u_short was; if (vs.color == 0) - printf(""); - else printf(""); + printf("pc%d: type monochrome\n",dev->id_unit); + else printf("pc%d: type color\n",dev->id_unit); cursor(0); } @@ -1377,10 +1357,22 @@ update_led() { int response; - if (kbd_cmd(KBC_STSIND) != 0) + if (kbd_cmd(KBC_STSIND) != 0) { printf("Timeout for keyboard LED command\n"); - else if (kbd_cmd(scroll | (num << 1) | (caps << 2)) != 0) - printf("Timeout for keyboard LED data\n"); + } else { + /* + * XXX This is quite questionable, but seems to fix + * the problem reported. + * some keyboard controllers need some time after they + * get a command. Without this the keyboard 'hangs'. + * This seems to be the only place where two commands + * are just one behind another. + */ + DELAY (10000); + + if (kbd_cmd(scroll | (num << 1) | (caps << 2)) != 0) + printf("Timeout for keyboard LED data\n"); + } #if 0 else if ((response = kbd_response()) < 0) printf("Timeout for keyboard LED ack\n"); @@ -1415,6 +1407,14 @@ loop: #ifdef XSERVER /* 15 Aug 92*/ if (inb(KBSTATP) & KBS_DIB) { dt = inb(KBDATAP); +#ifdef REVERSE_CAPS_CTRL + /* switch the caps lock and control keys */ + if ((dt & 0x7f) == 29) + dt = (dt & 0x80) | 58; + else + if ((dt & 0x7f) == 58) + dt = (dt & 0x80) | 29; +#endif if (pc_xmode) { capchar[0] = dt; /* @@ -1443,8 +1443,17 @@ loop: } } #else /* !XSERVER*/ - if (inb(KBSTATP) & KBS_DIB) + if (inb(KBSTATP) & KBS_DIB) { dt = inb(KBDATAP); +#ifdef REVERSE_CAPS_CTRL + /* switch the caps lock and control keys */ + if ((dt & 0x7f) == 29) + dt = (dt & 0x80) | 58; + else + if ((dt & 0x7f) == 58) + dt = (dt & 0x80) | 29; +#endif + } #endif /* !XSERVER*/ else { diff --git a/sys/i386/isa/rtc.h b/sys/i386/isa/rtc.h index 2de060db24..3f7b4c24d3 100644 --- a/sys/i386/isa/rtc.h +++ b/sys/i386/isa/rtc.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)rtc.h 7.1 (Berkeley) 5/12/91 + * from: @(#)rtc.h 7.1 (Berkeley) 5/12/91 + * $Id$ */ /* diff --git a/sys/i386/isa/sio.c b/sys/i386/isa/sio.c index 38503fd783..055a5e807d 100644 --- a/sys/i386/isa/sio.c +++ b/sys/i386/isa/sio.c @@ -30,17 +30,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)sio.c 7.5 (Berkeley) 5/16/91 - * - * 27 May 93 Bruce Evans From com-0.2 package, fast interrupt - * com port driver. - * 27 May 93 Guido van Rooij Ported in Chris Demetriou's BIDIR - * code, add multiport support. - * 27 May 93 Rodney W. Grimes I then renamed it to sio.c for putting - * into the patch kit. Added in sioselect - * from com.c. Added port 4 support. + * from: @(#)com.c 7.5 (Berkeley) 5/16/91 + * $Id$ */ -static char rcsid[] = "$Header: /usr/bill/working/sys/i386/isa/RCS/com.c,v 1.2 92/01/21 14:34:11 william Exp $"; #include "sio.h" #if NSIO > 0 @@ -269,6 +261,7 @@ static int sioprobe __P((struct isa_device *dev)); static void compoll __P((void)); static int comstart __P((struct tty *tp)); static void comwakeup __P((void)); +static int tiocm2mcr __P((int)); /* table and macro for fast conversion from a unit number to its com struct */ static struct com_s *p_com_addr[NSIO]; @@ -351,7 +344,7 @@ sioprobe(dev) already_init = TRUE; } iobase = dev->id_iobase; - result = 1; + result = IO_COMSIZE; /* * We don't want to get actual interrupts, just masked ones. @@ -376,7 +369,6 @@ sioprobe(dev) if ( inb(iobase + com_cfcr) != CFCR_8BITS || inb(iobase + com_ier) != IER_ETXRDY || inb(iobase + com_mcr) != MCR_IENABLE - || !isa_irq_pending(dev) || (inb(iobase + com_iir) & IIR_IMASK) != IIR_TXRDY || isa_irq_pending(dev) || (inb(iobase + com_iir) & IIR_IMASK) != IIR_NOPEND) @@ -465,8 +457,13 @@ sioattach(isdp) outb(iobase + com_scr, 0x5a); scr2 = inb(iobase + com_scr); outb(iobase + com_scr, scr); + printf("sio%d: type", unit); +#ifdef COM_MULTIPORT + if (0); +#else if (scr1 != 0xa5 || scr2 != 0x5a) printf(" <8250>"); +#endif else { outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER_14); DELAY(100); @@ -507,6 +504,7 @@ sioattach(isdp) else com->multiport = FALSE; #endif /* COM_MULTIPORT */ + printf("\n"); #ifdef KGDB if (kgdb_dev == makedev(commajor, unit)) { @@ -639,7 +637,7 @@ bidir_open_top: /* if not active, turn DTR & RTS off */ if (!com->active) - (void) commctl(com, MCR_DTR | MCR_RTS, DMBIC); + (void) commctl(com, MCR_DTR, DMBIC); /* if there was an error, take off. */ if (error != 0) { @@ -668,13 +666,11 @@ bidir_open_top: * since those are only relevant for logins. It's * important to have echo off initially so that the * line doesn't start blathering before the echo flag - * can be turned off. It's useful to have clocal on - * initially so that "stty changed-defaults t_iflag = 0; tp->t_oflag = 0; - tp->t_cflag = CREAD | CS8 | CLOCAL; + tp->t_cflag = CREAD | CS8 | HUPCL; tp->t_lflag = 0; tp->t_ispeed = tp->t_ospeed = comdefaultrate; } @@ -783,7 +779,7 @@ comhardclose(com) tp = com->tp; if (tp->t_cflag & HUPCL || tp->t_state & TS_WOPEN || !(tp->t_state & TS_ISOPEN)) - (void) commctl(com, 0, DMSET); + (void) commctl(com, MCR_RTS, DMSET); #ifdef COM_BIDIR com->active = com->active_in = com->active_out = FALSE; com->softDCD = FALSE; @@ -975,6 +971,15 @@ comintr1(struct com_s *com) } } +static int +tiocm2mcr(data) + int data; +{ + register m = 0; + if (data & TIOCM_DTR) m |= MCR_DTR; + if (data & TIOCM_RTS) m |= MCR_RTS; + return m; +} int sioioctl(dev, cmd, data, flag, p) dev_t dev; @@ -1008,22 +1013,34 @@ sioioctl(dev, cmd, data, flag, p) outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; case TIOCSDTR: - (void) commctl(com, MCR_DTR | MCR_RTS, DMBIS); + (void) commctl(com, MCR_DTR, DMBIS); break; case TIOCCDTR: - (void) commctl(com, MCR_DTR | MCR_RTS, DMBIC); + (void) commctl(com, MCR_DTR, DMBIC); break; case TIOCMSET: - (void) commctl(com, *(int *)data, DMSET); + (void) commctl(com, tiocm2mcr(*(int *)data), DMSET); break; case TIOCMBIS: - (void) commctl(com, *(int *)data, DMBIS); + (void) commctl(com, tiocm2mcr(*(int *)data), DMBIS); break; case TIOCMBIC: - (void) commctl(com, *(int *)data, DMBIC); + (void) commctl(com, tiocm2mcr(*(int *)data), DMBIC); break; case TIOCMGET: - *(int *)data = commctl(com, 0, DMGET); + { + register int bits = 0, mode; + + mode = commctl(com, 0, DMGET); + if (inb(com->iobase+com_ier)) bits |= TIOCM_LE; /* XXX */ + if (mode & MSR_DCD) bits |= TIOCM_CD; + if (mode & MSR_CTS) bits |= TIOCM_CTS; + if (mode & MSR_DSR) bits |= TIOCM_DSR; + if (mode & (MCR_DTR<<8)) bits |= TIOCM_DTR; + if (mode & (MCR_RTS<<8)) bits |= TIOCM_RTS; + if (mode & (MSR_RI|MSR_TERI)) bits |= TIOCM_RI; + *(int *)data = bits; + } break; #ifdef COM_BIDIR case TIOCMSBIDIR: @@ -1162,7 +1179,7 @@ repeat: disable_intr(); outb(com->modem_ctl_port, com->mcr_image - &= ~(MCR_DTR | MCR_RTS)); + &= ~MCR_DTR); enable_intr(); } } @@ -1297,7 +1314,7 @@ comparam(tp, t) iobase = com->iobase; s = spltty(); if (divisor == 0) { - (void) commctl(com, 0, DMSET); /* hang up line */ + (void) commctl(com, MCR_RTS, DMSET); /* hang up line */ splx(s); return (0); } @@ -1404,8 +1421,10 @@ comstart(tp) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); } else { - if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater) + if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater) { + tp->t_state &= ~TS_RTSBLOCK; outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); + } } enable_intr(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) @@ -1493,7 +1512,7 @@ commctl(com, bits, how) com->mcr_image &= ~(bits & ~MCR_IENABLE)); break; case DMGET: - bits = com->prev_modem_status; + bits = com->prev_modem_status | (com->mcr_image << 8); break; } enable_intr(); diff --git a/sys/i386/isa/sound/COPYING b/sys/i386/isa/sound/COPYING new file mode 100644 index 0000000000..be7703ab2f --- /dev/null +++ b/sys/i386/isa/sound/COPYING @@ -0,0 +1,27 @@ +/* + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Unmodified version of this file (COPYING) must be provided with + * any source or binary distributions. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ diff --git a/sys/i386/isa/sound/HOWTO_MIDI b/sys/i386/isa/sound/HOWTO_MIDI new file mode 100644 index 0000000000..f0601e52b3 --- /dev/null +++ b/sys/i386/isa/sound/HOWTO_MIDI @@ -0,0 +1,51 @@ +The following file describes the procedure for adding modules to MIDI +Please READ the main documentation files for the driver first!!! + + + Example: We have a sound card with a MIDI chip & port on it + and, we call it the 'MYBLASTER' card: + + ************************************************************************** + + 0: Run 'configure'. Select the MIDI on CHIP support option. + + 1: Write a midi driver module; 'blast_midi.c' + (with functions for open,close,read,write,attach.) + + 1a: Write all functions except the 'attach' the way you want. + + For the 'attach' function, look at a model in the 'pro_midi.c' + file. Just dup it in the same fashion. For the 'generic_midi_operations' + structure which is required, see file 'dev_table.h'. + + 2: We called the 'attach' function: 'blast_attach'. + + Go to the file 'dev_table.h' and add your driver name and the function + pointer ( which is 'blast_attach' ) to the 'midi_supported' table. + + 3: You are almost set. Go and make a reference + to an 'exclude constant'; say EXLCUDE_BLAST_MIDI in your module + (refer to the 'pro_midi.c' file for model). Also make sure to + add the constant to the file 'sound_config.h' (for example, look + where the constant EXCLUDE_PRO_MIDI is in the file.) + + 4: Add the line + + #define ALL_EXTERNAL_TO_ME + + as the 1st line of your 'blast_midi.c' file. This happily, makes + you ignorant of everything else specific to the driver! :). + + 4a: And of course, don't forget to make a device :). Note that your + minor number should be = ( 15 + position of your driver in the + 'midi_supported' table in the 'dev_table.h' file ). + + Eg: Your driver is the second one in the table. viz midi_supported[1]. + Your device minor number should be ( 15 + 1 = 16 ). Then, make the + reference to your device as, say CMIDI_DEV_BLAST in the file + 'sound_config.h'. Also add this in 'soundcard.c' for the open, read, + write and close routines. See files for example using CMIDI_DEV_PRO + (which is the ProAudioSpectrum on chip MIDI). + + 5: You are all set. If ever you have problems, follow the same model + as the file 'pro_midi.c', except for substituting your own functions! diff --git a/sys/i386/isa/sound/README b/sys/i386/isa/sound/README new file mode 100644 index 0000000000..efb0b11952 --- /dev/null +++ b/sys/i386/isa/sound/README @@ -0,0 +1,17 @@ +CAUTION! + +This is a prototype version of the Linux Sound Driver for FreeBSD. + +The official and supported version is 1.0c. + +This version 'should work' but there may be some bugs and the programmers +API may change before the final version. + +There are some additional programs for GUS owners in the +gustest subdirectory of this directory, namely a module +(.MOD, .STM and .669) player and a patch file loader. +Additionally, there is a midithru program which allows +you to play the synth on the soundcard with a midi keyboard +(also usable for OPL-3 owners). + +Hannu & FreeBSD team. diff --git a/sys/i386/isa/sound/RELNOTES b/sys/i386/isa/sound/RELNOTES new file mode 100644 index 0000000000..03d492dd0e --- /dev/null +++ b/sys/i386/isa/sound/RELNOTES @@ -0,0 +1,38 @@ +Welcome to use the Linux sound driver for FreeBSD. This +driver supports the SoundBlaster, SB Pro, Pro Audio Spectrum 16, +AdLib and Gravis UltraSound sound cards. + +In addition there is rather limited support for MPU-401 +(and compatible) midi cards. Also, the OPL-3 synthesizer +of the SB Pro and PAS16 cards is now supported in the 4 OP +modes. + +Most of the features of the /dev/sequencer device file are +available just for GUS owners. + +The SoundBlaster 16 and SB 16 ASP cards are not supported, +though they may work in mono mode with speeds < 22 kHz. +The OPL-3 chicp of the SB 16 should work (without problems?). +Is there anybody willing to implement the SB 16 support +(have the SB 16 and the SDK for it)? + +Since this driver is a sound driver, it does not contain support +for SCSI/CD-ROM/Joystick -devices. + +Known bugs +---------- + +- It's not possible to open /dev/dsp (or /dev/audio) while the + /dev/sequencer is open for output and GUS is the only soundcard + installed. It's possible if /dev/dsp is opened before /dev/sequencer + but at this time the GUS is not available for access via /dev/sequencer. + This is a limitation of the driver. +- MPU-401 driver hangs the computer on boot if there is no MPU-401 installed. + It uses by default the I/O port 0x330, which is also used by the + Adaptec 1542 SCSI adapter. +- The /dev/sequencer playback to GUS sounds sometimes rather weird. Hitting + ^C and playing again should solve this problem. This is probably caused by + incompatibilities between the GUS and certain VLB motherboards. Try to avoid + switching between VTs while patches are being loaded to the GUS. +- There is a skeleton of the patch manager support. It doesn't work in + this version. diff --git a/sys/i386/isa/sound/RELNOTES.Linux b/sys/i386/isa/sound/RELNOTES.Linux new file mode 100644 index 0000000000..4082f4e255 --- /dev/null +++ b/sys/i386/isa/sound/RELNOTES.Linux @@ -0,0 +1,191 @@ +Release notes for the Linux Sound Driver 1.99.9 +----------------------------------------------- + +******** THIS IS A BETA TEST RELEASE ******** +which means that there can be some untested things. In theory +there is a risk that this driver causes some trouble to your system. +You should not use this driver before backing up your disks. + + + + +Welcome to use the Gravis UltraSound driver for Linux. This +driver still supports the same cards than version 1.0c +(SoundBlaster, SB Pro, Pro Audio Spectrum 16 and AdLib). +In addition there is rather limited support for MPU-401 +(and compatible) midi cards. Also the OPL-3 synthesizer +of the SB Pro and PAS16 cards is now supported in the 4 OP +modes. +Most of the features of the /dev/sequencer device file are +available just for GUS owners. + +The SoundBlaster 16 and SB 16 ASP cards are not supported. +They could work in mono mode with speeds < 22 kHz. +The OPL-3 chicp of the SB 16 should work (without problems?). +Is there anybody willing to implement the SB 16 support +(have the SB 16 and the SDK for it)? + + +This is the first version of the driver which has almost +all of the features which I have planned to include into +version 2.0. Some features are still missing and some ones +doesn't work. + +NOTE! There are separate driver for CD-ROMS supported by + some soundcards. The driver for CDU31A (Fusion 16) is + called cdu31a-0.6.diff.z. It will be contained in the + Linux version 0.99.12. The driver for the CD-ROM of SB Pro + is sbpcd0.4.tar.gz (these were the latest versions when I wrote + this). These files should be at least at sunsite.unc.edu. + As far as I know, there is no driver for the SCSI interface of PAS16 + (yet). + + There is also a driver for joystick. Look for file joystick-0.5.tar.gz + (sunsite). + + Since this driver is a sound driver, it will not contain support + for SCSI/CD-ROM/Joystick -devices. + +Compatibility with the earlier versions +--------------------------------------- + +This is just like the version 1.99.7/1.99.8. There is just some minor +enhancements. Most of them are portability fixes. If you are porting +this driver to any OS, please look at the 386bsd/os.h. There is some +new macros and some macros have more parameters. In addition this file +contains some usefull comments. + +**** There is some ISC and 386bsd stuff in this driver. Please stay away **** +This stuff is here just because I want to be in sync with the porters. These +ports don't work yet. + +The ioctl() interface has changed completely since version 1.0c. All +programs using this driver must be at least recompiled. +The snd-util-1.99.6 package contains some utilities for this version. + +The version 1.0c and earlier used a 'nonportable' ioctl calling scheme +where the input argument was passed by value and the output value was +returned as the functional return. For example setting the speed of +/dev/dsp were done as the following: + + int actual_speed; + actual_speed = ioctl(fd, SOUND_PCM_WRITE_RATE, 44100); + +After version 1.99.0 this must be done as the following: + + int actual_speed = 44100; + ioctl(fd, SOUND_PCM_WRITE_RATE, &actual_speed); + +If you have an application written for the version 1.0, you should search +for the strings SNDCTL_ and SOUND_ and to check the parameters. + +Since the this version will support more than one synthesizer devices +at the same time, the ioctl(SNDCTL_FM_LOAD_INSTR) is obsolete. In addition +there is some new fields which must be initialized. Look at the sbiset.c in +the snd-util-1.99.6 package for further info. + +The GUS patch format has changed since the version 1.99.3. You have to +use latest versions of the programs in the sound/gustest directory. In +addition the version 0.4g of the Adagio package supports this format. + +New features +------------ + +There is also some changes which make this version more usable than +the version 1.0c. + +- /dev/dsp and /dev/audio + +The DMA buffering is now little bit more intelligent than earlier. The +buffer size is selected run-time so that a buffer holds data for 0.5 to +1.0 seconds of recording or playback. This makes recording more comfortable +than with version 1.0. With the previous version there was sometimes more +than 10 seconds of delay before the driver returned the first input byte. + +There is also support for more than one digitized voice devices. The device +files /dev/dsp1 and /dev/audio1 (minor 19 and 20) are available with PAS16. +The /dev/dsp (/dev/audio) is connected to the PCM circuit of the PAS16 itself +and the /dev/dsp1 (/dev/audio1) to the SB emulation of PAS16 card. Two +dsp/audio devices are available also if you have combination of SB and GUS. +With GUS and PAS16 you will have even three dsp/audio devices. These devices +can be used independently and can be active at the same time (3 channels +at the same time propably don't work). + +The dsp/audio support of PAS16 should be much cleaner now since the +constant clicking sound between the DMA blocks (about once per second) has +been eliminated. + +The stereo playback of GUS doesn't work perfectly. There is lot of +clicking in the output. + +- /dev/mixer + +No changes. + +There is no mixer for the GUS yet. + +- /dev/sequencer + +This part has the most changes. Mostly to support the rich +features of the Gravis UltraSound. There is also the support +for the OPL-3 synthesizer chip. + +- /dev/sndstat + +This is a new devicefile for debugging purposes. A better place for +it is in the /proc -directory but I was just too lazy to implement it +properly. The /dev/sndstat (major 14, minor 6) is a file which returns +info about the current configuration (see the example below). If you +send me a error/problem report, please include a printout from this +device to your message (cat /dev/sndstat). + +------ cut here --- cat /dev/sndstat example -------- +Sound Driver:1.99.7 (Fri Jul 9 17:01:47 GMT 1993 root@lucifer.savolai.fi) +Config options: 0x00000d4b + +Major number: 14 +HW config: +Type 4: Gravis Ultrasound at 0x210 irq 15 drq 6 +Type 3: ProAudioSpectrum at 0x388 irq 10 drq 3 +Type 2: SoundBlaster at 0x220 irq 7 drq 1 +Type 1: AdLib at 0x388 irq 0 drq 0 + +PCM devices: +00: Gravis UltraSound +01: Pro Audio Spectrum +02: SoundBlaster 2.0 + +Synth devices: +00: Gravis UltraSound +01: Yamaha OPL-3 + +Midi devices: +00: Gravis UltraSound +01: Pro Audio Spectrum + +Mixer(s) installed +------ cut here ---- End of Example ----------- + + +Known bugs +---------- + +- There was clicking during stereo playback to /dev/dsp with GUS. + * Fixed in 1.99.9 * +- It's not possible to open /dev/dsp (or /dev/audio) while the + /dev/sequencer is open for output and GUS is the only soundcard + installed. It's possible if /dev/dsp is opened before /dev/sequencer + but at this time the GUS is not available for access via /dev/sequencer. + This is a limitation of the driver. +- MPU-401 driver hangs the computer on boot if there is no MPU-401 installed. + It uses by default the I/O port 0x330 whic is used by Adaptec 1542 SCSI + adapter. +- The /dev/sequencer playback to GUS sounds sometimes rather weird. Hitting + ^C and playing again should solve this problem. This is propably caused by + incompatibilities between GUS and certain VLB motherboards. Try to avoid + switching between VTs while patches are being loaded to the GUS. +- There was some problems with GUS and Mitsumi CD in version 1.99.8. Fixed + in 1.99.9. +- /dev/audio sounded like stereo with GUS. Fixed in 1.99.9. +- There is a skeleton of the patch manager support. It don't work in + this version. diff --git a/sys/i386/isa/sound/adlib_card.c b/sys/i386/isa/sound/adlib_card.c new file mode 100644 index 0000000000..a9cf568e42 --- /dev/null +++ b/sys/i386/isa/sound/adlib_card.c @@ -0,0 +1,32 @@ + +/* + * linux/kernel/chr_drv/sound/adlib_card.c + * + * Detection routine for the AdLib card. + * + * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further + * details. Should be distributed with this file. + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_YM3812) + +long +attach_adlib_card (long mem_start, struct address_info *hw_config) +{ + + if (opl3_detect (FM_MONO)) + { + mem_start = opl3_init (mem_start); + } + return mem_start; +} + +int +probe_adlib (struct address_info *hw_config) +{ + return opl3_detect (FM_MONO); +} + +#endif diff --git a/sys/i386/isa/sound/audio.c b/sys/i386/isa/sound/audio.c new file mode 100644 index 0000000000..e35366e090 --- /dev/null +++ b/sys/i386/isa/sound/audio.c @@ -0,0 +1,278 @@ +/* + * linux/kernel/chr_drv/sound/audio.c + * + * Device file manager for /dev/audio + * + * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further + * details. Should be distributed with this file. + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD +#ifndef EXCLUDE_AUDIO + +#include "ulaw.h" + +#define ON 1 +#define OFF 0 + +static int wr_buff_no[MAX_DSP_DEV]; /* != -1, if there is a + * incomplete output block */ +static int wr_buff_size[MAX_DSP_DEV], wr_buff_ptr[MAX_DSP_DEV]; +static char *wr_dma_buf[MAX_DSP_DEV]; + +int +audio_open (int dev, struct fileinfo *file) +{ + int mode; + int ret; + + dev = dev >> 4; + mode = file->mode & O_ACCMODE; + + if ((ret = DMAbuf_open (dev, mode)) < 0) + return ret; + + wr_buff_no[dev] = -1; + return ret; +} + +void +audio_release (int dev, struct fileinfo *file) +{ + int mode; + + dev = dev >> 4; + mode = file->mode & O_ACCMODE; + + if (wr_buff_no[dev] >= 0) + { + DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]); + + wr_buff_no[dev] = -1; + } + + DMAbuf_release (dev, mode); +} + +#ifdef NO_INLINE_ASM +static void +translate_bytes (const unsigned char *table, unsigned char *buff, unsigned long n) +{ + unsigned long i; + + for (i = 0; i < n; ++i) + buff[i] = table[buff[i]]; +} + +#else +extern inline void +translate_bytes (const void *table, void *buff, unsigned long n) +{ + __asm__ ("cld\n" + "1:\tlodsb\n\t" + "xlatb\n\t" + "stosb\n\t" + "loop 1b\n\t": + :"b" ((long) table), "c" (n), "D" ((long) buff), "S" ((long) buff) + :"bx", "cx", "di", "si", "ax"); +} + +#endif + +int +audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + int c, p, l; + int err; + + dev = dev >> 4; + + p = 0; + c = count; + + if (!count) /* Flush output */ + { + if (wr_buff_no[dev] >= 0) + { + DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]); + + wr_buff_no[dev] = -1; + } + return 0; + } + + while (c) + { /* Perform output blocking */ + if (wr_buff_no[dev] < 0) /* There is no incomplete buffers */ + { + if ((wr_buff_no[dev] = DMAbuf_getwrbuffer (dev, &wr_dma_buf[dev], &wr_buff_size[dev])) < 0) + return wr_buff_no[dev]; + wr_buff_ptr[dev] = 0; + } + + l = c; + if (l > (wr_buff_size[dev] - wr_buff_ptr[dev])) + l = (wr_buff_size[dev] - wr_buff_ptr[dev]); + + COPY_FROM_USER (&wr_dma_buf[dev][wr_buff_ptr[dev]], buf, p, l); + + /* Insert local processing here */ + +#ifdef linux + /* This just allows interrupts while the conversion is running */ + __asm__ ("sti"); +#endif + translate_bytes (ulaw_dsp, &wr_dma_buf[dev][wr_buff_ptr[dev]], l); + + c -= l; + p += l; + wr_buff_ptr[dev] += l; + + if (wr_buff_ptr[dev] >= wr_buff_size[dev]) + { + if ((err = DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev])) < 0) + return err; + + wr_buff_no[dev] = -1; + } + + } + + return count; +} + +int +audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + int c, p, l; + char *dmabuf; + int buff_no; + + dev = dev >> 4; + p = 0; + c = count; + + while (c) + { + if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l)) < 0) + return buff_no; + + if (l > c) + l = c; + + /* Insert any local processing here. */ +#ifdef linux + /* This just allows interrupts while the conversion is running */ + __asm__ ("sti"); +#endif + + translate_bytes (dsp_ulaw, dmabuf, l); + + COPY_TO_USER (buf, p, dmabuf, l); + + DMAbuf_rmchars (dev, buff_no, l); + + p += l; + c -= l; + } + + return count - c; +} + +int +audio_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg) +{ + dev = dev >> 4; + + switch (cmd) + { + case SNDCTL_DSP_SYNC: + if (wr_buff_no[dev] >= 0) + { + DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]); + + wr_buff_no[dev] = -1; + } + return DMAbuf_ioctl (dev, cmd, arg, 0); + break; + + case SNDCTL_DSP_POST: + if (wr_buff_no[dev] >= 0) + { + DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]); + + wr_buff_no[dev] = -1; + } + return 0; + break; + + case SNDCTL_DSP_RESET: + wr_buff_no[dev] = -1; + return DMAbuf_ioctl (dev, cmd, arg, 0); + break; + + default: +#if 1 + return RET_ERROR (EIO); +#else + return DMAbuf_ioctl (dev, cmd, arg, 0); +#endif + } +} + +long +audio_init (long mem_start) +{ + return mem_start; +} + +#else +/* Stub versions */ + +int +audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + return RET_ERROR (EIO); +} + +int +audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + return RET_ERROR (EIO); +} + +int +audio_open (int dev, struct fileinfo *file) + { + return RET_ERROR (ENXIO); + } + +void +audio_release (int dev, struct fileinfo *file) + { + }; +int +audio_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg) +{ + return RET_ERROR (EIO); +} + +int +audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig) +{ + return RET_ERROR (EIO); +} + +long +audio_init (long mem_start) +{ + return mem_start; +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/debug.h b/sys/i386/isa/sound/debug.h new file mode 100644 index 0000000000..79b4acdeba --- /dev/null +++ b/sys/i386/isa/sound/debug.h @@ -0,0 +1,29 @@ +/* Kernel driver debugging stuff - cmetz@thor.tjhsst.edu */ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#if defined (DEBUG_ME) && !defined(HALT_DEBUGGING) + +#define DEB(X) printk("%s %s: ", __FILE__, __FUNCTION__); X +#define DEB1(X) printk("%s %s: ", __FILE__, __FUNCTION__); X +#define RETURN_HEX(X, Y) { Y _foo; _foo = X; printk("%s %s: 0x%x\n", __FILE__, __FUNCTION__, ((int)_foo)); return(_foo); } +#define RETURN_DEC(X, Y) { Y _foo; _foo = X; printk("%s %s: %d\n", __FILE__, __FUNCTION__, ((int)_foo)); return(_foo); } +#define RETURN_PTR(X, Y) { Y _foo; _foo = X; printk("%s %s: 0x%08x\n", __FILE__, __FUNCTION__, ((void *)_foo)); return(_foo); } +#define RETURN_ERR(X) { int _foo; _foo = X; printk("%s %s: ", __FILE__, __FUNCTION__); switch(_foo) { case 0: printk("No error"); break; case -ENODEV: printk("ENODEV"); break; case -EBUSY: printk("EBUSY"); break; default: printk("Error %d", _foo); } printk(".\n"); return(_foo); } +#define DEB_OUTB OUTB +#define DEB_INB INB + +#else + +#define DEB(X) +#define DEB1(X) +#define RETURN_HEX(X, Y) return(X) +#define RETURN_DEC(X, Y) return(X) +#define RETURN_PTR(X, Y) return(X) +#define RETURN_ERR(X) return(X) +#define DEB_OUTB OUTB +#define DEB_INB INB + +#endif +#endif diff --git a/sys/i386/isa/sound/dev_table.c b/sys/i386/isa/sound/dev_table.c new file mode 100644 index 0000000000..96765cd8d4 --- /dev/null +++ b/sys/i386/isa/sound/dev_table.c @@ -0,0 +1,83 @@ +/* + * linux/kernel/chr_drv/sound/dev_table.c + * + * Device call tables. + * + * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further + * details. Should be distributed with this file. + */ + +#define _DEV_TABLE_C_ +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +long +sndtable_init (long mem_start) +{ + int i, n = sizeof (supported_drivers) / sizeof (struct card_info); + + for (i = 0; i < (n - 1); i++) + if (supported_drivers[i].probe (&supported_drivers[i].config)) + { +#ifndef SHORT_BANNERS + printk ("snd%d", + supported_drivers[i].card_type); +#endif + + mem_start = supported_drivers[i].attach (mem_start, &supported_drivers[i].config); +#ifndef SHORT_BANNERS + printk (" at 0x%03x irq %d drq %d\n", + supported_drivers[i].config.io_base, + supported_drivers[i].config.irq, + supported_drivers[i].config.dma); +#endif + } + return mem_start; +} + +int +sndtable_probe (int unit, struct address_info *hw_config) +{ + int i, n = sizeof (supported_drivers) / sizeof (struct card_info); + + if (!unit) + return TRUE; + + for (i = 0; i < (n - 1); i++) + if (supported_drivers[i].card_type == unit) + return supported_drivers[i].probe (hw_config); + + return FALSE; +} + +int +sndtable_init_card (int unit, struct address_info *hw_config) +{ + int i, n = sizeof (supported_drivers) / sizeof (struct card_info); + + if (!unit) + { + if (sndtable_init (0) != 0) + panic ("snd: Invalid memory allocation\n"); + return TRUE; + } + + for (i = 0; i < (n - 1); i++) + if (supported_drivers[i].card_type == unit) + { + if (supported_drivers[i].attach (0, hw_config) != 0) + panic ("snd#: Invalid memory allocation\n"); + return TRUE; + } + + return FALSE; +} + +int +sndtable_get_cardcount (void) +{ + return num_dspdevs + num_mixers + num_synths + num_midis; +} + +#endif diff --git a/sys/i386/isa/sound/dev_table.h b/sys/i386/isa/sound/dev_table.h new file mode 100644 index 0000000000..64482ce9fd --- /dev/null +++ b/sys/i386/isa/sound/dev_table.h @@ -0,0 +1,229 @@ +/* + linux/kernel/chr_drv/sound/dev_table.h + + Global definitions for device call tables + + (C) Hannu Savolainen 1992 + See COPYING for further details. Should be distributed with this file. + +*/ + +#ifndef _DEV_TABLE_H_ +#define _DEV_TABLE_H_ + +/* + * NOTE! NOTE! NOTE! NOTE! + * + * If you modify this file, please check the dev_table.c also. + * + * NOTE! NOTE! NOTE! NOTE! + */ + +struct card_info { + int card_type; /* From soundcard.c */ + char *name; + long (*attach) (long mem_start, struct address_info *hw_config); + int (*probe) (struct address_info *hw_config); + struct address_info config; +}; + +/** UWM -- new MIDI structure here.. **/ + +struct generic_midi_info{ + char *name; /* Name of the MIDI device.. */ + long (*attach) (long mem_start); +}; + +struct audio_operations { + char name[32]; + int (*open) (int dev, int mode); + void (*close) (int dev); + void (*output_block) (int dev, unsigned long buf, int count, int intrflag); + void (*start_input) (int dev, unsigned long buf, int count, int intrflag); + int (*ioctl) (int dev, unsigned int cmd, unsigned int arg, int local); + int (*prepare_for_input) (int dev, int bufsize, int nbufs); + int (*prepare_for_output) (int dev, int bufsize, int nbufs); + void (*reset) (int dev); + void (*halt_xfer) (int dev); + int (*has_output_drained)(int dev); + void (*copy_from_user)(int dev, char *localbuf, int localoffs, + snd_rw_buf *userbuf, int useroffs, int len); +}; + +struct mixer_operations { + int (*ioctl) (int dev, unsigned int cmd, unsigned int arg); +}; + +struct synth_operations { + struct synth_info *info; + int synth_type; + int synth_subtype; + + int (*open) (int dev, int mode); + void (*close) (int dev); + int (*ioctl) (int dev, unsigned int cmd, unsigned int arg); + int (*kill_note) (int dev, int voice, int velocity); + int (*start_note) (int dev, int voice, int note, int velocity); + int (*set_instr) (int dev, int voice, int instr); + void (*reset) (int dev); + void (*hw_control) (int dev, unsigned char *event); + int (*load_patch) (int dev, int format, snd_rw_buf *addr, + int offs, int count, int pmgr_flag); + void (*aftertouch) (int dev, int voice, int pressure); + void (*controller) (int dev, int voice, int ctrl_num, int value); + void (*panning) (int dev, int voice, int value); + int (*pmgr_interface) (int dev, struct patmgr_info *info); +}; + +struct midi_operations { + struct midi_info info; + int (*open) (int dev, int mode); + void (*close) (int dev); + int (*ioctl) (int dev, unsigned int cmd, unsigned int arg); + int (*putc) (int dev, unsigned char data); + int (*start_read) (int dev); + int (*end_read) (int dev); + void (*kick)(int dev); + int (*command) (int dev, unsigned char data); + int (*buffer_status) (int dev); +}; + +/** UWM -- new structure for MIDI **/ + +struct generic_midi_operations { + struct midi_info info; + int (*open) (int dev, int mode); + void (*close) (int dev); + int (*write) (int dev, struct uio *data); + int (*read) (int dev, struct uio *data); +}; + +#ifndef ALL_EXTERNAL_TO_ME + +#ifdef _MIDI_TABLE_C_ + +/** UWM **/ + struct generic_midi_operations * generic_midi_devs[MAX_MIDI_DEV] = {NULL}; + int num_generic_midis = 0, pro_midi_dev = 0; + + struct generic_midi_info midi_supported[] = { + +#ifndef EXCLUDE_PRO_MIDI + {"ProAudioSpectrum MV101",pro_midi_attach} +#endif + }; + + int num_midi_drivers = + sizeof (midi_supported) / sizeof(struct generic_midi_info); + +#endif + + +#ifdef _DEV_TABLE_C_ + struct audio_operations * dsp_devs[MAX_DSP_DEV] = {NULL}; int num_dspdevs = 0; + struct mixer_operations * mixer_devs[MAX_MIXER_DEV] = {NULL}; int num_mixers = 0; + struct synth_operations * synth_devs[MAX_SYNTH_DEV] = {NULL}; int num_synths = 0; + struct midi_operations * midi_devs[MAX_MIDI_DEV] = {NULL}; int num_midis = 0; + + +# ifndef EXCLUDE_MPU401 + int mpu401_dev = 0; +# endif + +/* + * Note! The detection order is significant. Don't change it. + */ + + struct card_info supported_drivers[] = { +#ifndef EXCLUDE_MPU401 + {SNDCARD_MPU401,"Roland MPU-401", attach_mpu401, probe_mpu401, + {MPU_BASE, MPU_IRQ, 0}}, +#endif + +#ifndef EXCLUDE_GUS + {SNDCARD_GUS, "Gravis Ultrasound", attach_gus_card, probe_gus, + {GUS_BASE, GUS_IRQ, GUS_DMA}}, +#endif + +#ifndef EXCLUDE_PAS + {SNDCARD_PAS, "ProAudioSpectrum", attach_pas_card, probe_pas, + {PAS_BASE, PAS_IRQ, PAS_DMA}}, +#endif + +#ifndef EXCLUDE_SB + {SNDCARD_SB, "SoundBlaster", attach_sb_card, probe_sb, + {SBC_BASE, SBC_IRQ, SBC_DMA}}, +#endif + +#ifndef EXCLUDE_YM3812 + {SNDCARD_ADLIB, "AdLib", attach_adlib_card, probe_adlib, + {FM_MONO, 0, 0}}, +#endif + {0, "*?*", NULL} + }; + + int num_sound_drivers = + sizeof(supported_drivers) / sizeof (struct card_info); + + +# ifndef EXCLUDE_AUDIO + int sound_buffcounts[MAX_DSP_DEV] = {0}; + long sound_buffsizes[MAX_DSP_DEV] = {0}; + int sound_dsp_dmachan[MAX_DSP_DEV] = {0}; + int sound_dma_automode[MAX_DSP_DEV] = {0}; +# endif +#else + extern struct audio_operations * dsp_devs[MAX_DSP_DEV]; int num_dspdevs; + extern struct mixer_operations * mixer_devs[MAX_MIXER_DEV]; extern int num_mixers; + extern struct synth_operations * synth_devs[MAX_SYNTH_DEV]; extern int num_synths; + extern struct midi_operations * midi_devs[MAX_MIDI_DEV]; extern int num_midis; +# ifndef EXCLUDE_MPU401 + extern int mpu401_dev; +# endif + + extern struct card_info supported_drivers[]; + extern int num_sound_drivers; + +# ifndef EXCLUDE_AUDIO + extern int sound_buffcounts[MAX_DSP_DEV]; + extern long sound_buffsizes[MAX_DSP_DEV]; + extern int sound_dsp_dmachan[MAX_DSP_DEV]; + extern int sound_dma_automode[MAX_DSP_DEV]; +# endif + +#endif + +long sndtable_init(long mem_start); +int sndtable_get_cardcount (void); +long CMIDI_init(long mem_start); /* */ +#endif + +#endif + +/* If external to me.... :) */ + +#ifdef ALL_EXTERNAL_TO_ME + + extern struct audio_operations * dsp_devs[MAX_DSP_DEV]; int num_dspdevs; + extern struct mixer_operations * mixer_devs[MAX_MIXER_DEV]; extern int num_mixers; + extern struct synth_operations * synth_devs[MAX_SYNTH_DEV]; extern int num_synths; + extern struct midi_operations * midi_devs[MAX_MIDI_DEV]; extern int num_midis; + extern struct generic_midi_operations *generic_midi_devs[]; + extern int num_generic_midis, pro_midi_dev; + +#ifndef EXCLUDE_MPU401 + extern int mpu401_dev; +#endif + + extern struct generic_midi_info midi_supported[]; + extern struct card_info supported_drivers[]; + extern int num_sound_drivers; + extern int num_midi_drivers; +#ifndef EXCLUDE_AUDIO + extern int sound_buffcounts[MAX_DSP_DEV]; + extern long sound_buffsizes[MAX_DSP_DEV]; + extern int sound_dsp_dmachan[MAX_DSP_DEV]; + extern int sound_dma_automode[MAX_DSP_DEV]; +#endif + +#endif diff --git a/sys/i386/isa/sound/dmabuf.c b/sys/i386/isa/sound/dmabuf.c new file mode 100644 index 0000000000..5123022961 --- /dev/null +++ b/sys/i386/isa/sound/dmabuf.c @@ -0,0 +1,773 @@ +/* + * linux/kernel/chr_drv/sound/dmabuf.c + * + * The DMA buffer manager for digitized voice applications + * + * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further + * details. Should be distributed with this file. + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#include "sound_calls.h" + +#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_GUS) + +#define MAX_SUB_BUFFERS 16 + +/* + * The DSP channel can be used either for input or output. Variable + * 'dma_mode' will be set when the program calls read or write first time + * after open. Current version doesn't support mode changes without closing + * and reopening the device. Support for this feature may be implemented in a + * future version of this driver. + */ + +#define DMODE_NONE 0 +#define DMODE_OUTPUT 1 +#define DMODE_INPUT 2 +#define DMODE_INIT 3 + +DEFINE_WAIT_QUEUES (dev_sleeper[MAX_DSP_DEV], dev_sleep_flag[MAX_DSP_DEV]); + +static int dma_mode[MAX_DSP_DEV] = +{0}; /* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */ + +static volatile int dmabuf_interrupted[MAX_DSP_DEV] = +{0}; + +#ifdef ISC +/* I don't like this. */ +#undef INTERRUPTIBLE_SLEEP_ON +#define INTERRUPTIBLE_SLEEP_ON(A,F) { \ + A = F = 1; \ + if (sleep(&(A), (PZERO + 5) | PCATCH)) { \ + A = F = 0; \ + dmabuf_interrupted[dev] = 1; \ + dev_busy[dev] = 0; \ + dma_reset(dev); \ + dmabuf_interrupted[dev] = 0; \ + /* longjmp(u.u_qsav, 1); Where it goes??? */ \ + } \ + } +#endif + +/* + * Pointers to raw buffers + */ + +char *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT]; +unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT]; +int snd_raw_count[MAX_DSP_DEV]; + +/* + * Device state tables + */ + +static int dev_busy[MAX_DSP_DEV]; +static int dev_active[MAX_DSP_DEV]; +static int dev_qlen[MAX_DSP_DEV]; +static int dev_qhead[MAX_DSP_DEV]; +static int dev_qtail[MAX_DSP_DEV]; +static int dev_underrun[MAX_DSP_DEV]; +static int bufferalloc_done[MAX_DSP_DEV] = +{0}; + +/* + * Logical buffers for each devices + */ + +static int dev_nbufs[MAX_DSP_DEV]; /* # of logical buffers ( >= + * sound_buffcounts[dev] */ +static int dev_counts[MAX_DSP_DEV][MAX_SUB_BUFFERS]; +static unsigned long dev_buf_phys[MAX_DSP_DEV][MAX_SUB_BUFFERS]; +static char *dev_buf[MAX_DSP_DEV][MAX_SUB_BUFFERS]; +static int dev_buffsize[MAX_DSP_DEV]; + +static void +reorganize_buffers (int dev) +{ + /* + * This routine breaks the physical device buffers to logical ones. + */ + + unsigned long i, p, n; + unsigned long sr, nc, sz, bsz; + + sr = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_RATE, 0, 1); + nc = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_CHANNELS, 0, 1); + sz = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_BITS, 0, 1); + + if (sr < 1 || nc < 1 || sz < 1) + { + printk ("SOUND: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz); + sr = DSP_DEFAULT_SPEED; + nc = 1; + sz = 8; + } + + sz /= 8; /* Convert # of bits -> # of bytes */ + + sz = sr * nc * sz; + + /* + * Compute a buffer size not exeeding 1 second. + */ + + bsz = sound_buffsizes[dev]; + + while (bsz > sz) + bsz >>= 1; /* Divide by 2 */ + + if (sound_buffcounts[dev] == 1 && bsz == sound_buffsizes[dev]) + bsz >>= 1; /* Need at least 2 buffers */ + + dev_buffsize[dev] = bsz; + n = 0; + + /* + * Now computing addresses for the logical buffers + */ + + for (i = 0; i < snd_raw_count[dev]; i++) + { + p = 0; + + while ((p + bsz) <= sound_buffsizes[dev]) + { + dev_buf[dev][n] = snd_raw_buf[dev][i] + p; + dev_buf_phys[dev][n] = snd_raw_buf_phys[dev][i] + p; + p += bsz; + n++; + } + } + + dev_nbufs[dev] = n; + + for (i = 0; i < dev_nbufs[dev]; i++) + { + dev_counts[dev][i] = 0; + } + + bufferalloc_done[dev] = 1; +} + +int +DMAbuf_open (int dev, int mode) +{ + int retval; + + if (dev >= num_dspdevs) + { + printk ("PCM device %d not installed.\n", dev); + return RET_ERROR (ENXIO); + } + + if (dev_busy[dev]) + return RET_ERROR (EBUSY); + + if (!dsp_devs[dev]) + { + printk ("DSP device %d not initialized\n", dev); + return RET_ERROR (ENXIO); + } + + if (sound_buffcounts[dev] <= 0) + return RET_ERROR (ENOSPC); /* Memory allocation failed during boot */ + + if ((retval = dsp_devs[dev]->open (dev, mode)) < 0) + return retval; + + dev_underrun[dev] = 0; + + dev_busy[dev] = 1; + + reorganize_buffers (dev); + bufferalloc_done[dev] = 0; + + dev_qlen[dev] = dev_qtail[dev] = dev_qhead[dev] = 0; + + return 0; +} + +static void +dma_reset (int dev) +{ + dsp_devs[dev]->reset (dev); + + dev_qlen[dev] = 0; + dev_qhead[dev] = 0; + dev_qtail[dev] = 0; + dev_active[dev] = 0; +} + +static int +dma_sync (int dev) +{ + unsigned long flags; + unsigned long time; + int timed_out; + + if (dma_mode[dev] == DMODE_OUTPUT) + { + DISABLE_INTR (flags); + + timed_out = 0; + time = GET_TIME (); + + while ((!(PROCESS_ABORTING || dmabuf_interrupted[dev]) && !timed_out) + && dev_qlen[dev]) + { + REQUEST_TIMEOUT (10 * HZ, dev_sleeper[dev]); + INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]); + if ((GET_TIME () - time) > (10 * HZ)) + timed_out = 1; + } + RESTORE_INTR (flags); + + /* + * Some devices such as GUS have huge amount of on board RAM for the + * audio data. We have to wait util the device has finished playing. + */ + + DISABLE_INTR (flags); + if (dsp_devs[dev]->has_output_drained) /* Device has hidden buffers */ + { + while (!(PROCESS_ABORTING || dmabuf_interrupted[dev]) + && !dsp_devs[dev]->has_output_drained (dev)) + { + REQUEST_TIMEOUT (HZ / 4, dev_sleeper[dev]); + INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]); + } + } + RESTORE_INTR (flags); + } + return dev_qlen[dev]; +} + +int +DMAbuf_release (int dev, int mode) +{ + + if (!(PROCESS_ABORTING || dmabuf_interrupted[dev]) + && (dma_mode[dev] == DMODE_OUTPUT)) + { + dma_sync (dev); + } + + dma_reset (dev); + + if (!dev_active[dev]) + dsp_devs[dev]->close (dev); + + dma_mode[dev] = DMODE_NONE; + dev_busy[dev] = 0; + + return 0; +} + +int +DMAbuf_getrdbuffer (int dev, char **buf, int *len) +{ + unsigned long flags; + + if (!bufferalloc_done[dev]) + reorganize_buffers (dev); + + if (!dma_mode[dev]) + { + int err; + + if ((err = dsp_devs[dev]->prepare_for_input (dev, + dev_buffsize[dev], dev_nbufs[dev])) < 0) + return err; + dma_mode[dev] = DMODE_INPUT; + } + + if (dma_mode[dev] != DMODE_INPUT) + return RET_ERROR (EBUSY); /* Can't change mode on fly */ + + DISABLE_INTR (flags); + if (!dev_qlen[dev]) + { + if (!dev_active[dev]) + { + dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]], dev_buffsize[dev], 0); + dev_active[dev] = 1; + } + + /* Wait for the next block */ +#ifdef CRYPTO + REQUEST_TIMEOUT (60 * HZ, dev_sleeper[dev]); +#else + REQUEST_TIMEOUT (10 * HZ, dev_sleeper[dev]); +#endif + INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]); + } + RESTORE_INTR (flags); + + if (!dev_qlen[dev]) + return RET_ERROR (EINTR); + + *buf = &dev_buf[dev][dev_qhead[dev]][dev_counts[dev][dev_qhead[dev]]]; + *len = dev_buffsize[dev] - dev_counts[dev][dev_qhead[dev]]; + + return dev_qhead[dev]; +} + +int +DMAbuf_rmchars (int dev, int buff_no, int c) +{ + int p = dev_counts[dev][dev_qhead[dev]] + c; + + if (p >= dev_buffsize[dev]) + { /* This buffer is now empty */ + dev_counts[dev][dev_qhead[dev]] = 0; + dev_qlen[dev]--; + dev_qhead[dev] = (dev_qhead[dev] + 1) % dev_nbufs[dev]; + } + else + dev_counts[dev][dev_qhead[dev]] = p; + + return 0; +} + +int +DMAbuf_read (int dev, snd_rw_buf * user_buf, int count) +{ + char *dmabuf; + int buff_no, c, err; + + /* + * This routine returns at most 'count' bytes from the dsp input buffers. + * Returns negative value if there is an error. + */ + + if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &c)) < 0) + return buff_no; + + if (c > count) + c = count; + + COPY_TO_USER (user_buf, 0, dmabuf, c); + + if ((err = DMAbuf_rmchars (dev, buff_no, c)) < 0) + return err; + return c; + +} + +int +DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) +{ + switch (cmd) + { + case SNDCTL_DSP_RESET: + dma_reset (dev); + return 0; + break; + + case SNDCTL_DSP_SYNC: + dma_sync (dev); + return 0; + break; + + case SNDCTL_DSP_GETBLKSIZE: + if (!bufferalloc_done[dev]) + reorganize_buffers (dev); + + return IOCTL_OUT (arg, dev_buffsize[dev]); + break; + + default: + return dsp_devs[dev]->ioctl (dev, cmd, arg, local); + } + + return RET_ERROR (EIO); +} + +int +DMAbuf_getwrbuffer (int dev, char **buf, int *size) +{ + unsigned long flags; + + if (!bufferalloc_done[dev]) + reorganize_buffers (dev); + + if (!dma_mode[dev]) + { + int err; + + dma_mode[dev] = DMODE_OUTPUT; + if ((err = dsp_devs[dev]->prepare_for_output (dev, + dev_buffsize[dev], dev_nbufs[dev])) < 0) + return err; + } + + if (dma_mode[dev] != DMODE_OUTPUT) + return RET_ERROR (EBUSY); /* Can't change mode on fly */ + + DISABLE_INTR (flags); + if (dev_qlen[dev] == dev_nbufs[dev]) + { + if (!dev_active[dev]) + { + printk ("Soundcard warning: DMA not activated %d/%d\n", + dev_qlen[dev], dev_nbufs[dev]); + return RET_ERROR (EIO); + } + + /* Wait for free space */ + REQUEST_TIMEOUT (60 * HZ, dev_sleeper[dev]); /* Overestimated timeout */ + INTERRUPTIBLE_SLEEP_ON (dev_sleeper[dev], dev_sleep_flag[dev]); + } + RESTORE_INTR (flags); + + if (dev_qlen[dev] == dev_nbufs[dev]) + return RET_ERROR (EIO); /* We have got signal (?) */ + + *buf = dev_buf[dev][dev_qtail[dev]]; + *size = dev_buffsize[dev]; + dev_counts[dev][dev_qtail[dev]] = 0; + + return dev_qtail[dev]; +} + +int +DMAbuf_start_output (int dev, int buff_no, int l) +{ + if (buff_no != dev_qtail[dev]) + printk ("Soundcard warning: DMA buffers out of sync %d != %d\n", buff_no, dev_qtail[dev]); + + dev_qlen[dev]++; + + dev_counts[dev][dev_qtail[dev]] = l; + + dev_qtail[dev] = (dev_qtail[dev] + 1) % dev_nbufs[dev]; + + if (!dev_active[dev]) + { + dev_active[dev] = 1; + dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]], dev_counts[dev][dev_qhead[dev]], 0); + } + + return 0; +} + +int +DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) +{ + int chan = sound_dsp_dmachan[dev]; + unsigned long flags; + + /* + * This function is not as portable as it should be. + */ + + /* + * The count must be one less than the actual size. This is handled by + * set_dma_addr() + */ + + if (sound_dma_automode[dev]) + { /* Auto restart mode. Transfer the whole + * buffer */ +#ifdef linux + DISABLE_INTR (flags); + disable_dma (chan); + clear_dma_ff (chan); + set_dma_mode (chan, dma_mode | DMA_AUTOINIT); + set_dma_addr (chan, snd_raw_buf_phys[dev][0]); + set_dma_count (chan, sound_buffsizes[dev]); + enable_dma (chan); + RESTORE_INTR (flags); +#else + +#ifdef __386BSD__ + printk ("sound: Invalid DMA mode for device %d\n", dev); + + isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE, + snd_raw_buf_phys[dev][0], + sound_buffsizes[dev], + chan); +#else +#ifdef ISC + printk ("sound: Invalid DMA mode for device %d\n", dev); + dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode) | DMAMODE_AUTO, + snd_raw_buf_phys[dev][0], count - 1); + dma_enable (chan); +#else +# error This routine is not valid for this OS. +#endif +#endif + +#endif + } + else + { +#ifdef linux + DISABLE_INTR (flags); + disable_dma (chan); + clear_dma_ff (chan); + set_dma_mode (chan, dma_mode); + set_dma_addr (chan, physaddr); + set_dma_count (chan, count); + enable_dma (chan); + RESTORE_INTR (flags); +#else +#ifdef __386BSD__ + isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE, + physaddr, + count, + chan); +#else + +#ifdef ISC + dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode), + physaddr, count - 1); + dma_enable (chan); +#else +# error This routine is not valid for this OS. +#endif /* !ISC */ +#endif + +#endif + } + + return count; +} + +long +DMAbuf_init (long mem_start) +{ + int i; + + /* + * In this version the DMA buffer allocation is done by sound_mem_init() + * which is called by init/main.c + */ + + for (i = 0; i < MAX_DSP_DEV; i++) + { + dev_qlen[i] = 0; + dev_qhead[i] = 0; + dev_qtail[i] = 0; + dev_active[i] = 0; + dev_busy[i] = 0; + bufferalloc_done[i] = 0; + } + + return mem_start; +} + +void +DMAbuf_outputintr (int dev) +{ + unsigned long flags; + + dev_active[dev] = 0; + dev_qlen[dev]--; + dev_qhead[dev] = (dev_qhead[dev] + 1) % dev_nbufs[dev]; + + if (dev_qlen[dev]) + { + dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]], dev_counts[dev][dev_qhead[dev]], 1); + dev_active[dev] = 1; + } + else + { + if (dev_busy[dev]) + { + dev_underrun[dev]++; + dsp_devs[dev]->halt_xfer (dev); + } + else + { /* Device has been closed */ + dsp_devs[dev]->close (dev); + } + } + + DISABLE_INTR (flags); + if (dev_sleep_flag[dev]) + { + dev_sleep_flag[dev] = 0; + WAKE_UP (dev_sleeper[dev]); + } + RESTORE_INTR (flags); +} + +void +DMAbuf_inputintr (int dev) +{ + unsigned long flags; + + dev_active[dev] = 0; + if (!dev_busy[dev]) + { + dsp_devs[dev]->close (dev); + } + else if (dev_qlen[dev] == (dev_nbufs[dev] - 1)) + { + dev_underrun[dev]++; + dsp_devs[dev]->halt_xfer (dev); + } + else + { + dev_qlen[dev]++; + dev_qtail[dev] = (dev_qtail[dev] + 1) % dev_nbufs[dev]; + + dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]], dev_buffsize[dev], 1); + dev_active[dev] = 1; + } + + DISABLE_INTR (flags); + if (dev_sleep_flag[dev]) + { + dev_sleep_flag[dev] = 0; + WAKE_UP (dev_sleeper[dev]); + } + RESTORE_INTR (flags); +} + +int +DMAbuf_open_dma (int dev) +{ + unsigned long flags; + int chan = sound_dsp_dmachan[dev]; + + if (ALLOC_DMA_CHN (chan)) + { + printk ("Unable to grab DMA%d for the audio driver\n", chan); + return 0; + } + + DISABLE_INTR (flags); +#ifdef linux + disable_dma (chan); + clear_dma_ff (chan); +#endif + RESTORE_INTR (flags); + + return 1; +} + +void +DMAbuf_close_dma (int dev) +{ + int chan = sound_dsp_dmachan[dev]; + + DMAbuf_reset_dma (chan); + RELEASE_DMA_CHN (chan); +} + +void +DMAbuf_reset_dma (int chan) +{ +} + +/* + * The sound_mem_init() is called by mem_init() immediately after mem_map is + * initialized and before free_page_list is created. + * + * This routine allocates DMA buffers at the end of available physical memory ( + * <16M) and marks pages reserved at mem_map. + */ + +#else +/* Stub versions if audio services not included */ + +int +DMAbuf_open (int dev, int mode) +{ + return RET_ERROR (ENXIO); +} + +int +DMAbuf_release (int dev, int mode) +{ + return 0; +} + +int +DMAbuf_read (int dev, snd_rw_buf * user_buf, int count) +{ + return RET_ERROR (EIO); +} + +int +DMAbuf_getwrbuffer (int dev, char **buf, int *size) +{ + return RET_ERROR (EIO); +} + +int +DMAbuf_getrdbuffer (int dev, char **buf, int *len) +{ + return RET_ERROR (EIO); +} + +int +DMAbuf_rmchars (int dev, int buff_no, int c) +{ + return RET_ERROR (EIO); +} + +int +DMAbuf_start_output (int dev, int buff_no, int l) +{ + return RET_ERROR (EIO); +} + +int +DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) +{ + return RET_ERROR (EIO); +} + +long +DMAbuf_init (long mem_start) +{ + return mem_start; +} + +int +DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) +{ + return RET_ERROR (EIO); +} + +int +DMAbuf_open_dma (int chan) +{ + return RET_ERROR (ENXIO); +} + +void +DMAbuf_close_dma (int chan) +{ + return; +} + +void +DMAbuf_reset_dma (int chan) +{ + return; +} + +void +DMAbuf_inputintr (int dev) +{ + return; +} + +void +DMAbuf_outputintr (int dev) +{ + return; +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/dsp.c b/sys/i386/isa/sound/dsp.c new file mode 100644 index 0000000000..ce6d71e1c6 --- /dev/null +++ b/sys/i386/isa/sound/dsp.c @@ -0,0 +1,250 @@ +/* + * linux/kernel/chr_drv/sound/dsp.c + * + * Device file manager for /dev/dsp + * + * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further + * details. Should be distributed with this file. + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#ifndef EXCLUDE_AUDIO + +#define ON 1 +#define OFF 0 + +static int wr_buff_no[MAX_DSP_DEV]; /* != -1, if there is a + * incomplete output block */ +static int wr_buff_size[MAX_DSP_DEV], wr_buf_ptr[MAX_DSP_DEV]; +static char *wr_dma_buf[MAX_DSP_DEV]; + +int +dsp_open (int dev, struct fileinfo *file, int bits) +{ + int mode; + int ret; + + dev = dev >> 4; + mode = file->mode & O_ACCMODE; + + if ((ret = DMAbuf_open (dev, mode)) < 0) + return ret; + + if (DMAbuf_ioctl (dev, SNDCTL_DSP_SAMPLESIZE, bits, 1) != bits) + { + dsp_release (dev, file); + return RET_ERROR (ENXIO); + } + + wr_buff_no[dev] = -1; + + return ret; +} + +void +dsp_release (int dev, struct fileinfo *file) +{ + int mode; + + dev = dev >> 4; + mode = file->mode & O_ACCMODE; + + if (wr_buff_no[dev] >= 0) + { + DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_ptr[dev]); + + wr_buff_no[dev] = -1; + } + + DMAbuf_release (dev, mode); +} + + +int +dsp_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + int c, p, l; + int err; + + dev = dev >> 4; + + p = 0; + c = count; + + if (!count) /* Flush output */ + { + if (wr_buff_no[dev] >= 0) + { + DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_ptr[dev]); + + wr_buff_no[dev] = -1; + } + return 0; + } + + while (c) + { /* Perform output blocking */ + if (wr_buff_no[dev] < 0) /* There is no incomplete buffers */ + { + if ((wr_buff_no[dev] = DMAbuf_getwrbuffer (dev, &wr_dma_buf[dev], + &wr_buff_size[dev])) < 0) + return wr_buff_no[dev]; + wr_buf_ptr[dev] = 0; + } + + l = c; + if (l > (wr_buff_size[dev] - wr_buf_ptr[dev])) + l = (wr_buff_size[dev] - wr_buf_ptr[dev]); + + if (!dsp_devs[dev]->copy_from_user) + { /* No device specific copy routine */ + COPY_FROM_USER (&wr_dma_buf[dev][wr_buf_ptr[dev]], buf, p, l); + } + else + dsp_devs[dev]->copy_from_user (dev, + wr_dma_buf[dev], wr_buf_ptr[dev], buf, p, l); + + c -= l; + p += l; + wr_buf_ptr[dev] += l; + + if (wr_buf_ptr[dev] >= wr_buff_size[dev]) + { + if ((err = DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_ptr[dev])) < 0) + return err; + + wr_buff_no[dev] = -1; + } + + } + + return count; +} + + +int +dsp_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + int c, p, l; + char *dmabuf; + int buff_no; + + dev = dev >> 4; + p = 0; + c = count; + + while (c) + { + if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l)) < 0) + return buff_no; + + if (l > c) + l = c; + + /* Insert any local processing here. */ + + COPY_TO_USER (buf, 0, dmabuf, l); + + DMAbuf_rmchars (dev, buff_no, l); + + p += l; + c -= l; + } + + return count - c; +} + +int +dsp_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg) +{ + + dev = dev >> 4; + + switch (cmd) + { + case SNDCTL_DSP_SYNC: + if (wr_buff_no[dev] >= 0) + { + DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_ptr[dev]); + + wr_buff_no[dev] = -1; + } + return DMAbuf_ioctl (dev, cmd, arg, 0); + break; + + case SNDCTL_DSP_POST: + if (wr_buff_no[dev] >= 0) + { + DMAbuf_start_output (dev, wr_buff_no[dev], wr_buf_ptr[dev]); + + wr_buff_no[dev] = -1; + } + return 0; + break; + + case SNDCTL_DSP_RESET: + wr_buff_no[dev] = -1; + return DMAbuf_ioctl (dev, cmd, arg, 0); + break; + + default: + return DMAbuf_ioctl (dev, cmd, arg, 0); + } +} + +long +dsp_init (long mem_start) +{ + return mem_start; +} + +#else +/* Stub version */ +int +dsp_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + return RET_ERROR (EIO); +} + +int +dsp_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + return RET_ERROR (EIO); +} + +int +dsp_open (int dev, struct fileinfo *file, int bits) +{ + return RET_ERROR (ENXIO); +} + +void +dsp_release (int dev, struct fileinfo *file) + { + }; +int +dsp_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg) +{ + return RET_ERROR (EIO); +} + +int +dsp_lseek (int dev, struct fileinfo *file, off_t offset, int orig) +{ + return RET_ERROR (EIO); +} + +long +dsp_init (long mem_start) +{ + return mem_start; +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/finetune.h b/sys/i386/isa/sound/finetune.h new file mode 100644 index 0000000000..07fd3e064f --- /dev/null +++ b/sys/i386/isa/sound/finetune.h @@ -0,0 +1,28 @@ +#ifdef SEQUENCER_C +/* + * Copyright Hannu Savolainen 1993 + * See COPYING for further details. Should be distributed with this file. + */ + + unsigned short finetune_table[128] = + { +/* 0 */ 9439, 9447, 9456, 9464, 9473, 9481, 9490, 9499, +/* 8 */ 9507, 9516, 9524, 9533, 9542, 9550, 9559, 9567, +/* 16 */ 9576, 9585, 9593, 9602, 9611, 9619, 9628, 9637, +/* 24 */ 9645, 9654, 9663, 9672, 9680, 9689, 9698, 9707, +/* 32 */ 9715, 9724, 9733, 9742, 9750, 9759, 9768, 9777, +/* 40 */ 9786, 9795, 9803, 9812, 9821, 9830, 9839, 9848, +/* 48 */ 9857, 9866, 9874, 9883, 9892, 9901, 9910, 9919, +/* 56 */ 9928, 9937, 9946, 9955, 9964, 9973, 9982, 9991, +/* 64 */ 10000, 10009, 10018, 10027, 10036, 10045, 10054, 10063, +/* 72 */ 10072, 10082, 10091, 10100, 10109, 10118, 10127, 10136, +/* 80 */ 10145, 10155, 10164, 10173, 10182, 10191, 10201, 10210, +/* 88 */ 10219, 10228, 10237, 10247, 10256, 10265, 10274, 10284, +/* 96 */ 10293, 10302, 10312, 10321, 10330, 10340, 10349, 10358, +/* 104 */ 10368, 10377, 10386, 10396, 10405, 10415, 10424, 10433, +/* 112 */ 10443, 10452, 10462, 10471, 10481, 10490, 10499, 10509, +/* 120 */ 10518, 10528, 10537, 10547, 10556, 10566, 10576, 10585 + }; +#else + extern unsigned short finetune_table[128]; +#endif diff --git a/sys/i386/isa/sound/gus_card.c b/sys/i386/isa/sound/gus_card.c new file mode 100644 index 0000000000..82fa3a49f3 --- /dev/null +++ b/sys/i386/isa/sound/gus_card.c @@ -0,0 +1,186 @@ + +/* + * linux/kernel/chr_drv/sound/gus_card.c + * + * Detection routine for the Gravis Ultrasound. + * + * (C) 1993 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further + * details. Should be distributed with this file. + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS) + +#include "gus_hw.h" + +void gusintr (int); + +int gus_base, gus_irq, gus_dma; + +static int +set_gus_irq (int interrupt_level) +{ + int retcode; + +#ifdef linux + struct sigaction sa; + + sa.sa_handler = gusintr; + +#ifdef SND_SA_INTERRUPT + sa.sa_flags = SA_INTERRUPT; +#else + sa.sa_flags = 0; +#endif + + sa.sa_mask = 0; + sa.sa_restorer = NULL; + + retcode = irqaction (interrupt_level, &sa); + + if (retcode < 0) + { + printk ("GUS: IRQ%d already in use\n", interrupt_level); + } + +#else + /* # error Unimplemented for this OS */ +#endif + return retcode; +} + +int +gus_set_midi_irq (int interrupt_level) +{ + int retcode; + +#ifdef linux + struct sigaction sa; + + sa.sa_handler = gus_midi_interrupt; + +#ifdef SND_SA_INTERRUPT + sa.sa_flags = SA_INTERRUPT; +#else + sa.sa_flags = 0; +#endif + + sa.sa_mask = 0; + sa.sa_restorer = NULL; + + retcode = irqaction (interrupt_level, &sa); + + if (retcode < 0) + { + printk ("GUS: IRQ%d already in use\n", interrupt_level); + } + +#else + /* # error Unimplemented for this OS */ +#endif + return retcode; +} + +long +attach_gus_card (long mem_start, struct address_info *hw_config) +{ + int io_addr; + + set_gus_irq (hw_config->irq); + + if (gus_wave_detect (hw_config->io_base)) /* Try first the default */ + { + mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma); +#ifndef EXCLUDE_MIDI + mem_start = gus_midi_init (mem_start); +#endif + return mem_start; + } + +#ifndef EXCLUDE_GUS_IODETECT + + /* + * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6) + */ + + for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10) + if (io_addr != hw_config->io_base) /* Already tested */ + if (gus_wave_detect (io_addr)) + { + printk (" WARNING! GUS found at %03x, config was %03x ", io_addr, hw_config->io_base); + mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma); +#ifndef EXCLUDE_MIDI + mem_start = gus_midi_init (mem_start); +#endif + return mem_start; + } + +#endif + + return mem_start; /* Not detected */ +} + +int +probe_gus (struct address_info *hw_config) +{ + int io_addr; + + if (gus_wave_detect (hw_config->io_base)) + return 1; + +#ifndef EXCLUDE_GUS_IODETECT + + /* + * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6) + */ + + for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10) + if (io_addr != hw_config->io_base) /* Already tested */ + if (gus_wave_detect (io_addr)) + return 1; + +#endif + + return 0; +} + +void +gusintr (int unit) +{ + unsigned char src; + unsigned long flags; + + while (1) + { + if (!(src = INB (u_IrqStatus))) + return; + + if (src & DMA_TC_IRQ) + { + guswave_dma_irq (); + } + + if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ)) + { +#ifndef EXCLUDE_MIDI + gus_midi_interrupt (0); +#endif + } + + if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ)) + { + printk ("T"); + gus_write8 (0x45, 0); /* Timer control */ + } + + if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ)) + { + DISABLE_INTR (flags); + gus_voice_irq (); + RESTORE_INTR (flags); + } + } +} + +#endif diff --git a/sys/i386/isa/sound/gus_hw.h b/sys/i386/isa/sound/gus_hw.h new file mode 100644 index 0000000000..48233e7e1e --- /dev/null +++ b/sys/i386/isa/sound/gus_hw.h @@ -0,0 +1,35 @@ + +/* + * I/O addresses + */ + +#define u_Base (gus_base + 0x000) +#define u_Mixer u_Base +#define u_Status (gus_base + 0x006) +#define u_TimerControl (gus_base + 0x008) +#define u_TimerData (gus_base + 0x009) +#define u_IRQDMAControl (gus_base + 0x00b) +#define u_MidiControl (gus_base + 0x100) +#define MIDI_RESET 0x03 +#define MIDI_ENABLE_XMIT 0x20 +#define MIDI_ENABLE_RCV 0x80 +#define u_MidiStatus u_MidiControl +#define MIDI_RCV_FULL 0x01 +#define MIDI_XMIT_EMPTY 0x02 +#define MIDI_FRAME_ERR 0x10 +#define MIDI_OVERRUN 0x20 +#define MIDI_IRQ_PEND 0x80 +#define u_MidiData (gus_base + 0x101) +#define u_Voice (gus_base + 0x102) +#define u_Command (gus_base + 0x103) +#define u_DataLo (gus_base + 0x104) +#define u_DataHi (gus_base + 0x105) +#define u_IrqStatus u_Status +# define MIDI_TX_IRQ 0x01 /* pending MIDI xmit IRQ */ +# define MIDI_RX_IRQ 0x02 /* pending MIDI recv IRQ */ +# define GF1_TIMER1_IRQ 0x04 /* general purpose timer */ +# define GF1_TIMER2_IRQ 0x08 /* general purpose timer */ +# define WAVETABLE_IRQ 0x20 /* pending wavetable IRQ */ +# define ENVELOPE_IRQ 0x40 /* pending volume envelope IRQ */ +# define DMA_TC_IRQ 0x80 /* pending dma tc IRQ */ +#define u_DRAMIO (gus_base + 0x107) diff --git a/sys/i386/isa/sound/gus_midi.c b/sys/i386/isa/sound/gus_midi.c new file mode 100644 index 0000000000..6978eed967 --- /dev/null +++ b/sys/i386/isa/sound/gus_midi.c @@ -0,0 +1,257 @@ +/* + * linux/kernel/chr_drv/sound/gus2_midi.c + * + * The low level driver for the GUS Midi Interface. + * + * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further + * details. Should be distributed with this file. + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#include "gus_hw.h" + +#if !defined(EXCLUDE_GUS) && !defined(EXCLUDE_MIDI) + +static int midi_busy = 0, input_opened = 0; +static int my_dev; +static int output_used = 0; +static volatile unsigned char gus_midi_control; + +static unsigned char tmp_queue[256]; +static volatile int qlen; +static volatile unsigned char qhead, qtail; +extern int gus_base, gus_irq, gus_dma; + +#define GUS_MIDI_STATUS() INB(u_MidiStatus) + +static int +gus_midi_open (int dev, int mode) +{ + + if (midi_busy) + { + printk ("GUS: Midi busy\n"); + return RET_ERROR (EBUSY); + } + + OUTB (MIDI_RESET, u_MidiControl); + gus_delay (); + + gus_midi_control = 0; + input_opened = 0; + + if (mode == OPEN_READ || mode == OPEN_READWRITE) + { + gus_midi_control |= MIDI_ENABLE_RCV; + input_opened = 1; + } + + if (mode == OPEN_WRITE || mode == OPEN_READWRITE) + { + gus_midi_control |= MIDI_ENABLE_XMIT; + } + + OUTB (gus_midi_control, u_MidiControl); /* Enable */ + + midi_busy = 1; + qlen = qhead = qtail = output_used = 0; + + return 0; +} + +static int +dump_to_midi (unsigned char midi_byte) +{ + unsigned long flags; + int ok = 0; + + output_used = 1; + + DISABLE_INTR (flags); + + if (GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY) + { + ok = 1; + OUTB (midi_byte, u_MidiData); + } + else + { + /* Enable Midi xmit interrupts (again) */ + gus_midi_control |= MIDI_ENABLE_XMIT; + OUTB (gus_midi_control, u_MidiControl); + } + + RESTORE_INTR (flags); + return ok; +} + +static void +gus_midi_close (int dev) +{ + /* Reset FIFO pointers, disable intrs */ + + OUTB (MIDI_RESET, u_MidiControl); + midi_busy = 0; +} + +static int +gus_midi_out (int dev, unsigned char midi_byte) +{ + + unsigned long flags; + + /* + * Drain the local queue first + */ + + DISABLE_INTR (flags); + + while (qlen && dump_to_midi (tmp_queue[qhead])) + { + qlen--; + qhead++; + } + + RESTORE_INTR (flags); + + /* + * Output the byte if the local queue is empty. + */ + + if (!qlen) + if (dump_to_midi (midi_byte)) + return 1; /* OK */ + + /* + * Put to the local queue + */ + + if (qlen >= 256) + return 0; /* Local queue full */ + + DISABLE_INTR (flags); + + tmp_queue[qtail] = midi_byte; + qlen++; + qtail++; + + RESTORE_INTR (flags); + + return 1; +} + +static int +gus_midi_start_read (int dev) +{ + return 0; +} + +static int +gus_midi_end_read (int dev) +{ + return 0; +} + +static int +gus_midi_ioctl (int dev, unsigned cmd, unsigned arg) +{ + return RET_ERROR (EINVAL); +} + +static void +gus_midi_kick (int dev) +{ +} + +static int +gus_midi_buffer_status (int dev) +{ + unsigned long flags; + + if (!output_used) + return 0; + + DISABLE_INTR (flags); + + if (qlen && dump_to_midi (tmp_queue[qhead])) + { + qlen--; + qhead++; + } + + RESTORE_INTR (flags); + + return (qlen > 0) | !(GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY); +} + +static struct midi_operations gus_midi_operations = +{ + {"Gravis UltraSound", 0}, + gus_midi_open, + gus_midi_close, + gus_midi_ioctl, + gus_midi_out, + gus_midi_start_read, + gus_midi_end_read, + gus_midi_kick, + NULL, /* command */ + gus_midi_buffer_status +}; + +long +gus_midi_init (long mem_start) +{ + OUTB (MIDI_RESET, u_MidiControl); + + my_dev = num_midis; + midi_devs[num_midis++] = &gus_midi_operations; + return mem_start; +} + +void +gus_midi_interrupt (int dummy) +{ + unsigned char stat, data; + unsigned long flags; + + DISABLE_INTR (flags); + + stat = GUS_MIDI_STATUS (); + + if (stat & MIDI_RCV_FULL) + { + data = INB (u_MidiData); + if (input_opened) + sequencer_midi_input (my_dev, data); + } + + if (stat & MIDI_XMIT_EMPTY) + { + while (qlen && dump_to_midi (tmp_queue[qhead])) + { + qlen--; + qhead++; + } + + if (!qlen) + { + /* Disable Midi output interrupts, since no data in the buffer */ + gus_midi_control &= ~MIDI_ENABLE_XMIT; + OUTB (gus_midi_control, u_MidiControl); + } + } + + if (stat & MIDI_FRAME_ERR) + printk ("Midi framing error\n"); + if (stat & MIDI_OVERRUN && input_opened) + printk ("GUS: Midi input overrun\n"); + + RESTORE_INTR (flags); +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/gus_vol.c b/sys/i386/isa/sound/gus_vol.c new file mode 100644 index 0000000000..b3f1e84060 --- /dev/null +++ b/sys/i386/isa/sound/gus_vol.c @@ -0,0 +1,101 @@ +/* + * gus_vol.c - Compute volume for GUS. + * + * Greg Lee 1993. + */ +#include "sound_config.h" +#ifndef EXCLUDE_GUS + +#define GUS_VOLUME gus_wave_volume + + +extern int gus_wave_volume; + +/* + * Calculate gus volume from note velocity, main volume, expression, and + * intrinsic patch volume given in patch library. Expression is multiplied + * in, so it emphasizes differences in note velocity, while main volume is + * added in -- I don't know whether this is right, but it seems reasonable to + * me. (In the previous stage, main volume controller messages were changed + * to expression controller messages, if they were found to be used for + * dynamic volume adjustments, so here, main volume can be assumed to be + * constant throughout a song.) + * + * Intrinsic patch volume is added in, but if over 64 is also multiplied in, so + * we can give a big boost to very weak voices like nylon guitar and the + * basses. The normal value is 64. Strings are assigned lower values. + */ +unsigned short +gus_adagio_vol (int vel, int mainv, int xpn, int voicev) +{ + int i, m, n, x; + + + /* + * A voice volume of 64 is considered neutral, so adjust the main volume if + * something other than this neutral value was assigned in the patch + * library. + */ + x = 256 + 6 * (voicev - 64); + + /* Boost expression by voice volume above neutral. */ + if (voicev > 65) + xpn += voicev - 64; + xpn += (voicev - 64) / 2; + + /* Combine multiplicative and level components. */ + x = vel * xpn * 6 + (voicev / 4) * x; + +#ifdef GUS_VOLUME + /* + * Further adjustment by installation-specific master volume control + * (default 50). + */ + x = (x * GUS_VOLUME * GUS_VOLUME) / 10000; +#endif + + if (x < (1 << 11)) + return (11 << 8); + else if (x >= 65535) + return ((15 << 8) | 255); + + /* + * Convert to gus's logarithmic form with 4 bit exponent i and 8 bit + * mantissa m. + */ + n = x; + i = 7; + if (n < 128) + { + while (i > 0 && n < (1 << i)) + i--; + } + else + while (n > 255) + { + n >>= 1; + i++; + } + /* + * Mantissa is part of linear volume not expressed in exponent. (This is + * not quite like real logs -- I wonder if it's right.) + */ + m = x - (1 << i); + + /* Adjust mantissa to 8 bits. */ + if (m > 0) + { + if (i > 8) + m >>= i - 8; + else if (i < 8) + m <<= 8 - i; + } + + /* low volumes give occasional sour notes */ + if (i < 11) + return (11 << 8); + + return ((i << 8) + m); +} + +#endif diff --git a/sys/i386/isa/sound/gus_wave.c b/sys/i386/isa/sound/gus_wave.c new file mode 100644 index 0000000000..2ea4569b84 --- /dev/null +++ b/sys/i386/isa/sound/gus_wave.c @@ -0,0 +1,2523 @@ + +/* + * linux/kernel/chr_drv/sound/gus_wave.c + * + * Driver for the Gravis UltraSound wave table synth. + * + * (C) 1993 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further + * details. Should be distributed with this file. + */ + +/* #define GUS_LINEAR_VOLUME */ + +#include "sound_config.h" +#include "ultrasound.h" +#include "gus_hw.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS) + +#define MAX_SAMPLE 256 +#define MAX_PATCH 256 + +struct voice_info + { + unsigned long orig_freq; + unsigned long current_freq; + unsigned long mode; + int bender; + int bender_range; + int panning; + int midi_volume; + unsigned int initial_volume; + unsigned int current_volume; + int loop_irq_mode, loop_irq_parm; +#define LMODE_FINISH 1 +#define LMODE_PCM 2 +#define LMODE_PCM_STOP 3 + int volume_irq_mode, volume_irq_parm; +#define VMODE_HALT 1 +#define VMODE_ENVELOPE 2 + + int env_phase; + unsigned char env_rate[6]; + unsigned char env_offset[6]; + + /* + * Volume computation parameters for gus_adagio_vol() + */ + int main_vol, expression_vol, patch_vol; + + }; + +extern int gus_base; +extern int gus_irq, gus_dma; +extern char *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT]; +extern unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT]; +extern int snd_raw_count[MAX_DSP_DEV]; +static long gus_mem_size = 0; +static long free_mem_ptr = 0; +static int gus_busy = 0; +static int nr_voices = 0; /* Number of currently allowed voices */ +static int gus_devnum = 0; +static int volume_base, volume_scale, volume_method; + +#define VOL_METHOD_ADAGIO 1 +int gus_wave_volume = 60; /* Master wolume for wave (0 to 100) */ +static unsigned char mix_image = 0x00; + +/* + * Current version of this_one driver doesn't allow synth and PCM functions + * at the same time. The active_device specifies the active driver + */ +static int active_device = 0; + +#define GUS_DEV_WAVE 1 /* Wave table synth */ +#define GUS_DEV_PCM_DONE 2 /* PCM device, transfer done */ +#define GUS_DEV_PCM_CONTINUE 3 /* PCM device, transfer the second + * chn */ + +static int gus_sampling_speed; +static int gus_sampling_channels; +static int gus_sampling_bits; + +DEFINE_WAIT_QUEUE (dram_sleeper, dram_sleep_flag); + +/* + * Variables and buffers for PCM output + */ +#define MAX_PCM_BUFFERS 32 /* Don't change */ +static int pcm_bsize, /* Current blocksize */ + pcm_nblk, /* Current # of blocks */ + pcm_banksize; /* # bytes allocated for channels */ +static int pcm_datasize[MAX_PCM_BUFFERS]; /* Actual # of bytes in blk */ +static volatile int pcm_head, pcm_tail, pcm_qlen; /* DRAM queue */ +static volatile int pcm_active; +static int pcm_current_dev; +static int pcm_current_block; +static unsigned long pcm_current_buf; +static int pcm_current_count; +static int pcm_current_intrflag; + +struct voice_info voices[32]; + +static int freq_div_table[] = +{ + 44100, /* 14 */ + 41160, /* 15 */ + 38587, /* 16 */ + 36317, /* 17 */ + 34300, /* 18 */ + 32494, /* 19 */ + 30870, /* 20 */ + 29400, /* 21 */ + 28063, /* 22 */ + 26843, /* 23 */ + 25725, /* 24 */ + 24696, /* 25 */ + 23746, /* 26 */ + 22866, /* 27 */ + 22050, /* 28 */ + 21289, /* 29 */ + 20580, /* 30 */ + 19916, /* 31 */ + 19293 /* 32 */ +}; + +static struct patch_info samples[MAX_SAMPLE + 1]; +static long sample_ptrs[MAX_SAMPLE + 1]; +static int sample_map[32]; +static int free_sample; + + +static int patch_table[MAX_PATCH]; +static int patch_map[32]; + +static struct synth_info gus_info = +{"Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 0, 16, 0, MAX_PATCH}; + +static void gus_poke (long addr, unsigned char data); +static void compute_and_set_volume (int voice, int volume, int ramp_time); +extern unsigned short gus_adagio_vol (int vel, int mainv, int xpn, int voicev); +static void compute_volume (int voice, int volume); + +#define INSTANT_RAMP -1 /* Dont use ramping */ +#define FAST_RAMP 0 /* Fastest possible ramp */ + +static void +reset_sample_memory (void) +{ + int i; + + for (i = 0; i <= MAX_SAMPLE; i++) + sample_ptrs[i] = -1; + for (i = 0; i < 32; i++) + sample_map[i] = -1; + for (i = 0; i < 32; i++) + patch_map[i] = -1; + + gus_poke (0, 0); /* Put silence here */ + gus_poke (1, 0); + + free_mem_ptr = 2; + free_sample = 0; + + for (i = 0; i < MAX_PATCH; i++) + patch_table[i] = -1; +} + +void +gus_delay (void) +{ + int i; + + for (i = 0; i < 7; i++) + INB (u_DRAMIO); +} + +static void +gus_poke (long addr, unsigned char data) +{ + unsigned long flags; + + DISABLE_INTR (flags); + OUTB (0x43, u_Command); + OUTB (addr & 0xff, u_DataLo); + OUTB ((addr >> 8) & 0xff, u_DataHi); + + OUTB (0x44, u_Command); + OUTB ((addr >> 16) & 0xff, u_DataHi); + OUTB (data, u_DRAMIO); + RESTORE_INTR (flags); +} + +static unsigned char +gus_peek (long addr) +{ + unsigned long flags; + unsigned char tmp; + + DISABLE_INTR (flags); + OUTB (0x43, u_Command); + OUTB (addr & 0xff, u_DataLo); + OUTB ((addr >> 8) & 0xff, u_DataHi); + + OUTB (0x44, u_Command); + OUTB ((addr >> 16) & 0xff, u_DataHi); + tmp = INB (u_DRAMIO); + RESTORE_INTR (flags); + + return tmp; +} + +void +gus_write8 (int reg, unsigned char data) +{ + unsigned long flags; + + DISABLE_INTR (flags); + + OUTB (reg, u_Command); + OUTB (data, u_DataHi); + + RESTORE_INTR (flags); +} + +unsigned char +gus_read8 (int reg) +{ + unsigned long flags; + unsigned char val; + + DISABLE_INTR (flags); + OUTB (reg | 0x80, u_Command); + val = INB (u_DataHi); + RESTORE_INTR (flags); + + return val; +} + +unsigned char +gus_look8 (int reg) +{ + unsigned long flags; + unsigned char val; + + DISABLE_INTR (flags); + OUTB (reg, u_Command); + val = INB (u_DataHi); + RESTORE_INTR (flags); + + return val; +} + +void +gus_write16 (int reg, unsigned short data) +{ + unsigned long flags; + + DISABLE_INTR (flags); + + OUTB (reg, u_Command); + + OUTB (data & 0xff, u_DataLo); + OUTB ((data >> 8) & 0xff, u_DataHi); + + RESTORE_INTR (flags); +} + +unsigned short +gus_read16 (int reg) +{ + unsigned long flags; + unsigned char hi, lo; + + DISABLE_INTR (flags); + + OUTB (reg | 0x80, u_Command); + + lo = INB (u_DataLo); + hi = INB (u_DataHi); + + RESTORE_INTR (flags); + + return ((hi << 8) & 0xff00) | lo; +} + +void +gus_write_addr (int reg, unsigned long address, int is16bit) +{ + unsigned long hold_address; + + if (is16bit) + { + /* + * Special processing required for 16 bit patches + */ + + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + + gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff)); + gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff)); +} + +static void +gus_select_voice (int voice) +{ + if (voice < 0 || voice > 31) + return; + + OUTB (voice, u_Voice); +} + +static void +gus_select_max_voices (int nvoices) +{ + if (nvoices < 14) + nvoices = 14; + if (nvoices > 32) + nvoices = 32; + + nr_voices = nvoices; + + gus_write8 (0x0e, (nvoices - 1) | 0xc0); +} + +static void +gus_voice_on (unsigned char mode) +{ + gus_write8 (0x00, mode & 0xfc); + gus_delay (); + gus_write8 (0x00, mode & 0xfc); +} + +static void +gus_voice_off (void) +{ + gus_write8 (0x00, gus_read8 (0x00) | 0x03); +} + +static void +gus_voice_mode (unsigned char mode) +{ + gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); /* Don't start or stop + * voice */ + gus_delay (); + gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); +} + +static void +gus_voice_freq (unsigned long freq) +{ + unsigned long divisor = freq_div_table[nr_voices - 14]; + unsigned short fc; + + fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor); + fc = fc << 1; + + gus_write16 (0x01, fc); +} + +static void +gus_voice_volume (unsigned short vol) +{ + gus_write8 (0x0d, 0x03); /* Stop ramp before setting volume */ + gus_write16 (0x09, vol << 4); +} + +static void +gus_voice_balance (unsigned char balance) +{ + gus_write8 (0x0c, balance); +} + +static void +gus_ramp_range (unsigned short low, unsigned short high) +{ + gus_write8 (0x07, (low >> 4) & 0xff); + gus_write8 (0x08, (high >> 4) & 0xff); +} + +static void +gus_ramp_rate (unsigned char scale, unsigned char rate) +{ + gus_write8 (0x06, ((scale & 0x03) << 6) | (rate & 0x3f)); +} + +static void +gus_rampon (unsigned char mode) +{ + gus_write8 (0x0d, mode & 0xfc); + gus_delay (); + gus_write8 (0x0d, mode & 0xfc); +} + +static void +gus_ramp_mode (unsigned char mode) +{ + gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); /* Don't start or stop + * ramping */ + gus_delay (); + gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); +} + +static void +gus_rampoff (void) +{ + gus_write8 (0x0d, 0x03); +} + +static void +gus_voice_init (int voice) +{ + unsigned long flags; + + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_volume (0); + gus_write_addr (0x0a, 0, 0); /* Set current position to 0 */ + gus_write8 (0x00, 0x03); /* Voice off */ + gus_write8 (0x0d, 0x03); /* Ramping off */ + RESTORE_INTR (flags); + + voices[voice].panning = 0; + voices[voice].mode = 0; + voices[voice].orig_freq = 20000; + voices[voice].current_freq = 20000; + voices[voice].bender = 0; + voices[voice].bender_range = 200; + voices[voice].initial_volume = 0; + voices[voice].current_volume = 0; + voices[voice].loop_irq_mode = 0; + voices[voice].loop_irq_parm = 0; + voices[voice].volume_irq_mode = 0; + voices[voice].volume_irq_parm = 0; + voices[voice].env_phase = 0; + voices[voice].main_vol = 127; + voices[voice].patch_vol = 127; + voices[voice].expression_vol = 127; +} + +static void +step_envelope (int voice) +{ + unsigned vol, prev_vol, phase; + unsigned char rate; + + if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2) + { + gus_rampoff (); + return; /* Sustain */ + } + + if (voices[voice].env_phase >= 5) + { + /* + * Shoot the voice off + */ + + gus_voice_init (voice); + return; + } + + prev_vol = voices[voice].current_volume; + gus_voice_volume (prev_vol); + phase = ++voices[voice].env_phase; + + compute_volume (voice, voices[voice].midi_volume); + + vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255; + rate = voices[voice].env_rate[phase]; + gus_write8 (0x06, rate); /* Ramping rate */ + + voices[voice].volume_irq_mode = VMODE_ENVELOPE; + + if (((vol - prev_vol) / 64) == 0) /* No significant volume change */ + { + step_envelope (voice); /* Continue with the next phase */ + return; + } + + if (vol > prev_vol) + { + if (vol >= (4096 - 64)) + vol = 4096 - 65; + gus_ramp_range (0, vol); + gus_rampon (0x20); /* Increasing, irq */ + } + else + { + if (vol <= 64) + vol = 65; + gus_ramp_range (vol, 4095); + gus_rampon (0x60); /* Decreasing, irq */ + } + voices[voice].current_volume = vol; +} + +static void +init_envelope (int voice) +{ + voices[voice].env_phase = -1; + voices[voice].current_volume = 64; + + step_envelope (voice); +} + +static void +start_release (int voice) +{ + if (gus_read8 (0x00) & 0x03) + return; /* Voice already stopped */ + + voices[voice].env_phase = 2; /* Will be incremented by step_envelope */ + + voices[voice].current_volume = + voices[voice].initial_volume = + gus_read16 (0x09) >> 4; /* Get current volume */ + + voices[voice].mode &= ~WAVE_SUSTAIN_ON; + gus_rampoff (); + step_envelope (voice); +} + +static void +gus_voice_fade (int voice) +{ + int instr_no = sample_map[voice], is16bits; + + if (instr_no < 0 || instr_no > MAX_SAMPLE) + { + gus_write8 (0x00, 0x03); /* Hard stop */ + return; + } + + is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bit samples */ + + if (voices[voice].mode & WAVE_ENVELOPES) + { + start_release (voice); + return; + } + + /* + * Ramp the volume down but not too quickly. + */ + if ((gus_read16 (0x09) >> 4) < 100) /* Get current volume */ + { + gus_voice_off (); + gus_rampoff (); + gus_voice_init (voice); + return; + } + + gus_ramp_range (65, 4095); + gus_ramp_rate (2, 4); + gus_rampon (0x40 | 0x20); /* Down, once, irq */ + voices[voice].volume_irq_mode = VMODE_HALT; +} + +static void +gus_reset (void) +{ + int i; + + gus_select_max_voices (24); + volume_base = 3071; + volume_scale = 4; + volume_method = VOL_METHOD_ADAGIO; + + for (i = 0; i < 32; i++) + { + gus_voice_init (i); /* Turn voice off */ + } + + INB (u_Status); /* Touch the status register */ + + gus_look8 (0x41); /* Clear any pending DMA IRQs */ + gus_look8 (0x49); /* Clear any pending sample IRQs */ + gus_read8 (0x0f); /* Clear pending IRQs */ + +} + +static void +gus_initialize (void) +{ + unsigned long flags; + unsigned char dma_image, irq_image, tmp; + + static unsigned char gus_irq_map[16] = + {0, 0, 1, 3, 0, 2, 0, 4, 0, 0, 0, 5, 6, 0, 0, 7}; + + static unsigned char gus_dma_map[8] = + {0, 1, 0, 2, 0, 3, 4, 5}; + + DISABLE_INTR (flags); + + gus_write8 (0x4c, 0); /* Reset GF1 */ + gus_delay (); + gus_delay (); + + gus_write8 (0x4c, 1); /* Release Reset */ + gus_delay (); + gus_delay (); + + /* + * Clear all interrupts + */ + + gus_write8 (0x41, 0); /* DMA control */ + gus_write8 (0x45, 0); /* Timer control */ + gus_write8 (0x49, 0); /* Sample control */ + + gus_select_max_voices (24); + + INB (u_Status); /* Touch the status register */ + + gus_look8 (0x41); /* Clear any pending DMA IRQs */ + gus_look8 (0x49); /* Clear any pending sample IRQs */ + gus_read8 (0x0f); /* Clear pending IRQs */ + + gus_reset (); /* Resets all voices */ + + gus_look8 (0x41); /* Clear any pending DMA IRQs */ + gus_look8 (0x49); /* Clear any pending sample IRQs */ + gus_read8 (0x0f); /* Clear pending IRQs */ + + gus_write8 (0x4c, 7); /* Master reset | DAC enable | IRQ enable */ + + /* + * Set up for Digital ASIC + */ + + OUTB (0x05, gus_base + 0x0f); + + mix_image |= 0x02; /* Disable line out */ + OUTB (mix_image, u_Mixer); + + OUTB (0x00, u_IRQDMAControl); + + OUTB (0x00, gus_base + 0x0f); + + /* + * Now set up the DMA and IRQ interface + * + * The GUS supports two IRQs and two DMAs. + * + * If GUS_MIDI_IRQ is defined and if it's != GUS_IRQ, separate Midi IRQ is set + * up. Otherwise the same IRQ is shared by the both devices. + * + * Just one DMA channel is used. This prevents simultaneous ADC and DAC. + * Adding this support requires significant changes to the dmabuf.c, dsp.c + * and audio.c also. + */ + + irq_image = 0; + tmp = gus_irq_map[gus_irq]; + if (!tmp) + printk ("Warning! GUS IRQ not selected\n"); + irq_image |= tmp; + + if (GUS_MIDI_IRQ != gus_irq) + { /* The midi irq was defined and != wave irq */ + tmp = gus_irq_map[GUS_MIDI_IRQ]; + tmp <<= 3; + + if (!tmp) + printk ("Warning! GUS Midi IRQ not selected\n"); + else + gus_set_midi_irq (GUS_MIDI_IRQ); + + irq_image |= tmp; + } + else + irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */ + + dma_image = 0x40; /* Combine DMA1 (DRAM) and IRQ2 (ADC) */ + tmp = gus_dma_map[gus_dma]; + if (!tmp) + printk ("Warning! GUS DMA not selected\n"); + dma_image |= tmp; + + /* + * For some reason the IRQ and DMA addresses must be written twice + */ + + /* Doing it first time */ + + OUTB (mix_image, u_Mixer); /* Select DMA control */ + OUTB (dma_image, u_IRQDMAControl); /* Set DMA address */ + + OUTB (mix_image | 0x40, u_Mixer); /* Select IRQ control */ + OUTB (irq_image, u_IRQDMAControl); /* Set IRQ address */ + + /* Doing it second time */ + + OUTB (mix_image, u_Mixer); /* Select DMA control */ + OUTB (dma_image, u_IRQDMAControl); /* Set DMA address */ + + OUTB (mix_image | 0x40, u_Mixer); /* Select IRQ control */ + OUTB (irq_image, u_IRQDMAControl); /* Set IRQ address */ + + gus_select_voice (0); /* This disables writes to IRQ/DMA reg */ + + mix_image &= ~0x02; /* Enable line out */ + mix_image |= 0x08; /* Enable IRQ */ + OUTB (mix_image, u_Mixer); /* Turn mixer channels on */ + + gus_select_voice (0); /* This disables writes to IRQ/DMA reg */ + + gusintr (0); /* Serve pending interrupts */ + RESTORE_INTR (flags); +} + +int +gus_wave_detect (int baseaddr) +{ + gus_base = baseaddr; + + gus_write8 (0x4c, 0); /* Reset GF1 */ + gus_delay (); + gus_delay (); + + gus_write8 (0x4c, 1); /* Release Reset */ + gus_delay (); + gus_delay (); + + gus_poke (0x000, 0xaa); + gus_poke (0x100, 0x55); + + if (gus_peek (0x000) != 0xaa) + return 0; + if (gus_peek (0x100) != 0x55) + return 0; + + gus_mem_size = 0x40000; /* 256k */ + gus_poke (0x40000, 0xaa); + if (gus_peek (0x40000) != 0xaa) + return 1; + + gus_mem_size = 0x80000; /* 512k */ + gus_poke (0x80000, 0xaa); + if (gus_peek (0x80000) != 0xaa) + return 1; + + gus_mem_size = 0xc0000; /* 768k */ + gus_poke (0xc0000, 0xaa); + if (gus_peek (0xc0000) != 0xaa) + return 1; + + gus_mem_size = 0x100000; /* 1M */ + + return 1; +} + +static int +guswave_ioctl (int dev, + unsigned int cmd, unsigned int arg) +{ + + switch (cmd) + { + case SNDCTL_SYNTH_INFO: + gus_info.nr_voices = nr_voices; + IOCTL_TO_USER ((char *) arg, 0, &gus_info, sizeof (gus_info)); + return 0; + break; + + case SNDCTL_SEQ_RESETSAMPLES: + reset_sample_memory (); + return 0; + break; + + case SNDCTL_SEQ_PERCMODE: + return 0; + break; + + case SNDCTL_SYNTH_MEMAVL: + return gus_mem_size - free_mem_ptr - 32; + + default: + return RET_ERROR (EINVAL); + } +} + +static int +guswave_set_instr (int dev, int voice, int instr_no) +{ + int sample_no; + + if (instr_no < 0 || instr_no > MAX_PATCH) + return RET_ERROR (EINVAL); + + if (voice < 0 || voice > 31) + return RET_ERROR (EINVAL); + + sample_no = patch_table[instr_no]; + patch_map[voice] = -1; + + if (sample_no < 0) + { + printk ("GUS: Undefined patch %d for voice %d\n", instr_no, voice); + return RET_ERROR (EINVAL);/* Patch not defined */ + } + + if (sample_ptrs[sample_no] == -1) /* Sample not loaded */ + { + printk ("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice); + return RET_ERROR (EINVAL); + } + + sample_map[voice] = sample_no; + patch_map[voice] = instr_no; + return 0; +} + +static int +guswave_kill_note (int dev, int voice, int velocity) +{ + unsigned long flags; + + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_fade (voice); + RESTORE_INTR (flags); + + return 0; +} + +static void +guswave_aftertouch (int dev, int voice, int pressure) +{ + short lo_limit, hi_limit; + unsigned long flags; + + return; /* Currently disabled */ + + if (voice < 0 || voice > 31) + return; + + if (voices[voice].mode & WAVE_ENVELOPES && voices[voice].env_phase != 2) + return; /* Don't mix with envelopes */ + + if (pressure < 32) + { + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_rampoff (); + compute_and_set_volume (voice, 255, 0); /* Back to original volume */ + RESTORE_INTR (flags); + return; + } + + hi_limit = voices[voice].current_volume; + lo_limit = hi_limit * 99 / 100; + if (lo_limit < 65) + lo_limit = 65; + + DISABLE_INTR (flags); + gus_select_voice (voice); + if (hi_limit > (4095 - 65)) + { + hi_limit = 4095 - 65; + gus_voice_volume (hi_limit); + } + gus_ramp_range (lo_limit, hi_limit); + gus_ramp_rate (3, 8); + gus_rampon (0x58); /* Bidirectional, Down, Loop */ + RESTORE_INTR (flags); +} + +static void +guswave_panning (int dev, int voice, int value) +{ + if (voice >= 0 || voice < 32) + voices[voice].panning = value; +} + +static void +compute_volume (int voice, int volume) +{ + if (volume < 128) + { + voices[voice].midi_volume = volume; + + switch (volume_method) + { + case VOL_METHOD_ADAGIO: + voices[voice].initial_volume = + gus_adagio_vol (volume, voices[voice].main_vol, + voices[voice].expression_vol, + voices[voice].patch_vol); + break; + + default: + voices[voice].initial_volume = volume_base + (volume * volume_scale); + } + } + + if (voices[voice].initial_volume > 4095) + voices[voice].initial_volume = 4095; +} + +static void +compute_and_set_volume (int voice, int volume, int ramp_time) +{ + int current, target, rate; + + compute_volume (voice, volume); + voices[voice].current_volume = voices[voice].initial_volume; + + current = gus_read16 (0x09) >> 4; + target = voices[voice].initial_volume; + + if (ramp_time == INSTANT_RAMP) + { + gus_rampoff (); + gus_voice_volume (target); + return; + } + + if (ramp_time == FAST_RAMP) + rate = 63; + else + rate = 16; + gus_ramp_rate (0, rate); + + if ((target - current) / 64 == 0) /* Too close */ + { + gus_rampoff (); + gus_voice_volume (target); + return; + } + + if (target > current) + { + if (target > (4095 - 65)) + target = 4095 - 65; + gus_ramp_range (current, target); + gus_rampon (0x00); /* Ramp up, once, no irq */ + } + else + { + if (target < 65) + target = 65; + + gus_ramp_range (target, current); + gus_rampon (0x40); /* Ramp down, once, no irq */ + } +} + +static void +dynamic_volume_change (int voice) +{ + unsigned char status; + unsigned long flags; + + DISABLE_INTR (flags); + gus_select_voice (voice); + status = gus_read8 (0x00); /* Voice status */ + RESTORE_INTR (flags); + + if (status & 0x03) + return; /* Voice not started */ + + if (!(voices[voice].mode & WAVE_ENVELOPES)) + { + compute_and_set_volume (voice, voices[voice].midi_volume, 1); + return; + } + + /* + * Voice is running and has envelopes. + */ + + DISABLE_INTR (flags); + gus_select_voice (voice); + status = gus_read8 (0x0d); /* Ramping status */ + RESTORE_INTR (flags); + + if (status & 0x03) /* Sustain phase? */ + { + compute_and_set_volume (voice, voices[voice].midi_volume, 1); + return; + } + + if (voices[voice].env_phase < 0) + return; + + compute_volume (voice, voices[voice].midi_volume); + +#if 0 /* Is this really required */ + voices[voice].current_volume = + gus_read16 (0x09) >> 4; /* Get current volume */ + + voices[voice].env_phase--; + step_envelope (voice); +#endif +} + +static void +guswave_controller (int dev, int voice, int ctrl_num, int value) +{ + unsigned long flags; + unsigned long freq; + + if (voice < 0 || voice > 31) + return; + + switch (ctrl_num) + { + case CTRL_PITCH_BENDER: + voices[voice].bender = value; + freq = compute_finetune (voices[voice].orig_freq, value, voices[voice].bender_range); + voices[voice].current_freq = freq; + + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_freq (freq); + RESTORE_INTR (flags); + break; + + case CTRL_PITCH_BENDER_RANGE: + voices[voice].bender_range = value; + break; + + case CTRL_EXPRESSION: + volume_method = VOL_METHOD_ADAGIO; + voices[voice].expression_vol = value; + dynamic_volume_change (voice); + break; + + case CTRL_MAIN_VOLUME: + volume_method = VOL_METHOD_ADAGIO; + voices[voice].main_vol = value; + dynamic_volume_change (voice); + break; + + default: /* Ignore */ + break; + } +} + +static int +guswave_start_note (int dev, int voice, int note_num, int volume) +{ + int sample, best_sample, best_delta, delta_freq; + int is16bits, samplep, patch, pan; + unsigned long note_freq, base_note, freq, flags; + unsigned char mode = 0; + + if (voice < 0 || voice > 31) + { + printk ("GUS: Invalid voice\n"); + return RET_ERROR (EINVAL); + } + + if (note_num == 255) + { + if (voices[voice].mode & WAVE_ENVELOPES) + { + voices[voice].midi_volume = volume; + dynamic_volume_change (voice); + return 0; + } + + compute_and_set_volume (voice, volume, 1); + return 0; + } + + if ((patch = patch_map[voice]) == -1) + { + return RET_ERROR (EINVAL); + } + + if ((samplep = patch_table[patch]) == -1) + { + return RET_ERROR (EINVAL); + } + + note_freq = note_to_freq (note_num); + + /* + * Find a sample within a patch so that the note_freq is between low_note + * and high_note. + */ + sample = -1; + + best_sample = samplep; + best_delta = 1000000; + while (samplep >= 0 && sample == -1) + { + delta_freq = note_freq - samples[samplep].base_note; + if (delta_freq < 0) + delta_freq = -delta_freq; + if (delta_freq < best_delta) + { + best_sample = samplep; + best_delta = delta_freq; + } + if (samples[samplep].low_note <= note_freq && note_freq <= samples[samplep].high_note) + sample = samplep; + else + samplep = samples[samplep].key; /* Follow link */ + } + if (sample == -1) + sample = best_sample; + + if (sample == -1) + { + printk ("GUS: Patch %d not defined for note %d\n", patch, note_num); + return 0; /* Should play default patch ??? */ + } + + is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bit samples */ + voices[voice].mode = samples[sample].mode; + voices[voice].patch_vol = samples[sample].volume; + + if (voices[voice].mode & WAVE_ENVELOPES) + { + int i; + + for (i = 0; i < 6; i++) + { + voices[voice].env_rate[i] = samples[sample].env_rate[i]; + voices[voice].env_offset[i] = samples[sample].env_offset[i]; + } + } + + sample_map[voice] = sample; + + base_note = samples[sample].base_note / 100; /* To avoid overflows */ + note_freq /= 100; + + freq = samples[sample].base_freq * note_freq / base_note; + + voices[voice].orig_freq = freq; + + /* + * Since the pitch bender may have been set before playing the note, we + * have to calculate the bending now. + */ + + freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range); + voices[voice].current_freq = freq; + + pan = (samples[sample].panning + voices[voice].panning) / 32; + pan += 7; + if (pan < 0) + pan = 0; + if (pan > 15) + pan = 15; + + if (samples[sample].mode & WAVE_16_BITS) + { + mode |= 0x04; /* 16 bits */ + if ((sample_ptrs[sample] >> 18) != + ((sample_ptrs[sample] + samples[sample].len) >> 18)) + printk ("GUS: Sample address error\n"); + } + + /************************************************************************* + * CAUTION! Interrupts disabled. Don't return before enabling + *************************************************************************/ + + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_off (); /* It may still be running */ + gus_rampoff (); + if (voices[voice].mode & WAVE_ENVELOPES) + { + compute_volume (voice, volume); + init_envelope (voice); + } + else + compute_and_set_volume (voice, volume, 0); + + if (samples[sample].mode & WAVE_LOOP_BACK) + gus_write_addr (0x0a, sample_ptrs[sample] + samples[sample].len, is16bits); /* Sample start=end */ + else + gus_write_addr (0x0a, sample_ptrs[sample], is16bits); /* Sample start=begin */ + + if (samples[sample].mode & WAVE_LOOPING) + { + mode |= 0x08; /* Looping on */ + + if (samples[sample].mode & WAVE_BIDIR_LOOP) + mode |= 0x10; /* Bidirectional looping on */ + + if (samples[sample].mode & WAVE_LOOP_BACK) + { + gus_write_addr (0x0a, /* Put the current location = loop_end */ + sample_ptrs[sample] + samples[sample].loop_end, is16bits); + mode |= 0x40; /* Loop backwards */ + } + + gus_write_addr (0x02, sample_ptrs[sample] + samples[sample].loop_start, is16bits); /* Loop start location */ + gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].loop_end, is16bits); /* Loop end location */ + } + else + { + mode |= 0x20; /* Loop irq at the end */ + voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp it down at the + * end */ + voices[voice].loop_irq_parm = 1; + gus_write_addr (0x02, sample_ptrs[sample], is16bits); /* Loop start location */ + gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].len, is16bits); /* Loop end location */ + } + gus_voice_freq (freq); + gus_voice_balance (pan); + gus_voice_on (mode); + RESTORE_INTR (flags); + + return 0; +} + +static void +guswave_reset (int dev) +{ + int i; + + for (i = 0; i < 32; i++) + gus_voice_init (i); +} + +static int +guswave_open (int dev, int mode) +{ + int err; + + if (gus_busy) + return RET_ERROR (EBUSY); + + if ((err = DMAbuf_open_dma (gus_devnum))) + return err; + + gus_busy = 1; + active_device = GUS_DEV_WAVE; + + gus_reset (); + + return 0; +} + +static void +guswave_close (int dev) +{ + gus_busy = 0; + active_device = 0; + gus_reset (); + + DMAbuf_close_dma (gus_devnum); +} + +static int +guswave_load_patch (int dev, int format, snd_rw_buf * addr, + int offs, int count, int pmgr_flag) +{ + struct patch_info patch; + int instr; + + unsigned long blk_size, blk_end, left, src_offs, target; + + if (format != GUS_PATCH) + { + printk ("GUS Error: Invalid patch format (key) 0x%04x\n", format); +#ifndef CPU_I486 + if (format == OBSOLETE_GUS_PATCH) + printk ("GUS Error: obsolete patch format\n"); +#endif + return RET_ERROR (EINVAL); + } + + if (count < sizeof (patch)) + { + printk ("GUS Error: Patch header too short\n"); + return RET_ERROR (EINVAL); + } + + count -= sizeof (patch); + + if (free_sample >= MAX_SAMPLE) + { + printk ("GUS: Sample table full\n"); + return RET_ERROR (ENOSPC); + } + + /* + * Copy the header from user space but ignore the first bytes which have + * been transferred already. + */ + + COPY_FROM_USER (&((char *) &patch)[offs], addr, offs, sizeof (patch) - offs); + + instr = patch.instr_no; + + if (instr < 0 || instr > MAX_PATCH) + { + printk ("GUS: Invalid patch number %d\n", instr); + return RET_ERROR (EINVAL); + } + + if (count < patch.len) + { + printk ("GUS Warning: Patch record too short (%d<%d)\n", + count, patch.len); + patch.len = count; + } + + if (patch.len <= 0 || patch.len > gus_mem_size) + { + printk ("GUS: Invalid sample length %d\n", patch.len); + return RET_ERROR (EINVAL); + } + + if (patch.mode & WAVE_LOOPING) + { + if (patch.loop_start < 0 || patch.loop_start >= patch.len) + { + printk ("GUS: Invalid loop start\n"); + return RET_ERROR (EINVAL); + } + + if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len) + { + printk ("GUS: Invalid loop end\n"); + return RET_ERROR (EINVAL); + } + } + + free_mem_ptr = (free_mem_ptr + 31) & ~31; /* Alignment 32 bytes */ + +#define GUS_BANK_SIZE (256*1024) + + if (patch.mode & WAVE_16_BITS) + { + /* + * 16 bit samples must fit one 256k bank. + */ + if (patch.len >= GUS_BANK_SIZE) + { + printk ("GUS: Sample (16 bit) too long %d\n", patch.len); + return RET_ERROR (ENOSPC); + } + + if ((free_mem_ptr / GUS_BANK_SIZE) != + ((free_mem_ptr + patch.len) / GUS_BANK_SIZE)) + { + unsigned long tmp_mem = /* Align to 256K*N */ + ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE; + + if ((tmp_mem + patch.len) > gus_mem_size) + return RET_ERROR (ENOSPC); + + free_mem_ptr = tmp_mem; /* This leaves unusable memory */ + } + } + + if ((free_mem_ptr + patch.len) > gus_mem_size) + return RET_ERROR (ENOSPC); + + sample_ptrs[free_sample] = free_mem_ptr; + + /* Tremolo is not possible with envelopes */ + + if (patch.mode & WAVE_ENVELOPES) + patch.mode &= ~WAVE_TREMOLO; + + memcpy ((char *) &samples[free_sample], &patch, sizeof (patch)); + + /* + * Link this_one sample to the list of samples for patch 'instr'. + */ + + samples[free_sample].key = patch_table[instr]; + patch_table[instr] = free_sample; + + /* + * Use DMA to transfer the wave data to the DRAM + */ + + left = patch.len; + src_offs = 0; + target = free_mem_ptr; + + while (left) /* Not all moved */ + { + blk_size = sound_buffsizes[gus_devnum]; + if (blk_size > left) + blk_size = left; + + /* + * DMA cannot cross 256k bank boundaries. Check for that. + */ + blk_end = target + blk_size; + + if ((target >> 18) != (blk_end >> 18)) + { /* Have to split the block */ + + blk_end &= ~(256 * 1024 - 1); + blk_size = blk_end - target; + } + +#ifdef GUS_NO_DMA + /* + * For some reason the DMA is not possible. We have to use PIO. + */ + { + long i; + unsigned char data; + + for (i = 0; i < blk_size; i++) + { + GET_BYTE_FROM_USER (data, addr, sizeof (patch) + i); + gus_poke (target + i, data); + } + } +#else /* GUS_NO_DMA */ + { + unsigned long address, hold_address; + unsigned char dma_command; + + /* + * OK, move now. First in and then out. + */ + + COPY_FROM_USER (snd_raw_buf[gus_devnum][0], + addr, sizeof (patch) + src_offs, + blk_size); + + gus_write8 (0x41, 0); /* Disable GF1 DMA */ + DMAbuf_start_dma (gus_devnum, snd_raw_buf_phys[gus_devnum][0], + blk_size, DMA_MODE_WRITE); + + /* + * Set the DRAM address for the wave data + */ + + address = target; + + if (sound_dsp_dmachan[gus_devnum] > 3) + { + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + + gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ + + /* + * Start the DMA transfer + */ + + dma_command = 0x21; /* IRQ enable, DMA start */ + if (patch.mode & WAVE_UNSIGNED) + dma_command |= 0x80; /* Invert MSB */ + if (patch.mode & WAVE_16_BITS) + dma_command |= 0x40; /* 16 bit _DATA_ */ + if (sound_dsp_dmachan[gus_devnum] > 3) + dma_command |= 0x04; /* 16 bit DMA channel */ + + gus_write8 (0x41, dma_command); /* Let's go luteet (=bugs) */ + + /* + * Sleep here until the DRAM DMA done interrupt is served + */ + active_device = GUS_DEV_WAVE; + + INTERRUPTIBLE_SLEEP_ON (dram_sleeper, dram_sleep_flag); + } +#endif /* GUS_NO_DMA */ + + /* + * Now the next part + */ + + left -= blk_size; + src_offs += blk_size; + target += blk_size; + + gus_write8 (0x41, 0); /* Stop DMA */ + } + + free_mem_ptr += patch.len; + + if (!pmgr_flag) + pmgr_inform (dev, PM_E_PATCH_LOADED, instr, free_sample, 0, 0); + free_sample++; + return 0; +} + +static void +guswave_hw_control (int dev, unsigned char *event) +{ + int voice, cmd; + unsigned short p1, p2; + unsigned long plong, flags; + + cmd = event[2]; + voice = event[3]; + p1 = *(unsigned short *) &event[4]; + p2 = *(unsigned short *) &event[6]; + plong = *(unsigned long *) &event[4]; + + switch (cmd) + { + + case _GUS_NUMVOICES: + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_select_max_voices (p1); + RESTORE_INTR (flags); + break; + + case _GUS_VOICESAMPLE: + guswave_set_instr (dev, voice, p1); + break; + + case _GUS_VOICEON: + DISABLE_INTR (flags); + gus_select_voice (voice); + p1 &= ~0x20; /* Disable intr */ + gus_voice_on (p1); + RESTORE_INTR (flags); + break; + + case _GUS_VOICEOFF: + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_off (); + RESTORE_INTR (flags); + break; + + case _GUS_VOICEFADE: + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_fade (voice); + RESTORE_INTR (flags); + break; + + case _GUS_VOICEMODE: + DISABLE_INTR (flags); + gus_select_voice (voice); + p1 &= ~0x20; /* Disable intr */ + gus_voice_mode (p1); + RESTORE_INTR (flags); + break; + + case _GUS_VOICEBALA: + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_balance (p1); + RESTORE_INTR (flags); + break; + + case _GUS_VOICEFREQ: + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_freq (plong); + RESTORE_INTR (flags); + break; + + case _GUS_VOICEVOL: + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_voice_volume (p1); + RESTORE_INTR (flags); + break; + + case _GUS_VOICEVOL2: /* Just update the voice value */ + voices[voice].initial_volume = + voices[voice].current_volume = p1; + break; + + case _GUS_RAMPRANGE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NO-NO */ + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_ramp_range (p1, p2); + RESTORE_INTR (flags); + break; + + case _GUS_RAMPRATE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NO-NO */ + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_ramp_rate (p1, p2); + RESTORE_INTR (flags); + break; + + case _GUS_RAMPMODE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NO-NO */ + DISABLE_INTR (flags); + gus_select_voice (voice); + p1 &= ~0x20; /* Disable intr */ + gus_ramp_mode (p1); + RESTORE_INTR (flags); + break; + + case _GUS_RAMPON: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NO-NO */ + DISABLE_INTR (flags); + gus_select_voice (voice); + p1 &= ~0x20; /* Disable intr */ + gus_rampon (p1); + RESTORE_INTR (flags); + break; + + case _GUS_RAMPOFF: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NO-NO */ + DISABLE_INTR (flags); + gus_select_voice (voice); + gus_rampoff (); + RESTORE_INTR (flags); + break; + + case _GUS_VOLUME_SCALE: + volume_base = p1; + volume_scale = p2; + break; + + default:; + } +} + +static int +gus_sampling_set_speed (int speed) +{ + if (speed <= 0) + return gus_sampling_speed; + + if (speed > 44100) + speed = 44100; + + gus_sampling_speed = speed; + return speed; +} + +static int +gus_sampling_set_channels (int channels) +{ + if (!channels) + return gus_sampling_channels; + if (channels > 2) + channels = 2; + if (channels < 1) + channels = 1; + gus_sampling_channels = channels; + return channels; +} + +static int +gus_sampling_set_bits (int bits) +{ + if (!bits) + return gus_sampling_bits; + + if (bits != 8 && bits != 16) + bits = 8; + + gus_sampling_bits = bits; + return bits; +} + +static int +gus_sampling_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) +{ + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (local) + return gus_sampling_set_speed (arg); + return IOCTL_OUT (arg, gus_sampling_set_speed (IOCTL_IN (arg))); + break; + + case SOUND_PCM_READ_RATE: + if (local) + return gus_sampling_speed; + return IOCTL_OUT (arg, gus_sampling_speed); + break; + + case SNDCTL_DSP_STEREO: + if (local) + return gus_sampling_set_channels (arg + 1) - 1; + return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg) + 1) - 1); + break; + + case SOUND_PCM_WRITE_CHANNELS: + return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg))); + break; + + case SOUND_PCM_READ_CHANNELS: + if (local) + return gus_sampling_channels; + return IOCTL_OUT (arg, gus_sampling_channels); + break; + + case SNDCTL_DSP_SAMPLESIZE: + if (local) + return gus_sampling_set_bits (arg); + return IOCTL_OUT (arg, gus_sampling_set_bits (IOCTL_IN (arg))); + break; + + case SOUND_PCM_READ_BITS: + if (local) + return gus_sampling_bits; + return IOCTL_OUT (arg, gus_sampling_bits); + + case SOUND_PCM_WRITE_FILTER: /* NOT YET IMPLEMENTED */ + return IOCTL_OUT (arg, RET_ERROR (EINVAL)); + break; + + case SOUND_PCM_READ_FILTER: + return IOCTL_OUT (arg, RET_ERROR (EINVAL)); + break; + + default: + return RET_ERROR (EINVAL); + } + return RET_ERROR (EINVAL); +} + +static void +gus_sampling_reset (int dev) +{ +} + +static int +gus_sampling_open (int dev, int mode) +{ +#ifdef GUS_NO_DMA + printk ("GUS: DMA mode not enabled. Device not supported\n"); + return RET_ERROR (ENXIO); +#endif + + if (gus_busy) + return RET_ERROR (EBUSY); + + gus_busy = 1; + active_device = 0; + + gus_reset (); + reset_sample_memory (); + gus_select_max_voices (14); + + gus_sampling_set_bits (8); + gus_sampling_set_channels (1); + gus_sampling_set_speed (DSP_DEFAULT_SPEED); + pcm_active = 0; + + return 0; +} + +static void +gus_sampling_close (int dev) +{ + gus_reset (); + gus_busy = 0; + active_device = 0; +} + +static void +play_next_pcm_block (void) +{ + unsigned long flags; + int speed = gus_sampling_speed; + int this_one, is16bits, chn; + unsigned long dram_loc; + unsigned char mode[2], ramp_mode[2]; + + if (!pcm_qlen) + return; + + this_one = pcm_head; + + for (chn = 0; chn < gus_sampling_channels; chn++) + { + mode[chn] = 0x00; + ramp_mode[chn] = 0x03; /* Ramping and rollover off */ + + if (chn == 0) + { + mode[chn] |= 0x20; /* Loop irq */ + voices[chn].loop_irq_mode = LMODE_PCM; + } + + if (gus_sampling_bits != 8) + { + is16bits = 1; + mode[chn] |= 0x04; /* 16 bit data */ + } + else + is16bits = 0; + + dram_loc = this_one * pcm_bsize; + dram_loc += chn * pcm_banksize; + + if (this_one == (pcm_nblk - 1)) /* Last of the DRAM buffers */ + { + mode[chn] |= 0x08; /* Enable loop */ + ramp_mode[chn] = 0x03;/* Disable rollover */ + } + else + { + if (chn == 0) + ramp_mode[chn] = 0x04; /* Enable rollover bit */ + } + + DISABLE_INTR (flags); + gus_select_voice (chn); + gus_voice_freq (speed); + + if (gus_sampling_channels == 1) + gus_voice_balance (7); /* mono */ + else if (chn == 0) + gus_voice_balance (0); /* left */ + else + gus_voice_balance (15); /* right */ + + if (!pcm_active) /* Voice not started yet */ + { + /* + * The playback was not started yet (or there has been a pause). + * Start the voice (again) and ask for a rollover irq at the end of + * this_one block. If this_one one is last of the buffers, use just + * the normal loop with irq. + */ + + gus_voice_off (); /* It could already be running */ + gus_rampoff (); + gus_voice_volume (4000); + gus_ramp_range (65, 4030); + + gus_write_addr (0x0a, dram_loc, is16bits); /* Starting position */ + gus_write_addr (0x02, chn * pcm_banksize, is16bits); /* Loop start location */ + + if (chn != 0) + gus_write_addr (0x04, pcm_banksize + (pcm_bsize * pcm_nblk), + is16bits); /* Loop end location */ + } + + if (chn == 0) + gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits); /* Loop end location */ + else + mode[chn] |= 0x08; /* Enable loop */ + + if (pcm_datasize[this_one] != pcm_bsize) + { + /* Incomplete block. Possibly the last one. */ + if (chn == 0) + { + mode[chn] &= ~0x08; /* Disable loop */ + mode[chn] |= 0x20;/* Enable loop IRQ */ + voices[0].loop_irq_mode = LMODE_PCM_STOP; + ramp_mode[chn] = 0x03; /* No rollover bit */ + } + else + { + gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits); /* Loop end location */ + mode[chn] &= ~0x08; /* Disable loop */ + } + } + + RESTORE_INTR (flags); + } + + for (chn = 0; chn < gus_sampling_channels; chn++) + { + DISABLE_INTR (flags); + gus_select_voice (chn); + gus_write8 (0x0d, ramp_mode[chn]); + gus_voice_on (mode[chn]); + RESTORE_INTR (flags); + } + + pcm_active = 1; +} + +static void +gus_transfer_output_block (int dev, unsigned long buf, + int total_count, int intrflag, int chn) +{ + /* + * This routine transfers one block of audio data to the DRAM. In mono mode + * it's called just once. When in stereo mode, this_one routine is called + * once for both channels. + * + * The left/mono channel data is transferred to the beginning of dram and the + * right data to the area pointed by gus_page_size. + */ + + int this_one, count; + unsigned long flags; + unsigned char dma_command; + unsigned long address, hold_address; + + DISABLE_INTR (flags); + + count = total_count / gus_sampling_channels; + + if (chn == 0) + { + if (pcm_qlen >= pcm_nblk) + printk ("GUS Warning: PCM buffers out of sync\n"); + + this_one = pcm_current_block = pcm_tail; + pcm_qlen++; + pcm_tail = (pcm_tail + 1) % pcm_nblk; + pcm_datasize[this_one] = count; + } + else + this_one = pcm_current_block; + + gus_write8 (0x41, 0); /* Disable GF1 DMA */ + DMAbuf_start_dma (dev, buf + (chn * count), count, DMA_MODE_WRITE); + + address = this_one * pcm_bsize; + address += chn * pcm_banksize; + + if (sound_dsp_dmachan[dev] > 3) + { + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + + gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ + + dma_command = 0x21; /* IRQ enable, DMA start */ + + if (gus_sampling_bits != 8) + dma_command |= 0x40; /* 16 bit _DATA_ */ + else + dma_command |= 0x80; /* Invert MSB */ + + if (sound_dsp_dmachan[dev] > 3) + dma_command |= 0x04; /* 16 bit DMA channel */ + + gus_write8 (0x41, dma_command); /* Kick on */ + + if (chn == (gus_sampling_channels - 1)) /* Last channel */ + { + /* Last (right or mono) channel data */ + active_device = GUS_DEV_PCM_DONE; + if (!pcm_active && (pcm_qlen > 2 || count < pcm_bsize)) + { + play_next_pcm_block (); + } + } + else /* Left channel data. The right channel is + * transferred after DMA interrupt */ + active_device = GUS_DEV_PCM_CONTINUE; + + RESTORE_INTR (flags); +} + +static void +gus_sampling_output_block (int dev, unsigned long buf, int total_count, int intrflag) +{ + pcm_current_buf = buf; + pcm_current_count = total_count; + pcm_current_intrflag = intrflag; + pcm_current_dev = dev; + gus_transfer_output_block (dev, buf, total_count, intrflag, 0); +} + +static void +gus_sampling_start_input (int dev, unsigned long buf, int count, int intrflag) +{ + unsigned long flags; + unsigned char mode; + + DISABLE_INTR (flags); + + DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); + + mode = 0xa0; /* DMA IRQ enable, invert MSB */ + + if (sound_dsp_dmachan[dev] > 3) + mode |= 0x04; /* 16 bit DMA channel */ + if (gus_sampling_channels > 1) + mode |= 0x02; /* Stereo */ + mode |= 0x01; /* DMA enable */ + + gus_write8 (0x49, mode); + + RESTORE_INTR (flags); +} + +static int +gus_sampling_prepare_for_input (int dev, int bsize, int bcount) +{ + unsigned int rate; + + rate = (9878400 / (gus_sampling_speed + 2)) / 16; + + gus_write8 (0x48, rate & 0xff); /* Set sampling frequency */ + + if (gus_sampling_bits != 8) + { + printk ("GUS Error: 16 bit recording not supported\n"); + return RET_ERROR (EINVAL); + } + + return 0; +} + +static int +gus_sampling_prepare_for_output (int dev, int bsize, int bcount) +{ + int i; + + long mem_ptr, mem_size; + + mem_ptr = 0; + mem_size = gus_mem_size / gus_sampling_channels; + + if (mem_size > (256 * 1024)) + mem_size = 256 * 1024; + + pcm_bsize = bsize / gus_sampling_channels; + pcm_head = pcm_tail = pcm_qlen = 0; + + pcm_nblk = MAX_PCM_BUFFERS; + if ((pcm_bsize * pcm_nblk) > mem_size) + pcm_nblk = mem_size / pcm_bsize; + + for (i = 0; i < pcm_nblk; i++) + pcm_datasize[i] = 0; + + pcm_banksize = pcm_nblk * pcm_bsize; + + if (gus_sampling_bits != 8 && pcm_banksize == (256 * 1024)) + pcm_nblk--; + + return 0; +} + +static int +gus_has_output_drained (int dev) +{ + return !pcm_qlen; +} + +static void +gus_copy_from_user (int dev, char *localbuf, int localoffs, + snd_rw_buf * userbuf, int useroffs, int len) +{ + if (gus_sampling_channels == 1) + COPY_FROM_USER (&localbuf[localoffs], userbuf, useroffs, len); + else if (gus_sampling_bits == 8) + { + int in_left = useroffs; + int in_right = useroffs + 1; + char *out_left, *out_right; + int i; + + len /= 2; + localoffs /= 2; + out_left = &localbuf[localoffs]; + out_right = out_left + pcm_bsize; + + for (i = 0; i < len; i++) + { + GET_BYTE_FROM_USER (*out_left++, userbuf, in_left); + in_left += 2; + GET_BYTE_FROM_USER (*out_right++, userbuf, in_right); + in_right += 2; + } + } + else + { + int in_left = useroffs; + int in_right = useroffs + 1; + short *out_left, *out_right; + int i; + + len /= 4; + localoffs /= 4; + + out_left = (short *) &localbuf[localoffs]; + out_right = out_left + (pcm_bsize / 2); + + for (i = 0; i < len; i++) + { + GET_SHORT_FROM_USER (*out_left++, (short *) userbuf, in_left); + in_left += 2; + GET_SHORT_FROM_USER (*out_right++, (short *) userbuf, in_right); + in_right += 2; + } + } +} + +static struct audio_operations gus_sampling_operations = +{ + "Gravis UltraSound", + gus_sampling_open, /* */ + gus_sampling_close, /* */ + gus_sampling_output_block, /* */ + gus_sampling_start_input, /* */ + gus_sampling_ioctl, /* */ + gus_sampling_prepare_for_input, /* */ + gus_sampling_prepare_for_output, /* */ + gus_sampling_reset, /* */ + gus_sampling_reset, /* halt_xfer */ + gus_has_output_drained, + gus_copy_from_user +}; + +static int +guswave_patchmgr (int dev, struct patmgr_info *rec) +{ + int i, n; + + switch (rec->command) + { + case PM_GET_DEVTYPE: + rec->parm1 = PMTYPE_WAVE; + return 0; + break; + + case PM_GET_NRPGM: + rec->parm1 = MAX_PATCH; + return 0; + break; + + case PM_GET_PGMMAP: + rec->parm1 = MAX_PATCH; + + for (i = 0; i < MAX_PATCH; i++) + { + int ptr = patch_table[i]; + + rec->data.data8[i] = 0; + + while (ptr >= 0 && ptr < free_sample) + { + rec->data.data8[i]++; + ptr = samples[ptr].key; /* Follow link */ + } + } + return 0; + break; + + case PM_GET_PGM_PATCHES: + { + int ptr = patch_table[rec->parm1]; + + n = 0; + + while (ptr >= 0 && ptr < free_sample) + { + rec->data.data32[n++] = ptr; + ptr = samples[ptr].key; /* Follow link */ + } + } + rec->parm1 = n; + return 0; + break; + + case PM_GET_PATCH: + { + int ptr = rec->parm1; + struct patch_info *pat; + + if (ptr < 0 || ptr >= free_sample) + return RET_ERROR (EINVAL); + + memcpy (rec->data.data8, (char *) &samples[ptr], + sizeof (struct patch_info)); + + pat = (struct patch_info *) rec->data.data8; + + pat->key = GUS_PATCH; /* Restore patch type */ + rec->parm1 = sample_ptrs[ptr]; /* DRAM address */ + rec->parm2 = sizeof (struct patch_info); + } + return 0; + break; + + case PM_SET_PATCH: + { + int ptr = rec->parm1; + struct patch_info *pat; + + if (ptr < 0 || ptr >= free_sample) + return RET_ERROR (EINVAL); + + pat = (struct patch_info *) rec->data.data8; + + if (pat->len > samples[ptr].len) /* Cannot expand sample */ + return RET_ERROR (EINVAL); + + pat->key = samples[ptr].key; /* Ensure the link is correct */ + + memcpy ((char *) &samples[ptr], rec->data.data8, + sizeof (struct patch_info)); + + pat->key = GUS_PATCH; + } + return 0; + break; + + case PM_READ_PATCH: /* Returns a block of wave data from the DRAM */ + { + int sample = rec->parm1; + int n; + long offs = rec->parm2; + int l = rec->parm3; + + if (sample < 0 || sample >= free_sample) + return RET_ERROR (EINVAL); + + if (offs < 0 || offs >= samples[sample].len) + return RET_ERROR (EINVAL); /* Invalid offset */ + + n = samples[sample].len - offs; /* Nr of bytes left */ + + if (l > n) + l = n; + + if (l > sizeof (rec->data.data8)) + l = sizeof (rec->data.data8); + + if (l <= 0) + return RET_ERROR (EINVAL); /* Was there a bug? */ + + offs += sample_ptrs[sample]; /* Begin offsess + offset to DRAM */ + + for (n = 0; n < l; n++) + rec->data.data8[n] = gus_peek (offs++); + rec->parm1 = n; /* Nr of bytes copied */ + } + return 0; + break; + + case PM_WRITE_PATCH: /* Writes a block of wave data to the DRAM */ + { + int sample = rec->parm1; + int n; + long offs = rec->parm2; + int l = rec->parm3; + + if (sample < 0 || sample >= free_sample) + return RET_ERROR (EINVAL); + + if (offs < 0 || offs >= samples[sample].len) + return RET_ERROR (EINVAL); /* Invalid offset */ + + n = samples[sample].len - offs; /* Nr of bytes left */ + + if (l > n) + l = n; + + if (l > sizeof (rec->data.data8)) + l = sizeof (rec->data.data8); + + if (l <= 0) + return RET_ERROR (EINVAL); /* Was there a bug? */ + + offs += sample_ptrs[sample]; /* Begin offsess + offset to DRAM */ + + for (n = 0; n < l; n++) + gus_poke (offs++, rec->data.data8[n]); + rec->parm1 = n; /* Nr of bytes copied */ + } + return 0; + break; + + default: + return RET_ERROR (EINVAL); + } +} + +static struct synth_operations guswave_operations = +{ + &gus_info, + SYNTH_TYPE_SAMPLE, + SAMPLE_TYPE_GUS, + guswave_open, + guswave_close, + guswave_ioctl, + guswave_kill_note, + guswave_start_note, + guswave_set_instr, + guswave_reset, + guswave_hw_control, + guswave_load_patch, + guswave_aftertouch, + guswave_controller, + guswave_panning, + guswave_patchmgr +}; + +long +gus_wave_init (long mem_start, int irq, int dma) +{ + printk ("snd4: ", gus_mem_size / 1024); + + if (irq < 0 || irq > 15) + { + printk ("ERROR! Invalid IRQ#%d. GUS Disabled", irq); + return mem_start; + } + + if (dma < 0 || dma > 7) + { + printk ("ERROR! Invalid DMA#%d. GUS Disabled", dma); + return mem_start; + } + + gus_irq = irq; + gus_dma = dma; + + if (num_synths >= MAX_SYNTH_DEV) + printk ("GUS Error: Too many synthesizers\n"); + else + synth_devs[num_synths++] = &guswave_operations; + + reset_sample_memory (); + + gus_initialize (); + + if (num_dspdevs < MAX_DSP_DEV) + { + dsp_devs[gus_devnum = num_dspdevs++] = &gus_sampling_operations; + sound_dsp_dmachan[gus_devnum] = dma; + sound_buffcounts[gus_devnum] = 1; + sound_buffsizes[gus_devnum] = DSP_BUFFSIZE; + sound_dma_automode[gus_devnum] = 0; + } + else + printk ("GUS: Too many PCM devices available\n"); + + return mem_start; +} + +static void +do_loop_irq (int voice) +{ + unsigned char tmp; + int mode, parm; + unsigned long flags; + + DISABLE_INTR (flags); + gus_select_voice (voice); + + tmp = gus_read8 (0x00); + tmp &= ~0x20; /* Disable wave IRQ for this_one voice */ + gus_write8 (0x00, tmp); + + mode = voices[voice].loop_irq_mode; + voices[voice].loop_irq_mode = 0; + parm = voices[voice].loop_irq_parm; + + switch (mode) + { + + case LMODE_FINISH: /* Final loop finished, shoot volume down */ + + if ((gus_read16 (0x09) >> 4) < 100) /* Get current volume */ + { + gus_voice_off (); + gus_rampoff (); + gus_voice_init (voice); + return; + } + gus_ramp_range (65, 4065); + gus_ramp_rate (0, 63); /* Fastest possible rate */ + gus_rampon (0x20 | 0x40); /* Ramp down, once, irq */ + voices[voice].volume_irq_mode = VMODE_HALT; + break; + + case LMODE_PCM_STOP: + pcm_active = 0; /* Requires extensive processing */ + case LMODE_PCM: + { + int orig_qlen = pcm_qlen; + + pcm_qlen--; + pcm_head = (pcm_head + 1) % pcm_nblk; + if (pcm_qlen) + { + play_next_pcm_block (); + } + else + { /* Out of data. Just stop the voice */ + gus_voice_off (); + gus_rampoff (); + pcm_active = 0; + } + + if (orig_qlen == pcm_nblk) + { + DMAbuf_outputintr (gus_devnum); + } + } + break; + + default:; + } + RESTORE_INTR (flags); +} + +static void +do_volume_irq (int voice) +{ + unsigned char tmp; + int mode, parm; + unsigned long flags; + + DISABLE_INTR (flags); + + gus_select_voice (voice); + + tmp = gus_read8 (0x0d); + tmp &= ~0x20; /* Disable volume ramp IRQ */ + gus_write8 (0x0d, tmp); + + mode = voices[voice].volume_irq_mode; + voices[voice].volume_irq_mode = 0; + parm = voices[voice].volume_irq_parm; + + switch (mode) + { + case VMODE_HALT: /* Decay phase finished */ + gus_voice_init (voice); + break; + + case VMODE_ENVELOPE: + gus_rampoff (); + step_envelope (voice); + break; + + default:; + } + + RESTORE_INTR (flags); +} + +void +gus_voice_irq (void) +{ + unsigned long wave_ignore = 0, volume_ignore = 0; + unsigned long voice_bit; + + unsigned char src, voice; + + while (1) + { + src = gus_read8 (0x0f); /* Get source info */ + voice = src & 0x1f; + src &= 0xc0; + + if (src == (0x80 | 0x40)) + return; /* No interrupt */ + + voice_bit = 1 << voice; + + if (!(src & 0x80)) /* Wave IRQ pending */ + if (!(wave_ignore & voice_bit) && voice < nr_voices) /* Not done yet */ + { + wave_ignore |= voice_bit; + do_loop_irq (voice); + } + + if (!(src & 0x40)) /* Volume IRQ pending */ + if (!(volume_ignore & voice_bit) && voice < nr_voices) /* Not done yet */ + { + volume_ignore |= voice_bit; + do_volume_irq (voice); + } + } +} + +void +guswave_dma_irq (void) +{ + unsigned char status; + + status = gus_look8 (0x41); /* Get DMA IRQ Status */ + if (status & 0x40) /* DMA Irq pending */ + switch (active_device) + { + case GUS_DEV_WAVE: + if (dram_sleep_flag) + WAKE_UP (dram_sleeper); + break; + + case GUS_DEV_PCM_CONTINUE: + gus_transfer_output_block (pcm_current_dev, pcm_current_buf, + pcm_current_count, + pcm_current_intrflag, 1); + break; + + case GUS_DEV_PCM_DONE: + if (pcm_qlen < pcm_nblk) + { + DMAbuf_outputintr (gus_devnum); + } + break; + + default:; + } + + status = gus_look8 (0x49); /* Get Sampling IRQ Status */ + if (status & 0x40) /* Sampling Irq pending */ + { + DMAbuf_inputintr (gus_devnum); + } + +} + +#endif diff --git a/sys/i386/isa/sound/gustest/Makefile b/sys/i386/isa/sound/gustest/Makefile new file mode 100644 index 0000000000..d161e5b811 --- /dev/null +++ b/sys/i386/isa/sound/gustest/Makefile @@ -0,0 +1,16 @@ +all: gustest gusload gmod midithru + +gustest: gustest.c + $(CC) -o gustest gustest.c -lm + +gusload: gusload.c + $(CC) -o gusload gusload.c + +gmod: gmod.c + $(CC) -o gmod gmod.c + +midithru: midithru.c + $(CC) -o midithru midithru.c + +clean: + rm -f gusload gustest gmod midithru *.o diff --git a/sys/i386/isa/sound/gustest/Readme b/sys/i386/isa/sound/gustest/Readme new file mode 100644 index 0000000000..7640bf8260 --- /dev/null +++ b/sys/i386/isa/sound/gustest/Readme @@ -0,0 +1,67 @@ +The programs in this directory are more or less incompletely implemented. +I have used them for debugging purposes while developing the driver. + +Files in this directory: + + +../ultrasound.h (sys/ultrasound.h) + This file contains some macros which are similar than + the procedures provided by GUSUNIT.PAS. See gustest.c + for more information. + INSTALL THIS FILE TO YOUR /usr/include/sys !!!!!!!!!!!! + +gusload.c This program can be used to load patches (samples) to + the DRAM of GUS. It understands the format used in the + .pat files shipped with GUS. + + Usage: gusload pgm# patchfile. + or gusload reset #Removes all patches from memory. + + You should load just the patches you will need to play + a Midi file, since the memory capacity of GUS is rather + limited (256k-1M). + + Example: + + gusload 0 acpiano.pat + gusload 1 britepno.pat + gusload 19 church.pat + + This program is not required if the adagio package is + used. It can do the patch uploading itself. + +gmod.c This is a simple module player which demonstrates + programming with GUS. It doesn't try to interpret + most of the effect commands. In fact this program + may interpret the modules incorrectly since I am + not a module player expert. + This version plays .MOD, .STM and .669 modules. + +midithru.c This program reads messages from the Midi interface + and plays the notes with an internal synthesizer + (FM or GUS). The program accepts one argument, the + synthesizer device number. In addition to the note on + and note off messages it interprets also program changes + and channel pressure messages. + If you need an example on programming the /dev/sequencer, + this is a good one. The voice allocation algorithm is + not good so don't look at it. + + NOTE! This program is useful with gmod. Jus load + a module with gmod. Wait until the module has + finished or hit ^C. Now you can play the samples + with the midithru program. + + NOTE2! You need a Midi keyboard to use this program. In + addition the Midi interface of GUS is not supported + yet which means you need also PAS16 or MPU-401. + +pmtest.c +gpatinfo.c ******* For information only ******* + These programs demonstrate the patch manager interface + which will be included to some later driver version. + This interface is not complete in version 1.99.9. + Using pmtest will hang your system sooner or later. + +Hannu Savolainen +hsavolai@cs.helsinki.fi diff --git a/sys/i386/isa/sound/gustest/gmidi.h b/sys/i386/isa/sound/gustest/gmidi.h new file mode 100644 index 0000000000..ab951e7253 --- /dev/null +++ b/sys/i386/isa/sound/gustest/gmidi.h @@ -0,0 +1,131 @@ + char patch_names[][9] = + { + /* 0 */ "acpiano", + /* 1 */ "britepno", + /* 2 */ "synpiano", + /* 3 */ "honktonk", + /* 4 */ "epiano1", + /* 5 */ "epiano2", + /* 6 */ "hrpschrd", + /* 7 */ "clavinet", + /* 8 */ "celeste", + /* 9 */ "glocken", + /* 10 */ "musicbox", + /* 11 */ "vibes", + /* 12 */ "marimba", + /* 13 */ "xylophon", + /* 14 */ "tubebell", + /* 15 */ "santur", + /* 16 */ "homeorg", + /* 17 */ "percorg", + /* 18 */ "rockorg", + /* 19 */ "church", + /* 20 */ "reedorg", + /* 21 */ "accordn", + /* 22 */ "harmonca", + /* 23 */ "concrtna", + /* 24 */ "nyguitar", + /* 25 */ "acguitar", + /* 26 */ "jazzgtr", + /* 27 */ "cleangtr", + /* 28 */ "mutegtr", + /* 29 */ "odguitar", + /* 30 */ "distgtr", + /* 31 */ "gtrharm", + /* 32 */ "acbass", + /* 33 */ "fngrbass", + /* 34 */ "pickbass", + /* 35 */ "fretless", + /* 36 */ "slapbas1", + /* 37 */ "slapbas2", + /* 38 */ "synbass1", + /* 39 */ "synbass2", + /* 40 */ "violin", + /* 41 */ "viola", + /* 42 */ "cello", + /* 43 */ "contraba", + /* 44 */ "marcato", + /* 45 */ "pizzcato", + /* 46 */ "harp", + /* 47 */ "timpani", + /* 48 */ "marcato", + /* 49 */ "slowstr", + /* 50 */ "synstr1", + /* 51 */ "synstr2", + /* 52 */ "choir", + /* 53 */ "doo", + /* 54 */ "voices", + /* 55 */ "orchhit", + /* 56 */ "trumpet", + /* 57 */ "trombone", + /* 58 */ "tuba", + /* 59 */ "mutetrum", + /* 60 */ "frenchrn", + /* 61 */ "hitbrass", + /* 62 */ "synbras1", + /* 63 */ "synbras2", + /* 64 */ "sprnosax", + /* 65 */ "altosax", + /* 66 */ "tenorsax", + /* 67 */ "barisax", + /* 68 */ "oboe", + /* 69 */ "englhorn", + /* 70 */ "bassoon", + /* 71 */ "clarinet", + /* 72 */ "piccolo", + /* 73 */ "flute", + /* 74 */ "recorder", + /* 75 */ "woodflut", + /* 76 */ "bottle", + /* 77 */ "shakazul", + /* 78 */ "whistle", + /* 79 */ "ocarina", + /* 80 */ "sqrwave", + /* 81 */ "sawwave", + /* 82 */ "calliope", + /* 83 */ "chiflead", + /* 84 */ "voxlead", + /* 85 */ "voxlead", + /* 86 */ "lead5th", + /* 87 */ "basslead", + /* 88 */ "fantasia", + /* 89 */ "warmpad", + /* 90 */ "polysyn", + /* 91 */ "ghostie", + /* 92 */ "bowglass", + /* 93 */ "metalpad", + /* 94 */ "halopad", + /* 95 */ "sweeper", + /* 96 */ "aurora", + /* 97 */ "soundtrk", + /* 98 */ "crystal", + /* 99 */ "atmosphr", + /* 100 */ "freshair", + /* 101 */ "unicorn", + /* 102 */ "sweeper", + /* 103 */ "startrak", + /* 104 */ "sitar", + /* 105 */ "banjo", + /* 106 */ "shamisen", + /* 107 */ "koto", + /* 108 */ "kalimba", + /* 109 */ "bagpipes", + /* 110 */ "fiddle", + /* 111 */ "Shannai", + /* 112 */ "carillon", + /* 113 */ "agogo", + /* 114 */ "steeldrm", + /* 115 */ "woodblk", + /* 116 */ "taiko", + /* 117 */ "toms", + /* 118 */ "syntom", + /* 119 */ "revcym", + /* 120 */ "fx-fret", + /* 121 */ "fx-blow", + /* 122 */ "seashore", + /* 123 */ "jungle", + /* 124 */ "telephon", + /* 125 */ "helicptr", + /* 126 */ "applause", + /* 127 */ "ringwhsl" + }; diff --git a/sys/i386/isa/sound/gustest/gmod.c b/sys/i386/isa/sound/gustest/gmod.c new file mode 100644 index 0000000000..d730e26e84 --- /dev/null +++ b/sys/i386/isa/sound/gustest/gmod.c @@ -0,0 +1,1589 @@ +/* + * gmod.c - Module player for GUS and Linux. + * (C) Hannu Savolainen, 1993 + * + * NOTE! This program doesn't try to be a complete module player. + * It's just a too I used while developing the driver. In + * addition it can be used as an example on programming + * the LInux Sound Driver with GUS. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define CMD_ARPEG 0x00 +#define CMD_SLIDEUP 0x01 +#define CMD_SLIDEDOWN 0x02 +#define CMD_SLIDETO 0x03 +#define SLIDE_SIZE 8 +#define CMD_VOLSLIDE 0x0a +#define CMD_JUMP 0x0b +#define CMD_VOLUME 0x0c +#define CMD_BREAK 0x0d +#define CMD_SPEED 0x0f +#define CMD_NOP 0xfe +#define CMD_NONOTE 0xff + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#define MAX_TRACK 8 +#define MAX_PATTERN 128 +#define MAX_POSITION 128 + +struct note_info + { + unsigned char note; + unsigned char vol; + unsigned char sample; + unsigned char command; + short parm1, parm2; + }; + +struct voice_info + { + int sample; + int note; + int volume; + int pitchbender; + + /* Pitch sliding */ + + int slide_pitch; + int slide_goal; + int slide_rate; + + int volslide; + }; + +typedef struct note_info pattern[MAX_TRACK][64]; +int pattern_len[MAX_POSITION]; +int pattern_tempo[MAX_POSITION]; +pattern *pattern_table[MAX_PATTERN]; + +struct voice_info voices[MAX_TRACK]; + +int nr_channels, nr_patterns, songlength; +int tune[MAX_POSITION]; +double tick_duration; + +int period_table[] = +{ + 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453, + 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, + 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113 +}; + +SEQ_DEFINEBUF (2048); + +int seqfd; +int sample_ok[128], sample_vol[128]; +int tmp, gus_dev; +double this_time, next_time; +int ticks_per_division; +double clock_rate; /* HZ */ + +/* + * The function seqbuf_dump() must always be provided + */ + +void play_module (char *name); +int load_module (char *name); +int play_note (int channel, struct note_info *pat); +void lets_play_voice (int channel, struct voice_info *v); + +void +seqbuf_dump () +{ + if (_seqbufptr) + if (write (seqfd, _seqbuf, _seqbufptr) == -1) + { + perror ("write /dev/sequencer"); + exit (-1); + } + _seqbufptr = 0; +} + +void +init_voices () +{ + int i; + + for (i = 0; i < MAX_TRACK; i++) + { + voices[i].sample = 0; + voices[i].note = 0; + voices[i].volume = 64; + + voices[i].slide_pitch = 0; + voices[i].slide_goal = 0; + voices[i].slide_rate = 0; + voices[i].pitchbender = 0; + + voices[i].volslide = 0; + } +} + +int +main (int argc, char *argv[]) +{ + int i, n, j; + struct synth_info info; + + if ((seqfd = open ("/dev/sequencer", O_WRONLY, 0)) == -1) + { + perror ("/dev/sequencer"); + exit (-1); + } + + if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1) + { + perror ("/dev/sequencer"); + exit (-1); + } + + for (i = 0; i < n; i++) + { + info.device = i; + + if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1) + { + perror ("/dev/sequencer"); + exit (-1); + } + + if (info.synth_type == SYNTH_TYPE_SAMPLE + && info.synth_subtype == SAMPLE_TYPE_GUS) + gus_dev = i; + } + + if (gus_dev == -1) + { + fprintf (stderr, "Gravis Ultrasound not detected\n"); + exit (-1); + } + + GUS_NUMVOICES (gus_dev, 14); + + for (i = 1; i < argc; i++) + { + for (j = 0; j < MAX_PATTERN; j++) + pattern_table[j] = NULL; + + if (load_module (argv[i])) + { + tick_duration = 100.0 / clock_rate; + play_module (argv[i]); + } + + } + + SEQ_DUMPBUF (); + close (seqfd); + + exit (0); +} + +unsigned short +intelize (unsigned short v) +{ + return ((v & 0xff) << 8) | ((v >> 8) & 0xff); +} + +unsigned long +intelize4 (unsigned long v) +{ + return + (((v >> 16) & 0xff) << 8) | (((v >> 16) >> 8) & 0xff) | + (((v & 0xff) << 8) | ((v >> 8) & 0xff) << 16); +} + +int +load_stm_module (int mod_fd, char *name) +{ + + struct sample_header + { + char name[12]; + unsigned char instr_disk; + unsigned short reserved1; + unsigned short length; /* In bytes */ + unsigned short loop_start; + unsigned short loop_end; + unsigned char volume; + unsigned char reserved2; + unsigned short C2_speed; + unsigned short reserved3; + + }; + + int i, total_mem; + int sample_ptr; + + int position; + + unsigned char *tune_ptr; /* array 0-127 */ + + char header[1105], sname[21]; + + int nr_samples; /* 16 or 32 samples (or 64 or ???) */ + int slen, npat; + + fprintf (stderr, "Loading .STM module: %s\n", name); + + if (read (mod_fd, header, sizeof (header)) != sizeof (header)) + { + fprintf (stderr, "%s: Short file (header)\n", name); + close (mod_fd); + return 0; + } + + strncpy (sname, header, 20); + + fprintf (stderr, "\nModule: %s - ", sname); + + if (header[28] != 0x1a) + { + fprintf (stderr, "Not a STM module\n"); + close (mod_fd); + return 0; + } + + npat = header[33]; + slen = 0; + tune_ptr = &header[48 + (31 * 32)]; + + for (i = 0; i < 64; i++) + { + tune[i] = tune_ptr[i]; + if (tune[i] < npat) + slen = i; + } + + fprintf (stderr, "Song lenght %d, %d patterns.\n", slen, npat); + + nr_samples = 31; + + sample_ptr = 48 + (31 * 32) + 64 + (npat * 1024); /* Location where the + * first sample is + * stored */ + total_mem = 0; + + for (i = 0; i < 32; i++) + sample_ok[i] = 0; + + for (i = 0; i < nr_samples; i++) + { + int len, loop_start, loop_end, base_freq; + unsigned short loop_flags = 0; + + struct sample_header *sample; + + struct patch_info *patch; + + sample = (struct sample_header *) &header[48 + (i * 32)]; + + len = sample->length; + loop_start = sample->loop_start; + loop_end = sample->loop_end; + base_freq = sample->C2_speed; + + if (strlen (sample->name) > 21) + { + fprintf (stderr, "\nInvalid name for sample #%d\n", i); + close (mod_fd); + return 0; + } + + if (len > 0) + { + int x; + + if (loop_end > len) + loop_end = 1; + else if (loop_end < loop_start) + { + loop_start = 0; + loop_end = 0; + } + else + loop_flags = WAVE_LOOPING; + + total_mem += len; + patch = (struct patch_info *) malloc (sizeof (*patch) + len); + + patch->key = GUS_PATCH; + patch->device_no = gus_dev; + patch->instr_no = i; + patch->mode = loop_flags; + patch->len = len; + patch->loop_start = loop_start; + patch->loop_end = loop_end; + patch->base_freq = base_freq; + patch->base_note = 261630; /* Mid C */ + patch->low_note = 0; + patch->high_note = 0x7fffffff; + patch->volume = 120; + + if (lseek (mod_fd, sample_ptr, 0) == -1) + { + perror (name); + close (mod_fd); + free (patch); + return 0; + } + + sample_ptr += len; + + if ((x = read (mod_fd, patch->data, len)) != len) + { + fprintf (stderr, "Short file (sample at %d (%d!=%d)\n", sample_ptr, x, len); + close (mod_fd); + free (patch); + return 0; + } + + fprintf (stderr, "Sample %02d: %05d, %05d, %05d, %07d %s\n", + i, + len, + loop_start, + loop_end, + base_freq, + sample->name); + + if (write (seqfd, patch, sizeof (*patch) + len) == -1) + { + perror ("ioctl /dev/sequencer"); + exit (-1); + } + else + sample_ok[i] = 1; + + free (patch); + } + } + + nr_patterns = slen; + songlength = slen; + nr_channels = 4; + + for (position = 0; position < npat; position++) + { + unsigned char patterns[64][4][4]; + int pat, channel, x; + + int pp = 1104 + (position * 1024); + + if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL) + { + fprintf (stderr, "Can't allocate memory for a pattern\n"); + return 0; + } + + if (lseek (mod_fd, pp, 0) == -1) + { + perror (name); + close (mod_fd); + return 0; + } + + if ((x = read (mod_fd, patterns, 1024)) != 1024) + { + fprintf (stderr, "Short file (pattern at %d), %d!=%d\n", pp, x, 1024); + close (mod_fd); + return 0; + } + + for (pat = 0; pat < 64; pat++) + { + + for (channel = 0; channel < 4; channel++) + { + unsigned char *p; + + unsigned vol, note, octave, sample, effect, params; + + p = &patterns[pat][channel][0]; + + if (p[0] < 251) + { + note = p[0] & 15; + octave = p[0] / 16; + + note = 48 + octave * 12 + note; + + sample = p[1] / 8; + vol = (p[1] & 7) + (p[2] / 2); + effect = p[2] & 0xF; + params = p[3]; + } + else + { + note = 0; + octave = 0; + + sample = 0; + vol = 0; + effect = CMD_NONOTE; + params = 0; + } + + (*pattern_table[position])[channel][pat].note = note; + (*pattern_table[position])[channel][pat].sample = sample; + (*pattern_table[position])[channel][pat].command = effect; + (*pattern_table[position])[channel][pat].parm1 = params; + (*pattern_table[position])[channel][pat].parm2 = 0; + (*pattern_table[position])[channel][pat].vol = vol; + } + + } + + } + + close (mod_fd); + return 1; +} + +int +load_669_module (int mod_fd, char *name) +{ + struct sample_header + { + char name[13]; + unsigned long length; /* In bytes */ + unsigned long loop_start; + unsigned long loop_end; + }; + + int i, total_mem; + int sample_ptr; + + int position; + + unsigned char *tune_ptr, *len_ptr, *tempo_ptr; /* array 0-127 */ + + char header[1084]; + char msg[110]; + + int nr_samples; /* 16 or 32 samples */ + int slen, npat; + + clock_rate = 25.0; + + fprintf (stderr, "Loading .669 module: %s\n", name); + + if (read (mod_fd, header, sizeof (header)) != sizeof (header)) + { + fprintf (stderr, "%s: Short file (header)\n", name); + close (mod_fd); + return 0; + } + + if (*(unsigned short *) &header[0] != 0x6669) + { + fprintf (stderr, "Not a 669 file\n"); + close (mod_fd); + return 0; + } + + strncpy (msg, &header[2], 108); + + for (i = 0; i < strlen (msg); i++) + if ((msg[i] >= ' ' && msg[i] <= 'z') || msg[i] == '\n') + printf ("%c", msg[i]); + printf ("\n"); + + npat = header[0x6f]; + + tune_ptr = &header[0x71]; + + for (slen = 0; slen < 128 && tune_ptr[slen] != 0xff; slen++); + slen--; + + for (i = 0; i < slen; i++) + tune[i] = tune_ptr[i]; + + len_ptr = &header[0x171]; + for (i = 0; i < slen; i++) + pattern_len[i] = len_ptr[i] - 1; + + tempo_ptr = &header[0xf1]; + for (i = 0; i < slen; i++) + pattern_tempo[i] = tempo_ptr[i]; + + nr_samples = header[0x6e]; + + fprintf (stderr, "Song lenght %d, %d patterns, %d samples.\n", slen, npat, nr_samples); + + sample_ptr = 0x1f1 + (nr_samples * 0x19) + (npat * 0x600); /* Location where the + * first sample is + * stored */ + total_mem = 0; + + for (i = 0; i < 64; i++) + sample_ok[i] = 0; + + for (i = 0; i < nr_samples; i++) + { + int len, loop_start, loop_end; + unsigned short loop_flags = 0; + + struct sample_header *sample; + char sname[14]; + + struct patch_info *patch; + + sample = (struct sample_header *) &header[0x1f1 + (i * 0x19)]; + + len = *(unsigned long *) &sample->name[13]; + loop_start = *(unsigned long *) &sample->name[17]; + loop_end = *(unsigned long *) &sample->name[21]; + if (loop_end > len) + loop_end = 1; + else if (loop_end == len) + loop_end--; + + if (loop_end < loop_start) + { + loop_start = 0; + loop_end = 0; + } + + strncpy (sname, sample->name, 13); + + if (len > 0 && len < 200000) + { + total_mem += len; + + fprintf (stderr, "Sample %02d: %05d, %05d, %05d %s\n", + i, + len, + loop_start, + loop_end, + sname); + + patch = (struct patch_info *) malloc (sizeof (*patch) + len); + + if (loop_end == 0) + loop_end = 1; + if (loop_end >= len) + loop_end = 1; + + if (loop_end > 1) loop_flags = WAVE_LOOPING; + + patch->key = GUS_PATCH; + patch->device_no = gus_dev; + patch->instr_no = i; + patch->mode = WAVE_UNSIGNED | loop_flags; + patch->len = len; + patch->loop_start = loop_start; + patch->loop_end = loop_end; + patch->base_freq = 8448; + patch->base_note = 261630; + patch->low_note = 1000; + patch->high_note = 0x7fffffff; + patch->volume = 120; + + if (lseek (mod_fd, sample_ptr, 0) == -1) + { + fprintf (stderr, "Seek failed\n"); + perror (name); + close (mod_fd); + free (patch); + return 0; + } + + sample_ptr += len; + + if (read (mod_fd, patch->data, len) != len) + { + fprintf (stderr, "Short file (sample at %d)\n", sample_ptr); + close (mod_fd); + free (patch); + return 0; + } + + if (write (seqfd, patch, sizeof (*patch) + len) == -1) + { + perror ("ioctl /dev/sequencer"); + /* exit (-1); */ + } + else + sample_ok[i] = 1; + + free (patch); + } + } + + nr_patterns = slen; + songlength = slen; + nr_channels = 8; + + for (position = 0; position < npat; position++) + { + unsigned char patterns[0x600]; + int pat, channel, x; + + int pp = 0x1f1 + (nr_samples * 0x19) + (position * 0x600); + + if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL) + { + fprintf (stderr, "Can't allocate memory for a pattern\n"); + return 0; + } + + + if (lseek (mod_fd, pp, 0) == -1) + { + perror (name); + close (mod_fd); + return 0; + } + + if ((x = read (mod_fd, patterns, 1024)) != 1024) + { + fprintf (stderr, "Short file (pattern at %d) %d!=1024\n", pp, x); + close (mod_fd); + return 0; + } + + for (pat = 0; pat < 64; pat++) + { + + for (channel = 0; channel < 8; channel++) + { + unsigned char *p; + + unsigned vol, period, sample, effect, params; + + p = &patterns[pat * 24 + channel * 3]; + + if (p[0] >= 0xfe || + (p[0] == 0xff && p[1] == 0xff && p[2] == 0xff) || + (p[0] == 0 && p[1] == 0 && p[2] == 0) || + *(int *) p == -1) + { + period = 0; + effect = CMD_NONOTE; + sample = 0; + vol = 0; + params = 0; + + if (p[0] == 0) + { + effect = CMD_BREAK; + params = -2; + } + } + else + { + period = (p[0] >> 2) + 48; + effect = (p[2] >> 4); + params = p[2] & 0x0f; + vol = p[1] & 0x0f; + + if (p[2] == 0xfe) + { + effect = CMD_VOLUME; + params = vol; + } + else if (p[2] == 0xff) + { + effect = CMD_NOP; + } + else + switch (effect) + { + case 0: /* a - Portamento up */ + effect = CMD_SLIDEUP; + break; + + case 1: /* b - Portamento down */ + effect = CMD_SLIDEDOWN; + break; + + case 2: /* c - Port to note */ + effect = CMD_SLIDETO; + break; + + case 3: /* d - Frequency adjust */ + effect = CMD_NOP; /* To be implemented */ + break; + + case 4: /* e - Frequency vibrato */ + effect = CMD_NOP; /* To be implemented */ + break; + + case 5: /* f - Set tempo */ + effect = CMD_SPEED; + break; + + default: + effect = CMD_NOP; + } + + sample = (((p[0] << 4) & 0x30) | ((p[1] >> 4) & 0x0f)) + 1; + } + + (*pattern_table[position])[channel][pat].note = period; + (*pattern_table[position])[channel][pat].sample = sample; + (*pattern_table[position])[channel][pat].command = effect; + (*pattern_table[position])[channel][pat].parm1 = params; + (*pattern_table[position])[channel][pat].parm2 = 0; + (*pattern_table[position])[channel][pat].vol = vol; + } + + } + + } + + close (mod_fd); + return 1; +} + +int +load_mmd0_module (int mod_fd, char *name) +{ + + struct sample_header + { + unsigned short loop_start; + unsigned short loop_end; + unsigned char midich; + unsigned char midipreset; + unsigned char volume; + unsigned char strans; + }; + + int i, total_mem; + int sample_ptr; + + int position; + + unsigned char *tune_ptr; /* array 0-127 */ + + char header[1105]; + + int nr_samples; /* 16 or 32 samples (or 64 or ???) */ + int slen, npat; + + fprintf (stderr, "Loading .MED module: %s\n", name); + + if (read (mod_fd, header, sizeof (header)) != sizeof (header)) + { + fprintf (stderr, "%s: Short file (header)\n", name); + close (mod_fd); + return 0; + } + + if (strncmp (header, "MMD0", 4)) + { + fprintf (stderr, "Not a MED module\n"); + close (mod_fd); + return 0; + } + + printf ("Module len %d\n", intelize4 (*(long *) &header[4])); + printf ("Song info %d\n", intelize4 (*(long *) &header[8])); + printf ("Song len %d\n", intelize4 (*(long *) &header[12])); + printf ("Blockarr %x\n", intelize4 (*(long *) &header[16])); + printf ("Blockarr len %d\n", intelize4 (*(long *) &header[20])); + printf ("Sample array %x\n", intelize4 (*(long *) &header[24])); + printf ("Sample array len %d\n", intelize4 (*(long *) &header[28])); + printf ("Exp data %x\n", intelize4 (*(long *) &header[32])); + printf ("Exp size %d\n", intelize4 (*(long *) &header[36])); + printf ("Pstate %d\n", intelize (*(long *) &header[40])); + printf ("Pblock %d\n", intelize (*(long *) &header[42])); + + return 0; + + npat = header[33]; + slen = 0; + tune_ptr = &header[48 + (31 * 32)]; + + for (i = 0; i < 64; i++) + { + tune[i] = tune_ptr[i]; + if (tune[i] < npat) + slen = i; + } + + fprintf (stderr, "Song lenght %d, %d patterns.\n", slen, npat); + + nr_samples = 31; + + sample_ptr = 48 + (31 * 32) + 64 + (npat * 1024); /* Location where the + * first sample is + * stored */ + total_mem = 0; + + for (i = 0; i < 32; i++) + sample_ok[i] = 0; + + for (i = 0; i < nr_samples; i++) + { + int len, loop_start, loop_end, base_freq; + unsigned short loop_flags = 0; + + struct sample_header *sample; + + struct patch_info *patch; + + sample = (struct sample_header *) &header[48 + (i * 32)]; + + /* + * len = sample->length; loop_start = sample->loop_start; loop_end = + * sample->loop_end; base_freq = sample->C2_speed; + * + * if (strlen (sample->name) > 21) { fprintf (stderr, "\nInvalid name for + * sample #%d\n", i); close (mod_fd); return 0; } + */ + if (len > 0) + { + int x; + + if (loop_end > len) + loop_end = 1; + + if (loop_end < loop_start) + { + loop_start = 0; + loop_end = 0; + } + + if (loop_end > 2) loop_flags = WAVE_LOOPING; + + total_mem += len; + patch = (struct patch_info *) malloc (sizeof (*patch) + len); + + patch->key = GUS_PATCH; + patch->device_no = gus_dev; + patch->instr_no = i; + patch->mode = loop_flags; + patch->len = len; + patch->loop_start = loop_start; + patch->loop_end = loop_end; + patch->base_freq = base_freq; + patch->base_note = 261630; /* Mid C */ + patch->low_note = 0; + patch->high_note = 0x7fffffff; + patch->volume = 120; + + if (lseek (mod_fd, sample_ptr, 0) == -1) + { + perror (name); + close (mod_fd); + free (patch); + return 0; + } + + sample_ptr += len; + + if ((x = read (mod_fd, patch->data, len)) != len) + { + fprintf (stderr, "Short file (sample at %d (%d!=%d)\n", sample_ptr, x, len); + close (mod_fd); + free (patch); + return 0; + } + /* + * fprintf (stderr, "Sample %02d: %05d, %05d, %05d, %07d %s\n", i, + * len, loop_start, loop_end, base_freq, sample->name); + */ + if (write (seqfd, patch, sizeof (*patch) + len) == -1) + { + perror ("ioctl /dev/sequencer"); + exit (-1); + } + else + sample_ok[i] = 1; + + free (patch); + } + } + + nr_patterns = slen; + songlength = slen; + nr_channels = 4; + + for (position = 0; position < npat; position++) + { + unsigned char patterns[64][4][4]; + int pat, channel, x; + + int pp = 1104 + (position * 1024); + + if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL) + { + fprintf (stderr, "Can't allocate memory for a pattern\n"); + return 0; + } + + if (lseek (mod_fd, pp, 0) == -1) + { + perror (name); + close (mod_fd); + return 0; + } + + if ((x = read (mod_fd, patterns, 1024)) != 1024) + { + fprintf (stderr, "Short file (pattern at %d), %d!=%d\n", pp, x, 1024); + close (mod_fd); + return 0; + } + + for (pat = 0; pat < 64; pat++) + { + + for (channel = 0; channel < 4; channel++) + { + unsigned char *p; + + unsigned vol, note, octave, sample, effect, params; + + p = &patterns[pat][channel][0]; + + if (p[0] < 251) + { + note = p[0] & 15; + octave = p[0] / 16; + + note = 48 + octave * 12 + note; + + sample = p[1] / 8; + vol = (p[1] & 7) + (p[2] / 2); + effect = p[2] & 0xF; + params = p[3]; + } + else + { + note = 0; + octave = 0; + + sample = 0; + vol = 0; + effect = CMD_NONOTE; + params = 0; + } + + (*pattern_table[position])[channel][pat].note = note; + (*pattern_table[position])[channel][pat].sample = sample; + (*pattern_table[position])[channel][pat].command = effect; + (*pattern_table[position])[channel][pat].parm1 = params; + (*pattern_table[position])[channel][pat].parm2 = 0; + (*pattern_table[position])[channel][pat].vol = vol; + } + + } + + } + + close (mod_fd); + return 1; +} + +int +load_module (char *name) +{ + + struct sample_header + { + char name[22]; + unsigned short length; /* In words */ + + unsigned char finetune; + unsigned char volume; + + unsigned short repeat_point; /* In words */ + unsigned short repeat_length; /* In words */ + }; + + int i, mod_fd, total_mem; + int sample_ptr, pattern_loc; + + int position; + + unsigned char *tune_ptr; /* array 0-127 */ + + char header[1084]; + + int nr_samples; /* 16 or 32 samples */ + int slen, npat; + char mname[23]; + + ioctl (seqfd, SNDCTL_SEQ_SYNC, 0); + ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev); + + clock_rate = 50.0; + + for (i = 0; i < MAX_POSITION; i++) + pattern_len[i] = 64; + + for (i = 0; i < MAX_POSITION; i++) + pattern_tempo[i] = 0; + + if ((mod_fd = open (name, O_RDONLY, 0)) == -1) + { + perror (name); + return 0; + } + + if (read (mod_fd, header, sizeof (header)) != sizeof (header)) + { + fprintf (stderr, "%s: Short file (header)\n", name); + close (mod_fd); + return 0; + } + + if (lseek (mod_fd, 0, 0) == -1) + { + perror (name); + close (mod_fd); + return 0; + } + + if (header[28] == 0x1a) + return load_stm_module (mod_fd, name); + + if (*(unsigned short *) &header[0] == 0x6669) + return load_669_module (mod_fd, name); + + if (!strncmp (header, "MMD0", 4)) + return load_mmd0_module (mod_fd, name); + + fprintf (stderr, "Loading .MOD module: %s\n", name); + + strncpy (mname, header, 22); + fprintf (stderr, "\nModule: %s - ", mname); + + if (!strncmp (&header[1080], "M.K.", 4) || !strncmp (&header[1080], "FLT8", 4)) + { + fprintf (stderr, "31 samples\n"); + nr_samples = 31; + } + else + { + fprintf (stderr, "15 samples\n"); + nr_samples = 15; + } + + if (nr_samples == 31) + { + sample_ptr = pattern_loc = 1084; + slen = header[950]; + tune_ptr = (unsigned char *) &header[952]; + } + else + { + sample_ptr = pattern_loc = 600; + slen = header[470]; + tune_ptr = (unsigned char *) &header[472]; + } + + npat = 0; + for (i = 0; i < 128; i++) + { + tune[i] = tune_ptr[i]; + + if (tune_ptr[i] > npat) + npat = tune_ptr[i]; + } + npat++; + + fprintf (stderr, "Song lenght %d, %d patterns.\n", slen, npat); + + sample_ptr += (npat * 1024); /* Location where the first sample is stored */ + total_mem = 0; + + for (i = 0; i < 32; i++) + sample_ok[i] = 0; + + for (i = 0; i < nr_samples; i++) + { + int len, loop_start, loop_end; + unsigned short loop_flags = 0; + char pname[22]; + + struct sample_header *sample; + + struct patch_info *patch; + + sample = (struct sample_header *) &header[20 + (i * 30)]; + + len = intelize (sample->length) * 2; + loop_start = intelize (sample->repeat_point) * 2; + loop_end = loop_start + (intelize (sample->repeat_length) * 2); + + if (loop_start > len) + loop_start = 0; + if (loop_end > len) + loop_end = len; + + if (loop_end <= loop_start) + loop_end = loop_start + 1; + + if (loop_end > 2 && loop_end > loop_start) + loop_flags = WAVE_LOOPING; + + strncpy (pname, sample->name, 20); + + if (len > 0) + { + fprintf (stderr, "Sample %02d: L%05d, S%05d, E%05d V%02d %s\n", + i, + len, + loop_start, + loop_end, + sample->volume, + pname); + + total_mem += len; + + patch = (struct patch_info *) malloc (sizeof (*patch) + len); + + patch->key = GUS_PATCH; + patch->device_no = gus_dev; + patch->instr_no = i; + patch->mode = loop_flags; + patch->len = len; + patch->loop_start = loop_start; + patch->loop_end = loop_end; + patch->base_note = 261630; /* Middle C */ + patch->base_freq = 8448; + patch->low_note = 0; + patch->high_note = 20000000; + patch->volume = 120; + patch->panning = 0; + + if (lseek (mod_fd, sample_ptr, 0) == -1) + { + perror (name); + close (mod_fd); + free (patch); + return 0; + } + + sample_ptr += len; + + if (read (mod_fd, patch->data, len) != len) + { + fprintf (stderr, "Short file (sample) %d\n", sample_ptr); + close (mod_fd); + free (patch); + return 0; + } + + SEQ_WRPATCH (patch, sizeof (*patch) + len); + + sample_ok[i] = 1; + if (sample->volume == 0) sample->volume = 64; + sample_vol[i] = sample->volume; + + free (patch); + } + } + + nr_patterns = npat; + songlength = slen; + nr_channels = 4; + + for (position = 0; position < npat; position++) + { + unsigned char patterns[64][4][4]; + int pat, channel; + + int pp = pattern_loc + (position * 1024); + + if (lseek (mod_fd, pp, 0) == -1) + { + perror (name); + close (mod_fd); + return 0; + } + + if (read (mod_fd, patterns, 1024) != 1024) + { + fprintf (stderr, "Short file (pattern %d) %d\n", tune[position], pp); + close (mod_fd); + return 0; + } + + if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL) + { + fprintf (stderr, "Can't allocate memory for a pattern\n"); + return 0; + } + + for (pat = 0; pat < 64; pat++) + { + for (channel = 0; channel < 4; channel++) + { + unsigned short tmp; + unsigned char *p; + + unsigned period, sample, effect, params, note, vol; + + p = &patterns[pat][channel][0]; + + tmp = (p[0] << 8) | p[1]; + sample = (tmp >> 8) & 0x10; + period = + MIN (tmp & 0xFFF, 1023); + tmp = (p[2] << 8) | p[3]; + sample |= tmp >> 12; + effect = (tmp >> 8) & 0xF; + params = tmp & 0xFF; + + note = 0; + + if (period) + { + /* + * Convert period to a Midi note number + */ + + for (note = 0; note < 37 && period != period_table[note]; note++); + if (note >= 37) + note = 0; + + note += 48; + } + + vol = 64; + + if (sample) + if (effect == 0xc) + { + vol = params; + } + else + vol = sample_vol[sample - 1]; + + vol *= 2; + if (vol>64)vol--; + + (*pattern_table[position])[channel][pat].note = note; + (*pattern_table[position])[channel][pat].sample = sample; + (*pattern_table[position])[channel][pat].command = effect; + (*pattern_table[position])[channel][pat].parm1 = params; + (*pattern_table[position])[channel][pat].parm2 = 0; + (*pattern_table[position])[channel][pat].vol = vol; + } + } + } + + close (mod_fd); + return 1; +} + +int +panning (int ch) +{ + static int panning_tab[] = + {-110, 110, 110, -110}; + + return panning_tab[ch % 4]; +} + +void +set_speed (int parm) +{ + if (!parm) + parm = 1; + + if (parm < 32) + { + ticks_per_division = parm; + } + else + { + tick_duration = (60.0 / parm) * 10.0; + } + +} + +void +play_module (char *name) +{ + int i, position, jump_to_pos; + + init_voices (); + + SEQ_START_TIMER (); +#if 1 + for (i=0;i<32;i++) + { + SEQ_EXPRESSION(gus_dev, i, 127); + SEQ_MAIN_VOLUME(gus_dev, i, 100); + } +#endif + next_time = 0.0; + + set_speed (6); + + for (position = 0; position < songlength; position++) + { + int tick, pattern, channel, pos, go_to; + + pos = tune[position]; + if (pattern_tempo[position]) + set_speed (pattern_tempo[position]); + + jump_to_pos = -1; + for (pattern = 0; pattern < pattern_len[position] && jump_to_pos == -1; pattern++) + { + this_time = 0.0; + + for (channel = 0; channel < nr_channels; channel++) + { + if ((go_to = play_note (channel, &(*pattern_table[pos])[channel][pattern])) != -1) + jump_to_pos = go_to; + + } + + next_time += tick_duration; + + for (tick = 1; tick < ticks_per_division; tick++) + { + for (channel = 0; channel < nr_channels; channel++) + lets_play_voice (channel, &voices[channel]); + next_time += tick_duration; + } + + } + + if (jump_to_pos >= 0) + position = jump_to_pos; + } + + SEQ_WAIT_TIME ((int) next_time + 200); /* Wait extra 2 secs */ + + for (i = 0; i < nr_channels; i++) + SEQ_STOP_NOTE (gus_dev, i, 0, 127); + SEQ_DUMPBUF (); + + for (i = 0; i < nr_patterns; i++) + free (pattern_table[i]); +} + +void +sync_time () +{ + if (next_time > this_time) + { + SEQ_WAIT_TIME ((long) next_time); + this_time = next_time; + } +} + +void +set_volslide (int channel, struct note_info *pat) +{ + int n; + + voices[channel].volslide = 0; + + if ((n = (pat->parm1 & 0xf0) >> 4)) + voices[channel].volslide = n; + else + voices[channel].volslide = pat->parm1 & 0xf; +} + +void +set_slideto (int channel, struct note_info *pat) +{ + int size, rate, dir, range = 200; + + rate = pat->parm1; + size = voices[channel].note - pat->note; + if (!size) + return; + + if (size < 0) + { + size *= -1; + dir = -1; + } + else + dir = 1; + + if (size > 2) + { + range = size * 100; + rate = rate * size / 200; + } + + rate = pat->parm1 * dir / 30; + if (!rate) + rate = 1; + + voices[channel].slide_pitch = 1; + voices[channel].slide_goal = (dir * 8192 * 200 * 2 / size) / range; + voices[channel].pitchbender = 0; + voices[channel].slide_rate = rate; + SEQ_BENDER_RANGE (gus_dev, channel, range); +} + +int +play_note (int channel, struct note_info *pat) +{ + int jump = -1; + int sample; + + if (pat->sample == 0x3f) + pat->sample = 0; + + if (pat->command == CMD_NONOTE) + return -1; /* Undefined */ + + sample = pat->sample; + + if (sample && !pat->note) + { + pat->note = voices[channel].note; + } + + if (sample) + voices[channel].sample = sample; + else + sample = voices[channel].sample; + + sample--; + + if (pat->note && pat->command != 3) /* Have a note -> play */ + { + if (sample < 0) + sample = voices[channel].sample - 1; + + if (!sample_ok[sample]) + sample = voices[channel].sample - 1; + + if (sample < 0) + sample = 0; + + if (sample_ok[sample]) + { + sync_time (); + + if (pat->vol > 127) pat->vol=127; + SEQ_SET_PATCH (gus_dev, channel, sample); + SEQ_PANNING (gus_dev, channel, panning (channel)); + SEQ_PITCHBEND (gus_dev, channel, 0); + SEQ_START_NOTE (gus_dev, channel, pat->note, pat->vol); + + voices[channel].volume = pat->vol; + voices[channel].note = pat->note; + voices[channel].slide_pitch = 0; + } + else + SEQ_STOP_NOTE (gus_dev, channel, pat->note, pat->vol); + } + + switch (pat->command) + { + + case CMD_NOP:; + break; + + case CMD_JUMP: + jump = pat->parm1; + break; + + case CMD_BREAK: + jump = -2; + break; + + case CMD_SPEED: + set_speed (pat->parm1); + break; + + case CMD_SLIDEUP: + voices[channel].slide_pitch = 1; + voices[channel].slide_goal = 8191; + voices[channel].pitchbender = 0; + voices[channel].slide_rate = pat->parm1 * SLIDE_SIZE; + SEQ_BENDER_RANGE (gus_dev, channel, 200); + break; + + case CMD_SLIDEDOWN: + voices[channel].slide_pitch = 1; + voices[channel].slide_goal = -8192; + voices[channel].pitchbender = 0; + voices[channel].slide_rate = -pat->parm1 * SLIDE_SIZE; + SEQ_BENDER_RANGE (gus_dev, channel, 200); + break; + + case CMD_SLIDETO: + set_slideto (channel, pat); + break; + + case CMD_VOLUME: + { + int vol = pat->parm1*2; + if (vol>127) vol=127; + if (pat->note && pat->command != 3) + break; + SEQ_START_NOTE (gus_dev, channel, 255, vol); + } + break; + + case CMD_ARPEG: + break; + + case 0x0e: + /* printf ("Cmd 0xE%02x\n", pat->parm1); */ + break; + + case CMD_VOLSLIDE: + set_slideto (channel, pat); + break; + + default: + /* printf ("Command %x %02x\n", pat->command, pat->parm1); */ + } + + return jump; +} + +void +lets_play_voice (int channel, struct voice_info *v) +{ + if (v->slide_pitch) + { + v->pitchbender += v->slide_rate; + if (v->slide_goal < 0) + { + if (v->pitchbender <= v->slide_goal) + { + v->pitchbender = v->slide_goal; + v->slide_pitch = 0; /* Stop */ + } + } + else + { + if (v->pitchbender >= v->slide_goal) + { + v->pitchbender = v->slide_goal; + v->slide_pitch = 0; /* Stop */ + } + } + + sync_time (); + SEQ_PITCHBEND (gus_dev, channel, v->pitchbender); + } + + if (v->volslide) + { + v->volume += v->volslide; + sync_time (); + + if (v->volume > 127) v->volume = 127; + SEQ_START_NOTE (gus_dev, channel, 255, v->volume); + } +} diff --git a/sys/i386/isa/sound/gustest/gpatinfo.c b/sys/i386/isa/sound/gustest/gpatinfo.c new file mode 100644 index 0000000000..3e21fc5cf2 --- /dev/null +++ b/sys/i386/isa/sound/gustest/gpatinfo.c @@ -0,0 +1,176 @@ +/* + * gpatinfo.c: This program demonstrates the patch management + * interface of the GUS driver. + * + * NOTE! The patch manager interface is highly device dependent, + * currently incompletely implemented prototype and + * will change before final implementation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "gmidi.h" + +#define GUS_DEV gus_dev + +#define patch_access(cmd, rec) \ + rec.command = cmd;\ + rec.device = gus_dev;\ + if (ioctl(seqfd, SNDCTL_PMGR_IFACE, &rec)==-1)\ + {\ + perror("/dev/sequencer(SNDCTL_PMGR_IFACE/" #cmd ")");\ + exit(-1);\ + } + +SEQ_DEFINEBUF (2048); + +int seqfd; + +int gus_dev = -1; + +/* + * The function seqbuf_dump() must always be provided + */ + +void +seqbuf_dump () +{ + if (_seqbufptr) + if (write (seqfd, _seqbuf, _seqbufptr) == -1) + { + perror ("write /dev/sequencer"); + exit (-1); + } + _seqbufptr = 0; +} + +int +main (int argc, char *argv[]) +{ + int i, j, n; + struct synth_info info; + struct patch_info *patch; + struct patmgr_info mgr, mgr2, mgr3; + + if ((seqfd = open ("/dev/sequencer", O_WRONLY, 0)) == -1) + { + perror ("/dev/sequencer"); + exit (-1); + } + + if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1) + { + perror ("/dev/sequencer"); + exit (-1); + } + +/* + * First locate the GUS device + */ + + for (i = 0; i < n; i++) + { + info.device = i; + + if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1) + { + perror ("/dev/sequencer"); + exit (-1); + } + + if (info.synth_type == SYNTH_TYPE_SAMPLE + && info.synth_subtype == SAMPLE_TYPE_GUS) + gus_dev = i; + } + + if (gus_dev == -1) + { + fprintf (stderr, "Error: Gravis Ultrasound not detected\n"); + exit (-1); + } + + printf("Gravis UltraSound device = %d\n", gus_dev); + + /* + * Get type of the Patch Manager interface of the GUS device + */ + + patch_access(PM_GET_DEVTYPE, mgr); + printf("Patch manager type: %d\n", mgr.parm1); + + if (mgr.parm1 != PMTYPE_WAVE) + { + fprintf(stderr, "Hups, this program seems to be obsolete\n"); + exit(-1); + } + + /* + * The GUS driver supports up to 256 different midi program numbers but + * this limit can be changed before compiling the driver. The following + * call returns the value compiled to the driver. + */ + + patch_access(PM_GET_PGMMAP, mgr); + printf("Device supports %d midi programs.\n", mgr.parm1); + + /* + * Each program can be undefined or it may have one or more patches. + * A patch consists of header and the waveform data. If there is more + * than one patch in a program, the right one is selected by checking the + * note number when the program is played. + * + * The following call reads an array indexed by program number. Each + * element defines the number of patches defined for the corresponding + * program. + */ + printf("Loaded programs:\n"); + + for (i=0;ilen); + + } + } + + i = gus_dev; + + if (ioctl(seqfd, SNDCTL_SYNTH_MEMAVL, &i)==-1) exit(-1); + printf("%d bytes of DRAM available for wave data\n", i); + + + exit(0); +} diff --git a/sys/i386/isa/sound/gustest/gusload.c b/sys/i386/isa/sound/gustest/gusload.c new file mode 100644 index 0000000000..216150ad48 --- /dev/null +++ b/sys/i386/isa/sound/gustest/gusload.c @@ -0,0 +1,350 @@ +/* + * patutil.c - A sample program which loads patches to the Gravis + * Ultrasound + * + */ + +#ifndef PATCH_PATH +#define PATCH_PATH "/D/ultrasnd/midi" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "gmidi.h" + +struct pat_header + { + char magic[12]; + char version[10]; + char description[60]; + unsigned char instruments; + char voices; + char channels; + unsigned short nr_waveforms; + unsigned short master_volume; + unsigned long data_size; + }; + +struct sample_header + { + char name[7]; + unsigned char fractions; + long len; + long loop_start; + long loop_end; + unsigned short base_freq; + long low_note; + long high_note; + long base_note; + short detune; + unsigned char panning; + + unsigned char envelope_rate[6]; + unsigned char envelope_offset[6]; + + unsigned char tremolo_sweep; + unsigned char tremolo_rate; + unsigned char tremolo_depth; + + unsigned char vibrato_sweep; + unsigned char vibrato_rate; + unsigned char vibrato_depth; + + char modes; + + short scale_frequency; + unsigned short scale_factor; + }; + +#define GUS_DEV gus_dev + +SEQ_DEFINEBUF (2048); + +int seqfd; + +int gus_dev = -1; + +struct patch_info *patch; + +/* + * The function seqbuf_dump() must always be provided + */ + +void +seqbuf_dump () +{ + if (_seqbufptr) + if (write (seqfd, _seqbuf, _seqbufptr) == -1) + { + perror ("write /dev/sequencer"); + exit (-1); + } + _seqbufptr = 0; +} + +int +main (int argc, char *argv[]) +{ + int i, n, patfd, pgm, print_only = 0; + struct synth_info info; + struct pat_header header; + struct sample_header sample; + char buf[256]; + char name[256]; + long offset; + + if ((seqfd = open ("/dev/sequencer", O_WRONLY, 0)) == -1) + { + perror ("/dev/sequencer"); + exit (-1); + } + + if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1) + { + perror ("/dev/sequencer"); + exit (-1); + } + + + for (i = 0; i < n; i++) + { + info.device = i; + + if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1) + { + perror ("/dev/sequencer"); + exit (-1); + } + + if (info.synth_type == SYNTH_TYPE_SAMPLE + && info.synth_subtype == SAMPLE_TYPE_GUS) + gus_dev = i; + } + + if (gus_dev == -1) + { + fprintf (stderr, "Error: Gravis Ultrasound not detected\n"); + exit (-1); + } + + if (argc == 2) + { + if (!strcmp (argv[1], "reset")) + if (ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1) + perror ("Sample reset"); + exit (0); + } + + if (argc != 3) + { + fprintf (stderr, "Usage: %s pgm# patchfile\n", argv[0]); + fprintf (stderr, " or : %s pgm# GM\n", argv[0]); + fprintf (stderr, " or : %s pgm# -l\n", argv[0]); + fprintf (stderr, " or : %s reset\n", argv[0]); + fprintf (stderr, " or : %s -l patchfile\n", argv[0]); + exit (-1); + } + + pgm = atoi (argv[1]); + strcpy (name, argv[2]); + + if (strcmp (name, "GM") == 0 || strcmp(name, "-l")==0) + { + if (strcmp (name, "-l") == 0) print_only = 1; + if (pgm < 0 || pgm > 127) + { + fprintf (stderr, "pgm# must be between 0 and 127\n"); + exit (-1); + } + + sprintf (name, PATCH_PATH "/%s.pat", patch_names[pgm]); + + if (!print_only) + fprintf (stderr, "Loading program %d from %s\n", pgm, name); + } + else if (strcmp (argv[1], "-l") == 0) + print_only = 1; + + if ((patfd = open (name, O_RDONLY, 0)) == -1) + { + perror (name); + exit (-1); + } + + if (read (patfd, buf, 0xef) != 0xef) + { + fprintf (stderr, "%s: Short file\n", name); + exit (-1); + } + + memcpy ((char *) &header, buf, sizeof (header)); + + if (strncmp (header.magic, "GF1PATCH110", 12)) + { + fprintf (stderr, "%s: Not a patch file\n", name); + exit (-1); + } + + if (strncmp (header.version, "ID#000002", 10)) + { + fprintf (stderr, "%s: Incompatible patch file version\n", name); + exit (-1); + } + + header.nr_waveforms = *(unsigned short *) &buf[85]; + header.master_volume = *(unsigned short *) &buf[87]; + + if (print_only) + { + printf ("Patch file: %s contains %d samples\n\n", name, header.nr_waveforms); + printf ("Master volume: %d\n", header.master_volume); + } + + offset = 0xef; + + for (i = 0; i < header.nr_waveforms; i++) + { + if (lseek (patfd, offset, 0) == -1) + { + perror (name); + exit (-1); + } + + if (read (patfd, &buf, sizeof (sample)) != sizeof (sample)) + { + fprintf (stderr, "%s: Short file\n", name); + exit (-1); + } + + memcpy ((char *) &sample, buf, sizeof (sample)); + + /* + * Since some fields of the patch record are not 32bit aligned, we must + * handle them specially. + */ + sample.low_note = *(long *) &buf[22]; + sample.high_note = *(long *) &buf[26]; + sample.base_note = *(long *) &buf[30]; + sample.detune = *(short *) &buf[34]; + sample.panning = (unsigned char) buf[36]; + + memcpy (sample.envelope_rate, &buf[37], 6); + memcpy (sample.envelope_offset, &buf[43], 6); + + sample.tremolo_sweep = (unsigned char) buf[49]; + sample.tremolo_rate = (unsigned char) buf[50]; + sample.tremolo_depth = (unsigned char) buf[51]; + + sample.vibrato_sweep = (unsigned char) buf[52]; + sample.vibrato_rate = (unsigned char) buf[53]; + sample.vibrato_depth = (unsigned char) buf[54]; + sample.modes = (unsigned char) buf[55]; + sample.scale_frequency = *(short *) &buf[56]; + sample.scale_factor = *(unsigned short *) &buf[58]; + + if (print_only) + { + printf("\nSample: %03d / %s\n", i, sample.name); + printf ("Len: %d, Loop start: %d, Loop end: %d\n", sample.len, sample.loop_start, sample.loop_end); + printf ("Flags: "); + if (sample.modes & WAVE_16_BITS) + printf ("16 bit "); + if (sample.modes & WAVE_UNSIGNED) + printf ("unsigned "); + if (sample.modes & WAVE_LOOP_BACK) + printf("reverse "); + if (sample.modes & WAVE_BIDIR_LOOP) + printf("bidir "); + if (sample.modes & WAVE_LOOPING) + printf ("looping "); else printf("one_shot" ); + if (sample.modes & WAVE_SUSTAIN_ON) + printf ("sustain "); + if (sample.modes & WAVE_ENVELOPES) + printf ("enveloped "); + printf ("\n"); + + if (sample.modes & WAVE_ENVELOPES) + { + int i; + + printf ("Envelope info: "); + for (i = 0; i < 6; i++) + { + printf ("%d/%d ", sample.envelope_rate[i], + sample.envelope_offset[i]); + } + printf ("\n"); + } + + printf("Tremolo: sweep=%d, rate=%d, depth=%d\n", + sample.tremolo_sweep, + sample.tremolo_rate, + sample.tremolo_depth); + + printf("Vibrato: sweep=%d, rate=%d, depth=%d\n", + sample.vibrato_sweep, + sample.vibrato_rate, + sample.vibrato_depth); + } + + offset = offset + 96; + patch = (struct patch_info *) malloc (sizeof (*patch) + sample.len); + + patch->key = GUS_PATCH; + patch->device_no = GUS_DEV; + patch->instr_no = pgm; + patch->mode = sample.modes | WAVE_TREMOLO | + WAVE_VIBRATO | WAVE_SCALE; + patch->len = sample.len; + patch->loop_start = sample.loop_start; + patch->loop_end = sample.loop_end; + patch->base_note = sample.base_note; + patch->high_note = sample.high_note; + patch->low_note = sample.low_note; + patch->base_freq = sample.base_freq; + patch->detuning = sample.detune; + patch->panning = (sample.panning - 7) * 16; + + memcpy (patch->env_rate, sample.envelope_rate, 6); + memcpy (patch->env_offset, sample.envelope_offset, 6); + + patch->tremolo_sweep = sample.tremolo_sweep; + patch->tremolo_rate = sample.tremolo_rate; + patch->tremolo_depth = sample.tremolo_depth; + + patch->vibrato_sweep = sample.vibrato_sweep; + patch->vibrato_rate = sample.vibrato_rate; + patch->vibrato_depth = sample.vibrato_depth; + + patch->scale_frequency = sample.scale_frequency; + patch->scale_factor = sample.scale_factor; + + patch->volume = header.master_volume; + + if (lseek (patfd, offset, 0) == -1) + { + perror (name); + exit (-1); + } + + if (!print_only) + { + if (read (patfd, patch->data, sample.len) != sample.len) + { + fprintf (stderr, "%s: Short file\n", name); + exit (-1); + } + + SEQ_WRPATCH (patch, sizeof (*patch) + sample.len); + } + + offset = offset + sample.len; + } + + exit (0); +} diff --git a/sys/i386/isa/sound/gustest/midithru.c b/sys/i386/isa/sound/gustest/midithru.c new file mode 100644 index 0000000000..36e1e07207 --- /dev/null +++ b/sys/i386/isa/sound/gustest/midithru.c @@ -0,0 +1,325 @@ +#include +#include +#include +#include +#include +#include + +SEQ_DEFINEBUF (2048); +SEQ_PM_DEFINES; + +int seqfd, dev = 0; +unsigned char buf[100]; +int bufp; + +/* LRU list for free operators */ + +unsigned char free_list[256]; +int fhead=0, ftail=0, flen=0; + +/* LRU list for still playing notes */ + +unsigned char note_list[256]; +int nhead=0, ntail=0, nlen=0; +unsigned char oper_note[32]; + +int pgm = 0; +int num_voices; +int bender = 0; /* Initially off */ + +void +seqbuf_dump () +{ + if (_seqbufptr) + if (write (seqfd, _seqbuf, _seqbufptr) == -1) + { + perror ("write /dev/sequencer"); + exit (-1); + } + _seqbufptr = 0; +} + +void +stop_note(int note, int velocity) +{ + int i, op; + + op=255; + + for (i=0;i255) + { +#if 0 + fprintf(stderr, "Note list overflow %d, %d, %d\n", + nlen, nhead, ntail); +#endif + nlen=0; /* Overflow -> hard reset */ + } + nlen++; + ntail = (ntail+1) % 256; + + oper_note[free] = note; + + SEQ_SET_PATCH(dev, free, pgm); + SEQ_PITCHBEND(dev, free, bender); + SEQ_START_NOTE(dev, free, note, velocity); + SEQ_DUMPBUF(); +} + +void +channel_pressure(int ch, int pressure) +{ + int i; + + for (i=0;i> 7); */ + pitch_bender(ch, value); + bufp=1; + break; + + case 0xc0: /* Pgm change */ + if (bufp < 2) break; + /* printf("Pgm change %d %d\n", ch, buf[1]); */ + pgm = buf[1]; + if (PM_LOAD_PATCH(dev, 0, pgm) < 0) + if (errno != ESRCH) /* No such process */ + perror("PM_LOAD_PATCH"); + bufp=0; + break; + + case 0xd0: /* Channel pressure */ + if (bufp < 2) break; + /* printf("Channel pressure %d %d\n", ch, buf[1]); */ + channel_pressure(ch, buf[1]); + bufp=1; + break; + + default: + bufp=0; + } +} + +int +main (int argc, char *argv[]) +{ + int i, n, max_voice = 999; + + struct synth_info info; + + unsigned char ev[4], *p; + + if (argc >= 2) dev = atoi(argv[1]); + + for (i=0;i<16;i++) oper_note[i] = 255; + + if ((seqfd = open ("/dev/sequencer", O_RDWR, 0)) == -1) + { + perror ("open /dev/sequencer"); + exit (-1); + } + + if (argc >= 3) + { + int d = dev; + ioctl(seqfd, SNDCTL_FM_4OP_ENABLE, &d); + } + + info.device = dev; + + if (ioctl(seqfd, SNDCTL_SYNTH_INFO, &info)==-1) + { + perror ("info /dev/sequencer"); + exit (-1); + } + + num_voices = info.nr_voices; + if (num_voices>max_voice)num_voices = max_voice; + fprintf(stderr, "Output to synth device %d (%s)\n", dev, info.name); + fprintf(stderr, "%d voices available\n", num_voices); + + for (i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include "gmidi.h" + +#ifndef PATCH_PATH +#define PATCH_PATH "/D/ultrasnd/midi" +#endif + +char loadmap[256] = +{0}; /* 1 if the patch is already loaded */ + +struct pat_header + { + char magic[12]; + char version[10]; + char description[60]; + unsigned char instruments; + char voices; + char channels; + unsigned short nr_waveforms; + unsigned short master_volume; + unsigned long data_size; + }; + +struct sample_header + { + char name[7]; + unsigned char fractions; + long len; + long loop_start; + long loop_end; + unsigned short base_freq; + long low_note; + long high_note; + long base_note; + short detune; + unsigned char panning; + + unsigned char envelope_rate[6]; + unsigned char envelope_offset[6]; + + unsigned char tremolo_sweep; + unsigned char tremolo_rate; + unsigned char tremolo_depth; + + unsigned char vibrato_sweep; + unsigned char vibrato_rate; + unsigned char vibrato_depth; + + char modes; + + short scale_frequency; + unsigned short scale_factor; + }; +int seqfd = 0, gus_dev = -1; + +struct patch_info *patch; + +int +do_load_patch (struct patmgr_info *rec) +{ + int i, patfd, pgm, print_only = 0; + struct pat_header header; + struct sample_header sample; + char buf[256]; + char name[256]; + long offset; + + pgm = rec->data.data8[0]; + + if (loadmap[pgm]) + return 0; /* Already loaded */ + + sprintf (name, PATCH_PATH "/%s.pat", patch_names[pgm]); + + if ((patfd = open (name, O_RDONLY, 0)) == -1) + { + perror (name); + return errno; + } + + if (read (patfd, buf, 0xef) != 0xef) + { + fprintf (stderr, "%s: Short file\n", name); + return EIO; + } + + memcpy ((char *) &header, buf, sizeof (header)); + + if (strncmp (header.magic, "GF1PATCH110", 12)) + { + fprintf (stderr, "%s: Not a patch file\n", name); + return EINVAL; + } + + if (strncmp (header.version, "ID#000002", 10)) + { + fprintf (stderr, "%s: Incompatible patch file version\n", name); + return EINVAL; + } + + header.nr_waveforms = *(unsigned short *) &buf[85]; + header.master_volume = *(unsigned short *) &buf[87]; + + printf ("GUS: Loading: %s\n", name); + + offset = 0xef; + + for (i = 0; i < header.nr_waveforms; i++) + { + if (lseek (patfd, offset, 0) == -1) + { + perror (name); + return errno; + } + + if (read (patfd, &buf, sizeof (sample)) != sizeof (sample)) + { + fprintf (stderr, "%s: Short file\n", name); + return EIO; + } + + memcpy ((char *) &sample, buf, sizeof (sample)); + + /* + * Since some fields of the patch record are not 32bit aligned, we must + * handle them specially. + */ + sample.low_note = *(long *) &buf[22]; + sample.high_note = *(long *) &buf[26]; + sample.base_note = *(long *) &buf[30]; + sample.detune = *(short *) &buf[34]; + sample.panning = (unsigned char) buf[36]; + + memcpy (sample.envelope_rate, &buf[37], 6); + memcpy (sample.envelope_offset, &buf[43], 6); + + sample.tremolo_sweep = (unsigned char) buf[49]; + sample.tremolo_rate = (unsigned char) buf[50]; + sample.tremolo_depth = (unsigned char) buf[51]; + + sample.vibrato_sweep = (unsigned char) buf[52]; + sample.vibrato_rate = (unsigned char) buf[53]; + sample.vibrato_depth = (unsigned char) buf[54]; + sample.modes = (unsigned char) buf[55]; + sample.scale_frequency = *(short *) &buf[56]; + sample.scale_factor = *(unsigned short *) &buf[58]; + + if (print_only) + { + printf ("\nSample: %03d / %s\n", i, sample.name); + printf ("Len: %d, Loop start: %d, Loop end: %d\n", sample.len, sample.loop_start, sample.loop_end); + printf ("Flags: "); + if (sample.modes & WAVE_16_BITS) + printf ("16 bit "); + if (sample.modes & WAVE_UNSIGNED) + printf ("unsigned "); + if (sample.modes & WAVE_LOOP_BACK) + printf ("reverse "); + if (sample.modes & WAVE_BIDIR_LOOP) + printf ("bidir "); + if (sample.modes & WAVE_LOOPING) + printf ("looping "); + else + printf ("one_shot"); + if (sample.modes & WAVE_SUSTAIN_ON) + printf ("sustain "); + if (sample.modes & WAVE_ENVELOPES) + printf ("enveloped "); + printf ("\n"); + + if (sample.modes & WAVE_ENVELOPES) + { + int i; + + printf ("Envelope info: "); + for (i = 0; i < 6; i++) + { + printf ("%d/%d ", sample.envelope_rate[i], + sample.envelope_offset[i]); + } + printf ("\n"); + } + + printf ("Tremolo: sweep=%d, rate=%d, depth=%d\n", + sample.tremolo_sweep, + sample.tremolo_rate, + sample.tremolo_depth); + + printf ("Vibrato: sweep=%d, rate=%d, depth=%d\n", + sample.vibrato_sweep, + sample.vibrato_rate, + sample.vibrato_depth); + } + + offset = offset + 96; + patch = (struct patch_info *) malloc (sizeof (*patch) + sample.len); + + patch->key = GUS_PATCH; + patch->device_no = gus_dev; + patch->instr_no = pgm; + patch->mode = sample.modes | WAVE_TREMOLO | + WAVE_VIBRATO | WAVE_SCALE; + patch->len = sample.len; + patch->loop_start = sample.loop_start; + patch->loop_end = sample.loop_end; + patch->base_note = sample.base_note; + patch->high_note = sample.high_note; + patch->low_note = sample.low_note; + patch->base_freq = sample.base_freq; + patch->detuning = sample.detune; + patch->panning = (sample.panning - 7) * 16; + + memcpy (patch->env_rate, sample.envelope_rate, 6); + memcpy (patch->env_offset, sample.envelope_offset, 6); + + patch->tremolo_sweep = sample.tremolo_sweep; + patch->tremolo_rate = sample.tremolo_rate; + patch->tremolo_depth = sample.tremolo_depth; + + patch->vibrato_sweep = sample.vibrato_sweep; + patch->vibrato_rate = sample.vibrato_rate; + patch->vibrato_depth = sample.vibrato_depth; + + patch->scale_frequency = sample.scale_frequency; + patch->scale_factor = sample.scale_factor; + + patch->volume = header.master_volume; + + if (lseek (patfd, offset, 0) == -1) + { + perror (name); + return errno; + } + + if (!print_only) + { + if (read (patfd, patch->data, sample.len) != sample.len) + { + fprintf (stderr, "%s: Short file\n", name); + return EIO; + } + + if (write (seqfd, patch, sizeof (*patch) + sample.len) == -1) + { + perror ("/dev/pmgr0"); + return errno; + } + } + + offset = offset + sample.len; + } + + loadmap[pgm] = 1; + return 0; +} + +int +main (int argc, char *argv[]) +{ + struct patmgr_info inf; + int err, i, n; + struct synth_info info; + + if ((seqfd = open ("/dev/patmgr0", O_RDWR, 0)) == -1) + { + fprintf (stderr, "Cannot open\n"); + perror ("/dev/patmgr0"); + exit (-1); + } + + if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1) + { + perror ("NRSYNTH: /dev/patmgr0"); + exit (-1); + } + + for (i = 0; i < n; i++) + { + info.device = i; + + if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1) + { + perror ("SYNTH_INFO: /dev/patmgr0"); + exit (-1); + } + + if (info.synth_type == SYNTH_TYPE_SAMPLE + && info.synth_subtype == SAMPLE_TYPE_GUS) + gus_dev = i; + } + + if (gus_dev == -1) + { + fprintf (stderr, "Error: Gravis Ultrasound not detected\n"); + exit (-1); + } + + if (ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1) + perror ("Sample reset"); + + for (i = 0; i < 256; i++) + loadmap[i] = 0; + + while (1) + { + if (read (seqfd, (char *) &inf, sizeof (inf)) != sizeof (inf)) + { + perror ("Read"); + exit (-1); + } + + if (inf.key == PM_K_EVENT) + switch (inf.command) + { + case PM_E_OPENED: + printf ("Opened\n"); + break; + + case PM_E_CLOSED: + printf ("Closed\n"); + if (ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1) + perror ("Sample reset"); + for (i = 0; i < 256; i++) + loadmap[i] = 0; + break; + + case PM_E_PATCH_RESET: + printf ("Patch reset called\n"); + for (i = 0; i < 256; i++) + loadmap[i] = 0; + break; + + case PM_E_PATCH_LOADED: + printf ("Patch loaded by client\n"); + break; + + default: + printf ("Unknown event %d\n", inf.command); + inf.key = PM_ERROR; + inf.parm1 = EINVAL; + } + else if (inf.key == PM_K_COMMAND) + switch (inf.command) + { + case _PM_LOAD_PATCH: + if ((err = do_load_patch (&inf))) + if (err == ENOSPC) + { + if (ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1) + { + perror ("Sample reset"); + return errno; + } + + for (i = 0; i < 256; i++) + loadmap[i] = 0; + err = do_load_patch (&inf); + } + + if (err) + { + inf.key = PM_ERROR; + inf.parm1 = err; + printf("Error = %d\n", err); + } + else + { + inf.key = PM_K_COMMAND; + inf.parm1 = 0; + } + break; + + default: + printf ("Unknown command %d\n", inf.command); + inf.key = PM_ERROR; + inf.parm1 = EINVAL; + } + else + { + printf ("Unknown event %d/%d\n", inf.key, inf.command); + inf.key = PM_ERROR; + inf.parm1 = EINVAL; + } + + if (write (seqfd, (char *) &inf, sizeof (inf)) != sizeof (inf)) + { + perror ("write"); + exit (-1); + } + } + + exit (0); +} diff --git a/sys/i386/isa/sound/local.h b/sys/i386/isa/sound/local.h new file mode 100644 index 0000000000..38d4c789d0 --- /dev/null +++ b/sys/i386/isa/sound/local.h @@ -0,0 +1,17 @@ +/* Generated by configure. Don't edit!!!! */ + +#define KERNEL_SOUNDCARD +#undef EXCLUDE_PAS +#undef EXCLUDE_SB +#undef EXCLUDE_ADLIB +#undef EXCLUDE_GUS +#undef EXCLUDE_MPU401 +#undef EXCLUDE_SBPRO +#undef EXCLUDE_AUDIO +#undef EXCLUDE_MIDI +#undef EXCLUDE_YM3812 +#undef EXCLUDE_SEQUENCER +#undef EXCLUDE_CHIP_MIDI + +#define DSP_BUFFSIZE 32768 +#define SELECTED_SOUND_OPTIONS 0xffffffff diff --git a/sys/i386/isa/sound/midi.c b/sys/i386/isa/sound/midi.c new file mode 100644 index 0000000000..83088d272f --- /dev/null +++ b/sys/i386/isa/sound/midi.c @@ -0,0 +1,176 @@ +/* UWM - comments to soft-eng@cs.uwm.edu */ +#define _MIDI_TABLE_C_ +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#ifndef EXCLUDE_CHIP_MIDI + + +static int generic_midi_busy[MAX_MIDI_DEV]; + +long CMIDI_init (long mem_start) +{ + + int i; + int n = num_midi_drivers; + /* int n = sizeof (midi_supported) / sizeof( struct generic_midi_info ); + */ + for (i = 0; i < n; i++) + { + if ( midi_supported[i].attach (mem_start) ) + { + printk("MIDI: Successfully attached %s\n",midi_supported[i].name); + } + + } + return (mem_start); +} + + +int +CMIDI_open (int dev, struct fileinfo *file) +{ + + int mode, err, retval; + + dev = dev >> 4; + + mode = file->mode & O_ACCMODE; + + + if (generic_midi_busy[dev]) + return (RET_ERROR(EBUSY)); + + + if (dev >= num_generic_midis) + { + printk(" MIDI device %d not installed.\n", dev); + return (ENXIO); + } + + if (!generic_midi_devs[dev]) + { + printk(" MIDI device %d not initialized\n",dev); + return (ENXIO); + } + + /* If all good and healthy, go ahead and issue call! */ + + + retval = generic_midi_devs[dev]->open (dev, mode) ; + + /* If everything ok, set device as busy */ + + if ( retval >= 0 ) + generic_midi_busy[dev] = 1; + + return ( retval ); + +} + +int +CMIDI_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + + int retval; + + dev = dev >> 4; + + if (dev >= num_generic_midis) + { + printk(" MIDI device %d not installed.\n", dev); + return (ENXIO); + } + + /* Make double sure of healthiness -- doubt + * Need we check this again?? + * + */ + + if (!generic_midi_devs[dev]) + { + printk(" MIDI device %d not initialized\n",dev); + return (ENXIO); + } + + /* If all good and healthy, go ahead and issue call! */ + + + retval = generic_midi_devs[dev]->write (dev, buf); + + return ( retval ); + +} + +int +CMIDI_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count) +{ + int retval; + + dev = dev >> 4; + + if (dev >= num_generic_midis) + { + printk(" MIDI device %d not installed.\n", dev); + return (ENXIO); + } + + /* Make double sure of healthiness -- doubt + * Need we check this again?? + * + */ + + if (!generic_midi_devs[dev]) + { + printk(" MIDI device %d not initialized\n",dev); + return (ENXIO); + } + + /* If all good and healthy, go ahead and issue call! */ + + + retval = generic_midi_devs[dev]->read(dev,buf); + + return (retval); + +} + +int +CMIDI_close (int dev, struct fileinfo *file) +{ + + int retval; + dev = dev >> 4; + + if (dev >= num_generic_midis) + { + printk(" MIDI device %d not installed.\n", dev); + return (ENXIO); + } + + /* Make double sure of healthiness -- doubt + * Need we check this again?? + * + */ + + if (!generic_midi_devs[dev]) + { + printk(" MIDI device %d not initialized\n",dev); + return (ENXIO); + } + + /* If all good and healthy, go ahead and issue call! */ + + + generic_midi_devs[dev]->close(dev); + + generic_midi_busy[dev] = 0; /* Free the device */ + + return (0) ; + +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/midibuf.c b/sys/i386/isa/sound/midibuf.c new file mode 100644 index 0000000000..ca1bdf70d8 --- /dev/null +++ b/sys/i386/isa/sound/midibuf.c @@ -0,0 +1,105 @@ +/* + * linux/kernel/chr_drv/sound/midibuf.c + * + * Device file manager for /dev/midi + * + * NOTE! This part of the driver is currently just a stub. + * + * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further + * details. Should be distributed with this file. + * + * Based on the Midi driver for bsd386 by Mike Durian. + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MPU401) + +#if 0 +#include "midiioctl.h" +#include "midivar.h" +#endif + +static int midibuf_busy = 0; + +int +MIDIbuf_open (int dev, struct fileinfo *file) +{ + int mode, err; + + dev = dev >> 4; + mode = file->mode & O_ACCMODE; + + if (midibuf_busy) + return RET_ERROR (EBUSY); + + if (!mpu401_dev) + { + printk ("Midi: MPU-401 compatible Midi interface not present\n"); + return RET_ERROR (ENXIO); + } + + if ((err = midi_devs[mpu401_dev]->open (mpu401_dev, mode)) < 0) + return err; + + midibuf_busy = 1; + + return RET_ERROR (ENXIO); +} + +void +MIDIbuf_release (int dev, struct fileinfo *file) +{ + int mode; + + dev = dev >> 4; + mode = file->mode & O_ACCMODE; + + midi_devs[mpu401_dev]->close (mpu401_dev); + midibuf_busy = 0; +} + +int +MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + + dev = dev >> 4; + + return count; +} + + +int +MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + dev = dev >> 4; + + return RET_ERROR (EIO); +} + +int +MIDIbuf_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg) +{ + dev = dev >> 4; + + switch (cmd) + { + + default: + return midi_devs[0]->ioctl (dev, cmd, arg); + } +} + +void +MIDIbuf_bytes_received (int dev, unsigned char *buf, int count) +{ +} + +long +MIDIbuf_init (long mem_start) +{ + return mem_start; +} + +#endif diff --git a/sys/i386/isa/sound/mpu401.c b/sys/i386/isa/sound/mpu401.c new file mode 100644 index 0000000000..f9485108e8 --- /dev/null +++ b/sys/i386/isa/sound/mpu401.c @@ -0,0 +1,344 @@ +/* + * linux/kernel/chr_drv/sound/mpu401.c + * + * The low level driver for Roland MPU-401 compatible Midi cards. + * + * This version supports just the DUMB UART mode. + * + * (C) 1993 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further + * details. Should be distributed with this file. + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#if !defined(EXCLUDE_MPU401) && !defined(EXCLUDE_MIDI) + +#define DATAPORT (mpu401_base)/* MPU-401 Data I/O Port on IBM */ +#define COMDPORT (mpu401_base+1) /* MPU-401 Command Port on IBM */ +#define STATPORT (mpu401_base+1) /* MPU-401 Status Port on IBM */ + +#define mpu401_status() INB(STATPORT) +#define input_avail() (!(mpu401_status()&INPUT_AVAIL)) +#define output_ready() (!(mpu401_status()&OUTPUT_READY)) +#define mpu401_cmd(cmd) OUTB(cmd, COMDPORT) +#define mpu401_read() INB(DATAPORT) +#define mpu401_write(byte) OUTB(byte, DATAPORT) + +#define OUTPUT_READY 0x40 /* Mask for Data Read Redy Bit */ +#define INPUT_AVAIL 0x80 /* Mask for Data Send Ready Bit */ +#define MPU_ACK 0xFE /* MPU-401 Acknowledge Response */ +#define MPU_RESET 0xFF /* MPU-401 Total Reset Command */ +#define UART_MODE_ON 0x3F /* MPU-401 "Dumb UART Mode" */ + +static int mpu401_opened = 0; +static int mpu401_base = 0x330; +static int mpu401_irq; +static int mpu401_detected = 0; +static int my_dev; + +static int reset_mpu401 (void); + +static void +mpu401_input_loop (void) +{ + int count; + + count = 10; + + while (count) /* Not timed out */ + if (input_avail ()) + { + unsigned char c = mpu401_read (); + + count = 100; + + if (mpu401_opened & OPEN_READ) + sequencer_midi_input (my_dev, c); + } + else + while (!input_avail () && count) + count--; +} + +void +mpuintr (int unit) +{ + unsigned char c; + + if (input_avail ()) + mpu401_input_loop (); +} + +/* + * It looks like there is no input interrupts in the UART mode. Let's try + * polling. + */ + +/* XXX WARNING: FreeBSD doesn't seem to have timer_lists like this, + * so until I figure out how to do the analogous thing in FreeBSD, I'm + * not all that sure WHAT this driver will do! It might work, depending + * on the condition taken, but then again it might not! -jkh + * XXX WARNING + */ +static void +poll_mpu401 (unsigned long dummy) +{ + unsigned long flags; + +#ifdef linux + static struct timer_list mpu401_timer = + {NULL, 0, 0, poll_mpu401}; +#endif + + if (!(mpu401_opened & OPEN_READ)) + return; /* No longer required */ + + DISABLE_INTR (flags); + + if (input_avail ()) + mpu401_input_loop (); + +#ifdef linux + mpu401_timer.expires = 1; + add_timer (&mpu401_timer); /* Come back later */ +#endif + + RESTORE_INTR (flags); +} + +static int +set_mpu401_irq (int interrupt_level) +{ + int retcode; + +#ifdef linux + struct sigaction sa; + + sa.sa_handler = mpuintr; + +#ifdef SND_SA_INTERRUPT + sa.sa_flags = SA_INTERRUPT; +#else + sa.sa_flags = 0; +#endif + + sa.sa_mask = 0; + sa.sa_restorer = NULL; + + retcode = irqaction (interrupt_level, &sa); + + if (retcode < 0) + { + printk ("MPU-401: IRQ%d already in use\n", interrupt_level); + } + +#else + /* # error Unimplemented for this OS */ +#endif + return retcode; +} + +static int +mpu401_open (int dev, int mode) +{ + if (mpu401_opened) + { + printk ("MPU-401: Midi busy\n"); + return RET_ERROR (EBUSY); + } + + mpu401_input_loop (); + + mpu401_opened = mode; + poll_mpu401 (0); /* Enable input polling */ + + return 0; +} + +static void +mpu401_close (int dev) +{ + mpu401_opened = 0; +} + +static int +mpu401_out (int dev, unsigned char midi_byte) +{ + int timeout; + unsigned long flags; + + /* + * Test for input since pending input seems to block the output. + */ + + DISABLE_INTR (flags); + + if (input_avail ()) + mpu401_input_loop (); + + RESTORE_INTR (flags); + + /* + * Sometimes it takes about 13000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /* Wait */ + + if (!output_ready ()) + { + printk ("MPU-401: Timeout\n"); + return 0; + } + + mpu401_write (midi_byte); + return 1; +} + +static int +mpu401_command (int dev, unsigned char midi_byte) +{ + return 1; +} + +static int +mpu401_start_read (int dev) +{ + return 0; +} + +static int +mpu401_end_read (int dev) +{ + return 0; +} + +static int +mpu401_ioctl (int dev, unsigned cmd, unsigned arg) +{ + return RET_ERROR (EINVAL); +} + +static void +mpu401_kick (int dev) +{ +} + +static int +mpu401_buffer_status (int dev) +{ + return 0; /* No data in buffers */ +} + +static struct midi_operations mpu401_operations = +{ + {"MPU-401", 0}, + mpu401_open, + mpu401_close, + mpu401_ioctl, + mpu401_out, + mpu401_start_read, + mpu401_end_read, + mpu401_kick, + mpu401_command, + mpu401_buffer_status +}; + + +long +attach_mpu401 (long mem_start, struct address_info *hw_config) +{ + int ok, timeout; + unsigned long flags; + + mpu401_base = hw_config->io_base; + mpu401_irq = hw_config->irq; + + if (!mpu401_detected) + return RET_ERROR (EIO); + + DISABLE_INTR (flags); + for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* Wait */ + mpu401_cmd (UART_MODE_ON); + + ok = 0; + for (timeout = 50000; timeout > 0 && !ok; timeout--) + if (input_avail ()) + if (mpu401_read () == MPU_ACK) + ok = 1; + + RESTORE_INTR (flags); + + printk ("snd5: "); + + my_dev = num_midis; +#ifdef linux + mpu401_dev = num_midis; +#endif + midi_devs[num_midis++] = &mpu401_operations; + return mem_start; +} + +static int +reset_mpu401 (void) +{ + unsigned long flags; + int ok, timeout, n; + + /* + * Send the RESET command. Try twice if no success at the first time. + */ + + ok = 0; + + DISABLE_INTR (flags); + + for (n = 0; n < 2 && !ok; n++) + { + for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* Wait */ + mpu401_cmd (MPU_RESET); /* Send MPU-401 RESET Command */ + + /* + * Wait at least 25 msec. This method is not accurate so let's make the + * loop bit longer. Cannot sleep since this is called during boot. + */ + + for (timeout = 50000; timeout > 0 && !ok; timeout--) + if (input_avail ()) + if (mpu401_read () == MPU_ACK) + ok = 1; + + } + + mpu401_opened = 0; + if (ok) + mpu401_input_loop (); /* Flush input before enabling interrupts */ + + RESTORE_INTR (flags); + + return ok; +} + + +int +probe_mpu401 (struct address_info *hw_config) +{ + int ok = 0; + + mpu401_base = hw_config->io_base; + mpu401_irq = hw_config->irq; + + if (set_mpu401_irq (mpu401_irq) < 0) + return 0; + + ok = reset_mpu401 (); + + mpu401_detected = ok; + return ok; +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/opl3.c b/sys/i386/isa/sound/opl3.c new file mode 100644 index 0000000000..a9ef7cb068 --- /dev/null +++ b/sys/i386/isa/sound/opl3.c @@ -0,0 +1,913 @@ +/* + * linux/kernel/chr_drv/sound/opl3.c + * + * A low level driver for Yamaha YM3812 and OPL-3 -chips + * + * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further + * details. Should be distributed with this file. + */ + +/* Major improvements to the FM handling 30AUG92 by Rob Hooft, */ +/* hooft@chem.ruu.nl */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_YM3812) + +#include "opl3.h" + +#define MAX_VOICE 18 +#define OFFS_4OP 11 /* Definitions for the operators OP3 and OP4 + * begin here */ + +static int opl3_enabled = 0; +static int left_address = 0x388, right_address = 0x388, both_address = 0; + +static int nr_voices = 9; +static int logical_voices[MAX_VOICE] = +{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}; + +struct voice_info + { + unsigned char keyon_byte; + long bender; + long bender_range; + unsigned long orig_freq; + unsigned long current_freq; + int mode; + }; + +static struct voice_info voices[MAX_VOICE]; + +typedef struct sbi_instrument instr_array[SBFM_MAXINSTR]; +static instr_array instrmap; +static struct sbi_instrument *active_instrument[MAX_VOICE] = +{NULL}; + +static struct synth_info fm_info = +{"AdLib", 0, SYNTH_TYPE_FM, FM_TYPE_ADLIB, 0, 9, 0, SBFM_MAXINSTR, 0}; + +static int already_initialized = 0; + +static int opl3_ok = 0; +static int opl3_busy = 0; +static int fm_model = 0; /* 0=no fm, 1=mono, 2=SB Pro 1, 3=SB Pro 2 */ + +static int store_instr (int instr_no, struct sbi_instrument *instr); +static void freq_to_fnum (int freq, int *block, int *fnum); +static void opl3_command (int io_addr, const unsigned char addr, const unsigned char val); +static int opl3_kill_note (int dev, int voice, int velocity); +static unsigned char connection_mask = 0x00; + +void +enable_opl3_mode (int left, int right, int both) +{ + opl3_enabled = 1; + left_address = left; + right_address = right; + both_address = both; + fm_info.capabilities = SYNTH_CAP_OPL3; + fm_info.synth_subtype = FM_TYPE_OPL3; +} + +static void +enter_4op_mode (void) +{ + int i; + static int voices_4op[MAX_VOICE] = + {0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17}; + + connection_mask = 0x3f; + opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x3f); /* Select all 4-OP + * voices */ + for (i = 0; i < 3; i++) + physical_voices[i].voice_mode = 4; + for (i = 3; i < 6; i++) + physical_voices[i].voice_mode = 0; + + for (i = 9; i < 12; i++) + physical_voices[i].voice_mode = 4; + for (i = 12; i < 15; i++) + physical_voices[i].voice_mode = 0; + + for (i = 0; i < 12; i++) + logical_voices[i] = voices_4op[i]; + nr_voices = 6; +} + +static int +opl3_ioctl (int dev, + unsigned int cmd, unsigned int arg) +{ + switch (cmd) + { + + case SNDCTL_FM_LOAD_INSTR: + { + struct sbi_instrument ins; + + IOCTL_FROM_USER ((char *) &ins, (char *) arg, 0, sizeof (ins)); + + if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) + { + printk ("FM Error: Invalid instrument number %d\n", ins.channel); + return RET_ERROR (EINVAL); + } + + pmgr_inform (dev, PM_E_PATCH_LOADED, ins.channel, 0, 0, 0); + return store_instr (ins.channel, &ins); + } + break; + + case SNDCTL_SYNTH_INFO: + fm_info.nr_voices = nr_voices; + + IOCTL_TO_USER ((char *) arg, 0, &fm_info, sizeof (fm_info)); + return 0; + break; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + break; + + case SNDCTL_FM_4OP_ENABLE: + if (opl3_enabled) + enter_4op_mode (); + return 0; + break; + + default: + return RET_ERROR (EINVAL); + } + +} + +int +opl3_detect (int ioaddr) +{ + /* + * This function returns 1 if the FM chicp is present at the given I/O port + * The detection algorithm plays with the timer built in the FM chip and + * looks for a change in the status register. + * + * Note! The timers of the FM chip are not connected to AdLib (and compatible) + * boards. + * + * Note2! The chip is initialized if detected. + */ + + unsigned char stat1, stat2; + int i; + + if (already_initialized) + { + return 0; /* Do avoid duplicate initializations */ + } + + opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); /* Reset timers 1 and 2 */ + opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); /* Reset the IRQ of FM + * chicp */ + + stat1 = INB (ioaddr); /* Read status register */ + + if ((stat1 & 0xE0) != 0x00) + { + return 0; /* Should be 0x00 */ + } + + opl3_command (ioaddr, TIMER1_REGISTER, 0xff); /* Set timer 1 to 0xff */ + opl3_command (ioaddr, TIMER_CONTROL_REGISTER, + TIMER2_MASK | TIMER1_START); /* Unmask and start timer 1 */ + + /* + * Now we have to delay at least 80 msec + */ + + for (i = 0; i < 50; i++) + tenmicrosec (); /* To be sure */ + + stat2 = INB (ioaddr); /* Read status after timers have expired */ + + /* Stop the timers */ + + opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); /* Reset timers 1 and 2 */ + opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); /* Reset the IRQ of FM + * chicp */ + + if ((stat2 & 0xE0) != 0xc0) + { + return 0; /* There is no YM3812 */ + } + + /* There is a FM chicp in this address. Now set some default values. */ + + for (i = 0; i < 9; i++) + opl3_command (ioaddr, KEYON_BLOCK + i, 0); /* Note off */ + + opl3_command (ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT); + opl3_command (ioaddr, PERCUSSION_REGISTER, 0x00); /* Melodic mode. */ + + return 1; +} + +static int +opl3_kill_note (int dev, int voice, int velocity) +{ + struct physical_voice_info *map; + + if (voice < 0 || voice >= nr_voices) + return 0; + + map = &physical_voices[logical_voices[voice]]; + + DEB (printk ("Kill note %d\n", voice)); + + if (map->voice_mode == 0) + return 0; + + opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, voices[voice].keyon_byte & ~0x20); + + voices[voice].keyon_byte = 0; + voices[voice].bender = 0; + voices[voice].bender_range = 200; /* 200 cents = 2 semitones */ + voices[voice].orig_freq = 0; + voices[voice].current_freq = 0; + voices[voice].mode = 0; + + return 0; +} + +#define HIHAT 0 +#define CYMBAL 1 +#define TOMTOM 2 +#define SNARE 3 +#define BDRUM 4 +#define UNDEFINED TOMTOM +#define DEFAULT TOMTOM + +static int +store_instr (int instr_no, struct sbi_instrument *instr) +{ + + if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || !opl3_enabled)) + printk ("FM warning: Invalid patch format field (key) 0x%04x\n", instr->key); + memcpy ((char *) &(instrmap[instr_no]), (char *) instr, sizeof (*instr)); + + return 0; +} + +static int +opl3_set_instr (int dev, int voice, int instr_no) +{ + if (voice < 0 || voice >= nr_voices) + return 0; + + if (instr_no < 0 || instr_no >= SBFM_MAXINSTR) + return 0; + + active_instrument[voice] = &instrmap[instr_no]; + return 0; +} + +/* + * The next table looks magical, but it certainly is not. Its values have + * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception + * for i=0. This log-table converts a linear volume-scaling (0..127) to a + * logarithmic scaling as present in the FM-synthesizer chips. so : Volume + * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative + * volume -8 it was implemented as a table because it is only 128 bytes and + * it saves a lot of log() calculations. (RH) + */ +char fm_volume_table[128] = +{-64, -48, -40, -35, -32, -29, -27, -26, /* 0 - 7 */ + -24, -23, -21, -20, -19, -18, -18, -17, /* 8 - 15 */ + -16, -15, -15, -14, -13, -13, -12, -12, /* 16 - 23 */ + -11, -11, -10, -10, -10, -9, -9, -8, /* 24 - 31 */ + -8, -8, -7, -7, -7, -6, -6, -6,/* 32 - 39 */ + -5, -5, -5, -5, -4, -4, -4, -4,/* 40 - 47 */ + -3, -3, -3, -3, -2, -2, -2, -2,/* 48 - 55 */ + -2, -1, -1, -1, -1, 0, 0, 0, /* 56 - 63 */ + 0, 0, 0, 1, 1, 1, 1, 1, /* 64 - 71 */ + 1, 2, 2, 2, 2, 2, 2, 2, /* 72 - 79 */ + 3, 3, 3, 3, 3, 3, 3, 4, /* 80 - 87 */ + 4, 4, 4, 4, 4, 4, 4, 5, /* 88 - 95 */ + 5, 5, 5, 5, 5, 5, 5, 5, /* 96 - 103 */ + 6, 6, 6, 6, 6, 6, 6, 6, /* 104 - 111 */ + 6, 7, 7, 7, 7, 7, 7, 7, /* 112 - 119 */ + 7, 7, 7, 8, 8, 8, 8, 8}; /* 120 - 127 */ + +static void +calc_vol (unsigned char *regbyte, int volume) +{ + int level = (~*regbyte & 0x3f); + + if (level) + level += fm_volume_table[volume]; + + if (level > 0x3f) + level = 0x3f; + if (level < 0) + level = 0; + + *regbyte = (*regbyte & 0xc0) | (~level & 0x3f); +} + +static void +set_voice_volume (int voice, int volume) +{ + unsigned char vol1, vol2, vol3, vol4; + struct sbi_instrument *instr; + struct physical_voice_info *map; + + if (voice < 0 || voice >= nr_voices) + return; + + map = &physical_voices[logical_voices[voice]]; + + instr = active_instrument[voice]; + + if (!instr) + instr = &instrmap[0]; + + if (instr->channel < 0) + return; + + if (voices[voice].mode == 0) + return; + + if (voices[voice].mode == 2) + { /* 2 OP voice */ + + vol1 = instr->operators[2]; + vol2 = instr->operators[3]; + + if ((instr->operators[10] & 0x01)) + { /* Additive synthesis */ + calc_vol (&vol1, volume); + calc_vol (&vol2, volume); + } + else + { /* FM synthesis */ + calc_vol (&vol2, volume); + } + + opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1); /* Modulator volume */ + opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2); /* Carrier volume */ + } + else + { /* 4 OP voice */ + int connection; + + vol1 = instr->operators[2]; + vol2 = instr->operators[3]; + vol3 = instr->operators[OFFS_4OP + 2]; + vol4 = instr->operators[OFFS_4OP + 3]; + + /* + * The connection method for 4 OP voices is defined by the rightmost + * bits at the offsets 10 and 10+OFFS_4OP + */ + + connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); + + switch (connection) + { + case 0: + calc_vol (&vol4, volume); /* Just the OP 4 is carrier */ + break; + + case 1: + calc_vol (&vol2, volume); + calc_vol (&vol4, volume); + break; + + case 2: + calc_vol (&vol1, volume); + calc_vol (&vol4, volume); + break; + + case 3: + calc_vol (&vol1, volume); + calc_vol (&vol3, volume); + calc_vol (&vol4, volume); + break; + + default:/* Why ?? */ ; + } + + opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1); + opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2); + opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], vol3); + opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], vol4); + } +} + +static int +opl3_start_note (int dev, int voice, int note, int volume) +{ + unsigned char data; + int block, fnum, freq, voice_mode; + struct sbi_instrument *instr; + struct physical_voice_info *map; + + if (voice < 0 || voice >= nr_voices) + return 0; + + map = &physical_voices[logical_voices[voice]]; + + if (map->voice_mode == 0) + return 0; + + if (note == 255) /* Just change the volume */ + { + set_voice_volume (voice, volume); + return 0; + } + + /* Kill previous note before playing */ + opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], 0xff); /* Carrier volume to min */ + opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], 0xff); /* Modulator volume to */ + + if (map->voice_mode == 4) + { + opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], 0xff); + opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], 0xff); + } + + opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /* Note off */ + + instr = active_instrument[voice]; + + if (!instr) + instr = &instrmap[0]; + + if (instr->channel < 0) + { + printk ( + "OPL3: Initializing voice %d with undefined instrument\n", + voice); + return 0; + } + + if (map->voice_mode == 2 && instr->key == OPL3_PATCH) + return 0; /* Cannot play */ + + voice_mode = map->voice_mode; + + if (voice_mode == 4) + { + int voice_shift; + + voice_shift = (map->ioaddr == left_address) ? 0 : 3; + voice_shift += map->voice_num; + + if (instr->key != OPL3_PATCH) /* Just 2 OP patch */ + { + voice_mode = 2; + connection_mask &= ~(1 << voice_shift); + } + else + { + connection_mask |= (1 << voice_shift); + } + + opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask); + } + + /* Set Sound Characteristics */ + opl3_command (map->ioaddr, AM_VIB + map->op[0], instr->operators[0]); + opl3_command (map->ioaddr, AM_VIB + map->op[1], instr->operators[1]); + + /* Set Attack/Decay */ + opl3_command (map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]); + opl3_command (map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]); + + /* Set Sustain/Release */ + opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]); + opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]); + + /* Set Wave Select */ + opl3_command (map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]); + opl3_command (map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]); + + /* Set Feedback/Connection */ + /* Connect the voice to both stereo channels */ + opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num, instr->operators[10] | 0x30); + + /* + * If the voice is a 4 OP one, initialize the operators 3 and 4 also + */ + + if (voice_mode == 4) + { + + /* Set Sound Characteristics */ + opl3_command (map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]); + opl3_command (map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]); + + /* Set Attack/Decay */ + opl3_command (map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]); + opl3_command (map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]); + + /* Set Sustain/Release */ + opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]); + opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]); + + /* Set Wave Select */ + opl3_command (map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]); + opl3_command (map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]); + + /* Set Feedback/Connection */ + /* Connect the voice to both stereo channels */ + opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, instr->operators[OFFS_4OP + 10] | 0x30); + } + + voices[voice].mode = voice_mode; + + set_voice_volume (voice, volume); + + freq = voices[voice].orig_freq = note_to_freq (note) / 1000; + + /* + * Since the pitch bender may have been set before playing the note, we + * have to calculate the bending now. + */ + + freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range); + voices[voice].current_freq = freq; + + freq_to_fnum (freq, &block, &fnum); + + /* Play note */ + + data = fnum & 0xff; /* Least significant bits of fnumber */ + opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data); + + data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); + voices[voice].keyon_byte = data; + opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data); + if (voice_mode == 4) + opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data); + + return 0; +} + +static void +freq_to_fnum (int freq, int *block, int *fnum) +{ + int f, octave; + + /* Converts the note frequency to block and fnum values for the FM chip */ + /* First try to compute the block -value (octave) where the note belongs */ + + f = freq; + + octave = 5; + + if (f == 0) + octave = 0; + else if (f < 261) + { + while (f < 261) + { + octave--; + f <<= 1; + } + } + else if (f > 493) + { + while (f > 493) + { + octave++; + f >>= 1; + } + } + + if (octave > 7) + octave = 7; + + *fnum = freq * (1 << (20 - octave)) / 49716; + *block = octave; +} + +static void +opl3_command (int io_addr, const unsigned char addr, const unsigned char val) +{ + int i; + + /* + * The original 2-OP synth requires a quite long delay after writing to a + * register. The OPL-3 survives with just two INBs + */ + + OUTB (addr, io_addr); /* Select register */ + + if (!opl3_enabled) + tenmicrosec (); + else + for (i = 0; i < 2; i++) + INB (io_addr); + + OUTB (val, io_addr + 1); /* Write to register */ + + if (!opl3_enabled) + { + tenmicrosec (); + tenmicrosec (); + tenmicrosec (); + } + else + for (i = 0; i < 2; i++) + INB (io_addr); +} + +static void +opl3_reset (int dev) +{ + int i; + + for (i = 0; i < nr_voices; i++) + { + opl3_command (physical_voices[logical_voices[i]].ioaddr, + KSL_LEVEL + physical_voices[logical_voices[i]].op[0], 0xff); /* OP1 volume to min */ + + opl3_command (physical_voices[logical_voices[i]].ioaddr, + KSL_LEVEL + physical_voices[logical_voices[i]].op[1], 0xff); /* OP2 volume to min */ + + if (physical_voices[logical_voices[i]].voice_mode == 4) /* 4 OP voice */ + { + opl3_command (physical_voices[logical_voices[i]].ioaddr, + KSL_LEVEL + physical_voices[logical_voices[i]].op[2], 0xff); /* OP3 volume to min */ + + opl3_command (physical_voices[logical_voices[i]].ioaddr, + KSL_LEVEL + physical_voices[logical_voices[i]].op[3], 0xff); /* OP4 volume to min */ + } + + opl3_kill_note (dev, i, 64); + } + + if (opl3_enabled) + { + nr_voices = 18; + + for (i = 0; i < 18; i++) + logical_voices[i] = i; + + for (i = 0; i < 18; i++) + physical_voices[i].voice_mode = 2; + + } + +} + +static int +opl3_open (int dev, int mode) +{ + if (!opl3_ok) + return RET_ERROR (ENXIO); + if (opl3_busy) + return RET_ERROR (EBUSY); + opl3_busy = 1; + + connection_mask = 0x00; /* Just 2 OP voices */ + if (opl3_enabled) + opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask); + return 0; +} + +static void +opl3_close (int dev) +{ + opl3_busy = 0; + nr_voices = opl3_enabled ? 18 : 9; + fm_info.nr_drums = 0; + fm_info.perc_mode = 0; + + opl3_reset (dev); +} + +static void +opl3_hw_control (int dev, unsigned char *event) +{ +} + +static int +opl3_load_patch (int dev, int format, snd_rw_buf * addr, + int offs, int count, int pmgr_flag) +{ + struct sbi_instrument ins; + + if (count < sizeof (ins)) + { + printk ("FM Error: Patch record too short\n"); + return RET_ERROR (EINVAL); + } + + COPY_FROM_USER (&((char *) &ins)[offs], (char *) addr, offs, sizeof (ins) - offs); + + if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) + { + printk ("FM Error: Invalid instrument number %d\n", ins.channel); + return RET_ERROR (EINVAL); + } + ins.key = format; + + return store_instr (ins.channel, &ins); +} + +static void +opl3_panning (int dev, int voice, int pressure) +{ +} + +#define SET_VIBRATO(cell) { \ + tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \ + if (pressure > 110) \ + tmp |= 0x40; /* Vibrato on */ \ + opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);} + +static void +opl3_aftertouch (int dev, int voice, int pressure) +{ + int tmp; + struct sbi_instrument *instr; + struct physical_voice_info *map; + + if (voice < 0 || voice >= nr_voices) + return; + + map = &physical_voices[logical_voices[voice]]; + + DEB (printk ("Aftertouch %d\n", voice)); + + if (map->voice_mode == 0) + return; + + /* + * Adjust the amount of vibrato depending the pressure + */ + + instr = active_instrument[voice]; + + if (!instr) + instr = &instrmap[0]; + + if (voices[voice].mode == 4) + { + int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); + + switch (connection) + { + case 0: + SET_VIBRATO (4); + break; + + case 1: + SET_VIBRATO (2); + SET_VIBRATO (4); + break; + + case 2: + SET_VIBRATO (1); + SET_VIBRATO (4); + break; + + case 3: + SET_VIBRATO (1); + SET_VIBRATO (3); + SET_VIBRATO (4); + break; + + } + /* Not implemented yet */ + } + else + { + SET_VIBRATO (1); + + if ((instr->operators[10] & 0x01)) /* Additive synthesis */ + SET_VIBRATO (2); + } +} + +#undef SET_VIBRATO + +static void +opl3_controller (int dev, int voice, int ctrl_num, int value) +{ + unsigned char data; + int block, fnum, freq; + struct physical_voice_info *map; + + if (voice < 0 || voice >= nr_voices) + return; + + map = &physical_voices[logical_voices[voice]]; + + if (map->voice_mode == 0) + return; + + switch (ctrl_num) + { + case CTRL_PITCH_BENDER: + voices[voice].bender = value; + if (!value) + return; + if (!(voices[voice].keyon_byte & 0x20)) + return; /* Not keyed on */ + + freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range); + voices[voice].current_freq = freq; + + freq_to_fnum (freq, &block, &fnum); + + data = fnum & 0xff; /* Least significant bits of fnumber */ + opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data); + + data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); /* KEYON|OCTAVE|MS bits + * of f-num */ + voices[voice].keyon_byte = data; + opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data); + break; + + case CTRL_PITCH_BENDER_RANGE: + voices[voice].bender_range = value; + break; + } +} + +static int +opl3_patchmgr (int dev, struct patmgr_info *rec) +{ + return RET_ERROR (EINVAL); +} + +static struct synth_operations opl3_operations = +{ + &fm_info, + SYNTH_TYPE_FM, + FM_TYPE_ADLIB, + opl3_open, + opl3_close, + opl3_ioctl, + opl3_kill_note, + opl3_start_note, + opl3_set_instr, + opl3_reset, + opl3_hw_control, + opl3_load_patch, + opl3_aftertouch, + opl3_controller, + opl3_panning, + opl3_patchmgr +}; + +long +opl3_init (long mem_start) +{ + int i; + + synth_devs[num_synths++] = &opl3_operations; + fm_model = 0; + opl3_ok = 1; + if (opl3_enabled) + { + printk ("snd1: "); + fm_model = 2; + nr_voices = 18; + fm_info.nr_drums = 0; + fm_info.capabilities |= SYNTH_CAP_OPL3; + strcpy (fm_info.name, "Yamaha OPL-3"); + + for (i = 0; i < 18; i++) + if (physical_voices[i].ioaddr == USE_LEFT) + physical_voices[i].ioaddr = left_address; + else + physical_voices[i].ioaddr = right_address; + + + opl3_command (right_address, OPL3_MODE_REGISTER, OPL3_ENABLE); /* Enable OPL-3 mode */ + opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x00); /* Select all 2-OP + * voices */ + } + else + { + printk ("snd1: "); + fm_model = 1; + nr_voices = 9; + fm_info.nr_drums = 0; + + for (i = 0; i < 18; i++) + physical_voices[i].ioaddr = left_address; + }; + + already_initialized = 1; + for (i = 0; i < SBFM_MAXINSTR; i++) + instrmap[i].channel = -1; + + printk("\n"); + + return mem_start; +} + +#endif diff --git a/sys/i386/isa/sound/opl3.h b/sys/i386/isa/sound/opl3.h new file mode 100644 index 0000000000..b577af7015 --- /dev/null +++ b/sys/i386/isa/sound/opl3.h @@ -0,0 +1,263 @@ +/* + * opl3.h - Definitions of the OPL-3 registers + * + */ + +/* + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * The OPL-3 mode is switched on by writing 0x01, to the offset 5 + * of the right side. + * + * Another special register at the right side is at offset 4. It contains + * a bit mask defining which voices are used as 4 OP voices. + * + * The percussive mode is implemented in the left side only. + * + * With the above exeptions the both sides can be operated independently. + * + * A 4 OP voice can be created by setting the corresponding + * bit at offset 4 of the right side. + * + * For example setting the rightmost bit (0x01) changes the + * first voice on the right side to the 4 OP mode. The fourth + * voice is made inaccessible. + * + * If a voice is set to the 2 OP mode, it works like 2 OP modes + * of the original YM3812 (AdLib). In addition the voice can + * be connected the left, right or both stereo channels. It can + * even be left unconnected. This works with 4 OP voices also. + * + * The stereo connection bits are located in the FEEDBACK_CONNECTION + * register of the voice (0xC0-0xC8). In 4 OP voices these bits are + * in the second half of the voice. + */ + +/* + * Register numbers for the global registers + */ + +#define TEST_REGISTER 0x01 +#define ENABLE_WAVE_SELECT 0x20 + +#define TIMER1_REGISTER 0x02 +#define TIMER2_REGISTER 0x03 +#define TIMER_CONTROL_REGISTER 0x04 /* Left side */ +#define IRQ_RESET 0x80 +#define TIMER1_MASK 0x40 +#define TIMER2_MASK 0x20 +#define TIMER1_START 0x01 +#define TIMER2_START 0x02 + +#define CONNECTION_SELECT_REGISTER 0x04 /* Right side */ +#define RIGHT_4OP_0 0x01 +#define RIGHT_4OP_1 0x02 +#define RIGHT_4OP_2 0x04 +#define LEFT_4OP_0 0x08 +#define LEFT_4OP_1 0x10 +#define LEFT_4OP_2 0x20 + +#define OPL3_MODE_REGISTER 0x05 /* Right side */ +#define OPL3_ENABLE 0x01 + +#define KBD_SPLIT_REGISTER 0x08 /* Left side */ +#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */ +#define KEYBOARD_SPLIT 0x40 + +#define PERCUSSION_REGISTER 0xbd /* Left side only */ +#define TREMOLO_DEPTH 0x80 +#define VIBRATO_DEPTH 0x40 +#define PERCUSSION_ENABLE 0x20 +#define BASSDRUM_ON 0x10 +#define SNAREDRUM_ON 0x08 +#define TOMTOM_ON 0x04 +#define CYMBAL_ON 0x02 +#define HIHAT_ON 0x01 + +/* + * Offsets to the register banks for operators. To get the + * register number just add the operator offset to the bank offset + * + * AM/VIB/EG/KSR/Multiple (0x20 to 0x35) + */ + #define AM_VIB 0x20 + #define TREMOLO_ON 0x80 + #define VIBRATO_ON 0x40 + #define SUSTAIN_ON 0x20 + #define KSR 0x10 /* Key scaling rate */ + #define MULTIPLE_MASK 0x0f /* Frequency multiplier */ + + /* + * KSL/Total level (0x40 to 0x55) + */ +#define KSL_LEVEL 0x40 +#define KSL_MASK 0xc0 /* Envelope scaling bits */ +#define TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */ + +/* + * Attack / Decay rate (0x60 to 0x75) + */ +#define ATTACK_DECAY 0x60 +#define ATTACK_MASK 0xf0 +#define DECAY_MASK 0x0f + +/* + * Sustain level / Release rate (0x80 to 0x95) + */ +#define SUSTAIN_RELEASE 0x80 +#define SUSTAIN_MASK 0xf0 +#define RELEASE_MASK 0x0f + +/* + * Wave select (0xE0 to 0xF5) + */ +#define WAVE_SELECT 0xe0 + +/* + * Offsets to the register banks for voices. Just add to the + * voice number to get the register number. + * + * F-Number low bits (0xA0 to 0xA8). + */ +#define FNUM_LOW 0xa0 + +/* + * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8) + */ +#define KEYON_BLOCK 0xb0 +#define KEYON_BIT 0x20 +#define BLOCKNUM_MASK 0x1c +#define FNUM_HIGH_MASK 0x03 + +/* + * Feedback / Connection (0xc0 to 0xc8) + * + * These registers have two new bits when the OPL-3 mode + * is selected. These bits controls connecting the voice + * to the stereo channels. For 4 OP voices this bit is + * defined in the second half of the voice (add 3 to the + * register offset). + * + * For 4 OP voices the connection bit is used in the + * both halfs (gives 4 ways to connect the operators). + */ +#define FEEDBACK_CONNECTION 0xc0 +#define FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */ +#define CONNECTION_BIT 0x01 +/* + * In the 4 OP mode there is four possible configurations how the + * operators can be connected together (in 2 OP modes there is just + * AM or FM). The 4 OP connection mode is defined by the rightmost + * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halfs. + * + * First half Second half Mode + * + * +---+ + * v | + * 0 0 >+-1-+--2--3--4--> + * + * + * + * +---+ + * | | + * 0 1 >+-1-+--2-+ + * |-> + * >--3----4-+ + * + * +---+ + * | | + * 1 0 >+-1-+-----+ + * |-> + * >--2--3--4-+ + * + * +---+ + * | | + * 1 1 >+-1-+--+ + * | + * >--2--3-+-> + * | + * >--4----+ + */ +#define STEREO_BITS 0x30 /* OPL-3 only */ +#define VOICE_TO_LEFT 0x10 +#define VOICE_TO_RIGHT 0x20 + +/* + * Definition table for the physical voices + */ + +struct physical_voice_info { + unsigned char voice_num; + unsigned char voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */ + unsigned short ioaddr; /* I/O port (left or right side) */ + unsigned char op[4]; /* Operator offsets */ + }; + +/* + * There is 18 possible 2 OP voices + * (9 in the left and 9 in the right). + * The first OP is the modulator and 2nd is the carrier. + * + * The first three voices in the both sides may be connected + * with another voice to a 4 OP voice. For example voice 0 + * can be connected with voice 3. The operators of voice 3 are + * used as operators 3 and 4 of the new 4 OP voice. + * In this case the 2 OP voice number 0 is the 'first half' and + * voice 3 is the second. + */ + +#define USE_LEFT 0 +#define USE_RIGHT 1 + +static struct physical_voice_info physical_voices[18] = +{ +/* No Mode Side OP1 OP2 OP3 OP4 */ +/* --------------------------------------------------- */ + { 0, 2, USE_LEFT, {0x00, 0x03, 0x08, 0x0b}}, + { 1, 2, USE_LEFT, {0x01, 0x04, 0x09, 0x0c}}, + { 2, 2, USE_LEFT, {0x02, 0x05, 0x0a, 0x0d}}, + + { 3, 2, USE_LEFT, {0x08, 0x0b, 0x00, 0x00}}, + { 4, 2, USE_LEFT, {0x09, 0x0c, 0x00, 0x00}}, + { 5, 2, USE_LEFT, {0x0a, 0x0d, 0x00, 0x00}}, + + { 6, 2, USE_LEFT, {0x10, 0x13, 0x00, 0x00}}, /* Used by percussive voices */ + { 7, 2, USE_LEFT, {0x11, 0x14, 0x00, 0x00}}, /* if the percussive mode */ + { 8, 2, USE_LEFT, {0x12, 0x15, 0x00, 0x00}}, /* is selected */ + + { 0, 2, USE_RIGHT, {0x00, 0x03, 0x08, 0x0b}}, + { 1, 2, USE_RIGHT, {0x01, 0x04, 0x09, 0x0c}}, + { 2, 2, USE_RIGHT, {0x02, 0x05, 0x0a, 0x0d}}, + + { 3, 2, USE_RIGHT, {0x08, 0x0b, 0x00, 0x00}}, + { 4, 2, USE_RIGHT, {0x09, 0x0c, 0x00, 0x00}}, + { 5, 2, USE_RIGHT, {0x0a, 0x0d, 0x00, 0x00}}, + + { 6, 2, USE_RIGHT, {0x10, 0x13, 0x00, 0x00}}, + { 7, 2, USE_RIGHT, {0x11, 0x14, 0x00, 0x00}}, + { 8, 2, USE_RIGHT, {0x12, 0x15, 0x00, 0x00}} +}; diff --git a/sys/i386/isa/sound/os.h b/sys/i386/isa/sound/os.h new file mode 100644 index 0000000000..2ea9e5a654 --- /dev/null +++ b/sys/i386/isa/sound/os.h @@ -0,0 +1,273 @@ +#ifndef _OS_H_ +#define _OS_H_ +/* + * OS specific settings for FreeBSD + * + * This chould be used as an example when porting the driver to a new + * operating systems. + * + * What you should do is to rewrite the soundcard.c and os.h (this file). + * You should create a new subdirectory and put these two files there. + * In addition you have to do a makefile.. + * + * If you have to make changes to other than these two files, please contact me + * before making the changes. It's possible that I have already made the + * change. + */ + +/* + * Insert here the includes required by your kernel. + */ + +#include "param.h" +#include "systm.h" +#include "ioctl.h" +#include "tty.h" +#include "proc.h" +#include "user.h" +#include "conf.h" +#include "file.h" +#include "uio.h" +/* #include "kernel.h" */ +#include "syslog.h" +#include "errno.h" +#include "malloc.h" +#include "buf.h" +#include "i386/isa/isa_device.h" + +/* These few lines are used by FreeBSD (only??). */ + +#if NSND > 0 +#define KERNEL_SOUNDCARD +#else +#undef KERNEL_SOUNDCARD +#endif + + +/* + * Rest of the file is compiled only if the driver is really required. + */ +#ifdef CONFIGURE_SOUNDCARD + +/* lbolt is required by the FreeBSD version (only???) */ +extern int __timeout_val; +extern int __process_aborting; + +/* + * select() is currently implemented in Linux specific way. Don't enable. + * I don't remember what the SHORT_BANNERS means so forget it. + */ + +#undef ALLOW_SELECT +#define SHORT_BANNERS + +/* The soundcard.h could be in a nonstandard place so inclyde it here. */ +#include "soundcard.h" + +/* + * Here is the first portability problem. Every OS has it's own way to + * pass a pointer to the buffer in read() and write() calls. In Linux it's + * just a char*. In BSD it's struct uio. This parameter is passed to + * all functions called from read() or write(). Since nothing can be + * assumed about this structure, the driver uses set of macros for + * accessing the user buffer. + * + * The driver reads/writes bytes in the user buffer sequentially which + * means that calls like uiomove() can be used. + * + * snd_rw_buf is the type which is passed to the device file specific + * read() and write() calls. + * + * The following macros are used to move date to and from the + * user buffer. These macros should be used only when the + * target or source parameter has snd_rw_buf type. + * The offs parameter is a offset relative to the beginning of + * the user buffer. In Linux the offset is required but for example + * BSD passes the offset info in the uio structure. It could be usefull + * if these macros verify that the offs parameter and the value in + * the snd_rw_buf structure are equal. + */ +typedef struct uio snd_rw_buf; + +/* + * Move bytes from the buffer which the application given in a + * write() call. + * offs is position relative to the beginning of the buffer in + * user space. The count is number of bytes to be moved. + */ +#define COPY_FROM_USER(target, source, offs, count) \ + if (uiomove(target, count, source)) { \ + printf ("sb: Bad copyin()!\n"); \ + } else +/* Like COPY_FOM_USER but for writes. */ +#define COPY_TO_USER(target, offs, source, count) \ + if (uiomove(source, count, target)) { \ + printf ("sb: Bad copyout()!\n"); \ + } else +/* + * The following macros are like COPY_*_USER but work just with one byte (8bit), + * short (16 bit) or long (32 bit) at a time. + * The same restrictions apply than for COPY_*_USER + */ +#define GET_BYTE_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 1, addr);} +#define GET_SHORT_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 2, addr);} +#define GET_WORD_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 4, addr);} +#define PUT_WORD_TO_USER(addr, offs, data) {uiomove((char*)&(data), 4, addr);} + +/* + * The way how the ioctl arguments are passed is another nonportable thing. + * In Linux the argument is just a pointer directly to the user segment. On + * 386bsd the data is already moved to the kernel space. The following + * macros should handle the difference. + */ + +/* + * IOCTL_FROM_USER is used to copy a record pointed by the argument to + * a buffer in the kernel space. On 386bsd it can be done just by calling + * memcpy. With Linux a memcpy_from_fs should be called instead. + * Parameters of the following macros are like in the COPY_*_USER macros. + */ + +/* + * When the ioctl argument points to a record or array (longer than 32 bits), + * the macros IOCTL_*_USER are used. It's assumed that the source and target + * parameters are direct memory addresses. + */ +#define IOCTL_FROM_USER(target, source, offs, count) {memcpy(target, &((source)[offs]), count);} +#define IOCTL_TO_USER(target, offs, source, count) {memcpy(&((target)[offs]), source, count);} +/* The following macros are used if the ioctl argument points to 32 bit int */ +#define IOCTL_IN(arg) (*(int*)arg) +#define IOCTL_OUT(arg, ret) *(int*)arg = ret + +/* + * When the driver displays something to the console, printk() will be called. + * The name can be changed here. + */ +#define printk printf + +/* + * The following macros define an interface to the process management. + */ + +/* + * DEFINE_WAIT_QUEUE is used where a wait queue is required. It must define + * a structure which can be passed as a parameter to a sleep(). The second + * parameter is name of a flag variable (must be defined as int). + */ +#define DEFINE_WAIT_QUEUE(qname, flag) static int *qname = NULL; static int flag = 0 +/* Like the above but defines an array of wait queues and flags */ +#define DEFINE_WAIT_QUEUES(qname, flag) static int *qname = {NULL}; static int flag = {0} +/* + * This driver handles interrupts little bit nonstandard way. The following + * macro is used to test if the current process has received a signal which + * is aborts the process. This macro is called from close() to see if the + * buffers should be discarded. If this kind info is not available, a constant + * 1 or 0 could be returned (1 should be better than 0). + * I'm not sure if the following is correct for FreeBSD. + */ +#define PROCESS_ABORTING (__process_aborting | curproc->p_sig) +/* + * REQUEST_TIMEOUT is called before sleep. It shoud ensure that the + * process is woken up after given number of ticks (1/HZ secs.). + * The wqueue gives the wait queue. + */ +#define REQUEST_TIMEOUT(nticks, wqueue) __timeout_val = nticks; + +/* + * The following macro calls sleep. It should be implemented such that + * the process is resumed if it receives a signal. The following is propably + * not the way how it should be done on 386bsd. + * The on_what parameter is a wait_queue defined with DEFINE_WAIT_QUEUE() + * The second parameter is a flag. It must be initialized to 1 before sleep + * and to zero after proces continues. + */ +#define INTERRUPTIBLE_SLEEP_ON(on_what, flag) \ + { \ + flag = 1; \ + flag=tsleep(&(on_what), (PRIBIO-5)|PCATCH, "sndint", __timeout_val); \ + if(flag == ERESTART) __process_aborting = 1;\ + else __process_aborting = 0;\ + __timeout_val = 0; \ + flag = 0; \ + } + +/* An the following wakes up a process */ +#define WAKE_UP(who) wakeup(&(who)) + +/* + * Timing macros. This driver assumes that there is a timer running in the + * kernel. The timer should return a value which is increased once at every + * timer tick. The macro HZ should return the number of such ticks/sec. + */ + +#ifndef HZ +extern int hz; +#define HZ hz +#endif + +/* + * GET_TIME() returns current value of the counter incremented at timer + * ticks. This can overflow, so the timeout might be real big... + * + */ +#define GET_TIME() get_time() +/*#define GET_TIME() (lbolt) /* Returns current time (1/HZ secs since boot) */ + +/* + * The following three macros are called before and after atomic + * code sequences. The flags parameter has always type of unsigned long. + * The macro DISABLE_INTR() should ensure that all interrupts which + * may invoke any part of the driver (timer, soundcard interrupts) are + * disabled. + * RESTORE_INTR() should return the interrupt status back to the + * state when DISABLE_INTR() was called. The flags parameter is + * a variable which can carry 32 bits of state information between + * DISABLE_INTR() and RESTORE_INTR() calls. + */ +#define DISABLE_INTR(flags) flags = splhigh() +#define RESTORE_INTR(flags) splx(flags) + +/* + * INB() and OUTB() should be obvious. NOTE! The order of + * paratemeters of OUTB() is different than on some other + * operating systems. + */ + +#define INB inb +#define OUTB(addr, data) outb(data, addr) + +/* memcpy() was not defined og 386bsd. Lets define it here */ +#define memcpy(d, s, c) bcopy(s, d, c) + +/* + * When a error (such as EINVAL) is returned by a function, + * the following macro is used. The driver assumes that a + * error is signalled by returning a negative value. + */ + +#define RET_ERROR(err) -(err) + +/* + KERNEL_MALLOC() allocates requested number of memory and + KERNEL_FREE is used to free it. + These macros are never called from interrupt, in addition the + nbytes will never be more than 4096 bytes. Generally the driver + will allocate memory in blocks of 4k. If the kernel has just a + page level memory allocation, 4K can be safely used as the size + (the nbytes parameter can be ignored). +*/ +#define KERNEL_MALLOC(nbytes) malloc(nbytes, M_TEMP, M_WAITOK) +#define KERNEL_FREE(addr) free(addr, M_TEMP) + +/* + * The rest of this file is not complete yet. The functions using these + * macros will not work + */ +#define ALLOC_DMA_CHN(chn) (0) +#define RELEASE_DMA_CHN(chn) (0) +#define DMA_MODE_READ 0 +#define DMA_MODE_WRITE 1 +#define RELEASE_IRQ(irq_no) + +#endif +#endif diff --git a/sys/i386/isa/sound/pas.h b/sys/i386/isa/sound/pas.h new file mode 100644 index 0000000000..4dadea3713 --- /dev/null +++ b/sys/i386/isa/sound/pas.h @@ -0,0 +1,249 @@ +/* */ +/* Port addresses and bit fields for the Media Vision Pro AudioSpectrum second generation sound cards. */ +/* */ +/* Feel free to use this header file in any application you create that has support for the Media Vision */ +/* Pro AudioSpectrum second generation sound cards. Other uses prohibited without prior permission. */ +/* */ +/* - cmetz@thor.tjhsst.edu */ +/* */ +/* Notes: */ +/* */ +/* * All of these ports go into the MVD101 multimedia controller chip, which then signals the other chips to do */ +/* the actual work. Many ports like the FM ones functionally attach directly to the destination chip though */ +/* they don't actually have a direct connection. */ +/* */ +/* * The PAS2 series cards have an MVD101 multimedia controller chip, the original PAS cards don't. The original */ +/* PAS cards are pretty defunct now, so no attempt is made here to support them. */ +/* */ +/* * The PAS2 series cards are all really different at the hardware level, though the MVD101 hides some of the */ +/* incompatibilities, there still are differences that need to be accounted for. */ +/* */ +/* Card CD-ROM interface PCM chip Mixer chip FM chip */ +/* PAS Plus Sony proprietary (Crystal?) 8-bit DAC National OPL3 */ +/* PAS 16 Zilog SCSI MVA416 16-bit Codec MVA508 OPL3 */ +/* CDPC Sony proprietary Sony 16-bit Codec National OPL3 */ +/* Fusion CD 16 Sony proprietary MVA416 16-bit Codec MVA508 OPL3 */ +/* Fusion CD Sony proprietary (Crystal?) 8-bit DAC National OPL3 */ +/* */ +#define PAS_DEFAULT_BASE 0x388 + +/* Symbolic Name Value R W Subsystem Description */ +#define SPEAKER_CONTROL 0x61 /* W PC speaker Control register */ +#define SPEAKER_CONTROL_GHOST 0x738B /* R W PC speaker Control ghost register */ +#define SPEAKER_TIMER_CONTROL 0x43 /* W PC speaker Timer control register */ +#define SPEAKER_TIMER_CONTROL_GHOST 0x778B /* R W PC speaker Timer control register ghost */ +#define SPEAKER_TIMER_DATA 0x42 /* W PC speaker Timer data register */ +#define SPEAKER_TIMER_DATA_GHOST 0x138A /* R W PC speaker Timer data register ghost */ + +#define WARM_BOOT 0x41 /* W Control Used to detect system warm boot */ +#define WARM_BOOT_GHOST 0x7789 /* ? W Control Use to get the card to fake warm boot */ +#define MASTER_DECODE 0x9A01 /* W Control Address >> 2 of card base address */ +#define PRESCALE_DIVIDER 0xBF8A /* R W PCM Ration between Codec clock and master clock */ +#define WAIT_STATE 0xBF88 /* R W Control Four-bit bus wait-state count (~140ns ea.) */ +#define BOARD_REV_ID 0x2789 /* R Control Extended Board Revision ID */ + +#define SYSTEM_CONFIGURATION_1 0x8388 /* R W Control */ + #define S_C_1_PCS_ENABLE 0x01 /* R W PC speaker 1=enable, 0=disable PC speaker emulation */ + #define S_C_1_PCM_CLOCK_SELECT 0x02 /* R W PCM 1=14.31818Mhz/12, 0=28.224Mhz master clock */ + #define S_C_1_FM_EMULATE_CLOCK 0x04 /* R W FM 1=use 28.224Mhz/2, 0=use 14.31818Mhz clock */ + #define S_C_1_PCS_STEREO 0x10 /* R W PC speaker 1=enable PC speaker stereo effect, 0=disable */ + #define S_C_1_PCS_REALSOUND 0x20 /* R W PC speaker 1=enable RealSound enhancement, 0=disable */ + #define S_C_1_FORCE_EXT_RESET 0x40 /* R W Control Force external reset */ + #define S_C_1_FORCE_INT_RESET 0x80 /* R W Control Force internal reset */ +#define SYSTEM_CONFIGURATION_2 0x8389 /* R W Control */ + #define S_C_2_PCM_OVERSAMPLING 0x03 /* R W PCM 00=0x, 01=2x, 10=4x, 11=reserved */ + #define S_C_2_PCM_16_BIT 0x04 /* R W PCM 1=16-bit, 0=8-bit samples */ +#define SYSTEM_CONFIGURATION_3 0x838A /* R W Control */ + #define S_C_3_PCM_CLOCK_SELECT 0x02 /* R W PCM 1=use 1.008Mhz clock for PCM, 0=don't */ +#define SYSTEM_CONFIGURATION_4 0x838B /* R W Control CD-ROM interface controls */ + +#define IO_CONFIGURATION_1 0xF388 /* R W Control */ + #define I_C_1_BOOT_RESET_ENABLE 0x80 /* R W Control 1=reset board on warm boot, 0=don't */ +#define IO_CONFIGURATION_2 0xF389 /* R W Control */ + #define I_C_2_PCM_DMA_DISABLED 0x00 /* R W PCM PCM DMA disabled */ +#define IO_CONFIGURATION_3 0xF38A /* R W Control */ + #define I_C_3_PCM_IRQ_DISABLED 0x00 /* R W PCM PCM IRQ disabled */ + +#define COMPATIBILITY_ENABLE 0xF788 /* R W Control */ + #define C_E_MPU401_ENABLE 0x01 /* R W MIDI 1=enable, 0=disable MPU401 MIDI emulation */ + #define C_E_SB_ENABLE 0x02 /* R W PCM 1=enable, 0=disable Sound Blaster emulation */ + #define C_E_SB_ACTIVE 0x04 /* R PCM "Sound Blaster Interrupt active" */ + #define C_E_MPU401_ACTIVE 0x08 /* R MIDI "MPU UART mode active" */ + #define C_E_PCM_COMPRESSION 0x10 /* R W PCM 1=enable, 0=disabled compression */ +#define EMULATION_ADDRESS 0xF789 /* R W Control */ + #define E_A_SB_BASE 0x0f /* R W PCM bits A4-A7 for SB base port */ + #define E_A_MPU401_BASE 0xf0 /* R W MIDI bits A4-A7 for MPU401 base port */ +#define EMULATION_CONFIGURATION 0xFB8A /* R W ***** Only valid on newer PAS2 cards (?) ***** */ + #define E_C_MPU401_IRQ 0x07 /* R W MIDI MPU401 emulation IRQ */ + #define E_C_SB_IRQ 0x38 /* R W PCM SB emulation IRQ */ + #define E_C_SB_DMA 0xC0 /* R W PCM SB emulation DMA */ + +#define OPERATION_MODE_1 0xEF8B /* R Control */ + #define O_M_1_CDROM_TYPE 0x03 /* R CD-ROM 3=SCSI, 2=Sony, 0=no CD-ROM interface */ + #define O_M_1_FM_TYPE 0x04 /* R FM 1=sterero, 0=mono FM chip */ + #define O_M_1_PCM_TYPE 0x08 /* R PCM 1=16-bit Codec, 0=8-bit DAC */ +#define OPERATION_MODE_2 0xFF8B /* R Control */ + #define O_M_2_PCS_ENABLED 0x02 /* R PC speaker PC speaker emulation 1=enabled, 0=disabled */ + #define O_M_2_BUS_TIMING 0x10 /* R Control 1=AT bus timing, 0=XT bus timing */ + #define O_M_2_BOARD_REVISION 0xe0 /* R Control Board revision */ + +#define INTERRUPT_MASK 0x0B8B /* R W Control */ + #define I_M_FM_LEFT_IRQ_ENABLE 0x01 /* R W FM Enable FM left interrupt */ + #define I_M_FM_RIGHT_IRQ_ENABLE 0x02 /* R W FM Enable FM right interrupt */ + #define I_M_PCM_RATE_IRQ_ENABLE 0x04 /* R W PCM Enable Sample Rate interrupt */ + #define I_M_PCM_BUFFER_IRQ_ENABLE 0x08 /* R W PCM Enable Sample Buffer interrupt */ + #define I_M_MIDI_IRQ_ENABLE 0x10 /* R W MIDI Enable MIDI interrupt */ + #define I_M_BOARD_REV 0xE0 /* R Control Board revision */ + +#define INTERRUPT_STATUS 0x0B89 /* R W Control */ + #define I_S_FM_LEFT_IRQ 0x01 /* R W FM Left FM Interrupt Pending */ + #define I_S_FM_RIGHT_IRQ 0x02 /* R W FM Right FM Interrupt Pending */ + #define I_S_PCM_SAMPLE_RATE_IRQ 0x04 /* R W PCM Sample Rate Interrupt Pending */ + #define I_S_PCM_SAMPLE_BUFFER_IRQ 0x08 /* R W PCM Sample Buffer Interrupt Pending */ + #define I_S_MIDI_IRQ 0x10 /* R W MIDI MIDI Interrupt Pending */ + #define I_S_PCM_CHANNEL 0x20 /* R W PCM 1=right, 0=left */ + #define I_S_RESET_ACTIVE 0x40 /* R W Control Reset is active (Timed pulse not finished) */ + #define I_S_PCM_CLIPPING 0x80 /* R W PCM Clipping has occurred */ + +#define FILTER_FREQUENCY 0x0B8A /* R W Control */ + #define F_F_FILTER_DISABLED 0x00 /* R W Mixer No filter */ +#if 0 + struct { /* R W Mixer Filter translation */ + unsigned int freq:24; + unsigned int value:8; + } F_F_FILTER_translate[] = + { { 73500, 0x01 }, /* 73500Hz - divide by 16 */ + { 65333, 0x02 }, /* 65333Hz - divide by 18 */ + { 49000, 0x09 }, /* 49000Hz - divide by 24 */ + { 36750, 0x11 }, /* 36750Hz - divide by 32 */ + { 24500, 0x19 }, /* 24500Hz - divide by 48 */ + { 18375, 0x07 }, /* 18375Hz - divide by 64 */ + { 12783, 0x0f }, /* 12783Hz - divide by 92 */ + { 12250, 0x04 }, /* 12250Hz - divide by 96 */ + { 9188, 0x17 }, /* 9188Hz - divide by 128 */ + { 6125, 0x1f }, /* 6125Hz - divide by 192 */ + }; +#endif + #define F_F_MIXER_UNMUTE 0x20 /* R W Mixer 1=disable, 0=enable board mute */ + #define F_F_PCM_RATE_COUNTER 0x40 /* R W PCM 1=enable, 0=disable sample rate counter */ + #define F_F_PCM_BUFFER_COUNTER 0x80 /* R W PCM 1=enable, 0=disable sample buffer counter */ + +#define PAS_NONE 0 +#define PAS_PLUS 1 +#define PAS_CDPC 2 +#define PAS_16 3 + +#ifdef DEFINE_TRANSLATIONS + char I_C_2_PCM_DMA_translate[] = /* R W PCM PCM DMA channel value translations */ + { 4, 1, 2, 3, 0, 5, 6, 7 }; + char I_C_3_PCM_IRQ_translate[] = /* R W PCM PCM IRQ level value translation */ + { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 0, 10, 11 }; + char E_C_MPU401_IRQ_translate[] = /* R W MIDI MPU401 emulation IRQ value translation */ + { 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x05, 0x06, 0x07 }; + char E_C_SB_IRQ_translate[] = /* R W PCM SB emulation IRQ translate */ + { 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 0x00, 0x00, 0x28, 0x30, 0x38 }; + char E_C_SB_DMA_translate[] = /* R W PCM SB emulation DMA translate */ + { 0x00, 0x40, 0x80, 0xC0 }; + char O_M_1_to_card[] = /* R W Control Translate (OM1 & 0x0f) to card type */ + { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 3, 0, 2, 3 }; +#else + extern char I_C_2_PCM_DMA_translate[]; /* R W PCM PCM DMA channel value translations */ + extern char I_C_3_PCM_IRQ_translate[]; /* R W PCM PCM IRQ level value translation */ + extern char E_C_MPU401_IRQ_translate[]; /* R W MIDI MPU401 emulation IRQ value translation */ + extern char E_C_SB_IRQ_translate[]; /* R W PCM SB emulation IRQ translate */ + extern char E_C_SB_DMA_translate[]; /* R W PCM SB emulation DMA translate */ + extern char O_M_1_to_card[]; /* R W Control Translate (OM1 & 0x0f) to card type */ +#endif + +#define PARALLEL_MIXER 0x078B /* W Mixer Documented for MVD101 as FM Mono Right decode?? */ + #define P_M_MV508_ADDRESS 0x80 /* W Mixer MVD508 Address/mixer select */ + #define P_M_MV508_DATA 0x00 + #define P_M_MV508_LEFT 0x20 /* W Mixer MVD508 Left channel select */ + #define P_M_MV508_RIGHT 0x40 /* W Mixer MVD508 Right channel select */ + #define P_M_MV508_BOTH 0x00 /* W Mixer MVD508 Both channel select */ + #define P_M_MV508_MIXER 0x10 /* W Mixer MVD508 Select a mixer (rather than a volume) */ + #define P_M_MV508_VOLUME 0x00 + + #define P_M_MV508_INPUTMIX 0x20 /* W Mixer MVD508 Select mixer A */ + #define P_M_MV508_OUTPUTMIX 0x00 /* W Mixer MVD508 Select mixer B */ + + #define P_M_MV508_MASTER_A 0x01 /* W Mixer MVD508 Master volume control A (output) */ + #define P_M_MV508_MASTER_B 0x02 /* W Mixer MVD508 Master volume control B (DSP input) */ + #define P_M_MV508_BASS 0x03 /* W Mixer MVD508 Bass control */ + #define P_M_MV508_TREBLE 0x04 /* W Mixer MVD508 Treble control */ + #define P_M_MV508_MODE 0x05 /* W Mixer MVD508 Master mode control */ + + #define P_M_MV508_LOUDNESS 0x04 /* W Mixer MVD508 Mode control - Loudness filter */ + #define P_M_MV508_ENHANCE_BITS 0x03 + #define P_M_MV508_ENHANCE_NONE 0x00 /* W Mixer MVD508 Mode control - No stereo enhancement */ + #define P_M_MV508_ENHANCE_40 0x01 /* W Mixer MVD508 Mode control - 40% stereo enhancement */ + #define P_M_MV508_ENHANCE_60 0x02 /* W Mixer MVD508 Mode control - 60% stereo enhancement */ + #define P_M_MV508_ENHANCE_80 0x03 /* W Mixer MVD508 Mode control - 80% stereo enhancement */ + + #define P_M_MV508_FM 0x00 /* W Mixer MVD508 Channel 0 - FM */ + #define P_M_MV508_IMIXER 0x01 /* W Mixer MVD508 Channel 1 - Input mixer (rec monitor) */ + #define P_M_MV508_LINE 0x02 /* W Mixer MVD508 Channel 2 - Line in */ + #define P_M_MV508_CDROM 0x03 /* W Mixer MVD508 Channel 3 - CD-ROM */ + #define P_M_MV508_MIC 0x04 /* W Mixer MVD508 Channel 4 - Microphone */ + #define P_M_MV508_PCM 0x05 /* W Mixer MVD508 Channel 5 - PCM */ + #define P_M_MV508_SPEAKER 0x06 /* W Mixer MVD508 Channel 6 - PC Speaker */ + #define P_M_MV508_SB 0x07 /* W Mixer MVD508 Channel 7 - SB DSP */ + +#define SERIAL_MIXER 0xB88 /* R W Control Serial mixer control (used other ways) */ + #define S_M_PCM_RESET 0x01 /* R W PCM Codec/DSP reset */ + #define S_M_FM_RESET 0x02 /* R W FM FM chip reset */ + #define S_M_SB_RESET 0x04 /* R W PCM SB emulation chip reset */ + #define S_M_MIXER_RESET 0x10 /* R W Mixer Mixer chip reset */ + #define S_M_INTEGRATOR_ENABLE 0x40 /* R W Speaker Enable PC speaker integrator (FORCE RealSound) */ + #define S_M_OPL3_DUAL_MONO 0x80 /* R W FM Set the OPL-3 to dual mono mode */ + +#define PCM_CONTROL 0xF8A /* R W PCM PCM Control Register */ + #define P_C_MIXER_CROSS_FIELD 0x0f + #define P_C_MIXER_CROSS_R_TO_R 0x01 /* R W Mixer Connect Right to Right */ + #define P_C_MIXER_CROSS_L_TO_R 0x02 /* R W Mixer Connect Left to Right */ + #define P_C_MIXER_CROSS_R_TO_L 0x04 /* R W Mixer Connect Right to Left */ + #define P_C_MIXER_CROSS_L_TO_L 0x08 /* R W Mixer Connect Left to Left */ + #define P_C_PCM_DAC_MODE 0x10 /* R W PCM Playback (DAC) mode */ + #define P_C_PCM_ADC_MODE 0x00 /* R W PCM Record (ADC) mode */ + #define P_C_PCM_MONO 0x20 /* R W PCM Mono mode */ + #define P_C_PCM_STEREO 0x00 /* R W PCM Stereo mode */ + #define P_C_PCM_ENABLE 0x40 /* R W PCM Enable PCM engine */ + #define P_C_PCM_DMA_ENABLE 0x80 /* R W PCM Enable DRQ */ + +#define SAMPLE_COUNTER_CONTROL 0x138B /* R W PCM Sample counter control register */ + #define S_C_C_SQUARE_WAVE 0x04 /* R W PCM Square wave generator (use for sample rate) */ + #define S_C_C_RATE 0x06 /* R W PCM Rate generator (use for sample buffer count) */ + #define S_C_C_LSB_THEN_MSB 0x30 /* R W PCM Change all 16 bits, LSB first, then MSB */ + + /* MVD101 and SDK documentations have S_C_C_SAMPLE_RATE and S_C_C_SAMPLE_BUFFER transposed. Only one works :-) */ + #define S_C_C_SAMPLE_RATE 0x00 /* R W PCM Select sample rate timer */ + #define S_C_C_SAMPLE_BUFFER 0x40 /* R W PCM Select sample buffer counter */ + + #define S_C_C_PC_SPEAKER 0x80 /* R W PCM Select PC speaker counter */ + +#define SAMPLE_RATE_TIMER 0x1388 /* W PCM Sample rate timer register (PCM wait interval) */ +#define SAMPLE_BUFFER_COUNTER 0x1389 /* R W PCM Sample buffer counter (DMA buffer size) */ + +#define MIDI_CONTROL 0x178b /* R W MIDI Midi control register */ + #define M_C_ENA_TSTAMP_IRQ 0x01 /* R W MIDI Enable Time Stamp Interrupts */ + #define M_C_ENA_TME_COMP_IRQ 0x02 /* R W MIDI Enable time compare interrupts */ + #define M_C_ENA_INPUT_IRQ 0x04 /* R W MIDI Enable input FIFO interrupts */ + #define M_C_ENA_OUTPUT_IRQ 0x08 /* R W MIDI Enable output FIFO interrupts */ + #define M_C_ENA_OUTPUT_HALF_IRQ 0x10 /* R W MIDI Enable output FIFO half full interrupts */ + #define M_C_RESET_INPUT_FIFO 0x20 /* R W MIDI Reset input FIFO pointer */ + #define M_C_RESET_OUTPUT_FIFO 0x40 /* R W MIDI Reset output FIFO pointer */ + #define M_C_ENA_THRU_MODE 0x80 /* R W MIDI Echo input to output (THRU) */ + +#define MIDI_STATUS 0x1B88 /* R W MIDI Midi (interrupt) status register */ + #define M_S_TIMESTAMP 0x01 /* R W MIDI Midi time stamp interrupt occurred */ + #define M_S_COMPARE 0x02 /* R W MIDI Midi compare time interrupt occurred */ + #define M_S_INPUT_AVAIL 0x04 /* R W MIDI Midi input data available interrupt occurred */ + #define M_S_OUTPUT_EMPTY 0x08 /* R W MIDI Midi output FIFO empty interrupt occurred */ + #define M_S_OUTPUT_HALF_EMPTY 0x10 /* R W MIDI Midi output FIFO half empty interrupt occurred */ + #define M_S_INPUT_OVERRUN 0x20 /* R W MIDI Midi input overrun error occurred */ + #define M_S_OUTPUT_OVERRUN 0x40 /* R W MIDI Midi output overrun error occurred */ + #define M_S_FRAMING_ERROR 0x80 /* R W MIDI Midi input framing error occurred */ + +#define MIDI_FIFO_STATUS 0x1B89 /* R W MIDI Midi fifo status */ +#define MIDI_DATA 0x178A /* R W MIDI Midi data register */ +#define MIDI_INPUT_AVAILABLE 0x0f /* RW MIDI */ diff --git a/sys/i386/isa/sound/pas2_card.c b/sys/i386/isa/sound/pas2_card.c new file mode 100644 index 0000000000..a341273ce3 --- /dev/null +++ b/sys/i386/isa/sound/pas2_card.c @@ -0,0 +1,343 @@ +#define _PAS2_CARD_C_ +#define SND_SA_INTERRUPT +/* + * linux/kernel/chr_drv/sound/pas2_card.c + * + * Detection routine for the Pro Audio Spectrum cards. + * + * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) Craig Metz + * (cmetz@thor.tjhsst.edu) See COPYING for further details. Should be + * distributed with this file. + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS) + +#define DEFINE_TRANSLATIONS +#include "pas.h" + +/* + * The Address Translation code is used to convert I/O register addresses to + * be relative to the given base -register + */ + +int translat_code; +static int pas_intr_mask = 0; +static int pas_irq = 0; + +static char pas_model; +static char *pas_model_names[] = +{"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16"}; + +/* pas_read() and pas_write() are equivalents of INB() and OUTB() */ +/* These routines perform the I/O address translation required */ +/* to support other than the default base address */ + +unsigned char +pas_read (int ioaddr) +{ + return INB (ioaddr ^ translat_code); +} + +void +pas_write (unsigned char data, int ioaddr) +{ + OUTB (data, ioaddr ^ translat_code); +} + +void +pas2_msg (char *foo) +{ + printk (" PAS2: %s.\n", foo); +} + +/******************* Begin of the Interrupt Handler ********************/ + +void +pasintr (int unused) +{ + int status; + + status = pas_read (INTERRUPT_STATUS); + pas_write (status, INTERRUPT_STATUS); /* Clear interrupt */ + + if (status & I_S_PCM_SAMPLE_BUFFER_IRQ) + { +#ifndef EXCLUDE_AUDIO + pas_pcm_interrupt (status, 1); +#endif + status &= ~I_S_PCM_SAMPLE_BUFFER_IRQ; + } + if (status & I_S_MIDI_IRQ) + { +#ifndef EXCLUDE_MIDI +#ifdef EXCLUDE_PRO_MIDI + pas_midi_interrupt (); +#endif +#endif + status &= ~I_S_MIDI_IRQ; + } + +} + +static int +set_pas_irq (int interrupt_level) +{ +#ifdef linux + int retcode; + struct sigaction sa; + + pas_write (0xff, INTERRUPT_STATUS); /* Reset pending interrupts */ + + sa.sa_handler = pasintr; + +#ifdef SND_SA_INTERRUPT + sa.sa_flags = SA_INTERRUPT; +#else + sa.sa_flags = 0; +#endif + + sa.sa_mask = 0; + sa.sa_restorer = NULL; + + retcode = irqaction (interrupt_level, &sa); + + if (retcode < 0) + { + printk ("ProAudioSpectrum: IRQ%d already in use\n", interrupt_level); + } + return retcode; +#else + /* # error This routine does not work with this OS */ +#endif +} + +int +pas_set_intr (int mask) +{ + int err; + + if (!mask) + return 0; + + if (!pas_intr_mask) + { + if ((err = set_pas_irq (pas_irq)) < 0) + return err; + } + pas_intr_mask |= mask; + + pas_write (pas_intr_mask, INTERRUPT_MASK); + return 0; +} + +int +pas_remove_intr (int mask) +{ + if (!mask) + return 0; + + pas_intr_mask &= ~mask; + pas_write (pas_intr_mask, INTERRUPT_MASK); + + if (!pas_intr_mask) + { + RELEASE_IRQ (pas_irq); + } + return 0; +} + +/******************* End of the Interrupt handler **********************/ + +/******************* Begin of the Initialization Code ******************/ + +int +config_pas_hw (struct address_info *hw_config) +{ + char ok = 1; + + pas_irq = hw_config->irq; + + pas_write (0x00, INTERRUPT_MASK); + + pas_write (0x36, SAMPLE_COUNTER_CONTROL); /* Local timer control + * register */ + + pas_write (0x36, SAMPLE_RATE_TIMER); /* Sample rate timer (16 bit) */ + pas_write (0, SAMPLE_RATE_TIMER); + + pas_write (0x74, SAMPLE_COUNTER_CONTROL); /* Local timer control + * register */ + + pas_write (0x74, SAMPLE_BUFFER_COUNTER); /* Sample count register (16 + * bit) */ + pas_write (0, SAMPLE_BUFFER_COUNTER); + + pas_write (F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER | F_F_MIXER_UNMUTE | 1, FILTER_FREQUENCY); + pas_write (P_C_PCM_DMA_ENABLE | P_C_PCM_MONO | P_C_PCM_DAC_MODE | P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R, PCM_CONTROL); + pas_write (S_M_PCM_RESET | S_M_FM_RESET | S_M_SB_RESET | S_M_MIXER_RESET /* | S_M_OPL3_DUAL_MONO */ , SERIAL_MIXER); + + pas_write (I_C_1_BOOT_RESET_ENABLE, IO_CONFIGURATION_1); + + if (pas_irq < 0 || pas_irq > 15) + { + printk ("PAS2: Invalid IRQ %d", pas_irq); + ok = 0; + } + else + { + pas_write (I_C_3_PCM_IRQ_translate[pas_irq], IO_CONFIGURATION_3); + if (!I_C_3_PCM_IRQ_translate[pas_irq]) + { + printk ("PAS2: Invalid IRQ %d", pas_irq); + ok = 0; + } + } + + if (hw_config->dma < 0 || hw_config->dma > 7) + { + printk ("PAS2: Invalid DMA selection %d", hw_config->dma); + ok = 0; + } + else + { + pas_write (I_C_2_PCM_DMA_translate[hw_config->dma], IO_CONFIGURATION_2); + if (!I_C_2_PCM_DMA_translate[hw_config->dma]) + { + printk ("PAS2: Invalid DMA selection %d", hw_config->dma); + ok = 0; + } + } + +#ifdef BROKEN_BUS_CLOCK + pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND | S_C_1_FM_EMULATE_CLOCK, SYSTEM_CONFIGURATION_1); +#else + /* pas_write(S_C_1_PCS_ENABLE, SYSTEM_CONFIGURATION_1); */ + pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND, SYSTEM_CONFIGURATION_1); +#endif + /* pas_write(S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2); Don't do this */ + pas_write (0x18, SYSTEM_CONFIGURATION_3); /* ??? */ + + pas_write (F_F_MIXER_UNMUTE | 0x01, FILTER_FREQUENCY); /* Sets mute off and + * selects filter rate + * of 17.897 kHz */ + + if (pas_model == PAS_16) + pas_write (8, PRESCALE_DIVIDER); + else + pas_write (0, PRESCALE_DIVIDER); + + pas_write (P_M_MV508_ADDRESS | 5, PARALLEL_MIXER); + pas_write (5, PARALLEL_MIXER); + +#if !defined(EXCLUDE_SB_EMULATION) || !defined(EXCLUDE_SB) + + /* Turn on Sound Blaster compatibility */ + /* bit 1 = SB emulation */ + /* bit 0 = MPU401 emulation (CDPC only :-( ) */ + pas_write (0x02, COMPATIBILITY_ENABLE); + + /* "Emulation address" */ + pas_write ((SBC_BASE >> 4) & 0x0f, EMULATION_ADDRESS); +#endif + + if (!ok) + pas2_msg ("Driver not enabled"); + + return ok; +} + +int +detect_pas_hw (struct address_info *hw_config) +{ + unsigned char board_id, foo; + + /* + * WARNING: Setting an option like W:1 or so that disables warm boot reset + * of the card will screw up this detect code something fierce. Adding code + * to handle this means possibly interfering with other cards on the bus if + * you have something on base port 0x388. SO be forewarned. + */ + + OUTB (0xBC, MASTER_DECODE); /* Talk to first board */ + OUTB (hw_config->io_base >> 2, MASTER_DECODE); /* Set base address */ + translat_code = PAS_DEFAULT_BASE ^ hw_config->io_base; + pas_write (1, WAIT_STATE); /* One wait-state */ + + board_id = pas_read (INTERRUPT_MASK); + + if (board_id == 0xff) + return 0; + + /* + * We probably have a PAS-series board, now check for a PAS2-series board + * by trying to change the board revision bits. PAS2-series hardware won't + * let you do this - the bits are read-only. + */ + + foo = board_id ^ 0xe0; + + pas_write (foo, INTERRUPT_MASK); + foo = INB (INTERRUPT_MASK); + pas_write (board_id, INTERRUPT_MASK); + + if (board_id != foo) /* Not a PAS2 */ + return 0; + + if ((pas_model = O_M_1_to_card[pas_read (OPERATION_MODE_1) & 0x0f])); + + return pas_model; +} + +long +attach_pas_card (long mem_start, struct address_info *hw_config) +{ + pas_irq = hw_config->irq; + + if (detect_pas_hw (hw_config)) + { + + if ((pas_model = O_M_1_to_card[pas_read (OPERATION_MODE_1) & 0x0f])) + { + printk ("snd3: <%s rev %d>", pas_model_names[(int) pas_model], pas_read (BOARD_REV_ID)); + } + + if (config_pas_hw (hw_config)) + { + +#ifndef EXCLUDE_AUDIO + mem_start = pas_pcm_init (mem_start, hw_config); +#endif + +# if !defined(EXCLUDE_SB_EMULATION) && !defined(EXCLUDE_SB) + + sb_dsp_disable_midi (); /* The SB emulation don't support + * midi */ +# endif + +#ifndef EXCLUDE_YM3812 + enable_opl3_mode (0x388, 0x38a, 0); +#endif + +#ifndef EXCLUDE_MIDI +#ifdef EXCLUDE_PRO_MIDI + mem_start = pas_midi_init (mem_start); +#endif +#endif + + pas_init_mixer (); + } + } + + printk("\n"); + return mem_start; +} + +int +probe_pas (struct address_info *hw_config) +{ + return detect_pas_hw (hw_config); +} + +#endif diff --git a/sys/i386/isa/sound/pas2_midi.c b/sys/i386/isa/sound/pas2_midi.c new file mode 100644 index 0000000000..a5fd6044db --- /dev/null +++ b/sys/i386/isa/sound/pas2_midi.c @@ -0,0 +1,269 @@ +/* + * linux/kernel/chr_drv/sound/pas2_midi.c + * + * The low level driver for the PAS Midi Interface. + * + * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further + * details. Should be distributed with this file. + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#include "pas.h" + +#if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_MIDI) && defined(EXCLUDE_PRO_MIDI) + +static int midi_busy = 0, input_opened = 0; +static int my_dev; +static volatile int ofifo_bytes = 0; + +static unsigned char tmp_queue[256]; +static volatile int qlen; +static volatile unsigned char qhead, qtail; + +static int +pas_midi_open (int dev, int mode) +{ + int err; + unsigned long flags; + unsigned char ctrl; + + + if (midi_busy) + { + printk ("PAS2: Midi busy\n"); + return RET_ERROR (EBUSY); + } + + /* Reset input and output FIFO pointers */ + pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO, + MIDI_CONTROL); + + DISABLE_INTR (flags); + + if ((err = pas_set_intr (I_M_MIDI_IRQ_ENABLE)) < 0) + return err; + + /* Enable input available and output FIFO empty interrupts */ + + ctrl = 0; + input_opened = 0; + + if (mode == OPEN_READ || mode == OPEN_READWRITE) + { + ctrl |= M_C_ENA_INPUT_IRQ;/* Enable input */ + input_opened = 1; + } + + if (mode == OPEN_WRITE || mode == OPEN_READWRITE) + { + ctrl |= M_C_ENA_OUTPUT_IRQ | /* Enable output */ + M_C_ENA_OUTPUT_HALF_IRQ; + } + + pas_write (ctrl, + MIDI_CONTROL); + + /* Acknowledge any pending interrupts */ + + pas_write (0xff, MIDI_STATUS); + ofifo_bytes = 0; + + RESTORE_INTR (flags); + + midi_busy = 1; + qlen = qhead = qtail = 0; + return 0; +} + +static void +pas_midi_close (int dev) +{ + + /* Reset FIFO pointers, disable intrs */ + pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO, MIDI_CONTROL); + + pas_remove_intr (I_M_MIDI_IRQ_ENABLE); + midi_busy = 0; +} + +static int +dump_to_midi (unsigned char midi_byte) +{ + int fifo_space, x; + + fifo_space = ((x = pas_read (MIDI_FIFO_STATUS)) >> 4) & 0x0f; + + if (fifo_space == 15 || (fifo_space < 2 && ofifo_bytes > 13)) /* Fifo full */ + { + return 0; /* Upper layer will call again */ + } + + ofifo_bytes++; + + pas_write (midi_byte, MIDI_DATA); + + return 1; +} + +static int +pas_midi_out (int dev, unsigned char midi_byte) +{ + + unsigned long flags; + + /* + * Drain the local queue first + */ + + DISABLE_INTR (flags); + + while (qlen && dump_to_midi (tmp_queue[qhead])) + { + qlen--; + qhead++; + } + + RESTORE_INTR (flags); + + /* + * Output the byte if the local queue is empty. + */ + + if (!qlen) + if (dump_to_midi (midi_byte)) + return 1; /* OK */ + + /* + * Put to the local queue + */ + + if (qlen >= 256) + return 0; /* Local queue full */ + + DISABLE_INTR (flags); + + tmp_queue[qtail] = midi_byte; + qlen++; + qtail++; + + RESTORE_INTR (flags); + + return 1; +} + +static int +pas_midi_start_read (int dev) +{ + return 0; +} + +static int +pas_midi_end_read (int dev) +{ + return 0; +} + +static int +pas_midi_ioctl (int dev, unsigned cmd, unsigned arg) +{ + return RET_ERROR (EINVAL); +} + +static void +pas_midi_kick (int dev) +{ + ofifo_bytes = 0; +} + +static int +pas_buffer_status (int dev) +{ + return !qlen; +} + +static struct midi_operations pas_midi_operations = +{ + {"Pro Audio Spectrum", 0}, + pas_midi_open, + pas_midi_close, + pas_midi_ioctl, + pas_midi_out, + pas_midi_start_read, + pas_midi_end_read, + pas_midi_kick, + NULL, /* command */ + pas_buffer_status +}; + +long +pas_midi_init (long mem_start) +{ + my_dev = num_midis; + midi_devs[num_midis++] = &pas_midi_operations; + return mem_start; +} + +void +pas_midi_interrupt (void) +{ + unsigned char stat; + int i, incount; + unsigned long flags; + + stat = pas_read (MIDI_STATUS); + + if (stat & M_S_INPUT_AVAIL) /* Input byte available */ + { + incount = pas_read (MIDI_FIFO_STATUS) & 0x0f; /* Input FIFO count */ + if (!incount) + incount = 16; + + for (i = 0; i < incount; i++) + if (input_opened) + { + sequencer_midi_input (my_dev, pas_read (MIDI_DATA)); + } + else + pas_read (MIDI_DATA); /* Flush */ + } + + if (stat & (M_S_OUTPUT_EMPTY | M_S_OUTPUT_HALF_EMPTY)) + { + if (!(stat & M_S_OUTPUT_EMPTY)) + { + ofifo_bytes = 8; + } + else + { + ofifo_bytes = 0; + } + + DISABLE_INTR (flags); + + while (qlen && dump_to_midi (tmp_queue[qhead])) + { + qlen--; + qhead++; + } + + RESTORE_INTR (flags); + } + + if (stat & M_S_FRAMING_ERROR) + printk ("MIDI framing error\n"); + + if (stat & M_S_OUTPUT_OVERRUN) + { + printk ("MIDI output overrun %02x,%02x,%d \n", pas_read (MIDI_FIFO_STATUS), stat, ofifo_bytes); + ofifo_bytes = 100; + } + + pas_write (stat, MIDI_STATUS);/* Acknowledge interrupts */ +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/pas2_mixer.c b/sys/i386/isa/sound/pas2_mixer.c new file mode 100644 index 0000000000..3aa207224e --- /dev/null +++ b/sys/i386/isa/sound/pas2_mixer.c @@ -0,0 +1,481 @@ +#define _PAS2_MIXER_C_ + +/* + * linux/kernel/chr_drv/sound/pas2_mixer.c + * + * Mixer routines for the Pro Audio Spectrum cards. + * + * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) Craig Metz + * (cmetz@thor.tjhsst.edu) See COPYING for further details. Should be + * distributed with this file. + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS) + +#include "pas.h" + +#define TRACE(what) /* (what) */ + +extern int translat_code; + +static int rec_devices = (SOUND_MASK_MIC); /* Default recording source */ +static int mode_control = 0; + +#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_ALTPCM) + +#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD /*|SOUND_MASK_ALTPCM*/ | SOUND_MASK_IMIX | \ + SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV | \ + SOUND_MASK_MUTE | SOUND_MASK_ENHANCE | SOUND_MASK_LOUD) + +static unsigned short levels[SOUND_MIXER_NRDEVICES] = +{ + 0x3232, /* Master Volume */ + 0x3232, /* Bass */ + 0x3232, /* Treble */ + 0x5050, /* FM */ + 0x4b4b, /* PCM */ + 0x3232, /* PC Speaker */ + 0x4b4b, /* Ext Line */ + 0x4b4b, /* Mic */ + 0x4b4b, /* CD */ + 0x6464, /* Recording monitor */ + 0x4b4b, /* SB PCM */ + 0x6464}; /* Recording level */ + +static int +mixer_output (int right_vol, int left_vol, int div, int bits, + int mixer /* Input or output mixer */ ) +{ + int left = left_vol * div / 100; + int right = right_vol * div / 100; + + /* + * The Revision D cards have a problem with their MVA508 interface. The + * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and + * MSBs out of the output byte and to do a 16-bit out to the mixer port - + * 1. We don't need to do this because the call to pas_write more than + * compensates for the timing problems. + */ + + if (bits & P_M_MV508_MIXER) + { /* Select input or output mixer */ + left |= mixer; + right |= mixer; + } + + if (bits == P_M_MV508_BASS || bits == P_M_MV508_TREBLE) + { /* Bass and trebble are mono devices */ + pas_write (P_M_MV508_ADDRESS | bits, PARALLEL_MIXER); + pas_write (left, PARALLEL_MIXER); + right_vol = left_vol; + } + else + { + pas_write (P_M_MV508_ADDRESS | P_M_MV508_LEFT | bits, PARALLEL_MIXER); + pas_write (left, PARALLEL_MIXER); + pas_write (P_M_MV508_ADDRESS | P_M_MV508_RIGHT | bits, PARALLEL_MIXER); + pas_write (right, PARALLEL_MIXER); + } + + return (left_vol | (right_vol << 8)); +} + +void +set_mode (int new_mode) +{ + pas_write (P_M_MV508_ADDRESS | P_M_MV508_MODE, PARALLEL_MIXER); + pas_write (new_mode, PARALLEL_MIXER); + + mode_control = new_mode; +} + +static int +pas_mixer_set (int whichDev, unsigned int level) +{ + int left, right, devmask, changed, i, mixer = 0; + + TRACE (printk ("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level)); + + left = level & 0x7f; + right = (level & 0x7f00) >> 8; + + if (whichDev < SOUND_MIXER_NRDEVICES) + if ((1 << whichDev) & rec_devices) + mixer = P_M_MV508_INPUTMIX; + else + mixer = P_M_MV508_OUTPUTMIX; + + switch (whichDev) + { + case SOUND_MIXER_VOLUME: /* Master volume (0-63) */ + levels[whichDev] = mixer_output (right, left, 63, P_M_MV508_MASTER_A, 0); + break; + + /* + * Note! Bass and Treble are mono devices. Will use just the left + * channel. + */ + case SOUND_MIXER_BASS: /* Bass (0-12) */ + levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_BASS, 0); + break; + case SOUND_MIXER_TREBLE: /* Treble (0-12) */ + levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_TREBLE, 0); + break; + + case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-31) */ + levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_FM, mixer); + break; + case SOUND_MIXER_PCM: /* PAS PCM (0-31) */ + levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_PCM, mixer); + break; + case SOUND_MIXER_ALTPCM: /* SB PCM (0-31) */ + levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SB, mixer); + break; + case SOUND_MIXER_SPEAKER: /* PC speaker (0-31) */ + levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SPEAKER, mixer); + break; + case SOUND_MIXER_LINE: /* External line (0-31) */ + levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_LINE, mixer); + break; + case SOUND_MIXER_CD: /* CD (0-31) */ + levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_CDROM, mixer); + break; + case SOUND_MIXER_MIC: /* External microphone (0-31) */ + levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_MIC, mixer); + break; + case SOUND_MIXER_IMIX: /* Recording monitor (0-31) (Only available + * on the Output Mixer) */ + levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_IMIXER, + P_M_MV508_OUTPUTMIX); + break; + case SOUND_MIXER_RECLEV: /* Recording level (0-15) */ + levels[whichDev] = mixer_output (right, left, 15, P_M_MV508_MASTER_B, 0); + break; + + case SOUND_MIXER_MUTE: + return 0; + break; + + case SOUND_MIXER_ENHANCE: + i = 0; + level &= 0x7f; + if (level) + i = (level / 20) - 1; + + mode_control &= ~P_M_MV508_ENHANCE_BITS; + mode_control |= P_M_MV508_ENHANCE_BITS; + set_mode (mode_control); + + if (i) + i = (i + 1) * 20; + return i; + break; + + case SOUND_MIXER_LOUD: + mode_control &= ~P_M_MV508_LOUDNESS; + if (level) + mode_control |= P_M_MV508_LOUDNESS; + set_mode (mode_control); + return !!level; /* 0 or 1 */ + break; + + case SOUND_MIXER_RECSRC: + devmask = level & POSSIBLE_RECORDING_DEVICES; + + changed = devmask ^ rec_devices; + rec_devices = devmask; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (changed & (1 << i)) + { + pas_mixer_set (i, levels[i]); + } + return rec_devices; + break; + + default: + return RET_ERROR (EINVAL); + } + + return (levels[whichDev]); +} + +/*****/ + +static int +mixer_set_levels (struct sb_mixer_levels *user_l) +{ +#define cmix(v) ((((v.r*100+7)/15)<<8)| ((v.l*100+7)/15)) + + struct sb_mixer_levels l; + + IOCTL_FROM_USER ((char *) &l, (char *) user_l, 0, sizeof (l)); + + if (l.master.l & ~0xF || l.master.r & ~0xF + || l.line.l & ~0xF || l.line.r & ~0xF + || l.voc.l & ~0xF || l.voc.r & ~0xF + || l.fm.l & ~0xF || l.fm.r & ~0xF + || l.cd.l & ~0xF || l.cd.r & ~0xF + || l.mic & ~0x7) + return (RET_ERROR (EINVAL)); + + pas_mixer_set (SOUND_MIXER_VOLUME, cmix (l.master)); + pas_mixer_set (SOUND_MIXER_LINE, cmix (l.line)); + pas_mixer_set (SOUND_MIXER_PCM, cmix (l.voc)); + pas_mixer_set (SOUND_MIXER_ALTPCM, cmix (l.voc)); + pas_mixer_set (SOUND_MIXER_SYNTH, cmix (l.fm)); + pas_mixer_set (SOUND_MIXER_CD, cmix (l.cd)); + pas_mixer_set (SOUND_MIXER_MIC, ((l.mic * 100 + 3) / 7) | (((l.mic * 100 + 3) / 7) << 8)); + return (0); +} + +/* + * This sets aspects of the Mixer that are not volume levels. (Recording + * source, filter level, I/O filtering, and stereo.) + */ +static int +mixer_set_params (struct sb_mixer_params *user_p) +{ + struct sb_mixer_params p; + S_BYTE val; + int src; + unsigned long flags; + + IOCTL_FROM_USER ((char *) &p, (char *) user_p, 0, sizeof (p)); + + if (p.record_source != SRC_MIC + && p.record_source != SRC_CD + && p.record_source != SRC_LINE) + return (RET_ERROR (EINVAL)); + + /* + * I'm not sure if this is The Right Thing. Should stereo be entirely + * under control of DSP? I like being able to toggle it while a sound is + * playing, so I do this... because I can. + */ + + DISABLE_INTR (flags); + + val = (pas_read (PCM_CONTROL) & ~P_C_MIXER_CROSS_FIELD) | P_C_MIXER_CROSS_R_TO_R | P_C_MIXER_CROSS_L_TO_L; + if (!p.dsp_stereo) + val |= (P_C_MIXER_CROSS_R_TO_L | P_C_MIXER_CROSS_L_TO_R); /* Mono */ + pas_write (val, PCM_CONTROL); + + RESTORE_INTR (flags); + + switch (p.record_source) + { + case SRC_CD: + src = SOUND_MASK_CD; + break; + + case SRC_LINE: + src = SOUND_MASK_LINE; + break; + + default: + src = SOUND_MASK_MIC; + break; + } + + pas_mixer_set (SOUND_MIXER_RECSRC, src); + + /* + * setmixer (OUT_FILTER, ((dsp_stereo ? STEREO_DAC : MONO_DAC) | + * (p.filter_output ? FILT_ON : FILT_OFF))); + */ + return (0); +} + +static int +getmixer (int dev, int chn) +{ + if (chn == P_M_MV508_RIGHT) + { + return (levels[dev] >> 8) & 0x7f; + } + else + { + return levels[dev] & 0x7f; + } +} + +/* Read the current mixer level settings into the user's struct. */ +static int +mixer_get_levels (struct sb_mixer_levels *user_l) +{ + + struct sb_mixer_levels l; + + l.master.r = ((((levels[SOUND_MIXER_VOLUME] >> 8) & 0x7f) * 15) + 50) / 100; /* Master */ + l.master.l = (((levels[SOUND_MIXER_VOLUME] & 0x7f) * 15) + 50) / 100; /* Master */ + + l.line.r = ((getmixer (SOUND_MIXER_LINE, P_M_MV508_RIGHT) * 15) + 50) / 100; /* Line */ + l.line.l = ((getmixer (SOUND_MIXER_LINE, P_M_MV508_LEFT) * 15) + 50) / 100; + + l.voc.r = ((getmixer (SOUND_MIXER_PCM, P_M_MV508_RIGHT) * 15) + 50) / 100; /* DAC */ + l.voc.l = ((getmixer (SOUND_MIXER_PCM, P_M_MV508_LEFT) * 15) + 50) / 100; + + l.fm.r = ((getmixer (SOUND_MIXER_SYNTH, P_M_MV508_RIGHT) * 15) + 50) / 100; /* FM */ + l.fm.l = ((getmixer (SOUND_MIXER_SYNTH, P_M_MV508_LEFT) * 15) + 50) / 100; + + l.cd.r = ((getmixer (SOUND_MIXER_CD, P_M_MV508_RIGHT) * 15) + 50) / 100; /* CD */ + l.cd.l = ((getmixer (SOUND_MIXER_CD, P_M_MV508_LEFT) * 15) + 50) / 100; + + l.mic = ((getmixer (SOUND_MIXER_MIC, P_M_MV508_LEFT) * 7) + 50) / 100; /* Microphone */ + + IOCTL_TO_USER ((char *) user_l, 0, (char *) &l, sizeof (l)); + return (0); +} + +/* Read the current mixer parameters into the user's struct. */ +static int +mixer_get_params (struct sb_mixer_params *user_params) +{ + S_BYTE val; + struct sb_mixer_params params; + + switch (rec_devices) + { + case SOUND_MASK_CD: + params.record_source = SRC_CD; + break; + + case SOUND_MASK_LINE: + params.record_source = SRC_LINE; + break; + + case SOUND_MASK_MIC: + params.record_source = SRC_MIC; + break; + + default: + params.record_source = SRC_MIC; + pas_mixer_set (SOUND_MIXER_RECSRC, SOUND_MASK_MIC); /* Adjust */ + } + + params.hifreq_filter = OFF; + params.filter_input = OFF; + params.filter_output = OFF; + + val = INB (PCM_CONTROL); + params.dsp_stereo = ((val & P_C_MIXER_CROSS_FIELD) == (P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R)); + + IOCTL_TO_USER ((char *) user_params, 0, (char *) ¶ms, sizeof (params)); + return (0); +} + +/*****/ + +static void +pas_mixer_reset (void) +{ + int foo; + + TRACE (printk ("pas2_mixer.c: void pas_mixer_reset(void)\n")); + + for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++) + pas_mixer_set (foo, levels[foo]); + + set_mode (P_M_MV508_LOUDNESS | P_M_MV508_ENHANCE_40); +} + +int +pas_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) +{ + TRACE (printk ("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); + + if (((cmd >> 8) & 0xff) == 'M') + { + if (cmd & IOC_IN) + return IOCTL_OUT (arg, pas_mixer_set (cmd & 0xff, IOCTL_IN (arg))); + else + { /* Read parameters */ + + switch (cmd & 0xff) + { + + case SOUND_MIXER_RECSRC: + return IOCTL_OUT (arg, rec_devices); + break; + + case SOUND_MIXER_STEREODEVS: + return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE)); + break; + + case SOUND_MIXER_DEVMASK: + return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES); + break; + + case SOUND_MIXER_RECMASK: + return IOCTL_OUT (arg, POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES); + break; + + case SOUND_MIXER_CAPS: + return IOCTL_OUT (arg, 0); /* No special capabilities */ + break; + + case SOUND_MIXER_MUTE: + return IOCTL_OUT (arg, 0); /* No mute yet */ + break; + + case SOUND_MIXER_ENHANCE: + if (!(mode_control & P_M_MV508_ENHANCE_BITS)) + return IOCTL_OUT (arg, 0); + return IOCTL_OUT (arg, ((mode_control & P_M_MV508_ENHANCE_BITS) + 1) * 20); + break; + + case SOUND_MIXER_LOUD: + if (mode_control & P_M_MV508_LOUDNESS) + return IOCTL_OUT (arg, 1); + return IOCTL_OUT (arg, 0); + break; + + default: + return IOCTL_OUT (arg, levels[cmd & 0xff]); + } + } + } + else + { + switch (cmd) + { + case MIXER_IOCTL_SET_LEVELS: + mixer_set_levels ((struct sb_mixer_levels *) arg); + return mixer_get_levels ((struct sb_mixer_levels *) arg); + case MIXER_IOCTL_SET_PARAMS: + mixer_set_params ((struct sb_mixer_params *) arg); + return mixer_get_params ((struct sb_mixer_params *) arg); + case MIXER_IOCTL_READ_LEVELS: + return mixer_get_levels ((struct sb_mixer_levels *) arg); + case MIXER_IOCTL_READ_PARAMS: + return mixer_get_params ((struct sb_mixer_params *) arg); + case MIXER_IOCTL_RESET: + pas_mixer_reset (); + return (0); + default: + return RET_ERROR (EINVAL); + } + } + return RET_ERROR (EINVAL); +} + +static struct mixer_operations pas_mixer_operations = +{ + pas_mixer_ioctl +}; + +int +pas_init_mixer (void) +{ + pas_mixer_reset (); + + mixer_devs[num_mixers++] = &pas_mixer_operations; + return 1; +} + +#endif diff --git a/sys/i386/isa/sound/pas2_pcm.c b/sys/i386/isa/sound/pas2_pcm.c new file mode 100644 index 0000000000..f4634c5586 --- /dev/null +++ b/sys/i386/isa/sound/pas2_pcm.c @@ -0,0 +1,412 @@ +#define _PAS2_PCM_C_ +/* + * linux/kernel/chr_drv/sound/pas2_pcm.c + * + * The low level driver for the Pro Audio Spectrum ADC/DAC. + * + * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) Craig Metz + * (cmetz@thor.tjhsst.edu) See COPYING for further details. Should be + * distributed with this file. + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#include "pas.h" + +#if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_AUDIO) + +#define TRACE(WHAT) /* (WHAT) */ + +#define PAS_PCM_INTRBITS (0x08) +/* Sample buffer timer interrupt enable */ + +#define PCM_NON 0 +#define PCM_DAC 1 +#define PCM_ADC 2 + +static unsigned long pcm_speed = 0; /* sampling rate */ +static unsigned char pcm_channels = 1; /* channels/sample (1 or 2) */ +static unsigned char pcm_bits = 8; /* bits/sample (8 or 16) */ +static unsigned char pcm_filter = 0; /* filter FLAG */ +static unsigned char pcm_mode = PCM_NON; +static unsigned long pcm_count = 0; +static unsigned short pcm_bitsok = 8; /* mask of OK bits */ +static int my_devnum = 0; + +int +pcm_set_speed (int arg) +{ + int foo, tmp; + unsigned long flags; + + if (arg > 44100) + arg = 44100; + if (arg < 5000) + arg = 5000; + + foo = 1193180 / arg; + arg = 1193180 / foo; + + if (pcm_channels & 2) + foo = foo >> 1; + + pcm_speed = arg; + + tmp = pas_read (FILTER_FREQUENCY); + + DISABLE_INTR (flags); + + pas_write (tmp & ~(F_F_PCM_RATE_COUNTER | F_F_PCM_BUFFER_COUNTER), FILTER_FREQUENCY); + pas_write (S_C_C_SAMPLE_RATE | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL); + pas_write (foo & 0xff, SAMPLE_RATE_TIMER); + pas_write ((foo >> 8) & 0xff, SAMPLE_RATE_TIMER); + pas_write (tmp, FILTER_FREQUENCY); + + RESTORE_INTR (flags); + + return pcm_speed; +} + +int +pcm_set_channels (int arg) +{ + + if ((arg != 1) && (arg != 2)) + return pcm_channels; + + if (arg != pcm_channels) + { + pas_write (pas_read (PCM_CONTROL) ^ P_C_PCM_MONO, PCM_CONTROL); + + pcm_channels = arg; + pcm_set_speed (pcm_speed);/* The speed must be reinitialized */ + } + + return pcm_channels; +} + +int +pcm_set_bits (int arg) +{ + if ((arg & pcm_bitsok) != arg) + return pcm_bits; + + if (arg != pcm_bits) + { + pas_write (pas_read (SYSTEM_CONFIGURATION_2) ^ S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2); + + pcm_bits = arg; + } + + return pcm_bits; +} + +static int +pas_pcm_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) +{ + TRACE (printk ("pas2_pcm.c: static int pas_pcm_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); + + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (local) + return pcm_set_speed (arg); + return IOCTL_OUT (arg, pcm_set_speed (IOCTL_IN (arg))); + break; + + case SOUND_PCM_READ_RATE: + if (local) + return pcm_speed; + return IOCTL_OUT (arg, pcm_speed); + break; + + case SNDCTL_DSP_STEREO: + if (local) + return pcm_set_channels (arg + 1) - 1; + return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg) + 1) - 1); + break; + + case SOUND_PCM_WRITE_CHANNELS: + return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg))); + break; + + case SOUND_PCM_READ_CHANNELS: + if (local) + return pcm_channels; + return IOCTL_OUT (arg, pcm_channels); + break; + + case SNDCTL_DSP_SAMPLESIZE: + if (local) + return pcm_set_bits (arg); + return IOCTL_OUT (arg, pcm_set_bits (IOCTL_IN (arg))); + break; + + case SOUND_PCM_READ_BITS: + if (local) + return pcm_bits; + return IOCTL_OUT (arg, pcm_bits); + + case SOUND_PCM_WRITE_FILTER: /* NOT YET IMPLEMENTED */ + if (IOCTL_IN (arg) > 1) + return IOCTL_OUT (arg, RET_ERROR (EINVAL)); + break; + + pcm_filter = IOCTL_IN (arg); + case SOUND_PCM_READ_FILTER: + return IOCTL_OUT (arg, pcm_filter); + break; + + default: + return RET_ERROR (EINVAL); + } + + return RET_ERROR (EINVAL); +} + +static void +pas_pcm_reset (int dev) +{ + TRACE (printk ("pas2_pcm.c: static void pas_pcm_reset(void)\n")); + + pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL); +} + +static int +pas_pcm_open (int dev, int mode) +{ + int err; + + TRACE (printk ("pas2_pcm.c: static int pas_pcm_open(int mode = %X)\n", mode)); + + if (mode != OPEN_READ && mode != OPEN_WRITE) + { + printk ("PAS2: Attempt to open PCM device for simultaneous read and write"); + return RET_ERROR (EINVAL); + } + + if ((err = pas_set_intr (PAS_PCM_INTRBITS)) < 0) + return err; + + if (!DMAbuf_open_dma (dev)) + { + pas_remove_intr (PAS_PCM_INTRBITS); + return RET_ERROR (EBUSY); + } + + pcm_count = 0; + + pcm_set_bits (8); + pcm_set_channels (1); + pcm_set_speed (DSP_DEFAULT_SPEED); + + return 0; +} + +static void +pas_pcm_close (int dev) +{ + unsigned long flags; + + TRACE (printk ("pas2_pcm.c: static void pas_pcm_close(void)\n")); + + DISABLE_INTR (flags); + + pas_pcm_reset (dev); + DMAbuf_close_dma (dev); + pas_remove_intr (PAS_PCM_INTRBITS); + pcm_mode = PCM_NON; + + RESTORE_INTR (flags); +} + +static void +pas_pcm_output_block (int dev, unsigned long buf, int count, int intrflag) +{ + unsigned long flags, cnt; + + TRACE (printk ("pas2_pcm.c: static void pas_pcm_output_block(char *buf = %P, int count = %X)\n", buf, count)); + + cnt = count; + if (sound_dsp_dmachan[dev] > 3) + cnt >>= 1; + cnt--; + + if (sound_dma_automode[dev] && + intrflag && + cnt == pcm_count) + return; /* Auto mode on. No need to react */ + + DISABLE_INTR (flags); + + pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, + PCM_CONTROL); + + DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); + + if (sound_dsp_dmachan[dev] > 3) + count >>= 1; + count--; + + if (count != pcm_count) + { + pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); + pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL); + pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER); + pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER); + pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); + + pcm_count = count; + } + pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY); + pas_write (pas_read (PCM_CONTROL) | P_C_PCM_ENABLE | P_C_PCM_DAC_MODE, PCM_CONTROL); + + pcm_mode = PCM_DAC; + + RESTORE_INTR (flags); +} + +static void +pas_pcm_start_input (int dev, unsigned long buf, int count, int intrflag) +{ + unsigned long flags; + int cnt; + + TRACE (printk ("pas2_pcm.c: static void pas_pcm_start_input(char *buf = %P, int count = %X)\n", buf, count)); + + cnt = count; + if (sound_dsp_dmachan[dev] > 3) + cnt >>= 1; + cnt--; + + if (sound_dma_automode[my_devnum] && + intrflag && + cnt == pcm_count) + return; /* Auto mode on. No need to react */ + + DISABLE_INTR (flags); + + DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); + + if (sound_dsp_dmachan[dev] > 3) + count >>= 1; + + count--; + + if (count != pcm_count) + { + pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); + pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL); + pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER); + pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER); + pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); + + pcm_count = count; + } + pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY); + pas_write ((pas_read (PCM_CONTROL) | P_C_PCM_ENABLE) & ~P_C_PCM_DAC_MODE, PCM_CONTROL); + + pcm_mode = PCM_ADC; + + RESTORE_INTR (flags); +} + +static int +pas_pcm_prepare_for_input (int dev, int bsize, int bcount) +{ + return 0; +} +static int +pas_pcm_prepare_for_output (int dev, int bsize, int bcount) +{ + return 0; +} + +static struct audio_operations pas_pcm_operations = +{ + "Pro Audio Spectrum", + pas_pcm_open, /* */ + pas_pcm_close, /* */ + pas_pcm_output_block, /* */ + pas_pcm_start_input, /* */ + pas_pcm_ioctl, /* */ + pas_pcm_prepare_for_input, /* */ + pas_pcm_prepare_for_output, /* */ + pas_pcm_reset, /* */ + pas_pcm_reset, /* halt_xfer */ + NULL, /* has_output_drained */ + NULL /* copy_from_user */ +}; + +long +pas_pcm_init (long mem_start, struct address_info *hw_config) +{ + TRACE (printk ("pas2_pcm.c: long pas_pcm_init(long mem_start = %X)\n", mem_start)); + + pcm_bitsok = 8; + if (pas_read (OPERATION_MODE_1) & O_M_1_PCM_TYPE) + pcm_bitsok |= 16; + + pcm_set_speed (DSP_DEFAULT_SPEED); + + if (num_dspdevs < MAX_DSP_DEV) + { + dsp_devs[my_devnum = num_dspdevs++] = &pas_pcm_operations; + sound_dsp_dmachan[my_devnum] = hw_config->dma; + if (hw_config->dma > 3) + { + sound_buffcounts[my_devnum] = 1; + sound_buffsizes[my_devnum] = 2 * 65536; + sound_dma_automode[my_devnum] = 1; + } + else + { + sound_buffcounts[my_devnum] = 1; + sound_buffsizes[my_devnum] = DSP_BUFFSIZE; + sound_dma_automode[my_devnum] = 1; + } + } + else + printk ("PAS2: Too many PCM devices available\n"); + + return mem_start; +} + +void +pas_pcm_interrupt (unsigned char status, int cause) +{ + if (cause == 1) /* PCM buffer done */ + { + /* + * Halt the PCM first. Otherwise we don't have time to start a new + * block before the PCM chip proceeds to the next sample + */ + + if (!sound_dma_automode[my_devnum]) + { + pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, + PCM_CONTROL); + } + + switch (pcm_mode) + { + + case PCM_DAC: + DMAbuf_outputintr (my_devnum); + break; + + case PCM_ADC: + DMAbuf_inputintr (my_devnum); + break; + + default: + printk ("PAS: Unexpected PCM interrupt\n"); + } + } +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/patmgr.c b/sys/i386/isa/sound/patmgr.c new file mode 100644 index 0000000000..48c10046fa --- /dev/null +++ b/sys/i386/isa/sound/patmgr.c @@ -0,0 +1,239 @@ +/* + * linux/kernel/chr_drv/sound/patmgr.c + * + * The patch maneger interface for the /dev/sequencer + * + * (C) 1993 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further + * details. Should be distributed with this file. + */ + +#define PATMGR_C +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SEQUENCER) + +DEFINE_WAIT_QUEUES (server_procs[MAX_SYNTH_DEV], + server_wait_flag[MAX_SYNTH_DEV]); + +static struct patmgr_info *mbox[MAX_SYNTH_DEV] = +{NULL}; +static volatile int msg_direction[MAX_SYNTH_DEV] = +{0}; + +static int pmgr_opened[MAX_SYNTH_DEV] = +{0}; + +#define A_TO_S 1 +#define S_TO_A 2 + +DEFINE_WAIT_QUEUE (appl_proc, appl_wait_flag); + +int +pmgr_open (int dev) +{ + if (dev < 0 || dev >= num_synths) + return RET_ERROR (ENXIO); + + if (pmgr_opened[dev]) + return RET_ERROR (EBUSY); + pmgr_opened[dev] = 1; + + return 0; +} + +void +pmgr_release (int dev) +{ + + if (mbox[dev]) /* Killed in action. Inform the client */ + { + + mbox[dev]->key = PM_ERROR; + mbox[dev]->parm1 = RET_ERROR (EIO); + + if (appl_wait_flag) + WAKE_UP (appl_proc); + } + + pmgr_opened[dev] = 0; +} + +int +pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + unsigned long flags; + int ok = 0; + + if (count != sizeof (struct patmgr_info)) + { + printk ("PATMGR%d: Invalid read count\n", dev); + return RET_ERROR (EIO); + } + + while (!ok && !PROCESS_ABORTING) + { + DISABLE_INTR (flags); + + while (!(mbox[dev] && msg_direction[dev] == A_TO_S) && !PROCESS_ABORTING) + { + INTERRUPTIBLE_SLEEP_ON (server_procs[dev], server_wait_flag[dev]); + } + + if (mbox[dev] && msg_direction[dev] == A_TO_S) + { + COPY_TO_USER (buf, 0, (char *) mbox[dev], count); + msg_direction[dev] = 0; + ok = 1; + } + + RESTORE_INTR (flags); + + } + + if (!ok) + return RET_ERROR (EINTR); + return count; +} + +int +pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + unsigned long flags; + + if (count < 4) + { + printk ("PATMGR%d: Write count < 4\n", dev); + return RET_ERROR (EIO); + } + + COPY_FROM_USER (mbox[dev], buf, 0, 4); + + if (*(unsigned char *) mbox[dev] == SEQ_FULLSIZE) + { + int tmp_dev; + + tmp_dev = ((unsigned short *) mbox[dev])[2]; + if (tmp_dev != dev) + return RET_ERROR (ENXIO); + + return synth_devs[dev]->load_patch (dev, *(unsigned short *) mbox[dev], + buf, 4, count, 1); + } + + if (count != sizeof (struct patmgr_info)) + { + printk ("PATMGR%d: Invalid write count\n", dev); + return RET_ERROR (EIO); + } + + /* + * If everything went OK, there should be a preallocated buffer in the + * mailbox and a client waiting. + */ + + DISABLE_INTR (flags); + + if (mbox[dev] && !msg_direction[dev]) + { + COPY_FROM_USER (&((char *) mbox[dev])[4], buf, 4, count - 4); + msg_direction[dev] = S_TO_A; + + if (appl_wait_flag) + { + WAKE_UP (appl_proc); + } + } + + RESTORE_INTR (flags); + + return count; +} + +int +pmgr_access (int dev, struct patmgr_info *rec) +{ + unsigned long flags; + int err = 0; + + DISABLE_INTR (flags); + + if (mbox[dev]) + printk (" PATMGR: Server %d mbox full. Why?\n", dev); + else + { + rec->key = PM_K_COMMAND; + mbox[dev] = rec; + msg_direction[dev] = A_TO_S; + + if (server_wait_flag[dev]) + { + WAKE_UP (server_procs[dev]); + } + + INTERRUPTIBLE_SLEEP_ON (appl_proc, appl_wait_flag); + + if (msg_direction[dev] != S_TO_A) + { + rec->key = PM_ERROR; + rec->parm1 = RET_ERROR (EIO); + } + else if (rec->key == PM_ERROR) + { + err = rec->parm1; + if (err > 0) + err = -err; + } + + mbox[dev] = NULL; + msg_direction[dev] = 0; + } + + RESTORE_INTR (flags); + + return err; +} + +int +pmgr_inform (int dev, int event, unsigned long p1, unsigned long p2, + unsigned long p3, unsigned long p4) +{ + unsigned long flags; + int err = 0; + + if (!pmgr_opened[dev]) + return 0; + + DISABLE_INTR (flags); + + if (mbox[dev]) + printk (" PATMGR: Server %d mbox full. Why?\n", dev); + else + { + mbox[dev] = + (struct patmgr_info *) KERNEL_MALLOC (sizeof (struct patmgr_info)); + + mbox[dev]->key = PM_K_EVENT; + mbox[dev]->command = event; + mbox[dev]->parm1 = p1; + mbox[dev]->parm2 = p2; + mbox[dev]->parm3 = p3; + msg_direction[dev] = A_TO_S; + + if (server_wait_flag[dev]) + { + WAKE_UP (server_procs[dev]); + } + + INTERRUPTIBLE_SLEEP_ON (appl_proc, appl_wait_flag); + if (mbox[dev]) + KERNEL_FREE (mbox[dev]); + mbox[dev] = NULL; + msg_direction[dev] = 0; + } + + RESTORE_INTR (flags); + + return err; +} + +#endif diff --git a/sys/i386/isa/sound/pro_midi.c b/sys/i386/isa/sound/pro_midi.c new file mode 100644 index 0000000000..b7f6b9a2fa --- /dev/null +++ b/sys/i386/isa/sound/pro_midi.c @@ -0,0 +1,155 @@ +/* UWM -- comments to soft-eng@cs.uwm.edu */ +#define ALL_EXTERNAL_TO_ME +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#include "pas.h" +#define ESUCCESS 0 + +#if !defined(EXCLUDE_PRO_MIDI) && !defined(EXCLUDE_CHIP_MIDI) + + +/** Structure for handling operations **/ + + +static struct generic_midi_operations pro_midi_operations = { + + {"Pro_Audio_Spectrum 16 MV101", 0}, + pro_midi_open, + pro_midi_close, + pro_midi_write, + pro_midi_read +}; + +/* + * Note! Note! Note! + * Follow the same model for any other attach function you + * may write + */ + +long pro_midi_attach( long mem_start) +{ + pro_midi_dev = num_generic_midis; + generic_midi_devs[num_generic_midis++] = &pro_midi_operations; + return mem_start; +} + +int pro_midi_open(int dev, int mode) +{ + + int intr_mask, s; + + + s = splhigh(); + + + /* Reset the input and output FIFO pointers */ + + + outb(MIDI_CONTROL,M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO); + + /* Get the interrupt status */ + + intr_mask = inb(INTERRUPT_MASK); + + + /* Enable MIDI IRQ */ + + intr_mask |= I_M_MIDI_IRQ_ENABLE; + outb(INTERRUPT_MASK, intr_mask); + + + /* Enable READ/WRITE on MIDI port. This part is quite unsure though */ + + outb(MIDI_CONTROL,M_C_ENA_OUTPUT_IRQ | M_C_ENA_INPUT_IRQ); + + /* Acknowledge pending interrupts */ + + outb(MIDI_STATUS,0xff); + + + splx(s); + + return(ESUCCESS); + + +} + + +void pro_midi_close(int dev) +{ + + int intr_mask; + + /* Clean up */ + + outb(MIDI_CONTROL,M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO); + intr_mask = inb(INTERRUPT_MASK); + intr_mask &= ~I_M_MIDI_IRQ_ENABLE; + outb(INTERRUPT_MASK,intr_mask); + + return; +} + +int pro_midi_write(int dev, struct uio *uio) +{ + + int s; + unsigned char data; + + /* printf("midi: Going to do write routine..\n"); */ + while(uio->uio_resid) { + + if ( uiomove(&data,1,uio) ) return(ENOTTY); + + s = splhigh(); + + DELAY(30); + outb(MIDI_DATA,data); + DELAY(70); /* Ze best pause.. find a better one if + * you can :) + */ + splx(s); + } + + return(ESUCCESS); + +} + + +int pro_midi_read(int dev, struct uio *uio) +{ + + int s; + unsigned char data; + + s = splhigh(); + + /* For each uio_iov[] entry .... */ + + while (uio->uio_resid) { + + if((( inb(MIDI_STATUS) & M_S_INPUT_AVAIL) == 0 ) && + ((inb(MIDI_FIFO_STATUS) & MIDI_INPUT_AVAILABLE) == 0 ) ) + + data = 0xfe; + else + data = inb(MIDI_DATA); + + if ( uiomove(&data, 1 , uio)) { + + printf("midi: Bad copyout()!\n"); + return(ENOTTY); + + } + + } + splx(s); + return(ESUCCESS); + +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/sb_card.c b/sys/i386/isa/sound/sb_card.c new file mode 100644 index 0000000000..b9d070182a --- /dev/null +++ b/sys/i386/isa/sound/sb_card.c @@ -0,0 +1,33 @@ + +/* + * linux/kernel/chr_drv/sound/sb_card.c + * + * Detection routine for the SoundBlaster cards. + * + * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further + * details. Should be distributed with this file. + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) + +long +attach_sb_card (long mem_start, struct address_info *hw_config) +{ +#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_MIDI) + if (!sb_dsp_detect (hw_config)) + return mem_start; + mem_start = sb_dsp_init (mem_start, hw_config); +#endif + + return mem_start; +} + +int +probe_sb (struct address_info *hw_config) +{ + return sb_dsp_detect (hw_config); +} + +#endif diff --git a/sys/i386/isa/sound/sb_dsp.c b/sys/i386/isa/sound/sb_dsp.c new file mode 100644 index 0000000000..965922fab3 --- /dev/null +++ b/sys/i386/isa/sound/sb_dsp.c @@ -0,0 +1,1303 @@ +/* + * linux/kernel/chr_drv/sound/sb_dsp.c + * + * The low level driver for the SoundBlaster DS chips. + * + * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further + * details. Should be distributed with this file. + * + * The mixer support is based on the SB-BSD 1.5 driver by (C) Steve Haehnichen + * + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) + +#undef SB_TEST_IRQ + +#define DSP_RESET (sbc_base + 0x6) +#define DSP_READ (sbc_base + 0xA) +#define DSP_WRITE (sbc_base + 0xC) +#define DSP_COMMAND (sbc_base + 0xC) +#define DSP_STATUS (sbc_base + 0xC) +#define DSP_DATA_AVAIL (sbc_base + 0xE) +#define MIXER_ADDR (sbc_base + 0x4) +#define MIXER_DATA (sbc_base + 0x5) +#define OPL3_LEFT (sbc_base + 0x0) +#define OPL3_RIGHT (sbc_base + 0x2) +#define OPL3_BOTH (sbc_base + 0x8) + +static int sbc_base = 0; +static int sbc_irq = 0; + +#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD) + +#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_VOLUME) + +/* + * Mixer registers + * + * NOTE! RECORD_SRC == IN_FILTER + */ + +#define VOC_VOL 0x04 +#define MIC_VOL 0x0A +#define MIC_MIX 0x0A +#define RECORD_SRC 0x0C +#define IN_FILTER 0x0C +#define OUT_FILTER 0x0E +#define MASTER_VOL 0x22 +#define FM_VOL 0x26 +#define CD_VOL 0x28 +#define LINE_VOL 0x2E + +#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */ +#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */ +#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */ +#define FILT_OFF (1 << 5) + +/* Convenient byte masks */ +#define B1(x) ((x) & 0x01) +#define B2(x) ((x) & 0x03) +#define B3(x) ((x) & 0x07) +#define B4(x) ((x) & 0x0f) +#define B5(x) ((x) & 0x1f) +#define B6(x) ((x) & 0x3f) +#define B7(x) ((x) & 0x7f) +#define B8(x) ((x) & 0xff) +#define F(x) (!!(x)) /* 0 or 1 only */ + +#define MONO_DAC 0x00 +#define STEREO_DAC 0x02 + +/* DSP Commands */ + +#define DSP_CMD_SPKON 0xD1 +#define DSP_CMD_SPKOFF 0xD3 + +/* + * The DSP channel can be used either for input or output. Variable + * 'irq_mode' will be set when the program calls read or write first time + * after open. Current version doesn't support mode changes without closing + * and reopening the device. Support for this feature may be implemented in a + * future version of this driver. + */ + +#define IMODE_NONE 0 +#define IMODE_OUTPUT 1 +#define IMODE_INPUT 2 +#define IMODE_INIT 3 +#define IMODE_MIDI 4 + +#define NORMAL_MIDI 0 +#define UART_MIDI 1 + +static int sb_dsp_ok = 0; /* Set to 1 after successful initialization */ +static int midi_disabled = 0; +static int dsp_highspeed = 0, dsp_stereo = 0; +static int dsp_current_speed = DSP_DEFAULT_SPEED; + +#ifndef EXCLUDE_SBPRO +static int rec_devices = SOUND_MASK_MIC; +static int hi_filter = 0, filter_in = 0, filter_out = 0; + +#endif + +static int midi_mode = NORMAL_MIDI; +static int midi_busy = 0; /* 1 if the process has output to MIDI */ +static int dsp_busy = 0; + +static volatile int irq_mode = IMODE_NONE; /* IMODE_INPUT, IMODE_OUTPUT + * or IMODE_NONE */ +static volatile int irq_ok = 0; + +static int dsp_model = 1; /* 1=SB, 2=SB Pro */ +static int duplex_midi = 0; +static int my_dev = 0; + +static volatile int intr_active = 0; + +static int dsp_speed (int); +static int dsp_set_stereo (int mode); +static int dsp_command (unsigned char val); + +#ifndef EXCLUDE_SBPRO +static void setmixer (unsigned char port, unsigned char value); +static int getmixer (unsigned char port); +static void init_mixer (void); +static int detect_mixer (void); + +#endif + +#if !defined(EXCLUDE_MIDI) || !defined(EXCLUDE_AUDIO) + +/* Common code for the midi and pcm functions */ + +static int +dsp_command (unsigned char val) +{ + int i, limit; + + limit = GET_TIME () + 10; /* The timeout is 0.1 secods */ + + /* + * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes + * called while interrupts are disabled. This means that the timer is + * disabled also. However the timeout situation is a abnormal condition. + * Normally the DSP should be ready to accept commands after just couple of + * loops. + */ + + for (i = 0; i < 5000000 && GET_TIME () < limit; i++) + { + if ((INB (DSP_STATUS) & 0x80) == 0) + { + OUTB (val, DSP_COMMAND); + return 1; + } + } + + printk ("SoundBlaster: DSP Command(%02x) Timeout.\n", val); + printk ("IRQ conflict???\n"); + return 0; +} + +void +sbintr (int unused) +{ + int status, data; + + status = INB (DSP_DATA_AVAIL);/* Clear interrupt */ + + if (intr_active) + switch (irq_mode) + { + case IMODE_OUTPUT: + intr_active = 0; + DMAbuf_outputintr (my_dev); + break; + + case IMODE_INPUT: + intr_active = 0; + DMAbuf_inputintr (my_dev); + /* A complete buffer has been input. Let's start new one */ + break; + + case IMODE_INIT: + intr_active = 0; + irq_ok = 1; + break; + + case IMODE_MIDI: + printk ("+"); + data = INB (DSP_READ); + printk ("%02x", data); + + break; + + default: + printk ("SoundBlaster: Unexpected interrupt\n"); + } +} + +static int +set_dsp_irq (int interrupt_level) +{ + int retcode; + +#ifdef linux + struct sigaction sa; + + sa.sa_handler = sbintr; + +#ifdef SND_SA_INTERRUPT + sa.sa_flags = SA_INTERRUPT; +#else + sa.sa_flags = 0; +#endif + + sa.sa_mask = 0; + sa.sa_restorer = NULL; + + retcode = irqaction (interrupt_level, &sa); + + if (retcode < 0) + { + printk ("SoundBlaster: IRQ%d already in use\n", interrupt_level); + } + +#else + /* # error Unimplemented for this OS */ +#endif + return retcode; +} + +static int +reset_dsp (void) +{ + int loopc; + + OUTB (1, DSP_RESET); + tenmicrosec (); + OUTB (0, DSP_RESET); + tenmicrosec (); + tenmicrosec (); + tenmicrosec (); + + for (loopc = 0; loopc < 1000 && !(INB (DSP_DATA_AVAIL) & 0x80); loopc++); /* Wait for data + * available status */ + + if (INB (DSP_READ) != 0xAA) + return 0; /* Sorry */ + + return 1; +} + +#endif + +#ifndef EXCLUDE_AUDIO + +static void +dsp_speaker (char state) +{ + if (state) + dsp_command (DSP_CMD_SPKON); + else + dsp_command (DSP_CMD_SPKOFF); +} + +static int +dsp_speed (int speed) +{ + unsigned char tconst; + unsigned long flags; + + + if (speed < 4000) + speed = 4000; + + if (speed > 44100) + speed = 44100; /* Invalid speed */ + + if (dsp_model == 1 && speed > 22050) + speed = 22050; + /* SB Classic doesn't support higher speed */ + + + if (dsp_stereo && speed > 22050) + speed = 22050; + /* Max. stereo speed is 22050 */ + + if ((speed > 22050) && midi_busy) + { + printk ("SB Warning: High speed DSP not possible simultaneously with MIDI output\n"); + speed = 22050; + } + + if (dsp_stereo) + speed <<= 1; + + /* Now the speed should be valid */ + + if (speed > 22050) + { /* High speed mode */ + tconst = (unsigned char) ((65536 - (256000000 / speed)) >> 8); + dsp_highspeed = 1; + + DISABLE_INTR (flags); + if (dsp_command (0x40)) + dsp_command (tconst); + RESTORE_INTR (flags); + + speed = (256000000 / (65536 - (tconst << 8))); + } + else + { + dsp_highspeed = 0; + tconst = (256 - (1000000 / speed)) & 0xff; + + DISABLE_INTR (flags); + if (dsp_command (0x40)) /* Set time constant */ + dsp_command (tconst); + RESTORE_INTR (flags); + + speed = 1000000 / (256 - tconst); + } + + if (dsp_stereo) + speed >>= 1; + + dsp_current_speed = speed; + return speed; +} + +static int +dsp_set_stereo (int mode) +{ + dsp_stereo = 0; + + if (dsp_model == 1) + return 0; /* Sorry no stereo */ + + if (mode && midi_busy) + { + printk ("SB Warning: Stereo DSP not possible simultaneously with MIDI output\n"); + return 0; + } + + dsp_stereo = !!mode; + +#ifndef EXCLUDE_SBPRO + setmixer (OUT_FILTER, ((getmixer (OUT_FILTER) & ~STEREO_DAC) + | (mode ? STEREO_DAC : MONO_DAC))); +#endif + dsp_speed (dsp_current_speed);/* Speed must be recalculated if #channels + * changes */ + return mode; +} + +static void +sb_dsp_output_block (int dev, unsigned long buf, int count, int intrflag) +{ + unsigned long flags; + + if (!irq_mode) + dsp_speaker (ON); + + irq_mode = IMODE_OUTPUT; + DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); + + if (sound_dsp_dmachan[dev] > 3) + count >>= 1; + count--; + + if (dsp_highspeed) + { + DISABLE_INTR (flags); + if (dsp_command (0x48)) /* High speed size */ + { + dsp_command (count & 0xff); + dsp_command ((count >> 8) & 0xff); + dsp_command (0x91); /* High speed 8 bit DAC */ + } + else + printk ("SB Error: Unable to start (high speed) DAC\n"); + RESTORE_INTR (flags); + } + else + { + DISABLE_INTR (flags); + if (dsp_command (0x14)) /* 8-bit DAC (DMA) */ + { + dsp_command (count & 0xff); + dsp_command ((count >> 8) & 0xff); + } + else + printk ("SB Error: Unable to start DAC\n"); + RESTORE_INTR (flags); + } + intr_active = 1; +} + +static void +sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag) +{ + /* Start a DMA input to the buffer pointed by dmaqtail */ + + unsigned long flags; + + if (!irq_mode) + dsp_speaker (OFF); + + irq_mode = IMODE_INPUT; + DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); + + if (sound_dsp_dmachan[dev] > 3) + count >>= 1; + count--; + + if (dsp_highspeed) + { + DISABLE_INTR (flags); + if (dsp_command (0x48)) /* High speed size */ + { + dsp_command (count & 0xff); + dsp_command ((count >> 8) & 0xff); + dsp_command (0x99); /* High speed 8 bit ADC */ + } + else + printk ("SB Error: Unable to start (high speed) ADC\n"); + RESTORE_INTR (flags); + } + else + { + DISABLE_INTR (flags); + if (dsp_command (0x24)) /* 8-bit ADC (DMA) */ + { + dsp_command (count & 0xff); + dsp_command ((count >> 8) & 0xff); + } + else + printk ("SB Error: Unable to start ADC\n"); + RESTORE_INTR (flags); + } + + intr_active = 1; +} + +static void +dsp_cleanup (void) +{ + intr_active = 0; +} + +static int +sb_dsp_prepare_for_input (int dev, int bsize, int bcount) +{ + dsp_cleanup (); + dsp_speaker (OFF); + return 0; +} + +static int +sb_dsp_prepare_for_output (int dev, int bsize, int bcount) +{ + dsp_cleanup (); + dsp_speaker (ON); + return 0; +} + +static void +sb_dsp_halt_xfer (int dev) +{ +} + +static int +sb_dsp_open (int dev, int mode) +{ + int retval; + + if (!sb_dsp_ok) + { + printk ("SB Error: SoundBlaster board not installed\n"); + return RET_ERROR (ENXIO); + } + + if (!irq_ok) + { + printk ("SB Error: Incorrect IRQ setting (%d)\n", sbc_irq); + return RET_ERROR (ENXIO); + } + + if (intr_active || (midi_busy && midi_mode == UART_MIDI)) + { + printk ("SB: PCM not possible during MIDI input\n"); + return RET_ERROR (EBUSY); + } + + if (mode != OPEN_READ && mode != OPEN_WRITE) + { + printk ("SoundBlaster error: DAC and ACD not possible simultaneously\n"); + return RET_ERROR (EINVAL); + } + + retval = set_dsp_irq (sbc_irq); + if (retval) + return retval; + + if (!DMAbuf_open_dma (dev)) + { + RELEASE_IRQ (sbc_irq); + printk ("SB: DMA Busy\n"); + return RET_ERROR (EBUSY); + } + + dsp_set_stereo (OFF); + dsp_speed (DSP_DEFAULT_SPEED); + irq_mode = IMODE_NONE; + + dsp_busy = 1; + + return 0; +} + +static void +sb_dsp_close (int dev) +{ + DMAbuf_close_dma (dev); + RELEASE_IRQ (sbc_irq); + dsp_cleanup (); + dsp_speed (DSP_DEFAULT_SPEED); + dsp_set_stereo (OFF); + dsp_speaker (OFF); + dsp_busy = 0; +} + +static int +sb_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) +{ + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (local) + return dsp_speed (arg); + return IOCTL_OUT (arg, dsp_speed (IOCTL_IN (arg))); + break; + + case SOUND_PCM_READ_RATE: + if (local) + return dsp_current_speed; + return IOCTL_OUT (arg, dsp_current_speed); + break; + + case SOUND_PCM_WRITE_CHANNELS: + return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg) - 1) + 1); + break; + + case SOUND_PCM_READ_CHANNELS: + if (local) + return dsp_stereo + 1; + return IOCTL_OUT (arg, dsp_stereo + 1); + break; + + case SNDCTL_DSP_STEREO: + if (local) + return dsp_set_stereo (arg); + return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg))); + break; + + case SOUND_PCM_WRITE_BITS: + case SOUND_PCM_READ_BITS: + if (local) + return 8; + return IOCTL_OUT (arg, 8);/* Only 8 bits/sample supported */ + break; + + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + return RET_ERROR (EINVAL); + break; + + default: + return RET_ERROR (EINVAL); + } + + return RET_ERROR (EINVAL); +} + +static void +sb_dsp_reset (int dev) +{ + unsigned long flags; + + DISABLE_INTR (flags); + + reset_dsp (); + dsp_cleanup (); + + RESTORE_INTR (flags); +} + +#endif + +int +sb_dsp_detect (struct address_info *hw_config) +{ + sbc_base = hw_config->io_base; + sbc_irq = hw_config->irq; + + if (sb_dsp_ok) + return 0; /* Already initialized */ + + if (!reset_dsp ()) + return 0; + + return 1; /* Detected */ +} + +#ifndef EXCLUDE_SBPRO + +static void +setmixer (unsigned char port, unsigned char value) +{ + OUTB (port, MIXER_ADDR); /* Select register */ + tenmicrosec (); + OUTB (value, MIXER_DATA); + tenmicrosec (); +} + +static int +getmixer (unsigned char port) +{ + int val; + + OUTB (port, MIXER_ADDR); /* Select register */ + tenmicrosec (); + val = INB (MIXER_DATA); + tenmicrosec (); + + return val; +} + +static int +detect_mixer (void) +{ + /* + * Detect the mixer by changing parameters of two volume channels. If the + * values read back match with the values written, the mixer is there (is + * it?) + */ + setmixer (FM_VOL, 0xff); + setmixer (VOC_VOL, 0x33); + + if (getmixer (FM_VOL) != 0xff) + return 0; /* No match */ + if (getmixer (VOC_VOL) != 0x33) + return 0; + + return 1; +} + +static void +init_mixer (void) +{ + setmixer (MASTER_VOL, 0xbb); + setmixer (VOC_VOL, 0x99); + setmixer (LINE_VOL, 0xbb); + setmixer (FM_VOL, 0x99); + setmixer (CD_VOL, 0x11); + setmixer (MIC_MIX, 0x11); + setmixer (RECORD_SRC, 0x31); + setmixer (OUT_FILTER, 0x31); +} + +static void +set_filter (int record_source, int hifreq_filter, int filter_input, int filter_output) +{ + setmixer (RECORD_SRC, (record_source + | (hifreq_filter ? FREQ_HI : FREQ_LOW) + | (filter_input ? FILT_ON : FILT_OFF))); + + setmixer (OUT_FILTER, ((dsp_stereo ? STEREO_DAC : MONO_DAC) + | (filter_output ? FILT_ON : FILT_OFF))); + + hi_filter = hifreq_filter; + filter_in = filter_input; + filter_out = filter_output; +} + +static int +mixer_output (int right_vol, int left_vol, int div, int device) +{ + int left = ((left_vol * div) + 50) / 100; + int right = ((right_vol * div) + 50) / 100; + + setmixer (device, ((left & 0xf) << 4) | (right & 0xf)); + + return (left_vol | (right_vol << 8)); +} + +static int +sbp_mixer_set (int whichDev, unsigned int level) +{ + int left, right, devmask; + + left = level & 0x7f; + right = (level & 0x7f00) >> 8; + + switch (whichDev) + { + case SOUND_MIXER_VOLUME: /* Master volume (0-15) */ + return mixer_output (right, left, 15, MASTER_VOL); + break; + case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-15) */ + return mixer_output (right, left, 15, FM_VOL); + break; + case SOUND_MIXER_PCM: /* PAS PCM (0-15) */ + return mixer_output (right, left, 15, VOC_VOL); + break; + case SOUND_MIXER_LINE: /* External line (0-15) */ + return mixer_output (right, left, 15, LINE_VOL); + break; + case SOUND_MIXER_CD: /* CD (0-15) */ + return mixer_output (right, left, 15, CD_VOL); + break; + case SOUND_MIXER_MIC: /* External microphone (0-7) */ + return mixer_output (right, left, 7, MIC_VOL); + break; + + case SOUND_MIXER_RECSRC: + devmask = level & POSSIBLE_RECORDING_DEVICES; + + if (devmask != SOUND_MASK_MIC && + devmask != SOUND_MASK_LINE && + devmask != SOUND_MASK_CD) + { /* More than one devices selected. Drop the + * previous selection */ + devmask &= ~rec_devices; + } + + if (devmask != SOUND_MASK_MIC && + devmask != SOUND_MASK_LINE && + devmask != SOUND_MASK_CD) + { /* More than one devices selected. Default to + * mic */ + devmask = SOUND_MASK_MIC; + } + + if (devmask ^ rec_devices)/* Input source changed */ + { + switch (devmask) + { + + case SOUND_MASK_MIC: + set_filter (SRC_MIC, hi_filter, filter_in, filter_out); + break; + + case SOUND_MASK_LINE: + set_filter (SRC_LINE, hi_filter, filter_in, filter_out); + break; + + case SOUND_MASK_CD: + set_filter (SRC_CD, hi_filter, filter_in, filter_out); + break; + + default: + set_filter (SRC_MIC, hi_filter, filter_in, filter_out); + } + } + + rec_devices = devmask; + + return rec_devices; + break; + + default: + return RET_ERROR (EINVAL); + } + +} + +static int +mixer_input (int div, int device) +{ + int level, left, right, half; + + level = getmixer (device); + half = div / 2; + + left = ((((level & 0xf0) >> 4) * 100) + half) / div; + right = (((level & 0x0f) * 100) + half) / div; + + return (right << 8) | left; +} + +static int +sbp_mixer_get (int whichDev) +{ + + switch (whichDev) + { + case SOUND_MIXER_VOLUME: /* Master volume (0-15) */ + return mixer_input (15, MASTER_VOL); + break; + case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-15) */ + return mixer_input (15, FM_VOL); + break; + case SOUND_MIXER_PCM: /* PAS PCM (0-15) */ + return mixer_input (15, VOC_VOL); + break; + case SOUND_MIXER_LINE: /* External line (0-15) */ + return mixer_input (15, LINE_VOL); + break; + case SOUND_MIXER_CD: /* CD (0-15) */ + return mixer_input (15, CD_VOL); + break; + case SOUND_MIXER_MIC: /* External microphone (0-7) */ + return mixer_input (7, MIC_VOL); + break; + + default: + return RET_ERROR (EINVAL); + } + +} + +/* + * Sets mixer volume levels. All levels except mic are 0 to 15, mic is 7. See + * sbinfo.doc for details on granularity and such. Basically, the mixer + * forces the lowest bit high, effectively reducing the possible settings by + * one half. Yes, that's right, volume levels have 8 settings, and + * microphone has four. Sucks. + */ +static int +mixer_set_levels (struct sb_mixer_levels *user_l) +{ + struct sb_mixer_levels l; + + IOCTL_FROM_USER ((char *) &l, ((char *) user_l), 0, sizeof (l)); + + if (l.master.l & ~0xF || l.master.r & ~0xF + || l.line.l & ~0xF || l.line.r & ~0xF + || l.voc.l & ~0xF || l.voc.r & ~0xF + || l.fm.l & ~0xF || l.fm.r & ~0xF + || l.cd.l & ~0xF || l.cd.r & ~0xF + || l.mic & ~0x7) + return (RET_ERROR (EINVAL)); + + setmixer (MASTER_VOL, (l.master.l << 4) | l.master.r); + setmixer (LINE_VOL, (l.line.l << 4) | l.line.r); + setmixer (VOC_VOL, (l.voc.l << 4) | l.voc.r); + setmixer (FM_VOL, (l.fm.l << 4) | l.fm.r); + setmixer (CD_VOL, (l.cd.l << 4) | l.cd.r); + setmixer (MIC_VOL, l.mic); + return (0); +} + +/* + * This sets aspects of the Mixer that are not volume levels. (Recording + * source, filter level, I/O filtering, and stereo.) + */ + +static int +mixer_set_params (struct sb_mixer_params *user_p) +{ + struct sb_mixer_params p; + + IOCTL_FROM_USER ((char *) &p, (char *) user_p, 0, sizeof (p)); + + if (p.record_source != SRC_MIC + && p.record_source != SRC_CD + && p.record_source != SRC_LINE) + return (EINVAL); + + /* + * I'm not sure if this is The Right Thing. Should stereo be entirely + * under control of DSP? I like being able to toggle it while a sound is + * playing, so I do this... because I can. + */ + + dsp_stereo = !!p.dsp_stereo; + + set_filter (p.record_source, p.hifreq_filter, p.filter_input, p.filter_output); + + switch (p.record_source) + { + + case SRC_MIC: + rec_devices = SOUND_MASK_MIC; + break; + + case SRC_LINE: + rec_devices = SOUND_MASK_LINE; + break; + + case SRC_CD: + rec_devices = SOUND_MASK_CD; + } + + return (0); +} + +/* Read the current mixer level settings into the user's struct. */ +static int +mixer_get_levels (struct sb_mixer_levels *user_l) +{ + S_BYTE val; + struct sb_mixer_levels l; + + val = getmixer (MASTER_VOL); /* Master */ + l.master.l = B4 (val >> 4); + l.master.r = B4 (val); + + val = getmixer (LINE_VOL); /* FM */ + l.line.l = B4 (val >> 4); + l.line.r = B4 (val); + + val = getmixer (VOC_VOL); /* DAC */ + l.voc.l = B4 (val >> 4); + l.voc.r = B4 (val); + + val = getmixer (FM_VOL); /* FM */ + l.fm.l = B4 (val >> 4); + l.fm.r = B4 (val); + + val = getmixer (CD_VOL); /* CD */ + l.cd.l = B4 (val >> 4); + l.cd.r = B4 (val); + + val = getmixer (MIC_VOL); /* Microphone */ + l.mic = B3 (val); + + IOCTL_TO_USER ((char *) user_l, 0, (char *) &l, sizeof (l)); + + return (0); +} + +/* Read the current mixer parameters into the user's struct. */ +static int +mixer_get_params (struct sb_mixer_params *user_params) +{ + S_BYTE val; + struct sb_mixer_params params; + + val = getmixer (RECORD_SRC); + params.record_source = val & 0x07; + params.hifreq_filter = !!(val & FREQ_HI); + params.filter_input = (val & FILT_OFF) ? OFF : ON; + params.filter_output = (getmixer (OUT_FILTER) & FILT_OFF) ? OFF : ON; + params.dsp_stereo = dsp_stereo; + + IOCTL_TO_USER ((char *) user_params, 0, (char *) ¶ms, sizeof (params)); + return (0); +} + +static int +sb_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) +{ + if (((cmd >> 8) & 0xff) == 'M') + { + if (cmd & IOC_IN) + return IOCTL_OUT (arg, sbp_mixer_set (cmd & 0xff, IOCTL_IN (arg))); + else + { /* Read parameters */ + + switch (cmd & 0xff) + { + + case SOUND_MIXER_RECSRC: + return IOCTL_OUT (arg, rec_devices); + break; + + case SOUND_MIXER_DEVMASK: + return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES); + break; + + case SOUND_MIXER_STEREODEVS: + return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES & ~SOUND_MASK_MIC); + break; + + case SOUND_MIXER_RECMASK: + return IOCTL_OUT (arg, POSSIBLE_RECORDING_DEVICES); + break; + + case SOUND_MIXER_CAPS: + return IOCTL_OUT (arg, SOUND_CAP_EXCL_INPUT); + break; + + default: + return IOCTL_OUT (arg, sbp_mixer_get (cmd & 0xff)); + } + } + } + else + { + switch (cmd) + { + case MIXER_IOCTL_SET_LEVELS: + return (mixer_set_levels ((struct sb_mixer_levels *) arg)); + case MIXER_IOCTL_SET_PARAMS: + return (mixer_set_params ((struct sb_mixer_params *) arg)); + case MIXER_IOCTL_READ_LEVELS: + return (mixer_get_levels ((struct sb_mixer_levels *) arg)); + case MIXER_IOCTL_READ_PARAMS: + return (mixer_get_params ((struct sb_mixer_params *) arg)); + case MIXER_IOCTL_RESET: + init_mixer (); + return (0); + default: + return RET_ERROR (EINVAL); + } + } +} + +/* End of mixer code */ +#endif + +#ifndef EXCLUDE_MIDI + +/* Midi code */ + +static int +sb_midi_open (int dev, int mode) +{ + int ret; + + if (!sb_dsp_ok) + { + printk ("SB Error: MIDI hardware not installed\n"); + return RET_ERROR (ENXIO); + } + + if (mode != OPEN_WRITE && !duplex_midi) + { + printk ("SoundBlaster: Midi input not currently supported\n"); + return RET_ERROR (EPERM); + } + + midi_mode = NORMAL_MIDI; + if (mode != OPEN_WRITE) + { + if (dsp_busy || intr_active) + return RET_ERROR (EBUSY); + midi_mode = UART_MIDI; + } + + if (dsp_highspeed || dsp_stereo) + { + printk ("SB Error: Midi output not possible during stereo or high speed audio\n"); + return RET_ERROR (EBUSY); + } + + if (midi_mode == UART_MIDI) + { + irq_mode = IMODE_MIDI; + + reset_dsp (); + dsp_speaker (OFF); + + if (!dsp_command (0x35)) + return RET_ERROR (EIO); /* Enter the UART mode */ + intr_active = 1; + + if ((ret = set_dsp_irq (sbc_irq)) < 0) + { + reset_dsp (); + return 0; /* IRQ not free */ + } + } + + midi_busy = 1; + + return 0; +} + +static void +sb_midi_close (int dev) +{ + if (midi_mode == UART_MIDI) + { + reset_dsp (); /* The only way to kill the UART mode */ + RELEASE_IRQ (sbc_irq); + } + intr_active = 0; + midi_busy = 0; +} + +static int +sb_midi_out (int dev, unsigned char midi_byte) +{ + unsigned long flags; + + midi_busy = 1; /* Kill all notes after close */ + + if (midi_mode == NORMAL_MIDI) + { + DISABLE_INTR (flags); + if (dsp_command (0x38)) + dsp_command (midi_byte); + else + printk ("SB Error: Unable to send a MIDI byte\n"); + RESTORE_INTR (flags); + } + else + dsp_command (midi_byte); /* UART write */ + + return 1; +} + +static int +sb_midi_start_read (int dev) +{ + if (midi_mode != UART_MIDI) + { + printk ("SoundBlaster: MIDI input not implemented.\n"); + return RET_ERROR (EPERM); + } + return 0; +} + +static int +sb_midi_end_read (int dev) +{ + if (midi_mode == UART_MIDI) + { + reset_dsp (); + intr_active = 0; + } + return 0; +} + +static int +sb_midi_ioctl (int dev, unsigned cmd, unsigned arg) +{ + return RET_ERROR (EPERM); +} + +/* End of midi code */ +#endif + +#ifndef EXCLUDE_AUDIO +static struct audio_operations sb_dsp_operations = +{ + "SoundBlaster", + sb_dsp_open, + sb_dsp_close, + sb_dsp_output_block, + sb_dsp_start_input, + sb_dsp_ioctl, + sb_dsp_prepare_for_input, + sb_dsp_prepare_for_output, + sb_dsp_reset, + sb_dsp_halt_xfer, + NULL, /* has_output_drained */ + NULL /* copy_from_user */ +}; + +#endif + +#ifndef EXCLUDE_SBPRO +static struct mixer_operations sb_mixer_operations = +{ + sb_mixer_ioctl +}; + +#endif + +#ifndef EXCLUDE_MIDI +static struct midi_operations sb_midi_operations = +{ + {"SoundBlaster", 0}, + sb_midi_open, + sb_midi_close, + sb_midi_ioctl, + sb_midi_out, + sb_midi_start_read, + sb_midi_end_read, + NULL, /* Kick */ + NULL, /* command */ + NULL /* buffer_status */ +}; + +#endif + +static int +verify_irq (void) +{ +#if 0 + unsigned long loop; + + irq_ok = 0; + + if (set_dsp_irq (sbc_irq) == -1) + { + printk ("*** SB Error: Irq %d already in use\n", sbc_irq); + return 0; + } + + + irq_mode = IMODE_INIT; + + dsp_command (0xf2); /* This should cause immediate interrupt */ + + for (loop = 100000; loop > 0 && !irq_ok; loop--); + + RELEASE_IRQ (sbc_irq); + + if (!irq_ok) + { + printk ("SB Warning: IRQ test not passed!"); + irq_ok = 1; + } +#else + irq_ok = 1; +#endif + return irq_ok; +} + +long +sb_dsp_init (long mem_start, struct address_info *hw_config) +{ + int i, major, minor; + + major = minor = 0; + dsp_command (0xe1); /* Get version */ + + for (i = 1000; i; i--) + { + if (inb (DSP_DATA_AVAIL) & 0x80) + { /* wait for Data Ready */ + if (major == 0) + major = inb (DSP_READ); + else + { + minor = inb (DSP_READ); + break; + } + } + } + +#ifndef EXCLUDE_SBPRO + if (detect_mixer ()) + { + sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", major, minor); + init_mixer (); +#if SBC_DMA < 4 + /* This is a kludge for SB16 cards */ + if (major == 3) + dsp_model = 2; /* Do not enable if SB16 */ +#endif + mixer_devs[num_mixers++] = &sb_mixer_operations; + + if (major == 2 || major == 3) + duplex_midi = 1; + +#ifndef EXCLUDE_YM8312 + if (major > 3 || (major == 3 && minor > 0)) /* SB Pro2 or later */ + { + enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH); + } +#endif + } + else +#endif + sprintf (sb_dsp_operations.name, "SoundBlaster %d.%d", major, minor); + + printk ("snd2: <%s>", sb_dsp_operations.name); + + if (!verify_irq ()) + return mem_start; + +#ifndef EXCLUDE_AUDIO + if (num_dspdevs < MAX_DSP_DEV) + { + dsp_devs[my_dev = num_dspdevs++] = &sb_dsp_operations; + sound_buffcounts[my_dev] = DSP_BUFFCOUNT; + sound_buffsizes[my_dev] = DSP_BUFFSIZE; + sound_dsp_dmachan[my_dev] = hw_config->dma; + sound_dma_automode[my_dev] = 0; + } + else + printk ("SB: Too many DSP devices available\n"); +#endif + +#ifndef EXCLUDE_MIDI + if (!midi_disabled) /* Midi don't work in the SB emulation mode + * of PAS */ + midi_devs[num_midis++] = &sb_midi_operations; +#endif + + sb_dsp_ok = 1; + printk("\n"); + return mem_start; +} + +void +sb_dsp_disable_midi (void) +{ + midi_disabled = 1; +} + +#endif diff --git a/sys/i386/isa/sound/sequencer.c b/sys/i386/isa/sound/sequencer.c new file mode 100644 index 0000000000..2d4ef378b7 --- /dev/null +++ b/sys/i386/isa/sound/sequencer.c @@ -0,0 +1,1137 @@ +/* + * linux/kernel/chr_drv/sound/sequencer.c + * + * The sequencer personality manager. + * + * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further + * details. Should be distributed with this file. + */ + +#define SEQUENCER_C +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#ifndef EXCLUDE_SEQUENCER + +static int sequencer_ok = 0; + +DEFINE_WAIT_QUEUE (seq_sleeper, seq_sleep_flag); +DEFINE_WAIT_QUEUE (midi_sleeper, midi_sleep_flag); + +static int midi_opened[MAX_MIDI_DEV] = +{0}; /* 1 if the process has opened MIDI */ +static int midi_written[MAX_MIDI_DEV] = +{0}; + +long seq_time = 0; /* Reference point for the timer */ + +#include "tuning.h" + +#define EV_SZ 8 +static unsigned char queue[SEQ_MAX_QUEUE][EV_SZ]; +static unsigned char iqueue[SEQ_MAX_QUEUE][4]; +static volatile int qhead = 0, qtail = 0, qlen = 0; +static volatile int iqhead = 0, iqtail = 0, iqlen = 0; +static volatile int seq_playing = 0; +static int sequencer_busy = 0; +static int output_treshold; +static unsigned synth_open_mask; + +static int seq_queue (unsigned char *note); +static void seq_startplay (void); +static int seq_sync (void); +static void seq_reset (void); +static int pmgr_present[MAX_SYNTH_DEV] = +{0}; + +#if MAX_SYNTH_DEV > 15 +#error Too many synthesizer devices +#endif + +int +sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + int c = count, p = 0; + + dev = dev >> 4; + + if (dev) /* Patch manager device */ + return pmgr_read (dev - 1, file, buf, count); + + while (c > 3) + { + if (!iqlen) + { + INTERRUPTIBLE_SLEEP_ON (midi_sleeper, midi_sleep_flag); + + if (!iqlen) + return count - c; + } + + COPY_TO_USER (buf, p, &iqueue[iqhead][0], 4); + p += 4; + c -= 4; + + iqhead = (iqhead + 1) % SEQ_MAX_QUEUE; + iqlen--; + } + + return count - c; +} + +void +sequencer_midi_output (int dev) +{ + /* Currently NOP */ +} + +static void +copy_to_input (unsigned char *event) +{ + unsigned long flags; + + if (iqlen >= (SEQ_MAX_QUEUE - 1)) + return; /* Overflow */ + + memcpy (iqueue[iqtail], event, 4); + iqlen++; + iqtail = (iqtail + 1) % SEQ_MAX_QUEUE; + + DISABLE_INTR (flags); + if (midi_sleep_flag) + { + WAKE_UP (midi_sleeper); + } + RESTORE_INTR (flags); +} + +void +sequencer_midi_input (int dev, unsigned char data) +{ + int tstamp; + unsigned char event[4]; + + if (data == 0xfe) /* Active sensing */ + return; /* Ignore */ + + tstamp = GET_TIME () - seq_time; /* Time since open() */ + tstamp = (tstamp << 8) | SEQ_WAIT; + + copy_to_input ((unsigned char *) &tstamp); + + event[0] = SEQ_MIDIPUTC; + event[1] = data; + event[2] = dev; + event[3] = 0; + + copy_to_input (event); +} + +int +sequencer_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + unsigned char event[EV_SZ], ev_code; + int p = 0, c, ev_size; + int err; + int mode = file->mode & O_ACCMODE; + + dev = dev >> 4; + + DEB (printk ("sequencer_write(dev=%d, count=%d)\n", dev, count)); + + if (mode == OPEN_READ) + return RET_ERROR (EIO); + + if (dev) /* Patch manager device */ + return pmgr_write (dev - 1, file, buf, count); + + c = count; + + while (c >= 4) + { + COPY_FROM_USER (event, buf, p, 4); + ev_code = event[0]; + + if (ev_code == SEQ_FULLSIZE) + { + int err; + + dev = *(unsigned short *) &event[2]; + if (dev < 0 || dev >= num_synths) + return RET_ERROR (ENXIO); + + if (!(synth_open_mask & (1 << dev))) + return RET_ERROR (ENXIO); + + err = synth_devs[dev]->load_patch (dev, *(short *) &event[0], buf, p + 4, c, 0); + if (err < 0) + return err; + + return err; + } + + if (ev_code == SEQ_EXTENDED || ev_code == SEQ_PRIVATE) + { + + ev_size = 8; + + if (c < ev_size) + { + if (!seq_playing) + seq_startplay (); + return count - c; + } + + COPY_FROM_USER (&event[4], buf, p + 4, 4); + + } + else + ev_size = 4; + + if (event[0] == SEQ_MIDIPUTC) + { + + if (!midi_opened[event[2]]) + { + int mode; + int dev = event[2]; + + if (dev >= num_midis) + { + printk ("Sequencer Error: Nonexistent MIDI device %d\n", dev); + return RET_ERROR (ENXIO); + } + + mode = file->mode & O_ACCMODE; + + if ((err = midi_devs[dev]->open (dev, mode)) < 0) + { + seq_reset (); + printk ("Sequencer Error: Unable to open Midi #%d\n", dev); + return err; + } + + midi_opened[dev] = 1; + } + + } + + if (!seq_queue (event)) + { + + if (!seq_playing) + seq_startplay (); + return count - c; + } + + p += ev_size; + c -= ev_size; + } + + if (!seq_playing) + seq_startplay (); + + return count; +} + +static int +seq_queue (unsigned char *note) +{ + + /* Test if there is space in the queue */ + + if (qlen >= SEQ_MAX_QUEUE) + if (!seq_playing) + seq_startplay (); /* Give chance to drain the queue */ + + if (qlen >= SEQ_MAX_QUEUE && !seq_sleep_flag) + { + /* Sleep until there is enough space on the queue */ + INTERRUPTIBLE_SLEEP_ON (seq_sleeper, seq_sleep_flag); + } + + if (qlen >= SEQ_MAX_QUEUE) + return 0; /* To be sure */ + + memcpy (&queue[qtail][0], note, EV_SZ); + + qtail = (qtail + 1) % SEQ_MAX_QUEUE; + qlen++; + + return 1; +} + +static int +extended_event (unsigned char *q) +{ + int dev = q[2]; + + if (dev < 0 || dev >= num_synths) + return RET_ERROR (ENXIO); + + if (!(synth_open_mask & (1 << dev))) + return RET_ERROR (ENXIO); + + switch (q[1]) + { + case SEQ_NOTEOFF: + synth_devs[dev]->kill_note (dev, q[3], q[5]); + break; + + case SEQ_NOTEON: + if (q[4] > 127 && q[4] != 255) + return 0; + + synth_devs[dev]->start_note (dev, q[3], q[4], q[5]); + break; + + case SEQ_PGMCHANGE: + synth_devs[dev]->set_instr (dev, q[3], q[4]); + break; + + case SEQ_AFTERTOUCH: + synth_devs[dev]->aftertouch (dev, q[3], q[4]); + break; + + case SEQ_BALANCE: + synth_devs[dev]->panning (dev, q[3], (char) q[4]); + break; + + case SEQ_CONTROLLER: + synth_devs[dev]->controller (dev, q[3], q[4], *(short *) &q[5]); + break; + + default: + return RET_ERROR (EINVAL); + } + + return 0; +} + +static void +seq_startplay (void) +{ + int this_one; + unsigned long *delay; + unsigned char *q; + + while (qlen > 0) + { + qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE; + qlen--; + + q = &queue[this_one][0]; + + switch (q[0]) + { + case SEQ_NOTEOFF: + if (synth_open_mask & (1 << 0)) + if (synth_devs[0]) + synth_devs[0]->kill_note (0, q[1], q[3]); + break; + + case SEQ_NOTEON: + if (q[4] < 128 || q[4] == 255) + if (synth_open_mask & (1 << 0)) + if (synth_devs[0]) + synth_devs[0]->start_note (0, q[1], q[2], q[3]); + break; + + case SEQ_WAIT: + delay = (unsigned long *) q; /* Bytes 1 to 3 are containing the + * delay in GET_TIME() */ + *delay = (*delay >> 8) & 0xffffff; + + if (*delay > 0) + { + long time; + + seq_playing = 1; + time = *delay; + + request_sound_timer (time); + + if ((SEQ_MAX_QUEUE - qlen) >= output_treshold) + { + unsigned long flags; + + DISABLE_INTR (flags); + if (seq_sleep_flag) + { + seq_sleep_flag = 0; + WAKE_UP (seq_sleeper); + } + RESTORE_INTR (flags); + } + return; /* Stop here. Timer routine will continue + * playing after the delay */ + } + break; + + case SEQ_PGMCHANGE: + if (synth_open_mask & (1 << 0)) + if (synth_devs[0]) + synth_devs[0]->set_instr (0, q[1], q[2]); + break; + + case SEQ_SYNCTIMER: /* Reset timer */ + seq_time = GET_TIME (); + break; + + case SEQ_MIDIPUTC: /* Put a midi character */ + if (midi_opened[q[2]]) + { + int dev; + + dev = q[2]; + + if (!midi_devs[dev]->putc (dev, q[1])) + { + /* + * Output FIFO is full. Wait one timer cycle and try again. + */ + + qlen++; + qhead = this_one; /* Restore queue */ + seq_playing = 1; + request_sound_timer (-1); + return; + } + else + midi_written[dev] = 1; + } + break; + + case SEQ_ECHO: + copy_to_input (q); /* Echo back to the process */ + break; + + case SEQ_PRIVATE: + if (q[1] < num_synths) + synth_devs[q[1]]->hw_control (q[1], q); + break; + + case SEQ_EXTENDED: + extended_event (q); + break; + + default:; + } + + } + + seq_playing = 0; + + if ((SEQ_MAX_QUEUE - qlen) >= output_treshold) + { + unsigned long flags; + + DISABLE_INTR (flags); + if (seq_sleep_flag) + { + seq_sleep_flag = 0; + WAKE_UP (seq_sleeper); + } + RESTORE_INTR (flags); + } + +} + +int +sequencer_open (int dev, struct fileinfo *file) + { + int retval, mode, i; + + dev = dev >> 4; + mode = file->mode & O_ACCMODE; + + DEB (printk ("sequencer_open(dev=%d)\n", dev)); + + if (!sequencer_ok) + { + printk ("Soundcard: Sequencer not initialized\n"); + return RET_ERROR (ENXIO); + } + + if (dev) /* Patch manager device */ + { + int err; + + dev--; + if (pmgr_present[dev]) + return RET_ERROR (EBUSY); + if ((err = pmgr_open (dev)) < 0) + return err; /* Failed */ + + pmgr_present[dev] = 1; + return err; + } + + if (sequencer_busy) + { + printk ("Sequencer busy\n"); + return RET_ERROR (EBUSY); + } + + if (!(num_synths + num_midis)) + return RET_ERROR (ENXIO); + + synth_open_mask = 0; + + if (mode == OPEN_WRITE || mode == OPEN_READWRITE) + for (i = 0; i < num_synths; i++) /* Open synth devices */ + if (synth_devs[i]->open (i, mode) < 0) + printk ("Sequencer: Warning! Cannot open synth device #%d\n", i); + else + synth_open_mask |= (1 << i); + + seq_time = GET_TIME (); + + for (i = 0; i < num_midis; i++) + { + midi_opened[i] = 0; + midi_written[i] = 0; + } + + if (mode == OPEN_READ || mode == OPEN_READWRITE) + { /* Initialize midi input devices */ + if (!num_midis) + { + printk ("Sequencer: No Midi devices. Input not possible\n"); + return RET_ERROR (ENXIO); + } + + for (i = 0; i < num_midis; i++) + { + if ((retval = midi_devs[i]->open (i, mode)) >= 0) + midi_opened[i] = 1; + } + } + + sequencer_busy = 1; + seq_sleep_flag = midi_sleep_flag = 0; + output_treshold = SEQ_MAX_QUEUE / 2; + + for (i = 0; i < num_synths; i++) + if (pmgr_present[i]) + pmgr_inform (i, PM_E_OPENED, 0, 0, 0, 0); + + return 0; + } + +void +seq_drain_midi_queues (void) +{ + int i, n; + + /* + * Give the Midi drivers time to drain their output queues + */ + + n = 1; + + while (!PROCESS_ABORTING && n) + { + n = 0; + + for (i = 0; i < num_midis; i++) + if (midi_opened[i] && midi_written[i]) + if (midi_devs[i]->buffer_status != NULL) + if (midi_devs[i]->buffer_status (i)) + n++; + + /* + * Let's have a delay + */ + if (n) + { + REQUEST_TIMEOUT (HZ / 10, seq_sleeper); + INTERRUPTIBLE_SLEEP_ON (seq_sleeper, seq_sleep_flag); + } + } +} + +void +sequencer_release (int dev, struct fileinfo *file) + { + int i; + int mode = file->mode & O_ACCMODE; + + dev = dev >> 4; + + DEB (printk ("sequencer_release(dev=%d)\n", dev)); + + if (dev) /* Patch manager device */ + { + dev--; + pmgr_release (dev); + pmgr_present[dev] = 0; + return; + } + + /* + * Wait until the queue is empty + */ + while (!PROCESS_ABORTING && qlen) + { + seq_sync (); + } + + if (mode != OPEN_READ) + seq_drain_midi_queues (); /* Ensure the output queues are empty */ + seq_reset (); + if (mode != OPEN_READ) + seq_drain_midi_queues (); /* Flush the all notes off messages */ + + for (i = 0; i < num_midis; i++) + if (midi_opened[i]) + midi_devs[i]->close (i); + + if (mode == OPEN_WRITE || mode == OPEN_READWRITE) + for (i = 0; i < num_synths; i++) + if (synth_open_mask & (1 << i)) /* Actually opened */ + if (synth_devs[i]) + synth_devs[i]->close (i); + + for (i = 0; i < num_synths; i++) + if (pmgr_present[i]) + pmgr_inform (i, PM_E_CLOSED, 0, 0, 0, 0); + + sequencer_busy = 0; + } + +static int +seq_sync (void) +{ + if (qlen && !seq_playing && !PROCESS_ABORTING) + seq_startplay (); + + if (qlen && !seq_sleep_flag) /* Queue not empty */ + { + INTERRUPTIBLE_SLEEP_ON (seq_sleeper, seq_sleep_flag); + } + + return qlen; +} + +static void +midi_outc (int dev, unsigned char data) +{ + /* + * NOTE! Calls sleep(). Don't call this from interrupt. + */ + + int n; + + /* This routine sends one byte to the Midi channel. */ + /* If the output Fifo is full, it waits until there */ + /* is space in the queue */ + + n = 300; /* Timeout in jiffies */ + + while (n && !midi_devs[dev]->putc (dev, data)) + { + REQUEST_TIMEOUT (1, seq_sleeper); + INTERRUPTIBLE_SLEEP_ON (seq_sleeper, seq_sleep_flag); + n--; + } +} + +static void +seq_reset (void) +{ + /* + * NOTE! Calls sleep(). Don't call this from interrupt. + */ + + int i, chn; + + sound_stop_timer (); + + qlen = qhead = qtail = 0; + iqlen = iqhead = iqtail = 0; + + for (i = 0; i < num_synths; i++) + if (synth_open_mask & (1 << i)) + if (synth_devs[i]) + synth_devs[i]->reset (i); + + for (i = 0; i < num_midis; i++) + if (midi_written[i]) /* Midi used. Some notes may still be playing */ + { + for (chn = 0; chn < 16; chn++) + { + midi_outc (i, 0xb0 + chn); /* Channel message */ + midi_outc (i, 0x7b);/* All notes off */ + midi_outc (i, 0); /* Dummy parameter */ + } + + midi_devs[i]->close (i); + + midi_written[i] = 0; + midi_opened[i] = 0; + } + + seq_playing = 0; + + if (seq_sleep_flag) + printk ("Sequencer Warning: Unexpected sleeping process\n"); + +} + +int +sequencer_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg) +{ + int midi_dev, orig_dev; + int mode = file->mode & O_ACCMODE; + + orig_dev = dev = dev >> 4; + + switch (cmd) + { + + case SNDCTL_SEQ_SYNC: + if (dev) /* Patch manager */ + return RET_ERROR (EIO); + + if (mode == OPEN_READ) + return 0; + while (qlen && !PROCESS_ABORTING) + seq_sync (); + return 0; + break; + + case SNDCTL_SEQ_RESET: + if (dev) /* Patch manager */ + return RET_ERROR (EIO); + + seq_reset (); + return 0; + break; + + case SNDCTL_SEQ_TESTMIDI: + if (dev) /* Patch manager */ + return RET_ERROR (EIO); + + midi_dev = IOCTL_IN (arg); + if (midi_dev >= num_midis) + return RET_ERROR (ENXIO); + + if (!midi_opened[midi_dev]) + { + int err, mode; + + mode = file->mode & O_ACCMODE; + if ((err = midi_devs[midi_dev]->open (midi_dev, mode)) < 0) + return err; + } + + midi_opened[midi_dev] = 1; + + return 0; + break; + + case SNDCTL_SEQ_GETINCOUNT: + if (dev) /* Patch manager */ + return RET_ERROR (EIO); + + if (mode == OPEN_WRITE) + return 0; + return IOCTL_OUT (arg, iqlen); + break; + + case SNDCTL_SEQ_GETOUTCOUNT: + + if (mode == OPEN_READ) + return 0; + return IOCTL_OUT (arg, SEQ_MAX_QUEUE - qlen); + break; + + case SNDCTL_SEQ_CTRLRATE: + if (dev) /* Patch manager */ + return RET_ERROR (EIO); + + /* If *arg == 0, just return the current rate */ + return IOCTL_OUT (arg, HZ); + break; + + case SNDCTL_SEQ_RESETSAMPLES: + dev = IOCTL_IN (arg); + if (dev < 0 || dev >= num_synths) + return RET_ERROR (ENXIO); + + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return RET_ERROR (EBUSY); + + if (!orig_dev && pmgr_present[dev]) + pmgr_inform (dev, PM_E_PATCH_RESET, 0, 0, 0, 0); + + return synth_devs[dev]->ioctl (dev, cmd, arg); + break; + + case SNDCTL_SEQ_NRSYNTHS: + return IOCTL_OUT (arg, num_synths); + break; + + case SNDCTL_SEQ_NRMIDIS: + return IOCTL_OUT (arg, num_midis); + break; + + case SNDCTL_SYNTH_MEMAVL: + { + int dev = IOCTL_IN (arg); + + if (dev < 0 || dev >= num_synths) + return RET_ERROR (ENXIO); + + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return RET_ERROR (EBUSY); + + return IOCTL_OUT (arg, synth_devs[dev]->ioctl (dev, cmd, arg)); + } + break; + + case SNDCTL_FM_4OP_ENABLE: + { + int dev = IOCTL_IN (arg); + + if (dev < 0 || dev >= num_synths) + return RET_ERROR (ENXIO); + + if (!(synth_open_mask & (1 << dev))) + return RET_ERROR (ENXIO); + + synth_devs[dev]->ioctl (dev, cmd, arg); + return 0; + } + break; + + case SNDCTL_SYNTH_INFO: + { + struct synth_info inf; + int dev; + + IOCTL_FROM_USER ((char *) &inf, (char *) arg, 0, sizeof (inf)); + dev = inf.device; + + if (dev < 0 || dev >= num_synths) + return RET_ERROR (ENXIO); + + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return RET_ERROR (EBUSY); + + return synth_devs[dev]->ioctl (dev, cmd, arg); + } + break; + + case SNDCTL_MIDI_INFO: + { + struct midi_info inf; + int dev; + + IOCTL_FROM_USER ((char *) &inf, (char *) arg, 0, sizeof (inf)); + dev = inf.device; + + if (dev < 0 || dev >= num_midis) + return RET_ERROR (ENXIO); + + IOCTL_TO_USER ((char *) arg, 0, (char *) &(midi_devs[dev]->info), sizeof (inf)); + return 0; + } + break; + + case SNDCTL_PMGR_IFACE: + { + struct patmgr_info *inf; + int dev, err; + + inf = (struct patmgr_info *) KERNEL_MALLOC (sizeof (*inf)); + + IOCTL_FROM_USER ((char *) inf, (char *) arg, 0, sizeof (*inf)); + dev = inf->device; + + if (dev < 0 || dev >= num_synths) + { + KERNEL_FREE (inf); + return RET_ERROR (ENXIO); + } + + if (!synth_devs[dev]->pmgr_interface) + { + KERNEL_FREE (inf); + return RET_ERROR (ENXIO); + } + + if ((err = synth_devs[dev]->pmgr_interface (dev, inf)) == -1) + { + KERNEL_FREE (inf); + return err; + } + + IOCTL_TO_USER ((char *) arg, 0, (char *) inf, sizeof (*inf)); + KERNEL_FREE (inf); + return 0; + } + break; + + case SNDCTL_PMGR_ACCESS: + { + struct patmgr_info *inf; + int dev, err; + + inf = (struct patmgr_info *) KERNEL_MALLOC (sizeof (*inf)); + + IOCTL_FROM_USER ((char *) inf, (char *) arg, 0, sizeof (*inf)); + dev = inf->device; + + if (dev < 0 || dev >= num_synths) + { + KERNEL_FREE (inf); + return RET_ERROR (ENXIO); + } + + if (!pmgr_present[dev]) + { + KERNEL_FREE (inf); + return RET_ERROR (ESRCH); + } + + if ((err = pmgr_access (dev, inf)) < 0) + { + KERNEL_FREE (inf); + return err; + } + + IOCTL_TO_USER ((char *) arg, 0, (char *) inf, sizeof (*inf)); + KERNEL_FREE (inf); + return 0; + } + break; + + case SNDCTL_SEQ_TRESHOLD: + { + int tmp = IOCTL_IN (arg); + + if (dev) /* Patch manager */ + return RET_ERROR (EIO); + + if (tmp < 1) + tmp = 1; + if (tmp >= SEQ_MAX_QUEUE) + tmp = SEQ_MAX_QUEUE - 1; + output_treshold = tmp; + return 0; + } + break; + + default: + if (dev) /* Patch manager */ + return RET_ERROR (EIO); + + if (mode == OPEN_READ) + return RET_ERROR (EIO); + + if (!synth_devs[0]) + return RET_ERROR (ENXIO); + if (!(synth_open_mask & (1 << 0))) + return RET_ERROR (ENXIO); + return synth_devs[0]->ioctl (0, cmd, arg); + break; + } + + return RET_ERROR (EINVAL); +} + +#ifdef ALLOW_SELECT +int +sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * wait) +{ + dev = dev >> 4; + + switch (sel_type) + { + case SEL_IN: + if (!iqlen) + { + midi_sleep_flag = 1; + select_wait (&midi_sleeper, wait); + return 0; + } + return 1; + + break; + + case SEL_OUT: + if (qlen >= SEQ_MAX_QUEUE) + { + seq_sleep_flag = 1; + select_wait (&seq_sleeper, wait); + return 0; + } + return 1; + break; + + case SEL_EX: + return 0; + } + + return 0; +} + +#endif + +void +sequencer_timer (void) +{ + seq_startplay (); +} + +int +note_to_freq (int note_num) +{ + + /* + * This routine converts a midi note to a frequency (multiplied by 1000) + */ + + int note, octave, note_freq; + int notes[] = + { + 261632, 277189, 293671, 311132, 329632, 349232, + 369998, 391998, 415306, 440000, 466162, 493880 + }; /* Note freq*1000 for octave 5 */ + +#define BASE_OCTAVE 5 + + octave = note_num / 12; + note = note_num % 12; + + note_freq = notes[note]; + + if (octave < BASE_OCTAVE) + note_freq >>= (BASE_OCTAVE - octave); + else if (octave > BASE_OCTAVE) + note_freq <<= (octave - BASE_OCTAVE); + + /* note_freq >>= 1; */ + + return note_freq; +} + +unsigned long +compute_finetune (unsigned long base_freq, int bend, int range) +{ + unsigned long amount; + int negative, semitones, cents; + + if (!bend) + return base_freq; + if (!range) + return base_freq; + + if (!base_freq) + return base_freq; + + if (range >= 8192) + range = 8191; + + bend = bend * range / 8192; + if (!bend) + return base_freq; + + negative = bend < 0 ? 1 : 0; + + if (bend < 0) + bend *= -1; + if (bend > range) + bend = range; + + if (bend > 2399) + bend = 2399; + + semitones = bend / 100; + cents = bend % 100; + + amount = semitone_tuning[semitones] * cent_tuning[cents] / 10000; + + if (negative) + return (base_freq * 10000) / amount; /* Bend down */ + else + return (base_freq * amount) / 10000; /* Bend up */ +} + + +long +sequencer_init (long mem_start) +{ + + sequencer_ok = 1; + return mem_start; +} + +#else +/* Stub version */ +int +sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + return RET_ERROR (EIO); +} + +int +sequencer_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + return RET_ERROR (EIO); +} + +int +sequencer_open (int dev, struct fileinfo *file) + { + return RET_ERROR (ENXIO); + } + +void +sequencer_release (int dev, struct fileinfo *file) + { + } +int +sequencer_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg) +{ + return RET_ERROR (EIO); +} + +int +sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig) +{ + return RET_ERROR (EIO); +} + +long +sequencer_init (long mem_start) +{ + return mem_start; +} + +void +sequencer_midi_input (int dev, unsigned char data) +{ + return; +} + +void +sequencer_midi_output (int dev) +{ + return; +} + +int +sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * wait) +{ + return RET_ERROR (EIO); +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/sound_calls.h b/sys/i386/isa/sound/sound_calls.h new file mode 100644 index 0000000000..fdf1fe145f --- /dev/null +++ b/sys/i386/isa/sound/sound_calls.h @@ -0,0 +1,183 @@ +/* + * DMA buffer calls + */ + +int DMAbuf_open(int dev, int mode); +int DMAbuf_release(int dev, int mode); +int DMAbuf_read (int dev, snd_rw_buf *user_buf, int count); +int DMAbuf_getwrbuffer(int dev, char **buf, int *size); +int DMAbuf_getrdbuffer(int dev, char **buf, int *len); +int DMAbuf_rmchars(int dev, int buff_no, int c); +int DMAbuf_start_output(int dev, int buff_no, int l); +int DMAbuf_ioctl(int dev, unsigned int cmd, unsigned int arg, int local); +long DMAbuf_init(long mem_start); +int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode); +int DMAbuf_open_dma (int chan); +void DMAbuf_close_dma (int chan); +void DMAbuf_reset_dma (int chan); +void DMAbuf_inputintr(int dev); +void DMAbuf_outputintr(int dev); + +/* + * System calls for the /dev/dsp + */ + +int dsp_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int dsp_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int dsp_open (int dev, struct fileinfo *file, int bits); +void dsp_release (int dev, struct fileinfo *file); +int dsp_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg); +int dsp_lseek (int dev, struct fileinfo *file, off_t offset, int orig); +long dsp_init (long mem_start); + +/* + * System calls for the /dev/audio + */ + +int audio_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int audio_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int audio_open (int dev, struct fileinfo *file); +void audio_release (int dev, struct fileinfo *file); +int audio_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg); +int audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig); +long audio_init (long mem_start); + +/* + * System calls for the /dev/sequencer + */ + +int sequencer_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int sequencer_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int sequencer_open (int dev, struct fileinfo *file); +void sequencer_release (int dev, struct fileinfo *file); +int sequencer_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg); +int sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig); +long sequencer_init (long mem_start); +void sequencer_midi_input(int dev, unsigned char data); +void sequencer_midi_output(int dev); +void sequencer_timer(void); +int note_to_freq(int note_num); +unsigned long compute_finetune(unsigned long base_freq, int bend, int range); + +#ifdef ALLOW_SELECT +int sequencer_select(int dev, struct fileinfo *file, int sel_type, select_table * wait); +#endif + +/* + * System calls for the /dev/midi + */ + +int MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int MIDIbuf_open (int dev, struct fileinfo *file); +void MIDIbuf_release (int dev, struct fileinfo *file); +int MIDIbuf_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg); +int MIDIbuf_lseek (int dev, struct fileinfo *file, off_t offset, int orig); +void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count); +long MIDIbuf_init(long mem_start); + +/* + * System calls for the generic midi interface. + * + */ + +long CMIDI_init (long mem_start); +int CMIDI_open (int dev, struct fileinfo *file); +int CMIDI_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int CMIDI_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int CMIDI_close (int dev, struct fileinfo *file); + +/* + * + * Misc calls from various sources + */ + +/* From pro_midi.c */ + +long pro_midi_attach(long mem_start); +int pro_midi_open(int dev, int mode); +void pro_midi_close(int dev); +int pro_midi_write(int dev, struct uio *uio); +int pro_midi_read(int dev, struct uio *uio); + +/* From soundcard.c */ +long soundcard_init(long mem_start); +void tenmicrosec(void); +void request_sound_timer (int count); +void sound_stop_timer(void); +int snd_ioctl_return(int *addr, int value); + +/* From sb_dsp.c */ +int sb_dsp_detect (struct address_info *hw_config); +long sb_dsp_init (long mem_start, struct address_info *hw_config); +void sb_dsp_disable_midi(void); + +/* From opl3.c */ +int opl3_detect (int ioaddr); +long opl3_init(long mem_start); + +/* From sb_card.c */ +long attach_sb_card(long mem_start, struct address_info *hw_config); +int probe_sb(struct address_info *hw_config); + +/* From adlib_card.c */ +long attach_adlib_card(long mem_start, struct address_info *hw_config); +int probe_adlib(struct address_info *hw_config); + +/* From pas_card.c */ +long attach_pas_card(long mem_start, struct address_info *hw_config); +int probe_pas(struct address_info *hw_config); +int pas_set_intr(int mask); +int pas_remove_intr(int mask); +unsigned char pas_read(int ioaddr); +void pas_write(unsigned char data, int ioaddr); + +/* From pas_audio.c */ +void pas_pcm_interrupt(unsigned char status, int cause); +long pas_pcm_init(long mem_start, struct address_info *hw_config); + +/* From pas_mixer.c */ +int pas_init_mixer(void); + +/* From pas_midi.c */ +long pas_midi_init(long mem_start); +void pas_midi_interrupt(void); + +/* From gus_card.c */ +long attach_gus_card(long mem_start, struct address_info * hw_config); +int probe_gus(struct address_info *hw_config); +int gus_set_midi_irq(int num); +void gusintr(int); + +/* From gus_wave.c */ +int gus_wave_detect(int baseaddr); +long gus_wave_init(long mem_start, int irq, int dma); +void gus_voice_irq(void); +unsigned char gus_read8 (int reg); +void gus_write8(int reg, unsigned char data); +void guswave_dma_irq(void); +void gus_delay(void); + +/* From gus_midi.c */ +long gus_midi_init(long mem_start); +void gus_midi_interrupt(int dummy); + +/* From mpu401.c */ +long attach_mpu401(long mem_start, struct address_info * hw_config); +int probe_mpu401(struct address_info *hw_config); + +/* From opl3.c */ +void enable_opl3_mode(int left, int right, int both); + +/* From patmgr.c */ +int pmgr_open(int dev); +void pmgr_release(int dev); +int pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count); +int pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count); +int pmgr_access(int dev, struct patmgr_info *rec); +int pmgr_inform(int dev, int event, unsigned long parm1, unsigned long parm2, + unsigned long parm3, unsigned long parm4); diff --git a/sys/i386/isa/sound/sound_config.h b/sys/i386/isa/sound/sound_config.h new file mode 100644 index 0000000000..09d36e5631 --- /dev/null +++ b/sys/i386/isa/sound/sound_config.h @@ -0,0 +1,182 @@ +/* linux/kernel/chr_drv/sound/sound-config.h + +A driver for Soundcards, misc configuration parameters. + +(C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) */ + +#include "local.h" + + +#undef CONFIGURE_SOUNDCARD +#undef DYNAMIC_BUFFER + +#ifdef KERNEL_SOUNDCARD +#define CONFIGURE_SOUNDCARD +#define DYNAMIC_BUFFER +#undef LOADABLE_SOUNDCARD +#endif + +#ifdef EXCLUDE_SEQUENCER +#define EXCLUDE_MIDI +#define EXCLUDE_YM3812 +#define EXCLUDE_OPL3 +#endif + +/** UWM - new MIDI stuff **/ + +#ifdef EXCLUDE_CHIP_MIDI +#define EXCLUDE_PRO_MIDI +#endif + +/** UWM - stuff **/ + +#if defined(EXCLUDE_SEQUENCER) && defined(EXCLUDE_AUDIO) +#undef CONFIGURE_SOUNDCARD +#endif + +#ifdef CONFIGURE_SOUNDCARD + +/* ****** IO-address, DMA and IRQ settings **** + +If your card has nonstandard I/O address or IRQ number, change defines + for the following settings in your kernel Makefile */ + +#ifndef SBC_BASE +#define SBC_BASE 0x220 /* 0x220 is the factory default. */ +#endif + +#ifndef SBC_IRQ +#define SBC_IRQ 7 /* IQR7 is the factory default. */ +#endif + +#ifndef SBC_DMA +#define SBC_DMA 1 +#endif + +#ifndef PAS_BASE +#define PAS_BASE 0x388 +#endif + +#ifndef PAS_IRQ +#define PAS_IRQ 5 +#endif + +#ifndef PAS_DMA +#define PAS_DMA 3 +#endif + +#ifndef GUS_BASE +#define GUS_BASE 0x220 +#endif + +#ifndef GUS_IRQ +#define GUS_IRQ 15 +#endif + +#ifndef GUS_MIDI_IRQ +#define GUS_MIDI_IRQ GUS_IRQ +#endif + +#ifndef GUS_DMA +#define GUS_DMA 6 +#endif + +#ifndef MPU_BASE +#define MPU_BASE 0x330 +#endif + +#ifndef MPU_IRQ +#define MPU_IRQ 6 +#endif + +/************* PCM DMA buffer sizes *******************/ + +/* If you are using high playback or recording speeds, the default buffersize + is too small. DSP_BUFFSIZE must be 64k or less. + + A rule of thumb is 64k for PAS16, 32k for PAS+, 16k for SB Pro and + 4k for SB. + + If you change the DSP_BUFFSIZE, don't modify this file. + Use the make config command instead. */ + +#ifndef DSP_BUFFSIZE +#define DSP_BUFFSIZE (4096) +#endif + +#ifndef DSP_BUFFCOUNT +#define DSP_BUFFCOUNT 2 /* 2 is recommended. */ +#endif + +#define DMA_AUTOINIT 0x10 + +#define SND_MAJOR 14 + +#define FM_MONO 0x388 /* This is the I/O address used by AdLib */ + +/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the + driver. (There is no need to alter this) */ +#define SEQ_MAX_QUEUE 512 + +#define SBFM_MAXINSTR (256) /* Size of the FM Instrument + bank */ +/* 128 instruments for general MIDI setup and 16 unassigned */ + +#define SND_NDEVS 50 /* Number of supported devices */ +#define SND_DEV_CTL 0 /* Control port /dev/mixer */ +#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM + synthesizer and MIDI output) */ +#define SND_DEV_MIDIN 2 /* MIDI input /dev/midin (not implemented + yet) */ +#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */ +#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */ +#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */ +#define SND_DEV_STATUS 6 /* /dev/sndstatus */ + +/* UWM ... note add new MIDI devices here.. + * Also do not forget to add table midi_supported[] + * Minor numbers for on-chip midi devices start from 15.. and + * should be contiguous.. viz. 15,16,17.... + * Also note the max # of midi devices as MAX_MIDI_DEV + */ + +#define CMIDI_DEV_PRO 15 /* Chip midi device == /dev/pro_midi */ + +/* + * Add other midis here... + . + . + . + . + */ + +#define DSP_DEFAULT_SPEED 8000 + +#define ON 1 +#define OFF 0 + +#define MAX_DSP_DEV 3 +#define MAX_MIXER_DEV 1 +#define MAX_SYNTH_DEV 3 +#define MAX_MIDI_DEV 3 + +struct fileinfo { + int mode; /* Open mode */ + }; + +struct address_info { + int io_base; + int irq; + int dma; +}; + +#define OPEN_READ 1 +#define OPEN_WRITE 2 +#define OPEN_READWRITE 3 + +#include "os.h" +#include "sound_calls.h" +#include "dev_table.h" +#include "debug.h" + +#endif diff --git a/sys/i386/isa/sound/soundcard.c b/sys/i386/isa/sound/soundcard.c new file mode 100644 index 0000000000..a0ded01edc --- /dev/null +++ b/sys/i386/isa/sound/soundcard.c @@ -0,0 +1,593 @@ +/* + * sound/386bsd/soundcard.c + * + * Soundcard driver for 386BSD. + * + * (C) 1992 Hannu Savolainen (hsavolai@cs.helsinki.fi) + * See COPYING for further details. Should be distributed with this file. + */ + +#include "sound_config.h" + +#ifdef CONFIGURE_SOUNDCARD + +#include "dev_table.h" + +int __timeout_val = 0; +int __process_aborting = 0; + +u_int snd1mask; +u_int snd2mask; +u_int snd3mask; +u_int snd4mask; +u_int snd5mask; + +struct sbc_device +{ + int usecount; +}; + +#define FIX_RETURN(ret) {if ((ret)<0) return -(ret); else return 0;} + +static struct sbc_device sbc_devices[SND_NDEVS]; +static int timer_running = 0; + +static int in_use = 0; /* Total # of open device files (excluding + * minor 0) */ + +static int soundcards_installed = 0; /* Number of installed + * soundcards */ +static int soundcard_configured = 0; +extern char *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT]; +extern unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT]; +extern int snd_raw_count[MAX_DSP_DEV]; + +static struct fileinfo files[SND_NDEVS]; + +int sndprobe (struct isa_device *dev); +int sndattach (struct isa_device *dev); +int sndopen (dev_t dev, int flags); +int sndclose (dev_t dev, int flags); +int sndioctl (dev_t dev, int cmd, caddr_t arg, int mode); +int sndread (int dev, struct uio *uio); +int sndwrite (int dev, struct uio *uio); +int sndselect (int dev, int rw); +static void sound_mem_init(void); + +int +get_time() +{ +extern struct timeval time; + + return(time.tv_usec + (time.tv_sec*1000000)); +} + + +int +sndread (int dev, struct uio *buf) +{ + int count = buf->uio_resid; + + dev = minor (dev); + + DEB (printk ("sound_read(dev=%d, count=%d)\n", dev, count)); + + switch (dev & 0xff) /* Changed to 0xff from 0x0f */ + { + case SND_DEV_AUDIO: + FIX_RETURN (audio_read (dev, &files[dev], buf, count)); + break; + + case SND_DEV_DSP: + case SND_DEV_DSP16: + FIX_RETURN (dsp_read (dev, &files[dev], buf, count)); + break; + + case SND_DEV_SEQ: + FIX_RETURN (sequencer_read (dev, &files[dev], buf, count)); + break; + +#ifndef EXCLUDE_CHIP_MIDI + case CMIDI_DEV_PRO: + FIX_RETURN (CMIDI_read (dev, &files[dev], buf, count)); + + break; +#endif + + +#ifndef EXCLUDE_MPU401 + case SND_DEV_MIDIN: + FIX_RETURN (MIDIbuf_read (dev, &files[dev], buf, count)); +#endif + + default: + ; + } + + FIX_RETURN (-EPERM); +} + +int +sndwrite (int dev, struct uio *buf) +{ + int count = buf->uio_resid; + + DEB (printk ("sound_write(dev=%d, count=%d)\n", dev, count)); + + dev = minor (dev); + + switch (dev & 0xff) /* Changed to 0xff from 0x0f */ + { + + case SND_DEV_SEQ: + FIX_RETURN (sequencer_write (dev, &files[dev], buf, count)); + break; + + case SND_DEV_AUDIO: + FIX_RETURN (audio_write (dev, &files[dev], buf, count)); + break; + + case SND_DEV_DSP: + case SND_DEV_DSP16: + FIX_RETURN (dsp_write (dev, &files[dev], buf, count)); + break; + +#ifndef EXCLUDE_CHIP_MIDI + case CMIDI_DEV_PRO: + FIX_RETURN (CMIDI_write (dev, &files[dev], buf, count)); + break; +#endif + + default: + FIX_RETURN (-EPERM); + } + + FIX_RETURN (count); +} + +int +sndopen (dev_t dev, int flags) +{ + int retval; + + dev = minor (dev); + + /* printf("SND: Minor number is now : %ld\n",dev); */ + + DEB (printk ("sound_open(dev=%d) : usecount=%d\n", dev, sbc_devices[dev].usecount)); + + if ((dev >= SND_NDEVS) || (dev < 0)) + { + printk ("Invalid minor device %d\n", dev); + FIX_RETURN (-ENODEV); + } + + if (!soundcard_configured && dev) + { + printk ("SoundCard Error: The soundcard system has not been configured\n"); + FIX_RETURN (-ENODEV); + } + + files[dev].mode = 0; + + if (flags & FREAD && flags & FWRITE) + files[dev].mode = OPEN_READWRITE; + else if (flags & FREAD) + files[dev].mode = OPEN_READ; + else if (flags & FWRITE) + files[dev].mode = OPEN_WRITE; + + switch (dev & 0xff) /* Changed to 0xff from 0x0f */ + { + case SND_DEV_CTL: + if (!soundcards_installed) + if (soundcard_configured) + { + printk ("Soundcard not installed\n"); + FIX_RETURN (-ENODEV); + } + break; + + case SND_DEV_SEQ: + if ((retval = sequencer_open (dev, &files[dev])) < 0) + FIX_RETURN (retval); + break; + +/** UWM stuff **/ + +#ifndef EXCLUDE_CHIP_MIDI + case CMIDI_DEV_PRO: + FIX_RETURN ( CMIDI_open (dev, &files[dev]) ); + break; +#endif + + +#ifndef EXCLUDE_MPU401 + case SND_DEV_MIDIN: + if ((retval = MIDIbuf_open (dev, &files[dev])) < 0) + FIX_RETURN (retval); + break; +#endif + + case SND_DEV_AUDIO: + if ((retval = audio_open (dev, &files[dev])) < 0) + FIX_RETURN (retval); + break; + + case SND_DEV_DSP: + if ((retval = dsp_open (dev, &files[dev], 8)) < 0) + FIX_RETURN (retval); + break; + + case SND_DEV_DSP16: + if ((retval = dsp_open (dev, &files[dev], 16)) < 0) + FIX_RETURN (retval); + break; + + default: + printk ("Invalid minor device %d\n", dev); + FIX_RETURN (-ENODEV); + } + + sbc_devices[dev].usecount++; + in_use++; + + FIX_RETURN (0); +} + +int +sndclose (dev_t dev, int flags) +{ + + dev = minor (dev); + + DEB (printk ("sound_release(dev=%d)\n", dev)); + + switch (dev & 0xff) /* Changed to 0xff from 0x0f */ + { + case SND_DEV_SEQ: + sequencer_release (dev, &files[dev]); + break; + +#ifndef EXCLUDE_CHIP_MIDI + case CMIDI_DEV_PRO: + CMIDI_close (dev, &files[dev]); + break; +#endif + +#ifndef EXCLUDE_MPU401 + case SND_DEV_MIDIN: + MIDIbuf_release (dev, &files[dev]); + break; +#endif + + case SND_DEV_AUDIO: + audio_release (dev, &files[dev]); + break; + + case SND_DEV_DSP: + case SND_DEV_DSP16: + dsp_release (dev, &files[dev]); + break; + + default:; + } + + sbc_devices[dev].usecount--; + in_use--; /* If not control port */ + + FIX_RETURN (0); +} + +int +sndioctl (dev_t dev, int cmd, caddr_t arg, int mode) +{ + dev = minor (dev); + + DEB (printk ("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg)); + + switch (dev & 0x0f) + { + + case SND_DEV_CTL: + if (!num_mixers) + FIX_RETURN (-ENODEV); + + if (dev >= num_mixers) + FIX_RETURN (-ENODEV); + + FIX_RETURN (mixer_devs[dev]->ioctl (dev, cmd, (unsigned int) arg)); + break; + + case SND_DEV_SEQ: + FIX_RETURN (sequencer_ioctl (dev, &files[dev], cmd, (unsigned int) arg)); + break; + + case SND_DEV_AUDIO: + FIX_RETURN (audio_ioctl (dev, &files[dev], cmd, (unsigned int) arg)); + break; + + case SND_DEV_DSP: + case SND_DEV_DSP16: + FIX_RETURN (dsp_ioctl (dev, &files[dev], cmd, (unsigned int) arg)); + break; + +#ifndef EXCLUDE_MPU401 + case SND_DEV_MIDIN: + FIX_RETURN (MIDIbuf_ioctl (dev, &files[dev], cmd, (unsigned int) arg)); + break; +#endif + + default: + FIX_RETURN (-EPERM); + break; + } + + FIX_RETURN (-EPERM); +} + +int +sndselect (int dev, int rw) +{ + dev = minor (dev); + + DEB (printk ("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg)); + + FIX_RETURN (0); +} + +static short +ipri_to_irq (short ipri) +{ + /* + * Converts the ipri (bitmask) to the corresponding irq number + */ + int irq; + + for (irq = 0; irq < 16; irq++) + if (ipri == (1 << irq)) + return irq; + + return -1; /* Invalid argument */ +} + +int +sndprobe (struct isa_device *dev) +{ + struct address_info hw_config; + + hw_config.io_base = dev->id_iobase; + hw_config.irq = ipri_to_irq (dev->id_irq); + hw_config.dma = dev->id_drq; + + + return sndtable_probe (dev->id_unit, &hw_config); +} + +int +sndattach (struct isa_device *dev) +{ + int i; + static int dsp_initialized = 0; + static int midi_initialized = 0; + static int seq_initialized = 0; + static int generic_midi_initialized = 0; + unsigned long mem_start = 0xefffffff; + struct address_info hw_config; + + hw_config.io_base = dev->id_iobase; + hw_config.irq = ipri_to_irq (dev->id_irq); + hw_config.dma = dev->id_drq; + + if (dev->id_unit) /* Card init */ + if (!sndtable_init_card (dev->id_unit, &hw_config)) + { + printf (" "); + return FALSE; + } + + /* + * Init the high level sound driver + */ + + if (!(soundcards_installed = sndtable_get_cardcount ())) + { + printf (" "); + return FALSE; /* No cards detected */ + } + +#ifndef EXCLUDE_AUDIO + soundcard_configured = 1; + if (num_dspdevs) + sound_mem_init (); +#endif + + if (num_dspdevs && !dsp_initialized) /* Audio devices present */ + { + dsp_initialized = 1; + mem_start = DMAbuf_init (mem_start); + mem_start = audio_init (mem_start); + mem_start = dsp_init (mem_start); + } + +/** UWM stuff **/ + +#ifndef EXCLUDE_CHIP_MIDI + + if (!generic_midi_initialized) + { + generic_midi_initialized = 1; + mem_start = CMIDI_init (mem_start); + } + +#endif + +#ifndef EXCLUDE_MPU401 + if (num_midis && !midi_initialized) + { + midi_initialized = 1; + mem_start = MIDIbuf_init (mem_start); + } +#endif + + if ((num_midis + num_synths) && !seq_initialized) + { + seq_initialized = 1; + mem_start = sequencer_init (mem_start); + } + + for (i = 0; i < SND_NDEVS; i++) + { + sbc_devices[i].usecount = 0; + } + + return TRUE; +} + +void +tenmicrosec (void) +{ + int i; + + for (i = 0; i < 16; i++) + inb (0x80); +} + +#ifdef EXCLUDE_GUS +void +gusintr (int unit) +{ + return (0); +} +#endif + +void +request_sound_timer (int count) +{ + static int current = 0; + int tmp = count; + + if (count < 0) + timeout (sequencer_timer, 0, -count); + else + { + + if (count < current) + current = 0; /* Timer restarted */ + + count = count - current; + + current = tmp; + + if (!count) + count = 1; + + timeout (sequencer_timer, 0, count); + } + timer_running = 1; +} + +void +sound_stop_timer (void) +{ + if (timer_running) + untimeout (sequencer_timer, 0); + timer_running = 0; +} + +#ifndef EXCLUDE_AUDIO +static void +sound_mem_init (void) +{ + int i, dev; + unsigned long dma_pagesize; + static unsigned long dsp_init_mask = 0; + + for (dev = 0; dev < num_dspdevs; dev++) /* Enumerate devices */ + if (!(dsp_init_mask & (1 << dev))) /* Not already done */ + if (sound_buffcounts[dev] > 0 && sound_dsp_dmachan[dev] > 0) + { + dsp_init_mask |= (1 << dev); + + if (sound_dma_automode[dev]) + { + sound_dma_automode[dev] = 0; /* Not possible with 386BSD */ + } + + if (sound_buffcounts[dev] == 1) + { + sound_buffcounts[dev] = 2; + sound_buffsizes[dev] /= 2; + } + + if (sound_buffsizes[dev] > 65536) /* Larger is not possible (yet) */ + sound_buffsizes[dev] = 65536; + + if (sound_dsp_dmachan[dev] > 3 && sound_buffsizes[dev] > 65536) + dma_pagesize = 131072; /* 128k */ + else + dma_pagesize = 65536; + + /* More sanity checks */ + + if (sound_buffsizes[dev] > dma_pagesize) + sound_buffsizes[dev] = dma_pagesize; + sound_buffsizes[dev] &= 0xfffff000; /* Truncate to n*4k */ + if (sound_buffsizes[dev] < 4096) + sound_buffsizes[dev] = 4096; + + /* Now allocate the buffers */ + + for (snd_raw_count[dev] = 0; snd_raw_count[dev] < sound_buffcounts[dev]; snd_raw_count[dev]++) + { + /* + * The DMA buffer allocation algorithm hogs memory. We allocate + * a memory area which is two times the requires size. This + * guarantees that it contains at least one valid DMA buffer. + * + * This really needs some kind of finetuning. + */ + char *tmpbuf = malloc (2*sound_buffsizes[dev], M_DEVBUF, M_NOWAIT); + unsigned long addr, rounded; + + if (tmpbuf == NULL) + { + printk ("snd: Unable to allocate %d bytes of buffer\n", + 2 * sound_buffsizes[dev]); + return; + } + + addr = kvtop (tmpbuf); + /* + * Align the start address + */ + rounded = (addr & ~(dma_pagesize - 1)) + dma_pagesize; + + snd_raw_buf[dev][snd_raw_count[dev]] = + &tmpbuf[rounded - addr]; /* Compute offset */ + /* + * Use virtual address as the physical address, since + * isa_dmastart performs the phys address computation. + */ + snd_raw_buf_phys[dev][snd_raw_count[dev]] = + (unsigned long) snd_raw_buf[dev][snd_raw_count[dev]]; + } + } /* for dev */ + +} + +#endif + +struct isa_driver snddriver = +{sndprobe, sndattach, "snd"}; + +int +snd_ioctl_return (int *addr, int value) +{ + if (value < 0) + return value; /* Error */ + suword (addr, value); + return 0; +} + +#endif diff --git a/sys/i386/isa/sound/soundcard.h b/sys/i386/isa/sound/soundcard.h new file mode 100644 index 0000000000..f853de470d --- /dev/null +++ b/sys/i386/isa/sound/soundcard.h @@ -0,0 +1,737 @@ +#ifndef SOUNDCARD_H +#define SOUNDCARD_H +/* + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + */ + + /* + * If you make modifications to this file, please contact me before + * distributing the modified version. There is already enough + * divercity in the world. + * + * Regards, + * Hannu Savolainen + * hsavolai@cs.helsinki.fi + */ + +#define SOUND_VERSION 200 + +#include + +/* + * Supported card ID numbers (Should be somewhere else?) + */ + +#define SNDCARD_ADLIB 1 +#define SNDCARD_SB 2 +#define SNDCARD_PAS 3 +#define SNDCARD_GUS 4 +#define SNDCARD_MPU401 5 + +/*********************************** + * IOCTL Commands for /dev/sequencer + */ + +#ifndef _IOWR +/* @(#)ioctlp.h */ + +/* Ioctl's have the command encoded in the lower word, + * and the size of any in or out parameters in the upper + * word. The high 2 bits of the upper word are used + * to encode the in/out status of the parameter; for now + * we restrict parameters to at most 128 bytes. + */ +/* #define IOCTYPE (0xff<<8) */ +#define IOCPARM_MASK 0x7f /* parameters must be < 128 bytes */ +#define IOC_VOID 0x20000000 /* no parameters */ +#define IOC_OUT 0x40000000 /* copy out parameters */ +#define IOC_IN 0x80000000 /* copy in parameters */ +#define IOC_INOUT (IOC_IN|IOC_OUT) +/* the 0x20000000 is so we can distinguish new ioctl's from old */ +#define _IO(x,y) ((int)(IOC_VOID|(x<<8)|y)) +#define _IOR(x,y,t) ((int)(IOC_OUT|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y)) +#define _IOW(x,y,t) ((int)(IOC_IN|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y)) +/* this should be _IORW, but stdio got there first */ +#define _IOWR(x,y,t) ((int)(IOC_INOUT|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y)) +#endif /* !_IOWR */ + +#define SNDCTL_SEQ_RESET _IO ('Q', 0) +#define SNDCTL_SEQ_SYNC _IO ('Q', 1) +#define SNDCTL_SYNTH_INFO _IOWR('Q', 2, struct synth_info) +#define SNDCTL_SEQ_CTRLRATE _IOWR('Q', 3, int) /* Set/get timer resolution (HZ) */ +#define SNDCTL_SEQ_GETOUTCOUNT _IOR ('Q', 4, int) +#define SNDCTL_SEQ_GETINCOUNT _IOR ('Q', 5, int) +#define SNDCTL_SEQ_PERCMODE _IOW ('Q', 6, int) +#define SNDCTL_FM_LOAD_INSTR _IOW ('Q', 7, struct sbi_instrument) /* Valid for FM only */ +#define SNDCTL_SEQ_TESTMIDI _IOW ('Q', 8, int) +#define SNDCTL_SEQ_RESETSAMPLES _IOW ('Q', 9, int) +#define SNDCTL_SEQ_NRSYNTHS _IOR ('Q',10, int) +#define SNDCTL_SEQ_NRMIDIS _IOR ('Q',11, int) +#define SNDCTL_MIDI_INFO _IOWR('Q',12, struct midi_info) +#define SNDCTL_SEQ_TRESHOLD _IOW ('Q',13, int) +#define SNDCTL_SYNTH_MEMAVL _IOWR('Q',14, int) /* in=dev#, out=memsize */ +#define SNDCTL_FM_4OP_ENABLE _IOW ('Q',15, int) /* in=dev# */ +#define SNDCTL_PMGR_ACCESS _IOWR('Q',16, struct patmgr_info) + +/* + * Sample loading mechanism for internal synthesizers (/dev/sequencer) + * The following patch_info structure has been designed to support + * Gravis UltraSound. It tries to be universal format for uploading + * sample based patches but is propably too limited. + */ + +struct patch_info { + short key; /* Use GUS_PATCH here */ +#define GUS_PATCH 0x04fd +#define OBSOLETE_GUS_PATCH 0x02fd + short device_no; /* Synthesizer number */ + short instr_no; /* Midi pgm# */ + + unsigned long mode; +/* + * The least significant byte has the same format than the GUS .PAT + * files + */ +#define WAVE_16_BITS 0x01 /* bit 0 = 8 or 16 bit wave data. */ +#define WAVE_UNSIGNED 0x02 /* bit 1 = Signed - Unsigned data. */ +#define WAVE_LOOPING 0x04 /* bit 2 = looping enabled-1. */ +#define WAVE_BIDIR_LOOP 0x08 /* bit 3 = Set is bidirectional looping. */ +#define WAVE_LOOP_BACK 0x10 /* bit 4 = Set is looping backward. */ +#define WAVE_SUSTAIN_ON 0x20 /* bit 5 = Turn sustaining on. (Env. pts. 3)*/ +#define WAVE_ENVELOPES 0x40 /* bit 6 = Enable envelopes - 1 */ + /* (use the env_rate/env_offs fields). */ +/* Linux specific bits */ +#define WAVE_VIBRATO 0x00010000 /* The vibrato info is valid */ +#define WAVE_TREMOLO 0x00020000 /* The tremolo info is valid */ +#define WAVE_SCALE 0x00040000 /* The scaling info is valid */ +/* Other bits must be zeroed */ + + long len; /* Size of the wave data in bytes */ + long loop_start, loop_end; /* Byte offsets from the beginning */ + +/* + * The base_freq and base_note fields are used when computing the + * playback speed for a note. The base_note defines the tone frequency + * which is heard if the sample is played using the base_freq as the + * playback speed. + * + * The low_note and high_note fields define the minimum and maximum note + * frequencies for which this sample is valid. It is possible to define + * more than one samples for a instrument number at the same time. The + * low_note and high_note fields are used to select the most suitable one. + * + * The fields base_note, high_note and low_note should contain + * the note frequency multiplied by 1000. For example value for the + * middle A is 440*1000. + */ + + unsigned int base_freq; + unsigned long base_note; + unsigned long high_note; + unsigned long low_note; + int panning; /* -128=left, 127=right */ + int detuning; + +/* New fields introduced in version 1.99.5 */ + + /* Envelope. Enabled by mode bit WAVE_ENVELOPES */ + unsigned char env_rate[ 6 ]; /* GUS HW ramping rate */ + unsigned char env_offset[ 6 ]; /* 255 == 100% */ + + /* + * The tremolo, vibrato and scale info are not supported yet. + * Enable by setting the mode bits WAVE_TREMOLO, WAVE_VIBRATO or + * WAVE_SCALE + */ + + unsigned char tremolo_sweep; + unsigned char tremolo_rate; + unsigned char tremolo_depth; + + unsigned char vibrato_sweep; + unsigned char vibrato_rate; + unsigned char vibrato_depth; + + int scale_frequency; + unsigned int scale_factor; /* from 0 to 2048 or 0 to 2 */ + + int volume; + int spare[4]; + char data[0]; /* The waveform data starts here */ + }; + + +/* + * Patch management interface (/dev/sequencer, /dev/patmgr#) + * Don't use these calls if you want to maintain compatibility with + * the future versions of the driver. + */ + +#define PS_NO_PATCHES 0 /* No patch support on device */ +#define PS_MGR_NOT_OK 1 /* Plain patch support (no mgr) */ +#define PS_MGR_OK 2 /* Patch manager supported */ +#define PS_MANAGED 3 /* Patch manager running */ + +#define SNDCTL_PMGR_IFACE _IOWR('P', 1, struct patmgr_info) + +/* + * The patmgr_info is a fixed size structure which is used for two + * different purposes. The intended use is for communication between + * the application using /dev/sequencer and the patch manager daemon + * associated with a synthesizer device (ioctl(SNDCTL_PMGR_ACCESS)). + * + * This structure is also used with ioctl(SNDCTL_PGMR_IFACE) which allows + * a patch manager daemon to read and write device parameters. This + * ioctl available through /dev/sequencer also. Avoid using it since it's + * extremely hardware dependent. In addition access trough /dev/sequencer + * may confuse the patch manager daemon. + */ + +struct patmgr_info { /* Note! size must be < 4k since kmalloc() is used */ + unsigned long key; /* Don't worry. Reserved for communication + between the patch manager and the driver. */ +#define PM_K_EVENT 1 /* Event from the /dev/sequencer driver */ +#define PM_K_COMMAND 2 /* Request from a application */ +#define PM_K_RESPONSE 3 /* From patmgr to application */ +#define PM_ERROR 4 /* Error returned by the patmgr */ + int device; + int command; + +/* + * Commands 0x000 to 0xfff reserved for patch manager programs + */ +#define PM_GET_DEVTYPE 1 /* Returns type of the patch mgr interface of dev */ +#define PMTYPE_FM2 1 /* 2 OP fm */ +#define PMTYPE_FM4 2 /* Mixed 4 or 2 op FM (OPL-3) */ +#define PMTYPE_WAVE 3 /* Wave table synthesizer (GUS) */ +#define PM_GET_NRPGM 2 /* Returns max # of midi programs in parm1 */ +#define PM_GET_PGMMAP 3 /* Returns map of loaded midi programs in data8 */ +#define PM_GET_PGM_PATCHES 4 /* Return list of patches of a program (parm1) */ +#define PM_GET_PATCH 5 /* Return patch header of patch parm1 */ +#define PM_SET_PATCH 6 /* Set patch header of patch parm1 */ +#define PM_READ_PATCH 7 /* Read patch (wave) data */ +#define PM_WRITE_PATCH 8 /* Write patch (wave) data */ + +/* + * Commands 0x1000 to 0xffff are for communication between the patch manager + * and the client + */ +#define _PM_LOAD_PATCH 0x100 + +/* + * Commands above 0xffff reserved for device specific use + */ + + long parm1; + long parm2; + long parm3; + + union { + unsigned char data8[4000]; + unsigned short data16[2000]; + unsigned long data32[1000]; + struct patch_info patch; + } data; + }; + +/* + * When a patch manager daemon is present, it will be informed by the + * driver when something important happens. For example when the + * /dev/sequencer is opened or closed. A record with key == PM_K_EVENT is + * returned. The command field contains the event type: + */ +#define PM_E_OPENED 1 /* /dev/sequencer opened */ +#define PM_E_CLOSED 2 /* /dev/sequencer closed */ +#define PM_E_PATCH_RESET 3 /* SNDCTL_RESETSAMPLES called */ +#define PM_E_PATCH_LOADED 4 /* A patch has been loaded by appl */ + +/* + * /dev/sequencer input events. + * + * The data written to the /dev/sequencer is a stream of events. Events + * are records of 4 or 8 bytes. The first byte defines the size. + * Any number of events can be written with a write call. There + * is a set of macros for sending these events. Use these macros if you + * want to maximize portability of your program. + * + * Events SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO. Are also input events. + * (All input events are currently 4 bytes long. Be prepared to support + * 8 byte events also. If you receive any event having first byte >= 0xf0, + * it's a 8 byte event. + * + * The events are documented at the end of this file. + * + * Normal events (4 bytes) + * There is also a 8 byte version of most of the 4 byte events. The + * 8 byte one is recommended. + */ +#define SEQ_NOTEOFF 0 +#define SEQ_FMNOTEOFF SEQ_NOTEOFF /* Just old name */ +#define SEQ_NOTEON 1 +#define SEQ_FMNOTEON SEQ_NOTEON +#define SEQ_WAIT 2 +#define SEQ_PGMCHANGE 3 +#define SEQ_FMPGMCHANGE SEQ_PGMCHANGE +#define SEQ_SYNCTIMER 4 +#define SEQ_MIDIPUTC 5 +#define SEQ_DRUMON 6 /*** OBSOLETE ***/ +#define SEQ_DRUMOFF 7 /*** OBSOLETE ***/ +#define SEQ_ECHO 8 /* For synching programs with output */ +#define SEQ_AFTERTOUCH 9 +#define SEQ_CONTROLLER 10 +#define CTRL_PITCH_BENDER 255 +#define CTRL_PITCH_BENDER_RANGE 254 +#define CTRL_EXPRESSION 253 +#define CTRL_MAIN_VOLUME 252 +#define SEQ_BALANCE 11 + +/* + * Note! SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO are used also as + * input events. + */ + +/* + * Event codes 0xf0 to 0xfc are reserved for future extensions. + */ + +#define SEQ_FULLSIZE 0xfd /* Long events */ +/* + * SEQ_FULLSIZE events are used for loading patches/samples to the + * synthesizer devices. These events are passed directly to the driver + * of the associated synthesizer device. There is no limit to the size + * of the extended events. These events are not queued but executed + * immediately when the write() is called (execution can take several + * seconds of time). + * + * When a SEQ_FULLSIZE message is written to the device, it must + * be written using exactly one write() call. Other events cannot + * be mixed to the same write. + * + * For FM synths (YM3812/OPL3) use struct sbi_instrument and write it to the + * /dev/sequencer. Don't write other data together with the instrument structure + * Set the key field of the structure to FM_PATCH. The device field is used to + * route the patch to the corresponding device. + * + * For Gravis UltraSound use struct patch_info. Initialize the key field + * to GUS_PATCH. + */ +#define SEQ_PRIVATE 0xfe /* Low level HW dependent events (8 bytes) */ +#define SEQ_EXTENDED 0xff /* Extended events (8 bytes) */ + +/* + * Extended events for synthesizers (8 bytes) + * + * Format: + * + * b0 = SEQ_EXTENDED + * b1 = command + * b2 = device + * b3-b7 = parameters + * + * Command b3 b4 b5 b6 b7 + * ---------------------------------------------------------------------------- + * SEQ_NOTEON voice note volume 0 0 + * SEQ_NOTEOFF voice note volume 0 0 + * SEQ_PGMCHANGE voice pgm 0 0 0 + * SEQ_DRUMON (voice) drum# volume 0 0 + * SEQ_DRUMOFF (voice) drum# volume 0 0 + */ + +/* + * Record for FM patches + */ + +typedef unsigned char sbi_instr_data[32]; + +struct sbi_instrument { + unsigned short key; /* Initialize to FM_PATCH or OPL3_PATCH */ +#define FM_PATCH 0x01fd +#define OPL3_PATCH 0x03fd + short device; /* Synth# (0-4) */ + int channel; /* Program# to be initialized */ + sbi_instr_data operators; /* Register settings for operator cells (.SBI format) */ + }; + +struct synth_info { /* Read only */ + char name[30]; + int device; /* 0-N. INITIALIZE BEFORE CALLING */ + int synth_type; +#define SYNTH_TYPE_FM 0 +#define SYNTH_TYPE_SAMPLE 1 + + int synth_subtype; +#define FM_TYPE_ADLIB 0x00 +#define FM_TYPE_OPL3 0x01 + +#define SAMPLE_TYPE_GUS 0x10 + + int perc_mode; /* No longer supported */ + int nr_voices; + int nr_drums; /* Obsolete field */ + int instr_bank_size; + unsigned long capabilities; +#define SYNTH_CAP_PERCMODE 0x00000001 /* No longer used */ +#define SYNTH_CAP_OPL3 0x00000002 /* Set if OPL3 supported */ + int dummies[19]; /* Reserve space */ + }; + +struct midi_info { + char name[30]; + int device; /* 0-N. INITIALIZE BEFORE CALLING */ + unsigned long capabilities; /* To be defined later */ + int dummies[19]; /* Reserve space */ + }; + +/******************************************** + * IOCTL commands for /dev/dsp and /dev/audio + */ + +#define SNDCTL_DSP_RESET _IO ('P', 0) +#define SNDCTL_DSP_SYNC _IO ('P', 1) +#define SNDCTL_DSP_SPEED _IOWR('P', 2, int) +#define SNDCTL_DSP_STEREO _IOWR('P', 3, int) +#define SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int) +#define SNDCTL_DSP_SAMPLESIZE _IOWR('P', 5, int) /* 8, 12 or 16 */ +#define SOUND_PCM_WRITE_CHANNELS _IOWR('P', 6, int) +#define SOUND_PCM_WRITE_FILTER _IOWR('P', 7, int) +#define SNDCTL_DSP_POST _IO ('P', 8) + +#define SOUND_PCM_READ_RATE _IOR ('P', 2, int) +#define SOUND_PCM_READ_CHANNELS _IOR ('P', 6, int) +#define SOUND_PCM_READ_BITS _IOR ('P', 5, int) +#define SOUND_PCM_READ_FILTER _IOR ('P', 7, int) + +/* Some alias names */ +#define SOUND_PCM_WRITE_BITS SNDCTL_DSP_SAMPLESIZE +#define SOUND_PCM_WRITE_RATE SNDCTL_DSP_SPEED +#define SOUND_PCM_POST SNDCTL_DSP_POST +#define SOUND_PCM_RESET SNDCTL_DSP_RESET +#define SOUND_PCM_SYNC SNDCTL_DSP_SYNC + +/********************************************* + * IOCTL commands for /dev/mixer + */ + +/* + * Mixer devices + * + * There can be up to 20 different analog mixer channels. The + * SOUND_MIXER_NRDEVICES gives the currently supported maximum. + * The SOUND_MIXER_READ_DEVMASK returns a bitmask which tells + * the devices supported by the particular mixer. + */ + +#define SOUND_MIXER_NRDEVICES 12 +#define SOUND_MIXER_VOLUME 0 +#define SOUND_MIXER_BASS 1 +#define SOUND_MIXER_TREBLE 2 +#define SOUND_MIXER_SYNTH 3 +#define SOUND_MIXER_PCM 4 +#define SOUND_MIXER_SPEAKER 5 +#define SOUND_MIXER_LINE 6 +#define SOUND_MIXER_MIC 7 +#define SOUND_MIXER_CD 8 +#define SOUND_MIXER_IMIX 9 /* Recording monitor */ +#define SOUND_MIXER_ALTPCM 10 +#define SOUND_MIXER_RECLEV 11 /* Recording level */ + +/* Some on/off settings (SOUND_SPECIAL_MIN - SOUND_SPECIAL_MAX) */ +/* Not counted to SOUND_MIXER_NRDEVICES, but use the same number space */ +#define SOUND_ONOFF_MIN 28 +#define SOUND_ONOFF_MAX 30 +#define SOUND_MIXER_MUTE 28 /* 0 or 1 */ +#define SOUND_MIXER_ENHANCE 29 /* Enhanced stereo (0, 40, 60 or 80) */ +#define SOUND_MIXER_LOUD 30 /* 0 or 1 */ + +/* Note! Number 31 cannot be used since the sign bit is reserved */ + +#define SOUND_DEVICE_LABELS {"Vol ", "Bass ", "Trebl", "Synth", "Pcm ", "Spkr ", "Line ", \ + "Mic ", "CD ", "Mix ", "Pcm2 ", "rec"} + +#define SOUND_DEVICE_NAMES {"vol", "bass", "treble", "synth", "pcm", "speaker", "line", \ + "mic", "cd", "mix", "pcm2", "rec"} + +/* Device bitmask identifiers */ + +#define SOUND_MIXER_RECSRC 0xff /* Arg contains a bit for each recording source */ +#define SOUND_MIXER_DEVMASK 0xfe /* Arg contains a bit for each supported device */ +#define SOUND_MIXER_RECMASK 0xfd /* Arg contains a bit for each supported recording source */ +#define SOUND_MIXER_CAPS 0xfc + #define SOUND_CAP_EXCL_INPUT 0x00000001 /* Only one recording source at a time */ +#define SOUND_MIXER_STEREODEVS 0xfb /* Mixer channels supporting stereo */ + +/* Device mask bits */ + +#define SOUND_MASK_VOLUME (1 << SOUND_MIXER_VOLUME) +#define SOUND_MASK_BASS (1 << SOUND_MIXER_BASS) +#define SOUND_MASK_TREBLE (1 << SOUND_MIXER_TREBLE) +#define SOUND_MASK_SYNTH (1 << SOUND_MIXER_SYNTH) +#define SOUND_MASK_PCM (1 << SOUND_MIXER_PCM) +#define SOUND_MASK_SPEAKER (1 << SOUND_MIXER_SPEAKER) +#define SOUND_MASK_LINE (1 << SOUND_MIXER_LINE) +#define SOUND_MASK_MIC (1 << SOUND_MIXER_MIC) +#define SOUND_MASK_CD (1 << SOUND_MIXER_CD) +#define SOUND_MASK_IMIX (1 << SOUND_MIXER_IMIX) +#define SOUND_MASK_ALTPCM (1 << SOUND_MIXER_ALTPCM) +#define SOUND_MASK_RECLEV (1 << SOUND_MIXER_RECLEV) + +#define SOUND_MASK_MUTE (1 << SOUND_MIXER_MUTE) +#define SOUND_MASK_ENHANCE (1 << SOUND_MIXER_ENHANCE) +#define SOUND_MASK_LOUD (1 << SOUND_MIXER_LOUD) + +#define MIXER_READ(dev) _IOR('M', dev, int) +#define SOUND_MIXER_READ_VOLUME MIXER_READ(SOUND_MIXER_VOLUME) +#define SOUND_MIXER_READ_BASS MIXER_READ(SOUND_MIXER_BASS) +#define SOUND_MIXER_READ_TREBLE MIXER_READ(SOUND_MIXER_TREBLE) +#define SOUND_MIXER_READ_SYNTH MIXER_READ(SOUND_MIXER_SYNTH) +#define SOUND_MIXER_READ_PCM MIXER_READ(SOUND_MIXER_PCM) +#define SOUND_MIXER_READ_SPEAKER MIXER_READ(SOUND_MIXER_SPEAKER) +#define SOUND_MIXER_READ_LINE MIXER_READ(SOUND_MIXER_LINE) +#define SOUND_MIXER_READ_MIC MIXER_READ(SOUND_MIXER_MIC) +#define SOUND_MIXER_READ_CD MIXER_READ(SOUND_MIXER_CD) +#define SOUND_MIXER_READ_IMIX MIXER_READ(SOUND_MIXER_IMIX) +#define SOUND_MIXER_READ_ALTPCM MIXER_READ(SOUND_MIXER_ALTPCM) +#define SOUND_MIXER_READ_RECLEV MIXER_READ(SOUND_MIXER_RECLEV) +#define SOUND_MIXER_READ_MUTE MIXER_READ(SOUND_MIXER_MUTE) +#define SOUND_MIXER_READ_ENHANCE MIXER_READ(SOUND_MIXER_ENHANCE) +#define SOUND_MIXER_READ_LOUD MIXER_READ(SOUND_MIXER_LOUD) + +#define SOUND_MIXER_READ_RECSRC MIXER_READ(SOUND_MIXER_RECSRC) +#define SOUND_MIXER_READ_DEVMASK MIXER_READ(SOUND_MIXER_DEVMASK) +#define SOUND_MIXER_READ_RECMASK MIXER_READ(SOUND_MIXER_RECMASK) +#define SOUND_MIXER_READ_STEREODEVS MIXER_READ(SOUND_MIXER_STEREODEVS) +#define SOUND_MIXER_READ_CAPS MIXER_READ(SOUND_MIXER_CAPS) + +#define MIXER_WRITE(dev) _IOWR('M', dev, int) +#define SOUND_MIXER_WRITE_VOLUME MIXER_WRITE(SOUND_MIXER_VOLUME) +#define SOUND_MIXER_WRITE_BASS MIXER_WRITE(SOUND_MIXER_BASS) +#define SOUND_MIXER_WRITE_TREBLE MIXER_WRITE(SOUND_MIXER_TREBLE) +#define SOUND_MIXER_WRITE_SYNTH MIXER_WRITE(SOUND_MIXER_SYNTH) +#define SOUND_MIXER_WRITE_PCM MIXER_WRITE(SOUND_MIXER_PCM) +#define SOUND_MIXER_WRITE_SPEAKER MIXER_WRITE(SOUND_MIXER_SPEAKER) +#define SOUND_MIXER_WRITE_LINE MIXER_WRITE(SOUND_MIXER_LINE) +#define SOUND_MIXER_WRITE_MIC MIXER_WRITE(SOUND_MIXER_MIC) +#define SOUND_MIXER_WRITE_CD MIXER_WRITE(SOUND_MIXER_CD) +#define SOUND_MIXER_WRITE_IMIX MIXER_WRITE(SOUND_MIXER_IMIX) +#define SOUND_MIXER_WRITE_ALTPCM MIXER_WRITE(SOUND_MIXER_ALTPCM) +#define SOUND_MIXER_WRITE_RECLEV MIXER_WRITE(SOUND_MIXER_RECLEV) +#define SOUND_MIXER_WRITE_MUTE MIXER_WRITE(SOUND_MIXER_MUTE) +#define SOUND_MIXER_WRITE_ENHANCE MIXER_WRITE(SOUND_MIXER_ENHANCE) +#define SOUND_MIXER_WRITE_LOUD MIXER_WRITE(SOUND_MIXER_LOUD) + +#define SOUND_MIXER_WRITE_RECSRC MIXER_WRITE(SOUND_MIXER_RECSRC) + +/* + * The following mixer ioctl calls are compatible with the BSD driver by + * Steve Haehnichen + * + * Since this interface is entirely SB specific, it will be dropped in the + * near future. + */ + +typedef unsigned char S_BYTE; +typedef unsigned char S_FLAG; +struct stereo_vol +{ + S_BYTE l; /* Left volume */ + S_BYTE r; /* Right volume */ +}; + +#define MIXER_IOCTL_SET_LEVELS _IOW ('s', 20, struct sb_mixer_levels) +#define MIXER_IOCTL_SET_PARAMS _IOW ('s', 21, struct sb_mixer_params) +#define MIXER_IOCTL_READ_LEVELS _IOR ('s', 22, struct sb_mixer_levels) +#define MIXER_IOCTL_READ_PARAMS _IOR ('s', 23, struct sb_mixer_params) +#define MIXER_IOCTL_RESET _IO ('s', 24) + +/* + * Mixer volume levels for MIXER_IOCTL_SET_VOL & MIXER_IOCTL_READ_VOL + */ +struct sb_mixer_levels +{ + struct stereo_vol master; /* Master volume */ + struct stereo_vol voc; /* DSP Voice volume */ + struct stereo_vol fm; /* FM volume */ + struct stereo_vol line; /* Line-in volume */ + struct stereo_vol cd; /* CD audio */ + S_BYTE mic; /* Microphone level */ +}; + +/* + * Mixer parameters for MIXER_IOCTL_SET_PARAMS & MIXER_IOCTL_READ_PARAMS + */ +struct sb_mixer_params +{ + S_BYTE record_source; /* Recording source (See SRC_xxx below) */ + S_FLAG hifreq_filter; /* Filter frequency (hi/low) */ + S_FLAG filter_input; /* ANFI input filter */ + S_FLAG filter_output; /* DNFI output filter */ + S_FLAG dsp_stereo; /* 1 if DSP is in Stereo mode */ +}; + +#define SRC_MIC 1 /* Select Microphone recording source */ +#define SRC_CD 3 /* Select CD recording source */ +#define SRC_LINE 7 /* Use Line-in for recording source */ + +#if !defined(KERNEL) && !defined(INKERNEL) +/* + * Some convenience macros to simplify programming of the + * /dev/sequencer interface + * + * These macros define the API which should be used when possible. + */ + +void seqbuf_dump(); /* This function must be provided by programs */ + +/* Sample seqbuf_dump() implementation: + * + * SEQ_DEFINEBUF (2048); -- Defines a buffer for 2048 bytes + * + * int seqfd; -- The file descriptor for /dev/sequencer. + * + * void + * seqbuf_dump () + * { + * if (_seqbufptr) + * if (write (seqfd, _seqbuf, _seqbufptr) == -1) + * { + * perror ("write /dev/sequencer"); + * exit (-1); + * } + * _seqbufptr = 0; + * } + */ + +#define SEQ_DEFINEBUF(len) unsigned char _seqbuf[len]; int _seqbuflen = len, _seqbufptr = 0 +#define SEQ_PM_DEFINES struct patmgr_info _pm_info +#define _SEQ_NEEDBUF(len) if ((_seqbufptr+(len)) > _seqbuflen) seqbuf_dump() +#define _SEQ_ADVBUF(len) _seqbufptr += len +#define SEQ_DUMPBUF seqbuf_dump +#define PM_LOAD_PATCH(dev, bank, pgm) (SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \ + _pm_info.device=dev, _pm_info.data.data8[0]=pgm, \ + _pm_info.parm1 = bank, _pm_info.parm2 = 1, \ + ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info)) +#define PM_LOAD_PATCHES(dev, bank, pgm) (SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \ + _pm_info.device=dev, memcpy(_pm_info.data.data8, pgm, 128), \ + _pm_info.parm1 = bank, _pm_info.parm2 = 128, \ + ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info)) + +#define SEQ_START_NOTE(dev, voice, note, vol) {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr] = SEQ_EXTENDED;\ + _seqbuf[_seqbufptr+1] = SEQ_NOTEON;\ + _seqbuf[_seqbufptr+2] = (dev);\ + _seqbuf[_seqbufptr+3] = (voice);\ + _seqbuf[_seqbufptr+4] = (note);\ + _seqbuf[_seqbufptr+5] = (vol);\ + _seqbuf[_seqbufptr+6] = 0;\ + _seqbuf[_seqbufptr+7] = 0;\ + _SEQ_ADVBUF(8);} + +#define SEQ_STOP_NOTE(dev, voice, note, vol) {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr] = SEQ_EXTENDED;\ + _seqbuf[_seqbufptr+1] = SEQ_NOTEOFF;\ + _seqbuf[_seqbufptr+2] = (dev);\ + _seqbuf[_seqbufptr+3] = (voice);\ + _seqbuf[_seqbufptr+4] = (note);\ + _seqbuf[_seqbufptr+5] = (vol);\ + _seqbuf[_seqbufptr+6] = 0;\ + _seqbuf[_seqbufptr+7] = 0;\ + _SEQ_ADVBUF(8);} + +#define SEQ_CHN_PRESSURE(dev, voice, pressure) {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr] = SEQ_EXTENDED;\ + _seqbuf[_seqbufptr+1] = SEQ_AFTERTOUCH;\ + _seqbuf[_seqbufptr+2] = (dev);\ + _seqbuf[_seqbufptr+3] = (voice);\ + _seqbuf[_seqbufptr+4] = (pressure);\ + _seqbuf[_seqbufptr+5] = 0;\ + _seqbuf[_seqbufptr+6] = 0;\ + _seqbuf[_seqbufptr+7] = 0;\ + _SEQ_ADVBUF(8);} + +#define SEQ_PANNING(dev, voice, pos) {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr] = SEQ_EXTENDED;\ + _seqbuf[_seqbufptr+1] = SEQ_BALANCE;\ + _seqbuf[_seqbufptr+2] = (dev);\ + _seqbuf[_seqbufptr+3] = (voice);\ + (char)_seqbuf[_seqbufptr+4] = (pos);\ + _seqbuf[_seqbufptr+5] = 0;\ + _seqbuf[_seqbufptr+6] = 0;\ + _seqbuf[_seqbufptr+7] = 0;\ + _SEQ_ADVBUF(8);} + +#define SEQ_CONTROL(dev, voice, controller, value) {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr] = SEQ_EXTENDED;\ + _seqbuf[_seqbufptr+1] = SEQ_CONTROLLER;\ + _seqbuf[_seqbufptr+2] = (dev);\ + _seqbuf[_seqbufptr+3] = (voice);\ + _seqbuf[_seqbufptr+4] = (controller);\ + *(short *)&_seqbuf[_seqbufptr+5] = (value);\ + _seqbuf[_seqbufptr+7] = 0;\ + _SEQ_ADVBUF(8);} + +#define SEQ_PITCHBEND(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_PITCH_BENDER, value) +#define SEQ_BENDER_RANGE(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_PITCH_BENDER_RANGE, value) +#define SEQ_EXPRESSION(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_EXPRESSION, value) +#define SEQ_MAIN_VOLUME(dev, voice, value) SEQ_CONTROL(dev, voice, CTRL_MAIN_VOLUME, value) + +#define SEQ_START_TIMER() {_SEQ_NEEDBUF(4);\ + _seqbuf[_seqbufptr] = SEQ_SYNCTIMER;\ + _seqbuf[_seqbufptr+1] = 0;\ + _seqbuf[_seqbufptr+2] = 0;\ + _seqbuf[_seqbufptr+3] = 0;\ + _SEQ_ADVBUF(4);} +#define SEQ_SET_PATCH(dev, voice, patch) {_SEQ_NEEDBUF(8);\ + _seqbuf[_seqbufptr] = SEQ_EXTENDED;\ + _seqbuf[_seqbufptr+1] = SEQ_PGMCHANGE;\ + _seqbuf[_seqbufptr+2] = (dev);\ + _seqbuf[_seqbufptr+3] = (voice);\ + _seqbuf[_seqbufptr+4] = (patch);\ + _seqbuf[_seqbufptr+5] = 0;\ + _seqbuf[_seqbufptr+6] = 0;\ + _seqbuf[_seqbufptr+7] = 0;\ + _SEQ_ADVBUF(8);} + +#define SEQ_WAIT_TIME(ticks) {_SEQ_NEEDBUF(4);\ + *(unsigned long *)&_seqbuf[_seqbufptr] = SEQ_WAIT | ((ticks) << 8);\ + _SEQ_ADVBUF(4);} + +#define SEQ_ECHO_BACK(key) {_SEQ_NEEDBUF(4);\ + *(unsigned long *)&_seqbuf[_seqbufptr] = SEQ_ECHO | ((key) << 8);\ + _SEQ_ADVBUF(4);} + +#define SEQ_MIDIOUT(device, byte) {_SEQ_NEEDBUF(4);\ + _seqbuf[_seqbufptr] = SEQ_MIDIPUTC;\ + _seqbuf[_seqbufptr+1] = (byte);\ + _seqbuf[_seqbufptr+2] = (device);\ + _seqbuf[_seqbufptr+3] = 0;\ + _SEQ_ADVBUF(4);} +#define SEQ_WRPATCH(patch, len) {if (_seqbufptr) seqbuf_dump();\ + if (write(seqfd, (char*)(patch), len)==-1) \ + perror("Write patch: /dev/sequencer");} + +#endif +long soundcard_init(long mem_start); +#endif diff --git a/sys/i386/isa/sound/tuning.h b/sys/i386/isa/sound/tuning.h new file mode 100644 index 0000000000..858e1fe6c6 --- /dev/null +++ b/sys/i386/isa/sound/tuning.h @@ -0,0 +1,29 @@ +#ifdef SEQUENCER_C + +unsigned short semitone_tuning[24] = +{ +/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983, +/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784, +/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755 +}; + +unsigned short cent_tuning[100] = +{ +/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041, +/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087, +/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134, +/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181, +/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228, +/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275, +/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323, +/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371, +/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419, +/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467, +/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515, +/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564, +/* 96 */ 10570, 10576, 10582, 10589 +}; +#else +extern unsigned short semitone_tuning[24]; +extern unsigned short cent_tuning[100]; +#endif diff --git a/sys/i386/isa/sound/ulaw.h b/sys/i386/isa/sound/ulaw.h new file mode 100644 index 0000000000..be9f92d998 --- /dev/null +++ b/sys/i386/isa/sound/ulaw.h @@ -0,0 +1,69 @@ +static unsigned char ulaw_dsp[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2, + 5, 9, 13, 17, 21, 25, 29, 33, + 37, 41, 45, 49, 53, 57, 61, 65, + 68, 70, 72, 74, 76, 78, 80, 82, + 84, 86, 88, 90, 92, 94, 96, 98, + 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, + 115, 116, 116, 117, 117, 118, 118, 119, + 119, 120, 120, 121, 121, 122, 122, 123, + 123, 123, 124, 124, 124, 124, 125, 125, + 125, 125, 126, 126, 126, 126, 127, 127, + 127, 127, 127, 127, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 252, 248, 244, 240, 236, 232, 228, 224, + 220, 216, 212, 208, 204, 200, 196, 192, + 189, 187, 185, 183, 181, 179, 177, 175, + 173, 171, 169, 167, 165, 163, 161, 159, + 157, 156, 155, 154, 153, 152, 151, 150, + 149, 148, 147, 146, 145, 144, 143, 142, + 142, 141, 141, 140, 140, 139, 139, 138, + 138, 137, 137, 136, 136, 135, 135, 134, + 134, 134, 133, 133, 133, 133, 132, 132, + 132, 132, 131, 131, 131, 131, 130, 130, + 130, 130, 130, 130, 129, 129, 129, 129, + 129, 129, 129, 129, 128, 128, 128, 128, +}; + +static unsigned char dsp_ulaw[] = { + 31, 31, 31, 32, 32, 32, 32, 33, + 33, 33, 33, 34, 34, 34, 34, 35, + 35, 35, 35, 36, 36, 36, 36, 37, + 37, 37, 37, 38, 38, 38, 38, 39, + 39, 39, 39, 40, 40, 40, 40, 41, + 41, 41, 41, 42, 42, 42, 42, 43, + 43, 43, 43, 44, 44, 44, 44, 45, + 45, 45, 45, 46, 46, 46, 46, 47, + 47, 47, 47, 48, 48, 49, 49, 50, + 50, 51, 51, 52, 52, 53, 53, 54, + 54, 55, 55, 56, 56, 57, 57, 58, + 58, 59, 59, 60, 60, 61, 61, 62, + 62, 63, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 81, 83, 85, 87, 89, + 91, 93, 95, 99, 103, 107, 111, 119, + 255, 247, 239, 235, 231, 227, 223, 221, + 219, 217, 215, 213, 211, 209, 207, 206, + 205, 204, 203, 202, 201, 200, 199, 198, + 197, 196, 195, 194, 193, 192, 191, 191, + 190, 190, 189, 189, 188, 188, 187, 187, + 186, 186, 185, 185, 184, 184, 183, 183, + 182, 182, 181, 181, 180, 180, 179, 179, + 178, 178, 177, 177, 176, 176, 175, 175, + 175, 175, 174, 174, 174, 174, 173, 173, + 173, 173, 172, 172, 172, 172, 171, 171, + 171, 171, 170, 170, 170, 170, 169, 169, + 169, 169, 168, 168, 168, 168, 167, 167, + 167, 167, 166, 166, 166, 166, 165, 165, + 165, 165, 164, 164, 164, 164, 163, 163, + 163, 163, 162, 162, 162, 162, 161, 161, + 161, 161, 160, 160, 160, 160, 159, 159, +}; diff --git a/sys/i386/isa/sound/ultrasound.h b/sys/i386/isa/sound/ultrasound.h new file mode 100644 index 0000000000..236577a14b --- /dev/null +++ b/sys/i386/isa/sound/ultrasound.h @@ -0,0 +1,124 @@ +#ifndef _ULTRASOUND_H_ +#define _ULTRASOUND_H_ +/* + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * ultrasound.h - Macros for programming the Gravis Ultrasound + * These macros are extremely device dependent + * and not portable. + */ + +#ifdef linux +#include +#else +#include +#endif + +/* + * Private events for Gravis Ultrasound (GUS) + * + * Format: + * byte 0 - SEQ_PRIVATE (0xfe) + * byte 1 - Synthesizer device number (0-N) + * byte 2 - Command (see below) + * byte 3 - Voice number (0-31) + * bytes 4 and 5 - parameter P1 (unsigned short) + * bytes 6 and 7 - parameter P2 (unsigned short) + * + * Commands: + * Each command affects one voice defined in byte 3. + * Unused parameters (P1 and/or P2 *MUST* be initialized to zero). + * _GUS_NUMVOICES - Sets max. number of concurrent voices (P1=14-31, default 16) + * _GUS_VOICESAMPLE- ************ OBSOLETE ************* + * _GUS_VOICEON - Starts voice (P1=voice mode) + * _GUS_VOICEOFF - Stops voice (no parameters) + * _GUS_VOICEFADE - Stops the voice smoothly. + * _GUS_VOICEMODE - Alters the voice mode, don't start or stop voice (P1=voice mode) + * _GUS_VOICEBALA - Sets voice balence (P1, 0=left, 7=middle and 15=right, default 7) + * _GUS_VOICEFREQ - Sets voice (sample) playback frequency (P1=Hz) + * _GUS_VOICEVOL - Sets voice volume (P1=volume, 0xfff=max, 0xeff=half, 0x000=off) + * _GUS_VOICEVOL2 - Sets voice volume (P1=volume, 0xfff=max, 0xeff=half, 0x000=off) + * (Like GUS_VOICEVOL but doesn't change the hw + * volume. It just updates volume in the voice table). + * + * _GUS_RAMPRANGE - Sets limits for volume ramping (P1=low volume, P2=high volume) + * _GUS_RAMPRATE - Sets the speed for volume ramping (P1=scale, P2=rate) + * _GUS_RAMPMODE - Sets the volume ramping mode (P1=ramping mode) + * _GUS_RAMPON - Starts volume ramping (no parameters) + * _GUS_RAMPOFF - Stops volume ramping (no parameters) + * _GUS_VOLUME_SCALE - Changes the volume calculation constants + * for all voices. + */ + +#define _GUS_NUMVOICES 0x00 +#define _GUS_VOICESAMPLE 0x01 /* OBSOLETE */ +#define _GUS_VOICEON 0x02 +#define _GUS_VOICEOFF 0x03 +#define _GUS_VOICEMODE 0x04 +#define _GUS_VOICEBALA 0x05 +#define _GUS_VOICEFREQ 0x06 +#define _GUS_VOICEVOL 0x07 +#define _GUS_RAMPRANGE 0x08 +#define _GUS_RAMPRATE 0x09 +#define _GUS_RAMPMODE 0x0a +#define _GUS_RAMPON 0x0b +#define _GUS_RAMPOFF 0x0c +#define _GUS_VOICEFADE 0x0d +#define _GUS_VOLUME_SCALE 0x0e +#define _GUS_VOICEVOL2 0x0f + +/* + * GUS API macros + */ + +#define _GUS_CMD(chn, voice, cmd, p1, p2) \ + {_SEQ_NEEDBUF(8); _seqbuf[_seqbufptr] = SEQ_PRIVATE;\ + _seqbuf[_seqbufptr+1] = (chn); _seqbuf[_seqbufptr+2] = cmd;\ + _seqbuf[_seqbufptr+3] = voice;\ + *(unsigned short*)&_seqbuf[_seqbufptr+4] = p1;\ + *(unsigned short*)&_seqbuf[_seqbufptr+6] = p2;\ + _SEQ_ADVBUF(8);} + +#define GUS_NUMVOICES(chn, p1) _GUS_CMD(chn, 0, _GUS_NUMVOICES, (p1), 0) +#define GUS_VOICESAMPLE(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICESAMPLE, (p1), 0) /* OBSOLETE */ +#define GUS_VOICEON(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICEON, (p1), 0) +#define GUS_VOICEOFF(chn, voice) _GUS_CMD(chn, voice, _GUS_VOICEOFF, 0, 0) +#define GUS_VOICEFADE(chn, voice) _GUS_CMD(chn, voice, _GUS_VOICEFADE, 0, 0) +#define GUS_VOICEMODE(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICEMODE, (p1), 0) +#define GUS_VOICEBALA(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICEBALA, (p1), 0) +#define GUS_VOICEFREQ(chn, voice, p) _GUS_CMD(chn, voice, _GUS_VOICEFREQ, \ + (p) & 0xffff, ((p) >> 16) & 0xffff) +#define GUS_VOICEVOL(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICEVOL, (p1), 0) +#define GUS_VOICEVOL2(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICEVOL2, (p1), 0) +#define GUS_RAMPRANGE(chn, voice, low, high) _GUS_CMD(chn, voice, _GUS_RAMPRANGE, (low), (high)) +#define GUS_RAMPRATE(chn, voice, p1, p2) _GUS_CMD(chn, voice, _GUS_RAMPRATE, (p1), (p2)) +#define GUS_RAMPMODE(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_RAMPMODE, (p1), 0) +#define GUS_RAMPON(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_RAMPON, (p1), 0) +#define GUS_RAMPOFF(chn, voice) _GUS_CMD(chn, voice, _GUS_RAMPOFF, 0, 0) +#define GUS_VOLUME_SCALE(chn, voice, p1, p2) _GUS_CMD(chn, voice, _GUS_VOLUME_SCALE, (p1), (p2)) + +#endif diff --git a/sys/i386/isa/spkr.c b/sys/i386/isa/spkr.c index ffeec08fe5..7b82f16fab 100644 --- a/sys/i386/isa/spkr.c +++ b/sys/i386/isa/spkr.c @@ -5,6 +5,8 @@ * modified for 386bsd by Andrew A. Chernov * 386bsd only clean version, all SYSV stuff removed * use hz value from param.c + * + * $Id$ */ #include "speaker.h" @@ -12,6 +14,7 @@ #if NSPEAKER > 0 #include "param.h" +#include "systm.h" #include "kernel.h" #include "errno.h" #include "buf.h" diff --git a/sys/i386/isa/syscons.c b/sys/i386/isa/syscons.c new file mode 100644 index 0000000000..e639252dcc --- /dev/null +++ b/sys/i386/isa/syscons.c @@ -0,0 +1,2415 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz and Don Ahn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)pccons.c 5.11 (Berkeley) 5/21/91 + * from: @(#)syscons.c 1.0 930928 + * $Id: syscons.c,v 1.13 1993/10/16 13:46:23 rgrimes Exp $ + * + * Heavily modified by Søren Schmidt (sos@login.dkuug.dk) to provide: + * + * virtual consoles, SYSV ioctl's, ANSI emulation + */ + +#define STAR_SAVER +/* #define FAT_CURSOR /* This breaks on some CGA displays */ + +#include "param.h" +#include "conf.h" +#include "ioctl.h" +#include "proc.h" +#include "user.h" +#include "tty.h" +#include "uio.h" +#include "callout.h" +#include "systm.h" +#include "kernel.h" +#include "syslog.h" +#include "errno.h" +#include "machine/console.h" +#include "malloc.h" +#include "i386/isa/icu.h" +#include "i386/isa/isa.h" +#include "i386/isa/isa_device.h" +#include "machine/pc/display.h" +#include "i386/i386/cons.h" +#include "machine/psl.h" +#include "machine/frame.h" +#include "sc.h" +#include "ddb.h" +#include "iso8859.font" +#include "kbdtables.h" + +#if NSC > 0 +#ifndef NCONS +#define NCONS 12 +#endif + +/* status flags */ +#define LOCK_KEY_MASK 0x0000F +#define LED_MASK 0x00007 +#define UNKNOWN_MODE 0x00010 +#define KBD_RAW_MODE 0x00020 +#define SWITCH_WAIT_REL 0x00040 +#define SWITCH_WAIT_ACQ 0x00080 + +/* virtual video memory addresses */ +#define MONO_BUF (KERNBASE + 0xB0000) +#define CGA_BUF (KERNBASE + 0xB8000) +#define VGA_BUF (KERNBASE + 0xA0000) +#define VIDEOMEM 0x000A0000 +#define MEMSIZE 0x00020000 + +/* misc defines */ +#define MAX_ESC_PAR 3 +#define TEXT80x25 1 +#define TEXT80x50 2 +#define COL 80 +#define ROW 25 +#ifndef XTALSPEED +#define XTALSPEED 1193182 /* should be in isa.h */ +#endif + +/* defines related to hardware addresses */ +#define MONO_BASE 0x3B4 /* crt controller base mono */ +#define COLOR_BASE 0x3D4 /* crt controller base color */ +#define ATC IO_VGA+0x00 /* attribute controller */ +#define TSIDX IO_VGA+0x04 /* timing sequencer idx */ +#define TSREG IO_VGA+0x05 /* timing sequencer data */ +#define PIXMASK IO_VGA+0x06 /* pixel write mask */ +#define PALRADR IO_VGA+0x07 /* palette read address */ +#define PALWADR IO_VGA+0x08 /* palette write address */ +#define PALDATA IO_VGA+0x09 /* palette data register */ +#define GDCIDX IO_VGA+0x0E /* graph data controller idx */ +#define GDCREG IO_VGA+0x0F /* graph data controller data */ + +typedef struct term_stat { + int esc; /* processing escape sequence */ + int n_par; /* # of parameters to ESC */ + int last_par; /* last parameter # */ + int par[MAX_ESC_PAR]; /* contains ESC parameters */ + int attr; /* current attributes */ + int std_attr; /* normal attributes */ + int rev_attr; /* reverse attributes */ +} term_stat; + +typedef struct scr_stat { + u_short *crt_base; /* address of screen memory */ + u_short *scr; /* buffer when off screen */ + u_short *crtat; /* cursor address */ + int posx; /* current X position */ + int posy; /* current Y position */ + int max_posx; /* X size */ + int max_posy; /* X size */ + term_stat term; /* terminal emulation stuff */ + char cursor_start; /* cursor start line # */ + char cursor_end; /* cursor start end # */ + u_char border; /* border color */ + u_short bell_duration; + u_short bell_pitch; + u_short status; /* status (bitfield) */ + u_short mode; /* mode */ + pid_t pid; /* pid of controlling proc */ + struct proc *proc; /* proc* of controlling proc */ + struct vt_mode smode; /* switch mode */ +} scr_stat; + +typedef struct default_attr { + int std_attr; /* normal attributes */ + int rev_attr; /* reverse attributes */ +} default_attr; + +static default_attr user_default = { + (FG_LIGHTGREY | BG_BLACK) << 8, + (FG_BLACK | BG_LIGHTGREY) << 8 +}; + +static default_attr kernel_default = { + (FG_WHITE | BG_BLACK) << 8, + (FG_BLACK | BG_LIGHTGREY) << 8 +}; + +static default_attr *current_default; + +static scr_stat cons_scr_stat[NCONS]; +static scr_stat *cur_scr_stat = &cons_scr_stat[0]; +static scr_stat *new_scp, *old_scp; +static term_stat kernel_console; +static int switch_in_progress = 0; + +u_short *Crtat = (u_short *)MONO_BUF; +static u_short *crtat = 0; +static u_int crtc_addr = MONO_BASE; +static char crtc_vga = 0; +static u_char shfts = 0, ctls = 0, alts = 0, agrs = 0; +static u_char nlkcnt = 0, clkcnt = 0, slkcnt = 0, alkcnt = 0; +static char palette[3*256]; +static const u_int n_fkey_tab = sizeof(fkey_tab) / sizeof(*fkey_tab); +static int cur_cursor_pos = -1; +static char in_putc, nx_scr; +static char saved_console = -1; /* saved console number */ +static long scrn_blank_time = 0; /* screen saver timout value */ +static int scrn_blanked = 0; /* screen saver active flag */ +static long scrn_time_stamp; +static u_char scr_map[256]; + +struct tty pccons[NCONS]; +struct tty *cur_pccons = &pccons[0]; +struct tty *new_pccons; + +extern int hz; +extern struct timeval time; + +#define CSF_ACTIVE 0x1 /* timeout active */ +#define CSF_POLLING 0x2 /* polling for input */ + +struct pcconsoftc { + char cs_flags; + char cs_lastc; /* last char sent */ + int cs_timo; /* timeouts since interrupt */ + u_long cs_wedgecnt; /* times restarted */ +} pcconsoftc = {0, 0, 0, 0}; + + +/* special characters */ +#define bs 8 +#define lf 10 +#define cr 13 +#define cntlc 3 +#define del 0177 +#define cntld 4 + +/* function prototypes */ +int pcprobe(struct isa_device *dev); +int pcattach(struct isa_device *dev); +int pcopen(dev_t dev, int flag, int mode, struct proc *p); +int pcclose(dev_t dev, int flag, int mode, struct proc *p); +int pcread(dev_t dev, struct uio *uio, int flag); +int pcwrite(dev_t dev, struct uio *uio, int flag); +int pcparam(struct tty *tp, struct termios *t); +int pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p); +int pcxint(dev_t dev); +int pcstart(struct tty *tp); +int pccnprobe(struct consdev *cp); +int pccninit(struct consdev *cp); +int pccnputc(dev_t dev, char c); +int pccngetc(dev_t dev); +int scintr(dev_t dev, int irq, int cpl); +void scrn_saver(int test); +static struct tty *get_pccons(dev_t dev); +static scr_stat *get_scr_stat(dev_t dev); +static int get_scr_num(scr_stat *scp); +static void cursor_shape(int start, int end); +static void get_cursor_shape(int *start, int *end); +static void cursor_pos(void); +static void clear_screen(scr_stat *scp); +static switch_scr(u_int next_scr); +static void exchange_scr(void); +static void move_crsr(scr_stat *scp, int x, int y); +static void move_up(u_short *s, u_short *d, u_int len); +static void move_down(u_short *s, u_short *d, u_int len); +static void scan_esc(scr_stat *scp, u_char c); +static void ansi_put(scr_stat *scp, u_char c); +void consinit(void); +static void sput(u_char c); +static u_char *get_fstr(u_int c, u_int *len); +static update_leds(int which); +void reset_cpu(void); +u_int sgetc(int noblock); +int pcmmap(dev_t dev, int offset, int nprot); +int getchar(void); +static void kbd_wait(void); +static void kbd_cmd(u_char command); +static void set_mode(scr_stat *scp); +static void set_border(int color); +static load_font(int segment, int size, char* font); +static void save_palette(void); +static void load_palette(void); +static change_winsize(struct tty *tp, int x, int y); + +struct isa_driver scdriver = { + pcprobe, pcattach, "sc", +}; + + +int pcprobe(struct isa_device *dev) +{ + u_char c; + int again = 0; + + /* Enable interrupts and keyboard controller */ + kbd_wait(); + outb(KB_STAT, KB_WRITE); + kbd_cmd(0x4D); + + /* Start keyboard stuff RESET */ + kbd_cmd(KB_RESET); + while ((c=inb(KB_DATA)) != KB_ACK) { + if ((c == 0xFE) || (c == 0xFF)) { + if (!again) + printf("KEYBOARD disconnected: RECONNECT \n"); + kbd_cmd(KB_RESET); + again = 1; + } + } + kbd_wait(); + return (IO_KBDSIZE); +} + + +int pcattach(struct isa_device *dev) +{ + scr_stat *scp; + int start = -1, end = -1, i; + + if (crtc_vga) + if (crtc_addr == MONO_BASE) + printf("VGA mono"); + else + printf("VGA color"); + else + if (crtc_addr == MONO_BASE) + printf("MDA/hercules"); + else + printf("CGA/EGA"); + + if (NCONS > 1) + printf(" <%d virtual consoles>\n", NCONS); + else + printf("\n"); +#ifdef FAT_CURSOR + start = 0; + end = 18; +#endif + if (crtc_vga) { +#ifndef FAT_CURSOR + get_cursor_shape(&start, &end); +#endif + save_palette(); + load_font(0, 16, font_8x16); + load_font(1, 8, font_8x8); + load_font(2, 14, font_8x14); + } + current_default = &user_default; + for (i = 0; i < NCONS; i++) { + scp = &cons_scr_stat[i]; + scp->scr = (u_short *)malloc(COL * ROW * 2, M_DEVBUF, M_NOWAIT); + scp->mode = TEXT80x25; + scp->term.esc = 0; + scp->term.std_attr = current_default->std_attr; + scp->term.rev_attr = current_default->rev_attr; + scp->term.attr = scp->term.std_attr; + scp->border = BG_BLACK; + scp->cursor_start = start; + scp->cursor_end = end; + scp->max_posx = COL; + scp->max_posy = ROW; + scp->bell_pitch = 800; + scp->bell_duration = 10; + scp->status = 0; + scp->pid = 0; + scp->proc = NULL; + scp->smode.mode = VT_AUTO; + if (i > 0) { + scp->crt_base = scp->crtat = scp->scr; + fillw(scp->term.attr|scr_map[0x20], scp->scr, COL*ROW); + } + } + /* get cursor going */ +#ifdef FAT_CURSOR + cursor_shape(cons_scr_stat[0].cursor_start, + cons_scr_stat[0].cursor_end); +#endif + cursor_pos(); +} + + +static struct tty *get_pccons(dev_t dev) +{ + int i = minor(dev); + + if (i >= NCONS) + return(NULL); + return(&pccons[i]); +} + + +static scr_stat *get_scr_stat(dev_t dev) +{ + int i = minor(dev); + + if (i >= NCONS) + return(NULL); + return(&cons_scr_stat[i]); +} + + +static int get_scr_num(scr_stat *scp) /* allways call with legal scp !! */ +{ + int i = 0; + + while ((i < NCONS) && (cur_scr_stat != &cons_scr_stat[i])) i++; + return i; +} + +pcopen(dev_t dev, int flag, int mode, struct proc *p) +{ + struct tty *tp = get_pccons(dev); + + if (!tp) + return(ENXIO); + tp->t_oproc = pcstart; + tp->t_param = pcparam; + tp->t_dev = dev; + if ((tp->t_state & TS_ISOPEN) == 0) { + tp->t_state |= TS_WOPEN; + ttychars(tp); + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_cflag = TTYDEF_CFLAG; + tp->t_lflag = TTYDEF_LFLAG; + tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; + pcparam(tp, &tp->t_termios); + ttsetwater(tp); + } else if (tp->t_state&TS_XCLUDE && p->p_ucred->cr_uid != 0) + return(EBUSY); + tp->t_state |= TS_CARR_ON; + return((*linesw[tp->t_line].l_open)(dev, tp)); +} + + +pcclose(dev_t dev, int flag, int mode, struct proc *p) +{ + struct tty *tp = get_pccons(dev); + struct scr_stat *scp; + + if (!tp) + return(ENXIO); + scp = get_scr_stat(tp->t_dev); + scp->pid = 0; + scp->proc = NULL; + scp->smode.mode = VT_AUTO; + (*linesw[tp->t_line].l_close)(tp, flag); + ttyclose(tp); + return(0); +} + + +pcread(dev_t dev, struct uio *uio, int flag) +{ + struct tty *tp = get_pccons(dev); + + if (!tp) + return(ENXIO); + return((*linesw[tp->t_line].l_read)(tp, uio, flag)); +} + + +pcwrite(dev_t dev, struct uio *uio, int flag) +{ + struct tty *tp = get_pccons(dev); + + if (!tp) + return(ENXIO); + return((*linesw[tp->t_line].l_write)(tp, uio, flag)); +} + + +/* + * Got a console interrupt, keyboard action ! + * Catch the character, and see who it goes to. + */ +scintr(dev_t dev, int irq, int cpl) +{ + int c, len; + u_char *cp; + + /* make screensaver happy */ + scrn_time_stamp = time.tv_sec; + if (scrn_blanked) + scrn_saver(0); + c = sgetc(1); + if (c & 0x100) + return; + if ((cur_pccons->t_state & TS_ISOPEN) == 0) + return; + if (pcconsoftc.cs_flags & CSF_POLLING) + return; + if (c < 0x100) + (*linesw[cur_pccons->t_line].l_rint)(c & 0xFF, cur_pccons); + else if (cp = get_fstr((u_int)c, (u_int *)&len)) { + while (len-- > 0) + (*linesw[cur_pccons->t_line].l_rint) + (*cp++ & 0xFF, cur_pccons); + } +} + + +/* + * Set line parameters + */ +pcparam(struct tty *tp, struct termios *t) +{ + int cflag = t->c_cflag; + + /* and copy to tty */ + tp->t_ispeed = t->c_ispeed; + tp->t_ospeed = t->c_ospeed; + tp->t_cflag = cflag; + return(0); +} + + +pcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) +{ + int i, error; + struct tty *tp; + struct syscframe *fp; + scr_stat *scp; + + tp = get_pccons(dev); + if (!tp) + return ENXIO; + scp = get_scr_stat(tp->t_dev); + + switch (cmd) { /* process console hardware related ioctl's */ + + case CONS_BLANKTIME: /* set screen saver timeout (0 = no saver) */ + scrn_blank_time = *(int*)data; + return 0; + + case CONS_80x25TEXT: /* set 80x25 text mode */ + if (!crtc_vga) + return ENXIO; + scp->mode = TEXT80x25; + scp->max_posy = 25; + free(scp->scr, M_DEVBUF); + scp->scr = (u_short *)malloc(scp->max_posx*scp->max_posy*2, + M_DEVBUF, M_NOWAIT); + if (scp != cur_scr_stat) + scp->crt_base = scp->scr; + set_mode(scp); + clear_screen(scp); + change_winsize(tp, scp->max_posx, scp->max_posy); + return 0; + + case CONS_80x50TEXT: /* set 80x50 text mode */ + if (!crtc_vga) + return ENXIO; + scp->mode = TEXT80x50; + scp->max_posy = 50; + free(scp->scr, M_DEVBUF); + scp->scr = (u_short *)malloc(scp->max_posx*scp->max_posy*2, + M_DEVBUF, M_NOWAIT); + if (scp != cur_scr_stat) + scp->crt_base = scp->scr; + set_mode(scp); + clear_screen(scp); + change_winsize(tp, scp->max_posx, scp->max_posy); + return 0; + + case CONS_GETVERS: /* get version number */ + *(int*)data = 0x100; /* version 1.0 */ + return 0; + + case CONS_GETINFO: /* get current (virtual) console info */ + if (*data == sizeof(struct vid_info)) { + vid_info_t *ptr = (vid_info_t*)data; + ptr->m_num = get_scr_num(scp); + ptr->mv_col = scp->posx; + ptr->mv_row = scp->posy; + ptr->mv_csz = scp->max_posx; + ptr->mv_rsz = scp->max_posy; + ptr->mv_norm.fore = (scp->term.std_attr & 0x0f00)>>8; + ptr->mv_norm.back = (scp->term.std_attr & 0xf000)>>12; + ptr->mv_rev.fore = (scp->term.rev_attr & 0x0f00)>>8; + ptr->mv_rev.back = (scp->term.rev_attr & 0xf000)>>12; + ptr->mv_grfc.fore = 0; /* not supported */ + ptr->mv_grfc.back = 0; /* not supported */ + ptr->mv_ovscan = scp->border; + ptr->mk_keylock = scp->status & LOCK_KEY_MASK; + return 0; + } + return EINVAL; + + case VT_SETMODE: /* set screen switcher mode */ + bcopy(data, &scp->smode, sizeof(struct vt_mode)); + if (scp->smode.mode == VT_PROCESS) { + scp->proc = p; + scp->pid = scp->proc->p_pid; + } + return 0; + + case VT_GETMODE: /* get screen switcher mode */ + bcopy(&scp->smode, data, sizeof(struct vt_mode)); + return 0; + + case VT_RELDISP: /* screen switcher ioctl */ + switch(*data) { + case VT_FALSE: /* user refuses to release screen, abort */ + if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { + old_scp->status &= ~SWITCH_WAIT_REL; + switch_in_progress = 0; + return 0; + } + return EINVAL; + + case VT_TRUE: /* user has released screen, go on */ + if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { + scp->status &= ~SWITCH_WAIT_REL; + exchange_scr(); + if (new_scp->smode.mode == VT_PROCESS) { + new_scp->status |= SWITCH_WAIT_ACQ; + psignal(new_scp->proc, + new_scp->smode.acqsig); + } + else + switch_in_progress = 0; + return 0; + } + return EINVAL; + + case VT_ACKACQ: /* acquire acknowledged, switch completed */ + if (scp == new_scp && (scp->status & SWITCH_WAIT_ACQ)) { + scp->status &= ~SWITCH_WAIT_ACQ; + switch_in_progress = 0; + return 0; + } + return EINVAL; + + default: + return EINVAL; + } + /* NOT REACHED */ + + case VT_OPENQRY: /* return free virtual console */ + for (i = 0; i < NCONS; i++) + if (!(pccons[i].t_state & TS_ISOPEN)) { + *data = i + 1; + return 0; + } + return EINVAL; + /* NOT REACHED */ + + case VT_GETACTIVE: /* return number of active virtual console */ + *data = get_scr_num(scp) + 1; + return 0; + + case VT_ACTIVATE: /* switch to screen *data */ + return switch_scr((*data) - 1); + + case VT_WAITACTIVE: /* wait for switch to occur */ + if (*data > NCONS) + return EINVAL; + if (minor(dev) == (*data) - 1) + return 0; + if (*data == 0) { + if (scp == cur_scr_stat) + return 0; + while ((error=tsleep(&scp->smode, + PZERO|PCATCH, "waitvt", 0)) + == ERESTART) ; + } + else + while ((error=tsleep(&cons_scr_stat[*data].smode, + PZERO|PCATCH, "waitvt", 0)) + == ERESTART) ; + return error; + + case KDENABIO: /* allow io operations */ + fp = (struct syscframe *)p->p_regs; + fp->sf_eflags |= PSL_IOPL; + return 0; + + case KDDISABIO: /* disallow io operations (default) */ + fp = (struct syscframe *)p->p_regs; + fp->sf_eflags &= ~PSL_IOPL; + return 0; + + case KDSETMODE: /* set current mode of this (virtual) console */ + switch (*data) { + case KD_TEXT: /* switch to TEXT (known) mode */ + /* restore fonts & palette ! */ + if (crtc_vga) { + load_font(0, 16, font_8x16); + load_font(1, 8, font_8x8); + load_font(2, 14, font_8x14); + load_palette(); + } + /* FALL THROUGH */ + + case KD_TEXT1: /* switch to TEXT (known) mode */ + /* no restore fonts & palette */ + scp->status &= ~UNKNOWN_MODE; + set_mode(scp); + clear_screen(scp); + return 0; + + case KD_GRAPHICS:/* switch to GRAPHICS (unknown) mode */ + scp->status |= UNKNOWN_MODE; + return 0; + default: + return EINVAL; + } + /* NOT REACHED */ + + case KDGETMODE: /* get current mode of this (virtual) console */ + *data = (scp->status & UNKNOWN_MODE) ? KD_GRAPHICS : KD_TEXT; + return 0; + + case KDSBORDER: /* set border color of this (virtual) console */ + if (!crtc_vga) + return ENXIO; + scp->border = *data; + if (scp == cur_scr_stat) + set_border(scp->border); + return 0; + + case KDSKBSTATE: /* set keyboard state (locks) */ + if (*data >= 0 && *data <= LOCK_KEY_MASK) { + scp->status &= ~LOCK_KEY_MASK; + scp->status |= *data; + if (scp == cur_scr_stat) + update_leds(scp->status & LED_MASK); + return 0; + } + return EINVAL; + + case KDGKBSTATE: /* get keyboard state (locks) */ + *data = scp->status & LOCK_KEY_MASK; + return 0; + + case KDSETRAD: /* set keyboard repeat & delay rates */ + if (*(u_char*)data < 0x80) { + kbd_cmd(KB_SETRAD); + kbd_cmd(*data & 0x7f); + return 0; + } + return EINVAL; + + case KDSKBMODE: /* set keyboard mode */ + switch (*data) { + case K_RAW: /* switch to RAW scancode mode */ + scp->status |= KBD_RAW_MODE; + return 0; + + case K_XLATE: /* switch to XLT ascii mode */ + scp->status &= ~KBD_RAW_MODE; + return 0; + default: + return EINVAL; + } + /* NOT REACHED */ + + case KDGKBMODE: /* get keyboard mode */ + *data = (scp->status & KBD_RAW_MODE) ? K_RAW : K_XLATE; + return 0; + + case KDMKTONE: /* sound the bell */ + if (scp == cur_scr_stat) + sysbeep(scp->bell_pitch, scp->bell_duration); + return 0; + + case KIOCSOUND: /* make tone (*data) hz */ + if (scp == cur_scr_stat) { + if (*(int*)data) { + int pitch = XTALSPEED/(*(int*)data); + /* enable counter 2 */ + outb(0x61, inb(0x61) | 3); + /* set command for counter 2, 2 byte write */ + outb(0x43, 0xb6); + /* set pitch */ + outb(0x42, pitch); + outb(0x42, (pitch>>8)); + } + else { + /* disable counter 2 */ + outb(0x61, inb(0x61) & 0xFC); + } + } + return 0; + + case KDGKBTYPE: /* get keyboard type */ + *data = 0; /* type not known (yet) */ + return 0; + + case KDSETLED: /* set keyboard LED status */ + if (*data >= 0 && *data <= LED_MASK) { + scp->status &= ~LED_MASK; + scp->status |= *data; + if (scp == cur_scr_stat) + update_leds(scp->status & LED_MASK); + return 0; + } + return EINVAL; + + case KDGETLED: /* get keyboard LED status */ + *data = scp->status & LED_MASK; + return 0; + + case GETFKEY: /* get functionkey string */ + if (*(u_short*)data < n_fkey_tab) { + fkeyarg_t *ptr = (fkeyarg_t*)data; + bcopy(&fkey_tab[ptr->keynum].str, + ptr->keydef, + fkey_tab[ptr->keynum].len); + ptr->flen = fkey_tab[ptr->keynum].len; + return 0; + } + else + return EINVAL; + + case SETFKEY: /* set functionkey string */ + if (*(u_short*)data < n_fkey_tab) { + fkeyarg_t *ptr = (fkeyarg_t*)data; + bcopy(ptr->keydef, + &fkey_tab[ptr->keynum].str, + min(ptr->flen, MAXFK)); + fkey_tab[ptr->keynum].len = min(ptr->flen, MAXFK); + return 0; + } + else + return EINVAL; + + case GIO_SCRNMAP: /* get output translation table */ + bcopy(&scr_map, data, sizeof(scr_map)); + return 0; + + case PIO_SCRNMAP: /* set output translation table */ + bcopy(data, &scr_map, sizeof(scr_map)); + return 0; + + case GIO_KEYMAP: /* get keyboard translation table */ + bcopy(&key_map, data, sizeof(key_map)); + return 0; + + case PIO_KEYMAP: /* set keyboard translation table */ + bcopy(data, &key_map, sizeof(key_map)); + return 0; + + case PIO_FONT8x8: /* set 8x8 dot font */ + if (!crtc_vga) + return ENXIO; + bcopy(data, &font_8x8, sizeof(font_8x8)); + load_font(1, 8, font_8x8); + return 0; + + case GIO_FONT8x8: /* get 8x8 dot font */ + if (!crtc_vga) + return ENXIO; + bcopy(&font_8x8, data, sizeof(font_8x8)); + return 0; + + case PIO_FONT8x14: /* set 8x14 dot font */ + if (!crtc_vga) + return ENXIO; + bcopy(data, &font_8x14, sizeof(font_8x14)); + load_font(2, 14, font_8x14); + return 0; + + case GIO_FONT8x14: /* get 8x14 dot font */ + if (!crtc_vga) + return ENXIO; + bcopy(&font_8x14, data, sizeof(font_8x14)); + return 0; + + case PIO_FONT8x16: /* set 8x16 dot font */ + if (!crtc_vga) + return ENXIO; + bcopy(data, &font_8x16, sizeof(font_8x16)); + load_font(0, 16, font_8x16); + return 0; + + case GIO_FONT8x16: /* get 8x16 dot font */ + if (!crtc_vga) + return ENXIO; + bcopy(&font_8x16, data, sizeof(font_8x16)); + return 0; + + case CONSOLE_X_MODE_ON: /* just to be compatible */ + if (saved_console < 0) { + saved_console = get_scr_num(cur_scr_stat); + switch_scr(minor(dev)); + fp = (struct syscframe *)p->p_regs; + fp->sf_eflags |= PSL_IOPL; + scp->status |= UNKNOWN_MODE; + scp->status |= KBD_RAW_MODE; + return 0; + } + return EAGAIN; + + case CONSOLE_X_MODE_OFF:/* just to be compatible */ + fp = (struct syscframe *)p->p_regs; + fp->sf_eflags &= ~PSL_IOPL; + if (crtc_vga) { + load_font(0, 16, font_8x16); + load_font(1, 8, font_8x8); + load_font(2, 14, font_8x14); + load_palette(); + } + scp->status &= ~UNKNOWN_MODE; + set_mode(scp); + clear_screen(scp); + scp->status &= ~KBD_RAW_MODE; + switch_scr(saved_console); + saved_console = -1; + return 0; + + case CONSOLE_X_BELL: /* more compatibility */ + /* + * if set, data is a pointer to a length 2 array of + * integers. data[0] is the pitch in Hz and data[1] + * is the duration in msec. + */ + if (data) + sysbeep(XTALSPEED/((int*)data)[0], ((int*)data)[1]*hz/3000); + else + sysbeep(0x31b, hz/4); + return 0; + + default: + break; + } + + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag); + if (error >= 0) + return(error); + error = ttioctl(tp, cmd, data, flag); + if (error >= 0) + return(error); + return(ENOTTY); +} + + +pcxint(dev_t dev) +{ + pccons[minor(dev)].t_state &= ~TS_BUSY; + pcconsoftc.cs_timo = 0; + if (pccons[minor(dev)].t_line) + (*linesw[pccons[minor(dev)].t_line].l_start) + (&pccons[minor(dev)]); + else + pcstart(&pccons[minor(dev)]); +} + + +pcstart(struct tty *tp) +{ + int c, s; + scr_stat *scp = get_scr_stat(tp->t_dev); + + s = spltty(); + if (!(tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))) + for (;;) { + if (RB_LEN(&tp->t_out) <= tp->t_lowat) { + if (tp->t_state & TS_ASLEEP) { + tp->t_state &= ~TS_ASLEEP; + wakeup((caddr_t)&tp->t_out); + } + if (tp->t_wsel) { + selwakeup(tp->t_wsel, + tp->t_state & TS_WCOLL); + tp->t_wsel = 0; + tp->t_state &= ~TS_WCOLL; + } + } + if (RB_LEN(&tp->t_out) == 0) + break; + if (scp->status & SLKED) + break; + c = getc(&tp->t_out); + tp->t_state |= TS_BUSY; + splx(s); + ansi_put(scp, c); + s = spltty(); + tp->t_state &= ~TS_BUSY; + } + splx(s); +} + + +pccnprobe(struct consdev *cp) +{ + int maj; + + /* locate the major number */ + for (maj = 0; maj < nchrdev; maj++) + if (cdevsw[maj].d_open == pcopen) + break; + + /* initialize required fields */ + cp->cn_dev = makedev(maj, 0); + cp->cn_tp = &pccons[0]; + cp->cn_pri = CN_INTERNAL; +} + + +pccninit(struct consdev *cp) +{ +} + + +pccnputc(dev_t dev, char c) +{ + int pos; + + if (cur_scr_stat->status & UNKNOWN_MODE) + return; + if (c == '\n') + sput('\r'); + sput(c); + pos = cur_scr_stat->crtat - cur_scr_stat->crt_base; + if (pos != cur_cursor_pos) { + cur_cursor_pos = pos; + outb(crtc_addr,14); + outb(crtc_addr+1,pos >> 8); + outb(crtc_addr,15); + outb(crtc_addr+1,pos&0xff); + } +} + + +pccngetc(dev_t dev) +{ + int c, s; + + s = spltty(); /* block scintr while we poll */ + c = sgetc(0); + splx(s); + if (c == '\r') c = '\n'; + return(c); +} + +#if !defined(STAR_SAVER) && !defined(SNAKE_SAVER) + +void scrn_saver(int test) +{ + u_char val; + + if (test) { + scrn_blanked = 1; + outb(TSIDX, 0x01); val = inb(TSREG); + outb(TSIDX, 0x01); outb(TSREG, val | 0x20); + } + else { + scrn_blanked = 0; + outb(TSIDX, 0x01); val = inb(TSREG); + outb(TSIDX, 0x01); outb(TSREG, val & 0xDF); + } +} +#endif +#if defined(STAR_SAVER) || defined(SNAKE_SAVER) + +static u_long rand_next = 1; + +static rand() +{ + return ((rand_next = rand_next * 1103515245 + 12345) & 0x7FFFFFFF); +} +#endif +#ifdef STAR_SAVER +/* + * Alternate saver that got its inspiration from a well known utility + * package for an unfamous OS. + */ + +#define NUM_STARS 50 + +void scrn_saver(int test) +{ + scr_stat *scp = cur_scr_stat; + int cell, i; + char pattern[] = {"...........++++*** "}; + char colors[] = {FG_DARKGREY, FG_LIGHTGREY, + FG_WHITE, FG_LIGHTCYAN}; + static u_short stars[NUM_STARS][2]; + + if (test) { + if (!scrn_blanked) { + bcopy(Crtat, scp->scr, + scp->max_posx * scp->max_posy * 2); + fillw((FG_LIGHTGREY|BG_BLACK)<<8 | scr_map[0x20], Crtat, + scp->max_posx * scp->max_posy); + set_border(0); + i = scp->max_posy * scp->max_posx + 5; + outb(crtc_addr, 14); + outb(crtc_addr+1, i >> 8); + outb(crtc_addr, 15); + outb(crtc_addr+1, i & 0xff); + scrn_blanked = 1; + for(i=0; imax_posx*scp->max_posy); + stars[i][1] = 0; + } + } + cell = rand() % NUM_STARS; + *((u_short*)(Crtat + stars[cell][0])) = + scr_map[pattern[stars[cell][1]]] | + colors[rand()%sizeof(colors)] << 8; + if ((stars[cell][1]+=(rand()%4)) >= sizeof(pattern)-1) { + stars[cell][0] = rand() % (scp->max_posx*scp->max_posy); + stars[cell][1] = 0; + } + } + else { + if (scrn_blanked) { + bcopy(scp->scr, Crtat, scp->max_posx*scp->max_posy*2); + cur_cursor_pos = -1; + set_border(scp->border); + scrn_blanked = 0; + } + } +} +#endif +#ifdef SNAKE_SAVER +/* + * alternative screen saver for cards that do not like blanking + */ + +void scrn_saver(int test) +{ + const char saves[] = {"FreeBSD"}; + static u_char *savs[sizeof(saves)-1]; + static int dirx, diry; + int f; + scr_stat *scp = cur_scr_stat; + + if (test) { + if (!scrn_blanked) { + bcopy(Crtat, scp->scr, + scp->max_posx * scp->max_posy * 2); + fillw((FG_LIGHTGREY|BG_BLACK)<<8 | scr_map[0x20], Crtat, + scp->max_posx * scp->max_posy); + set_border(0); + dirx = (scp->posx ? 1 : -1); + diry = (scp->posy ? + scp->max_posx : -scp->max_posx); + for (f=0; f< sizeof(saves)-1; f++) + savs[f] = (u_char *)Crtat + 2 * + (scp->posx+scp->posy*scp->max_posx); + *(savs[0]) = scr_map[*saves]; + f = scp->max_posy * scp->max_posx + 5; + outb(crtc_addr, 14); + outb(crtc_addr+1, f >> 8); + outb(crtc_addr, 15); + outb(crtc_addr+1, f & 0xff); + scrn_blanked = 1; + } + if (scrn_blanked++ < 4) + return; + scrn_blanked = 1; + *(savs[sizeof(saves)-2]) = scr_map[0x20]; + for (f=sizeof(saves)-2; f > 0; f--) + savs[f] = savs[f-1]; + f = (savs[0] - (u_char *)Crtat) / 2; + if ((f % scp->max_posx) == 0 || + (f % scp->max_posx) == scp->max_posx - 1 || + (rand() % 50) == 0) + dirx = -dirx; + if ((f / scp->max_posx) == 0 || + (f / scp->max_posx) == scp->max_posy - 1 || + (rand() % 20) == 0) + diry = -diry; + savs[0] += 2*dirx + 2*diry; + for (f=sizeof(saves)-2; f>=0; f--) + *(savs[f]) = scr_map[saves[f]]; + } + else { + if (scrn_blanked) { + bcopy(scp->scr, Crtat, + scp->max_posx * scp->max_posy * 2); + cur_cursor_pos = -1; + set_border(scp->border); + scrn_blanked = 0; + } + } +} +#endif + +static void cursor_shape(int start, int end) +{ + outb(crtc_addr, 10); + outb(crtc_addr+1, start & 0xFF); + outb(crtc_addr, 11); + outb(crtc_addr+1, end & 0xFF); +} + + +static void get_cursor_shape(int *start, int *end) +{ + outb(crtc_addr, 10); + *start = inb(crtc_addr+1) & 0x1F; + outb(crtc_addr, 11); + *end = inb(crtc_addr+1) & 0x1F; +} + + +static void cursor_pos(void) +{ + int pos; + + if (cur_scr_stat->status & UNKNOWN_MODE) + return; + if (scrn_blank_time && (time.tv_sec > scrn_time_stamp+scrn_blank_time)) + scrn_saver(1); + pos = cur_scr_stat->crtat - cur_scr_stat->crt_base; + if (!scrn_blanked && pos != cur_cursor_pos) { + cur_cursor_pos = pos; + outb(crtc_addr, 14); + outb(crtc_addr+1, pos>>8); + outb(crtc_addr, 15); + outb(crtc_addr+1, pos&0xff); + } + timeout(cursor_pos, 0, hz/20); +} + + +static void clear_screen(scr_stat *scp) +{ + move_crsr(scp, 0, 0); + fillw(scp->term.attr | scr_map[0x20], scp->crt_base, + scp->max_posx * scp->max_posy); +} + + +static switch_scr(u_int next_scr) +{ + if (in_putc) { /* don't switch if in putc */ + nx_scr = next_scr+1; + return 0; + } + if (switch_in_progress && + (cur_scr_stat->proc != pfind(cur_scr_stat->pid))) + switch_in_progress = 0; + if (next_scr >= NCONS || switch_in_progress) { + sysbeep(800, hz/4); + return -1; + } + switch_in_progress = 1; + old_scp = cur_scr_stat; + new_scp = &cons_scr_stat[next_scr]; + wakeup(&new_scp->smode); + if (new_scp == old_scp) { + switch_in_progress = 0; + return 0; + } + new_pccons = &pccons[next_scr]; + + /* has controlling process died? */ + if (old_scp->proc && (old_scp->proc != pfind(old_scp->pid))) + old_scp->smode.mode = VT_AUTO; + if (new_scp->proc && (new_scp->proc != pfind(new_scp->pid))) + new_scp->smode.mode = VT_AUTO; + + /* check the modes and switch approbiatly */ + if (old_scp->smode.mode == VT_PROCESS) { + old_scp->status |= SWITCH_WAIT_REL; + psignal(old_scp->proc, old_scp->smode.relsig); + } + else { + exchange_scr(); + if (new_scp->smode.mode == VT_PROCESS) { + new_scp->status |= SWITCH_WAIT_ACQ; + psignal(new_scp->proc, new_scp->smode.acqsig); + } + else + switch_in_progress = 0; + } + return 0; +} + + +static void exchange_scr(void) +{ + bcopy(Crtat, old_scp->scr, old_scp->max_posx * old_scp->max_posy * 2); + old_scp->crt_base = old_scp->scr; + move_crsr(old_scp, old_scp->posx, old_scp->posy); + cur_scr_stat = new_scp; + cur_pccons = new_pccons; + if (old_scp->status & KBD_RAW_MODE || new_scp->status & KBD_RAW_MODE) + shfts = ctls = alts = 0; + update_leds(new_scp->status & LED_MASK); + set_mode(new_scp); + new_scp->crt_base = Crtat; + move_crsr(new_scp, new_scp->posx, new_scp->posy); + bcopy(new_scp->scr, Crtat, new_scp->max_posx * new_scp->max_posy * 2); + nx_scr = 0; +} + + +static void move_crsr(scr_stat *scp, int x, int y) +{ + if (x < 0 || y < 0 || x >= scp->max_posx || y >= scp->max_posy) + return; + scp->posx = x; + scp->posy = y; + scp->crtat = scp->crt_base + scp->posy * scp->max_posx + scp->posx; +} + + +static void move_up(u_short *s, u_short *d, u_int len) +{ + s += len; + d += len; + while (len-- > 0) + *--d = *--s; +} + + +static void move_down(u_short *s, u_short *d, u_int len) +{ + while (len-- > 0) + *d++ = *s++; +} + + +static void scan_esc(scr_stat *scp, u_char c) +{ + static u_char ansi_col[16] = + {0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15}; + int i, n; + u_short *src, *dst, count; + + if (scp->term.esc == 1) { + switch (c) { + + case '[': /* Start ESC [ sequence */ + scp->term.esc = 2; + scp->term.last_par = -1; + for (i = scp->term.n_par; i < MAX_ESC_PAR; i++) + scp->term.par[i] = 1; + scp->term.n_par = 0; + return; + + case 'M': /* Move cursor up 1 line, scroll if at top */ + if (scp->posy > 0) + move_crsr(scp, scp->posx, scp->posy - 1); + else { + move_up(scp->crt_base, + scp->crt_base + scp->max_posx, + (scp->max_posy - 1) * scp->max_posx); + fillw(scp->term.attr | scr_map[0x20], + scp->crt_base, scp->max_posx); + } + break; +#if notyet + case 'Q': + scp->term.esc = 4; + break; +#endif + case 'c': /* Clear screen & home */ + clear_screen(scp); + break; + } + } + else if (scp->term.esc == 2) { + if (c >= '0' && c <= '9') { + if (scp->term.n_par < MAX_ESC_PAR) { + if (scp->term.last_par != scp->term.n_par) { + scp->term.last_par = scp->term.n_par; + scp->term.par[scp->term.n_par] = 0; + } + else + scp->term.par[scp->term.n_par] *= 10; + scp->term.par[scp->term.n_par] += c - '0'; + return; + } + } + scp->term.n_par = scp->term.last_par + 1; + switch (c) { + + case ';': + if (scp->term.n_par < MAX_ESC_PAR) + return; + break; + + case '=': + scp->term.esc = 3; + scp->term.last_par = -1; + for (i = scp->term.n_par; i < MAX_ESC_PAR; i++) + scp->term.par[i] = 1; + scp->term.n_par = 0; + return; + + case 'A': /* up n rows */ + n = scp->term.par[0]; if (n < 1) n = 1; + move_crsr(scp, scp->posx, scp->posy - n); + break; + + case 'B': /* down n rows */ + n = scp->term.par[0]; if (n < 1) n = 1; + move_crsr(scp, scp->posx, scp->posy + n); + break; + + case 'C': /* right n columns */ + n = scp->term.par[0]; if (n < 1) n = 1; + move_crsr(scp, scp->posx + n, scp->posy); + break; + + case 'D': /* left n columns */ + n = scp->term.par[0]; if (n < 1) n = 1; + move_crsr(scp, scp->posx - n, scp->posy); + break; + + case 'E': /* cursor to start of line n lines down */ + n = scp->term.par[0]; if (n < 1) n = 1; + move_crsr(scp, 0, scp->posy + n); + break; + + case 'F': /* cursor to start of line n lines up */ + n = scp->term.par[0]; if (n < 1) n = 1; + move_crsr(scp, 0, scp->posy - n); + break; + + case 'f': /* System V consoles .. */ + case 'H': /* Cursor move */ + if (scp->term.n_par == 0) + move_crsr(scp, 0, 0); + else if (scp->term.n_par == 2) + move_crsr(scp, scp->term.par[1] - 1, + scp->term.par[0] - 1); + break; + + case 'J': /* Clear all or part of display */ + if (scp->term.n_par == 0) + n = 0; + else + n = scp->term.par[0]; + switch (n) { + case 0: /* clear form cursor to end of display */ + fillw(scp->term.attr | scr_map[0x20], + scp->crtat, scp->crt_base + + scp->max_posx * scp->max_posy - + scp->crtat); + break; + case 1: /* clear from beginning of display to cursor */ + fillw(scp->term.attr | scr_map[0x20], + scp->crt_base, + scp->crtat - scp->crt_base); + break; + case 2: /* clear entire display */ + clear_screen(scp); + break; + } + break; + + case 'K': /* Clear all or part of line */ + if (scp->term.n_par == 0) + n = 0; + else + n = scp->term.par[0]; + switch (n) { + case 0: /* clear form cursor to end of line */ + fillw(scp->term.attr | scr_map[0x20], + scp->crtat, scp->max_posx - scp->posx); + break; + case 1: /* clear from beginning of line to cursor */ + fillw(scp->term.attr|scr_map[0x20], + scp->crtat - (scp->max_posx - scp->posx), + (scp->max_posx - scp->posx) + 1); + break; + case 2: /* clear entire line */ + fillw(scp->term.attr|scr_map[0x20], + scp->crtat - (scp->max_posx - scp->posx), + scp->max_posx); + break; + } + break; + + case 'L': /* Insert n lines */ + n = scp->term.par[0]; if (n < 1) n = 1; + if (n > scp->max_posy - scp->posy) + n = scp->max_posy - scp->posy; + src = scp->crt_base + scp->posy * scp->max_posx; + dst = src + n * scp->max_posx; + count = scp->max_posy - (scp->posy + n); + move_up(src, dst, count * scp->max_posx); + fillw(scp->term.attr | scr_map[0x20], src, + n * scp->max_posx); + break; + + case 'M': /* Delete n lines */ + n = scp->term.par[0]; if (n < 1) n = 1; + if (n > scp->max_posy - scp->posy) + n = scp->max_posy - scp->posy; + dst = scp->crt_base + scp->posy * scp->max_posx; + src = dst + n * scp->max_posx; + count = scp->max_posy - (scp->posy + n); + move_down(src, dst, count * scp->max_posx); + src = dst + count * scp->max_posx; + fillw(scp->term.attr | scr_map[0x20], src, + n * scp->max_posx); + break; + + case 'P': /* Delete n chars */ + n = scp->term.par[0]; if (n < 1) n = 1; + if (n > scp->max_posx - scp->posx) + n = scp->max_posx - scp->posx; + dst = scp->crtat; + src = dst + n; + count = scp->max_posx - (scp->posx + n); + move_down(src, dst, count); + src = dst + count; + fillw(scp->term.attr | scr_map[0x20], src, n); + break; + + case '@': /* Insert n chars */ + n = scp->term.par[0]; if (n < 1) n = 1; + if (n > scp->max_posx - scp->posx) + n = scp->max_posx - scp->posx; + src = scp->crtat; + dst = src + n; + count = scp->max_posx - (scp->posx + n); + move_up(src, dst, count); + fillw(scp->term.attr | scr_map[0x20], src, n); + break; + + case 'S': /* scroll up n lines */ + n = scp->term.par[0]; if (n < 1) n = 1; + bcopy(scp->crt_base + (scp->max_posx * n), + scp->crt_base, + scp->max_posx * (scp->max_posy - n) * + sizeof(u_short)); + fillw(scp->term.attr | scr_map[0x20], + scp->crt_base + scp->max_posx * + (scp->max_posy - 1), + scp->max_posx); + break; + + case 'T': /* scroll down n lines */ + n = scp->term.par[0]; if (n < 1) n = 1; + bcopy(scp->crt_base, + scp->crt_base + (scp->max_posx * n), + scp->max_posx * (scp->max_posy - n) * + sizeof(u_short)); + fillw(scp->term.attr | scr_map[0x20], scp->crt_base, + scp->max_posx); + break; + + case 'X': /* delete n characters in line */ + n = scp->term.par[0]; if (n < 1) n = 1; + fillw(scp->term.attr | scr_map[0x20], + scp->crt_base + scp->posx + + ((scp->max_posx*scp->posy) * sizeof(u_short)), n); + break; + + case 'Z': /* move n tabs backwards */ + n = scp->term.par[0]; if (n < 1) n = 1; + if ((i = scp->posx & 0xf8) == scp->posx) + i -= 8*n; + else + i -= 8*(n-1); + if (i < 0) + i = 0; + move_crsr(scp, i, scp->posy); + break; + + case '`': /* move cursor to column n */ + n = scp->term.par[0]; if (n < 1) n = 1; + move_crsr(scp, n, scp->posy); + break; + + case 'a': /* move cursor n columns to the right */ + n = scp->term.par[0]; if (n < 1) n = 1; + move_crsr(scp, scp->posx + n, scp->posy); + break; + + case 'd': /* move cursor to row n */ + n = scp->term.par[0]; if (n < 1) n = 1; + move_crsr(scp, scp->posx, n); + break; + + case 'e': /* move cursor n rows down */ + n = scp->term.par[0]; if (n < 1) n = 1; + move_crsr(scp, scp->posx, scp->posy + n); + break; + + case 'm': /* change attribute */ + if (scp->term.n_par == 0) + n = 0; + else + n = scp->term.par[0]; + switch (n) { + case 0: /* back to normal */ + scp->term.attr = scp->term.std_attr; + break; + case 1: /* highlight (bold) */ + scp->term.attr &= 0xFF00; + scp->term.attr |= 0x0800; + break; + case 4: /* highlight (underline) */ + scp->term.attr &= 0x0F00; + scp->term.attr |= 0x0800; + break; + case 5: /* blink */ + scp->term.attr &= 0xFF00; + scp->term.attr |= 0x8000; + break; + case 7: /* reverse video */ + scp->term.attr = scp->term.rev_attr; + break; + case 30: case 31: case 32: case 33: /* set fg color */ + case 34: case 35: case 36: case 37: + scp->term.attr = (scp->term.attr & 0xF0FF) + | (ansi_col[(n - 30) & 7] << 8); + break; + case 40: case 41: case 42: case 43: /* set bg color */ + case 44: case 45: case 46: case 47: + scp->term.attr = (scp->term.attr & 0x0FFF) + | (ansi_col[(n - 40) & 7] << 12); + break; + } + break; + + case 'x': + if (scp->term.n_par == 0) + n = 0; + else + n = scp->term.par[0]; + switch (n) { + case 0: /* reset attributes */ + scp->term.attr = scp->term.std_attr = + current_default->std_attr; + scp->term.rev_attr = current_default->rev_attr; + break; + case 1: /* set ansi background */ + scp->term.attr = scp->term.std_attr = + (scp->term.std_attr & 0x0F00) | + (ansi_col[(scp->term.par[1])&0x0F]<<12); + break; + case 2: /* set ansi foreground */ + scp->term.attr = scp->term.std_attr = + (scp->term.std_attr & 0xF000) | + (ansi_col[(scp->term.par[1])&0x0F]<<8); + break; + case 3: /* set ansi attribute directly */ + scp->term.attr = scp->term.std_attr = + (scp->term.par[1]&0xFF)<<8; + break; + case 5: /* set ansi reverse video background */ + scp->term.rev_attr = + (scp->term.rev_attr & 0x0F00) | + (ansi_col[(scp->term.par[1])&0x0F]<<12); + break; + case 6: /* set ansi reverse video foreground */ + scp->term.rev_attr = + (scp->term.rev_attr & 0xF000) | + (ansi_col[(scp->term.par[1])&0x0F]<<8); + break; + case 7: /* set ansi reverse video directly */ + scp->term.rev_attr = (scp->term.par[1]&0xFF)<<8; + break; + } + break; + + case 'z': /* switch to (virtual) console n */ + if (scp->term.n_par == 1) + switch_scr(scp->term.par[0]); + break; + } + } + else if (scp->term.esc == 3) { + if (c >= '0' && c <= '9') { + if (scp->term.n_par < MAX_ESC_PAR) { + if (scp->term.last_par != scp->term.n_par) { + scp->term.last_par = scp->term.n_par; + scp->term.par[scp->term.n_par] = 0; + } + else + scp->term.par[scp->term.n_par] *= 10; + scp->term.par[scp->term.n_par] += c - '0'; + return; + } + } + scp->term.n_par = scp->term.last_par + 1; + switch (c) { + + case ';': + if (scp->term.n_par < MAX_ESC_PAR) + return; + break; + + case 'A': /* set display border color */ + if (scp->term.n_par == 1) + scp->border=scp->term.par[0] & 0xff; + if (scp == cur_scr_stat) + set_border(scp->border); + break; + + case 'B': /* set bell pitch and duration */ + if (scp->term.n_par == 2) { + scp->bell_pitch = scp->term.par[0]; + scp->bell_duration = scp->term.par[1]*10; + } + break; + + case 'C': /* set cursor shape (start & end line) */ + if (scp->term.n_par == 2) { + scp->cursor_start = scp->term.par[0] & 0x1F; + scp->cursor_end = scp->term.par[1] & 0x1F; + if (scp == cur_scr_stat) + cursor_shape(scp->cursor_start, + scp->cursor_end); + } + break; + + case 'F': /* set ansi foreground */ + if (scp->term.n_par == 1) + scp->term.attr = scp->term.std_attr = + (scp->term.std_attr & 0xF000) + | ((scp->term.par[0] & 0x0F) << 8); + break; + + case 'G': /* set ansi background */ + if (scp->term.n_par == 1) + scp->term.attr = scp->term.std_attr = + (scp->term.std_attr & 0x0F00) + | ((scp->term.par[0] & 0x0F) << 12); + break; + + case 'H': /* set ansi reverse video foreground */ + if (scp->term.n_par == 1) + scp->term.rev_attr = + (scp->term.rev_attr & 0xF000) + | ((scp->term.par[0] & 0x0F) << 8); + break; + + case 'I': /* set ansi reverse video background */ + if (scp->term.n_par == 1) + scp->term.rev_attr = + (scp->term.rev_attr & 0x0F00) + | ((scp->term.par[0] & 0x0F) << 12); + break; + } + } + scp->term.esc = 0; +} + + +static void ansi_put(scr_stat *scp, u_char c) +{ + if (scp->status & UNKNOWN_MODE) + return; + + /* make screensaver happy */ + if (scp == cur_scr_stat) { + scrn_time_stamp = time.tv_sec; + if (scrn_blanked) + scrn_saver(0); + } + in_putc++; + if (scp->term.esc) + scan_esc(scp, c); + else switch(c) { + case 0x1B: /* start escape sequence */ + scp->term.esc = 1; + scp->term.n_par = 0; + break; + case 0x07: + if (scp == cur_scr_stat) + sysbeep(scp->bell_pitch, scp->bell_duration); + break; + case '\t': /* non-destructive tab */ + scp->crtat += (8 - scp->posx % 8); + scp->posx += (8 - scp->posx % 8); + break; + case '\b': /* non-destructive backspace */ + if (scp->crtat > scp->crt_base) { + scp->crtat--; + if (scp->posx > 0) + scp->posx--; + else { + scp->posx += scp->max_posx - 1; + scp->posy--; + } + } + break; + case '\r': /* return to pos 0 */ + move_crsr(scp, 0, scp->posy); + break; + case '\n': /* newline, same pos */ + scp->crtat += scp->max_posx; + scp->posy++; + break; + case '\f': /* form feed, clears screen */ + clear_screen(scp); + break; + default: + /* Print only printables */ + *scp->crtat = (scp->term.attr | scr_map[c]); + scp->crtat++; + if (++scp->posx >= scp->max_posx) { + scp->posx = 0; + scp->posy++; + } + break; + } + if (scp->crtat >= scp->crt_base + scp->max_posy * scp->max_posx) { + bcopy(scp->crt_base + scp->max_posx, scp->crt_base, + scp->max_posx * (scp->max_posy - 1) * sizeof(u_short)); + fillw(scp->term.attr | scr_map[0x20], + scp->crt_base + scp->max_posx * (scp->max_posy - 1), + scp->max_posx); + scp->crtat -= scp->max_posx; + scp->posy--; + } + in_putc--; + if (nx_scr) + switch_scr(nx_scr - 1); +} + + +void consinit(void) +{ + u_short volatile *cp = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short), was; + unsigned cursorat; + int i; + + /* + * catch that once in a blue moon occurence when consinit is called + * TWICE, adding the CGA_BUF offset again -> poooff + */ + if (crtat != 0) + return; + /* + * Crtat initialized to point to MONO buffer, if not present change + * to CGA_BUF offset. ONLY ADD the difference since locore.s adds + * in the remapped offset at the right time + */ + was = *cp; + *cp = (u_short) 0xA55A; + if (*cp != 0xA55A) { + crtc_addr = MONO_BASE; + } else { + *cp = was; + crtc_addr = COLOR_BASE; + Crtat = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short); + } + + /* Extract cursor location */ + outb(crtc_addr,14); + cursorat = inb(crtc_addr+1)<<8 ; + outb(crtc_addr,15); + cursorat |= inb(crtc_addr+1); + crtat = Crtat + cursorat; + + /* is this a VGA or higher ? */ + outb(crtc_addr, 7); + if (inb(crtc_addr) == 7) + crtc_vga = 1; + + current_default = &user_default; + cons_scr_stat[0].crtat = crtat; + cons_scr_stat[0].crt_base = Crtat; + cons_scr_stat[0].term.esc = 0; + cons_scr_stat[0].term.std_attr = current_default->std_attr; + cons_scr_stat[0].term.rev_attr = current_default->rev_attr; + cons_scr_stat[0].term.attr = current_default->std_attr; + cons_scr_stat[0].posx = cursorat % COL; + cons_scr_stat[0].posy = cursorat / COL; + cons_scr_stat[0].border = BG_BLACK;; + cons_scr_stat[0].max_posx = COL; + cons_scr_stat[0].max_posy = ROW; + cons_scr_stat[0].status = 0; + cons_scr_stat[0].pid = 0; + cons_scr_stat[0].proc = NULL; + cons_scr_stat[0].smode.mode = VT_AUTO; + cons_scr_stat[0].bell_pitch = 800; + cons_scr_stat[0].bell_duration = 10; + kernel_console.esc = 0; + kernel_console.std_attr = kernel_default.std_attr; + kernel_console.rev_attr = kernel_default.rev_attr; + kernel_console.attr = kernel_default.std_attr; + /* initialize mapscrn array to */ + for (i=0; iterm; + scp->term = kernel_console; + current_default = &kernel_default; + ansi_put(scp, c); + kernel_console = scp->term; + current_default = &user_default; + scp->term = save; +} + + +static u_char *get_fstr(u_int c, u_int *len) +{ + u_int i; + + if (!(c & FKEY)) + return(NULL); + i = (c & 0xFF) - F_FN; + if (i > n_fkey_tab) + return(NULL); + *len = fkey_tab[i].len; + return(fkey_tab[i].str); +} + + +static update_leds(int which) +{ + u_char xlate_leds[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + + kbd_cmd(KB_SETLEDS); /* LED Command */ + kbd_cmd(xlate_leds[which & LED_MASK]); + kbd_wait(); +} + + +volatile void reset_cpu(void) +{ + while (1) { + kbd_cmd(KB_RESET_CPU); /* Reset Command */ + DELAY(4000000); + kbd_cmd(KB_RESET); /* Keyboard Reset Command */ + } +} + + +/* + * sgetc(noblock) : get a character from the keyboard. + * If noblock = 0 wait until a key is gotten. Otherwise return a 0x100. + */ +u_int sgetc(int noblock) +{ + u_char val, code, release; + u_int state, action; + struct key_t *key; + static u_char esc_flag = 0, compose = 0; + static u_int chr = 0; + +next_code: + kbd_wait(); + /* First see if there is something in the keyboard port */ + if (inb(KB_STAT) & KB_BUF_FULL) + val = inb(KB_DATA); + else if (noblock) + return(0x100); + else + goto next_code; + + if (cur_scr_stat->status & KBD_RAW_MODE) + return val; + + code = val & 0x7F; + release = val & 0x80; + + /* Check for cntl-alt-del */ + if ((code == 83) && ctls && alts) + cpu_reset(); +#if NDDB > 0 + /* Check for cntl-alt-esc */ + if ((val == 1) && ctls && alts) { + /* if debugger called, try to switch to console 0 */ + if (cur_scr_stat->smode.mode == VT_AUTO && + cons_scr_stat[0].smode.mode == VT_AUTO) + switch_scr(0); + Debugger(); + return(0x100); + } +#endif + switch (esc_flag) { + case 0x00: /* normal scancode */ + switch(code) { + case 0x38: /* left alt (compose key) */ + if (release && compose) { + compose = 0; + if (chr > 255) { + sysbeep(500, hz/4); + chr = 0; + } + } + else { + if (!compose) { + compose = 1; + chr = 0; + } + } + break; + case 0x60: + case 0x61: + esc_flag = code; + goto next_code; + } + break; + case 0x60: /* 0xE0 prefix */ + esc_flag = 0; + switch (code) { + case 0x1c: /* right enter key */ + code = 0x59; + break; + case 0x1d: /* right ctrl key */ + code = 0x5a; + break; + case 0x35: /* keypad divide key */ + code = 0x5b; + break; + case 0x37: /* print scrn key */ + code = 0x5c; + break; + case 0x38: /* right alt key (alt gr) */ + code = 0x5d; + break; + case 0x47: /* grey home key */ + code = 0x5e; + break; + case 0x48: /* grey up arrow key */ + code = 0x5f; + break; + case 0x49: /* grey page up key */ + code = 0x60; + break; + case 0x4b: /* grey left arrow key */ + code = 0x61; + break; + case 0x4d: /* grey right arrow key */ + code = 0x62; + break; + case 0x4f: /* grey end key */ + code = 0x63; + break; + case 0x50: /* grey down arrow key */ + code = 0x64; + break; + case 0x51: /* grey page down key */ + code = 0x65; + break; + case 0x52: /* grey insert key */ + code = 0x66; + break; + case 0x53: /* grey delete key */ + code = 0x67; + break; + default: /* ignore everything else */ + goto next_code; + } + break; + case 0x61: /* 0xE1 prefix */ + esc_flag = 0; + if (code == 0x1D) + esc_flag = 0x1D; + goto next_code; + /* NOT REACHED */ + case 0x1D: /* pause / break */ + esc_flag = 0; + if (code != 0x45) + goto next_code; + code = 0x68; + break; + } + + if (compose) { + switch (code) { + case 0x47: + case 0x48: /* keypad 7,8,9 */ + case 0x49: + if (!release) + chr = (code - 0x40) + chr*10; + goto next_code; + case 0x4b: + case 0x4c: /* keypad 4,5,6 */ + case 0x4d: + if (!release) + chr = (code - 0x47) + chr*10; + goto next_code; + case 0x4f: + case 0x50: /* keypad 1,2,3 */ + case 0x51: + if (!release) + chr = (code - 0x4e) + chr*10; + goto next_code; + case 0x52: /* keypad 0 */ + if (!release) + chr *= 10; + goto next_code; + case 0x38: /* left alt key */ + break; + default: + if (chr) { + compose = chr = 0; + sysbeep(500, hz/4); + goto next_code; + } + break; + } + } + + state = (shfts ? 1 : 0 ) | (2 * (ctls ? 1 : 0)) | (4 * (alts ? 1 : 0)); + if ((!agrs && (cur_scr_stat->status & ALKED)) + || (agrs && !(cur_scr_stat->status & ALKED))) + code += ALTGR_OFFSET; + key = &key_map.key[code]; + if ( ((key->flgs & FLAG_LOCK_C) && (cur_scr_stat->status & CLKED)) + || ((key->flgs & FLAG_LOCK_N) && (cur_scr_stat->status & NLKED)) ) + state ^= 1; + + /* Check for make/break */ + action = key->map[state]; + if (release) { /* key released */ + if (key->spcl & 0x80) { + switch (action) { + case LSH: + shfts &= ~1; + break; + case RSH: + shfts &= ~2; + break; + case LCTR: + ctls &= ~1; + break; + case RCTR: + ctls &= ~2; + break; + case LALT: + alts &= ~1; + break; + case RALT: + alts &= ~2; + break; + case NLK: + nlkcnt = 0; + break; + case CLK: + clkcnt = 0; + break; + case SLK: + slkcnt = 0; + break; + case ASH: + agrs = 0; + break; + case ALK: + alkcnt = 0; + break; + } + } + if (chr && !compose) { + action = chr; + chr = 0; + return (action); + } + } else { + /* key pressed */ + if (key->spcl & (0x80>>state)) { + switch (action) { + /* LOCKING KEYS */ + case NLK: + if (!nlkcnt) { + nlkcnt++; + if (cur_scr_stat->status & NLKED) + cur_scr_stat->status &= ~NLKED; + else + cur_scr_stat->status |= NLKED; + update_leds(cur_scr_stat->status & LED_MASK); + } + break; + case CLK: + if (!clkcnt) { + clkcnt++; + if (cur_scr_stat->status & CLKED) + cur_scr_stat->status &= ~CLKED; + else + cur_scr_stat->status |= CLKED; + update_leds(cur_scr_stat->status & LED_MASK); + } + break; + case SLK: + if (!slkcnt) { + slkcnt++; + if (cur_scr_stat->status & SLKED) { + cur_scr_stat->status &= ~SLKED; + pcstart(&pccons[get_scr_num(cur_scr_stat)]); + } + else + cur_scr_stat->status |= SLKED; + update_leds(cur_scr_stat->status & LED_MASK); + } + break; + case ALK: + if (!alkcnt) { + alkcnt++; + if (cur_scr_stat->status & ALKED) + cur_scr_stat->status &= ~ALKED; + else + cur_scr_stat->status |= ALKED; + } + break; + + /* NON-LOCKING KEYS */ + case LSH: + shfts |= 1; + break; + case RSH: + shfts |= 2; + break; + case LCTR: + ctls |= 1; + break; + case RCTR: + ctls |= 2; + break; + case LALT: + alts |= 1; + break; + case RALT: + alts |= 2; + break; + case ASH: + agrs = 1; + break; + case NOP: + break; + default: + if (action >= F_SCR && action <= L_SCR) { + switch_scr(action - F_SCR); + break; + } + if (action >= F_FN && action <= L_FN) { + return(action | FKEY); + } + return(action); + } + } + else return(action); + } + goto next_code; +} + +/* July '93, jkh. Added in for init_main.c */ +void cons_highlight() +{ + cons_scr_stat[0].term.attr &= 0xFF00; + cons_scr_stat[0].term.attr |= 0x0800; +} + +void cons_normal() +{ + cons_scr_stat[0].term.attr = cons_scr_stat[0].term.std_attr; +} + +int getchar(void) +{ + char thechar; + int s; + + pcconsoftc.cs_flags |= CSF_POLLING; + s = splhigh(); + sput('>'); + thechar = (char) sgetc(0); + pcconsoftc.cs_flags &= ~CSF_POLLING; + splx(s); + switch (thechar) { + default: + if (thechar >= scr_map[0x20]) + sput(thechar); + return(thechar); + case cr: + case lf: + sput(cr); sput(lf); + return(lf); + case bs: + case del: + sput(bs); sput(scr_map[0x20]); sput(bs); + return(thechar); + case cntld: + sput('^'); sput('D'); sput('\r'); sput('\n'); + return(0); + } +} + + +int pcmmap(dev_t dev, int offset, int nprot) +{ + if (offset > 0x20000) + return EINVAL; + return i386_btop((VIDEOMEM + offset)); +} + + +static void kbd_wait(void) +{ + int i; + for (i=0; i<10000; i++) + if ((inb(KB_STAT) & KB_READY) == 0) + break; +} + + +static void kbd_cmd(u_char command) +{ + kbd_wait(); + outb(KB_DATA, command); +} + + +static void set_mode(scr_stat *scp) +{ + u_char byte; + int s; + + if (scp != cur_scr_stat) + return; + + /* (re)activate cursor */ + untimeout(cursor_pos, 0); + cursor_pos(); + + /* change cursor type if set */ + if (scp->cursor_start != -1 && scp->cursor_end != -1) + cursor_shape(scp->cursor_start, scp->cursor_end); + + /* mode change only on VGA's */ + if (!crtc_vga) + return; + + /* setup video hardware for the given mode */ + s = splhigh(); + switch(scp->mode) { + case TEXT80x25: + outb(crtc_addr, 9); byte = inb(crtc_addr+1); + outb(crtc_addr, 9); outb(crtc_addr+1, byte | 0x0F); + outb(TSIDX, 0x03); outb(TSREG, 0x00); /* select font 0 */ + break; + case TEXT80x50: + outb(crtc_addr, 9); byte = inb(crtc_addr+1); + outb(crtc_addr, 9); outb(crtc_addr+1, (byte & 0xF0) | 0x07); + outb(TSIDX, 0x03); outb(TSREG, 0x05); /* select font 1 */ + break; + default: + return; + } + splx(s); + + /* set border color for this (virtual) console */ + set_border(scp->border); + return; +} + + +static void set_border(int color) +{ + inb(crtc_addr+6); /* reset flip-flop */ + outb(ATC, 0x11); outb(ATC, color); + inb(crtc_addr+6); /* reset flip-flop */ + outb(ATC, 0x20); /* enable Palette */ +} + +static load_font(int segment, int size, char* font) +{ + int ch, line, s; + u_char val; + + outb(TSIDX, 0x01); val = inb(TSREG); /* blank screen */ + outb(TSIDX, 0x01); outb(TSREG, val | 0x20); + + /* setup vga for loading fonts (graphics plane mode) */ + s = splhigh(); + inb(crtc_addr+6); /* reset flip/flop */ + outb(ATC, 0x30); outb(ATC, 0x01); + outb(TSIDX, 0x02); outb(TSREG, 0x04); + outb(TSIDX, 0x04); outb(TSREG, 0x06); + outb(GDCIDX, 0x04); outb(GDCREG, 0x02); + outb(GDCIDX, 0x05); outb(GDCREG, 0x00); + outb(GDCIDX, 0x06); outb(GDCREG, 0x05); /* addr = a0000, 64kb */ + splx(s); + for (ch=0; ch < 256; ch++) + for (line=0; line < size; line++) + *((char *)atdevbase+(segment*0x4000)+(ch*32)+line) = + font[(ch*size)+line]; + /* setup vga for text mode again */ + s = splhigh(); + inb(crtc_addr+6); /* reset flip/flop */ + outb(ATC, 0x30); outb(ATC, 0x0C); + outb(TSIDX, 0x02); outb(TSREG, 0x03); + outb(TSIDX, 0x04); outb(TSREG, 0x02); + outb(GDCIDX, 0x04); outb(GDCREG, 0x00); + outb(GDCIDX, 0x05); outb(GDCREG, 0x10); + if (crtc_addr == MONO_BASE) { + outb(GDCIDX, 0x06); outb(GDCREG, 0x0A); /* addr = b0000, 32kb */ + } + else { + outb(GDCIDX, 0x06); outb(GDCREG, 0x0E); /* addr = b8000, 32kb */ + } + splx(s); + outb(TSIDX, 0x01); val = inb(TSREG); /* unblank screen */ + outb(TSIDX, 0x01); outb(TSREG, val & 0xDF); +} + + +static void load_palette(void) +{ + int i; + + outb(PIXMASK, 0xFF); /* no pixelmask */ + outb(PALWADR, 0x00); + for (i=0x00; i<0x300; i++) + outb(PALDATA, palette[i]); + inb(crtc_addr+6); /* reset flip/flop */ + outb(ATC, 0x20); /* enable palette */ +} + +static void save_palette(void) +{ + int i; + + outb(PALRADR, 0x00); + for (i=0x00; i<0x300; i++) + palette[i] = inb(PALDATA); + inb(crtc_addr+6); /* reset flip/flop */ +} + + +static change_winsize(struct tty *tp, int x, int y) +{ + if (tp->t_winsize.ws_col != x || tp->t_winsize.ws_row != y) { + tp->t_winsize.ws_col = x; + tp->t_winsize.ws_row = y; + pgsignal(tp->t_pgrp, SIGWINCH, 1); + } +} + +#endif /* NSC */ diff --git a/sys/i386/isa/timerreg.h b/sys/i386/isa/timerreg.h index 72c70227e3..5742f66014 100644 --- a/sys/i386/isa/timerreg.h +++ b/sys/i386/isa/timerreg.h @@ -30,7 +30,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Header: timerreg.h,v 1.2 93/02/28 15:08:58 mccanne Exp $ + * from: Header: timerreg.h,v 1.2 93/02/28 15:08:58 mccanne Exp + * $Id$ + */ + +/* * * Register definitions for the Intel 8253 Programmable Interval Timer. * diff --git a/sys/i386/isa/ultra14f.c b/sys/i386/isa/ultra14f.c index 46626b7ff9..95662585bf 100644 --- a/sys/i386/isa/ultra14f.c +++ b/sys/i386/isa/ultra14f.c @@ -15,14 +15,10 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- - * - * 16 Feb 93 Julian Elischer ADDED for SCSI system * commenced: Sun Sep 27 18:14:01 PDT 1992 + * slight mod to make work with 34F as well: Wed Jun 2 18:05:48 WST 1993 + * + * $Id: ultra14f.c,v 1.9 1993/10/12 07:15:38 rgrimes Exp $ */ #include @@ -90,10 +86,10 @@ extern physaddr kvtophys(); #endif MACH #ifdef __386BSD__ -#define PHYSTOKV(x) (x | 0xFE000000) #define KVTOPHYS(x) vtophys(x) #endif __386BSD__ +extern int hz; extern int delaycount; /* from clock setup code */ #define NUM_CONCURRENT 16 /* number of concurrent ops per board */ #define UHA_NSEG 33 /* number of dma segments supported */ @@ -243,8 +239,6 @@ struct mscp /*-----------------end of hardware supported fields----------------*/ struct mscp *next; /* in free list */ struct scsi_xfer *xs; /* the scsi_xfer for this cmd */ - long int delta; /* difference from previous*/ - struct mscp *later,*sooner; int flags; #define MSCP_FREE 0 #define MSCP_ACTIVE 1 @@ -253,9 +247,6 @@ struct mscp struct scsi_sense_data mscp_sense; }; -struct mscp *uha_soonest = (struct mscp *)0; -struct mscp *uha_latest = (struct mscp *)0; -long int uha_furtherest = 0; /* longest time in the timeout queue */ /* */ struct uha_data @@ -292,7 +283,9 @@ struct isa_driver uhadriver = { uhaprobe, uha_attach, "uha"}; #endif __386BSD__ static uha_unit = 0; +#ifdef UHADEBUG int uha_debug = 0; +#endif /*UHADEBUG*/ #define UHA_SHOWMSCPS 0x01 #define UHA_SHOWINTS 0x02 #define UHA_SHOWCMDS 0x04 @@ -308,7 +301,8 @@ struct scsi_switch uha_switch = 0, 0, uha_adapter_info, - 0,0,0 + "uha", + 0,0 }; /* */ @@ -319,7 +313,7 @@ uha_send_mbox( int unit ,struct mscp *mscp) { int port = uha_data[unit].baseport; - int spincount = FUDGE(delaycount) * 1; /* 1ms should be enough */ + int spincount = FUDGE(delaycount) * 1000; /* 1s should be enough */ int s = splbio(); while( ((inb(port + UHA_LINT) & (UHA_LDIP)) @@ -327,7 +321,7 @@ uha_send_mbox( int unit && (spincount--)); if(spincount == -1) { - printf("uha%d: board not responding\n",unit); + printf("uha%d: uha_send_mbox, board not responding\n",unit); Debugger(); } @@ -353,7 +347,7 @@ uha_abort( int unit && (spincount--)); if(spincount == -1); { - printf("uha%d: board not responding\n",unit); + printf("uha%d: uha_abort, board not responding\n",unit); Debugger(); } @@ -363,7 +357,7 @@ uha_abort( int unit while((abortcount--) && (!(inb(port + UHA_SINT) & UHA_ABORT_FAIL))); if(abortcount == -1) { - printf("uha%d: board not responding\n",unit); + printf("uha%d: uha_abort, board not responding\n",unit); Debugger(); } if((inb(port + UHA_SINT) & 0x10) != 0) @@ -392,12 +386,12 @@ retry: while( (spincount--) && (!(inb(stport) & UHA_SINTP))); if(spincount == -1) { - printf("uha%d: board not responding\n",unit); + printf("uha%d: uha_poll, board not responding\n",unit); return(EIO); } if ((int)cheat != PHYSTOKV(inl(port + UHA_ICM0))) { - printf("discarding %x ",inl(port + UHA_ICM0)); + printf("uha%d: discarding %x\n",unit,inl(port + UHA_ICM0)); outb(port + UHA_SINT, UHA_ICM_ACK); spinwait(50); goto retry; @@ -420,7 +414,7 @@ struct isa_dev *dev; uha_data[unit].baseport = dev->dev_addr; if(unit >= NUHA) { - printf("uha: unit number (%d) too high\n",unit); + printf("uha%d: unit number too high\n",unit); return(0); } @@ -449,10 +443,6 @@ struct isa_dev *dev; int unit = dev->dev_unit; -#ifdef __386BSD__ - printf(" probing for scsi devices**\n"); -#endif __386BSD__ - /***********************************************\ * ask the adapter what subunits are present * \***********************************************/ @@ -461,15 +451,6 @@ struct isa_dev *dev; #if defined(OSF) uha_attached[unit]=1; #endif /* defined(OSF) */ - if(!unit) /* only one for all boards */ - { - uha_timeout(0); - } - - -#ifdef __386BSD__ - printf("uha%d",unit); -#endif __386BSD__ return; } @@ -495,8 +476,10 @@ uhaintr(unit) int port = uha_data[unit].baseport; +#ifdef UHADEBUG if(scsi_debug & PRINTROUTINES) printf("uhaintr "); +#endif /*UHADEBUG*/ #if defined(OSF) if (!uha_attached[unit]) @@ -514,21 +497,25 @@ uhaintr(unit) mboxval = inl(port + UHA_ICM0); outb(port + UHA_SINT,UHA_ICM_ACK); +#ifdef UHADEBUG if(scsi_debug & TRACEINTERRUPTS) printf("status = 0x%x ",uhastat); +#endif /*UHADEBUG*/ /***********************************************\ * Process the completed operation * \***********************************************/ mscp = (struct mscp *)(PHYSTOKV(mboxval)); +#ifdef UHADEBUG if(uha_debug & UHA_SHOWCMDS ) { uha_show_scsi_cmd(mscp->xs); } if((uha_debug & UHA_SHOWMSCPS) && mscp) printf("",mscp); - uha_remove_timeout(mscp); +#endif /*UHADEBUG*/ + untimeout(uha_timeout,mscp); uha_done(unit,mscp); } @@ -548,8 +535,10 @@ struct mscp *mscp; struct scsi_sense_data *s1,*s2; struct scsi_xfer *xs = mscp->xs; +#ifdef UHADEBUG if(scsi_debug & (PRINTROUTINES | TRACEINTERRUPTS)) printf("uha_done "); +#endif /*UHADEBUG*/ /***********************************************\ * Otherwise, put the results of the operation * * into the xfer and call whoever started it * @@ -570,33 +559,41 @@ struct mscp *mscp; switch(mscp->ha_status) { case UHA_SBUS_TIMEOUT: /* No response */ +#ifdef UHADEBUG if (uha_debug & UHA_SHOWMISC) { printf("timeout reported back\n"); } +#endif /*UHADEBUG*/ xs->error = XS_TIMEOUT; break; case UHA_SBUS_OVER_UNDER: +#ifdef UHADEBUG if (uha_debug & UHA_SHOWMISC) { printf("scsi bus xfer over/underrun\n"); } +#endif /*UHADEBUG*/ xs->error = XS_DRIVER_STUFFUP; break; case UHA_BAD_SG_LIST: +#ifdef UHADEBUG if (uha_debug & UHA_SHOWMISC) { printf("bad sg list reported back\n"); } +#endif /*UHADEBUG*/ xs->error = XS_DRIVER_STUFFUP; break; default: /* Other scsi protocol messes */ xs->error = XS_DRIVER_STUFFUP; +#ifdef UHADEBUG if (uha_debug & UHA_SHOWMISC) { printf("unexpected ha_status: %x\n", mscp->ha_status); } +#endif /*UHADEBUG*/ } } @@ -611,11 +608,13 @@ struct mscp *mscp; \**************************************************************************/ { +#ifdef UHADEBUG if (uha_debug & UHA_SHOWMISC) { printf("unexpected targ_status: %x\n", mscp->targ_status); } +#endif /*UHADEBUG*/ xs->error = XS_DRIVER_STUFFUP; } } @@ -635,8 +634,10 @@ struct mscp *mscp; { unsigned int opri; +#ifdef UHADEBUG if(scsi_debug & PRINTROUTINES) printf("mscp%d(0x%x)> ",unit,flags); +#endif /*UHADEBUG*/ if (!(flags & SCSI_NOMASK)) opri = splbio(); @@ -663,8 +664,10 @@ uha_get_mscp(unit,flags) unsigned opri; struct mscp *rc; +#ifdef UHADEBUG if(scsi_debug & PRINTROUTINES) printf("bp) flags |= (SCSI_NOSLEEP); /* just to be sure */ if(flags & ITSDONE) { - printf("Already done?"); + printf("uha%d: Already done?",unit); xs->flags &= ~ITSDONE; } if(!(flags & INUSE)) { - printf("Not in use?"); + printf("uha%d: Not in use?",unit); xs->flags |= INUSE; } if (!(mscp = uha_get_mscp(unit,flags))) @@ -863,12 +871,14 @@ struct scsi_xfer *xs; } cheat = mscp; +#ifdef UHADEBUG if(uha_debug & UHA_SHOWMSCPS) printf("",mscp); if(scsi_debug & SHOWCOMMANDS) { uha_show_scsi_cmd(xs); } +#endif /*UHADEBUG*/ mscp->xs = xs; /***********************************************\ * Put all the arguments for the xfer in the mscp * @@ -944,10 +954,12 @@ cheat = mscp; sg->addr.addr[2] = ((scratch >> 16) & 0xff); sg->addr.addr[3] = ((scratch >> 24) & 0xff); xs->datalen += *(unsigned long *)sg->len.len = iovp->iov_len; +#ifdef UHADEBUG if(scsi_debug & SHOWSCATGATH) printf("(0x%x@0x%x)" ,iovp->iov_len ,iovp->iov_base); +#endif /*UHADEBUG*/ sg++; iovp++; seg++; @@ -960,8 +972,10 @@ cheat = mscp; * Set up the scatter gather block * \***********************************************/ +#ifdef UHADEBUG if(scsi_debug & SHOWSCATGATH) printf("%d @0x%x:- ",xs->datalen,xs->data); +#endif /*UHADEBUG*/ datalen = xs->datalen; thiskv = (int)xs->data; thisphys = KVTOPHYS(thiskv); @@ -977,8 +991,10 @@ cheat = mscp; sg->addr.addr[2] = ((thisphys >> 16) & 0xff); sg->addr.addr[3] = ((thisphys >> 24) & 0xff); +#ifdef UHADEBUG if(scsi_debug & SHOWSCATGATH) printf("0x%x",thisphys); +#endif /*UHADEBUG*/ /* do it at least once */ nextphys = thisphys; @@ -1007,8 +1023,10 @@ cheat = mscp; /********************************************\ * next page isn't contiguous, finish the seg * \********************************************/ +#ifdef UHADEBUG if(scsi_debug & SHOWSCATGATH) printf("(0x%x)",bytes_this_seg); +#endif /*UHADEBUG*/ sg->len.len[0] = (bytes_this_seg & 0xff); sg->len.len[1] = ((bytes_this_seg >> 8) & 0xff); sg->len.len[2] = ((bytes_this_seg >> 16) & 0xff); @@ -1024,11 +1042,13 @@ cheat = mscp; mscp->datalen.len[3] = ((templen >> 24) & 0xff); mscp->sg_num = seg; +#ifdef UHADEBUG if(scsi_debug & SHOWSCATGATH) printf("\n"); +#endif /*UHADEBUG*/ if (datalen) { /* there's still data, must have run out of segs! */ - printf("uha_scsi_cmd%d: more than %d DMA segs\n", + printf("uha%d: uha_scsi_cmd, more than %d DMA segs\n", unit,UHA_NSEG); xs->error = XS_DRIVER_STUFFUP; uha_free_mscp(unit,mscp,flags); @@ -1063,35 +1083,37 @@ cheat = mscp; { s = splbio(); uha_send_mbox(unit,mscp); - uha_add_timeout(mscp,xs->timeout); + timeout(uha_timeout,mscp,(xs->timeout * hz) / 1000); splx(s); +#ifdef UHADEBUG if(scsi_debug & TRACEINTERRUPTS) printf("cmd_sent "); +#endif /*UHADEBUG*/ return(SUCCESSFULLY_QUEUED); } /***********************************************\ * If we can't use interrupts, poll on completion* \***********************************************/ uha_send_mbox(unit,mscp); +#ifdef UHADEBUG if(scsi_debug & TRACEINTERRUPTS) printf("cmd_wait "); +#endif /*UHADEBUG*/ do { if(uha_poll(unit,xs->timeout)) { - if (!(xs->flags & SCSI_SILENT)) printf("cmd fail\n"); + if (!(xs->flags & SCSI_SILENT)) + printf("uha%d: cmd fail\n",unit); if(!(uha_abort(unit,mscp))) { - printf("abort failed in wait\n"); + printf("uha%d: abort failed in wait\n",unit); uha_free_mscp(unit,mscp,flags); } xs->error = XS_DRIVER_STUFFUP; - splx(s); return(HAD_ERROR); } } while (!(xs->flags & ITSDONE));/* something (?) else finished */ - splx(s); -scsi_debug = 0;uha_debug = 0; if(xs->error) { return(HAD_ERROR); @@ -1099,154 +1121,34 @@ scsi_debug = 0;uha_debug = 0; return(COMPLETE); } -/* - * +----------+ +----------+ +----------+ - * uha_soonest--->| later |--->| later|--->| later|--->0 - * | [Delta] | | [Delta] | | [Delta] | - * 0<---|sooner |<---|sooner |<---|sooner |<---uha_latest - * +----------+ +----------+ +----------+ - * - * uha_furtherest = sum(Delta[1..n]) - */ -uha_add_timeout(mscp,time) -struct mscp *mscp; -int time; -{ - int timeprev; - struct mscp *prev; - int s = splbio(); - if(prev = uha_latest) /* yes, an assign */ - { - timeprev = uha_furtherest; - } - else - { - timeprev = 0; - } - while(prev && (timeprev > time)) - { - timeprev -= prev->delta; - prev = prev->sooner; - } - if(prev) - { - mscp->delta = time - timeprev; - if( mscp->later = prev->later) /* yes an assign */ - { - mscp->later->sooner = mscp; - mscp->later->delta -= mscp->delta; - } - else - { - uha_furtherest = time; - uha_latest = mscp; - } - mscp->sooner = prev; - prev->later = mscp; - } - else - { - if( mscp->later = uha_soonest) /* yes, an assign*/ - { - mscp->later->sooner = mscp; - mscp->later->delta -= time; - } - else - { - uha_furtherest = time; - uha_latest = mscp; - } - mscp->delta = time; - mscp->sooner = (struct mscp *)0; - uha_soonest = mscp; - } - splx(s); -} - -uha_remove_timeout(mscp) -struct mscp *mscp; -{ - int s = splbio(); - - if(mscp->sooner) - { - mscp->sooner->later = mscp->later; - } - else - { - uha_soonest = mscp->later; - } - if(mscp->later) - { - mscp->later->sooner = mscp->sooner; - mscp->later->delta += mscp->delta; - } - else - { - uha_latest = mscp->sooner; - uha_furtherest -= mscp->delta; - } - mscp->sooner = mscp->later = (struct mscp *)0; - splx(s); -} - - -extern int hz; -#define ONETICK 500 /* milliseconds */ -#define SLEEPTIME ((hz * 1000) / ONETICK) -uha_timeout(arg) -int arg; +uha_timeout(struct mscp *mscp) { - struct mscp *mscp; int unit; int s = splbio(); - unsigned int stat; int port = uha_data[unit].baseport; - while( mscp = uha_soonest ) + unit = mscp->xs->adapter; + printf("uha%d:%d device timed out\n",unit + ,mscp->xs->targ); +#ifdef UHADEBUG + if(uha_debug & UHA_SHOWMSCPS) + uha_print_active_mscp(unit); +#endif /*UHADEBUG*/ + + if((uha_abort(unit,mscp) !=1) || (mscp->flags = MSCP_ABORTED)) { - if(mscp->delta <= ONETICK) - /***********************************************\ - * It has timed out, we need to do some work * - \***********************************************/ - { - unit = mscp->xs->adapter; - printf("uha%d:%d device timed out\n",unit - ,mscp->xs->targ); - if(uha_debug & UHA_SHOWMSCPS) - uha_print_active_mscp(); - - /***************************************\ - * Unlink it from the queue * - \***************************************/ - uha_remove_timeout(mscp); - - if((uha_abort(unit,mscp) !=1) || (mscp->flags = MSCP_ABORTED)) - { - printf("AGAIN"); - mscp->xs->retries = 0; /* I MEAN IT ! */ - uha_done(unit,mscp,FAIL); - } - else /* abort the operation that has timed out */ - { - printf("\n"); - uha_add_timeout(mscp,2000 + ONETICK); - mscp->flags = MSCP_ABORTED; - } - } - else - /***********************************************\ - * It has not timed out, adjust and leave * - \***********************************************/ - { - mscp->delta -= ONETICK; - uha_furtherest -= ONETICK; - break; - } + printf("AGAIN"); + mscp->xs->retries = 0; /* I MEAN IT ! */ + uha_done(unit,mscp,FAIL); + } + else /* abort the operation that has timed out */ + { + printf("\n"); + timeout(uha_timeout,mscp,2 * hz); + mscp->flags = MSCP_ABORTED; } splx(s); - timeout(uha_timeout,arg,SLEEPTIME); } uha_show_scsi_cmd(struct scsi_xfer *xs) @@ -1283,26 +1185,25 @@ struct mscp *mscp; ,mscp->opcode ,mscp->cdblen ,mscp->senselen); - printf(" sg:%d sgnum:%x datlen:%d hstat:%x tstat:%x delta:%d flags:%x\n" + printf(" sg:%d sgnum:%x datlen:%d hstat:%x tstat:%x flags:%x\n" ,mscp->sgth ,mscp->sg_num ,mscp->datalen ,mscp->ha_status ,mscp->targ_status - ,mscp->delta ,mscp->flags); uha_show_scsi_cmd(mscp->xs); } -uha_print_active_mscp() +uha_print_active_mscp(int unit) { - struct mscp *mscp; - mscp = uha_soonest; + struct mscp *mscp = uha_data[unit].mscps; + int i = NUHA; - while(mscp) + while(i--) { - uha_print_mscp(mscp); - mscp = mscp->later; + if(mscp->flags != MSCP_FREE) + uha_print_mscp(mscp); + mscp++; } - printf("Furtherest = %d\n",uha_furtherest); } diff --git a/sys/i386/isa/vector.s b/sys/i386/isa/vector.s index 38ac79cdb3..aa40fd51e7 100644 --- a/sys/i386/isa/vector.s +++ b/sys/i386/isa/vector.s @@ -1,14 +1,6 @@ -/* vector.s */ /* - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00167 - * -------------------- ----- ---------------------- - * - * 04 Jun 93 Bruce Evans Fixed irq_num vs id_num for multiple - * devices configed on the same irq with - * respect to ipending. - * + * from: vector.s, 386BSD 0.1 unknown origin + * $Id$ */ #include "i386/isa/icu.h" @@ -284,12 +276,12 @@ _intrcnt_stray: .space 4 /* total count of stray interrupts */ _intrcnt_actv: .space NR_REAL_INT_HANDLERS * 4 /* active interrupts */ .globl _intrcnt_pend _intrcnt_pend: .space NR_REAL_INT_HANDLERS * 4 /* pending interrupts */ + .globl _eintrcnt +_eintrcnt: /* used by vmstat to calc size of table */ .globl _intrcnt_spl _intrcnt_spl: .space 32 * 4 /* XXX 32 should not be hard coded ? */ .globl _intrcnt_show _intrcnt_show: .space 8 * 4 /* XXX 16 should not be hard coded ? */ - .globl _eintrcnt -_eintrcnt: /* used by vmstat to calc size of table */ /* * Build the interrupt name table for vmstat @@ -325,6 +317,7 @@ _intrnames: .asciz "name pend" BUILD_VECTORS +_eintrnames: /* * now the spl names @@ -338,7 +331,8 @@ _intrnames: .asciz "netisr_ip" .asciz "netisr_imp" .asciz "netisr_ns" - .asciz "softclock" + .asciz "netisr_iso" + .asciz "softclock" /* 10 */ .asciz "trap" .asciz "doreti_exit2" .asciz "splbio" @@ -348,17 +342,16 @@ _intrnames: .asciz "splnet" .asciz "splsoftclock" .asciz "spltty" - .asciz "spl0" + .asciz "spl0" /* 20 */ .asciz "netisr_raw2" .asciz "netisr_ip2" + .asciz "netisr_imp2" + .asciz "netisr_ns2" + .asciz "netisr_iso2" .asciz "splx" .asciz "splx!0" .asciz "unpend_V" - .asciz "spl25" /* spl25-spl31 are spares */ - .asciz "spl26" - .asciz "spl27" - .asciz "spl28" - .asciz "spl29" + .asciz "spl29" /* spl29-spl31 are spares */ .asciz "spl30" .asciz "spl31" /* @@ -373,4 +366,3 @@ _intrnames: .asciz "mask6" .asciz "mask7" -_eintrnames: diff --git a/sys/i386/isa/wd.c b/sys/i386/isa/wd.c index bbec45b569..02b53e4280 100644 --- a/sys/i386/isa/wd.c +++ b/sys/i386/isa/wd.c @@ -33,29 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * from:@(#)wd.c 7.2 (Berkeley) 5/9/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 6 00155 - * -------------------- ----- ---------------------- - * - * 17 Sep 92 Frank Maclachlan Fixed I/O error reporting on raw device - * 31 Jul 92 Christoph Robitschko Fixed second disk recognition, - * bzero of malloced memory for warm - * boot problem. - * 19 Aug 92 Frank Maclachlan Fixed bug when first sector of a - * multisector read is in bad144 table. - * 17 Jan 93 B. Evans & A.Chernov Fixed bugs from previous patches, - * driver initialization, and cylinder - * boundary conditions. - * 28 Mar 93 Charles Hannum Add missing splx calls. - * 20 Apr 93 Terry Lee Always report disk errors - * 20 Apr 93 Brett Lymn Change infinite while loops to - * timeouts - * 17 May 93 Rodney W. Grimes Fixed all 1000000 to use WDCTIMEOUT, - * and increased to 1000000*10 for new - * intr-0.1 code. + * from: @(#)wd.c 7.2 (Berkeley) 5/9/91 + * $Id$ */ /* TODO:peel out buffer at low ipl, speed improvement */ @@ -76,6 +55,7 @@ #include "uio.h" #include "malloc.h" #include "machine/cpu.h" +#include "i386/isa/isa.h" #include "i386/isa/isa_device.h" #include "i386/isa/icu.h" #include "i386/isa/wdreg.h" @@ -154,12 +134,12 @@ struct isa_driver wddriver = { wdprobe, wdattach, "wd", }; -void wdustart(struct disk *); -void wdstart(); -int wdcommand(struct disk *, int); -int wdcontrol(struct buf *); -int wdsetctlr(dev_t, struct disk *); -int wdgetctlr(int, struct disk *); +static void wdustart(struct disk *); +static void wdstart(); +static int wdcommand(struct disk *, int); +static int wdcontrol(struct buf *); +static int wdsetctlr(dev_t, struct disk *); +static int wdgetctlr(int, struct disk *); /* * Probe for controller. @@ -200,7 +180,7 @@ wdprobe(struct isa_device *dvp) (void) inb(wdc+wd_error); /* XXX! */ outb(wdc+wd_ctlr, WDCTL_4BIT); - return (1); + return (IO_WDCSIZE); nodevice: free(du, M_TEMP); @@ -231,7 +211,7 @@ wdattach(struct isa_device *dvp) if(wdgetctlr(unit, du) == 0) { int i, blank; char c; - printf(" %d:<", unit); + printf("wd%d: unit %d type ", unit, unit); for (i = blank = 0 ; i < sizeof(du->dk_params.wdp_model); i++) { char c = du->dk_params.wdp_model[i]; @@ -246,14 +226,9 @@ wdattach(struct isa_device *dvp) else printf("%c", c); } - printf(">"); + printf("\n"); du->dk_unit = unit; } - else { - /* old ST506 controller */ - printf(" %d:", - unit); - } } return(1); } @@ -423,7 +398,14 @@ loop: */ if ((du->dk_flags & (DKFL_SINGLE|DKFL_BADSECT)) /* 19 Aug 92*/ == (DKFL_SINGLE|DKFL_BADSECT)) - for (bt_ptr = du->dk_bad.bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) { + /* XXX + * BAD144END was done to clean up some old bad code that was + * attempting to compare a u_short to -1. This makes the compilers + * happy and clearly shows what is going on. + * rgrimes 93/06/17 + */ +#define BAD144END (u_short)(-1) + for (bt_ptr = du->dk_bad.bt_bad; bt_ptr->bt_cyl != BAD144END; bt_ptr++) { if (bt_ptr->bt_cyl > cylin) /* Sorted list, and we passed our cylinder. quit. */ break; @@ -626,22 +608,19 @@ wdintr(struct intrframe wdif) if (++wdtab.b_errcnt < RETRIES) { wdtab.b_active = 0; } else { - if((du->dk_flags&DKFL_QUIET) == 0) { - diskerr(bp, "wd", "hard error", - LOG_PRINTF, du->dk_skip, - &du->dk_dd); + diskerr(bp, "wd", "hard error", LOG_PRINTF, + du->dk_skip, &du->dk_dd); #ifdef WDDEBUG - printf( "status %b error %b\n", - status, WDCS_BITS, - inb(wdc+wd_error), WDERR_BITS); + printf( "status %b error %b\n", + status, WDCS_BITS, + inb(wdc+wd_error), WDERR_BITS); #endif - } bp->b_error = EIO; /* 17 Sep 92*/ bp->b_flags |= B_ERROR; /* flag the error */ } - } else if((du->dk_flags&DKFL_QUIET) == 0) { - diskerr(bp, "wd", "soft ecc", 0, - du->dk_skip, &du->dk_dd); + } else { + diskerr(bp, "wd", "soft ecc", 0, + du->dk_skip, &du->dk_dd); } } outt: @@ -671,7 +650,7 @@ outt: if (wdtab.b_active) { if ((bp->b_flags & B_ERROR) == 0) { du->dk_skip++; /* Add to successful sectors. */ - if (wdtab.b_errcnt && (du->dk_flags & DKFL_QUIET) == 0) + if (wdtab.b_errcnt) diskerr(bp, "wd", "soft error", 0, du->dk_skip, &du->dk_dd); wdtab.b_errcnt = 0; @@ -752,17 +731,15 @@ wdopen(dev_t dev, int flags, int fmt, struct proc *p) du->dk_dd.d_secpercyl = 17*8; du->dk_state = WANTOPEN; du->dk_unit = unit; - du->dk_flags &= ~DKFL_QUIET; /* read label using "c" partition */ if (msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW), wdstrategy, &du->dk_dd, du->dk_dospartitions, &du->dk_bad, 0)) { - if((du->dk_flags&DKFL_QUIET) == 0) { - log(LOG_WARNING, "wd%d: cannot find label (%s)\n", - unit, msg); + log(LOG_WARNING, "wd%d: cannot find label (%s)\n", + unit, msg); + if (part != WDRAW) error = EINVAL; /* XXX needs translation */ - } goto done; } else { @@ -868,12 +845,9 @@ wdcontrol(register struct buf *bp) case RECAL: if ((stat = inb(wdc+wd_status)) & WDCS_ERR) { - if ((du->dk_flags & DKFL_QUIET) == 0) { - printf("wd%d: recal", du->dk_unit); - printf(": status %b error %b\n", - stat, WDCS_BITS, inb(wdc+wd_error), - WDERR_BITS); - } + printf("wd%d: recal", du->dk_unit); + printf(": status %b error %b\n", stat, WDCS_BITS, + inb(wdc+wd_error), WDERR_BITS); if (++wdtab.b_errcnt < RETRIES) { du->dk_state = WANTOPEN; goto tryagainrecal; @@ -899,9 +873,8 @@ wdcontrol(register struct buf *bp) /* NOTREACHED */ badopen: - if ((du->dk_flags & DKFL_QUIET) == 0) - printf(": status %b error %b\n", - stat, WDCS_BITS, inb(wdc + wd_error), WDERR_BITS); + printf(": status %b error %b\n", stat, WDCS_BITS, + inb(wdc + wd_error), WDERR_BITS); bp->b_flags |= B_ERROR; return(1); } @@ -989,10 +962,26 @@ wdgetctlr(int u, struct disk *du) { splx(x); return(stat); } + /* + * If WDCC_READP fails then we might have an old ST506 type drive + * so we try a seek to 0; if that passes then the + * drive is there but it's OLD AND KRUSTY + */ if (stat & WDCS_ERR) { - stat = inb(wdc+wd_error); + stat = wdcommand(du, WDCC_RESTORE | WD_STEP); + if (stat & WDCS_ERR) { + stat = inb(wdc+wd_error); + splx(x); + return(stat); + } + + strncpy(du->dk_dd.d_typename, "ST506", + sizeof du->dk_dd.d_typename); + strncpy(du->dk_params.wdp_model, "Unknown Type", + sizeof du->dk_params.wdp_model); + du->dk_dd.d_type = DTYPE_ST506; splx(x); - return(stat); + return(0); } /* obtain parameters */ @@ -1177,7 +1166,7 @@ wdformat(struct buf *bp) int wdsize(dev_t dev) { - int unit = wdunit(dev), part = wdpart(dev), val; + int unit = wdunit(dev), part = wdpart(dev), val = 0; struct disk *du; if (unit >= _NWD) /* 31 Jul 92*/ diff --git a/sys/i386/isa/wdreg.h b/sys/i386/isa/wdreg.h index d938915703..5feabf5b50 100644 --- a/sys/i386/isa/wdreg.h +++ b/sys/i386/isa/wdreg.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)wdreg.h 7.1 (Berkeley) 5/9/91 + * from: @(#)wdreg.h 7.1 (Berkeley) 5/9/91 + * $Id$ */ /* diff --git a/sys/i386/isa/wt.c b/sys/i386/isa/wt.c index 4376395e1c..7ae351f4d3 100644 --- a/sys/i386/isa/wt.c +++ b/sys/i386/isa/wt.c @@ -1,40 +1,28 @@ -/*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. +/* + * Streamer tape driver for 386bsd and FreeBSD. + * Supports Archive QIC-02 and Wangtek QIC-02/QIC-36 boards. + * + * Copyright (C) 1993 by: + * Sergey Ryzhkov + * Serge Vakulenko + * + * Placed in the public domain with NO WARRANTIES, not even the implied + * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. + * Authors grant any other persons or organisations permission to use + * or modify this software as long as this message is kept with the software, + * all derivative works or modified versions. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * This driver is derived from the old 386bsd Wangtek streamer tape driver, + * made by Robert Baron at CMU, based on Intel sources. + * Authors thank Robert Baron, CMU and Intel and retain here + * the original CMU copyright notice. * - * @(#)wt.c 7.1 (Berkeley) 5/9/91 + * from: Version 1.1, Fri Sep 24 02:14:31 MSD 1993 + * $Id$ */ /* - * * Copyright (c) 1989 Carnegie-Mellon University. * All rights reserved. * @@ -63,1100 +51,771 @@ #include "wt.h" #if NWT > 0 -/* - * HISTORY - * $Log: wt.c,v $ - * Revision 2.2.1.3 90/01/08 13:29:38 rvb - * Add Intel copyright. - * [90/01/08 rvb] - * - * Revision 2.2.1.2 89/12/21 18:00:09 rvb - * Change WTPRI to make the streamer tape read/write - * interruptible. [lin] - * - * Revision 2.2.1.1 89/11/10 09:49:49 rvb - * ORC likes their streamer port at 0x288. - * [89/11/08 rvb] - * - * Revision 2.2 89/09/25 12:33:02 rvb - * Driver was provided by Intel 9/18/89. - * [89/09/23 rvb] - * - */ - -/* - * - * Copyright 1988, 1989 by Intel Corporation - * - * Support Bell Tech QIC-02 and WANGTEK QIC-36 or QIC-02 - */ -/*#include -#include -#include */ #include "sys/param.h" #include "sys/buf.h" -#include "sys/file.h" -#include "sys/proc.h" -#include "sys/user.h" +#include "sys/fcntl.h" +#include "sys/malloc.h" +#include "sys/ioctl.h" +#include "sys/mtio.h" +#include "vm/vm_param.h" +#include "i386/include/pio.h" +#include "i386/isa/isa_device.h" #include "i386/isa/wtreg.h" -#ifdef ORC -unsigned wtport = 0x288; /* base I/O port of controller */ -#else ORC -unsigned wtport = 0x300; /* base I/O port of controller */ -#endif ORC - /* standard = 0x300 */ - /* alternate = 0x338 */ - -unsigned wtchan = 1; /* DMA channel number */ - /* stardard = 1 */ - /* hardware permits 1, 2 or 3. */ - /* (Avoid DMA 2: used by disks) */ - -int first_wtopen_ever = 1; - - -#define ERROR 1 /* return from tape routines */ -#define SUCCESS 0 /* return from tape routines */ - -int wci = 0; -int exflag = 0; -int bytes = 0; - -static unsigned char eqdma = 0x8; -static unsigned char pagereg = 0x83; -static unsigned char dmareg = 2; -static unsigned char dma_write = 0x49; -static unsigned char dma_read = 0x45; -static unsigned char dma_done = 2; -static unsigned char mode = 0; -static unsigned char mbits; /* map bits into each other */ -static long bufptr; -static unsigned numbytes; -/* -_wci dw 0 ; interrupt chain finished normally -_exflag dw 0 ; exception variable -_bytes dw 0 ; current bytes - -eqdma db 8h ; enable dma command: ch1,ch2=8h, ch3=10h -pagereg db 83h ; ch1=83h, ch2=81h, ch3=82h -dmareg db 2 ; ch1=2, ch2=4, ch3=6 -dma_write db 49h ; write dma command: 48h+_wtchan -dma_read db 45h ; read dma command: 44h+_wtchan -dma_done db 2 ; dma done flag: 1<<_wtchan -mode db 0 ; dma operation mode -lbufptr dw 0 ; buffer pointer to data buffers, low word -hbufptr dw 0 ; buffer pointer to data buffers, high word -numbytes dw 0 ; number of bytes to read or write (new) -*/ - -#define PAGESIZ 4096 -#define HZ 60 - -/* tape controller ports */ -#define STATPORT wtport -#define CTLPORT STATPORT -#define CMDPORT (wtport+1) -#define DATAPORT CMDPORT - -/* defines for reading out status from wangtek tape controller */ -#define READY 0x01 /* ready bit define */ -#define EXCEP 0x02 /* exception bit define */ -#define STAT (READY|EXCEP) -#define RESETMASK 0x7 -#define RESETVAL (RESETMASK & ~EXCEP) - -/* tape controller control bits (CTLPORT) */ -#define ONLINE 0x01 -#define RESET 0x02 -#define REQUEST 0x04 /* request command */ -#define CMDOFF 0xC0 - -/* QIC-02 commands (CMDPORT) */ -#define RDDATA 0x80 /* read data */ -#define READFM 0xA0 /* read file mark */ -#define WRTDATA 0x40 /* write data */ -#define WRITEFM 0x60 /* write file mark */ -#define RDSTAT 0xC0 /* read status command */ -#define REWIND 0x21 /* rewind command (position+bot) */ - -/* 8237 DMA controller regs */ -#define STATUSREG 0x8 -#define MASKREG 0xA -#define MODEREG 0xB -#define CLEARFF 0xC - -/* streamer tape block size */ -#define BLKSIZE 512 - -/* Tape characteristics */ -#define NBPS 512 /* 512-byte blocks */ -#define ERROR 1 /* return from tape routines */ -#define SUCCESS 0 /* return from tape routines */ - -/* Minor devs */ -#define TP_REWCLOSE(d) ((minor(d)&04) == 0) /* Rewind tape on close if read/write */ -#define TP_DENS(dev) ((minor(dev) >> 3) & 03) /* set density */ -#define TPHOG(d) 0 /* use Hogproc during tape I/O */ - -/* defines for wtflags */ -#define TPINUSE 0x0001 /* tape is already open */ -#define TPREAD 0x0002 /* tape is only open for reading */ -#define TPWRITE 0x0004 /* tape is only open for writing */ -#define TPSTART 0x0008 /* tape must be rewound and reset */ -#define TPDEAD 0x0010 /* tape drive does not work or driver error */ -#define TPSESS 0x0020 /* no more reads or writes allowed in session */ - /* for example, when tape has to be changed */ -#define TPSTOP 0x0040 /* Stop command outstanding */ -#define TPREW 0x0080 /* Rewind command outstanding, see wtdsl2() */ -#define TPVOL 0x0100 /* Read file mark, or hit end of tape */ -#define TPWO 0x0200 /* write command outstanding */ -#define TPRO 0x0400 /* read command outstanding */ -#define TPWANY 0x0800 /* write command requested */ -#define TPRANY 0x1000 /* read command requested */ -#define TPWP 0x2000 /* write protect error seen */ - -unsigned int wtflags = TPSTART; /* state of tape drive */ - -struct buf rwtbuf; /* header for raw i/o */ -struct proc *myproc; /* process which opened tape driver */ - -char wtimeron; /* wtimer() active flag */ -char wtio; /* dma (i/o) active flag */ -char isrlock; /* isr() flag */ - -struct proc * Hogproc; /* no Hogproc on Microport */ -#define ftoseg(x) ((unsigned) (x >> 16)) - -struct wtstatus { - ushort wt_err; /* code for error encountered */ - ushort wt_ercnt; /* number of error blocks */ - ushort wt_urcnt; /* number of underruns */ -} wterror; - -/* defines for wtstatus.wt_err */ -#define TP_POR 0x100 /* Power on/reset occurred */ -#define TP_RES1 0x200 /* Reserved for end of media */ -#define TP_RES2 0x400 /* Reserved for bus parity */ -#define TP_BOM 0x800 /* Beginning of media */ -#define TP_MBD 0x1000 /* Marginal block detected */ -#define TP_NDT 0x2000 /* No data detected */ -#define TP_ILL 0x4000 /* Illegal command */ -#define TP_ST1 0x8000 /* Status byte 1 bits */ -#define TP_FIL 0x01 /* File mark detected */ -#define TP_BNL 0x02 /* Bad block not located */ -#define TP_UDA 0x04 /* Unrecoverable data error */ -#define TP_EOM 0x08 /* End of media */ -#define TP_WRP 0x10 /* Write protected cartridge */ -#define TP_USL 0x20 /* Unselected drive */ -#define TP_CNI 0x40 /* Cartridge not in place */ -#define TP_ST0 0x80 /* Status byte 0 bits */ - -/* Grounds for reporting I/O error to user */ -#define TP_ERR0 (TP_BNL|TP_UDA|TP_WRP|TP_CNI|TP_FIL|TP_EOM|TP_USL) -#define TP_ERR1 (TP_MBD|TP_NDT|TP_ILL) -/* TP_ILL should never happen! */ -/* -#define TP_ERR0 0x7f -#define TP_ERR1 0x7700 -*/ - -/* defines for reading out status from wangtek tape controller */ -#define READY 0x01 /* ready bit define */ -#define EXCEP 0x02 /* exception bit define */ - -/* sleep priority */ -#define WTPRI (PZERO+10) - -char pagebuf[NBPS]; /* buffer of size NBPS */ -unsigned long pageaddr; /* physical addr of pagebuf */ - /* pageaddr is used with DMA controller */ -time_t Hogtime; /* lbolt when Hog timer started */ -extern time_t lbolt; - -#define debug printf +#define WTPRI (PZERO+10) /* sleep priority */ +#define BLKSIZE 512 /* streamer tape block size */ /* - * Strategy routine. - * - * Arguments: - * Pointer to buffer structure - * Function: - * Start transfer. - * - * It would be nice to have this multiple-threaded. - * There is a version of dump from Berkeley that works with multiple processes - * trading off with disk & tape I/O. + * Wangtek controller ports */ +#define WT_CTLPORT(base) ((base)+0) /* control, write only */ +#define WT_STATPORT(base) ((base)+0) /* status, read only */ +#define WT_CMDPORT(base) ((base)+1) /* command, write only */ +#define WT_DATAPORT(base) ((base)+1) /* data, read only */ +#define WT_NPORT 2 /* 2 i/o ports */ + +/* status port bits */ +#define WT_BUSY 0x01 /* not ready bit define */ +#define WT_NOEXCEP 0x02 /* no exception bit define */ +#define WT_RESETMASK 0x07 /* to check after reset */ +#define WT_RESETVAL 0x05 /* state after reset */ + +/* control port bits */ +#define WT_ONLINE 0x01 /* device selected */ +#define WT_RESET 0x02 /* reset command */ +#define WT_REQUEST 0x04 /* request command */ +#define WT_IEN(chan) ((chan)>2 ? 0x10 : 0x8) /* enable intr */ -int -wtstrategy(bp) -register struct buf *bp; -{ - unsigned ucnt1, ucnt2, finished; - unsigned long adr1, adr2; - int bad; - - adr1 = kvtop(bp->b_un.b_addr); -#ifdef DEBUG - debug("bpaddr %x\n", adr1); -#endif - ucnt1 = bp->b_bcount % NBPG; - ucnt2 = 0; - adr2 = 0; -#ifdef DEBUG - debug("WTstart: adr1 %lx cnt %x\n", adr1, ucnt1); -#endif - /* 64K boundary? (XXX) */ - if (ftoseg(adr1) != ftoseg(adr1 + (unsigned) ucnt1 - 1)) - { - adr2 = (adr1 & 0xffff0000L) + 0x10000L; - ucnt2 = (adr1 + ucnt1) - adr2; - ucnt1 -= ucnt2; - } - /* page boundary? */ - if (trunc_page(adr1) != trunc_page(adr1 + (unsigned) ucnt1 - 1)) - { unsigned u; - u = NBPG - ((unsigned)bp->b_un.b_addr & (NBPG-1)); - adr2 = kvtop(bp->b_un.b_addr + u); - ucnt2 = ucnt1 - u; - ucnt1 = u; - } - /* at file marks and end of tape, we just return '0 bytes available' */ - if (wtflags & TPVOL) { - bp->b_resid = bp->b_bcount; - goto xit; - } - if ((Hogproc == (struct proc *) 0) && TPHOG(bp->b_dev)) - { -#ifdef DEBUG - printf("setting Hogproc\n"); -#endif - Hogtime = 0; - Hogproc = myproc; - } - if (bp->b_flags & B_READ) { - bad = 0; - - /* For now, we assume that all data will be copied out */ - /* If read command outstanding, just skip down */ - if (!(wtflags & TPRO)) { - if (ERROR == wtsense(TP_WRP)) /* clear status */ - goto errxit; -#ifdef DEBUG - debug("WTread: Start read\n"); -#endif - if (!(wtflags & TPREAD) || (wtflags & TPWANY) || - (rstart() == ERROR)) { -#ifdef DEBUG - debug("Tpstart: read init error\n"); /* */ -#endif - goto errxit; - } - wtflags |= TPRO|TPRANY; - } - - finished = 0; - /* Take a deep breath */ - if (ucnt1) { - if ((rtape(adr1, ucnt1) == ERROR) && - (wtsense(TP_WRP) == ERROR)) - goto endio; - /* wait for it */ - bad = pollrdy(); - finished = bytes; - if (bad) - goto endio; - } - /* if a second I/O region, start it */ - if (ucnt2) { - if ((rtape(adr2, ucnt2) == ERROR) && - (wtsense(TP_WRP) == ERROR)) - ucnt2 = 0; /* don't poll for me */ - } - - /* if second i/o pending wait for it */ - if (ucnt2) { - pollrdy(); - /* whether pollrdy is ok or not */ - finished += bytes; - } - } else { - if (wtflags & TPWP) /* write protected */ - goto errxit; - - /* If write command outstanding, just skip down */ - if (!(wtflags & TPWO)) { - if (ERROR == wtsense(0)) /* clear status */ - { -#ifdef DEBUG - debug("TPstart: sense 0\n"); -#endif - goto errxit; - } - if (!(wtflags & TPWRITE) || (wtflags & TPRANY) || - (wstart() == ERROR)) { -#ifdef DEBUG - debug("Tpstart: write init error\n"); /* */ -#endif - wtsense(0); - -errxit: bp->b_flags |= B_ERROR; - bp->b_resid = bp->b_bcount; - goto xit; - } - wtflags |= TPWO|TPWANY; - } - - /* and hold your nose */ - if (ucnt1 && ((wtape(adr1, ucnt1) == ERROR) - && (wtsense(0) == ERROR))) - finished = bytes; - - else if (ucnt2 && - (((ucnt1 && pollrdy()) || - (wtape(adr2, ucnt2) == ERROR)) && - (wtsense(0) == ERROR))) - finished = ucnt1 + NBPS + bytes; - /* All writes and/or copyins were fine! */ - else - finished = bp->b_bcount; - bad = pollrdy(); - } - - endio: - if(bad == EIO) bad = 0; - wterror.wt_err = 0; - if (exflag && wtsense((bp->b_flags & B_READ) ? TP_WRP : 0)) { - if ((wterror.wt_err & TP_ST0) - && (wterror.wt_err & (TP_FIL|TP_EOM))) { -#ifdef DEBUG - debug("WTsta: Hit end of tape\n"); /* */ -#endif - wtflags |= TPVOL; - if (wterror.wt_err & TP_FIL) { - if (wtflags & TPRO) - /* interrupter is bogus */ - rstart(); /* restart read command */ - else - wtflags &= ~TPWO; - finished += NBPS; - } - /* Reading file marks or writing end of tape return 0 bytes */ - } else { - bp->b_flags |= B_ERROR; - wtflags &= ~(TPWO|TPRO); - } - } +/* + * Archive controller ports + */ +#define AV_DATAPORT(base) ((base)+0) /* data, read only */ +#define AV_CMDPORT(base) ((base)+0) /* command, write only */ +#define AV_STATPORT(base) ((base)+1) /* status, read only */ +#define AV_CTLPORT(base) ((base)+1) /* control, write only */ +#define AV_SDMAPORT(base) ((base)+2) /* start dma */ +#define AV_RDMAPORT(base) ((base)+3) /* reset dma */ +#define AV_NPORT 4 /* 4 i/o ports */ + +/* status port bits */ +#define AV_BUSY 0x40 /* not ready bit define */ +#define AV_NOEXCEP 0x20 /* no exception bit define */ +#define AV_RESETMASK 0xf8 /* to check after reset */ +#define AV_RESETVAL 0x50 /* state after reset */ + +/* control port bits */ +#define AV_RESET 0x80 /* reset command */ +#define AV_REQUEST 0x40 /* request command */ +#define AV_IEN 0x20 /* enable interrupts */ + +#define DMA_STATUSREG 0x8 +#define DMA_DONE(chan) (1 << (chan)) + +typedef struct { + unsigned short err; /* code for error encountered */ + unsigned short ercnt; /* number of error blocks */ + unsigned short urcnt; /* number of underruns */ +} wtstatus_t; + +typedef struct { + unsigned unit; /* unit number */ + unsigned port; /* base i/o port */ + unsigned chan; /* dma channel number, 1..3 */ + unsigned flags; /* state of tape drive */ + unsigned dens; /* tape density */ + void *buf; /* internal i/o buffer */ + + void *dmavaddr; /* virtual address of dma i/o buffer */ + unsigned dmatotal; /* size of i/o buffer */ + unsigned dmaflags; /* i/o direction, B_READ or B_WRITE */ + unsigned dmacount; /* resulting length of dma i/o */ + + wtstatus_t error; /* status of controller */ + + unsigned short DATAPORT, CMDPORT, STATPORT, CTLPORT, SDMAPORT, RDMAPORT; + unsigned char BUSY, NOEXCEP, RESETMASK, RESETVAL; + unsigned char ONLINE, RESET, REQUEST, IEN; +} wtinfo_t; + +wtinfo_t wttab[NWT]; /* tape info by unit number */ + +extern int hz; /* number of ticks per second */ + +static int wtwait (wtinfo_t *t, int catch, char *msg); +static int wtcmd (wtinfo_t *t, int cmd); +static int wtstart (wtinfo_t *t, unsigned mode, void *vaddr, unsigned len); +static void wtdma (wtinfo_t *t); +static void wtimer (wtinfo_t *t); +static void wtclock (wtinfo_t *t); +static int wtreset (wtinfo_t *t); +static int wtsense (wtinfo_t *t, int ignor); +static int wtstatus (wtinfo_t *t); +static void wtrewind (wtinfo_t *t); +static int wtreadfm (wtinfo_t *t); +static int wtwritefm (wtinfo_t *t); +static int wtpoll (wtinfo_t *t); + +extern void DELAY (int usec); +extern void bcopy (void *from, void *to, unsigned len); +extern void isa_dmastart (int flags, void *addr, unsigned len, unsigned chan); +extern void isa_dmadone (int flags, void *addr, unsigned len, int chan); +extern void printf (char *str, ...); +extern int splbio (void); +extern int splx (int level); +extern void timeout (void (*func) (), void *arg, int timo); +extern int tsleep (void *chan, int priority, char *msg, int timo); +extern void wakeup (void *chan); - if(bad) { - bp->b_flags |= B_ERROR; - bp->b_error = bad; - } - bp->b_resid = bp->b_bcount - finished; -xit: - biodone(bp); - if (wtimeron) - Hogtime = lbolt; - else if (Hogproc == myproc) - Hogproc = (struct proc *) 0; +/* + * Probe for the presence of the device. + */ +int wtprobe (struct isa_device *id) +{ + wtinfo_t *t = wttab + id->id_unit; + + t->unit = id->id_unit; + t->chan = id->id_drq; + t->port = 0; /* Mark it as not configured. */ + if (t->chan<1 || t->chan>3) { + printf ("wt%d: Bad drq=%d, should be 1..3\n", t->unit, t->chan); + return (0); + } + t->port = id->id_iobase; + + /* Try Wangtek. */ + t->CTLPORT = WT_CTLPORT (t->port); t->STATPORT = WT_STATPORT (t->port); + t->CMDPORT = WT_CMDPORT (t->port); t->DATAPORT = WT_DATAPORT (t->port); + t->SDMAPORT = 0; t->RDMAPORT = 0; + t->BUSY = WT_BUSY; t->NOEXCEP = WT_NOEXCEP; + t->RESETMASK = WT_RESETMASK; t->RESETVAL = WT_RESETVAL; + t->ONLINE = WT_ONLINE; t->RESET = WT_RESET; + t->REQUEST = WT_REQUEST; t->IEN = WT_IEN (t->chan); + if (wtreset (t)) + return (WT_NPORT); + + /* Try Archive. */ + t->CTLPORT = AV_CTLPORT (t->port); t->STATPORT = AV_STATPORT (t->port); + t->CMDPORT = AV_CMDPORT (t->port); t->DATAPORT = AV_DATAPORT (t->port); + t->SDMAPORT = AV_SDMAPORT (t->port); t->RDMAPORT = AV_RDMAPORT (t->port); + t->BUSY = AV_BUSY; t->NOEXCEP = AV_NOEXCEP; + t->RESETMASK = AV_RESETMASK; t->RESETVAL = AV_RESETVAL; + t->ONLINE = 0; t->RESET = AV_RESET; + t->REQUEST = AV_REQUEST; t->IEN = AV_IEN; + if (wtreset (t)) + return (AV_NPORT); + + /* Tape controller not found. */ + t->port = 0; + return (0); } /* - * simulate an interrupt periodically while I/O is going - * this is necessary in case interrupts get eaten due to - * multiple devices on a single IRQ line + * Device is found, configure it. */ -wtimer() +int wtattach (struct isa_device *id) { - /* If I/O going and not in isr(), simulate interrupt - * If no I/O for at least 1 second, stop being a Hog - * If I/O done and not a Hog, turn off wtimer() - */ - if (wtio && !isrlock) - isr(); + wtinfo_t *t = wttab + id->id_unit; - if ((Hogproc == myproc) && Hogtime && (lbolt-Hogtime > HZ)) - Hogproc = (struct proc *) 0; - - if (wtio || (Hogproc == myproc)) - timeout(wtimer, (caddr_t) 0, HZ); - else - wtimeron = 0; + if (t->RDMAPORT) { + printf ("wt%d: type \n", t->unit); + outb (t->RDMAPORT, 0); /* reset dma */ + } else + printf ("wt%d: type \n", t->unit); + t->flags = TPSTART; /* tape is rewound */ + t->dens = -1; /* unknown density */ + t->buf = malloc (BLKSIZE, M_TEMP, M_NOWAIT); + return (1); } +struct isa_driver wtdriver = { wtprobe, wtattach, "wt", }; -wtrawio(bp) -struct buf *bp; +int wtdump (int dev) { - wtstrategy(bp); - biowait(bp); - return(0); + /* Not implemented */ + return (EINVAL); } -/* - * ioctl routine - * for user level QIC commands only - */ -wtioctl(dev, cmd, arg, mode) -int dev, cmd; -unsigned long arg; -int mode; +int wtsize (int dev) { - if (cmd == WTQICMD) - { - if ((qicmd((int)arg) == ERROR) || (rdyexc(HZ) == ERROR)) - { - wtsense(0); - return(EIO); - } - return(0); - } - return(EINVAL); + /* Not implemented */ + return (-1); } /* - * open routine - * called on every device open + * Open routine, called on every device open. */ -wtopen(dev, flag) -int dev, flag; -{ - if (first_wtopen_ever) { - wtinit(); - first_wtopen_ever = 0; - } -#ifdef DEBUG - printf("wtopen ...\n"); -#endif - if (!pageaddr) { - return(ENXIO); - } - if (wtflags & (TPINUSE)) { - return(ENXIO); - } - if (wtflags & (TPDEAD)) { - return(EIO); - } - /* If a rewind from the last session is going on, wait */ - while(wtflags & TPREW) { -#ifdef DEBUG - debug("Waiting for rew to finish\n"); -#endif - DELAY(1000000); /* delay one second */ - } - /* Only do reset and select when tape light is off, and tape is rewound. - * This allows multiple volumes. */ - if (wtflags & TPSTART) { - if (t_reset() != SUCCESS) { - return(ENXIO); +int wtopen (int dev, int flag) +{ + int u = minor (dev) & T_UNIT; + wtinfo_t *t = wttab + u; + int error; + + if (u >= NWT || !t->port) + return (ENXIO); + + /* Check that device is not in use */ + if (t->flags & TPINUSE) + return (EBUSY); + + /* If the tape is in rewound state, check the status and set density. */ + if (t->flags & TPSTART) { + /* If rewind is going on, wait */ + error = wtwait (t, PCATCH, "wtrew"); + if (error) + return (error); + + if (! wtsense (t, (flag & FWRITE) ? 0 : TP_WRP)) { + /* Bad status. Reset the controller. */ + if (! wtreset (t)) + return (ENXIO); + if (! wtsense (t, (flag & FWRITE) ? 0 : TP_WRP)) + return (ENXIO); } -#ifdef DEBUG - debug("reset done. calling wtsense\n"); -#endif - if (wtsense(TP_WRP) == ERROR) { - return (EIO); + + /* Set up tape density. */ + if (t->dens != (minor (dev) & T_DENSEL)) { + int d; + + switch (minor (dev) & T_DENSEL) { + default: + case T_800BPI: d = QIC_FMT150; break; /* minor 000 */ + case T_1600BPI: d = QIC_FMT120; break; /* minor 010 */ + case T_6250BPI: d = QIC_FMT24; break; /* minor 020 */ + case T_BADBPI: d = QIC_FMT11; break; /* minor 030 */ + } + if (! wtcmd (t, d)) + return (ENXIO); + + /* Check the status of the controller. */ + if (! wtsense (t, (flag & FWRITE) ? 0 : TP_WRP)) + return (ENXIO); + + t->dens = minor (dev) & T_DENSEL; } -#ifdef DEBUG - debug("wtsense done\n"); -#endif - wtflags &= ~TPSTART; - } + t->flags &= ~TPSTART; + } else if (t->dens != (minor (dev) & T_DENSEL)) + return (ENXIO); - wtflags = TPINUSE; + t->flags = TPINUSE; if (flag & FREAD) - wtflags |= TPREAD; + t->flags |= TPREAD; if (flag & FWRITE) - wtflags |= TPWRITE; - rwtbuf.b_flags = 0; - myproc = curproc; /* for comparison */ -#ifdef not - switch(TP_DENS(dev)) { -case 0: -cmds(0x28); -break; -case 1: -cmds(0x29); -break; -case 2: -cmds(0x27); -break; -case 3: -cmds(0x24); - } -#endif - return(0); + t->flags |= TPWRITE; + return (0); } /* - * close routine - * called on last device close - * If not rewind-on-close, leave read or write command intact. + * Close routine, called on last device close. */ -wtclose(dev) +int wtclose (int dev) { - int wtdsl2(); - -#ifdef DEBUG - debug("WTclose:\n"); -#endif - if (Hogproc == myproc) - Hogproc = (struct proc *) 0; - if (!exflag && (wtflags & TPWANY) && !(wtflags & (TPSESS|TPDEAD))) { - if (!(wtflags & TPWO)) - wstart(); -#ifdef DEBUG - debug("WT: Writing file mark\n"); -#endif - wmark(); /* write file mark */ -#ifdef DEBUG - debug("WT: Wrote file mark, going to wait\n"); -#endif - if (rdyexc(HZ/10) == ERROR) { - wtsense(0); - } - } - if (TP_REWCLOSE(dev) || (wtflags & (TPSESS|TPDEAD))) { - /* rewind tape to beginning of tape, deselect tape, and make a note */ - /* don't wait until rewind, though */ - /* Ending read or write causes rewind to happen, if no error, - * and READY and EXCEPTION stay up until it finishes */ - if (wtflags & (TPRO|TPWO)) - { -#ifdef DEBUG - debug("End read or write\n"); -#endif - rdyexc(HZ/10); - ioend(); - wtflags &= ~(TPRO|TPWO); - } - else wtwind(); - wtflags |= TPSTART | TPREW; - timeout(wtdsl2, 0, HZ); - } - else if (!(wtflags & (TPVOL|TPWANY))) - { - /* space forward to after next file mark no writing done */ - /* This allows skipping data without reading it.*/ -#ifdef DEBUG - debug("Reading past file mark\n"); -#endif - if (!(wtflags & TPRO)) - rstart(); - rmark(); - if (rdyexc(HZ/10)) - { - wtsense(TP_WRP); - } - } - wtflags &= TPREW|TPDEAD|TPSTART|TPRO|TPWO; - return(0); -} + int u = minor (dev) & T_UNIT; + wtinfo_t *t = wttab + u; -/* return ERROR if user I/O request should receive an I/O error code */ - -wtsense(ignor) -{ - wtflags &= ~(TPRO|TPWO); -#ifdef DEBUGx - debug("WTsense: start "); -#endif - if (rdstatus(&wterror) == ERROR) - { -#ifdef DEBUG - debug("WTsense: Can't read status\n"); -#endif - return(ERROR); - } -#ifdef DEBUG - if (wterror.wt_err & (TP_ST0|TP_ST1)) - { - debug("Tperror: status %x error %d underruns %d\n", - wterror.wt_err, wterror.wt_ercnt, wterror.wt_urcnt); - } - else - debug("done. no error\n"); -#endif - wterror.wt_err &= ~ignor; /* ignore certain errors */ - reperr(wterror.wt_err); - if (((wterror.wt_err & TP_ST0) && (wterror.wt_err & TP_ERR0)) || - ((wterror.wt_err & TP_ST1) && (wterror.wt_err & TP_ERR1))) - return ERROR; - - return SUCCESS; -} + if (u >= NWT || !t->port) + return (ENXIO); -/* lifted from tdriver.c from Wangtek */ -reperr(srb0) -int srb0; -{ - int s0 = srb0 & (TP_ERR0|TP_ERR1); /* find out which exception to report */ - - if (s0) { - if (s0 & TP_USL) - sterr("Drive not online"); - else if (s0 & TP_CNI) - sterr("No cartridge"); - else if ((s0 & TP_WRP) && !(wtflags & TPWP)) - { - sterr("Tape is write protected"); - wtflags |= TPWP; - } - /* - if (s0 & TP_FIL) - sterr("Filemark detected"); - */ - else if (s0 & TP_BNL) - sterr("Block in error not located"); - else if (s0 & TP_UDA) - sterr("Unrecoverable data error"); - /* - else if (s0 & TP_EOM) - sterr("End of tape"); - */ - else if (s0 & TP_NDT) - sterr("No data detected"); - /* - if (s0 & TP_POR) - sterr("Reset occured"); - */ - else if (s0 & TP_BOM) - sterr("Beginning of tape"); - else if (s0 & TP_ILL) - sterr("Illegal command"); - } -} - -sterr(errstr) -char *errstr; -{ - printf("Streamer: %s\n", errstr); -} + /* If rewind is pending, do nothing */ + if (t->flags & TPREW) + goto done; -/* Wait until rewind finishes, and deselect drive */ -wtdsl2() { - int stat; - - stat = inb(wtport) & (READY|EXCEP); -#ifdef DEBUG - debug("Timeout: Waiting for rewind to finish: stat %x\n", stat); -#endif - switch (stat) { - /* They're active low, ya'know */ - case READY|EXCEP: - timeout(wtdsl2, (caddr_t) 0, HZ); - return; - case EXCEP: - wtflags &= ~TPREW; - return; - case READY: - case 0: - wtflags &= ~TPREW; - sterr("Rewind failed"); - wtsense(TP_WRP); - return; - } - } + /* If seek forward is pending and no rewind on close, do nothing */ + if ((t->flags & TPRMARK) && (minor (dev) & T_NOREWIND)) + goto done; -wtwind() { -#ifdef DEBUG - debug("WT: About to rewind\n"); -#endif - rwind(); /* actually start rewind */ -} + /* If file mark read is going on, wait */ + wtwait (t, 0, "wtrfm"); -wtintr(unit) { - if (wtflags & (TPWO|TPRO)) - { - isrlock = 1; - if (wtio) isr(); - isrlock = 0; - } -} + if (t->flags & TPWANY) + /* Tape was written. Write file mark. */ + wtwritefm (t); -wtinit() { - if (wtchan < 1 || wtchan > 3) - { - sterr("Bad DMA channel, cannot init driver"); - return; + if (! (minor (dev) & T_NOREWIND)) { + /* Rewind tape to beginning of tape. */ + /* Don't wait until rewind, though. */ + wtrewind (t); + goto done; } - wtlinit(); /* init assembly language variables */ - pageset(); + if ((t->flags & TPRANY) && ! (t->flags & (TPVOL | TPWANY))) + /* Space forward to after next file mark if no writing done. */ + /* Don't wait for completion. */ + wtreadfm (t); +done: + t->flags &= TPREW | TPRMARK | TPSTART | TPTIMER; + return (0); } -rdyexc(ticks) -{ - int s; -#ifdef DEBUG - int os = 0xffff; /* force printout first time */ -#endif - for (;;) { /* loop until ready or exception */ - s=(inb(wtport) & 0xff); /* read the status register */ -#ifdef DEBUG - if (os != s) { - debug("Status reg = %x\n", s); /* */ - os = s; - } -#endif - if (!(s & EXCEP)) /* check if exception have occured */ +/* + * Ioctl routine. Compatible with BSD ioctls. + * Direct QIC-02 commands ERASE and RETENSION added. + * There are three possible ioctls: + * ioctl (int fd, MTIOCGET, struct mtget *buf) -- get status + * ioctl (int fd, MTIOCTOP, struct mtop *buf) -- do BSD-like op + * ioctl (int fd, WTQICMD, int qicop) -- do QIC op + */ +int wtioctl (int dev, int cmd, void *arg, int mode) +{ + int u = minor (dev) & T_UNIT; + wtinfo_t *t = wttab + u; + int error, count, op; + + if (u >= NWT || !t->port) + return (ENXIO); + + switch (cmd) { + default: + return (EINVAL); + case WTQICMD: /* direct QIC command */ + op = (int) *(void**)arg; + switch (op) { + default: + return (EINVAL); + case QIC_ERASE: /* erase the whole tape */ + if (! (t->flags & TPWRITE) || (t->flags & TPWP)) + return (EACCES); + if (error = wtwait (t, PCATCH, "wterase")) + return (error); break; - if (!(s & READY)) /* check if controller is ready */ + case QIC_RETENS: /* retension the tape */ + if (error = wtwait (t, PCATCH, "wtretens")) + return (error); break; - s = splbio(); - DELAY((ticks/HZ)*1000000); /* */ - splx(s); + } + /* Both ERASE and RETENS operations work like REWIND. */ + /* Simulate the rewind operation here. */ + t->flags &= ~(TPRO | TPWO | TPVOL); + if (! wtcmd (t, op)) + return (EIO); + t->flags |= TPSTART | TPREW; + if (op == QIC_ERASE) + t->flags |= TPWANY; + wtclock (t); + return (0); + case MTIOCIEOT: /* ignore EOT errors */ + case MTIOCEEOT: /* enable EOT errors */ + return (0); + case MTIOCGET: + ((struct mtget*)arg)->mt_type = t->RDMAPORT ? MT_ISVIPER1 : 0x11; + ((struct mtget*)arg)->mt_dsreg = t->flags; /* status */ + ((struct mtget*)arg)->mt_erreg = t->error.err; /* errors */ + ((struct mtget*)arg)->mt_resid = 0; + ((struct mtget*)arg)->mt_fileno = 0; /* file */ + ((struct mtget*)arg)->mt_blkno = 0; /* block */ + return (0); + case MTIOCTOP: + break; } -#ifdef DEBUG - debug("Status reg = %x on return\n", s); /* */ -#endif - return((s & EXCEP)?SUCCESS:ERROR); /* return exception if it occured */ -} - -pollrdy() -{ - int sps; -#ifdef DEBUG - debug("Pollrdy\n"); -#endif - sps = splbio(); - while (wtio) { - int error; - - if (error = tsleep((caddr_t)&wci, WTPRI | PCATCH, - "wtpoll", 0)) { - splx(sps); - return(error); + switch ((short) ((struct mtop*)arg)->mt_op) { + default: + case MTFSR: /* forward space record */ + case MTBSR: /* backward space record */ + case MTBSF: /* backward space file */ + break; + case MTNOP: /* no operation, sets status only */ + case MTCACHE: /* enable controller cache */ + case MTNOCACHE: /* disable controller cache */ + return (0); + case MTREW: /* rewind */ + case MTOFFL: /* rewind and put the drive offline */ + if (t->flags & TPREW) /* rewind is running */ + return (0); + if (error = wtwait (t, PCATCH, "wtorew")) + return (error); + wtrewind (t); + return (0); + case MTFSF: /* forward space file */ + for (count=((struct mtop*)arg)->mt_count; count>0; --count) { + if (error = wtwait (t, PCATCH, "wtorfm")) + return (error); + if (error = wtreadfm (t)) + return (error); } + return (0); + case MTWEOF: /* write an end-of-file record */ + if (! (t->flags & TPWRITE) || (t->flags & TPWP)) + return (EACCES); + if (error = wtwait (t, PCATCH, "wtowfm")) + return (error); + if (error = wtwritefm (t)) + return (error); + return (0); } - splx(sps); -#ifdef DEBUG - debug("Finish poll, wci %d exflag %d\n", wci, exflag); -#endif - return (EIO); + return (EINVAL); } -wtdma() /* start up i/o operation, called from dma() in wtlib1.s */ +/* + * Strategy routine. + */ +void wtstrategy (struct buf *bp) { - wtio = 1; - if (!wtimeron) - { - wtimeron = 1; - timeout(wtimer, (caddr_t) 0, HZ/2); - } -} + int u = minor (bp->b_dev) & T_UNIT; + wtinfo_t *t = wttab + u; + int s; -wtwake() /* end i/o operation, called from isr() in wtlib1.s */ -{ - wtio = 0; - wakeup(&wci); -} + bp->b_resid = bp->b_bcount; + if (u >= NWT || !t->port) + goto errxit; -pageset() -{ - unsigned long pp; + /* at file marks and end of tape, we just return '0 bytes available' */ + if (t->flags & TPVOL) + goto xit; - pp = (unsigned long) pagebuf; - pageaddr = kvtop(pp); -#ifdef DEBUG - debug("pageset: addr %lx\n", pageaddr); -#endif -} + if (bp->b_flags & B_READ) { + /* Check read access and no previous write to this tape. */ + if (! (t->flags & TPREAD) || (t->flags & TPWANY)) + goto errxit; + /* For now, we assume that all data will be copied out */ + /* If read command outstanding, just skip down */ + if (! (t->flags & TPRO)) { + if (! wtsense (t, TP_WRP)) /* clear status */ + goto errxit; + if (! wtcmd (t, QIC_RDDATA)) { /* sed read mode */ + wtsense (t, TP_WRP); + goto errxit; + } + t->flags |= TPRO | TPRANY; + } + } else { + /* Check write access and write protection. */ + /* No previous read from this tape allowed. */ + if (! (t->flags & TPWRITE) || (t->flags & (TPWP | TPRANY))) + goto errxit; + /* If write command outstanding, just skip down */ + if (! (t->flags & TPWO)) { + if (! wtsense (t, 0)) /* clear status */ + goto errxit; + if (! wtcmd (t, QIC_WRTDATA)) { /* set write mode */ + wtsense (t, 0); + goto errxit; + } + t->flags |= TPWO | TPWANY; + } + } -#define near + if (! bp->b_bcount) + goto xit; -static near -sendcmd() -{ - /* desired command in global mbits */ + t->flags &= ~TPEXCEP; + s = splbio (); + if (wtstart (t, bp->b_flags, bp->b_un.b_addr, bp->b_bcount)) { + wtwait (t, 0, (bp->b_flags & B_READ) ? "wtread" : "wtwrite"); + bp->b_resid -= t->dmacount; + } + splx (s); - outb(CTLPORT, mbits | REQUEST); /* set request */ - while (inb(STATPORT) & READY); /* wait for ready */ - outb(CTLPORT, mbits & ~REQUEST); /* reset request */ - while ((inb(STATPORT) & READY) == 0); /* wait for not ready */ + if (t->flags & TPEXCEP) { +errxit: bp->b_flags |= B_ERROR; + bp->b_error = EIO; + } +xit: biodone (bp); + return; } -static near /* execute command */ -cmds(cmd) +/* + * Interrupt routine. + */ +void wtintr (int u) { - register s; - - do s = inb(STATPORT); - while ((s & STAT) == STAT); /* wait for ready */ - - if ((s & EXCEP) == 0) /* if exception */ - return ERROR; /* error */ - - outb(CMDPORT, cmd); /* output the command */ - - outb(CTLPORT, mbits=ONLINE); /* set & send ONLINE */ - sendcmd(); - - return SUCCESS; -} + wtinfo_t *t = wttab + u; + unsigned char s; -qicmd(cmd) -{ - return cmds(cmd); -} + if (u >= NWT || !t->port) + return; -rstart() -{ - return cmds(RDDATA); -} + s = inb (t->STATPORT); /* get status */ + if ((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP)) + return; /* device is busy */ + outb (t->CTLPORT, t->ONLINE); /* stop controller */ -rmark() -{ - return cmds(READFM); -} + /* + * Check if rewind finished. + */ + if (t->flags & TPREW) { + t->flags &= ~TPREW; /* Rewind finished. */ + wtsense (t, TP_WRP); + wakeup (t); + return; + } -wstart() -{ - return cmds(WRTDATA); -} + /* + * Check if writing/reading of file mark finished. + */ + if (t->flags & (TPRMARK | TPWMARK)) { + if (! (s & t->NOEXCEP)) /* Operation failed. */ + wtsense (t, (t->flags & TPRMARK) ? TP_WRP : 0); + t->flags &= ~(TPRMARK | TPWMARK); /* Operation finished. */ + wakeup (t); + return; + } -ioend() -{ - register s; - register rval = SUCCESS; + /* + * Do we started any i/o? If no, just return. + */ + if (! (t->flags & TPACTIVE)) + return; + t->flags &= ~TPACTIVE; - do s = inb(STATPORT); - while ((s & STAT) == STAT); /* wait for ready */ + if (inb (DMA_STATUSREG) & DMA_DONE (t->chan)) /* if dma finished */ + t->dmacount += BLKSIZE; /* increment counter */ - if ((s & EXCEP) == 0) /* if exception */ - rval = ERROR; /* error */ - - mbits &= ~ONLINE; - outb(CTLPORT, mbits); /* reset ONLINE */ - outb(MASKREG, wtchan+4); /* turn off dma */ - outb(CLEARFF, 0); /* reset direction flag */ + /* + * Clean up dma. + */ + if ((t->dmaflags & B_READ) && (t->dmatotal - t->dmacount) < BLKSIZE) { + /* If the address crosses 64-k boundary, or reading short block, + * copy the internal buffer to the user memory. */ + isa_dmadone (t->dmaflags, t->buf, BLKSIZE, t->chan); + bcopy (t->buf, t->dmavaddr, t->dmatotal - t->dmacount); + } else + isa_dmadone (t->dmaflags, t->dmavaddr, BLKSIZE, t->chan); + + /* + * On exception, check for end of file and end of volume. + */ + if (! (s & t->NOEXCEP)) { + wtsense (t, (t->dmaflags & B_READ) ? TP_WRP : 0); + if (t->error.err & (TP_EOM | TP_FIL)) + t->flags |= TPVOL; /* end of file */ + else + t->flags |= TPEXCEP; /* i/o error */ + wakeup (t); + return; + } - return rval; + if (t->dmacount < t->dmatotal) { /* continue i/o */ + t->dmavaddr += BLKSIZE; + wtdma (t); + return; + } + if (t->dmacount > t->dmatotal) /* short last block */ + t->dmacount = t->dmatotal; + wakeup (t); /* wake up user level */ } -wmark() +/* start the rewind operation */ +static void wtrewind (wtinfo_t *t) { - register s; - - if (cmds(WRITEFM) == ERROR) - return ERROR; - - do s = inb(STATPORT); - while ((s & STAT) == STAT); /* wait for ready */ - - if ((s & EXCEP) == 0) /* if exception */ - return ERROR; /* error */ - - return SUCCESS; + t->flags &= ~(TPRO | TPWO | TPVOL); + if (! wtcmd (t, QIC_REWIND)) + return; + t->flags |= TPSTART | TPREW; + wtclock (t); } -rwind() +/* start the `read marker' operation */ +static int wtreadfm (wtinfo_t *t) { - register s; - - mbits = CMDOFF; - - do s = inb(STATPORT); - while ((s & STAT) == STAT); /* wait for ready */ - - outb(CMDPORT, REWIND); - sendcmd(); - - return SUCCESS; + t->flags &= ~(TPRO | TPWO | TPVOL); + if (! wtcmd (t, QIC_READFM)) { + wtsense (t, TP_WRP); + return (EIO); + } + t->flags |= TPRMARK | TPRANY; + wtclock (t); + /* Don't wait for completion here. */ + return (0); } -rdstatus(stp) -char *stp; /* pointer to 6 byte buffer */ +/* write marker to the tape */ +static int wtwritefm (wtinfo_t *t) { - register s; - int n; - - do s = inb(STATPORT); - while ((s & STAT) == STAT); /* wait for ready or exception */ - - outb(CMDPORT, RDSTAT); - sendcmd(); /* send read status command */ - - for (n=0; n<6; n++) - { -#ifdef DEBUGx - debug("rdstatus: waiting, byte %d\n", n); -#endif - do s = inb(STATPORT); - while ((s & STAT) == STAT); /* wait for ready */ -#ifdef DEBUGx - debug("rdstatus: done\n"); -#endif - if ((s & EXCEP) == 0) /* if exception */ - return ERROR; /* error */ - - *stp++ = inb(DATAPORT); /* read status byte */ - - outb(CTLPORT, mbits | REQUEST); /* set request */ -#ifdef DEBUGx - debug("rdstatus: waiting after request, byte %d\n", n); -#endif - while ((inb(STATPORT)&READY) == 0); /* wait for not ready */ - for (s=100; s>0; s--); /* wait an additional time */ - - outb(CTLPORT, mbits & ~REQUEST);/* unset request */ -#ifdef DEBUGx - debug("rdstatus: done\n"); -#endif + tsleep (wtwritefm, WTPRI, "wtwfm", hz); /* timeout: 1 second */ + t->flags &= ~(TPRO | TPWO); + if (! wtcmd (t, QIC_WRITEFM)) { + wtsense (t, 0); + return (EIO); } - return SUCCESS; + t->flags |= TPWMARK | TPWANY; + wtclock (t); + return (wtwait (t, 0, "wtwfm")); } -t_reset() +/* wait for controller ready or exception */ +static int wtpoll (wtinfo_t *t) { - register i; - mbits |= RESET; - outb(CTLPORT, mbits); /* send reset */ - DELAY(20); - mbits &= ~RESET; - outb(CTLPORT, mbits); /* turn off reset */ - if ((inb(STATPORT) & RESETMASK) == RESETVAL) - return SUCCESS; - return ERROR; -} + int s, NOTREADY = t->BUSY | t->NOEXCEP; -static -dma() -{ - int x=splbio(); - wtdma(); - outb(CLEARFF, 0); - outb(MODEREG, mode); /* set dma mode */ - outb(dmareg, bufptr & 0xFF); - outb(dmareg, (bufptr>>8) & 0xFF); - outb(pagereg, (bufptr>>16) & 0xFF); - outb(dmareg+1, (BLKSIZE-1) & 0xFF); - outb(dmareg+1, (BLKSIZE-1) >> 8); - outb(wtport, eqdma+ONLINE); - outb(MASKREG, wtchan); /* enable command to 8237, start dma */ - splx(x); + /* Poll status port, waiting for ready or exception. */ + do s = inb (t->STATPORT); + while ((s & NOTREADY) == NOTREADY); + return (s); } -static near -wtstart(buf, cnt) -long buf; -int cnt; +/* execute QIC command */ +static int wtcmd (wtinfo_t *t, int cmd) { - register s; - - bufptr = buf; /* init statics */ - numbytes = cnt; - wci = 0; /* init flags */ - exflag = 0; - bytes = 0; /* init counter */ + if (! (wtpoll (t) & t->NOEXCEP)) /* wait for ready */ + return (0); /* error */ + + outb (t->CMDPORT, cmd); /* output the command */ - do s = inb(STATPORT) & STAT; - while (s == STAT); /* wait for ready or error */ + outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */ + while (inb (t->STATPORT) & t->BUSY) /* wait for ready */ + continue; + outb (t->CTLPORT, t->IEN | t->ONLINE); /* reset request */ + while (! (inb (t->STATPORT) & t->BUSY)) /* wait for not ready */ + continue; - if (s & EXCEP) /* no error */ - { - dma(); - return SUCCESS; - } - return ERROR; /* error */ + return (1); } -rtape(buf, cnt) -long buf; /* physical address */ -int cnt; /* number of bytes */ +/* wait for the end of i/o, seeking marker or rewind operation */ +static int wtwait (wtinfo_t *t, int catch, char *msg) { - mode = dma_read; - return wtstart(buf,cnt); -} + int error; -wtape(buf, cnt) -long buf; /* physical address */ -int cnt; /* number of bytes */ -{ - mode = dma_write; - return wtstart(buf,cnt); + while (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK)) + if (error = tsleep (t, WTPRI | catch, msg, 0)) + return (error); + return (0); } -isr() +/* initialize dma for the i/o operation */ +static void wtdma (wtinfo_t *t) { - int stat = inb(wtport); - if (!(stat & EXCEP)) /* exception during I/O */ - { - if (bytes + BLKSIZE >= numbytes) wci = 1; - exflag = 1; - goto isrwake; - } - if ((stat & READY) || !(inb(STATUSREG) & dma_done)) - return; - exflag = 0; - outb(wtport, ONLINE); - bytes += BLKSIZE; - if (bytes >= numbytes) /* normal completion of I/O */ - { - wci = 1; -isrwake: - outb(MASKREG, 4+wtchan); /* turn off dma */ - wtwake(); /* wake up user level */ - } + t->flags |= TPACTIVE; + wtclock (t); + + if (t->SDMAPORT) + outb (t->SDMAPORT, 0); /* set dma */ + + if ((t->dmaflags & B_READ) && (t->dmatotal - t->dmacount) < BLKSIZE) + /* Reading short block. Do it through the internal buffer. */ + isa_dmastart (t->dmaflags, t->buf, BLKSIZE, t->chan); else - { /* continue I/O */ - bufptr += BLKSIZE; - dma(); - } -} + isa_dmastart (t->dmaflags, t->dmavaddr, BLKSIZE, t->chan); -wtlinit() -{ - switch (wtchan) { - case 1: - return; - case 2: - pagereg = 0x81; - dma_done = 4; - break; - case 3: - eqdma = 0x10; - pagereg = 0x82; - dma_done = 8; - break; - } - dma_write = wtchan+0x48; - dma_read = wtchan+0x44; - dmareg = wtchan+wtchan; + outb (t->CTLPORT, t->IEN | t->ONLINE); } -wtsize() +/* start i/o operation */ +static int wtstart (wtinfo_t *t, unsigned flags, void *vaddr, unsigned len) { + if (! (wtpoll (t) & t->NOEXCEP)) { /* wait for ready or error */ + t->flags |= TPEXCEP; /* error */ + return (0); + } + t->flags &= ~TPEXCEP; /* clear exception flag */ + t->dmavaddr = vaddr; + t->dmatotal = len; + t->dmacount = 0; + t->dmaflags = flags; + wtdma (t); + return (1); } -wtdump() +/* start timer */ +static void wtclock (wtinfo_t *t) { + if (! (t->flags & TPTIMER)) { + t->flags |= TPTIMER; + timeout (wtimer, t, hz); + } } -#include "i386/isa/isa_device.h" -#include "i386/isa/icu.h" - -int wtprobe(), wtattach(); -struct isa_driver wtdriver = { - wtprobe, wtattach, "wt", -}; - -wtprobe(dvp) - struct isa_device *dvp; +/* + * Simulate an interrupt periodically while i/o is going. + * This is necessary in case interrupts get eaten due to + * multiple devices on a single IRQ line. + */ +static void wtimer (wtinfo_t *t) { - int val,i,s; - -#ifdef lint - wtintr(0); -#endif - - wtport = dvp->id_iobase; - if(t_reset() != SUCCESS) return(0); - return(1); -} + int s; -wtattach() { } + t->flags &= ~TPTIMER; + if (! (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK))) + return; -#endif NWT + /* If i/o going, simulate interrupt. */ + s = splbio (); + wtintr (t->unit); + splx (s); + + /* Restart timer if i/o pending. */ + if (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK)) + wtclock (t); +} + +/* reset the controller */ +static int wtreset (wtinfo_t *t) +{ + outb (t->CTLPORT, t->RESET); /* send reset */ + DELAY (25); + outb (t->CTLPORT, 0); /* turn off reset */ + if ((inb (t->STATPORT) & t->RESETMASK) != t->RESETVAL) + return (0); + return (1); +} + +/* get controller status information */ +/* return 0 if user i/o request should receive an i/o error code */ +static int wtsense (wtinfo_t *t, int ignor) +{ + char *msg = 0; + int err; + + t->flags &= ~(TPRO | TPWO); + if (! wtstatus (t)) + return (0); + if (! (t->error.err & TP_ST0)) + t->error.err &= ~TP_ST0MASK; + if (! (t->error.err & TP_ST1)) + t->error.err &= ~TP_ST1MASK; + t->error.err &= ~ignor; /* ignore certain errors */ + err = t->error.err & (TP_FIL | TP_BNL | TP_UDA | TP_EOM | TP_WRP | + TP_USL | TP_CNI | TP_MBD | TP_NDT | TP_ILL); + if (! err) + return (1); + + /* lifted from tdriver.c from Wangtek */ + if (err & TP_USL) msg = "Drive not online"; + else if (err & TP_CNI) msg = "No cartridge"; + else if ((err & TP_WRP) && !(t->flags & TPWP)) { + msg = "Tape is write protected"; + t->flags |= TPWP; + } + else if (err & TP_FIL) msg = 0 /*"Filemark detected"*/; + else if (err & TP_EOM) msg = 0 /*"End of tape"*/; + else if (err & TP_BNL) msg = "Block not located"; + else if (err & TP_UDA) msg = "Unrecoverable data error"; + else if (err & TP_NDT) msg = "No data detected"; + else if (err & TP_ILL) msg = "Illegal command"; + if (msg) + printf ("wt%d: %s\n", t->unit, msg); + return (0); +} + +/* get controller status information */ +static int wtstatus (wtinfo_t *t) +{ + char *p; + + wtpoll (t); /* wait for ready or exception */ + outb (t->CMDPORT, QIC_RDSTAT); /* send `read status' command */ + + outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */ + while (inb (t->STATPORT) & t->BUSY) /* wait for ready */ + continue; + outb (t->CTLPORT, t->ONLINE); /* reset request */ + while (! (inb (t->STATPORT) & t->BUSY)) /* wait for not ready */ + continue; + + p = (char*) &t->error; + while (p < (char*)&t->error + 6) { + if (! (wtpoll (t) & t->NOEXCEP)) /* wait for ready */ + return (0); /* error */ + + *p++ = inb (t->DATAPORT); /* read status byte */ + + outb (t->CTLPORT, t->REQUEST); /* set request */ + while (! (inb (t->STATPORT) & t->BUSY)) /* wait for not ready */ + continue; + /* DELAY (50); */ /* wait 50 usec */ + outb (t->CTLPORT, 0); /* unset request */ + } + return (1); +} +#endif /* NWT */ diff --git a/sys/i386/isa/wtreg.h b/sys/i386/isa/wtreg.h index 8f79ca1dce..d7658f3fb9 100644 --- a/sys/i386/isa/wtreg.h +++ b/sys/i386/isa/wtreg.h @@ -1,40 +1,28 @@ -/*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. +/* + * Streamer tape driver for 386bsd and FreeBSD. + * Supports Archive QIC-02 and Wangtek QIC-02/QIC-36 boards. + * + * Copyright (C) 1993 by: + * Sergey Ryzhkov + * Serge Vakulenko + * + * Placed in the public domain with NO WARRANTIES, not even the implied + * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. + * Authors grant any other persons or organisations permission to use + * or modify this software as long as this message is kept with the software, + * all derivative works or modified versions. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * This driver is derived from the old 386bsd Wangtek streamer tape driver, + * made by Robert Baron at CMU, based on Intel sources. + * Authors thank Robert Baron, CMU and Intel and retain here + * the original CMU copyright notice. * - * @(#)wtreg.h 7.1 (Berkeley) 5/9/91 + * from: Version 1.1, Fri Sep 24 02:14:42 MSD 1993 + * $Id$ */ /* - * * Copyright (c) 1989 Carnegie-Mellon University. * All rights reserved. * @@ -61,35 +49,63 @@ * rights to redistribute these changes. */ -/* - * HISTORY - * $Log: wtreg.h,v $ - * Revision 2.2.1.1 90/01/08 13:29:25 rvb - * Add Intel copyright. - * [90/01/08 rvb] - * - * Revision 2.2 89/09/25 12:33:09 rvb - * Driver was provided by Intel 9/18/89. - * [89/09/23 rvb] - * - */ +/* ioctl for direct QIC commands */ +#define WTQICMD _IO('W', 0) -/* - * - * Copyright 1988, 1989 by Intel Corporation - * - */ +/* QIC-02 commands allowed for WTQICMD */ +#define QIC_ERASE 0x22 +#define QIC_RETENS 0x24 -/* - * wtioctl.h - * defines ioctl parameters for direct QIC commands - */ +/* internal QIC-02 commands */ +#define QIC_RDDATA 0x80 /* read data */ +#define QIC_READFM 0xa0 /* read file mark */ +#define QIC_WRTDATA 0x40 /* write data */ +#define QIC_WRITEFM 0x60 /* write file mark */ +#define QIC_RDSTAT 0xc0 /* read status command */ +#define QIC_REWIND 0x21 /* rewind command (position+bot) */ +#define QIC_FMT11 0x26 /* set format QIC-11 */ +#define QIC_FMT24 0x27 /* set format QIC-24 */ +#define QIC_FMT120 0x28 /* set format QIC-120 */ +#define QIC_FMT150 0x29 /* set format QIC-150 */ + +/* tape driver flags */ +#define TPINUSE 0x0001 /* tape is already open */ +#define TPREAD 0x0002 /* tape is only open for reading */ +#define TPWRITE 0x0004 /* tape is only open for writing */ +#define TPSTART 0x0008 /* tape must be rewound and reset */ +#define TPRMARK 0x0010 /* read file mark command outstanding */ +#define TPWMARK 0x0020 /* write file mark command outstanding */ +#define TPREW 0x0040 /* rewind command outstanding */ +#define TPEXCEP 0x0080 /* i/o exception flag */ +#define TPVOL 0x0100 /* read file mark or hit end of tape */ +#define TPWO 0x0200 /* write command outstanding */ +#define TPRO 0x0400 /* read command outstanding */ +#define TPWANY 0x0800 /* write command requested */ +#define TPRANY 0x1000 /* read command requested */ +#define TPWP 0x2000 /* write protect error seen */ +#define TPTIMER 0x4000 /* timer() is active */ +#define TPACTIVE 0x8000 /* dma i/o active */ -#define WTIOC ('W'<<8) -#define WTQICMD (WTIOC|0) +/* controller error register bits */ +#define TP_FIL 0x0001 /* File mark detected */ +#define TP_BNL 0x0002 /* Block not located */ +#define TP_UDA 0x0004 /* Unrecoverable data error */ +#define TP_EOM 0x0008 /* End of media */ +#define TP_WRP 0x0010 /* Write protected cartridge */ +#define TP_USL 0x0020 /* Unselected drive */ +#define TP_CNI 0x0040 /* Cartridge not in place */ +#define TP_ST0 0x0080 /* Status byte 0 bits */ +#define TP_ST0MASK 0x00ff /* Status byte 0 mask */ +#define TP_POR 0x0100 /* Power on/reset occurred */ +#define TP_RES1 0x0200 /* Reserved for end of media */ +#define TP_RES2 0x0400 /* Reserved for bus parity */ +#define TP_BOM 0x0800 /* Beginning of media */ +#define TP_MBD 0x1000 /* Marginal block detected */ +#define TP_NDT 0x2000 /* No data detected */ +#define TP_ILL 0x4000 /* Illegal command - should not happen! */ +#define TP_ST1 0x8000 /* Status byte 1 bits */ +#define TP_ST1MASK 0xff00 /* Status byte 1 mask */ -/* QIC commands allowed */ -#define SELECT 0x01 -#define REWIND 0x21 -#define ERASE 0x22 -#define RETENS 0x24 +/* formats for printing flags and error values */ +#define WTDS_BITS "\20\1inuse\2read\3write\4start\5rmark\6wmark\7rew\10excep\11vol\12wo\13ro\14wany\15rany\16wp\17timer\20active" +#define WTER_BITS "\20\1eof\2bnl\3uda\4eom\5wrp\6usl\7cni\11por\12res1\13res2\14bom\15mbd\16ndt\17ill" diff --git a/sys/i386/stand/Makefile b/sys/i386/stand/Makefile index 4a6f77d2a9..21c3564d96 100644 --- a/sys/i386/stand/Makefile +++ b/sys/i386/stand/Makefile @@ -1,14 +1,5 @@ # from: @(#)Makefile 7.9 (Berkeley) 5/8/91 -# -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 1 00088 -# -------------------- ----- ---------------------- -# -# 23 Oct 92 Joerg Lohse added rule to compile 'trimhd' -# (trimhd = 'dd bs=1 skip=32') -# 10 Mar 93 Rodney W. Grimes Fixed DESTDIR and STANDIR so -# that make install works correctly +# $Id$ # DESTDIR=/usr diff --git a/sys/i386/stand/as.c b/sys/i386/stand/as.c index d731f5a2cd..77810d9364 100644 --- a/sys/i386/stand/as.c +++ b/sys/i386/stand/as.c @@ -1,17 +1,11 @@ /* * sys/i386/stand/as.c * + * from: 386BSD 0.1 + * $Id$ * Standalone driver for Adaptech 1542 SCSI * * Pace Willisson pace@blitz.com April 8, 1992 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00088 - * -------------------- ----- ---------------------- - * - * 23 Oct 92 Joerg Lohse changed ccb opcode for compatibility - * with Adaptec AHA-1542A */ #include "param.h" diff --git a/sys/i386/stand/asbootblk.c b/sys/i386/stand/asbootblk.c index 87b543654b..23fefd0fa9 100644 --- a/sys/i386/stand/asbootblk.c +++ b/sys/i386/stand/asbootblk.c @@ -29,13 +29,8 @@ * This bootblock does not support fdisk partitions, and can only be used * as the master boot block. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00088 - * -------------------- ----- ---------------------- - * - * 23 Oct 92 Joerg Lohse changed ccb opcode for compatibility - * with Adaptec AHA-1542A + * from: 386BSD 0.1 + * $Id$ */ #include "param.h" diff --git a/sys/i386/stand/bmap.c b/sys/i386/stand/bmap.c index fb509847cd..2fcce47a5c 100644 --- a/sys/i386/stand/bmap.c +++ b/sys/i386/stand/bmap.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * derived from: @(#)ufs_bmap.c 7.13 (Berkeley) 5/8/91 + * from: @(#)ufs_bmap.c 7.13 (Berkeley) 5/8/91 + * $Id$ */ #include "param.h" diff --git a/sys/i386/stand/boot.c b/sys/i386/stand/boot.c index e59479beb8..da77fc615c 100644 --- a/sys/i386/stand/boot.c +++ b/sys/i386/stand/boot.c @@ -32,6 +32,9 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * from: @(#)boot.c 7.3 (Berkeley) 5/4/91 + * $Id$ */ #ifdef lint @@ -40,10 +43,6 @@ char copyright[] = All rights reserved.\n"; #endif /* not lint */ -#ifdef lint -static char sccsid[] = "from:@(#)boot.c 7.3 (Berkeley) 5/4/91"; -#endif /* not lint */ - #include "param.h" #include "reboot.h" #include diff --git a/sys/i386/stand/breadxx.c b/sys/i386/stand/breadxx.c index 4354274e97..bbc2668713 100644 --- a/sys/i386/stand/breadxx.c +++ b/sys/i386/stand/breadxx.c @@ -45,6 +45,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * + * from: 386BSD 0.1 + * $Id$ */ /* diff --git a/sys/i386/stand/cga.c b/sys/i386/stand/cga.c index 23713ba2c2..531655c51a 100644 --- a/sys/i386/stand/cga.c +++ b/sys/i386/stand/cga.c @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)cga.c 5.3 (Berkeley) 4/28/91 + * from: @(#)cga.c 5.3 (Berkeley) 4/28/91 + * $Id$ */ #include "param.h" diff --git a/sys/i386/stand/fd.c b/sys/i386/stand/fd.c index f933b6be7d..9c8bd8ce15 100644 --- a/sys/i386/stand/fd.c +++ b/sys/i386/stand/fd.c @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)fd.c 7.3 (Berkeley) 5/25/91 + * from: @(#)fd.c 7.3 (Berkeley) 5/25/91 + * $Id$ */ /****************************************************************************/ diff --git a/sys/i386/stand/fdbootblk.c b/sys/i386/stand/fdbootblk.c index d1a7adee8e..a0354105b2 100644 --- a/sys/i386/stand/fdbootblk.c +++ b/sys/i386/stand/fdbootblk.c @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)fdbootblk.c 7.2 (Berkeley) 5/4/91 + * from: @(#)fdbootblk.c 7.2 (Berkeley) 5/4/91 + * $Id$ */ /* @@ -45,15 +46,8 @@ * execute. * * No attempt is made to handle disk errors. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00073 - * -------------------- ----- ---------------------- - * - * 22 Jan 93 Frank Maclachlan Fixed NOP's to read correct register - * */ + /*#include "/sys/i386/isa/isa.h" #include "/sys/i386/isa/fdreg.h"*/ #define NOP inb $0x84,%al diff --git a/sys/i386/stand/fs.c b/sys/i386/stand/fs.c index 2401dcab70..71c635924c 100644 --- a/sys/i386/stand/fs.c +++ b/sys/i386/stand/fs.c @@ -13,7 +13,7 @@ * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This software is a component of "386BSD" developed by - William F. Jolitz, TeleMuse. + * William F. Jolitz, TeleMuse. * 4. Neither the name of the developer nor the name "386BSD" * may be used to endorse or promote products derived from this software * without specific prior written permission. @@ -44,10 +44,15 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * from: 386BSD 0.1 + * $Id$ + */ + +/* * * Routines to sift through a BSD fast filesystem. -wfj */ -static char rcsid[] = "$Header: /usr/src/sys.386bsd/i386/boot/RCS/fs.c,v 1.2 92/02/04 14:18:08 bill Exp $"; #include "param.h" #include "fs.h" diff --git a/sys/i386/stand/kbd.c b/sys/i386/stand/kbd.c index 63fc938060..a2e4182473 100644 --- a/sys/i386/stand/kbd.c +++ b/sys/i386/stand/kbd.c @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kbd.c 7.4 (Berkeley) 5/4/91 + * from: @(#)kbd.c 7.4 (Berkeley) 5/4/91 + * $Id$ */ #define L 0x01 /* locking function */ diff --git a/sys/i386/stand/prf.c b/sys/i386/stand/prf.c index 144c7fd572..f7c2cff6ed 100644 --- a/sys/i386/stand/prf.c +++ b/sys/i386/stand/prf.c @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)prf.c 7.4 (Berkeley) 5/4/91 + * from: @(#)prf.c 7.4 (Berkeley) 5/4/91 + * $Id$ */ #include "types.h" diff --git a/sys/i386/stand/saio.h b/sys/i386/stand/saio.h index c46fac5f5c..747d9b5668 100644 --- a/sys/i386/stand/saio.h +++ b/sys/i386/stand/saio.h @@ -45,13 +45,14 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * I/O interface to driver. - * - * $Header: /usr/bill/working/sys/kern/RCS/subr_rlist.c,v 1.2 92/01/21 21:29:31 william Exp $"; + * from: 386BSD 0.1 + * $Id$ */ /* + * I/O interface to driver. */ + struct iob { int i_dev; int i_adapt; diff --git a/sys/i386/stand/srt0.c b/sys/i386/stand/srt0.c index 59a51399e4..a702584975 100644 --- a/sys/i386/stand/srt0.c +++ b/sys/i386/stand/srt0.c @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)srt0.c 5.3 (Berkeley) 4/28/91 + * from: @(#)srt0.c 5.3 (Berkeley) 4/28/91 + * $Id$ */ /* diff --git a/sys/i386/stand/trimhd.c b/sys/i386/stand/trimhd.c index 7438a62f90..08bc5162b3 100644 --- a/sys/i386/stand/trimhd.c +++ b/sys/i386/stand/trimhd.c @@ -45,9 +45,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * + * from: 386BSD 0.1 + * $Id$ + */ + +/* * Trim off the start of a file. */ -static char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/subr_rlist.c,v 1.2 92/01/21 21:29:31 william Exp $"; main(argc, argv) char *argv[]; { int val, rv, buf; diff --git a/sys/i386/stand/wd.c b/sys/i386/stand/wd.c index 92428045c3..2e8a36ac6d 100644 --- a/sys/i386/stand/wd.c +++ b/sys/i386/stand/wd.c @@ -33,14 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)wd.c 7.3 (Berkeley) 5/4/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00049 - * -------------------- ----- ---------------------- - * - * 06 Sep 92 Robert D. Thrush Fix for HD not booting after install + * from: @(#)wd.c 7.3 (Berkeley) 5/4/91 + * $Id$ */ /* device driver for winchester disk */ diff --git a/sys/i386/stand/wdboot.x b/sys/i386/stand/wdboot.x deleted file mode 100644 index 488054bd1bcc0b366c3d59eabe3b4b3f5702b7c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 512 zcmeyh!+@dh!X5@j!vlRc`VQFfGm3fD@H0N(VdQ80@S1Ug;eibTAc?*UX|Dom7#KJh zVBr6MAZz}41~5JOJcBrc5m0^~1CZFyz`)6{lYxnW;q#I`3=CWh896|K!@E8)GB9l5 zfA^$i*Jlum3B>viVkrPwf56NqEpJvdlzv&kQSxEe2cXnW1|X64DfdZBj%Lq*`g3zh)G0|7f2SU4H}Uw}Dtl*SVRp{oEMeOHmnt_data)) diff --git a/sys/isofs/iso_rrip.h b/sys/isofs/iso_rrip.h new file mode 100644 index 0000000000..01df4e49e3 --- /dev/null +++ b/sys/isofs/iso_rrip.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 1993 Atsushi Murai (amurai@spec.co.jp) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Atsushi Murai(amurai@spec.co.jp)``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)iso_rrip.h + * $Id: iso_rrip.h,v 1.2 1993/10/16 19:26:44 rgrimes Exp $ + */ + +/* + * Analyze function flag + */ +#define ISO_SUSP_ATTR 0x0001 +#define ISO_SUSP_DEVICE 0x0002 +#define ISO_SUSP_SLINK 0x0004 +#define ISO_SUSP_ALTNAME 0x0008 +#define ISO_SUSP_CLINK 0x0010 +#define ISO_SUSP_PLINK 0x0020 +#define ISO_SUSP_RELDIR 0x0040 +#define ISO_SUSP_TSTAMP 0x0080 +#define ISO_SUSP_IDFLAG 0x0100 +#define ISO_SUSP_EXFLAG 0x0200 +#define ISO_SUSP_UNKNOWN 0x0400 + +typedef struct { + ISO_RRIP_INODE inode; + u_short iso_altlen; /* Alt Name length */ + u_short iso_symlen; /* Symbol Name length */ + char *iso_altname; /* Alt Name (no Null terminated ) */ + char *iso_symname; /* Symbol Name (no NULL termninated )*/ +} ISO_RRIP_ANALYZE; diff --git a/sys/isofs/isofs_bmap.c b/sys/isofs/isofs_bmap.c index 8659831127..dd8859bb66 100644 --- a/sys/isofs/isofs_bmap.c +++ b/sys/isofs/isofs_bmap.c @@ -1,3 +1,7 @@ +/* + * $Id: isofs_bmap.c,v 1.2 1993/05/20 03:30:44 cgd Exp $ + */ + #include "param.h" #include "namei.h" #include "buf.h" diff --git a/sys/isofs/isofs_lookup.c b/sys/isofs/isofs_lookup.c index a3b0a1c35e..049f2346b7 100644 --- a/sys/isofs/isofs_lookup.c +++ b/sys/isofs/isofs_lookup.c @@ -2,6 +2,9 @@ * Copyright (c) 1989 The Regents of the University of California. * All rights reserved. * + * Copyright (c) 1983 Atsushi Murai (amurai@spec.co.jp) + * All rights reserved for Rock Ridge Extension Support. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -30,18 +33,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ufs_lookup.c 7.33 (Berkeley) 5/19/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00151 - * -------------------- ----- ---------------------- - * - * 10 Aug 92 Scott Burris Fixed "delete from CD-ROM" bug - * 23 Apr 93 Jagane D Sundar support nfs exported isofs + * from: @(#)ufs_lookup.c 7.33 (Berkeley) 5/19/91 + * $Id: isofs_lookup.c,v 1.3 1993/07/27 10:52:35 davidg Exp $ */ #include "param.h" +#include "systm.h" #include "namei.h" #include "buf.h" #include "file.h" @@ -50,6 +47,8 @@ #include "iso.h" #include "isofs_node.h" +#include "iso_rrip.h" +#include "isofs_rrip.h" struct nchstats nchstats; @@ -115,6 +114,8 @@ isofs_lookup(vdp, ndp, p) int reclen; int namelen; + char altname[251]; + int i; ndp->ni_dvp = vdp; ndp->ni_vp = NULL; @@ -143,7 +144,7 @@ isofs_lookup(vdp, ndp, p) if (error == ENOENT) return (error); #ifdef PARANOID - if (vdp == ndp->ni_rdir && ndp->ni_isdotdot) + if (vdp == ndp->ni_rootdir && ndp->ni_isdotdot) panic("ufs_lookup: .. through root"); #endif /* @@ -260,14 +261,11 @@ searchloop: /* illegal entry, stop */ break; - if ((namelen == 1 + if (namelen == 1 && ((ndp->ni_namelen == 1 && ndp->ni_ptr[0] == '.' && ep->name[0] == 0) - || (ndp->ni_isdotdot && ep->name[0] == 1))) - || (namelen >= ndp->ni_namelen - && isofncmp(ndp->ni_ptr, ndp->ni_namelen, ep->name, - namelen))) { + || (ndp->ni_isdotdot && ep->name[0] == 1))) { /* * Save directory entry's inode number and * reclen in ndp->ni_ufs area, and release @@ -276,6 +274,28 @@ searchloop: ndp->ni_ufs.ufs_ino = isonum_733 (ep->extent); brelse(bp); goto found; + } else { + switch ( imp->iso_ftype ) { + case ISO_FTYPE_9660: + if( ( namelen >= ndp->ni_namelen ) && + ( isofncmp( ndp->ni_ptr, ndp->ni_namelen, ep->name, namelen ) ) ) { + ndp->ni_ufs.ufs_ino = isonum_733 (ep->extent); + brelse(bp); + goto found; + } + break; + case ISO_FTYPE_RRIP: + isofs_rrip_getname( ep, altname, &namelen ); + if ( ( namelen == ndp->ni_namelen ) && + ( !bcmp( ndp->ni_ptr, altname, ndp->ni_namelen ) ) ) { + ndp->ni_ufs.ufs_ino = isonum_733 (ep->extent); + brelse(bp); + goto found; + } + break; + default: + break; + } } ndp->ni_ufs.ufs_offset += reclen; entryoffsetinblock += reclen; @@ -391,5 +411,3 @@ iso_blkatoff(ip, offset, res, bpp) return (0); } - - diff --git a/sys/isofs/isofs_node.c b/sys/isofs/isofs_node.c index f80ee23587..8f3b521183 100644 --- a/sys/isofs/isofs_node.c +++ b/sys/isofs/isofs_node.c @@ -30,14 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)isofs_inode.c - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00151 - * -------------------- ----- ---------------------- - * - * 23 Apr 93 Jagane D Sundar support nfs exported isofs + * from: @(#)isofs_inode.c + * $Id: isofs_node.c,v 1.3 1993/07/27 18:38:16 nate Exp $ */ #include "param.h" @@ -52,6 +46,7 @@ #include "iso.h" #include "isofs_node.h" +#include "iso_rrip.h" #define INOHSZ 512 #if ((INOHSZ&(INOHSZ-1)) == 0) @@ -107,7 +102,7 @@ iso_iget(xp, ino, ipp, isodir) struct buf *bp; struct dinode *dp; union iso_ihead *ih; - int i, error; + int i, error, result; struct iso_mnt *imp; ih = &iso_ihead[INOHASH(dev, ino)]; @@ -139,7 +134,6 @@ loop: ip->i_flag = 0; ip->i_devvp = 0; ip->i_diroff = 0; - /* The following two are req for NFS FH. -Jagane D Sundar.- */ ip->iso_parent = xp->i_diroff; /* Parent directory's */ ip->iso_parent_ext = xp->iso_extent; ip->i_lockf = 0; @@ -158,22 +152,43 @@ loop: ip->iso_extlen = isonum_711 (isodir->ext_attr_length); ip->iso_extent = isonum_733 (isodir->extent); ip->i_size = isonum_733 (isodir->size); - bcopy (isodir->date, ip->iso_time, sizeof ip->iso_time); ip->iso_flags = isonum_711 (isodir->flags); ip->iso_unit_size = isonum_711 (isodir->file_unit_size); ip->iso_interleave_gap = isonum_711 (isodir->interleave); ip->iso_volume_seq = isonum_723 (isodir->volume_sequence_number); ip->iso_namelen = isonum_711 (isodir->name_len); + imp = VFSTOISOFS (mntp); + vp = ITOV(ip); + /* + * Setup time stamp, attribute , if CL or PL, set loc but not yet.. + */ + switch ( imp->iso_ftype ) { + case ISO_FTYPE_9660: + isofs_rrip_defattr ( isodir, &(ip->inode) ); + isofs_rrip_deftstamp( isodir, &(ip->inode) ); + goto FlameOff; + break; + case ISO_FTYPE_RRIP: + result = isofs_rrip_analyze( isodir, &(ip->inode) ); + break; + default: + printf("unknown iso_ftype.. %d\n", imp->iso_ftype ); + break; + } /* * Initialize the associated vnode */ - vp = ITOV(ip); - - if (ip->iso_flags & 2) - vp->v_type = VDIR; - else - vp->v_type = VREG; + if ( result & ISO_SUSP_SLINK ) { + vp->v_type = VLNK; /* Symbolic Link */ + } else { +FlameOff: + if (ip->iso_flags & 2) { + vp->v_type = VDIR; + } else { + vp->v_type = VREG; + } + } imp = VFSTOISOFS (mntp); @@ -221,8 +236,16 @@ isofs_inactive(vp, p) * If we are done with the inode, reclaim it * so that it can be reused immediately. */ - if (vp->v_usecount == 0 /* && ip->i_mode == 0 */) + + /* + * Purge symlink entries since they cause problems + * when cached. Leave other entries alone since flushing + * them every time is a major performance hit. + */ + if (vp->v_usecount == 0 && vp->v_type == VLNK) { +/* printf("Flushing symlink entry\n");*/ vgone(vp); + } return (error); } diff --git a/sys/isofs/isofs_node.h b/sys/isofs/isofs_node.h index e20489d6dc..fc5fdca4e0 100644 --- a/sys/isofs/isofs_node.h +++ b/sys/isofs/isofs_node.h @@ -1,11 +1,19 @@ /* - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00151 - * -------------------- ----- ---------------------- - * - * 23 Apr 93 Jagane D Sundar support nfs exported isofs + * $Id: isofs_node.h,v 1.5 1993/07/19 13:40:05 cgd Exp $ */ + + +typedef struct { + unsigned iso_cln; /* Child link */ + unsigned iso_pln; /* Parents link */ + struct timeval iso_atime; /* time of last access */ + struct timeval iso_mtime; /* time of last modification */ + struct timeval iso_ctime; /* time file changed */ + u_short iso_mode; /* files access mode and type */ + uid_t iso_uid; /* owner user id */ + gid_t iso_gid; /* owner group id */ +} ISO_RRIP_INODE; + struct iso_node { struct iso_node *i_chain[2]; /* hash chain, MUST be first */ struct vnode *i_vnode; /* vnode associated with this inode */ @@ -25,15 +33,14 @@ struct iso_node { int iso_extlen; int iso_extent; int i_size; - unsigned char iso_time[7]; int iso_flags; int iso_unit_size; int iso_interleave_gap; int iso_volume_seq; - int iso_namelen; - /* The following are reqd for NFS FH. -Jagane D Sundar- */ - int iso_parent; - int iso_parent_ext; + int iso_namelen; /* ISO9660/RRIP name len */ + int iso_parent; /* byte offset in beginning of dir record */ + int iso_parent_ext; /* block number of dir record */ + ISO_RRIP_INODE inode; }; #define i_forw i_chain[0] @@ -50,8 +57,6 @@ struct iso_node { #define ISO_ILOCK(ip) iso_ilock(ip) #define ISO_IUNLOCK(ip) iso_iunlock(ip) -#define VT_ISOFS (VT_MFS+1) - /* * Prototypes for ISOFS vnode operations */ diff --git a/sys/isofs/isofs_rrip.c b/sys/isofs/isofs_rrip.c new file mode 100644 index 0000000000..f59f898832 --- /dev/null +++ b/sys/isofs/isofs_rrip.c @@ -0,0 +1,660 @@ +/* + * Copyright (c) 1993 Atsushi Murai (amurai@spec.co.jp) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Atsushi Murai(amurai@spec.co.jp)``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: isofs_rrip.c,v 1.2 1993/07/27 10:52:36 davidg Exp $ + */ + +#include "param.h" +#include "systm.h" +#include "namei.h" +#include "buf.h" +#include "file.h" +#include "vnode.h" +#include "mount.h" +#include "kernel.h" + +#include "sys/time.h" + +#include "iso.h" +#include "isofs_node.h" +#include "isofs_rrip.h" +#include "iso_rrip.h" + +/* + * POSIX file attribute + */ +static int isofs_rrip_attr( p, ana ) +ISO_RRIP_ATTR *p; +ISO_RRIP_ANALYZE *ana; +{ + ana->inode.iso_mode = isonum_731(p->mode_l); + ana->inode.iso_uid = (uid_t)isonum_731(p->uid_l); + ana->inode.iso_gid = (gid_t)isonum_731(p->gid_l); +/* ana->inode.iso_links = isonum_731(p->links_l); */ + return; +} + +int isofs_rrip_defattr( isodir, ana ) +struct iso_directory_record *isodir; +ISO_RRIP_ANALYZE *ana; +{ + ana->inode.iso_mode = (VREAD|VEXEC|(VREAD|VEXEC)>>3|(VREAD|VEXEC)>>6); + ana->inode.iso_uid = (uid_t)0; + ana->inode.iso_gid = (gid_t)0; +} + +/* + * POSIX device modes + */ +static int isofs_rrip_device( p, ana ) +ISO_RRIP_DEVICE *p; +ISO_RRIP_ANALYZE *ana; +{ + char buf[3]; + + buf[0] = p->h.type[0]; + buf[1] = p->h.type[1]; + buf[2] = 0x00; + + printf("isofs:%s[%d] high=0x%08x, low=0x%08x\n", + buf, + isonum_711(p->h.length), + isonum_731(p->dev_t_high_l), + isonum_731(p->dev_t_low_l) + ); + return; +} + +/* + * Symbolic Links + */ +static int isofs_rrip_slink( p, ana ) +ISO_RRIP_SLINK *p; +ISO_RRIP_ANALYZE *ana; +{ + return; +} + +/* + * Alternate name + */ +static int isofs_rrip_altname( p, ana ) +ISO_RRIP_ALTNAME *p; +ISO_RRIP_ANALYZE *ana; +{ + return; +} + +/* + * Child Link + */ +static int isofs_rrip_clink( p, ana ) +ISO_RRIP_CLINK *p; +ISO_RRIP_ANALYZE *ana; +{ + char buf[3]; + buf[0] = p->h.type[0]; + buf[1] = p->h.type[1]; + buf[2] = 0x00; + printf("isofs:%s[%d] loc=%d\n", + buf, + isonum_711(p->h.length), + isonum_733(p->dir_loc) + ); + ana->inode.iso_cln = isonum_733(p->dir_loc); + return; +} + +/* + * Parent Link + */ +static int isofs_rrip_plink( p, ana ) +ISO_RRIP_PLINK *p; +ISO_RRIP_ANALYZE *ana; +{ + + char buf[3]; + buf[0] = p->h.type[0]; + buf[1] = p->h.type[1]; + buf[2] = 0x00; + printf("isofs:%s[%d] loc=%d\n", + buf, + isonum_711(p->h.length), + isonum_733(p->dir_loc) + ); + ana->inode.iso_pln = isonum_733(p->dir_loc); + return; +} + +/* + * Relocated directory + */ +static int isofs_rrip_reldir( p, ana ) +ISO_RRIP_RELDIR *p; +ISO_RRIP_ANALYZE *ana; +{ + char buf[3]; + + buf[0] = p->h.type[0]; + buf[1] = p->h.type[1]; + buf[2] = 0x00; + + printf("isofs:%s[%d]\n",buf, isonum_711(p->h.length) ); + return; +} + +/* + * Time stamp + */ +static void isofs_rrip_tstamp_conv7(pi, pu) +char *pi; +struct timeval *pu; +{ + int i; + int crtime,days; + int year,month,day,hour,minute,second,tz; + + year = pi[0] - 70; + month = pi[1]; + day = pi[2]; + hour = pi[3]; + minute = pi[4]; + second = pi[5]; + tz = pi[6]; + + if (year < 0) { + crtime = 0; + } else { + int monlen[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; + days = year * 365; + if (year > 2) + days += (year+2) / 4; + for (i = 1; i < month; i++) + days += monlen[i-1]; + if (((year+2) % 4) == 0 && month > 2) + days++; + days += day - 1; + crtime = ((((days * 24) + hour) * 60 + minute) * 60) + + second; + + /* sign extend */ + if (tz & 0x80) + tz |= (-1 << 8); + + /* timezone offset is unreliable on some disks */ + if (-48 <= tz && tz <= 52) + crtime += tz * 15 * 60; + } + pu->tv_sec = crtime; + pu->tv_usec = 0; +} + + +static unsigned isofs_chars2ui( begin, end ) +unsigned char *begin; +unsigned char *end; +{ + unsigned rc=0; + int len; + int wlen; + static int pow[]={ 1, 10, 100, 1000 }; + + len = end - begin; + wlen= len; + for (; len >= 0; len -- ) { + rc += ( *(begin+len) * pow[wlen - len] ); + } + return( rc ); +} + +static void isofs_rrip_tstamp_conv17(pi, pu) +unsigned char *pi; +struct timeval *pu; +{ + unsigned char buf[7]; + + /* year:"0001"-"9999" -> -1900 */ + buf[0] = (unsigned char)(isofs_chars2ui( &pi[0], &pi[3]) - 1900 ); + + /* month: " 1"-"12" -> 1 - 12 */ + buf[1] = (unsigned char)isofs_chars2ui( &pi[4], &pi[5]); + + /* day: " 1"-"31" -> 1 - 31 */ + buf[2] = isofs_chars2ui( &pi[6], &pi[7]); + + /* hour: " 0"-"23" -> 0 - 23 */ + buf[3] = isofs_chars2ui( &pi[8], &pi[9]); + + /* minute:" 0"-"59" -> 0 - 59 */ + buf[4] = isofs_chars2ui( &pi[10], &pi[11] ); + + /* second:" 0"-"59" -> 0 - 59 */ + buf[5] = isofs_chars2ui( &pi[12], &pi[13] ); + + /* difference of GMT */ + buf[6] = pi[16]; + + isofs_rrip_tstamp_conv7(buf, pu); +} + +static int isofs_rrip_tstamp( p, ana ) +ISO_RRIP_TSTAMP *p; +ISO_RRIP_ANALYZE *ana; +{ + unsigned char *ptime; + + ptime = p->time; + + /* Check a format of time stamp (7bytes/17bytes) */ + if ( !(*p->flags & ISO_SUSP_TSTAMP_FORM17 ) ) { + isofs_rrip_tstamp_conv7(ptime, &ana->inode.iso_ctime ); + + if ( *p->flags & ISO_SUSP_TSTAMP_MODIFY ) + isofs_rrip_tstamp_conv7(ptime+7, &ana->inode.iso_mtime ); + else + ana->inode.iso_mtime = ana->inode.iso_ctime; + + if ( *p->flags & ISO_SUSP_TSTAMP_ACCESS ) + isofs_rrip_tstamp_conv7(ptime+14, &ana->inode.iso_atime ); + else + ana->inode.iso_atime = ana->inode.iso_ctime; + } else { + isofs_rrip_tstamp_conv17(ptime, &ana->inode.iso_ctime ); + + if ( *p->flags & ISO_SUSP_TSTAMP_MODIFY ) + isofs_rrip_tstamp_conv17(ptime+17, &ana->inode.iso_mtime ); + else + ana->inode.iso_mtime = ana->inode.iso_ctime; + + if ( *p->flags & ISO_SUSP_TSTAMP_ACCESS ) + isofs_rrip_tstamp_conv17(ptime+34, &ana->inode.iso_atime ); + else + ana->inode.iso_atime = ana->inode.iso_ctime; + } + return; +} + +int isofs_rrip_deftstamp( isodir, ana ) +struct iso_directory_record *isodir; +ISO_RRIP_ANALYZE *ana; +{ + isofs_rrip_tstamp_conv7(isodir->date, &ana->inode.iso_ctime ); + ana->inode.iso_atime = ana->inode.iso_ctime; + ana->inode.iso_mtime = ana->inode.iso_ctime; +} + + +/* + * Flag indicating + * Nothing to do.... + */ +static int isofs_rrip_idflag( p, ana ) +ISO_RRIP_IDFLAG *p; +ISO_RRIP_ANALYZE *ana; +{ + char buf[3]; + + buf[0] = p->h.type[0]; + buf[1] = p->h.type[1]; + buf[2] = 0x00; + + printf("isofs:%s[%d] idflag=0x%x\n", + buf, + isonum_711(p->h.length), + p->flags ); + return; +} + +/* + * Extension reference + * Nothing to do.... + */ +static int isofs_rrip_exflag( p, ana ) +ISO_RRIP_EXFLAG *p; +ISO_RRIP_ANALYZE *ana; +{ + char buf[3]; + + buf[0] = p->h.type[0]; + buf[1] = p->h.type[1]; + buf[2] = 0x00; + + printf("isofs:%s[%d] exflag=0x%x", + buf, + isonum_711(p->h.length), + p->flags ); + return; +} + +/* + * Unknown ... + * Nothing to do.... + */ +static int isofs_rrip_unknown( p, ana ) +ISO_RRIP_EXFLAG *p; +ISO_RRIP_ANALYZE *ana; +{ + return; +} + +typedef struct { + char type[2]; + int (*func)(); + int (*func2)(); + int result; +} RRIP_TABLE; + +static RRIP_TABLE rrip_table [] = { + { 'P', 'X', isofs_rrip_attr, isofs_rrip_defattr, ISO_SUSP_ATTR }, + { 'T', 'F', isofs_rrip_tstamp, isofs_rrip_deftstamp, ISO_SUSP_TSTAMP }, + { 'N', 'M', isofs_rrip_altname,0, ISO_SUSP_ALTNAME }, + { 'C', 'L', isofs_rrip_clink, 0, ISO_SUSP_CLINK }, + { 'P', 'L', isofs_rrip_plink, 0, ISO_SUSP_PLINK }, + { 'S', 'L', isofs_rrip_slink, 0, ISO_SUSP_SLINK }, + { 'R', 'E', isofs_rrip_reldir, 0, ISO_SUSP_RELDIR }, + { 'P', 'N', isofs_rrip_device, 0, ISO_SUSP_DEVICE }, + { 'R', 'R', isofs_rrip_idflag, 0, ISO_SUSP_IDFLAG }, + { 'E', 'R', isofs_rrip_exflag, 0, ISO_SUSP_EXFLAG }, + { 'S', 'P', isofs_rrip_unknown,0, ISO_SUSP_UNKNOWN }, + { 'C', 'E', isofs_rrip_unknown,0, ISO_SUSP_UNKNOWN } +}; + +int isofs_rrip_analyze ( isodir, analyze ) +struct iso_directory_record *isodir; +ISO_RRIP_ANALYZE *analyze; +{ + register RRIP_TABLE *ptable; + register ISO_SUSP_HEADER *phead; + register ISO_SUSP_HEADER *pend; + int found; + int i; + char *pwhead; + int result; + + /* + * Note: If name length is odd, + * it will be padding 1 byte after the name + */ + pwhead = isodir->name + isonum_711(isodir->name_len); + if ( !(isonum_711(isodir->name_len) & 0x0001) ) + pwhead ++; + phead = (ISO_SUSP_HEADER *)pwhead; + pend = (ISO_SUSP_HEADER *)( (char *)isodir + isonum_711(isodir->length) ); + + result = 0; + if ( pend == phead ) { + goto setdefault; + } + /* + * Note: "pend" should be more than one SUSP header + */ + while ( pend >= phead + 1) { + found = 0; + for ( ptable=&rrip_table[0];ptable < &rrip_table[sizeof(rrip_table)/sizeof(RRIP_TABLE)]; ptable ++) { + if ( bcmp( phead->type, ptable->type, 2 ) == 0 ) { + found = 1; + ptable->func( phead, analyze ); + result |= ptable->result; + break; + } + } + if ( found == 0 ) { + printf("isofs: name '"); + for ( i =0; i < isonum_711(isodir->name_len) ;i++) { + printf("%c", *(isodir->name + i) ); + } + printf("'"); + printf(" - type %c%c [%08x/%08x]...not found\n", + phead->type[0], phead->type[1], phead, pend ); + isofs_hexdump( phead, (int)( (char *)pend - (char *)phead ) ); + break; + } + + /* + * move to next SUSP + */ + phead = (ISO_SUSP_HEADER *) ((unsigned)isonum_711(phead->length) + (char *)phead); + } + +setdefault: + /* + * If we don't find the Basic SUSP stuffs, just set default value + * ( attribute/time stamp ) + */ + for ( ptable=&rrip_table[0];ptable < &rrip_table[2]; ptable ++) { + if ( ptable->func2 != 0 && !(ptable->result & result) ) { + ptable->func2( isodir, analyze ); + } + } + return ( result ); +} + +/* + * Get Alternate Name from 'AL' record + * If either no AL record nor 0 lenght, + * it will be return the translated ISO9660 name, + */ +int isofs_rrip_getname( isodir, outbuf, outlen ) +struct iso_directory_record *isodir; +char *outbuf; +int *outlen; +{ + ISO_SUSP_HEADER *phead, *pend; + ISO_RRIP_ALTNAME *p; + char *pwhead; + int found; + + /* + * Note: If name length is odd, + * it will be padding 1 byte after the name + */ + pwhead = isodir->name + isonum_711(isodir->name_len); + if ( !(isonum_711(isodir->name_len) & 0x0001) ) + pwhead ++; + phead = (ISO_SUSP_HEADER *)pwhead; + pend = (ISO_SUSP_HEADER *)( (char *)isodir + isonum_711(isodir->length) ); + + found = 0; + if ( pend != phead ) { + while ( pend >= phead + 1) { + if ( bcmp( phead->type, "NM", 2 ) == 0 ) { + found = 1; + break; + } + phead = (ISO_SUSP_HEADER *) ((unsigned)isonum_711(phead->length) + (char *)phead); + } + } + if ( found == 1 ) { + p = (ISO_RRIP_ALTNAME *)phead; + *outlen = isonum_711( p->h.length ) - sizeof( ISO_RRIP_ALTNAME ); + bcopy( (char *)( &p->flags + 1 ), outbuf, *outlen ); + } else { + isofntrans(isodir->name, isonum_711(isodir->name_len), outbuf, outlen ); + if ( *outlen == 1) { + switch ( outbuf[0] ) { + case 0: + outbuf[0] = '.'; + break; + case 1: + outbuf[0] = '.'; + outbuf[1] = '.'; + *outlen = 2; + } + } + } + return( found ); +} + +/* + * Get Symbolic Name from 'SL' record + * + * Note: isodir should contains SL record! + */ +int isofs_rrip_getsymname( vp, isodir, outbuf, outlen ) +struct vnode *vp; +struct iso_directory_record *isodir; +char *outbuf; +int *outlen; +{ + register ISO_RRIP_SLINK_COMPONENT *pcomp; + register ISO_SUSP_HEADER *phead, *pend; + register ISO_RRIP_SLINK_COMPONENT *pcompe; + ISO_RRIP_SLINK *p; + char *pwhead; + int found; + int slash; + int wlen; + + /* + * Note: If name length is odd, + * it will be padding 1 byte after the name + */ + pwhead = isodir->name + isonum_711(isodir->name_len); + if ( !(isonum_711(isodir->name_len) & 0x0001) ) + pwhead ++; + phead = (ISO_SUSP_HEADER *)pwhead; + pend = (ISO_SUSP_HEADER *)( (char *)isodir + isonum_711(isodir->length) ); + + found = 0; + if ( pend != phead ) { + while ( pend >= phead + 1) { + if ( bcmp( phead->type, "SL", 2 ) == 0 ) { + found = 1; + break; + } + phead = (ISO_SUSP_HEADER *) ((unsigned)isonum_711(phead->length) + (char *)phead); + } + } + if ( found == 0 ) { + *outlen = 0; + return( found ); + } + + p = (ISO_RRIP_SLINK *)phead; + pcomp = (ISO_RRIP_SLINK_COMPONENT *)p->component; + pcompe = (ISO_RRIP_SLINK_COMPONENT *)((char *)p + isonum_711(p->h.length)); + + /* + * Gathering a Symbolic name from each component with path + */ + *outlen = 0; + slash = 0; + while ( pcomp < pcompe ) { + + /* Inserting Current */ + if ( pcomp->cflag[0] & ISO_SUSP_CFLAG_CURRENT ) { + bcopy("./", outbuf+*outlen, 2); + *outlen += 2; + slash = 0; + } + + /* Inserting Parent */ + if ( pcomp->cflag[0] & ISO_SUSP_CFLAG_PARENT ) { + bcopy("../", outbuf+*outlen, 3); + *outlen += 3; + slash = 0; + } + + /* Inserting slash for ROOT */ + if ( pcomp->cflag[0] & ISO_SUSP_CFLAG_ROOT ) { + bcopy("/", outbuf+*outlen, 1); + *outlen += 1; + slash = 0; + } + + /* Inserting a mount point i.e. "/cdrom" */ + if ( pcomp->cflag[0] & ISO_SUSP_CFLAG_VOLROOT ) { + wlen = strlen(vp->v_mount->mnt_stat.f_mntonname); + bcopy(vp->v_mount->mnt_stat.f_mntonname,outbuf+*outlen, wlen); + *outlen += wlen; + slash = 1; + } + + /* Inserting hostname i.e. "tama:" */ + if ( pcomp->cflag[0] & ISO_SUSP_CFLAG_HOST ) { + bcopy(hostname, outbuf+*outlen, hostnamelen); + *(outbuf+hostnamelen) = ':'; + *outlen += (hostnamelen + 1); + slash = 0; /* Uuum Should we insert a slash ? */ + } + + /* Inserting slash for next component */ + if ( slash == 1 ) { + outbuf[*outlen] = '/'; + *outlen += 1; + slash = 0; + } + + /* Inserting component */ + wlen = isonum_711(pcomp->clen); + if ( wlen != 0 ) { + bcopy( pcomp->name, outbuf + *outlen, wlen ); + *outlen += wlen; + slash = 1; + } + + /* + * Move to next component... + */ + pcomp = (ISO_RRIP_SLINK_COMPONENT *)((char *)pcomp + + sizeof(ISO_RRIP_SLINK_COMPONENT) - 1 + + isonum_711(pcomp->clen)); + } + return( found ); +} +/* Hexdump routine for debug*/ +int isofs_hexdump( p, size ) + unsigned char *p; + int size; +{ + int i,j,k; + unsigned char *wp; + + for ( i = 0; i < size; i += 16 ) { + printf("isofs: "); + wp = p; + k = ( (size - i) > 16 ? 16 : size - i ); + for ( j =0; j < k; j ++ ){ + printf("%02x ", *p ); + p++; + } + printf(" : "); + p = wp; + for ( j =0; j < k; j ++ ){ + if ( (*p > 0x20) && (*p < 0x7f) ) + printf("%c", *p ); + else + printf(" "); + p++; + } + printf("\n"); + } + printf("\n"); +} diff --git a/sys/isofs/isofs_rrip.h b/sys/isofs/isofs_rrip.h new file mode 100644 index 0000000000..d4be228cf6 --- /dev/null +++ b/sys/isofs/isofs_rrip.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 1993 Atsushi Murai (amurai@spec.co.jp) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Atsushi Murai(amurai@spec.co.jp)``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)isofs_rrip.h + * $Id$ + */ + +typedef struct { + char type [ISODCL ( 0, 1)]; + unsigned char length [ISODCL ( 2, 2)]; /* 711 */ + unsigned char version [ISODCL ( 3, 3)]; +} ISO_SUSP_HEADER; + +typedef struct { + ISO_SUSP_HEADER h; + char mode_l [ISODCL ( 4, 7)]; /* 731 */ + char mode_m [ISODCL ( 8, 11)]; /* 732 */ + char links_l [ISODCL ( 12, 15)]; /* 731 */ + char links_m [ISODCL ( 16, 19)]; /* 732 */ + char uid_l [ISODCL ( 20, 23)]; /* 731 */ + char uid_m [ISODCL ( 24, 27)]; /* 732 */ + char gid_l [ISODCL ( 28, 31)]; /* 731 */ + char gid_m [ISODCL ( 32, 35)]; /* 732 */ +} ISO_RRIP_ATTR; + +typedef struct { + ISO_SUSP_HEADER h; + char dev_t_high_l [ISODCL ( 4, 7)]; /* 731 */ + char dev_t_high_m [ISODCL ( 8, 11)]; /* 732 */ + char dev_t_low_l [ISODCL ( 12, 15)]; /* 731 */ + char dev_t_low_m [ISODCL ( 16, 19)]; /* 732 */ +} ISO_RRIP_DEVICE; + +#define ISO_SUSP_CFLAG_CONTINUE 0x01 +#define ISO_SUSP_CFLAG_CURRENT 0x02 +#define ISO_SUSP_CFLAG_PARENT 0x04 +#define ISO_SUSP_CFLAG_ROOT 0x08 +#define ISO_SUSP_CFLAG_VOLROOT 0x10 +#define ISO_SUSP_CFLAG_HOST 0x20 + +typedef struct { + u_char cflag [ISODCL ( 1, 1)]; + u_char clen [ISODCL ( 2, 2)]; + u_char name [ISODCL ( 3, 3)]; +} ISO_RRIP_SLINK_COMPONENT; + +typedef struct { + ISO_SUSP_HEADER h; + u_char flags [ISODCL ( 4, 4)]; + u_char component [ISODCL ( 5, 5)]; +} ISO_RRIP_SLINK; + +typedef struct { + ISO_SUSP_HEADER h; + char flags [ISODCL ( 4, 4)]; +} ISO_RRIP_ALTNAME; + +typedef struct { + ISO_SUSP_HEADER h; + char dir_loc [ISODCL ( 4, 11)]; /* 733 */ +} ISO_RRIP_CLINK; + +typedef struct { + ISO_SUSP_HEADER h; + char dir_loc [ISODCL ( 4, 11)]; /* 733 */ +} ISO_RRIP_PLINK; + +typedef struct { + ISO_SUSP_HEADER h; +} ISO_RRIP_RELDIR; + +#define ISO_SUSP_TSTAMP_FORM17 0x80 +#define ISO_SUSP_TSTAMP_FORM7 0x00 +#define ISO_SUSP_TSTAMP_CREAT 0x01 +#define ISO_SUSP_TSTAMP_MODIFY 0x02 +#define ISO_SUSP_TSTAMP_ACCESS 0x04 +#define ISO_SUSP_TSTAMP_ATTR 0x08 +#define ISO_SUSP_TSTAMP_BACKUP 0x10 +#define ISO_SUSP_TSTAMP_EXPIRE 0x20 +#define ISO_SUSP_TSTAMP_EFFECT 0x40 + +typedef struct { + ISO_SUSP_HEADER h; + unsigned char flags [ISODCL ( 4, 4)]; + unsigned char time [ISODCL ( 5, 5)]; +} ISO_RRIP_TSTAMP; + +typedef struct { + ISO_SUSP_HEADER h; + unsigned char flags [ISODCL ( 4, 4)]; +} ISO_RRIP_IDFLAG; + +typedef struct { + ISO_SUSP_HEADER h; + unsigned char flags [ISODCL ( 4, 4)]; +} ISO_RRIP_EXFLAG; + diff --git a/sys/isofs/isofs_util.c b/sys/isofs/isofs_util.c index 20f747bb00..0014cf59ba 100644 --- a/sys/isofs/isofs_util.c +++ b/sys/isofs/isofs_util.c @@ -1,3 +1,7 @@ +/* + * $Id: isofs_util.c,v 1.3 1993/07/19 13:40:08 cgd Exp $ + */ + int isonum_711 (p) char *p; @@ -46,7 +50,7 @@ char *p; int isonum_731 (p) -char *p; +unsigned char *p; { return ((p[0] & 0xff) | ((p[1] & 0xff) << 8) @@ -56,7 +60,7 @@ char *p; int isonum_732 (p) -char *p; +unsigned char *p; { return (((p[0] & 0xff) << 24) | ((p[1] & 0xff) << 16) @@ -66,7 +70,7 @@ char *p; int isonum_733 (p) -char *p; +unsigned char *p; { int i; diff --git a/sys/isofs/isofs_vfsops.c b/sys/isofs/isofs_vfsops.c index bc55abb0c5..9c19cf40e6 100644 --- a/sys/isofs/isofs_vfsops.c +++ b/sys/isofs/isofs_vfsops.c @@ -1,11 +1,7 @@ /* - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00151 - * -------------------- ----- ---------------------- - * - * 23 Apr 93 Jagane D Sundar support nfs exported isofs + * $Id: isofs_vfsops.c,v 1.2 1993/07/20 03:27:36 jkh Exp $ */ + #include "param.h" #include "systm.h" #include "namei.h" @@ -174,6 +170,16 @@ isofs_mount(mp, path, data, ndp, p) return (error); } imp = VFSTOISOFS(mp); + + /* Check the Rock Ridge Extention support */ + if ( args.exflags & MNT_NORRIP ) { + imp->iso_ftype = ISO_FTYPE_9660; + mp->mnt_flag |= MNT_NORRIP; + } else { + imp->iso_ftype = ISO_FTYPE_RRIP; + mp->mnt_flag &= ~MNT_NORRIP; + } + (void) copyinstr(path, imp->im_fsmnt, sizeof(imp->im_fsmnt)-1, &size); bzero(imp->im_fsmnt + size, sizeof(imp->im_fsmnt) - size); bcopy((caddr_t)imp->im_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname, @@ -273,7 +279,7 @@ iso_mountfs(devvp, mp, p) rootp = (struct iso_directory_record *)pri->root_directory_record; - isomp = (struct iso_mnt *)malloc(sizeof *isomp,M_UFSMNT,M_WAITOK); + isomp = (struct iso_mnt *)malloc(sizeof *isomp,M_ISOFSMNT,M_WAITOK); isomp->logical_block_size = logical_block_size; isomp->volume_space_size = isonum_733 (pri->volume_space_size); bcopy (rootp, isomp->root, sizeof isomp->root); @@ -311,7 +317,7 @@ out: if (needclose) (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p); if (isomp) { - free((caddr_t)isomp, M_UFSMNT); + free((caddr_t)isomp, M_ISOFSMNT); mp->mnt_data = (qaddr_t)0; } return (error); @@ -359,7 +365,7 @@ isofs_unmount(mp, mntflags, p) error = VOP_CLOSE(isomp->im_devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p); vrele(isomp->im_devvp); - free((caddr_t)isomp, M_UFSMNT); + free((caddr_t)isomp, M_ISOFSMNT); mp->mnt_data = (qaddr_t)0; mp->mnt_flag &= ~MNT_LOCAL; return (error); @@ -406,7 +412,6 @@ isofs_root(mp, vpp) ip->i_dev = imp->im_dev; ip->i_diroff = 0; ip->iso_extent = imp->root_extent; - ip->i_diroff = 0; error = iso_iget(ip, imp->root_extent, &nip, (struct iso_directory_record *) imp->root); if (error) @@ -455,9 +460,11 @@ isofs_sync(mp, waitfor) /* * File handle to vnode * - * Less complicated than ufs file systems 'cos of read only. - * - * -Jagane D Sundar- + * Have to be really careful about stale file handles: + * - check that the inode number is in range + * - call iget() to get the locked inode + * - check for an unallocated inode (i_mode == 0) + * - check that the generation number matches */ @@ -495,8 +502,8 @@ isofs_fhtovp(mp, fhp, vpp) lbn = ifhp->ifid_lbn; off = ifhp->ifid_offset; - ifhp->ifid_lbn += (ifhp->ifid_offset >> 12); - ifhp->ifid_offset &= 0x7fff; + ifhp->ifid_lbn += (ifhp->ifid_offset >> 11); + ifhp->ifid_offset &= 0x7ff; if (ifhp->ifid_lbn >= imp->volume_space_size) return (EINVAL); diff --git a/sys/isofs/isofs_vnops.c b/sys/isofs/isofs_vnops.c index 742f25ccfa..8e40b0587e 100644 --- a/sys/isofs/isofs_vnops.c +++ b/sys/isofs/isofs_vnops.c @@ -1,12 +1,5 @@ /* - * - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00040 - * -------------------- ----- ---------------------- - * - * 10 Aug 92 Scott Burris Fixed "delete from CD-ROM" bug + * $Id: isofs_vnops.c,v 1.5 1993/07/19 13:40:10 cgd Exp $ */ #include "param.h" #include "systm.h" @@ -27,6 +20,7 @@ #include "iso.h" #include "isofs_node.h" +#include "iso_rrip.h" /* * Open called. @@ -80,61 +74,25 @@ isofs_getattr(vp, vap, cred, p) struct proc *p; { register struct iso_node *ip = VTOI(vp); - int year, month, day, hour ,minute, second, tz; - int crtime, days; int i; - year = ip->iso_time[0] - 70; - month = ip->iso_time[1]; - day = ip->iso_time[2]; - hour = ip->iso_time[3]; - minute = ip->iso_time[4]; - second = ip->iso_time[5]; - tz = ip->iso_time[6]; - - if (year < 0) { - crtime = 0; - } else { - int monlen[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; - days = year * 365; - if (year > 2) - days += (year+2) / 4; - for (i = 1; i < month; i++) - days += monlen[i-1]; - if (((year+2) % 4) == 0 && month > 2) - days++; - days += day - 1; - crtime = ((((days * 24) + hour) * 60 + minute) * 60) - + second; - - /* sign extend */ - if (tz & 0x80) - tz |= (-1 << 8); - - /* timezone offset is unreliable on some disks */ - if (-48 <= tz && tz <= 52) - crtime += tz * 15 * 60; - } - vap->va_fsid = ip->i_dev; vap->va_fileid = ip->i_number; - vap->va_mode = VREAD|VEXEC; - vap->va_mode |= (vap->va_mode >> 3) | (vap->va_mode >> 6); if (vp->v_type == VDIR) vap->va_nlink = 2; else vap->va_nlink = 1; - vap->va_uid = 0; - vap->va_gid = 0; + + vap->va_mode = ip->inode.iso_mode; + vap->va_uid = ip->inode.iso_uid; + vap->va_gid = ip->inode.iso_gid; + vap->va_atime= ip->inode.iso_atime; + vap->va_mtime= ip->inode.iso_mtime; + vap->va_ctime= ip->inode.iso_ctime; + vap->va_rdev = 0; vap->va_size = ip->i_size; vap->va_size_rsv = 0; - vap->va_atime.tv_sec = crtime; - vap->va_atime.tv_usec = 0; - vap->va_mtime.tv_sec = crtime; - vap->va_mtime.tv_usec = 0; - vap->va_ctime.tv_sec = crtime; - vap->va_ctime.tv_usec = 0; vap->va_flags = 0; vap->va_gen = 1; vap->va_blocksize = ip->i_mnt->logical_block_size; @@ -284,6 +242,7 @@ isofs_readdir(vp, uio, cred, eofflagp) struct buf *bp = NULL; int i; int end_flag = 0; + ISO_RRIP_ANALYZE ana; ip = VTOI (vp); imp = ip->i_mnt; @@ -344,20 +303,33 @@ isofs_readdir(vp, uio, cred, eofflagp) /* illegal entry, stop */ break; - /*bcopy (ep->name, dirent.d_name, dirent.d_namlen);*/ - isofntrans(ep->name, dirent.d_namlen, - dirent.d_name, &dirent.d_namlen); - if (dirent.d_namlen == 1) { - switch (dirent.d_name[0]) { + /* + * + */ + switch (ep->name[0]) { case 0: dirent.d_name[0] = '.'; + dirent.d_namlen = 1; break; case 1: dirent.d_name[0] = '.'; dirent.d_name[1] = '.'; dirent.d_namlen = 2; - } + break; + default: + switch ( imp->iso_ftype ) { + case ISO_FTYPE_RRIP: + isofs_rrip_getname( ep, dirent.d_name, &dirent.d_namlen ); + break; + case ISO_FTYPE_9660: + isofntrans(ep->name, dirent.d_namlen, dirent.d_name, &dirent.d_namlen); + break; + default: + break; + } + break; } + dirent.d_name[dirent.d_namlen] = 0; dirent.d_reclen = DIRSIZ (&dirent); @@ -384,6 +356,91 @@ isofs_readdir(vp, uio, cred, eofflagp) return (error); } +/* + * Return target name of a symbolic link + */ +typedef struct iso_directory_record ISODIR; +typedef struct iso_node ISONODE; +typedef struct iso_mnt ISOMNT; +int isofs_readlink(vp, uio, cred) +struct vnode *vp; +struct uio *uio; +struct ucred *cred; +{ + ISONODE *ip; + ISODIR *dirp; + ISOMNT *imp; + struct buf *bp; + int symlen; + int error; + char symname[NAME_MAX]; + + ip = VTOI( vp ); + imp = ip->i_mnt; + /* + * Get parents directory record block that this inode included. + */ + error = bread( imp->im_devvp, + (daddr_t)(( ip->iso_parent_ext + (ip->iso_parent >> 11 ) ) + * imp->im_bsize / DEV_BSIZE ), + imp->im_bsize, + NOCRED, + &bp ); + if ( error ) { + return( EINVAL ); + } + + /* + * Setup the directory pointer for this inode + */ + + dirp = (ISODIR *)(bp->b_un.b_addr + ( ip->iso_parent & 0x7ff ) ); +#ifdef DEBUG + printf("lbn=%d[base=%d,off=%d,bsize=%d,DEV_BSIZE=%d], dirp= %08x, b_addr=%08x, offset=%08x(%08x)\n", + (daddr_t)(( ip->iso_parent_ext + (ip->iso_parent >> 12 ) ) * imp->im_bsize / DEV_BSIZE ), + ip->iso_parent_ext, + (ip->iso_parent >> 11 ), + imp->im_bsize, + DEV_BSIZE, + dirp, + bp->b_un.b_addr, + ip->iso_parent, + ip->iso_parent & 0x7ff ); +#endif + + /* + * Just make sure, we have a right one.... + * 1: Check not cross boundary on block + * 2: Check number of inode + */ + if ( (ip->iso_parent & 0x7ff) + isonum_711( dirp->length ) >= + imp->im_bsize ) { + brelse ( bp ); + return( EINVAL ); + } + if ( isonum_733(dirp->extent) != ip->i_number ) { + brelse ( bp ); + return( EINVAL ); + } + + /* + * Ok, we just gathering a Symbolick name in SL record. + */ + if ( isofs_rrip_getsymname( vp, dirp, symname, &symlen ) == 0 ) { + brelse ( bp ); + return( EINVAL ); + } + /* + * Don't forget before you leave from home ;-) + */ + brelse( bp ); + + /* + * return with the Symbolick name to caller's. + */ + return ( uiomove( symname, symlen, uio ) ); +} + /* * Ufs abort op, called after namei() when a CREATE/DELETE isn't actually * done. If a buffer has been saved in anticipation of a CREATE, delete it. @@ -472,7 +529,7 @@ isofs_strategy(bp) isofs_print(vp) struct vnode *vp; { - printf ("isoprint\n"); + printf ("tag VT_ISOFS, isofs vnode\n"); } extern int enodev (); @@ -503,7 +560,7 @@ struct vnodeops isofs_vnodeops = { (void *)enodev, /* rmdir */ (void *)enodev, /* symlink */ isofs_readdir, /* readdir */ - (void *)enodev, /* readlink */ + isofs_readlink, /* readlink */ isofs_abortop, /* abortop */ isofs_inactive, /* inactive */ isofs_reclaim, /* reclaim */ @@ -515,5 +572,3 @@ struct vnodeops isofs_vnodeops = { isofs_islocked, /* islocked */ (void *)enodev, /* advlock */ }; - - diff --git a/sys/kern/Makefile b/sys/kern/Makefile new file mode 100644 index 0000000000..a402a5451f --- /dev/null +++ b/sys/kern/Makefile @@ -0,0 +1,50 @@ +# from: @(#)Makefile 7.13 (Berkeley) 5/9/91 +# $Id$ + +# Makefile for kernel tags files, init_systent, etc. + +ARCH= i386 + +all: + @echo "make tags, make links or init_sysent.c only" + +init_sysent.c syscalls.c ../sys/syscall.h: makesyscalls.sh syscalls.master + -mv -f init_sysent.c init_sysent.c.bak + -mv -f syscalls.c syscalls.c.bak + -mv -f ../sys/syscall.h ../sys/syscall.h.bak + sh makesyscalls.sh syscalls.master + +# Kernel tags: +# We need to have links to tags files from the generic directories +# that are relative to the machine type, even via remote mounts; +# therefore we use symlinks to $SYSTAGS, which points at +# ${SYSDIR}/${MACHINE}/tags. + +SYSTAGS=/var/db/sys_tags +SYSDIR=/sys + +# Put the ../sys stuff near the end so that subroutine definitions win when +# there is a struct tag with the same name (eg., vmmeter). The real +# solution would probably be for ctags to generate "struct vmmeter" tags. + +COMM= ../net/*.[ch] ../netimp/*.[ch] ../netinet/*.[ch] ../netns/*.[ch] \ + ../netiso/*.[ch] ../netccitt/*.[ch] \ + ../kern/*.c ../ufs/*.[ch] ../nfs/*.[ch] ../vm/*.[ch] ../sys/*.h + +# Directories in which to place tags links (other than machine-dependent) +DGEN= kern sys net netimp netinet netns netccitt nfs ufs vm + +tags: FRC + -for i in ${ARCH}; do \ + cd ../$$i && make ${MFLAGS} tags; done + +links: ${SYSTAGS} + -for i in ${DGEN}; do \ + cd $$i && rm -f tags; ln -s ${SYSTAGS} tags; done + -for i in ${ARCH}; do \ + cd ../$$i && make ${MFLAGS} links; done + +${SYSTAGS}: + ln -s ${SYSDIR}/${MACHINE}/tags ${SYSTAGS} + +FRC: diff --git a/sys/kern/dead_vnops.c b/sys/kern/dead_vnops.c index e1430bcf09..17ab56567c 100644 --- a/sys/kern/dead_vnops.c +++ b/sys/kern/dead_vnops.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)dead_vnops.c 7.13 (Berkeley) 4/15/91 + * from: @(#)dead_vnops.c 7.13 (Berkeley) 4/15/91 + * $Id$ */ #include "param.h" diff --git a/sys/kern/fifo_vnops.c b/sys/kern/fifo_vnops.c index 959e3638fe..a774fa3736 100644 --- a/sys/kern/fifo_vnops.c +++ b/sys/kern/fifo_vnops.c @@ -30,18 +30,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)fifo_vnops.c 7.7 (Berkeley) 4/15/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00141 - * -------------------- ----- ---------------------- - * - * 20 Apr 93 Jay Fenlason 1. fi_{readers|fi_writers} of fifoinfo init to 0 - * 2. fifo_open() was calling tsleep() without - * unlocking the inode of the fifo + * from: @(#)fifo_vnops.c 7.7 (Berkeley) 4/15/91 + * $Id$ */ +#ifdef FIFO #include "param.h" #include "time.h" #include "namei.h" @@ -262,7 +255,7 @@ fifo_write(vp, uio, ioflag, cred) if (ioflag & IO_NDELAY) wso->so_state |= SS_NBIO; VOP_UNLOCK(vp); - error = sosend(wso, (struct mbuf *)0, uio, 0, (struct mbuf *)0); + error = sosend(wso, (struct mbuf *)0, uio, 0, (struct mbuf *)0, 0); VOP_LOCK(vp); if (ioflag & IO_NDELAY) wso->so_state &= ~SS_NBIO; @@ -436,3 +429,4 @@ fifo_badop() panic("fifo_badop called"); /* NOTREACHED */ } +#endif /*FIFO*/ diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index 33c067d38e..9ecd7e4402 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -30,17 +30,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)init_main.c 7.41 (Berkeley) 5/15/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00162 - * -------------------- ----- ---------------------- - * - * 26 May 93 Holger Veit Remove hard coded escapes - * Rodney W. Grimes Added two more \n to clean up output + * from: @(#)init_main.c 7.41 (Berkeley) 5/15/91 + * $Id$ */ -static char rcsid[] = "$Header: /usr/src/sys.386bsd/kern/RCS/init_main.c,v 1.3 92/01/21 21:28:49 william Exp Locker: root $"; #include "param.h" #include "filedesc.h" @@ -57,6 +49,7 @@ static char rcsid[] = "$Header: /usr/src/sys.386bsd/kern/RCS/init_main.c,v 1.3 9 #include "protosw.h" #include "reboot.h" #include "user.h" +#include "utsname.h" #include "ufs/quota.h" @@ -64,12 +57,13 @@ static char rcsid[] = "$Header: /usr/src/sys.386bsd/kern/RCS/init_main.c,v 1.3 9 #include "vm/vm.h" -char copyright1[] = -"386BSD Release 0.1 by William and Lynne Jolitz."; -char copyright2[] = +char copyright[] = "Copyright (c) 1989,1990,1991,1992 William F. Jolitz. All rights reserved.\n\ -Based in part on work by the 386BSD User Community and the\n\ -BSD Networking Software, Release 2 by UCB EECS Department.\n"; +Copyright (c) 1982,1986,1989,1991 The Regents of the University\n\ +of California. All rights reserved.\n\n"; + +/* For uname() */ +struct utsname utsname; /* * Components of process 0; @@ -92,6 +86,10 @@ extern int (*mountroot)(); struct vnode *rootvp, *swapdev_vp; int boothowto; +#if __GNUC__ >= 2 +__main() {} +#endif + /* * System startup; initialize the world, create process 0, * mount root filesystem, and fork to create init and pagedaemon. @@ -105,6 +103,7 @@ main() register struct proc *p; register struct filedesc0 *fdp; int s, rval[2]; + char *cp; /* * Initialize curproc before any possible traps/probes @@ -119,15 +118,7 @@ main() startrtclock(); consinit(); - /* -hv- 22 Apr 93 corrects a hack which prevents proper handling - * of different terminal emulators - * plain ESC codes in the kernel other than in the console part - * should be a NO-NO! Fixed in pccons.c/co_vga.c - */ - cons_highlight(); - printf(copyright1); - cons_normal(); - printf("\n[0.1.%s]\n%s\n", version+9,copyright2); + printf("%s", copyright); vm_mem_init(); kmeminit(); @@ -236,6 +227,10 @@ main() #if NSL > 0 slattach(); /* XXX */ #endif +#include "ppp.h" +#if NPPP > 0 + pppattach(); /* XXX */ +#endif #include "loop.h" #if NLOOP > 0 loattach(); /* XXX */ @@ -281,6 +276,30 @@ main() */ boottime = p->p_stats->p_start = time; + /* + * Setup version number for uname syscall + * XXX probably should go elsewhere. + */ + bzero(utsname.sysname, sizeof(utsname.sysname)); + for (cp = version, i= 0; + *cp && *cp != ' ' && i <= sizeof(utsname.sysname); + ) + utsname.sysname[i++] = *cp++; + bzero(utsname.release, sizeof(utsname.release)); + for (cp++, i= 0; *cp && *cp != ' ' && i <= sizeof(utsname.release);) + utsname.release[i++] = *cp++; + bzero(utsname.version, sizeof(utsname.version)); + for (; *cp != '('; cp++); + for (cp++, i= 0; *cp && *cp != ')' && i <= sizeof(utsname.version);) + utsname.version[i++] = *cp++; + for (; *cp != '#'; cp++); + if(i <= sizeof(utsname.version)) + utsname.version[i++] = '#'; + for (cp++; *cp && *cp != ':' && i <= sizeof(utsname.version);) + utsname.version[i++] = *cp++; + strncpy(utsname.machine, MACHINE, sizeof(utsname.machine)); + utsname.machine[sizeof(utsname.machine)-1] = '\0'; + /* * make init process */ diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c index 92fdcb77f4..55e428c00f 100644 --- a/sys/kern/init_sysent.c +++ b/sys/kern/init_sysent.c @@ -2,7 +2,7 @@ * System call switch table. * * DO NOT EDIT-- this file is automatically generated. - * created from @(#)syscalls.master 7.26 (Berkeley) 3/25/91 + * created from $Id: syscalls.master,v 1.5 1993/10/24 06:19:58 paul Exp $ */ #include "param.h" @@ -158,6 +158,9 @@ int async_daemon(); int getfh(); #else #endif +int getdomainname(); +int setdomainname(); +int uname(); #ifdef SYSVSHM int shmsys(); #else @@ -302,7 +305,7 @@ struct sysent sysent[] = { 3, mincore, /* 78 = mincore */ 2, getgroups, /* 79 = getgroups */ 2, setgroups, /* 80 = setgroups */ - 1, getpgrp, /* 81 = getpgrp */ + 0, getpgrp, /* 81 = getpgrp */ 2, setpgid, /* 82 = setpgid */ 3, setitimer, /* 83 = setitimer */ compat(0,wait), /* 84 = old wait */ @@ -400,9 +403,9 @@ struct sysent sysent[] = { 0, nosys, /* 160 = nosys */ 0, nosys, /* 161 = nosys */ #endif - 0, nosys, /* 162 = nosys */ - 0, nosys, /* 163 = nosys */ - 0, nosys, /* 164 = nosys */ + 2, getdomainname, /* 162 = getdomainname */ + 2, setdomainname, /* 163 = setdomainname */ + 1, uname, /* 164 = uname */ 0, nosys, /* 165 = nosys */ 0, nosys, /* 166 = nosys */ 0, nosys, /* 167 = nosys */ diff --git a/sys/kern/kern__physio.c b/sys/kern/kern__physio.c index 8b2cf42888..0995b1b604 100644 --- a/sys/kern/kern__physio.c +++ b/sys/kern/kern__physio.c @@ -45,15 +45,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00065 - * -------------------- ----- ---------------------- - * - * 09 Sep 92 Paul Kranenburg Fixed read from /dev/drum - * 28 Nov 92 Mark Tinguely Fixed small leak in physio() + * $Id$ */ -static char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/kern__physio.c,v 1.3 92/01/21 21:29:06 william Exp $"; #include "param.h" #include "systm.h" diff --git a/sys/kern/kern_acct.c b/sys/kern/kern_acct.c index c1e07cf6cf..192ef69e47 100644 --- a/sys/kern/kern_acct.c +++ b/sys/kern/kern_acct.c @@ -31,6 +31,7 @@ * SUCH DAMAGE. * * from: @(#)kern_acct.c 7.18 (Berkeley) 5/11/91 + * $Id: kern_acct.c,v 1.4 1993/10/19 01:07:21 nate Exp $ */ #include "param.h" @@ -48,14 +49,17 @@ #include "acct.h" #include "syslog.h" +#include "vm/vm.h" +#include "vm/vm_param.h" + /* * Values associated with enabling and disabling accounting */ int acctsuspend = 2; /* stop accounting when < 2% free space left */ int acctresume = 4; /* resume when free space risen to > 4% */ struct timeval chk = { 15, 0 };/* frequency to check space for accounting */ -struct vnode *acctp; /* file to which to do accounting */ -struct vnode *savacctp; /* file to which to do accounting when space */ +struct vnode *acctp = NULL; /* file to which to do accounting */ +struct vnode *savacctp = NULL; /* file to which to do accounting when space */ /* * Enable or disable process accounting. @@ -68,19 +72,89 @@ struct vnode *savacctp; /* file to which to do accounting when space */ * accounting has been suspended, and freespace rises above acctresume, * accounting is resumed. */ + +/* Mark Tinguely (tinguely@plains.NoDak.edu) 8/10/93 */ + +struct sysacct_args { + char *fname; +}; + /* ARGSUSED */ sysacct(p, uap, retval) struct proc *p; - struct args { - char *fname; - } *uap; + struct sysacct_args *uap; int *retval; { + register struct nameidata *ndp; + struct nameidata nd; + struct vattr attr; + int rv, acctwatch(); + + if (p->p_ucred->cr_uid != 0) + return(EPERM); /* must be root */ + + /* + * Step 1. turn off accounting (if on). exit if fname is nil + */ + + rv = 0; /* just in case nothing is open */ + if (acctp != NULL) { + rv = vn_close(acctp, FWRITE, p->p_ucred, p); + untimeout(acctwatch, (caddr_t) &chk); /* turn off disk check */ + acctp = NULL; + } + else if (savacctp != NULL ) { + rv = vn_close(savacctp, FWRITE, p->p_ucred, p); + untimeout(acctwatch, (caddr_t) &chk); /* turn off disk check */ + savacctp = NULL; + } + + if (uap->fname == NULL) /* accounting stopping complete */ + return(rv); + /* - * Body deleted. + * Step 2. open accounting filename for writing. */ - return (ENOSYS); + + nd.ni_segflg = UIO_USERSPACE; + nd.ni_dirp = uap->fname; + + /* is it there? */ + if (rv = vn_open(&nd, p, FWRITE, 0)) + return (rv); + + /* Step 2. Check the attributes on accounting file */ + rv = VOP_GETATTR(nd.ni_vp, &attr, p->p_ucred, p); + if (rv) + goto acct_fail; + + /* is filesystem writable, do I have permission to write and is + * a regular file? + */ + if (nd.ni_vp->v_mount->mnt_flag & MNT_RDONLY) { + rv = EROFS; /* to be consistant with man page */ + goto acct_fail; + } + + if ((VOP_ACCESS(nd.ni_vp, VWRITE, p->p_ucred, p)) || + (attr.va_type != VREG)) { + rv = EACCES; /* permission denied error */ + goto acct_fail; + } + + /* Step 3. Save the accounting file vnode, schedule freespace watch. */ + + acctp = nd.ni_vp; + savacctp = NULL; + acctwatch(&chk); /* look for full system */ + VOP_UNLOCK(acctp); + return(0); /* end successfully */ + +acct_fail: + + vn_close(nd.ni_vp, FWRITE, p->p_ucred, p); + return(rv); } /* @@ -116,12 +190,93 @@ acctwatch(resettime) * This routine calculates an accounting record for a process and, * if accounting is enabled, writes it to the accounting file. */ + +/* Mark Tinguely (tinguely@plains.NoDak.edu) 8/10/93 */ + acct(p) register struct proc *p; { - /* - * Body deleted. - */ - return; + struct acct acct; + struct rusage *r; + int rv; + long i; + u_int cnt; + char *c; + comp_t int2comp(); + + + if (acctp == NULL) /* accounting not turned on */ + return; + + /* Step 1. Get command name (remove path if necessary) */ + + strncpy(acct.ac_comm, p->p_comm, sizeof(acct.ac_comm)); + + /* Step 2. Get rest of information */ + + acct.ac_utime = int2comp((unsigned) p->p_utime.tv_sec * 1000000 + p->p_utime.tv_usec); + acct.ac_stime = int2comp((unsigned) p->p_stime.tv_sec * 1000000 + p->p_stime.tv_usec); + acct.ac_btime = p->p_stats->p_start.tv_sec; + /* elapse time = current - start */ + i = (time.tv_sec - p->p_stats->p_start.tv_sec) * 1000000 + + (time.tv_usec - p->p_stats->p_start.tv_usec); + acct.ac_etime = int2comp((unsigned) i); + + acct.ac_uid = p->p_cred->p_ruid; + acct.ac_gid = p->p_cred->p_rgid; + + r = &p->p_stats->p_ru; + if (i = (p->p_utime.tv_sec + p->p_stime.tv_sec) * hz + + (p->p_utime.tv_usec + p->p_stime.tv_usec) / tick) + acct.ac_mem = (r->ru_ixrss + r->ru_idrss + r->ru_isrss) / i; + else + acct.ac_mem = 0; + acct.ac_io = int2comp((unsigned) (r->ru_inblock + r->ru_oublock) * 1000000); + + if ((p->p_flag & SCTTY) && p->p_pgrp->pg_session->s_ttyp) + acct.ac_tty = p->p_pgrp->pg_session->s_ttyp->t_dev; + else + acct.ac_tty = NODEV; + acct.ac_flag = p->p_acflag; + + /* Step 3. Write record to file */ + + + rv = vn_rdwr(UIO_WRITE, acctp, (caddr_t) &acct, sizeof (acct), + (off_t)0, UIO_SYSSPACE, IO_APPEND|IO_UNIT, p->p_ucred, (int *) NULL, + p); +} + +/* int2comp converts from ticks in a microsecond to ticks in 1/AHZ second + * + * comp_t is a psuedo-floating point number with 13 bits of + * mantissa and 3 bits of base 8 exponent and has resolution + * of 1/AHZ seconds. + * + * notice I already converted the incoming values into microseconds + * I need to convert back into AHZ ticks. + */ + +/* Mark Tinguely (tinguely@plains.NoDak.edu) 8/10/93 */ + + +#define RES 13 +#define EXP 3 +#define MAXFRACT 1< MAXFRACT) { + mantissa >>= EXP; /* base 8 exponent */ + exp++; + } + exp <<= RES; /* move the exponent */ + exp += mantissa; /* add on the manissa */ + return (exp); } diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c index 631b1d8e13..4ec550f421 100644 --- a/sys/kern/kern_clock.c +++ b/sys/kern/kern_clock.c @@ -30,15 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kern_clock.c 7.16 (Berkeley) 5/9/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00158 - * -------------------- ----- ---------------------- - * - * 08 Apr 93 Poul-Henning Kamp Add support for dcfclock - * 25 Apr 93 Bruce Evans Support new interrupt code (intr-0.1) + * from: @(#)kern_clock.c 7.16 (Berkeley) 5/9/91 + * $Id: kern_clock.c,v 1.4 1993/10/19 09:05:51 davidg Exp $ */ #include "param.h" @@ -51,6 +44,9 @@ #include "machine/cpu.h" +#include "resource.h" +#include "vm/vm.h" + #ifdef GPROF #include "gprof.h" #endif @@ -101,6 +97,8 @@ hardclock(frame) register struct callout *p1; register struct proc *p = curproc; register struct pstats *pstats; + register struct rusage *ru; + register struct vmspace *vm; register int s; int needsoft = 0; extern int tickdelta; @@ -158,6 +156,18 @@ hardclock(frame) BUMPTIME(&p->p_stime, tick); } + /* bump the resource usage of integral space use */ + if (p && pstats && (ru = &pstats->p_ru) && (vm = p->p_vmspace)) { + ru->ru_ixrss += vm->vm_tsize * NBPG / 1024; + ru->ru_idrss += vm->vm_dsize * NBPG / 1024; + ru->ru_isrss += vm->vm_ssize * NBPG / 1024; + if ((vm->vm_pmap.pm_stats.resident_count * NBPG / 1024) > + ru->ru_maxrss) { + ru->ru_maxrss = + vm->vm_pmap.pm_stats.resident_count * NBPG / 1024; + } + } + /* * If the cpu is currently scheduled to a process, then * charge it with resource utilization for a tick, updating diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index de896941f4..086ecab870 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -30,9 +30,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kern_descrip.c 7.28 (Berkeley) 6/25/91 + * from: @(#)kern_descrip.c 7.28 (Berkeley) 6/25/91 + * $Id$ */ -static char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/kern_descrip.c,v 1.2 92/01/21 21:29:09 william Exp $"; #include "param.h" #include "systm.h" @@ -55,6 +55,7 @@ static char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/kern_descrip.c,v */ struct file *filehead; /* head of list of open files */ int nfiles; /* actual number of open files */ +extern int maxfdescs; /* maximum number of file descriptors to a process */ /* * System calls on descriptors. @@ -73,12 +74,15 @@ getdtablesize(p, uap, retval) /* * Duplicate a file descriptor. */ + +struct dup_args { + int i; +}; + /* ARGSUSED */ dup(p, uap, retval) struct proc *p; - struct args { - int i; - } *uap; + struct dup_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; @@ -107,13 +111,16 @@ dup(p, uap, retval) /* * Duplicate a file descriptor to a particular value. */ + +struct dup2_args { + u_int from; + u_int to; +}; + /* ARGSUSED */ dup2(p, uap, retval) struct proc *p; - struct args { - u_int from; - u_int to; - } *uap; + struct dup2_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; @@ -123,7 +130,8 @@ dup2(p, uap, retval) if (old >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[old]) == NULL || - new >= p->p_rlimit[RLIMIT_OFILE].rlim_cur) + new >= p->p_rlimit[RLIMIT_OFILE].rlim_cur || + new >= maxfdescs) return (EBADF); *retval = new; if (old == new) @@ -152,14 +160,17 @@ dup2(p, uap, retval) /* * The file control system call. */ + +struct fcntl_args { + int fd; + int cmd; + int arg; +}; + /* ARGSUSED */ fcntl(p, uap, retval) struct proc *p; - register struct args { - int fd; - int cmd; - int arg; - } *uap; + register struct fcntl_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; @@ -175,7 +186,8 @@ fcntl(p, uap, retval) pop = &fdp->fd_ofileflags[uap->fd]; switch(uap->cmd) { case F_DUPFD: - if ((unsigned)uap->arg >= p->p_rlimit[RLIMIT_OFILE].rlim_cur) + if ((unsigned)uap->arg >= p->p_rlimit[RLIMIT_OFILE].rlim_cur || + ((unsigned)uap->arg >= maxfdescs)) return (EINVAL); if (error = fdalloc(p, uap->arg, &i)) return (error); @@ -301,11 +313,13 @@ fcntl(p, uap, retval) * Close a file descriptor. */ /* ARGSUSED */ +struct close_args { + int fd; +}; + close(p, uap, retval) struct proc *p; - struct args { - int fd; - } *uap; + struct close_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; @@ -331,13 +345,16 @@ close(p, uap, retval) /* * Return status information about a file descriptor. */ + +struct fstat_args { + int fd; + struct stat *sb; +}; + /* ARGSUSED */ fstat(p, uap, retval) struct proc *p; - register struct args { - int fd; - struct stat *sb; - } *uap; + register struct fstat_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; @@ -690,13 +707,15 @@ closef(fp, p) * the entire file (l_whence = SEEK_SET, l_start = 0, l_len = 0). */ +struct flock_args { + int fd; + int how; +}; + /* ARGSUSED */ flock(p, uap, retval) struct proc *p; - register struct args { - int fd; - int how; - } *uap; + register struct flock_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; diff --git a/sys/kern/kern_execve b/sys/kern/kern_execve deleted file mode 100644 index cc740b1b67..0000000000 --- a/sys/kern/kern_execve +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (c) 1992 William Jolitz. All rights reserved. - * Written by William Jolitz 1/92 - * - * Redistribution and use in source and binary forms are freely permitted - * provided that the above copyright notice and attribution and date of work - * and this paragraph are duplicated in all such forms. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - * This procedure implements a minimal program execution facility for - * 386BSD. It interfaces to the BSD kernel as the execve system call. - * Significant limitations and lack of compatiblity with POSIX are - * present with this version, to make its basic operation more clear. - * - */ - -#include "param.h" -#include "systm.h" -#include "proc.h" -#include "mount.h" -#include "namei.h" -#include "vnode.h" -#include "file.h" -#include "exec.h" -#include "stat.h" -#include "wait.h" -#include "signalvar.h" -#include "mman.h" - -#include "vm/vm.h" -#include "vm/vm_param.h" -#include "vm/vm_map.h" -#include "vm/vm_kern.h" - -#include "machine/reg.h" - -static char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/kern_execve.c,v 1.3 92/01/21 21:29:13 william Exp $"; - -/* - * Bill's first-cut execve() system call. Puts hair on your chest. - */ - -/* ARGSUSED */ -execve(p, uap, retval) - struct proc *p; - register struct args { - char *fname; - char **argp; - char **envp; - } *uap; - int *retval; -{ - register struct nameidata *ndp; - int rv, amt; - struct nameidata nd; - struct exec hdr; - char **kargbuf, **kargbufp, *kstringbuf, *kstringbufp; - char **org, **vectp, *ep; - u_int needsenv, limitonargs, stringlen; - int addr, size; - int argc; - char *cp; - struct stat statb; - struct vmspace *vs; - int tsize, dsize, bsize, cnt, foff; - - /* - * Step 1. Lookup filename to see if we have something to execute. - */ - ndp = &nd; - ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - - /* is it there? */ - if (rv = namei(ndp, p)) - return (rv); - - /* is it a regular file? */ - if (ndp->ni_vp->v_type != VREG) { - vput(ndp->ni_vp); - return(ENOEXEC); - } - - /* is it executable? */ - rv = VOP_ACCESS(ndp->ni_vp, VEXEC, p->p_ucred, p); - if (rv) - goto exec_fail; - - rv = vn_stat(ndp->ni_vp, &statb, p); - if (rv) - goto exec_fail; - - /* - * Step 2. Does the file contain a format we can - * understand and execute - */ - rv = vn_rdwr(UIO_READ, ndp->ni_vp, (caddr_t)&hdr, sizeof(hdr), - 0, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &amt, p); - - /* big enough to hold a header? */ - if (rv) - goto exec_fail; - - /* that we recognize? */ - rv = ENOEXEC; - if (hdr.a_magic != ZMAGIC) - goto exec_fail; - - /* sanity check "ain't not such thing as a sanity clause" -groucho */ - if (/*hdr.a_text == 0 || */ hdr.a_text > MAXTSIZ - || hdr.a_text % NBPG || hdr.a_text > statb.st_size) - goto exec_fail; - - if (hdr.a_data == 0 || hdr.a_data > DFLDSIZ - || hdr.a_data > statb.st_size - || hdr.a_data + hdr.a_text > statb.st_size) - goto exec_fail; - - if (hdr.a_bss > MAXDSIZ) - goto exec_fail; - - if (hdr.a_text + hdr.a_data + hdr.a_bss > MAXTSIZ + MAXDSIZ) - goto exec_fail; - - /* - * Step 3. File and header are valid. Now, dig out the strings - * out of the old process image. - */ - - /* assumption: most execve's have less than 256 arguments, with a - * total of string storage space not exceeding 2K. It is more - * frequent that when this fails, string space falls short first - * (e.g. as when a large termcap environment variable is present). - * It is infrequent when more than 256 arguments are used that take - * up less than 2K of space (e.g. args average more than 8 chars). - * - * What we give up in this implementation is a dense encoding of - * the data structure in the receiving program's address space. - * This means that there is plenty of wasted space (up to 6KB) - * as the price we pay for a fast, single pass algorithm. - * - * Our alternative would be to accumulate strings and pointers - * in the first pass, then, knowing the sizes and number of the - * strings, pack them neatly and tightly togeither in the second - * pass. This means two copies of the strings, and string copying - * is much of the cost of exec. - */ - - /* allocate string buffer and arg buffer */ - org = kargbuf = (char **) kmem_alloc_wait(exec_map, - (NCARGS + PAGE_SIZE)/PAGE_SIZE); - kstringbuf = kstringbufp = ((char *)kargbuf) + NBPG/2; - kargbuf += NBPG/(4*sizeof(int)); - kargbufp = kargbuf; - - /* first, do args */ - needsenv = 1; - vectp = uap->argp; - -do_env_as_well: - cnt = 0; - /* for each envp, copy in string */ - limitonargs = NCARGS; - if(vectp == 0) goto dont_bother; - do { - /* did we outgrow initial argbuf, if so, die */ - if (kargbufp == (char **)kstringbuf) - goto exec_fail; - - /* get an string pointer */ - ep = (char *)fuword(vectp++); - if (ep == (char *)-1) { - rv = EFAULT; - goto exec_fail; - } - - /* if not null pointer, copy in string */ - if (ep) { - if (rv = copyinstr(ep, kstringbufp, limitonargs, - &stringlen)) goto exec_fail; - /* assume that strings usually all fit in last page */ - *kargbufp = (char *)(kstringbufp - kstringbuf - + USRSTACK - NBPG + NBPG/2); - kargbufp++; - cnt++; - kstringbufp += stringlen; - limitonargs -= stringlen + sizeof(long); - } else { - *kargbufp++ = 0; - limitonargs -= sizeof(long); - break; - } - } while (limitonargs > 0); - -dont_bother: - if (limitonargs <= 0) { - rv = E2BIG; - goto exec_fail; - } - - if (needsenv) { - argc = cnt; - vectp = uap->envp; - needsenv = 0; - goto do_env_as_well; - } - - kargbuf[-1] = (char *)argc; - - /* - * Step 4. Build the new processes image. - */ - - /* At this point, we are committed -- destroy old executable */ - vs = p->p_vmspace; - addr = 0; - size = USRSTACK - addr; - /* blow away all address space */ - rv = vm_deallocate(&vs->vm_map, addr, size, FALSE); - if (rv) - goto exec_abort; - - /* build a new address space */ - addr = 0; - if (hdr.a_text == 0) { - /* screwball mode */ - foff = tsize = 0; - hdr.a_data += hdr.a_text; - } else { - tsize = roundup(hdr.a_text, NBPG); - foff = NBPG; - } - dsize = roundup(hdr.a_data, NBPG); - bsize = roundup(hdr.a_bss + dsize, NBPG); - bsize -= dsize; - - /* map text & data*/ - rv = vm_mmap(&vs->vm_map, &addr, tsize+dsize, VM_PROT_ALL, - MAP_FILE|MAP_COPY|MAP_FIXED, (caddr_t)ndp->ni_vp, foff); - if (rv) - goto exec_abort; - - /* r/w data, ro text */ - if (tsize) { - addr = 0; - rv = vm_protect(&vs->vm_map, addr, tsize, FALSE, VM_PROT_READ|VM_PROT_EXECUTE); - if (rv) - goto exec_abort; - } - - /* create anonymous memory region for bss */ - addr = dsize + tsize; - rv = vm_allocate(&vs->vm_map, &addr, bsize, FALSE); - if (rv) - goto exec_abort; - - /* create anonymous memory region for stack */ - addr = USRSTACK - MAXSSIZ; - rv = vm_allocate(&vs->vm_map, &addr, MAXSSIZ, FALSE); - if (rv) - goto exec_abort; - - /* - * Step 5. Prepare process for execution. - */ - - /* touchup process information */ - vs->vm_tsize = tsize/NBPG; /* text size (pages) XXX */ - vs->vm_dsize = (dsize+bsize)/NBPG; /* data size (pages) XXX */ - vs->vm_ssize = MAXSSIZ/NBPG; /* stack size (pages) */ - vs->vm_taddr = 0; /* user virtual address of text XXX */ - vs->vm_daddr = (caddr_t)tsize; /* user virtual address of data XXX */ - /* user VA at max stack growth */ - vs->vm_maxsaddr = (caddr_t)(USRSTACK - MAXSSIZ); - - /* everything fits in a single page, no fixups, no more work */ - /* (groan) due to bug in vm_map_copy, can't remap. copy for now. */ - rv = copyout((caddr_t)org, (caddr_t)USRSTACK - NBPG, NBPG); - if(rv) - goto exec_abort; - - /* close files on exec, fixup signals */ - fdcloseexec(p); - execsigs(p); - - p->p_regs[SP] = USRSTACK - NBPG + NBPG/4 - 4; - vs->vm_ssize = 1; /* stack size (pages) */ - setregs(p, hdr.a_entry); - kmem_free_wakeup(exec_map, org, (NCARGS + PAGE_SIZE)/PAGE_SIZE); - vput(ndp->ni_vp); - return (0); - -exec_fail: - vput(ndp->ni_vp); - return(rv); - -exec_abort: - /* untested and probably bogus */ - kmem_free_wakeup(exec_map, org, (NCARGS + PAGE_SIZE)/PAGE_SIZE); - vput(ndp->ni_vp); - exit(p, W_EXITCODE(0, SIGABRT)); - return(0); - -} diff --git a/sys/kern/kern_execve.c b/sys/kern/kern_execve.c index 4bef214f76..b070aed1d7 100644 --- a/sys/kern/kern_execve.c +++ b/sys/kern/kern_execve.c @@ -50,20 +50,7 @@ * Significant limitations and lack of compatiblity with POSIX are * present with this version, to make its basic operation more clear. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 6 00165 - * -------------------- ----- ---------------------- - * - * 05 Aug 92 Paul Kranenburg Fixed #! as a magic number - * 29 Jul 92 Mark Tinguely Fixed execute permission enforcement - * 15 Aug 92 Terry Lambert Fixed CMOS RAM size bug - * 12 Dec 92 Julians Elischer Place argc into user address space - * correctly - * 10 Apr 93 Yoval Yarom Fix for busy text on executables - * 20 Apr 93 markie Stop execution of a file open for write - * Rodney W. Grimes Fix date on Yoval Yarom's patch - * 01 Jun 93 Chris Demetriou Completed markie's patch for VTEXT busy + * $Id: kern_execve.c,v 1.7 1993/10/19 00:58:51 nate Exp $ */ #include "param.h" @@ -75,6 +62,7 @@ #include "namei.h" #include "vnode.h" #include "file.h" +#include "acct.h" #include "exec.h" #include "stat.h" #include "wait.h" @@ -95,14 +83,16 @@ extern int dostacklimits; * execve() system call. */ +struct execve_args { + char *fname; + char **argp; + char **envp; +}; + /* ARGSUSED */ execve(p, uap, retval) struct proc *p; - register struct args { - char *fname; - char **argp; - char **envp; - } *uap; + register struct execve_args *uap; int *retval; { register struct nameidata *ndp; @@ -110,11 +100,13 @@ execve(p, uap, retval) char **argbuf, **argbufp, *stringbuf, *stringbufp; char **vectp, *ep; int needsenv, limitonargs, stringlen, addr, size, len, - rv, amt, argc, tsize, dsize, bsize, cnt, foff; + rv, amt, argc, tsize, dsize, bsize, cnt, file_offset, + virtual_offset; struct vattr attr; struct vmspace *vs; caddr_t newframe; char shellname[MAXINTERP]; /* 05 Aug 92*/ + char *shellargs; union { char ex_shell[MAXINTERP]; /* #! and interpreter name */ struct exec ex_hdr; @@ -184,45 +176,85 @@ again: /* 05 Aug 92*/ goto exec_fail; } +#define SHELLMAGIC 0x2123 /* #! */ - /* ... that we recognize? */ - rv = ENOEXEC; - if (exdata.ex_hdr.a_magic != ZMAGIC) { - char *cp, *sp; - - if (exdata.ex_shell[0] != '#' || - exdata.ex_shell[1] != '!' || indir) { - rv = ENOEXEC; - goto exec_fail; + switch (exdata.ex_hdr.a_magic) { + case ZMAGIC: + virtual_offset = 0; + if (exdata.ex_hdr.a_text) { + file_offset = NBPG; + } else { + /* Bill's "screwball mode" */ + file_offset = 0; } - for (cp = &exdata.ex_shell[2];; ++cp) { - if (cp >= &exdata.ex_shell[MAXINTERP]) { + break; + case QMAGIC: + virtual_offset = NBPG; + file_offset = 0; + break; + default: + if ((exdata.ex_hdr.a_magic & 0xffff) != SHELLMAGIC) { + /* NetBSD compatibility */ + switch (ntohl(exdata.ex_hdr.a_magic) & 0xffff) { + case ZMAGIC: + case QMAGIC: + virtual_offset = NBPG; + file_offset = 0; + break; + default: rv = ENOEXEC; goto exec_fail; } - if (*cp == '\n') { - *cp = '\0'; - break; + } else { + char *cp, *sp; + + if (indir) { + rv = ENOEXEC; + goto exec_fail; + } + for (cp = &exdata.ex_shell[2];; ++cp) { + if (cp >= &exdata.ex_shell[MAXINTERP]) { + rv = ENOEXEC; + goto exec_fail; + } + if (*cp == '\n') { + *cp = '\0'; + break; + } + if (*cp == '\t') + *cp = ' '; } - if (*cp == '\t') - *cp = ' '; + cp = &exdata.ex_shell[2]; /* get shell interpreter name */ + while (*cp == ' ') + cp++; + + sp = shellname; + while (*cp && *cp != ' ') + *sp++ = *cp++; + *sp = '\0'; + + /* copy the args in the #! line */ + while (*cp == ' ') + cp++; + if (*cp) { + sp++; + shellargs = sp; + while (*cp) + *sp++ = *cp++; + *sp = '\0'; + } else { + shellargs = 0; + } + + indir = 1; /* indicate this is a script file */ + vput(ndp->ni_vp); + FREE(ndp->ni_pnbuf, M_NAMEI); + + ndp->ni_dirp = shellname; /* find shell interpreter */ + ndp->ni_segflg = UIO_SYSSPACE; + goto again; } - cp = &exdata.ex_shell[2]; /* get shell interpreter name */ - while (*cp == ' ') - cp++; - - sp = shellname; - while (*cp && *cp != ' ') - *sp++ = *cp++; - *sp = '\0'; - - indir = 1; /* indicate this is a script file */ - vput(ndp->ni_vp); - FREE(ndp->ni_pnbuf, M_NAMEI); - - ndp->ni_dirp = shellname; /* find shell interpreter */ - ndp->ni_segflg = UIO_SYSSPACE; - goto again; + /* NOT REACHED */ } /* sanity check "ain't not such thing as a sanity clause" -groucho */ @@ -289,7 +321,7 @@ again: /* 05 Aug 92*/ /* first, do (shell name if any then) args */ if (indir) { ep = shellname; -twice: +thrice: if (ep) { /* did we outgrow initial argbuf, if so, die */ if (argbufp >= (char **)stringbuf) { @@ -309,11 +341,17 @@ twice: limitonargs -= stringlen; } + if (shellargs) { + ep = shellargs; + shellargs = 0; + goto thrice; + } + if (indir) { indir = 0; /* orginal executable is 1st argument with scripts */ ep = uap->fname; - goto twice; + goto thrice; } /* terminate in case no more args to script */ suword(argbufp, 0); @@ -407,39 +445,35 @@ dont_bother: } /* build a new address space */ - addr = 0; - /* screwball mode -- special case of 413 to save space for floppy */ - if (exdata.ex_hdr.a_text == 0) { - foff = tsize = 0; - exdata.ex_hdr.a_data += exdata.ex_hdr.a_text; - } else { - tsize = roundup(exdata.ex_hdr.a_text, NBPG); - foff = NBPG; - } - /* treat text and data in terms of integral page size */ + + /* treat text, data, and bss in terms of integral page size */ + tsize = roundup(exdata.ex_hdr.a_text, NBPG); dsize = roundup(exdata.ex_hdr.a_data, NBPG); - bsize = roundup(exdata.ex_hdr.a_bss + dsize, NBPG); - bsize -= dsize; + bsize = roundup(exdata.ex_hdr.a_bss, NBPG); + + addr = virtual_offset; - /* map text & data in file, as being "paged in" on demand */ - rv = vm_mmap(&vs->vm_map, &addr, tsize+dsize, VM_PROT_ALL, - MAP_FILE|MAP_COPY|MAP_FIXED, (caddr_t)ndp->ni_vp, foff); + /* map text as being read/execute only and demand paged */ + rv = vm_mmap(&vs->vm_map, &addr, tsize, VM_PROT_READ|VM_PROT_EXECUTE, + VM_PROT_DEFAULT, MAP_FILE|MAP_PRIVATE|MAP_FIXED, + (caddr_t)ndp->ni_vp, file_offset); if (rv) goto exec_abort; - /* mark pages r/w data, r/o text */ - if (tsize) { - addr = 0; - rv = vm_protect(&vs->vm_map, addr, tsize, FALSE, - VM_PROT_READ|VM_PROT_EXECUTE); - if (rv) - goto exec_abort; - } + addr = virtual_offset + tsize; + + /* map data as being read/write and demand paged */ + rv = vm_mmap(&vs->vm_map, &addr, dsize, + VM_PROT_READ | VM_PROT_WRITE | (tsize ? 0 : VM_PROT_EXECUTE), + VM_PROT_DEFAULT, MAP_FILE|MAP_PRIVATE|MAP_FIXED, + (caddr_t)ndp->ni_vp, file_offset + tsize); + if (rv) + goto exec_abort; /* create anonymous memory region for bss */ - addr = dsize + tsize; + addr = virtual_offset + tsize + dsize; rv = vm_allocate(&vs->vm_map, &addr, bsize, FALSE); if (rv) goto exec_abort; @@ -451,8 +485,8 @@ dont_bother: /* touchup process information -- vm system is unfinished! */ vs->vm_tsize = tsize/NBPG; /* text size (pages) XXX */ vs->vm_dsize = (dsize+bsize)/NBPG; /* data size (pages) XXX */ - vs->vm_taddr = 0; /* user virtual address of text XXX */ - vs->vm_daddr = (caddr_t)tsize; /* user virtual address of data XXX */ + vs->vm_taddr = (caddr_t) virtual_offset; /* virtual address of text */ + vs->vm_daddr = (caddr_t) virtual_offset + tsize; /* virtual address of data */ vs->vm_maxsaddr = newframe; /* user VA at max stack growth XXX */ vs->vm_ssize = ((unsigned)vs->vm_maxsaddr + MAXSSIZ - (unsigned)argbuf)/ NBPG + 1; /* stack size (pages) */ @@ -498,6 +532,7 @@ dont_bother: can be set before the program "runs" */ if (p->p_flag & STRC) psignal(p, SIGTRAP); + p->p_acflag &= ~AFORK; /* remove fork, but no exec flag */ return (0); @@ -517,7 +552,7 @@ exec_abort: vm_deallocate(&vs->vm_map, newframe, MAXSSIZ); vput(ndp->ni_vp); FREE(ndp->ni_pnbuf, M_NAMEI); - exit(p, W_EXITCODE(0, SIGABRT)); + kexit(p, W_EXITCODE(0, SIGABRT)); /* NOTREACHED */ return(0); diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c index bf0e56a84f..ac967af5fe 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kern_exit.c 7.35 (Berkeley) 6/27/91 + * from: @(#)kern_exit.c 7.35 (Berkeley) 6/27/91 + * $Id: kern_exit.c,v 1.8 1993/10/16 15:24:15 rgrimes Exp $ */ #include "param.h" @@ -61,16 +62,19 @@ /* * Exit system call: pass back caller's arg */ + +struct rexit_args { + int rval; +}; /* ARGSUSED */ +void rexit(p, uap, retval) struct proc *p; - struct args { - int rval; - } *uap; + struct rexit_args *uap; int *retval; { - exit(p, W_EXITCODE(uap->rval, 0)); + kexit(p, W_EXITCODE(uap->rval, 0)); /* NOTREACHED */ } @@ -80,7 +84,8 @@ rexit(p, uap, retval) * and parent's lists. Save exit status and rusage for wait(). * Check for child processes and orphan them. */ -exit(p, rv) +void +kexit(p, rv) register struct proc *p; int rv; { @@ -88,6 +93,9 @@ exit(p, rv) register struct proc **pp; int s; + acct(p); /* MT - do process accounting -- must be done before + address space is released */ + #ifdef PGINPROF vmsizmon(); #endif @@ -251,15 +259,18 @@ done: } #ifdef COMPAT_43 + +struct owait_args { + int pid; + int *status; + int options; + struct rusage *rusage; + int compat; +}; + owait(p, uap, retval) struct proc *p; - register struct args { - int pid; - int *status; - int options; - struct rusage *rusage; - int compat; - } *uap; + register struct owait_args *uap; int *retval; { @@ -271,15 +282,17 @@ owait(p, uap, retval) return (wait1(p, uap, retval)); } +struct wait4_args { + int pid; + int *status; + int options; + struct rusage *rusage; + int compat; +}; + wait4(p, uap, retval) struct proc *p; - struct args { - int pid; - int *status; - int options; - struct rusage *rusage; - int compat; - } *uap; + struct wait4_args *uap; int *retval; { @@ -295,17 +308,20 @@ wait4(p, uap, retval) * stopped under trace, or (optionally) stopped by a signal. * Pass back status and deallocate exited child's proc structure. */ -wait1(q, uap, retval) - register struct proc *q; - register struct args { - int pid; - int *status; - int options; - struct rusage *rusage; + +struct wait1_args { + int pid; + int *status; + int options; + struct rusage *rusage; #ifdef COMPAT_43 - int compat; + int compat; #endif - } *uap; +}; + +wait1(q, uap, retval) + register struct proc *q; + register struct wait1_args *uap; int retval[]; { register int nfound; diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index d17831aa8f..4be3c5fa25 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kern_fork.c 7.29 (Berkeley) 5/15/91 + * from: @(#)kern_fork.c 7.29 (Berkeley) 5/15/91 + * $Id$ */ #include "param.h" diff --git a/sys/kern/kern_kinfo.c b/sys/kern/kern_kinfo.c index f97eff25fb..cbed34510b 100644 --- a/sys/kern/kern_kinfo.c +++ b/sys/kern/kern_kinfo.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kern_kinfo.c 7.17 (Berkeley) 6/26/91 + * from: @(#)kern_kinfo.c 7.17 (Berkeley) 6/26/91 + * $Id$ */ #include "param.h" @@ -49,15 +50,17 @@ extern int kinfo_doproc(), kinfo_rtable(), kinfo_vnode(), kinfo_file(); struct kinfo_lock kinfo_lock; +struct getkerninfo_args { + int op; + char *where; + int *size; + int arg; +}; + /* ARGSUSED */ getkerninfo(p, uap, retval) struct proc *p; - register struct args { - int op; - char *where; - int *size; - int arg; - } *uap; + register struct getkerninfo_args *uap; int *retval; { diff --git a/sys/kern/kern_ktrace.c b/sys/kern/kern_ktrace.c index dcde2c163a..cfd84fcdef 100644 --- a/sys/kern/kern_ktrace.c +++ b/sys/kern/kern_ktrace.c @@ -30,12 +30,14 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kern_ktrace.c 7.15 (Berkeley) 6/21/91 + * from: @(#)kern_ktrace.c 7.15 (Berkeley) 6/21/91 + * $Id$ */ #ifdef KTRACE #include "param.h" +#include "systm.h" #include "proc.h" #include "file.h" #include "namei.h" @@ -171,15 +173,18 @@ ktrpsig(vp, sig, action, mask, code) /* * ktrace system call */ + +struct ktrace_args { + char *fname; + int ops; + int facs; + int pid; +}; + /* ARGSUSED */ ktrace(curp, uap, retval) struct proc *curp; - register struct args { - char *fname; - int ops; - int facs; - int pid; - } *uap; + register struct ktrace_args *uap; int *retval; { register struct vnode *vp = NULL; diff --git a/sys/kern/kern_malloc.c b/sys/kern/kern_malloc.c index 88f7a09253..35a8d28f46 100644 --- a/sys/kern/kern_malloc.c +++ b/sys/kern/kern_malloc.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kern_malloc.c 7.25 (Berkeley) 5/8/91 + * from: @(#)kern_malloc.c 7.25 (Berkeley) 5/8/91 + * $Id: kern_malloc.c,v 1.2 1993/10/16 15:24:21 rgrimes Exp $ */ #include "param.h" @@ -228,13 +229,10 @@ kmeminit() register long indx; int npg; -#if ((MAXALLOCSAVE & (MAXALLOCSAVE - 1)) != 0) - ERROR!_kmeminit:_MAXALLOCSAVE_not_power_of_2 -#endif #if (MAXALLOCSAVE > MINALLOCSIZE * 32768) ERROR!_kmeminit:_MAXALLOCSAVE_too_big #endif -#if (MAXALLOCSAVE < CLBYTES) +#if (MAXALLOCSAVE < CLBYTES-1) ERROR!_kmeminit:_MAXALLOCSAVE_too_small #endif npg = VM_KMEM_SIZE/ NBPG; diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index 8b0403c945..f821e6f8fa 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kern_proc.c 7.16 (Berkeley) 6/28/91 + * from: @(#)kern_proc.c 7.16 (Berkeley) 6/28/91 + * $Id$ */ #include "param.h" diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c index 965927aeec..867ff517f4 100644 --- a/sys/kern/kern_prot.c +++ b/sys/kern/kern_prot.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kern_prot.c 7.21 (Berkeley) 5/3/91 + * from: @(#)kern_prot.c 7.21 (Berkeley) 5/3/91 + * $Id$ */ /* @@ -137,12 +138,14 @@ getegid(p, uap, retval) return (0); } +struct getgroups_args { + u_int gidsetsize; + int *gidset; /* XXX not yet POSIX */ +}; + getgroups(p, uap, retval) struct proc *p; - register struct arg { - u_int gidsetsize; - int *gidset; /* XXX not yet POSIX */ - } *uap; + register struct getgroups_args *uap; int *retval; { register struct pcred *pc = p->p_cred; @@ -197,13 +200,16 @@ setsid(p, uap, retval) * there must exist some pid in same session having pgid (EPERM) * pid must not be session leader (EPERM) */ + +struct setpgid_args { + int pid; /* target process id */ + int pgid; /* target pgrp id */ +}; + /* ARGSUSED */ setpgid(curp, uap, retval) struct proc *curp; - register struct args { - int pid; /* target process id */ - int pgid; /* target pgrp id */ - } *uap; + register struct setpgid_args *uap; int *retval; { register struct proc *targp; /* target process */ @@ -230,12 +236,14 @@ setpgid(curp, uap, retval) return (0); } +struct setuid_args { + int uid; +}; + /* ARGSUSED */ setuid(p, uap, retval) struct proc *p; - struct args { - int uid; - } *uap; + struct setuid_args *uap; int *retval; { register struct pcred *pc = p->p_cred; @@ -257,12 +265,14 @@ setuid(p, uap, retval) return (0); } +struct seteuid_args { + int euid; +}; + /* ARGSUSED */ seteuid(p, uap, retval) struct proc *p; - struct args { - int euid; - } *uap; + struct seteuid_args *uap; int *retval; { register struct pcred *pc = p->p_cred; @@ -282,12 +292,14 @@ seteuid(p, uap, retval) return (0); } +struct setgid_args { + int gid; +}; + /* ARGSUSED */ setgid(p, uap, retval) struct proc *p; - struct args { - int gid; - } *uap; + struct setgid_args *uap; int *retval; { register struct pcred *pc = p->p_cred; @@ -304,12 +316,14 @@ setgid(p, uap, retval) return (0); } +struct setegid_args { + int egid; +}; + /* ARGSUSED */ setegid(p, uap, retval) struct proc *p; - struct args { - int egid; - } *uap; + struct setegid_args *uap; int *retval; { register struct pcred *pc = p->p_cred; @@ -326,13 +340,16 @@ setegid(p, uap, retval) } #ifdef COMPAT_43 + +struct osetreuid_args { + int ruid; + int euid; +}; + /* ARGSUSED */ osetreuid(p, uap, retval) register struct proc *p; - struct args { - int ruid; - int euid; - } *uap; + struct osetreuid_args *uap; int *retval; { register struct pcred *pc = p->p_cred; @@ -370,13 +387,15 @@ osetreuid(p, uap, retval) return (0); } +struct osetregid_args { + int rgid; + int egid; +}; + /* ARGSUSED */ osetregid(p, uap, retval) register struct proc *p; - struct args { - int rgid; - int egid; - } *uap; + struct osetregid_args *uap; int *retval; { register struct pcred *pc = p->p_cred; @@ -412,13 +431,15 @@ osetregid(p, uap, retval) } #endif +struct setgroups_args { + u_int gidsetsize; + int *gidset; +}; + /* ARGSUSED */ setgroups(p, uap, retval) struct proc *p; - struct args { - u_int gidsetsize; - int *gidset; - } *uap; + struct setgroups_args *uap; int *retval; { register struct pcred *pc = p->p_cred; @@ -544,13 +565,16 @@ crdup(cr) /* * Get login name, if available. */ + +struct getlogin_args { + char *namebuf; + u_int namelen; +}; + /* ARGSUSED */ getlogin(p, uap, retval) struct proc *p; - struct args { - char *namebuf; - u_int namelen; - } *uap; + struct getlogin_args *uap; int *retval; { @@ -563,12 +587,15 @@ getlogin(p, uap, retval) /* * Set login name. */ + +struct setlogin_args { + char *namebuf; +}; + /* ARGSUSED */ setlogin(p, uap, retval) struct proc *p; - struct args { - char *namebuf; - } *uap; + struct setlogin_args *uap; int *retval; { int error; diff --git a/sys/kern/kern_resource.c b/sys/kern/kern_resource.c index 4c9b4986bd..9c398d1960 100644 --- a/sys/kern/kern_resource.c +++ b/sys/kern/kern_resource.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kern_resource.c 7.13 (Berkeley) 5/9/91 + * from: @(#)kern_resource.c 7.13 (Berkeley) 5/9/91 + * $Id: kern_resource.c,v 1.5 1993/10/16 15:24:26 rgrimes Exp $ */ #include "param.h" @@ -44,12 +45,14 @@ * Resource controls and accounting. */ +struct getpriority_args { + int which; + int who; +}; + getpriority(curp, uap, retval) struct proc *curp; - register struct args { - int which; - int who; - } *uap; + register struct getpriority_args *uap; int *retval; { register struct proc *p; @@ -100,14 +103,16 @@ getpriority(curp, uap, retval) return (0); } +struct setpriority_args { + int which; + int who; + int prio; +}; + /* ARGSUSED */ setpriority(curp, uap, retval) struct proc *curp; - register struct args { - int which; - int who; - int prio; - } *uap; + register struct setpriority_args *uap; int *retval; { register struct proc *p; @@ -179,18 +184,20 @@ donice(curp, chgp, n) return (0); } +struct setrlimit_args { + u_int which; + struct rlimit *lim; +}; + /* ARGSUSED */ setrlimit(p, uap, retval) struct proc *p; - register struct args { - u_int which; - struct rlimit *lim; - } *uap; + register struct setrlimit_args *uap; int *retval; { struct rlimit alim; register struct rlimit *alimp; - extern unsigned maxdmap; + extern int maxfdescs; int error; if (uap->which >= RLIM_NLIMITS) @@ -211,17 +218,23 @@ setrlimit(p, uap, retval) switch (uap->which) { case RLIMIT_DATA: - if (alim.rlim_cur > maxdmap) - alim.rlim_cur = maxdmap; - if (alim.rlim_max > maxdmap) - alim.rlim_max = maxdmap; + if (alim.rlim_cur > MAXDSIZ) + alim.rlim_cur = MAXDSIZ; + if (alim.rlim_max > MAXDSIZ) + alim.rlim_max = MAXDSIZ; break; + case RLIMIT_OFILE: + if (alim.rlim_cur > maxfdescs) + alim.rlim_cur = maxfdescs; + if (alim.rlim_max > maxfdescs) + alim.rlim_max = maxfdescs; + break; case RLIMIT_STACK: - if (alim.rlim_cur > maxdmap) - alim.rlim_cur = maxdmap; - if (alim.rlim_max > maxdmap) - alim.rlim_max = maxdmap; + if (alim.rlim_cur > MAXSSIZ) + alim.rlim_cur = MAXSSIZ; + if (alim.rlim_max > MAXSSIZ) + alim.rlim_max = MAXSSIZ; /* * Stack is allocated to the max at exec time with only * "rlim_cur" bytes accessible. If stack limit is going @@ -254,13 +267,15 @@ setrlimit(p, uap, retval) return (0); } +struct getrlimit_args { + u_int which; + struct rlimit *rlp; +}; + /* ARGSUSED */ getrlimit(p, uap, retval) struct proc *p; - register struct args { - u_int which; - struct rlimit *rlp; - } *uap; + register struct getrlimit_args *uap; int *retval; { @@ -270,13 +285,15 @@ getrlimit(p, uap, retval) sizeof (struct rlimit))); } +struct getrusage_args { + int who; + struct rusage *rusage; +}; + /* ARGSUSED */ getrusage(p, uap, retval) register struct proc *p; - register struct args { - int who; - struct rusage *rusage; - } *uap; + register struct getrusage_args *uap; int *retval; { register struct rusage *rup; @@ -316,7 +333,7 @@ ruadd(ru, ru2) if (ru->ru_maxrss < ru2->ru_maxrss) ru->ru_maxrss = ru2->ru_maxrss; ip = &ru->ru_first; ip2 = &ru2->ru_first; - for (i = &ru->ru_last - &ru->ru_first; i > 0; i--) + for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--) /* Yuval fix */ *ip++ += *ip2++; } diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index c5fecdabd4..6c1612fcf9 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kern_sig.c 7.35 (Berkeley) 6/28/91 + * from: @(#)kern_sig.c 7.35 (Berkeley) 6/28/91 + * $Id$ */ #define SIGPROP /* include signal properties table */ @@ -39,6 +40,8 @@ #include "resourcevar.h" #include "namei.h" #include "vnode.h" +#include "mount.h" +#include "filedesc.h" #include "proc.h" #include "systm.h" #include "timeb.h" @@ -67,14 +70,16 @@ (pc)->pc_ucred->cr_uid == (q)->p_ucred->cr_uid || \ ((signo) == SIGCONT && (q)->p_session == (p)->p_session)) +struct sigaction_args { + int signo; + struct sigaction *nsa; + struct sigaction *osa; +}; + /* ARGSUSED */ sigaction(p, uap, retval) struct proc *p; - register struct args { - int signo; - struct sigaction *nsa; - struct sigaction *osa; - } *uap; + register struct sigaction_args *uap; int *retval; { struct sigaction vec; @@ -218,12 +223,15 @@ execsigs(p) * and return old mask as return value; * the library stub does the rest. */ + +struct sigprocmask_args { + int how; + sigset_t mask; +}; + sigprocmask(p, uap, retval) register struct proc *p; - struct args { - int how; - sigset_t mask; - } *uap; + struct sigprocmask_args *uap; int *retval; { int error = 0; @@ -267,14 +275,17 @@ sigpending(p, uap, retval) /* * Generalized interface signal handler, 4.3-compatible. */ + +struct osigvec_args { + int signo; + struct sigvec *nsv; + struct sigvec *osv; +}; + /* ARGSUSED */ osigvec(p, uap, retval) struct proc *p; - register struct args { - int signo; - struct sigvec *nsv; - struct sigvec *osv; - } *uap; + register struct osigvec_args *uap; int *retval; { struct sigvec vec; @@ -312,11 +323,13 @@ osigvec(p, uap, retval) return (0); } +struct osigblock_args { + int mask; +}; + osigblock(p, uap, retval) register struct proc *p; - struct args { - int mask; - } *uap; + struct osigblock_args *uap; int *retval; { @@ -327,11 +340,13 @@ osigblock(p, uap, retval) return (0); } +struct osigsetmask_args { + int mask; +}; + osigsetmask(p, uap, retval) struct proc *p; - struct args { - int mask; - } *uap; + struct osigsetmask_args *uap; int *retval; { @@ -348,12 +363,15 @@ osigsetmask(p, uap, retval) * in the meantime. Note nonstandard calling convention: * libc stub passes mask, not pointer, to save a copyin. */ + +struct sigsuspend_args { + sigset_t mask; +}; + /* ARGSUSED */ sigsuspend(p, uap, retval) register struct proc *p; - struct args { - sigset_t mask; - } *uap; + struct sigsuspend_args *uap; int *retval; { register struct sigacts *ps = p->p_sigacts; @@ -373,13 +391,15 @@ sigsuspend(p, uap, retval) return (EINTR); } +struct sigstack_args { + struct sigstack *nss; + struct sigstack *oss; +}; + /* ARGSUSED */ sigstack(p, uap, retval) struct proc *p; - register struct args { - struct sigstack *nss; - struct sigstack *oss; - } *uap; + register struct sigstack_args *uap; int *retval; { struct sigstack ss; @@ -394,13 +414,15 @@ sigstack(p, uap, retval) return (error); } +struct kill_args { + int pid; + int signo; +}; + /* ARGSUSED */ kill(cp, uap, retval) register struct proc *cp; - register struct args { - int pid; - int signo; - } *uap; + register struct kill_args *uap; int *retval; { register struct proc *p; @@ -431,13 +453,16 @@ kill(cp, uap, retval) } #ifdef COMPAT_43 + +struct okillpg_args { + int pgid; + int signo; +}; + /* ARGSUSED */ okillpg(p, uap, retval) struct proc *p; - register struct args { - int pgid; - int signo; - } *uap; + register struct okillpg_args *uap; int *retval; { @@ -998,14 +1023,19 @@ sigexit(p, sig) if (coredump(p) == 0) sig |= WCOREFLAG; } - exit(p, W_EXITCODE(0, sig)); + kexit(p, W_EXITCODE(0, sig)); /* NOTREACHED */ } /* * Create a core dump. - * The file name is "core.progname". - * Core dumps are not created if the process is setuid. + * The file name is "progname.core". + * Core dumps are not created if: + * the process is setuid, + * we are on a filesystem mounted with MNT_NOCORE, + * a file already exists and is not a core file, + * or was not produced from the same program, + * the link count to the corefile is > 1. */ coredump(p) register struct proc *p; @@ -1015,9 +1045,9 @@ coredump(p) register struct ucred *cred = pcred->pc_ucred; register struct vmspace *vm = p->p_vmspace; struct vattr vattr; - int error, error1; + int error, error1, exists; struct nameidata nd; - char name[MAXCOMLEN+6]; /* core.progname */ + char name[MAXCOMLEN+6]; /* progname.core */ if (pcred->p_svuid != pcred->p_ruid || pcred->p_svgid != pcred->p_rgid) @@ -1025,10 +1055,19 @@ coredump(p) if (ctob(UPAGES + vm->vm_dsize + vm->vm_ssize) >= p->p_rlimit[RLIMIT_CORE].rlim_cur) return (EFAULT); - sprintf(name, "core.%s", p->p_comm); + if (p->p_fd->fd_cdir->v_mount->mnt_flag & MNT_NOCORE) + return (EFAULT); + + sprintf(name, "%s.core", p->p_comm); nd.ni_dirp = name; nd.ni_segflg = UIO_SYSSPACE; - if (error = vn_open(&nd, p, O_CREAT|FWRITE, 0644)) + if ((error = vn_open(&nd, p, FWRITE, 0644)) == 0) + exists = 1; + else + exists = 0; + if (error == ENOENT) + error = vn_open(&nd, p, O_CREAT | FWRITE, 0644); + if (error) return (error); vp = nd.ni_vp; if (vp->v_type != VREG || VOP_GETATTR(vp, &vattr, cred, p) || @@ -1036,6 +1075,19 @@ coredump(p) error = EFAULT; goto out; } + if (exists) { /* if file already exists, look if it's a coredump */ + struct user userbuf; /* XXX */ + error = vn_rdwr(UIO_READ, vp, (caddr_t)&userbuf, sizeof(userbuf), + (off_t)0, UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, cred, + (int *)NULL, p); + if (error || (vattr.va_size != ctob(UPAGES + + userbuf.u_kproc.kp_eproc.e_vm.vm_dsize + + userbuf.u_kproc.kp_eproc.e_vm.vm_ssize)) || + strcmp(p->p_comm, userbuf.u_kproc.kp_proc.p_comm)) { + error = EFAULT; + goto out; + } + } VATTR_NULL(&vattr); vattr.va_size = 0; VOP_SETATTR(vp, &vattr, cred, p); diff --git a/sys/kern/kern_subr.c b/sys/kern/kern_subr.c index 7c8c64eae9..aa48658f3f 100644 --- a/sys/kern/kern_subr.c +++ b/sys/kern/kern_subr.c @@ -30,16 +30,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kern_subr.c 7.7 (Berkeley) 4/15/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00081 - * -------------------- ----- ---------------------- - * - * 07 Feb 93 Julian Elischer Moverd strcmp here where it belongs + * from: @(#)kern_subr.c 7.7 (Berkeley) 4/15/91 + * $Id$ */ -static char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/kern_subr.c,v 1.3 92/01/21 21:29:28 william Exp $"; #include "param.h" #include "systm.h" diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c index ee384eda51..4d3ca14115 100644 --- a/sys/kern/kern_synch.c +++ b/sys/kern/kern_synch.c @@ -31,14 +31,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kern_synch.c 7.18 (Berkeley) 6/27/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00077 - * -------------------- ----- ---------------------- - * - * 11 Dec 92 Williams Jolitz Fixed panic:remrq hangs + * from: @(#)kern_synch.c 7.18 (Berkeley) 6/27/91 + * $Id$ */ #include "param.h" diff --git a/sys/kern/kern_time.c b/sys/kern/kern_time.c index e5291f23fb..cf7964dd29 100644 --- a/sys/kern/kern_time.c +++ b/sys/kern/kern_time.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kern_time.c 7.15 (Berkeley) 3/17/91 + * from: @(#)kern_time.c 7.15 (Berkeley) 3/17/91 + * $Id$ */ #include "param.h" @@ -50,13 +51,15 @@ * timers when they expire. */ +struct gettimeofday_args { + struct timeval *tp; + struct timezone *tzp; +}; + /* ARGSUSED */ gettimeofday(p, uap, retval) struct proc *p; - register struct args { - struct timeval *tp; - struct timezone *tzp; - } *uap; + register struct gettimeofday_args *uap; int *retval; { struct timeval atv; @@ -74,13 +77,15 @@ gettimeofday(p, uap, retval) return (error); } +struct settimeofday_args { + struct timeval *tv; + struct timezone *tzp; +}; + /* ARGSUSED */ settimeofday(p, uap, retval) struct proc *p; - struct args { - struct timeval *tv; - struct timezone *tzp; - } *uap; + struct settimeofday_args *uap; int *retval; { struct timeval atv; @@ -109,13 +114,15 @@ int tickdelta; /* current clock skew, us. per tick */ long timedelta; /* unapplied time correction, us. */ long bigadj = 1000000; /* use 10x skew above bigadj us. */ +struct adjtime_args { + struct timeval *delta; + struct timeval *olddelta; +}; + /* ARGSUSED */ adjtime(p, uap, retval) struct proc *p; - register struct args { - struct timeval *delta; - struct timeval *olddelta; - } *uap; + register struct adjtime_args *uap; int *retval; { struct timeval atv, oatv; @@ -171,13 +178,16 @@ adjtime(p, uap, retval) * real time timers .it_interval. Rather, we compute the next time in * absolute time the timer should go off. */ + +struct getitimer_args { + u_int which; + struct itimerval *itv; +}; + /* ARGSUSED */ getitimer(p, uap, retval) struct proc *p; - register struct args { - u_int which; - struct itimerval *itv; - } *uap; + register struct getitimer_args *uap; int *retval; { struct itimerval aitv; @@ -206,13 +216,15 @@ getitimer(p, uap, retval) sizeof (struct itimerval))); } +struct setitimer_args { + u_int which; + struct itimerval *itv, *oitv; +}; + /* ARGSUSED */ setitimer(p, uap, retval) struct proc *p; - register struct args { - u_int which; - struct itimerval *itv, *oitv; - } *uap; + register struct setitimer_args *uap; int *retval; { struct itimerval aitv; diff --git a/sys/kern/kern_xxx.c b/sys/kern/kern_xxx.c index ab93812ed6..a0a1e34883 100644 --- a/sys/kern/kern_xxx.c +++ b/sys/kern/kern_xxx.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kern_xxx.c 7.17 (Berkeley) 4/20/91 + * from: @(#)kern_xxx.c 7.17 (Berkeley) 4/20/91 + * $Id: kern_xxx.c,v 1.4 1993/10/16 15:24:35 rgrimes Exp $ */ #include "param.h" @@ -38,6 +39,7 @@ #include "kernel.h" #include "proc.h" #include "reboot.h" +#include "utsname.h" /* ARGSUSED */ gethostid(p, uap, retval) @@ -50,12 +52,14 @@ gethostid(p, uap, retval) return (0); } +struct sethostid_args { + long hostid; +}; + /* ARGSUSED */ sethostid(p, uap, retval) struct proc *p; - struct args { - long hostid; - } *uap; + struct sethostid_args *uap; int *retval; { int error; @@ -66,13 +70,15 @@ sethostid(p, uap, retval) return (0); } +struct gethostname_args { + char *hostname; + u_int len; +}; + /* ARGSUSED */ gethostname(p, uap, retval) struct proc *p; - struct args { - char *hostname; - u_int len; - } *uap; + struct gethostname_args *uap; int *retval; { @@ -81,13 +87,15 @@ gethostname(p, uap, retval) return (copyout((caddr_t)hostname, (caddr_t)uap->hostname, uap->len)); } +struct sethostname_args { + char *hostname; + u_int len; +}; + /* ARGSUSED */ sethostname(p, uap, retval) struct proc *p; - register struct args { - char *hostname; - u_int len; - } *uap; + register struct sethostname_args *uap; int *retval; { int error; @@ -102,12 +110,72 @@ sethostname(p, uap, retval) return (error); } +struct getdomainname_args { + char *domainname; + u_int len; +}; + +/* ARGSUSED */ +int +getdomainname(p, uap, retval) + struct proc *p; + struct getdomainname_args *uap; + int *retval; +{ + if (uap->len > domainnamelen + 1) + uap->len = domainnamelen + 1; + return (copyout((caddr_t)domainname, (caddr_t)uap->domainname, uap->len)); +} + +struct setdomainname_args { + char *domainname; + u_int len; +}; + +/* ARGSUSED */ +int +setdomainname(p, uap, retval) + struct proc *p; + struct setdomainname_args *uap; + int *retval; +{ + int error; + + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + if (uap->len > sizeof (domainname) - 1) + return EINVAL; + domainnamelen = uap->len; + error = copyin((caddr_t)uap->domainname, domainname, uap->len); + domainname[domainnamelen] = 0; + return (error); +} + +struct uname_args { + struct utsname *name; +}; + +/* ARGSUSED */ +int +uname(p, uap, retval) + struct proc *p; + struct uname_args *uap; + int *retval; +{ + bcopy(hostname, utsname.nodename, sizeof(utsname.nodename)); + utsname.nodename[sizeof(utsname.nodename)-1] = '\0'; + return (copyout((caddr_t)&utsname, (caddr_t)uap->name, + sizeof(struct utsname))); +} + +struct reboot_args { + int opt; +}; + /* ARGSUSED */ reboot(p, uap, retval) struct proc *p; - struct args { - int opt; - } *uap; + struct reboot_args *uap; int *retval; { int error; diff --git a/sys/kern/makesyscalls.sh b/sys/kern/makesyscalls.sh new file mode 100644 index 0000000000..38bcafdf44 --- /dev/null +++ b/sys/kern/makesyscalls.sh @@ -0,0 +1,172 @@ +#! /bin/sh - +# from: @(#)makesyscalls.sh 7.6 (Berkeley) 4/20/91 +# $Id$ + +set -e + +# name of compat option: +compat=COMPAT_43 + +# output files: +sysnames="syscalls.c" +syshdr="../sys/syscall.h" +syssw="init_sysent.c" + +# tmp files: +sysdcl="sysent.dcl" +syscompat="sysent.compat" +sysent="sysent.switch" + +trap "rm $sysdcl $syscompat $sysent" 0 + +case $# in + 0) echo "Usage: $0 input-file" 1>&2 + exit 1 + ;; +esac + +awk < $1 " + BEGIN { + sysdcl = \"$sysdcl\" + syscompat = \"$syscompat\" + sysent = \"$sysent\" + sysnames = \"$sysnames\" + syshdr = \"$syshdr\" + compat = \"$compat\" + infile = \"$1\" + "' + + printf "/*\n * System call switch table.\n *\n" > sysdcl + printf " * DO NOT EDIT-- this file is automatically generated.\n" > sysdcl + + printf "\n#ifdef %s\n", compat > syscompat + printf "#define compat(n, name) n, __CONCAT(o,name)\n\n" > syscompat + + printf "/*\n * System call names.\n *\n" > sysnames + printf " * DO NOT EDIT-- this file is automatically generated.\n" > sysnames + + printf "/*\n * System call numbers.\n *\n" > syshdr + printf " * DO NOT EDIT-- this file is automatically generated.\n" > syshdr + } + NR == 1 { + printf " * created from%s\n */\n\n", $0 > sysdcl + printf "#include \"param.h\"\n" > sysdcl + printf "#include \"systm.h\"\n\n" > sysdcl + printf "int\tnosys();\n\n" > sysdcl + + printf "struct sysent sysent[] = {\n" > sysent + + printf " * created from%s\n */\n\n", $0 > sysnames + printf "char *syscallnames[] = {\n" > sysnames + + printf " * created from%s\n */\n\n", $0 > syshdr + next + } + NF == 0 || $1 ~ /^;/ { + next + } + $1 ~ /^#[ ]*if/ { + print > sysent + print > sysdcl + print > syscompat + print > sysnames + savesyscall = syscall + next + } + $1 ~ /^#[ ]*else/ { + print > sysent + print > sysdcl + print > syscompat + print > sysnames + syscall = savesyscall + next + } + $1 ~ /^#/ { + print > sysent + print > sysdcl + print > syscompat + print > sysnames + next + } + syscall != $1 { + printf "%s: line %d: syscall number out of sync at %d\n", \ + infile, NR, syscall + printf "line is:\n" + print + exit 1 + } + { comment = $4 + for (i = 5; i <= NF; i++) + comment = comment " " $i + if (NF < 5) + $5 = $4 + } + $2 == "STD" { + printf("int\t%s();\n", $4) > sysdcl + printf("\t%d, %s,\t\t\t/* %d = %s */\n", \ + $3, $4, syscall, $5) > sysent + printf("\t\"%s\",\t\t\t/* %d = %s */\n", \ + $5, syscall, $5) > sysnames + printf("#define\tSYS_%s\t%d\n", \ + $5, syscall) > syshdr + syscall++ + next + } + $2 == "COMPAT" { + printf("int\to%s();\n", $4) > syscompat + printf("\tcompat(%d,%s),\t\t/* %d = old %s */\n", \ + $3, $4, syscall, $5) > sysent + printf("\t\"old.%s\",\t\t/* %d = old %s */\n", \ + $5, syscall, $5) > sysnames + printf("\t\t\t\t/* %d is old %s */\n", \ + syscall, comment) > syshdr + syscall++ + next + } + $2 == "LIBCOMPAT" { + printf("int\to%s();\n", $4) > syscompat + printf("\tcompat(%d,%s),\t\t/* %d = old %s */\n", \ + $3, $4, syscall, $5) > sysent + printf("\t\"old.%s\",\t\t/* %d = old %s */\n", \ + $5, syscall, $5) > sysnames + printf("#define\tSYS_%s\t%d\t/* compatibility; still used by libc */\n", \ + $5, syscall) > syshdr + syscall++ + next + } + $2 == "OBSOL" { + printf("\t0, nosys,\t\t\t/* %d = obsolete %s */\n", \ + syscall, comment) > sysent + printf("\t\"obs_%s\",\t\t\t/* %d = obsolete %s */\n", \ + $4, syscall, comment) > sysnames + printf("\t\t\t\t/* %d is obsolete %s */\n", \ + syscall, comment) > syshdr + syscall++ + next + } + $2 == "UNIMPL" { + printf("\t0, nosys,\t\t\t/* %d = %s */\n", \ + syscall, comment) > sysent + printf("\t\"#%d\",\t\t\t/* %d = %s */\n", \ + syscall, syscall, comment) > sysnames + syscall++ + next + } + { + printf "%s: line %d: unrecognized keyword %s\n", infile, NR, $2 + exit 1 + } + END { + printf("\n#else /* %s */\n", compat) > syscompat + printf("#define compat(n, name) 0, nosys\n") > syscompat + printf("#endif /* %s */\n\n", compat) > syscompat + + printf("};\n\n") > sysent + printf("int\tnsysent = sizeof(sysent) / sizeof(sysent[0]);\n") > sysent + + printf("};\n") > sysnames + } ' + +cat $sysdcl $syscompat $sysent >$syssw + +chmod 444 $sysnames $syshdr $syssw diff --git a/sys/kern/spec_vnops.c b/sys/kern/spec_vnops.c index 65c5766744..d1be0ab627 100644 --- a/sys/kern/spec_vnops.c +++ b/sys/kern/spec_vnops.c @@ -30,14 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)spec_vnops.c 7.37 (Berkeley) 5/30/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00007 - * -------------------- ----- ---------------------- - * - * 20 Aug 92 David Greenman Fixed incorrect setting of B_AGE on + * from: @(#)spec_vnops.c 7.37 (Berkeley) 5/30/91 + * $Id$ */ #include "param.h" diff --git a/sys/kern/subr_log.c b/sys/kern/subr_log.c index 76347f900c..fac6060ab7 100644 --- a/sys/kern/subr_log.c +++ b/sys/kern/subr_log.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)subr_log.c 7.11 (Berkeley) 3/17/91 + * from: @(#)subr_log.c 7.11 (Berkeley) 3/17/91 + * $Id$ */ /* @@ -38,6 +39,7 @@ */ #include "param.h" +#include "systm.h" #include "proc.h" #include "vnode.h" #include "ioctl.h" @@ -51,7 +53,7 @@ struct logsoftc { int sc_state; /* see above for possibilities */ - struct proc *sc_selp; /* process waiting on select call */ + pid_t sc_sel; /* pid of process waiting on select call 16 Jun 93 */ int sc_pgid; /* process/group for async I/O */ } logsoftc; @@ -91,7 +93,7 @@ logclose(dev, flag) { log_open = 0; logsoftc.sc_state = 0; - logsoftc.sc_selp = 0; + logsoftc.sc_sel = 0; /* 16 Jun 93 */ } /*ARGSUSED*/ @@ -154,7 +156,7 @@ logselect(dev, rw, p) splx(s); return (1); } - logsoftc.sc_selp = p; + logsoftc.sc_sel = p->p_pid; /* 16 Jun 93 */ break; } splx(s); @@ -167,9 +169,9 @@ logwakeup() if (!log_open) return; - if (logsoftc.sc_selp) { - selwakeup(logsoftc.sc_selp, 0); - logsoftc.sc_selp = 0; + if (logsoftc.sc_sel) { /* 16 Jun 93 */ + selwakeup(logsoftc.sc_sel, 0); + logsoftc.sc_sel = 0; } if (logsoftc.sc_state & LOG_ASYNC) { if (logsoftc.sc_pgid < 0) diff --git a/sys/kern/subr_mcount.c b/sys/kern/subr_mcount.c index 16413ebef8..89e76e97fd 100644 --- a/sys/kern/subr_mcount.c +++ b/sys/kern/subr_mcount.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)subr_mcount.c 7.10 (Berkeley) 5/7/91 + * from: @(#)subr_mcount.c 7.10 (Berkeley) 5/7/91 + * $Id$ */ #ifdef GPROF @@ -148,23 +149,26 @@ mcount() asm("movl a6@(4),%0" : "=r" (selfpc)); asm("movl a6@(0)@(4),%0" : "=r" (frompcindex)); #endif +#if defined(i386) + /* + * selfpc = pc pushed by mcount call + */ + asm("movl 4(%%ebp),%0" : "=r" (selfpc)); + /* + * frompcindex = pc pushed by jsr into self. + * in GCC, the caller's stack frame has already been built, so we + * have to chase the base pointer to find caller's raddr. + */ + asm("movl (%%ebp),%0" : "=r" (frompcindex)); + frompcindex = ((unsigned short **)frompcindex)[1]; +#endif /* i386 */ #else #if defined(vax) asm(" movl (sp), r11"); /* selfpc = ... (jsb frame) */ asm(" movl 16(fp), r10"); /* frompcindex = (calls frame) */ #endif #if defined(i386) - /* - * selfpc = pc pushed by mcount call - */ - asm("movl 4(%%ebp),%0" : "=r" (selfpc)); - /* - * frompcindex = pc pushed by jsr into self. - * in GCC, the caller's stack frame has already been built, so we - * have to chase the base pointer to find caller's raddr. - */ - asm("movl (%%ebp),%0" : "=r" (frompcindex)); - frompcindex = ((unsigned short **)frompcindex)[1]; + Fix Me!! #endif /* i386 */ #if defined(tahoe) asm(" movl -8(fp),r12"); /* selfpc = callf frame */ diff --git a/sys/kern/subr_prf.c b/sys/kern/subr_prf.c index 248b85a97d..e8ecca2369 100644 --- a/sys/kern/subr_prf.c +++ b/sys/kern/subr_prf.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)subr_prf.c 7.30 (Berkeley) 6/29/91 + * from: @(#)subr_prf.c 7.30 (Berkeley) 6/29/91 + * $Id$ */ #include "param.h" @@ -64,8 +65,10 @@ struct tty *constty; /* pointer to console "window" tty */ -#ifdef KADB +#if defined(KADB) || defined(PANICWAIT) extern cngetc(); /* standard console getc */ +#endif +#ifdef KADB int (*v_getc)() = cngetc; /* "" getc from virtual console */ extern cnpoll(); int (*v_poll)() = cnpoll; /* kdb hook to enable input polling */ @@ -77,21 +80,33 @@ static void logpri __P((int level)); static void putchar __P((int ch, int flags, struct tty *tp)); static char *ksprintn __P((u_long num, int base, int *len)); void kprintf __P((const char *fmt, int flags, struct tty *tp, va_list)); +volatile void boot(int bootopt); /* * Variable panicstr contains argument to first call to panic; used * as flag to indicate that the kernel has already called panic. */ -char *panicstr; +const char *panicstr; + +/* + * Message buffer + */ +struct msgbuf *msgbufp; +int msgbufmapped; /* * Panic is called on unresolvable fatal errors. It prints "panic: mesg", * and then reboots. If we are called twice, then we avoid trying to sync * the disks as this often leads to recursive panics. */ +#ifdef __STDC__ +volatile void +panic(const char *msg) +#else void panic(msg) char *msg; +#endif { int bootopt = RB_AUTOBOOT | RB_DUMP; @@ -116,7 +131,14 @@ panic(msg) #if NDDB > 0 Debugger (); #else -/* pg("press key to boot/dump");*/ +#ifdef PANICWAIT + printf("hit any key to boot/dump...\n>"); + cngetc(); +#endif /* PANICWAIT */ +#ifdef PANICDELAY + printf("Waiting 20 secs....\n"); + DELAY(20000000); +#endif /* PANICDELAY */ #endif boot(bootopt); } @@ -272,7 +294,7 @@ logpri(level) putchar('>', TOLOG, NULL); } -void +int #ifdef __STDC__ addlog(const char *fmt, ...) #else @@ -294,11 +316,12 @@ addlog(fmt /*, va_alist */) va_end(ap); } logwakeup(); + return (0); } int consintr = 1; /* ok to handle console interrupts? */ -void +int #ifdef __STDC__ printf(const char *fmt, ...) #else @@ -317,6 +340,8 @@ printf(fmt /*, va_alist */) if (!panicstr) logwakeup(); consintr = savintr; /* reenable interrupts */ + + return 0; /* for compatibility with libc's printf() */ } /* @@ -362,7 +387,7 @@ kprintf(fmt, flags, tp, ap) struct tty *tp; va_list ap; { - register char *p; + register char *p, *p2; register int ch, n; u_long ul; int base, lflag, tmp, width; @@ -396,7 +421,7 @@ reswitch: switch (ch = *(u_char *)fmt++) { case 'b': ul = va_arg(ap, int); p = va_arg(ap, char *); - for (p = ksprintn(ul, *p++, NULL); ch = *p--;) + for (p2 = ksprintn(ul, *p++, NULL); ch = *p2--;) putchar(ch, flags, tp); if (!ul) @@ -474,7 +499,6 @@ putchar(c, flags, tp) int flags; struct tty *tp; { - extern int msgbufmapped; register struct msgbuf *mbp; if (panicstr) @@ -505,8 +529,10 @@ putchar(c, flags, tp) * Scaled down version of sprintf(3). */ #ifdef __STDC__ +int sprintf(char *buf, const char *cfmt, ...) #else +int sprintf(buf, cfmt /*, va_alist */) char *buf, *cfmt; #endif diff --git a/sys/kern/subr_rlist.c b/sys/kern/subr_rlist.c index 3adf5a8195..b8fc075033 100644 --- a/sys/kern/subr_rlist.c +++ b/sys/kern/subr_rlist.c @@ -45,8 +45,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * + * $Id$ */ -static char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/subr_rlist.c,v 1.2 92/01/21 21:29:31 william Exp $"; #include "sys/param.h" #include "sys/cdefs.h" diff --git a/sys/kern/subr_xxx.c b/sys/kern/subr_xxx.c index 9f529957df..d60ca644db 100644 --- a/sys/kern/subr_xxx.c +++ b/sys/kern/subr_xxx.c @@ -30,14 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)subr_xxx.c 7.10 (Berkeley) 4/20/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00158 - * -------------------- ----- ---------------------- - * - * 25 Apr 93 Bruce Evans Support new interrupt code (intr-0.1) + * from: @(#)subr_xxx.c 7.10 (Berkeley) 4/20/91 + * $Id$ */ /* diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c index daf8f6cccb..40a30c806f 100644 --- a/sys/kern/sys_generic.c +++ b/sys/kern/sys_generic.c @@ -30,14 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)sys_generic.c 7.30 (Berkeley) 5/30/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00061 - * -------------------- ----- ---------------------- - * - * 11 Dec 92 Williams Jolitz Fixed tty handling + * from: @(#)sys_generic.c 7.30 (Berkeley) 5/30/91 + * $Id$ */ #include "param.h" @@ -55,17 +49,19 @@ #include "ktrace.h" #endif +struct read_args { + int fdes; + char *cbuf; + unsigned count; +}; + /* * Read system call. */ /* ARGSUSED */ read(p, uap, retval) struct proc *p; - register struct args { - int fdes; - char *cbuf; - unsigned count; - } *uap; + register struct read_args *uap; int *retval; { register struct file *fp; @@ -113,14 +109,17 @@ read(p, uap, retval) /* * Scatter read system call. */ + +struct readv_args { + int fdes; + struct iovec *iovp; + unsigned iovcnt; +}; + /* ARGSUSED */ readv(p, uap, retval) struct proc *p; - register struct args { - int fdes; - struct iovec *iovp; - unsigned iovcnt; - } *uap; + register struct readv_args *uap; int *retval; { register struct file *fp; @@ -201,13 +200,16 @@ done: /* * Write system call */ + +struct write_args { + int fdes; + char *cbuf; + unsigned count; +}; + write(p, uap, retval) struct proc *p; - register struct args { - int fdes; - char *cbuf; - unsigned count; - } *uap; + register struct write_args *uap; int *retval; { register struct file *fp; @@ -259,13 +261,16 @@ write(p, uap, retval) /* * Gather write system call */ + +struct writev_args { + int fdes; + struct iovec *iovp; + unsigned iovcnt; +}; + writev(p, uap, retval) struct proc *p; - register struct args { - int fdes; - struct iovec *iovp; - unsigned iovcnt; - } *uap; + register struct writev_args *uap; int *retval; { register struct file *fp; @@ -349,14 +354,17 @@ done: /* * Ioctl system call */ + +struct ioctl_args { + int fdes; + int cmd; + caddr_t cmarg; +}; + /* ARGSUSED */ ioctl(p, uap, retval) struct proc *p; - register struct args { - int fdes; - int cmd; - caddr_t cmarg; - } *uap; + register struct ioctl_args *uap; int *retval; { register struct file *fp; @@ -485,18 +493,22 @@ int selwait, nselcoll; /* * Select system call. */ -select(p, uap, retval) - register struct proc *p; - register struct args { - int nd; + +struct select_args { + u_int nd; fd_set *in, *ou, *ex; struct timeval *tv; - } *uap; +}; + +select(p, uap, retval) + register struct proc *p; + register struct select_args *uap; int *retval; { fd_set ibits[3], obits[3]; struct timeval atv; - int s, ncoll, ni, error = 0, timo; + int s, ncoll, error = 0, timo; + u_int ni; bzero((caddr_t)ibits, sizeof(ibits)); bzero((caddr_t)obits, sizeof(obits)); diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index f4c9c06bc6..516fa79344 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -31,17 +31,11 @@ * SUCH DAMAGE. * * from: @(#)sys_process.c 7.22 (Berkeley) 5/11/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00137 - * -------------------- ----- ---------------------- - * - * 04 Sep 92 Paul Kranenburg Fixed copy-on-write checking for pages - * other than anonymous (text pages, etc.) - * 08 Apr 93 Bruce Evans Several VM system fixes + * $Id$ */ +#include + #define IPCREG #include "param.h" #include "proc.h" @@ -49,6 +43,7 @@ #include "buf.h" #include "ptrace.h" +#include "machine/eflags.h" #include "machine/reg.h" #include "machine/psl.h" #include "vm/vm.h" @@ -105,14 +100,17 @@ struct { /* * Process debugging system call. */ + +struct ptrace_args { + int req; + int pid; + int *addr; + int data; +}; + ptrace(curp, uap, retval) struct proc *curp; - register struct args { - int req; - int pid; - int *addr; - int data; - } *uap; + register struct ptrace_args *uap; int *retval; { struct proc *p; @@ -264,6 +262,10 @@ procxmt(p) register struct proc *p; { int i, *xreg, rv = 0; +#ifdef i386 + int new_eflags, old_cs, old_ds, old_es, old_ss, old_eflags; + int *regs; +#endif /* Are we still being traced? */ if ((p->p_flag & STRC) == 0) @@ -307,6 +309,8 @@ procxmt(p) /* * XXX - the useracc check is stronger than the vm * checks because the user page tables are in the map. + * Anyway, most of this can be removed now that COW + * works. */ if (!useracc(ipc.addr, sizeof(ipc.data), B_READ) || vm_region(&p->p_vmspace->vm_map, &addr, &size, @@ -333,11 +337,92 @@ procxmt(p) } case PT_WRITE_U: +#ifdef i386 + regs = p->p_regs; + /* + * XXX - privileged kernel state is scattered all over the + * user area. Only allow write access to areas known to + * be safe. + */ +#define GO_IF_SAFE(min, size) \ + if ((u_int)ipc.addr >= (min) \ + && (u_int)ipc.addr <= (min) + (size) - sizeof(int)) \ + goto pt_write_u + /* + * Allow writing entire FPU state. + */ + GO_IF_SAFE(offsetof(struct user, u_pcb) + + offsetof(struct pcb, pcb_savefpu), + sizeof(struct save87)); + /* + * Allow writing ordinary registers. Changes to segment + * registers and to some bits in %eflags will be silently + * ignored. Such changes ought to be an error. + */ +/* + * XXX - there is no define for the base of the user area except USRSTACK. + * XXX - USRSTACK is not the base of the user stack. It is the base of the + * user area. + */ +#define USER_OFF(va) ((u_int)(va) - USRSTACK) + GO_IF_SAFE(USER_OFF(regs), + (curpcb->pcb_flags & FM_TRAP ? tSS + 1 : sSS + 1) + * sizeof *regs); + ipc.error = EFAULT; + break; +#else if ((u_int)ipc.addr > UPAGES * NBPG - sizeof(int)) { ipc.error = EFAULT; break; } +#endif + pt_write_u: +#ifdef i386 + if (curpcb->pcb_flags & FM_TRAP) { + old_cs = regs[tCS]; + old_ds = regs[tES]; + old_es = regs[tES]; + old_ss = regs[tSS]; + old_eflags = regs[tEFLAGS]; + } else { + old_cs = regs[sCS]; + old_ss = regs[sSS]; + old_eflags = regs[sEFLAGS]; + } +#endif *(int *)((u_int)p->p_addr + (u_int)ipc.addr) = ipc.data; +#ifdef i386 + /* + * Don't allow segment registers to change (although they can + * be changed directly to certain values). + * Don't allow privileged bits in %eflags to change. Users + * have privilege to change TF and NT although although they + * usually shouldn't. + * XXX - fix PT_SETREGS. + * XXX - simplify. Maybe copy through a temporary struct. + * Watch out for problems when ipc.addr is not a multiple + * of the register size. + */ +#define EFL_UNPRIVILEGED (EFL_CF | EFL_PF | EFL_AF | EFL_ZF | EFL_SF \ + | EFL_TF | EFL_DF | EFL_OF | EFL_NT) + if (curpcb->pcb_flags & FM_TRAP) { + regs[tCS] = old_cs; + regs[tDS] = old_ds; + regs[tES] = old_es; + regs[tSS] = old_es; + new_eflags = regs[tEFLAGS]; + regs[tEFLAGS] + = (new_eflags & EFL_UNPRIVILEGED) + | (old_eflags & ~EFL_UNPRIVILEGED); + } else { + regs[sCS] = old_cs; + regs[sSS] = old_ss; + new_eflags = regs[sEFLAGS]; + regs[sEFLAGS] + = (new_eflags & EFL_UNPRIVILEGED) + | (old_eflags & ~EFL_UNPRIVILEGED); + } +#endif break; case PT_CONTINUE: @@ -418,7 +503,7 @@ procxmt(p) wakeup((caddr_t)&ipc); if (rv == 2) - exit(p, 0); /*???*/ + kexit(p, 0); /*???*/ return rv; } @@ -426,15 +511,18 @@ procxmt(p) /* * Enable process profiling system call. */ + +struct profil_args { + short *bufbase; /* base of data buffer */ + unsigned bufsize; /* size of data buffer */ + unsigned pcoffset; /* pc offset (for subtraction) */ + unsigned pcscale; /* scaling factor for offset pc */ +}; + /* ARGSUSED */ profil(p, uap, retval) struct proc *p; - register struct args { - short *bufbase; /* base of data buffer */ - unsigned bufsize; /* size of data buffer */ - unsigned pcoffset; /* pc offset (for subtraction) */ - unsigned pcscale; /* scaling factor for offset pc */ - } *uap; + register struct profil_args *uap; int *retval; { /* from looking at man pages, and include files, looks like diff --git a/sys/kern/sys_socket.c b/sys/kern/sys_socket.c index 241a12f819..59d2cfee0c 100644 --- a/sys/kern/sys_socket.c +++ b/sys/kern/sys_socket.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)sys_socket.c 7.11 (Berkeley) 4/16/91 + * from: @(#)sys_socket.c 7.11 (Berkeley) 4/16/91 + * $Id$ */ #include "param.h" diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c index 2753835cb0..4fe1c43354 100644 --- a/sys/kern/syscalls.c +++ b/sys/kern/syscalls.c @@ -2,7 +2,7 @@ * System call names. * * DO NOT EDIT-- this file is automatically generated. - * created from @(#)syscalls.master 7.26 (Berkeley) 3/25/91 + * created from $Id: syscalls.master,v 1.5 1993/10/24 06:19:58 paul Exp $ */ char *syscallnames[] = { @@ -193,9 +193,9 @@ char *syscallnames[] = { "#160", /* 160 = nosys */ "#161", /* 161 = nosys */ #endif - "#162", /* 162 = nosys */ - "#163", /* 163 = nosys */ - "#164", /* 164 = nosys */ + "getdomainname", /* 162 = getdomainname */ + "setdomainname", /* 163 = setdomainname */ + "uname", /* 164 = uname */ "#165", /* 165 = nosys */ "#166", /* 166 = nosys */ "#167", /* 167 = nosys */ diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master new file mode 100644 index 0000000000..82595f479d --- /dev/null +++ b/sys/kern/syscalls.master @@ -0,0 +1,255 @@ + $Id: syscalls.master,v 1.4 1993/10/16 15:22:35 rgrimes Exp $ +; from: @(#)syscalls.master 7.26 (Berkeley) 3/25/91 +; System call name/number master file. +; Processed to created init_sysent.c, syscalls.c and syscall.h. + +; Columns: number type nargs name altname/comments +; number system call number, must be in order +; type one of STD, OBSOL, UNIMPL, COMPAT +; nargs number of arguments +; name name of syscall routine +; altname name of system call if different +; for UNIMPL/OBSOL, name continues with comments + +; types: +; STD always included +; COMPAT included on COMPAT #ifdef +; LIBCOMPAT included on COMPAT #ifdef, and placed in syscall.h +; OBSOL obsolete, not included in system, only specifies name +; UNIMPL not implemented, placeholder only + +; #ifdef's, etc. may be included, and are copied to the output files. + +; Reserved/unimplemented system calls in the range 0-150 inclusive +; are reserved for use in future Berkeley releases. +; Additional system calls implemented in vendor and other +; redistributions should be placed in the reserved range at the end +; of the current calls. + +0 UNIMPL 0 indir or out-of-range +1 STD 1 rexit exit +2 STD 0 fork +3 STD 3 read +4 STD 3 write +5 STD 3 open +6 STD 1 close +7 STD 4 wait4 +8 COMPAT 2 creat +9 STD 2 link +10 STD 1 unlink +11 OBSOL 2 execv +12 STD 1 chdir +13 STD 1 fchdir +14 STD 3 mknod +15 STD 2 chmod +16 STD 3 chown +17 STD 1 obreak break +18 STD 3 getfsstat +19 STD 3 lseek +20 STD 0 getpid +21 STD 4 mount +22 STD 2 unmount +23 STD 1 setuid +24 STD 0 getuid +25 STD 0 geteuid +26 STD 4 ptrace +27 STD 3 recvmsg +28 STD 3 sendmsg +29 STD 6 recvfrom +30 STD 3 accept +31 STD 3 getpeername +32 STD 3 getsockname +33 STD 2 saccess access +34 STD 2 chflags +35 STD 2 fchflags +36 STD 0 sync +37 STD 2 kill +38 STD 2 stat +39 STD 0 getppid +40 STD 2 lstat +41 STD 2 dup +42 STD 0 pipe +43 STD 0 getegid +44 STD 4 profil +#ifdef KTRACE +45 STD 4 ktrace +#else +45 UNIMPL 0 ktrace +#endif +46 STD 3 sigaction +47 STD 0 getgid +48 STD 2 sigprocmask +49 STD 2 getlogin +50 STD 1 setlogin +51 STD 1 sysacct acct +52 STD 0 sigpending +#ifdef notyet +53 STD 3 sigaltstack +#else +53 UNIMPL 3 sigaltstack +#endif +54 STD 3 ioctl +55 STD 1 reboot +56 STD 1 revoke +57 STD 2 symlink +58 STD 3 readlink +59 STD 3 execve +60 STD 1 umask +61 STD 1 chroot +62 STD 2 fstat +63 STD 4 getkerninfo +64 STD 0 getpagesize +65 STD 2 msync +66 STD 0 vfork +67 OBSOL 0 vread +68 OBSOL 0 vwrite +69 STD 1 sbrk +70 STD 1 sstk +71 STD 6 smmap mmap +72 STD 1 ovadvise vadvise +73 STD 2 munmap +74 STD 3 mprotect +75 STD 3 madvise +76 OBSOL 0 vhangup +77 OBSOL 0 vlimit +78 STD 3 mincore +79 STD 2 getgroups +80 STD 2 setgroups +81 STD 0 getpgrp +82 STD 2 setpgid +83 STD 3 setitimer +84 COMPAT 0 wait +85 STD 1 swapon +86 STD 2 getitimer +87 STD 2 gethostname +88 STD 2 sethostname +89 STD 0 getdtablesize +90 STD 2 dup2 +91 UNIMPL 2 getdopt +92 STD 3 fcntl +93 STD 5 select +94 UNIMPL 2 setdopt +95 STD 1 fsync +96 STD 3 setpriority +97 STD 3 socket +98 STD 3 connect +99 COMPAT 3 accept +100 STD 2 getpriority +101 COMPAT 4 send +102 COMPAT 4 recv +103 STD 1 sigreturn +104 STD 3 bind +105 STD 5 setsockopt +106 STD 2 listen +107 OBSOL 0 vtimes +108 COMPAT 3 sigvec +109 COMPAT 1 sigblock +110 COMPAT 1 sigsetmask +111 STD 1 sigsuspend +112 STD 2 sigstack +113 COMPAT 3 recvmsg +114 COMPAT 3 sendmsg +#ifdef TRACE +115 STD 2 vtrace +#else +115 OBSOL 2 vtrace +#endif +116 STD 2 gettimeofday +117 STD 2 getrusage +118 STD 5 getsockopt +#ifdef vax +119 STD 1 resuba +#else +119 UNIMPL 0 nosys +#endif +120 STD 3 readv +121 STD 3 writev +122 STD 2 settimeofday +123 STD 3 fchown +124 STD 2 fchmod +125 COMPAT 6 recvfrom +126 LIBCOMPAT 2 setreuid +127 LIBCOMPAT 2 setregid +128 STD 2 rename +129 STD 2 truncate +130 STD 2 ftruncate +131 STD 2 flock +132 STD 2 mkfifo +133 STD 6 sendto +134 STD 2 shutdown +135 STD 5 socketpair +136 STD 2 mkdir +137 STD 1 rmdir +138 STD 2 utimes +139 OBSOL 0 4.2 sigreturn +140 STD 2 adjtime +141 COMPAT 3 getpeername +142 STD 0 gethostid +143 STD 1 sethostid +144 STD 2 getrlimit +145 STD 2 setrlimit +146 COMPAT 2 killpg +147 STD 0 setsid +148 STD 4 quotactl +149 COMPAT 4 quota +150 COMPAT 3 getsockname + +; Syscalls 151-180 inclusive are reserved for vendor-specific +; system calls. (This includes various calls added for compatibity +; with other Unix variants.) +; Some of these calls are now supported by BSD... +151 UNIMPL 0 nosys +152 UNIMPL 0 nosys +153 UNIMPL 0 nosys +154 UNIMPL 0 nosys +#ifdef NFS +155 STD 5 nfssvc +#else +155 UNIMPL 0 nosys +#endif +156 STD 4 getdirentries +157 STD 2 statfs +158 STD 2 fstatfs +159 UNIMPL 0 nosys +#ifdef NFS +160 STD 0 async_daemon +161 STD 2 getfh +#else +160 UNIMPL 0 nosys +161 UNIMPL 0 nosys +#endif +162 STD 2 getdomainname +163 STD 2 setdomainname +164 STD 1 uname +165 UNIMPL 0 nosys +166 UNIMPL 0 nosys +167 UNIMPL 0 nosys +168 UNIMPL 0 nosys +169 UNIMPL 0 nosys +170 UNIMPL 0 nosys +#ifdef SYSVSHM +171 STD 4 shmsys +#else +171 UNIMPL 0 nosys +#endif +172 UNIMPL 0 nosys +173 UNIMPL 0 nosys +174 UNIMPL 0 nosys +175 UNIMPL 0 nosys +176 UNIMPL 0 nosys +177 UNIMPL 0 nosys +178 UNIMPL 0 nosys +179 UNIMPL 0 nosys +180 UNIMPL 0 nosys + +; Syscalls 180-199 are used by/reserved for BSD +181 STD 1 setgid +182 STD 1 setegid +183 STD 1 seteuid +184 UNIMPL 0 nosys +185 UNIMPL 0 nosys +186 UNIMPL 0 nosys +187 UNIMPL 0 nosys +188 UNIMPL 0 nosys +189 UNIMPL 0 nosys +190 UNIMPL 0 nosys diff --git a/sys/kern/sysv_shm.c b/sys/kern/sysv_shm.c index fca74d3f6b..a1ffc194bf 100644 --- a/sys/kern/sysv_shm.c +++ b/sys/kern/sysv_shm.c @@ -35,9 +35,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * from: Utah $Hdr: uipc_shm.c 1.9 89/08/14$ - * - * @(#)sysv_shm.c 7.15 (Berkeley) 5/13/91 + * from: Utah $Hdr: uipc_shm.c 1.9 89/08/14$ + * from: @(#)sysv_shm.c 7.15 (Berkeley) 5/13/91 + * $Id$ */ /* @@ -105,11 +105,14 @@ shminit() /* * Entry point for all SHM calls */ + +struct shmsys_args { + u_int which; +}; + shmsys(p, uap, retval) struct proc *p; - struct args { - u_int which; - } *uap; + struct shmsys_args *uap; int *retval; { @@ -121,13 +124,16 @@ shmsys(p, uap, retval) /* * Get a shared memory segment */ + +struct shmget_args { + key_t key; + int size; + int shmflg; +}; + shmget(p, uap, retval) struct proc *p; - register struct args { - key_t key; - int size; - int shmflg; - } *uap; + register struct shmget_args *uap; int *retval; { register struct shmid_ds *shp; @@ -179,7 +185,7 @@ shmget(p, uap, retval) shmh->shmh_kva = 0; shmh->shmh_id = (caddr_t)(0xc0000000|rval); /* XXX */ error = vm_mmap(shm_map, &shmh->shmh_kva, ctob(size), - VM_PROT_ALL, MAP_ANON, shmh->shmh_id, 0); + VM_PROT_ALL, VM_PROT_DEFAULT, MAP_ANON, shmh->shmh_id, 0); if (error) { free((caddr_t)shmh, M_SHM); shp->shm_perm.mode = 0; @@ -214,14 +220,17 @@ shmget(p, uap, retval) /* * Shared memory control */ + +struct shmctl_args { + int shmid; + int cmd; + caddr_t buf; +}; + /* ARGSUSED */ shmctl(p, uap, retval) struct proc *p; - register struct args { - int shmid; - int cmd; - caddr_t buf; - } *uap; + register struct shmctl_args *uap; int *retval; { register struct shmid_ds *shp; @@ -283,13 +292,16 @@ shmctl(p, uap, retval) /* * Attach to shared memory segment. */ + +struct shmat_args { + int shmid; + caddr_t shmaddr; + int shmflg; +}; + shmat(p, uap, retval) struct proc *p; - register struct args { - int shmid; - caddr_t shmaddr; - int shmflg; - } *uap; + register struct shmat_args *uap; int *retval; { register struct shmid_ds *shp; @@ -345,7 +357,7 @@ shmat(p, uap, retval) flags |= MAP_FIXED; else uva = (caddr_t)0x1000000; /* XXX */ - error = vm_mmap(&p->p_vmspace->vm_map, &uva, (vm_size_t)size, prot, + error = vm_mmap(&p->p_vmspace->vm_map, &uva, (vm_size_t)size, prot, VM_PROT_DEFAULT, flags, ((struct shmhandle *)shp->shm_handle)->shmh_id, 0); if (error) return(error); @@ -364,12 +376,15 @@ shmat(p, uap, retval) /* * Detach from shared memory segment. */ + +struct shmdt_args { + caddr_t shmaddr; +}; + /* ARGSUSED */ shmdt(p, uap, retval) struct proc *p; - struct args { - caddr_t shmaddr; - } *uap; + struct shmdt_args *uap; int *retval; { register struct shmdesc *shmd; diff --git a/sys/kern/tty.c b/sys/kern/tty.c index 197208341c..31103a30a5 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -31,22 +31,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tty.c 7.44 (Berkeley) 5/28/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 3 00163 - * -------------------- ----- ---------------------- - * - * 11 Dec 92 Williams Jolitz Fixed tty handling - * 28 Nov 1991 Warren Toomey Cleaned up the use of COMPAT_43 - * in the 386BSD kernel. - * 27 May 93 Bruce Evans Sign Ext fix for TIOCSTI from the net - * Kludge to hook in RTS/CTS flow control - * Avoid sleeping on lbolt, it slows down - * output unnecessarily. + * from: @(#)tty.c 7.44 (Berkeley) 5/28/91 + * $Id$ */ -static char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/tty.c,v 1.3 92/01/21 21:31:11 william Exp $"; #include "param.h" #include "systm.h" @@ -219,7 +206,7 @@ ttyflush(tp, rw) flushq(&tp->t_raw); tp->t_rocount = 0; tp->t_rocol = 0; - tp->t_state &= ~TS_LOCAL; + tp->t_state &= ~(TS_LOCAL|TS_TBLOCK); /* XXX - should be TS_RTSBLOCK */ ttwakeup(tp); } if (rw & FWRITE) { @@ -250,19 +237,18 @@ ttyblock(tp) x = rawcc + cancc; if (rawcc > TTYHOG) { ttyflush(tp, FREAD|FWRITE); - tp->t_state &= ~TS_TBLOCK; } /* * Block further input iff: * Current input > threshold AND input is available to user program */ if (x >= TTYHOG/2 && (tp->t_state & TS_TBLOCK) == 0 && - ((tp->t_lflag&ICANON) == 0) || (cancc > 0) && - tp->t_cc[VSTOP] != _POSIX_VDISABLE) { - if (putc(tp->t_cc[VSTOP], &tp->t_out) == 0) { - tp->t_state |= TS_TBLOCK; - ttstart(tp); + ((tp->t_lflag&ICANON) == 0) || (cancc > 0)) { + if (tp->t_cc[VSTOP] != _POSIX_VDISABLE) { + putc(tp->t_cc[VSTOP], &tp->t_out); } + tp->t_state |= TS_TBLOCK; /* XXX - should be TS_RTSBLOCK? */ + ttstart(tp); } } @@ -316,6 +302,7 @@ ttioctl(tp, com, data, flag) case TIOCSETA: case TIOCSETAW: case TIOCSETAF: + case TIOCSTAT: #ifdef COMPAT_43 case TIOCSETP: case TIOCSETN: @@ -514,6 +501,14 @@ ttioctl(tp, com, data, flag) break; } + /* + * Give load average stats if requested (tcsh uses raw mode + * and directly sends the ioctl() to the tty driver) + */ + case TIOCSTAT: + ttyinfo(tp); + break; + /* * Set controlling terminal. * Session ctty vnode pointer set in vnode layer. @@ -1699,13 +1694,18 @@ ttyecho(c, tp) register c; register struct tty *tp; { - if ((tp->t_state&TS_CNTTB) == 0) + if ((tp->t_state & TS_CNTTB) == 0) tp->t_lflag &= ~FLUSHO; - if (((tp->t_lflag&ECHO) == 0 && - ((tp->t_lflag&ECHONL) == 0 || c == '\n')) || (tp->t_lflag&EXTPROC)) + if (tp->t_lflag & EXTPROC) return; - if (tp->t_lflag&ECHOCTL) { - if ((c&TTY_CHARMASK) <= 037 && c != '\t' && c != '\n' || + if ((tp->t_lflag & ECHO) == 0) { + if ((tp->t_lflag & ECHONL) == 0) + return; + else if (c != '\n') + return; + } + if (tp->t_lflag & ECHOCTL) { + if ((c & TTY_CHARMASK) <= 037 && c != '\t' && c != '\n' || c == 0177) { (void) ttyoutput('^', tp); c &= TTY_CHARMASK; diff --git a/sys/kern/tty_compat.c b/sys/kern/tty_compat.c index 5d1490147d..84e9ec672c 100644 --- a/sys/kern/tty_compat.c +++ b/sys/kern/tty_compat.c @@ -30,21 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tty_compat.c 7.10 (Berkeley) 5/9/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00163 - * -------------------- ----- ---------------------- - * - * 22 Apr 93 David Greenman support for 57600 and 115200 baud - * 27 May 93 Andrew A. Chernov Make more compatible with POSIX - * 27 May 93 Bruce Evans The work David did above was replaced - * by Bruces work plus some more stuff. - * (Chernov's fixes from the net edited - * by Bruce) - * Guido van Rooij Fix a bug caused by Bruces patches. - * + * from: @(#)tty_compat.c 7.10 (Berkeley) 5/9/91 + * $Id$ */ /* diff --git a/sys/kern/tty_conf.c b/sys/kern/tty_conf.c index 2745f43a53..dc3f01d0c9 100644 --- a/sys/kern/tty_conf.c +++ b/sys/kern/tty_conf.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tty_conf.c 7.6 (Berkeley) 5/9/91 + * from: @(#)tty_conf.c 7.6 (Berkeley) 5/9/91 + * $Id$ */ #include "param.h" @@ -55,6 +56,11 @@ int tbopen(),tbclose(),tbread(),tbinput(),tbioctl(); #if NSL > 0 int slopen(),slclose(),slinput(),sltioctl(),slstart(); #endif +#include "ppp.h" +#if NPPP > 0 +int pppopen(),pppclose(),pppread(),pppwrite(),pppinput(); +int ppptioctl(),pppstart(),pppselect(); +#endif struct linesw linesw[] = @@ -81,6 +87,13 @@ struct linesw linesw[] = enodev, enodev, enodev, enodev, enodev, enodev, enodev, enodev, enodev, enodev, #endif +#if NPPP > 0 + pppopen, pppclose, pppread, pppwrite, ppptioctl, + pppinput, enodev, nullop, pppstart, ttymodem, /* 5- PPPDISC */ +#else + enodev, enodev, enodev, enodev, enodev, + enodev, enodev, enodev, enodev, enodev, +#endif }; int nldisp = sizeof (linesw) / sizeof (linesw[0]); diff --git a/sys/kern/tty_pty.c b/sys/kern/tty_pty.c index 143fd347ed..8642c01c50 100644 --- a/sys/kern/tty_pty.c +++ b/sys/kern/tty_pty.c @@ -30,24 +30,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tty_pty.c 7.21 (Berkeley) 5/30/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 5 00094 - * -------------------- ----- ---------------------- - * - * 11 Dec 92 Williams Jolitz Fixed tty handling - * - * 28 Nov 1991 Warren Toomey Cleaned up the use of COMPAT_43 - * in the 386BSD kernel. - * 6 Oct 1992 Holger Veit Fixed 'hanging console' bug - * 11 Jan 93 Julian Elischer Fixes multiple processes on one - * pty bug - * 27 Feb 93 Charles Hannum Proper return values for ptsclose() - * and ptcclose() + * from: @(#)tty_pty.c 7.21 (Berkeley) 5/30/91 + * $Id$ */ -static char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/tty_pty.c,v 1.3 92/01/21 21:31:23 william Exp $"; /* * Pseudo-teletype Driver @@ -483,7 +468,7 @@ again: while (uio->uio_resid > 0 && RB_LEN(&tp->t_can) < TTYHOG - 1) { if (cc == 0) { cc = min(uio->uio_resid, BUFSIZ); - cc = min(cc, TTYHOG - 1 - RB_CONTIGPUT(&tp->t_can)); + cc = min(cc, RB_CONTIGPUT(&tp->t_can)); cp = locbuf; error = uiomove((caddr_t)cp, cc, uio); if (error) @@ -655,8 +640,9 @@ ptyioctl(dev, cmd, data, flag) return(0); } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag); - if (error < 0) - error = ttioctl(tp, cmd, data, flag); + if (error >= 0) + return (error); + error = ttioctl(tp, cmd, data, flag); /* * Since we use the tty queues internally, * pty's can't be switched to disciplines which overwrite diff --git a/sys/kern/tty_ring.c b/sys/kern/tty_ring.c index d6fbfdf343..74bb8d8438 100644 --- a/sys/kern/tty_ring.c +++ b/sys/kern/tty_ring.c @@ -45,17 +45,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00163 - * -------------------- ----- ---------------------- - * - * 14 Mar 93 David Greenman Upgrade bpf to match tcpdump 2.2.1 - * 27 May 93 Bruce Evans Who really did the changes that David - * supplied in the 14 Mar 93 patch. + * $Id$ */ -static char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/tty_ring.c,v 1.2 92/01/21 21:29:55 william Exp $"; #include "param.h" #include "systm.h" diff --git a/sys/kern/tty_tb.c b/sys/kern/tty_tb.c index 1f3bd470cb..d37b3e6cff 100644 --- a/sys/kern/tty_tb.c +++ b/sys/kern/tty_tb.c @@ -30,15 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tty_tb.c 7.7 (Berkeley) 5/9/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00163 - * -------------------- ----- ---------------------- - * - * 27 May 93 Andrew A. Chernov Compatibility fix - * + * from: @(#)tty_tb.c 7.7 (Berkeley) 5/9/91 + * $Id$ */ #include "tb.h" diff --git a/sys/kern/tty_tty.c b/sys/kern/tty_tty.c index 4a0bed3e27..c67849190d 100644 --- a/sys/kern/tty_tty.c +++ b/sys/kern/tty_tty.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tty_tty.c 7.15 (Berkeley) 5/28/91 + * from: @(#)tty_tty.c 7.15 (Berkeley) 5/28/91 + * $Id$ */ /* diff --git a/sys/kern/uipc_domain.c b/sys/kern/uipc_domain.c index f8ca170b0c..6b0d60fb34 100644 --- a/sys/kern/uipc_domain.c +++ b/sys/kern/uipc_domain.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)uipc_domain.c 7.9 (Berkeley) 3/4/91 + * from: @(#)uipc_domain.c 7.9 (Berkeley) 3/4/91 + * $Id$ */ #include diff --git a/sys/kern/uipc_mbuf.c b/sys/kern/uipc_mbuf.c index 36bdee6896..302fc633d2 100644 --- a/sys/kern/uipc_mbuf.c +++ b/sys/kern/uipc_mbuf.c @@ -30,17 +30,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)uipc_mbuf.c 7.19 (Berkeley) 4/20/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00009 - * -------------------- ----- ---------------------- - * - * 31 Aug 92 Wolfgang Solfrank Fixed mbuf allocation code + * from: @(#)uipc_mbuf.c 7.19 (Berkeley) 4/20/91 + * $Id$ */ #include "param.h" +#include "systm.h" #include "proc.h" #include "malloc.h" #define MBTYPES diff --git a/sys/kern/uipc_proto.c b/sys/kern/uipc_proto.c index fd1dbae02b..0afd5e0f99 100644 --- a/sys/kern/uipc_proto.c +++ b/sys/kern/uipc_proto.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)uipc_proto.c 7.6 (Berkeley) 5/9/91 + * from: @(#)uipc_proto.c 7.6 (Berkeley) 5/9/91 + * $Id$ */ #include "param.h" diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index 38a32b16b3..68c8518b75 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -30,10 +30,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)uipc_socket.c 7.28 (Berkeley) 5/4/91 + * from: @(#)uipc_socket.c 7.28 (Berkeley) 5/4/91 + * $Id: uipc_socket.c,v 1.7 1993/10/18 05:40:30 davidg Exp $ */ #include "param.h" +#include "systm.h" #include "proc.h" #include "file.h" #include "malloc.h" @@ -67,7 +69,7 @@ socreate(dom, aso, type, proto) prp = pffindproto(dom, proto, type); else prp = pffindtype(dom, type); - if (prp == 0) + if (prp == 0 || !prp->pr_usrreq) return (EPROTONOSUPPORT); if (prp->pr_type != type) return (EPROTOTYPE); @@ -318,6 +320,11 @@ sosend(so, addr, uio, top, control, flags) resid = uio->uio_resid; else resid = top->m_pkthdr.len; + + /* Don't allow negative sized sends */ + if (resid < 0) + return (EINVAL); + dontroute = (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 && (so->so_proto->pr_flags & PR_ATOMIC); @@ -381,25 +388,15 @@ restart: MGET(m, M_WAIT, MT_DATA); mlen = MLEN; } - if (resid >= MINCLSIZE && space >= MCLBYTES) { + if (resid >= MINCLSIZE) { MCLGET(m, M_WAIT); if ((m->m_flags & M_EXT) == 0) goto nopages; mlen = MCLBYTES; -#ifdef MAPPED_MBUFS - len = min(MCLBYTES, resid); -#else - if (top == 0) { - len = min(MCLBYTES - max_hdr, resid); - m->m_data += max_hdr; - } else - len = min(MCLBYTES, resid); -#endif - space -= MCLBYTES; + len = min(min(mlen, resid), space); } else { nopages: len = min(min(mlen, resid), space); - space -= len; /* * For datagram protocols, leave room * for protocol headers in first mbuf. @@ -407,6 +404,7 @@ nopages: if (atomic && top == 0 && len < mlen) MH_ALIGN(m, len); } + space -= len; error = uiomove(mtod(m, caddr_t), (int)len, uio); resid = uio->uio_resid; m->m_len = len; @@ -479,6 +477,7 @@ soreceive(so, paddr, uio, mp0, controlp, flagsp) struct protosw *pr = so->so_proto; struct mbuf *nextrecord; int moff, type; + int orig_resid = uio->uio_resid; mp = mp0; if (paddr) @@ -530,7 +529,7 @@ restart: while (m == 0 || so->so_rcv.sb_cc < uio->uio_resid && (so->so_rcv.sb_cc < so->so_rcv.sb_lowat || ((flags & MSG_WAITALL) && uio->uio_resid <= so->so_rcv.sb_hiwat)) && - m->m_nextpkt == 0) { + m->m_nextpkt == 0 && (pr->pr_flags & PR_ATOMIC) == 0) { #ifdef DIAGNOSTIC if (m == 0 && so->so_rcv.sb_cc) panic("receive 1"); @@ -580,6 +579,7 @@ dontblock: if (m->m_type != MT_SONAME) panic("receive 1a"); #endif + orig_resid = 0; if (flags & MSG_PEEK) { if (paddr) *paddr = m_copy(m, 0, m->m_len); @@ -618,8 +618,10 @@ dontblock: m = so->so_rcv.sb_mb; } } - if (controlp) + if (controlp) { + orig_resid = 0; controlp = &(*controlp)->m_next; + } } if (m) { if ((flags & MSG_PEEK) == 0) @@ -699,8 +701,11 @@ dontblock: so->so_state |= SS_RCVATMARK; break; } - } else + } else { offset += len; + if (offset == so->so_oobmark) + break; + } } if (flags & MSG_EOR) break; @@ -712,7 +717,7 @@ dontblock: * Keep sockbuf locked against other readers. */ while (flags & MSG_WAITALL && m == 0 && uio->uio_resid > 0 && - !sosendallatonce(so)) { + !sosendallatonce(so) && !nextrecord) { if (so->so_error || so->so_state & SS_CANTRCVMORE) break; error = sbwait(&so->so_rcv); @@ -725,18 +730,27 @@ dontblock: nextrecord = m->m_nextpkt; } } + + if (m && pr->pr_flags & PR_ATOMIC) { + flags |= MSG_TRUNC; + if ((flags & MSG_PEEK) == 0) + (void) sbdroprecord(&so->so_rcv); + } if ((flags & MSG_PEEK) == 0) { if (m == 0) so->so_rcv.sb_mb = nextrecord; - else if (pr->pr_flags & PR_ATOMIC) { - flags |= MSG_TRUNC; - (void) sbdroprecord(&so->so_rcv); - } if (pr->pr_flags & PR_WANTRCVD && so->so_pcb) (*pr->pr_usrreq)(so, PRU_RCVD, (struct mbuf *)0, (struct mbuf *)flags, (struct mbuf *)0, (struct mbuf *)0); } + if (orig_resid == uio->uio_resid && orig_resid && + (flags & MSG_EOR) == 0 && (so->so_state & SS_CANTRCVMORE) == 0) { + sbunlock(&so->so_rcv); + splx(s); + goto restart; + } + if (flagsp) *flagsp |= flags; release: diff --git a/sys/kern/uipc_socket2.c b/sys/kern/uipc_socket2.c index e17b8791c2..d7301fcd89 100644 --- a/sys/kern/uipc_socket2.c +++ b/sys/kern/uipc_socket2.c @@ -30,14 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)uipc_socket2.c 7.17 (Berkeley) 5/4/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00061 - * -------------------- ----- ---------------------- - * - * 11 Dec 92 Williams Jolitz Fixed tty handling + * from: @(#)uipc_socket2.c 7.17 (Berkeley) 5/4/91 + * $Id$ */ #include "param.h" diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c index 86ffd5f8c2..ec6a2706f4 100644 --- a/sys/kern/uipc_syscalls.c +++ b/sys/kern/uipc_syscalls.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)uipc_syscalls.c 7.24 (Berkeley) 6/3/91 + * from: @(#)uipc_syscalls.c 7.24 (Berkeley) 6/3/91 + * $Id$ */ #include "param.h" @@ -53,13 +54,15 @@ extern struct fileops socketops; +struct socket_args { + int domain; + int type; + int protocol; +}; + socket(p, uap, retval) struct proc *p; - register struct args { - int domain; - int type; - int protocol; - } *uap; + register struct socket_args *uap; int *retval; { struct filedesc *fdp = p->p_fd; @@ -82,14 +85,16 @@ socket(p, uap, retval) return (error); } +struct bind_args { + int s; + caddr_t name; + int namelen; +}; + /* ARGSUSED */ bind(p, uap, retval) struct proc *p; - register struct args { - int s; - caddr_t name; - int namelen; - } *uap; + register struct bind_args *uap; int *retval; { struct file *fp; @@ -105,13 +110,15 @@ bind(p, uap, retval) return (error); } +struct listen_args { + int s; + int backlog; +}; + /* ARGSUSED */ listen(p, uap, retval) struct proc *p; - register struct args { - int s; - int backlog; - } *uap; + register struct listen_args *uap; int *retval; { struct file *fp; @@ -123,14 +130,17 @@ listen(p, uap, retval) } #ifdef COMPAT_43 + +struct accept_args { + int s; + caddr_t name; + int *anamelen; + int compat_43; +}; + accept(p, uap, retval) struct proc *p; - struct args { - int s; - caddr_t name; - int *anamelen; - int compat_43; - } *uap; + struct accept_args *uap; int *retval; { @@ -138,14 +148,16 @@ accept(p, uap, retval) return (accept1(p, uap, retval)); } +struct oaccept_args { + int s; + caddr_t name; + int *anamelen; + int compat_43; +}; + oaccept(p, uap, retval) struct proc *p; - struct args { - int s; - caddr_t name; - int *anamelen; - int compat_43; - } *uap; + struct oaccept_args *uap; int *retval; { @@ -157,16 +169,18 @@ oaccept(p, uap, retval) #define accept1 accept #endif -accept1(p, uap, retval) - struct proc *p; - register struct args { - int s; - caddr_t name; - int *anamelen; +struct accept1_args { + int s; + caddr_t name; + int *anamelen; #ifdef COMPAT_43 - int compat_43; + int compat_43; #endif - } *uap; +}; + +accept1(p, uap, retval) + struct proc *p; + register struct accept1_args *uap; int *retval; { struct file *fp; @@ -240,14 +254,16 @@ accept1(p, uap, retval) return (error); } +struct connect_args { + int s; + caddr_t name; + int namelen; +}; + /* ARGSUSED */ connect(p, uap, retval) struct proc *p; - register struct args { - int s; - caddr_t name; - int namelen; - } *uap; + register struct connect_args *uap; int *retval; { struct file *fp; @@ -287,14 +303,16 @@ bad: return (error); } +struct socketpair_args { + int domain; + int type; + int protocol; + int *rsv; +}; + socketpair(p, uap, retval) struct proc *p; - register struct args { - int domain; - int type; - int protocol; - int *rsv; - } *uap; + register struct socketpair_args *uap; int retval[]; { register struct filedesc *fdp = p->p_fd; @@ -346,16 +364,18 @@ free1: return (error); } +struct sendto_args { + int s; + caddr_t buf; + int len; + int flags; + caddr_t to; + int tolen; +}; + sendto(p, uap, retval) struct proc *p; - register struct args { - int s; - caddr_t buf; - int len; - int flags; - caddr_t to; - int tolen; - } *uap; + register struct sendto_args *uap; int *retval; { struct msghdr msg; @@ -376,14 +396,17 @@ sendto(p, uap, retval) } #ifdef COMPAT_43 + +struct osend_args { + int s; + caddr_t buf; + int len; + int flags; +}; + osend(p, uap, retval) struct proc *p; - register struct args { - int s; - caddr_t buf; - int len; - int flags; - } *uap; + register struct osend_args *uap; int *retval; { struct msghdr msg; @@ -401,13 +424,16 @@ osend(p, uap, retval) } #define MSG_COMPAT 0x8000 + +struct osendmsg_args { + int s; + caddr_t msg; + int flags; +}; + osendmsg(p, uap, retval) struct proc *p; - register struct args { - int s; - caddr_t msg; - int flags; - } *uap; + register struct osendmsg_args *uap; int *retval; { struct msghdr msg; @@ -437,13 +463,15 @@ done: } #endif +struct sendmsg_args { + int s; + caddr_t msg; + int flags; +}; + sendmsg(p, uap, retval) struct proc *p; - register struct args { - int s; - caddr_t msg; - int flags; - } *uap; + register struct sendmsg_args *uap; int *retval; { struct msghdr msg; @@ -577,16 +605,19 @@ bad: } #ifdef COMPAT_43 + +struct orecvfrom_args { + int s; + caddr_t buf; + int len; + int flags; + caddr_t from; + int *fromlenaddr; +}; + orecvfrom(p, uap, retval) struct proc *p; - struct args { - int s; - caddr_t buf; - int len; - int flags; - caddr_t from; - int *fromlenaddr; - } *uap; + struct orecvfrom_args *uap; int *retval; { @@ -595,16 +626,18 @@ orecvfrom(p, uap, retval) } #endif +struct recvfrom_args { + int s; + caddr_t buf; + int len; + int flags; + caddr_t from; + int *fromlenaddr; +}; + recvfrom(p, uap, retval) struct proc *p; - register struct args { - int s; - caddr_t buf; - int len; - int flags; - caddr_t from; - int *fromlenaddr; - } *uap; + register struct recvfrom_args *uap; int *retval; { struct msghdr msg; @@ -628,14 +661,17 @@ recvfrom(p, uap, retval) } #ifdef COMPAT_43 + +struct orecv_args { + int s; + caddr_t buf; + int len; + int flags; +}; + orecv(p, uap, retval) struct proc *p; - register struct args { - int s; - caddr_t buf; - int len; - int flags; - } *uap; + register struct orecv_args *uap; int *retval; { struct msghdr msg; @@ -657,13 +693,16 @@ orecv(p, uap, retval) * overlays the new one, missing only the flags, and with the (old) access * rights where the control fields are now. */ + +struct orecvmsg_args { + int s; + struct omsghdr *msg; + int flags; +}; + orecvmsg(p, uap, retval) struct proc *p; - register struct args { - int s; - struct omsghdr *msg; - int flags; - } *uap; + register struct orecvmsg_args *uap; int *retval; { struct msghdr msg; @@ -698,13 +737,15 @@ done: } #endif +struct recvmsg_args { + int s; + struct msghdr *msg; + int flags; +}; + recvmsg(p, uap, retval) struct proc *p; - register struct args { - int s; - struct msghdr *msg; - int flags; - } *uap; + register struct recvmsg_args *uap; int *retval; { struct msghdr msg; @@ -784,7 +825,8 @@ recvit(p, s, mp, namelenp, retsize) #endif len = auio.uio_resid; if (error = soreceive((struct socket *)fp->f_data, &from, &auio, - (struct mbuf **)0, &control, &mp->msg_flags)) { + (struct mbuf **)0, mp->msg_control ? &control : (struct mbuf **)0, + &mp->msg_flags)) { if (auio.uio_resid != len && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; @@ -870,13 +912,15 @@ out: return (error); } +struct shutdown_args { + int s; + int how; +}; + /* ARGSUSED */ shutdown(p, uap, retval) struct proc *p; - register struct args { - int s; - int how; - } *uap; + register struct shutdown_args *uap; int *retval; { struct file *fp; @@ -887,16 +931,18 @@ shutdown(p, uap, retval) return (soshutdown((struct socket *)fp->f_data, uap->how)); } +struct setsocketopt_args { + int s; + int level; + int name; + caddr_t val; + int valsize; +}; + /* ARGSUSED */ setsockopt(p, uap, retval) struct proc *p; - register struct args { - int s; - int level; - int name; - caddr_t val; - int valsize; - } *uap; + register struct setsocketopt_args *uap; int *retval; { struct file *fp; @@ -922,16 +968,18 @@ setsockopt(p, uap, retval) uap->name, m)); } +struct getsockopt_args { + int s; + int level; + int name; + caddr_t val; + int *avalsize; +}; + /* ARGSUSED */ getsockopt(p, uap, retval) struct proc *p; - register struct args { - int s; - int level; - int name; - caddr_t val; - int *avalsize; - } *uap; + register struct getsockopt_args *uap; int *retval; { struct file *fp; @@ -1009,14 +1057,17 @@ free1: * Get socket name. */ #ifdef COMPAT_43 + +struct getsockname_args { + int fdes; + caddr_t asa; + int *alen; + int compat_43; +}; + getsockname(p, uap, retval) struct proc *p; - struct args { - int fdes; - caddr_t asa; - int *alen; - int compat_43; - } *uap; + struct getsockname_args *uap; int *retval; { @@ -1024,14 +1075,16 @@ getsockname(p, uap, retval) return (getsockname1(p, uap, retval)); } +struct ogetsockname_args { + int fdes; + caddr_t asa; + int *alen; + int compat_43; +}; + ogetsockname(p, uap, retval) struct proc *p; - struct args { - int fdes; - caddr_t asa; - int *alen; - int compat_43; - } *uap; + struct ogetsockname_args *uap; int *retval; { @@ -1043,17 +1096,19 @@ ogetsockname(p, uap, retval) #define getsockname1 getsockname #endif +struct getsockname1_args { + int fdes; + caddr_t asa; + int *alen; +#ifdef COMPAT_43 + int compat_43; +#endif +}; + /* ARGSUSED */ getsockname1(p, uap, retval) struct proc *p; - register struct args { - int fdes; - caddr_t asa; - int *alen; -#ifdef COMPAT_43 - int compat_43; -#endif - } *uap; + register struct getsockname1_args *uap; int *retval; { struct file *fp; @@ -1091,14 +1146,17 @@ bad: * Get name of peer for connected socket. */ #ifdef COMPAT_43 + +struct getpeername_args { + int fdes; + caddr_t asa; + int *alen; + int compat_43; +}; + getpeername(p, uap, retval) struct proc *p; - struct args { - int fdes; - caddr_t asa; - int *alen; - int compat_43; - } *uap; + struct getpeername_args *uap; int *retval; { @@ -1106,14 +1164,16 @@ getpeername(p, uap, retval) return (getpeername1(p, uap, retval)); } +struct ogetpeername_args { + int fdes; + caddr_t asa; + int *alen; + int compat_43; +}; + ogetpeername(p, uap, retval) struct proc *p; - struct args { - int fdes; - caddr_t asa; - int *alen; - int compat_43; - } *uap; + struct ogetpeername_args *uap; int *retval; { @@ -1125,17 +1185,19 @@ ogetpeername(p, uap, retval) #define getpeername1 getpeername #endif +struct getpeername1_args { + int fdes; + caddr_t asa; + int *alen; +#ifdef COMPAT_43 + int compat_43; +#endif +}; + /* ARGSUSED */ getpeername1(p, uap, retval) struct proc *p; - register struct args { - int fdes; - caddr_t asa; - int *alen; -#ifdef COMPAT_43 - int compat_43; -#endif - } *uap; + register struct getpeername1_args *uap; int *retval; { struct file *fp; diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c index 2d30ff6f0f..26886aafae 100644 --- a/sys/kern/uipc_usrreq.c +++ b/sys/kern/uipc_usrreq.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)uipc_usrreq.c 7.26 (Berkeley) 6/3/91 + * from: @(#)uipc_usrreq.c 7.26 (Berkeley) 6/3/91 + * $Id: uipc_usrreq.c,v 1.3 1993/09/14 04:26:40 rgrimes Exp $ */ #include "param.h" @@ -359,8 +360,17 @@ unp_detach(unp) unp->unp_socket->so_pcb = 0; m_freem(unp->unp_addr); (void) m_free(dtom(unp)); - if (unp_rights) + if (unp_rights) { + /* + * Normally the receive buffer is flushed later, + * in sofree, but if our receive buffer holds references + * to descriptors that are now garbage, we will dispose + * of those descriptor references after the garbage collector + * gets them (resulting in a "panic: closef: count < 0"). + */ + sorflush(unp->unp_socket); unp_gc(); + } } unp_bind(unp, nam, p) @@ -591,7 +601,7 @@ unp_externalize(rights) int newfds = (cm->cmsg_len - sizeof(*cm)) / sizeof (int); int f; - if (fdavail(p, newfds)) { + if (!fdavail(p, newfds)) { for (i = 0; i < newfds; i++) { fp = *rp; unp_discard(fp); @@ -761,6 +771,8 @@ unp_discard(fp) struct file *fp; { + if (fp->f_msgcount == 0) + return; fp->f_msgcount--; unp_rights--; (void) closef(fp, curproc); diff --git a/sys/kern/vfs__bio.c b/sys/kern/vfs__bio.c index ce6a153560..e510e59164 100644 --- a/sys/kern/vfs__bio.c +++ b/sys/kern/vfs__bio.c @@ -45,17 +45,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00042 - * -------------------- ----- ---------------------- - * - * 24 Apr 92 Martin Renters Fix NFS read request hang - * 20 Aug 92 David Greenman Fix getnewbuf() 2xAllocation + * $Id: vfs__bio.c,v 1.6 1993/10/16 15:25:16 rgrimes Exp $ */ -static char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/vfs__bio.c,v 1.2 92/01/21 21:30:08 william Exp $"; #include "param.h" +#include "systm.h" #include "proc.h" #include "vnode.h" #include "buf.h" @@ -65,7 +59,7 @@ static char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/vfs__bio.c,v 1.2 #include "vm/vm.h" #include "resourcevar.h" -struct buf *getnewbuf(int); +static struct buf *getnewbuf(int); extern vm_map_t buffer_map; /* @@ -118,6 +112,8 @@ bread(struct vnode *vp, daddr_t blkno, int size, struct ucred *cred, /* if not found in cache, do some I/O */ if ((bp->b_flags & B_CACHE) == 0 || (bp->b_flags & B_INVAL) != 0) { + if (curproc && curproc->p_stats) /* count block I/O */ + curproc->p_stats->p_ru.ru_inblock++; bp->b_flags |= B_READ; bp->b_flags &= ~(B_DONE|B_ERROR|B_INVAL); if (cred != NOCRED) crhold(cred); /* 25 Apr 92*/ @@ -145,6 +141,8 @@ breada(struct vnode *vp, daddr_t blkno, int size, daddr_t rablkno, int rabsize, /* if not found in cache, do some I/O */ if ((bp->b_flags & B_CACHE) == 0 || (bp->b_flags & B_INVAL) != 0) { + if (curproc && curproc->p_stats) /* count block I/O */ + curproc->p_stats->p_ru.ru_inblock++; bp->b_flags |= B_READ; bp->b_flags &= ~(B_DONE|B_ERROR|B_INVAL); if (cred != NOCRED) crhold(cred); /* 25 Apr 92*/ @@ -157,6 +155,8 @@ breada(struct vnode *vp, daddr_t blkno, int size, daddr_t rablkno, int rabsize, /* if not found in cache, do some I/O (overlapped with first) */ if ((rabp->b_flags & B_CACHE) == 0 || (rabp->b_flags & B_INVAL) != 0) { + if (curproc && curproc->p_stats) /* count block I/O */ + curproc->p_stats->p_ru.ru_inblock++; rabp->b_flags |= B_READ | B_ASYNC; rabp->b_flags &= ~(B_DONE|B_ERROR|B_INVAL); if (cred != NOCRED) crhold(cred); /* 25 Apr 92*/ @@ -196,6 +196,8 @@ bwrite(register struct buf *bp) if(wasdelayed) reassignbuf(bp, bp->b_vp); + if (curproc && curproc->p_stats) /* count block I/O */ + curproc->p_stats->p_ru.ru_oublock++; bp->b_flags |= B_DIRTY; bp->b_vp->v_numoutput++; VOP_STRATEGY(bp); @@ -225,6 +227,7 @@ bdwrite(register struct buf *bp) if(bp->b_flags & B_INVAL) { brelse(bp); + return; } if(bp->b_flags & B_TAPE) { bwrite(bp); @@ -259,6 +262,8 @@ bawrite(register struct buf *bp) if(wasdelayed) reassignbuf(bp, bp->b_vp); + if (curproc && curproc->p_stats) /* count block I/O */ + curproc->p_stats->p_ru.ru_oublock++; bp->b_flags |= B_DIRTY | B_ASYNC; bp->b_vp->v_numoutput++; VOP_STRATEGY(bp); @@ -396,6 +401,7 @@ fillin: bp->b_blkno = bp->b_lblkno = 0; bp->b_iodone = 0; bp->b_error = 0; + bp->b_resid = 0; bp->b_wcred = bp->b_rcred = NOCRED; if (bp->b_bufsize != sz) allocbuf(bp, sz); diff --git a/sys/kern/vfs_bio.old.c b/sys/kern/vfs_bio.old.c index e31e10124e..863a8ed258 100644 --- a/sys/kern/vfs_bio.old.c +++ b/sys/kern/vfs_bio.old.c @@ -45,8 +45,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * + * $Id$ */ -static char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/vfs__bio.c,v 1.2 92/01/21 21:30:08 william Exp $"; #include "param.h" #include "proc.h" diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index d24de73e63..58df7acf27 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vfs_cache.c 7.8 (Berkeley) 2/28/91 + * from: @(#)vfs_cache.c 7.8 (Berkeley) 2/28/91 + * $Id$ */ #include "param.h" diff --git a/sys/kern/vfs_conf.c b/sys/kern/vfs_conf.c index f8604c031e..abbe629328 100644 --- a/sys/kern/vfs_conf.c +++ b/sys/kern/vfs_conf.c @@ -30,15 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vfs_conf.c 7.3 (Berkeley) 6/28/90 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00129 - * -------------------- ----- ---------------------- - * - * 02 Apr 93 Paul Popelka Added support for PCFS - * + * from: @(#)vfs_conf.c 7.3 (Berkeley) 6/28/90 + * $Id$ */ #include "param.h" diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c index 05a7a122cb..6ab1543df5 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -30,14 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vfs_lookup.c 7.32 (Berkeley) 5/21/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00006 - * -------------------- ----- ---------------------- - * - * 17 Aug 92 Christoph Robitschko Fixed parent of chroot panic + * from: @(#)vfs_lookup.c 7.32 (Berkeley) 5/21/91 + * $Id$ */ #include "param.h" diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c index 98c5a632ab..c9fcb51f30 100644 --- a/sys/kern/vfs_subr.c +++ b/sys/kern/vfs_subr.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vfs_subr.c 7.60 (Berkeley) 6/21/91 + * from: @(#)vfs_subr.c 7.60 (Berkeley) 6/21/91 + * $Id: vfs_subr.c,v 1.3 1993/10/16 15:25:25 rgrimes Exp $ */ /* diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 72966789e6..f91ccf0f8d 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vfs_syscalls.c 7.74 (Berkeley) 6/21/91 + * from: @(#)vfs_syscalls.c 7.74 (Berkeley) 6/21/91 + * $Id: vfs_syscalls.c,v 1.4 1993/10/16 15:25:26 rgrimes Exp $ */ #include "param.h" @@ -53,15 +54,18 @@ /* * Mount system call. */ + +struct mount_args { + int type; + char *dir; + int flags; + caddr_t data; +}; + /* ARGSUSED */ mount(p, uap, retval) struct proc *p; - register struct args { - int type; - char *dir; - int flags; - caddr_t data; - } *uap; + register struct mount_args *uap; int *retval; { register struct nameidata *ndp; @@ -162,6 +166,10 @@ update: mp->mnt_flag |= MNT_NODEV; else mp->mnt_flag &= ~MNT_NODEV; + if (uap->flags & MNT_NOCORE) + mp->mnt_flag |= MNT_NOCORE; + else + mp->mnt_flag &= ~MNT_NOCORE; if (uap->flags & MNT_SYNCHRONOUS) mp->mnt_flag |= MNT_SYNCHRONOUS; else @@ -203,13 +211,16 @@ update: * Note: unmount takes a path to the vnode mounted on as argument, * not special file (as before). */ + +struct umount_args { + char *pathp; + int flags; +}; + /* ARGSUSED */ unmount(p, uap, retval) struct proc *p; - register struct args { - char *pathp; - int flags; - } *uap; + register struct umount_args *uap; int *retval; { register struct vnode *vp; @@ -290,7 +301,8 @@ sync(p, uap, retval) register struct mount *mp; struct mount *omp; - mp = rootfs; + if ((mp = rootfs) == NULL) + return (0); do { /* * The lock check below is to avoid races with mount @@ -311,15 +323,18 @@ sync(p, uap, retval) /* * Operate on filesystem quotas. */ + +struct quotactl_args { + char *path; + int cmd; + int uid; + caddr_t arg; +}; + /* ARGSUSED */ quotactl(p, uap, retval) struct proc *p; - register struct args { - char *path; - int cmd; - int uid; - caddr_t arg; - } *uap; + register struct quotactl_args *uap; int *retval; { register struct mount *mp; @@ -341,13 +356,16 @@ quotactl(p, uap, retval) /* * Get filesystem statistics. */ + +struct statfs_args { + char *path; + struct statfs *buf; +}; + /* ARGSUSED */ statfs(p, uap, retval) struct proc *p; - register struct args { - char *path; - struct statfs *buf; - } *uap; + register struct statfs_args *uap; int *retval; { register struct mount *mp; @@ -374,13 +392,16 @@ statfs(p, uap, retval) /* * Get filesystem statistics. */ + +struct fstatfs_args { + int fd; + struct statfs *buf; +}; + /* ARGSUSED */ fstatfs(p, uap, retval) struct proc *p; - register struct args { - int fd; - struct statfs *buf; - } *uap; + register struct fstatfs_args *uap; int *retval; { struct file *fp; @@ -401,13 +422,16 @@ fstatfs(p, uap, retval) /* * Get statistics on all filesystems. */ + +struct getfsstat_args { + struct statfs *buf; + long bufsize; + int flags; +}; + getfsstat(p, uap, retval) struct proc *p; - register struct args { - struct statfs *buf; - long bufsize; - int flags; - } *uap; + register struct getfsstat_args *uap; int *retval; { register struct mount *mp; @@ -451,12 +475,15 @@ getfsstat(p, uap, retval) /* * Change current working directory to a given file descriptor. */ + +struct fchdir_args { + int fd; +}; + /* ARGSUSED */ fchdir(p, uap, retval) struct proc *p; - struct args { - int fd; - } *uap; + struct fchdir_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; @@ -484,12 +511,15 @@ fchdir(p, uap, retval) /* * Change current working directory (``.''). */ + +struct chdir_args { + char *fname; +}; + /* ARGSUSED */ chdir(p, uap, retval) struct proc *p; - struct args { - char *fname; - } *uap; + struct chdir_args *uap; int *retval; { register struct nameidata *ndp; @@ -511,12 +541,15 @@ chdir(p, uap, retval) /* * Change notion of root (``/'') directory. */ + +struct chroot_args { + char *fname; +}; + /* ARGSUSED */ chroot(p, uap, retval) struct proc *p; - struct args { - char *fname; - } *uap; + struct chroot_args *uap; int *retval; { register struct nameidata *ndp; @@ -566,13 +599,16 @@ chdirec(ndp, p) * Check permissions, allocate an open file structure, * and call the device open routine if any. */ + +struct open_args { + char *fname; + int mode; + int crtmode; +}; + open(p, uap, retval) struct proc *p; - register struct args { - char *fname; - int mode; - int crtmode; - } *uap; + register struct open_args *uap; int *retval; { struct nameidata *ndp; @@ -598,7 +634,7 @@ open(p, uap, retval) if (error = vn_open(ndp, p, fmode, cmode)) { ffree(fp); if (error == ENODEV && /* XXX from fdopen */ - p->p_dupfd >= 0 && + ((short) p->p_dupfd) >= 0 && (error = dupfdopen(fdp, indx, p->p_dupfd, fmode)) == 0) { *retval = indx; return (0); @@ -642,12 +678,15 @@ open(p, uap, retval) /* * Creat system call. */ + +struct ocreat_args { + char *fname; + int fmode; +}; + ocreat(p, uap, retval) struct proc *p; - register struct args { - char *fname; - int fmode; - } *uap; + register struct ocreat_args *uap; int *retval; { struct newargs { @@ -666,14 +705,17 @@ ocreat(p, uap, retval) /* * Mknod system call. */ + +struct mknod_args { + char *fname; + int fmode; + int dev; +}; + /* ARGSUSED */ mknod(p, uap, retval) struct proc *p; - register struct args { - char *fname; - int fmode; - int dev; - } *uap; + register struct mknod_args *uap; int *retval; { register struct nameidata *ndp; @@ -731,13 +773,16 @@ out: /* * Mkfifo system call. */ + +struct mkfifo_args { + char *fname; + int fmode; +}; + /* ARGSUSED */ mkfifo(p, uap, retval) struct proc *p; - register struct args { - char *fname; - int fmode; - } *uap; + register struct mkfifo_args *uap; int *retval; { register struct nameidata *ndp; @@ -773,13 +818,16 @@ mkfifo(p, uap, retval) /* * Link system call. */ + +struct link_args { + char *target; + char *linkname; +}; + /* ARGSUSED */ link(p, uap, retval) struct proc *p; - register struct args { - char *target; - char *linkname; - } *uap; + register struct link_args *uap; int *retval; { register struct nameidata *ndp; @@ -829,13 +877,16 @@ out1: /* * Make a symbolic link. */ + +struct symlink_args { + char *target; + char *linkname; +}; + /* ARGSUSED */ symlink(p, uap, retval) struct proc *p; - register struct args { - char *target; - char *linkname; - } *uap; + register struct symlink_args *uap; int *retval; { register struct nameidata *ndp; @@ -874,12 +925,15 @@ out: /* * Delete a name from the filesystem. */ + +struct unlink_args { + char *fname; +}; + /* ARGSUSED */ unlink(p, uap, retval) struct proc *p; - struct args { - char *fname; - } *uap; + struct unlink_args *uap; int *retval; { register struct nameidata *ndp; @@ -922,13 +976,16 @@ out: /* * Seek system call. */ + +struct lseek_args { + int fdes; + off_t off; + int sbase; +}; + lseek(p, uap, retval) struct proc *p; - register struct args { - int fdes; - off_t off; - int sbase; - } *uap; + register struct lseek_args *uap; off_t *retval; { struct ucred *cred = p->p_ucred; @@ -969,13 +1026,16 @@ lseek(p, uap, retval) /* * Check access permissions. */ + +struct saccess_args { + char *fname; + int fmode; +}; + /* ARGSUSED */ saccess(p, uap, retval) struct proc *p; - register struct args { - char *fname; - int fmode; - } *uap; + register struct saccess_args *uap; int *retval; { register struct nameidata *ndp; @@ -1020,13 +1080,16 @@ out1: * Stat system call. * This version follows links. */ + +struct stat_args { + char *fname; + struct stat *ub; +}; + /* ARGSUSED */ stat(p, uap, retval) struct proc *p; - register struct args { - char *fname; - struct stat *ub; - } *uap; + register struct stat_args *uap; int *retval; { register struct nameidata *ndp; @@ -1052,13 +1115,16 @@ stat(p, uap, retval) * Lstat system call. * This version does not follow links. */ + +struct lstat_args { + char *fname; + struct stat *ub; +}; + /* ARGSUSED */ lstat(p, uap, retval) struct proc *p; - register struct args { - char *fname; - struct stat *ub; - } *uap; + register struct lstat_args *uap; int *retval; { register struct nameidata *ndp; @@ -1083,14 +1149,17 @@ lstat(p, uap, retval) /* * Return target name of a symbolic link. */ + +struct readlink_args { + char *name; + char *buf; + int count; +}; + /* ARGSUSED */ readlink(p, uap, retval) struct proc *p; - register struct args { - char *name; - char *buf; - int count; - } *uap; + register struct readlink_args *uap; int *retval; { register struct nameidata *ndp; @@ -1130,13 +1199,16 @@ out: /* * Change flags of a file given path name. */ + +struct chflags_args { + char *fname; + int flags; +}; + /* ARGSUSED */ chflags(p, uap, retval) struct proc *p; - register struct args { - char *fname; - int flags; - } *uap; + register struct chflags_args *uap; int *retval; { register struct nameidata *ndp; @@ -1167,13 +1239,16 @@ out: /* * Change flags of a file given a file descriptor. */ + +struct fdchflags_args { + int fd; + int flags; +}; + /* ARGSUSED */ fchflags(p, uap, retval) struct proc *p; - register struct args { - int fd; - int flags; - } *uap; + register struct fdchflags_args *uap; int *retval; { struct vattr vattr; @@ -1200,13 +1275,16 @@ out: /* * Change mode of a file given path name. */ + +struct chmod_args { + char *fname; + int fmode; +}; + /* ARGSUSED */ chmod(p, uap, retval) struct proc *p; - register struct args { - char *fname; - int fmode; - } *uap; + register struct chmod_args *uap; int *retval; { register struct nameidata *ndp; @@ -1237,13 +1315,16 @@ out: /* * Change mode of a file given a file descriptor. */ + +struct fchmod_args { + int fd; + int fmode; +}; + /* ARGSUSED */ fchmod(p, uap, retval) struct proc *p; - register struct args { - int fd; - int fmode; - } *uap; + register struct fchmod_args *uap; int *retval; { struct vattr vattr; @@ -1270,14 +1351,17 @@ out: /* * Set ownership given a path name. */ + +struct chown_args { + char *fname; + int uid; + int gid; +}; + /* ARGSUSED */ chown(p, uap, retval) struct proc *p; - register struct args { - char *fname; - int uid; - int gid; - } *uap; + register struct chown_args *uap; int *retval; { register struct nameidata *ndp; @@ -1309,14 +1393,17 @@ out: /* * Set ownership given a file descriptor. */ + +struct fchown_args { + int fd; + int uid; + int gid; +}; + /* ARGSUSED */ fchown(p, uap, retval) struct proc *p; - register struct args { - int fd; - int uid; - int gid; - } *uap; + register struct fchown_args *uap; int *retval; { struct vattr vattr; @@ -1344,13 +1431,16 @@ out: /* * Set the access and modification times of a file. */ + +struct utimes_args { + char *fname; + struct timeval *tptr; +}; + /* ARGSUSED */ utimes(p, uap, retval) struct proc *p; - register struct args { - char *fname; - struct timeval *tptr; - } *uap; + register struct utimes_args *uap; int *retval; { register struct nameidata *ndp; @@ -1385,13 +1475,16 @@ out: /* * Truncate a file given its path name. */ + +struct truncate_args { + char *fname; + off_t length; +}; + /* ARGSUSED */ truncate(p, uap, retval) struct proc *p; - register struct args { - char *fname; - off_t length; - } *uap; + register struct truncate_args *uap; int *retval; { register struct nameidata *ndp; @@ -1425,13 +1518,16 @@ out: /* * Truncate a file given a file descriptor. */ + +struct ftruncate_args { + int fd; + off_t length; +}; + /* ARGSUSED */ ftruncate(p, uap, retval) struct proc *p; - register struct args { - int fd; - off_t length; - } *uap; + register struct ftruncate_args *uap; int *retval; { struct vattr vattr; @@ -1462,12 +1558,15 @@ out: /* * Synch an open file. */ + +struct fsync_args { + int fd; +}; + /* ARGSUSED */ fsync(p, uap, retval) struct proc *p; - struct args { - int fd; - } *uap; + struct fsync_args *uap; int *retval; { register struct vnode *vp; @@ -1489,13 +1588,16 @@ fsync(p, uap, retval) * Source and destination must either both be directories, or both * not be directories. If target is a directory, it must be empty. */ + +struct rename_args { + char *from; + char *to; +}; + /* ARGSUSED */ rename(p, uap, retval) struct proc *p; - register struct args { - char *from; - char *to; - } *uap; + register struct rename_args *uap; int *retval; { register struct vnode *tvp, *fvp, *tdvp; @@ -1565,7 +1667,8 @@ out: vrele(tond.ni_startdir); FREE(tond.ni_pnbuf, M_NAMEI); out1: - vrele(fromnd.ni_startdir); + if (fromnd.ni_startdir) + vrele(fromnd.ni_startdir); FREE(fromnd.ni_pnbuf, M_NAMEI); if (error == -1) return (0); @@ -1575,13 +1678,16 @@ out1: /* * Mkdir system call. */ + +struct mkdir_args { + char *name; + int dmode; +}; + /* ARGSUSED */ mkdir(p, uap, retval) struct proc *p; - register struct args { - char *name; - int dmode; - } *uap; + register struct mkdir_args *uap; int *retval; { register struct nameidata *ndp; @@ -1618,12 +1724,15 @@ mkdir(p, uap, retval) /* * Rmdir system call. */ + +struct rmdir_args { + char *name; +}; + /* ARGSUSED */ rmdir(p, uap, retval) struct proc *p; - struct args { - char *name; - } *uap; + struct rmdir_args *uap; int *retval; { register struct nameidata *ndp; @@ -1671,14 +1780,17 @@ out: /* * Read a block of directory entries in a file system independent format. */ + +struct getdirentries_args { + int fd; + char *buf; + unsigned count; + long *basep; +}; + getdirentries(p, uap, retval) struct proc *p; - register struct args { - int fd; - char *buf; - unsigned count; - long *basep; - } *uap; + register struct getdirentries_args *uap; int *retval; { register struct vnode *vp; @@ -1718,12 +1830,15 @@ getdirentries(p, uap, retval) /* * Set the mode mask for creation of filesystem nodes. */ + +struct umask_args { + int mask; +}; + mode_t umask(p, uap, retval) struct proc *p; - struct args { - int mask; - } *uap; + struct umask_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; @@ -1737,12 +1852,15 @@ umask(p, uap, retval) * Void all references to file by ripping underlying filesystem * away from vnode. */ + +struct revoke_args { + char *fname; +}; + /* ARGSUSED */ revoke(p, uap, retval) struct proc *p; - register struct args { - char *fname; - } *uap; + register struct revoke_args *uap; int *retval; { register struct nameidata *ndp; diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c index 24e8ecc9a1..4892b61cc6 100644 --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vfs_vnops.c 7.33 (Berkeley) 6/27/91 + * from: @(#)vfs_vnops.c 7.33 (Berkeley) 6/27/91 + * $Id$ */ #include "param.h" diff --git a/sys/net/af.c b/sys/net/af.c index 4684e0924b..3d0a2c803c 100644 --- a/sys/net/af.c +++ b/sys/net/af.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)af.c 7.6 (Berkeley) 6/28/90 + * from: @(#)af.c 7.6 (Berkeley) 6/28/90 + * $Id$ */ #include "param.h" diff --git a/sys/net/af.h b/sys/net/af.h index 3a47cdcc3c..4c3c3e58bc 100644 --- a/sys/net/af.h +++ b/sys/net/af.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)af.h 7.5 (Berkeley) 6/28/90 + * from: @(#)af.h 7.5 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/net/bpf.c b/sys/net/bpf.c index ca30952d90..e8839b137e 100644 --- a/sys/net/bpf.c +++ b/sys/net/bpf.c @@ -35,19 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)bpf.c 7.5 (Berkeley) 7/15/91 - * - * static char rcsid[] = - * "$Header: bpf.c,v 1.49 92/05/25 14:43:09 mccanne Exp $"; - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00112 - * -------------------- ----- ---------------------- - * - * 14 Mar 93 David Greenman Upgrade bpf to match tcpdump 2.2.1 - * 24 Mar 93 Rodney W. Grimes Fixed selwakeup to use pid_t from proc, - * disabled definition of inline. + * from: @(#)bpf.c 7.5 (Berkeley) 7/15/91 + * $Id$ */ #include "bpfilter.h" diff --git a/sys/net/bpf.h b/sys/net/bpf.h index f202a0179b..563d74528e 100644 --- a/sys/net/bpf.h +++ b/sys/net/bpf.h @@ -35,16 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)bpf.h 7.1 (Berkeley) 5/7/91 - * - * @(#) $Header: bpf.h,v 1.27 92/05/25 14:42:52 mccanne Exp $ (LBL) - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00112 - * -------------------- ----- ---------------------- - * - * 14 Mar 93 David Greenman Upgrade bpf to match tcpdump 2.2.1 + * from: @(#)bpf.h 7.1 (Berkeley) 5/7/91 + * $Id$ */ /* diff --git a/sys/net/bpf_filter.c b/sys/net/bpf_filter.c index 65cf8dc15a..029a508a6f 100644 --- a/sys/net/bpf_filter.c +++ b/sys/net/bpf_filter.c @@ -35,22 +35,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)bpf.c 7.5 (Berkeley) 7/15/91 - * - * static char rcsid[] = - * "$Header: bpf_filter.c,v 1.20 92/04/06 10:43:03 mccanne Exp $"; - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00112 - * -------------------- ----- ---------------------- - * - * 14 Mar 93 David Greenman Upgrade bpf to match tcpdump 2.2.1 + * from: @(#)bpf.c 7.5 (Berkeley) 7/15/91 + * $Id$ */ -#if !(defined(lint) || defined(KERNEL)) -static char rcsid[] = - "@(#) $Header: bpf_filter.c,v 1.20 92/04/06 10:43:03 mccanne Exp $ (LBL)"; -#endif #include #include diff --git a/sys/net/bpfdesc.h b/sys/net/bpfdesc.h index 6d630334c8..562ff43704 100644 --- a/sys/net/bpfdesc.h +++ b/sys/net/bpfdesc.h @@ -35,16 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)bpfdesc.h 7.1 (Berkeley) 5/7/91 - * - * @(#) $Header: bpfdesc.h,v 1.10 92/02/24 21:28:24 mccanne Exp $ (LBL) - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00112 - * -------------------- ----- ---------------------- - * - * 14 Mar 93 David Greenman Upgrade bpf to match tcpdump 2.2.1 + * from: @(#)bpfdesc.h 7.1 (Berkeley) 5/7/91 + * $Id$ */ /* diff --git a/sys/net/if.c b/sys/net/if.c index dc395f5522..6c766487c7 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)if.c 7.14 (Berkeley) 4/20/91 + * from: @(#)if.c 7.14 (Berkeley) 4/20/91 + * $Id$ */ #include "param.h" @@ -118,12 +119,14 @@ if_attach(ifp) } ifnet_addrs = q; } +#if defined(INET) && NETHER > 0 /* XXX -- Temporary fix before changing 10 ethernet drivers */ if (ifp->if_output == ether_output) { ifp->if_type = IFT_ETHER; ifp->if_addrlen = 6; ifp->if_hdrlen = 14; } +#endif /* * create a Link Level name for this device */ @@ -484,6 +487,15 @@ ifioctl(so, cmd, data, p) ifp->if_metric = ifr->ifr_metric; break; + case SIOCSIFMTU: + case SIOCGIFMTU: + case SIOCSIFASYNCMAP: + case SIOCGIFASYNCMAP: + if (!ifp->if_ioctl) + return (EOPNOTSUPP); + return ((*ifp->if_ioctl)(ifp, cmd, data)); + break; + default: if (so->so_proto == 0) return (EOPNOTSUPP); diff --git a/sys/net/if.h b/sys/net/if.h index 52ecd69c6c..4f224d4795 100644 --- a/sys/net/if.h +++ b/sys/net/if.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)if.h 7.11 (Berkeley) 3/19/91 + * from: @(#)if.h 7.11 (Berkeley) 3/19/91 + * $Id$ */ /* diff --git a/sys/net/if_arp.h b/sys/net/if_arp.h index 355af2ae40..8c2eb5d7d9 100644 --- a/sys/net/if_arp.h +++ b/sys/net/if_arp.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)if_arp.h 7.4 (Berkeley) 6/28/90 + * from: @(#)if_arp.h 7.4 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/net/if_dl.h b/sys/net/if_dl.h index 65a4109b6f..4c7e89c6de 100644 --- a/sys/net/if_dl.h +++ b/sys/net/if_dl.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)if_dl.h 7.2 (Berkeley) 2/22/91 + * from: @(#)if_dl.h 7.2 (Berkeley) 2/22/91 + * $Id$ */ /* diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 69e8554625..6fe67ab260 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)if_ethersubr.c 7.13 (Berkeley) 4/20/91 + * from: @(#)if_ethersubr.c 7.13 (Berkeley) 4/20/91 + * $Id$ */ #include "param.h" @@ -239,12 +240,15 @@ gottype: goto bad; } eh = mtod(m, struct ether_header *); - type = htons((u_short)type); - bcopy((caddr_t)&type,(caddr_t)&eh->ether_type, - sizeof(eh->ether_type)); - bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst)); - bcopy((caddr_t)ac->ac_enaddr, (caddr_t)eh->ether_shost, - sizeof(eh->ether_shost)); + + eh->ether_type = htons((u_short) type); + + *(int *) eh->ether_dhost = *(int *) edst; + ((short *) eh->ether_dhost)[2] = ((short *) edst)[2]; + + *(int *) eh->ether_shost = *(int *) ac->ac_enaddr; + ((short *) eh->ether_shost)[2] = ((short *) ac->ac_enaddr)[2]; + s = splimp(); /* * Queue message on interface, and start output if interface diff --git a/sys/net/if_llc.h b/sys/net/if_llc.h index e9aebdfa15..6add37ebd0 100644 --- a/sys/net/if_llc.h +++ b/sys/net/if_llc.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)if_llc.h 7.2 (Berkeley) 6/28/90 + * from: @(#)if_llc.h 7.2 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/net/if_loop.c b/sys/net/if_loop.c index 9ed81ded8e..b2bf1cb103 100644 --- a/sys/net/if_loop.c +++ b/sys/net/if_loop.c @@ -30,14 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)if_loop.c 7.13 (Berkeley) 4/26/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00112 - * -------------------- ----- ---------------------- - * - * 14 Mar 93 David Greenman Upgrade bpf to match tcpdump 2.2.1 + * from: @(#)if_loop.c 7.13 (Berkeley) 4/26/91 + * $Id$ */ /* diff --git a/sys/net/if_ppp.c b/sys/net/if_ppp.c new file mode 100644 index 0000000000..a1da98a8e9 --- /dev/null +++ b/sys/net/if_ppp.c @@ -0,0 +1,1441 @@ +/* + * if_ppp.c - Point-to-Point Protocol (PPP) Asynchronous driver. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Drew D. Perkins + * Carnegie Mellon University + * 4910 Forbes Ave. + * Pittsburgh, PA 15213 + * (412) 268-8576 + * ddp@andrew.cmu.edu + * + * Based on: + * @(#)if_sl.c 7.6.1.2 (Berkeley) 2/15/89 + * + * Copyright (c) 1987 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Serial Line interface + * + * Rick Adams + * Center for Seismic Studies + * 1300 N 17th Street, Suite 1450 + * Arlington, Virginia 22209 + * (703)276-7900 + * rick@seismo.ARPA + * seismo!rick + * + * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris). + * Converted to 4.3BSD Beta by Chris Torek. + * Other changes made at Berkeley, based in part on code by Kirk Smith. + * + * Converted to 4.3BSD+ 386BSD by Brad Parker (brad@cayman.com) + * Added VJ tcp header compression; more unified ioctls + * + * Extensively modified by Paul Mackerras (paulus@cs.anu.edu.au). + * Cleaned up a lot of the mbuf-related code to fix bugs that + * caused system crashes and packet corruption. Changed pppstart + * so that it doesn't just give up with a collision if the whole + * packet doesn't fit in the output ring buffer. + * + * Added priority queueing for interactive IP packets, following + * the model of if_sl.c, plus hooks for bpf. + * Paul Mackerras (paulus@cs.anu.edu.au). + */ + +/* + * $Id: if_ppp.c,v 1.3 1993/09/06 19:48:58 rgrimes Exp $ + * From: if_ppp.c,v 1.22 1993/08/31 23:20:40 paulus Exp + * From: if_ppp.c,v 1.21 1993/08/29 11:22:37 paulus Exp + * From: if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp + */ + +#include "ppp.h" +#if NPPP > 0 + +#define VJC + +#include "param.h" +#include "systm.h" +#include "proc.h" +#include "mbuf.h" +#include "buf.h" +#include "dkstat.h" +#include "socket.h" +#include "ioctl.h" +#include "file.h" +#include "tty.h" +#include "kernel.h" +#include "conf.h" + +#include "if.h" +#include "if_types.h" +#include "netisr.h" +#include "route.h" +#if INET +#include "../netinet/in.h" +#include "../netinet/in_systm.h" +#include "../netinet/in_var.h" +#include "../netinet/ip.h" +#endif + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include "time.h" +#include "bpf.h" +#endif + +/* + * Here we try to tell whether we are in a 386BSD kernel, or + * in a NetBSD/Net-2/4.3-Reno kernel. + */ +#ifndef RB_LEN +/* NetBSD, 4.3-Reno or similar */ +#define CCOUNT(q) ((q)->c_cc) + +#else +/* 386BSD, Jolitz-style ring buffers */ +#define t_outq t_out +#define t_rawq t_raw +#define t_canq t_can +#define CCOUNT(q) (RB_LEN(q)) +#endif + +#ifdef VJC +#include "slcompress.h" +#define HDROFF MAX_HDR +/* HDROFF should really be 128, but other parts of the system will + panic on TCP+IP headers bigger than MAX_HDR = MHLEN (100). */ + +#else +#define HDROFF (0) +#endif + +#include "if_ppp.h" +#include "machine/mtpr.h" + +struct ppp_softc ppp_softc[NPPP]; +int ppp_async_out_debug = 0; +int ppp_async_in_debug = 0; +int ppp_debug = 0; +int ppp_raw_in_debug = -1; +char ppp_rawin[32]; +int ppp_rawin_count; + +void pppattach __P((void)); +int pppopen __P((dev_t dev, struct tty *tp)); +void pppclose __P((struct tty *tp, int flag)); +int pppread __P((struct tty *tp, struct uio *uio, int flag)); +int pppwrite __P((struct tty *tp, struct uio *uio, int flag)); +int ppptioctl __P((struct tty *tp, int cmd, caddr_t data, int flag)); +int pppoutput __P((struct ifnet *ifp, struct mbuf *m0, + struct sockaddr *dst)); +void pppstart __P((struct tty *tp)); +void pppinput __P((int c, struct tty *tp)); +int pppioctl __P((struct ifnet *ifp, int cmd, caddr_t data)); + +static u_short pppfcs __P((u_short fcs, u_char *cp, int len)); +static int pppinit __P((struct ppp_softc *sc)); +static struct mbuf *ppp_btom __P((struct ppp_softc *sc)); +static void pppdumpm __P((struct mbuf *m0, int pktlen)); +static void pppdumpb __P((u_char *b, int l)); + +/* + * Some useful mbuf macros not in mbuf.h. + */ +#define M_DATASTART(m) \ + ((m)->m_flags & M_EXT ? (m)->m_ext.ext_buf : \ + (m)->m_flags & M_PKTHDR ? (m)->m_pktdat : (m)->m_dat) + +#define M_DATASIZE(m) \ + ((m)->m_flags & M_EXT ? (m)->m_ext.ext_size : \ + (m)->m_flags & M_PKTHDR ? MHLEN: MLEN) + +/* + * The following disgusting hack gets around the problem that IP TOS + * can't be set yet. We want to put "interactive" traffic on a high + * priority queue. To decide if traffic is interactive, we check that + * a) it is TCP and b) one of its ports is telnet, rlogin or ftp control. + */ +static u_short interactive_ports[8] = { + 0, 513, 0, 0, + 0, 21, 0, 23, +}; +#define INTERACTIVE(p) (interactive_ports[(p) & 7] == (p)) + +/* + * Does c need to be escaped? + */ +#define ESCAPE_P(c) (((c) == PPP_FLAG) || ((c) == PPP_ESCAPE) || \ + (c) < 0x20 && (sc->sc_asyncmap & (1 << (c)))) + +/* + * Called from boot code to establish ppp interfaces. + */ +void +pppattach() +{ + register struct ppp_softc *sc; + register int i = 0; + + for (sc = ppp_softc; i < NPPP; sc++) { + sc->sc_if.if_name = "ppp"; + sc->sc_if.if_unit = i++; + sc->sc_if.if_mtu = PPP_MTU; + sc->sc_if.if_flags = IFF_POINTOPOINT; + sc->sc_if.if_type = IFT_PPP; + sc->sc_if.if_hdrlen = PPP_HEADER_LEN; + sc->sc_if.if_ioctl = pppioctl; + sc->sc_if.if_output = pppoutput; + sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN; + sc->sc_inq.ifq_maxlen = IFQ_MAXLEN; + sc->sc_fastq.ifq_maxlen = IFQ_MAXLEN; + if_attach(&sc->sc_if); +#if NBPFILTER > 0 + bpfattach(&sc->sc_bpf, &sc->sc_if, DLT_PPP, PPP_HEADER_LEN); +#endif + } +} + +/* + * Line specific open routine. + * Attach the given tty to the first available ppp unit. + */ +/* ARGSUSED */ +int +pppopen(dev, tp) + dev_t dev; + register struct tty *tp; +{ + struct proc *p = curproc; /* XXX */ + register struct ppp_softc *sc; + register int nppp; + int error, s; + + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + + if (tp->t_line == PPPDISC) + return (0); + + for (nppp = 0, sc = ppp_softc; nppp < NPPP; nppp++, sc++) + if (sc->sc_ttyp == NULL) + break; + if (nppp >= NPPP) + return ENXIO; + + sc->sc_flags = 0; + sc->sc_ilen = 0; + sc->sc_asyncmap = 0xffffffff; + sc->sc_rasyncmap = 0; + sc->sc_mru = PPP_MRU; +#ifdef VJC + sl_compress_init(&sc->sc_comp); +#endif + if (pppinit(sc) == 0) { + sc->sc_if.if_flags &= ~(IFF_UP|IFF_RUNNING); + return (ENOBUFS); + } + tp->t_sc = (caddr_t)sc; + sc->sc_ttyp = tp; + sc->sc_outm = NULL; + ttyflush(tp, FREAD | FWRITE); + sc->sc_if.if_flags |= IFF_RUNNING; + +#ifdef PPP_OUTQ_SIZE + /* N.B. this code is designed *only* for use in NetBSD */ + s = spltty(); + /* get rid of the default outq clist buffer */ + clfree(&tp->t_outq); + /* and get a new one, without quoting support, much larger */ + clalloc(&tp->t_outq, PPP_OUTQ_SIZE, 0); + splx (s); +#endif /* PPP_OUTQ_SIZE */ + + return (0); +} + +/* + * Line specific close routine. + * Detach the tty from the ppp unit. + * Mimics part of ttyclose(). + */ +void +pppclose(tp, flag) + struct tty *tp; + int flag; +{ + register struct ppp_softc *sc; + struct mbuf *m; + int s; + + ttywflush(tp); + s = splimp(); /* paranoid; splnet probably ok */ + tp->t_line = 0; + sc = (struct ppp_softc *)tp->t_sc; + if (sc != NULL) { + if_down(&sc->sc_if); + sc->sc_ttyp = NULL; + tp->t_sc = NULL; + m_freem(sc->sc_outm); + sc->sc_outm = NULL; + m_freem(sc->sc_m); + sc->sc_m = NULL; + for (;;) { + IF_DEQUEUE(&sc->sc_inq, m); + if (m == NULL) + break; + m_freem(m); + } + for (;;) { + IF_DEQUEUE(&sc->sc_fastq, m); + if (m == NULL) + break; + m_freem(m); + } + sc->sc_if.if_flags &= ~(IFF_UP|IFF_RUNNING); + +#ifdef PPP_OUTQ_SIZE + /* reinstall default clist-buffer for outq + XXXX should really remember old value and restore that!! */ + clfree(&tp->t_outq); + clalloc(&tp->t_outq, 1024, 0); +#endif /* PPP_OUTQ_SIZE */ + + } + splx(s); +} + +/* + * Line specific (tty) read routine. + */ +int +pppread(tp, uio, flag) + register struct tty *tp; + struct uio *uio; + int flag; +{ + register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc; + struct mbuf *m, *m0; + register int s; + int error; + + if ((tp->t_state & TS_CARR_ON)==0) + return (EIO); + s = splimp(); + while (sc->sc_inq.ifq_head == NULL && tp->t_line == PPPDISC) { + if (tp->t_state & TS_ASYNC) { + splx(s); + return (EWOULDBLOCK); + } + error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI|PCATCH, ttyin, 0); + if (error) + return error; + } + if (tp->t_line != PPPDISC) { + splx(s); + return (-1); + } + + /* Pull place-holder byte out of canonical queue */ + getc(&tp->t_canq); + + /* Get the packet from the input queue */ + IF_DEQUEUE(&sc->sc_inq, m0); + splx(s); + + for (m = m0; m && uio->uio_resid; m = m->m_next) + if (error = uiomove(mtod(m, u_char *), m->m_len, uio)) + break; + m_freem(m0); + return (error); +} + +/* + * Line specific (tty) write routine. + */ +int +pppwrite(tp, uio, flag) + register struct tty *tp; + struct uio *uio; + int flag; +{ + register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc; + struct mbuf *m, *m0, **mp; + struct sockaddr dst; + struct ppp_header *ph1, *ph2; + int len, error; + + if ((tp->t_state & TS_CARR_ON)==0) + return (EIO); + if (tp->t_line != PPPDISC) + return (EINVAL); + if (uio->uio_resid > sc->sc_if.if_mtu + PPP_HEADER_LEN || + uio->uio_resid < PPP_HEADER_LEN) + return (EMSGSIZE); + for (mp = &m0; uio->uio_resid; mp = &m->m_next) { + MGET(m, M_WAIT, MT_DATA); + if ((*mp = m) == NULL) { + m_freem(m0); + return (ENOBUFS); + } + if (uio->uio_resid >= MCLBYTES / 2) + MCLGET(m, M_DONTWAIT); + len = MIN(M_TRAILINGSPACE(m), uio->uio_resid); + if (error = uiomove(mtod(m, u_char *), len, uio)) { + m_freem(m0); + return (error); + } + m->m_len = len; + } + dst.sa_family = AF_UNSPEC; + ph1 = (struct ppp_header *) &dst.sa_data; + ph2 = mtod(m0, struct ppp_header *); + *ph1 = *ph2; + m0->m_data += PPP_HEADER_LEN; + m0->m_len -= PPP_HEADER_LEN; + return (pppoutput(&sc->sc_if, m0, &dst)); +} + +/* + * Line specific (tty) ioctl routine. + * Provide a way to get the ppp unit number. + * This discipline requires that tty device drivers call + * the line specific l_ioctl routine from their ioctl routines. + */ +/* ARGSUSED */ +int +ppptioctl(tp, cmd, data, flag) + struct tty *tp; + caddr_t data; + int cmd, flag; +{ + register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc; + struct proc *p = curproc; /* XXX */ + int s, error, flags, mru; + + switch (cmd) { +#if 0 /* this is handled (properly) by ttioctl */ + case TIOCGETD: + *(int *)data = sc->sc_if.if_unit; + break; +#endif + case FIONREAD: + *(int *)data = sc->sc_inq.ifq_len; + break; + + case PPPIOCGUNIT: + *(int *)data = sc->sc_if.if_unit; + break; + + case PPPIOCGFLAGS: + *(u_int *)data = sc->sc_flags; + break; + + case PPPIOCSFLAGS: + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + flags = *(int *)data & SC_MASK; + s = splimp(); + sc->sc_flags = (sc->sc_flags & ~SC_MASK) | flags; + splx(s); + break; + + case PPPIOCSASYNCMAP: + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + sc->sc_asyncmap = *(u_int *)data; + break; + + case PPPIOCGASYNCMAP: + *(u_int *)data = sc->sc_asyncmap; + break; + + case PPPIOCSRASYNCMAP: + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + sc->sc_rasyncmap = *(u_int *)data; + break; + + case PPPIOCGRASYNCMAP: + *(u_int *)data = sc->sc_rasyncmap; + break; + + case PPPIOCSMRU: + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + mru = *(int *)data; + if (mru >= PPP_MRU && mru <= PPP_MAXMRU) { + sc->sc_mru = mru; + if (pppinit(sc) == 0) { + error = ENOBUFS; + sc->sc_mru = PPP_MRU; + if (pppinit(sc) == 0) + sc->sc_if.if_flags &= ~IFF_UP; + } + } + break; + + case PPPIOCGMRU: + *(int *)data = sc->sc_mru; + break; + + default: + return (-1); + } + return (0); +} + +/* + * FCS lookup table as calculated by genfcstab. + */ +static u_short fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +/* + * Calculate a new FCS given the current FCS and the new data. + */ +static u_short +pppfcs(fcs, cp, len) + register u_short fcs; + register u_char *cp; + register int len; +{ + while (len--) + fcs = PPP_FCS(fcs, *cp++); + return (fcs); +} + +/* + * Queue a packet. Start transmission if not active. + * Packet is placed in Information field of PPP frame. + */ +int +pppoutput(ifp, m0, dst) + struct ifnet *ifp; + struct mbuf *m0; + struct sockaddr *dst; +{ + register struct ppp_softc *sc = &ppp_softc[ifp->if_unit]; + struct ppp_header *ph; + int protocol, address, control; + u_char *cp; + int s, error; + struct ip *ip; + struct ifqueue *ifq; + + if (sc->sc_ttyp == NULL || (ifp->if_flags & IFF_RUNNING) == 0 + || (ifp->if_flags & IFF_UP) == 0 && dst->sa_family != AF_UNSPEC) { + error = ENETDOWN; /* sort of */ + goto bad; + } + if ((sc->sc_ttyp->t_state & TS_CARR_ON) == 0) { + error = EHOSTUNREACH; + goto bad; + } + + /* + * Compute PPP header. + */ + address = PPP_ALLSTATIONS; + control = PPP_UI; + ifq = &ifp->if_snd; + switch (dst->sa_family) { +#ifdef INET + case AF_INET: + protocol = PPP_IP; + /* + * If this is a TCP packet to or from an "interactive" port, + * put the packet on the fastq instead. + */ + if ((ip = mtod(m0, struct ip *))->ip_p == IPPROTO_TCP) { + register int p = ((int *)ip)[ip->ip_hl]; + if (INTERACTIVE(p & 0xffff) || INTERACTIVE(p >> 16)) + ifq = &sc->sc_fastq; + } + break; +#endif +#ifdef NS + case AF_NS: + protocol = PPP_XNS; + break; +#endif + case AF_UNSPEC: + ph = (struct ppp_header *) dst->sa_data; + address = ph->ph_address; + control = ph->ph_control; + protocol = ntohs(ph->ph_protocol); + break; + default: + printf("ppp%d: af%d not supported\n", ifp->if_unit, dst->sa_family); + error = EAFNOSUPPORT; + goto bad; + } + + /* + * Add PPP header. If no space in first mbuf, allocate another. + * (This assumes M_LEADINGSPACE is always 0 for a cluster mbuf.) + */ + if (M_LEADINGSPACE(m0) < PPP_HEADER_LEN) { + m0 = m_prepend(m0, PPP_HEADER_LEN, M_DONTWAIT); + if (m0 == 0) { + error = ENOBUFS; + goto bad; + } + m0->m_len = 0; + } else + m0->m_data -= PPP_HEADER_LEN; + + cp = mtod(m0, u_char *); + *cp++ = address; + *cp++ = control; + *cp++ = protocol >> 8; + *cp++ = protocol & 0xff; + m0->m_len += PPP_HEADER_LEN; + + if (ppp_async_out_debug) { + printf("ppp%d output: ", ifp->if_unit); + pppdumpm(m0, -1); + } + +#if NBPFILTER > 0 + /* See if bpf wants to look at the packet. */ + if (sc->sc_bpf) + bpf_mtap(sc->sc_bpf, m0); +#endif + + /* + * Put the packet on the appropriate queue. + */ + s = splimp(); + if (IF_QFULL(ifq)) { + IF_DROP(ifq); + splx(s); + sc->sc_if.if_oerrors++; + error = ENOBUFS; + goto bad; + } + IF_ENQUEUE(ifq, m0); + /* + * The next statement used to be subject to: + * if (CCOUNT(&sc->sc_ttyp->t_outq) == 0) + * which was removed so that we don't hang up completely + * if the serial transmitter loses an interrupt. + */ + pppstart(sc->sc_ttyp); + splx(s); + return (0); + +bad: + m_freem(m0); + return (error); +} + +/* + * Start output on interface. Get another datagram + * to send from the interface queue and map it to + * the interface before starting output. + */ +void +pppstart(tp) + register struct tty *tp; +{ + register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc; + register struct mbuf *m; + register int len; + register u_char *start, *stop, *cp; + int n, s, ndone, done; + struct mbuf *m2; + int address, control, protocol; + int compac, compprot, nb; + + for (;;) { + /* + * If there is more in the output queue, just send it now. + * We are being called in lieu of ttstart and must do what + * it would. + */ + if (CCOUNT(&tp->t_outq) != 0 && tp->t_oproc != NULL) { + (*tp->t_oproc)(tp); + if (CCOUNT(&tp->t_outq) > PPP_HIWAT) + return; + } + /* + * This happens briefly when the line shuts down. + */ + if (sc == NULL) + return; + + /* + * See if we have an existing packet partly sent. + * If not, get a new packet and start sending it. + * We take packets on the priority queue ahead of those + * on the normal queue. + */ + m = sc->sc_outm; + if (m == NULL) { + s = splimp(); + IF_DEQUEUE(&sc->sc_fastq, m); + if (m == NULL) + IF_DEQUEUE(&sc->sc_if.if_snd, m); + splx(s); + if (m == NULL) + return; + + /* + * Extract the ppp header of the new packet. + * The ppp header will be in one mbuf. + */ + cp = mtod(m, u_char *); + address = *cp++; + control = *cp++; + protocol = *cp++; + protocol = (protocol << 8) + *cp++; + m->m_data += PPP_HEADER_LEN; + m->m_len -= PPP_HEADER_LEN; + +#ifdef VJC + /* + * If the packet is a TCP/IP packet, see if we can compress it. + */ + if (protocol == PPP_IP && sc->sc_flags & SC_COMP_TCP) { + struct ip *ip; + int type; + struct mbuf *mp; + + mp = m; + if (mp->m_len <= 0) { + mp = mp->m_next; + cp = mtod(mp, u_char *); + } + ip = (struct ip *) cp; + if (ip->ip_p == IPPROTO_TCP) { + type = sl_compress_tcp(mp, ip, &sc->sc_comp, + !(sc->sc_flags & SC_NO_TCP_CCID)); + switch (type) { + case TYPE_UNCOMPRESSED_TCP: + protocol = PPP_VJC_UNCOMP; + break; + case TYPE_COMPRESSED_TCP: + protocol = PPP_VJC_COMP; + break; + } + } + } +#endif + + /* + * Compress the address/control and protocol, if possible. + */ + compac = sc->sc_flags & SC_COMP_AC && address == PPP_ALLSTATIONS && + control == PPP_UI && protocol != PPP_ALLSTATIONS && + protocol != PPP_LCP; + compprot = sc->sc_flags & SC_COMP_PROT && protocol < 0x100; + nb = (compac ? 0 : 2) + (compprot ? 1 : 2); + m->m_data -= nb; + m->m_len += nb; + + cp = mtod(m, u_char *); + if (!compac) { + *cp++ = address; + *cp++ = control; + } + if (!compprot) + *cp++ = protocol >> 8; + *cp++ = protocol; + + /* + * The extra PPP_FLAG will start up a new packet, and thus + * will flush any accumulated garbage. We do this whenever + * the line may have been idle for some time. + */ + if (CCOUNT(&tp->t_outq) == 0) { + ++sc->sc_bytessent; + (void) putc(PPP_FLAG, &tp->t_outq); + } + + /* Calculate the FCS for the first mbuf's worth. */ + sc->sc_outfcs = pppfcs(PPP_INITFCS, mtod(m, u_char *), m->m_len); + } + + for (;;) { + start = mtod(m, u_char *); + len = m->m_len; + stop = start + len; + while (len > 0) { + /* + * Find out how many bytes in the string we can + * handle without doing something special. + */ + for (cp = start; cp < stop; cp++) + if (ESCAPE_P(*cp)) + break; + n = cp - start; + if (n) { +#ifndef RB_LEN + /* NetBSD (0.9 or later), 4.3-Reno or similar. */ + ndone = n - b_to_q(start, n, &tp->t_outq); +#else +#ifdef NetBSD + /* NetBSD with 2-byte ring buffer entries */ + ndone = rb_cwrite(&tp->t_out, start, n); +#else + /* 386BSD, FreeBSD */ + int cc, nleft; + for (nleft = n; nleft > 0; nleft -= cc) { + if ((cc = RB_CONTIGPUT(&tp->t_out)) == 0) + break; + cc = min (cc, nleft); + bcopy((char *)start + n - nleft, tp->t_out.rb_tl, cc); + tp->t_out.rb_tl = RB_ROLLOVER(&tp->t_out, + tp->t_out.rb_tl + cc); + } + ndone = n - nleft; +#endif /* NetBSD */ +#endif /* RB_LEN */ + len -= ndone; + start += ndone; + sc->sc_bytessent += ndone; + + if (ndone < n) + break; /* packet doesn't fit */ + } + /* + * If there are characters left in the mbuf, + * the first one must be special.. + * Put it out in a different form. + */ + if (len) { + if (putc(PPP_ESCAPE, &tp->t_outq)) + break; + if (putc(*start ^ PPP_TRANS, &tp->t_outq)) { + (void) unputc(&tp->t_outq); + break; + } + sc->sc_bytessent += 2; + start++; + len--; + } + } + /* + * If we didn't empty this mbuf, remember where we're up to. + * If we emptied the last mbuf, try to add the FCS and closing + * flag, and if we can't, leave sc_outm pointing to m, but with + * m->m_len == 0, to remind us to output the FCS and flag later. + */ + done = len == 0; + if (done && m->m_next == NULL) { + u_char *p, *q; + int c; + u_char endseq[8]; + + /* + * We may have to escape the bytes in the FCS. + */ + p = endseq; + c = ~sc->sc_outfcs & 0xFF; + if (ESCAPE_P(c)) { + *p++ = PPP_ESCAPE; + *p++ = c ^ PPP_TRANS; + } else + *p++ = c; + c = (~sc->sc_outfcs >> 8) & 0xFF; + if (ESCAPE_P(c)) { + *p++ = PPP_ESCAPE; + *p++ = c ^ PPP_TRANS; + } else + *p++ = c; + *p++ = PPP_FLAG; + + /* + * Try to output the FCS and flag. If the bytes + * don't all fit, back out. + */ + for (q = endseq; q < p; ++q) + if (putc(*q, &tp->t_outq)) { + done = 0; + for (; q > endseq; --q) + unputc(&tp->t_outq); + break; + } + } + + if (!done) { + m->m_data = start; + m->m_len = len; + sc->sc_outm = m; + if (tp->t_oproc != NULL) + (*tp->t_oproc)(tp); + return; /* can't do any more at the moment */ + } + + /* Finished with this mbuf; free it and move on. */ + MFREE(m, m2); + if (m2 == NULL) + break; + + m = m2; + sc->sc_outfcs = pppfcs(sc->sc_outfcs, mtod(m, u_char *), m->m_len); + } + + /* Finished a packet */ + sc->sc_outm = NULL; + sc->sc_bytessent++; /* account for closing flag */ + sc->sc_if.if_opackets++; + sc->sc_if.if_obytes = sc->sc_bytessent; + } +} + +/* + * Allocate enough mbuf to handle current MRU. + */ +static int +pppinit(sc) + register struct ppp_softc *sc; +{ + struct mbuf *m, **mp; + int len = HDROFF + sc->sc_mru + PPP_HEADER_LEN + PPP_FCS_LEN; + int s; + + s = splimp(); + for (mp = &sc->sc_m; (m = *mp) != NULL; mp = &m->m_next) + if ((len -= M_DATASIZE(m)) <= 0) { + splx(s); + return (1); + } + + for (;; mp = &m->m_next) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) { + m_freem(sc->sc_m); + sc->sc_m = NULL; + splx(s); + printf("ppp%d: can't allocate mbuf\n", sc->sc_if.if_unit); + return (0); + } + *mp = m; + MCLGET(m, M_DONTWAIT); + if ((len -= M_DATASIZE(m)) <= 0) { + splx(s); + return (1); + } + } +} + +/* + * Copy mbuf chain. Would like to use m_copy(), but we need a real copy + * of the data, not just copies of pointers to the data. + */ +static struct mbuf * +ppp_btom(sc) + struct ppp_softc *sc; +{ + register struct mbuf *m, **mp; + struct mbuf *top = sc->sc_m; + + /* + * First check current mbuf. If we have more than a small mbuf, + * return the whole cluster and set beginning of buffer to the + * next mbuf. + * Else, copy the current bytes into a small mbuf, attach the new + * mbuf to the end of the chain and set beginning of buffer to the + * current mbuf. + */ + + if (sc->sc_mc->m_len > MHLEN) { + sc->sc_m = sc->sc_mc->m_next; + sc->sc_mc->m_next = NULL; + } + else { + /* rather than waste a whole cluster on <= MHLEN bytes, + alloc a small mbuf and copy to it */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return (NULL); + + bcopy(mtod(sc->sc_mc, caddr_t), mtod(m, caddr_t), sc->sc_mc->m_len); + m->m_len = sc->sc_mc->m_len; + for (mp = ⊤ *mp != sc->sc_mc; mp = &(*mp)->m_next) + ; + *mp = m; + sc->sc_m = sc->sc_mc; + } + + /* + * Try to allocate enough extra mbufs to handle the next packet. + */ + if (pppinit(sc) == 0) { + m_freem(top); + if (pppinit(sc) == 0) + sc->sc_if.if_flags &= ~IFF_UP; + return (NULL); + } + + return (top); +} + +/* + * tty interface receiver interrupt. + */ +#define COMPTYPE(proto) ((proto) == PPP_VJC_COMP? TYPE_COMPRESSED_TCP: \ + TYPE_UNCOMPRESSED_TCP) + +void +pppinput(c, tp) + int c; + register struct tty *tp; +{ + register struct ppp_softc *sc; + struct mbuf *m; + struct ifqueue *inq; + int s, ilen, xlen, proto; + struct ppp_header hdr; + + tk_nin++; + sc = (struct ppp_softc *)tp->t_sc; + if (sc == NULL) + return; + + ++sc->sc_if.if_ibytes; + + if (c & TTY_FE) { + /* framing error or overrun on this char - abort packet */ + if (ppp_debug) + printf("ppp%d: bad char %x\n", sc->sc_if.if_unit, c); + goto flush; + } + + c &= 0xff; + + if (sc->sc_if.if_unit == ppp_raw_in_debug) { + ppp_rawin[ppp_rawin_count++] = c; + if (ppp_rawin_count >= sizeof(ppp_rawin)) { + printf("raw ppp%d: ", ppp_raw_in_debug); + pppdumpb(ppp_rawin, ppp_rawin_count); + ppp_rawin_count = 0; + } + } + + if (c == PPP_FLAG) { + ilen = sc->sc_ilen; + sc->sc_ilen = 0; + + if (sc->sc_flags & SC_FLUSH + || ilen > 0 && sc->sc_fcs != PPP_GOODFCS) { +#ifdef VJC + /* + * If we've missed a packet, we must toss subsequent compressed + * packets which don't have an explicit connection ID. + */ + sl_uncompress_tcp(NULL, 0, TYPE_ERROR, &sc->sc_comp); +#endif + if ((sc->sc_flags & SC_FLUSH) == 0){ + if (ppp_debug) + printf("ppp%d: bad fcs\n", sc->sc_if.if_unit); + sc->sc_if.if_ierrors++; + } else + sc->sc_flags &= ~SC_FLUSH; + return; + } + + if (ilen < PPP_HEADER_LEN + PPP_FCS_LEN) { + if (ilen) { + if (ppp_debug) + printf("ppp%d: too short (%d)\n", sc->sc_if.if_unit, ilen); + sc->sc_if.if_ierrors++; + } + return; + } + + /* + * Remove FCS trailer. Somewhat painful... + */ + ilen -= 2; + if (--sc->sc_mc->m_len == 0) { + for (m = sc->sc_m; m->m_next != sc->sc_mc; m = m->m_next) + ; + sc->sc_mc = m; + } + sc->sc_mc->m_len--; + + sc->sc_if.if_ipackets++; + m = sc->sc_m; + + if (ppp_async_in_debug) { + printf("ppp%d: got %d bytes\n", sc->sc_if.if_unit, ilen); + pppdumpm(m, ilen); + } + + hdr = *mtod(m, struct ppp_header *); + proto = ntohs(hdr.ph_protocol); + +#ifdef VJC + /* + * See if we have a VJ-compressed packet to uncompress. + */ + if (proto == PPP_VJC_COMP || proto == PPP_VJC_UNCOMP) { + char *pkttype = proto == PPP_VJC_COMP? "": "un"; + + if (sc->sc_flags & SC_REJ_COMP_TCP) { + if (ppp_debug) + printf("ppp%d: %scomp pkt w/o compression; flags 0x%x\n", + sc->sc_if.if_unit, pkttype, sc->sc_flags); + sc->sc_if.if_ierrors++; + return; + } + + m->m_data += PPP_HEADER_LEN; + m->m_len -= PPP_HEADER_LEN; + ilen -= PPP_HEADER_LEN; + xlen = sl_uncompress_tcp_part((u_char **)(&m->m_data), + m->m_len, ilen, + COMPTYPE(proto), &sc->sc_comp); + + if (xlen == 0) { + if (ppp_debug) + printf("ppp%d: sl_uncompress failed on type %scomp\n", + sc->sc_if.if_unit, pkttype); + sc->sc_if.if_ierrors++; + return; + } + + /* adjust the first mbuf by the decompressed amt */ + xlen += PPP_HEADER_LEN; + m->m_len += xlen - ilen; + ilen = xlen; + m->m_data -= PPP_HEADER_LEN; + proto = PPP_IP; + +#if NBPFILTER > 0 + /* put the ppp header back in place */ + hdr.ph_protocol = htons(PPP_IP); + *mtod(m, struct ppp_header *) = hdr; +#endif /* NBPFILTER */ + } +#endif /* VJC */ + + /* get this packet as an mbuf chain */ + if ((m = ppp_btom(sc)) == NULL) { + sc->sc_if.if_ierrors++; + return; + } + m->m_pkthdr.len = ilen; + m->m_pkthdr.rcvif = &sc->sc_if; + +#if NBPFILTER > 0 + /* See if bpf wants to look at the packet. */ + if (sc->sc_bpf) + bpf_mtap(sc->sc_bpf, m); +#endif + + switch (proto) { +#ifdef INET + case PPP_IP: + /* + * IP packet - take off the ppp header and pass it up to IP. + */ + if ((sc->sc_if.if_flags & IFF_UP) == 0) { + /* interface is down - drop the packet. */ + m_freem(m); + sc->sc_if.if_ierrors++; + return; + } + m->m_pkthdr.len -= PPP_HEADER_LEN; + m->m_data += PPP_HEADER_LEN; + m->m_len -= PPP_HEADER_LEN; + schednetisr(NETISR_IP); + inq = &ipintrq; + break; +#endif + + default: + /* + * Some other protocol - place on input queue for read(). + * Put a placeholder byte in canq for ttselect()/ttnread(). + */ + putc(0, &tp->t_canq); + ttwakeup(tp); + inq = &sc->sc_inq; + break; + } + + /* + * Put the packet on the appropriate input queue. + */ + s = splimp(); + if (IF_QFULL(inq)) { + IF_DROP(inq); + if (ppp_debug) + printf("ppp%d: queue full\n", sc->sc_if.if_unit); + sc->sc_if.if_ierrors++; + sc->sc_if.if_iqdrops++; + m_freem(m); + } else + IF_ENQUEUE(inq, m); + + splx(s); + return; + } + + if (sc->sc_flags & SC_FLUSH) + return; + if (c == PPP_ESCAPE) { + sc->sc_flags |= SC_ESCAPED; + return; + } + if (c < 0x20 && (sc->sc_rasyncmap & (1 << c))) + return; + + if (sc->sc_flags & SC_ESCAPED) { + sc->sc_flags &= ~SC_ESCAPED; + c ^= PPP_TRANS; + } + + /* + * Initialize buffer on first octet received. + * First octet could be address or protocol (when compressing + * address/control). + * Second octet is control. + * Third octet is first or second (when compressing protocol) + * octet of protocol. + * Fourth octet is second octet of protocol. + */ + if (sc->sc_ilen == 0) { + /* reset the first input mbuf */ + m = sc->sc_m; + m->m_len = 0; + m->m_data = M_DATASTART(sc->sc_m) + HDROFF; + sc->sc_mc = m; + sc->sc_mp = mtod(m, char *); + sc->sc_fcs = PPP_INITFCS; + if (c != PPP_ALLSTATIONS) { + if (sc->sc_flags & SC_REJ_COMP_AC) { + if (ppp_debug) + printf("ppp%d: missing ALLSTATIONS, got 0x%x; flags %x\n", + sc->sc_if.if_unit, c, sc->sc_flags); + goto flush; + } + *sc->sc_mp++ = PPP_ALLSTATIONS; + *sc->sc_mp++ = PPP_UI; + sc->sc_ilen += 2; + m->m_len += 2; + } + } + if (sc->sc_ilen == 1 && c != PPP_UI) { + if (ppp_debug) + printf("ppp%d: missing UI, got 0x%x\n", sc->sc_if.if_unit, c); + goto flush; + } + if (sc->sc_ilen == 2 && (c & 1) == 1) { + /* RFC1331 says we have to accept a compressed protocol */ + *sc->sc_mp++ = 0; + sc->sc_ilen++; + sc->sc_mc->m_len++; + } + if (sc->sc_ilen == 3 && (c & 1) == 0) { + if (ppp_debug) + printf("ppp%d: bad protocol %x\n", sc->sc_if.if_unit, + (sc->sc_mp[-1] << 8) + c); + goto flush; + } + + /* packet beyond configured mru? */ + if (++sc->sc_ilen > sc->sc_mru + PPP_HEADER_LEN + PPP_FCS_LEN) { + if (ppp_debug) + printf("ppp%d: packet too big\n", sc->sc_if.if_unit); + goto flush; + } + + /* is this mbuf full? */ + m = sc->sc_mc; + if (M_TRAILINGSPACE(m) <= 0) { + sc->sc_mc = m = m->m_next; + if (m == NULL) { + printf("ppp%d: too few input mbufs!\n", sc->sc_if.if_unit); + goto flush; + } + m->m_len = 0; + m->m_data = M_DATASTART(m); + sc->sc_mp = mtod(m, char *); + } + + ++m->m_len; + *sc->sc_mp++ = c; + sc->sc_fcs = PPP_FCS(sc->sc_fcs, c); + return; + + flush: + sc->sc_if.if_ierrors++; + sc->sc_flags |= SC_FLUSH; +} + +/* + * Process an ioctl request to interface. + */ +pppioctl(ifp, cmd, data) + register struct ifnet *ifp; + int cmd; + caddr_t data; +{ + struct proc *p = curproc; /* XXX */ + register struct ppp_softc *sc = &ppp_softc[ifp->if_unit]; + register struct ifaddr *ifa = (struct ifaddr *)data; + register struct ifreq *ifr = (struct ifreq *)data; + int s = splimp(), error = 0; + + + switch (cmd) { + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_RUNNING) == 0) + ifp->if_flags &= ~IFF_UP; + break; + + case SIOCSIFADDR: + if (ifa->ifa_addr->sa_family != AF_INET) + error = EAFNOSUPPORT; + break; + + case SIOCSIFDSTADDR: + if (ifa->ifa_addr->sa_family != AF_INET) + error = EAFNOSUPPORT; + break; + + case SIOCSIFMTU: + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + sc->sc_if.if_mtu = ifr->ifr_mtu; + break; + + case SIOCGIFMTU: + ifr->ifr_mtu = sc->sc_if.if_mtu; + break; + + default: + error = EINVAL; + } + splx(s); + return (error); +} + +#define MAX_DUMP_BYTES 128 + +static void +pppdumpm(m0, pktlen) + struct mbuf *m0; + int pktlen; +{ + char buf[2*MAX_DUMP_BYTES+4]; + char *bp = buf; + struct mbuf *m; + static char digits[] = "0123456789abcdef"; + + for (m = m0; m && pktlen; m = m->m_next) { + int l = m->m_len; + u_char *rptr = (u_char *)m->m_data; + + if (pktlen > 0) { + l = min(l, pktlen); + pktlen -= l; + } + while (l--) { + if (bp > buf + sizeof(buf) - 4) + goto done; + *bp++ = digits[*rptr >> 4]; /* convert byte to ascii hex */ + *bp++ = digits[*rptr++ & 0xf]; + } + + if (m->m_next) { + if (bp > buf + sizeof(buf) - 3) + goto done; + *bp++ = '|'; + } + } +done: + if (m && pktlen) + *bp++ = '>'; + *bp = 0; + printf("%s\n", buf); +} + +static void +pppdumpb(b, l) + u_char *b; + int l; +{ + char buf[2*MAX_DUMP_BYTES+4]; + char *bp = buf; + static char digits[] = "0123456789abcdef"; + + while (l--) { + *bp++ = digits[*b >> 4]; /* convert byte to ascii hex */ + *bp++ = digits[*b++ & 0xf]; + if (bp >= buf + sizeof(buf) - 2) { + *bp++ = '>'; + break; + } + } + + *bp = 0; + printf("%s\n", buf); +} + + +#endif /* NPPP > 0 */ diff --git a/sys/net/if_ppp.h b/sys/net/if_ppp.h new file mode 100644 index 0000000000..5c41bf0a63 --- /dev/null +++ b/sys/net/if_ppp.h @@ -0,0 +1,119 @@ +/* + * if_ppp.h - Point-to-Point Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Modified by Paul Mackerras (paulus@cs.anu.edu.au) + * Added PPP_MRU, sc_outm, sc_fastq, sc_bpf. + * + * $Id$ + * From: if_ppp.h,v 1.4 1993/08/29 11:22:37 paulus Exp $ + */ + +/* + * Standard PPP header. + */ +struct ppp_header { + u_char ph_address; /* Address Field */ + u_char ph_control; /* Control Field */ + u_short ph_protocol; /* Protocol Field */ +}; + +#define PPP_HEADER_LEN 4 /* octets, must == sizeof(struct ppp_header) */ +#define PPP_FCS_LEN 2 /* octets for FCS */ + +#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ +#define PPP_UI 0x03 /* Unnumbered Information */ +#define PPP_FLAG 0x7e /* Flag Sequence */ +#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */ +#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */ + +/* + * Protocol types. + */ +#define PPP_IP 0x21 /* Internet Protocol */ +#define PPP_XNS 0x25 /* Xerox NS */ +#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */ +#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */ +#define PPP_LCP 0xc021 /* Link Control Protocol */ + +/* + * Important FCS values. + */ +#define PPP_INITFCS 0xffff /* Initial FCS value */ +#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ +#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) + +#define PPP_MTU 1500 /* Default MTU (size of Info field) */ +#define PPP_MRU 1500 /* Default MRU (max receive unit) */ +#define PPP_MAXMRU 65000 /* Largest MRU we allow */ +#define PPP_HIWAT 400 /* Don't start a new packet if HIWAT on que */ + +struct ppp_softc { + struct ifnet sc_if; /* network-visible interface */ + u_int sc_flags; /* see below */ + struct tty *sc_ttyp; /* pointer to tty structure */ + struct mbuf *sc_outm; /* mbuf chain being output currently */ + struct mbuf *sc_m; /* pointer to input mbuf chain */ + struct mbuf *sc_mc; /* pointer to current input mbuf */ + char *sc_mp; /* pointer to next char in input mbuf */ + short sc_ilen; /* length of input-packet-so-far */ + u_short sc_fcs; /* FCS so far (input) */ + u_short sc_outfcs; /* FCS so far for output packet */ + short sc_mru; /* max receive unit */ + u_long sc_asyncmap; /* async control character map */ + u_long sc_rasyncmap; /* receive async control char map */ + struct ifqueue sc_inq; /* TTY side input queue */ + struct ifqueue sc_fastq; /* IP interactive output packet queue */ +#ifdef VJC + struct slcompress sc_comp; /* vjc control buffer */ +#endif + u_int sc_bytessent; + u_int sc_bytesrcvd; + caddr_t sc_bpf; +}; + +/* flags */ +#define SC_COMP_PROT 0x00000001 /* protocol compression (output) */ +#define SC_COMP_AC 0x00000002 /* header compression (output) */ +#define SC_COMP_TCP 0x00000004 /* TCP (VJ) compression (output) */ +#define SC_NO_TCP_CCID 0x00000008 /* disable VJ connection-id comp. */ +#define SC_REJ_COMP_AC 0x00000010 /* reject adrs/ctrl comp. on input */ +#define SC_REJ_COMP_TCP 0x00000020 /* reject TCP (VJ) comp. on input */ +#define SC_MASK 0x0000ffff /* bits that user can change */ + +/* state bits */ +#define SC_ESCAPED 0x00010000 /* saw a PPP_ESCAPE */ +#define SC_FLUSH 0x00020000 /* flush input until next PPP_FLAG */ + +#define t_sc T_LINEP + +/* this stuff doesn't belong here... */ +#define PPPIOCGFLAGS _IOR('t', 90, int) /* get configuration flags */ +#define PPPIOCSFLAGS _IOW('t', 89, int) /* set configuration flags */ +#define PPPIOCGASYNCMAP _IOR('t', 88, int) /* get async map */ +#define PPPIOCSASYNCMAP _IOW('t', 87, int) /* set async map */ +#define PPPIOCGUNIT _IOR('t', 86, int) /* get ppp unit number */ +#define PPPIOCGRASYNCMAP _IOR('t', 85, int) /* get receive async map */ +#define PPPIOCSRASYNCMAP _IOW('t', 84, int) /* set receive async map */ +#define PPPIOCGMRU _IOR('t', 83, int) /* get max receive unit */ +#define PPPIOCSMRU _IOW('t', 82, int) /* set max receive unit */ + +/* old copies of PPP may have defined this */ +#if !defined(ifr_mtu) +#define ifr_mtu ifr_metric +#endif + diff --git a/sys/net/if_sl.c b/sys/net/if_sl.c index 048baaf0cd..817fa34952 100644 --- a/sys/net/if_sl.c +++ b/sys/net/if_sl.c @@ -30,15 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)if_sl.c 7.22 (Berkeley) 4/20/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00112 - * -------------------- ----- ---------------------- - * - * 30 Aug 92 Poul-Henning Kamp Stabilize SLIP on lossy lines/UARTS - * 14 Mar 93 David Greenman Upgrade bpf to match tcpdump 2.2.1 + * from: @(#)if_sl.c 7.22 (Berkeley) 4/20/91 + * $Id$ */ /* @@ -72,13 +65,14 @@ * interrupts and network activity; thus, splimp must be >= spltty. */ -/* $Header: /usr/bill/working/sys/net/RCS/if_sl.c,v 1.2 92/01/15 17:36:38 william Exp $ */ +/* $Header: /a/cvs/386BSD/src/sys/net/if_sl.c,v 1.3 1993/09/07 16:01:08 rgrimes Exp $ */ /* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */ #include "sl.h" #if NSL > 0 #include "param.h" +#include "systm.h" #include "proc.h" #include "mbuf.h" #include "buf.h" @@ -375,9 +369,10 @@ sloutput(ifp, m, dst) m_freem(m); return (ENETDOWN); /* sort of */ } - if ((sc->sc_ttyp->t_state & TS_CARR_ON) == 0) { + if (((sc->sc_ttyp->t_state & TS_CARR_ON) == 0) + && ((sc->sc_ttyp->t_cflag & CLOCAL) == 0)) { m_freem(m); - return (EHOSTUNREACH); + return (ENETDOWN); } ifq = &sc->sc_if.if_snd; if ((ip = mtod(m, struct ip *))->ip_p == IPPROTO_TCP) { @@ -429,15 +424,14 @@ slstart(tp) for (;;) { /* - * If there is more in the output queue, just send it now. + * Call output process whether or not there is any output. * We are being called in lieu of ttstart and must do what * it would. */ - if (RB_LEN(&tp->t_out) != 0) { - (*tp->t_oproc)(tp); - if (RB_LEN(&tp->t_out) > SLIP_HIWAT) - return; - } + (*tp->t_oproc)(tp); + if (RB_LEN(&tp->t_out) > SLIP_HIWAT) + return; + /* * This happens briefly when the line shuts down. */ @@ -452,7 +446,7 @@ slstart(tp) * of RBSZ in tty.h also has to be upped to be at least * SLMTU*2. */ - if (RBSZ - RB_LEN(&tp->t_out) < 2 * SLMTU + 2) + if (min(RBSZ, 4 * SLMTU + 4) - RB_LEN(&tp->t_out) < 2 * SLMTU + 2) return; /* @@ -651,7 +645,9 @@ slinput(c, tp) sc = (struct sl_softc *)tp->t_sc; if (sc == NULL) return; - if (c & TTY_ERRORMASK || !(tp->t_state & TS_CARR_ON)) { /* XXX */ + if ((c & TTY_ERRORMASK) || (((tp->t_state & TS_CARR_ON) == 0) + && ((tp->t_cflag & CLOCAL) == 0))) { + /* XXX */ sc->sc_flags |= SC_ERROR; return; } diff --git a/sys/net/if_slvar.h b/sys/net/if_slvar.h index f5a742bcd8..7956d17d36 100644 --- a/sys/net/if_slvar.h +++ b/sys/net/if_slvar.h @@ -30,17 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)if_slvar.h 7.7 (Berkeley) 5/7/91 - * - * $Header: if_slvar.h,v 1.3 89/05/31 02:25:18 van Exp $ - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00112 - * -------------------- ----- ---------------------- - * - * 30 Aug 92 Poul-Henning Kamp Stabilize SLIP on lossy lines/UARTS - * 14 Mar 93 David Greenman Upgrade bpf to match tcpdump 2.2.1 + * from: @(#)if_slvar.h 7.7 (Berkeley) 5/7/91 + * $Id$ */ /* diff --git a/sys/net/if_tun.c b/sys/net/if_tun.c index cf6880ee9f..b5adf63056 100644 --- a/sys/net/if_tun.c +++ b/sys/net/if_tun.c @@ -1,58 +1,19 @@ -/* UNFINISHED CONVERSION TO 386BSD -wfj */ - -/* if_tun.c - tunnel interface module & driver */ - /* * Copyright (c) 1988, Julian Onions. * * This source may be freely distributed, however I would be interested * in any changes that are made. - */ - -/* - * 90/02/06 15:03 - Fixed a bug in where TIOCGPGRP and TIOCSPGRP were - * mixed up. Anders Klemets - klemets@sics.se * - * $Header: if_tun.c,v 1.13 88/07/11 08:28:51 jpo Exp $ - * - * $Log: if_tun.c,v $ - * Revision 1.13 88/07/11 08:28:51 jpo - * Some tidying up - * - * Revision 1.12 87/12/10 09:16:29 jpo - * Decided the vax/mtpr was unnecessay. - * - * Revision 1.11 87/12/10 09:10:36 jpo - * Fixed some minor things & 1 major bug. - * - * Revision 1.10 87/11/04 14:27:41 jpo - * A few sanity checks added. - * - * Revision 1.9 87/11/04 14:13:45 jpo - * Added some ioctls for non-blocking & async I/O - * - * Revision 1.8 87/10/19 10:28:14 jpo - * Another touch up (sigh) - * - * Revision 1.7 87/10/19 10:25:48 jpo - * Touch up. - * - * Revision 1.6 87/10/19 09:15:14 jpo - * Touch up. - * - * Revision 1.5 87/10/19 08:34:51 jpo - * General clean up - plus sun specific fixes - * - * Revision 1.4 87/10/16 17:10:12 jpo - * Purged all ioctl read/writes and non-standard routing stuff. - * - * Revision 1.3 87/10/05 11:57:09 jpo - * More debugging - in error mainly. - * - * Revision 1.2 87/10/04 18:29:45 jpo - * Select & read/write working. + * from: Revision 1.13 88/07/11 08:28:51 jpo + * from: 90/02/06 15:03 - Fixed a bug in where + * TIOCGPGRP and TIOCSPGRP were mixed up + * $Id$ */ +/* if_tun.c - tunnel interface module & driver */ + +/* UNFINISHED CONVERSION TO 386BSD -wfj */ + #include "tun.h" #if NTUN > 0 diff --git a/sys/net/if_types.h b/sys/net/if_types.h index 70b81957cf..51dc4ae2b4 100644 --- a/sys/net/if_types.h +++ b/sys/net/if_types.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)if_types.h 7.3 (Berkeley) 6/28/90 + * from: @(#)if_types.h 7.3 (Berkeley) 6/28/90 + * $Id$ */ @@ -62,3 +63,4 @@ #define IFT_XETHER 0x1a /* obsolete 3MB experimental ethernet */ #define IFT_NSIP 0x1b /* XNS over IP */ #define IFT_SLIP 0x1c /* IP over generic TTY */ +#define IFT_PPP 0x1d /* PPP over generic TTY */ diff --git a/sys/net/netisr.h b/sys/net/netisr.h index 534c68c37c..980a3b1b8a 100644 --- a/sys/net/netisr.h +++ b/sys/net/netisr.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)netisr.h 7.8 (Berkeley) 5/7/91 + * from: @(#)netisr.h 7.8 (Berkeley) 5/7/91 + * $Id$ */ /* diff --git a/sys/net/ppp.h b/sys/net/ppp.h new file mode 100644 index 0000000000..810adb9b12 --- /dev/null +++ b/sys/net/ppp.h @@ -0,0 +1,41 @@ +/* + * ppp.h - PPP global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * from: unknown + * $Id$ + */ + +#ifndef __PPP_H__ +#define __PPP_H__ + +#define NPPP 1 /* One PPP interface supported (per process) */ + +/* + * Data Link Layer header = Address, Control, Protocol. + */ +#define ALLSTATIONS 0xff /* All-Stations Address */ +#define UI 0x03 /* Unnumbered Information */ +#define LCP 0xc021 /* Link Control Protocol */ +#define IPCP 0x8021 /* IP Control Protocol */ +#define UPAP 0xc023 /* User/Password Authentication Protocol */ +#define CHAP 0xc223 /* Crytpographic Handshake Protocol */ +#define IP_VJ_COMP 0x002d /* VJ TCP compressed IP packet */ +#define DLLHEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) +#define MTU 1500 /* Default MTU */ + +#endif /* __PPP_H__ */ diff --git a/sys/net/radix.c b/sys/net/radix.c index 5f3df88349..35ad9fd7fe 100644 --- a/sys/net/radix.c +++ b/sys/net/radix.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)radix.c 7.9 (Berkeley) 2/4/91 + * from: @(#)radix.c 7.9 (Berkeley) 2/4/91 + * $Id$ */ /* @@ -38,6 +39,7 @@ */ #ifndef RNF_NORMAL #include "param.h" +#include "systm.h" #include "radix.h" #include "malloc.h" #define M_DONTWAIT M_NOWAIT diff --git a/sys/net/radix.h b/sys/net/radix.h index 349e969540..548503428d 100644 --- a/sys/net/radix.h +++ b/sys/net/radix.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)radix.h 7.4 (Berkeley) 6/28/90 + * from: @(#)radix.h 7.4 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/net/raw_cb.c b/sys/net/raw_cb.c index 87f6ca4b2a..cd01d41842 100644 --- a/sys/net/raw_cb.c +++ b/sys/net/raw_cb.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)raw_cb.c 7.11 (Berkeley) 6/28/90 + * from: @(#)raw_cb.c 7.11 (Berkeley) 6/28/90 + * $Id$ */ #include "param.h" diff --git a/sys/net/raw_cb.h b/sys/net/raw_cb.h index aab20b5a24..5e3be10287 100644 --- a/sys/net/raw_cb.h +++ b/sys/net/raw_cb.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)raw_cb.h 7.6 (Berkeley) 6/28/90 + * from: @(#)raw_cb.h 7.6 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/net/raw_usrreq.c b/sys/net/raw_usrreq.c index 511cff343a..297873dbf6 100644 --- a/sys/net/raw_usrreq.c +++ b/sys/net/raw_usrreq.c @@ -30,10 +30,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)raw_usrreq.c 7.9 (Berkeley) 6/28/90 + * from: @(#)raw_usrreq.c 7.9 (Berkeley) 6/28/90 + * $Id$ */ #include "param.h" +#include "systm.h" #include "mbuf.h" #include "domain.h" #include "protosw.h" diff --git a/sys/net/route.c b/sys/net/route.c index 5e2ceb910f..0f1662d875 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -30,15 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)route.c 7.22 (Berkeley) 6/27/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00095 - * -------------------- ----- ---------------------- - * - * 27 Feb 93 Charles Hannum & ??? Proper initialization of *rt - * + * from: @(#)route.c 7.22 (Berkeley) 6/27/91 + * $Id$ */ #include "param.h" diff --git a/sys/net/route.h b/sys/net/route.h index 4db57cf337..ccdf41f727 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)route.h 7.13 (Berkeley) 4/25/91 + * from: @(#)route.h 7.13 (Berkeley) 4/25/91 + * $Id$ */ /* diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index a64719bf8b..5f6b885ff6 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -30,10 +30,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)rtsock.c 7.18 (Berkeley) 6/27/91 + * from: @(#)rtsock.c 7.18 (Berkeley) 6/27/91 + * $Id$ */ #include "param.h" +#include "systm.h" #include "mbuf.h" #include "proc.h" #include "socket.h" diff --git a/sys/net/slcompress.c b/sys/net/slcompress.c index 1f23073ac4..2b8143384a 100644 --- a/sys/net/slcompress.c +++ b/sys/net/slcompress.c @@ -20,20 +20,24 @@ * Van Jacobson (van@ee.lbl.gov), Dec 31, 1989: * - Initial distribution. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00112 - * -------------------- ----- ---------------------- + * Modified March 14, 1993 by David Greenman, upgraded to slcompress.c + * from tcpdump 2.2.1. * - * 14 Mar 93 David Greenman Upgrade bpf to match tcpdump 2.2.1 + * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au, + * so that the entire packet being decompressed doesn't have + * to be in contiguous memory (just the compressed header). + * + * $Id$ + * From: slcompress.c,v 1.22 92/05/24 11:48:20 van Exp $ (LBL) */ #ifndef lint static char rcsid[] = - "@(#) $Header: slcompress.c,v 1.22 92/05/24 11:48:20 van Exp $ (LBL)"; + "$Id$"; #endif #include #include +#include #include #include #include @@ -391,6 +395,23 @@ sl_uncompress_tcp(bufp, len, type, comp) int len; u_int type; struct slcompress *comp; +{ + return sl_uncompress_tcp_part(bufp, len, len, type, comp); +} + + +/* + * Uncompress a packet of total length total_len. The first buflen + * bytes are at *bufp; this must include the entire (compressed or + * uncompressed) TCP/IP header. In addition, there must be enough + * clear space before *bufp to build a full-length TCP/IP header. + */ +int +sl_uncompress_tcp_part(bufp, buflen, total_len, type, comp) + u_char **bufp; + int buflen, total_len; + u_int type; + struct slcompress *comp; { register u_char *cp; register u_int hlen, changes; @@ -414,7 +435,7 @@ sl_uncompress_tcp(bufp, len, type, comp) cs->cs_ip.ip_sum = 0; cs->cs_hlen = hlen; INCR(sls_uncompressedin) - return (len); + return (total_len); default: goto bad; @@ -495,20 +516,21 @@ sl_uncompress_tcp(bufp, len, type, comp) * prepend 128 bytes of header). Adjust the length to account for * the new header & fill in the IP total length. */ - len -= (cp - *bufp); - if (len < 0) + buflen -= (cp - *bufp); + total_len -= (cp - *bufp); + if (buflen < 0) /* we must have dropped some characters (crc should detect * this but the old slip framing won't) */ goto bad; if ((int)cp & 3) { - if (len > 0) - (void) ovbcopy(cp, (caddr_t)((int)cp &~ 3), len); + if (buflen > 0) + (void) ovbcopy(cp, (caddr_t)((int)cp &~ 3), buflen); cp = (u_char *)((int)cp &~ 3); } cp -= cs->cs_hlen; - len += cs->cs_hlen; - cs->cs_ip.ip_len = htons(len); + total_len += cs->cs_hlen; + cs->cs_ip.ip_len = htons(total_len); BCOPY(&cs->cs_ip, cp, cs->cs_hlen); *bufp = cp; @@ -521,7 +543,7 @@ sl_uncompress_tcp(bufp, len, type, comp) changes = (changes & 0xffff) + (changes >> 16); ((struct ip *)cp)->ip_sum = ~ changes; } - return (len); + return (total_len); bad: comp->flags |= SLF_TOSS; INCR(sls_errorin) diff --git a/sys/net/slcompress.h b/sys/net/slcompress.h index f799706fa1..4554d12eb9 100644 --- a/sys/net/slcompress.h +++ b/sys/net/slcompress.h @@ -2,7 +2,9 @@ /* * Definitions for tcp compression routines. * - * $Header: slcompress.h,v 1.10 89/12/31 08:53:02 van Exp $ + * $Id$ + * From: slcompress.h,v 1.13 1993/08/09 02:37:32 paulus Exp + * From: slcompress.h,v 1.10 89/12/31 08:53:02 van Exp * * Copyright (c) 1989 Regents of the University of California. * All rights reserved. @@ -37,6 +39,9 @@ * * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: * - Initial distribution. + * + * Paul Mackerras (paulus@cs.anu.edu.au), June 1993: + * - added sl_uncompress_tcp_part. */ #define MAX_STATES 16 /* must be > 2 and < 256 */ @@ -151,7 +156,11 @@ struct slcompress { /* flag values */ #define SLF_TOSS 1 /* tossing rcvd frames because of input err */ -extern void sl_compress_init(/* struct slcompress * */); -extern u_char sl_compress_tcp(/* struct mbuf *, struct ip *, - struct slcompress *, int compress_cid_flag */); -extern int sl_uncompress_tcp(/* u_char **, int, u_char, struct slcompress * */); +extern void sl_compress_init __P((struct slcompress *)); +extern u_char sl_compress_tcp __P((struct mbuf *m, struct ip *ip, + struct slcompress *, int comp_cid_flag)); +extern int sl_uncompress_tcp __P((u_char **bufp, int len, u_int type, + struct slcompress *)); +extern int sl_uncompress_tcp_part __P((u_char **bufp, int buflen, + int total_len, u_int type, + struct slcompress *)); diff --git a/sys/net/slip.h b/sys/net/slip.h index 806f95ae3e..9bbdba3244 100644 --- a/sys/net/slip.h +++ b/sys/net/slip.h @@ -1,9 +1,4 @@ /* - * Definitions that user level programs might need to know to interact - * with serial line IP (slip) lines. - - * @(#) $Header: slip.h,v 1.4 92/02/02 17:26:02 leres Exp $ (LBL) - * * Copyright (c) 1990 Regents of the University of California. * All rights reserved. * @@ -18,6 +13,14 @@ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * from: unknown + * $Id$ + */ + +/* + * Definitions that user level programs might need to know to interact + * with serial line IP (slip) lines. */ /* diff --git a/sys/netccitt/README.hdlc b/sys/netccitt/README.hdlc index d5b9d7278e..5b4d587eaa 100644 --- a/sys/netccitt/README.hdlc +++ b/sys/netccitt/README.hdlc @@ -45,5 +45,5 @@ * * History: * - * + * $Id$ */ diff --git a/sys/netccitt/README.packet b/sys/netccitt/README.packet index 8625b05766..28e0288be6 100644 --- a/sys/netccitt/README.packet +++ b/sys/netccitt/README.packet @@ -32,4 +32,5 @@ * * History: * + * $Id$ */ diff --git a/sys/netccitt/ccitt_proto.c b/sys/netccitt/ccitt_proto.c index 35f8cf26da..1b15a044e0 100644 --- a/sys/netccitt/ccitt_proto.c +++ b/sys/netccitt/ccitt_proto.c @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ccitt_proto.c 7.5 (Berkeley) 8/30/90 + * from: @(#)ccitt_proto.c 7.5 (Berkeley) 8/30/90 + * $Id$ */ #define HDLC #include "param.h" diff --git a/sys/netccitt/hd_debug.c b/sys/netccitt/hd_debug.c index 9e313c383f..b1a54b3e8f 100644 --- a/sys/netccitt/hd_debug.c +++ b/sys/netccitt/hd_debug.c @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)hd_debug.c 7.5 (Berkeley) 5/29/91 + * from: @(#)hd_debug.c 7.5 (Berkeley) 5/29/91 + * $Id$ */ #include "param.h" diff --git a/sys/netccitt/hd_input.c b/sys/netccitt/hd_input.c index 01d22c7b32..48adb70865 100644 --- a/sys/netccitt/hd_input.c +++ b/sys/netccitt/hd_input.c @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)hd_input.c 7.7 (Berkeley) 5/29/91 + * From: @(#)hd_input.c 7.7 (Berkeley) 5/29/91 + * $Id$ */ #include "param.h" @@ -54,6 +55,18 @@ #include "hd_var.h" #include "x25.h" +/* + * forward references + */ +static +frame_reject (struct hdcb *hdp, int rejectcode, struct Hdlc_iframe *frame); + +static +rej_routine (struct hdcb *hdp, int rejnr); + +static +free_iframes (struct hdcb *hdp, int *nr, int finalbit); + /* * HDLC INPUT INTERFACE * @@ -444,6 +457,7 @@ int rear, static frame_reject (hdp, rejectcode, frame) struct hdcb *hdp; +int rejectcode; struct Hdlc_iframe *frame; { register struct Frmr_frame *frmr = &hd_frmr; diff --git a/sys/netccitt/hd_output.c b/sys/netccitt/hd_output.c index 162c8dab0d..133b2d34ae 100644 --- a/sys/netccitt/hd_output.c +++ b/sys/netccitt/hd_output.c @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)hd_output.c 7.6 (Berkeley) 5/29/91 + * from: @(#)hd_output.c 7.6 (Berkeley) 5/29/91 + * $Id$ */ #include "param.h" diff --git a/sys/netccitt/hd_subr.c b/sys/netccitt/hd_subr.c index bc881ecdad..8a3a7d9909 100644 --- a/sys/netccitt/hd_subr.c +++ b/sys/netccitt/hd_subr.c @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)hd_subr.c 7.6 (Berkeley) 5/29/91 + * from: @(#)hd_subr.c 7.6 (Berkeley) 5/29/91 + * $Id$ */ #include "param.h" diff --git a/sys/netccitt/hd_timer.c b/sys/netccitt/hd_timer.c index 8d69c0c913..95b70d1b75 100644 --- a/sys/netccitt/hd_timer.c +++ b/sys/netccitt/hd_timer.c @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)hd_timer.c 7.4 (Berkeley) 5/29/91 + * from: @(#)hd_timer.c 7.4 (Berkeley) 5/29/91 + * $Id$ */ #include "param.h" diff --git a/sys/netccitt/hd_var.h b/sys/netccitt/hd_var.h index 9dbd162502..4d67d2fe47 100644 --- a/sys/netccitt/hd_var.h +++ b/sys/netccitt/hd_var.h @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)hd_var.h 7.4 (Berkeley) 5/29/91 + * from: @(#)hd_var.h 7.4 (Berkeley) 5/29/91 + * $Id$ */ /* diff --git a/sys/netccitt/hdlc.h b/sys/netccitt/hdlc.h index 283eb63bb4..09613edbb4 100644 --- a/sys/netccitt/hdlc.h +++ b/sys/netccitt/hdlc.h @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)hdlc.h 7.4 (Berkeley) 5/6/91 + * from: @(#)hdlc.h 7.4 (Berkeley) 5/6/91 + * $Id$ */ #ifndef ORDER4 diff --git a/sys/netccitt/if_x25subr.c b/sys/netccitt/if_x25subr.c index 07d916cfa7..0e1e7bc594 100644 --- a/sys/netccitt/if_x25subr.c +++ b/sys/netccitt/if_x25subr.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)if_x25subr.c 7.14 (Berkeley) 6/26/91 + * from: @(#)if_x25subr.c 7.14 (Berkeley) 6/26/91 + * $Id$ */ #include "param.h" diff --git a/sys/netccitt/pk.h b/sys/netccitt/pk.h index f27d902e8e..b741b236af 100644 --- a/sys/netccitt/pk.h +++ b/sys/netccitt/pk.h @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pk.h 7.8 (Berkeley) 4/30/91 + * from: @(#)pk.h 7.8 (Berkeley) 4/30/91 + * $Id$ */ /* diff --git a/sys/netccitt/pk_acct.c b/sys/netccitt/pk_acct.c index a66906c6e7..4a0972d76c 100644 --- a/sys/netccitt/pk_acct.c +++ b/sys/netccitt/pk_acct.c @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pk_acct.c 7.6 (Berkeley) 6/26/91 + * from: @(#)pk_acct.c 7.6 (Berkeley) 6/26/91 + * $Id$ */ #include "param.h" diff --git a/sys/netccitt/pk_debug.c b/sys/netccitt/pk_debug.c index 94c29eaddf..0b3c523f68 100644 --- a/sys/netccitt/pk_debug.c +++ b/sys/netccitt/pk_debug.c @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pk_debug.c 7.7 (Berkeley) 5/9/91 + * from: @(#)pk_debug.c 7.7 (Berkeley) 5/9/91 + * $Id$ */ #include "param.h" diff --git a/sys/netccitt/pk_input.c b/sys/netccitt/pk_input.c index 6f4b358146..edeefd93ea 100644 --- a/sys/netccitt/pk_input.c +++ b/sys/netccitt/pk_input.c @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pk_input.c 7.14 (Berkeley) 7/16/91 + * from: @(#)pk_input.c 7.14 (Berkeley) 7/16/91 + * $Id$ */ #include "param.h" diff --git a/sys/netccitt/pk_output.c b/sys/netccitt/pk_output.c index ff1d17e1cf..789adc12f5 100644 --- a/sys/netccitt/pk_output.c +++ b/sys/netccitt/pk_output.c @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pk_output.c 7.10 (Berkeley) 5/29/91 + * from: @(#)pk_output.c 7.10 (Berkeley) 5/29/91 + * $Id$ */ #include "param.h" diff --git a/sys/netccitt/pk_subr.c b/sys/netccitt/pk_subr.c index e1bf921c6b..81b55affc8 100644 --- a/sys/netccitt/pk_subr.c +++ b/sys/netccitt/pk_subr.c @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pk_subr.c 7.16 (Berkeley) 6/6/91 + * from: @(#)pk_subr.c 7.16 (Berkeley) 6/6/91 + * $Id$ */ #include "param.h" diff --git a/sys/netccitt/pk_timer.c b/sys/netccitt/pk_timer.c index 91106e7489..8fd8b8cb02 100644 --- a/sys/netccitt/pk_timer.c +++ b/sys/netccitt/pk_timer.c @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pk_timer.c 7.5 (Berkeley) 5/29/91 + * from: @(#)pk_timer.c 7.5 (Berkeley) 5/29/91 + * $Id$ */ #include "param.h" diff --git a/sys/netccitt/pk_usrreq.c b/sys/netccitt/pk_usrreq.c index 7bfcd0a886..ea61c933d5 100644 --- a/sys/netccitt/pk_usrreq.c +++ b/sys/netccitt/pk_usrreq.c @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pk_usrreq.c 7.16 (Berkeley) 6/27/91 + * from: @(#)pk_usrreq.c 7.16 (Berkeley) 6/27/91 + * $Id$ */ #include "param.h" @@ -55,6 +56,16 @@ #include "pk.h" #include "pk_var.h" +/* + * forward references + */ + +static +old_to_new (struct mbuf *m); + +static +new_to_old (struct mbuf *m); + /* * * X.25 Packet level protocol interface to socket abstraction. diff --git a/sys/netccitt/pk_var.h b/sys/netccitt/pk_var.h index e8bfe3bf28..ff1b558864 100644 --- a/sys/netccitt/pk_var.h +++ b/sys/netccitt/pk_var.h @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pk_var.h 7.11 (Berkeley) 5/29/91 + * from: @(#)pk_var.h 7.11 (Berkeley) 5/29/91 + * $Id$ */ diff --git a/sys/netccitt/x25.h b/sys/netccitt/x25.h index 5c25c6bec3..f4d77c90ac 100644 --- a/sys/netccitt/x25.h +++ b/sys/netccitt/x25.h @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)x25.h 7.8 (Berkeley) 5/29/91 + * from: @(#)x25.h 7.8 (Berkeley) 5/29/91 + * $Id$ */ #ifdef KERNEL diff --git a/sys/netccitt/x25acct.h b/sys/netccitt/x25acct.h index fde84b3fb7..6d2b6f64d7 100644 --- a/sys/netccitt/x25acct.h +++ b/sys/netccitt/x25acct.h @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)x25acct.h 7.2 (Berkeley) 5/11/90 + * from: @(#)x25acct.h 7.2 (Berkeley) 5/11/90 + * $Id$ */ /* diff --git a/sys/netccitt/x25err.h b/sys/netccitt/x25err.h index fb842ea1f9..4c0757c0a0 100644 --- a/sys/netccitt/x25err.h +++ b/sys/netccitt/x25err.h @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)x25err.h 7.2 (Berkeley) 5/11/90 + * from: @(#)x25err.h 7.2 (Berkeley) 5/11/90 + * $Id$ */ /* diff --git a/sys/netinet/icmp_var.h b/sys/netinet/icmp_var.h index 4e311e4afb..51570cef49 100644 --- a/sys/netinet/icmp_var.h +++ b/sys/netinet/icmp_var.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)icmp_var.h 7.5 (Berkeley) 6/28/90 + * from: @(#)icmp_var.h 7.5 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c index d4e60a5ec7..14e0b0cef5 100644 --- a/sys/netinet/if_ether.c +++ b/sys/netinet/if_ether.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)if_ether.c 7.13 (Berkeley) 10/31/90 + * from: @(#)if_ether.c 7.13 (Berkeley) 10/31/90 + * $Id$ */ /* @@ -241,8 +242,10 @@ arpresolve(ac, m, destip, desten, usetrailers) } at->at_timer = 0; /* restart the timer */ if (at->at_flags & ATF_COM) { /* entry IS complete */ - bcopy((caddr_t)at->at_enaddr, (caddr_t)desten, - sizeof(at->at_enaddr)); + + *(int *) desten = *(int *) at->at_enaddr; + ((short *) desten)[2] = ((short *) at->at_enaddr)[2]; + if (at->at_flags & ATF_USETRAILERS) *usetrailers = 1; splx(s); diff --git a/sys/netinet/if_ether.h b/sys/netinet/if_ether.h index ef86f7cae9..3ad2cd6f3f 100644 --- a/sys/netinet/if_ether.h +++ b/sys/netinet/if_ether.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)if_ether.h 7.5 (Berkeley) 6/28/90 + * from: @(#)if_ether.h 7.5 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netinet/in.c b/sys/netinet/in.c index 75824d1a20..cf261759fb 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)in.c 7.17 (Berkeley) 4/20/91 + * from: @(#)in.c 7.17 (Berkeley) 4/20/91 + * $Id$ */ #include "param.h" diff --git a/sys/netinet/in.h b/sys/netinet/in.h index be63cbffd1..df657adb4d 100644 --- a/sys/netinet/in.h +++ b/sys/netinet/in.h @@ -30,9 +30,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)in.h 7.11 (Berkeley) 4/20/91 + * from: @(#)in.h 7.11 (Berkeley) 4/20/91 + * $Id$ */ +#ifndef _NETINET_IN_H_ +#define _NETINET_IN_H_ + /* * Constants and structures defined by the internet system, * Per RFC 790, September 1981. @@ -149,3 +153,5 @@ struct ip_opts { struct in_addr in_makeaddr(); u_long in_netof(), in_lnaof(); #endif + +#endif /* _NETINET_IN_H_ */ diff --git a/sys/netinet/in_cksum.c b/sys/netinet/in_cksum.c index 68c9a54ddc..f0d17f1328 100644 --- a/sys/netinet/in_cksum.c +++ b/sys/netinet/in_cksum.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)in_cksum.c 7.3 (Berkeley) 6/28/90 + * from: @(#)in_cksum.c 7.3 (Berkeley) 6/28/90 + * $Id$ */ #include "../h/types.h" diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index 8b1c06e256..0ac55d931f 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)in_pcb.c 7.14 (Berkeley) 4/20/91 + * from: @(#)in_pcb.c 7.14 (Berkeley) 4/20/91 + * $Id$ */ #include "param.h" diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index efbd4e2c9b..1667ea79cd 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)in_pcb.h 7.6 (Berkeley) 6/28/90 + * from: @(#)in_pcb.h 7.6 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c index 52b282c030..8ea718ee33 100644 --- a/sys/netinet/in_proto.c +++ b/sys/netinet/in_proto.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)in_proto.c 7.5 (Berkeley) 6/28/90 + * from: @(#)in_proto.c 7.5 (Berkeley) 6/28/90 + * $Id$ */ #include "param.h" diff --git a/sys/netinet/in_systm.h b/sys/netinet/in_systm.h index c246a599c0..448e0e5da9 100644 --- a/sys/netinet/in_systm.h +++ b/sys/netinet/in_systm.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)in_systm.h 7.4 (Berkeley) 6/28/90 + * from: @(#)in_systm.h 7.4 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h index 72c48bbb0b..0daace81e6 100644 --- a/sys/netinet/in_var.h +++ b/sys/netinet/in_var.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)in_var.h 7.6 (Berkeley) 6/28/90 + * from: @(#)in_var.h 7.6 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netinet/ip.h b/sys/netinet/ip.h index 751dc4d1e0..bc93cc69dd 100644 --- a/sys/netinet/ip.h +++ b/sys/netinet/ip.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ip.h 7.10 (Berkeley) 6/28/90 + * from: @(#)ip.h 7.10 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netinet/ip_icmp.c b/sys/netinet/ip_icmp.c index 69815bde1b..1e99abb9c0 100644 --- a/sys/netinet/ip_icmp.c +++ b/sys/netinet/ip_icmp.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ip_icmp.c 7.15 (Berkeley) 4/20/91 + * from: @(#)ip_icmp.c 7.15 (Berkeley) 4/20/91 + * $Id$ */ #include "param.h" diff --git a/sys/netinet/ip_icmp.h b/sys/netinet/ip_icmp.h index 10bf7fc757..1dd385265e 100644 --- a/sys/netinet/ip_icmp.h +++ b/sys/netinet/ip_icmp.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ip_icmp.h 7.5 (Berkeley) 6/28/90 + * from: @(#)ip_icmp.h 7.5 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index 889e37004f..4d28578648 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ip_input.c 7.19 (Berkeley) 5/25/91 + * from: @(#)ip_input.c 7.19 (Berkeley) 5/25/91 + * $Id$ */ #include "param.h" diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index 58dbb05dea..0cbb18c05a 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ip_output.c 7.23 (Berkeley) 11/12/90 + * from: @(#)ip_output.c 7.23 (Berkeley) 11/12/90 + * $Id: ip_output.c,v 1.2 1993/10/16 18:26:16 rgrimes Exp $ */ #include "param.h" @@ -157,6 +158,17 @@ ip_output(m0, opt, ro, flags) if (ip->ip_src.s_addr == INADDR_ANY) ip->ip_src = IA_SIN(ia)->sin_addr; #endif + + /* + * Verify that we have any chance at all of being able to queue + * the packet or packet fragments + */ + if ((ifp->if_snd.ifq_len + ip->ip_len / ifp->if_mtu + 1) >= + ifp->if_snd.ifq_maxlen) { + error = ENOBUFS; + goto bad; + } + /* * Look for broadcast address and * and verify user is allowed to send diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h index f654393b35..606209ff50 100644 --- a/sys/netinet/ip_var.h +++ b/sys/netinet/ip_var.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ip_var.h 7.7 (Berkeley) 6/28/90 + * from: @(#)ip_var.h 7.7 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c index a87f1ac9cf..664d4326b8 100644 --- a/sys/netinet/raw_ip.c +++ b/sys/netinet/raw_ip.c @@ -30,15 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)raw_ip.c 7.8 (Berkeley) 7/25/90 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00020 - * -------------------- ----- ---------------------- - * - * 03 Nov 92 Julian Elischer Fixed memory leak that caused ping - * and traceroute to cause panic + * from: @(#)raw_ip.c 7.8 (Berkeley) 7/25/90 + * $Id$ */ #include "param.h" diff --git a/sys/netinet/tcp.h b/sys/netinet/tcp.h index d38f340a6c..a2474c5d88 100644 --- a/sys/netinet/tcp.h +++ b/sys/netinet/tcp.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tcp.h 7.7 (Berkeley) 6/28/90 + * from: @(#)tcp.h 7.7 (Berkeley) 6/28/90 + * $Id$ */ typedef u_long tcp_seq; diff --git a/sys/netinet/tcp_debug.c b/sys/netinet/tcp_debug.c index 15e0094aca..5818827601 100644 --- a/sys/netinet/tcp_debug.c +++ b/sys/netinet/tcp_debug.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tcp_debug.c 7.6 (Berkeley) 6/28/90 + * from: @(#)tcp_debug.c 7.6 (Berkeley) 6/28/90 + * $Id$ */ #ifdef TCPDEBUG diff --git a/sys/netinet/tcp_debug.h b/sys/netinet/tcp_debug.h index 4e3372b665..fd3aab6e5f 100644 --- a/sys/netinet/tcp_debug.h +++ b/sys/netinet/tcp_debug.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tcp_debug.h 7.4 (Berkeley) 6/28/90 + * from: @(#)tcp_debug.h 7.4 (Berkeley) 6/28/90 + * $Id$ */ struct tcp_debug { diff --git a/sys/netinet/tcp_fsm.h b/sys/netinet/tcp_fsm.h index e9280b5eab..9c4bfc3523 100644 --- a/sys/netinet/tcp_fsm.h +++ b/sys/netinet/tcp_fsm.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tcp_fsm.h 7.4 (Berkeley) 6/28/90 + * from: @(#)tcp_fsm.h 7.4 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index f6fcc06b75..3f043be74c 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tcp_input.c 7.25 (Berkeley) 6/30/90 + * from: @(#)tcp_input.c 7.25 (Berkeley) 6/30/90 + * $Id$ */ #include "param.h" diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c index 167b193ede..41c2174b54 100644 --- a/sys/netinet/tcp_output.c +++ b/sys/netinet/tcp_output.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tcp_output.c 7.22 (Berkeley) 8/31/90 + * from: @(#)tcp_output.c 7.22 (Berkeley) 8/31/90 + * $Id$ */ #include "param.h" diff --git a/sys/netinet/tcp_seq.h b/sys/netinet/tcp_seq.h index 6b90e1ad7a..e295693c8a 100644 --- a/sys/netinet/tcp_seq.h +++ b/sys/netinet/tcp_seq.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tcp_seq.h 7.4 (Berkeley) 6/28/90 + * from: @(#)tcp_seq.h 7.4 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index b2b1542211..95f2b9cfbe 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tcp_subr.c 7.20 (Berkeley) 12/1/90 + * from: @(#)tcp_subr.c 7.20 (Berkeley) 12/1/90 + * $Id$ */ #include "param.h" diff --git a/sys/netinet/tcp_timer.c b/sys/netinet/tcp_timer.c index e372e368d3..5eaa9258b4 100644 --- a/sys/netinet/tcp_timer.c +++ b/sys/netinet/tcp_timer.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tcp_timer.c 7.18 (Berkeley) 6/28/90 + * from: @(#)tcp_timer.c 7.18 (Berkeley) 6/28/90 + * $Id$ */ #include "param.h" diff --git a/sys/netinet/tcp_timer.h b/sys/netinet/tcp_timer.h index 095be439eb..5ce5871b2c 100644 --- a/sys/netinet/tcp_timer.h +++ b/sys/netinet/tcp_timer.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tcp_timer.h 7.8 (Berkeley) 6/28/90 + * from: @(#)tcp_timer.h 7.8 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index c5592b922a..e52f02d524 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -30,18 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tcp_usrreq.c 7.15 (Berkeley) 6/28/90 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00152 - * -------------------- ----- ---------------------- - * - * 22 Feb 93 David Greenman Increased tcp_sendspace and - * tcp_recvspace to 16k bytes - * 20 Aug 93 Rodney W. Grimes Add #ifdef's so you can go back to - * the stock 4k with a kernel config - * options "TCP_SMALLSPACE" + * from: @(#)tcp_usrreq.c 7.15 (Berkeley) 6/28/90 + * $Id$ */ #include "param.h" diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h index 0f04c3862a..b0551c52db 100644 --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tcp_var.h 7.10 (Berkeley) 6/28/90 + * from: @(#)tcp_var.h 7.10 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netinet/tcpip.h b/sys/netinet/tcpip.h index b75a813242..4c37d44b53 100644 --- a/sys/netinet/tcpip.h +++ b/sys/netinet/tcpip.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tcpip.h 7.4 (Berkeley) 6/28/90 + * from: @(#)tcpip.h 7.4 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netinet/udp.h b/sys/netinet/udp.h index ac6e55d9b7..d731c5330e 100644 --- a/sys/netinet/udp.h +++ b/sys/netinet/udp.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)udp.h 7.4 (Berkeley) 6/28/90 + * from: @(#)udp.h 7.4 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index 778fa635d0..cb875f0a75 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)udp_usrreq.c 7.20 (Berkeley) 4/20/91 + * from: @(#)udp_usrreq.c 7.20 (Berkeley) 4/20/91 + * $Id$ */ #include "param.h" diff --git a/sys/netinet/udp_var.h b/sys/netinet/udp_var.h index f9e19f8855..1887105e62 100644 --- a/sys/netinet/udp_var.h +++ b/sys/netinet/udp_var.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)udp_var.h 7.7 (Berkeley) 6/28/90 + * from: @(#)udp_var.h 7.7 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netiso/argo_debug.h b/sys/netiso/argo_debug.h index f1028b100d..23dc18a4f7 100644 --- a/sys/netiso/argo_debug.h +++ b/sys/netiso/argo_debug.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)argo_debug.h 7.4 (Berkeley) 5/6/91 + * from: @(#)argo_debug.h 7.4 (Berkeley) 5/6/91 + * $Id$ */ /***************************************************************** @@ -59,10 +60,6 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* - * $Header: argo_debug.h,v 4.6 88/07/19 15:53:40 hagens Exp $ - * $Source: /usr/argo/sys/netiso/RCS/argo_debug.h,v $ - */ #ifndef __ARGO_DEBUG__ #define __ARGO_DEBUG__ diff --git a/sys/netiso/clnl.h b/sys/netiso/clnl.h index cf7b10f196..10a3eae0f3 100644 --- a/sys/netiso/clnl.h +++ b/sys/netiso/clnl.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)clnl.h 7.3 (Berkeley) 5/6/91 + * from: @(#)clnl.h 7.3 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** diff --git a/sys/netiso/clnp.h b/sys/netiso/clnp.h index 2b4bc63207..2c8b4523ca 100644 --- a/sys/netiso/clnp.h +++ b/sys/netiso/clnp.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)clnp.h 7.8 (Berkeley) 5/6/91 + * from: @(#)clnp.h 7.8 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,8 +60,6 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: /var/src/sys/netiso/RCS/clnp.h,v 5.1 89/02/09 16:17:22 hagens Exp $ */ -/* $Source: /var/src/sys/netiso/RCS/clnp.h,v $ */ #ifndef BYTE_ORDER /* diff --git a/sys/netiso/clnp_debug.c b/sys/netiso/clnp_debug.c index 7e0621d72d..b795173a8b 100644 --- a/sys/netiso/clnp_debug.c +++ b/sys/netiso/clnp_debug.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)clnp_debug.c 7.8 (Berkeley) 5/27/91 + * from: @(#)clnp_debug.c 7.8 (Berkeley) 5/27/91 + * $Id$ */ /*********************************************************** @@ -59,8 +60,6 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: clnp_debug.c,v 4.2 88/06/29 14:58:34 hagens Exp $ */ -/* $Source: /usr/argo/sys/netargo/RCS/clnp_debug.c,v $ */ #include "types.h" #include "param.h" diff --git a/sys/netiso/clnp_er.c b/sys/netiso/clnp_er.c index c048ebd15c..7be8918d91 100644 --- a/sys/netiso/clnp_er.c +++ b/sys/netiso/clnp_er.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)clnp_er.c 7.7 (Berkeley) 5/6/91 + * from: @(#)clnp_er.c 7.7 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,8 +60,6 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: /var/src/sys/netiso/RCS/clnp_er.c,v 5.1 89/02/09 16:20:18 hagens Exp $ */ -/* $Source: /var/src/sys/netiso/RCS/clnp_er.c,v $ */ #include "param.h" #include "mbuf.h" diff --git a/sys/netiso/clnp_frag.c b/sys/netiso/clnp_frag.c index 6e5403b591..59251f2a45 100644 --- a/sys/netiso/clnp_frag.c +++ b/sys/netiso/clnp_frag.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)clnp_frag.c 7.12 (Berkeley) 5/6/91 + * from: @(#)clnp_frag.c 7.12 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,10 +60,9 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: /var/src/sys/netiso/RCS/clnp_frag.c,v 5.1 89/02/09 16:20:26 hagens Exp $ */ -/* $Source: /var/src/sys/netiso/RCS/clnp_frag.c,v $ */ #include "param.h" +#include "systm.h" #include "mbuf.h" #include "domain.h" #include "protosw.h" diff --git a/sys/netiso/clnp_input.c b/sys/netiso/clnp_input.c index d7abd5b706..a7a4565c81 100644 --- a/sys/netiso/clnp_input.c +++ b/sys/netiso/clnp_input.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)clnp_input.c 7.13 (Berkeley) 5/6/91 + * from: @(#)clnp_input.c 7.13 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,8 +60,6 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: /var/src/sys/netiso/RCS/clnp_input.c,v 5.1 89/02/09 16:20:32 hagens Exp $ */ -/* $Source: /var/src/sys/netiso/RCS/clnp_input.c,v $ */ #include "types.h" #include "param.h" diff --git a/sys/netiso/clnp_options.c b/sys/netiso/clnp_options.c index 408bf5664d..a59dfe3c3e 100644 --- a/sys/netiso/clnp_options.c +++ b/sys/netiso/clnp_options.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)clnp_options.c 7.8 (Berkeley) 5/6/91 + * from: @(#)clnp_options.c 7.8 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,8 +60,6 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: /var/src/sys/netiso/RCS/clnp_options.c,v 5.1 89/02/09 16:20:37 hagens Exp $ */ -/* $Source: /var/src/sys/netiso/RCS/clnp_options.c,v $ */ #ifdef ISO diff --git a/sys/netiso/clnp_output.c b/sys/netiso/clnp_output.c index 30524e4619..e470051503 100644 --- a/sys/netiso/clnp_output.c +++ b/sys/netiso/clnp_output.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)clnp_output.c 7.10 (Berkeley) 5/6/91 + * from: @(#)clnp_output.c 7.10 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,8 +60,6 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: /var/src/sys/netiso/RCS/clnp_output.c,v 5.0 89/02/08 12:00:15 hagens Exp $ */ -/* $Source: /var/src/sys/netiso/RCS/clnp_output.c,v $ */ #include "param.h" #include "mbuf.h" diff --git a/sys/netiso/clnp_raw.c b/sys/netiso/clnp_raw.c index 9a14a103f2..582d6e2289 100644 --- a/sys/netiso/clnp_raw.c +++ b/sys/netiso/clnp_raw.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)clnp_raw.c 7.8 (Berkeley) 5/6/91 + * from: @(#)clnp_raw.c 7.8 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,8 +60,6 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: clnp_raw.c,v 4.2 88/06/29 14:58:56 hagens Exp $ */ -/* $Source: /usr/argo/sys/netiso/RCS/clnp_raw.c,v $ */ #include "param.h" #include "mbuf.h" diff --git a/sys/netiso/clnp_stat.h b/sys/netiso/clnp_stat.h index 223fe58081..2f8cfebb07 100644 --- a/sys/netiso/clnp_stat.h +++ b/sys/netiso/clnp_stat.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)clnp_stat.h 7.4 (Berkeley) 5/6/91 + * from: @(#)clnp_stat.h 7.4 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,8 +60,6 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: /var/src/sys/netiso/RCS/clnp_stat.h,v 5.1 89/02/09 16:20:42 hagens Exp $ */ -/* $Source: /var/src/sys/netiso/RCS/clnp_stat.h,v $ */ #ifndef __CLNP_STAT__ diff --git a/sys/netiso/clnp_subr.c b/sys/netiso/clnp_subr.c index e21481715d..6cabeea90e 100644 --- a/sys/netiso/clnp_subr.c +++ b/sys/netiso/clnp_subr.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)clnp_subr.c 7.13 (Berkeley) 5/6/91 + * from: @(#)clnp_subr.c 7.13 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,13 +60,12 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: /var/src/sys/netiso/RCS/clnp_subr.c,v 5.1 89/02/09 16:20:46 hagens Exp $ */ -/* $Source: /var/src/sys/netiso/RCS/clnp_subr.c,v $ */ #ifdef ISO #include "types.h" #include "param.h" +#include "systm.h" #include "mbuf.h" #include "domain.h" #include "protosw.h" diff --git a/sys/netiso/clnp_timer.c b/sys/netiso/clnp_timer.c index acdca36f3b..c175741755 100644 --- a/sys/netiso/clnp_timer.c +++ b/sys/netiso/clnp_timer.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)clnp_timer.c 7.5 (Berkeley) 5/6/91 + * from: @(#)clnp_timer.c 7.5 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,8 +60,6 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: clnp_timer.c,v 4.2 88/06/29 14:59:05 hagens Exp $ */ -/* $Source: /usr/argo/sys/netiso/RCS/clnp_timer.c,v $ */ #include "param.h" #include "mbuf.h" diff --git a/sys/netiso/cltp_usrreq.c b/sys/netiso/cltp_usrreq.c index c0566f316c..c6cfee7512 100644 --- a/sys/netiso/cltp_usrreq.c +++ b/sys/netiso/cltp_usrreq.c @@ -30,11 +30,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)cltp_usrreq.c 7.6 (Berkeley) 6/27/91 + * from: @(#)cltp_usrreq.c 7.6 (Berkeley) 6/27/91 + * $Id$ */ #ifndef CLTPOVAL_SRC /* XXX -- till files gets changed */ #include "param.h" +#include "systm.h" #include "malloc.h" #include "mbuf.h" #include "protosw.h" diff --git a/sys/netiso/cltp_var.h b/sys/netiso/cltp_var.h index d61659784f..2ead968bf9 100644 --- a/sys/netiso/cltp_var.h +++ b/sys/netiso/cltp_var.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)cltp_var.h 7.3 (Berkeley) 6/28/90 + * from: @(#)cltp_var.h 7.3 (Berkeley) 6/28/90 + * $Id$ */ #define UD_TPDU_type 0x40 /* packet type */ diff --git a/sys/netiso/cons.h b/sys/netiso/cons.h index 9eaa9df821..816fcb0450 100644 --- a/sys/netiso/cons.h +++ b/sys/netiso/cons.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)cons.h 7.3 (Berkeley) 5/6/91 + * from: @(#)cons.h 7.3 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,10 +60,8 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* - * $Header: cons.h,v 4.4 88/09/09 19:01:28 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/cons.h,v $ - * * interface between TP and CONS */ diff --git a/sys/netiso/cons_pcb.h b/sys/netiso/cons_pcb.h index 27f6166858..579c040235 100644 --- a/sys/netiso/cons_pcb.h +++ b/sys/netiso/cons_pcb.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)cons_pcb.h 7.4 (Berkeley) 5/6/91 + * from: @(#)cons_pcb.h 7.4 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,8 +60,6 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: cons_pcb.h,v 4.2 88/06/29 14:59:08 hagens Exp $ */ -/* $Source: /usr/argo/sys/netiso/RCS/cons_pcb.h,v $ */ /* * protocol control block for the connection oriented network service diff --git a/sys/netiso/eonvar.h b/sys/netiso/eonvar.h index 7614a43d54..8d4aeb21cc 100644 --- a/sys/netiso/eonvar.h +++ b/sys/netiso/eonvar.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)eonvar.h 7.5 (Berkeley) 5/6/91 + * from: @(#)eonvar.h 7.5 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** diff --git a/sys/netiso/esis.c b/sys/netiso/esis.c index efa03607d8..f80de34e19 100644 --- a/sys/netiso/esis.c +++ b/sys/netiso/esis.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)esis.c 7.19 (Berkeley) 6/27/91 + * from: @(#)esis.c 7.19 (Berkeley) 6/27/91 + * $Id$ */ /*********************************************************** diff --git a/sys/netiso/esis.h b/sys/netiso/esis.h index 8b8287e98f..fd4bcd9e91 100644 --- a/sys/netiso/esis.h +++ b/sys/netiso/esis.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)esis.h 7.4 (Berkeley) 5/6/91 + * from: @(#)esis.h 7.4 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,10 +60,6 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* - * $Header: esis.h,v 4.7 88/09/15 11:24:18 hagens Exp $ - * $Source: /usr/argo/sys/netiso/RCS/esis.h,v $ - */ #ifndef BYTE_ORDER /* diff --git a/sys/netiso/if_cons.c b/sys/netiso/if_cons.c index d9101d24d9..49fffa2687 100644 --- a/sys/netiso/if_cons.c +++ b/sys/netiso/if_cons.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)if_cons.c 7.10 (Berkeley) 5/29/91 + * from: @(#)if_cons.c 7.10 (Berkeley) 5/29/91 + * $Id$ */ /*********************************************************** @@ -59,10 +60,8 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* - * $Header: if_cons.c,v 4.7 88/08/11 15:52:55 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/if_cons.c,v $ - * * cons.c - Connection Oriented Network Service: * including support for a) user transport-level service, * b) COSNS below CLNP, and c) CONS below TP. diff --git a/sys/netiso/if_eon.c b/sys/netiso/if_eon.c index 3210eed134..01bcd56e0f 100644 --- a/sys/netiso/if_eon.c +++ b/sys/netiso/if_eon.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)if_eon.c 7.16 (Berkeley) 6/27/91 + * from: @(#)if_eon.c 7.16 (Berkeley) 6/27/91 + * $Id$ */ /*********************************************************** @@ -59,10 +60,8 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* - * $Header: if_eon.c,v 1.4 88/07/19 15:53:59 hagens Exp $ - * $Source: /usr/argo/sys/netiso/RCS/if_eon.c,v $ - * * EON rfc * Layer between IP and CLNL * diff --git a/sys/netiso/iso.c b/sys/netiso/iso.c index 8044d89a9b..49290f774e 100644 --- a/sys/netiso/iso.c +++ b/sys/netiso/iso.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)iso.c 7.14 (Berkeley) 6/27/91 + * from: @(#)iso.c 7.14 (Berkeley) 6/27/91 + * $Id$ */ /*********************************************************** @@ -59,15 +60,14 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* - * $Header: iso.c,v 4.11 88/09/19 14:58:35 root Exp $ - * $Source: /usr/argo/sys/netiso/RCS/iso.c,v $ - * * iso.c: miscellaneous routines to support the iso address family */ #include "types.h" #include "param.h" +#include "systm.h" #include "ioctl.h" #include "mbuf.h" #include "domain.h" diff --git a/sys/netiso/iso.h b/sys/netiso/iso.h index 6485814537..5f092bb2c9 100644 --- a/sys/netiso/iso.h +++ b/sys/netiso/iso.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)iso.h 7.6 (Berkeley) 5/6/91 + * from: @(#)iso.h 7.6 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,8 +60,6 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: iso.h,v 4.9 88/09/11 18:06:38 hagens Exp $ */ -/* $Source: /usr/argo/sys/netiso/RCS/iso.h,v $ */ #ifndef __ISO__ #define __ISO__ diff --git a/sys/netiso/iso_chksum.c b/sys/netiso/iso_chksum.c index 1588372cad..56c8d8843f 100644 --- a/sys/netiso/iso_chksum.c +++ b/sys/netiso/iso_chksum.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)iso_chksum.c 7.5 (Berkeley) 5/6/91 + * from: @(#)iso_chksum.c 7.5 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,10 +60,8 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* - * $Header: iso_chksum.c,v 4.7 88/07/29 15:31:26 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/iso_chksum.c,v $ - * * ISO CHECKSUM * * The checksum generation and check routines are here. @@ -85,6 +84,7 @@ SOFTWARE. #ifdef ISO #include "argo_debug.h" #include "param.h" +#include "systm.h" #include "mbuf.h" #endif ISO diff --git a/sys/netiso/iso_errno.h b/sys/netiso/iso_errno.h index b33843a725..e9066684f6 100644 --- a/sys/netiso/iso_errno.h +++ b/sys/netiso/iso_errno.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)iso_errno.h 7.5 (Berkeley) 5/6/91 + * from: @(#)iso_errno.h 7.5 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** diff --git a/sys/netiso/iso_map.h b/sys/netiso/iso_map.h index 7e494fda3c..70d36c4147 100644 --- a/sys/netiso/iso_map.h +++ b/sys/netiso/iso_map.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)iso_map.h 7.3 (Berkeley) 5/6/91 + * from: @(#)iso_map.h 7.3 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** diff --git a/sys/netiso/iso_pcb.c b/sys/netiso/iso_pcb.c index f7e3eb3aba..eca53856be 100644 --- a/sys/netiso/iso_pcb.c +++ b/sys/netiso/iso_pcb.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)iso_pcb.c 7.10 (Berkeley) 6/27/91 + * from: @(#)iso_pcb.c 7.10 (Berkeley) 6/27/91 + * $Id$ */ /*********************************************************** @@ -59,10 +60,8 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* - * $Header: iso_pcb.c,v 4.5 88/06/29 14:59:56 hagens Exp $ - * $Source: /usr/argo/sys/netiso/RCS/iso_pcb.c,v $ - * * Iso address family net-layer(s) pcb stuff. NEH 1/29/87 */ diff --git a/sys/netiso/iso_pcb.h b/sys/netiso/iso_pcb.h index 1b9b0845fa..17276b80e3 100644 --- a/sys/netiso/iso_pcb.h +++ b/sys/netiso/iso_pcb.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)iso_pcb.h 7.5 (Berkeley) 5/6/91 + * from: @(#)iso_pcb.h 7.5 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,8 +60,6 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: iso_pcb.h,v 4.3 88/06/29 15:00:01 hagens Exp $ */ -/* $Source: /usr/argo/sys/netiso/RCS/iso_pcb.h,v $ */ #define MAXX25CRUDLEN 16 /* 16 bytes of call request user data */ diff --git a/sys/netiso/iso_proto.c b/sys/netiso/iso_proto.c index f4a5c2230b..85c20d48be 100644 --- a/sys/netiso/iso_proto.c +++ b/sys/netiso/iso_proto.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)iso_proto.c 7.8 (Berkeley) 5/6/91 + * from: @(#)iso_proto.c 7.8 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,9 +60,8 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: iso_proto.c,v 4.4 88/09/08 08:38:42 hagens Exp $ - * $Source: /usr/argo/sys/netiso/RCS/iso_proto.c,v $ - * + +/* * iso_proto.c : protocol switch tables in the ISO domain * * ISO protocol family includes TP, CLTP, CLNP, 8208 diff --git a/sys/netiso/iso_snpac.c b/sys/netiso/iso_snpac.c index df5d75c008..632a47543a 100644 --- a/sys/netiso/iso_snpac.c +++ b/sys/netiso/iso_snpac.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)iso_snpac.c 7.14 (Berkeley) 6/27/91 + * from: @(#)iso_snpac.c 7.14 (Berkeley) 6/27/91 + * $Id$ */ /*********************************************************** @@ -59,8 +60,6 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: iso_snpac.c,v 1.8 88/09/19 13:51:36 hagens Exp $ */ -/* $Source: /usr/argo/sys/netiso/RCS/iso_snpac.c,v $ */ #ifdef ISO diff --git a/sys/netiso/iso_snpac.h b/sys/netiso/iso_snpac.h index dc64d503f2..703114af58 100644 --- a/sys/netiso/iso_snpac.h +++ b/sys/netiso/iso_snpac.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)iso_snpac.h 7.8 (Berkeley) 5/6/91 + * from: @(#)iso_snpac.h 7.8 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** diff --git a/sys/netiso/iso_var.h b/sys/netiso/iso_var.h index 79e8ac386e..5c7c398ff3 100644 --- a/sys/netiso/iso_var.h +++ b/sys/netiso/iso_var.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)iso_var.h 7.5 (Berkeley) 5/6/91 + * from: @(#)iso_var.h 7.5 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,9 +60,6 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: iso_var.h,v 4.2 88/06/29 15:00:08 hagens Exp $ - * $Source: /usr/argo/sys/netiso/RCS/iso_var.h,v $ - */ /* * Interface address, iso version. One of these structures is diff --git a/sys/netiso/tp.trans b/sys/netiso/tp.trans index 9a9a1b7467..6b12f578e9 100644 --- a/sys/netiso/tp.trans +++ b/sys/netiso/tp.trans @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp.trans 7.9 (Berkeley) 5/7/91 + * from: @(#)tp.trans 7.9 (Berkeley) 5/7/91 + * $Id$ */ /*********************************************************** @@ -59,8 +60,8 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ -/* $Header: tp.trans,v 5.1 88/10/12 12:22:07 root Exp $ - * + +/* * Transition file for TP. * * DO NOT: diff --git a/sys/netiso/tp_astring.c b/sys/netiso/tp_astring.c index 01b9456255..1c62b375a2 100644 --- a/sys/netiso/tp_astring.c +++ b/sys/netiso/tp_astring.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_astring.c 7.4 (Berkeley) 5/6/91 + * from: @(#)tp_astring.c 7.4 (Berkeley) 5/6/91 + * $Id$ */ #ifndef _NFILE diff --git a/sys/netiso/tp_clnp.h b/sys/netiso/tp_clnp.h index 711174c775..503178f278 100644 --- a/sys/netiso/tp_clnp.h +++ b/sys/netiso/tp_clnp.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_clnp.h 7.3 (Berkeley) 5/6/91 + * from: @(#)tp_clnp.h 7.3 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,12 +60,10 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP * - * $Header: tp_clnp.h,v 5.1 88/10/12 12:16:36 root Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_clnp.h,v $ - * * AF_ISO net-dependent structures and include files * */ diff --git a/sys/netiso/tp_cons.c b/sys/netiso/tp_cons.c index bfd5e127ce..ac89698863 100644 --- a/sys/netiso/tp_cons.c +++ b/sys/netiso/tp_cons.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_cons.c 7.8 (Berkeley) 5/9/91 + * from: @(#)tp_cons.c 7.8 (Berkeley) 5/9/91 + * $Id$ */ /*********************************************************** @@ -59,10 +60,9 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP - * $Header: tp_cons.c,v 5.6 88/11/18 17:27:13 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_cons.c,v $ * * Here is where you find the iso- and cons-dependent code. We've tried * keep all net-level and (primarily) address-family-dependent stuff diff --git a/sys/netiso/tp_driver.c b/sys/netiso/tp_driver.c index 1a7d1f89d3..10305a3770 100644 --- a/sys/netiso/tp_driver.c +++ b/sys/netiso/tp_driver.c @@ -1,8 +1,8 @@ -/* $Header$ */ -/* $Source$ */ -#ifndef lint -static char *rcsid = "$Header/**/$"; -#endif lint +/* + * from: unknown + * $Id$ + */ + #define _XEBEC_PG static #include "tp_states.h" @@ -16,6 +16,7 @@ static struct act_ent { /* %W% (Berkeley) %G% */ #include "param.h" +#include "systm.h" #include "socket.h" #include "socketvar.h" #include "protosw.h" diff --git a/sys/netiso/tp_emit.c b/sys/netiso/tp_emit.c index 3bf198280f..d3281d5f02 100644 --- a/sys/netiso/tp_emit.c +++ b/sys/netiso/tp_emit.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_emit.c 7.9 (Berkeley) 5/9/91 + * from: @(#)tp_emit.c 7.9 (Berkeley) 5/9/91 + * $Id$ */ /*********************************************************** @@ -59,12 +60,10 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP * - * $Header: tp_emit.c,v 5.5 88/11/18 17:27:20 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_emit.c,v $ - * * This file contains tp_emit() and tp_error_emit(), which * form TPDUs and hand them to ip. * They take data in the form of mbuf chain, allocate mbufs as @@ -80,6 +79,7 @@ SOFTWARE. */ #include "param.h" +#include "systm.h" #include "mbuf.h" #include "socket.h" #include "socketvar.h" diff --git a/sys/netiso/tp_events.h b/sys/netiso/tp_events.h index 48222830a0..7d8d9b084a 100644 --- a/sys/netiso/tp_events.h +++ b/sys/netiso/tp_events.h @@ -1,5 +1,8 @@ -/* $Header$ */ -/* $Source$ */ +/* + * from: unknown + * $Id$ + */ + struct tp_event { int ev_number; struct timeval e_time; diff --git a/sys/netiso/tp_inet.c b/sys/netiso/tp_inet.c index 7da8a52598..6c2ee7e97d 100644 --- a/sys/netiso/tp_inet.c +++ b/sys/netiso/tp_inet.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_inet.c 7.8 (Berkeley) 5/6/91 + * from: @(#)tp_inet.c 7.8 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,10 +60,9 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP - * $Header: tp_inet.c,v 5.3 88/11/18 17:27:29 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_inet.c,v $ * * Here is where you find the inet-dependent code. We've tried * keep all net-level and (primarily) address-family-dependent stuff diff --git a/sys/netiso/tp_input.c b/sys/netiso/tp_input.c index 2321514c11..8f262698e1 100644 --- a/sys/netiso/tp_input.c +++ b/sys/netiso/tp_input.c @@ -31,6 +31,7 @@ * SUCH DAMAGE. * * @(#)tp_input.c 7.19 (Berkeley) 6/27/91 + * $Id$ */ /*********************************************************** @@ -59,12 +60,9 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP - * - * $Header: tp_input.c,v 5.6 88/11/18 17:27:38 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_input.c,v $ - * * tp_input() gets an mbuf chain from ip. Actually, not directly * from ip, because ip calls a net-level routine that strips off * the net header and then calls tp_input(), passing the proper type diff --git a/sys/netiso/tp_ip.h b/sys/netiso/tp_ip.h index a00301f9b6..33b3877134 100644 --- a/sys/netiso/tp_ip.h +++ b/sys/netiso/tp_ip.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_ip.h 7.3 (Berkeley) 5/6/91 + * from: @(#)tp_ip.h 7.3 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,12 +60,10 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP * - * $Header: tp_ip.h,v 5.1 88/10/12 12:19:47 root Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_ip.h,v $ - * * internet IP-dependent structures and include files * */ diff --git a/sys/netiso/tp_iso.c b/sys/netiso/tp_iso.c index e524bacb80..0fd25e9cf2 100644 --- a/sys/netiso/tp_iso.c +++ b/sys/netiso/tp_iso.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_iso.c 7.11 (Berkeley) 5/6/91 + * from: @(#)tp_iso.c 7.11 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,10 +60,9 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP - * $Header: /var/src/sys/netiso/RCS/tp_iso.c,v 5.1 89/02/09 16:20:51 hagens Exp $ - * $Source: /var/src/sys/netiso/RCS/tp_iso.c,v $ * * Here is where you find the iso-dependent code. We've tried * keep all net-level and (primarily) address-family-dependent stuff @@ -87,6 +87,7 @@ SOFTWARE. #ifdef ISO #include "param.h" +#include "systm.h" #include "socket.h" #include "socketvar.h" #include "domain.h" diff --git a/sys/netiso/tp_meas.c b/sys/netiso/tp_meas.c index 10a1cb0b3a..60304a3ced 100644 --- a/sys/netiso/tp_meas.c +++ b/sys/netiso/tp_meas.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_meas.c 7.4 (Berkeley) 5/6/91 + * from: @(#)tp_meas.c 7.4 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,10 +60,8 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* - * $Header: tp_meas.c,v 5.2 88/11/18 17:28:04 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_meas.c,v $ - * * tp_meas.c : create a performance measurement event * in the circular buffer tp_Meas[] */ diff --git a/sys/netiso/tp_meas.h b/sys/netiso/tp_meas.h index 46e63f017b..1f6f5885ec 100644 --- a/sys/netiso/tp_meas.h +++ b/sys/netiso/tp_meas.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_meas.h 7.5 (Berkeley) 5/6/91 + * from: @(#)tp_meas.h 7.5 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** diff --git a/sys/netiso/tp_output.c b/sys/netiso/tp_output.c index d2611c373d..e625e16a27 100644 --- a/sys/netiso/tp_output.c +++ b/sys/netiso/tp_output.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_output.c 7.10 (Berkeley) 6/27/91 + * From: @(#)tp_output.c 7.10 (Berkeley) 6/27/91 + * $Id: tp_output.c,v 1.2 1993/09/09 23:35:48 rgrimes Exp $ */ /*********************************************************** @@ -59,12 +60,10 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP * - * $Header: tp_output.c,v 5.4 88/11/18 17:28:08 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_output.c,v $ - * * In here is tp_ctloutput(), the guy called by [sg]etsockopt(), */ @@ -163,7 +162,13 @@ tp_consistency( tpcb, cmd, param ) printf("p_class 0x%x, class_to_use 0x%x\n", param->p_class, class_to_use); ENDDEBUG +#ifdef XXXRWGXXX + /* this next line is questionable p_netservice is u_char, how can it + * ever be less than 0 ?? + */ if((param->p_netservice < 0) || (param->p_netservice > TP_MAX_NETSERVICES)){ +#endif + if(/*(param->p_netservice < 0) ||*/ (param->p_netservice > TP_MAX_NETSERVICES)){ error = EINVAL; goto done; } if( (param->p_class & TP_CLASSES_IMPLEMENTED) == 0 ) { diff --git a/sys/netiso/tp_param.h b/sys/netiso/tp_param.h index 888c89fcba..7b96f55f03 100644 --- a/sys/netiso/tp_param.h +++ b/sys/netiso/tp_param.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_param.h 7.8 (Berkeley) 6/27/91 + * from: @(#)tp_param.h 7.8 (Berkeley) 6/27/91 + * $Id$ */ /*********************************************************** @@ -59,12 +60,9 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP - * - * $Header: tp_param.h,v 5.3 88/11/18 17:28:18 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_param.h,v $ - * */ #ifndef __TP_PARAM__ diff --git a/sys/netiso/tp_pcb.c b/sys/netiso/tp_pcb.c index 6c9d309ca1..58d39ea0c1 100644 --- a/sys/netiso/tp_pcb.c +++ b/sys/netiso/tp_pcb.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_pcb.c 7.11 (Berkeley) 5/6/91 + * from: @(#)tp_pcb.c 7.11 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,13 +60,10 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP * - * $Header: tp_pcb.c,v 5.4 88/11/18 17:28:24 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_pcb.c,v $ - * - * * This is the initialization and cleanup stuff - * for the tp machine in general as well as for the individual pcbs. * tp_init() is called at system startup. tp_attach() and tp_getref() are diff --git a/sys/netiso/tp_pcb.h b/sys/netiso/tp_pcb.h index 690a4a54dd..e22ccf7c8d 100644 --- a/sys/netiso/tp_pcb.h +++ b/sys/netiso/tp_pcb.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_pcb.h 7.9 (Berkeley) 5/6/91 + * from: @(#)tp_pcb.h 7.9 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,13 +60,10 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP * - * $Header: tp_pcb.h,v 5.2 88/11/18 17:09:32 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_pcb.h,v $ - * - * * This file defines the transport protocol control block (tpcb). * and a bunch of #define values that are used in the tpcb. */ diff --git a/sys/netiso/tp_seq.h b/sys/netiso/tp_seq.h index fef41cf8df..914ac777ef 100644 --- a/sys/netiso/tp_seq.h +++ b/sys/netiso/tp_seq.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_seq.h 7.5 (Berkeley) 5/6/91 + * from: @(#)tp_seq.h 7.5 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,12 +60,10 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP * - * $Header: tp_seq.h,v 5.1 88/10/12 12:20:59 root Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_seq.h,v $ - * * These macros perform sequence number arithmetic modulo (2**7 or 2**31). * The relevant fields in the tpcb are: * tp_seqmask : the mask of bits that define the sequence space. diff --git a/sys/netiso/tp_stat.h b/sys/netiso/tp_stat.h index 5ac931f8a5..8e524b6033 100644 --- a/sys/netiso/tp_stat.h +++ b/sys/netiso/tp_stat.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_stat.h 7.5 (Berkeley) 6/27/91 + * from: @(#)tp_stat.h 7.5 (Berkeley) 6/27/91 + * $Id$ */ /*********************************************************** @@ -59,12 +60,10 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP * - * $Header: tp_stat.h,v 5.4 88/11/18 17:28:38 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_stat.h,v $ - * * Here are the data structures in which the global * statistics(counters) are gathered. */ diff --git a/sys/netiso/tp_states.h b/sys/netiso/tp_states.h index ac6213a64d..388b9ad79b 100644 --- a/sys/netiso/tp_states.h +++ b/sys/netiso/tp_states.h @@ -1,5 +1,8 @@ -/* $Header$ */ -/* $Source$ */ +/* + * from: unknown + * $Id$ + */ + #define ST_ERROR 0x0 #define TP_CLOSED 0x1 #define TP_CRSENT 0x2 diff --git a/sys/netiso/tp_states.init b/sys/netiso/tp_states.init index 89e5345386..4bdd190190 100644 --- a/sys/netiso/tp_states.init +++ b/sys/netiso/tp_states.init @@ -1,5 +1,8 @@ -/* $Header$ */ -/* $Source$ */ +/* + * from: unknown + * $Id$ + */ + {0x3,0x0}, {0x6,0x1}, {0x6,0x2}, diff --git a/sys/netiso/tp_subr.c b/sys/netiso/tp_subr.c index 0732344267..38ad30a907 100644 --- a/sys/netiso/tp_subr.c +++ b/sys/netiso/tp_subr.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_subr.c 7.9 (Berkeley) 6/27/91 + * from: @(#)tp_subr.c 7.9 (Berkeley) 6/27/91 + * $Id$ */ /*********************************************************** @@ -59,12 +60,10 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP * - * $Header: tp_subr.c,v 5.3 88/11/18 17:28:43 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_subr.c,v $ - * * The main work of data transfer is done here. * These routines are called from tp.trans. * They include the routines that check the validity of acks and Xacks, @@ -75,6 +74,7 @@ SOFTWARE. */ #include "param.h" +#include "systm.h" #include "mbuf.h" #include "socket.h" #include "socketvar.h" diff --git a/sys/netiso/tp_subr2.c b/sys/netiso/tp_subr2.c index df2d20100e..95c5525a45 100644 --- a/sys/netiso/tp_subr2.c +++ b/sys/netiso/tp_subr2.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_subr2.c 7.10 (Berkeley) 6/27/91 + * from: @(#)tp_subr2.c 7.10 (Berkeley) 6/27/91 + * $Id$ */ /*********************************************************** @@ -59,12 +60,10 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP * - * $Header: tp_subr2.c,v 5.5 88/11/18 17:28:55 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_subr2.c,v $ - * * Some auxiliary routines: * tp_protocol_error: required by xebec- called when a combo of state, * event, predicate isn't covered for by the transition file. @@ -636,6 +635,10 @@ done: return error; } +#ifndef TPCONS +static +pk_flowcontrol() {} +#endif /* class zero version */ void @@ -698,10 +701,6 @@ register struct tp_pcb *tpcb; pk_flowcontrol(lcp, 0, 0); } } -#ifndef TPCONS -static -pk_flowcontrol() {} -#endif #ifdef TP_PERF_MEAS /* diff --git a/sys/netiso/tp_timer.c b/sys/netiso/tp_timer.c index 9aab6471f0..e717332333 100644 --- a/sys/netiso/tp_timer.c +++ b/sys/netiso/tp_timer.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_timer.c 7.5 (Berkeley) 5/6/91 + * from: @(#)tp_timer.c 7.5 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,12 +60,10 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP * - * $Header: tp_timer.c,v 5.2 88/11/18 17:29:07 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_timer.c,v $ - * * Contains all the timer code. * There are two sources of calls to these routines: * the clock, and tp.trans. (ok, and tp_pcb.c calls it at init time) @@ -83,6 +82,7 @@ SOFTWARE. */ #include "param.h" +#include "systm.h" #include "types.h" #include "time.h" #include "malloc.h" diff --git a/sys/netiso/tp_timer.h b/sys/netiso/tp_timer.h index 2b8372e579..bb34cd828f 100644 --- a/sys/netiso/tp_timer.h +++ b/sys/netiso/tp_timer.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_timer.h 7.4 (Berkeley) 5/6/91 + * from: @(#)tp_timer.h 7.4 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,12 +60,10 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP * - * $Header: tp_timer.h,v 5.1 88/10/12 12:21:41 root Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_timer.h,v $ - * * ARGO TP * The callout structures used by the tp timers. */ diff --git a/sys/netiso/tp_tpdu.h b/sys/netiso/tp_tpdu.h index e9b9cb1ed1..623f492e1b 100644 --- a/sys/netiso/tp_tpdu.h +++ b/sys/netiso/tp_tpdu.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_tpdu.h 7.4 (Berkeley) 5/6/91 + * from: @(#)tp_tpdu.h 7.4 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,12 +60,10 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP * - * $Header: tp_tpdu.h,v 4.4 88/07/26 16:45:40 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_tpdu.h,v $ - * * This ghastly set of macros makes it possible to * refer to tpdu structures without going mad. */ diff --git a/sys/netiso/tp_trace.c b/sys/netiso/tp_trace.c index 64ebeeea57..6c0a3b7c56 100644 --- a/sys/netiso/tp_trace.c +++ b/sys/netiso/tp_trace.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_trace.c 7.4 (Berkeley) 5/6/91 + * from: @(#)tp_trace.c 7.4 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,12 +60,10 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP * - * $Header: tp_trace.c,v 5.3 88/11/18 17:29:14 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_trace.c,v $ - * * The whole protocol trace module. * We keep a circular buffer of trace structures, which are big * unions of different structures we might want to see. diff --git a/sys/netiso/tp_trace.h b/sys/netiso/tp_trace.h index 9d0c6b3edc..1f90a091b2 100644 --- a/sys/netiso/tp_trace.h +++ b/sys/netiso/tp_trace.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_trace.h 7.5 (Berkeley) 6/27/91 + * from: @(#)tp_trace.h 7.5 (Berkeley) 6/27/91 + * $Id$ */ /*********************************************************** @@ -59,12 +60,9 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP - * - * $Header: tp_trace.h,v 5.1 88/10/12 12:21:51 root Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_trace.h,v $ - * * * Definitions needed for the protocol trace mechanism. */ diff --git a/sys/netiso/tp_user.h b/sys/netiso/tp_user.h index 257f8a7aa9..0653773f9c 100644 --- a/sys/netiso/tp_user.h +++ b/sys/netiso/tp_user.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_user.h 7.10 (Berkeley) 5/6/91 + * from: @(#)tp_user.h 7.10 (Berkeley) 5/6/91 + * $Id$ */ /*********************************************************** @@ -59,12 +60,10 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP * - * $Header: tp_user.h,v 5.2 88/11/04 15:44:44 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_user.h,v $ - * * These are the values a real-live user ;-) needs. */ diff --git a/sys/netiso/tp_usrreq.c b/sys/netiso/tp_usrreq.c index e085ad2b79..b3ef56e333 100644 --- a/sys/netiso/tp_usrreq.c +++ b/sys/netiso/tp_usrreq.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tp_usrreq.c 7.17 (Berkeley) 6/27/91 + * from: @(#)tp_usrreq.c 7.17 (Berkeley) 6/27/91 + * $Id$ */ /*********************************************************** @@ -59,12 +60,10 @@ SOFTWARE. /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ + /* * ARGO TP * - * $Header: tp_usrreq.c,v 5.4 88/11/18 17:29:18 nhall Exp $ - * $Source: /usr/argo/sys/netiso/RCS/tp_usrreq.c,v $ - * * tp_usrreq(), the fellow that gets called from most of the socket code. * Pretty straighforward. * THe only really awful stuff here is the OOB processing, which is done diff --git a/sys/netiso/xebec/Makefile b/sys/netiso/xebec/Makefile index fa05f9cc47..2cc7aa27dd 100644 --- a/sys/netiso/xebec/Makefile +++ b/sys/netiso/xebec/Makefile @@ -1,4 +1,5 @@ -# @(#)Makefile 5.16 (Berkeley) 4/26/91 +# from: @(#)Makefile 5.16 (Berkeley) 4/26/91 +# $Id$ PROG= xebec SRCS= llparse.c llscan.c main.c malloc.c procs.c putdriver.c sets.c xebec.c diff --git a/sys/netiso/xebec/debug.h b/sys/netiso/xebec/debug.h index 2e3f16794d..882ea0992c 100644 --- a/sys/netiso/xebec/debug.h +++ b/sys/netiso/xebec/debug.h @@ -1,5 +1,7 @@ -/* $Header: debug.h,v 2.1 88/09/19 12:56:16 nhall Exp $ */ -/* $Source: /var/home/tadl/src/argo/xebec/RCS/debug.h,v $ */ +/* + * from: debug.h,v 2.1 88/09/19 12:56:16 nhall Exp + * $Id$ + */ #define OUT stdout diff --git a/sys/netiso/xebec/llparse.c b/sys/netiso/xebec/llparse.c index fee7a9f7e4..c8b0089dcf 100644 --- a/sys/netiso/xebec/llparse.c +++ b/sys/netiso/xebec/llparse.c @@ -1,5 +1,8 @@ -/* $Header: llparse.c,v 2.2 88/09/19 12:54:59 nhall Exp $ */ -/* $Source: /var/home/tadl/src/argo/xebec/RCS/llparse.c,v $ */ +/* + * from: llparse.c,v 2.2 88/09/19 12:54:59 nhall Exp + * $Id$ + */ + /* * ************************* NOTICE ******************************* * This code is in the public domain. It cannot be copyrighted. diff --git a/sys/netiso/xebec/llparse.h b/sys/netiso/xebec/llparse.h index 1b6133b1b7..16ac26cd07 100644 --- a/sys/netiso/xebec/llparse.h +++ b/sys/netiso/xebec/llparse.h @@ -1,5 +1,7 @@ -/* $Header: llparse.h,v 2.1 88/09/19 12:56:20 nhall Exp $ */ -/* $Source: /var/home/tadl/src/argo/xebec/RCS/llparse.h,v $ */ +/* + * from: llparse.h,v 2.1 88/09/19 12:56:20 nhall Exp + * $Id$ + */ /************************************************************ attributes stack garbage diff --git a/sys/netiso/xebec/llscan.c b/sys/netiso/xebec/llscan.c index fa2abee377..d33db77729 100644 --- a/sys/netiso/xebec/llscan.c +++ b/sys/netiso/xebec/llscan.c @@ -1,5 +1,8 @@ -/* $Header: llscan.c,v 2.2 88/09/19 12:55:06 nhall Exp $ */ -/* $Source: /var/home/tadl/src/argo/xebec/RCS/llscan.c,v $ */ +/* + * from: llscan.c,v 2.2 88/09/19 12:55:06 nhall Exp + * $Id$ + */ + /* * ************************* NOTICE ******************************* * This code is in the public domain. It cannot be copyrighted. diff --git a/sys/netiso/xebec/main.c b/sys/netiso/xebec/main.c index a0b4842f30..0f6305533e 100644 --- a/sys/netiso/xebec/main.c +++ b/sys/netiso/xebec/main.c @@ -1,5 +1,8 @@ -/* $Header: main.c,v 2.4 88/09/19 12:55:13 nhall Exp $ */ -/* $Source: /var/home/tadl/src/argo/xebec/RCS/main.c,v $ */ +/* + * from: main.c,v 2.4 88/09/19 12:55:13 nhall Exp + * $Id$ + */ + /* * TODO: * rewrite the command line stuff altogether - it's kludged beyond diff --git a/sys/netiso/xebec/main.h b/sys/netiso/xebec/main.h index cb5bd74f31..34d7131c92 100644 --- a/sys/netiso/xebec/main.h +++ b/sys/netiso/xebec/main.h @@ -1,5 +1,7 @@ -/* $Header: main.h,v 2.1 88/09/19 12:56:24 nhall Exp $ */ -/* $Source: /var/home/tadl/src/argo/xebec/RCS/main.h,v $ */ +/* + * from: main.h,v 2.1 88/09/19 12:56:24 nhall Exp + * $Id$ + */ #define TRUE 1 #define FALSE 0 diff --git a/sys/netiso/xebec/malloc.c b/sys/netiso/xebec/malloc.c index 5cdfc147a7..cb6d91c5c2 100644 --- a/sys/netiso/xebec/malloc.c +++ b/sys/netiso/xebec/malloc.c @@ -1,5 +1,8 @@ -/* $Header: malloc.c,v 2.2 88/09/19 12:55:18 nhall Exp $ */ -/* $Source: /var/home/tadl/src/argo/xebec/RCS/malloc.c,v $ */ +/* + * from: malloc.c,v 2.2 88/09/19 12:55:18 nhall Exp + * $Id$ + */ + /* * This code is such a kludge that I don't want to put my name on it. * It was a ridiculously fast hack and needs rewriting. diff --git a/sys/netiso/xebec/malloc.h b/sys/netiso/xebec/malloc.h index 53d865bf47..b4b2a4ee72 100644 --- a/sys/netiso/xebec/malloc.h +++ b/sys/netiso/xebec/malloc.h @@ -1,4 +1,6 @@ -/* $Header: malloc.h,v 2.1 88/09/19 12:56:27 nhall Exp $ */ -/* $Source: /var/home/tadl/src/argo/xebec/RCS/malloc.h,v $ */ +/* + * from: malloc.h,v 2.1 88/09/19 12:56:27 nhall Exp + * $Id$ + */ char *Malloc(); diff --git a/sys/netiso/xebec/procs.c b/sys/netiso/xebec/procs.c index 49d862ac5b..0a97e89311 100644 --- a/sys/netiso/xebec/procs.c +++ b/sys/netiso/xebec/procs.c @@ -1,5 +1,8 @@ -/* $Header: procs.c,v 2.3 88/09/19 12:55:22 nhall Exp $ */ -/* $Source: /var/home/tadl/src/argo/xebec/RCS/procs.c,v $ */ +/* + * from: procs.c,v 2.3 88/09/19 12:55:22 nhall Exp + * $Id$ + */ + /* * This code is such a kludge that I don't want to put my name on it. * It was a ridiculously fast hack and needs rewriting. diff --git a/sys/netiso/xebec/procs.h b/sys/netiso/xebec/procs.h index e41ae75995..16d5b29b75 100644 --- a/sys/netiso/xebec/procs.h +++ b/sys/netiso/xebec/procs.h @@ -1,5 +1,7 @@ -/* $Header: procs.h,v 2.1 88/09/19 12:56:30 nhall Exp $ */ -/* $Source: /var/home/tadl/src/argo/xebec/RCS/procs.h,v $ */ +/* + * from: procs.h,v 2.1 88/09/19 12:56:30 nhall Exp + * $Id$ + */ extern char *stash(); extern struct Object *SameState; diff --git a/sys/netiso/xebec/putdriver.c b/sys/netiso/xebec/putdriver.c index 996ac643d1..d4b1e5a09f 100644 --- a/sys/netiso/xebec/putdriver.c +++ b/sys/netiso/xebec/putdriver.c @@ -1,5 +1,7 @@ -/* $Header: putdriver.c,v 2.2 88/09/19 12:55:27 nhall Exp $ */ -/* $Source: /var/home/tadl/src/argo/xebec/RCS/putdriver.c,v $ */ +/* + * from: putdriver.c,v 2.2 88/09/19 12:55:27 nhall Exp + * $Id$ + */ /* * This code is such a kludge that I don't want to put my name on it. diff --git a/sys/netiso/xebec/sets.c b/sys/netiso/xebec/sets.c index 3bb74ed8d2..fae3a87adf 100644 --- a/sys/netiso/xebec/sets.c +++ b/sys/netiso/xebec/sets.c @@ -1,5 +1,8 @@ -/* $Header: sets.c,v 2.3 88/09/19 12:55:30 nhall Exp $ */ -/* $Source: /var/home/tadl/src/argo/xebec/RCS/sets.c,v $ */ +/* + * from: sets.c,v 2.3 88/09/19 12:55:30 nhall Exp + * $Id$ + */ + /* * This code is such a kludge that I don't want to put my name on it. * It was a ridiculously fast hack and needs rewriting. diff --git a/sys/netiso/xebec/sets.h b/sys/netiso/xebec/sets.h index 96eb791edc..3f982516b0 100644 --- a/sys/netiso/xebec/sets.h +++ b/sys/netiso/xebec/sets.h @@ -1,5 +1,7 @@ -/* $Header: sets.h,v 2.1 88/09/19 12:56:33 nhall Exp $ */ -/* $Source: /var/home/tadl/src/argo/xebec/RCS/sets.h,v $ */ +/* + * from: sets.h,v 2.1 88/09/19 12:56:33 nhall Exp + * $Id$ + */ #define MAXEVENTS 200 #define MAXSTATES 200 diff --git a/sys/netiso/xebec/test.trans b/sys/netiso/xebec/test.trans index 49db361099..2e16ab922d 100644 --- a/sys/netiso/xebec/test.trans +++ b/sys/netiso/xebec/test.trans @@ -1,5 +1,8 @@ -/* $Header: test.trans,v 0.2 88/09/19 12:58:29 nhall Exp $ +/* + * from: test.trans,v 0.2 88/09/19 12:58:29 nhall Exp + * $Id$ */ + *PROTOCOL test *INCLUDE diff --git a/sys/netiso/xebec/test_def.h b/sys/netiso/xebec/test_def.h index 6faa2dfce8..5152d7ab7b 100644 --- a/sys/netiso/xebec/test_def.h +++ b/sys/netiso/xebec/test_def.h @@ -1,3 +1,7 @@ +/* + * from: unknown + * $Id$ + */ struct blah { unsigned int blahfield; diff --git a/sys/netiso/xebec/xebec.bnf b/sys/netiso/xebec/xebec.bnf index d7406d9d5c..1cb4459c35 100644 --- a/sys/netiso/xebec/xebec.bnf +++ b/sys/netiso/xebec/xebec.bnf @@ -1,3 +1,8 @@ +/* + * from: unknown + * $Id$ + */ + { #include "main.h" #include "sets.h" diff --git a/sys/netiso/xebec/xebec.c b/sys/netiso/xebec/xebec.c index 132bcb8487..bda6cd0777 100644 --- a/sys/netiso/xebec/xebec.c +++ b/sys/netiso/xebec/xebec.c @@ -1,5 +1,7 @@ -/* $Header: xebec.c,v 2.2 88/09/19 12:55:37 nhall Exp $ */ -/* $Source: /var/home/tadl/src/argo/xebec/RCS/xebec.c,v $ */ +/* + * from: xebec.c,v 2.2 88/09/19 12:55:37 nhall Exp + * $Id$ + */ #include "xebec.h" #include "llparse.h" diff --git a/sys/netiso/xebec/xebec.h b/sys/netiso/xebec/xebec.h index 168bb77b24..57dc4231e4 100644 --- a/sys/netiso/xebec/xebec.h +++ b/sys/netiso/xebec/xebec.h @@ -1,5 +1,7 @@ -/* $Header: xebec.h,v 2.1 88/09/19 12:56:35 nhall Exp $ */ -/* $Source: /var/home/tadl/src/argo/xebec/RCS/xebec.h,v $ */ +/* + * from: xebec.h,v 2.1 88/09/19 12:56:35 nhall Exp + * $Id$ + */ union llattrib { struct { diff --git a/sys/netns/idp.h b/sys/netns/idp.h index 1ed795d75d..6b451e5771 100644 --- a/sys/netns/idp.h +++ b/sys/netns/idp.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)idp.h 7.4 (Berkeley) 6/28/90 + * from: @(#)idp.h 7.4 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netns/idp_usrreq.c b/sys/netns/idp_usrreq.c index 22d6c9df6b..9fb831a8ae 100644 --- a/sys/netns/idp_usrreq.c +++ b/sys/netns/idp_usrreq.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)idp_usrreq.c 7.11 (Berkeley) 6/27/91 + * from: @(#)idp_usrreq.c 7.11 (Berkeley) 6/27/91 + * $Id$ */ #include "param.h" diff --git a/sys/netns/idp_var.h b/sys/netns/idp_var.h index 5dd61b88f4..e382b905b7 100644 --- a/sys/netns/idp_var.h +++ b/sys/netns/idp_var.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)idp_var.h 7.4 (Berkeley) 6/28/90 + * from: @(#)idp_var.h 7.4 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netns/ns.c b/sys/netns/ns.c index d9796fe04a..914b002ebc 100644 --- a/sys/netns/ns.c +++ b/sys/netns/ns.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ns.c 7.8 (Berkeley) 6/27/91 + * from: @(#)ns.c 7.8 (Berkeley) 6/27/91 + * $Id$ */ #include "param.h" diff --git a/sys/netns/ns.h b/sys/netns/ns.h index 6eece989f4..97b5a2059d 100644 --- a/sys/netns/ns.h +++ b/sys/netns/ns.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ns.h 7.8 (Berkeley) 2/22/91 + * from: @(#)ns.h 7.8 (Berkeley) 2/22/91 + * $Id$ */ /* diff --git a/sys/netns/ns_error.c b/sys/netns/ns_error.c index 592b1bf64b..0a0a7abf0c 100644 --- a/sys/netns/ns_error.c +++ b/sys/netns/ns_error.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ns_error.c 7.8 (Berkeley) 6/28/90 + * from: @(#)ns_error.c 7.8 (Berkeley) 6/28/90 + * $Id$ */ #include "param.h" diff --git a/sys/netns/ns_error.h b/sys/netns/ns_error.h index d5ec64d2c5..8a68adb9a4 100644 --- a/sys/netns/ns_error.h +++ b/sys/netns/ns_error.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ns_error.h 7.5 (Berkeley) 6/28/90 + * from: @(#)ns_error.h 7.5 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netns/ns_if.h b/sys/netns/ns_if.h index 27834a6a30..0d2da9743e 100644 --- a/sys/netns/ns_if.h +++ b/sys/netns/ns_if.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ns_if.h 7.6 (Berkeley) 6/28/90 + * from: @(#)ns_if.h 7.6 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netns/ns_input.c b/sys/netns/ns_input.c index f9121b30a8..38e82c1e44 100644 --- a/sys/netns/ns_input.c +++ b/sys/netns/ns_input.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ns_input.c 7.8 (Berkeley) 6/27/91 + * from: @(#)ns_input.c 7.8 (Berkeley) 6/27/91 + * $Id$ */ #include "param.h" diff --git a/sys/netns/ns_ip.c b/sys/netns/ns_ip.c index 12962113a8..0eaf5d6f40 100644 --- a/sys/netns/ns_ip.c +++ b/sys/netns/ns_ip.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ns_ip.c 7.6 (Berkeley) 6/28/90 + * from: @(#)ns_ip.c 7.6 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netns/ns_output.c b/sys/netns/ns_output.c index 9a485f5f8f..49ec4ccad8 100644 --- a/sys/netns/ns_output.c +++ b/sys/netns/ns_output.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ns_output.c 7.8 (Berkeley) 12/16/90 + * from: @(#)ns_output.c 7.8 (Berkeley) 12/16/90 + * $Id$ */ #include "param.h" diff --git a/sys/netns/ns_pcb.c b/sys/netns/ns_pcb.c index 0d3bd8e55c..d250f7fd6d 100644 --- a/sys/netns/ns_pcb.c +++ b/sys/netns/ns_pcb.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ns_pcb.c 7.11 (Berkeley) 6/27/91 + * from: @(#)ns_pcb.c 7.11 (Berkeley) 6/27/91 + * $Id$ */ #include "param.h" diff --git a/sys/netns/ns_pcb.h b/sys/netns/ns_pcb.h index d310d3adaf..674c716af6 100644 --- a/sys/netns/ns_pcb.h +++ b/sys/netns/ns_pcb.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ns_pcb.h 7.4 (Berkeley) 6/28/90 + * from: @(#)ns_pcb.h 7.4 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netns/ns_proto.c b/sys/netns/ns_proto.c index 3ff4a14e41..3ec33f391d 100644 --- a/sys/netns/ns_proto.c +++ b/sys/netns/ns_proto.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ns_proto.c 7.4 (Berkeley) 6/28/90 + * from: @(#)ns_proto.c 7.4 (Berkeley) 6/28/90 + * $Id$ */ #include "param.h" diff --git a/sys/netns/sp.h b/sys/netns/sp.h index 1c8840a7fb..797851f33a 100644 --- a/sys/netns/sp.h +++ b/sys/netns/sp.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)sp.h 7.4 (Berkeley) 6/28/90 + * from: @(#)sp.h 7.4 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netns/spidp.h b/sys/netns/spidp.h index 6b31368835..3c53f5a0eb 100644 --- a/sys/netns/spidp.h +++ b/sys/netns/spidp.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)spidp.h 7.4 (Berkeley) 6/28/90 + * from: @(#)spidp.h 7.4 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netns/spp_debug.c b/sys/netns/spp_debug.c index 65ed716bb9..3bbc72aca1 100644 --- a/sys/netns/spp_debug.c +++ b/sys/netns/spp_debug.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)spp_debug.c 7.7 (Berkeley) 6/28/90 + * from: @(#)spp_debug.c 7.7 (Berkeley) 6/28/90 + * $Id$ */ #include "param.h" diff --git a/sys/netns/spp_debug.h b/sys/netns/spp_debug.h index 4ddeaf2415..e52f1a1316 100644 --- a/sys/netns/spp_debug.h +++ b/sys/netns/spp_debug.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)spp_debug.h 7.4 (Berkeley) 6/28/90 + * from: @(#)spp_debug.h 7.4 (Berkeley) 6/28/90 + * $Id$ */ struct spp_debug { diff --git a/sys/netns/spp_timer.h b/sys/netns/spp_timer.h index 7ff80bd54f..c7f8a4b9e9 100644 --- a/sys/netns/spp_timer.h +++ b/sys/netns/spp_timer.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)spp_timer.h 7.3 (Berkeley) 6/28/90 + * from: @(#)spp_timer.h 7.3 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/netns/spp_usrreq.c b/sys/netns/spp_usrreq.c index af308db9a2..13e7f72832 100644 --- a/sys/netns/spp_usrreq.c +++ b/sys/netns/spp_usrreq.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)spp_usrreq.c 7.15 (Berkeley) 6/27/91 + * from: @(#)spp_usrreq.c 7.15 (Berkeley) 6/27/91 + * $Id$ */ #include "param.h" diff --git a/sys/netns/spp_var.h b/sys/netns/spp_var.h index 1fad77f47b..e7bc92e976 100644 --- a/sys/netns/spp_var.h +++ b/sys/netns/spp_var.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)spp_var.h 7.7 (Berkeley) 6/28/90 + * from: @(#)spp_var.h 7.7 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/nfs/nfs.h b/sys/nfs/nfs.h index 2be9d4be60..4078c4698e 100644 --- a/sys/nfs/nfs.h +++ b/sys/nfs/nfs.h @@ -33,9 +33,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfs.h 7.11 (Berkeley) 4/19/91 + * From: @(#)nfs.h 7.11 (Berkeley) 4/19/91 + * $Id$ */ +#ifndef __h_nfs +#define __h_nfs 1 + /* * Tunable constants for nfs */ @@ -162,3 +166,5 @@ struct nfsstats { #ifdef KERNEL struct nfsstats nfsstats; #endif /* KERNEL */ + +#endif /* __h_nfs */ diff --git a/sys/nfs/nfs_bio.c b/sys/nfs/nfs_bio.c index 1081a83ec4..89862b7abf 100644 --- a/sys/nfs/nfs_bio.c +++ b/sys/nfs/nfs_bio.c @@ -33,18 +33,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfs_bio.c 7.19 (Berkeley) 4/16/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00147 - * -------------------- ----- ---------------------- - * - * 20 Apr 93 Paul Kranenburg Detect and prevent kernel deadlocks in - * VM system + * From: @(#)nfs_bio.c 7.19 (Berkeley) 4/16/91 + * $Id$ */ #include "param.h" +#include "systm.h" #include "proc.h" #include "buf.h" #include "uio.h" @@ -53,6 +47,9 @@ #include "trace.h" #include "mount.h" #include "resourcevar.h" +#ifdef PROTOTYPESDONE +#include "vm/vnode_pager.h" +#endif /*PROTOTYPESDONE*/ #include "nfsnode.h" #include "nfsv2.h" @@ -68,6 +65,7 @@ * Vnode op for read using bio * Any similarity to readip() is purely coincidental */ +int nfs_bioread(vp, uio, ioflag, cred) register struct vnode *vp; register struct uio *uio; @@ -80,7 +78,7 @@ nfs_bioread(vp, uio, ioflag, cred) struct vattr vattr; daddr_t lbn, bn, rablock; int diff, error = 0; - long n, on; + long n = 0, on = 0; #ifdef lint ioflag = ioflag; @@ -166,6 +164,8 @@ nfs_bioread(vp, uio, ioflag, cred) error = bread(vp, uio->uio_offset, NFS_DIRBLKSIZ, cred, &bp); n = MIN(uio->uio_resid, NFS_DIRBLKSIZ - bp->b_resid); break; + default: + ; }; if (error) { brelse(bp); @@ -184,6 +184,8 @@ nfs_bioread(vp, uio, ioflag, cred) case VDIR: uio->uio_offset = bp->b_blkno; break; + default: + ; }; brelse(bp); } while (error == 0 && uio->uio_resid > 0 && n != 0); @@ -193,6 +195,7 @@ nfs_bioread(vp, uio, ioflag, cred) /* * Vnode op for write using bio */ +int nfs_write(vp, uio, ioflag, cred) register struct vnode *vp; register struct uio *uio; diff --git a/sys/nfs/nfs_node.c b/sys/nfs/nfs_node.c index 810378e6ca..d46f7d0680 100644 --- a/sys/nfs/nfs_node.c +++ b/sys/nfs/nfs_node.c @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfs_node.c 7.34 (Berkeley) 5/15/91 + * From: @(#)nfs_node.c 7.34 (Berkeley) 5/15/91 + * $Id$ */ #include "param.h" @@ -72,6 +73,7 @@ union nhead { * Initialize hash links for nfsnodes * and build nfsnode free list. */ +void nfs_nhinit() { register int i; @@ -111,6 +113,7 @@ nfs_hash(fhp) * In all cases, a pointer to a * nfsnode structure is returned. */ +int nfs_nget(mntp, fhp, npp) struct mount *mntp; register nfsv2fh_t *fhp; @@ -163,6 +166,7 @@ loop: return (0); } +int nfs_inactive(vp, p) struct vnode *vp; struct proc *p; @@ -219,6 +223,7 @@ nfs_inactive(vp, p) /* * Reclaim an nfsnode so that it can be used for other purposes. */ +int nfs_reclaim(vp) register struct vnode *vp; { @@ -247,7 +252,7 @@ int donfslocking = 0; /* * Lock an nfsnode */ - +int nfs_lock(vp) struct vnode *vp; { @@ -265,11 +270,13 @@ nfs_lock(vp) np->n_lockwaiter = 0; np->n_lockholder = curproc->p_pid; np->n_flag |= NLOCKED; + return(0); } /* * Unlock an nfsnode */ +int nfs_unlock(vp) struct vnode *vp; { @@ -281,11 +288,13 @@ nfs_unlock(vp) np->n_flag &= ~NWANT; wakeup((caddr_t)np); } + return(0); } /* * Check for a locked nfsnode */ +int nfs_islocked(vp) struct vnode *vp; { @@ -300,6 +309,7 @@ nfs_islocked(vp) * since I can't decide if dirs. should be locked, I will check for * the lock and be flexible */ +void nfs_nput(vp) struct vnode *vp; { @@ -315,6 +325,7 @@ nfs_nput(vp) * done. Currently nothing to do. */ /* ARGSUSED */ +int nfs_abortop(ndp) struct nameidata *ndp; { diff --git a/sys/nfs/nfs_serv.c b/sys/nfs/nfs_serv.c index 80f8008a50..927960451c 100644 --- a/sys/nfs/nfs_serv.c +++ b/sys/nfs/nfs_serv.c @@ -33,16 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfs_serv.c 7.40 (Berkeley) 5/15/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 3 00090 - * -------------------- ----- ---------------------- - * - * 08 Sep 92 Rick "gopher I" Fix "truncate" (conflicting?) - * 28 Aug 92 Arne Henrik Juul Fixed NFS "create" bug - * 02 Mar 92 Greg Hackney Make NFS POSIX compliant (anon fix) + * From: @(#)nfs_serv.c 7.40 (Berkeley) 5/15/91 + * $Id$ */ /* @@ -64,6 +56,7 @@ */ #include "param.h" +#include "systm.h" #include "proc.h" #include "file.h" #include "namei.h" @@ -94,6 +87,7 @@ nfstype nfs_type[9]={ NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFNON, /* * nfs getattr service */ +int nfsrv_getattr(mrep, md, dpos, cred, xid, mrq, repstat, p) struct mbuf **mrq; struct mbuf *mrep, *md; @@ -131,6 +125,7 @@ nfsrv_getattr(mrep, md, dpos, cred, xid, mrq, repstat, p) /* * nfs setattr service */ +int nfsrv_setattr(mrep, md, dpos, cred, xid, mrq, repstat, p) struct mbuf **mrq; struct mbuf *mrep, *md; @@ -206,6 +201,7 @@ out: /* * nfs lookup rpc */ +int nfsrv_lookup(mrep, md, dpos, cred, xid, mrq, repstat, p) struct mbuf **mrq; struct mbuf *mrep, *md; @@ -256,6 +252,7 @@ nfsrv_lookup(mrep, md, dpos, cred, xid, mrq, repstat, p) /* * nfs readlink service */ +int nfsrv_readlink(mrep, md, dpos, cred, xid, mrq, repstat, p) struct mbuf **mrq; struct mbuf *mrep, *md; @@ -339,6 +336,7 @@ out: /* * nfs read service */ +int nfsrv_read(mrep, md, dpos, cred, xid, mrq, repstat, p) struct mbuf **mrq; struct mbuf *mrep, *md; @@ -449,6 +447,7 @@ nfsrv_read(mrep, md, dpos, cred, xid, mrq, repstat, p) /* * nfs write service */ +int nfsrv_write(mrep, md, dpos, cred, xid, mrq, repstat, p) struct mbuf *mrep, *md, **mrq; caddr_t dpos; @@ -563,6 +562,7 @@ nfsrv_write(mrep, md, dpos, cred, xid, mrq, repstat, p) * if it already exists, just set length * 28 Aug 92* * do NOT truncate unconditionally ! */ +int nfsrv_create(mrep, md, dpos, cred, xid, mrq, repstat, p) struct mbuf *mrep, *md, **mrq; caddr_t dpos; @@ -625,7 +625,7 @@ nfsrv_create(mrep, md, dpos, cred, xid, mrq, repstat, p) error = ENXIO; goto out; #endif /* FIFO */ - } else if (error = suser(cred, (short *)0)) { + } else if (error = suser(cred, (u_short *)0)) { VOP_ABORTOP(&nd); vput(nd.ni_dvp); goto out; @@ -700,11 +700,13 @@ out: vrele(nd.ni_startdir); free(nd.ni_pnbuf, M_NAMEI); nfsm_reply(0); + return 0; } /* * nfs remove service */ +int nfsrv_remove(mrep, md, dpos, cred, xid, mrq, repstat, p) struct mbuf *mrep, *md, **mrq; caddr_t dpos; @@ -734,7 +736,7 @@ nfsrv_remove(mrep, md, dpos, cred, xid, mrq, repstat, p) nfsm_reply(0); vp = nd.ni_vp; if (vp->v_type == VDIR && - (error = suser(cred, (short *)0))) + (error = suser(cred, (u_short *)0))) goto out; /* * The root of a mounted filesystem cannot be deleted. @@ -763,6 +765,7 @@ out: /* * nfs rename service */ +int nfsrv_rename(mrep, md, dpos, cred, xid, mrq, repstat, p) struct mbuf *mrep, *md, **mrq; caddr_t dpos; @@ -881,6 +884,7 @@ nfsmout: /* * nfs link service */ +int nfsrv_link(mrep, md, dpos, cred, xid, mrq, repstat, p) struct mbuf *mrep, *md, **mrq; caddr_t dpos; @@ -943,6 +947,7 @@ out1: /* * nfs symbolic link service */ +int nfsrv_symlink(mrep, md, dpos, cred, xid, mrq, repstat, p) struct mbuf *mrep, *md, **mrq; caddr_t dpos; @@ -1023,6 +1028,7 @@ nfsmout: /* * nfs mkdir service */ +int nfsrv_mkdir(mrep, md, dpos, cred, xid, mrq, repstat, p) struct mbuf *mrep, *md, **mrq; caddr_t dpos; @@ -1099,6 +1105,7 @@ nfsmout: /* * nfs rmdir service */ +int nfsrv_rmdir(mrep, md, dpos, cred, xid, mrq, repstat, p) struct mbuf *mrep, *md, **mrq; caddr_t dpos; @@ -1186,6 +1193,7 @@ out: * to including the status longwords that are not a part of the dir. * "entry" structures, but are in the rpc. */ +int nfsrv_readdir(mrep, md, dpos, cred, xid, mrq, repstat, p) struct mbuf **mrq; struct mbuf *mrep, *md; @@ -1375,6 +1383,7 @@ again: /* * nfs statfs service */ +int nfsrv_statfs(mrep, md, dpos, cred, xid, mrq, repstat, p) struct mbuf **mrq; struct mbuf *mrep, *md; @@ -1418,6 +1427,7 @@ nfsrv_statfs(mrep, md, dpos, cred, xid, mrq, repstat, p) * Null operation, used by clients to ping server */ /* ARGSUSED */ +int nfsrv_null(mrep, md, dpos, cred, xid, mrq, repstat, p) struct mbuf **mrq; struct mbuf *mrep, *md; @@ -1440,6 +1450,7 @@ nfsrv_null(mrep, md, dpos, cred, xid, mrq, repstat, p) * No operation, used for obsolete procedures */ /* ARGSUSED */ +int nfsrv_noop(mrep, md, dpos, cred, xid, mrq, repstat, p) struct mbuf **mrq; struct mbuf *mrep, *md; @@ -1471,6 +1482,7 @@ nfsrv_noop(mrep, md, dpos, cred, xid, mrq, repstat, p) * this because it opens a security hole, but since the nfs server opens * a security hole the size of a barn door anyhow, what the heck. */ +int nfsrv_access(vp, flags, cred, p) register struct vnode *vp; int flags; @@ -1490,6 +1502,8 @@ nfsrv_access(vp, flags, cred, p) switch (vp->v_type) { case VREG: case VDIR: case VLNK: return (EROFS); + default: + ; } } /* diff --git a/sys/nfs/nfs_socket.c b/sys/nfs/nfs_socket.c index 904cc51515..cf23d4a302 100644 --- a/sys/nfs/nfs_socket.c +++ b/sys/nfs/nfs_socket.c @@ -33,15 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfs_socket.c 7.23 (Berkeley) 4/20/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00053 - * -------------------- ----- ---------------------- - * - * 08 Sep 92 Rick "gopher I" Fix "reserved port" bug, fixed for - * AIX3.2 NFS clients + * From: @(#)nfs_socket.c 7.23 (Berkeley) 4/20/91 + * $Id$ */ /* @@ -49,6 +42,7 @@ */ #include "param.h" +#include "systm.h" #include "proc.h" #include "mount.h" #include "kernel.h" @@ -154,6 +148,7 @@ int nfs_tcpnodelay = 1; * Initialize sockets and congestion for a new NFS connection. * We do not free the sockaddr if error. */ +int nfs_connect(nmp) register struct nfsmount *nmp; { @@ -284,6 +279,7 @@ bad: * If this fails the mount point is DEAD! * nb: Must be called with the nfs_solock() set on the mount point. */ +int nfs_reconnect(rep, nmp) register struct nfsreq *rep; register struct nfsmount *nmp; @@ -339,6 +335,7 @@ nfs_disconnect(nmp) * must be called with an nfs_solock() on the socket. * "rep == NULL" indicates that it has been called from a server. */ +int nfs_send(so, nam, top, rep) register struct socket *so; struct mbuf *nam; @@ -393,6 +390,7 @@ nfs_send(so, nam, top, rep) * For SOCK_STREAM we must be very careful to read an entire record once * we have read any of it, even if the system call has been interrupted. */ +int nfs_receive(so, aname, mp, rep) register struct socket *so; struct mbuf **aname; @@ -402,8 +400,8 @@ nfs_receive(so, aname, mp, rep) struct uio auio; struct iovec aio; register struct mbuf *m; - struct mbuf *m2, *mnew, **mbp; - caddr_t fcp, tcp; + struct mbuf *m2 = 0, *mnew, **mbp; + caddr_t fcp, tcp = 0; u_long len; struct mbuf **getnam; int error, siz, mlen, soflags, rcvflg; @@ -615,6 +613,7 @@ errout: * with outstanding requests using the xid, until ours is found. */ /* ARGSUSED */ +int nfs_reply(nmp, myrep) struct nfsmount *nmp; struct nfsreq *myrep; @@ -624,8 +623,8 @@ nfs_reply(nmp, myrep) register int error = 0; u_long rxid; struct mbuf *mp, *nam; - char *cp; - int cnt, xfer; +/* char *cp; */ +/* int cnt, xfer; */ /* * Loop around until we get our own reply @@ -735,6 +734,7 @@ nfs_reply(nmp, myrep) * by mrep or error * nb: always frees up mreq mbuf list */ +int nfs_request(vp, mreq, xid, procnum, procp, tryhard, mp, mrp, mdp, dposp) struct vnode *vp; struct mbuf *mreq; @@ -931,6 +931,7 @@ nfsmout: * - verify it * - fill in the cred struct. */ +int nfs_getreq(so, prog, vers, maxproc, nam, mrp, mdp, dposp, retxid, procnum, cr, msk, mtch, wascomp, repstat) /* 08 Aug 92*/ struct socket *so; @@ -1060,6 +1061,7 @@ nfsmout: * Generate the rpc reply header * siz arg. is used to decide if adding a cluster is worthwhile */ +int nfs_rephead(siz, retxid, err, mrq, mbp, bposp) int siz; u_long retxid; @@ -1127,7 +1129,9 @@ nfs_rephead(siz, retxid, err, mrq, mbp, bposp) * To avoid retransmission attempts on STREAM sockets (in the future) make * sure to set the r_retry field to 0 (implies nm_retry == 0). */ -nfs_timer() +void +nfs_timer(arg) + caddr_t arg; { register struct nfsreq *rep; register struct mbuf *m; @@ -1196,7 +1200,8 @@ nfs_timer() nfsstats.rpcretries++; if ((nmp->nm_flag & NFSMNT_NOCONN) == 0) error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, m, - (caddr_t)0, (struct mbuf *)0, (struct mbuf *)0); + (struct mbuf *)0, (struct mbuf *)0, + (struct mbuf *)0); else error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, m, nmp->nm_nam, (struct mbuf *)0, (struct mbuf *)0); @@ -1256,6 +1261,7 @@ nfs_timer() */ #define NFS_RTO(nmp) (((nmp)->nm_srtt >> 3) + (nmp)->nm_rttvar) +void nfs_updatetimer(nmp) register struct nfsmount *nmp; { @@ -1304,6 +1310,7 @@ nfs_updatetimer(nmp) nmp->nm_window = NFS_MAXWINDOW; } +void nfs_backofftimer(nmp) register struct nfsmount *nmp; { @@ -1339,6 +1346,7 @@ nfs_backofftimer(nmp) * Test for a termination signal pending on procp. * This is used for NFSMNT_INT mounts. */ +int nfs_sigintr(p) register struct proc *p; { @@ -1349,9 +1357,10 @@ nfs_sigintr(p) return (0); } +void nfs_msg(p, server, msg) struct proc *p; - char *server, *msg; + const char *server, *msg; { tpr_t tpr; @@ -1369,6 +1378,7 @@ nfs_msg(p, server, msg) * and also to avoid race conditions between the processes with nfs requests * in progress when a reconnect is necessary. */ +void nfs_solock(flagp) register int *flagp; { @@ -1383,6 +1393,7 @@ nfs_solock(flagp) /* * Unlock the stream socket for others. */ +void nfs_sounlock(flagp) register int *flagp; { @@ -1401,6 +1412,7 @@ nfs_sounlock(flagp) * if they are the same. * If there is any doubt, return FALSE. */ +int nfs_netaddr_match(nam1, nam2) struct mbuf *nam1, *nam2; { @@ -1434,6 +1446,7 @@ nfs_netaddr_match(nam1, nam2) * - Compare for == with match * return TRUE if not equal */ +int nfs_badnam(nam, msk, mtch) register struct mbuf *nam, *msk, *mtch; { diff --git a/sys/nfs/nfs_srvcache.c b/sys/nfs/nfs_srvcache.c index b274b17fbe..802ca5075a 100644 --- a/sys/nfs/nfs_srvcache.c +++ b/sys/nfs/nfs_srvcache.c @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfs_srvcache.c 7.11 (Berkeley) 4/16/91 + * From: @(#)nfs_srvcache.c 7.11 (Berkeley) 4/16/91 + * $Id$ */ /* @@ -71,7 +72,8 @@ union rhead { } rhead[NFSRCHSZ]; static struct nfsrvcache nfsrvcachehead; -static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ]; +/* static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ]; */ +static struct nfsrvcache *nfsrvcache; #define TRUE 1 #define FALSE 0 @@ -125,13 +127,18 @@ static int repliesstatus[NFS_NPROCS] = { /* * Initialize the server request cache list */ +void nfsrv_initcache() { register int i; - register struct nfsrvcache *rp = nfsrvcache; + register struct nfsrvcache *rp; register struct nfsrvcache *hp = &nfsrvcachehead; register union rhead *rh = rhead; + MALLOC(nfsrvcache, struct nfsrvcache *, + NFSRVCACHESIZ * sizeof *nfsrvcache, M_CACHE, M_WAITOK); + rp = nfsrvcache; + for (i = NFSRCHSZ; --i >= 0; rh++) { rh->rh_head[0] = rh; rh->rh_head[1] = rh; @@ -164,6 +171,7 @@ nfsrv_initcache() * return DOIT * Update/add new request at end of lru list */ +int nfsrv_getcache(nam, xid, proc, repp) struct mbuf *nam; u_long xid; @@ -176,6 +184,8 @@ nfsrv_getcache(nam, xid, proc, repp) caddr_t bpos; int ret; + if(!nfsrvcache) nfsrv_initcache(); + rh = &rhead[NFSRCHASH(xid)]; loop: for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) { @@ -245,6 +255,7 @@ loop: /* * Update a request cache entry after the rpc has been done */ +void nfsrv_updatecache(nam, xid, proc, repvalid, repstat, repmbuf) struct mbuf *nam; u_long xid; @@ -256,6 +267,8 @@ nfsrv_updatecache(nam, xid, proc, repvalid, repstat, repmbuf) register struct nfsrvcache *rp; register union rhead *rh; + if(!nfsrvcache) nfsrv_initcache(); + rh = &rhead[NFSRCHASH(xid)]; loop: for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) { diff --git a/sys/nfs/nfs_subs.c b/sys/nfs/nfs_subs.c index 86537c658b..6d86451f24 100644 --- a/sys/nfs/nfs_subs.c +++ b/sys/nfs/nfs_subs.c @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfs_subs.c 7.41 (Berkeley) 5/15/91 + * From: @(#)nfs_subs.c 7.41 (Berkeley) 5/15/91 + * $Id$ */ /* @@ -51,6 +52,9 @@ #include "vnode.h" #include "namei.h" #include "mbuf.h" +#ifdef PROTOTYPESDONE +#include "vm/vnode_pager.h" +#endif /*PROTOTYPESDONE*/ #include "../ufs/quota.h" #include "../ufs/inode.h" @@ -184,6 +188,7 @@ struct mbuf *nfsm_reqh(prog, vers, procid, cred, hsiz, bpos, mb, retxid) /* * copies mbuf chain to the uio scatter/gather list */ +int nfsm_mbuftouio(mrep, uiop, siz, dpos) struct mbuf **mrep; register struct uio *uiop; @@ -258,6 +263,7 @@ nfsm_mbuftouio(mrep, uiop, siz, dpos) /* * copies a uio scatter/gather list to an mbuf chain... */ +int nfsm_uiotombuf(uiop, mq, siz, bpos) register struct uio *uiop; struct mbuf **mq; @@ -343,6 +349,7 @@ nfsm_uiotombuf(uiop, mq, siz, bpos) * This is used by the macros nfsm_disect and nfsm_disecton for tough * cases. (The macros use the vars. dpos and dpos2) */ +int nfsm_disct(mdp, dposp, siz, left, updateflg, cp2) struct mbuf **mdp; caddr_t *dposp; @@ -411,6 +418,7 @@ nfsm_disct(mdp, dposp, siz, left, updateflg, cp2) /* * Advance the position in the mbuf chain. */ +int nfs_adv(mdp, dposp, offs, left) struct mbuf **mdp; caddr_t *dposp; @@ -437,6 +445,7 @@ nfs_adv(mdp, dposp, offs, left) /* * Copy a string into mbufs for the hard cases... */ +int nfsm_strtmbuf(mb, bpos, cp, siz) struct mbuf **mb; char **bpos; @@ -502,6 +511,7 @@ nfsm_strtmbuf(mb, bpos, cp, siz) /* * Called once to initialize data structures... */ +void nfs_init() { register int i; @@ -596,6 +606,7 @@ static char *nfs_unixauth(cr) * Iff vap not NULL * copy the attributes to *vaper */ +int nfs_loadattrcache(vpp, mdp, dposp, vaper) struct vnode **vpp; struct mbuf **mdp; @@ -715,6 +726,7 @@ nfs_loadattrcache(vpp, mdp, dposp, vaper) * If the cache is valid, copy contents to *vap and return 0 * otherwise return an error */ +int nfs_getattrcache(vp, vap) register struct vnode *vp; struct vattr *vap; @@ -740,6 +752,7 @@ nfs_getattrcache(vp, vap) /* * Set up nameidata for a namei() call and do it */ +int nfs_namei(ndp, fhp, len, mdp, dposp, p) register struct nameidata *ndp; fhandle_t *fhp; @@ -847,6 +860,7 @@ out: * A fiddled version of m_adj() that ensures null fill to a long * boundary and only trims off the back end */ +void nfsm_adj(mp, len, nul) struct mbuf *mp; register int len; @@ -912,6 +926,7 @@ nfsm_adj(mp, len, nul) * - if not lockflag unlock it with VOP_UNLOCK() * - if cred->cr_uid == 0 set it to m_exroot */ +int nfsrv_fhtovp(fhp, lockflag, vpp, cred) fhandle_t *fhp; int lockflag; diff --git a/sys/nfs/nfs_syscalls.c b/sys/nfs/nfs_syscalls.c index 285625c024..a334b51dce 100644 --- a/sys/nfs/nfs_syscalls.c +++ b/sys/nfs/nfs_syscalls.c @@ -33,15 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfs_syscalls.c 7.26 (Berkeley) 4/16/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00053 - * -------------------- ----- ---------------------- - * - * 08 Sep 92 Rick "gopher I" Fix "reserved port" bug, fixed for - * AIX3.2 NFS clients + * From: @(#)nfs_syscalls.c 7.26 (Berkeley) 4/16/91 + * $Id$ */ #include "param.h" @@ -109,13 +102,17 @@ static int compressreply[NFS_NPROCS] = { /* * Get file handle system call */ + +struct getfh_args { + char *fname; + fhandle_t *fhp; +}; + /* ARGSUSED */ +int getfh(p, uap, retval) struct proc *p; - register struct args { - char *fname; - fhandle_t *fhp; - } *uap; + register struct getfh_args *uap; int *retval; { register struct nameidata *ndp; @@ -150,16 +147,20 @@ getfh(p, uap, retval) * Nfs server psuedo system call for the nfsd's * Never returns unless it fails or gets killed */ + +struct nfssvc_args { + int s; + caddr_t mskval; + int msklen; + caddr_t mtchval; + int mtchlen; +}; + /* ARGSUSED */ +int nfssvc(p, uap, retval) struct proc *p; - register struct args { - int s; - caddr_t mskval; - int msklen; - caddr_t mtchval; - int mtchlen; - } *uap; + register struct nfssvc_args *uap; int *retval; { register struct mbuf *m; @@ -318,6 +319,7 @@ bad: * Never returns unless it fails or gets killed */ /* ARGSUSED */ +int async_daemon(p, uap, retval) struct proc *p; struct args *uap; diff --git a/sys/nfs/nfs_vfsops.c b/sys/nfs/nfs_vfsops.c index d08c12dd02..d79e856c88 100644 --- a/sys/nfs/nfs_vfsops.c +++ b/sys/nfs/nfs_vfsops.c @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfs_vfsops.c 7.31 (Berkeley) 5/6/91 + * From: @(#)nfs_vfsops.c 7.31 (Berkeley) 5/6/91 + * $Id$ */ #include "param.h" @@ -47,6 +48,7 @@ #include "buf.h" #include "mbuf.h" #include "socket.h" +#include "socketvar.h" #include "systm.h" #include "../net/if.h" @@ -89,6 +91,7 @@ void nfs_disconnect(); /* * nfs statfs call */ +int nfs_statfs(mp, sbp, p) struct mount *mp; register struct statfs *sbp; @@ -149,13 +152,14 @@ nfs_statfs(mp, sbp, p) * - hand craft the swap nfs vnode hanging off a fake mount point * - build the rootfs mount point and call mountnfs() to do the rest. */ +int nfs_mountroot() { register struct mount *mp; register struct mbuf *m; struct socket *so; struct vnode *vp; - int error; +/* int error;*/ /* * Do enough of ifconfig(8) so that critical net interface can @@ -163,7 +167,9 @@ nfs_mountroot() */ if (socreate(nfs_diskless.myif.ifra_addr.sa_family, &so, SOCK_DGRAM, 0)) panic("nfs ifconf"); - if (ifioctl(so, SIOCAIFADDR, &nfs_diskless.myif)) + /* XXX - this probably will crash with a vm_fault, since + ifioctl expects to get a valid process pointer */ + if (ifioctl(so, SIOCAIFADDR, (caddr_t)&nfs_diskless.myif, 0)) panic("nfs ifconf2"); soclose(so); @@ -182,8 +188,15 @@ nfs_mountroot() bcopy((caddr_t)&nfs_diskless.mygateway, (caddr_t)&rt.rt_gateway, sizeof (struct sockaddr_in)); rt.rt_flags = (RTF_UP | RTF_GATEWAY); - if (rtioctl(SIOCADDRT, (caddr_t)&rt)) - panic("nfs root route"); + { + /* copied over from rtioctl() -GW */ + extern struct sockaddr_in icmpmask; + if (rtrequest(RTM_ADD, &rt.rt_dst, &rt.rt_gateway, + (struct sockaddr *)&icmpmask, + RTF_UP | RTF_GATEWAY, + (struct rtentry **)0)) + panic("nfs root route"); + } } #endif /* COMPAT_43 */ @@ -258,7 +271,9 @@ nfs_mountroot() mp->mnt_vnodecovered = NULLVP; vfs_unlock(mp); rootvp = vp; +#ifndef __386BSD__ inittodr((time_t)0); /* There is no time in the nfs fsstat so ?? */ +#endif return (0); } @@ -272,6 +287,7 @@ nfs_mountroot() * an error after that means that I have to release the mbuf. */ /* ARGSUSED */ +int nfs_mount(mp, path, data, ndp, p) struct mount *mp; char *path; @@ -311,6 +327,7 @@ nfs_mount(mp, path, data, ndp, p) /* * Common code for mount and mountroot */ +int mountnfs(argp, mp, nam, pth, hst, vpp) register struct nfs_args *argp; register struct mount *mp; @@ -444,6 +461,7 @@ bad: /* * unmount system call */ +int nfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; @@ -506,6 +524,7 @@ nfs_unmount(mp, mntflags, p) /* * Return root of a filesystem */ +int nfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; @@ -531,6 +550,7 @@ extern int syncprt; * Flush out the buffer cache */ /* ARGSUSED */ +int nfs_sync(mp, waitfor) struct mount *mp; int waitfor; @@ -548,6 +568,7 @@ nfs_sync(mp, waitfor) * At this point, this should never happen */ /* ARGSUSED */ +int nfs_fhtovp(mp, fhp, vpp) struct mount *mp; struct fid *fhp; @@ -561,6 +582,7 @@ nfs_fhtovp(mp, fhp, vpp) * Vnode pointer to File handle, should never happen either */ /* ARGSUSED */ +int nfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; @@ -573,6 +595,7 @@ nfs_vptofh(vp, fhp) * Vfs start routine, a no-op. */ /* ARGSUSED */ +int nfs_start(mp, flags, p) struct mount *mp; int flags; @@ -585,6 +608,7 @@ nfs_start(mp, flags, p) /* * Do operations associated with quotas, not supported */ +int nfs_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; diff --git a/sys/nfs/nfs_vnops.c b/sys/nfs/nfs_vnops.c index 736bc496ea..b52aa5d4da 100644 --- a/sys/nfs/nfs_vnops.c +++ b/sys/nfs/nfs_vnops.c @@ -33,15 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfs_vnops.c 7.60 (Berkeley) 5/24/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00090 - * -------------------- ----- ---------------------- - * - * 02 Mar 93 Greg Hackney Make NFS client POSIX compliant (anon) - * + * From: @(#)nfs_vnops.c 7.60 (Berkeley) 5/24/91 + * $Id: nfs_vnops.c,v 1.3 1993/10/20 07:31:12 davidg Exp $ */ /* @@ -207,6 +200,7 @@ int nfs_numasync = 0; /* * nfs null call from vfs. */ +int nfs_null(vp, cred, p) struct vnode *vp; struct ucred *cred; @@ -227,6 +221,7 @@ nfs_null(vp, cred, p) * nfs access vnode op. * Essentially just get vattr and then imitate iaccess() */ +int nfs_access(vp, mode, cred, p) struct vnode *vp; int mode; @@ -273,6 +268,7 @@ found: * Just check to see if the type is ok */ /* ARGSUSED */ +int nfs_open(vp, mode, cred, p) struct vnode *vp; int mode; @@ -293,6 +289,7 @@ nfs_open(vp, mode, cred, p) * For reg files, invalidate any buffer cache entries. */ /* ARGSUSED */ +int nfs_close(vp, fflags, cred, p) register struct vnode *vp; int fflags; @@ -319,6 +316,7 @@ nfs_close(vp, fflags, cred, p) /* * nfs getattr call from vfs. */ +int nfs_getattr(vp, vap, cred, p) register struct vnode *vp; struct vattr *vap; @@ -328,6 +326,7 @@ nfs_getattr(vp, vap, cred, p) return (nfs_dogetattr(vp, vap, cred, 0, p)); } +int nfs_dogetattr(vp, vap, cred, tryhard, p) register struct vnode *vp; struct vattr *vap; @@ -357,6 +356,7 @@ nfs_dogetattr(vp, vap, cred, tryhard, p) /* * nfs setattr call. */ +int nfs_setattr(vp, vap, cred, p) register struct vnode *vp; register struct vattr *vap; @@ -416,6 +416,7 @@ nfs_setattr(vp, vap, cred, p) * First look in cache * If not found, unlock the directory nfsnode and do the rpc */ +int nfs_lookup(vp, ndp, p) register struct vnode *vp; register struct nameidata *ndp; @@ -608,6 +609,7 @@ nfsmout: * nfs read call. * Just call nfs_bioread() to do the work. */ +int nfs_read(vp, uiop, ioflag, cred) register struct vnode *vp; struct uio *uiop; @@ -622,6 +624,7 @@ nfs_read(vp, uiop, ioflag, cred) /* * nfs readlink call */ +int nfs_readlink(vp, uiop, cred) struct vnode *vp; struct uio *uiop; @@ -636,6 +639,7 @@ nfs_readlink(vp, uiop, cred) * Do a readlink rpc. * Called by nfs_doio() from below the buffer cache. */ +int nfs_readlinkrpc(vp, uiop, cred) register struct vnode *vp; struct uio *uiop; @@ -664,6 +668,7 @@ nfs_readlinkrpc(vp, uiop, cred) * nfs read rpc call * Ditto above */ +int nfs_readrpc(vp, uiop, cred) register struct vnode *vp; struct uio *uiop; @@ -707,6 +712,7 @@ nfsmout: /* * nfs write call */ +int nfs_writerpc(vp, uiop, cred) register struct vnode *vp; struct uio *uiop; @@ -749,6 +755,7 @@ nfsmout: * set to specify the file type and the size field for rdev. */ /* ARGSUSED */ +int nfs_mknod(ndp, vap, cred, p) struct nameidata *ndp; struct ucred *cred; @@ -800,6 +807,7 @@ nfs_mknod(ndp, vap, cred, p) /* * nfs file create call */ +int nfs_create(ndp, vap, p) register struct nameidata *ndp; register struct vattr *vap; @@ -847,6 +855,7 @@ nfs_create(ndp, vap, p) * else * do the remove rpc */ +int nfs_remove(ndp, p) register struct nameidata *ndp; struct proc *p; @@ -895,6 +904,7 @@ nfs_remove(ndp, p) /* * nfs file remove rpc called from nfs_inactive */ +int nfs_removeit(sp, p) register struct sillyrename *sp; struct proc *p; @@ -921,6 +931,7 @@ nfs_removeit(sp, p) /* * nfs file rename call */ +int nfs_rename(sndp, tndp, p) register struct nameidata *sndp, *tndp; struct proc *p; @@ -969,6 +980,7 @@ nfs_rename(sndp, tndp, p) /* * nfs file rename rpc called from nfs_remove() above */ +int nfs_renameit(sndp, sp, p) register struct nameidata *sndp; register struct sillyrename *sp; @@ -1001,6 +1013,7 @@ nfs_renameit(sndp, sp, p) /* * nfs hard link create call */ +int nfs_link(vp, ndp, p) register struct vnode *vp; register struct nameidata *ndp; @@ -1041,6 +1054,7 @@ nfs_link(vp, ndp, p) /* * nfs symbolic link create call */ +int nfs_symlink(ndp, vap, nm, p) struct nameidata *ndp; struct vattr *vap; @@ -1085,6 +1099,7 @@ nfs_symlink(ndp, vap, nm, p) /* * nfs make dir call */ +int nfs_mkdir(ndp, vap, p) register struct nameidata *ndp; struct vattr *vap; @@ -1148,6 +1163,7 @@ nfs_mkdir(ndp, vap, p) /* * nfs remove directory call */ +int nfs_rmdir(ndp, p) register struct nameidata *ndp; struct proc *p; @@ -1192,6 +1208,7 @@ nfs_rmdir(ndp, p) * order so that it looks more sensible. This appears consistent with the * Ultrix implementation of NFS. */ +int nfs_readdir(vp, uiop, cred, eofflagp) register struct vnode *vp; struct uio *uiop; @@ -1234,6 +1251,7 @@ nfs_readdir(vp, uiop, cred, eofflagp) * Readdir rpc call. * Called from below the buffer cache by nfs_doio(). */ +int nfs_readdirrpc(vp, uiop, cred) register struct vnode *vp; struct uio *uiop; @@ -1382,6 +1400,7 @@ static char hextoasc[] = "0123456789abcdef"; * to create the same funny name between the nfs_lookitup() fails and the * nfs_rename() completes, but... */ +int nfs_sillyrename(ndp, p) register struct nameidata *ndp; struct proc *p; @@ -1436,6 +1455,7 @@ bad: * into the nfsnode table. * If fhp != NULL it copies the returned file handle out */ +int nfs_lookitup(sp, fhp, p) register struct sillyrename *sp; nfsv2fh_t *fhp; @@ -1476,6 +1496,7 @@ nfs_lookitup(sp, fhp, p) * a lot more work than bcopy() and also it currently happens in the * context of the swapper process (2). */ +int nfs_bmap(vp, bn, vpp, bnp) struct vnode *vp; daddr_t bn; @@ -1494,6 +1515,7 @@ nfs_bmap(vp, bn, vpp, bnp) * If the biod's are running, queue a request * otherwise just call nfs_doio() to get it done */ +int nfs_strategy(bp) register struct buf *bp; { @@ -1552,6 +1574,7 @@ nfs_strategy(bp) * otherwise it is called by the nfsiods to do what would normally be * partially disk interrupt driven. */ +int nfs_doio(bp) register struct buf *bp; { @@ -1559,7 +1582,7 @@ nfs_doio(bp) register struct vnode *vp; struct nfsnode *np; struct ucred *cr; - int error; + int error = 0; struct uio uio; struct iovec io; #if !defined(hp300) && !defined(i386) @@ -1637,6 +1660,8 @@ nfs_doio(bp) */ bp->b_blkno = uiop->uio_offset; break; + default: + panic("nfs_doio bad type"); }; bp->b_error = error; } else { @@ -1669,6 +1694,7 @@ nfs_doio(bp) * NB Currently unsupported. */ /* ARGSUSED */ +int nfs_mmap(vp, fflags, cred, p) struct vnode *vp; int fflags; @@ -1685,6 +1711,7 @@ nfs_mmap(vp, fflags, cred, p) * associated with the vnode. */ /* ARGSUSED */ +int nfs_fsync(vp, fflags, cred, waitfor, p) register struct vnode *vp; int fflags; @@ -1708,6 +1735,7 @@ nfs_fsync(vp, fflags, cred, waitfor, p) * NFS advisory byte-level locks. * Currently unsupported. */ +int nfs_advlock(vp, id, op, fl, flags) struct vnode *vp; caddr_t id; @@ -1715,13 +1743,19 @@ nfs_advlock(vp, id, op, fl, flags) struct flock *fl; int flags; { +#ifdef notyet + register struct nfsnode *np = VTONFS(vp); + return (lf_advlock(&(np->n_lockf), np->n_size, id, op, fl, flags)); +#else return (EOPNOTSUPP); +#endif } /* * Print out the contents of an nfsnode. */ +int nfs_print(vp) struct vnode *vp; { @@ -1740,4 +1774,5 @@ nfs_print(vp) if (np->n_lockwaiter) printf(" waiting pid %d", np->n_lockwaiter); printf("\n"); + return(0); } diff --git a/sys/nfs/nfscompress.h b/sys/nfs/nfscompress.h index fed621ca51..7f71b7efc2 100644 --- a/sys/nfs/nfscompress.h +++ b/sys/nfs/nfscompress.h @@ -33,9 +33,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfscompress.h 7.2 (Berkeley) 10/2/90 + * From: @(#)nfscompress.h 7.2 (Berkeley) 10/2/90 + * $Id$ */ +#ifndef __h_nfscompress +#define __h_nfscompress 1 + /* * Definitions for the compression algorithm */ @@ -76,3 +80,5 @@ (c) = *ip++; \ ileft--; \ } + +#endif /* __h_nfscompress */ diff --git a/sys/nfs/nfsdiskless.h b/sys/nfs/nfsdiskless.h index bee5d12c13..b1cd3b571d 100644 --- a/sys/nfs/nfsdiskless.h +++ b/sys/nfs/nfsdiskless.h @@ -33,9 +33,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfsdiskless.h 7.1 (Berkeley) 3/4/91 + * From: @(#)nfsdiskless.h 7.1 (Berkeley) 3/4/91 + * $Id$ */ +#ifndef __h_nfsdiskless +#define __h_nfsdiskless 1 + /* * Structure that must be initialized for a diskless nfs client. * This structure is used by nfs_mountroot() to set up the root and swap @@ -56,3 +60,5 @@ struct nfs_diskless { struct sockaddr root_saddr; /* Address of root server */ char *root_hostnam; /* Host name for mount pt */ }; + +#endif /* __h_nfsdiskless */ diff --git a/sys/nfs/nfsiom.h b/sys/nfs/nfsiom.h index 0466b7a09d..1d3ebc5595 100644 --- a/sys/nfs/nfsiom.h +++ b/sys/nfs/nfsiom.h @@ -33,9 +33,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfsiom.h 7.3 (Berkeley) 6/28/90 + * From: @(#)nfsiom.h 7.3 (Berkeley) 6/28/90 + * $Id$ */ +#ifndef __h_nfsiom +#define __h_nfsiom 1 + /* * Size of the resource map and system page table pte's that are mapped * by nfs for i/o much like a bus adapter @@ -43,3 +47,5 @@ #define NFS_MAPREG 512 /* Num. of kernel pte's for i/o mapping */ /* Must be >= MAXPHYS/NBPG */ #define NFS_MSIZ 100 /* Size of alloc. map for pte's */ + +#endif /* __h_nfsiom */ diff --git a/sys/nfs/nfsm_subs.h b/sys/nfs/nfsm_subs.h index 7bbf77c182..9841f65c25 100644 --- a/sys/nfs/nfsm_subs.h +++ b/sys/nfs/nfsm_subs.h @@ -33,9 +33,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfsm_subs.h 7.11 (Berkeley) 4/16/91 + * From: @(#)nfsm_subs.h 7.11 (Berkeley) 4/16/91 + * $Id$ */ +#ifndef __h_nfsm_subs +#define __h_nfsm_subs 1 + /* * These macros do strange and peculiar things to mbuf chains for * the assistance of the nfs code. To attempt to use them for any @@ -299,3 +303,18 @@ extern struct mbuf *nfsm_reqh(); fp->fa_ctime.tv_sec = txdr_unsigned(vap->va_ctime.tv_sec); \ fp->fa_ctime.tv_usec = txdr_unsigned(vap->va_gen) +/* These are here to provide prototypes for everything in the NFS system. + * Added 8.6.-93, Garrett A. Wollman, University of Vermont. */ +struct nfsmount; + +void nfs_updatetimer __P((struct nfsmount *)); +void nfs_backofftimer __P((struct nfsmount *)); +int nfs_sigintr __P((struct proc *)); +void nfs_msg __P((struct proc *, const char *, const char *)); +void nfs_solock __P((int *)); +void nfs_sounlock __P((int *)); +int nfs_netaddr_match __P((struct mbuf *, struct mbuf *)); +int nfs_badnam __P((struct mbuf *, struct mbuf *, struct mbuf *)); + + +#endif /* __h_nfsm_subs */ diff --git a/sys/nfs/nfsmount.h b/sys/nfs/nfsmount.h index a2a2c5d8c5..fd7b33beaa 100644 --- a/sys/nfs/nfsmount.h +++ b/sys/nfs/nfsmount.h @@ -33,9 +33,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfsmount.h 7.7 (Berkeley) 4/16/91 + * From: @(#)nfsmount.h 7.7 (Berkeley) 4/16/91 + * $Id$ */ +#ifndef __h_nfsmount +#define __h_nfsmount 1 + /* * Mount structure. * One allocated on every NFS mount. @@ -115,3 +119,5 @@ int nfs_vptofh __P(( struct vnode *vp, struct fid *fhp)); int nfs_init __P(()); + +#endif /* __h_nfsmount */ diff --git a/sys/nfs/nfsnode.h b/sys/nfs/nfsnode.h index e08a338cd3..fc16705044 100644 --- a/sys/nfs/nfsnode.h +++ b/sys/nfs/nfsnode.h @@ -33,9 +33,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfsnode.h 7.12 (Berkeley) 4/16/91 + * From: @(#)nfsnode.h 7.12 (Berkeley) 4/16/91 + * $Id: nfsnode.h,v 1.2 1993/09/09 22:06:24 rgrimes Exp $ */ +#ifndef __h_nfsnode +#define __h_nfsnode 1 + /* * The nfsnode is the nfs equivalent to ufs's inode. Any similarity * is purely coincidental. @@ -53,6 +57,7 @@ struct nfsnode { struct vattr n_vattr; /* Vnode attribute cache */ struct sillyrename *n_sillyrename; /* Ptr to silly rename struct */ u_long n_size; /* Current size of file */ + struct lockf *n_lockf; /* Locking record of file */ time_t n_mtime; /* Prev modify time to maintain data cache consistency*/ time_t n_ctime; /* Prev create time for name cache consistency*/ int n_error; /* Save write error value */ @@ -219,3 +224,6 @@ int nfs_advlock __P(( int op, struct flock *fl, int flags)); + +void nfs_nput __P((struct vnode *)); +#endif /* __h_nfsnode */ diff --git a/sys/nfs/nfsrvcache.h b/sys/nfs/nfsrvcache.h index 7aa6f810f1..c0b66bcd26 100644 --- a/sys/nfs/nfsrvcache.h +++ b/sys/nfs/nfsrvcache.h @@ -33,9 +33,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfsrvcache.h 7.3 (Berkeley) 6/28/90 + * From: @(#)nfsrvcache.h 7.3 (Berkeley) 6/28/90 + * $Id$ */ +#ifndef __h_nfsrvcache +#define __h_nfsrvcache 1 + /* * Definitions for the server recent request cache */ @@ -91,3 +95,5 @@ struct nfsrvcache { /* Delay time after completion that request is dropped */ #define RC_DELAY 2 /* seconds */ + +#endif /* __h_nfsrvcache */ diff --git a/sys/nfs/nfsv2.h b/sys/nfs/nfsv2.h index 365696b476..30ecd8195c 100644 --- a/sys/nfs/nfsv2.h +++ b/sys/nfs/nfsv2.h @@ -33,17 +33,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)nfsv2.h 7.8 (Berkeley) 6/28/90 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00053 - * -------------------- ----- ---------------------- - * - * 08 Sep 92 Rick "gopher I" Fix "reserved port" bug, fixed for - * AIX3.2 NFS clients + * From: @(#)nfsv2.h 7.8 (Berkeley) 6/28/90 + * $Id$ */ +#ifndef __h_nfsv2 +#define __h_nfsv2 1 + /* * nfs definitions as per the version 2 specs */ @@ -166,3 +162,5 @@ struct nfsv2_statfs { u_long sf_bfree; u_long sf_bavail; }; + +#endif /* __h_nfsv2 */ diff --git a/sys/nfs/rpcv2.h b/sys/nfs/rpcv2.h index 8f81484afe..1808103bea 100644 --- a/sys/nfs/rpcv2.h +++ b/sys/nfs/rpcv2.h @@ -33,9 +33,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)rpcv2.h 7.4 (Berkeley) 6/28/90 + * From: @(#)rpcv2.h 7.4 (Berkeley) 6/28/90 + * $Id$ */ +#ifndef __h_rpcv2 +#define __h_rpcv2 1 + /* * Definitions for Sun RPC Version 2, from * "RPC: Remote Procedure Call Protocol Specification" RFC1057 @@ -85,3 +89,5 @@ #define RPCMNT_NAMELEN 255 #define RPCMNT_PATHLEN 1024 #define RPCPROG_NFS 100003 + +#endif /* __h_rpcv2 */ diff --git a/sys/nfs/xdr_subs.h b/sys/nfs/xdr_subs.h index 5bf212df85..08819c323b 100644 --- a/sys/nfs/xdr_subs.h +++ b/sys/nfs/xdr_subs.h @@ -33,9 +33,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)xdr_subs.h 7.3 (Berkeley) 6/28/90 + * From: @(#)xdr_subs.h 7.3 (Berkeley) 6/28/90 + * $Id$ */ +#ifndef __h_xdr_subs +#define __h_xdr_subs 1 + /* * Macros used for conversion to/from xdr representation by nfs... * These use the MACHINE DEPENDENT routines ntohl, htonl @@ -55,3 +59,5 @@ ((struct timeval *)(t))->tv_usec=htonl( \ ((struct timeval *)(f))->tv_usec);} + +#endif /* __h_xdr_subs */ diff --git a/sys/pcfs/bootsect.h b/sys/pcfs/bootsect.h index 026c8c4530..9b836a2cc4 100644 --- a/sys/pcfs/bootsect.h +++ b/sys/pcfs/bootsect.h @@ -15,8 +15,7 @@ * * October 1992 * - * $Header: /usr/src/CVS/sys/pcfs/bootsect.h,v 1.1.2.1 1993/02/07 21:57:06 friedl Exp $ - * + * $Id$ */ /* diff --git a/sys/pcfs/bpb.h b/sys/pcfs/bpb.h index 99d44eb46d..844d8513a9 100644 --- a/sys/pcfs/bpb.h +++ b/sys/pcfs/bpb.h @@ -15,8 +15,7 @@ * * October 1992 * - * $Header: /usr/src/CVS/sys/pcfs/bpb.h,v 1.1.2.1 1993/02/07 21:57:08 friedl Exp $ - * + * $Id$ */ /* diff --git a/sys/pcfs/denode.h b/sys/pcfs/denode.h index 1f6d4db254..4c28393e53 100644 --- a/sys/pcfs/denode.h +++ b/sys/pcfs/denode.h @@ -15,8 +15,7 @@ * * October 1992 * - * $Header: /usr/src/CVS/sys/pcfs/denode.h,v 1.1.2.1 1993/02/07 21:57:10 friedl Exp $ - * + * $Id$ */ /* diff --git a/sys/pcfs/direntry.h b/sys/pcfs/direntry.h index f0a7624b52..7d25f6a80a 100644 --- a/sys/pcfs/direntry.h +++ b/sys/pcfs/direntry.h @@ -15,8 +15,7 @@ * * October 1992 * - * $Header: /usr/src/CVS/sys/pcfs/direntry.h,v 1.1.2.1 1993/02/07 21:57:12 friedl Exp $ - * + * $Id$ */ /* diff --git a/sys/pcfs/fat.h b/sys/pcfs/fat.h index 4fd771226e..1a3c1cf4a7 100644 --- a/sys/pcfs/fat.h +++ b/sys/pcfs/fat.h @@ -15,8 +15,7 @@ * * October 1992 * - * $Header: /usr/src/CVS/sys/pcfs/fat.h,v 1.1.2.1 1993/02/07 21:57:13 friedl Exp $ - * + * $Id$ */ /* diff --git a/sys/pcfs/pcfs_conv.c b/sys/pcfs/pcfs_conv.c index 415ac1e1d1..b78925cd99 100644 --- a/sys/pcfs/pcfs_conv.c +++ b/sys/pcfs/pcfs_conv.c @@ -15,8 +15,7 @@ * * October 1992 * - * $Header: /usr/src/CVS/sys/pcfs/pcfs_conv.c,v 1.1.2.1 1993/02/07 21:57:15 friedl Exp $ - * + * $Id$ */ /* diff --git a/sys/pcfs/pcfs_denode.c b/sys/pcfs/pcfs_denode.c index 3a873839d3..1601df0fb8 100644 --- a/sys/pcfs/pcfs_denode.c +++ b/sys/pcfs/pcfs_denode.c @@ -15,15 +15,7 @@ * * October 1992 * - * $Header: /usr/src/CVS/sys/pcfs/pcfs_denode.c,v 1.1.2.1 1993/02/07 21:57:17 friedl Exp $ - * - * April 15, 1993 - * "James Jegers" , made it work for rgrimes! - * in file pcfs_denode.c on line 184 change - * - * ldep->de_FileSize = pmp->pm_rootdirsize; - * to - * ldep->de_FileSize = pmp->pm_rootdirsize * 512; + * $Id$ */ #include "param.h" diff --git a/sys/pcfs/pcfs_fat.c b/sys/pcfs/pcfs_fat.c index b2e93c2163..eea358fc1f 100644 --- a/sys/pcfs/pcfs_fat.c +++ b/sys/pcfs/pcfs_fat.c @@ -15,8 +15,7 @@ * * October 1992 * - * $Header: /usr/src/CVS/sys/pcfs/pcfs_fat.c,v 1.1.2.2 1993/02/07 21:57:19 friedl Exp $ - * + * $Id$ */ /* diff --git a/sys/pcfs/pcfs_lookup.c b/sys/pcfs/pcfs_lookup.c index 3ef6e053e9..39f164f925 100644 --- a/sys/pcfs/pcfs_lookup.c +++ b/sys/pcfs/pcfs_lookup.c @@ -15,11 +15,11 @@ * * October 1992 * - * $Header: /usr/src/CVS/sys/pcfs/pcfs_lookup.c,v 1.1.2.2 1993/02/07 21:57:22 friedl Exp $ - * + * $Id: pcfs_lookup.c,v 1.3 1993/10/16 19:29:35 rgrimes Exp $ */ #include "param.h" +#include "systm.h" #include "namei.h" #include "buf.h" #include "vnode.h" @@ -113,7 +113,7 @@ printf("pcfs_lookup(): vdp %08x, dp %08x, Attr %02x\n", if (error == ENOENT) return error; #ifdef PARANOID - if (vdp == ndp->ni_rdir && ndp->ni_isdotdot) + if (vdp == ndp->ni_rootdir && ndp->ni_isdotdot) panic("pcfs_lookup: .. thru root"); #endif /* PARANOID */ pdp = dp; diff --git a/sys/pcfs/pcfs_vfsops.c b/sys/pcfs/pcfs_vfsops.c index 2aae2422f0..a71deaca8b 100644 --- a/sys/pcfs/pcfs_vfsops.c +++ b/sys/pcfs/pcfs_vfsops.c @@ -15,15 +15,7 @@ * * October 1992 * - * $Header: /usr/src/CVS/sys/pcfs/pcfs_vfsops.c,v 1.1.2.1 1993/02/07 21:57:25 friedl Exp $ - * - * April 6, 1992 - * - * Changed MOUNT_PCFS to MOUNT_MSDOS, this whole package should be renamed - * to msdosfs, but I did not have the time to do it. Some one please do - * this and resubmit it to the patchkit! - * Rodney W. Grimes - * + * $Id$ */ #include "param.h" @@ -196,7 +188,7 @@ mountpcfs(devvp, mp, p) int ronly = (mp->mnt_flag & MNT_RDONLY) != 0; dev_t dev = devvp->v_rdev; union bootsector *bsp; - struct pcfsmount *pmp; + struct pcfsmount *pmp = NULL; struct buf *bp0 = NULL; struct byte_bpb33 *b33; struct byte_bpb50 *b50; diff --git a/sys/pcfs/pcfs_vnops.c b/sys/pcfs/pcfs_vnops.c index 3fa588a07d..b508ec2903 100644 --- a/sys/pcfs/pcfs_vnops.c +++ b/sys/pcfs/pcfs_vnops.c @@ -15,8 +15,7 @@ * * October 1992 * - * $Header: /usr/src/CVS/sys/pcfs/pcfs_vnops.c,v 1.1.2.2 1993/02/07 21:57:27 friedl Exp $ - * + * $Id$ */ #include "param.h" diff --git a/sys/pcfs/pcfsmount.h b/sys/pcfs/pcfsmount.h index ecf27bbeae..c7c2537a59 100644 --- a/sys/pcfs/pcfsmount.h +++ b/sys/pcfs/pcfsmount.h @@ -15,8 +15,7 @@ * * October 1992 * - * $Header: /usr/src/CVS/sys/pcfs/pcfsmount.h,v 1.1.2.1 1993/02/07 21:57:30 friedl Exp $ - * + * $Id$ */ /* diff --git a/sys/scsi/README b/sys/scsi/README index 16e0998eb2..fbd1e6e415 100644 --- a/sys/scsi/README +++ b/sys/scsi/README @@ -1,10 +1,3 @@ - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- - * - * 16 Feb 93 Julian Elischer ADDED for SCSI system This release consists of the following files (relative to the base of the kernel tree) @@ -20,9 +13,14 @@ scsi/scsi_all.h scsi/scsi_disk.h scsi/sd.c +sys/mtio.h (modified) scsi/scsi_tape.h scsi/st.c +sys/sgio.h +scsi/scsi_generic.h +scsi/sg.c /* port not complete */ + sys/chio.h scsi/scsi_changer.h scsi/ch.c @@ -34,7 +32,6 @@ scsi/cd.c i386/conf/SCSITEST i386/isa/aha1542.c -README.AHA1742 i386/conf/AHBTEST i386/isa/aha1742.c @@ -59,15 +56,16 @@ AEG Character recognition devices * Calera Character recognition devices * Kodak IL900 scanner * Exabyte tape changer device. +GENERIC SCSI DEVICES (user generated scsi commands) (port not complete) ---------------------------------------------------------------- There are also working bottom end drivers for: ---------------------------------------------------------------- adaptec 1542 (and 1742 in 1542 mode) -bustec 742a +bustec 742a (apparently works for VESA version) adaptec 174x -Ultrastore 14f +Ultrastore 14f (works for 34f (VESA version)) ---------------------------------------------------------------- @@ -97,6 +95,12 @@ e.g. the first tape found will become st0 (minor number 0) These devices could be on the same scsi bus or different scsi busses. That would not change their minor numbers. +THE EXCEPTION TO THIS IS IN THE GENERIC SCSI DRIVER. in which case +the following mapping applies: + +BB LLL TTT B= scsi bus number, T = target number, L = LUN. +(yes I know it's strange but it's SGI compatible) + It is possible to run two different TYPES of scsi adapters at the same time and have st0 on one and st1 on another. (for example) @@ -129,6 +133,7 @@ encoded into the minor number in some way, and this may be supported at some timein the future, using minor numbers greater than 128. (or maybe a different major number) + I will also be writing (probably) a generic scsi-error handling routine that will be table driven, so that the routine can be removed from each individual driver. With enough care, @@ -152,16 +157,17 @@ scsi-changer.h commands medium changer devices --- CHAPTER 16 ---------ioctl definitions------------- User accessable structures (e.g. ioctl definitions) have been -placed in sys/cdio and sys/chio (based after sys/mtio for +placed in sys/cdio, sys/sgio and sys/chio (based after sys/mtio for the ioctls for mag tapes (including st). -----------cd-rom----------------- The cd rom driver ha been tested by a number of people and grefen@wilbur.zdv.uni-mainz.de has completed the audio play functions. -He tells me he has some Public Domain package that -allows an control of the cd player from an Xwindow -but I don't have it. +(xcdplayer was available from the 'from_ref' directory on agate) + +At this time it is possible audio play is broken on cdroms and I will +be unable to fix it until I get one to test. -------------media changer--------------- Once again courtesy of grefen@wilbur.zdv.uni-mainz.de. @@ -170,6 +176,8 @@ If anyone has an exabyte tape changer or similar, contact the author for information regarding the control interface and program. +WARNING: This has not been tested for a LONG TIME! + -----------booting from an AHA-174x--------- For some reason I have not yet worked out, the BIOS-based bootblocks I have posted will not boot @@ -180,3 +188,22 @@ mode during probe, so it can be left in standard mode during the boot. During the next reboot, the bios will place it back in standard mode ready for the NEXT boot. +[Update: This has apparently been fixed in the newest NetBSD/FreeBSD +releases ] + + + +---------recent changes----------- +Removed all bitfields from machine independent sections to make +it possible for them to be used on big-endian architectures. + +Removed scsi specific timeouts in favour of system timeout handling. + +Many structures (getting more all the time) now dynamically allocated. + +Addition of code in the tape driver to recognise models of drive that +have particular problems so they can be handled specially. + +many bug-fixes and cleanups. + +$Id$ diff --git a/sys/scsi/README.AHA1742 b/sys/scsi/README.AHA1742 deleted file mode 100644 index 3362f1f82b..0000000000 --- a/sys/scsi/README.AHA1742 +++ /dev/null @@ -1,24 +0,0 @@ - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- - * - * 16 Feb 93 Julian Elischer ADDED for SCSI system - -Just a note: -The new bootblocks do not work with the 1742 in extended mode: -They DO however love it in standard mode. - -The driver automatically switches the board to extended mode when -it finds it -so leave your 1742 board in standard mode and just boot with the new bootblocks. - -When unix starts the board will magically be in extended mode -and when it is rebooted the bios will take it back to standard mode again. - -Eventually I will fix the bootblocks, but this works just as well. - -julian - - diff --git a/sys/scsi/cd.c b/sys/scsi/cd.c index 2a2261e697..fd4879c3c5 100644 --- a/sys/scsi/cd.c +++ b/sys/scsi/cd.c @@ -12,19 +12,9 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * - */ -static char rev[] = "$Revision: 1.3 $"; - -/* * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00149 - * -------------------- ----- ---------------------- - * - * 16 Feb 93 Julian Elischer ADDED for SCSI system - * 20 Apr 93 Julian Elishcer Fixed error reporting + * $Id: cd.c,v 1.9 1993/09/20 06:27:02 rgrimes Exp $ */ #define SPLCD splbio @@ -78,10 +68,6 @@ int cd_done(); int cdstrategy(); int cd_debug = 0; -struct buf cd_buf_queue[NCD]; -struct scsi_xfer cd_scsi_xfer[NCD][CDOUTSTANDING]; /* XXX */ -struct scsi_xfer *cd_free_xfer[NCD]; -int cd_xfer_block_wait[NCD]; struct cd_data { @@ -104,12 +90,21 @@ struct cd_data int partflags[MAXPARTITIONS]; /* per partition flags */ #define CDOPEN 0x01 int openparts; /* one bit for each open partition */ -}cd_data[NCD]; + int xfer_block_wait; + struct scsi_xfer *free_xfer; + struct scsi_xfer scsi_xfer[CDOUTSTANDING]; /* XXX */ + struct buf buf_queue; +}; #define CD_STOP 0 #define CD_START 1 #define CD_EJECT -2 +struct cd_driver +{ + int size; + struct cd_data **cd_data; +}*cd_driver; static int next_cd_unit = 0; /***********************************************************************\ @@ -119,23 +114,82 @@ static int next_cd_unit = 0; int cdattach(ctlr,targ,lu,scsi_switch) struct scsi_switch *scsi_switch; { - int unit,i; - unsigned char *tbl; - struct cd_data *cd; - struct cd_parms *dp; + int unit,i; + unsigned char *tbl; + struct cd_data *cd, **cdrealloc; + struct cd_parms *dp; - unit = next_cd_unit++; - cd = cd_data + unit; - dp = &(cd->params); +#ifdef CDDEBUG if(scsi_debug & PRINTROUTINES) printf("cdattach: "); +#endif /*CDDEBUG*/ /*******************************************************\ - * Check we have the resources for another drive * + * Check if we have resources allocated yet, if not * + * allocate and initialize them * \*******************************************************/ - if( unit >= NCD) + if (next_cd_unit == 0) { - printf("Too many scsi CDs..(%d > %d) reconfigure kernel",(unit + 1),NCD); + cd_driver = + malloc(sizeof(struct cd_driver),M_DEVBUF,M_NOWAIT); + if(!cd_driver) + { + printf("cd%d: malloc failed for cd_driver\n",unit); + return(0); + } + bzero(cd_driver,sizeof(cd_driver)); + cd_driver->size = 0; + } + /*******************************************************\ + * allocate the resources for another drive * + * if we have already allocate a cd_data pointer we must * + * copy the old pointers into a new region that is * + * larger and release the old region, aka realloc * + \*******************************************************/ + unit = next_cd_unit++; + /* XXX + * This if will always be true for now, but future code may + * preallocate more units to reduce overhead. This would be + * done by changing the malloc to be (next_cd_unit * x) and + * the cd_driver->size++ to be +x + */ + if(unit >= cd_driver->size) + { + cdrealloc = + malloc(sizeof(cd_driver->cd_data) * next_cd_unit, + M_DEVBUF,M_NOWAIT); + if(!cdrealloc) + { + printf("cd%d: malloc failed for cdrealloc\n",unit); + return(0); + } + /* Make sure we have something to copy before we copy it */ + bzero(cdrealloc,sizeof(cd_driver->cd_data) * next_cd_unit); + if(cd_driver->size) + { + bcopy(cd_driver->cd_data,cdrealloc, + sizeof(cd_driver->cd_data) * cd_driver->size); + free(cd_driver->cd_data,M_DEVBUF); + } + cd_driver->cd_data = cdrealloc; + cd_driver->cd_data[unit] = NULL; + cd_driver->size++; + } + if(cd_driver->cd_data[unit]) + { + printf("cd%d: Already has storage!\n",unit); + return(0); + } + /*******************************************************\ + * allocate the per drive data area * + \*******************************************************/ + cd = cd_driver->cd_data[unit] = + malloc(sizeof(struct cd_data),M_DEVBUF,M_NOWAIT); + if(!cd) + { + printf("cd%d: malloc failed for cd_data\n",unit); return(0); } + bzero(cd,sizeof(struct cd_data)); + dp = &(cd->params); /*******************************************************\ * Store information needed to contact our base driver * \*******************************************************/ @@ -145,12 +199,11 @@ struct scsi_switch *scsi_switch; cd->lu = lu; cd->cmdscount = CDOUTSTANDING; /* XXX (ask the board) */ - i = cd->cmdscount; while(i-- ) { - cd_scsi_xfer[unit][i].next = cd_free_xfer[unit]; - cd_free_xfer[unit] = &cd_scsi_xfer[unit][i]; + cd->scsi_xfer[i].next = cd->free_xfer; + cd->free_xfer = &cd->scsi_xfer[i]; } /*******************************************************\ * Use the subdriver to request information regarding * @@ -160,19 +213,19 @@ struct scsi_switch *scsi_switch; cd_get_parms(unit, SCSI_NOSLEEP | SCSI_NOMASK); if(dp->disksize) { - printf("cd present\n"); + printf("cd%d: cd present.[%d x %d byte records]\n", + unit, + cd->params.disksize, + cd->params.blksize); } else { - printf("drive empty\n"); + printf("cd%d: drive empty\n", unit); } cd->flags |= CDINIT; return; - } - - /*******************************************************\ * open the device. Make sure the partition info * * is a up-to-date as can be. * @@ -182,27 +235,28 @@ cdopen(dev) int errcode = 0; int unit, part; struct cd_parms cd_parms; - struct cd_data *cd ; + struct cd_data *cd; unit = UNIT(dev); part = PARTITION(dev); - cd = cd_data + unit; + +#ifdef CDDEBUG if(scsi_debug & (PRINTROUTINES | TRACEOPENS)) printf("cdopen: dev=0x%x (unit %d (of %d),partition %d)\n" - , dev, unit, NCD, part); + ,dev,unit,cd_driver->size,part); +#endif /*CDDEBUG*/ /*******************************************************\ * Check the unit is legal * \*******************************************************/ - if ( unit >= NCD ) + if ( unit >= cd_driver->size ) { return(ENXIO); } + cd = cd_driver->cd_data[unit]; /*******************************************************\ - * Make sure the disk has been initialised * - * At some point in the future, get the scsi driver * - * to look for a new device if we are not initted * + * Make sure the device has been initialised * \*******************************************************/ - if (! (cd->flags & CDINIT)) + if ((cd == NULL) || (!(cd->flags & CDINIT))) return(ENXIO); /*******************************************************\ @@ -213,48 +267,66 @@ cdopen(dev) if ((! (cd->flags & CDVALID)) && ( cd->openparts)) return(ENXIO); + /*******************************************************\ * Check that it is still responding and ok. * * if the media has been changed this will result in a * * "unit attention" error which the error code will * * disregard because the CDVALID flag is not yet set * \*******************************************************/ - if (cd_req_sense(unit, SCSI_SILENT) != 0) { + cd_test_ready(unit, SCSI_SILENT); + + /*******************************************************\ + * Next time actually take notice of error returns * + \*******************************************************/ + if (cd_test_ready(unit, SCSI_SILENT) != 0) { +#ifdef CDDEBUG if(scsi_debug & TRACEOPENS) - printf("not reponding\n"); + printf("not ready\n"); +#endif /*CDDEBUG*/ return(ENXIO); } +#ifdef CDDEBUG if(scsi_debug & TRACEOPENS) printf("Device present\n"); +#endif /*CDDEBUG*/ /*******************************************************\ * In case it is a funny one, tell it to start * - * not needed for hard drives * + * not needed for some drives * \*******************************************************/ cd_start_unit(unit,part,CD_START); cd_prevent_unit(unit,PR_PREVENT,SCSI_SILENT); +#ifdef CDDEBUG if(scsi_debug & TRACEOPENS) printf("started "); +#endif /*CDDEBUG*/ /*******************************************************\ * Load the physical device parameters * \*******************************************************/ cd_get_parms(unit, 0); +#ifdef CDDEBUG if(scsi_debug & TRACEOPENS) printf("Params loaded "); +#endif /*CDDEBUG*/ /*******************************************************\ * Load the partition info if not already loaded * \*******************************************************/ cdgetdisklabel(unit); +#ifdef CDDEBUG if(scsi_debug & TRACEOPENS) printf("Disklabel fabricated "); +#endif /*CDDEBUG*/ /*******************************************************\ * Check the partition is legal * \*******************************************************/ if (( part >= cd->disklabel.d_npartitions ) && (part != RAW_PART)) { +#ifdef CDDEBUG if(scsi_debug & TRACEOPENS) printf("partition %d > %d\n",part ,cd->disklabel.d_npartitions); +#endif /*CDDEBUG*/ cd_prevent_unit(unit,PR_ALLOW,SCSI_SILENT); return(ENXIO); } @@ -266,14 +338,18 @@ cdopen(dev) { cd->partflags[part] |= CDOPEN; cd->openparts |= (1 << part); +#ifdef CDDEBUG if(scsi_debug & TRACEOPENS) printf("open complete\n"); +#endif /*CDDEBUG*/ cd->flags |= CDVALID; } else { +#ifdef CDDEBUG if(scsi_debug & TRACEOPENS) printf("part %d type UNUSED\n",part); +#endif /*CDDEBUG*/ cd_prevent_unit(unit,PR_ALLOW,SCSI_SILENT); return(ENXIO); } @@ -289,26 +365,28 @@ int flags; int unit; { struct scsi_xfer *xs; + struct cd_data *cd; int s; + cd = cd_driver->cd_data[unit]; if(flags & (SCSI_NOSLEEP | SCSI_NOMASK)) { - if (xs = cd_free_xfer[unit]) + if (xs = cd->free_xfer) { - cd_free_xfer[unit] = xs->next; + cd->free_xfer = xs->next; xs->flags = 0; } } else { s = SPLCD(); - while (!(xs = cd_free_xfer[unit])) + while (!(xs = cd->free_xfer)) { - cd_xfer_block_wait[unit]++; /* someone waiting! */ - sleep((caddr_t)&cd_free_xfer[unit], PRIBIO+1); - cd_xfer_block_wait[unit]--; + cd->xfer_block_wait++; /* someone waiting! */ + sleep((caddr_t)&cd->free_xfer, PRIBIO+1); + cd->xfer_block_wait--; } - cd_free_xfer[unit] = xs->next; + cd->free_xfer = xs->next; splx(s); xs->flags = 0; } @@ -323,25 +401,27 @@ struct scsi_xfer *xs; int unit; int flags; { + struct cd_data *cd; int s; + cd = cd_driver->cd_data[unit]; if(flags & SCSI_NOMASK) { - if (cd_xfer_block_wait[unit]) + if (cd->xfer_block_wait) { - printf("doing a wakeup from NOMASK mode\n"); - wakeup((caddr_t)&cd_free_xfer[unit]); + printf("cd%d: doing a wakeup from NOMASK mode\n", unit); + wakeup((caddr_t)&cd->free_xfer); } - xs->next = cd_free_xfer[unit]; - cd_free_xfer[unit] = xs; + xs->next = cd->free_xfer; + cd->free_xfer = xs; } else { s = SPLCD(); - if (cd_xfer_block_wait[unit]) - wakeup((caddr_t)&cd_free_xfer[unit]); - xs->next = cd_free_xfer[unit]; - cd_free_xfer[unit] = xs; + if (cd->xfer_block_wait) + wakeup((caddr_t)&cd->free_xfer); + xs->next = cd->free_xfer; + cd->free_xfer = xs; splx(s); } } @@ -356,7 +436,7 @@ int flags; void cdminphys(bp) struct buf *bp; { - (*(cd_data[UNIT(bp->b_dev)].sc_sw->scsi_minphys))(bp); + (*(cd_driver->cd_data[UNIT(bp->b_dev)]->sc_sw->scsi_minphys))(bp); } /*******************************************************\ @@ -371,15 +451,17 @@ struct buf *bp; { struct buf *dp; unsigned int opri; - struct cd_data *cd ; + struct cd_data *cd; int unit; cdstrats++; unit = UNIT((bp->b_dev)); - cd = cd_data + unit; + cd = cd_driver->cd_data[unit]; +#ifdef CDDEBUG if(scsi_debug & PRINTROUTINES) printf("\ncdstrategy "); if(scsi_debug & SHOWREQUESTS) printf("cd%d: %d bytes @ blk%d\n", unit,bp->b_bcount,bp->b_blkno); +#endif /*CDDEBUG*/ cdminphys(bp); /*******************************************************\ * If the device has been made invalid, error out * @@ -424,7 +506,7 @@ struct buf *bp; } opri = SPLCD(); - dp = &cd_buf_queue[unit]; + dp = &cd->buf_queue; /*******************************************************\ * Place it in the queue of disk activities for this disk* @@ -475,26 +557,29 @@ int unit; struct scsi_xfer *xs; struct scsi_rw_big cmd; int blkno, nblk; - struct cd_data *cd = cd_data + unit; + struct cd_data *cd; struct partition *p ; +#ifdef CDDEBUG if(scsi_debug & PRINTROUTINES) printf("cdstart%d ",unit); +#endif /*CDDEBUG*/ + cd = cd_driver->cd_data[unit]; /*******************************************************\ * See if there is a buf to do and we are not already * * doing one * \*******************************************************/ - if(!cd_free_xfer[unit]) + if(!cd->free_xfer) { return; /* none for us, unit already underway */ } - if(cd_xfer_block_wait[unit]) /* there is one, but a special waits */ + if(cd->xfer_block_wait) /* there is one, but a special waits */ { return; /* give the special that's waiting a chance to run */ } - dp = &cd_buf_queue[unit]; + dp = &cd->buf_queue; if ((bp = dp->b_actf) != NULL) /* yes, an assign */ { dp->b_actf = bp->av_forw; @@ -517,15 +602,20 @@ int unit; /*******************************************************\ * We have a buf, now we should move the data into * * a scsi_xfer definition and try start it * - \*******************************************************/ - /*******************************************************\ + * * * First, translate the block to absolute * * and put it in terms of the logical blocksize of the * * device.. * + * really a bit silly until we have real partitions, but.* \*******************************************************/ - p = cd->disklabel.d_partitions + PARTITION(bp->b_dev); - blkno = ((bp->b_blkno / (cd->params.blksize/512)) + p->p_offset); + blkno = bp->b_blkno / (cd->params.blksize/512); + if(PARTITION(bp->b_dev) != RAW_PART) + { + p = cd->disklabel.d_partitions + PARTITION(bp->b_dev); + blkno += p->p_offset; + } nblk = (bp->b_bcount + (cd->params.blksize - 1)) / (cd->params.blksize); + /* what if something asks for 512 bytes not on a 2k boundary? *//*XXX*/ /*******************************************************\ * Fill out the scsi command * @@ -583,66 +673,71 @@ struct scsi_xfer *xs; { struct buf *bp; int retval; + struct cd_data *cd = cd_driver->cd_data[unit]; +#ifdef CDDEBUG if(scsi_debug & PRINTROUTINES) printf("cd_done%d ",unit); +#endif /*CDDEBUG*/ +#ifdef PARANOIA if (! (xs->flags & INUSE)) /* paranoia always pays off */ panic("scsi_xfer not in use!"); - if(bp = xs->bp) +#endif /*PARANOIA*/ + if(!(bp = xs->bp)) { - switch(xs->error) + wakeup(xs); + return 0; + } + switch(xs->error) + { + case XS_NOERROR: + bp->b_error = 0; + bp->b_resid = 0; + break; + + case XS_SENSE: + retval = (cd_interpret_sense(unit,xs)); + if(retval) { - case XS_NOERROR: - bp->b_error = 0; - bp->b_resid = 0; - break; + bp->b_flags |= B_ERROR; + bp->b_error = retval; + } + break; - case XS_SENSE: - retval = (cd_interpret_sense(unit,xs)); - if(retval) - { - bp->b_flags |= B_ERROR; - bp->b_error = retval; - } - break; + case XS_TIMEOUT: + printf("cd%d timeout\n",unit); - case XS_TIMEOUT: - printf("cd%d timeout\n",unit); - - case XS_BUSY: - /***********************************\ - * Just resubmit it straight back to * - * the SCSI driver to try it again * - \***********************************/ - if(xs->retries--) - { - xs->error = XS_NOERROR; - xs->flags &= ~ITSDONE; - if ( (*(cd_data[unit].sc_sw->scsi_cmd))(xs) - == SUCCESSFULLY_QUEUED) - { /* shhh! don't wake the job, ok? */ - /* don't tell cdstart either, */ - return; - } - /* xs->error is set by the scsi driver */ - } /* Fall through */ + case XS_BUSY: + /***********************************\ + * Just resubmit it straight back to * + * the SCSI driver to try it again * + \***********************************/ + if(xs->retries--) + { + xs->error = XS_NOERROR; + xs->flags &= ~ITSDONE; + if ((*(cd->sc_sw->scsi_cmd))(xs) + == SUCCESSFULLY_QUEUED) + { /* shhh! don't wake the job, ok? */ + /* don't tell cdstart either, */ + return 0; + } + /* xs->error is set by the scsi driver */ + } /* Fall through */ - case XS_DRIVER_STUFFUP: - bp->b_flags |= B_ERROR; - bp->b_error = EIO; - break; - default: - printf("cd%d: unknown error category from scsi driver\n" - ,unit); - } - biodone(bp); - cd_free_xs(unit,xs,0); - cdstart(unit); /* If there's anything waiting.. do it */ - } - else /* special has finished */ - { - wakeup(xs); - } + case XS_DRIVER_STUFFUP: + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + break; + default: + printf("cd%d: unknown error category from scsi driver\n" + ,unit); + } + biodone(bp); + cd_free_xs(unit,xs,0); + cdstart(unit); /* If there's anything waiting.. do it */ + return 0; } + /*******************************************************\ * Perform special action on behalf of the user * * Knows about the internals of this device * @@ -660,13 +755,15 @@ cdioctl(dev_t dev, int cmd, caddr_t addr, int flag) \*******************************************************/ unit = UNIT(dev); part = PARTITION(dev); - cd = &cd_data[unit]; + cd = cd_driver->cd_data[unit]; +#ifdef CDDEBUG if(scsi_debug & PRINTROUTINES) printf("cdioctl%d ",unit); +#endif /*CDDEBUG*/ /*******************************************************\ * If the device is not valid.. abandon ship * \*******************************************************/ - if (!(cd_data[unit].flags & CDVALID)) + if (!(cd_driver->cd_data[unit]->flags & CDVALID)) return(EIO); switch(cmd) { @@ -710,8 +807,8 @@ cdioctl(dev_t dev, int cmd, caddr_t addr, int flag) struct cd_mode_data data; if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) break; - data.page.audio.sotc = 0; - data.page.audio.immed = 1; + data.page.audio.flags &= ~CD_PA_SOTC; + data.page.audio.flags |= CD_PA_IMMED; if(error = cd_set_mode(unit,&data)) break; return(cd_play_tracks(unit @@ -722,6 +819,27 @@ cdioctl(dev_t dev, int cmd, caddr_t addr, int flag) )); } break; + case CDIOCPLAYMSF: + { + struct ioc_play_msf *args + = (struct ioc_play_msf *)addr; + struct cd_mode_data data; + if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) + break; + data.page.audio.flags &= ~CD_PA_SOTC; + data.page.audio.flags |= CD_PA_IMMED; + if(error = cd_set_mode(unit,&data)) + break; + return(cd_play_msf(unit + ,args->start_m + ,args->start_s + ,args->start_f + ,args->end_m + ,args->end_s + ,args->end_f + )); + } + break; case CDIOCPLAYBLOCKS: { struct ioc_play_blocks *args @@ -729,8 +847,8 @@ cdioctl(dev_t dev, int cmd, caddr_t addr, int flag) struct cd_mode_data data; if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) break; - data.page.audio.sotc = 0; - data.page.audio.immed = 1; + data.page.audio.flags &= ~CD_PA_SOTC; + data.page.audio.flags |= CD_PA_IMMED; if(error = cd_set_mode(unit,&data)) break; return(cd_play(unit,args->blk,args->len)); @@ -883,10 +1001,10 @@ cdioctl(dev_t dev, int cmd, caddr_t addr, int flag) struct cd_mode_data data; if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) break; - data.page.audio.port[LEFT_PORT].channels = 15; - data.page.audio.port[RIGHT_PORT].channels = 15; - data.page.audio.port[2].channels = 15; - data.page.audio.port[3].channels = 15; + data.page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL; + data.page.audio.port[RIGHT_PORT].channels = LEFT_CHANNEL; + data.page.audio.port[2].channels = 0; + data.page.audio.port[3].channels = 0; if(error = cd_set_mode(unit,&data)) break; } @@ -949,10 +1067,11 @@ unsigned char unit; /*unsigned int n, m;*/ char *errstring; struct dos_partition *dos_partition_p; - struct cd_data *cd = cd_data + unit; + struct cd_data *cd; + cd = cd_driver->cd_data[unit]; /*******************************************************\ - * If the inflo is already loaded, use it * + * If the info is already loaded, use it * \*******************************************************/ if(cd->flags & CDHAVELABEL) return; @@ -975,7 +1094,8 @@ unsigned char unit; cd->disklabel.d_npartitions = 1; cd->disklabel.d_partitions[0].p_offset = 0; - cd->disklabel.d_partitions[0].p_size = cd->params.disksize; + cd->disklabel.d_partitions[0].p_size + = cd->params.disksize * (cd->params.blksize / 512); cd->disklabel.d_partitions[0].p_fstype = 9; cd->disklabel.d_magic = DISKMAGIC; @@ -990,6 +1110,28 @@ unsigned char unit; return(ESUCCESS); } +/*******************************************************\ +* Get scsi driver to send a "are you ready" command * +\*******************************************************/ +cd_test_ready(unit,flags) +int unit,flags; +{ + struct scsi_test_unit_ready scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = TEST_UNIT_READY; + + return (cd_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 100000, + NULL, + flags)); +} + + /*******************************************************\ * Find out form the device what it's capacity is * \*******************************************************/ @@ -1017,9 +1159,10 @@ cd_size(unit, flags) &rdcap, sizeof(rdcap), 2000, + NULL, flags) != 0) { - printf("could not get size of unit %d\n", unit); + printf("cd%d: could not get size\n", unit); return(0); } else { size = rdcap.addr_0 + 1 ; @@ -1031,37 +1174,14 @@ cd_size(unit, flags) blksize += rdcap.length_2 << 16; blksize += rdcap.length_3 << 24; } +#ifdef CDDEBUG if(cd_debug)printf("cd%d: %d %d byte blocks\n",unit,size,blksize); - cd_data[unit].params.disksize = size; - cd_data[unit].params.blksize = blksize; +#endif /*CDDEBUG*/ + cd_driver->cd_data[unit]->params.disksize = size; + cd_driver->cd_data[unit]->params.blksize = blksize; return(size); } -/*******************************************************\ -* Check with the device that it is ok, (via scsi driver)* -\*******************************************************/ -cd_req_sense(unit, flags) -{ - struct scsi_sense_data sense_data; - struct scsi_sense scsi_cmd; - - bzero(&scsi_cmd, sizeof(scsi_cmd)); - scsi_cmd.op_code = REQUEST_SENSE; - scsi_cmd.length = sizeof(sense_data); - - if (cd_scsi_cmd(unit, - &scsi_cmd, - sizeof(scsi_cmd), - &sense_data, - sizeof(sense_data), - 2000, - flags) != 0) - { - return(ENXIO); - } - else - return(0); -} /*******************************************************\ * Get the requested page into the buffer given * @@ -1077,7 +1197,7 @@ int page; bzero(&scsi_cmd, sizeof(scsi_cmd)); bzero(data,sizeof(*data)); scsi_cmd.op_code = MODE_SENSE; - scsi_cmd.page_code = page; + scsi_cmd.page = page; scsi_cmd.length = sizeof(*data) & 0xff; retval = cd_scsi_cmd(unit, &scsi_cmd, @@ -1085,6 +1205,7 @@ int page; data, sizeof(*data), 20000, /* should be immed */ + NULL, 0); return (retval); } @@ -1099,7 +1220,7 @@ struct cd_mode_data *data; bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = MODE_SELECT; - scsi_cmd.pf = 1; + scsi_cmd.byte2 |= SMS_PF; scsi_cmd.length = sizeof(*data) & 0xff; data->header.data_length = 0; /*show_mem(data,sizeof(*data));/**/ @@ -1109,6 +1230,7 @@ struct cd_mode_data *data; data, sizeof(*data), 20000, /* should be immed */ + NULL, 0) ); } @@ -1135,6 +1257,7 @@ int unit,blk,len; 0, 0, 200000, /* should be immed */ + NULL, 0); return(retval); } @@ -1163,6 +1286,7 @@ int unit,blk,len; 0, 0, 20000, /* should be immed */ + NULL, 0); return(retval); } @@ -1187,10 +1311,37 @@ int unit,strack,sindex,etrack,eindex; 0, 0, 20000, /* should be immed */ + NULL, 0); return(retval); } /*******************************************************\ +* Get scsi driver to send a "play msf" command * +\*******************************************************/ +cd_play_msf(unit,startm,starts,startf,endm,ends,endf) +int unit,startm,starts,startf,endm,ends,endf; +{ + struct scsi_play_msf scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = PLAY_MSF; + scsi_cmd.start_m=startm; + scsi_cmd.start_s=starts; + scsi_cmd.start_f=startf; + scsi_cmd.end_m=endm; + scsi_cmd.end_s=ends; + scsi_cmd.end_f=endf; + + return (cd_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 2000, + NULL, + 0)); +} +/*******************************************************\ * Get scsi driver to send a "start up" command * \*******************************************************/ cd_pause(unit,go) @@ -1208,15 +1359,23 @@ int unit,go; 0, 0, 2000, + NULL, 0)); } /*******************************************************\ -* Get scsi driver to send a "start up" command * +* Get scsi driver to send a "RESET" command * \*******************************************************/ cd_reset(unit) int unit; { - return(cd_scsi_cmd(unit,0,0,0,0,2000,SCSI_RESET)); + return(cd_scsi_cmd(unit, + 0, + 0, + 0, + 0, + 2000, + NULL, + SCSI_RESET)); } /*******************************************************\ * Get scsi driver to send a "start up" command * @@ -1224,15 +1383,18 @@ int unit; cd_start_unit(unit,part,type) { struct scsi_start_stop scsi_cmd; + struct cd_data *cd = cd_driver->cd_data[unit]; - if(type==CD_EJECT && (cd_data[unit].openparts&~(1<openparts == 0)*/)/* trouble is WE have it open *//*XXX*/ + { + cd_prevent_unit(unit,PR_ALLOW,0); } bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = START_STOP; - scsi_cmd.start = type==CD_START?1:0; - scsi_cmd.loej = type==CD_EJECT?1:0; + scsi_cmd.how |= (type==CD_START)?SSS_START:0; + scsi_cmd.how |= (type==CD_EJECT)?SSS_LOEJ:0; if (cd_scsi_cmd(unit, &scsi_cmd, @@ -1240,6 +1402,7 @@ cd_start_unit(unit,part,type) 0, 0, 2000, + NULL, 0) != 0) { return(ENXIO); } else @@ -1252,21 +1415,25 @@ cd_prevent_unit(unit,type,flags) int unit,type,flags; { struct scsi_prevent scsi_cmd; + struct cd_data *cd = cd_driver->cd_data[unit]; - if(type==CD_EJECT || type==PR_PREVENT || cd_data[unit].openparts == 0 ) { + if(type==PR_PREVENT + || ( type==PR_ALLOW && cd->openparts == 0 ))/*XXX*/ + { bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = PREVENT_ALLOW; - scsi_cmd.prevent=type==CD_EJECT?PR_ALLOW:type; + scsi_cmd.how = type; if (cd_scsi_cmd(unit, &scsi_cmd, sizeof(struct scsi_prevent), 0, 0, 5000, + NULL, 0) != 0) { if(!(flags & SCSI_SILENT)) - printf("cannot prevent/allow on cd%d\n", unit); + printf("cd%d: cannot prevent/allow\n", unit); return(0); } } @@ -1288,8 +1455,8 @@ struct cd_sub_channel_info *data; scsi_cmd.op_code=READ_SUBCHANNEL; if(mode==CD_MSF_FORMAT) - scsi_cmd.msf=1; - scsi_cmd.subQ=1; + scsi_cmd.byte2 |= CD_MSF; + scsi_cmd.byte3=SRS_SUBQ; scsi_cmd.subchan_format=format; scsi_cmd.track=track; scsi_cmd.data_len[0]=(len)>>8; @@ -1300,6 +1467,7 @@ struct cd_sub_channel_info *data; data, len, 5000, + NULL, 0); } @@ -1322,7 +1490,7 @@ struct cd_toc_entry *data; scsi_cmd.op_code=READ_TOC; if(mode==CD_MSF_FORMAT) - scsi_cmd.msf=1; + scsi_cmd.byte2 |= CD_MSF; scsi_cmd.from_track=start; scsi_cmd.data_len[0]=(ntoc)>>8; scsi_cmd.data_len[1]=(ntoc)&0xff; @@ -1332,6 +1500,7 @@ struct cd_toc_entry *data; data, len, 5000, + NULL, 0); } @@ -1346,7 +1515,7 @@ struct cd_toc_entry *data; int cd_get_parms(unit, flags) { - struct cd_data *cd = cd_data + unit; + struct cd_data *cd = cd_driver->cd_data[unit]; /*******************************************************\ * First check if we have it all loaded * @@ -1379,10 +1548,12 @@ dev_t dev; unit = UNIT(dev); part = PARTITION(dev); +#ifdef CDDEBUG if(scsi_debug & TRACEOPENS) - printf("closing cd%d part %d\n",unit,part); - cd_data[unit].partflags[part] &= ~CDOPEN; - cd_data[unit].openparts &= ~(1 << part); + printf("cd%d: closing part %d\n",unit,part); +#endif + cd_driver->cd_data[unit]->partflags[part] &= ~CDOPEN; + cd_driver->cd_data[unit]->openparts &= ~(1 << part); cd_prevent_unit(unit,PR_ALLOW,SCSI_SILENT); return(0); } @@ -1396,7 +1567,7 @@ dev_t dev; * Also tell it where to read/write the data, and how * * long the data is supposed to be * \*******************************************************/ -int cd_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags) +int cd_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,bp,flags) int unit,flags; struct scsi_generic *scsi_cmd; @@ -1404,108 +1575,138 @@ int cmdlen; int timeout; u_char *data_addr; int datalen; +struct buf *bp; { struct scsi_xfer *xs; int retval; int s; - struct cd_data *cd = cd_data + unit; + struct cd_data *cd = cd_driver->cd_data[unit]; +#ifdef CDDEBUG if(scsi_debug & PRINTROUTINES) printf("\ncd_scsi_cmd%d ",unit); - if(cd->sc_sw) /* If we have a scsi driver */ +#endif /*CDDEBUG*/ +#ifdef PARANOID + if(!(cd->sc_sw)) /* If we have a scsi driver */ { - xs = cd_get_xs(unit,flags); /* should wait unless booting */ - if(!xs) - { - printf("cd_scsi_cmd%d: controller busy" - " (this should never happen)\n",unit); - return(EBUSY); - } - xs->flags |= INUSE; - /*******************************************************\ - * Fill out the scsi_xfer structure * - \*******************************************************/ - xs->flags |= flags; - xs->adapter = cd->ctlr; - xs->targ = cd->targ; - xs->lu = cd->lu; - xs->retries = CD_RETRIES; - xs->timeout = timeout; - xs->cmd = scsi_cmd; - xs->cmdlen = cmdlen; - xs->data = data_addr; - xs->datalen = datalen; - xs->resid = datalen; - xs->when_done = (flags & SCSI_NOMASK) - ?(int (*)())0 - :cd_done; - xs->done_arg = unit; - xs->done_arg2 = (int)xs; -retry: xs->error = XS_NOERROR; - xs->bp = 0; - retval = (*(cd->sc_sw->scsi_cmd))(xs); - switch(retval) - { - case SUCCESSFULLY_QUEUED: - s = splbio(); - while(!(xs->flags & ITSDONE)) - sleep(xs,PRIBIO+1); - splx(s); - - case HAD_ERROR: - /*printf("err = %d ",xs->error);*/ - switch(xs->error) - { - case XS_NOERROR: - retval = ESUCCESS; - break; - case XS_SENSE: - retval = (cd_interpret_sense(unit,xs)); - break; - case XS_DRIVER_STUFFUP: - retval = EIO; - break; + /* !? how'd we GET here? */ + panic("attempt to run bad cd device"); + } +#endif /*PARANOID*/ + xs = cd_get_xs(unit,flags); /* should wait unless booting */ + if(!xs) + { + printf("cd%d: scsi_cmd controller busy" + " (this should never happen)\n",unit); + return(EBUSY); + } + xs->flags |= INUSE; + /*******************************************************\ + * Fill out the scsi_xfer structure * + \*******************************************************/ + xs->flags |= flags; + xs->adapter = cd->ctlr; + xs->targ = cd->targ; + xs->lu = cd->lu; + xs->retries = CD_RETRIES; + xs->timeout = timeout; + xs->cmd = scsi_cmd; + xs->cmdlen = cmdlen; + xs->data = data_addr; + xs->datalen = datalen; + xs->resid = datalen; + xs->when_done = (flags & SCSI_NOMASK) + ?(int (*)())0 + :cd_done; + xs->done_arg = unit; + xs->done_arg2 = (int)xs; + xs->bp = bp; +retry: xs->error = XS_NOERROR; + /*******************************************************\ + * Do the transfer. If we are polling we will return: * + * COMPLETE, Was poll, and cd_done has been called * + * HAD_ERROR, Was poll and an error was encountered * + * TRY_AGAIN_LATER, Adapter short resources, try again * + * * + * if under full steam (interrupts) it will return: * + * SUCCESSFULLY_QUEUED, will do a wakeup when complete * + * HAD_ERROR, had an erro before it could queue * + * TRY_AGAIN_LATER, (as for polling) * + * After the wakeup, we must still check if it succeeded * + * * + * If we have a bp however, all the error proccessing * + * and the buffer code both expect us to return straight * + * to them, so as soon as the command is queued, return * + \*******************************************************/ + retval = (*(cd->sc_sw->scsi_cmd))(xs); + if(bp) return retval; /* will sleep (or not) elsewhere */ + /*******************************************************\ + * Only here for non I/O cmds. It's cheaper to process * + * the error status here than at interrupt time so * + * sd_done will have done nothing except wake us up. * + \*******************************************************/ + switch(retval) + { + case SUCCESSFULLY_QUEUED: + s = splbio(); + while(!(xs->flags & ITSDONE)) + sleep(xs,PRIBIO+1); + splx(s); + /* Fall through to check the result */ - case XS_BUSY: - case XS_TIMEOUT: - if(xs->retries-- ) - { - xs->flags &= ~ITSDONE; - goto retry; - } - retval = EIO; - break; - default: - retval = EIO; - printf("cd%d: unknown error category from scsi driver\n" - ,unit); - } - break; - case COMPLETE: + case HAD_ERROR: + switch(xs->error) + { + case XS_NOERROR: /* usually this one */ retval = ESUCCESS; break; - case TRY_AGAIN_LATER: + + case XS_SENSE: + retval = (cd_interpret_sense(unit,xs)); + break; + case XS_BUSY: + /* should sleep here 1 sec */ + /* fall through */ + case XS_TIMEOUT: if(xs->retries-- ) { - if(tsleep( 0,PRIBIO + 2,"retry",hz * 2)) - { - xs->flags &= ~ITSDONE; - goto retry; - } + xs->flags &= ~ITSDONE; + goto retry; } + /* fall through */ + case XS_DRIVER_STUFFUP: retval = EIO; break; default: retval = EIO; + printf("cd%d: unknown error category from scsi driver\n" + ,unit); + } + break; + case COMPLETE: + retval = ESUCCESS; + break; + + case TRY_AGAIN_LATER: + if(xs->retries-- ) + { + if(tsleep( 0,PRIBIO + 2,"retry",hz * 2)) + { + xs->flags &= ~ITSDONE; + goto retry; + } } - cd_free_xs(unit,xs,flags); - cdstart(unit); /* check if anything is waiting fr the xs */ - } - else - { - printf("cd%d: not set up\n",unit); - return(EINVAL); + /* fall through */ + default: + retval = EIO; } + + /*******************************************************\ + * we have finished doing the command, free the struct * + * and check if anyone else needs it * + \*******************************************************/ + cd_free_xs(unit,xs,flags); + cdstart(unit); /* check if anything is waiting for the xs */ return(retval); } /***************************************************************\ @@ -1520,6 +1721,18 @@ struct scsi_xfer *xs; struct scsi_sense_data *sense; int key; int silent; + int info; + struct cd_data *cd = cd_driver->cd_data[unit]; + + static char *error_mes[] = { "soft error (corrected)", + "not ready", "medium error", + "non-media hardware failure", "illegal request", + "unit attention", "readonly device", + "no data found", "vendor unique", + "copy aborted", "command aborted", + "search returned equal", "volume overflow", + "verify miscompare", "unknown error key" + }; /***************************************************************\ * If the flags say errs are ok, then always return ok. * @@ -1528,169 +1741,119 @@ struct scsi_xfer *xs; silent = (xs->flags & SCSI_SILENT); sense = &(xs->sense); - switch(sense->error_class) +#ifdef CDDEBUG + if(cd_debug) { - case 7: + int count = 0; + printf("code%x valid%x\n" + ,sense->error_code & SSD_ERRCODE + ,sense->error_code & SSD_ERRCODE_VALID ? 1 : 0); + printf("seg%x key%x ili%x eom%x fmark%x\n" + ,sense->ext.extended.segment + ,sense->ext.extended.flags & SSD_KEY + ,sense->ext.extended.flags & SSD_ILI ? 1 : 0 + ,sense->ext.extended.flags & SSD_EOM ? 1 : 0 + ,sense->ext.extended.flags & SSD_FILEMARK ? 1 : 0); + printf("info: %x %x %x %x followed by %d extra bytes\n" + ,sense->ext.extended.info[0] + ,sense->ext.extended.info[1] + ,sense->ext.extended.info[2] + ,sense->ext.extended.info[3] + ,sense->ext.extended.extra_len); + printf("extra: "); + while(count < sense->ext.extended.extra_len) { - key=sense->ext.extended.sense_key; - switch(key) + printf ("%x ",sense->ext.extended.extra_bytes[count++]); + } + printf("\n"); + } +#endif /*CDDEBUG*/ + switch(sense->error_code & SSD_ERRCODE) + { + /***************************************************************\ + * If it's code 70, use the extended stuff and interpret the key * + \***************************************************************/ + case 0x71:/* delayed error */ + printf("cd%d: DELAYED ERROR, key = 0x%x\n",unit,key); + case 0x70: + if(sense->error_code & SSD_ERRCODE_VALID) { - case 0x0: - return(ESUCCESS); - case 0x1: - if(!silent) + info = ntohl(*((long *)sense->ext.extended.info)); + } + else + { + info = 0; + } + + key=sense->ext.extended.flags & SSD_KEY; + + if (!silent) + { + printf("cd%d: %s", unit, error_mes[key - 1]); + if(sense->error_code & SSD_ERRCODE_VALID) { - printf("cd%d: soft error(corrected) ", unit); - if(sense->valid) + switch (key) { - printf("block no. %d (decimal)", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); + case 0x2: /* NOT READY */ + case 0x5: /* ILLEGAL REQUEST */ + case 0x6: /* UNIT ATTENTION */ + case 0x7: /* DATA PROTECT */ + break; + case 0x8: /* BLANK CHECK */ + printf(", requested size: %d (decimal)", + info); + break; + default: + printf(", info = %d (decimal)", info); } - printf("\n"); } + printf("\n"); + } + + switch (key) + { + case 0x0: /* NO SENSE */ + case 0x1: /* RECOVERED ERROR */ + xs->resid = 0; + case 0xc: /* EQUAL */ return(ESUCCESS); - case 0x2: - if(!silent)printf("cd%d: not ready\n ", - unit); + case 0x2: /* NOT READY */ + cd->flags &= ~(CDVALID | CDHAVELABEL); return(ENODEV); - case 0x3: - if(!silent) - { - printf("cd%d: medium error ", unit); - if(sense->valid) - { - printf("block no. %d (decimal)", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); - } - printf("\n"); - } - return(EIO); - case 0x4: - if(!silent)printf("cd%d: non-media hardware failure\n ", - unit); - return(EIO); - case 0x5: - if(!silent)printf("cd%d: illegal request\n ", - unit); + case 0x5: /* ILLEGAL REQUEST */ return(EINVAL); - case 0x6: - if(!silent)printf("cd%d: Unit attention.\n ", unit); - if (cd_data[unit].openparts) - cd_data[unit].flags &= ~(CDVALID | CDHAVELABEL); + case 0x6: /* UNIT ATTENTION */ + cd->flags &= ~(CDVALID | CDHAVELABEL); + if (cd->openparts) { return(EIO); } return(ESUCCESS); - case 0x7: - if(!silent) - { - printf("cd%d: attempted protection violation ", - unit); - if(sense->valid) - { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); - } - printf("\n"); - } + case 0x7: /* DATA PROTECT */ return(EACCES); - case 0x8: - if(!silent) - { - printf("cd%d: block wrong state (worm)\n ", - unit); - if(sense->valid) - { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); - } - printf("\n"); - } - return(EIO); - case 0x9: - if(!silent)printf("cd%d: vendor unique\n", - unit); - return(EIO); - case 0xa: - if(!silent)printf("cd%d: copy aborted\n ", - unit); - return(EIO); - case 0xb: - if(!silent)printf("cd%d: command aborted\n ", - unit); - return(EIO); - case 0xc: - if(!silent) - { - printf("cd%d: search returned\n ", - unit); - if(sense->valid) - { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); - } - printf("\n"); - } - return(ESUCCESS); - case 0xd: - if(!silent)printf("cd%d: volume overflow\n ", - unit); + case 0xd: /* VOLUME OVERFLOW */ return(ENOSPC); - case 0xe: - if(!silent) - { - printf("cd%d: verify miscompare\n ", - unit); - if(sense->valid) - { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); - } - printf("\n"); - } - return(EIO); - case 0xf: - if(!silent)printf("cd%d: unknown error key\n ", - unit); + case 0x8: /* BLANK CHECK */ + return(ESUCCESS); + default: return(EIO); } - break; - } - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: + /*******************************\ + * Not code 70, just report it * + \*******************************/ + default: + if(!silent) { - if(!silent)printf("cd%d: error class %d code %d\n", - unit, - sense->error_class, - sense->error_code); - if(sense->valid) - if(!silent)printf("block no. %d (decimal)\n", - (sense->ext.unextended.blockhi <<16) - + (sense->ext.unextended.blockmed <<8) - + (sense->ext.unextended.blocklow )); + printf("cd%d: error code %d", unit, + sense->error_code & SSD_ERRCODE); + if(sense->error_code & SSD_ERRCODE_VALID) + { + printf(" at block no. %d (decimal)", + (sense->ext.unextended.blockhi <<16) + + (sense->ext.unextended.blockmed <<8) + + (sense->ext.unextended.blocklow )); + } + printf("\n"); } return(EIO); } @@ -1705,6 +1868,7 @@ cdsize(dev_t dev) return (-1); } +#if 0 show_mem(address,num) unsigned char *address; int num; @@ -1719,4 +1883,4 @@ int num; } printf("\n------------------------------\n"); } - +#endif diff --git a/sys/scsi/ch.c b/sys/scsi/ch.c index 1e7469b66b..a76d522b3d 100644 --- a/sys/scsi/ch.c +++ b/sys/scsi/ch.c @@ -1,16 +1,7 @@ /* - */ -/* - * HISTORY - * - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- + * Written by Julian Elischer (julian@tfs.com) * - * 16 Feb 93 Julian Elischer ADDED for SCSI system - * + * $Id$ */ #include @@ -100,7 +91,7 @@ struct scsi_switch *scsi_switch; unit = next_ch_unit++; if( unit >= NCH) { - printf("Too many scsi changers..(%d > %d) reconfigure kernel",(unit + 1),NCH); + printf("Too many scsi changers..(%d > %d) reconfigure kernel\n",(unit + 1),NCH); return(0); } /*******************************************************\ @@ -118,13 +109,13 @@ struct scsi_switch *scsi_switch; \*******************************************************/ if((ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK /*| SCSI_SILENT*/))) { - printf(" ch%d: scsi changer, %d slot(s) %d drive(s) %d arm(s) %d i/e-slot(s) \n", + printf("ch%d: scsi changer, %d slot(s) %d drive(s) %d arm(s) %d i/e-slot(s)\n", unit, ch_data[unit].slots, ch_data[unit].drives, ch_data[unit].chms, ch_data[unit].imexs); stat=CH_KNOWN; } else { - printf(" ch%d: scsi changer :- offline\n", unit); + printf("ch%d: scsi changer :- offline\n", unit); stat=CH_OPEN; } ch_initialized[unit] = stat; @@ -151,7 +142,7 @@ chopen(dev) \*******************************************************/ if ( unit >= NCH ) { - printf("ch %d > %d\n",unit,NCH); + printf("ch%d: ch %d > %d\n",unit,unit,NCH); errcode = ENXIO; return(errcode); } @@ -160,7 +151,7 @@ chopen(dev) \*******************************************************/ if(ch_data[unit].flags & CH_OPEN) { - printf("CH%d already open\n",unit); + printf("ch%d: already open\n",unit); errcode = ENXIO; goto bad; } @@ -181,7 +172,7 @@ chopen(dev) } else { - printf(" ch%d: scsi changer :- offline\n", unit); + printf("ch%d: scsi changer :- offline\n", unit); return(ENXIO); } } @@ -203,7 +194,7 @@ chopen(dev) if(!(ch_test_ready(unit,0))) { - printf("ch%d not ready\n",unit); + printf("ch%d: not ready\n",unit); return(EIO); } @@ -370,7 +361,7 @@ char *data; bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = READ_ELEMENT_STATUS; - scsi_cmd.element_type_code=type; + scsi_cmd.byte2 = type; scsi_cmd.starting_element_addr[0]=(from>>8)&0xff; scsi_cmd.starting_element_addr[1]=from&0xff; scsi_cmd.number_of_elements[1]=1; @@ -529,8 +520,8 @@ int unit,flags; for(l=1;l>=0;l--) { bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = MODE_SENSE; - scsi_cmd.dbd = l; - scsi_cmd.page_code = 0x3f; /* All Pages */ + scsi_cmd.byte2 = SMS_DBD; + scsi_cmd.page = 0x3f; /* All Pages */ scsi_cmd.length = sizeof(scsi_sense); /*******************************************************\ * do the command, but we don't need the results * @@ -549,7 +540,7 @@ int unit,flags; } if (ch_info_valid[unit]!=CH_KNOWN) { if(!(flags & SCSI_SILENT)) - printf("could not mode sense for unit %d\n", unit); + printf("ch%d: could not mode sense\n", unit); return(FALSE); } l=scsi_sense[0]-3; @@ -713,7 +704,7 @@ retry: xs->error = XS_NOERROR; break; default: retval = EIO; - printf("st%d: unknown error category from scsi driver\n" + printf("ch%d: unknown error category from scsi driver\n" ,unit); break; } @@ -734,7 +725,7 @@ retry: xs->error = XS_NOERROR; } else { - printf("chd: not set up\n",unit); + printf("ch%d: not set up\n",unit); return(EINVAL); } return(retval); @@ -761,42 +752,41 @@ struct scsi_xfer *xs; * Get the sense fields and work out what CLASS * \***************************************************************/ sense = &(xs->sense); - switch(sense->error_class) + switch(sense->error_code & SSD_ERRCODE) { /***************************************************************\ * If it's class 7, use the extended stuff and interpret the key * \***************************************************************/ - case 7: + case 0x70: { - key=sense->ext.extended.sense_key; - if(sense->ext.extended.ili) + key=sense->ext.extended.flags & SSD_KEY; + if(sense->ext.extended.flags & SSD_ILI) if(!silent) { printf("length error "); } - if(sense->valid) + if(sense->error_code & SSD_ERRCODE_VALID) xs->resid = ntohl(*((long *)sense->ext.extended.info)); if(xs->bp) { xs->bp->b_flags |= B_ERROR; return(ESUCCESS); } - if(sense->ext.extended.eom) + if(sense->ext.extended.flags & SSD_EOM) if(!silent) printf("end of medium "); - if(sense->ext.extended.filemark) + if(sense->ext.extended.flags & SSD_FILEMARK) if(!silent) printf("filemark "); if(ch_debug) { - printf("code%x class%x valid%x\n" - ,sense->error_code - ,sense->error_class - ,sense->valid); + printf("code%x valid%x\n" + ,sense->error_code & SSD_ERRCODE + ,sense->error_code & SSD_ERRCODE_VALID); printf("seg%x key%x ili%x eom%x fmark%x\n" ,sense->ext.extended.segment - ,sense->ext.extended.sense_key - ,sense->ext.extended.ili - ,sense->ext.extended.eom - ,sense->ext.extended.filemark); + ,sense->ext.extended.flags & SSD_KEY + ,sense->ext.extended.flags & SSD_ILI + ,sense->ext.extended.flags & SSD_EOM + ,sense->ext.extended.flags & SSD_FILEMARK); printf("info: %x %x %x %x followed by %d extra bytes\n" ,sense->ext.extended.info[0] ,sense->ext.extended.info[1] @@ -829,59 +819,53 @@ struct scsi_xfer *xs; case 0x1: if(!silent) { - printf("st%d: soft error(corrected) ", unit); - if(sense->valid) + printf("ch%d: soft error(corrected)", unit); + if(sense->error_code & SSD_ERRCODE_VALID) { - printf("block no. %d (decimal)\n", + printf(" block no. %d (decimal)", (sense->ext.extended.info[0] <<24)| (sense->ext.extended.info[1] <<16)| (sense->ext.extended.info[2] <<8)| (sense->ext.extended.info[3] )); } - else - { - printf("\n"); - } + printf("\n"); } return(ESUCCESS); case 0x2: - if(!silent) printf("st%d: not ready\n ", unit); + if(!silent) printf("ch%d: not ready\n", unit); ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)| - sense->ext.extended.info[13] ; + sense->ext.extended.info[13] ; return(ENODEV); case 0x3: if(!silent) { - printf("st%d: medium error ", unit); - if(sense->valid) + printf("ch%d: medium error", unit); + if(sense->error_code & SSD_ERRCODE_VALID) { - printf("block no. %d (decimal)\n", + printf(" block no. %d (decimal)", (sense->ext.extended.info[0] <<24)| (sense->ext.extended.info[1] <<16)| (sense->ext.extended.info[2] <<8)| (sense->ext.extended.info[3] )); } - else - { - printf("\n"); - } + printf("\n"); } return(EIO); case 0x4: - if(!silent) printf("st%d: non-media hardware failure\n ", + if(!silent) printf("ch%d: non-media hardware failure\n", unit); ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)| - sense->ext.extended.info[13] ; + sense->ext.extended.info[13] ; return(EIO); case 0x5: - if(!silent) printf("st%d: illegal request\n ", unit); + if(!silent) printf("ch%d: illegal request\n", unit); ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)| - sense->ext.extended.info[13] ; + sense->ext.extended.info[13] ; return(EINVAL); case 0x6: - if(!silent) printf("st%d: Unit attention.\n ", unit); + if(!silent) printf("ch%d: Unit attention\n", unit); ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)| - sense->ext.extended.info[13] ; + sense->ext.extended.info[13] ; ch_info_valid[unit] = FALSE; if (ch_data[unit].flags & CH_OPEN) /* TEMP!!!! */ return(EIO); @@ -890,98 +874,81 @@ struct scsi_xfer *xs; case 0x7: if(!silent) { - printf("st%d: attempted protection violation " + printf("ch%d: attempted protection violation" , unit); - if(sense->valid) + if(sense->error_code & SSD_ERRCODE_VALID) { - printf("block no. %d (decimal)\n", + printf(" block no. %d (decimal)\n", (sense->ext.extended.info[0] <<24)| (sense->ext.extended.info[1] <<16)| (sense->ext.extended.info[2] <<8)| (sense->ext.extended.info[3] )); } - else - { - printf("\n"); - } + printf("\n"); } return(EACCES); case 0x8: if(!silent) { - printf("st%d: block wrong state (worm)\n " + printf("ch%d: block wrong state (worm)" , unit); - if(sense->valid) + if(sense->error_code & SSD_ERRCODE_VALID) { - printf("block no. %d (decimal)\n", + printf(" block no. %d (decimal)", (sense->ext.extended.info[0] <<24)| (sense->ext.extended.info[1] <<16)| (sense->ext.extended.info[2] <<8)| (sense->ext.extended.info[3] )); } - else - { - printf("\n"); - } + printf("\n"); } return(EIO); case 0x9: - if(!silent) printf("st%d: vendor unique\n", - unit); + if(!silent) printf("ch%d: vendor unique\n", unit); return(EIO); case 0xa: - if(!silent) printf("st%d: copy aborted\n ", - unit); + if(!silent) printf("ch%d: copy aborted\n", unit); return(EIO); case 0xb: - if(!silent) printf("st%d: command aborted\n ", - unit); + if(!silent) printf("ch%d: command aborted\n", unit); ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)| - sense->ext.extended.info[13] ; + sense->ext.extended.info[13] ; return(EIO); case 0xc: if(!silent) { - printf("st%d: search returned\n ", unit); - if(sense->valid) + printf("ch%d: search returned", unit); + if(sense->error_code & SSD_ERRCODE_VALID) { - printf("block no. %d (decimal)\n", + printf(" block no. %d (decimal)", (sense->ext.extended.info[0] <<24)| (sense->ext.extended.info[1] <<16)| (sense->ext.extended.info[2] <<8)| (sense->ext.extended.info[3] )); } - else - { - printf("\n"); - } + printf("\n"); } return(ESUCCESS); case 0xd: - if(!silent) printf("st%d: volume overflow\n ", - unit); + if(!silent) printf("ch%d: volume overflow\n", unit); return(ENOSPC); case 0xe: if(!silent) { - printf("st%d: verify miscompare\n ", unit); - if(sense->valid) + printf("ch%d: verify miscompare", unit); + if(sense->error_code & SSD_ERRCODE_VALID) { - printf("block no. %d (decimal)\n", + printf(" block no. %d (decimal)", (sense->ext.extended.info[0] <<24)| (sense->ext.extended.info[1] <<16)| (sense->ext.extended.info[2] <<8)| (sense->ext.extended.info[3] )); } - else - { - printf("\n"); - } + printf("\n"); } return(EIO); case 0xf: - if(!silent) printf("st%d: unknown error key\n ", - unit); + if(!silent) printf("ch%d: unknown error key\n", unit); return(EIO); } break; @@ -989,23 +956,22 @@ struct scsi_xfer *xs; /***************************************************************\ * If it's NOT class 7, just report it. * \***************************************************************/ - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: + default: { - if(!silent) printf("st%d: error class %d code %d\n", - unit, - sense->error_class, - sense->error_code); - if(sense->valid) - if(!silent) printf("block no. %d (decimal)\n", - (sense->ext.unextended.blockhi <<16), - + (sense->ext.unextended.blockmed <<8), - + (sense->ext.unextended.blocklow )); + if(!silent) + { + printf("ch%d: error code %d", + unit, + sense->error_code & SSD_ERRCODE); + if(sense->error_code & SSD_ERRCODE_VALID) + { + printf(" block no. %d (decimal)", + (sense->ext.unextended.blockhi <<16), + + (sense->ext.unextended.blockmed <<8), + + (sense->ext.unextended.blocklow )); + } + printf("\n"); + } } return(EIO); } diff --git a/sys/scsi/scsi_all.h b/sys/scsi/scsi_all.h index 430c70c3d6..3f1c348ebe 100644 --- a/sys/scsi/scsi_all.h +++ b/sys/scsi/scsi_all.h @@ -1,27 +1,7 @@ -/* - * HISTORY - * $Log: scsi_all.h,v $ - * Revision 1.2 1992/11/20 23:07:13 julian - * add a definition for device type T_NODEVICE - * - * Revision 1.1 1992/09/26 22:14:02 julian - * Initial revision - * - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- - * - * 16 Feb 93 Julian Elischer ADDED for SCSI system - * - */ - /* * SCSI general interface description */ - /* * Largely written by Julian Elischer (julian@tfs.com) * for TRW Financial Systems. @@ -36,15 +16,23 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * + * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * + * $Id$ */ /* - * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * SCSI command format */ /* - * SCSI command format + * Define dome bits that are in ALL (or a lot of) scsi commands */ +#define SCSI_CTL_LINK 0x01 +#define SCSI_CTL_FLAG 0x02 +#define SCSI_CTL_VENDOR 0xC0 +#define SCSI_CMD_LUN 0xA0 /*these two should not be needed*/ +#define SCSI_CMD_LUN_SHIFT 5 /* LUN in the cmd is no longer SCSI */ struct scsi_generic @@ -56,152 +44,117 @@ struct scsi_generic struct scsi_test_unit_ready { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char unused[3]; - u_char link:1; - u_char flag:4; - u_char :3; + u_char control; }; struct scsi_send_diag { u_char op_code; - u_char uol:1; - u_char dol:1; - u_char selftest:1; - u_char :1; - u_char pf:1; - u_char lun:3; + u_char byte2; +#define SSD_UOL 0x01 +#define SSD_DOL 0x02 +#define SSD_SELFTEST 0x04 +#define SSD_PF 0x10 u_char unused[1]; u_char paramlen[2]; - u_char link:1; - u_char flag:4; - u_char :3; + u_char control; }; struct scsi_sense { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char unused[2]; u_char length; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_inquiry { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char unused[2]; u_char length; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_mode_sense { u_char op_code; - u_char :3; - u_char dbd:1; - u_char rsvd:1; - u_char lun:3; - u_char page_code:6; - u_char page_ctrl:2; + u_char byte2; +#define SMS_DBD 0x08 + u_char page; +#define SMS_PAGE_CODE 0x3F +#define SMS_PAGE_CTRL 0xC0 +#define SMS_PAGE_CTRL_CURRENT 0x00 +#define SMS_PAGE_CTRL_CHANGEABLE 0x40 +#define SMS_PAGE_CTRL_DEFAULT 0x80 +#define SMS_PAGE_CTRL_SAVED 0xC0 u_char unused; u_char length; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_mode_sense_big { u_char op_code; - u_char :3; - u_char dbd:1; - u_char rsvd:1; - u_char lun:3; - u_char page_code:6; - u_char page_ctrl:2; + u_char byte2; /* same bits as small version */ + u_char page; /* same bits as small version */ u_char unused[4]; u_char length[2]; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_mode_select { u_char op_code; - u_char sp:1; - u_char :3; - u_char pf:1; - u_char lun:3; + u_char byte2; +#define SMS_SP 0x01 +#define SMS_PF 0x10 u_char unused[2]; u_char length; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_mode_select_big { u_char op_code; - u_char sp:1; - u_char :3; - u_char pf:1; - u_char lun:3; + u_char byte2; /* same bits as small version */ u_char unused[5]; u_char length[2]; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_reserve { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char unused[2]; u_char length; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_release { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char unused[2]; u_char length; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_prevent { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char unused[2]; - u_char prevent:1; - u_char :7; - u_char link:1; - u_char flag:1; - u_char :6; + u_char how; + u_char control; }; -#define PR_PREVENT 1 -#define PR_ALLOW 0 +#define PR_PREVENT 0x01 +#define PR_ALLOW 0x00 /* * Opcodes @@ -244,19 +197,31 @@ struct scsi_prevent struct scsi_inquiry_data { - u_char device_type:5; - u_char device_qualifier:3; - u_char dev_qual2:7; - u_char removable:1; - u_char ansii_version:3; - u_char :5; + u_char device; +#define SID_TYPE 0x1F +#define SID_QUAL 0xE0 +#define SID_QUAL_LU_OK 0x00 +#define SID_QUAL_LU_OFFLINE 0x20 +#define SID_QUAL_RSVD 0x40 +#define SID_QUAL_BAD_LU 0x60 + u_char dev_qual2; +#define SID_QUAL2 0x7F +#define SID_REMOVABLE 0x80 + u_char version; +#define SID_ANSII 0x07 +#define SID_ECMA 0x38 +#define SID_ISO 0xC0 u_char response_format; u_char additional_length; u_char unused[2]; - u_char :3; - u_char can_link:1; - u_char can_sync:1; - u_char :3; + u_char flags; +#define SID_SftRe 0x01 +#define SID_CmdQue 0x02 +#define SID_Linked 0x08 +#define SID_Sync 0x10 +#define SID_WBus16 0x20 +#define SID_WBus32 0x40 +#define SID_RelAdr 0x80 char vendor[8]; char product[16]; char revision[4]; @@ -266,37 +231,22 @@ struct scsi_inquiry_data struct scsi_sense_data { - u_char error_code:4; - u_char error_class:3; - u_char valid:1; + u_char error_code; /* same bits as new version */ union { struct { - u_char blockhi:5; - u_char vendor:3; + u_char blockhi; u_char blockmed; u_char blocklow; } unextended; struct { u_char segment; - u_char sense_key:4; - u_char :1; - u_char ili:1; - u_char eom:1; - u_char filemark:1; + u_char flags; /* same bits as new version */ u_char info[4]; u_char extra_len; /* allocate enough room to hold new stuff - u_char cmd_spec_info[4]; - u_char add_sense_code; - u_char add_sense_code_qual; - u_char fru; - u_char sense_key_spec_1:7; - u_char sksv:1; - u_char sense_key_spec_2; - u_char sense_key_spec_3; ( by increasing 16 to 26 below) */ u_char extra_bytes[26]; } extended; @@ -304,33 +254,33 @@ struct scsi_sense_data }; struct scsi_sense_data_new { - u_char error_code:7; - u_char valid:1; + u_char error_code; +#define SSD_ERRCODE 0x7F +#define SSD_ERRCODE_VALID 0x80 union { - struct + struct /* this is depreciated, the standard says "DON'T"*/ { - u_char blockhi:5; - u_char vendor:3; + u_char blockhi; u_char blockmed; u_char blocklow; } unextended; struct { u_char segment; - u_char sense_key:4; - u_char :1; - u_char ili:1; - u_char eom:1; - u_char filemark:1; + u_char flags; +#define SSD_KEY 0x0F +#define SSD_ILI 0x20 +#define SSD_EOM 0x40 +#define SSD_FILEMARK 0x80 u_char info[4]; u_char extra_len; u_char cmd_spec_info[4]; u_char add_sense_code; u_char add_sense_code_qual; u_char fru; - u_char sense_key_spec_1:7; - u_char sksv:1; + u_char sense_key_spec_1; +#define SSD_SCS_VALID 0x80 u_char sense_key_spec_2; u_char sense_key_spec_3; u_char extra_bytes[16]; diff --git a/sys/scsi/scsi_cd.h b/sys/scsi/scsi_cd.h index c35bdff7c0..eb9bfac8fc 100644 --- a/sys/scsi/scsi_cd.h +++ b/sys/scsi/scsi_cd.h @@ -12,20 +12,16 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * + * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- - * - * 16 Feb 93 Julian Elischer ADDED for SCSI system + * $Id$ */ - - /* - * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * Define two bits always in the same place in byte 2 (flag byte) */ +#define CD_RELADDR 0x01 +#define CD_MSF 0x02 /* * SCSI command format @@ -34,29 +30,22 @@ struct scsi_read_capacity_cd { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char addr_3; /* Most Significant */ u_char addr_2; u_char addr_1; u_char addr_0; /* Least Significant */ u_char unused[3]; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_pause { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char unused[6]; - u_char resume:1; - u_char :7; - u_char link:1; - u_char flag:1; - u_char :6; + u_char resume; + u_char control; }; #define PA_PAUSE 1 #define PA_RESUME 0 @@ -64,8 +53,7 @@ struct scsi_pause struct scsi_play_msf { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char unused; u_char start_m; u_char start_s; @@ -73,132 +61,96 @@ struct scsi_play_msf u_char end_m; u_char end_s; u_char end_f; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_play_track { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char unused[2]; u_char start_track; u_char start_index; u_char unused1; u_char end_track; u_char end_index; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_play { u_char op_code; - u_char reladdr:1; - u_char :4; - u_char lun:3; + u_char byte2; u_char blk_addr[4]; u_char unused; u_char xfer_len[2]; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_play_big { u_char op_code; - u_char reladdr:1; - u_char :4; - u_char lun:3; + u_char byte2; /* same as above */ u_char blk_addr[4]; u_char xfer_len[4]; u_char unused; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_play_rel_big { u_char op_code; - u_char reladdr:1; - u_char :4; - u_char lun:3; + u_char byte2; /* same as above */ u_char blk_addr[4]; u_char xfer_len[4]; u_char track; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_read_header { u_char op_code; - u_char :1; - u_char msf:1; - u_char :3; - u_char lun:3; + u_char byte2; u_char blk_addr[4]; u_char unused; u_char data_len[2]; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_read_subchannel { u_char op_code; - u_char :1; - u_char msf:1; - u_char :3; - u_char lun:3; - u_char :6; - u_char subQ:1; - u_char :1; + u_char byte2; + u_char byte3; +#define SRS_SUBQ 0x40 u_char subchan_format; u_char unused[2]; u_char track; u_char data_len[2]; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_read_toc { u_char op_code; - u_char :1; - u_char msf:1; - u_char :3; - u_char lun:3; + u_char byte2; u_char unused[4]; u_char from_track; u_char data_len[2]; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; ; struct scsi_read_cd_capacity { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char addr_3; /* Most Significant */ u_char addr_2; u_char addr_1; u_char addr_0; /* Least Significant */ u_char unused[3]; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; /* @@ -218,26 +170,6 @@ struct scsi_read_cd_capacity #define PLAY_TRACK_REL_BIG 0xa9 /* cdrom play track/index mode */ -struct cd_inquiry_data /* in case there is some special info */ -{ - u_char device_type:5; - u_char device_qualifier:3; - u_char dev_qual2:7; - u_char removable:1; - u_char ansii_version:3; - u_char :5; - u_char response_format; - u_char additional_length; - u_char unused[2]; - u_char :3; - u_char can_link:1; - u_char can_sync:1; - u_char :3; - char vendor[8]; - char product[16]; - char revision[4]; - u_char extra[8]; -}; struct scsi_read_cd_cap_data { @@ -253,32 +185,31 @@ struct scsi_read_cd_cap_data union cd_pages { -#define AUDIO_PAGE 0x0e struct audio_page { - u_char page_code:6; - u_char :1; - u_char ps:1; + u_char page_code; +#define CD_PAGE_CODE 0x3F +#define AUDIO_PAGE 0x0e +#define CD_PAGE_PS 0x80 u_char param_len; - u_char :1; - u_char sotc:1; - u_char immed:1; - u_char :5; + u_char flags; +#define CD_PA_SOTC 0x02 +#define CD_PA_IMMED 0x04 u_char unused[2]; - u_char format_lba:4; - u_char :3; - u_char apr_valid:1; + u_char format_lba; +#define CD_PA_FORMAT_LBA 0x0F +#define CD_PA_APR_VALID 0x80 u_char lb_per_sec[2]; struct port_control { - u_char channels:4; + u_char channels; +#define CHANNEL 0x0F #define CHANNEL_0 1 #define CHANNEL_1 2 #define CHANNEL_2 4 #define CHANNEL_3 8 #define LEFT_CHANNEL CHANNEL_0 #define RIGHT_CHANNEL CHANNEL_1 - u_char :4; u_char volume; } port[4]; #define LEFT_PORT 0 diff --git a/sys/scsi/scsi_changer.h b/sys/scsi/scsi_changer.h index 63a5b1398a..4cadd3221a 100644 --- a/sys/scsi/scsi_changer.h +++ b/sys/scsi/scsi_changer.h @@ -1,15 +1,3 @@ -/* - * HISTORY - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- - * - * 16 Feb 93 Julian Elischer ADDED for SCSI system - * - */ - /* * SCSI changer interface description */ @@ -29,10 +17,9 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * - */ - -/* * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * + * $Id$ */ /* @@ -41,17 +28,15 @@ struct scsi_read_element_status { u_char op_code; - u_char element_type_code:4; - u_char voltag:1; - u_char lun:3; + u_char byte2; +#define SRES_ELEM_TYPE_CODE 0x0F +#define SRES_ELEM_VOLTAG 0x10 u_char starting_element_addr[2]; u_char number_of_elements[2]; u_char resv1; u_char allocation_length[3]; u_char resv2; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; #define RE_ALL_ELEMENTS 0 #define RE_MEDIUM_TRANSPORT_ELEMENT 1 @@ -62,32 +47,24 @@ struct scsi_read_element_status struct scsi_move_medium { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char transport_element_address[2]; u_char source_address[2]; u_char destination_address[2]; u_char rsvd[2]; - u_char invert:1; - u_char :7; - u_char link:1; - u_char flag:1; - u_char :6; + u_char invert; + u_char control; }; struct scsi_position_to_element { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char transport_element_address[2]; u_char source_address[2]; u_char rsvd[2]; - u_char invert:1; - u_char :7; - u_char link:1; - u_char flag:1; - u_char :6; + u_char invert; + u_char control; }; /* @@ -108,9 +85,9 @@ struct scsi_element_status_data struct element_status_page { u_char element_type_code; - u_char :5; - u_char avoltag:1; - u_char pvoltag:1; + u_char flags; +#define ESP_AVOLTAG 0x40 +#define ESP_PVOLTAG 0x80 u_char element_descriptor_length[2]; u_char rsvd; u_char byte_count_of_descriptor_data[3]; diff --git a/sys/scsi/scsi_disk.h b/sys/scsi/scsi_disk.h index 35a3b78378..a4eebf5752 100644 --- a/sys/scsi/scsi_disk.h +++ b/sys/scsi/scsi_disk.h @@ -1,22 +1,3 @@ -/* - * HISTORY - * $Log: scsi_disk.h,v $ - * Revision 1.2 1992/10/13 03:14:21 julian - * added the load-eject field in 'start/stop' for removable devices. - * - * Revision 1.1 1992/09/26 22:11:29 julian - * Initial revision - * - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- - * - * 16 Feb 93 Julian Elischer ADDED for SCSI system - * - */ - /* * SCSI interface description */ @@ -63,10 +44,9 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * - */ - -/* * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * + * $Id$ */ /* @@ -77,73 +57,58 @@ struct scsi_reassign_blocks { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char unused[3]; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_rw { u_char op_code; - u_char addr_2:5; /* Most significant */ - u_char lun:3; + u_char addr_2; /* Most significant */ +#define SRW_TOPADDR 0x1F /* only 5 bits here */ u_char addr_1; u_char addr_0; /* least significant */ u_char length; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_rw_big { u_char op_code; - u_char rel_addr:1; - u_char :4; /* Most significant */ - u_char lun:3; - u_char addr_3; + u_char byte2; +#define SRWB_RELADDR 0x01 + u_char addr_3; /* Most significant */ u_char addr_2; u_char addr_1; u_char addr_0; /* least significant */ u_char reserved;; u_char length2; u_char length1; - u_char link:1; - u_char flag:1; - u_char :4; - u_char vendor:2; + u_char control; }; struct scsi_read_capacity { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char addr_3; /* Most Significant */ u_char addr_2; u_char addr_1; u_char addr_0; /* Least Significant */ u_char unused[3]; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; }; struct scsi_start_stop { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char unused[2]; - u_char start:1; - u_char loej:1; - u_char :6; - u_char link:1; - u_char flag:1; - u_char :6; + u_char how; +#define SSS_START 0x01 +#define SSS_LOEJ 0x02 + u_char control; }; @@ -194,8 +159,8 @@ struct scsi_reassign_blocks_data union disk_pages /* this is the structure copied from osf */ { struct page_disk_format { - u_char pg_code:6; /* page code (should be 3) */ - u_char :2; + u_char pg_code; /* page code (should be 3) */ +#define DISK_PGCODE 0x3F /* only 6 bits valid */ u_char pg_length; /* page length (should be 0x16) */ u_char trk_z_1; /* tracks per zone (MSB) */ u_char trk_z_0; /* tracks per zone (LSB) */ @@ -215,17 +180,16 @@ union disk_pages /* this is the structure copied from osf */ u_char trk_skew_0; /* track skew factor (LSB) */ u_char cyl_skew_1; /* cylinder skew (MSB) */ u_char cyl_skew_0; /* cylinder skew (LSB) */ - u_char reserved1:4; - u_char surf:1; - u_char rmb:1; - u_char hsec:1; - u_char ssec:1; + u_char flags; /* various */ +#define DISK_FMT_SURF 0x10 +#define DISK_FMT_RMB 0x20 +#define DISK_FMT_HSEC 0x40 +#define DISK_FMT_SSEC 0x80 u_char reserved2; u_char reserved3; } disk_format; struct page_rigid_geometry { - u_char pg_code:7; /* page code (should be 4) */ - u_char mbone:1; /* must be one */ + u_char pg_code; /* page code (should be 4) */ u_char pg_length; /* page length (should be 0x16) */ u_char ncyl_2; /* number of cylinders (MSB) */ u_char ncyl_1; /* number of cylinders */ diff --git a/sys/scsi/scsi_generic.h b/sys/scsi/scsi_generic.h new file mode 100644 index 0000000000..44f2bd11cf --- /dev/null +++ b/sys/scsi/scsi_generic.h @@ -0,0 +1,63 @@ +/* + * Contributed by HD Associates (hd@world.std.com). + * Copyright (c) 1992, 1993 HD Associates + * + * Berkeley style copyright. I've just snarfed it out of stdio.h: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)stdio.h 5.17 (Berkeley) 6/3/91 + * $Id$ + */ + +/* generic SCSI header file. We use the same minor number format + * as on SGI except that the flag bits aren't available because they + * are used as the board index. + * + * The minor number format is: + * FF UUU III (FFUU UIII) + * + * Where: + * FF is the board index + * UUU are the LUN + * III is the SCSI ID (controller) + */ + +#ifndef _SCSI_GENERIC_H_ +#define _SCSI_GENERIC_H_ + +#define G_SCSI_FLAG(DEV) (((DEV) & 0xC0) >> 6) +#define G_SCSI_UNIT(DEV) G_SCSI_FLAG(DEV) +#define G_SCSI_LUN(DEV) (((DEV) & 0x38) >> 3) +#define G_SCSI_ID(DEV) ((DEV) & 0x7) + +#define G_SCSI_MINOR(FLAG, LUN, ID) \ + (((FLAG) << 6) | ((LUN) << 3) | (ID)) + +#endif /* _SCSI_GENERIC_H_ */ diff --git a/sys/scsi/scsi_tape.h b/sys/scsi/scsi_tape.h index 2b8a82f074..eb46092009 100644 --- a/sys/scsi/scsi_tape.h +++ b/sys/scsi/scsi_tape.h @@ -1,22 +1,3 @@ -/* - * HISTORY - * $Log: scsi_tape.h,v $ - * Revision 1.2 1993/01/26 18:39:08 julian - * add the 'write protected' bit in the device status struct. - * - * Revision 1.1 1992/09/26 22:10:21 julian - * Initial revision - * - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- - * - * 16 Feb 93 Julian Elischer ADDED for SCSI system - * - */ - /* * SCSI tape interface description */ @@ -39,35 +20,33 @@ /* * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * + * $Id: scsi_tape.h,v 1.8 93/08/31 21:40:16 julian Exp Locker: julian $ */ + + /* - * SCSI command format + * SCSI command formats */ struct scsi_rw_tape { u_char op_code; - u_char fixed:1; - u_char :4; - u_char lun:3; + u_char byte2; +#define SRWT_FIXED 0x01 u_char len[3]; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; } rw_tape; struct scsi_space { u_char op_code; - u_char code:2; - u_char :3; - u_char lun:3; + u_char byte2; +#define SS_CODE 0x03 u_char number[3]; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; } space; #define SP_BLKS 0 #define SP_FILEMARKS 1 @@ -77,52 +56,40 @@ struct scsi_space struct scsi_write_filemarks { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char number[3]; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; } write_filemarks; struct scsi_rewind { u_char op_code; - u_char immed:1; - u_char :4; - u_char lun:3; + u_char byte2; +#define SR_IMMED 0x01 u_char unused[3]; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; } rewind; struct scsi_load { u_char op_code; - u_char immed:1; - u_char :4; - u_char lun:3; + u_char byte2; +#define SL_IMMED 0x01 u_char unused[2]; - u_char load:1; - u_char reten:1; - u_char :6; - u_char link:1; - u_char flag:1; - u_char :6; + u_char how; + u_char control; } load; #define LD_UNLOAD 0 #define LD_LOAD 1 +#define LD_RETEN 2 + struct scsi_blk_limits { u_char op_code; - u_char :5; - u_char lun:3; + u_char byte2; u_char unused[3]; - u_char link:1; - u_char flag:1; - u_char :6; + u_char control; } blk_limits; /* @@ -149,21 +116,87 @@ struct scsi_blk_limits_data u_char min_length_0; /* Least significant */ }; -struct scsi_mode_header_tape +/* defines for the device specific byte in the mode select/sense header */ +#define SMH_DSP_SPEED 0x0F +#define SMH_DSP_BUFF_MODE 0x70 +#define SMH_DSP_BUFF_MODE_OFF 0x00 +#define SMH_DSP_BUFF_MODE_ON 0x10 +#define SMH_DSP_BUFF_MODE_MLTI 0x20 +#define SMH_DSP_WRITE_PROT 0x80 + +/* A special for the CIPHER ST150S(old drive) */ +struct blk_desc_cipher { - u_char data_length; /* Sense data length */ - u_char medium_type; - u_char speed:4; - u_char buf_mode:3; - u_char write_protected:1; - u_char blk_desc_len; + u_char density; + u_char nblocks[3]; + u_char reserved; + u_char blklen[3]; + u_char other; +#define ST150_SEC 0x01 /* soft error count */ +#define SR150_AUI 0x02 /* autoload inhibit */ }; -#define QIC_120 0x0f -#define QIC_150 0x10 -#define QIC_320 0x11 -#define QIC_525 0x11 -#define QIC_1320 0x12 + +/********************************************************************** + from the scsi2 spec + Value Tracks Density(bpi) Code Type Reference Note + 0x1 9 800 NRZI R X3.22-1983 2 + 0x2 9 1600 PE R X3.39-1986 2 + 0x3 9 6250 GCR R X3.54-1986 2 + 0x5 4/9 8000 GCR C X3.136-1986 1 + 0x6 9 3200 PE R X3.157-1987 2 + 0x7 4 6400 IMFM C X3.116-1986 1 + 0x8 4 8000 GCR CS X3.158-1986 1 + 0x9 18 37871 GCR C X3B5/87-099 2 + 0xA 22 6667 MFM C X3B5/86-199 1 + 0xB 4 1600 PE C X3.56-1986 1 + 0xC 24 12690 GCR C HI-TC1 1,5 + 0xD 24 25380 GCR C HI-TC2 1,5 + 0xF 15 10000 GCR C QIC-120 1,5 + 0x10 18 10000 GCR C QIC-150 1,5 + 0x11 26 16000 GCR C QIC-320(525?) 1,5 + 0x12 30 51667 RLL C QIC-1350 1,5 + 0x13 1 61000 DDS CS X3B5/88-185A 4 + 0x14 1 43245 RLL CS X3.202-1991 4 + 0x15 1 45434 RLL CS ECMA TC17 4 + 0x16 48 10000 MFM C X3.193-1990 1 + 0x17 48 42500 MFM C X3B5/91-174 1 + + where Code means: + NRZI Non Return to Zero, change on ones + GCR Group Code Recording + PE Phase Encoded + IMFM Inverted Modified Frequency Modulation + MFM Modified Frequency Modulation + DDS Dat Data Storage + RLL Run Length Encoding + + where Type means: + R Real-to-Real + C Cartridge + CS cassette + + where Notes means: + 1 Serial Recorded + 2 Parallel Recorded + 3 Old format know as QIC-11 + 4 Helical Scan + 5 Not ANSI standard, rather industry standard. + +********************************************************************/ + +#define HALFINCH_800 0x01 +#define HALFINCH_1600 0x02 +#define HALFINCH_6250 0x03 +#define QIC_11 0x04 /* from Archive 150S Theory of Op. XXX */ +#define QIC_24 0x05 /* may be bad, works for CIPHER ST150S XXX */ +#define QIC_120 0x0f +#define QIC_150 0x10 +#define QIC_320 0x11 +#define QIC_525 0x11 +#define QIC_1320 0x12 +#define DDS 0x13 +#define DAT-1 0x13 diff --git a/sys/scsi/scsiconf.c b/sys/scsi/scsiconf.c index fc1b5ca5f4..70765b062a 100644 --- a/sys/scsi/scsiconf.c +++ b/sys/scsi/scsiconf.c @@ -12,29 +12,17 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00149 - * -------------------- ----- ---------------------- - * - * 16 Feb 93 Julian Elischer ADDED for SCSI system - * 23 May 93 Rodney W. Grimes ADDED Pioneer DRM-600 cd changer - */ - -/* * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * + * $Id: scsiconf.c,v 1.5 1993/08/28 03:08:53 rgrimes Exp $ */ -/* -$Log: -* -*/ #include #include "st.h" #include "sd.h" #include "ch.h" #include "cd.h" +#include "sg.h" #ifdef MACH #include @@ -178,9 +166,10 @@ struct scsi_switch *scsi_switch; #ifdef SCSI_DELAY #if SCSI_DELAY > 2 - printf("waiting for scsi devices to settle\n"); + printf("%s%d waiting for scsi devices to settle\n", + scsi_switch->name, unit); #else SCSI_DELAY > 2 -#define SCSI_DELAY 15 +#define SCSI_DELAY 2 #endif SCSI_DELAY > 2 #else #define SCSI_DELAY 2 @@ -201,6 +190,7 @@ struct scsi_switch *scsi_switch; predef = scsi_get_predef(scsibus ,targ ,lun + ,scsi_switch ,&maybe_more); bestmatch = scsi_probedev(unit ,targ @@ -243,12 +233,12 @@ struct scsi_switch *scsi_switch; } targ++; } -#if NGENSCSI > 0 +#if NSG > 0 /***************************************************************\ * If available hook up the generic scsi driver, letting it * * know which target is US. (i.e. illegal or at least special) * \***************************************************************/ - genscsi_attach(unit,scsi_addr,0,scsi_switch); + sg_attach(unit,scsi_addr,scsi_switch); #endif scsibus++; /* next time we are on the NEXT scsi bus */ } @@ -257,8 +247,9 @@ struct scsi_switch *scsi_switch; * given a target and lu, check if there is a * * predefined device for that address * \***********************************************/ -struct predefined *scsi_get_predef(unit,target,lu,maybe_more) +struct predefined *scsi_get_predef(unit,target,lu,scsi_switch,maybe_more) int unit,target,lu,*maybe_more; +struct scsi_switch *scsi_switch; { int upto,numents; @@ -273,7 +264,9 @@ int unit,target,lu,*maybe_more; if(pd[upto].lu != lu) continue; - printf(" dev%d,lu%d: %s - PRECONFIGURED -\n" + printf("%s%d targ %d lun %d: <%s> - PRECONFIGURED -\n" + ,scsi_switch->name + ,unit ,target ,lu ,pd[upto].devname); @@ -345,19 +338,10 @@ int *maybe_more; desc[12]); } - type = inqbuf.device_type; - qualifier = inqbuf.device_qualifier; - remov = inqbuf.removable; + type = inqbuf.device & SID_TYPE; + qualifier = inqbuf.device & SID_QUAL; + remov = inqbuf.dev_qual2 & SID_REMOVABLE; - /* Check for a non-existent unit. If the device is returning - * this much, then we must set the flag that has - * the searcher keep looking on other luns. - */ - if (qualifier == 3 && type == T_NODEVICE) - { - *maybe_more = 1; - return (struct scsidevs *)0; - } /* Any device qualifier that has * the top bit set (qualifier&4 != 0) is vendor specific and @@ -366,20 +350,31 @@ int *maybe_more; switch(qualifier) { - case 0: + case SID_QUAL_LU_OK: qtype=""; break; - case 1: + + case SID_QUAL_LU_OFFLINE: qtype=", Unit not Connected!"; break; - case 2: + + case SID_QUAL_RSVD: qtype=", Reserved Peripheral Qualifier!"; + *maybe_more = 1; + return (struct scsidevs *)0; break; - case 3: + + case SID_QUAL_BAD_LU: + /* + * Check for a non-existent unit. If the device is returning + * this much, then we must set the flag that has + * the searcher keep looking on other luns. + */ qtype=", The Target can't support this Unit!"; - break; + *maybe_more = 1; + return (struct scsidevs *)0; - default: + default: dtype="vendor specific"; qtype=""; *maybe_more = 1; @@ -387,48 +382,53 @@ int *maybe_more; } if (dtype == 0) + { switch(type) { - case T_DIRECT: - dtype="direct"; - break; - case T_SEQUENTIAL: - dtype="sequential"; - break; - case T_PRINTER: - dtype="printer"; - break; - case T_PROCESSOR: - dtype="processor"; - break; - case T_READONLY: - dtype="readonly"; - break; - case T_WORM: - dtype="worm"; - break; - case T_SCANNER: - dtype="scanner"; - break; - case T_OPTICAL: - dtype="optical"; - break; - case T_CHANGER: - dtype="changer"; - break; - case T_COMM: - dtype="communication"; - break; - default: - dtype="unknown"; - break; + case T_DIRECT: + dtype="direct"; + break; + case T_SEQUENTIAL: + dtype="sequential"; + break; + case T_PRINTER: + dtype="printer"; + break; + case T_PROCESSOR: + dtype="processor"; + break; + case T_READONLY: + dtype="readonly"; + break; + case T_WORM: + dtype="worm"; + break; + case T_SCANNER: + dtype="scanner"; + break; + case T_OPTICAL: + dtype="optical"; + break; + case T_CHANGER: + dtype="changer"; + break; + case T_COMM: + dtype="communication"; + break; + case T_NODEVICE: + *maybe_more = 1; + return (struct scsidevs *)0; + default: + dtype="unknown"; + break; } + } /***********************************************\ * Then if it's advanced enough, more detailed * * information * \***********************************************/ - if(inqbuf.ansii_version > 0) + if((inqbuf.version & SID_ANSII) > 0) { if ((len = inqbuf.additional_length + ( (char *)inqbuf.unused @@ -451,23 +451,42 @@ int *maybe_more; strncpy(model,"unknown",16); strncpy(version,"????",4); } - printf(" dev%d,lu%d: type %d:%d(%s%s),%s '%s%s%s' scsi%d\n" + printf("%s%d targ %d lun %d: type %d(%s) %s SCSI%d\n" + ,scsi_switch->name + ,unit ,target ,lu - ,qualifier,type - ,dtype,qtype + ,type + ,dtype ,remov?"removable":"fixed" + ,inqbuf.version & SID_ANSII + ); + printf("%s%d targ %d lun %d: <%s%s%s>\n" + ,scsi_switch->name + ,unit + ,target + ,lu ,manu ,model ,version - ,inqbuf.ansii_version ); + if(qtype[0]) + { + printf("%s%d targ %d lun %d: qualifier %d(%s)\n" + ,scsi_switch->name + ,unit + ,target + ,lu + ,qualifier + ,qtype + ); + } /***********************************************\ * Try make as good a match as possible with * * available sub drivers * \***********************************************/ bestmatch = (selectdev(unit,target,lu,&scsi_switch, - qualifier,type,remov,manu,model,version)); + qualifier,type,remov?T_REMOV:T_FIXED,manu,model,version)); if((bestmatch) && (bestmatch->flags & SC_MORE_LUS)) { *maybe_more = 1; @@ -492,7 +511,7 @@ char *manu,*model,*rev; struct scsidevs *bestmatch = (struct scsidevs *)0; struct scsidevs *thisentry = knowndevs; - type |= (qualifier << 5); + type |= qualifier; /* why? */ thisentry--; while( count++ < numents) @@ -611,9 +630,10 @@ retry: scsi_xfer.error=0; * correct response. * *( especially exabytes) * \*******************************************************/ - if(scsi_xfer.sense.error_class == 7 ) + if(((scsi_xfer.sense.error_code & SSD_ERRCODE) == 0x70 ) + ||((scsi_xfer.sense.error_code & SSD_ERRCODE) == 0x71 )) { - key = scsi_xfer.sense.ext.extended.sense_key ; + key = scsi_xfer.sense.ext.extended.flags & SSD_KEY ; switch(key) { case 2: /* not ready BUT PRESENT! */ @@ -696,8 +716,8 @@ retry: scsi_xfer.error=0; * correct response. * *( especially exabytes) * \*******************************************************/ - if((scsi_xfer.sense.error_class == 7 ) - && (scsi_xfer.sense.ext.extended.sense_key == 6)) + if(((scsi_xfer.sense.error_code & SSD_ERRCODE) == 0x70 ) + && ((scsi_xfer.sense.ext.extended.flags & SSD_KEY) == 6)) { /* it's changed so it's there */ spinwait(1000); { diff --git a/sys/scsi/scsiconf.h b/sys/scsi/scsiconf.h index c36efde535..b0b45efdf5 100644 --- a/sys/scsi/scsiconf.h +++ b/sys/scsi/scsiconf.h @@ -12,17 +12,9 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- - * - * 16 Feb 93 Julian Elischer ADDED for SCSI system - * - */ - -/* * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * + * $Id: scsiconf.h,v 1.3 93/08/26 21:09:43 julian Exp Locker: julian $ */ /***********************************************\ @@ -37,12 +29,13 @@ struct scsi_switch int (*open_target_lu)(); int (*close_target_lu)(); long int (*adapter_info)(); /* see definitions below */ - u_long spare[3]; + char *name; /* name of scsi bus controller */ + u_long spare[2]; }; #define AD_INF_MAX_CMDS 0x000000FF /* maximum number of entries queuable to a device by the adapter */ -/* 24 bits of other adapter charcteristics go here */ +/* 24 bits of other adapter characteristics go here */ /***********************************************\ * The scsi debug control bits * @@ -64,6 +57,7 @@ extern int scsi_debug; #define TRY_AGAIN_LATER 1 #define COMPLETE 2 #define HAD_ERROR 3 +#define ESCAPE_NOT_SUPPORTED 4 struct scsi_xfer { @@ -85,6 +79,13 @@ struct scsi_xfer int error; struct buf *bp; struct scsi_sense_data sense; + + /* Believe it or not, Some targets fall on the ground with + * anything but a certain sense length. + */ + int req_sense_length; /* Explicit request sense length */ + + int status; /* SCSI status */ }; /********************************/ /* Flag values */ @@ -101,6 +102,17 @@ struct scsi_xfer #define SCSI_DATA_IN 0x400 /* expect data to come INTO memory */ #define SCSI_DATA_OUT 0x800 /* expect data to flow OUT of memory */ #define SCSI_TARGET 0x1000 /* This defines a TARGET mode op. */ +#define SCSI_ESCAPE 0x2000 /* Escape operation */ + +/*************************************************************************/ +/* Escape op codes. This provides an extensible setup for operations */ +/* that are not scsi commands. They are intended for modal operations. */ +/*************************************************************************/ + +#define SCSI_OP_TARGET 0x0001 +#define SCSI_OP_RESET 0x0002 +#define SCSI_OP_BDINFO 0x0003 + /********************************/ /* Error values */ /********************************/ diff --git a/sys/scsi/sd.c b/sys/scsi/sd.c index af8ae79176..3ee38d29f5 100644 --- a/sys/scsi/sd.c +++ b/sys/scsi/sd.c @@ -1,5 +1,5 @@ /* - * Written by Julian Elischer (julian@tfs.com) + * Written by Julian Elischer (julian@dialix.oz.au) * for TRW Financial Systems for use under the MACH(2.5) operating system. * * TRW Financial Systems, in accordance with their agreement with Carnegie @@ -12,20 +12,9 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00149 - * -------------------- ----- ---------------------- + * Ported to run under 386BSD by Julian Elischer (julian@dialix.oz.au) Sept 1992 * - * 16 Feb 93 Julian Elischer ADDED for SCSI system - * 20 Apr 93 Julian Elischer Fixed error reporting - * - */ - -static char rev[] = "$Revision: 1.3 $"; - -/* - * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * $Id: sd.c,v 1.9 1993/09/20 06:27:04 rgrimes Exp $ */ #define SPLSD splbio @@ -79,7 +68,9 @@ struct buf sd_buf_queue[NSD]; int sd_done(); int sdstrategy(); +#ifdef SDDEBUG int sd_debug = 0; +#endif /*SDDEBUG*/ struct scsi_xfer *sd_free_xfer[NSD]; int sd_xfer_block_wait[NSD]; @@ -114,7 +105,7 @@ struct sd_data #define SDOPEN 0x01 int openparts; /* one bit for each open partition */ unsigned int sd_start_of_unix; /* unix vs dos partitions */ -}sd_data[NSD]; +}*sd_data[NSD]; static int next_sd_unit = 0; @@ -134,17 +125,31 @@ struct scsi_switch *scsi_switch; struct scsi_xfer *sd_scsi_xfer; unit = next_sd_unit++; - sd = sd_data + unit; - dp = &(sd->params); +#ifdef SDDEBUG if(scsi_debug & PRINTROUTINES) printf("sdattach: "); +#endif /*SDDEBUG*/ /*******************************************************\ * Check we have the resources for another drive * \*******************************************************/ if( unit >= NSD) { - printf("Too many scsi disks..(%d > %d) reconfigure kernel",(unit + 1),NSD); + printf("Too many scsi disks..(%d > %d) reconfigure kernel\n",(unit + 1),NSD); + return(0); + } + if(sd_data[unit]) + { + printf("sd%d: unit already has storage allocated!\n",unit); return(0); } + sd = sd_data[unit] = malloc(sizeof(struct sd_data),M_DEVBUF,M_NOWAIT); + if(!sd) + { + printf("malloc failed in sd.c\n"); + return(0); + } + bzero(sd,sizeof(struct sd_data)); + + dp = &(sd->params); /*******************************************************\ * Store information needed to contact our base driver * \*******************************************************/ @@ -182,21 +187,14 @@ struct scsi_switch *scsi_switch; * request must specify this. * \*******************************************************/ sd_get_parms(unit, SCSI_NOSLEEP | SCSI_NOMASK); - printf(" sd%d: %dMB, cyls %d, heads %d, secs %d, bytes/sec %d\n", + printf("sd%d: %dMB (%d total sec), %d cyl, %d head, %d sec, bytes/sec %d\n", unit, - ( dp->cyls - * dp->heads - * dp->sectors - * dp->secsiz - ) - / (1024 * 1024), + dp->disksize / ((1024L * 1024L) / dp->secsiz), + dp->disksize, dp->cyls, dp->heads, dp->sectors, dp->secsiz); - /*******************************************************\ - * Set up the bufs for this device * - \*******************************************************/ sd->flags |= SDINIT; return; @@ -217,10 +215,12 @@ sdopen(dev) unit = UNIT(dev); part = PARTITION(dev); - sd = sd_data + unit; + sd = sd_data[unit]; +#ifdef SDDEBUG if(scsi_debug & (PRINTROUTINES | TRACEOPENS)) printf("sdopen: dev=0x%x (unit %d (of %d),partition %d)\n" , dev, unit, NSD, part); +#endif /*SDDEBUG*/ /*******************************************************\ * Check the unit is legal * \*******************************************************/ @@ -246,51 +246,64 @@ sdopen(dev) && ( sd->openparts)) return(ENXIO); /*******************************************************\ - * Check that it is still responding and ok. * - * "unit attention errors should occur here if the drive * - * has been restarted or the pack changed * + * "unit attention" errors should occur here if the * + * drive has been restarted or the pack changed. * + * just ingnore the result, it's a decoy instruction * + * The error code will act on the error though * + * and invalidate any media information we had. * \*******************************************************/ + sd_test_unit_ready(unit,0); - if(scsi_debug & TRACEOPENS) - printf("device is "); - if (sd_test_unit_ready(unit,0)) - { - if(scsi_debug & TRACEOPENS) printf("not reponding\n"); - return(ENXIO); - } - if(scsi_debug & TRACEOPENS) - printf("ok\n"); /*******************************************************\ * In case it is a funny one, tell it to start * * not needed for most hard drives (ignore failure) * \*******************************************************/ sd_start_unit(unit,SCSI_ERR_OK|SCSI_SILENT); + + /*******************************************************\ + * Check that it is still responding and ok. * + \*******************************************************/ + if (sd_test_unit_ready(unit,0)) + { +#ifdef SDDEBUG + if(scsi_debug & TRACEOPENS) printf("device not reponding\n"); +#endif /*SDDEBUG*/ + return(ENXIO); + } +#ifdef SDDEBUG if(scsi_debug & TRACEOPENS) - printf("started "); + printf("device ok\n"); +#endif /*SDDEBUG*/ + /*******************************************************\ * Load the physical device parameters * \*******************************************************/ sd_get_parms(unit, 0); /* sets SDVALID */ - if (sd->params.secsiz != SECSIZE) + if (sd->params.secsiz != SECSIZE) /* XXX One day...*/ { printf("sd%d: Can't deal with %d bytes logical blocks\n" ,unit, sd->params.secsiz); Debugger(); return(ENXIO); } +#ifdef SDDEBUG if(scsi_debug & TRACEOPENS) printf("Params loaded "); +#endif /*SDDEBUG*/ /*******************************************************\ * Load the partition info if not already loaded * + * Lock the pack in * \*******************************************************/ - sd_prevent(unit,PR_PREVENT,SCSI_ERR_OK|SCSI_SILENT); /* who cares if it fails? */ + sd_prevent(unit,PR_PREVENT,SCSI_ERR_OK|SCSI_SILENT); if((errcode = sdgetdisklabel(unit)) && (part != RAW_PART)) { - sd_prevent(unit,PR_ALLOW,SCSI_ERR_OK|SCSI_SILENT); /* who cares if it fails? */ + sd_prevent(unit,PR_ALLOW,SCSI_ERR_OK|SCSI_SILENT); return(errcode); } +#ifdef SDDEBUG if(scsi_debug & TRACEOPENS) printf("Disklabel loaded "); +#endif /*SDDEBUG*/ /*******************************************************\ * Check the partition is legal * \*******************************************************/ @@ -298,8 +311,10 @@ sdopen(dev) sd_prevent(unit,PR_ALLOW,SCSI_ERR_OK|SCSI_SILENT); /* who cares if it fails? */ return(ENXIO); } +#ifdef SDDEBUG if(scsi_debug & TRACEOPENS) printf("ok"); +#endif /*SDDEBUG*/ /*******************************************************\ * Check that the partition exists * \*******************************************************/ @@ -311,8 +326,10 @@ sdopen(dev) } sd->partflags[part] |= SDOPEN; sd->openparts |= (1 << part); +#ifdef SDDEBUG if(scsi_debug & TRACEOPENS) printf("open %d %d\n",sdstrats,sdqueues); +#endif /*SDDEBUG*/ return(0); } @@ -392,7 +409,7 @@ int flags; void sdminphys(bp) struct buf *bp; { - (*(sd_data[UNIT(bp->b_dev)].sc_sw->scsi_minphys))(bp); + (*(sd_data[UNIT(bp->b_dev)]->sc_sw->scsi_minphys))(bp); } /*******************************************************\ @@ -412,10 +429,12 @@ struct buf *bp; sdstrats++; unit = UNIT((bp->b_dev)); - sd = sd_data + unit; + sd = sd_data[unit]; +#ifdef SDDEBUG if(scsi_debug & PRINTROUTINES) printf("\nsdstrategy "); if(scsi_debug & SHOWREQUESTS) printf("sd%d: %d bytes @ blk%d\n", unit,bp->b_bcount,bp->b_blkno); +#endif /*SDDEBUG*/ sdminphys(bp); /*******************************************************\ * If the device has been made invalid, error out * @@ -492,7 +511,7 @@ done: /***************************************************************\ * sdstart looks to see if there is a buf waiting for the device * * and that the device is not already busy. If both are true, * -* It deques the buf and creates a scsi command to perform the * +* It dequeues the buf and creates a scsi command to perform the * * transfer in the buf. The transfer request will call sd_done * * on completion, which will in turn call this routine again * * so that the next queued transfer is performed. * @@ -514,37 +533,41 @@ int unit; struct scsi_xfer *xs; struct scsi_rw_big cmd; int blkno, nblk; - struct sd_data *sd = sd_data + unit; + struct sd_data *sd = sd_data[unit]; struct partition *p ; +#ifdef SDDEBUG if(scsi_debug & PRINTROUTINES) printf("sdstart%d ",unit); +#endif /*SDDEBUG*/ /*******************************************************\ - * See if there is a buf to do and we are not already * - * doing one * + * Check if the device is already running full capacity * \*******************************************************/ if(!sd_free_xfer[unit]) { return; /* none for us, unit already underway */ } - if(sd_xfer_block_wait[unit]) /* there is one, but a special waits */ + /*******************************************************\ + * there is excess capacity, but a special waits * + * It'll need the adapter as soon as we clear out of the * + * way and let it run (user level wait). * + \*******************************************************/ + if(sd_xfer_block_wait[unit]) { - return; /* give the special that's waiting a chance to run */ + return; } - + /*******************************************************\ + * See if there is a buf with work for us to do.. * + \*******************************************************/ dp = &sd_buf_queue[unit]; - if ((bp = dp->b_actf) != NULL) /* yes, an assign */ + if ((bp = dp->b_actf) == NULL) /* yes, an assign */ { - dp->b_actf = bp->av_forw; - } - else - { return; } - xs=sd_get_xs(unit,0); /* ok we can grab it */ - xs->flags = INUSE; /* Now ours */ + dp->b_actf = bp->av_forw; + /*******************************************************\ * If the device has become invalid, abort all the * * reads and writes until all files have been closed and * @@ -552,15 +575,12 @@ int unit; \*******************************************************/ if(!(sd->flags & SDVALID)) { - xs->error = XS_DRIVER_STUFFUP; - sd_done(unit,xs); /* clean up (calls sdstart) */ - return ; + goto bad; } /*******************************************************\ - * We have a buf, now we should move the data into * - * a scsi_xfer definition and try start it * - \*******************************************************/ - /*******************************************************\ + * We have a buf, now we know we are going to go through * + * With this thing.. * + * * * First, translate the block to absolute * \*******************************************************/ p = sd->disklabel.d_partitions + PARTITION(bp->b_dev); @@ -580,111 +600,30 @@ int unit; cmd.length2 = (nblk & 0xff00) >> 8; cmd.length1 = (nblk & 0xff); /*******************************************************\ - * Fill out the scsi_xfer structure * + * Call the routine that chats with the adapter * * Note: we cannot sleep as we may be an interrupt * \*******************************************************/ - xs->flags |= SCSI_NOSLEEP; - xs->adapter = sd->ctlr; - xs->targ = sd->targ; - xs->lu = sd->lu; - xs->retries = SD_RETRIES; - xs->timeout = 10000;/* 10000 millisecs for a disk !*/ - xs->cmd = (struct scsi_generic *)&cmd; - xs->cmdlen = sizeof(cmd); - xs->resid = bp->b_bcount; - xs->when_done = sd_done; - xs->done_arg = unit; - xs->done_arg2 = (int)xs; - xs->error = XS_NOERROR; - xs->bp = bp; - xs->data = (u_char *)bp->b_un.b_addr; - xs->datalen = bp->b_bcount; - /*******************************************************\ - * Pass all this info to the scsi driver. * - \*******************************************************/ - - - - if ( (*(sd->sc_sw->scsi_cmd))(xs) != SUCCESSFULLY_QUEUED) + if (sd_scsi_cmd(unit, + &cmd, + sizeof(cmd), + (u_char *)bp->b_un.b_addr, + bp->b_bcount, + 10000, + bp, + SCSI_NOSLEEP| ((bp->b_flags & B_READ)? + SCSI_DATA_IN : SCSI_DATA_OUT)) + != SUCCESSFULLY_QUEUED) { +bad: printf("sd%d: oops not queued",unit); - xs->error = XS_DRIVER_STUFFUP; - sd_done(unit,xs); /* clean up (calls sdstart) */ + bp->b_error = EIO; + bp->b_flags |= B_ERROR; + biodone(bp); + return ; } sdqueues++; } -/*******************************************************\ -* This routine is called by the scsi interrupt when * -* the transfer is complete. -\*******************************************************/ -int sd_done(unit,xs) -int unit; -struct scsi_xfer *xs; -{ - struct buf *bp; - int retval; - int retries = 0; - - if(scsi_debug & PRINTROUTINES) printf("sd_done%d ",unit); - if (! (xs->flags & INUSE)) - panic("scsi_xfer not in use!"); - if(bp = xs->bp) - { - switch(xs->error) - { - case XS_NOERROR: - bp->b_error = 0; - bp->b_resid = 0; - break; - - case XS_SENSE: - retval = (sd_interpret_sense(unit,xs)); - if(retval) - { - bp->b_flags |= B_ERROR; - bp->b_error = retval; - } - break; - - case XS_TIMEOUT: - printf("sd%d timeout\n",unit); - - case XS_BUSY: /* should retry */ /* how? */ - /************************************************/ - /* SHOULD put buf back at head of queue */ - /* and decrement retry count in (*xs) */ - /* HOWEVER, this should work as a kludge */ - /************************************************/ - if(xs->retries--) - { - xs->error = XS_NOERROR; - xs->flags &= ~ITSDONE; - if ( (*(sd_data[unit].sc_sw->scsi_cmd))(xs) - == SUCCESSFULLY_QUEUED) - { /* don't wake the job, ok? */ - return; - } - xs->flags |= ITSDONE; - } /* fall through */ - - case XS_DRIVER_STUFFUP: - bp->b_flags |= B_ERROR; - bp->b_error = EIO; - break; - default: - printf("sd%d: unknown error category from scsi driver\n" - ,unit); - } - biodone(bp); - sd_free_xs(unit,xs,0); - sdstart(unit); /* If there's anything waiting.. do it */ - } - else /* special has finished */ - { - wakeup(xs); - } -} /*******************************************************\ * Perform special action on behalf of the user * * Knows about the internals of this device * @@ -703,13 +642,15 @@ sdioctl(dev_t dev, int cmd, caddr_t addr, int flag) \*******************************************************/ unit = UNIT(dev); part = PARTITION(dev); - sd = &sd_data[unit]; + sd = sd_data[unit]; +#ifdef SDDEBUG if(scsi_debug & PRINTROUTINES) printf("sdioctl%d ",unit); +#endif /*SDDEBUG*/ /*******************************************************\ * If the device is not valid.. abandon ship * \*******************************************************/ - if (!(sd_data[unit].flags & SDVALID)) + if (!(sd->flags & SDVALID)) return(EIO); switch(cmd) { @@ -793,7 +734,7 @@ unsigned char unit; /*unsigned int n, m;*/ char *errstring; struct dos_partition *dos_partition_p; - struct sd_data *sd = sd_data + unit; + struct sd_data *sd = sd_data[unit]; /*******************************************************\ * If the inflo is already loaded, use it * @@ -804,6 +745,7 @@ unsigned char unit; /*******************************************************\ * make partition 3 the whole disk in case of failure * * then get pdinfo * + * for historical reasons, make part a same as raw part * \*******************************************************/ sd->disklabel.d_partitions[0].p_offset = 0; sd->disklabel.d_partitions[0].p_size = sd->params.disksize; @@ -819,11 +761,11 @@ unsigned char unit; { sd->disklabel.d_secpercyl = 100; /* as long as it's not 0 */ - /* readdisklabel divides by it */ + /* readdisklabel divides by it (?)*/ } /*******************************************************\ - * all the generic bisklabel extraction routine * + * Call the generic disklabel extraction routine * \*******************************************************/ if(errstring = readdisklabel(makedev(0 ,(unit<flags |= SDHAVELABEL; /* WE HAVE IT ALL NOW */ return(ESUCCESS); @@ -868,10 +807,11 @@ sd_size(unit, flags) sizeof(scsi_cmd), &rdcap, sizeof(rdcap), - 2000, - flags) != 0) + 6000, + NULL, + flags | SCSI_DATA_IN) != 0) { - printf("could not get size of unit %d\n", unit); + printf("sd%d: could not get size\n", unit); return(0); } else { size = rdcap.addr_0 + 1 ; @@ -899,26 +839,31 @@ int unit,flags; 0, 0, 100000, + NULL, flags)); } /*******************************************************\ * Prevent or allow the user to remove the tape * +* Don't change this status if any partitions are open * \*******************************************************/ sd_prevent(unit,type,flags) int unit,type,flags; { struct scsi_prevent scsi_cmd; + if(sd_data[unit]->openparts) return; + bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = PREVENT_ALLOW; - scsi_cmd.prevent=type; + scsi_cmd.how=type; return (sd_scsi_cmd(unit, &scsi_cmd, sizeof(scsi_cmd), 0, 0, 5000, + NULL, flags) ); } /*******************************************************\ @@ -931,7 +876,7 @@ int unit,flags; bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = START_STOP; - scsi_cmd.start = 1; + scsi_cmd.how = SSS_START; return (sd_scsi_cmd(unit, &scsi_cmd, @@ -939,6 +884,7 @@ int unit,flags; 0, 0, 2000, + NULL, flags)); } @@ -968,7 +914,8 @@ sd_reassign_blocks(unit,block) &rbdata, sizeof(rbdata), 5000, - 0)); + NULL, + SCSI_DATA_OUT)); } #define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 ) @@ -981,7 +928,7 @@ sd_reassign_blocks(unit,block) int sd_get_parms(unit, flags) { - struct sd_data *sd = sd_data + unit; + struct sd_data *sd = sd_data[unit]; struct disk_parms *disk_parms = &sd->params; struct scsi_mode_sense scsi_cmd; struct scsi_mode_sense_data @@ -999,11 +946,12 @@ int sd_get_parms(unit, flags) /*******************************************************\ * First do a mode sense page 3 * \*******************************************************/ +#ifdef SDDEBUG if (sd_debug) { bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = MODE_SENSE; - scsi_cmd.page_code = 3; + scsi_cmd.page = 3; scsi_cmd.length = 0x24; /*******************************************************\ * do the command, but we don't need the results * @@ -1015,11 +963,13 @@ int sd_get_parms(unit, flags) &scsi_sense, sizeof(scsi_sense), 2000, - flags) != 0) + NULL, + flags | SCSI_DATA_IN) != 0) { - printf("could not mode sense (3) for unit %d\n", unit); - return(ENXIO); + printf("sd%d: could not mode sense (3)\n", unit); } + else + { printf("unit %d: %d trk/zone, %d alt_sec/zone, %d alt_trk/zone, %d alt_trk/lun\n", unit, b2tol(scsi_sense.pages.disk_format.trk_z), @@ -1032,7 +982,9 @@ int sd_get_parms(unit, flags) b2tol(scsi_sense.pages.disk_format.interleave), sd_size(unit, flags), _3btol(scsi_sense.blk_desc.blklen)); + } } +#endif /*SDDEBUG*/ /*******************************************************\ @@ -1040,7 +992,7 @@ int sd_get_parms(unit, flags) \*******************************************************/ bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = MODE_SENSE; - scsi_cmd.page_code = 4; + scsi_cmd.page = 4; scsi_cmd.length = 0x20; /*******************************************************\ * If the command worked, use the results to fill out * @@ -1052,20 +1004,27 @@ int sd_get_parms(unit, flags) &scsi_sense, sizeof(scsi_sense), 2000, - flags) != 0) + NULL, + flags | SCSI_DATA_IN) != 0) { - printf("could not mode sense (4) for unit %d\n", unit); - printf(" using ficticious geometry\n"); - /* use adaptec standard ficticious geometry */ + printf("sd%d could not mode sense (4).", unit); + printf(" Using ficticious geometry\n"); + /* + * use adaptec standard ficticious geometry + * this depends on which controller (e.g. 1542C is + * different. but we have to put SOMETHING here..) + */ sectors = sd_size(unit, flags); disk_parms->heads = 64; disk_parms->sectors = 32; disk_parms->cyls = sectors/(64 * 32); disk_parms->secsiz = SECSIZE; + disk_parms->disksize = sectors; } else { +#ifdef SDDEBUG if (sd_debug) { printf(" %d cyls, %d heads, %d precomp, %d red_write, %d land_zone\n", @@ -1075,20 +1034,22 @@ int sd_get_parms(unit, flags) b2tol(scsi_sense.pages.rigid_geometry.st_cyl_rwc), b2tol(scsi_sense.pages.rigid_geometry.land_zone)); } +#endif /*SDDEBUG*/ /*******************************************************\ * KLUDGE!!(for zone recorded disks) * * give a number of sectors so that sec * trks * cyls * * is <= disk_size * + * can lead to wasted space! THINK ABOUT THIS ! * \*******************************************************/ disk_parms->heads = scsi_sense.pages.rigid_geometry.nheads; disk_parms->cyls = _3btol(&scsi_sense.pages.rigid_geometry.ncyl_2); disk_parms->secsiz = _3btol(&scsi_sense.blk_desc.blklen); sectors = sd_size(unit, flags); - sectors /= disk_parms->cyls; - sectors /= disk_parms->heads; - disk_parms->sectors = sectors; /* dubious on SCSI*/ + disk_parms->disksize = sectors; + sectors /= (disk_parms->heads * disk_parms->cyls); + disk_parms->sectors = sectors; /* dubious on SCSI*//*XXX*/ } sd->flags |= SDVALID; @@ -1098,22 +1059,110 @@ int sd_get_parms(unit, flags) /*******************************************************\ * close the device.. only called if we are the LAST * * occurence of an open device * +* convenient now but usually a pain * \*******************************************************/ sdclose(dev) dev_t dev; { unsigned char unit, part; unsigned int old_priority; + struct sd_data *sd; unit = UNIT(dev); part = PARTITION(dev); - sd_data[unit].partflags[part] &= ~SDOPEN; - sd_data[unit].openparts &= ~(1 << part); - if(sd_data[unit].openparts == 0) /* if all partitions closed */ + sd = sd_data[unit]; + sd->partflags[part] &= ~SDOPEN; + sd->openparts &= ~(1 << part); + sd_prevent(unit,PR_ALLOW,SCSI_SILENT|SCSI_ERR_OK); + return(0); +} + +/*******************************************************\ +* This routine is called by the scsi interrupt when * +* the transfer is complete. +\*******************************************************/ +int sd_done(unit,xs) +int unit; +struct scsi_xfer *xs; +{ + struct buf *bp; + int retval; + int retries = 0; + +#ifdef SDDEBUG + if(scsi_debug & PRINTROUTINES) printf("sd_done%d ",unit); +#endif /*SDDEBUG*/ +#ifdef PARANOID + if (! (xs->flags & INUSE)) + panic("scsi_xfer not in use!"); +#endif + if((bp = xs->bp) == NULL) { - sd_prevent(unit,PR_ALLOW,SCSI_SILENT|SCSI_ERR_OK); + /***********************************************\ + * if it's a normal user level request, then ask * + * The user level code to handle error checking * + * rather than doing it here at interrupt time * + \***********************************************/ + wakeup(xs); + return; } - return(0); + + /***********************************************\ + * If it has a buf, we might be working with * + * a request from the buffer cache or some other * + * piece of code that requires us to process * + * errors right now, despite cost * + \***********************************************/ + switch(xs->error) + { + case XS_NOERROR: + bp->b_error = 0; + bp->b_resid = 0; + break; + + case XS_SENSE: + retval = (sd_interpret_sense(unit,xs)); + if(retval) + { + bp->b_flags |= B_ERROR; + bp->b_error = retval; + } + break; + + case XS_BUSY: + /*should somehow arange for a 1 sec delay here (how?)*/ + case XS_TIMEOUT: + /***********************************************\ + * If we can, resubmit it to the adapter. * + \***********************************************/ + if(xs->retries--) + { + xs->error = XS_NOERROR; + xs->flags &= ~ITSDONE; + if ( (*(sd_data[unit]->sc_sw->scsi_cmd))(xs) + == SUCCESSFULLY_QUEUED) + { /* don't wake the job, ok? */ + return; + } + xs->flags |= ITSDONE; + } /* fall through */ + + case XS_DRIVER_STUFFUP: + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + break; + default: + printf("sd%d: unknown error category from scsi driver\n" + ,unit); + } + /*******************************\ + * tell the owner we're done * + * then free our resources * + * and see if there's more work * + \*******************************/ + biodone(bp); + sd_free_xs(unit,xs,0); + sdstart(unit); /* If there's anything waiting.. do it */ } /*******************************************************\ @@ -1121,11 +1170,12 @@ dev_t dev; * Call it through the switch table, and tell it which * * sub-unit we want, and what target and lu we wish to * * talk to. Also tell it where to find the command * -* how long int is. * +* and how long it is. * * Also tell it where to read/write the data, and how * -* long the data is supposed to be * +* long the data is supposed to be. If we have a buf * +* to associate with the transfer, we need that too. * \*******************************************************/ -int sd_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags) +int sd_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,bp,flags) int unit,flags; struct scsi_generic *scsi_cmd; @@ -1133,112 +1183,136 @@ int cmdlen; int timeout; u_char *data_addr; int datalen; +struct buf *bp; { struct scsi_xfer *xs; int retval; int s; - struct sd_data *sd = sd_data + unit; + struct sd_data *sd = sd_data[unit]; +#ifdef SDDEBUG if(scsi_debug & PRINTROUTINES) printf("\nsd_scsi_cmd%d ",unit); - if(sd->sc_sw) /* If we have a scsi driver */ +#endif /*SDDEBUG*/ + +#ifdef PARANOID + if(!(sd->sc_sw)) /* If we have a scsi driver */ + {/* How we got here is anyone's guess */ + printf("sd%d: not set up\n",unit); + return(EINVAL); + } +#endif + xs = sd_get_xs(unit,flags); /* should wait unless booting */ +#ifdef PARANOID + if(!xs) { - xs = sd_get_xs(unit,flags); /* should wait unless booting */ - if(!xs) - { - printf("sd_scsi_cmd%d: controller busy" - " (this should never happen)\n",unit); - return(EBUSY); - } - xs->flags |= INUSE; - /*******************************************************\ - * Fill out the scsi_xfer structure * - \*******************************************************/ - xs->flags |= flags; - xs->adapter = sd->ctlr; - xs->targ = sd->targ; - xs->lu = sd->lu; - xs->retries = SD_RETRIES; - xs->timeout = timeout; - xs->cmd = scsi_cmd; - xs->cmdlen = cmdlen; - xs->data = data_addr; - xs->datalen = datalen; - xs->resid = datalen; - xs->when_done = (flags & SCSI_NOMASK) - ?(int (*)())0 - :sd_done; - xs->done_arg = unit; - xs->done_arg2 = (int)xs; -retry: xs->error = XS_NOERROR; - xs->bp = 0; - retval = (*(sd->sc_sw->scsi_cmd))(xs); - switch(retval) - { - case SUCCESSFULLY_QUEUED: - s = splbio(); - while(!(xs->flags & ITSDONE)) - sleep(xs,PRIBIO+1); - splx(s); + printf("sd_scsi_cmd%d: controller busy" + " (this should never happen)\n",unit); + return(EBUSY); + } +#endif + /*******************************************************\ + * Fill out the scsi_xfer structure * + \*******************************************************/ + xs->flags = INUSE | flags; + xs->adapter = sd->ctlr; + xs->targ = sd->targ; + xs->lu = sd->lu; + xs->retries = SD_RETRIES; + xs->timeout = timeout; + xs->cmd = scsi_cmd; + xs->cmdlen = cmdlen; + xs->data = data_addr; + xs->datalen = datalen; + xs->resid = datalen; + xs->when_done = sd_done; + xs->done_arg = unit; + xs->done_arg2 = (int)xs; + xs->bp = bp; +retry: xs->error = XS_NOERROR; - case HAD_ERROR: - /*printf("err = %d ",xs->error);*/ - switch(xs->error) - { - case XS_NOERROR: - retval = ESUCCESS; - break; - case XS_SENSE: - retval = (sd_interpret_sense(unit,xs)); - break; - case XS_DRIVER_STUFFUP: - retval = EIO; - break; - case XS_TIMEOUT: - if(xs->retries-- ) - { - xs->flags &= ~ITSDONE; - goto retry; - } - retval = EIO; - break; - case XS_BUSY: - if(xs->retries-- ) - { - xs->flags &= ~ITSDONE; - goto retry; - } - retval = EIO; - break; - default: - retval = EIO; - printf("sd%d: unknown error category from scsi driver\n" - ,unit); - } - break; - case COMPLETE: + /*******************************************************\ + * Do the transfer. If we are polling we will return: * + * COMPLETE, Was poll, and sd_done has been called * + * HAD_ERROR, Was poll and an error was encountered * + * TRY_AGAIN_LATER, Adapter short resources, try again * + * * + * if under full steam (interrupts) it will return: * + * SUCCESSFULLY_QUEUED, will do a wakeup when complete * + * HAD_ERROR, had an erro before it could queue * + * TRY_AGAIN_LATER, (as for polling) * + * After the wakeup, we must still check if it succeeded * + * * + * If we have a bp however, all the error proccessing * + * and the buffer code both expect us to return straight * + * to them, so as soon as the command is queued, return * + \*******************************************************/ + retval = (*(sd->sc_sw->scsi_cmd))(xs); + if(bp) return retval; /* will sleep (or not) elsewhere */ + + /*******************************************************\ + * Only here for non I/O cmds. It's cheaper to process * + * the error status here than at interrupt time so * + * sd_done will have done nothing except wake us up. * + \*******************************************************/ + switch(retval) + { + case SUCCESSFULLY_QUEUED: + s = splbio(); + while(!(xs->flags & ITSDONE)) + sleep(xs,PRIBIO+1); + splx(s); + /* fall through to check success of completed command */ + + case HAD_ERROR: + switch(xs->error) + { + case XS_NOERROR: /* nearly always hit this one */ retval = ESUCCESS; break; - case TRY_AGAIN_LATER: + + case XS_SENSE: + retval = (sd_interpret_sense(unit,xs)); + break; + case XS_BUSY: + /* should sleep 1 sec here */ + case XS_TIMEOUT: if(xs->retries-- ) { xs->flags &= ~ITSDONE; goto retry; } + case XS_DRIVER_STUFFUP: retval = EIO; break; default: retval = EIO; + printf("sd%d: unknown error category from scsi driver\n" + ,unit); + } + break; + case COMPLETE: /* Polling command completed ok */ + retval = ESUCCESS; + break; + + case TRY_AGAIN_LATER: /* adapter resource shortage */ + /* should sleep 1 sec here */ + if(xs->retries-- ) + { + xs->flags &= ~ITSDONE; + goto retry; } - sd_free_xs(unit,xs,flags); - sdstart(unit); /* check if anything is waiting fr the xs */ - } - else - { - printf("sd%d: not set up\n",unit); - return(EINVAL); + default: + retval = EIO; } + /*******************************************************\ + * we have finished with the xfer stuct, free it and * + * check if anyone else needs to be started up. * + \*******************************************************/ + sd_free_xs(unit,xs,flags); + sdstart(unit); /* check queue */ return(retval); } + /***************************************************************\ * Look at the returned sense and act on the error and detirmine * * The unix error number to pass back... (0 = report no error) * @@ -1251,6 +1325,7 @@ struct scsi_xfer *xs; struct scsi_sense_data *sense; int key; int silent; + long int info; /***************************************************************\ * If the flags say errs are ok, then always return ok. * @@ -1259,11 +1334,15 @@ struct scsi_xfer *xs; silent = (xs->flags & SCSI_SILENT); sense = &(xs->sense); - switch(sense->error_class) + info = ((sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + switch(sense->error_code & SSD_ERRCODE) { - case 7: + case 0x70: { - key=sense->ext.extended.sense_key; + key=sense->ext.extended.flags & SSD_KEY; switch(key) { case 0x0: @@ -1272,13 +1351,9 @@ struct scsi_xfer *xs; if(!silent) { printf("sd%d: soft error(corrected) ", unit); - if(sense->valid) + if(sense->error_code & SSD_ERRCODE_VALID) { - printf("block no. %d (decimal)", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); + printf("block no. %d (decimal)",info); } printf("\n"); } @@ -1291,13 +1366,9 @@ struct scsi_xfer *xs; if(!silent) { printf("sd%d: medium error ", unit); - if(sense->valid) + if(sense->error_code & SSD_ERRCODE_VALID) { - printf("block no. %d (decimal)", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); + printf("block no. %d (decimal)",info); } printf("\n"); } @@ -1317,8 +1388,8 @@ struct scsi_xfer *xs; * sure that we don't have any residual state * \***********************************************/ if(!silent)printf("sd%d: Unit attention.\n ", unit); - sd_data[unit].flags &= ~(SDVALID | SDHAVELABEL); - if (sd_data[unit].openparts) + sd_data[unit]->flags &= ~(SDVALID | SDHAVELABEL); + if (sd_data[unit]->openparts) { return(EIO); } @@ -1328,13 +1399,9 @@ struct scsi_xfer *xs; { printf("sd%d: attempted protection violation ", unit); - if(sense->valid) + if(sense->error_code & SSD_ERRCODE_VALID) { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); + printf("block no. %d (decimal)\n",info); } printf("\n"); } @@ -1342,15 +1409,11 @@ struct scsi_xfer *xs; case 0x8: if(!silent) { - printf("sd%d: block wrong state (worm)\n ", + printf("sd%d: block wrong state (format?)\n ", unit); - if(sense->valid) + if(sense->error_code & SSD_ERRCODE_VALID) { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); + printf("block no. %d (decimal)\n",info); } printf("\n"); } @@ -1372,13 +1435,9 @@ struct scsi_xfer *xs; { printf("sd%d: search returned\n ", unit); - if(sense->valid) + if(sense->error_code & SSD_ERRCODE_VALID) { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); + printf("block no. %d (decimal)\n",info); } printf("\n"); } @@ -1392,13 +1451,9 @@ struct scsi_xfer *xs; { printf("sd%d: verify miscompare\n ", unit); - if(sense->valid) + if(sense->error_code & SSD_ERRCODE_VALID) { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); + printf("block no. %d (decimal)\n",info); } printf("\n"); } @@ -1410,19 +1465,12 @@ struct scsi_xfer *xs; } break; } - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: + default: { - if(!silent)printf("sd%d: error class %d code %d\n", + if(!silent)printf("sd%d: code %d\n", unit, - sense->error_class, - sense->error_code); - if(sense->valid) + sense->error_code & SSD_ERRCODE); + if(sense->error_code & SSD_ERRCODE_VALID) if(!silent)printf("block no. %d (decimal)\n", (sense->ext.unextended.blockhi <<16) + (sense->ext.unextended.blockmed <<8) @@ -1444,7 +1492,8 @@ sdsize(dev_t dev) if (unit >= NSD) return(-1); - sd = &sd_data[unit]; + sd = sd_data[unit]; + if(!sd) return(-1); if((sd->flags & SDINIT) == 0) return(-1); if (sd == 0 || (sd->flags & SDHAVELABEL) == 0) val = sdopen (MAKESDDEV(major(dev), unit, RAW_PART), FREAD, S_IFBLK, 0); @@ -1453,14 +1502,146 @@ sdsize(dev_t dev) return((int)sd->disklabel.d_partitions[part].p_size); } - -sddump() +/*#define SCSIDUMP*/ +#ifdef SCSIDUMP +#include +/***********************************************************************\ +* dump all of physical memory into the partition specified, starting * +* at offset 'dumplo' into the partition. * +\***********************************************************************/ +static struct scsi_xfer sx; +#define MAXTRANSFER 8 /* 1 page at a time */ +int +sddump(dev_t dev) /* dump core after a system crash */ { - printf("sddump() -- not implemented\n"); - return(-1); -} + register struct sd_data *sd; /* disk unit to do the IO */ + long num; /* number of sectors to write */ + int unit, part, sdc; + long blkoff, blknum, blkcnt; + long nblocks; + char *addr; + struct scsi_rw_big cmd; + extern int Maxmem; + static sddoingadump = 0 ; + extern caddr_t CADDR1; /* map the page we are about to write, here*/ + struct scsi_xfer *xs = &sx; + int retval; + + addr = (char *) 0; /* starting address */ + + /* toss any characters present prior to dump */ + while (sgetc(1)) + ; + + /* size of memory to dump */ + num = Maxmem; + unit = UNIT(dev); /* eventually support floppies? */ + part = PARTITION(dev); /* file system */ + /* check for acceptable drive number */ + if (unit >= NSD) return(ENXIO); /* 31 Jul 92*/ + + sd = sd_data[unit]; + if(!sd) return (ENXIO); + /* was it ever initialized etc. ? */ + if (!(sd->flags & SDINIT)) return (ENXIO); + if (sd->flags & SDVALID != SDVALID) return (ENXIO) ; + if (sd->flags & SDWRITEPROT) return (ENXIO); + /* Convert to disk sectors */ + num = (u_long) num * NBPG / sd->disklabel.d_secsize; + /* check if controller active */ + if (sddoingadump) return(EFAULT); + nblocks = sd->disklabel.d_partitions[part].p_size; + blkoff = sd->disklabel.d_partitions[part].p_offset; + /* check transfer bounds against partition size */ + if ((dumplo < 0) || ((dumplo + num) > nblocks)) + return(EINVAL); + + sddoingadump = 1 ; + + blknum = dumplo + blkoff; + while (num > 0) + { + if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER; + pmap_enter( kernel_pmap, + CADDR1, + trunc_page(addr), + VM_PROT_READ, + TRUE); +#ifndef NOT_TRUSTED + /*******************************************************\ + * Fill out the scsi command * + \*******************************************************/ + bzero(&cmd, sizeof(cmd)); + cmd.op_code = WRITE_BIG; + cmd.addr_3 = (blknum & 0xff000000) >> 24; + cmd.addr_2 = (blknum & 0xff0000) >> 16; + cmd.addr_1 = (blknum & 0xff00) >> 8; + cmd.addr_0 = blknum & 0xff; + cmd.length2 = (blkcnt & 0xff00) >> 8; + cmd.length1 = (blkcnt & 0xff); + /*******************************************************\ + * Fill out the scsi_xfer structure * + * Note: we cannot sleep as we may be an interrupt * + \*******************************************************/ + bzero(xs, sizeof(sx)); + xs->flags |= SCSI_NOMASK|SCSI_NOSLEEP|INUSE; + xs->adapter = sd->ctlr; + xs->targ = sd->targ; + xs->lu = sd->lu; + xs->retries = SD_RETRIES; + xs->timeout = 10000;/* 10000 millisecs for a disk !*/ + xs->cmd = (struct scsi_generic *)&cmd; + xs->cmdlen = sizeof(cmd); + xs->resid = blkcnt * 512; + xs->when_done = 0; + xs->done_arg = unit; + xs->done_arg2 = (int)xs; + xs->error = XS_NOERROR; + xs->bp = 0; + xs->data = (u_char *)CADDR1; + xs->datalen = blkcnt * 512; + + /*******************************************************\ + * Pass all this info to the scsi driver. * + \*******************************************************/ + retval = (*(sd->sc_sw->scsi_cmd))(xs); + switch(retval) + { + case SUCCESSFULLY_QUEUED: + case HAD_ERROR: + return(ENXIO); /* we said not to sleep! */ + case COMPLETE: + break; + default: + return(ENXIO); /* we said not to sleep! */ + } +#else NOT_TRUSTED + /* lets just talk about this first...*/ + printf ("sd%d: dump addr 0x%x, blk %d\n",unit,addr,blknum); +#endif NOT_TRUSTED + + if ((unsigned)addr % (1024*1024) == 0) printf("%d ", num/2048) ; + /* update block count */ + num -= MAXTRANSFER; + blknum += MAXTRANSFER ; + (int) addr += 512 * MAXTRANSFER; + + /* operator aborting dump? */ + if (sgetc(1)) + return(EINTR); + } + return(0); +} +#else SCSIDUMP +sddump() +{ + printf("\nsddump() -- not implemented\n"); + DELAY(20000000); /* 20 seconds */ + return(-1); +} +#endif SCSIDUMP diff --git a/sys/scsi/sg.c b/sys/scsi/sg.c new file mode 100644 index 0000000000..b0ab406e11 --- /dev/null +++ b/sys/scsi/sg.c @@ -0,0 +1,760 @@ +/* + * Contributed by HD Associates (hd@world.std.com). + * Copyright (c) 1992, 1993 HD Associates + * + * Berkeley style copyright. I've just snarfed it out of stdio.h: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)stdio.h 5.17 (Berkeley) 6/3/91 + * $Id$ + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "sg.h" +#include + +#define SGOUTSTANDING 2 +#define SG_RETRIES 2 +#define SPLSG splbio + +/* Use one of the implementation defined spare bits + * to indicate the escape op: + */ +#define DSRQ_ESCAPE DSRQ_CTRL1 + +struct sg +{ + int flags; + struct scsi_switch *sc_sw; + int ctlr; + + long int ad_info; /* info about the adapter */ + int cmdscount; /* cmds allowed outstanding by the board */ + + struct scsi_xfer *free_xfer; + int free_xfer_wait; +}; + +/* This is used to associate a struct dsreq and a struct buf. + */ +typedef struct dsbuf +{ + dsreq_t *dsreq; + struct buf buf; + + /* I think this is a portable way to get back to the base of + * the enclosing structure: + */ +# define DSBUF_P(BP) ((dsbuf_t *)((caddr_t)(BP) - (caddr_t)&((dsbuf_t *)0)->buf)) + + int magic; + +# define DSBUF_MAGIC 0xDBFACDBF +} dsbuf_t; + +#if NSG > 4 +/* The host adapter unit is encoded in the upper 2 bits of the minor number + * (the SGI flag bits). + */ +#error "NSG can't be > 4 unless the method of encoding the board unit changes" +#endif + +struct sg *sgs[NSG]; + +#define SG(DEV) sgs[G_SCSI_UNIT(DEV)] + +struct sg *sg_new(int lun) +{ + struct sg *sg = (struct sg *)malloc(sizeof(*sg),M_TEMP, M_NOWAIT); + + if (sg == 0) + return 0; + + bzero(sg, sizeof(struct sg)); + + return sg; +} + +int sg_attach(ctlr, scsi_addr, scsi_switch) +int ctlr,scsi_addr; +struct scsi_switch *scsi_switch; +{ + struct sg *sg; + int i; + struct scsi_xfer *scsi_xfer; + static int next_sg_unit = 0; + + int unit = next_sg_unit++; + + if (unit >= NSG) + { + printf("Too many generic SCSIs (%d > %d); reconfigure the kernel.\n", + unit+1, NSG); + + if (NSG == 4) + printf( + "You have hit the max of 4. You will have to change the driver.\n"); + + return 0; + } + + if ((sg = sg_new(0)) == 0) + return 0; + + sgs[unit] = sg; + + sg->sc_sw = scsi_switch; + sg->ctlr = ctlr; + + /* This is a bit confusing. It looks like Julian calls back into the + * adapter to find out how many outstanding transactions it can + * handle. How does he handle a tape/disk combo? + */ + + if (sg->sc_sw->adapter_info) + { + sg->ad_info = ( (*(sg->sc_sw->adapter_info))(unit)); + sg->cmdscount = sg->ad_info & AD_INF_MAX_CMDS; + if(sg->cmdscount > SGOUTSTANDING) + sg->cmdscount = SGOUTSTANDING; + } + else + { + sg->ad_info = 1; + sg->cmdscount = 1; + } + + i = sg->cmdscount; + + scsi_xfer = (struct scsi_xfer *)malloc(sizeof(struct scsi_xfer) * + i, M_TEMP, M_NOWAIT); + + if (scsi_xfer == 0) + { + printf("scsi_generic: Can't malloc.\n"); + return 0; + } + + while (i--) + { + scsi_xfer->next = sg->free_xfer; + sg->free_xfer = scsi_xfer; + scsi_xfer++; + } + +#ifndef EMBEDDED + if (unit == 0) + printf(" /dev/gs%d (instance 0) generic SCSI via controller %d\n", + scsi_addr, sg->ctlr); + else + printf(" /dev/gs%d-%d generic SCSI via controller %d\n", + unit, scsi_addr, sg->ctlr); +#endif + + return 1; +} + +/* It is trivial to add support for processor target devices + * here - enable target mode on open and disable on close + * if a flag bit is set in the minor number + */ +int sgopen(dev_t dev) +{ + if (SG(dev) == 0) + return ENXIO; + + return 0; +} + +int sgclose(dev_t dev) +{ + return 0; +} + + +/* Free a scsi_xfer, wake processes waiting for it + */ +void sg_free_xs(dev_t dev, struct scsi_xfer *xs, int flags) +{ + int s; + struct sg *sg = SG(dev); + + if(flags & SCSI_NOMASK) + { + if (sg->free_xfer_wait) + { + printf("sg_free_xs: doing a wakeup from NOMASK mode!\n"); + wakeup((caddr_t)&sg->free_xfer); + } + xs->next = sg->free_xfer; + sg->free_xfer = xs; + } + else + { + s = SPLSG(); + if (sg->free_xfer_wait) + wakeup((caddr_t)&sg->free_xfer); + xs->next = sg->free_xfer; + sg->free_xfer = xs; + splx(s); + } +} + +/* Get ownership of a scsi_xfer + * If need be, sleep on it, until it comes free + */ +struct scsi_xfer *sg_get_xs(dev_t dev, int flags) +{ + struct scsi_xfer *xs; + int s; + struct sg *sg = SG(dev); + + if(flags & (SCSI_NOSLEEP | SCSI_NOMASK)) + { + if (xs = sg->free_xfer) + { + sg->free_xfer = xs->next; + xs->flags = 0; + } + } + else + { + s = SPLSG(); + while (!(xs = sg->free_xfer)) + { + sg->free_xfer_wait++; /* someone waiting! */ + sleep((caddr_t)&sg->free_xfer, PRIBIO+1); + sg->free_xfer_wait--; + } + sg->free_xfer = xs->next; + splx(s); + xs->flags = 0; + } + + return xs; +} + +/* We let the user interpret his own sense in the + * generic scsi world + */ +int sg_interpret_sense(dev_t dev, struct scsi_xfer *xs, int *flag_p) +{ + return 0; +} + +/* ITSDONE is really used for things that are marked one + * in the interrupt. I'll leave the logic in in case I want + * to move done processing (and therefore have a start queue) + * back into the interrupt. + * BUG: No start queue. + */ + +int sg_done(dev_t dev, +struct scsi_xfer *xs) +{ + xs->flags |= ITSDONE; + wakeup(xs); + return 0; +} + +int sg_submit_cmd(dev_t dev, struct scsi_xfer *xs, dsreq_t *dsreq) +{ + int retval; + + struct sg *sg = SG(dev); + +retry: + xs->error = XS_NOERROR; + + xs->bp = 0; /* This bp doesn't seem to be used except to + * disable sleeping in the host adapter code. + * "st" does set it up, though. + */ + + retval = (*(sg->sc_sw->scsi_cmd))(xs); + + switch(retval) + { + case SUCCESSFULLY_QUEUED: + while(!(xs->flags & ITSDONE)) + sleep(xs,PRIBIO+1); + + /* Fall through... */ + + case HAD_ERROR: + + if (dsreq) + dsreq->ds_status = xs->status; + + switch(xs->error) + { + case XS_NOERROR: + if (dsreq) + dsreq->ds_datasent = dsreq->ds_datalen - xs->resid; + retval = 0; + break; + + case XS_SENSE: + retval = (sg_interpret_sense(dev ,xs, (int *)0)); + if (dsreq) + { + dsreq->ds_sensesent = sizeof(xs->sense); + dsreq->ds_ret = DSRT_SENSE; + } + retval = 0; + break; + + case XS_DRIVER_STUFFUP: + if (dsreq) + dsreq->ds_ret = DSRT_HOST; + printf("sg%d: host adapter code inconsistency\n" ,G_SCSI_UNIT(dev)); + retval = EIO; + break; + + case XS_TIMEOUT: + if (dsreq) + dsreq->ds_ret = DSRT_TIMEOUT; + retval = ETIMEDOUT; + break; + + case XS_BUSY: + if(xs->retries-- ) + { + xs->flags &= ~ITSDONE; + goto retry; + } + retval = EBUSY; + break; + + default: + printf("sg%d: unknown error category from host adapter code\n" + ,G_SCSI_UNIT(dev)); + retval = EIO; + break; + } + break; + + case COMPLETE: + if (dsreq) + dsreq->ds_datasent = dsreq->ds_datalen - xs->resid; + retval = 0; + break; + + case TRY_AGAIN_LATER: + if(xs->retries-- ) + { + xs->flags &= ~ITSDONE; + goto retry; + } + retval = EBUSY; + break; + + case ESCAPE_NOT_SUPPORTED: + retval = ENOSYS; /* "Function not implemented" */ + break; + + default: + printf("sg%d: illegal return from host adapter code\n", + G_SCSI_UNIT(dev)); + retval = EIO; + break; + } + + return retval; +} + +/* sg_escape: Do a generic SCSI escape + */ +int sg_escape(dev_t dev, int op_code, u_char *b, int nb) +{ + int retval; + + struct scsi_generic scsi_generic; + + int flags = SCSI_ESCAPE; + + struct scsi_xfer *xs; + struct sg *sg = SG(dev); + + xs = sg_get_xs(dev, flags); + + if (xs == 0) + { + printf("sg_target%d: controller busy" + " (this should never happen)\n",G_SCSI_UNIT(dev)); + return EBUSY; + } + + scsi_generic.opcode = op_code; + bcopy(b, scsi_generic.bytes, nb); + + /* Fill out the scsi_xfer structure + */ + xs->flags = (flags|INUSE); + xs->adapter = sg->ctlr; + xs->cmd = &scsi_generic; + xs->targ = G_SCSI_ID(dev); + xs->lu = G_SCSI_LUN(dev); + xs->retries = SG_RETRIES; + xs->timeout = 100; + xs->when_done = (flags & SCSI_NOMASK) + ?(int (*)())0 + :(int (*)())sg_done; + xs->done_arg = dev; + xs->done_arg2 = (int)xs; + + xs->status = 0; + + retval = sg_submit_cmd(dev, xs, 0); + + bcopy(scsi_generic.bytes, b, nb); + + sg_free_xs(dev,xs,flags); + + return retval; +} + +/* sg_target: Turn on / off target mode + */ +int sg_target(dev_t dev, int enable) +{ + u_char b0 = enable; + return sg_escape(dev, SCSI_OP_TARGET, &b0, 1); +} + +#ifdef EMBEDDED +/* This should REALLY be a select call! + * This is used in a stand alone system without an O/S. I didn't + * have the time to add select, which the system was missing, + * so I added this stuff to poll for the async arrival of + * connections for target mode. + */ +int sg_poll(dev_t dev, int *send, int *recv) +{ + scsi_op_poll_t s; + int ret; + + ret = sg_escape(dev, SCSI_OP_POLL, (u_char *)&s, sizeof(s)); + + if (ret == 0) + { + *send = s.send; + *recv = s.recv; + } + + return ret; +} +#endif /* EMBEDDED */ + +int sg_scsi_cmd(dev_t dev, +dsreq_t *dsreq, +struct scsi_generic *scsi_cmd, +u_char *d_addr, +long d_count, +struct scsi_sense_data *scsi_sense) +{ + int retval; + + int flags = 0; + struct scsi_xfer *xs; + struct sg *sg = SG(dev); + + if (sg->sc_sw == 0) + return ENODEV; + + dsreq->ds_status = 0; + dsreq->ds_sensesent = 0; + + if (dsreq->ds_flags & DSRQ_READ) + flags |= SCSI_DATA_IN; + + if (dsreq->ds_flags & DSRQ_WRITE) + flags |= SCSI_DATA_OUT; + + if (dsreq->ds_flags & DSRQ_TARGET) + flags |= SCSI_TARGET; + + if (dsreq->ds_flags & DSRQ_ESCAPE) + flags |= SCSI_ESCAPE; + +#ifdef SCSI_PHYSADDR + if (dsreq->ds_flags & DSRQ_PHYSADDR) + flags |= SCSI_PHYSADDR; +#endif + + xs = sg_get_xs(dev, flags); + + if (xs == 0) + { + printf("sg_scsi_cmd%d: controller busy" + " (this should never happen)\n",G_SCSI_UNIT(dev)); + + return EBUSY; + } + + /* Fill out the scsi_xfer structure + */ + xs->flags |= (flags|INUSE); + xs->adapter = sg->ctlr; + xs->targ = G_SCSI_ID(dev); + xs->lu = G_SCSI_LUN(dev); + xs->retries = SG_RETRIES; + xs->timeout = dsreq->ds_time; + xs->cmd = scsi_cmd; + xs->cmdlen = dsreq->ds_cmdlen; + xs->data = d_addr; + xs->datalen = d_count; + xs->resid = d_count; + xs->when_done = (flags & SCSI_NOMASK) + ?(int (*)())0 + :(int (*)())sg_done; + xs->done_arg = dev; + xs->done_arg2 = (int)xs; + + xs->req_sense_length = (dsreq->ds_senselen < sizeof(struct scsi_sense_data)) + ? dsreq->ds_senselen + : sizeof(struct scsi_sense_data); + xs->status = 0; + + retval = sg_submit_cmd(dev, xs, dsreq); + + if (dsreq->ds_ret == DSRT_SENSE) + bcopy(&(xs->sense), scsi_sense, sizeof(xs->sense)); + + sg_free_xs(dev,xs,flags); + + return retval; +} + +void sgerr(struct buf *bp, int err) +{ + bp->b_error = err; + bp->b_flags |= B_ERROR; + + iodone(bp); +} + +/* strategy function + * + * Should I reorganize this so it returns to physio instead + * of sleeping in sg_scsi_cmd? Is there any advantage, other + * than avoiding the probable duplicate wakeup in iodone? + * + * Don't create a block device entry point for this + * driver without making some fixes: + * you have to be able to go from the bp to the dsreq somehow. + */ +void sgstrategy(struct buf *bp) +{ + int err; + struct scsi_generic scsi_generic; + struct scsi_sense_data scsi_sense; + int lun = G_SCSI_LUN(bp->b_dev); + + dsbuf_t *dsbuf = DSBUF_P(bp); + dsreq_t *dsreq; + + if (dsbuf->magic != DSBUF_MAGIC) + { + printf("sgstrategy: struct buf not magic.\n"); + sgerr(bp, EFAULT); + return; + } + + dsreq = dsbuf->dsreq; + + /* We're in trouble if physio tried to break up the + * transfer: + */ + if (bp->b_bcount != dsreq->ds_datalen) + { + printf("sgstrategy unit%d: Transfer broken up.\n", + G_SCSI_UNIT(bp->b_dev)); + sgerr(bp, EIO); + return; + } + + dsreq->ds_ret = DSRT_OK; + + /* Reject 0 length timeouts. + */ + if (dsreq->ds_time == 0) + { + sgerr(bp, EINVAL); + return; + } + + if (dsreq->ds_cmdlen > sizeof(struct scsi_generic)) + { + sgerr(bp, EFAULT); + return; + } + + copyin(dsreq->ds_cmdbuf, (char *)&scsi_generic, dsreq->ds_cmdlen); + + /* Use device unit for the LUN. Using the one the user provided + * would be a huge security problem. + */ + if ((dsreq->ds_flags & DSRQ_ESCAPE) == 0) + scsi_generic.bytes[0] = (scsi_generic.bytes[0] & 0x1F) | (lun << 5); + + err = sg_scsi_cmd(bp->b_dev, dsreq, + &scsi_generic, + (u_char *)bp->b_un.b_addr, + bp->b_bcount, + &scsi_sense); + + if (dsreq->ds_sensesent) + { + if (dsreq->ds_sensesent > dsreq->ds_senselen) + dsreq->ds_sensesent = dsreq->ds_senselen; + + copyout(&scsi_sense, dsreq->ds_sensebuf, dsreq->ds_sensesent); + } + + if (err) + { + if (dsreq->ds_ret == DSRT_OK) + dsreq->ds_ret = DSRT_DEVSCSI; + + sgerr(bp, err); + return; + } + + /* This is a fake. It would be nice to know if the + * command was sent or not instead of pretending it was if + * we get this far. That would involve adding "sent" members + * to the xs so it could be set up down in the host adapter code. + */ + dsreq->ds_cmdsent = dsreq->ds_cmdlen; + + if (dsreq->ds_ret == 0) + dsreq->ds_ret = DSRT_OK; + + iodone(bp); /* Shouldn't this iodone be done in the interrupt? + */ + + return; +} + +void sgminphys(struct buf *bp) +{ +} + +int sgioctl(dev_t dev, int cmd, caddr_t addr, int f) +{ + int ret = 0; + int phys; + + switch(cmd) + { + case DS_ENTER: + { + dsreq_t *dsreq = (dsreq_t *)addr; + + int rwflag = (dsreq->ds_flags & DSRQ_READ) ? B_READ : B_WRITE; + + struct dsbuf dsbuf; + struct buf *bp = &dsbuf.buf; + + bzero(&dsbuf, sizeof(dsbuf)); + + dsbuf.dsreq = dsreq; + dsbuf.magic = DSBUF_MAGIC; + +#ifdef SCSI_PHYSADDR /* Physical memory addressing option */ + phys = (dsreq->ds_flags & DSRQ_PHYSADDR); +#else + phys = 0; +#endif + + if (phys) + { + bp->b_un.b_addr = dsreq->ds_databuf; + bp->b_bcount = dsreq->ds_datalen; + bp->b_dev = dev; + bp->b_flags = rwflag; + + sgstrategy(bp); + ret = bp->b_error; + } + else if (dsreq->ds_datalen) + { + struct uio uio; + struct iovec iovec; + + iovec.iov_base = dsreq->ds_databuf; + iovec.iov_len = dsreq->ds_datalen; + + uio.uio_offset = 0; + uio.uio_resid = dsreq->ds_datalen; + + uio.uio_segflg = UIO_USERSPACE; + uio.uio_procp = curproc; + uio.uio_rw = (rwflag == B_READ) ? UIO_READ : UIO_WRITE; + uio.uio_iov = &iovec; + uio.uio_iovcnt = 1; + + if ((ret = rawio(dev, &uio, bp)) == 0) + ret = bp->b_error; + } + else + { + bp->b_un.b_addr = 0; + bp->b_bcount = 0; + bp->b_dev = dev; + bp->b_flags = 0; + + sgstrategy(bp); + ret = bp->b_error; + } + + } + break; + + case DS_TARGET: + ret = sg_target(dev, *(int *)addr); + break; + + default: + ret = ENOTTY; + break; + } + + return ret; +} diff --git a/sys/scsi/st.c b/sys/scsi/st.c index e8969025be..e367389144 100644 --- a/sys/scsi/st.c +++ b/sys/scsi/st.c @@ -1,5 +1,5 @@ /* - * Written by Julian Elischer (julian@tfs.com) + * Written by Julian Elischer (julian@tfs.com)(now julian@DIALix.oz.au) * for TRW Financial Systems for use under the MACH(2.5) operating system. * * TRW Financial Systems, in accordance with their agreement with Carnegie @@ -12,17 +12,10 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- - * - * 16 Feb 93 Julian Elischer ADDED for SCSI system - */ - -/* * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * major changes by Julian Elischer (julian@jules.dialix.oz.au) May 1993 + * + * $Id: st.c,v 1.11 1993/10/16 17:21:10 rgrimes Exp $ */ @@ -39,16 +32,15 @@ #include #include +#include #include #include +#include #include #include #include #include -#if defined(OSF) -#define SECSIZE 512 -#endif /* defined(OSF) */ #include #include @@ -57,8 +49,10 @@ long int ststrats,stqueues; - +/* Defines for device specific stuff */ +#define PAGE_0_SENSE_DATA_SIZE 12 #define PAGESIZ 4096 +#define DEF_FIXED_BSIZE 512 #define STQSIZE 4 #define ST_RETRIES 4 @@ -67,62 +61,151 @@ long int ststrats,stqueues; #define DSTY(z) ( ((minor(z) >> 2) & 0x03) ) #define UNIT(z) ( (minor(z) >> 4) ) -#define DSTY_QIC120 3 -#define DSTY_QIC150 2 -#define DSTY_QIC525 1 - -#define QIC120 0x0f -#define QIC150 0x10 -#define QIC525 0x11 - +#define DSTY3 3 +#define DSTY2 2 +#define DSTY1 1 +#define SCSI_2_MAX_DENSITY_CODE 0x17 /* maximum density code specified + in SCSI II spec. */ +/***************************************************************\ +* Define various devices that we know mis-behave in some way, * +* and note how they are bad, so we can correct for them * +\***************************************************************/ +struct modes +{ + int quirks; /* same definitions as in rogues */ + char density; + char spare[3]; +}; +struct rogues +{ + char *name; + char *manu; + char *model; + char *version; + int quirks; /* valid for all modes */ + struct modes modes[4]; +}; + +/* define behaviour codes (quirks) */ +#define ST_Q_NEEDS_PAGE_0 0x00001 +#define ST_Q_FORCE_FIXED_MODE 0x00002 +#define ST_Q_FORCE_VAR_MODE 0x00004 +#define ST_Q_SNS_HLP 0x00008 /* must do READ for good MODE SENSE */ +#define ST_Q_IGNORE_LOADS 0x00010 +#define ST_Q_BLKSIZ 0x00020 /* variable-block media_blksiz > 0 */ + +static struct rogues gallery[] = /* ends with an all null entry */ +{ + { "Such an old device ", "pre-scsi", " unknown model ","????", + 0, + { {ST_Q_FORCE_FIXED_MODE,0}, /* minor 0,1,2,3 */ + {ST_Q_FORCE_FIXED_MODE,QIC_24}, /* minor 4,5,6,7 */ + {ST_Q_FORCE_VAR_MODE,HALFINCH_1600}, /* minor 8,9,10,11*/ + {ST_Q_FORCE_VAR_MODE,HALFINCH_6250} /* minor 12,13,14,15*/ + } + }, + { "Tandberg tdc3600", "TANDBERG", " TDC 3600","????", + ST_Q_NEEDS_PAGE_0, + { {0,0}, /* minor 0,1,2,3*/ + {ST_Q_FORCE_VAR_MODE,QIC_525}, /* minor 4,5,6,7*/ + {0,QIC_150}, /* minor 8,9,10,11*/ + {0,QIC_120} /* minor 12,13,14,15*/ + } + }, + { "Rev 5 of the Archive 2525", "ARCHIVE ", "VIPER 2525 25462","-005", + 0, + { {ST_Q_SNS_HLP,0}, /* minor 0,1,2,3*/ + {ST_Q_SNS_HLP,QIC_525}, /* minor 4,5,6,7*/ + {0,QIC_150}, /* minor 8,9,10,11*/ + {0,QIC_120} /* minor 12,13,14,15*/ + } + }, + { "Archive Viper 150", "ARCHIVE ", "VIPER 150","????", + ST_Q_NEEDS_PAGE_0, + { {0,0}, /* minor 0,1,2,3*/ + {0,QIC_150}, /* minor 4,5,6,7*/ + {0,QIC_120}, /* minor 8,9,10,11*/ + {0,QIC_24} /* minor 12,13,14,15*/ + } + }, + { "Wangtek 5525ES", "WANGTEK ", "5525ES SCSI REV7", "????", + 0, + { {0,0}, /* minor 0,1,2,3*/ + {ST_Q_BLKSIZ,QIC_525}, /* minor 4,5,6,7*/ + {0,QIC_150}, /* minor 8,9,10,11*/ + {0,QIC_120} /* minor 12,13,14,15*/ + } + }, + {(char *)0} +}; -#ifndef __386BSD__ -struct buf stbuf[NST][STQSIZE]; /* buffer for raw io (one per device) */ -struct buf *stbuf_free[NST]; /* queue of free buffers for raw io */ -#endif __386BSD__ -struct buf st_buf_queue[NST]; int ststrategy(); void stminphys(); -struct scsi_xfer st_scsi_xfer[NST]; -int st_xfer_block_wait[NST]; -#if defined(OSF) -caddr_t st_window[NST]; -#endif /* defined(OSF) */ -#ifndef MACH #define ESUCCESS 0 -#endif MACH -int st_info_valid[NST]; /* the info about the device is valid */ -int st_initialized[NST] ; -int st_debug = 0; +#ifdef STDEBUG +int st_debug = 1; +#endif /*STDEBUG*/ int stattach(); int st_done(); + struct st_data { - int flags; - struct scsi_switch *sc_sw; /* address of scsi low level switch */ - int ctlr; /* so they know which one we want */ - int targ; /* our scsi target ID */ - int lu; /* out scsi lu */ - int blkmin; /* min blk size */ - int blkmax; /* max blk size */ - int numblks; /* nominal blocks capacity */ - int blksiz; /* nominal block size */ -}st_data[NST]; -#define ST_OPEN 0x01 -#define ST_NOREWIND 0x02 -#define ST_WRITTEN 0x04 -#define ST_FIXEDBLOCKS 0x10 -#define ST_AT_FILEMARK 0x20 -#define ST_AT_EOM 0x40 - -#define ST_PER_ACTION (ST_AT_FILEMARK | ST_AT_EOM) -#define ST_PER_OPEN (ST_OPEN | ST_NOREWIND | ST_WRITTEN | ST_PER_ACTION) -#define ST_PER_MEDIA ST_FIXEDBLOCKS +/*--------------------present operating parameters, flags etc.----------------*/ + int flags; /* see below */ + int blksiz; /* blksiz we are using */ + int density; /* present density */ + int quirks; /* quirks for the open mode */ + int last_dsty; /* last density used */ +/*--------------------device/scsi parameters----------------------------------*/ + struct scsi_switch *sc_sw; /* address of scsi low level switch */ + int ctlr; /* so they know which one we want */ + int targ; /* our scsi target ID */ + int lu; /* our scsi lu */ +/*--------------------parameters reported by the device ----------------------*/ + int blkmin; /* min blk size */ + int blkmax; /* max blk size */ +/*--------------------parameters reported by the device for this media--------*/ + int numblks; /* nominal blocks capacity */ + int media_blksiz; /* 0 if not ST_FIXEDBLOCKS */ + int media_density; /* this is what it said when asked */ +/*--------------------quirks for the whole drive------------------------------*/ + int drive_quirks; /* quirks of this drive */ +/*--------------------How we should set up when openning each minor device----*/ + struct modes modes[4]; /* plus more for each mode */ +/*--------------------storage for sense data returned by the drive------------*/ + unsigned char sense_data[12]; /* additional sense data needed */ + /* for mode sense/select. */ + struct buf *buf_queue; /* the queue of pending IO operations */ + struct scsi_xfer scsi_xfer; /* The scsi xfer struct for this drive*/ + int xfer_block_wait; /* whether there is a process waiting */ +}*st_data[NST]; +#define ST_INITIALIZED 0x01 +#define ST_INFO_VALID 0x02 +#define ST_OPEN 0x04 +#define ST_BLOCK_SET 0x08 /* block size, mode set by ioctl */ +#define ST_WRITTEN 0x10 /* data have been written, EOD needed */ +#define ST_FIXEDBLOCKS 0x20 +#define ST_AT_FILEMARK 0x40 +#define ST_EIO_PENDING 0x80 /* we couldn't report it then (had data)*/ +#define ST_AT_BOM 0x100 /* ops history suggests Beg of Medium */ +#define ST_READONLY 0x200 /* st_mode_sense says write protected */ +#define ST_FM_WRITTEN 0x400 /* EOF file mark written -- */ + /* used with ~ST_WRITTEN to indicate */ + /* that multiple file marks have been */ + /* written */ +#define ST_BLANK_READ 0x800 /* BLANK CHECK encountered already */ +#define ST_2FM_AT_EOD 0x1000 /* write 2 file marks at EOD */ + +#define ST_PER_ACTION (ST_AT_FILEMARK | ST_EIO_PENDING | ST_BLANK_READ) +#define ST_PER_OPEN ST_OPEN +#define ST_PER_MEDIA (ST_INFO_VALID | ST_BLOCK_SET | ST_WRITTEN | \ + ST_FIXEDBLOCKS | ST_AT_BOM | ST_READONLY | \ + ST_FM_WRITTEN | ST_2FM_AT_EOD | ST_PER_ACTION) static int next_st_unit = 0; /***********************************************************************\ @@ -134,196 +217,414 @@ int stattach(ctlr,targ,lu,scsi_switch) struct scsi_switch *scsi_switch; { int unit,i; - unsigned char *tbl; struct st_data *st; +#ifdef STDEBUG if(scsi_debug & PRINTROUTINES) printf("stattach: "); +#endif /*STDEBUG*/ /*******************************************************\ * Check we have the resources for another drive * \*******************************************************/ unit = next_st_unit++; if( unit >= NST) { - printf("Too many scsi tapes..(%d > %d) reconfigure kernel",(unit + 1),NST); + printf("Too many scsi tapes..(%d > %d) reconfigure kernel\n", + (unit + 1),NST); + return(0); + } + if(st_data[unit]) + { + printf("st%d: Already has storage!\n",unit); + return(0); + } + st = st_data[unit] = malloc(sizeof(struct st_data),M_DEVBUF,M_NOWAIT); + if(!st) + { + printf("st%d: malloc failed in st.c\n",unit); return(0); } - st = st_data + unit; + bzero(st,sizeof(struct st_data)); + /*******************************************************\ * Store information needed to contact our base driver * \*******************************************************/ st->sc_sw = scsi_switch; st->ctlr = ctlr; st->targ = targ; - st->lu = lu; + st->lu = lu; + + /*******************************************************\ + * Store information about default densities * + \*******************************************************/ + st->modes[DSTY1].density = QIC_525; + st->modes[DSTY2].density = QIC_150; + st->modes[DSTY3].density = QIC_120; + + /*******************************************************\ + * Check if the drive is a known criminal and take * + * Any steps needed to bring it into line * + \*******************************************************/ + st_identify_drive(unit); /*******************************************************\ * Use the subdriver to request information regarding * * the drive. We cannot use interrupts yet, so the * * request must specify this. * \*******************************************************/ - if((st_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT))) + if(st_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT)) { - printf(" st%d: scsi tape drive, %d blocks of %d bytes\n", - unit, st->numblks, st->blksiz); + printf("st%d: drive offline\n", unit); } else { - printf(" st%d: scsi tape drive :- offline\n", unit); + if(!st_test_ready(unit,SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT)) + { + printf("st%d: density code 0x%x, ", + unit, st->media_density); + if (st->media_blksiz) + { + printf("%d-byte", st->media_blksiz); + } + else + { + printf("variable"); + } + printf(" blocks, write-%s\n", + st->flags & ST_READONLY ? "protected" : "enabled"); + } + else + { + printf("st%d: drive empty\n", unit); + } } /*******************************************************\ * Set up the bufs for this device * \*******************************************************/ -#ifndef __386BSD__ - stbuf_free[unit] = (struct buf *)0; - for (i = 1; i < STQSIZE; i++) - { - stbuf[unit][i].b_forw = stbuf_free[unit]; - stbuf_free[unit]=&stbuf[unit][i]; - } -#endif __386BSD__ - st_buf_queue[unit].b_active = 0; - st_buf_queue[unit].b_actf = st_buf_queue[unit].b_actl = 0; - st_initialized[unit] = 1; + st->buf_queue = 0; -#if defined(OSF) - st_window[unit] = (caddr_t)alloc_kva(SECSIZE*256+PAGESIZ); -#endif /* defined(OSF) */ + st->flags |= ST_INITIALIZED; return; } +/***********************************************************************\ +* Use the identify routine in 'scsiconf' to get drive info so we can * +* Further tailor our behaviour. * +\***********************************************************************/ + +st_identify_drive(unit) +int unit; +{ + + struct st_data *st = st_data[unit]; + struct scsi_inquiry_data inqbuf; + struct rogues *finger; + char manu[32]; + char model[32]; + char model2[32]; + char version[32]; + int model_len; + /*******************************************************\ + * Get the device type information * + \*******************************************************/ + if (scsi_inquire(st->ctlr, st->targ, st->lu, st->sc_sw, &inqbuf, + SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT) != COMPLETE) + { + printf("st%d: couldn't get device type, using default\n", unit); + return; + } + if((inqbuf.version & SID_ANSII) == 0) + { + /***********************************************\ + * If not advanced enough, use default values * + \***********************************************/ + strncpy(manu,"pre-scsi",8);manu[8]=0; + strncpy(model," unknown model ",16);model[16]=0; + strncpy(version,"????",4);version[4]=0; + } + else + { + strncpy(manu,inqbuf.vendor,8);manu[8]=0; + strncpy(model,inqbuf.product,16);model[16]=0; + strncpy(version,inqbuf.revision,4);version[4]=0; + } + + /*******************************************************\ + * Load the parameters for this kind of device, so we * + * treat it as appropriate for each operating mode * + * Only check the number of characters in the array's * + * model entry, not the entire model string returned. * + \*******************************************************/ + finger = gallery; + while(finger->name) + { + model_len = 0; + while(finger->model[model_len] && (model_len < 32)) + { + model2[model_len] = model[model_len]; + model_len++; + } + model2[model_len] = 0; + if ((strcmp(manu, finger->manu) == 0 ) + && (strcmp(model2, finger->model) == 0 || + strcmp("????????????????", finger->model) == 0) + && (strcmp(version, finger->version) == 0 || + strcmp("????", finger->version) == 0)) + { + printf("st%d: %s is a known rogue\n", unit,finger->name); + st->modes[0] = finger->modes[0]; + st->modes[1] = finger->modes[1]; + st->modes[2] = finger->modes[2]; + st->modes[3] = finger->modes[3]; + st->drive_quirks= finger->quirks; + st->quirks = finger->quirks; /*start value*/ + break; + } + else + { + finger++; /* go to next suspect */ + } + } +} + /*******************************************************\ * open the device. * \*******************************************************/ -stopen(dev) +stopen(dev, flags) +int dev, flags; { - int errcode = 0; int unit,mode,dsty; - int dsty_code; + int errno = 0; struct st_data *st; unit = UNIT(dev); mode = MODE(dev); dsty = DSTY(dev); - st = st_data + unit; /*******************************************************\ * Check the unit is legal * \*******************************************************/ if ( unit >= NST ) { - errcode = ENXIO; - return(errcode); + return(ENXIO); } + st = st_data[unit]; + /*******************************************************\ + * Make sure the device has been initialised * + \*******************************************************/ + if ((st == NULL) || (!(st->flags & ST_INITIALIZED))) + return(ENXIO); + /*******************************************************\ * Only allow one at a time * \*******************************************************/ if(st->flags & ST_OPEN) { - errcode = ENXIO; - goto bad; + return(ENXIO); + } + + /*******************************************************\ + * Throw out a dummy instruction to catch 'Unit attention* + * errors (the error handling will invalidate all our * + * device info if we get one, but otherwise, ignore it * + \*******************************************************/ + st_test_ready(unit, SCSI_SILENT); + + /***************************************************************\ + * Check that the device is ready to use (media loaded?) * + * This time take notice of the return result * + \***************************************************************/ + if(errno = (st_test_ready(unit,0))) + { + printf("st%d: not ready\n",unit); + return(errno); } + /*******************************************************\ * Set up the mode flags according to the minor number * * ensure all open flags are in a known state * + * if it's a different mode, dump all cached parameters * \*******************************************************/ - st->flags &= ~ST_PER_OPEN; - switch(mode) + if(st->last_dsty != dsty || !(st->flags & ST_INFO_VALID)) { - case 2: - case 0: - st->flags &= ~ST_NOREWIND; - break; - case 3: - case 1: - st->flags |= ST_NOREWIND; - break; - default: - printf("st%d: Bad mode (minor number)%d\n",unit,mode); - return(EINVAL); - } - /*******************************************************\ - * Check density code: 0 is drive default * - \*******************************************************/ - switch(dsty) - { - case 0: dsty_code = 0; break; - case DSTY_QIC120: dsty_code = QIC120; break; - case DSTY_QIC150: dsty_code = QIC150; break; - case DSTY_QIC525: dsty_code = QIC525; break; - default: - printf("st%d: Bad density (minor number)%d\n",unit,dsty); - return(EINVAL); + st->flags &= ~ST_INFO_VALID; + st->last_dsty = dsty; + st->quirks = st->drive_quirks | st->modes[dsty].quirks; + st->density = st->modes[dsty].density; } + st->flags &= ~ST_PER_OPEN; + +#ifdef STDEBUG if(scsi_debug & (PRINTROUTINES | TRACEOPENS)) printf("stopen: dev=0x%x (unit %d (of %d))\n" , dev, unit, NST); - /*******************************************************\ - * Make sure the device has been initialised * - \*******************************************************/ +#endif /*STDEBUG*/ + /***************************************************************\ + * If the media is new, then make sure we give it a chance to * + * to do a 'load' instruction. * + \***************************************************************/ + if(!(st->flags & ST_INFO_VALID)) /* is media new? */ + { + if(errno = st_load(unit,LD_LOAD,0)) + { + return(errno); + } + st_test_ready(unit,0); + if(st->quirks & ST_Q_SNS_HLP) + { + /***********************************************\ + * The quirk here is that the drive returns some * + * value to st_mode_sense incorrectly until the * + * tape has actually passed by the head. * + * * + * The method is to set the drive to large * + * fixed-block state (user-specified density and * + * 1024-byte blocks), then read and rewind to * + * get it to sense the tape. If that doesn't * + * work, try 512-byte fixed blocks. If that * + * doesn't work, as a last resort, try variable- * + * length blocks. The result will be the * + * ability to do an accurate st_mode_sense. * + * * + * We pretend not to be at beginning of medium * + * to keep st_read from calling st_decide_mode. * + * * + * We know we can do a rewind because we just * + * did a load, which implies rewind. Rewind * + * seems preferable to space backward if we have * + * a virgin tape. * + * * + * The rest of the code for this quirk is in ILI * + * processing and BLANK CHECK error processing, * + * both part of st_interpret_sense. * + \***********************************************/ + char *buf; + int readsiz; - if (!st_initialized[unit]) - return(ENXIO); - /*******************************************************\ - * Check that it is still responding and ok. * - \*******************************************************/ + buf = malloc(1024,M_TEMP,M_NOWAIT); + if(!buf) return(ENOMEM); - if(scsi_debug & TRACEOPENS) - printf("device is "); - if (!(st_req_sense(unit, 0))) - { - errcode = ENXIO; - if(scsi_debug & TRACEOPENS) - printf("not responding\n"); - goto bad; + if (errno = st_mode_sense(unit, 0)) + { + goto bad; + } + st->blksiz = 1024; + do { + switch (st->blksiz) + { + case 512: + case 1024: + readsiz = st->blksiz; + st->flags |= ST_FIXEDBLOCKS; + break; + default: + readsiz = 1; + st->flags &= ~ST_FIXEDBLOCKS; + } + if (errno = st_mode_select(unit, 0)) + { + goto bad; + } + st->flags &= ~ST_AT_BOM; + st_read(unit, buf, readsiz, SCSI_SILENT); + if (errno = st_rewind(unit, FALSE, 0)) + { +bad: free(buf,M_TEMP); + return(errno); + } + } while (readsiz != 1 && readsiz > st->blksiz); + free(buf,M_TEMP); + } } - if(scsi_debug & TRACEOPENS) - printf("ok\n"); - if(!(st_test_ready(unit,0))) + /*******************************************************\ + * Load the physical device parameters * + * loads: blkmin, blkmax * + \*******************************************************/ + if(errno = st_rd_blk_lim(unit,0)) { - printf("st%d not ready\n",unit); - return(EIO); + return(errno); } - if(!st_info_valid[unit]) /* is media new? */ - if(!st_load(unit,LD_LOAD,0)) - { - return(EIO); - } - - if(!st_rd_blk_lim(unit,0)) + /*******************************************************\ + * Load the media dependent parameters * + * includes: media_blksiz,media_density,numblks * + \*******************************************************/ + if(errno = st_mode_sense(unit,0)) { - return(EIO); + return(errno); } - if(!st_mode_sense(unit,0)) + if (!(st->flags & ST_INFO_VALID) && dsty == 0) { - return(EIO); + /*******************************************************\ + * If the user defaulted the density, use the drive's * + * opinion of it. * + \*******************************************************/ + st->quirks = st->drive_quirks; + st->density = st->media_density; + do { + if (st->density == st->modes[dsty].density) + { + st->quirks |= st->modes[dsty].quirks; +#ifdef STDEBUG + if(st_debug) printf("selected density %d\n", dsty); +#endif /*STDEBUG*/ + break; /* only out of the loop*/ + } + } while (++dsty < 4); + /*******************************************************\ + * If dsty got to 4, the drive must have reported a * + * density which isn't in our density list (e.g. QIC-24 * + * for a default drive). We can handle that, except * + * there'd better be no density-specific quirks in the * + * drive's behavior. * + \*******************************************************/ } - if(!st_mode_select(unit,0,dsty_code)) + /***************************************************************\ + * Decide whether or not to write two file marks to signify end- * + * of-data. Make the decision as a function of density. If * + * the decision is not to use a second file mark, the SCSI BLANK * + * CHECK condition code will be recognized as end-of-data when * + * first read. * + \***************************************************************/ + switch (st->density) { - return(EIO); +/* case 8 mm: What is the SCSI density code for 8 mm, anyway? */ + case QIC_11: + case QIC_24: + case QIC_120: + case QIC_150: + case QIC_525: + case QIC_1320: + st->flags &= ~ST_2FM_AT_EOD; + break; + default: + st->flags |= ST_2FM_AT_EOD; } - st_info_valid[unit] = TRUE; + /***************************************************************\ + * Make sure that a tape opened in write-only mode will have * + * file marks written on it. This is the only way to write a * + * zero-length file on tape. * + \***************************************************************/ + if ((flags & O_ACCMODE) == FWRITE) + st->flags |= ST_WRITTEN; + + st->flags |= ST_INFO_VALID; st_prevent(unit,PR_PREVENT,0); /* who cares if it fails? */ - /*******************************************************\ - * Load the physical device parameters * - \*******************************************************/ +#ifdef STDEBUG if(scsi_debug & TRACEOPENS) printf("Params loaded "); - +#endif /*STDEBUG*/ st->flags |= ST_OPEN; - -bad: - return(errcode); + return(0); } /*******************************************************\ @@ -337,35 +638,36 @@ stclose(dev) unit = UNIT(dev); mode = MODE(dev); - st = st_data + unit; + st = st_data[unit]; +#ifdef STDEBUG if(scsi_debug & TRACEOPENS) printf("Closing device"); - if(st->flags & ST_WRITTEN) - { - st_write_filemarks(unit,1,0); - } - st->flags &= ~ST_WRITTEN; +#endif /*STDEBUG*/ switch(mode) { case 0: st_rewind(unit,FALSE,SCSI_SILENT); st_prevent(unit,PR_ALLOW,SCSI_SILENT); + st->flags &= ~ST_PER_MEDIA; break; - case 1: - st_prevent(unit,PR_ALLOW,SCSI_SILENT); + case 1: /*non rewind*/ + if((st->flags & (ST_WRITTEN | ST_FM_WRITTEN)) == ST_WRITTEN) + st_write_filemarks(unit, 1, 0); break; case 2: st_rewind(unit,FALSE,SCSI_SILENT); st_prevent(unit,PR_ALLOW,SCSI_SILENT); st_load(unit,LD_UNLOAD,SCSI_SILENT); + st->flags &= ~ST_PER_MEDIA; break; - case 3: + case 3:/* a bit silly really */ st_prevent(unit,PR_ALLOW,SCSI_SILENT); st_load(unit,LD_UNLOAD,SCSI_SILENT); + st->flags &= ~ST_PER_MEDIA; break; default: - printf("st%d:close: Bad mode (minor number)%d how's it open?\n" + printf("st%d: close: Bad mode (minor number)%d how's it open?\n" ,unit,mode); return(EINVAL); } @@ -373,70 +675,155 @@ stclose(dev) return(0); } -#ifndef __386BSD__ -/*******************************************************\ -* Get ownership of this unit's buf * -* If need be, sleep on it, until it comes free * -\*******************************************************/ -struct buf * -st_get_buf(unit) { - struct buf *rc; - - while (!(rc = stbuf_free[unit])) - sleep((caddr_t)&stbuf_free[unit], PRIBIO+1); - stbuf_free[unit] = stbuf_free[unit]->b_forw; - rc->b_error = 0; - rc->b_resid = 0; - rc->b_flags = 0; - return(rc); -} - -/*******************************************************\ -* Free this unit's buf, wake processes waiting for it * -\*******************************************************/ -st_free_buf(unit,bp) -struct buf *bp; +/***************************************************************\ +* Given all we know about the device, media, mode, 'quirks' and * +* initial operation, make a decision as to how we should be set * +* up. First, choose the density, then variable/fixed blocks. * +\***************************************************************/ +st_decide_mode(unit, first_read) +int unit, first_read; { - if (!stbuf_free[unit]) - wakeup((caddr_t)&stbuf_free[unit]); - bp->b_forw = stbuf_free[unit]; - stbuf_free[unit] = bp; -} - + int dsty, error; + struct st_data *st = st_data[unit]; -/*******************************************************\ -* Get the buf for this unit and use physio to do it * -\*******************************************************/ -stread(dev,uio) -register short dev; -struct uio *uio; -{ - int unit = UNIT(dev); - struct buf *bp = st_get_buf(unit); - int rc; - rc = physio(ststrategy, bp, dev, B_READ, stminphys, uio); - st_free_buf(unit,bp); - return(rc); -} + /***************************************************************\ + * First, if our information about the tape is out of date, get * + * new information. * + \***************************************************************/ + if (!(st->flags & ST_INFO_VALID)) + { + if (error = st_mode_sense(unit, 0)) + return (error); + st->flags |= ST_INFO_VALID; + } +#ifdef STDEBUG + if(st_debug) printf("starting mode decision\n"); +#endif /*STDEBUG*/ -/*******************************************************\ -* Get the buf for this unit and use physio to do it * -\*******************************************************/ -stwrite(dev,uio) -dev_t dev; -struct uio *uio; -{ - int unit = UNIT(dev); - struct buf *bp = st_get_buf(unit); - int rc; + /***************************************************************\ + * If the user has already specified fixed or variable-length * + * blocks using an ioctl, just believe him. OVERRIDE ALL * + \***************************************************************/ + if (st->flags & ST_BLOCK_SET) + { +#ifdef STDEBUG + if(st_debug) printf("user has specified %s mode\n", + st->flags & ST_FIXEDBLOCKS ? "fixed" : "variable"); +#endif /*STDEBUG*/ + goto done; + } - rc = physio(ststrategy, bp, dev, B_WRITE, stminphys, uio); - st_free_buf(unit,bp); - return(rc); -} + /***************************************************************\ + * If the user hasn't already specified fixed or variable-length * + * blocks and the block size (zero if variable-length), we'll * + * have to try to figure them out ourselves. * + * * + * Our first shot at a method is, "The quirks made me do it!" * + \***************************************************************/ + switch (st->quirks & (ST_Q_FORCE_FIXED_MODE | ST_Q_FORCE_VAR_MODE)) + { + case (ST_Q_FORCE_FIXED_MODE | ST_Q_FORCE_VAR_MODE): + printf("st%d: bad quirks\n",unit); + return (EINVAL); + case ST_Q_FORCE_FIXED_MODE: + st->flags |= ST_FIXEDBLOCKS; + if (st->blkmin && (st->blkmin == st->blkmax)) + st->blksiz = st->blkmin; + else if(st->media_blksiz > 0) + st->blksiz = st->media_blksiz; + else + st->blksiz = DEF_FIXED_BSIZE; +#ifdef STDEBUG + if(st_debug) printf("Quirks force fixed mode\n"); +#endif /*STDEBUG*/ + goto done; + case ST_Q_FORCE_VAR_MODE: + st->flags &= ~ST_FIXEDBLOCKS; + st->blksiz = 0; +#ifdef STDEBUG + if(st_debug) printf("Quirks force variable mode\n"); +#endif /*STDEBUG*/ + goto done; + } + + /***************************************************************\ + * If the drive can only handle fixed-length blocks and only at * + * one size, perhaps we should just do that. * + \***************************************************************/ + if (st->blkmin && (st->blkmin == st->blkmax)) + { + st->flags |= ST_FIXEDBLOCKS; + st->blksiz = st->blkmin; +#ifdef STDEBUG + if(st_debug) printf("blkmin == blkmax of %d\n",st->blkmin); +#endif /*STDEBUG*/ + goto done; + } + + /***************************************************************\ + * If the tape density mandates use of fixed or variable-length * + * blocks, comply. * + \***************************************************************/ + switch (st->density) + { + case HALFINCH_800: + case HALFINCH_1600: + case HALFINCH_6250: + case DDS: + st->flags &= ~ST_FIXEDBLOCKS; + st->blksiz = 0; +#ifdef STDEBUG + if(st_debug) printf("density specified variable\n"); +#endif /*STDEBUG*/ + goto done; + case QIC_11: + case QIC_24: + case QIC_120: + case QIC_150: + st->flags |= ST_FIXEDBLOCKS; + if (st->media_blksiz > 0) + st->blksiz = st->media_blksiz; + else + st->blksiz = DEF_FIXED_BSIZE; +#ifdef STDEBUG + if(st_debug) printf("density specified fixed\n"); +#endif /*STDEBUG*/ + goto done; + } + + /***************************************************************\ + * If we're about to read the tape, perhaps we should choose * + * fixed or variable-length blocks and block size according to * + * what the drive found on the tape. * + \***************************************************************/ + if (first_read && (!(st->quirks & ST_Q_BLKSIZ) || st->media_blksiz == 0 + || st->media_blksiz == DEF_FIXED_BSIZE || st->media_blksiz == 1024)) + { + if (st->media_blksiz == 0) + st->flags &= ~ST_FIXEDBLOCKS; + else + st->flags |= ST_FIXEDBLOCKS; + st->blksiz = st->media_blksiz; +#ifdef STDEBUG + if(st_debug) printf("Used media_blksiz of %d\n",st->media_blksiz); +#endif /*STDEBUG*/ + goto done; + } + /***************************************************************\ + * We're getting no hints from any direction. Choose variable- * + * length blocks arbitrarily. * + \***************************************************************/ + st->flags &= ~ST_FIXEDBLOCKS; + st->blksiz = 0; +#ifdef STDEBUG + if(st_debug) printf("Give up and default to variable mode\n"); +#endif /*STDEBUG*/ +done: + st->flags &= ~ST_AT_BOM; + return (st_mode_select(unit, 0)); +} -#endif __386BSD__ /*******************************************************\ * trim the size of the transfer if needed, * * called by physio * @@ -446,7 +833,7 @@ struct uio *uio; void stminphys(bp) struct buf *bp; { - (*(st_data[UNIT(bp->b_dev)].sc_sw->scsi_minphys))(bp); + (*(st_data[UNIT(bp->b_dev)]->sc_sw->scsi_minphys))(bp); } /*******************************************************\ @@ -459,54 +846,80 @@ struct buf *bp; int ststrategy(bp) struct buf *bp; { - struct buf *dp; + struct buf **dp; unsigned char unit; unsigned int opri; + struct st_data *st; ststrats++; unit = UNIT((bp->b_dev)); + st = st_data[unit]; +#ifdef STDEBUG if(scsi_debug & PRINTROUTINES) printf("\nststrategy "); if(scsi_debug & SHOWREQUESTS) printf("st%d: %d bytes @ blk%d\n", unit,bp->b_bcount,bp->b_blkno); +#endif /*STDEBUG*/ /*******************************************************\ * If it's a null transfer, return immediatly * \*******************************************************/ - if (bp->b_bcount == 0) { + if (bp->b_bcount == 0) + { goto done; } + /*******************************************************\ + * If we're at beginning of medium, now is the time to * + * set medium access density, fixed or variable-blocks * + * and, if fixed, the block size. * + \*******************************************************/ + if (st->flags & ST_AT_BOM && + (bp->b_error = st_decide_mode(unit, (bp->b_flags & B_READ) != 0))) + goto bad; + /*******************************************************\ * Odd sized request on fixed drives are verboten * \*******************************************************/ - if((st_data[unit].flags & ST_FIXEDBLOCKS) - && bp->b_bcount % st_data[unit].blkmin) + if(st->flags & ST_FIXEDBLOCKS) + { + if(bp->b_bcount % st->blksiz) + { + printf("st%d: bad request, must be multiple of %d\n", + unit, st->blksiz); + bp->b_error = EIO; + goto bad; + } + } + /*******************************************************\ + * as are out-of-range requests on variable drives. * + \*******************************************************/ + else if(bp->b_bcount < st->blkmin || bp->b_bcount > st->blkmax) { - printf("st%d: bad request, must be multiple of %d\n", - unit, st_data[unit].blkmin); + printf("st%d: bad request, must be between %d and %d\n", + unit, st->blkmin, st->blkmax); bp->b_error = EIO; goto bad; } -#ifdef __386BSD__ stminphys(bp); -#endif __386BSD__ opri = splbio(); - dp = &st_buf_queue[unit]; /*******************************************************\ - * Place it in the queue of disk activities for this tape* - * at the end * + * Place it in the queue of activities for this tape * + * at the end (a bit silly because we only have on user..* + * (but it could fork() )) * \*******************************************************/ - while ( dp->b_actf) + dp = &(st->buf_queue); + while (*dp) { - dp = dp->b_actf; + dp = &((*dp)->b_actf); } - dp->b_actf = bp; + *dp = bp; bp->b_actf = NULL; /*******************************************************\ * Tell the device to get going on the transfer if it's * * not doing anything, otherwise just wait for completion* + * (All a bit silly if we're only allowing 1 open but..) * \*******************************************************/ ststart(unit); @@ -523,359 +936,7 @@ done: } -/***************************************************************\ -* ststart looks to see if there is a buf waiting for the device * -* and that the device is not already busy. If both are true, * -* It deques the buf and creates a scsi command to perform the * -* transfer in the buf. The transfer request will call st_done * -* on completion, which will in turn call this routine again * -* so that the next queued transfer is performed. * -* The bufs are queued by the strategy routine (ststrategy) * -* * -* This routine is also called after other non-queued requests * -* have been made of the scsi driver, to ensure that the queue * -* continues to be drained. * -\***************************************************************/ -/* ststart() is called at splbio */ -ststart(unit) -{ - int drivecount; - register struct buf *bp = 0; - register struct buf *dp; - struct scsi_xfer *xs; - struct scsi_rw_tape cmd; - int blkno, nblk; - struct st_data *st; - - - st = st_data + unit; - - if(scsi_debug & PRINTROUTINES) printf("ststart%d ",unit); - /*******************************************************\ - * See if there is a buf to do and we are not already * - * doing one * - \*******************************************************/ - xs=&st_scsi_xfer[unit]; - if(xs->flags & INUSE) - { - return; /* unit already underway */ - } -trynext: - if(st_xfer_block_wait[unit]) /* a special awaits, let it proceed first */ - { - wakeup(&st_xfer_block_wait[unit]); - return; - } - - dp = &st_buf_queue[unit]; - if ((bp = dp->b_actf) != NULL) - { - dp->b_actf = bp->b_actf; - } - else /* no work to do */ - { - return; - } - xs->flags = INUSE; /* Now ours */ - - - /*******************************************************\ - * We have a buf, now we should move the data into * - * a scsi_xfer definition and try start it * - \*******************************************************/ - - /*******************************************************\ - * If we are at a filemark but have not reported it yet * - * then we should report it now * - \*******************************************************/ - if(st->flags & ST_AT_FILEMARK) - { - bp->b_error = 0; - bp->b_flags |= B_ERROR; /* EOF*/ - st->flags &= ~ST_AT_FILEMARK; - biodone(bp); - xs->flags = 0; /* won't need it now */ - goto trynext; - } - /*******************************************************\ - * If we are at EOM but have not reported it yet * - * then we should report it now * - \*******************************************************/ - if(st->flags & ST_AT_EOM) - { - bp->b_error = EIO; - bp->b_flags |= B_ERROR; - st->flags &= ~ST_AT_EOM; - biodone(bp); - xs->flags = 0; /* won't need it now */ - goto trynext; - } - /*******************************************************\ - * Fill out the scsi command * - \*******************************************************/ - bzero(&cmd, sizeof(cmd)); - if((bp->b_flags & B_READ) == B_WRITE) - { - st->flags |= ST_WRITTEN; - xs->flags |= SCSI_DATA_OUT; - } - else - { - xs->flags |= SCSI_DATA_IN; - } - cmd.op_code = (bp->b_flags & B_READ) - ? READ_COMMAND_TAPE - : WRITE_COMMAND_TAPE; - - /*******************************************************\ - * Handle "fixed-block-mode" tape drives by using the * - * block count instead of the length. * - \*******************************************************/ - if(st->flags & ST_FIXEDBLOCKS) - { - cmd.fixed = 1; - lto3b(bp->b_bcount/st->blkmin,cmd.len); - } - else - { - lto3b(bp->b_bcount,cmd.len); - } - - /*******************************************************\ - * Fill out the scsi_xfer structure * - * Note: we cannot sleep as we may be an interrupt * - \*******************************************************/ - xs->flags |= SCSI_NOSLEEP; - xs->adapter = st->ctlr; - xs->targ = st->targ; - xs->lu = st->lu; - xs->retries = 1; /* can't retry on tape*/ - xs->timeout = 100000; /* allow 100 secs for retension */ - xs->cmd = (struct scsi_generic *)&cmd; - xs->cmdlen = sizeof(cmd); - xs->data = (u_char *)bp->b_un.b_addr; - xs->datalen = bp->b_bcount; - xs->resid = bp->b_bcount; - xs->when_done = st_done; - xs->done_arg = unit; - xs->done_arg2 = (int)xs; - xs->error = XS_NOERROR; - xs->bp = bp; - /*******************************************************\ - * Pass all this info to the scsi driver. * - \*******************************************************/ - - -#if defined(OSF)||defined(FIX_ME) - if (bp->b_flags & B_PHYS) { - xs->data = (u_char*)map_pva_kva(bp->b_proc, bp->b_un.b_addr, - bp->b_bcount, st_window[unit], - (bp->b_flags&B_READ)?B_WRITE:B_READ); - } else { - xs->data = (u_char*)bp->b_un.b_addr; - } -#endif /* defined(OSF) */ - - if ( (*(st->sc_sw->scsi_cmd))(xs) != SUCCESSFULLY_QUEUED) - { - printf("st%d: oops not queued",unit); - xs->error = XS_DRIVER_STUFFUP; - st_done(unit,xs); - } - stqueues++; -} - -/*******************************************************\ -* This routine is called by the scsi interrupt when * -* the transfer is complete. -\*******************************************************/ -int st_done(unit,xs) -int unit; -struct scsi_xfer *xs; -{ - struct buf *bp; - int retval; - - if(scsi_debug & PRINTROUTINES) printf("st_done%d ",unit); - if (! (xs->flags & INUSE)) - panic("scsi_xfer not in use!"); - if(bp = xs->bp) - { - switch(xs->error) - { - case XS_NOERROR: - bp->b_flags &= ~B_ERROR; - bp->b_error = 0; - bp->b_resid = 0; - break; - case XS_SENSE: - retval = (st_interpret_sense(unit,xs)); - if(retval) - { - /***************************************\ - * We have a real error, the bit should * - * be set to indicate this. The return * - * value will contain the unix error code* - * that the error interpretation routine * - * thought was suitable, so pass this * - * value back in the buf structure. * - * Furthermore we return information * - * saying that no data was transferred * - \***************************************/ - bp->b_flags |= B_ERROR; - bp->b_error = retval; - bp->b_resid = bp->b_bcount; - st_data[unit].flags - &= ~(ST_AT_FILEMARK|ST_AT_EOM); - } - else - { - /***********************************************\ - * The error interpretation code has declared * - * that it wasn't a real error, or at least that * - * we should be ignoring it if it was. * - \***********************************************/ - if(xs->resid && ( xs->resid != xs->datalen )) - { - /***************************************\ - * Here we have the tricky part.. * - * We successfully read less data than * - * we requested. (but not 0) * - *------for variable blocksize tapes:----* - * UNDER 386BSD: * - * We should legitimatly have the error * - * bit set, with the error value set to * - * zero.. This is to indicate to the * - * physio code that while we didn't get * - * as much information as was requested, * - * we did reach the end of the record * - * and so physio should not call us * - * again for more data... we have it all * - * SO SET THE ERROR BIT! * - * * - * UNDER MACH:(CMU) * - * To indicate the same as above, we * - * need only have a non 0 resid that is * - * less than the b_bcount, but the * - * ERROR BIT MUST BE CLEAR! (sigh) * - * * - * UNDER OSF1: * - * To indicate the same as above, we * - * need to have a non 0 resid that is * - * less than the b_bcount, but the * - * ERROR BIT MUST BE SET! (gasp)(sigh) * - * * - *-------for fixed blocksize device------* - * We could have read some successful * - * records before hitting * - * the EOF or EOT. These must be passed * - * to the user, before we report the * - * EOx. Only if there is no data for the * - * user do we report it now. (via an EIO * - * for EOM and resid == count for EOF). * - * We will report the EOx NEXT time.. * - \***************************************/ -#ifdef MACH /*osf and cmu varieties */ -#ifdef OSF - bp->b_flags |= B_ERROR; -#else OSF - bp->b_flags &= ~B_ERROR; -#endif OSF -#endif MACH -#ifdef __386BSD__ - bp->b_flags |= B_ERROR; -#endif __386BSD__ - bp->b_error = 0; - bp->b_resid = xs->resid; - if((st_data[unit].flags & ST_FIXEDBLOCKS)) - { - bp->b_resid *= st_data[unit].blkmin; - if( (st_data[unit].flags & ST_AT_EOM) - && (bp->b_resid == bp->b_bcount)) - { - bp->b_error = EIO; - st_data[unit].flags - &= ~ST_AT_EOM; - } - } - xs->error = XS_NOERROR; - break; - } - else - { - /***************************************\ - * We have come out of the error handler * - * with no error code.. we have also * - * not had an ili (would have gone to * - * the previous clause). Now we need to * - * distiguish between succesful read of * - * no data (EOF or EOM) and successfull * - * read of all requested data. * - * At least all o/s agree that: * - * 0 bytes read with no error is EOF * - * 0 bytes read with an EIO is EOM * - \***************************************/ - - bp->b_resid = bp->b_bcount; - if(st_data[unit].flags & ST_AT_FILEMARK) - { - st_data[unit].flags &= ~ST_AT_FILEMARK; - bp->b_flags &= ~B_ERROR; - bp->b_error = 0; - break; - } - if(st_data[unit].flags & ST_AT_EOM) - { - bp->b_flags |= B_ERROR; - bp->b_error = EIO; - st_data[unit].flags &= ~ST_AT_EOM; - break; - } - printf("st%d:error ignored\n" ,unit); - } - } - break; - - case XS_TIMEOUT: - printf("st%d timeout\n",unit); - break; - - case XS_BUSY: /* should retry */ /* how? */ - /************************************************/ - /* SHOULD put buf back at head of queue */ - /* and decrement retry count in (*xs) */ - /* HOWEVER, this should work as a kludge */ - /************************************************/ - if(xs->retries--) - { - xs->flags &= ~ITSDONE; - xs->error = XS_NOERROR; - if ( (*(st_data[unit].sc_sw->scsi_cmd))(xs) - == SUCCESSFULLY_QUEUED) - { /* don't wake the job, ok? */ - return; - } - printf("device busy"); - xs->flags |= ITSDONE; - } - case XS_DRIVER_STUFFUP: - bp->b_flags |= B_ERROR; - bp->b_error = EIO; - break; - default: - printf("st%d: unknown error category from scsi driver\n" - ,unit); - } - biodone(bp); - xs->flags = 0; /* no longer in use */ - ststart(unit); /* If there's another waiting.. do it */ - } - else - { - wakeup(xs); - } -} /*******************************************************\ * Perform special action on behalf of the user * * Knows about the internals of this device * @@ -885,17 +946,19 @@ dev_t dev; int cmd; caddr_t arg; { - register i,j; - unsigned int opri; int errcode = 0; unsigned char unit; - int number,flags,ret; + int number,flags,dsty; + struct st_data *st; + /*******************************************************\ * Find the device that the user is talking about * \*******************************************************/ flags = 0; /* give error messages, act on errors etc. */ unit = UNIT(dev); + dsty = DSTY(dev); + st = st_data[unit]; switch(cmd) { @@ -906,18 +969,29 @@ caddr_t arg; bzero(g, sizeof(struct mtget)); g->mt_type = 0x7; /* Ultrix compat */ /*?*/ - ret=TRUE; + if (st->flags & ST_FIXEDBLOCKS) { + g->mt_bsiz = st->blksiz; + } else { + g->mt_bsiz = 0; + } + g->mt_dns_dflt = st->modes[0].density; + g->mt_dns_dsty1 = st->modes[DSTY1].density; + g->mt_dns_dsty2 = st->modes[DSTY2].density; + g->mt_dns_dsty3 = st->modes[DSTY3].density; break; } case MTIOCTOP: { + int nmarks; struct mtop *mt = (struct mtop *) arg; +#ifdef STDEBUG if (st_debug) printf("[sctape_sstatus: %x %x]\n", mt->mt_op, mt->mt_count); +#endif /*STDEBUG*/ @@ -926,80 +1000,137 @@ caddr_t arg; switch ((short)(mt->mt_op)) { case MTWEOF: /* write an end-of-file record */ - ret = st_write_filemarks(unit,number,flags); - st_data[unit].flags &= ~ST_WRITTEN; - break; - case MTFSF: /* forward space file */ - ret = st_space(unit,number,SP_FILEMARKS,flags); + errcode = st_write_filemarks(unit,number,flags); break; case MTBSF: /* backward space file */ - ret = st_space(unit,-number,SP_FILEMARKS,flags); - break; - case MTFSR: /* forward space record */ - ret = st_space(unit,number,SP_BLKS,flags); + number = -number; + case MTFSF: /* forward space file */ + errcode = st_chkeod(unit, FALSE, &nmarks, flags); + if (errcode == ESUCCESS) + errcode = st_space(unit, number - nmarks, + SP_FILEMARKS, flags); break; case MTBSR: /* backward space record */ - ret = st_space(unit,-number,SP_BLKS,flags); + number = -number; + case MTFSR: /* forward space record */ + errcode = st_chkeod(unit, TRUE, &nmarks, flags); + if (errcode == ESUCCESS) + errcode = st_space(unit,number,SP_BLKS,flags); break; case MTREW: /* rewind */ - ret = st_rewind(unit,FALSE,flags); + errcode = st_rewind(unit,FALSE,flags); break; case MTOFFL: /* rewind and put the drive offline */ - if((ret = st_rewind(unit,FALSE,flags))) + if(st_rewind(unit,FALSE,flags)) { - st_prevent(unit,PR_ALLOW,0); - ret = st_load(unit,LD_UNLOAD,flags); + printf("st%d: rewind failed, unit still loaded\n", + unit); } else { - printf("rewind failed, unit still loaded\n"); + st_prevent(unit,PR_ALLOW,0); + st_load(unit,LD_UNLOAD,flags); } break; case MTNOP: /* no operation, sets status only */ case MTCACHE: /* enable controller cache */ case MTNOCACHE: /* disable controller cache */ - ret = TRUE;; break; + case MTSETBSIZ: /* Set block size for device */ + if (!(st->flags & ST_AT_BOM)) + { + errcode = EINVAL; + break; + } + if (number == 0) + { + st->flags &= ~ST_FIXEDBLOCKS; + } + else + { + if ((st->blkmin || st->blkmax) /* they exist */ + && ((number < st->blkmin + || number > st->blkmax))) + { + errcode = EINVAL; + break; + } + st->flags |= ST_FIXEDBLOCKS; + } + st->blksiz = number; + st->flags |= ST_BLOCK_SET; + break; + + /* How do we check that the drive can handle + the requested density ? */ + + case MTSETDNSTY: /* Set density for device and mode */ + if (number < 0 || number > SCSI_2_MAX_DENSITY_CODE) + { + errcode = EINVAL; + } + else + { + st->modes[dsty].density = number; + } + break; + default: - return EINVAL; + errcode = EINVAL; } break; } case MTIOCIEOT: case MTIOCEEOT: - ret=TRUE; break; + default: + errcode = EINVAL; } - return(ret?ESUCCESS:EIO); + return errcode; } - /*******************************************************\ -* Check with the device that it is ok, (via scsi driver)* +* Do a synchronous read. * \*******************************************************/ -st_req_sense(unit, flags) -int flags; +int st_read(unit, buf, size, flags) +int unit, size, flags; +char *buf; { - struct scsi_sense_data sense; - struct scsi_sense scsi_cmd; + int error; + struct scsi_rw_tape scsi_cmd; + struct st_data *st = st_data[unit]; - bzero(&scsi_cmd, sizeof(scsi_cmd)); - scsi_cmd.op_code = REQUEST_SENSE; - scsi_cmd.length = sizeof(sense); + /*******************************************************\ + * If it's a null transfer, return immediatly * + \*******************************************************/ + if (size == 0) + { + return(ESUCCESS); + } - if (st_scsi_cmd(unit, + /*******************************************************\ + * If we're at beginning of medium, now is the time to * + * set medium access density, fixed or variable-blocks * + * and, if fixed, the block size. * + \*******************************************************/ + if (st->flags & ST_AT_BOM && (error = st_decide_mode(unit, TRUE))) + return (error); + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = READ_COMMAND_TAPE; + scsi_cmd.byte2 |= st->flags & ST_FIXEDBLOCKS ? SRWT_FIXED : 0; + lto3b(scsi_cmd.byte2 & SRWT_FIXED ? + size / (st->blksiz ? st->blksiz : DEF_FIXED_BSIZE) : size, + scsi_cmd.len); + return (st_scsi_cmd(unit, &scsi_cmd, sizeof(scsi_cmd), - &sense, - sizeof(sense), + buf, + size, 100000, - flags | SCSI_DATA_IN) != 0) - { - return(FALSE); - } - else - return(TRUE); + NULL, + flags | SCSI_DATA_IN)); } /*******************************************************\ @@ -1013,16 +1144,14 @@ int unit,flags; bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = TEST_UNIT_READY; - if (st_scsi_cmd(unit, + return (st_scsi_cmd(unit, &scsi_cmd, sizeof(scsi_cmd), 0, 0, 100000, - flags) != 0) { - return(FALSE); - } else - return(TRUE); + NULL, + flags)); } @@ -1040,11 +1169,13 @@ int unit,flags; { struct scsi_blk_limits scsi_cmd; struct scsi_blk_limits_data scsi_blkl; - struct st_data *st = st_data + unit; + struct st_data *st = st_data[unit]; + int errno; + /*******************************************************\ * First check if we have it all loaded * \*******************************************************/ - if (st_info_valid[unit]) goto done; + if ((st->flags & ST_INFO_VALID)) return 0; /*******************************************************\ * do a 'Read Block Limits' * @@ -1055,145 +1186,204 @@ int unit,flags; /*******************************************************\ * do the command, update the global values * \*******************************************************/ - if (st_scsi_cmd(unit, + if ( errno = st_scsi_cmd(unit, &scsi_cmd, sizeof(scsi_cmd), &scsi_blkl, sizeof(scsi_blkl), 5000, - flags | SCSI_DATA_IN) != 0) + NULL, + flags | SCSI_DATA_IN)) { - if(!(flags & SCSI_SILENT)) - printf("could not get blk limits for unit %d\n", unit); - st_info_valid[unit] = FALSE; - return(FALSE); + return errno; } - if (st_debug) - { - printf(" (%d <= blksiz <= %d\n) ", - b2tol(scsi_blkl.min_length), - _3btol(&scsi_blkl.max_length_2)); - } st->blkmin = b2tol(scsi_blkl.min_length); st->blkmax = _3btol(&scsi_blkl.max_length_2); -done: - if(st->blkmin && (st->blkmin == st->blkmax)) +#ifdef STDEBUG + if (st_debug) { - st->flags |= ST_FIXEDBLOCKS; + printf("(%d <= blksiz <= %d)\n", st->blkmin, st->blkmax); } - return(TRUE); +#endif /*STDEBUG*/ + return 0; } + /*******************************************************\ * Get the scsi driver to send a full inquiry to the * * device and use the results to fill out the global * * parameter structure. * +* * +* called from: * +* attach * +* open * +* ioctl (to reset original blksize) * \*******************************************************/ st_mode_sense(unit, flags) int unit,flags; { + int scsi_sense_len; + int errno; + char *scsi_sense_ptr; struct scsi_mode_sense scsi_cmd; - struct + struct scsi_sense { - struct scsi_mode_header_tape header; + struct scsi_mode_header header; struct blk_desc blk_desc; }scsi_sense; - struct st_data *st = st_data + unit; + struct scsi_sense_page_0 + { + struct scsi_mode_header header; + struct blk_desc blk_desc; + unsigned char sense_data[PAGE_0_SENSE_DATA_SIZE]; + /* Tandberg tape drives returns page 00 */ + /* with the sense data, whether or not */ + /* you want it( ie the don't like you */ + /* saying you want anything less!!!!! */ + /* They also expect page 00 */ + /* back when you issue a mode select */ + }scsi_sense_page_0; + struct st_data *st = st_data[unit]; + /*******************************************************\ * First check if we have it all loaded * \*******************************************************/ - if (st_info_valid[unit]) return(TRUE); + if ((st->flags & ST_INFO_VALID)) return 0; + + /*******************************************************\ + * Define what sort of structure we're working with * + \*******************************************************/ + if (st->quirks & ST_Q_NEEDS_PAGE_0) + { + scsi_sense_len = sizeof(scsi_sense_page_0); + scsi_sense_ptr = (char *) &scsi_sense_page_0; + } + else + { + scsi_sense_len = sizeof(scsi_sense); + scsi_sense_ptr = (char *) &scsi_sense; + } + /*******************************************************\ - * First do a mode sense * + * Set up a mode sense * \*******************************************************/ bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = MODE_SENSE; - scsi_cmd.length = sizeof(scsi_sense); + scsi_cmd.length = scsi_sense_len; + /*******************************************************\ * do the command, but we don't need the results * - * just print them for our interest's sake * + * just print them for our interest's sake, if asked, * + * or if we need it as a template for the mode select * + * store it away. * \*******************************************************/ - if (st_scsi_cmd(unit, + if (errno = st_scsi_cmd(unit, &scsi_cmd, sizeof(scsi_cmd), - &scsi_sense, - sizeof(scsi_sense), + scsi_sense_ptr, + scsi_sense_len, 5000, - flags | SCSI_DATA_IN) != 0) + NULL, + flags | SCSI_DATA_IN) ) { - if(!(flags & SCSI_SILENT)) - printf("could not mode sense for unit %d\n", unit); - st_info_valid[unit] = FALSE; - return(FALSE); + return errno; } + st->numblks = _3btol(&(((struct scsi_sense *)scsi_sense_ptr)->blk_desc.nblocks)); + st->media_blksiz = _3btol(&(((struct scsi_sense *)scsi_sense_ptr)->blk_desc.blklen)); + st->media_density = ((struct scsi_sense *)scsi_sense_ptr)->blk_desc.density; + if (((struct scsi_sense *)scsi_sense_ptr)->header.dev_spec & + SMH_DSP_WRITE_PROT) + st->flags |= ST_READONLY; +#ifdef STDEBUG if (st_debug) { - printf("unit %d: %d blocks of %d bytes, write %s, %sbuffered", - unit, - _3btol(&scsi_sense.blk_desc.nblocks), - _3btol(&scsi_sense.blk_desc.blklen), - (scsi_sense.header.write_protected ? - "protected" : "enabled"), - (scsi_sense.header.buf_mode ? - "" : "un") - ); + printf("st%d: density code 0x%x, %d-byte blocks, write-%s, ", + unit, st->media_density, st->media_blksiz, + st->flags & ST_READONLY ? "protected" : "enabled"); + printf("%sbuffered\n", + ((struct scsi_sense *)scsi_sense_ptr)->header.dev_spec + & SMH_DSP_BUFF_MODE ? "" : "un"); + } +#endif /*STDEBUG*/ + if (st->quirks & ST_Q_NEEDS_PAGE_0) + { + bcopy(((struct scsi_sense_page_0 *)scsi_sense_ptr)->sense_data, + st->sense_data, + sizeof(((struct scsi_sense_page_0 *)scsi_sense_ptr)->sense_data)); } - st->numblks = _3btol(&scsi_sense.blk_desc.nblocks); - st->blksiz = _3btol(&scsi_sense.blk_desc.blklen); - return(TRUE); + return 0; } /*******************************************************\ -* Get the scsi driver to send a full inquiry to the * -* device and use the results to fill out the global * -* parameter structure. * +* Send a filled out parameter structure to the drive to * +* set it into the desire modes etc. * \*******************************************************/ -st_mode_select(unit, flags, dsty_code) -int unit,flags,dsty_code; +st_mode_select(unit, flags) +int unit, flags; { + int dat_len; + char *dat_ptr; struct scsi_mode_select scsi_cmd; - struct + struct dat { - struct scsi_mode_header_tape header; + struct scsi_mode_header header; struct blk_desc blk_desc; }dat; - struct st_data *st = st_data + unit; + struct dat_page_0 + { + struct scsi_mode_header header; + struct blk_desc blk_desc; + unsigned char sense_data[PAGE_0_SENSE_DATA_SIZE]; + }dat_page_0; + struct st_data *st = st_data[unit]; + /*******************************************************\ + * Define what sort of structure we're working with * + \*******************************************************/ + if (st->quirks & ST_Q_NEEDS_PAGE_0) + { + dat_len = sizeof(dat_page_0); + dat_ptr = (char *) &dat_page_0; + } + else + { + dat_len = sizeof(dat); + dat_ptr = (char *) &dat; + } + /*******************************************************\ * Set up for a mode select * \*******************************************************/ - bzero(&dat, sizeof(dat)); + bzero(dat_ptr, dat_len); bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = MODE_SELECT; - scsi_cmd.length = sizeof(dat); - dat.header.blk_desc_len = sizeof(struct blk_desc); - dat.header.buf_mode = 1; - dat.blk_desc.density = dsty_code; + scsi_cmd.length = dat_len; + ((struct dat *)dat_ptr)->header.blk_desc_len = sizeof(struct blk_desc); + ((struct dat *)dat_ptr)->header.dev_spec |= SMH_DSP_BUFF_MODE_ON; + ((struct dat *)dat_ptr)->blk_desc.density = st->density; if(st->flags & ST_FIXEDBLOCKS) { - lto3b( st->blkmin , dat.blk_desc.blklen); + lto3b( st->blksiz , ((struct dat *)dat_ptr)->blk_desc.blklen); + } + if (st->quirks & ST_Q_NEEDS_PAGE_0) + { + bcopy(st->sense_data, ((struct dat_page_0 *)dat_ptr)->sense_data, + sizeof(((struct dat_page_0 *)dat_ptr)->sense_data)); + /* the Tandberg tapes need the block size to */ + /* be set on each mode sense/select. */ } -/* lto3b( st->numblks , dat.blk_desc.nblocks); use defaults!!!! - lto3b( st->blksiz , dat.blk_desc.blklen); -*/ /*******************************************************\ * do the command * \*******************************************************/ - if (st_scsi_cmd(unit, + return (st_scsi_cmd(unit, &scsi_cmd, sizeof(scsi_cmd), - &dat, - sizeof(dat), + dat_ptr, + dat_len, 5000, - flags | SCSI_DATA_OUT) != 0) - { - if(!(flags & SCSI_SILENT)) - printf("could not mode select for unit %d\n", unit); - st_info_valid[unit] = FALSE; - return(FALSE); - } - return(TRUE); + NULL, + flags | SCSI_DATA_OUT) ); } /*******************************************************\ @@ -1202,28 +1392,87 @@ int unit,flags,dsty_code; st_space(unit,number,what,flags) int unit,number,what,flags; { + int error; struct scsi_space scsi_cmd; + struct st_data *st = st_data[unit]; + + /*******************************************************\ + * If we're at beginning of medium, now is the time to * + * set medium access density, fixed or variable-blocks * + * and, if fixed, the block size. * + \*******************************************************/ + if (st->flags & ST_AT_BOM && + (error = st_decide_mode(unit, TRUE))) + return (error); - /* if we are at a filemark now, we soon won't be*/ - st_data[unit].flags &= ~(ST_AT_FILEMARK | ST_AT_EOM); + switch (what) + { + case SP_BLKS: + if (st->flags & ST_PER_ACTION) + { + if (number > 0) + { + st->flags &= ~ST_PER_ACTION; + return(EIO); + } + else if (number < 0) + { + if (st->flags & ST_AT_FILEMARK) + { + /*******************************\ + * Handling of ST_AT_FILEMARK * + * in st_space will fill in the * + * right file mark count. * + \*******************************/ + error = st_space(unit, 0, SP_FILEMARKS, + flags); + if (error) return(error); + } + if (st->flags & ST_BLANK_READ) + { + st->flags &= ~ST_BLANK_READ; + return(EIO); + } + st->flags &= ~ST_EIO_PENDING; + } + } + break; + case SP_FILEMARKS: + if (st->flags & ST_EIO_PENDING) + { + if (number > 0) + { + st->flags &= ~ST_EIO_PENDING; + return(EIO); + } + else if (number < 0) + st->flags &= ~ST_EIO_PENDING; + } + if (st->flags & ST_AT_FILEMARK) + { + st->flags &= ~ST_AT_FILEMARK; + number--; + } + if (st->flags & ST_BLANK_READ && number < 0) + { + st->flags &= ~ST_BLANK_READ; + number++; + } + } + if (number == 0) + return(ESUCCESS); bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = SPACE; - scsi_cmd.code = what; + scsi_cmd.byte2 = what & SS_CODE; lto3b(number,scsi_cmd.number); - if (st_scsi_cmd(unit, + return (st_scsi_cmd(unit, &scsi_cmd, sizeof(scsi_cmd), 0, 0, 600000, /* 10 mins enough? */ - flags) != 0) - { - if(!(flags & SCSI_SILENT)) - printf("could not space st%d\n", unit); - st_info_valid[unit] = FALSE; - return(FALSE); - } - return(TRUE); + NULL, + flags)); } /*******************************************************\ * write N filemarks * @@ -1231,62 +1480,124 @@ int unit,number,what,flags; st_write_filemarks(unit,number,flags) int unit,number,flags; { + int error; struct scsi_write_filemarks scsi_cmd; + struct st_data *st = st_data[unit]; + + /*******************************************************\ + * It's hard to write a negative number of file marks. * + * Don't try. * + \*******************************************************/ + if (number < 0) + return (EINVAL); + + /*******************************************************\ + * If we're at beginning of medium, now is the time to * + * set medium access density, fixed or variable-blocks * + * and, if fixed, the block size. * + \*******************************************************/ + if (st->flags & ST_AT_BOM && number > 0 && + (error = st_decide_mode(unit, FALSE))) + return (error); + + switch (number) + { + case 0: /* really a command to sync the drive's buffers */ + break; + case 1: + if (st->flags & ST_FM_WRITTEN) + st->flags &= ~ST_WRITTEN; + else + st->flags |= ST_FM_WRITTEN; + st->flags &= ~ST_PER_ACTION; + break; + default: + st->flags &= ~(ST_PER_ACTION | ST_WRITTEN); + } - st_data[unit].flags &= ~(ST_AT_FILEMARK); bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = WRITE_FILEMARKS; lto3b(number,scsi_cmd.number); - if (st_scsi_cmd(unit, + return (st_scsi_cmd(unit, &scsi_cmd, sizeof(scsi_cmd), 0, 0, 100000, /* 10 secs.. (may need to repos head )*/ - flags) != 0) + NULL, + flags) ); +} + +/***************************************************************\ +* Make sure the right number of file marks is on tape if the * +* tape has been written. If the position argument is true, * +* leave the tape positioned where it was originally. * +* * +* nmarks returns the number of marks to skip (or, if position * +* true, which were skipped) to get back original position. * +\***************************************************************/ +st_chkeod(unit, position, nmarks, flags) +int unit; +int position; +int *nmarks; +int flags; +{ + int error; + struct st_data *st = st_data[unit]; + + switch (st->flags & (ST_WRITTEN | ST_FM_WRITTEN | ST_2FM_AT_EOD)) { - if(!(flags & SCSI_SILENT)) - printf("could not write_filemarks st%d\n", unit); - st_info_valid[unit] = FALSE; - return(FALSE); + default: + *nmarks = 0; + return(ESUCCESS); + case ST_WRITTEN: + case ST_WRITTEN | ST_FM_WRITTEN | ST_2FM_AT_EOD: + *nmarks = 1; + break; + case ST_WRITTEN | ST_2FM_AT_EOD: + *nmarks = 2; } - return(TRUE); + error = st_write_filemarks(unit, *nmarks, flags); + if (position && error == ESUCCESS) + error = st_space(unit, -*nmarks, SP_FILEMARKS, flags); + return (error); } + /*******************************************************\ -* load /unload (with retension if true) * +* load/unload (with retension if true) * \*******************************************************/ st_load(unit,type,flags) int unit,type,flags; { struct scsi_load scsi_cmd; + struct st_data *st = st_data[unit]; - st_data[unit].flags &= ~(ST_AT_FILEMARK | ST_AT_EOM); + st->flags &= ~ST_PER_MEDIA; bzero(&scsi_cmd, sizeof(scsi_cmd)); - scsi_cmd.op_code = LOAD_UNLOAD; - scsi_cmd.load=type; if (type == LD_LOAD) { - /*scsi_cmd.reten=TRUE;*/ - scsi_cmd.reten=FALSE; + /*scsi_cmd.how |= LD_RETEN;*/ + st->flags |= ST_AT_BOM; } else { - scsi_cmd.reten=FALSE; + int error, nmarks; + + error = st_chkeod(unit, FALSE, &nmarks, flags); + if (error != ESUCCESS) return(error); + st->flags &= ~ST_INFO_VALID; } - if (st_scsi_cmd(unit, + if(st->quirks & ST_Q_IGNORE_LOADS) return(0); + scsi_cmd.op_code = LOAD_UNLOAD; + scsi_cmd.how |= type; + return (st_scsi_cmd(unit, &scsi_cmd, sizeof(scsi_cmd), 0, 0, - 30000, /* 30 secs */ - flags) != 0) - { - if(!(flags & SCSI_SILENT)) - printf("cannot load/unload st%d\n", unit); - st_info_valid[unit] = FALSE; - return(FALSE); - } - return(TRUE); + 300000, /* 5 min */ + NULL, + flags)); } /*******************************************************\ * Prevent or allow the user to remove the tape * @@ -1296,23 +1607,25 @@ int unit,type,flags; { struct scsi_prevent scsi_cmd; + if (type == PR_ALLOW) + { + int error, nmarks; + + error = st_chkeod(unit, TRUE, &nmarks, flags); + if (error != ESUCCESS) return(error); + st_data[unit]->flags &= ~ST_INFO_VALID; + } bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = PREVENT_ALLOW; - scsi_cmd.prevent=type; - if (st_scsi_cmd(unit, + scsi_cmd.how=type; + return (st_scsi_cmd(unit, &scsi_cmd, sizeof(scsi_cmd), 0, 0, 5000, - flags) != 0) - { - if(!(flags & SCSI_SILENT)) - printf("cannot prevent/allow on st%d\n", unit); - st_info_valid[unit] = FALSE; - return(FALSE); - } - return(TRUE); + NULL, + flags)); } /*******************************************************\ * Rewind the device * @@ -1321,26 +1634,348 @@ st_rewind(unit,immed,flags) int unit,immed,flags; { struct scsi_rewind scsi_cmd; + struct st_data *st = st_data[unit]; + int error, nmarks; - st_data[unit].flags &= ~(ST_AT_FILEMARK | ST_AT_EOM); + error = st_chkeod(unit, FALSE, &nmarks, flags); + if (error != ESUCCESS) return(error); + st->flags &= ~ST_PER_ACTION; + st->flags |= ST_AT_BOM; bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = REWIND; - scsi_cmd.immed=immed; - if (st_scsi_cmd(unit, + scsi_cmd.byte2 = immed ? SR_IMMED : 0; + return (st_scsi_cmd(unit, &scsi_cmd, sizeof(scsi_cmd), 0, 0, immed?5000:300000, /* 5 sec or 5 min */ - flags) != 0) + NULL, + flags)); +} + +/***************************************************************\ +* ststart looks to see if there is a buf waiting for the device * +* and that the device is not already busy. If both are true, * +* It deques the buf and creates a scsi command to perform the * +* transfer in the buf. The transfer request will call st_done * +* on completion, which will in turn call this routine again * +* so that the next queued transfer is performed. * +* The bufs are queued by the strategy routine (ststrategy) * +* * +* This routine is also called after other non-queued requests * +* have been made of the scsi driver, to ensure that the queue * +* continues to be drained. * +\***************************************************************/ +/* ststart() is called at splbio */ +ststart(unit) +{ + int drivecount; + register struct buf *bp = 0; + register struct buf *dp; + struct scsi_rw_tape cmd; + int blkno, nblk; + struct st_data *st = st_data[unit]; + int flags; + + + +#ifdef STDEBUG + if(scsi_debug & PRINTROUTINES) printf("ststart%d ",unit); +#endif /*STDEBUG*/ + /*******************************************************\ + * See if there is a buf to do and we are not already * + * doing one * + \*******************************************************/ + if(st->scsi_xfer.flags & INUSE) + { + return; /* unit already underway */ + } +trynext: + if(st->xfer_block_wait) /* a special awaits, let it proceed first */ + { + wakeup(&(st->xfer_block_wait)); + return; + } + + if ((bp = st->buf_queue) == NULL) + { + return; /* no work to bother with */ + } + st->buf_queue = bp->b_actf; + + + + /*******************************************************\ + * only FIXEDBLOCK devices have pending operations * + \*******************************************************/ + if(st->flags & ST_FIXEDBLOCKS) + { + /*******************************************************\ + * If we are at a filemark but have not reported it yet * + * then we should report it now * + \*******************************************************/ + if(st->flags & ST_AT_FILEMARK) + { + if ((bp->b_flags & B_READ) == B_WRITE) + { + /***************************************\ + * Handling of ST_AT_FILEMARK in * + * st_space will fill in the right file * + * mark count. * + \***************************************/ + if (st_space(unit, 0, SP_FILEMARKS, 0) != + ESUCCESS) goto badnews; + } + else + { + bp->b_resid = bp->b_bcount; + bp->b_error = 0; + bp->b_flags &= ~B_ERROR; + st->flags &= ~ST_AT_FILEMARK; + biodone(bp); + goto trynext; + } + } + /*******************************************************\ + * If we are at EIO (e.g. EOM) but have not reported it * + * yet then we should report it now * + \*******************************************************/ + if(st->flags & ST_EIO_PENDING) + { + bp->b_resid = bp->b_bcount; + bp->b_error = EIO; + bp->b_flags |= B_ERROR; + st->flags &= ~ST_EIO_PENDING; + biodone(bp); + goto trynext; + } + } + /*******************************************************\ + * Fill out the scsi command * + \*******************************************************/ + bzero(&cmd, sizeof(cmd)); + if((bp->b_flags & B_READ) == B_WRITE) + { + cmd.op_code = WRITE_COMMAND_TAPE; + st->flags &= ~ST_FM_WRITTEN; + st->flags |= ST_WRITTEN; + flags = SCSI_DATA_OUT; + } + else + { + cmd.op_code = READ_COMMAND_TAPE; + flags = SCSI_DATA_IN; + } + + /*******************************************************\ + * Handle "fixed-block-mode" tape drives by using the * + * block count instead of the length. * + \*******************************************************/ + if(st->flags & ST_FIXEDBLOCKS) + { + cmd.byte2 |= SRWT_FIXED; + lto3b(bp->b_bcount/st->blksiz,cmd.len); + } + else + { + lto3b(bp->b_bcount,cmd.len); + } + + /*******************************************************\ + * go ask the adapter to do all this for us * + \*******************************************************/ + if (st_scsi_cmd(unit, + &cmd, + sizeof(cmd), + (u_char *)bp->b_un.b_addr, + bp->b_bcount, + 100000, + bp, + flags | SCSI_NOSLEEP ) != SUCCESSFULLY_QUEUED) + + { +badnews: + printf("st%d: oops not queued\n",unit); + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + biodone(bp); + return; + } + stqueues++; +} + +/*******************************************************\ +* This routine is called by the scsi interrupt when * +* the transfer is complete. +\*******************************************************/ +int st_done(unit,xs) +int unit; +struct scsi_xfer *xs; +{ + struct buf *bp; + int retval; + struct st_data *st = st_data[unit]; + +#ifdef STDEBUG + if(scsi_debug & PRINTROUTINES) printf("st_done%d ",unit); +#endif /*STDEBUG*/ +#ifdef PARANOID + if (! (xs->flags & INUSE)) + panic("scsi_xfer not in use!"); +#endif /*PARANOID*/ + if((bp = xs->bp)== NULL) { - if(!(flags & SCSI_SILENT)) - printf("could not rewind st%d\n", unit); - st_info_valid[unit] = FALSE; - return(FALSE); + wakeup(xs); + return; } - return(TRUE); + switch(xs->error) + { + case XS_NOERROR: + bp->b_flags &= ~B_ERROR; + bp->b_error = 0; + bp->b_resid = 0; + break; + case XS_SENSE: + retval = (st_interpret_sense(unit,xs)); + bp->b_resid = xs->resid; /* already multiplied by blksiz */ + if(retval) + { + /***************************************\ + * We have a real error, the bit should * + * be set to indicate this. The return * + * value will contain the unix error code* + * that the error interpretation routine * + * thought was suitable, so pass this * + * value back in the buf structure. * + * Furthermore we return information * + * saying that no data was transferred * + * All status is now suspect * + \***************************************/ + bp->b_flags |= B_ERROR; + bp->b_error = retval; + bp->b_resid = bp->b_bcount; + st->flags &= ~ST_PER_ACTION; + } + else + { + /***********************************************\ + * The error interpretation code has declared * + * that it wasn't a real error, or at least that * + * we should be ignoring it if it was. * + \***********************************************/ + if(xs->resid == 0) + { + /***************************************\ + * we apparently had a corrected error * + * or something. * + * pretend the error never happenned * + \***************************************/ + bp->b_flags &= ~B_ERROR; + bp->b_error = 0; + break; + } + if ( xs->resid != xs->datalen ) + { + /***************************************\ + * Here we have the tricky part.. * + * We successfully read less data than * + * we requested. (but not 0) * + *------for variable blocksize tapes:----* + * UNDER 386BSD: * + * We should legitimatly have the error * + * bit set, with the error value set to * + * zero.. This is to indicate to the * + * physio code that while we didn't get * + * as much information as was requested, * + * we did reach the end of the record * + * and so physio should not call us * + * again for more data... we have it all * + * SO SET THE ERROR BIT! * + * * + * UNDER NetBSD: * + * To indicate the same as above, we * + * need only have a non 0 resid that is * + * less than the b_bcount, but the * + * ERROR BIT MUST BE CLEAR! (sigh) * + * * + *-------for fixed blocksize device------* + * We read some successful records * + * before hitting the EOF or EOT. These * + * must be passed to the user, before we * + * report the EOx. We will report the * + * EOx NEXT time. * + \***************************************/ +#ifdef NETBSD + bp->b_flags &= ~B_ERROR; +#else + bp->b_flags |= B_ERROR; +#endif + bp->b_error = 0; + xs->error = XS_NOERROR; + break; + } + else + { + /***************************************\ + * We have come out of the error handler * + * with no error code. We have also not * + * transferred any data (would have gone * + * to the previous clause). * + * This must be an EOF * + * Any caller request to read no * + * data would have been short-circuited * + * at st_read or ststrategy. * + * * + * At least all o/s agree that: * + * 0 bytes read with no error is EOF * + \***************************************/ + + bp->b_error = 0; + bp->b_flags &= ~B_ERROR; + st->flags &= ~ST_AT_FILEMARK; + break; + } + } + break; + + case XS_TIMEOUT: + printf("st%d: timeout\n",unit); + + case XS_BUSY: /* should retry */ /* how? */ + /************************************************/ + /* SHOULD put buf back at head of queue */ + /* and decrement retry count in (*xs) */ + /* HOWEVER, this should work as a kludge */ + /************************************************/ + if(xs->retries--) + { + xs->flags &= ~ITSDONE; + xs->error = XS_NOERROR; + if ( (*(st->sc_sw->scsi_cmd))(xs) + == SUCCESSFULLY_QUEUED) + { /* don't wake the job, ok? */ + return; + } + printf("st%d: device busy\n",unit); + xs->flags |= ITSDONE; + } + + case XS_DRIVER_STUFFUP: + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + break; + default: + printf("st%d: unknown error category from scsi driver\n" + ,unit); + } + biodone(bp); + xs->flags = 0; /* no longer in use */ + ststart(unit); /* If there's another waiting.. do it */ } + + + /*******************************************************\ * ask the scsi driver to perform a command for us. * * Call it through the switch table, and tell it which * @@ -1350,115 +1985,120 @@ int unit,immed,flags; * Also tell it where to read/write the data, and how * * long the data is supposed to be * \*******************************************************/ -int st_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags) +int st_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,bp,flags) int unit,flags; struct scsi_generic *scsi_cmd; int cmdlen; int timeout; u_char *data_addr; +struct buf *bp; int datalen; { struct scsi_xfer *xs; int retval; int s; - struct st_data *st = st_data + unit; + struct st_data *st = st_data[unit]; +#ifdef STDEBUG if(scsi_debug & PRINTROUTINES) printf("\nst_scsi_cmd%d ",unit); - if(st->sc_sw) /* If we have a scsi driver */ +#endif /*STDEBUG*/ +#ifdef PARANOID + if(st->sc_sw == NULL) /* If we have no scsi driver */ { + printf("st%d: not set up\n",unit); + return(EINVAL); + } +#endif /*PARANOID*/ - xs = &(st_scsi_xfer[unit]); - if(!(flags & SCSI_NOMASK)) - s = splbio(); - st_xfer_block_wait[unit]++; /* there is someone waiting */ - while (xs->flags & INUSE) - { - sleep(&st_xfer_block_wait[unit],PRIBIO+1); - } - st_xfer_block_wait[unit]--; - xs->flags = INUSE; - if(!(flags & SCSI_NOMASK)) - splx(s); + xs = &(st->scsi_xfer); + if(!(flags & SCSI_NOMASK)) + s = splbio(); + st->xfer_block_wait++; /* there is someone waiting */ + while (xs->flags & INUSE) + { + if(flags & SCSI_NOSLEEP) + return EBUSY; + sleep(&(st->xfer_block_wait),PRIBIO+1); + } + st->xfer_block_wait--; + xs->flags = INUSE; + if(!(flags & SCSI_NOMASK)) + splx(s); - /*******************************************************\ - * Fill out the scsi_xfer structure * - \*******************************************************/ - xs->flags |= flags; - xs->adapter = st->ctlr; - xs->targ = st->targ; - xs->lu = st->lu; - xs->retries = ST_RETRIES; - xs->timeout = timeout; - xs->cmd = scsi_cmd; - xs->cmdlen = cmdlen; - xs->data = data_addr; - xs->datalen = datalen; - xs->resid = datalen; - xs->when_done = (flags & SCSI_NOMASK) - ?(int (*)())0 - :st_done; - xs->done_arg = unit; - xs->done_arg2 = (int)xs; -retry: xs->error = XS_NOERROR; - xs->bp = 0; - retval = (*(st->sc_sw->scsi_cmd))(xs); - switch(retval) + /*******************************************************\ + * Fill out the scsi_xfer structure * + \*******************************************************/ + xs->flags |= flags; + xs->adapter = st->ctlr; + xs->targ = st->targ; + xs->lu = st->lu; + xs->retries = bp?0:ST_RETRIES;/*can't retry on IO*/ + xs->timeout = timeout; + xs->cmd = scsi_cmd; + xs->cmdlen = cmdlen; + xs->data = data_addr; + xs->datalen = datalen; + xs->resid = datalen; + xs->when_done = st_done; + xs->done_arg = unit; + xs->done_arg2 = (int)xs; + xs->bp = bp; +retry: xs->error = XS_NOERROR; + + /***********************************************\ + * Ask the adapter to do the command for us * + \***********************************************/ + retval = (*(st->sc_sw->scsi_cmd))(xs); + + /***********************************************\ + * IO operations are handled differently.. * + * Physio does the sleep, and error handling is * + * Done in st_done at interrupt time * + \***********************************************/ + if(bp) return retval; + + /***********************************************\ + * Wait for the result if queued, or handle the * + * error if it was rejected.. * + \***********************************************/ + switch(retval) + { + case SUCCESSFULLY_QUEUED: + s = splbio(); + while(!(xs->flags & ITSDONE)) + sleep(xs,PRIBIO+1); + splx(s); + /*******************************\ + * finished.. check for failure * + * Fall through...... * + \*******************************/ + case HAD_ERROR: + case COMPLETE: + switch(xs->error) { - case SUCCESSFULLY_QUEUED: - s = splbio(); - while(!(xs->flags & ITSDONE)) - sleep(xs,PRIBIO+1); - splx(s); - - case HAD_ERROR: - case COMPLETE: - switch(xs->error) + case XS_NOERROR: + retval = ESUCCESS; + break; + case XS_SENSE: + retval = (st_interpret_sense(unit,xs)); + /* only useful for reads *//* why did I say that?*/ + if (retval) + { /* error... don't care about filemarks */ + st->flags &= ~ST_PER_ACTION; + } + else { - case XS_NOERROR: + xs->error = XS_NOERROR; retval = ESUCCESS; - break; - case XS_SENSE: - retval = (st_interpret_sense(unit,xs)); - /* only useful for reads */ - if (retval) - { /* error... don't care about filemarks */ - st->flags &= ~(ST_AT_FILEMARK - | ST_AT_EOM); - } - else - { - xs->error = XS_NOERROR; - retval = ESUCCESS; - } - break; - case XS_DRIVER_STUFFUP: - retval = EIO; - break; - case XS_TIMEOUT: - if(xs->retries-- ) - { - xs->flags &= ~ITSDONE; - goto retry; - } - retval = EIO; - break; - case XS_BUSY: - if(xs->retries-- ) - { - xs->flags &= ~ITSDONE; - goto retry; - } - retval = EIO; - break; - default: - retval = EIO; - printf("st%d: unknown error category from scsi driver\n" - ,unit); - break; - } + } + break; + case XS_DRIVER_STUFFUP: + retval = EIO; break; - case TRY_AGAIN_LATER: + case XS_BUSY: + /* should sleep 1 sec here */ + case XS_TIMEOUT: if(xs->retries-- ) { xs->flags &= ~ITSDONE; @@ -1468,15 +2108,25 @@ retry: xs->error = XS_NOERROR; break; default: retval = EIO; + printf("st%d: unknown error category from scsi driver\n" + ,unit); + break; + } + break; + case TRY_AGAIN_LATER: + if(xs->retries-- ) + { + xs->flags &= ~ITSDONE; + /* should delay here */ + goto retry; } - xs->flags = 0; /* it's free! */ - ststart(unit); - } - else - { - printf("st%d: not set up\n",unit); - return(EINVAL); + retval = EIO; + break; + default: + retval = EIO; } + xs->flags = 0; /* it's free! */ + ststart(unit); return(retval); } /***************************************************************\ @@ -1491,6 +2141,17 @@ struct scsi_xfer *xs; struct scsi_sense_data *sense; int key; int silent = xs->flags & SCSI_SILENT; + struct st_data *st = st_data[unit]; + int info; + static char *error_mes[] = { "soft error (corrected)", + "not ready", "medium error", + "non-media hardware failure", "illegal request", + "unit attention", "tape is write-protected", + "no data found", "vendor unique", + "copy aborted", "command aborted", + "search returned equal", "volume overflow", + "verify miscompare", "unknown error key" + }; /***************************************************************\ * If errors are ok, report a success * @@ -1498,22 +2159,22 @@ struct scsi_xfer *xs; if(xs->flags & SCSI_ERR_OK) return(ESUCCESS); /***************************************************************\ - * Get the sense fields and work out what CLASS * + * Get the sense fields and work out what code * \***************************************************************/ sense = &(xs->sense); +#ifdef STDEBUG if(st_debug) { int count = 0; - printf("code%x class%x valid%x\n" - ,sense->error_code - ,sense->error_class - ,sense->valid); + printf("code%x valid%x\n" + ,sense->error_code & SSD_ERRCODE + ,sense->error_code & SSD_ERRCODE_VALID ? 1 : 0); printf("seg%x key%x ili%x eom%x fmark%x\n" ,sense->ext.extended.segment - ,sense->ext.extended.sense_key - ,sense->ext.extended.ili - ,sense->ext.extended.eom - ,sense->ext.extended.filemark); + ,sense->ext.extended.flags & SSD_KEY + ,sense->ext.extended.flags & SSD_ILI ? 1 : 0 + ,sense->ext.extended.flags & SSD_EOM ? 1 : 0 + ,sense->ext.extended.flags & SSD_FILEMARK ? 1 : 0); printf("info: %x %x %x %x followed by %d extra bytes\n" ,sense->ext.extended.info[0] ,sense->ext.extended.info[1] @@ -1527,248 +2188,183 @@ struct scsi_xfer *xs; } printf("\n"); } - switch(sense->error_class) +#endif /*STDEBUG*/ + if(sense->error_code & SSD_ERRCODE_VALID) { - /***************************************************************\ - * If it's class 7, use the extended stuff and interpret the key * - \***************************************************************/ - case 7: + info = ntohl(*((long *)sense->ext.extended.info)); + } + else { - if(sense->ext.extended.eom) - { - st_data[unit].flags |= ST_AT_EOM; - } + info = xs->datalen; /* bad choice if fixed blocks */ + } - if(sense->ext.extended.filemark) - { - st_data[unit].flags |= ST_AT_FILEMARK; - } - if(sense->ext.extended.ili) + switch(sense->error_code & SSD_ERRCODE) + { + /***************************************************************\ + * If it's code 70, use the extended stuff and interpret the key * + \***************************************************************/ + case 0x70: + if(st->flags & ST_FIXEDBLOCKS) { - if(sense->valid) + xs->resid = info * st->blksiz; + if(sense->ext.extended.flags & SSD_EOM) { - /*******************************\ - * In all ili cases, note that * - * the resid is non-0 AND not * - * unchanged. * - \*******************************/ - xs->resid - = ntohl(*((long *)sense->ext.extended.info)); - if(xs->bp) - { - if(xs->resid < 0) - { /* never on block devices */ - /***********************\ - * it's only really bad * - * if we have lost data * - * (the record was * - * bigger than the read) * - \***********************/ - return(EIO); - } - } + st->flags |= ST_EIO_PENDING; } - else - { /* makes no sense.. complain */ - printf("BAD length error?"); + if(sense->ext.extended.flags & SSD_FILEMARK) + { + st->flags |= ST_AT_FILEMARK; } - }/* there may be some other error. check the rest */ - - key=sense->ext.extended.sense_key; - switch(key) - { - case 0x0: - return(ESUCCESS); - case 0x1: - if(!silent) + if(sense->ext.extended.flags & SSD_ILI) { - printf("st%d: soft error(corrected) ", unit); - if(sense->valid) - { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); - } - else + st->flags |= ST_EIO_PENDING; + if (sense->error_code & SSD_ERRCODE_VALID && + !silent) + printf("st%d: %d-byte block wrong size" + "\n", unit, xs->datalen - info); + + /***************************************\ + * This quirk code helps the drive read * + * the first tape block, regardless of * + * format. That is required for these * + * drives to return proper MODE SENSE * + * information. * + \***************************************/ + if ((st->quirks & ST_Q_SNS_HLP) && + !(st->flags & ST_INFO_VALID)) { - printf("\n"); + st->blksiz -= 512; } } - return(ESUCCESS); - case 0x2: - if(!silent) printf("st%d: not ready\n ", unit); - return(ENODEV); - case 0x3: - if(!silent) + /***********************************************\ + * If no data was tranfered, do it immediatly * + \***********************************************/ + if(xs->resid >= xs->datalen) { - printf("st%d: medium error ", unit); - if(sense->valid) + if(st->flags & ST_EIO_PENDING) { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); + return EIO; } - else + if(st->flags & ST_AT_FILEMARK) { - printf("\n"); + return 0; } } - return(EIO); - case 0x4: - if(!silent) printf("st%d: non-media hardware failure\n ", - unit); - return(EIO); - case 0x5: - if(!silent) printf("st%d: illegal request\n ", unit); - return(EINVAL); - case 0x6: - if(!silent) printf("st%d: Unit attention.\n ", unit); - st_data[unit].flags &= ~(ST_AT_FILEMARK|ST_AT_EOM); - st_info_valid[unit] = FALSE; - if (st_data[unit].flags & ST_OPEN) /* TEMP!!!! */ + } + else + { + xs->resid = xs->datalen; /* to be sure */ + if(sense->ext.extended.flags & SSD_EOM) + { return(EIO); - else - return(ESUCCESS); - case 0x7: - if(!silent) + } + if(sense->ext.extended.flags & SSD_FILEMARK) { - printf("st%d: attempted protection violation " - , unit); - if(sense->valid) - { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); - } - else - { - printf("\n"); - } + return 0; } - return(EACCES); - case 0x8: - if(!silent) + if(sense->ext.extended.flags & SSD_ILI) { - printf("st%d: block wrong state (worm)\n " - , unit); - if(sense->valid) - { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); - } - else + if(info < 0) + /***************************************\ + * the record was bigger than the read * + \***************************************/ { - printf("\n"); + if (!silent) + printf("st%d: %d-byte record " + "too big\n", unit, + xs->datalen - info); + return(EIO); } + xs->resid = info; } - return(EIO); - case 0x9: - if(!silent) printf("st%d: vendor unique\n", - unit); - return(EIO); - case 0xa: - if(!silent) printf("st%d: copy aborted\n ", - unit); - return(EIO); - case 0xb: - if(!silent) printf("st%d: command aborted\n ", - unit); - return(EIO); - case 0xc: - if(!silent) + }/* there may be some other error. check the rest */ + + key=sense->ext.extended.flags & SSD_KEY; + + if (!silent && key > 0 && (key != 0x8 || + st->flags & (ST_2FM_AT_EOD | ST_BLANK_READ))) + { + printf("st%d: %s", unit, error_mes[key - 1]); + if(sense->error_code & SSD_ERRCODE_VALID) { - printf("st%d: search returned\n ", unit); - if(sense->valid) + switch (key) { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); - } - else - { - printf("\n"); + case 0x2: /* NOT READY */ + case 0x5: /* ILLEGAL REQUEST */ + case 0x6: /* UNIT ATTENTION */ + case 0x7: /* DATA PROTECT */ + break; + case 0x8: /* BLANK CHECK */ + printf(", requested size: %d (decimal)", + info); + break; + default: + printf(", info = %d (decimal)", info); } } + printf("\n"); + } + + switch (key) + { + case 0x0: /* NO SENSE */ + case 0x1: /* RECOVERED ERROR */ + if(xs->resid == xs->datalen) xs->resid = 0; + case 0xc: /* EQUAL */ return(ESUCCESS); - case 0xd: - if(!silent) printf("st%d: volume overflow\n ", - unit); + case 0x2: /* NOT READY */ + return(ENODEV); + case 0x5: /* ILLEGAL REQUEST */ + return(EINVAL); + case 0x6: /* UNIT ATTENTION */ + st->flags &= ~ST_PER_MEDIA; + if (st->flags & ST_OPEN) /* TEMP!!!! */ + return(EIO); + else + return(ESUCCESS); + case 0x7: /* DATA PROTECT */ + return(EACCES); + case 0xd: /* VOLUME OVERFLOW */ return(ENOSPC); - case 0xe: - if(!silent) + case 0x8: /* BLANK CHECK */ + /***********************************************\ + * This quirk code helps the drive read the * + * first tape block, regardless of format. That * + * is required for these drives to return proper * + * MODE SENSE information. * + \***********************************************/ + if ((st->quirks & ST_Q_SNS_HLP) && + !(st->flags & ST_INFO_VALID)) { - printf("st%d: verify miscompare\n ", unit); - if(sense->valid) - { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); - } - else - { - printf("\n"); - } + st->blksiz -= 512; } - return(EIO); - case 0xf: - if(!silent) printf("st%d: unknown error key\n ", - unit); + else if (!(st->flags & (ST_2FM_AT_EOD | ST_BLANK_READ))) + { + st->flags |= ST_BLANK_READ; + xs->resid = xs->datalen; + return(ESUCCESS); + } + default: return(EIO); } - break; - } /***************************************************************\ - * If it's NOT class 7, just report it. * + * If it's NOT code 70, just report it. * \***************************************************************/ - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: + default: + if (!silent) { - if(!silent) printf("st%d: error class %d code %d\n", - unit, - sense->error_class, - sense->error_code); - if(sense->valid) - if(!silent) printf("block no. %d (decimal)\n", - (sense->ext.unextended.blockhi <<16), - + (sense->ext.unextended.blockmed <<8), - + (sense->ext.unextended.blocklow )); + printf("st%d: error code %d", unit, + sense->error_code & SSD_ERRCODE); + if(sense->error_code & SSD_ERRCODE_VALID) + { + printf(" at block no. %d (decimal)", + (sense->ext.unextended.blockhi << 16) + + (sense->ext.unextended.blockmed << 8) + + sense->ext.unextended.blocklow); + } + printf("\n"); } return(EIO); } } - -#if defined(OSF) - -stsize(dev_t dev) -{ - printf("stsize() -- not implemented\n"); - return(0); -} - -stdump() -{ - printf("stdump() -- not implemented\n"); - return(-1); -} - -#endif /* defined(OSF) */ - diff --git a/sys/stand/cat.c b/sys/stand/cat.c index 6f4067c205..06580ce58b 100644 --- a/sys/stand/cat.c +++ b/sys/stand/cat.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)cat.c 7.5 (Berkeley) 6/28/90 + * from: @(#)cat.c 7.5 (Berkeley) 6/28/90 + * $Id$ */ main() diff --git a/sys/stand/copy.c b/sys/stand/copy.c index b77d5e25d0..6cc5d8c64b 100644 --- a/sys/stand/copy.c +++ b/sys/stand/copy.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)copy.c 7.7 (Berkeley) 5/21/91 + * from: @(#)copy.c 7.7 (Berkeley) 5/21/91 + * $Id$ */ #define BSIZE 10240 diff --git a/sys/stand/dev.c b/sys/stand/dev.c index bfe3b05340..551a8c080f 100644 --- a/sys/stand/dev.c +++ b/sys/stand/dev.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)dev.c 7.14 (Berkeley) 5/5/91 + * from: @(#)dev.c 7.14 (Berkeley) 5/5/91 + * $Id$ */ #include diff --git a/sys/stand/ls.c b/sys/stand/ls.c index 6e18262da6..d4f453b2f9 100644 --- a/sys/stand/ls.c +++ b/sys/stand/ls.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ls.c 7.9 (Berkeley) 6/28/90 + * from: @(#)ls.c 7.9 (Berkeley) 6/28/90 + * $Id$ */ #include "sys/param.h" diff --git a/sys/stand/printf.c b/sys/stand/printf.c index e6cb572517..aef0884cf5 100644 --- a/sys/stand/printf.c +++ b/sys/stand/printf.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)printf.c 5.6 (Berkeley) 5/25/91 + * from: @(#)printf.c 5.6 (Berkeley) 5/25/91 + * $Id$ */ /* diff --git a/sys/stand/saerrno.h b/sys/stand/saerrno.h index 3a2127ec17..48555f7c20 100644 --- a/sys/stand/saerrno.h +++ b/sys/stand/saerrno.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)saerrno.h 7.3 (Berkeley) 6/28/90 + * from: @(#)saerrno.h 7.3 (Berkeley) 6/28/90 + * $Id$ */ extern int errno; /* just like unix */ diff --git a/sys/stand/saioctl.h b/sys/stand/saioctl.h index 6d493045ca..99574edb8e 100644 --- a/sys/stand/saioctl.h +++ b/sys/stand/saioctl.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)saioctl.h 7.4 (Berkeley) 6/28/90 + * from: @(#)saioctl.h 7.4 (Berkeley) 6/28/90 + * $Id$ */ /* ioctl's -- for disks just now */ diff --git a/sys/stand/stat.c b/sys/stand/stat.c index d75f5f6fa3..2ec23843e8 100644 --- a/sys/stand/stat.c +++ b/sys/stand/stat.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)stat.c 7.1 (Berkeley) 5/5/91 + * from: @(#)stat.c 7.1 (Berkeley) 5/5/91 + * $Id$ */ #include diff --git a/sys/sys/acct.h b/sys/sys/acct.h index 110eca44c1..1b2cc1e1c2 100644 --- a/sys/sys/acct.h +++ b/sys/sys/acct.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)acct.h 7.3 (Berkeley) 2/15/91 + * from: @(#)acct.h 7.3 (Berkeley) 2/15/91 + * $Id$ */ /* diff --git a/sys/sys/asm.h b/sys/sys/asm.h index 6085dcd5e8..23fcfe2826 100644 --- a/sys/sys/asm.h +++ b/sys/sys/asm.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)asm.h 5.5 (Berkeley) 5/7/91 + * from: @(#)asm.h 5.5 (Berkeley) 5/7/91 + * $Id$ */ #ifdef PROF diff --git a/sys/sys/buf.h b/sys/sys/buf.h index c01df7a199..717b20403a 100644 --- a/sys/sys/buf.h +++ b/sys/sys/buf.h @@ -30,15 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)buf.h 7.11 (Berkeley) 5/9/90 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00007 - * -------------------- ----- ---------------------- - * - * 20 Aug 92 David Greenman Fixed buffer cache hash index - * calculation + * from: @(#)buf.h 7.11 (Berkeley) 5/9/90 + * $Id$ */ /* diff --git a/sys/sys/callout.h b/sys/sys/callout.h index 522be36404..a0bea88bbc 100644 --- a/sys/sys/callout.h +++ b/sys/sys/callout.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)callout.h 7.2 (Berkeley) 2/15/91 + * from: @(#)callout.h 7.2 (Berkeley) 2/15/91 + * $Id$ */ struct callout { diff --git a/sys/sys/cdefs.h b/sys/sys/cdefs.h index fbe572a444..90e11dc6de 100644 --- a/sys/sys/cdefs.h +++ b/sys/sys/cdefs.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)cdefs.h 7.6 (Berkeley) 5/4/91 + * from: @(#)cdefs.h 8.1 (Berkeley) 6/2/93 + * $Id$ */ #ifndef _CDEFS_H_ @@ -52,27 +53,48 @@ * strings produced by the __STRING macro, but this only works with ANSI C. */ #if defined(__STDC__) || defined(__cplusplus) +#if defined(__P) +#undef __P +#endif /* defined(__P) */ #define __P(protos) protos /* full-blown ANSI C */ #define __CONCAT(x,y) x ## y #define __STRING(x) #x #else /* !(__STDC__ || __cplusplus) */ +#if defined(__P) +#undef __P +#endif /* defined(__P) */ #define __P(protos) () /* traditional C preprocessor */ #define __CONCAT(x,y) x/**/y #define __STRING(x) "x" -#ifdef __GNUC__ -#define const __const /* GCC: ANSI C with -traditional */ -#define inline __inline -#define signed __signed -#define volatile __volatile - -#else /* !__GNUC__ */ -#define const /* delete ANSI C keywords */ -#define inline -#define signed -#define volatile -#endif /* !__GNUC__ */ +/* delete ANSI C keywords */ +#define const +#define inline +#define signed +#define volatile #endif /* !(__STDC__ || __cplusplus) */ +/* + * GCC has extensions for declaring functions as const (`pure' - always returns + * the same value given the same inputs, i.e., has no external state and + * no side effects) and volatile (nonreturning or `dead'). + * These mainly affect optimization and warnings. + * + * To facilitate portability of a non-standard extension we define __pure + * and __dead and use these for qualifying functions. Non-gcc compilers + * which have similar extensions can then define these appropriately. + * + * Unfortunately, GCC complains if these are used under strict ANSI mode + * (`gcc -ansi -pedantic'), hence we need to define them only if compiling + * without this. + */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define __dead __volatile +#define __pure __const +#else +#define __dead +#define __pure +#endif + #endif /* !_CDEFS_H_ */ diff --git a/sys/sys/cdio.h b/sys/sys/cdio.h index 0bd4ea7748..202e8540ac 100644 --- a/sys/sys/cdio.h +++ b/sys/sys/cdio.h @@ -1,13 +1,11 @@ /* + * 16 Feb 93 Julian Elischer (julian@dialix.oz.au) * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- - * - * 16 Feb 93 Julian Elischer ADDED for SCSI system + * $Id$ */ /* Shared between kernel & process */ +#ifndef SYS_CDIO_H +#define SYS_CDIO_H struct cd_toc_entry { u_char :8; @@ -145,5 +143,17 @@ struct ioc_vol #define CDIOCSTOP _IO('c',23) #define CDIOCEJECT _IO('c',24) +struct ioc_play_msf +{ + u_char start_m; + u_char start_s; + u_char start_f; + u_char end_m; + u_char end_s; + u_char end_f; +}; +#define CDIOCPLAYMSF _IOW('c',25,struct ioc_play_msf) + +#endif /* SYS_CDIO_H */ diff --git a/sys/sys/chio.h b/sys/sys/chio.h index 532e6eed54..1106e2f321 100644 --- a/sys/sys/chio.h +++ b/sys/sys/chio.h @@ -1,43 +1,7 @@ /* - * Copyright (c) 1982, 1986 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)chio.h 7.6 (Berkeley) 2/5/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- - * * 16 Feb 93 Julian Elischer ADDED for SCSI system + * + * $Id$ */ /* This is a "convertet" mtio.h from 386BSD diff --git a/sys/sys/clist.h b/sys/sys/clist.h index 7181093869..12272d5795 100644 --- a/sys/sys/clist.h +++ b/sys/sys/clist.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)clist.h 7.3 (Berkeley) 2/15/91 + * from: @(#)clist.h 7.3 (Berkeley) 2/15/91 + * $Id$ */ struct cblock { diff --git a/sys/sys/conf.h b/sys/sys/conf.h index ac6034017e..d5f7882927 100644 --- a/sys/sys/conf.h +++ b/sys/sys/conf.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)conf.h 7.9 (Berkeley) 5/5/91 + * from: @(#)conf.h 7.9 (Berkeley) 5/5/91 + * $Id$ */ /* diff --git a/sys/sys/dir.h b/sys/sys/dir.h index bc4ca3ffa9..9cf712f495 100644 --- a/sys/sys/dir.h +++ b/sys/sys/dir.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)dir.h 7.3 (Berkeley) 2/5/91 + * from: @(#)dir.h 7.3 (Berkeley) 2/5/91 + * $Id$ */ /* diff --git a/sys/sys/disklabel.h b/sys/sys/disklabel.h index c49ffeaa44..3e795989d1 100644 --- a/sys/sys/disklabel.h +++ b/sys/sys/disklabel.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)disklabel.h 7.19 (Berkeley) 5/7/91 + * from: @(#)disklabel.h 7.19 (Berkeley) 5/7/91 + * $Id$ */ /* diff --git a/sys/sys/dkbad.h b/sys/sys/dkbad.h index 9857d97b92..4f429f4472 100644 --- a/sys/sys/dkbad.h +++ b/sys/sys/dkbad.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)dkbad.h 7.2 (Berkeley) 2/15/91 + * from: @(#)dkbad.h 7.2 (Berkeley) 2/15/91 + * $Id$ */ /* diff --git a/sys/sys/dkstat.h b/sys/sys/dkstat.h index 320c65db57..7d9f8bc043 100644 --- a/sys/sys/dkstat.h +++ b/sys/sys/dkstat.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)dkstat.h 7.5 (Berkeley) 2/15/91 + * from: @(#)dkstat.h 7.5 (Berkeley) 2/15/91 + * $Id$ */ #define CP_USER 0 diff --git a/sys/sys/dmap.h b/sys/sys/dmap.h index 5284b410a7..26fe1c1914 100644 --- a/sys/sys/dmap.h +++ b/sys/sys/dmap.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)dmap.h 7.3 (Berkeley) 2/15/91 + * from: @(#)dmap.h 7.3 (Berkeley) 2/15/91 + * $Id$ */ #ifndef _DMAP_H_ diff --git a/sys/sys/domain.h b/sys/sys/domain.h index 9555516812..2c4f2bf2a7 100644 --- a/sys/sys/domain.h +++ b/sys/sys/domain.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)domain.h 7.4 (Berkeley) 6/28/90 + * from: @(#)domain.h 7.4 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/sys/errno.h b/sys/sys/errno.h index 8869d596fa..f798752993 100644 --- a/sys/sys/errno.h +++ b/sys/sys/errno.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)errno.h 7.13 (Berkeley) 2/19/91 + * from: @(#)errno.h 7.13 (Berkeley) 2/19/91 + * $Id$ */ #ifndef KERNEL diff --git a/sys/sys/exec.h b/sys/sys/exec.h index ec539d93fc..8cc27644be 100644 --- a/sys/sys/exec.h +++ b/sys/sys/exec.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)exec.h 7.5 (Berkeley) 2/15/91 + * from: @(#)exec.h 7.5 (Berkeley) 2/15/91 + * $Id$ */ #ifndef _EXEC_H_ @@ -58,6 +59,7 @@ unsigned long a_drsize; /* data relocation size */ #define OMAGIC 0407 /* old impure format */ #define NMAGIC 0410 /* read-only text */ #define ZMAGIC 0413 /* demand load format */ +#define QMAGIC 0314 /* "compact" demand load format -- DEPRICATE */ /* a_mid */ #define MID_ZERO 0 /* unknown - implementation dependent */ diff --git a/sys/sys/fcntl.h b/sys/sys/fcntl.h index cc72bca9a1..787b0bee49 100644 --- a/sys/sys/fcntl.h +++ b/sys/sys/fcntl.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)fcntl.h 5.14 (Berkeley) 7/1/91 + * from: @(#)fcntl.h 5.14 (Berkeley) 7/1/91 + * $Id$ */ #ifndef _FCNTL_H_ diff --git a/sys/sys/fifo.h b/sys/sys/fifo.h index 4ddd510e11..d00b8021bf 100644 --- a/sys/sys/fifo.h +++ b/sys/sys/fifo.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)fifo.h 7.1 (Berkeley) 4/15/91 + * from: @(#)fifo.h 7.1 (Berkeley) 4/15/91 + * $Id$ */ #ifdef FIFO diff --git a/sys/sys/file.h b/sys/sys/file.h index 31f198937e..522ebda83d 100644 --- a/sys/sys/file.h +++ b/sys/sys/file.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)file.h 7.10 (Berkeley) 6/4/91 + * from: @(#)file.h 7.10 (Berkeley) 6/4/91 + * $Id$ */ #include diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h index b6243c28e0..f130198eae 100644 --- a/sys/sys/filedesc.h +++ b/sys/sys/filedesc.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)filedesc.h 7.4 (Berkeley) 5/4/91 + * from: @(#)filedesc.h 7.4 (Berkeley) 5/4/91 + * $Id$ */ /* diff --git a/sys/sys/filio.h b/sys/sys/filio.h index 5fdf9ed31d..16a95a3f46 100644 --- a/sys/sys/filio.h +++ b/sys/sys/filio.h @@ -1,3 +1,5 @@ /* * dummy file for SVR4 compatibility -wfj + * + * $Id$ */ diff --git a/sys/sys/gprof.h b/sys/sys/gprof.h index 03167191a4..8ebbad2335 100644 --- a/sys/sys/gprof.h +++ b/sys/sys/gprof.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)gprof.h 7.2 (Berkeley) 2/15/91 + * from: @(#)gprof.h 7.2 (Berkeley) 2/15/91 + * $Id$ */ struct phdr { diff --git a/sys/sys/ieeefp.h b/sys/sys/ieeefp.h new file mode 100644 index 0000000000..f0a8a92066 --- /dev/null +++ b/sys/sys/ieeefp.h @@ -0,0 +1,104 @@ +/*- + * Copyright (c) 1990 Andrew Moore, Talke Studio + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#) ieeefp.h 1.0 (Berkeley) 9/23/93 + * $Id$ + */ + +/* + * IEEE floating point type and constant definitions. + */ + +#ifndef _IEEEFP_H_ +#define _IEEEFP_H_ + +#ifdef __i386__ + +/* + * FP rounding modes + */ +typedef enum { + FP_RN=0, /* round to nearest */ + FP_RM, /* round down to minus infinity */ + FP_RP, /* round up to plus infinity */ + FP_RZ /* truncate */ +} fp_rnd_t; + +/* + * FP precison modes + */ +typedef enum { + FP_PS=0, /* 24 bit (single-precsion) */ + FP_PRS, /* reserved */ + FP_PD, /* 53 bit (double-precision) */ + FP_PE /* 64 bit (extended-precsion) */ +} fp_prec_t; + +#define fp_except_t int + +/* + * FP exception masks + */ +#define FP_X_INV 0x01 /* invalid operation */ +#define FP_X_DNML 0x02 /* denormal */ +#define FP_X_DZ 0x04 /* zero divide */ +#define FP_X_OFL 0x08 /* overflow */ +#define FP_X_UFL 0x10 /* underflow */ +#define FP_X_IMP 0x20 /* (im)precision */ + +/* + * FP registers + */ +#define FP_MSKS_REG 0 /* exception masks */ +#define FP_PRC_REG 0 /* precision */ +#define FP_RND_REG 0 /* direction */ +#define FP_STKY_REG 1 /* sticky flags */ + +/* + * FP register bit field masks + */ +#define FP_MSKS_FLD 0x3f /* exception masks field */ +#define FP_PRC_FLD 0x300 /* precision control field */ +#define FP_RND_FLD 0xc00 /* round control field */ +#define FP_STKY_FLD 0x3f /* sticky flags field */ + +/* + * FP register bit field offsets + */ +#define FP_MSKS_OFF 0 /* exception masks offset */ +#define FP_PRC_OFF 8 /* precision control offset */ +#define FP_RND_OFF 10 /* round control offset */ +#define FP_STKY_OFF 0 /* sticky flags offset */ + +#endif /* __i386__ */ + +#endif /* !_IEEEFP_H_ */ diff --git a/sys/sys/ioctl.h b/sys/sys/ioctl.h index 633a274b7f..0436e76409 100644 --- a/sys/sys/ioctl.h +++ b/sys/sys/ioctl.h @@ -30,15 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ioctl.h 7.19 (Berkeley) 6/26/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00163 - * -------------------- ----- ---------------------- - * - * 27 Nov 1992 Scott Reynolds Completed V7 ioctl compatablity - * 27 May 93 Guido van Rooij Comcontrol ioctls + * from: @(#)ioctl.h 7.19 (Berkeley) 6/26/91 + * $Id$ */ #ifndef _IOCTL_H_ @@ -84,7 +77,7 @@ struct ttysize { #define IOC_INOUT (IOC_IN|IOC_OUT) #define IOC_DIRMASK 0xe0000000 /* mask for IN/OUT/VOID */ -#define _IOC(inout,group,num,len) \ +#define _IOC(inout,group,num,len) \ (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num)) #define _IO(g,n) _IOC(IOC_VOID, (g), (n), 0) #define _IOR(g,n,t) _IOC(IOC_OUT, (g), (n), sizeof(t)) @@ -148,19 +141,24 @@ struct ttysize { #define TIOCSWINSZ _IOW('t', 103, struct winsize) /* set window size */ #define TIOCUCNTL _IOW('t', 102, int) /* pty: set/clr usr cntl mode */ #define UIOCCMD(n) _IO('u', n) /* usr cntl op "n" */ -#define TIOCSTAT _IOW('t', 101, int) /* generate status message */ +#define TIOCSTAT _IOW('t', 101, int) /* generate status message */ #define TIOCCONS _IOW('t', 98, int) /* become virtual console */ #define TIOCSCTTY _IO('t', 97) /* become controlling tty */ #define TIOCEXT _IOW('t', 96, int) /* pty: external processing */ #define TIOCSIG _IO('t', 95) /* pty: generate signal */ -#define TIOCDRAIN _IO('t', 94) /* wait till output drained */ -#define TIOCMSBIDIR _IOW('t', 93, int) /* modem: set bidir cap. */ -#define TIOCMGBIDIR _IOR('t', 92, int) /* modem: get bidir cap. */ - -#define TTYDISC 0 /* termios tty line discipline */ -#define TABLDISC 3 /* tablet discipline */ +#define TIOCDRAIN _IO('t', 94) /* wait till output drained */ +#define TIOCMSBIDIR _IOW('t', 93, int) /* modem: set bidir cap. */ +#define TIOCMGBIDIR _IOR('t', 92, int) /* modem: get bidir cap. */ + +#define TTYDISC 0 /* termios tty line discipline */ +#define NETLDISC 1 /* line discip for berk net */ +#define NTTYDISC 2 /* new tty discipline */ +#define TABLDISC 3 /* hitachi tablet discipline */ #define SLIPDISC 4 /* serial IP discipline */ - +#define PPPDISC 5 /* PPP discipline */ +#define MOUSELDISC 6 /* mouse discipline */ +#define KBDLDISC 7 /* up/down keyboard trans (console) */ +#define NTABLDISC 8 /* gtco tablet discipline */ #define FIOCLEX _IO('f', 1) /* set close on exec on fd */ #define FIONCLEX _IO('f', 2) /* remove close on exec */ @@ -208,6 +206,11 @@ struct ttysize { #define SIOCGARP _IOWR('i',38, struct arpreq) /* get arp entry */ #define SIOCDARP _IOW('i', 32, struct arpreq) /* delete arp entry */ +#define SIOCSIFMTU _IOW('i', 127, struct ifreq) /* set ifnet mtu */ +#define SIOCGIFMTU _IOWR('i',126, struct ifreq) /* get ifnet mtu */ +#define SIOCSIFASYNCMAP _IOW('i', 125, struct ifreq) /* set ppp asyncmap */ +#define SIOCGIFASYNCMAP _IOWR('i',124, struct ifreq) /* get ppp asyncmap */ + #ifndef KERNEL #include diff --git a/sys/sys/ioctl_compat.h b/sys/sys/ioctl_compat.h index f042246efd..4a006ca924 100644 --- a/sys/sys/ioctl_compat.h +++ b/sys/sys/ioctl_compat.h @@ -30,16 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ioctl_compat.h 7.4 (Berkeley) 2/5/91 - * - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00059 - * -------------------- ----- ---------------------- - * - * 27 Nov 1992 Scott Reynolds Completed V7 ioctl compatablity - * by adding gtty and stty macros + * from: @(#)ioctl_compat.h 7.4 (Berkeley) 2/5/91 + * $Id$ */ #ifndef _IOCTL_COMPAT_H_ diff --git a/sys/sys/ioctl_pc.h b/sys/sys/ioctl_pc.h deleted file mode 100644 index 176ef85fd5..0000000000 --- a/sys/sys/ioctl_pc.h +++ /dev/null @@ -1,793 +0,0 @@ -/* Copyright 1992,1993 by Holger Veit - * May be freely used with Bill Jolitz's port of - * 386bsd and may be included in a 386bsd collection - * as long as binary and source are available and reproduce the above - * copyright. - * - * You may freely modify this code and contribute improvements based - * on this code as long as you don't claim to be the original author. - * Commercial use of this source requires permittance of the copyright - * holder. A general license for 386bsd will override this restriction. - * - * Use at your own risk. The copyright holder or any person who makes - * this code available for the public (administrators of public archives - * for instance) are not responsible for any harm to hardware or software - * that might happen due to wrong application or program faults. - * - * Addendum: The XFree86 developers and maintainers are hereby granted the - * right to distribute this file together with their source distributions - * and patchkits of XFree86 without further explicit permission of the - * above copyright holder. - * This and another file is a necessary include file for the unified - * pccons/codrv implementation of XFree86. This file is needed if - * someone wants to compile an Xserver on a system which does not have, - * for some reasons, the codrv console driver which comes with this file. The - * availability of this file avoids a large number of #ifdef's and - * allows to make the xserver code easier runtime-configurable. - * To make use of this file, it must be installed in /usr/include/sys. - * This file is not the complete console device driver, so it is possible - * that properties described in this file do not work without having the - * complete driver distribution. This is not a fault of the Xserver that - * was built with this file. - * - * - * - * @(#)$RCSfile$ $Revision$ (Contributed to 386bsd) $Date$ - * - * Important notice: #defined values are subject to be changed!!! - * Don't use the constant, use the name instead! - * - * codrv1-style uses ioctls 'K': 1-33,255 - * 'V': 100-109 - * - * -hv- Holger Veit, Holger.Veit@gmd.de - * -hm Hellmuth Michaelis, hm@hcshh.hcs.de - * -vak- Sergey Vakulenko, vak@kiae.su - * - * 25-07-92 -hv- First version - * 16-08-92 -hm adding vga ioctl for cursor shape - * 25-10-92 -hv- X11 + video related ioctls - * 01/12/92 -vak- 8x16 font loading, beep ioctl, - * LED reassignment ioctl. - * 22-04-93 -hv- unified most CODRV1/CODRV2 codes - * 24-04-93 -hv- revised parts of keymap structures - */ - -#ifndef _IOCTL_PC_H_ -#define _IOCTL_PC_H_ - -#ifdef NOTDEF -#if __GNUC__ >= 2 -#pragma pack(1) -#endif -#endif - -#ifndef KERNEL -#include -#ifndef _TYPES_H_ -#include -#endif -#else -#include "ioctl.h" -#endif - - -/*************************************************************************** - * Basic definitions - ***************************************************************************/ - -/* Use this data type when manipulating characters, don't use 'char' or 'u_char' - * some day this will be changed to 'u_short' or 'u_long' size to allow - * characters > 255 - */ -typedef u_char XCHAR; - -/*************************************************************************** - * driver identification - ***************************************************************************/ - -/* - * This defines the CONSOLE INFORMATION data structure, used to - * describe console capabilities, to distinguish between different - * versions. If this ioctl fail, you probably have an old style "pccons" - * driver (or an "improved" console driver, whose writer is not interested - * in providing compatibility for anything). - * In this case, a considerable number of features may not work as expected, - * or do not work at all. - */ - -#define MAXINFOSIZE 16 -struct consinfo { - u_long info1; - u_long __reserved1__; - u_long __reserved2__; - u_long __reserved3__; - XCHAR drv_name[MAXINFOSIZE+1]; - XCHAR emul_name[MAXINFOSIZE+1]; - XCHAR __reserved1_name__[MAXINFOSIZE+1]; - XCHAR __reserved2_name__[MAXINFOSIZE+1]; -}; - -struct oldconsinfo { - u_long info1; - u_long __reserved__; -}; - -#define CONSGINFO _IOR('K',255,struct consinfo) /* Get console capabilities */ -#define OLDCONSGINFO _IOR('K',255,struct oldconsinfo) /* compatibility */ -#define CONS_ISPC 0x00000001 /* is derived from old PCCONS */ -#define CONS_ISCO 0x00000002 /* is derived from CO driver */ -#define CONS_reserved1 0x00000004 /* reserved for other console drivers */ -#define CONS_reserved2 0x00000008 /* reserved for other console drivers */ -#define CONS_HASKBD 0x00000010 /* has /dev/kbd */ -#define CONS_HASSCAN 0x00000020 /* uses Scan codes */ -#define CONS_HASKEYNUM 0x00000040 /* uses KEYNUMS */ -#define CONS_HASVTY 0x00000080 /* has /dev/vty* */ -#define CONS_HASPC3 0x00000100 /* unused, historical */ -#define CONS_HASVTHP 0x00000200 /* unused, historical */ -#define CONS_reserved3 0x00000400 /* reserved */ -#define CONS_reserved4 0x00000800 /* reserved */ -#define CONS_HASPX386 0x00001000 /* has X386 probing support +new CONSOLE_X_MODE */ -#define CONS_HASOX386 0x00002000 /* has old X386 support CONSOLE_X_MODE_ON/OFF */ -#define CONS_reserved5 0x00004000 /* reserved */ -#define CONS_reserved6 0x00008000 /* reserved */ -#define CONS_HASKCAP 0x00010000 /* has ioctl keycap support */ -#define CONS_HASFNT 0x00020000 /* has ioctl font support */ -#define CONS_reserved7 0x00040000 /* reserved */ -#define CONS_reserved8 0x00080000 /* reserved */ -#define CONS_USE7BIT 0x00100000 /* does not support 8bit characters */ -#define CONS_USEPC8 0x00200000 /* uses PC8 8-bit mapping */ -#define CONS_USELATIN1 0x00400000 /* uses ISO LATIN1 mapping */ -#define CONS_HAS10646 0x00800000 /* has /dev/unicode */ -#define CONS_PCCONS2 0x01000000 /* modified pccons */ -#define CONS_CODRV1 0x02000000 /* old codrv ioctls */ -#define CONS_CODRV2 0x04000000 /* codrv ioctls 0.1.2 */ -#define CONS_reserved9 0x08000000 /* reserved */ -#define CONS_reserved10 0x10000000 /* reserved */ -#define CONS_reserved11 0x20000000 /* reserved */ -#define CONS_reserved12 0x40000000 /* reserved */ -#define CONS_reserved13 0x80000000 /* reserved */ - - -/*************************************************************************** - * IOCTLs for AT Keyboard - ***************************************************************************/ - -/**** initializing the keyboard ****/ - -/* reset keyboard, run selftests and set default values: - * default keymap, no overloaded keys, default typematic rate - * KBD_TPD500|KBD_TPM100, repetition on - */ -#define KBDCOLDRESET _IO('K', 1) /* reset keyboard and set default - * values: - * default keymap, no overloaded - * keys, default typematic rate - * KBD_TPD500|KBD_TPM100 - */ -/* resets the mode in keyboard controller only */ -#define KBDWARMRESET _IO('K', 23) - - - -/**** key repetition (typematic) feature ****/ - -/* get (G) / set (S) key repetition rate and delay - * see below for a definition of rate and delay and the necessary - * argument - */ -#define KBDGTPMAT _IOR('K', 2, int) -#define KBDSTPMAT _IOW('K', 3, int) - -/* Typematic rates: - * Rate = 1 / Period, with - * Period = (8+ (Val&7)) * 2^((Val>>3)&3) * 0.00417 seconds, - * and Val the typematic value below - * - * The typematic delay is determined by - * Delay = (1+((Val>>5)&3)) * 250 msec +/- 20 % - * - * Source IBM/AT reference manual, 1987 - * - * Note that you have to pass one TPD* and one TPM* value to the KBDSTPMAT - * ioctl: they are different flags of the same data word. Also note that - * 0x00 is a valid value: KBD_TPD250|KBD_TPM300 which is really fast, instead - * of turning off key repetition entirely. You can turn off key repetition - * with the ioctls KBDGREPSW/KBDSREPSW. -*/ - -#define KBD_TPD250 0x0000 /* 250 ms */ -#define KBD_TPD500 0x0020 /* 500 ms */ -#define KBD_TPD750 0x0040 /* 750 ms */ -#define KBD_TPD1000 0x0060 /* 1000 ms */ - -#define KBD_TPM300 0x0000 /* 30.0 rate */ -#define KBD_TPM267 0x0001 /* 26.7 rate */ -#define KBD_TPM240 0x0002 /* 24.0 rate */ -#define KBD_TPM218 0x0003 /* 21.8 rate */ -#define KBD_TPM200 0x0004 /* 20.0 rate */ -#define KBD_TPM185 0x0005 /* 18.5 rate */ -#define KBD_TPM171 0x0006 /* 17.1 rate */ -#define KBD_TPM160 0x0007 /* 16.0 rate */ -#define KBD_TPM150 0x0008 /* 15.0 rate */ -#define KBD_TPM133 0x0009 /* 13.3 rate */ -#define KBD_TPM120 0x000a /* 12.0 rate */ -#define KBD_TPM109 0x000b /* 10.9 rate */ -#define KBD_TPM100 0x000c /* 10.0 rate */ -#define KBD_TPM92 0x000d /* 9.2 rate */ -#define KBD_TPM86 0x000e /* 8.6 rate */ -#define KBD_TPM80 0x000f /* 8.0 rate */ -#define KBD_TPM75 0x0010 /* 7.5 rate */ -#define KBD_TPM67 0x0011 /* 6.7 rate */ -#define KBD_TPM60 0x0012 /* 6.0 rate */ -#define KBD_TPM55 0x0013 /* 5.5 rate */ -#define KBD_TPM50 0x0014 /* 5.0 rate */ -#define KBD_TPM46 0x0015 /* 4.6 rate */ -#define KBD_TPM43 0x0016 /* 4.3 rate */ -#define KBD_TPM40 0x0017 /* 4.0 rate */ -#define KBD_TPM37 0x0018 /* 3.7 rate */ -#define KBD_TPM33 0x0019 /* 3.3 rate */ -#define KBD_TPM30 0x001a /* 3.0 rate */ -#define KBD_TPM27 0x001b /* 2.7 rate */ -#define KBD_TPM25 0x001c /* 2.5 rate */ -#define KBD_TPM23 0x001d /* 2.3 rate */ -#define KBD_TPM21 0x001e /* 2.1 rate */ -#define KBD_TPM20 0x001f /* 2.0 rate */ - - -/* get (G) / set (S) the key repetition switch */ -#define KBD_REPEATOFF 0 -#define KBD_REPEATON 1 -#define KBDGREPSW _IOR('K', 4, int) -#define KBDSREPSW _IOW('K', 5, int) - - - -/**** handling keyboard LEDS and Lock keys ****/ - -/* get (G) / set (S) the keyboard LEDs, - * does not influence the state of the lock keys. - * Note: if keyboard serves tty console mode (VTYs have keyboard focus), - * the lock keys will still modify the state when used - */ -#define KBDGLEDS _IOR('K', 6, int) -#define KBDSLEDS _IOW('K', 7, int) - -/* get (G) / set (S) the SCROLL, NUM, CAPS ALTGRLOCK keys - * (note: ALTGRLOCK or SHIFTLOCK are not necessarily accessible - * on your keyboard) - */ -#define KBD_LOCKSCROLL 0x0001 -#define KBD_LOCKNUM 0x0002 -#define KBD_LOCKCAPS 0x0004 -#define KBD_LOCKALTGR 0x0008 -#define KBD_LOCKSHIFT 0x0010 -#define KBDGLOCK _IOR('K', 8, int) -#define KBDSLOCK _IOW('K', 9, int) - - - -/**** making noise ****/ - -/* get (G) / set (S) the beeper frequency and tone duration - * the nr param determines the VTY which parameters are changed - * VTY# = 0...n, n < max_vtys - * nr = -1: actual vty - * nr = -2: Set the system default beep frequency - * - * in some emulations, you can also set pitch and duration by an ESC code - */ -#define KBD_ACTVTY -1 -#define KBD_DEFLT -2 -struct kbd_bell { - int pitch; - int duration; - int nr; -}; - -#define KBDGETBEEP _IOWR('K',28, struct kbd_bell) -#define KBDSETBEEP _IOW('K',29, struct kbd_bell) - -/* do a beep of specified frequency and duration - * the argument nr is unused - * a NULL arg performs a default system beep - */ -#define KBDBELL _IOW('K',30, struct kbd_bell) - - - -/**** I/O access ****/ - -/* This call allows programs to access I/O ports. - * The ioctl is intended to perform several tasks for the XFree86 Xserver, - * but currently has other interesting applications. This is why it is - * priviledged and can only be executed by root (or with setuid-root). - * In future the ioctl might be restricted to allow access to video ports - * only. - */ -#define X_MODE_ON 1 -#define X_MODE_OFF 0 -#define CONSOLE_X_MODE _IOW('K',22,int) - - -/**** keyboard overloading ****/ - -/* Codrv allows loading of strings to keys in six layers. - * Any string may have a length of up to KBDMAXOVLKEYSIZE XCHARS. - * !!! Warning: This ioctl uses the type XCHAR. In future, this may - * !!! no longer be a char type, so str*** functions might not work any more - * !!! some day. - * The available layers are: - * - * - unshifted - * - with shift key - * - with ctrl key - * - with meta key (usually ALT-left) - * - with altgr key (usually ALT-right) - * - with shift+altgr key - * - * There are no combinations: shift-ctrl, ctrl-alt, shift-meta. - * The combination ctrl-altleft-somekey is reserved for system purposes. - * These keys are usually processed before the above keys. To gain control - * over these keys, you must run the keyboard in raw mode (/dev/kbd) and - * do ALL the processing yourself. The Xserver for instance does it this way. - * The following special keys are currently defined: - * - * CTRL-ALTLEFT-DELETE: Reboot - * CTRL-ALTLEFT-ESCAPE: Call the debugger (if compiled into the kernel) - * CTRL-ALTLEFT-KP+: Switch to next resolution (Xserver only) - * CTRL-ALTLEFT-KP-: Switch to previous resolution (Xserver only) - */ - -/* values for type field of various kbd_overload ioctls */ -#define KBD_NONE 0 /* no function, key is disabled */ -#define KBD_SHIFT 1 /* keyboard shift */ -#define KBD_META 2 /* (ALT) alternate shift, sets bit8 to ASCII code */ -#define KBD_NUM 3 /* numeric shift cursors vs. numeric */ -#define KBD_CTL 4 /* control shift -- allows ctl function */ -#define KBD_CAPS 5 /* caps shift -- swaps case of letter */ -#define KBD_ASCII 6 /* ascii code for this key */ -#define KBD_SCROLL 7 /* stop output */ -#define KBD_FUNC 8 /* function key */ -#define KBD_KP 9 /* Keypad keys */ -#define KBD_BREAK 10 /* The damned BREAK key, ignored in ioctl */ -#define KBD_ALTGR 11 /* AltGr Translation feature */ -#define KBD_SHFTLOCK 12 /* some people are accustomed to this nonsense */ -#define KBD_ALTGRLOCK 13 /* Useful for 8-bit national kbds (cyrillic) */ -#define KBD_DOALTCAPS 0x0400 /* change by altgr + caps shift */ -#define KBD_DOCAPS 0x0800 /* change by caps shift */ -#define KBD_DIACPFX 0x4000 /* Key carries a diacritical prefix */ -#define KBD_OVERLOAD 0x8000 /* Key is overloaded, ignored in ioctl */ -#define KBD_MASK 0x001f /* mask for type */ - -#define KBDMAXOVLKEYSIZE 15 /* excl. zero byte */ -struct kbd_ovlkey { - u_short keynum; - u_short type; - XCHAR unshift[KBDMAXOVLKEYSIZE+1]; - XCHAR shift[KBDMAXOVLKEYSIZE+1]; - XCHAR ctrl[KBDMAXOVLKEYSIZE+1]; - XCHAR meta[KBDMAXOVLKEYSIZE+1]; - XCHAR altgr[KBDMAXOVLKEYSIZE+1]; - XCHAR shiftaltgr[KBDMAXOVLKEYSIZE+1]; -}; - - -/* Get (G) / Set (S) a key assignment. This will influence the current - * key value only - */ -#define KBDGCKEY _IOWR('K',16, struct kbd_ovlkey) -#define KBDSCKEY _IOW('K',17, struct kbd_ovlkey) - -/* Get (G) the default (old) key assignment. You cannot overwrite the - * default setting, so this ioctl is unpaired - */ -#define KBDGOKEY _IOWR('K',18, struct kbd_ovlkey) - - - -/* Remove a key assignment for a key, i.e. restore default setting for key - * arg = keynum - */ -#define KBDRMKEY _IOW('K', 19, int) - -/* Restore the default key setting */ -#define KBDDEFAULT _IO('K',20) - - - -/* Set behavior of unassigned key layers - * Note that there is a hack from further versions which uses - * the flags KBD_C0 and KBD_A0 for this. This is still supported, but - * is not recommended way to do. It may disappear in future - * (what means that it won't :-)) - */ -#define KBD_CLEARCTRL 2 -#define KBD_CLEARMETA 4 -#define KBD_CLEARALT 1 -#ifdef notyet - #define KBD_CLEARNORM 8 - #define KBD_CLEARSHIFT 16 - #define KBD_CLEARSHALT 32 -#endif -#define KBDSCLRLYR _IOW('K',31,int) - -/* get (G) / set (S) CAPSLOCK LED behaviour. - * Not all of this keys may be accessible at your keyboard - * Note: For compatibility, the S ioctl returns the old state in arg - */ -#define KBD_CAPSCAPS 0 /* LED follows CAPSLOCK state */ -#define KBD_CAPSSHIFT 1 /* LED follows SHIFTLOCK state */ -#define KBD_CAPSALTGR 2 /* LED follows ALTGRLOCK state */ -#define KBD_CAPSINIT 0x04 /* bit to set to set a default for all VTYs */ -#define KBDGCAPSLED _IOR('K',27,int) -#define KBDSCAPSLED _IOWR('K',25,int) - -/* extended functions: functions that are triggered by a keypress - * before key is converted to ASCII - * - * use function KBD_HOTKEYDELETE to remove a hotkey from a key - */ -struct kbd_hotkey { - u_short key; - u_short modifier; - u_short function; -}; -#define KBDGSPECF _IOWR('K',32,struct kbd_hotkey) -#define KBDSSPECF _IOW('K',33,struct kbd_hotkey) - -/* extended function prefixes (in modifier field) - * bit set triggers a special function on the key layer - */ -#define KBD_NOEXT 0x00 /* trigger never */ -#define KBD_EXT_N 0x01 /* on normal key (normal layer) */ -#define KBD_EXT_S 0x02 /* on shift key (shift layer) */ -#define KBD_EXT_C 0x04 /* on ctrl key (ctrl layer) */ -#define KBD_EXT_A 0x08 /* on alt key (alt layer) */ -#define KBD_EXT_SK 0x10 /* on syskey (PRINTSCREEN) (Meta Layer) */ -#define KBD_EXT_CA 0x20 /* on ctrl-alt (shift alt layer) */ - -/* extended functions (in function field) */ -#define KBD_VTY0 0 /* select vty 0 */ -#define KBD_VTY1 1 /* select vty 1 */ -#define KBD_VTY2 2 /* select vty 2 */ -#define KBD_VTY3 3 /* select vty 3 */ -#define KBD_VTY4 4 /* select vty 4 */ -#define KBD_VTY5 5 /* select vty 5 */ -#define KBD_VTY6 6 /* select vty 6 */ -#define KBD_VTY7 7 /* select vty 7 */ -#define KBD_VTY8 8 /* select vty 8 */ -#define KBD_VTY9 9 /* select vty 9 */ -#define KBD_VTY10 10 /* select vty 10 */ -#define KBD_VTY11 11 /* select vty 11 */ -#define KBD_VTYUP 0x80 /* select next vty */ -#define KBD_VTYDOWN 0x81 /* select previous vty */ -#define KBD_RESETKEY 0x82 /* the CTRL-ALT-DEL key (movable) */ -#define KBD_DEBUGKEY 0x83 /* the CTRL-ALT-ESC key (debugger) */ - -#define KBD_HOTKEYDELETE 0xff /* use to delete a hotkey KBDSSPECF */ - - - -/* These are names used in older versions of keycap/codrv */ -/* do not use the following functions any longer in future */ -#ifdef COMPAT_CO011 -#define KBDRESET KBDCOLDRESET -#define KBDRESET8042 KBDWARMRESET -#define KBDFORCEASCII _IOW('K', 24, int) /* no op in codrv-0.1.2 */ -#define KBD_SCROLLLOCK KBD_LOCKSCROLL -#define KBD_NUMLOCK KBD_LOCKNUM -#define KBD_CAPSLOCK KBD_LOCKCAPS -#define KBDASGNLEDS KBDSCAPSLED -#ifndef KERNEL -struct kbd_sound { - int pitch; /* Frequency in Hz */ - int duration; /* Time in msec */ -}; -#endif -#define KBDSETBELL _IOW('K',21, struct kbd_sound) /* do some music */ -#define OLDKBDSETBEEP _IOW('K',26, struct kbd_sound) /* change beep settings */ - -struct oldkbd_ovlkey { - u_short keynum; - u_short type; - char unshift[KBDMAXOVLKEYSIZE+1]; - char shift[KBDMAXOVLKEYSIZE+1]; - char ctrl[KBDMAXOVLKEYSIZE+1]; - char altgr[KBDMAXOVLKEYSIZE+1]; -}; -#define OLDKBDGCKEY _IOWR('K',16, struct oldkbd_ovlkey) /* get current key values */ - - - -#endif /*COMPAT_CO011*/ - -/*************************************************************************** - * IOCTLs for Video Adapter - ***************************************************************************/ - -/* to define the cursor shape for ioctl */ -struct cursorshape { - int start; /* topmost scanline, range 0...31 */ - int end; /* bottom scanline, range 0...31 */ -}; - -#define VGAGCURSOR _IOR('V',100, struct cursorshape) /* get cursor shape */ -#define VGASCURSOR _IOW('V',101, struct cursorshape) /* set cursor shape */ - - - -/**** information ****/ - -/* the video information structure for ioctl */ -struct videoinfo { - char name[20]; /* ASCIZ name of detected card */ - short type; /* Adapter type, see below */ - short subtype; /* Adapter specific subtype */ - short ram; /* in KBytes */ - short iobase; /* Address of 6845: 0x3b0 / 0x3d0 */ -}; - -/* Get information about the videoboard */ -#define VGAGINFO _IOR('V',102, struct videoinfo) - -/* recognized Adapter types */ -#define VG_UNKNOWN 0 -#define VG_MONO 1 -#define VG_CGA 2 -#define VG_EGA 3 -#define VG_VGA 4 -#define VG_CHIPS 5 -/* CHIPS & TECHNOLOGIES has subtypes: - * 0x10 82c451 - * 0x11 82c452 - * 0x20 82c455 - * 0x30 82c453 - * 0x50 82c455 - */ -#define VG_GENOA 6 -/* GENOA has subtypes: - * 0x33/0x55 5100-5400, ET3000 based - * 0x22 6100 - * 0x00 6200,6300 - * 0x11 6400,6600 - */ -#define VG_PARADISE 7 -/* PARADISE has subtypes: - * 01 PVGA1A,WD90C90 - * 02 WD90C00 - * 03 WD90C10 - * 04 WD90C11 - */ -#define VG_TVGA 8 -/* TVGA has subtypes: - * 00-02 8800 - * 03 8900B - * 04 8900C - * 13 8900C - * 23 9000 - */ -#define VG_ET3000 9 -#define VG_ET4000 10 -#define VG_VIDEO7 11 -/* VIDEO7 has subtypes: - * 0x80-0xfe VEGA VGA - * 0x70-0x7e V7VGA FASTWRITE/VRAM - * 0x50-0x59 V7VGA version 5 - * 0x41-0x49 1024i - */ -#define VG_ATI 12 -/* ATI has subtypes: - * 0x01nn 18800 - * 0x02nn 18800-1 - * 0x03nn 28800-2 - * 0x04nn-05nn - * with nn: - * 0x01 VGA WONDER - * 0x02 EGA WONDER800+ - * 0x03 VGA BASIC 16+ - */ - - - -/**** Screen blanking ****/ - -/* Get (G) / Set (S) screen blanker timeout (seconds), - * time=0 disables blanking - * - * The blanking state is coded in bits 31 and 30 of word returned by get - */ -#define VGA_BLANKOFF 0x00000000 /* display is on, no blanking */ -#define VGA_BLANKON 0x40000000 /* display is on, wait for blank */ -#define VGA_BLANKED 0x80000000 /* display is dark */ -#define VGAGBLANK _IOR('V',2,int) -#define VGASBLANK _IOW('V',3,int) - - - -/**** Text/Attribute direct access, block move ****/ - -struct vga_block { - short mode; - short pagenum; - short x0,y0; /* upper left coordinates 0..x-1, 0..y-1 */ - short x1,y1; /* lower right coordinates >= x0,y0 */ - u_char *map; /* must be allocated by user process ! */ -}; - -/* mode word */ -#define VGA_SCREEN 0x01 /* entire screen, ignore x,y */ -#define VGA_WINDOW 0x02 /* use x,y for a rectangular window */ -#define VGA_TEXT 0x10 /* copy text information only */ -#define VGA_ATTR 0x20 /* copy attribute information only */ -#define VGA_BOTH 0x30 /* copy text and attribute */ -#define VGA_ALL 0x31 /* copy complete screen */ - -/* Get (G) / Set (S) a rectangular block of screen - * The virtual screen need not be visible. - * The buffer must be provided by the user process and must be large enough - * use VGAGVRES to find out how many bytes - * pagenum: 0..n, n < max_vty, VTY number - * -1, actual VTY - */ -#define VGAGBLOCK _IOWR('V',4,struct vga_block) -#define VGASBLOCK _IOW('V',5,struct vga_block) - - - -#define VGA_TXTPAGE0 0 -#define VGA_TXTPAGE1 1 -#ifdef notyet -#define VGA_GFXPAGE 2 -#endif -#define VGA_PC8CODING 0x80 /* obsolete ! */ - -/* maximum dimension of pixels - * Note: this is the space reserved in the fontchar map, but - * does not mean, that this resolution is accepted in the current release - * codrv-0.1.2 accepts 8x16 / "9x16" fonts only - */ -#define VGA_MAXX 16 -#define VGA_MAXY 16 - -struct fchar { - XCHAR encoding; /* encoding of character */ - char _f1_,_f2_,_f3_; /* filler */ - u_char map[VGA_MAXX/8*VGA_MAXY]; -}; - -struct fmap { - short page; /* page to load */ - short nr; /* nr of characters to load */ - char x,y; /* x,y pixel width */ - XCHAR start; /* first character in sequence (get only) */ - struct fchar *fntmap; /* allocated by user process */ -}; - -/* get (G) / set (S) font map. Must provide page,nr,start for get */ -#define VGAGFONTMAP _IOWR('V',6,struct fmap) -#define VGASFONTMAP _IOW('V',7,struct fmap) - - - -/* do not use the following functions any longer in future */ -#ifdef COMPAT_CO011 -/* miscellaneous functions: */ -#define VGA_DIS1 1 /* disable font 1 */ -#define VGA_GTENC 2 /* get current encoding */ -#define VGA_SBLANK 3 /* set screen blanking timeout (use VGASBLANK!) */ -#define VGA_GBLANK 4 /* get screen blanking timeout (use VGAGBLANK!) */ - -struct miscfcns { - u_char cmd; - union { - short enc[2]; - int timeout; - } u; -}; -#define VGAMISCFCNS _IOWR('V',107,struct miscfcns) /* misc functions */ - - -/* Font mapping this needs at least an EGA card (else EINVAL) */ -#define VGAFNTLATIN1 0x00 -#define VGAFNTEXTEND1 0x01 -#define VGAFNTEXTEND2 0x02 -#define VGAFNTGREEK 0x03 -#define VGAFNTCYRILLIC 0x04 -#define VGAFNTHEBREW 0x05 -#define VGAFNTARABIAN 0x06 - -#define VGA_FNTNCHARS 256 -#define VGA_FNTCSIZE 15 - -struct fontchar { - u_char page; /* which font page */ - u_char idx; /* which char in font page */ - u_char cmap[VGA_FNTCSIZE]; /* character bitmap */ -}; - -#define OLDVGAGCHAR _IOWR('V',105,struct fontchar) /* get character of font */ -#define OLDVGASCHAR _IOW('V',106,struct fontchar) /* set character in font */ - -struct fontmap { - u_char page; /* page to load */ - u_short encoding; /* font encoding */ - u_char map[VGA_FNTNCHARS*VGA_FNTCSIZE]; -}; - -#define OLDVGAGFNTMAP _IOWR('V',103,struct fontmap) /* get font */ -#define VGAGFNTMAP OLDVGAGFNTMAP -#define OLDVGASFNTMAP _IOW('V',104,struct fontmap) /* set font */ -#define VGASFNTMAP OLDVGASFNTMAP - -#endif - - - - -struct textpage { - u_char pagenum; /* note: only page 0 used by vtys */ -#define VGA_TEXTATTR 0 -#define VGA_TEXTDATA 1 - u_char ad; -#define VGA_LINES 50 /* only 25 used for now */ -#define VGA_COLUMNS 80 - u_char map[VGA_LINES*VGA_COLUMNS]; -}; - -#define VGAGPAGE _IOWR('V',108,struct textpage) /* get a data page */ -#define VGASPAGE _IOW('V',109,struct textpage) /* set a data page */ - -/**** Signalling access ****/ - -/* Use "take control" in an application program to signal the kernel - * that the program wants to use video memory (such as Xserver) - * before the program switches modes - * - * Use "give control" to return the control to the kernel. The application - * should have restored the original state before giving back control. - * Close /dev/vga also returns control. - * - * However, the kernel remains the master in the house, and reserves the right - * to grab control back at any time. (It usually doesn't). - * - */ -#define VGATAKECTRL _IO('V',8) -#define VGAGIVECTRL _IO('V',9) - -/*************************************************************************** - * Pandora's box, don't even think of using the following ioctl's - * (if you happen to find some; codrv_experimental might not be - * available at your system) - ***************************************************************************/ - -#ifdef PANDORA -#include "codrv_experimental.h" -#endif - - - -/*************************************************************************** - * XFree86 pccons support - ***************************************************************************/ - -#ifdef COMPAT_PCCONS -/* The following calls are special to the old pccons driver and are - * not understood or supported by codrv. - * This file serves as a central definition base for these calls - * in order to avoid defining them in applications that want to - * use them. - * - * One word of warning: There are different purpose tty ioctls - * with the same encoding, see - * TIOCSDTR = _IO('t', 121) - * TIOCCBRK = _IO('t', 122) - * - */ -#define CONSOLE_X_MODE_ON _IO('t',121) -#define CONSOLE_X_MODE_OFF _IO('t',122) -#define CONSOLE_X_BELL _IOW('t',123,int[2]) -#endif /* COMPAT_PCCONS */ - -#endif /* _IOCTL_PC_H_ */ - diff --git a/sys/sys/ipc.h b/sys/sys/ipc.h index d72a765e69..f715e0c26d 100644 --- a/sys/sys/ipc.h +++ b/sys/sys/ipc.h @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ipc.h 7.2 (Berkeley) 2/5/91 + * from: @(#)ipc.h 7.2 (Berkeley) 2/5/91 + * $Id$ */ /* diff --git a/sys/sys/kernel.h b/sys/sys/kernel.h index fe778edc04..88eb604e7c 100644 --- a/sys/sys/kernel.h +++ b/sys/sys/kernel.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kernel.h 7.4 (Berkeley) 2/15/91 + * from: @(#)kernel.h 7.4 (Berkeley) 2/15/91 + * $Id: kernel.h,v 1.2 1993/10/16 17:16:56 rgrimes Exp $ */ /* Global variables for the kernel. */ @@ -40,6 +41,8 @@ long rmalloc(); long hostid; char hostname[MAXHOSTNAMELEN]; int hostnamelen; +char domainname[MAXHOSTNAMELEN]; +int domainnamelen; /* 1.2 */ struct timeval boottime; diff --git a/sys/sys/kinfo.h b/sys/sys/kinfo.h index c4a155c3dd..e946934ec8 100644 --- a/sys/sys/kinfo.h +++ b/sys/sys/kinfo.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kinfo.h 7.9 (Berkeley) 6/26/91 + * from: @(#)kinfo.h 7.9 (Berkeley) 6/26/91 + * $Id$ */ /* diff --git a/sys/sys/kinfo_proc.h b/sys/sys/kinfo_proc.h index 28b2c177ce..ffa0bbdd6a 100644 --- a/sys/sys/kinfo_proc.h +++ b/sys/sys/kinfo_proc.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kinfo_proc.h 7.1 (Berkeley) 5/9/91 + * from: @(#)kinfo_proc.h 7.1 (Berkeley) 5/9/91 + * $Id$ */ #ifndef _KINFO_PROC_H_ diff --git a/sys/sys/ktrace.h b/sys/sys/ktrace.h index 9f63359e2d..a415db6449 100644 --- a/sys/sys/ktrace.h +++ b/sys/sys/ktrace.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ktrace.h 7.4 (Berkeley) 5/7/91 + * from: @(#)ktrace.h 7.4 (Berkeley) 5/7/91 + * $Id$ */ /* diff --git a/sys/sys/malloc.h b/sys/sys/malloc.h index 77d7bd8de8..119f76e2e4 100644 --- a/sys/sys/malloc.h +++ b/sys/sys/malloc.h @@ -30,15 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)malloc.h 7.25 (Berkeley) 5/15/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00129 - * -------------------- ----- ---------------------- - * - * 02 Apr 93 Paul Popelka Added support for PCFS - * + * from: @(#)malloc.h 7.25 (Berkeley) 5/15/91 + * $Id$ */ #ifndef _MALLOC_H_ @@ -98,6 +91,7 @@ #define M_LOCKF 40 /* Byte-range locking structures */ #define M_PROC 41 /* Proc structures */ #define M_SUBPROC 42 /* Proc sub-structures */ +#define M_ISOFSMNT 48 /* isofs mount structures */ #define M_TEMP 49 /* misc temporary data buffers */ #define M_PCFSMNT 50 /* PCFS mount structure */ #define M_PCFSFAT 51 /* PCFS fat table */ @@ -147,7 +141,8 @@ "lockf", /* 40 M_LOCKF */ \ "proc", /* 41 M_PROC */ \ "subproc", /* 42 M_PROC */ \ - 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, \ + "isofs mount", /* 48 M_ISOFSMNT */ \ "temp", /* 49 M_TEMP */ \ "PCFS mount", /* 50 M_PCFSMNT */ \ "PCFS fat", /* 51 M_PCFSFAT */ \ diff --git a/sys/sys/map.h b/sys/sys/map.h new file mode 100644 index 0000000000..eb2b88b33c --- /dev/null +++ b/sys/sys/map.h @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 1982, 1986, The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)map.h 7.5 (Berkeley) 5/7/91 + * $Id$ + */ + +/* + * Resource allocation maps. + * + * Associated routines manage sub-allocation of an address space using + * an array of segment descriptors. The first element of this array + * is a map structure, describing the arrays extent and the name + * of the controlled object. Each additional structure represents + * a free segment of the address space. + * + * A call to rminit initializes a resource map and may also be used + * to free some address space for the map. Subsequent calls to rmalloc + * and rmfree allocate and free space in the resource map. If the resource + * map becomes too fragmented to be described in the available space, + * then some of the resource is discarded. This may lead to critical + * shortages, but is better than not checking (as the previous versions + * of these routines did) or giving up and calling panic(). The routines + * could use linked lists and call a memory allocator when they run + * out of space, but that would not solve the out of space problem when + * called at interrupt time. + * + * N.B.: The address 0 in the resource address space is not available + * as it is used internally by the resource map routines. + */ +struct map { + struct mapent *m_limit; /* address of last slot in map */ + char *m_name; /* name of resource, for messages */ +}; + +struct mapent { + int m_size; /* size of this segment of the map */ + int m_addr; /* start of segment */ +}; + +#ifdef KERNEL +#define ARGMAPSIZE 16 +struct map *kmemmap, *mbmap, *swapmap; +int nswapmap; +#endif diff --git a/sys/sys/mapmem.h b/sys/sys/mapmem.h index 654eb32ed2..afdecac004 100644 --- a/sys/sys/mapmem.h +++ b/sys/sys/mapmem.h @@ -35,9 +35,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * from: Utah $Hdr: mmap.h 1.4 89/08/14$ - * - * @(#)mapmem.h 7.2 (Berkeley) 6/6/90 + * from: Utah $Hdr: mmap.h 1.4 89/08/14$ + * from: @(#)mapmem.h 7.2 (Berkeley) 6/6/90 + * $Id$ */ /* diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h index 290b917a10..817225abbe 100644 --- a/sys/sys/mbuf.h +++ b/sys/sys/mbuf.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)mbuf.h 7.14 (Berkeley) 12/5/90 + * from: @(#)mbuf.h 7.14 (Berkeley) 12/5/90 + * $Id$ */ #ifndef M_WAITOK diff --git a/sys/sys/mman.h b/sys/sys/mman.h index b3e19d7a22..6f4a4add02 100644 --- a/sys/sys/mman.h +++ b/sys/sys/mman.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)mman.h 7.5 (Berkeley) 6/27/91 + * from: @(#)mman.h 7.5 (Berkeley) 6/27/91 + * $Id$ */ /* diff --git a/sys/sys/mount.h b/sys/sys/mount.h index 097f4091a6..090a313eac 100644 --- a/sys/sys/mount.h +++ b/sys/sys/mount.h @@ -30,15 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)mount.h 7.22 (Berkeley) 6/3/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00129 - * -------------------- ----- ---------------------- - * - * 02 Apr 93 Paul Popelka Added support for PCFS - * + * from: @(#)mount.h 7.22 (Berkeley) 6/3/91 + * $Id: mount.h,v 1.4 1993/10/16 17:17:14 rgrimes Exp $ */ typedef quad fsid_t; /* file system id type */ @@ -114,6 +107,8 @@ struct mount { #define MNT_NOEXEC 0x00000004 /* can't exec from filesystem */ #define MNT_NOSUID 0x00000008 /* don't honor setuid bits on fs */ #define MNT_NODEV 0x00000010 /* don't interpret special files */ +#define MNT_NOCORE 0x00000020 /* don't create core files */ +#define MNT_NORRIP 0x00000040 /* disable Rock Ridge Ext.*/ /* * exported mount flags. diff --git a/sys/sys/msgbuf.h b/sys/sys/msgbuf.h index 821b124938..9c395b6f91 100644 --- a/sys/sys/msgbuf.h +++ b/sys/sys/msgbuf.h @@ -30,9 +30,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)msgbuf.h 7.5 (Berkeley) 5/2/91 + * from: @(#)msgbuf.h 7.5 (Berkeley) 5/2/91 + * $Id$ */ +#ifndef _SYS_MSGBUF_H_ +#define _SYS_MSGBUF_H_ + #define MSG_BSIZE (4096 - 3 * sizeof(long)) struct msgbuf { #define MSG_MAGIC 0x063061 @@ -42,5 +46,8 @@ struct msgbuf { char msg_bufc[MSG_BSIZE]; /* buffer */ }; #ifdef KERNEL -struct msgbuf *msgbufp; +extern struct msgbuf *msgbufp; +extern int msgbufmapped; #endif + +#endif /* !_SYS_MSGBUF_H_ */ diff --git a/sys/sys/mtio.h b/sys/sys/mtio.h index e42c92bd02..1fcb3600eb 100644 --- a/sys/sys/mtio.h +++ b/sys/sys/mtio.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)mtio.h 7.6 (Berkeley) 2/5/91 + * from: @(#)mtio.h 7.6 (Berkeley) 2/5/91 + * $Id$ */ /* @@ -54,6 +55,22 @@ struct mtop { #define MTNOP 7 /* no operation, sets status only */ #define MTCACHE 8 /* enable controller cache */ #define MTNOCACHE 9 /* disable controller cache */ +#if defined(__386BSD__) + +/* Set block size for device. If device is a variable size dev */ +/* a non zero parameter will change the device to a fixed block size */ +/* device with block size set to that of the parameter passed in. */ +/* Resetting the block size to 0 will restore the device to a variable */ +/* block size device. At this time this command works across all modes */ +/* unlike the density command below.. this may change */ + +#define MTSETBSIZ 10 + +/* Set density values for device. Thye aredefined in the SCSI II spec */ +/* and range from 0 to 0x17. Sets the value for the openned mode only */ + +#define MTSETDNSTY 11 +#endif /* structure for MTIOCGET - mag tape get status command */ @@ -64,6 +81,13 @@ struct mtget { short mt_erreg; /* ``error'' register */ /* end device-dependent registers */ short mt_resid; /* residual count */ +#if defined (__386BSD__) + daddr_t mt_bsiz; /* block size, 0 is variable */ + short mt_dns_dflt; /* density setting for default density */ + short mt_dns_dsty1; /* density setting for high density */ + short mt_dns_dsty2; /* density setting for medium density */ + short mt_dns_dsty3; /* density setting for low density */ +#endif /* the following two are not yet implemented */ daddr_t mt_fileno; /* file number of current position */ daddr_t mt_blkno; /* block number of current position */ @@ -99,7 +123,7 @@ struct mtget { #define MTIOCEEOT _IO('m', 4) /* enable EOT error */ #ifndef KERNEL -#define DEFTAPE "/dev/rmt12" +#define DEFTAPE "/dev/rst0" #endif #ifdef KERNEL diff --git a/sys/sys/namei.h b/sys/sys/namei.h index 9c8d5ac91b..18e6db0425 100644 --- a/sys/sys/namei.h +++ b/sys/sys/namei.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)namei.h 7.15 (Berkeley) 5/15/91 + * from: @(#)namei.h 7.15 (Berkeley) 5/15/91 + * $Id$ */ #ifndef _NAMEI_H_ diff --git a/sys/sys/param.h b/sys/sys/param.h index 73dd4a6e9b..b5b7b8322f 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -30,10 +30,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)param.h 7.23 (Berkeley) 5/6/91 + * from: @(#)param.h 7.23 (Berkeley) 5/6/91 + * $Id: param.h,v 1.2 1993/10/08 20:59:38 rgrimes Exp $ */ -#define __386BSDREL__ 0.1 /* Release 0.1 */ +#ifndef _SYS_PARAM_H_ +#define _SYS_PARAM_H_ + #define BSD 199103 /* CSRG: March, 1991 system version (year & month) */ #define BSD4_3 1 #define BSD4_4 0.5 @@ -56,7 +59,7 @@ #include #define MAXCOMLEN 16 /* max command name remembered */ -#define MAXINTERP 32 /* max interpreter file name length */ +#define MAXINTERP 64 /* max interpreter file name length */ #define MAXLOGNAME 12 /* max login name length */ #define MAXUPRC CHILD_MAX /* max simultaneous processes */ #define NCARGS ARG_MAX /* max bytes for an exec function */ @@ -199,7 +202,7 @@ * MAXALLOCSIZE must be a power of two. */ #define MINBUCKET 4 /* 4 => min allocation of 16 bytes */ -#define MAXALLOCSAVE (2 * CLBYTES) +#define MAXALLOCSAVE (CLBYTES-1) /* * Scale factor for scaled integers used to count %cpu time and load avgs. @@ -214,3 +217,5 @@ */ #define FSHIFT 11 /* bits to right of fixed binary point */ #define FSCALE (1< +#include + +typedef struct dsreq { + u_long ds_flags; + u_long ds_time; + + u_long ds_private; + + caddr_t ds_cmdbuf; + u_char ds_cmdlen; + + caddr_t ds_databuf; + u_long ds_datalen; + + caddr_t ds_sensebuf; + u_char ds_senselen; + + u_char ds_ret; + u_char ds_status; + u_char ds_msg; + u_char ds_cmdsent; + u_long ds_datasent; + u_char ds_sensesent; +} dsreq_t; + +#define DS_ENTER _IOWR('d', 1, struct dsreq) +#define DS_DISCONNECT _IOW('d', 2, int) +#define DS_SYNC _IOW('d', 3, int) +#define DS_TARGET _IOW('d', 4, int) + +/* Data transfer: + */ +#define DSRQ_READ 0x00000001 +#define DSRQ_WRITE 0x00000002 +#define DSRQ_IOV 0x00000004 +#define DSRQ_BUF 0x00000008 + +/* devscsi options: + */ +#define DSRQ_ASYNC 0x00000010 +#define DSRQ_SENSE 0x00000020 +#define DSRQ_TARGET 0x00000040 + +/* select options: + */ +#define DSRQ_SELATN 0x00000080 +#define DSRQ_DISC 0x00000100 +#define DSRQ_SYNXFR 0x00000200 +#define DSRQ_SELMSG 0x00000400 + +/* progress/continuation callbacs: + */ +#define DSRQ_CALL 0x00000800 +#define DSRQ_ACKH 0x00001000 +#define DSRQ_ATNH 0x00002000 +#define DSRQ_ABORT 0x00004000 + +/* Host options (non-portable): + */ +#define DSRQ_TRACE 0x00008000 +#define DSRQ_PRINT 0x00010000 +#define DSRQ_CTRL1 0x00020000 +#define DSRQ_CTRL2 0x00040000 + +/* Additional flags: + */ +#define DSRQ_MIXRDWR 0x00080000 + +#define DSRT_OK 0 + +#define DSRT_DEVSCSI 1 +#define DSRT_MULT 2 +#define DSRT_CANCEL 3 +#define DSRT_REVCODE 4 +#define DSRT_AGAIN 5 +#define DSRT_UNIMPL 6 + +#define DSRT_HOST 7 +#define DSRT_NOSEL 8 +#define DSRT_SHORT 9 +#define DSRT_SENSE 10 +#define DSRT_NOSENSE 11 +#define DSRT_TIMEOUT 12 +#define DSRT_LONG 13 + +#define DSRT_PROTO 14 +#define DSRT_EBSY 15 +#define DSRT_REJECT 16 +#define DSRT_PARITY 17 +#define DSRT_MEMORY 18 +#define DSRT_CMDO 19 +#define DSRT_STAI 20 + +#ifdef BRAINDEAD +/* BUG: This does not belong here, + * but I don't want to break my code; it will be moved out + * in the near future. + */ + +#define CMDBUF(DS) (DS)->ds_cmdbuf +#endif + +#endif /* _SYS_SGIO_H_ */ diff --git a/sys/sys/shm.h b/sys/sys/shm.h index 4374d2d129..73489cee92 100644 --- a/sys/sys/shm.h +++ b/sys/sys/shm.h @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)shm.h 7.2 (Berkeley) 2/5/91 + * from: @(#)shm.h 7.2 (Berkeley) 2/5/91 + * $Id$ */ /* @@ -91,4 +92,20 @@ struct shmid_ds *shmsegs; struct shminfo shminfo; #endif +#ifndef KERNEL +#include + +__BEGIN_DECLS + +int shmsys __P((int, ...)); + +void *shmat __P((int, void *, int)); +int shmget __P((key_t, int, int)); +int shmctl __P((int, int, void *)); +int shmdt __P((void *)); + +__END_DECLS + +#endif + #endif /* !_SHM_H_ */ diff --git a/sys/sys/signal.h b/sys/sys/signal.h index 829f47e94b..bf638d72bb 100644 --- a/sys/sys/signal.h +++ b/sys/sys/signal.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)signal.h 7.16 (Berkeley) 3/17/91 + * from: @(#)signal.h 7.16 (Berkeley) 3/17/91 + * $Id$ */ #ifndef _SIGNAL_H_ @@ -185,7 +186,7 @@ struct sigcontext { */ #define sigmask(m) (1 << ((m)-1)) -#define BADSIG ((__sighandler_t) -1) +#define SIG_ERR ((__sighandler_t) -1) #endif /* _POSIX_SOURCE */ #define SIG_DFL ((__sighandler_t) 0) diff --git a/sys/sys/signalvar.h b/sys/sys/signalvar.h index d446d93d28..50758b6e15 100644 --- a/sys/sys/signalvar.h +++ b/sys/sys/signalvar.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)signalvar.h 7.1 (Berkeley) 5/9/91 + * from: @(#)signalvar.h 7.1 (Berkeley) 5/9/91 + * $Id$ */ #ifndef _SIGNALVAR_H_ /* tmp for user.h */ diff --git a/sys/sys/socket.h b/sys/sys/socket.h index 58febae34d..2bdbf0e115 100644 --- a/sys/sys/socket.h +++ b/sys/sys/socket.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)socket.h 7.13 (Berkeley) 4/20/91 + * from: @(#)socket.h 7.13 (Berkeley) 4/20/91 + * $Id$ */ /* diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h index ca115a1d59..761f1518cb 100644 --- a/sys/sys/socketvar.h +++ b/sys/sys/socketvar.h @@ -30,14 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)socketvar.h 7.17 (Berkeley) 5/5/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00061 - * -------------------- ----- ---------------------- - * - * 11 Dec 92 Williams Jolitz Fixed tty handling + * from: @(#)socketvar.h 7.17 (Berkeley) 5/5/91 + * $Id$ */ /* diff --git a/sys/sys/specdev.h b/sys/sys/specdev.h index 2012ca7ab7..f2313cbaf0 100644 --- a/sys/sys/specdev.h +++ b/sys/sys/specdev.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)specdev.h 7.4 (Berkeley) 4/19/91 + * from: @(#)specdev.h 7.4 (Berkeley) 4/19/91 + * $Id$ */ /* diff --git a/sys/sys/spkr.h b/sys/sys/spkr.h index 9aac0742f2..550aeeab3a 100644 --- a/sys/sys/spkr.h +++ b/sys/sys/spkr.h @@ -4,6 +4,8 @@ * v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990 * modified for 386bsd by Andrew A. Chernov * 386bsd only clean version, all SYSV stuff removed + * + * $Id$ */ #ifndef _SPKR_H_ @@ -26,4 +28,3 @@ typedef struct tone_t; #endif /* _SPKR_H_ */ -/* spkr.h ends here */ diff --git a/sys/sys/stat.h b/sys/sys/stat.h index 52948f38e5..0fcfac18a0 100644 --- a/sys/sys/stat.h +++ b/sys/sys/stat.h @@ -30,9 +30,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)stat.h 7.11 (Berkeley) 3/3/91 + * from: @(#)stat.h 7.11 (Berkeley) 3/3/91 + * $Id$ */ +#ifndef _SYS_STAT_H_ +#define _SYS_STAT_H_ + struct stat { dev_t st_dev; /* inode's device */ @@ -126,3 +130,4 @@ int lstat __P((const char *, struct stat *)); #endif /* not POSIX */ __END_DECLS #endif +#endif /* _SYS_STAT_H_ */ diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h index f4c2c904f2..3ce6b5f50b 100644 --- a/sys/sys/syscall.h +++ b/sys/sys/syscall.h @@ -2,7 +2,7 @@ * System call numbers. * * DO NOT EDIT-- this file is automatically generated. - * created from @(#)syscalls.master 7.26 (Berkeley) 3/25/91 + * created from $Id: syscalls.master,v 1.5 1993/10/24 06:19:58 paul Exp $ */ #define SYS_exit 1 @@ -160,6 +160,9 @@ #define SYS_fstatfs 158 #define SYS_async_daemon 160 #define SYS_getfh 161 +#define SYS_getdomainname 162 +#define SYS_setdomainname 163 +#define SYS_uname 164 #define SYS_shmsys 171 #define SYS_setgid 181 #define SYS_setegid 182 diff --git a/sys/sys/syslimits.h b/sys/sys/syslimits.h index 6ef56e3d72..c13270b51e 100644 --- a/sys/sys/syslimits.h +++ b/sys/sys/syslimits.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)syslimits.h 7.4 (Berkeley) 2/4/91 + * from: @(#)syslimits.h 7.4 (Berkeley) 2/4/91 + * $Id$ */ #define ARG_MAX 20480 /* max bytes for an exec function */ diff --git a/sys/sys/syslog.h b/sys/sys/syslog.h index f5635da03b..4a3ef77c9a 100644 --- a/sys/sys/syslog.h +++ b/sys/sys/syslog.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)syslog.h 7.20 (Berkeley) 2/23/91 + * from: @(#)syslog.h 7.20 (Berkeley) 2/23/91 + * $Id$ */ #define _PATH_LOG "/dev/log" diff --git a/sys/sys/systm.h b/sys/sys/systm.h index 5475fef05a..85d5e9fac7 100644 --- a/sys/sys/systm.h +++ b/sys/sys/systm.h @@ -30,20 +30,28 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)systm.h 7.17 (Berkeley) 5/25/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00158 - * -------------------- ----- ---------------------- - * - * 11 Dec 92 Williams Jolitz Fixed and tty handling - * 25 Apr 93 Bruce Evans Support for intr-0.1 - * 16 May 93 Rodney W. Grimes Added prototype for spinwait - * Disabled prototype for wakeup, timeout + * from: @(#)systm.h 7.17 (Berkeley) 5/25/91 + * $Id$ */ -extern char *panicstr; /* panic message */ +#ifndef _SYS_SYSTM_H_ +#define _SYS_SYSTM_H_ + +extern struct sysent { /* system call table */ + int sy_narg; /* number of arguments */ + int (*sy_call)(); /* implementing function */ +} sysent[]; + +/* Prototypes I needed to fix that kern_exit warning + ---- this really the first step in the work that's + been done on sun-lamp to add kernel function + prototypes. */ +void kexit __P((struct proc *, int)); +void cpu_exit __P((struct proc *)); +void swtch __P((void)); + + +extern const char *panicstr; /* panic message */ extern char version[]; /* system version */ extern char copyright[]; /* system copyright */ @@ -68,11 +76,6 @@ extern struct vnode *rootvp; /* vnode equivalent to above */ extern dev_t swapdev; /* swapping device */ extern struct vnode *swapdev_vp;/* vnode equivalent to above */ -extern struct sysent { /* system call table */ - int sy_narg; /* number of arguments */ - int (*sy_call)(); /* implementing function */ -} sysent[]; - extern int boothowto; /* reboot flags, from console subsystem */ #ifdef KADB extern char *bootesym; /* end of symbol info from boot */ @@ -85,6 +88,8 @@ extern char *bootesym; /* end of symbol info from boot */ /* * General function declarations. */ + + int nullop __P((void)); int enodev __P((void)); int enoioctl __P((void)); @@ -95,19 +100,19 @@ int selscan __P((struct proc *p, fd_set *ibits, fd_set *obits, int seltrue __P((dev_t dev, int which, struct proc *p)); void selwakeup __P((pid_t pid, int coll)); -void panic __P((char *)); +__dead void panic __P((const char *)); void tablefull __P((char *)); -void addlog __P((const char *, ...)); +int addlog __P((const char *, ...)); void log __P((int, const char *, ...)); -void printf __P((const char *, ...)); +int printf __P((const char *, ...)); int sprintf __P((char *buf, const char *, ...)); void ttyprintf __P((struct tty *, const char *, ...)); void bcopy __P((void *from, void *to, u_int len)); void ovbcopy __P((void *from, void *to, u_int len)); void bzero __P((void *buf, u_int len)); -int bcmp __P((void *str1, void *str2, u_int len)); -size_t strlen __P((const char *string)); +static int bcmp __P((void *str1, void *str2, u_int len)); +static size_t strlen __P((const char *string)); int copystr __P((void *kfaddr, void *kdaddr, u_int len, u_int *done)); int copyinstr __P((void *udaddr, void *kaddr, u_int len, u_int *done)); @@ -129,7 +134,7 @@ int suiword __P((void *base, int word)); int scanc __P((unsigned size, u_char *cp, u_char *table, int mask)); int skpc __P((int mask, int size, char *cp)); int locc __P((int mask, char *cp, unsigned size)); -int ffs __P((long value)); +static int ffs __P((long value)); /* * XXX - a lot were missing. A lot are still missing. Some of the above @@ -175,3 +180,4 @@ nonint wakeup __P((caddr_t chan)); * Machine-dependent function declarations. */ #include +#endif /* _SYS_SYSTM_H_ */ diff --git a/sys/sys/tablet.h b/sys/sys/tablet.h index e364ce1892..d5e9ae9d03 100644 --- a/sys/sys/tablet.h +++ b/sys/sys/tablet.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tablet.h 7.5 (Berkeley) 2/15/91 + * from: @(#)tablet.h 7.5 (Berkeley) 2/15/91 + * $Id$ */ #ifndef _TABLET_H_ diff --git a/sys/sys/termios.h b/sys/sys/termios.h index 9326d600a5..83fd1cbf3b 100644 --- a/sys/sys/termios.h +++ b/sys/sys/termios.h @@ -30,15 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)termios.h 7.22 (Berkeley) 5/7/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00150 - * -------------------- ----- ---------------------- - * - * 22 Apr 93 David Greenman support for 57600 and 115200 baud - * + * from: @(#)termios.h 7.22 (Berkeley) 5/7/91 + * $Id$ */ /* @@ -84,9 +77,9 @@ #define VTIME 17 /* !ICANON */ #ifndef _POSIX_SOURCE #define VSTATUS 18 /* ICANON */ +#endif /* 19 spare 2 */ #define NCCS 20 -#endif #define _POSIX_VDISABLE ((unsigned char)'\377') diff --git a/sys/sys/time.h b/sys/sys/time.h index 4f12085ae3..217c063c93 100644 --- a/sys/sys/time.h +++ b/sys/sys/time.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)time.h 7.6 (Berkeley) 2/22/91 + * from: @(#)time.h 7.6 (Berkeley) 2/22/91 + * $Id$ */ #ifndef _SYS_TIME_H_ diff --git a/sys/sys/timeb.h b/sys/sys/timeb.h index 6e048456f0..db1c7dc6c8 100644 --- a/sys/sys/timeb.h +++ b/sys/sys/timeb.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)timeb.h 7.2 (Berkeley) 5/5/91 + * from: @(#)timeb.h 7.2 (Berkeley) 5/5/91 + * $Id$ */ /* The ftime(2) system call structure -- deprecated. */ diff --git a/sys/sys/times.h b/sys/sys/times.h index 52ce6fa55a..fba49ead36 100644 --- a/sys/sys/times.h +++ b/sys/sys/times.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)times.h 7.6 (Berkeley) 3/9/91 + * from: @(#)times.h 7.6 (Berkeley) 3/9/91 + * $Id$ */ #include diff --git a/sys/sys/tprintf.h b/sys/sys/tprintf.h index 3bb8daddc9..10aed10379 100644 --- a/sys/sys/tprintf.h +++ b/sys/sys/tprintf.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tprintf.h 7.2 (Berkeley) 5/4/91 + * from: @(#)tprintf.h 7.2 (Berkeley) 5/4/91 + * $Id$ */ typedef struct session *tpr_t; diff --git a/sys/sys/trace.h b/sys/sys/trace.h index 573d3fb70c..3ee98949c4 100644 --- a/sys/sys/trace.h +++ b/sys/sys/trace.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)trace.h 7.6 (Berkeley) 5/5/91 + * from: @(#)trace.h 7.6 (Berkeley) 5/5/91 + * $Id$ */ /* diff --git a/sys/sys/tty.h b/sys/sys/tty.h index 1c6cddd148..d120cc8fa7 100644 --- a/sys/sys/tty.h +++ b/sys/sys/tty.h @@ -30,20 +30,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)tty.h 7.10 (Berkeley) 6/26/91 - * $Header: /usr/bill/working/sys/sys/RCS/tty.h,v 1.3 92/01/21 21:51:49 william Exp $ - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00061 - * -------------------- ----- ---------------------- - * - * 18 Aug 92 Stephen McKay Fixed RB_LEN macro - * 11 Dec 92 Williams Jolitz Fixed tty handling - * -------------------- ----- ---------------------- - * + * from: @(#)tty.h 7.10 (Berkeley) 6/26/91 + * $Id$ */ +#ifndef _SYS_TTY_H_ +#define _SYS_TTY_H_ + #include /* @@ -208,3 +201,5 @@ struct speedtab { /* symbolic sleep message strings */ extern char ttyin[], ttyout[], ttopen[], ttclos[], ttybg[], ttybuf[]; #endif + +#endif /* _SYS_TTY_H_ */ diff --git a/sys/sys/ttychars.h b/sys/sys/ttychars.h index 27bb20c434..3d611a48d2 100644 --- a/sys/sys/ttychars.h +++ b/sys/sys/ttychars.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ttychars.h 7.6 (Berkeley) 5/9/91 + * from: @(#)ttychars.h 7.6 (Berkeley) 5/9/91 + * $Id$ */ /* diff --git a/sys/sys/ttydefaults.h b/sys/sys/ttydefaults.h index 97a9dffe06..8b0122cc9e 100644 --- a/sys/sys/ttydefaults.h +++ b/sys/sys/ttydefaults.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ttydefaults.h 7.9 (Berkeley) 5/9/91 + * from: @(#)ttydefaults.h 7.9 (Berkeley) 5/9/91 + * $Id$ */ /* diff --git a/sys/sys/ttydev.h b/sys/sys/ttydev.h index 4e5731cdf4..9e57d709f9 100644 --- a/sys/sys/ttydev.h +++ b/sys/sys/ttydev.h @@ -30,15 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ttydev.h 7.8 (Berkeley) 5/9/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00150 - * -------------------- ----- ---------------------- - * - * 22 Apr 93 David Greenman support for 57600 and 115200 baud - * + * from: @(#)ttydev.h 7.8 (Berkeley) 5/9/91 + * $Id$ */ /* COMPATABILITY HEADER FILE */ diff --git a/sys/sys/types.h b/sys/sys/types.h index 937219fb48..ac2e068406 100644 --- a/sys/sys/types.h +++ b/sys/sys/types.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)types.h 7.17 (Berkeley) 5/6/91 + * from: @(#)types.h 7.17 (Berkeley) 5/6/91 + * $Id$ */ #ifndef _TYPES_H_ diff --git a/sys/sys/ucred.h b/sys/sys/ucred.h index 3e99505300..45a0f8afab 100644 --- a/sys/sys/ucred.h +++ b/sys/sys/ucred.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ucred.h 7.5 (Berkeley) 2/5/91 + * from: @(#)ucred.h 7.5 (Berkeley) 2/5/91 + * $Id$ */ #ifndef _UCRED_H_ diff --git a/sys/sys/uio.h b/sys/sys/uio.h index 30d315e6ed..395643d580 100644 --- a/sys/sys/uio.h +++ b/sys/sys/uio.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)uio.h 7.8 (Berkeley) 4/15/91 + * from: @(#)uio.h 7.8 (Berkeley) 4/15/91 + * $Id$ */ #ifndef _UIO_H_ diff --git a/sys/sys/un.h b/sys/sys/un.h index 445878185e..219603ed81 100644 --- a/sys/sys/un.h +++ b/sys/sys/un.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)un.h 7.7 (Berkeley) 6/28/90 + * from: @(#)un.h 7.7 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/sys/unistd.h b/sys/sys/unistd.h index 4d3460af16..584b7b2e5f 100644 --- a/sys/sys/unistd.h +++ b/sys/sys/unistd.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)unistd.h 5.14 (Berkeley) 4/1/91 + * from: @(#)unistd.h 5.14 (Berkeley) 4/1/91 + * $Id$ */ #ifndef _SYS_UNISTD_H_ diff --git a/sys/sys/unpcb.h b/sys/sys/unpcb.h index f7014fff61..ce4aac62c9 100644 --- a/sys/sys/unpcb.h +++ b/sys/sys/unpcb.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)unpcb.h 7.6 (Berkeley) 6/28/90 + * from: @(#)unpcb.h 7.6 (Berkeley) 6/28/90 + * $Id$ */ /* diff --git a/sys/sys/user.h b/sys/sys/user.h index b175752e0a..d9a815b921 100644 --- a/sys/sys/user.h +++ b/sys/sys/user.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)user.h 7.19 (Berkeley) 5/4/91 + * from: @(#)user.h 7.19 (Berkeley) 5/4/91 + * $Id$ */ #include diff --git a/sys/sys/utsname.h b/sys/sys/utsname.h new file mode 100644 index 0000000000..631f753ffd --- /dev/null +++ b/sys/sys/utsname.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1982, 1986, 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)utsname.h 1.0 (Berkeley) 43/29/93 + * $Id: utsname.h,v 1.2 1993/05/20 16:23:46 cgd Exp $ + */ + +#ifndef _UTSNAME_H_ +#define _UTSNAME_H_ + +#define SYS_NMLN 32 + +struct utsname { + char sysname[SYS_NMLN]; + char nodename[SYS_NMLN]; + char release[SYS_NMLN]; + char version[SYS_NMLN]; + char machine[SYS_NMLN]; +}; + +#ifndef KERNEL +#ifdef __STDC__ +extern int uname(struct utsname *); +#else +extern int uname(); +#endif +#else +extern struct utsname utsname; +#endif /* KERNEL */ + +#endif /* _UTSNAME_H_ */ + diff --git a/sys/sys/vadvise.h b/sys/sys/vadvise.h index 71ae7856f0..88627182c6 100644 --- a/sys/sys/vadvise.h +++ b/sys/sys/vadvise.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vadvise.h 7.2 (Berkeley) 5/5/91 + * from: @(#)vadvise.h 7.2 (Berkeley) 5/5/91 + * $Id$ */ /* diff --git a/sys/sys/vcmd.h b/sys/sys/vcmd.h index 8057499c1d..42cf30d3c4 100644 --- a/sys/sys/vcmd.h +++ b/sys/sys/vcmd.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vcmd.h 7.4 (Berkeley) 5/5/91 + * from: @(#)vcmd.h 7.4 (Berkeley) 5/5/91 + * $Id$ */ #include diff --git a/sys/sys/vlimit.h b/sys/sys/vlimit.h index 1306818535..0c9c3b620b 100644 --- a/sys/sys/vlimit.h +++ b/sys/sys/vlimit.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vlimit.h 7.2 (Berkeley) 5/5/91 + * from: @(#)vlimit.h 7.2 (Berkeley) 5/5/91 + * $Id$ */ /* diff --git a/sys/sys/vmmeter.h b/sys/sys/vmmeter.h index 99cd4ebc7c..1d9d124a7b 100644 --- a/sys/sys/vmmeter.h +++ b/sys/sys/vmmeter.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vmmeter.h 7.3 (Berkeley) 5/5/91 + * from: @(#)vmmeter.h 7.3 (Berkeley) 5/5/91 + * $Id$ */ /* diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index fa006e8915..d8b23d7e79 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -30,15 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vnode.h 7.39 (Berkeley) 6/27/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00129 - * -------------------- ----- ---------------------- - * - * 02 Apr 93 Paul Popelka Added support for PCFS - * + * from: @(#)vnode.h 7.39 (Berkeley) 6/27/91 + * $Id$ */ #ifndef KERNEL @@ -61,7 +54,7 @@ enum vtype { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO, VBAD }; * These are for the benefit of external programs only (e.g., pstat) * and should NEVER be inspected inside the kernel. */ -enum vtagtype { VT_NON, VT_UFS, VT_NFS, VT_MFS, VT_PCFS }; +enum vtagtype { VT_NON, VT_UFS, VT_NFS, VT_MFS, VT_PCFS, VT_ISOFS }; /* * This defines the maximum size of the private data area diff --git a/sys/sys/vsio.h b/sys/sys/vsio.h index e9a78a1c35..5eb6bee8ea 100644 --- a/sys/sys/vsio.h +++ b/sys/sys/vsio.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vsio.h 7.4 (Berkeley) 5/9/91 + * from: @(#)vsio.h 7.4 (Berkeley) 5/9/91 + * $Id$ */ /**************************************************************************** diff --git a/sys/sys/vtimes.h b/sys/sys/vtimes.h index a614568a43..a6f8580741 100644 --- a/sys/sys/vtimes.h +++ b/sys/sys/vtimes.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vtimes.h 7.2 (Berkeley) 5/5/91 + * from: @(#)vtimes.h 7.2 (Berkeley) 5/5/91 + * $Id$ */ /* diff --git a/sys/sys/wait.h b/sys/sys/wait.h index de3a50d6f6..b33f3031ea 100644 --- a/sys/sys/wait.h +++ b/sys/sys/wait.h @@ -30,9 +30,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)wait.h 7.17 (Berkeley) 6/19/91 + * from: @(#)wait.h 7.17 (Berkeley) 6/19/91 + * $Id$ */ +#ifndef _SYS_WAIT_H_ +#define _SYS_WAIT_H_ + /* * This file holds definitions relevent to the wait4 system call * and the alternate interfaces that use it (wait, wait3, waitpid). @@ -156,3 +160,4 @@ pid_t wait4 __P((pid_t, int *, int, struct rusage *)); #endif __END_DECLS #endif +#endif /* _SYS_WAIT_H_ */ diff --git a/sys/ufs/dinode.h b/sys/ufs/dinode.h index 599c6a4e27..abfeab0f5a 100644 --- a/sys/ufs/dinode.h +++ b/sys/ufs/dinode.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)dinode.h 7.10 (Berkeley) 5/8/91 + * from: @(#)dinode.h 7.10 (Berkeley) 5/8/91 + * $Id$ */ /* @@ -41,6 +42,8 @@ #define NDADDR 12 /* direct addresses in inode */ #define NIADDR 3 /* indirect addresses in inode */ +#define MAXFASTLINK (((NDADDR+NIADDR) * sizeof(daddr_t)) - 1) + struct dinode { u_short di_mode; /* 0: mode and type of file */ short di_nlink; /* 2: number of links to file */ @@ -53,14 +56,24 @@ struct dinode { long di_mtspare; time_t di_ctime; /* 32: last time inode changed */ long di_ctspare; - daddr_t di_db[NDADDR]; /* 40: disk block addresses */ - daddr_t di_ib[NIADDR]; /* 88: indirect blocks */ + union { + struct { + daddr_t di_udb[NDADDR]; /* 40: disk block addresses */ + daddr_t di_uib[NIADDR]; /* 88: indirect blocks */ + } di_addr; + char di_usymlink[MAXFASTLINK+1]; + } di_un; long di_flags; /* 100: status, currently unused */ long di_blocks; /* 104: blocks actually held */ long di_gen; /* 108: generation number */ - long di_spare[4]; /* 112: reserved, currently unused */ +#define DI_SPARE_SZ 4 /* 112: spare for 4 longs */ + u_long di_spare[DI_SPARE_SZ]; /* reserved (unused) */ }; +#define di_db di_un.di_addr.di_udb +#define di_ib di_un.di_addr.di_uib +#define di_symlink di_un.di_usymlink + #if BYTE_ORDER == LITTLE_ENDIAN || defined(tahoe) /* ugh! -- must be fixed */ #define di_size di_qsize.val[0] #else /* BYTE_ORDER == BIG_ENDIAN */ @@ -84,3 +97,8 @@ struct dinode { #define IREAD 0400 /* read permission */ #define IWRITE 0200 /* write permission */ #define IEXEC 0100 /* execute permission */ + +#define DFASTLINK(di) \ + ((((di).di_mode & IFMT) == IFLNK) && \ + ((di).di_size <= MAXFASTLINK) && \ + ((di).di_size == (di).di_spare[0])) diff --git a/sys/ufs/dir.h b/sys/ufs/dir.h index b5c154e5c3..d8e7e4e728 100644 --- a/sys/ufs/dir.h +++ b/sys/ufs/dir.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)dir.h 7.10 (Berkeley) 3/25/91 + * from: @(#)dir.h 7.10 (Berkeley) 3/25/91 + * $Id$ */ #ifndef _DIR_H_ diff --git a/sys/ufs/fs.h b/sys/ufs/fs.h index 37ad2b327a..f4c280d13b 100644 --- a/sys/ufs/fs.h +++ b/sys/ufs/fs.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)fs.h 7.12 (Berkeley) 5/8/91 + * from: @(#)fs.h 7.12 (Berkeley) 5/8/91 + * $Id$ */ /* diff --git a/sys/ufs/inode.h b/sys/ufs/inode.h index b945ae4238..b83c741abf 100644 --- a/sys/ufs/inode.h +++ b/sys/ufs/inode.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)inode.h 7.17 (Berkeley) 5/8/91 + * from: @(#)inode.h 7.17 (Berkeley) 5/8/91 + * $Id$ */ #ifdef KERNEL @@ -67,6 +68,8 @@ struct inode { struct dinode i_din; /* the on-disk dinode */ }; +#define FASTLINK(ip) (DFASTLINK((ip)->i_din)) +#define i_symlink i_din.di_symlink #define i_mode i_din.di_mode #define i_nlink i_din.di_nlink #define i_uid i_din.di_uid @@ -87,6 +90,7 @@ struct inode { #define i_gen i_din.di_gen #define i_forw i_chain[0] #define i_back i_chain[1] +#define i_di_spare i_din.di_spare /* flags */ #define ILOCKED 0x0001 /* inode is locked */ diff --git a/sys/ufs/lockf.h b/sys/ufs/lockf.h index 5ac4c6a07a..df1815ea7c 100644 --- a/sys/ufs/lockf.h +++ b/sys/ufs/lockf.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)lockf.h 7.1 (Berkeley) 2/1/91 + * from: @(#)lockf.h 7.1 (Berkeley) 2/1/91 + * $Id: lockf.h,v 1.2 1993/10/16 18:17:41 rgrimes Exp $ */ /* @@ -48,7 +49,7 @@ struct lockf { off_t lf_start; /* The byte # of the start of the lock */ off_t lf_end; /* The byte # of the end of the lock (-1=EOF)*/ caddr_t lf_id; /* The id of the resource holding the lock */ - struct inode *lf_inode; /* Back pointer to the inode */ + struct lockf **lf_head; /* Back pointer to the head of lockf list */ struct lockf *lf_next; /* A pointer to the next lock on this inode */ struct lockf *lf_block; /* The list of blocked locks */ }; diff --git a/sys/ufs/mfs_vfsops.c b/sys/ufs/mfs_vfsops.c index d2085f1f08..b4156e600d 100644 --- a/sys/ufs/mfs_vfsops.c +++ b/sys/ufs/mfs_vfsops.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)mfs_vfsops.c 7.19 (Berkeley) 4/16/91 + * from: @(#)mfs_vfsops.c 7.19 (Berkeley) 4/16/91 + * $Id$ */ #include "param.h" @@ -131,6 +132,7 @@ mfs_mount(mp, path, data, ndp, p) bzero(fs->fs_fsmnt + size, sizeof(fs->fs_fsmnt) - size); bcopy((caddr_t)fs->fs_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN); + mp->mnt_stat.f_mntonname[MNAMELEN-1] = '\0'; (void) copyinstr(args.name, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); diff --git a/sys/ufs/mfs_vnops.c b/sys/ufs/mfs_vnops.c index dfbdf659ef..c8f5fa4d50 100644 --- a/sys/ufs/mfs_vnops.c +++ b/sys/ufs/mfs_vnops.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)mfs_vnops.c 7.22 (Berkeley) 4/16/91 + * from: @(#)mfs_vnops.c 7.22 (Berkeley) 4/16/91 + * $Id$ */ #include "param.h" diff --git a/sys/ufs/mfsiom.h b/sys/ufs/mfsiom.h index 855a86bfdc..448ae55fc8 100644 --- a/sys/ufs/mfsiom.h +++ b/sys/ufs/mfsiom.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)mfsiom.h 7.2 (Berkeley) 6/28/90 + * from: @(#)mfsiom.h 7.2 (Berkeley) 6/28/90 + * $Id$ */ #define MFS_MAPREG (MAXPHYS/NBPG + 2) /* Kernel mapping pte's */ diff --git a/sys/ufs/mfsnode.h b/sys/ufs/mfsnode.h index 26735918c8..746cabfa3e 100644 --- a/sys/ufs/mfsnode.h +++ b/sys/ufs/mfsnode.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)mfsnode.h 7.3 (Berkeley) 4/16/91 + * from: @(#)mfsnode.h 7.3 (Berkeley) 4/16/91 + * $Id$ */ /* diff --git a/sys/ufs/quota.h b/sys/ufs/quota.h index a049e62bb7..e847050666 100644 --- a/sys/ufs/quota.h +++ b/sys/ufs/quota.h @@ -33,7 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)quota.h 7.9 (Berkeley) 2/22/91 + * from: @(#)quota.h 7.9 (Berkeley) 2/22/91 + * $Id$ */ #ifndef _QUOTA_ diff --git a/sys/ufs/ufs_alloc.c b/sys/ufs/ufs_alloc.c index 74886f7454..343055bf71 100644 --- a/sys/ufs/ufs_alloc.c +++ b/sys/ufs/ufs_alloc.c @@ -30,15 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ufs_alloc.c 7.26 (Berkeley) 5/2/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00106 - * -------------------- ----- ---------------------- - * - * 28 Mar 93 Chris Torek Fix contiguous block allocation. - * + * from: @(#)ufs_alloc.c 7.26 (Berkeley) 5/2/91 + * $Id$ */ #include "param.h" diff --git a/sys/ufs/ufs_bmap.c b/sys/ufs/ufs_bmap.c index cc0a23bf5f..d50b2ab388 100644 --- a/sys/ufs/ufs_bmap.c +++ b/sys/ufs/ufs_bmap.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ufs_bmap.c 7.13 (Berkeley) 5/8/91 + * from: @(#)ufs_bmap.c 7.13 (Berkeley) 5/8/91 + * $Id$ */ #include "param.h" diff --git a/sys/ufs/ufs_disksubr.c b/sys/ufs/ufs_disksubr.c index 2ee669e215..dbd02fed7e 100644 --- a/sys/ufs/ufs_disksubr.c +++ b/sys/ufs/ufs_disksubr.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91 + * from: @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91 + * $Id$ */ #include "param.h" @@ -556,7 +557,7 @@ diskerr(bp, dname, what, pri, blkdone, lp) register struct disklabel *lp; { int unit = dkunit(bp->b_dev), part = dkpart(bp->b_dev); - register void (*pr) __P((const char *, ...)); + register int (*pr) __P((const char *, ...)); char partname = 'a' + part; int sn; @@ -588,4 +589,5 @@ diskerr(bp, dname, what, pri, blkdone, lp) sn %= lp->d_secpercyl; (*pr)(" tn %d sn %d)", sn / lp->d_nsectors, sn % lp->d_nsectors); } + (*pr)("\n"); } diff --git a/sys/ufs/ufs_inode.c b/sys/ufs/ufs_inode.c index 34eaa76b1b..4a74d34523 100644 --- a/sys/ufs/ufs_inode.c +++ b/sys/ufs/ufs_inode.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ufs_inode.c 7.40 (Berkeley) 5/8/91 + * from: @(#)ufs_inode.c 7.40 (Berkeley) 5/8/91 + * $Id$ */ #include "param.h" @@ -140,6 +141,8 @@ loop: for (i = 0; i < MAXQUOTAS; i++) ip->i_dquot[i] = NODQUOT; #endif + for (i=0; i < DI_SPARE_SZ; i++) + ip->i_di_spare[i] = (unsigned long)0L; /* * Put it onto its hash chain and lock it so that other requests for * this inode will block if they arrive while we are sleeping waiting @@ -406,6 +409,13 @@ itrunc(oip, length, flags) struct inode tip; vnode_pager_setsize(ITOV(oip), length); + if (FASTLINK(oip)) { + if (length != 0) + panic("itrunc fastlink to non-zero"); + bzero(oip->i_symlink, MAXFASTLINK); + oip->i_size = 0; + oip->i_din.di_spare[0] = 0; + } if (oip->i_size <= length) { oip->i_flag |= ICHG|IUPD; error = iupdat(oip, &time, &time, 1); diff --git a/sys/ufs/ufs_lockf.c b/sys/ufs/ufs_lockf.c index 66987a4cd7..f5e416032c 100644 --- a/sys/ufs/ufs_lockf.c +++ b/sys/ufs/ufs_lockf.c @@ -33,15 +33,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ufs_lockf.c 7.7 (Berkeley) 7/2/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00169 - * -------------------- ----- ---------------------- - * - * 04 Jun 93 Paul Kranenburg Fix dangling pointer in lockf struct - * + * from: @(#)ufs_lockf.c 7.7 (Berkeley) 7/2/91 + * $Id: ufs_lockf.c,v 1.4 1993/10/20 07:31:39 davidg Exp $ */ #include "param.h" @@ -57,6 +50,96 @@ #include "quota.h" #include "inode.h" + + +/* + * Advisory record locking support + */ +lf_advlock(head, size, id, op, fl, flags) + struct lockf **head; + u_long size; + caddr_t id; + int op; + register struct flock *fl; + int flags; +{ + register struct lockf *lock; + off_t start, end; + int error; + + /* + * Avoid the common case of unlocking when inode has no locks. + */ + if (*head == (struct lockf *)0) { + if (op != F_SETLK) { + fl->l_type = F_UNLCK; + return (0); + } + } + + /* + * Convert the flock structure into a start and end. + */ + switch (fl->l_whence) { + + case SEEK_SET: + case SEEK_CUR: + /* + * Caller is responsible for adding any necessary offset + * when SEEK_CUR is used. + */ + start = fl->l_start; + break; + + case SEEK_END: + start = size + fl->l_start; + break; + + default: + return (EINVAL); + } + if (start < 0) + return (EINVAL); + if (fl->l_len == 0) + end = -1; + else + end = start + fl->l_len - 1; + /* + * Create the lockf structure + */ + MALLOC(lock, struct lockf *, sizeof *lock, M_LOCKF, M_WAITOK); + lock->lf_start = start; + lock->lf_end = end; + lock->lf_id = id; + lock->lf_head = head; + lock->lf_type = fl->l_type; + lock->lf_next = (struct lockf *)0; + lock->lf_block = (struct lockf *)0; + lock->lf_flags = flags; + /* + * Do the requested operation. + */ + switch(op) { + case F_SETLK: + return (lf_setlock(lock)); + + case F_UNLCK: + error = lf_clearlock(lock); + FREE(lock, M_LOCKF); + return (error); + + case F_GETLK: + error = lf_getlock(lock, fl); + FREE(lock, M_LOCKF); + return (error); + + default: + free(lock, M_LOCKF); + return (EINVAL); + } + /* NOTREACHED */ +} + /* * This variable controls the maximum number of processes that will * be checked in doing deadlock detection. @@ -78,7 +161,7 @@ lf_setlock(lock) register struct lockf *lock; { register struct lockf *block; - struct inode *ip = lock->lf_inode; + struct lockf **head = lock->lf_head; struct lockf **prev, *overlap, *ltmp; static char lockstr[] = "lockf"; int ovcase, priority, needtolink, error; @@ -164,6 +247,7 @@ lf_setlock(lock) #endif /* LOCKF_DEBUG */ if (error = tsleep((caddr_t)lock, priority, lockstr, 0)) { +#ifdef PK_LOCKF_FIX /* Paul Kranenburg's lockf fix (buggy!) */ /* Don't leave a dangling pointer in block list */ if (lf_getblock(lock) == block) { struct lockf **prev; @@ -179,6 +263,13 @@ lf_setlock(lock) } } free(lock, M_LOCKF); +#else /* Mark Tinguely's fix instead */ + (void) lf_clearlock(lock); + return (error); +#endif +#if 0 /* ...and this is the original code -DLG */ + free(lock, M_LOCKF); +#endif return (error); } } @@ -190,8 +281,8 @@ lf_setlock(lock) * Skip over locks owned by other processes. * Handle any locks that overlap and are owned by ourselves. */ - prev = &ip->i_lockf; - block = ip->i_lockf; + prev = head; + block = *head; needtolink = 1; for (;;) { if (ovcase = lf_findoverlap(block, lock, SELF, &prev, &overlap)) @@ -314,8 +405,8 @@ lf_setlock(lock) lf_clearlock(unlock) register struct lockf *unlock; { - struct inode *ip = unlock->lf_inode; - register struct lockf *lf = ip->i_lockf; + struct lockf **head = unlock->lf_head; + register struct lockf *lf = *head; struct lockf *overlap, **prev; int ovcase; @@ -327,7 +418,7 @@ lf_clearlock(unlock) if (lockf_debug & 1) lf_print("lf_clearlock", unlock); #endif /* LOCKF_DEBUG */ - prev = &ip->i_lockf; + prev = head; while (ovcase = lf_findoverlap(lf, unlock, SELF, &prev, &overlap)) { /* * Wakeup the list of locks to be retried. @@ -417,10 +508,10 @@ struct lockf * lf_getblock(lock) register struct lockf *lock; { - struct lockf **prev, *overlap, *lf = lock->lf_inode->i_lockf; + struct lockf **prev, *overlap, *lf = *(lock->lf_head); int ovcase; - prev = &lock->lf_inode->i_lockf; + prev = lock->lf_head; while (ovcase = lf_findoverlap(lf, lock, OTHERS, &prev, &overlap)) { /* * We've found an overlap, see if it blocks us @@ -515,7 +606,7 @@ lf_findoverlap(lf, lock, type, prev, overlap) return (2); } if (start <= lf->lf_start && - (end == -1 || + (end == -1 || (lf->lf_end != -1 && end >= lf->lf_end))) { /* Case 3 */ #ifdef LOCKF_DEBUG @@ -628,21 +719,21 @@ lf_split(lock1, lock2) lf_wakelock(listhead) struct lockf *listhead; { - register struct lockf *blocklist, *wakelock; + register struct lockf *blocklist, *wakelock; blocklist = listhead->lf_block; listhead->lf_block = NOLOCKF; - while (blocklist != NOLOCKF) { - wakelock = blocklist; - blocklist = blocklist->lf_block; + while (blocklist != NOLOCKF) { + wakelock = blocklist; + blocklist = blocklist->lf_block; wakelock->lf_block = NOLOCKF; wakelock->lf_next = NOLOCKF; #ifdef LOCKF_DEBUG if (lockf_debug & 2) lf_print("lf_wakelock: awakening", wakelock); #endif /* LOCKF_DEBUG */ - wakeup((caddr_t)wakelock); - } + wakeup((caddr_t)wakelock); + } } #ifdef LOCKF_DEBUG diff --git a/sys/ufs/ufs_lookup.c b/sys/ufs/ufs_lookup.c index 25d1f7a365..a4aa3d8ae7 100644 --- a/sys/ufs/ufs_lookup.c +++ b/sys/ufs/ufs_lookup.c @@ -30,10 +30,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ufs_lookup.c 7.33 (Berkeley) 5/19/91 + * from: @(#)ufs_lookup.c 7.33 (Berkeley) 5/19/91 + * $Id$ */ #include "param.h" +#include "systm.h" #include "namei.h" #include "buf.h" #include "file.h" @@ -141,7 +143,7 @@ ufs_lookup(vdp, ndp, p) if (error == ENOENT) return (error); #ifdef PARANOID - if (vdp == ndp->ni_rdir && ndp->ni_isdotdot) + if (vdp == ndp->ni_rootdir && ndp->ni_isdotdot) panic("ufs_lookup: .. through root"); #endif /* diff --git a/sys/ufs/ufs_quota.c b/sys/ufs/ufs_quota.c index 3813d2d90e..a3862ff7ab 100644 --- a/sys/ufs/ufs_quota.c +++ b/sys/ufs/ufs_quota.c @@ -33,8 +33,10 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ufs_quota.c 7.11 (Berkeley) 6/21/91 + * from: @(#)ufs_quota.c 7.11 (Berkeley) 6/21/91 + * $Id$ */ + #include "param.h" #include "kernel.h" #include "systm.h" diff --git a/sys/ufs/ufs_subr.c b/sys/ufs/ufs_subr.c index 9a730d535f..2fb344acdf 100644 --- a/sys/ufs/ufs_subr.c +++ b/sys/ufs/ufs_subr.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ufs_subr.c 7.13 (Berkeley) 6/28/90 + * from: @(#)ufs_subr.c 7.13 (Berkeley) 6/28/90 + * $Id$ */ #ifdef KERNEL diff --git a/sys/ufs/ufs_tables.c b/sys/ufs/ufs_tables.c index 1903e7c2dc..42ccff41ca 100644 --- a/sys/ufs/ufs_tables.c +++ b/sys/ufs/ufs_tables.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ufs_tables.c 7.4 (Berkeley) 6/28/90 + * from: @(#)ufs_tables.c 7.4 (Berkeley) 6/28/90 + * $Id$ */ #ifdef KERNEL diff --git a/sys/ufs/ufs_vfsops.c b/sys/ufs/ufs_vfsops.c index 3f6986c829..af663dccff 100644 --- a/sys/ufs/ufs_vfsops.c +++ b/sys/ufs/ufs_vfsops.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ufs_vfsops.c 7.56 (Berkeley) 6/28/91 + * from: @(#)ufs_vfsops.c 7.56 (Berkeley) 6/28/91 + * $Id$ */ #include "param.h" @@ -207,6 +208,7 @@ ufs_mount(mp, path, data, ndp, p) bzero(fs->fs_fsmnt + size, sizeof(fs->fs_fsmnt) - size); bcopy((caddr_t)fs->fs_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN); + mp->mnt_stat.f_mntonname[MNAMELEN-1] = '\0'; (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); @@ -457,7 +459,7 @@ ufs_quotactl(mp, cmds, uid, arg, p) #ifndef QUOTA return (EOPNOTSUPP); #else - if (uid == -1) + if (uid == (uid_t)(-1)) uid = p->p_cred->p_ruid; cmd = cmds >> SUBCMDSHIFT; diff --git a/sys/ufs/ufs_vnops.c b/sys/ufs/ufs_vnops.c index b7ff76c66d..7ddf0ea3ce 100644 --- a/sys/ufs/ufs_vnops.c +++ b/sys/ufs/ufs_vnops.c @@ -30,18 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ufs_vnops.c 7.64 (Berkeley) 5/16/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 3 00147 - * -------------------- ----- ---------------------- - * - * 27 Nov 92 Bruce Evans Fixed access() - * 20 Aug 92 David Greenman Fixed incorrect setting of B_AGE after - * each read to improve cache performance - * 20 Apr 93 Paul Kranenburg Detect and prevent kernel deadlocks in - * VM system + * from: @(#)ufs_vnops.c 7.64 (Berkeley) 5/16/91 + * $Id: ufs_vnops.c,v 1.4 1993/10/16 18:18:03 rgrimes Exp $ */ #include "param.h" @@ -606,7 +596,7 @@ ufs_write(vp, uio, ioflag, cred) if (cred->cr_uid != 0) ip->i_mode &= ~(ISUID|ISGID); } while (error == 0 && uio->uio_resid > 0 && n != 0); - if (error && (ioflag & IO_UNIT)) { + if (error == EFAULT || error && (ioflag & IO_UNIT)) { (void) itrunc(ip, osize, ioflag & IO_SYNC); uio->uio_offset -= resid - uio->uio_resid; uio->uio_resid = resid; @@ -1281,12 +1271,22 @@ ufs_symlink(ndp, vap, target, p) struct proc *p; { struct inode *ip; + int len = strlen(target); int error; error = maknode(IFLNK | vap->va_mode, ndp, &ip); if (error) return (error); - error = vn_rdwr(UIO_WRITE, ITOV(ip), target, strlen(target), (off_t)0, +#ifdef FASTLINKS + if (len <= MAXFASTLINK) { + ip->i_din.di_spare[0] = len; + ip->i_size = len; + bcopy(target, ip->i_symlink, len); + ip->i_flag |= ICHG; + error = iupdat(ip, &time, &time, 1); + } else +#endif + error = vn_rdwr(UIO_WRITE, ITOV(ip), target, len, (off_t)0, UIO_SYSSPACE, IO_NODELOCKED, ndp->ni_cred, (int *)0, (struct proc *)0); iput(ip); @@ -1328,8 +1328,11 @@ ufs_readlink(vp, uiop, cred) struct uio *uiop; struct ucred *cred; { - - return (ufs_read(vp, uiop, 0, cred)); + struct inode *ip = VTOI(vp); + if (FASTLINK(ip)) + return (uiomove(ip->i_symlink, ip->i_size, uiop)); + else + return (ufs_read(vp, uiop, 0, cred)); } /* @@ -1679,80 +1682,8 @@ ufs_advlock(vp, id, op, fl, flags) int flags; { register struct inode *ip = VTOI(vp); - register struct lockf *lock; - off_t start, end; - int error; - - /* - * Avoid the common case of unlocking when inode has no locks. - */ - if (ip->i_lockf == (struct lockf *)0) { - if (op != F_SETLK) { - fl->l_type = F_UNLCK; - return (0); - } - } - /* - * Convert the flock structure into a start and end. - */ - switch (fl->l_whence) { - - case SEEK_SET: - case SEEK_CUR: - /* - * Caller is responsible for adding any necessary offset - * when SEEK_CUR is used. - */ - start = fl->l_start; - break; - - case SEEK_END: - start = ip->i_size + fl->l_start; - break; - - default: - return (EINVAL); - } - if (start < 0) - return (EINVAL); - if (fl->l_len == 0) - end = -1; - else - end = start + fl->l_len - 1; - /* - * Create the lockf structure - */ - MALLOC(lock, struct lockf *, sizeof *lock, M_LOCKF, M_WAITOK); - lock->lf_start = start; - lock->lf_end = end; - lock->lf_id = id; - lock->lf_inode = ip; - lock->lf_type = fl->l_type; - lock->lf_next = (struct lockf *)0; - lock->lf_block = (struct lockf *)0; - lock->lf_flags = flags; - /* - * Do the requested operation. - */ - switch(op) { - case F_SETLK: - return (lf_setlock(lock)); - - case F_UNLCK: - error = lf_clearlock(lock); - FREE(lock, M_LOCKF); - return (error); - case F_GETLK: - error = lf_getlock(lock, fl); - FREE(lock, M_LOCKF); - return (error); - - default: - free(lock, M_LOCKF); - return (EINVAL); - } - /* NOTREACHED */ + return (lf_advlock(&(ip->i_lockf), ip->i_size, id, op, fl, flags)); } /* diff --git a/sys/ufs/ufsmount.h b/sys/ufs/ufsmount.h index 262665d25b..e072889a7f 100644 --- a/sys/ufs/ufsmount.h +++ b/sys/ufs/ufsmount.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ufsmount.h 7.9 (Berkeley) 5/8/91 + * from: @(#)ufsmount.h 7.9 (Berkeley) 5/8/91 + * $Id$ */ /* diff --git a/sys/vm/device_pager.c b/sys/vm/device_pager.c index 404bf965ed..91105c666c 100644 --- a/sys/vm/device_pager.c +++ b/sys/vm/device_pager.c @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)device_pager.c 7.2 (Berkeley) 4/20/91 + * from: @(#)device_pager.c 7.2 (Berkeley) 4/20/91 + * $Id$ */ /* @@ -54,6 +55,8 @@ #include "vm_page.h" #include "vm_kern.h" #include "device_pager.h" +#include "vnode.h" +#include "specdev.h" queue_head_t dev_pager_list; /* list of managed devices */ @@ -110,7 +113,7 @@ dev_pager_alloc(handle, size, prot) * Validation. Make sure this device can be mapped * and that range to map is acceptible to device. */ - dev = (dev_t)handle; + dev = ((struct vnode *) handle)->v_rdev; mapfunc = cdevsw[major(dev)].d_mmap; if (!mapfunc || mapfunc == enodev || mapfunc == nullop) return(NULL); diff --git a/sys/vm/device_pager.h b/sys/vm/device_pager.h index 9130bdbe2d..8940fe9da1 100644 --- a/sys/vm/device_pager.h +++ b/sys/vm/device_pager.h @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)device_pager.h 7.1 (Berkeley) 12/5/90 + * from: @(#)device_pager.h 7.1 (Berkeley) 12/5/90 + * $Id$ */ #ifndef _DEVICE_PAGER_ diff --git a/sys/vm/kern_lock.c b/sys/vm/kern_lock.c index 4f0eaf7069..2246f0faa9 100644 --- a/sys/vm/kern_lock.c +++ b/sys/vm/kern_lock.c @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)kern_lock.c 7.4 (Berkeley) 4/21/91 - * - * + * from: @(#)kern_lock.c 7.4 (Berkeley) 4/21/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * diff --git a/sys/vm/lock.h b/sys/vm/lock.h index fdd00d9f16..aaf1738c36 100644 --- a/sys/vm/lock.h +++ b/sys/vm/lock.h @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)lock.h 7.3 (Berkeley) 4/21/91 - * - * + * from: @(#)lock.h 7.3 (Berkeley) 4/21/91 + * $Id + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * diff --git a/sys/vm/pmap.h b/sys/vm/pmap.h index 0dd019bf01..6c68384312 100644 --- a/sys/vm/pmap.h +++ b/sys/vm/pmap.h @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pmap.h 7.4 (Berkeley) 5/7/91 - * - * + * from: @(#)pmap.h 7.4 (Berkeley) 5/7/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * diff --git a/sys/vm/queue.h b/sys/vm/queue.h index 00175baaab..581f13b432 100644 --- a/sys/vm/queue.h +++ b/sys/vm/queue.h @@ -34,8 +34,10 @@ * SUCH DAMAGE. * * @(#)queue.h 7.3 (Berkeley) 4/21/91 - * - * + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * diff --git a/sys/vm/swap_pager.c b/sys/vm/swap_pager.c index 3b5907894e..7ccab9fa6f 100644 --- a/sys/vm/swap_pager.c +++ b/sys/vm/swap_pager.c @@ -35,19 +35,10 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * from: Utah $Hdr: swap_pager.c 1.4 91/04/30$ - * - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00075 - * -------------------- ----- ---------------------- - * - * 22 Jan 1993 Bruce Evans Fixed unmatched spl calls - * - * @(#)swap_pager.c 7.4 (Berkeley) 5/7/91 + * from: Utah $Hdr: swap_pager.c 1.4 91/04/30$ + * from: @(#)swap_pager.c 7.4 (Berkeley) 5/7/91 + * $Id$ */ -static char rcsid[] = "$Header: /usr/bill/working/sys/vm/RCS/swap_pager.c,v 1.3 92/01/21 21:57:58 william Exp $"; /* * Quick hack to page to dedicated partition(s). diff --git a/sys/vm/swap_pager.h b/sys/vm/swap_pager.h index 7e9ea7932b..8f403d3631 100644 --- a/sys/vm/swap_pager.h +++ b/sys/vm/swap_pager.h @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)swap_pager.h 7.1 (Berkeley) 12/5/90 + * from: @(#)swap_pager.h 7.1 (Berkeley) 12/5/90 + * $Id$ */ #ifndef _SWAP_PAGER_ diff --git a/sys/vm/vm.h b/sys/vm/vm.h index 2f7d2447c7..45e876027b 100644 --- a/sys/vm/vm.h +++ b/sys/vm/vm.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm.h 7.1 (Berkeley) 5/5/91 + * from: @(#)vm.h 7.1 (Berkeley) 5/5/91 + * $Id$ */ #ifndef VM_H diff --git a/sys/vm/vm_fault.c b/sys/vm/vm_fault.c index 9939347684..d8b36f4fae 100644 --- a/sys/vm/vm_fault.c +++ b/sys/vm/vm_fault.c @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_fault.c 7.6 (Berkeley) 5/7/91 - * - * + * from: @(#)vm_fault.c 7.6 (Berkeley) 5/7/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * @@ -62,8 +64,6 @@ * rights to redistribute these changes. */ -static char rcsid[] = "$Header: /usr/bill/working/sys/vm/RCS/vm_fault.c,v 1.2 92/01/21 21:58:17 william Exp $"; - /* * Page fault handling module. */ @@ -528,6 +528,7 @@ thread_wakeup(&vm_pages_needed); /* XXX */ */ vm_page_lock_queues(); + vm_page_activate(m); vm_page_deactivate(m); pmap_page_protect(VM_PAGE_TO_PHYS(m), VM_PROT_NONE); vm_page_unlock_queues(); diff --git a/sys/vm/vm_glue.c b/sys/vm/vm_glue.c index 6416cebc08..2262f31aaa 100644 --- a/sys/vm/vm_glue.c +++ b/sys/vm/vm_glue.c @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_glue.c 7.8 (Berkeley) 5/15/91 - * - * + * from: @(#)vm_glue.c 7.8 (Berkeley) 5/15/91 + * $Id: vm_glue.c,v 1.8 1993/10/16 16:20:25 rgrimes Exp $ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * @@ -58,16 +60,9 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00137 - * -------------------- ----- ---------------------- - * - * 08 Apr 93 Bruce Evans Several VM system fixes */ -static char rcsid[] = "$Header: /usr/bill/working/sys/vm/RCS/vm_glue.c,v 1.2 92/01/21 21:58:21 william Exp $"; +#include "ddb.h" #include "param.h" #include "systm.h" #include "proc.h" @@ -80,7 +75,6 @@ static char rcsid[] = "$Header: /usr/bill/working/sys/vm/RCS/vm_glue.c,v 1.2 92/ #include "vm_kern.h" int avefree = 0; /* XXX */ -unsigned maxdmap = MAXDSIZ; /* XXX */ int readbuffers = 0; /* XXX allow kgdb to read kernel buffer pool */ kernacc(addr, len, rw) @@ -92,7 +86,7 @@ kernacc(addr, len, rw) vm_prot_t prot = rw == B_READ ? VM_PROT_READ : VM_PROT_WRITE; saddr = trunc_page(addr); - eaddr = round_page(addr+len-1); + eaddr = round_page(addr+len); rv = vm_map_check_protection(kernel_map, saddr, eaddr, prot); /* * XXX there are still some things (e.g. the buffer cache) that @@ -127,13 +121,13 @@ useracc(addr, len, rw) * only used (as an end address) in trap.c. Use it as an end * address here too. */ - if ((vm_offset_t) addr >= VM_MAXUSER_ADDRESS + UPAGES * NBPG - || (vm_offset_t) addr + len > VM_MAXUSER_ADDRESS + UPAGES * NBPG + if ((vm_offset_t) addr >= VM_MAXUSER_ADDRESS + || (vm_offset_t) addr + len > VM_MAXUSER_ADDRESS || (vm_offset_t) addr + len <= (vm_offset_t) addr) return (FALSE); rv = vm_map_check_protection(&curproc->p_vmspace->vm_map, - trunc_page(addr), round_page(addr+len-1), prot); + trunc_page(addr), round_page(addr+len), prot); return(rv == TRUE); } @@ -150,7 +144,7 @@ chgkprot(addr, len, rw) vm_prot_t prot = rw == B_READ ? VM_PROT_READ : VM_PROT_WRITE; vm_map_protect(kernel_map, trunc_page(addr), - round_page(addr+len-1), prot, FALSE); + round_page(addr+len), prot, FALSE); } #endif @@ -159,7 +153,7 @@ vslock(addr, len) u_int len; { vm_map_pageable(&curproc->p_vmspace->vm_map, trunc_page(addr), - round_page(addr+len-1), FALSE); + round_page(addr+len), FALSE); } vsunlock(addr, len, dirtied) @@ -171,7 +165,7 @@ vsunlock(addr, len, dirtied) dirtied++; #endif lint vm_map_pageable(&curproc->p_vmspace->vm_map, trunc_page(addr), - round_page(addr+len-1), TRUE); + round_page(addr+len), TRUE); } /* @@ -457,6 +451,7 @@ swapout(p) #endif size = round_page(ctob(UPAGES)); addr = (vm_offset_t) p->p_addr; + p->p_stats->p_ru.ru_nswap++ ; /* record in resource stats */ #ifdef notyet #ifdef hp300 /* @@ -544,6 +539,7 @@ thread_wakeup(event) * DEBUG stuff */ +#if defined(DEBUG) || (NDDB > 0) int indent = 0; /*ARGSUSED2*/ @@ -561,3 +557,4 @@ iprintf(a, b, c, d, e, f, g, h) printf(" "); printf(a, b, c, d, e, f, g, h); } +#endif /* defined(DEBUG) || (NDDB > 0) */ diff --git a/sys/vm/vm_inherit.h b/sys/vm/vm_inherit.h index c185845ecc..40f1e0925c 100644 --- a/sys/vm/vm_inherit.h +++ b/sys/vm/vm_inherit.h @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_inherit.h 7.2 (Berkeley) 4/21/91 - * - * + * from: @(#)vm_inherit.h 7.2 (Berkeley) 4/21/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * diff --git a/sys/vm/vm_init.c b/sys/vm/vm_init.c index e18f64ac20..f74b970a42 100644 --- a/sys/vm/vm_init.c +++ b/sys/vm/vm_init.c @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_init.c 7.3 (Berkeley) 4/21/91 - * - * + * from: @(#)vm_init.c 7.3 (Berkeley) 4/21/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * diff --git a/sys/vm/vm_kern.c b/sys/vm/vm_kern.c index aa9f793de0..94539a9c42 100644 --- a/sys/vm/vm_kern.c +++ b/sys/vm/vm_kern.c @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_kern.c 7.4 (Berkeley) 5/7/91 - * - * + * from: @(#)vm_kern.c 7.4 (Berkeley) 5/7/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * @@ -61,13 +63,6 @@ * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00134 - * -------------------- ----- ---------------------- - * - * 08 Apr 93 Andrew Herbert Better messages for kmem_alloc panics - * John Dyson Add vm_map_simplify's to reduce frags */ /* @@ -75,6 +70,7 @@ */ #include "param.h" +#include "syslog.h" #include "vm.h" #include "vm_page.h" @@ -382,7 +378,8 @@ kmem_malloc(map, size, canwait) if (map == kmem_map) panic("kmem_malloc: kmem_map too small"); else if (map == mb_map) - printf("kmem_malloc: mb_map too small (can't wait)\n"); + log(LOG_WARNING, + "kmem_malloc: mb_map too small (can't wait)\n"); } return 0; } diff --git a/sys/vm/vm_kern.h b/sys/vm/vm_kern.h index f52bcfe2df..5f2fe01358 100644 --- a/sys/vm/vm_kern.h +++ b/sys/vm/vm_kern.h @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_kern.h 7.2 (Berkeley) 4/21/91 - * - * + * from: @(#)vm_kern.h 7.2 (Berkeley) 4/21/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c index b7a36e03ed..74f3b1f337 100644 --- a/sys/vm/vm_map.c +++ b/sys/vm/vm_map.c @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_map.c 7.3 (Berkeley) 4/21/91 - * - * + * from: @(#)vm_map.c 7.3 (Berkeley) 4/21/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * @@ -60,20 +62,15 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00137 - * -------------------- ----- ---------------------- - * - * 08 Apr 93 Yuval Yarom Several VM system fixes */ /* * Virtual memory mapping module. */ +#include "ddb.h" #include "param.h" +#include "systm.h" #include "malloc.h" #include "vm.h" #include "vm_page.h" @@ -2410,6 +2407,7 @@ void vm_map_simplify(map, start) vm_map_unlock(map); } +#if defined(DEBUG) || (NDDB > 0) /* * vm_map_print: [ debug ] */ @@ -2479,3 +2477,4 @@ void vm_map_print(map, full) } indent -= 2; } +#endif /* defined(DEBUG) || (NDDB > 0) */ diff --git a/sys/vm/vm_map.h b/sys/vm/vm_map.h index 0f4e016600..8f1334d5f1 100644 --- a/sys/vm/vm_map.h +++ b/sys/vm/vm_map.h @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_map.h 7.3 (Berkeley) 4/21/91 - * - * + * from: @(#)vm_map.h 7.3 (Berkeley) 4/21/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * @@ -61,12 +63,6 @@ * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00002 - * -------------------- ----- ---------------------- - * - * 15 Aug 92 William Jolitz Prevent running out of map entries... */ /* diff --git a/sys/vm/vm_meter.c b/sys/vm/vm_meter.c index e1dfaa397b..8272915e86 100644 --- a/sys/vm/vm_meter.c +++ b/sys/vm/vm_meter.c @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_meter.c 7.11 (Berkeley) 4/20/91 + * from: @(#)vm_meter.c 7.11 (Berkeley) 4/20/91 + * $Id$ */ #include "param.h" diff --git a/sys/vm/vm_mmap.c b/sys/vm/vm_mmap.c index a399bd4e4e..65c96da090 100644 --- a/sys/vm/vm_mmap.c +++ b/sys/vm/vm_mmap.c @@ -35,16 +35,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * from: Utah $Hdr: vm_mmap.c 1.3 90/01/21$ - * - * @(#)vm_mmap.c 7.5 (Berkeley) 6/28/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00137 - * -------------------- ----- ---------------------- - * - * 08 Apr 93 Yuval Yarom Several VM system fixes + * from: Utah $Hdr: vm_mmap.c 1.3 90/01/21$ + * from: @(#)vm_mmap.c 7.5 (Berkeley) 6/28/91 + * $Id$ */ /* @@ -84,12 +77,14 @@ getpagesize(p, uap, retval) return (0); } +struct sbrk_args { + int incr; +}; + /* ARGSUSED */ sbrk(p, uap, retval) struct proc *p; - struct args { - int incr; - } *uap; + struct sbrk_args *uap; int *retval; { @@ -97,12 +92,14 @@ sbrk(p, uap, retval) return (EOPNOTSUPP); } +struct sstk_args { + int incr; +}; + /* ARGSUSED */ sstk(p, uap, retval) struct proc *p; - struct args { - int incr; - } *uap; + struct sstk_args *uap; int *retval; { @@ -110,16 +107,18 @@ sstk(p, uap, retval) return (EOPNOTSUPP); } +struct smmap_args { + caddr_t addr; + int len; + int prot; + int flags; + int fd; + off_t pos; +}; + smmap(p, uap, retval) struct proc *p; - register struct args { - caddr_t addr; - int len; - int prot; - int flags; - int fd; - off_t pos; - } *uap; + register struct smmap_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; @@ -127,9 +126,11 @@ smmap(p, uap, retval) struct vnode *vp; vm_offset_t addr; vm_size_t size; + vm_prot_t maxprot; vm_prot_t prot; caddr_t handle; int mtype, error; + int flags = uap->flags; #ifdef DEBUG if (mmapdebug & MDB_FOLLOW) @@ -140,7 +141,7 @@ smmap(p, uap, retval) /* * Make sure one of the sharing types is specified */ - mtype = uap->flags & MAP_TYPE; + mtype = flags & MAP_TYPE; switch (mtype) { case MAP_FILE: case MAP_ANON: @@ -153,7 +154,7 @@ smmap(p, uap, retval) * Size is implicitly rounded to a page boundary. */ addr = (vm_offset_t) uap->addr; - if ((uap->flags & MAP_FIXED) && (addr & page_mask) || uap->len < 0) + if ((flags & MAP_FIXED) && (addr & page_mask) || uap->len < 0) return(EINVAL); size = (vm_size_t) round_page(uap->len); if ((uap->flags & MAP_FIXED) && (addr + size > VM_MAXUSER_ADDRESS)) @@ -165,7 +166,7 @@ smmap(p, uap, retval) * There should really be a pmap call to determine a reasonable * location. */ - if (addr == 0 && (uap->flags & MAP_FIXED) == 0) + if (addr == 0 && (flags & MAP_FIXED) == 0) addr = round_page(p->p_vmspace->vm_daddr + MAXDSIZ); /* * Mapping file or named anonymous, get fp for validation @@ -194,14 +195,29 @@ smmap(p, uap, retval) * if mapping is shared. */ if ((uap->prot & PROT_READ) && (fp->f_flag & FREAD) == 0 || - ((uap->flags & MAP_SHARED) && + ((flags & MAP_SHARED) && (uap->prot & PROT_WRITE) && (fp->f_flag & FWRITE) == 0)) return(EACCES); handle = (caddr_t)vp; - } else if (uap->fd != -1) + /* + * PATCH GVR 25-03-93 + * Map protections to MACH style + */ + if(uap->flags & MAP_SHARED) { + maxprot = VM_PROT_EXECUTE; + if (fp->f_flag & FREAD) + maxprot |= VM_PROT_READ; + if (fp->f_flag & FWRITE) + maxprot |= VM_PROT_WRITE; + } else + maxprot = VM_PROT_ALL; + } else if (uap->fd != -1) { + maxprot = VM_PROT_ALL; handle = (caddr_t)fp; - else + } else { + maxprot = VM_PROT_ALL; handle = NULL; + } /* * Map protections to MACH style */ @@ -213,19 +229,21 @@ smmap(p, uap, retval) if (uap->prot & PROT_EXEC) prot |= VM_PROT_EXECUTE; - error = vm_mmap(&p->p_vmspace->vm_map, &addr, size, prot, - uap->flags, handle, (vm_offset_t)uap->pos); + error = vm_mmap(&p->p_vmspace->vm_map, &addr, size, prot, maxprot, + flags, handle, (vm_offset_t)uap->pos); if (error == 0) *retval = (int) addr; return(error); } +struct msync_args { + caddr_t addr; + int len; +}; + msync(p, uap, retval) struct proc *p; - struct args { - caddr_t addr; - int len; - } *uap; + struct msync_args *uap; int *retval; { vm_offset_t addr, objoff, oaddr; @@ -292,12 +310,14 @@ msync(p, uap, retval) return(0); } +struct munmap_args { + caddr_t addr; + int len; +}; + munmap(p, uap, retval) register struct proc *p; - register struct args { - caddr_t addr; - int len; - } *uap; + register struct munmap_args *uap; int *retval; { vm_offset_t addr; @@ -339,13 +359,15 @@ munmapfd(p, fd) p->p_fd->fd_ofileflags[fd] &= ~UF_MAPPED; } +struct mprotect_args { + caddr_t addr; + int len; + int prot; +}; + mprotect(p, uap, retval) struct proc *p; - struct args { - caddr_t addr; - int len; - int prot; - } *uap; + struct mprotect_args *uap; int *retval; { vm_offset_t addr; @@ -383,14 +405,16 @@ mprotect(p, uap, retval) return (EINVAL); } +struct madvise_args { + caddr_t addr; + int len; + int behav; +}; + /* ARGSUSED */ madvise(p, uap, retval) struct proc *p; - struct args { - caddr_t addr; - int len; - int behav; - } *uap; + struct madvise_args *uap; int *retval; { @@ -398,14 +422,16 @@ madvise(p, uap, retval) return (EOPNOTSUPP); } +struct mincore_args { + caddr_t addr; + int len; + char *vec; +}; + /* ARGSUSED */ mincore(p, uap, retval) struct proc *p; - struct args { - caddr_t addr; - int len; - char *vec; - } *uap; + struct mincore_args *uap; int *retval; { @@ -420,11 +446,12 @@ mincore(p, uap, retval) * MAP_FILE: a vnode pointer * MAP_ANON: NULL or a file pointer */ -vm_mmap(map, addr, size, prot, flags, handle, foff) +vm_mmap(map, addr, size, prot, maxprot, flags, handle, foff) register vm_map_t map; register vm_offset_t *addr; register vm_size_t size; vm_prot_t prot; + vm_prot_t maxprot; register int flags; caddr_t handle; /* XXX should be vp */ vm_offset_t foff; @@ -458,7 +485,7 @@ vm_mmap(map, addr, size, prot, flags, handle, foff) vp = (struct vnode *)handle; if (vp->v_type == VCHR) { type = PG_DEVICE; - handle = (caddr_t)vp->v_rdev; + handle = (caddr_t)vp; } else type = PG_VNODE; } @@ -643,14 +670,26 @@ vm_mmap(map, addr, size, prot, flags, handle, foff) * entirely correct. Maybe the maximum protection should be based * on the object permissions where it makes sense (e.g. a vnode). * - * Changed my mind: leave max prot at VM_PROT_ALL. + * XXX Changed my mind: leave max prot at VM_PROT_ALL. + * PATCH GVR 25-03-93: + * Changed again: indeed set maximum protection based on + * object permissions. */ - if (prot != VM_PROT_ALL) { rv = vm_map_protect(map, *addr, *addr+size, prot, FALSE); if (rv != KERN_SUCCESS) { (void) vm_deallocate(map, *addr, size); goto out; } + /* + * We only need to set max_protection in case it's + * unequal to its default, which is VM_PROT_DEFAULT. + */ + if(maxprot != VM_PROT_DEFAULT) { + rv = vm_map_protect(map, *addr, *addr+size, maxprot, TRUE); + if (rv != KERN_SUCCESS) { + (void) vm_deallocate(map, *addr, size); + goto out; + } } /* * Shared memory is also shared with children. diff --git a/sys/vm/vm_object.c b/sys/vm/vm_object.c index fdb465c5e7..28845b6b5f 100644 --- a/sys/vm/vm_object.c +++ b/sys/vm/vm_object.c @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_object.c 7.4 (Berkeley) 5/7/91 - * - * + * from: @(#)vm_object.c 7.4 (Berkeley) 5/7/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * @@ -60,20 +62,14 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00147 - * -------------------- ----- ---------------------- - * - * 20 Apr 93 Paul Kranenburg Detect and prevent kernel deadlocks in - * VM system */ /* * Virtual memory object module. */ +#include "ddb.h" + #include "param.h" #include "malloc.h" @@ -256,6 +252,7 @@ void vm_object_deallocate(object) */ if (object->can_persist) { +#ifdef DIAGNOSTIC register vm_page_t p; /* @@ -276,6 +273,7 @@ void vm_object_deallocate(object) p = (vm_page_t) queue_next(&p->listq); } +#endif /* DIAGNOSTIC */ queue_enter(&vm_object_cached_list, object, vm_object_t, cached_list); @@ -483,7 +481,11 @@ vm_object_deactivate_pages(object) while (!queue_end(&object->memq, (queue_entry_t) p)) { next = (vm_page_t) queue_next(&p->listq); vm_page_lock_queues(); - vm_page_deactivate(p); + if (!p->busy) + vm_page_deactivate(p); /* optimisation from mach 3.0 - + * andrew@werple.apana.org.au, + * Feb '93 + */ vm_page_unlock_queues(); p = next; } @@ -1170,12 +1172,29 @@ void vm_object_collapse(object) } else { if (pp) { +#if 1 + /* + * This should never happen -- the + * parent cannot have ever had an + * external memory object, and thus + * cannot have absent pages. + */ + panic("vm_object_collapse: bad case"); + /* andrew@werple.apana.org.au - from + mach 3.0 VM */ +#else /* may be someone waiting for it */ PAGE_WAKEUP(pp); vm_page_lock_queues(); vm_page_free(pp); vm_page_unlock_queues(); +#endif } + /* + * Parent now has no page. + * Move the backing object's page + * up. + */ vm_page_rename(p, object, new_offset); } } @@ -1190,7 +1209,21 @@ void vm_object_collapse(object) */ object->pager = backing_object->pager; +#if 1 + /* Mach 3.0 code */ + /* andrew@werple.apana.org.au, 12 Feb 1993 */ + + /* + * If there is no pager, leave paging-offset alone. + */ + if (object->pager) + object->paging_offset = + backing_object->paging_offset + + backing_offset; +#else + /* old VM 2.5 version */ object->paging_offset += backing_offset; +#endif backing_object->pager = NULL; @@ -1289,6 +1322,18 @@ void vm_object_collapse(object) vm_object_reference(object->shadow = backing_object->shadow); object->shadow_offset += backing_object->shadow_offset; +#if 1 + /* Mach 3.0 code */ + /* andrew@werple.apana.org.au, 12 Feb 1993 */ + + /* + * Backing object might have had a copy pointer + * to us. If it did, clear it. + */ + if (backing_object->copy == object) + backing_object->copy = NULL; +#endif + /* Drop the reference count on backing_object. * Since its ref_count was at least 2, it * will not vanish; so we don't need to call @@ -1428,6 +1473,7 @@ boolean_t vm_object_coalesce(prev_object, next_object, return(TRUE); } +#if defined(DEBUG) || (NDDB > 0) /* * vm_object_print: [ debug ] */ @@ -1476,3 +1522,4 @@ void vm_object_print(object, full) printf("\n"); indent -= 2; } +#endif /* defined(DEBUG) || (NDDB > 0) */ diff --git a/sys/vm/vm_object.h b/sys/vm/vm_object.h index f024f8e519..125891a0a1 100644 --- a/sys/vm/vm_object.h +++ b/sys/vm/vm_object.h @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_object.h 7.3 (Berkeley) 4/21/91 - * - * + * from: @(#)vm_object.h 7.3 (Berkeley) 4/21/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * diff --git a/sys/vm/vm_page.c b/sys/vm/vm_page.c index ff3e21cf98..bcc7b546ec 100644 --- a/sys/vm/vm_page.c +++ b/sys/vm/vm_page.c @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_page.c 7.4 (Berkeley) 5/7/91 - * - * + * from: @(#)vm_page.c 7.4 (Berkeley) 5/7/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * @@ -60,17 +62,6 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 3 00147 - * -------------------- ----- ---------------------- - * - * 22 Jan 93 Paul Mackerras Fixed bug where pages got lost - * 08 Apr 93 Yuval Yarom Several VM system fixes - * 20 Apr 93 Paul Kranenburg Detect and prevent kernel deadlocks in - * VM system - * */ /* diff --git a/sys/vm/vm_page.h b/sys/vm/vm_page.h index 45aa618722..6d5f18fbb2 100644 --- a/sys/vm/vm_page.h +++ b/sys/vm/vm_page.h @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_page.h 7.3 (Berkeley) 4/21/91 - * - * + * from: @(#)vm_page.h 7.3 (Berkeley) 4/21/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * diff --git a/sys/vm/vm_pageout.c b/sys/vm/vm_pageout.c index c89e884e27..62820cee1d 100644 --- a/sys/vm/vm_pageout.c +++ b/sys/vm/vm_pageout.c @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_pageout.c 7.4 (Berkeley) 5/7/91 - * - * + * from: @(#)vm_pageout.c 7.4 (Berkeley) 5/7/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * @@ -60,15 +62,6 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00137 - * -------------------- ----- ---------------------- - * - * 20 Aug 92 David Greenman Removed un-necessary call to - * swapout_thread - * 08 Aug 93 Paul Kranenburg Add counters for vmstat */ /* diff --git a/sys/vm/vm_pageout.h b/sys/vm/vm_pageout.h index 3f63f758ac..ffa94c8b41 100644 --- a/sys/vm/vm_pageout.h +++ b/sys/vm/vm_pageout.h @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_pageout.h 7.3 (Berkeley) 4/21/91 - * - * + * from: @(#)vm_pageout.h 7.3 (Berkeley) 4/21/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * diff --git a/sys/vm/vm_pager.c b/sys/vm/vm_pager.c index 8d6be7f0b6..df6ffe3521 100644 --- a/sys/vm/vm_pager.c +++ b/sys/vm/vm_pager.c @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_pager.c 7.4 (Berkeley) 5/7/91 - * - * + * from: @(#)vm_pager.c 7.4 (Berkeley) 5/7/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * @@ -60,13 +62,6 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00063 - * -------------------- ----- ---------------------- - * - * 28 Nov 1991 Poul-Henning Kamp Speedup processing */ /* @@ -130,9 +125,10 @@ vm_pager_init() PAGER_MAP_SIZE, FALSE); /* * Initialize known pagers + * If pgops is a null pointer skip over it. */ for (pgops = pagertab; pgops < &pagertab[npagers]; pgops++) - (*(*pgops)->pgo_init)(); + if (*pgops) (*(*pgops)->pgo_init)(); if (dfltpagerops == NULL) panic("no default pager"); } diff --git a/sys/vm/vm_pager.h b/sys/vm/vm_pager.h index e3e0880645..a36832b5d6 100644 --- a/sys/vm/vm_pager.h +++ b/sys/vm/vm_pager.h @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_pager.h 7.2 (Berkeley) 4/20/91 + * from: @(#)vm_pager.h 7.2 (Berkeley) 4/20/91 + * $Id$ */ /* diff --git a/sys/vm/vm_param.h b/sys/vm/vm_param.h index 8f530d01e7..db2f0824dd 100644 --- a/sys/vm/vm_param.h +++ b/sys/vm/vm_param.h @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_param.h 7.2 (Berkeley) 4/21/91 - * - * + * from: @(#)vm_param.h 7.2 (Berkeley) 4/21/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * diff --git a/sys/vm/vm_prot.h b/sys/vm/vm_prot.h index dc01b13c34..ffda8f8078 100644 --- a/sys/vm/vm_prot.h +++ b/sys/vm/vm_prot.h @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_prot.h 7.2 (Berkeley) 4/21/91 - * - * + * from: @(#)vm_prot.h 7.2 (Berkeley) 4/21/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * diff --git a/sys/vm/vm_statistics.h b/sys/vm/vm_statistics.h index 82a85c75f0..4cfd83728d 100644 --- a/sys/vm/vm_statistics.h +++ b/sys/vm/vm_statistics.h @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_statistics.h 7.2 (Berkeley) 4/21/91 - * - * + * from: @(#)vm_statistics.h 7.2 (Berkeley) 4/21/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * diff --git a/sys/vm/vm_swap.c b/sys/vm/vm_swap.c index 9850f220da..0d4e71783d 100644 --- a/sys/vm/vm_swap.c +++ b/sys/vm/vm_swap.c @@ -30,9 +30,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_swap.c 7.18 (Berkeley) 5/6/91 + * from: @(#)vm_swap.c 7.18 (Berkeley) 5/6/91 + * $Id$ */ -static char rcsid[] = "$Header: /usr/bill/working/sys/vm/RCS/vm_swap.c,v 1.3 92/01/21 21:58:25 william Exp $"; #include "param.h" #include "systm.h" @@ -167,12 +167,15 @@ swstrategy(bp) * which must be in the swdevsw. Return EBUSY * if already swapping on this device. */ + +struct swapon_args { + char *name; +}; + /* ARGSUSED */ swapon(p, uap, retval) struct proc *p; - struct args { - char *name; - } *uap; + struct swapon_args *uap; int *retval; { register struct vnode *vp; @@ -207,9 +210,8 @@ swapon(p, uap, retval) return (EBUSY); } sp->sw_vp = vp; - printf("adding swap: "); if (error = swfree(p, sp - swdevt)) { - printf("failed! (unchanged)\n"); + printf("swap failed! (unchanged)\n"); vrele(vp); return (error); } diff --git a/sys/vm/vm_unix.c b/sys/vm/vm_unix.c index 0a9d97c0c8..971680371d 100644 --- a/sys/vm/vm_unix.c +++ b/sys/vm/vm_unix.c @@ -35,9 +35,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * from: Utah $Hdr: vm_unix.c 1.1 89/11/07$ - * - * @(#)vm_unix.c 7.2 (Berkeley) 4/20/91 + * from: Utah $Hdr: vm_unix.c 1.1 89/11/07$ + * from: @(#)vm_unix.c 7.2 (Berkeley) 4/20/91 + * $Id$ */ /* @@ -50,12 +50,14 @@ #include "vm.h" +struct obreak_args { + char *nsiz; +}; + /* ARGSUSED */ obreak(p, uap, retval) struct proc *p; - struct args { - char *nsiz; - } *uap; + struct obreak_args *uap; int *retval; { register struct vmspace *vm = p->p_vmspace; @@ -119,12 +121,14 @@ grow(p, sp) return (1); } +struct ovadvise_args { + int anom; +}; + /* ARGSUSED */ ovadvise(p, uap, retval) struct proc *p; - struct args { - int anom; - } *uap; + struct ovadvise_args *uap; int *retval; { diff --git a/sys/vm/vm_user.c b/sys/vm/vm_user.c index 0457a66915..a624aee503 100644 --- a/sys/vm/vm_user.c +++ b/sys/vm/vm_user.c @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_user.c 7.3 (Berkeley) 4/21/91 - * - * + * from: @(#)vm_user.c 7.3 (Berkeley) 4/21/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * @@ -80,15 +82,18 @@ simple_lock_data_t vm_alloc_lock; /* XXX */ * BSD style syscall interfaces to MACH calls * All return MACH return values. */ + +struct svm_allocate_args { + vm_map_t map; + vm_offset_t *addr; + vm_size_t size; + boolean_t anywhere; +}; + /* ARGSUSED */ svm_allocate(p, uap, retval) struct proc *p; - struct args { - vm_map_t map; - vm_offset_t *addr; - vm_size_t size; - boolean_t anywhere; - } *uap; + struct svm_allocate_args *uap; int *retval; { vm_offset_t addr; @@ -107,14 +112,16 @@ svm_allocate(p, uap, retval) return((int)rv); } +struct svm_deallocate_args { + vm_map_t map; + vm_offset_t addr; + vm_size_t size; +}; + /* ARGSUSED */ svm_deallocate(p, uap, retval) struct proc *p; - struct args { - vm_map_t map; - vm_offset_t addr; - vm_size_t size; - } *uap; + struct svm_deallocate_args *uap; int *retval; { int rv; @@ -124,15 +131,17 @@ svm_deallocate(p, uap, retval) return((int)rv); } +struct svm_inherit_args { + vm_map_t map; + vm_offset_t addr; + vm_size_t size; + vm_inherit_t inherit; +}; + /* ARGSUSED */ svm_inherit(p, uap, retval) struct proc *p; - struct args { - vm_map_t map; - vm_offset_t addr; - vm_size_t size; - vm_inherit_t inherit; - } *uap; + struct svm_inherit_args *uap; int *retval; { int rv; @@ -142,16 +151,18 @@ svm_inherit(p, uap, retval) return((int)rv); } +struct svm_protect_args { + vm_map_t map; + vm_offset_t addr; + vm_size_t size; + boolean_t setmax; + vm_prot_t prot; +}; + /* ARGSUSED */ svm_protect(p, uap, retval) struct proc *p; - struct args { - vm_map_t map; - vm_offset_t addr; - vm_size_t size; - boolean_t setmax; - vm_prot_t prot; - } *uap; + struct svm_protect_args *uap; int *retval; { int rv; diff --git a/sys/vm/vm_user.h b/sys/vm/vm_user.h index be75db3668..420419e65f 100644 --- a/sys/vm/vm_user.h +++ b/sys/vm/vm_user.h @@ -33,9 +33,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vm_user.h 7.2 (Berkeley) 4/21/91 - * - * + * from: @(#)vm_user.h 7.2 (Berkeley) 4/21/91 + * $Id$ + */ + +/* * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * diff --git a/sys/vm/vnode_pager.c b/sys/vm/vnode_pager.c index 635c0fe6fb..d3fef2d725 100644 --- a/sys/vm/vnode_pager.c +++ b/sys/vm/vnode_pager.c @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vnode_pager.c 7.5 (Berkeley) 4/20/91 + * from: @(#)vnode_pager.c 7.5 (Berkeley) 4/20/91 + * $Id$ */ /* diff --git a/sys/vm/vnode_pager.h b/sys/vm/vnode_pager.h index cf24d4bda1..bea6caea59 100644 --- a/sys/vm/vnode_pager.h +++ b/sys/vm/vnode_pager.h @@ -35,7 +35,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)vnode_pager.h 7.1 (Berkeley) 12/5/90 + * from: @(#)vnode_pager.h 7.1 (Berkeley) 12/5/90 + * $Id$ */ #ifndef _VNODE_PAGER_ diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 283ca98827..f879f00eed 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -1,35 +1,19 @@ # @(#)Makefile 5.8.1.1 (Berkeley) 5/8/91 # -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 2 00124 -# -------------------- ----- ---------------------- -# -# 09 Mar 93 Rodney W. Grimes Added netstat, rwho, and ruptime -# to SUBDIR -# 04 Apr 93 Rodney W. Grimes Added 15 programs, disabled tn3270, -# enable msgs -# -SUBDIR= ar awk biff basename cal calendar \ - checknr chpass cksum cmp col colcrt colrm column comm compress \ - cpio crontab ctags cut diff diff3 dirname du egrep elvis elvisrecover \ - env error expand false fgrep file find finger fmt fold fpr from fsplit \ - fstat ftp \ - g++ gas gcc gdb grep gprof groff groups head hexdump id indent join \ - ktrace last lastcomm ld leave lex locate lock logger \ - login logname look lorder m4 machine mail make man mesg mkdep mkfifo \ +SUBDIR= ar basename bdes biff cal calendar checknr chat chpass cksum cmp col \ + colcrt colrm column comm compress crontab ctags cut dirname \ + du elvis elvisrecover env error expand false file find finger fmt \ + fold fpr from fsplit fstat ftp getopt gprof groups head hexdump id \ + indent join ktrace last lastcomm leave lex locate lock logger \ + login logname look lorder m4 machine mail make mesg mkdep mkfifo \ mkstr more msgs mt netstat nfsstat nice nm nohup pagesize \ - passwd paste pr printenv printf quota ranlib rcs \ - rdist renice rlogin rsh ruptime rwho sccs script sed \ - shar showmount size soelim sort split strings \ - strip su symorder tail talk tar tcopy tee telnet tftp time \ - tip touch tput tr true tset tsort tty ul unexpand unifdef uniq \ - unvis users uudecode uuencode vacation vgrind vis vmstat \ - w wall wc what whereis which who whoami whois \ - window write xargs xinstall xstr yacc yes + passwd paste printenv printf quota ranlib rdist ref renice rev rlogin \ + rpcgen rpcinfo rsh rup ruptime rusers rwall rwho script sed shar \ + showmount size soelim split strings strip su symorder syscons tail \ + talk tcopy tee telnet tftp time tip tn3270 touch tput tr true tset \ + tsort tty ul uname unexpand unifdef uniq unvis users uudecode uuencode \ + vacation vgrind vis vmstat w wall wc what whereis which who whoami \ + whois window write xargs xinstall xstr yacc yes - -# disabled tn3270 - .include diff --git a/usr.bin/ar/Makefile b/usr.bin/ar/Makefile index d79757d554..2860f664a0 100644 --- a/usr.bin/ar/Makefile +++ b/usr.bin/ar/Makefile @@ -4,13 +4,8 @@ PROG= ar CFLAGS+=-I${.CURDIR} SRCS= append.c ar.c archive.c contents.c delete.c extract.c misc.c \ move.c print.c replace.c -MAN1= ar.0 -CLEANFILES=ar.5.0 -ar.0: ar.5.0 - -afterinstall: - install -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} ar.5.0 \ - ${DESTDIR}${MANDIR}5/ar.0 +MAN1= ar.1 +MAN5= ar.5 .include diff --git a/usr.bin/ar/ar.5.5 b/usr.bin/ar/ar.5 similarity index 100% rename from usr.bin/ar/ar.5.5 rename to usr.bin/ar/ar.5 diff --git a/usr.bin/bdes/bdes.c b/usr.bin/bdes/bdes.c index 3d5521bca6..53080d8533 100644 --- a/usr.bin/bdes/bdes.c +++ b/usr.bin/bdes/bdes.c @@ -78,6 +78,13 @@ static char sccsid[] = "@(#)bdes.c 5.5 (Berkeley) 6/27/91"; * or the technical report for a complete reference). */ +#include +#include +#include +#include +#include +#include + #ifndef DES main() { @@ -86,13 +93,6 @@ main() } #else -#include -#include -#include -#include -#include -#include - /* * BSD and System V systems offer special library calls that do * block moves and fills, so if possible we take advantage of them diff --git a/usr.bin/chat/Example b/usr.bin/chat/Example new file mode 100644 index 0000000000..e46fbd0abf --- /dev/null +++ b/usr.bin/chat/Example @@ -0,0 +1,5 @@ +# + +../pppd/pppd -d connect 'chat "" ATDT5551212 CONNECT "" ogin: ppp' netmask 255.255.255.0 /dev/com1 38400 + +../pppd/pppd connect 'chat "" AATDT5551212 CONNECT "" ogin: ppp' netmask 255.255.255.0 /dev/com1 38400 diff --git a/usr.bin/chat/Makefile b/usr.bin/chat/Makefile new file mode 100644 index 0000000000..330a0185c0 --- /dev/null +++ b/usr.bin/chat/Makefile @@ -0,0 +1,7 @@ +# $Id$ + +PROG= chat +SRCS= chat.c +NOMAN= noman + +.include diff --git a/usr.bin/chat/README b/usr.bin/chat/README new file mode 100644 index 0000000000..73d28af1b8 --- /dev/null +++ b/usr.bin/chat/README @@ -0,0 +1,169 @@ +I run PPP between crappie.morningstar.com (137.175.6.3, my home +machine) and remora.morningstar.com (137.175.2.7, my workstation at +the office). This document describes how I use it. The installation +of PPP itself is covered in the PPP distribution. + +I put a line like this in remora's /etc/passwd: + + Pkarl:2y4613BDaQD3x:51:10:Karl's PPP login:/tmp:/usr/local/etc/pppstart + +I created a login shell script on remora called +/usr/local/etc/pppstart: + + #!/bin/sh + /usr/bin/mesg n + stty -tostop + exec /usr/local/etc/ppp 137.175.2.7: + +I use the ppp-on command to bring up a connection, and ppp-off to shut +it down. These shell scripts, plus the unlock and fix-cua scripts and +the source to the chat program are included. You will need to heavily +modify these to suit your own situation, including Internet addresses, +machine names, telephone numbers, modem dialing commands, baud rates, +login names and passwords. Make the "ppp..." command in the ppp-on +script look something like this: + + ppp 137.175.6.3: /dev/cua & + +The "137.175.6.3:" is of the format "local-addr:remote-addr" with the +remote address null (it will be negotiated by PPP). Look at the login +shell script above; it can be common to all dial-in PPP users on your +machine because it only specifies the address of the remora +(receiving) end of the link. + +If you use the enclosed chat and unlock programs, be sure they are +suid uucp, and fix-cua should be suid root. The ppp-on script should +be chmod 700, owner yourself, to keep the password (semi-) secure. + +I use the following eeprom settings and /dev and /etc/ttytab entries +in order to support dial-in and dial-out on a single phone line: + + crappie 12% eeprom | grep ttya + ttya-mode=19200,8,1,n,h + ttya-rts-dtr-off=false + ttya-ignore-cd=false + crappie 13% ls -lg /dev/cua /dev/ttya + crw-rw-rw- 1 root staff 12, 128 Nov 20 09:14 /dev/cua + crw--w--w- 1 root wheel 12, 0 Nov 20 08:25 /dev/ttya + crappie 14% grep ttya /etc/ttytab + ttya "/usr/etc/getty std.19200" unknown on + crappie 15% + +On SunOS 4.1 and later, make sure that the /etc/ttytab line for ttya +doesn't say "local": + + ttya "/usr/etc/getty std.38400" unknown on + +Make sure your modem passes data transparently; watch out especially +for ^S, ^Q, ^P (UUCP spoofing) and parity problems. I have a Telebit +Trailblazer+ attached to /dev/ttya with the following register +settings: + + aaatz + OK + aat&n + E1 F1 M1 Q6 P V1 X0 Version BA4.00 + S00=001 S01=000 S02=043 S03=013 S04=010 S05=008 S06=002 S07=060 S08=002 S09=006 + S10=007 S11=070 S12=050 + S45=000 S47=004 S48=000 S49=000 + S50=000 S51=005 S52=002 S53=003 S54=001 S55=000 S56=017 S57=019 S58=002 S59=000 + S60=000 S61=000 S62=003 S63=001 S64=000 S65=000 S66=001 S67=000 S68=255 + S90=000 S91=000 S92=001 S95=000 + S100=000 S101=000 S102=000 S104=000 + S110=001 S111=030 S112=001 + S121=000 + N0: + N1: + N2: + N3: + N4: + N5: + N6: + N7: + N8: + N9: + OK + +And, the following entry is in /etc/gettytab: + + # + # 19200/2400 dialin for Telebit Trailblazer+ modem + # + T|T19200:dial-19200:\ + :nx=T2400:sp#19200: + T2400|dial-2400:\ + :nx=T19200:sp#2400: + +My chat script dialing command looks like "ATs50=255s111=0DT4515678" +instead of just "ATDT4515678" in order to force a PEP mode connection +and to disable the UUCP spoofing (otherwise, the modem swallows or +delays ^P characters). + +I run /usr/etc/in.routed on crappie (the calling end) and have this in +my /etc/gateways file: + + net 0.0.0.0 gateway remora metric 1 passive + host crappie gateway crappie metric 0 passive + +Routed is started in /etc/rc.local. This way, I don't have to +manually add or delete routes when links come up. I ifconfig the ppp0 +interface on crappie at boot time like this (in /etc/rc.local with the +other ifconfig's): + + ifconfig ppp0 crappie remora netmask 0xffffff00 down + +I put "init ppp_attach" in my /sys/sun4c/conf/CRAPPIE file so that the +above ifconfig down will work: + + pseudo-device ppp1 init ppp_attach # Point-to-Point Protocol, 1 line + +Routed now keeps my routes sane at the crappie.MorningStar.Com end. + +My ethernet (le0) and PPP (ppp0) interfaces are configured with the +same address and netmask. IP is smart enough to figure out (via the +routes in /etc/gateways) that everything useful needs to go out ppp0. +Also, the remora end of my PPP link is configured the same way -- the +ppp0 interface there is configured with the same address and netmask +as remora's le0 ethernet. This means that separate interface names +like "remora-ppp" are not needed; point-to-point links (whether PPP, +Xerox Synchronous Point-to-Point Protocol, SLIP, IGP or whatever) have +(apparently) been used this (seemingly bizarre) way for some time. +This works because when IP looks at a POINTOPOINT link it ignores the +local address (unlike an ethernet interface) and only looks at the +remote address. + +Here's what netstat shows for me: + + crappie 109% netstat -r + Routing tables + Destination Gateway Flags Refcnt Use Interface + localhost localhost UH 0 0 lo0 + crappie crappie UH 1 11339 le0 + default remora UG 0 1266 ppp0 + mstar-net-ppp-remora crappie U 0 0 le0 + crappie 110% netstat -rn + Routing tables + Destination Gateway Flags Refcnt Use Interface + 127.0.0.1 127.0.0.1 UH 0 0 lo0 + 137.175.6.3 137.175.6.3 UH 1 11339 le0 + default 137.175.2.7 UG 0 1266 ppp0 + 137.175.6.0 137.175.6.3 U 0 0 le0 + crappie 111% + +The default route to remora is a result of the first line in the +/etc/gateways file ("default" can't be used there; you have to say +"0.0.0.0"). + +On the network at work, I add a static route in our gateway machine's +/etc/rc.local file: + + /usr/etc/route add net 137.175.6 remora 1 + +All the other machines in the office have default routes pointing at +the gateway machine, and all PPP-connected external machines are on the +137.175.6 subnet. + +Send me mail or post to the newsgroup comp.protocols.ppp if you have +any questions. + +Karl Fox diff --git a/usr.bin/chat/chat.c b/usr.bin/chat/chat.c new file mode 100644 index 0000000000..553b7d4675 --- /dev/null +++ b/usr.bin/chat/chat.c @@ -0,0 +1,856 @@ +/* + * Chat -- a program for automatic session establishment (i.e. dial + * the phone and log in). + * + * This software is in the public domain. + * + * Please send all bug reports, requests for information, etc. to: + * + * Karl Fox + * Morning Star Technologies, Inc. + * 1760 Zollinger Road + * Columbus, OH 43221 + * (614)451-1883 + */ + +static char sccs_id[] = "@(#)chat.c 1.7"; + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __386BSD__ +#define TERMIOS +#define SIGHAND_TYPE __sighandler_t +#else +#define TERMIO +#endif + +#ifdef sun +#define SIGHAND_TYPE int (*)() +# if defined(SUNOS) && SUNOS >= 41 +# ifndef HDB +# define HDB +# endif +# endif +#endif + +#ifdef TERMIO +#include +#endif +#ifdef TERMIOS +#include +#include +#endif + +#define STR_LEN 1024 + +#if defined(sun) | defined(SYSV) | defined(POSIX_SOURCE) +#define SIGTYPE void +#else +#define SIGTYPE int +#endif + +/*************** Micro getopt() *********************************************/ +#define OPTION(c,v) (_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \ + (--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\ + &&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0)) +#define OPTARG(c,v) (_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \ + (_O=4,(char*)0):(char*)0) +#define OPTONLYARG(c,v) (_O&2&&**v?(_O=1,--c,*v++):(char*)0) +#define ARG(c,v) (c?(--c,*v++):(char*)0) + +static int _O = 0; /* Internal state */ +/*************** Micro getopt() *********************************************/ + +char *program_name; + +extern char *strcpy(), *strcat(), *malloc(); +extern int strlen(); +#define copyof(s) ((s) ? strcpy(malloc(strlen(s) + 1), s) : (s)) + +#ifndef LOCK_DIR +# ifdef HDB +# define LOCK_DIR "/usr/spool/locks" +# else /* HDB */ +# define LOCK_DIR "/usr/spool/uucp" +# endif /* HDB */ +#endif /* LOCK_DIR */ + +#define MAX_ABORTS 50 +#define DEFAULT_CHAT_TIMEOUT 45 + +int verbose = 0; +int quiet = 0; +char *lock_file = (char *)0; +int timeout = DEFAULT_CHAT_TIMEOUT; + +int have_tty_parameters = 0; +#ifdef TERMIO +struct termio saved_tty_parameters; +#endif +#ifdef TERMIOS +struct termios saved_tty_parameters; +#endif + +char *abort_string[MAX_ABORTS], *fail_reason = (char *)0, + fail_buffer[50]; +int n_aborts = 0, abort_next = 0, timeout_next = 0; + +/* + * chat [ -v ] [ -t timeout ] [ -l lock-file ] \ + * [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]] + * + * Perform a UUCP-dialer-like chat script on stdin and stdout. + */ +main(argc, argv) +int argc; +char **argv; + { + int option, n; + char *arg; + + program_name = *argv; + + while (option = OPTION(argc, argv)) + switch (option) + { + case 'v': + ++verbose; + break; + + case 'l': + if (arg = OPTARG(argc, argv)) + lock_file = copyof(arg); + else + usage(); + + break; + + case 't': + if (arg = OPTARG(argc, argv)) + timeout = atoi(arg); + else + usage(); + + break; + + default: + usage(); + } + + openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2); + + if (verbose) { + setlogmask(LOG_UPTO(LOG_INFO)); + } else { + setlogmask(LOG_UPTO(LOG_WARNING)); + } + + init(); + + while (arg = ARG(argc, argv)) + { + chat_expect(arg); + + if (arg = ARG(argc, argv)) + chat_send(arg); + } + + terminate(0); + } + +/* + * We got an error parsing the command line. + */ +usage() + { + fprintf(stderr, + "Usage: %s [ -v ] [ -l lock-file ] [ -t timeout ] chat-script\n", + program_name); + exit(1); + } + +/* + * Print a warning message. + */ +/*VARARGS1*/ +warn(format, arg1, arg2, arg3, arg4) +char *format; +int arg1, arg2, arg3, arg4; + { + logf("%s: Warning: ", program_name); + logf(format, arg1, arg2, arg3, arg4); + logf("\n"); + } + +/* + * Print an error message and terminate. + */ +/*VARARGS1*/ +fatal(format, arg1, arg2, arg3, arg4) +char *format; +int arg1, arg2, arg3, arg4; + { + logf("%s: ", program_name); + logf(format, arg1, arg2, arg3, arg4); + logf("\n"); + unlock(); + terminate(1); + } + +/* + * Print an error message along with the system error message and + * terminate. + */ +/*VARARGS1*/ +sysfatal(format, arg1, arg2, arg3, arg4) +char *format; +int arg1, arg2, arg3, arg4; + { + char message[STR_LEN]; + + sprintf(message, "%s: ", program_name); + sprintf(message + strlen(message), format, arg1, arg2, arg3, arg4); + perror(message); + unlock(); + terminate(1); + } + +int alarmed = 0; + +SIGTYPE + sigalrm() +{ + int flags; + + alarm(1); alarmed = 1; /* Reset alarm to avoid race window */ + signal(SIGALRM, (SIGHAND_TYPE)sigalrm); /* that can cause hanging in read() */ + + if ((flags = fcntl(0, F_GETFL, 0)) == -1) + sysfatal("Can't get file mode flags on stdin"); + else + if (fcntl(0, F_SETFL, flags | FNDELAY) == -1) + sysfatal("Can't set file mode flags on stdin"); + + if (verbose) + { + logf("alarm\n"); + } + } + +unalarm() + { + int flags; + + if ((flags = fcntl(0, F_GETFL, 0)) == -1) + sysfatal("Can't get file mode flags on stdin"); + else + if (fcntl(0, F_SETFL, flags & ~FNDELAY) == -1) + sysfatal("Can't set file mode flags on stdin"); + } + +SIGTYPE + sigint() +{ + fatal("SIGINT"); +} + +SIGTYPE + sigterm() +{ + fatal("SIGTERM"); +} + +SIGTYPE + sighup() +{ + fatal("SIGHUP"); +} + +init() + { + signal(SIGINT, (SIGHAND_TYPE)sigint); + signal(SIGTERM, (SIGHAND_TYPE)sigterm); + signal(SIGHUP, (SIGHAND_TYPE)sighup); + + if (lock_file) + lock(); + + set_tty_parameters(); + signal(SIGALRM, (SIGHAND_TYPE)sigalrm); + alarm(0); alarmed = 0; + } + + +set_tty_parameters() + { +#ifdef TERMIO + struct termio t; + + if (ioctl(0, TCGETA, &t) < 0) + sysfatal("Can't get terminal parameters"); +#endif +#ifdef TERMIOS + struct termios t; + + if (ioctl(0, TIOCGETA, &t) < 0) + sysfatal("Can't get terminal parameters"); +#endif + + saved_tty_parameters = t; + have_tty_parameters = 1; + + t.c_iflag = IGNBRK | ISTRIP | IGNPAR; + t.c_oflag = 0; + t.c_lflag = 0; + t.c_cc[VERASE] = t.c_cc[VKILL] = 0; + t.c_cc[VMIN] = 1; + t.c_cc[VTIME] = 0; + +#ifdef TERMIO + if (ioctl(0, TCSETA, &t) < 0) + sysfatal("Can't set terminal parameters"); +#endif +#ifdef TERMIOS + if (ioctl(0, TIOCSETA, &t) < 0) + sysfatal("Can't set terminal parameters"); +#endif + } + + +terminate(status) +{ + if (have_tty_parameters && +#ifdef TERMIO + ioctl(0, TCSETA, &saved_tty_parameters) < 0 +#endif +#ifdef TERMIOS + ioctl(0, TIOCSETA, &saved_tty_parameters) < 0 +#endif + ) { + perror("Can't restore terminal parameters"); + unlock(); + exit(1); + } + exit(status); +} + +/* + * Create a lock file for the named lock device + */ +lock() + { + char hdb_lock_buffer[12]; + int fd, pid; + + lock_file = strcat(strcat(strcpy(malloc(strlen(LOCK_DIR) + + 1 + strlen(lock_file) + 1), + LOCK_DIR), "/"), lock_file); + + if ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) + { + char *s = lock_file; + + lock_file = (char *)0; /* Don't remove someone else's lock file! */ + sysfatal("Can't get lock file '%s'", s); + } + +# ifdef HDB + sprintf(hdb_lock_buffer, "%10d\n", getpid()); + write(fd, hdb_lock_buffer, 11); +# else /* HDB */ + pid = getpid(); + write(fd, &pid, sizeof pid); +# endif /* HDB */ + + close(fd); + } + +/* + * Remove our lockfile + */ +unlock() + { + if (lock_file) + { + unlink(lock_file); + lock_file = (char *)0; + } + } + +/* + * 'Clean up' this string. + */ +char *clean(s, sending) +register char *s; +int sending; + { + char temp[STR_LEN]; + register char *s1; + int add_return = sending; + + for (s1 = temp; *s; ++s) + switch (*s) + { + case '\\': + switch (*++s) + { + case '\\': + case 'd': if (sending) + *s1++ = '\\'; + + *s1++ = *s; + break; + + case 'q': quiet = ! quiet; break; + case 'r': *s1++ = '\r'; break; + case 'n': *s1++ = '\n'; break; + case 's': *s1++ = ' '; break; + + case 'c': if (sending && s[1] == '\0') + add_return = 0; + else + *s1++ = *s; + + break; + + default: *s1++ = *s; + } + + break; + + case '^': + *s1++ = (int)(*++s) & 0x1F; + break; + + default: + *s1++ = *s; + } + + if (add_return) + *s1++ = '\r'; + + *s1 = '\0'; + return (copyof(temp)); + } + +/* + * + */ +chat_expect(s) +register char *s; + { + if (strcmp(s, "ABORT") == 0) + { + ++abort_next; + return; + } + + if (strcmp(s, "TIMEOUT") == 0) + { + ++timeout_next; + return; + } + + while (*s) + { + register char *hyphen; + + for (hyphen = s; *hyphen; ++hyphen) + if (*hyphen == '-') + if (hyphen == s || hyphen[-1] != '\\') + break; + + if (*hyphen == '-') + { + *hyphen = '\0'; + + if (get_string(s)) + return; + else + { + s = hyphen + 1; + + for (hyphen = s; *hyphen; ++hyphen) + if (*hyphen == '-') + if (hyphen == s || hyphen[-1] != '\\') + break; + + if (*hyphen == '-') + { + *hyphen = '\0'; + + chat_send(s); + s = hyphen + 1; + } + else + { + chat_send(s); + return; + } + } + } + else + if (get_string(s)) + return; + else + { + if (fail_reason) + logf("Failed(%s)\n", fail_reason); + else + logf("Failed\n"); + + unlock(); + terminate(1); + } + } + } + +char *character(c) +char c; + { + static char string[10]; + char *meta; + + meta = (c & 0x80) ? "M-" : ""; + c &= 0x7F; + + if (c < 32) + sprintf(string, "%s^%c", meta, (int)c + '@'); + else + if (c == 127) + sprintf(string, "%s^?", meta); + else + sprintf(string, "%s%c", meta, c); + + return (string); + } + +/* + * + */ +chat_send(s) +register char *s; + { + if (abort_next) + { + char *s1; + + abort_next = 0; + + if (n_aborts >= MAX_ABORTS) + fatal("Too many ABORT strings"); + + s1 = clean(s, 0); + + if (strlen(s1) > strlen(s)) + fatal("Illegal ABORT string ('%s')\n", s); + + if (strlen(s1) > sizeof fail_buffer - 1) + fatal("Too long ABORT string ('%s')\n", s); + + strcpy(s, s1); + abort_string[n_aborts++] = s; + + if (verbose) + { + register char *s1 = s; + + logf("abort on ("); + + for (s1 = s; *s1; ++s1) + logf("%s", character(*s1)); + + logf(")\n"); + } + } + else + if (timeout_next) + { + timeout_next = 0; + timeout = atoi(s); + + if (timeout <= 0) + timeout = DEFAULT_CHAT_TIMEOUT; + + if (verbose) + { + logf("timeout set to %d seconds\n", timeout); + } + } + else + if ( ! put_string(s)) + { + logf("Failed\n"); + unlock(); + terminate(1); + } + } + +int get_char() + { + int status; + char c; + + status = read(0, &c, 1); + + switch (status) + { + case 1: + return ((int)c & 0x7F); + + default: + warn("read() on stdin returned %d", status); + + case -1: + if ((status = fcntl(0, F_GETFL, 0)) == -1) + sysfatal("Can't get file mode flags on stdin"); + else + if (fcntl(0, F_SETFL, status & ~FNDELAY) == -1) + sysfatal("Can't set file mode flags on stdin"); + + return (-1); + } + } + +int put_char(c) +char c; + { + int status; + + delay(); + + status = write(1, &c, 1); + + switch (status) + { + case 1: + return (0); + + default: + warn("write() on stdout returned %d", status); + + case -1: + if ((status = fcntl(0, F_GETFL, 0)) == -1) + sysfatal("Can't get file mode flags on stdin"); + else + if (fcntl(0, F_SETFL, status & ~FNDELAY) == -1) + sysfatal("Can't set file mode flags on stdin"); + + return (-1); + } + } + +int put_string(s) +register char *s; + { + s = clean(s, 1); + + if (verbose) + { + logf("send ("); + + if (quiet) + logf("??????"); + else + { + register char *s1 = s; + + for (s1 = s; *s1; ++s1) + logf("%s", character(*s1)); + } + + logf(")\n"); + } + + alarm(timeout); alarmed = 0; + + for ( ; *s; ++s) + { + register char c = *s; + + if (c == '\\') + if ((c = *++s) == '\0') + break; + else + if (c == 'd') /* \d -- Delay */ + { + sleep(2); + continue; + } + + if (alarmed || put_char(*s) < 0) + { + extern int errno; + + alarm(0); alarmed = 0; + + if (verbose) + { + if (errno == EINTR || errno == EWOULDBLOCK) + logf(" -- write timed out\n"); + else + syslog(LOG_INFO, " -- write failed: %m"); + } + + return (0); + } + } + + alarm(0); alarmed = 0; + return (1); + } + +/* + * 'Wait for' this string to appear on this file descriptor. + */ +int get_string(string) +register char *string; + { + char temp[STR_LEN]; + int c, printed = 0, len; + register char *s = temp, *end = s + STR_LEN; + + fail_reason = (char *)0; + string = clean(string, 0); + len = strlen(string); + + if (verbose) + { + register char *s1; + + logf("expect ("); + + for (s1 = string; *s1; ++s1) + logf("%s", character(*s1)); + + logf(")\n"); + } + + if (len == 0) + { + if (verbose) + { + logf("got it\n"); + } + + return (1); + } + + alarm(timeout); alarmed = 0; + + while ( ! alarmed && (c = get_char()) >= 0) + { + int n, abort_len; + + if (verbose) + { + if (c == '\n') + logf("\n"); + else + logf("%s", character(c)); + } + + *s++ = c; + + if (s >= end) + { + if (verbose) + { + logf(" -- too much data\n"); + } + + alarm(0); alarmed = 0; + return (0); + } + + if (s - temp >= len && + c == string[len - 1] && + strncmp(s - len, string, len) == 0) + { + if (verbose) + { + logf("got it\n"); + } + + alarm(0); alarmed = 0; + return (1); + } + + for (n = 0; n < n_aborts; ++n) + if (s - temp >= (abort_len = strlen(abort_string[n])) && + strncmp(s - abort_len, abort_string[n], abort_len) == 0) + { + if (verbose) + { + logf(" -- failed\n"); + } + + alarm(0); alarmed = 0; + strcpy(fail_reason = fail_buffer, abort_string[n]); + return (0); + } + + if (alarmed && verbose) + warn("Alarm synchronization problem"); + } + + alarm(0); + + if (verbose && printed) + { + extern int errno; + + if (alarmed) + logf(" -- read timed out\n"); + else + syslog(LOG_INFO, " -- read failed: %m"); + } + + alarmed = 0; + return (0); + } + +/* + * Delay an amount appropriate for between typed characters. + */ +delay() + { + register int i; + +# ifdef NO_USLEEP + for (i = 0; i < 30000; ++i) /* ... did we just say appropriate? */ + ; +# else /* NO_USLEEP */ + usleep(100); +# endif /* NO_USLEEP */ + } + +char line[256]; +char *p; + +logf(fmt, va_alist) +char *fmt; +va_dcl +{ + va_list pvar; + char buf[256]; + + va_start(pvar); + vsprintf(buf, fmt, pvar); + va_end(pvar); + + p = line + strlen(line); + strcat(p, buf); + + if (buf[strlen(buf)-1] == '\n') { + syslog(LOG_INFO, "%s", line); + line[0] = 0; + } +} diff --git a/usr.bin/chat/fix-cua b/usr.bin/chat/fix-cua new file mode 100644 index 0000000000..74f000a30f --- /dev/null +++ b/usr.bin/chat/fix-cua @@ -0,0 +1,16 @@ +#!/bin/sh + +LOCKDIR=/var/spool/lock + +case "$1" in + "") echo "Usage: fix-cua device"; exit 1 ;; +esac + +if [ -f $LOCKDIR/LCK..$1 ] +then + echo "/dev/$1 is locked" 2>&1 + exit 1 +fi + +chown root /dev/$1 +chmod 666 /dev/$1 diff --git a/usr.bin/chat/ppp-off b/usr.bin/chat/ppp-off new file mode 100644 index 0000000000..22b46f8a6e --- /dev/null +++ b/usr.bin/chat/ppp-off @@ -0,0 +1,5 @@ +#!/bin/sh + +kill -INT `ps -ax | egrep " ppp " | egrep -v "egrep" | sed 's/^\([ 0-9]*\) .*/\1'/` + +exit 0 diff --git a/usr.bin/chat/ppp-on b/usr.bin/chat/ppp-on new file mode 100644 index 0000000000..305f2b00aa --- /dev/null +++ b/usr.bin/chat/ppp-on @@ -0,0 +1,37 @@ +#!/bin/sh + +# +# ppp-on +# +# Set up a PPP link +# + +LOCKDIR=/var/spool/lock +DEVICE=com1 + +PHONE=4511234 +USER=Pkarl +PASSWORD=password +OUR_IP_ADDR=137.175.6.3 + +if [ -f $LOCKDIR/LCK..$DEVICE ] +then + echo "PPP device is locked" + exit 1 +fi + +fix-cua $DEVICE + +( + stty 19200 -tostop + + if chat -l LCK..$DEVICE ABORT "NO CARRIER" ABORT BUSY "" ATZ OK ATs50=255s111=0DT$PHONE CONNECT "" ogin: $USER ssword: \\q$PASSWORD + then + ppp mru 1500 $OUR_IP_ADDR: /dev/$DEVICE & + sleep 10 + exit 0 + else + echo "PPP call failed" 1>&2 + exit 1 + fi +) < /dev/$DEVICE > /dev/$DEVICE diff --git a/usr.bin/chat/unlock b/usr.bin/chat/unlock new file mode 100644 index 0000000000..978bc6e19c --- /dev/null +++ b/usr.bin/chat/unlock @@ -0,0 +1,23 @@ +#!/bin/sh + +LOCKDIR=/var/spool/lock + +case "$1" in + "") echo "Usage: unlock lockfile"; exit 1 ;; + .*) echo "Usage: unlock lockfile"; exit 1 ;; +esac + +if [ -f $LOCKDIR/$1 ] +then + if [ `wc -c < $LOCKDIR/$1` -eq 4 ] + then + rm -f $LOCKDIR/$1 + exit 0 + else + echo "Usage: unlock lockfile" + exit 1 + fi +else + echo "lockfile" $LOCKDIR/$1 "does not exist" + exit 1 +fi diff --git a/usr.bin/checknr/checknr.1 b/usr.bin/checknr/checknr.1 index 1f6a6604e0..4463003057 100644 --- a/usr.bin/checknr/checknr.1 +++ b/usr.bin/checknr/checknr.1 @@ -77,7 +77,7 @@ as undefined. Request .Nm checknr to ignore -.Ql\ef +.Ql \ef font changes. .It Fl s Ignore diff --git a/usr.bin/cksum/Makefile b/usr.bin/cksum/Makefile index 5dfecd573f..d2f09b7b4a 100644 --- a/usr.bin/cksum/Makefile +++ b/usr.bin/cksum/Makefile @@ -1,4 +1,4 @@ -# @(#)Makefile 5.3 (Berkeley) 4/4/91 +# @(#)Makefile 8.1 (Berkeley) 6/6/93 PROG= cksum SRCS= cksum.c crc.c print.c sum1.c sum2.c diff --git a/usr.bin/cksum/cksum.1 b/usr.bin/cksum/cksum.1 index 448a7cfd39..7c2cc24592 100644 --- a/usr.bin/cksum/cksum.1 +++ b/usr.bin/cksum/cksum.1 @@ -1,5 +1,5 @@ -.\" Copyright (c) 1991, 1991 The Regents of the University of California. -.\" All rights reserved. +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. .\" .\" This code is derived from software contributed to Berkeley by .\" the Institute of Electrical and Electronics Engineers, Inc. @@ -32,11 +32,11 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)cksum.1 5.5 (Berkeley) 6/27/91 +.\" @(#)cksum.1 8.1 (Berkeley) 6/29/93 .\" -.Dd June 27, 1991 +.Dd June 29, 1993 .Dt CKSUM 1 -.Os +.Os BSD 4.4 .Sh NAME .Nm cksum .Nd display file checksums and block counts @@ -47,12 +47,13 @@ .Sh DESCRIPTION The .Nm cksum -utility writes to standard output three whitespace separated fields for each -input file (or the standard input by default). +utility writes to the standard output three whitespace separated +fields for each input file. These fields are a checksum .Tn CRC , -the total number of bytes in the file and -the file name. +the total number of octets in the file and the file name. +If no file name is specified, the standard input is used and no file name +is written. .Pp The options are as follows: .Bl -tag -width indent @@ -86,7 +87,7 @@ r = s % 2^16 + (s % 2^32) / 2^16; cksum = (r % 2^16) + r / 2^16; .Ed .Pp -Both algorithm 1 and 2 write to standard output the same fields as +Both algorithm 1 and 2 write to the standard output the same fields as the default algorithm except that the size of the file in bytes is replaced with the size of the file in blocks. For historic reasons, the block size is 1024 for algorithm 1 and 512 @@ -117,30 +118,35 @@ the following procedure: .Bd -filled -offset indent The .Ar n -bits of the file are considered to be the coefficients of a mod 2 +bits to be evaluated are considered to be the coefficients of a mod 2 polynomial M(x) of degree .Ar n Ns \-1 . +These +.Ar n +bits are the bits from the file, with the most significant bit being the most +significant bit of the first octet of the file and the last bit being the least +significant bit of the last octet, padded with zero bits (if necessary) to +achieve an integral number of octets, followed by one or more octets +representing the length of the file as a binary value, least significant octet +first. +The smallest number of octets capable of representing this integer are used. .Pp M(x) is multiplied by x^32 (i.e., shifted left 32 bits) and divided by G(x) using mod 2 division, producing a remainder R(x) of degree <= 31. -During the division, each time the intermediate remainder is zero, it -is changed to the next value from a predefined sequence of -32-bit integers before completing the division. -This sequence is long and complex -- see the source code for more -information. .Pp The coefficients of R(x) are considered to be a 32-bit sequence. -.Ed .Pp -The calculation used is identical to that given in pseudo-code in -the -.Tn ACM -article referenced below. +The bit sequence is complemented and the result is the CRC. +.Ed .Pp The .Nm cksum utility exits 0 on success, and >0 if an error occurs. .Sh SEE ALSO +The default calculation is identical to that given in pseudo-code +in the following +.Tn ACM +article. .Rs .%T "Computation of Cyclic Redundancy Checks Via Table Lookup" .%A Dilip V. Sarwate @@ -148,11 +154,11 @@ utility exits 0 on success, and >0 if an error occurs. .%D "August 1988" .Re .Sh STANDARDS -.Nm Cksum -is expected to conform to -.St -p1003.2 . +The +.Nm cksum +utility is expected to be POSIX 1003.2 compatible. .Sh HISTORY The .Nm cksum -utility is -.Ud . +utility appears in +.Bx 4.4 . diff --git a/usr.bin/cksum/cksum.c b/usr.bin/cksum/cksum.c index c189d935c0..3e66ca5841 100644 --- a/usr.bin/cksum/cksum.c +++ b/usr.bin/cksum/cksum.c @@ -1,9 +1,9 @@ /*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by - * James W. Williams of the University of Maryland. + * James W. Williams of NASA Goddard Space Flight Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,13 +35,13 @@ */ #ifndef lint -char copyright[] = -"@(#) Copyright (c) 1991 The Regents of the University of California.\n\ - All rights reserved.\n"; +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)cksum.c 5.3 (Berkeley) 4/4/91"; +static char sccsid[] = "@(#)cksum.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #include @@ -54,6 +54,9 @@ static char sccsid[] = "@(#)cksum.c 5.3 (Berkeley) 4/4/91"; #include #include "extern.h" +void usage __P((void)); + +int main(argc, argv) int argc; char **argv; @@ -90,21 +93,21 @@ main(argc, argv) argv += optind; fd = STDIN_FILENO; - fn = "stdin"; + fn = NULL; rval = 0; do { if (*argv) { fn = *argv++; if ((fd = open(fn, O_RDONLY, 0)) < 0) { - (void)fprintf(stderr, - "cksum: %s: %s\n", fn, strerror(errno)); + (void)fprintf(stderr, "cksum: %s: %s\n", + fn, strerror(errno)); rval = 1; continue; } } if (cfncn(fd, &val, &len)) { - (void)fprintf(stderr, - "cksum: %s: %s\n", fn, strerror(errno)); + (void)fprintf(stderr, "cksum: %s: %s\n", + fn ? fn : "stdin", strerror(errno)); rval = 1; } else pfncn(fn, val, len); @@ -113,6 +116,7 @@ main(argc, argv) exit(rval); } +void usage() { (void)fprintf(stderr, "usage: cksum [-o 1 | 2] [file ...]\n"); diff --git a/usr.bin/cksum/crc.c b/usr.bin/cksum/crc.c index 3662eba949..2a5eb6dba7 100644 --- a/usr.bin/cksum/crc.c +++ b/usr.bin/cksum/crc.c @@ -1,9 +1,9 @@ /*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by - * James W. Williams of the University of Maryland. + * James W. Williams of NASA Goddard Space Flight Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,98 +35,106 @@ */ #ifndef lint -static char sccsid[] = "@(#)crc.c 5.2 (Berkeley) 4/4/91"; +static char sccsid[] = "@(#)crc.c 8.1 (Berkeley) 6/17/93"; #endif /* not lint */ #include #include -u_long crctab[] = { - 0x7fffffff, - 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, - 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, - 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, - 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, - 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, - 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, - 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, - 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, - 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, - 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, - 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, - 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, - 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, - 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, - 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, - 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, - 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, - 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, - 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, - 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, - 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, - 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, - 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, - 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, - 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, - 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, - 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, - 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, - 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, - 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, - 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, - 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, - 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, - 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, - 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, - 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, - 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, - 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +static u_long crctab[] = { + 0x0, + 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, + 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, + 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, + 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, + 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, + 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, + 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, + 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, + 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, + 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, + 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, + 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, + 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, + 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, + 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, + 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, + 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, + 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, + 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, + 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, + 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, + 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, + 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, + 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, + 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, + 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, + 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, + 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, + 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, + 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, + 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, + 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, + 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, + 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 }; /* - * crc -- - * Compute a POSIX.2 checksum. This routine has been broken out since - * it is anticipated that other programs will use it. It takes a file - * descriptor to read from and locations to store the crc and the number - * of bytes read. It returns 0 on success and 1 on failure. Errno is - * set on failure. + * Compute a POSIX 1003.2 checksum. This routine has been broken out so that + * other programs can use it. It takes a file descriptor to read from and + * locations to store the crc and the number of bytes read. It returns 0 on + * success and 1 on failure. Errno is set on failure. */ +u_long crc_total = ~0; /* The crc over a number of files. */ + +int crc(fd, cval, clen) register int fd; u_long *cval, *clen; { - register int i, nr, step; register u_char *p; - register u_long crc, total; - u_char buf[8192]; + register int nr; + register u_long crc, len; + u_char buf[16 * 1024]; + +#define COMPUTE(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)] - crc = step = total = 0; + crc = len = 0; + crc_total = ~crc_total; while ((nr = read(fd, buf, sizeof(buf))) > 0) - for (total += nr, p = buf; nr--; ++p) { - if (!(i = crc >> 24 ^ *p)) { - i = step++; - if (step >= sizeof(crctab)/sizeof(crctab[0])) - step = 0; - } - crc = (crc << 8) ^ crctab[i]; + for (len += nr, p = buf; nr--; ++p) { + COMPUTE(crc, *p); + COMPUTE(crc_total, *p); } if (nr < 0) - return(1); + return (1); + + *clen = len; + + /* Include the length of the file. */ + for (; len != 0; len >>= 8) { + COMPUTE(crc, len & 0xff); + COMPUTE(crc_total, len & 0xff); + } - *cval = crc & 0xffffffff; /* Mask to 32 bits. */ - *clen = total; - return(0); + *cval = ~crc; + crc_total = ~crc_total; + return (0); } diff --git a/usr.bin/cksum/extern.h b/usr.bin/cksum/extern.h index 4555fe032e..ed6112046e 100644 --- a/usr.bin/cksum/extern.h +++ b/usr.bin/cksum/extern.h @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)extern.h 5.1 (Berkeley) 4/4/91 + * @(#)extern.h 8.1 (Berkeley) 6/6/93 */ #include diff --git a/usr.bin/cksum/print.c b/usr.bin/cksum/print.c index 62779d1ee7..d0ffeb3030 100644 --- a/usr.bin/cksum/print.c +++ b/usr.bin/cksum/print.c @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,7 +32,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)print.c 5.1 (Berkeley) 4/4/91"; +static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #include @@ -44,7 +44,10 @@ pcrc(fn, val, len) char *fn; u_long val, len; { - (void)printf("%lu %lu %s\n", val, len, fn); + (void)printf("%lu %lu", val, len); + if (fn) + (void)printf(" %s", fn); + (void)printf("\n"); } void @@ -52,7 +55,10 @@ psum1(fn, val, len) char *fn; u_long val, len; { - (void)printf("%lu %lu %s\n", val, (len + 1023) / 1024, fn); + (void)printf("%lu %lu", val, (len + 1023) / 1024); + if (fn) + (void)printf(" %s", fn); + (void)printf("\n"); } void @@ -60,5 +66,8 @@ psum2(fn, val, len) char *fn; u_long val, len; { - (void)printf("%lu %lu %s\n", val, (len + 511) / 512, fn); + (void)printf("%lu %lu", val, (len + 511) / 512); + if (fn) + (void)printf(" %s", fn); + (void)printf("\n"); } diff --git a/usr.bin/cksum/sum1.c b/usr.bin/cksum/sum1.c index 6bf9cc3417..19eeebeeef 100644 --- a/usr.bin/cksum/sum1.c +++ b/usr.bin/cksum/sum1.c @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,12 +32,13 @@ */ #ifndef lint -static char sccsid[] = "@(#)sum1.c 5.1 (Berkeley) 4/4/91"; +static char sccsid[] = "@(#)sum1.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #include #include +int csum1(fd, cval, clen) register int fd; u_long *cval, *clen; diff --git a/usr.bin/cksum/sum2.c b/usr.bin/cksum/sum2.c index 730359abf1..c54d88e12b 100644 --- a/usr.bin/cksum/sum2.c +++ b/usr.bin/cksum/sum2.c @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,12 +32,13 @@ */ #ifndef lint -static char sccsid[] = "@(#)sum2.c 5.1 (Berkeley) 4/4/91"; +static char sccsid[] = "@(#)sum2.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #include #include +int csum2(fd, cval, clen) register int fd; u_long *cval, *clen; diff --git a/usr.bin/col/col.c b/usr.bin/col/col.c index 375bb6cd14..709bfd4cf9 100644 --- a/usr.bin/col/col.c +++ b/usr.bin/col/col.c @@ -101,6 +101,9 @@ int no_backspaces; /* if not to output any backspaces */ if (putchar(ch) == EOF) \ wrerr(); +/* next tabstop after col */ +#define TABSTOP(col,ts) ((col) + (ts) - (col) % (ts)) + main(argc, argv) int argc; char **argv; @@ -424,10 +427,17 @@ flush_line(l) if (compress_spaces && nspace > 1) { int ntabs; + while ((ntabs = TABSTOP(last_col, 8)) <= this_col) { + PUTC('\t'); + last_col = ntabs; + } + nspace = this_col - last_col; +/* ntabs = this_col / 8 - last_col / 8; nspace -= ntabs * 8; while (--ntabs >= 0) PUTC('\t'); +*/ } while (--nspace >= 0) PUTC(' '); diff --git a/usr.bin/compress/Makefile b/usr.bin/compress/Makefile index d801ae2b5c..2ff443672a 100644 --- a/usr.bin/compress/Makefile +++ b/usr.bin/compress/Makefile @@ -6,8 +6,7 @@ USERMEM=4194304 PROG= compress CFLAGS+=-DBSD4_2 -DSACREDMEM=256000 -DUSERMEM=${USERMEM} -LINKS= ${BINDIR}/compress ${BINDIR}/uncompress \ - ${BINDIR}/compress ${BINDIR}/zcat -MLINKS= compress.1 uncompress.1 compress.1 zcat.1 +LINKS= ${BINDIR}/compress ${BINDIR}/uncompress +MLINKS= compress.1 uncompress.1 .include diff --git a/usr.bin/crontab/Makefile b/usr.bin/crontab/Makefile index 20e43fe7b9..6457796020 100644 --- a/usr.bin/crontab/Makefile +++ b/usr.bin/crontab/Makefile @@ -12,14 +12,8 @@ SRCS= crontab.c misc.c entry.c env.c CFLAGS+=-I${.CURDIR} -I${.CURDIR}/../../libexec/crond -DDEBUGGING=1 -DBSD -DCRONDIR='"/var/cron"' -fstrength-reduce BINOWN =root BINMODE=4111 -MAN1= crontab.0 -CLEANFILES=crontab.5.0 - -crontab.0: crontab.5.0 - -afterinstall: - install -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} crontab.5.0 \ - ${DESTDIR}${MANDIR}5/crontab.0 +MAN1= crontab.1 +MAN5= crontab.5 .include .PATH: ${.CURDIR}/../../libexec/crond diff --git a/usr.bin/crontab/crontab.5 b/usr.bin/crontab/crontab.5 new file mode 100644 index 0000000000..d7cb2d9d73 --- /dev/null +++ b/usr.bin/crontab/crontab.5 @@ -0,0 +1,188 @@ +.\" $Header: /home/cvs/386BSD/src/usr.bin/crontab/crontab.5.5,v 1.1.1.1 1993/06/12 14:53:53 rgrimes Exp $ +.\" +.\"/* Copyright 1988,1990 by Paul Vixie +.\" * All rights reserved +.\" * +.\" * Distribute freely, except: don't remove my name from the source or +.\" * documentation (don't take credit for my work), mark your changes (don't +.\" * get me blamed for your possible bugs), don't alter or remove this +.\" * notice. May be sold if buildable source is provided to buyer. No +.\" * warrantee of any kind, express or implied, is included with this +.\" * software; use at your own risk, responsibility for damages (if any) to +.\" * anyone resulting from the use of this software rests entirely with the +.\" * user. +.\" * +.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and +.\" * I'll try to keep a version up to date. I can be reached as follows: +.\" * Paul Vixie, 329 Noe Street, San Francisco, CA, 94114, (415) 864-7013, +.\" * paul@vixie.sf.ca.us || {hoptoad,pacbell,decwrl,crash}!vixie!paul +.\" */ +.TH CRONTAB 5 "15 January 1990" +.UC 4 +.SH NAME +crontab \- tables for driving cron +.SH DESCRIPTION +A +.I crontab +file contains instructions to the +.IR crond (8) +daemon of the general form: ``run this command at this time on this date''. +Each user has their own crontab, and commands in any given crontab will be +executed as the user who owns the crontab. Uucp and News will usually have +their own crontabs, eliminating the need for explicitly running +.IR su (1) +as part of a cron command. +.PP +Blank lines and leading spaces and tabs are ignored. Lines whose first +non-space character is a pound-sign (#) are comments, and are ignored. +Note that comments are not allowed on the same line as cron commands, since +they will be taken to be part of the command. Similarly, comments are not +allowed on the same line as environment variable settings. +.PP +An active line in a crontab will be either an environment setting or a cron +command. An environment setting is of the form, +.PP + name = value +.PP +where the spaces around the equal-sign (=) are optional, and any subsequent +non-leading spaces in +.I value +will be part of the value assigned to +.IR name . +The +.I value +string may be placed in quotes (single or double, but matching) to preserve +leading or trailing blanks. +.PP +Several environment variables are set up +automatically by the +.IR crond (8) +daemon from the /etc/passwd line of the crontab's owner: USER, HOME, and SHELL. +HOME and SHELL may be overridden by settings in the crontab; USER may not. +.PP +(Note: for UUCP, always set SHELL=/bin/sh, or +.IR crond (8) +will cheerfully try to execute your commands using /usr/lib/uucp/uucico.) +.PP +(Another note: the USER variable is sometimes called LOGNAME or worse on +System V... on these systems, LOGNAME will be set rather than USER.) +.PP +In addition to USER, HOME, and SHELL, +.IR crond (8) +will look at MAILTO if it has any reason to send mail as a result of running +commands in ``this'' crontab. If MAILTO is defined (and non-empty), mail is +sent to the user so named. If MAILTO is defined but empty (MAILTO=""), no +mail will be sent. Otherwise mail is sent to the owner of the crontab. This +option is useful if you decide on /bin/mail instead of /usr/lib/sendmail as +your mailer when you install cron -- /bin/mail doesn't do aliasing, and UUCP +usually doesn't read its mail. +.PP +The format of a cron command is very much the V7 standard, with a number of +upward-compatible extensions. Each line has five time and date fields, +followed by a command. Commands are executed by +.IR crond (8) +when the minute, hour, and month of year fields match the current time, +.I and +when at least one of the two day fields (day of month, or day of week) +match the current time (see ``Note'' below). +.IR crond (8) +examines cron entries once every minute. +The time and date fields are: +.IP +.ta 1.5i +field allowed values +.br +----- -------------- +.br +minute 0-59 +.br +hour 0-23 +.br +day of month 0-31 +.br +month 0-12 (or names, see below) +.br +day of week 0-7 (0 or 7 is Sun, or use names) +.br +.PP +A field may be an asterisk (*), which always matches the +current time. +.PP +Ranges of numbers are allowed. Ranges are two numbers separated +with a hyphen. The specified range is inclusive. For example, +8-11 for an ``hours'' entry specifies execution at hours 8, 9, 10 +and 11. +.PP +Lists are allowed. A list is a set of numbers (or ranges) +separated by commas. Examples: ``1,2,5,9'', ``0-4,8-12''. +.PP +Step values can be used in conjunction with ranges. Following +a range with ``/'' specifies skips of the number's value +through the range. For example, ``0-23/2'' can be used in the hours +field to specify command execution every other hour (the alternative +in the V7 standard is ``0,2,4,6,8,10,12,14,16,18,20,22''). +.PP +Names can also be used for the ``month'' and ``day of week'' +fields. Use the first three letters of the particular +day or month (case doesn't matter). Ranges or +lists of names are not allowed. +.PP +The ``sixth'' field (the rest of the line) specifies the command to be +run. +The entire command portion of the line, up to a newline or % +character, will be executed by the user's login shell or by the shell +specified in the SHELL variable of the cronfile. +Percent-signs (%) in the command, unless escaped with backslash +(\\), will be changed into newline characters, and all data +after the first % will be sent to the command as standard +input. +.PP +Note: The day of a command's execution can be specified by two +fields \(em day of month, and day of week. If both fields are +restricted (ie, aren't *), the command will be run when +.I either +field matches the current time. For example, +.br +``30 4 1,15 * 5'' +would cause a command to be run at 4:30 am on the 1st and 15th of each +month, plus every Friday. +.SH EXAMPLE CRON FILE +.nf + +# use /bin/sh to run commands, no matter what /etc/passwd says +SHELL=/bin/sh +# mail any output to `paul', no matter whose crontab this is +MAILTO=paul +# +# run five minutes after midnight, every day +5 0 * * * $HOME/bin/daily.job >> $HOME/tmp/out 2>&1 +# run at 2:15pm on the first of every month -- output mailed to paul +15 14 1 * * $HOME/bin/monthly +# run at 10 pm on weekdays, annoy Joe +0 22 * * 1-5 mail -s "It's 10pm" joe%Joe,%%Where are your kids?% +23 0-23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday" +5 4 * * sun echo "run at 5 after 4 every sunday" +.fi +.SH SEE ALSO +crond(8), crontab(1) +.SH EXTENSIONS +When specifying day of week, both day 0 and day 7 will be considered Sunday. +BSD and ATT seem to disagree about this. +.PP +Lists and ranges are allowed to co-exist in the same field. "1-3,7-9" would +be rejected by ATT or BSD cron -- they want to see "1-3" or "7,8,9" ONLY. +.PP +Ranges can include "steps", so "1-9/2" is the same as "1,3,5,7,9". +.PP +Names of months or days of the week can be specified by name. +.PP +Environment variables can be set in the crontab. In BSD or ATT, the +environment handed to child processes is basically the one from /etc/rc. +.PP +Command output is mailed to the crontab owner (BSD can't do this), can be +mailed to a person other than the crontab owner (SysV can't do this), or the +feature can be turned off and no mail will be sent at all (SysV can't do this +either). +.SH AUTHOR +.nf +Paul Vixie, paul@vixie.sf.ca.us diff --git a/usr.bin/crontab/crontab.5.5 b/usr.bin/crontab/crontab.5.5 deleted file mode 100644 index fb981eaf88..0000000000 --- a/usr.bin/crontab/crontab.5.5 +++ /dev/null @@ -1,188 +0,0 @@ -.\" $Header: crontab.5,v 2.1 90/07/18 00:23:50 vixie Exp $ -.\" -.\"/* Copyright 1988,1990 by Paul Vixie -.\" * All rights reserved -.\" * -.\" * Distribute freely, except: don't remove my name from the source or -.\" * documentation (don't take credit for my work), mark your changes (don't -.\" * get me blamed for your possible bugs), don't alter or remove this -.\" * notice. May be sold if buildable source is provided to buyer. No -.\" * warrantee of any kind, express or implied, is included with this -.\" * software; use at your own risk, responsibility for damages (if any) to -.\" * anyone resulting from the use of this software rests entirely with the -.\" * user. -.\" * -.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and -.\" * I'll try to keep a version up to date. I can be reached as follows: -.\" * Paul Vixie, 329 Noe Street, San Francisco, CA, 94114, (415) 864-7013, -.\" * paul@vixie.sf.ca.us || {hoptoad,pacbell,decwrl,crash}!vixie!paul -.\" */ -.TH CRONTAB 5 "15 January 1990" -.UC 4 -.SH NAME -crontab \- tables for driving cron -.SH DESCRIPTION -A -.I crontab -file contains instructions to the -.IR crond (8) -daemon of the general form: ``run this command at this time on this date''. -Each user has their own crontab, and commands in any given crontab will be -executed as the user who owns the crontab. Uucp and News will usually have -their own crontabs, eliminating the need for explicitly running -.IR su (1) -as part of a cron command. -.PP -Blank lines and leading spaces and tabs are ignored. Lines whose first -non-space character is a pound-sign (#) are comments, and are ignored. -Note that comments are not allowed on the same line as cron commands, since -they will be taken to be part of the command. Similarly, comments are not -allowed on the same line as environment variable settings. -.PP -An active line in a crontab will be either an environment setting or a cron -command. An environment setting is of the form, -.PP - name = value -.PP -where the spaces around the equal-sign (=) are optional, and any subsequent -non-leading spaces in -.I value -will be part of the value assigned to -.IR name . -The -.I value -string may be placed in quotes (single or double, but matching) to preserve -leading or trailing blanks. -.PP -Several environment variables are set up -automatically by the -.IR crond (8) -daemon from the /etc/passwd line of the crontab's owner: USER, HOME, and SHELL. -HOME and SHELL may be overridden by settings in the crontab; USER may not. -.PP -(Note: for UUCP, always set SHELL=/bin/sh, or -.IR crond (8) -will cheerfully try to execute your commands using /usr/lib/uucp/uucico.) -.PP -(Another note: the USER variable is sometimes called LOGNAME or worse on -System V... on these systems, LOGNAME will be set rather than USER.) -.PP -In addition to USER, HOME, and SHELL, -.IR crond (8) -will look at MAILTO if it has any reason to send mail as a result of running -commands in ``this'' crontab. If MAILTO is defined (and non-empty), mail is -sent to the user so named. If MAILTO is defined but empty (MAILTO=""), no -mail will be sent. Otherwise mail is sent to the owner of the crontab. This -option is useful if you decide on /bin/mail instead of /usr/lib/sendmail as -your mailer when you install cron -- /bin/mail doesn't do aliasing, and UUCP -usually doesn't read its mail. -.PP -The format of a cron command is very much the V7 standard, with a number of -upward-compatible extensions. Each line has five time and date fields, -followed by a command. Commands are executed by -.IR crond (8) -when the minute, hour, and month of year fields match the current time, -.I and -when at least one of the two day fields (day of month, or day of week) -match the current time (see ``Note'' below). -.IR crond (8) -examines cron entries once every minute. -The time and date fields are: -.IP -.ta 1.5i -field allowed values -.br ------ -------------- -.br -minute 0-59 -.br -hour 0-23 -.br -day of month 0-31 -.br -month 0-12 (or names, see below) -.br -day of week 0-7 (0 or 7 is Sun, or use names) -.br -.PP -A field may be an asterisk (*), which always matches the -current time. -.PP -Ranges of numbers are allowed. Ranges are two numbers separated -with a hyphen. The specified range is inclusive. For example, -8-11 for an ``hours'' entry specifies execution at hours 8, 9, 10 -and 11. -.PP -Lists are allowed. A list is a set of numbers (or ranges) -separated by commas. Examples: ``1,2,5,9'', ``0-4,8-12''. -.PP -Step values can be used in conjunction with ranges. Following -a range with ``/'' specifies skips of the number's value -through the range. For example, ``0-23/2'' can be used in the hours -field to specify command execution every other hour (the alternative -in the V7 standard is ``0,2,4,6,8,10,12,14,16,18,20,22''). -.PP -Names can also be used for the ``month'' and ``day of week'' -fields. Use the first three letters of the particular -day or month (case doesn't matter). Ranges or -lists of names are not allowed. -.PP -The ``sixth'' field (the rest of the line) specifies the command to be -run. -The entire command portion of the line, up to a newline or % -character, will be executed by the user's login shell or by the shell -specified in the SHELL variable of the cronfile. -Percent-signs (%) in the command, unless escaped with backslash -(\\), will be changed into newline characters, and all data -after the first % will be sent to the command as standard -input. -.PP -Note: The day of a command's execution can be specified by two -fields \(em day of month, and day of week. If both fields are -restricted (ie, aren't *), the command will be run when -.I either -field matches the current time. For example, -.br -``30 4 1,15 * 5'' -would cause a command to be run at 4:30 am on the 1st and 15th of each -month, plus every Friday. -.SH EXAMPLE CRON FILE -.nf - -# use /bin/sh to run commands, no matter what /etc/passwd says -SHELL=/bin/sh -# mail any output to `paul', no matter whose crontab this is -MAILTO=paul -# -# run five minutes after midnight, every day -5 0 * * * $HOME/bin/daily.job >> $HOME/tmp/out 2>&1 -# run at 2:15pm on the first of every month -- output mailed to paul -15 14 1 * * $HOME/bin/monthly -# run at 10 pm on weekdays, annoy Joe -0 22 * * 1-5 mail -s "It's 10pm" joe%Joe,%%Where are your kids?% -23 0-23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday" -5 4 * * sun echo "run at 5 after 4 every sunday" -.fi -.SH SEE ALSO -crond(8), crontab(1) -.SH EXTENSIONS -When specifying day of week, both day 0 and day 7 will be considered Sunday. -BSD and ATT seem to disagree about this. -.PP -Lists and ranges are allowed to co-exist in the same field. "1-3,7-9" would -be rejected by ATT or BSD cron -- they want to see "1-3" or "7,8,9" ONLY. -.PP -Ranges can include "steps", so "1-9/2" is the same as "1,3,5,7,9". -.PP -Names of months or days of the week can be specified by name. -.PP -Environment variables can be set in the crontab. In BSD or ATT, the -environment handed to child processes is basically the one from /etc/rc. -.PP -Command output is mailed to the crontab owner (BSD can't do this), can be -mailed to a person other than the crontab owner (SysV can't do this), or the -feature can be turned off and no mail will be sent at all (SysV can't do this -either). -.SH AUTHOR -.nf -Paul Vixie, paul@vixie.sf.ca.us diff --git a/usr.bin/du/Makefile b/usr.bin/du/Makefile index ecf0e92547..6b83bae910 100644 --- a/usr.bin/du/Makefile +++ b/usr.bin/du/Makefile @@ -1,5 +1,7 @@ -# @(#)Makefile 5.3 (Berkeley) 5/11/90 +# @(#)Makefile 5.4 (Berkeley) 3/1/92 PROG= du +SRCS= du.c getbsize.c +.PATH: ${.CURDIR}/../../bin/df .include diff --git a/usr.bin/du/du.1 b/usr.bin/du/du.1 index 07c754b208..1c9b042025 100644 --- a/usr.bin/du/du.1 +++ b/usr.bin/du/du.1 @@ -29,11 +29,11 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)du.1 6.13 (Berkeley) 6/20/91 +.\" @(#)du.1 6.15 (Berkeley) 3/1/92 .\" -.Dd June 20, 1991 +.Dd March 1, 1992 .Dt DU 1 -.Os BSD 4.4 +.Os .Sh NAME .Nm du .Nd Display disk usage statistics @@ -45,29 +45,28 @@ .Sh DESCRIPTION The .Nm du -utility displays the file system block usage for each file argument -and for each directory in the file hierarchy rooted in each directory +utility displays the file system block usage for each file +argument and for each directory in the file hierarchy rooted in each directory argument. If no file is specified, the block usage of the hierarchy rooted in the current directory is displayed. +The number of blocks are in the same units as that returned by the +.Xr stat 2 +system call, i.e. 512-byte blocks, unless the +.Fl k +option is specified, or the +.Ev BLOCKSIZE +environment variable is set (see below). +Partial numbers of blocks are rounded up. .Pp The options are as follows: .Bl -tag -width Ds .It Fl a Display an entry for each file in the file hierarchy. -.It Fl k -By default, -.Nm du -displays the number of blocks as returned by the -.Xr stat 2 -system call, i.e. 512-byte blocks. -If the -.Fl k -flag is specified, the number displayed is the number of 1024-byte -blocks. -Partial numbers of blocks are rounded up. .It Fl s Display only the grand total for the specified files. +.It Fl k +The block counts are forced to be in 1K (1024 8-bit bytes) size blocks. .It Fl x Filesystem mount points are not traversed. .El @@ -81,11 +80,18 @@ Files having multiple hard links are counted (and displayed) a single time per .Nm du execution. +.Sh ENVIRONMENTAL VARIABLES +.Bl -tag -width BLOCKSIZE +.It Ev BLOCKSIZE +If the environmental variable +.Ev BLOCKSIZE +is set, the block counts will be displayed in units of that size block. +.El .Sh SEE ALSO .Xr df 1 , .Xr quot 8 .Sh HISTORY A -.Nm +.Nm du command appeared in .At v6 . diff --git a/usr.bin/du/du.c b/usr.bin/du/du.c index fd3babfcc0..ac543e2f4d 100644 --- a/usr.bin/du/du.c +++ b/usr.bin/du/du.c @@ -41,7 +41,7 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)du.c 5.12 (Berkeley) 6/20/91"; +static char sccsid[] = "@(#)du.c 5.17 (Berkeley) 5/20/92"; #endif /* not lint */ #include @@ -53,28 +53,34 @@ static char sccsid[] = "@(#)du.c 5.12 (Berkeley) 6/20/91"; #include #include +void err __P((const char *, ...)); +char *getbsize __P((char *, int *, long *)); +int linkchk __P((FTSENT *)); +void usage __P((void)); + +int kflag; + main(argc, argv) int argc; - char **argv; + char *argv[]; { - extern int optind; register FTS *fts; register FTSENT *p; - register int kvalue, listdirs, listfiles; - int aflag, ch, ftsoptions, sflag; + register int listdirs, listfiles; + long blocksize; + int aflag, ch, ftsoptions, notused, sflag; char **save; ftsoptions = FTS_PHYSICAL; - kvalue = 0; save = argv; - aflag = sflag = 0; + aflag = kflag = sflag = 0; while ((ch = getopt(argc, argv, "aksx")) != EOF) switch(ch) { case 'a': aflag = 1; break; case 'k': - kvalue = 1; + kflag = 1; break; case 's': sflag = 1; @@ -105,10 +111,11 @@ main(argc, argv) argv[1] = NULL; } - if (!(fts = fts_open(argv, ftsoptions, (int (*)())NULL))) { - (void)fprintf(stderr, "du: %s.\n", strerror(errno)); - exit(1); - } + (void)getbsize("du", ¬used, &blocksize); + blocksize /= 512; + + if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL) + err("%s", strerror(errno)); while (p = fts_read(fts)) switch(p->fts_info) { @@ -116,22 +123,22 @@ main(argc, argv) break; case FTS_DP: p->fts_parent->fts_number += - p->fts_number += p->fts_statb.st_blocks; + p->fts_number += p->fts_statp->st_blocks; /* * If listing each directory, or not listing files * or directories and this is post-order of the * root of a traversal, display the total. */ if (listdirs || !listfiles && !p->fts_level) - (void)printf("%ld\t%s\n", kvalue ? - howmany(p->fts_number, 2) : - p->fts_number, p->fts_path); + (void)printf("%ld\t%s\n", + howmany(p->fts_number, blocksize), + p->fts_path); break; case FTS_DNR: case FTS_ERR: case FTS_NS: (void)fprintf(stderr, - "du: %s: %s.\n", p->fts_path, strerror(errno)); + "du: %s: %s\n", p->fts_path, strerror(errno)); break; case FTS_SL: if (p->fts_level == FTS_ROOTLEVEL) { @@ -140,18 +147,20 @@ main(argc, argv) } /* FALLTHROUGH */ default: - if (p->fts_statb.st_nlink > 1 && linkchk(p)) + if (p->fts_statp->st_nlink > 1 && linkchk(p)) break; /* * If listing each file, or a non-directory file was * the root of a traversal, display the total. */ if (listfiles || !p->fts_level) - (void)printf("%ld\t%s\n", kvalue ? - howmany(p->fts_statb.st_blocks, 2) : - p->fts_statb.st_blocks, p->fts_path); - p->fts_parent->fts_number += p->fts_statb.st_blocks; + (void)printf("%ld\t%s\n", + howmany(p->fts_statp->st_blocks, blocksize), + p->fts_path); + p->fts_parent->fts_number += p->fts_statp->st_blocks; } + if (errno) + err("%s", strerror(errno)); exit(0); } @@ -160,6 +169,7 @@ typedef struct _ID { ino_t inode; } ID; +int linkchk(p) register FTSENT *p; { @@ -169,26 +179,54 @@ linkchk(p) register ino_t ino; register dev_t dev; - ino = p->fts_statb.st_ino; - dev = p->fts_statb.st_dev; + ino = p->fts_statp->st_ino; + dev = p->fts_statp->st_dev; if (start = files) for (fp = start + nfiles - 1; fp >= start; --fp) if (ino == fp->inode && dev == fp->dev) return(1); - if (nfiles == maxfiles && !(files = (ID *)realloc((char *)files, - (u_int)(sizeof(ID) * (maxfiles += 128))))) { - (void)fprintf(stderr, "du: %s\n", strerror(errno)); - exit(1); - } + if (nfiles == maxfiles && (files = realloc((char *)files, + (u_int)(sizeof(ID) * (maxfiles += 128)))) == NULL) + err("%s", strerror(errno)); files[nfiles].inode = ino; files[nfiles].dev = dev; ++nfiles; return(0); } +void usage() { - (void)fprintf(stderr, "usage: du [-a | -s] [-kx] [file ...]\n"); + (void)fprintf(stderr, "usage: du [-a | -s] [-x] [file ...]\n"); + exit(1); +} + +#if __STDC__ +#include +#else +#include +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "du: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); exit(1); + /* NOTREACHED */ } diff --git a/usr.bin/elvis/Makefile b/usr.bin/elvis/Makefile index 79be174c5d..eb65165b48 100644 --- a/usr.bin/elvis/Makefile +++ b/usr.bin/elvis/Makefile @@ -1,7 +1,7 @@ - +CFLAGS+=-DBSD -DREGEX DPADD= ${LIBTERM} -LDADD= -ltermcap +LDADD= -ltermcap -lgnuregex PROG= elvis SRCS= blk.c cmd1.c cmd2.c ctype.c curses.c cut.c ex.c input.c main.c misc.c \ diff --git a/usr.bin/elvis/blk.c b/usr.bin/elvis/blk.c index db41d9320e..091962dab7 100644 --- a/usr.bin/elvis/blk.c +++ b/usr.bin/elvis/blk.c @@ -38,6 +38,9 @@ static struct _blkbuf +void blkflush P_((REG struct _blkbuf *this)); + + /* This function wipes out all buffers */ @@ -72,7 +75,7 @@ BLK *blkget(logical) /* see if we have that block in mem already */ for (this = blk; this < &blk[NBUFS]; this++) { - if (this->logical == logical) + if (this->logical == (unsigned)logical) { newtoo = toonew; toonew = this; @@ -162,6 +165,7 @@ void blkflush(this) if (write(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE) { msg("Trouble writing to tmp file"); + deathtrap(0); } this->dirty = FALSE; @@ -243,7 +247,7 @@ void blkdirty(bp) k = blk[i].logical; for (j = 0; j < NBUFS; j++) { - if (blk[j].logical >= k) + if (blk[j].logical >= (unsigned)k) { blk[j].logical--; } @@ -268,8 +272,21 @@ void blkdirty(bp) BLK *blkadd(logical) int logical; /* where to insert the new block */ { + static long chg; REG int i; + /* if we're approaching the limit, then give a warning */ + if (hdr.n[MAXBLKS - 10] && chg != changes) + { + chg = changes; + msg("WARNING: The edit buffer will overflow soon."); + } + if (hdr.n[MAXBLKS - 2]) + { + msg("BAD NEWS: edit buffer overflow -- GOOD NEWS: text preserved"); + deathtrap(0); + } + /* adjust hdr and lnum[] */ for (i = MAXBLKS - 1; i > logical; i--) { @@ -282,7 +299,7 @@ BLK *blkadd(logical) /* adjust the cache */ for (i = 0; i < NBUFS; i++) { - if (blk[i].logical >= logical) + if (blk[i].logical >= (unsigned)logical) { blk[i].logical++; } @@ -343,7 +360,8 @@ void beforedo(forundo) lseek(tmpfd, 0L, 0); if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) != BLKSIZE) { - msg("Trouble writing header to tmp file "); + msg("Trouble writing header to tmp file"); + deathtrap(0); } /* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */ diff --git a/usr.bin/elvis/cmd1.c b/usr.bin/elvis/cmd1.c index b322ec4122..c3d3f1b7f2 100644 --- a/usr.bin/elvis/cmd1.c +++ b/usr.bin/elvis/cmd1.c @@ -15,7 +15,24 @@ #include "config.h" #include "ctype.h" #include "vi.h" -#include "regexp.h" +#ifdef REGEX +# include +#else +# include "regexp.h" +#endif /* REGEX */ + +#ifndef NO_TAGSTACK +/* These describe the current state of the tag related commands */ +#define MAXTAGS 15 + +struct Tag_item { + MARK tag_mark; + char *tag_file; +}; + +static struct Tag_item tag_stack[MAXTAGS]; +static int curr_tag = -1; +#endif /* !NO_TAGSTACK */ #ifdef DEBUG /* print the selected lines with info on the blocks */ @@ -197,7 +214,7 @@ void cmd_write(frommark, tomark, cmd, bang, extra) } /* either the file must not exist, or we must have a ! or be appending */ - if (access(extra, 0) == 0 && !bang && !append) + if (*extra && access(extra, 0) == 0 && !bang && !append) { msg("File already exists - Use :w! to overwrite"); return; @@ -257,7 +274,11 @@ void cmd_shell(frommark, tomark, cmd, bang, extra) int bang; char *extra; { +#ifdef BSD + static char *prevextra = NULL; +#else static char prevextra[80]; +#endif /* special case: ":sh" means ":!sh" */ if (cmd == CMD_SHELL) @@ -269,14 +290,32 @@ void cmd_shell(frommark, tomark, cmd, bang, extra) /* if extra is "!", substitute previous command */ if (*extra == '!') { - if (!*prevextra) +#ifdef BSD + if (prevextra == NULL) +#else + if (*prevextra == '\0') +#endif { msg("No previous shell command to substitute for '!'"); return; } +#ifdef BSD + else if ((prevextra = (char *) realloc(prevextra, + strlen(prevextra) + strlen(extra))) != NULL) { + strcat(prevextra, extra + 1); + extra = prevextra; + } +#else extra = prevextra; +#endif + } - else if (cmd == CMD_BANG && strlen(extra) < sizeof(prevextra) - 1) + else if (cmd == CMD_BANG && +#ifdef BSD + (prevextra = (char *) realloc(prevextra, strlen(extra) + 1)) != NULL) +#else + strlen(extra) < sizeof(prevextra) - 1) +#endif { strcpy(prevextra, extra); } @@ -320,7 +359,11 @@ void cmd_global(frommark, tomark, cmd, bang, extra) long l; /* used as a counter to move through lines */ long lqty; /* quantity of lines to be scanned */ long nchanged; /* number of lines changed */ +#ifdef REGEX + regex_t *re, *optpat(); +#else regexp *re; /* the compiled search expression */ +#endif /* can't nest global commands */ if (doingglobal) @@ -337,7 +380,7 @@ void cmd_global(frommark, tomark, cmd, bang, extra) } /* make sure we got a search pattern */ - if (*extra != '/' && *extra != '?') + if (*extra == ' ' || *extra == '\n') { msg("Usage: %c /regular expression/ command", cmd == CMD_GLOBAL ? 'g' : 'v'); return; @@ -350,13 +393,16 @@ void cmd_global(frommark, tomark, cmd, bang, extra) msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v'); return; } +#ifdef REGEX + re = optpat(extra + 1); +#else re = regcomp(extra + 1); +#endif if (!re) { /* regcomp found & described an error */ return; } - /* for each line in the range */ doingglobal = TRUE; ChangeText @@ -377,7 +423,11 @@ void cmd_global(frommark, tomark, cmd, bang, extra) line = fetchline(nlines - l); /* if it contains the search pattern... */ +#ifdef REGEX + if ((!regexec(re, line, 0, NULL, 0)) == (cmd == CMD_GLOBAL)) +#else if ((!regexec(re, line, 1)) == (cmd != CMD_GLOBAL)) +#endif { /* move the cursor to that line */ cursor = MARK_AT_LINE(nlines - l); @@ -394,8 +444,10 @@ void cmd_global(frommark, tomark, cmd, bang, extra) } doingglobal = FALSE; +#ifndef REGEX /* free the regexp */ - free(re); + _free_(re); +#endif /* Reporting...*/ rptlines = nchanged; @@ -421,9 +473,9 @@ void cmd_file(frommark, tomark, cmd, bang, extra) if (cmd == CMD_FILE) { #ifndef CRUNCH - msg("\"%s\" %s%s%s %ld lines, line %ld [%ld%%]", + msg("\"%s\" %s%s%s line %ld of %ld [%ld%%]", #else - msg("\"%s\" %s%s %ld lines, line %ld [%ld%%]", + msg("\"%s\" %s%s line %ld of %ld [%ld%%]", #endif *origname ? origname : "[NO FILE]", tstflag(file, MODIFIED) ? "[MODIFIED]" : "", @@ -431,8 +483,8 @@ void cmd_file(frommark, tomark, cmd, bang, extra) tstflag(file, NOTEDITED) ?"[NOT EDITED]":"", #endif tstflag(file, READONLY) ? "[READONLY]" : "", - nlines, markline(frommark), + nlines, markline(frommark) * 100 / nlines); } #ifndef CRUNCH @@ -590,8 +642,8 @@ void cmd_next(frommark, tomark, cmd, bang, extra) } } -/* also called from :wq -- always writes back in this case */ - +/* also called for :wq -- always writes back in this case */ +/* also called for :q -- never writes back in that case */ /*ARGSUSED*/ void cmd_xit(frommark, tomark, cmd, bang, extra) MARK frommark, tomark; @@ -602,45 +654,40 @@ void cmd_xit(frommark, tomark, cmd, bang, extra) static long whenwarned; /* when the user was last warned of extra files */ int oldflag; - /* if there are more files to edit, then warn user */ - if (argno >= 0 && argno + 1 < nargs && whenwarned != changes && (!bang || cmd != CMD_QUIT)) + /* Unless the command is ":q", save the file if it has been modified */ + if (cmd != CMD_QUIT + && (cmd == CMD_WQUIT || tstflag(file, MODIFIED)) + && !tmpsave((char *)0, FALSE) && !bang) + { + msg("Could not save file -- use quit! to abort changes, or w filename"); + return; + } + + /* If there are more files to edit, then warn user */ + if (argno >= 0 && argno + 1 < nargs /* more args */ + && whenwarned != changes /* user not already warned */ + && (!bang || cmd != CMD_QUIT)) /* command not ":q!" */ { msg("More files to edit -- Use \":n\" to go to next file"); whenwarned = changes; return; } - if (cmd == CMD_QUIT) + /* Discard the temp file. Note that we should already have saved the + * the file, unless the command is ":q", so the only way that tmpabort + * could fail would be if you did a ":q" on a modified file. + */ + oldflag = *o_autowrite; + *o_autowrite = FALSE; + if (tmpabort(bang)) { - oldflag = *o_autowrite; - *o_autowrite = FALSE; - if (tmpabort(bang)) - { - mode = MODE_QUIT; - } - else - { - msg("Use q! to abort changes, or wq to save changes"); - } - *o_autowrite = oldflag; + mode = MODE_QUIT; } else { - /* else try to save this file */ - oldflag = tstflag(file, MODIFIED); - if (cmd == CMD_WQUIT) - setflag(file, MODIFIED); - if (tmpend(bang)) - { - mode = MODE_QUIT; - } - else - { - msg("Could not save file -- use quit! to abort changes, or w filename"); - } - if (!oldflag) - clrflag(file, MODIFIED); + msg("Use q! to abort changes, or wq to save changes"); } + *o_autowrite = oldflag; } @@ -746,8 +793,6 @@ void cmd_cd(frommark, tomark, cmd, bang, extra) int bang; char *extra; { - char *getenv(); - #ifndef CRUNCH /* if current file is modified, and no '!' was given, then error */ if (tstflag(file, MODIFIED) && !bang) @@ -759,7 +804,7 @@ void cmd_cd(frommark, tomark, cmd, bang, extra) /* default directory name is $HOME */ if (!*extra) { - extra = getenv("HOME"); + extra = gethome((char *)0); if (!extra) { msg("environment variable $HOME not set"); @@ -928,7 +973,14 @@ void cmd_tag(frommark, tomark, cmd, bang, extra) #ifdef INTERNAL_TAGS char *cmp; /* char of tag name we're comparing, or NULL */ char *end; /* marks the end of chars in tmpblk.c */ + char file[128]; /* name of file containing tag */ + int found; /* whether the tag has been found */ + int file_exists; /* whether any tag file exists */ + char *s, *t; #else +# ifndef NO_TAGSTACK + char *s; +# endif int i; #endif #ifndef NO_MAGIC @@ -971,66 +1023,96 @@ void cmd_tag(frommark, tomark, cmd, bang, extra) *scan = '\0'; /* close the pipe. abort if error */ - if (rpclose(fd) != 0 || scan < tmpblk.c + 3) + if (rpclose(fd) != 0) { - msg("tag \"%s\" not found", extra); + msg("Trouble running \"ref\" -- Can't do tag lookup"); return; } - -#else /* use internal code to look up the tag */ - /* open the tags file */ - fd = open(TAGS, O_RDONLY); - if (fd < 0) + else if (scan < tmpblk.c + 3) { - msg("No tags file"); + msg("tag \"%s\" not found", extra); return; } - /* Hmmm... this would have been a lot easier with */ +#else /* use internal code to look up the tag */ + found = 0; + file_exists = 0; + s = o_tags; + while (!found && *s != 0) { + while (isspace(*s)) s++; + for(t = file; s && *s && !isspace(*s); s++) + *t++ = *s; + *t = '\0'; + + /* open the next tags file */ + fd = open(file, O_RDONLY); + if (fd < 0) + continue; + else + file_exists = 1; - /* find the line with our tag in it */ - for(scan = end = tmpblk.c, cmp = extra; ; scan++) - { - /* read a block, if necessary */ - if (scan >= end) + /* Hmmm... this would have been a lot easier with */ + + /* find the line with our tag in it */ + for(scan = end = tmpblk.c, cmp = extra; ; scan++) { - end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE); - scan = tmpblk.c; + /* read a block, if necessary */ if (scan >= end) { - msg("tag \"%s\" not found", extra); - close(fd); - return; + end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE); + scan = tmpblk.c; + if (scan >= end) + { + close(fd); + break; + } } - } - - /* if we're comparing, compare... */ - if (cmp) - { - /* matched??? wow! */ - if (!*cmp && *scan == '\t') + + /* if we're comparing, compare... */ + if (cmp) { - break; + /* matched??? wow! */ + if (!*cmp && *scan == '\t') + { + if ((s = strrchr(file, '/')) != 0 || + (s = strrchr(file, '\\')) != 0) + ++s; + else + s = file; + *s = '\0'; + found = 1; + break; + } + if (*cmp++ != *scan) + { + /* failed! skip to newline */ + cmp = (char *)0; + } } - if (*cmp++ != *scan) + + /* if we're skipping to newline, do it fast! */ + if (!cmp) { - /* failed! skip to newline */ - cmp = (char *)0; + while (scan < end && *scan != '\n') + { + scan++; + } + if (scan < end) + { + cmp = extra; + } } } + } - /* if we're skipping to newline, do it fast! */ - if (!cmp) - { - while (scan < end && *scan != '\n') - { - scan++; - } - if (scan < end) - { - cmp = extra; - } - } + if (!file_exists) { + msg("No tags file"); + return; + } + + if (!found) { + msg("tag \"%s\" not found", extra); + return; } /* found it! get the rest of the line into memory */ @@ -1062,7 +1144,32 @@ void cmd_tag(frommark, tomark, cmd, bang, extra) return; } tmpstart(tmpblk.c); +#ifdef NO_TAGSTACK } +#else /* tagstack enabled */ + s = prevorig; + } + else + s = origname; + + if (frommark != MARK_UNSET && *s && *o_tagstack) + { + curr_tag++; + if (curr_tag >= MAXTAGS) + { + /* discard the oldest tag position */ + free(tag_stack[0].tag_file); + for (curr_tag = 0; curr_tag < MAXTAGS - 1; curr_tag++) + { + tag_stack[curr_tag] = tag_stack[curr_tag + 1]; + } + /* at this point, curr_tag = MAXTAGS-1 */ + } + tag_stack[curr_tag].tag_file = (char *) malloc(strlen(s) + 1); + strcpy(tag_stack[curr_tag].tag_file, s); + tag_stack[curr_tag].tag_mark = frommark; + } +#endif /* move to the desired line (or to line 1 if that fails) */ #ifndef NO_MAGIC @@ -1082,6 +1189,44 @@ void cmd_tag(frommark, tomark, cmd, bang, extra) } +#ifndef NO_TAGSTACK +/*ARGSUSED*/ +void cmd_pop(frommark, tomark, cmd, bang, extra) + MARK frommark, tomark; + CMD cmd; + int bang; + char *extra; +{ + char buf[8]; + + if (!*o_tagstack) + { + msg("Tagstack not enabled"); + return; + } + + if (curr_tag < 0) + msg("Tagstack empty"); + else + { + if (strcmp(origname, tag_stack[curr_tag].tag_file) != 0) + { + if (!tmpabort(bang)) + { + msg("Use :pop! to abort changes, or :w to save changes"); + return; + } + tmpstart(tag_stack[curr_tag].tag_file); + } + cursor = tag_stack[curr_tag].tag_mark; + if (cursor < MARK_FIRST || cursor > MARK_LAST + BLKSIZE) + { + cursor = MARK_FIRST; + } + free(tag_stack[curr_tag--].tag_file); + } +} +#endif @@ -1247,7 +1392,7 @@ static char *parse_errmsg(text) } /* if the number is part of a larger word, then ignore this line */ - if (*text && isalpha(text[-1])) + if (*text && (isalpha(text[-1]) || text[-1] == '_')) { return (char *)0; } @@ -1451,15 +1596,38 @@ void cmd_make(frommark, tomark, cmd, bang, extra) errfd = -3; } +#if MINT + /* I guess MiNT can't depend on the shell for redirection? */ + close(creat(ERRLIST, 0666)); + if ((fd = open(ERRLIST, O_RDWR)) == -1) + { + unlink(ERRLIST); + return; + } + suspend_curses(); + old2 = dup(2); + dup2(fd, 2); + system(buf.c); + dup2(old2, 2); + close(old2); + close(fd); +#else /* run the command, with curses temporarily disabled */ suspend_curses(); system(buf.c); +#endif resume_curses(mode == MODE_EX); if (mode == MODE_COLON) - mode = MODE_VI; + /* ':' hit instead of CR, so let him escape... -nox */ + return; /* run the "errlist" command */ cmd_errlist(MARK_UNSET, MARK_UNSET, cmd, bang, ERRLIST); + + /* avoid spurious `Hit ' after 1st error message -nox */ + /* (which happened when cmd_errlist didn't have to change files...) */ + if (mode == MODE_VI) + refresh(); } #endif @@ -1738,9 +1906,9 @@ void cmd_suspend(frommark, tomark, cmd, bang, extra) int bang; char *extra; { - void (*func)(); /* stores the previous setting of SIGTSTP */ + SIGTYPE (*func)(); /* stores the previous setting of SIGTSTP */ -#if !defined(__386BSD__) && defined(ANY_UNIX) +#if ANY_UNIX /* the Bourne shell can't handle ^Z */ if (!strcmp(o_shell, "/bin/sh")) { @@ -1749,11 +1917,6 @@ void cmd_suspend(frommark, tomark, cmd, bang, extra) } #endif - func = signal(SIGTSTP, SIG_DFL); - if ( func == SIG_IGN ) { - msg("SIGTSTP is being ignored, you may not suspend the editor", func); - return; - } move(LINES - 1, 0); if (tstflag(file, MODIFIED)) { @@ -1764,7 +1927,7 @@ void cmd_suspend(frommark, tomark, cmd, bang, extra) } refresh(); suspend_curses(); - /* was here func = signal(SIGTSTP, SIG_DFL); /* races ??? */ + func = signal(SIGTSTP, SIG_DFL); kill (0, SIGTSTP); /* the process stops and resumes here */ diff --git a/usr.bin/elvis/cmd2.c b/usr.bin/elvis/cmd2.c index 5d88acc1f1..361799f85e 100644 --- a/usr.bin/elvis/cmd2.c +++ b/usr.bin/elvis/cmd2.c @@ -13,7 +13,11 @@ #include "config.h" #include "ctype.h" #include "vi.h" -#include "regexp.h" +#ifdef REGEX +# include +#else +# include "regexp.h" +#endif #if TOS # include #else @@ -28,6 +32,9 @@ # endif #endif +#ifdef REGEX +int patlock = 0; /* lock substitute pattern */ +#endif /*ARGSUSED*/ void cmd_substitute(frommark, tomark, cmd, bang, extra) @@ -38,8 +45,19 @@ void cmd_substitute(frommark, tomark, cmd, bang, extra) char *extra; /* rest of the command line */ { char *line; /* a line from the file */ +#ifdef REGEX + static regex_t *ore = NULL; /* old regex */ + regex_t *optpat(); + regex_t *re = NULL; + regmatch_t rm[SE_MAX]; + char *startp, *endp; + int n; +#else regexp *re; /* the compiled search expression */ +#endif + char *eol; char *subst; /* the substitution string */ + static char *osubst; char *opt; /* substitution options */ long l; /* a line number */ char *s, *d; /* used during subtitutions */ @@ -57,7 +75,7 @@ void cmd_substitute(frommark, tomark, cmd, bang, extra) /* for now, assume this will fail */ rptlines = -1L; - if (cmd == CMD_SUBAGAIN) + if (cmd == CMD_SUBAGAIN || !*extra) { #ifndef NO_MAGIC if (*o_magic) @@ -65,7 +83,15 @@ void cmd_substitute(frommark, tomark, cmd, bang, extra) else #endif subst = "\\~"; +#ifdef REGEX + /* get the previous substitute pattern; not necessarily + * the previous pattern. + */ + if ((re = ore) == NULL) + msg("RE error: no previous pattern"); +#else re = regcomp(""); +#endif /* if visual "&", then turn off the "p" and "c" options */ if (bang) @@ -76,7 +102,7 @@ void cmd_substitute(frommark, tomark, cmd, bang, extra) else /* CMD_SUBSTITUTE */ { /* make sure we got a search pattern */ - if (*extra != '/' && *extra != '?') + if (*extra == ' ' || *extra == '\n') { msg("Usage: s/regular expression/new text/"); return; @@ -84,7 +110,19 @@ void cmd_substitute(frommark, tomark, cmd, bang, extra) /* parse & compile the search pattern */ subst = parseptrn(extra); +#ifdef REGEX + if (re = optpat(extra + 1)) + patlock = 1; + else + return; + if (re != ore && ore) { + regfree(ore); + free(ore); + } + ore = re; +#else re = regcomp(extra + 1); +#endif } /* abort if RE error -- error message already given by regcomp() */ @@ -146,9 +184,14 @@ void cmd_substitute(frommark, tomark, cmd, bang, extra) { /* fetch the line */ line = fetchline(l); + eol = line + strlen(line); /* if it contains the search pattern... */ +#ifdef REGEX + if (!regexec(re, line, SE_MAX, rm, 0)) +#else if (regexec(re, line, TRUE)) +#endif { /* increment the line change counter */ chline++; @@ -160,14 +203,26 @@ void cmd_substitute(frommark, tomark, cmd, bang, extra) /* do once or globally ... */ do { +#ifdef REGEX + startp = s + rm[0].rm_so; + endp = s + rm[0].rm_eo; +#endif #ifndef CRUNCH /* confirm, if necessary */ if (optc) { +#ifdef REGEX + for (conf = line; conf < startp; conf++) +#else for (conf = line; conf < re->startp[0]; conf++) +#endif addch(*conf); standout(); +#ifdef REGEX + for ( ; conf < endp; conf++) +#else for ( ; conf < re->endp[0]; conf++) +#endif addch(*conf); standend(); for (; *conf; conf++) @@ -177,7 +232,11 @@ void cmd_substitute(frommark, tomark, cmd, bang, extra) if (getkey(0) != 'y') { /* copy accross the original chars */ +#ifdef REGEX + while (s < endp) +#else while (s < re->endp[0]) +#endif *d++ = *s++; /* skip to next match on this line, if any */ @@ -190,17 +249,31 @@ void cmd_substitute(frommark, tomark, cmd, bang, extra) chsub++; /* copy stuff from before the match */ +#ifdef REGEX + while (s < startp) +#else while (s < re->startp[0]) +#endif { *d++ = *s++; } /* substitute for the matched part */ +#ifdef REGEX + regsub(rm, startp, endp, subst, d); +#else regsub(re, subst, d); +#endif +#ifdef REGEX + s = endp; +#else s = re->endp[0]; +#endif d += strlen(d); Continue: +; +#ifndef REGEX /* if this regexp could conceivably match * a zero-length string, then require at * least 1 unmatched character between @@ -212,8 +285,18 @@ Continue: break; *d++ = *s++; } +#endif - } while (optg && regexec(re, s, FALSE)); + } +#ifdef REGEX + while (*s && optg && rm[0].rm_eo && !regexec(re, s, SE_MAX, rm, REG_NOTBOL)); + if (eol - s > 0 && !rm[0].rm_eo && optg) { + msg("RE error: line too long"); + return; + } +#else + while (optg && regexec(re, s, FALSE)); +#endif /* copy stuff from after the match */ while (*d++ = *s++) /* yes, ASSIGNMENT! */ @@ -253,7 +336,9 @@ Continue: } /* free the regexp */ - free(re); +#ifndef REGEX + _free_(re); +#endif /* if done from within a ":g" command, then finish silently */ if (doingglobal) @@ -447,7 +532,7 @@ void cmd_join(frommark, tomark, cmd, bang, extra) } /* see if the line will fit */ - if (strlen(scan) + len + 3 > BLKSIZE) + if (strlen(scan) + len + 3 > (unsigned)BLKSIZE) { msg("Can't join -- the resulting line would be too long"); return; @@ -461,10 +546,14 @@ void cmd_join(frommark, tomark, cmd, bang, extra) if (tmpblk.c[len - 1] == '.' || tmpblk.c[len - 1] == '?' || tmpblk.c[len - 1] == '!') + { + tmpblk.c[len++] = ' '; + tmpblk.c[len++] = ' '; + } + else if (tmpblk.c[len - 1] != ' ') { tmpblk.c[len++] = ' '; } - tmpblk.c[len++] = ' '; } } strcpy(tmpblk.c + len, scan); @@ -769,7 +858,7 @@ void cmd_print(frommark, tomark, cmd, bang, extra) col++; } while (col % *o_tabstop != 0); } - else if (*scan > 0 && *scan < ' ' || *scan == '\177') + else if (*scan >= 1 && *scan < ' ' || *scan == '\177') { qaddch('^'); qaddch(*scan ^ 0x40); @@ -801,6 +890,9 @@ void cmd_print(frommark, tomark, cmd, bang, extra) addch('\n'); exrefresh(); } + + /* leave the cursor on the last line printed */ + cursor = tomark; } diff --git a/usr.bin/elvis/config.h b/usr.bin/elvis/config.h index ef121fd5c2..9ad48e80b2 100644 --- a/usr.bin/elvis/config.h +++ b/usr.bin/elvis/config.h @@ -1,28 +1,225 @@ /* * vi configuration file + * We try to automatically configure to various compilers and operating + * systems. Extend the autoconf section as needed. */ +#ifndef _CONFIG_H +# define _CONFIG_H + +/*************************** autoconf section ************************/ + +/* Commodore-Amiga */ +#ifdef amiga +# define AMIGA 1 +# define COMPILED_BY "Manx Aztec C 5.2b" +# define TINYSTACK 1 +#endif + +/* standard unix V (?) */ +#ifdef M_SYSV +# define UNIXV 1 +# ifdef M_XENIX +# ifndef M_I386 +# define TINYSTACK 1 +# endif +# endif +# undef COHERENT +#endif + +/* xelos system, University of Ulm */ +#ifdef xelos +# define UNIXV 1 +#endif + +/* BSD UNIX? */ +#ifdef bsd # define BSD 1 +#else +# ifdef sun +# ifndef M_SYSV +# define BSD 1 +# endif +# endif +#endif + +/* Microsoft C: sorry, Watcom does the same thing */ +#ifdef M_I86 +# ifndef M_SYSV +# define MSDOS 1 +# ifdef IBMC2 +# define COMPILED_BY "IBM C/2 1.00" +# else +# define MICROSOFT 1 +# define COMPILED_BY "Microsoft C 5.10" +# endif +# define TINYSTACK 1 +# endif +#endif + +/* Borland's Turbo C */ +#ifdef __TURBOC__ +# define MSDOS 1 +# define TURBOC 1 +# ifdef __BORLANDC__ +# define COMPILED_BY "Borland C 2.00" +# else +# define COMPILED_BY (__TURBOC__ >= 661 ? "Turbo C++ 1.00" : "Turbo C 2.00") +# endif +# define TINYSTACK 1 +#endif + +/* Tos Mark-Williams */ +#ifdef M68000 +# define TOS 1 +# define COMPILED_BY "Mark Williams C" +# define TINYSTACK 1 +#endif + +/* Tos GNU-C */ +#ifdef __atarist__ +# ifdef __gem__ +# define TOS 1 +# define COMPILED_BY "GNU-C " __VERSION__ +# define TINYSTACK 1 +# endif +#endif +/* OS9/68000 */ +#ifdef OSK +# define COMPILED_BY "Microware C V2.3 Edition 40" +# define TINYSTACK 1 +#endif + +/* DEC Rainbow, running MS-DOS (handled by earlier MS-DOS tests) */ +/* (would need -DRAINBOW in CFLAGS to compile a Rainbow-compatible .EXE) */ + +#ifdef VMS +# define COMPILED_BY "VAX/VMS VAXC compiler" +# undef VMS +# define VMS 1 +#endif + + +#ifdef COHERENT +# ifdef _I386 +# define COH_386 1 +# define COH_286 0 +# else +# define COH_386 0 +# define COH_286 1 +# endif +# undef COHERENT +# define COHERENT 1 +#endif + +/*************************** end of autoconf section ************************/ /* All undefined symbols are defined to zero here, to allow for older */ /* compilers which dont understand #if defined() or #if UNDEFINED_SYMBOL */ +/*************************** operating systems *****************************/ + +#ifndef BSD +# define BSD 0 /* UNIX - Berkeley 4.x */ +#endif + +#ifndef UNIXV # define UNIXV 0 /* UNIX - AT&T SYSV */ +#endif + +#ifndef UNIX7 # define UNIX7 0 /* UNIX - version 7 */ +#endif + +#ifndef MSDOS # define MSDOS 0 /* PC */ +#endif + +#ifndef TOS # define TOS 0 /* Atari ST */ +#endif + +#ifndef AMIGA # define AMIGA 0 /* Commodore Amiga */ +#endif + +#ifndef OSK # define OSK 0 /* OS-9 / 68k */ +#endif + +#ifndef COHERENT # define COHERENT 0 /* Coherent */ +#endif + +#ifndef RAINBOW /* DEC Rainbow support, under MS-DOS */ +# define RAINBOW 0 +#endif + +#ifndef VMS +# define VMS 0 /* VAX/VMS */ +#endif + + /* Minix has no predefines */ +#if !BSD && !UNIXV && !UNIX7 && !MSDOS && !TOS && !AMIGA && !OSK && !COHERENT && !VMS +# define MINIX 1 +#else # define MINIX 0 +#endif + /* generic combination of Unices */ +#if UNIXV || UNIX7 || BSD || MINIX || COHERENT # define ANY_UNIX 1 +#else +# define ANY_UNIX 0 +#endif + +#ifndef TINYSTACK +# define TINYSTACK 0 +#endif + +/*************************** compilers **************************************/ + +#ifndef AZTEC_C +# define AZTEC_C 0 +#endif + +#ifndef MICROSOFT +# define MICROSOFT 0 +#endif + +#ifndef TURBOC +# define TURBOC 0 +#endif + +/* Should we use "new style" ANSI C prototypes? */ +#ifdef __STDC__ +# define NEWSTYLE 1 +#endif +#ifdef __cplusplus +# define NEWSTYLE 1 +#endif +#ifndef NEWSTYLE +# define NEWSTYLE 0 +#endif + +#if NEWSTYLE +# define P_(s) s +#else +# define P_(s) () +#endif /******************************* Credit ************************************/ #if MSDOS # define CREDIT "Ported to MS-DOS by Guntram Blohm & Martin Patzel" +# if RAINBOW +# define CREDIT2 "Rainbow support added by Willett Kempton" +# endif +#endif + +#if AMIGA +# define CREDIT "Ported to AmigaDOS 2.04 by Mike Rieser & Dale Rahn" #endif #if TOS @@ -37,49 +234,331 @@ # define CREDIT "Ported to Coherent by Esa Ahola" #endif +#if VMS +# define CREDIT "Ported to VAX/VMS by John Campbell" +#endif /*************************** functions depending on OS *********************/ +/* There are two terminal-related functions that we need: ttyread() and + * ttywrite(). The ttyread() function implements read-with-timeout and is + * a true function on all systems. The ttywrite() function is almost always + * just a macro... + */ +#if !TOS && !AMIGA +# define ttywrite(buf, len) write(1, buf, (unsigned)(len)) /* raw write */ +#endif + +/* The strchr() function is an official standard now, so everybody has it + * except Unix version 7 (which is old) and BSD Unix (which is academic). + * Those guys use something called index() to do the same thing. + */ +#if BSD || UNIX7 || OSK +# define strchr index +# define strrchr rindex +#endif +#if !NEWSTYLE extern char *strchr(); +#endif + +/* BSD uses bcopy() instead of memcpy() */ +#if BSD +# define memcpy(dest, src, siz) bcopy(src, dest, siz) +#endif + +/* BSD uses getwd() instead of getcwd(). The arguments are a little different, + * but we'll ignore that and hope for the best; adding arguments to the macro + * would mess up an "extern" declaration of the function. + * + * Also, the Coherent-286 uses getwd(), but Coherent-386 uses getcwd() + */ +#if BSD +#ifndef __386BSD__ +# define getcwd getwd +#endif +#endif +#if COH_286 +# define getcwd getwd +#endif +extern char *getcwd(); +/* text versa binary mode for read/write */ +#if !TOS #define tread(fd,buf,n) read(fd,buf,(unsigned)(n)) #define twrite(fd,buf,n) write(fd,buf,(unsigned)(n)) +#endif + +/**************************** Compiler quirks *********************************/ -#define ttywrite(buf, len) write(1, buf, (unsigned)(len)) /* raw write */ +/* the UNIX version 7 and (some) TOS compilers, don't allow "void" */ +#if UNIX7 || TOS +# define void int +#endif + +/* as far as I know, all compilers except version 7 support unsigned char */ +/* NEWFLASH: the Minix-ST compiler has subtle problems with unsigned char */ +#if UNIX7 || MINIX +# define UCHAR(c) ((c) & 0xff) +# define uchar char +#else +# define UCHAR(c) ((unsigned char)(c)) +# define uchar unsigned char +#endif +/* Some compilers prefer to have malloc declared as returning a (void *) */ +/* ANSI, on the other hand, needs the arguments to free() to be cast */ +#ifndef __STDC__ +# if BSD || AMIGA || MINIX extern void *malloc(); +# define _free_(ptr) free((void *)ptr) +# else +extern char *malloc(); +# define _free_(ptr) free((char *)ptr) +# endif +#else +# define _free_(ptr) free((void *)ptr) +#endif /* __STDC__ */ + +/* everybody but Amiga wants lseek declared here */ +#if !AMIGA +extern long lseek(); +#endif -/* Most compilers could benefit from using the "register" storage class */ -#if 1 -# define REG register +/* ANSI C has getenv() declared in stdlib.h, which we've already included. + * Other compilers will need it declared here, though. + */ +#ifndef __STDC__ +extern char *getenv(); #endif -typedef unsigned char uchar; -#define UCHAR(s) ((unsigned char) (s)) +/* Signal handler functions used to return an int value, which was ignored. + * On newer systems, signal handlers are void functions. Here, we try to + * guess the proper return type for this system. + */ +#ifdef __STDC__ +# define SIGTYPE void +#else +# if MSDOS +# define SIGTYPE void +# else +# if UNIXV +# define SIGTYPE void /* Note: This is wrong for SCO Xenix. */ +# endif +# endif +#endif +#ifndef SIGTYPE +# define SIGTYPE int +#endif /******************* Names of files and environment vars **********************/ -#define TMPDIR "/var/tmp" -#define TMPNAME "%s/elvis%04x%03x" /* temp file */ -#define CUTNAME "%s/elvis_%04x%03x" /* cut buffer's temp file */ +#if ANY_UNIX +# ifndef TMPDIR +# if MINIX +# define TMPDIR "/usr/tmp" /* Keep elvis' temp files off RAM disk! */ +# else +# define TMPDIR "/var/tmp" /* directory where temp files live */ +# endif +# endif +# ifndef PRSVDIR +# define PRSVDIR "/var/preserve" /* directory where preserved file live */ +# endif +# ifndef PRSVINDEX +# define PRSVINDEX "/var/preserve/Index" /* index of files in PRSVDIR */ +# endif # ifndef EXRC # define EXRC ".exrc" /* init file in current directory */ # endif # define SCRATCHOUT "%s/soXXXXXX" /* temp file used as input to filter */ -# ifndef EXINIT -# define EXINIT "EXINIT" -# endif # ifndef SHELL # define SHELL "/bin/sh" /* default shell */ # endif +# if COHERENT +# ifndef REDIRECT +# define REDIRECT ">" /* Coherent CC writes errors to stdout */ +# endif +# endif +# define gethome(x) getenv("HOME") +#endif + +#if AMIGA /* Specify AMIGA environment */ +# ifndef CC_COMMAND +# define CC_COMMAND "cc" /* generic C compiler */ +# endif +# ifndef COLON +# define COLON ':' /* Amiga files can also end in `:' */ +# endif +# ifndef SYSEXRC +# define SYSEXRC "S:" EXRC /* name of ".exrc" file in system dir */ +# endif +# ifndef MAXRCLEN +# define MAXRCLEN 2048 /* max size of a .exrc file */ +# endif +# ifndef NBUFS +# define NBUFS 10 /* must be at least 3 -- more is better */ +# endif +# ifndef NEEDSYNC +# define NEEDSYNC TRUE /* assume ":se sync" by default */ +# endif # ifndef PRSVDIR -# define PRSVDIR "/var/preserve" /* directory where preserved file live */ +# define PRSVDIR "Elvis:" /* directory where preserved file live */ # endif # ifndef PRSVINDEX -# define PRSVINDEX "/var/preserve/Index" /* index of files in PRSVDIR */ +# define PRSVINDEX "Elvis:Index" /* index of files in PRSVDIR */ +# endif +# ifndef REDIRECT +# define REDIRECT ">" /* Amiga writes errors to stdout */ +# endif +# ifndef SCRATCHIN +# define SCRATCHIN "%sSIXXXXXX" +# endif +# ifndef SCRATCHOUT +# define SCRATCHOUT "%sSOXXXXXX" +# endif +# ifndef SHELL +# define SHELL "newshell" /* default shell */ +# endif +# ifndef TERMTYPE +# define TERMTYPE "amiga" /* default termtype */ +# endif +# ifndef TMPDIR /* for AMIGA should end in `:' or `/' */ +# define TMPDIR "T:" /* directory where temp files live */ +# endif +# ifndef TMPNAME +# define TMPNAME "%selv_%x.%x" /* format of names for temp files */ +# endif +# define gethome(x) getenv("HOME") +#endif + +#if MSDOS || TOS +/* do not change TMPNAME and SCRATCH*: they MUST begin with '%s\\'! */ +# ifndef TMPDIR +# define TMPDIR "C:\\tmp" /* directory where temp files live */ +# endif +# ifndef PRSVDIR +# define PRSVDIR "C:\\preserve" /* directory where preserved file live */ +# endif +# ifndef PRSVINDEX +# define PRSVINDEX "C:\\preserve\\Index" /* index of files in PRSVDIR */ +# endif +# define TMPNAME "%s\\elv_%x.%x" /* temp file */ +# if MSDOS +# if MICROSOFT +# define CC_COMMAND "cl -c" /* C compiler */ +# else +# if __BORLANDC__ /* Borland C */ +# define CC_COMMAND "bcc" /* C compiler */ +# else +# if TURBOC /* Turbo C */ +# define CC_COMMAND "tcc" /* C compiler */ +# endif /* TURBOC */ +# endif /* BORLANDC */ +# endif /* MICROSOFT */ +# endif /* MSDOS */ +# define SCRATCHIN "%s\\siXXXXXX" /* DOS ONLY - output of filter program */ +# define SCRATCHOUT "%s\\soXXXXXX" /* temp file used as input to filter */ +# define SLASH '\\' +# ifndef SHELL +# if TOS +# define SHELL "shell.ttp" /* default shell */ +# else +# define SHELL "command.com" /* default shell */ +# endif +# endif +# define NEEDSYNC TRUE /* assume ":se sync" by default */ +# if TOS && __GNUC__ /* probably on other systems, too */ +# define REDIRECT "2>" /* GNUC reports on 2, others on 1 */ +# define CC_COMMAND "gcc -c" +# else +# define REDIRECT ">" /* shell's redirection of stderr */ +# endif +#endif + +#if VMS +/* do not change TMPNAME, and SCRATCH*: they MUST begin with '%s\\'! */ +# ifndef TMPDIR +# define TMPDIR "sys$scratch:" /* directory where temp files live */ # endif +# define TMPNAME "%selv_%x.%x;1" /* temp file */ +# define SCRATCHIN "%ssiXXXXXX" /* DOS ONLY - output of filter program */ +# define SCRATCHOUT "%ssoXXXXXX" /* temp file used as input to filter */ +# define SLASH '\:' /* Worry point... jdc */ +# ifndef SHELL +# define SHELL "" /* default shell */ +# endif +# define REDIRECT ">" /* shell's redirection of stderr */ +# define tread(fd,buf,n) vms_read(fd,buf,(unsigned)(n)) +# define close vms_close +# define lseek vms_lseek +# define unlink vms_delete +# define delete __delete /* local routine conflicts w/VMS rtl routine. */ +# define rpipe vms_rpipe +# define rpclose vms_rpclose +# define ttyread vms_ttyread +# define gethome(x) getenv("HOME") +/* There is no sync() on vms */ +# define sync() +/* jdc -- seems VMS external symbols are case insensitive */ +# define m_fWord m_fw_ord +# define m_bWord m_bw_ord +# define m_eWord m_ew_ord +# define m_Nsrch m_n_srch +# define m_Fch m_f_ch +# define m_Tch m_t_ch +# define v_Xchar v_x_char +/* jdc -- also, braindead vms curses always found by linker. */ +# define LINES elvis_LINES +# define COLS elvis_COLS +# define curscr elvis_curscr +# define stdscr elvis_stdscr +# define initscr elvis_initscr +# define endwin elvis_endwin +# define wrefresh elvis_wrefresh +#endif + +#if OSK +# ifndef TMPDIR +# define TMPDIR "/dd/tmp" /* directory where temp files live */ +# endif +# ifndef PRSVDIR +# define PRSVDIR "/dd/usr/preserve" /* directory where preserved file live */ +# endif +# ifndef PRSVINDEX +# define PRSVINDEX "/dd/usr/preserve/Index" /* index of files in PRSVDIR */ +# endif +# ifndef CC_COMMAND +# define CC_COMMAND "cc -r" /* name of the compiler */ +# endif +# ifndef EXRC +# define EXRC ".exrc" /* init file in current directory */ +# endif +# define SCRATCHOUT "%s/soXXXXXX" /* temp file used as input to filter */ +# ifndef SHELL +# define SHELL "shell" /* default shell */ +# endif +# define FILEPERMS (S_IREAD|S_IWRITE) /* file permissions used for creat() */ +# define REDIRECT ">>-" /* shell's redirection of stderr */ +# define sync() /* OS9 doesn't need a sync() */ +# define gethome(x) getenv("HOME") +#endif #ifndef TAGS -# define TAGS "tags" /* tags file */ +# define TAGS "tags" /* name of the tags file */ +#endif + +#ifndef TMPNAME +# define TMPNAME "%s/elv_%x.%x" /* format of names for temp files */ +#endif + +#ifndef EXINIT +# define EXINIT "EXINIT" /* name of EXINIT environment variable */ +#endif + +#ifndef EXRC +# define EXRC "elvis.rc" /* name of ".exrc" file in current dir */ +#endif + +#ifndef HMEXRC +# define HMEXRC EXRC /* name of ".exrc" file in home dir */ #endif #ifndef KEYWORDPRG @@ -92,7 +571,7 @@ typedef unsigned char uchar; #endif #ifndef ERRLIST -# define ERRLIST "errlist" +# define ERRLIST "errs" #endif #ifndef SLASH @@ -104,7 +583,7 @@ typedef unsigned char uchar; #endif #ifndef REG -# define REG +# define REG register #endif #ifndef NEEDSYNC @@ -115,6 +594,10 @@ typedef unsigned char uchar; # define FILEPERMS 0666 #endif +#ifndef PRESERVE +# define PRESERVE "/usr/libexec/elvispreserve" /* name of the "preserve" program */ +#endif + #ifndef CC_COMMAND # define CC_COMMAND "cc -c" #endif @@ -127,18 +610,6 @@ typedef unsigned char uchar; # define REDIRECT "2>" #endif -#ifndef PRESERVE -# define PRESERVE "/usr/libexec/elvispreserve" /* name of the "preserve" program */ -#endif - -#if !defined(CRUNCH) && defined(LETS_GET_SMALL) -#define CRUNCH -#endif - -#if defined(CRUNCH) && !defined(LETS_GET_SMALL) -#define LETS_GET_SMALL -#endif - #ifndef BLKSIZE # ifdef CRUNCH # define BLKSIZE 1024 @@ -151,16 +622,12 @@ typedef unsigned char uchar; # define KEYBUFSIZE 1000 #endif -#ifdef LETS_GET_SMALL -#define NO_CHARATTR -#define NO_DIGRAPH -#define NO_SENTENCE -#define NO_EXTENSIONS -#define NO_MAGIC -#define NO_ERRLIST -#define NO_MODELINE -#define NO_SHOWMATCH -#define NO_SHOWMODE -#define NO_OPTCOLS -#define NO_MKEXRC +#ifndef MAILER +# define MAILER "mail" #endif + +#ifndef gethome +extern char *gethome(); +#endif + +#endif /* ndef _CONFIG_H */ diff --git a/usr.bin/elvis/ctype.c b/usr.bin/elvis/ctype.c index ac6118307f..fa0a7ba23a 100644 --- a/usr.bin/elvis/ctype.c +++ b/usr.bin/elvis/ctype.c @@ -7,6 +7,8 @@ #include "config.h" #include "ctype.h" +void _ct_init P_((uchar *)); + uchar _ct_toupper[256]; uchar _ct_tolower[256]; uchar _ct_ctypes[256]; diff --git a/usr.bin/elvis/curses.c b/usr.bin/elvis/curses.c index 58ebaed335..31302d3ddd 100644 --- a/usr.bin/elvis/curses.c +++ b/usr.bin/elvis/curses.c @@ -18,15 +18,17 @@ #include "vi.h" #if ANY_UNIX -# if UNIXV +# if UNIXV || COH_386 # ifdef TERMIOS # include # else # include # endif -# ifdef S5WINSIZE -# include /* winsize struct defined in one of these? */ -# include +# ifndef NO_S5WINSIZE +# ifndef _SEQUENT_ +# include /* winsize struct defined in one of these? */ +# include +# endif # else # undef TIOCGWINSZ /* we can't handle it correctly yet */ # endif @@ -48,7 +50,6 @@ extern int VMS_read_raw; /* Set in initscr() */ #endif -extern char *getenv(); static void starttcap(); /* variables, publicly available & used in the macros */ @@ -89,8 +90,8 @@ char *SR_; /* :sr=: scroll reverse */ #else char *SR; /* :sr=: scroll reverse */ #endif -char *KS = ""; /* :ks=: init string for cursor */ -char *KE = ""; /* :ke=: restore string for cursor */ +char *KS = ""; /* :ks=: switch keypad to application mode */ +char *KE = ""; /* :ke=: switch keypad to system mode */ char *KU; /* :ku=: key sequence sent by up arrow */ char *KD; /* :kd=: key sequence sent by down arrow */ char *KL; /* :kl=: key sequence sent by left arrow */ @@ -127,25 +128,25 @@ char *CR = ""; /* :cR=: cursor used for VI replace mode */ char *aend = ""; /* end an attribute -- either UE or ME */ char ERASEKEY; /* backspace key taken from ioctl structure */ #ifndef NO_COLOR -char normalcolor[16]; -char SOcolor[16]; -char SEcolor[16]; -char UScolor[16]; -char UEcolor[16]; -char MDcolor[16]; -char MEcolor[16]; -char AScolor[16]; -char AEcolor[16]; +char normalcolor[24]; +char SOcolor[24]; +char SEcolor[24]; +char UScolor[24]; +char UEcolor[24]; +char MDcolor[24]; +char MEcolor[24]; +char AScolor[24]; +char AEcolor[24]; # ifndef NO_POPUP -char POPUPcolor[16]; +char POPUPcolor[24]; # endif # ifndef NO_VISIBLE -char VISIBLEcolor[16]; +char VISIBLEcolor[24]; # endif #endif #if ANY_UNIX -# if UNIXV +# if UNIXV || COH_386 # ifdef TERMIOS static struct termios oldtermio; /* original tty mode */ static struct termios newtermio; /* cbreak/noecho tty mode */ @@ -210,11 +211,11 @@ void initscr() { #if ANY_UNIX write(2, "Environment variable TERM must be set\n", (unsigned)38); - exit(1); + exit(2); #endif #if OSK writeln(2, "Environment variable TERM must be set\n", (unsigned)38); - exit(1); + exit(2); #endif #if AMIGA termtype = TERMTYPE; @@ -229,7 +230,7 @@ void initscr() #endif #if VMS write(2, "UNKNOWN terminal: define ELVIS_TERM\n", (unsigned)36); - exit(1); + exit(2); #endif } else @@ -246,7 +247,7 @@ void initscr() /* change the terminal mode to cbreak/noecho */ #if ANY_UNIX -# if UNIXV +# if UNIXV || COH_386 # ifdef TERMIOS tcgetattr(2, &oldtermio); # else @@ -281,10 +282,12 @@ void endwin() static int curses_active = FALSE; +extern int oldcurs; + /* Send any required termination strings. Turn off "raw" mode. */ void suspend_curses() { -#if ANY_UNIX && !UNIXV +#if ANY_UNIX && !(UNIXV || COH_386) struct tchars tbuf; # ifdef TIOCSLTC struct ltchars ltbuf; @@ -294,6 +297,7 @@ void suspend_curses() if (has_CQ) { do_CQ(); + oldcurs = 0; } #endif if (has_TE) /* GB */ @@ -311,8 +315,8 @@ void suspend_curses() /* change the terminal mode back the way it was */ #if ANY_UNIX -# if UNIXV -# if TERMIOS +# if (UNIXV || COH_386) +# ifdef TERMIOS tcsetattr(2, TCSADRAIN, &oldtermio); # else ioctl(2, TCSETAW, &oldtermio); @@ -360,7 +364,7 @@ void resume_curses(quietly) { /* change the terminal mode to cbreak/noecho */ #if ANY_UNIX -# if UNIXV +# if UNIXV || COH_386 ospeed = (oldtermio.c_cflag & CBAUD); ERASEKEY = oldtermio.c_cc[VERASE]; newtermio = oldtermio; @@ -381,7 +385,7 @@ void resume_curses(quietly) # else ioctl(2, TCSETAW, &newtermio); # endif -# else /* BSD or V7 or Coherent or Minix */ +# else /* BSD, V7, Coherent-286, or Minix */ struct tchars tbuf; # ifdef TIOCSLTC struct ltchars ltbuf; @@ -438,6 +442,12 @@ void resume_curses(quietly) ERASEKEY = '\177'; /* Accept as <^H> for VMS */ #endif + curses_active = TRUE; + } + + /* If we're supposed to quit quietly, then we're done */ + if (quietly) + { if (has_TI) /* GB */ { do_TI(); @@ -447,12 +457,6 @@ void resume_curses(quietly) do_KS(); } - curses_active = TRUE; - } - - /* If we're supposed to quit quietly, then we're done */ - if (quietly) - { return; } @@ -468,6 +472,10 @@ void resume_curses(quietly) do_SE(); refresh(); ttyread(kbuf, 20, 0); /* in RAW mode, so <20 is very likely */ + if (has_TI) + { + do_TI(); + } if (kbuf[0] == ':') { mode = MODE_COLON; @@ -481,11 +489,7 @@ void resume_curses(quietly) } exwrote = FALSE; -#if TURBOC || __GNUC__ - signal(SIGINT, (void(*)()) trapint); -#else signal(SIGINT, trapint); -#endif } /* This function fetches an optional string from termcap */ @@ -517,7 +521,7 @@ static void musthave(T, s) #if OSK write(2, "\l", 1); #endif - exit(1); + exit(2); } } @@ -615,11 +619,16 @@ static void starttcap(term) getsize(0); /* Key sequences */ - pair(&KS, &KE, "ks", "ke"); + pair(&KS, &KE, "ks", "ke"); /* keypad enable/disable */ mayhave(&KU, "ku"); /* up */ mayhave(&KD, "kd"); /* down */ - mayhave(&KL, "kl"); /* left */ mayhave(&KR, "kr"); /* right */ + mayhave(&KL, "kl"); /* left */ + if (KL && KL[0]=='\b' && !KL[1]) + { + /* never use '\b' as a left arrow! */ + KL = (char *)0; + } mayhave(&PU, "kP"); /* PgUp */ mayhave(&PD, "kN"); /* PgDn */ mayhave(&HM, "kh"); /* Home */ @@ -745,7 +754,7 @@ int getsize(signo) #ifdef SIGWINCH /* reset the signal vector */ - signal(SIGWINCH, getsize); + signal(SIGWINCH, (void *)getsize); #endif /* get the window size, one way or another. */ @@ -913,8 +922,8 @@ void wqrefresh() /* This function is called during termination. It resets color modes */ int ansiquit() { - /* if ANSI color terminal, then reset the colors */ - if (!strcmp(UP, "\033[A")) + /* if ANSI terminal & colors were set, then reset the colors */ + if (!strcmp(UP, "\033[A") && strcmp(SOcolor, SO)) { tputs("\033[37;40m\033[m", 1, faddch); clrtoeol(); @@ -931,21 +940,33 @@ int ansicolor(cmode, attrbyte) int cmode; /* mode to set, e.g. A_NORMAL */ int attrbyte; /* IBM PC attribute byte */ { - char temp[16]; /* hold the new mode string */ + char temp[24]; /* hold the new mode string */ /* if not ANSI-ish, then fail */ if (strcmp(UP, "\033[A") && strcmp(UP, "\033OA")) { - msg("Don't know how to set colors for this terminal"); - return 0; + /* Only give an error message if we're editing a file. + * (I.e., if we're *NOT* currently doing a ".exrc") + */ + if (tmpfd >= 0) + msg("Don't know how to set colors for this terminal"); + return FALSE; } /* construct the color string */ +#ifdef MWC /* either Coherent-286 ("COHERENT"), or Coherent-386 ("M_SYSV") */ + sprintf(temp, "\033[m\033[3%cm\033[4%cm%s%s", + "04261537"[attrbyte & 0x07], + "04261537"[(attrbyte >> 4) & 0x07], + (attrbyte & 0x08) ? "\033[1m" : "", + (attrbyte & 0x80) ? "\033[5m" : ""); +#else sprintf(temp, "\033[m\033[3%c;4%c%s%sm", "04261537"[attrbyte & 0x07], "04261537"[(attrbyte >> 4) & 0x07], (attrbyte & 0x08) ? ";1" : "", (attrbyte & 0x80) ? ";5" : ""); +#endif /* stick it in the right place */ switch (cmode) @@ -997,7 +1018,7 @@ int ansicolor(cmode, attrbyte) #endif } - return 1; + return TRUE; } @@ -1006,6 +1027,7 @@ int ansicolor(cmode, attrbyte) * yet, this is one of the termcap strings; for color terminals that really * have had colors defined, we just the "normal color" escape sequence. */ +int endcolor() { if (aend == ME) diff --git a/usr.bin/elvis/cut.c b/usr.bin/elvis/cut.c index 0e0bb393f5..cfa032160c 100644 --- a/usr.bin/elvis/cut.c +++ b/usr.bin/elvis/cut.c @@ -150,7 +150,7 @@ static void cutfree(buf) msg("cutfree() tried to free a NULL buf->phys pointer."); else #endif - free((char *)buf->phys); + _free_((char *)buf->phys); /* maybe delete the temp file */ maybezap(num); @@ -666,7 +666,7 @@ int cb2str(name, buf, size) } /* if too big, return the size now, without doing anything */ - if (cb->end - cb->start >= size) + if ((unsigned)(cb->end - cb->start) >= size) { return cb->end - cb->start; } diff --git a/usr.bin/elvis/doc/Makedoc b/usr.bin/elvis/doc/Makedoc new file mode 100644 index 0000000000..dacbc371d9 --- /dev/null +++ b/usr.bin/elvis/doc/Makedoc @@ -0,0 +1,88 @@ +# This is the Makefile for Elvis' "doc" directory. It makes use of a +# troff-like formatter called mroff. Since you probably don't have mroff, +# you'll need to edit this Makefile before you can fully use it. It can +# also use nroff, though, so you should be able to get something out of it. +# +# make Use nroff to create an ASCII version of the manual. +# make foo.doc Use nroff to create an ASCII version of foo.man or foo.ms +# make manual Use MROFF to print a typeset manual on a laser printer +# make foo.1200 Use MROFF to print a typeset version of foo.man or foo.ms +# make foo.100 Use MROFF to print a draft-quality version of foo.man or foo.ms +# make foo.more Use MROFF to preview foo.man or foo.more on your terminal +# + +############################################################################### +# Definitions... + +MAN= ctags.man elvis.man elvprsv.man elvrec.man fmt.man ref.man +MS= title.ms index.ms intro.ms visual.ms ex.ms regexp.ms options.ms\ + cutbufs.ms differ.ms internal.ms cflags.ms termcap.ms environ.ms\ + versions.ms question.ms +DOC= title.doc index.doc intro.doc visual.doc ex.doc regexp.doc options.doc\ + cutbufs.doc differ.doc internal.doc cflags.doc termcap.doc environ.doc\ + versions.doc question.doc\ + ctags.doc elvis.doc elvprsv.doc elvrec.doc fmt.doc ref.doc +MANUAL= title.1200 index.1200 intro.1200 visual.1200 ex.1200 regexp.1200 options.1200\ + cutbufs.1200 differ.1200 internal.1200 cflags.1200 termcap.1200 environ.1200\ + versions.1200\ + ctags.1200 elvis.1200 elvprsv.1200 elvrec.1200 fmt.1200 ref.1200 +VER= ver.ms +TROFF= mroff +NROFF= nroff + +############################################################################### +# Rules... + +.SUFFIXES: .tmp .100 .1200 .more .doc .man .ms .vga .vgas + +.ms.tmp: + $(TROFF) -ms $(VER) $< >tmp + +.man.tmp: + $(TROFF) -man $< >tmp + +.ms.more: + $(TROFF) -ms $(VER) $< | draft | more + +.man.more: + $(TROFF) -man $< | draft | more + +.ms.1200: + $(TROFF) -ms $(VER) $< | hp2 1200 | lp -og $(PRINTER) + +.man.1200: + $(TROFF) -man $< | hp2 1200 | lp -og $(PRINTER) + +.ms.100: + $(TROFF) -ms $(VER) $< | hp2 100 | lp -og $(PRINTER) + +.man.100: + $(TROFF) -man $< | hp2 100 | lp -og $(PRINTER) + +.ms.doc: + $(NROFF) -ms $(VER) $< >$@ + +.man.doc: + $(NROFF) -man $< >$@ + +.ms.vga: + $(TROFF) -ms $(VER) $< >/tmp/foo + -vga /tmp/foo + rm /tmp/foo + +.ms.vgas: + $(TROFF) -ms $(VER) $< >/tmp/foo + -vgas /tmp/foo + rm /tmp/foo + +############################################################################# +# Targets... + +Elvisman.txt: $(DOC) + cat $(DOC) | col -b >Elvisman.txt + #cat $(DOC) | col -b -x | sed 's/^/ /' >Elvisman.txt + +manual: $(MANUAL) + +clean: + rm -f *.doc diff --git a/usr.bin/elvis/doc/Makefile b/usr.bin/elvis/doc/Makefile new file mode 100644 index 0000000000..d0802dc13c --- /dev/null +++ b/usr.bin/elvis/doc/Makefile @@ -0,0 +1,46 @@ +NROFF= nroff +HPROFF= troff +HPDEST= >/dev/hp +PSROFF= troff -p +PSDEST= >/dev/lp +MS= title.ms index.ms intro.ms visual.ms ex.ms regexp.ms options.ms \ + cutbufs.ms differ.ms internal.ms makefile.ms cflags.ms termcap.ms \ + environ.ms versions.ms question.ms +MAN= ctags.man elvis.man elvprsv.man elvrec.man fmt.man ref.man +DOC= title.doc index.doc intro.doc visual.doc ex.doc regexp.doc options.doc \ + cutbufs.doc differ.doc internal.doc makefile.doc cflags.doc termcap.doc \ + environ.doc versions.doc question.doc ctags.doc elvis.doc elvprsv.doc \ + elvrec.doc fmt.doc ref.doc +PS= title.ps index.ps intro.ps visual.ps ex.ps regexp.ps options.ps \ + cutbufs.ps differ.ps internal.ps makefile.ps cflags.ps termcap.ps \ + environ.ps versions.ps question.ps ctags.ps elvis.ps elvprsv.ps \ + elvrec.ps fmt.ps ref.ps +HP= title.ps index.ps intro.hp visual.hp ex.hp regexp.hp options.hp \ + cutbufs.hp differ.hp internal.hp makefile.hp cflags.hp termcap.hp \ + environ.hp versions.hp question.hp ctags.hp elvis.hp elvprsv.hp \ + elvrec.hp fmt.hp ref.hp + +.SUFFIXES: .test .doc .ps .hp .ms .man +.ms.doc: + $(NROFF) -ms ver.ms $*.ms | col -bx >$*.doc +.man.doc: + $(NROFF) -man $*.man | col -bx >$*.doc +.ms.ps: + $(PSROFF) -ms ver.ms $*.ms $(PSDEST) +.man.ps: + $(PSROFF) -man $*.man $(PSDEST) +.ms.hp: + $(HPROFF) -ms ver.ms $*.ms $(HPDEST) +.man.hp: + $(HPROFF) -man $*.man $(HPDEST) +.ms.test: + $(PSROFF) -ms ver.ms $*.ms >/dev/null +.man.test: + $(PSROFF) -man $*.man >/dev/null + +../Elvisman.txt: $(DOC) + cat $(DOC) >../Elvisman.txt +Elvisman.ps: $(PS) +Elvisman.hp: $(HP) +clean: + rm -f $(DOC) Elvisman.txt diff --git a/usr.bin/elvis/doc/cflags.ms b/usr.bin/elvis/doc/cflags.ms index 8405c3be22..2bbfa3f57c 100644 --- a/usr.bin/elvis/doc/cflags.ms +++ b/usr.bin/elvis/doc/cflags.ms @@ -1,4 +1,4 @@ -.Go 9 "CFLAGS" +.Go 10 "CFLAGS" .PP \*E uses many preprocessor symbols to control compilation. Some of these control the sizes of buffers and such. @@ -15,15 +15,11 @@ For other systems, the config.h file can generally figure it out automatically. .IP -DRAINBOW For MS-DOS systems, this causes support for the DEC Rainbow to be compiled into \*E. -.IP -DS5WINSIZE +.IP -DNO_S5WINSIZE Some versions of SysV UNIX don't support support the "winsize" -style of screen-size testing, -so elvis ignores window size changes by default. -.IP -However, many of the newer SysV systems defines "winsize" in the -file "/usr/include/sys/ptem.h". -If your SysV system has "winsize" then you should add --DS5SWINSIZE to the CFLAGS setting. +style of screen-size testing. +If you have a SysV system and can't compile "curses.c", +then try adding -DNO_S5WINSIZE to the CFLAGS. .IP -DTERMIOS POSIX is a SysV-derived specification which uses a terminal control package called "termios", instead of "termio". @@ -39,7 +35,7 @@ The default is 5 blocks, and the minimum is 3 blocks. (See the -DBLKSIZE flag, below.) .IP More RAM allows global changes to happen a little faster. - f you're just making many small changes in one section of a file, though, +If you're just making many small changes in one section of a file, though, extra RAM won't help much. .IP -DBLKSIZE=\fInumber\fP This controls the size of blocks that \*E uses internally. @@ -109,6 +105,14 @@ This only controls the default; the value you give here may be overridden at run-time by setting an environment variable named SHELL (or COMSPEC for MS-DOS). Its value must be a string constant, so be careful about quoting. +.IP -DMAILER=\fIstring\fP +This is the name of the program that \*E uses to send mail to a user whose +text has just been preserved. +(See the manual page for the \fIelvprsv\fR program.) +If your system doesn't use electronic mail, then this option is irrelevent. +For UNIX and OS-9 systems, though, the value should be a quoted string. +The default value is "mail", but +SysV users may prefer to use "mailx", and BSD users may prefer "Mail". .IP -DTAGS=\fIstring\fP This sets the name of the "tags" file, which is used by the :tag command. @@ -137,7 +141,7 @@ A couple of obscure features are disabled by this, too. .IP -DNO_MKEXRC This removes the ":mkexrc" command, so you have to create any .exrc files manually. -The size of the .text segment will be reduced by about 600 bytes. +The size of the .text segment will be reduced by about 1500 bytes. .IP -DNO_CHARATTR Permanently disables the charattr option. This reduces the size of your ".text" segment by about 850 bytes. @@ -217,7 +221,10 @@ This removes all support for modelines. .IP -DNO_TAG This disables tag lookup. It reduces the size of the .text segment by about 750 bytes. -.IP "-DNO_ALT_FKEY -DNO_CTRL_FKEY -DNO_SHIFT_FKEY -DNO_FKEY" +.IP -DNO_TAGSTACK +This disables the tagstack. +The ^T and :pop commands will no longer be available. +.IP "-DNO_ALT_FKEY, -DNO_CTRL_FKEY, -DNO_SHIFT_FKEY, -DNO_FKEY" These remove explicit support of function keys. -DNO_ALT_FKEY removes support for the versions function keys. -DNO_CTRL_FKEY removes support for the and versions function keys. @@ -248,8 +255,7 @@ so you might as well add -DNO_SHIFT_FKEY to CFLAGS if you're using terminfo. Note that, even if you have -DNO_FKEYS, you can still configure \*E to use your function keys my mapping the literal character codes sent by the key. You just couldn't do it in a terminal-independent way. -TERM_925 -.IP "-DTERM_AMIGA -DTERM_VT100 -DTERM_VT52 etc." +.IP "-DTERM_925, -DTERM_AMIGA, -DTERM_VT100, -DTERM_VT52, etc." The tinytcap.c file contains descriptions of several terminal types. For each system that uses tinytcap, a reasonable subset of the available descriptions is actually compiled into \*E. @@ -263,7 +269,7 @@ This is more powerful than the real vi's tag lookup, but it can be much slower. .IP If you add -DINTERNAL_TAGS to your CFLAGS setting, -then \* will use its own internal tag lookup code, which is faster. +then \*E will use its own internal tag lookup code, which is faster. .IP -DPRSVDIR=\fIdirectory\fR This controls where preserved files will be placed. An appropriate default has been chosen for each Operating System, diff --git a/usr.bin/elvis/doc/col b/usr.bin/elvis/doc/col new file mode 100644 index 0000000000..d03ee5f727 --- /dev/null +++ b/usr.bin/elvis/doc/col @@ -0,0 +1,4 @@ +case $1 in + -*) shift;; +esac +sed 's/.//g' $* diff --git a/usr.bin/elvis/doc/ctags.man b/usr.bin/elvis/doc/ctags.man new file mode 100644 index 0000000000..7aa3175866 --- /dev/null +++ b/usr.bin/elvis/doc/ctags.man @@ -0,0 +1,84 @@ +.TH CTAGS 1 +.SH NAME +ctags - Generates "tags" and (optionally) "refs" files +.SH SYNOPSIS +\fBctags\fP [\fB-stvra\fP] \fIfilesnames\fP... +.SH DESCRIPTION +\fIctags\fP generates the "tags" and "refs" files +from a group of C source files. +The "tags" file is used by Elvis' ":tag" command, +control-] command, +and -t option. +The "refs" file is sometimes used by the \fIref(1)\fP program. +.PP +Each C source file is scanned for #define statements and +global function definitions. +The name of the macro or function becomes the name of a tag. +For each tag, a line is added to the "tags" file which contains: +.RS +.nf + - the name of the tag + - a tab character + - the name of the file containing the tag + - a tab character + - a way to find the particular line within the file. +.RE +.fi +.PP +The filenames list will typically be the names of all C source +files in the current directory, like this: +.RS +.nf +$ ctags -stv *.[ch] +.RE +.fi +.SH OPTIONS +.IP \fB-t\fR +Include typedefs. +A tag will be generated for each user-defined type. +Also tags will be generated for struct and enum names. +Types are considered to be global if they are defined in a header file, +and static if they are defined in a C source file. +.IP \fB-v\fR +Include variable declarations. +A tag will be generated for each variable, except for those that are declared +inside the body of a function. +.IP \fB-s\fR +Include static tags. +\fICtags\fR will normally put global tags in the "tags" file, and silently ignore +the static tags. +This flag causes both global and static tags to be added. +The name of a static tag is generated by prefixing the name of the declared +item with the name of the file where it is defined, with a colon in between. +For example, "static foo(){}" in "bar.c" results in a tag named "bar.c:foo". +.IP \fB-r\fP +This causes \fIctags\fP to generate both "tags" and "refs". +Without \fB-r\fP, it would only generate "tags". +.IP \fB-a\fR +Append to "tags", and maybe "refs". +Normally, \fIctags\fR overwrites these files each time it is invoked. +This flag is useful when you have to many files in the current directory +for you to list them on a single command-line; +it allows you to split the arguments among several invocations. +.SH FILES +.IP tags +A cross-reference that lists each tag name, the name of the source file that +contains it, and a way to locate a particular line in the source file. +.IP refs +The "refs" file contains the definitions for each tag in the "tags" file, +and very little else. +This file can be useful, for example, when licensing restrictions prevent +you from making the source code to the standard C library readable by everybody, +but you still everybody to know what arguments the library functions need. +.SH BUGS +.PP +\fIctags\fR is sensitive to indenting and line breaks. +Consequently, it might not discover all of the tags in a file that +is formatted in an unusual way. +.SH "SEE ALSO" +elvis(1), refs(1) +.SH AUTHOR +.nf +Steve Kirkendall +kirkenda@cs.pdx.edu +.fi diff --git a/usr.bin/elvis/doc/differ.ms b/usr.bin/elvis/doc/differ.ms index c48e21e595..037a1b28ac 100644 --- a/usr.bin/elvis/doc/differ.ms +++ b/usr.bin/elvis/doc/differ.ms @@ -6,8 +6,8 @@ are implemented in a slightly different manner. .NH 2 Extensions .IP "Save Configuration" 1i -The :mkexrc command saves the current :set and :map configurations in -the ".exrc" file in your current directory. +The :mkexrc command saves the current :set, :map, :ab, :color, and :digraph +configurations in the ".exrc" file in your current directory. .IP "Previous File" 1i The :N or :prev command moves backwards through the args list. .IP "Center Current Row" 1i @@ -92,7 +92,7 @@ the temporary file and then immediately reopen it. \*E changes the shape of the cursor to indicate which mode you're in, if your terminal's termcap entry includes the necessary capabilities. .IP "Hide nroff Lines" 1i -Th ":set hideformat" option hides nroff format control lines. +The ":set hideformat" option hides nroff format control lines. (They are displayed on the screen as blank lines.) .ne 7 .IP "Compiler Interface" 1i @@ -116,7 +116,7 @@ to the other endpoint, and then press one of the operator commands The operator will then immediately be applied to the selected text. .IP "Pop-up Menu Operator" 1i The '\\' key is a new operator, -similar in operation to the c/d/y//! operators +similar in operation to the c/d/y//! operators. It conjures up a menu, from which you can select any of the other operators plus a few other common commands. .IP "Preset Filter Operator" 1i diff --git a/usr.bin/elvis/doc/elvis.man b/usr.bin/elvis/doc/elvis.man new file mode 100644 index 0000000000..8d10b78a44 --- /dev/null +++ b/usr.bin/elvis/doc/elvis.man @@ -0,0 +1,149 @@ +.TH ELVIS 1 +.SH NAME +elvis, ex, vi, view, input - The editor +.SH SYNOPSIS +\fBelvis\fP [\fIflags\fP] [\fB+\fP\fIcmd\fP] [\fIfiles\fP...] +.SH DESCRIPTION +\fIElvis\fP is a text editor which emulates \fIvi\fP/\fIex\fP. +.PP +On systems which pass the program name as an argument, such as Unix and Minix, +you may also install \fIelvis\fP under the names "ex", "vi", "view", and "input". +These extra names would normally be links to elvis; +see the "ln" shell command. +.PP +When \fIelvis\fP is invoked as "vi", +it behaves exactly as though it was invoked as "elvis". +However, if you invoke \fIelvis\fP as "view", +then the readonly option is set as though you had given it the "-R" flag. +If you invoke \fIelvis\fP as "ex", +then \fIelvis\fP will start up in the colon command mode +instead of the visual command mode, +as though you had given it the "-e" flag. +If you invoke \fIelvis\fP as "input" or "edit", +then \fIelvis\fP will start up in input mode, +as though the "-i" flag was given. +.SH OPTIONS +.IP \fB-r\fP +To the real vi, this flag means that a previous edit should be recovered. +\fIElvis\fP, though, has a separate program, called \fIelvrec(1)\fP, for recovering +files. +When you invoke \fIelvis\fP with -r, \fIelvis\fP will tell you to run \fIelvrec\fP. +.IP \fB-R\fP +This sets the "readonly" option, +so you won't accidentally overwrite a file. +.IP "\fB-t\fP \fItag\fP" +This causes \fIelvis\fP to start editing at the given tag. +.IP "\fB-m\fP [\fIfile\fP]" +\fIElvis\fP will search through \fIfile\fP for something that looks like +an error message from a compiler. +It will then begin editing the source file that caused the error, +with the cursor sitting on the line where the error was detected. +If you don't explicitly name a \fIfile\fP, then "errlist" is assumed. +.IP \fB-e\fP +\fIElvis\fP will start up in colon command mode. +.IP \fB-v\fP +\fIElvis\fP will start up in visual command mode. +.IP \fB-i\fP +\fIElvis\fP will start up in input mode. +.IP "\fB-w\fR \fIwinsize\fR" +Sets the "window" option's value to \fIwinsize\fR. +.IP "\fB+\fP\fIcommand\fP or \fB-c\fP \fIcommand\fP" +If you use the +\fIcommand\fP parameter, +then after the first file is loaded +\fIcommand\fP is executed as an EX command. +A typical example would be "elvis +237 foo", +which would cause \fIelvis\fP to start editing foo and +then move directly to line 237. +The "-c \fIcommand\fP" variant was added for UNIX SysV compatibility. +.SH FILES +.IP /tmp/elv* +During editing, +\fIelvis\fP stores text in a temporary file. +For UNIX, this file will usually be stored in the /tmp directory, +and the first three characters will be "elv". +For other systems, the temporary files may be stored someplace else; +see the version-specific section of the documentation. +.IP tags +This is the database used by the \fI:tags\fP command and the \fB-t\fP option. +It is usually created by the \fIctags(1)\fP program. +.IP ".exrc or elvis.rc" +On UNIX-like systems, a file called ".exrc" in your home directory +is executed as a series of \fIex\fR commands. +A file by the same name may be executed in the current directory, too. +On non-UNIX systems, ".exrc" is usually an invalid file name; +there, the initialization file is called "elvis.rc" instead. +.SH ENVIRONMENT +.IP TERM +This is the name of your terminal's entry in the termcap or terminfo +database. +The list of legal values varies from one system to another. +.IP TERMCAP +Optional. +If your system uses termcap, and the TERMCAP variable is unset, then +\*E will read your terminal's definition from \fB/etc/termcap\fR. +If TERMCAP is set to the full pathname of a file (starting with a '/') +then \*E will look in the named file instead of \fB/etc/termcap\fR. +If TERMCAP is set to a value which doesn't start with a '/', +then its value is assumed to be the full termcap entry for your terminal. +.IP TERMINFO +Optional. +If your system uses terminfo, and the TERMINFO variable is unset, then +\*E will read your terminal's definition from the database in the +\fB/usr/lib/terminfo\fR database. +If TERMINFO is set, then its value is used as the database name to use +instead of \fB/usr/lib/terminfo\fR. +.IP "LINES, COLUMNS" +Optional. +These variables, if set, will override the screen size values given in +the termcap/terminfo for your terminal. +On windowing systems such as X, \*E has other ways of determining the +screen size, so you should probably leave these variables unset. +.IP EXINIT +Optional. +This variable can hold EX commands which will be executed before any .exrc +files. +.IP SHELL +Optional. +The SHELL variable sets the default value for the "shell" option, +which determines which shell program is used to perform wildcard +expansion in file names, and also which is used to execute filters +or external programs. +The default value on UNIX systems is "/bin/sh". +.IP +Note: Under MS-DOS, this variable is called COMSPEC instead of SHELL. +.IP HOME +This variable should be set to the name of your home directory. +\*E looks for its initialization file there; +if HOME is unset then the initialization file will not be executed. +.IP TAGPATH +Optional. +This variable is used by the "ref" program, which is invoked by the shift-K, +control-], and :tag commands. +See "ref" for more information. +.IP "TMP, TEMP" +These optional environment variables are only used in non-UNIX versions +of \*E. +They allow you to supply a directory name to be used for storing temporary files. +.SH "SEE ALSO" +ctags(1), ref(1), virec(1) +.PP +\fIElvis - A Clone of Vi/Ex\fP, the complete \fIelvis\fP documentation. +.SH BUGS +There is no LISP support. +Certain other features are missing, too. +.PP +Auto-indent mode is not quite compatible with the real vi. +Among other things, 0^D and ^^D don't do what you might expect. +.PP +Long lines are displayed differently. +The real vi wraps long lines onto multiple rows of the screen, +but \fIelvis\fP scrolls sideways. +.SH AUTHOR +.nf +Steve Kirkendall +kirkenda@cs.pdx.edu +.fi +.PP +Many other people have worked to port \fIelvis\fP to various operating systems. +To see who deserves credit, run the \fI:version\fP command from within \fIelvis\fP, +or look in the system-specific section of the complete documentation. diff --git a/usr.bin/elvis/doc/elvprsv.man b/usr.bin/elvis/doc/elvprsv.man new file mode 100644 index 0000000000..065fb2b120 --- /dev/null +++ b/usr.bin/elvis/doc/elvprsv.man @@ -0,0 +1,54 @@ +.TH ELVPRSV 1 +.SH NAME +elvprsv - Preserve the the modified version of a file after a crash. +.SH SYNOPSIS +.nf +\fB\fBelvprsv\fP ["-\fIwhy elvis died\fP"] /tmp/\fIfilename\fP... +\fB\fBelvprsv\fP -R /tmp/\fIfilename\fP... +.fi +.SH DESCRIPTION +.PP +\fIelvprsv\fP preserves your edited text after \fIelvis\fP dies. +The text can be recovered later, via the \fIelvprsv\fP program. +.PP +For UNIX-like systems, +you should never need to run this program from the command line. +It is run automatically when \fIelvis\fP is about to die, +and it should be run (via /etc/rc) when the computer is booted. +THAT'S ALL! +.PP +For non-UNIX systems such as MS-DOS, you can either use \fIelvprsv\fP +the same way as under UNIX systems (by running it from your AUTOEXEC.BAT file), +or you can run it separately with the "-R" flag to recover the files +in one step. +.PP +If you're editing a file when \fIelvis\fP dies +(due to a bug, system crash, power failure, etc.) +then \fIelvprsv\fP will preserve the most recent version of your text. +The preserved text is stored in a special directory; it does NOT overwrite +your text file automatically. +.PP +\fIelvprsv\fP will send mail to any user whose work it preserves, +if your operating system normally supports mail. +.SH FILES +.IP /tmp/elv* +The temporary file that \fIelvis\fP was using when it died. +.IP /usr/preserve/p* +The text that is preserved by \fIelvprsv\fP. +.IP /usr/preserve/Index +A text file which lists the names of all preserved files, and the names +of the /usr/preserve/p* files which contain their preserved text. +.SH BUGS +.PP +Due to the permissions on the /usr/preserve directory, on UNIX systems +\fIelvprsv\fP must be run as superuser. +This is accomplished by making the \fIelvprsv\fP executable be owned by "root" +and turning on its "set user id" bit. +.PP +If you're editing a nameless buffer when \fIelvis\fP dies, then \fIelvprsv\fP will pretend +that the file was named "foo". +.SH AUTHOR +.nf +Steve Kirkendall +kirkenda@cs.pdx.edu +.fi diff --git a/usr.bin/elvis/doc/elvrec.man b/usr.bin/elvis/doc/elvrec.man new file mode 100644 index 0000000000..b541c2ed14 --- /dev/null +++ b/usr.bin/elvis/doc/elvrec.man @@ -0,0 +1,47 @@ +.TH ELVREC 1 +.SH NAME +elvrec - Recover the modified version of a file after a crash +.SH SYNOPSIS +.nf +\fBelvrec\fP [\fIpreservedfile\fP [\fInewfile\fR]] +.fi +.SH DESCRIPTION +.PP +If you're editing a file when \fIelvis\fP dies, the system crashes, or power fails, +the most recent version of your text will be preserved. +The preserved text is stored in a special directory; it does NOT overwrite +your text file automatically. +.PP +The \fIelvrec\fP program locates the preserved version of a given file, +and writes it over the top of your text file -- or to a new file, if you prefer. +The recovered file will have nearly all of your changes. +.PP +To see a list of all recoverable files, run \fIelvrec\fP with no arguments. +.SH FILES +.IP /usr/preserve/p* +The text that was preserved when \fIelvis\fP died. +.IP /usr/preserve/Index +A text file which lists the names of all preserved files, and the names +of the /usr/preserve/p* files which contain their preserved text. +.SH BUGS +.PP +\fIelvrec\fP is very picky about filenames. +You must tell it to recover the file using exactly the same pathname as +when you were editing it. +The simplest way to do this is to go into the same directory that you were +editing, and invoke \fIelvrec\fP with the same filename as \fIelvis\fP. +If that doesn't work, then try running \fIelvrec\fP with no arguments, +to see exactly which pathname it is using for the desired file. +.PP +Due to the permissions on the /usr/preserve directory, on UNIX systems +\fIelvrec\fP must be run as superuser. +This is accomplished by making the \fIelvrec\fP executable be owned by "root" +and setting its "set user id" bit. +.PP +If you're editing a nameless buffer when \fIelvis\fP dies, then \fIelvrec\fP +will pretend that the file was named "foo". +.SH AUTHOR +.nf +Steve Kirkendall +kirkenda@cs.pdx.edu +.fi diff --git a/usr.bin/elvis/doc/environ.ms b/usr.bin/elvis/doc/environ.ms index 74bb79ee94..6c97f51ea1 100644 --- a/usr.bin/elvis/doc/environ.ms +++ b/usr.bin/elvis/doc/environ.ms @@ -1,4 +1,4 @@ -.Go 11 "ENVIRONMENT VARIABLES" +.Go 12 "ENVIRONMENT VARIABLES" .PP \*E examines several environment variables when it starts up. The values of these variables are used internally for a variety @@ -6,27 +6,24 @@ of purposes. You don't need to define all of these; on most systems, \*E only requires TERM to be defined. On AmigaDOS, MS-DOS or TOS systems, even that is optional. -.SH -TERM, TERMCAP -.PP +.IP "TERM, TERMCAP" TERM tells \*E the name of the termcap entry to use. TERMCAP may contain either the entire termcap entry, or the full pathname of the termcap file to search through. -.PP +.IP If your version of \*E is using tinytcap instead of the full termcap library, -then the value of TERMCAP \fIcannot\fR contain any backslash escapes (\\E, \\r, etc.) -or carat escapes (^[, ^M, etc.), because tinytcap doesn't understand them. +then the value of TERMCAP can't be the name of a file; it can only be undefined, +or contain the entire termcap entry. +In the termcap entry, tinytcap will convert \\E to an character, +but other backslash escapes (\\b, \\r, etc.) or carat escapes (^[, ^M, etc.) +will not be converted to control characters. Instead, you should embed the actual control character into the string. -.SH -TMP, TEMP -.PP +.IP "TMP, TEMP" These only work for AmigaDOS, MS-DOS and Atari TOS. Either of these variables may be used to set the "directory" option, which controls where temporary files are stored. If you define them both, then TMP is used, and TEMP is ignored. -.SH -LINES, COLUMNS -.PP +.IP "LINES, COLUMNS" The termcap entry for your terminal should specify the size of your screen. If you're using a windowing interface, then there is an ioctl() call which will provide the size of the window; the ioctl() values will override the @@ -34,38 +31,30 @@ values in the termcap entry. The LINES and COLUMNS environment variables (if defined) will override either of these sources. They, in turn, can be overridden by a ":set" command. -.PP +.IP Normally, the LINES and COLUMNS variables shouldn't need to be defined. -.SH -EXINIT -.PP +.IP EXINIT This variable's value may contain one or more colon-mode commands, which will be executed after all of the ".exrc" files but before interactive editing begins. -.PP +.IP To put more than one command in EXINIT, you can separate the commands with either a newline or a '|' character. -.SH -SHELL, COMSPEC -.PP +.IP "SHELL, COMSPEC" You can use COMSPEC in MS-DOS, or SHELL in any other system, to specify which shell should be used for executing commands and expanding wildcards. -.SH -HOME -.PP +.IP HOME This variable should give the full pathname of your home directory. \*E needs to know the name of your home directory so it can locate the ".exrc" file there. -.SH -TAGPATH -.PP +.IP TAGPATH This variable is used by the "ref" program. It contains a list of directories that might contain a relevent "tags" file. Under AmigaDOS, MS-DOS or Atari TOS, the names of the directories should be separated by semicolons (";"). Under other operating systems, the names should be separated by colons (":"). -.PP +.IP If you don't define TAGPATH, then "ref" will use a default list which includes the current directory and a few other likely places. See the definition of DEFTAGPATH at the start of ref.c for an accurate list. diff --git a/usr.bin/elvis/doc/ex.ms b/usr.bin/elvis/doc/ex.ms index b43eb3edda..1d97697e08 100644 --- a/usr.bin/elvis/doc/ex.ms +++ b/usr.bin/elvis/doc/ex.ms @@ -10,14 +10,14 @@ .\" .de Cm .if "\\$1"0" \t\\$2\t\\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 -.if "\\$1"1" \s-2[line]\s+2\t\\$2\t\\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 -.if "\\$1"2" \s-2[line][,line]\s+2\t\\$2\t\\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.if "\\$1"1" \s'-2'[line]\s'+2'\t\\$2\t\\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.if "\\$1"2" \s'-2'[line][,line]\s'+2'\t\\$2\t\\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 .. .if t .ds Q `` .if t .ds U '' .if n .ds Q " .if n .ds U " -\s+2LINES COMMAND ARGUMENTS\s-2 +\s'+2'LINES COMMAND ARGUMENTS\s'-2' .Cm 0 ab[br] [short] [expanded form] .Cm 1 a[ppend][!] .Cm 0 ar[gs] [files] @@ -438,9 +438,9 @@ In visual command mode, hitting `*' will do this, too. .PP You can also create an "errlist" file from outside of \*E, -and use "\*E -m" to start elvis and have the cursor moved to the +and use "elvis -m" to start \*E and have the cursor moved to the first error. -Note that you don't need to supply a filename with "\*E -m" because +Note that you don't need to supply a filename with "elvis -m" because the error messages always say which source file an error is in. .PP Note: diff --git a/usr.bin/elvis/doc/fmt.man b/usr.bin/elvis/doc/fmt.man new file mode 100644 index 0000000000..3c731faf5d --- /dev/null +++ b/usr.bin/elvis/doc/fmt.man @@ -0,0 +1,26 @@ +.TH FMT 1 +.SH NAME +fmt - adjust line-length for paragraphs of text +.SH SYNOPSIS +\fBfmt\fP [\-\fIwidth\fP] [\fIfiles\fP]... +.SH DESCRIPTION +\fIfmt\fR is a simple text formatter. +It inserts or deletes newlines, as necessary, to make all lines in a +paragraph be approximately the same width. +It preserves indentation and word spacing. +.PP +The default line width is 72 characters. +You can override this with the \-\fIwidth\fR flag. +If you don't name any files on the command line, +then \fIfmt\fR will read from stdin. +.PP +It is typically used from within \fIvi\fR to adjust the line breaks +in a single paragraph. +To do this, move the cursor to the top of the paragraph, +type "!}fmt", and +hit . +.SH AUTHOR +.nf +Steve Kirkendall +kirkenda@cs.pdx.edu +.fi diff --git a/usr.bin/elvis/doc/index.ms b/usr.bin/elvis/doc/index.ms index b57da69038..0e4a9e23e6 100644 --- a/usr.bin/elvis/doc/index.ms +++ b/usr.bin/elvis/doc/index.ms @@ -1,8 +1,8 @@ .XS 1 INTRODUCTION -What E\s-2LVIS\s+2 does, +What \*E does, Copyright, -How to compile E\s-2LVIS\s+2, +How to compile \*E, Overview .XA 2 VISUAL MODE COMMANDS @@ -47,7 +47,7 @@ Pasting from a cut buffer, Macros, The effect of switching files .XA 7 -DIFFERENCES BETWEEN E\s-2LVIS\s+2 AND THE REAL VI/EX +DIFFERENCES BETWEEN \*E AND THE REAL VI/EX Extensions, Omissions .XA 8 @@ -60,14 +60,16 @@ Colon command interpretation, Screen control, Portability .XA 9 -CFLAGS +MAKEFILE .XA 10 -TERMCAP +CFLAGS .XA 11 -ENVIRONMENT VARIABLES +TERMCAP .XA 12 -VERSIONS +ENVIRONMENT VARIABLES .XA 13 +VERSIONS +.XA 14 QUESTIONS & ANSWERS .XE .PX diff --git a/usr.bin/elvis/doc/internal.ms b/usr.bin/elvis/doc/internal.ms index add10e890f..e18960c62a 100644 --- a/usr.bin/elvis/doc/internal.ms +++ b/usr.bin/elvis/doc/internal.ms @@ -134,7 +134,7 @@ the screen is redrawn by a function called \fBredraw()\fR. This function is called in the getkey() function before each keystroke is read in, if necessary. .PP -Redraw() write to the screen via a package which looks like the "curses" +Redraw() writes to the screen via a package which looks like the "curses" library, but isn't. It is actually much simpler. Most curses operations are implemented as macros which copy characters diff --git a/usr.bin/elvis/doc/makefile.ms b/usr.bin/elvis/doc/makefile.ms new file mode 100644 index 0000000000..ae00f9476a --- /dev/null +++ b/usr.bin/elvis/doc/makefile.ms @@ -0,0 +1,255 @@ +.Go 9 "MAKEFILE" +.PP +On most Operating Systems, and with most compilers, the "Makefile.mix" +file is used to control compilation and installation of \*E. +This section of the manual describes the overall structure of "Makefile.mix", +and the various configuration options in it. +.NH 2 +Configuring the Makefile +.PP +Begin by copying "Makefile.mix" to "Makefile". +Never alter the original "Makefile.mix". +.PP +Most of the configuration options are controlled via a group of macros. +Makefile.mix begins with several pre-configured sets of macro definitions \- +one group for each of the most common supported systems. +As shipped, all of these macro definitions are commented out; +you must either uncomment out one of the groups, or +(for less common systems) construct an entirely new group. +.NH 2 +Using the Makefile +.PP +After configuring the Makefile, you can run "make" to compile the programs. +There are also some other useful things that the Makefile can do... +.LD +.ta 1i 2.2i + COMMAND RESULT + make compile all programs + make install copy the programs to the BIN directory + make clean remove all object files + make clobber remove everything except source & documentation + make uue produce uuencoded compressed tar archives of the source + make sh produce shar archives of the source +.DE +.PP +Note that the last two will probably work only under UNIX. +.NH 2 +What "make install" does +.PP +To install elvis, we should copy all of the executables into a directory +where users can find them; +copy the documentation into a directory where the on-line manual program +can find them; +and arrange for edit buffers to be preserved after a system crash. +The "make install" command tries to do this automatically, +but there are problems. +.PP +Practically all operating systems allow programs to be installed different +directories. +As shipped, Makefile.mix contains somebody's best guess as to where you'd +like them to go. +You should double check it, though. +The BIN macro controls where the programs will be installed. +.PP +On UNIX systems the "elvprsv" and "elvrec" programs need to be +installed as SUID-root programs. +Consequently, you must run "make install" as root; +then they will automatically be installed as SUID-root. +.PP +For text to be recovered after a crash, you need to arrange for the +"elvprsv" program to be run before the /tmp file is cleaned. +This means that the /etc/rc file (or whatever) needs to be edited. +If you have a SysV UNIX system which uses a /etc/rc2.d directory for storing +start-up commands, then you're lucky. +"make install" will detect that /etc/rc2.d exists and attempt to +automatically create a file called "/etc/rc2.d/S03elvis" which runs elvprsv. +However, for non-UNIX systems, or UNIX systems which don't have a /etc/rc2.d +directory, you'll need to do this by hand. +See the "Versions" section of the manual for hints about doing this on +your particular system. +.PP +Non-UNIX systems don't have a standard place where UNIX-style man-pages +go, so "make install" doesn't attempt to install documentation on those +systems. +.PP +On UNIX systems, there is no standard place either, but you can be +pretty sure that your system has a non-standard one. +There is a shell script called "instman.sh" which attempts to figure +out where the man-pages belong on your system, and then copies them there. +You might need to edit "instman.sh" to make it work, but try it as-is first. +"instman.sh" is automatically run by "make install". +.PP +Note: It is safe to run "make install" more than once. +.NH 2 +Summary of Macros +.PP +The following describes the configuration macros. +With most versions of make, a blank macro can simply be left undefined. +.IP OBJ +This is the filename extension for unlinked object files +\- usually .o, but MS-DOS uses .obj. +.IP EXE +This is the filename extension for elvis executable file +\- usually nothing, but MS-DOS uses .exe, +and other operating systems may use something else. +.IP COM +This is the filename extension for the executables of elvis' support programs +\- usually the same as the EXE macro, but since the support programs are all +much smaller that elvis, MS-DOS can use the .com format. +.IP EXTRA +This is a space-delimited list of version-specific object files to be linked +into elvis. +Typically, this list will contain at least one object file which was written +specifically for a given operating system. +It may also contain "tinytcap$(OBJ)" or "tinyprnt$(OBJ)". +.IP EXTRA2 +This is a space-delimited list of version-specific object files used in elvis +and a few of the support programs. +For UNIX-like systems, this is typically an empty list. +For non-UNIX systems, it will usually either be empty, or it will contain +the name of an object file which contains functions which emulate certain +UNIX system calls. +(Not all non-UNIX systems need any special emulation functions, because +all C libraries try to emulate UNIX. You only need an EXTRA2 list if +the library doesn't emulate UNIX well enough.) +.IP LIBS +This is a list of library flags used while linking elvis. +UNIX systems need "-ltermcap" or something similar, unless the EXTRA +macro includes "tinytcap$(OBJ)". +Most other operating systems use "tinytcap$(OBJ)" and don't need anything else, +so they leave the LIBS list empty. +.IP BIN +This is the directory where executables should be installed by "make install". +.IP CC +This is the C compiler command, possibly with "memory model" flags. +.IP CFLAGS +This lists the compiler flags used to select compile-time options. +The "CFLAGS" section of this manual describes this in detail. +.IP LNK +This is the name of the linker. +If you want to use $(CC) as your linker, then you can leave LNK undefined. +.IP LFLAGS +This is a list of linker flags used to select link-time options. +It is almost always blank. +.IP SMALL +The flag for special small memory model compilation \- usually blank. +.IP OF +The link flag to control the output file's name \- usually -o. +The Sun version of "make" strips off trailing whitespace, so a pair +of empty quotes has been added after the space, to protect it. +On non-Suns, this isn't necessary. +.IP RF +The flag used to denote "compile but don't link" \- usually -c +.IP PROGS +This is a space-delimited list of all programs. +This list always includes elvis, ctags, ref, elvrec, and elvprsv. +Also, everybody gets fmt except for BSD UNIX; it already has its own +version of fmt as standard equipment. +.IP +Most non-UNIX systems also include the vi, ex, and view aliases. +(UNIX doesn't need those aliases in the PROGS list because it creates +them via file links during installation.) +OS-9 doesn't include the ex alias, because there is already a command +by that name built into its standard shell. +.IP +Note: some MS-DOS configurations break this list into two smaller lists, +to compensate for MS-DOS's limitations on command line length. +.IP CHMEM +This is either blank, or a command to be run immediately after linking elvis. +Under Minix and Coherent, elvis needs to have extra space assigned for the +stack & heap after it has been linked, +so their commands to do that are placed here. +Most other operating systems generally either don't need to have their stacks +enlarged, or they enlarge it during linking. +.IP SORT +This should be defined to be -DSORT if you want your tags list to be sorted, +or blank if you want it unsorted. +The real vi requires a sorted tags file, so for the sake of compatibility +all of the UNIX configurations use -DSORT. +Elvis doesn't need a sorted tags file, though, so on non-UNIX systems you +can leave this macro blank. +.IP RM +This is the name of a program that deletes files unconditionally. +It is used during "make clean". +RM is defined as "rm -f" for UNIX systems, or "del" for most others. +.IP CP +This is the name of a program that copies files. +\- usually "cp" or "copy". +It is used during "make install". +.IP SYS +This is the type of system. +It is used to select an appropriate style of linking and installation +that are used by "make" and "make install", respectively. +The available types are: +.LD +.ta 2i 3i + unx UNIX and UNIX-like systems + dos MS-DOS + ami AmigaDos + tos Atari TOS + os9 OS-9/68k + vms VAX/VMS + xdos cross-compiled on SCO for MS-DOS +.DE +.IP DUMMY +This is used as the "source" filename in the dependency list of +targets which are supposed to be unconditionally compiled. +It is usually nothing since most versions of "make" treat an +empty source file list as a special case, +but OS-9 needs it defined as "dummy" and further requires that +there be no actual file named dummy. +.IP CFG +The is the name of the compiler configuration file \- usually blank, +since most compilers don't need a configuration file. +Some MS-DOS compilers need it, though. +.NH 2 +Structure of Makefile.mix +.PP +Makefile.mix begins with several sets of commented out configuration +macro definitions, as described above. +A comment saying "The rest of this Makefile contains no user-serviceable parts" +marks the end of this section. +Most people won't need to edit anything after that. +.PP +This is followed by macro definitions which are identical, +regardless of your operating system. +The OBJS macros list the object files that form the portable parts of +elvis, and are used together with the EXTRA and EXTRA2 configuration macros +during linking. +.PP +The SRC macros list all of the files mentioned in the "MANIFEST" file. +These are used to bundle the source code via "make uue" or "make sh". +.PP +This is followed by a target named "all" which depends on all of the +programs listed in the PROGS configuration macro. +This is followed by detailed instructions describing how each file is +compiled and linked. +The only exceptions are the "elvis" program, and the various forms of the +"alias" program. +.PP +Linking a big program like elvis is non-standard on some systems. +To support this, we just say that elvis depends on "linkelv.$(SYS)", +where "$(SYS)" is replaced by whatever you defined the SYS configuration +macro to be. +The various link styles are listed after that. +The only really tricky one is for DOS. +Since the list of files to be linked is too long to fit on a DOS command line, +a customized response file is created, and the name of the response file +is passed instead. +The exact format of the response file depends on the compiler you're using. +.PP +This is followed by system-dependent ways of linking the "alias" object +file to create multiple executables. +For most systems, we only really link it once to form the "ex" executable, +and then copy that executable to form the "vi", "view", and "input" executables. +OS-9, though, doesn't need an "ex" executable and it requires actual +linking for each alias. +.PP +Next comes installation, in all its system dependent forms. +This uses the now-familiar trick of saying that the "install" target +depends on a bogus file named "inst.$(SYS)" and then listing each +installation technique after that. +There should be no surprises here. +.PP +The rest of Makefile.mix contains a few handy pseudo-targets, +such as "make clean". diff --git a/usr.bin/elvis/doc/options.ms b/usr.bin/elvis/doc/options.ms index 35af19e164..79f5ea2a88 100644 --- a/usr.bin/elvis/doc/options.ms +++ b/usr.bin/elvis/doc/options.ms @@ -27,7 +27,7 @@ For string options, you may enclose the new value in quotes. .ta 1.9i 2.4i 3.8i .ps +2 \fBNAMES TYPE DEFAULT MEANING\fP -.ps +.ps -2 autoindent, ai Bool noai auto-indent during input autoprint, ap Bool ap in EX, print the current line autotab, at Bool at auto-indent allowed to use tabs? @@ -57,7 +57,9 @@ make, mk Str mk="make" name of the "make" program mesg, ms Bool ms allow messages from other users? modelines, ml Bool noml are modelines processed? more, more Bool more pause between messages? +nearscroll, ns Num ns=15 when to scroll vs. redraw novice, nov Bool nonovice set options for ease of use +number, nu Bool nonumber show line numbers paragraphs, para Str para="PPppIPLPQP" names of "paragraph" nroff cmd prompt, pr Bool pr show ':' prompt in \fIex\fR mode readonly, ro Bool noro prevent overwriting of orig file @@ -74,6 +76,8 @@ sidescroll, ss Num ss=8 amount of sideways scrolling sync, sy Bool nosy call sync() often tabstop, ts Num ts=8 width of tab characters taglength, tl Num tl=0 significant chars in tag name +tags, tag Str tags="tags" list of tags files +tagstack, tgs Bool tgs enable tagstack? term, te Str te="$TERM" name of the termcap entry terse, tr Bool notr give shorter error messages timeout, to Bool to distinguish from ? @@ -324,8 +328,25 @@ so you can place the "ex:yowza:" in a comment: Normally these lines are ignored, for security reasons, but if you have "set modelines" in your .exrc file then "yowza" is executed. +.IP "nearscroll, ns" +The line that contains the cursor will always be on the screen. +If you move the cursor to a line that isn't on the screen, +then elvis will either scroll +(if the cursor's line is nearly on the screen already) +or redraw the screen completely with the cursor's line centered +(if the cursor line is \fInot\fR near the screen already). +.IP +This option allows you to control elvis' idea of "near". +A value of 15 is typical. +A value of 1 would cause elvis to scroll no more that one line. +A value of 0 disables scrolling. .IP "novice, nov" The command ":set novice" is equivelent to ":set nomagic report=1 showmode". +.IP "number, nu" +The "number" option causes \*E to display line numbers at the start of +each line. +The numbers are not actually part of the text; when the file is written out, +it will be written without line numbers. .IP "paragraphs, pa" The { and } commands move the cursor forward or backward in increments of one paragraph. @@ -466,6 +487,21 @@ As a special case, ":set taglength=0" means that all characters of a tag's name must match. .IP Note: some configurations of \*E don't support this option. +.IP "tags, tag" +If your version of elvis is compiled with -DINTERNAL_TAGS, +then this is a space-delimited list of tags files. +When you tell elvis to look up a tag, +it searches though each file in turn until it finds the tag. +.IP +If your version of elvis is compiled without -DINTERNAL_TAGS, +then you can achieve the same effect via an environment variable called +TAGPATH. +TAGPATH's value is a colon-delimited list of file or directory names. +(For some operating systems, including MS-DOS, the list is delimited by +semicolons instead of colons.) +.IP "tagstack" +This option allows you to disable the tagstack. +I can't think of any reason why you would want to do that. .IP "term, te" This read-only option shows the name of the termcap entry that \*E is using for your terminal. diff --git a/usr.bin/elvis/doc/question.ms b/usr.bin/elvis/doc/question.ms index 2abe559809..db8f8d2819 100644 --- a/usr.bin/elvis/doc/question.ms +++ b/usr.bin/elvis/doc/question.ms @@ -1,28 +1,28 @@ -.nr Qn 0 1 -.de QQ +.Go 14 "QUESTIONS & ANSWERS" +.nr qN 0 1 +.de Q .sp -.IP \fB\\n+(Qn) 0.3i +.IP \fB\\n+(qN) 0.4i .. -.de AA +.de A .IP \fR 0.75i .. -.Go 13 "QUESTIONS & ANSWERS" -.QQ -How can I make elvis run faster under DOS? -.AA +.Q +How can I make \*E run faster under DOS? +.A There are several things you can do. The first thing to do is get a good screen driver such as NANSI.SYS. This can speed up screen redrawing by as much as a factor of eight! The DOS-specific part of section 12 tells you how to do this. -.AA -You might also consider reducing the size of the blocks that elvis uses. +.A +You might also consider reducing the size of the blocks that \*E uses. You'll need to recompile \*E to do this. The default BLKSIZE is 1024 byte for the DOS version of \*E, which means -that for each keystroke that you insert, elvis must shift an average of +that for each keystroke that you insert, \*E must shift an average of about 500 bytes. That's a lot to ask from a little old 5MHz 8088. A BLKSIZE of 512 bytes might be more appropriate. -.AA +.A If you're \fIreally\fR desperate for more speed, you might want to make \*E store its temporary files on a RAM disk. However, this limits the size of the file you can edit, and it eliminates any @@ -30,102 +30,103 @@ chance you may have had to recover your work after a power failure or system crash, but it might be worth it; you decide. To do this, add ":set dir=R:\\" (or whatever your RAM disk's name is) to the \fIelvis.rc\fP file. -.AA +.A Next, consider turning off the "sync" option. When the sync option is turned on, \*E will close the temporary file and reopen it after every change, in order to force DOS to update the file's directory entry. -If you put ":set nosync" into the \fIelvis.rc\fP file, then elvis will +If you put ":set nosync" into the \fIelvis.rc\fP file, then \*E will only close the file when you start editing a different text file, or when you're exiting \*E. Consequently, there is no chance that you'll be able to recover your changes after a power failure... so if you're going to this, then you might as well store the temp files on the RAM disk, too. -.QQ +.Q Where's the key on a DEC keyboard? -.AA +.A I don't know. Maybe the key? You could always use ":map!" to make some other key act like the key. If all else fails, use <[>. -.QQ +.Q Is there a way to show which keys do what? -.AA +.A Yes. The command ":map" will show what each key does in command mode, and ":map!" (with an exclamation mark) shows what each key does in input mode. -.AA +.A The table is divided into three columns: the key's label, the characters that it sends, and the characters that \*E pretends you typed. -.QQ +.Q How can I make \*E display long lines like the real vi? -.AA +.A You can't yet. -The next version of \*E shouldsupport this, though. -.QQ +The next version of \*E should support this, though. +.Q I can't recover my text [under MS-DOS or Atari TOS]. According to the directory listing, the temporary file is 0 bytes long. What went wrong? -.AA +.A MS-DOS and TOS only update a file's directory entry when the file is closed. If the system crashes while the file is still open, then the file's length is stored as 0 bytes. The ":set sync" option is supposed to prevent this; you probably turned it off in the interest of speed, right? -.AA +.A Under MS-DOS [I don't know about TOS], you should delete the empty temporary file, and then run CHKDSK/F. This \fImight\fP find the data that belonged in the empty file, and place it in a new file with a name like "000001.CHK" -- something like that. You can then try to extract the text from that temporary file by giving the -command "elvprsv -R 000001.chk >goodnews.txt". -If you're lucky, then your text might be in GOODNEWS.TXT. -.QQ +command "elvprsv -R 000001.chk". +If you're lucky, then this might recover your text. +.Q What is the most current version of \*E? -.AA +.A Each version of \*E that is released to the public has a version number of the form "number point number". -As I write this, the most current version of elvis is 1.5. -.AA +As I write this, the most current version of \*E is \*V. +.A The intermediate steps between one release and the next are labeled with the \fInext\fP version number, with a letter appended. For example, after 1.4 was released, I started working on 1.5a. I am currently working on 2.0a. When \*E reaches a stable state, I'll call it 2.0 and release it. -.AA -Sometimes a beta-test version of elvis will be available via anonymous FTP +.A +Sometimes a beta-test version of \*E will be available via anonymous FTP from m2xenix.psg.com, in the directory "pub/elvis/beta". -.QQ +.Q I only got executables, but now I want the source code. Where can I get it? -.AA +.A If you have access to the Internet, then you should be able to fetch it from one of the public archives such as \fBplains.nodak.edu\fP. It is accessible via anonymous FTP, or via an email server named "archive-server@plains.nodak.edu". Elvis is located in the directory "/pub/Minix/all.contrib". -.AA -I will also offer it to the C Users' Group. -They sell C source code for us$8 per diskette -(or slightly more outside North America). -Their phone number is (913) 841-1631, -and their address is: -.ID -The C Users' Group -PO Box 3127 -Lawrence KS 66046-0127 +.A +It is also available from the C Users' Group, in volume #365. +As I write this, they are asking $4 per disk plus $3.50 per order in the US, +and elvis requires three disks; this is subject to change. +Their phone number is (913) 841-1631, and their address is: +.LD +.ta 2.5i +.sp + The C Users' Group + 1601 W. 23rd Street, #200 + Lawrence KS 66046-2743 .DE -.QQ +.Q Is this shareware, or public domain, or what? -.AA +.A It is not public domain; it is copyrighted by me, Steve Kirkendall. However, this particular version is freely redistributable, in either source form or executable form. (I would prefer that you give copies away for free, complete with the full source code... but I'm not going to force you.) -.AA +.A It is not shareware; you aren't expected to send me anything. You can use it without guilt. -.AA +.A It is not "copylefted." I hold a copyright, but currently I have not added any of the usual restrictions that you would find on copylefted software. @@ -134,29 +135,36 @@ adding restrictions to subsequent versions, but earlier versions won't be affected. (So far, everybody has been pretty good about this so no restrictions have been necessary.) -.QQ +.Q Can I reuse parts of your source code? -.AA +.A Yes. Please be careful, though, to make sure that the code really is mine. Some of the code was contributed by other people, and I don't have the authority to give you permission to use it. The author's name can be found near the top of each source file. If it says "Steve Kirkendall" then you may use it; otherwise, you'd better contact the author first. -.AA +.A Please don't remove my name from the source code. If you modify the source, please make a note of that fact in a comment near the top of the source code. And, finally, please mention my name in your documentation. -.QQ +.Q Can \*E work with non-ASCII files? -.AA +.A +\*E is 8-bit clean. +This means that \*E will allow you to edit files that use a European +extended ASCII character set. +However, some terminals are not 8-bit clean; +they treat characters in the range 0x80-0x9f as control characters. +\*E expects all characters above 0x7f to be treated as normal displayable +characters, so on these terminals \*E may produce a scrambled display. +.A \*E can't edit binary files because it can't handle the NUL character, and because of line-length limitations. -However, it is 8-bit clean so you should be able to edit any European -extended ASCII file without any surprises. -.AA -\*E has also been modified to work with 16-bit character sets. +.A +\*E has also modified to work with 16-bit character sets, but that modification +is not part of the standard \*E distribution. Yongguang Zhang (ygz@cs.purdue.edu) has created a Chinese version of \*E that uses 16-bit characters and runs under cxterm (Chinese X-term) on X-windows systems. diff --git a/usr.bin/elvis/doc/ref.man b/usr.bin/elvis/doc/ref.man new file mode 100644 index 0000000000..e7d2e178f1 --- /dev/null +++ b/usr.bin/elvis/doc/ref.man @@ -0,0 +1,88 @@ +.TH REF 1 +.SH NAME +ref - Display a C function header +.SH SYNOPSIS +\fBref\fR [-t] [-c \fIclass\fR]... [-f \fIfile\fR]... \fItag\fR +.SH DESCRIPTION +\fIref\fP quickly locates and displays the header of a function. +To do this, \fIref\fR +looks in the "tags" file for the line that describes the function, and then +scans the source file for the function. +When it locates the function, it displays an introductory comment +(if there is one), the function's declaration, and the declarations of all +arguments. +.SH "SEARCH METHOD" +.PP +\fIref\fR uses a fairly sophisticated tag look-up algorithm. +If you supply a filename via \fB-f\fR \fIfile\fR, then elvis first scans +the tags file for a static tag from that file. +This search is limited to the tags file in the current directory. +.PP +If you supply a classname via \fB-c\fR \fIclass\fR, then elvis searches +for a tag from that class. +This search is not limited to the current directory; +You can supply a list of directories in the environment variable \fITAGPATH\fR, +and \fIref\fR will search through the "tags" file in each directory until it finds +a tag in the desired class. +.PP +If that fails, \fIref\fR will then try to look up an ordinary global tag. +This search checks all of the directories listed in \fITAGPATH\fR, too. +.PP +If you've given the \fB-t\fR flag, then \fIref\fR will simply output the tag line that +it found, and then exit. +Without \fB-t\fR, though, \fIref\fR will search for the tag line. +It will try to open the source file, which should be in the same directory +as the tags file where the tag was discovered. +If the source file doesn't exist, or is unreadable, then \fIref\fR will try to open +a file called "\fIrefs\fR" in that directory. +Either way, \fIref\fR will try to locate the tag, and display whatever it finds. +.SH "INTERACTION WITH ELVIS" +.PP +\fIref\fP is used by \fIelvis\fR' shift-K command. +If the cursor is located on a word such as "splat", in the file "foo.c", +then \fIelvis\fR will invoke \fIref\fR with the command "ref -f foo.c splat". +.PP +If \fIelvis\fR has been compiled with the -DEXTERNAL_TAGS flag, then \fIelvis\fR will +use \fIref\fR \fB\fRto scan the tags files. +This is slower than the built-in tag searching, but it allows \fIelvis\fR to access +the more sophisticated tag lookup provided by \fIref\fR. +Other than that, external tags should act exactly like internal tags. +.SH OPTIONS +.IP \fB-t\fR +Output tag info, instead of the function header. +.IP "\fB-f\fR \fIfile\fR" +The tag might be a static function in \fIfile\fR. +You can use several -f flags to have \fIref\fR consider static tags from more than one file. +.IP "\fB-c\fR \fIclass\fR" +The tag might be a member of class \fIclass\fR. +You can use several -c flags to have \fIref\fR consider tags from more than one class. +.SH FILES +.IP \fBtags\fR +List of function names and their locations, generated by \fIctags\fR. +.IP \fBrefs\fR +Function headers extracted from source files (optional). +.SH ENVIRONMENT +.IP \fBTAGPATH\fR +List of directories to be searched. +The elements in the list are separated by either +semicolons (for MS-DOS, Atari TOS, and AmigaDos), or +by colons (every other operating system). +For each operating system, \fIref\fR has a built-in default which is probably +adequate. +.SH NOTES +.PP +You might want to generate a "tags" file the directory that contains the +source code for standard C library on your system. +If licensing restrictions prevent you from making the library source readable +by everybody, then you can have \fIctags\fR generate a "refs" file, +and make "refs" readable by everybody. +.PP +If your system doesn't come with the library source code, then perhaps you +can produce something workable from the \fIlint\fR libraries. +.SH "SEE ALSO" +elvis(1), ctags(1) +.SH AUTHOR +.nf +Steve Kirkendall +kirkenda@cs.pdx.edu +.fi diff --git a/usr.bin/elvis/doc/regexp.ms b/usr.bin/elvis/doc/regexp.ms index 6b6677e61e..3bf77a14dd 100644 --- a/usr.bin/elvis/doc/regexp.ms +++ b/usr.bin/elvis/doc/regexp.ms @@ -10,8 +10,8 @@ Syntax .PP \*E' regexp package treats the following one- or two-character strings (called meta-characters) in special ways: -.IP "\\\\\\\\(\fIsubexpression\fP\\\\\\\\)" 0.8i -The \\( and \\) metacharacters are used to delimit subexpressions. +.IP "\e(\fIsubexpression\fP\e)" 0.8i +The \e( and \e) metacharacters are used to delimit subexpressions. When the regular expression matches a particular chunk of text, \*E will remember which portion of that chunk matched the \fIsubexpression\fP. The :s/regexp/newtext/ command makes use of this feature. @@ -28,17 +28,17 @@ It is only a metacharacter when it occurs at the end of a regular expression; elsewhere, it is treated as a normal character. For example, the regular expression /$$/ will search for a dollar sign at the end of a line. -.IP "\\\\\\\\<" 0.8i -The \\< metacharacter matches a zero-length string at the beginning of +.IP "\e<" 0.8i +The \e< metacharacter matches a zero-length string at the beginning of a word. A word is considered to be a string of 1 or more letters and digits. A word can begin at the beginning of a line or after 1 or more non-alphanumeric characters. -.IP "\\\\\\\\>" 0.8i -The \\> metacharacter matches a zero-length string at the end of a word. +.IP "\e>" 0.8i +The \e> metacharacter matches a zero-length string at the end of a word. A word can end at the end of the line or before 1 or more non-alphanumeric characters. -For example, /\\/ would find any instance of the word "end", +For example, /\e/ would find any instance of the word "end", but would ignore any instances of e-n-d inside another word such as "calendar". .IP "\&." 0.8i @@ -53,42 +53,42 @@ list is inverted -- it will match character that \fIisn't\fP mentioned in the list. For example, /[a-zA-Z]/ matches any letter, and /[^ ]/ matches anything other than a blank. -.IP "\\\\\\\\{\fIn\fP\\\\\\\\}" 0.8i +.IP "\e{\fIn\fP\e}" 0.8i This is a closure operator, which means that it can only be placed after something that matches a single character. It controls the number of times that the single-character expression should be repeated. .IP "" 0.8i -The \\{\fIn\fP\\} operator, in particular, means that the preceding +The \e{\fIn\fP\e} operator, in particular, means that the preceding expression should be repeated exactly \fIn\fP times. -For example, /^-\\{80\\}$/ matches a line of eighty hyphens, and -/\\<[a-zA-Z]\\{4\\}\\>/ matches any four-letter word. -.IP "\\\\\\\\{\fIn\fP,\fIm\fP\\\\\\\\}" 0.8i +For example, /^-\e{80\e}$/ matches a line of eighty hyphens, and +/\e<[a-zA-Z]\e{4\e}\e>/ matches any four-letter word. +.IP "\e{\fIn\fP,\fIm\fP\e}" 0.8i This is a closure operator which means that the preceding single-character expression should be repeated between \fIn\fP and \fIm\fP times, inclusive. If the \fIm\fP is omitted (but the comma is present) then \fIm\fP is taken to be inifinity. -For example, /"[^"]\\{3,5\\}"/ matches any pair of quotes which contains +For example, /"[^"]\e{3,5\e}"/ matches any pair of quotes which contains three, four, or five non-quote characters. .IP "*" 0.8i The * metacharacter is a closure operator which means that the preceding single-character expression can be repeated zero or more times. -It is equivelent to \\{0,\\}. +It is equivelent to \e{0,\e}. For example, /.*/ matches a whole line. -.IP "\\\\\\\\+" 0.8i -The \\+ metacharacter is a closure operator which means that the preceding +.IP "\e+" 0.8i +The \e+ metacharacter is a closure operator which means that the preceding single-character expression can be repeated one or more times. -It is equivelent to \\{1,\\}. -For example, /.\\+/ matches a whole line, but only if the line contains +It is equivelent to \e{1,\e}. +For example, /.\e+/ matches a whole line, but only if the line contains at least one character. It doesn't match empty lines. -.IP "\\\\\\\\?" 0.8i -The \\? metacharacter is a closure operator which indicates that the +.IP "\e?" 0.8i +The \e? metacharacter is a closure operator which indicates that the preceding single-character expression is optional -- that is, that it can occur 0 or 1 times. -It is equivelent to \\{0,1\\}. -For example, /no[ -]\\?one/ matches "no one", "no-one", or "noone". +It is equivelent to \e{0,1\e}. +For example, /no[ -]\e?one/ matches "no one", "no-one", or "noone". .PP Anything else is treated as a normal character which must exactly match a character from the scanned text. @@ -110,21 +110,21 @@ text literally but a few have special meaning: .ta 0.75i 1.3i & Insert a copy of the original text ~ Insert a copy of the previous replacement text - \\1 Insert a copy of that portion of the original text which - matched the first set of \\( \\) parentheses - \\2-\\9 Do the same for the second (etc.) pair of \\( \\) - \\U Convert all chars of any later & or \\# to uppercase - \\L Convert all chars of any later & or \\# to lowercase - \\E End the effect of \\U or \\L - \\u Convert the first char of the next & or \\# to uppercase - \\l Convert the first char of the next & or \\# to lowercase + \e1 Insert a copy of that portion of the original text which + matched the first set of \e( \e) parentheses + \e2-\e9 Do the same for the second (etc.) pair of \e( \e) + \eU Convert all chars of any later & or \e# to uppercase + \eL Convert all chars of any later & or \e# to lowercase + \eE End the effect of \eU or \eL + \eu Convert the first char of the next & or \e# to uppercase + \el Convert the first char of the next & or \e# to lowercase .TA .DE .PP These may be preceded by a backslash to force them to be treated normally. If "nomagic" mode is in effect, then & and ~ will be treated normally, -and you must write them as \\& and \\~ for them to have special meaning. +and you must write them as \e& and \e~ for them to have special meaning. .SH Options .PP @@ -156,12 +156,12 @@ in the file. (The brackets contain a single space and a single tab.): .sp .ti +1i -:%s/[ ]\\+$// +:%s/[ ]\e+$// .PP This example converts the current line to uppercase: .sp .ti +1i -:s/.*/\\U&/ +:s/.*/\eU&/ .PP This example underlines each letter in the current line, by changing it into an "underscore backspace letter" sequence. @@ -172,10 +172,10 @@ by changing it into an "underscore backspace letter" sequence. .PP This example locates the last colon in a line, and swaps the text before the colon with the text after the colon. -The first \\( \\) pair is used to delimit the stuff before the colon, +The first \e( \e) pair is used to delimit the stuff before the colon, and the second pair delimit the stuff after. -In the substitution text, \\1 and \\2 are given in reverse order +In the substitution text, \e1 and \e2 are given in reverse order to perform the swap: .sp .ti +1i -:s/\\(.*\\):\\(.*\\)/\\2:\\1/ +:s/\e(.*\e):\e(.*\e)/\e2:\e1/ diff --git a/usr.bin/elvis/doc/termcap.ms b/usr.bin/elvis/doc/termcap.ms index bb72cb30c9..cd38f1c3c8 100644 --- a/usr.bin/elvis/doc/termcap.ms +++ b/usr.bin/elvis/doc/termcap.ms @@ -1,19 +1,21 @@ -.Go 10 "TERMCAP" +.Go 11 "TERMCAP" .PP \*E uses fairly standard termcap fields for most things. -I invented the cursor shape names +I invented the cursor shape names and some of the function key names, but other than that there should be few surprises. .SH Required numeric fields .if n .ul 0 .ID +.ta 12n :co#: number of columns on the screen (chars per line) :li#: number of lines on the screen .DE .SH Required string fields -.ID .if n .ul 0 +.ID +.ta 12n :ce=: clear to end-of-line :cl=: home the cursor & clear the screen :cm=: move the cursor to a given row/column @@ -23,6 +25,7 @@ Required string fields Boolean fields .if n .ul 0 .ID +.ta 12n :am: auto margins - wrap when char is written in last column? :xn: brain-damaged auto margins - newline ignored after wrap :pt: physical tabs? @@ -31,6 +34,7 @@ Boolean fields Optional string fields .if n .ul 0 .ID +.ta 12n :al=: insert a blank row on the screen :dl=: delete a row from the screen :cd=: clear to end of display @@ -40,15 +44,16 @@ Optional string fields :dc=: delete a character :sr=: scroll reverse (insert row at top of screen) :vb=: visible bell +:ks=: keypad enable +:ke=: keypad disable :ti=: terminal initialization string, to start full-screen mode :te=: terminal termination, to end full-screen mode -:ks=: enables the cursor keypad -:ke=: disables the cursor keypad .DE .SH Optional strings received from the keyboard .if n .ul 0 .ID +.ta 12n :kd=: sequence sent by the key :kl=: sequence sent by the key :kr=: sequence sent by the key @@ -73,6 +78,7 @@ so an IBM PC keyboard may be described using those names instead. Optional strings sent by function keys .if n .ul 0 .ID +.ta 20n :k1=:...:k9=:k0=: codes sent by through keys :s1=:...:s9=:s0=: codes sent by ... :c1=:...:c9=:c0=: codes sent by ... @@ -85,10 +91,13 @@ the key, but \*E doesn't support that. .PP Also, the :s1=:..., :c1=:..., and :a1=:... codes are very non-standard. The terminfo library doesn't support them. +Consequently, if you're using the terminfo library then you might as well +add -DNO_SHIFT_FKEY to your CFLAGS setting. .SH Optional fields that describe character attributes .if n .ul 0 .ID +.ta 12n :so=:se=: start/end standout mode (We don't care about :sg#:) :us=:ue=: start/end underlined mode :md=:me=: start/end boldface mode @@ -102,6 +111,7 @@ The :cQ=: string is used by \*E immediately before exiting to undo the effects of the other cursor shape strings. If :cQ=: is not given, then all other cursor shape strings are ignored. .ID +.ta 12n :cQ=: normal cursor :cX=: cursor used for reading EX command :cV=: cursor used for reading VI commands @@ -112,6 +122,7 @@ If :cQ=: is not given, then all other cursor shape strings are ignored. If the capabilities above aren't given, then \*E will try to use the following values instead. .ID +.ta 12n :ve=: normal cursor, used as :cQ=:cX=:cI=:cR=: :vs=: gaudy cursor, used as :cV=: .DE @@ -119,23 +130,17 @@ following values instead. An example .PP Here's the termcap entry I use on my Minix-ST system. -Some of the fields in it have nothing to do with \*E. -Some can only work on my system; -I have modified my kernel's screen driver. .sp .LD -.ne 14 -mx|minix|minixst|ansi:\\ - :is=\\E[0~:co#80:li#25:bs:pt:\\ - :cm=\\E[%i%d;%dH:up=\\E[A:do=^J:nd=\\E[C:sr=\\EM:\\ - :cd=\\E[J:ce=\\E[K:cl=\\E[H\\E[J:\\ - :al=\\E[L:dl=\\E[M:ic=\\E[@:dc=\\E[P:im=:ei=:\\ - :so=\\E[7m:se=\\E[m:us=\\E[4m:ue=\\E[m:\\ - :md=\\E[1m:me=\\E[m:as=\\E[1;3m:ae=\\E[m:\\ - :ku=\\E[A:kd=\\E[B:kr=\\E[C:kl=\\E[D:\\ - :k1=\\E[1~:k2=\\E[2~:k3=\\E[3~:k4=\\E[4~:k5=\\E[5~:\\ - :k6=\\E[6~:k7=\\E[17~:k8=\\E[18~:k9=\\E[19~:k0=\\E[20~:\\ - :kU=\\E[36~:kQ=\\E[32~:kH=\\E[28~:\\ - :GV=3:GH=D:G1=?:G2=Z:G3=@:G4=Y:GC=E:GL=4:GR=C:GU=A:GD=B:\\ - :cQ=\\E[k:cX=\\E[2;0k:cV=\\E[16;0k:cI=\\E[k:cR=\\E[16;20k: +.ne 10 +mx|minix|minixst|ansi:\e + :is=\eE[0~:co#80:li#25:bs:pt:\e + :cm=\eE[%i%d;%dH:up=\eE[A:do=^J:nd=\eE[C:sr=\eEM:\e + :cd=\eE[J:ce=\eE[K:cl=\eE[H\eE[J:\e + :al=\eE[L:dl=\eE[M:ic=\eE[@:dc=\eE[P:im=:ei=:\e + :so=\eE[7m:se=\eE[m:us=\eE[4m:ue=\eE[m:\e + :md=\eE[1m:me=\eE[m:as=\eE[1;3m:ae=\eE[m:\e + :ku=\eE[A:kd=\eE[B:kr=\eE[C:kl=\eE[D:\e + :k1=\eE[1~:k2=\eE[2~:k3=\eE[3~:k4=\eE[4~:k5=\eE[5~:\e + :k6=\eE[6~:k7=\eE[17~:k8=\eE[18~:k9=\eE[19~:k0=\eE[20~: .DE diff --git a/usr.bin/elvis/doc/title.ms b/usr.bin/elvis/doc/title.ms index 019a59f745..7d879d6f11 100644 --- a/usr.bin/elvis/doc/title.ms +++ b/usr.bin/elvis/doc/title.ms @@ -5,15 +5,16 @@ .. .de nE .ce 7 -####### -# # # # # #### -# # # # # # -##### # # # # #### -# # # # # # -# # # # # # # -####### ###### ## # #### +:: ####### :: +::: # # # # # #### ::: +:::: # # # # # # :::: +::::: ##### # # # # #### ::::: +:::: # # # # # # :::: +::: # # # # # # # ::: +:: ####### ###### ## # #### :: .. -.sp |2i + +.sp |2.5i .if t .tE .if n .nE .ps 10 diff --git a/usr.bin/elvis/doc/ver.ms b/usr.bin/elvis/doc/ver.ms index 20a9335e9d..2ce756cbb2 100644 --- a/usr.bin/elvis/doc/ver.ms +++ b/usr.bin/elvis/doc/ver.ms @@ -1,5 +1,5 @@ -.ds V 1.5j-betatest -.if t .ds E E\s-2LVIS\s+2 +.ds V 1.7 +.if t .ds E E\s'-2'LVIS\s'0' .if n .ds E Elvis .\" .\" usage: .Go diff --git a/usr.bin/elvis/doc/versions.ms b/usr.bin/elvis/doc/versions.ms index b800f38fe6..7137d9da36 100644 --- a/usr.bin/elvis/doc/versions.ms +++ b/usr.bin/elvis/doc/versions.ms @@ -1,11 +1,11 @@ -.Go 12 "VERSIONS" +.Go 13 "VERSIONS" .PP \*E currently works under BSD UNIX, AT&T System-V UNIX, SCO XENIX, Minix, Coherent, MS-DOS, Atari TOS, OS9/68k, VAX/VMS, and AmigaDos. This section of the manual provides special information that applies to each particular version of \*E. .PP -For all versions except MS-DOS, +For all versions except MS-DOS and VMS, the file "Makefile.mix" should be copied to "Makefile", and then edited to select the correct set of options for your system. There is more information about this embedded in the file itself. @@ -17,12 +17,12 @@ Temporary files are stored in /tmp. You should modify /etc/rc so that the temp files are preserved when the system is rebooted. Find a line in /etc/rc which reads -.br +.sp .ti +0.5i ex4.3preserve /tmp -.PP -or something like that, and append the following line after it: -.br +.LP +or something like that, and append the following line: +.sp .ti +0.5i elvprsv /tmp/elv* .PP @@ -36,18 +36,21 @@ Only a system crash or power failure could hurt you. .PP Both \*E and the real Vi read initialization commands from a file called ".exrc", -but the commands in that file might work on one but not the other. +but the commands in that file might work on one editor but not the other. For example, "set keywordprg=man" will work for \*E, but Vi will complain because it doesn't have a "keywordprg" option. -If the warning messages annoy you, then you can edit the config.h file -to change the name of the initialization file ".exrc" to something else, -such as ".elvisrc". +If the warning messages annoy you, then you can edit the CFLAGS setting +in the Makefile and add -DEXRC=\\".elvisrc\\". .PP If you use X windows, you may wish to add "-DCS_LATIN1" to CFLAGS. This will cause the digraph table and the flipcase option to have default values that are appropriate for the LATIN-1 character set. That's the standard character set for X. .PP +The default mailer used notify users when text is preserver is "mail". +You may wish to change this to "Mail" (with an uppercase 'M'). +See the description of "MAILER" in the CFLAGS section of this manual. +.PP The default keyboard macro time-out value is larger for BSD than it is for some other systems, because I've had trouble running \*E via rlogin or Xterm. I guess it takes a while for those keystokes to squirt through the net. @@ -56,32 +59,38 @@ System-V UNIX .PP Most SysV UNIX systems use terminfo instead of termcap, but the terminfo library doesn't seem to have a standard name. -As shipped, Elvis' Makefile.mix is configured with "LIBS=-lterm". -You may need to change it to "LIBS=-ltermcap" or "LIBS=-lterminfo" +As shipped, \*E' Makefile.mix is configured with "LIBS=-ltermcap". +You may need to change it to "LIBS=-lterm" or "LIBS=-lterminfo" or even "LIBS=-lcurses". .PP -The /etc/rc file should be modified as described for BSD systems, above. -The only difference is that SysV systems tend to have directories for -initialization, instead of a single large /etc/rc file. -Editor recovery is usually done somewhere in the /etc/rc2.d directory. +The /etc/rc file (or its equivelent) should be modified as described +for BSD systems, above. +There's a pretty good chance that "make install" will do this for you; +it knows how to create an editor recovery file in the /etc/rc2.d directory, +which is where most modern SysV systems store initialization commands. +You only need to do it manually for older SysV systems. .PP The potential trouble with ".exrc" described above for BSD UNIX applies to System-V UNIX as well. .PP +The default mailer used notify users when text is preserver is "mail". +You may wish to change this to "mailx". +See the description of "MAILER" in the CFLAGS section of this manual. +.PP \*E uses control-C as the interrupt key, not Delete. +This was done so that the <Del> key could be used for character deletion. .NH 2 SCO Xenix .PP For Xenix-386, you can use the generic System-V settings. You may wish to add "-DCS_IBMPC" to CFLAGS, to have the digraph table and flipcase option start up in a mode that is appropriate for the console. - -There is a separate group of settings for use with Xenix-286. +Also, note that there is a separate group of settings for use with Xenix-286. It already has "-DCS_IBMPC" in CFLAGS. .PP Because Xenix is so similar to System-V, everything I said earlier about System-V applies to the Xenix version too, except that editor recovery -probably belongs in a directory called /etc/rc.d/8. +might belong in a directory called /etc/rc.d/8 instead. .NH 2 Minix .PP @@ -90,20 +99,26 @@ The differences between these two are that the 68k version uses ".o" for the object file extension where the PC version uses ".s", and the PC version has some extra flags in CFLAGS to reduce the size of \*E. -The PC version also uses tinytcap (instead of the full termcap) to make it smaller. +The PC version also uses tinytcap (instead of the full termcap) +to make it smaller. .PP Minix-PC users should read the CFLAGS section of this manual very carefully. You have some choices to make... .PP The temporary files are stored in /usr/tmp. The /usr/tmp directory must exist before you run \*E, -and it must be readable/writable by everybody. +and it must be readable & writable by everybody. We use /usr/tmp instead of /tmp because after a system crash or power failure, you can recover the altered version of a file from the temporary file in /usr/tmp. If it was stored in /tmp, though, then it would be lost because /tmp is normally located on the RAM disk. +Also, you'll need a /usr/preserve directory which is readable & writable +by root; +this directory is used to store text files that have been preserved after a +crash. +The "make install" script will create it if necessary. .PP \*E uses control-C as the interrupt key, not Delete. .NH 2 @@ -125,7 +140,13 @@ To use tinytcap, just add "tinytcap.o" to the "EXTRA=" line in the Makefile, and remove "-lterm" from the "LIBS=" line. .PP The temporary files are stored in /tmp. -You should modify your /etc/rc file as described for BSD earlier. +Preserved files are stored in /usr/preserve. +You should modify your /etc/rc file to support file file preservation; +add the line... +.sp + /usr/bin/elvprsv /tmp/* +.sp +... just before the first "/bin/rm" line. .NH 2 MS-DOS .PP @@ -141,7 +162,7 @@ With Microsoft C 6.00, you may have trouble compiling regexp.c. If so, try compiling it without optimization. .PP The "Makefile.mix" file contains a set of suggested settings for compiling -elvis with Turbo-C or Borland C. +\*E with Turbo-C or Borland C. (If you have Turbo-C, but not the Make utility, then you can \fIalmost\fR use the "\*E.prj" file to compile \*E, but you must explicitly force Turbo-C to compile it with the "medium" memory model. @@ -158,7 +179,7 @@ You can also control the name of the temp directory via an environment variable named TMP or TEMP. The directory must exist before you can run \*E. .PP -The TERM environment variable determines how elvis will write to the screen. +The TERM environment variable determines how \*E will write to the screen. It can be set to any one of the following values: .LD .ta 1.5i 2.5i @@ -168,7 +189,7 @@ It can be set to any one of the following values: nansi User faster NANSI.SYS driver. .DE .PP -If the TERM variable isn't set, then elvis will automatically select either +If the TERM variable isn't set, then \*E will automatically select either the "rainbow" interface (when run on a Rainbow) or "pcbios" (on an IBM clone). .PP You may prefer to use NANSI.SYS for speed; @@ -183,11 +204,13 @@ After that, \*E will notice the "TERM" setting and use the driver. .PP Since ".exrc" is not a valid DOS filename, the name of the initialization file has been changed to "elvis.rc". -Elvis will look for an "elvis.rc" file first in your home directory, -and then in the current directory. -Note that you must set an environment variable named "HOME" to the -full pathname of your home directory, for Elvis to check there; -if "HOME" isn't set, then Elvis will only look in the current directory. +\*E will look for an "elvis.rc" file first in your home directory. +If it exists, and contains ":set exrc", then \*E will check for +another "elvis.rc" in the current directory. +By default, the directory where ELVIS.EXE resides is taken to be your +home directory. +You can override this default by setting an environment variable named +"HOME" to the full pathname of your home directory. To set "HOME", you would typically add the following line to your AUTOEXEC.BAT file: .br @@ -199,11 +222,11 @@ It expands wildcard characters in file names. If \*E flashes a "Bad command or filename" message when it starts, then you've probably lost the WILDCARD.EXE program somehow. .PP -\*E can run under Windows, but only in full-screen mode. -Also, Windows uses an environment variable called TEMP which interferes with -elvis' usage of TEMP; +\*E can run under Windows, but you may have trouble with TEMP. +Windows uses an environment variable called TEMP which interferes with +\*E' usage of TEMP; to work around this, you can simply set an environment variable named -TMP (with no 'E') to the name of elvis' temporary directory. +TMP (with no 'E') to the name of \*E' temporary directory. When TEMP and TMP are both set, \*E uses TMP and ignored TEMP. .NH 2 Atari TOS @@ -265,14 +288,14 @@ VAX/VMS .PP John Campbell ported \*E to VAX/VMS. .PP -A heavily laden VAX can take half an hour to compile elvis. +A heavily laden VAX can take half an hour to compile \*E. This is normal. Don't panic. .PP -While running, elvis will create temporary files in SYS$SCRATCH. +While running, \*E will create temporary files in SYS$SCRATCH. Enter SHOW LOGICAL SYS$SCRATCH to see what actual directory you are using. Many sites have SYS$SCRATCH equivalenced to SYS$LOGIN. -The elvis temporary files look like the following on VMS while elvis is running: +The \*E temporary files look like the following on VMS while \*E is running: .br .ti 0.75i ELV_1123A.1;1 ELV_1123A.2;1 SO070202.;1 @@ -318,7 +341,7 @@ Under AmigaDOS 1.2/1.3 \*E works, buts lacks the more advanced features. .PP For the port to AmigaDOS 2.04, we tried to use as many Native AmigaDOS calls as we could. -This should increase Elvis's chances at being compiled with other compilers. +This should increase \*E's chances at being compiled with other compilers. DICE seems to have a different default char type. You may need to use the UCHAR() macro in tio.c. To test it, try the :map command; if it looks right, things are cool. @@ -335,8 +358,8 @@ supplied with Aztec, but for people with a straight Aztec installation, I went with the default names for the Aztec pragmas. .PP One include you'll need is <sys/types.h>. -Its a common include when porting software just make yourself one. -Its a two line file that saves a lot of hassle especially in the elvis source. +It's a common include when porting software just make yourself one. +It's a two line file that saves a lot of hassle especially in the \*E source. So, make a directory where your includes are located called `sys' and in a file below that type: .br @@ -349,7 +372,7 @@ and in a file below that type: When setting environment variables (either local or global) for variables that specify a directory, make sure the variable ends in `:' or `/'. -This saved from having to change much of the way elvis works. +This saved from having to change much of the way \*E works. The default temporary directory (if TEMP and TMP aren't specified) is "T:". The default if HOME directory (if no HOME environment variable is set) is "S:". .PP @@ -358,9 +381,10 @@ where it looks for macros. .NH 2 Other Systems .PP -For Sun workstations, use the BSD configuration. -Earlier versions of elvis didn't link correctly due to a quirk in Sun's -version of the "make" utility, but this version of elvis has a work-around +For SunOS and Solaris 1.x, use the BSD settings; +for Solaris 2.x, use the SysV settings. +Earlier versions of \*E didn't link correctly due to a quirk in Sun's +version of the "make" utility, but this version of \*E has a work-around for that quirk so you should have no trouble at all. .PP For Linux, use the SysV settings. @@ -376,3 +400,6 @@ will therefore support V7/Minix code. You may need to fiddle with #include directives or something, though. Minix-68k is a better starting point than Minix-PC because the PC compiler has some severe quirks. +.PP +If you're thinking of porting \*E to some non-UNIX system, +I suggest you begin by studying the "INTERNALS" section of this manual. diff --git a/usr.bin/elvis/doc/visual.ms b/usr.bin/elvis/doc/visual.ms index 8d73ca52a1..18f216e9e7 100644 --- a/usr.bin/elvis/doc/visual.ms +++ b/usr.bin/elvis/doc/visual.ms @@ -20,7 +20,7 @@ but by hitting <Esc> you will have switched to visual command mode. So, one way or another, after <Esc> \*E will be ready for a command. .LD .ta 0.7i 1.3i -\s+2COMMAND DESCRIPTION\s-2 +\s'+2'COMMAND DESCRIPTION\s'-2' ^A Search for next occurence of word at cursor (MOVE)(EXT) ^B Move toward the top of the file by 1 screenful ^C --- (usually sends SIGINT, to interupt a command) @@ -53,13 +53,13 @@ count ^Y Scroll down <count> lines ^^ Switch to the previous file, like ":e #" ^_ --- count SPC Move right,like l (MOVE) - ! \s-2mv\s+2 Run the selected lines thru an external filter program - " \s-2key\s+2 Select which cut buffer to use next -count # \s-2+\s+2 Increment a number (EDIT) (EXT) + ! \s'-2'mv\s'+2' Run the selected lines thru an external filter program + " \s'-2'key\s'+2' Select which cut buffer to use next +count # \s'-2'+\s'+2' Increment a number (EDIT) (EXT) $ Move to the rear of the current line (MOVE) count % Move to matching (){}[] or to a given % of file (MOVE) (EXT) count & Repeat the previous ":s//" command here (EDIT) - ' \s-2key\s+2 Move to a marked line (MOVE) + ' \s'-2'key\s'+2' Move to a marked line (MOVE) count ( Move backward <count> sentences (MOVE) count ) Move forward <count> sentences (MOVE) * Go to the next error in the errlist (EXT) @@ -67,7 +67,7 @@ count + Move to the front of the next line (MOVE) count , Repeat the previous [fFtT] but in the other direction (MOVE) count - Move to the front of the preceding line (MOVE) count . Repeat the previous "edit" command - / \s-2text\s+2 Search forward for a given regular expression (MOVE) + / \s'-2'text\s'+2' Search forward for a given regular expression (MOVE) 0 If not part of count, move to 1st char of this line (MOVE) 1 Part of count 2 Part of count @@ -78,33 +78,33 @@ count . Repeat the previous "edit" command 7 Part of count 8 Part of count 9 Part of count - : \s-2text\s+2 Run single EX cmd + : \s'-2'text\s'+2' Run single EX cmd count ; Repeat the previous [fFtT] cmd (MOVE) - < \s-2mv\s+2 Shift text left (EDIT) - = \s-2mv\s+2 Reformat - > \s-2mv\s+2 Shift text right (EDIT) - ? \s-2text\s+2 Search backward for a given regular expression (MOVE) - @ \s-2key\s+2 Execute the contents of a cut-buffer as VI commands -count A \s-2inp\s+2 Append at end of the line (EDIT) + < \s'-2'mv\s'+2' Shift text left (EDIT) + = \s'-2'mv\s'+2' Reformat + > \s'-2'mv\s'+2' Shift text right (EDIT) + ? \s'-2'text\s'+2' Search backward for a given regular expression (MOVE) + @ \s'-2'key\s'+2' Execute the contents of a cut-buffer as VI commands +count A \s'-2'inp\s'+2' Append at end of the line (EDIT) count B Move back Word (MOVE) - C \s-2inp\s+2 Change text from the cursor through the end of the line (EDIT) + C \s'-2'inp\s'+2' Change text from the cursor through the end of the line (EDIT) D Delete text from the cursor through the end of the line (EDIT) count E Move end of Word (MOVE) -count F \s-2key\s+2 Move leftward to a given character (MOVE) +count F \s'-2'key\s'+2' Move leftward to a given character (MOVE) count G Move to line #<count> (default is the bottom line) (MOVE) count H Move to home row (the line at the top of the screen) -count I \s-2inp\s+2 Insert at the front of the line (after indents) (EDIT) +count I \s'-2'inp\s'+2' Insert at the front of the line (after indents) (EDIT) count J Join lines, to form one big line (EDIT) K Look up keyword (EXT) count L Move to last row (the line at the bottom of the screen) M Move to middle row N Repeat previous search, but in the opposite direction (MOVE) -count O \s-2inp\s+2 Open up a new line above the current line (EDIT) +count O \s'-2'inp\s'+2' Open up a new line above the current line (EDIT) P Paste text before the cursor (EDIT) Q Quit to EX mode - R \s-2inp\s+2 Overtype (EDIT) -count S \s-2inp\s+2 Change lines, like <count>cc -count T \s-2key\s+2 Move leftward *almost* to a given character (MOVE) + R \s'-2'inp\s'+2' Overtype (EDIT) +count S \s'-2'inp\s'+2' Change lines, like <count>cc +count T \s'-2'key\s'+2' Move leftward *almost* to a given character (MOVE) U Undo all recent changes to the current line V Start marking lines for c/d/y/</>/!/\\ (EXT) count W Move forward <count> Words (MOVE) @@ -112,78 +112,78 @@ count X Delete the character(s) to the left of the cursor (EDIT) count Y Yank text line(s) (copy them into a cut buffer) Z Z Save the file & exit [ [ Move back 1 section (MOVE) - \\ \s-2mv\s+2 Pop-up menu for modifying text (EXT) + \\ \s'-2'mv\s'+2' Pop-up menu for modifying text (EXT) ] ] Move forward 1 section (MOVE) ^ Move to the front of the current line (after indent) (MOVE) count _ Move to the current line - ` \s-2key\s+2 Move to a marked character (MOVE) -count a \s-2inp\s+2 Insert text after the cursor (EDIT) + ` \s'-2'key\s'+2' Move to a marked character (MOVE) +count a \s'-2'inp\s'+2' Insert text after the cursor (EDIT) count b Move back <count> words (MOVE) - c \s-2mv\s+2 Change text (EDIT) - d \s-2mv\s+2 Delete text (EDIT) + c \s'-2'mv\s'+2' Change text (EDIT) + d \s'-2'mv\s'+2' Delete text (EDIT) count e Move forward to the end of the current word (MOVE) -count f \s-2key\s+2 Move rightward to a given character (MOVE) +count f \s'-2'key\s'+2' Move rightward to a given character (MOVE) g --- count h Move left (MOVE) -count i \s-2inp\s+2 Insert text at the cursor (EDIT) +count i \s'-2'inp\s'+2' Insert text at the cursor (EDIT) count j Move down (MOVE) count k Move up (MOVE) count l Move right (MOVE) - m \s-2key\s+2 Mark a line or character + m \s'-2'key\s'+2' Mark a line or character n Repeat the previous search (MOVE) -count o \s-2inp\s+2 Open a new line below the current line (EDIT) +count o \s'-2'inp\s'+2' Open a new line below the current line (EDIT) p Paste text after the cursor (EDIT) q --- -count r \s-2key\s+2 Replace <count> chars by a given character (EDIT) -count s \s-2inp\s+2 Replace <count> chars with text from the user (EDIT) -count t \s-2key\s+2 Move rightward *almost* to a given character (MOVE) +count r \s'-2'key\s'+2' Replace <count> chars by a given character (EDIT) +count s \s'-2'inp\s'+2' Replace <count> chars with text from the user (EDIT) +count t \s'-2'key\s'+2' Move rightward *almost* to a given character (MOVE) u Undo the previous edit command v Start marking characters for c/d/y/</>/!/\\ (EXT) count w Move forward <count> words (MOVE) count x Delete the character that the cursor's on (EDIT) - y \s-2mv\s+2 Yank text (copy it into a cut buffer) - z \s-2key\s+2 Scroll current line to the screen's +=top -=bottom .=middle + y \s'-2'mv\s'+2' Yank text (copy it into a cut buffer) + z \s'-2'key\s'+2' Scroll current line to the screen's +=top -=bottom .=middle count { Move back <count> paragraphs (MOVE) count | Move to column <count> (the leftmost column is 1) count } Move forward <count> paragraphs (MOVE) count ~ Switch a character between uppercase & lowercase (EDIT) DEL --- (usually mapped to shift-X, so it deletes one character) .DE -.IP count +.IP count 0.7i Many commands may be preceded by a count. This is a sequence of digits representing a decimal number. For most commands that use a count, the command is repeated <count> times. The count is always optional, and usually defaults to 1. -.IP key +.IP key 0.7i Some commands require two keystrokes. The first key always determines which command is to be executed. The second key is used as a parameter to the command. -.IP mv +.IP mv 0.7i Some commands (! < > c d y \\ =) operate on text between the cursor and some other position. There are three ways that you can specifify that other position. -.IP +.IP "" 0.7i The first way is to follow the command keystroke with a movement command. For example, "dw" deletes a single word. "d3w" and "3dw" both delete three words. -.IP +.IP "" 0.7i The second way is to type the command keystroke twice. This causes whole lines to be acted upon. For example, ">>" indents the current line. "3>>" indents the current line and the following two lines. -.IP +.IP "" 0.7i The last way is to move the cursor to one end of the text, type 'v' or 'V' to start marking, move the cursor to the other end, and then type the desired command key. -.IP inp +.IP inp 0.7i Many commands allow the user to interactively enter text. See the discussion of "input mode" in the following section. -.IP (EXT) +.IP (EXT) 0.7i These commands are extensions -- the real vi doesn't have them. -.IP (EDIT) +.IP (EDIT) 0.7i These commands affect text, and may be repeated by the "." command. -.IP (MOVE) +.IP (MOVE) 0.7i These commands move the cursor, and may be used to specify the extent of a member of the "mv" class of commands. .NH 2 diff --git a/usr.bin/elvis/elvis.1 b/usr.bin/elvis/elvis.1 index 495bcea2cb..8d10b78a44 100644 --- a/usr.bin/elvis/elvis.1 +++ b/usr.bin/elvis/elvis.1 @@ -72,6 +72,58 @@ is executed as a series of \fIex\fR commands. A file by the same name may be executed in the current directory, too. On non-UNIX systems, ".exrc" is usually an invalid file name; there, the initialization file is called "elvis.rc" instead. +.SH ENVIRONMENT +.IP TERM +This is the name of your terminal's entry in the termcap or terminfo +database. +The list of legal values varies from one system to another. +.IP TERMCAP +Optional. +If your system uses termcap, and the TERMCAP variable is unset, then +\*E will read your terminal's definition from \fB/etc/termcap\fR. +If TERMCAP is set to the full pathname of a file (starting with a '/') +then \*E will look in the named file instead of \fB/etc/termcap\fR. +If TERMCAP is set to a value which doesn't start with a '/', +then its value is assumed to be the full termcap entry for your terminal. +.IP TERMINFO +Optional. +If your system uses terminfo, and the TERMINFO variable is unset, then +\*E will read your terminal's definition from the database in the +\fB/usr/lib/terminfo\fR database. +If TERMINFO is set, then its value is used as the database name to use +instead of \fB/usr/lib/terminfo\fR. +.IP "LINES, COLUMNS" +Optional. +These variables, if set, will override the screen size values given in +the termcap/terminfo for your terminal. +On windowing systems such as X, \*E has other ways of determining the +screen size, so you should probably leave these variables unset. +.IP EXINIT +Optional. +This variable can hold EX commands which will be executed before any .exrc +files. +.IP SHELL +Optional. +The SHELL variable sets the default value for the "shell" option, +which determines which shell program is used to perform wildcard +expansion in file names, and also which is used to execute filters +or external programs. +The default value on UNIX systems is "/bin/sh". +.IP +Note: Under MS-DOS, this variable is called COMSPEC instead of SHELL. +.IP HOME +This variable should be set to the name of your home directory. +\*E looks for its initialization file there; +if HOME is unset then the initialization file will not be executed. +.IP TAGPATH +Optional. +This variable is used by the "ref" program, which is invoked by the shift-K, +control-], and :tag commands. +See "ref" for more information. +.IP "TMP, TEMP" +These optional environment variables are only used in non-UNIX versions +of \*E. +They allow you to supply a directory name to be used for storing temporary files. .SH "SEE ALSO" ctags(1), ref(1), virec(1) .PP diff --git a/usr.bin/elvis/ex.c b/usr.bin/elvis/ex.c index a0e6d34e2e..eda5f1139f 100644 --- a/usr.bin/elvis/ex.c +++ b/usr.bin/elvis/ex.c @@ -5,14 +5,6 @@ * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00043 - * -------------------- ----- ---------------------- - * - * 27 Nov 1992 Felix Gaehtgens Fixed <ESC>:wq! - * */ @@ -60,6 +52,8 @@ static struct } cmdnames[] = { /* cmd name cmd code function arguments */ + {"print", CMD_PRINT, cmd_print, RANGE+NL }, + {"append", CMD_APPEND, cmd_append, FROM+ZERO+BANG }, #ifdef DEBUG {"bug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL}, @@ -76,7 +70,6 @@ static struct {"move", CMD_MOVE, cmd_move, RANGE+EXTRA }, {"next", CMD_NEXT, cmd_next, BANG+NAMEDFS }, {"Next", CMD_PREVIOUS, cmd_next, BANG }, - {"print", CMD_PRINT, cmd_print, RANGE+NL }, {"quit", CMD_QUIT, cmd_xit, BANG }, {"read", CMD_READ, cmd_read, FROM+ZERO+NAMEDF}, {"substitute", CMD_SUBSTITUTE, cmd_substitute, RANGE+EXTRA }, @@ -118,6 +111,9 @@ static struct {"mkexrc", CMD_MKEXRC, cmd_mkexrc, NAMEDF }, #endif {"number", CMD_NUMBER, cmd_print, RANGE+NL }, +#ifndef NO_TAGSTACK + {"pop", CMD_POP, cmd_pop, BANG+WORD1 }, +#endif {"put", CMD_PUT, cmd_put, FROM+ZERO+WORD1 }, {"set", CMD_SET, cmd_set, EXRCOK+EXTRA }, {"shell", CMD_SHELL, cmd_shell, NL }, @@ -128,7 +124,7 @@ static struct {"tag", CMD_TAG, cmd_tag, BANG+WORD1 }, {"version", CMD_VERSION, cmd_version, EXRCOK+NONE }, {"visual", CMD_VISUAL, cmd_edit, BANG+NAMEDF }, - {"wq", CMD_WQUIT, cmd_xit, BANG+NL }, + {"wq", CMD_WQUIT, cmd_xit, NL }, #ifdef DEBUG {"debug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL}, @@ -392,8 +388,8 @@ void doexcmd(cmdbuf) } else if (*scan == '0') { - frommark = tomark = MARK_UNSET; scan++; + frommark = tomark = (*scan ? MARK_UNSET : MARK_FIRST); } else { @@ -424,16 +420,23 @@ void doexcmd(cmdbuf) scan++; } - /* if no command, then just move the cursor to the mark */ + /* Figure out how long the command name is. If no command, then the + * length is 0, which will match the "print" command. + */ if (!*scan) { - if (tomark != MARK_UNSET) - cursor = tomark; - return; + /* if not in ex mode, and both endpoints are at the line, + * then just move to the start of that line without printing + */ + if (mode != MODE_EX && frommark == tomark) + { + if (tomark != MARK_UNSET) + cursor = tomark; + return; + } + cmdlen = 0; } - - /* figure out how long the command name is */ - if (!isalpha(*scan)) + else if (!isalpha(*scan)) { cmdlen = 1; } @@ -672,23 +675,60 @@ int doexrc(filename) int fd; /* file descriptor */ int len; /* length of the ".exrc" file */ +#ifdef CRUNCH + /* small address space - we need to conserve space */ + /* !!! kludge: we use U_text as the buffer. This has the side-effect * of interfering with the shift-U visual command. Disable shift-U. */ U_line = 0L; +#else +# if TINYSTACK +# if TOS || MINT + /* small stack, but big heap. Allocate buffer from heap */ + char *U_text = (char *)malloc(4096); + if (!U_text) + { + return 0; + } +# else + /* small stack - we need to conserve space */ + + /* !!! kludge: we use U_text as the buffer. This has the side-effect + * of interfering with the shift-U visual command. Disable shift-U. + */ + U_line = 0L; +# endif +# else + /* This is how we would *like* to do it -- with a large buffer on the + * stack, so we can handle large .exrc files and also recursion. + */ + char U_text[4096]; +# endif +#endif /* open the file, read it, and close */ fd = open(filename, O_RDONLY); if (fd < 0) { +#if TINYSTACK && (TOS || MINT) + free(U_text); +#endif return 0; } - len = tread(fd, U_text, BLKSIZE); +#if TINYSTACK && (TOS || MINT) + len = tread(fd, U_text, 4096); +#else + len = tread(fd, U_text, sizeof U_text); +#endif close(fd); /* execute the string */ exstring(U_text, len, ctrl('V')); +#if TINYSTACK && (TOS || MINT) + free(U_text); +#endif return 1; } diff --git a/usr.bin/elvis/input.c b/usr.bin/elvis/input.c index 563f64351a..ca5b11581b 100644 --- a/usr.bin/elvis/input.c +++ b/usr.bin/elvis/input.c @@ -28,8 +28,8 @@ static struct _DIG } *digs; char digraph(key1, key2) - char key1; /* the underlying character */ - char key2; /* the second character */ + int key1; /* the underlying character */ + int key2; /* the second character */ { int newkey; REG struct _DIG *dp; @@ -158,7 +158,7 @@ void do_digraph(bang, extra) prev->next = dp->next; else digs = dp->next; - free(dp); + _free_(dp); return; } @@ -211,11 +211,11 @@ void savedigs(fd) * chunk of text with typed-in text. It returns the MARK of the last character * that the user typed in. */ -MARK input(from, to, when, above) +MARK input(from, to, when, delta) MARK from; /* where to start inserting text */ MARK to; /* extent of text to delete */ int when; /* either WHEN_VIINP or WHEN_VIREP */ - int above; /* boolean: take indentation from lower line? */ + int delta; /* 1 to take indent from lower line, -1 for upper, 0 for none */ { char key[2]; /* key char followed by '\0' char */ char *build; /* used in building a newline+indent string */ @@ -287,8 +287,8 @@ MARK input(from, to, when, above) /* handle autoindent of the first line, maybe */ cursor = from; - m = (above ? (cursor + BLKSIZE) : (cursor - BLKSIZE)); - if (*o_autoindent && markidx(m) == 0 + m = cursor + MARK_AT_LINE(delta); + if (delta != 0 && *o_autoindent && markidx(m) == 0 && markline(m) >= 1L && markline(m) <= nlines) { /* Only autoindent blank lines. */ @@ -307,7 +307,7 @@ MARK input(from, to, when, above) { *build = '\0'; add(cursor, tmpblk.c); - cursor += (build - tmpblk.c); + cursor += (int)(build - tmpblk.c); if (cursor > to) to = cursor; } @@ -329,7 +329,7 @@ MARK input(from, to, when, above) build = ptext; if (pline == markline(from)) build += markidx(from); - for (scan = ptext + markidx(cursor); --scan >= build && isalnum(*scan); ) + for (scan = ptext + markidx(cursor); --scan >= build && !isspace(*scan); ) { } scan++; @@ -338,7 +338,7 @@ MARK input(from, to, when, above) key[0] = getkey(when); #endif #ifndef NO_VISIBLE - if (key[0] != '\0' && V_from != MARK_UNSET) + if (key[0] != ctrl('O') && V_from != MARK_UNSET) { msg("Can't modify text during a selection"); beep(); @@ -375,10 +375,11 @@ MARK input(from, to, when, above) * warpmargin, then change the last whitespace * characters on line into a newline */ - if (*o_wrapmargin != 0) + if (*o_wrapmargin) { pfetch(markline(cursor)); - if (idx2col(cursor, ptext, TRUE) > COLS - (*o_wrapmargin & 0xff)) + if (plen == idx2col(cursor, ptext, TRUE) + && plen > COLS - (*o_wrapmargin & 0xff)) { build = tmpblk.c; *build++ = '\n'; @@ -401,11 +402,15 @@ MARK input(from, to, when, above) continue; /*break up line, and we do autoindent if needed*/ - change(m + (scan - ptext), m + (scan - ptext) + 1, tmpblk.c); - cursor = (cursor & ~(BLKSIZE - 1)) - + BLKSIZE - + strlen(tmpblk.c) - 1 - + plen - (scan - ptext) - 1; + change(m + (int)(scan - ptext), m + (int)(scan - ptext) + 1, tmpblk.c); + + /* NOTE: for some reason, MSC 5.10 doesn't + * like for these lines to be combined!!! + */ + cursor = (cursor & ~(BLKSIZE - 1)); + cursor += BLKSIZE; + cursor += strlen(tmpblk.c) - 1; + cursor += plen - (int)(scan - ptext) - 1; /*remove trailing spaces on previous line*/ pfetch(markline(m)); @@ -416,7 +421,7 @@ MARK input(from, to, when, above) if (*scan != ' ' && *scan != '\t') break; } - delete(m + (scan-ptext) + 1, m + plen); + delete(m + (int)(scan - ptext) + 1, m + plen); break; } @@ -559,7 +564,7 @@ MARK input(from, to, when, above) V_from = MARK_UNSET; ChangeText { - m = from = to = cursor = paste(cursor, (*key == 'p'), FALSE); + m = paste(cursor, (*key == 'p'), FALSE); } break; # endif /* !NO_VISIBLE */ @@ -569,8 +574,8 @@ MARK input(from, to, when, above) /* adjust the moved cursor */ if (m != cursor) { - m = adjmove(cursor, m, (*key == 'j' || *key == 'k' ? 0x20 : 0)); - if (*key == '$' || (*key == 'l' && m <= cursor)) + m = adjmove(cursor, m, (*key == 'j' || *key == 'k' ? NCOL|FINL : FINL)); + if (plen && (*key == '$' || (*key == 'l' && m <= cursor))) { m++; } @@ -696,11 +701,21 @@ MARK input(from, to, when, above) } /* remove indent from this line, if blank */ - if ((scan - ptext) >= markidx(cursor) && plen > 0) + if ((int)(scan - ptext) >= markidx(cursor) && plen > 0) { to = cursor &= ~(BLKSIZE - 1); - delete(cursor, cursor + plen); + delete(cursor, cursor + (int)(scan - ptext)); + } + +#if 0 + /* advance "to" past whitespace at the cursor */ + if (to >= cursor) + { + for (scan = ptext + markidx(cursor), to = cursor; *scan == ' ' || *scan == '\t'; scan++, to++) + { + } } +#endif } *build = 0; if (cursor >= to && when != WHEN_VIREP) @@ -727,7 +742,11 @@ MARK input(from, to, when, above) { cutname('.'); } - to = cursor = paste(cursor, FALSE, TRUE) + 1L; + m = paste(cursor, FALSE, TRUE); + if (m != MARK_UNSET) + { + to = cursor = m + 1L; + } break; case ctrl('V'): diff --git a/usr.bin/elvis/main.c b/usr.bin/elvis/main.c index 2192a2efec..f93f780225 100644 --- a/usr.bin/elvis/main.c +++ b/usr.bin/elvis/main.c @@ -20,8 +20,7 @@ #include <setjmp.h> #include "vi.h" -extern trapint(); /* defined below */ -extern char *getenv(); +extern SIGTYPE trapint(); /* defined below */ jmp_buf jmpenv; #ifndef NO_DIGRAPH @@ -89,33 +88,33 @@ void main(argc, argv) /* arrange for deadly signals to be caught */ # ifdef SIGHUP - signal(SIGHUP, (void(*)()) deathtrap); + signal(SIGHUP, deathtrap); # endif # ifndef DEBUG # ifdef SIGILL - signal(SIGILL, (void(*)()) deathtrap); + signal(SIGILL, deathtrap); # endif # ifdef SIGBUS - signal(SIGBUS, (void(*)()) deathtrap); + signal(SIGBUS, deathtrap); # endif # ifdef SIGSEGV - signal(SIGSEGV, (void(*)()) deathtrap); + signal(SIGSEGV, deathtrap); # endif # ifdef SIGSYS - signal(SIGSYS, (void(*)()) deathtrap); + signal(SIGSYS, deathtrap); # endif # endif /* !DEBUG */ # ifdef SIGPIPE - signal(SIGPIPE, (void(*)()) deathtrap); + signal(SIGPIPE, deathtrap); # endif # ifdef SIGTERM - signal(SIGTERM, (void(*)()) deathtrap); + signal(SIGTERM, deathtrap); # endif # ifdef SIGUSR1 - signal(SIGUSR1, (void(*)()) deathtrap); + signal(SIGUSR1, deathtrap); # endif # ifdef SIGUSR2 - signal(SIGUSR2, (void(*)()) deathtrap); + signal(SIGUSR2, deathtrap); # endif /* initialize the options - must be done after initscr(), so that @@ -187,11 +186,11 @@ void main(argc, argv) case 'L': case 'r': /* recover */ - msg("Use the `elvisrecover` program to recover lost files"); + msg("Use the `elvrec` program to recover lost files"); endmsgs(); refresh(); endwin(); - exit(0); + exit(1); break; case 't': /* tag */ @@ -309,7 +308,7 @@ void main(argc, argv) doexrc(SYSEXRC); #endif #ifdef HMEXRC - str = getenv("HOME"); + str = gethome(argv[0]); if (str && *str) { strcpy(tmpblk.c, str); @@ -346,7 +345,7 @@ void main(argc, argv) blkinit(); if (tag) { - cmd_tag(MARK_FIRST, MARK_FIRST, CMD_TAG, 0, tag); + cmd_tag(MARK_UNSET, MARK_FIRST, CMD_TAG, 0, tag); } #ifndef NO_ERRLIST else if (err) @@ -386,7 +385,7 @@ void main(argc, argv) /* Maybe we just aborted a change? */ abortdo(); } - signal(SIGINT, (void(*)()) trapint); + signal(SIGINT, trapint); switch (mode) { @@ -419,13 +418,13 @@ void main(argc, argv) refresh(); endwin(); - exit(0); + exit(exitcode); /*NOTREACHED*/ } /*ARGSUSED*/ -int trapint(signo) +SIGTYPE trapint(signo) int signo; { beep(); @@ -434,16 +433,11 @@ int trapint(signo) #if OSK sigmask(-1); #endif -#if TURBOC || __GNUC__ - signal(signo, (void (*)())trapint); -#else signal(signo, trapint); -#endif doingglobal = FALSE; longjmp(jmpenv, 1); - - return 0; + /*NOTREACHED*/ } @@ -502,7 +496,7 @@ static char digtable[][4] = "" }; -static init_digraphs() +static int init_digraphs() { int i; diff --git a/usr.bin/elvis/misc.c b/usr.bin/elvis/misc.c index c9e0f689d2..f193e5cd26 100644 --- a/usr.bin/elvis/misc.c +++ b/usr.bin/elvis/misc.c @@ -83,7 +83,7 @@ char *fetchline(line) /* error message from the regexp code */ -void regerror(txt) +void regerr(txt) char *txt; /* an error message */ { msg("RE error: %s", txt); diff --git a/usr.bin/elvis/modify.c b/usr.bin/elvis/modify.c index af79146010..79b910366e 100644 --- a/usr.bin/elvis/modify.c +++ b/usr.bin/elvis/modify.c @@ -378,7 +378,7 @@ void add(atmark, newtext) { /* hey, we probably can! Get the following block & see... */ following = blkget(blkno + 1); - if (strlen(following->c) + (build - blk->c) < BLKSIZE - 1) + if (strlen(following->c) + (build - blk->c) < (unsigned)(BLKSIZE - 1)) { /* we can! Copy text from following to blk */ for (scan = following->c; *scan; ) diff --git a/usr.bin/elvis/move1.c b/usr.bin/elvis/move1.c index f57ab9a0a7..604b83d963 100644 --- a/usr.bin/elvis/move1.c +++ b/usr.bin/elvis/move1.c @@ -17,7 +17,7 @@ MARK m_updnto(m, cnt, cmd) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ - char cmd; /* the command character */ + int cmd; /* the command character */ { DEFAULT(cmd == 'G' ? nlines : 1L); @@ -245,6 +245,10 @@ MARK m_sentence(m, cnt, cmd) { REG char *text; REG long l; +#ifndef CRUNCH + /* figure out where the paragraph boundary is */ + MARK pp = m_paragraph(m, 1L, cmd=='(' ? '{' : '}'); +#endif DEFAULT(1); @@ -277,9 +281,9 @@ MARK m_sentence(m, cnt, cmd) /* move forward, wrap at end of line */ if (!text[0]) { - if (l >= nlines) + if (l == nlines) { - return MARK_UNSET; + goto BreakBreak; } l++; pfetch(l); @@ -297,9 +301,9 @@ MARK m_sentence(m, cnt, cmd) { do { - if (l <= 1) + if (l == 1L) { - return MARK_FIRST; + goto BreakBreak; } l--; pfetch(l); @@ -313,12 +317,25 @@ MARK m_sentence(m, cnt, cmd) } } while (!isperiod(text)); } +BreakBreak: /* construct a mark for this location */ m = buildmark(text); /* move forward to the first word of the next sentence */ m = m_fword(m, 1L, 'w', '\0'); + if (m == MARK_UNSET) + { + m = MARK_EOF; + } + +#ifndef CRUNCH + /* don't cross the paragraph boundary */ + if (pp && ((cmd=='(') ? (m<pp) : (m>pp))) + { + m = pp; + } +#endif return m; } @@ -334,11 +351,15 @@ MARK m_paragraph(m, cnt, cmd) long l, ol; /* current line number, original line number */ int dir; /* -1 if we're moving up, or 1 if down */ char col0; /* character to expect in column 0 */ + long limit; /* line where searching must stop */ #ifndef NO_SENTENCE # define SENTENCE(x) (x) char *list; /* either o_sections or o_paragraph */ #else # define SENTENCE(x) +#endif +#ifndef CRUNCH + MARK ss; #endif DEFAULT(1); @@ -350,12 +371,26 @@ MARK m_paragraph(m, cnt, cmd) dir = -1; col0 = '\0'; SENTENCE(list = o_paragraphs); +#ifndef CRUNCH + ss = m_paragraph(m, 1L, '<'); + if (ss) + limit = markline(ss); + else +#endif + limit = 1L; break; case '}': dir = 1; col0 = '\0'; SENTENCE(list = o_paragraphs); +#ifndef CRUNCH + ss = m_paragraph(m, 1L, '>'); + if (ss) + limit = markline(ss); + else +#endif + limit = nlines; break; case '[': @@ -363,9 +398,12 @@ MARK m_paragraph(m, cnt, cmd) { return MARK_UNSET; } + /* fall through... */ + case '<': dir = -1; col0 = '{'; SENTENCE(list = o_sections); + limit = 1L; break; case ']': @@ -373,18 +411,21 @@ MARK m_paragraph(m, cnt, cmd) { return MARK_UNSET; } + /* fall through... */ + case '>': dir = 1; col0 = '{'; SENTENCE(list = o_sections); + limit = nlines; break; } ol = l = markline(m); /* for each paragraph that we want to travel through... */ - while (l > 0 && l <= nlines && cnt-- > 0) + while (l != limit && cnt-- > 0) { /* skip blank lines between paragraphs */ - while (l > 0 && l <= nlines && col0 == *(text = fetchline(l))) + while (l != limit && col0 == *(text = fetchline(l))) { l += dir; } @@ -407,22 +448,14 @@ MARK m_paragraph(m, cnt, cmd) } #endif l += dir; - } while (l > 0 && l <= nlines && col0 != *(text = fetchline(l))); + } while (l != limit && col0 != *(text = fetchline(l))); BreakBreak: ; } - if (l > nlines) - { - m = MARK_LAST; - } - else if (l <= 0) - { - m = MARK_FIRST; - } - else - { - m = MARK_AT_LINE(l); - } + m = MARK_AT_LINE(l); +#ifdef DEBUG2 + debout("m_paragraph() returning %ld.%d\n", markline(m), markidx(m)); +#endif return m; } diff --git a/usr.bin/elvis/move2.c b/usr.bin/elvis/move2.c index b664153cb2..2c768cd579 100644 --- a/usr.bin/elvis/move2.c +++ b/usr.bin/elvis/move2.c @@ -12,11 +12,19 @@ #include "config.h" #include "vi.h" -#include "regexp.h" +#ifdef REGEX +# include <regex.h> +#else +# include "regexp.h" +#endif extern long atol(); +#ifdef REGEX +static regex_t *re = NULL; /* compiled version of the pattern to search for */ +#else static regexp *re; /* compiled version of the pattern to search for */ +#endif static prevsf; /* boolean: previous search direction was forward? */ #ifndef NO_EXTENSIONS @@ -44,38 +52,51 @@ MARK m_wsrch(word, m, cnt) } #endif -MARK m_nsrch(m) +MARK m_nsrch(m, cnt, cmd) MARK m; /* where to start searching */ + long cnt; /* number of searches to do */ + int cmd; /* command character -- 'n' or 'N' */ { - if (prevsf) - { - m = m_fsrch(m, (char *)0); - prevsf = TRUE; - } - else - { - m = m_bsrch(m, (char *)0); - prevsf = FALSE; - } - return m; -} + int oldprevsf; /* original value of prevsf, so we can fix any changes */ -MARK m_Nsrch(m) - MARK m; /* where to start searching */ -{ - if (prevsf) + DEFAULT(1L); + + /* clear the bottom line. In particular, we want to loose any + * "(wrapped)" notice. + */ + move(LINES - 1, 0); + clrtoeol(); + + /* if 'N' command, then invert the "prevsf" variable */ + oldprevsf = prevsf; + if (cmd == 'N') { - m = m_bsrch(m, (char *)0); - prevsf = TRUE; + prevsf = !prevsf; } - else + + /* search forward if prevsf -- i.e., if previous search was forward */ + while (--cnt >= 0L && m != MARK_UNSET) { - m = m_fsrch(m, (char *)0); - prevsf = FALSE; + if (prevsf) + { + m = m_fsrch(m, (char *)0); + } + else + { + m = m_bsrch(m, (char *)0); + } } + + /* restore the old value of prevsf -- if cmd=='N' then it was inverted, + * and the m_fsrch() and m_bsrch() functions force it to a (possibly + * incorrect) value. The value of prevsf isn't supposed to be changed + * at all here! + */ + prevsf = oldprevsf; return m; } + MARK m_fsrch(m, ptrn) MARK m; /* where to start searching */ char *ptrn; /* pattern to search for */ @@ -84,6 +105,11 @@ MARK m_fsrch(m, ptrn) char *line; /* text of line to be searched */ int wrapped;/* boolean: has our search wrapped yet? */ int pos; /* where we are in the line */ +#ifdef REGEX + regex_t *optpat(); + regmatch_t rm[SE_MAX]; + int n; +#endif #ifndef CRUNCH long delta = INFINITY;/* line offset, for things like "/foo/+1" */ #endif @@ -103,11 +129,17 @@ MARK m_fsrch(m, ptrn) #endif ptrn++; + +#ifdef REGEX + /* XXX where to free re? */ + re = optpat(ptrn); +#else /* free the previous pattern */ - if (re) free(re); + if (re) _free_(re); /* compile the pattern */ re = regcomp(ptrn); +#endif if (!re) { return MARK_UNSET; @@ -156,7 +188,11 @@ MARK m_fsrch(m, ptrn) line = fetchline(l); /* check this line */ +#ifdef REGEX + if (!regexec(re, &line[pos], SE_MAX, rm, (pos == 0) ? 0 : REG_NOTBOL)) +#else if (regexec(re, &line[pos], (pos == 0))) +#endif { /* match! */ if (wrapped && *o_warn) @@ -174,7 +210,11 @@ MARK m_fsrch(m, ptrn) return MARK_AT_LINE(l); } #endif +#ifdef REGEX + return MARK_AT_LINE(l) + pos + rm[0].rm_so; +#else return MARK_AT_LINE(l) + (int)(re->startp[0] - line); +#endif } pos = 0; } @@ -194,6 +234,11 @@ MARK m_bsrch(m, ptrn) int pos; /* last acceptable idx for a match on this line */ int last; /* remembered idx of the last acceptable match on this line */ int try; /* an idx at which we strat searching for another match */ +#ifdef REGEX + regex_t *optpat(); + regmatch_t rm[SE_MAX]; + int n; +#endif #ifndef CRUNCH long delta = INFINITY;/* line offset, for things like "/foo/+1" */ #endif @@ -213,11 +258,16 @@ MARK m_bsrch(m, ptrn) #endif ptrn++; +#ifdef REGEX + /* XXX where to free re? */ + re = optpat(ptrn); +#else /* free the previous pattern, if any */ - if (re) free(re); + if (re) _free_(re); /* compile the pattern */ re = regcomp(ptrn); +#endif if (!re) { return MARK_UNSET; @@ -253,16 +303,31 @@ MARK m_bsrch(m, ptrn) line = fetchline(l); /* check this line */ +#ifdef REGEX + if (!regexec(re, line, SE_MAX, rm, 0) && rm[0].rm_so < pos) +#else if (regexec(re, line, 1) && (int)(re->startp[0] - line) < pos) +#endif { + try = 0; /* match! now find the last acceptable one in this line */ do { +#ifdef REGEX + last = try + rm[0].rm_so; + try += rm[0].rm_eo; +#else last = (int)(re->startp[0] - line); try = (int)(re->endp[0] - line); +#endif } while (try > 0 +#ifdef REGEX + && !regexec(re, &line[try], SE_MAX, rm, REG_NOTBOL) + && try + rm[0].rm_so < pos); +#else && regexec(re, &line[try], FALSE) && (int)(re->startp[0] - line) < pos); +#endif if (wrapped && *o_warn) msg("(wrapped)"); diff --git a/usr.bin/elvis/move3.c b/usr.bin/elvis/move3.c index a9e46ea8a9..69949eed36 100644 --- a/usr.bin/elvis/move3.c +++ b/usr.bin/elvis/move3.c @@ -21,7 +21,7 @@ static char prev_key; /* sought cvhar from previous [fFtT] */ MARK m__ch(m, cnt, cmd) MARK m; /* current position */ long cnt; - char cmd; /* command: either ',' or ';' */ + int cmd; /* command: either ',' or ';' */ { MARK (*tmp)(); @@ -52,7 +52,7 @@ MARK m__ch(m, cnt, cmd) MARK m_fch(m, cnt, key) MARK m; /* where to search from */ long cnt; - char key; /* what to search for */ + int key; /* what to search for */ { REG char *text; @@ -83,7 +83,7 @@ MARK m_fch(m, cnt, key) MARK m_Fch(m, cnt, key) MARK m; /* where to search from */ long cnt; - char key; /* what to search for */ + int key; /* what to search for */ { REG char *text; @@ -114,50 +114,32 @@ MARK m_Fch(m, cnt, key) MARK m_tch(m, cnt, key) MARK m; /* where to search from */ long cnt; - char key; /* what to search for */ + int key; /* what to search for */ { - /* skip the adjacent char */ - pfetch(markline(m)); - if (plen <= markidx(m)) - { - return MARK_UNSET; - } - m++; - m = m_fch(m, cnt, key); - if (m == MARK_UNSET) + if (m != MARK_UNSET) { - return MARK_UNSET; + prevfwdfn = m_tch; + prevrevfn = m_Tch; + m--; } - - prevfwdfn = m_tch; - prevrevfn = m_Tch; - - return m - 1; + return m; } /* move backward within this line almost to previous occurrence of key */ MARK m_Tch(m, cnt, key) MARK m; /* where to search from */ long cnt; - char key; /* what to search for */ + int key; /* what to search for */ { - /* skip the adjacent char */ - if (markidx(m) == 0) - { - return MARK_UNSET; - } - m--; - m = m_Fch(m, cnt, key); if (m == MARK_UNSET) { - return MARK_UNSET; + prevfwdfn = m_Tch; + prevrevfn = m_tch; + m++; } - prevfwdfn = m_Tch; - prevrevfn = m_tch; - - return m + 1; + return m; } #endif diff --git a/usr.bin/elvis/move4.c b/usr.bin/elvis/move4.c index a31ef881f3..d42082d653 100644 --- a/usr.bin/elvis/move4.c +++ b/usr.bin/elvis/move4.c @@ -136,6 +136,12 @@ MARK m_scroll(m, cnt, key) int key; /* keystroke that causes this movement */ { MARK tmp; /* a temporary mark, used as arg to redraw() */ +#ifndef CRUNCH + int savenearscroll; + + savenearscroll = *o_nearscroll; + *o_nearscroll = LINES; +#endif /* adjust cnt, and maybe *o_scroll, depending of key */ switch (key) @@ -207,5 +213,8 @@ MARK m_scroll(m, cnt, key) break; } +#ifndef CRUNCH + *o_nearscroll = savenearscroll; +#endif return m; } diff --git a/usr.bin/elvis/move5.c b/usr.bin/elvis/move5.c index aec5e785b0..408bac379c 100644 --- a/usr.bin/elvis/move5.c +++ b/usr.bin/elvis/move5.c @@ -36,7 +36,7 @@ MARK m_fword(m, cnt, cmd, prevkey) */ if (cnt == 1L && prevkey == 'c' && isspace(*text)) { - return m + 1L; + return m; } #endif diff --git a/usr.bin/elvis/opts.c b/usr.bin/elvis/opts.c index c9c32c0faa..0d2d8d63f6 100644 --- a/usr.bin/elvis/opts.c +++ b/usr.bin/elvis/opts.c @@ -18,7 +18,6 @@ #ifndef NULL #define NULL (char *)0 #endif -extern char *getenv(); /* maximum width to permit for strings, including ="" */ #define MAXWIDTH 20 @@ -28,7 +27,7 @@ char o_autoindent[1] = {FALSE}; char o_autoprint[1] = {TRUE}; char o_autotab[1] = {TRUE}; char o_autowrite[1] = {FALSE}; -char o_columns[3] = {80, 32, 255}; +char o_columns[3] = {80, 32, ~0}; char o_directory[30] = TMPDIR; char o_edcompatible[1] = {FALSE}; char o_equalprg[80] = {"fmt"}; @@ -45,7 +44,7 @@ char o_remap[1] = {TRUE}; char o_report[3] = {5, 1, 127}; char o_scroll[3] = {12, 1, 127}; char o_shell[60] = SHELL; -char o_shiftwidth[3] = {8, 1, 255}; +char o_shiftwidth[3] = {8, 1, ~0}; char o_sidescroll[3] = {8, 1, 40}; char o_sync[1] = {NEEDSYNC}; char o_tabstop[3] = {8, 1, 40}; @@ -59,12 +58,14 @@ char o_beautify[1] = {FALSE}; char o_exrc[1] = {FALSE}; char o_mesg[1] = {TRUE}; char o_more[1] = {TRUE}; +char o_nearscroll[3] = {15, 0, ~0}; char o_novice[1] = {FALSE}; char o_prompt[1] = {TRUE}; char o_taglength[3] = {0, 0, 30}; +char o_tags[256] = {"tags"}; char o_terse[1] = {FALSE}; char o_window[3] = {0, 1, 24}; -char o_wrapmargin[3] = {0, 0, 255}; +char o_wrapmargin[3] = {0, 0, ~0}; char o_writeany[1] = {FALSE}; #endif @@ -123,6 +124,11 @@ char o_showmatch[1] = {FALSE}; char o_smd[1] = {FALSE}; #endif +#ifndef NO_TAGSTACK +char o_tagstack[1] = {TRUE}; +#endif + + /* The following describes the names & types of all options */ #define BOOL 0 @@ -199,6 +205,7 @@ struct #endif #ifndef CRUNCH { "more", "mo", BOOL, CANSET, o_more }, + { "nearscroll", "ns", NUM, CANSET, o_nearscroll }, { "novice", "nov", BOOL, CANSET, o_novice }, #endif { "number", "nu", BOOL, CANSET|MR, o_number }, @@ -234,6 +241,10 @@ struct { "tabstop", "ts", NUM, CANSET|MR, o_tabstop }, #ifndef CRUNCH { "taglength", "tl", NUM, CANSET, o_taglength }, + { "tags", "tag", STR, CANSET, o_tags }, +#endif +#ifndef NO_TAGSTACK + { "tagstack", "tgs", BOOL, CANSET, o_tagstack }, #endif { "term", "te", STR, SET, o_term }, #ifndef CRUNCH @@ -304,6 +315,7 @@ void initopts() { o_window[0] = o_window[2] = *o_lines; } + *o_nearscroll = *o_lines; #endif /* disable the flash option if we don't know how to do a flash */ @@ -685,7 +697,11 @@ void setopts(assignments) /* change the variable */ if (!opts[i].name) { - msg("invalid option name \"%s\"", name); + /* only complain about unknown options if we're editing + * a file; i.e., if we're not executing the .exrc now. + */ + if (tmpfd >= 0) + msg("invalid option name \"%s\"", name); } else if ((opts[i].flags & CANSET) != CANSET) { diff --git a/usr.bin/elvis/prsvunix.c b/usr.bin/elvis/prsvunix.c deleted file mode 100644 index e03e077526..0000000000 --- a/usr.bin/elvis/prsvunix.c +++ /dev/null @@ -1,108 +0,0 @@ -/* prsvunix.c */ - -/* This file contains the UNIX-specific parts of the "elvprsv" program. */ - -#if OSK -#define ELVPRSV -#include "osk.c" -#else -#include <sys/stat.h> -#include <pwd.h> -#endif -#ifndef __386BSD__ -extern struct passwd *getpwuid(); -#endif - -/* This variable is used to add extra error messages for mail sent to root */ -char *ps; - -/* This function returns the login name of the owner of a file */ -char *ownername(filename) - char *filename; /* name of a file */ -{ - struct stat st; - struct passwd *pw; - - /* stat the file, to get its uid */ - if (stat(filename, &st) < 0) - { - ps = "stat() failed"; - return "root"; - } - - /* get the /etc/passwd entry for that user */ - pw = getpwuid(st.st_uid); - if (!pw) - { - ps = "uid not found in password file"; - return "root"; - } - - /* return the user's name */ - return pw->pw_name; -} - - -/* This function sends a mail message to a given user, saying that a file - * has been preserved. - */ -void mail(user, file, when) - char *user; /* name of user who should receive the mail */ - char *file; /* name of original text file that was preserved */ - char *when; /* description of why the file was preserved */ -{ - char cmd[80];/* buffer used for constructing a "mail" command */ - FILE *m, *popen(); /* stream used for giving text to the "mail" program */ - char *base; /* basename of the file */ - - /* separate the directory name from the basename. */ - for (base = file + strlen(file); --base > file && *base != SLASH; ) - { - } - if (*base == SLASH) - { - *base++ = '\0'; - } - - /* for anonymous buffers, pretend the name was "foo" */ - if (!strcmp(base, "*")) - { - base = "foo"; - } - - /* open a pipe to the "mail" program */ -#if OSK - sprintf(cmd, "mail \"-s=%s preserved!\" %s", base, user); -#else /* ANY_UNIX */ - sprintf(cmd, "mail %s >/dev/null 2>/dev/null", user); -#endif - m = popen(cmd, "w"); - if (!m) - { - /* Can't send mail! Hope the user figures it out. */ - return; - } - - /* Tell the user that the file was preserved */ - fprintf(m, "A version of your file \"%s%c%s\"\n", file, SLASH, base); - fprintf(m, "was preserved when %s.\n", when); - fprintf(m, "To recover this file, do the following:\n"); - fprintf(m, "\n"); -#if OSK - fprintf(m, " chd %s\n", file); -#else /* ANY_UNIX */ - fprintf(m, " cd %s\n", file); -#endif - fprintf(m, " elvisrecover %s\n", base); - fprintf(m, "\n"); - fprintf(m, "With fond wishes for a speedy recovery,\n"); - fprintf(m, " Elvis\n"); - if (ps) - { - fprintf(m, "\nP.S. %s\n", ps); - ps = (char *)0; - } - - /* close the stream */ - pclose(m); -} diff --git a/usr.bin/elvis/recycle.c b/usr.bin/elvis/recycle.c index 421603610a..c3a2a8fa4a 100644 --- a/usr.bin/elvis/recycle.c +++ b/usr.bin/elvis/recycle.c @@ -180,4 +180,31 @@ dbfree(ptr, file, line) } free(ptr - sizeof(long)); } + +dbcheckmem(file, line) + char *file; + int line; +{ + int i, j; + + for (i = j = 0; i < MAXALLOC && allocated[i]; i++) + { + if (((long *)allocated[i])[-1] != MEMMAGIC) + { + if (!j) endwin(); + fprintf(stderr, "\r\n%s(%d): underflowed malloc space, allocated at %s(%d)\n", file, line, fromfile[i], fromline[i]); + j++; + } + if (((long *)allocated[i])[sizes[i]] != MEMMAGIC) + { + if (!j) endwin(); + fprintf(stderr, "\r\n%s(%d): overflowed malloc space, allocated at %s(%d)\n", file, line, fromfile[i], fromline[i]); + j++; + } + } + if (j) + { + abort(); + } +} #endif diff --git a/usr.bin/elvis/redraw.c b/usr.bin/elvis/redraw.c index ae923cf60a..358eaec7fc 100644 --- a/usr.bin/elvis/redraw.c +++ b/usr.bin/elvis/redraw.c @@ -19,6 +19,11 @@ #include "config.h" #include "vi.h" +#ifdef CRUNCH +# define NEAR LINES +#else +# define NEAR (*o_nearscroll&0xff) +#endif /* This variable contains the line number that smartdrawtext() knows best */ static long smartlno; @@ -367,7 +372,7 @@ static void drawtext(text, lno, clr) /* show the line number, if necessary */ if (*o_number) { - sprintf(numstr, "%6ld |", lno); + sprintf(numstr, "%6ld ", lno); qaddstr(numstr); } @@ -494,7 +499,7 @@ static void drawtext(text, lno, clr) { qaddch(' '); col++; - } while (col < i); + } while (col < i && col < limitcol); } else #endif /* !NO_VISIBLE */ @@ -522,11 +527,16 @@ static void drawtext(text, lno, clr) { qaddch(' '); col++; - } while (col < i); + } while (col < i && col < limitcol); } } else /* tab ending after screen? next line! */ { +#ifdef CRUNCH + /* needed at least when scrolling the screen right -nox */ + if (clr && col < limitcol) + clrtoeol(); +#endif col = limitcol; if (has_AM) { @@ -798,7 +808,7 @@ static void smartdrawtext(text, lno, showit) /* show the line number, if necessary */ if (*o_number) { - sprintf(numstr, "%6ld |", lno); + sprintf(numstr, "%6ld ", lno); qaddstr(numstr); } @@ -980,7 +990,7 @@ void redraw(curs, inputting) smartdrawtext(text, l, (chgs != changes)); } } - else if (l < topline && l > topline - LINES && (has_SR || has_AL)) + else if (l < topline && l >= topline - NEAR && (has_SR || has_AL)) { /* near top - scroll down */ if (!mustredraw) @@ -1012,7 +1022,7 @@ void redraw(curs, inputting) redrawrange(0L, INFINITY, INFINITY); } } - else if (l > topline && l < botline + LINES) + else if (l > topline && l <= botline + NEAR) { /* near bottom -- scroll up */ if (!mustredraw) @@ -1038,12 +1048,13 @@ void redraw(curs, inputting) else { /* distant line - center it & force a redraw */ - topline = l - (LINES / 2) - 1; + topline = l - (LINES - 1) / 2; if (topline < 1) { topline = 1; } redrawrange(0L, INFINITY, INFINITY); + smartlno = 0L; changes++; } diff --git a/usr.bin/elvis/regexp.c b/usr.bin/elvis/regexp.c index 91db71af85..43a8672a13 100644 --- a/usr.bin/elvis/regexp.c +++ b/usr.bin/elvis/regexp.c @@ -33,10 +33,69 @@ #include "config.h" #include "ctype.h" #include "vi.h" -#include "regexp.h" +#ifdef REGEX +# include <regex.h> +#else +# include "regexp.h" +#endif + + +#ifdef REGEX +extern int patlock; /* from cmd_substitute() module */ +static regex_t *previous = NULL; /* the previous regexp, used when null regexp is given */ +regex_t * +optpat(s) + char *s; +{ + static char *neuter(); + + int n; + if (*s == '\0') { + if (!previous) regerr("RE error: no previous pattern"); + return previous; + } else if (previous && !patlock) + regfree(previous); + else if ((previous = (regex_t *) malloc(sizeof(regex_t))) == NULL) { + regerr("RE error: out of memory"); + return previous; + } + patlock = 0; + if (n = regcomp(previous, *o_magic ? s : neuter(s), + *o_ignorecase ? REG_ICASE : 0)) { + regerr("RE error: %d", n); + free(previous); + return previous = NULL; + } + return previous; +} +/* escape BRE meta-characters in a string */ +static char * +neuter(s) + char *s; +{ + static char *hd = NULL; + + char *t; + int n = strlen(s); + + free(hd); + if ((hd = t = (char *) malloc(n + n + 1)) == NULL) + return NULL; + if (*s == '^') + *t++ = *s++; + while (*s) { + if (*s == '.' || *s == '\\' || *s == '[' || *s == '*') + *t++ = '\\'; + *t++ = *s++; + } + *t = '\0'; + return hd; +} + +#else static char *previous; /* the previous regexp, used when null regexp is given */ @@ -78,7 +137,7 @@ static char *retext; /* points to the text being compiled */ /* error-handling stuff */ jmp_buf errorhandler; -#define FAIL(why) regerror(why); longjmp(errorhandler, 1) +#define FAIL(why) regerr(why); longjmp(errorhandler, 1) @@ -93,6 +152,8 @@ static char *makeclass(text, bmap) int complement = 0; + checkmem(); + /* zero the bitmap */ for (i = 0; bmap && i < 32; i++) { @@ -119,7 +180,7 @@ static char *makeclass(text, bmap) } /* add each character in the span to the bitmap */ - for (i = text[0]; bmap && i <= text[2]; i++) + for (i = UCHAR(text[0]); bmap && (unsigned)i <= UCHAR(text[2]); i++) { bmap[i >> 3] |= (1 << (i & 7)); } @@ -133,7 +194,7 @@ static char *makeclass(text, bmap) i = *text++; if (bmap) { - bmap[i >> 3] |= (1 << (i & 7)); + bmap[UCHAR(i) >> 3] |= (1 << (UCHAR(i) & 7)); } } } @@ -153,6 +214,8 @@ static char *makeclass(text, bmap) } } + checkmem(); + return text; } @@ -171,6 +234,10 @@ static int gettoken(sptr, re) int c; c = **sptr; + if (!c) + { + return c; + } ++*sptr; if (c == '\\') { @@ -353,21 +420,30 @@ regexp *regcomp(exp) int token; int peek; char *build; +#if __STDC__ + volatile +#endif regexp *re; #ifndef CRUNCH int from; int to; int digit; #endif +#ifdef DEBUG + int calced; +#endif + + checkmem(); /* prepare for error handling */ re = (regexp *)0; if (setjmp(errorhandler)) { + checkmem(); if (re) { - free(re); + _free_(re); } return (regexp *)0; } @@ -384,20 +460,26 @@ regexp *regcomp(exp) else /* non-empty regexp given, so remember it */ { if (previous) - free(previous); + _free_(previous); previous = (char *)malloc((unsigned)(strlen(exp) + 1)); if (previous) strcpy(previous, exp); } /* allocate memory */ + checkmem(); class_cnt = 0; start_cnt = 1; end_sp = 0; retext = exp; +#ifdef DEBUG + calced = calcsize(exp); + size = calced + sizeof(regexp); +#else size = calcsize(exp) + sizeof(regexp) + 10; /* !!! 10 bytes for slop */ +#endif #ifdef lint - re = ((regexp *)0) + size; + re = (regexp *)0; #else re = (regexp *)malloc((unsigned)size); #endif @@ -405,6 +487,7 @@ regexp *regcomp(exp) { FAIL("Not enough memory for this RE"); } + checkmem(); /* compile it */ build = &re->program[1 + 32 * class_cnt]; @@ -541,6 +624,7 @@ regexp *regcomp(exp) *build++ = token; } } + checkmem(); /* end it with a \) which MUST MATCH the opening \( */ ADD_META(build, M_END(0)); @@ -549,6 +633,15 @@ regexp *regcomp(exp) FAIL("Not enough \\)s"); } +#ifdef DEBUG + if ((int)(build - re->program) != calced) + { + msg("regcomp error: calced=%d, actual=%d", calced, (int)(build - re->program)); + getkey(0); + } +#endif + + checkmem(); return re; } @@ -577,7 +670,7 @@ int match1(re, ch, token) } else if (IS_CLASS(token)) { - if (re->program[1 + 32 * (token - M_CLASS(0)) + (ch >> 3)] & (1 << (ch & 7))) + if (re->program[1 + 32 * (token - M_CLASS(0)) + (UCHAR(ch) >> 3)] & (1 << (UCHAR(ch) & 7))) return 0; } else if (ch == token || *o_ignorecase && tolower(ch) == tolower(token)) @@ -733,6 +826,8 @@ int regexec(re, str, bol) int len; /* length of the string */ REG char *here; + checkmem(); + /* if must start at the beginning of a line, and this isn't, then fail */ if (re->bol && !bol) { @@ -780,6 +875,7 @@ int regexec(re, str, bol) } /* if we didn't fail, then we must have succeeded */ + checkmem(); return 1; } @@ -802,7 +898,7 @@ regexp *regcomp(exp) #endif if (!re) { - regerror("Could not malloc a regexp structure"); + regerr("Could not malloc a regexp structure"); return (regexp *)0; } @@ -852,7 +948,7 @@ regexp *regcomp(exp) } else { - regerror("extra \\ at end of regular expression"); + regerr("extra \\ at end of regular expression"); } break; @@ -932,3 +1028,4 @@ int regexec(prog, string, bolflag) return rc == 1; } #endif +#endif /* !REGEX */ diff --git a/usr.bin/elvis/regexp.h b/usr.bin/elvis/regexp.h index 6d043b027d..eb8726a67f 100644 --- a/usr.bin/elvis/regexp.h +++ b/usr.bin/elvis/regexp.h @@ -18,4 +18,4 @@ typedef struct regexp { extern regexp *regcomp(); extern int regexec(); extern void regsub(); -extern void regerror(); +extern void regerr(); diff --git a/usr.bin/elvis/regsub.c b/usr.bin/elvis/regsub.c index d9ea1a153c..fe74e89f4f 100644 --- a/usr.bin/elvis/regsub.c +++ b/usr.bin/elvis/regsub.c @@ -7,14 +7,26 @@ #include "config.h" #include "ctype.h" #include "vi.h" -#include "regexp.h" +#ifdef REGEX +# include <regex.h> +#else +# include "regexp.h" +#endif /* perform substitutions after a regexp match */ +#ifdef REGEX +void regsub(rm, startp, endp, src, dst) + regmatch_t *rm; /* the regexp with pointers into matched text */ + char *startp, *endp; + REG char *src; /* the replacement string */ + REG char *dst; /* where to put the result of the subst */ +#else void regsub(re, src, dst) regexp *re; /* the regexp with pointers into matched text */ REG char *src; /* the replacement string */ REG char *dst; /* where to put the result of the subst */ +#endif { REG char *cpy; /* pointer to start of text to copy */ REG char *end; /* pointer to end of text to copy */ @@ -38,7 +50,8 @@ void regsub(re, src, dst) { if (!prev) { - regerror("No prev text to substitute for ~"); + regerr("No prev text to substitute for ~"); + return; } len += strlen(prev) - 1; @@ -56,10 +69,11 @@ void regsub(re, src, dst) } /* allocate memory for the ~ed version of src */ + checkmem(); start = cpy = (char *)malloc((unsigned)(len + 1)); if (!cpy) { - regerror("Not enough memory for ~ expansion"); + regerr("Not enough memory for ~ expansion"); return; } @@ -94,10 +108,11 @@ void regsub(re, src, dst) msg("Bug in regsub.c! Predicted length = %d, Actual length = %d", len, (int)(cpy - start)); } #endif + checkmem(); /* remember this as the "previous" for next time */ if (prev) - free(prev); + _free_(prev); prev = src = start; #endif /* undef CRUNCH */ @@ -109,8 +124,13 @@ void regsub(re, src, dst) /* recognize any meta characters */ if (c == '&' && *o_magic) { +#ifdef REGEX + cpy = startp; + end = endp; +#else cpy = re->startp[0]; end = re->endp[0]; +#endif } else #endif /* not NO_MAGIC */ @@ -132,8 +152,13 @@ void regsub(re, src, dst) case '9': /* \0 thru \9 mean "copy subexpression" */ c -= '0'; +#ifdef REGEX + cpy = startp + (rm[c].rm_so - rm[0].rm_so); + end = endp + (rm[c].rm_eo - rm[0].rm_eo); +#else cpy = re->startp[c]; end = re->endp[c]; +#endif break; # ifndef CRUNCH case 'U': @@ -157,15 +182,25 @@ void regsub(re, src, dst) *dst++ = c; continue; } +#ifdef REGEX + cpy = startp; + end = endp; +#else cpy = re->startp[0]; end = re->endp[0]; +#endif break; #else /* NO_MAGIC */ case '&': /* "\&" means "original text" */ +#ifdef REGEX + cpy = startp; + end = endp; +#else cpy = re->startp[0]; end = re->endp[0]; +#endif break; #endif /* NO_MAGIC */ default: diff --git a/usr.bin/elvis/system.c b/usr.bin/elvis/system.c index 988cdf7959..1d7b7db620 100644 --- a/usr.bin/elvis/system.c +++ b/usr.bin/elvis/system.c @@ -22,7 +22,9 @@ #include "config.h" #include "vi.h" +#ifndef XDOS extern char **environ; +#endif #if ANY_UNIX @@ -30,6 +32,9 @@ extern char **environ; * between this one and the library one is: this one uses the o_shell option. */ int system(cmd) +#ifdef __STDC__ + const +#endif char *cmd; /* a command to run */ { int pid; /* process ID of child */ @@ -73,11 +78,7 @@ int system(cmd) { status = -1; } -#if __GNUC__ - signal(SIGINT, (void (*)()) trapint); -#else signal(SIGINT, trapint); -#endif } return status; @@ -168,11 +169,7 @@ int rpclose(fd) close(fd); wait(&status); -#if __GNUC__ - signal(SIGINT, (void (*)()) trapint); -#else signal(SIGINT, trapint); -#endif return status; } @@ -372,13 +369,13 @@ int filter(from, to, cmd, back) } #endif } - } - /* delete old text, if any */ - if (to) - { - cut(from, to); - delete(from, to); + /* delete old text, if any */ + if (to) + { + cut(from, to); + delete(from, to); + } } } else diff --git a/usr.bin/elvis/tio.c b/usr.bin/elvis/tio.c index 66bd261836..56b0e9370f 100644 --- a/usr.bin/elvis/tio.c +++ b/usr.bin/elvis/tio.c @@ -14,10 +14,11 @@ #include "vi.h" #include "ctype.h" +static int showmsg P_((void)); /* This function reads in a line from the terminal. */ int vgets(prompt, buf, bsize) - char prompt; /* the prompt character, or '\0' for none */ + int prompt; /* the prompt character, or '\0' for none */ char *buf; /* buffer into which the string is read */ int bsize; /* size of the buffer */ { @@ -57,7 +58,7 @@ int vgets(prompt, buf, bsize) else { /* maybe expand an abbreviation while getting key */ - for (word = len; --word >= 0 && isalnum(buf[word]); ) + for (word = len; --word >= 0 && !isspace(buf[word]); ) { } word++; @@ -74,8 +75,10 @@ int vgets(prompt, buf, bsize) #endif /* some special conversions */ +#if 0 if (ch == ctrl('D') && len == 0) ch = ctrl('['); +#endif #ifndef NO_DIGRAPH if (*o_digraph && erased != 0 && ch != '\b') { @@ -99,9 +102,10 @@ int vgets(prompt, buf, bsize) quoted = TRUE; break; - case ctrl('['): + case ctrl('D'): return -1; + case ctrl('['): case '\n': #if OSK case '\l': @@ -111,6 +115,21 @@ int vgets(prompt, buf, bsize) clrtoeol(); goto BreakBreak; +#ifndef CRUNCH + case ctrl('U'): + while (len > 0) + { + len--; + while (widths[len]-- > 0) + { + qaddch('\b'); + qaddch(' '); + qaddch('\b'); + } + } + break; +#endif + case '\b': if (len > 0) { @@ -235,14 +254,24 @@ void endmsgs() * msg("") - clears the message line * msg("%s %d", ...) - does a printf onto the message line */ -/*VARARGS1*/ +#ifdef __STDC__ +void msg (char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); +#else void msg(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7) char *fmt; long arg1, arg2, arg3, arg4, arg5, arg6, arg7; { +#endif if (mode != MODE_VI) { +#ifdef __STDC__ + vsprintf (pmsg, fmt, ap); +#else sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7); +#endif qaddstr(pmsg); addch('\n'); exrefresh(); @@ -256,12 +285,19 @@ void msg(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7) } /* real message */ +#ifdef __STDC__ + vsprintf (pmsg, fmt, ap); +#else sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7); +#endif if (*fmt) { manymsgs = TRUE; } } +#ifdef __STDC__ + va_end (ap); +#endif } @@ -330,7 +366,7 @@ void beep() } else if (*o_errorbells) { - ttywrite("\007", 1); + tputs("\007", 1, faddch); } /* discard any buffered input, and abort macros */ @@ -426,6 +462,10 @@ void execmap(rawlen, cookedstr, visual) #endif } +#ifndef NO_CURSORSHAPE +/* made global so that suspend_curses() can reset it. -nox */ +int oldcurs; +#endif /* This function calls ttyread(). If necessary, it will also redraw the screen, * change the cursor shape, display the mode, and update the ruler. If the * number of characters read is 0, and we didn't time-out, then it exits because @@ -443,9 +483,6 @@ static int fillkeybuf(when, timeout) static long oldnlines; char *str; #endif -#ifndef NO_CURSORSHAPE - static int oldcurs; -#endif #ifdef DEBUG watch(); @@ -480,7 +517,10 @@ static int fillkeybuf(when, timeout) redraw(cursor, !(when & WHEN_VICMD)); /* now the "topline" test should be valid */ - if (when != oldwhen || topline != oldtop || leftcol != oldleft || nlines != oldnlines) + if (when != oldwhen + || topline != oldtop + || leftcol != oldleft + || nlines != oldnlines) { oldwhen = when; oldtop = topline; @@ -490,9 +530,9 @@ static int fillkeybuf(when, timeout) if (when & WHEN_VICMD) str = "Command"; else if (when & WHEN_VIINP) str = " Input "; else if (when & WHEN_VIREP) str = "Replace"; - else if (when & WHEN_REP1) str = " Rep 1 "; - else if (when & WHEN_CUT) str = "BufName"; - else if (when & WHEN_MARK) str = "Mark AZ"; + else if (when & WHEN_REP1) str = "Replc 1"; + else if (when & WHEN_CUT) str = "Buffer "; + else if (when & WHEN_MARK) str = " Mark "; else if (when & WHEN_CHAR) str = "Dest Ch"; else str = (char *)0; @@ -539,7 +579,7 @@ static int fillkeybuf(when, timeout) clrtoeol(); refresh(); endwin(); - exit(1); + exit(exitcode); } cend += nkeys; @@ -592,7 +632,12 @@ static int countmatch(when) /* No, it wouldn't. But check for partial match */ if (!strncmp(map->rawin, &keybuf[next], cend - next)) { - count++; + /* increment by 2 instead of 1 so that, in the + * event that we have a partial match with a + * single map, we don't mistakenly assume we + * have resolved the map yet. + */ + count += 2; } } } @@ -612,7 +657,7 @@ static void expandabbr(word, wlen) MAP *abbr; /* if the next character wouldn't end the word, then don't expand */ - if (isalnum(keybuf[next]) || keybuf[next] == ctrl('V')) + if (isalnum(keybuf[next]) || keybuf[next] == ctrl('V') || keybuf[next] == '\b') { return; } @@ -696,7 +741,7 @@ int getabkey(when, word, wlen) } /* try to map the key, unless already mapped and not ":set noremap" */ - if (next >= user || *o_remap) + if (next <= user || *o_remap) { do { @@ -713,6 +758,12 @@ int getabkey(when, word, wlen) } while (*o_remap && matches == 1); } + /* ERASEKEY should always be mapped to '\b'. */ + if (keybuf[next] == ERASEKEY) + { + keybuf[next] = '\b'; + } + #ifndef NO_ABBR /* try to expand an abbreviation, except in visual command mode */ if (wlen > 0 && (mode & (WHEN_EX|WHEN_VIINP|WHEN_VIREP)) != 0) @@ -721,12 +772,6 @@ int getabkey(when, word, wlen) } #endif - /* ERASEKEY should always be mapped to '\b'. */ - if (keybuf[next] == ERASEKEY) - { - keybuf[next] = '\b'; - } - /* return the next key */ return keybuf[next++]; } @@ -735,7 +780,7 @@ int getabkey(when, word, wlen) void mapkey(rawin, cooked, when, name) char *rawin; /* the input key sequence, before mapping */ char *cooked;/* after mapping -- or NULL to remove map */ - short when; /* bitmap of when mapping should happen */ + int when; /* bitmap of when mapping should happen */ char *name; /* name of the key, NULL for no name, "abbr" for abbr */ { MAP **head; /* head of list of maps or abbreviations */ @@ -751,7 +796,7 @@ void mapkey(rawin, cooked, when, name) /* try to find the map in the list */ for (scan = *head, prev = (MAP *)0; - scan && (strcmp(rawin, scan->rawin) || + scan && (strcmp(rawin, scan->rawin) && strcmp(rawin, scan->cooked) || !(scan->flags & when & (WHEN_EX|WHEN_VICMD|WHEN_VIINP|WHEN_VIREP))); prev = scan, scan = scan->next) { @@ -778,7 +823,7 @@ void mapkey(rawin, cooked, when, name) { scan = (MAP *)malloc(sizeof(MAP)); scan->len = strlen(rawin); - scan->rawin = malloc(scan->len + 1); + scan->rawin = malloc((unsigned)(scan->len + 1)); strcpy(scan->rawin, rawin); scan->flags = when; scan->label = name; @@ -794,9 +839,9 @@ void mapkey(rawin, cooked, when, name) } else /* recycle old structure */ { - free(scan->cooked); + _free_(scan->cooked); } - scan->cooked = malloc(strlen(cooked) + 1); + scan->cooked = malloc((unsigned)(strlen(cooked) + 1)); strcpy(scan->cooked, cooked); } else /* unmapping */ @@ -818,9 +863,9 @@ void mapkey(rawin, cooked, when, name) } /* free it, and the strings that it refers to */ - free(scan->rawin); - free(scan->cooked); - free(scan); + _free_(scan->rawin); + _free_(scan->cooked); + _free_(scan); } } @@ -924,7 +969,7 @@ void dumpkey(when, abbr) #ifndef NO_MKEXRC -static safequote(str) +static void safequote(str) char *str; { char *build; @@ -949,15 +994,14 @@ static safequote(str) * :abbr dumpkey(WHEN_VIINP|WHEN_VIREP, TRUE); * :abbr! dumpkey(WHEN_EX|WHEN_VIINP|WHEN_VIREP, TRUE); */ +void savemaps(fd, abbr) int fd; /* file descriptor of an open file to write to */ int abbr; /* boolean: do abbr table? (else do map table) */ { MAP *scan; - char *str; int bang; int when; - int len; # ifndef NO_ABBR for (scan = (abbr ? abbrs : maps); scan; scan = scan->next) diff --git a/usr.bin/elvis/tmp.c b/usr.bin/elvis/tmp.c index 23c80fedb4..46d29ec81d 100644 --- a/usr.bin/elvis/tmp.c +++ b/usr.bin/elvis/tmp.c @@ -5,13 +5,6 @@ * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00041 - * -------------------- ----- ---------------------- - * - * 12 Aug 92 Bob Wilcox Fixed named yank buffer problem */ @@ -153,11 +146,15 @@ int tmpstart(filename) ((getuid() >> 16) == 0 ? S_IOWRITE | S_IWRITE : ((statb.st_gid != (getuid() >> 16) ? S_IOWRITE : S_IWRITE))))) #endif -#if AMIGA || MSDOS || (TOS && defined(__GNUC__)) +#if AMIGA || MSDOS if (*o_readonly || !(statb.st_mode & S_IWRITE)) #endif -#if TOS && !defined(__GNUC__) +#if TOS +# ifdef __GNUC__ + if (*o_readonly || !(statb.st_mode & S_IWRITE)) +# else if (*o_readonly || (statb.st_mode & S_IJRON)) +# endif #endif #if ANY_UNIX if (*o_readonly || !(statb.st_mode & @@ -185,25 +182,36 @@ int tmpstart(filename) } /* make a name for the tmp file */ - tmpnum++; + do + { + tmpnum++; #if MSDOS || TOS - /* MS-Dos doesn't allow multiple slashes, but supports drives - * with current directories. - * This relies on TMPNAME beginning with "%s\\"!!!! - */ - strcpy(tmpname, o_directory); - if ((i = strlen(tmpname)) && !strchr(":/\\", tmpname[i-1])) - tmpname[i++]=SLASH; - sprintf(tmpname+i, TMPNAME+3, getpid(), tmpnum); + /* MS-Dos doesn't allow multiple slashes, but supports drives + * with current directories. + * This relies on TMPNAME beginning with "%s\\"!!!! + */ + strcpy(tmpname, o_directory); + if ((i = strlen(tmpname)) && !strchr(":/\\", tmpname[i-1])) + tmpname[i++]=SLASH; + sprintf(tmpname+i, TMPNAME+3, getpid(), tmpnum); #else - sprintf(tmpname, TMPNAME, o_directory, getpid(), tmpnum); + sprintf(tmpname, TMPNAME, o_directory, getpid(), tmpnum); #endif - - /* make sure nobody else is editing the same file */ - if (access(tmpname, 0) == 0) - { - FAIL("Temp file \"%s\" already exists?", tmpname); - } + } while (access(tmpname, 0) == 0); + + /* !!! RACE CONDITION HERE - some other process with the same PID could + * create the temp file between the access() call and the creat() call. + * This could happen in a couple of ways: + * - different workstation may share the same temp dir via NFS. Each + * workstation could have a process with the same number. + * - The DOS version may be running multiple times on the same physical + * machine in different virtual machines. The DOS pid number will + * be the same on all virtual machines. + * + * This race condition could be fixed by replacing access(tmpname, 0) + * with open(tmpname, O_CREAT|O_EXCL, 0600), if we could only be sure + * that open() *always* used modern UNIX semantics. + */ /* create the temp file */ #if ANY_UNIX @@ -219,8 +227,11 @@ int tmpstart(filename) } /* allocate space for the header in the file */ - write(tmpfd, hdr.c, (unsigned)BLKSIZE); - write(tmpfd, tmpblk.c, (unsigned)BLKSIZE); + if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) < BLKSIZE + || write(tmpfd, tmpblk.c, (unsigned)BLKSIZE) < BLKSIZE) + { + FAIL("Error writing headers to \"%s\"", tmpname); + } #ifndef NO_RECYCLE /* initialize the block allocator */ @@ -292,7 +303,7 @@ int tmpstart(filename) this->c[j++] = 0x80; } #ifndef CRUNCH - else if (*o_beautify && this->c[k] < ' ' && this->c[k] > 0) + else if (*o_beautify && this->c[k] < ' ' && this->c[k] >= 1) { if (this->c[k] == '\t' || this->c[k] == '\n' @@ -345,6 +356,10 @@ int tmpstart(filename) } /* allocate next buffer */ + if (i >= MAXBLKS - 2) + { + FAIL("File too big. Limit is approx %ld kbytes.", MAXBLKS * BLKSIZE / 1024L); + } next = blkget(++i); /* move fragmentary last line to next buffer */ @@ -475,11 +490,7 @@ int tmpsave(filename, bang) } /* can't rewrite a READONLY file */ -#if AMIGA if (!strcmp(filename, origname) && tstflag(file, READONLY) && !bang) -#else - if (!strcmp(filename, origname) && *o_readonly && !bang) -#endif { msg("\"%s\" [READONLY] -- NOT WRITTEN", filename); return FALSE; @@ -552,6 +563,10 @@ int tmpsave(filename, bang) /* reset the "modified" flag, but not the "undoable" flag */ clrflag(file, MODIFIED); significant = FALSE; + if (!strcmp(origname, filename)) + { + exitcode &= ~1; + } /* report lines & characters */ #if MSDOS || TOS @@ -601,11 +616,6 @@ int tmpabort(bang) blkinit(); nlines = 0; initflags(); -#ifdef BROKEN_YANK_BUFFERS /* 12 Aug 92*/ - close(tmpfd); - tmpfd = -1; - unlink(tmpname); -#endif /* BROKEN_YANK_BUFFERS*/ return TRUE; } @@ -652,6 +662,7 @@ sync() * to store the arguments to a command, so we can't use it here. Instead, * we'll borrow the buffer that is used for "shift-U". */ +int storename(name) char *name; /* the name of the file - normally origname */ { @@ -669,7 +680,11 @@ storename(name) U_text[1] = 127; } #ifndef CRUNCH +# if TOS || MINT || MSDOS || AMIGA + else if (*name != '/' && *name != '\\' && !(*name && name[1] == ':')) +# else else if (*name != SLASH) +# endif { /* get the directory name */ ptr = getcwd(U_text, BLKSIZE); @@ -696,7 +711,10 @@ storename(name) { /* write the name out to second block of the temp file */ lseek(tmpfd, (long)BLKSIZE, 0); - write(tmpfd, U_text, (unsigned)BLKSIZE); + if (write(tmpfd, U_text, (unsigned)BLKSIZE) < BLKSIZE) + { + FAIL("Error stuffing name \"%s\" into temp file", U_text); + } } return 0; } @@ -706,7 +724,7 @@ storename(name) /* This function handles deadly signals. It restores sanity to the terminal * preserves the current temp file, and deletes any old temp files. */ -int deathtrap(sig) +SIGTYPE deathtrap(sig) int sig; /* the deadly signal that we caught */ { char *why; @@ -730,8 +748,10 @@ int deathtrap(sig) # ifdef SIGBUS case SIGBUS: why = "-Elvis had a bus error"; break; # endif -# if defined(SIGSEGV) && !defined(TOS) +# ifdef SIGSEGV +# if !TOS case SIGSEGV: why = "-Elvis had a segmentation violation"; break; +# endif # endif # ifdef SIGSYS case SIGSYS: why = "-Elvis munged a system call"; break; diff --git a/usr.bin/elvis/unix.c b/usr.bin/elvis/unix.c index e136484543..f25b60afeb 100644 --- a/usr.bin/elvis/unix.c +++ b/usr.bin/elvis/unix.c @@ -79,6 +79,9 @@ int ttyread(buf, len, time) case -1: /* assume we got an EINTR because of SIGWINCH */ if (*o_lines != LINES || *o_columns != COLS) { +#ifndef CRUNCH + *o_nearscroll = +#endif *o_lines = LINES; *o_columns = COLS; #ifndef CRUNCH @@ -106,10 +109,10 @@ int ttyread(buf, len, time) } # else -# if M_SYSV -/* For System-V or Coherent, we use VMIN/VTIME to implement the timeout. - * For no timeout, VMIN should be 1 and VTIME should be 0; for timeout, - * VMIN should be 0 and VTIME should be the timeout value. +# if UNIXV || COH_386 +/* For System-V, we use VMIN/VTIME to implement the timeout. For no timeout, + * VMIN should be 1 and VTIME should be 0; for timeout, VMIN should be 0 and + * VTIME should be the timeout value. */ # include <termio.h> int ttyread(buf, len, time) @@ -135,7 +138,7 @@ int ttyread(buf, len, time) ioctl(0, TCSETA, &tio); /* Perform the read. Loop if EINTR error happens */ - while ((bytes = read(0, buf, len)) < 0) + while ((bytes = read(0, buf, (unsigned)len)) < 0) { /* probably EINTR error because a SIGWINCH was received */ if (*o_lines != LINES || *o_columns != COLS) @@ -145,6 +148,7 @@ int ttyread(buf, len, time) #ifndef CRUNCH if (!wset) { + *o_nearscroll = LINES; *o_window = LINES - 1; } #endif @@ -178,7 +182,7 @@ int ttyread(buf, len, time) static jmp_buf env; /*ARGSUSED*/ -int dummy(signo) +SIGTYPE dummy(signo) int signo; { longjmp(env, 1); @@ -189,17 +193,13 @@ int ttyread(buf, len, time) int time; /* maximum time to allow for reading */ { /* arrange for timeout */ -#if __GNUC__ - signal(SIGALRM, (void (*)()) dummy); -#else signal(SIGALRM, dummy); -#endif alarm(time); /* perform the blocking read */ if (setjmp(env) == 0) { - len = read(0, buf, len); + len = read(0, buf, (unsigned)len); } else /* I guess we timed out */ { @@ -216,7 +216,7 @@ int ttyread(buf, len, time) return len; } -# endif /* !(M_SYSV || COHERENT) */ +# endif /* M_SYSV */ # endif /* !BSD */ #endif /* ANY_UNIX */ diff --git a/usr.bin/elvis/vars.c b/usr.bin/elvis/vars.c index 19b5d193ac..13075b7136 100644 --- a/usr.bin/elvis/vars.c +++ b/usr.bin/elvis/vars.c @@ -51,6 +51,7 @@ int bavar; /* used only in BeforeAfter macros */ /* used to detect changes that invalidate cached text/blocks */ long changes; /* incremented when file is changed */ int significant; /* boolean: was a *REAL* change made? */ +int exitcode = 0; /* status code */ /* used to support the pfetch() macro */ int plen; /* length of the line */ diff --git a/usr.bin/elvis/vcmd.c b/usr.bin/elvis/vcmd.c index 494f7e1f72..4fd0923804 100644 --- a/usr.bin/elvis/vcmd.c +++ b/usr.bin/elvis/vcmd.c @@ -264,7 +264,7 @@ MARK v_overtype(m) /* Normally, we input starting here, in replace mode */ ChangeText { - end = input(m, m, WHEN_VIREP, FALSE); + end = input(m, m, WHEN_VIREP, 0); } /* if we ended on the same line we started on, then this @@ -361,14 +361,13 @@ MARK v_insert(m, cnt, key) { int wasdot; long reps; - int above; /* boolean: new line going above old line? */ + int delta = 0;/* 1 to take autoindent from line below, -1 for above */ DEFAULT(1); ChangeText { /* tweak the insertion point, based on command key */ - above = FALSE; switch (key) { case 'i': @@ -394,19 +393,20 @@ MARK v_insert(m, cnt, key) case 'O': m &= ~(BLKSIZE - 1); add(m, "\n"); - above = TRUE; + delta = 1; break; case 'o': m = (m & ~(BLKSIZE - 1)) + BLKSIZE; add(m, "\n"); + delta = -1; break; } /* insert the same text once or more */ for (reps = cnt, wasdot = doingdot; reps > 0; reps--, doingdot = TRUE) { - m = input(m, m, WHEN_VIINP, above) + 1; + m = input(m, m, WHEN_VIINP, delta) + 1; } if (markidx(m) > 0) { @@ -455,7 +455,7 @@ MARK v_change(m, n) ChangeText { cut(m, n); - m = input(m, n, WHEN_VIINP, FALSE); + m = input(m, n, WHEN_VIINP, 0); } return m; @@ -479,7 +479,7 @@ MARK v_subst(m, cnt) ChangeText { cut(m, m + cnt); - m = input(m, m + cnt, WHEN_VIINP, FALSE); + m = input(m, m + cnt, WHEN_VIINP, 0); } return m; } @@ -964,12 +964,24 @@ MARK v_popup(m, n) break; } - /* arrange for the menu to be erased (except that "chg from kbd" - * already erased it, and "save & exit" doesn't care) + /* arrange for the menu to be erased (except "save & exit" doesn't care) */ - if (sel != 5 && sel != 9) + if (mode == MODE_VI) redraw(MARK_UNSET, FALSE); return m; } #endif /* undef NO_POPUP */ + +#ifndef NO_TAGSTACK +MARK v_pop(m, cnt, cmd) + MARK m; /* original cursor position (ignored) */ + long cnt; /* number of levels to pop */ + int cmd; /* command key -- ^T (ignored) */ +{ + DEFAULT(1L); + sprintf(tmpblk.c, "%ld", cnt); + cmd_pop(m, m, CMD_POP, FALSE, tmpblk.c); + return cursor; +} +#endif diff --git a/usr.bin/elvis/vi.c b/usr.bin/elvis/vi.c index 24c46dc3f8..2c4648f461 100644 --- a/usr.bin/elvis/vi.c +++ b/usr.bin/elvis/vi.c @@ -73,7 +73,11 @@ static struct keystru /* ^Q not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^R redraw screen */ {v_redraw, NO_ARGS, NO_FLAGS|VIZ}, /* ^S not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#ifndef NO_TAGSTACK +/* ^T pop tagstack */ {v_pop, CURSOR, NO_FLAGS}, +#else /* ^T not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#endif /* ^U scroll up 1/2page*/ {m_scroll, CURSOR, NCOL|VIZ}, /* ^V not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^W not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, @@ -87,7 +91,7 @@ static struct keystru /* ESC not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^\ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* ^] keyword is tag */ {v_tag, KEYWORD, NO_FLAGS}, -/* ^^ previous file */ {v_switch, CURSOR, NO_FLAGS}, +/* ^^ previous file */ {v_switch, CURSOR, FRNT}, /* ^_ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, /* SPC move right,like l*/ {m_right, CURSOR, MVMT|INCL|VIZ}, /* ! run thru filter */ {v_filter, CURSOR_MOVED, FRNT|LNMD|INCL|VIZ}, @@ -168,7 +172,7 @@ static struct keystru #endif /* L move to last row */ {m_row, CURSOR, MVMT|LNMD|FRNT|VIZ|INCL}, /* M move to mid row */ {m_row, CURSOR, MVMT|LNMD|FRNT|VIZ|INCL}, -/* N reverse prev srch*/ {m_Nsrch, CURSOR, MVMT|NREL|VIZ}, +/* N reverse prev srch*/ {m_nsrch, CURSOR, MVMT|NREL|VIZ}, /* O insert above line*/ {v_insert, CURSOR, SDOT}, /* P paste before */ {v_paste, CURSOR, SDOT}, /* Q quit to EX mode */ {v_quit, NO_ARGS, NO_FLAGS}, @@ -185,7 +189,7 @@ static struct keystru #else /* V not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, #endif -/* W move forward Word*/ {m_fword, CURSOR, MVMT|INCL|VIZ}, +/* W move forward Word*/ {m_fword, CURSOR, MVMT|INCL|NWRP|VIZ}, /* X delete to left */ {v_xchar, CURSOR, SDOT}, /* Y yank text */ {v_yank, CURSOR_MOVED, NCOL}, /* Z save file & exit */ {v_xit, CURSOR_CNT_KEY, NO_FLAGS}, @@ -233,13 +237,13 @@ static struct keystru #else /* v not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, #endif -/* w move fwd word */ {m_fword, CURSOR, MVMT|INCL|VIZ}, +/* w move fwd word */ {m_fword, CURSOR, MVMT|INCL|NWRP|VIZ}, /* x delete character */ {v_xchar, CURSOR, SDOT}, /* y yank text */ {v_yank, CURSOR_MOVED, NCOL|VIZ}, /* z adjust scrn row */ {m_z, CURSOR_CNT_KEY, NCOL|VIZ}, -/* { back paragraph */ {m_paragraph, CURSOR, MVMT|LNMD|VIZ}, +/* { back paragraph */ {m_paragraph, CURSOR, MVMT|VIZ}, /* | move to column */ {m_tocol, CURSOR, MVMT|NREL|VIZ}, -/* } fwd paragraph */ {m_paragraph, CURSOR, MVMT|LNMD|VIZ}, +/* } fwd paragraph */ {m_paragraph, CURSOR, MVMT|VIZ}, /* ~ upper/lowercase */ {v_ulcase, CURSOR, SDOT}, /* DEL not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS} }; @@ -316,6 +320,9 @@ void vi() key = getkey(WHEN_VICMD); } while (key < 0 || key > 127); } +#ifdef DEBUG2 + debout("\nkey='%c'\n", key); +#endif /* Convert a doubled-up operator such as "dd" into "d_" */ if (prevkey && key == prevkey) @@ -551,7 +558,8 @@ void vi() do { text[0] = key; - if (vgets(key, text + 1, sizeof text - 1) >= 0) + text[1] = '\0'; + if (doingdot || vgets(key, text + 1, sizeof text - 1) >= 0) { /* reassure user that <CR> was hit */ qaddch('\r'); @@ -583,16 +591,25 @@ void vi() } /* now move the cursor, as appropriate */ - if (keyptr->args == CURSOR_MOVED) + if (prevkey && ((keyptr->flags & MVMT) +#ifndef NO_VISIBLE + || V_from +#endif + ) && count == 0L) + { + /* movements used as targets are less strict */ + tcurs = adjmove(cursor, tcurs, (int)(keyptr->flags | force_flags)); + } + else if (keyptr->args == CURSOR_MOVED) { /* the < and > keys have FRNT, * but it shouldn't be applied yet */ - tcurs = adjmove(cursor, tcurs, 0); + tcurs = adjmove(cursor, tcurs, FINL); } else { - tcurs = adjmove(cursor, tcurs, (int)keyptr->flags | force_flags); + tcurs = adjmove(cursor, tcurs, (int)(keyptr->flags | force_flags | FINL)); } /* was that the end of a d/c/y/</>/! command? */ @@ -629,7 +646,7 @@ void vi() * to the front of a line. Instead, they should take * us only to the end of the preceding line. */ - if ((keyptr->flags & (MVMT|NREL|LNMD|FRNT|INCL)) == MVMT + if ((keyptr->flags & NWRP) == NWRP && markline(range) < markline(tcurs) && (markline(tcurs) > nlines || tcurs == m_front(tcurs, 0L))) { @@ -660,8 +677,8 @@ void vi() tcurs = (*vikeys[prevkey].func)(range, tcurs); if (mode == MODE_VI) { - (void)adjmove(cursor, cursor, 0); - cursor = adjmove(cursor, tcurs, (int)vikeys[prevkey].flags); + (void)adjmove(cursor, cursor, FINL); + cursor = adjmove(cursor, tcurs, (int)(vikeys[prevkey].flags | FINL)); } /* cleanup */ @@ -688,6 +705,9 @@ MARK adjmove(old, new, flags) REG char *text; /* used to scan through the line's text */ REG int i; +#ifdef DEBUG2 + debout("adjmove(%ld.%d, %ld.%d, 0x%x)\n", markline(old), markidx(old), markline(new), markidx(new), flags); +#endif #ifdef DEBUG watch(); #endif @@ -695,8 +715,12 @@ MARK adjmove(old, new, flags) /* if the command failed, bag it! */ if (new == MARK_UNSET) { - beep(); - return old; + if (flags & FINL) + { + beep(); + return old; + } + return new; } /* if this is a non-relative movement, set the '' mark */ @@ -712,6 +736,10 @@ MARK adjmove(old, new, flags) } else if (markline(new) > nlines) { + if (!(flags & FINL)) + { + return MARK_EOF; + } new = MARK_LAST; } diff --git a/usr.bin/elvis/vi.h b/usr.bin/elvis/vi.h index 4c42dd2fc9..51d0978218 100644 --- a/usr.bin/elvis/vi.h +++ b/usr.bin/elvis/vi.h @@ -7,13 +7,14 @@ * kirkenda@cs.pdx.edu */ -#define VERSION "ELVIS 1.5, by Steve Kirkendall (23 March 1992)" +#define VERSION "ELVIS 1.7, by Steve Kirkendall (30 December 1992)" #define COPYING "This version of ELVIS is freely redistributable." #include <errno.h> -extern int errno; -#if TOS && !defined(__GNUC__) -#define ENOENT (-AEFILNF) +#if TOS +# ifndef __GNUC__ +# define ENOENT (-AEFILNF) +# endif #endif #if TOS || VMS @@ -33,8 +34,10 @@ extern int errno; # define ENOENT E_PNNF # define sprintf Sprintf # else -# include <sys/types.h> -# if COHERENT +# if !AMIGA +# include <sys/types.h> +# endif +# if COH_286 # include <sys/fcntl.h> # else # include <fcntl.h> @@ -49,10 +52,23 @@ extern int errno; #include "curses.h" #include <signal.h> +#ifdef __STDC__ +# include <stdio.h> /* for [v]sprintf prototype */ +# include <string.h> /* for str* prototypes */ +# include <stdlib.h> /* for atoi, system, malloc, free */ +# include <stdarg.h> /* for vararg definitions */ +# if ANY_UNIX +# include <unistd.h> /* for read, write, ... prototypes */ +# include <sys/wait.h> /* for wait prototype */ +# endif +#endif /*------------------------------------------------------------------------*/ /* Miscellaneous constants. */ +#ifdef REGEX +# define SE_MAX 30 /* max number of subexpressions */ +#endif #define INFINITY 2000000001L /* a very large integer */ #define LONGKEY 10 /* longest possible raw :map key */ #ifndef MAXRCLEN @@ -74,8 +90,8 @@ typedef union /* These are used manipulate BLK buffers. */ extern BLK hdr; /* buffer for the header block */ -extern BLK *blkget(); /* given index into hdr.c[], reads block */ -extern BLK *blkadd(); /* inserts a new block into hdr.c[] */ +extern BLK *blkget P_((int)); /* given index into hdr.c[], reads block */ +extern BLK *blkadd P_((int)); /* inserts a new block into hdr.c[] */ /*------------------------------------------------------------------------*/ /* These are used to keep track of various flags */ @@ -138,9 +154,11 @@ extern char o_beautify[1]; extern char o_exrc[1]; extern char o_mesg[1]; extern char o_more[1]; +extern char o_nearscroll[3]; extern char o_novice[1]; extern char o_prompt[1]; extern char o_taglength[3]; +extern char o_tags[256]; extern char o_terse[1]; extern char o_window[3]; extern char o_wrapmargin[3]; @@ -195,6 +213,10 @@ extern char o_showmatch[1]; extern char o_smd[1]; #endif +#ifndef NO_TAGSTACK +extern char o_tagstack[1]; +#endif + /*------------------------------------------------------------------------*/ /* These help support the single-line multi-change "undo" -- shift-U */ @@ -210,6 +232,7 @@ typedef long MARK; #define MARK_UNSET ((MARK)0) #define MARK_FIRST ((MARK)BLKSIZE) #define MARK_LAST ((MARK)(nlines * BLKSIZE)) +#define MARK_EOF ((MARK)((nlines + 1) * BLKSIZE)) #define MARK_AT_LINE(x) ((MARK)(x) * BLKSIZE) #define NMARKS 29 @@ -227,78 +250,101 @@ extern long prevline; /* line number from preceding file */ /*------------------------------------------------------------------------*/ /* misc housekeeping variables & functions */ -extern int tmpfd; /* fd used to access the tmp file */ -extern int tmpnum; /* counter used to generate unique filenames */ -extern long lnum[MAXBLKS]; /* last line# of each block */ -extern long nlines; /* number of lines in the file */ -extern char args[BLKSIZE]; /* file names given on the command line */ -extern int argno; /* the current element of args[] */ -extern int nargs; /* number of filenames in args */ -extern long changes; /* counts changes, to prohibit short-cuts */ -extern int significant; /* boolean: was a *REAL* change made? */ -extern BLK tmpblk; /* a block used to accumulate changes */ -extern long topline; /* file line number of top line */ -extern int leftcol; /* column number of left col */ +extern int tmpfd; /* fd used to access the tmp file */ +extern int tmpnum; /* counter used to generate unique filenames */ +extern long lnum[MAXBLKS]; /* last line# of each block */ +extern long nlines; /* number of lines in the file */ +extern char args[BLKSIZE]; /* file names given on the command line */ +extern int argno; /* the current element of args[] */ +extern int nargs; /* number of filenames in args */ +extern long changes; /* counts changes, to prohibit short-cuts */ +extern int significant; /* boolean: was a *REAL* change made? */ +extern int exitcode; /* 0=not updated, 1=overwritten, else error */ +extern BLK tmpblk; /* a block used to accumulate changes */ +extern long topline; /* file line number of top line */ +extern int leftcol; /* column number of left col */ #define botline (topline + LINES - 2) #define rightcol (leftcol + COLS - (*o_number ? 9 : 1)) -extern int physcol; /* physical column number that cursor is on */ -extern int physrow; /* physical row number that cursor is on */ -extern int exwrote; /* used to detect verbose ex commands */ -extern int doingdot; /* boolean: are we doing the "." command? */ -extern int doingglobal; /* boolean: are doing a ":g" command? */ -extern long rptlines; /* number of lines affected by a command */ -extern char *rptlabel; /* description of how lines were affected */ -extern char *fetchline(); /* read a given line from tmp file */ -extern char *parseptrn(); /* isolate a regexp in a line */ -extern MARK paste(); /* paste from cut buffer to a given point */ -extern char *wildcard(); /* expand wildcards in filenames */ -extern MARK input(); /* inserts characters from keyboard */ -extern char *linespec(); /* finds the end of a /regexp/ string */ +extern int physcol; /* physical column number that cursor is on */ +extern int physrow; /* physical row number that cursor is on */ +extern int exwrote; /* used to detect verbose ex commands */ +extern int doingdot; /* boolean: are we doing the "." command? */ +extern int doingglobal; /* boolean: are doing a ":g" command? */ +extern long rptlines; /* number of lines affected by a command */ +extern char *rptlabel; /* description of how lines were affected */ +extern char *fetchline P_((long)); /* read a given line from tmp file */ +extern char *parseptrn P_((REG char *)); /* isolate a regexp in a line */ +extern MARK paste P_((MARK, int, int)); /* paste from cut buffer to a given point */ +extern char *wildcard P_((char *)); /* expand wildcards in filenames */ +extern MARK input P_((MARK, MARK, int, int)); /* inserts characters from keyboard */ +extern char *linespec P_((REG char *, MARK *)); /* finds the end of a /regexp/ string */ #define ctrl(ch) ((ch)&037) #ifndef NO_RECYCLE -extern long allocate(); /* allocate a free block of the tmp file */ -#endif -extern int trapint(); /* trap handler for SIGINT */ -extern int deathtrap(); /* trap handler for deadly signals */ -extern void blkdirty(); /* marks a block as being "dirty" */ -extern void blkflush(); /* writes a single dirty block to the disk */ -extern void blksync(); /* forces all "dirty" blocks to disk */ -extern void blkinit(); /* resets the block cache to "empty" state */ -extern void beep(); /* rings the terminal's bell */ -extern void exrefresh(); /* writes text to the screen */ -extern void msg(); /* writes a printf-style message to the screen */ -extern void endmsgs(); /* if "manymsgs" is set, then scroll up 1 line */ -extern void garbage(); /* reclaims any garbage blocks */ -extern void redraw(); /* updates the screen after a change */ -extern void resume_curses();/* puts the terminal in "cbreak" mode */ -extern void beforedo(); /* saves current revision before a new change */ -extern void afterdo(); /* marks end of a beforedo() change */ -extern void abortdo(); /* like "afterdo()" followed by "undo()" */ -extern int undo(); /* restores file to previous undo() */ -extern void dumpkey(); /* lists key mappings to the screen */ -extern void mapkey(); /* defines a new key mapping */ -extern void savekeys(); /* lists key mappings to a file */ -extern void redrawrange(); /* records clues from modify.c */ -extern void cut(); /* saves text in a cut buffer */ -extern void delete(); /* deletes text */ -extern void add(); /* adds text */ -extern void change(); /* deletes text, and then adds other text */ -extern void cutswitch(); /* updates cut buffers when we switch files */ -extern void do_abbr(); /* defines or lists abbreviations */ -extern void do_digraph(); /* defines or lists digraphs */ -extern void exstring(); /* execute a string as EX commands */ -extern void dumpopts(); -extern void setopts(); -extern void saveopts(); -extern void savedigs(); -extern void saveabbr(); -extern void savecolor(); -extern void cutname(); -extern void cutname(); -extern void initopts(); -extern void cutend(); +extern long allocate P_((void)); /* allocate a free block of the tmp file */ +#endif +extern SIGTYPE trapint P_((int)); /* trap handler for SIGINT */ +extern SIGTYPE deathtrap P_((int)); /* trap handler for deadly signals */ +extern void blkdirty P_((BLK *)); /* marks a block as being "dirty" */ +extern void blksync P_((void)); /* forces all "dirty" blocks to disk */ +extern void blkinit P_((void)); /* resets the block cache to "empty" state */ +extern void beep P_((void)); /* rings the terminal's bell */ +extern void exrefresh P_((void)); /* writes text to the screen */ +#ifdef __STDC__ +extern void msg (char *, ...); /* writes a printf-style message to the screen */ +#else +extern void msg (); /* writes a printf-style message to the screen */ +#endif +extern void endmsgs P_((void)); /* if "manymsgs" is set, then scroll up 1 line */ +extern void garbage P_((void)); /* reclaims any garbage blocks */ +extern void redraw P_((MARK, int)); /* updates the screen after a change */ +extern void resume_curses P_((int)); /* puts the terminal in "cbreak" mode */ +extern void beforedo P_((int)); /* saves current revision before a new change */ +extern void afterdo P_((void)); /* marks end of a beforedo() change */ +extern void abortdo P_((void)); /* like "afterdo()" followed by "undo()" */ +extern int undo P_((void)); /* restores file to previous undo() */ +extern void dumpkey P_((int, int)); /* lists key mappings to the screen */ +extern void mapkey P_((char *, char *, int, char *)); /* defines a new key mapping */ +extern void redrawrange P_((long, long, long)); /* records clues from modify.c */ +extern void cut P_((MARK, MARK)); /* saves text in a cut buffer */ +extern void delete P_((MARK, MARK)); /* deletes text */ +extern void add P_((MARK, char *)); /* adds text */ +extern void change P_((MARK, MARK, char *));/* deletes text, and then adds other text */ +extern void cutswitch P_((void)); /* updates cut buffers when we switch files */ +extern void do_digraph P_((int, char [])); /* defines or lists digraphs */ +extern void exstring P_((char *, int, int));/* execute a string as EX commands */ +extern void dumpopts P_((int)); /* display current option settings on the screen */ +extern void setopts P_((char *)); /* assign new values to options */ +extern void saveopts P_((int)); /* save current option values to a given fd */ +extern void savedigs P_((int)); /* save current non-standard digraphs to fd */ +extern void savecolor P_((int)); /* save current color settings (if any) to fd */ +extern void cutname P_((int)); /* select cut buffer for next cut/paste */ +extern void initopts P_((void)); /* initialize options */ +extern void cutend P_((void)); /* free all cut buffers & delete temp files */ +extern int storename P_((char *)); /* stamp temp file with pathname of text file */ +extern int tmpstart P_((char *)); /* load a text file into edit buffer */ +extern int tmpsave P_((char *, int)); /* write edit buffer out to text file */ +extern int tmpend P_((int)); /* call tmpsave(), and then tmpabort */ +extern int tmpabort P_((int)); /* abandon the current edit buffer */ +extern void savemaps P_((int, int)); /* write current :map or :ab commands to fd */ +extern int ansicolor P_((int, int)); /* emit ANSI color command to terminal */ +extern int filter P_((MARK, MARK, char *, int)); /* I/O though another program */ +extern int getkey P_((int)); /* return a keystroke, interpretting maps */ +extern int vgets P_((int, char *, int)); /* read a single line from keyboard */ +extern int doexrc P_((char *)); /* execute a string as a sequence of EX commands */ +extern int cb2str P_((int, char *, unsigned));/* return a string containing cut buffer's contents */ +extern int ansiquit P_((void)); /* neutralize previous ansicolor() call */ +extern int ttyread P_((char *, int, int)); /* read from keyboard with optional timeout */ +extern int tgetent P_((char *, char *)); /* start termcap */ +extern int tgetnum P_((char *)); /* get a termcap number */ +extern int tgetflag P_((char *)); /* get a termcap boolean */ +extern int getsize P_((int)); /* determine how big the screen is */ +extern int endcolor P_((void)); /* used during color output */ +extern int getabkey P_((int, char *, int));/* like getkey(), but also does abbreviations */ +extern int idx2col P_((MARK, REG char *, int)); /* returns column# of a given MARK */ +extern int cutneeds P_((BLK *)); /* returns bitmap of blocks needed to hold cutbuffer text */ +extern void execmap P_((int, char *, int)); /* replaces "raw" keys with "mapped" keys */ #ifndef CRUNCH -extern int wset; /* boolean: has the "window" size been set? */ +extern int wset; /* boolean: has the "window" size been set? */ #endif /*------------------------------------------------------------------------*/ @@ -313,42 +359,41 @@ extern int bavar; /* used only in BeforeAfter macros */ /* These are the movement commands. Each accepts a mark for the starting */ /* location & number and returns a mark for the destination. */ -extern MARK m_updnto(); /* k j G */ -extern MARK m_right(); /* h */ -extern MARK m_left(); /* l */ -extern MARK m_tocol(); /* | */ -extern MARK m_front(); /* ^ */ -extern MARK m_rear(); /* $ */ -extern MARK m_fword(); /* w */ -extern MARK m_bword(); /* b */ -extern MARK m_eword(); /* e */ -extern MARK m_paragraph(); /* { } [[ ]] */ -extern MARK m_match(); /* % */ +extern MARK m_updnto P_((MARK, long, int)); /* k j G */ +extern MARK m_right P_((MARK, long, int, int)); /* h */ +extern MARK m_left P_((MARK, long)); /* l */ +extern MARK m_tocol P_((MARK, long, int)); /* | */ +extern MARK m_front P_((MARK, long)); /* ^ */ +extern MARK m_rear P_((MARK, long)); /* $ */ +extern MARK m_fword P_((MARK, long, int, int)); /* w */ +extern MARK m_bword P_((MARK, long, int)); /* b */ +extern MARK m_eword P_((MARK, long, int)); /* e */ +extern MARK m_paragraph P_((MARK, long, int)); /* { } [[ ]] */ +extern MARK m_match P_((MARK, long)); /* % */ #ifndef NO_SENTENCE - extern MARK m_sentence(); /* ( ) */ +extern MARK m_sentence P_((MARK, long, int)); /* ( ) */ #endif -extern MARK m_tomark(); /* 'm */ +extern MARK m_tomark P_((MARK, long, int)); /* 'm */ #ifndef NO_EXTENSIONS -extern MARK m_wsrch(); /* ^A */ +extern MARK m_wsrch P_((char *, MARK, int)); /* ^A */ #endif -extern MARK m_nsrch(); /* n */ -extern MARK m_Nsrch(); /* N */ -extern MARK m_fsrch(); /* /regexp */ -extern MARK m_bsrch(); /* ?regexp */ +extern MARK m_nsrch P_((MARK, long, int)); /* n */ +extern MARK m_fsrch P_((MARK, char *)); /* /regexp */ +extern MARK m_bsrch P_((MARK, char *)); /* ?regexp */ #ifndef NO_CHARSEARCH - extern MARK m__ch(); /* ; , */ - extern MARK m_fch(); /* f */ - extern MARK m_tch(); /* t */ - extern MARK m_Fch(); /* F */ - extern MARK m_Tch(); /* T */ +extern MARK m__ch P_((MARK, long, int)); /* ; , */ +extern MARK m_fch P_((MARK, long, int)); /* f */ +extern MARK m_tch P_((MARK, long, int)); /* t */ +extern MARK m_Fch P_((MARK, long, int)); /* F */ +extern MARK m_Tch P_((MARK, long, int)); /* T */ #endif -extern MARK m_row(); /* H L M */ -extern MARK m_z(); /* z */ -extern MARK m_scroll(); /* ^B ^F ^E ^Y ^U ^D */ +extern MARK m_row P_((MARK, long, int)); /* H L M */ +extern MARK m_z P_((MARK, long, int)); /* z */ +extern MARK m_scroll P_((MARK, long, int)); /* ^B ^F ^E ^Y ^U ^D */ /* Some stuff that is used by movement functions... */ -extern MARK adjmove(); /* a helper fn, used by move fns */ +extern MARK adjmove P_((MARK, REG MARK, int)); /* a helper fn, used by move fns */ /* This macro is used to set the default value of cnt */ #define DEFAULT(val) if (cnt < 1) cnt = (val) @@ -358,8 +403,8 @@ extern int plen; /* length of the line */ extern long pline; /* line number that len refers to */ extern long pchgs; /* "changes" level that len refers to */ extern char *ptext; /* text of previous line, if valid */ -extern void pfetch(); -extern char digraph(); +extern void pfetch P_((long)); +extern char digraph P_((int, int)); /* This is used to build a MARK that corresponds to a specific point in the * line that was most recently pfetch'ed. @@ -370,169 +415,174 @@ extern char digraph(); /*------------------------------------------------------------------------*/ /* These are used to handle EX commands. */ -#define CMD_NULL 0 /* NOT A VALID COMMAND */ -#define CMD_ABBR 1 /* "define an abbreviation" */ -#define CMD_ARGS 2 /* "show me the args" */ -#define CMD_APPEND 3 /* "insert lines after this line" */ -#define CMD_AT 4 /* "execute a cut buffer's contents via EX" */ -#define CMD_BANG 5 /* "run a single shell command" */ -#define CMD_CC 6 /* "run `cc` and then do CMD_ERRLIST" */ -#define CMD_CD 7 /* "change directories" */ -#define CMD_CHANGE 8 /* "change some lines" */ -#define CMD_COLOR 9 /* "change the default colors" */ -#define CMD_COPY 10 /* "copy the selected text to a given place" */ -#define CMD_DELETE 11 /* "delete the selected text" */ -#define CMD_DIGRAPH 12 /* "add a digraph, or display them all" */ -#define CMD_EDIT 13 /* "switch to a different file" */ -#define CMD_EQUAL 14 /* "display a line number" */ -#define CMD_ERRLIST 15 /* "locate the next error in a list" */ -#define CMD_FILE 16 /* "show the file's status" */ -#define CMD_GLOBAL 17 /* "globally search & do a command" */ -#define CMD_INSERT 18 /* "insert lines before the current line" */ -#define CMD_JOIN 19 /* "join the selected line & the one after" */ -#define CMD_LIST 20 /* "print lines, making control chars visible" */ -#define CMD_MAKE 21 /* "run `make` and then do CMD_ERRLIST" */ -#define CMD_MAP 22 /* "adjust the keyboard map" */ -#define CMD_MARK 23 /* "mark this line" */ -#define CMD_MKEXRC 24 /* "make a .exrc file" */ -#define CMD_MOVE 25 /* "move the selected text to a given place" */ -#define CMD_NEXT 26 /* "switch to next file in args" */ -#define CMD_NUMBER 27 /* "print lines from the file w/ line numbers" */ -#define CMD_PRESERVE 28 /* "act as though vi crashed" */ -#define CMD_PREVIOUS 29 /* "switch to the previous file in args" */ -#define CMD_PRINT 30 /* "print the selected text" */ -#define CMD_PUT 31 /* "insert any cut lines before this line" */ -#define CMD_QUIT 32 /* "quit without writing the file" */ -#define CMD_READ 33 /* "append the given file after this line */ -#define CMD_RECOVER 34 /* "recover file after vi crashes" - USE -r FLAG */ -#define CMD_REWIND 35 /* "rewind to first file" */ -#define CMD_SET 36 /* "set a variable's value" */ -#define CMD_SHELL 37 /* "run some lines through a command" */ -#define CMD_SHIFTL 38 /* "shift lines left" */ -#define CMD_SHIFTR 39 /* "shift lines right" */ -#define CMD_SOURCE 40 /* "interpret a file's contents as ex commands" */ -#define CMD_STOP 41 /* same as CMD_SUSPEND */ -#define CMD_SUBAGAIN 42 /* "repeat the previous substitution" */ -#define CMD_SUBSTITUTE 43 /* "substitute text in this line" */ -#define CMD_SUSPEND 44 /* "suspend the vi session" */ -#define CMD_TR 45 /* "transliterate chars in the selected lines" */ -#define CMD_TAG 46 /* "go to a particular tag" */ -#define CMD_UNABBR 47 /* "remove an abbreviation definition" */ -#define CMD_UNDO 48 /* "undo the previous command" */ -#define CMD_UNMAP 49 /* "remove a key sequence map */ -#define CMD_VERSION 50 /* "describe which version this is" */ -#define CMD_VGLOBAL 51 /* "apply a cmd to lines NOT containing an RE" */ -#define CMD_VISUAL 52 /* "go into visual mode" */ -#define CMD_WQUIT 53 /* "write this file out (any case) & quit" */ -#define CMD_WRITE 54 /* "write the selected(?) text to a given file" */ -#define CMD_XIT 55 /* "write this file out (if modified) & quit" */ -#define CMD_YANK 56 /* "copy the selected text into the cut buffer" */ -#ifdef DEBUG -# define CMD_DEBUG 57 /* access to internal data structures */ -# define CMD_VALIDATE 58 /* check for internal consistency */ -#endif +#define CMD_NULL 0 /* NOT A VALID COMMAND */ +#define CMD_ABBR 1 /* "define an abbreviation" */ +#define CMD_ARGS 2 /* "show me the args" */ +#define CMD_APPEND 3 /* "insert lines after this line" */ +#define CMD_AT 4 /* "execute a cut buffer's contents via EX" */ +#define CMD_BANG 5 /* "run a single shell command" */ +#define CMD_CC 6 /* "run `cc` and then do CMD_ERRLIST" */ +#define CMD_CD 7 /* "change directories" */ +#define CMD_CHANGE 8 /* "change some lines" */ +#define CMD_COLOR 9 /* "change the default colors" */ +#define CMD_COPY 10 /* "copy the selected text to a given place" */ +#define CMD_DELETE 11 /* "delete the selected text" */ +#define CMD_DIGRAPH 12 /* "add a digraph, or display them all" */ +#define CMD_EDIT 13 /* "switch to a different file" */ +#define CMD_EQUAL 14 /* "display a line number" */ +#define CMD_ERRLIST 15 /* "locate the next error in a list" */ +#define CMD_FILE 16 /* "show the file's status" */ +#define CMD_GLOBAL 17 /* "globally search & do a command" */ +#define CMD_INSERT 18 /* "insert lines before the current line" */ +#define CMD_JOIN 19 /* "join the selected line & the one after" */ +#define CMD_LIST 20 /* "print lines, making control chars visible" */ +#define CMD_MAKE 21 /* "run `make` and then do CMD_ERRLIST" */ +#define CMD_MAP 22 /* "adjust the keyboard map" */ +#define CMD_MARK 23 /* "mark this line" */ +#define CMD_MKEXRC 24 /* "make a .exrc file" */ +#define CMD_MOVE 25 /* "move the selected text to a given place" */ +#define CMD_NEXT 26 /* "switch to next file in args" */ +#define CMD_NUMBER 27 /* "print lines from the file w/ line numbers" */ +#define CMD_POP 28 /* "pop a position off the tagstack" */ +#define CMD_PRESERVE 29 /* "act as though vi crashed" */ +#define CMD_PREVIOUS 30 /* "switch to the previous file in args" */ +#define CMD_PRINT 31 /* "print the selected text" */ +#define CMD_PUT 32 /* "insert any cut lines before this line" */ +#define CMD_QUIT 33 /* "quit without writing the file" */ +#define CMD_READ 34 /* "append the given file after this line */ +#define CMD_RECOVER 35 /* "recover file after vi crashes" - USE -r FLAG */ +#define CMD_REWIND 36 /* "rewind to first file" */ +#define CMD_SET 37 /* "set a variable's value" */ +#define CMD_SHELL 38 /* "run some lines through a command" */ +#define CMD_SHIFTL 39 /* "shift lines left" */ +#define CMD_SHIFTR 40 /* "shift lines right" */ +#define CMD_SOURCE 41 /* "interpret a file's contents as ex commands" */ +#define CMD_STOP 42 /* same as CMD_SUSPEND */ +#define CMD_SUBAGAIN 43 /* "repeat the previous substitution" */ +#define CMD_SUBSTITUTE 44 /* "substitute text in this line" */ +#define CMD_SUSPEND 45 /* "suspend the vi session" */ +#define CMD_TR 46 /* "transliterate chars in the selected lines" */ +#define CMD_TAG 47 /* "go to a particular tag" */ +#define CMD_UNABBR 48 /* "remove an abbreviation definition" */ +#define CMD_UNDO 49 /* "undo the previous command" */ +#define CMD_UNMAP 50 /* "remove a key sequence map */ +#define CMD_VERSION 51 /* "describe which version this is" */ +#define CMD_VGLOBAL 52 /* "apply a cmd to lines NOT containing an RE" */ +#define CMD_VISUAL 53 /* "go into visual mode" */ +#define CMD_WQUIT 54 /* "write this file out (any case) & quit" */ +#define CMD_WRITE 55 /* "write the selected(?) text to a given file" */ +#define CMD_XIT 56 /* "write this file out (if modified) & quit" */ +#define CMD_YANK 57 /* "copy the selected text into the cut buffer" */ +#define CMD_DEBUG 58 /* access to internal data structures */ +#define CMD_VALIDATE 59 /* check for internal consistency */ typedef int CMD; -extern void ex(); -extern void vi(); -extern void doexcmd(); +extern void ex P_((void)); +extern void vi P_((void)); +extern void doexcmd P_((char *)); -extern void cmd_append(); -extern void cmd_args(); +extern void cmd_append P_((MARK, MARK, CMD, int, char *)); +extern void cmd_args P_((MARK, MARK, CMD, int, char *)); #ifndef NO_AT -extern void cmd_at(); + extern void cmd_at P_((MARK, MARK, CMD, int, char *)); #endif -extern void cmd_cd(); +extern void cmd_cd P_((MARK, MARK, CMD, int, char *)); #ifndef NO_COLOR -extern void cmd_color(); + extern void cmd_color P_((MARK, MARK, CMD, int, char *)); #endif -extern void cmd_delete(); +extern void cmd_delete P_((MARK, MARK, CMD, int, char *)); #ifndef NO_DIGRAPH -extern void cmd_digraph(); + extern void cmd_digraph P_((MARK, MARK, CMD, int, char *)); #endif -extern void cmd_edit(); +extern void cmd_edit P_((MARK, MARK, CMD, int, char *)); #ifndef NO_ERRLIST -extern void cmd_errlist(); + extern void cmd_errlist P_((MARK, MARK, CMD, int, char *)); #endif -extern void cmd_file(); -extern void cmd_global(); -extern void cmd_join(); -extern void cmd_mark(); +extern void cmd_file P_((MARK, MARK, CMD, int, char *)); +extern void cmd_global P_((MARK, MARK, CMD, int, char *)); +extern void cmd_join P_((MARK, MARK, CMD, int, char *)); +extern void cmd_mark P_((MARK, MARK, CMD, int, char *)); #ifndef NO_ERRLIST -extern void cmd_make(); + extern void cmd_make P_((MARK, MARK, CMD, int, char *)); #endif -extern void cmd_map(); +extern void cmd_map P_((MARK, MARK, CMD, int, char *)); #ifndef NO_MKEXRC -extern void cmd_mkexrc(); -#endif -extern void cmd_next(); -extern void cmd_print(); -extern void cmd_put(); -extern void cmd_read(); -extern void cmd_set(); -extern void cmd_shell(); -extern void cmd_shift(); -extern void cmd_source(); -extern void cmd_substitute(); -extern void cmd_tag(); -extern void cmd_undo(); -extern void cmd_version(); -extern void cmd_write(); -extern void cmd_xit(); -extern void cmd_move(); + extern void cmd_mkexrc P_((MARK, MARK, CMD, int, char *)); +#endif +extern void cmd_next P_((MARK, MARK, CMD, int, char *)); +#ifndef NO_TAGSTACK +extern void cmd_pop P_((MARK, MARK, CMD, int, char *)); +#endif +extern void cmd_print P_((MARK, MARK, CMD, int, char *)); +extern void cmd_put P_((MARK, MARK, CMD, int, char *)); +extern void cmd_read P_((MARK, MARK, CMD, int, char *)); +extern void cmd_set P_((MARK, MARK, CMD, int, char *)); +extern void cmd_shell P_((MARK, MARK, CMD, int, char *)); +extern void cmd_shift P_((MARK, MARK, CMD, int, char *)); +extern void cmd_source P_((MARK, MARK, CMD, int, char *)); +extern void cmd_substitute P_((MARK, MARK, CMD, int, char *)); +extern void cmd_tag P_((MARK, MARK, CMD, int, char *)); +extern void cmd_undo P_((MARK, MARK, CMD, int, char *)); +extern void cmd_version P_((MARK, MARK, CMD, int, char *)); +extern void cmd_write P_((MARK, MARK, CMD, int, char *)); +extern void cmd_xit P_((MARK, MARK, CMD, int, char *)); +extern void cmd_move P_((MARK, MARK, CMD, int, char *)); #ifdef DEBUG -extern void cmd_debug(); -extern void cmd_validate(); + extern void cmd_debug P_((MARK, MARK, CMD, int, char *)); + extern void cmd_validate P_((MARK, MARK, CMD, int, char *)); #endif #ifdef SIGTSTP -extern void cmd_suspend(); + extern void cmd_suspend P_((MARK, MARK, CMD, int, char *)); #endif /*----------------------------------------------------------------------*/ /* These are used to handle VI commands */ -extern MARK v_1ex(); /* : */ -extern MARK v_mark(); /* m */ -extern MARK v_quit(); /* Q */ -extern MARK v_redraw(); /* ^L ^R */ -extern MARK v_ulcase(); /* ~ */ -extern MARK v_undo(); /* u */ -extern MARK v_xchar(); /* x X */ -extern MARK v_replace(); /* r */ -extern MARK v_overtype(); /* R */ -extern MARK v_selcut(); /* " */ -extern MARK v_paste(); /* p P */ -extern MARK v_yank(); /* y Y */ -extern MARK v_delete(); /* d D */ -extern MARK v_join(); /* J */ -extern MARK v_insert(); /* a A i I o O */ -extern MARK v_change(); /* c C */ -extern MARK v_subst(); /* s */ -extern MARK v_lshift(); /* < */ -extern MARK v_rshift(); /* > */ -extern MARK v_reformat(); /* = */ -extern MARK v_filter(); /* ! */ -extern MARK v_status(); /* ^G */ -extern MARK v_switch(); /* ^^ */ -extern MARK v_tag(); /* ^] */ -extern MARK v_xit(); /* ZZ */ -extern MARK v_undoline(); /* U */ -extern MARK v_again(); /* & */ +extern MARK v_1ex P_((MARK, char *)); /* : */ +extern MARK v_mark P_((MARK, long, int)); /* m */ +extern MARK v_quit P_((void)); /* Q */ +extern MARK v_redraw P_((void)); /* ^L ^R */ +extern MARK v_ulcase P_((MARK, long)); /* ~ */ +extern MARK v_undo P_((MARK)); /* u */ +extern MARK v_xchar P_((MARK, long, int)); /* x X */ +extern MARK v_replace P_((MARK, long, int));/* r */ +extern MARK v_overtype P_((MARK)); /* R */ +extern MARK v_selcut P_((MARK, long, int)); /* " */ +extern MARK v_paste P_((MARK, long, int)); /* p P */ +extern MARK v_yank P_((MARK, MARK)); /* y Y */ +extern MARK v_delete P_((MARK, MARK)); /* d D */ +extern MARK v_join P_((MARK, long)); /* J */ +extern MARK v_insert P_((MARK, long, int)); /* a A i I o O */ +extern MARK v_change P_((MARK, MARK)); /* c C */ +extern MARK v_subst P_((MARK, long)); /* s */ +extern MARK v_lshift P_((MARK, MARK)); /* < */ +extern MARK v_rshift P_((MARK, MARK)); /* > */ +extern MARK v_reformat P_((MARK, MARK)); /* = */ +extern MARK v_filter P_((MARK, MARK)); /* ! */ +extern MARK v_status P_((void)); /* ^G */ +extern MARK v_switch P_((void)); /* ^^ */ +extern MARK v_tag P_((char *, MARK, long)); /* ^] */ +extern MARK v_xit P_((MARK, long, int)); /* ZZ */ +extern MARK v_undoline P_((MARK)); /* U */ +extern MARK v_again P_((MARK, MARK)); /* & */ #ifndef NO_EXTENSIONS - extern MARK v_keyword(); /* K */ - extern MARK v_increment(); /* * */ +extern MARK v_keyword P_((char *, MARK, long)); /* K */ +extern MARK v_increment P_((char *, MARK, long)); /* * */ #endif #ifndef NO_ERRLIST - extern MARK v_errlist(); /* * */ +extern MARK v_errlist P_((MARK)); /* * */ #endif #ifndef NO_AT - extern MARK v_at(); /* @ */ +extern MARK v_at P_((MARK, long, int)); /* @ */ #endif #ifdef SIGTSTP - extern MARK v_suspend(); /* ^Z */ +extern MARK v_suspend P_((void)); /* ^Z */ #endif #ifndef NO_POPUP - extern MARK v_popup(); /* \ */ +extern MARK v_popup P_((MARK, MARK)); /* \ */ +#endif +#ifndef NO_TAGSTACK +extern MARK v_pop P_((MARK, long, int)); /* ^T */ #endif /*----------------------------------------------------------------------*/ @@ -546,8 +596,10 @@ extern MARK v_again(); /* & */ #define NCOL 0x20 /* this command can't change the column# */ #define NREL 0x40 /* this is "non-relative" -- set the '' mark */ #define SDOT 0x80 /* set the "dot" variables, for the "." cmd */ +#define FINL 0x100 /* final testing, more strict! */ +#define NWRP 0x200 /* no line-wrap (used for 'w' and 'W') */ #ifndef NO_VISIBLE -# define VIZ 0x100 /* commands which can be used with 'v' */ +# define VIZ 0x400 /* commands which can be used with 'v' */ #else # define VIZ 0 #endif @@ -586,11 +638,14 @@ extern int mode; #ifndef NO_VISIBLE extern MARK V_from; extern int V_linemd; -extern MARK v_start(); +extern MARK v_start P_((MARK m, long cnt, int cmd)); #endif #ifdef DEBUG # define malloc(size) dbmalloc(size, __FILE__, __LINE__) # define free(ptr) dbfree(ptr, __FILE__, __LINE__) -extern char *dbmalloc(); +# define checkmem() dbcheckmem(__FILE__, __LINE__) +extern char *dbmalloc P_((int, char *, int)); +#else +# define checkmem() #endif diff --git a/usr.bin/elvisrecover/Makefile b/usr.bin/elvisrecover/Makefile index 367ba55ce0..fbe5a7ce84 100644 --- a/usr.bin/elvisrecover/Makefile +++ b/usr.bin/elvisrecover/Makefile @@ -1,7 +1,7 @@ PROG= elvisrecover #SRCS= elvisrecover.c -CFLAGS= -I${.CURDIR}/../elvis +CFLAGS+=-I${.CURDIR}/../elvis .include <bsd.prog.mk> .PATH: ${.CURDIR}/../elvis diff --git a/usr.bin/elvisrecover/README b/usr.bin/elvisrecover/README deleted file mode 100644 index dc7656f99a..0000000000 --- a/usr.bin/elvisrecover/README +++ /dev/null @@ -1,31 +0,0 @@ -Elvis is a clone of vi/ex, the standard UNIX editor. Elvis supports -nearly all of the vi/ex commands, in both visual mode and colon mode. - -Elvis runs under BSD UNIX, AT&T SysV UNIX, SCO Xenix, Minix, MS-DOS -(Turbo-C or MSC 5.1), Atari TOS, OS9/68000, Coherent, VMS, and AmigaDos. -Ports to other operating systems are in progress; contact me before you -start porting it to some other OS, because somebody else may have -already done it for you. - -Elvis is freely redistributable, in either source form or executable -form. There are no restrictions on how you may use it. - -The file "elvisman.txt" contains the manual for elvis. It is a plain -ASCII file with nothing more exotic than a newline character. It is -formatted for 66-line, 80-column pages. There may also be an archive of -"*.ms" and "*.man" files, which contain the TROFF source text used to -generate that manual. - -The file named "Makefile.mix" is used to compile elvis for all systems -except VMS and possibly MS-DOS. You should copy "Makefile.mix" to -"Makefile", and then edit "Makefile" to select the appropriate group of -settings for your system. - - -Author: Steve Kirkendall - 14407 SW Teal Blvd. #C - Beaverton, OR 97005 - -E-mail: kirkenda@cs.pdx.edu - -Phone: (503) 643-6980 diff --git a/usr.bin/elvisrecover/elvisrecover.c b/usr.bin/elvisrecover/elvisrecover.c index a0069a94e9..e4d7159439 100644 --- a/usr.bin/elvisrecover/elvisrecover.c +++ b/usr.bin/elvisrecover/elvisrecover.c @@ -14,6 +14,10 @@ #include "config.h" #include "vi.h" +void recover P_((char *, char *)); +void main P_((int, char **)); + + void recover(basename, outname) char *basename; /* the name of the file to recover */ char *outname; /* the name of the file to write to */ @@ -164,6 +168,7 @@ void recover(basename, outname) #endif } +void main(argc, argv) int argc; char **argv; @@ -172,7 +177,7 @@ main(argc, argv) if (argc > 3) { fprintf(stderr, "usage: %s [preserved_file [recovery_file]]\n", argv[0]); - exit(1); + exit(2); } /* recover the requested file, or list recoverable files */ diff --git a/usr.bin/env/Makefile b/usr.bin/env/Makefile index 372f3b924e..21280fa230 100644 --- a/usr.bin/env/Makefile +++ b/usr.bin/env/Makefile @@ -1,6 +1,5 @@ # @(#)Makefile 5.1 (Berkeley) 5/11/90 PROG= env -NOMAN= noman .include <bsd.prog.mk> diff --git a/usr.bin/env/env.1 b/usr.bin/env/env.1 new file mode 100644 index 0000000000..fcefb261e5 --- /dev/null +++ b/usr.bin/env/env.1 @@ -0,0 +1,113 @@ +.\" Copyright (c) 1980, 1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)printenv.1 6.7 (Berkeley) 7/28/91 +.\" +.Dd July 14, 1993 +.Dt ENV 1 +.Os +.Sh NAME +.Nm env +.Nd set and print environment +.Sh SYNOPSIS +.Nm env +.Op Fl i +.Op Ar name=value ... +.Oo +.Ar command +.Op argument ... +.Oc +.Sh DESCRIPTION +.Nm env +executes +.Ar command +after modifying the environment as +specified on the command line. The option +.Ar name=value +specifies +an environmental variable, +.Ar name , +with a value of +.Ar value . +The option +.Sq Fl i +causes +.Nm env +to completely ignore the environment +it inherits. +.Pp +If no +.Ar command +is specified, +.Nm env +prints out the names and values +of the variables in the environment, with one name/value pair per line. +.Sh DIAGNOSTICS +If the +.Ar command +is invoked, the exit status of +.Nm env +shall be the exit status of +.Ar command ; +otherwise, the +.Nm env +command exits with one of the following values: +.Bl -tag -width Ds +.It 0 +The +.Nm env +command completed successfully +.It 1-125 +An error occured. +.It 126 +The +.Ar command +was found, but could not be invoked. +.It 127 +The +.Ar command +could not be found. +.El +.Sh SEE ALSO +.Xr execvp 3 , +.Xr environ 7 +.Sh STANDARDS +The +.Nm env +command is expected to be +.St -p1003.2 +compliant. +.Sh BUGS +.Nm env +doesn't handle commands with equal (``='') signs in their +names, for obvious reasons. diff --git a/usr.bin/env/env.c b/usr.bin/env/env.c index 5c25a16f24..7583a696bc 100644 --- a/usr.bin/env/env.c +++ b/usr.bin/env/env.c @@ -43,6 +43,9 @@ static char sccsid[] = "@(#)env.c 5.3 (Berkeley) 6/1/90"; #include <stdio.h> #include <string.h> +#include <errno.h> + +static void usage(); main(argc, argv) int argc; @@ -54,27 +57,41 @@ main(argc, argv) char *cleanenv[1]; int ch; - while ((ch = getopt(argc, argv, "-")) != EOF) + while ((ch = getopt(argc, argv, "-i")) != EOF) switch((char)ch) { - case '-': + case '-': /* obsolete */ + case 'i': environ = cleanenv; cleanenv[0] = NULL; break; case '?': default: - (void)fprintf(stderr, - "usage: env [-] [name=value ...] [command]\n"); - exit(1); + usage(); } + for (argv += optind; *argv && (p = index(*argv, '=')); ++argv) (void)setenv(*argv, ++p, 1); + if (*argv) { + /* return 127 if the command to be run could not be found; 126 + if the command was was found but could not be invoked */ + int status; + execvp(*argv, argv); - (void)fprintf(stderr, "env: %s: %s\n", *argv, - strerror(errno)); - exit(1); + status = (errno == ENOENT) ? 127 : 126; + (void)fprintf(stderr, "env: %s: %s\n", *argv, strerror(errno)); + exit(status); } + for (ep = environ; *ep; ep++) (void)printf("%s\n", *ep); + exit(0); } + +static void +usage () +{ + (void) fprintf(stderr, "usage: env [-i] [name=value ...] [command]\n"); + exit (1); +} diff --git a/usr.bin/file/LEGAL.NOTICE b/usr.bin/file/LEGAL.NOTICE index f466b8f4e0..b70ac990cf 100644 --- a/usr.bin/file/LEGAL.NOTICE +++ b/usr.bin/file/LEGAL.NOTICE @@ -1,8 +1,16 @@ -Copyright (c) Ian F. Darwin 1986, 1987. -Written by Ian F. Darwin. +Copyright (c) Ian F. Darwin 1986, 1987, 1989, 1990, 1991, 1992. +Written by Ian F. Darwin and others. +LEGAL.NOTICE,v 1.2 1993/06/10 00:37:55 jtc Exp -This software is not subject to any license of the American Telephone -and Telegraph Company or of the Regents of the University of California. +This software is not subject to and may not be made subject to any +license of the American Telephone and Telegraph Company (AT&T Inc.), +UNIX System Laboratories (USL Inc.), Novell Inc., Sun Microsystems +Inc., Digital Equipment Inc., Lotus Development Inc., the Regents of +the University of California, The X Consortium or MIT, or The Free +Software Foundation. + +This software is not subject to any export provision of the United States +Department of Commerce, and may be exported to any country or planet. Permission is granted to anyone to use this software for any purpose on any computer system, and to alter it and redistribute it freely, subject @@ -20,3 +28,10 @@ to the following restrictions: ever read sources, credits must appear in the documentation. 4. This notice may not be removed or altered. + +UNIX is a trademark of UNIX System Laboratories (which is probably a +subsidiary of Novell, Inc., by the time you read this). The name "UNIX" +may not be used by commercial undertakings without permission in +writing from USL. Just ask BSDI (Berkeley Software Design Inc.), a +commercial venture not officially connected with the University of +California at Berkeley. diff --git a/usr.bin/file/MAINT b/usr.bin/file/MAINT new file mode 100644 index 0000000000..32aa852d68 --- /dev/null +++ b/usr.bin/file/MAINT @@ -0,0 +1,33 @@ +MAINT,v 1.1 1993/06/10 00:37:57 jtc Exp + +Maintenance notes: + +I am continuing to maintain the file command. I welcome your help, +but to make my life easier I'd like to request the following: + +- Don't change the version numbers! + +If your changes are extensive, I will have to work hard to +integrate them into my version. If you check it into SCCS locally, +the version numbers will likely be kept. IF you check it into RCS +or CVS locally, please use -k to keep the version numbers, and +please use branch deltas (1.21.1, 1.21.2, ...). If you don't do +this, I will likely be unable to use your changes; life's just too +short. + +- Do not distribute changed versions. + +People trying to be helpful occasionally put up their hacked versions +of the file command for FTP, then the "archie" server finds and publishes +the hacked version, and people all over the world get copies of it. +Within a day or two I am getting email from around the world +asking me why "my" file command won't compile!!! Needless to say this +detracts from the limited time I have available to work on the actual +software. Therefore I ask you again to please NOT distribute +your changed version. + + +Thank you for your assistance and cooperation. + +Ian Darwin. +ian@sq.com diff --git a/usr.bin/file/Makefile b/usr.bin/file/Makefile index b95624c437..9f5484bd49 100644 --- a/usr.bin/file/Makefile +++ b/usr.bin/file/Makefile @@ -1,16 +1,26 @@ -PROG= file -MAGIC = /etc/magic -CFLAGS+= -DMAGIC='"$(MAGIC)"' -SRCS= file.c apprentice.c fsmagic.c softmagic.c ascmagic.c is_tar.c \ - print.c $(LOCALSRCS) -MAN5= magic.0 -MAN1= file.0 +MAGIC= /etc/magic +MAGICOWN= bin +MAGICGRP= bin +MAGICMODE= 444 -ascmagic.o: names.h +PROG= file +SRCS= file.c apprentice.c fsmagic.c softmagic.c ascmagic.c is_tar.c \ + print.c compress.c +CFLAGS+= -DMAGIC='"$(MAGIC)"' +MAN1= file.1 +MAN5= magic.5 -apprentice.o ascmagic.o file.o fsmagic.o print.o softmagic.o: file.h +CLEANFILES+= magic +all: file magic + +MAGFILES= $(.CURDIR)/magdir/Header\ + $(.CURDIR)/magdir/Localstuff\ + $(.CURDIR)/magdir/[a-z]* +magic: $(MAGFILES) + cat $(MAGFILES) > $(.TARGET) afterinstall: - install -c ${.CURDIR}/magic $(MAGIC) + install -c -o $(MAGICOWN) -g $(MAGICGRP) -m $(MAGICMODE) magic \ + $(DESTDIR)$(MAGIC) .include <bsd.prog.mk> diff --git a/usr.bin/file/Makefile.ian b/usr.bin/file/Makefile.ian deleted file mode 100644 index f7e1dd192e..0000000000 --- a/usr.bin/file/Makefile.ian +++ /dev/null @@ -1,85 +0,0 @@ -# Makefile for file(1) cmd. -# Copyright (c) Ian F. Darwin 86/09/01 - see LEGAL.NOTICE. -# @(#)$Header: Makefile,v 1.17 88/01/15 13:03:16 ian Exp $ -# -CC = cc -SHELL = /bin/sh -MAGIC = /etc/magic -DEFS = -DMAGIC='"$(MAGIC)"' # -Dvoid=int -COPTS = -g -CFLAGS = $(COPTS) $(DEFS) -SHAR = bundle -OFILE = /bin/file.orig # old or distributed version, for comparison -# Where new binary lives; typically /usr/local (BSD), /usr/lbin (USG). -BINDIR = /usr/local -# For installing our man pages; -# MANCxxx is manual section for Commands, MANFxxx is section for file formats. -# MANxDIR is directory names; MANxEXT is the filename extention. Usual values: -# Variable V7 4BSD Sys V -# MANCDIR /usr/man/man1 /usr/man/man1 /usr/man/u_man/man1 -# MANFDIR /usr/man/man5 /usr/man/man5 /usr/man/u_man/man4 -# MANCEXT 1 1 1 -# MANFEXT 5 5 4 -# --- possible alternative for 4BSD --- -# MANCDIR /usr/man/manl -# MANCEXT l -# --- possible alternative for USG --- -# MANCDIR /usr/man/local/man1 -# MANCEXT 1 - -MANCDIR = /usr/man/manl -MANFDIR = /usr/man/man5 -MANCEXT = l -MANFEXT = 5 - -# There are no system-dependant configuration options (except maybe CFLAGS). -# Delete any of LOCALSRCS and LOCALOBJS that are in your C library. -LOCALSRCS = getopt.c strtol.c strtok.c strchr.c -SRCS = file.c apprentice.c fsmagic.c softmagic.c ascmagic.c is_tar.c \ - print.c $(LOCALSRCS) -#LOCALOBJS = getopt.o strtol.o strtok.o strchr.o -LOCALOBJS = # getopt.o strtol.o strtok.o strchr.o -OBJS = file.o apprentice.o fsmagic.o softmagic.o ascmagic.o is_tar.o \ - print.o $(LOCALOBJS) - -ALLSRC = LEGAL.NOTICE README PORTING $(SRCS) *.h \ - Makefile file.1 magic.4 magdir/[a-z]* tst/Makefile - -all: file magic - -try: all $(OFILE) - cd tst; make - time $(OFILE) -m ./magic * tst/* >/tmp/t1 - time ./file -m ./magic * tst/* >/tmp/t2 - -diff -b /tmp/t[12] - what ./file >lastnocore - -file: $(OBJS) - $(CC) $(CFLAGS) $(OBJS) -o $@ -lint: $(SRCS) - lint -ha $(DEFS) $(SRCS) | tee $@ -magic: magdir -# exclude RCS or SCCS dirs: - cat magdir/[a-z]* >$@ - -ascmagic.o: names.h - -apprentice.o ascmagic.o file.o fsmagic.o print.o softmagic.o: file.h - -install: file magic file.1 magic.4 $(BINDIR) $(MANCDIR) $(MANCDIR) - cp file $(BINDIR)/file - cp magic $(MAGIC) - cp file.1 $(MANCDIR)/file.$(MANCEXT) - cp magic.4 $(MANFDIR)/magic.$(MANFEXT) - -clean: - rm -f *.o file magic lint.out - (cd tst; make clean) - -dist: $(ALLSRC) -# Some versions of shar can't handle a single file from -# a subdirectory, so we manually insert mkdir as needed. -# Put the extra "mkdir" AFTER the ": to unbundle..." line. - $(SHAR) $(ALLSRC) | sed -e '1a\ - mkdir magdir tst' >$@ - diff --git a/usr.bin/file/PORTING b/usr.bin/file/PORTING index 36c48ecb0f..e0b292f20c 100644 --- a/usr.bin/file/PORTING +++ b/usr.bin/file/PORTING @@ -1,10 +1,39 @@ Portability of the new file(1) command. -@(#) $Header: PORTING,v 1.6 87/11/08 23:03:41 ian Exp $ +@(#) PORTING,v 1.2 1993/06/10 00:38:00 jtc Exp Read this file only if the program doesn't compile on your system. -I have tried to make a program that doesn't need any command-line -defines (-D) to specify what version of UNIX is in use, +This release has been around UNIX; it has been compiled and tested +in the following environments: + +SunOS sqarc 4.1.1 8 sun4 + No problems. +ULTRIX squint 4.2 0 RISC + No problems. +A/UX sqmac 3.0a9 SVR22 mc68020 + No problems. +AIX sqibm 2 3 000XXXXXX100 + Had weird "make" problems making "magic" file automatically; just + built it by hand. Your mileage may vary. +SCO sqwang 3.2 2 i386 + Compiles fine; their weird make can't handle "[a-z]*" as a dependancy, + so build magic by hand. Runs fine. +sqzme sqzme 3.1.1 3 3B2 + The 3B2 SVR3 needed a few tweaks as well as COPTS = -Ilocalinc + in order to compile. + +This version, reluctanly, includes <stdlib.h>, which won't exist +on older systems or those that aren't even close to the ANSI C +standard. There is a null "stdlib.h", and some other bogus headers, +in subdirectory "localinc"; if you get complaints about missing +stdlib.h and others, uncomment the line with COPTS=-Ilocalinc +in the Makefile, and try again. + +You must have either <stdarg.h> or the older <varargs.h>, otherwise you'll +have to butcher some routines in print.c. + +Beyond that, I have tried to make a program that doesn't need any +command-line defines (-D) to specify what version of UNIX is in use, by using the definitions available in the system #include files. For example, the lstat(2) call is normally found in 4BSD systems, but might be grafted into some other variant @@ -12,9 +41,11 @@ of UNIX. If it's done right (ie., using the same definitions), my program will compile and work correctly. Look at the #ifdefs to see how it's done. -I've also tried to include all the non-portable library routines +I've also tried to include source for all the non-portable library routines I used (getopt, str*). Non-portable here means `not in every reasonably standard UNIX out there: V7, System V, 4BSD'. +These are in subdirectory "localsrc", and not used unless you +need them; again, see the Makefile. There is one area that just might cause problems. On System V, they moved the definition of major() and minor() out of @@ -26,21 +57,20 @@ have a system in which neither types.h nor sysmacros.h defines systems, you will get a compilation error in trying to compile a warning message. Please do the following: - 1) change the appropriate (2nd) #include at the start of - fsmagic.c + 1) change the appropriate #include at the start of fsmagic.c and 2) let me know the name of the system, the release number, and the name of the header file that *does* include this "standard" definition. If you are running the old Ritchie PDP-11 C compiler or some other compiler that doesn't know about `void', you will have -to un-comment-out the definition of `void=int' in the Makefile. +to include `-Dvoid=int' in the variable COPTS in the Makefile. Other than this, there should be no portability problems, but one never knows these days. Please let me know of any other problems you find porting to a UNIX system. I don't much -care for non-UNIX systems but will collect widely-used magic +care about non-UNIX systems but will collect widely-used magic numbers for them as well as for UNIX systems. Ian Darwin -Toronto, Canada +(address in README) diff --git a/usr.bin/file/README b/usr.bin/file/README index f1870ac166..d40b307ad1 100644 --- a/usr.bin/file/README +++ b/usr.bin/file/README @@ -1,22 +1,41 @@ ** README for file(1) Command ** -@(#) $Header: README,v 1.9 87/11/07 12:38:30 ian Exp $ - -This is Ian Darwin's (copyright but distributable) file(1) -command. It follows the USG (Sys V) model of the file command, -rather than the Research (V7) version or the V7-derived Berkeley -one. That is, there is a file (/etc/magic) that contains much -of the ritual information that is the source of this program's -power. It knows a little more magic (including tar archives) -than System V; the /etc/magic parsing seems to be compatible -with the (poorly documented) System V /etc/magic format (with -one exception; see the man page). +@(#) README,v 1.2 1993/06/10 00:38:01 jtc Exp + +This is Release 3.x of Ian Darwin's (copyright but distributable) +file(1) command. Release 3.x is scheduled for inclusion in the +4.4 BSD (Berkeley Software Distribution) of UNIX-like +software, and is the standard "file" command for Linux, 386bsd, +and other systems. (See "patchlevel.h" for the exact release number). + +UNIX is a trademark of UNIX System Laboratories. + +The prime contributor to Release 3.8 was Guy Harris, who put in megachanges +including byte-order independance. + +The prime contributor to Release 3.0 was Christos Zoulas, who put +in hundreds of lines of source code changes, including his own +ANSIfication of the code (I liked my own ANSIfication better, but +his (__P()) is the "Berkeley standard" way of doing it, and I wanted UCB +to include the code...), his HP-like "indirection" (a feature of +the HP file command, I think), and his mods that finally got the +uncompress (-z) mode finished and working. + +This release has compiled in numerous environments; see PORTING +for a list and problems. + +This fine freeware file(1) follows the USG (System V) model of the file +command, rather than the Research (V7) version or the V7-derived 4.[23] +Berkeley one. That is, the file /etc/magic contains much of the ritual +information that is the source of this program's power. My version +knows a little more magic (including tar archives) than System V; the +/etc/magic parsing seems to be compatible with the (poorly documented) +System V /etc/magic format (with one exception; see the man page). In addition, the /etc/magic file is built from a subdirectory -for easier maintenance. I will act as a clearinghouse for +for easier(?) maintenance. I will act as a clearinghouse for magic numbers assigned to all sorts of data files that are in reasonable circulation. Send your magic numbers, -in magic(4) format please, to the author, Ian Darwin, -{utzoo|ihnp4}!darwin!ian, ian@sq.com. +in magic(4) format please, to the author, Ian Darwin. LEGAL.NOTICE - read this first. README - read this second (you are currently reading this file). @@ -41,8 +60,29 @@ file.h - header file fsmagic.c - first set of tests the program runs, based on filesystem info is_tar.c - knows about tarchives (courtesy John Gilmore). magdir - directory of /etc/magic pieces + magdir/Makefile - ADJUST THIS FOR YOUR CONFIGURATION names.h - header file for ascmagic.c softmagic.c - 2nd set of tests, based on /etc/magic strtok.c, getopt.c - in case you them (courtesy of Henry Spencer). strtol.c, strchr.c - in case you need them - public domain. tst - simple test suite, built from tst/Makefile + +Ian Darwin +Darwin Open Systems +R R 1 +Palgrave, Ontario L0N 1P0 +Canada + +E-mail: darwin@cs.toronto.edu + ian@sq.com + utzoo!ian + ian@darwin.uucp + uunet!sq!ian + +Phone: Do not even think of telephoning me about this program. Send cash first! + +Parts of this software were developed at SoftQuad Inc., 56 Aberfoyle +Cres, # 810, Toronto, Ontario CANADA M8X 2W4. Phone: 416-239-4801 or +800-387-2777. Email: mail@sq.com. Call for information on SGML editing +and browsing, Unix text processing, and customised products on Unix, +DOS and Mac. diff --git a/usr.bin/file/apprentice.c b/usr.bin/file/apprentice.c index a762442e1b..358a8a4b6b 100644 --- a/usr.bin/file/apprentice.c +++ b/usr.bin/file/apprentice.c @@ -26,33 +26,34 @@ */ #include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <ctype.h> #include "file.h" #ifndef lint static char *moduleid = - "@(#)$Header: apprentice.c,v 1.8 87/11/06 21:14:34 ian Exp $"; + "@(#)apprentice.c,v 1.2 1993/06/10 00:38:02 jtc Exp"; #endif /* lint */ -#define MAXSTR 500 -#define EATAB {while (isascii(*l) && isspace(*l)) ++l;} +#define EATAB {while (isascii((unsigned char) *l) && \ + isspace((unsigned char) *l)) ++l;} -extern char *progname; -extern char *magicfile; -extern int debug; /* option */ -extern int nmagic; /* number of valid magic[]s */ -extern long strtol(); -struct magic magic[MAXMAGIS]; +static int getvalue __P((struct magic *, char **)); +static int hextoint __P((int)); +static char *getstr __P((char *, char *, int, int *)); +static int parse __P((char *, int *, int)); -char *getstr(); +static int maxmagic = 0; +int apprentice(fn, check) char *fn; /* name of magic file */ -int check; /* non-zero: checking-only run. */ +int check; /* non-zero? checking-only run. */ { FILE *f; - char line[MAXSTR+1]; + char line[BUFSIZ+1]; int errs = 0; f = fopen(fn, "r"); @@ -65,14 +66,24 @@ int check; /* non-zero: checking-only run. */ exit(1); } + maxmagic = MAXMAGIS; + if ((magic = (struct magic *) malloc(sizeof(struct magic) * maxmagic)) + == NULL) { + (void) fprintf(stderr, "%s: Out of memory.\n", progname); + if (check) + return -1; + else + exit(1); + } + /* parse it */ if (check) /* print silly verbose header for USG compat. */ - (void) printf("cont\toffset\ttype\topcode\tvalue\tdesc\n"); + (void) printf("cont\toffset\ttype\topcode\tmask\tvalue\tdesc\n"); - while (fgets(line, MAXSTR, f) != NULL) { + for (lineno = 1;fgets(line, BUFSIZ, f) != NULL; lineno++) { if (line[0]=='#') /* comment, do not parse */ continue; - if (strlen(line) <= 1) /* null line, garbage, etc */ + if (strlen(line) <= (unsigned)1) /* null line, garbage, etc */ continue; line[strlen(line)-1] = '\0'; /* delete newline */ if (parse(line, &nmagic, check) != 0) @@ -86,45 +97,98 @@ int check; /* non-zero: checking-only run. */ /* * parse one line from magic file, put into magic[index++] if valid */ -int +static int parse(l, ndx, check) char *l; int *ndx, check; { int i = 0, nd = *ndx; - int slen; - static int warned = 0; struct magic *m; - extern int errno; - - /* - * TODO malloc the magic structures (linked list?) so this can't happen - */ - if (nd+1 >= MAXMAGIS){ - if (warned++ == 0) - warning( -"magic table overflow - increase MAXMAGIS beyond %d in file/apprentice.c\n", - MAXMAGIS); - return -1; + char *t, *s; + + if (nd+1 >= maxmagic){ + maxmagic += 20; + if ((magic = (struct magic *) realloc(magic, + sizeof(struct magic) * + maxmagic)) == NULL) { + (void) fprintf(stderr, "%s: Out of memory.\n", progname); + if (check) + return -1; + else + exit(1); + } } m = &magic[*ndx]; + m->flag = 0; + m->cont_level = 0; - if (*l == '>') { + while (*l == '>') { ++l; /* step over */ - m->contflag = 1; - } else - m->contflag = 0; + m->cont_level++; + } + + if (m->cont_level != 0 && *l == '(') { + ++l; /* step over */ + m->flag |= INDIR; + } /* get offset, then skip over it */ - m->offset = atoi(l); - while (isascii(*l) && isdigit(*l)) + m->offset = (int) strtol(l,&t,0); + if (l == t) + magwarn("offset %s invalid", l); + l = t; + + if (m->flag & INDIR) { + m->in.type = LONG; + m->in.offset = 0; + /* + * read [.lbs][+-]nnnnn) + */ + if (*l == '.') { + switch (*++l) { + case 'l': + m->in.type = LONG; + break; + case 's': + m->in.type = SHORT; + break; + case 'b': + m->in.type = BYTE; + break; + default: + magwarn("indirect offset type %c invalid", *l); + break; + } + l++; + } + s = l; + if (*l == '+' || *l == '-') l++; + if (isdigit((unsigned char)*l)) { + m->in.offset = strtol(l, &t, 0); + if (*s == '-') m->in.offset = - m->in.offset; + } + if (*t++ != ')') + magwarn("missing ')' in indirect offset"); + l = t; + } + + + while (isascii((unsigned char)*l) && isdigit((unsigned char)*l)) ++l; EATAB; -#define NBYTE 4 -#define NSHORT 5 -#define NLONG 4 -#define NSTRING 6 +#define NBYTE 4 +#define NSHORT 5 +#define NLONG 4 +#define NSTRING 6 +#define NDATE 4 +#define NBESHORT 7 +#define NBELONG 6 +#define NBEDATE 6 +#define NLESHORT 7 +#define NLELONG 6 +#define NLEDATE 6 + /* get type, skip it */ if (strncmp(l, "byte", NBYTE)==0) { m->type = BYTE; @@ -138,52 +202,90 @@ int *ndx, check; } else if (strncmp(l, "string", NSTRING)==0) { m->type = STRING; l += NSTRING; + } else if (strncmp(l, "date", NDATE)==0) { + m->type = DATE; + l += NDATE; + } else if (strncmp(l, "beshort", NBESHORT)==0) { + m->type = BESHORT; + l += NBESHORT; + } else if (strncmp(l, "belong", NBELONG)==0) { + m->type = BELONG; + l += NBELONG; + } else if (strncmp(l, "bedate", NBEDATE)==0) { + m->type = BEDATE; + l += NBEDATE; + } else if (strncmp(l, "leshort", NLESHORT)==0) { + m->type = LESHORT; + l += NLESHORT; + } else if (strncmp(l, "lelong", NLELONG)==0) { + m->type = LELONG; + l += NLELONG; + } else if (strncmp(l, "ledate", NLEDATE)==0) { + m->type = LEDATE; + l += NLEDATE; } else { - errno = 0; - warning("type %s invalid", l); + magwarn("type %s invalid", l); return -1; } - EATAB; - - if (*l == '>' || *l == '<' || *l == '&' || *l == '=') { - m->reln = *l; + /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */ + if (*l == '&') { ++l; + m->mask = strtol(l, &l, 0); } else - m->reln = '='; + m->mask = 0L; EATAB; - -/* - * TODO finish this macro and start using it! - * #define offsetcheck {if (offset > HOWMANY-1) warning("offset too big"); } - */ - switch(m->type) { - /* - * Do not remove the casts below. They are vital. - * When later compared with the data, the sign extension must - * have happened. - */ - case BYTE: - m->value.l = (char) strtol(l,&l,0); - break; - case SHORT: - m->value.l = (short) strtol(l,&l,0); - break; - case LONG: - m->value.l = (long) strtol(l,&l,0); - break; - case STRING: - l = getstr(l, m->value.s, sizeof(m->value.s), &slen); - m->vallen = slen; + + switch (*l) { + case '>': + case '<': + /* Old-style anding: "0 byte &0x80 dynamically linked" */ + case '&': + case '^': + case '=': + m->reln = *l; + ++l; break; + case '!': + if (m->type != STRING) { + m->reln = *l; + ++l; + break; + } + /* FALL THROUGH */ default: - warning("can't happen: m->type=%d\n", m->type); - return -1; + if (*l == 'x' && isascii((unsigned char)l[1]) && + isspace((unsigned char)l[1])) { + m->reln = *l; + ++l; + goto GetDesc; /* Bill The Cat */ + } + m->reln = '='; + break; } + EATAB; + + if (getvalue(m, &l)) + return -1; + /* + * TODO finish this macro and start using it! + * #define offsetcheck {if (offset > HOWMANY-1) + * magwarn("offset too big"); } + */ /* * now get last part - the description */ +GetDesc: EATAB; + if (l[0] == '\b') { + ++l; + m->nospflag = 1; + } else if ((l[0] == '\\') && (l[1] == 'b')) { + ++l; + ++l; + m->nospflag = 1; + } else + m->nospflag = 0; while ((m->desc[i++] = *l++) != '\0' && i<MAXDESC) /* NULLBODY */; @@ -194,13 +296,61 @@ int *ndx, check; return 0; } +/* + * Read a numeric value from a pointer, into the value union of a magic + * pointer, according to the magic type. Update the string pointer to point + * just after the number read. Return 0 for success, non-zero for failure. + */ +static int +getvalue(m, p) +struct magic *m; +char **p; +{ + int slen; + + if (m->type == STRING) { + *p = getstr(*p, m->value.s, sizeof(m->value.s), &slen); + m->vallen = slen; + } else { + if (m->reln != 'x') { + switch(m->type) { + /* + * Do not remove the casts below. They are vital. + * When later compared with the data, the sign + * extension must have happened. + */ + case BYTE: + m->value.l = (char) strtol(*p,p,0); + break; + case SHORT: + case BESHORT: + case LESHORT: + m->value.l = (short) strtol(*p,p,0); + break; + case DATE: + case BEDATE: + case LEDATE: + case LONG: + case BELONG: + case LELONG: + m->value.l = (long) strtol(*p,p,0); + break; + default: + magwarn("can't happen: m->type=%d\n", m->type); + return -1; + } + } + } + return 0; +} + /* * Convert a string containing C character escapes. Stop at an unescaped * space or tab. * Copy the converted version to "p", returning its length in *slen. * Return updated scan pointer as function result. */ -char * +static char * getstr(s, p, plen, slen) register char *s; register char *p; @@ -211,8 +361,9 @@ int plen, *slen; register int c; register int val; - while((c = *s++) != '\0') { - if (isspace(c)) break; + while ((c = *s++) != '\0') { + if (isspace((unsigned char) c)) + break; if (p >= pmax) { fprintf(stderr, "String too long: %s\n", origs); break; @@ -224,7 +375,7 @@ int plen, *slen; goto out; default: - *p++ = c; + *p++ = (char) c; break; case 'n': @@ -272,7 +423,7 @@ int plen, *slen; } else --s; - *p++ = val; + *p++ = (char)val; break; /* \x and up to 3 hex digits */ @@ -293,28 +444,28 @@ int plen, *slen; --s; } else --s; - *p++ = val; + *p++ = (char)val; break; } } else - *p++ = c; + *p++ = (char)c; } out: *p = '\0'; *slen = p - origp; - return(s); + return s; } /* Single hex char to int; -1 if not a hex char. */ -int +static int hextoint(c) - char c; +int c; { - if (!isascii(c)) return -1; - if (isdigit(c)) return c - '0'; - if ((c>='a')&(c<='f')) return c + 10 - 'a'; - if ((c>='A')&(c<='F')) return c + 10 - 'A'; + if (!isascii((unsigned char) c)) return -1; + if (isdigit((unsigned char) c)) return c - '0'; + if ((c>='a')&&(c<='f')) return c + 10 - 'a'; + if ((c>='A')&&(c<='F')) return c + 10 - 'A'; return -1; } @@ -324,12 +475,12 @@ hextoint(c) */ void showstr(s) -register char *s; +const char *s; { register char c; while((c = *s++) != '\0') { - if(c >= 040 && c <= 0176) + if(c >= 040 && c <= 0176) /* TODO isprint && !iscntrl */ putchar(c); else { putchar('\\'); diff --git a/usr.bin/file/ascmagic.c b/usr.bin/file/ascmagic.c index 07e3e9d154..b7308018c5 100644 --- a/usr.bin/file/ascmagic.c +++ b/usr.bin/file/ascmagic.c @@ -27,41 +27,49 @@ */ #include <stdio.h> +#include <string.h> #include <ctype.h> +#include <stdlib.h> +#include <unistd.h> #include "file.h" #include "names.h" #ifndef lint static char *moduleid = - "@(#)$Header: ascmagic.c,v 1.5 87/09/16 14:44:45 ian Exp $"; + "@(#)ascmagic.c,v 1.2 1993/06/10 00:38:04 jtc Exp"; #endif /* lint */ -char *ckfmsg = "write error on output"; - /* an optimisation over plain strcmp() */ #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0) -ascmagic(buf) -register char *buf; +int +ascmagic(buf, nbytes) +unsigned char *buf; +int nbytes; /* size actually read */ { - register int i; - char *s, *strtok(), *token; + int i, isblock, has_escapes = 0; + unsigned char *s; + char nbuf[HOWMANY+1]; /* one extra for terminating '\0' */ + char *token; register struct names *p; - extern int nbytes; - short has_escapes = 0; /* these are easy, do them first */ /* - * for troff, look for . + letter + letter; + * for troff, look for . + letter + letter or .\"; * this must be done to disambiguate tar archives' ./file * and other trash from real troff input. */ - if (*buf == '.' && - isascii(*(buf+1)) && isalnum(*(buf+1)) && - isascii(*(buf+2)) && isalnum(*(buf+2))){ - ckfputs("troff or preprocessor input text", stdout); - return 1; + if (*buf == '.') { + unsigned char *tp = buf + 1; + + while (isascii(*tp) && isspace(*tp)) + ++tp; /* skip leading whitespace */ + if ((isascii(*tp) && (isalnum(*tp) || *tp=='\\') && + isascii(*(tp+1)) && (isalnum(*(tp+1)) || *tp=='"'))) { + ckfputs("troff or preprocessor input text", stdout); + return 1; + } } if ((*buf == 'c' || *buf == 'C') && isascii(*(buf + 1)) && isspace(*(buf + 1))) { @@ -70,12 +78,17 @@ register char *buf; } /* look for tokens from names.h - this is expensive! */ - s = buf; - while ((token = strtok(s, " \t\n\r\f")) != NULL) { + /* make a copy of the buffer here because strtok() will destroy it */ + s = (unsigned char*) memcpy(nbuf, buf, HOWMANY); + has_escapes = (memchr(s, '\033', HOWMANY) != NULL); + while ((token = strtok((char*)s, " \t\n\r\f")) != NULL) { s = NULL; /* make strtok() keep on tokin' */ for (p = names; p < names + NNAMES; p++) { if (STREQ(p->name, token)) { ckfputs(types[p->type], stdout); + if (has_escapes) + ckfputs(" (with escape sequences)", + stdout); return 1; } } @@ -90,20 +103,33 @@ register char *buf; return 1; } + if (i = is_compress(buf, &isblock)) { + if (zflag) { + unsigned char *newbuf; + int newsize; + + if (newsize = uncompress(buf, &newbuf, nbytes)) { + tryit(newbuf, newsize); + free(newbuf); + } + printf(" (%scompressed data - %d bits)", + isblock ? "block " : "", i); + } + else printf("%scompressed data - %d bits", + isblock ? "block " : "", i); + return 1; + } + for (i = 0; i < nbytes; i++) { if (!isascii(*(buf+i))) return 0; /* not all ascii */ - if (*(buf+i) == '\033') /* ascii ESCAPE */ - has_escapes ++; } /* all else fails, but it is ascii... */ - if (has_escapes){ - ckfputs("ascii text (with escape sequences)", stdout); - } - else { - ckfputs("ascii text", stdout); - } + ckfputs("ascii text", stdout); + if (has_escapes) { + ckfputs(" (with escape sequences)", stdout); + } return 1; } diff --git a/usr.bin/file/compress.c b/usr.bin/file/compress.c new file mode 100644 index 0000000000..d0d1f17246 --- /dev/null +++ b/usr.bin/file/compress.c @@ -0,0 +1,86 @@ +/* + * compress routines: + * is_compress() returns 0 if uncompressed, number of bits if compressed. + * uncompress(old, n, newch) - uncompress old into new, return sizeof new + * compress.c,v 1.1 1993/06/10 00:38:05 jtc Exp + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/wait.h> + +#include "file.h" + +/* Check for compression, return nbits. Algorithm, in magic(4) format: + * 0 string \037\235 compressed data + * >2 byte&0x80 >0 block compressed + * >2 byte&0x1f x %d bits + */ +int +is_compress(p, b) +const unsigned char *p; +int *b; +{ + + if (*p != '\037' || *(/*signed*/ char*)(p+1) != '\235') + return 0; /* not compress()ed */ + + *b = *(p+2) & 0x80; + return *(p+2) & 0x1f; +} + +int +uncompress(old, newch, n) +const unsigned char *old; +unsigned char **newch; +int n; +{ + int fdin[2], fdout[2]; + + if (pipe(fdin) == -1 || pipe(fdout) == -1) { + error("cannot create pipe (%s).\n", strerror(errno)); + /*NOTREACHED*/ + } + switch (fork()) { + case 0: /* child */ + (void) close(0); + (void) dup(fdin[0]); + (void) close(fdin[0]); + (void) close(fdin[1]); + + (void) close(1); + (void) dup(fdout[1]); + (void) close(fdout[0]); + (void) close(fdout[1]); + + execlp("uncompress", "uncompress", "-c", NULL); + error("could not execute `uncompress' (%s).\n", + strerror(errno)); + /*NOTREACHED*/ + case -1: + error("could not fork (%s).\n", strerror(errno)); + /*NOTREACHED*/ + + default: /* parent */ + (void) close(fdin[0]); + (void) close(fdout[1]); + if (write(fdin[1], old, n) != n) { + error("write failed (%s).\n", strerror(errno)); + /*NOTREACHED*/ + } + (void) close(fdin[1]); + if ((*newch = (unsigned char *) malloc(n)) == NULL) { + error("out of memory.\n"); + /*NOTREACHED*/ + } + if ((n = read(fdout[0], *newch, n)) <= 0) { + free(*newch); + error("read failed (%s).\n", strerror(errno)); + /*NOTREACHED*/ + } + (void) close(fdout[0]); + (void) wait(NULL); + return n; + } +} diff --git a/usr.bin/file/file.1 b/usr.bin/file/file.1 index 4354a75288..b805b46bef 100644 --- a/usr.bin/file/file.1 +++ b/usr.bin/file/file.1 @@ -1,26 +1,24 @@ -.\" -.\" PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -.\" -------------------- ----- ---------------------- -.\" CURRENT PATCH LEVEL: 1 00103 -.\" -------------------- ----- ---------------------- -.\" -.\" 16 Feb 93 Rodney W. Grimes Fixed all the commands that had two -.\" dots in them. -.\" .TH FILE 1 "Copyright but distributable" +.\# file.1,v 1.3 1993/06/10 00:38:06 jtc Exp .SH NAME .I file \- determine file type .SH SYNOPSIS .B file [ -.B -c +.B \-c ] [ -.B -f +.B \-z +] +[ +.B \-L +] +[ +.B \-f namefile ] [ -.B -m +.B \-m magicfile ] file ... .SH DESCRIPTION @@ -50,28 +48,29 @@ or the program itself, .B "preserve these keywords" . People depend on knowing that all the readable files in a directory have the word ``text'' printed. -Don't do as one computer vendor did \- change ``shell commands text'' +Don't do as Berkeley did \- change ``shell commands text'' to ``shell script''. .PP The filesystem tests are based on examining the return from a -.I stat (2) +.IR stat (2) system call. The program checks to see if the file is empty, or if it's some sort of special file. Any known file types appropriate to the system you are running on -(sockets and symbolic links on 4.2BSD, named pipes (FIFOs) on System V) +(sockets, symbolic links, or named pipes (FIFOs) on those systems that +implement them) are intuited if they are defined in the system header file -.I sys/stat.h . +.BR sys/stat.h . .PP The magic number tests are used to check for files with data in particular fixed formats. The canonical example of this is a binary executable (compiled program) -.I a.out +.B a.out file, whose format is defined in -.I a.out.h +.B a.out.h and possibly -.I exec.h +.B exec.h in the standard include directory. These files have a `magic number' stored in a particular place near the beginning of the file that tells the \s-1UNIX\s0 operating system @@ -80,7 +79,7 @@ The concept of `magic number' has been applied by extension to data files. Any file with some invariant identifier at a small fixed offset into the file can usually be described in this way. The information in these files is read from the magic file -.I /etc/magic . +.I /etc/magic. .PP If an argument appears to be an .SM ASCII @@ -90,10 +89,10 @@ attempts to guess its language. The language tests look for particular strings (cf \fInames.h\fP) that can appear anywhere in the first few blocks of a file. For example, the keyword -.I .br +.B .br indicates that the file is most likely a troff input file, just as the keyword -.I struct +.B struct indicates a C program. These tests are less reliable than the previous two groups, so they are performed last. @@ -104,19 +103,23 @@ archives) and determine whether an unknown file should be labelled as `ascii text' or `data'. .PP Use -.B -m +.B \-m .I file to specify an alternate file of magic numbers. .PP The -.B -c +.B \-z +tries to look inside compressed files. +.PP +The +.B \-c option causes a checking printout of the parsed form of the magic file. This is usually used in conjunction with -.B -m +.B \-m to debug a new magic file before installing it. .PP The -.B -f +.B \-f .I namefile option specifies that the names of the files to be examined are to be read (one per line) from @@ -126,11 +129,16 @@ Either .I namefile or at least one filename argument must be present; to test the standard input, use ``-'' as a filename argument. +.PP +The +.B \-L +option causes symlinks to be followed, as the like-named option in +.IR ls (1). .SH FILES .I /etc/magic \- default list of magic numbers .SH SEE ALSO -.IR Magic (FILES) +.IR magic (5) \- description of magic file format. .br .IR Strings (1), " od" (1) @@ -154,21 +162,25 @@ For example, in an existing magic file would have to be changed to .br >10 string language\e impress (imPRESS data) +.br +In addition, in this version, if a pattern string contains a backslash, +it must be escaped. For example +.br +0 string \ebegindata Andrew Toolkit document +.br +in an existing magic file would have to be changed to +.br +0 string \e\ebegindata Andrew Toolkit document +.br .PP -The Sun Microsystems implementation of System V compatibility -includes a file(1) command that has some extentions. +SunOS releases 3.2 and later from Sun Microsystems include a +.IR file (1) +command derived from the System V one, but with some extensions. My version differs from Sun's only in minor ways. -The significant one is the `&' operator, which Sun's program expects as, +It includes the extension of the `&' operator, used as, for example, .br >16 long&0x7fffffff >0 not stripped -.br -would be entered in my version as -.br ->16 long &0x7fffffff not stripped -.br -which is a little less general; it simply tests (location 16)&0x7ffffff -and returns its truth value as a C expression. .SH MAGIC DIRECTORY The magic file entries have been collected from various sources, mainly USENET, and contributed by various authors. @@ -203,19 +215,47 @@ the first version. Geoff Collyer found several inadequacies and provided some magic file entries. The program has undergone continued evolution since. -.SH NOTICE -Copyright (c) Ian F. Darwin, 1986 and 1987. +.SH AUTHOR Written by Ian F. Darwin, UUCP address {utzoo | ihnp4}!darwin!ian, Internet address ian@sq.com, postal address: P.O. Box 603, Station F, Toronto, Ontario, CANADA M4Y 2L8. .PP -.I Strtok.c -and -.I getopt.c -written by and copyright by Henry Spencer, utzoo!henry. +Altered by Rob McMahon, cudcv@warwick.ac.uk, 1989, to extend the `&' operator +from simple `x&y != 0' to `x&y op z'. +.PP +Altered by Guy Harris, guy@auspex.com, 1993, to: +.RS +.PP +put the ``old-style'' `&' +operator back the way it was, because 1) Rob McMahon's change broke the +previous style of usage, 2) the SunOS ``new-style'' `&' operator, +which this version of +.I file +supports, also handles `x&y op z', and 3) Rob's change wasn't documented +in any case; +.PP +put in multiple levels of `>'; .PP -This software is not subject to any license of the American Telephone -and Telegraph Company or of the Regents of the University of California. +put in ``beshort'', ``leshort'', etc. keywords to look at numbers in the +file in a specific byte order, rather than in the native byte order of +the process running +.IR file . +.RE +.PP +Changes by Ian Darwin and various authors including +Christos Zoulas (christos@ee.cornell.edu), 1990-1992. +.SH LEGAL NOTICE +Copyright (c) Ian F. Darwin, Toronto, Canada, +1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993. +.PP +This software is not subject to and may not be made subject to any +license of the American Telephone and Telegraph Company, Sun +Microsystems Inc., Digital Equipment Inc., Lotus Development Inc., the +Regents of the University of California, The X Consortium or MIT, or +The Free Software Foundation. +.PP +This software is not subject to any export provision of the United States +Department of Commerce, and may be exported to any country or planet. .PP Permission is granted to anyone to use this software for any purpose on any computer system, and to alter it and redistribute it freely, subject @@ -250,8 +290,14 @@ were written by John Gilmore from his public-domain .I tar program, and are not covered by the above restrictions. .SH BUGS -There must be a way to automate the construction of the Magic -file from all the glop in magdir. What is it? +There must be a better way to automate the construction of the Magic +file from all the glop in Magdir. What is it? +Better yet, the magic file should be compiled into binary (say, +.IR ndbm (3) +or, better yet, fixed-length ASCII strings +for use in heterogenous network environments) for faster startup. +Then the program would run as fast as the Version 7 program of the same name, +with the flexibility of the System V version. .PP .I File uses several algorithms that favor speed over accuracy, @@ -260,14 +306,11 @@ thus it can be misled about the contents of ASCII files. The support for ASCII files (primarily for programming languages) is simplistic, inefficient and requires recompilation to update. .PP -Should there be an ``else'' clause to follow a series of continuation lines? -.PP -Is it worthwhile to implement recursive file inspection, -so that compressed files, uuencoded, etc., can say ``compressed -ascii text'' or ``compressed executable'' or ``compressed tar archive" -or whatever? +There should be an ``else'' clause to follow a series of continuation lines. .PP The magic file and keywords should have regular expression support. +Their use of ASCII TAB as a field delimiter is ugly and makes +it hard to edit the files, but is entrenched. .PP It might be advisable to allow upper-case letters in keywords for e.g., troff commands vs man page macros. @@ -283,18 +326,6 @@ The list of keywords in probably belongs in the Magic file. This could be done by using some keyword like `*' for the offset value. .PP -The program should malloc the magic file structures, -rather than using a fixed-size array as at present. -.PP -The magic file should be compiled into binary -(or better yet, fixed-length ASCII strings -for use in heterogenous network environments) for faster startup. -Then the program would run as fast as the Version 7 program of the same name, -with the flexibility of the System V version. -But then there would have to be yet another magic number for the -.I magic.out -file. -.PP Another optimisation would be to sort the magic file so that we can just run down all the tests for the first byte, first word, first long, etc, once we @@ -309,10 +340,12 @@ they are not as good as other guesses (e.g. ``Newsgroups:'' versus "Return-Path:"). Still, if the others don't pan out, it should be possible to use the first guess. .PP -Perhaps the program should automatically try all tests with -byte-swapping done, to avoid having to figure out the byte-swapped values -when constructing the magic file. -Of course this will run more slowly, so it should probably be -an option (-a?). +This program is slower than some vendors' file commands. .PP This manual page, and particularly this section, is too long. +.SH AVAILABILITY +You can obtain the original author's latest version by anonymous FTP +on +.B ftp.cs.toronto.edu +in the directory +.BR /pub/darwin/file . diff --git a/usr.bin/file/file.c b/usr.bin/file/file.c index 983619dd2f..b08cf79686 100644 --- a/usr.bin/file/file.c +++ b/usr.bin/file/file.c @@ -24,50 +24,68 @@ * * 4. This notice may not be removed or altered. */ +#ifndef lint +static char *moduleid = + "@(#)file.c,v 1.2 1993/06/10 00:38:08 jtc Exp"; +#endif /* lint */ #include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <sys/types.h> +#include <sys/param.h> /* for MAXPATHLEN */ #include <sys/stat.h> -#include "file.h" +#include <fcntl.h> /* for open() */ +#include <utime.h> +#include <unistd.h> /* for read() */ -#define USAGE "usage: %s [-c] [-f namefile] [-m magicfile] file...\n" +#include "file.h" -#ifndef lint -static char *moduleid = - "@(#)$Header: file.c,v 1.14 87/11/12 13:11:06 ian Exp $"; -#endif /* lint */ -extern char *ckfmsg; -int debug = 0, /* huh? */ - nbytes = 0, /* number of bytes read from a datafile */ - nmagic = 0; /* number of valid magic[]s */ -FILE *efopen(); -#ifdef MAGIC -char *magicfile = MAGIC; /* where magic be found */ +#ifdef S_IFLNK +# define USAGE "Usage: %s [-czL] [-f namefile] [-m magicfile] file...\n" #else -char *magicfile = "/etc/magic"; /* where magic be found */ +# define USAGE "Usage: %s [-cz] [-f namefile] [-m magicfile] file...\n" #endif -char *progname; -struct stat statbuf; -struct utimbuf { /* for utime(2), belongs in a .h file */ - time_t actime; /* access time */ - time_t modtime; /* modification time */ -}; + +#ifndef MAGIC +# define MAGIC "/etc/magic" +#endif + +int /* Global command-line options */ + debug = 0, /* debugging */ + lflag = 0, /* follow Symlinks (BSD only) */ + zflag = 0; /* follow (uncompress) compressed files */ + +int /* Misc globals */ + nmagic = 0; /* number of valid magic[]s */ + +struct magic *magic; /* array of magic entries */ + +char *magicfile = MAGIC;/* where magic be found */ + +char *progname; /* used throughout */ +int lineno; /* line number in the magic file */ + + +static void unwrap __P((char *fn)); /* * main - parse arguments and handle options */ +int main(argc, argv) int argc; char *argv[]; { int c; int check = 0, didsomefiles = 0, errflg = 0, ret = 0; - extern int optind; - extern char *optarg; - progname = argv[0]; + if ((progname = strrchr(argv[0], '/')) != NULL) + progname++; + else + progname = argv[0]; - while ((c = getopt(argc, argv, "cdf:m:")) != EOF) + while ((c = getopt(argc, argv, "cdf:Lm:z")) != EOF) switch (c) { case 'c': ++check; @@ -79,9 +97,17 @@ char *argv[]; unwrap(optarg); ++didsomefiles; break; +#ifdef S_IFLNK + case 'L': + ++lflag; + break; +#endif case 'm': magicfile = optarg; break; + case 'z': + zflag++; + break; case '?': default: errflg++; @@ -97,108 +123,150 @@ char *argv[]; exit(ret); if (optind == argc) { - if (!didsomefiles) + if (!didsomefiles) { (void)fprintf(stderr, USAGE, progname); + exit(2); + } } - else + else { + int i, wid, nw; + for (wid = 0, i = optind; i < argc; i++) { + nw = strlen(argv[i]); + if (nw > wid) + wid = nw; + } for (; optind < argc; optind++) - process(argv[optind]); + process(argv[optind], wid); + } - exit(0); + return 0; } + /* * unwrap -- read a file of filenames, do each one. */ +static void unwrap(fn) char *fn; { -#define FILENAMELEN 128 - char buf[FILENAMELEN]; + char buf[MAXPATHLEN]; FILE *f; + int wid = 0, cwid; - if ((f = fopen(fn, "r")) == NULL) - (void) fprintf(stderr, "%s: file %s unreadable\n", - progname, fn); - else { - while (fgets(buf, FILENAMELEN, f) != NULL) { - buf[strlen(buf)-1] = '\0'; - process(buf); - } - (void) fclose(f); + if ((f = fopen(fn, "r")) == NULL) { + error("Cannot open `%s' (%s).\n", fn, strerror(errno)); + /*NOTREACHED*/ } + + while (fgets(buf, MAXPATHLEN, f) != NULL) { + cwid = strlen(buf) - 1; + if (cwid > wid) + wid = cwid; + } + + rewind(f); + + while (fgets(buf, MAXPATHLEN, f) != NULL) { + buf[strlen(buf)-1] = '\0'; + process(buf, wid); + } + + (void) fclose(f); } + /* * process - process input file */ -process(inname) -char *inname; +void +process(inname, wid) +const char *inname; +int wid; { - int fd; - char buf[HOWMANY]; - struct utimbuf utbuf; + int fd = 0; + static const char stdname[] = "standard input"; + unsigned char buf[HOWMANY+1]; /* one extra for terminating '\0' */ + struct utimbuf utbuf; + struct stat sb; + int nbytes = 0; /* number of bytes read from a datafile */ if (strcmp("-", inname) == 0) { - (void) printf("standard input:\t"); - if (fstat(0, &statbuf)<0) - warning("cannot fstat; "); - fd = 0; - goto readit; + if (fstat(0, &sb)<0) { + error("cannot fstat `%s' (%s).\n", stdname, + strerror(errno)); + /*NOTREACHED*/ + } + inname = stdname; } + + if (wid > 0) + (void) printf("%s:%*s ", inname, wid - strlen(inname), ""); + + if (inname != stdname) { + /* + * first try judging the file based on its filesystem status + */ + if (fsmagic(inname, &sb) != 0) { + putchar('\n'); + return; + } - (void) printf("%s:\t", inname); + if ((fd = open(inname, O_RDONLY)) < 0) { + /* We can't open it, but we were able to stat it. */ + if (sb.st_mode & 0002) ckfputs("writeable, ", stdout); + if (sb.st_mode & 0111) ckfputs("executable, ", stdout); + ckfprintf(stdout, "can't read `%s' (%s).\n", + inname, strerror(errno)); + return; + } + } + /* - * first try judging the file based on its filesystem status - * Side effect: fsmagic updates global data `statbuf'. + * try looking at the first HOWMANY bytes */ - if (fsmagic(inname) != 0) { - /*NULLBODY*/; - } else if ((fd = open(inname, 0)) < 0) { - /* We can't open it, but we were able to stat it. */ - if (statbuf.st_mode & 0002) ckfputs("writeable, ", stdout); - if (statbuf.st_mode & 0111) ckfputs("executable, ", stdout); - warning("can't read"); - } else { -readit: - /* - * try looking at the first HOWMANY bytes - */ - if ((nbytes = read(fd, buf, HOWMANY)) == -1) - warning("read failed"); - if (nbytes == 0) { - ckfputs("empty", stdout); - } else + if ((nbytes = read(fd, (char *)buf, HOWMANY)) == -1) { + error("read failed (%s).\n", strerror(errno)); + /*NOTREACHED*/ + } + + if (nbytes == 0) + ckfputs("empty", stdout); + else { + buf[nbytes] = '\0'; /* null-terminate it */ + tryit(buf, nbytes); + } + + if (inname != stdname) { /* - * try tests in /etc/magic (or surrogate magic file) + * Try to restore access, modification times if read it. */ - if (softmagic(buf) == 1) - /*NULLBODY*/; - else if (ascmagic(buf) == 1) - /* - * try known keywords, check for ascii-ness too. - */ - /*NULLBODY*/; - else { - /* - * abandon hope, all ye who remain here - */ - ckfputs("data", stdout); - } - if (strcmp("-", inname) != 0) { - /* - * Restore access, modification times if we read it. - */ - utbuf.actime = statbuf.st_atime; - utbuf.modtime = statbuf.st_mtime; - (void) utime(inname, &utbuf); - /* we don't care if we lack perms */ - (void) close(fd); - } + utbuf.actime = sb.st_atime; + utbuf.modtime = sb.st_mtime; + (void) utime(inname, &utbuf); /* don't care if loses */ + (void) close(fd); } - (void) putchar('\n'); } +void +tryit(buf, nb) +unsigned char *buf; +int nb; +{ + /* + * try tests in /etc/magic (or surrogate magic file) + */ + if (softmagic(buf, nb) != 1) + /* + * try known keywords, check for ascii-ness too. + */ + if (ascmagic(buf, nb) != 1) + /* + * abandon hope, all ye who remain here + */ + ckfputs("data", stdout); +} + diff --git a/usr.bin/file/file.h b/usr.bin/file/file.h index 9916692cae..980a55ccac 100644 --- a/usr.bin/file/file.h +++ b/usr.bin/file/file.h @@ -1,6 +1,6 @@ /* * file.h - definitions for file(1) program - # @(#)$Header: file.h,v 1.4 87/09/18 10:56:09 ian Exp $ + * @(#)file.h,v 1.2 1993/06/10 00:38:09 jtc Exp * * Copyright (c) Ian F. Darwin, 1987. * Written by Ian F. Darwin. @@ -27,28 +27,101 @@ */ #define HOWMANY 1024 /* how much of the file to look at */ -#define MAXMAGIS 250 /* max entries in /etc/magic */ +#define MAXMAGIS 1000 /* max entries in /etc/magic */ #define MAXDESC 50 /* max leng of text description */ #define MAXstring 32 /* max leng of "string" types */ -#define ckfputs(str,fil) {if (fputs(str,fil)==EOF) error(ckfmsg,"");} struct magic { - short contflag; /* 1 if '>0' appears */ + short flag; +#define INDIR 1 /* if '>(...)' appears, */ + short cont_level; /* level of ">" */ + struct { + char type; /* byte short long */ + long offset; /* offset from indirection */ + } in; long offset; /* offset to magic number */ - char reln; /* relation (0=eq, '>'=gt, etc) */ +#define MASK 0200 /* this is a masked op, like & v1 = v2 */ + unsigned char reln; /* relation (0=eq, '>'=gt, etc) */ char type; /* int, short, long or string. */ char vallen; /* length of string value, if any */ #define BYTE 1 #define SHORT 2 #define LONG 4 #define STRING 5 +#define DATE 6 +#define BESHORT 7 +#define BELONG 8 +#define BEDATE 9 +#define LESHORT 10 +#define LELONG 11 +#define LEDATE 12 union VALUETYPE { char b; short h; long l; char s[MAXstring]; + unsigned char hs[2]; /* 2 bytes of a fixed-endian "short" */ + unsigned char hl[4]; /* 2 bytes of a fixed-endian "long" */ } value; /* either number or string */ + long mask; /* mask before comparison with value */ + char nospflag; /* supress space character */ char desc[MAXDESC]; /* description */ }; -extern void error(), exit(); +#include <stdio.h> /* Include that here, to make sure __P gets defined */ + +#ifndef __P +# if __STDC__ || __cplusplus +# define __P(a) a +# else +# define __P(a) () +# define const +# endif +#endif + +extern int apprentice __P((char *, int)); +extern int ascmagic __P((unsigned char *, int)); +extern void error __P((const char *, ...)); +extern void ckfputs __P((const char *, FILE *)); +struct stat; +extern int fsmagic __P((const char *, struct stat *)); +extern int is_compress __P((const unsigned char *, int *)); +extern int is_tar __P((unsigned char *)); +extern void magwarn __P((const char *, ...)); +extern void mdump __P((struct magic *)); +extern void process __P((const char *, int)); +extern void showstr __P((const char *)); +extern int softmagic __P((unsigned char *, int)); +extern void tryit __P((unsigned char *, int)); +extern int uncompress __P((const unsigned char *, unsigned char **, int)); +extern void ckfprintf __P((FILE *, const char *, ...)); + + + +extern int errno; /* Some unixes don't define this.. */ + +extern char *progname; /* the program name */ +extern char *magicfile; /* name of the magic file */ +extern int lineno; /* current line number in magic file */ + +extern struct magic *magic; /* array of magic entries */ +extern int nmagic; /* number of valid magic[]s */ + + +extern int debug; /* enable debugging? */ +extern int zflag; /* process compressed files? */ +extern int lflag; /* follow symbolic links? */ + +extern int optind; /* From getopt(3) */ +extern char *optarg; + +#if !defined(__STDC__) || defined(sun) +extern int sys_nerr; +extern char *sys_errlist[]; +#define strerror(e) \ + (((e) >= 0 && (e) < sys_nerr) ? sys_errlist[(e)] : "Unknown error") +#endif + +#ifndef MAXPATHLEN +#define MAXPATHLEN 512 +#endif diff --git a/usr.bin/file/fsmagic.c b/usr.bin/file/fsmagic.c index 63197ca646..69caf23c8c 100644 --- a/usr.bin/file/fsmagic.c +++ b/usr.bin/file/fsmagic.c @@ -26,7 +26,11 @@ */ #include <stdio.h> +#include <string.h> #include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdlib.h> #ifndef major /* if `major' not defined in types.h, */ #include <sys/sysmacros.h> /* try this one. */ #endif @@ -35,55 +39,57 @@ /* On most systems cpp will discard it automatically */ Congratulations, you have found a portability bug. Please grep /usr/include/sys and edit the above #include - to point at the file that defines the major macro. + to point at the file that defines the "major" macro. #endif /*major*/ -#include <sys/stat.h> + #include "file.h" #ifndef lint static char *moduleid = - "@(#)$Header: fsmagic.c,v 1.8 88/01/15 12:13:52 ian Exp $"; + "@(#)fsmagic.c,v 1.3 1993/06/10 00:38:10 jtc Exp"; #endif /* lint */ -extern char *progname; -extern char *ckfmsg, *magicfile; -extern int debug; -extern FILE *efopen(); - -fsmagic(fn) -char *fn; +int +fsmagic(fn, sb) +const char *fn; +struct stat *sb; { - extern struct stat statbuf; + int ret = 0; /* * Fstat is cheaper but fails for files you don't have read perms on. - * On 4.2BSD and similar systems, use lstat() so identify symlinks. + * On 4.2BSD and similar systems, use lstat() to identify symlinks. */ #ifdef S_IFLNK - if (lstat(fn, &statbuf) <0) -#else - if (stat(fn, &statbuf) <0) + if (!lflag) + ret = lstat(fn, sb); + else #endif - { - warning("can't stat", ""); - return -1; - } + ret = stat(fn, sb); /* don't merge into if; see "ret =" above */ - if (statbuf.st_mode & S_ISUID) ckfputs("setuid ", stdout); - if (statbuf.st_mode & S_ISGID) ckfputs("setgid ", stdout); - if (statbuf.st_mode & S_ISVTX) ckfputs("sticky ", stdout); + if (ret) { + ckfprintf(stdout, + /* Yes, I do mean stdout. */ + /* No \n, caller will provide. */ + "can't stat `%s' (%s).", fn, strerror(errno)); + return 1; + } + + if (sb->st_mode & S_ISUID) ckfputs("setuid ", stdout); + if (sb->st_mode & S_ISGID) ckfputs("setgid ", stdout); + if (sb->st_mode & S_ISVTX) ckfputs("sticky ", stdout); - switch (statbuf.st_mode & S_IFMT) { + switch (sb->st_mode & S_IFMT) { case S_IFDIR: ckfputs("directory", stdout); return 1; case S_IFCHR: (void) printf("character special (%d/%d)", - major(statbuf.st_rdev), minor(statbuf.st_rdev)); + major(sb->st_rdev), minor(sb->st_rdev)); return 1; case S_IFBLK: (void) printf("block special (%d/%d)", - major(statbuf.st_rdev), minor(statbuf.st_rdev)); + major(sb->st_rdev), minor(sb->st_rdev)); return 1; /* TODO add code to handle V7 MUX and Blit MUX files */ #ifdef S_IFIFO @@ -93,7 +99,55 @@ char *fn; #endif #ifdef S_IFLNK case S_IFLNK: - ckfputs("symbolic link", stdout); + { + char buf[BUFSIZ+4]; + register int nch; + struct stat tstatbuf; + + if ((nch = readlink(fn, buf, BUFSIZ-1)) <= 0) { + ckfprintf(stdout, "unreadable symlink (%s).", + strerror(errno)); + return 1; + } + buf[nch] = '\0'; /* readlink(2) forgets this */ + + /* If broken symlink, say so and quit early. */ + if (*buf == '/') { + if (stat(buf, &tstatbuf) < 0) { + ckfprintf(stdout, + "broken symbolic link to %s", buf); + return 1; + } + } + else { + char *tmp; + char buf2[BUFSIZ+BUFSIZ+4]; + + if ((tmp = strrchr(fn, '/')) == NULL) { + tmp = buf; /* in current directory anyway */ + } + else { + strcpy (buf2, fn); /* take directory part */ + buf2[tmp-fn+1] = '\0'; + strcat (buf2, buf); /* plus (relative) symlink */ + tmp = buf2; + } + if (stat(tmp, &tstatbuf) < 0) { + ckfprintf(stdout, + "broken symbolic link to %s", buf); + return 1; + } + } + + /* Otherwise, handle it. */ + if (lflag) { + process(buf, strlen(buf)); + return 1; + } else { /* just print what it points to */ + ckfputs("symbolic link to ", stdout); + ckfputs(buf, stdout); + } + } return 1; #endif #ifdef S_IFSOCK @@ -104,13 +158,14 @@ char *fn; case S_IFREG: break; default: - warning("invalid st_mode %d in statbuf!", statbuf.st_mode); + error("invalid mode 0%o.\n", sb->st_mode); + /*NOTREACHED*/ } /* * regular file, check next possibility */ - if (statbuf.st_size == 0) { + if (sb->st_size == 0) { ckfputs("empty", stdout); return 1; } diff --git a/usr.bin/file/is_tar.c b/usr.bin/file/is_tar.c index e0c32d7f97..2b8eb10d4d 100644 --- a/usr.bin/file/is_tar.c +++ b/usr.bin/file/is_tar.c @@ -5,18 +5,24 @@ * Pubic Domain version written 26 Aug 1985 John Gilmore (ihnp4!hoptoad!gnu). * * @(#)list.c 1.18 9/23/86 Public Domain - gnu + * is_tar.c,v 1.2 1993/06/10 00:38:12 jtc Exp * * Comments changed and some code/comments reformatted * for file command by Ian Darwin. */ +#include <string.h> #include <ctype.h> #include <sys/types.h> #include "tar.h" #define isodigit(c) ( ((c) >= '0') && ((c) <= '7') ) -long from_oct(); /* Decode octal number */ +#if defined(__STDC__) || defined(__cplusplus) +static long from_oct(int, char*); /* Decode octal number */ +#else +static long from_oct(); +#endif /* * Return @@ -25,9 +31,10 @@ long from_oct(); /* Decode octal number */ * 2 for Unix Std (POSIX) tar file. */ int -is_tar(header) - register union record *header; +is_tar(buf) +unsigned char *buf; { + register union record *header = (union record *)buf; register int i; register long sum, recsum; register char *p; @@ -36,7 +43,7 @@ is_tar(header) sum = 0; p = header->charptr; - for (i = sizeof(*header); --i >= 0;) { + for (i = sizeof(union record); --i >= 0;) { /* * We can't use unsigned char here because of old compilers, * e.g. V7. @@ -64,7 +71,7 @@ is_tar(header) * * Result is -1 if the field is invalid (all blank, or nonoctal). */ -long +static long from_oct(digs, where) register int digs; register char *where; diff --git a/usr.bin/file/magdir/aa b/usr.bin/file/magdir/Header similarity index 100% rename from usr.bin/file/magdir/aa rename to usr.bin/file/magdir/Header diff --git a/usr.bin/file/magdir/Localstuff b/usr.bin/file/magdir/Localstuff new file mode 100644 index 0000000000..a05e2694ff --- /dev/null +++ b/usr.bin/file/magdir/Localstuff @@ -0,0 +1,3 @@ +# Localstuff,v 1.1 1993/06/10 00:57:30 jtc Exp +# Add any locally-observed files here. Remember: +# text if readable, executable if runnable binary, data if unreadable. diff --git a/usr.bin/file/magdir/aalocal b/usr.bin/file/magdir/aalocal deleted file mode 100644 index d0c2a5191c..0000000000 --- a/usr.bin/file/magdir/aalocal +++ /dev/null @@ -1,3 +0,0 @@ -# Add any locally-observed files here. Remember: -# text if readable, executable if runnable binary, data if unreadable. -22 short 023000 core dump data diff --git a/usr.bin/file/magdir/alliant b/usr.bin/file/magdir/alliant new file mode 100644 index 0000000000..52ed628db2 --- /dev/null +++ b/usr.bin/file/magdir/alliant @@ -0,0 +1,15 @@ +# +# Alliant FX series a.out files: +# If the FX series is the one that had a processor with a 68K-derived +# instruction set, the "short" should probably become "beshort" and the +# "long" should probably become "belong". +# If it's the i860-based one, they should probably become either the +# big-endian or little-endian versions, depending on the mode they ran +# the 860 in.... +# +0 short 0420 0420 Alliant virtual executable +>2 short &0x0020 common library +>16 long >0 not stripped +0 short 0421 0421 Alliant compact executable +>2 short &0x0020 common library +>16 long >0 not stripped diff --git a/usr.bin/file/magdir/apl b/usr.bin/file/magdir/apl new file mode 100644 index 0000000000..6ede926af3 --- /dev/null +++ b/usr.bin/file/magdir/apl @@ -0,0 +1,4 @@ +# +# magic.apl: +# +0 long 0100554 APL workspace (Ken's original?) diff --git a/usr.bin/file/magdir/ar b/usr.bin/file/magdir/ar new file mode 100644 index 0000000000..5ab63aeab5 --- /dev/null +++ b/usr.bin/file/magdir/ar @@ -0,0 +1,66 @@ +# +# "ar", for all kinds of archives. +# +# XXX - why are there multiple <ar> thingies? Note that 0x213c6172 is +# "!<ar", so, for new-style (4.xBSD/SVR2andup) archives, we have: +# +# 0 string !<arch> current ar archive +# 0 long 0x213c6172 archive file +# +# and for SVR3.1 archives, we have: +# +# 0 string \<ar> System V Release 1 ar archive +# 0 string =<ar> archive +# 0 string =<ar> archive +# +# XXX - did Aegis really store shared libraries, breakpointed modules, +# and absolute code program modules in the same format as new-style +# "ar" archives? +# +0 string !<arch> current ar archive +>8 string __.SYMDEF random library +>0 belong =65538 - pre SR9.5 +>0 belong =65539 - post SR9.5 +>0 beshort 2 - object archive +>0 beshort 3 - shared library module +>0 beshort 4 - debug break-pointed module +>0 beshort 5 - absolute code program module +0 string \<ar> System V Release 1 ar archive +0 string =<ar> archive +# +# XXX - from "vax", which appears to collect a bunch of byte-swapped +# thingies, to help you recognize VAX files on big-endian machines; +# with "leshort", "lelong", and "string", that's no longer necessary.... +# +# 0 long 0x3c61723e VAX 5.0 archive +# +0 long 0x213c6172 archive file +0 lelong 0177555 very old VAX archive +0 leshort 0177555 very old PDP-11 archive +# +# XXX - "pdp" claims that 0177545 can have an __.SYMDEF member and thus +# be a random library (it said 0xff65 rather than 0177545). +# +0 lelong 0177545 old VAX archive +>8 string __.SYMDEF random library +0 leshort 0177545 old PDP-11 archive +>8 string __.SYMDEF random library +# +0 string =<ar> archive +# +# From "pdp": +# +0 lelong 0x39bed PDP-11 old archive +0 lelong 0x39bee PDP-11 4.0 archive +# +0 string -h- Software Tools format archive text +# "arc" archiver +0 byte 26 'arc' archive +>1 byte 0 (empty) +>1 byte 1 (old format) +# Rahul Dhesi's zoo archive format, from keith@cerberus.uchicago.edu. +20 long 0xdca7c4fd Rahul Dhesi's "zoo" archive +# ZIP archiver +0 string PK zip archive file +>2 byte >0 - version [%d +>3 byte >0 %d] diff --git a/usr.bin/file/magdir/arc b/usr.bin/file/magdir/arc deleted file mode 100644 index 1ebb260162..0000000000 --- a/usr.bin/file/magdir/arc +++ /dev/null @@ -1,3 +0,0 @@ -0 byte 26 'arc' archive ->1 byte 0 (empty) ->1 byte 1 (old format) diff --git a/usr.bin/file/magdir/archive b/usr.bin/file/magdir/archive deleted file mode 100644 index e676057eb1..0000000000 --- a/usr.bin/file/magdir/archive +++ /dev/null @@ -1,11 +0,0 @@ -0 short 070707 cpio archive -0 string 070707 ASCII cpio archive -0 long 0177555 very old archive -0 short 0177555 very old PDP-11 archive -0 long 0177545 old archive -0 short 0177545 old PDP-11 archive -0 long 0100554 apl workspace -0 string =<ar> archive -0 string !<arch> archive ->8 string __.SYMDEF random library -0 string -h- archive (Software Tools format) text diff --git a/usr.bin/file/magdir/att3b b/usr.bin/file/magdir/att3b new file mode 100644 index 0000000000..dfd4670072 --- /dev/null +++ b/usr.bin/file/magdir/att3b @@ -0,0 +1,34 @@ +# +# AT&T 3B machines +# +# The `versions' should be un-commented if they work for you. +# (Was the problem just one of endianness?) +# +# 3B20 +# +0 beshort 0550 3b20 COFF executable +>12 belong >0 not stripped +#>22 beshort >0 - version %ld +0 beshort 0551 3b20 COFF executable (TV) +>12 belong >0 not stripped +#>22 beshort >0 - version %ld +# +# WE32K +# +0 beshort 0560 WE32000 COFF +>18 beshort ^00000020 object +>18 beshort &00000020 executable +>12 belong >0 not stripped +>18 beshort ^00010000 N/A on 3b2/300 w/paging +>18 beshort &00020000 32100 required +>18 beshort &00040000 and mau hardware required +>20 beshort 0407 (impure) +>20 beshort 0410 (pure) +>20 beshort 0413 (demand paged) +>20 beshort 0443 (target shared library) +>22 beshort >0 - version %ld +0 beshort 0561 WE32000 COFF executable (TV) +>12 belong >0 not stripped +#>18 beshort &00020000 - 32100 required +#>18 beshort &00040000 and mau hardware required +#>22 beshort >0 - version %ld diff --git a/usr.bin/file/magdir/audio b/usr.bin/file/magdir/audio new file mode 100644 index 0000000000..6b4cb36453 --- /dev/null +++ b/usr.bin/file/magdir/audio @@ -0,0 +1,43 @@ +# +# Sound formats, from Jan Nicolai Langfeldt <janl@ifi.uio.no>, +# + +# Sun/NeXT audio data +0 string .snd audio data: +>12 belong 1 8-bit u-law, +>12 belong 2 8-bit linear PCM, +>12 belong 3 16-bit linear PCM, +>12 belong 4 24-bit linear PCM, +>12 belong 5 32-bit linear PCM, +>12 belong 6 32-bit floating point, +>12 belong 7 64-bit floating point, +>12 belong 23 compressed (G.721 ADPCM), +>20 belong 1 mono, +>20 belong 2 stereo, +>20 belong 4 quad, +>16 belong x %d Hz +# DEC systems (e.g. DECstation 5000) use a variant of the Sun/NeXT format +# that uses little-endian encoding and has a different magic number +# (0x0064732E in little-endian encoding). +0 lelong 0x0064732E DEC audio data: +>12 lelong 1 8-bit u-law, +>12 lelong 2 8-bit linear PCM, +>12 lelong 3 16-bit linear PCM, +>12 lelong 4 24-bit linear PCM, +>12 lelong 5 32-bit linear PCM, +>12 lelong 6 32-bit floating point, +>12 lelong 7 64-bit floating point, +>12 lelong 23 compressed (G.721 ADPCM), +>20 lelong 1 mono, +>20 lelong 2 stereo, +>20 lelong 4 quad, +>16 lelong x %d Hz +# Bytes 0-3 of AIFF, AIFF-C, & 8SVX audio files are "FORM" +8 string AIFF AIFF audio data +8 string AIFC AIFF-C audio data +8 string 8SVX IFF/8SVX audio data +# Bytes 0-3 of Waveform (*.wav) audio files are "RIFF" +8 string WAVE Waveform audio data +0 string Creative\ Voice\ File Soundblaster audio data +0 long 0x4e54524b MultiTrack sound data file +>4 long x - version %ld diff --git a/usr.bin/file/magdir/blit b/usr.bin/file/magdir/blit new file mode 100644 index 0000000000..0f4804da8c --- /dev/null +++ b/usr.bin/file/magdir/blit @@ -0,0 +1,16 @@ +# 68K Blit stuff as seen from 680x0 machine +# Note that this 0407 conflicts with several other a.out formats... +# +# XXX - should this be redone with "be" and "le", so that it works on +# little-endian machines as well? If so, what's the deal with +# "VAX-order" and "VAX-order2"? +# +#0 long 0407 68K Blit (standalone) executable +#0 short 0407 VAX-order2 68K Blit (standalone) executable +0 short 03401 VAX-order 68K Blit (standalone) executable +0 long 0406 68k Blit mpx/mux executable +0 short 0406 VAX-order2 68k Blit mpx/mux executable +0 short 03001 VAX-order 68k Blit mpx/mux executable +# Need more values for WE32 DMD executables. +# Note that 0520 is the same as COFF +#0 short 0520 tty630 layers executable diff --git a/usr.bin/file/magdir/bsdi b/usr.bin/file/magdir/bsdi new file mode 100644 index 0000000000..add5fddd9f --- /dev/null +++ b/usr.bin/file/magdir/bsdi @@ -0,0 +1,3 @@ +# BSDI BSD/386 +0 long 0314 demand paged (first page unmapped) pure executable +>16 long >0 not stripped diff --git a/usr.bin/file/magdir/c b/usr.bin/file/magdir/c deleted file mode 100644 index 74f1c4d51f..0000000000 --- a/usr.bin/file/magdir/c +++ /dev/null @@ -1,7 +0,0 @@ -# this first will upset you if you're a PL/1 shop... -# in which case rm it; ascmagic will catch real C programs -0 string /* c program text -# check for various C program generators... -# offsets derived empirically, your offsets may vary! -# (this obviously belongs in ascmagic.c/names.h!). -53 string yyprevious c program text (from lex) diff --git a/usr.bin/file/magdir/c-lang b/usr.bin/file/magdir/c-lang new file mode 100644 index 0000000000..79b2254bc6 --- /dev/null +++ b/usr.bin/file/magdir/c-lang @@ -0,0 +1,3 @@ +# this first will upset you if you're a PL/1 shop... +# in which case rm it; ascmagic will catch real C programs +0 string /* c program text diff --git a/usr.bin/file/magdir/clipper b/usr.bin/file/magdir/clipper new file mode 100644 index 0000000000..f4e9fa73cb --- /dev/null +++ b/usr.bin/file/magdir/clipper @@ -0,0 +1,63 @@ +# +# Intergraph, formerly Fairchild, Clipper. +# +# XXX - what byte order does the Clipper use? +# +# XXX - what's the "!" stuff: +# +# >18 short !074000,000000 C1 R1 +# >18 short !074000,004000 C2 R1 +# >18 short !074000,010000 C3 R1 +# >18 short !074000,074000 TEST +# +# I shall assume it's ANDing the field with the first value and +# comparing it with the second, and rewrite it as: +# +# >18 short&074000 000000 C1 R1 +# >18 short&074000 004000 C2 R1 +# >18 short&074000 010000 C3 R1 +# >18 short&074000 074000 TEST +# +# as SVR3.1's "file" doesn't support anything of the "!074000,000000" +# sort, nor does SunOS 4.x, so either it's something Intergraph added +# in CLIX, or something AT&T added in SVR3.2 or later, or something +# somebody else thought was a good idea; it's not documented in the +# man page for this version of "magic", nor does it appear to be +# implemented (at least not after I blew off the bogus code to turn +# old-style "&"s into new-style "&"s, which just didn't work at all). +# +0 short 0575 CLIPPER COFF executable (VAX #) +>20 short 0407 (impure) +>20 short 0410 (5.2 compatible) +>20 short 0411 (pure) +>20 short 0413 (demand paged) +>20 short 0443 (target shared library) +>12 long >0 not stripped +>22 short >0 - version %ld +0 short 0577 CLIPPER COFF executable +>18 short&074000 000000 C1 R1 +>18 short&074000 004000 C2 R1 +>18 short&074000 010000 C3 R1 +>18 short&074000 074000 TEST +>20 short 0407 (impure) +>20 short 0410 (pure) +>20 short 0411 (separate I&D) +>20 short 0413 (paged) +>20 short 0443 (target shared library) +>12 long >0 not stripped +>22 short >0 - version %ld +>48 long&01 01 alignment trap enabled +>52 byte 1 -Ctnc +>52 byte 2 -Ctsw +>52 byte 3 -Ctpw +>52 byte 4 -Ctcb +>53 byte 1 -Cdnc +>53 byte 2 -Cdsw +>53 byte 3 -Cdpw +>53 byte 4 -Cdcb +>54 byte 1 -Csnc +>54 byte 2 -Cssw +>54 byte 3 -Cspw +>54 byte 4 -Cscb +4 string pipe CLIPPER instruction trace +4 string prof CLIPPER instruction profile diff --git a/usr.bin/file/magdir/commands b/usr.bin/file/magdir/commands index 417eee8076..a987ff212c 100644 --- a/usr.bin/file/magdir/commands +++ b/usr.bin/file/magdir/commands @@ -1,14 +1,32 @@ -0 string #!\ /bin/sh commands text -0 string #!/bin/sh commands text -0 string #!\ /bin/csh C shell commands text -0 string #!/bin/csh C shell commands text -0 string #!\ /bin/awk awk commands text -0 string #!/bin/awk awk commands text -0 string #!\ / some kinda commands text -0 string #!/ some kinda commands text -0 string #!\ commands text ->3 string >\0 for %s -# An "antique" kernel is either unmodified early V7, -# without DMR's 1979 mod for #!, or any kernel -# derived from a pre-v7 kernel (i.e., System V) -0 string :\ shell archive or commands for antique kernel text +# +# "Commands": stuff for various shells and interpreters. +# +0 string :\ shell archive or commands for antique kernel text +0 string #!/bin/sh Bourne Shell script text +0 string #!\ /bin/sh Bourne Shell script text +0 string #!/bin/csh C Shell script text +0 string #!\ /bin/csh C Shell script text +# korn shell magic, sent by George Wu, gwu@clyde.att.com +0 string #!/bin/ksh Korn Shell script text +0 string #!\ /bin/ksh Korn Shell script text +0 string #!/bin/tcsh Tenex C Shell script text +0 string #!\ /bin/tcsh Tenex C Shell script text +0 string #!/usr/local/tcsh Tenex C Shell script text +0 string #!\ /usr/local/tcsh Tenex C Shell script text +0 string #!/usr/local/bin/tcsh Tenex C Shell script text +0 string #!\ /usr/local/bin/tcsh Tenex C Shell script text +0 string #!/bin/awk Awk Commands text +0 string #!\ /bin/awk Awk Commands text +0 string #!\ / a +>3 string >\0 %s script +0 string #!/ a +>2 string >\0 %s script +0 string #!\ commands text +>3 string >\0 for %s + +# For Larry Wall's perl language. The ``eval'' line recognizes an +# outrageously clever hack for USG systems. +# Keith Waclena <keith@cerberus.uchicago.edu> +0 string #!/bin/perl perl commands text +0 string #!\ /bin/perl perl commands text +0 string eval\ "exec\ /bin/perl perl commands text diff --git a/usr.bin/file/magdir/compress b/usr.bin/file/magdir/compress index d90c6ba908..d296559825 100644 --- a/usr.bin/file/magdir/compress +++ b/usr.bin/file/magdir/compress @@ -1,18 +1,57 @@ -0 short 017037 packed data -# CPL - added pack to /etc/magic +# +# Formats for various forms of compressed data +# Formats for "compress" proper have been moved into "compress.c", +# because it tries to uncompress it to figure out what's inside. +# +# XXX - the two "packed data" versions are byte-swapped versions of +# one another; is that because the 2-byte magic number is written +# out in native byte order, with "unpack" figuring out the byte order +# from the magic number (in which case both can be left as is, or +# changed to specify a byte order *and* to indicate the byte order of +# the packing machine), or because the old "file" didn't have any way of +# having "magic"-file entries that specified a particular byte order? +# 0 short 017436 packed data -0 short 0145405 huf output +0 short 017037 packed data -0 string \037\235 compressed data -# non block compressed ->2 byte 12 - with 12 bits ->2 byte 13 - with 13 bits ->2 byte 14 - with 14 bits ->2 byte 15 - with 15 bits ->2 byte 16 - with 16 bits -# block compressed ->2 byte 140 - with 12 bits ->2 byte 141 - with 13 bits ->2 byte 142 - with 14 bits ->2 byte 143 - with 15 bits ->2 byte 144 - with 16 bits +# +# This magic number is byte-order-independent. +# +0 short 017437 old packed data + +# +0 string \377\037 compacted data +0 short 0145405 huf output +# +# Squeeze and Crunch, from Keith Waclena <keith@cerberus.uchicago.edu> +# These numbers were gleaned from the Unix versions of the programs to +# handle these formats. Note that I can only uncrunch, not crunch, and +# I didn't have a crunched file handy, so the crunch number is untested. +0 short 0x76FF squeezed data (CP/M, DOS) +0 short 0x76FE crunched data (CP/M, DOS) +# Freeze +0 short 0x1f9f Frozen file 2.1 +0 short 0x1f9e Frozen file 1.0 +# +# GNU gzip compressor, from christos@deshaw.com (Christos Zoulas) +# +0 string \037\213 gzip compressed file method: +>2 byte <8 reserved, +>2 byte 8 deflate, +>3 byte &0x1f flags: +>3 byte &0x01 ascii-text, +>3 byte &0x02 multi-part, +>3 byte &0x04 name-present, +>3 byte &0x08 comment-present, +>3 byte &0x10 encrypted, +>4 ledate x last modified: %s, +>8 byte x extra-flags: %x, +>9 byte =0x00 os: MS/DOS +>9 byte =0x01 os: Amiga +>9 byte =0x02 os: VMS +>9 byte =0x03 os: Unix +>9 byte =0x05 os: Atari +>9 byte =0x06 os: OS/2 +>9 byte =0x07 os: MacOS +>9 byte =0x0A os: Tops/20 +>9 byte =0x0B os: Win/32 diff --git a/usr.bin/file/magdir/convex b/usr.bin/file/magdir/convex index 560fd485a6..0220a81473 100644 --- a/usr.bin/file/magdir/convex +++ b/usr.bin/file/magdir/convex @@ -1 +1,4 @@ +# +# XXX - what byte order does a Convex use? +# 0 long 0513 Convex executable diff --git a/usr.bin/file/magdir/cpio b/usr.bin/file/magdir/cpio new file mode 100644 index 0000000000..d1805cc171 --- /dev/null +++ b/usr.bin/file/magdir/cpio @@ -0,0 +1,16 @@ +# +# Yes, the two "cpio archive" formats *are* supposed to just be "short". +# The idea is to indicate archives produced on machines with the same +# byte order as the machine running "file" with "cpio archive", and +# to indicate archives produced on machines with the opposite byte order +# from the machine running "file" with "byte-swapped cpio archive". +# +# The SVR4 "cpio(4)" hints that there are additional formats, but they +# are defined as "short"s; I think all the new formats are +# character-header formats, and thus are strings not numbers. +# +0 short 070707 cpio archive +0 short 0143561 byte-swapped cpio archive +0 string 070707 ASCII cpio archive (pre-SVR4 or odc) +0 string 070701 ASCII cpio archive (SVR4 with no CRC) +0 string 070702 ASCII cpio archive (SVR4 with CRC) diff --git a/usr.bin/file/magdir/diamond b/usr.bin/file/magdir/diamond new file mode 100644 index 0000000000..d515d61fe9 --- /dev/null +++ b/usr.bin/file/magdir/diamond @@ -0,0 +1,8 @@ +# +# ... diamond is a multi-media mail and electronic conferencing system.... +# +# XXX - I think it was either renamed Slate, or replaced by Slate.... +# +# The full deal is too long... +#0 string <list>\n<protocol\ bbn-multimedia-format> Diamond Multimedia Document +0 string =<list>\n<protocol\ bbn-m Diamond Multimedia Document diff --git a/usr.bin/file/magdir/dump b/usr.bin/file/magdir/dump new file mode 100644 index 0000000000..923fffd0d2 --- /dev/null +++ b/usr.bin/file/magdir/dump @@ -0,0 +1,43 @@ +# +# magic.dump, dump file format. For new and old dump filesystems +# +# No, these shouldn't specify a specific byte order, because the byte +# order is dependent on the machine that produced the dump. +# +24 long 60012 new-fs dump file, +>4 date x Previous dump %s, +>8 date x This dump %s, +>12 long >0 Volume %ld, +>692 long 0 Level zero, type: +>692 long >0 Level %d, type: +>0 long 1 tape header, +>0 long 2 beginning of file record, +>0 long 3 map of inodes on tape, +>0 long 4 continuation of file record, +>0 long 5 end of volume, +>0 long 6 map of inodes deleted, +>0 long 7 end of medium (for floppy), +>676 string >\0 Label %s, +>696 string >\0 Filesystem %s, +>760 string >\0 Device %s, +>824 string >\0 Host %s, +>888 long >0 Flags %x + +24 long 60011 old-fs dump file, +#>4 date x Previous dump %s, +#>8 date x This dump %s, +>12 long >0 Volume %ld, +>692 long 0 Level zero, type: +>692 long >0 Level %d, type: +>0 long 1 tape header, +>0 long 2 beginning of file record, +>0 long 3 map of inodes on tape, +>0 long 4 continuation of file record, +>0 long 5 end of volume, +>0 long 6 map of inodes deleted, +>0 long 7 end of medium (for floppy), +>676 string >\0 Label %s, +>696 string >\0 Filesystem %s, +>760 string >\0 Device %s, +>824 string >\0 Host %s, +>888 long >0 Flags %x diff --git a/usr.bin/file/magdir/elf b/usr.bin/file/magdir/elf new file mode 100644 index 0000000000..e34332dc1c --- /dev/null +++ b/usr.bin/file/magdir/elf @@ -0,0 +1,44 @@ +# +# ELF +# Missing MIPS image type and flags +# +# Question marks on processor types flag "should not happen because the +# byte order is wrong". We have to check the byte order flag to see what +# byte order all the other stuff in the header is in. +# +0 string \177ELF ELF +>4 byte 0 invalid class +>4 byte 1 32-bit +>4 byte 2 64-bit +>5 byte 0 invalid byte order +>5 byte 1 LSB +>>16 leshort 0 unknown type +>>16 leshort 1 relocatable +>>16 leshort 2 executable +>>16 leshort 3 dynamic lib +>>16 leshort 4 core file +>>18 leshort 0 unknown machine +>>18 leshort 1 WE32100 and up +>>18 leshort 2 SPARC? +>>18 leshort 3 i386 (386 and up) +>>18 leshort 4 M68000? +>>18 leshort 5 M88000? +>>18 leshort 7 i860 +>>20 lelong 1 Version 1 +>>36 lelong 1 MathCoPro/FPU/MAU Required +>5 byte 2 MSB +>>16 beshort 0 unknown type +>>16 beshort 1 relocatable +>>16 beshort 2 executable +>>16 beshort 3 dynamic lib +>>16 beshort 4 core file +>>18 beshort 0 unknown machine +>>18 beshort 1 WE32100 and up +>>18 beshort 2 SPARC +>>18 beshort 3 i386 (386 and up)? +>>18 beshort 4 M68000 +>>18 beshort 5 M88000 +>>18 beshort 7 i860 +>>20 belong 1 Version 1 +>>36 belong 1 MathCoPro/FPU/MAU Required + diff --git a/usr.bin/file/magdir/encore b/usr.bin/file/magdir/encore new file mode 100644 index 0000000000..7244f87a91 --- /dev/null +++ b/usr.bin/file/magdir/encore @@ -0,0 +1,20 @@ +# +# magic.encore: Recognize encore machines +# +# XXX - needs to have the byte order specified (NS32K was little-endian, +# dunno whether they run the 88K in little-endian mode or not). +# +0 short 0x154 Encore +>20 short 0x107 executable +>20 short 0x108 pure executable +>20 short 0x10b demand-paged executable +>20 short 0x10f unsupported executable +>12 long >0 not stripped +>22 short >0 - version %ld +>22 short 0 - +#>4 date x stamp %s +0 short 0x155 Encore unsupported executable +>12 long >0 not stripped +>22 short >0 - version %ld +>22 short 0 - +#>4 date x stamp %s diff --git a/usr.bin/file/magdir/floppy.raw b/usr.bin/file/magdir/floppy.raw new file mode 100644 index 0000000000..75091b9b55 --- /dev/null +++ b/usr.bin/file/magdir/floppy.raw @@ -0,0 +1 @@ +0 string \366\366\366\366 Formatted floppy w/ no filesystem data diff --git a/usr.bin/file/magdir/frame b/usr.bin/file/magdir/frame index 2d503c6674..46e8648fdd 100644 --- a/usr.bin/file/magdir/frame +++ b/usr.bin/file/magdir/frame @@ -1,4 +1,28 @@ +# # Magic number for FrameMaker files -# Thanks to Berry Kercheval +# This stuff came on a FrameMaker demo tape, most of which is +# copyright, but this file is "published" as witness the following: # 0 string \<MakerFile FrameMaker document +>11 string 3.0 (3.0 +>11 string 2.0 (2.0 +>11 string 1.0 (1.0 +>14 byte x %c) +0 string \<MIFFile FrameMaker MIF file +>9 string 3.0 (3.0) +>9 string 2.0 (2.0) +>9 string 1.0 (1.x) +0 string \<MakerDictionary FraneMaker Dictionary text +>17 string 3.0 (3.0) +>17 string 2.0 (2.0) +>17 string 1.0 (1.x) +0 string \<MakerScreenFon FrameMaker Font file +>17 string 1.01 (%s) +0 string \<MML FrameMaker MML file +0 string \<Book FrameMaker Book file +>10 string 3.0 (3.0 +>10 string 2.0 (2.0 +>10 string 1.0 (1.0 +>13 byte x %c) +0 string \<Maker Intermediate Print File FrameMaker IPL file +0 string \<MakerDictionary FraneMaker Dictionary text diff --git a/usr.bin/file/magdir/freebsd b/usr.bin/file/magdir/freebsd new file mode 100644 index 0000000000..9b5e1f9682 --- /dev/null +++ b/usr.bin/file/magdir/freebsd @@ -0,0 +1,5 @@ +# the following are for 386BSD/FreeBSD + +0 long 0410 pure executable +0 long 0413 demand paged executable +>16 long >0 not stripped diff --git a/usr.bin/file/magdir/hp b/usr.bin/file/magdir/hp new file mode 100644 index 0000000000..1353d8c14d --- /dev/null +++ b/usr.bin/file/magdir/hp @@ -0,0 +1,153 @@ +# +# magic.hp: Hewlett Packard Magic +# +# XXX - somebody should figure out whether any byte order needs to be +# applied to the "TML" stuff; I'm assuming the Apollo stuff is +# big-endian as it was mostly 68K-based. +# +# HP-PA is big-endian, so it (and "800", which is *also* HP-PA-based; I +# assume "HPPA-RISC1.1" really means "HP-PA Version 1.1", which first +# showed up in the 700 series, although later 800 series machines are, +# I think, based on the PA7100 which implements HP-PA 1.1) are flagged +# as big-endian. +# +# I think the 500 series was the old stack-based machines, running a +# UNIX environment atop the "SUN kernel"; dunno whether it was +# big-endian or little-endian. +# +# I'm guessing that the 200 series was 68K-based; the 300 and 400 series +# are. +# +# The "misc" stuff needs a byte order; the archives look suspiciously +# like the old 177545 archives (0xff65 = 0177545). +# +#### Old Apollo stuff +0 beshort 0627 Apollo m68k COFF executable +>18 beshort ^040000 not stripped +>22 beshort >0 - version %ld +0 beshort 0624 apollo a88k COFF executable +>18 beshort ^040000 not stripped +>22 beshort >0 - version %ld +0 long 01203604016 TML 0123 byte-order format +0 long 01702407010 TML 1032 byte-order format +0 long 01003405017 TML 2301 byte-order format +0 long 01602007412 TML 3210 byte-order format +#### HPPA +0 belong 0x02100106 HPPA-RISC1.1 relocatable object +0 belong 0x02100107 HPPA-RISC1.1 executable +>(144) belong 0x054ef630 dynamically linked +>96 belong >0 -not stripped + +0 belong 0x02100108 HPPA-RISC1.1 shared executable +>(144) belong 0x054ef630 dynamically linked +>96 belong >0 -not stripped + +0 belong 0x0210010b HPPA-RISC1.1 demand-load executable +>(144) belong 0x054ef630 dynamically linked +>96 belong >0 -not stripped + +0 belong 0x0210010e HPPA-RISC1.1 shared library +>96 belong >0 -not stripped + +0 belong 0x0210010d HPPA-RISC1.1 dynamic load library +>96 belong >0 -not stripped + +#### 800 +0 belong 0x020b0106 HP s800 relocatable object + +0 belong 0x020b0107 HP s800 executable +>(144) belong 0x054ef630 dynamically linked +>96 belong >0 -not stripped + +0 belong 0x020b0108 HP s800 shared executable +>(144) belong 0x054ef630 dynamically linked +>96 belong >0 -not stripped + +0 belong 0x020b010b HP s800 demand-load executable +>(144) belong 0x054ef630 dynamically linked +>96 belong >0 -not stripped + +0 belong 0x020b010e HP s800 shared library +>96 belong >0 -not stripped + +0 belong 0x020b010d HP s800 dynamic load library +>96 belong >0 -not stripped + +0 belong 0x213c6172 archive file +>68 belong 0x020b0619 -HP s800 relocatable library + +#### 500 +0 long 0x02080106 HP s500 relocatable executable +>16 long >0 -version %ld + +0 long 0x02080107 HP s500 executable +>16 long >0 -version %ld + +0 long 0x02080108 HP s500 pure executable +>16 long >0 -version %ld + +#### 200 +0 belong 0x020c0108 HP s200 pure executable +>4 beshort >0 -version %ld +>8 belong &0x80000000 save fp regs +>8 belong &0x40000000 dynamically linked +>8 belong &0x20000000 debuggable +>36 belong >0 not stripped + +0 belong 0x020c0107 HP s200 executable +>4 beshort >0 -version %ld +>8 belong &0x80000000 save fp regs +>8 belong &0x40000000 dynamically linked +>8 belong &0x20000000 debuggable +>36 belong >0 not stripped + +0 belong 0x020c010b HP s200 demand-load executable +>4 beshort >0 -version %ld +>8 belong &0x80000000 save fp regs +>8 belong &0x40000000 dynamically linked +>8 belong &0x20000000 debuggable +>36 belong >0 not stripped + +0 belong 0x020c0106 HP s200 relocatable executable +>4 beshort >0 -version %ld +>6 beshort >0 -highwater %d +>8 belong &0x80000000 save fp regs +>8 belong &0x20000000 debuggable +>8 belong &0x10000000 PIC + +0 belong 0x020a0108 HP s200 (2.x release) pure executable +>4 beshort >0 -version %ld +>36 belong >0 not stripped + +0 belong 0x020a0107 HP s200 (2.x release) executable +>4 beshort >0 -version %ld +>36 belong >0 not stripped + +0 belong 0x020c010e HP s200 shared library +>4 beshort >0 -version %ld +>6 beshort >0 -highwater %d +>36 belong >0 not stripped + +0 belong 0x020c010d HP s200 dynamic load library +>4 beshort >0 -version %ld +>6 beshort >0 -highwater %d +>36 belong >0 not stripped + +#### MISC +0 long 0x0000ff65 HP old archive +0 long 0x020aff65 HP s200 old archive +0 long 0x020cff65 HP s200 old archive +0 long 0x0208ff65 HP s500 old archive + +0 long 0x015821a6 HP core file + +0 long 0x4da7eee8 HP-WINDOWS font +>8 byte >0 -version %ld +0 string Bitmapfile HP Bitmapfile + +0 string IMGfile CIS compimg HP Bitmapfile +0 short 0x8000 lif file +0 long 0x020c010c compiled Lisp + +0 string msgcat01 HP NLS message catalog, +>8 long >0 %d messages diff --git a/usr.bin/file/magdir/ibm370 b/usr.bin/file/magdir/ibm370 new file mode 100644 index 0000000000..b40fbf4208 --- /dev/null +++ b/usr.bin/file/magdir/ibm370 @@ -0,0 +1,19 @@ +# +# IBM 370 and compatibles. +# +# "ibm370" said that 0x15d == 0535 was "ibm 370 pure executable". +# What the heck *is* "USS/370"? +# +0 beshort 0531 SVR2 executable (Amdahl-UTS) +>12 belong >0 not stripped +>24 belong >0 - version %ld +0 beshort 0534 SVR2 pure executable (Amdahl-UTS) +>12 belong >0 not stripped +>24 belong >0 - version %ld +0 beshort 0530 SVR2 pure executable (USS/370) +>12 belong >0 not stripped +>24 belong >0 - version %ld +0 beshort 0535 SVR2 executable (USS/370) +>12 belong >0 not stripped +>24 belong >0 - version %ld + diff --git a/usr.bin/file/magdir/ibm6000 b/usr.bin/file/magdir/ibm6000 new file mode 100644 index 0000000000..e4d73df4ec --- /dev/null +++ b/usr.bin/file/magdir/ibm6000 @@ -0,0 +1,17 @@ +# +# magic.rs6000: +# +# RS/6000 and the RT PC. +# +0 beshort 0x01df executable (RISC System/6000 V3.1) or obj module +>12 belong >0 not stripped +# Breaks sun4 statically linked execs. +#0 beshort 0x0103 executable (RT Version 2) or obj module +#>2 byte 0x50 pure +#>28 belong >0 not stripped +#>6 beshort >0 - version %ld +0 beshort 0x0104 shared library +0 beshort 0x0105 ctab data +0 beshort 0xfe04 structured file +0 string 0xabcdef message catalog +#0 string <aiaff> archive diff --git a/usr.bin/file/magdir/iff b/usr.bin/file/magdir/iff new file mode 100644 index 0000000000..2d2f95bba0 --- /dev/null +++ b/usr.bin/file/magdir/iff @@ -0,0 +1,5 @@ +# image file format +# From Robert Potter, potter@cs.rochester.edu +0 string Imagefile\ version- iff image data +# this adds the whole header (inc. version number), informative but longish +>10 string >\0 %s diff --git a/usr.bin/file/magdir/images b/usr.bin/file/magdir/images new file mode 100644 index 0000000000..32c19b6dd9 --- /dev/null +++ b/usr.bin/file/magdir/images @@ -0,0 +1,56 @@ +# image formats, originally from jef@helios.ee.lbl.gov (Jef Poskanzer), +# additions by janl@ifi.uio.no as well as others. Jan also suggested +# merging several one- and two-line files into here. +# +# XXX - byte order for GIF and TIFF fields? +# + +0 string xbtoa btoa'd file + +# PBMPLUS +0 string P1 PBM file +0 string P2 PGM file +0 string P3 PPM file +0 string P4 PBM "rawbits" file +0 string P5 PGM "rawbits" file +0 string P6 PPM "rawbits" file + +# TIFF and friends +0 string \115\115 TIFF file, big-endian +>2 short >0 version %d +0 string \111\111 TIFF file, little-endian +>2 short >0 version %d +# +# NIFF (Navy Interchange File Format, a modification of TIFF) +0 string IIN1 NIFF raster data + +# GIF +0 string GIF GIF picture +>3 string 87a - version 87a +>3 string 89a - version 89a +>6 leshort x %d x +>8 leshort x %d, +>10 byte &0x40 interlaced, +>10 byte&0x07 =0x00 2 colors +>10 byte&0x07 =0x01 4 colors +>10 byte&0x07 =0x02 8 colors +>10 byte&0x07 =0x03 16 colors +>10 byte&0x07 =0x04 32 colors +>10 byte&0x07 =0x05 64 colors +>10 byte&0x07 =0x06 128 colors +>10 byte&0x07 =0x07 256 colors + +# Miscellany +0 long 1123028772 Artisan image file +>4 long 1 rectangular 24-bit image +>4 long 2 rectangular 8-bit image with colormap +>4 long 3 rectangular 32-bit image (24-bit with matte) +0 string \361\0\100\273 CMU window manager bitmap +0 string #FIG FIG graphics savefile text +>6 string 2.1 Version 2.1 +>6 string 2.0 Version 2.0 +0 string GKSM GKS Metafile +8 string ILBM IFF ILBM file +0 string yz MGR bitmap +6 string JFIF JPEG picture +0 string ARF_BEGARF PHIGS clear text archive diff --git a/usr.bin/file/magdir/intel b/usr.bin/file/magdir/intel index 41963b5ca9..3f7ade7962 100644 --- a/usr.bin/file/magdir/intel +++ b/usr.bin/file/magdir/intel @@ -1,20 +1,31 @@ -# various intel-CPU magic numbers -0 short 01006 80286 executable (STL) ->31 byte <0x040 small model ->31 byte =0x048 large model ->31 byte =0x049 huge model ->16 long >0 not stripped -0 string MZ DOS executable (EXE) -0 string LZ DOS executable (built-in) -0 byte 0xe9 DOS executable (COM) -0 byte 0xeb DOS executable (COM) -0 short =0512 80286 executable small model (COFF) ->12 long >0 not stripped ->22 short >0 - version %ld -0 short =0522 80286 executable large model (COFF) ->12 long >0 not stripped ->22 short >0 - version %ld -0 short =0514 80386 executable ->12 long >0 not stripped ->22 short >0 - version %ld - +# +# Various flavors of x86 UNIX executable/object (other than Xenix, which +# is in "microsoft"). DOS is in "ms-dos"; the ambitious soul can do +# Windows as well. +# +# Windows NT belongs elsewhere, as you need x86 and MIPS and Alpha and +# whatever comes next (HP-PA Hummingbird?). OS/2 may also go elsewhere +# as well, if, as, and when IBM makes it portable. +# +# The `versions' should be un-commented if they work for you. +# (Was the problem just one of endianness?) +# +0 leshort 0502 basic-16 executable +>12 lelong >0 not stripped +#>22 leshort >0 - version %ld +0 leshort 0503 basic-16 executable (TV) +>12 lelong >0 not stripped +#>22 leshort >0 - version %ld +0 leshort 0510 x86 executable +>12 lelong >0 not stripped +0 leshort 0511 x86 executable (TV) +>12 lelong >0 not stripped +0 leshort =0512 iAPX 286 executable small model (COFF) +>12 lelong >0 not stripped +#>22 leshort >0 - version %ld +0 leshort =0522 iAPX 286 executable large model (COFF) +>12 lelong >0 not stripped +#>22 leshort >0 - version %ld +0 leshort =0514 80386 COFF executable +>12 lelong >0 not stripped +>22 leshort >0 - version %ld diff --git a/usr.bin/file/magdir/interleaf b/usr.bin/file/magdir/interleaf new file mode 100644 index 0000000000..ab5f3c654f --- /dev/null +++ b/usr.bin/file/magdir/interleaf @@ -0,0 +1,7 @@ +# +# magic for InterLeaf TPS: +0 string =\210OPS Interleaf saved data +0 string =<!OPS Interleaf document text +>5 string ,\ Version\ (version +>>14 string >\0 %s) + diff --git a/usr.bin/file/magdir/iris b/usr.bin/file/magdir/iris new file mode 100644 index 0000000000..952a5f1978 --- /dev/null +++ b/usr.bin/file/magdir/iris @@ -0,0 +1,57 @@ +# +# magic.iris: Magic for mips from an iris4d +# +# Dunno what byte-order munging is needed; all of SGI's *current* +# machines and OSes run in big-endian mode on the MIPS machines, +# as far as I know, but they do have the MIPSEB and MIPSEL stuff +# here.... +# +0 short 0x0160 mipseb +>20 short 0407 executable +>20 short 0410 pure +>20 short 0413 demand paged +>8 long >0 not stripped +>8 long 0 stripped +>22 byte >0 - version %ld. +>23 byte >0 %ld +0 short 0x0162 mipsel +>20 short 0407 executable +>20 short 0410 pure +>20 short 0413 demand paged +>8 long >0 not stripped +>8 long 0 stripped +>23 byte >0 - version %ld. +>22 byte >0 %ld +0 short 0x6001 swapped mipseb +>20 short 03401 executable +>20 short 04001 pure +>20 short 05401 demand paged +>8 long >0 not stripped +>8 long 0 stripped +>22 byte >0 - version %ld. +>23 byte >0 %ld +0 short 0x6201 swapped mipsel +>20 short 03401 executable +>20 short 04001 pure +>20 short 05401 demand paged +>8 long >0 not stripped +>8 long 0 stripped +>22 byte >0 - version %ld. +>23 byte >0 %ld +0 short 0x180 mipseb ucode +0 short 0x182 mipsel ucode +# +# IRIX core format version 1 (from /usr/include/core.out.h) +0 long 0xdeadadb0 IRIX core dump +>4 long 1 of +>16 string >\0 '%s' +# +# Archives - This handles archive subtypes +# +0 string !<arch>\n__________E MIPS archive +>20 string U with mipsucode members +>21 string L with mipsel members +>21 string B with mipseb members +>19 string L and a EL hash table +>19 string B and a EB hash table +>22 string X -- out of date diff --git a/usr.bin/file/magdir/ispell b/usr.bin/file/magdir/ispell new file mode 100644 index 0000000000..04814b8f19 --- /dev/null +++ b/usr.bin/file/magdir/ispell @@ -0,0 +1,23 @@ +# +# magic.ispell +# +# XXX - byte order? +# +0 short 0xffff9601 ispell hash file +>2 short 0x00 - 8-bit, no capitalization, 26 flags +>2 short 0x01 - 7-bit, no capitalization, 26 flags +>2 short 0x02 - 8-bit, capitalization, 26 flags +>2 short 0x03 - 7-bit, capitalization, 26 flags +>2 short 0x04 - 8-bit, no capitalization, 52 flags +>2 short 0x05 - 7-bit, no capitalization, 52 flags +>2 short 0x06 - 8-bit, capitalization, 52 flags +>2 short 0x07 - 7-bit, capitalization, 52 flags +>2 short 0x08 - 8-bit, no capitalization, 128 flags +>2 short 0x09 - 7-bit, no capitalization, 128 flags +>2 short 0x0A - 8-bit, capitalization, 128 flags +>2 short 0x0B - 7-bit, capitalization, 128 flags +>2 short 0x0C - 8-bit, no capitalization, 256 flags +>2 short 0x0D - 7-bit, no capitalization, 256 flags +>2 short 0x0E - 8-bit, capitalization, 256 flags +>2 short 0x0F - 7-bit, capitalization, 256 flags +>4 short >0 and %d string characters diff --git a/usr.bin/file/magdir/lex b/usr.bin/file/magdir/lex new file mode 100644 index 0000000000..929e68dd67 --- /dev/null +++ b/usr.bin/file/magdir/lex @@ -0,0 +1,3 @@ +# derived empirically, your offsets may vary! +53 string yyprevious c program text (from lex) +>3 string >\0 for %s diff --git a/usr.bin/file/magdir/lif b/usr.bin/file/magdir/lif new file mode 100644 index 0000000000..9ac80b9c1b --- /dev/null +++ b/usr.bin/file/magdir/lif @@ -0,0 +1,6 @@ +# +# magic.lif: +# +# XXX - byte order? +# +0 short 0x8000 lif file diff --git a/usr.bin/file/magdir/linux b/usr.bin/file/magdir/linux new file mode 100644 index 0000000000..7467fc425c --- /dev/null +++ b/usr.bin/file/magdir/linux @@ -0,0 +1,9 @@ +# Values for Linux/i386 binaries, From: Rik Faith <faith@cs.unc.edu> +2 short 100 Linux/i386 +>0 short 0407 executable +>0 short 0410 pure executable +>0 short 0413 demand paged executable +>16 long >0 not stripped +>0 string Jump jump +# core dump file +216 long 0421 core file (Linux) diff --git a/usr.bin/file/magdir/magic b/usr.bin/file/magdir/magic index 882b24033a..20ee340a03 100644 --- a/usr.bin/file/magdir/magic +++ b/usr.bin/file/magdir/magic @@ -1 +1 @@ -0 string #magic magic text file for file(1) cmd +0 string #\ Magic magic text file for file(1) cmd diff --git a/usr.bin/file/magdir/microsoft b/usr.bin/file/magdir/microsoft new file mode 100644 index 0000000000..72bb0df4af --- /dev/null +++ b/usr.bin/file/magdir/microsoft @@ -0,0 +1,68 @@ +# +# Microsoft (Xenix, not DOS) +# +# "Middle model" stuff, and "Xenix 8086 relocatable or 80286 small +# model" lifted from "magic.xenix", with comment "derived empirically; +# treat as folklore until proven" +# +# "small model", "large model", "huge model" stuff lifted from XXX +# +# XXX - "x.out" collides with PDP-11 archives.... +# +0 string core core file (Xenix) +0 byte 0x80 8086 relocatable (Microsoft) +0 leshort 0xff65 x.out +>2 string __.SYMDEF randomized +>0 byte x archive +0 leshort 0x206 Microsoft a.out +>8 leshort 1 Middle model +>0x1e leshort &0x10 overlay +>0x1e leshort &0x2 separate +>0x1e leshort &0x4 pure +>0x1e leshort &0x800 segmented +>0x1e leshort &0x400 standalone +>0x1e leshort &0x8 fixed-stack +>0x1c byte &0x80 byte-swapped +>0x1c byte &0x40 word-swapped +>0x10 lelong >0 not-stripped +>0x1e leshort ^0xc000 pre-SysV +>0x1c byte &0x4 86 +>0x1c byte &0x9 286 +>0x1c byte &0xa 386 +>0x1f byte <0x040 small model +>0x1f byte =0x048 large model +>0x1f byte =0x049 huge model +>0x1e leshort &0x1 executable +>0x1e leshort ^0x1 object file +>0x1e leshort &0x40 Large Text +>0x1e leshort &0x20 Large Data +>0x1e leshort &0x120 Huge Objects Enabled +>0x10 lelong >0 not stripped + +0 leshort 0x140 old Microsoft 8086 x.out +>0x3 byte &0x4 separate +>0x3 byte &0x2 pure +>0 byte &0x1 executable +>0 byte ^0x1 relocatable +>0x14 lelong >0 not stripped + +0 lelong 0x206 b.out +>0x1e leshort &0x10 overlay +>0x1e leshort &0x2 separate +>0x1e leshort &0x4 pure +>0x1e leshort &0x800 segmented +>0x1e leshort &0x400 standalone +>0x1e leshort &0x1 executable +>0x1e leshort ^0x1 object file +>0x1e leshort &0x4000 V2.3 +>0x1e leshort &0x8000 V3.0 +>0x1c byte &0x4 86 +>0x1c byte &0xb 186 +>0x1c byte &0x9 286 +>0x1c byte &0x29 286 +>0x1c byte &0xa 386 +>0x1e leshort &0x4 Large Text +>0x1e leshort &0x2 Large Data +>0x1e leshort &0x102 Huge Objects Enabled + +0 leshort 0x580 XENIX 8086 relocatable or 80286 small model diff --git a/usr.bin/file/magdir/mips b/usr.bin/file/magdir/mips new file mode 100644 index 0000000000..ae17cbda2c --- /dev/null +++ b/usr.bin/file/magdir/mips @@ -0,0 +1,8 @@ +# +# RISC MIPS decstation +# Should this be "leshort", given that DEC ran the DECstations in +# little-endian mode? +# +# Where is the non-SGI, non-DEC MIPS stuff? +# +0 short 0x6201 MIPS executable diff --git a/usr.bin/file/magdir/mirage b/usr.bin/file/magdir/mirage index 28ee77861b..8a2dbcbe83 100644 --- a/usr.bin/file/magdir/mirage +++ b/usr.bin/file/magdir/mirage @@ -1 +1,4 @@ +# +# XXX - byte order? +# 0 long 31415 Mirage Assembler m.out executable diff --git a/usr.bin/file/magdir/misc b/usr.bin/file/magdir/misc deleted file mode 100644 index 4f83dd258d..0000000000 --- a/usr.bin/file/magdir/misc +++ /dev/null @@ -1 +0,0 @@ -0 string begin uuencoded mail text diff --git a/usr.bin/file/magdir/misc2 b/usr.bin/file/magdir/misc2 deleted file mode 100644 index 199f388d62..0000000000 --- a/usr.bin/file/magdir/misc2 +++ /dev/null @@ -1,2 +0,0 @@ -# derived empirically, your offsets may vary! -53 string yyprevious c program text (from lex) diff --git a/usr.bin/file/magdir/mkid b/usr.bin/file/magdir/mkid new file mode 100644 index 0000000000..cd2cf86442 --- /dev/null +++ b/usr.bin/file/magdir/mkid @@ -0,0 +1,7 @@ +# +# ID is the binary tags database produced by mkid(1). +# +# XXX - byte order? +# +0 string \311\304 ID tags data +>2 short >0 version %d diff --git a/usr.bin/file/magdir/mmdf b/usr.bin/file/magdir/mmdf new file mode 100644 index 0000000000..aa264c3761 --- /dev/null +++ b/usr.bin/file/magdir/mmdf @@ -0,0 +1 @@ +0 long 0x1010101 MMDF mailbox diff --git a/usr.bin/file/magdir/motorola b/usr.bin/file/magdir/motorola new file mode 100644 index 0000000000..b89279468f --- /dev/null +++ b/usr.bin/file/magdir/motorola @@ -0,0 +1,28 @@ +# +# Motorola +# +# 68K +# +0 beshort 0520 mc68k COFF +>18 beshort ^00000020 object +>18 beshort &00000020 executable +>12 belong >0 not stripped +>168 string .lowmem Apple toolbox +>20 beshort 0407 (impure) +>20 beshort 0410 (pure) +>20 beshort 0413 (demand paged) +>20 beshort 0421 (standalone) +0 beshort 0521 mc68k executable (shared) +>12 belong >0 not stripped +0 beshort 0522 mc68k executable (shared demand paged) +>12 belong >0 not stripped +# +# Motorola/UniSoft 68K Binary Compatibility Standard (BCS) +# +0 beshort 0554 68K BCS executable +# +# 88K +# +# Motorola/88Open BCS +# +0 beshort 0555 88K BCS executable diff --git a/usr.bin/file/magdir/ms-dos b/usr.bin/file/magdir/ms-dos new file mode 100644 index 0000000000..830493aef9 --- /dev/null +++ b/usr.bin/file/magdir/ms-dos @@ -0,0 +1,8 @@ +# +# Various MS-DOS magic numbers +# +0 string MZ DOS executable (EXE) +0 string LZ DOS executable (built-in) +0 byte 0xe9 DOS executable (COM) +0 byte 0xeb DOS executable (COM) +0 byte 0xf0 MS-DOS program library diff --git a/usr.bin/file/magdir/ncr b/usr.bin/file/magdir/ncr new file mode 100644 index 0000000000..89df75058d --- /dev/null +++ b/usr.bin/file/magdir/ncr @@ -0,0 +1,47 @@ +# +# magic.tower: +# +# NCR Tower objects, contributed by +# Michael R. Wayne *** TMC & Associates *** INTERNET: wayne@ford-vax.arpa +# uucp: {philabs | pyramid} !fmsrl7!wayne OR wayne@fmsrl7.UUCP +# +0 beshort 000610 Tower/XP rel 2 object +>12 belong >0 not stripped +>20 beshort 0407 executable +>20 beshort 0410 pure executable +>22 beshort >0 - version %ld +0 beshort 000615 Tower/XP rel 2 object +>12 belong >0 not stripped +>20 beshort 0407 executable +>20 beshort 0410 pure executable +>22 beshort >0 - version %ld +0 beshort 000620 Tower/XP rel 3 object +>12 belong >0 not stripped +>20 beshort 0407 executable +>20 beshort 0410 pure executable +>22 beshort >0 - version %ld +0 beshort 000625 Tower/XP rel 3 object +>12 belong >0 not stripped +>20 beshort 0407 executable +>20 beshort 0410 pure executable +>22 beshort >0 - version %ld +0 beshort 000630 Tower32/600/400 68020 object +>12 belong >0 not stripped +>20 beshort 0407 executable +>20 beshort 0410 pure executable +>22 beshort >0 - version %ld +0 beshort 000640 Tower32/800 68020 +>18 beshort &020000 w/68881 object +>18 beshort &040000 compatible object +>18 beshort &~060000 object +>20 beshort 0407 executable +>20 beshort 0413 pure executable +>12 belong >0 not stripped +>22 beshort >0 - version %ld +0 beshort 000645 Tower32/800 68010 +>18 beshort &040000 compatible object +>18 beshort &~060000 object +>20 beshort 0407 executable +>20 beshort 0413 pure executable +>12 belong >0 not stripped +>22 beshort >0 - version %ld diff --git a/usr.bin/file/magdir/netbsd b/usr.bin/file/magdir/netbsd new file mode 100644 index 0000000000..d7aafc603d --- /dev/null +++ b/usr.bin/file/magdir/netbsd @@ -0,0 +1,18 @@ +# +# All new-style magic numbers are in network byte order. +# +0 belong&077777777 041400413 netbsd/i386 demand paged +>0 byte &0x80 +>>20 long <4096 shared library +>>20 long =4096 dynamically linked executable +>>20 long >4096 dynamically linked executable +>0 byte ^0x80 executable +>16 long >0 not stripped +0 belong&077777777 041400410 netbsd/i386 pure +>0 byte &0x80 dynamically linked executable +>0 byte ^0x80 executable +>16 long >0 not stripped +0 belong&077777777 041400407 netbsd/i386 +>0 byte &0x80 dynamically linked executable +>0 byte ^0x80 executable +>16 long >0 not stripped diff --git a/usr.bin/file/magdir/news b/usr.bin/file/magdir/news new file mode 100644 index 0000000000..df7b6599c5 --- /dev/null +++ b/usr.bin/file/magdir/news @@ -0,0 +1,5 @@ +# NeWS, not "news" as in "netnews" +0 string StartFontMetrics ASCII font metrics +0 string StartFont ASCII font bits +0 long 0x137A2944 NeWS bitmap font +0 long 0x137A2947 NeWS font family diff --git a/usr.bin/file/magdir/olda.out b/usr.bin/file/magdir/olda.out deleted file mode 100644 index 6b894d1db3..0000000000 --- a/usr.bin/file/magdir/olda.out +++ /dev/null @@ -1,23 +0,0 @@ -0 long 0407 executable ->16 long >0 not stripped -#>2 short >0 - version %ld -0 short 0407 PDP-11 executable ->8 short >0 not stripped -0 short 0401 unix-rt ldp -0 short 0405 old overlay -0 long 0410 pure executable ->16 long >0 not stripped -#>2 short >0 - version %ld -0 short 0410 PDP-11 pure executable ->8 short >0 not stripped -#>2 short >0 - version %ld -0 short 0411 PDP-11 separate I&D executable ->8 short >0 not stripped -#>2 short >0 - version %ld -0 long 0413 demand paged pure executable ->16 long >0 not stripped -#>2 short >0 - version %ld -0 long 0420 demand paged (first page unmapped) pure executable ->16 long >0 not stripped -#>2 short >0 - version %ld -0 short 0437 pdp11 kernel overlay diff --git a/usr.bin/file/magdir/pbm b/usr.bin/file/magdir/pbm new file mode 100644 index 0000000000..bed702e660 --- /dev/null +++ b/usr.bin/file/magdir/pbm @@ -0,0 +1,4 @@ +# +# XXX - byte order? +# +0 short 0x2a17 "compact bitmap" format (Poskanzer) diff --git a/usr.bin/file/magdir/pdp b/usr.bin/file/magdir/pdp new file mode 100644 index 0000000000..cf1129dbc3 --- /dev/null +++ b/usr.bin/file/magdir/pdp @@ -0,0 +1,22 @@ +# +# magic.pdp: PDP-11 executable/object and APL workspace +# +0 lelong 0101555 PDP-11 single precision APL workspace +0 lelong 0101554 PDP-11 double precision APL workspace + +# +# PDP-11 a.out +# +0 leshort 0407 PDP-11 executable +>8 leshort >0 not stripped + +0 leshort 0401 PDP-11 UNIX/RT ldp +0 leshort 0405 PDP-11 old overlay + +0 leshort 0410 PDP-11 pure executable +>8 leshort >0 not stripped + +0 leshort 0411 PDP-11 separate I&D executable +>8 leshort >0 not stripped + +0 leshort 0437 PDP-11 kernel overlay diff --git a/usr.bin/file/magdir/pgp b/usr.bin/file/magdir/pgp new file mode 100644 index 0000000000..7d01ed7070 --- /dev/null +++ b/usr.bin/file/magdir/pgp @@ -0,0 +1,9 @@ +# +# pgp (pretty good protection) +0 short 0x9900 pgp key public ring +0 short 0x9501 pgp key security ring +0 string -----BEGIN\040PGP pgp armored data +>15 string PUBLIC\040KEY\040BLOCK- public key blocK +>15 string MESSAGE- message +>15 string SIGNED\040MESSAGE- signed message +>15 string PGP\040SIGNATURE- signature diff --git a/usr.bin/file/magdir/pkgadd b/usr.bin/file/magdir/pkgadd new file mode 100644 index 0000000000..2cdb5e57f3 --- /dev/null +++ b/usr.bin/file/magdir/pkgadd @@ -0,0 +1,4 @@ +# +# SysV R4 PKG Datastreams: +# +0 string #\ PaCkAgE\ DaTaStReAm pkg Datastream (SVR4) diff --git a/usr.bin/file/magdir/plus5 b/usr.bin/file/magdir/plus5 new file mode 100644 index 0000000000..a5fa4e7dcc --- /dev/null +++ b/usr.bin/file/magdir/plus5 @@ -0,0 +1,16 @@ +# +#/etc/magic entries for Plus Five's UNIX MUMPS +# +# XXX - byte order? Paging Hokey.... +# +0 short 0x259 mumps avl global +>2 byte >0 (V%d) +>6 byte >0 with %d byte name +>7 byte >0 and %d byte data cells +0 short 0x25a mumps blt global +>2 byte >0 (V%d) +>8 short >0 - %d byte blocks +>15 byte 0x00 - P/D format +>15 byte 0x01 - P/K/D format +>15 byte 0x02 - K/D format +>15 byte >0x02 - Bad Flags diff --git a/usr.bin/file/magdir/postscript b/usr.bin/file/magdir/postscript index c31a3593fb..6cc40a640a 100644 --- a/usr.bin/file/magdir/postscript +++ b/usr.bin/file/magdir/postscript @@ -1,5 +1,10 @@ # -# Let us not forget PostScript -0 string %! PostScript text +# magic.postscript: Magic for postscript files +# +# XXX - should we match only versions 1.0 and 2.0, or should we wildcard +# it? +# +0 string %! PostScript text >2 string PS-Adobe- conforming ->11 string 1.0 at level %s +>>11 string 1.0 at level %s +>>11 string 2.0 at level %s diff --git a/usr.bin/file/magdir/psdbms b/usr.bin/file/magdir/psdbms new file mode 100644 index 0000000000..2b29f41caf --- /dev/null +++ b/usr.bin/file/magdir/psdbms @@ -0,0 +1,6 @@ +# +# magic.ps: psdatabase magic +# +0 byte 0126 ps database +>1 string >\0 - version %s +>4 string >\0 from kernel %s diff --git a/usr.bin/file/magdir/pyramid b/usr.bin/file/magdir/pyramid new file mode 100644 index 0000000000..b9c9e56cc8 --- /dev/null +++ b/usr.bin/file/magdir/pyramid @@ -0,0 +1,10 @@ +# +# magic.pyramid: Magic for pyramids +# +# XXX - byte order? +# +0 long 0x50900107 Pyramid 90x family executable +0 long 0x50900108 Pyramid 90x family pure executable +>16 long >0 not stripped +0 long 0x5090010b Pyramid 90x family demand paged pure executable +>16 long >0 not stripped diff --git a/usr.bin/file/magdir/rasterfile b/usr.bin/file/magdir/rasterfile deleted file mode 100644 index 10c297b1c1..0000000000 --- a/usr.bin/file/magdir/rasterfile +++ /dev/null @@ -1,8 +0,0 @@ -# Sun rasterfiles -0 string \x59\xa6\x6a\x95 rasterfile ->4 long >0 %d ->8 long >0 x %d ->12 long >0 x %d ->20 long 0 old format ->20 long 2 compressed ->24 long 1 with color map diff --git a/usr.bin/file/magdir/sc b/usr.bin/file/magdir/sc new file mode 100644 index 0000000000..9bc91998e1 --- /dev/null +++ b/usr.bin/file/magdir/sc @@ -0,0 +1,2 @@ +# for SC +38 string Spreadsheet sc file diff --git a/usr.bin/file/magdir/sendmail b/usr.bin/file/magdir/sendmail new file mode 100644 index 0000000000..7eea31d217 --- /dev/null +++ b/usr.bin/file/magdir/sendmail @@ -0,0 +1,9 @@ +# +# magic.sendmail: +# +# XXX - byte order? +# +0 byte 046 Sendmail frozen configuration +>16 string >\0 - version %s +0 short 0x271c Sendmail frozen configuration +>16 string >\0 - version %s diff --git a/usr.bin/file/magdir/sequent b/usr.bin/file/magdir/sequent index c9567eebd0..24e317b034 100644 --- a/usr.bin/file/magdir/sequent +++ b/usr.bin/file/magdir/sequent @@ -1,9 +1,10 @@ +# Sequent information updated by Don Dwiggins <atsun!dwiggins>. # For Sequent's multiprocessor systems (incomplete). -0 long 000352 BALANCE NS32000 .o -0 long 010352 BALANCE NS32000 executable (0 @ 0) ->16 long >0 not stripped -0 long 020352 BALANCE NS32000 executable (invalid @ 0) ->16 long >0 not stripped -0 long 030352 BALANCE NS32000 standalone executable ->16 long >0 not stripped +0 leshort 0x00ea BALANCE NS32000 .o +0 leshort 0x10ea BALANCE NS32000 executable (0 @ 0) +>16 lelong >0 not stripped +0 leshort 0x20ea BALANCE NS32000 executable (invalid @ 0) +>16 lelong >0 not stripped +0 leshort 0x30ea BALANCE NS32000 standalone executable +>16 lelong >0 not stripped # Also need info on Sequent "Symmetry" series... diff --git a/usr.bin/file/magdir/sgml b/usr.bin/file/magdir/sgml new file mode 100644 index 0000000000..9716e36731 --- /dev/null +++ b/usr.bin/file/magdir/sgml @@ -0,0 +1,6 @@ +# sgml,v 1.1 1993/06/10 00:59:29 jtc Exp +# SGML goop, mostly from rph@sq. +0 string \<!DOCTYPE Exported SGML document +0 string \<!doctype Exported SGML document +0 string \<!SUBDOC Exported SGML subdocument +0 string \<!subdoc Exported SGML subdocument diff --git a/usr.bin/file/magdir/softquad b/usr.bin/file/magdir/softquad index 4a072c77ac..72d72ea3d9 100644 --- a/usr.bin/file/magdir/softquad +++ b/usr.bin/file/magdir/softquad @@ -1,4 +1,27 @@ -# SoftQuad troff magic numbers -# SoftQuad @(#)magic 1.2 86/09/15 +# SoftQuad Publishing Software magic numbers +# softquad,v 1.2 1993/06/10 00:59:31 jtc Exp +# Author/Editor and RulesBuilder +# +# XXX - byte order? +# +0 string \<!SQ\ DTD> Compiled SGML rules file +>9 string >\0 Type %s +0 string \<!SQ\ A/E> A/E SGML Document binary +>9 string >\0 Type %s +0 string \<!SQ\ STS> A/E SGML binary styles file +>9 string >\0 Type %s +0 short 0xc0de Compiled PSI (v1) data +0 short 0xc0da Compiled PSI (v2) data +>3 string >\0 (%s) +# Binary sqtroff font/desc files... 0 short 0125252 SoftQuad DESC or font file binary >2 short >0 - version %d +# Bitmaps... +0 string SQ\ BITMAP1 SoftQuad Raster Format text +#0 string SQ\ BITMAP2 SoftQuad Raster Format data +# sqtroff intermediate language (replacement for ditroff int. lang.) +0 string X\ SoftQuad troff Context intermediate +>2 string 495 for AT&T 495 laser printer +>2 string hp for Hewlett-Packard LaserJet +>2 string impr for IMAGEN imPRESS +>2 string ps for PostScript diff --git a/usr.bin/file/magdir/sun b/usr.bin/file/magdir/sun index 53e6cdd10a..152b891af5 100644 --- a/usr.bin/file/magdir/sun +++ b/usr.bin/file/magdir/sun @@ -1,21 +1,70 @@ -# Values for Sun MC680x0 binaries -0 short 2 mc68020 ->2 short 0407 executable ->2 short 0410 pure executable ->2 short 0413 demand paged executable ->16 long >0 not stripped -0 short 1 mc68010 ->2 short 0407 executable ->2 short 0410 pure executable ->2 short 0413 demand paged executable ->16 long >0 not stripped -0 short 0 old sun-2 ->2 short 0407 executable ->2 short 0410 pure executable ->2 short 0413 demand paged executable ->16 long >0 not stripped -0 long 0x080456 core file ->128 string >0 from '%s' # -0 short 05401 byte-swapped demand paged executable -0 short 010001 byte-swapped demand paged executable +# Values for big-endian Sun (MC680x0, SPARC) binaries on pre-5.x +# releases. +# (5.x uses ELF.) +# +0 belong&077777777 0600413 sparc demand paged +>0 byte &0x80 +>>20 belong <4096 shared library +>>20 belong =4096 dynamically linked executable +>>20 belong >4096 dynamically linked executable +>0 byte ^0x80 executable +>16 belong >0 not stripped +0 belong&077777777 0600410 sparc pure +>0 byte &0x80 dynamically linked executable +>0 byte ^0x80 executable +>16 belong >0 not stripped +0 belong&077777777 0600407 sparc +>0 byte &0x80 dynamically linked executable +>0 byte ^0x80 executable +>16 belong >0 not stripped + +0 belong&077777777 0400413 mc68020 demand paged +>0 byte &0x80 +>>20 belong <4096 shared library +>>20 belong =4096 dynamically linked executable +>>20 belong >4096 dynamically linked executable +>16 belong >0 not stripped +0 belong&077777777 0400410 mc68020 pure +>0 byte &0x80 dynamically linked executable +>0 byte ^0x80 executable +>16 belong >0 not stripped +0 belong&077777777 0400407 mc68020 +>0 byte &0x80 dynamically linked executable +>0 byte ^0x80 executable +>16 belong >0 not stripped + +0 belong&077777777 0200413 mc68010 demand paged +>0 byte &0x80 +>>20 belong <4096 shared library +>>20 belong =4096 dynamically linked executable +>>20 belong >4096 dynamically linked executable +>16 belong >0 not stripped +0 belong&077777777 0200410 mc68010 pure +>0 byte &0x80 dynamically linked executable +>0 byte ^0x80 executable +>16 belong >0 not stripped +0 belong&077777777 0200407 mc68010 +>0 byte &0x80 dynamically linked executable +>0 byte ^0x80 executable +>16 belong >0 not stripped + +# reworked these to avoid anything beginning with zero becoming "old sun-2" +0 belong 0407 old sun-2 executable +>16 belong >0 not stripped +0 belong 0410 old sun-2 pure executable +>16 belong >0 not stripped +0 belong 0413 old sun-2 demand paged executable +>16 belong >0 not stripped + +# +# Core files. "SPARC 4.x BCP" means "core file from a SunOS 4.x SPARC +# binary executed in compatibility mode under SunOS 5.x". +# +0 belong 0x080456 SunOS core file +>4 belong 432 (SPARC) +>>132 string >\0 from '%s' +>4 belong 826 (68K) +>>128 string >\0 from '%s' +>4 belong 456 (SPARC 4.x BCP) +>>152 string >\0 from '%s' diff --git a/usr.bin/file/magdir/sunraster b/usr.bin/file/magdir/sunraster new file mode 100644 index 0000000000..a7aca3e760 --- /dev/null +++ b/usr.bin/file/magdir/sunraster @@ -0,0 +1,12 @@ +# +# Sun rasterfiles +# +0 string \x59\xa6\x6a\x95 rasterfile +>4 belong x %ld x +>8 belong x %ld x +>12 belong x %ld +>20 belong 0 old format image +>20 belong 1 standard format image +>20 belong 2 run-length byte encoded image +>20 belong 0xffff experimental format image +>24 belong 1 with color map diff --git a/usr.bin/file/magdir/terminfo b/usr.bin/file/magdir/terminfo new file mode 100644 index 0000000000..76e171d84d --- /dev/null +++ b/usr.bin/file/magdir/terminfo @@ -0,0 +1,8 @@ +# +# Terminfo +# +# XXX - byte order for screen images? +# +0 string \032\001 Compiled terminfo entry +0 short 0433 Curses screen image +0 short 0434 Curses screen image diff --git a/usr.bin/file/magdir/tex b/usr.bin/file/magdir/tex new file mode 100644 index 0000000000..cdcd42a2f5 --- /dev/null +++ b/usr.bin/file/magdir/tex @@ -0,0 +1,20 @@ +# +# magic.tex: +# + +# Although we may know the offset of certain text fields in TeX DVI and +# font files, we cannot use them reliably because they may not be zero +# terminated. + +0 string \367\002 TeX DVI file +0 string \367\203 TeX generic font data +0 string \367\131 TeX packed font data +0 string \367\312 TeX virtual font data +0 string This\ is\ TeX, TeX transcript text +0 string This\ is\ METAFONT, METAFONT transcript text + +# Unfortunately, there is no way to detect TeX Font Metric (*.tfm) files +# without breaking them apart and reading the data. The following patterns +# match most *.tfm files generated by METAFONT or afm2tfm. +2 string \000\021 TeX font metric data +2 string \000\022 TeX font metric data diff --git a/usr.bin/file/magdir/tower b/usr.bin/file/magdir/tower deleted file mode 100644 index 891a7ed014..0000000000 --- a/usr.bin/file/magdir/tower +++ /dev/null @@ -1,44 +0,0 @@ -# NCR Tower objects, contributed by -# Michael R. Wayne *** TMC & Associates *** INTERNET: wayne@ford-vax.arpa -# uucp: {philabs | pyramid} !fmsrl7!wayne OR wayne@fmsrl7.UUCP -# -0 short 000610 Tower/XP rel 2 object ->12 long >0 not stripped ->20 short 0407 executable ->20 short 0410 pure executable ->22 short >0 -version %ld -0 short 000615 Tower/XP rel 2 object ->12 long >0 not stripped ->20 short 0407 executable ->20 short 0410 pure executable ->22 short >0 -version %ld -0 short 000620 Tower/XP rel 3 object ->12 long >0 not stripped ->20 short 0407 executable ->20 short 0410 pure executable ->22 short >0 -version %ld -0 short 000625 Tower/XP rel 3 object ->12 long >0 not stripped ->20 short 0407 executable ->20 short 0410 pure executable ->22 short >0 -version %ld -0 short 000630 Tower32/600/400 68020 object ->12 long >0 not stripped ->20 short 0407 executable ->20 short 0410 pure executable ->22 short >0 -version %ld -0 short 000640 Tower32/800 68020 ->18 short &020000 w/68881 object ->18 short &040000 compatible object ->18 short &~060000 object ->20 short 0407 executable ->20 short 0413 pure executable ->12 long >0 not stripped ->22 short >0 -version %ld -0 short 000645 Tower32/800 68010 ->18 short &040000 compatible object ->18 short &~060000 object ->20 short 0407 executable ->20 short 0413 pure executable ->12 long >0 not stripped ->22 short >0 -version %ld diff --git a/usr.bin/file/magdir/troff b/usr.bin/file/magdir/troff new file mode 100644 index 0000000000..d414c16493 --- /dev/null +++ b/usr.bin/file/magdir/troff @@ -0,0 +1,6 @@ +# +# magic.troff: +# +0 string \100\357 very old (C/A/T) troff output data +0 string ' [nt]roff, tbl, or eqn input text + diff --git a/usr.bin/file/magdir/typeset b/usr.bin/file/magdir/typeset index dbb0733498..506456350c 100644 --- a/usr.bin/file/magdir/typeset +++ b/usr.bin/file/magdir/typeset @@ -1,3 +1,5 @@ # other typesetting magic 0 string \100\357 very old (C/A/T) troff output data 0 string Interpress/Xerox Xerox InterPress data +>16 string / (version +>>17 string >\0 %s) diff --git a/usr.bin/file/magdir/unknown b/usr.bin/file/magdir/unknown new file mode 100644 index 0000000000..1ea5f066d6 --- /dev/null +++ b/usr.bin/file/magdir/unknown @@ -0,0 +1,35 @@ +# +# magic.unknown: Unknown machine magic +# +# XXX - this probably should be pruned, as it'll match PDP-11 and +# VAX image formats. +# +# 0x107 is 0407; 0x108 is 0410; both are PDP-11 (executable and pure, +# respectively). +# +# 0x109 is 0411; that's PDP-11 split I&D, but the PDP-11 version doesn't +# have the "version %ld", which may be a bogus COFFism (I don't think +# there ever was COFF for the PDP-11). +# +# 0x10B is 0413; that's VAX demand-paged, but this is a short, not a +# long, as it would be on a VAX. +# +# 0x10C is 0414, 0x10D is 0415, and 0x10E is 416; those *are* unknown. +# +0 short 0x107 unknown machine executable +>8 short >0 not stripped +>15 byte >0 - version %ld +0 short 0x108 unknown pure executable +>8 short >0 not stripped +>15 byte >0 - version %ld +0 short 0x109 PDP-11 separate I&D +>8 short >0 not stripped +>15 byte >0 - version %ld +0 short 0x10b unknown pure executable +>8 short >0 not stripped +>15 byte >0 - version %ld +0 long 0x10c unknown demand paged pure executable +>16 long >0 not stripped +0 long 0x10d unknown demand paged pure executable +>16 long >0 not stripped +0 long 0x10e unknown readable demand paged pure executable diff --git a/usr.bin/file/magdir/uuencode b/usr.bin/file/magdir/uuencode new file mode 100644 index 0000000000..add48a8c32 --- /dev/null +++ b/usr.bin/file/magdir/uuencode @@ -0,0 +1,3 @@ +0 string begin uuencoded mail text +# Btoa(1) is an alternative to uuencode that requires less space. +0 string xbtoa\ Begin btoa'd text diff --git a/usr.bin/file/magdir/varied.out b/usr.bin/file/magdir/varied.out index e66ad58071..3a997d1c57 100644 --- a/usr.bin/file/magdir/varied.out +++ b/usr.bin/file/magdir/varied.out @@ -1,26 +1,5 @@ # Herewith many of the object file formats used by USG systems. -# The `versions' should be un-commented if they work for you. -0 short 0570 SysV executable ->12 long >0 not stripped -#>22 short >0 - version %ld -0 short 0575 SysV pure executable ->12 long >0 not stripped -#>22 short >0 - version %ld -0 short 0502 basic-16 executable ->12 long >0 not stripped -0 short 0503 basic-16 executable (TV) ->12 long >0 not stripped -0 short 0510 x86 executable ->12 long >0 not stripped -0 short 0511 x86 executable (TV) ->12 long >0 not stripped -0 short 0550 3b20 executable ->12 long >0 not stripped -0 short 0551 3b20 executable (TV) ->12 long >0 not stripped -0 short 0560 WE32000 executable ->12 long >0 not stripped -0 short 0561 WE32000 executable (TV) ->12 long >0 not stripped +# Most have been moved to files for a particular processor, +# and deleted if they duplicate other entries. +# 0 short 0610 Perkin-Elmer executable - diff --git a/usr.bin/file/magdir/vax b/usr.bin/file/magdir/vax new file mode 100644 index 0000000000..f45080c319 --- /dev/null +++ b/usr.bin/file/magdir/vax @@ -0,0 +1,33 @@ +# +# magic.pdp: VAX executable/object and APL workspace +# +0 lelong 0101557 VAX single precision APL workspace +0 lelong 0101556 VAX double precision APL workspace + +# +# VAX a.out (32V, BSD) +# +0 lelong 0407 VAX executable +>16 lelong >0 not stripped + +0 lelong 0410 VAX pure executable +>16 lelong >0 not stripped + +0 lelong 0413 VAX demand paged pure executable +>16 lelong >0 not stripped + +0 lelong 0420 VAX demand paged (first page unmapped) pure executable +>16 lelong >0 not stripped + +# +# VAX COFF +# +# The `versions' should be un-commented if they work for you. +# (Was the problem just one of endianness?) +# +0 leshort 0570 VAX COFF executable +>12 lelong >0 not stripped +#>22 leshort >0 - version %ld +0 leshort 0575 VAX COFF pure executable +>12 lelong >0 not stripped +#>22 leshort >0 - version %ld diff --git a/usr.bin/file/magdir/vax.byteswap b/usr.bin/file/magdir/vax.byteswap deleted file mode 100644 index be51eceab7..0000000000 --- a/usr.bin/file/magdir/vax.byteswap +++ /dev/null @@ -1,17 +0,0 @@ -# Byte-swapped VAXen -# From: dupuy@amsterdam.columbia.edu (Alexander Dupuy) -# -# Here are a few lines you can add to /etc/magic on your sun workstations in -# order to recognize VAX executables and objects.... you could do something -# similar (in reverse) for your vaxen, but since 4.3+NFS' file(1) doesn't look -# for /etc/magic, I've never bothered. It really should be built in to file(1) -# so you would see the state of setuid/setgid/sticky bits. Or actually, there -# should be support for checking that sort of thing in /etc/magic. -# -0 long 00700200000 VAX executable ->16 long &0x7fffffff not stripped -0 long 01000200000 VAX pure executable ->16 long &0x7fffffff not stripped -0 long 01300200000 VAX demand-paged pure executable ->16 long &0x7fffffff not stripped -0 long 01100200000 PDP-11 executable diff --git a/usr.bin/file/magdir/visx b/usr.bin/file/magdir/visx new file mode 100644 index 0000000000..e95cc5ee6e --- /dev/null +++ b/usr.bin/file/magdir/visx @@ -0,0 +1,30 @@ +# +# magic.visx: Visx format file +# +0 short 0x5555 VISX image file +>2 byte 0 (zero) +>2 byte 1 (unsigned char) +>2 byte 2 (short integer) +>2 byte 3 (float 32) +>2 byte 4 (float 64) +>2 byte 5 (signed char) +>2 byte 6 (bit-plane) +>2 byte 7 (classes) +>2 byte 8 (statistics) +>2 byte 10 (ascii text) +>2 byte 15 (image segments) +>2 byte 100 (image set) +>2 byte 101 (unsigned char vector) +>2 byte 102 (short integer vector) +>2 byte 103 (float 32 vector) +>2 byte 104 (float 64 vector) +>2 byte 105 (signed char vector) +>2 byte 106 (bit plane vector) +>2 byte 121 (feature vector) +>2 byte 122 (feature vector library) +>2 byte 124 (chain code) +>2 byte 126 (bit vector) +>2 byte 130 (graph) +>2 byte 131 (adjacency graph) +>2 byte 132 (adjacency graph library) +>2 string .VISIX (ascii text) diff --git a/usr.bin/file/magdir/x11 b/usr.bin/file/magdir/x11 new file mode 100644 index 0000000000..ed0ff440e4 --- /dev/null +++ b/usr.bin/file/magdir/x11 @@ -0,0 +1,9 @@ +# +# magic.x11 +# +# I think this is byte-order-dependent; if so, it should become: +# +# 0 belong 00000004 X11 big-endian snf font +# 0 lelong 00000004 X11 little-endian snf font +# +0 long 00000004 X11 snf font diff --git a/usr.bin/file/magdir/xenix b/usr.bin/file/magdir/xenix deleted file mode 100644 index 104d5be50c..0000000000 --- a/usr.bin/file/magdir/xenix +++ /dev/null @@ -1,5 +0,0 @@ -# XENIX executable formats: derived empirically; treat as folklore until proven0 short 01006 XENIX (x.out) executable ->8 short 1 Middle model ->16 short >0 not stripped -0 short 02600 XENIX 8086 relocatable or 80286 small model - diff --git a/usr.bin/file/magdir/zilog b/usr.bin/file/magdir/zilog new file mode 100644 index 0000000000..c7503cddd5 --- /dev/null +++ b/usr.bin/file/magdir/zilog @@ -0,0 +1,11 @@ +# +# Zilog Z8000. +# +# Was it big-endian or little-endian? My Product Specification doesn't +# say. +# +0 long 0xe807 object file (z8000 a.out) +0 long 0xe808 pure object file (z8000 a.out) +0 long 0xe809 separate object file (z8000 a.out) +0 long 0xe805 overlay object file (z8000 a.out) + diff --git a/usr.bin/file/magic b/usr.bin/file/magic deleted file mode 100644 index 5acb52a496..0000000000 --- a/usr.bin/file/magic +++ /dev/null @@ -1,313 +0,0 @@ -#! file -# Magic data for file(1) command. -# Machine-genererated from src/cmd/file/magdir/*; edit there only! -# Format is described in magic(files), where: -# files is 4 on V7 and BSD, 4 on SV, and ?? in the SVID. -# Add any locally-observed files here. Remember: -# text if readable, executable if runnable binary, data if unreadable. -22 short 023000 core dump data -0 byte 26 'arc' archive ->1 byte 0 (empty) ->1 byte 1 (old format) -0 short 070707 cpio archive -0 string 070707 ASCII cpio archive -0 long 0177555 very old archive -0 short 0177555 very old PDP-11 archive -0 long 0177545 old archive -0 short 0177545 old PDP-11 archive -0 long 0100554 apl workspace -0 string =<ar> archive -0 string !<arch> archive ->8 string __.SYMDEF random library -0 string -h- archive (Software Tools format) text -# this first will upset you if you're a PL/1 shop... -# in which case rm it; ascmagic will catch real C programs -0 string /* c program text -# check for various C program generators... -# offsets derived empirically, your offsets may vary! -# (this obviously belongs in ascmagic.c/names.h!). -53 string yyprevious c program text (from lex) -0 string #!\ /bin/sh commands text -0 string #!/bin/sh commands text -0 string #!\ /bin/csh C shell commands text -0 string #!/bin/csh C shell commands text -0 string #!\ /bin/awk awk commands text -0 string #!/bin/awk awk commands text -0 string #!\ / some kinda commands text -0 string #!/ some kinda commands text -0 string #!\ commands text ->3 string >\0 for %s -# An "antique" kernel is either unmodified early V7, -# without DMR's 1979 mod for #!, or any kernel -# derived from a pre-v7 kernel (i.e., System V) -0 string :\ shell archive or commands for antique kernel text -0 short 017037 packed data -# CPL - added pack to /etc/magic -0 short 017436 packed data -0 short 0145405 huf output - -0 string \037\235 compressed data -# non block compressed ->2 byte 12 - with 12 bits ->2 byte 13 - with 13 bits ->2 byte 14 - with 14 bits ->2 byte 15 - with 15 bits ->2 byte 16 - with 16 bits -# block compressed ->2 byte 140 - with 12 bits ->2 byte 141 - with 13 bits ->2 byte 142 - with 14 bits ->2 byte 143 - with 15 bits ->2 byte 144 - with 16 bits -0 long 0513 Convex executable -# -# magic file lines for output from "diff"... -0 string diff\ 'diff' output text -0 string ***\ 'diff' output text -0 string Only\ in\ 'diff' output text -0 string Common\ subdirectories:\ 'diff' output text -# Magic numbers for ditroff intermediate language -0 string x\ T\ cat titroff output for the C/A/T text -0 string x\ T\ ps titroff output for PostScript -0 string x\ T titroff output text -0 string FONT ASCII vfont text -0 short 0436 Berkeley vfont data -0 short 017001 byte-swapped Berkeley vfont data -# Magic number for FrameMaker files -# Thanks to Berry Kercheval -# -0 string \<MakerFile FrameMaker document -# Tell file about magic for IMAGEN printer-ready files: -0 string @document( Imagen printer -# this only works if "language xxx" is first item in Imagen header. ->10 string language\ impress (imPRESS data) ->10 string language\ daisy (daisywheel text) ->10 string language\ diablo (daisywheel text) ->10 string language\ printer (line printer emulation) ->10 string language\ tektronix (Tektronix 4014 emulation) -# Add any other languages that your Imagen uses - remember -# to keep the word `text' if the file is human-readable. -# -# Now magic for IMAGEN font files... -0 string Rast RST-format raster font data ->45 string >0 face % -# various intel-CPU magic numbers -0 short 01006 80286 executable (STL) ->31 byte <0x040 small model ->31 byte =0x048 large model ->31 byte =0x049 huge model ->16 long >0 not stripped -0 string MZ DOS executable (EXE) -0 string LZ DOS executable (built-in) -0 byte 0xe9 DOS executable (COM) -0 byte 0xeb DOS executable (COM) -0 short =0512 80286 executable small model (COFF) ->12 long >0 not stripped ->22 short >0 - version %ld -0 short =0522 80286 executable large model (COFF) ->12 long >0 not stripped ->22 short >0 - version %ld -0 short =0514 80386 executable ->12 long >0 not stripped ->22 short >0 - version %ld - -0 string #magic magic text file for file(1) cmd -# Unfortunately, saved netnews also has From line added in some news software. -#0 string From mail text -# There are tests to ascmagic.c to cope with mail and news. -0 string Relay-Version: old news text -0 string #!\ rnews batched news text -0 string N#!\ rnews mailed, batched news text -0 string Forward\ to mail forwarding text -0 string Pipe\ to mail piping text -0 string Return-Path: smtp mail text -0 string Path: news text -0 string Xref: news text -0 string From: news or mail text -0 string Article saved news text -0 long 31415 Mirage Assembler m.out executable -0 string begin uuencoded mail text -# derived empirically, your offsets may vary! -53 string yyprevious c program text (from lex) -0 long 0407 executable ->16 long >0 not stripped -#>2 short >0 - version %ld -0 short 0407 PDP-11 executable ->8 short >0 not stripped -0 short 0401 unix-rt ldp -0 short 0405 old overlay -0 long 0410 pure executable ->16 long >0 not stripped -#>2 short >0 - version %ld -0 short 0410 PDP-11 pure executable ->8 short >0 not stripped -#>2 short >0 - version %ld -0 short 0411 PDP-11 separate I&D executable ->8 short >0 not stripped -#>2 short >0 - version %ld -0 long 0413 demand paged pure executable ->16 long >0 not stripped -#>2 short >0 - version %ld -0 long 0420 demand paged (first page unmapped) pure executable ->16 long >0 not stripped -#>2 short >0 - version %ld -0 short 0437 pdp11 kernel overlay -# -# Let us not forget PostScript -0 string %! PostScript text ->2 string PS-Adobe- conforming ->11 string 1.0 at level %s -# Sun rasterfiles -0 string \x59\xa6\x6a\x95 rasterfile ->4 long >0 %d ->8 long >0 x %d ->12 long >0 x %d ->20 long 0 old format ->20 long 2 compressed ->24 long 1 with color map -# SCCS archive structure: -# \001h01207 -# \001s 00276/00000/00000 -# \001d D 1.1 87/09/23 08:09:20 ian 1 0 -# \001c date and time created 87/09/23 08:09:20 by ian -# \001e -# \001u -# \001U -# ... etc. -# Now '\001h' happens to be the same as the 3B20's a.out magic number (0550). -# *Sigh*. And these both came from various parts of the USG. -# Maybe we should just switch everybody from SCCS to RCS! -# Further, you can't just say '\001h0', because the five-digit number -# is a checksum that could (presumably) have any leading digit, -# and we don't have regular expression matching yet. -# Hence the following official kludge: -8 string \001s\ SCCS archive. -# For Sequent's multiprocessor systems (incomplete). -0 long 000352 BALANCE NS32000 .o -0 long 010352 BALANCE NS32000 executable (0 @ 0) ->16 long >0 not stripped -0 long 020352 BALANCE NS32000 executable (invalid @ 0) ->16 long >0 not stripped -0 long 030352 BALANCE NS32000 standalone executable ->16 long >0 not stripped -# Also need info on Sequent "Symmetry" series... -# SoftQuad troff magic numbers -# SoftQuad @(#)magic 1.2 86/09/15 -0 short 0125252 SoftQuad DESC or font file binary ->2 short >0 - version %d -# Values for Sun MC680x0 binaries -0 short 2 mc68020 ->2 short 0407 executable ->2 short 0410 pure executable ->2 short 0413 demand paged executable ->16 long >0 not stripped -0 short 1 mc68010 ->2 short 0407 executable ->2 short 0410 pure executable ->2 short 0413 demand paged executable ->16 long >0 not stripped -0 short 0 old sun-2 ->2 short 0407 executable ->2 short 0410 pure executable ->2 short 0413 demand paged executable ->16 long >0 not stripped -0 long 0x080456 core file ->128 string >0 from '%s' -# -0 short 05401 byte-swapped demand paged executable -0 short 010001 byte-swapped demand paged executable -# NCR Tower objects, contributed by -# Michael R. Wayne *** TMC & Associates *** INTERNET: wayne@ford-vax.arpa -# uucp: {philabs | pyramid} !fmsrl7!wayne OR wayne@fmsrl7.UUCP -# -0 short 000610 Tower/XP rel 2 object ->12 long >0 not stripped ->20 short 0407 executable ->20 short 0410 pure executable ->22 short >0 -version %ld -0 short 000615 Tower/XP rel 2 object ->12 long >0 not stripped ->20 short 0407 executable ->20 short 0410 pure executable ->22 short >0 -version %ld -0 short 000620 Tower/XP rel 3 object ->12 long >0 not stripped ->20 short 0407 executable ->20 short 0410 pure executable ->22 short >0 -version %ld -0 short 000625 Tower/XP rel 3 object ->12 long >0 not stripped ->20 short 0407 executable ->20 short 0410 pure executable ->22 short >0 -version %ld -0 short 000630 Tower32/600/400 68020 object ->12 long >0 not stripped ->20 short 0407 executable ->20 short 0410 pure executable ->22 short >0 -version %ld -0 short 000640 Tower32/800 68020 ->18 short &020000 w/68881 object ->18 short &040000 compatible object ->18 short &~060000 object ->20 short 0407 executable ->20 short 0413 pure executable ->12 long >0 not stripped ->22 short >0 -version %ld -0 short 000645 Tower32/800 68010 ->18 short &040000 compatible object ->18 short &~060000 object ->20 short 0407 executable ->20 short 0413 pure executable ->12 long >0 not stripped ->22 short >0 -version %ld -# other typesetting magic -0 string \100\357 very old (C/A/T) troff output data -0 string Interpress/Xerox Xerox InterPress data -# Herewith many of the object file formats used by USG systems. -# The `versions' should be un-commented if they work for you. -0 short 0570 SysV executable ->12 long >0 not stripped -#>22 short >0 - version %ld -0 short 0575 SysV pure executable ->12 long >0 not stripped -#>22 short >0 - version %ld -0 short 0502 basic-16 executable ->12 long >0 not stripped -0 short 0503 basic-16 executable (TV) ->12 long >0 not stripped -0 short 0510 x86 executable ->12 long >0 not stripped -0 short 0511 x86 executable (TV) ->12 long >0 not stripped -0 short 0550 3b20 executable ->12 long >0 not stripped -0 short 0551 3b20 executable (TV) ->12 long >0 not stripped -0 short 0560 WE32000 executable ->12 long >0 not stripped -0 short 0561 WE32000 executable (TV) ->12 long >0 not stripped -0 short 0610 Perkin-Elmer executable - -# Byte-swapped VAXen -# From: dupuy@amsterdam.columbia.edu (Alexander Dupuy) -# -# Here are a few lines you can add to /etc/magic on your sun workstations in -# order to recognize VAX executables and objects.... you could do something -# similar (in reverse) for your vaxen, but since 4.3+NFS' file(1) doesn't look -# for /etc/magic, I've never bothered. It really should be built in to file(1) -# so you would see the state of setuid/setgid/sticky bits. Or actually, there -# should be support for checking that sort of thing in /etc/magic. -# -0 long 00700200000 VAX executable ->16 long &0x7fffffff not stripped -0 long 01000200000 VAX pure executable ->16 long &0x7fffffff not stripped -0 long 01300200000 VAX demand-paged pure executable ->16 long &0x7fffffff not stripped -0 long 01100200000 PDP-11 executable -# XENIX executable formats: derived empirically; treat as folklore until proven0 short 01006 XENIX (x.out) executable ->8 short 1 Middle model ->16 short >0 not stripped -0 short 02600 XENIX 8086 relocatable or 80286 small model - diff --git a/usr.bin/file/magic.5 b/usr.bin/file/magic.5 index d736dcefb6..7479f2711a 100644 --- a/usr.bin/file/magic.5 +++ b/usr.bin/file/magic.5 @@ -1,13 +1,4 @@ -.\" -.\" PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -.\" -------------------- ----- ---------------------- -.\" CURRENT PATCH LEVEL: 1 00103 -.\" -------------------- ----- ---------------------- -.\" -.\" 16 Feb 93 Rodney W. Grimes Fixed all the commands that had two -.\" dots in them. -.\" -.TH MAGIC FILES "Public Domain" +.TH MAGIC 5 "Public Domain" .\" install as magic.4 on USG, magic.5 on V7 or Berkeley systems. .SH NAME magic \- file command's magic number file @@ -38,12 +29,33 @@ The type of the data to be tested. The possible values are: .IP byte \w'message'u+2n A one-byte value. .IP short -A two-byte value (on most systems). +A two-byte value (on most systems) in this machine's native byte order. .IP long -A four-byte value (on most systems). +A four-byte value (on most systems) in this machine's native byte order. .IP string A string of bytes. +.IP date +A four-byte value interpreted as a unix date. +.IP beshort +A two-byte value (on most systems) in big-endian byte order. +.IP belong +A four-byte value (on most systems) in big-endian byte order. +.IP bedate +A four-byte value (on most systems) in big-endian byte order, +interpreted as a unix date. +.IP leshort +A two-byte value (on most systems) in little-endian byte order. +.IP lelong +A four-byte value (on most systems) in little-endian byte order. +.IP ledate +A four-byte value (on most systems) in little-endian byte order, +interpreted as a unix date. .RE +The numeric types may optionally be followed by +.B & +and a numeric value, +to specify that the value is to be AND'ed with the +numeric value before any comparisons are done. .IP test The value to be compared with the value from the file. If the type is numeric, this value @@ -61,10 +73,14 @@ value, .BR > , to specify that the value from the file must be greater than the specified value, -or .BR & , -to specify that the value is to be AND'ed with the -numeric value before any comparisons are done. +to specify that the value from the file must have set all of the bits +that are set in the specified value, +or +.BR ^ , +to specify that the value from the file must have clear any of the bits +that are set in the specified value. +.IP Numeric values are specified in C form; e.g. .B 13 is decimal, @@ -78,9 +94,19 @@ is omitted, it is assumed to be .IP For string values, the byte string from the file must match the specified byte string. -The operators =, < and > (but not &) can be applied to strings. +The operators +.BR = , +.B < +and +.B > +(but not +.BR & ) +can be applied to strings. The length used for matching is that of the string argument -in the magic file. +in the magic file. This means that a line can match any string, and +then presumably print that string, by doing +.B >\e0 +(because all strings are greater than the null string). .IP message The message to be printed if the comparison succeeds. If the string contains a @@ -91,30 +117,61 @@ performed) is printed using the message as the format string. Some file formats contain additional information which is to be printed along with the file type. A line which begins with the character .B > -indicates additional tests and messages to be printed. If the test on the -line preceding the first line with a +indicates additional tests and messages to be printed. The number of .B > -succeeds, the tests specified in all the subsequent lines beginning with +on the line indicates the level of the test; a line with no .B > +at the beginning is considered to be at level 0. +Each line at level +.IB n \(pl1 +is under the control of the line at level +.IB n +most closely preceding it in the magic file. +If the test on a line at level +.I n +succeeds, the tests specified in all the subsequent lines at level +.IB n \(pl1 are performed, and the messages printed if the tests succeed. The next -line which does not begin with a -.B > +line at level +.I n terminates this. +If the first character following the last +.B > +is a +.B ( +then the string after the parenthesis is interpreted as an indirect offset. +That means that the number after the parenthesis is used as a offset in +the file. The value at that offset is read, and is used again as an offset +in the file. Indirect offsets are of the form: +.BI (( x [.[bsl]][+-][ y ]). +The value of +.I x +is used as an offset in the file. A byte, short or long is read at that offset +depending on the +.B [bsl] +type specifier. To that number the value of +.I y +is added and the result is used as an offset in the file. The default type +if one is not specified is long. .SH BUGS The formats -.I long +.IR long , +.IR belong , +.IR lelong , +.IR short , +.IR beshort , +.IR leshort , +.IR date , +.IR bedate , and -.I short +.I ledate are system-dependant; perhaps they should be specified as a number of bytes (2B, 4B, etc), since the files being recognized typically come from a system on which the lengths are invariant. .PP -There should be more than one level of subtests, -with the level possibly indicated by -the number of -.B > -at the beginning of the line. +There is (currently) no support for specified-endian data to be used in +indirect offsets. .SH SEE ALSO .IR file (1) \- the command that reads this file. @@ -131,4 +188,4 @@ at the beginning of the line. .\" the changes I posted to the S5R2 version. .\" .\" Modified for Ian Darwin's version of the file command. -.\" @(#)$Header: magic.4,v 1.5 87/11/06 20:54:31 ian Exp $ +.\" @(#)magic.5,v 1.3 1993/06/10 00:38:13 jtc Exp diff --git a/usr.bin/file/names.h b/usr.bin/file/names.h index cdc641c489..dfb4bff8cf 100644 --- a/usr.bin/file/names.h +++ b/usr.bin/file/names.h @@ -8,12 +8,9 @@ * Copyright (c) Ian F. Darwin, 1987. * Written by Ian F. Darwin. * - * This software is not subject to any license of the American Telephone - * and Telegraph Company or of the Regents of the University of California. + * See LEGAL.NOTICE * - * Permission is granted to anyone to use this software for any purpose on - * any computer system, and to alter it and redistribute it freely, subject - * to the terms in the accompanying LEGAL.NOTICE file. + * names.h,v 1.2 1993/06/10 00:38:14 jtc Exp */ /* these types are used to index the table 'types': keep em in sync! */ @@ -27,7 +24,7 @@ #define L_MAIL 7 /* Electronic mail */ #define L_NEWS 8 /* Usenet Netnews */ -char *types[] = { +static char *types[] = { "c program text", "fortran program text", "make commands text" , @@ -40,7 +37,7 @@ char *types[] = { "can't happen error on names.h/types", 0}; -struct names { +static struct names { char *name; short type; } names[] = { @@ -88,5 +85,6 @@ struct names { {"Newsgroups:", L_NEWS}, {"Path:", L_NEWS}, {"Organization:",L_NEWS}, - 0}; + {0} + }; #define NNAMES ((sizeof(names)/sizeof(struct names)) - 1) diff --git a/usr.bin/file/patchlevel.h b/usr.bin/file/patchlevel.h new file mode 100644 index 0000000000..985ec22ca6 --- /dev/null +++ b/usr.bin/file/patchlevel.h @@ -0,0 +1,34 @@ +#define FILE_VERSION_MAJOR 3 +#define patchlevel 9 + +/* + * Patchlevel file for Ian Darwin's MAGIC command. + * patchlevel.h,v 1.1 1993/06/10 00:38:15 jtc Exp + * + * patchlevel.h,v + * Revision 1.1 1993/06/10 00:38:15 jtc + * Updated to Ian Darwin's latest release. + * Adds lots of neat features from most other vendors file(1) commands, + * plus (important!) little and big endian file types. This will alow + * us to detect and extract meaningful information from a many more + * file types. + * + * Revision 1.9 93/03/24 14:23:40 ian + * Batch of minor changes from several contributors. + * + * Revision 1.8 93/02/19 15:01:26 ian + * Numerous changes from Guy Harris too numerous to mention but including + * byte-order independance, fixing "old-style masking", etc. etc. A bugfix + * for broken symlinks from martin@@d255s004.zfe.siemens.de. + * + * Revision 1.7 93/01/05 14:57:27 ian + * Couple of nits picked by Christos (again, thanks). + * + * Revision 1.6 93/01/05 13:51:09 ian + * Lotsa work on the Magic directory. + * + * Revision 1.5 92/09/14 14:54:51 ian + * Fix a tiny null-pointer bug in previous fix for tar archive + uncompress. + * + */ + diff --git a/usr.bin/file/print.c b/usr.bin/file/print.c index 2addbfac6a..9aeb4a8056 100644 --- a/usr.bin/file/print.c +++ b/usr.bin/file/print.c @@ -27,71 +27,144 @@ #include <stdio.h> #include <errno.h> +#include <string.h> +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif +#include <stdlib.h> +#include <unistd.h> #include "file.h" -#ifndef lint -static char *moduleid = - "@(#)$Header: print.c,v 1.11 88/01/15 12:17:06 ian Exp $"; -#endif /* lint */ - -#define MAXSTR 500 - -extern char *progname; -extern char *magicfile; -extern int debug, nmagic; /* number of valid magic[]s */ -extern void showstr(); +#ifndef lint +static char *moduleid = + "@(#)print.c,v 1.2 1993/06/10 00:38:17 jtc Exp"; +#endif /* lint */ +void mdump(m) struct magic *m; { - (void) printf("%d\t%d\t%d\t%c\t", - m->contflag, + static char *offs[] = { "absolute", "offset", + "indirect", "indirect-offset" }; + static char *typ[] = { "invalid", "byte", "short", "invalid", + "long", "string", "date", "beshort", + "belong", "bedate", "leshort", "lelong", + "ledate" }; + (void) fprintf(stderr, "[%s,%d,%s,%s%c,", + (m->flag >= 0 && m->flag < 4 ? offs[m->flag]: "*bad*"), m->offset, - m->type, - m->reln, - 0); + (m->type >= 0 && m->type < 13 ? + typ[(unsigned char) m->type] : "*bad*"), + m->reln & MASK ? "&" : "", + m->reln & ~MASK); + if (m->flag & INDIR) + (void) fprintf(stderr, "(%s,%d)", + (m->in.type >= 0 && + m->in.type < 6 ? typ[(unsigned char) m->in.type] : "*bad*"), + m->in.offset); + if (m->type == STRING) showstr(m->value.s); else - (void) printf("%d",m->value.l); - (void) printf("\t%s", m->desc); - (void) putchar('\n'); + (void) fprintf(stderr, "%d",m->value.l); + (void) fprintf(stderr, ",%s", m->desc); + (void) fputs("]\n", stderr); } /* - * error - print best error message possible and exit + * ckfputs - futs, but with error checking + * ckfprintf - fprintf, but with error checking */ -/*ARGSUSED1*/ +void +ckfputs(str, fil) + const char *str; + FILE *fil; +{ + if (fputs(str,fil) == EOF) + error("write failed.\n"); +} + /*VARARGS*/ void -error(s1, s2) -char *s1, *s2; +#if __STDC__ +ckfprintf(FILE *f, const char *fmt, ...) +#else +ckfprintf(va_alist) + va_dcl +#endif { - warning(s1, s2); - exit(1); + va_list va; +#if __STDC__ + va_start(va, fmt); +#else + FILE *f; + const char *fmt; + va_start(va); + f = va_arg(va, FILE *); + fmt = va_arg(va, const char *); +#endif + (void) vfprintf(f, fmt, va); + if (ferror(f)) + error("write failed.\n"); + va_end(va); } -/*ARGSUSED1*/ +/* + * error - print best error message possible and exit + */ /*VARARGS*/ -warning(f, a) -char *f, *a; +void +#if __STDC__ +error(const char *f, ...) +#else +error(va_alist) + va_dcl +#endif { - extern int errno, sys_nerr; - extern char *sys_errlist[]; - int myerrno; + va_list va; +#if __STDC__ + va_start(va, f); +#else + const char *f; + va_start(va); + f = va_arg(va, const char *); +#endif + /* cuz we use stdout for most, stderr here */ + (void) fflush(stdout); - myerrno = errno; + if (progname != NULL) + (void) fprintf(stderr, "%s: ", progname); + (void) vfprintf(stderr, f, va); + va_end(va); + exit(1); +} +/*VARARGS*/ +void +#if __STDC__ +magwarn(const char *f, ...) +#else +magwarn(va_alist) + va_dcl +#endif +{ + va_list va; +#if __STDC__ + va_start(va, f); +#else + const char *f; + va_start(va); + f = va_arg(va, const char *); +#endif /* cuz we use stdout for most, stderr here */ (void) fflush(stdout); - if (progname != NULL) { - (void) fputs(progname, stderr); - (void) putc(':', stderr); - (void) putc(' ', stderr); - } - (void) fprintf(stderr, f, a); - if (myerrno > 0 && myerrno < sys_nerr) - (void) fprintf(stderr, " (%s)", sys_errlist[myerrno]); - putc('\n', stderr); + if (progname != NULL) + (void) fprintf(stderr, "%s: %s, %d: ", + progname, magicfile, lineno); + (void) vfprintf(stderr, f, va); + va_end(va); + fputc('\n', stderr); } diff --git a/usr.bin/file/softmagic.c b/usr.bin/file/softmagic.c index b0c87245db..b4f6299670 100644 --- a/usr.bin/file/softmagic.c +++ b/usr.bin/file/softmagic.c @@ -26,29 +26,32 @@ */ #include <stdio.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> + #include "file.h" #ifndef lint static char *moduleid = - "@(#)$Header: softmagic.c,v 1.7 87/11/06 11:25:31 ian Exp $"; + "@(#)softmagic.c,v 1.2 1993/06/10 00:38:18 jtc Exp"; #endif /* lint */ -extern char *progname; -extern char *magicfile; /* name of current /etc/magic or clone */ -extern int debug, nmagic; -extern FILE *efopen(); -extern struct magic magic[]; -static int magindex; +static int match __P((unsigned char *)); +static int mcheck __P((unsigned char *, struct magic *)); +static void mprint __P((struct magic *, unsigned char *)); /* * softmagic - lookup one file in database * (already read from /etc/magic by apprentice.c). * Passed the name and FILE * of one file to be typed. */ -softmagic(buf) -char *buf; +/*ARGSUSED1*/ /* nbytes passed for regularity, maybe need later */ +int +softmagic(buf, nbytes) +unsigned char *buf; +int nbytes; { - magindex = 0; if (match(buf)) return 1; @@ -56,29 +59,100 @@ char *buf; } /* - * go through the whole list, stopping if you find a match. - * Be sure to process every continuation of this match. + * Go through the whole list, stopping if you find a match. Process all + * the continuations of that match before returning. + * + * We support multi-level continuations: + * + * At any time when processing a successful top-level match, there is a + * current continuation level; it represents the level of the last + * successfully matched continuation. + * + * Continuations above that level are skipped as, if we see one, it + * means that the continuation that controls them - i.e, the + * lower-level continuation preceding them - failed to match. + * + * Continuations below that level are processed as, if we see one, + * it means we've finished processing or skipping higher-level + * continuations under the control of a successful or unsuccessful + * lower-level continuation, and are now seeing the next lower-level + * continuation and should process it. The current continuation + * level reverts to the level of the one we're seeing. + * + * Continuations at the current level are processed as, if we see + * one, there's no lower-level continuation that may have failed. + * + * If a continuation matches, we bump the current continuation level + * so that higher-level continuations are processed. */ +static int match(s) -char *s; +unsigned char *s; { + int magindex = 0; + int cont_level = 0; + int need_separator = 0; + while (magindex < nmagic) { /* if main entry matches, print it... */ if (mcheck(s, &magic[magindex])) { mprint(&magic[magindex],s); + /* + * If we printed something, we'll need to print + * a blank before we print something else. + */ + if (magic[magindex].desc[0]) + need_separator = 1; /* and any continuations that match */ - while (magic[magindex+1].contflag && + cont_level++; + while (magic[magindex+1].cont_level != 0 && magindex < nmagic) { ++magindex; - if (mcheck(s, &magic[magindex])){ - (void) putchar(' '); - mprint(&magic[magindex],s); + if (cont_level >= + magic[magindex].cont_level) { + if (cont_level > + magic[magindex].cont_level) { + /* + * We're at the end of the + * level-"cont_level" + * continuations. + */ + cont_level = + magic[magindex].cont_level; + } + if (mcheck(s, &magic[magindex])) { + /* + * This continuation matched. + * Print its message, with + * a blank before it if + * the previous item printed + * and this item isn't empty. + */ + /* space if previous printed */ + if (need_separator + && (magic[magindex].nospflag == 0) + && (magic[magindex].desc[0] != '\0') + ) { + (void) putchar(' '); + need_separator = 0; + } + mprint(&magic[magindex],s); + if (magic[magindex].desc[0]) + need_separator = 1; + + /* + * If we see any continuations + * at a higher level, + * process them. + */ + cont_level++; + } } } return 1; /* all through */ } else { - /* main entry didn't match, flush its continuations */ - while (magic[magindex+1].contflag && + /* main entry didn't match, flush its continuation */ + while (magic[magindex+1].cont_level != 0 && magindex < nmagic) { ++magindex; } @@ -88,53 +162,101 @@ char *s; return 0; /* no match at all */ } - -mprint(m,s) +static void +mprint(m, s) struct magic *m; -char *s; +unsigned char *s; { register union VALUETYPE *p = (union VALUETYPE *)(s+m->offset); - char *pp, *strchr(); + char *pp, *rt; + /* correct byte order dependancies */ switch (m->type) { - case BYTE: - (void) printf(m->desc, p->b); + case BESHORT: + p->h = (short)((p->hs[0]<<8)|(p->hs[1])); break; - case SHORT: - (void) printf(m->desc, p->h); + case BELONG: + case BEDATE: + p->l = (long) + ((p->hl[0]<<24)|(p->hl[1]<<16)|(p->hl[2]<<8)|(p->hl[3])); break; - case LONG: - (void) printf(m->desc, p->l); + case LESHORT: + p->h = (short)((p->hs[1]<<8)|(p->hs[0])); break; - case STRING: - if ((pp=strchr(p->s, '\n')) != NULL) - *pp = '\0'; + case LELONG: + case LEDATE: + p->l = (long) + ((p->hl[3]<<24)|(p->hl[2]<<16)|(p->hl[1]<<8)|(p->hl[0])); + break; + } + + switch (m->type) { + case BYTE: + (void) printf(m->desc, + (m->reln & MASK) ? p->b & m->mask : p->b); + break; + case SHORT: + case BESHORT: + case LESHORT: + (void) printf(m->desc, + (m->reln & MASK) ? p->h & m->mask : p->h); + break; + case LONG: + case BELONG: + case LELONG: + (void) printf(m->desc, + (m->reln & MASK) ? p->l & m->mask : p->l); + break; + case STRING: + if ((rt=strchr(p->s, '\n')) != NULL) + *rt = '\0'; (void) printf(m->desc, p->s); + if (rt) + *rt = '\n'; + break; + case DATE: + case BEDATE: + case LEDATE: + pp = ctime((time_t*) &p->l); + if ((rt = strchr(pp, '\n')) != NULL) + *rt = '\0'; + (void) printf(m->desc, pp); + if (rt) + *rt = '\n'; break; default: - warning("invalid m->type (%d) in mprint()", m->type); + error("invalid m->type (%d) in mprint().\n", m->type); + /*NOTREACHED*/ } } -int +static int mcheck(s, m) -char *s; +unsigned char *s; struct magic *m; { register union VALUETYPE *p = (union VALUETYPE *)(s+m->offset); register long l = m->value.l; + register long mask = m->mask; register long v; if (debug) { (void) printf("mcheck: %10.10s ", s); mdump(m); } + + if ( (m->value.s[0] == 'x') && (m->value.s[1] == '\0') ) { + printf("BOINK"); + return 1; + } + switch (m->type) { case BYTE: v = p->b; break; case SHORT: v = p->h; break; case LONG: + case DATE: v = p->l; break; case STRING: l = 0; @@ -143,6 +265,7 @@ struct magic *m; * but ignoring any nulls. bcmp doesn't give -/+/0 * and isn't universally available anyway. */ + v = 0; { register unsigned char *a = (unsigned char*)m->value.s; register unsigned char *b = (unsigned char*)p->s; @@ -153,12 +276,35 @@ struct magic *m; break; } break; + case BESHORT: + v = (short)((p->hs[0]<<8)|(p->hs[1])); + break; + case BELONG: + case BEDATE: + v = (long) + ((p->hl[0]<<24)|(p->hl[1]<<16)|(p->hl[2]<<8)|(p->hl[3])); + break; + case LESHORT: + v = (short)((p->hs[1]<<8)|(p->hs[0])); + break; + case LELONG: + case LEDATE: + v = (long) + ((p->hl[3]<<24)|(p->hl[2]<<16)|(p->hl[1]<<8)|(p->hl[0])); + break; default: - warning("invalid type %d in mcheck()", m->type); - return 0; + error("invalid type %d in mcheck().\n", m->type); + return -1;/*NOTREACHED*/ } + if (m->mask != 0L) + v &= m->mask; + switch (m->reln) { + case 'x': + return 1; + case '!': + return v != l; case '=': return v == l; case '>': @@ -166,9 +312,17 @@ struct magic *m; case '<': return v < l; case '&': - return v & l; + return (v & l) == l; + case '^': + return (v & l) != l; + case MASK | '=': + return (v & mask) == l; + case MASK | '>': + return (v & mask) > l; + case MASK | '<': + return (v & mask) < l; default: - warning("mcheck: can't happen: invalid relation %d", m->reln); - return 0; + error("mcheck: can't happen: invalid relation %d.\n", m->reln); + return -1;/*NOTREACHED*/ } } diff --git a/usr.bin/file/tar.h b/usr.bin/file/tar.h index 652e518efe..9d5dce300a 100644 --- a/usr.bin/file/tar.h +++ b/usr.bin/file/tar.h @@ -4,6 +4,8 @@ * @(#)tar.h 1.20 86/10/29 Public Domain. * * Created 25 August 1985 by John Gilmore, ihnp4!hoptoad!gnu. + * + * tar.h,v 1.2 1993/06/10 00:38:20 jtc Exp # checkin only */ /* diff --git a/usr.bin/file/tst/Makefile b/usr.bin/file/tst/Makefile index b6a03645e4..c45dfaa783 100644 --- a/usr.bin/file/tst/Makefile +++ b/usr.bin/file/tst/Makefile @@ -1,12 +1,12 @@ # Make up some fake test files that are easily produced. # By no means an exhaustive test! -# @(#) $Header: Makefile,v 1.4 87/11/07 12:46:09 ian Exp $ -all: ar cmd emp i t x +# @(#) Makefile,v 1.2 1993/06/10 00:38:32 jtc Exp +all: ar cmd emp i tarf x ar: echo '<ar> fake fake fake' >$@ echo 070707 fake fake fake >$@.asc echo '!<arch>.__.SYMDEF fake fake fake' >$@.ranlib - echo - -h- >$@.swt + echo -h- >$@.swt cmd: echo '#! /bin/sh' >$@ echo '#!/bin/sh' >c.sh2 @@ -22,8 +22,8 @@ emp: i: echo '@document(language impress)fake fake' >$@ echo '@document(language diablo)fake fake' >$@.d -t: - rm -f $@ +tarf: + rm -f $@ # so not include self tar cvf $@ * x: echo 'Interpress/Xerox fake fake fake' >$@ diff --git a/usr.bin/find/Makefile b/usr.bin/find/Makefile index 7fc747b929..f15dd25bbd 100644 --- a/usr.bin/find/Makefile +++ b/usr.bin/find/Makefile @@ -1,4 +1,4 @@ -# @(#)Makefile 5.2 (Berkeley) 5/24/91 +# @(#)Makefile 8.1 (Berkeley) 6/6/93 PROG= find SRCS= find.c function.c ls.c main.c misc.c operator.c option.c diff --git a/usr.bin/find/extern.h b/usr.bin/find/extern.h index 750d633418..1f02f12bb4 100644 --- a/usr.bin/find/extern.h +++ b/usr.bin/find/extern.h @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,18 +30,18 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)extern.h 5.2 (Berkeley) 5/24/91 + * @(#)extern.h 8.1 (Berkeley) 6/6/93 */ #include <sys/cdefs.h> void brace_subst __P((char *, char **, char *, int)); void *emalloc __P((unsigned int)); -void err __P((const char *, ...)); PLAN *find_create __P((char ***)); void find_execute __P((PLAN *, char **)); PLAN *find_formplan __P((char **)); PLAN *not_squish __P((PLAN *)); +OPTION *option __P((char *)); PLAN *or_squish __P((PLAN *)); PLAN *paren_squish __P((PLAN *)); struct stat; @@ -62,6 +62,7 @@ PLAN *c_name __P((char *)); PLAN *c_newer __P((char *)); PLAN *c_nogroup __P((void)); PLAN *c_nouser __P((void)); +PLAN *c_path __P((char *)); PLAN *c_perm __P((char *)); PLAN *c_print __P((void)); PLAN *c_prune __P((void)); diff --git a/usr.bin/find/find.1 b/usr.bin/find/find.1 index 23b06252fb..b6ce6b9d58 100644 --- a/usr.bin/find/find.1 +++ b/usr.bin/find/find.1 @@ -1,5 +1,5 @@ -.\" Copyright (c) 1990 The Regents of the University of California. -.\" All rights reserved. +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. .\" .\" This code is derived from software contributed to Berkeley by .\" the Institute of Electrical and Electronics Engineers, Inc. @@ -32,9 +32,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)find.1 6.29 (Berkeley) 7/29/91 +.\" @(#)find.1 8.1 (Berkeley) 6/6/93 .\" -.Dd July 29, 1991 +.Dd June 6, 1993 .Dt FIND 1 .Os .Sh NAME @@ -42,7 +42,7 @@ .Nd walk a file hierarchy .Sh SYNOPSIS .Nm find -.Op Fl dsXx +.Op Fl HdhXx .Op Fl f Ar file .Op Ar file ... .Ar expression @@ -55,14 +55,19 @@ listed, evaluating an (composed of the ``primaries'' and ``operands'' listed below) in terms of each file in the tree. .Pp -If -.Ar file -is a symbolic link referencing an existing file, the directory tree -referenced by the link is descended instead of the link itself. -.Pp The options are as follows: .Pp .Bl -tag -width Ds +.It Fl H +The +.Fl H +option causes the file information and file type (see +.Xr stat 2 ) , +returned for each symbolic link encountered on the command line to be +those of the file referenced by the link, not the link itself. +If the referenced file does not exist, the file information and type will +be for the link itself. File information of all symbolic links not on +the command line is that of the link itself. .It Fl d The .Fl d @@ -85,9 +90,9 @@ option specifies a file hierarchy for to traverse. File hierarchies may also be specified as the operands immediately following the options. -.It Fl s +.It Fl h The -.Fl s +.Fl h option causes the file information and file type (see .Xr stat 2 ) , returned for each symbolic link to be those of the file referenced by the @@ -145,7 +150,7 @@ was executed. .It Ic -fstype Ar type True if the file is contained in a file system of type .Ar type . -Currently supported types are ``local'', ``mfs'', ``nfs'', ``msdos'', ``isofs'', +Currently supported types are ``isofs'', ``local'', ``mfs'', ``nfs'', ``pc'', ``rdonly'' and ``ufs''. The types ``local'' and ``rdonly'' are not specific file system types. The former matches any file system physically mounted on the system where @@ -373,13 +378,13 @@ that are newer than ``ttt''. .El .Sh SEE ALSO .Xr chmod 1 , -.Xr sh 1 , -.Xr test 1 , +.Xr locate 1 , .Xr stat 2 , -.Xr umask 2 , +.Xr fts 3 , .Xr getpwent 3 , .Xr getgrent 3 , -.Xr strmode 3 +.Xr strmode 3 , +.Xr symlink 7 .Sh STANDARDS The .Nm find diff --git a/usr.bin/find/find.c b/usr.bin/find/find.c index a332682a17..a1a068c337 100644 --- a/usr.bin/find/find.c +++ b/usr.bin/find/find.c @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Cimarron D. Taylor of the University of California, Berkeley. @@ -35,16 +35,19 @@ */ #ifndef lint -static char sccsid[] = "@(#)find.c 5.3 (Berkeley) 5/25/91"; +static char sccsid[] = "@(#)find.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #include <sys/types.h> #include <sys/stat.h> -#include <sys/errno.h> + +#include <err.h> +#include <errno.h> #include <fts.h> #include <stdio.h> #include <string.h> #include <stdlib.h> + #include "find.h" /* @@ -57,8 +60,6 @@ find_formplan(argv) char **argv; { PLAN *plan, *tail, *new; - PLAN *c_print(), *find_create(), *not_squish(), *or_squish(); - PLAN *paren_squish(); /* * for each argument in the command line, determine what kind of node @@ -76,7 +77,7 @@ find_formplan(argv) * by c_name() with an argument of foo and `-->' represents the * plan->next pointer. */ - for (plan = NULL; *argv;) { + for (plan = tail = NULL; *argv;) { if (!(new = find_create(&argv))) continue; if (plan == NULL) @@ -129,7 +130,7 @@ find_formplan(argv) plan = paren_squish(plan); /* ()'s */ plan = not_squish(plan); /* !'s */ plan = or_squish(plan); /* -o's */ - return(plan); + return (plan); } FTS *tree; /* pointer to top of FTS hierarchy */ @@ -148,7 +149,7 @@ find_execute(plan, paths) PLAN *p; if (!(tree = fts_open(paths, ftsoptions, (int (*)())NULL))) - err("ftsopen: %s", strerror(errno)); + err(1, "ftsopen"); while (entry = fts_read(tree)) { switch(entry->fts_info) { @@ -163,21 +164,14 @@ find_execute(plan, paths) case FTS_DNR: case FTS_ERR: case FTS_NS: - (void)fprintf(stderr, "find: %s: %s\n", - entry->fts_path, strerror(errno)); + (void)fflush(stdout); + warn("%s", entry->fts_path); continue; - case FTS_SL: - if (entry->fts_level == FTS_ROOTLEVEL) { - (void)fts_set(tree, entry, FTS_FOLLOW); - continue; - } - break; } - #define BADCH " \t\n\\'\"" if (isxargs && strpbrk(entry->fts_path, BADCH)) { - (void)fprintf(stderr, - "find: illegal path: %s\n", entry->fts_path); + (void)fflush(stdout); + warnx("%s: illegal path", entry->fts_path); continue; } diff --git a/usr.bin/find/find.h b/usr.bin/find/find.h index 3a78e9ee6a..4c4ffa54e9 100644 --- a/usr.bin/find/find.h +++ b/usr.bin/find/find.h @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Cimarron D. Taylor of the University of California, Berkeley. @@ -33,7 +33,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)find.h 5.8 (Berkeley) 5/24/91 + * @(#)find.h 8.1 (Berkeley) 6/6/93 */ /* node type */ @@ -41,14 +41,22 @@ enum ntype { N_AND = 1, /* must start > 0 */ N_ATIME, N_CLOSEPAREN, N_CTIME, N_DEPTH, N_EXEC, N_EXPR, N_FOLLOW, N_FSTYPE, N_GROUP, N_INUM, N_LINKS, N_LS, N_MTIME, N_NAME, N_NEWER, - N_NOGROUP, N_NOT, N_NOUSER, N_OK, N_OPENPAREN, N_OR, N_PERM, N_PRINT, - N_PRUNE, N_SIZE, N_TYPE, N_USER, N_XDEV, + N_NOGROUP, N_NOT, N_NOUSER, N_OK, N_OPENPAREN, N_OR, N_PATH, + N_PERM, N_PRINT, N_PRUNE, N_SIZE, N_TYPE, N_USER, N_XDEV, }; /* node definition */ typedef struct _plandata { struct _plandata *next; /* next node */ - int (*eval)(); /* node evaluation function */ + int (*eval) /* node evaluation function */ + __P((struct _plandata *, FTSENT *)); +#define F_EQUAL 1 /* [acm]time inum links size */ +#define F_LESSTHAN 2 +#define F_GREATER 3 +#define F_NEEDOK 1 /* exec ok */ +#define F_MTFLAG 1 /* fstype */ +#define F_MTTYPE 2 +#define F_ATLEAST 1 /* perm */ int flags; /* private flags */ enum ntype type; /* plan node type */ union { @@ -59,6 +67,7 @@ typedef struct _plandata { off_t _o_data; /* file size */ time_t _t_data; /* time value */ uid_t _u_data; /* uid */ + short _mt_data; /* mount flags */ struct _plandata *_p_data[2]; /* PLAN trees */ struct _ex { char **_e_argv; /* argv array */ @@ -68,12 +77,14 @@ typedef struct _plandata { char *_a_data[2]; /* array of char pointers */ char *_c_data; /* char pointer */ } p_un; +} PLAN; #define a_data p_un._a_data #define c_data p_un._c_data #define i_data p_un._i_data #define g_data p_un._g_data #define l_data p_un._l_data #define m_data p_un._m_data +#define mt_data p_un._mt_data #define o_data p_un._o_data #define p_data p_un._p_data #define t_data p_un._t_data @@ -81,6 +92,16 @@ typedef struct _plandata { #define e_argv p_un.ex._e_argv #define e_orig p_un.ex._e_orig #define e_len p_un.ex._e_len -} PLAN; + +typedef struct _option { + char *name; /* option name */ + enum ntype token; /* token type */ + PLAN *(*create)(); /* create function: DON'T PROTOTYPE! */ +#define O_NONE 0x01 /* no call required */ +#define O_ZERO 0x02 /* pass: nothing */ +#define O_ARGV 0x04 /* pass: argv, increment argv */ +#define O_ARGVP 0x08 /* pass: *argv, N_OK || N_EXEC */ + int flags; +} OPTION; #include "extern.h" diff --git a/usr.bin/find/function.c b/usr.bin/find/function.c index 510a0d1145..df053f9a18 100644 --- a/usr.bin/find/function.c +++ b/usr.bin/find/function.c @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Cimarron D. Taylor of the University of California, Berkeley. @@ -35,97 +35,111 @@ */ #ifndef lint -static char sccsid[] = "@(#)function.c 5.17 (Berkeley) 5/24/91"; +static char sccsid[] = "@(#)function.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #include <sys/param.h> +#include <sys/ucred.h> #include <sys/stat.h> #include <sys/wait.h> #include <sys/mount.h> + +#include <err.h> #include <errno.h> +#include <fnmatch.h> +#include <fts.h> #include <grp.h> #include <pwd.h> -#include <fts.h> -#include <unistd.h> -#include <tzfile.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "find.h" +#include <tzfile.h> +#include <unistd.h> -#define FIND_EQUAL 0 -#define FIND_LESSTHAN 1 -#define FIND_GREATER 2 +#include "find.h" -#define COMPARE(a, b) { \ - switch(plan->flags) { \ - case FIND_EQUAL: \ - return(a == b); \ - case FIND_LESSTHAN: \ - return(a < b); \ - case FIND_GREATER: \ - return(a > b); \ - } \ - return(0); \ +#define COMPARE(a, b) { \ + switch (plan->flags) { \ + case F_EQUAL: \ + return (a == b); \ + case F_LESSTHAN: \ + return (a < b); \ + case F_GREATER: \ + return (a > b); \ + default: \ + abort(); \ + } \ } -static PLAN *palloc __P((enum ntype, int (*)())); +static PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *)))); /* * find_parsenum -- * Parse a string of the form [+-]# and return the value. */ -long -find_parsenum(plan, option, str, endch) +static long +find_parsenum(plan, option, vp, endch) PLAN *plan; - char *option, *str, *endch; + char *option, *vp, *endch; { long value; - char *endchar; /* pointer to character ending conversion */ + char *endchar, *str; /* Pointer to character ending conversion. */ - /* determine comparison from leading + or - */ - switch(*str) { + /* Determine comparison from leading + or -. */ + str = vp; + switch (*str) { case '+': ++str; - plan->flags = FIND_GREATER; + plan->flags = F_GREATER; break; case '-': ++str; - plan->flags = FIND_LESSTHAN; + plan->flags = F_LESSTHAN; break; default: - plan->flags = FIND_EQUAL; + plan->flags = F_EQUAL; break; } /* - * convert the string with strtol(). Note, if strtol() returns zero + * Convert the string with strtol(). Note, if strtol() returns zero * and endchar points to the beginning of the string we know we have * a syntax error. */ value = strtol(str, &endchar, 10); - if (!value && endchar == str || - endchar[0] && (!endch || endchar[0] != *endch)) - err("%s: %s", option, "illegal numeric value"); + if (value == 0 && endchar == str) + errx(1, "%s: %s: illegal numeric value", option, vp); + if (endchar[0] && (endch == NULL || endchar[0] != *endch)) + errx(1, "%s: %s: illegal trailing character", option, vp); if (endch) *endch = endchar[0]; - return(value); + return (value); } +/* + * The value of n for the inode times (atime, ctime, and mtime) is a range, + * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with + * -n, such that "-mtime -1" would be less than 0 days, which isn't what the + * user wanted. Correct so that -1 is "less than 1". + */ +#define TIME_CORRECT(p, ttype) \ + if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \ + ++((p)->t_data); + /* * -atime n functions -- * * True if the difference between the file access time and the * current time is n 24 hour periods. - * */ +int f_atime(plan, entry) PLAN *plan; FTSENT *entry; { extern time_t now; - COMPARE((now - entry->fts_statb.st_atime + + COMPARE((now - entry->fts_statp->st_atime + SECSPERDAY - 1) / SECSPERDAY, plan->t_data); } @@ -139,7 +153,8 @@ c_atime(arg) new = palloc(N_ATIME, f_atime); new->t_data = find_parsenum(new, "-atime", arg, NULL); - return(new); + TIME_CORRECT(new, N_ATIME); + return (new); } /* * -ctime n functions -- @@ -147,13 +162,14 @@ c_atime(arg) * True if the difference between the last change of file * status information and the current time is n 24 hour periods. */ +int f_ctime(plan, entry) PLAN *plan; FTSENT *entry; { extern time_t now; - COMPARE((now - entry->fts_statb.st_ctime + + COMPARE((now - entry->fts_statp->st_ctime + SECSPERDAY - 1) / SECSPERDAY, plan->t_data); } @@ -166,8 +182,9 @@ c_ctime(arg) ftsoptions &= ~FTS_NOSTAT; new = palloc(N_CTIME, f_ctime); - new->t_data = find_parsenum(new, "-ctime", arg, (char *)NULL); - return(new); + new->t_data = find_parsenum(new, "-ctime", arg, NULL); + TIME_CORRECT(new, N_CTIME); + return (new); } /* @@ -177,12 +194,12 @@ c_ctime(arg) * so that all entries in a directory are acted on before the directory * itself. */ -/* ARGSUSED */ +int f_always_true(plan, entry) PLAN *plan; FTSENT *entry; { - return(1); + return (1); } PLAN * @@ -190,7 +207,7 @@ c_depth() { isdepth = 1; - return(palloc(N_DEPTH, f_always_true)); + return (palloc(N_DEPTH, f_always_true)); } /* @@ -205,6 +222,7 @@ c_depth() * The primary -ok is different in that it requests affirmation of the * user before executing the utility. */ +int f_exec(plan, entry) register PLAN *plan; FTSENT *entry; @@ -219,26 +237,24 @@ f_exec(plan, entry) brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], entry->fts_path, plan->e_len[cnt]); - if (plan->flags && !queryuser(plan->e_argv)) - return(0); + if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv)) + return (0); - switch(pid = vfork()) { + switch (pid = vfork()) { case -1: - err("fork: %s", strerror(errno)); + err(1, "fork"); /* NOTREACHED */ case 0: if (fchdir(dotfd)) { - (void)fprintf(stderr, - "find: chdir: %s\n", strerror(errno)); + warn("chdir"); _exit(1); } execvp(plan->e_argv[0], plan->e_argv); - (void)fprintf(stderr, - "find: %s: %s\n", plan->e_argv[0], strerror(errno)); + warn("%s", plan->e_argv[0]); _exit(1); } pid = waitpid(pid, &status, 0); - return(pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); + return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); } /* @@ -260,12 +276,13 @@ c_exec(argvp, isok) isoutput = 1; new = palloc(N_EXEC, f_exec); - new->flags = isok; + if (isok) + new->flags = F_NEEDOK; for (ap = argv = *argvp;; ++ap) { if (!*ap) - err("%s: %s", - isok ? "-ok" : "-exec", "no terminating \";\""); + errx(1, + "%s: no terminating \";\"", isok ? "-ok" : "-exec"); if (**ap == ';') break; } @@ -291,7 +308,7 @@ c_exec(argvp, isok) new->e_argv[cnt] = new->e_orig[cnt] = NULL; *argvp = argv + 1; - return(new); + return (new); } /* @@ -306,7 +323,7 @@ c_follow() ftsoptions &= ~FTS_PHYSICAL; ftsoptions |= FTS_LOGICAL; - return(palloc(N_FOLLOW, f_always_true)); + return (palloc(N_FOLLOW, f_always_true)); } /* @@ -314,6 +331,7 @@ c_follow() * * True if the file is of a certain type. */ +int f_fstype(plan, entry) PLAN *plan; FTSENT *entry; @@ -324,9 +342,9 @@ f_fstype(plan, entry) static short val; char *p, save[2]; - /* only check when we cross mount point */ - if (first || curdev != entry->fts_statb.st_dev) { - curdev = entry->fts_statb.st_dev; + /* Only check when we cross mount point. */ + if (first || curdev != entry->fts_statp->st_dev) { + curdev = entry->fts_statp->st_dev; /* * Statfs follows symlinks; find wants the link's file system, @@ -334,7 +352,7 @@ f_fstype(plan, entry) */ if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE) { - if (p = rindex(entry->fts_accpath, '/')) + if (p = strrchr(entry->fts_accpath, '/')) ++p; else p = entry->fts_accpath; @@ -347,7 +365,7 @@ f_fstype(plan, entry) p = NULL; if (statfs(entry->fts_accpath, &sb)) - err("%s: %s", entry->fts_accpath, strerror(errno)); + err(1, "%s", entry->fts_accpath); if (p) { p[0] = save[0]; @@ -355,10 +373,25 @@ f_fstype(plan, entry) } first = 0; - val = plan->flags == MOUNT_NONE ? sb.f_flags : sb.f_type; + switch (plan->flags) { + case F_MTFLAG: + val = sb.f_flags; + break; + case F_MTTYPE: + val = sb.f_type; + break; + default: + abort(); + } + } + switch(plan->flags) { + case F_MTFLAG: + return (val & plan->mt_data); + case F_MTTYPE: + return (val == plan->mt_data); + default: + abort(); } - return(plan->flags == MOUNT_NONE ? - val & MNT_LOCAL : val == plan->flags); } PLAN * @@ -370,43 +403,60 @@ c_fstype(arg) ftsoptions &= ~FTS_NOSTAT; new = palloc(N_FSTYPE, f_fstype); - switch(*arg) { + switch (*arg) { case 'i': if (!strcmp(arg, "isofs")) { - new->flags = MOUNT_ISOFS; - return(new); + new->flags = F_MTTYPE; + new->mt_data = MOUNT_ISOFS; + return (new); } break; case 'l': if (!strcmp(arg, "local")) { - new->flags = MOUNT_NONE; - return(new); + new->flags = F_MTFLAG; + new->mt_data = MNT_LOCAL; + return (new); } break; case 'm': if (!strcmp(arg, "mfs")) { - new->flags = MOUNT_MFS; - return(new); - } - if (!strcmp(arg, "msdos")) { - new->flags = MOUNT_MSDOS; - return(new); + new->flags = F_MTTYPE; + new->mt_data = MOUNT_MFS; + return (new); } break; case 'n': if (!strcmp(arg, "nfs")) { - new->flags = MOUNT_NFS; - return(new); + new->flags = F_MTTYPE; + new->mt_data = MOUNT_NFS; + return (new); + } + break; +#ifdef MOUNT_PC + case 'p': + if (!strcmp(arg, "pc")) { + new->flags = F_MTTYPE; + new->mt_data = MOUNT_PC; + return (new); + } + break; +#endif + case 'r': + if (!strcmp(arg, "rdonly")) { + new->flags = F_MTFLAG; + new->mt_data = MNT_RDONLY; + return (new); } break; case 'u': if (!strcmp(arg, "ufs")) { - new->flags = MOUNT_UFS; - return(new); + new->flags = F_MTTYPE; + new->mt_data = MOUNT_UFS; + return (new); } break; } - err("unknown file type %s", arg); + errx(1, "%s: unknown file type", arg); /* NOTREACHED */ } @@ -417,11 +467,12 @@ c_fstype(arg) * an equivalent of the getgrnam() function does not return a valid group * name, gname is taken as a group ID. */ +int f_group(plan, entry) PLAN *plan; FTSENT *entry; { - return(entry->fts_statb.st_gid == plan->g_data); + return (entry->fts_statp->st_gid == plan->g_data); } PLAN * @@ -438,13 +489,13 @@ c_group(gname) if (g == NULL) { gid = atoi(gname); if (gid == 0 && gname[0] != '0') - err("%s: %s", "-group", "no such group"); + errx(1, "-group: %s: no such group", gname); } else gid = g->gr_gid; new = palloc(N_GROUP, f_group); new->g_data = gid; - return(new); + return (new); } /* @@ -452,11 +503,12 @@ c_group(gname) * * True if the file has inode # n. */ +int f_inum(plan, entry) PLAN *plan; FTSENT *entry; { - COMPARE(entry->fts_statb.st_ino, plan->i_data); + COMPARE(entry->fts_statp->st_ino, plan->i_data); } PLAN * @@ -468,8 +520,8 @@ c_inum(arg) ftsoptions &= ~FTS_NOSTAT; new = palloc(N_INUM, f_inum); - new->i_data = find_parsenum(new, "-inum", arg, (char *)NULL); - return(new); + new->i_data = find_parsenum(new, "-inum", arg, NULL); + return (new); } /* @@ -477,11 +529,12 @@ c_inum(arg) * * True if the file has n links. */ +int f_links(plan, entry) PLAN *plan; FTSENT *entry; { - COMPARE(entry->fts_statb.st_nlink, plan->l_data); + COMPARE(entry->fts_statp->st_nlink, plan->l_data); } PLAN * @@ -493,8 +546,8 @@ c_links(arg) ftsoptions &= ~FTS_NOSTAT; new = palloc(N_LINKS, f_links); - new->l_data = (nlink_t)find_parsenum(new, "-links", arg, (char *)NULL); - return(new); + new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL); + return (new); } /* @@ -502,13 +555,13 @@ c_links(arg) * * Always true - prints the current entry to stdout in "ls" format. */ -/* ARGSUSED */ +int f_ls(plan, entry) PLAN *plan; FTSENT *entry; { - printlong(entry->fts_path, entry->fts_accpath, &entry->fts_statb); - return(1); + printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); + return (1); } PLAN * @@ -517,7 +570,38 @@ c_ls() ftsoptions &= ~FTS_NOSTAT; isoutput = 1; - return(palloc(N_LS, f_ls)); + return (palloc(N_LS, f_ls)); +} + +/* + * -mtime n functions -- + * + * True if the difference between the file modification time and the + * current time is n 24 hour periods. + */ +int +f_mtime(plan, entry) + PLAN *plan; + FTSENT *entry; +{ + extern time_t now; + + COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) / + SECSPERDAY, plan->t_data); +} + +PLAN * +c_mtime(arg) + char *arg; +{ + PLAN *new; + + ftsoptions &= ~FTS_NOSTAT; + + new = palloc(N_MTIME, f_mtime); + new->t_data = find_parsenum(new, "-mtime", arg, NULL); + TIME_CORRECT(new, N_MTIME); + return (new); } /* @@ -526,11 +610,12 @@ c_ls() * True if the basename of the filename being examined * matches pattern using Pattern Matching Notation S3.14 */ +int f_name(plan, entry) PLAN *plan; FTSENT *entry; { - return(fnmatch(plan->c_data, entry->fts_name, FNM_QUOTE)); + return (!fnmatch(plan->c_data, entry->fts_name, 0)); } PLAN * @@ -541,7 +626,7 @@ c_name(pattern) new = palloc(N_NAME, f_name); new->c_data = pattern; - return(new); + return (new); } /* @@ -551,11 +636,12 @@ c_name(pattern) * then the modification time of the file named by the pathname * file. */ +int f_newer(plan, entry) PLAN *plan; FTSENT *entry; { - return(entry->fts_statb.st_mtime > plan->t_data); + return (entry->fts_statp->st_mtime > plan->t_data); } PLAN * @@ -568,10 +654,10 @@ c_newer(filename) ftsoptions &= ~FTS_NOSTAT; if (stat(filename, &sb)) - err("%s: %s", filename, strerror(errno)); + err(1, "%s", filename); new = palloc(N_NEWER, f_newer); new->t_data = sb.st_mtime; - return(new); + return (new); } /* @@ -580,14 +666,14 @@ c_newer(filename) * True if file belongs to a user ID for which the equivalent * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. */ -/* ARGSUSED */ +int f_nogroup(plan, entry) PLAN *plan; FTSENT *entry; { char *group_from_gid(); - return(group_from_gid(entry->fts_statb.st_gid, 1) ? 1 : 0); + return (group_from_gid(entry->fts_statp->st_gid, 1) ? 1 : 0); } PLAN * @@ -595,7 +681,7 @@ c_nogroup() { ftsoptions &= ~FTS_NOSTAT; - return(palloc(N_NOGROUP, f_nogroup)); + return (palloc(N_NOGROUP, f_nogroup)); } /* @@ -604,14 +690,14 @@ c_nogroup() * True if file belongs to a user ID for which the equivalent * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. */ -/* ARGSUSED */ +int f_nouser(plan, entry) PLAN *plan; FTSENT *entry; { char *user_from_uid(); - return(user_from_uid(entry->fts_statb.st_uid, 1) ? 1 : 0); + return (user_from_uid(entry->fts_statp->st_uid, 1) ? 1 : 0); } PLAN * @@ -619,7 +705,32 @@ c_nouser() { ftsoptions &= ~FTS_NOSTAT; - return(palloc(N_NOUSER, f_nouser)); + return (palloc(N_NOUSER, f_nouser)); +} + +/* + * -path functions -- + * + * True if the path of the filename being examined + * matches pattern using Pattern Matching Notation S3.14 + */ +int +f_path(plan, entry) + PLAN *plan; + FTSENT *entry; +{ + return (!fnmatch(plan->c_data, entry->fts_path, 0)); +} + +PLAN * +c_path(pattern) + char *pattern; +{ + PLAN *new; + + new = palloc(N_NAME, f_path); + new->c_data = pattern; + return (new); } /* @@ -629,18 +740,19 @@ c_nouser() * with a leading digit, it's treated as an octal mode, otherwise as a * symbolic mode. */ +int f_perm(plan, entry) PLAN *plan; FTSENT *entry; { mode_t mode; - mode = entry->fts_statb.st_mode & + mode = entry->fts_statp->st_mode & (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); - if (plan->flags) - return((plan->m_data | mode) == mode); + if (plan->flags == F_ATLEAST) + return ((plan->m_data | mode) == mode); else - return(mode == plan->m_data); + return (mode == plan->m_data); /* NOTREACHED */ } @@ -656,15 +768,15 @@ c_perm(perm) new = palloc(N_PERM, f_perm); if (*perm == '-') { - new->flags = 1; + new->flags = F_ATLEAST; ++perm; } if ((set = setmode(perm)) == NULL) - err("%s: %s", "-perm", "illegal mode string"); + err(1, "-perm: %s: illegal mode string", perm); new->m_data = getmode(set, 0); - return(new); + return (new); } /* @@ -673,13 +785,13 @@ c_perm(perm) * Always true, causes the current pathame to be written to * standard output. */ -/* ARGSUSED */ +int f_print(plan, entry) PLAN *plan; FTSENT *entry; { (void)printf("%s\n", entry->fts_path); - return(1); + return (1); } PLAN * @@ -687,7 +799,7 @@ c_print() { isoutput = 1; - return(palloc(N_PRINT, f_print)); + return (palloc(N_PRINT, f_print)); } /* @@ -695,7 +807,7 @@ c_print() * * Prune a portion of the hierarchy. */ -/* ARGSUSED */ +int f_prune(plan, entry) PLAN *plan; FTSENT *entry; @@ -703,14 +815,14 @@ f_prune(plan, entry) extern FTS *tree; if (fts_set(tree, entry, FTS_SKIP)) - err("%s: %s", entry->fts_path, strerror(errno)); - return(1); + err(1, "%s", entry->fts_path); + return (1); } PLAN * c_prune() { - return(palloc(N_PRUNE, f_prune)); + return (palloc(N_PRUNE, f_prune)); } /* @@ -723,14 +835,15 @@ c_prune() #define FIND_SIZE 512 static int divsize = 1; +int f_size(plan, entry) PLAN *plan; FTSENT *entry; { off_t size; - size = divsize ? (entry->fts_statb.st_size + FIND_SIZE - 1) / - FIND_SIZE : entry->fts_statb.st_size; + size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / + FIND_SIZE : entry->fts_statp->st_size; COMPARE(size, plan->o_data); } @@ -744,10 +857,11 @@ c_size(arg) ftsoptions &= ~FTS_NOSTAT; new = palloc(N_SIZE, f_size); + endch = 'c'; new->o_data = find_parsenum(new, "-size", arg, &endch); if (endch == 'c') divsize = 0; - return(new); + return (new); } /* @@ -757,11 +871,12 @@ c_size(arg) * block special file, character special file, directory, FIFO, or * regular file, respectively. */ +int f_type(plan, entry) PLAN *plan; FTSENT *entry; { - return((entry->fts_statb.st_mode & S_IFMT) == plan->m_data); + return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data); } PLAN * @@ -796,12 +911,12 @@ c_type(typestring) mask = S_IFSOCK; break; default: - err("%s: %s", "-type", "unknown type"); + errx(1, "-type: %s: unknown type", typestring); } new = palloc(N_TYPE, f_type); new->m_data = mask; - return(new); + return (new); } /* @@ -811,11 +926,12 @@ c_type(typestring) * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not * return a valid user name, uname is taken as a user ID. */ +int f_user(plan, entry) PLAN *plan; FTSENT *entry; { - return(entry->fts_statb.st_uid == plan->u_data); + return (entry->fts_statp->st_uid == plan->u_data); } PLAN * @@ -832,13 +948,13 @@ c_user(username) if (p == NULL) { uid = atoi(username); if (uid == 0 && username[0] != '0') - err("%s: %s", "-user", "no such user"); + errx(1, "-user: %s: no such user", username); } else uid = p->pw_uid; new = palloc(N_USER, f_user); new->u_data = uid; - return(new); + return (new); } /* @@ -852,7 +968,7 @@ c_xdev() { ftsoptions |= FTS_XDEV; - return(palloc(N_XDEV, f_always_true)); + return (palloc(N_XDEV, f_always_true)); } /* @@ -860,6 +976,7 @@ c_xdev() * * True if expression is true. */ +int f_expr(plan, entry) PLAN *plan; FTSENT *entry; @@ -869,7 +986,7 @@ f_expr(plan, entry) for (p = plan->p_data[0]; p && (state = (p->eval)(p, entry)); p = p->next); - return(state); + return (state); } /* @@ -880,49 +997,21 @@ f_expr(plan, entry) PLAN * c_openparen() { - return(palloc(N_OPENPAREN, (int (*)())-1)); + return (palloc(N_OPENPAREN, (int (*)())-1)); } PLAN * c_closeparen() { - return(palloc(N_CLOSEPAREN, (int (*)())-1)); -} - -/* - * -mtime n functions -- - * - * True if the difference between the file modification time and the - * current time is n 24 hour periods. - */ -f_mtime(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - extern time_t now; - - COMPARE((now - entry->fts_statb.st_mtime + SECSPERDAY - 1) / - SECSPERDAY, plan->t_data); + return (palloc(N_CLOSEPAREN, (int (*)())-1)); } -PLAN * -c_mtime(arg) - char *arg; -{ - PLAN *new; - - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_MTIME, f_mtime); - new->t_data = find_parsenum(new, "-mtime", arg, (char *)NULL); - return(new); -} - /* * ! expression functions -- * * Negation of a primary; the unary NOT operator. */ +int f_not(plan, entry) PLAN *plan; FTSENT *entry; @@ -932,13 +1021,13 @@ f_not(plan, entry) for (p = plan->p_data[0]; p && (state = (p->eval)(p, entry)); p = p->next); - return(!state); + return (!state); } PLAN * c_not() { - return(palloc(N_NOT, f_not)); + return (palloc(N_NOT, f_not)); } /* @@ -947,6 +1036,7 @@ c_not() * Alternation of primaries; the OR operator. The second expression is * not evaluated if the first expression is true. */ +int f_or(plan, entry) PLAN *plan; FTSENT *entry; @@ -958,23 +1048,23 @@ f_or(plan, entry) p && (state = (p->eval)(p, entry)); p = p->next); if (state) - return(1); + return (1); for (p = plan->p_data[1]; p && (state = (p->eval)(p, entry)); p = p->next); - return(state); + return (state); } PLAN * c_or() { - return(palloc(N_OR, f_or)); + return (palloc(N_OR, f_or)); } static PLAN * palloc(t, f) enum ntype t; - int (*f)(); + int (*f) __P((PLAN *, FTSENT *)); { PLAN *new; @@ -983,8 +1073,8 @@ palloc(t, f) new->eval = f; new->flags = 0; new->next = NULL; - return(new); + return (new); } - err("%s", strerror(errno)); + err(1, NULL); /* NOTREACHED */ } diff --git a/usr.bin/find/ls.c b/usr.bin/find/ls.c index a404f1b995..6c0352ebe1 100644 --- a/usr.bin/find/ls.c +++ b/usr.bin/find/ls.c @@ -1,6 +1,6 @@ /* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,21 +32,26 @@ */ #ifndef lint -static char sccsid[] = "@(#)ls.c 5.6 (Berkeley) 3/9/91"; +static char sccsid[] = "@(#)ls.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #include <sys/param.h> #include <sys/stat.h> -#include <time.h> -#include <tzfile.h> + +#include <err.h> #include <errno.h> -#include <utmp.h> -#include <unistd.h> #include <stdio.h> #include <string.h> +#include <time.h> +#include <tzfile.h> +#include <unistd.h> +#include <utmp.h> /* Derived from the print routines in the ls(1) source code. */ +static void printlink __P((char *)); +static void printtime __P((time_t)); + void printlong(name, accpath, sb) char *name; /* filename to print */ @@ -55,7 +60,7 @@ printlong(name, accpath, sb) { char modep[15], *user_from_uid(), *group_from_gid(); - (void)printf("%6lu %4ld ", sb->st_ino, sb->st_blocks); + (void)printf("%6lu %4qd ", sb->st_ino, (long long)sb->st_blocks); (void)strmode(sb->st_mode, modep); (void)printf("%s %3u %-*s %-*s ", modep, sb->st_nlink, UT_NAMESIZE, user_from_uid(sb->st_uid, 0), UT_NAMESIZE, @@ -65,7 +70,7 @@ printlong(name, accpath, sb) (void)printf("%3d, %3d ", major(sb->st_rdev), minor(sb->st_rdev)); else - (void)printf("%8ld ", sb->st_size); + (void)printf("%8qd ", (long long)sb->st_size); printtime(sb->st_mtime); (void)printf("%s", name); if (S_ISLNK(sb->st_mode)) @@ -73,6 +78,7 @@ printlong(name, accpath, sb) (void)putchar('\n'); } +static void printtime(ftime) time_t ftime; { @@ -96,6 +102,7 @@ printtime(ftime) (void)putchar(' '); } +static void printlink(name) char *name; { @@ -103,8 +110,7 @@ printlink(name) char path[MAXPATHLEN + 1]; if ((lnklen = readlink(name, path, MAXPATHLEN)) == -1) { - (void)fprintf(stderr, - "\nfind: %s: %s\n", name, strerror(errno)); + warn("%s", name); return; } path[lnklen] = '\0'; diff --git a/usr.bin/find/main.c b/usr.bin/find/main.c index 114e8ef08b..073a5f030d 100644 --- a/usr.bin/find/main.c +++ b/usr.bin/find/main.c @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,17 +32,20 @@ */ #ifndef lint -static char sccsid[] = "@(#)main.c 5.9 (Berkeley) 5/24/91"; +static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #include <sys/types.h> #include <sys/stat.h> + +#include <err.h> +#include <errno.h> #include <fcntl.h> -#include <time.h> #include <fts.h> -#include <errno.h> #include <stdio.h> #include <stdlib.h> +#include <time.h> + #include "find.h" time_t now; /* time find was run */ @@ -53,29 +56,32 @@ int isdepth; /* do directories on post-order visit */ int isoutput; /* user specified output operator */ int isxargs; /* don't permit xargs delimiting chars */ -static void usage(); +static void usage __P((void)); +int main(argc, argv) int argc; - char **argv; + char *argv[]; { register char **p, **start; - PLAN *find_formplan(); int ch; (void)time(&now); /* initialize the time-of-day */ p = start = argv; ftsoptions = FTS_NOSTAT|FTS_PHYSICAL; - while ((ch = getopt(argc, argv, "df:sXx")) != EOF) + while ((ch = getopt(argc, argv, "Hdf:hXx")) != EOF) switch(ch) { + case 'H': + ftsoptions |= FTS_COMFOLLOW; + break; case 'd': isdepth = 1; break; case 'f': *p++ = optarg; break; - case 's': + case 'h': ftsoptions &= ~FTS_PHYSICAL; ftsoptions |= FTS_LOGICAL; break; @@ -83,7 +89,6 @@ main(argc, argv) isxargs = 1; break; case 'x': - ftsoptions &= ~FTS_NOSTAT; ftsoptions |= FTS_XDEV; break; case '?': @@ -106,15 +111,25 @@ main(argc, argv) *p = NULL; if ((dotfd = open(".", O_RDONLY, 0)) < 0) - err(".: %s", strerror(errno)); + err(1, ".:"); find_execute(find_formplan(argv), start); + exit(0); } static void usage() { (void)fprintf(stderr, - "usage: find [-dsXx] [-f file] [file ...] expression\n"); + "usage: find [-HdhXx] [-f file] [file ...] expression\n"); exit(1); } + + + + + + + + + diff --git a/usr.bin/find/misc.c b/usr.bin/find/misc.c index 3ff4d5a438..ea9a3337b1 100644 --- a/usr.bin/find/misc.c +++ b/usr.bin/find/misc.c @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Cimarron D. Taylor of the University of California, Berkeley. @@ -35,15 +35,19 @@ */ #ifndef lint -static char sccsid[] = "@(#)misc.c 5.8 (Berkeley) 5/24/91"; +static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #include <sys/types.h> #include <sys/stat.h> -#include <sys/errno.h> + +#include <err.h> +#include <errno.h> +#include <fts.h> #include <stdio.h> #include <stdlib.h> #include <string.h> + #include "find.h" /* @@ -63,8 +67,8 @@ brace_subst(orig, store, path, len) if (ch == '{' && orig[1] == '}') { while ((p - *store) + plen > len) if (!(*store = realloc(*store, len *= 2))) - err("%s", strerror(errno)); - bcopy(path, p, plen); + err(1, NULL); + memmove(p, path, plen); p += plen; ++orig; } else @@ -77,6 +81,7 @@ brace_subst(orig, store, path, len) * print a message to standard error and then read input from standard * input. If the input is 'y' then 1 is returned. */ +int queryuser(argv) register char **argv; { @@ -103,7 +108,7 @@ queryuser(argv) (void)fprintf(stderr, "\n"); (void)fflush(stderr); } - return(first == 'y'); + return (first == 'y'); } /* @@ -117,36 +122,6 @@ emalloc(len) void *p; if (p = malloc(len)) - return(p); - err("%s", strerror(errno)); - /* NOTREACHED */ -} - -#if __STDC__ -#include <stdarg.h> -#else -#include <varargs.h> -#endif - -void -#if __STDC__ -err(const char *fmt, ...) -#else -err(fmt, va_alist) - char *fmt; - va_dcl -#endif -{ - va_list ap; -#if __STDC__ - va_start(ap, fmt); -#else - va_start(ap); -#endif - (void)fprintf(stderr, "find: "); - (void)vfprintf(stderr, fmt, ap); - va_end(ap); - (void)fprintf(stderr, "\n"); - exit(1); - /* NOTREACHED */ + return (p); + err(1, NULL); } diff --git a/usr.bin/find/operator.c b/usr.bin/find/operator.c index b62add6a2f..a706b880cf 100644 --- a/usr.bin/find/operator.c +++ b/usr.bin/find/operator.c @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Cimarron D. Taylor of the University of California, Berkeley. @@ -35,11 +35,15 @@ */ #ifndef lint -static char sccsid[] = "@(#)operator.c 5.4 (Berkeley) 5/24/91"; +static char sccsid[] = "@(#)operator.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #include <sys/types.h> + +#include <err.h> +#include <fts.h> #include <stdio.h> + #include "find.h" /* @@ -53,10 +57,10 @@ yanknode(planp) PLAN *node; /* top node removed from the plan */ if ((node = (*planp)) == NULL) - return(NULL); + return (NULL); (*planp) = (*planp)->next; node->next = NULL; - return(node); + return (node); } /* @@ -77,7 +81,7 @@ yankexpr(planp) /* first pull the top node from the plan */ if ((node = yanknode(planp)) == NULL) - return(NULL); + return (NULL); /* * If the node is an '(' then we recursively slurp up expressions @@ -88,7 +92,7 @@ yankexpr(planp) if (node->type == N_OPENPAREN) for (tail = subplan = NULL;;) { if ((next = yankexpr(planp)) == NULL) - err("%s: %s", "(", "missing closing ')'"); + err(1, "(: missing closing ')'"); /* * If we find a closing ')' we store the collected * subplan in our '(' node and convert the node to @@ -98,8 +102,7 @@ yankexpr(planp) */ if (next->type == N_CLOSEPAREN) { if (subplan == NULL) - err("%s: %s", - "()", "empty inner expression"); + errx(1, "(): empty inner expression"); node->p_data[0] = subplan; node->type = N_EXPR; node->eval = f_expr; @@ -114,7 +117,7 @@ yankexpr(planp) tail->next = NULL; } } - return(node); + return (node); } /* @@ -141,7 +144,7 @@ paren_squish(plan) * '(' someplace. */ if (expr->type == N_CLOSEPAREN) - err("%s: %s", ")", "no beginning '('"); + errx(1, "): no beginning '('"); /* add the expression to our result plan */ if (result == NULL) @@ -152,7 +155,7 @@ paren_squish(plan) } tail->next = NULL; } - return(result); + return (result); } /* @@ -192,9 +195,9 @@ not_squish(plan) node = yanknode(&plan); } if (node == NULL) - err("%s: %s", "!", "no following expression"); + errx(1, "!: no following expression"); if (node->type == N_OR) - err("%s: %s", "!", "nothing between ! and -o"); + errx(1, "!: nothing between ! and -o"); if (notlevel % 2 != 1) next = node; else @@ -210,7 +213,7 @@ not_squish(plan) } tail->next = NULL; } - return(result); + return (result); } /* @@ -246,12 +249,12 @@ or_squish(plan) */ if (next->type == N_OR) { if (result == NULL) - err("%s: %s", "-o", "no expression before -o"); + errx(1, "-o: no expression before -o"); next->p_data[0] = result; next->p_data[1] = or_squish(plan); if (next->p_data[1] == NULL) - err("%s: %s", "-o", "no expression after -o"); - return(next); + errx(1, "-o: no expression after -o"); + return (next); } /* add the node to our result plan */ @@ -263,5 +266,5 @@ or_squish(plan) } tail->next = NULL; } - return(result); + return (result); } diff --git a/usr.bin/find/option.c b/usr.bin/find/option.c index 934d2d7315..87337ca671 100644 --- a/usr.bin/find/option.c +++ b/usr.bin/find/option.c @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Cimarron D. Taylor of the University of California, Berkeley. @@ -35,60 +35,53 @@ */ #ifndef lint -static char sccsid[] = "@(#)option.c 5.8 (Berkeley) 6/4/91"; +static char sccsid[] = "@(#)option.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #include <sys/types.h> #include <sys/stat.h> + +#include <err.h> #include <fts.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "find.h" -typedef struct _option { - char *name; /* option name */ - enum ntype token; /* token type */ - PLAN *(*create)(); /* create function */ -#define O_NONE 0x01 /* no call required */ -#define O_ZERO 0x02 /* pass: nothing */ -#define O_ARGV 0x04 /* pass: argv, increment argv */ -#define O_ARGVP 0x08 /* pass: *argv, N_OK || N_EXEC */ - int flags; -} OPTION; +#include "find.h" -OPTION options[] = { - "!", N_NOT, c_not, O_ZERO, - "(", N_OPENPAREN, c_openparen, O_ZERO, - ")", N_CLOSEPAREN, c_closeparen, O_ZERO, - "-a", N_AND, NULL, O_NONE, - "-and", N_AND, NULL, O_NONE, - "-atime", N_ATIME, c_atime, O_ARGV, - "-ctime", N_CTIME, c_ctime, O_ARGV, - "-depth", N_DEPTH, c_depth, O_ZERO, - "-exec", N_EXEC, c_exec, O_ARGVP, - "-follow", N_FOLLOW, c_follow, O_ZERO, - "-fstype", N_FSTYPE, c_fstype, O_ARGV, - "-group", N_GROUP, c_group, O_ARGV, - "-inum", N_INUM, c_inum, O_ARGV, - "-links", N_LINKS, c_links, O_ARGV, - "-ls", N_LS, c_ls, O_ZERO, - "-mtime", N_MTIME, c_mtime, O_ARGV, - "-name", N_NAME, c_name, O_ARGV, - "-newer", N_NEWER, c_newer, O_ARGV, - "-nogroup", N_NOGROUP, c_nogroup, O_ZERO, - "-nouser", N_NOUSER, c_nouser, O_ZERO, - "-o", N_OR, c_or, O_ZERO, - "-ok", N_OK, c_exec, O_ARGVP, - "-or", N_OR, c_or, O_ZERO, - "-perm", N_PERM, c_perm, O_ARGV, - "-print", N_PRINT, c_print, O_ZERO, - "-prune", N_PRUNE, c_prune, O_ZERO, - "-size", N_SIZE, c_size, O_ARGV, - "-type", N_TYPE, c_type, O_ARGV, - "-user", N_USER, c_user, O_ARGV, - "-xdev", N_XDEV, c_xdev, O_ZERO, - { NULL }, +/* NB: the following table must be sorted lexically. */ +static OPTION options[] = { + { "!", N_NOT, c_not, O_ZERO }, + { "(", N_OPENPAREN, c_openparen, O_ZERO }, + { ")", N_CLOSEPAREN, c_closeparen, O_ZERO }, + { "-a", N_AND, NULL, O_NONE }, + { "-and", N_AND, NULL, O_NONE }, + { "-atime", N_ATIME, c_atime, O_ARGV }, + { "-ctime", N_CTIME, c_ctime, O_ARGV }, + { "-depth", N_DEPTH, c_depth, O_ZERO }, + { "-exec", N_EXEC, c_exec, O_ARGVP }, + { "-follow", N_FOLLOW, c_follow, O_ZERO }, + { "-fstype", N_FSTYPE, c_fstype, O_ARGV }, + { "-group", N_GROUP, c_group, O_ARGV }, + { "-inum", N_INUM, c_inum, O_ARGV }, + { "-links", N_LINKS, c_links, O_ARGV }, + { "-ls", N_LS, c_ls, O_ZERO }, + { "-mtime", N_MTIME, c_mtime, O_ARGV }, + { "-name", N_NAME, c_name, O_ARGV }, + { "-newer", N_NEWER, c_newer, O_ARGV }, + { "-nogroup", N_NOGROUP, c_nogroup, O_ZERO }, + { "-nouser", N_NOUSER, c_nouser, O_ZERO }, + { "-o", N_OR, c_or, O_ZERO }, + { "-ok", N_OK, c_exec, O_ARGVP }, + { "-or", N_OR, c_or, O_ZERO }, + { "-path", N_PATH, c_path, O_ARGV }, + { "-perm", N_PERM, c_perm, O_ARGV }, + { "-print", N_PRINT, c_print, O_ZERO }, + { "-prune", N_PRUNE, c_prune, O_ZERO }, + { "-size", N_SIZE, c_size, O_ARGV }, + { "-type", N_TYPE, c_type, O_ARGV }, + { "-user", N_USER, c_user, O_ARGV }, + { "-xdev", N_XDEV, c_xdev, O_ZERO }, }; /* @@ -106,20 +99,14 @@ find_create(argvp) register OPTION *p; PLAN *new; char **argv; - OPTION *option(); argv = *argvp; - if ((p = option(*argv)) == NULL) { - (void)fprintf(stderr, "find: unknown option %s.\n", *argv); - exit(1); - } + if ((p = option(*argv)) == NULL) + errx(1, "%s: unknown option", *argv); ++argv; - if (p->flags & (O_ARGV|O_ARGVP) && !*argv) { - (void)fprintf(stderr, - "find: %s requires additional arguments.\n", *--argv); - exit(1); - } + if (p->flags & (O_ARGV|O_ARGVP) && !*argv) + errx(1, "%s: requires additional arguments", *--argv); switch(p->flags) { case O_NONE: @@ -134,9 +121,11 @@ find_create(argvp) case O_ARGVP: new = (p->create)(&argv, p->token == N_OK); break; + default: + abort(); } *argvp = argv; - return(new); + return (new); } OPTION * @@ -147,12 +136,13 @@ option(name) int typecompare __P((const void *, const void *)); tmp.name = name; - return((OPTION *)bsearch(&tmp, options, + return ((OPTION *)bsearch(&tmp, options, sizeof(options)/sizeof(OPTION), sizeof(OPTION), typecompare)); } - + +int typecompare(a, b) const void *a, *b; { - return(strcmp(((OPTION *)a)->name, ((OPTION *)b)->name)); + return (strcmp(((OPTION *)a)->name, ((OPTION *)b)->name)); } diff --git a/usr.bin/fstat/fstat.1 b/usr.bin/fstat/fstat.1 index bd1277fcc6..5486308abe 100644 --- a/usr.bin/fstat/fstat.1 +++ b/usr.bin/fstat/fstat.1 @@ -70,7 +70,7 @@ instead of the default .Pa /dev/kmem . .It Fl N Extract the name list from the specified system instead of the default -.Pa /vmunix . +.Pa /386bsd . .It Fl n Numerical format. Print the device number (maj,min) of the filesystem the file resides in rather than the mount point name; for special diff --git a/usr.bin/fstat/fstat.c b/usr.bin/fstat/fstat.c index 264851d889..d0e48d928f 100644 --- a/usr.bin/fstat/fstat.c +++ b/usr.bin/fstat/fstat.c @@ -48,13 +48,6 @@ static char sccsid[] = "@(#)fstat.c 5.32 (Berkeley) 6/17/91"; #include <sys/time.h> #include <sys/proc.h> #include <sys/user.h> -#ifdef SPPWAIT -#define NEWVM -#endif -#ifndef NEWVM -#include <machine/pte.h> -#include <sys/vmmac.h> -#endif #include <sys/stat.h> #include <sys/vnode.h> #include <sys/socket.h> @@ -112,12 +105,6 @@ struct filestat { dev_t rdev; }; -#ifdef notdef -struct nlist nl[] = { - { "" }, -}; -#endif - int fsflg, /* show files on same filesystem as file(s) argument */ pflg, /* show files open by a particular pid */ uflg; /* show files open by a particular (effective) user */ @@ -129,6 +116,7 @@ int vflg; /* display errors in locating kernel data objects etc... */ struct file **ofiles; /* buffer of pointers to file structures */ int maxfiles; + #define ALLOC_OFILES(d) \ if ((d) > maxfiles) { \ free(ofiles); \ @@ -161,6 +149,7 @@ main(argc, argv) arg = 0; what = KINFO_PROC_ALL; + while ((ch = getopt(argc, argv, "fnp:u:vNM")) != EOF) switch((char)ch) { case 'f': @@ -227,22 +216,19 @@ main(argc, argv) fprintf(stderr, "fstat: %s\n", kvm_geterr()); exit(1); } -#ifdef notdef - if (kvm_nlist(nl) != 0) { - fprintf(stderr, "fstat: no namelist: %s\n", kvm_geterr()); - exit(1); - } -#endif + if (kvm_getprocs(what, arg) == -1) { fprintf(stderr, "fstat: %s\n", kvm_geterr()); exit(1); } + if (nflg) printf("%s", "USER CMD PID FD DEV INUM MODE SZ|DV"); else printf("%s", "USER CMD PID FD MOUNT INUM MODE SZ|DV"); + if (checkfile && fsflg == 0) printf(" NAME\n"); else @@ -253,6 +239,7 @@ main(argc, argv) continue; dofiles(p); } + exit(0); } @@ -287,100 +274,68 @@ dofiles(p) { int i, last; struct file file; -#ifdef NEWVM struct filedesc0 filed0; #define filed filed0.fd_fd struct eproc *ep; -#else - struct filedesc filed; -#endif - extern char *user_from_uid(); -#ifndef NEWVM - struct vnode *xvptr; -#endif -#ifdef NEWVM ep = kvm_geteproc(p); Uname = user_from_uid(ep->e_ucred.cr_uid, 0); -#else - Uname = user_from_uid(p->p_uid, 0); -#endif Pid = p->p_pid; Comm = p->p_comm; if (p->p_fd == NULL) return; -#ifdef NEWVM + if (!KVM_READ(p->p_fd, &filed0, sizeof (filed0))) { dprintf(stderr, "can't read filedesc at %x for pid %d\n", p->p_fd, Pid); return; } -#else - if (!KVM_READ(p->p_fd, &filed, sizeof (filed))) { - dprintf(stderr, "can't read filedesc at %x for pid %d\n", - p->p_fd, Pid); - return; - } -#endif + /* * root directory vnode, if one */ if (filed.fd_rdir) vtrans(filed.fd_rdir, RDIR); -#ifndef NEWVM - /* - * text vnode - */ - if (p->p_textp && - KVM_READ(&(p->p_textp->x_vptr), &xvptr, sizeof (struct vnode *)) && - xvptr != NULL) - vtrans(xvptr, TEXT); -#endif + /* * current working directory vnode */ vtrans(filed.fd_cdir, CDIR); + /* * ktrace vnode, if one */ if (p->p_tracep) vtrans(p->p_tracep, TRACE); + /* * open files */ #define FPSIZE (sizeof (struct file *)) - ALLOC_OFILES(filed.fd_lastfile); -#ifdef NEWVM + ALLOC_OFILES(filed.fd_lastfile + 1); if (filed.fd_nfiles > NDFILE) { if (!KVM_READ(filed.fd_ofiles, ofiles, - filed.fd_lastfile * FPSIZE)) { + (filed.fd_lastfile + 1) * FPSIZE)) { dprintf(stderr, "can't read file structures at %x for pid %d\n", filed.fd_ofiles, Pid); return; } } else - bcopy(filed0.fd_dfiles, ofiles, filed.fd_lastfile * FPSIZE); -#else - bcopy(filed.fd_ofile, ofiles, MIN(filed.fd_lastfile, NDFILE) * FPSIZE); - last = filed.fd_lastfile; - if ((last > NDFILE) && !KVM_READ(filed.fd_moreofiles, &ofiles[NDFILE], - (last - NDFILE) * FPSIZE)) { - dprintf(stderr, "can't read rest of files at %x for pid %d\n", - filed.fd_moreofiles, Pid); - return; - } -#endif + bcopy(filed0.fd_dfiles, ofiles, (filed.fd_lastfile + 1) * FPSIZE); + for (i = 0; i <= filed.fd_lastfile; i++) { if (ofiles[i] == NULL) continue; + if (!KVM_READ(ofiles[i], &file, sizeof (struct file))) { dprintf(stderr, "can't read file %d at %x for pid %d\n", i, ofiles[i], Pid); continue; } + if (file.f_type == DTYPE_VNODE) vtrans((struct vnode *)file.f_data, i); else if (file.f_type == DTYPE_SOCKET && checkfile == 0) @@ -405,11 +360,13 @@ vtrans(vp, i) char *badtype, *filename, *getmnton(); filename = badtype = NULL; + if (!KVM_READ(vp, &vn, sizeof (struct vnode))) { dprintf(stderr, "can't read vnode at %x for pid %d\n", vp, Pid); return; } + if (vn.v_type == VNON || vn.v_tag == VT_NON) badtype = "none"; else if (vn.v_type == VBAD) @@ -431,13 +388,15 @@ vtrans(vp, i) break;; } } + if (checkfile) { int fsmatch = 0; register DEVS *d; if (badtype) return; - for (d = devs; d != NULL; d = d->next) + + for (d = devs; d != NULL; d = d->next) { if (d->fsid == fst.fsid) { fsmatch = 1; if (d->ino == fst.fileid) { @@ -445,14 +404,19 @@ vtrans(vp, i) break; } } + } + if (fsmatch == 0 || (filename == NULL && fsflg == 0)) return; } + PREFIX(i); + if (badtype) { (void)printf(" - - %10s -\n", badtype); return; } + if (nflg) (void)printf(" %2d,%-2d", major(fst.fsid), minor(fst.fsid)); else @@ -461,7 +425,9 @@ vtrans(vp, i) (void)sprintf(mode, "%o", fst.mode); else strmode(fst.mode, mode); + (void)printf(" %6d %10s", fst.fileid, mode); + switch (vn.v_type) { case VBLK: case VCHR: { @@ -477,6 +443,7 @@ vtrans(vp, i) default: printf(" %6d", fst.size); } + if (filename && !fsflg) printf(" %s", filename); @@ -510,6 +477,7 @@ nfs_filestat(vp, fsp) fsp->size = np->n_size; fsp->rdev = np->n_vattr.va_rdev; mode = (mode_t)np->n_vattr.va_mode; + switch (vp->v_type) { case VREG: mode |= S_IFREG; @@ -533,6 +501,7 @@ nfs_filestat(vp, fsp) mode |= S_IFIFO; break; }; + fsp->mode = mode; } @@ -552,18 +521,22 @@ getmnton(m) for (mt = mhead; mt != NULL; mt = mt->next) if (m == mt->m) return (mt->mntonname); + if (!KVM_READ(m, &mount, sizeof(struct mount))) { fprintf(stderr, "can't read mount table at %x\n", m); return (NULL); } + if ((mt = malloc(sizeof (struct mtab))) == NULL) { fprintf(stderr, "fstat: %s\n", strerror(errno)); exit(1); } + mt->m = m; bcopy(&mount.mnt_stat.f_mntonname[0], &mt->mntonname[0], MNAMELEN); mt->next = mhead; mhead = mt; + return (mt->mntonname); } @@ -721,6 +694,7 @@ getinetproto(number) printf(" %d", number); return; } + printf(" %s", cp); } @@ -735,10 +709,12 @@ getfname(filename) filename); return(0); } + if ((cur = malloc(sizeof(DEVS))) == NULL) { fprintf(stderr, "fstat: %s\n", strerror(errno)); exit(1); } + cur->next = devs; devs = cur; diff --git a/usr.bin/ftp/ftp.c b/usr.bin/ftp/ftp.c index a63c98636a..ce6128f787 100644 --- a/usr.bin/ftp/ftp.c +++ b/usr.bin/ftp/ftp.c @@ -1101,7 +1101,7 @@ ptransfer(direction, bytes, t0, t1) s = td.tv_sec + (td.tv_usec / 1000000.); #define nz(x) ((x) == 0 ? 1 : (x)) bs = bytes / nz(s); - printf("%ld bytes %s in %.2g seconds (%.2g Kbytes/s)\n", + printf("%ld bytes %s in %.3g seconds (%.3g Kbytes/s)\n", bytes, direction, s, bs / 1024.); } } diff --git a/usr.bin/getopt/Makefile b/usr.bin/getopt/Makefile new file mode 100644 index 0000000000..21dde959af --- /dev/null +++ b/usr.bin/getopt/Makefile @@ -0,0 +1,7 @@ +# $Header: /b/source/CVS/src/usr.bin/getopt/Makefile,v 1.1 1993/06/21 12:43:58 brezak Exp $ +# + +PROG = getopt +MAN1 = getopt.1 + +.include <bsd.prog.mk> diff --git a/usr.bin/getopt/README b/usr.bin/getopt/README new file mode 100644 index 0000000000..55e6998729 --- /dev/null +++ b/usr.bin/getopt/README @@ -0,0 +1,57 @@ +/***** unido:mod.std.unix / ut-sally!jsq / 8:54 pm Jul 4, 1985*/ +From: John Quarterman (moderator) <ut-sally!std-unix> + +Topic: yet more on getopt (command line arguments) + +Two more messages, the first a followup to a previous posting, and +the second public domain sources and man pages for getopt(3) and getopt(1). + -mod + +---------------------------------------------------------------------- + +From: ihnp4!utzoo!henry +Date: 3 Jul 85 18:34:41 CDT (Wed) +To: ihnp4!ut-sally!std-unix +Subject: Re: command line arguments + +> > A group of bundled options may end with an option that has an argument. +> +> This creates confusion in using C-Kermit when you want to send an image +> file. For example: +> +> send -is filename < --- works fine +> send -si filename < --- bombs the program + +The AT&T syntax standard (which getopt does not completely enforce) +actually forbids both of these usages. Options with arguments are not +allowed to be bundled, and they must be separated from their arguments +by a space. + +> I would *much* prefer to bundle the flags, then +> have those with arguments pick them up in the same order as the flags are +> listed. + +The few existing commands that use such a convention, notably tar(1), are +(in my experience) the worse for it. It's seriously error-prone. I think +the AT&T people did the right thing. + +------------------------------ + +Date: Tue, 2 Jul 85 13:07:09 edt +From: ihnp4!utcs!ian (Ian F. Darwin) +To: ihnp4!ut-sally!jsq@tzec.UTEXAS.ARPA +Subject: here is getopt + +Here is the source for getopt(3), the function that should be in +everybody's C program, and getopt(1), a program that uses it to +make shell programs comprehensible and consistent. There are man +pages for both. Please send these on to the mod. group. Thanks. + +[ I have hacked the following shell script slightly so that +it doesn't extract directly into system source directories, +rather into the current directory. It should be assumed that +this code comes with no warranty from me, Ian Darwin, or anyone +else as to whether it accurately represents getopt as distributed +with System V, or any command line standard, or that it works +at all, or that it will cause no damage when extracted or used. -mod] + diff --git a/usr.bin/getopt/getopt.1 b/usr.bin/getopt/getopt.1 new file mode 100644 index 0000000000..16a50f090a --- /dev/null +++ b/usr.bin/getopt/getopt.1 @@ -0,0 +1,103 @@ +.Dd June 21, 1993 +.Dt GETOPT 1 +.Os +.Sh NAME +.Nm getopt +.Nd parse command options +.Sh SYNOPSIS +.Nm set \-\- \`getopt optstring $*\` +.Sh DESCRIPTION +.Nm Getopt +is used to break up options in command lines for easy parsing by +shell procedures, and to check for legal options. +.Op Optstring +is a string of recognized option letters (see +.Xr getopt 3 +); +if a letter is followed by a colon, the option +is expected to have an argument which may or may not be +separated from it by white space. +The special option +.B \-\- +is used to delimit the end of the options. +.Nm Getopt +will place +.B \-\- +in the arguments at the end of the options, +or recognize it if used explicitly. +The shell arguments +(\fB$1 $2\fR ...) are reset so that each option is +preceded by a +.B \- +and in its own shell argument; +each option argument is also in its own shell argument. +.Sh EXAMPLE +The following code fragment shows how one might process the arguments +for a command that can take the options +.Op a +and +.Op b , +and the option +.Op o , +which requires an argument. +.Pp +.Bd -literal -offset indent +set \-\- \`getopt abo: $*\` +if test $? != 0 +then + echo 'Usage: ...' + exit 2 +fi +for i +do + case "$i" + in + \-a|\-b) + flag=$i; shift;; + \-o) + oarg=$2; shift; shift;; + \-\-) + shift; break;; + esac +done +.Ed +.Pp +This code will accept any of the following as equivalent: +.Pp +.Bd -literal -offset indent +cmd \-aoarg file file +cmd \-a \-o arg file file +cmd \-oarg -a file file +cmd \-a \-oarg \-\- file file +.Ed +.Sh SEE ALSO +.Xr sh 1 , +.Xr getopt 3 +.Sh DIAGNOSTICS +.Nm Getopt +prints an error message on the standard error output when it +encounters an option letter not included in +.Op optstring . +.Sh HISTORY +Written by Henry Spencer, working from a Bell Labs manual page. +Behavior believed identical to the Bell version. +.Sh BUGS +Whatever +.Xr getopt 3 +has. +.Pp +Arguments containing white space or imbedded shell metacharacters +generally will not survive intact; this looks easy to fix but isn't. +.Pp +The error message for an invalid option is identified as coming +from +.Nm getopt +rather than from the shell procedure containing the invocation +of +.Nm getopt ; +this again is hard to fix. +.Pp +The precise best way to use the +.Nm set +command to set the arguments without disrupting the value(s) of +shell options varies from one shell version to another. diff --git a/usr.bin/getopt/getopt.c b/usr.bin/getopt/getopt.c new file mode 100644 index 0000000000..03b0987ef1 --- /dev/null +++ b/usr.bin/getopt/getopt.c @@ -0,0 +1,30 @@ +#include <stdio.h> + +main(argc, argv) +int argc; +char *argv[]; +{ + extern int optind; + extern char *optarg; + int c; + int status = 0; + + optind = 2; /* Past the program name and the option letters. */ + while ((c = getopt(argc, argv, argv[1])) != EOF) + switch (c) { + case '?': + status = 1; /* getopt routine gave message */ + break; + default: + if (optarg != NULL) + printf(" -%c %s", c, optarg); + else + printf(" -%c", c); + break; + } + printf(" --"); + for (; optind < argc; optind++) + printf(" %s", argv[optind]); + printf("\n"); + exit(status); +} diff --git a/usr.bin/groups/groups.1 b/usr.bin/groups/groups.1 index bafa34cf52..2311aa64c2 100644 --- a/usr.bin/groups/groups.1 +++ b/usr.bin/groups/groups.1 @@ -64,7 +64,7 @@ the group of the containing directory. .El .Sh HISTORY The -.Nm group +.Nm groups command appeared in .Bx 4.2 . diff --git a/usr.bin/head/head.1 b/usr.bin/head/head.1 index a9b28b342d..27e32bc46a 100644 --- a/usr.bin/head/head.1 +++ b/usr.bin/head/head.1 @@ -31,15 +31,15 @@ .\" .\" @(#)head.1 6.6 (Berkeley) 7/24/91 .\" -.Dd July 24, 1991 +.Dd July 14, 1993 .Dt HEAD 1 -.Os BSD 3 +.Os .Sh NAME .Nm head .Nd give first few lines .Sh SYNOPSIS .Nm head -.Op Fl Ns Ar count +.Op Fl n Ar count .Op Ar .Sh DESCRIPTION This filter gives the first @@ -51,6 +51,16 @@ is omitted it defaults to 10. .Sh SEE ALSO .Xr tail 1 +.Sh STANDARDS +The +.Nm head +command is expected to be +.St -p1003.2 +compliant. +.Pp +The historic command line syntax of +.Nm head +is supported by this implementation. .Sh HISTORY The .Nm head diff --git a/usr.bin/head/head.c b/usr.bin/head/head.c index cbedc34db2..2956fdb5e7 100644 --- a/usr.bin/head/head.c +++ b/usr.bin/head/head.c @@ -42,7 +42,11 @@ static char sccsid[] = "@(#)head.c 5.5 (Berkeley) 6/1/90"; #endif /* not lint */ #include <stdio.h> +#include <stdlib.h> #include <ctype.h> + +static void usage (); + /* * head - give the first few lines of a stream or of each of a set of files * @@ -56,19 +60,28 @@ main(argc, argv) register int ch, cnt; int firsttime, linecnt = 10; - if (argc > 1 && argv[1][0] == '-') { - if (!isdigit(argv[1][1])) { - fprintf(stderr, "head: illegal option -- %c\n", argv[1][1]); - goto usage; - } + /* handle obsolete -number syntax */ + if (argc > 1 && argv[1][0] == '-' && isdigit(argv[1][1])) { if ((linecnt = atoi(argv[1] + 1)) < 0) { -usage: fputs("usage: head [-line_count] [file ...]\n", stderr); - exit(1); + usage (); } - --argc; ++argv; + argc--; argv++; } + + while ((ch = getopt (argc, argv, "n:")) != EOF) + switch (ch) { + case 'n': + if ((linecnt = atoi(optarg)) < 0) + usage (); + break; + + default: + usage(); + } + argc -= optind, argv += optind; + /* setlinebuf(stdout); */ - for (firsttime = 1, --argc, ++argv;; firsttime = 0) { + for (firsttime = 1; ; firsttime = 0) { if (!*argv) { if (!firsttime) exit(0); @@ -92,3 +105,12 @@ usage: fputs("usage: head [-line_count] [file ...]\n", stderr); } /*NOTREACHED*/ } + + +static void +usage () +{ + fputs("usage: head [-n line_count] [file ...]\n", stderr); + exit(1); +} + diff --git a/usr.bin/hexdump/Makefile b/usr.bin/hexdump/Makefile index 7dda07ded9..b8e7982027 100644 --- a/usr.bin/hexdump/Makefile +++ b/usr.bin/hexdump/Makefile @@ -2,7 +2,7 @@ PROG= hexdump SRCS= conv.c display.c hexdump.c hexsyntax.c odsyntax.c parse.c -MAN1= hexdump.0 od.0 +MAN1= hexdump.1 od.1 LINKS= ${BINDIR}/hexdump ${BINDIR}/od .include <bsd.prog.mk> diff --git a/usr.bin/join/COPYING b/usr.bin/join/COPYING deleted file mode 100644 index 9a17037581..0000000000 --- a/usr.bin/join/COPYING +++ /dev/null @@ -1,249 +0,0 @@ - - GNU GENERAL PUBLIC LICENSE - Version 1, February 1989 - - Copyright (C) 1989 Free Software Foundation, Inc. - 675 Mass Ave, Cambridge, MA 02139, USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The license agreements of most software companies try to keep users -at the mercy of those companies. By contrast, our General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. The -General Public License applies to the Free Software Foundation's -software and to any other program whose authors commit to using it. -You can use it for your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Specifically, the General Public License is designed to make -sure that you have the freedom to give away or sell copies of free -software, that you receive source code or can get it if you want it, -that you can change the software or use pieces of it in new free -programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of a such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must tell them their rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any program or other work which -contains a notice placed by the copyright holder saying it may be -distributed under the terms of this General Public License. The -"Program", below, refers to any such program or work, and a "work based -on the Program" means either the Program or any work containing the -Program or a portion of it, either verbatim or with modifications. Each -licensee is addressed as "you". - - 1. You may copy and distribute verbatim copies of the Program's source -code as you receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice and -disclaimer of warranty; keep intact all the notices that refer to this -General Public License and to the absence of any warranty; and give any -other recipients of the Program a copy of this General Public License -along with the Program. You may charge a fee for the physical act of -transferring a copy. - - 2. You may modify your copy or copies of the Program or any portion of -it, and copy and distribute such modifications under the terms of Paragraph -1 above, provided that you also do the following: - - a) cause the modified files to carry prominent notices stating that - you changed the files and the date of any change; and - - b) cause the whole of any work that you distribute or publish, that - in whole or in part contains the Program or any part thereof, either - with or without modifications, to be licensed at no charge to all - third parties under the terms of this General Public License (except - that you may choose to grant warranty protection to some or all - third parties, at your option). - - c) If the modified program normally reads commands interactively when - run, you must cause it, when started running for such interactive use - in the simplest and most usual way, to print or display an - announcement including an appropriate copyright notice and a notice - that there is no warranty (or else, saying that you provide a - warranty) and that users may redistribute the program under these - conditions, and telling the user how to view a copy of this General - Public License. - - d) You may charge a fee for the physical act of transferring a - copy, and you may at your option offer warranty protection in - exchange for a fee. - -Mere aggregation of another independent work with the Program (or its -derivative) on a volume of a storage or distribution medium does not bring -the other work under the scope of these terms. - - 3. You may copy and distribute the Program (or a portion or derivative of -it, under Paragraph 2) in object code or executable form under the terms of -Paragraphs 1 and 2 above provided that you also do one of the following: - - a) accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of - Paragraphs 1 and 2 above; or, - - b) accompany it with a written offer, valid for at least three - years, to give any third party free (except for a nominal charge - for the cost of distribution) a complete machine-readable copy of the - corresponding source code, to be distributed under the terms of - Paragraphs 1 and 2 above; or, - - c) accompany it with the information you received as to where the - corresponding source code may be obtained. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form alone.) - -Source code for a work means the preferred form of the work for making -modifications to it. For an executable file, complete source code means -all the source code for all modules it contains; but, as a special -exception, it need not include source code for modules which are standard -libraries that accompany the operating system on which the executable -file runs, or for standard header files or definitions files that -accompany that operating system. - - 4. You may not copy, modify, sublicense, distribute or transfer the -Program except as expressly provided under this General Public License. -Any attempt otherwise to copy, modify, sublicense, distribute or transfer -the Program is void, and will automatically terminate your rights to use -the Program under this License. However, parties who have received -copies, or rights to use copies, from you under this General Public -License will not have their licenses terminated so long as such parties -remain in full compliance. - - 5. By copying, distributing or modifying the Program (or any work based -on the Program) you indicate your acceptance of this license to do so, -and all its terms and conditions. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the original -licensor to copy, distribute or modify the Program subject to these -terms and conditions. You may not impose any further restrictions on the -recipients' exercise of the rights granted herein. - - 7. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of the license which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -the license, you may choose any version ever published by the Free Software -Foundation. - - 8. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - Appendix: How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to humanity, the best way to achieve this is to make it -free software which everyone can redistribute and change under these -terms. - - To do so, attach the following notices to the program. It is safest to -attach them to the start of each source file to most effectively convey -the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - <one line to give the program's name and a brief idea of what it does.> - Copyright (C) 19yy <name of author> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19xx name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the -appropriate parts of the General Public License. Of course, the -commands you use may be called something other than `show w' and `show -c'; they could even be mouse-clicks or menu items--whatever suits your -program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - program `Gnomovision' (a program to direct compilers to make passes - at assemblers) written by James Hacker. - - <signature of Ty Coon>, 1 April 1989 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/usr.bin/join/Makefile b/usr.bin/join/Makefile index afe7b7b675..b607b183e8 100644 --- a/usr.bin/join/Makefile +++ b/usr.bin/join/Makefile @@ -1,5 +1,5 @@ +# @(#)Makefile 5.2 (Berkeley) 5/11/90 PROG= join -SRCS= join.c getopt.c getopt1.c .include <bsd.prog.mk> diff --git a/usr.bin/join/getopt.c b/usr.bin/join/getopt.c deleted file mode 100644 index d7677b4163..0000000000 --- a/usr.bin/join/getopt.c +++ /dev/null @@ -1,595 +0,0 @@ -/* Getopt for GNU. - Copyright (C) 1987, 1989, 1990 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifdef __STDC__ -#define CONST const -#else -#define CONST -#endif - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of `argv' so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. - - Setting the environment variable _POSIX_OPTION_ORDER disables permutation. - Then the behavior is completely standard. - - GNU application programs can use a third alternative mode in which - they can distinguish the relative order of options and other arguments. */ - -#include <stdio.h> - -/* If compiled with GNU C, use the built-in alloca */ -#ifdef __GNUC__ -#define alloca __builtin_alloca -#else /* not __GNUC__ */ -#ifdef sparc -#include <alloca.h> -#else -char *alloca (); -#endif -#endif /* not __GNUC__ */ - -#if defined(STDC_HEADERS) || defined(__GNU_LIBRARY__) -#include <stdlib.h> -#include <string.h> -#define bcopy(s, d, n) memcpy ((d), (s), (n)) -#define index strchr -#else - -#ifdef USG -#include <string.h> -#define bcopy(s, d, n) memcpy ((d), (s), (n)) -#define index strchr -#else -#ifdef VMS -#include <string.h> -#else -#include <strings.h> -#endif -void bcopy (); -#endif - -char *getenv (); -char *malloc (); -#endif - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -char *optarg = 0; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns EOF, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -int optind = 0; - -/* The next char to be scanned in the option-element - in which the last option character we returned was found. - This allows us to pick up the scan where we left off. - - If this is zero, or a null string, it means resume the scan - by advancing to the next ARGV-element. */ - -static char *nextchar; - -/* Callers store zero here to inhibit the error message - for unrecognized options. */ - -int opterr = 1; - -/* Describe how to deal with options that follow non-option ARGV-elements. - - If the caller did not specify anything, - the default is REQUIRE_ORDER if the environment variable - _POSIX_OPTION_ORDER is defined, PERMUTE otherwise. - - REQUIRE_ORDER means don't recognize them as options; - stop option processing when the first non-option is seen. - This is what Unix does. - This mode of operation is selected by either setting the environment - variable _POSIX_OPTION_ORDER, or using `+' as the first character - of the list of option characters. - - PERMUTE is the default. We permute the contents of ARGV as we scan, - so that eventually all the non-options are at the end. This allows options - to be given in any order, even with programs that were not written to - expect this. - - RETURN_IN_ORDER is an option available to programs that were written - to expect options and other ARGV-elements in any order and that care about - the ordering of the two. We describe each non-option ARGV-element - as if it were the argument of an option with character code 1. - Using `-' as the first character of the list of option characters - selects this mode of operation. - - The special argument `--' forces an end of option-scanning regardless - of the value of `ordering'. In the case of RETURN_IN_ORDER, only - `--' can cause `getopt' to return EOF with `optind' != ARGC. */ - -static enum -{ - REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER -} ordering; - -/* Describe the long-named options requested by the application. - _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an - element containing a name which is zero. - The field `has_arg' is 1 if the option takes an argument, - 2 if it takes an optional argument. */ - -struct option -{ - char *name; - int has_arg; - int *flag; - int val; -}; - -CONST struct option *_getopt_long_options; - -int _getopt_long_only = 0; - -/* Index in _GETOPT_LONG_OPTIONS of the long-named option actually found. - Only valid when a long-named option was found. */ - -int option_index; - -/* Handle permutation of arguments. */ - -/* Describe the part of ARGV that contains non-options that have - been skipped. `first_nonopt' is the index in ARGV of the first of them; - `last_nonopt' is the index after the last of them. */ - -static int first_nonopt; -static int last_nonopt; - -/* Exchange two adjacent subsequences of ARGV. - One subsequence is elements [first_nonopt,last_nonopt) - which contains all the non-options that have been skipped so far. - The other is elements [last_nonopt,optind), which contains all - the options processed since those non-options were skipped. - - `first_nonopt' and `last_nonopt' are relocated so that they describe - the new indices of the non-options in ARGV after they are moved. */ - -static void -exchange (argv) - char **argv; -{ - int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); - char **temp = (char **) alloca (nonopts_size); - - /* Interchange the two blocks of data in ARGV. */ - - bcopy (&argv[first_nonopt], temp, nonopts_size); - bcopy (&argv[last_nonopt], &argv[first_nonopt], - (optind - last_nonopt) * sizeof (char *)); - bcopy (temp, &argv[first_nonopt + optind - last_nonopt], nonopts_size); - - /* Update records for the slots the non-options now occupy. */ - - first_nonopt += (optind - last_nonopt); - last_nonopt = optind; -} - -/* Scan elements of ARGV (whose length is ARGC) for option characters - given in OPTSTRING. - - If an element of ARGV starts with '-', and is not exactly "-" or "--", - then it is an option element. The characters of this element - (aside from the initial '-') are option characters. If `getopt' - is called repeatedly, it returns successively each of the option characters - from each of the option elements. - - If `getopt' finds another option character, it returns that character, - updating `optind' and `nextchar' so that the next call to `getopt' can - resume the scan with the following option character or ARGV-element. - - If there are no more option characters, `getopt' returns `EOF'. - Then `optind' is the index in ARGV of the first ARGV-element - that is not an option. (The ARGV-elements have been permuted - so that those that are not options now come last.) - - OPTSTRING is a string containing the legitimate option characters. - If an option character is seen that is not listed in OPTSTRING, - return '?' after printing an error message. If you set `opterr' to - zero, the error message is suppressed but we still return '?'. - - If a char in OPTSTRING is followed by a colon, that means it wants an arg, - so the following text in the same ARGV-element, or the text of the following - ARGV-element, is returned in `optarg'. Two colons mean an option that - wants an optional arg; if there is text in the current ARGV-element, - it is returned in `optarg', otherwise `optarg' is set to zero. - - If OPTSTRING starts with `-' or `+', it requests different methods of - handling the non-option ARGV-elements. - See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - - Long-named options begin with `+' instead of `-'. - Their names may be abbreviated as long as the abbreviation is unique - or is an exact match for some defined option. If they have an - argument, it follows the option name in the same ARGV-element, separated - from the option name by a `=', or else the in next ARGV-element. - When `getopt' finds a long-named option, it returns 0 if that option's - `flag' field is nonzero, the value of the option's `val' field - otherwise. */ - -int -getopt (argc, argv, optstring) - int argc; - char **argv; - CONST char *optstring; -{ - optarg = 0; - - /* Initialize the internal data when the first call is made. - Start processing options with ARGV-element 1 (since ARGV-element 0 - is the program name); the sequence of previously skipped - non-option ARGV-elements is empty. */ - - if (optind == 0) - { - first_nonopt = last_nonopt = optind = 1; - - nextchar = 0; - - /* Determine how to handle the ordering of options and nonoptions. */ - - if (optstring[0] == '-') - { - ordering = RETURN_IN_ORDER; - ++optstring; - } - else if (optstring[0] == '+') - { - ordering = REQUIRE_ORDER; - ++optstring; - } - else if (getenv ("_POSIX_OPTION_ORDER") != 0) - ordering = REQUIRE_ORDER; - else - ordering = PERMUTE; - } - - if (nextchar == 0 || *nextchar == 0) - { - if (ordering == PERMUTE) - { - /* If we have just processed some options following some non-options, - exchange them so that the options come first. */ - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange (argv); - else if (last_nonopt != optind) - first_nonopt = optind; - - /* Now skip any additional non-options - and extend the range of non-options previously skipped. */ - - while (optind < argc - && (argv[optind][0] != '-' - || argv[optind][1] == 0) - && (_getopt_long_options == 0 - || argv[optind][0] != '+' - || argv[optind][1] == 0)) - optind++; - last_nonopt = optind; - } - - /* Special ARGV-element `--' means premature end of options. - Skip it like a null option, - then exchange with previous non-options as if it were an option, - then skip everything else like a non-option. */ - - if (optind != argc && !strcmp (argv[optind], "--")) - { - optind++; - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange (argv); - else if (first_nonopt == last_nonopt) - first_nonopt = optind; - last_nonopt = argc; - - optind = argc; - } - - /* If we have done all the ARGV-elements, stop the scan - and back over any non-options that we skipped and permuted. */ - - if (optind == argc) - { - /* Set the next-arg-index to point at the non-options - that we previously skipped, so the caller will digest them. */ - if (first_nonopt != last_nonopt) - optind = first_nonopt; - return EOF; - } - - /* If we have come to a non-option and did not permute it, - either stop the scan or describe it to the caller and pass it by. */ - - if ((argv[optind][0] != '-' || argv[optind][1] == 0) - && (_getopt_long_options == 0 - || argv[optind][0] != '+' || argv[optind][1] == 0)) - { - if (ordering == REQUIRE_ORDER) - return EOF; - optarg = argv[optind++]; - return 1; - } - - /* We have found another option-ARGV-element. - Start decoding its characters. */ - - nextchar = argv[optind] + 1; - } - - if (_getopt_long_options != 0 - && (argv[optind][0] == '+' - || (_getopt_long_only && argv[optind][0] == '-')) - ) - { - CONST struct option *p; - char *s = nextchar; - int exact = 0; - int ambig = 0; - CONST struct option *pfound = 0; - int indfound; - - while (*s && *s != '=') - s++; - - /* Test all options for either exact match or abbreviated matches. */ - for (p = _getopt_long_options, option_index = 0; p->name; - p++, option_index++) - if (!strncmp (p->name, nextchar, s - nextchar)) - { - if (s - nextchar == strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == 0) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else - /* Second nonexact match found. */ - ambig = 1; - } - - if (ambig && !exact) - { - fprintf (stderr, "%s: option `%s' is ambiguous\n", - argv[0], argv[optind]); - nextchar += strlen (nextchar); - optind++; - return '?'; - } - - if (pfound != 0) - { - option_index = indfound; - optind++; - if (*s) - { - if (pfound->has_arg > 0) - optarg = s + 1; - else - { - fprintf (stderr, - "%s: option `%c%s' doesn't allow an argument\n", - argv[0], argv[optind - 1][0], pfound->name); - nextchar += strlen (nextchar); - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (optind < argc) - optarg = argv[optind++]; - else - { - fprintf (stderr, "%s: option `%s' requires an argument\n", - argv[0], argv[optind - 1]); - nextchar += strlen (nextchar); - return '?'; - } - } - nextchar += strlen (nextchar); - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - /* Can't find it as a long option. If this is getopt_long_only, - and the option starts with '-' and is a valid short - option, then interpret it as a short option. Otherwise it's - an error. */ - if (_getopt_long_only == 0 || argv[optind][0] == '+' || - index (optstring, *nextchar) == 0) - { - if (opterr != 0) - fprintf (stderr, "%s: unrecognized option `%c%s'\n", - argv[0], argv[optind][0], nextchar); - nextchar += strlen (nextchar); - optind++; - return '?'; - } - } - - /* Look at and handle the next option-character. */ - - { - char c = *nextchar++; - char *temp = index (optstring, c); - - /* Increment `optind' when we start to process its last character. */ - if (*nextchar == 0) - optind++; - - if (temp == 0 || c == ':') - { - if (opterr != 0) - { - if (c < 040 || c >= 0177) - fprintf (stderr, "%s: unrecognized option, character code 0%o\n", - argv[0], c); - else - fprintf (stderr, "%s: unrecognized option `-%c'\n", - argv[0], c); - } - return '?'; - } - if (temp[1] == ':') - { - if (temp[2] == ':') - { - /* This is an option that accepts an argument optionally. */ - if (*nextchar != 0) - { - optarg = nextchar; - optind++; - } - else - optarg = 0; - nextchar = 0; - } - else - { - /* This is an option that requires an argument. */ - if (*nextchar != 0) - { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - optind++; - } - else if (optind == argc) - { - if (opterr != 0) - fprintf (stderr, "%s: option `-%c' requires an argument\n", - argv[0], c); - c = '?'; - } - else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - optarg = argv[optind++]; - nextchar = 0; - } - } - return c; - } -} - -#ifdef TEST - -/* Compile with -DTEST to make an executable for use in testing - the above definition of `getopt'. */ - -int -main (argc, argv) - int argc; - char **argv; -{ - int c; - int digit_optind = 0; - - while (1) - { - int this_option_optind = optind ? optind : 1; - - c = getopt (argc, argv, "abc:d:0123456789"); - if (c == EOF) - break; - - switch (c) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 && digit_optind != this_option_optind) - printf ("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf ("option %c\n", c); - break; - - case 'a': - printf ("option a\n"); - break; - - case 'b': - printf ("option b\n"); - break; - - case 'c': - printf ("option c with value `%s'\n", optarg); - break; - - case '?': - break; - - default: - printf ("?? getopt returned character code 0%o ??\n", c); - } - } - - if (optind < argc) - { - printf ("non-option ARGV-elements: "); - while (optind < argc) - printf ("%s ", argv[optind++]); - printf ("\n"); - } - - exit (0); -} - -#endif /* TEST */ diff --git a/usr.bin/join/getopt.h b/usr.bin/join/getopt.h deleted file mode 100644 index 661acb9273..0000000000 --- a/usr.bin/join/getopt.h +++ /dev/null @@ -1,102 +0,0 @@ -/* declarations for getopt - Copyright (C) 1989, 1990 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -extern char *optarg; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns EOF, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -extern int optind; - -/* Callers store zero here to inhibit the error message `getopt' prints - for unrecognized options. */ - -extern int opterr; - -/* Describe the long-named options requested by the application. - _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an - element containing a name which is zero. - - The field `has_arg' is: - 0 if the option does not take an argument, - 1 if the option requires an argument, - 2 if the option takes an optional argument. - - If the field `flag' is nonzero, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. - - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ - -struct option -{ - char *name; - int has_arg; - int *flag; - int val; -}; - -#ifdef __STDC__ -extern const struct option *_getopt_long_options; -#else -extern struct option *_getopt_long_options; -#endif - -/* If nonzero, '-' can introduce long-named options. - Set by getopt_long_only. */ - -extern int _getopt_long_only; - -/* The index in GETOPT_LONG_OPTIONS of the long-named option found. - Only valid when a long-named option has been found by the most - recent call to `getopt'. */ - -extern int option_index; - -#ifdef __STDC__ -int getopt (int argc, char **argv, const char *shortopts); -int getopt_long (int argc, char **argv, const char *shortopts, - const struct option *longopts, int *longind); -int getopt_long_only (int argc, char **argv, const char *shortopts, - const struct option *longopts, int *longind); -void envopt(int *pargc, char ***pargv, char *optstr); -#else -int getopt (); -int getopt_long (); -int getopt_long_only (); -void envopt(); -#endif diff --git a/usr.bin/join/getopt1.c b/usr.bin/join/getopt1.c deleted file mode 100644 index bb73d86e31..0000000000 --- a/usr.bin/join/getopt1.c +++ /dev/null @@ -1,160 +0,0 @@ -/* Getopt for GNU. - Copyright (C) 1987, 1989 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include "getopt.h" - -#ifdef __STDC__ -#define CONST const -#else -#define CONST -#endif - -#if !defined (NULL) -#define NULL 0 -#endif - -int -getopt_long (argc, argv, options, long_options, opt_index) - int argc; - char **argv; - CONST char *options; - CONST struct option *long_options; - int *opt_index; -{ - int val; - - _getopt_long_options = long_options; - val = getopt (argc, argv, options); - if (val == 0 && opt_index != NULL) - *opt_index = option_index; - return val; -} - -/* Like getopt_long, but '-' as well as '+' can indicate a long option. - If an option that starts with '-' doesn't match a long option, - but does match a short option, it is parsed as a short option - instead. */ - -int -getopt_long_only (argc, argv, options, long_options, opt_index) - int argc; - char **argv; - CONST char *options; - CONST struct option *long_options; - int *opt_index; -{ - int val; - - _getopt_long_options = long_options; - _getopt_long_only = 1; - val = getopt (argc, argv, options); - if (val == 0 && opt_index != NULL) - *opt_index = option_index; - return val; -} - - -#ifdef TEST - -#include <stdio.h> - -int -main (argc, argv) - int argc; - char **argv; -{ - int c; - int digit_optind = 0; - - while (1) - { - int this_option_optind = optind ? optind : 1; - char *name = '\0'; - int option_index = 0; - static struct option long_options[] = - { - {"add", 1, 0, 0}, - {"append", 0, 0, 0}, - {"delete", 1, 0, 0}, - {"verbose", 0, 0, 0}, - {"create", 0, 0, 0}, - {"file", 1, 0, 0}, - {0, 0, 0, 0} - }; - - c = getopt_long (argc, argv, "abc:d:0123456789", - long_options, &option_index); - if (c == EOF) - break; - - switch (c) - { - case 0: - printf ("option %s", (long_options[option_index]).name); - if (optarg) - printf (" with arg %s", optarg); - printf ("\n"); - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 && digit_optind != this_option_optind) - printf ("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf ("option %c\n", c); - break; - - case 'a': - printf ("option a\n"); - break; - - case 'b': - printf ("option b\n"); - break; - - case 'c': - printf ("option c with value `%s'\n", optarg); - break; - - case '?': - break; - - default: - printf ("?? getopt returned character code 0%o ??\n", c); - } - } - - if (optind < argc) - { - printf ("non-option ARGV-elements: "); - while (optind < argc) - printf ("%s ", argv[optind++]); - printf ("\n"); - } - - exit (0); -} - -#endif /* TEST */ diff --git a/usr.bin/join/join.1 b/usr.bin/join/join.1 index 150d97acc1..83b74d96ef 100644 --- a/usr.bin/join/join.1 +++ b/usr.bin/join/join.1 @@ -1,93 +1,206 @@ -.TH JOIN 1L \" -*- nroff -*- -.SH NAME -join \- join lines of two files on a common field -.SH SYNOPSIS -.B join -[\-a 1|2] [\-v 1|2] [\-e empty-string] [\-o field-list...] [\-t char] -[\-j[1|2] field] [\-1 field] [\-2 field] file1 file2 -.SH DESCRIPTION -This manual page -documents the GNU version of -.BR join . -.B join -prints to the standard output a line for each pair of input lines, one -each from -.I file1 +.\" Copyright (c) 1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)join.1 6.8 (Berkeley) 11/18/91 +.\" +.Dd November 18, 1991 +.Dt JOIN 1 +.Os +.Sh NAME +.Nm join +.Nd relational database operator +.Sh SYNOPSIS +.Nm join +.Oo +.Fl a Ar file_number | Fl v Ar file_number +.Oc +.Op Fl e Ar string +.Op Fl j Ar file_number field +.Op Fl o Ar list +.Bk -words +.Ek +.Op Fl t Ar char +.Op Fl \&1 Ar field +.Op Fl \&2 Ar field +.Ar file1 +.Ar file2 +.Sh DESCRIPTION +The join utility performs an ``equality join'' on the specified files +and writes the result to the standard output. +The ``join field'' is the field in each file by which the files are compared. +The first field in each line is used by default. +There is one line in the output for each pair of lines in +.Ar file1 and -.IR file2 , -that have identical join fields. Either filename (but not both) can -be `\-', meaning the standard input. -.I file1 +.Ar file2 +which have identical join fields. +Each output line consists of the join field, the remaining fields from +.Ar file1 +and then the remaining fields from +.Ar file2 . +.Pp +The default field separators are tab and space characters. +In this case, multiple tabs and spaces count as a single field separator, +and leading tabs and spaces are ignored. +The default output field separator is a single space character. +.Pp +Many of the options use file and field numbers. +Both file numbers and field numbers are 1 based, i.e. the first file on +the command line is file number 1 and the first field is field number 1. +The following options are available: +.Bl -tag -width Fl +.It Fl a Ar file_number +In addition to the default output, produce a line for each unpairable +line in file +.Ar file_number . +.It Fl e Ar string +Replace empty output fields with +.Ar string . +.It Fl o Ar list +The +.Fl o +option specifies the fields that will be output from each file for +each line with matching join fields. +Each element of +.Ar list +has the form +.Ql file_number.field , +where +.Ar file_number +is a file number and +.Ar field +is a field number. +The elements of list must be either comma (``,'') or whitespace separated. +(This will require quoting to protect it from the shell, or, a simpler +approach is to use multiple +.Fl o +options.) +.It Fl t Ar char +Use character +.Ar char +as a field delimiter for both input and output. +Every occurrence of +.Ar char +in a line is significant. +.It Fl v Ar file_number +Do not display the default output, but display a line for each unpairable +line in file +.Ar file_number . +The options +.Fl v Ar 1 and -.I file2 -should be already sorted in increasing order (not numerically) on the -join fields; unless the -.I \-t -option is given, they should be sorted ignoring blanks at the start of -the line, as -.B sort -does when given the -.I \-b +.Fl v Ar 2 +may be specified at the same time. +.It Fl 1 Ar field +Join on the +.Ar field Ns 'th +field of file 1. +.It Fl 2 Ar field +Join on the +.Ar field Ns 'th +field of file 2. +.El +.Pp +When the default field delimiter characters are used, the files to be joined +should be ordered in the collating sequence of +.Xr sort 1 , +using the +.Fl b +option, on the fields on which they are to be joined, otherwise +.Nm join +may not report all field matches. +When the field delimiter characters are specified by the +.Fl t +option, the collating sequence should be the same as +.Xr sort +without the +.Fl b option. -.PP -The defaults are: the join field is the first field in each line; -fields in the input are separated by one or more blanks, with leading -blanks on the line ignored; fields in the output are separated by a -space; each output line consists of the join field, the remaining -fields from -.IR file1 , -then the remaining fields from -.IR file2 . -.SS OPTIONS -.TP -.I "\-a file-number" -Print a line for each unpairable line in file -.I file-number -(either 1 or 2), in addition to the normal output. -.TP -.I "\-e string" -Replace empty output fields (those that are missing in the input) with -.IR string . -.TP -.I "\-1, \-j1 field" -Join on field -.I field -(a positive integer) of file 1. -.TP -.I "\-2, \-j2 field" -Join on field -.I field -(a positive integer) of file 2. -.TP -.I "\-j field" -Equivalent to -.IR "\-1 field \-2 field" . -.TP -.I "\-o field-list..." -Construct each output line according to the format in -.IR field-list . -Each element in -.I field-list -consists of a file number (either 1 or 2), a period, and a field -number (a positive integer). The elements in the list are separated -by commas or blanks. Multiple -.I field-list -arguments can be given after a single -.I \-o -option; the values of all lists given with -.I \-o -are concatenated together. -.TP -.I "\-t char" -Use character -.I char -as the input and output field separator. -.TP -.I "\-v file-number" -Print a line for each unpairable line in file -.I file-number -(either 1 or 2), instead of the normal output. -.PP -The long-named options can be introduced with `+' as well as `\-\-', -for compatibility with previous releases. Eventually support for `+' -will be removed, because it is incompatible with the POSIX.2 standard. +.Pp +If one of the arguments +.Ar file1 +or +.Ar file2 +is ``-'', the standard input is used. +.Pp +The +.Nm join +utility exits 0 on success, and >0 if an error occurs. +.Sh COMPATIBILITY +For compatibility with historic versions of +.Nm join , +the following options are available: +.Bl -tag -width Fl +.It Fl a +In addition to the default output, produce a line for each unpairable line +in both file 1 and file 2. +.It Fl j1 Ar field +Join on the +.Ar field Ns 'th +field of file 1. +.It Fl j2 Ar field +Join on the +.Ar field Ns 'th +field of file 2. +.It Fl j Ar field +Join on the +.Ar field Ns 'th +field of both file 1 and file 2. +.It Fl o Ar list ... +Historical implementations of +.Nm join +permitted multiple arguments to the +.Fl o +option. +These arguments were of the form ``file_number.field_number'' as described +for the current +.Fl o +option. +This has obvious difficulties in the presence of files named ``1.2''. +.El +.Pp +These options are available only so historic shellscripts don't require +modification and should not be used. +.Sh STANDARDS +The +.Nm join +command is expected to be +.St -p1003.2 +compatible. +.Sh SEE ALSO +.Xr awk 1 , +.Xr comm 1 , +.Xr paste 1 , +.Xr sort 1 , +.Xr uniq 1 diff --git a/usr.bin/join/join.c b/usr.bin/join/join.c index 34275cbe2b..14bbc4cd1a 100644 --- a/usr.bin/join/join.c +++ b/usr.bin/join/join.c @@ -1,739 +1,611 @@ -/* join - join lines of two files on a common field - Copyright (C) 1991 Free Software Foundation, Inc. +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Steve Hayman of Indiana University, Michiro Hikida and David + * Goodenough. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1991 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)join.c 5.1 (Berkeley) 11/18/91"; +#endif /* not lint */ - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - Written by Mike Haertel, mike@gnu.ai.mit.edu. */ - -#define _GNU_SOURCE -#include <ctype.h> -#ifndef isblank -#define isblank(c) ((c) == ' ' || (c) == '\t') -#endif -#include <stdio.h> #include <sys/types.h> -#include "getopt.h" -#include <sys/stat.h> -#include <unistd.h> -#include <string.h> #include <errno.h> -/*#include <stdlib.h>*/ -#include <fcntl.h> - -#ifdef isascii -#define ISSPACE(c) (isascii(c) && isspace(c)) -#define ISDIGIT(c) (isascii(c) && isdigit(c)) -#else -#define ISSPACE(c) isspace(c) -#define ISDIGIT(c) isdigit(c) -#endif - -void error (int, int, char *, ...); -static void usage (); - -#define min(A, B) ((A) < (B) ? (A) : (B)) - -/* An element of the list describing the format of each - output line. */ -struct outlist -{ - int file; /* File to take field from (1 or 2). */ - int field; /* Field number to print. */ - struct outlist *next; -}; - -/* A field of a line. */ -struct field -{ - char *beg; /* First character in field. */ - char *lim; /* Character after last character in field. */ -}; - -/* A line read from an input file. Newlines are not stored. */ -struct line -{ - char *beg; /* First character in line. */ - char *lim; /* Character after last character in line. */ - int nfields; /* Number of elements in `fields'. */ - struct field *fields; -}; - -/* One or more consecutive lines read from a file that all have the - same join field value. */ -struct seq -{ - int count; /* Elements used in `lines'. */ - int alloc; /* Elements allocated in `lines'. */ - struct line *lines; -}; - -/* If nonzero, print unpairable lines in file 1 or 2. */ -static int print_unpairables_1, print_unpairables_2; - -/* If nonzero, print pairable lines. */ -static int print_pairables; - -/* Empty output field filler. */ -static char *empty_filler; - -/* Field to join on. */ -static int join_field_1, join_field_2; - -/* List of fields to print. */ -struct outlist *outlist; - -/* Last element in `outlist', where a new element can be added. */ -struct outlist *outlist_end; - -/* Tab character separating fields; if this is NUL fields are separated - by any nonempty string of white space, otherwise by exactly one - tab character. */ -static char tab; - -/* The name this program was run with. */ -char *program_name; - -/* Fill in the `fields' structure in LINE. */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> -static void -xfields (line) - struct line *line; +/* + * There's a structure per input file which encapsulates the state of the + * file. We repeatedly read lines from each file until we've read in all + * the consecutive lines from the file with a common join field. Then we + * compare the set of lines with an equivalent set from the other file. + */ +typedef struct { + char *line; /* line */ + u_long linealloc; /* line allocated count */ + char **fields; /* line field(s) */ + u_long fieldcnt; /* line field(s) count */ + u_long fieldalloc; /* line field(s) allocated count */ +} LINE; + +typedef struct { + FILE *fp; /* file descriptor */ + u_long joinf; /* join field (-1, -2, -j) */ + int unpair; /* output unpairable lines (-a) */ + int number; /* 1 for file 1, 2 for file 2 */ + + LINE *set; /* set of lines with same field */ + u_long pushback; /* line on the stack */ + u_long setcnt; /* set count */ + u_long setalloc; /* set allocated count */ +} INPUT; +INPUT input1 = { NULL, 0, 0, 1, NULL, -1, 0, 0, }, + input2 = { NULL, 0, 0, 1, NULL, -1, 0, 0, }; + +typedef struct { + u_long fileno; /* file number */ + u_long fieldno; /* field number */ +} OLIST; +OLIST *olist; /* output field list */ +u_long olistcnt; /* output field list count */ +u_long olistalloc; /* output field allocated count */ + +int joinout = 1; /* show lines with matched join fields (-v) */ +int needsep; /* need separator character */ +int showusage = 1; /* show usage for usage err() calls */ +int spans = 1; /* span multiple delimiters (-t) */ +char *empty; /* empty field replacement string (-e) */ +char *tabchar = " \t"; /* delimiter characters (-t) */ + +int cmp __P((LINE *, u_long, LINE *, u_long)); +void enomem __P((void)); +void err __P((const char *, ...)); +void fieldarg __P((char *)); +void joinlines __P((INPUT *, INPUT *)); +void obsolete __P((char **)); +void outfield __P((LINE *, u_long)); +void outoneline __P((INPUT *, LINE *)); +void outtwoline __P((INPUT *, LINE *, INPUT *, LINE *)); +void slurp __P((INPUT *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; { - static int nfields = 2; - int i; - register char *ptr, *lim; - - line->fields = (struct field *) malloc (nfields * sizeof (struct field)); - - ptr = line->beg; - lim = line->lim; - - for (i = 0; ptr < lim; ++i) - { - if (i == nfields) - { - nfields *= 2; - line->fields = (struct field *) - realloc ((char *) line->fields, nfields * sizeof (struct field)); + register INPUT *F1, *F2; + int aflag, ch, cval, vflag; + char *end; + + F1 = &input1; + F2 = &input2; + + aflag = vflag = 0; + obsolete(argv); + while ((ch = getopt(argc, argv, "\01a:e:j:1:2:o:t:v:")) != EOF) { + switch (ch) { + case '\01': + aflag = 1; + F1->unpair = F2->unpair = 1; + break; + case '1': + if ((F1->joinf = strtol(optarg, &end, 10)) < 1) + err("-1 option field number less than 1"); + if (*end) + err("illegal field number -- %s", optarg); + --F1->joinf; + break; + case '2': + if ((F2->joinf = strtol(optarg, &end, 10)) < 1) + err("-2 option field number less than 1"); + if (*end) + err("illegal field number -- %s", optarg); + --F2->joinf; + break; + case 'a': + aflag = 1; + switch(strtol(optarg, &end, 10)) { + case 1: + F1->unpair = 1; + break; + case 2: + F2->unpair = 1; + break; + default: + err("-a option file number not 1 or 2"); + break; + } + if (*end) + err("illegal file number -- %s", optarg); + break; + case 'e': + empty = optarg; + break; + case 'j': + if ((F1->joinf = F2->joinf = + strtol(optarg, &end, 10)) < 1) + err("-j option field number less than 1"); + if (*end) + err("illegal field number -- %s", optarg); + --F1->joinf; + --F2->joinf; + break; + case 'o': + fieldarg(optarg); + break; + case 't': + spans = 0; + if (strlen(tabchar = optarg) != 1) + err("illegal tab character specification"); + break; + case 'v': + vflag = 1; + joinout = 0; + switch(strtol(optarg, &end, 10)) { + case 1: + F1->unpair = 1; + break; + case 2: + F2->unpair = 1; + break; + default: + err("-v option file number not 1 or 2"); + break; + } + if (*end) + err("illegal file number -- %s", optarg); + break; + case '?': + default: + usage(); + } } - if (tab) - { - line->fields[i].beg = ptr; - while (ptr < lim && *ptr != tab) - ++ptr; - line->fields[i].lim = ptr; - if (ptr < lim) - ++ptr; + argc -= optind; + argv += optind; + + if (aflag && vflag) + err("-a and -v options mutually exclusive"); + + if (argc != 2) + usage(); + showusage = 0; + + /* Open the files; "-" means stdin. */ + if (!strcmp(*argv, "-")) + F1->fp = stdin; + else if ((F1->fp = fopen(*argv, "r")) == NULL) + err("%s: %s", *argv, strerror(errno)); + ++argv; + if (!strcmp(*argv, "-")) + F2->fp = stdin; + else if ((F2->fp = fopen(*argv, "r")) == NULL) + err("%s: %s", *argv, strerror(errno)); + if (F1->fp == stdin && F2->fp == stdin) + err("only one input file may be stdin"); + + slurp(F1); + slurp(F2); + while (F1->setcnt && F2->setcnt) { + cval = cmp(F1->set, F1->joinf, F2->set, F2->joinf); + if (cval == 0) { + /* Oh joy, oh rapture, oh beauty divine! */ + if (joinout) + joinlines(F1, F2); + slurp(F1); + slurp(F2); + } else if (cval < 0) { + /* File 1 takes the lead... */ + if (F1->unpair) + joinlines(F1, NULL); + slurp(F1); + } else { + /* File 2 takes the lead... */ + if (F2->unpair) + joinlines(F2, NULL); + slurp(F2); + } } - else - { - line->fields[i].beg = ptr; - while (ptr < lim && !ISSPACE (*ptr)) - ++ptr; - line->fields[i].lim = ptr; - while (ptr < lim && ISSPACE (*ptr)) - ++ptr; - } - } - line->nfields = i; + /* + * Now that one of the files is used up, optionally output any + * remaining lines from the other file. + */ + if (F1->unpair) + while (F1->setcnt) { + joinlines(F1, NULL); + slurp(F1); + } + if (F2->unpair) + while (F2->setcnt) { + joinlines(F2, NULL); + slurp(F2); + } + exit(0); } -/* Read a line from FP into LINE and split it into fields. - Return 0 if EOF, 1 otherwise. */ - -static int -get_line (fp, line) - FILE *fp; - struct line *line; +void +slurp(F) + INPUT *F; { - static int linesize = 80; - int c, i; - char *ptr; - - if (feof (fp)) - return 0; - - ptr = malloc (linesize); - - for (i = 0; (c = getc (fp)) != EOF && c != '\n'; ++i) - { - if (i == linesize) - { - linesize *= 2; - ptr = realloc (ptr, linesize); + register LINE *lp, *lastlp; + LINE tmp; + size_t len; + int cnt; + char *bp, *fieldp, *token; + + /* + * Read all of the lines from an input file that have the same + * join field. + */ + F->setcnt = 0; + for (lastlp = NULL;; ++F->setcnt, lastlp = lp) { + /* + * If we're out of space to hold line structures, allocate + * more. Initialize the structure so that we know that this + * is new space. + */ + if (F->setcnt == F->setalloc) { + cnt = F->setalloc; + F->setalloc += 100; + if ((F->set = realloc(F->set, + F->setalloc * sizeof(LINE))) == NULL) + enomem(); + bzero(F->set + cnt, 100 * sizeof(LINE *)); + } + + /* + * Get any pushed back line, else get the next line. Allocate + * space as necessary. If taking the line from the stack swap + * the two structures so that we don't lose the allocated space. + * This could be avoided by doing another level of indirection, + * but it's probably okay as is. + */ + lp = &F->set[F->setcnt]; + if (F->pushback != -1) { + tmp = F->set[F->setcnt]; + F->set[F->setcnt] = F->set[F->pushback]; + F->set[F->pushback] = tmp; + F->pushback = -1; + continue; + } + if ((bp = fgetline(F->fp, &len)) == NULL) + return; + if (lp->linealloc <= len) { + lp->linealloc += 100; + if ((lp->line = realloc(lp->line, + lp->linealloc * sizeof(char))) == NULL) + enomem(); + } + bcopy(bp, lp->line, len); + + /* Split the line into fields, allocate space as necessary. */ + token = bp; + lp->fieldcnt = 0; + while ((fieldp = strsep(&token, tabchar)) != NULL) { + if (spans && *fieldp == '\0') + continue; + if (lp->fieldcnt == lp->fieldalloc) { + lp->fieldalloc += 100; + if ((lp->fields = realloc(lp->fields, + lp->fieldalloc * sizeof(char *))) == NULL) + enomem(); + } + lp->fields[lp->fieldcnt++] = fieldp; + } + + /* See if the join field value has changed. */ + if (lastlp != NULL && cmp(lp, F->joinf, lastlp, F->joinf)) { + F->pushback = F->setcnt; + break; + } } - ptr[i] = c; - } - - if (c == EOF && i == 0) - { - free (ptr); - return 0; - } - - line->beg = ptr; - line->lim = line->beg + i; - xfields (line); - return 1; } -static void -freeline (line) - struct line *line; +int +cmp(lp1, fieldno1, lp2, fieldno2) + LINE *lp1, *lp2; + u_long fieldno1, fieldno2; { - free ((char *) line->fields); - free (line->beg); + if (lp1->fieldcnt < fieldno1) + return (lp2->fieldcnt < fieldno2 ? 0 : 1); + if (lp2->fieldcnt < fieldno2) + return (-1); + return (strcmp(lp1->fields[fieldno1], lp2->fields[fieldno2])); } -static void -initseq (seq) - struct seq *seq; -{ - seq->count = 0; - seq->alloc = 1; - seq->lines = (struct line *) malloc (seq->alloc * sizeof (struct line)); -} - -/* Read a line from FP and add it to SEQ. Return 0 if EOF, 1 otherwise. */ - -static int -getseq (fp, seq) - FILE *fp; - struct seq *seq; -{ - if (seq->count == seq->alloc) - { - seq->alloc *= 2; - seq->lines = (struct line *) - realloc ((char *) seq->lines, seq->alloc * sizeof (struct line)); - } - - if (get_line (fp, &seq->lines[seq->count])) - { - ++seq->count; - return 1; - } - return 0; -} - -static void -delseq (seq) - struct seq *seq; -{ - free ((char *) seq->lines); -} - -/* Return <0 if the join field in LINE1 compares less than the one in LINE2; - >0 if it compares greater; 0 if it compares equal. */ - -static int -keycmp (line1, line2) - struct line *line1; - struct line *line2; -{ - char *beg1, *beg2; /* Start of field to compare in each file. */ - int len1, len2; /* Length of fields to compare. */ - int diff; - - if (join_field_1 < line1->nfields) - { - beg1 = line1->fields[join_field_1].beg; - len1 = line1->fields[join_field_1].lim - - line1->fields[join_field_1].beg; - } - else - { - beg1 = NULL; - len1 = 0; - } - - if (join_field_2 < line2->nfields) - { - beg2 = line2->fields[join_field_2].beg; - len2 = line2->fields[join_field_2].lim - - line2->fields[join_field_2].beg; - } - else - { - beg2 = NULL; - len2 = 0; - } - - if (len1 == 0) - return len2 == 0 ? 0 : -1; - if (len2 == 0) - return 1; - diff = memcmp (beg1, beg2, min (len1, len2)); - if (diff) - return diff; - return len1 - len2; -} - -/* Print field N of LINE if it exists and is nonempty, otherwise - `empty_filler' if it is nonempty. */ - -static void -prfield (n, line) - int n; - struct line *line; +void +joinlines(F1, F2) + register INPUT *F1, *F2; { - int len; - - if (n < line->nfields) - { - len = line->fields[n].lim - line->fields[n].beg; - if (len) - fwrite (line->fields[n].beg, 1, len, stdout); - else if (empty_filler) - fputs (empty_filler, stdout); - } - else if (empty_filler) - fputs (empty_filler, stdout); + register int cnt1, cnt2; + + /* + * Output the results of a join comparison. The output may be from + * either file 1 or file 2 (in which case the first argument is the + * file from which to output) or from both. + */ + if (F2 == NULL) { + for (cnt1 = 0; cnt1 < F1->setcnt; ++cnt1) + outoneline(F1, &F1->set[cnt1]); + return; + } + for (cnt1 = 0; cnt1 < F1->setcnt; ++cnt1) + for (cnt2 = 0; cnt2 < F2->setcnt; ++cnt2) + outtwoline(F1, &F1->set[cnt1], F2, &F2->set[cnt2]); } -/* Print LINE, with its fields separated by `tab'. */ - -static void -prline (line) - struct line *line; +void +outoneline(F, lp) + INPUT *F; + register LINE *lp; { - int i; - - for (i = 0; i < line->nfields; ++i) - { - prfield (i, line); - if (i == line->nfields - 1) - putchar ('\n'); - else - putchar (tab ? tab : ' '); - } + register int cnt; + + /* + * Output a single line from one of the files, according to the + * join rules. This happens when we are writing unmatched single + * lines. Output empty fields in the right places. + */ + if (olist) + for (cnt = 0; cnt < olistcnt; ++cnt) { + if (olist[cnt].fileno == F->number) + outfield(lp, olist[cnt].fieldno); + } + else + for (cnt = 0; cnt < lp->fieldcnt; ++cnt) + outfield(lp, cnt); + (void)printf("\n"); + if (ferror(stdout)) + err("stdout: %s", strerror(errno)); + needsep = 0; } -/* Print the join of LINE1 and LINE2. */ - -static void -prjoin (line1, line2) - struct line *line1; - struct line *line2; +void +outtwoline(F1, lp1, F2, lp2) + register INPUT *F1, *F2; + register LINE *lp1, *lp2; { - if (outlist) - { - struct outlist *o; - - prfield (outlist->field - 1, outlist->file == 1 ? line1 : line2); - for (o = outlist->next; o; o = o->next) - { - putchar (tab ? tab : ' '); - prfield (o->field - 1, o->file == 1 ? line1 : line2); - } - putchar ('\n'); - } - else - { - int i; - - prfield (join_field_1, line1); - for (i = 0; i < join_field_1 && i < line1->nfields; ++i) - { - putchar (tab ? tab : ' '); - prfield (i, line1); - } - for (i = join_field_1 + 1; i < line1->nfields; ++i) - { - putchar (tab ? tab : ' '); - prfield (i, line1); - } - - for (i = 0; i < join_field_2 && i < line2->nfields; ++i) - { - putchar (tab ? tab : ' '); - prfield (i, line2); + register int cnt; + + /* Output a pair of lines according to the join list (if any). */ + if (olist) + for (cnt = 0; cnt < olistcnt; ++cnt) + if (olist[cnt].fileno == 1) + outfield(lp1, olist[cnt].fieldno); + else /* if (olist[cnt].fileno == 2) */ + outfield(lp2, olist[cnt].fieldno); + else { + /* + * Output the join field, then the remaining fields from F1 + * and F2. + */ + outfield(lp1, F1->joinf); + for (cnt = 0; cnt < lp1->fieldcnt; ++cnt) + if (F1->joinf != cnt) + outfield(lp1, cnt); + for (cnt = 0; cnt < lp2->fieldcnt; ++cnt) + if (F2->joinf != cnt) + outfield(lp2, cnt); } - for (i = join_field_2 + 1; i < line2->nfields; ++i) - { - putchar (tab ? tab : ' '); - prfield (i, line2); - } - putchar ('\n'); - } + (void)printf("\n"); + if (ferror(stdout)) + err("stdout: %s", strerror(errno)); + needsep = 0; } -/* Print the join of the files in FP1 and FP2. */ - -static void -join (fp1, fp2) - FILE *fp1; - FILE *fp2; +void +outfield(lp, fieldno) + LINE *lp; + u_long fieldno; { - struct seq seq1, seq2; - struct line line; - int diff, i, j, eof1, eof2; - - /* Read the first line of each file. */ - initseq (&seq1); - getseq (fp1, &seq1); - initseq (&seq2); - getseq (fp2, &seq2); - - while (seq1.count && seq2.count) - { - diff = keycmp (&seq1.lines[0], &seq2.lines[0]); - if (diff < 0) - { - if (print_unpairables_1) - prline (&seq1.lines[0]); - freeline (&seq1.lines[0]); - seq1.count = 0; - getseq (fp1, &seq1); - continue; - } - if (diff > 0) - { - if (print_unpairables_2) - prline (&seq2.lines[0]); - freeline (&seq2.lines[0]); - seq2.count = 0; - getseq (fp2, &seq2); - continue; - } - - /* Keep reading lines from file1 as long as they continue to - match the current line from file2. */ - eof1 = 0; - do - if (!getseq (fp1, &seq1)) - { - eof1 = 1; - ++seq1.count; - break; - } - while (!keycmp (&seq1.lines[seq1.count - 1], &seq2.lines[0])); - - /* Keep reading lines from file2 as long as they continue to - match the current line from file1. */ - eof2 = 0; - do - if (!getseq (fp2, &seq2)) - { - eof2 = 1; - ++seq2.count; - break; - } - while (!keycmp (&seq1.lines[0], &seq2.lines[seq2.count - 1])); - - if (print_pairables) - { - for (i = 0; i < seq1.count - 1; ++i) - for (j = 0; j < seq2.count - 1; ++j) - prjoin (&seq1.lines[i], &seq2.lines[j]); - } - - for (i = 0; i < seq1.count - 1; ++i) - freeline (&seq1.lines[i]); - if (!eof1) - { - seq1.lines[0] = seq1.lines[seq1.count - 1]; - seq1.count = 1; - } - else - seq1.count = 0; - - for (i = 0; i < seq2.count - 1; ++i) - freeline (&seq2.lines[i]); - if (!eof2) - { - seq2.lines[0] = seq2.lines[seq2.count - 1]; - seq2.count = 1; - } - else - seq2.count = 0; - } - - if (print_unpairables_1 && seq1.count) - { - prline (&seq1.lines[0]); - freeline (&seq1.lines[0]); - while (get_line (fp1, &line)) - { - prline (&line); - freeline (&line); - } - } - - if (print_unpairables_2 && seq2.count) - { - prline (&seq2.lines[0]); - freeline (&seq2.lines[0]); - while (get_line (fp2, &line)) - { - prline (&line); - freeline (&line); - } - } - - delseq (&seq1); - delseq (&seq2); + if (needsep++) + (void)printf("%c", *tabchar); + if (!ferror(stdout)) + if (lp->fieldcnt < fieldno) { + if (empty != NULL) + (void)printf("%s", empty); + } else { + if (*lp->fields[fieldno] == '\0') + return; + (void)printf("%s", lp->fields[fieldno]); + } + if (ferror(stdout)) + err("stdout: %s", strerror(errno)); } -/* Add a field spec for field FIELD of file FILE to `outlist' and return 1, - unless either argument is invalid; then just return 0. */ - -static int -add_field (file, field) - int file; - int field; +/* + * Convert an output list argument "2.1, 1.3, 2.4" into an array of output + * fields. + */ +void +fieldarg(option) + char *option; { - struct outlist *o; - - if (file < 1 || file > 2 || field < 1) - return 0; - o = (struct outlist *) malloc (sizeof (struct outlist)); - o->file = file; - o->field = field; - o->next = NULL; - - /* Add to the end of the list so the fields are in the right order. */ - if (outlist == NULL) - outlist = o; - else - outlist_end->next = o; - outlist_end = o; - - return 1; + u_long fieldno; + char *end, *token; + + while ((token = strsep(&option, " \t")) != NULL) { + if (*token == '\0') + continue; + if (token[0] != '1' && token[0] != '2' || token[1] != '.') + err("malformed -o option field"); + fieldno = strtol(token + 2, &end, 10); + if (*end) + err("malformed -o option field"); + if (fieldno == 0) + err("field numbers are 1 based"); + if (olistcnt == olistalloc) { + olistalloc += 50; + if ((olist = realloc(olist, + olistalloc * sizeof(OLIST))) == NULL) + enomem(); + } + olist[olistcnt].fileno = token[0] - '0'; + olist[olistcnt].fieldno = fieldno - 1; + ++olistcnt; + } } -/* Add the comma or blank separated field spec(s) in STR to `outlist'. - Return the number of fields added. */ - -static int -add_field_list (str) - char *str; +void +obsolete(argv) + char **argv; { - int added = 0; - int file = -1, field = -1; - int dot_found = 0; - - for (; *str; str++) - { - if (*str == ',' || isblank (*str)) - { - added += add_field (file, field); - file = field = -1; - dot_found = 0; + int len; + char **p, *ap, *t; + + while (ap = *++argv) { + /* Return if "--". */ + if (ap[0] == '-' && ap[1] == '-') + return; + switch (ap[1]) { + case 'a': + /* + * The original join allowed "-a", which meant the + * same as -a1 plus -a2. POSIX 1003.2, Draft 11.2 + * only specifies this as "-a 1" and "a -2", so we + * have to use another option flag, one that is + * unlikely to ever be used or accidentally entered + * on the command line. (Well, we could reallocate + * the argv array, but that hardly seems worthwhile.) + */ + if (ap[2] == '\0') + ap[1] = '\01'; + break; + case 'j': + /* + * The original join allowed "-j[12] arg" and "-j arg". + * Convert the former to "-[12] arg". Don't convert + * the latter since getopt(3) can handle it. + */ + switch(ap[2]) { + case '1': + if (ap[3] != '\0') + goto jbad; + ap[1] = '1'; + ap[2] = '\0'; + break; + case '2': + if (ap[3] != '\0') + goto jbad; + ap[1] = '2'; + ap[2] = '\0'; + break; + case '\0': + break; + default: +jbad: err("illegal option -- %s", ap); + usage(); + } + break; + case 'o': + /* + * The original join allowed "-o arg arg". Convert to + * "-o arg -o arg". + */ + if (ap[2] != '\0') + break; + for (p = argv + 2; *p; ++p) { + if (p[0][0] != '1' && p[0][0] != '2' || + p[0][1] != '.') + break; + len = strlen(*p); + if (len - 2 != strspn(*p + 2, "0123456789")) + break; + if ((t = malloc(len + 3)) == NULL) + enomem(); + t[0] = '-'; + t[1] = 'o'; + bcopy(*p, t + 2, len + 1); + *p = t; + } + argv = p - 1; + break; + } } - else if (*str == '.') - dot_found = 1; - else if (ISDIGIT (*str)) - { - if (!dot_found) - { - if (file == -1) - file = 0; - file = file * 10 + *str - '0'; - } - else - { - if (field == -1) - field = 0; - field = field * 10 + *str - '0'; - } - } - else - return 0; - } - - added += add_field (file, field); - return added; } -/* When using getopt_long_only, no long option can start with - a character that is a short option. */ -static struct option longopts[] = -{ - {"j", 1, NULL, 'j'}, - {"j1", 1, NULL, '1'}, - {"j2", 1, NULL, '2'}, - {NULL, 0, NULL, 0} -}; - void -main (argc, argv) - int argc; - char *argv[]; +enomem() { - char *names[2]; - FILE *fp1, *fp2; - int optc, prev_optc = 0, nfiles, val; - - program_name = argv[0]; - nfiles = 0; - print_pairables = 1; - - while ((optc = getopt_long_only (argc, argv, "-a:e:1:2:o:t:v:", longopts, - (int *) 0)) != EOF) - { - switch (optc) - { - case 'a': - val = atoi (optarg); - if (val == 1) - print_unpairables_1 = 1; - else if (val == 2) - print_unpairables_2 = 1; - else - error (2, 0, "invalid file number for `-a'"); - break; - - case 'e': - empty_filler = optarg; - break; - - case '1': - val = atoi (optarg); - if (val <= 0) - error (2, 0, "invalid field number for `-1'"); - join_field_1 = val - 1; - break; - - case '2': - val = atoi (optarg); - if (val <= 0) - error (2, 0, "invalid field number for `-2'"); - join_field_2 = val - 1; - break; - - case 'j': - val = atoi (optarg); - if (val <= 0) - error (2, 0, "invalid field number for `-j'"); - join_field_1 = join_field_2 = val - 1; - break; - - case 'o': - if (add_field_list (optarg) == 0) - error (2, 0, "invalid field list for `-o'"); - break; - - case 't': - tab = *optarg; - break; - - case 'v': - val = atoi (optarg); - if (val == 1) - print_unpairables_1 = 1; - else if (val == 2) - print_unpairables_2 = 1; - else - error (2, 0, "invalid file number for `-v'"); - print_pairables = 0; - break; - - case 1: /* Non-option argument. */ - if (prev_optc == 'o') - { - /* Might be continuation of args to -o. */ - if (add_field_list (optarg) > 0) - continue; /* Don't change `prev_optc'. */ - } - - if (nfiles > 1) - usage (); - names[nfiles++] = optarg; - break; - - case '?': - usage (); - } - prev_optc = optc; - } - - if (nfiles != 2) - usage (); - - fp1 = strcmp (names[0], "-") ? fopen (names[0], "r") : stdin; - if (!fp1) - error (1, errno, "%s", names[0]); - fp2 = strcmp (names[1], "-") ? fopen (names[1], "r") : stdin; - if (!fp2) - error (1, errno, "%s", names[1]); - if (fp1 == fp2) - error (1, errno, "both files cannot be standard input"); - join (fp1, fp2); - - if ((fp1 == stdin || fp2 == stdin) && fclose (stdin) == EOF) - error (1, errno, "-"); - if (ferror (stdout) || fclose (stdout) == EOF) - error (1, 0, "write error"); - - exit (0); + showusage = 0; + err("%s", strerror(errno)); } -static void -usage () +void +usage() { - fprintf (stderr, "\ -Usage: %s [-a 1|2] [-v 1|2] [-e empty-string] [-o field-list...] [-t char]\n\ - [-j[1|2] field] [-1 field] [-2 field] file1 file2\n", - program_name); - exit (1); + (void)fprintf(stderr, "%s%s\n", + "usage: join [-a fileno | -v fileno ] [-e string] [-1 field] ", + "[-2 field]\n [-o list] [-t char] file1 file2"); + exit(1); } -/* error.c -- error handler for noninteractive utilities - Copyright (C) 1990, 1991 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* David MacKenzie */ - - +#if __STDC__ #include <stdarg.h> -#define VA_START(args, lastarg) va_start(args, lastarg) +#else +#include <varargs.h> +#endif -/* Print the program name and error message MESSAGE, which is a printf-style - format string with optional args. - If ERRNUM is nonzero, print its corresponding system error message. - Exit with status STATUS if it is nonzero. */ -/* VARARGS */ void -error (int status, int errnum, char *message, ...) +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif { - extern char *program_name; - va_list args; - - fprintf (stderr, "%s: ", program_name); - va_start (args, message); - vfprintf (stderr, message, args); - va_end (args); - if (errnum) - fprintf (stderr, ": %s", strerror (errnum)); - putc ('\n', stderr); - fflush (stderr); - if (status) - exit (status); + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "join: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + if (showusage) + usage(); + exit(1); + /* NOTREACHED */ } diff --git a/usr.bin/lastcomm/lastcomm.c b/usr.bin/lastcomm/lastcomm.c index a5b1cc5c35..298b9082c4 100644 --- a/usr.bin/lastcomm/lastcomm.c +++ b/usr.bin/lastcomm/lastcomm.c @@ -211,7 +211,8 @@ setupdevs() while (dp = readdir(fd)) { if (dp->d_ino == 0) continue; - if (dp->d_name[0] != 't' && strcmp(dp->d_name, "console")) + if (dp->d_name[0] != 't' && strcmp(dp->d_name, "console") + && strcmp(dp->d_name, "vga")) continue; (void)strncpy(hashtab->dev_name, dp->d_name, UT_LINESIZE); hashtab->dev_name[UT_LINESIZE] = 0; diff --git a/usr.bin/lex/COPYING b/usr.bin/lex/COPYING index 3eb69473fb..246628282c 100644 --- a/usr.bin/lex/COPYING +++ b/usr.bin/lex/COPYING @@ -2,51 +2,37 @@ Flex carries the copyright used for BSD software, slightly modified because it originated at the Lawrence Berkeley (not Livermore!) Laboratory, which operates under a contract with the Department of Energy: -/*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Vern Paxson of Lawrence Berkeley Laboratory. - * - * The United States Government has rights in this work pursuant - * to contract no. DE-AC03-76SF00098 between the United States - * Department of Energy and the University of California. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)COPYING 5.2 (Berkeley) 4/12/91 - */ + Copyright (c) 1990 The Regents of the University of California. + All rights reserved. + + This code is derived from software contributed to Berkeley by + Vern Paxson. + + The United States Government has rights in this work pursuant + to contract no. DE-AC03-76SF00098 between the United States + Department of Energy and the University of California. + + Redistribution and use in source and binary forms are permitted + provided that: (1) source distributions retain this entire + copyright notice and comment, and (2) distributions including + binaries display the following acknowledgement: ``This product + includes software developed by the University of California, + Berkeley and its contributors'' in the documentation or other + materials provided with the distribution and in all advertising + materials mentioning features or use of this software. Neither the + name of the University nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. This basically says "do whatever you please with this software except -remove this notice". +remove this notice or take advantage of the University's (or the flex +authors') name". -Note that the "flex.skel" scanner skeleton carries no copyright notice. +Note that the "lex.skel" scanner skeleton carries no copyright notice. You are free to do whatever you please with scanners generated using flex; for them, you are not even bound by the above copyright. diff --git a/usr.bin/lex/Makefile b/usr.bin/lex/Makefile index 4a682a33cd..b5d92f5695 100644 --- a/usr.bin/lex/Makefile +++ b/usr.bin/lex/Makefile @@ -11,14 +11,14 @@ # # To bootstrap lex, cp initscan.c to scan.c and run make. -PROG= lex -CFLAGS+=-I. -I${.CURDIR} -LFLAGS= -is8 -SRCS= ccl.c dfa.c ecs.c gen.c main.c misc.c nfa.c parse.c sym.c tblcmp.c \ - yylex.c -OBJS+= scan.o -SUBDIR= lib -CLEANFILES+=parse.c parse.h scan.c y.tab.h +PROG= lex +SRCS= ccl.c dfa.c ecs.c gen.c main.c misc.c nfa.c parse.c sym.c \ + tblcmp.c yylex.c +OBJS+= scan.o +LFLAGS= -is8 +SUBDIR= lib +CFLAGS+= -I. -I${.CURDIR} +CLEANFILES+= parse.c parse.h scan.c y.tab.h parse.h parse.c: ${YACC} -d ${.IMPSRC} diff --git a/usr.bin/lex/ccl.c b/usr.bin/lex/ccl.c index c717a0881d..ed330903f1 100644 --- a/usr.bin/lex/ccl.c +++ b/usr.bin/lex/ccl.c @@ -1,48 +1,35 @@ +/* ccl - routines for character classes */ + /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by - * Vern Paxson of Lawrence Berkeley Laboratory. + * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint -static char sccsid[] = "@(#)ccl.c 5.2 (Berkeley) 6/18/90"; -#endif /* not lint */ - -/* ccl - routines for character classes */ +static char rcsid[] = + "@(#) $Header: /usr/fsys/odin/a/vern/flex/RCS/ccl.c,v 2.5 90/06/27 23:48:13 vern Exp $ (LBL)"; +#endif #include "flexdef.h" diff --git a/usr.bin/lex/dfa.c b/usr.bin/lex/dfa.c index ce0a25c271..920ae7761c 100644 --- a/usr.bin/lex/dfa.c +++ b/usr.bin/lex/dfa.c @@ -1,51 +1,39 @@ +/* dfa - DFA construction routines */ + /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by - * Vern Paxson of Lawrence Berkeley Laboratory. + * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint -static char sccsid[] = "@(#)dfa.c 5.2 (Berkeley) 6/18/90"; -#endif /* not lint */ - -/* dfa - DFA construction routines */ +static char rcsid[] = + "@(#) $Header: /usr/fsys/odin/a/vern/flex/RCS/dfa.c,v 2.7 90/06/27 23:48:15 vern Exp $ (LBL)"; +#endif #include "flexdef.h" + /* declare functions that have forward references */ void dump_associated_rules PROTO((FILE*, int)); @@ -1024,9 +1012,9 @@ int symlist[]; if ( tch != SYM_EPSILON ) { - if ( tch < -lastccl || tch > csize ) + if ( tch < -lastccl || tch >= csize ) { - if ( tch > csize && tch <= CSIZE ) + if ( tch >= csize && tch <= CSIZE ) flexerror( "scanner requires -8 flag" ); else diff --git a/usr.bin/lex/ecs.c b/usr.bin/lex/ecs.c index 899015057b..010efebde9 100644 --- a/usr.bin/lex/ecs.c +++ b/usr.bin/lex/ecs.c @@ -1,48 +1,35 @@ +/* ecs - equivalence class routines */ + /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by - * Vern Paxson of Lawrence Berkeley Laboratory. + * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint -static char sccsid[] = "@(#)ecs.c 5.2 (Berkeley) 6/18/90"; -#endif /* not lint */ - -/* ecs - equivalence class routines */ +static char rcsid[] = + "@(#) $Header: /usr/fsys/odin/a/vern/flex/RCS/ecs.c,v 2.5 90/06/27 23:48:17 vern Exp $ (LBL)"; +#endif #include "flexdef.h" diff --git a/usr.bin/lex/flexdef.h b/usr.bin/lex/flexdef.h index 1966fe5630..e4fe170764 100644 --- a/usr.bin/lex/flexdef.h +++ b/usr.bin/lex/flexdef.h @@ -1,46 +1,32 @@ +/* flexdef - definitions file for flex */ + /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by - * Vern Paxson of Lawrence Berkeley Laboratory. + * Vern Paxson. * - * The United States Government has rights in this work pursuant + * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)flexdef.h 5.4 (Berkeley) 2/14/91 - */ - -/* flexdef - definitions file for flex */ + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* @(#) $Header: /home/cvs/386BSD/src/usr.bin/lex/flexdef.h,v 1.2 1993/06/29 03:27:08 nate Exp $ (LBL) */ #ifndef FILE #include <stdio.h> @@ -70,7 +56,27 @@ #endif #endif + +#ifdef USG +#define SYS_V +#endif + +#ifdef SYS_V #include <string.h> +#else + +#include <strings.h> +#ifdef lint +char *sprintf(); /* keep lint happy */ +#endif +#ifdef SCO_UNIX +void *memset(); +#else +#ifndef __386BSD__ +char *memset(); +#endif +#endif +#endif #ifdef AMIGA #define bzero(s, n) setmem((char *)(s), n, '\0') @@ -86,6 +92,21 @@ #define SHORT_FILE_NAMES #endif +#ifdef __STDC__ + +#ifdef __GNUC__ +#include <stddef.h> +void *malloc( size_t ); +void free( void* ); +#else +#include <stdlib.h> +#endif + +#else /* ! __STDC__ */ +char *malloc(), *realloc(); +#endif + + /* maximum line length we'll have to deal with */ #define MAXLINE BUFSIZ @@ -110,6 +131,10 @@ #define false 0 +#ifndef DEFAULT_SKELETON_FILE +#define DEFAULT_SKELETON_FILE "/usr/share/misc/lex.skel" +#endif + /* special chk[] values marking the slots taking by end-of-buffer and action * numbers */ diff --git a/usr.bin/lex/flexdoc.1 b/usr.bin/lex/flexdoc.1 index 9d5a8ff9c1..fa1076ed25 100644 --- a/usr.bin/lex/flexdoc.1 +++ b/usr.bin/lex/flexdoc.1 @@ -1,45 +1,4 @@ -.\" Copyright (c) 1991 The Regents of the University of California. -.\" All rights reserved. -.\" -.\" new copyright; att/bsd/shared -.\" This code is derived from software contributed to Berkeley by -.\" Vern Paxson of Lawrence Berkeley Laboratory. -.\" -.\" The United States Government has rights in this work pursuant -.\" to contract no. DE-AC03-76SF00098 between the United States -.\" Department of Energy and the University of California. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)flexdoc.1 5.2 (Berkeley) 4/12/91 -.\" -.TH FLEX 1 "April 12, 1991" +.TH FLEX 1 "26 May 1990" "Version 2.3" .SH NAME flex - fast lexical analyzer generator .SH SYNOPSIS @@ -146,7 +105,7 @@ A somewhat more complicated example: } {DIGIT}+"."{DIGIT}* { - printf( "A float: %s (%d)\\n", yytext, + printf( "A float: %s (%g)\\n", yytext, atof( yytext ) ); } @@ -827,7 +786,10 @@ section of the input file): %{ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) \\ - result = ((buf[0] = getchar()) == EOF) ? YY_NULL : 1; + { \\ + int c = getchar(); \\ + result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \\ + } %} .fi @@ -2082,6 +2044,99 @@ is fully compatible with .I lex with the following exceptions: .IP - +The undocumented +.I lex +scanner internal variable +.B yylineno +is not supported. It is difficult to support this option efficiently, +since it requires examining every character scanned and reexamining +the characters when the scanner backs up. +Things get more complicated when the end of buffer or file is reached or a +NUL is scanned (since the scan must then be restarted with the proper line +number count), or the user uses the yyless(), unput(), or REJECT actions, +or the multiple input buffer functions. +.IP +The fix is to add rules which, upon seeing a newline, increment +yylineno. This is usually an easy process, though it can be a drag if some +of the patterns can match multiple newlines along with other characters. +.IP +yylineno is not part of the POSIX draft. +.IP - +The +.B input() +routine is not redefinable, though it may be called to read characters +following whatever has been matched by a rule. If +.B input() +encounters an end-of-file the normal +.B yywrap() +processing is done. A ``real'' end-of-file is returned by +.B input() +as +.I EOF. +.IP +Input is instead controlled by redefining the +.B YY_INPUT +macro. +.IP +The +.I flex +restriction that +.B input() +cannot be redefined is in accordance with the POSIX draft, but +.B YY_INPUT +has not yet been accepted into the draft (and probably won't; it looks +like the draft will simply not specify any way of controlling the +scanner's input other than by making an initial assignment to +.I yyin). +.IP - +.I flex +scanners do not use stdio for input. Because of this, when writing an +interactive scanner one must explicitly call fflush() on the +stream associated with the terminal after writing out a prompt. +With +.I lex +such writes are automatically flushed since +.I lex +scanners use +.B getchar() +for their input. Also, when writing interactive scanners with +.I flex, +the +.B -I +flag must be used. +.IP - +.I flex +scanners are not as reentrant as +.I lex +scanners. In particular, if you have an interactive scanner and +an interrupt handler which long-jumps out of the scanner, and +the scanner is subsequently called again, you may get the following +message: +.nf + + fatal flex scanner internal error--end of buffer missed + +.fi +To reenter the scanner, first use +.nf + + yyrestart( yyin ); + +.fi +.IP - +.B output() +is not supported. +Output from the +.B ECHO +macro is done to the file-pointer +.I yyout +(default +.I stdout). +.IP +The POSIX draft mentions that an +.B output() +routine exists but currently gives no details as to what it does. +.IP - .I lex does not support exclusive start conditions (%x), though they are in the current POSIX draft. @@ -2125,49 +2180,6 @@ one must use "[^\\]]". The latter works with .I lex, too. .IP - -The undocumented -.I lex -scanner internal variable -.B yylineno -is not supported. (The variable is not part of the POSIX draft.) -.IP - -The -.B input() -routine is not redefinable, though it may be called to read characters -following whatever has been matched by a rule. If -.B input() -encounters an end-of-file the normal -.B yywrap() -processing is done. A ``real'' end-of-file is returned by -.B input() -as -.I EOF. -.IP -Input is instead controlled by redefining the -.B YY_INPUT -macro. -.IP -The -.I flex -restriction that -.B input() -cannot be redefined is in accordance with the POSIX draft, but -.B YY_INPUT -has not yet been accepted into the draft. -.IP - -.B output() -is not supported. -Output from the -.B ECHO -macro is done to the file-pointer -.I yyout -(default -.I stdout). -.IP -The POSIX draft mentions that an -.B output() -routine exists but currently gives no details as to what it does. -.IP - The .I lex .B %r @@ -2179,7 +2191,7 @@ If you are providing your own yywrap() routine, you must include a the "#undef" will have to be enclosed in %{}'s. .IP The POSIX draft -specifies that yywrap() is a function and this is unlikely to change; so +specifies that yywrap() is a function and this is very unlikely to change; so .I flex users are warned that .B yywrap() @@ -2345,7 +2357,7 @@ any of its rules. a scanner rule matched a string long enough to overflow the scanner's internal input buffer (16K bytes by default - controlled by .B YY_BUF_SIZE -in "flex.skel". Note that to redefine this macro, you must first +in "lex.skel". Note that to redefine this macro, you must first .B #undefine it). .LP @@ -2354,6 +2366,17 @@ Your scanner specification includes recognizing 8-bit characters and you did not specify the -8 flag (and your site has not installed flex with -8 as the default). .LP +.I +fatal flex scanner internal error--end of buffer missed - +This can occur in an scanner which is reentered after a long-jump +has jumped out (or over) the scanner's activation frame. Before +reentering the scanner, use: +.nf + + yyrestart( yyin ); + +.fi +.LP .I too many %t classes! - You managed to put every single character into its own %t class. .I flex @@ -2375,7 +2398,7 @@ Jacobson. The implementation was done by Kevin Gong and Vern Paxson. Thanks to the many .I flex beta-testers, feedbackers, and contributors, especially Casey -Leedom, benson@odi.com, +Leedom, benson@odi.com, Keith Bostic, Frederic Brehm, Nick Christopher, Jason Coughlin, Scott David Daniels, Leo Eskin, Chris Faylor, Eric Goldman, Eric @@ -2412,12 +2435,13 @@ Send comments to: .nf Vern Paxson - Computer Science Department - 4126 Upson Hall - Cornell University - Ithaca, NY 14853-7501 - - vern@cs.cornell.edu - decvax!cornell!vern + Computer Systems Engineering + Bldg. 46A, Room 1123 + Lawrence Berkeley Laboratory + University of California + Berkeley, CA 94720 + + vern@ee.lbl.gov + ucbvax!ee.lbl.gov!vern .fi diff --git a/usr.bin/lex/gen.c b/usr.bin/lex/gen.c index 820ec39aac..90b8c58335 100644 --- a/usr.bin/lex/gen.c +++ b/usr.bin/lex/gen.c @@ -1,51 +1,39 @@ +/* gen - actual generation (writing) of flex scanners */ + /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by - * Vern Paxson of Lawrence Berkeley Laboratory. + * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint -static char sccsid[] = "@(#)gen.c 5.2 (Berkeley) 6/18/90"; -#endif /* not lint */ - -/* gen - actual generation (writing) of flex scanners */ +static char rcsid[] = + "@(#) $Header: /home/horse/u0/vern/flex/RCS/gen.c,v 2.12 91/03/28 12:01:38 vern Exp $ (LBL)"; +#endif #include "flexdef.h" + /* declare functions that have forward references */ void gen_next_state PROTO((int)); @@ -688,11 +676,7 @@ void gen_NUL_trans() do_indent(); - if ( interactive ) - printf( "yy_is_jam = (yy_base[yy_current_state] == %d);\n", - jambase ); - else - printf( "yy_is_jam = (yy_current_state == %d);\n", jamstate ); + printf( "yy_is_jam = (yy_current_state == %d);\n", jamstate ); } /* if we've entered an accepting state, backtrack; note that @@ -811,7 +795,7 @@ void gentabs() if ( variable_trailing_context_rules && ! (accnum & YY_TRAILING_HEAD_MASK) && - accnum > 0 && + accnum > 0 && accnum <= num_rules && rule_type[accnum] == RULE_VARIABLE ) { /* special hack to flag accepting number as part @@ -1137,9 +1121,10 @@ void make_tables() puts( "static int yy_looking_for_trail_begin = 0;" ); puts( "static int yy_full_lp;" ); puts( "static int *yy_full_state;" ); - printf( "#define YY_TRAILING_MASK 0x%x\n", YY_TRAILING_MASK ); + printf( "#define YY_TRAILING_MASK 0x%x\n", + (unsigned int) YY_TRAILING_MASK ); printf( "#define YY_TRAILING_HEAD_MASK 0x%x\n", - YY_TRAILING_HEAD_MASK ); + (unsigned int) YY_TRAILING_HEAD_MASK ); } puts( "#define REJECT \\" ); @@ -1210,6 +1195,7 @@ void make_tables() if ( yymore_used ) { + indent_puts( "yy_more_len = 0;" ); indent_puts( "yy_doing_yy_more = yy_more_flag;" ); indent_puts( "if ( yy_doing_yy_more )" ); indent_up(); diff --git a/usr.bin/lex/initscan.c b/usr.bin/lex/initscan.c index e75d72b6b8..41618020ed 100644 --- a/usr.bin/lex/initscan.c +++ b/usr.bin/lex/initscan.c @@ -1,26 +1,13 @@ /* A lexical scanner generated by flex */ /* scanner skeleton version: - * $Header: /usr/fsys/odin/a/vern/flex/RCS/flex.skel,v 2.13 90/05/26 17:24:13 vern Exp $ + * $Header: /home/horse/u0/vern/flex/RCS/flex.skel,v 2.16 90/08/03 14:09:36 vern Exp Locker: vern $ */ #define FLEX_SCANNER #include <stdio.h> -#ifdef __STDC__ - -#ifndef DONT_HAVE_STDLIB_H -#include <stdlib.h> -#else -void *malloc( unsigned ); -void free( void* ); -#endif - -#define YY_USE_PROTOS -#define YY_USE_CONST -#endif - /* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ #ifdef c_plusplus @@ -32,10 +19,7 @@ void free( void* ); #ifdef __cplusplus -#ifndef __STDC__ #include <stdlib.h> -#endif - #include <osfcn.h> /* use prototypes in function declarations */ @@ -44,7 +28,23 @@ void free( void* ); /* the "const" storage-class-modifier is valid */ #define YY_USE_CONST -#endif +#else /* ! __cplusplus */ + +#ifdef __STDC__ + +#ifdef __GNUC__ +#include <stddef.h> +void *malloc( size_t ); +void free( void* ); +#else +#include <stdlib.h> +#endif /* __GNUC__ */ + +#define YY_USE_PROTOS +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ #ifdef __TURBOC__ @@ -61,7 +61,10 @@ void free( void* ); #define YY_PROTO(proto) proto #else #define YY_PROTO(proto) () -/* there's no standard place to get these definitions */ +/* we can't get here if it's an ANSI C compiler, or a C++ compiler, + * so it's got to be a K&R compiler, and therefore there's no standard + * place from which to include these definitions + */ char *malloc(); int free(); int read(); @@ -164,44 +167,43 @@ typedef struct yy_buffer_state *YY_BUFFER_STATE; # line 1 "scan.l" #define INITIAL 0 /* scan.l - scanner for flex input */ -/* - * Copyright (c) 1989 The Regents of the University of California. +# line 5 "scan.l" +/*- + * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by - * Vern Paxson of Lawrence Berkeley Laboratory. + * Vern Paxson. * - * The United States Government has rights in this work pursuant to - * contract no. DE-AC03-76SF00098 between the United States Department of - * Energy and the University of California. + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. * - * Redistribution and use in source and binary forms are permitted - * provided that the above copyright notice and this paragraph are - * duplicated in all such forms and that any documentation, - * advertising materials, and other materials related to such - * distribution and use acknowledge that the software was developed - * by the University of California, Berkeley. The name of the - * University may not be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -# line 28 "scan.l" -#undef yywrap - -#include "flexdef.h" -#include "parse.h" #ifndef lint -static char copyright[] = - "@(#) Copyright (c) 1989 The Regents of the University of California.\n"; -static char CR_continuation[] = "@(#) All rights reserved.\n"; - static char rcsid[] = - "@(#) $Header: /usr/fsys/odin/a/vern/flex/RCS/scan.l,v 2.8 90/05/26 16:53:23 vern Exp $ (LBL)"; + "@(#) $Header: /usr/fsys/odin/a/vern/flex/RCS/scan.l,v 2.9 90/06/27 23:48:34 vern Exp $ (LBL)"; #endif +#undef yywrap + +#include "flexdef.h" +#include "parse.h" + #define ACTION_ECHO fprintf( temp_action_file, "%s", yytext ) #define MARK_END_OF_PROLOG fprintf( temp_action_file, "%%%% end of prolog\n" ); @@ -249,7 +251,7 @@ static char rcsid[] = #define USED_LIST 19 #define CODEBLOCK_2 20 #define XLATION 21 -# line 85 "scan.l" +# line 84 "scan.l" /* done after the current pattern has been matched and before the * corresponding action - sets up yytext @@ -286,7 +288,7 @@ struct yy_buffer_state YY_CHAR *yy_ch_buf; /* input buffer */ YY_CHAR *yy_buf_pos; /* current position in input buffer */ - /* size of input buffer in bytes, not including room for EOB characters*/ + /* size of input buffer in bytes, not including room for EOB characters */ int yy_buf_size; /* number of characters read into yy_ch_buf, not including EOB characters */ @@ -336,7 +338,7 @@ FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; #define YY_END_OF_BUFFER 121 typedef int yy_state_type; -static const short int yy_accept[341] = +static const short int yy_accept[342] = { 0, 0, 0, 0, 0, 0, 0, 119, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -369,12 +371,13 @@ static const short int yy_accept[341] = 58, 0, 64, 0, 0, 117, 117, 55, 55, 65, 63, 28, 28, 28, 25, 0, 118, 118, 100, 100, 0, 21, 92, 92, 95, 95, 38, 38, 0, 39, - 43, 43, 0, 11, 4, 0, 11, 0, 0, 5, + 43, 43, 0, 0, 11, 4, 0, 11, 0, 0, - 0, 0, 0, 117, 28, 28, 118, 100, 100, 95, - 95, 38, 38, 43, 0, 9, 0, 0, 0, 28, - 28, 100, 100, 95, 95, 38, 38, 0, 0, 26, - 27, 93, 94, 93, 94, 36, 37, 10, 62, 0 + 5, 0, 0, 0, 117, 28, 28, 118, 100, 100, + 95, 95, 38, 38, 43, 0, 9, 0, 0, 0, + 28, 28, 100, 100, 95, 95, 38, 38, 0, 0, + 26, 27, 93, 94, 93, 94, 36, 37, 10, 62, + 0 } ; static const YY_CHAR yy_ec[256] = @@ -382,17 +385,17 @@ static const YY_CHAR yy_ec[256] = 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 5, 1, 6, 7, 8, 9, 1, 10, 11, - 11, 12, 11, 13, 14, 11, 15, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 1, 1, 17, - 1, 18, 11, 1, 24, 25, 26, 27, 28, 29, - 23, 23, 23, 30, 31, 23, 32, 33, 34, 31, - 23, 35, 36, 37, 38, 23, 23, 39, 40, 23, - 19, 20, 21, 22, 23, 1, 24, 25, 26, 27, - - 28, 29, 23, 23, 23, 30, 31, 23, 32, 33, - 34, 31, 23, 35, 36, 37, 38, 23, 23, 39, - 40, 23, 41, 42, 43, 1, 1, 1, 1, 1, + 1, 2, 1, 5, 6, 7, 8, 1, 9, 10, + 10, 11, 10, 12, 13, 10, 14, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 1, 1, 16, + 1, 17, 10, 1, 23, 24, 25, 26, 27, 28, + 22, 22, 22, 29, 30, 22, 31, 32, 33, 30, + 22, 34, 35, 36, 37, 22, 22, 38, 39, 22, + 18, 19, 20, 21, 22, 1, 23, 24, 25, 26, + + 27, 28, 22, 22, 22, 29, 30, 22, 31, 32, + 33, 30, 22, 34, 35, 36, 37, 22, 22, 38, + 39, 22, 40, 41, 42, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -409,445 +412,437 @@ static const YY_CHAR yy_ec[256] = 1, 1, 1, 1, 1 } ; -static const YY_CHAR yy_meta[44] = +static const YY_CHAR yy_meta[43] = { 0, - 1, 2, 3, 2, 2, 4, 1, 1, 1, 5, - 1, 6, 1, 7, 5, 8, 1, 1, 1, 9, - 10, 1, 11, 12, 12, 12, 12, 12, 12, 11, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 5, 1, 13 + 1, 2, 3, 2, 4, 1, 1, 1, 5, 1, + 6, 1, 7, 5, 8, 1, 1, 1, 9, 10, + 1, 11, 12, 12, 12, 12, 12, 12, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 5, + 1, 13 } ; -static const short int yy_base[404] = +static const short int yy_base[405] = { 0, - 0, 43, 85, 126, 1371, 1370, 1369, 1353, 168, 1346, - 104, 108, 211, 0, 1332, 1320, 120, 252, 95, 119, - 137, 144, 100, 141, 295, 0, 1327, 1323, 113, 336, - 254, 255, 257, 258, 253, 268, 379, 0, 338, 421, - 0, 0, 273, 460, 1325, 1442, 281, 1442, 1287, 0, - 287, 1442, 1279, 472, 1257, 0, 1442, 425, 1442, 1442, - 147, 1442, 1239, 1235, 78, 513, 433, 1442, 83, 1442, - 1248, 0, 1247, 1442, 0, 1442, 0, 1218, 1205, 1194, - 0, 342, 1442, 1442, 1442, 1442, 1202, 0, 1442, 1442, - 1442, 1442, 1201, 1442, 1442, 1442, 1442, 79, 1442, 1442, - - 103, 1198, 1442, 0, 248, 1442, 0, 1442, 1442, 252, - 1199, 0, 1173, 1158, 1442, 1442, 1185, 1442, 1442, 1442, - 0, 1442, 1170, 0, 1442, 1152, 0, 1442, 1442, 0, - 0, 346, 1442, 1123, 0, 1125, 1105, 0, 352, 1442, - 1116, 1103, 1442, 356, 1442, 1100, 329, 1442, 360, 1093, - 1101, 333, 441, 1442, 445, 0, 449, 1442, 1101, 1442, - 365, 453, 1094, 466, 0, 480, 330, 0, 1095, 1442, - 0, 555, 1442, 1044, 1077, 1442, 1051, 133, 456, 1442, - 1059, 0, 0, 1442, 584, 563, 1442, 0, 1442, 1071, - 0, 1442, 1063, 1442, 0, 0, 1010, 1007, 627, 0, - - 484, 1442, 0, 998, 1442, 992, 0, 1442, 1442, 1442, - 0, 421, 1442, 0, 1442, 0, 971, 964, 992, 1442, - 0, 962, 0, 1442, 0, 1442, 0, 488, 921, 670, - 0, 717, 714, 0, 497, 715, 712, 569, 573, 1442, - 727, 0, 577, 726, 581, 1442, 585, 0, 590, 738, - 597, 0, 712, 683, 691, 0, 670, 1442, 1442, 623, - 1442, 591, 1442, 458, 702, 590, 0, 0, 0, 1442, - 0, 576, 569, 0, 1442, 593, 575, 0, 560, 546, - 567, 1442, 0, 0, 541, 529, 534, 527, 730, 1442, - 500, 0, 509, 1442, 0, 734, 1442, 468, 467, 0, - - 462, 704, 724, 1442, 461, 438, 1442, 440, 425, 433, - 405, 413, 398, 1442, 404, 1442, 359, 259, 332, 338, - 346, 334, 331, 257, 253, 226, 137, 133, 81, 0, - 0, 0, 0, 0, 0, 0, 0, 1442, 1442, 1442, - 753, 766, 779, 792, 805, 818, 831, 844, 857, 870, - 883, 896, 909, 922, 935, 948, 955, 967, 980, 986, - 998, 1011, 1024, 1037, 1050, 1063, 1070, 1082, 1089, 1101, - 1114, 1127, 1140, 1150, 1157, 1169, 1182, 1195, 1208, 1221, - 1234, 1241, 1253, 1266, 1279, 1282, 1284, 1296, 1309, 1315, - 1327, 1339, 1345, 1357, 1363, 1375, 1382, 1388, 1393, 1405, - - 1411, 1423, 1429 + 0, 42, 83, 123, 1401, 1400, 1399, 1398, 164, 1391, + 101, 104, 206, 0, 1377, 1376, 106, 107, 93, 110, + 132, 133, 137, 246, 266, 0, 1382, 1378, 113, 117, + 89, 247, 249, 250, 306, 307, 326, 0, 367, 370, + 0, 0, 373, 376, 1380, 1405, 158, 1405, 1343, 0, + 252, 1405, 1360, 402, 1351, 0, 1405, 385, 1405, 1405, + 260, 1405, 1345, 1338, 76, 442, 483, 1405, 103, 1405, + 1353, 0, 1348, 1405, 0, 1405, 0, 1323, 1294, 1290, + 0, 311, 1405, 1405, 1405, 1405, 1310, 0, 1405, 1405, + 1405, 1405, 1300, 1405, 1405, 1405, 1405, 367, 1405, 1405, + + 370, 1301, 1405, 0, 375, 1405, 0, 1405, 1405, 305, + 1302, 0, 1274, 1250, 1405, 1405, 1282, 1405, 1405, 1405, + 0, 1405, 1270, 0, 1405, 1269, 0, 1405, 1405, 0, + 0, 314, 1405, 1199, 0, 1201, 1176, 0, 315, 1405, + 1187, 1171, 1405, 319, 1405, 1173, 379, 1405, 396, 1169, + 1189, 381, 412, 1405, 418, 0, 486, 1405, 1193, 1405, + 395, 426, 429, 490, 0, 493, 351, 0, 1180, 1405, + 0, 496, 1405, 1136, 1168, 1405, 1143, 383, 403, 1405, + 1145, 0, 0, 1405, 537, 499, 1405, 0, 1405, 1154, + 0, 1405, 1144, 1405, 0, 0, 1115, 1095, 579, 0, + + 504, 1405, 0, 1110, 1405, 1097, 0, 1405, 1405, 1405, + 0, 496, 1405, 0, 1405, 0, 1075, 1072, 1099, 1405, + 0, 1093, 0, 1405, 0, 1405, 0, 508, 1047, 621, + 0, 1047, 1032, 0, 507, 1029, 1026, 515, 518, 1405, + 1029, 0, 521, 1024, 524, 1405, 527, 0, 528, 531, + 662, 0, 665, 1001, 1007, 0, 1030, 1405, 1405, 1022, + 1405, 966, 1405, 655, 661, 983, 0, 0, 0, 1405, + 0, 946, 934, 0, 1405, 957, 940, 0, 927, 891, + 920, 1405, 0, 0, 679, 672, 677, 670, 669, 1405, + 687, 0, 698, 675, 1405, 0, 688, 1405, 665, 670, + + 0, 667, 678, 684, 1405, 669, 631, 1405, 599, 484, + 468, 411, 399, 378, 1405, 380, 1405, 303, 494, 663, + 228, 230, 215, 131, 111, 111, 96, 90, 69, 72, + 0, 0, 0, 0, 0, 0, 0, 0, 1405, 1405, + 1405, 712, 725, 738, 751, 764, 777, 790, 803, 816, + 829, 842, 855, 868, 881, 894, 907, 914, 926, 939, + 945, 957, 970, 983, 996, 1009, 1022, 1029, 1041, 1048, + 1060, 1073, 1086, 1099, 1109, 1116, 1128, 1141, 1154, 1167, + 1180, 1193, 1200, 1212, 1225, 1238, 1241, 1243, 1255, 1268, + 1274, 1286, 1298, 1304, 1316, 1322, 1334, 1341, 1347, 1352, + + 1364, 1370, 1382, 1388 } ; -static const short int yy_def[404] = +static const short int yy_def[405] = { 0, - 340, 340, 341, 341, 342, 342, 343, 343, 340, 9, - 344, 344, 340, 13, 345, 345, 346, 346, 347, 347, - 348, 348, 349, 349, 340, 25, 350, 350, 345, 345, - 351, 351, 352, 352, 353, 353, 340, 37, 354, 354, - 37, 37, 355, 356, 340, 340, 340, 340, 340, 357, - 340, 340, 358, 359, 340, 360, 340, 340, 340, 340, - 340, 340, 340, 361, 362, 340, 340, 340, 340, 340, - 363, 364, 365, 340, 366, 340, 367, 367, 367, 366, - 368, 340, 340, 340, 340, 340, 340, 369, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 362, 340, 340, - - 370, 371, 340, 372, 362, 340, 373, 340, 340, 374, - 340, 375, 375, 375, 340, 340, 376, 340, 340, 340, - 377, 340, 340, 378, 340, 340, 379, 340, 340, 380, - 381, 381, 340, 381, 382, 382, 382, 383, 340, 340, - 383, 383, 340, 340, 340, 340, 384, 340, 340, 340, - 340, 384, 340, 340, 340, 357, 340, 340, 358, 340, - 340, 385, 340, 340, 386, 340, 340, 387, 388, 340, - 360, 340, 340, 340, 389, 340, 340, 361, 361, 340, - 340, 390, 391, 340, 391, 340, 340, 392, 340, 363, - 364, 340, 365, 340, 366, 367, 367, 367, 340, 368, - - 340, 340, 369, 340, 340, 340, 393, 340, 340, 340, - 373, 374, 340, 374, 340, 375, 375, 375, 376, 340, - 377, 394, 378, 340, 379, 340, 381, 381, 381, 340, - 382, 382, 382, 383, 340, 383, 383, 340, 340, 340, - 340, 395, 340, 340, 340, 340, 340, 385, 385, 396, - 340, 397, 396, 340, 340, 398, 388, 340, 340, 389, - 340, 340, 340, 361, 361, 340, 399, 391, 185, 340, - 392, 367, 367, 199, 340, 400, 340, 401, 375, 375, - 394, 340, 230, 402, 382, 382, 383, 383, 340, 340, - 340, 403, 396, 340, 397, 396, 340, 340, 340, 398, - - 340, 361, 265, 340, 367, 367, 340, 375, 375, 382, - 382, 383, 383, 340, 340, 340, 340, 361, 361, 367, - 367, 375, 375, 382, 382, 383, 383, 340, 340, 367, - 367, 375, 375, 382, 382, 383, 383, 340, 340, 0, - 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, - - 340, 340, 340 + 341, 341, 342, 342, 343, 343, 344, 344, 341, 9, + 345, 345, 341, 13, 346, 346, 347, 347, 348, 348, + 349, 349, 350, 350, 341, 25, 351, 351, 346, 346, + 352, 352, 353, 353, 354, 354, 341, 37, 355, 355, + 37, 37, 356, 357, 341, 341, 341, 341, 341, 358, + 341, 341, 359, 360, 341, 361, 341, 341, 341, 341, + 341, 341, 341, 362, 363, 341, 341, 341, 341, 341, + 364, 365, 366, 341, 367, 341, 368, 368, 368, 367, + 369, 341, 341, 341, 341, 341, 341, 370, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 363, 341, 341, + + 371, 372, 341, 373, 363, 341, 374, 341, 341, 375, + 341, 376, 376, 376, 341, 341, 377, 341, 341, 341, + 378, 341, 341, 379, 341, 341, 380, 341, 341, 381, + 382, 382, 341, 382, 383, 383, 383, 384, 341, 341, + 384, 384, 341, 341, 341, 341, 385, 341, 341, 341, + 341, 385, 341, 341, 341, 358, 341, 341, 359, 341, + 341, 386, 341, 341, 387, 341, 341, 388, 389, 341, + 361, 341, 341, 341, 390, 341, 341, 362, 362, 341, + 341, 391, 392, 341, 341, 341, 341, 393, 341, 364, + 365, 341, 366, 341, 367, 368, 368, 368, 341, 369, + + 341, 341, 370, 341, 341, 341, 394, 341, 341, 341, + 374, 375, 341, 375, 341, 376, 376, 376, 377, 341, + 378, 395, 379, 341, 380, 341, 382, 382, 382, 341, + 383, 383, 383, 384, 341, 384, 384, 341, 341, 341, + 341, 396, 341, 341, 341, 341, 341, 386, 386, 397, + 341, 398, 397, 341, 341, 399, 389, 341, 341, 390, + 341, 341, 341, 362, 362, 341, 400, 392, 185, 341, + 393, 368, 368, 199, 341, 401, 341, 402, 376, 376, + 395, 341, 230, 403, 383, 383, 384, 384, 341, 341, + 341, 404, 397, 397, 341, 398, 397, 341, 341, 341, + + 399, 341, 362, 265, 341, 368, 368, 341, 376, 376, + 383, 383, 384, 384, 341, 341, 341, 341, 362, 362, + 368, 368, 376, 376, 383, 383, 384, 384, 341, 341, + 368, 368, 376, 376, 383, 383, 384, 384, 341, 341, + 0, 341, 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, + + 341, 341, 341, 341 } ; -static const short int yy_nxt[1486] = +static const short int yy_nxt[1448] = { 0, - 46, 47, 48, 47, 47, 46, 46, 46, 49, 46, + 46, 47, 48, 47, 46, 46, 46, 49, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 46, 46, 46, 46, 51, 52, 51, 51, 46, 53, - 46, 54, 46, 46, 46, 46, 46, 55, 46, 46, - 46, 46, 46, 46, 46, 56, 56, 56, 56, 56, + 46, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 46, + 46, 46, 46, 51, 52, 51, 46, 53, 46, 54, + 46, 46, 46, 46, 46, 55, 46, 46, 46, 46, + 46, 46, 46, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, - 56, 56, 56, 46, 46, 46, 58, 59, 58, 58, - 60, 188, 61, 181, 181, 62, 62, 96, 339, 62, - - 97, 63, 85, 64, 65, 82, 83, 82, 82, 82, - 83, 82, 82, 104, 98, 119, 182, 182, 206, 105, - 106, 96, 91, 189, 97, 66, 62, 67, 68, 67, - 67, 60, 92, 61, 69, 93, 62, 62, 98, 85, - 62, 207, 63, 85, 64, 65, 85, 70, 176, 176, - 100, 176, 179, 263, 104, 120, 101, 100, 102, 338, - 105, 106, 94, 101, 337, 102, 66, 62, 75, 75, - 76, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 56, 46, 46, 46, 58, 59, 58, 60, 340, 61, + 181, 122, 62, 62, 339, 96, 62, 97, 63, 123, + + 64, 65, 82, 83, 82, 82, 83, 82, 91, 91, + 188, 98, 96, 182, 97, 119, 338, 92, 92, 119, + 93, 93, 66, 62, 67, 68, 67, 60, 98, 61, + 69, 337, 62, 62, 85, 85, 62, 336, 63, 85, + 64, 65, 189, 70, 100, 100, 335, 94, 94, 104, + 101, 101, 102, 102, 120, 105, 106, 334, 120, 153, + 154, 153, 66, 62, 75, 75, 76, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 75, 75, 75, 75, 75, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 78, 77, 77, - 77, 77, 78, 77, 77, 77, 77, 79, 75, 75, - 75, 84, 84, 85, 84, 84, 84, 84, 84, 84, - 84, 84, 84, 86, 84, 84, 84, 84, 87, 84, - 84, 84, 84, 88, 88, 88, 88, 88, 88, 88, + 77, 77, 79, 75, 75, 75, 84, 84, 85, 84, + 84, 84, 84, 84, 84, 84, 84, 86, 84, 84, + 84, 84, 87, 84, 84, 84, 84, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, - 88, 84, 84, 84, 91, 128, 122, 122, 129, 125, - 125, 213, 336, 181, 92, 123, 123, 93, 126, 126, - 128, 214, 130, 129, 144, 145, 144, 144, 179, 263, - 335, 146, 153, 154, 153, 153, 182, 130, 157, 158, - 157, 157, 147, 334, 94, 107, 107, 108, 107, 107, - + 88, 88, 88, 88, 88, 84, 84, 84, 85, 122, + 333, 125, 125, 157, 158, 157, 332, 123, 104, 126, + 126, 176, 176, 331, 105, 106, 107, 107, 108, 107, 109, 107, 107, 107, 110, 107, 107, 107, 107, 111, 107, 107, 107, 107, 107, 107, 107, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 113, - 112, 112, 112, 112, 114, 115, 107, 116, 119, 139, - 140, 139, 139, 201, 241, 201, 201, 228, 241, 228, - 228, 179, 263, 235, 229, 235, 235, 238, 333, 238, - 238, 243, 254, 243, 243, 255, 247, 242, 247, 247, - 332, 242, 141, 331, 330, 244, 329, 142, 120, 131, - 132, 133, 132, 132, 131, 131, 131, 134, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - - 131, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 136, 135, 135, 135, 135, 137, 131, - 131, 131, 139, 140, 139, 139, 172, 173, 172, 172, - 213, 328, 327, 174, 186, 187, 186, 186, 326, 325, - 214, 174, 153, 154, 153, 153, 245, 246, 245, 245, - 157, 158, 157, 157, 249, 141, 249, 249, 324, 323, - 142, 149, 145, 149, 149, 322, 175, 251, 150, 251, - 251, 264, 321, 302, 175, 151, 178, 179, 263, 152, - 162, 245, 246, 245, 253, 201, 320, 201, 201, 228, - 317, 228, 228, 316, 265, 163, 229, 164, 235, 163, - - 235, 235, 163, 315, 163, 163, 164, 165, 166, 167, - 168, 294, 169, 183, 183, 240, 183, 183, 183, 183, - 183, 183, 183, 183, 183, 183, 183, 183, 184, 183, - 183, 183, 183, 183, 183, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 183, 183, 183, 172, 173, 172, 172, - 313, 312, 311, 174, 186, 187, 186, 186, 310, 282, - 238, 174, 238, 238, 289, 290, 289, 289, 243, 309, - 243, 243, 245, 246, 245, 245, 247, 308, 247, 247, - 205, 249, 244, 249, 249, 275, 175, 269, 251, 269, - 251, 251, 306, 305, 175, 180, 269, 269, 269, 269, + 112, 112, 112, 112, 114, 115, 107, 116, 128, 128, + 129, 129, 201, 213, 201, 228, 235, 228, 235, 330, + 238, 229, 238, 214, 130, 130, 131, 132, 133, 132, + 131, 131, 131, 134, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 136, + 135, 135, 135, 135, 137, 131, 131, 131, 139, 140, + 139, 139, 140, 139, 144, 145, 144, 149, 145, 149, + 146, 181, 254, 150, 206, 255, 172, 173, 172, 181, + 151, 147, 174, 241, 152, 241, 247, 243, 247, 243, + + 141, 179, 263, 141, 182, 142, 329, 207, 142, 162, + 244, 328, 182, 153, 154, 153, 242, 264, 242, 245, + 246, 245, 178, 327, 163, 175, 164, 249, 163, 249, + 250, 163, 250, 163, 163, 164, 165, 166, 167, 168, + 265, 169, 183, 183, 326, 183, 183, 183, 183, 183, + 183, 183, 183, 183, 183, 183, 184, 183, 183, 183, + 183, 183, 183, 185, 185, 185, 185, 185, 185, 185, + 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, + 185, 183, 183, 183, 186, 187, 186, 157, 158, 157, + 174, 251, 325, 251, 253, 246, 253, 172, 173, 172, + + 186, 187, 186, 174, 213, 201, 174, 201, 235, 228, + 235, 228, 179, 263, 214, 229, 238, 324, 238, 289, + 290, 289, 243, 175, 243, 245, 246, 245, 247, 249, + 247, 249, 294, 295, 294, 244, 175, 268, 268, 175, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 269, + 268, 269, 268, 268, 268, 268, 268, 268, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, - 269, 269, 269, 269, 301, 261, 270, 274, 274, 275, + 269, 269, 269, 269, 269, 269, 268, 268, 270, 274, + 274, 275, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 276, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, - 276, 276, 276, 276, 276, 276, 276, 274, 274, 274, - 283, 283, 258, 283, 283, 283, 283, 283, 283, 283, + 276, 276, 276, 276, 276, 276, 276, 276, 274, 274, + 274, 283, 283, 323, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 284, 284, 284, 284, 284, 284, 284, 284, - 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, - 283, 283, 283, 296, 297, 296, 296, 303, 299, 318, - 298, 179, 263, 179, 263, 303, 303, 303, 303, 303, - 303, 289, 290, 289, 289, 296, 297, 296, 296, 319, - 294, 244, 291, 288, 287, 286, 285, 319, 319, 319, - 319, 319, 319, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 71, 73, + 283, 283, 283, 251, 322, 251, 297, 298, 297, 303, + 289, 290, 289, 179, 263, 304, 294, 295, 294, 179, + 263, 179, 263, 304, 304, 304, 304, 304, 304, 297, + 298, 297, 319, 321, 318, 317, 179, 263, 320, 316, + + 295, 240, 314, 313, 312, 311, 320, 320, 320, 320, + 320, 320, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, - 73, 73, 81, 81, 81, 81, 81, 81, 81, 81, - - 81, 81, 81, 81, 81, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 90, 90, + 73, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 121, 121, 121, 121, 121, 121, 121, - 121, 121, 121, 121, 121, 121, 124, 124, 124, 124, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 127, + 95, 95, 95, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 138, 138, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 143, 143, 143, 143, 143, - 143, 143, 143, 143, 143, 143, 143, 143, 148, 148, + 127, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 143, 143, 143, 143, 143, 143, + + 143, 143, 143, 143, 143, 143, 143, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, - 148, 156, 156, 230, 282, 156, 156, 159, 159, 159, - 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 156, 156, 282, 310, 156, 156, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 171, 171, 220, 280, 171, 171, 178, 178, + 161, 171, 171, 309, 205, 171, 171, 178, 178, 275, + 178, 178, 178, 178, 178, 178, 307, 178, 178, 178, + 180, 180, 306, 180, 180, 180, 180, 180, 180, 180, + 180, 180, 180, 190, 190, 190, 190, 190, 190, 190, + 190, 190, 190, 190, 190, 190, 192, 180, 302, 192, - 279, 178, 178, 178, 178, 178, 178, 277, 178, 178, - 178, 180, 180, 204, 180, 180, 180, 180, 180, 180, - 180, 180, 180, 180, 190, 190, 190, 190, 190, 190, - 190, 190, 190, 190, 190, 190, 190, 192, 273, 272, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, - 193, 193, 193, 195, 195, 194, 195, 195, 195, 195, - 195, 195, 195, 191, 266, 195, 196, 196, 262, 261, - 196, 196, 200, 200, 259, 200, 200, 200, 200, 200, - 200, 200, 200, 200, 200, 203, 203, 258, 250, 203, - - 203, 205, 205, 160, 205, 205, 205, 205, 205, 205, - 205, 205, 205, 205, 208, 208, 244, 208, 208, 208, - 208, 208, 208, 208, 208, 208, 208, 210, 210, 239, - 210, 210, 210, 210, 210, 210, 239, 210, 210, 210, - 211, 211, 237, 236, 233, 211, 211, 211, 211, 211, - 212, 212, 232, 212, 212, 212, 212, 212, 212, 212, - 212, 212, 212, 216, 216, 230, 224, 216, 216, 219, + 193, 193, 195, 195, 261, 195, 195, 195, 195, 195, + 195, 195, 258, 300, 195, 196, 196, 299, 244, 196, + 196, 200, 200, 291, 200, 200, 200, 200, 200, 200, + 200, 200, 200, 200, 203, 203, 288, 287, 203, 203, + 205, 205, 286, 205, 205, 205, 205, 205, 205, 205, + 205, 205, 205, 208, 208, 285, 208, 208, 208, 208, + 208, 208, 208, 208, 208, 208, 210, 210, 230, 210, + 210, 210, 210, 210, 210, 282, 210, 210, 210, 211, + + 211, 220, 280, 279, 211, 211, 211, 211, 211, 212, + 212, 277, 212, 212, 212, 212, 212, 212, 212, 212, + 212, 212, 216, 216, 204, 273, 216, 216, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, - 219, 219, 221, 221, 222, 221, 221, 220, 221, 221, - 221, 221, 221, 221, 221, 223, 223, 218, 223, 223, - - 217, 223, 223, 223, 223, 223, 223, 223, 225, 225, - 215, 209, 225, 225, 225, 225, 204, 225, 225, 225, - 225, 226, 226, 202, 226, 226, 226, 226, 226, 226, - 226, 226, 226, 226, 227, 227, 199, 227, 227, 227, - 227, 227, 227, 227, 198, 197, 227, 231, 231, 194, - 191, 231, 231, 234, 179, 177, 234, 234, 234, 234, - 234, 234, 234, 234, 234, 234, 240, 240, 170, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 248, - 248, 160, 248, 248, 248, 248, 248, 248, 248, 248, - 248, 248, 252, 252, 256, 256, 257, 257, 257, 257, - - 257, 257, 257, 257, 257, 257, 257, 257, 257, 260, + 219, 221, 221, 272, 221, 221, 194, 221, 221, 221, + 221, 221, 221, 221, 223, 223, 191, 223, 223, 266, + 223, 223, 223, 223, 223, 223, 223, 225, 225, 262, + 261, 225, 225, 225, 225, 259, 225, 225, 225, 225, + 226, 226, 258, 226, 226, 226, 226, 226, 226, 226, + 226, 226, 226, 227, 227, 160, 227, 227, 227, 227, + + 227, 227, 227, 244, 239, 227, 231, 231, 239, 237, + 231, 231, 234, 236, 233, 234, 234, 234, 234, 234, + 234, 234, 234, 234, 234, 240, 240, 232, 240, 240, + 240, 240, 240, 240, 240, 240, 240, 240, 248, 248, + 230, 248, 248, 248, 248, 248, 248, 248, 248, 248, + 248, 252, 252, 256, 256, 257, 257, 257, 257, 257, + 257, 257, 257, 257, 257, 257, 257, 257, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, - 260, 260, 267, 155, 340, 118, 267, 268, 268, 118, - 268, 268, 268, 268, 268, 268, 268, 268, 268, 271, - 271, 89, 271, 271, 271, 271, 271, 271, 271, 271, - 271, 271, 278, 89, 80, 74, 278, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 292, 74, 72, 72, 292, 293, 293, 293, 293, 293, - 293, 293, 293, 293, 293, 293, 293, 293, 295, 295, - 340, 340, 295, 295, 300, 300, 340, 340, 300, 300, - - 304, 340, 340, 340, 304, 276, 276, 276, 276, 276, - 276, 276, 276, 276, 276, 276, 276, 276, 307, 340, - 340, 340, 307, 284, 284, 340, 284, 284, 284, 284, - 284, 284, 284, 284, 284, 284, 314, 340, 340, 340, - 314, 45, 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340 + 260, 267, 224, 222, 220, 267, 268, 268, 218, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 271, 271, + + 217, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 278, 215, 209, 204, 278, 281, 281, 281, 281, + 281, 281, 281, 281, 281, 281, 281, 281, 281, 292, + 202, 199, 198, 292, 293, 293, 293, 293, 293, 293, + 293, 293, 293, 293, 293, 293, 293, 296, 296, 197, + 194, 296, 296, 301, 301, 191, 179, 301, 301, 305, + 177, 170, 160, 305, 276, 276, 276, 276, 276, 276, + 276, 276, 276, 276, 276, 276, 276, 308, 155, 341, + 118, 308, 284, 284, 118, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 315, 89, 89, 80, 315, + + 74, 74, 72, 72, 45, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341 } ; -static const short int yy_chk[1486] = +static const short int yy_chk[1448] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, - 3, 69, 3, 65, 98, 3, 3, 19, 329, 3, - - 19, 3, 23, 3, 3, 11, 11, 11, 11, 12, - 12, 12, 12, 23, 19, 29, 65, 98, 101, 23, - 23, 20, 17, 69, 20, 3, 3, 4, 4, 4, - 4, 4, 17, 4, 4, 17, 4, 4, 20, 21, - 4, 101, 4, 24, 4, 4, 22, 4, 61, 61, - 21, 61, 178, 178, 24, 29, 21, 22, 21, 328, - 24, 24, 17, 22, 327, 22, 4, 4, 9, 9, + 2, 2, 2, 2, 3, 3, 3, 3, 330, 3, + 65, 31, 3, 3, 329, 19, 3, 19, 3, 31, + + 3, 3, 11, 11, 11, 12, 12, 12, 17, 18, + 69, 19, 20, 65, 20, 29, 328, 17, 18, 30, + 17, 18, 3, 3, 4, 4, 4, 4, 20, 4, + 4, 327, 4, 4, 21, 22, 4, 326, 4, 23, + 4, 4, 69, 4, 21, 22, 325, 17, 18, 23, + 21, 22, 21, 22, 29, 23, 23, 324, 30, 47, + 47, 47, 4, 4, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 9, 9, 9, 9, 9, 9, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 18, 35, 31, 32, 35, 33, - 34, 110, 326, 105, 18, 31, 32, 18, 33, 34, - 36, 110, 35, 36, 43, 43, 43, 43, 318, 318, - 325, 43, 47, 47, 47, 47, 105, 36, 51, 51, - 51, 51, 43, 324, 18, 25, 25, 25, 25, 25, - + 13, 13, 13, 13, 13, 13, 13, 13, 24, 32, + 323, 33, 34, 51, 51, 51, 322, 32, 24, 33, + 34, 61, 61, 321, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, - 25, 25, 25, 25, 25, 25, 25, 25, 30, 39, - 39, 39, 39, 82, 147, 82, 82, 132, 152, 132, - 132, 319, 319, 139, 132, 139, 139, 144, 323, 144, - 144, 149, 167, 149, 149, 167, 161, 147, 161, 161, - 322, 152, 39, 321, 320, 149, 317, 39, 30, 37, - 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 25, 25, 25, 25, 25, 25, 25, 25, 35, 36, + 35, 36, 82, 110, 82, 132, 139, 132, 139, 318, + 144, 132, 144, 110, 35, 36, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 40, 40, 40, 40, 58, 58, 58, 58, - 212, 315, 313, 58, 67, 67, 67, 67, 312, 311, - 212, 67, 153, 153, 153, 153, 155, 155, 155, 155, - 157, 157, 157, 157, 162, 40, 162, 162, 310, 309, - 40, 44, 44, 44, 44, 308, 58, 164, 44, 164, - 164, 179, 306, 264, 67, 44, 179, 264, 264, 44, - 54, 166, 166, 166, 166, 201, 305, 201, 201, 228, - 301, 228, 228, 299, 179, 54, 228, 54, 235, 54, - - 235, 235, 54, 298, 54, 54, 54, 54, 54, 54, - 54, 293, 54, 66, 66, 291, 66, 66, 66, 66, + 37, 37, 37, 37, 37, 37, 37, 37, 39, 39, + 39, 40, 40, 40, 43, 43, 43, 44, 44, 44, + 43, 98, 167, 44, 101, 167, 58, 58, 58, 105, + 44, 43, 58, 147, 44, 152, 161, 149, 161, 149, + + 39, 178, 178, 40, 98, 39, 316, 101, 40, 54, + 149, 314, 105, 153, 153, 153, 147, 179, 152, 155, + 155, 155, 179, 313, 54, 58, 54, 162, 54, 162, + 163, 54, 163, 54, 54, 54, 54, 54, 54, 54, + 179, 54, 66, 66, 312, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 172, 172, 172, 172, - 288, 287, 286, 172, 186, 186, 186, 186, 285, 281, - 238, 186, 238, 238, 239, 239, 239, 239, 243, 280, - 243, 243, 245, 245, 245, 245, 247, 279, 247, 247, - 277, 249, 243, 249, 249, 276, 172, 185, 251, 185, + 66, 66, 66, 66, 67, 67, 67, 157, 157, 157, + 67, 164, 311, 164, 166, 166, 166, 172, 172, 172, - 251, 251, 273, 272, 186, 266, 185, 185, 185, 185, + 186, 186, 186, 172, 212, 201, 186, 201, 235, 228, + 235, 228, 319, 319, 212, 228, 238, 310, 238, 239, + 239, 239, 243, 67, 243, 245, 245, 245, 247, 249, + 247, 249, 250, 250, 250, 243, 172, 185, 185, 186, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 262, 260, 185, 199, 199, 199, + 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, + 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, + 185, 185, 185, 185, 185, 185, 185, 185, 185, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, + 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, - 230, 230, 257, 230, 230, 230, 230, 230, 230, 230, + 199, 230, 230, 309, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, - 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, - 230, 230, 230, 253, 253, 253, 253, 265, 255, 302, - 254, 265, 265, 302, 302, 265, 265, 265, 265, 265, - 265, 289, 289, 289, 289, 296, 296, 296, 296, 303, - 250, 244, 241, 237, 236, 233, 232, 303, 303, 303, - 303, 303, 303, 341, 341, 341, 341, 341, 341, 341, - 341, 341, 341, 341, 341, 341, 342, 342, 342, 342, - 342, 342, 342, 342, 342, 342, 342, 342, 342, 343, - 343, 343, 343, 343, 343, 343, 343, 343, 343, 343, - 343, 343, 344, 344, 344, 344, 344, 344, 344, 344, - - 344, 344, 344, 344, 344, 345, 345, 345, 345, 345, - 345, 345, 345, 345, 345, 345, 345, 345, 346, 346, - 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, - 346, 347, 347, 347, 347, 347, 347, 347, 347, 347, - 347, 347, 347, 347, 348, 348, 348, 348, 348, 348, - 348, 348, 348, 348, 348, 348, 348, 349, 349, 349, - 349, 349, 349, 349, 349, 349, 349, 349, 349, 349, - 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, - 350, 350, 350, 351, 351, 351, 351, 351, 351, 351, - 351, 351, 351, 351, 351, 351, 352, 352, 352, 352, - - 352, 352, 352, 352, 352, 352, 352, 352, 352, 353, - 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, - 353, 353, 354, 354, 354, 354, 354, 354, 354, 354, - 354, 354, 354, 354, 354, 355, 355, 355, 355, 355, - 355, 355, 355, 355, 355, 355, 355, 355, 356, 356, - 356, 356, 356, 356, 356, 356, 356, 356, 356, 356, - 356, 357, 357, 229, 222, 357, 357, 358, 358, 358, - 358, 358, 358, 358, 358, 358, 358, 358, 358, 358, - 359, 359, 359, 359, 359, 359, 359, 359, 359, 359, - 359, 359, 360, 360, 219, 218, 360, 360, 361, 361, - - 217, 361, 361, 361, 361, 361, 361, 206, 361, 361, - 361, 362, 362, 204, 362, 362, 362, 362, 362, 362, - 362, 362, 362, 362, 363, 363, 363, 363, 363, 363, - 363, 363, 363, 363, 363, 363, 363, 364, 198, 197, - 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, - 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, - 365, 365, 365, 366, 366, 193, 366, 366, 366, 366, - 366, 366, 366, 190, 181, 366, 367, 367, 177, 175, - 367, 367, 368, 368, 174, 368, 368, 368, 368, 368, - 368, 368, 368, 368, 368, 369, 369, 169, 163, 369, - - 369, 370, 370, 159, 370, 370, 370, 370, 370, 370, - 370, 370, 370, 370, 371, 371, 151, 371, 371, 371, - 371, 371, 371, 371, 371, 371, 371, 372, 372, 150, - 372, 372, 372, 372, 372, 372, 146, 372, 372, 372, - 373, 373, 142, 141, 137, 373, 373, 373, 373, 373, - 374, 374, 136, 374, 374, 374, 374, 374, 374, 374, - 374, 374, 374, 375, 375, 134, 126, 375, 375, 376, - 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, - 376, 376, 377, 377, 123, 377, 377, 117, 377, 377, - 377, 377, 377, 377, 377, 378, 378, 114, 378, 378, - - 113, 378, 378, 378, 378, 378, 378, 378, 379, 379, - 111, 102, 379, 379, 379, 379, 93, 379, 379, 379, - 379, 380, 380, 87, 380, 380, 380, 380, 380, 380, - 380, 380, 380, 380, 381, 381, 80, 381, 381, 381, - 381, 381, 381, 381, 79, 78, 381, 382, 382, 73, - 71, 382, 382, 383, 64, 63, 383, 383, 383, 383, - 383, 383, 383, 383, 383, 383, 384, 384, 55, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 385, - 385, 53, 385, 385, 385, 385, 385, 385, 385, 385, - 385, 385, 386, 386, 387, 387, 388, 388, 388, 388, - - 388, 388, 388, 388, 388, 388, 388, 388, 388, 389, - 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, - 389, 389, 390, 49, 45, 28, 390, 391, 391, 27, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 392, - 392, 16, 392, 392, 392, 392, 392, 392, 392, 392, - 392, 392, 393, 15, 10, 8, 393, 394, 394, 394, - 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, - 395, 7, 6, 5, 395, 396, 396, 396, 396, 396, - 396, 396, 396, 396, 396, 396, 396, 396, 397, 397, - 0, 0, 397, 397, 398, 398, 0, 0, 398, 398, - - 399, 0, 0, 0, 399, 400, 400, 400, 400, 400, - 400, 400, 400, 400, 400, 400, 400, 400, 401, 0, - 0, 0, 401, 402, 402, 0, 402, 402, 402, 402, - 402, 402, 402, 402, 402, 402, 403, 0, 0, 0, - 403, 340, 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340 + 230, 230, 230, 251, 307, 251, 253, 253, 253, 264, + 289, 289, 289, 264, 264, 265, 294, 294, 294, 265, + 265, 320, 320, 265, 265, 265, 265, 265, 265, 297, + 297, 297, 303, 306, 302, 300, 303, 303, 304, 299, + + 293, 291, 288, 287, 286, 285, 304, 304, 304, 304, + 304, 304, 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 343, 343, 343, 343, 343, + 343, 343, 343, 343, 343, 343, 343, 343, 344, 344, + 344, 344, 344, 344, 344, 344, 344, 344, 344, 344, + 344, 345, 345, 345, 345, 345, 345, 345, 345, 345, + 345, 345, 345, 345, 346, 346, 346, 346, 346, 346, + 346, 346, 346, 346, 346, 346, 346, 347, 347, 347, + 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, + 348, 348, 348, 348, 348, 348, 348, 348, 348, 348, + + 348, 348, 348, 349, 349, 349, 349, 349, 349, 349, + 349, 349, 349, 349, 349, 349, 350, 350, 350, 350, + 350, 350, 350, 350, 350, 350, 350, 350, 350, 351, + 351, 351, 351, 351, 351, 351, 351, 351, 351, 351, + 351, 351, 352, 352, 352, 352, 352, 352, 352, 352, + 352, 352, 352, 352, 352, 353, 353, 353, 353, 353, + 353, 353, 353, 353, 353, 353, 353, 353, 354, 354, + 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, + 354, 355, 355, 355, 355, 355, 355, 355, 355, 355, + 355, 355, 355, 355, 356, 356, 356, 356, 356, 356, + + 356, 356, 356, 356, 356, 356, 356, 357, 357, 357, + 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, + 358, 358, 281, 280, 358, 358, 359, 359, 359, 359, + 359, 359, 359, 359, 359, 359, 359, 359, 359, 360, + 360, 360, 360, 360, 360, 360, 360, 360, 360, 360, + 360, 361, 361, 279, 277, 361, 361, 362, 362, 276, + 362, 362, 362, 362, 362, 362, 273, 362, 362, 362, + 363, 363, 272, 363, 363, 363, 363, 363, 363, 363, + 363, 363, 363, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 365, 266, 262, 365, + + 365, 365, 365, 365, 365, 365, 365, 365, 365, 366, + 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, + 366, 366, 367, 367, 260, 367, 367, 367, 367, 367, + 367, 367, 257, 255, 367, 368, 368, 254, 244, 368, + 368, 369, 369, 241, 369, 369, 369, 369, 369, 369, + 369, 369, 369, 369, 370, 370, 237, 236, 370, 370, + 371, 371, 233, 371, 371, 371, 371, 371, 371, 371, + 371, 371, 371, 372, 372, 232, 372, 372, 372, 372, + 372, 372, 372, 372, 372, 372, 373, 373, 229, 373, + 373, 373, 373, 373, 373, 222, 373, 373, 373, 374, + + 374, 219, 218, 217, 374, 374, 374, 374, 374, 375, + 375, 206, 375, 375, 375, 375, 375, 375, 375, 375, + 375, 375, 376, 376, 204, 198, 376, 376, 377, 377, + 377, 377, 377, 377, 377, 377, 377, 377, 377, 377, + 377, 378, 378, 197, 378, 378, 193, 378, 378, 378, + 378, 378, 378, 378, 379, 379, 190, 379, 379, 181, + 379, 379, 379, 379, 379, 379, 379, 380, 380, 177, + 175, 380, 380, 380, 380, 174, 380, 380, 380, 380, + 381, 381, 169, 381, 381, 381, 381, 381, 381, 381, + 381, 381, 381, 382, 382, 159, 382, 382, 382, 382, + + 382, 382, 382, 151, 150, 382, 383, 383, 146, 142, + 383, 383, 384, 141, 137, 384, 384, 384, 384, 384, + 384, 384, 384, 384, 384, 385, 385, 136, 385, 385, + 385, 385, 385, 385, 385, 385, 385, 385, 386, 386, + 134, 386, 386, 386, 386, 386, 386, 386, 386, 386, + 386, 387, 387, 388, 388, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 391, 126, 123, 117, 391, 392, 392, 114, 392, + 392, 392, 392, 392, 392, 392, 392, 392, 393, 393, + + 113, 393, 393, 393, 393, 393, 393, 393, 393, 393, + 393, 394, 111, 102, 93, 394, 395, 395, 395, 395, + 395, 395, 395, 395, 395, 395, 395, 395, 395, 396, + 87, 80, 79, 396, 397, 397, 397, 397, 397, 397, + 397, 397, 397, 397, 397, 397, 397, 398, 398, 78, + 73, 398, 398, 399, 399, 71, 64, 399, 399, 400, + 63, 55, 53, 400, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 402, 49, 45, + 28, 402, 403, 403, 27, 403, 403, 403, 403, 403, + 403, 403, 403, 403, 403, 404, 16, 15, 10, 404, + + 8, 7, 6, 5, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341 } ; static yy_state_type yy_last_accepting_state; @@ -955,13 +950,13 @@ yy_match: while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = yy_def[yy_current_state]; - if ( yy_current_state >= 341 ) + if ( yy_current_state >= 342 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; ++yy_cp; } - while ( yy_current_state != 340 ); + while ( yy_current_state != 341 ); yy_cp = yy_last_accepting_cpos; yy_current_state = yy_last_accepting_state; @@ -984,27 +979,27 @@ do_action: /* this label is used only to access EOF actions */ goto yy_find_action; case 1: -# line 91 "scan.l" +# line 90 "scan.l" indented_code = true; BEGIN(CODEBLOCK); YY_BREAK case 2: -# line 92 "scan.l" +# line 91 "scan.l" ++linenum; /* treat as a comment */ YY_BREAK case 3: -# line 93 "scan.l" +# line 92 "scan.l" ECHO; BEGIN(C_COMMENT); YY_BREAK case 4: -# line 94 "scan.l" +# line 93 "scan.l" return ( SCDECL ); YY_BREAK case 5: -# line 95 "scan.l" +# line 94 "scan.l" return ( XSCDECL ); YY_BREAK case 6: -# line 96 "scan.l" +# line 95 "scan.l" { ++linenum; line_directive_out( stdout ); @@ -1013,11 +1008,11 @@ case 6: } YY_BREAK case 7: -# line 103 "scan.l" +# line 102 "scan.l" return ( WHITESPACE ); YY_BREAK case 8: -# line 105 "scan.l" +# line 104 "scan.l" { sectnum = 2; line_directive_out( stdout ); @@ -1026,14 +1021,14 @@ case 8: } YY_BREAK case 9: -# line 112 "scan.l" +# line 111 "scan.l" { pinpoint_message( "warning - %%used/%%unused have been deprecated" ); checking_used = REALLY_USED; BEGIN(USED_LIST); } YY_BREAK case 10: -# line 116 "scan.l" +# line 115 "scan.l" { checking_used = REALLY_NOT_USED; BEGIN(USED_LIST); pinpoint_message( "warning - %%used/%%unused have been deprecated" ); @@ -1041,7 +1036,7 @@ case 10: } YY_BREAK case 11: -# line 123 "scan.l" +# line 122 "scan.l" { #ifdef NOTDEF fprintf( stderr, @@ -1052,11 +1047,11 @@ case 11: } YY_BREAK case 12: -# line 132 "scan.l" +# line 131 "scan.l" /* ignore old lex directive */ YY_BREAK case 13: -# line 134 "scan.l" +# line 133 "scan.l" { ++linenum; xlation = @@ -1075,11 +1070,11 @@ case 13: } YY_BREAK case 14: -# line 151 "scan.l" +# line 150 "scan.l" synerr( "unrecognized '%' directive" ); YY_BREAK case 15: -# line 153 "scan.l" +# line 152 "scan.l" { (void) strcpy( nmstr, (char *) yytext ); didadef = false; @@ -1087,59 +1082,59 @@ case 15: } YY_BREAK case 16: -# line 159 "scan.l" +# line 158 "scan.l" RETURNNAME; YY_BREAK case 17: -# line 160 "scan.l" +# line 159 "scan.l" ++linenum; /* allows blank lines in section 1 */ YY_BREAK case 18: -# line 161 "scan.l" +# line 160 "scan.l" ++linenum; return ( '\n' ); YY_BREAK case 19: -# line 162 "scan.l" +# line 161 "scan.l" synerr( "illegal character" ); BEGIN(RECOVER); YY_BREAK case 20: -# line 165 "scan.l" +# line 164 "scan.l" ECHO; BEGIN(INITIAL); YY_BREAK case 21: -# line 166 "scan.l" +# line 165 "scan.l" ++linenum; ECHO; BEGIN(INITIAL); YY_BREAK case 22: -# line 167 "scan.l" +# line 166 "scan.l" ECHO; YY_BREAK case 23: -# line 168 "scan.l" +# line 167 "scan.l" ECHO; YY_BREAK case 24: -# line 169 "scan.l" +# line 168 "scan.l" ++linenum; ECHO; YY_BREAK case 25: -# line 172 "scan.l" +# line 171 "scan.l" ++linenum; BEGIN(INITIAL); YY_BREAK case 26: -# line 173 "scan.l" +# line 172 "scan.l" ECHO; CHECK_REJECT(yytext); YY_BREAK case 27: -# line 174 "scan.l" +# line 173 "scan.l" ECHO; CHECK_YYMORE(yytext); YY_BREAK case 28: -# line 175 "scan.l" +# line 174 "scan.l" ECHO; YY_BREAK case 29: -# line 176 "scan.l" +# line 175 "scan.l" { ++linenum; ECHO; @@ -1148,11 +1143,11 @@ case 29: } YY_BREAK case 30: -# line 184 "scan.l" +# line 183 "scan.l" /* separates name and definition */ YY_BREAK case 31: -# line 186 "scan.l" +# line 185 "scan.l" { (void) strcpy( (char *) nmdef, (char *) yytext ); @@ -1169,7 +1164,7 @@ case 31: } YY_BREAK case 32: -# line 201 "scan.l" +# line 200 "scan.l" { if ( ! didadef ) synerr( "incomplete name definition" ); @@ -1178,19 +1173,19 @@ case 32: } YY_BREAK case 33: -# line 208 "scan.l" +# line 207 "scan.l" ++linenum; BEGIN(INITIAL); RETURNNAME; YY_BREAK case 34: -# line 211 "scan.l" +# line 210 "scan.l" ++linenum; BEGIN(INITIAL); YY_BREAK case 35: -# line 212 "scan.l" +# line 211 "scan.l" YY_BREAK case 36: -# line 213 "scan.l" +# line 212 "scan.l" { if ( all_upper( yytext ) ) reject_really_used = checking_used; @@ -1199,7 +1194,7 @@ case 36: } YY_BREAK case 37: -# line 219 "scan.l" +# line 218 "scan.l" { if ( all_lower( yytext ) ) yymore_really_used = checking_used; @@ -1208,27 +1203,27 @@ case 37: } YY_BREAK case 38: -# line 225 "scan.l" +# line 224 "scan.l" synerr( "unrecognized %used/%unused construct" ); YY_BREAK case 39: -# line 228 "scan.l" +# line 227 "scan.l" ++linenum; BEGIN(INITIAL); YY_BREAK case 40: -# line 229 "scan.l" +# line 228 "scan.l" ++num_xlations; new_xlation = true; YY_BREAK case 41: -# line 230 "scan.l" +# line 229 "scan.l" synerr( "bad row in translation table" ); YY_BREAK case 42: -# line 231 "scan.l" +# line 230 "scan.l" /* ignore whitespace */ YY_BREAK case 43: -# line 233 "scan.l" +# line 232 "scan.l" { xlation[myesc( yytext )] = (new_xlation ? num_xlations : -num_xlations); @@ -1236,7 +1231,7 @@ case 43: } YY_BREAK case 44: -# line 238 "scan.l" +# line 237 "scan.l" { xlation[yytext[0]] = (new_xlation ? num_xlations : -num_xlations); @@ -1244,14 +1239,14 @@ case 44: } YY_BREAK case 45: -# line 244 "scan.l" +# line 243 "scan.l" ++linenum; YY_BREAK case 46: *yy_cp = yy_hold_char; /* undo effects of setting up yytext */ yy_c_buf_p = yy_cp -= 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ -# line 247 "scan.l" +# line 246 "scan.l" { ++linenum; ACTION_ECHO; @@ -1260,19 +1255,19 @@ YY_DO_BEFORE_ACTION; /* set up yytext again */ } YY_BREAK case 47: -# line 254 "scan.l" +# line 253 "scan.l" ++linenum; ACTION_ECHO; YY_BREAK case YY_STATE_EOF(SECT2PROLOG): -# line 256 "scan.l" +# line 255 "scan.l" MARK_END_OF_PROLOG; yyterminate(); YY_BREAK case 49: -# line 258 "scan.l" +# line 257 "scan.l" ++linenum; /* allow blank lines in section 2 */ YY_BREAK case 50: -# line 260 "scan.l" +# line 259 "scan.l" { indented_code = (yytext[0] != '%'); doing_codeblock = true; @@ -1285,37 +1280,37 @@ case 50: } YY_BREAK case 51: -# line 271 "scan.l" +# line 270 "scan.l" BEGIN(SC); return ( '<' ); YY_BREAK case 52: -# line 272 "scan.l" +# line 271 "scan.l" return ( '^' ); YY_BREAK case 53: -# line 273 "scan.l" +# line 272 "scan.l" BEGIN(QUOTE); return ( '"' ); YY_BREAK case 54: *yy_cp = yy_hold_char; /* undo effects of setting up yytext */ yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ -# line 274 "scan.l" +# line 273 "scan.l" BEGIN(NUM); return ( '{' ); YY_BREAK case 55: -# line 275 "scan.l" +# line 274 "scan.l" BEGIN(BRACEERROR); YY_BREAK case 56: *yy_cp = yy_hold_char; /* undo effects of setting up yytext */ yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ -# line 276 "scan.l" +# line 275 "scan.l" return ( '$' ); YY_BREAK case 57: -# line 278 "scan.l" +# line 277 "scan.l" { bracelevel = 1; BEGIN(PERCENT_BRACE_ACTION); @@ -1323,11 +1318,11 @@ case 57: } YY_BREAK case 58: -# line 283 "scan.l" +# line 282 "scan.l" continued_action = true; ++linenum; return ( '\n' ); YY_BREAK case 59: -# line 285 "scan.l" +# line 284 "scan.l" { /* this rule is separate from the one below because * otherwise we get variable trailing context, so @@ -1343,7 +1338,7 @@ case 60: *yy_cp = yy_hold_char; /* undo effects of setting up yytext */ yy_c_buf_p = yy_cp -= 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ -# line 296 "scan.l" +# line 295 "scan.l" { bracelevel = 0; continued_action = false; @@ -1352,15 +1347,15 @@ YY_DO_BEFORE_ACTION; /* set up yytext again */ } YY_BREAK case 61: -# line 303 "scan.l" +# line 302 "scan.l" ++linenum; return ( '\n' ); YY_BREAK case 62: -# line 305 "scan.l" +# line 304 "scan.l" return ( EOF_OP ); YY_BREAK case 63: -# line 307 "scan.l" +# line 306 "scan.l" { sectnum = 3; BEGIN(SECT3); @@ -1368,7 +1363,7 @@ case 63: } YY_BREAK case 64: -# line 313 "scan.l" +# line 312 "scan.l" { int cclval; @@ -1399,7 +1394,7 @@ case 64: } YY_BREAK case 65: -# line 342 "scan.l" +# line 341 "scan.l" { register Char *nmdefptr; Char *ndlookup(); @@ -1420,54 +1415,54 @@ case 65: } YY_BREAK case 66: -# line 361 "scan.l" +# line 360 "scan.l" return ( yytext[0] ); YY_BREAK case 67: -# line 362 "scan.l" +# line 361 "scan.l" RETURNCHAR; YY_BREAK case 68: -# line 363 "scan.l" +# line 362 "scan.l" ++linenum; return ( '\n' ); YY_BREAK case 69: -# line 366 "scan.l" +# line 365 "scan.l" return ( ',' ); YY_BREAK case 70: -# line 367 "scan.l" +# line 366 "scan.l" BEGIN(SECT2); return ( '>' ); YY_BREAK case 71: *yy_cp = yy_hold_char; /* undo effects of setting up yytext */ yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ -# line 368 "scan.l" +# line 367 "scan.l" BEGIN(CARETISBOL); return ( '>' ); YY_BREAK case 72: -# line 369 "scan.l" +# line 368 "scan.l" RETURNNAME; YY_BREAK case 73: -# line 370 "scan.l" +# line 369 "scan.l" synerr( "bad start condition name" ); YY_BREAK case 74: -# line 372 "scan.l" +# line 371 "scan.l" BEGIN(SECT2); return ( '^' ); YY_BREAK case 75: -# line 375 "scan.l" +# line 374 "scan.l" RETURNCHAR; YY_BREAK case 76: -# line 376 "scan.l" +# line 375 "scan.l" BEGIN(SECT2); return ( '"' ); YY_BREAK case 77: -# line 378 "scan.l" +# line 377 "scan.l" { synerr( "missing quote" ); BEGIN(SECT2); @@ -1479,56 +1474,56 @@ case 78: *yy_cp = yy_hold_char; /* undo effects of setting up yytext */ yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ -# line 386 "scan.l" +# line 385 "scan.l" BEGIN(CCL); return ( '^' ); YY_BREAK case 79: *yy_cp = yy_hold_char; /* undo effects of setting up yytext */ yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ -# line 387 "scan.l" +# line 386 "scan.l" return ( '^' ); YY_BREAK case 80: -# line 388 "scan.l" +# line 387 "scan.l" BEGIN(CCL); yylval = '-'; return ( CHAR ); YY_BREAK case 81: -# line 389 "scan.l" +# line 388 "scan.l" BEGIN(CCL); RETURNCHAR; YY_BREAK case 82: *yy_cp = yy_hold_char; /* undo effects of setting up yytext */ yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ -# line 391 "scan.l" +# line 390 "scan.l" return ( '-' ); YY_BREAK case 83: -# line 392 "scan.l" +# line 391 "scan.l" RETURNCHAR; YY_BREAK case 84: -# line 393 "scan.l" +# line 392 "scan.l" BEGIN(SECT2); return ( ']' ); YY_BREAK case 85: -# line 396 "scan.l" +# line 395 "scan.l" { yylval = myctoi( yytext ); return ( NUMBER ); } YY_BREAK case 86: -# line 401 "scan.l" +# line 400 "scan.l" return ( ',' ); YY_BREAK case 87: -# line 402 "scan.l" +# line 401 "scan.l" BEGIN(SECT2); return ( '}' ); YY_BREAK case 88: -# line 404 "scan.l" +# line 403 "scan.l" { synerr( "bad character inside {}'s" ); BEGIN(SECT2); @@ -1536,7 +1531,7 @@ case 88: } YY_BREAK case 89: -# line 410 "scan.l" +# line 409 "scan.l" { synerr( "missing }" ); BEGIN(SECT2); @@ -1545,37 +1540,37 @@ case 89: } YY_BREAK case 90: -# line 418 "scan.l" +# line 417 "scan.l" synerr( "bad name in {}'s" ); BEGIN(SECT2); YY_BREAK case 91: -# line 419 "scan.l" +# line 418 "scan.l" synerr( "missing }" ); ++linenum; BEGIN(SECT2); YY_BREAK case 92: -# line 422 "scan.l" +# line 421 "scan.l" bracelevel = 0; YY_BREAK case 93: -# line 423 "scan.l" +# line 422 "scan.l" { ACTION_ECHO; CHECK_REJECT(yytext); } YY_BREAK case 94: -# line 427 "scan.l" +# line 426 "scan.l" { ACTION_ECHO; CHECK_YYMORE(yytext); } YY_BREAK case 95: -# line 431 "scan.l" +# line 430 "scan.l" ACTION_ECHO; YY_BREAK case 96: -# line 432 "scan.l" +# line 431 "scan.l" { ++linenum; ACTION_ECHO; @@ -1592,35 +1587,35 @@ case 96: YY_BREAK /* Reject and YYmore() are checked for above, in PERCENT_BRACE_ACTION */ case 97: -# line 448 "scan.l" +# line 447 "scan.l" ACTION_ECHO; ++bracelevel; YY_BREAK case 98: -# line 449 "scan.l" +# line 448 "scan.l" ACTION_ECHO; --bracelevel; YY_BREAK case 99: -# line 450 "scan.l" +# line 449 "scan.l" ACTION_ECHO; YY_BREAK case 100: -# line 451 "scan.l" +# line 450 "scan.l" ACTION_ECHO; YY_BREAK case 101: -# line 452 "scan.l" +# line 451 "scan.l" ACTION_ECHO; BEGIN(ACTION_COMMENT); YY_BREAK case 102: -# line 453 "scan.l" +# line 452 "scan.l" ACTION_ECHO; /* character constant */ YY_BREAK case 103: -# line 454 "scan.l" +# line 453 "scan.l" ACTION_ECHO; BEGIN(ACTION_STRING); YY_BREAK case 104: -# line 455 "scan.l" +# line 454 "scan.l" { ++linenum; ACTION_ECHO; @@ -1632,67 +1627,67 @@ case 104: } YY_BREAK case 105: -# line 464 "scan.l" +# line 463 "scan.l" ACTION_ECHO; YY_BREAK case 106: -# line 466 "scan.l" +# line 465 "scan.l" ACTION_ECHO; BEGIN(ACTION); YY_BREAK case 107: -# line 467 "scan.l" +# line 466 "scan.l" ACTION_ECHO; YY_BREAK case 108: -# line 468 "scan.l" +# line 467 "scan.l" ACTION_ECHO; YY_BREAK case 109: -# line 469 "scan.l" +# line 468 "scan.l" ++linenum; ACTION_ECHO; YY_BREAK case 110: -# line 470 "scan.l" +# line 469 "scan.l" ACTION_ECHO; YY_BREAK case 111: -# line 472 "scan.l" +# line 471 "scan.l" ACTION_ECHO; YY_BREAK case 112: -# line 473 "scan.l" +# line 472 "scan.l" ACTION_ECHO; YY_BREAK case 113: -# line 474 "scan.l" +# line 473 "scan.l" ++linenum; ACTION_ECHO; YY_BREAK case 114: -# line 475 "scan.l" +# line 474 "scan.l" ACTION_ECHO; BEGIN(ACTION); YY_BREAK case 115: -# line 476 "scan.l" +# line 475 "scan.l" ACTION_ECHO; YY_BREAK case YY_STATE_EOF(ACTION): case YY_STATE_EOF(ACTION_COMMENT): case YY_STATE_EOF(ACTION_STRING): -# line 478 "scan.l" +# line 477 "scan.l" { synerr( "EOF encountered inside an action" ); yyterminate(); } YY_BREAK case 117: -# line 484 "scan.l" +# line 483 "scan.l" { yylval = myesc( yytext ); return ( CHAR ); } YY_BREAK case 118: -# line 489 "scan.l" +# line 488 "scan.l" { yylval = myesc( yytext ); BEGIN(CCL); @@ -1700,11 +1695,11 @@ case 118: } YY_BREAK case 119: -# line 496 "scan.l" +# line 495 "scan.l" ECHO; YY_BREAK case 120: -# line 497 "scan.l" +# line 496 "scan.l" YY_FATAL_ERROR( "flex scanner jammed" ); YY_BREAK case YY_STATE_EOF(INITIAL): @@ -1896,7 +1891,7 @@ static int yy_get_next_buffer() if ( yy_n_chars == 0 ) { - if ( number_to_move == 1 ) + if ( number_to_move - YY_MORE_ADJ == 1 ) { ret_val = EOB_ACT_END_OF_FILE; yy_current_buffer->yy_eof_status = EOF_DONE; @@ -1957,7 +1952,7 @@ static yy_state_type yy_get_previous_state() while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = yy_def[yy_current_state]; - if ( yy_current_state >= 341 ) + if ( yy_current_state >= 342 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; @@ -1993,11 +1988,11 @@ register yy_state_type yy_current_state; while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = yy_def[yy_current_state]; - if ( yy_current_state >= 341 ) + if ( yy_current_state >= 342 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; - yy_is_jam = (yy_current_state == 340); + yy_is_jam = (yy_current_state == 341); return ( yy_is_jam ? 0 : yy_current_state ); } @@ -2251,7 +2246,7 @@ FILE *file; b->yy_eof_status = EOF_NOT_SEEN; } -# line 497 "scan.l" +# line 496 "scan.l" diff --git a/usr.bin/lex/lex.1 b/usr.bin/lex/lex.1 index c67df0a397..685d3da25d 100644 --- a/usr.bin/lex/lex.1 +++ b/usr.bin/lex/lex.1 @@ -1,581 +1,545 @@ -.\" Copyright (c) 1990 The Regents of the University of California. -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)lex.1 5.13 (Berkeley) 7/24/91 -.\" -.Dd July 24, 1991 -.Dt LEX 1 -.Os -.Sh NAME -.Nm lex -.Nd fast lexical analyzer generator -.Sh SYNOPSIS -.Nm lex -.Oo -.Op Fl bcdfinpstvFILT8 -.Fl C Ns Ns Op Cm efmF -.Fl S Ns Ns Ar skeleton -.Oc -.Op Ar -.Sh DESCRIPTION -.Nm Lex +.TH FLEX 1 "26 May 1990" "Version 2.3" +.SH NAME +flex - fast lexical analyzer generator +.SH SYNOPSIS +.B flex +.B [-bcdfinpstvFILT8 -C[efmF] -Sskeleton] +.I [filename ...] +.SH DESCRIPTION +.I flex is a tool for generating -.Ar scanners : +.I scanners: programs which recognized lexical patterns in text. -.Nm Lex +.I flex reads the given input files, or its standard input if no file names are given, for a description of a scanner to generate. The description is in the form of pairs of regular expressions and C code, called -.Em rules . -.Nm Lex +.I rules. flex generates as output a C source file, -.Pa lex.yy.c , +.B lex.yy.c, which defines a routine -.Fn yylex . +.B yylex(). This file is compiled and linked with the -.Fl lfl +.B -lfl library to produce an executable. When the executable is run, it analyzes its input for occurrences of the regular expressions. Whenever it finds one, it executes the corresponding C code. -.Pp +.LP For full documentation, see -.Em Lexdoc . +.B flexdoc(1). This manual entry is intended for use as a quick reference. -.Sh OPTIONS -.Nm Lex +.SH OPTIONS +.I flex has the following options: -.Bl -tag -width Ds -.It Fl b +.TP +.B -b Generate backtracking information to -.Va lex.backtrack . +.I lex.backtrack. This is a list of scanner states which require backtracking and the input characters on which they do so. By adding rules one can remove backtracking states. If all backtracking states are eliminated and -.Fl f +.B -f or -.Fl F +.B -F is used, the generated scanner will run faster. -.It Fl c -is a do-nothing, deprecated option included for -.Tn POSIX -compliance. -.Pp -.Ar NOTE : +.TP +.B -c +is a do-nothing, deprecated option included for POSIX compliance. +.IP +.B NOTE: in previous releases of -.Nm Lex -.Op Fl c +.I flex +.B -c specified table-compression options. This functionality is now given by the -.Fl C +.B -C flag. To ease the the impact of this change, when -.Nm lex +.I flex encounters -.Fl c, +.B -c, it currently issues a warning message and assumes that -.Fl C +.B -C was desired instead. In the future this "promotion" of -.Fl c +.B -c to -.Fl C -will go away in the name of full -.Tn POSIX -compliance (unless -the -.Tn POSIX -meaning is removed first). -.It Fl d -Makes the generated scanner run in -.Ar debug +.B -C +will go away in the name of full POSIX compliance (unless +the POSIX meaning is removed first). +.TP +.B -d +makes the generated scanner run in +.I debug mode. Whenever a pattern is recognized and the global -.Va yy_Lex_debug +.B yy_flex_debug is non-zero (which is the default), the scanner will write to -.Li stderr +.I stderr a line of the form: -.Pp -.Dl --accepting rule at line 53 ("the matched text") -.Pp +.nf + + --accepting rule at line 53 ("the matched text") + +.fi The line number refers to the location of the rule in the file -defining the scanner (i.e., the file that was fed to lex). Messages +defining the scanner (i.e., the file that was fed to flex). Messages are also generated when the scanner backtracks, accepts the default rule, reaches the end of its input buffer (or encounters -a -.Tn NUL ; -the two look the same as far as the scanner's concerned), +a NUL; the two look the same as far as the scanner's concerned), or reaches an end-of-file. -.It Fl f -Specifies (take your pick) -.Em full table +.TP +.B -f +specifies (take your pick) +.I full table or -.Em fast scanner . +.I fast scanner. No table compression is done. The result is large but fast. This option is equivalent to -.Fl Cf +.B -Cf (see below). -.It Fl i -Instructs -.Nm lex +.TP +.B -i +instructs +.I flex to generate a -.Em case-insensitive +.I case-insensitive scanner. The case of letters given in the -.Nm lex +.I flex input patterns will be ignored, and tokens in the input will be matched regardless of case. The matched text given in -.Va yytext +.I yytext will have the preserved case (i.e., it will not be folded). -.It Fl n -Is another do-nothing, deprecated option included only for -.Tn POSIX -compliance. -.It Fl p -Generates a performance report to stderr. The report +.TP +.B -n +is another do-nothing, deprecated option included only for +POSIX compliance. +.TP +.B -p +generates a performance report to stderr. The report consists of comments regarding features of the -.Nm lex +.I flex input file which will cause a loss of performance in the resulting scanner. -.It Fl s -Causes the -.Ar default rule +.TP +.B -s +causes the +.I default rule (that unmatched scanner input is echoed to -.Ar stdout ) +.I stdout) to be suppressed. If the scanner encounters input that does not match any of its rules, it aborts with an error. -.It Fl t -Instructs -.Nm lex +.TP +.B -t +instructs +.I flex to write the scanner it generates to standard output instead of -.Pa lex.yy.c . -.It Fl v -Specifies that -.Nm lex +.B lex.yy.c. +.TP +.B -v +specifies that +.I flex should write to -.Li stderr +.I stderr a summary of statistics regarding the scanner it generates. -.It Fl F -Specifies that the -.Em fast +.TP +.B -F +specifies that the +.ul +fast scanner table representation should be used. This representation is about as fast as the full table representation -.Pq Fl f , +.ul +(-f), and for some sets of patterns will be considerably smaller (and for others, larger). See -.Em Lexdoc +.B flexdoc(1) for details. -.Pp +.IP This option is equivalent to -.Fl CF +.B -CF (see below). -.It Fl I -Instructs -.Nm lex +.TP +.B -I +instructs +.I flex to generate an -.Em interactive +.I interactive scanner, that is, a scanner which stops immediately rather than looking ahead if it knows that the currently scanned text cannot be part of a longer rule's match. Again, see -.Em Lexdoc +.B flexdoc(1) for details. -.Pp +.IP Note, -.Fl I +.B -I cannot be used in conjunction with -.Em full +.I full or -.Em fast tables , +.I fast tables, i.e., the -.Fl f , F , Cf , +.B -f, -F, -Cf, or -.Fl CF +.B -CF flags. -.It Fl L -Instructs -.Nm lex +.TP +.B -L +instructs +.I flex not to generate -.Li #line +.B #line directives in -.Pa lex.yy.c . +.B lex.yy.c. The default is to generate such directives so error messages in the actions will be correctly located with respect to the original -.Nm lex +.I flex input file, and not to the fairly meaningless line numbers of -.Pa lex.yy.c . -.It Fl T -Makes -.Nm lex +.B lex.yy.c. +.TP +.B -T +makes +.I flex run in -.Em trace +.I trace mode. It will generate a lot of messages to -.Li stdout +.I stdout concerning the form of the input and the resultant non-deterministic and deterministic finite automata. This option is mostly for use in maintaining -.Nm lex . -.It Fl 8 -Instructs -.Nm lex +.I flex. +.TP +.B -8 +instructs +.I flex to generate an 8-bit scanner. On some sites, this is the default. On others, the default is 7-bit characters. To see which is the case, check the verbose -.Pq Fl v +.B (-v) output for "equivalence classes created". If the denominator of the number shown is 128, then by default -.Nm lex +.I flex is generating 7-bit characters. If it is 256, then the default is 8-bit characters. -.It Fl C Ns Op Cm efmF -Controls the degree of table compression. The default setting is -.Fl Cem . -.Pp -.Bl -tag -width Ds -.It Fl C -A lone -.Fl C -specifies that the scanner tables should be compressed but neither -equivalence classes nor meta-equivalence classes should be used. -.It Fl \&Ce -Directs -.Nm lex +.TP +.B -C[efmF] +controls the degree of table compression. +.IP +.B -Ce +directs +.I flex to construct -.Em equivalence classes , +.I equivalence classes, i.e., sets of characters which have identical lexical properties. Equivalence classes usually give dramatic reductions in the final table/object file sizes (typically a factor of 2-5) and are pretty cheap performance-wise (one array look-up per character scanned). -.It Fl \&Cf -Specifies that the -.Em full +.IP +.B -Cf +specifies that the +.I full scanner tables should be generated - -.Nm lex +.I flex should not compress the tables by taking advantages of similar transition functions for different states. -.It Fl \&CF -Specifies that the alternate fast scanner representation (described in -.Em Lexdoc ) +.IP +.B -CF +specifies that the alternate fast scanner representation (described in +.B flexdoc(1)) should be used. -.It Fl \&Cm -Directs -.Nm lex +.IP +.B -Cm +directs +.I flex to construct -.Em meta-equivalence classes , +.I meta-equivalence classes, which are sets of equivalence classes (or characters, if equivalence classes are not being used) that are commonly used together. Meta-equivalence classes are often a big win when using compressed tables, but they have a moderate performance impact (one or two "if" tests and one array look-up per character scanned). -.It Fl Cem -(Default) -Generate both equivalence classes -and meta-equivalence classes. This setting provides the highest -degree of table compression. -.El -.Pp -Faster-executing scanners can be traded off at the -cost of larger tables with -the following generally being true: -.Bd -ragged -offset center -slowest & smallest - -Cem - -Cm - -Ce - -C - -C{f,F}e - -C{f,F} -fastest & largest -.Ed -.Pp -.Fl C -options are not cumulative; whenever the flag is encountered, the -previous -C settings are forgotten. -.Pp +.IP +A lone +.B -C +specifies that the scanner tables should be compressed but neither +equivalence classes nor meta-equivalence classes should be used. +.IP The options -.Fl \&Cf +.B -Cf or -.Fl \&CF +.B -CF and -.Fl \&Cm +.B -Cm do not make sense together - there is no opportunity for meta-equivalence classes if the table is not being compressed. Otherwise the options may be freely mixed. -.It Fl S Ns Ar skeleton_file -Overrides the default skeleton file from which -.Nm lex -constructs its scanners. Useful for -.Nm lex +.IP +The default setting is +.B -Cem, +which specifies that +.I flex +should generate equivalence classes +and meta-equivalence classes. This setting provides the highest +degree of table compression. You can trade off +faster-executing scanners at the cost of larger tables with +the following generally being true: +.nf + + slowest & smallest + -Cem + -Cm + -Ce + -C + -C{f,F}e + -C{f,F} + fastest & largest + +.fi +.IP +.B -C +options are not cumulative; whenever the flag is encountered, the +previous -C settings are forgotten. +.TP +.B -Sskeleton_file +overrides the default skeleton file from which +.I flex +constructs its scanners. You'll never need this option unless you are doing +.I flex maintenance or development. -.El -.Sh SUMMARY OF LEX REGULAR EXPRESSIONS +.SH SUMMARY OF FLEX REGULAR EXPRESSIONS The patterns in the input are written using an extended set of regular expressions. These are: -.Pp -.Bl -tag -width 10n -compact -.It Li x -Match the character 'x'. -.It Li \&. -Any character except newline. -.It Op Li xyz -A "character class"; in this case, the pattern -matches either an 'x', a 'y', or a 'z'. -.It Op Li abj-oZ -A "character class" with a range in it; matches -an 'a', a 'b', any letter from 'j' through 'o', -or a 'Z'. -.It Op Li ^A-Z -A "negated character class", i.e., any character -but those in the class. In this case, any -character -.Em except -an uppercase letter. -.It Op Li ^A-Z\en -Any character -.Em except -an uppercase letter or -a newline. -.It Li r* -Zero or more r's, where r is any regular expression. -.It Li r+ -One or more r's. -.It Li r? -Zero or one r's (that is, "an optional r"). -.It Li r{2,5} -Anywhere from two to five r's. -.It Li r{2,} -Two or more r's. -.It Li r{4} -Exactly 4 r's. -.It Li {name} -The expansion of the "name" definition -(see above). -.It Xo -.Oo Li xyz Oc Ns Li "\e\&\*qfoo" -.Xc -The literal string: -[xyz]\*qfoo. -.It Li \&\eX -If X is an 'a', 'b', 'f', 'n', 'r', 't', or 'v', -then the -.Tn ANSI-C -interpretation of \ex. -Otherwise, a literal 'X' (used to escape -operators such as '*'). -.It Li \&\e123 -The character with octal value 123. -.It Li \&\ex2a -The character with hexadecimal value 2a. -.It Li (r) -Match an r; parentheses are used to override -precedence (see below). -.It Li rs -The regular expression r followed by the -regular expression s; called "concatenation". -.It Li rs -Either an r or an s. -.It Li r/s -An r but only if it is followed by an s. The -s is not part of the matched text. This type -of pattern is called as "trailing context". -.It Li \&^r -An r, but only at the beginning of a line. -.It Li r$ -An r, but only at the end of a line. Equivalent -to "r/\en". -.It Li <s>r -An r, but only in start condition s (see -below for discussion of start conditions). -.It Li <s1,s2,s3>r -Same, but in any of start conditions s1, -s2, or s3. -.It Li <<EOF>> -An end-of-file. -.It Li <s1,s2><<EOF>> -An end-of-file when in start condition s1 or s2. -.El +.nf + + x match the character 'x' + . any character except newline + [xyz] a "character class"; in this case, the pattern + matches either an 'x', a 'y', or a 'z' + [abj-oZ] a "character class" with a range in it; matches + an 'a', a 'b', any letter from 'j' through 'o', + or a 'Z' + [^A-Z] a "negated character class", i.e., any character + but those in the class. In this case, any + character EXCEPT an uppercase letter. + [^A-Z\\n] any character EXCEPT an uppercase letter or + a newline + r* zero or more r's, where r is any regular expression + r+ one or more r's + r? zero or one r's (that is, "an optional r") + r{2,5} anywhere from two to five r's + r{2,} two or more r's + r{4} exactly 4 r's + {name} the expansion of the "name" definition + (see above) + "[xyz]\\"foo" + the literal string: [xyz]"foo + \\X if X is an 'a', 'b', 'f', 'n', 'r', 't', or 'v', + then the ANSI-C interpretation of \\x. + Otherwise, a literal 'X' (used to escape + operators such as '*') + \\123 the character with octal value 123 + \\x2a the character with hexadecimal value 2a + (r) match an r; parentheses are used to override + precedence (see below) + + + rs the regular expression r followed by the + regular expression s; called "concatenation" + + + r|s either an r or an s + + + r/s an r but only if it is followed by an s. The + s is not part of the matched text. This type + of pattern is called as "trailing context". + ^r an r, but only at the beginning of a line + r$ an r, but only at the end of a line. Equivalent + to "r/\\n". + + + <s>r an r, but only in start condition s (see + below for discussion of start conditions) + <s1,s2,s3>r + same, but in any of start conditions s1, + s2, or s3 + + + <<EOF>> an end-of-file + <s1,s2><<EOF>> + an end-of-file when in start condition s1 or s2 + +.fi The regular expressions listed above are grouped according to precedence, from highest precedence at the top to lowest at the bottom. Those grouped together have equal precedence. -.Pp +.LP Some notes on patterns: -.Pp +.IP - Negated character classes -.Ar match newlines -unless "\en" (or an equivalent escape sequence) is one of the +.I match newlines +unless "\\n" (or an equivalent escape sequence) is one of the characters explicitly present in the negated character class -(e.g., " [^A-Z\en] "). -.Pp +(e.g., "[^A-Z\\n]"). +.IP - A rule can have at most one instance of trailing context (the '/' operator or the '$' operator). The start condition, '^', and "<<EOF>>" patterns can only occur at the beginning of a pattern, and, as well as with '/' and '$', cannot be grouped inside parentheses. The following are all illegal: -.Pp -.Bd -literal -offset indent -foo/bar$ -foo(bar$) -foo^bar -<sc1>foo<sc2>bar -.Ed -.Sh SUMMARY OF SPECIAL ACTIONS +.nf + + foo/bar$ + foo|(bar$) + foo|^bar + <sc1>foo<sc2>bar + +.fi +.SH SUMMARY OF SPECIAL ACTIONS In addition to arbitrary C code, the following can appear in actions: -.Bl -tag -width Fl -.It Ic ECHO -Copies -.Va yytext -to the scanner's output. -.It Ic BEGIN -Followed by the name of a start condition places the scanner in the +.IP - +.B ECHO +copies yytext to the scanner's output. +.IP - +.B BEGIN +followed by the name of a start condition places the scanner in the corresponding start condition. -.It Ic REJECT -Directs the scanner to proceed on to the "second best" rule which matched the +.IP - +.B REJECT +directs the scanner to proceed on to the "second best" rule which matched the input (or a prefix of the input). -.Va yytext +.B yytext and -.Va yyleng +.B yyleng are set up appropriately. Note that -.Ic REJECT +.B REJECT is a particularly expensive feature in terms scanner performance; if it is used in -.Em any +.I any of the scanner's actions it will slow down -.Em all +.I all of the scanner's matching. Furthermore, -.Ic REJECT +.B REJECT cannot be used with the -.Fl f +.I -f or -.Fl F +.I -F options. -.Pp +.IP Note also that unlike the other special actions, -.Ic REJECT +.B REJECT is a -.Em branch ; +.I branch; code immediately following it in the action will -.Em not +.I not be executed. -.It Fn yymore +.IP - +.B yymore() tells the scanner that the next time it matches a rule, the corresponding token should be -.Em appended +.I appended onto the current value of -.Va yytext +.B yytext rather than replacing it. -.It Fn yyless \&n +.IP - +.B yyless(n) returns all but the first -.Ar n +.I n characters of the current token back to the input stream, where they will be rescanned when the scanner looks for the next match. -.Va yytext +.B yytext and -.Va yyleng +.B yyleng are adjusted appropriately (e.g., -.Va yyleng +.B yyleng will now be equal to -.Ar n ) . -.It Fn unput c +.I n +). +.IP - +.B unput(c) puts the character -.Ar c +.I c back onto the input stream. It will be the next character scanned. -.It Fn input +.IP - +.B input() reads the next character from the input stream (this routine is called -.Fn yyinput +.B yyinput() if the scanner is compiled using -.Em C \&+\&+ ) . -.It Fn yyterminate +.B C++). +.IP - +.B yyterminate() can be used in lieu of a return statement in an action. It terminates the scanner and returns a 0 to the scanner's caller, indicating "all done". -.Pp +.IP By default, -.Fn yyterminate +.B yyterminate() is also called when an end-of-file is encountered. It is a macro and may be redefined. -.It Ic YY_NEW_FILE +.IP - +.B YY_NEW_FILE is an action available only in <<EOF>> rules. It means "Okay, I've set up a new input file, continue scanning". -.It Fn yy_create_buffer file size +.IP - +.B yy_create_buffer( file, size ) takes a -.Ic FILE +.I FILE pointer and an integer -.Ar size . +.I size. It returns a YY_BUFFER_STATE handle to a new input buffer large enough to accomodate -.Ar size +.I size characters and associated with the given file. When in doubt, use -.Ar YY_BUF_SIZE +.B YY_BUF_SIZE for the size. -.It Fn yy_switch_to_buffer new_buffer +.IP - +.B yy_switch_to_buffer( new_buffer ) switches the scanner's processing to scan for tokens from the given buffer, which must be a YY_BUFFER_STATE. -.It Fn yy_delete_buffer buffer +.IP - +.B yy_delete_buffer( buffer ) deletes the given buffer. -.El -.Sh VALUES AVAILABLE TO THE USER -.Bl -tag -width Fl -.It Va char \&*yytext +.SH VALUES AVAILABLE TO THE USER +.IP - +.B char *yytext holds the text of the current token. It may not be modified. -.It Va int yyleng +.IP - +.B int yyleng holds the length of the current token. It may not be modified. -.It Va FILE \&*yyin +.IP - +.B FILE *yyin is the file which by default -.Nm lex +.I flex reads from. It may be redefined but doing so only makes sense before scanning begins. Changing it in the middle of scanning will have unexpected results since -.Nm lex +.I flex buffers its input. Once scanning terminates because an end-of-file has been seen, -.Fn "void yyrestart" "FILE *new_file" +.B +void yyrestart( FILE *new_file ) may be called to point -.Va yyin +.I yyin at the new input file. -.It Va FILE \&*yyout +.IP - +.B FILE *yyout is the file to which -.Ar ECHO +.B ECHO actions are done. It can be reassigned by the user. -.It Va YY_CURRENT_BUFFER +.IP - +.B YY_CURRENT_BUFFER returns a -YY_BUFFER_STATE +.B YY_BUFFER_STATE handle to the current buffer. -.El -.Sh MACROS THE USER CAN REDEFINE -.Bl -tag -width Fl -.It Va YY_DECL +.SH MACROS THE USER CAN REDEFINE +.IP - +.B YY_DECL controls how the scanning routine is declared. By default, it is "int yylex()", or, if prototypes are being used, "int yylex(void)". This definition may be changed by redefining @@ -583,250 +547,235 @@ the "YY_DECL" macro. Note that if you give arguments to the scanning routine using a K&R-style/non-prototyped function declaration, you must terminate the definition with a semi-colon (;). -.It Va YY_INPUT +.IP - The nature of how the scanner gets its input can be controlled by redefining the -YY_INPUT +.B YY_INPUT macro. YY_INPUT's calling sequence is "YY_INPUT(buf,result,max_size)". Its action is to place up to -.Ar max _size +.I max_size characters in the character array -.Ar buf +.I buf and return in the integer variable -.Ar result +.I result either the number of characters read or the constant YY_NULL (0 on Unix systems) to indicate EOF. The default YY_INPUT reads from the global file-pointer "yyin". A sample redefinition of YY_INPUT (in the definitions section of the input file): -.Bd -literal -offset indent -%{ -#undef YY_INPUT -#define YY_INPUT(buf,result,max_size) \e - result = ((buf[0] = getchar()) == EOF) ? YY_NULL : 1; -%} -.Ed -.It Va YY_INPUT +.nf + + %{ + #undef YY_INPUT + #define YY_INPUT(buf,result,max_size) \\ + { \\ + int c = getchar(); \\ + result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \\ + } + %} + +.fi +.IP - When the scanner receives an end-of-file indication from YY_INPUT, it then checks the -.Fn yywrap +.B yywrap() function. If -.Fn yywrap +.B yywrap() returns false (zero), then it is assumed that the function has gone ahead and set up -.Va yyin +.I yyin to point to another input file, and scanning continues. If it returns true (non-zero), then the scanner terminates, returning 0 to its caller. -.It Va yywrap +.IP The default -.Fn yywrap +.B yywrap() always returns 1. Presently, to redefine it you must first "#undef yywrap", as it is currently implemented as a macro. It is likely that -.Fn yywrap +.B yywrap() will soon be defined to be a function rather than a macro. -.It Va YY_USER_ACTION +.IP - +YY_USER_ACTION can be redefined to provide an action which is always executed prior to the matched rule's action. -.It Va YY_USER_INIT +.IP - The macro -.Va YY _USER_INIT +.B YY_USER_INIT may be redefined to provide an action which is always executed before the first scan. -.It Va YY_BREAK +.IP - In the generated scanner, the actions are all gathered in one large switch statement and separated using -.Va YY _BREAK , +.B YY_BREAK, which may be redefined. By default, it is simply a "break", to separate each rule's action from the following rule's. -.El -.Sh FILES -.Bl -tag -width lex.backtrack -compact -.It Pa lex.skel +.SH FILES +.TP +.I lex.skel skeleton scanner. -.It Pa lex.yy.c -generated scanner -(called -.Pa lexyy.c +.TP +.I lex.yy.c +generated scanner (called +.I lexyy.c on some systems). -.It Pa lex.backtrack +.TP +.I lex.backtrack backtracking information for -.Fl b -.It Pa flag -(called -.Pa lex.bck +.B -b +flag (called +.I lex.bck on some systems). -.El -.Sh SEE ALSO -.Xr lex 1 , -.Xr yacc 1 , -.Xr sed 1 , -.Xr awk 1 . -.Rs -.%T "lexdoc" -.Re -.Rs -.%A M. E. Lesk -.%A E. Schmidt -.%T "LEX \- Lexical Analyzer Generator" -.Re -.Sh DIAGNOSTICS -.Bl -tag -width Fl -.It Li reject_used_but_not_detected undefined +.TP +.B -lfl +library with which to link the scanners. +.SH "SEE ALSO" +.LP +flexdoc(1), lex(1), yacc(1), sed(1), awk(1). +.LP +M. E. Lesk and E. Schmidt, +.I LEX - Lexical Analyzer Generator +.SH DIAGNOSTICS +.I reject_used_but_not_detected undefined or -.It Li yymore_used_but_not_detected undefined -These errors can occur at compile time. -They indicate that the +.LP +.I yymore_used_but_not_detected undefined - +These errors can occur at compile time. They indicate that the scanner uses -.Ic REJECT +.B REJECT or -.Fn yymore +.B yymore() but that -.Nm lex -failed to notice the fact, -meaning that -.Nm lex +.I flex +failed to notice the fact, meaning that +.I flex scanned the first two sections looking for occurrences of these actions -and failed to find any, -but somehow you snuck some in via a #include -file, -for example . -Make an explicit reference to the action in your -.Nm lex -input file. -Note that previously -.Nm lex +and failed to find any, but somehow you snuck some in (via a #include +file, for example). Make an explicit reference to the action in your +.I flex +input file. (Note that previously +.I flex supported a -.Li %used/%unused -mechanism for dealing with this problem; -this feature is still supported -but now deprecated, -and will go away soon unless the author hears from -people who can argue compellingly that they need it. -.It Li lex scanner jammed +.B %used/%unused +mechanism for dealing with this problem; this feature is still supported +but now deprecated, and will go away soon unless the author hears from +people who can argue compellingly that they need it.) +.LP +.I flex scanner jammed - a scanner compiled with -.Fl s +.B -s has encountered an input string which wasn't matched by any of its rules. -.It Li lex input buffer overflowed +.LP +.I flex input buffer overflowed - a scanner rule matched a string long enough to overflow the -scanner's internal input buffer 16K bytes - controlled by -.Va YY_BUF_MAX -in -.Pa lex.skel . -.It Li scanner requires \&\-8 flag +scanner's internal input buffer (16K bytes - controlled by +.B YY_BUF_MAX +in "lex.skel"). +.LP +.I scanner requires -8 flag - Your scanner specification includes recognizing 8-bit characters and -you did not specify the -8 flag and your site has not installed lex -with -8 as the default . -.It Li too many \&%t classes! +you did not specify the -8 flag (and your site has not installed flex +with -8 as the default). +.LP +.I +fatal flex scanner internal error--end of buffer missed - +This can occur in an scanner which is reentered after a long-jump +has jumped out (or over) the scanner's activation frame. Before +reentering the scanner, use: +.nf + + yyrestart( yyin ); + +.fi +.LP +.I too many %t classes! - You managed to put every single character into its own %t class. -.Nm Lex +.I flex requires that at least one of the classes share characters. -.El -.Sh HISTORY -A -.Nm lex -appeared in -.At v6 . -The version this man page describes is -derived from code contributed by Vern Paxson. -.Sh AUTHOR +.SH AUTHOR Vern Paxson, with the help of many ideas and much inspiration from Van Jacobson. Original version by Jef Poskanzer. -.Pp -See -.%T "Lexdoc" -for additional credits and the address to send comments to. -.Sh BUGS -.Pp +.LP +See flexdoc(1) for additional credits and the address to send comments to. +.SH DEFICIENCIES / BUGS +.LP Some trailing context patterns cannot be properly matched and generate warning messages ("Dangerous trailing context"). These are patterns where the ending of the first part of the rule matches the beginning of the second part, such as "zx*/xy*", where the 'x*' matches the 'x' at -the beginning of the trailing context. (Note that the -.Tn POSIX -draft +the beginning of the trailing context. (Note that the POSIX draft states that the text matched by such patterns is undefined.) -.Pp +.LP For some trailing context rules, parts which are actually fixed-length are not recognized as such, leading to the abovementioned performance loss. -In particular, parts using '\&|' or {n} (such as "foo{3}") are always +In particular, parts using '|' or {n} (such as "foo{3}") are always considered variable-length. -.Pp -Combining trailing context with the special '\&|' action can result in -.Em fixed +.LP +Combining trailing context with the special '|' action can result in +.I fixed trailing context being turned into the more expensive -.Em variable -trailing context. This happens in the following example: -.Bd -literal -offset indent -%% -abc \&| -xyz/def -.Ed -.Pp -Use of -.Fn unput -invalidates yytext and yyleng. -.Pp -Use of -.Fn unput -to push back more text than was matched can +.I variable +trailing context. For example, this happens in the following example: +.nf + + %% + abc | + xyz/def + +.fi +.LP +Use of unput() invalidates yytext and yyleng. +.LP +Use of unput() to push back more text than was matched can result in the pushed-back text matching a beginning-of-line ('^') rule even though it didn't come at the beginning of the line (though this is rare!). -.Pp -Pattern-matching of -.Tn NUL Ns 's -is substantially slower than matching other +.LP +Pattern-matching of NUL's is substantially slower than matching other characters. -.Pp -.Nm Lex +.LP +.I flex does not generate correct #line directives for code internal to the scanner; thus, bugs in -.Pa lex.skel +.I lex.skel yield bogus line numbers. -.Pp +.LP Due to both buffering of input and read-ahead, you cannot intermix -calls to -.Aq Pa stdio.h -routines, such as, for example, -.Fn getchar , +calls to <stdio.h> routines, such as, for example, +.B getchar(), with -.Nm lex +.I flex rules and expect it to work. Call -.Fn input +.B input() instead. -.Pp +.LP The total table entries listed by the -.Fl v +.B -v flag excludes the number of table entries needed to determine what rule has been matched. The number of entries is equal -to the number of -.Tn DFA -states if the scanner does not use -.Ic REJECT , +to the number of DFA states if the scanner does not use +.B REJECT, and somewhat greater than the number of states if it does. -.Pp -.Ic REJECT +.LP +.B REJECT cannot be used with the -.Fl f +.I -f or -.Fl F +.I -F options. -.Pp +.LP Some of the macros, such as -.Fn yywrap , +.B yywrap(), may in the future become functions which live in the -.Fl lfl +.B -lfl library. This will doubtless break a lot of code, but may be -required for -.Tn POSIX Ns \-compliance . -.Pp +required for POSIX-compliance. +.LP The -.Nm lex +.I flex internal algorithms need documentation. diff --git a/usr.bin/lex/lex.skel b/usr.bin/lex/lex.skel index 6542d586d6..1acc95d1f3 100644 --- a/usr.bin/lex/lex.skel +++ b/usr.bin/lex/lex.skel @@ -1,21 +1,12 @@ /* A lexical scanner generated by flex */ /* scanner skeleton version: - * - * $Header: flex/RCS/flex.skel,v 2.13 90/05/26 17:24:13 vern Exp $ - * - * @(#)lex.skel 5.5 (Berkeley) 5/6/91 + * $Header: /home/horse/u0/vern/flex/RCS/flex.skel,v 2.16 90/08/03 14:09:36 vern Exp Locker: vern $ */ #define FLEX_SCANNER #include <stdio.h> -#include <stdlib.h> - -#ifdef __STDC__ - -#define YY_USE_PROTOS -#endif /* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ @@ -28,11 +19,41 @@ #ifdef __cplusplus +#include <stdlib.h> #include <osfcn.h> /* use prototypes in function declarations */ #define YY_USE_PROTOS +/* the "const" storage-class-modifier is valid */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#ifdef __STDC__ + +#ifdef __GNUC__ +#include <stddef.h> +void *malloc( size_t ); +void free( void* ); +#else +#include <stdlib.h> +#endif /* __GNUC__ */ + +#define YY_USE_PROTOS +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + + +#ifdef __TURBOC__ +#define YY_USE_CONST +#endif + + +#ifndef YY_USE_CONST +#define const #endif @@ -40,6 +61,13 @@ #define YY_PROTO(proto) proto #else #define YY_PROTO(proto) () +/* we can't get here if it's an ANSI C compiler, or a C++ compiler, + * so it's got to be a K&R compiler, and therefore there's no standard + * place from which to include these definitions + */ +char *malloc(); +int free(); +int read(); #endif @@ -172,7 +200,7 @@ struct yy_buffer_state YY_CHAR *yy_ch_buf; /* input buffer */ YY_CHAR *yy_buf_pos; /* current position in input buffer */ - /* size of input buffer in bytes, not including room for EOB characters*/ + /* size of input buffer in bytes, not including room for EOB characters */ int yy_buf_size; /* number of characters read into yy_ch_buf, not including EOB characters */ @@ -482,7 +510,7 @@ static int yy_get_next_buffer() if ( yy_n_chars == 0 ) { - if ( number_to_move == 1 ) + if ( number_to_move - YY_MORE_ADJ == 1 ) { ret_val = EOB_ACT_END_OF_FILE; yy_current_buffer->yy_eof_status = EOF_DONE; diff --git a/usr.bin/lex/lib/Makefile b/usr.bin/lex/lib/Makefile index 00dc7ed95b..6d5fbc2366 100644 --- a/usr.bin/lex/lib/Makefile +++ b/usr.bin/lex/lib/Makefile @@ -1,12 +1,4 @@ # @(#)Makefile 5.1 (Berkeley) 6/18/90 -# -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 1 00138 -# -------------------- ----- ---------------------- -# -# 11 Apr 93 Rodney W. Grimes Added conditional for profiled -# library LINKS LIB= ln SRCS= libmain.c diff --git a/usr.bin/lex/lib/ln.order b/usr.bin/lex/lib/ln.order deleted file mode 100644 index 484a5a9898..0000000000 --- a/usr.bin/lex/lib/ln.order +++ /dev/null @@ -1 +0,0 @@ -libmain.o diff --git a/usr.bin/lex/main.c b/usr.bin/lex/main.c index 42e6d5f3f0..c66c19ca65 100644 --- a/usr.bin/lex/main.c +++ b/usr.bin/lex/main.c @@ -1,41 +1,29 @@ +/* flex - tool to generate fast lexical analyzers */ + /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by - * Vern Paxson of Lawrence Berkeley Laboratory. + * Vern Paxson. * - * The United States Government has rights in this work pursuant + * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint @@ -45,10 +33,10 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)main.c 5.3 (Berkeley) 6/18/90"; -#endif /* not lint */ +static char rcsid[] = + "@(#) $Header: /usr/fsys/odin/a/vern/flex/RCS/main.c,v 2.9 90/06/27 23:48:24 vern Exp $ (LBL)"; +#endif -/* flex - tool to generate fast lexical analyzers */ #include "flexdef.h" #include "pathnames.h" @@ -301,7 +289,7 @@ int status; if ( usemecs ) putc( 'm', stderr ); - if ( strcmp( skelname, _PATH_SKELETONFILE ) ) + if ( skelname && strcmp( skelname, _PATH_SKELETONFILE ) ) fprintf( stderr, " -S%s", skelname ); putc( '\n', stderr ); @@ -420,6 +408,8 @@ char **argv; csize = DEFAULT_CSIZE; + starttime = flex_gettime(); + program_name = argv[0]; /* read flags */ @@ -604,7 +594,6 @@ get_next_arg: /* used by -C and -S flags in lieu of a "continue 2" control */ lastsc = 0; /* initialize the statistics */ - starttime = flex_gettime(); if ( (skelfile = fopen( skelname, "r" )) == NULL ) lerrsf( "can't open skeleton file %s", skelname ); diff --git a/usr.bin/lex/misc.c b/usr.bin/lex/misc.c index 64725987a2..777a8be375 100644 --- a/usr.bin/lex/misc.c +++ b/usr.bin/lex/misc.c @@ -1,53 +1,40 @@ +/* misc - miscellaneous flex routines */ + /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by - * Vern Paxson of Lawrence Berkeley Laboratory. + * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint -static char sccsid[] = "@(#)misc.c 5.3 (Berkeley) 2/26/91"; -#endif /* not lint */ - -/* misc - miscellaneous flex routines */ +static char rcsid[] = + "@(#) $Header: /usr/fsys/odin/a/vern/flex/RCS/misc.c,v 2.9 90/08/14 00:10:24 vern Exp $ (LBL)"; +#endif #include <ctype.h> -#include <stdlib.h> #include "flexdef.h" + /* ANSI C does not guarantee that isascii() is defined */ #ifndef isascii #define isascii(c) ((c) <= 0177) @@ -386,7 +373,7 @@ char msg[]; { fprintf( stderr, "%s: fatal internal error, %s\n", program_name, msg ); - flexend( 1 ); + exit( 1 ); } @@ -490,6 +477,38 @@ Char str[]; } +/* is_hex_digit - returns true if a character is a valid hex digit, false + * otherwise + * + * synopsis: + * int true_or_false, is_hex_digit(); + * int ch; + * val = is_hex_digit( ch ); + */ + +int is_hex_digit( ch ) +int ch; + + { + if ( isdigit( ch ) ) + return ( 1 ); + + switch ( clower( ch ) ) + { + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + return ( 1 ); + + default: + return ( 0 ); + } + } + + /* line_directive_out - spit out a "# line" statement */ void line_directive_out( output_file_name ) @@ -597,9 +616,14 @@ Char myesc( array ) Char array[]; { + Char c, esc_char; + register int sptr; + switch ( array[1] ) { +#ifdef __STDC__ case 'a': return ( '\a' ); +#endif case 'b': return ( '\b' ); case 'f': return ( '\f' ); case 'n': return ( '\n' ); @@ -607,9 +631,6 @@ Char array[]; case 't': return ( '\t' ); case 'v': return ( '\v' ); - case 'x': - /* fall through */ - case '0': case '1': case '2': @@ -620,17 +641,33 @@ Char array[]; case '7': case '8': case '9': + { /* \<octal> */ + sptr = 1; - { /* \<octal> or \x<hex> */ - Char c, esc_char; - register int sptr = 1; - - if ( array[1] == 'x' ) + while ( isascii( array[sptr] ) && isdigit( array[sptr] ) ) + /* don't increment inside loop control because if + * isdigit() is a macro it might expand into multiple + * increments ... + */ ++sptr; - while ( isascii( array[sptr] ) && isdigit( array[sptr] ) ) + c = array[sptr]; + array[sptr] = '\0'; + + esc_char = otoi( array + 1 ); + + array[sptr] = c; + + return ( esc_char ); + } + + case 'x': + { /* \x<hex> */ + int sptr = 2; + + while ( isascii( array[sptr] ) && is_hex_digit( array[sptr] ) ) /* don't increment inside loop control because if - * isdigit() is a macro it will expand it to two + * isdigit() is a macro it might expand into multiple * increments ... */ ++sptr; @@ -638,10 +675,7 @@ Char array[]; c = array[sptr]; array[sptr] = '\0'; - if ( array[1] == 'x' ) - esc_char = htoi( array + 2 ); - else - esc_char = otoi( array + 1 ); + esc_char = htoi( array + 2 ); array[sptr] = c; diff --git a/usr.bin/lex/nfa.c b/usr.bin/lex/nfa.c index 5e0fa64e74..4a64d7451a 100644 --- a/usr.bin/lex/nfa.c +++ b/usr.bin/lex/nfa.c @@ -1,51 +1,39 @@ +/* nfa - NFA construction routines */ + /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by - * Vern Paxson of Lawrence Berkeley Laboratory. + * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint -static char sccsid[] = "@(#)nfa.c 5.2 (Berkeley) 6/18/90"; -#endif /* not lint */ - -/* nfa - NFA construction routines */ +static char rcsid[] = + "@(#) $Header: /usr/fsys/odin/a/vern/flex/RCS/nfa.c,v 2.6 90/06/27 23:48:29 vern Exp $ (LBL)"; +#endif #include "flexdef.h" + /* declare functions that have forward references */ int dupmachine PROTO((int)); diff --git a/usr.bin/lex/parse.y b/usr.bin/lex/parse.y index ae17846ebf..5966b06646 100644 --- a/usr.bin/lex/parse.y +++ b/usr.bin/lex/parse.y @@ -1,58 +1,47 @@ + /* parse.y - parser for flex input */ %token CHAR NUMBER SECTEND SCDECL XSCDECL WHITESPACE NAME PREVCCL EOF_OP %{ - /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by - * Vern Paxson of Lawrence Berkeley Laboratory. + * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint -static char sccsid[] = "@(#)parse.y 5.2 (Berkeley) 6/18/90"; -#endif /* not lint */ +static char rcsid[] = + "@(#) $Header: /usr/fsys/odin/a/vern/flex/RCS/parse.y,v 2.7 90/06/27 23:48:31 vern Exp $ (LBL)"; +#endif #include "flexdef.h" int pat, scnum, eps, headcnt, trailcnt, anyccl, lastchar, i, actvp, rulelen; int trlcontxt, xcluflg, cclsorted, varlength, variable_trail_rule; Char clower(); +void build_eof_action(); +void yyerror(); static int madeany = false; /* whether we've made the '.' character class */ int previous_continued_action; /* whether the previous rule's action was '|' */ @@ -346,13 +335,25 @@ rule : re2 re headcnt = 0; } - trlcontxt = true; + if ( varlength && headcnt == 0 ) + { + /* again, see the comment in the rule for "re2 re" + * above + */ + add_accept( $1, num_rules | YY_TRAILING_HEAD_MASK ); + variable_trail_rule = true; + } - if ( ! varlength ) - headcnt = rulelen; + else + { + if ( ! varlength ) + headcnt = rulelen; - ++rulelen; - trailcnt = 1; + ++rulelen; + trailcnt = 1; + } + + trlcontxt = true; eps = mkstate( SYM_EPSILON ); $$ = link_machines( $1, @@ -456,7 +457,15 @@ singleton : singleton '*' else { if ( $3 == 0 ) - $$ = mkopt( mkrep( $1, $3, $5 ) ); + { + if ( $5 <= 0 ) + { + synerr( "bad iteration values" ); + $$ = $1; + } + else + $$ = mkopt( mkrep( $1, 1, $5 ) ); + } else $$ = mkrep( $1, $3, $5 ); } diff --git a/usr.bin/lex/scan.l b/usr.bin/lex/scan.l index 5170bada4b..1ba0d8e10d 100644 --- a/usr.bin/lex/scan.l +++ b/usr.bin/lex/scan.l @@ -1,62 +1,43 @@ + +/* scan.l - scanner for flex input */ + +%{ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by - * Vern Paxson of Lawrence Berkeley Laboratory. + * Vern Paxson. * - * The United States Government has rights in this work pursuant + * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)scan.l 5.3 (Berkeley) 4/12/91 + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -/* scan.l - scanner for flex input */ +#ifndef lint +static char rcsid[] = + "@(#) $Header: /usr/fsys/odin/a/vern/flex/RCS/scan.l,v 2.9 90/06/27 23:48:34 vern Exp $ (LBL)"; +#endif -%{ #undef yywrap #include "flexdef.h" #include "parse.h" -#ifndef lint -static char copyright[] = - "@(#) Copyright (c) 1989 The Regents of the University of California.\n"; -static char CR_continuation[] = "@(#) All rights reserved.\n"; - -static char rcsid[] = - "@(#) $Header: /usr/fsys/odin/a/vern/flex/RCS/scan.l,v 2.8 90/05/26 16:53:23 vern Exp $ (LBL)"; -#endif - #define ACTION_ECHO fprintf( temp_action_file, "%s", yytext ) #define MARK_END_OF_PROLOG fprintf( temp_action_file, "%%%% end of prolog\n" ); @@ -138,7 +119,7 @@ ESCSEQ \\([^\n]|[0-9]{1,3}|x[0-9a-f]{1,2}) } -^"%"[aeknopt]" ".*\n { +^"%"[aeknopt]{WS}.*\n { #ifdef NOTDEF fprintf( stderr, "old-style lex command at line %d ignored:\n\t%s", diff --git a/usr.bin/lex/sym.c b/usr.bin/lex/sym.c index ba4e55a426..326135bac0 100644 --- a/usr.bin/lex/sym.c +++ b/usr.bin/lex/sym.c @@ -5,47 +5,35 @@ * All rights reserved. * * This code is derived from software contributed to Berkeley by - * Vern Paxson of Lawrence Berkeley Laboratory. + * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint -static char sccsid[] = "@(#)sym.c 5.2 (Berkeley) 6/18/90"; -#endif /* not lint */ +static char rcsid[] = + "@(#) $Header: /usr/fsys/odin/a/vern/flex/RCS/sym.c,v 2.4 90/06/27 23:48:36 vern Exp $ (LBL)"; +#endif #include "flexdef.h" + /* declare functions that have forward references */ int hashfunct PROTO((register char[], int)); @@ -210,7 +198,7 @@ int hash_size; locstr = 0; while ( str[locstr] ) - hashval = ((hashval << 1) + str[locstr++]) % hash_size; + hashval = ((hashval << 1) + (unsigned char) str[locstr++]) % hash_size; return ( hashval ); } diff --git a/usr.bin/lex/tblcmp.c b/usr.bin/lex/tblcmp.c index 423a00dd3c..10db6a3955 100644 --- a/usr.bin/lex/tblcmp.c +++ b/usr.bin/lex/tblcmp.c @@ -5,47 +5,35 @@ * All rights reserved. * * This code is derived from software contributed to Berkeley by - * Vern Paxson of Lawrence Berkeley Laboratory. + * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint -static char sccsid[] = "@(#)tblcmp.c 5.2 (Berkeley) 6/18/90"; -#endif /* not lint */ +static char rcsid[] = + "@(#) $Header: /usr/fsys/odin/a/vern/flex/RCS/tblcmp.c,v 2.5 90/06/27 23:48:38 vern Exp $ (LBL)"; +#endif #include "flexdef.h" + /* declarations for functions that have forward references */ void mkentry PROTO((register int*, int, int, int, int)); @@ -380,7 +368,7 @@ int *state, numtrans; while ( 1 ) /* loops until a space is found */ { - if ( i + numecs > current_max_xpairs ) + if ( i + numecs >= current_max_xpairs ) expand_nxt_chk(); /* loops until space for end-of-buffer and action number are found */ @@ -401,7 +389,7 @@ int *state, numtrans; else ++i; - if ( i + numecs > current_max_xpairs ) + if ( i + numecs >= current_max_xpairs ) expand_nxt_chk(); } @@ -488,7 +476,7 @@ void mkdeftbl() ++tblend; /* room for transition on end-of-buffer character */ - if ( tblend + numecs > current_max_xpairs ) + if ( tblend + numecs >= current_max_xpairs ) expand_nxt_chk(); /* add in default end-of-buffer transition */ @@ -596,7 +584,7 @@ int numchars, statenum, deflink, totaltrans; ; } - if ( baseaddr + maxec - minec >= current_max_xpairs ) + if ( baseaddr + maxec - minec + 1 >= current_max_xpairs ) expand_nxt_chk(); for ( i = minec; i <= maxec; ++i ) @@ -610,7 +598,8 @@ int numchars, statenum, deflink, totaltrans; ++baseaddr ) ; - if ( baseaddr + maxec - minec >= current_max_xpairs ) + if ( baseaddr + maxec - minec + 1 >= + current_max_xpairs ) expand_nxt_chk(); /* reset the loop counter so we'll start all @@ -632,7 +621,7 @@ int numchars, statenum, deflink, totaltrans; tblbase = baseaddr - minec; tbllast = tblbase + maxec; - if ( tbllast >= current_max_xpairs ) + if ( tbllast + 1 >= current_max_xpairs ) expand_nxt_chk(); base[statenum] = tblbase; diff --git a/usr.bin/lex/yylex.c b/usr.bin/lex/yylex.c index 7dafbd2f58..a35e787ba6 100644 --- a/usr.bin/lex/yylex.c +++ b/usr.bin/lex/yylex.c @@ -5,44 +5,31 @@ * All rights reserved. * * This code is derived from software contributed to Berkeley by - * Vern Paxson of Lawrence Berkeley Laboratory. + * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint -static char sccsid[] = "@(#)yylex.c 5.2 (Berkeley) 6/18/90"; -#endif /* not lint */ +static char rcsid[] = + "@(#) $Header: /usr/fsys/odin/a/vern/flex/RCS/yylex.c,v 2.5 90/06/27 23:48:40 vern Exp $ (LBL)"; +#endif #include <ctype.h> #include "flexdef.h" diff --git a/usr.bin/locate/locate/locate.c b/usr.bin/locate/locate/locate.c index 440aadb87d..d97f32f4aa 100644 --- a/usr.bin/locate/locate/locate.c +++ b/usr.bin/locate/locate/locate.c @@ -74,6 +74,8 @@ static char sccsid[] = "@(#)locate.c 5.2 (Berkeley) 6/1/90"; #include <sys/param.h> #include <unistd.h> #include <stdio.h> +#include <string.h> +#include <fnmatch.h> #include "locate.h" #include "pathnames.h" @@ -103,7 +105,7 @@ fastfind(pathpart) register char *p, *s; register int c; int count, found, globflag; - char *cutoff, *patend, *q, *index(), *patprep(); + char *cutoff, *patend, *q, *patprep(); char bigram1[NBG], bigram2[NBG], path[MAXPATHLEN]; for (c = 0, p = bigram1, s = bigram2; c < NBG; c++) @@ -135,7 +137,7 @@ fastfind(pathpart) if (*p == NULL) { /* fast match success */ found = 1; if (!globflag || - fnmatch(pathpart, path, FNM_QUOTE)) + !fnmatch(pathpart, path, 0)) (void)printf("%s\n", path); break; } diff --git a/usr.bin/locate/locate/updatedb.sh b/usr.bin/locate/locate/updatedb.sh new file mode 100644 index 0000000000..01005a3205 --- /dev/null +++ b/usr.bin/locate/locate/updatedb.sh @@ -0,0 +1,79 @@ +#!/bin/sh +# +# Copyright (c) 1989 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# James A. Woods. +# +# Modified to be run by /bin/sh by Nate Williams, Aug, 1993 +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)updatedb.csh 5.1 (Berkeley) 4/2/91 +# + +SRCHPATHS="/" # directories to be put in the database +LIBDIR="/usr/libexec" # for subprograms +FCODES="/var/db/locate.database" # the database +if [ "$TMPDIR" = "" ]; then + TMPDIR="/var/tmp" # for temp files +fi + +PATH=/bin:/usr/bin +BIGRAMS="$TMPDIR/locate.bigrams.$$" +FILELIST="$TMPDIR/locate.list.$$" +ERRS="$TMPDIR/locate.errs.$$" + +# Make a file list and compute common bigrams. +# Alphabetize '/' before any other char with 'tr'. +# If the system is very short of sort space, 'bigram' can be made +# smarter to accumulate common bigrams directly without sorting +# ('awk', with its associative memory capacity, can do this in several +# lines, but is too slow, and runs out of string space on small machines). + +# search locally or everything +# find ${SRCHPATHS} -print | \ +find ${SRCHPATHS} ! -fstype local -a -prune -o -print | \ + tr '/' '\001' | \ + (sort -f; echo $? > $ERRS) | tr '\001' '/' > $FILELIST + +$LIBDIR/locate.bigram < $FILELIST | \ + (sort ; echo $? >> $ERRS) | \ + uniq -c | sort -nr | \ + awk '{ if (NR <= 128) print $2 }' | tr -d '\012' > $BIGRAMS + +# code the file list +if [ `sort -u $ERRS | grep -s -v 0` ]; then + printf 'locate: updatedb failed\n\n' +else + $LIBDIR/locate.code $BIGRAMS < $FILELIST > $FCODES + chmod 644 $FCODES + rm $BIGRAMS $FILELIST $ERRS +fi diff --git a/usr.bin/lock/Makefile b/usr.bin/lock/Makefile index b222425ce6..a33790a9d0 100644 --- a/usr.bin/lock/Makefile +++ b/usr.bin/lock/Makefile @@ -4,4 +4,9 @@ PROG= lock BINOWN= root BINMODE=4555 +.if exists(/usr/lib/libcrypt.a) +DPADD+= ${LIBCRYPT} +LDADD+= -lcrypt +.endif + .include <bsd.prog.mk> diff --git a/usr.bin/login/Makefile b/usr.bin/login/Makefile index 06d87297c2..9b496a38fb 100644 --- a/usr.bin/login/Makefile +++ b/usr.bin/login/Makefile @@ -1,10 +1,16 @@ # @(#)Makefile 5.6 (Berkeley) 6/24/90 PROG= login -SRCS= login.c +SRCS= klogin.c login.c DPADD= ${LIBUTIL} LDADD= -lutil BINOWN= root BINMODE=4555 +.if exists(/usr/lib/libcrypt.a) +#CFLAGS+=-DKERBEROS +DPADD+= ${LIBCRYPT} #${LIBKRB} +LDADD+= -lcrypt #-lkrb +.endif + .include <bsd.prog.mk> diff --git a/usr.bin/login/klogin.c b/usr.bin/login/klogin.c new file mode 100644 index 0000000000..faf5aa63ad --- /dev/null +++ b/usr.bin/login/klogin.c @@ -0,0 +1,187 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)klogin.c 5.11 (Berkeley) 7/12/92"; +#endif /* not lint */ + +#ifdef KERBEROS +#include <sys/param.h> +#include <sys/syslog.h> +#include <kerberosIV/des.h> +#include <kerberosIV/krb.h> +#include <pwd.h> +#include <netdb.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#define INITIAL_TICKET "krbtgt" +#define VERIFY_SERVICE "rcmd" + +extern int notickets; +extern char *krbtkfile_env; + +/* + * Attempt to log the user in using Kerberos authentication + * + * return 0 on success (will be logged in) + * 1 if Kerberos failed (try local password in login) + */ +int +klogin(pw, instance, localhost, password) + struct passwd *pw; + char *instance, *localhost, *password; +{ + int kerror; + AUTH_DAT authdata; + KTEXT_ST ticket; + struct hostent *hp; + unsigned long faddr; + char realm[REALM_SZ], savehost[MAXHOSTNAMELEN]; + char tkt_location[MAXPATHLEN]; + char *krb_get_phost(); + + /* + * Root logins don't use Kerberos. + * If we have a realm, try getting a ticket-granting ticket + * and using it to authenticate. Otherwise, return + * failure so that we can try the normal passwd file + * for a password. If that's ok, log the user in + * without issuing any tickets. + */ + if (strcmp(pw->pw_name, "root") == 0 || + krb_get_lrealm(realm, 0) != KSUCCESS) + return (1); + + /* + * get TGT for local realm + * tickets are stored in a file named TKT_ROOT plus uid + * except for user.root tickets. + */ + + if (strcmp(instance, "root") != 0) + (void)sprintf(tkt_location, "%s%d", TKT_ROOT, pw->pw_uid); + else { + (void)sprintf(tkt_location, "%s_root_%d", TKT_ROOT, pw->pw_uid); + krbtkfile_env = tkt_location; + } + (void)krb_set_tkt_string(tkt_location); + + /* + * Set real as well as effective ID to 0 for the moment, + * to make the kerberos library do the right thing. + */ + if (setuid(0) < 0) { + perror("login: setuid"); + return (1); + } + kerror = krb_get_pw_in_tkt(pw->pw_name, instance, + realm, INITIAL_TICKET, realm, DEFAULT_TKT_LIFE, password); + /* + * If we got a TGT, get a local "rcmd" ticket and check it so as to + * ensure that we are not talking to a bogus Kerberos server. + * + * There are 2 cases where we still allow a login: + * 1: the VERIFY_SERVICE doesn't exist in the KDC + * 2: local host has no srvtab, as (hopefully) indicated by a + * return value of RD_AP_UNDEC from krb_rd_req(). + */ + if (kerror != INTK_OK) { + if (kerror != INTK_BADPW && kerror != KDC_PR_UNKNOWN) { + syslog(LOG_ERR, "Kerberos intkt error: %s", + krb_err_txt[kerror]); + dest_tkt(); + } + return (1); + } + + if (chown(TKT_FILE, pw->pw_uid, pw->pw_gid) < 0) + syslog(LOG_ERR, "chown tkfile (%s): %m", TKT_FILE); + + (void)strncpy(savehost, krb_get_phost(localhost), sizeof(savehost)); + savehost[sizeof(savehost)-1] = NULL; + + /* + * if the "VERIFY_SERVICE" doesn't exist in the KDC for this host, + * still allow login with tickets, but log the error condition. + */ + + kerror = krb_mk_req(&ticket, VERIFY_SERVICE, savehost, realm, 33); + if (kerror == KDC_PR_UNKNOWN) { + syslog(LOG_NOTICE, + "warning: TGT not verified (%s); %s.%s not registered, or srvtab is wrong?", + krb_err_txt[kerror], VERIFY_SERVICE, savehost); + notickets = 0; + return(0); + } + + if (kerror != KSUCCESS) { + (void)printf("unable to use TGT: (%s)\n", krb_err_txt[kerror]); + syslog(LOG_NOTICE, "unable to use TGT: (%s)", + krb_err_txt[kerror]); + dest_tkt(); + return(1); + } + + if (!(hp = gethostbyname(localhost))) { + syslog(LOG_ERR, "couldn't get local host address"); + dest_tkt(); + return(1); + } + + bcopy((void *)hp->h_addr, (void *)&faddr, sizeof(faddr)); + + kerror = krb_rd_req(&ticket, VERIFY_SERVICE, savehost, faddr, + &authdata, ""); + + if (kerror == KSUCCESS) { + notickets = 0; + return(0); + } + + /* undecipherable: probably didn't have a srvtab on the local host */ + if (kerror = RD_AP_UNDEC) { + syslog(LOG_NOTICE, "krb_rd_req: (%s)\n", krb_err_txt[kerror]); + dest_tkt(); + return(1); + } + /* failed for some other reason */ + (void)printf("unable to verify %s ticket: (%s)\n", VERIFY_SERVICE, + krb_err_txt[kerror]); + syslog(LOG_NOTICE, "couldn't verify %s ticket: %s", VERIFY_SERVICE, + krb_err_txt[kerror]); + dest_tkt(); + return(1); +} +#endif diff --git a/usr.bin/login/login.c b/usr.bin/login/login.c index e0955381ec..3aa6883e99 100644 --- a/usr.bin/login/login.c +++ b/usr.bin/login/login.c @@ -38,7 +38,7 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)login.c 5.73 (Berkeley) 6/29/91"; +static char sccsid[] = "@(#)login.c 5.78 (Berkeley) 6/29/92"; #endif /* not lint */ /* @@ -53,19 +53,35 @@ static char sccsid[] = "@(#)login.c 5.73 (Berkeley) 6/29/91"; #include <sys/resource.h> #include <sys/file.h> -#include <utmp.h> #include <signal.h> -#include <errno.h> #include <ttyent.h> #include <syslog.h> +#include <setjmp.h> +#include <tzfile.h> +#include <utmp.h> +#include <errno.h> #include <grp.h> #include <pwd.h> -#include <setjmp.h> +#include <unistd.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> -#include <tzfile.h> #include "pathnames.h" +void badlogin __P((char *)); +void checknologin __P((void)); +void dolastlog __P((int)); +void getloginname __P((void)); +void motd __P((void)); +int rootterm __P((char *)); +void sigint __P((int)); +void sleepexit __P((int)); +char *stypeof __P((char *)); +void timedout __P((int)); +#ifdef KERBEROS +int klogin __P((struct passwd *, char *, char *, char *)); +#endif + #define TTYGRPNAME "tty" /* name of group to own ttys */ /* @@ -73,7 +89,7 @@ static char sccsid[] = "@(#)login.c 5.73 (Berkeley) 6/29/91"; * be patched on machines where it's too small. */ int timeout = 300; -int rootlogin; + #ifdef KERBEROS int notickets = 1; char *instance; @@ -85,25 +101,22 @@ struct passwd *pwd; int failures; char term[64], *envinit[1], *hostname, *username, *tty; +int main(argc, argv) int argc; - char **argv; + char *argv[]; { - extern int optind; - extern char *optarg, **environ; - struct timeval tp; - struct group *gr; + extern char **environ; register int ch; register char *p; - int ask, fflag, hflag, pflag, cnt, uid; - int quietlog, rval; + struct group *gr; + struct stat st; + struct timeval tp; + struct utmp utmp; + int ask, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval, uid; char *domain, *salt, *ttyn; char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10]; char localhost[MAXHOSTNAMELEN]; - char *ctime(), *ttyname(), *stypeof(), *crypt(), *getpass(); - time_t time(); - off_t lseek(); - void timedout(); (void)signal(SIGALRM, timedout); (void)alarm((u_int)timeout); @@ -115,7 +128,7 @@ main(argc, argv) /* * -p is used by getty to tell login not to destroy the environment - * -f is used to skip a second login authentication + * -f is used to skip a second login authentication * -h is used by other servers to pass the name of the remote * host to login so that it may be placed in utmp and wtmp */ @@ -157,6 +170,7 @@ main(argc, argv) } argc -= optind; argv += optind; + if (*argv) { username = *argv; ask = 0; @@ -164,11 +178,11 @@ main(argc, argv) ask = 1; for (cnt = getdtablesize(); cnt > 2; cnt--) - close(cnt); + (void)close(cnt); - ttyn = ttyname(0); + ttyn = ttyname(STDIN_FILENO); if (ttyn == NULL || *ttyn == '\0') { - (void)sprintf(tname, "%s??", _PATH_TTY); + (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY); ttyn = tname; } if (tty = rindex(ttyn, '/')) @@ -181,17 +195,14 @@ main(argc, argv) fflag = 0; getloginname(); } + rootlogin = 0; #ifdef KERBEROS if ((instance = index(username, '.')) != NULL) { if (strncmp(instance, ".root", 5) == 0) - rootlogin++; + rootlogin = 1; *instance++ = '\0'; - } else { - rootlogin = 0; + } else instance = ""; - } -#else - rootlogin = 0; #endif if (strlen(username) > UT_NAMESIZE) username[UT_NAMESIZE] = '\0'; @@ -235,19 +246,10 @@ main(argc, argv) rval = klogin(pwd, instance, localhost, p); if (rval == 0) authok = 1; - else if (rval == 1) { - if (pwd->pw_uid != 0) - rootlogin = 0; + else if (rval == 1) rval = strcmp(crypt(p, salt), pwd->pw_passwd); - } #else - if (pwd->pw_uid != 0) - rootlogin = 0; -#ifdef DES rval = strcmp(crypt(p, salt), pwd->pw_passwd); -#else - rval = strcmp(p, pwd->pw_passwd); -#endif #endif } bzero(p, strlen(p)); @@ -319,7 +321,7 @@ main(argc, argv) } else if (pwd->pw_change - tp.tv_sec < 2 * DAYSPERWEEK * SECSPERDAY && !quietlog) (void)printf("Warning: your password expires on %s", - ctime(&pwd->pw_expire)); + ctime(&pwd->pw_change)); if (pwd->pw_expire) if (tp.tv_sec >= pwd->pw_expire) { (void)printf("Sorry -- your account has expired.\n"); @@ -329,18 +331,14 @@ main(argc, argv) (void)printf("Warning: your account expires on %s", ctime(&pwd->pw_expire)); - /* nothing else left to fail -- really log in */ - { - struct utmp utmp; - - bzero((void *)&utmp, sizeof(utmp)); - (void)time(&utmp.ut_time); - strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); - if (hostname) - strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); - strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); - login(&utmp); - } + /* Nothing else left to fail -- really log in. */ + bzero((void *)&utmp, sizeof(utmp)); + (void)time(&utmp.ut_time); + (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); + if (hostname) + (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); + (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); + login(&utmp); dolastlog(quietlog); @@ -353,13 +351,13 @@ main(argc, argv) if (*pwd->pw_shell == '\0') pwd->pw_shell = _PATH_BSHELL; - /* destroy environment unless user has requested preservation */ + /* Destroy environment unless user has requested its preservation. */ if (!pflag) environ = envinit; (void)setenv("HOME", pwd->pw_dir, 1); (void)setenv("SHELL", pwd->pw_shell, 1); if (term[0] == '\0') - strncpy(term, stypeof(tty), sizeof(term)); + (void)strncpy(term, stypeof(tty), sizeof(term)); (void)setenv("TERM", term, 0); (void)setenv("LOGNAME", pwd->pw_name, 1); (void)setenv("USER", pwd->pw_name, 1); @@ -371,7 +369,8 @@ main(argc, argv) if (tty[sizeof("tty")-1] == 'd') syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); - /* if fflag is on, assume caller/authenticator has logged root login */ + + /* If fflag is on, assume caller/authenticator has logged root login. */ if (rootlogin && fflag == 0) if (hostname) syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s", @@ -385,16 +384,12 @@ main(argc, argv) #endif if (!quietlog) { - struct stat st; - - printf("%s%s", - "386BSD Release 0.1 by William and Lynne Jolitz.\n", -"Copyright (c) 1989,1990,1991,1992 William F. Jolitz. All rights reserved.\n\ -Based in part on work by the 386BSD User Community and the\n\ -BSD Networking Software, Release 2 by UCB EECS Department.\n"); - + (void)printf( +"Copyright (c) 1980,1983,1986,1988,1990,1991 The Regents of the University\n%s", +"of California. All rights reserved.\n\n"); motd(); - (void)sprintf(tbuf, "%s/%s", _PATH_MAILDIR, pwd->pw_name); + (void)snprintf(tbuf, + sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name); if (stat(tbuf, &st) == 0 && st.st_size != 0) (void)printf("You have %smail.\n", (st.st_mtime > st.st_atime) ? "new " : ""); @@ -406,13 +401,13 @@ BSD Networking Software, Release 2 by UCB EECS Department.\n"); (void)signal(SIGTSTP, SIG_IGN); tbuf[0] = '-'; - strcpy(tbuf + 1, (p = rindex(pwd->pw_shell, '/')) ? + (void)strcpy(tbuf + 1, (p = rindex(pwd->pw_shell, '/')) ? p + 1 : pwd->pw_shell); if (setlogin(pwd->pw_name) < 0) syslog(LOG_ERR, "setlogin() failure: %m"); - /* discard permissions last so can't get killed and drop core */ + /* Discard permissions last so can't get killed and drop core. */ if (rootlogin) (void) setuid(0); else @@ -424,11 +419,12 @@ BSD Networking Software, Release 2 by UCB EECS Department.\n"); } #ifdef KERBEROS -#define NBUFSIZ (UT_NAMESIZE + 1 + 5) /* .root suffix */ +#define NBUFSIZ (UT_NAMESIZE + 1 + 5) /* .root suffix */ #else #define NBUFSIZ (UT_NAMESIZE + 1) #endif +void getloginname() { register int ch; @@ -457,28 +453,22 @@ getloginname() } } -void -timedout() -{ - (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout); - exit(0); -} - +int rootterm(ttyn) char *ttyn; { struct ttyent *t; - return((t = getttynam(ttyn)) && t->ty_status&TTY_SECURE); + return((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE); } jmp_buf motdinterrupt; +void motd() { register int fd, nchars; sig_t oldint; - void sigint(); char tbuf[8192]; if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0) @@ -491,12 +481,24 @@ motd() (void)close(fd); } +/* ARGSUSED */ void -sigint() +sigint(signo) + int signo; { longjmp(motdinterrupt, 1); } +/* ARGSUSED */ +void +timedout(signo) + int signo; +{ + (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout); + exit(0); +} + +void checknologin() { register int fd, nchars; @@ -509,12 +511,12 @@ checknologin() } } +void dolastlog(quiet) int quiet; { struct lastlog ll; int fd; - char *ctime(); if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) { (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); @@ -525,23 +527,26 @@ dolastlog(quiet) 24-5, (char *)ctime(&ll.ll_time)); if (*ll.ll_host != '\0') (void)printf("from %.*s\n", - sizeof(ll.ll_host), ll.ll_host); + (int)sizeof(ll.ll_host), + ll.ll_host); else (void)printf("on %.*s\n", - sizeof(ll.ll_line), ll.ll_line); + (int)sizeof(ll.ll_line), + ll.ll_line); } (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); } bzero((void *)&ll, sizeof(ll)); (void)time(&ll.ll_time); - strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); + (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); if (hostname) - strncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); + (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); (void)write(fd, (char *)&ll, sizeof(ll)); (void)close(fd); } } +void badlogin(name) char *name; { @@ -574,9 +579,10 @@ stypeof(ttyid) return(ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN); } +void sleepexit(eval) int eval; { - sleep((u_int)5); + (void)sleep((u_int)5); exit(eval); } diff --git a/usr.bin/lorder/Makefile b/usr.bin/lorder/Makefile index 7fc6f94ca6..013a413e04 100644 --- a/usr.bin/lorder/Makefile +++ b/usr.bin/lorder/Makefile @@ -1,14 +1,9 @@ # @(#)Makefile 5.3 (Berkeley) 5/18/90 -MAN1= lorder.0 +MAN1= lorder.1 all lorder: ${MAN1} -clean depend lint tags: - -cleandir: - rm -f ${MAN1} - install: maninstall install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ ${.CURDIR}/lorder.sh ${DESTDIR}/${BINDIR}/lorder diff --git a/usr.bin/m4/Makefile b/usr.bin/m4/Makefile index 4435de773e..c60b6264a0 100644 --- a/usr.bin/m4/Makefile +++ b/usr.bin/m4/Makefile @@ -5,7 +5,7 @@ PROG= m4 CFLAGS+=-DEXTENDED -SRCS = main.c eval.c serv.c look.c misc.c expr.c -NOMAN= noman +SRCS = expr.c int2str.c look.c main.c misc.c serv.c +MAN1 = m4.1 .include <bsd.prog.mk> diff --git a/usr.bin/m4/TEST/ack.m4 b/usr.bin/m4/TEST/ack.m4 index 49e2c1e3db..e05f24139c 100644 --- a/usr.bin/m4/TEST/ack.m4 +++ b/usr.bin/m4/TEST/ack.m4 @@ -1,40 +1,2 @@ -# -# Copyright (c) 1989 The Regents of the University of California. -# All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Ozan Yigit. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)ack.m4 5.2 (Berkeley) 4/12/91 -# - define(ack, `ifelse($1,0,incr($2),$2,0,`ack(DECR($1),1)', `ack(DECR($1), ack($1,DECR($2)))')') diff --git a/usr.bin/m4/TEST/hanoi.m4 b/usr.bin/m4/TEST/hanoi.m4 index da5eb81a88..6a8f1b6322 100644 --- a/usr.bin/m4/TEST/hanoi.m4 +++ b/usr.bin/m4/TEST/hanoi.m4 @@ -1,41 +1,3 @@ -# -# Copyright (c) 1989 The Regents of the University of California. -# All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Ozan Yigit. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)hanoi.m4 5.2 (Berkeley) 4/12/91 -# - define(hanoi, `trans(A, B, C, $1)') define(moved,`move disk from $1 to $2 diff --git a/usr.bin/m4/TEST/hash.m4 b/usr.bin/m4/TEST/hash.m4 index 2f7cf67bfe..feb54b8a33 100644 --- a/usr.bin/m4/TEST/hash.m4 +++ b/usr.bin/m4/TEST/hash.m4 @@ -1,41 +1,3 @@ -# -# Copyright (c) 1989 The Regents of the University of California. -# All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Ozan Yigit. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)hash.m4 5.2 (Berkeley) 4/12/91 -# - dnl This probably will not run on any m4 that cannot dnl handle char constants in eval. dnl diff --git a/usr.bin/m4/TEST/sqroot.m4 b/usr.bin/m4/TEST/sqroot.m4 index dc9802402b..6087906980 100644 --- a/usr.bin/m4/TEST/sqroot.m4 +++ b/usr.bin/m4/TEST/sqroot.m4 @@ -1,41 +1,3 @@ -# -# Copyright (c) 1989 The Regents of the University of California. -# All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Ozan Yigit. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)sqroot.m4 5.2 (Berkeley) 4/12/91 -# - define(square_root, `ifelse(eval($1<0),1,negative-square-root, `square_root_aux($1, 1, eval(($1+1)/2))')') diff --git a/usr.bin/m4/TEST/string.m4 b/usr.bin/m4/TEST/string.m4 index 23297b8393..0a2ed81273 100644 --- a/usr.bin/m4/TEST/string.m4 +++ b/usr.bin/m4/TEST/string.m4 @@ -1,40 +1,3 @@ -# -# Copyright (c) 1989 The Regents of the University of California. -# All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Ozan Yigit. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)string.m4 5.2 (Berkeley) 4/12/91 -# define(string,`integer $1(len(substr($2,1))) str($1,substr($2,1),0) diff --git a/usr.bin/m4/TEST/test.m4 b/usr.bin/m4/TEST/test.m4 index 2d5e1b4166..c3400801a4 100644 --- a/usr.bin/m4/TEST/test.m4 +++ b/usr.bin/m4/TEST/test.m4 @@ -1,41 +1,4 @@ # -# Copyright (c) 1989 The Regents of the University of California. -# All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Ozan Yigit. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)test.m4 5.2 (Berkeley) 4/12/91 -# - # test file for mp (not comprehensive) # # v7 m4 does not have `decr'. diff --git a/usr.bin/m4/expr.c b/usr.bin/m4/expr.c index eb4da8e92d..fc48eb31e0 100644 --- a/usr.bin/m4/expr.c +++ b/usr.bin/m4/expr.c @@ -1,600 +1,617 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Ozan Yigit. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)expr.c 5.3 (Berkeley) 2/26/91"; -#endif /* not lint */ +/* File : expr.c + Authors: Mike Lutz & Bob Harper + Editors: Ozan Yigit & Richard A. O'Keefe + Updated: %G% + Purpose: arithmetic expression evaluator. -#include <setjmp.h> -#include <stdio.h> + expr() performs a standard recursive descent parse to evaluate any + expression permitted byf the following grammar: + + expr : query EOS + query : lor + | lor "?" query ":" query + lor : land { "||" land } or OR, for Pascal + land : bor { "&&" bor } or AND, for Pascal + bor : bxor { "|" bxor } + bxor : band { "^" band } + band : eql { "&" eql } + eql : relat { eqrel relat } + relat : shift { rel shift } + shift : primary { shop primary } + primary : term { addop term } + term : unary { mulop unary } + unary : factor + | unop unary + factor : constant + | "(" query ")" + constant: num + | "'" CHAR "'" or '"' CHAR '"' + num : DIGIT full ANSI C syntax + | DIGIT num + shop : "<<" + | ">>" + eqlrel : "=" + | "==" + | "!=" + rel : "<" or <>, Pascal not-equal + | ">" + | "<=" or =<, for Prolog users. + | ">=" + + This expression evaluator was lifted from a public-domain + C Pre-Processor included with the DECUS C Compiler distribution. + It has been hacked somewhat to be suitable for m4. + + 26-Mar-1993 Changed to work in any of EBCDIC, ASCII, DEC MNCS, + or ISO 8859/n. + + 26-Mar-1993 Changed to use "long int" rather than int, so that + we get the same 32-bit arithmetic on a PC as on a Sun. + It isn't fully portable, of course, but then on a 64- + bit machine we _want_ 64-bit arithmetic... + Shifting rewritten (using LONG_BIT) to give signed + shifts even when (long) >> (long) is unsigned. + + 26-Mar-1993 I finally got sick of the fact that &&, ||, and ?: + don't do conditional evaluation. What is the good + of having eval(0&&(1/0)) crash and dump core? Now + every function has a doit? argument. + + 26-Mar-1993 charcon() didn't actually accept 'abcd', which it + should have. Fixed it. + + 20-Apr-1993 eval(1/0) and eval(1%0) dumped core and crashed. + This is also true of the System V r 3.2 m4, but + it isn't good enough for ours! Changed it so that + x % 0 => x as per Concrete Mathematics + x / 0 => error and return 0 from expr(). +*/ -/* - * expression evaluator: performs a standard recursive - * descent parse to evaluate any expression permissible - * within the following grammar: - * - * expr : query EOS - * query : lor - * | lor "?" query ":" query - * lor : land { "||" land } - * land : bor { "&&" bor } - * bor : bxor { "|" bxor } - * bxor : band { "^" band } - * band : eql { "&" eql } - * eql : relat { eqrel relat } - * relat : shift { rel shift } - * shift : primary { shop primary } - * primary : term { addop term } - * term : unary { mulop unary } - * unary : factor - * | unop unary - * factor : constant - * | "(" query ")" - * constant: num - * | "'" CHAR "'" - * num : DIGIT - * | DIGIT num - * shop : "<<" - * | ">>" - * eqlrel : "=" - * | "==" - * | "!=" - * rel : "<" - * | ">" - * | "<=" - * | ">=" - * - * - * This expression evaluator is lifted from a public-domain - * C Pre-Processor included with the DECUS C Compiler distribution. - * It is hacked somewhat to be suitable for m4. - * - * Originally by: Mike Lutz - * Bob Harper - */ - -#define TRUE 1 #define FALSE 0 -#define EOS (char) 0 -#define EQL 0 -#define NEQ 1 -#define LSS 2 -#define LEQ 3 -#define GTR 4 -#define GEQ 5 -#define OCTAL 8 -#define DECIMAL 10 - -static char *nxtch; /* Parser scan pointer */ - -/* - * For longjmp - */ -static jmp_buf expjump; - -/* - * macros: - * - * ungetch - Put back the last character examined. - * getch - return the next character from expr string. - */ -#define ungetch() nxtch-- -#define getch() *nxtch++ - -expr(expbuf) -char *expbuf; -{ - register int rval; - - nxtch = expbuf; - if (setjmp(expjump) != 0) - return (FALSE); - rval = query(); - if (skipws() == EOS) - return(rval); - experr("Ill-formed expression"); -} - -/* - * query : lor | lor '?' query ':' query - * - */ -query() -{ - register int bool, true_val, false_val; - - bool = lor(); - if (skipws() != '?') { - ungetch(); - return(bool); - } - - true_val = query(); - if (skipws() != ':') - experr("Bad query"); - - false_val = query(); - return(bool ? true_val : false_val); -} - -/* - * lor : land { '||' land } - * - */ -lor() -{ - register int c, vl, vr; - - vl = land(); - while ((c = skipws()) == '|' && getch() == '|') { - vr = land(); - vl = vl || vr; - } - - if (c == '|') - ungetch(); - ungetch(); - return(vl); -} - -/* - * land : bor { '&&' bor } - * - */ -land() -{ - register int c, vl, vr; - - vl = bor(); - while ((c = skipws()) == '&' && getch() == '&') { - vr = bor(); - vl = vl && vr; - } - - if (c == '&') - ungetch(); - ungetch(); - return(vl); -} - -/* - * bor : bxor { '|' bxor } - * - */ -bor() -{ - register int vl, vr, c; - - vl = bxor(); - while ((c = skipws()) == '|' && getch() != '|') { - ungetch(); - vr = bxor(); - vl |= vr; - } - - if (c == '|') - ungetch(); - ungetch(); - return(vl); -} - -/* - * bxor : band { '^' band } - * - */ -bxor() -{ - register int vl, vr; - - vl = band(); - while (skipws() == '^') { - vr = band(); - vl ^= vr; - } - - ungetch(); - return(vl); -} - -/* - * band : eql { '&' eql } - * - */ -band() -{ - register int vl, vr, c; - - vl = eql(); - while ((c = skipws()) == '&' && getch() != '&') { - ungetch(); - vr = eql(); - vl &= vr; - } - - if (c == '&') - ungetch(); - ungetch(); - return(vl); -} - -/* - * eql : relat { eqrel relat } - * - */ -eql() -{ - register int vl, vr, rel; - - vl = relat(); - while ((rel = geteql()) != -1) { - vr = relat(); - - switch (rel) { - - case EQL: - vl = (vl == vr); - break; - case NEQ: - vl = (vl != vr); - break; - } - } - return(vl); -} - -/* - * relat : shift { rel shift } - * - */ -relat() -{ - register int vl, vr, rel; - - vl = shift(); - while ((rel = getrel()) != -1) { - - vr = shift(); - switch (rel) { - - case LEQ: - vl = (vl <= vr); - break; - case LSS: - vl = (vl < vr); - break; - case GTR: - vl = (vl > vr); - break; - case GEQ: - vl = (vl >= vr); - break; - } - } - return(vl); -} - -/* - * shift : primary { shop primary } - * - */ -shift() -{ - register int vl, vr, c; - - vl = primary(); - while (((c = skipws()) == '<' || c == '>') && c == getch()) { - vr = primary(); - - if (c == '<') - vl <<= vr; - else - vl >>= vr; - } - - if (c == '<' || c == '>') - ungetch(); - ungetch(); - return(vl); -} - -/* - * primary : term { addop term } - * - */ -primary() -{ - register int c, vl, vr; - - vl = term(); - while ((c = skipws()) == '+' || c == '-') { - vr = term(); - if (c == '+') - vl += vr; - else - vl -= vr; - } - - ungetch(); - return(vl); -} - -/* - * <term> := <unary> { <mulop> <unary> } - * - */ -term() -{ - register int c, vl, vr; - - vl = unary(); - while ((c = skipws()) == '*' || c == '/' || c == '%') { - vr = unary(); - - switch (c) { - case '*': - vl *= vr; - break; - case '/': - vl /= vr; - break; - case '%': - vl %= vr; - break; - } - } - ungetch(); - return(vl); -} - -/* - * unary : factor | unop unary - * - */ -unary() -{ - register int val, c; - - if ((c = skipws()) == '!' || c == '~' || c == '-') { - val = unary(); - - switch (c) { - case '!': - return(! val); - case '~': - return(~ val); - case '-': - return(- val); - } - } - - ungetch(); - return(factor()); -} - -/* - * factor : constant | '(' query ')' - * - */ -factor() -{ - register int val; - - if (skipws() == '(') { - val = query(); - if (skipws() != ')') - experr("Bad factor"); - return(val); - } - - ungetch(); - return(constant()); -} - -/* - * constant: num | 'char' - * - */ -constant() -{ - /* - * Note: constant() handles multi-byte constants - */ - - register int i; - register int value; - register char c; - int v[sizeof (int)]; - - if (skipws() != '\'') { - ungetch(); - return(num()); - } - for (i = 0; i < sizeof(int); i++) { - if ((c = getch()) == '\'') { - ungetch(); - break; - } - if (c == '\\') { - switch (c = getch()) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - ungetch(); - c = num(); - break; - case 'n': - c = 012; - break; - case 'r': - c = 015; - break; - case 't': - c = 011; - break; - case 'b': - c = 010; - break; - case 'f': - c = 014; - break; - } - } - v[i] = c; - } - if (i == 0 || getch() != '\'') - experr("Illegal character constant"); - for (value = 0; --i >= 0;) { - value <<= 8; - value += v[i]; - } - return(value); -} - -/* - * num : digit | num digit - * - */ -num() -{ - register int rval, c, base; - int ndig; - - base = ((c = skipws()) == '0') ? OCTAL : DECIMAL; - rval = 0; - ndig = 0; - while (c >= '0' && c <= (base == OCTAL ? '7' : '9')) { - rval *= base; - rval += (c - '0'); - c = getch(); - ndig++; - } - ungetch(); - if (ndig) - return(rval); - experr("Bad constant"); -} - -/* - * eqlrel : '=' | '==' | '!=' - * - */ -geteql() -{ - register int c1, c2; - - c1 = skipws(); - c2 = getch(); - - switch (c1) { - - case '=': - if (c2 != '=') - ungetch(); - return(EQL); - - case '!': - if (c2 == '=') - return(NEQ); - ungetch(); - ungetch(); - return(-1); - - default: - ungetch(); - ungetch(); - return(-1); - } -} - -/* - * rel : '<' | '>' | '<=' | '>=' - * - */ -getrel() -{ - register int c1, c2; - - c1 = skipws(); - c2 = getch(); - - switch (c1) { - - case '<': - if (c2 == '=') - return(LEQ); - ungetch(); - return(LSS); - - case '>': - if (c2 == '=') - return(GEQ); - ungetch(); - return(GTR); - - default: - ungetch(); - ungetch(); - return(-1); - } -} - -/* - * Skip over any white space and return terminating char. - */ -skipws() -{ - register char c; - - while ((c = getch()) <= ' ' && c > EOS) - ; - return(c); -} - -/* - * Error handler - resets environment to eval(), prints an error, - * and returns FALSE. - */ -experr(msg) -char *msg; -{ - printf("mp: %s\n",msg); - longjmp(expjump, -1); /* Force eval() to return FALSE */ -} +#define TRUE 1 + +#include <stdio.h> +#include <setjmp.h> +static jmp_buf expjump; /* Error exit point for expr() */ + +static unsigned char *nxtchr; /* Parser scan pointer */ + +#define deblank0 while ((unsigned)(*nxtchr-1) < ' ') nxtchr++ +#define deblank1 while ((unsigned)(*++nxtchr - 1) < ' ') +#define deblank2 nxtchr++; deblank1 + +#include "ourlims.h" +static char digval[1+UCHAR_MAX]; + +/* This file should work in any C implementation that doesn't have too + many characters to fit in one table. We use a table to convert + (unsigned) characters to numeric codes: + 0 to 9 for '0' to '9' + 10 to 35 for 'a' to 'z' + 10 to 35 for 'A' to 'Z' + 36 for '_' + Instead of asking whether tolower(c) == 'a' we ask whether + digval[c] == DIGIT_A, and so on. This essentially duplicates the + chtype[] table in main.c; we should use just one table. +*/ +#define DIGIT_A 10 +#define DIGIT_B 11 +#define DIGIT_C 12 +#define DIGIT_D 13 +#define DIGIT_E 14 +#define DIGIT_F 15 +#define DIGIT_G 16 +#define DIGIT_H 17 +#define DIGIT_I 18 +#define DIGIT_J 19 +#define DIGIT_K 20 +#define DIGIT_L 21 +#define DIGIT_M 22 +#define DIGIT_N 23 +#define DIGIT_O 24 +#define DIGIT_P 25 +#define DIGIT_Q 26 +#define DIGIT_R 27 +#define DIGIT_S 28 +#define DIGIT_T 29 +#define DIGIT_U 30 +#define DIGIT_V 31 +#define DIGIT_W 32 +#define DIGIT_X 33 +#define DIGIT_Y 34 +#define DIGIT_Z 35 + + +#ifdef __STDC__ +static long int query(int); +#else +static long int query(); +#endif + + +/* experr(msg) + prints an error message, resets environment to expr(), and + forces expr() to return FALSE. +*/ +void experr(msg) + char *msg; + { + (void) fprintf(stderr, "m4: %s\n", msg); + longjmp(expjump, -1); /* Force expr() to return FALSE */ + } + + +/* <numcon> ::= '0x' <hex> | '0X' <hex> | '0' <oct> | <dec> + For ANSI C, an integer may be followed by u, l, ul, or lu, + in any mix of cases. We accept and ignore those letters; + all the numbers are treated as long. +*/ +static long int numcon(doit) + int doit; + { + register long int v; /* current value */ + register int b; /* base (radix) */ + register int c; /* character or digit value */ + + if (!doit) { + do nxtchr++; while (digval[*nxtchr] <= 36); + deblank0; + return 0; + } + + v = digval[*nxtchr++]; /* We already know it's a digit */ + if (v != 0) { + b = 10; /* decimal number */ + } else + if (digval[*nxtchr] == DIGIT_X) { + nxtchr++; + b = 16; /* hexadecimal number */ + } else { + b = 8; /* octal number */ + } + do { + while (digval[c = *nxtchr++] < b) v = v*b + digval[c]; + } while (c == '_'); + while (digval[c] == DIGIT_L || digval[c] == DIGIT_U) c = *nxtchr++; + nxtchr--; /* unread c */ + if ((unsigned)(c-1) < ' ') { deblank1; } + return v; + } + + +/* <charcon> ::= <qt> { <char> } <qt> + Note: multibyte constants are accepted. + Note: BEL (\a) and ESC (\e) have the same values in EBCDIC and ASCII. +*/ +static long int charcon(doit) + int doit; + { + register int i; + long int value; + register int c; + int q; + int v[sizeof value]; + + q = *nxtchr++; /* the quote character */ + for (i = 0; ; i++) { + c = *nxtchr++; + if (c == q) { /* end of literal, or doubled quote */ + if (*nxtchr != c) break; + nxtchr++; /* doubled quote stands for one quote */ + } + if (i == sizeof value) experr("Unterminated character constant"); + if (c == '\\') { + switch (c = *nxtchr++) { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + c -= '0'; + if ((unsigned)(*nxtchr - '0') < 8) + c = (c << 3) | (*nxtchr++ - '0'); + if ((unsigned)(*nxtchr - '0') < 8) + c = (c << 3) | (*nxtchr++ - '0'); + break; + case 'n': case 'N': c = '\n'; break; + case 'r': case 'R': c = '\r'; break; + case 't': case 'T': c = '\t'; break; + case 'b': case 'B': c = '\b'; break; + case 'f': case 'F': c = '\f'; break; + case 'a': case 'A': c = 007; break; + case 'e': case 'E': c = 033; break; +#if ' ' == 64 + case 'd': case 'D': c = 045; break; /*EBCDIC DEL */ +#else + case 'd': case 'D': c = 127; break; /* ASCII DEL */ +#endif + default : break; + } + } + v[i] = c; + } + deblank0; + if (!doit) return 0; + for (value = 0; --i >= 0; ) value = (value << CHAR_BIT) | v[i]; + return value; + } + + +/* <unary> ::= <unop> <unary> | <factor> + <unop> ::= '!' || '~' | '-' + <factor> ::= '(' <query> ')' | <'> <char> <'> | <"> <char> <"> | <num> +*/ +static long int unary(doit) + int doit; + { + long int v; + + switch (nxtchr[0]) { + case 'n': case 'N': + if (digval[nxtchr[1]] != DIGIT_O + || digval[nxtchr[2]] != DIGIT_T) + experr("Bad 'not'"); + nxtchr += 2; + case '!': deblank1; return !unary(doit); + case '~': deblank1; return ~unary(doit); + case '-': deblank1; return -unary(doit); + case '+': deblank1; return unary(doit); + case '(': deblank1; v = query(doit); + if (nxtchr[0] != ')') experr("Bad factor"); + deblank1; return v; + case '\'': + case '\"': return charcon(doit); + case '0': case '1': case '2': + case '3': case '4': case '5': + case '6': case '7': case '8': + case '9': return numcon(doit); + default : experr("Bad constant"); + } + return 0; /*NOTREACHED*/ + } + + +/* <term> ::= <unary> { <mulop> <unary> } + <mulop> ::= '*' | '/' || '%' +*/ +static long int term(doit) + int doit; + { + register long int vl, vr; + + vl = unary(doit); + for (;;) + switch (nxtchr[0]) { + case '*': + deblank1; + vr = unary(doit); + if (doit) vl *= vr; + break; + case 'd': case 'D': + if (digval[nxtchr[1]] != DIGIT_I + || digval[nxtchr[2]] != DIGIT_V) + experr("Bad 'div'"); + nxtchr += 2; + /*FALLTHROUGH*/ + case '/': + deblank1; + vr = unary(doit); + if (doit) { + if (vr == 0) experr("Division by 0"); + vl /= vr; + } + break; + case 'm': case 'M': + if (digval[nxtchr[1]] != DIGIT_O + || digval[nxtchr[2]] != DIGIT_D) + experr("Bad 'mod'"); + nxtchr += 2; + /*FALLTHROUGH*/ + case '%': + deblank1; + vr = unary(doit); + if (doit) { + if (vr != 0) vl %= vr; + } + break; + default: + return vl; + } + /*NOTREACHED*/ + } + +/* <primary> ::= <term> { <addop> <term> } + <addop> ::= '+' | '-' +*/ +static long int primary(doit) + int doit; + { + register long int vl; + + vl = term(doit); + for (;;) + if (nxtchr[0] == '+') { + deblank1; + if (doit) vl += term(doit); else (void)term(doit); + } else + if (nxtchr[0] == '-') { + deblank1; + if (doit) vl -= term(doit); else (void)term(doit); + } else + return vl; + /*NOTREACHED*/ + } + + +/* <shift> ::= <primary> { <shop> <primary> } + <shop> ::= '<<' | '>>' +*/ +static long int shift(doit) + int doit; + { + register long int vl, vr; + + vl = primary(doit); + for (;;) { + if (nxtchr[0] == '<' && nxtchr[1] == '<') { + deblank2; + vr = primary(doit); + } else + if (nxtchr[0] == '>' && nxtchr[1] == '>') { + deblank2; + vr = -primary(doit); + } else { + return vl; + } + /* The following code implements shifts portably */ + /* Shifts are signed shifts, and the shift count */ + /* acts like repeated one-bit shifts, not modulo anything */ + if (doit) { + if (vr >= LONG_BIT) { + vl = 0; + } else + if (vr <= -LONG_BIT) { + vl = -(vl < 0); + } else + if (vr > 0) { + vl <<= vr; + } else + if (vr < 0) { + vl = (vl >> -vr) | (-(vl < 0) << (LONG_BIT + vr)); + } + } + } + /*NOTREACHED*/ + } + + +/* <relat> ::= <shift> { <rel> <shift> } + <rel> ::= '<=' | '>=' | '=<' | '=>' | '<' | '>' + Here I rely on the fact that '<<' and '>>' are swallowed by <shift> +*/ +static long int relat(doit) + int doit; + { + register long int vl; + + vl = shift(doit); + for (;;) + switch (nxtchr[0]) { + case '=': + switch (nxtchr[1]) { + case '<': /* =<, take as <= */ + deblank2; + vl = vl <= shift(doit); + break; + case '>': /* =>, take as >= */ + deblank2; + vl = vl >= shift(doit); + break; + default: /* == or =; OOPS */ + return vl; + } + break; + case '<': + if (nxtchr[1] == '=') { /* <= */ + deblank2; + vl = vl <= shift(doit); + } else + if (nxtchr[1] == '>') { /* <> (Pascal) */ + deblank2; + vl = vl != shift(doit); + } else { /* < */ + deblank1; + vl = vl < shift(doit); + } + break; + case '>': + if (nxtchr[1] == '=') { /* >= */ + deblank2; + vl = vl >= shift(doit); + } else { /* > */ + deblank1; + vl = vl > shift(doit); + } + break; + default: + return vl; + } + /*NOTREACHED*/ + } + + +/* <eql> ::= <relat> { <eqrel> <relat> } + <eqlrel> ::= '!=' | '==' | '=' +*/ +static long int eql(doit) + int doit; + { + register long int vl; + + vl = relat(doit); + for (;;) + if (nxtchr[0] == '!' && nxtchr[1] == '=') { + deblank2; + vl = vl != relat(doit); + } else + if (nxtchr[0] == '=' && nxtchr[1] == '=') { + deblank2; + vl = vl == relat(doit); + } else + if (nxtchr[0] == '=') { + deblank1; + vl = vl == relat(doit); + } else + return vl; + /*NOTREACHED*/ + } + + +/* <band> ::= <eql> { '&' <eql> } +*/ +static long int band(doit) + int doit; + { + register long int vl; + + vl = eql(doit); + while (nxtchr[0] == '&' && nxtchr[1] != '&') { + deblank1; + if (doit) vl &= eql(doit); else (void)eql(doit); + } + return vl; + } + + +/* <bxor> ::= <band> { '^' <band> } +*/ +static long int bxor(doit) + int doit; + { + register long int vl; + + vl = band(doit); + while (nxtchr[0] == '^') { + deblank1; + if (doit) vl ^= band(doit); else (void)band(doit); + } + return vl; + } + + +/* <bor> ::= <bxor> { '|' <bxor> } +*/ +static long int bor(doit) + int doit; + { + register long int vl; + + vl = bxor(doit); + while (nxtchr[0] == '|' && nxtchr[1] != '|') { + deblank1; + if (doit) vl |= bxor(doit); else (void)bxor(doit); + } + return vl; + } + + +/* <land> ::= <bor> { '&&' <bor> } +*/ +static long int land(doit) + int doit; + { + register long int vl; + + vl = bor(doit); + for (;;) { + if (nxtchr[0] == '&') { + if (nxtchr[1] != '&') break; + deblank2; + } else + if (digval[nxtchr[0]] == DIGIT_A) { + if (digval[nxtchr[1]] != DIGIT_N) break; + if (digval[nxtchr[2]] != DIGIT_D) break; + nxtchr += 2; deblank1; + } else { + /* neither && nor and */ + break; + } + vl = bor(doit && vl) != 0; + } + return vl; + } + + +/* <lor> ::= <land> { '||' <land> } +*/ +static long int lor(doit) + int doit; + { + register long int vl; + + vl = land(doit); + for (;;) { + if (nxtchr[0] == '|') { + if (nxtchr[1] != '|') break; + } else + if (digval[nxtchr[0]] == DIGIT_O) { + if (digval[nxtchr[1]] != DIGIT_R) break; + } else { + /* neither || nor or */ + break; + } + deblank2; + vl = land(doit && !vl) != 0; + } + return vl; + } + + +/* <query> ::= <lor> [ '?' <query> ':' <query> ] +*/ +static long int query(doit) + int doit; + { + register long int bool, true_val, false_val; + + bool = lor(doit); + if (*nxtchr != '?') return bool; + deblank1; + true_val = query(doit && bool); + if (*nxtchr != ':') experr("Bad query"); + deblank1; + false_val = query(doit && !bool); + return bool ? true_val : false_val; + } + + +static void initialise_digval() + { + register unsigned char *s; + register int c; + + for (c = 0; c <= UCHAR_MAX; c++) digval[c] = 99; + for (c = 0, s = (unsigned char *)"0123456789"; + /*while*/ *s; + /*doing*/ digval[*s++] = c++) /* skip */; + for (c = 10, s = (unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + /*while*/ *s; + /*doing*/ digval[*s++] = c++) /* skip */; + for (c = 10, s = (unsigned char *)"abcdefghijklmnopqrstuvwxyz"; + /*while*/ *s; + /*doing*/ digval[*s++] = c++) /* skip */; + digval['_'] = 36; + } + + +long int expr(expbuf) + char *expbuf; + { + register int rval; + + if (digval['1'] == 0) initialise_digval(); + nxtchr = (unsigned char *)expbuf; + deblank0; + if (setjmp(expjump) != 0) return FALSE; + rval = query(TRUE); + if (*nxtchr) experr("Ill-formed expression"); + return rval; + } + diff --git a/usr.bin/m4/extr.h b/usr.bin/m4/extr.h index b509a4a854..bf3ed92cec 100644 --- a/usr.bin/m4/extr.h +++ b/usr.bin/m4/extr.h @@ -1,59 +1,156 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Ozan Yigit. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)extr.h 5.2 (Berkeley) 6/1/90 - */ +/* Header : extr.h + Author : Ozan Yigit + Updated: %G% +*/ +#ifndef putback extern ndptr hashtab[]; /* hash table for macros etc. */ -extern char buf[]; /* push-back buffer */ +extern char buf[]; /* push-back buffer */ extern char *bp; /* first available character */ +extern char *bb; /* current beginning of bp */ extern char *endpbb; /* end of push-back buffer */ -extern stae mstack[]; /* stack of m4 machine */ +extern stae mstack[]; /* stack of m4 machine */ extern char *ep; /* first free char in strspace */ extern char *endest; /* end of string space */ -int sp; /* current m4 stack pointer */ -int fp; /* m4 call frame pointer */ +extern int sp; /* current m4 stack pointer */ +extern int fp; /* m4 call frame pointer */ +extern char *bbstack[]; extern FILE *infile[]; /* input file stack (0=stdin) */ extern FILE *outfile[]; /* diversion array(0=bitbucket)*/ extern FILE *active; /* active output file pointer */ extern char *m4temp; /* filename for diversions */ -extern int ilevel; /* input file stack pointer */ -extern int oindex; /* diversion index.. */ +extern int UNIQUE; /* where to change m4temp */ +extern int ilevel; /* input file stack pointer */ +extern int oindex; /* diversion index.. */ extern char *null; /* as it says.. just a null.. */ extern char *m4wraps; /* m4wrap string default.. */ -extern char lquote; /* left quote character (`) */ -extern char rquote; /* right quote character (') */ -extern char scommt; /* start character for comment */ -extern char ecommt; /* end character for comment */ +extern char lquote; /* left quote character (`) */ +extern char rquote; /* right quote character (') */ +extern char vquote; /* verbatim quote character ^V */ +extern char scommt; /* start character for comment */ +extern char ecommt; /* end character for comment */ + +/* inlined versions of chrsave() and putback() */ + +extern char pbmsg[]; /* error message for putback */ +extern char csmsg[]; /* error message for chrsave */ + +#define putback(c) do { if (bp >= endpbb) error(pbmsg); *bp++ = c; } while (0) +#define chrsave(c) do { if (ep >= endest) error(csmsg); *ep++ = c; } while (0) + +/* getopt() interface */ + +extern char * optarg; +extern int optind; +extern int getopt(); + +#ifdef __STDC__ +#include <stdlib.h> + +/* functions from misc.c */ + +extern char * strsave(char *); +extern int indx(char *, char *); +extern void pbstr(char *); +extern void pbqtd(char *); +extern void pbnum(int); +extern void pbrad(long int, int, int); +extern void getdiv(int); +extern void killdiv(); +extern void error(char *); +extern void onintr(int); +extern void usage(); + +/* functions from look.c */ + +extern ndptr lookup(char *); +extern ndptr addent(char *); +extern void remhash(char *, int); +extern void addkywd(char *, int); + +/* functions from int2str.c */ + +extern char* int2str(/* char*, int, long */); + +/* functions from serv.c */ + +extern void expand(char **, int); +extern void dodefine(char *, char *); +extern void dopushdef(char *, char *); +extern void dodefn(char *); +extern void dodump(char **, int); +extern void doifelse(char **, int); +extern int doincl(char *); +extern void dochq(char **, int); +extern void dochc(char **, int); +extern void dodiv(int); +extern void doundiv(char **, int); +extern void dosub(char **, int); +extern void map(char *, char *, char *, char *); +#ifdef EXTENDED +extern int dopaste(char *); +extern void m4trim(char **, int); +extern void dodefqt(char **, int); +extern void doqutr(char **, int); +#endif + +/* functions from expr.c */ + +extern long expr(char *); + +#else + +/* functions from misc.c */ + +extern char * malloc(); +extern char * strsave(); +extern int indx(); +extern void pbstr(); +extern void pbqtd(); +extern void pbnum(); +extern void pbrad(); +extern void getdiv(); +extern void killdiv(); +extern void error(); +extern int onintr(); +extern void usage(); + +/* functions from look.c */ + +extern ndptr lookup(); +extern ndptr addent(); +extern void remhash(); +extern void addkywd(); + +/* functions from int2str.c */ + +extern char* int2str(/* char*, int, long */); + +/* functions from serv.c */ + +extern void expand(); +extern void dodefine(); +extern void dopushdef(); +extern void dodefn(); +extern void dodump(); +extern void doifelse(); +extern int doincl(); +extern void dochq(); +extern void dochc(); +extern void dodiv(); +extern void doundiv(); +extern void dosub(); +extern void map(); +#ifdef EXTENDED +extern int dopaste(); +extern void m4trim(); +extern void dodefqt(); +extern void doqutr(); +#endif + +/* functions from expr.c */ + +extern long expr(); + +#endif +#endif diff --git a/usr.bin/m4/int2str.c b/usr.bin/m4/int2str.c new file mode 100644 index 0000000000..5ecc85b9ad --- /dev/null +++ b/usr.bin/m4/int2str.c @@ -0,0 +1,58 @@ +/* File : int2str.c + Author : Richard A. O'Keefe + Updated: 6 February 1993 + Defines: int2str() + + int2str(dst, radix, val) + converts the (long) integer "val" to character form and moves it to + the destination string "dst" followed by a terminating NUL. The + result is normally a pointer to this NUL character, but if the radix + is dud the result will be NullS and nothing will be changed. + + If radix is -2..-36, val is taken to be SIGNED. + If radix is 2.. 36, val is taken to be UNSIGNED. + That is, val is signed if and only if radix is. You will normally + use radix -10 only through itoa and ltoa, for radix 2, 8, or 16 + unsigned is what you generally want. +*/ + +static char dig_vec[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + +char *int2str(dst, radix, val) + register char *dst; + register int radix; + register long val; + { + char buffer[65]; /* Ready for 64-bit machines */ + register char *p; + + if (radix < 2 || radix > 36) { /* Not 2..36 */ + if (radix > -2 || radix < -36) return (char *)0; + if (val < 0) { + *dst++ = '-'; + val = -val; + } + radix = -radix; + } + /* The slightly contorted code which follows is due to the + fact that few machines directly support unsigned long / and %. + Certainly the VAX C compiler generates a subroutine call. In + the interests of efficiency (hollow laugh) I let this happen + for the first digit only; after that "val" will be in range so + that signed integer division will do. Sorry 'bout that. + CHECK THE CODE PRODUCED BY YOUR C COMPILER. The first % and / + should be unsigned, the second % and / signed, but C compilers + tend to be extraordinarily sensitive to minor details of style. + This works on a VAX, that's all I claim for it. + */ + p = &buffer[sizeof buffer]; + *--p = '\0'; + *--p = dig_vec[(unsigned long)val%(unsigned long)radix]; + val = (unsigned long)val/(unsigned long)radix; + while (val != 0) *--p = dig_vec[val%radix], val /= radix; + while (*dst++ = *p++) ; + return dst-1; + } + diff --git a/usr.bin/m4/look.c b/usr.bin/m4/look.c index 1f95d20ab8..965e590190 100644 --- a/usr.bin/m4/look.c +++ b/usr.bin/m4/look.c @@ -1,153 +1,107 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Ozan Yigit. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)look.c 5.3 (Berkeley) 2/26/91"; -#endif /* not lint */ +/* File : look.c + Author : Ozan Yigit + Updated: 4 May 1992 + Purpose: Hash table for M4 +*/ -/* - * look.c - * Facility: m4 macro processor - * by: oz - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> #include "mdef.h" #include "extr.h" +ndptr hashtab[HASHSIZE]; + + /* - * hash - compute hash value using the proverbial - * hashing function. Taken from K&R. + * hash - get a hash value for string s */ -hash (name) -register char *name; +int +hash(name) +char *name; { - register int h = 0; - while (*name) - h += *name++; - return (h % HASHSIZE); + register unsigned long h = 0; + + while (*name) + h = (h << 5) + h + *name++; + + return h % HASHSIZE; } -/* - * lookup - find name in the hash table - * +/* + * lookup(name) - find name in the hash table */ ndptr lookup(name) -char *name; -{ + char *name; + { register ndptr p; for (p = hashtab[hash(name)]; p != nil; p = p->nxtptr) - if (strcmp(name, p->name) == 0) - break; - return (p); -} + if (strcmp(name, p->name) == 0) + break; + return p; + } -/* - * addent - hash and create an entry in the hash - * table. The new entry is added in front - * of a hash bucket. +/* + * addent(name) - hash and create an entry in the hash table. + * The new entry is added at the front of a hash bucket. + * BEWARE: the type and defn fields are UNDEFINED. */ ndptr addent(name) -char *name; -{ - register int h; - ndptr p; - - h = hash(name); - if ((p = (ndptr) malloc(sizeof(struct ndblock))) != NULL) { - p->nxtptr = hashtab[h]; - hashtab[h] = p; - p->name = strdup(name); - } - else - error("m4: no more memory."); + char *name; + { + register ndptr p, *h; + + p = (ndptr)malloc(sizeof *p); + if (p == NULL) error("m4: no more memory."); + h = &hashtab[hash(name)]; + p->name = strsave(name); + p->defn = null; + p->nxtptr = *h; + *h = p; return p; -} + } -/* - * remhash - remove an entry from the hashtable - * + +/* + * addkywd(name, type) - stores a keyword in the hash table. */ -remhash(name, all) -char *name; -int all; -{ - register int h; - register ndptr xp, tp, mp; - - h = hash(name); - mp = hashtab[h]; - tp = nil; - while (mp != nil) { - if (strcmp(mp->name, name) == 0) { - mp = mp->nxtptr; - if (tp == nil) { - freent(hashtab[h]); - hashtab[h] = mp; - } - else { - xp = tp->nxtptr; - tp->nxtptr = mp; - freent(xp); - } - if (!all) - break; - } - else { - tp = mp; - mp = mp->nxtptr; - } - } -} +void addkywd(name, type) + char *name; + int type; + { + register ndptr p = addent(name); + p->type = type | STATIC; + } -/* - * freent - free a hashtable information block - * + +/* + * remhash(name, all) + * remove one entry (all==0) or all entries (all!=0) for a given name + * from the hash table. All hash table entries must have been obtained + * from malloc(), so it is safe to free the records themselves. + * However, the ->name and ->defn fields might point to storage which + * was obtained from strsave() -- in which case they may be freed -- or + * to static storage -- in which case they must not be freed. If the + * STATIC bit is set, the fields are not to be freed. */ -freent(p) -ndptr p; -{ - if (!(p->type & STATIC)) { - free(p->name); - if (p->defn != null) - free(p->defn); +void remhash(name, all) + char *name; + int all; + { + register ndptr p, *h; + /* h always points to the pointer to p */ + + h = &hashtab[hash(name)]; + while ((p = *h) != nil) { + if (strcmp(p->name, name) == 0) { + *h = p->nxtptr; /* delink this record */ + if (!(p->type & STATIC)) { /* free the name and defn */ + free(p->name); /* if they came from strsave */ + if (p->defn != null) free(p->defn); + } /* otherwise leave them */ + free(p); /* free the record itself */ + if (!all) return; /* first occurrence has gone */ + } else { + h = &(p->nxtptr); + } } - free(p); -} + } diff --git a/usr.bin/m4/m4.1 b/usr.bin/m4/m4.1 new file mode 100644 index 0000000000..8fd1bf09bb --- /dev/null +++ b/usr.bin/m4/m4.1 @@ -0,0 +1,179 @@ +.\" +.\" @(#) m4.1,v 1.1 1993/06/18 21:50:31 glass Exp +.\" +.Dd January 26, 1993 +.Dt m4 1 +.Os +.Sh NAME +.Nm m4 +.Nd macro language processor +.Sh SYNOPSIS +.Nm m4 +.Oo +.Fl D Ns Ar name Ns Op Ar =value +.Oc +.Op Fl U Ns Ar name +.Sh DESCRIPTION +The +.Nm m4 +utility is a macro processor that can be used as a front end to any +language (e.g., C, ratfor, fortran, lex, and yacc). +.Nm m4 +reads from the standard input and writes +the processed text to the standard output. +.Pp +Macro calls have the form name(argument1[, argument2, ...,] argumentN). +.Pp +There cannot be any space following the macro name and the open +parentheses '('. If the macro name is not followed by an open +parentheses it is processed with no arguments. +.Pp +Macro names consist of a leading alphabetic or underscore +possibly followed by alphanumeric or underscore characters, therefore +valid macro names match this pattern [a-zA-Z_][a-zA-Z0-9_]*. +.Pp +In arguments to macros, leading unquoted space, tab and newline +characters are ignored. To quote strings use left and right single +quotes (e.g., ` this is a string with a leading space'). You can change +the quote characters with the changequote built-in macro. +.Pp +The options are as follows: +.Bl -tag -width "-Dname[=value]xxx" +.It Fl D Ns Ar name Ns Oo +.Ar =value +.Oc +Define the symbol +.Ar name +to have some value (or NULL). +.It Fl "U" Ns Ar "name" +Undefine the symbol +.Ar name . +.El +.Sh SYNTAX +.Nm m4 +provides the following built-in macros. They may be +redefined, loosing their original meaning. +Return values are NULL unless otherwise stated. +.Bl -tag -width changequotexxx +.It changecom +Change the start and end comment sequences. The default is +the pound sign `#' and the newline character. With no arguments +comments are turned off. The maximum length for a comment marker is +five characters. +.It changequote +Defines the quote symbols to be the first and second arguments. +The symbols may be up to five characters long. If no arguments are +given it restores the default open and close single quotes. +.It decr +Decrements the argument by 1. The argument must be a valid numeric string. +.It define +Define a new macro named by the first argument to have the +value of the second argument. Each occurrence of $n (where n +is 0 through 9) is replaced by the n'th argument. $0 is the name +of the calling macro. Undefined arguments are replaced by a +NULL string. $# is replaced by the number of arguments; $* +is replaced by all arguments comma separated; $@ is the same +as $* but all arguments are quoted against further expansion. +.It defn +Returns the quoted definition for each argument. This can be used to rename +macro definitions (even for built-in macros). +.It divert +There are 10 output queues (numbered 0-9). +At the end of processing +.Nm m4 +concatenates all the queues in numerical order to produce the +final output. Initially the output queue is 0. The divert +macro allows you to select a new output queue (an invalid argument +passed to divert causes output to be discarded). +.It divnum +Returns the current output queue number. +.It dnl +Discard input characters up to and including the next newline. +.It dumpdef +Prints the names and definitions for the named items, or for everything +if no arguments are passed. +.It errprint +Prints the first argument on the standard error output stream. +.It eval +Computes the first argument as an arithmetic expression using 32-bit +arithmetic. Operators are the standard C ternary, arithmetic, logical, +shift, relational, bitwise, and parentheses operators. You can specify +octal, decimal, and hexadecimal numbers as in C. The second argument (if +any) specifies the radix for the result and the third argument (if +any) specifies the minimum number of digits in the result. +.It expr +This is an alias for eval. +.It ifdef +If the macro named by the first argument is defined then return the second +argument, otherwise the third. If there is no third argument, +the value is NULL. The word `unix' is predefined. +.It ifelse +If the first argument matches the second argument then ifelse returns +the third argument. If the match fails the three arguments are +discarded and the next three arguments are used until there is +zero or one arguments left, either this last argument or NULL is +returned if no other matches were found. +.It include +Returns the contents of the file specified in the first argument. +Include aborts with an error message if the file cannot be included. +.It incr +Increments the argument by 1. The argument must be a valid numeric string. +.It index +Returns the index of the second argument in the first argument (e.g., +index(the quick brown fox jumped, fox) returns 16). If the second +argument is not found index returns -1. +.It len +Returns the number of characters in the first argument. Extra arguments +are ignored. +.It m4exit +Immediately exits with the return value specified by the first argument, +0 if none. +.It m4wrap +Allows you to define what happens at the final EOF, usually for cleanup +purposes (e.g., m4wrap("cleanup(tempfile)") causes the macro cleanup to +invoked after all other processing is done.) +.It maketemp +Translates the string XXXXX in the first argument with the current process +ID leaving other characters alone. This can be used to create unique +temporary file names. +.It paste +Includes the contents of the file specified by the first argument without +any macro processing. Aborts with an error message if the file cannot be +included. +.It popdef +Restores the pushdef'ed definition for each argument. +.It pushdef +Takes the same arguments as define, but it saves the definition on a +stack for later retrieval by popdef. +.It shift +Returns all but the first argument, the remaining arguments are +quoted and pushed back with commas in between. The quoting +nullifies the effect of the extra scan that will subsequently be +performed. +.It sinclude +Similar to include, except it ignores any errors. +.It spaste +Similar to spaste, except it ignores any errors. +.It substr +Returns a substring of the first argument starting at the offset specified +by the second argument and the length specified by the third argument. +If no third argument is present it returns the rest of the string. +.It syscmd +Passes the first argument to the shell. Nothing is returned. +.It sysval +Returns the return value from the last syscmd. +.It translit +Transliterate the characters in the first argument from the set +given by the second argument to the set given by the third. You cannot +use +.Xr tr 1 +style abbreviations. +.It undefine +Removes the definition for the macro specified by the first argument. +.It undivert +Flushes the named output queues (or all queues if no arguments). +.It unix +A pre-defined macro for testing the OS platform. +.El +.Sh AUTHOR +Ozan Yigit <oz@sis.yorku.ca> and Richard A. O'Keefe (ok@goanna.cs.rmit.OZ.AU) diff --git a/usr.bin/m4/main.c b/usr.bin/m4/main.c index 4326b07bf9..14ccd9df6b 100644 --- a/usr.bin/m4/main.c +++ b/usr.bin/m4/main.c @@ -1,56 +1,18 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Ozan Yigit. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ +/* File : main.c + Author : Ozan Yigit + Updated: 4 May 1992 + Defines: M4 macro processor. +*/ -#ifndef lint -static char sccsid[] = "@(#)main.c 5.6 (Berkeley) 3/6/91"; -#endif /* not lint */ +#include "mdef.h" +#include "extr.h" +#include "ourlims.h" -/* - * main.c - * Facility: m4 macro processor - * by: oz - */ +char chtype[1 - EOF + UCHAR_MAX]; -#include <signal.h> -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "mdef.h" -#include "pathnames.h" +#define is_sym1(c) (chtype[(c)-EOF] > 10) +#define is_sym2(c) (chtype[(c)-EOF] > 0) +#define is_blnk(c) ((unsigned)((c)-1) < ' ') /* * m4 - macro processor @@ -83,11 +45,105 @@ static char sccsid[] = "@(#)main.c 5.6 (Berkeley) 3/6/91"; * * Modification History: * + * Mar 26 1992 RAOK 1. Eliminated magic numbers 8, 255, 256 in favour + * of the standard limits CHAR_BIT, UCHAR_MAX, which + * are in the new header ourlims.h. This is part of + * the "8-bit-clean M4" project. To the best of my + * belief, all of the code should work in EBCDIC, + * ASCII, DEC MNCS, ISO 8859/n, or the Mac character + * set, as long as chars are unsigned. There are + * still some places where signed bytes can cause + * trouble. + * + * 2. Changed expr() to use long int rather than int. + * This is so that we'd get 32-bit arithmetic on a Sun, + * Encore, PC, Mac &c. As part of this, the code for + * shifts has been elaborated to yield signed shifts + * on all machines. The charcon() function didn't work + * with multi-character literals, although it was meant + * to. Now it does. pbrad() has been changed so that + * eval('abcd',0) => abcd, not dcba, which was useless. + * + * 3. I finally got sick of the fact that &&, ||, and + * ?: always evaluate all their arguments. This is + * consistent with UNIX System V Release 3, but I for + * one don't see anything to gain by having eval(0&&1/0) + * crash when it would simply yield 0 in C. Now these + * operators are more consistent with the C preprocessor. + * + * Nov 13 1992 RAOK Added the quoter facility. The purpose of this is + * to make it easier to generate data for a variety of + * programming languages, including sh, awk, Lisp, C. + * There are two holes in the implementation: dumpdef + * prints junk and undefine doesn't release everything. + * This was mainly intended as a prototype to show that + * it could be done. + * + * Jun 16 1992 RAOK Added vquote and gave changequote a 3rd argument. + * The idea of this is to make it possible to quote + * ANY string, including one with unbalanced ` or '. + * I also made eval(c,0) convert decimal->ASCII, so + * that eval(39,0) yields ' and eval(96,0) yields `. + * + * Apr 28 1992 RAOK Used gcc to find and fix ANSI clashes, so that + * PD M4 could be ported to MS-DOS (Turbo C 3). + * Main known remaining problem: use of mktemp(). + * Also, command line handling needs to be worked out. + * + * Mar 26 1992 RAOK PD M4 now accepts file names on the command line + * just like UNIX M4. Warning: macro calls must NOT + * cross file boundaries. UNIX M4 doesn't mind; + * (m4 a b c) and (cat a b c | m4) are just the same + * except for error messages. PD M4 will report an + * unexpected EOF if a file ends while a macro call or + * string is still being parsed. When there is one + * file name argument, or none, you can't tell the + * difference, and that's all I need. + * + * May 15 1991 RAOK DIVNAM was a string constant, but was changed! + * Fixed that and a couple of other things to make + * GCC happy. (Also made "foo$bar" get through.) + * + * Apr 17 1991 RAOK There was a major mistake. If you did + * define(foo, `1 include(bar) 2') where + * file bar held "-bar-" you would naturally + * expect "1 -bar- 2" as the output, but you + * got "1 2-bar-". That is, include file + * processing was postponed until all macros + * had been expanded. The macro gpbc() was + * at fault. I added bb, bbstack[], and the + * code in main.c and serv.c that maintains + * them, in order to work around this bug. + * + * Apr 12 1991 RAOK inspect() didn't handle overflow well. + * Added the automatically maintained macro + * __FILE__, just as in C. To suppress it, + * define NO__FILE. At some point, $# had + * been made to return a value that was off + * by one; it now agrees with SysV M4. + * + * Aug 13 1990 RAOK The System V expr() has three arguments: + * expression [, radix:10 [, mindigits: 1]] + * Brought in my int2str() and wrote pbrad() + * to make this work here. With the wrong # + * of args, acts like System V. + * + * Aug 11 1990 RAOK Told expr.c about the Pascal operators + * not, div, mod, and, or + * so that Pascal constant expressions could + * be evaluated. (It still doesn't handle + * floats.) Fixed a mistake in 'character's. + * + * Apr 23 1988 RAOK Sped it up, mainly by making putback() and + * chrsave() into macros. + * Finished the -o option (was half done). + * Added the System V -e (interactive) option. + * * Jan 28 1986 Oz Break the whole thing into little * pieces, for easier (?) maintenance. * * Dec 12 1985 Oz Optimize the code, try to squeeze - * few microseconds out.. + * few microseconds out.. [didn't try very hard] * * Dec 05 1985 Oz Add getopt interface, define (-D), * undefine (-U) options. @@ -141,9 +197,9 @@ static char sccsid[] = "@(#)main.c 5.6 (Berkeley) 3/6/91"; * */ -ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */ char buf[BUFSIZE]; /* push-back buffer */ char *bp = buf; /* first available character */ +char *bb = buf; /* buffer beginning */ char *endpbb = buf+BUFSIZE; /* end of push-back buffer */ stae mstack[STACKMAX+1]; /* stack of m4 machine */ char strspace[STRSPMAX+1]; /* string space for evaluation */ @@ -151,335 +207,701 @@ char *ep = strspace; /* first free char in strspace */ char *endest= strspace+STRSPMAX;/* end of string space */ int sp; /* current m4 stack pointer */ int fp; /* m4 call frame pointer */ +char *bbstack[MAXINP]; /* stack where bb is saved */ FILE *infile[MAXINP]; /* input file stack (0=stdin) */ FILE *outfile[MAXOUT]; /* diversion array(0=bitbucket)*/ FILE *active; /* active output file pointer */ -char *m4temp; /* filename for diversions */ int ilevel = 0; /* input file stack pointer */ int oindex = 0; /* diversion index.. */ char *null = ""; /* as it says.. just a null.. */ char *m4wraps = ""; /* m4wrap string default.. */ char lquote = LQUOTE; /* left quote character (`) */ char rquote = RQUOTE; /* right quote character (') */ +char vquote = VQUOTE; /* verbatim quote character ^V */ char scommt = SCOMMT; /* start character for comment */ char ecommt = ECOMMT; /* end character for comment */ -struct keyblk keywrds[] = { /* m4 keywords to be installed */ - "include", INCLTYPE, - "sinclude", SINCTYPE, - "define", DEFITYPE, - "defn", DEFNTYPE, - "divert", DIVRTYPE, - "expr", EXPRTYPE, - "eval", EXPRTYPE, - "substr", SUBSTYPE, - "ifelse", IFELTYPE, - "ifdef", IFDFTYPE, - "len", LENGTYPE, - "incr", INCRTYPE, - "decr", DECRTYPE, - "dnl", DNLNTYPE, - "changequote", CHNQTYPE, - "changecom", CHNCTYPE, - "index", INDXTYPE, -#ifdef EXTENDED - "paste", PASTTYPE, - "spaste", SPASTYPE, +int strip = 0; /* throw away comments? */ + +/* Definitions of diversion files. The last 6 characters MUST be + "XXXXXX" -- that is a requirement of mktemp(). The character + '0' is to be replaced by the diversion number; we assume here + that it is just before the Xs. If not, you will have to alter + the definition of UNIQUE. +*/ + +#if unix +#include <sys/param.h> +#ifdef BSD +#include <paths.h> +#if __STDC__ +static char DIVNAM[] = _PATH_VARTMP "m40XXXXXX"; +#else +static char DIVNAM[] = "/usr/tmp/m40XXXXXX"; #endif - "popdef", POPDTYPE, - "pushdef", PUSDTYPE, - "dumpdef", DUMPTYPE, - "shift", SHIFTYPE, - "translit", TRNLTYPE, - "undefine", UNDFTYPE, - "undivert", UNDVTYPE, - "divnum", DIVNTYPE, - "maketemp", MKTMTYPE, - "errprint", ERRPTYPE, - "m4wrap", M4WRTYPE, - "m4exit", EXITTYPE, - "syscmd", SYSCTYPE, - "sysval", SYSVTYPE, - "unix", MACRTYPE, -}; - -#define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk)) - -extern ndptr lookup(); -extern ndptr addent(); -extern void onintr(); - -extern int optind; -extern char *optarg; - -main(argc,argv) - int argc; - char **argv; -{ - register int c; - register int n; - char *p; - - if (signal(SIGINT, SIG_IGN) != SIG_IGN) - signal(SIGINT, onintr); -#ifdef NONZEROPAGES - initm4(); +#else +static char DIVNAM[] = "/usr/tmp/m40XXXXXX"; #endif - initkwds(); - - while ((c = getopt(argc, argv, "tD:U:o:")) != EOF) - switch(c) { - - case 'D': /* define something..*/ - for (p = optarg; *p; p++) - if (*p == '=') - break; - if (*p) - *p++ = EOS; - dodefine(optarg, p); - break; - case 'U': /* undefine... */ - remhash(optarg, TOP); - break; - case 'o': /* specific output */ - case '?': - default: - usage(); - } +#else +#if vms +static char DIVNAM[] = "sys$login:m40XXXXXX"; +#else +static char DIVNAM[] = "M40XXXXXX"; /* was \M4, should it be \\M4? */ +#endif +#endif +int UNIQUE = sizeof DIVNAM - 7; /* where to change m4temp. */ +char *m4temp; /* filename for diversions */ +extern char *mktemp(); - infile[0] = stdin; /* default input (naturally) */ - active = stdout; /* default active output */ - m4temp = mktemp(strdup(DIVNAM));/* filename for diversions */ - sp = -1; /* stack pointer initialized */ - fp = 0; /* frame pointer initialized */ +void cantread(s) + char *s; + { + fprintf(stderr, "m4: %s: ", s); + error("cannot open for input."); + } - macro(); /* get some work done here */ - if (*m4wraps) { /* anything for rundown ?? */ - ilevel = 0; /* in case m4wrap includes.. */ - putback(EOF); /* eof is a must !! */ - pbstr(m4wraps); /* user-defined wrapup act */ - macro(); /* last will and testament */ - } +/* initkwds() + initialises the hash table to contain all the m4 built-in functions. + The original version breached module boundaries, but there did not + seem to be any benefit in that. +*/ +static void initkwds() + { + register int i; + static struct { char *name; int type; } keyword[] = + { + "include", INCLTYPE, + "sinclude", SINCTYPE, + "define", DEFITYPE, + "defn", DEFNTYPE, + "divert", DIVRTYPE, + "expr", EXPRTYPE, + "eval", EXPRTYPE, + "substr", SUBSTYPE, + "ifelse", IFELTYPE, + "ifdef", IFDFTYPE, + "len", LENGTYPE, + "incr", INCRTYPE, + "decr", DECRTYPE, + "dnl", DNLNTYPE, + "changequote", CHNQTYPE, + "changecom", CHNCTYPE, + "index", INDXTYPE, +#ifdef EXTENDED + "paste", PASTTYPE, + "spaste", SPASTYPE, + "m4trim", TRIMTYPE, + "defquote", DEFQTYPE, +#endif + "popdef", POPDTYPE, + "pushdef", PUSDTYPE, + "dumpdef", DUMPTYPE, + "shift", SHIFTYPE, + "translit", TRNLTYPE, + "undefine", UNDFTYPE, + "undivert", UNDVTYPE, + "divnum", DIVNTYPE, + "maketemp", MKTMTYPE, + "errprint", ERRPTYPE, + "m4wrap", M4WRTYPE, + "m4exit", EXITTYPE, +#if unix || vms + "syscmd", SYSCTYPE, + "sysval", SYSVTYPE, +#endif +#if unix + "unix", MACRTYPE, +#else +#if vms + "vms", MACRTYPE, +#endif +#endif + (char*)0, 0 + }; + + for (i = 0; keyword[i].type != 0; i++) + addkywd(keyword[i].name, keyword[i].type); + } + + +/* inspect(Name) + Build an input token.., considering only those which start with + [A-Za-z_]. This is fused with lookup() to speed things up. + name must point to an array of at least MAXTOK characters. +*/ +ndptr inspect(name) + char *name; + { + register char *tp = name; + register char *etp = name+(MAXTOK-1); + register int c; + register unsigned long h = 0; + register ndptr p; - if (active != stdout) - active = stdout; /* reset output just in case */ - for (n = 1; n < MAXOUT; n++) /* default wrap-up: undivert */ - if (outfile[n] != NULL) - getdiv(n); - /* remove bitbucket if used */ - if (outfile[0] != NULL) { - (void) fclose(outfile[0]); - m4temp[UNIQUE] = '0'; - (void) unlink(m4temp); + while (is_sym2(c = gpbc())) { + if (tp == etp) error("m4: token too long"); + *tp++ = c, h = (h << 5) + h + c; } + putback(c); + *tp = EOS; + for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr) + if (strcmp(name, p->name) == 0) + return p; + return nil; + } - exit(0); -} - -ndptr inspect(); /* forward ... */ /* * macro - the work horse.. * */ -macro() { +void macro() + { char token[MAXTOK]; - register char *s; - register int t, l; - register ndptr p; - register int nlpar; - - cycle { - if ((t = gpbc()) == '_' || isalpha(t)) { - putback(t); - if ((p = inspect(s = token)) == nil) { - if (sp < 0) - while (*s) - putc(*s++, active); - else - while (*s) - chrsave(*s++); - } - else { - /* - * real thing.. First build a call frame: - * - */ - pushf(fp); /* previous call frm */ - pushf(p->type); /* type of the call */ - pushf(0); /* parenthesis level */ - fp = sp; /* new frame pointer */ - /* - * now push the string arguments: - * - */ - pushs(p->defn); /* defn string */ - pushs(p->name); /* macro name */ - pushs(ep); /* start next..*/ - - putback(l = gpbc()); - if (l != LPAREN) { /* add bracks */ - putback(RPAREN); - putback(LPAREN); - } + register int t; + register FILE *op = active; + static char ovmsg[] = "m4: internal stack overflow"; + + for (;;) { + t = gpbc(); + if (is_sym1(t)) { + register char *s; + register ndptr p; + + putback(t); + if ((p = inspect(s = token)) == nil) { + if (sp < 0) { + while (t = *s++) putc(t, op); + } else { + while (t = *s++) chrsave(t); + } + } else { + /* real thing.. First build a call frame */ + if (sp >= STACKMAX-6) error(ovmsg); + mstack[1+sp].sfra = fp; /* previous call frm */ + mstack[2+sp].sfra = p->type; /* type of the call */ + mstack[3+sp].sfra = 0; /* parenthesis level */ + fp = sp+3; /* new frame pointer */ + /* now push the string arguments */ + mstack[4+sp].sstr = p->defn; /* defn string */ + mstack[5+sp].sstr = p->name; /* macro name */ + mstack[6+sp].sstr = ep; /* start next.. */ + sp += 6; + + t = gpbc(); + putback(t); + if (t != LPAREN) { putback(RPAREN); putback(LPAREN); } + } + } else + if (t == EOF) { + if (sp >= 0) error("m4: unexpected end of input"); + if (--ilevel < 0) break; /* all done thanks */ +#ifndef NO__FILE + remhash("__FILE__", TOP); +#endif + bb = bbstack[ilevel+1]; + (void) fclose(infile[ilevel+1]); + } else + /* non-alpha single-char token seen.. + [the order of else if .. stmts is important.] + */ + if (t == lquote) { /* strip quotes */ + register int nlpar; + + for (nlpar = 1; ; ) { + t = gpbc(); + if (t == rquote) { + if (--nlpar == 0) break; + } else + if (t == lquote) { + nlpar++; + } else { + if (t == vquote) t = gpbc(); + if (t == EOF) { + error("m4: missing right quote"); } + } + if (sp < 0) { + putc(t, op); + } else { + chrsave(t); + } } - else if (t == EOF) { - if (sp > -1) - error("m4: unexpected end of input"); - if (--ilevel < 0) - break; /* all done thanks.. */ - (void) fclose(infile[ilevel+1]); - continue; + } else + if (sp < 0) { /* not in a macro at all */ + if (t != scommt) { /* not a comment, so */ + putc(t, op); /* copy it to output */ + } else + if (strip) { /* discard a comment */ + do { + t = gpbc(); + } while (t != ecommt && t != EOF); + } else { /* copy comment to output */ + do { + putc(t, op); + t = gpbc(); + } while (t != ecommt && t != EOF); + putc(t, op); + /* A note on comment handling: this is NOT robust. + | We should do something safe with comments that + | are missing their ecommt termination. + */ } - /* - * non-alpha single-char token seen.. - * [the order of else if .. stmts is - * important.] - * - */ - else if (t == lquote) { /* strip quotes */ - nlpar = 1; - do { - if ((l = gpbc()) == rquote) - nlpar--; - else if (l == lquote) - nlpar++; - else if (l == EOF) - error("m4: missing right quote"); - if (nlpar > 0) { - if (sp < 0) - putc(l, active); - else - chrsave(l); + } else + switch (t) { + /* There is a peculiar detail to notice here. + Layout is _always_ discarded after left parentheses, + but it is only discarded after commas if they separate + arguments. For example, + define(foo,`|$1|$2|') + foo( a, b) => |a|b| + foo(( a ), ( b )) => |(a )|(b )| + foo((a, x), (b, y)) => |(a, x)|(b, y)| + I find this counter-intuitive, and would expect the code + for LPAREN to read something like this: + + if (PARLEV == 0) { + (* top level left parenthesis: skip layout *) + do t = gpbc(); while (is_blnk(t)); + putback(t); + } else { + (* left parenthesis inside an argument *) + chrsave(t); + } + PARLEV++; + + However, it turned out that Oz wrote the actual code + very carefully to mimic the behaviour of "real" m4; + UNIX m4 really does skip layout after all left parens + but only some commas in just this fashion. Sigh. + */ + case LPAREN: + if (PARLEV > 0) chrsave(t); + do t = gpbc(); while (is_blnk(t)); /* skip layout */ + putback(t); + PARLEV++; + break; + + case COMMA: + if (PARLEV == 1) { + chrsave(EOS); /* new argument */ + if (sp >= STACKMAX) error(ovmsg); + do t = gpbc(); while (is_blnk(t)); /* skip layout */ + putback(t); + mstack[++sp].sstr = ep; + } else { + chrsave(t); + } + break; + + case RPAREN: + if (--PARLEV > 0) { + chrsave(t); + } else { + char **argv = (char **)(mstack+fp+1); + int argc = sp-fp; +#if unix | vms + static int sysval; +#endif + + chrsave(EOS); /* last argument */ + if (sp >= STACKMAX) error(ovmsg); +#ifdef DEBUG + fprintf(stderr, "argc = %d\n", argc); + for (t = 0; t < argc; t++) + fprintf(stderr, "argv[%d] = %s\n", t, argv[t]); +#endif + /* If argc == 3 and argv[2] is null, then we + have a call like `macro_or_builtin()'. We + adjust argc to avoid further checking.. + */ + if (argc == 3 && !argv[2][0]) argc--; + + switch (CALTYP & ~STATIC) { + case MACRTYPE: + expand(argv, argc); + break; + + case DEFITYPE: /* define(..) */ + for (; argc > 2; argc -= 2, argv += 2) + dodefine(argv[2], argc > 3 ? argv[3] : null); + break; + + case PUSDTYPE: /* pushdef(..) */ + for (; argc > 2; argc -= 2, argv += 2) + dopushdef(argv[2], argc > 3 ? argv[3] : null); + break; + + case DUMPTYPE: + dodump(argv, argc); + break; + + case EXPRTYPE: /* eval(Expr) */ + { /* evaluate arithmetic expression */ + /* eval([val: 0[, radix:10 [,min: 1]]]) */ + /* excess arguments are ignored */ + /* eval() with no arguments returns 0 */ + /* this is based on V.3 behaviour */ + int min_digits = 1; + int radix = 10; + long int value = 0; + + switch (argc) { + default: + /* ignore excess arguments */ + case 5: + min_digits = expr(argv[4]); + case 4: + radix = expr(argv[3]); + case 3: + value = expr(argv[2]); + case 2: + break; + } + pbrad(value, radix, min_digits); } - } - while (nlpar != 0); - } + break; + + case IFELTYPE: /* ifelse(X,Y,IFX=Y,Else) */ + doifelse(argv, argc); + break; + + case IFDFTYPE: /* ifdef(Mac,IfDef[,IfNotDef]) */ + /* select one of two alternatives based on the existence */ + /* of another definition */ + if (argc > 3) { + if (lookup(argv[2]) != nil) { + pbstr(argv[3]); + } else + if (argc > 4) { + pbstr(argv[4]); + } + } + break; + + case LENGTYPE: /* len(Arg) */ + /* find the length of the argument */ + pbnum(argc > 2 ? strlen(argv[2]) : 0); + break; + + case INCRTYPE: /* incr(Expr) */ + /* increment the value of the argument */ + if (argc > 2) pbnum(expr(argv[2]) + 1); + break; + + case DECRTYPE: /* decr(Expr) */ + /* decrement the value of the argument */ + if (argc > 2) pbnum(expr(argv[2]) - 1); + break; + +#if unix || vms + case SYSCTYPE: /* syscmd(Command) */ + /* execute system command */ + /* Make sure m4 output is NOT interrupted */ + fflush(stdout); + fflush(stderr); + + if (argc > 2) sysval = system(argv[2]); + break; + + case SYSVTYPE: /* sysval() */ + /* return value of the last system call. */ + pbnum(sysval); + break; +#endif - else if (sp < 0) { /* not in a macro at all */ - if (t == scommt) { /* comment handling here */ - putc(t, active); - while ((t = gpbc()) != ecommt) - putc(t, active); - } - putc(t, active); /* output directly.. */ - } + case INCLTYPE: /* include(File) */ + for (t = 2; t < argc; t++) + if (!doincl(argv[t])) cantread(argv[t]); + break; - else switch(t) { + case SINCTYPE: /* sinclude(File) */ + for (t = 2; t < argc; t++) + (void) doincl(argv[t]); + break; - case LPAREN: - if (PARLEV > 0) - chrsave(t); - while (isspace(l = gpbc())) - ; /* skip blank, tab, nl.. */ - putback(l); - PARLEV++; - break; +#ifdef EXTENDED + case PASTTYPE: /* paste(File) */ + for (t = 2; t < argc; t++) + if (!dopaste(argv[t])) cantread(argv[t]); + break; + + case SPASTYPE: /* spaste(File) */ + for (t = 2; t < argc; t++) + (void) dopaste(argv[t]); + break; + + case TRIMTYPE: /* m4trim(Source,..) */ + if (argc > 2) m4trim(argv, argc); + break; + + case DEFQTYPE: /* defquote(Mac,...) */ + dodefqt(argv, argc); + break; + + case QUTRTYPE: /* <quote>(text...) */ + doqutr(argv, argc); + break; +#endif - case RPAREN: - if (--PARLEV > 0) - chrsave(t); - else { /* end of argument list */ - chrsave(EOS); - - if (sp == STACKMAX) - error("m4: internal stack overflow"); - - if (CALTYP == MACRTYPE) - expand(mstack+fp+1, sp-fp); - else - eval(mstack+fp+1, sp-fp, CALTYP); - - ep = PREVEP; /* flush strspace */ - sp = PREVSP; /* previous sp.. */ - fp = PREVFP; /* rewind stack...*/ + case CHNQTYPE: /* changequote([Left[,Right]]) */ + dochq(argv, argc); + break; + + case CHNCTYPE: /* changecom([Left[,Right]]) */ + dochc(argv, argc); + break; + + case SUBSTYPE: /* substr(Source[,Offset[,Length]]) */ + /* select substring */ + if (argc > 3) dosub(argv, argc); + break; + + case SHIFTYPE: /* shift(~args~) */ + /* push back all arguments except the first one */ + /* (i.e. skip argv[2]) */ + if (argc > 3) { + for (t = argc-1; t > 3; t--) { + pbqtd(argv[t]); + putback(','); + } + pbqtd(argv[3]); + } + break; + + case DIVRTYPE: /* divert(N) */ + if (argc > 2 && (t = expr(argv[2])) != 0) { + dodiv(t); + } else { + active = stdout; + oindex = 0; + } + op = active; + break; + + case UNDVTYPE: /* undivert(N...) */ + doundiv(argv, argc); + op = active; + break; + + case DIVNTYPE: /* divnum() */ + /* return the number of current output diversion */ + pbnum(oindex); + break; + + case UNDFTYPE: /* undefine(..) */ + /* undefine a previously defined macro(s) or m4 keyword(s). */ + for (t = 2; t < argc; t++) remhash(argv[t], ALL); + break; + + case POPDTYPE: /* popdef(Mac...) */ + /* remove the topmost definitions of macro(s) or m4 keyword(s). */ + for (t = 2; t < argc; t++) remhash(argv[t], TOP); + break; + + case MKTMTYPE: /* maketemp(Pattern) */ + /* create a temporary file */ + if (argc > 2) pbstr(mktemp(argv[2])); + break; + + case TRNLTYPE: /* translit(Source,Dom,Rng) */ + /* replace all characters in the source string that */ + /* appears in the "from" string with the corresponding */ + /* characters in the "to" string. */ + + if (argc > 3) { + char temp[MAXTOK]; + + if (argc > 4) + map(temp, argv[2], argv[3], argv[4]); + else + map(temp, argv[2], argv[3], null); + pbstr(temp); + } else if (argc > 2) + pbstr(argv[2]); + break; + + case INDXTYPE: /* index(Source,Target) */ + /* find the index of the second argument string in */ + /* the first argument string. -1 if not present. */ + pbnum(argc > 3 ? indx(argv[2], argv[3]) : -1); + break; + + case ERRPTYPE: /* errprint(W,...,W) */ + /* print the arguments to stderr file */ + for (t = 2; t < argc; t++) fprintf(stderr, "%s ", argv[t]); + fprintf(stderr, "\n"); + break; + + case DNLNTYPE: /* dnl() */ + /* eat upto and including newline */ + while ((t = gpbc()) != '\n' && t != EOF) ; + break; + + case M4WRTYPE: /* m4wrap(AtExit) */ + /* set up for wrap-up/wind-down activity. */ + /* NB: if there are several calls to m4wrap */ + /* only the last is effective; strange, but */ + /* that's what System V does. */ + m4wraps = argc > 2 ? strsave(argv[2]) : null; + break; + + case EXITTYPE: /* m4exit(Expr) */ + /* immediate exit from m4. */ + killdiv(); /* mustn't forget that one! */ + exit(argc > 2 ? expr(argv[2]) : 0); + break; + + case DEFNTYPE: /* defn(Mac) */ + for (t = 2; t < argc; t++) + dodefn(argv[t]); + break; + + default: + error("m4: major botch in eval."); + break; } - break; - case COMMA: - if (PARLEV == 1) { - chrsave(EOS); /* new argument */ - while (isspace(l = gpbc())) - ; - putback(l); - pushs(ep); - } - break; + ep = PREVEP; /* flush strspace */ + sp = PREVSP; /* previous sp.. */ + fp = PREVFP; /* rewind stack... */ + } + break; + default: - chrsave(t); /* stack the char */ - break; - } + chrsave(t); /* stack the char */ + break; + } } -} + } -/* - * build an input token.. - * consider only those starting with _ or A-Za-z. This is a - * combo with lookup to speed things up. - */ -ndptr -inspect(tp) -register char *tp; -{ - register int h = 0; - register char c; - register char *name = tp; - register char *etp = tp+MAXTOK; - register ndptr p; +int main(argc, argv) + int argc; + char **argv; + { + register int c; + register int n; + char *p; - while (tp < etp && (isalnum(c = gpbc()) || c == '_')) - h += (*tp++ = c); - putback(c); - if (tp == etp) - error("m4: token too long"); - *tp = EOS; - for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr) - if (strcmp(name, p->name) == 0) - break; - return(p); -} +#ifdef SIGINT + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, onintr); +#endif + + /* Initialise the chtype[] table. + '0' .. '9' -> 1..10 + 'A' .. 'Z' -> 11..37 + 'a' .. 'z' -> 11..37 + '_' -> 38 + all other characters -> 0 + */ + for (c = EOF; c <= UCHAR_MAX; c++) chtype[c - EOF] = 0; + for (c = 1, p = "0123456789"; *p; p++, c++) + chtype[*(unsigned char *)p - EOF] = c; + for (c = 11, p = "abcdefghijklmnopqrstuvwxyz"; *p; p++, c++) + chtype[*(unsigned char *)p - EOF] = c; + for (c = 11, p = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; *p; p++, c++) + chtype[*(unsigned char *)p - EOF] = c; + chtype['_' - EOF] = 38; #ifdef NONZEROPAGES -/* - * initm4 - initialize various tables. Useful only if your system - * does not know anything about demand-zero pages. - * - */ -initm4() -{ - register int i; + /* If your system does not initialise global variables to */ + /* 0 bits, do it here. */ + for (n = 0; n < HASHSIZE; n++) hashtab[n] = nil; + for (n = 0; n < MAXOUT; n++) outfile[n] = NULL; +#endif + initkwds(); - for (i = 0; i < HASHSIZE; i++) - hashtab[i] = nil; - for (i = 0; i < MAXOUT; i++) - outfile[i] = NULL; -} + while ((c = getopt(argc, argv, "cetD:U:o:B:H:S:T:")) != EOF) { + switch (c) { +#if 0 + case 's': /* enable #line sync in output */ + fprintf(stderr, "m4: this version does not support -s\n"); + exit(2); #endif -/* - * initkwds - initialise m4 keywords as fast as possible. - * This very similar to install, but without certain overheads, - * such as calling lookup. Malloc is not used for storing the - * keyword strings, since we simply use the static pointers - * within keywrds block. We also assume that there is enough memory - * to at least install the keywords (i.e. malloc won't fail). - * - */ -initkwds() { - register int i; - register int h; - register ndptr p; + case 'c': /* strip comments */ + strip ^= 1; + break; + + case 'e': /* interactive */ + (void) signal(SIGINT, SIG_IGN); + setbuf(stdout, NULL); + break; + + case 'D': /* define something..*/ + for (p = optarg; *p && *p != '='; p++) ; + if (*p) *p++ = EOS; + dodefine(optarg, p); + break; + + case 'U': /* undefine... */ + remhash(optarg, TOP); + break; - for (i = 0; i < MAXKEYS; i++) { - h = hash(keywrds[i].knam); - p = (ndptr) malloc(sizeof(struct ndblock)); - p->nxtptr = hashtab[h]; - hashtab[h] = p; - p->name = keywrds[i].knam; - p->defn = null; - p->type = keywrds[i].ktyp | STATIC; + case 'B': case 'H': /* System V compatibility */ + case 'S': case 'T': /* ignore them */ + break; + + case 'o': /* specific output */ + if (!freopen(optarg, "w", stdout)) { + perror(optarg); + exit(1); + } + break; + + case '?': + default: + usage(); + } } -} + + active = stdout; /* default active output */ + m4temp = mktemp(DIVNAM); /* filename for diversions */ + + sp = -1; /* stack pointer initialized */ + fp = 0; /* frame pointer initialized */ + + if (optind == argc) { /* no more args; read stdin */ + infile[0] = stdin; /* default input (naturally) */ +#ifndef NO__FILE + dodefine("__FILE__", "-"); /* Helas */ +#endif + macro(); /* process that file */ + } else /* file names in commandline */ + for (; optind < argc; optind++) { + char *name = argv[optind]; /* next file name */ + infile[0] = fopen(name, "r"); + if (!infile[0]) cantread(name); +#ifndef NO__FILE + dodefine("__FILE__", name); +#endif + macro(); + fclose(infile[0]); + } + + if (*m4wraps) { /* anything for rundown ?? */ + ilevel = 0; /* in case m4wrap includes.. */ + putback(EOF); /* eof is a must !! */ + pbstr(m4wraps); /* user-defined wrapup act */ + macro(); /* last will and testament */ + } else { /* default wrap-up: undivert */ + for (n = 1; n < MAXOUT; n++) + if (outfile[n] != NULL) getdiv(n); + } + + if (outfile[0] != NULL) { /* remove bitbucket if used */ + (void) fclose(outfile[0]); + m4temp[UNIQUE] = '0'; +#if unix + (void) unlink(m4temp); +#else + (void) remove(m4temp); +#endif + } + exit(0); + return 0; + } + diff --git a/usr.bin/m4/mdef.h b/usr.bin/m4/mdef.h index 4204a6d275..d8dc094fab 100644 --- a/usr.bin/m4/mdef.h +++ b/usr.bin/m4/mdef.h @@ -1,52 +1,32 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Ozan Yigit. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)mdef.h 5.6 (Berkeley) 2/26/91 - */ +/* Header : mdef.h + Author : Ozan Yigit + Updated: 4 May 1992 +*/ +#ifndef MACRTYPE -/* - * mdef.h - * Facility: m4 macro processor - * by: oz - */ +#ifndef unix +#define unix 0 +#endif -/* - * - * m4 constants.. - * - */ +#ifndef vms +#define vms 0 +#endif + +#include <stdio.h> +#include <signal.h> + +#ifdef __STDC__ +#include <string.h> +#else +#ifdef VOID +#define void int +#endif +extern int strlen(); +extern int strcmp(); +extern void memcpy(); +#endif + +/* m4 constants */ #define MACRTYPE 1 #define DEFITYPE 2 @@ -81,18 +61,22 @@ #define SYSVTYPE 31 #define EXITTYPE 32 #define DEFNTYPE 33 - +#define LINETYPE 34 +#define TRIMTYPE 35 +#define TLITTYPE 36 +#define DEFQTYPE 37 /* defquote */ +#define QUTRTYPE 38 /* quoter thus defined */ + #define STATIC 128 -/* - * m4 special characters - */ +/* m4 special characters */ #define ARGFLAG '$' #define LPAREN '(' #define RPAREN ')' #define LQUOTE '`' #define RQUOTE '\'' +#define VQUOTE ('V'&(' '- 1)) #define COMMA ',' #define SCOMMT '#' #define ECOMMT '\n' @@ -104,44 +88,45 @@ #define EOS (char) 0 #define MAXINP 10 /* maximum include files */ #define MAXOUT 10 /* maximum # of diversions */ -#define MAXSTR 512 /* maximum size of string */ +#ifdef SMALL +#define MAXSTR 512 /* maximum size of string */ #define BUFSIZE 4096 /* size of pushback buffer */ #define STACKMAX 1024 /* size of call stack */ #define STRSPMAX 4096 /* size of string space */ +#define HASHSIZE 199 /* maximum size of hashtab */ +#else +#define MAXSTR 1024 /* maximum size of string */ +#define BUFSIZE 8192 /* size of pushback buffer */ +#define STACKMAX 2048 /* size of call stack */ +#define STRSPMAX 8192 /* size of string space */ +#define HASHSIZE 509 /* maximum size of hashtab */ +#endif #define MAXTOK MAXSTR /* maximum chars in a tokn */ -#define HASHSIZE 199 /* maximum size of hashtab */ #define ALL 1 #define TOP 0 #define TRUE 1 #define FALSE 0 -#define cycle for(;;) -/* - * m4 data structures - */ +/* m4 data structures */ typedef struct ndblock *ndptr; -struct ndblock { /* hastable structure */ +struct ndblock /* hashtable structure */ + { char *name; /* entry name.. */ char *defn; /* definition.. */ int type; /* type of the entry.. */ ndptr nxtptr; /* link to next entry.. */ -}; + }; #define nil ((ndptr) 0) -struct keyblk { - char *knam; /* keyword name */ - int ktyp; /* keyword type */ -}; - -typedef union { /* stack structure */ - int sfra; /* frame entry */ +typedef union /* stack structure */ + { int sfra; /* frame entry */ char *sstr; /* string entry */ -} stae; + } stae; /* * macros for readibility and/or speed @@ -151,7 +136,7 @@ typedef union { /* stack structure */ * pushf() - push a call frame entry onto stack * pushs() - push a string pointer onto stack */ -#define gpbc() (bp > buf) ? *--bp : getc(infile[ilevel]) +#define gpbc() bp == bb ? getc(infile[ilevel]) : *--bp #define min(x,y) ((x > y) ? y : x) #define pushf(x) if (sp < STACKMAX) mstack[++sp].sfra = (x) #define pushs(x) if (sp < STACKMAX) mstack[++sp].sstr = (x) @@ -184,3 +169,5 @@ typedef union { /* stack structure */ #define PREVEP (mstack[fp+3].sstr) #define PREVSP (fp-3) #define PREVFP (mstack[fp-2].sfra) + +#endif diff --git a/usr.bin/m4/misc.c b/usr.bin/m4/misc.c index fae81ada3e..6d51835bef 100644 --- a/usr.bin/m4/misc.c +++ b/usr.bin/m4/misc.c @@ -1,211 +1,357 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Ozan Yigit. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)misc.c 5.6 (Berkeley) 2/26/91"; -#endif /* not lint */ - -/* - * misc.c - * Facility: m4 macro processor - * by: oz - */ - -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> +/* File : misc.c + Author : Ozan Yigit + Updated: 26-Mar-1993 + Purpose: Miscellaneous support code for PD M4. +*/ + #include "mdef.h" -#include "extr.h" -#include "pathnames.h" - -/* - * indx - find the index of second str in the - * first str. - */ -indx(s1, s2) -char *s1; -char *s2; -{ - register char *t; - register char *p; - register char *m; - - for (p = s1; *p; p++) { - for (t = p, m = s2; *m && *m == *t; m++, t++) - ; - if (!*m) - return(p - s1); - } - return (-1); -} - -/* - * putback - push character back onto input - * - */ -putback (c) -char c; -{ - if (bp < endpbb) - *bp++ = c; - else - error("m4: too many characters pushed back"); -} - -/* - * pbstr - push string back onto input - * putback is replicated to improve - * performance. - * - */ -pbstr(s) -register char *s; -{ - register char *es; +#include "extr.h" +#include "ourlims.h" + +#ifdef DUFFCP + +/* This version of the ANSI standard function memcpy() + uses Duff's Device (tm Tom Duff) to unroll the copying loop: + while (count-- > 0) *to++ = *from++; +*/ +void memcpy(to, from, count) + register char *from, *to; + register int count; + { + if (count > 0) { + register int loops = (count+8-1) >> 3; /* div 8 round up */ + + switch (count & (8-1)) { /* mod 8 */ + case 0: do { *to++ = *from++; + case 7: *to++ = *from++; + case 6: *to++ = *from++; + case 5: *to++ = *from++; + case 4: *to++ = *from++; + case 3: *to++ = *from++; + case 2: *to++ = *from++; + case 1: *to++ = *from++; + } while (--loops > 0); + } + } + } + +#endif + + +/* strsave(s) + return a new malloc()ed copy of s -- same as V.3's strdup(). +*/ +char *strsave(s) + char *s; + { + register int n = strlen(s)+1; + char *p = malloc(n); + + if (p) memcpy(p, s, n); + return p; + } + + +/* indx(s1, s2) + if s1 can be decomposed as alpha || s2 || omega, return the length + of the shortest such alpha, otherwise return -1. +*/ +int indx(s1, s2) + char *s1; + char *s2; + { + register char *t; + register char *m; + register char *p; + + for (p = s1; *p; p++) { + for (t = p, m = s2; *m && *m == *t; m++, t++); + if (!*m) return p-s1; + } + return -1; + } + + +char pbmsg[] = "m4: too many characters pushed back"; + +/* Xputback(c) + push character c back onto the input stream. + This is now macro putback() in misc.h +*/ +void Xputback(c) + char c; + { + if (bp < endpbb) *bp++ = c; else error(pbmsg); + } + + +/* pbstr(s) + push string s back onto the input stream. + putback() has been unfolded here to improve performance. + Example: + s = <ABC> + bp = <more stuff> + After the call: + bp = <more stuffCBA> + It would be more efficient if we ran the pushback buffer in the + opposite direction +*/ +void pbstr(s) + register char *s; + { + register char *es; + register char *zp; + + zp = bp; + for (es = s; *es; ) es++; /* now es points to terminating NUL */ + bp += es-s; /* advance bp as far as it should go */ + if (bp >= endpbb) error("m4: too many characters to push back"); + while (es > s) *zp++ = *--es; + } + + +/* pbqtd(s) + pushes string s back "quoted", doing whatever has to be done to it to + make sure that the result will evaluate to the original value. As it + happens, we have only to add lquote and rquote. +*/ +void pbqtd(s) + register char *s; + { + register char *es; register char *zp; - es = s; zp = bp; + for (es = s; *es; ) es++; /* now es points to terminating NUL */ + bp += 2+es-s; /* advance bp as far as it should go */ + if (bp >= endpbb) error("m4: too many characters to push back"); + *zp++ = rquote; + while (es > s) *zp++ = *--es; + *zp++ = lquote; + } + + +/* pbnum(n) + convert a number to a (decimal) string and push it back. + The original definition did not work for MININT; this does. +*/ +void pbnum(n) + int n; + { + register int num; + + num = n > 0 ? -n : n; /* MININT <= num <= 0 */ + do { + putback('0' - (num % 10)); + } while ((num /= 10) < 0); + if (n < 0) putback('-'); + } + + +/* pbrad(n, r, m) + converts a number n to base r ([-36..-2] U [2..36]), with at least + m digits. If r == 10 and m == 1, this is exactly the same as pbnum. + However, this uses the function int2str() from R.A.O'Keefe's public + domain string library, and puts the results of that back. + The Unix System V Release 3 version of m4 accepts radix 1; + THIS VERSION OF M4 DOES NOT ACCEPT RADIX 1 OR -1, + nor do we accept radix < -36 or radix > 36. At the moment such bad + radices quietly produce nothing. The V.3 treatment of radix 1 is + push back abs(n) "1"s, then + if n < 0, push back one "-". + Until I come across something which uses it, I can't bring myself to + implement this. + + I have, however, found a use for radix 0. Unsurprisingly, it is + related to radix 0 in Edinburgh Prolog. + eval('c1c2...cn', 0, m) + pushes back max(m-n,0) blanks and the characters c1...cn. This can + adjust to any byte size as long as UCHAR_MAX = (1 << CHAR_BIT) - 1. + In particular, eval(c, 0) where 0 < c <= UCHAR_MAX, pushes back the + character with code c. Note that this has to agree with eval(); so + both of them have to use the same byte ordering. +*/ +void pbrad(n, r, m) + long int n; + int r, m; + { + char buffer[34]; + char *p; + int L; - while (*es) - es++; - es--; - while (es >= s) - if (zp < endpbb) - *zp++ = *es--; - if ((bp = zp) == endpbb) - error("m4: too many characters pushed back"); -} - -/* - * pbnum - convert number to string, push back on input. - * - */ -pbnum (n) -int n; -{ - register int num; - - num = (n < 0) ? -n : n; - do { - putback(num % 10 + '0'); - } - while ((num /= 10) > 0); - - if (n < 0) putback('-'); -} - -/* - * chrsave - put single char on string space - * - */ -chrsave (c) -char c; -{ -/*** if (sp < 0) - putc(c, active); - else ***/ if (ep < endest) - *ep++ = c; - else - error("m4: string space overflow"); -} - -/* - * getdiv - read in a diversion file, and - * trash it. - */ -getdiv(ind) { - register int c; - register FILE *dfil; - - if (active == outfile[ind]) - error("m4: undivert: diversion still active."); - (void) fclose(outfile[ind]); - outfile[ind] = NULL; - m4temp[UNIQUE] = ind + '0'; - if ((dfil = fopen(m4temp, "r")) == NULL) - error("m4: cannot undivert."); - else - while((c = getc(dfil)) != EOF) - putc(c, active); - (void) fclose(dfil); - - if (unlink(m4temp) == -1) - error("m4: cannot unlink."); -} - -/* - * Very fatal error. Close all files - * and die hard. - */ -error(s) -char *s; -{ - killdiv(); - fprintf(stderr,"%s\n",s); - exit(1); -} - -/* - * Interrupt handling - */ + if (r == 0) { + unsigned long int x = (unsigned long)n; + int n; + + for (n = 0; x; x >>= CHAR_BIT, n++) buffer[n] = x & UCHAR_MAX; + for (L = n; --L >= 0; ) putback(buffer[L]); + for (L = m-n; --L >= 0; ) putback(' '); + return; + } + L = m - (int2str(p = buffer, -r, n)-buffer); + if (buffer[0] == '-') L++, p++; + if (L > 0) { + pbstr(p); + while (--L >= 0) putback('0'); + if (p != buffer) putback('-'); + } else { + pbstr(buffer); + } + } + + +char csmsg[] = "m4: string space overflow"; + +/* chrsave(c) + put the character c in the string space. +*/ +void Xchrsave(c) + char c; + { +#if 0 + if (sp < 0) putc(c, active); else +#endif + if (ep < endest) *ep++ = c; else + error(csmsg); + } + + +/* getdiv(ind) + read in a diversion file and then delete it. +*/ +void getdiv(ind) + int ind; + { + register int c; + register FILE *dfil; + register FILE *afil; + + afil = active; + if (outfile[ind] == afil) + error("m4: undivert: diversion still active."); + (void) fclose(outfile[ind]); + outfile[ind] = NULL; + m4temp[UNIQUE] = '0' + ind; + if ((dfil = fopen(m4temp, "r")) == NULL) + error("m4: cannot undivert."); + while ((c = getc(dfil)) != EOF) putc(c, afil); + (void) fclose(dfil); + +#if vms + if (remove(m4temp)) error("m4: cannot unlink."); +#else + if (unlink(m4temp) == -1) error("m4: cannot unlink."); +#endif + } + + +/* killdiv() + delete all the diversion files which have been created. +*/ +void killdiv() + { + register int n; + + for (n = 0; n < MAXOUT; n++) { + if (outfile[n] != NULL) { + (void) fclose(outfile[n]); + m4temp[UNIQUE] = '0' + n; +#if unix + (void) unlink(m4temp); +#else + (void) remove(m4temp); +#endif + } + } + } + + +/* error(s) + close all files, report a fatal error, and quit, letting the caller know. +*/ +void error(s) + char *s; + { + killdiv(); + fprintf(stderr, "%s\n", s); + exit(1); + } + + +/* Interrupt handling +*/ static char *msg = "\ninterrupted."; - -void -onintr() { - error(msg); -} - -/* - * killdiv - get rid of the diversion files - * - */ -killdiv() { - register int n; - - for (n = 0; n < MAXOUT; n++) - if (outfile[n] != NULL) { - (void) fclose (outfile[n]); - m4temp[UNIQUE] = n + '0'; - (void) unlink (m4temp); - } -} - -usage() { - fprintf(stderr, "usage: m4 [-Dname[=val]] [-Uname]\n"); - exit(1); -} + +#ifdef __STDC__ +void onintr(int signo) +#else +onintr() +#endif + { + error(msg); + } + + +void usage() + { + fprintf(stderr, "Usage: m4 [-e] [-[BHST]int] [-Dname[=val]] [-Uname]\n"); + exit(1); + } + +#ifdef GETOPT +/* Henry Spencer's getopt() - get option letter from argv */ + +char *optarg; /* Global argument pointer. */ +int optind = 0; /* Global argv index. */ + +static char *scan = NULL; /* Private scan pointer. */ + +#ifndef __STDC__ +extern char *index(); +#define strchr index +#endif + +int getopt(argc, argv, optstring) + int argc; + char **argv; + char *optstring; + { + register char c; + register char *place; + + optarg = NULL; + + if (scan == NULL || *scan == '\0') { + if (optind == 0) optind++; + if (optind >= argc + || argv[optind][0] != '-' + || argv[optind][1] == '\0') + return EOF; + if (strcmp(argv[optind], "--") == 0) { + optind++; + return EOF; + } + scan = argv[optind]+1; + optind++; + } + c = *scan++; + place = strchr(optstring, c); + + if (place == NULL || c == ':') { + fprintf(stderr, "%s: unknown option -%c\n", argv[0], c); + return '?'; + } + place++; + if (*place == ':') { + if (*scan != '\0') { + optarg = scan; + scan = NULL; + } else { + optarg = argv[optind]; + optind++; + } + } + return c; + } +#endif + diff --git a/usr.bin/m4/ourlims.h b/usr.bin/m4/ourlims.h new file mode 100644 index 0000000000..8bc0b56c0a --- /dev/null +++ b/usr.bin/m4/ourlims.h @@ -0,0 +1,16 @@ +/* File : ourlims.h + Author : Richard A. O'Keefe + Defines: UCHAR_MAX, CHAR_BIT, LONG_BIT +*/ +/* If <limits.h> is available, use that. + Otherwise, use 8-bit byte as the default. + If the number of characters is a power of 2, you might be able + to use (unsigned char)(~0), but why get fancy? +*/ +#ifdef __STDC__ +#include <limits.h> +#else +#define UCHAR_MAX 255 +#define CHAR_BIT 8 +#endif +#define LONG_BIT 32 diff --git a/usr.bin/m4/serv.c b/usr.bin/m4/serv.c index 6a9f709a18..6b0a4bb45e 100644 --- a/usr.bin/m4/serv.c +++ b/usr.bin/m4/serv.c @@ -1,470 +1,686 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Ozan Yigit. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)serv.c 5.3 (Berkeley) 2/26/91"; -#endif /* not lint */ +/* File : serv.c + Author : Ozan Yigit + Updated: 4 May 1992 + Defines: Principal built-in macros for PD M4. +*/ -/* - * serv.c - * Facility: m4 macro processor - * by: oz - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> #include "mdef.h" -#include "extr.h" -#include "pathnames.h" - -extern ndptr lookup(); -extern ndptr addent(); - -char *dumpfmt = "`%s'\t`%s'\n"; /* format string for dumpdef */ - -/* - * expand - user-defined macro expansion - * - */ -expand(argv, argc) -register char *argv[]; -register int argc; -{ - register char *t; - register char *p; - register int n; - register int argno; - - t = argv[0]; /* defn string as a whole */ - p = t; - while (*p) - p++; - p--; /* last character of defn */ - while (p > t) { - if (*(p-1) != ARGFLAG) - putback(*p); - else { - switch (*p) { - - case '#': - pbnum(argc-2); - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if ((argno = *p - '0') < argc-1) - pbstr(argv[argno+1]); - break; - case '*': - for (n = argc - 1; n > 2; n--) { - pbstr(argv[n]); - putback(','); - } - pbstr(argv[2]); - break; - default : - putback(*p); - break; - } - p--; - } - p--; - } - if (p == t) /* do last character */ - putback(*p); -} - -/* - * dodefine - install definition in the table - * - */ -dodefine(name, defn) -register char *name; -register char *defn; -{ - register ndptr p; - - if (!*name) - error("m4: null definition."); - if (strcmp(name, defn) == 0) - error("m4: recursive definition."); - if ((p = lookup(name)) == nil) - p = addent(name); - else if (p->defn != null) - free(p->defn); - if (!*defn) - p->defn = null; - else - p->defn = strdup(defn); - p->type = MACRTYPE; -} - -/* - * dodefn - push back a quoted definition of - * the given name. - */ - -dodefn(name) -char *name; -{ - register ndptr p; - - if ((p = lookup(name)) != nil && p->defn != null) { - putback(rquote); - pbstr(p->defn); - putback(lquote); - } -} - -/* - * dopushdef - install a definition in the hash table - * without removing a previous definition. Since - * each new entry is entered in *front* of the - * hash bucket, it hides a previous definition from - * lookup. - */ -dopushdef(name, defn) -register char *name; -register char *defn; -{ - register ndptr p; - - if (!*name) - error("m4: null definition"); - if (strcmp(name, defn) == 0) - error("m4: recursive definition."); - p = addent(name); - if (!*defn) - p->defn = null; - else - p->defn = strdup(defn); - p->type = MACRTYPE; -} - -/* - * dodumpdef - dump the specified definitions in the hash - * table to stderr. If nothing is specified, the entire - * hash table is dumped. - * - */ -dodump(argv, argc) -register char *argv[]; -register int argc; -{ - register int n; - ndptr p; - - if (argc > 2) { - for (n = 2; n < argc; n++) - if ((p = lookup(argv[n])) != nil) - fprintf(stderr, dumpfmt, p->name, - p->defn); - } - else { - for (n = 0; n < HASHSIZE; n++) - for (p = hashtab[n]; p != nil; p = p->nxtptr) - fprintf(stderr, dumpfmt, p->name, - p->defn); - } -} - -/* - * doifelse - select one of two alternatives - loop. - * - */ -doifelse(argv,argc) -register char *argv[]; -register int argc; -{ - cycle { - if (strcmp(argv[2], argv[3]) == 0) - pbstr(argv[4]); - else if (argc == 6) - pbstr(argv[5]); - else if (argc > 6) { - argv += 3; - argc -= 3; - continue; - } - break; - } -} - -/* - * doinclude - include a given file. - * +#include "extr.h" +#include "ourlims.h" + +#define ucArgv(n) ((unsigned char *)argv[n]) + +/* 26-Mar-1993 Made m4trim() 8-bit clean. +*/ + +/* expand(<DS FN A1 ... An>) + 0 1 2 n+1 -- initial indices in argv[] + -1 0 1 n -- after adjusting argv++, argc-- + This expands a user-defined macro; FN is the name of the macro, DS + is its definition string, and A1 ... An are its arguments. +*/ +void expand(argv, argc) + char **argv; + int argc; + { + register char *t; + register char *p; + register int n; + +#ifdef DEBUG + fprintf(stderr, "expand(%s,%d)\n", argv[1], argc); +#endif + argc--; /* discount definition string (-1th arg) */ + t = *argv++; /* definition string as a whole */ + for (p = t; *p++; ) ; + p -= 2; /* points to last character of definition */ + while (p > t) { /* if definition is empty, fails at once */ + if (*--p != ARGFLAG) { + putback(p[1]); + } else { + switch (p[1]) { + case '#': + pbnum(argc-1); + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if ((n = p[1]-'0') < argc) pbstr(argv[n]); + break; + case '*': /* push all arguments back */ + for (n = argc-1; n > 1; n--) { + pbstr(argv[n]); + putback(','); + } + pbstr(argv[1]); + break; + case '@': /* push arguments back quoted */ + for (n = argc-1; n > 1; n--) { + pbqtd(argv[n]); + putback(','); + } + pbqtd(argv[1]); + break; + case '$': /* $$ => $ */ + break; + default: + putback(p[1]); + putback(p[0]); + break; + } + p--; + } + } + if (p == t) putback(*p); /* do last character */ + } + + +static char nuldefmsg[] = "m4: defining null name."; +static char recdefmsg[] = "m4: macro defined as itself."; + +/* dodefine(Name, Definition) + install Definition as the only definition of Name in the hash table. */ -doincl(ifile) -char *ifile; -{ - if (ilevel+1 == MAXINP) - error("m4: too many include files."); - if ((infile[ilevel+1] = fopen(ifile, "r")) != NULL) { - ilevel++; - return (1); - } - else - return (0); -} - +void dodefine(name, defn) + register char *name; + register char *defn; + { + register ndptr p; + + if (!name || !*name) error(nuldefmsg); + if (strcmp(name, defn) == 0) error(recdefmsg); +#ifdef DEBUG + fprintf(stderr, "define(%s,--)\n", name); +#endif + if ((p = lookup(name)) == nil) { + p = addent(name); + } else + if (p->defn != null) { /* what if p->type & STATIC ? */ + free(p->defn); + } + p->defn = !defn || !*defn ? null : strsave(defn); + p->type = MACRTYPE; + } + + +/* dopushdef(Name, Definition) + install Definition as the *first* definition of Name in the hash table, + but do not remove any existing definitions. The new definition will + hide any old ones until a popdef() removes it. +*/ +void dopushdef(name, defn) + register char *name; + register char *defn; + { + register ndptr p; + + if (!name || !*name) error(nuldefmsg); + if (strcmp(name, defn) == 0) error(recdefmsg); +#ifdef DEBUG + fprintf(stderr, "pushdef(%s,--)\n", name); +#endif + p = addent(name); + p->defn = !defn || !*defn ? null : strsave(defn); + p->type = MACRTYPE; + } + + +/* dodefn(Name) + push back a *quoted* copy of Name's definition. +*/ +void dodefn(name) + char *name; + { + register ndptr p; + + if ((p = lookup(name)) != nil && p->defn != null) pbqtd(p->defn); + } + + +/* dodump(<? dump>) dump all definition in the hash table + dodump(<? dump F1 ... Fn>) dump the definitions of F1 ... Fn in that order + The requested definitions are written to stderr. What happens to names + which have a built-in (numeric) definition? +*/ +void dodump(argv, argc) + register char **argv; + register int argc; + { + register int n; + ndptr p; + static char dumpfmt[] = "define(`%s',\t`%s')\n"; + + if (argc > 2) { + for (n = 2; n < argc; n++) + if ((p = lookup(argv[n])) != nil) + fprintf(stderr, dumpfmt, p->name, p->defn); + } else { + for (n = 0; n < HASHSIZE; n++) + for (p = hashtab[n]; p != nil; p = p->nxtptr) + fprintf(stderr, dumpfmt, p->name, p->defn); + } + } + + +/* doifelse(<? ifelse {x y ifx=y}... [else]>) + 0 1 2 3 4 [2 when we get to it] +*/ +void doifelse(argv, argc) + register char **argv; + register int argc; + { + for (; argc >= 5; argv += 3, argc -= 3) + if (strcmp(argv[2], argv[3]) == 0) { + pbstr(argv[4]); + return; + } + if (argc >= 3) pbstr(argv[2]); + } + + +/* doinclude(FileName) + include a given file. +*/ +int doincl(FileName) + char *FileName; + { + if (ilevel+1 == MAXINP) error("m4: too many include files."); +#ifdef DEBUG + fprintf(stderr, "include(%s)\n", FileName); +#endif + if ((infile[ilevel+1] = fopen(FileName, "r")) != NULL) { +#ifndef NO__FILE + dopushdef("__FILE__", FileName); +#endif + bbstack[ilevel+1] = bb; + bb = bp; + ilevel++; + return 1; + } else { + return 0; + } + } + + #ifdef EXTENDED -/* - * dopaste - include a given file without any - * macro processing. - */ -dopaste(pfile) -char *pfile; -{ - FILE *pf; - register int c; - - if ((pf = fopen(pfile, "r")) != NULL) { - while((c = getc(pf)) != EOF) - putc(c, active); - (void) fclose(pf); - return(1); - } - else - return(0); -} +/* dopaste(FileName) + copy a given file to the output stream without any macro processing. +*/ +int dopaste(FileName) + char *FileName; + { + register FILE *pf; + register FILE *afil = active; + register int c; + + if ((pf = fopen(FileName, "r")) != NULL) { + while ((c = getc(pf)) != EOF) putc(c, afil); + (void) fclose(pf); + return 1; + } else { + return 0; + } + } #endif - -/* - * dochq - change quote characters - * - */ -dochq(argv, argc) -register char *argv[]; -register int argc; -{ - if (argc > 2) { - if (*argv[2]) - lquote = *argv[2]; - if (argc > 3) { - if (*argv[3]) - rquote = *argv[3]; - } - else - rquote = lquote; - } - else { - lquote = LQUOTE; - rquote = RQUOTE; - } -} - -/* - * dochc - change comment characters - * - */ -dochc(argv, argc) -register char *argv[]; -register int argc; -{ - if (argc > 2) { - if (*argv[2]) - scommt = *argv[2]; - if (argc > 3) { - if (*argv[3]) - ecommt = *argv[3]; - } - else - ecommt = ECOMMT; - } - else { - scommt = SCOMMT; - ecommt = ECOMMT; - } -} - -/* - * dodivert - divert the output to a temporary file - * - */ -dodiv(n) -register int n; -{ - if (n < 0 || n >= MAXOUT) - n = 0; /* bitbucket */ - if (outfile[n] == NULL) { - m4temp[UNIQUE] = n + '0'; - if ((outfile[n] = fopen(m4temp, "w")) == NULL) - error("m4: cannot divert."); - } - oindex = n; - active = outfile[n]; -} - -/* - * doundivert - undivert a specified output, or all + + +/* dochq(<? changequote [left [right [verbatim]]]>) + 0 1 2 3 4 + change the quote characters; to single characters only. + Empty arguments result in no change for that parameter. + Missing arguments result in defaults: + changequote => ` ' ^V + changequote(q) => q q ^V + changequote(l,r) => l r ^V + changequote(l,r,v) => l r v + There isn't any way of switching the verbatim-quote off, + but if you make it the same as the right quote it won't + be able to do anything (we check for R, L, V in that order). +*/ +void dochq(argv, argc) + register char **argv; + register int argc; + { + if (argc > 2) { + if (*argv[2]) lquote = *argv[2]; + if (argc > 3) { + if (*argv[3]) rquote = *argv[3]; + if (argc > 4 && *argv[4]) vquote = *argv[4]; + } else { + rquote = lquote; + } + } else { + lquote = LQUOTE; + rquote = RQUOTE; + vquote = VQUOTE; + } + } + + +/* dochc(<? changecomment [left [right]]>) + 0 1 2 3 + change the comment delimiters; to single characters only. +*/ +void dochc(argv, argc) + register char **argv; + register int argc; + { + if (argc > 2) { + if (*argv[2]) scommt = *argv[2]; + if (argc > 3) { + if (*argv[3]) ecommt = *argv[3]; + } else { + ecommt = ECOMMT; + } + } else { + scommt = SCOMMT; + ecommt = ECOMMT; + } + } + + +/* dodivert - divert the output to a temporary file +*/ +void dodiv(n) + register int n; + { + if (n < 0 || n >= MAXOUT) n = 0; /* bitbucket */ + if (outfile[n] == NULL) { + m4temp[UNIQUE] = '0' + n; + if ((outfile[n] = fopen(m4temp, "w")) == NULL) + error("m4: cannot divert."); + } + oindex = n; + active = outfile[n]; + } + + +/* doundivert - undivert a specified output, or all * other outputs, in numerical order. - */ -doundiv(argv, argc) -register char *argv[]; -register int argc; -{ - register int ind; - register int n; - - if (argc > 2) { - for (ind = 2; ind < argc; ind++) { - n = atoi(argv[ind]); - if (n > 0 && n < MAXOUT && outfile[n] != NULL) - getdiv(n); - - } - } - else - for (n = 1; n < MAXOUT; n++) - if (outfile[n] != NULL) - getdiv(n); -} - -/* - * dosub - select substring - * - */ -dosub (argv, argc) -register char *argv[]; -register int argc; -{ - register char *ap, *fc, *k; - register int nc; - - if (argc < 5) - nc = MAXTOK; - else -#ifdef EXPR - nc = expr(argv[4]); -#else - nc = atoi(argv[4]); -#endif - ap = argv[2]; /* target string */ -#ifdef EXPR - fc = ap + expr(argv[3]); /* first char */ -#else - fc = ap + atoi(argv[3]); /* first char */ +*/ +void doundiv(argv, argc) + register char **argv; + register int argc; + { + register int ind; + register int n; + + if (argc > 2) { + for (ind = 2; ind < argc; ind++) { + n = expr(argv[ind]); + if (n > 0 && n < MAXOUT && outfile[n] != NULL) getdiv(n); + } + } else { + for (n = 1; n < MAXOUT; n++) + if (outfile[n] != NULL) getdiv(n); + } + } + + +/* dosub(<? substr {offset} [{length}]>) + The System V Interface Definition does not say what happens when the + offset or length are out of range. I have chosen to force them into + range, with the result that unlike the former version of this code, + dosub cannot be tricked into SIGSEGV. + + BUG: This is not 8-bit clean yet. +*/ +void dosub(argv, argc) + char **argv; + int argc; + { + register int nc; /* number of characters */ + register char *ap = argv[2]; /* target string */ + register int al = strlen(ap); /* its length */ + register int df = expr(argv[3]);/* offset */ + + if (df < 0) df = 0; else /* force df back into the range */ + if (df > al) df = al; /* 0 <= df <= al */ + al -= df; /* now al limits nc */ + + if (argc >= 5) { /* nc is provided */ + nc = expr(argv[4]); + if (nc < 0) nc = 0; else /* force nc back into the range */ + if (nc > al) nc = al; /* 0 <= nc <= strlen(ap)-df */ + } else { + nc = al; /* default is all rest of ap */ + } + ap += df + nc; + while (--nc >= 0) putback(*--ap); + } + + +/* map(dest, src, from, to) + map every character of src that is specified in from + into "to" and replace in dest. (source "src" remains untouched) + + This is a standard implementation of Icon's map(s,from,to) function. + Within mapvec, we replace every character of "from" with the + corresponding character in "to". If "to" is shorter than "from", + then the corresponding entries are null, which means that those + characters disappear altogether. Furthermore, imagine a call like + map(dest, "sourcestring", "srtin", "rn..*"). In this case, `s' maps + to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s' ultimately + maps to `*'. In order to achieve this effect in an efficient manner + (i.e. without multiple passes over the destination string), we loop + over mapvec, starting with the initial source character. If the + character value (dch) in this location is different from the source + character (sch), sch becomes dch, once again to index into mapvec, + until the character value stabilizes (i.e. sch = dch, in other words + mapvec[n] == n). Even if the entry in the mapvec is null for an + ordinary character, it will stabilize, since mapvec[0] == 0 at all + times. At the end, we restore mapvec* back to normal where + mapvec[n] == n for 0 <= n <= 127. This strategy, along with the + restoration of mapvec, is about 5 times faster than any algorithm + that makes multiple passes over the destination string. +*/ + +void map(d, s, f, t) + char *d, *s, *f, *t; + { + register unsigned char *dest = (unsigned char *)d; + register unsigned char *src = (unsigned char *)s; + unsigned char *from = (unsigned char *)f; + register unsigned char *to = (unsigned char *)t; + register unsigned char *tmp; + register unsigned char sch, dch; + static unsigned char mapvec[1+UCHAR_MAX] = {1}; + + if (mapvec[0]) { + register int i; + for (i = 0; i <= UCHAR_MAX; i++) mapvec[i] = i; + } + if (src && *src) { + /* create a mapping between "from" and "to" */ + if (to && *to) + for (tmp = from; sch = *tmp++; ) mapvec[sch] = *to++; + else + for (tmp = from; sch = *tmp++; ) mapvec[sch] = '\0'; + + while (sch = *src++) { + while ((dch = mapvec[sch]) != sch) sch = dch; + if (*dest = dch) dest++; + } + /* restore all the changed characters */ + for (tmp = from; sch = *tmp++; ) mapvec[sch] = sch; + } + *dest = '\0'; + } + + +#ifdef EXTENDED + +/* m4trim(<? m4trim [string [leading [trailing [middle [rep]]]]]>) + 0 1 2 3 4 5 6 + + (1) Any prefix consisting of characters in the "leading" set is removed. + The default is " \t\n". + (2) Any suffix consisting of characters in the "trailing" set is removed. + The default is to be the same as leading. + (3) Any block of consecutive characters in the "middle" set is replaced + by the rep string. The default for middle is " \t\n", and the + default for rep is the first character of middle. +*/ +void m4trim(argv, argc) + char **argv; + int argc; + { + static unsigned char repbuf[2] = " "; + static unsigned char layout[] = " \t\n\r\f"; + unsigned char *string = argc > 2 ? ucArgv(2) : repbuf+1; + unsigned char *leading = argc > 3 ? ucArgv(3) : layout; + unsigned char *trailing = argc > 4 ? ucArgv(4) : leading; + unsigned char *middle = argc > 5 ? ucArgv(5) : trailing; + unsigned char *rep = argc > 6 ? ucArgv(6) : + (repbuf[0] = *middle, repbuf); + static unsigned char sets[1+UCHAR_MAX]; +# define PREF 1 +# define SUFF 2 +# define MIDL 4 + register int i, n; + + for (i = UCHAR_MAX; i >= 0; ) sets[i--] = 0; + while (*leading) sets[*leading++] |= PREF; + while (*trailing) sets[*trailing++] |= SUFF; + while (*middle) sets[*middle++] |= MIDL; + + while (*string && sets[*string]&PREF) string++; + n = strlen((char *)string); + while (n > 0 && sets[string[n-1]]&SUFF) n--; + while (n > 0) { + i = string[--n]; + if (sets[i]&MIDL) { + pbstr((char*)rep); + while (n > 0 && sets[string[n-1]]&MIDL) n--; + } else { + putback(i); + } + } + } + + +/* defquote(MacroName # The name of the "quoter" macro to be defined. + [, Opener # default: "'". The characters to place at the + # beginning of the result. + [, Separator # default: ",". The characters to place between + # successive arguments. + [, Closer # default: same as Opener. The characters to + # place at the end of the result. + [, Escape # default: `' The escape character to put in + # front of things that need escaping. + [, Default # default: simple. Possible values are + # [lL].* = letter, corresponds to PLAIN1. + # [dD].* = digit, corresponds to PLAIN2. + # [sS].* = simple, corresponds to SIMPLE. + # [eE].* = escaped,corresponds to SCAPED. + # .*, corresponds to FANCY + [, Letters # default: `'. The characters of type "L". + [, Digits # default: `'. The characters of type "D". + [, Simple # default: `'. The characters of type "S". + [, Escaped # default: `'. The characters of type "E". + {, Fancy # default: none. Each has the form `C'`Repr' + # saying that the character C is to be represented + # as Repr. Can be used for trigraphs, \n, &c. + }]]]]]]]]]) + + Examples: + defquote(DOUBLEQT, ") + defquote(SINGLEQT, ') + After these definitions, + DOUBLEQT(a, " b", c) => "a,"" b"",c" + SINGLEQT("Don't`, 'he said.") => '"Don''t, he said."' + Other examples defining quote styles for several languages will be + provided later. + + A quoter is represented in M4 by a special identifying number and a + pointer to a Quoter record. I expect that there will be few quoters + but that they will need to go fairly fast. + +*/ + +#define PLAIN1 0 +#define PLAIN2 1 +#define SIMPLE 2 +#define SCAPED 3 +#define FANCY 4 + +struct Quoter + { + char *opener; + char *separator; + char *closer; + char *escape; + char *fancy[1+UCHAR_MAX]; + char class[1+UCHAR_MAX]; + }; + +void freeQuoter(q) + struct Quoter *q; + { + int i; + + free(q->opener); + free(q->separator); + free(q->closer); + free(q->escape); + for (i = UCHAR_MAX; i >= 0; i--) + if (q->fancy[i]) free(q->fancy[i]); + free((char *)q); + } + +/* dodefqt(< + 0 ? + 1 defquote + 2 MacroName + [ 3 Opener + [ 4 Separator + [ 5 Closer + [ 6 Escape + [ 7 Default + [ 8 Letters + [ 9 Digits + [10 Simple + [11 Escaped + [11+i Fancy[i] ]]]]]]]]]]>) +*/ + +void dodefqt(argv, argc) + char **argv; + int argc; + { + struct Quoter q, *r; + register int i; + register unsigned char *s; + register int c; + ndptr p; + + if (!(argc > 2 && *argv[2])) error(nuldefmsg); + switch (argc > 7 ? argv[7][0] : '\0') { + case 'l': case 'L': c = PLAIN1; break; + case 'd': case 'D': c = PLAIN2; break; + case 'e': case 'E': c = SCAPED; break; + case 'f': case 'F': c = FANCY; break; + default: c = SIMPLE; + } + for (i = UCHAR_MAX; --i >= 0; ) q.class[i] = c; + for (i = UCHAR_MAX; --i >= 0; ) q.fancy[i] = 0; + q.opener = strsave(argc > 3 ? argv[3] : ""); + q.separator = strsave(argc > 4 ? argv[4] : ","); + q.closer = strsave(argc > 5 ? argv[5] : q.opener); + q.escape = strsave(argc > 6 ? argv[6] : ""); + if (argc > 8) + for (s = (unsigned char *)argv[8]; c = *s++; ) + q.class[c] = PLAIN1; + if (argc > 9) + for (s = (unsigned char *)argv[9]; c = *s++; ) + q.class[c] = PLAIN2; + if (argc > 10) + for (s = (unsigned char *)argv[10]; c = *s++; ) + q.class[c] = SIMPLE; + if (argc > 11) + for (s = (unsigned char *)argv[11]; c = *s++; ) + q.class[c] = SCAPED; + for (i = 12; i < argc; i++) { + s = (unsigned char *)argv[i]; + c = *s++; + q.fancy[c] = strsave((char *)s); + q.class[c] = FANCY; + } + /* Now we have to make sure that the closing quote works. */ + if ((c = q.closer[0]) && q.class[c] <= SIMPLE) { + if (q.escape[0]) { + q.class[c] = SCAPED; + } else { + char buf[3]; + buf[0] = c, buf[1] = c, buf[2] = '\0'; + q.fancy[c] = strsave(buf); + q.class[c] = FANCY; + } + } + /* We also have to make sure that the escape (if any) works. */ + if ((c = q.escape[0]) && q.class[c] <= SIMPLE) { + q.class[c] = SCAPED; + } + r = (struct Quoter *)malloc(sizeof *r); + if (r == NULL) error("m4: no more memory"); + *r = q; + p = addent(argv[2]); + p->defn = (char *)r; + p->type = QUTRTYPE; + } + + +/* doqutr(<DB MN A1 ... An>) + 0 1 2 n+1 argc + argv[0] points to the struct Quoter. + argv[1] points to the name of this quoting macro + argv[2..argc-1] point to the arguments. + This applies a user-defined quoting macro. For example, we could + define a macro to produce Prolog identifiers: + defquote(plid, ', , ', , simple, + abcdefghijklmnopqrstuvwxyz, + ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789) + + After doing that, + plid(foo) => foo + plid(*) => '*' + plid(Don't) => 'Don''t' + plid(foo,) => 'foo' +*/ +void doqutr(argv, argc) + char **argv; + int argc; + /* DEFINITION-BLOCK MacroName Arg1 ... Argn + 0 1 2 n-1 argc + */ + { + struct Quoter *r = (struct Quoter *)argv[0]; + char *p; + register unsigned char *b, *e; + int i; + register int c; + + for (;;) { /* does not actually loop */ + if (argc != 3) break; + b = ucArgv(2); + e = b + strlen((char*)b); + if (e == b) break; + if (r->class[*b++] != PLAIN1) break; + while (b != e && r->class[*b] <= PLAIN2) b++; + if (b != e) break; + pbstr(argv[2]); + return; + } + + p = r->closer; + if (argc < 3) { + pbstr(p); + } else + for (i = argc-1; i >= 2; i--) { + pbstr(p); + b = ucArgv(i); + e = b+strlen((char *)b); + while (e != b) + switch (r->class[c = *--e]) { + case FANCY: + p = r->fancy[c]; + if (p) { + pbstr(p); + } else { + pbrad(c, 8, 1); + pbstr(r->escape); + } + break; + case SCAPED: + putback(c); + pbstr(r->escape); + break; + default: + putback(c); + break; + } + p = r->separator; + } + pbstr(r->opener); + } + #endif - if (fc >= ap && fc < ap+strlen(ap)) - for (k = fc+min(nc,strlen(fc))-1; k >= fc; k--) - putback(*k); -} - -/* - * map: - * map every character of s1 that is specified in from - * into s3 and replace in s. (source s1 remains untouched) - * - * This is a standard implementation of map(s,from,to) function of ICON - * language. Within mapvec, we replace every character of "from" with - * the corresponding character in "to". If "to" is shorter than "from", - * than the corresponding entries are null, which means that those - * characters dissapear altogether. Furthermore, imagine - * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case, - * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s' - * ultimately maps to `*'. In order to achieve this effect in an efficient - * manner (i.e. without multiple passes over the destination string), we - * loop over mapvec, starting with the initial source character. if the - * character value (dch) in this location is different than the source - * character (sch), sch becomes dch, once again to index into mapvec, until - * the character value stabilizes (i.e. sch = dch, in other words - * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary - * character, it will stabilize, since mapvec[0] == 0 at all times. At the - * end, we restore mapvec* back to normal where mapvec[n] == n for - * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is - * about 5 times faster than any algorithm that makes multiple passes over - * destination string. - * - */ - -map(dest,src,from,to) -register char *dest; -register char *src; -register char *from; -register char *to; -{ - register char *tmp; - register char sch, dch; - static char mapvec[128] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, - 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, - 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, - 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127 - }; - - if (*src) { - tmp = from; - /* - * create a mapping between "from" and "to" - */ - while (*from) - mapvec[*from++] = (*to) ? *to++ : (char) 0; - - while (*src) { - sch = *src++; - dch = mapvec[sch]; - while (dch != sch) { - sch = dch; - dch = mapvec[sch]; - } - if (*dest = dch) - dest++; - } - /* - * restore all the changed characters - */ - while (*tmp) { - mapvec[*tmp] = *tmp; - tmp++; - } - } - *dest = (char) 0; -} diff --git a/usr.bin/mail/misc/mail.rc b/usr.bin/mail/misc/mail.rc index 90e937a7ec..21922d743a 100644 --- a/usr.bin/mail/misc/mail.rc +++ b/usr.bin/mail/misc/mail.rc @@ -1,2 +1,2 @@ -set append dot save +set append dot save ask ignore Received Message-Id Resent-Message-Id Status Mail-From Return-Path Via diff --git a/usr.bin/make/Makefile.dist b/usr.bin/make/Makefile.dist index c311d7e990..1149e60fac 100644 --- a/usr.bin/make/Makefile.dist +++ b/usr.bin/make/Makefile.dist @@ -1,7 +1,21 @@ # a very simple makefile... -pmake: - @echo 'make started.' - cc -I. -c *.c - cd lst.lib; cc -I.. -c *.c - cc *.o lst.lib/*.o -o pmake - @echo 'make completed.' + +CFLAGS = -I. -DMACHINE=\"sun3\" + +MAKESRC = arch.c buf.c compat.c cond.c dir.c hash.c job.c main.c make.c \ + parse.c str.c suff.c targ.c var.c +MAKEOBJS = arch.o buf.o compat.o cond.o dir.o hash.o job.o main.o make.o \ + parse.o str.o suff.o targ.o var.o + + +all: make + +lst.lib/liblst.a: + cd lst.lib ; make -f Makefile.dist + +make: $(MAKEOBJS) lst.lib/liblst.a + $(CC) -o make $(MAKEOBJS) lst.lib/liblst.a + +clean: + rm -f $(MAKEOBJS) make + cd lst.lib ; make -f Makefile.dist clean diff --git a/usr.bin/make/main.c b/usr.bin/make/main.c index 959e7b6eea..6a4195506c 100644 --- a/usr.bin/make/main.c +++ b/usr.bin/make/main.c @@ -35,12 +35,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00015 - * -------------------- ----- ---------------------- - * - * 17 Mar 92 John Woods Fix "make - make" bug */ #ifndef lint @@ -119,7 +113,8 @@ static Boolean jobsRunning; /* TRUE if the jobs might be running */ static Boolean ReadMakefile(); -static char *curdir; /* if chdir'd for an architecture */ +static char *curdir; /* pathname of dir where make ran */ +static int obj_is_elsewhere; /* if chdir'd for an architecture */ /*- * MainParseArgs -- @@ -364,18 +359,15 @@ main(argc, argv) */ if (!(path = getenv("MAKEOBJDIR"))) path = _PATH_OBJDIR; + curdir = emalloc((u_int)MAXPATHLEN + 1); + if (!getwd(curdir)) { + (void)fprintf(stderr, "make: %s.\n", curdir); + exit(2); + } if (!lstat(path, &sb)) { - if (S_ISDIR(sb.st_mode)) - curdir = ".."; - else { - curdir = emalloc((u_int)MAXPATHLEN + 1); - if (!getwd(curdir)) { - (void)fprintf(stderr, "make: %s.\n", curdir); - exit(2); - } - } + obj_is_elsewhere = 1; if (chdir(path)) { - (void)fprintf(stderr, "make: %s: %s.\n", + (void)fprintf(stderr, "make: fatal: %s exists, but I can't chdir to it: %s.\n", path, strerror(errno)); exit(2); } @@ -410,11 +402,9 @@ main(argc, argv) Var_Init(); /* As well as the lists of variables for * parsing arguments */ - if (curdir) { + if (obj_is_elsewhere) Dir_AddDir(dirSearchPath, curdir); - Var_Set(".CURDIR", curdir, VAR_GLOBAL); - } else - Var_Set(".CURDIR", ".", VAR_GLOBAL); + Var_Set(".CURDIR", curdir, VAR_GLOBAL); /* * Initialize various variables. @@ -610,7 +600,7 @@ ReadMakefile(fname) if (stream = fopen(fname, "r")) goto found; /* if we've chdir'd, rebuild the path name */ - if (curdir && *fname != '/') { + if (obj_is_elsewhere && *fname != '/') { (void)sprintf(path, "%s/%s", curdir, fname); if (stream = fopen(path, "r")) { fname = path; diff --git a/usr.bin/man/Makefile b/usr.bin/man/Makefile deleted file mode 100644 index 020a7c5f04..0000000000 --- a/usr.bin/man/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# @(#)Makefile 5.1 (Berkeley) 5/27/90 - -SUBDIR= apropos man whatis - -.include <bsd.subdir.mk> diff --git a/usr.bin/man/apropos/Makefile b/usr.bin/man/apropos/Makefile deleted file mode 100644 index bf87283f45..0000000000 --- a/usr.bin/man/apropos/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# @(#)Makefile 5.3 (Berkeley) 5/27/90 - -PROG= apropos -SRCS= apropos.c config.c -.PATH: ${.CURDIR}/../man - -.include "../../Makefile.inc" -.include <bsd.prog.mk> diff --git a/usr.bin/man/apropos/apropos.1 b/usr.bin/man/apropos/apropos.1 deleted file mode 100644 index 44653de134..0000000000 --- a/usr.bin/man/apropos/apropos.1 +++ /dev/null @@ -1,119 +0,0 @@ -.\" Copyright (c) 1989, 1990 The Regents of the University of California. -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)apropos.1 6.10 (Berkeley) 3/14/91 -.\" -.Dd March 14, 1991 -.Dt APROPOS 1 -.Sh NAME -.Nm apropos -.Nd locate commands by keyword lookup -.Sh SYNOPSIS -.Nm apropos -.Op Fl M Ar path -.Op Fl m Ar path -.Ar keyword ... -.Sh DESCRIPTION -.Nm Apropos -shows which manual pages contain instances of any of the given -.Ar keyword(s) -in their title line. -Each word is considered separately and case of letters is ignored. -Words which are part of other words are considered; when looking for -.Dq compile , -.Nm apropos -will also list all instances of -.Dq compiler . -.Pp -If the line output by -.Nm apropos -starts -.Dq Li name(section) ... -you can enter -.Dq Li man section name -to get -its documentation. -.Pp -The options are as follows: -.Bl -tag -width Ds -.It Fl M -Override the list of standard directories -.Nm apropos -searches for a database named -.Pa whatis.db . -The supplied -.Ar path -must be a colon -.Dq \&: -separated list of directories. -This search path may also be set using the environment variable -.Ev MANPATH . -.It Fl m -Augment the list of standard directories -.Nm apropos -searches for its database. -The supplied -.Ar path -must be a colon -.Dq \&: -separated list of directories. -These directories will be searched before the standard directories -or the directories supplied with the -.Fl M -option or the -.Ev MANPATH -environment variable are searched. -.Sh ENVIRONMENT -.Bl -tag -width MANPATH -.It Ev MANPATH -The standard search path used by -.Xr man 1 -may be overridden by specifying a path in the -.Ev MANPATH -environment variable. -The format of the path is a colon -.Dq \&: -separated list of directories. -.El -.Sh FILES -.Bl -tag -width whatis.db -compact -.It Pa whatis.db -name of the apropos database -.El -.Sh SEE ALSO -.Xr man 1 , -.Xr whatis 1 , -.Xr whereis 1 -.Sh HISTORY -The -.Nm -command appeared in -.Bx 3.0 . diff --git a/usr.bin/man/apropos/apropos.c b/usr.bin/man/apropos/apropos.c deleted file mode 100644 index 547edaab5b..0000000000 --- a/usr.bin/man/apropos/apropos.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (c) 1987 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1987 Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)apropos.c 5.12 (Berkeley) 6/1/90"; -#endif /* not lint */ - -#include <sys/param.h> -#include <stdio.h> -#include <ctype.h> -#include <string.h> -#include <stdlib.h> -#include "../man/pathnames.h" - -#define MAXLINELEN 256 /* max line handled */ - -char *progname; - -static int *found, foundman; - -main(argc, argv) - int argc; - char **argv; -{ - extern char *optarg; - extern int optind; - register char **p; - int ch; - char *p_augment, *p_path, **getdb(); - - progname = "apropos"; - p_augment = p_path = NULL; - while ((ch = getopt(argc, argv, "M:m:P:")) != EOF) - switch((char)ch) { - case 'M': - case 'P': /* backward compatible */ - p_path = optarg; - break; - case 'm': - p_augment = optarg; - break; - case '?': - default: - usage(); - } - argv += optind; - argc -= optind; - - if (argc < 1) - usage(); - - /*NOSTRICT*/ - if (!(found = (int *)malloc((u_int)argc))) - enomem(); - bzero((void *)found, argc * sizeof(int)); - - for (p = argv; *p; ++p) /* convert to lower-case */ - lowstr(*p, *p); - - if (p_augment) - apropos(argv, p_augment, 1); - if (p_path || (p_path = getenv("MANPATH"))) - apropos(argv, p_path, 1); - else - for (p = getdb(); *p; ++p) - apropos(argv, *p, 0); - - if (!foundman) { - (void)fprintf(stderr, - "apropos: : no %s file found.\n", _PATH_WHATIS); - exit(1); - } - for (p = argv; *p; ++p) - if (!found[p - argv]) - (void)printf("%s: nothing appropriate\n", *p); -} - -apropos(argv, path, buildpath) - char **argv, *path; - int buildpath; -{ - register char *end, *name, **p; - char buf[MAXLINELEN + 1], wbuf[MAXLINELEN + 1]; - - for (name = path; name; name = end) { /* through name list */ - if (end = index(name, ':')) - *end++ = '\0'; - - if (buildpath) { - char hold[MAXPATHLEN + 1]; - - (void)sprintf(hold, "%s/%s", name, _PATH_WHATIS); - name = hold; - } - - if (!freopen(name, "r", stdin)) - continue; - - foundman = 1; - - /* for each file found */ - while (fgets(buf, sizeof(buf), stdin)) { - if (!index(buf, '\n')) { - (void)fprintf(stderr, - "apropos: %s line too long.\n", name); - exit(1); - } - lowstr(buf, wbuf); - for (p = argv; *p; ++p) - if (match(wbuf, *p)) { - (void)printf("%s", buf); - found[p - argv] = 1; - - /* only print line once */ - while (*++p) - if (match(wbuf, *p)) - found[p - argv] = 1; - break; - } - } - } -} - -/* - * match -- - * match anywhere the string appears - */ -match(bp, str) - register char *bp, *str; -{ - register int len; - register char test; - - if (!*bp) - return(0); - /* backward compatible: everything matches empty string */ - if (!*str) - return(1); - for (test = *str++, len = strlen(str); *bp;) - if (test == *bp++ && !strncmp(bp, str, len)) - return(1); - return(0); -} - -/* - * lowstr -- - * convert a string to lower case - */ -lowstr(from, to) - register char *from, *to; -{ - register char ch; - - while ((ch = *from++) && ch != '\n') - *to++ = isupper(ch) ? tolower(ch) : ch; - *to = '\0'; -} - -/* - * usage -- - * print usage message and die - */ -usage() -{ - (void)fprintf(stderr, - "usage: apropos [-M path] [-m path] keyword ...\n"); - exit(1); -} diff --git a/usr.bin/man/man/Makefile b/usr.bin/man/man/Makefile deleted file mode 100644 index ec0efb69bb..0000000000 --- a/usr.bin/man/man/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# @(#)Makefile 5.12 (Berkeley) 9/30/90 - -PROG= man -SRCS= config.c man.c -MAN1= man.0 -MAN5= man.conf.0 - -.include "../../Makefile.inc" -.include <bsd.prog.mk> diff --git a/usr.bin/man/man/config.c b/usr.bin/man/man/config.c deleted file mode 100644 index ad460b86ac..0000000000 --- a/usr.bin/man/man/config.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00008 - * -------------------- ----- ---------------------- - * - * 03 Sep 92 James Dolter Fixed 1K pathbuf bug - */ - -#ifndef lint -static char sccsid[] = "@(#)config.c 5.6 (Berkeley) 3/1/91"; -#endif /* not lint */ - -#include <sys/param.h> -#include <stdio.h> -#include <errno.h> -#include <string.h> -#include <stdlib.h> -#include <pwd.h> -#include "pathnames.h" - -#define MAXLINE 1024 - -extern char *progname; -char *pathbuf, **arorder; - -static FILE *cfp; - -/* - * getpath -- - * read in the configuration file, calling a function with the line - * from each matching section. - */ -char * -getpath(sects) - char **sects; -{ - register char **av, *p; - size_t len; - char line[MAXLINE]; - static int openconfig(); - - openconfig(); - while (fgets(line, sizeof(line), cfp)) { - if (!index(line, '\n')) { - (void)fprintf(stderr, "%s: config line too long.\n", - progname); - exit(1); - } - p = strtok(line, " \t\n"); - if (!p || *p == '#') - continue; - for (av = sects; *av; ++av) - if (!strcmp(p, *av)) - break; - if (!*av) - continue; - while (p = strtok((char *)NULL, " \t\n")) { - len = strlen(p); - if (p[len - 1] == '/') - for (av = arorder; *av; ++av) - cadd(p, len, *av); - else - cadd(p, len, (char *)NULL); - } - } - return(pathbuf); -} - -cadd(add1, len1, add2) -char *add1, *add2; -register size_t len1; -{ - static size_t buflen; - static char *bp, *endp; - register size_t len2; - register size_t oldlen; /* 03 Sep 92*/ - - len2 = add2 ? strlen(add2) : 0; - if (!bp || bp + len1 + len2 + 2 >= endp) { - oldlen = bp - pathbuf; /* 03 Sep 92*/ - if (!(pathbuf = realloc(pathbuf, buflen += 1024))) - enomem(); - bp = pathbuf + oldlen; /* 03 Sep 92*/ - endp = pathbuf + buflen; - } - bcopy(add1, bp, len1); - bp += len1; - if (len2) { - bcopy(add2, bp, len2); - bp += len2; - } - *bp++ = ':'; - *bp = '\0'; -} - -static -openconfig() -{ - if (cfp) { - rewind(cfp); - return; - } - if (!(cfp = fopen(_PATH_MANCONF, "r"))) { - (void)fprintf(stderr, "%s: no configuration file %s.\n", - progname, _PATH_MANCONF); - exit(1); - } -} - -char ** -getdb() -{ - register char *p; - int cnt, num; - char **ar, line[MAXLINE]; - - ar = NULL; - num = 0; - cnt = -1; - openconfig(); - while (fgets(line, sizeof(line), cfp)) { - if (!index(line, '\n')) { - (void)fprintf(stderr, "%s: config line too long.\n", - progname); - exit(1); - } - p = strtok(line, " \t\n"); -#define WHATDB "_whatdb" - if (!p || *p == '#' || strcmp(p, WHATDB)) - continue; - while (p = strtok((char *)NULL, " \t\n")) { - if (cnt == num - 1 && - !(ar = realloc(ar, (num += 30) * sizeof(char **)))) - enomem(); - if (!(ar[++cnt] = strdup(p))) - enomem(); - } - } - if (ar) { - if (cnt == num - 1 && - !(ar = realloc(ar, ++num * sizeof(char **)))) - enomem(); - ar[++cnt] = NULL; - } - return(ar); -} - -char ** -getorder() -{ - register char *p; - int cnt, num; - char **ar, line[MAXLINE]; - - ar = NULL; - num = 0; - cnt = -1; - openconfig(); - while (fgets(line, sizeof(line), cfp)) { - if (!index(line, '\n')) { - (void)fprintf(stderr, "%s: config line too long.\n", - progname); - exit(1); - } - p = strtok(line, " \t\n"); -#define SUBDIR "_subdir" - if (!p || *p == '#' || strcmp(p, SUBDIR)) - continue; - while (p = strtok((char *)NULL, " \t\n")) { - if (cnt == num - 1 && - !(ar = realloc(ar, (num += 30) * sizeof(char **)))) - enomem(); - if (!(ar[++cnt] = strdup(p))) - enomem(); - } - } - if (ar) { - if (cnt == num - 1 && - !(ar = realloc(ar, ++num * sizeof(char **)))) - enomem(); - ar[++cnt] = NULL; - } - return(ar); -} - -getsection(sect) - char *sect; -{ - register char *p; - char line[MAXLINE]; - - openconfig(); - while (fgets(line, sizeof(line), cfp)) { - if (!index(line, '\n')) { - (void)fprintf(stderr, "%s: config line too long.\n", - progname); - exit(1); - } - p = strtok(line, " \t\n"); - if (!p || *p == '#') - continue; - if (!strcmp(p, sect)) - return(1); - } - return(0); -} - -enomem() -{ - (void)fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM)); - exit(1); -} diff --git a/usr.bin/man/man/man.1 b/usr.bin/man/man/man.1 deleted file mode 100644 index 07768c0a82..0000000000 --- a/usr.bin/man/man/man.1 +++ /dev/null @@ -1,177 +0,0 @@ -.\" Copyright (c) 1989, 1990 The Regents of the University of California. -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)man.1 6.14 (Berkeley) 4/29/91 -.\" -.Dd April 29, 1991 -.Dt MAN 1 -.Os BSD 4 -.Sh NAME -.Nm man -.Nd display the on-line manual pages -.Sh SYNOPSIS -.Nm man -.Op Fl achw -.Op Fl M Ar path -.Op Fl m Ar path -.Op Ar section -.Ar name Ar ... -.Sh DESCRIPTION -The -.Nm man -utility -displays the -.Bx -manual pages entitled -.Ar name . -.Pp -The options are as follows: -.Bl -tag -width indent -.It Fl a -Display all of the manual pages for a specified -.Ar section -and -.Ar name -combination. -(Normally, only the first manual page found is displayed.) -.It Fl c -Copy the manual page to the standard output instead of using -.Xr more 1 -to paginate it. -This is done by default if the standard output is not a terminal device. -.It Fl h -Display only the -.Dq Tn SYNOPSIS -lines of the requested manual pages. -.It Fl M -Override the list of standard directories which -.Nm man -searches for manual pages. -The supplied -.Ar path -must be a colon (``:'') separated list of directories. -This search path may also be set using the environment variable -.Ev MANPATH . -The subdirectories to be searched as well as their search order -is specified by the ``_subdir'' line in the -.Nm man -configuration file. -.It Fl m -Augment the list of standard directories which -.Nm man -searches for manual pages. -The supplied -.Ar path -must be a colon (``:'') separated list of directories. -These directories will be searched before the standard directories or -the directories specified using the -.Fl M -option or the -.Ev MANPATH -environment variable. -.It Fl w -List the pathnames of the manual pages which -.Nm man -would display for the specified -.Ar section -and -.Ar name -combination. -.El -.Pp -The optional -.Ar section -restricts the directories that -.Nm man -will search. -The -.Nm man -configuration file (see -.Xr man_conf 5 ) -specifies the possible -.Ar section -values that are currently available. -If only a single argument is specified or if the first argument is -not a valid section, -.Nm man -assumes that the argument is the name of a manual page to be displayed. -.Sh ENVIRONMENT -.Bl -tag -width MANPATHX -.It Ev MACHINE -As some manual pages are intended only for use on certain architectures, -.Nm man -searches certain directories applicable to the current machine. -Man's -determination of the current machine type may be overridden by setting -the environment variable -.Ev MACHINE -to the name of an architecture (see -.Xr machine 1 ) . -Machine specific areas are checked before general areas. -.It Ev MANPATH -The standard search path used by -.Nm man -may be overridden by specifying a path in the -.Ev MANPATH -environment -variable. -The format of the path is a colon (``:'') separated list of directories. -The subdirectories to be searched as well as their search order -is specified by the ``_subdir'' line in the -.Nm man -configuration file. -.It Ev PAGER -Any value of the environment variable -.Ev PAGER -will be used instead of the standard pagination program -.Xr more 1 . -.El -.Sh FILES -.Bl -tag -width /etc/man.conf -compact -.It Pa /etc/man.conf -man configuration file (see -.Xr man_conf 5 ) -.El -.Sh SEE ALSO -.Xr apropos 1 , -.Xr machine 1 , -.Xr whatis 1 , -.Xr whereis 1 , -.Xr man_conf 5 -.Sh BUGS -The on-line manual pages are, by necessity, forgiving toward stupid -display devices, causing some manual pages to not be as good as their -typeset counterparts. -.Sh HISTORY -A -.Nm -command appeared in -.At v6 . diff --git a/usr.bin/man/man/man.c b/usr.bin/man/man/man.c deleted file mode 100644 index c05a79e5c6..0000000000 --- a/usr.bin/man/man/man.c +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright (c) 1987 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1987 Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)man.c 5.24 (Berkeley) 7/21/90"; -#endif /* not lint */ - -#include <sys/param.h> -#include <sys/file.h> -#include <errno.h> -#include <stdio.h> -#include <ctype.h> -#include <string.h> -#include <stdlib.h> -#include "pathnames.h" - -extern int errno; - -int f_all, f_cat, f_how, f_where; -char *command, *machine, *p_augment, *p_path, *pager, *progname; -extern char **arorder, *pathbuf; - -main(argc, argv) - int argc; - register char **argv; -{ - extern char *optarg; - extern int optind; - int ch, res; - char *section[2], *check_pager(), *getpath(), **getorder(), *tmp; - - progname = "man"; - while ((ch = getopt(argc, argv, "-acfhkM:m:P:w")) != EOF) - switch((char)ch) { - case 'a': - f_all = 1; - break; - case 'c': - case '-': /* deprecated */ - f_cat = 1; - break; - case 'h': - f_how = 1; - break; - case 'm': - p_augment = optarg; - break; - case 'M': - case 'P': /* backward compatibility */ - p_path = optarg; - break; - /* - * "man -f" and "man -k" are backward compatible, undocumented - * ways of calling whatis(1) and apropos(1). - */ - case 'f': - jump(argv, "-f", "whatis"); - /* NOTREACHED */ - case 'k': - jump(argv, "-k", "apropos"); - /* NOTREACHED */ - case 'w': - f_all = f_where = 1; - break; - case '?': - default: - usage(); - } - argv += optind; - - if (!*argv) - usage(); - - if (!f_cat && !f_how) - if (!isatty(1)) - f_cat = 1; - else if (pager = getenv("PAGER")) - pager = check_pager(pager); - else - pager = _PATH_PAGER; - - if (!(machine = getenv("MACHINE"))) - machine = MACHINE; - - /* see if checking in a specific section */ - if (argc > 1 && getsection(*argv)) { - section[0] = *argv++; - section[1] = (char *)NULL; - } else { - section[0] = "_default"; - section[1] = (char *)NULL; - } - - arorder = getorder(); - if (p_path || (p_path = getenv("MANPATH"))) { - char buf[MAXPATHLEN], **av; - - tmp = strtok(p_path, ":"); - while (tmp) { - (void)sprintf(buf, "%s/", tmp); - for (av = arorder; *av; ++av) - cadd(buf, strlen(buf), *av); - tmp = strtok((char *)NULL, ":"); - } - p_path = pathbuf; - } else if (!(p_path = getpath(section)) && !p_augment) { - (void)fprintf(stderr, - "man: no place to search for those manual pages.\n"); - exit(1); - } - - for (; *argv; ++argv) { - if (p_augment) - res = manual(p_augment, *argv); - res = manual(p_path, *argv); - if (res || f_where) - continue; - (void)fprintf(stderr, - "man: no entry for %s in the manual.\n", *argv); - exit(1); - } - - /* use system(3) in case someone's pager is "pager arg1 arg2" */ - if (command) - (void)system(command); - exit(0); -} - -/* - * manual -- - * given a path, a directory list and a file name, find a file - * that matches; check ${directory}/${dir}/{file name} and - * ${directory}/${dir}/${machine}/${file name}. - */ -manual(path, name) - char *path, *name; -{ - register int res; - register char *end; - char fname[MAXPATHLEN + 1]; - - for (res = 0;; path = end + 1) { - if (!*path) /* foo: */ - break; - if (end = index(path, ':')) { - if (end == path + 1) /* foo::bar */ - continue; - *end = '\0'; - } - (void)sprintf(fname, "%s/%s.0", path, name); - if (access(fname, R_OK)) { - (void)sprintf(fname, "%s/%s/%s.0", path, machine, name); - if (access(fname, R_OK)) - continue; - } - - if (f_where) - (void)printf("man: found in %s.\n", fname); - else if (f_cat) - cat(fname); - else if (f_how) - how(fname); - else - add(fname); - if (!f_all) - return(1); - res = 1; - if (!end) - break; - *end = ':'; - } - return(res); -} - -/* - * how -- - * display how information - */ -how(fname) - char *fname; -{ - register FILE *fp; - - register int lcnt, print; - register char *p; - char buf[BUFSIZ]; - - if (!(fp = fopen(fname, "r"))) { - (void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno)); - exit(1); - } -#define S1 "SYNOPSIS" -#define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS" -#define D1 "DESCRIPTION" -#define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN" - for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) { - if (!strncmp(buf, S1, sizeof(S1) - 1) || - !strncmp(buf, S2, sizeof(S2) - 1)) { - print = 1; - continue; - } else if (!strncmp(buf, D1, sizeof(D1) - 1) || - !strncmp(buf, D2, sizeof(D2) - 1)) - return; - if (!print) - continue; - if (*buf == '\n') - ++lcnt; - else { - for(; lcnt; --lcnt) - (void)putchar('\n'); - for (p = buf; isspace(*p); ++p); - (void)fputs(p, stdout); - } - } - (void)fclose(fp); -} -/* - * cat -- - * cat out the file - */ -cat(fname) - char *fname; -{ - register int fd, n; - char buf[BUFSIZ]; - - if ((fd = open(fname, O_RDONLY, 0)) < 0) { - (void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno)); - exit(1); - } - while ((n = read(fd, buf, sizeof(buf))) > 0) - if (write(1, buf, n) != n) { - (void)fprintf(stderr, - "man: write: %s\n", strerror(errno)); - exit(1); - } - if (n == -1) { - (void)fprintf(stderr, "man: read: %s\n", strerror(errno)); - exit(1); - } - (void)close(fd); -} - -/* - * add -- - * add a file name to the list for future paging - */ -add(fname) - char *fname; -{ - static u_int buflen; - static int len; - static char *cp; - int flen; - - if (!command) { - if (!(command = malloc(buflen = 1024))) - enomem(); - len = strlen(strcpy(command, pager)); - cp = command + len; - } - flen = strlen(fname); - if (len + flen + 2 > buflen) { /* +2 == space, EOS */ - if (!(command = realloc(command, buflen += 1024))) - enomem(); - cp = command + len; - } - *cp++ = ' '; - len += flen + 1; /* +1 = space */ - (void)strcpy(cp, fname); - cp += flen; -} - -/* - * check_pager -- - * check the user supplied page information - */ -char * -check_pager(name) - char *name; -{ - register char *p; - char *save; - - /* - * if the user uses "more", we make it "more -s"; watch out for - * PAGER = "mypager /usr/ucb/more" - */ - for (p = name; *p && !isspace(*p); ++p); - for (; p > name && *p != '/'; --p); - if (p != name) - ++p; - - /* make sure it's "more", not "morex" */ - if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ - save = name; - /* allocate space to add the "-s" */ - if (!(name = - malloc((u_int)(strlen(save) + sizeof("-s") + 1)))) - enomem(); - (void)sprintf(name, "%s %s", save, "-s"); - } - return(name); -} - -/* - * jump -- - * strip out flag argument and jump - */ -jump(argv, flag, name) - char **argv, *name; - register char *flag; -{ - register char **arg; - - argv[0] = name; - for (arg = argv + 1; *arg; ++arg) - if (!strcmp(*arg, flag)) - break; - for (; *arg; ++arg) - arg[0] = arg[1]; - execvp(name, argv); - (void)fprintf(stderr, "%s: Command not found.\n", name); - exit(1); -} - -/* - * usage -- - * print usage message and die - */ -usage() -{ - (void)fprintf(stderr, - "usage: man [-ac] [-M path] [-m path] [section] title ...\n"); - exit(1); -} diff --git a/usr.bin/man/man/man.conf.5 b/usr.bin/man/man/man.conf.5 deleted file mode 100644 index d3faf0deb9..0000000000 --- a/usr.bin/man/man/man.conf.5 +++ /dev/null @@ -1,170 +0,0 @@ -.\" Copyright (c) 1989, 1991 The Regents of the University of California. -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)man.conf.5 5.6 (Berkeley) 5/10/91 -.\" -.Dd May 10, 1991 -.Dt MAN.CONF 5 -.Sh NAME -.Nm man.conf -.Nd configuration file for -.Xr man 1 -.Sh DESCRIPTION -The -.Xr man 1 , -.Xr apropos 1 , -and -.Xr whatis 1 -commands -search for manual pages or their database files as specified by the -.Nm man.conf -file. -Manual pages are expected to be preformatted (see -.Xr nroff 1 ) -and named with a trailing ``.0''. -.Pp -The -.Nm man.conf -file contains two types of lines. -.Pp -The first type of line is a ``section'' line, which contains a -section name followed by a directory path. -Lines in this format specify that manual pages for the section -may be found in the directory. -.Pp -Directories named with a trailing slash character (``/'') are expected -to contain subdirectories (see the keyword ``_subdir'' below) instead -of manual pages. -These subdirectories are searched instead of the directory. -.Pp -All directories (either explicitly specified or named with a trailing -slash) may contain subdirectories. -The -.Xr man 1 -command -automatically searches any subdirectory with the same name as the -current machine type before the directory is searched. -No specification of these subdirectories is necessary in the -.Nm man.conf -file. -.Pp -Section names are unrestricted except for the reserved words specified -below; in general, however, it is best to avoid anything beginning with -an underscore (``_'') in order to avoid future incompatibilities. -.Pp -The section named ``_default'' is the list of directories to be -searched if no section is specified. -.Pp -The second type of line is preceded with a ``keyword''. -The possible keywords and their meanings are as follows: -.Pp -.Bl -tag -width indent -.It _subdir -The list (in search order) of subdirectories which will be searched in -any directory named with a trailing slash (``/'') character. This -list is also used when a path is specified by the -.Ev MANPATH -environment -variable or the -.Fl M -option. -.It _version -The version of the configuration file. -.It _whatdb -The full pathname (not just a directory path) for a database to be used -by the -.Xr apropos 1 -and -.Xr whatis 1 -commands. -.El -.Pp -Multiple specifications for all types of lines (except for ``_version'') -are cumulative and the entries are used in the order listed in the file; -multiple entries may be listed per line, as well. -.Pp -Empty lines or lines whose first non-whitespace character is a hash -mark (``#'') are ignored. -.Sh EXAMPLES -Given the following -.Nm man.conf -file: -.Bd -literal -offset indent -_version BSD.1 -_subdir cat1 cat2 cat3 -_default /usr/share/man/ -sect3 /usr/share/man/cat3 -.Ed -.Pp -The default -.Xr mktemp 3 -manual page should be stored in -.Dq Pa /usr/share/man/cat3/mktemp.0 . -Any -.Tn VAX -architecture specific version of it should be stored in -.Dq Pa cat3/vax/mktemp.0 . -.Pp -The command -.Dq Li man mktemp -would search the subdirectories -.Dq Pa cat1 -.Dq Pa cat2 , -and -.Dq Pa cat3 , -in -.Dq Pa /usr/share/man , -in that order, for -.Dq Pa mktemp.0''. -If a subdirectory with the same name as the current machine type -existed in any of them, it would be searched as well. -.Pp -The command -.Dq Li man sect3 mktemp -would only search -.Dq Li /usr/share/man/cat3 -and any possible per machine subdirectory. -.Sh FILES -.Bl -tag -width /etc/man.conf -compact -.It Pa /etc/man.conf -Standard manual directory search path. -.El -.Sh SEE ALSO -.Xr apropos 1 , -.Xr machine 1 , -.Xr man 1 , -.Xr whatis 1 , -.Xr whereis 1 -.Sh HISTORY -The -.Nm -file format -.Ud . diff --git a/usr.bin/man/man/pathnames.h b/usr.bin/man/man/pathnames.h deleted file mode 100644 index a23c2f91f3..0000000000 --- a/usr.bin/man/man/pathnames.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)pathnames.h 5.4 (Berkeley) 6/1/90 - */ - -#define _PATH_MANCONF "/etc/man.conf" -#define _PATH_PAGER "/usr/bin/more -s" -#define _PATH_WHATIS "whatis.db" diff --git a/usr.bin/man/whatis/Makefile b/usr.bin/man/whatis/Makefile deleted file mode 100644 index 04fa09ae90..0000000000 --- a/usr.bin/man/whatis/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# @(#)Makefile 5.3 (Berkeley) 5/27/90 - -PROG= whatis -SRCS= whatis.c config.c -.PATH: ${.CURDIR}/../man - -.include "../../Makefile.inc" -.include <bsd.prog.mk> diff --git a/usr.bin/man/whatis/whatis.1 b/usr.bin/man/whatis/whatis.1 deleted file mode 100644 index be52ab71be..0000000000 --- a/usr.bin/man/whatis/whatis.1 +++ /dev/null @@ -1,105 +0,0 @@ -.\" Copyright (c) 1989, 1990 The Regents of the University of California. -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)whatis.1 6.7 (Berkeley) 4/23/91 -.\" -.Dd April 23, 1991 -.Dt WHATIS 1 -.Os BSD 4 -.Sh NAME -.Nm whatis -.Nd describe what a command is -.Sh SYNOPSIS -.Nm whatis -.Op Fl M Ar path -.Op Fl m Ar path -.Ar command Ar ... -.Sh DESCRIPTION -.Nm Whatis -looks up a given command and gives the header line from the manual page. -You can then use the -.Xr man 1 -command to get more information. -.Pp -The options are as follows: -.Bl -tag -width Fl -.It Fl M Ar path -Override the list of standard directories -.Nm whatis -searches for its database named -.Dq Pa whatis.db . -The supplied -.Ar path -must be a colon -.Dq \&: -separated list of directories. -This search path may also be set using the environment variable -.Ev MANPATH . -.It Fl m Ar path -Augment the list of standard directories -.Nm whatis -searches for its database named -.Dq Pa whatis.db . -The supplied -.Ar path -must be a colon -.Dq \&: -separated list of directories. -These directories will be searched before the standard directories -or the directories supplied with the -.Fl M -option or the -.Ev MANPATH -environment variable are searched. -.El -.Sh ENVIRONMENT -.Bl -tag -width MANPATH -.It Ev MANPATH -The standard search path used by -.Xr man 1 -may be overridden by specifying a path in the -.Ev MANPATH -environment variable. -.El -.Sh FILES -.Bl -tag -width whatis.db -.It Pa whatis.db -name of the whatis database -.El -.Sh SEE ALSO -.Xr apropos 1 , -.Xr man 1 , -.Xr whereis 1 -.Sh HISTORY -The -.Nm -command appeared in -.Bx 3.0 . diff --git a/usr.bin/man/whatis/whatis.c b/usr.bin/man/whatis/whatis.c deleted file mode 100644 index b6c84082ca..0000000000 --- a/usr.bin/man/whatis/whatis.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (c) 1987 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00005 - * -------------------- ----- ---------------------- - * - * 20 Aug 92 Andrew Moore Fixed core dump from uninited pointers - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1987 Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)whatis.c 5.6 (Berkeley) 6/1/90"; -#endif /* not lint */ - -#include <sys/param.h> -#include <stdio.h> -#include <ctype.h> -#include <string.h> -#include <stdlib.h> -#include "../man/pathnames.h" - -#define MAXLINELEN 256 /* max line handled */ - -char *progname; - -static int *found, foundman; - -main(argc, argv) - int argc; - char **argv; -{ - extern char *optarg; - extern int optind; - register char *beg, **p; - int ch; - char *p_augment, *p_path, **getdb(); - - progname = "whatis"; - p_augment = p_path = NULL; /* 20 Aug 92*/ - while ((ch = getopt(argc, argv, "M:m:P:")) != EOF) - switch((char)ch) { - case 'M': - case 'P': /* backward compatible */ - p_path = optarg; - break; - case 'm': - p_augment = optarg; - break; - case '?': - default: - usage(); - } - argv += optind; - argc -= optind; - - if (argc < 1) - usage(); - - /*NOSTRICT*/ - if (!(found = (int *)malloc((u_int)argc))) - enomem(); - bzero((char *)found, argc * sizeof(int)); - - for (p = argv; *p; ++p) /* trim full paths */ - if (beg = rindex(*p, '/')) - *p = beg + 1; - - if (p_augment) - whatis(argv, p_augment, 1); - if (p_path || (p_path = getenv("MANPATH"))) - whatis(argv, p_path, 1); - else - for (p = getdb(); *p; ++p) - whatis(argv, *p, 0); - - if (!foundman) { - fprintf(stderr, "whatis: no %s file found.\n", _PATH_WHATIS); - exit(1); - } - for (p = argv; *p; ++p) - if (!found[p - argv]) - printf("%s: not found\n", *p); -} - -whatis(argv, path, buildpath) - char **argv, *path; - int buildpath; -{ - register char *end, *name, **p; - char buf[MAXLINELEN + 1], wbuf[MAXLINELEN + 1]; - - for (name = path; name; name = end) { /* through name list */ - if (end = index(name, ':')) - *end++ = '\0'; - - if (buildpath) { - char hold[MAXPATHLEN + 1]; - - (void)sprintf(hold, "%s/%s", name, _PATH_WHATIS); - name = hold; - } - - if (!freopen(name, "r", stdin)) - continue; - - foundman = 1; - - /* for each file found */ - while (fgets(buf, sizeof(buf), stdin)) { - dashtrunc(buf, wbuf); - for (p = argv; *p; ++p) - if (match(wbuf, *p)) { - printf("%s", buf); - found[p - argv] = 1; - - /* only print line once */ - while (*++p) - if (match(wbuf, *p)) - found[p - argv] = 1; - break; - } - } - } -} - -/* - * match -- - * match a full word - */ -match(bp, str) - register char *bp, *str; -{ - register int len; - register char *start; - - if (!*str || !*bp) - return(0); - for (len = strlen(str);;) { - for (; *bp && !isdigit(*bp) && !isalpha(*bp); ++bp); - if (!*bp) - break; - for (start = bp++; - *bp && (*bp == '_' || isdigit(*bp) || isalpha(*bp)); ++bp); - if (bp - start == len && !strncasecmp(start, str, len)) - return(1); - } - return(0); -} - -/* - * dashtrunc -- - * truncate a string at " - " - */ -dashtrunc(from, to) - register char *from, *to; -{ - register int ch; - - for (; (ch = *from) && ch != '\n' && - (ch != ' ' || from[1] != '-' || from[2] != ' '); ++from) - *to++ = ch; - *to = '\0'; -} - -/* - * usage -- - * print usage message and die - */ -usage() -{ - (void)fprintf(stderr, - "usage: whatis [-M path] [-m path] command ...\n"); - exit(1); -} diff --git a/usr.bin/mkdep/Makefile b/usr.bin/mkdep/Makefile index 236a765d55..bd7e70a3ce 100644 --- a/usr.bin/mkdep/Makefile +++ b/usr.bin/mkdep/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.4 (Berkeley) 2/19/91 -MAN1= mkdep.0 +MAN1= mkdep.1 .if (${MACHINE} == "hp300" || ${MACHINE} == "i386") beforeinstall: diff --git a/usr.bin/mkdep/mkdep.gcc.sh b/usr.bin/mkdep/mkdep.gcc.sh index 94cc976f66..dbd401e49b 100644 --- a/usr.bin/mkdep/mkdep.gcc.sh +++ b/usr.bin/mkdep/mkdep.gcc.sh @@ -55,7 +55,7 @@ while : # the -p flag produces "program: program.c" style dependencies # so .o's don't get produced -p) - SED='s;\.o;;' + SED='s;\.o ; ;' shift ;; *) break ;; @@ -71,7 +71,7 @@ TMP=/tmp/mkdep$$ trap 'rm -f $TMP ; exit 1' 1 2 3 13 15 -cpp -M $* > $TMP +cpp -M $* | sed "$SED" > $TMP if [ $? != 0 ]; then echo 'mkdep: compile failed.' diff --git a/usr.bin/mkdep/mkdep.sh b/usr.bin/mkdep/mkdep.sh index 288d0003d7..2c17db3774 100644 --- a/usr.bin/mkdep/mkdep.sh +++ b/usr.bin/mkdep/mkdep.sh @@ -55,7 +55,7 @@ while : # the -p flag produces "program: program.c" style dependencies # so .o's don't get produced -p) - SED='s;\.o;;' + SED='s;\.o ; ;' shift ;; *) break ;; diff --git a/usr.bin/mkfifo/mkfifo.1 b/usr.bin/mkfifo/mkfifo.1 index e6922a63e4..5fb67002e8 100644 --- a/usr.bin/mkfifo/mkfifo.1 +++ b/usr.bin/mkfifo/mkfifo.1 @@ -42,19 +42,29 @@ .Nd make fifos .Sh SYNOPSIS .Nm mkfifo -.Op Fl p +.Op Fl m Ar mode .Ar fifo_name ... .Sh DESCRIPTION .Nm Mkfifo creates the fifos requested, in the order specified, using mode -.Li \&0777 . +.Li \&0666 +modified by the current +.Xr umask 2 . .Pp -Options available are: +The options are as follows: .Bl -tag -width Ds -.It Fl p -Create intermediate directories as required. If this option is not -specified, the full path prefix of each operand must already exist. +.It Fl m +Set the file permission bits of newly-created directories to +.Ar mode . +The mode is specified as in +.Xr chmod 1 . +In symbolic mode strings, the +.Dq + +and +.Dq - +operators are interpreted relative to an assumed initial mode of +.Dq a=rw .El .Pp .Nm Mkfifo @@ -63,18 +73,16 @@ requires write permission in the parent directory. .Nm Mkfifo exits 0 if successful, and >0 if an error occurred. .Sh STANDARDS -.Nm Mkfifo is +The +.Nm Mkfifo +command is expected to be .St -p1003.2 -compliant. -.Pp -This manual page derived from the -.St p1003.2 -manual page. +compatible. .Sh SEE ALSO .Xr mkdir 1 , .Xr rm 1 .Sh HISTORY The -.Nm +.Nm mkfifo command is .Ud diff --git a/usr.bin/mkfifo/mkfifo.c b/usr.bin/mkfifo/mkfifo.c index 1c9529a55b..065b0420f1 100644 --- a/usr.bin/mkfifo/mkfifo.c +++ b/usr.bin/mkfifo/mkfifo.c @@ -45,20 +45,36 @@ static char sccsid[] = "@(#)mkfifo.c 5.3 (Berkeley) 6/1/90"; #include <sys/stat.h> #include <errno.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> +extern void *setmode(); +extern mode_t getmode(); + main(argc, argv) int argc; char **argv; { - extern int errno, optind; - int ch, exitval, pflag; + int ch, exitval; + void * set; + mode_t mode; + + /* The default mode is the value of the bitwise inclusive or of + S_IRUSR, S_IWUSR, S_IRGRP, S_IWGRP, S_IROTH, and S_IWOTH + modified by the file creation mask */ + mode = 0666 & ~umask(0); - pflag = 0; - while ((ch = getopt(argc, argv, "p")) != EOF) + while ((ch = getopt(argc, argv, "m:")) != EOF) switch(ch) { - case 'p': - pflag = 1; + case 'm': + if (!(set = setmode(optarg))) { + (void)fprintf(stderr, "mkfifo: invalid file mode.\n"); + exit(1); + } + /* In symbolic mode strings, the + and - operators are + interpreted relative to an assumed initial mode of + a=rw. */ + mode = getmode (set, 0666); break; case '?': default: @@ -69,42 +85,17 @@ main(argc, argv) usage(); for (exitval = 0; *argv; ++argv) { - if (pflag && build(*argv)) { - exitval |= 1; - continue; - } - if (mkfifo(*argv, 0777) < 0) { + if (mkfifo(*argv, mode) < 0) { (void)fprintf(stderr, "mkfifo: %s: %s\n", *argv, strerror(errno)); - exitval |= 1; + exitval = 1; } } exit(exitval); } -build(path) - char *path; -{ - register char *p; - struct stat sb; - - for (p = path; *p; p++) { - if (*p != '/') - continue; - if (stat(path, &sb)) { - if (errno != ENOENT || mkdir(path, 0777) < 0) { - (void)fprintf(stderr, "mkdir: %s: %s\n", - path, strerror(errno)); - return(1); - } - } - *p = '/'; - } - return(0); -} - usage() { - (void)fprintf(stderr, "usage: mkfifo [-p] fifoname ...\n"); + (void)fprintf(stderr, "usage: mkfifo [-m mode] fifoname ...\n"); exit(1); } diff --git a/usr.bin/more/Makefile b/usr.bin/more/Makefile index c82bdfca22..e61848714e 100644 --- a/usr.bin/more/Makefile +++ b/usr.bin/more/Makefile @@ -1,13 +1,17 @@ -# @(#)Makefile 5.11 (Berkeley) 6/25/90 +# @(#)Makefile 5.6 (Berkeley) 3/12/91 PROG= more -DPADD= ${LIBTERM} -LDADD= -ltermcap +CFLAGS+=-I${.CURDIR} -DREGEX +SRCS= ch.c command.c decode.c filename.c help.c input.c line.c \ + linenum.c main.c option.c os.c output.c position.c prim.c \ + screen.c signal.c tags.c ttyin.c +LDADD+= -ltermcap -lgnuregex +DPADD+= ${LIBTERM} /usr/lib/libgnuregex.a MLINKS= more.1 page.1 LINKS= ${BINDIR}/more ${BINDIR}/page beforeinstall: install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/more.help \ - ${DESTDIR}/usr/share/misc/omore.help + ${DESTDIR}/usr/share/misc .include <bsd.prog.mk> diff --git a/usr.bin/more/ch.c b/usr.bin/more/ch.c new file mode 100644 index 0000000000..4924d2cc65 --- /dev/null +++ b/usr.bin/more/ch.c @@ -0,0 +1,454 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ch.c 5.11 (Berkeley) 6/21/92"; +#endif /* not lint */ + +/* + * Low level character input from the input file. + * We use these special purpose routines which optimize moving + * both forward and backward from the current read pointer. + */ + +#include <sys/types.h> +#include <sys/file.h> +#include <unistd.h> +#include <stdio.h> +#include <less.h> + +int file = -1; /* File descriptor of the input file */ + +/* + * Pool of buffers holding the most recently used blocks of the input file. + */ +struct buf { + struct buf *next, *prev; + long block; + int datasize; + char data[BUFSIZ]; +}; +int nbufs; + +/* + * The buffer pool is kept as a doubly-linked circular list, in order from + * most- to least-recently used. The circular list is anchored by buf_anchor. + */ +#define END_OF_CHAIN ((struct buf *)&buf_anchor) +#define buf_head buf_anchor.next +#define buf_tail buf_anchor.prev + +static struct { + struct buf *next, *prev; +} buf_anchor = { END_OF_CHAIN, END_OF_CHAIN }; + +extern int ispipe, cbufs, sigs; + +/* + * Current position in file. + * Stored as a block number and an offset into the block. + */ +static long ch_block; +static int ch_offset; + +/* Length of file, needed if input is a pipe. */ +static off_t ch_fsize; + +/* Number of bytes read, if input is standard input (a pipe). */ +static off_t last_piped_pos; + +/* + * Get the character pointed to by the read pointer. ch_get() is a macro + * which is more efficient to call than fch_get (the function), in the usual + * case that the block desired is at the head of the chain. + */ +#define ch_get() \ + ((buf_head->block == ch_block && \ + ch_offset < buf_head->datasize) ? \ + buf_head->data[ch_offset] : fch_get()) + +static +fch_get() +{ + extern int bs_mode; + register struct buf *bp; + register int n, ch; + register char *p, *t; + off_t pos, lseek(); + + /* look for a buffer holding the desired block. */ + for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) + if (bp->block == ch_block) { + if (ch_offset >= bp->datasize) + /* + * Need more data in this buffer. + */ + goto read_more; + /* + * On a pipe, we don't sort the buffers LRU + * because this can cause gaps in the buffers. + * For example, suppose we've got 12 1K buffers, + * and a 15K input stream. If we read the first 12K + * sequentially, then jump to line 1, then jump to + * the end, the buffers have blocks 0,4,5,6,..,14. + * If we then jump to line 1 again and try to + * read sequentially, we're out of luck when we + * get to block 1 (we'd get the "pipe error" below). + * To avoid this, we only sort buffers on a pipe + * when we actually READ the data, not when we + * find it already buffered. + */ + if (ispipe) + return(bp->data[ch_offset]); + goto found; + } + /* + * Block is not in a buffer. Take the least recently used buffer + * and read the desired block into it. If the LRU buffer has data + * in it, and input is a pipe, then try to allocate a new buffer first. + */ + if (ispipe && buf_tail->block != (long)(-1)) + (void)ch_addbuf(1); + bp = buf_tail; + bp->block = ch_block; + bp->datasize = 0; + +read_more: + pos = (ch_block * BUFSIZ) + bp->datasize; + if (ispipe) { + /* + * The data requested should be immediately after + * the last data read from the pipe. + */ + if (pos != last_piped_pos) { + error("pipe error"); + quit(); + } + } else + (void)lseek(file, pos, L_SET); + + /* + * Read the block. + * If we read less than a full block, we just return the + * partial block and pick up the rest next time. + */ + n = iread(file, &bp->data[bp->datasize], BUFSIZ - bp->datasize); + if (n == READ_INTR) + return (EOI); + if (n < 0) { + error("read error"); + quit(); + } + if (ispipe) + last_piped_pos += n; + + p = &bp->data[bp->datasize]; + bp->datasize += n; + + /* + * Set an EOI marker in the buffered data itself. Then ensure the + * data is "clean": there are no extra EOI chars in the data and + * that the "meta" bit (the 0200 bit) is reset in each char; + * also translate \r\n sequences to \n if -u flag not set. + */ + if (n == 0) { + ch_fsize = pos; + bp->data[bp->datasize++] = EOI; + } + + if (bs_mode) { + for (p = &bp->data[bp->datasize]; --n >= 0;) { + *--p &= 0177; + if (*p == EOI) + *p = 0200; + } + } + else { + for (t = p; --n >= 0; ++p) { + ch = *p & 0177; + if (ch == '\r' && n && (p[1] & 0177) == '\n') { + ++p; + *t++ = '\n'; + } + else + *t++ = (ch == EOI) ? 0200 : ch; + } + if (p != t) { + bp->datasize -= p - t; + if (ispipe) + last_piped_pos -= p - t; + } + } + +found: + if (buf_head != bp) { + /* + * Move the buffer to the head of the buffer chain. + * This orders the buffer chain, most- to least-recently used. + */ + bp->next->prev = bp->prev; + bp->prev->next = bp->next; + + bp->next = buf_head; + bp->prev = END_OF_CHAIN; + buf_head->prev = bp; + buf_head = bp; + } + + if (ch_offset >= bp->datasize) + /* + * After all that, we still don't have enough data. + * Go back and try again. + */ + goto read_more; + + return(bp->data[ch_offset]); +} + +/* + * Determine if a specific block is currently in one of the buffers. + */ +static +buffered(block) + long block; +{ + register struct buf *bp; + + for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) + if (bp->block == block) + return(1); + return(0); +} + +/* + * Seek to a specified position in the file. + * Return 0 if successful, non-zero if can't seek there. + */ +ch_seek(pos) + register off_t pos; +{ + long new_block; + + new_block = pos / BUFSIZ; + if (!ispipe || pos == last_piped_pos || buffered(new_block)) { + /* + * Set read pointer. + */ + ch_block = new_block; + ch_offset = pos % BUFSIZ; + return(0); + } + return(1); +} + +/* + * Seek to the end of the file. + */ +ch_end_seek() +{ + off_t ch_length(); + + if (!ispipe) + return(ch_seek(ch_length())); + + /* + * Do it the slow way: read till end of data. + */ + while (ch_forw_get() != EOI) + if (sigs) + return(1); + return(0); +} + +/* + * Seek to the beginning of the file, or as close to it as we can get. + * We may not be able to seek there if input is a pipe and the + * beginning of the pipe is no longer buffered. + */ +ch_beg_seek() +{ + register struct buf *bp, *firstbp; + + /* + * Try a plain ch_seek first. + */ + if (ch_seek((off_t)0) == 0) + return(0); + + /* + * Can't get to position 0. + * Look thru the buffers for the one closest to position 0. + */ + firstbp = bp = buf_head; + if (bp == END_OF_CHAIN) + return(1); + while ((bp = bp->next) != END_OF_CHAIN) + if (bp->block < firstbp->block) + firstbp = bp; + ch_block = firstbp->block; + ch_offset = 0; + return(0); +} + +/* + * Return the length of the file, if known. + */ +off_t +ch_length() +{ + off_t lseek(); + + if (ispipe) + return(ch_fsize); + return((off_t)(lseek(file, (off_t)0, L_XTND))); +} + +/* + * Return the current position in the file. + */ +off_t +ch_tell() +{ + return(ch_block * BUFSIZ + ch_offset); +} + +/* + * Get the current char and post-increment the read pointer. + */ +ch_forw_get() +{ + register int c; + + c = ch_get(); + if (c != EOI && ++ch_offset >= BUFSIZ) { + ch_offset = 0; + ++ch_block; + } + return(c); +} + +/* + * Pre-decrement the read pointer and get the new current char. + */ +ch_back_get() +{ + if (--ch_offset < 0) { + if (ch_block <= 0 || (ispipe && !buffered(ch_block-1))) { + ch_offset = 0; + return(EOI); + } + ch_offset = BUFSIZ - 1; + ch_block--; + } + return(ch_get()); +} + +/* + * Allocate buffers. + * Caller wants us to have a total of at least want_nbufs buffers. + * keep==1 means keep the data in the current buffers; + * otherwise discard the old data. + */ +ch_init(want_nbufs, keep) + int want_nbufs; + int keep; +{ + register struct buf *bp; + char message[80]; + + cbufs = nbufs; + if (nbufs < want_nbufs && ch_addbuf(want_nbufs - nbufs)) { + /* + * Cannot allocate enough buffers. + * If we don't have ANY, then quit. + * Otherwise, just report the error and return. + */ + (void)sprintf(message, "cannot allocate %d buffers", + want_nbufs - nbufs); + error(message); + if (nbufs == 0) + quit(); + return; + } + + if (keep) + return; + + /* + * We don't want to keep the old data, + * so initialize all the buffers now. + */ + for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) + bp->block = (long)(-1); + last_piped_pos = (off_t)0; + ch_fsize = NULL_POSITION; + (void)ch_seek((off_t)0); +} + +/* + * Allocate some new buffers. + * The buffers are added to the tail of the buffer chain. + */ +ch_addbuf(nnew) + int nnew; +{ + register struct buf *bp; + register struct buf *newbufs; + char *calloc(); + + /* + * We don't have enough buffers. + * Allocate some new ones. + */ + newbufs = (struct buf *)calloc((u_int)nnew, sizeof(struct buf)); + if (newbufs == NULL) + return(1); + + /* + * Initialize the new buffers and link them together. + * Link them all onto the tail of the buffer list. + */ + nbufs += nnew; + cbufs = nbufs; + for (bp = &newbufs[0]; bp < &newbufs[nnew]; bp++) { + bp->next = bp + 1; + bp->prev = bp - 1; + bp->block = (long)(-1); + } + newbufs[nnew-1].next = END_OF_CHAIN; + newbufs[0].prev = buf_tail; + buf_tail->next = &newbufs[0]; + buf_tail = &newbufs[nnew-1]; + return(0); +} diff --git a/usr.bin/more/command.c b/usr.bin/more/command.c new file mode 100644 index 0000000000..8421303e17 --- /dev/null +++ b/usr.bin/more/command.c @@ -0,0 +1,675 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)command.c 5.22 (Berkeley) 6/21/92"; +#endif /* not lint */ + +#include <sys/param.h> +#include <stdio.h> +#include <ctype.h> +#include <less.h> +#include "pathnames.h" + +#define NO_MCA 0 +#define MCA_DONE 1 +#define MCA_MORE 2 + +extern int erase_char, kill_char, werase_char; +extern int ispipe; +extern int sigs; +extern int quit_at_eof; +extern int hit_eof; +extern int sc_width; +extern int sc_height; +extern int sc_window; +extern int curr_ac; +extern int ac; +extern int quitting; +extern int scroll; +extern int screen_trashed; /* The screen has been overwritten */ + +static char cmdbuf[120]; /* Buffer for holding a multi-char command */ +static char *cp; /* Pointer into cmdbuf */ +static int cmd_col; /* Current column of the multi-char command */ +static int longprompt; /* if stat command instead of prompt */ +static int mca; /* The multicharacter command (action) */ +static int last_mca; /* The previous mca */ +static int number; /* The number typed by the user */ +static char *shellcmd = NULL; /* Pointer to a shell command */ +static int wsearch; /* Search for matches (1) or non-matches (0) */ + +#define CMD_RESET cp = cmdbuf /* reset command buffer to empty */ +#define CMD_EXEC lower_left(); flush() + +/* backspace in command buffer. */ +static +cmd_erase() +{ + /* + * backspace past beginning of the string: this usually means + * abort the command. + */ + if (cp == cmdbuf) + return(1); + + /* erase an extra character, for the carat. */ + if (CONTROL_CHAR(*--cp)) { + backspace(); + --cmd_col; + } + + backspace(); + --cmd_col; + return(0); +} + +/* set up the display to start a new multi-character command. */ +start_mca(action, prompt) + int action; + char *prompt; +{ + lower_left(); + clear_eol(); + putstr(prompt); + cmd_col = strlen(prompt); + mca = action; +} + +/* + * process a single character of a multi-character command, such as + * a number, or the pattern of a search command. + */ +static +cmd_char(c) + int c; +{ + if (c == erase_char) + return(cmd_erase()); + /* in this order, in case werase == erase_char */ + if (c == werase_char) { + if (cp > cmdbuf) { + while (isspace(cp[-1]) && !cmd_erase()); + while (!isspace(cp[-1]) && !cmd_erase()); + while (isspace(cp[-1]) && !cmd_erase()); + } + return(cp == cmdbuf); + } + if (c == kill_char) { + while (!cmd_erase()); + return(1); + } + /* + * No room in the command buffer, or no room on the screen; + * {{ Could get fancy here; maybe shift the displayed line + * and make room for more chars, like ksh. }} + */ + if (cp >= &cmdbuf[sizeof(cmdbuf)-1] || cmd_col >= sc_width-3) + bell(); + else { + *cp++ = c; + if (CONTROL_CHAR(c)) { + putchr('^'); + cmd_col++; + c = CARAT_CHAR(c); + } + putchr(c); + cmd_col++; + } + return(0); +} + +prompt() +{ + extern int linenums, short_file; + extern char *current_name, *firstsearch, *next_name; + off_t len, pos, ch_length(), position(), forw_line(); + char pbuf[40]; + + /* + * if nothing is displayed yet, display starting from line 1; + * if search string provided, go there instead. + */ + if (position(TOP) == NULL_POSITION) { + if (forw_line((off_t)0) == NULL_POSITION) + return(0); + if (!firstsearch || !search(1, firstsearch, 1, 1)) + jump_back(1); + } + else if (screen_trashed) + repaint(); + + /* if no -e flag and we've hit EOF on the last file, quit. */ + if ((!quit_at_eof || short_file) && hit_eof && curr_ac + 1 >= ac) + quit(); + + /* select the proper prompt and display it. */ + lower_left(); + clear_eol(); + if (longprompt) { + so_enter(); + putstr(current_name); + putstr(":"); + if (!ispipe) { + (void)sprintf(pbuf, " file %d/%d", curr_ac + 1, ac); + putstr(pbuf); + } + if (linenums) { + (void)sprintf(pbuf, " line %d", currline(BOTTOM)); + putstr(pbuf); + } + if ((pos = position(BOTTOM)) != NULL_POSITION) { + (void)sprintf(pbuf, " byte %ld", pos); + putstr(pbuf); + if (!ispipe && (len = ch_length())) { + (void)sprintf(pbuf, "/%ld pct %ld%%", + len, ((100 * pos) / len)); + putstr(pbuf); + } + } + so_exit(); + longprompt = 0; + } + else { + so_enter(); + putstr(current_name); + if (hit_eof) + if (next_name) { + putstr(": END (next file: "); + putstr(next_name); + putstr(")"); + } + else + putstr(": END"); + else if (!ispipe && + (pos = position(BOTTOM)) != NULL_POSITION && + (len = ch_length())) { + (void)sprintf(pbuf, " (%ld%%)", ((100 * pos) / len)); + putstr(pbuf); + } + so_exit(); + } + return(1); +} + +/* get command character. */ +static +getcc() +{ + extern int cmdstack; + int ch; + + /* left over from error() routine. */ + if (cmdstack) { + ch = cmdstack; + cmdstack = NULL; + return(ch); + } + if (cp > cmdbuf && position(TOP) == NULL_POSITION) { + /* + * Command is incomplete, so try to complete it. + * There are only two cases: + * 1. We have "/string" but no newline. Add the \n. + * 2. We have a number but no command. Treat as #g. + * (This is all pretty hokey.) + */ + if (mca != A_DIGIT) + /* Not a number; must be search string */ + return('\n'); + else + /* A number; append a 'g' */ + return('g'); + } + return(getchr()); +} + +/* execute a multicharacter command. */ +static +exec_mca() +{ + extern int file; + extern char *tagfile; + register char *p; + char *glob(), *fexpand(); + + *cp = '\0'; + CMD_EXEC; + switch (mca) { + case A_F_SEARCH: + (void)search(1, cmdbuf, number, wsearch); + break; + case A_B_SEARCH: + (void)search(0, cmdbuf, number, wsearch); + break; + case A_EXAMINE: + for (p = cmdbuf; isspace(*p); ++p); + (void)edit(glob(p)); + break; + case A_SHELL: + /* + * Copy cmdbuf to shellcmd, + * expanding any special characters ("%" or "#" + * or an initial !). + */ + if ((p = fexpand(cmdbuf, shellcmd)) == NULL) + break; + else if (shellcmd != NULL) + free(shellcmd); + lsystem(shellcmd = p); + error("!done"); + break; + case A_TAGFILE: + for (p = cmdbuf; isspace(*p); ++p); + findtag(p); + if (tagfile == NULL) + break; + if (edit(tagfile)) + (void)tagsearch(); + break; + } +} + +/* add a character to a multi-character command. */ +static +mca_char(c) + int c; +{ + switch (mca) { + case 0: /* not in a multicharacter command. */ + case A_PREFIX: /* in the prefix of a command. */ + return(NO_MCA); + case A_DIGIT: + /* + * Entering digits of a number. + * Terminated by a non-digit. + */ + if (!isascii(c) || !isdigit(c) && + c != erase_char && c != kill_char && c != werase_char) { + /* + * Not part of the number. + * Treat as a normal command character. + */ + *cp = '\0'; + number = atoi(cmdbuf); + CMD_RESET; + mca = 0; + return(NO_MCA); + } + break; + } + + /* + * Any other multicharacter command + * is terminated by a newline. + */ + if (c == '\n' || c == '\r') { + exec_mca(); + return(MCA_DONE); + } + + /* append the char to the command buffer. */ + if (cmd_char(c)) + return(MCA_DONE); + + return(MCA_MORE); +} + +/* + * Main command processor. + * Accept and execute commands until a quit command, then return. + */ +commands() +{ + register int c; + register int action; + + last_mca = 0; + scroll = (sc_height + 1) / 2; + + for (;;) { + mca = 0; + number = 0; + + /* + * See if any signals need processing. + */ + if (sigs) { + psignals(); + if (quitting) + quit(); + } + /* + * Display prompt and accept a character. + */ + CMD_RESET; + if (!prompt()) { + next_file(1); + continue; + } + noprefix(); + c = getcc(); + +again: if (sigs) + continue; + + /* + * If we are in a multicharacter command, call mca_char. + * Otherwise we call cmd_decode to determine the + * action to be performed. + */ + if (mca) + switch (mca_char(c)) { + case MCA_MORE: + /* + * Need another character. + */ + c = getcc(); + goto again; + case MCA_DONE: + /* + * Command has been handled by mca_char. + * Start clean with a prompt. + */ + continue; + case NO_MCA: + /* + * Not a multi-char command + * (at least, not anymore). + */ + break; + } + + /* decode the command character and decide what to do. */ + switch (action = cmd_decode(c)) { + case A_DIGIT: /* first digit of a number */ + start_mca(A_DIGIT, ":"); + goto again; + case A_F_SCREEN: /* forward one screen */ + CMD_EXEC; + if (number <= 0 && (number = sc_window) <= 0) + number = sc_height - 1; + forward(number, 1); + break; + case A_B_SCREEN: /* backward one screen */ + CMD_EXEC; + if (number <= 0 && (number = sc_window) <= 0) + number = sc_height - 1; + backward(number, 1); + break; + case A_F_LINE: /* forward N (default 1) line */ + CMD_EXEC; + forward(number <= 0 ? 1 : number, 0); + break; + case A_B_LINE: /* backward N (default 1) line */ + CMD_EXEC; + backward(number <= 0 ? 1 : number, 0); + break; + case A_F_SCROLL: /* forward N lines */ + CMD_EXEC; + if (number > 0) + scroll = number; + forward(scroll, 0); + break; + case A_B_SCROLL: /* backward N lines */ + CMD_EXEC; + if (number > 0) + scroll = number; + backward(scroll, 0); + break; + case A_FREPAINT: /* flush buffers and repaint */ + if (!ispipe) { + ch_init(0, 0); + clr_linenum(); + } + /* FALLTHROUGH */ + case A_REPAINT: /* repaint the screen */ + CMD_EXEC; + repaint(); + break; + case A_GOLINE: /* go to line N, default 1 */ + CMD_EXEC; + if (number <= 0) + number = 1; + jump_back(number); + break; + case A_PERCENT: /* go to percent of file */ + CMD_EXEC; + if (number < 0) + number = 0; + else if (number > 100) + number = 100; + jump_percent(number); + break; + case A_GOEND: /* go to line N, default end */ + CMD_EXEC; + if (number <= 0) + jump_forw(); + else + jump_back(number); + break; + case A_STAT: /* print file name, etc. */ + longprompt = 1; + continue; + case A_QUIT: /* exit */ + quit(); + case A_F_SEARCH: /* search for a pattern */ + case A_B_SEARCH: + if (number <= 0) + number = 1; + start_mca(action, (action==A_F_SEARCH) ? "/" : "?"); + last_mca = mca; + wsearch = 1; + c = getcc(); + if (c == '!') { + /* + * Invert the sense of the search; set wsearch + * to 0 and get a new character for the start + * of the pattern. + */ + start_mca(action, + (action == A_F_SEARCH) ? "!/" : "!?"); + wsearch = 0; + c = getcc(); + } + goto again; + case A_AGAIN_SEARCH: /* repeat previous search */ + if (number <= 0) + number = 1; + if (wsearch) + start_mca(last_mca, + (last_mca == A_F_SEARCH) ? "/" : "?"); + else + start_mca(last_mca, + (last_mca == A_F_SEARCH) ? "!/" : "!?"); + CMD_EXEC; + (void)search(mca == A_F_SEARCH, (char *)NULL, + number, wsearch); + break; + case A_HELP: /* help */ + lower_left(); + clear_eol(); + putstr("help"); + CMD_EXEC; + help(); + break; + case A_TAGFILE: /* tag a new file */ + CMD_RESET; + start_mca(A_TAGFILE, "Tag: "); + c = getcc(); + goto again; + case A_FILE_LIST: /* show list of file names */ + CMD_EXEC; + showlist(); + repaint(); + break; + case A_EXAMINE: /* edit a new file */ + CMD_RESET; + start_mca(A_EXAMINE, "Examine: "); + c = getcc(); + goto again; + case A_VISUAL: /* invoke the editor */ + if (ispipe) { + error("Cannot edit standard input"); + break; + } + CMD_EXEC; + editfile(); + ch_init(0, 0); + clr_linenum(); + break; + case A_NEXT_FILE: /* examine next file */ + if (number <= 0) + number = 1; + next_file(number); + break; + case A_PREV_FILE: /* examine previous file */ + if (number <= 0) + number = 1; + prev_file(number); + break; + case A_SETMARK: /* set a mark */ + lower_left(); + clear_eol(); + start_mca(A_SETMARK, "mark: "); + c = getcc(); + if (c == erase_char || c == kill_char) + break; + setmark(c); + break; + case A_GOMARK: /* go to mark */ + lower_left(); + clear_eol(); + start_mca(A_GOMARK, "goto mark: "); + c = getcc(); + if (c == erase_char || c == kill_char) + break; + gomark(c); + break; + case A_SHELL: + /* + * Shell escape. + */ + start_mca(A_SHELL, "!"); + c = getcc(); + goto again; + case A_PREFIX: + /* + * The command is incomplete (more chars are needed). + * Display the current char so the user knows what's + * going on and get another character. + */ + if (mca != A_PREFIX) + start_mca(A_PREFIX, ""); + if (CONTROL_CHAR(c)) { + putchr('^'); + c = CARAT_CHAR(c); + } + putchr(c); + c = getcc(); + goto again; + default: + bell(); + break; + } + } +} + +editfile() +{ + extern char *current_file; + static int dolinenumber; + static char *editor; + int c; + char buf[MAXPATHLEN * 2 + 20], *getenv(); + + if (editor == NULL) { + editor = getenv("EDITOR"); + /* pass the line number to vi */ + if (editor == NULL || *editor == '\0') { + editor = _PATH_VI; + dolinenumber = 1; + } + else + dolinenumber = 0; + } + if (dolinenumber && (c = currline(MIDDLE))) + (void)sprintf(buf, "%s +%d %s", editor, c, current_file); + else + (void)sprintf(buf, "%s %s", editor, current_file); + lsystem(buf); +} + +showlist() +{ + extern int sc_width; + extern char **av; + register int indx, width; + int len; + char *p; + + if (ac <= 0) { + error("No files provided as arguments."); + return; + } + for (width = indx = 0; indx < ac;) { + p = strcmp(av[indx], "-") ? av[indx] : "stdin"; + len = strlen(p) + 1; + if (curr_ac == indx) + len += 2; + if (width + len + 1 >= sc_width) { + if (!width) { + if (curr_ac == indx) + putchr('['); + putstr(p); + if (curr_ac == indx) + putchr(']'); + ++indx; + } + width = 0; + putchr('\n'); + continue; + } + if (width) + putchr(' '); + if (curr_ac == indx) + putchr('['); + putstr(p); + if (curr_ac == indx) + putchr(']'); + width += len; + ++indx; + } + putchr('\n'); + error((char *)NULL); +} diff --git a/usr.bin/more/decode.c b/usr.bin/more/decode.c new file mode 100644 index 0000000000..655e004f1e --- /dev/null +++ b/usr.bin/more/decode.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)decode.c 5.9 (Berkeley) 3/1/91"; +#endif /* not lint */ + +/* + * Routines to decode user commands. + * + * This is all table driven. + * A command table is a sequence of command descriptors. + * Each command descriptor is a sequence of bytes with the following format: + * <c1><c2>...<cN><0><action> + * The characters c1,c2,...,cN are the command string; that is, + * the characters which the user must type. + * It is terminated by a null <0> byte. + * The byte after the null byte is the action code associated + * with the command string. + * + * The default commands are described by cmdtable. + */ + +#include <sys/param.h> +#include <sys/file.h> +#include <stdio.h> +#include <less.h> + +/* + * Command table is ordered roughly according to expected + * frequency of use, so the common commands are near the beginning. + */ +#define CONTROL(c) ((c)&037) + +static char cmdtable[] = { + '\r',0, A_F_LINE, + '\n',0, A_F_LINE, + 's',0, A_F_LINE, + 'j',0, A_F_LINE, + 'k',0, A_B_LINE, + 'd',0, A_F_SCROLL, + CONTROL('D'),0, A_F_SCROLL, + 'f',0, A_F_SCROLL, + 'u',0, A_B_SCROLL, + CONTROL('U'),0, A_B_SCROLL, + ' ',0, A_F_SCREEN, + 'z',0, A_F_SCREEN, + CONTROL('F'),0, A_F_SCREEN, + 'b',0, A_B_SCREEN, + CONTROL('B'),0, A_B_SCREEN, + 'R',0, A_FREPAINT, + 'r',0, A_REPAINT, + CONTROL('L'),0, A_REPAINT, + 'g',0, A_GOLINE, + 'p',0, A_PERCENT, + '%',0, A_PERCENT, + 'G',0, A_GOEND, + '0',0, A_DIGIT, + '1',0, A_DIGIT, + '2',0, A_DIGIT, + '3',0, A_DIGIT, + '4',0, A_DIGIT, + '5',0, A_DIGIT, + '6',0, A_DIGIT, + '7',0, A_DIGIT, + '8',0, A_DIGIT, + '9',0, A_DIGIT, + + '=',0, A_STAT, + CONTROL('G'),0, A_STAT, + ':','f',0, A_STAT, + '/',0, A_F_SEARCH, + '?',0, A_B_SEARCH, + '!',0, A_SHELL, + ':','!',0, A_SHELL, + 'n',0, A_AGAIN_SEARCH, + 'm',0, A_SETMARK, + '\'',0, A_GOMARK, + 'E',0, A_EXAMINE, + 'N',0, A_NEXT_FILE, + ':','n',0, A_NEXT_FILE, + 'P',0, A_PREV_FILE, + ':','p',0, A_PREV_FILE, + 'v',0, A_VISUAL, + + 'h',0, A_HELP, + 'q',0, A_QUIT, + 'Q',0, A_QUIT, + ':','q',0, A_QUIT, + ':','t',0, A_TAGFILE, + ':', 'a', 0, A_FILE_LIST, + 'Z','Z',0, A_QUIT, +}; + +char *cmdendtable = cmdtable + sizeof(cmdtable); + +#define MAX_CMDLEN 16 + +static char kbuf[MAX_CMDLEN+1]; +static char *kp = kbuf; + +/* + * Indicate that we're not in a prefix command + * by resetting the command buffer pointer. + */ +noprefix() +{ + kp = kbuf; +} + +/* + * Decode a command character and return the associated action. + */ +cmd_decode(c) + int c; +{ + register int action = A_INVALID; + + /* + * Append the new command character to the command string in kbuf. + */ + *kp++ = c; + *kp = '\0'; + + action = cmd_search(cmdtable, cmdendtable); + + /* This is not a prefix character. */ + if (action != A_PREFIX) + noprefix(); + return(action); +} + +/* + * Search a command table for the current command string (in kbuf). + */ +cmd_search(table, endtable) + char *table; + char *endtable; +{ + register char *p, *q; + + for (p = table, q = kbuf; p < endtable; p++, q++) { + if (*p == *q) { + /* + * Current characters match. + * If we're at the end of the string, we've found it. + * Return the action code, which is the character + * after the null at the end of the string + * in the command table. + */ + if (*p == '\0') + return(p[1]); + } + else if (*q == '\0') { + /* + * Hit the end of the user's command, + * but not the end of the string in the command table. + * The user's command is incomplete. + */ + return(A_PREFIX); + } else { + /* + * Not a match. + * Skip ahead to the next command in the + * command table, and reset the pointer + * to the user's command. + */ + while (*p++ != '\0'); + q = kbuf-1; + } + } + /* + * No match found in the entire command table. + */ + return(A_INVALID); +} diff --git a/usr.bin/more/filename.c b/usr.bin/more/filename.c new file mode 100644 index 0000000000..f6ee769559 --- /dev/null +++ b/usr.bin/more/filename.c @@ -0,0 +1,100 @@ +/* + * Expand a string, substituting any "%" with the current filename, + * and any "#" with the previous filename and an initial "!" with + * the second string. + */ +char * +fexpand(s, t) + char *s; + char *t; +{ + extern char *current_file, *previous_file; + register char *fr, *to; + register int n; + register char *e; + + if (*s == '\0') + return ((char *) 0); + /* + * Make one pass to see how big a buffer we + * need to allocate for the expanded string. + */ + n = 0; + for (fr = s; *fr != '\0'; fr++) + { + switch (*fr) + { + case '!': + if (s == fr && t == (char *) 0) { + error("no previous command"); + return ((char *) 0); + } + n += (s == fr) ? strlen(t) : 1; + break; + case '%': + n += (current_file != (char *) 0) ? + strlen(current_file) : 1; + break; + case '#': + n += (previous_file != (char *) 0) ? + strlen(previous_file) : 1; + break; + default: + n++; + if (*fr == '\\') { + n++; + if (*++fr == '\0') { + error("syntax error"); + return ((char *) 0); + } + } + break; + } + } + + if ((e = (char *) calloc(n+1, sizeof(char))) == (char *) 0) { + error("cannot allocate memory"); + quit(); + } + + /* + * Now copy the string, expanding any "%" or "#". + */ + to = e; + for (fr = s; *fr != '\0'; fr++) + { + switch (*fr) + { + case '!': + if (s == fr) { + strcpy(to, t); + to += strlen(to); + } else + *to++ = *fr; + break; + case '%': + if (current_file == (char *) 0) + *to++ = *fr; + else { + strcpy(to, current_file); + to += strlen(to); + } + break; + case '#': + if (previous_file == (char *) 0) + *to++ = *fr; + else { + strcpy(to, previous_file); + to += strlen(to); + } + break; + default: + *to++ = *fr; + if (*fr == '\\') + *to++ = *++fr; + break; + } + } + *to = '\0'; + return (e); +} diff --git a/usr.bin/more/help.c b/usr.bin/more/help.c new file mode 100644 index 0000000000..4513c32111 --- /dev/null +++ b/usr.bin/more/help.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)help.c 5.7 (Berkeley) 6/1/90"; +#endif /* not lint */ + +#include <sys/param.h> +#include <less.h> +#include "pathnames.h" + +help() +{ + char cmd[MAXPATHLEN + 20]; + + (void)sprintf(cmd, "-more -ce %s", _PATH_HELPFILE); + lsystem(cmd); +} diff --git a/usr.bin/more/input.c b/usr.bin/more/input.c new file mode 100644 index 0000000000..54c05e948b --- /dev/null +++ b/usr.bin/more/input.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)input.c 5.4 (Berkeley) 6/1/90"; +#endif /* not lint */ + +/* + * High level routines dealing with getting lines of input + * from the file being viewed. + * + * When we speak of "lines" here, we mean PRINTABLE lines; + * lines processed with respect to the screen width. + * We use the term "raw line" to refer to lines simply + * delimited by newlines; not processed with respect to screen width. + */ + +#include <sys/types.h> +#include <less.h> + +extern int squeeze; +extern int sigs; +extern char *line; + +off_t ch_tell(); + +/* + * Get the next line. + * A "current" position is passed and a "new" position is returned. + * The current position is the position of the first character of + * a line. The new position is the position of the first character + * of the NEXT line. The line obtained is the line starting at curr_pos. + */ +off_t +forw_line(curr_pos) + off_t curr_pos; +{ + off_t new_pos; + register int c; + + if (curr_pos == NULL_POSITION || ch_seek(curr_pos)) + return (NULL_POSITION); + + c = ch_forw_get(); + if (c == EOI) + return (NULL_POSITION); + + prewind(); + for (;;) + { + if (sigs) + return (NULL_POSITION); + if (c == '\n' || c == EOI) + { + /* + * End of the line. + */ + new_pos = ch_tell(); + break; + } + + /* + * Append the char to the line and get the next char. + */ + if (pappend(c)) + { + /* + * The char won't fit in the line; the line + * is too long to print in the screen width. + * End the line here. + */ + new_pos = ch_tell() - 1; + break; + } + c = ch_forw_get(); + } + (void) pappend('\0'); + + if (squeeze && *line == '\0') + { + /* + * This line is blank. + * Skip down to the last contiguous blank line + * and pretend it is the one which we are returning. + */ + while ((c = ch_forw_get()) == '\n') + if (sigs) + return (NULL_POSITION); + if (c != EOI) + (void) ch_back_get(); + new_pos = ch_tell(); + } + + return (new_pos); +} + +/* + * Get the previous line. + * A "current" position is passed and a "new" position is returned. + * The current position is the position of the first character of + * a line. The new position is the position of the first character + * of the PREVIOUS line. The line obtained is the one starting at new_pos. + */ +off_t +back_line(curr_pos) + off_t curr_pos; +{ + off_t new_pos, begin_new_pos; + int c; + + if (curr_pos == NULL_POSITION || curr_pos <= (off_t)0 || + ch_seek(curr_pos-1)) + return (NULL_POSITION); + + if (squeeze) + { + /* + * Find out if the "current" line was blank. + */ + (void) ch_forw_get(); /* Skip the newline */ + c = ch_forw_get(); /* First char of "current" line */ + (void) ch_back_get(); /* Restore our position */ + (void) ch_back_get(); + + if (c == '\n') + { + /* + * The "current" line was blank. + * Skip over any preceeding blank lines, + * since we skipped them in forw_line(). + */ + while ((c = ch_back_get()) == '\n') + if (sigs) + return (NULL_POSITION); + if (c == EOI) + return (NULL_POSITION); + (void) ch_forw_get(); + } + } + + /* + * Scan backwards until we hit the beginning of the line. + */ + for (;;) + { + if (sigs) + return (NULL_POSITION); + c = ch_back_get(); + if (c == '\n') + { + /* + * This is the newline ending the previous line. + * We have hit the beginning of the line. + */ + new_pos = ch_tell() + 1; + break; + } + if (c == EOI) + { + /* + * We have hit the beginning of the file. + * This must be the first line in the file. + * This must, of course, be the beginning of the line. + */ + new_pos = ch_tell(); + break; + } + } + + /* + * Now scan forwards from the beginning of this line. + * We keep discarding "printable lines" (based on screen width) + * until we reach the curr_pos. + * + * {{ This algorithm is pretty inefficient if the lines + * are much longer than the screen width, + * but I don't know of any better way. }} + */ + if (ch_seek(new_pos)) + return (NULL_POSITION); + loop: + begin_new_pos = new_pos; + prewind(); + + do + { + c = ch_forw_get(); + if (c == EOI || sigs) + return (NULL_POSITION); + new_pos++; + if (c == '\n') + break; + if (pappend(c)) + { + /* + * Got a full printable line, but we haven't + * reached our curr_pos yet. Discard the line + * and start a new one. + */ + (void) pappend('\0'); + (void) ch_back_get(); + new_pos--; + goto loop; + } + } while (new_pos < curr_pos); + + (void) pappend('\0'); + + return (begin_new_pos); +} diff --git a/usr.bin/more/less.h b/usr.bin/more/less.h new file mode 100644 index 0000000000..75571f2e72 --- /dev/null +++ b/usr.bin/more/less.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)less.h 5.9 (Berkeley) 6/1/90 + */ + +#undef RECOMP + +#define NULL_POSITION ((off_t)(-1)) + +#define EOI (0) +#define READ_INTR (-2) + +/* Special chars used to tell put_line() to do something special */ +#define UL_CHAR '\201' /* Enter underline mode */ +#define UE_CHAR '\202' /* Exit underline mode */ +#define BO_CHAR '\203' /* Enter boldface mode */ +#define BE_CHAR '\204' /* Exit boldface mode */ + +#define CONTROL_CHAR(c) (iscntrl(c)) +#define CARAT_CHAR(c) ((c == '\177') ? '?' : (c | 0100)) + +#define TOP (0) +#define TOP_PLUS_ONE (1) +#define BOTTOM (-1) +#define BOTTOM_PLUS_ONE (-2) +#define MIDDLE (-3) + +#define A_INVALID -1 + +#define A_AGAIN_SEARCH 1 +#define A_B_LINE 2 +#define A_B_SCREEN 3 +#define A_B_SCROLL 4 +#define A_B_SEARCH 5 +#define A_DIGIT 6 +#define A_EXAMINE 7 +#define A_FREPAINT 8 +#define A_F_LINE 9 +#define A_F_SCREEN 10 +#define A_F_SCROLL 11 +#define A_F_SEARCH 12 +#define A_GOEND 13 +#define A_GOLINE 14 +#define A_GOMARK 15 +#define A_HELP 16 +#define A_NEXT_FILE 17 +#define A_PERCENT 18 +#define A_PREFIX 19 +#define A_PREV_FILE 20 +#define A_QUIT 21 +#define A_REPAINT 22 +#define A_SETMARK 23 +#define A_STAT 24 +#define A_VISUAL 25 +#define A_TAGFILE 26 +#define A_FILE_LIST 27 +#define A_SHELL 28 diff --git a/usr.bin/more/line.c b/usr.bin/more/line.c new file mode 100644 index 0000000000..15e77be1a3 --- /dev/null +++ b/usr.bin/more/line.c @@ -0,0 +1,508 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)line.c 5.5 (Berkeley) 7/24/91"; +#endif /* not lint */ + +/* + * Routines to manipulate the "line buffer". + * The line buffer holds a line of output as it is being built + * in preparation for output to the screen. + * We keep track of the PRINTABLE length of the line as it is being built. + */ + +#include <sys/types.h> +#include <ctype.h> +#include <less.h> + +static char linebuf[1024]; /* Buffer which holds the current output line */ +static char *curr; /* Pointer into linebuf */ +static int column; /* Printable length, accounting for + backspaces, etc. */ +/* + * A ridiculously complex state machine takes care of backspaces. The + * complexity arises from the attempt to deal with all cases, especially + * involving long lines with underlining, boldfacing or whatever. There + * are still some cases which will break it. + * + * There are four states: + * LN_NORMAL is the normal state (not in underline mode). + * LN_UNDERLINE means we are in underline mode. We expect to get + * either a sequence like "_\bX" or "X\b_" to continue + * underline mode, or anything else to end underline mode. + * LN_BOLDFACE means we are in boldface mode. We expect to get sequences + * like "X\bX\b...X\bX" to continue boldface mode, or anything + * else to end boldface mode. + * LN_UL_X means we are one character after LN_UNDERLINE + * (we have gotten the '_' in "_\bX" or the 'X' in "X\b_"). + * LN_UL_XB means we are one character after LN_UL_X + * (we have gotten the backspace in "_\bX" or "X\b_"; + * we expect one more ordinary character, + * which will put us back in state LN_UNDERLINE). + * LN_BO_X means we are one character after LN_BOLDFACE + * (we have gotten the 'X' in "X\bX"). + * LN_BO_XB means we are one character after LN_BO_X + * (we have gotten the backspace in "X\bX"; + * we expect one more 'X' which will put us back + * in LN_BOLDFACE). + */ +static int ln_state; /* Currently in normal/underline/bold/etc mode? */ +#define LN_NORMAL 0 /* Not in underline, boldface or whatever mode */ +#define LN_UNDERLINE 1 /* In underline, need next char */ +#define LN_UL_X 2 /* In underline, got char, need \b */ +#define LN_UL_XB 3 /* In underline, got char & \b, need one more */ +#define LN_BOLDFACE 4 /* In boldface, need next char */ +#define LN_BO_X 5 /* In boldface, got char, need \b */ +#define LN_BO_XB 6 /* In boldface, got char & \b, need same char */ + +char *line; /* Pointer to the current line. + Usually points to linebuf. */ + +extern int bs_mode; +extern int tabstop; +extern int bo_width, be_width; +extern int ul_width, ue_width; +extern int sc_width, sc_height; + +/* + * Rewind the line buffer. + */ +prewind() +{ + line = curr = linebuf; + ln_state = LN_NORMAL; + column = 0; +} + +/* + * Append a character to the line buffer. + * Expand tabs into spaces, handle underlining, boldfacing, etc. + * Returns 0 if ok, 1 if couldn't fit in buffer. + */ +#define NEW_COLUMN(addon) \ + if (column + addon + (ln_state ? ue_width : 0) > sc_width) \ + return(1); \ + else \ + column += addon + +pappend(c) + int c; +{ + if (c == '\0') { + /* + * Terminate any special modes, if necessary. + * Append a '\0' to the end of the line. + */ + switch (ln_state) { + case LN_UL_X: + curr[0] = curr[-1]; + curr[-1] = UE_CHAR; + curr++; + break; + case LN_BO_X: + curr[0] = curr[-1]; + curr[-1] = BE_CHAR; + curr++; + break; + case LN_UL_XB: + case LN_UNDERLINE: + *curr++ = UE_CHAR; + break; + case LN_BO_XB: + case LN_BOLDFACE: + *curr++ = BE_CHAR; + break; + } + ln_state = LN_NORMAL; + *curr = '\0'; + return(0); + } + + if (curr > linebuf + sizeof(linebuf) - 12) + /* + * Almost out of room in the line buffer. + * Don't take any chances. + * {{ Linebuf is supposed to be big enough that this + * will never happen, but may need to be made + * bigger for wide screens or lots of backspaces. }} + */ + return(1); + + if (!bs_mode) { + /* + * Advance the state machine. + */ + switch (ln_state) { + case LN_NORMAL: + if (curr <= linebuf + 1 + || curr[-1] != (char)('H' | 0200)) + break; + column -= 2; + if (c == curr[-2]) + goto enter_boldface; + if (c == '_' || curr[-2] == '_') + goto enter_underline; + curr -= 2; + break; + +enter_boldface: + /* + * We have "X\bX" (including the current char). + * Switch into boldface mode. + */ + column--; + if (column + bo_width + be_width + 1 >= sc_width) + /* + * Not enough room left on the screen to + * enter and exit boldface mode. + */ + return (1); + + if (bo_width > 0 && curr > linebuf + 2 + && curr[-3] == ' ') { + /* + * Special case for magic cookie terminals: + * if the previous char was a space, replace + * it with the "enter boldface" sequence. + */ + curr[-3] = BO_CHAR; + column += bo_width-1; + } else { + curr[-1] = curr[-2]; + curr[-2] = BO_CHAR; + column += bo_width; + curr++; + } + goto ln_bo_xb_case; + +enter_underline: + /* + * We have either "_\bX" or "X\b_" (including + * the current char). Switch into underline mode. + */ + column--; + if (column + ul_width + ue_width + 1 >= sc_width) + /* + * Not enough room left on the screen to + * enter and exit underline mode. + */ + return (1); + + if (ul_width > 0 && + curr > linebuf + 2 && curr[-3] == ' ') + { + /* + * Special case for magic cookie terminals: + * if the previous char was a space, replace + * it with the "enter underline" sequence. + */ + curr[-3] = UL_CHAR; + column += ul_width-1; + } else + { + curr[-1] = curr[-2]; + curr[-2] = UL_CHAR; + column += ul_width; + curr++; + } + goto ln_ul_xb_case; + /*NOTREACHED*/ + case LN_UL_XB: + /* + * Termination of a sequence "_\bX" or "X\b_". + */ + if (c != '_' && curr[-2] != '_' && c == curr[-2]) + { + /* + * We seem to have run on from underlining + * into boldfacing - this is a nasty fix, but + * until this whole routine is rewritten as a + * real DFA, ... well ... + */ + curr[0] = curr[-2]; + curr[-2] = UE_CHAR; + curr[-1] = BO_CHAR; + curr += 2; /* char & non-existent backspace */ + ln_state = LN_BO_XB; + goto ln_bo_xb_case; + } +ln_ul_xb_case: + if (c == '_') + c = curr[-2]; + curr -= 2; + ln_state = LN_UNDERLINE; + break; + case LN_BO_XB: + /* + * Termination of a sequnce "X\bX". + */ + if (c != curr[-2] && (c == '_' || curr[-2] == '_')) + { + /* + * We seem to have run on from + * boldfacing into underlining. + */ + curr[0] = curr[-2]; + curr[-2] = BE_CHAR; + curr[-1] = UL_CHAR; + curr += 2; /* char & non-existent backspace */ + ln_state = LN_UL_XB; + goto ln_ul_xb_case; + } +ln_bo_xb_case: + curr -= 2; + ln_state = LN_BOLDFACE; + break; + case LN_UNDERLINE: + if (column + ue_width + bo_width + 1 + be_width >= sc_width) + /* + * We have just barely enough room to + * exit underline mode and handle a possible + * underline/boldface run on mixup. + */ + return (1); + ln_state = LN_UL_X; + break; + case LN_BOLDFACE: + if (c == '\b') + { + ln_state = LN_BO_XB; + break; + } + if (column + be_width + ul_width + 1 + ue_width >= sc_width) + /* + * We have just barely enough room to + * exit underline mode and handle a possible + * underline/boldface run on mixup. + */ + return (1); + ln_state = LN_BO_X; + break; + case LN_UL_X: + if (c == '\b') + ln_state = LN_UL_XB; + else + { + /* + * Exit underline mode. + * We have to shuffle the chars a bit + * to make this work. + */ + curr[0] = curr[-1]; + curr[-1] = UE_CHAR; + column += ue_width; + if (ue_width > 0 && curr[0] == ' ') + /* + * Another special case for magic + * cookie terminals: if the next + * char is a space, replace it + * with the "exit underline" sequence. + */ + column--; + else + curr++; + ln_state = LN_NORMAL; + } + break; + case LN_BO_X: + if (c == '\b') + ln_state = LN_BO_XB; + else + { + /* + * Exit boldface mode. + * We have to shuffle the chars a bit + * to make this work. + */ + curr[0] = curr[-1]; + curr[-1] = BE_CHAR; + column += be_width; + if (be_width > 0 && curr[0] == ' ') + /* + * Another special case for magic + * cookie terminals: if the next + * char is a space, replace it + * with the "exit boldface" sequence. + */ + column--; + else + curr++; + ln_state = LN_NORMAL; + } + break; + } + } + + if (c == '\t') { + /* + * Expand a tab into spaces. + */ + do { + NEW_COLUMN(1); + } while ((column % tabstop) != 0); + *curr++ = '\t'; + return (0); + } + + if (c == '\b') { + if (ln_state == LN_NORMAL) + NEW_COLUMN(2); + else + column--; + *curr++ = ('H' | 0200); + return(0); + } + + if (CONTROL_CHAR(c)) { + /* + * Put a "^X" into the buffer. The 0200 bit is used to tell + * put_line() to prefix the char with a ^. We don't actually + * put the ^ in the buffer because we sometimes need to move + * chars around, and such movement might separate the ^ from + * its following character. + */ + NEW_COLUMN(2); + *curr++ = (CARAT_CHAR(c) | 0200); + return(0); + } + + /* + * Ordinary character. Just put it in the buffer. + */ + NEW_COLUMN(1); + *curr++ = c; + return (0); +} + +/* + * Analogous to forw_line(), but deals with "raw lines": + * lines which are not split for screen width. + * {{ This is supposed to be more efficient than forw_line(). }} + */ +off_t +forw_raw_line(curr_pos) + off_t curr_pos; +{ + register char *p; + register int c; + off_t new_pos, ch_tell(); + + if (curr_pos == NULL_POSITION || ch_seek(curr_pos) || + (c = ch_forw_get()) == EOI) + return (NULL_POSITION); + + p = linebuf; + + for (;;) + { + if (c == '\n' || c == EOI) + { + new_pos = ch_tell(); + break; + } + if (p >= &linebuf[sizeof(linebuf)-1]) + { + /* + * Overflowed the input buffer. + * Pretend the line ended here. + * {{ The line buffer is supposed to be big + * enough that this never happens. }} + */ + new_pos = ch_tell() - 1; + break; + } + *p++ = c; + c = ch_forw_get(); + } + *p = '\0'; + line = linebuf; + return (new_pos); +} + +/* + * Analogous to back_line(), but deals with "raw lines". + * {{ This is supposed to be more efficient than back_line(). }} + */ +off_t +back_raw_line(curr_pos) + off_t curr_pos; +{ + register char *p; + register int c; + off_t new_pos, ch_tell(); + + if (curr_pos == NULL_POSITION || curr_pos <= (off_t)0 || + ch_seek(curr_pos-1)) + return (NULL_POSITION); + + p = &linebuf[sizeof(linebuf)]; + *--p = '\0'; + + for (;;) + { + c = ch_back_get(); + if (c == '\n') + { + /* + * This is the newline ending the previous line. + * We have hit the beginning of the line. + */ + new_pos = ch_tell() + 1; + break; + } + if (c == EOI) + { + /* + * We have hit the beginning of the file. + * This must be the first line in the file. + * This must, of course, be the beginning of the line. + */ + new_pos = (off_t)0; + break; + } + if (p <= linebuf) + { + /* + * Overflowed the input buffer. + * Pretend the line ended here. + */ + new_pos = ch_tell() + 1; + break; + } + *--p = c; + } + line = p; + return (new_pos); +} diff --git a/usr.bin/more/linenum.c b/usr.bin/more/linenum.c new file mode 100644 index 0000000000..04637a3023 --- /dev/null +++ b/usr.bin/more/linenum.c @@ -0,0 +1,383 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)linenum.c 5.6 (Berkeley) 6/1/90"; +#endif /* not lint */ + +/* + * Code to handle displaying line numbers. + * + * Finding the line number of a given file position is rather tricky. + * We don't want to just start at the beginning of the file and + * count newlines, because that is slow for large files (and also + * wouldn't work if we couldn't get to the start of the file; e.g. + * if input is a long pipe). + * + * So we use the function add_lnum to cache line numbers. + * We try to be very clever and keep only the more interesting + * line numbers when we run out of space in our table. A line + * number is more interesting than another when it is far from + * other line numbers. For example, we'd rather keep lines + * 100,200,300 than 100,101,300. 200 is more interesting than + * 101 because 101 can be derived very cheaply from 100, while + * 200 is more expensive to derive from 100. + * + * The function currline() returns the line number of a given + * position in the file. As a side effect, it calls add_lnum + * to cache the line number. Therefore currline is occasionally + * called to make sure we cache line numbers often enough. + */ + +#include <sys/types.h> +#include <stdio.h> +#include <less.h> + +/* + * Structure to keep track of a line number and the associated file position. + * A doubly-linked circular list of line numbers is kept ordered by line number. + */ +struct linenum +{ + struct linenum *next; /* Link to next in the list */ + struct linenum *prev; /* Line to previous in the list */ + off_t pos; /* File position */ + off_t gap; /* Gap between prev and next */ + int line; /* Line number */ +}; +/* + * "gap" needs some explanation: the gap of any particular line number + * is the distance between the previous one and the next one in the list. + * ("Distance" means difference in file position.) In other words, the + * gap of a line number is the gap which would be introduced if this + * line number were deleted. It is used to decide which one to replace + * when we have a new one to insert and the table is full. + */ + +#define NPOOL 50 /* Size of line number pool */ + +#define LONGTIME (2) /* In seconds */ + +int lnloop = 0; /* Are we in the line num loop? */ + +static struct linenum anchor; /* Anchor of the list */ +static struct linenum *freelist; /* Anchor of the unused entries */ +static struct linenum pool[NPOOL]; /* The pool itself */ +static struct linenum *spare; /* We always keep one spare entry */ + +extern int linenums; +extern int sigs; + +/* + * Initialize the line number structures. + */ +clr_linenum() +{ + register struct linenum *p; + + /* + * Put all the entries on the free list. + * Leave one for the "spare". + */ + for (p = pool; p < &pool[NPOOL-2]; p++) + p->next = p+1; + pool[NPOOL-2].next = NULL; + freelist = pool; + + spare = &pool[NPOOL-1]; + + /* + * Initialize the anchor. + */ + anchor.next = anchor.prev = &anchor; + anchor.gap = 0; + anchor.pos = (off_t)0; + anchor.line = 1; +} + +/* + * Calculate the gap for an entry. + */ +static +calcgap(p) + register struct linenum *p; +{ + /* + * Don't bother to compute a gap for the anchor. + * Also don't compute a gap for the last one in the list. + * The gap for that last one should be considered infinite, + * but we never look at it anyway. + */ + if (p == &anchor || p->next == &anchor) + return; + p->gap = p->next->pos - p->prev->pos; +} + +/* + * Add a new line number to the cache. + * The specified position (pos) should be the file position of the + * FIRST character in the specified line. + */ +add_lnum(line, pos) + int line; + off_t pos; +{ + register struct linenum *p; + register struct linenum *new; + register struct linenum *nextp; + register struct linenum *prevp; + register off_t mingap; + + /* + * Find the proper place in the list for the new one. + * The entries are sorted by position. + */ + for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next) + if (p->line == line) + /* We already have this one. */ + return; + nextp = p; + prevp = p->prev; + + if (freelist != NULL) + { + /* + * We still have free (unused) entries. + * Use one of them. + */ + new = freelist; + freelist = freelist->next; + } else + { + /* + * No free entries. + * Use the "spare" entry. + */ + new = spare; + spare = NULL; + } + + /* + * Fill in the fields of the new entry, + * and insert it into the proper place in the list. + */ + new->next = nextp; + new->prev = prevp; + new->pos = pos; + new->line = line; + + nextp->prev = new; + prevp->next = new; + + /* + * Recalculate gaps for the new entry and the neighboring entries. + */ + calcgap(new); + calcgap(nextp); + calcgap(prevp); + + if (spare == NULL) + { + /* + * We have used the spare entry. + * Scan the list to find the one with the smallest + * gap, take it out and make it the spare. + * We should never remove the last one, so stop when + * we get to p->next == &anchor. This also avoids + * looking at the gap of the last one, which is + * not computed by calcgap. + */ + mingap = anchor.next->gap; + for (p = anchor.next; p->next != &anchor; p = p->next) + { + if (p->gap <= mingap) + { + spare = p; + mingap = p->gap; + } + } + spare->next->prev = spare->prev; + spare->prev->next = spare->next; + } +} + +/* + * If we get stuck in a long loop trying to figure out the + * line number, print a message to tell the user what we're doing. + */ +static +longloopmessage() +{ + ierror("Calculating line numbers"); + /* + * Set the lnloop flag here, so if the user interrupts while + * we are calculating line numbers, the signal handler will + * turn off line numbers (linenums=0). + */ + lnloop = 1; +} + +/* + * Find the line number associated with a given position. + * Return 0 if we can't figure it out. + */ +find_linenum(pos) + off_t pos; +{ + register struct linenum *p; + register int lno; + register int loopcount; + off_t cpos, back_raw_line(), forw_raw_line(); + time_t startime, time(); + + if (!linenums) + /* + * We're not using line numbers. + */ + return (0); + if (pos == NULL_POSITION) + /* + * Caller doesn't know what he's talking about. + */ + return (0); + if (pos == (off_t)0) + /* + * Beginning of file is always line number 1. + */ + return (1); + + /* + * Find the entry nearest to the position we want. + */ + for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next) + continue; + if (p->pos == pos) + /* Found it exactly. */ + return (p->line); + + /* + * This is the (possibly) time-consuming part. + * We start at the line we just found and start + * reading the file forward or backward till we + * get to the place we want. + * + * First decide whether we should go forward from the + * previous one or backwards from the next one. + * The decision is based on which way involves + * traversing fewer bytes in the file. + */ + flush(); + (void)time(&startime); + if (p == &anchor || pos - p->prev->pos < p->pos - pos) + { + /* + * Go forward. + */ + p = p->prev; + if (ch_seek(p->pos)) + return (0); + loopcount = 0; + for (lno = p->line, cpos = p->pos; cpos < pos; lno++) + { + /* + * Allow a signal to abort this loop. + */ + cpos = forw_raw_line(cpos); + if (sigs || cpos == NULL_POSITION) + return (0); + if (loopcount >= 0 && ++loopcount > 100) { + loopcount = 0; + if (time((time_t *)NULL) + >= startime + LONGTIME) { + longloopmessage(); + loopcount = -1; + } + } + } + lnloop = 0; + /* + * If the given position is not at the start of a line, + * make sure we return the correct line number. + */ + if (cpos > pos) + lno--; + } else + { + /* + * Go backward. + */ + if (ch_seek(p->pos)) + return (0); + loopcount = 0; + for (lno = p->line, cpos = p->pos; cpos > pos; lno--) + { + /* + * Allow a signal to abort this loop. + */ + cpos = back_raw_line(cpos); + if (sigs || cpos == NULL_POSITION) + return (0); + if (loopcount >= 0 && ++loopcount > 100) { + loopcount = 0; + if (time((time_t *)NULL) + >= startime + LONGTIME) { + longloopmessage(); + loopcount = -1; + } + } + } + lnloop = 0; + } + + /* + * We might as well cache it. + */ + add_lnum(lno, cpos); + return (lno); +} + +/* + * Return the line number of the "current" line. + * The argument "where" tells which line is to be considered + * the "current" line (e.g. TOP, BOTTOM, MIDDLE, etc). + */ +currline(where) + int where; +{ + off_t pos, ch_length(), position(); + + if ((pos = position(where)) == NULL_POSITION) + pos = ch_length(); + return(find_linenum(pos)); +} diff --git a/usr.bin/more/main.c b/usr.bin/more/main.c new file mode 100644 index 0000000000..f9d7130b12 --- /dev/null +++ b/usr.bin/more/main.c @@ -0,0 +1,367 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1988 Mark Nudleman.\n\ +@(#) Copyright (c) 1988 Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 5.13 (Berkeley) 6/1/90"; +#endif /* not lint */ + +/* + * Entry point, initialization, miscellaneous routines. + */ + +#include <sys/types.h> +#include <sys/file.h> +#include <stdio.h> +#include <less.h> + +int ispipe; +int new_file; +int is_tty; +char *current_file, *previous_file, *current_name, *next_name; +off_t prev_pos; +int any_display; +int scroll; +int ac; +char **av; +int curr_ac; +int quitting; + +extern int file; +extern int cbufs; +extern int errmsgs; + +extern char *tagfile; +extern int tagoption; + +/* + * Edit a new file. + * Filename "-" means standard input. + * No filename means the "current" file, from the command line. + */ +edit(filename) + register char *filename; +{ + extern int errno; + register int f; + register char *m; + off_t initial_pos, position(); + static int didpipe; + char message[100], *p; + char *rindex(), *strerror(), *save(), *bad_file(); + + initial_pos = NULL_POSITION; + if (filename == NULL || *filename == '\0') { + if (curr_ac >= ac) { + error("No current file"); + return(0); + } + filename = save(av[curr_ac]); + } + else if (strcmp(filename, "#") == 0) { + if (*previous_file == '\0') { + error("no previous file"); + return(0); + } + filename = save(previous_file); + initial_pos = prev_pos; + } else + filename = save(filename); + + /* use standard input. */ + if (!strcmp(filename, "-")) { + if (didpipe) { + error("Can view standard input only once"); + return(0); + } + f = 0; + } + else if ((m = bad_file(filename, message, sizeof(message))) != NULL) { + error(m); + free(filename); + return(0); + } + else if ((f = open(filename, O_RDONLY, 0)) < 0) { + (void)sprintf(message, "%s: %s", filename, strerror(errno)); + error(message); + free(filename); + return(0); + } + + if (isatty(f)) { + /* + * Not really necessary to call this an error, + * but if the control terminal (for commands) + * and the input file (for data) are the same, + * we get weird results at best. + */ + error("Can't take input from a terminal"); + if (f > 0) + (void)close(f); + (void)free(filename); + return(0); + } + + /* + * We are now committed to using the new file. + * Close the current input file and set up to use the new one. + */ + if (file > 0) + (void)close(file); + new_file = 1; + if (previous_file != NULL) + free(previous_file); + previous_file = current_file; + current_file = filename; + pos_clear(); + prev_pos = position(TOP); + ispipe = (f == 0); + if (ispipe) { + didpipe = 1; + current_name = "stdin"; + } else + current_name = (p = rindex(filename, '/')) ? p + 1 : filename; + if (curr_ac >= ac) + next_name = NULL; + else + next_name = av[curr_ac + 1]; + file = f; + ch_init(cbufs, 0); + init_mark(); + + if (is_tty) { + int no_display = !any_display; + any_display = 1; + if (no_display && errmsgs > 0) { + /* + * We displayed some messages on error output + * (file descriptor 2; see error() function). + * Before erasing the screen contents, + * display the file name and wait for a keystroke. + */ + error(filename); + } + /* + * Indicate there is nothing displayed yet. + */ + if (initial_pos != NULL_POSITION) + jump_loc(initial_pos); + clr_linenum(); + } + return(1); +} + +/* + * Edit the next file in the command line list. + */ +next_file(n) + int n; +{ + extern int quit_at_eof; + off_t position(); + + if (curr_ac + n >= ac) { + if (quit_at_eof || position(TOP) == NULL_POSITION) + quit(); + error("No (N-th) next file"); + } + else + (void)edit(av[curr_ac += n]); +} + +/* + * Edit the previous file in the command line list. + */ +prev_file(n) + int n; +{ + if (curr_ac - n < 0) + error("No (N-th) previous file"); + else + (void)edit(av[curr_ac -= n]); +} + +/* + * copy a file directly to standard output; used if stdout is not a tty. + * the only processing is to squeeze multiple blank input lines. + */ +static +cat_file() +{ + extern int squeeze; + register int c, empty; + + if (squeeze) { + empty = 0; + while ((c = ch_forw_get()) != EOI) + if (c != '\n') { + putchr(c); + empty = 0; + } + else if (empty < 2) { + putchr(c); + ++empty; + } + } + else while ((c = ch_forw_get()) != EOI) + putchr(c); + flush(); +} + +main(argc, argv) + int argc; + char **argv; +{ + int envargc, argcnt; + char *envargv[2], *getenv(); + + /* + * Process command line arguments and MORE environment arguments. + * Command line arguments override environment arguments. + */ + if (envargv[1] = getenv("MORE")) { + envargc = 2; + envargv[0] = "more"; + envargv[2] = NULL; + (void)option(envargc, envargv); + } + argcnt = option(argc, argv); + argv += argcnt; + argc -= argcnt; + + /* + * Set up list of files to be examined. + */ + ac = argc; + av = argv; + curr_ac = 0; + + /* + * Set up terminal, etc. + */ + is_tty = isatty(1); + if (!is_tty) { + /* + * Output is not a tty. + * Just copy the input file(s) to output. + */ + if (ac < 1) { + (void)edit("-"); + cat_file(); + } else { + do { + (void)edit((char *)NULL); + if (file >= 0) + cat_file(); + } while (++curr_ac < ac); + } + exit(0); + } + + raw_mode(1); + get_term(); + open_getchr(); + init(); + init_signals(1); + + /* select the first file to examine. */ + if (tagoption) { + /* + * A -t option was given; edit the file selected by the + * "tags" search, and search for the proper line in the file. + */ + if (!tagfile || !edit(tagfile) || tagsearch()) + quit(); + } + else if (ac < 1) + (void)edit("-"); /* Standard input */ + else { + /* + * Try all the files named as command arguments. + * We are simply looking for one which can be + * opened without error. + */ + do { + (void)edit((char *)NULL); + } while (file < 0 && ++curr_ac < ac); + } + + if (file >= 0) + commands(); + quit(); + /*NOTREACHED*/ +} + +/* + * Copy a string to a "safe" place + * (that is, to a buffer allocated by malloc). + */ +char * +save(s) + char *s; +{ + char *p, *strcpy(), *malloc(); + + p = malloc((u_int)strlen(s)+1); + if (p == NULL) + { + error("cannot allocate memory"); + quit(); + } + return(strcpy(p, s)); +} + +/* + * Exit the program. + */ +quit() +{ + /* + * Put cursor at bottom left corner, clear the line, + * reset the terminal modes, and exit. + */ + quitting = 1; + lower_left(); + clear_eol(); + deinit(); + flush(); + raw_mode(0); + exit(0); +} diff --git a/usr.bin/more/more.1 b/usr.bin/more/more.1 index e2939fa80a..20a1e021ce 100644 --- a/usr.bin/more/more.1 +++ b/usr.bin/more/more.1 @@ -1,4 +1,5 @@ -.\" Copyright (c) 1980 The Regents of the University of California. +.\" Copyright (c) 1988, 1990 The Regents of the University of California. +.\" Copyright (c) 1988 Mark Nudleman .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -29,328 +30,276 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)more.1 6.6 (Berkeley) 4/18/91 +.\" @(#)more.1 5.15 (Berkeley) 7/29/91 .\" -.TH MORE 1 "April 18, 1991" -.UC 4 -.SH NAME -more, page \- file perusal filter for crt viewing -.SH SYNOPSIS -.B more -[ -.B \-cdflsu -] -[ -.B \-\fIn\fP -] -[ -.B +\fIlinenumber\fP -] -[ -.B +/\fIpattern\fP -] [ name ... ] -.LP -.B page -.I "more options" -.SH DESCRIPTION -.I More -is a filter which allows examination of a continuous text -one screenful at a time on a soft-copy terminal. -It normally pauses after each screenful, printing --More-- -at the bottom of the screen. -If the user then types a carriage return, one more line is displayed. -If the user hits a space, -another screenful is displayed. Other possibilities are enumerated later. -.PP -The command line options are: -.TP -.I \-n -An integer which is the size (in lines) of the window which -.I more -will use instead of the default. -.TP -.B \-c -.I More -will draw each page by beginning at the top of the screen and erasing -each line just before it draws on it. -This avoids scrolling the screen, making it easier to read while -.I more -is writing. -This option will be ignored if the terminal does not have the ability -to clear to the end of a line. -.TP -.B \-d -.I More -will prompt the user with the message "Press -space to continue, \'q\' to quit." at the end of each screenful, -and will respond to subsequent illegal user input by -printing "Press \'h\' for instructions." instead of ringing the bell. -This is useful if -.I more -is being used as a filter in some setting, -such as a class, -where many users may be unsophisticated. -.TP -.B \-f -This causes -.I more -to count logical, rather than screen lines. -That is, long lines are not folded. -This option is recommended if -.I nroff -output is being piped through -.I ul, -since the latter may generate escape sequences. -These escape sequences contain characters which would ordinarily occupy -screen positions, but which do not print when they are sent to the -terminal as part of an escape sequence. -Thus -.I more -may think that lines are longer than they actually are, and fold -lines erroneously. -.TP -.B \-l -Do -not treat ^\&L (form feed) specially. -If this option is not given, -.I more -will pause after any line that contains a ^\&L, as if the end of a -screenful had been reached. -Also, if a file begins with a form feed, the screen will be cleared -before the file is printed. -.TP -.B \-s -Squeeze multiple blank lines from the output, producing only one blank -line. Especially helpful when viewing -.I nroff -output, this option maximizes the useful information present on the screen. -.TP -.B \-u +.Dd July 29, 1991 +.Dt MORE 1 +.Os +.Sh NAME +.Nm more +.Nd file perusal filter for crt viewing +.Sh SYNOPSIS +.Nm more +.Op Fl ceinus +.Op Fl t Ar tag +.Op Fl x Ar tabs +.Op Fl / Ar pattern +.Op Fl # +.Op Ar +.Sh DESCRIPTION +.Nm More +is a filter for paging through text one screenful at a time. It +uses +.Xr termcap 3 +so it can run on a variety of terminals. There is even limited support +for hardcopy terminals. (On a hardcopy terminal, lines which should be +printed at the top of the screen are prefixed with an up-arrow.) +.Ar File +may be a single dash (``-''), implying stdin. +.Sh OPTIONS +Command line options are described below. +Options are also taken from the environment variable +.Ev MORE +(make sure to precede them with a dash (``-'')) but command +line options will override them. +.Bl -tag -width flag +.It Fl c Normally, -.I more -will handle underlining such as produced by -.I nroff -in a manner appropriate to the particular terminal: if the terminal can -perform underlining or has a stand-out mode, -.I more -will output appropriate escape sequences to enable underlining or stand-out -mode for underlined information in the source file. The -.I \-u -option suppresses this processing. -.TP -.B +\fIlinenumber\fP -Start up at \fIlinenumber\fP. -.TP -.B +/\fIpattern\fP -Start up two lines before the line containing the -regular expression \fIpattern\fP. -.PP -If the program is invoked as -.I page, -then the screen is cleared before each screenful is printed (but only -if a full screenful is being printed), and -.I k -\- 1 rather -than -.I k -\- 2 lines are printed in each screenful, where -.I k -is the number of lines the terminal can display. -.PP -.I More -looks in the file -.I /etc/termcap -to determine terminal characteristics, -and to determine the default window size. -On a terminal capable of displaying 24 lines, -the default window size is 22 lines. -.PP -.I More -looks in the environment variable -.I MORE -to pre-set any flags desired. For example, if you prefer to view files using -the -.I \-c -mode of operation, the -.I csh -command -.I "setenv MORE -c" -or the -.I sh -command sequence -.I "MORE='-c' ; export MORE" -would cause all invocations of -.I more , -including invocations by programs such as -.I man -and -.I msgs , -to use this mode. -Normally, the user will place the command sequence which sets up the -.I MORE -environment variable in the -.I .cshrc -or -.I .profile -file. -.PP -If -.I more -is reading from a file, rather than a pipe, then a percentage is displayed -along with the --More-- prompt. -This gives the fraction of the file (in characters, not lines) that has been -read so far. -.PP -Other sequences which may be typed when -.I more -pauses, and their effects, are as follows (\fIi\fP is an optional integer -argument, defaulting to 1) : -.PP -.IP \fIi\|\fP<space> -display -.I i -more lines, (or another screenful if no argument is given) -.PP -.IP ^D -display 11 more lines (a ``scroll''). -If -.I i -is given, then the scroll size is set to \fIi\|\fP. -.PP -.IP d -same as ^D (control-D) -.PP -.IP \fIi\|\fPz -same as typing a space except that \fIi\|\fP, if present, becomes the new -window size. -.PP -.IP \fIi\|\fPs -skip \fIi\|\fP lines and print a screenful of lines -.PP -.IP \fIi\|\fPf -skip \fIi\fP screenfuls and print a screenful of lines -.PP -.IP \fIi\|\fPb -skip back \fIi\fP screenfuls and print a screenful of lines -.PP -.IP \fIi\|\fP^B -same as b -.PP -.IP "q or Q" -Exit from -.I more. -.PP -.IP = -Display the current line number. -.PP -.IP v -Start up the editor -.I vi -at the current line. -.PP -.IP h -Help command; give a description of all the -.I more -commands. -.PP -.IP \fIi\|\fP/expr -search for the \fIi\|\fP-th occurrence of the regular expression \fIexpr.\fP -If there are less than \fIi\fP occurrences of \fIexpr\|\fP, -and the input is a file (rather than a pipe), -then the position in the file remains unchanged. -Otherwise, a screenful is displayed, starting two lines before the place -where the expression was found. -The user's erase and kill characters may be used to edit the regular -expression. -Erasing back past the first column cancels the search command. -.PP -.IP \fIi\|\fPn -search for the \fIi\|\fP-th occurrence of the last regular expression entered. -.PP -.IP \' -(single quote) Go to the point from which the last search started. -If no search has been performed in the current file, this command -goes back to the beginning of the file. -.PP -.IP !command -invoke a shell with \fIcommand\|\fP. -The characters `%' and `!' in "command" are replaced with the -current file name and the previous shell command respectively. -If there is no current file name, `%' is not expanded. -The sequences "\\%" and "\\!" are replaced by "%" and "!" respectively. -.PP -.IP \fIi\|\fP:n -skip to the \fIi\|\fP-th next file given in the command line -(skips to last file if n doesn't make sense) -.PP -.IP \fIi\|\fP:p -skip to the \fIi\|\fP-th previous file given in the command line. -If this command is given in the middle of printing out a -file, then -.I more -goes back to the beginning of the file. If \fIi\fP doesn't make sense, -.I more -skips back to the first file. -If -.I more -is not reading from a file, the bell is rung and nothing else happens. -.PP -.IP :f -display the current file name and line number. -.PP -.IP ":q or :Q" -exit from -.I more -(same as q or Q). -.PP -.IP . -(dot) repeat the previous command. -.PP -The commands take effect immediately, i.e., it is not necessary to -type a carriage return. -Up to the time when the command character itself is given, -the user may hit the line kill character to cancel the numerical -argument being formed. -In addition, the user may hit the erase character to redisplay the ---More--(xx%) message. -.PP -At any time when output is being sent to the terminal, the user can -hit the quit key (normally control\-\\). -.I More -will stop sending output, and will display the usual --More-- -prompt. -The user may then enter one of the above commands in the normal manner. -Unfortunately, some output is lost when this is done, due to the -fact that any characters waiting in the terminal's output queue -are flushed when the quit signal occurs. -.PP -The terminal is set to -.I noecho -mode by this program so that the output can be continuous. -What you type will thus not show on your terminal, except for the / and ! -commands. -.PP -If the standard output is not a teletype, then -.I more -acts just like -.I cat, -except that a header is printed before each file (if there is -more than one). -.PP -.DT -A sample usage of -.I more -in previewing -.I nroff -output would be -.PP - nroff \-ms +2 doc.n | more -s -.SH FILES -.DT -/etc/termcap Terminal data base -.br -/usr/lib/more.help Help file -.SH "SEE ALSO" -csh(1), man(1), msgs(1), script(1), sh(1), environ(7) -.SH BUGS -Skipping backwards is too slow on large files. +.Nm more +will repaint the screen by scrolling from the bottom of the screen. +If the +.Fl c +option is set, when +.Nm more +needs to change the entire display, it will paint from the top line down. +.It Fl e +Normally, if displaying a single file, +.Nm more +exits as soon as it reaches end-of-file. The +.Fl e +option tells more to +exit if it reaches end-of-file twice without an intervening operation. +If the file is shorter than a single screen +.Nm more +will exit at end-of-file regardless. +.It Fl i +The +.Fl i +option causes searches to ignore case; that is, +uppercase and lowercase are considered identical. +.It Fl n +The +.Fl n +flag suppresses line numbers. +The default (to use line numbers) may cause +.Nm more +to run more slowly in some cases, especially with a very large input file. +Suppressing line numbers with the +.Fl n +flag will avoid this problem. +Using line numbers means: the line number will be displayed in the +.Cm = +command, and the +.Cm v +command will pass the current line number to the editor. +.It Fl s +The +.Fl s +option causes +consecutive blank lines to be squeezed into a single blank line. +.It Fl t +The +.Fl t +option, followed immediately by a tag, will edit the file +containing that tag. For more information, see the +.Xr ctags 1 +command. +.It Fl u +By default, +.Nm more +treats backspaces and +.Dv CR-LF +sequences specially. Backspaces which appear +adjacent to an underscore character are displayed as underlined text. +Backspaces which appear between two identical characters are displayed +as emboldened text. +.Dv CR-LF +sequences are compressed to a single linefeed +character. The +.Fl u +option causes backspaces to always be displayed as +control characters, i.e. as the two character sequence ``^H'', and +.Dv CR-LF +to be left alone. +.It Fl x +The +.Fl x +option sets tab stops every +.Ar N +positions. The default for +.Ar N +is 8. +.It Fl / +The +.Fl / +option specifies a string that will be searched for before +each file is displayed. +.Sh COMMANDS +Interactive commands for +.Nm more +are based on +.Xr vi 1 . +Some commands may be preceeded by a decimal number, called N in the +descriptions below. +In the following descriptions, ^X means control-X. +.Pp +.Bl -tag -width Ic +.It Ic h +Help: display a summary of these commands. +If you forget all the other commands, remember this one. +.It Xo +.Ic SPACE +.No or +.Ic f +.No or +.Ic \&^F +.Xc +Scroll forward N lines, default one window. +If N is more than the screen size, only the final screenful is displayed. +.It Ic b No or Ic \&^B +Scroll backward N lines, default one window (see option -z below). +If N is more than the screen size, only the final screenful is displayed. +.It Ic j No or Ic RETURN +Scroll forward N lines, default 1. +The entire N lines are displayed, even if N is more than the screen size. +.It Ic k +Scroll backward N lines, default 1. +The entire N lines are displayed, even if N is more than the screen size. +.It Ic d No or Ic \&^D +Scroll forward N lines, default one half of the screen size. +If N is specified, it becomes the new default for +subsequent d and u commands. +.It Ic u No or Ic \&^U +Scroll backward N lines, default one half of the screen size. +If N is specified, it becomes the new default for +subsequent d and u commands. +.It Ic g +Go to line N in the file, default 1 (beginning of file). +.It Ic G +Go to line N in the file, default the end of the file. +.It Ic p No or Ic \&% +Go to a position N percent into the file. N should be between 0 +and 100. (This works if standard input is being read, but only if +.Nm more +has already read to the end of the file. It is always fast, but +not always useful.) +.It Ic r No or Ic \&^L +Repaint the screen. +.It Ic R +Repaint the screen, discarding any buffered input. +Useful if the file is changing while it is being viewed. +.It Ic m +Followed by any lowercase letter, +marks the current position with that letter. +.It Ic \&' +(Single quote.) +Followed by any lowercase letter, returns to the position which +was previously marked with that letter. +Followed by another single quote, returns to the postion at +which the last "large" movement command was executed, or the +beginning of the file if no such movements have occurred. +All marks are lost when a new file is examined. +.It Ic \&! Ns Ar command +Invoke a shell command. The characters `%', `#', and `!' +are replaced by the current file name, previous filename +and previous shell command, respectively. If there is no +current or previous filename, `%' and `#' are not expanded. +`\%' `\#' and `\!' are replaced by `%', `#' and `!', +respectively. +.It Ic \&/ Ns Ar pattern +Search forward in the file for the N-th line containing the pattern. +N defaults to 1. +The pattern is a regular expression, as recognized by +.Xr ed . +The search starts at the second line displayed. +.It Ic \&? Ns Ar pattern +Search backward in the file for the N-th line containing the pattern. +The search starts at the line immediately before the top line displayed. +.It Ic \&/\&! Ns Ar pattern +Like /, but the search is for the N-th line +which does NOT contain the pattern. +.It Ic \&?\&! Ns Ar pattern +Like ?, but the search is for the N-th line +which does NOT contain the pattern. +.It Ic n +Repeat previous search, for N-th line containing the last pattern +(or +.Tn NOT +containing the last pattern, if the previous search +was /! or ?!). +.It Ic E Ns Op Ar filename +Examine a new file. +If the filename is missing, the "current" file (see the N and P commands +below) from the list of files in the command line is re-examined. +If the filename is a pound sign (#), the previously examined file is +re-examined. +.It Ic N No or Ic \&:n +Examine the next file (from the list of files given in the command line). +If a number N is specified (not to be confused with the command N), +the N-th next file is examined. +.It Ic P No or Ic \&:p +Examine the previous file. +If a number N is specified, the N-th previous file is examined. +.It Ic \&:t +Go to supplied tag. +.It Ic v +Invokes an editor to edit the current file being viewed. +The editor is taken from the environment variable +.Ev EDITOR , +or defaults to +.Xr vi 1 . +.It Ic \&= No or Ic \&^G +These options print out the number of the file currently being displayed +relative to the total number of files there are to display, the current +line number, the current byte number and the total bytes to display, and +what percentage of the file has been displayed. If +.Nm more +is reading from stdin, or the file is shorter than a single screen, some +of these items may not be available. Note, all of these items reference +the first byte of the last line displayed on the screen. +.It Xo +.Ic q +.No or +.Ic \&:q +.No or +.Ic ZZ +.Xc +Exits +.Nm more . +.El +.Sh ENVIRONMENT +.Nm More +utilizes the following environment variables, if they exist: +.Bl -tag -width Fl +.It Ev MORE +This variable may be set with favored options to +.Nm more . +.It Ev EDITOR +Specify default editor. +.It Ev SHELL +Current shell in use (normally set by the shell at login time). +.It Ev TERM +Specifies terminal type, used by more to get the terminal +characteristics necessary to manipulate the screen. +.El +.Sh SEE ALSO +.Xr ctags 1 , +.Xr vi 1 +.Sh AUTHOR +This software is derived from software contributed to Berkeley +by Mark Nudleman. +.Sh HISTORY +The +.Nm more +command appeared in +.Bx 3.0 . diff --git a/usr.bin/more/more.c b/usr.bin/more/more.c deleted file mode 100644 index 863593cab2..0000000000 --- a/usr.bin/more/more.c +++ /dev/null @@ -1,1870 +0,0 @@ -/*- - * Copyright (c) 1980 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1980 The Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)more.c 5.26 (Berkeley) 4/18/91"; -#endif /* not lint */ - -/* -** more.c - General purpose tty output filter and file perusal program -** -** by Eric Shienbrood, UC Berkeley -** -** modified by Geoff Peck, UCB to add underlining, single spacing -** modified by John Foderaro, UCB to add -c and MORE environment variable -*/ - -#include <sys/param.h> -#include <sys/stat.h> -#include <sys/file.h> -#include <signal.h> -#include <errno.h> -#include <sgtty.h> -#include <setjmp.h> -#include <a.out.h> -#include <varargs.h> -#include <stdio.h> -#include <ctype.h> -#include <regexp.h> -#include "pathnames.h" - -#define Fopen(s,m) (Currline = 0,file_pos=0,fopen(s,m)) -#define Ftell(f) file_pos -#define Fseek(f,off) (file_pos=off,fseek(f,off,0)) -#define Getc(f) (++file_pos, getc(f)) -#define Ungetc(c,f) (--file_pos, ungetc(c,f)) - -#define MBIT CBREAK -#define stty(fd,argp) ioctl(fd,TIOCSETN,argp) - -#define TBUFSIZ 1024 -#define LINSIZ 256 -#define ctrl(letter) (letter & 077) -#define RUBOUT '\177' -#define ESC '\033' -#define QUIT '\034' - -struct sgttyb otty, savetty; -long file_pos, file_size; -int fnum, no_intty, no_tty, slow_tty; -int dum_opt, dlines; -void chgwinsz(), end_it(), onquit(), onsusp(); -int nscroll = 11; /* Number of lines scrolled by 'd' */ -int fold_opt = 1; /* Fold long lines */ -int stop_opt = 1; /* Stop after form feeds */ -int ssp_opt = 0; /* Suppress white space */ -int ul_opt = 1; /* Underline as best we can */ -int promptlen; -int Currline; /* Line we are currently at */ -int startup = 1; -int firstf = 1; -int notell = 1; -int docrterase = 0; -int docrtkill = 0; -int bad_so; /* True if overwriting does not turn off standout */ -int inwait, Pause, errors; -int within; /* true if we are within a file, - false if we are between files */ -int hard, dumb, noscroll, hardtabs, clreol, eatnl; -int catch_susp; /* We should catch the SIGTSTP signal */ -char **fnames; /* The list of file names */ -int nfiles; /* Number of files left to process */ -char *shell; /* The name of the shell to use */ -int shellp; /* A previous shell command exists */ -char ch; -jmp_buf restore; -char Line[LINSIZ]; /* Line buffer */ -int Lpp = 24; /* lines per page */ -char *Clear; /* clear screen */ -char *eraseln; /* erase line */ -char *Senter, *Sexit;/* enter and exit standout mode */ -char *ULenter, *ULexit; /* enter and exit underline mode */ -char *chUL; /* underline character */ -char *chBS; /* backspace character */ -char *Home; /* go to home */ -char *cursorm; /* cursor movement */ -char cursorhome[40]; /* contains cursor movement to home */ -char *EodClr; /* clear rest of screen */ -char *tgetstr(); -int Mcol = 80; /* number of columns */ -int Wrap = 1; /* set if automargins */ -int soglitch; /* terminal has standout mode glitch */ -int ulglitch; /* terminal has underline mode glitch */ -int pstate = 0; /* current UL state */ -char *getenv(); -regexp *previous; /* previous regular expression */ -struct { - long chrctr, line; -} context, screen_start; -extern char PC; /* pad character */ -extern short ospeed; - - -main(argc, argv) -int argc; -char *argv[]; -{ - register FILE *f; - register char *s; - register char *p; - register char ch; - register int left; - int prnames = 0; - int initopt = 0; - int srchopt = 0; - int clearit = 0; - int initline; - char initbuf[80]; - FILE *checkf(); - - nfiles = argc; - fnames = argv; - initterm (); - nscroll = Lpp/2 - 1; - if (nscroll <= 0) - nscroll = 1; - if(s = getenv("MORE")) argscan(s); - while (--nfiles > 0) { - if ((ch = (*++fnames)[0]) == '-') { - argscan(*fnames+1); - } - else if (ch == '+') { - s = *fnames; - if (*++s == '/') { - srchopt++; - for (++s, p = initbuf; p < initbuf + 79 && *s != '\0';) - *p++ = *s++; - *p = '\0'; - } - else { - initopt++; - for (initline = 0; *s != '\0'; s++) - if (isdigit (*s)) - initline = initline*10 + *s -'0'; - --initline; - } - } - else break; - } - /* allow clreol only if Home and eraseln and EodClr strings are - * defined, and in that case, make sure we are in noscroll mode - */ - if(clreol) - { - if((Home == NULL) || (*Home == '\0') || - (eraseln == NULL) || (*eraseln == '\0') || - (EodClr == NULL) || (*EodClr == '\0') ) - clreol = 0; - else noscroll = 1; - } - if (dlines == 0) - dlines = Lpp - (noscroll ? 1 : 2); - left = dlines; - if (nfiles > 1) - prnames++; - if (!no_intty && nfiles == 0) { - char *rindex(); - - p = rindex(argv[0], '/'); - fputs("usage: ",stderr); - fputs(p ? p + 1 : argv[0],stderr); - fputs(" [-dfln] [+linenum | +/pattern] name1 name2 ...\n",stderr); - exit(1); - } - else - f = stdin; - if (!no_tty) { - signal(SIGQUIT, onquit); - signal(SIGINT, end_it); - signal(SIGWINCH, chgwinsz); - if (signal (SIGTSTP, SIG_IGN) == SIG_DFL) { - signal(SIGTSTP, onsusp); - catch_susp++; - } - stty (fileno(stderr), &otty); - } - if (no_intty) { - if (no_tty) - copy_file (stdin); - else { - if ((ch = Getc (f)) == '\f') - doclear(); - else { - Ungetc (ch, f); - if (noscroll && (ch != EOF)) { - if (clreol) - home (); - else - doclear (); - } - } - if (srchopt) - { - search (initbuf, stdin, 1); - if (noscroll) - left--; - } - else if (initopt) - skiplns (initline, stdin); - screen (stdin, left); - } - no_intty = 0; - prnames++; - firstf = 0; - } - - while (fnum < nfiles) { - if ((f = checkf (fnames[fnum], &clearit)) != NULL) { - context.line = context.chrctr = 0; - Currline = 0; - if (firstf) setjmp (restore); - if (firstf) { - firstf = 0; - if (srchopt) - { - search (initbuf, f, 1); - if (noscroll) - left--; - } - else if (initopt) - skiplns (initline, f); - } - else if (fnum < nfiles && !no_tty) { - setjmp (restore); - left = command (fnames[fnum], f); - } - if (left != 0) { - if ((noscroll || clearit) && (file_size != LONG_MAX)) - if (clreol) - home (); - else - doclear (); - if (prnames) { - if (bad_so) - erase (0); - if (clreol) - cleareol (); - pr("::::::::::::::"); - if (promptlen > 14) - erase (14); - prtf ("\n"); - if(clreol) cleareol(); - prtf("%s\n", fnames[fnum]); - if(clreol) cleareol(); - prtf("::::::::::::::\n"); - if (left > Lpp - 4) - left = Lpp - 4; - } - if (no_tty) - copy_file (f); - else { - within++; - screen(f, left); - within = 0; - } - } - setjmp (restore); - fflush(stdout); - fclose(f); - screen_start.line = screen_start.chrctr = 0L; - context.line = context.chrctr = 0L; - } - fnum++; - firstf = 0; - } - reset_tty (); - exit(0); -} - -argscan(s) -char *s; -{ - int seen_num = 0; - - while (*s != '\0') { - switch (*s) { - case '0': case '1': case '2': - case '3': case '4': case '5': - case '6': case '7': case '8': - case '9': - if (!seen_num) { - dlines = 0; - seen_num = 1; - } - dlines = dlines*10 + *s - '0'; - break; - case 'd': - dum_opt = 1; - break; - case 'l': - stop_opt = 0; - break; - case 'f': - fold_opt = 0; - break; - case 'p': - noscroll++; - break; - case 'c': - clreol++; - break; - case 's': - ssp_opt = 1; - break; - case 'u': - ul_opt = 0; - break; - } - s++; - } -} - - -/* -** Check whether the file named by fs is an ASCII file which the user may -** access. If it is, return the opened file. Otherwise return NULL. -*/ - -FILE * -checkf (fs, clearfirst) - register char *fs; - int *clearfirst; -{ - struct stat stbuf; - register FILE *f; - char c; - - if (stat (fs, &stbuf) == -1) { - (void)fflush(stdout); - if (clreol) - cleareol (); - perror(fs); - return((FILE *)NULL); - } - if ((stbuf.st_mode & S_IFMT) == S_IFDIR) { - prtf("\n*** %s: directory ***\n\n", fs); - return((FILE *)NULL); - } - if ((f = Fopen(fs, "r")) == NULL) { - (void)fflush(stdout); - perror(fs); - return((FILE *)NULL); - } - if (magic(f, fs)) - return((FILE *)NULL); - c = Getc(f); - *clearfirst = c == '\f'; - Ungetc (c, f); - if ((file_size = stbuf.st_size) == 0) - file_size = LONG_MAX; - return(f); -} - -/* - * magic -- - * check for file magic numbers. This code would best be shared with - * the file(1) program or, perhaps, more should not try and be so smart? - */ -magic(f, fs) - FILE *f; - char *fs; -{ - struct exec ex; - - if (fread(&ex, sizeof(ex), 1, f) == 1) - switch(ex.a_magic) { - case OMAGIC: - case NMAGIC: - case ZMAGIC: - case 0405: - case 0411: - case 0177545: - prtf("\n******** %s: Not a text file ********\n\n", fs); - (void)fclose(f); - return(1); - } - (void)fseek(f, 0L, L_SET); /* rewind() not necessary */ - return(0); -} - -/* -** A real function, for the tputs routine in termlib -*/ - -putch (ch) -char ch; -{ - putchar (ch); -} - -/* -** Print out the contents of the file f, one screenful at a time. -*/ - -#define STOP -10 - -screen (f, num_lines) -register FILE *f; -register int num_lines; -{ - register int c; - register int nchars; - int length; /* length of current line */ - static int prev_len = 1; /* length of previous line */ - - for (;;) { - while (num_lines > 0 && !Pause) { - if ((nchars = getline (f, &length)) == EOF) - { - if (clreol) - clreos(); - return; - } - if (ssp_opt && length == 0 && prev_len == 0) - continue; - prev_len = length; - if (bad_so || (Senter && *Senter == ' ') && promptlen > 0) - erase (0); - /* must clear before drawing line since tabs on some terminals - * do not erase what they tab over. - */ - if (clreol) - cleareol (); - prbuf (Line, length); - if (nchars < promptlen) - erase (nchars); /* erase () sets promptlen to 0 */ - else promptlen = 0; - /* is this needed? - * if (clreol) - * cleareol(); /* must clear again in case we wrapped * - */ - if (nchars < Mcol || !fold_opt) - prbuf("\n", 1); /* will turn off UL if necessary */ - if (nchars == STOP) - break; - num_lines--; - } - if (pstate) { - tputs(ULexit, 1, putch); - pstate = 0; - } - fflush(stdout); - if ((c = Getc(f)) == EOF) - { - if (clreol) - clreos (); - return; - } - - if (Pause && clreol) - clreos (); - Ungetc (c, f); - setjmp (restore); - Pause = 0; startup = 0; - if ((num_lines = command (NULL, f)) == 0) - return; - if (hard && promptlen > 0) - erase (0); - if (noscroll && num_lines >= dlines) - { - if (clreol) - home(); - else - doclear (); - } - screen_start.line = Currline; - screen_start.chrctr = Ftell (f); - } -} - -/* -** Come here if a quit signal is received -*/ - -void -onquit() -{ - signal(SIGQUIT, SIG_IGN); - if (!inwait) { - putchar ('\n'); - if (!startup) { - signal(SIGQUIT, onquit); - longjmp (restore, 1); - } - else - Pause++; - } - else if (!dum_opt && notell) { - write (2, "[Use q or Q to quit]", 20); - promptlen += 20; - notell = 0; - } - signal(SIGQUIT, onquit); -} - -/* -** Come here if a signal for a window size change is received -*/ - -void -chgwinsz() -{ - struct winsize win; - - (void) signal(SIGWINCH, SIG_IGN); - if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) { - if (win.ws_row != 0) { - Lpp = win.ws_row; - nscroll = Lpp/2 - 1; - if (nscroll <= 0) - nscroll = 1; - dlines = Lpp - (noscroll ? 1 : 2); - } - if (win.ws_col != 0) - Mcol = win.ws_col; - } - (void) signal(SIGWINCH, chgwinsz); -} - -/* -** Clean up terminal state and exit. Also come here if interrupt signal received -*/ - -void -end_it () -{ - - reset_tty (); - if (clreol) { - putchar ('\r'); - clreos (); - fflush (stdout); - } - else if (!clreol && (promptlen > 0)) { - kill_line (); - fflush (stdout); - } - else - write (2, "\n", 1); - _exit(0); -} - -copy_file(f) -register FILE *f; -{ - register int c; - - while ((c = getc(f)) != EOF) - putchar(c); -} - -/* Simplified printf function */ - -prtf (fmt, va_alist) -register char *fmt; -va_dcl -{ - va_list ap; - register char ch; - register int ccount; - - ccount = 0; - va_start(ap); - while (*fmt) { - while ((ch = *fmt++) != '%') { - if (ch == '\0') - return (ccount); - ccount++; - putchar (ch); - } - switch (*fmt++) { - case 'd': - ccount += printd (va_arg(ap, int)); - break; - case 's': - ccount += pr (va_arg(ap, char *)); - break; - case '%': - ccount++; - putchar ('%'); - break; - case '0': - return (ccount); - default: - break; - } - } - va_end(ap); - return (ccount); - -} - -/* -** Print an integer as a string of decimal digits, -** returning the length of the print representation. -*/ - -printd (n) -int n; -{ - int a, nchars; - - if (a = n/10) - nchars = 1 + printd(a); - else - nchars = 1; - putchar (n % 10 + '0'); - return (nchars); -} - -/* Put the print representation of an integer into a string */ -static char *sptr; - -scanstr (n, str) -int n; -char *str; -{ - sptr = str; - Sprintf (n); - *sptr = '\0'; -} - -Sprintf (n) -{ - int a; - - if (a = n/10) - Sprintf (a); - *sptr++ = n % 10 + '0'; -} - -static char bell = ctrl('G'); - -strlen (s) -char *s; -{ - register char *p; - - p = s; - while (*p++) - ; - return (p - s - 1); -} - -/* See whether the last component of the path name "path" is equal to the -** string "string" -*/ - -tailequ (path, string) -char *path; -register char *string; -{ - register char *tail; - - tail = path + strlen(path); - while (tail >= path) - if (*(--tail) == '/') - break; - ++tail; - while (*tail++ == *string++) - if (*tail == '\0') - return(1); - return(0); -} - -prompt (filename) -char *filename; -{ - if (clreol) - cleareol (); - else if (promptlen > 0) - kill_line (); - if (!hard) { - promptlen = 8; - if (Senter && Sexit) { - tputs (Senter, 1, putch); - promptlen += (2 * soglitch); - } - if (clreol) - cleareol (); - pr("--More--"); - if (filename != NULL) { - promptlen += prtf ("(Next file: %s)", filename); - } - else if (!no_intty) { - promptlen += prtf ("(%d%%)", (int)((file_pos * 100) / file_size)); - } - if (dum_opt) { - promptlen += pr("[Press space to continue, 'q' to quit.]"); - } - if (Senter && Sexit) - tputs (Sexit, 1, putch); - if (clreol) - clreos (); - fflush(stdout); - } - else - write (2, &bell, 1); - inwait++; -} - -/* -** Get a logical line -*/ - -getline(f, length) -register FILE *f; -int *length; -{ - register int c; - register char *p; - register int column; - static int colflg; - - p = Line; - column = 0; - c = Getc (f); - if (colflg && c == '\n') { - Currline++; - c = Getc (f); - } - while (p < &Line[LINSIZ - 1]) { - if (c == EOF) { - if (p > Line) { - *p = '\0'; - *length = p - Line; - return (column); - } - *length = p - Line; - return (EOF); - } - if (c == '\n') { - Currline++; - break; - } - *p++ = c; - if (c == '\t') - if (!hardtabs || column < promptlen && !hard) { - if (hardtabs && eraseln && !dumb) { - column = 1 + (column | 7); - tputs (eraseln, 1, putch); - promptlen = 0; - } - else { - for (--p; p < &Line[LINSIZ - 1];) { - *p++ = ' '; - if ((++column & 7) == 0) - break; - } - if (column >= promptlen) promptlen = 0; - } - } - else - column = 1 + (column | 7); - else if (c == '\b' && column > 0) - column--; - else if (c == '\r') - column = 0; - else if (c == '\f' && stop_opt) { - p[-1] = '^'; - *p++ = 'L'; - column += 2; - Pause++; - } - else if (c == EOF) { - *length = p - Line; - return (column); - } - else if (c >= ' ' && c != RUBOUT) - column++; - if (column >= Mcol && fold_opt) break; - c = Getc (f); - } - if (column >= Mcol && Mcol > 0) { - if (!Wrap) { - *p++ = '\n'; - } - } - colflg = column == Mcol && fold_opt; - if (colflg && eatnl && Wrap) { - *p++ = '\n'; /* simulate normal wrap */ - } - *length = p - Line; - *p = 0; - return (column); -} - -/* -** Erase the rest of the prompt, assuming we are starting at column col. -*/ - -erase (col) -register int col; -{ - - if (promptlen == 0) - return; - if (hard) { - putchar ('\n'); - } - else { - if (col == 0) - putchar ('\r'); - if (!dumb && eraseln) - tputs (eraseln, 1, putch); - else - for (col = promptlen - col; col > 0; col--) - putchar (' '); - } - promptlen = 0; -} - -/* -** Erase the current line entirely -*/ - -kill_line () -{ - erase (0); - if (!eraseln || dumb) putchar ('\r'); -} - -/* - * force clear to end of line - */ -cleareol() -{ - tputs(eraseln, 1, putch); -} - -clreos() -{ - tputs(EodClr, 1, putch); -} - -/* -** Print string and return number of characters -*/ - -pr(s1) -char *s1; -{ - register char *s; - register char c; - - for (s = s1; c = *s++; ) - putchar(c); - return (s - s1 - 1); -} - - -/* Print a buffer of n characters */ - -prbuf (s, n) -register char *s; -register int n; -{ - register char c; /* next output character */ - register int state; /* next output char's UL state */ -#define wouldul(s,n) ((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_'))) - - while (--n >= 0) - if (!ul_opt) - putchar (*s++); - else { - if (*s == ' ' && pstate == 0 && ulglitch && wouldul(s+1, n-1)) { - s++; - continue; - } - if (state = wouldul(s, n)) { - c = (*s == '_')? s[2] : *s ; - n -= 2; - s += 3; - } else - c = *s++; - if (state != pstate) { - if (c == ' ' && state == 0 && ulglitch && wouldul(s, n-1)) - state = 1; - else - tputs(state ? ULenter : ULexit, 1, putch); - } - if (c != ' ' || pstate == 0 || state != 0 || ulglitch == 0) - putchar(c); - if (state && *chUL) { - pr(chBS); - tputs(chUL, 1, putch); - } - pstate = state; - } -} - -/* -** Clear the screen -*/ - -doclear() -{ - if (Clear && !hard) { - tputs(Clear, 1, putch); - - /* Put out carriage return so that system doesn't - ** get confused by escape sequences when expanding tabs - */ - putchar ('\r'); - promptlen = 0; - } -} - -/* - * Go to home position - */ -home() -{ - tputs(Home,1,putch); -} - -static int lastcmd, lastarg, lastp; -static int lastcolon; -char shell_line[132]; - -/* -** Read a command and do it. A command consists of an optional integer -** argument followed by the command character. Return the number of lines -** to display in the next screenful. If there is nothing more to display -** in the current file, zero is returned. -*/ - -command (filename, f) -char *filename; -register FILE *f; -{ - register int nlines; - register int retval; - register char c; - char colonch; - FILE *helpf; - int done; - char comchar, cmdbuf[80], *p; - -#define ret(val) retval=val;done++;break - - done = 0; - if (!errors) - prompt (filename); - else - errors = 0; - if (MBIT == RAW && slow_tty) { - otty.sg_flags |= MBIT; - stty(fileno(stderr), &otty); - } - for (;;) { - nlines = number (&comchar); - lastp = colonch = 0; - if (comchar == '.') { /* Repeat last command */ - lastp++; - comchar = lastcmd; - nlines = lastarg; - if (lastcmd == ':') - colonch = lastcolon; - } - lastcmd = comchar; - lastarg = nlines; - if (comchar == otty.sg_erase) { - kill_line (); - prompt (filename); - continue; - } - switch (comchar) { - case ':': - retval = colon (filename, colonch, nlines); - if (retval >= 0) - done++; - break; - case 'b': - case ctrl('B'): - { - register int initline; - - if (no_intty) { - write(2, &bell, 1); - return (-1); - } - - if (nlines == 0) nlines++; - - putchar ('\r'); - erase (0); - prtf ("\n"); - if (clreol) - cleareol (); - prtf ("...back %d page", nlines); - if (nlines > 1) - pr ("s\n"); - else - pr ("\n"); - - if (clreol) - cleareol (); - pr ("\n"); - - initline = Currline - dlines * (nlines + 1); - if (! noscroll) - --initline; - if (initline < 0) initline = 0; - Fseek(f, 0L); - Currline = 0; /* skiplns() will make Currline correct */ - skiplns(initline, f); - if (! noscroll) { - ret(dlines + 1); - } - else { - ret(dlines); - } - } - case ' ': - case 'z': - if (nlines == 0) nlines = dlines; - else if (comchar == 'z') dlines = nlines; - ret (nlines); - case 'd': - case ctrl('D'): - if (nlines != 0) nscroll = nlines; - ret (nscroll); - case 'q': - case 'Q': - end_it (); - case 's': - case 'f': - if (nlines == 0) nlines++; - if (comchar == 'f') - nlines *= dlines; - putchar ('\r'); - erase (0); - prtf ("\n"); - if (clreol) - cleareol (); - prtf ("...skipping %d line", nlines); - if (nlines > 1) - pr ("s\n"); - else - pr ("\n"); - - if (clreol) - cleareol (); - pr ("\n"); - - while (nlines > 0) { - while ((c = Getc (f)) != '\n') - if (c == EOF) { - retval = 0; - done++; - goto endsw; - } - Currline++; - nlines--; - } - ret (dlines); - case '\n': - if (nlines != 0) - dlines = nlines; - else - nlines = 1; - ret (nlines); - case '\f': - if (!no_intty) { - doclear (); - Fseek (f, screen_start.chrctr); - Currline = screen_start.line; - ret (dlines); - } - else { - write (2, &bell, 1); - break; - } - case '\'': - if (!no_intty) { - kill_line (); - pr ("\n***Back***\n\n"); - Fseek (f, context.chrctr); - Currline = context.line; - ret (dlines); - } - else { - write (2, &bell, 1); - break; - } - case '=': - kill_line (); - promptlen = printd (Currline); - fflush (stdout); - break; - case 'n': - lastp++; - case '/': - if (nlines == 0) nlines++; - kill_line (); - pr ("/"); - promptlen = 1; - fflush (stdout); - if (lastp) { - write (2,"\r", 1); - search (NULL, f, nlines); /* Use previous r.e. */ - } - else { - ttyin (cmdbuf, 78, '/'); - write (2, "\r", 1); - search (cmdbuf, f, nlines); - } - ret (dlines-1); - case '!': - do_shell (filename); - break; - case '?': - case 'h': - if ((helpf = fopen (HELPFILE, "r")) == NULL) - error ("Can't open help file"); - if (noscroll) doclear (); - copy_file (helpf); - fclose (helpf); - prompt (filename); - break; - case 'v': /* This case should go right before default */ - if (!no_intty) { - kill_line (); - cmdbuf[0] = '+'; - scanstr (Currline - dlines < 0 ? 0 - : Currline - (dlines + 1) / 2, &cmdbuf[1]); - pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]); - execute (filename, _PATH_VI, "vi", cmdbuf, fnames[fnum], 0); - break; - } - default: - if (dum_opt) { - kill_line (); - if (Senter && Sexit) { - tputs (Senter, 1, putch); - promptlen = pr ("[Press 'h' for instructions.]") + (2 * soglitch); - tputs (Sexit, 1, putch); - } - else - promptlen = pr ("[Press 'h' for instructions.]"); - fflush (stdout); - } - else - write (2, &bell, 1); - break; - } - if (done) break; - } - putchar ('\r'); -endsw: - inwait = 0; - notell++; - if (MBIT == RAW && slow_tty) { - otty.sg_flags &= ~MBIT; - stty(fileno(stderr), &otty); - } - return (retval); -} - -char ch; - -/* - * Execute a colon-prefixed command. - * Returns <0 if not a command that should cause - * more of the file to be printed. - */ - -colon (filename, cmd, nlines) -char *filename; -int cmd; -int nlines; -{ - if (cmd == 0) - ch = readch (); - else - ch = cmd; - lastcolon = ch; - switch (ch) { - case 'f': - kill_line (); - if (!no_intty) - promptlen = prtf ("\"%s\" line %d", fnames[fnum], Currline); - else - promptlen = prtf ("[Not a file] line %d", Currline); - fflush (stdout); - return (-1); - case 'n': - if (nlines == 0) { - if (fnum >= nfiles - 1) - end_it (); - nlines++; - } - putchar ('\r'); - erase (0); - skipf (nlines); - return (0); - case 'p': - if (no_intty) { - write (2, &bell, 1); - return (-1); - } - putchar ('\r'); - erase (0); - if (nlines == 0) - nlines++; - skipf (-nlines); - return (0); - case '!': - do_shell (filename); - return (-1); - case 'q': - case 'Q': - end_it (); - default: - write (2, &bell, 1); - return (-1); - } -} - -/* -** Read a decimal number from the terminal. Set cmd to the non-digit which -** terminates the number. -*/ - -number(cmd) -char *cmd; -{ - register int i; - - i = 0; ch = otty.sg_kill; - for (;;) { - ch = readch (); - if (ch >= '0' && ch <= '9') - i = i*10 + ch - '0'; - else if (ch == otty.sg_kill) - i = 0; - else { - *cmd = ch; - break; - } - } - return (i); -} - -do_shell (filename) -char *filename; -{ - char cmdbuf[80]; - - kill_line (); - pr ("!"); - fflush (stdout); - promptlen = 1; - if (lastp) - pr (shell_line); - else { - ttyin (cmdbuf, 78, '!'); - if (expand (shell_line, cmdbuf)) { - kill_line (); - promptlen = prtf ("!%s", shell_line); - } - } - fflush (stdout); - write (2, "\n", 1); - promptlen = 0; - shellp = 1; - execute (filename, shell, shell, "-c", shell_line, 0); -} - -/* -** Search for nth ocurrence of regular expression contained in buf in the file -*/ - -search (buf, file, n) -char buf[]; -FILE *file; -register int n; -{ - long startline = Ftell (file); - register long line1 = startline; - register long line2 = startline; - register long line3 = startline; - register int lncount; - int saveln, rv; - regexp *s; - - context.line = saveln = Currline; - context.chrctr = startline; - lncount = 0; - if (buf) - previous = s = regcomp (buf); - else - s = previous; - while (!feof (file)) { - line3 = line2; - line2 = line1; - line1 = Ftell (file); - rdline (file); - lncount++; - if ((rv = regexec (s, Line)) == 1) - if (--n == 0) { - if (lncount > 3 || (lncount > 1 && no_intty)) - { - pr ("\n"); - if (clreol) - cleareol (); - pr("...skipping\n"); - } - if (!no_intty) { - Currline -= (lncount >= 3 ? 3 : lncount); - Fseek (file, line3); - if (noscroll) - if (clreol) { - home (); - cleareol (); - } - else - doclear (); - } - else { - kill_line (); - if (noscroll) - if (clreol) { - home (); - cleareol (); - } - else - doclear (); - pr (Line); - putchar ('\n'); - } - break; - } - } - free(s); - if (feof (file)) { - if (!no_intty) { - /* file->_flag &= ~_IOEOF; /* why doesn't fseek do this ??!!??! */ - Currline = saveln; - Fseek (file, startline); - } - else { - pr ("\nPattern not found\n"); - end_it (); - } - error ("Pattern not found"); - } -} - -/*VARARGS2*/ -execute (filename, cmd, va_alist) -char *filename; -char *cmd; -va_dcl -{ - int id; - int n; - va_list argp; - - fflush (stdout); - reset_tty (); - for (n = 10; (id = fork ()) < 0 && n > 0; n--) - sleep (5); - if (id == 0) { - if (!isatty(0)) { - close(0); - open("/dev/tty", 0); - } - va_start(argp); - execv (cmd, argp); - write (2, "exec failed\n", 12); - exit (1); - va_end(argp); /* balance {}'s for some UNIX's */ - } - if (id > 0) { - signal (SIGINT, SIG_IGN); - signal (SIGQUIT, SIG_IGN); - if (catch_susp) - signal(SIGTSTP, SIG_DFL); - while (wait(0) > 0); - signal (SIGINT, end_it); - signal (SIGQUIT, onquit); - if (catch_susp) - signal(SIGTSTP, onsusp); - } else - write(2, "can't fork\n", 11); - set_tty (); - pr ("------------------------\n"); - prompt (filename); -} -/* -** Skip n lines in the file f -*/ - -skiplns (n, f) -register int n; -register FILE *f; -{ - register char c; - - while (n > 0) { - while ((c = Getc (f)) != '\n') - if (c == EOF) - return; - n--; - Currline++; - } -} - -/* -** Skip nskip files in the file list (from the command line). Nskip may be -** negative. -*/ - -skipf (nskip) -register int nskip; -{ - if (nskip == 0) return; - if (nskip > 0) { - if (fnum + nskip > nfiles - 1) - nskip = nfiles - fnum - 1; - } - else if (within) - ++fnum; - fnum += nskip; - if (fnum < 0) - fnum = 0; - pr ("\n...Skipping "); - pr ("\n"); - if (clreol) - cleareol (); - pr ("...Skipping "); - pr (nskip > 0 ? "to file " : "back to file "); - pr (fnames[fnum]); - pr ("\n"); - if (clreol) - cleareol (); - pr ("\n"); - --fnum; -} - -/*----------------------------- Terminal I/O -------------------------------*/ - -initterm () -{ - char buf[TBUFSIZ]; - static char clearbuf[TBUFSIZ]; - char *clearptr, *padstr; - int ldisc; - int lmode; - char *term; - int tgrp; - struct winsize win; - -retry: - if (!(no_tty = ioctl(fileno(stdout), TIOCGETP, &otty))) { - if (ioctl(fileno(stdout), TIOCLGET, &lmode) < 0) { - perror("TIOCLGET"); - exit(1); - } - docrterase = ((lmode & LCRTERA) != 0); - docrtkill = ((lmode & LCRTKIL) != 0); - /* - * Wait until we're in the foreground before we save the - * the terminal modes. - */ - if (ioctl(fileno(stdout), TIOCGPGRP, &tgrp) < 0) { - perror("TIOCGPGRP"); - exit(1); - } - if (tgrp != getpgrp(0)) { - kill(0, SIGTTOU); - goto retry; - } - if ((term = getenv("TERM")) == 0 || tgetent(buf, term) <= 0) { - dumb++; ul_opt = 0; - } - else { - if (ioctl(fileno(stdout), TIOCGWINSZ, &win) < 0) { - Lpp = tgetnum("li"); - Mcol = tgetnum("co"); - } else { - if ((Lpp = win.ws_row) == 0) - Lpp = tgetnum("li"); - if ((Mcol = win.ws_col) == 0) - Mcol = tgetnum("co"); - } - if ((Lpp <= 0) || tgetflag("hc")) { - hard++; /* Hard copy terminal */ - Lpp = 24; - } - if (tgetflag("xn")) - eatnl++; /* Eat newline at last column + 1; dec, concept */ - if (Mcol <= 0) - Mcol = 80; - - if (tailequ (fnames[0], "page") || !hard && tgetflag("ns")) - noscroll++; - Wrap = tgetflag("am"); - bad_so = tgetflag ("xs"); - clearptr = clearbuf; - eraseln = tgetstr("ce",&clearptr); - Clear = tgetstr("cl", &clearptr); - Senter = tgetstr("so", &clearptr); - Sexit = tgetstr("se", &clearptr); - if ((soglitch = tgetnum("sg")) < 0) - soglitch = 0; - - /* - * Set up for underlining: some terminals don't need it; - * others have start/stop sequences, still others have an - * underline char sequence which is assumed to move the - * cursor forward one character. If underline sequence - * isn't available, settle for standout sequence. - */ - - if (tgetflag("ul") || tgetflag("os")) - ul_opt = 0; - if ((chUL = tgetstr("uc", &clearptr)) == NULL ) - chUL = ""; - if (((ULenter = tgetstr("us", &clearptr)) == NULL || - (ULexit = tgetstr("ue", &clearptr)) == NULL) && !*chUL) { - if ((ULenter = Senter) == NULL || (ULexit = Sexit) == NULL) { - ULenter = ""; - ULexit = ""; - } else - ulglitch = soglitch; - } else { - if ((ulglitch = tgetnum("ug")) < 0) - ulglitch = 0; - } - - if (padstr = tgetstr("pc", &clearptr)) - PC = *padstr; - Home = tgetstr("ho",&clearptr); - if (Home == 0 || *Home == '\0') - { - if ((cursorm = tgetstr("cm", &clearptr)) != NULL) { - strcpy(cursorhome, tgoto(cursorm, 0, 0)); - Home = cursorhome; - } - } - EodClr = tgetstr("cd", &clearptr); - if ((chBS = tgetstr("bc", &clearptr)) == NULL) - chBS = "\b"; - - } - if ((shell = getenv("SHELL")) == NULL) - shell = "/bin/sh"; - } - no_intty = ioctl(fileno(stdin), TIOCGETP, &otty); - ioctl(fileno(stderr), TIOCGETP, &otty); - savetty = otty; - ospeed = otty.sg_ospeed; - slow_tty = ospeed < B1200; - hardtabs = (otty.sg_flags & TBDELAY) != XTABS; - if (!no_tty) { - otty.sg_flags &= ~ECHO; - if (MBIT == CBREAK || !slow_tty) - otty.sg_flags |= MBIT; - } -} - -readch () -{ - char ch; - extern int errno; - - errno = 0; - if (read (2, &ch, 1) <= 0) - if (errno != EINTR) - end_it(); - else - ch = otty.sg_kill; - return (ch); -} - -static char BS = '\b'; -static char *BSB = "\b \b"; -static char CARAT = '^'; -#define ERASEONECHAR \ - if (docrterase) \ - write (2, BSB, sizeof(BSB)); \ - else \ - write (2, &BS, sizeof(BS)); - -ttyin (buf, nmax, pchar) -char buf[]; -register int nmax; -char pchar; -{ - register char *sptr; - register char ch; - register int slash = 0; - int maxlen; - char cbuf; - - sptr = buf; - maxlen = 0; - while (sptr - buf < nmax) { - if (promptlen > maxlen) maxlen = promptlen; - ch = readch (); - if (ch == '\\') { - slash++; - } - else if ((ch == otty.sg_erase) && !slash) { - if (sptr > buf) { - --promptlen; - ERASEONECHAR - --sptr; - if ((*sptr < ' ' && *sptr != '\n') || *sptr == RUBOUT) { - --promptlen; - ERASEONECHAR - } - continue; - } - else { - if (!eraseln) promptlen = maxlen; - longjmp (restore, 1); - } - } - else if ((ch == otty.sg_kill) && !slash) { - if (hard) { - show (ch); - putchar ('\n'); - putchar (pchar); - } - else { - putchar ('\r'); - putchar (pchar); - if (eraseln) - erase (1); - else if (docrtkill) - while (promptlen-- > 1) - write (2, BSB, sizeof(BSB)); - promptlen = 1; - } - sptr = buf; - fflush (stdout); - continue; - } - if (slash && (ch == otty.sg_kill || ch == otty.sg_erase)) { - ERASEONECHAR - --sptr; - } - if (ch != '\\') - slash = 0; - *sptr++ = ch; - if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) { - ch += ch == RUBOUT ? -0100 : 0100; - write (2, &CARAT, 1); - promptlen++; - } - cbuf = ch; - if (ch != '\n' && ch != ESC) { - write (2, &cbuf, 1); - promptlen++; - } - else - break; - } - *--sptr = '\0'; - if (!eraseln) promptlen = maxlen; - if (sptr - buf >= nmax - 1) - error ("Line too long"); -} - -expand (outbuf, inbuf) -char *outbuf; -char *inbuf; -{ - register char *instr; - register char *outstr; - register char ch; - char temp[200]; - int changed = 0; - - instr = inbuf; - outstr = temp; - while ((ch = *instr++) != '\0') - switch (ch) { - case '%': - if (!no_intty) { - strcpy (outstr, fnames[fnum]); - outstr += strlen (fnames[fnum]); - changed++; - } - else - *outstr++ = ch; - break; - case '!': - if (!shellp) - error ("No previous command to substitute for"); - strcpy (outstr, shell_line); - outstr += strlen (shell_line); - changed++; - break; - case '\\': - if (*instr == '%' || *instr == '!') { - *outstr++ = *instr++; - break; - } - default: - *outstr++ = ch; - } - *outstr++ = '\0'; - strcpy (outbuf, temp); - return (changed); -} - -show (ch) -register char ch; -{ - char cbuf; - - if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) { - ch += ch == RUBOUT ? -0100 : 0100; - write (2, &CARAT, 1); - promptlen++; - } - cbuf = ch; - write (2, &cbuf, 1); - promptlen++; -} - -error (mess) -char *mess; -{ - if (clreol) - cleareol (); - else - kill_line (); - promptlen += strlen (mess); - if (Senter && Sexit) { - tputs (Senter, 1, putch); - pr(mess); - tputs (Sexit, 1, putch); - } - else - pr (mess); - fflush(stdout); - errors++; - longjmp (restore, 1); -} - -void -regerror (mess) -const char *mess; -{ - if (clreol) - cleareol (); - else - kill_line (); - promptlen += strlen (mess); - if (Senter && Sexit) { - tputs (Senter, 1, putch); - pr(mess); - tputs (Sexit, 1, putch); - } - else - pr (mess); - fflush(stdout); - errors++; - longjmp (restore, 1); -} - - -set_tty () -{ - otty.sg_flags |= MBIT; - otty.sg_flags &= ~ECHO; - stty(fileno(stderr), &otty); -} - -reset_tty () -{ - if (no_tty) - return; - if (pstate) { - tputs(ULexit, 1, putch); - fflush(stdout); - pstate = 0; - } - otty.sg_flags |= ECHO; - otty.sg_flags &= ~MBIT; - stty(fileno(stderr), &savetty); -} - -rdline (f) -register FILE *f; -{ - register char c; - register char *p; - - p = Line; - while ((c = Getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1) - *p++ = c; - if (c == '\n') - Currline++; - *p = '\0'; -} - -/* Come here when we get a suspend signal from the terminal */ - -void -onsusp () -{ - /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */ - signal(SIGTTOU, SIG_IGN); - reset_tty (); - fflush (stdout); - signal(SIGTTOU, SIG_DFL); - /* Send the TSTP signal to suspend our process group */ - signal(SIGTSTP, SIG_DFL); - sigsetmask(0); - kill (0, SIGTSTP); - /* Pause for station break */ - - /* We're back */ - signal (SIGTSTP, onsusp); - set_tty (); - if (inwait) - longjmp (restore, 1); -} diff --git a/usr.bin/more/more.help b/usr.bin/more/more.help index 1ced5c9d68..2c0f54c4bb 100644 --- a/usr.bin/more/more.help +++ b/usr.bin/more/more.help @@ -1,24 +1,41 @@ + Commands flagged with an asterisk (``*'') may be preceeded by a number. + Commands of the form ``^X'' are control characters, i.e. control-X. + + h Display this help. + + f, ^F, SPACE * Forward N lines, default one screen. + b, ^B * Backward N lines, default one screen. + j, CR * Forward N lines, default 1 line. + k * Backward N lines, default 1 line. + d, ^D * Forward N lines, default half screen or last N to d/u. + u, ^U * Backward N lines, default half screen or last N to d/u. + g * Go to line N, default 1. + G * Go to line N, default the end of the file. + p, % * Position to N percent into the file. + + r, ^L Repaint screen. + R Repaint screen, discarding buffered input. + + m[a-z] Mark the current position with the supplied letter. + '[a-z] Return to the position previously marked by this letter. + '' Return to previous position. + + /pattern * Search forward for N-th line containing the pattern. + /!pattern * Search forward for N-th line NOT containing the pattern. + ?pattern * Search backward for N-th line containing the pattern. + ?!pattern * Search backward for N-th line NOT containing the pattern. + n * Repeat previous search (for N-th occurence). + + !command Execute command in a subshell. + :!command Execute command in a subshell. + :a Display the list of files. + E [file] Examine a new file. + :n, N * Examine the next file. + :p, P * Examine the previous file. + :t [tag] Examine the tag. + v Run an editor on the current file. + + =, ^G Print current file name and stats. + + q, :q, or ZZ Exit. -Most commands optionally preceded by integer argument k. Defaults in brackets. -Star (*) indicates argument becomes new default. -------------------------------------------------------------------------------- -<space> Display next k lines of text [current screen size] -z Display next k lines of text [current screen size]* -<return> Display next k lines of text [1]* -d or ctrl-D Scroll k lines [current scroll size, initially 11]* -q or Q or <interrupt> Exit from more -s Skip forward k lines of text [1] -f Skip forward k screenfuls of text [1] -b or ctrl-B Skip backwards k screenfuls of text [1] -' Go to place where previous search started -= Display current line number -/<regular expression> Search for kth occurrence of regular expression [1] -n Search for kth occurrence of last r.e [1] -!<cmd> or :!<cmd> Execute <cmd> in a subshell -v Start up /usr/ucb/vi at current line -ctrl-L Redraw screen -:n Go to kth next file [1] -:p Go to kth previous file [1] -:f Display current file name and line number -. Repeat previous command -------------------------------------------------------------------------------- diff --git a/usr.bin/more/option.c b/usr.bin/more/option.c new file mode 100644 index 0000000000..708df99265 --- /dev/null +++ b/usr.bin/more/option.c @@ -0,0 +1,128 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)option.c 5.11 (Berkeley) 6/1/90"; +#endif /* not lint */ + +#include <stdio.h> +#include <less.h> + +int top_scroll; /* Repaint screen from top */ +int bs_mode; /* How to process backspaces */ +int caseless; /* Do "caseless" searches */ +int cbufs = 10; /* Current number of buffers */ +int linenums = 1; /* Use line numbers */ +int quit_at_eof; +int squeeze; /* Squeeze multiple blank lines into one */ +int tabstop = 8; /* Tab settings */ +int tagoption; + +char *firstsearch; +extern int sc_height; + +option(argc, argv) + int argc; + char **argv; +{ + extern char *optarg; + extern int optind; + static int sc_window_set = 0; + int ch; + char *p; + + /* backward compatible processing for "+/search" */ + char **a; + for (a = argv; *a; ++a) + if ((*a)[0] == '+' && (*a)[1] == '/') + (*a)[0] = '-'; + + optind = 1; /* called twice, re-init getopt. */ + while ((ch = getopt(argc, argv, "0123456789/:ceinst:ux:f")) != EOF) + switch((char)ch) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + /* + * kludge: more was originally designed to take + * a number after a dash. + */ + if (!sc_window_set) { + p = argv[optind - 1]; + if (p[0] == '-' && p[1] == ch && !p[2]) + sc_height = atoi(++p); + else + sc_height = atoi(argv[optind] + 1); + sc_window_set = 1; + } + break; + case '/': + firstsearch = optarg; + break; + case 'c': + top_scroll = 1; + break; + case 'e': + quit_at_eof = 1; + break; + case 'i': + caseless = 1; + break; + case 'n': + linenums = 0; + break; + case 's': + squeeze = 1; + break; + case 't': + tagoption = 1; + findtag(optarg); + break; + case 'u': + bs_mode = 1; + break; + case 'x': + tabstop = atoi(optarg); + if (tabstop <= 0) + tabstop = 8; + break; + case 'f': /* ignore -f, compatability with old more */ + break; + case '?': + default: + fprintf(stderr, + "usage: more [-ceinus] [-t tag] [-x tabs] [-/ pattern] [-#] [file ...]\n"); + exit(1); + } + return(optind); +} diff --git a/usr.bin/more/os.c b/usr.bin/more/os.c new file mode 100644 index 0000000000..10be985f0d --- /dev/null +++ b/usr.bin/more/os.c @@ -0,0 +1,283 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)os.c 5.12 (Berkeley) 3/1/91"; +#endif /* not lint */ + +/* + * Operating system dependent routines. + * + * Most of the stuff in here is based on Unix, but an attempt + * has been made to make things work on other operating systems. + * This will sometimes result in a loss of functionality, unless + * someone rewrites code specifically for the new operating system. + * + * The makefile provides defines to decide whether various + * Unix features are present. + */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <signal.h> +#include <setjmp.h> +#include <stdio.h> +#include <less.h> +#include "pathnames.h" + +int reading; + +extern int screen_trashed; + +static jmp_buf read_label; + +/* + * Pass the specified command to a shell to be executed. + * Like plain "system()", but handles resetting terminal modes, etc. + */ +lsystem(cmd) + char *cmd; +{ + int inp; + char cmdbuf[256]; + char *shell, *getenv(); + + /* + * Print the command which is to be executed, + * unless the command starts with a "-". + */ + if (cmd[0] == '-') + cmd++; + else + { + lower_left(); + clear_eol(); + putstr("!"); + putstr(cmd); + putstr("\n"); + } + + /* + * De-initialize the terminal and take out of raw mode. + */ + deinit(); + flush(); + raw_mode(0); + + /* + * Restore signals to their defaults. + */ + init_signals(0); + + /* + * Force standard input to be the terminal, "/dev/tty", + * even if less's standard input is coming from a pipe. + */ + inp = dup(0); + (void)close(0); + if (open(_PATH_TTY, O_RDONLY, 0) < 0) + (void)dup(inp); + + /* + * Pass the command to the system to be executed. + * If we have a SHELL environment variable, use + * <$SHELL -c "command"> instead of just <command>. + * If the command is empty, just invoke a shell. + */ + if ((shell = getenv("SHELL")) != NULL && *shell != '\0') + { + if (*cmd == '\0') + cmd = shell; + else + { + (void)sprintf(cmdbuf, "%s -c \"%s\"", shell, cmd); + cmd = cmdbuf; + } + } + if (*cmd == '\0') + cmd = "sh"; + + (void)system(cmd); + + /* + * Restore standard input, reset signals, raw mode, etc. + */ + (void)close(0); + (void)dup(inp); + (void)close(inp); + + init_signals(1); + raw_mode(1); + init(); + screen_trashed = 1; +#if defined(SIGWINCH) || defined(SIGWIND) + /* + * Since we were ignoring window change signals while we executed + * the system command, we must assume the window changed. + */ + winch(); +#endif +} + +/* + * Like read() system call, but is deliberately interruptable. + * A call to intread() from a signal handler will interrupt + * any pending iread(). + */ +iread(fd, buf, len) + int fd; + char *buf; + int len; +{ + register int n; + + if (setjmp(read_label)) + /* + * We jumped here from intread. + */ + return (READ_INTR); + + flush(); + reading = 1; + n = read(fd, buf, len); + reading = 0; + if (n < 0) + return (-1); + return (n); +} + +intread() +{ + (void)sigsetmask(0L); + longjmp(read_label, 1); +} + +/* + * Expand a filename, substituting any environment variables, etc. + * The implementation of this is necessarily very operating system + * dependent. This implementation is unabashedly only for Unix systems. + */ +FILE *popen(); + +char * +glob(filename) + char *filename; +{ + FILE *f; + char *p; + int ch; + char *cmd, *malloc(), *getenv(); + static char buffer[MAXPATHLEN]; + + if (filename[0] == '#') + return (filename); + + /* + * We get the shell to expand the filename for us by passing + * an "echo" command to the shell and reading its output. + */ + p = getenv("SHELL"); + if (p == NULL || *p == '\0') + { + /* + * Read the output of <echo filename>. + */ + cmd = malloc((u_int)(strlen(filename)+8)); + if (cmd == NULL) + return (filename); + (void)sprintf(cmd, "echo \"%s\"", filename); + } else + { + /* + * Read the output of <$SHELL -c "echo filename">. + */ + cmd = malloc((u_int)(strlen(p)+12)); + if (cmd == NULL) + return (filename); + (void)sprintf(cmd, "%s -c \"echo %s\"", p, filename); + } + + if ((f = popen(cmd, "r")) == NULL) + return (filename); + free(cmd); + + for (p = buffer; p < &buffer[sizeof(buffer)-1]; p++) + { + if ((ch = getc(f)) == '\n' || ch == EOF) + break; + *p = ch; + } + *p = '\0'; + (void)pclose(f); + return(buffer); +} + +char * +bad_file(filename, message, len) + char *filename, *message; + u_int len; +{ + extern int errno; + struct stat statbuf; + char *strcat(), *strerror(); + + if (stat(filename, &statbuf) < 0) { + (void)sprintf(message, "%s: %s", filename, strerror(errno)); + return(message); + } + if ((statbuf.st_mode & S_IFMT) == S_IFDIR) { + static char is_dir[] = " is a directory"; + + strtcpy(message, filename, (int)(len-sizeof(is_dir)-1)); + (void)strcat(message, is_dir); + return(message); + } + return((char *)NULL); +} + +/* + * Copy a string, truncating to the specified length if necessary. + * Unlike strncpy(), the resulting string is guaranteed to be null-terminated. + */ +strtcpy(to, from, len) + char *to, *from; + int len; +{ + char *strncpy(); + + (void)strncpy(to, from, (int)len); + to[len-1] = '\0'; +} + diff --git a/usr.bin/more/output.c b/usr.bin/more/output.c new file mode 100644 index 0000000000..0f805036cf --- /dev/null +++ b/usr.bin/more/output.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)output.c 5.10 (Berkeley) 7/24/91"; +#endif /* not lint */ + +/* + * High level routines dealing with the output to the screen. + */ + +#include <stdio.h> +#include <less.h> + +int errmsgs; /* Count of messages displayed by error() */ + +extern int sigs; +extern int sc_width, sc_height; +extern int ul_width, ue_width; +extern int so_width, se_width; +extern int bo_width, be_width; +extern int tabstop; +extern int screen_trashed; +extern int any_display; +extern char *line; + +/* display the line which is in the line buffer. */ +put_line() +{ + register char *p; + register int c; + register int column; + extern int auto_wrap, ignaw; + + if (sigs) + { + /* + * Don't output if a signal is pending. + */ + screen_trashed = 1; + return; + } + + if (line == NULL) + line = ""; + + column = 0; + for (p = line; *p != '\0'; p++) + { + switch (c = *p) + { + case UL_CHAR: + ul_enter(); + column += ul_width; /* +1; XXX */ + break; + case UE_CHAR: + ul_exit(); + column += ue_width; + break; + case BO_CHAR: + bo_enter(); + column += bo_width; /* +1; XXX */ + break; + case BE_CHAR: + bo_exit(); + column += be_width; + break; + case '\t': + do + { + putchr(' '); + column++; + } while ((column % tabstop) != 0); + break; + case '\b': + putbs(); + column--; + break; + default: + if (c & 0200) + { + /* + * Control characters arrive here as the + * normal character [CARAT_CHAR(c)] with + * the 0200 bit set. See pappend(). + */ + putchr('^'); + putchr(c & 0177); + column += 2; + } else + { + putchr(c); + column++; + } + } + } + if (column < sc_width || !auto_wrap || ignaw) + putchr('\n'); +} + +static char obuf[1024]; +static char *ob = obuf; + +/* + * Flush buffered output. + */ +flush() +{ + register int n; + + n = ob - obuf; + if (n == 0) + return; + if (write(1, obuf, n) != n) + screen_trashed = 1; + ob = obuf; +} + +/* + * Purge any pending output. + */ +purge() +{ + + ob = obuf; +} + +/* + * Output a character. + */ +putchr(c) + int c; +{ + if (ob >= &obuf[sizeof(obuf)]) + flush(); + *ob++ = c; +} + +/* + * Output a string. + */ +putstr(s) + register char *s; +{ + while (*s != '\0') + putchr(*s++); +} + +int cmdstack; +static char return_to_continue[] = "(press RETURN)"; + +/* + * Output a message in the lower left corner of the screen + * and wait for carriage return. + */ +error(s) + char *s; +{ + int ch; + + ++errmsgs; + if (!any_display) { + /* + * Nothing has been displayed yet. Output this message on + * error output (file descriptor 2) and don't wait for a + * keystroke to continue. + * + * This has the desirable effect of producing all error + * messages on error output if standard output is directed + * to a file. It also does the same if we never produce + * any real output; for example, if the input file(s) cannot + * be opened. If we do eventually produce output, code in + * edit() makes sure these messages can be seen before they + * are overwritten or scrolled away. + */ + (void)write(2, s, strlen(s)); + (void)write(2, "\n", 1); + return; + } + + lower_left(); + clear_eol(); + so_enter(); + if (s) { + putstr(s); + putstr(" "); + } + putstr(return_to_continue); + so_exit(); + + if ((ch = getchr()) != '\n') { + if (ch == 'q') + quit(); + cmdstack = ch; + } + lower_left(); + + if (strlen(s) + sizeof(return_to_continue) + + so_width + se_width + 1 > sc_width) + /* + * Printing the message has probably scrolled the screen. + * {{ Unless the terminal doesn't have auto margins, + * in which case we just hammered on the right margin. }} + */ + /* repaint(); */ + screen_trashed = 1; /* XXX */ + flush(); +} + +static char intr_to_abort[] = "... (interrupt to abort)"; + +ierror(s) + char *s; +{ + lower_left(); + clear_eol(); + so_enter(); + putstr(s); + putstr(intr_to_abort); + so_exit(); + flush(); +} diff --git a/usr.bin/more/pathnames.h b/usr.bin/more/pathnames.h index 296d60aba3..6402b411b3 100644 --- a/usr.bin/more/pathnames.h +++ b/usr.bin/more/pathnames.h @@ -1,4 +1,4 @@ -/*- +/* * Copyright (c) 1989 The Regents of the University of California. * All rights reserved. * @@ -30,9 +30,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pathnames.h 5.2 (Berkeley) 4/18/91 + * @(#)pathnames.h 5.2 (Berkeley) 6/1/90 */ #include <paths.h> -#define HELPFILE "/usr/share/misc/omore.help" +#define _PATH_HELPFILE "/usr/share/misc/more.help" diff --git a/usr.bin/more/position.c b/usr.bin/more/position.c new file mode 100644 index 0000000000..46ed345e83 --- /dev/null +++ b/usr.bin/more/position.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)position.c 5.7 (Berkeley) 6/1/90"; +#endif /* not lint */ + +/* + * Routines dealing with the "position" table. + * This is a table which tells the position (in the input file) of the + * first char on each currently displayed line. + * + * {{ The position table is scrolled by moving all the entries. + * Would be better to have a circular table + * and just change a couple of pointers. }} + */ + +#include <sys/types.h> +#include <less.h> + +static off_t *table; /* The position table */ +static int tablesize; + +extern int sc_height; + +/* + * Return the starting file position of a line displayed on the screen. + * The line may be specified as a line number relative to the top + * of the screen, but is usually one of these special cases: + * the top (first) line on the screen + * the second line on the screen + * the bottom line on the screen + * the line after the bottom line on the screen + */ +off_t +position(where) + int where; +{ + switch (where) + { + case BOTTOM: + where = sc_height - 2; + break; + case BOTTOM_PLUS_ONE: + where = sc_height - 1; + break; + case MIDDLE: + where = sc_height / 2; + } + return (table[where]); +} + +/* + * Add a new file position to the bottom of the position table. + */ +add_forw_pos(pos) + off_t pos; +{ + register int i; + + /* + * Scroll the position table up. + */ + for (i = 1; i < sc_height; i++) + table[i-1] = table[i]; + table[sc_height - 1] = pos; +} + +/* + * Add a new file position to the top of the position table. + */ +add_back_pos(pos) + off_t pos; +{ + register int i; + + /* + * Scroll the position table down. + */ + for (i = sc_height - 1; i > 0; i--) + table[i] = table[i-1]; + table[0] = pos; +} + +copytable() +{ + register int a, b; + + for (a = 0; a < sc_height && table[a] == NULL_POSITION; a++); + for (b = 0; a < sc_height; a++, b++) { + table[b] = table[a]; + table[a] = NULL_POSITION; + } +} + +/* + * Initialize the position table, done whenever we clear the screen. + */ +pos_clear() +{ + register int i; + extern char *malloc(), *realloc(); + + if (table == 0) { + tablesize = sc_height > 25 ? sc_height : 25; + table = (off_t *)malloc(tablesize * sizeof *table); + } else if (sc_height >= tablesize) { + tablesize = sc_height; + table = (off_t *)realloc(table, tablesize * sizeof *table); + } + + for (i = 0; i < sc_height; i++) + table[i] = NULL_POSITION; +} + +/* + * See if the byte at a specified position is currently on the screen. + * Check the position table to see if the position falls within its range. + * Return the position table entry if found, -1 if not. + */ +onscreen(pos) + off_t pos; +{ + register int i; + + if (pos < table[0]) + return (-1); + for (i = 1; i < sc_height; i++) + if (pos < table[i]) + return (i-1); + return (-1); +} diff --git a/usr.bin/more/prim.c b/usr.bin/more/prim.c new file mode 100644 index 0000000000..e0410304cf --- /dev/null +++ b/usr.bin/more/prim.c @@ -0,0 +1,871 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)prim.c 5.8 (Berkeley) 6/1/90"; +#endif /* not lint */ + +/* + * Primitives for displaying the file on the screen. + */ + +#include <sys/types.h> +#include <stdio.h> +#include <ctype.h> +#include <less.h> + +#ifdef REGEX +#include <regex.h> +#endif + +int back_scroll = -1; +int hit_eof; /* keeps track of how many times we hit end of file */ +int screen_trashed; + +static int squished; + +extern int sigs; +extern int top_scroll; +extern int sc_width, sc_height; +extern int caseless; +extern int linenums; +extern int tagoption; +extern char *line; + +off_t position(), forw_line(), back_line(), forw_raw_line(), back_raw_line(); +off_t ch_length(), ch_tell(); + +/* + * Check to see if the end of file is currently "displayed". + */ +eof_check() +{ + off_t pos; + + if (sigs) + return; + /* + * If the bottom line is empty, we are at EOF. + * If the bottom line ends at the file length, + * we must be just at EOF. + */ + pos = position(BOTTOM_PLUS_ONE); + if (pos == NULL_POSITION || pos == ch_length()) + hit_eof++; +} + +/* + * If the screen is "squished", repaint it. + * "Squished" means the first displayed line is not at the top + * of the screen; this can happen when we display a short file + * for the first time. + */ +squish_check() +{ + if (squished) { + squished = 0; + repaint(); + } +} + +/* + * Display n lines, scrolling forward, starting at position pos in the + * input file. "only_last" means display only the last screenful if + * n > screen size. + */ +forw(n, pos, only_last) + register int n; + off_t pos; + int only_last; +{ + extern int short_file; + static int first_time = 1; + int eof = 0, do_repaint; + + squish_check(); + + /* + * do_repaint tells us not to display anything till the end, + * then just repaint the entire screen. + */ + do_repaint = (only_last && n > sc_height-1); + + if (!do_repaint) { + if (top_scroll && n >= sc_height - 1) { + /* + * Start a new screen. + * {{ This is not really desirable if we happen + * to hit eof in the middle of this screen, + * but we don't yet know if that will happen. }} + */ + clear(); + home(); + } else { + lower_left(); + clear_eol(); + } + + /* + * This is not contiguous with what is currently displayed. + * Clear the screen image (position table) and start a new + * screen. + */ + if (pos != position(BOTTOM_PLUS_ONE)) { + pos_clear(); + add_forw_pos(pos); + if (top_scroll) { + clear(); + home(); + } else if (!first_time) + putstr("...skipping...\n"); + } + } + + for (short_file = 0; --n >= 0;) { + /* + * Read the next line of input. + */ + pos = forw_line(pos); + if (pos == NULL_POSITION) { + /* + * end of file; copy the table if the file was + * too small for an entire screen. + */ + eof = 1; + if (position(TOP) == NULL_POSITION) { + copytable(); + if (!position(TOP)) + short_file = 1; + } + break; + } + /* + * Add the position of the next line to the position table. + * Display the current line on the screen. + */ + add_forw_pos(pos); + if (do_repaint) + continue; + /* + * If this is the first screen displayed and we hit an early + * EOF (i.e. before the requested number of lines), we + * "squish" the display down at the bottom of the screen. + * But don't do this if a -t option was given; it can cause + * us to start the display after the beginning of the file, + * and it is not appropriate to squish in that case. + */ + if (first_time && line == NULL && !top_scroll && !tagoption) { + squished = 1; + continue; + } + put_line(); + } + + if (eof && !sigs) + hit_eof++; + else + eof_check(); + if (do_repaint) + repaint(); + first_time = 0; + (void) currline(BOTTOM); +} + +/* + * Display n lines, scrolling backward. + */ +back(n, pos, only_last) + register int n; + off_t pos; + int only_last; +{ + int do_repaint; + + squish_check(); + do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1)); + hit_eof = 0; + while (--n >= 0) + { + /* + * Get the previous line of input. + */ + pos = back_line(pos); + if (pos == NULL_POSITION) + break; + /* + * Add the position of the previous line to the position table. + * Display the line on the screen. + */ + add_back_pos(pos); + if (!do_repaint) + { + home(); + add_line(); + put_line(); + } + } + + eof_check(); + if (do_repaint) + repaint(); + (void) currline(BOTTOM); +} + +/* + * Display n more lines, forward. + * Start just after the line currently displayed at the bottom of the screen. + */ +forward(n, only_last) + int n; + int only_last; +{ + off_t pos; + + if (hit_eof) { + /* + * If we're trying to go forward from end-of-file, + * go on to the next file. + */ + next_file(1); + return; + } + + pos = position(BOTTOM_PLUS_ONE); + if (pos == NULL_POSITION) + { + hit_eof++; + return; + } + forw(n, pos, only_last); +} + +/* + * Display n more lines, backward. + * Start just before the line currently displayed at the top of the screen. + */ +backward(n, only_last) + int n; + int only_last; +{ + off_t pos; + + pos = position(TOP); + /* + * This will almost never happen, because the top line is almost + * never empty. + */ + if (pos == NULL_POSITION) + return; + back(n, pos, only_last); +} + +/* + * Repaint the screen, starting from a specified position. + */ +prepaint(pos) + off_t pos; +{ + hit_eof = 0; + forw(sc_height-1, pos, 0); + screen_trashed = 0; +} + +/* + * Repaint the screen. + */ +repaint() +{ + /* + * Start at the line currently at the top of the screen + * and redisplay the screen. + */ + prepaint(position(TOP)); +} + +/* + * Jump to the end of the file. + * It is more convenient to paint the screen backward, + * from the end of the file toward the beginning. + */ +jump_forw() +{ + off_t pos; + + if (ch_end_seek()) + { + error("Cannot seek to end of file"); + return; + } + lastmark(); + pos = ch_tell(); + clear(); + pos_clear(); + add_back_pos(pos); + back(sc_height - 1, pos, 0); +} + +/* + * Jump to line n in the file. + */ +jump_back(n) + register int n; +{ + register int c, nlines; + + /* + * This is done the slow way, by starting at the beginning + * of the file and counting newlines. + * + * {{ Now that we have line numbering (in linenum.c), + * we could improve on this by starting at the + * nearest known line rather than at the beginning. }} + */ + if (ch_seek((off_t)0)) { + /* + * Probably a pipe with beginning of file no longer buffered. + * If he wants to go to line 1, we do the best we can, + * by going to the first line which is still buffered. + */ + if (n <= 1 && ch_beg_seek() == 0) + jump_loc(ch_tell()); + error("Cannot get to beginning of file"); + return; + } + + /* + * Start counting lines. + */ + for (nlines = 1; nlines < n; nlines++) + while ((c = ch_forw_get()) != '\n') + if (c == EOI) { + char message[40]; + (void)sprintf(message, "File has only %d lines", + nlines - 1); + error(message); + return; + } + jump_loc(ch_tell()); +} + +/* + * Jump to a specified percentage into the file. + * This is a poor compensation for not being able to + * quickly jump to a specific line number. + */ +jump_percent(percent) + int percent; +{ + off_t pos, len, ch_length(); + register int c; + + /* + * Determine the position in the file + * (the specified percentage of the file's length). + */ + if ((len = ch_length()) == NULL_POSITION) + { + error("Don't know length of file"); + return; + } + pos = (percent * len) / 100; + + /* + * Back up to the beginning of the line. + */ + if (ch_seek(pos) == 0) + { + while ((c = ch_back_get()) != '\n' && c != EOI) + ; + if (c == '\n') + (void) ch_forw_get(); + pos = ch_tell(); + } + jump_loc(pos); +} + +/* + * Jump to a specified position in the file. + */ +jump_loc(pos) + off_t pos; +{ + register int nline; + off_t tpos; + + if ((nline = onscreen(pos)) >= 0) { + /* + * The line is currently displayed. + * Just scroll there. + */ + forw(nline, position(BOTTOM_PLUS_ONE), 0); + return; + } + + /* + * Line is not on screen. + * Seek to the desired location. + */ + if (ch_seek(pos)) { + error("Cannot seek to that position"); + return; + } + + /* + * See if the desired line is BEFORE the currently displayed screen. + * If so, then move forward far enough so the line we're on will be + * at the bottom of the screen, in order to be able to call back() + * to make the screen scroll backwards & put the line at the top of + * the screen. + * {{ This seems inefficient, but it's not so bad, + * since we can never move forward more than a + * screenful before we stop to redraw the screen. }} + */ + tpos = position(TOP); + if (tpos != NULL_POSITION && pos < tpos) { + off_t npos = pos; + /* + * Note that we can't forw_line() past tpos here, + * so there should be no EOI at this stage. + */ + for (nline = 0; npos < tpos && nline < sc_height - 1; nline++) + npos = forw_line(npos); + + if (npos < tpos) { + /* + * More than a screenful back. + */ + lastmark(); + clear(); + pos_clear(); + add_back_pos(npos); + } + + /* + * Note that back() will repaint() if nline > back_scroll. + */ + back(nline, npos, 0); + return; + } + /* + * Remember where we were; clear and paint the screen. + */ + lastmark(); + prepaint(pos); +} + +/* + * The table of marks. + * A mark is simply a position in the file. + */ +#define NMARKS (27) /* 26 for a-z plus one for quote */ +#define LASTMARK (NMARKS-1) /* For quote */ +static off_t marks[NMARKS]; + +/* + * Initialize the mark table to show no marks are set. + */ +init_mark() +{ + int i; + + for (i = 0; i < NMARKS; i++) + marks[i] = NULL_POSITION; +} + +/* + * See if a mark letter is valid (between a and z). + */ + static int +badmark(c) + int c; +{ + if (c < 'a' || c > 'z') + { + error("Choose a letter between 'a' and 'z'"); + return (1); + } + return (0); +} + +/* + * Set a mark. + */ +setmark(c) + int c; +{ + if (badmark(c)) + return; + marks[c-'a'] = position(TOP); +} + +lastmark() +{ + marks[LASTMARK] = position(TOP); +} + +/* + * Go to a previously set mark. + */ +gomark(c) + int c; +{ + off_t pos; + + if (c == '\'') { + pos = marks[LASTMARK]; + if (pos == NULL_POSITION) + pos = 0; + } + else { + if (badmark(c)) + return; + pos = marks[c-'a']; + if (pos == NULL_POSITION) { + error("mark not set"); + return; + } + } + jump_loc(pos); +} + +/* + * Get the backwards scroll limit. + * Must call this function instead of just using the value of + * back_scroll, because the default case depends on sc_height and + * top_scroll, as well as back_scroll. + */ +get_back_scroll() +{ + if (back_scroll >= 0) + return (back_scroll); + if (top_scroll) + return (sc_height - 2); + return (sc_height - 1); +} + +/* + * Search for the n-th occurence of a specified pattern, + * either forward or backward. + */ +search(search_forward, pattern, n, wantmatch) + register int search_forward; + register char *pattern; + register int n; + int wantmatch; +{ + off_t pos, linepos; + register char *p; + register char *q; + int linenum; + int linematch; +#ifdef REGEX + static regex_t *cpattern = NULL; +#else +#ifdef RECOMP + char *re_comp(); + char *errmsg; +#else +#ifdef REGCMP + char *regcmp(); + static char *cpattern = NULL; +#else + static char lpbuf[100]; + static char *last_pattern = NULL; + char *strcpy(); +#endif +#endif +#endif /*REGEX */ + /* + * For a caseless search, convert any uppercase in the pattern to + * lowercase. + */ + if (caseless && pattern != NULL) + for (p = pattern; *p; p++) + if (isupper(*p)) + *p = tolower(*p); +#ifdef REGEX + if (pattern == NULL || *pattern == '\0') + { + /* + * A null pattern means use the previous pattern. + * The compiled previous pattern is in cpattern, so just use it. + */ + if (cpattern == NULL) + { + error("No previous regular expression"); + return(0); + } + } else + { + /* + * Otherwise compile the given pattern. + */ + if (cpattern == NULL + && (cpattern = (regex_t *) malloc(sizeof(regex_t))) == NULL) { + error("cannot allocate memory"); + quit(); + } + else + regfree(cpattern); + if (regcomp(cpattern, pattern, 0)) + { + error("Invalid pattern"); + return(0); + } + } +#else +#ifdef RECOMP + + /* + * (re_comp handles a null pattern internally, + * so there is no need to check for a null pattern here.) + */ + if ((errmsg = re_comp(pattern)) != NULL) + { + error(errmsg); + return(0); + } +#else +#ifdef REGCMP + if (pattern == NULL || *pattern == '\0') + { + /* + * A null pattern means use the previous pattern. + * The compiled previous pattern is in cpattern, so just use it. + */ + if (cpattern == NULL) + { + error("No previous regular expression"); + return(0); + } + } else + { + /* + * Otherwise compile the given pattern. + */ + char *s; + if ((s = regcmp(pattern, 0)) == NULL) + { + error("Invalid pattern"); + return(0); + } + if (cpattern != NULL) + free(cpattern); + cpattern = s; + } +#else + if (pattern == NULL || *pattern == '\0') + { + /* + * Null pattern means use the previous pattern. + */ + if (last_pattern == NULL) + { + error("No previous regular expression"); + return(0); + } + pattern = last_pattern; + } else + { + (void)strcpy(lpbuf, pattern); + last_pattern = lpbuf; + } +#endif +#endif +#endif /* REGEX */ + + /* + * Figure out where to start the search. + */ + + if (position(TOP) == NULL_POSITION) { + /* + * Nothing is currently displayed. Start at the beginning + * of the file. (This case is mainly for searches from the + * command line. + */ + pos = (off_t)0; + } else if (!search_forward) { + /* + * Backward search: start just before the top line + * displayed on the screen. + */ + pos = position(TOP); + } else { + /* + * Start at the second screen line displayed on the screen. + */ + pos = position(TOP_PLUS_ONE); + } + + if (pos == NULL_POSITION) + { + /* + * Can't find anyplace to start searching from. + */ + error("Nothing to search"); + return(0); + } + + linenum = find_linenum(pos); + for (;;) + { + /* + * Get lines until we find a matching one or + * until we hit end-of-file (or beginning-of-file + * if we're going backwards). + */ + if (sigs) + /* + * A signal aborts the search. + */ + return(0); + + if (search_forward) + { + /* + * Read the next line, and save the + * starting position of that line in linepos. + */ + linepos = pos; + pos = forw_raw_line(pos); + if (linenum != 0) + linenum++; + } else + { + /* + * Read the previous line and save the + * starting position of that line in linepos. + */ + pos = back_raw_line(pos); + linepos = pos; + if (linenum != 0) + linenum--; + } + + if (pos == NULL_POSITION) + { + /* + * We hit EOF/BOF without a match. + */ + error("Pattern not found"); + return(0); + } + + /* + * If we're using line numbers, we might as well + * remember the information we have now (the position + * and line number of the current line). + */ + if (linenums) + add_lnum(linenum, pos); + + /* + * If this is a caseless search, convert uppercase in the + * input line to lowercase. + */ + if (caseless) + for (p = q = line; *p; p++, q++) + *q = isupper(*p) ? tolower(*p) : *p; + + /* + * Remove any backspaces along with the preceeding char. + * This allows us to match text which is underlined or + * overstruck. + */ + for (p = q = line; *p; p++, q++) + if (q > line && *p == '\b') + /* Delete BS and preceeding char. */ + q -= 2; + else + /* Otherwise, just copy. */ + *q = *p; + + /* + * Test the next line to see if we have a match. + * This is done in a variety of ways, depending + * on what pattern matching functions are available. + */ +#ifdef REGEX + linematch = !regexec(cpattern, line, 0, NULL, 0); +#else +#ifdef REGCMP + linematch = (regex(cpattern, line) != NULL); +#else +#ifdef RECOMP + linematch = (re_exec(line) == 1); +#else + linematch = match(pattern, line); +#endif +#endif +#endif /* REGEX */ + /* + * We are successful if wantmatch and linematch are + * both true (want a match and got it), + * or both false (want a non-match and got it). + */ + if (((wantmatch && linematch) || (!wantmatch && !linematch)) && + --n <= 0) + /* + * Found the line. + */ + break; + } + jump_loc(linepos); + return(1); +} + +#if !defined(REGCMP) && !defined(RECOMP) && !defined(RECOMP) +/* + * We have neither regcmp() nor re_comp(). + * We use this function to do simple pattern matching. + * It supports no metacharacters like *, etc. + */ +static +match(pattern, buf) + char *pattern, *buf; +{ + register char *pp, *lp; + + for ( ; *buf != '\0'; buf++) + { + for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++) + if (*pp == '\0' || *lp == '\0') + break; + if (*pp == '\0') + return (1); + } + return (0); +} +#endif diff --git a/usr.bin/more/screen.c b/usr.bin/more/screen.c new file mode 100644 index 0000000000..64d3500840 --- /dev/null +++ b/usr.bin/more/screen.c @@ -0,0 +1,557 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)screen.c 5.8 (Berkeley) 6/28/92"; +#endif /* not lint */ + +/* + * Routines which deal with the characteristics of the terminal. + * Uses termcap to be as terminal-independent as possible. + * + * {{ Someday this should be rewritten to use curses. }} + */ + +#include <stdio.h> +#include <less.h> + +#if TERMIO +#include <termio.h> +#else +#include <sgtty.h> +#endif + +#ifdef TIOCGWINSZ +#include <sys/ioctl.h> +#else +/* + * For the Unix PC (ATT 7300 & 3B1): + * Since WIOCGETD is defined in sys/window.h, we can't use that to decide + * whether to include sys/window.h. Use SIGPHONE from sys/signal.h instead. + */ +#include <sys/signal.h> +#ifdef SIGPHONE +#include <sys/window.h> +#endif +#endif + +/* + * Strings passed to tputs() to do various terminal functions. + */ +static char + *sc_pad, /* Pad string */ + *sc_home, /* Cursor home */ + *sc_addline, /* Add line, scroll down following lines */ + *sc_lower_left, /* Cursor to last line, first column */ + *sc_move, /* General cursor positioning */ + *sc_clear, /* Clear screen */ + *sc_eol_clear, /* Clear to end of line */ + *sc_s_in, /* Enter standout (highlighted) mode */ + *sc_s_out, /* Exit standout mode */ + *sc_u_in, /* Enter underline mode */ + *sc_u_out, /* Exit underline mode */ + *sc_b_in, /* Enter bold mode */ + *sc_b_out, /* Exit bold mode */ + *sc_backspace, /* Backspace cursor */ + *sc_init, /* Startup terminal initialization */ + *sc_deinit; /* Exit terminal de-intialization */ + +int auto_wrap; /* Terminal does \r\n when write past margin */ +int ignaw; /* Terminal ignores \n immediately after wrap */ + /* The user's erase and line-kill chars */ +int erase_char, kill_char, werase_char; +int sc_width, sc_height = -1; /* Height & width of screen */ +int sc_window = -1; /* window size for forward and backward */ +int bo_width, be_width; /* Printing width of boldface sequences */ +int ul_width, ue_width; /* Printing width of underline sequences */ +int so_width, se_width; /* Printing width of standout sequences */ + +/* + * These two variables are sometimes defined in, + * and needed by, the termcap library. + * It may be necessary on some systems to declare them extern here. + */ +/*extern*/ short ospeed; /* Terminal output baud rate */ +/*extern*/ char PC; /* Pad character */ + +extern int back_scroll; +char *tgetstr(); +char *tgoto(); + +/* + * Change terminal to "raw mode", or restore to "normal" mode. + * "Raw mode" means + * 1. An outstanding read will complete on receipt of a single keystroke. + * 2. Input is not echoed. + * 3. On output, \n is mapped to \r\n. + * 4. \t is NOT expanded into spaces. + * 5. Signal-causing characters such as ctrl-C (interrupt), + * etc. are NOT disabled. + * It doesn't matter whether an input \n is mapped to \r, or vice versa. + */ +raw_mode(on) + int on; +{ +#if TERMIO + struct termio s; + static struct termio save_term; + + if (on) + { + /* + * Get terminal modes. + */ + (void)ioctl(2, TCGETA, &s); + + /* + * Save modes and set certain variables dependent on modes. + */ + save_term = s; + ospeed = s.c_cflag & CBAUD; + erase_char = s.c_cc[VERASE]; + kill_char = s.c_cc[VKILL]; + werase_char = s.c_cc[VWERASE]; + + /* + * Set the modes to the way we want them. + */ + s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL); + s.c_oflag |= (OPOST|ONLCR|TAB3); + s.c_oflag &= ~(OCRNL|ONOCR|ONLRET); + s.c_cc[VMIN] = 1; + s.c_cc[VTIME] = 0; + } else + { + /* + * Restore saved modes. + */ + s = save_term; + } + (void)ioctl(2, TCSETAW, &s); +#else + struct sgttyb s; + struct ltchars l; + static struct sgttyb save_term; + + if (on) + { + /* + * Get terminal modes. + */ + (void)ioctl(2, TIOCGETP, &s); + (void)ioctl(2, TIOCGLTC, &l); + + /* + * Save modes and set certain variables dependent on modes. + */ + save_term = s; + ospeed = s.sg_ospeed; + erase_char = s.sg_erase; + kill_char = s.sg_kill; + werase_char = l.t_werasc; + + /* + * Set the modes to the way we want them. + */ + s.sg_flags |= CBREAK; + s.sg_flags &= ~(ECHO|XTABS); + } else + { + /* + * Restore saved modes. + */ + s = save_term; + } + (void)ioctl(2, TIOCSETN, &s); +#endif +} + +/* + * Get terminal capabilities via termcap. + */ +get_term() +{ + char termbuf[2048]; + char *sp; + char *term; + int hard; +#ifdef TIOCGWINSZ + struct winsize w; +#else +#ifdef WIOCGETD + struct uwdata w; +#endif +#endif + static char sbuf[1024]; + + char *getenv(), *strcpy(); + + /* + * Find out what kind of terminal this is. + */ + if ((term = getenv("TERM")) == NULL) + term = "unknown"; + if (tgetent(termbuf, term) <= 0) + (void)strcpy(termbuf, "dumb:co#80:hc:"); + + /* + * Get size of the screen. + */ +#ifdef TIOCGWINSZ + if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0) + sc_height = w.ws_row; +#else +#ifdef WIOCGETD + if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0) + sc_height = w.uw_height/w.uw_vs; +#endif +#endif + else + sc_height = tgetnum("li"); + hard = (sc_height < 0 || tgetflag("hc")); + if (hard) { + /* Oh no, this is a hardcopy terminal. */ + sc_height = 24; + } + +#ifdef TIOCGWINSZ + if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0) + sc_width = w.ws_col; + else +#ifdef WIOCGETD + if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0) + sc_width = w.uw_width/w.uw_hs; + else +#endif +#endif + sc_width = tgetnum("co"); + if (sc_width < 0) + sc_width = 80; + + auto_wrap = tgetflag("am"); + ignaw = tgetflag("xn"); + + /* + * Assumes termcap variable "sg" is the printing width of + * the standout sequence, the end standout sequence, + * the underline sequence, the end underline sequence, + * the boldface sequence, and the end boldface sequence. + */ + if ((so_width = tgetnum("sg")) < 0) + so_width = 0; + be_width = bo_width = ue_width = ul_width = se_width = so_width; + + /* + * Get various string-valued capabilities. + */ + sp = sbuf; + + sc_pad = tgetstr("pc", &sp); + if (sc_pad != NULL) + PC = *sc_pad; + + sc_init = tgetstr("ti", &sp); + if (sc_init == NULL) + sc_init = ""; + + sc_deinit= tgetstr("te", &sp); + if (sc_deinit == NULL) + sc_deinit = ""; + + sc_eol_clear = tgetstr("ce", &sp); + if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0') + { + sc_eol_clear = ""; + } + + sc_clear = tgetstr("cl", &sp); + if (hard || sc_clear == NULL || *sc_clear == '\0') + { + sc_clear = "\n\n"; + } + + sc_move = tgetstr("cm", &sp); + if (hard || sc_move == NULL || *sc_move == '\0') + { + /* + * This is not an error here, because we don't + * always need sc_move. + * We need it only if we don't have home or lower-left. + */ + sc_move = ""; + } + + sc_s_in = tgetstr("so", &sp); + if (hard || sc_s_in == NULL) + sc_s_in = ""; + + sc_s_out = tgetstr("se", &sp); + if (hard || sc_s_out == NULL) + sc_s_out = ""; + + sc_u_in = tgetstr("us", &sp); + if (hard || sc_u_in == NULL) + sc_u_in = sc_s_in; + + sc_u_out = tgetstr("ue", &sp); + if (hard || sc_u_out == NULL) + sc_u_out = sc_s_out; + + sc_b_in = tgetstr("md", &sp); + if (hard || sc_b_in == NULL) + { + sc_b_in = sc_s_in; + sc_b_out = sc_s_out; + } else + { + sc_b_out = tgetstr("me", &sp); + if (hard || sc_b_out == NULL) + sc_b_out = ""; + } + + sc_home = tgetstr("ho", &sp); + if (hard || sc_home == NULL || *sc_home == '\0') + { + if (*sc_move == '\0') + { + /* + * This last resort for sc_home is supposed to + * be an up-arrow suggesting moving to the + * top of the "virtual screen". (The one in + * your imagination as you try to use this on + * a hard copy terminal.) + */ + sc_home = "|\b^"; + } else + { + /* + * No "home" string, + * but we can use "move(0,0)". + */ + (void)strcpy(sp, tgoto(sc_move, 0, 0)); + sc_home = sp; + sp += strlen(sp) + 1; + } + } + + sc_lower_left = tgetstr("ll", &sp); + if (hard || sc_lower_left == NULL || *sc_lower_left == '\0') + { + if (*sc_move == '\0') + { + sc_lower_left = "\r"; + } else + { + /* + * No "lower-left" string, + * but we can use "move(0,last-line)". + */ + (void)strcpy(sp, tgoto(sc_move, 0, sc_height-1)); + sc_lower_left = sp; + sp += strlen(sp) + 1; + } + } + + /* + * To add a line at top of screen and scroll the display down, + * we use "al" (add line) or "sr" (scroll reverse). + */ + if ((sc_addline = tgetstr("al", &sp)) == NULL || + *sc_addline == '\0') + sc_addline = tgetstr("sr", &sp); + + if (hard || sc_addline == NULL || *sc_addline == '\0') + { + sc_addline = ""; + /* Force repaint on any backward movement */ + back_scroll = 0; + } + + if (tgetflag("bs")) + sc_backspace = "\b"; + else + { + sc_backspace = tgetstr("bc", &sp); + if (sc_backspace == NULL || *sc_backspace == '\0') + sc_backspace = "\b"; + } +} + + +/* + * Below are the functions which perform all the + * terminal-specific screen manipulation. + */ + +int putchr(); + +/* + * Initialize terminal + */ +init() +{ + tputs(sc_init, sc_height, putchr); +} + +/* + * Deinitialize terminal + */ +deinit() +{ + tputs(sc_deinit, sc_height, putchr); +} + +/* + * Home cursor (move to upper left corner of screen). + */ +home() +{ + tputs(sc_home, 1, putchr); +} + +/* + * Add a blank line (called with cursor at home). + * Should scroll the display down. + */ +add_line() +{ + tputs(sc_addline, sc_height, putchr); +} + +int short_file; /* if file less than a screen */ +lower_left() +{ + if (short_file) { + putchr('\r'); + flush(); + } + else + tputs(sc_lower_left, 1, putchr); +} + +/* + * Ring the terminal bell. + */ +bell() +{ + putchr('\7'); +} + +/* + * Clear the screen. + */ +clear() +{ + tputs(sc_clear, sc_height, putchr); +} + +/* + * Clear from the cursor to the end of the cursor's line. + * {{ This must not move the cursor. }} + */ +clear_eol() +{ + tputs(sc_eol_clear, 1, putchr); +} + +/* + * Begin "standout" (bold, underline, or whatever). + */ +so_enter() +{ + tputs(sc_s_in, 1, putchr); +} + +/* + * End "standout". + */ +so_exit() +{ + tputs(sc_s_out, 1, putchr); +} + +/* + * Begin "underline" (hopefully real underlining, + * otherwise whatever the terminal provides). + */ +ul_enter() +{ + tputs(sc_u_in, 1, putchr); +} + +/* + * End "underline". + */ +ul_exit() +{ + tputs(sc_u_out, 1, putchr); +} + +/* + * Begin "bold" + */ +bo_enter() +{ + tputs(sc_b_in, 1, putchr); +} + +/* + * End "bold". + */ +bo_exit() +{ + tputs(sc_b_out, 1, putchr); +} + +/* + * Erase the character to the left of the cursor + * and move the cursor left. + */ +backspace() +{ + /* + * Try to erase the previous character by overstriking with a space. + */ + tputs(sc_backspace, 1, putchr); + putchr(' '); + tputs(sc_backspace, 1, putchr); +} + +/* + * Output a plain backspace, without erasing the previous char. + */ +putbs() +{ + tputs(sc_backspace, 1, putchr); +} diff --git a/usr.bin/more/signal.c b/usr.bin/more/signal.c new file mode 100644 index 0000000000..aa97f07418 --- /dev/null +++ b/usr.bin/more/signal.c @@ -0,0 +1,220 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)signal.c 5.8 (Berkeley) 3/1/91"; +#endif /* not lint */ + +/* + * Routines dealing with signals. + * + * A signal usually merely causes a bit to be set in the "signals" word. + * At some convenient time, the mainline code checks to see if any + * signals need processing by calling psignal(). + * If we happen to be reading from a file [in iread()] at the time + * the signal is received, we call intread to interrupt the iread. + */ + +#include <less.h> +#include <signal.h> + +/* + * "sigs" contains bits indicating signals which need to be processed. + */ +int sigs; + +#ifdef SIGTSTP +#define S_STOP 02 +#endif +#if defined(SIGWINCH) || defined(SIGWIND) +#define S_WINCH 04 +#endif + +extern int sc_width, sc_height; +extern int screen_trashed; +extern int lnloop; +extern int linenums; +extern int scroll; +extern int reading; + +#ifdef SIGTSTP +/* + * "Stop" (^Z) signal handler. + */ +static void +stop() +{ + (void)signal(SIGTSTP, stop); + sigs |= S_STOP; + if (reading) + intread(); +} +#endif + +#ifdef SIGWINCH +/* + * "Window" change handler + */ +void +winch() +{ + (void)signal(SIGWINCH, winch); + sigs |= S_WINCH; + if (reading) + intread(); +} +#else +#ifdef SIGWIND +/* + * "Window" change handler + */ +winch() +{ + (void)signal(SIGWIND, winch); + sigs |= S_WINCH; + if (reading) + intread(); +} +#endif +#endif + +static void +purgeandquit() +{ + + purge(); /* purge buffered output */ + quit(); +} + +/* + * Set up the signal handlers. + */ +init_signals(on) + int on; +{ + if (on) + { + /* + * Set signal handlers. + */ + (void)signal(SIGINT, purgeandquit); +#ifdef SIGTSTP + (void)signal(SIGTSTP, stop); +#endif +#ifdef SIGWINCH + (void)signal(SIGWINCH, winch); +#else +#ifdef SIGWIND + (void)signal(SIGWIND, winch); +#endif +#endif + } else + { + /* + * Restore signals to defaults. + */ + (void)signal(SIGINT, SIG_DFL); +#ifdef SIGTSTP + (void)signal(SIGTSTP, SIG_DFL); +#endif +#ifdef SIGWINCH + (void)signal(SIGWINCH, SIG_IGN); +#endif +#ifdef SIGWIND + (void)signal(SIGWIND, SIG_IGN); +#endif + } +} + +/* + * Process any signals we have received. + * A received signal cause a bit to be set in "sigs". + */ +psignals() +{ + register int tsignals; + + if ((tsignals = sigs) == 0) + return; + sigs = 0; + +#ifdef S_WINCH + if (tsignals & S_WINCH) + { + int old_width, old_height; + /* + * Re-execute get_term() to read the new window size. + */ + old_width = sc_width; + old_height = sc_height; + get_term(); + if (sc_width != old_width || sc_height != old_height) + { + scroll = (sc_height + 1) / 2; + screen_trashed = 1; + } + } +#endif +#ifdef SIGTSTP + if (tsignals & S_STOP) + { + /* + * Clean up the terminal. + */ +#ifdef SIGTTOU + (void)signal(SIGTTOU, SIG_IGN); +#endif + lower_left(); + clear_eol(); + deinit(); + (void)flush(); + raw_mode(0); +#ifdef SIGTTOU + (void)signal(SIGTTOU, SIG_DFL); +#endif + (void)signal(SIGTSTP, SIG_DFL); + (void)kill(getpid(), SIGTSTP); + /* + * ... Bye bye. ... + * Hopefully we'll be back later and resume here... + * Reset the terminal and arrange to repaint the + * screen when we get back to the main command loop. + */ + (void)signal(SIGTSTP, stop); + raw_mode(1); + init(); + screen_trashed = 1; + } +#endif +} diff --git a/usr.bin/more/tags.c b/usr.bin/more/tags.c new file mode 100644 index 0000000000..af186ec318 --- /dev/null +++ b/usr.bin/more/tags.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)tags.c 5.5 (Berkeley) 6/1/90"; +#endif /* not lint */ + +#include <sys/types.h> +#include <stdio.h> +#include <less.h> + +#define WHITESP(c) ((c)==' ' || (c)=='\t') + +char *tagfile; +char *tagpattern; + +static char *tags = "tags"; + +extern int linenums; +extern int sigs; +extern char *line; + +/* + * Find a tag in the "tags" file. + * Sets "tagfile" to the name of the file containing the tag, + * and "tagpattern" to the search pattern which should be used + * to find the tag. + */ +findtag(tag) + register char *tag; +{ + register char *p; + register FILE *f; + register int taglen; + int search_char; + static char tline[200]; + + if ((f = fopen(tags, "r")) == NULL) + { + error("No tags file"); + tagfile = NULL; + return; + } + + taglen = strlen(tag); + + /* + * Search the tags file for the desired tag. + */ + while (fgets(tline, sizeof(tline), f) != NULL) + { + if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen])) + continue; + + /* + * Found it. + * The line contains the tag, the filename and the + * pattern, separated by white space. + * The pattern is surrounded by a pair of identical + * search characters. + * Parse the line and extract these parts. + */ + tagfile = tagpattern = NULL; + + /* + * Skip over the whitespace after the tag name. + */ + for (p = tline; !WHITESP(*p) && *p != '\0'; p++) + continue; + while (WHITESP(*p)) + p++; + if (*p == '\0') + /* File name is missing! */ + continue; + + /* + * Save the file name. + * Skip over the whitespace after the file name. + */ + tagfile = p; + while (!WHITESP(*p) && *p != '\0') + p++; + *p++ = '\0'; + while (WHITESP(*p)) + p++; + if (*p == '\0') + /* Pattern is missing! */ + continue; + + /* + * Save the pattern. + * Skip to the end of the pattern. + * Delete the initial "^" and the final "$" from the pattern. + */ + search_char = *p++; + if (*p == '^') + p++; + tagpattern = p; + while (*p != search_char && *p != '\0') + p++; + if (p[-1] == '$') + p--; + *p = '\0'; + + (void)fclose(f); + return; + } + (void)fclose(f); + error("No such tag in tags file"); + tagfile = NULL; +} + +/* + * Search for a tag. + * This is a stripped-down version of search(). + * We don't use search() for several reasons: + * - We don't want to blow away any search string we may have saved. + * - The various regular-expression functions (from different systems: + * regcmp vs. re_comp) behave differently in the presence of + * parentheses (which are almost always found in a tag). + */ +tagsearch() +{ + off_t pos, linepos, forw_raw_line(); + int linenum; + + pos = (off_t)0; + linenum = find_linenum(pos); + + for (;;) + { + /* + * Get lines until we find a matching one or + * until we hit end-of-file. + */ + if (sigs) + return (1); + + /* + * Read the next line, and save the + * starting position of that line in linepos. + */ + linepos = pos; + pos = forw_raw_line(pos); + if (linenum != 0) + linenum++; + + if (pos == NULL_POSITION) + { + /* + * We hit EOF without a match. + */ + error("Tag not found"); + return (1); + } + + /* + * If we're using line numbers, we might as well + * remember the information we have now (the position + * and line number of the current line). + */ + if (linenums) + add_lnum(linenum, pos); + + /* + * Test the line to see if we have a match. + */ + if (strcmp(tagpattern, line) == 0) + break; + } + + jump_loc(linepos); + return (0); +} diff --git a/usr.bin/more/ttyin.c b/usr.bin/more/ttyin.c new file mode 100644 index 0000000000..44607751d2 --- /dev/null +++ b/usr.bin/more/ttyin.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ttyin.c 5.4 (Berkeley) 6/1/90"; +#endif /* not lint */ + +/* + * Routines dealing with getting input from the keyboard (i.e. from the user). + */ + +#include <less.h> + +static int tty; + +/* + * Open keyboard for input. + * (Just use file descriptor 2.) + */ +open_getchr() +{ + tty = 2; +} + +/* + * Get a character from the keyboard. + */ +getchr() +{ + char c; + int result; + + do + { + result = iread(tty, &c, 1); + if (result == READ_INTR) + return (READ_INTR); + if (result < 0) + { + /* + * Don't call error() here, + * because error calls getchr! + */ + quit(); + } + } while (result != 1); + return (c & 0177); +} diff --git a/usr.bin/msgs/msgs.1 b/usr.bin/msgs/msgs.1 index 0daa964b6d..41c512a3fe 100644 --- a/usr.bin/msgs/msgs.1 +++ b/usr.bin/msgs/msgs.1 @@ -69,27 +69,27 @@ If there is more to the message, you will be told how long it is and asked whether you wish to see the rest of the message. The possible responses are: .Bl -tag -width Fl -.It Fl y +.It Cm y Type the rest of the message. .It Ic RETURN Synonym for y. -.It Fl n +.It Cm n Skip this message and go on to the next message. .It Fl Redisplay the last message. -.It Fl q +.It Cm q Drop out of .Nm msgs ; the next time .Nm msgs will pick up where it last left off. -.It Fl s +.It Cm s Append the current message to the file ``Messages'' in the current directory; `s\-' will save the previously displayed message. A `s' or `s\-' may be followed by a space and a file name to receive the message replacing the default ``Messages''. -.It Fl m +.It Cm m A copy of the specified message is placed in a temporary mailbox and .Xr mail 1 @@ -123,7 +123,7 @@ The .Fl s option is used for setting up the posting of messages. The line .Pp -.Dl msgs: \&"\&| /usr/ucb/msgs \-s\&" +.Dl msgs: \&"\&| /usr/bin/msgs \-s\&" .Pp should be included in .Pa /etc/aliases @@ -196,8 +196,8 @@ and environment variables for the default home directory and terminal type. .Sh FILES -.Bl -tag -width /usr/msgs/* -compact -.It Pa /usr/msgs/* +.Bl -tag -width /var/msgs/* -compact +.It Pa /var/msgs/* database .It ~/.msgsrc number of next message to be presented diff --git a/usr.bin/msgs/msgs.c b/usr.bin/msgs/msgs.c index 28208e01c1..c473a89b1a 100644 --- a/usr.bin/msgs/msgs.c +++ b/usr.bin/msgs/msgs.c @@ -374,7 +374,6 @@ int argc; char *argv[]; } if (clean) exit(0); - /* * prepare to display messages */ @@ -382,27 +381,29 @@ int argc; char *argv[]; use_pager = use_pager && totty; sprintf(fname, "%s/%s", getenv("HOME"), MSGSRC); - msgsrc = fopen(fname, "r"); + msgsrc = fopen(fname, "r+"); if (msgsrc) { newrc = NO; - fscanf(msgsrc, "%d\n", &nextmsg); - fclose(msgsrc); - if (nextmsg > lastmsg+1) { + if (1 != fscanf(msgsrc, "%d\n", &nextmsg) || + nextmsg > lastmsg+1) { printf("Warning: bounds have been reset (%d, %d)\n", - firstmsg, lastmsg); + firstmsg, lastmsg); truncate(fname, (off_t)0); newrc = YES; } else if (!rcfirst) rcfirst = nextmsg - rcback; } - else + else { + msgsrc = fopen(fname, "w+"); newrc = YES; - msgsrc = fopen(fname, "a"); + } + if (msgsrc == NULL) { perror(fname); exit(errno); } + if (rcfirst) { if (rcfirst > lastmsg+1) { printf("Warning: the last message is number %d.\n", diff --git a/usr.bin/mt/mt.1 b/usr.bin/mt/mt.1 index a933efcffe..3a08f98acc 100644 --- a/usr.bin/mt/mt.1 +++ b/usr.bin/mt/mt.1 @@ -88,6 +88,10 @@ Rewind the tape and place the tape unit off-line (Count is ignored). .It Cm status Print status information about the tape unit. +.It Cm erase +Erase the tape. +.It Cm retension +Retension the tape. .El .Pp If a tape name is not specified, and the environment variable diff --git a/usr.bin/mt/mt.c b/usr.bin/mt/mt.c index 009ab1e87b..5186feedee 100644 --- a/usr.bin/mt/mt.c +++ b/usr.bin/mt/mt.c @@ -52,6 +52,9 @@ static char sccsid[] = "@(#)mt.c 5.6 (Berkeley) 6/6/91"; #include <stdio.h> #include <ctype.h> +#define MTERA -1 +#define MTRET -2 + #define equal(s1,s2) (strcmp(s1, s2) == 0) struct commands { @@ -69,6 +72,8 @@ struct commands { { "offline", MTOFFL, 1 }, { "rewoffl", MTOFFL, 1 }, { "status", MTNOP, 1 }, + { "erase", MTERA, 0 }, + { "retension", MTRET, 1 }, { 0 } }; @@ -92,22 +97,46 @@ main(argc, argv) if ((tape = getenv("TAPE")) == NULL) tape = DEFTAPE; if (argc < 2) { - fprintf(stderr, "usage: mt [ -f device ] command [ count ]\n"); +usage: fprintf(stderr, "Usage:\n"); + fprintf(stderr, "\tmt [ -f device ] command [ count ]\n"); + fprintf(stderr, "Commands:\n"); + fprintf(stderr, "\teof, weof - write tape mmrk\n"); + fprintf(stderr, "\tfsf - seek forward for tape mark\n"); + fprintf(stderr, "\tbsf - seek backward for tape mark\n"); + fprintf(stderr, "\tfsr - seek record forward\n"); + fprintf(stderr, "\tbsr - seek record backward\n"); + fprintf(stderr, "\trewind - rewind the tape\n"); + fprintf(stderr, "\toffline - rewind and unload the tape\n"); + fprintf(stderr, "\tstatus - get tape status\n"); + fprintf(stderr, "\terase - erase the tape\n"); + fprintf(stderr, "\tretension - retension the tape\n"); exit(1); } cp = argv[1]; for (comp = com; comp->c_name != NULL; comp++) if (strncmp(cp, comp->c_name, strlen(cp)) == 0) break; - if (comp->c_name == NULL) { - fprintf(stderr, "mt: don't grok \"%s\"\n", cp); - exit(1); - } + if (! comp->c_name) + goto usage; if ((mtfd = open(tape, comp->c_ronly ? O_RDONLY : O_RDWR)) < 0) { perror(tape); exit(1); } - if (comp->c_code != MTNOP) { + switch (comp->c_code) { + case MTERA: + command(comp->c_code, "erase"); + break; + case MTRET: + command(comp->c_code, "retension"); + break; + case MTNOP: + if (ioctl(mtfd, MTIOCGET, (char *)&mt_status) < 0) { + perror("mt"); + exit(2); + } + status(&mt_status); + break; + default: mt_com.mt_op = comp->c_code; mt_com.mt_count = (argc > 2 ? atoi(argv[2]) : 1); if (mt_com.mt_count < 0) { @@ -120,12 +149,7 @@ main(argc, argv) perror("failed"); exit(2); } - } else { - if (ioctl(mtfd, MTIOCGET, (char *)&mt_status) < 0) { - perror("mt"); - exit(2); - } - status(&mt_status); + break; } } @@ -148,6 +172,10 @@ main(argc, argv) #include <tahoe/vba/cyreg.h> #endif +#ifdef __386BSD__ +#include <sys/../i386/isa/wtreg.h> +#endif + struct tape_desc { short t_type; /* type of magtape device */ char *t_name; /* printing name */ @@ -167,10 +195,32 @@ struct tape_desc { #endif #ifdef tahoe { MT_ISCY, "cipher", CYS_BITS, CYCW_BITS }, +#endif +#ifdef __386BSD__ +#ifdef WTDS_BITS + { MT_ISVIPER1, "Archive", WTDS_BITS, WTER_BITS }, + { 0x11, "Wangtek", WTDS_BITS, WTER_BITS }, +#endif #endif { 0 } }; +/* + * Start erase or retension operation + */ + +command(op, opname) + char *opname; +{ +#ifdef WTQICMD + if (ioctl(mtfd, WTQICMD, op==MTERA ? QIC_ERASE : QIC_RETENS) >= 0) + return; + perror(opname); +#else + fprintf(stderr, "%s %s: operation not supported\n", tape, opname); +#endif +} + /* * Interpret the status buffer returned */ diff --git a/usr.bin/netstat/Makefile b/usr.bin/netstat/Makefile index 8f5f451f07..e2db444a18 100644 --- a/usr.bin/netstat/Makefile +++ b/usr.bin/netstat/Makefile @@ -2,16 +2,16 @@ PROG= netstat SRCS= inet.c if.c main.c mbuf.c route.c unix.c -# + #SRCS+= host.c #CFLAGS+= -DIMP -# -#SRCS+= ns.c -#CFLAGS+= -DNS -# -#SRCS+= iso.c -#CFLAGS+= -DISO -# + +SRCS+= ns.c +CFLAGS+= -DNS + +SRCS+= iso.c +CFLAGS+= -DISO + BINGRP= kmem BINMODE=2555 LDADD= -lutil diff --git a/usr.bin/netstat/iso.c b/usr.bin/netstat/iso.c index 2da77b2340..ed806040c6 100644 --- a/usr.bin/netstat/iso.c +++ b/usr.bin/netstat/iso.c @@ -36,8 +36,8 @@ static char sccsid[] = "@(#)iso.c 5.6 (Berkeley) 4/27/91"; #endif /* not lint */ /* - * $Header: iso.c,v 3.3 88/12/08 14:44:49 hagens Exp $ - * $Source: /usr/argo/src/ucb/netstat/RCS/iso.c,v $ + * $Header: /a/cvs/386BSD/src/usr.bin/netstat/iso.c,v 1.1.1.1 1993/06/12 14:47:55 rgrimes Exp $ + * $Source: /a/cvs/386BSD/src/usr.bin/netstat/iso.c,v $ */ /******************************************************************************* Copyright IBM Corporation 1987 @@ -210,7 +210,9 @@ iso_protopr(off, name) int istp = (strcmp(name, "tp") == 0); if (off == 0) { + /* printf("%s control block: symbol not in namelist\n", name); + */ return; } kget(off, cb); @@ -401,7 +403,9 @@ x25_protopr(off, name) struct x25_pcb xpcb; if (off == 0) { + /* printf("%s control block: symbol not in namelist\n", name); + */ return; } kvm_read(off, &xpcb, sizeof (struct x25_pcb)); diff --git a/usr.bin/netstat/netstat.1 b/usr.bin/netstat/netstat.1 index 3dd22c222c..25c3943ac3 100644 --- a/usr.bin/netstat/netstat.1 +++ b/usr.bin/netstat/netstat.1 @@ -114,7 +114,7 @@ Show statistics recorded by the memory management routines (the network manages a private pool of memory buffers). .It Fl N Extract the name list from the specified system instead of the default -.Pa /vmunix . +.Pa /386bsd . .It Fl n Show network addresses as numbers (normally .Nm netstat @@ -235,7 +235,7 @@ command appeared in .Bx 4.2 . .\" .Sh FILES .\" .Bl -tag -width /dev/kmem -compact -.\" .It Pa /vmunix +.\" .It Pa /386bsd .\" default kernel namelist .\" .It Pa /dev/kmem .\" default memory file diff --git a/usr.bin/netstat/route.c b/usr.bin/netstat/route.c index c645f7aad8..582f52c7e7 100644 --- a/usr.bin/netstat/route.c +++ b/usr.bin/netstat/route.c @@ -59,7 +59,7 @@ extern int nflag, aflag, Aflag, af; int do_rtent; extern char *routename(), *netname(), *plural(); #ifdef NS -exter char *ns_print(); +extern char *ns_print(); #endif extern char *malloc(); #define kget(p, d) \ @@ -84,6 +84,25 @@ struct bits { { 0 } }; +/* + * Print address family. + */ +void +p_proto(proto) + int proto; +{ + switch (proto) + { + case AF_INET: + printf("inet"); + break; + default: + printf("%d", proto); + break; + } +} + + /* * Print routing tables. */ @@ -169,8 +188,9 @@ off_t rtree; p_tree(head.rnh_treetop); } } else if (af == AF_UNSPEC || af == head.rnh_af) { - printf("\nRoute Tree for Protocol Family %d:\n", - head.rnh_af); + printf("\nRoute Tree for Protocol Family "); + p_proto(head.rnh_af); + printf(":\n"); do_rtent = 1; p_tree(head.rnh_treetop); } @@ -378,7 +398,7 @@ register struct rtentry *rt; struct ifnet ifnet; p_sockaddr(kgetsa(rt_key(rt)), rt->rt_flags, 16); - p_sockaddr(kgetsa(rt->rt_gateway), 0, 18); + p_sockaddr(kgetsa(rt->rt_gateway), RTF_HOST, 18); p_flags(rt->rt_flags, "%-6.6s "); printf("%6d %8d ", rt->rt_refcnt, rt->rt_use); if (rt->rt_ifp == 0) { diff --git a/usr.bin/netstat/unix.c b/usr.bin/netstat/unix.c index 695e30cc9f..a9225d536d 100644 --- a/usr.bin/netstat/unix.c +++ b/usr.bin/netstat/unix.c @@ -45,6 +45,8 @@ static char sccsid[] = "@(#)unix.c 5.11 (Berkeley) 7/1/91"; #include <sys/mbuf.h> #include <sys/un.h> #include <sys/unpcb.h> +#include <sys/time.h> +#include <sys/proc.h> #define KERNEL struct uio; #include <sys/file.h> diff --git a/usr.bin/nfsstat/nfsstat.1 b/usr.bin/nfsstat/nfsstat.1 index 7b8f058226..20ca91c522 100644 --- a/usr.bin/nfsstat/nfsstat.1 +++ b/usr.bin/nfsstat/nfsstat.1 @@ -58,7 +58,7 @@ instead of the default .Pa /dev/kmem . .It Fl N Extract the name list from the specified system instead of the default -.Pa /vmunix . +.Pa /386bsd . .It Fl w Display a shorter summary of .Tn NFS @@ -68,7 +68,7 @@ second intervals. .El .Sh FILES .Bl -tag -width /dev/kmem -compact -.It Pa /vmunix +.It Pa /386bsd default kernel namelist .It Pa /dev/kmem default memory file diff --git a/usr.bin/nohup/nohup.1 b/usr.bin/nohup/nohup.1 index a3601f1208..70eb8aac8b 100644 --- a/usr.bin/nohup/nohup.1 +++ b/usr.bin/nohup/nohup.1 @@ -42,7 +42,7 @@ .Nd invoke a command immune to hangups .Sh SYNOPSIS .Nm nohup -.Ar command +.Ar utility .Op Ar arg ... .Sh DESCRIPTION The @@ -53,20 +53,13 @@ with its arguments and at this time sets the signal .Dv SIGHUP -to be ignored. The signal -.Dv SIGQUIT -may also be set -to be ignored. +to be ignored. If the standard output is a terminal, the standard output is appended to the file .Pa nohup.out in the current directory. If standard error is a terminal, it is directed to the same place as the standard output. -.Pp -.Nm Nohup -exits 1 if an error occurs, otherwise the exit status is that of -.Ar command . .Sh ENVIRONMENT The following variable is utilized by .Nm nohup . @@ -80,6 +73,26 @@ utility uses the directory named by .Ev HOME to create the file. .El +.Sh DIAGNOSTICS +The +.Nm nohup +utility shall exit with one of the following values: +.Bl -tag -width Ds +.It 126 +The +.Ar utility +was found but could not be invoked. +.It 127 +The +.Ar utility +could not be found or an error occured in +.Nm nohup. +.El +.Pp +Otherwise, the exit status of +.Nm nohup +shall be that of +.Ar utility . .Sh SEE ALSO .Xr signal 3 .Sh STANDARDS diff --git a/usr.bin/nohup/nohup.c b/usr.bin/nohup/nohup.c index 7401dc0f86..b38b9f69fa 100644 --- a/usr.bin/nohup/nohup.c +++ b/usr.bin/nohup/nohup.c @@ -44,16 +44,31 @@ static char sccsid[] = "@(#)nohup.c 5.4 (Berkeley) 6/1/90"; #include <sys/param.h> #include <sys/signal.h> #include <sys/file.h> +#include <sys/stat.h> +#include <fcntl.h> #include <unistd.h> #include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> -extern int errno; +static void dofile(); +static void usage(); +/* nohup shall exit with one of the following values: + 126 - The utility was found but could not be invoked. + 127 - An error occured in the nohup utility, or the utility could + not be found. */ +#define EXIT_NOEXEC 126 +#define EXIT_NOTFOUND 127 +#define EXIT_MISC 127 + +int main(argc, argv) int argc; char **argv; { - char *strerror(); + int exit_status; if (argc < 2) usage(); @@ -63,49 +78,59 @@ main(argc, argv) if (isatty(STDERR_FILENO) && dup2(STDOUT_FILENO, STDERR_FILENO) == -1) { /* may have just closed stderr */ (void)fprintf(stdin, "nohup: %s\n", strerror(errno)); - exit(1); + exit(EXIT_MISC); } + /* The nohup utility shall take the standard action for all signals + except that SIGHUP shall be ignored. */ (void)signal(SIGHUP, SIG_IGN); - (void)signal(SIGQUIT, SIG_IGN); execvp(argv[1], &argv[1]); - (void)fprintf(stderr, - "nohup: %s: %s\n", argv[1], strerror(errno)); - exit(1); + exit_status = (errno = ENOENT) ? EXIT_NOTFOUND : EXIT_NOEXEC; + (void)fprintf(stderr, "nohup: %s: %s\n", argv[1], strerror(errno)); + exit(exit_status); } +static void dofile() { int fd; char *p, path[MAXPATHLEN]; - off_t lseek(); - char *getenv(), *strcpy(), *strcat(), *strerror(); + /* If the standard output is a terminal, all output written to + its standard output shall be appended to the end of the file + nohup.out in the current directory. If nohup.out cannot be + created or opened for appending, the output shall be appended + to the end of the file nohup.out in the directory specified + by the HOME environment variable. + + If a file is created, the file's permission bits shall be + set to S_IRUSR | S_IWUSR. */ #define FILENAME "nohup.out" p = FILENAME; - if ((fd = open(p, O_RDWR|O_CREAT, 0600)) >= 0) + if ((fd = open(p, O_RDWR|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR)) >= 0) goto dupit; - if (p = getenv("HOME")) { + if ((p = getenv("HOME")) != NULL) { (void)strcpy(path, p); (void)strcat(path, "/"); (void)strcat(path, FILENAME); - if ((fd = open(p = path, O_RDWR|O_CREAT, 0600)) >= 0) + if ((fd = open(p = path, O_RDWR|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR)) >= 0) goto dupit; } (void)fprintf(stderr, "nohup: can't open a nohup.out file.\n"); - exit(1); + exit(EXIT_MISC); dupit: (void)lseek(fd, 0L, SEEK_END); if (dup2(fd, STDOUT_FILENO) == -1) { (void)fprintf(stderr, "nohup: %s\n", strerror(errno)); - exit(1); + exit(EXIT_MISC); } (void)fprintf(stderr, "sending output to %s\n", p); } +static void usage() { (void)fprintf(stderr, "usage: nohup command\n"); - exit(1); + exit(EXIT_MISC); } diff --git a/usr.bin/pagesize/pagesize.1 b/usr.bin/pagesize/pagesize.1 index ae217d9fdd..dd5f8b75f4 100644 --- a/usr.bin/pagesize/pagesize.1 +++ b/usr.bin/pagesize/pagesize.1 @@ -31,8 +31,6 @@ .\" .\" @(#)pagesize.1 6.5 (Berkeley) 3/14/91 .\" -.Vx -.Vx .Dd March 14, 1991 .Dt PAGESIZE 1 .Os BSD 4.2 diff --git a/usr.bin/passwd/Makefile b/usr.bin/passwd/Makefile index c6d2e0fae5..3963a28f99 100644 --- a/usr.bin/passwd/Makefile +++ b/usr.bin/passwd/Makefile @@ -2,9 +2,17 @@ PROG= passwd SRCS= local_passwd.c passwd.c pw_copy.c pw_util.c +CFLAGS+=-I${.CURDIR} .PATH: ${.CURDIR}/../../usr.bin/chpass ${.CURDIR}/../../usr.sbin/vipw \ ${.CURDIR}/../rlogin -CFLAGS+=-I${.CURDIR} + +.if exists(/usr/lib/libcrypt.a) +#CFLAGS+=-DKERBEROS +DPADD+= ${LIBCRYPT} #${LIBKRB} +LDADD+= -lcrypt #-lkrb +.endif + + BINOWN= root BINMODE=4555 diff --git a/usr.bin/passwd/local_passwd.c b/usr.bin/passwd/local_passwd.c index 2e1eb538c1..a196127fb5 100644 --- a/usr.bin/passwd/local_passwd.c +++ b/usr.bin/passwd/local_passwd.c @@ -91,13 +91,9 @@ getnewpasswd(pw) (void)printf("Changing local password for %s.\n", pw->pw_name); - if (uid && pw->pw_passwd && -#ifdef DES + if (uid && pw->pw_passwd && *pw->pw_passwd && strcmp(crypt(getpass("Old password:"), pw->pw_passwd), pw->pw_passwd)) { -#else - strcmp(getpass("Old password:"), pw->pw_passwd)) { -#endif errno = EACCES; pw_error(NULL, 1, 1); } @@ -131,11 +127,7 @@ getnewpasswd(pw) #else to64(&salt[0], random(), 2); #endif -#ifdef DES return(crypt(buf, salt)); -#else - return(buf); -#endif } static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ diff --git a/usr.bin/printenv/Makefile b/usr.bin/printenv/Makefile index e6aac3898a..49367e9cf5 100644 --- a/usr.bin/printenv/Makefile +++ b/usr.bin/printenv/Makefile @@ -1,6 +1,5 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= printenv -MLINKS= printenv.1 env.1 .include <bsd.prog.mk> diff --git a/usr.bin/printenv/printenv.1 b/usr.bin/printenv/printenv.1 index 87634681bf..c865369441 100644 --- a/usr.bin/printenv/printenv.1 +++ b/usr.bin/printenv/printenv.1 @@ -35,17 +35,13 @@ .\" .Dd July 28, 1991 .Dt PRINTENV 1 -.Os BSD 3 +.Os .Sh NAME -.Nm printenv , env -.Nd print out the environment, set and print environment +.Nm printenv +.Nd print out the environment .Sh SYNOPSIS .Nm printenv .Op Ar name -.Nm env -.Op Fl -.Op Ar name=value ... -.Op Ar command .Sh DESCRIPTION .Nm Printenv prints out the names and values of the variables in the environment, @@ -59,40 +55,12 @@ If a is specified and it is not defined in the environment, .Nm printenv returns exit status 1, else it returns status 0. -.Pp -.Nm Env -executes -.Ar command -after modifying the environment as -specified on the command line. The option -.Ar name=value -specifies -an environmental variable, -.Ar name , -with a value of -.Ar value . -The option -.Sq Fl -causes -.Nm env -to completely ignore the environment -it inherits. -.Pp -If no command is specified, -.Nm env -prints out the names and values -of the variables in the environment, with one name/value pair per line. .Sh SEE ALSO .Xr csh 1 , .Xr sh 1 , -.Xr execvp 3 , .Xr environ 7 .Sh HISTORY The .Nm printenv command appeared in .Bx 3.0 . -.Sh BUGS -.Nm Env -doesn't handle commands with equal (``='') signs in their -names, for obvious reasons. diff --git a/usr.bin/ranlib/Makefile b/usr.bin/ranlib/Makefile index 4957300a00..0ed9749379 100644 --- a/usr.bin/ranlib/Makefile +++ b/usr.bin/ranlib/Makefile @@ -3,14 +3,8 @@ PROG= ranlib SRCS= archive.c build.c misc.c ranlib.c touch.c CFLAGS+=-I${.CURDIR} -I${.CURDIR}/../ar -MAN1= ranlib.0 +MAN1= ranlib.1 +MAN5= ranlib.5 VPATH= ${.CURDIR}/../ar -CLEANFILES=ranlib.5.0 - -ranlib.0: ranlib.5.0 - -afterinstall: - install -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} ranlib.5.0 \ - ${DESTDIR}${MANDIR}5/ranlib.0 .include <bsd.prog.mk> diff --git a/usr.bin/ranlib/ranlib.5.5 b/usr.bin/ranlib/ranlib.5 similarity index 100% rename from usr.bin/ranlib/ranlib.5.5 rename to usr.bin/ranlib/ranlib.5 diff --git a/usr.bin/rdist/server.c b/usr.bin/rdist/server.c index d129ab92ec..ca95322210 100644 --- a/usr.bin/rdist/server.c +++ b/usr.bin/rdist/server.c @@ -32,7 +32,8 @@ */ #ifndef lint -static char sccsid[] = "@(#)server.c 5.15 (Berkeley) 3/1/91"; +/*static char sccsid[] = "from: @(#)server.c 5.15 (Berkeley) 3/1/91";*/ +static char rcsid[] = "$Id"; #endif /* not lint */ #include "defs.h" @@ -1061,15 +1062,11 @@ chog(file, owner, group, mode) gid = -1; } ok: - if (userid) - setreuid(userid, 0); if (chown(file, uid, gid) < 0 || (mode & 07000) && chmod(file, mode) < 0) { note("%s: chown or chmod failed: file %s: %s", host, file, strerror(errno)); } - if (userid) - setreuid(0, userid); return(0); } diff --git a/usr.bin/renice/Makefile b/usr.bin/renice/Makefile index 3707af1d28..db7cf17fa8 100644 --- a/usr.bin/renice/Makefile +++ b/usr.bin/renice/Makefile @@ -1,5 +1,6 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= renice +MAN8= renice.8 .include <bsd.prog.mk> diff --git a/usr.bin/rev/Makefile b/usr.bin/rev/Makefile new file mode 100644 index 0000000000..d650c077ff --- /dev/null +++ b/usr.bin/rev/Makefile @@ -0,0 +1,5 @@ +# @(#)Makefile 5.4 (Berkeley) 3/21/92 + +PROG= rev + +.include <bsd.prog.mk> diff --git a/usr.bin/rev/rev.1 b/usr.bin/rev/rev.1 new file mode 100644 index 0000000000..d475aef11f --- /dev/null +++ b/usr.bin/rev/rev.1 @@ -0,0 +1,48 @@ +.\" Copyright (c) 1985, 1992 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rev.1 6.3 (Berkeley) 3/21/92 +.\" +.Dd March 21, 1992 +.Dt REV 1 +.Os +.Sh NAME +.Nm rev +.Nd reverse lines of a file +.Sh SYNOPSIS +.Nm rev +.Op Ar file +.Sh DESCRIPTION +The +.Nm rev +utility copies the specified files to the standard output, reversing the +order of characters in every line. +If no files are specified, the standard input is read. diff --git a/usr.bin/rev/rev.c b/usr.bin/rev/rev.c new file mode 100644 index 0000000000..bdbf3f4bca --- /dev/null +++ b/usr.bin/rev/rev.c @@ -0,0 +1,133 @@ +/*- + * Copyright (c) 1987, 1992 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1987, 1992 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rev.c 5.2 (Berkeley) 3/21/92"; +#endif /* not lint */ + +#include <sys/types.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void usage __P((void)); +void warn __P((const char *, ...)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register char *filename, *p, *t; + FILE *fp; + size_t len; + int ch, rval; + + while ((ch = getopt(argc, argv, "")) != EOF) + switch(ch) { + case '?': + default: + usage(); + } + + argc -= optind; + argv += optind; + + fp = stdin; + filename = "stdin"; + rval = 0; + do { + if (*argv) { + if ((fp = fopen(*argv, "r")) == NULL) { + warn("%s: %s", *argv, strerror(errno)); + rval = 1; + ++argv; + continue; + } + filename = *argv++; + } + while (p = fgetline(fp, &len)) { + t = p + len - 1; + for (t = p + len - 1; t >= p; --t) + putchar(*t); + putchar('\n'); + } + if (ferror(fp)) { + warn("%s: %s", filename, strerror(errno)); + rval = 1; + } + (void)fclose(fp); + } while(*argv); + exit(rval); +} + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +warn(const char *fmt, ...) +#else +warn(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "rev: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: rev [file ...]\n"); + exit(1); +} diff --git a/usr.bin/rlogin/Makefile b/usr.bin/rlogin/Makefile index f7761312bf..c4da477ed0 100644 --- a/usr.bin/rlogin/Makefile +++ b/usr.bin/rlogin/Makefile @@ -5,4 +5,10 @@ SRCS= rlogin.c BINOWN= root BINMODE=4555 +.if exists(/usr/lib/libcrypt.a) +#CFLAGS+=-DCRYPT -DKERBEROS +DPADD+= ${LIBCRYPT} #${LIBKRB} +LDADD+= -lcrypt #-lkrb +.endif + .include <bsd.prog.mk> diff --git a/usr.bin/rlogin/rlogin.c b/usr.bin/rlogin/rlogin.c index 2ba715c172..fedb615f55 100644 --- a/usr.bin/rlogin/rlogin.c +++ b/usr.bin/rlogin/rlogin.c @@ -42,7 +42,7 @@ static char sccsid[] = "@(#)rlogin.c 5.33 (Berkeley) 3/1/91"; #endif /* not lint */ /* - * $Source: mit/rlogin/RCS/rlogin.c,v $ + * $Source: /a/cvs/386BSD/src/usr.bin/rlogin/rlogin.c,v $ * $Header: mit/rlogin/RCS/rlogin.c,v 5.2 89/07/26 12:11:21 kfall * Exp Locker: kfall $ */ @@ -100,7 +100,7 @@ u_char escapechar = '~'; char *speeds[] = { "0", "50", "75", "110", "134", "150", "200", "300", "600", "1200", - "1800", "2400", "4800", "9600", "19200", "38400" + "1800", "2400", "4800", "9600", "19200", "38400", "57600", "115200" }; #ifdef sun diff --git a/usr.bin/rpcgen/Makefile b/usr.bin/rpcgen/Makefile new file mode 100644 index 0000000000..f3551adf06 --- /dev/null +++ b/usr.bin/rpcgen/Makefile @@ -0,0 +1,7 @@ +# $Id: Makefile,v 1.3 1993/08/23 21:21:49 jtc Exp $ + +PROG= rpcgen +SRCS= rpc_clntout.c rpc_cout.c rpc_hout.c rpc_main.c rpc_parse.c rpc_scan.c \ + rpc_svcout.c rpc_util.c + +.include <bsd.prog.mk> diff --git a/usr.bin/rpcgen/rpc_clntout.c b/usr.bin/rpcgen/rpc_clntout.c new file mode 100644 index 0000000000..bb92ccde68 --- /dev/null +++ b/usr.bin/rpcgen/rpc_clntout.c @@ -0,0 +1,130 @@ +/* @(#)rpc_clntout.c 2.1 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#ifndef lint +/*static char sccsid[] = "from: @(#)rpc_clntout.c 1.2 87/06/24 (C) 1987 SMI";*/ +static char rcsid[] = "$Id: rpc_clntout.c,v 1.2 1993/08/01 18:09:23 mycroft Exp $"; +#endif + +/* + * rpc_clntout.c, Client-stub outputter for the RPC protocol compiler + * Copyright (C) 1987, Sun Microsytsems, Inc. + */ +#include <stdio.h> +#include <strings.h> +#include "rpc_parse.h" +#include "rpc_util.h" + +#define DEFAULT_TIMEOUT 25 /* in seconds */ + +static int write_program(), printbody(); + + +void +write_stubs() +{ + list *l; + definition *def; + + f_print(fout, + "\n/* Default timeout can be changed using clnt_control() */\n"); + f_print(fout, "static struct timeval TIMEOUT = { %d, 0 };\n", + DEFAULT_TIMEOUT); + for (l = defined; l != NULL; l = l->next) { + def = (definition *) l->val; + if (def->def_kind == DEF_PROGRAM) { + write_program(def); + } + } +} + + +static +write_program(def) + definition *def; +{ + version_list *vp; + proc_list *proc; + + for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) { + for (proc = vp->procs; proc != NULL; proc = proc->next) { + f_print(fout, "\n"); + ptype(proc->res_prefix, proc->res_type, 1); + f_print(fout, "*\n"); + pvname(proc->proc_name, vp->vers_num); + f_print(fout, "(argp, clnt)\n"); + f_print(fout, "\t"); + ptype(proc->arg_prefix, proc->arg_type, 1); + f_print(fout, "*argp;\n"); + f_print(fout, "\tCLIENT *clnt;\n"); + f_print(fout, "{\n"); + printbody(proc); + f_print(fout, "}\n\n"); + } + } +} + +static char * +ampr(type) + char *type; +{ + if (isvectordef(type, REL_ALIAS)) { + return (""); + } else { + return ("&"); + } +} + +static +printbody(proc) + proc_list *proc; +{ + f_print(fout, "\tstatic "); + if (streq(proc->res_type, "void")) { + f_print(fout, "char "); + } else { + ptype(proc->res_prefix, proc->res_type, 0); + } + f_print(fout, "res;\n"); + f_print(fout, "\n"); + f_print(fout, "\tbzero((char *)%sres, sizeof(res));\n", + ampr(proc->res_type)); + f_print(fout, + "\tif (clnt_call(clnt, %s, xdr_%s, argp, xdr_%s, %sres, TIMEOUT) != RPC_SUCCESS) {\n", + proc->proc_name, stringfix(proc->arg_type), + stringfix(proc->res_type), ampr(proc->res_type)); + f_print(fout, "\t\treturn (NULL);\n"); + f_print(fout, "\t}\n"); + if (streq(proc->res_type, "void")) { + f_print(fout, "\treturn ((void *)%sres);\n", + ampr(proc->res_type)); + } else { + f_print(fout, "\treturn (%sres);\n", ampr(proc->res_type)); + } +} diff --git a/usr.bin/rpcgen/rpc_cout.c b/usr.bin/rpcgen/rpc_cout.c new file mode 100644 index 0000000000..1e9586d4c6 --- /dev/null +++ b/usr.bin/rpcgen/rpc_cout.c @@ -0,0 +1,355 @@ +/* @(#)rpc_cout.c 2.1 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#ifndef lint +/*static char sccsid[] = "from: @(#)rpc_cout.c 1.8 87/06/24 (C) 1987 SMI";*/ +static char rcsid[] = "$Id: rpc_cout.c,v 1.3 1993/08/01 18:09:22 mycroft Exp $"; +#endif + +/* + * rpc_cout.c, XDR routine outputter for the RPC protocol compiler + * Copyright (C) 1987, Sun Microsystems, Inc. + */ +#include <stdio.h> +#include <strings.h> +#include "rpc_util.h" +#include "rpc_parse.h" + +static int print_header(), print_trailer(), space(), emit_enum(), + emit_union(), emit_struct(), emit_typedef(), print_stat(); + + +/* + * Emit the C-routine for the given definition + */ +void +emit(def) + definition *def; +{ + if (def->def_kind == DEF_PROGRAM || def->def_kind == DEF_CONST) { + return; + } + print_header(def); + switch (def->def_kind) { + case DEF_UNION: + emit_union(def); + break; + case DEF_ENUM: + emit_enum(def); + break; + case DEF_STRUCT: + emit_struct(def); + break; + case DEF_TYPEDEF: + emit_typedef(def); + break; + } + print_trailer(); +} + +static +findtype(def, type) + definition *def; + char *type; +{ + if (def->def_kind == DEF_PROGRAM || def->def_kind == DEF_CONST) { + return (0); + } else { + return (streq(def->def_name, type)); + } +} + +static +undefined(type) + char *type; +{ + definition *def; + + def = (definition *) FINDVAL(defined, type, findtype); + return (def == NULL); +} + + +static +print_header(def) + definition *def; +{ + space(); + f_print(fout, "bool_t\n"); + f_print(fout, "xdr_%s(xdrs, objp)\n", def->def_name); + f_print(fout, "\tXDR *xdrs;\n"); + f_print(fout, "\t%s ", def->def_name); + if (def->def_kind != DEF_TYPEDEF || + !isvectordef(def->def.ty.old_type, def->def.ty.rel)) { + f_print(fout, "*"); + } + f_print(fout, "objp;\n"); + f_print(fout, "{\n"); +} + +static +print_trailer() +{ + f_print(fout, "\treturn (TRUE);\n"); + f_print(fout, "}\n"); + space(); +} + + +static +print_ifopen(indent, name) + int indent; + char *name; +{ + tabify(fout, indent); + f_print(fout, "if (!xdr_%s(xdrs", name); +} + + +static +print_ifarg(arg) + char *arg; +{ + f_print(fout, ", %s", arg); +} + + +static +print_ifsizeof(prefix, type) + char *prefix; + char *type; +{ + if (streq(type, "bool")) { + f_print(fout, ", sizeof(bool_t), xdr_bool"); + } else { + f_print(fout, ", sizeof("); + if (undefined(type) && prefix) { + f_print(fout, "%s ", prefix); + } + f_print(fout, "%s), xdr_%s", type, type); + } +} + +static +print_ifclose(indent) + int indent; +{ + f_print(fout, ")) {\n"); + tabify(fout, indent); + f_print(fout, "\treturn (FALSE);\n"); + tabify(fout, indent); + f_print(fout, "}\n"); +} + +static +space() +{ + f_print(fout, "\n\n"); +} + +static +print_ifstat(indent, prefix, type, rel, amax, objname, name) + int indent; + char *prefix; + char *type; + relation rel; + char *amax; + char *objname; + char *name; +{ + char *alt = NULL; + + switch (rel) { + case REL_POINTER: + print_ifopen(indent, "pointer"); + print_ifarg("(char **)"); + f_print(fout, "%s", objname); + print_ifsizeof(prefix, type); + break; + case REL_VECTOR: + if (streq(type, "string")) { + alt = "string"; + } else if (streq(type, "opaque")) { + alt = "opaque"; + } + if (alt) { + print_ifopen(indent, alt); + print_ifarg(objname); + } else { + print_ifopen(indent, "vector"); + print_ifarg("(char *)"); + f_print(fout, "%s", objname); + } + print_ifarg(amax); + if (!alt) { + print_ifsizeof(prefix, type); + } + break; + case REL_ARRAY: + if (streq(type, "string")) { + alt = "string"; + } else if (streq(type, "opaque")) { + alt = "bytes"; + } + if (streq(type, "string")) { + print_ifopen(indent, alt); + print_ifarg(objname); + } else { + if (alt) { + print_ifopen(indent, alt); + } else { + print_ifopen(indent, "array"); + } + print_ifarg("(char **)"); + if (*objname == '&') { + f_print(fout, "%s.%s_val, (u_int *)%s.%s_len", + objname, name, objname, name); + } else { + f_print(fout, "&%s->%s_val, (u_int *)&%s->%s_len", + objname, name, objname, name); + } + } + print_ifarg(amax); + if (!alt) { + print_ifsizeof(prefix, type); + } + break; + case REL_ALIAS: + print_ifopen(indent, type); + print_ifarg(objname); + break; + } + print_ifclose(indent); +} + + +/* ARGSUSED */ +static +emit_enum(def) + definition *def; +{ + print_ifopen(1, "enum"); + print_ifarg("(enum_t *)objp"); + print_ifclose(1); +} + + +static +emit_union(def) + definition *def; +{ + declaration *dflt; + case_list *cl; + declaration *cs; + char *object; + char *format = "&objp->%s_u.%s"; + + print_stat(&def->def.un.enum_decl); + f_print(fout, "\tswitch (objp->%s) {\n", def->def.un.enum_decl.name); + for (cl = def->def.un.cases; cl != NULL; cl = cl->next) { + cs = &cl->case_decl; + f_print(fout, "\tcase %s:\n", cl->case_name); + if (!streq(cs->type, "void")) { + object = alloc(strlen(def->def_name) + strlen(format) + + strlen(cs->name) + 1); + s_print(object, format, def->def_name, cs->name); + print_ifstat(2, cs->prefix, cs->type, cs->rel, cs->array_max, + object, cs->name); + free(object); + } + f_print(fout, "\t\tbreak;\n"); + } + dflt = def->def.un.default_decl; + if (dflt != NULL) { + if (!streq(dflt->type, "void")) { + f_print(fout, "\tdefault:\n"); + object = alloc(strlen(def->def_name) + strlen(format) + + strlen(dflt->name) + 1); + s_print(object, format, def->def_name, dflt->name); + print_ifstat(2, dflt->prefix, dflt->type, dflt->rel, + dflt->array_max, object, dflt->name); + free(object); + f_print(fout, "\t\tbreak;\n"); + } + } else { + f_print(fout, "\tdefault:\n"); + f_print(fout, "\t\treturn (FALSE);\n"); + } + f_print(fout, "\t}\n"); +} + + + +static +emit_struct(def) + definition *def; +{ + decl_list *dl; + + for (dl = def->def.st.decls; dl != NULL; dl = dl->next) { + print_stat(&dl->decl); + } +} + + + + +static +emit_typedef(def) + definition *def; +{ + char *prefix = def->def.ty.old_prefix; + char *type = def->def.ty.old_type; + char *amax = def->def.ty.array_max; + relation rel = def->def.ty.rel; + + print_ifstat(1, prefix, type, rel, amax, "objp", def->def_name); +} + + + + + +static +print_stat(dec) + declaration *dec; +{ + char *prefix = dec->prefix; + char *type = dec->type; + char *amax = dec->array_max; + relation rel = dec->rel; + char name[256]; + + if (isvectordef(type, rel)) { + s_print(name, "objp->%s", dec->name); + } else { + s_print(name, "&objp->%s", dec->name); + } + print_ifstat(1, prefix, type, rel, amax, name, dec->name); +} diff --git a/usr.bin/rpcgen/rpc_hout.c b/usr.bin/rpcgen/rpc_hout.c new file mode 100644 index 0000000000..567cdc9b5d --- /dev/null +++ b/usr.bin/rpcgen/rpc_hout.c @@ -0,0 +1,374 @@ +/* @(#)rpc_hout.c 2.1 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#ifndef lint +/*static char sccsid[] = "from: @(#)rpc_hout.c 1.6 87/07/28 (C) 1987 SMI";*/ +static char rcsid[] = "$Id: rpc_hout.c,v 1.3 1993/08/01 18:09:21 mycroft Exp $"; +#endif + +static int pconstdef(), pstructdef(), puniondef(), pdefine(), pprogramdef(), + penumdef(), ptypedef(), pdeclaration(), undefined2(); + +/* + * rpc_hout.c, Header file outputter for the RPC protocol compiler + * Copyright (C) 1987, Sun Microsystems, Inc. + */ +#include <stdio.h> +#include <ctype.h> +#include "rpc_util.h" +#include "rpc_parse.h" + + +/* + * Print the C-version of an xdr definition + */ +void +print_datadef(def) + definition *def; +{ + if (def->def_kind != DEF_CONST) { + f_print(fout, "\n"); + } + switch (def->def_kind) { + case DEF_STRUCT: + pstructdef(def); + break; + case DEF_UNION: + puniondef(def); + break; + case DEF_ENUM: + penumdef(def); + break; + case DEF_TYPEDEF: + ptypedef(def); + break; + case DEF_PROGRAM: + pprogramdef(def); + break; + case DEF_CONST: + pconstdef(def); + break; + } + if (def->def_kind != DEF_PROGRAM && def->def_kind != DEF_CONST) { + f_print(fout, "bool_t xdr_%s();\n", def->def_name); + } + if (def->def_kind != DEF_CONST) { + f_print(fout, "\n"); + } +} + +static +pconstdef(def) + definition *def; +{ + pdefine(def->def_name, def->def.co); +} + +static +pstructdef(def) + definition *def; +{ + decl_list *l; + char *name = def->def_name; + + f_print(fout, "struct %s {\n", name); + for (l = def->def.st.decls; l != NULL; l = l->next) { + pdeclaration(name, &l->decl, 1); + } + f_print(fout, "};\n"); + f_print(fout, "typedef struct %s %s;\n", name, name); +} + +static +puniondef(def) + definition *def; +{ + case_list *l; + char *name = def->def_name; + declaration *decl; + + f_print(fout, "struct %s {\n", name); + decl = &def->def.un.enum_decl; + if (streq(decl->type, "bool")) { + f_print(fout, "\tbool_t %s;\n", decl->name); + } else { + f_print(fout, "\t%s %s;\n", decl->type, decl->name); + } + f_print(fout, "\tunion {\n"); + for (l = def->def.un.cases; l != NULL; l = l->next) { + pdeclaration(name, &l->case_decl, 2); + } + decl = def->def.un.default_decl; + if (decl && !streq(decl->type, "void")) { + pdeclaration(name, decl, 2); + } + f_print(fout, "\t} %s_u;\n", name); + f_print(fout, "};\n"); + f_print(fout, "typedef struct %s %s;\n", name, name); +} + + + +static +pdefine(name, num) + char *name; + char *num; +{ + f_print(fout, "#define %s %s\n", name, num); +} + +static +puldefine(name, num) + char *name; + char *num; +{ + f_print(fout, "#define %s ((u_long)%s)\n", name, num); +} + +static +define_printed(stop, start) + proc_list *stop; + version_list *start; +{ + version_list *vers; + proc_list *proc; + + for (vers = start; vers != NULL; vers = vers->next) { + for (proc = vers->procs; proc != NULL; proc = proc->next) { + if (proc == stop) { + return (0); + } else if (streq(proc->proc_name, stop->proc_name)) { + return (1); + } + } + } + abort(); + /* NOTREACHED */ +} + + +static +pprogramdef(def) + definition *def; +{ + version_list *vers; + proc_list *proc; + + puldefine(def->def_name, def->def.pr.prog_num); + for (vers = def->def.pr.versions; vers != NULL; vers = vers->next) { + puldefine(vers->vers_name, vers->vers_num); + for (proc = vers->procs; proc != NULL; proc = proc->next) { + if (!define_printed(proc, def->def.pr.versions)) { + puldefine(proc->proc_name, proc->proc_num); + } + pprocdef(proc, vers); + } + } +} + + +pprocdef(proc, vp) + proc_list *proc; + version_list *vp; +{ + f_print(fout, "extern "); + if (proc->res_prefix) { + if (streq(proc->res_prefix, "enum")) { + f_print(fout, "enum "); + } else { + f_print(fout, "struct "); + } + } + if (streq(proc->res_type, "bool")) { + f_print(fout, "bool_t *"); + } else if (streq(proc->res_type, "string")) { + f_print(fout, "char **"); + } else { + f_print(fout, "%s *", fixtype(proc->res_type)); + } + pvname(proc->proc_name, vp->vers_num); + f_print(fout, "();\n"); +} + +static +penumdef(def) + definition *def; +{ + char *name = def->def_name; + enumval_list *l; + char *last = NULL; + int count = 0; + + f_print(fout, "enum %s {\n", name); + for (l = def->def.en.vals; l != NULL; l = l->next) { + f_print(fout, "\t%s", l->name); + if (l->assignment) { + f_print(fout, " = %s", l->assignment); + last = l->assignment; + count = 1; + } else { + if (last == NULL) { + f_print(fout, " = %d", count++); + } else { + f_print(fout, " = %s + %d", last, count++); + } + } + f_print(fout, ",\n"); + } + f_print(fout, "};\n"); + f_print(fout, "typedef enum %s %s;\n", name, name); +} + +static +ptypedef(def) + definition *def; +{ + char *name = def->def_name; + char *old = def->def.ty.old_type; + char prefix[8]; /* enough to contain "struct ", including NUL */ + relation rel = def->def.ty.rel; + + + if (!streq(name, old)) { + if (streq(old, "string")) { + old = "char"; + rel = REL_POINTER; + } else if (streq(old, "opaque")) { + old = "char"; + } else if (streq(old, "bool")) { + old = "bool_t"; + } + if (undefined2(old, name) && def->def.ty.old_prefix) { + s_print(prefix, "%s ", def->def.ty.old_prefix); + } else { + prefix[0] = 0; + } + f_print(fout, "typedef "); + switch (rel) { + case REL_ARRAY: + f_print(fout, "struct {\n"); + f_print(fout, "\tu_int %s_len;\n", name); + f_print(fout, "\t%s%s *%s_val;\n", prefix, old, name); + f_print(fout, "} %s", name); + break; + case REL_POINTER: + f_print(fout, "%s%s *%s", prefix, old, name); + break; + case REL_VECTOR: + f_print(fout, "%s%s %s[%s]", prefix, old, name, + def->def.ty.array_max); + break; + case REL_ALIAS: + f_print(fout, "%s%s %s", prefix, old, name); + break; + } + f_print(fout, ";\n"); + } +} + + +static +pdeclaration(name, dec, tab) + char *name; + declaration *dec; + int tab; +{ + char buf[8]; /* enough to hold "struct ", include NUL */ + char *prefix; + char *type; + + if (streq(dec->type, "void")) { + return; + } + tabify(fout, tab); + if (streq(dec->type, name) && !dec->prefix) { + f_print(fout, "struct "); + } + if (streq(dec->type, "string")) { + f_print(fout, "char *%s", dec->name); + } else { + prefix = ""; + if (streq(dec->type, "bool")) { + type = "bool_t"; + } else if (streq(dec->type, "opaque")) { + type = "char"; + } else { + if (dec->prefix) { + s_print(buf, "%s ", dec->prefix); + prefix = buf; + } + type = dec->type; + } + switch (dec->rel) { + case REL_ALIAS: + f_print(fout, "%s%s %s", prefix, type, dec->name); + break; + case REL_VECTOR: + f_print(fout, "%s%s %s[%s]", prefix, type, dec->name, + dec->array_max); + break; + case REL_POINTER: + f_print(fout, "%s%s *%s", prefix, type, dec->name); + break; + case REL_ARRAY: + f_print(fout, "struct {\n"); + tabify(fout, tab); + f_print(fout, "\tu_int %s_len;\n", dec->name); + tabify(fout, tab); + f_print(fout, "\t%s%s *%s_val;\n", prefix, type, dec->name); + tabify(fout, tab); + f_print(fout, "} %s", dec->name); + break; + } + } + f_print(fout, ";\n"); +} + + + +static +undefined2(type, stop) + char *type; + char *stop; +{ + list *l; + definition *def; + + for (l = defined; l != NULL; l = l->next) { + def = (definition *) l->val; + if (def->def_kind != DEF_PROGRAM) { + if (streq(def->def_name, stop)) { + return (1); + } else if (streq(def->def_name, type)) { + return (0); + } + } + } + return (1); +} diff --git a/usr.bin/rpcgen/rpc_main.c b/usr.bin/rpcgen/rpc_main.c new file mode 100644 index 0000000000..10689cefaa --- /dev/null +++ b/usr.bin/rpcgen/rpc_main.c @@ -0,0 +1,438 @@ +/* @(#)rpc_main.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#ifndef lint +/*static char sccsid[] = "from: @(#)rpc_main.c 1.7 87/06/24 (C) 1987 SMI";*/ +static char rcsid[] = "$Id: rpc_main.c,v 1.4 1993/08/01 18:09:20 mycroft Exp $"; +#endif + +/* + * rpc_main.c, Top level of the RPC protocol compiler. + * Copyright (C) 1987, Sun Microsystems, Inc. + */ + +#include <stdio.h> +#include <strings.h> +#include <sys/file.h> +#include "rpc_util.h" +#include "rpc_parse.h" +#include "rpc_scan.h" + +#define EXTEND 1 /* alias for TRUE */ + +struct commandline { + int cflag; + int hflag; + int lflag; + int sflag; + int mflag; + char *infile; + char *outfile; +}; + +static char *cmdname; +static char CPP[] = "/usr/bin/cpp"; +static char CPPFLAGS[] = "-C"; +static char *allv[] = { + "rpcgen", "-s", "udp", "-s", "tcp", +}; +static int allc = sizeof(allv)/sizeof(allv[0]); + + +static int h_output(), c_output(), s_output(), l_output(), do_registers(), + parseargs(); + +main(argc, argv) + int argc; + char *argv[]; + +{ + struct commandline cmd; + + if (!parseargs(argc, argv, &cmd)) { + f_print(stderr, + "usage: %s infile\n", cmdname); + f_print(stderr, + " %s [-c | -h | -l | -m] [-o outfile] [infile]\n", + cmdname); + f_print(stderr, + " %s [-s udp|tcp]* [-o outfile] [infile]\n", + cmdname); + exit(1); + } + if (cmd.cflag) { + c_output(cmd.infile, "-DRPC_XDR", !EXTEND, cmd.outfile); + } else if (cmd.hflag) { + h_output(cmd.infile, "-DRPC_HDR", !EXTEND, cmd.outfile); + } else if (cmd.lflag) { + l_output(cmd.infile, "-DRPC_CLNT", !EXTEND, cmd.outfile); + } else if (cmd.sflag || cmd.mflag) { + s_output(argc, argv, cmd.infile, "-DRPC_SVC", !EXTEND, + cmd.outfile, cmd.mflag); + } else { + c_output(cmd.infile, "-DRPC_XDR", EXTEND, "_xdr.c"); + reinitialize(); + h_output(cmd.infile, "-DRPC_HDR", EXTEND, ".h"); + reinitialize(); + l_output(cmd.infile, "-DRPC_CLNT", EXTEND, "_clnt.c"); + reinitialize(); + s_output(allc, allv, cmd.infile, "-DRPC_SVC", EXTEND, + "_svc.c", cmd.mflag); + } + exit(0); +} + +/* + * add extension to filename + */ +static char * +extendfile(file, ext) + char *file; + char *ext; +{ + char *res; + char *p; + + res = alloc(strlen(file) + strlen(ext) + 1); + if (res == NULL) { + abort(); + } + p = rindex(file, '.'); + if (p == NULL) { + p = file + strlen(file); + } + (void) strcpy(res, file); + (void) strcpy(res + (p - file), ext); + return (res); +} + +/* + * Open output file with given extension + */ +static +open_output(infile, outfile) + char *infile; + char *outfile; +{ + if (outfile == NULL) { + fout = stdout; + return; + } + if (infile != NULL && streq(outfile, infile)) { + f_print(stderr, "%s: output would overwrite %s\n", cmdname, + infile); + crash(); + } + fout = fopen(outfile, "w"); + if (fout == NULL) { + f_print(stderr, "%s: unable to open ", cmdname); + perror(outfile); + crash(); + } + record_open(outfile); +} + +/* + * Open input file with given define for C-preprocessor + */ +static +open_input(infile, define) + char *infile; + char *define; +{ + int pd[2]; + + infilename = (infile == NULL) ? "<stdin>" : infile; + (void) pipe(pd); + switch (fork()) { + case 0: + (void) close(1); + (void) dup2(pd[1], 1); + (void) close(pd[0]); + execl(CPP, CPP, CPPFLAGS, define, infile, NULL); + perror("execl"); + exit(1); + case -1: + perror("fork"); + exit(1); + } + (void) close(pd[1]); + fin = fdopen(pd[0], "r"); + if (fin == NULL) { + f_print(stderr, "%s: ", cmdname); + perror(infilename); + crash(); + } +} + +/* + * Compile into an XDR routine output file + */ +static +c_output(infile, define, extend, outfile) + char *infile; + char *define; + int extend; + char *outfile; +{ + definition *def; + char *include; + char *outfilename; + long tell; + + open_input(infile, define); + outfilename = extend ? extendfile(infile, outfile) : outfile; + open_output(infile, outfilename); + f_print(fout, "#include <rpc/rpc.h>\n"); + if (infile && (include = extendfile(infile, ".h"))) { + f_print(fout, "#include \"%s\"\n", include); + free(include); + } + tell = ftell(fout); + while (def = get_definition()) { + emit(def); + } + if (extend && tell == ftell(fout)) { + (void) unlink(outfilename); + } +} + +/* + * Compile into an XDR header file + */ +static +h_output(infile, define, extend, outfile) + char *infile; + char *define; + int extend; + char *outfile; +{ + definition *def; + char *outfilename; + long tell; + + open_input(infile, define); + outfilename = extend ? extendfile(infile, outfile) : outfile; + open_output(infile, outfilename); + tell = ftell(fout); + while (def = get_definition()) { + print_datadef(def); + } + if (extend && tell == ftell(fout)) { + (void) unlink(outfilename); + } +} + +/* + * Compile into an RPC service + */ +static +s_output(argc, argv, infile, define, extend, outfile, nomain) + int argc; + char *argv[]; + char *infile; + char *define; + int extend; + char *outfile; + int nomain; +{ + char *include; + definition *def; + int foundprogram; + char *outfilename; + + open_input(infile, define); + outfilename = extend ? extendfile(infile, outfile) : outfile; + open_output(infile, outfilename); + f_print(fout, "#include <stdio.h>\n"); + f_print(fout, "#include <rpc/rpc.h>\n"); + if (infile && (include = extendfile(infile, ".h"))) { + f_print(fout, "#include \"%s\"\n", include); + free(include); + } + foundprogram = 0; + while (def = get_definition()) { + foundprogram |= (def->def_kind == DEF_PROGRAM); + } + if (extend && !foundprogram) { + (void) unlink(outfilename); + return; + } + if (nomain) { + write_programs((char *)NULL); + } else { + write_most(); + do_registers(argc, argv); + write_rest(); + write_programs("static"); + } +} + +static +l_output(infile, define, extend, outfile) + char *infile; + char *define; + int extend; + char *outfile; +{ + char *include; + definition *def; + int foundprogram; + char *outfilename; + + open_input(infile, define); + outfilename = extend ? extendfile(infile, outfile) : outfile; + open_output(infile, outfilename); + f_print(fout, "#include <rpc/rpc.h>\n"); + if (infile && (include = extendfile(infile, ".h"))) { + f_print(fout, "#include \"%s\"\n", include); + free(include); + } + foundprogram = 0; + while (def = get_definition()) { + foundprogram |= (def->def_kind == DEF_PROGRAM); + } + if (extend && !foundprogram) { + (void) unlink(outfilename); + return; + } + write_stubs(); +} + +/* + * Perform registrations for service output + */ +static +do_registers(argc, argv) + int argc; + char *argv[]; + +{ + int i; + + for (i = 1; i < argc; i++) { + if (streq(argv[i], "-s")) { + write_register(argv[i + 1]); + i++; + } + } +} + +/* + * Parse command line arguments + */ +static +parseargs(argc, argv, cmd) + int argc; + char *argv[]; + struct commandline *cmd; + +{ + int i; + int j; + char c; + char flag[(1 << 8 * sizeof(char))]; + int nflags; + + cmdname = argv[0]; + cmd->infile = cmd->outfile = NULL; + if (argc < 2) { + return (0); + } + flag['c'] = 0; + flag['h'] = 0; + flag['s'] = 0; + flag['o'] = 0; + flag['l'] = 0; + flag['m'] = 0; + for (i = 1; i < argc; i++) { + if (argv[i][0] != '-') { + if (cmd->infile) { + return (0); + } + cmd->infile = argv[i]; + } else { + for (j = 1; argv[i][j] != 0; j++) { + c = argv[i][j]; + switch (c) { + case 'c': + case 'h': + case 'l': + case 'm': + if (flag[c]) { + return (0); + } + flag[c] = 1; + break; + case 'o': + case 's': + if (argv[i][j - 1] != '-' || + argv[i][j + 1] != 0) { + return (0); + } + flag[c] = 1; + if (++i == argc) { + return (0); + } + if (c == 's') { + if (!streq(argv[i], "udp") && + !streq(argv[i], "tcp")) { + return (0); + } + } else if (c == 'o') { + if (cmd->outfile) { + return (0); + } + cmd->outfile = argv[i]; + } + goto nextarg; + + default: + return (0); + } + } + nextarg: + ; + } + } + cmd->cflag = flag['c']; + cmd->hflag = flag['h']; + cmd->sflag = flag['s']; + cmd->lflag = flag['l']; + cmd->mflag = flag['m']; + nflags = cmd->cflag + cmd->hflag + cmd->sflag + cmd->lflag + cmd->mflag; + if (nflags == 0) { + if (cmd->outfile != NULL || cmd->infile == NULL) { + return (0); + } + } else if (nflags > 1) { + return (0); + } + return (1); +} diff --git a/usr.bin/rpcgen/rpc_parse.c b/usr.bin/rpcgen/rpc_parse.c new file mode 100644 index 0000000000..b1ef10ce9e --- /dev/null +++ b/usr.bin/rpcgen/rpc_parse.c @@ -0,0 +1,423 @@ +/* @(#)rpc_parse.c 2.1 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#ifndef lint +/*static char sccsid[] = "from: @(#)rpc_parse.c 1.4 87/04/28 (C) 1987 SMI";*/ +static char rcsid[] = "$Id: rpc_parse.c,v 1.3 1993/08/01 18:09:19 mycroft Exp $"; +#endif + +/* + * rpc_parse.c, Parser for the RPC protocol compiler + * Copyright (C) 1987 Sun Microsystems, Inc. + */ +#include <stdio.h> +#include "rpc_util.h" +#include "rpc_scan.h" +#include "rpc_parse.h" + +static int isdefined(), def_struct(), def_program(), def_enum(), def_const(), + def_union(), def_typedef(), get_declaration(), get_type(), + unsigned_dec(); +/* + * return the next definition you see + */ +definition * +get_definition() +{ + definition *defp; + token tok; + + defp = ALLOC(definition); + get_token(&tok); + switch (tok.kind) { + case TOK_STRUCT: + def_struct(defp); + break; + case TOK_UNION: + def_union(defp); + break; + case TOK_TYPEDEF: + def_typedef(defp); + break; + case TOK_ENUM: + def_enum(defp); + break; + case TOK_PROGRAM: + def_program(defp); + break; + case TOK_CONST: + def_const(defp); + break; + case TOK_EOF: + return (NULL); + break; + default: + error("definition keyword expected"); + } + scan(TOK_SEMICOLON, &tok); + isdefined(defp); + return (defp); +} + +static +isdefined(defp) + definition *defp; +{ + STOREVAL(&defined, defp); +} + + +static +def_struct(defp) + definition *defp; +{ + token tok; + declaration dec; + decl_list *decls; + decl_list **tailp; + + defp->def_kind = DEF_STRUCT; + + scan(TOK_IDENT, &tok); + defp->def_name = tok.str; + scan(TOK_LBRACE, &tok); + tailp = &defp->def.st.decls; + do { + get_declaration(&dec, DEF_STRUCT); + decls = ALLOC(decl_list); + decls->decl = dec; + *tailp = decls; + tailp = &decls->next; + scan(TOK_SEMICOLON, &tok); + peek(&tok); + } while (tok.kind != TOK_RBRACE); + get_token(&tok); + *tailp = NULL; +} + +static +def_program(defp) + definition *defp; +{ + token tok; + version_list *vlist; + version_list **vtailp; + proc_list *plist; + proc_list **ptailp; + + defp->def_kind = DEF_PROGRAM; + scan(TOK_IDENT, &tok); + defp->def_name = tok.str; + scan(TOK_LBRACE, &tok); + vtailp = &defp->def.pr.versions; + scan(TOK_VERSION, &tok); + do { + scan(TOK_IDENT, &tok); + vlist = ALLOC(version_list); + vlist->vers_name = tok.str; + scan(TOK_LBRACE, &tok); + ptailp = &vlist->procs; + do { + plist = ALLOC(proc_list); + get_type(&plist->res_prefix, &plist->res_type, DEF_PROGRAM); + if (streq(plist->res_type, "opaque")) { + error("illegal result type"); + } + scan(TOK_IDENT, &tok); + plist->proc_name = tok.str; + scan(TOK_LPAREN, &tok); + get_type(&plist->arg_prefix, &plist->arg_type, DEF_PROGRAM); + if (streq(plist->arg_type, "opaque")) { + error("illegal argument type"); + } + scan(TOK_RPAREN, &tok); + scan(TOK_EQUAL, &tok); + scan_num(&tok); + scan(TOK_SEMICOLON, &tok); + plist->proc_num = tok.str; + *ptailp = plist; + ptailp = &plist->next; + peek(&tok); + } while (tok.kind != TOK_RBRACE); + *vtailp = vlist; + vtailp = &vlist->next; + scan(TOK_RBRACE, &tok); + scan(TOK_EQUAL, &tok); + scan_num(&tok); + vlist->vers_num = tok.str; + scan(TOK_SEMICOLON, &tok); + scan2(TOK_VERSION, TOK_RBRACE, &tok); + } while (tok.kind == TOK_VERSION); + scan(TOK_EQUAL, &tok); + scan_num(&tok); + defp->def.pr.prog_num = tok.str; + *vtailp = NULL; +} + +static +def_enum(defp) + definition *defp; +{ + token tok; + enumval_list *elist; + enumval_list **tailp; + + defp->def_kind = DEF_ENUM; + scan(TOK_IDENT, &tok); + defp->def_name = tok.str; + scan(TOK_LBRACE, &tok); + tailp = &defp->def.en.vals; + do { + scan(TOK_IDENT, &tok); + elist = ALLOC(enumval_list); + elist->name = tok.str; + elist->assignment = NULL; + scan3(TOK_COMMA, TOK_RBRACE, TOK_EQUAL, &tok); + if (tok.kind == TOK_EQUAL) { + scan_num(&tok); + elist->assignment = tok.str; + scan2(TOK_COMMA, TOK_RBRACE, &tok); + } + *tailp = elist; + tailp = &elist->next; + } while (tok.kind != TOK_RBRACE); + *tailp = NULL; +} + +static +def_const(defp) + definition *defp; +{ + token tok; + + defp->def_kind = DEF_CONST; + scan(TOK_IDENT, &tok); + defp->def_name = tok.str; + scan(TOK_EQUAL, &tok); + scan2(TOK_IDENT, TOK_STRCONST, &tok); + defp->def.co = tok.str; +} + +static +def_union(defp) + definition *defp; +{ + token tok; + declaration dec; + case_list *cases; + case_list **tailp; + + defp->def_kind = DEF_UNION; + scan(TOK_IDENT, &tok); + defp->def_name = tok.str; + scan(TOK_SWITCH, &tok); + scan(TOK_LPAREN, &tok); + get_declaration(&dec, DEF_UNION); + defp->def.un.enum_decl = dec; + tailp = &defp->def.un.cases; + scan(TOK_RPAREN, &tok); + scan(TOK_LBRACE, &tok); + scan(TOK_CASE, &tok); + while (tok.kind == TOK_CASE) { + scan(TOK_IDENT, &tok); + cases = ALLOC(case_list); + cases->case_name = tok.str; + scan(TOK_COLON, &tok); + get_declaration(&dec, DEF_UNION); + cases->case_decl = dec; + *tailp = cases; + tailp = &cases->next; + scan(TOK_SEMICOLON, &tok); + scan3(TOK_CASE, TOK_DEFAULT, TOK_RBRACE, &tok); + } + *tailp = NULL; + if (tok.kind == TOK_DEFAULT) { + scan(TOK_COLON, &tok); + get_declaration(&dec, DEF_UNION); + defp->def.un.default_decl = ALLOC(declaration); + *defp->def.un.default_decl = dec; + scan(TOK_SEMICOLON, &tok); + scan(TOK_RBRACE, &tok); + } else { + defp->def.un.default_decl = NULL; + } +} + + +static +def_typedef(defp) + definition *defp; +{ + declaration dec; + + defp->def_kind = DEF_TYPEDEF; + get_declaration(&dec, DEF_TYPEDEF); + defp->def_name = dec.name; + defp->def.ty.old_prefix = dec.prefix; + defp->def.ty.old_type = dec.type; + defp->def.ty.rel = dec.rel; + defp->def.ty.array_max = dec.array_max; +} + + +static +get_declaration(dec, dkind) + declaration *dec; + defkind dkind; +{ + token tok; + + get_type(&dec->prefix, &dec->type, dkind); + dec->rel = REL_ALIAS; + if (streq(dec->type, "void")) { + return; + } + scan2(TOK_STAR, TOK_IDENT, &tok); + if (tok.kind == TOK_STAR) { + dec->rel = REL_POINTER; + scan(TOK_IDENT, &tok); + } + dec->name = tok.str; + if (peekscan(TOK_LBRACKET, &tok)) { + if (dec->rel == REL_POINTER) { + error("no array-of-pointer declarations -- use typedef"); + } + dec->rel = REL_VECTOR; + scan_num(&tok); + dec->array_max = tok.str; + scan(TOK_RBRACKET, &tok); + } else if (peekscan(TOK_LANGLE, &tok)) { + if (dec->rel == REL_POINTER) { + error("no array-of-pointer declarations -- use typedef"); + } + dec->rel = REL_ARRAY; + if (peekscan(TOK_RANGLE, &tok)) { + dec->array_max = "~0"; /* unspecified size, use max */ + } else { + scan_num(&tok); + dec->array_max = tok.str; + scan(TOK_RANGLE, &tok); + } + } + if (streq(dec->type, "opaque")) { + if (dec->rel != REL_ARRAY && dec->rel != REL_VECTOR) { + error("array declaration expected"); + } + } else if (streq(dec->type, "string")) { + if (dec->rel != REL_ARRAY) { + error("variable-length array declaration expected"); + } + } +} + + +static +get_type(prefixp, typep, dkind) + char **prefixp; + char **typep; + defkind dkind; +{ + token tok; + + *prefixp = NULL; + get_token(&tok); + switch (tok.kind) { + case TOK_IDENT: + *typep = tok.str; + break; + case TOK_STRUCT: + case TOK_ENUM: + case TOK_UNION: + *prefixp = tok.str; + scan(TOK_IDENT, &tok); + *typep = tok.str; + break; + case TOK_UNSIGNED: + unsigned_dec(typep); + break; + case TOK_SHORT: + *typep = "short"; + (void) peekscan(TOK_INT, &tok); + break; + case TOK_LONG: + *typep = "long"; + (void) peekscan(TOK_INT, &tok); + break; + case TOK_VOID: + if (dkind != DEF_UNION && dkind != DEF_PROGRAM) { + error("voids allowed only inside union and program definitions"); + } + *typep = tok.str; + break; + case TOK_STRING: + case TOK_OPAQUE: + case TOK_CHAR: + case TOK_INT: + case TOK_FLOAT: + case TOK_DOUBLE: + case TOK_BOOL: + *typep = tok.str; + break; + default: + error("expected type specifier"); + } +} + + +static +unsigned_dec(typep) + char **typep; +{ + token tok; + + peek(&tok); + switch (tok.kind) { + case TOK_CHAR: + get_token(&tok); + *typep = "u_char"; + break; + case TOK_SHORT: + get_token(&tok); + *typep = "u_short"; + (void) peekscan(TOK_INT, &tok); + break; + case TOK_LONG: + get_token(&tok); + *typep = "u_long"; + (void) peekscan(TOK_INT, &tok); + break; + case TOK_INT: + get_token(&tok); + *typep = "u_int"; + break; + default: + *typep = "u_int"; + break; + } +} diff --git a/usr.bin/rpcgen/rpc_parse.h b/usr.bin/rpcgen/rpc_parse.h new file mode 100644 index 0000000000..2cb13a7ba0 --- /dev/null +++ b/usr.bin/rpcgen/rpc_parse.h @@ -0,0 +1,159 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + * + * from: @(#)rpc_parse.h 1.3 87/03/09 (C) 1987 SMI + * $Id: rpc_parse.h,v 1.2 1993/08/01 18:09:25 mycroft Exp $ + */ + +/* + * rpc_parse.h, Definitions for the RPCL parser + * Copyright (C) 1987, Sun Microsystems, Inc. + */ + +enum defkind { + DEF_CONST, + DEF_STRUCT, + DEF_UNION, + DEF_ENUM, + DEF_TYPEDEF, + DEF_PROGRAM +}; +typedef enum defkind defkind; + +typedef char *const_def; + +enum relation { + REL_VECTOR, /* fixed length array */ + REL_ARRAY, /* variable length array */ + REL_POINTER, /* pointer */ + REL_ALIAS, /* simple */ +}; +typedef enum relation relation; + +struct typedef_def { + char *old_prefix; + char *old_type; + relation rel; + char *array_max; +}; +typedef struct typedef_def typedef_def; + + +struct enumval_list { + char *name; + char *assignment; + struct enumval_list *next; +}; +typedef struct enumval_list enumval_list; + +struct enum_def { + enumval_list *vals; +}; +typedef struct enum_def enum_def; + + +struct declaration { + char *prefix; + char *type; + char *name; + relation rel; + char *array_max; +}; +typedef struct declaration declaration; + + +struct decl_list { + declaration decl; + struct decl_list *next; +}; +typedef struct decl_list decl_list; + +struct struct_def { + decl_list *decls; +}; +typedef struct struct_def struct_def; + + +struct case_list { + char *case_name; + declaration case_decl; + struct case_list *next; +}; +typedef struct case_list case_list; + +struct union_def { + declaration enum_decl; + case_list *cases; + declaration *default_decl; +}; +typedef struct union_def union_def; + + + +struct proc_list { + char *proc_name; + char *proc_num; + char *arg_type; + char *arg_prefix; + char *res_type; + char *res_prefix; + struct proc_list *next; +}; +typedef struct proc_list proc_list; + + +struct version_list { + char *vers_name; + char *vers_num; + proc_list *procs; + struct version_list *next; +}; +typedef struct version_list version_list; + +struct program_def { + char *prog_num; + version_list *versions; +}; +typedef struct program_def program_def; + +struct definition { + char *def_name; + defkind def_kind; + union { + const_def co; + struct_def st; + union_def un; + enum_def en; + typedef_def ty; + program_def pr; + } def; +}; +typedef struct definition definition; + +/* @(#)rpc_parse.h 2.1 88/08/01 4.0 RPCSRC */ +definition *get_definition(); diff --git a/usr.bin/rpcgen/rpc_scan.c b/usr.bin/rpcgen/rpc_scan.c new file mode 100644 index 0000000000..dc440fed47 --- /dev/null +++ b/usr.bin/rpcgen/rpc_scan.c @@ -0,0 +1,476 @@ +/* @(#)rpc_scan.c 2.1 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#ifndef lint +/*static char sccsid[] = "from: @(#)rpc_scan.c 1.6 87/06/24 (C) 1987 SMI";*/ +static char rcsid[] = "$Id: rpc_scan.c,v 1.3 1993/08/01 18:09:18 mycroft Exp $"; +#endif + +/* + * rpc_scan.c, Scanner for the RPC protocol compiler + * Copyright (C) 1987, Sun Microsystems, Inc. + */ +#include <stdio.h> +#include <ctype.h> +#include <strings.h> +#include "rpc_scan.h" +#include "rpc_util.h" + +#define startcomment(where) (where[0] == '/' && where[1] == '*') +#define endcomment(where) (where[-1] == '*' && where[0] == '/') + +static int pushed = 0; /* is a token pushed */ +static token lasttok; /* last token, if pushed */ + +static int unget_token(), findstrconst(), findconst(), findkind(), cppline(), + directive(), printdirective(), docppline(); +/* + * scan expecting 1 given token + */ +void +scan(expect, tokp) + tok_kind expect; + token *tokp; +{ + get_token(tokp); + if (tokp->kind != expect) { + expected1(expect); + } +} + +/* + * scan expecting 2 given tokens + */ +void +scan2(expect1, expect2, tokp) + tok_kind expect1; + tok_kind expect2; + token *tokp; +{ + get_token(tokp); + if (tokp->kind != expect1 && tokp->kind != expect2) { + expected2(expect1, expect2); + } +} + +/* + * scan expecting 3 given token + */ +void +scan3(expect1, expect2, expect3, tokp) + tok_kind expect1; + tok_kind expect2; + tok_kind expect3; + token *tokp; +{ + get_token(tokp); + if (tokp->kind != expect1 && tokp->kind != expect2 + && tokp->kind != expect3) { + expected3(expect1, expect2, expect3); + } +} + + +/* + * scan expecting a constant, possibly symbolic + */ +void +scan_num(tokp) + token *tokp; +{ + get_token(tokp); + switch (tokp->kind) { + case TOK_IDENT: + break; + default: + error("constant or identifier expected"); + } +} + + +/* + * Peek at the next token + */ +void +peek(tokp) + token *tokp; +{ + get_token(tokp); + unget_token(tokp); +} + + +/* + * Peek at the next token and scan it if it matches what you expect + */ +int +peekscan(expect, tokp) + tok_kind expect; + token *tokp; +{ + peek(tokp); + if (tokp->kind == expect) { + get_token(tokp); + return (1); + } + return (0); +} + + + +/* + * Get the next token, printing out any directive that are encountered. + */ +void +get_token(tokp) + token *tokp; +{ + int commenting; + + if (pushed) { + pushed = 0; + *tokp = lasttok; + return; + } + commenting = 0; + for (;;) { + if (*where == 0) { + for (;;) { + if (!fgets(curline, MAXLINESIZE, fin)) { + tokp->kind = TOK_EOF; + *where = 0; + return; + } + linenum++; + if (commenting) { + break; + } else if (cppline(curline)) { + docppline(curline, &linenum, + &infilename); + } else if (directive(curline)) { + printdirective(curline); + } else { + break; + } + } + where = curline; + } else if (isspace(*where)) { + while (isspace(*where)) { + where++; /* eat */ + } + } else if (commenting) { + where++; + if (endcomment(where)) { + where++; + commenting--; + } + } else if (startcomment(where)) { + where += 2; + commenting++; + } else { + break; + } + } + + /* + * 'where' is not whitespace, comment or directive Must be a token! + */ + switch (*where) { + case ':': + tokp->kind = TOK_COLON; + where++; + break; + case ';': + tokp->kind = TOK_SEMICOLON; + where++; + break; + case ',': + tokp->kind = TOK_COMMA; + where++; + break; + case '=': + tokp->kind = TOK_EQUAL; + where++; + break; + case '*': + tokp->kind = TOK_STAR; + where++; + break; + case '[': + tokp->kind = TOK_LBRACKET; + where++; + break; + case ']': + tokp->kind = TOK_RBRACKET; + where++; + break; + case '{': + tokp->kind = TOK_LBRACE; + where++; + break; + case '}': + tokp->kind = TOK_RBRACE; + where++; + break; + case '(': + tokp->kind = TOK_LPAREN; + where++; + break; + case ')': + tokp->kind = TOK_RPAREN; + where++; + break; + case '<': + tokp->kind = TOK_LANGLE; + where++; + break; + case '>': + tokp->kind = TOK_RANGLE; + where++; + break; + + case '"': + tokp->kind = TOK_STRCONST; + findstrconst(&where, &tokp->str); + break; + + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + tokp->kind = TOK_IDENT; + findconst(&where, &tokp->str); + break; + + + default: + if (!(isalpha(*where) || *where == '_')) { + char buf[100]; + char *p; + + s_print(buf, "illegal character in file: "); + p = buf + strlen(buf); + if (isprint(*where)) { + s_print(p, "%c", *where); + } else { + s_print(p, "%d", *where); + } + error(buf); + } + findkind(&where, tokp); + break; + } +} + + + +static +unget_token(tokp) + token *tokp; +{ + lasttok = *tokp; + pushed = 1; +} + + +static +findstrconst(str, val) + char **str; + char **val; +{ + char *p; + int size; + + p = *str; + do { + *p++; + } while (*p && *p != '"'); + if (*p == 0) { + error("unterminated string constant"); + } + p++; + size = p - *str; + *val = alloc(size + 1); + (void) strncpy(*val, *str, size); + (*val)[size] = 0; + *str = p; +} + +static +findconst(str, val) + char **str; + char **val; +{ + char *p; + int size; + + p = *str; + if (*p == '0' && *(p + 1) == 'x') { + p++; + do { + p++; + } while (isxdigit(*p)); + } else { + do { + p++; + } while (isdigit(*p)); + } + size = p - *str; + *val = alloc(size + 1); + (void) strncpy(*val, *str, size); + (*val)[size] = 0; + *str = p; +} + + + +static token symbols[] = { + {TOK_CONST, "const"}, + {TOK_UNION, "union"}, + {TOK_SWITCH, "switch"}, + {TOK_CASE, "case"}, + {TOK_DEFAULT, "default"}, + {TOK_STRUCT, "struct"}, + {TOK_TYPEDEF, "typedef"}, + {TOK_ENUM, "enum"}, + {TOK_OPAQUE, "opaque"}, + {TOK_BOOL, "bool"}, + {TOK_VOID, "void"}, + {TOK_CHAR, "char"}, + {TOK_INT, "int"}, + {TOK_UNSIGNED, "unsigned"}, + {TOK_SHORT, "short"}, + {TOK_LONG, "long"}, + {TOK_FLOAT, "float"}, + {TOK_DOUBLE, "double"}, + {TOK_STRING, "string"}, + {TOK_PROGRAM, "program"}, + {TOK_VERSION, "version"}, + {TOK_EOF, "??????"}, +}; + + +static +findkind(mark, tokp) + char **mark; + token *tokp; +{ + + int len; + token *s; + char *str; + + str = *mark; + for (s = symbols; s->kind != TOK_EOF; s++) { + len = strlen(s->str); + if (strncmp(str, s->str, len) == 0) { + if (!isalnum(str[len]) && str[len] != '_') { + tokp->kind = s->kind; + tokp->str = s->str; + *mark = str + len; + return; + } + } + } + tokp->kind = TOK_IDENT; + for (len = 0; isalnum(str[len]) || str[len] == '_'; len++); + tokp->str = alloc(len + 1); + (void) strncpy(tokp->str, str, len); + tokp->str[len] = 0; + *mark = str + len; +} + +static +cppline(line) + char *line; +{ + return (line == curline && *line == '#'); +} + +static +directive(line) + char *line; +{ + return (line == curline && *line == '%'); +} + +static +printdirective(line) + char *line; +{ + f_print(fout, "%s", line + 1); +} + +static +docppline(line, lineno, fname) + char *line; + int *lineno; + char **fname; +{ + char *file; + int num; + char *p; + + line++; + while (isspace(*line)) { + line++; + } + num = atoi(line); + while (isdigit(*line)) { + line++; + } + while (isspace(*line)) { + line++; + } + if (*line != '"') { + error("preprocessor error"); + } + line++; + p = file = alloc(strlen(line) + 1); + while (*line && *line != '"') { + *p++ = *line++; + } + if (*line == 0) { + error("preprocessor error"); + } + *p = 0; + if (*file == 0) { + *fname = NULL; + } else { + *fname = file; + } + *lineno = num - 1; +} diff --git a/usr.bin/rpcgen/rpc_scan.h b/usr.bin/rpcgen/rpc_scan.h new file mode 100644 index 0000000000..3c7fbc492a --- /dev/null +++ b/usr.bin/rpcgen/rpc_scan.h @@ -0,0 +1,103 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + * + * from: @(#)rpc_scan.h 1.3 87/03/09 (C) 1987 SMI + * from: @(#)rpc_scan.h 2.1 88/08/01 4.0 RPCSRC + * $Id: rpc_scan.h,v 1.2 1993/08/01 18:09:26 mycroft Exp $ + */ + +/* + * rpc_scan.h, Definitions for the RPCL scanner + * Copyright (C) 1987, Sun Microsystems, Inc. + */ + +/* + * kinds of tokens + */ +enum tok_kind { + TOK_IDENT, + TOK_STRCONST, + TOK_LPAREN, + TOK_RPAREN, + TOK_LBRACE, + TOK_RBRACE, + TOK_LBRACKET, + TOK_RBRACKET, + TOK_LANGLE, + TOK_RANGLE, + TOK_STAR, + TOK_COMMA, + TOK_EQUAL, + TOK_COLON, + TOK_SEMICOLON, + TOK_CONST, + TOK_STRUCT, + TOK_UNION, + TOK_SWITCH, + TOK_CASE, + TOK_DEFAULT, + TOK_ENUM, + TOK_TYPEDEF, + TOK_INT, + TOK_SHORT, + TOK_LONG, + TOK_UNSIGNED, + TOK_FLOAT, + TOK_DOUBLE, + TOK_OPAQUE, + TOK_CHAR, + TOK_STRING, + TOK_BOOL, + TOK_VOID, + TOK_PROGRAM, + TOK_VERSION, + TOK_EOF +}; +typedef enum tok_kind tok_kind; + +/* + * a token + */ +struct token { + tok_kind kind; + char *str; +}; +typedef struct token token; + + +/* + * routine interface + */ +void scanprint(); +void scan(); +void scan2(); +void scan3(); +void scan_num(); +void peek(); +int peekscan(); +void get_token(); diff --git a/usr.bin/rpcgen/rpc_svcout.c b/usr.bin/rpcgen/rpc_svcout.c new file mode 100644 index 0000000000..90d2221bc0 --- /dev/null +++ b/usr.bin/rpcgen/rpc_svcout.c @@ -0,0 +1,276 @@ +/* @(#)rpc_svcout.c 2.1 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#ifndef lint +/*static char sccsid[] = "from: @(#)rpc_svcout.c 1.6 87/06/24 (C) 1987 SMI";*/ +static char rcsid[] = "$Id: rpc_svcout.c,v 1.3 1993/08/01 18:09:17 mycroft Exp $"; +#endif + +/* + * rpc_svcout.c, Server-skeleton outputter for the RPC protocol compiler + * Copyright (C) 1987, Sun Microsytsems, Inc. + */ +#include <stdio.h> +#include <strings.h> +#include "rpc_parse.h" +#include "rpc_util.h" + +static char RQSTP[] = "rqstp"; +static char TRANSP[] = "transp"; +static char ARG[] = "argument"; +static char RESULT[] = "result"; +static char ROUTINE[] = "local"; + +static int write_program(), printerr(), printif(); +/* + * write most of the service, that is, everything but the registrations. + */ +void +write_most() +{ + list *l; + definition *def; + version_list *vp; + + for (l = defined; l != NULL; l = l->next) { + def = (definition *) l->val; + if (def->def_kind == DEF_PROGRAM) { + for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) { + f_print(fout, "\nstatic void "); + pvname(def->def_name, vp->vers_num); + f_print(fout, "();"); + } + } + } + f_print(fout, "\n\n"); + f_print(fout, "main()\n"); + f_print(fout, "{\n"); + f_print(fout, "\tSVCXPRT *%s;\n", TRANSP); + f_print(fout, "\n"); + for (l = defined; l != NULL; l = l->next) { + def = (definition *) l->val; + if (def->def_kind != DEF_PROGRAM) { + continue; + } + for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) { + f_print(fout, "\t(void)pmap_unset(%s, %s);\n", def->def_name, vp->vers_name); + } + } +} + + +/* + * write a registration for the given transport + */ +void +write_register(transp) + char *transp; +{ + list *l; + definition *def; + version_list *vp; + + f_print(fout, "\n"); + f_print(fout, "\t%s = svc%s_create(RPC_ANYSOCK", TRANSP, transp); + if (streq(transp, "tcp")) { + f_print(fout, ", 0, 0"); + } + f_print(fout, ");\n"); + f_print(fout, "\tif (%s == NULL) {\n", TRANSP); + f_print(fout, "\t\t(void)fprintf(stderr, \"cannot create %s service.\\n\");\n", transp); + f_print(fout, "\t\texit(1);\n"); + f_print(fout, "\t}\n"); + + for (l = defined; l != NULL; l = l->next) { + def = (definition *) l->val; + if (def->def_kind != DEF_PROGRAM) { + continue; + } + for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) { + f_print(fout, + "\tif (!svc_register(%s, %s, %s, ", + TRANSP, def->def_name, vp->vers_name); + pvname(def->def_name, vp->vers_num); + f_print(fout, ", IPPROTO_%s)) {\n", + streq(transp, "udp") ? "UDP" : "TCP"); + f_print(fout, + "\t\t(void)fprintf(stderr, \"unable to register (%s, %s, %s).\\n\");\n", + def->def_name, vp->vers_name, transp); + f_print(fout, "\t\texit(1);\n"); + f_print(fout, "\t}\n"); + } + } +} + + +/* + * write the rest of the service + */ +void +write_rest() +{ + f_print(fout, "\tsvc_run();\n"); + f_print(fout, "\t(void)fprintf(stderr, \"svc_run returned\\n\");\n"); + f_print(fout, "\texit(1);\n"); + f_print(fout, "}\n"); +} + +void +write_programs(storage) + char *storage; +{ + list *l; + definition *def; + + for (l = defined; l != NULL; l = l->next) { + def = (definition *) l->val; + if (def->def_kind == DEF_PROGRAM) { + write_program(def, storage); + } + } +} + + +static +write_program(def, storage) + definition *def; + char *storage; +{ + version_list *vp; + proc_list *proc; + int filled; + + for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) { + f_print(fout, "\n"); + if (storage != NULL) { + f_print(fout, "%s ", storage); + } + f_print(fout, "void\n"); + pvname(def->def_name, vp->vers_num); + f_print(fout, "(%s, %s)\n", RQSTP, TRANSP); + f_print(fout, " struct svc_req *%s;\n", RQSTP); + f_print(fout, " SVCXPRT *%s;\n", TRANSP); + f_print(fout, "{\n"); + + filled = 0; + f_print(fout, "\tunion {\n"); + for (proc = vp->procs; proc != NULL; proc = proc->next) { + if (streq(proc->arg_type, "void")) { + continue; + } + filled = 1; + f_print(fout, "\t\t"); + ptype(proc->arg_prefix, proc->arg_type, 0); + pvname(proc->proc_name, vp->vers_num); + f_print(fout, "_arg;\n"); + } + if (!filled) { + f_print(fout, "\t\tint fill;\n"); + } + f_print(fout, "\t} %s;\n", ARG); + f_print(fout, "\tchar *%s;\n", RESULT); + f_print(fout, "\tbool_t (*xdr_%s)(), (*xdr_%s)();\n", ARG, RESULT); + f_print(fout, "\tchar *(*%s)();\n", ROUTINE); + f_print(fout, "\n"); + f_print(fout, "\tswitch (%s->rq_proc) {\n", RQSTP); + + if (!nullproc(vp->procs)) { + f_print(fout, "\tcase NULLPROC:\n"); + f_print(fout, "\t\t(void)svc_sendreply(%s, xdr_void, (char *)NULL);\n", TRANSP); + f_print(fout, "\t\treturn;\n\n"); + } + for (proc = vp->procs; proc != NULL; proc = proc->next) { + f_print(fout, "\tcase %s:\n", proc->proc_name); + f_print(fout, "\t\txdr_%s = xdr_%s;\n", ARG, + stringfix(proc->arg_type)); + f_print(fout, "\t\txdr_%s = xdr_%s;\n", RESULT, + stringfix(proc->res_type)); + f_print(fout, "\t\t%s = (char *(*)()) ", ROUTINE); + pvname(proc->proc_name, vp->vers_num); + f_print(fout, ";\n"); + f_print(fout, "\t\tbreak;\n\n"); + } + f_print(fout, "\tdefault:\n"); + printerr("noproc", TRANSP); + f_print(fout, "\t\treturn;\n"); + f_print(fout, "\t}\n"); + + f_print(fout, "\tbzero((char *)&%s, sizeof(%s));\n", ARG, ARG); + printif("getargs", TRANSP, "&", ARG); + printerr("decode", TRANSP); + f_print(fout, "\t\treturn;\n"); + f_print(fout, "\t}\n"); + + f_print(fout, "\t%s = (*%s)(&%s, %s);\n", RESULT, ROUTINE, ARG, + RQSTP); + f_print(fout, + "\tif (%s != NULL && !svc_sendreply(%s, xdr_%s, %s)) {\n", + RESULT, TRANSP, RESULT, RESULT); + printerr("systemerr", TRANSP); + f_print(fout, "\t}\n"); + + printif("freeargs", TRANSP, "&", ARG); + f_print(fout, "\t\t(void)fprintf(stderr, \"unable to free arguments\\n\");\n"); + f_print(fout, "\t\texit(1);\n"); + f_print(fout, "\t}\n"); + + f_print(fout, "}\n\n"); + } +} + +static +printerr(err, transp) + char *err; + char *transp; +{ + f_print(fout, "\t\tsvcerr_%s(%s);\n", err, transp); +} + +static +printif(proc, transp, prefix, arg) + char *proc; + char *transp; + char *prefix; + char *arg; +{ + f_print(fout, "\tif (!svc_%s(%s, xdr_%s, %s%s)) {\n", + proc, transp, arg, prefix, arg); +} + + +nullproc(proc) + proc_list *proc; +{ + for (; proc != NULL; proc = proc->next) { + if (streq(proc->proc_num, "0")) { + return (1); + } + } + return (0); +} diff --git a/usr.bin/rpcgen/rpc_util.c b/usr.bin/rpcgen/rpc_util.c new file mode 100644 index 0000000000..a9714fdea0 --- /dev/null +++ b/usr.bin/rpcgen/rpc_util.c @@ -0,0 +1,439 @@ +/* @(#)rpc_util.c 2.1 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#ifndef lint +/*static char sccsid[] = "from: @(#)rpc_util.c 1.5 87/06/24 (C) 1987 SMI";*/ +static char rcsid[] = "$Id: rpc_util.c,v 1.3 1993/08/01 18:09:15 mycroft Exp $"; +#endif + +/* + * rpc_util.c, Utility routines for the RPC protocol compiler + * Copyright (C) 1987, Sun Microsystems, Inc. + */ +#include <stdio.h> +#include "rpc_scan.h" +#include "rpc_parse.h" +#include "rpc_util.h" + +char curline[MAXLINESIZE]; /* current read line */ +char *where = curline; /* current point in line */ +int linenum = 0; /* current line number */ + +char *infilename; /* input filename */ + +#define NFILES 4 +char *outfiles[NFILES]; /* output file names */ +int nfiles; + +FILE *fout; /* file pointer of current output */ +FILE *fin; /* file pointer of current input */ + +list *defined; /* list of defined things */ + +static int printwhere(); + +/* + * Reinitialize the world + */ +reinitialize() +{ + bzero(curline, MAXLINESIZE); + where = curline; + linenum = 0; + defined = NULL; +} + +/* + * string equality + */ +streq(a, b) + char *a; + char *b; +{ + return (strcmp(a, b) == 0); +} + +/* + * find a value in a list + */ +char * +findval(lst, val, cmp) + list *lst; + char *val; + int (*cmp) (); + +{ + for (; lst != NULL; lst = lst->next) { + if ((*cmp) (lst->val, val)) { + return (lst->val); + } + } + return (NULL); +} + +/* + * store a value in a list + */ +void +storeval(lstp, val) + list **lstp; + char *val; +{ + list **l; + list *lst; + + for (l = lstp; *l != NULL; l = (list **) & (*l)->next); + lst = ALLOC(list); + lst->val = val; + lst->next = NULL; + *l = lst; +} + + +static +findit(def, type) + definition *def; + char *type; +{ + return (streq(def->def_name, type)); +} + + +static char * +fixit(type, orig) + char *type; + char *orig; +{ + definition *def; + + def = (definition *) FINDVAL(defined, type, findit); + if (def == NULL || def->def_kind != DEF_TYPEDEF) { + return (orig); + } + switch (def->def.ty.rel) { + case REL_VECTOR: + return (def->def.ty.old_type); + case REL_ALIAS: + return (fixit(def->def.ty.old_type, orig)); + default: + return (orig); + } +} + +char * +fixtype(type) + char *type; +{ + return (fixit(type, type)); +} + +char * +stringfix(type) + char *type; +{ + if (streq(type, "string")) { + return ("wrapstring"); + } else { + return (type); + } +} + +void +ptype(prefix, type, follow) + char *prefix; + char *type; + int follow; +{ + if (prefix != NULL) { + if (streq(prefix, "enum")) { + f_print(fout, "enum "); + } else { + f_print(fout, "struct "); + } + } + if (streq(type, "bool")) { + f_print(fout, "bool_t "); + } else if (streq(type, "string")) { + f_print(fout, "char *"); + } else { + f_print(fout, "%s ", follow ? fixtype(type) : type); + } +} + + +static +typedefed(def, type) + definition *def; + char *type; +{ + if (def->def_kind != DEF_TYPEDEF || def->def.ty.old_prefix != NULL) { + return (0); + } else { + return (streq(def->def_name, type)); + } +} + +isvectordef(type, rel) + char *type; + relation rel; +{ + definition *def; + + for (;;) { + switch (rel) { + case REL_VECTOR: + return (!streq(type, "string")); + case REL_ARRAY: + return (0); + case REL_POINTER: + return (0); + case REL_ALIAS: + def = (definition *) FINDVAL(defined, type, typedefed); + if (def == NULL) { + return (0); + } + type = def->def.ty.old_type; + rel = def->def.ty.rel; + } + } +} + + +static char * +locase(str) + char *str; +{ + char c; + static char buf[100]; + char *p = buf; + + while (c = *str++) { + *p++ = (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c; + } + *p = 0; + return (buf); +} + + +void +pvname(pname, vnum) + char *pname; + char *vnum; +{ + f_print(fout, "%s_%s", locase(pname), vnum); +} + + +/* + * print a useful (?) error message, and then die + */ +void +error(msg) + char *msg; +{ + printwhere(); + f_print(stderr, "%s, line %d: ", infilename, linenum); + f_print(stderr, "%s\n", msg); + crash(); +} + +/* + * Something went wrong, unlink any files that we may have created and then + * die. + */ +crash() +{ + int i; + + for (i = 0; i < nfiles; i++) { + (void) unlink(outfiles[i]); + } + exit(1); +} + + +void +record_open(file) + char *file; +{ + if (nfiles < NFILES) { + outfiles[nfiles++] = file; + } else { + f_print(stderr, "too many files!\n"); + crash(); + } +} + +static char expectbuf[100]; +static char *toktostr(); + +/* + * error, token encountered was not the expected one + */ +void +expected1(exp1) + tok_kind exp1; +{ + s_print(expectbuf, "expected '%s'", + toktostr(exp1)); + error(expectbuf); +} + +/* + * error, token encountered was not one of two expected ones + */ +void +expected2(exp1, exp2) + tok_kind exp1, exp2; +{ + s_print(expectbuf, "expected '%s' or '%s'", + toktostr(exp1), + toktostr(exp2)); + error(expectbuf); +} + +/* + * error, token encountered was not one of 3 expected ones + */ +void +expected3(exp1, exp2, exp3) + tok_kind exp1, exp2, exp3; +{ + s_print(expectbuf, "expected '%s', '%s' or '%s'", + toktostr(exp1), + toktostr(exp2), + toktostr(exp3)); + error(expectbuf); +} + +void +tabify(f, tab) + FILE *f; + int tab; +{ + while (tab--) { + (void) fputc('\t', f); + } +} + + + +static token tokstrings[] = { + {TOK_IDENT, "identifier"}, + {TOK_CONST, "const"}, + {TOK_RPAREN, ")"}, + {TOK_LPAREN, "("}, + {TOK_RBRACE, "}"}, + {TOK_LBRACE, "{"}, + {TOK_LBRACKET, "["}, + {TOK_RBRACKET, "]"}, + {TOK_STAR, "*"}, + {TOK_COMMA, ","}, + {TOK_EQUAL, "="}, + {TOK_COLON, ":"}, + {TOK_SEMICOLON, ";"}, + {TOK_UNION, "union"}, + {TOK_STRUCT, "struct"}, + {TOK_SWITCH, "switch"}, + {TOK_CASE, "case"}, + {TOK_DEFAULT, "default"}, + {TOK_ENUM, "enum"}, + {TOK_TYPEDEF, "typedef"}, + {TOK_INT, "int"}, + {TOK_SHORT, "short"}, + {TOK_LONG, "long"}, + {TOK_UNSIGNED, "unsigned"}, + {TOK_DOUBLE, "double"}, + {TOK_FLOAT, "float"}, + {TOK_CHAR, "char"}, + {TOK_STRING, "string"}, + {TOK_OPAQUE, "opaque"}, + {TOK_BOOL, "bool"}, + {TOK_VOID, "void"}, + {TOK_PROGRAM, "program"}, + {TOK_VERSION, "version"}, + {TOK_EOF, "??????"} +}; + +static char * +toktostr(kind) + tok_kind kind; +{ + token *sp; + + for (sp = tokstrings; sp->kind != TOK_EOF && sp->kind != kind; sp++); + return (sp->str); +} + + + +static +printbuf() +{ + char c; + int i; + int cnt; + +# define TABSIZE 4 + + for (i = 0; c = curline[i]; i++) { + if (c == '\t') { + cnt = 8 - (i % TABSIZE); + c = ' '; + } else { + cnt = 1; + } + while (cnt--) { + (void) fputc(c, stderr); + } + } +} + + +static +printwhere() +{ + int i; + char c; + int cnt; + + printbuf(); + for (i = 0; i < where - curline; i++) { + c = curline[i]; + if (c == '\t') { + cnt = 8 - (i % TABSIZE); + } else { + cnt = 1; + } + while (cnt--) { + (void) fputc('^', stderr); + } + } + (void) fputc('\n', stderr); +} diff --git a/usr.bin/rpcgen/rpc_util.h b/usr.bin/rpcgen/rpc_util.h new file mode 100644 index 0000000000..dfdb54985f --- /dev/null +++ b/usr.bin/rpcgen/rpc_util.h @@ -0,0 +1,113 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + * + * from: @(#)rpc_util.h 1.6 87/06/24 (C) 1987 SMI + * from: @(#)rpc_util.h 2.1 88/08/01 4.0 RPCSRC + * $Id: rpc_util.h,v 1.2 1993/08/01 18:09:24 mycroft Exp $ + */ + +/* + * rpc_util.h, Useful definitions for the RPC protocol compiler + * Copyright (C) 1987, Sun Microsystems, Inc. + */ +extern char *malloc(); + +#define alloc(size) malloc((unsigned)(size)) +#define ALLOC(object) (object *) malloc(sizeof(object)) + +#define s_print (void) sprintf +#define f_print (void) fprintf + +struct list { + char *val; + struct list *next; +}; +typedef struct list list; + +/* + * Global variables + */ +#define MAXLINESIZE 1024 +extern char curline[MAXLINESIZE]; +extern char *where; +extern int linenum; + +extern char *infilename; +extern FILE *fout; +extern FILE *fin; + +extern list *defined; + +/* + * rpc_util routines + */ +void storeval(); + +#define STOREVAL(list,item) \ + storeval(list,(char *)item) + +char *findval(); + +#define FINDVAL(list,item,finder) \ + findval(list, (char *) item, finder) + +char *fixtype(); +char *stringfix(); +void pvname(); +void ptype(); +int isvectordef(); +int streq(); +void error(); +void expected1(); +void expected2(); +void expected3(); +void tabify(); +void record_open(); + +/* + * rpc_cout routines + */ +void cprint(); +void emit(); + +/* + * rpc_hout routines + */ +void print_datadef(); + +/* + * rpc_svcout routines + */ +void write_most(); +void write_register(); +void write_rest(); + +/* + * rpc_clntout routines + */ +void write_stubs(); diff --git a/usr.bin/rpcgen/rpcgen.1 b/usr.bin/rpcgen/rpcgen.1 new file mode 100644 index 0000000000..a8e014c3a3 --- /dev/null +++ b/usr.bin/rpcgen/rpcgen.1 @@ -0,0 +1,156 @@ +.\" from: @(#)rpcgen.1 2.2 88/08/02 4.0 RPCSRC +.\" $Id: rpcgen.1,v 1.4 1993/08/01 07:29:24 mycroft Exp $ +.\" +.Dd January 18, 1988 +.Dt RPCGEN 1 +.Os +.Sh NAME +.Nm rpcgen +.Nd RPC protocol compiler +.Sh SYNOPSIS +.Nm rpcgen +.Ar infile +.Nm rpcgen +.Fl c Li | +.Fl h Li | +.Fl l Li | +.Fl m +.Op Fl o Ar outfile +.Op Ar infile +.Nm rpcgen +.Fl s Ar transport +.Op Fl o Ar outfile +.Op Ar infile +.Sh DESCRIPTION +.Nm rpcgen +is a tool that generates C code to implement an +.Tn RPC +protocol. The input to +.Nm rpcgen +is a language similar to C known as +.Tn RPC +Language (Remote Procedure Call Language). Information about the +syntax of +.Tn RPC +Language is available in the +.Rs +.%T "rpcgen Programming Guide" +.Re +.Pp +.Nm rpcgen +is normally used as in the first synopsis where it takes an input file +and generates four output files. If the +.Ar infile +is named +.Pa proto.x , +then +.Nm rpcgen +will generate a header file in +.Pa proto.h , +.Tn XDR +routines in +.Pa proto_xdr.c , +server-side stubs in +.Pa proto_svc.c , +and client-side stubs in +.Pa proto_clnt.c . +.Pp +The other synopses shown above are used when one does not want to +generate all the output files, but only a particular one. +.\" Their usage is described in the +.\" .Sx USAGE +.\" section below. +.Pp +The C-preprocessor, +.Xr cpp 1 , +is run on all input files before they are actually +interpreted by +.Nm rpcgen , +so all the +.Xr cpp +directives are legal within an +.Nm rpcgen +input file. For each type of output file, +.Nm rpcgen +defines a special +.Xr cpp +symbol for use by the +.Nm rpcgen +programmer: +.Pp +.Bl -tag -width RPC_CLNT -compact +.It Dv RPC_HDR +defined when compiling into header files +.It Dv RPC_XDR +defined when compiling into +.Tn XDR +routines +.It Dv RPC_SVC +defined when compiling into server-side stubs +.It Dv RPC_CLNT +defined when compiling into client-side stubs +.El +.Pp +In addition, +.Nm rpcgen +does a little preprocessing of its own. Any line beginning with +.Sq % +is passed directly into the output file, uninterpreted by +.Nm rpcgen . +.Pp +You can customize some of your +.Tn XDR +routines by leaving those data types undefined. For every data type +that is undefined, +.Nm rpcgen +will assume that there exists a routine with the name +.Tn xdr_ +prepended to the name of the undefined type. +.Sh OPTIONS +.Bl -tag -width indent +.It Fl c +Compile into +.Tn XDR +routines. +.It Fl h +Compile into C data-definitions (a header file). +.It Fl l +Compile into client-side stubs. +.It Fl m +Compile into server-side stubs, but do not generate a +.Fn main +routine. This option is useful for doing callback-routines and for +people who need to write their own +.Fn main +routine to do initialization. +.It Fl o Ar outfile +Specify the name of the output file. If none is specified, standard +output is used ( +.Fl c , +.Fl h , +.Fl l +and +.Fl s +modes only). +.It Fl s Ar transport +Compile into server-side stubs, using the the given transport. The +supported transports are +.Tn udp +and +.Tn tcp . +This option may be invoked more than once so as to compile a server +that serves multiple transports. +.El +.Sh SEE ALSO +.Xr cpp 1 +.Rs +.%T "rpcgen Programming Guide" +.Re +.Sh BUGS +Nesting is not supported. As a work-around, structures can be +declared at top-level, and their name used inside other structures in +order to achieve the same effect. +.Pp +Name clashes can occur when using program definitions, since the +apparent scoping does not really apply. Most of these can be avoided +by giving unique names for programs, versions, procedures and types. diff --git a/usr.bin/rpcinfo/Makefile b/usr.bin/rpcinfo/Makefile new file mode 100644 index 0000000000..5901596a27 --- /dev/null +++ b/usr.bin/rpcinfo/Makefile @@ -0,0 +1,10 @@ +# from: @(#)Makefile 5.2 (Berkeley) 5/11/90 +# $Id: Makefile,v 1.2 1993/07/30 23:51:26 mycroft Exp $ + +PROG= rpcinfo +LDADD= -lrpc +DPADD = ${LIBRPC} +MAN8 = rpcinfo.8 + + +.include <bsd.prog.mk> diff --git a/usr.bin/rpcinfo/rpcinfo.8 b/usr.bin/rpcinfo/rpcinfo.8 new file mode 100644 index 0000000000..42b578c240 --- /dev/null +++ b/usr.bin/rpcinfo/rpcinfo.8 @@ -0,0 +1,166 @@ +.\" from: @(#)rpcinfo.8c 2.2 88/08/03 4.0 RPCSRC; from 1.24 88/02/25 SMI +.\" $Id: rpcinfo.8,v 1.3 1993/08/01 07:29:21 mycroft Exp $ +.\" +.Dd December 17, 1987 +.Dt RPCINFO 8 +.Os +.Sh NAME +.Nm rpcinfo +.Nd report RPC information +.Sh SYNOPSIS +.Nm rpcinfo +.Fl p +.Op Ar host +.Nm rpcinfo +.Op Fl n Ar portnum +.Fl u Ar host +.Ar program +.Op Ar version +.Nm rpcinfo +.Op Fl n Ar portnum +.Fl t Ar host +.Ar program +.Op Ar version +.Nm rpcinfo +.Fl b +.Ar program version +.Nm rpcinfo +.Fl d +.Ar program version +.Sh DESCRIPTION +.Nm rpcinfo +makes an +.Tn RPC +call to an +.Tn RPC +server and reports what it finds. +.Sh OPTIONS +.Bl -tag -width indent +.It Fl p +Probe the portmapper on +.Ar host , +and print a list of all registered +.Tn RPC +programs. If +.Ar host +is not specified, it defaults to the value returned by +.Xr hostname 1 . +.It Fl u +Make an +.Tn RPC +call to procedure 0 of +.Ar program +on the specified +.Ar host +using +.Tn UDP , +and report whether a response was received. +.It Fl t +Make an +.Tn RPC +call to procedure 0 of +.Ar program +on the specified +.Ar host +using +.Tn TCP , +and report whether a response was received. +.It Fl n +Use +.Ar portnum +as the port number for the +.Fl t +and +.Fl u +options instead of the port number given by the portmapper. +.It Fl b +Make an +.Tn RPC +broadcast to procedure 0 of the specified +.Ar program +and +.Ar version +using +.Tn UDP +and report all hosts that respond. +.It Fl d +Delete registration for the +.Tn RPC +service of the specified +.Ar program +and +.Ar version . +This option can be exercised only by the super-user. +.El +.Pp +The +.Ar program +argument can be either a name or a number. +.Pp +If a +.Ar version +is specified, +.Nm rpcinfo +attempts to call that version of the specified +.Ar program . +Otherwise, +.Nm rpcinfo +attempts to find all the registered version +numbers for the specified +.Ar program +by calling version 0 (which is presumed not +to exist; if it does exist, +.Ar rpcinfo +attempts to obtain this information by calling +an extremely high version +number instead) and attempts to call each registered version. +Note: the version number is required for +.Fl b +and +.Fl d +options. +.Sh EXAMPLES +To show all of the +.Tn RPC +services registered on the local machine use: +.Pp +.Dl example% rpcinfo -p +.Pp +To show all of the +.Tn RPC +services registered on the machine named +.Ar klaxon +use: +.Pp +.Dl example% rpcinfo -p klaxon +.Pp +To show all machines on the local net that are running the Yellow Pages +service use: +.Pp +.Dl example% rpcinfo -b ypserv 'version' | uniq +.Pp +where 'version' is the current Yellow Pages version obtained from the +results of the +.Fl p +switch above. +.Pp +To delete the registration for version 1 of the +.Nm walld +service use: +.Pp +.Dl example% rpcinfo -d walld 1 +.Sh SEE ALSO +.Xr rpc 5 , +.Xr portmap 8 +.Rs +.%T "RPC Programming Guide" +.Re +.Sh BUGS +In releases prior to SunOS 3.0, the Network File System (NFS) did not +register itself with the portmapper; +.Nm rpcinfo +cannot be used to make +.Tn RPC +calls to the +.Tn NFS +server on hosts running such releases. diff --git a/usr.bin/rpcinfo/rpcinfo.c b/usr.bin/rpcinfo/rpcinfo.c new file mode 100644 index 0000000000..889919f29a --- /dev/null +++ b/usr.bin/rpcinfo/rpcinfo.c @@ -0,0 +1,666 @@ +#ifndef lint +/*static char sccsid[] = "from: @(#)rpcinfo.c 1.22 87/08/12 SMI";*/ +/*static char sccsid[] = "from: @(#)rpcinfo.c 2.2 88/08/11 4.0 RPCSRC";*/ +static char rcsid[] = "$Id: rpcinfo.c,v 1.3 1993/08/01 18:09:11 mycroft Exp $"; +#endif + +/* + * Copyright (C) 1986, Sun Microsystems, Inc. + */ + +/* + * rpcinfo: ping a particular rpc program + * or dump the portmapper + */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#include <rpc/rpc.h> +#include <stdio.h> +#include <sys/socket.h> +#include <netdb.h> +#include <rpc/pmap_prot.h> +#include <rpc/pmap_clnt.h> +#include <signal.h> +#include <ctype.h> + +#define MAXHOSTLEN 256 + +#define MIN_VERS ((u_long) 0) +#define MAX_VERS ((u_long) 4294967295UL) + +static void udpping(/*u_short portflag, int argc, char **argv*/); +static void tcpping(/*u_short portflag, int argc, char **argv*/); +static int pstatus(/*CLIENT *client, u_long prognum, u_long vers*/); +static void pmapdump(/*int argc, char **argv*/); +static bool_t reply_proc(/*void *res, struct sockaddr_in *who*/); +static void brdcst(/*int argc, char **argv*/); +static void deletereg(/* int argc, char **argv */) ; +static void usage(/*void*/); +static u_long getprognum(/*char *arg*/); +static u_long getvers(/*char *arg*/); +static void get_inet_address(/*struct sockaddr_in *addr, char *host*/); +extern u_long inet_addr(); /* in 4.2BSD, arpa/inet.h called that a in_addr */ +extern char *inet_ntoa(); + +/* + * Functions to be performed. + */ +#define NONE 0 /* no function */ +#define PMAPDUMP 1 /* dump portmapper registrations */ +#define TCPPING 2 /* ping TCP service */ +#define UDPPING 3 /* ping UDP service */ +#define BRDCST 4 /* ping broadcast UDP service */ +#define DELETES 5 /* delete registration for the service */ + +int +main(argc, argv) + int argc; + char **argv; +{ + register int c; + extern char *optarg; + extern int optind; + int errflg; + int function; + u_short portnum; + + function = NONE; + portnum = 0; + errflg = 0; + while ((c = getopt(argc, argv, "ptubdn:")) != EOF) { + switch (c) { + + case 'p': + if (function != NONE) + errflg = 1; + else + function = PMAPDUMP; + break; + + case 't': + if (function != NONE) + errflg = 1; + else + function = TCPPING; + break; + + case 'u': + if (function != NONE) + errflg = 1; + else + function = UDPPING; + break; + + case 'b': + if (function != NONE) + errflg = 1; + else + function = BRDCST; + break; + + case 'n': + portnum = (u_short) atoi(optarg); /* hope we don't get bogus # */ + break; + + case 'd': + if (function != NONE) + errflg = 1; + else + function = DELETES; + break; + + case '?': + errflg = 1; + } + } + + if (errflg || function == NONE) { + usage(); + return (1); + } + + switch (function) { + + case PMAPDUMP: + if (portnum != 0) { + usage(); + return (1); + } + pmapdump(argc - optind, argv + optind); + break; + + case UDPPING: + udpping(portnum, argc - optind, argv + optind); + break; + + case TCPPING: + tcpping(portnum, argc - optind, argv + optind); + break; + + case BRDCST: + if (portnum != 0) { + usage(); + return (1); + } + brdcst(argc - optind, argv + optind); + break; + + case DELETES: + deletereg(argc - optind, argv + optind); + break; + } + + return (0); +} + +static void +udpping(portnum, argc, argv) + u_short portnum; + int argc; + char **argv; +{ + struct timeval to; + struct sockaddr_in addr; + enum clnt_stat rpc_stat; + CLIENT *client; + u_long prognum, vers, minvers, maxvers; + int sock = RPC_ANYSOCK; + struct rpc_err rpcerr; + int failure; + + if (argc < 2 || argc > 3) { + usage(); + exit(1); + } + prognum = getprognum(argv[1]); + get_inet_address(&addr, argv[0]); + /* Open the socket here so it will survive calls to clnt_destroy */ + sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) { + perror("rpcinfo: socket"); + exit(1); + } + failure = 0; + if (argc == 2) { + /* + * A call to version 0 should fail with a program/version + * mismatch, and give us the range of versions supported. + */ + addr.sin_port = htons(portnum); + to.tv_sec = 5; + to.tv_usec = 0; + if ((client = clntudp_create(&addr, prognum, (u_long)0, + to, &sock)) == NULL) { + clnt_pcreateerror("rpcinfo"); + printf("program %lu is not available\n", + prognum); + exit(1); + } + to.tv_sec = 10; + to.tv_usec = 0; + rpc_stat = clnt_call(client, NULLPROC, xdr_void, (char *)NULL, + xdr_void, (char *)NULL, to); + if (rpc_stat == RPC_PROGVERSMISMATCH) { + clnt_geterr(client, &rpcerr); + minvers = rpcerr.re_vers.low; + maxvers = rpcerr.re_vers.high; + } else if (rpc_stat == RPC_SUCCESS) { + /* + * Oh dear, it DOES support version 0. + * Let's try version MAX_VERS. + */ + addr.sin_port = htons(portnum); + to.tv_sec = 5; + to.tv_usec = 0; + if ((client = clntudp_create(&addr, prognum, MAX_VERS, + to, &sock)) == NULL) { + clnt_pcreateerror("rpcinfo"); + printf("program %lu version %lu is not available\n", + prognum, MAX_VERS); + exit(1); + } + to.tv_sec = 10; + to.tv_usec = 0; + rpc_stat = clnt_call(client, NULLPROC, xdr_void, + (char *)NULL, xdr_void, (char *)NULL, to); + if (rpc_stat == RPC_PROGVERSMISMATCH) { + clnt_geterr(client, &rpcerr); + minvers = rpcerr.re_vers.low; + maxvers = rpcerr.re_vers.high; + } else if (rpc_stat == RPC_SUCCESS) { + /* + * It also supports version MAX_VERS. + * Looks like we have a wise guy. + * OK, we give them information on all + * 4 billion versions they support... + */ + minvers = 0; + maxvers = MAX_VERS; + } else { + (void) pstatus(client, prognum, MAX_VERS); + exit(1); + } + } else { + (void) pstatus(client, prognum, (u_long)0); + exit(1); + } + clnt_destroy(client); + for (vers = minvers; vers <= maxvers; vers++) { + addr.sin_port = htons(portnum); + to.tv_sec = 5; + to.tv_usec = 0; + if ((client = clntudp_create(&addr, prognum, vers, + to, &sock)) == NULL) { + clnt_pcreateerror("rpcinfo"); + printf("program %lu version %lu is not available\n", + prognum, vers); + exit(1); + } + to.tv_sec = 10; + to.tv_usec = 0; + rpc_stat = clnt_call(client, NULLPROC, xdr_void, + (char *)NULL, xdr_void, (char *)NULL, to); + if (pstatus(client, prognum, vers) < 0) + failure = 1; + clnt_destroy(client); + } + } + else { + vers = getvers(argv[2]); + addr.sin_port = htons(portnum); + to.tv_sec = 5; + to.tv_usec = 0; + if ((client = clntudp_create(&addr, prognum, vers, + to, &sock)) == NULL) { + clnt_pcreateerror("rpcinfo"); + printf("program %lu version %lu is not available\n", + prognum, vers); + exit(1); + } + to.tv_sec = 10; + to.tv_usec = 0; + rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL, + xdr_void, (char *)NULL, to); + if (pstatus(client, prognum, vers) < 0) + failure = 1; + } + (void) close(sock); /* Close it up again */ + if (failure) + exit(1); +} + +static void +tcpping(portnum, argc, argv) + u_short portnum; + int argc; + char **argv; +{ + struct timeval to; + struct sockaddr_in addr; + enum clnt_stat rpc_stat; + CLIENT *client; + u_long prognum, vers, minvers, maxvers; + int sock = RPC_ANYSOCK; + struct rpc_err rpcerr; + int failure; + + if (argc < 2 || argc > 3) { + usage(); + exit(1); + } + prognum = getprognum(argv[1]); + get_inet_address(&addr, argv[0]); + failure = 0; + if (argc == 2) { + /* + * A call to version 0 should fail with a program/version + * mismatch, and give us the range of versions supported. + */ + addr.sin_port = htons(portnum); + if ((client = clnttcp_create(&addr, prognum, MIN_VERS, + &sock, 0, 0)) == NULL) { + clnt_pcreateerror("rpcinfo"); + printf("program %lu is not available\n", + prognum); + exit(1); + } + to.tv_sec = 10; + to.tv_usec = 0; + rpc_stat = clnt_call(client, NULLPROC, xdr_void, (char *)NULL, + xdr_void, (char *)NULL, to); + if (rpc_stat == RPC_PROGVERSMISMATCH) { + clnt_geterr(client, &rpcerr); + minvers = rpcerr.re_vers.low; + maxvers = rpcerr.re_vers.high; + } else if (rpc_stat == RPC_SUCCESS) { + /* + * Oh dear, it DOES support version 0. + * Let's try version MAX_VERS. + */ + addr.sin_port = htons(portnum); + if ((client = clnttcp_create(&addr, prognum, MAX_VERS, + &sock, 0, 0)) == NULL) { + clnt_pcreateerror("rpcinfo"); + printf("program %lu version %lu is not available\n", + prognum, MAX_VERS); + exit(1); + } + to.tv_sec = 10; + to.tv_usec = 0; + rpc_stat = clnt_call(client, NULLPROC, xdr_void, + (char *)NULL, xdr_void, (char *)NULL, to); + if (rpc_stat == RPC_PROGVERSMISMATCH) { + clnt_geterr(client, &rpcerr); + minvers = rpcerr.re_vers.low; + maxvers = rpcerr.re_vers.high; + } else if (rpc_stat == RPC_SUCCESS) { + /* + * It also supports version MAX_VERS. + * Looks like we have a wise guy. + * OK, we give them information on all + * 4 billion versions they support... + */ + minvers = 0; + maxvers = MAX_VERS; + } else { + (void) pstatus(client, prognum, MAX_VERS); + exit(1); + } + } else { + (void) pstatus(client, prognum, MIN_VERS); + exit(1); + } + clnt_destroy(client); + (void) close(sock); + sock = RPC_ANYSOCK; /* Re-initialize it for later */ + for (vers = minvers; vers <= maxvers; vers++) { + addr.sin_port = htons(portnum); + if ((client = clnttcp_create(&addr, prognum, vers, + &sock, 0, 0)) == NULL) { + clnt_pcreateerror("rpcinfo"); + printf("program %lu version %lu is not available\n", + prognum, vers); + exit(1); + } + to.tv_usec = 0; + to.tv_sec = 10; + rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL, + xdr_void, (char *)NULL, to); + if (pstatus(client, prognum, vers) < 0) + failure = 1; + clnt_destroy(client); + (void) close(sock); + sock = RPC_ANYSOCK; + } + } + else { + vers = getvers(argv[2]); + addr.sin_port = htons(portnum); + if ((client = clnttcp_create(&addr, prognum, vers, &sock, + 0, 0)) == NULL) { + clnt_pcreateerror("rpcinfo"); + printf("program %lu version %lu is not available\n", + prognum, vers); + exit(1); + } + to.tv_usec = 0; + to.tv_sec = 10; + rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL, + xdr_void, (char *)NULL, to); + if (pstatus(client, prognum, vers) < 0) + failure = 1; + } + if (failure) + exit(1); +} + +/* + * This routine should take a pointer to an "rpc_err" structure, rather than + * a pointer to a CLIENT structure, but "clnt_perror" takes a pointer to + * a CLIENT structure rather than a pointer to an "rpc_err" structure. + * As such, we have to keep the CLIENT structure around in order to print + * a good error message. + */ +static int +pstatus(client, prognum, vers) + register CLIENT *client; + u_long prognum; + u_long vers; +{ + struct rpc_err rpcerr; + + clnt_geterr(client, &rpcerr); + if (rpcerr.re_status != RPC_SUCCESS) { + clnt_perror(client, "rpcinfo"); + printf("program %lu version %lu is not available\n", + prognum, vers); + return (-1); + } else { + printf("program %lu version %lu ready and waiting\n", + prognum, vers); + return (0); + } +} + +static void +pmapdump(argc, argv) + int argc; + char **argv; +{ + struct sockaddr_in server_addr; + register struct hostent *hp; + struct pmaplist *head = NULL; + int socket = RPC_ANYSOCK; + struct timeval minutetimeout; + register CLIENT *client; + struct rpcent *rpc; + + if (argc > 1) { + usage(); + exit(1); + } + if (argc == 1) + get_inet_address(&server_addr, argv[0]); + else { + bzero((char *)&server_addr, sizeof server_addr); + server_addr.sin_family = AF_INET; + if ((hp = gethostbyname("localhost")) != NULL) + bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr, + hp->h_length); + else + server_addr.sin_addr.s_addr = inet_addr("0.0.0.0"); + } + minutetimeout.tv_sec = 60; + minutetimeout.tv_usec = 0; + server_addr.sin_port = htons(PMAPPORT); + if ((client = clnttcp_create(&server_addr, PMAPPROG, + PMAPVERS, &socket, 50, 500)) == NULL) { + clnt_pcreateerror("rpcinfo: can't contact portmapper"); + exit(1); + } + if (clnt_call(client, PMAPPROC_DUMP, xdr_void, NULL, + xdr_pmaplist, &head, minutetimeout) != RPC_SUCCESS) { + fprintf(stderr, "rpcinfo: can't contact portmapper: "); + clnt_perror(client, "rpcinfo"); + exit(1); + } + if (head == NULL) { + printf("No remote programs registered.\n"); + } else { + printf(" program vers proto port\n"); + for (; head != NULL; head = head->pml_next) { + printf("%10ld%5ld", + head->pml_map.pm_prog, + head->pml_map.pm_vers); + if (head->pml_map.pm_prot == IPPROTO_UDP) + printf("%6s", "udp"); + else if (head->pml_map.pm_prot == IPPROTO_TCP) + printf("%6s", "tcp"); + else + printf("%6ld", head->pml_map.pm_prot); + printf("%7ld", head->pml_map.pm_port); + rpc = getrpcbynumber(head->pml_map.pm_prog); + if (rpc) + printf(" %s\n", rpc->r_name); + else + printf("\n"); + } + } +} + +/* + * reply_proc collects replies from the broadcast. + * to get a unique list of responses the output of rpcinfo should + * be piped through sort(1) and then uniq(1). + */ + +/*ARGSUSED*/ +static bool_t +reply_proc(res, who) + void *res; /* Nothing comes back */ + struct sockaddr_in *who; /* Who sent us the reply */ +{ + register struct hostent *hp; + + hp = gethostbyaddr((char *) &who->sin_addr, sizeof who->sin_addr, + AF_INET); + printf("%s %s\n", inet_ntoa(who->sin_addr), + (hp == NULL) ? "(unknown)" : hp->h_name); + return(FALSE); +} + +static void +brdcst(argc, argv) + int argc; + char **argv; +{ + enum clnt_stat rpc_stat; + u_long prognum, vers; + + if (argc != 2) { + usage(); + exit(1); + } + prognum = getprognum(argv[0]); + vers = getvers(argv[1]); + rpc_stat = clnt_broadcast(prognum, vers, NULLPROC, xdr_void, + (char *)NULL, xdr_void, (char *)NULL, reply_proc); + if ((rpc_stat != RPC_SUCCESS) && (rpc_stat != RPC_TIMEDOUT)) { + fprintf(stderr, "rpcinfo: broadcast failed: %s\n", + clnt_sperrno(rpc_stat)); + exit(1); + } + exit(0); +} + +static void +deletereg(argc, argv) + int argc; + char **argv; +{ u_long prog_num, version_num ; + + if (argc != 2) { + usage() ; + exit(1) ; + } + if (getuid()) { /* This command allowed only to root */ + fprintf(stderr, "Sorry. You are not root\n") ; + exit(1) ; + } + prog_num = getprognum(argv[0]); + version_num = getvers(argv[1]); + if ((pmap_unset(prog_num, version_num)) == 0) { + fprintf(stderr, "rpcinfo: Could not delete registration for prog %s version %s\n", + argv[0], argv[1]) ; + exit(1) ; + } +} + +static void +usage() +{ + fprintf(stderr, "Usage: rpcinfo [ -n portnum ] -u host prognum [ versnum ]\n"); + fprintf(stderr, " rpcinfo [ -n portnum ] -t host prognum [ versnum ]\n"); + fprintf(stderr, " rpcinfo -p [ host ]\n"); + fprintf(stderr, " rpcinfo -b prognum versnum\n"); + fprintf(stderr, " rpcinfo -d prognum versnum\n") ; +} + +static u_long +getprognum(arg) + char *arg; +{ + register struct rpcent *rpc; + register u_long prognum; + + if (isalpha(*arg)) { + rpc = getrpcbyname(arg); + if (rpc == NULL) { + fprintf(stderr, "rpcinfo: %s is unknown service\n", + arg); + exit(1); + } + prognum = rpc->r_number; + } else { + prognum = (u_long) atoi(arg); + } + + return (prognum); +} + +static u_long +getvers(arg) + char *arg; +{ + register u_long vers; + + vers = (int) atoi(arg); + return (vers); +} + +static void +get_inet_address(addr, host) + struct sockaddr_in *addr; + char *host; +{ + register struct hostent *hp; + + bzero((char *)addr, sizeof *addr); + addr->sin_addr.s_addr = (u_long) inet_addr(host); + if (addr->sin_addr.s_addr == -1 || addr->sin_addr.s_addr == 0) { + if ((hp = gethostbyname(host)) == NULL) { + fprintf(stderr, "rpcinfo: %s is unknown host\n", host); + exit(1); + } + bcopy(hp->h_addr, (char *)&addr->sin_addr, hp->h_length); + } + addr->sin_family = AF_INET; +} diff --git a/usr.bin/rsh/Makefile b/usr.bin/rsh/Makefile index 046c14ed94..434e8d1aa6 100644 --- a/usr.bin/rsh/Makefile +++ b/usr.bin/rsh/Makefile @@ -6,4 +6,10 @@ BINOWN= root BINMODE=4555 .PATH: ${.CURDIR}/../rlogin +.if exists(/usr/lib/libcrypt.a) +#CFLAGS+=-DCRYPT -DKERBEROS +DPADD+= ${LIBCRYPT} #${LIBKRB} +LDADD+= -lcrypt #-lkrb +.endif + .include <bsd.prog.mk> diff --git a/usr.bin/rup/Makefile b/usr.bin/rup/Makefile new file mode 100644 index 0000000000..84b43a8548 --- /dev/null +++ b/usr.bin/rup/Makefile @@ -0,0 +1,9 @@ +# $Id: Makefile,v 1.3 1993/08/02 17:55:38 mycroft Exp $ + +PROG= rup +MAN1= rup.1 + +DPADD= ${LIBRPCSVC} ${LIBRPC} +LDADD= -lrpcsvc -lrpc + +.include <bsd.prog.mk> diff --git a/usr.bin/rup/rup.1 b/usr.bin/rup/rup.1 new file mode 100644 index 0000000000..00d9242f10 --- /dev/null +++ b/usr.bin/rup/rup.1 @@ -0,0 +1,94 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 1985, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: rup.1,v 1.4 1993/08/02 17:55:40 mycroft Exp $ +.\" +.Dd June 7, 1993 +.Dt RUP 1 +.Os BSD 4.3 +.Sh NAME +.Nm rup +.Nd remote status display +.Sh SYNOPSIS +.Nm rup +.Op Ar host ... +.Sh DESCRIPTION +.Nm rup +displays a summary of the current system status of a particular +.Em host +or all hosts on the local network. +The output shows the current time of day, how long the system has +been up, +and the load averages. +The load average numbers give the number of jobs in the run queue +averaged over 1, 5 and 15 minutes. +.Pp +The +.Xr rpc.rstatd 8 +daemon must be running on the remote host for this command to +work. +.Nm rup +uses an RPC protocol defined in /usr/include/rpcsvc/rstat.x. +.Sh EXAMPLE +.Bd -unfilled -offset indent -compact +example% rup otherhost +otherhost 7:36am up 6 days, 16:45, load average: 0.20, 0.23, 0.18 +example% +.Ed +.Sh DIAGNOSTICS +.Bl -tag -width indent +.It rup: RPC: Program not registered +The +.Xr rpc.rstatd 8 +daemon has not been started on the remote host. +.It rup: RPC: Timed out +A communication error occurred. Either the network is +excessively congested, or the +.Xr rpc.rstatd 8 +daemon has terminated on the remote host. +.It rup: RPC: Port mapper failure - RPC: Timed out +The remote host is not running the portmapper (see +.Xr portmap 8 ), +and cannot accomodate any RPC-based services. The host may be down. +.El +.Sh SEE ALSO +.Xr portmap 8 , +.Xr rpc.rstatd 8 +.Sh HISTORY +The +.Nm rwall +command +appeared in +.Em Sun-OS . +.Sh BUGS +The sorting options are not implemented. diff --git a/usr.bin/rup/rup.c b/usr.bin/rup/rup.c new file mode 100644 index 0000000000..3957daf222 --- /dev/null +++ b/usr.bin/rup/rup.c @@ -0,0 +1,223 @@ +/*- + * Copyright (c) 1993, John Brezak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$Id: rup.c,v 1.6 1993/09/23 18:37:28 jtc Exp $"; +#endif /* not lint */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netdb.h> +#include <rpc/rpc.h> +#include <arpa/inet.h> + +#undef FSHIFT /* Use protocol's shift and scale values */ +#undef FSCALE +#include <rpcsvc/rstat.h> + +#define HOST_WIDTH 15 + +char *argv0; + +struct host_list { + struct host_list *next; + struct in_addr addr; +} *hosts; + +int search_host(struct in_addr addr) +{ + struct host_list *hp; + + if (!hosts) + return(0); + + for (hp = hosts; hp != NULL; hp = hp->next) { + if (hp->addr.s_addr == addr.s_addr) + return(1); + } + return(0); +} + +void remember_host(struct in_addr addr) +{ + struct host_list *hp; + + if (!(hp = (struct host_list *)malloc(sizeof(struct host_list)))) { + fprintf(stderr, "%s: no memory.\n", argv0); + exit(1); + } + hp->addr.s_addr = addr.s_addr; + hp->next = hosts; + hosts = hp; +} + +rstat_reply(char *replyp, struct sockaddr_in *raddrp) +{ + struct tm *tmp_time; + struct tm host_time; + struct tm host_uptime; + char days_buf[16]; + char hours_buf[16]; + struct hostent *hp; + char *host; + statstime *host_stat = (statstime *)replyp; + + if (search_host(raddrp->sin_addr)) + return(0); + + hp = gethostbyaddr((char *)&raddrp->sin_addr.s_addr, + sizeof(struct in_addr), AF_INET); + if (hp) + host = hp->h_name; + else + host = inet_ntoa(raddrp->sin_addr); + + printf("%-*s\t", HOST_WIDTH, host); + + tmp_time = localtime((time_t *)&host_stat->curtime.tv_sec); + host_time = *tmp_time; + + host_stat->curtime.tv_sec -= host_stat->boottime.tv_sec; + + tmp_time = gmtime((time_t *)&host_stat->curtime.tv_sec); + host_uptime = *tmp_time; + + if (host_uptime.tm_yday != 0) + sprintf(days_buf, "%3d day%s, ", host_uptime.tm_yday, + (host_uptime.tm_yday > 1) ? "s" : ""); + else + days_buf[0] = '\0'; + + if (host_uptime.tm_hour != 0) + sprintf(hours_buf, "%2d:%02d, ", + host_uptime.tm_hour, host_uptime.tm_min); + else + if (host_uptime.tm_min != 0) + sprintf(hours_buf, "%2d mins, ", host_uptime.tm_min); + else + hours_buf[0] = '\0'; + + printf(" %2d:%02d%cm up %9.9s%9.9s load average: %.2f %.2f %.2f\n", + host_time.tm_hour % 12, + host_time.tm_min, + (host_time.tm_hour >= 12) ? 'p' : 'a', + days_buf, + hours_buf, + (double)host_stat->avenrun[0]/FSCALE, + (double)host_stat->avenrun[1]/FSCALE, + (double)host_stat->avenrun[2]/FSCALE); + + remember_host(raddrp->sin_addr); + return(0); +} + +onehost(char *host) +{ + CLIENT *rstat_clnt; + statstime host_stat; + struct sockaddr_in addr; + struct hostent *hp; + + hp = gethostbyname(host); + if (hp == NULL) { + fprintf(stderr, "%s: unknown host \"%s\"\n", + argv0, host); + return(-1); + } + + rstat_clnt = clnt_create(host, RSTATPROG, RSTATVERS_TIME, "udp"); + if (rstat_clnt == NULL) { + fprintf(stderr, "%s: %s %s", argv0, host, clnt_spcreateerror("")); + return(-1); + } + + bzero((char *)&host_stat, sizeof(host_stat)); + if (clnt_call(rstat_clnt, RSTATPROC_STATS, xdr_void, NULL, xdr_statstime, &host_stat, NULL) != RPC_SUCCESS) { + fprintf(stderr, "%s: %s: %s\n", argv0, host, clnt_sperror(rstat_clnt, host)); + return(-1); + } + + addr.sin_addr.s_addr = *(int *)hp->h_addr; + rstat_reply((char *)&host_stat, &addr); +} + +allhosts() +{ + statstime host_stat; + enum clnt_stat clnt_stat; + + clnt_stat = clnt_broadcast(RSTATPROG, RSTATVERS_TIME, RSTATPROC_STATS, + xdr_void, NULL, + xdr_statstime, &host_stat, rstat_reply); + if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_TIMEDOUT) { + fprintf(stderr, "%s: %s\n", argv0, clnt_sperrno(clnt_stat)); + exit(1); + } +} + +usage() +{ + fprintf(stderr, "Usage: %s [hosts ...]\n", argv0); + exit(1); +} + +main(int argc, char *argv[]) +{ + int ch; + extern int optind; + + if (!(argv0 = rindex(argv[0], '/'))) + argv0 = argv[0]; + else + argv0++; + + while ((ch = getopt(argc, argv, "?")) != -1) + switch (ch) { + default: + usage(); + /*NOTREACHED*/ + } + + setlinebuf(stdout); + if (argc == optind) + allhosts(); + else { + for (; optind < argc; optind++) + (void) onehost(argv[optind]); + } + exit(0); +} diff --git a/usr.bin/rusers/Makefile b/usr.bin/rusers/Makefile new file mode 100644 index 0000000000..cfc3f428e2 --- /dev/null +++ b/usr.bin/rusers/Makefile @@ -0,0 +1,9 @@ +# $Id: Makefile,v 1.4 1993/08/02 17:55:45 mycroft Exp $ + +PROG = rusers +MAN1 = rusers.1 + +DPADD= ${LIBRPCSVC} ${LIBRPC} +LDADD= -lrpcsvc -lrpc + +.include <bsd.prog.mk> diff --git a/usr.bin/rusers/rusers.1 b/usr.bin/rusers/rusers.1 new file mode 100644 index 0000000000..b7220765d8 --- /dev/null +++ b/usr.bin/rusers/rusers.1 @@ -0,0 +1,93 @@ +.\" Copyright (c) 1983, 1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)rusers.1 6.7 (Berkeley) 4/23/91 +.\" $Id: rusers.1,v 1.2 1993/08/01 07:29:09 mycroft Exp $ +.\" +.Dd April 23, 1991 +.Dt RUSERS 1 +.Os BSD 4.2 +.Sh NAME +.Nm rusers +.Nd who is logged in to machines on local network +.Sh SYNOPSIS +.Nm rusers +.Op Fl al +.Op Ar host ... +.Sh DESCRIPTION +The +.Nm rusers +command produces output similar to +.Xr who , +but for the list of hosts or all machines on the local +network. For each host responding to the rusers query, +the hostname with the names of the users currently logged +on is printed on each line. The rusers command will wait for +one minute to catch late responders. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl a +Print all machines responding even if no one is currently logged in. +.It Fl l +Print a long format listing. This includes the user name, host name, +tty that the user is logged in to, the date and time the user +logged in, the amount of time since the user typed on the keyboard, +and the remote host they logged in from (if applicable). +.El +.Sh DIAGNOSTICS +.Bl -tag -width indent +.It rusers: RPC: Program not registered +The +.Xr rpc.rusersd 8 +daemon has not been started on the remote host. +.It rusers: RPC: Timed out +A communication error occurred. Either the network is +excessively congested, or the +.Xr rpc.rusersd 8 +daemon has terminated on the remote host. +.It rusers: RPC: Port mapper failure - RPC: Timed out +The remote host is not running the portmapper (see +.Xr portmap 8 ), +and cannot accomodate any RPC-based services. The host may be down. +.El +.Sh SEE ALSO +.Xr portmap 8 , +.Xr who 1 , +.Xr rpc.rusersd 8 +.Sh HISTORY +The +.Nm rusers +command +appeared in +.Em Sun-OS . +.Sh BUGS +The sorting options are not implemented. diff --git a/usr.bin/rusers/rusers.c b/usr.bin/rusers/rusers.c new file mode 100644 index 0000000000..4efaba86ba --- /dev/null +++ b/usr.bin/rusers/rusers.c @@ -0,0 +1,249 @@ +/*- + * Copyright (c) 1993, John Brezak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$Id: rusers.c,v 1.4 1993/08/02 17:55:46 mycroft Exp $"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netdb.h> +#include <stdio.h> +#include <strings.h> +#include <rpc/rpc.h> +#include <arpa/inet.h> +#include <rpcsvc/rnusers.h> + +#define MAX_INT 0x7fffffff +#define HOST_WIDTH 20 +#define LINE_WIDTH 15 +char *argv0; + +int longopt; +int allopt; + +struct host_list { + struct host_list *next; + struct in_addr addr; +} *hosts; + +int search_host(struct in_addr addr) +{ + struct host_list *hp; + + if (!hosts) + return(0); + + for (hp = hosts; hp != NULL; hp = hp->next) { + if (hp->addr.s_addr == addr.s_addr) + return(1); + } + return(0); +} + +void remember_host(struct in_addr addr) +{ + struct host_list *hp; + + if (!(hp = (struct host_list *)malloc(sizeof(struct host_list)))) { + fprintf(stderr, "%s: no memory.\n", argv0); + exit(1); + } + hp->addr.s_addr = addr.s_addr; + hp->next = hosts; + hosts = hp; +} + +rusers_reply(char *replyp, struct sockaddr_in *raddrp) +{ + int x, idle; + char date[32], idle_time[64], remote[64]; + struct hostent *hp; + utmpidlearr *up = (utmpidlearr *)replyp; + char *host; + int days, hours, minutes, seconds; + + if (search_host(raddrp->sin_addr)) + return(0); + + if (!allopt && !up->utmpidlearr_len) + return(0); + + hp = gethostbyaddr((char *)&raddrp->sin_addr.s_addr, + sizeof(struct in_addr), AF_INET); + if (hp) + host = hp->h_name; + else + host = inet_ntoa(raddrp->sin_addr); + + if (!longopt) + printf("%-*s ", HOST_WIDTH, host); + + for (x = 0; x < up->utmpidlearr_len; x++) { + strncpy(date, + &(ctime((time_t *)&(up->utmpidlearr_val[x].ui_utmp.ut_time))[4]), + sizeof(date)-1); + + idle = up->utmpidlearr_val[x].ui_idle; + sprintf(idle_time, " :%02d", idle); + if (idle == MAX_INT) + strcpy(idle_time, "??"); + else if (idle == 0) + strcpy(idle_time, ""); + else { + seconds = idle; + days = seconds/(60*60*24); + seconds %= (60*60*24); + hours = seconds/(60*60); + seconds %= (60*60); + minutes = seconds/60; + seconds %= 60; + if (idle > 60) + sprintf(idle_time, "%d:%02d", + minutes, seconds); + if (idle >= (60*60)) + sprintf(idle_time, "%d:%02d:%02d", + hours, minutes, seconds); + if (idle >= (24*60*60)) + sprintf(idle_time, "%d days, %d:%02d:%02d", + days, hours, minutes, seconds); + } + + strncpy(remote, up->utmpidlearr_val[x].ui_utmp.ut_host, sizeof(remote)-1); + if (strlen(remote) != 0) + sprintf(remote, "(%.16s)", up->utmpidlearr_val[x].ui_utmp.ut_host); + + if (longopt) + printf("%-8.8s %*s:%-*.*s %-12.12s %6s %.18s\n", + up->utmpidlearr_val[x].ui_utmp.ut_name, + HOST_WIDTH, host, + LINE_WIDTH, LINE_WIDTH, up->utmpidlearr_val[x].ui_utmp.ut_line, + date, + idle_time, + remote + ); + else + printf("%s ", + up->utmpidlearr_val[x].ui_utmp.ut_name); + } + if (!longopt) + putchar('\n'); + + remember_host(raddrp->sin_addr); + return(0); +} + +onehost(char *host) +{ + utmpidlearr up; + CLIENT *rusers_clnt; + struct sockaddr_in addr; + struct hostent *hp; + + hp = gethostbyname(host); + if (hp == NULL) { + fprintf(stderr, "%s: unknown host \"%s\"\n", + argv0, host); + exit(1); + } + + rusers_clnt = clnt_create(host, RUSERSPROG, RUSERSVERS_IDLE, "udp"); + if (rusers_clnt == NULL) { + clnt_pcreateerror(argv0); + exit(1); + } + + bzero((char *)&up, sizeof(up)); + if (clnt_call(rusers_clnt, RUSERSPROC_NAMES, xdr_void, NULL, xdr_utmpidlearr, &up, NULL) != RPC_SUCCESS) { + clnt_perror(rusers_clnt, argv0); + exit(1); + } + addr.sin_addr.s_addr = *(int *)hp->h_addr; + rusers_reply((char *)&up, &addr); +} + +allhosts() +{ + utmpidlearr up; + enum clnt_stat clnt_stat; + + bzero((char *)&up, sizeof(up)); + clnt_stat = clnt_broadcast(RUSERSPROG, RUSERSVERS_IDLE, RUSERSPROC_NAMES, + xdr_void, NULL, + xdr_utmpidlearr, &up, rusers_reply); + if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_TIMEDOUT) { + fprintf(stderr, "%s: %s\n", argv0, clnt_sperrno(clnt_stat)); + exit(1); + } +} + +usage() +{ + fprintf(stderr, "Usage: %s [-la] [hosts ...]\n", argv0); + exit(1); +} + +main(int argc, char *argv[]) +{ + int ch; + extern int optind; + + if (!(argv0 = rindex(argv[0], '/'))) + argv0 = argv[0]; + else + argv0++; + + + while ((ch = getopt(argc, argv, "al")) != -1) + switch (ch) { + case 'a': + allopt++; + break; + case 'l': + longopt++; + break; + default: + usage(); + /*NOTREACHED*/ + } + + setlinebuf(stdout); + if (argc == optind) + allhosts(); + else { + for (; optind < argc; optind++) + (void) onehost(argv[optind]); + } + exit(0); +} diff --git a/usr.bin/rwall/Makefile b/usr.bin/rwall/Makefile new file mode 100644 index 0000000000..fa3e7aa6f4 --- /dev/null +++ b/usr.bin/rwall/Makefile @@ -0,0 +1,9 @@ +# $Id: Makefile,v 1.5 1993/08/02 17:55:49 mycroft Exp $ + +PROG = rwall +MAN1 = rwall.1 + +DPADD= ${LIBRPCSVC} ${LIBRPC} +LDADD= -lrpcsvc -lrpc + +.include <bsd.prog.mk> diff --git a/usr.bin/rwall/rwall.1 b/usr.bin/rwall/rwall.1 new file mode 100644 index 0000000000..680c3e9a65 --- /dev/null +++ b/usr.bin/rwall/rwall.1 @@ -0,0 +1,79 @@ +.\" Copyright (c) 1983, 1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)rwall.1 6.7 (Berkeley) 4/23/91 +.\" $Id: rwall.1,v 1.2 1993/08/01 07:29:02 mycroft Exp $ +.\" +.Dd April 23, 1991 +.Dt RWALL 1 +.Os BSD 4.2 +.Sh NAME +.Nm rwall +.Nd send a message to users logged on a host +.Sh SYNOPSIS +.Nm rwall +.Ar host +.Op Ar file +.Sh DESCRIPTION +The +.Nm rwall +command sends a message to the users logged into the specified host. The +message to be sent can be typed in and terminated with EOF or it can +be in a +.Ar file . +.Sh DIAGNOSTICS +.Bl -tag -width indent +.It rwall: RPC: Program not registered +The +.Xr rpc.rwalld 8 +daemon has not been started on the remote host. +.It rwall: RPC: Timed out +A communication error occurred. Either the network is +excessively congested, or the +.Xr rpc.rwalld 8 +daemon has terminated on the remote host. +.It rwall: RPC: Port mapper failure - RPC: Timed out +The remote host is not running the portmapper (see +.Xr portmap 8 ), +and cannot accomodate any RPC-based services. The host may be down. +.El +.Sh SEE ALSO +.Xr portmap 8 , +.Xr who 1 , +.Xr rpc.rwalld 8 +.Sh HISTORY +The +.Nm rwall +command +appeared in +.Em Sun-OS . +.Sh BUGS +The sorting options are not implemented. diff --git a/usr.bin/rwall/rwall.c b/usr.bin/rwall/rwall.c new file mode 100644 index 0000000000..b3cde1f9ed --- /dev/null +++ b/usr.bin/rwall/rwall.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 1993 Christopher G. Demetriou + * Copyright (c) 1988, 1990 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1988 Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)wall.c 5.14 (Berkeley) 3/2/91";*/ +static char rcsid[] = "$Id: rwall.c,v 1.4 1993/08/01 18:08:48 mycroft Exp $"; +#endif /* not lint */ + +/* + * This program is not related to David Wall, whose Stanford Ph.D. thesis + * is entitled "Mechanisms for Broadcast and Selective Broadcast". + */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/uio.h> +#include <utmp.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <paths.h> + +#include <rpc/rpc.h> +#include <rpcsvc/rwall.h> + +int mbufsize; +char *mbuf; + +/* ARGSUSED */ +main(argc, argv) + int argc; + char **argv; +{ + char *wallhost, res; + CLIENT *cl; + + if ((argc < 2) || (argc > 3)) { + fprintf(stderr, "usage: %s hostname [file]\n", argv[0]); + exit(1); + } + + wallhost = argv[1]; + + makemsg(argv[2]); + + /* + * Create client "handle" used for calling MESSAGEPROG on the + * server designated on the command line. We tell the rpc package + * to use the "tcp" protocol when contacting the server. + */ + cl = clnt_create(wallhost, WALLPROG, WALLVERS, "udp"); + if (cl == NULL) { + /* + * Couldn't establish connection with server. + * Print error message and die. + */ + clnt_pcreateerror(wallhost); + exit(1); + } + + if (clnt_call(cl, WALLPROC_WALL, xdr_wrapstring, &mbuf, xdr_void, &res, NULL) != RPC_SUCCESS) { + /* + * An error occurred while calling the server. + * Print error message and die. + */ + clnt_perror(cl, wallhost); + exit(1); + } + + exit(0); +} + +makemsg(fname) + char *fname; +{ + register int ch, cnt; + struct tm *lt; + struct passwd *pw; + struct stat sbuf; + time_t now, time(); + FILE *fp; + int fd; + char *p, *whom, hostname[MAXHOSTNAMELEN], lbuf[100], tmpname[15]; + char *getlogin(), *strcpy(), *ttyname(); + + (void)strcpy(tmpname, _PATH_TMP); + (void)strcat(tmpname, "/wall.XXXXXX"); + if (!(fd = mkstemp(tmpname)) || !(fp = fdopen(fd, "r+"))) { + (void)fprintf(stderr, "wall: can't open temporary file.\n"); + exit(1); + } + (void)unlink(tmpname); + + if (!(whom = getlogin())) + whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; + (void)gethostname(hostname, sizeof(hostname)); + (void)time(&now); + lt = localtime(&now); + + /* + * all this stuff is to blank out a square for the message; + * we wrap message lines at column 79, not 80, because some + * terminals wrap after 79, some do not, and we can't tell. + * Which means that we may leave a non-blank character + * in column 80, but that can't be helped. + */ + (void)fprintf(fp, "Remote Broadcast Message from %s@%s\n", + whom, hostname); + (void)fprintf(fp, " (%s) at %d:%02d ...\n", ttyname(2), + lt->tm_hour, lt->tm_min); + + putc('\n', fp); + + if (fname && !(freopen(fname, "r", stdin))) { + (void)fprintf(stderr, "wall: can't read %s.\n", fname); + exit(1); + } + while (fgets(lbuf, sizeof(lbuf), stdin)) + fputs(lbuf, fp); + rewind(fp); + + if (fstat(fd, &sbuf)) { + (void)fprintf(stderr, "wall: can't stat temporary file.\n"); + exit(1); + } + mbufsize = sbuf.st_size; + if (!(mbuf = malloc((u_int)mbufsize))) { + (void)fprintf(stderr, "wall: out of memory.\n"); + exit(1); + } + if (fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize) { + (void)fprintf(stderr, "wall: can't read temporary file.\n"); + exit(1); + } + (void)close(fd); +} diff --git a/usr.bin/sccs/Makefile b/usr.bin/sccs/Makefile deleted file mode 100644 index be42d485d1..0000000000 --- a/usr.bin/sccs/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# @(#)Makefile 5.3 (Berkeley) 5/11/90 - -PROG= sccs - -.include <bsd.prog.mk> diff --git a/usr.bin/sccs/pathnames.h b/usr.bin/sccs/pathnames.h deleted file mode 100644 index 5510033619..0000000000 --- a/usr.bin/sccs/pathnames.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)pathnames.h 5.6 (Berkeley) 3/4/91 - */ - -#include <paths.h> - -#define _PATH_SCCSADMIN "/usr/local/bin/admin" -#define _PATH_SCCSBDIFF "/usr/local/bin/bdiff" -#define _PATH_SCCSCOMB "/usr/local/bin/comb" -#define _PATH_SCCSDELTA "/usr/local/bin/delta" -#define _PATH_SCCSDIFF "/usr/local/bin/sccsdiff" -#define _PATH_SCCSGET "/usr/local/bin/get" -#define _PATH_SCCSHELP "/usr/local/bin/help" -#define _PATH_SCCSPRS "/usr/local/bin/prs" -#define _PATH_SCCSPRT "/usr/local/bin/prt" -#define _PATH_SCCSRMDEL "/usr/local/bin/rmdel" -#define _PATH_SCCSVAL "/usr/local/bin/val" -#define _PATH_SCCSWHAT "/usr/local/bin/what" -#undef _PATH_TMP -#define _PATH_TMP "/tmp/sccsXXXXX" diff --git a/usr.bin/sccs/sccs.1 b/usr.bin/sccs/sccs.1 deleted file mode 100644 index d38f3ce2df..0000000000 --- a/usr.bin/sccs/sccs.1 +++ /dev/null @@ -1,398 +0,0 @@ -.\" Copyright (c) 1983, 1990 The Regents of the University of California. -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)sccs.1 2.10 (Berkeley) 7/24/91 -.\" -.Dd July 24, 1991 -.Dt SCCS 1 -.Os BSD 4.2 -.Sh NAME -.Nm sccs -.Nd front end for the -.Li SCCS -subsystem -.Sh SYNOPSIS -.Nm sccs -.Op Fl r -.Op Fl d Ar path -.Op Fl p Ar path -.Ar command -.Op flags -.Op Ar -.Sh DESCRIPTION -.Nm Sccs -is a front end to the -.Li SCCS -programs -that -helps them mesh more cleanly -with -the rest of UNIX. -It -also includes the capability to run -.Dq set user id -to another user -to -provide additional protection. -.Pp -Basically, -.Nm sccs -runs the command with the specified -.Ar flags -and -.Ar args . -Each argument is normally modified to be prepended with -.Dq Li SCCS/s. . -.Pp -Flags to be interpreted by the -.Nm sccs -program must be before the -.Ar command -argument. -Flags to be passed to the actual -.Li SCCS -program must come after the -.Ar command -argument. -These flags are specific to the command and -are discussed in the documentation for that command. -.Pp -Besides the usual -.Li SCCS -commands, -several -.Dq pseudo-commands -can be issued. -These are: -.Bl -tag -width deledit -.It Cm edit -Equivalent -to -.Dq Li get \-e . -.It Cm delget -Perform a delta on the named files and -then get new versions. -The new versions will have id keywords expanded, and -will not be editable. -The -.Fl m , -.Fl p , -.Fl r , -.Fl s , -and -.Fl y -flags will be passed to -.Nm delta , -and the -.Fl b, -.Fl c , -.Fl e , -.Fl i , -.Fl k , -.Fl l , -.Fl s , -.\" anybody who has a bad xterm which is almost anyone -and -.Fl x -flags will be passed to get. -.It Cm deledit -Equivalent -to -.Nm delget -except that the -.Nm get -phase includes the -.Fl e -flag. -This -option is useful for making a -.Em checkpoint -of your current editing phase. The same flags will be passed to delta -as described above, and -all the flags listed for -.om get -above except -.Fl e -and -.Fl k -are -passed to -.Nm edit . -.It Cm create -Creates -an -.Li SCCS -file , -taking -the initial contents from the file of the same name. -Any -flags to -.Nm admin -are accepted. If the creation is successful, -the files are renamed with a comma on the front. -These should be removed when you are convinced that the -.Li SCCS -files -have been created successfully. -.It Cm fix -Must -be followed by a -.Fl r -flag. -This command essentially removes the named delta, but -leaves you with a copy of the delta -with the changes that were in it. It -is useful for fixing small compiler bugs, etc. -Since it doesn't leave audit trails, it should be used carefully. -.It Cm clean -This routine removes everything from the current directory -that can be recreated from SCCS files. -It will not remove any files being edited. -If the -.Fl b -flag is given, branches are ignored in the determination of -whether they are being edited; this -is dangerous if you are keeping the branches in the -same directory. -.It Cm unedit -This -is the opposite of an -.Nm edit -or -a -.Dq Li get \-e . -It should be used with extreme caution, since -any changes you made since the get will be irretrievably lost. -.It Cm info -Gives a listing of all files being edited. -If the -.Fl b -flag -is given, branches (i.e., -.Li SID Ns \&\'s -with two or fewer components) -are ignored. If the -.Fl u -flag is given (with an optional argument) then -only files being edited by you (or the named user) are listed. -.It Cm check -Like -.Nm info -except that nothing is printed if nothing is being edited, and -a non-zero exit status is returned if anything is being edited. -The intent is to have this included in an -.Em install -entry in a makefile to insure that everything is included into the -.Li SCCS -file before a version is installed. -.It Cm tell -Gives a newline-separated list of the files being edited -on the standard output. Takes the -.Fl b -and -.Fl u -flags like -.Nm info -and -.Nm check . -.It Cm diffs -Gives a -.Nm diff -listing between the current version of the -program(s) you have out for editing and the versions in -.Li SCCS -format. -The -.Fl r , -.Fl c , -.Fl i , -.Fl x , -and -.Fl t -flags are passed to -.if n \{\ -. br -.\} -.Nm get ; -the -.Fl l , -.Fl s , -.Fl e , -.Fl f , -.Fl h , -and -.Fl b -options are passed to -.if n \{\ -. br -.\} -.Nm diff . -The -.Fl C -flag is passed to -.Nm diff -as -.Fl c . -.It Cm print -This command prints out verbose information -about the named files. -.Pp -.It Fl r -Runs -.Nm sccs -as the real user rather than as whatever effective user -.Nm sccs -is -.Dq Li set user id -to. -.It Fl d -Specifies a root directory for the -.Li SCCS -files. -The default is the current directory. -If environment variable -.Ev PROJECT -is set, -it will be used to determine the -.Fl d -flag. -.It Fl p -Defines the pathname of the directory in which the -.Li SCCS -files will be found; -.Dq Li SCCS -is the default. -The -.Fl p -flag -differs from the -.Fl d -flag -in that the -.Fl d -argument is prepended to the entire pathname and the -.Fl p -argument is inserted before the final component of the pathname. -For example, -.Dq Li sccs \-d/x \-py get a/b -will convert to -.Dq Li get /x/a/y/s.b . -The intent here is to create aliases such as -.Dq Li alias syssccs sccs -d/usr/src -which -will be used as -.Dq Li syssccs get cmd/who.c . -.Pp -Certain -commands (such as -.Nm admin ) -cannot be run -.Dq Li set user id -by all users, since this would allow anyone to change the authorizations. -These commands are always run as the real user. -.Sh EXAMPLES -To get a file for editing, -edit it, -and produce a new delta: -.Pp -.Dl sccs get \-e file.c -.Dl ex file.c -.Dl sccs delta file.c -.Pp -To get a file from another directory: -.Pp -.Dl sccs \-p/usr/src/sccs/s. get cc.c -.Pp -or -.Pp -.Dl sccs get /usr/src/sccs/s.cc.c -.Pp -To make a delta of a large number of files -in the current directory: -.Pp -.Dl sccs delta *.c -.Pp -To get a list of files being edited that are not on branches: -.Pp -.Dl sccs info \-b -.Pp -To delta everything being edited by you: -.Pp -.Dl sccs delta \`sccs tell \-u\` -.Pp -In a makefile, to get source files -from an -.Li SCCS -file if it does not already exist: -.Pp -.Dl SRCS = <list of source files> -.Dl $(SRCS): -.Dl \&\tsccs get $(REL) $@ -.Sh ENVIRONMENT -.Bl -tag -width Ar -.It Ev PROJECT -The PROJECT environment variable is checked by the -.Fl d -flag. If -it begins with a slash, it is taken directly; otherwise, -the home directory of a user of that name is -examined for a subdirectory -.Dq Li src -or -.Dq Li source . -If such a directory is found, it is used. -.El -.Sh SEE ALSO -.Xr what 1 -.Xr admin SCCS , -.Xr chghist SCCS , -.Xr comb SCCS , -.Xr delta SCCS , -.Xr get SCCS , -.Xr help SCCS , -.Xr prt SCCS , -.Xr rmdel SCCS , -.Xr sccsdiff SCCS , -.Rs -.%A Eric Allman -.%T "An Introduction to the Source Code Control System" -.Re -.Sh HISTORY -The -.Nm sccs -command -appeared in -.Bx 4.3 . -.Sh BUGS -It should be able to take directory arguments on pseudo-commands -like the -.Li SCCS -commands do. diff --git a/usr.bin/sccs/sccs.c b/usr.bin/sccs/sccs.c deleted file mode 100644 index d244092c04..0000000000 --- a/usr.bin/sccs/sccs.c +++ /dev/null @@ -1,1623 +0,0 @@ -/* - * Copyright (c) 1980 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1980 Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)sccs.c 5.11 (Berkeley) 3/4/91"; -#endif /* not lint */ - -#include <sys/cdefs.h> -#include <sys/param.h> -#include <sys/stat.h> -#include <sys/dir.h> -#include <signal.h> -#include <sysexits.h> -#include <errno.h> -#include <pwd.h> -#include <stdio.h> -#include "pathnames.h" - -/* -** SCCS.C -- human-oriented front end to the SCCS system. -** -** Without trying to add any functionality to speak of, this -** program tries to make SCCS a little more accessible to human -** types. The main thing it does is automatically put the -** string "SCCS/s." on the front of names. Also, it has a -** couple of things that are designed to shorten frequent -** combinations, e.g., "delget" which expands to a "delta" -** and a "get". -** -** This program can also function as a setuid front end. -** To do this, you should copy the source, renaming it to -** whatever you want, e.g., "syssccs". Change any defaults -** in the program (e.g., syssccs might default -d to -** "/usr/src/sys"). Then recompile and put the result -** as setuid to whomever you want. In this mode, sccs -** knows to not run setuid for certain programs in order -** to preserve security, and so forth. -** -** Usage: -** sccs [flags] command [args] -** -** Flags: -** -d<dir> <dir> represents a directory to search -** out of. It should be a full pathname -** for general usage. E.g., if <dir> is -** "/usr/src/sys", then a reference to the -** file "dev/bio.c" becomes a reference to -** "/usr/src/sys/dev/bio.c". -** -p<path> prepends <path> to the final component -** of the pathname. By default, this is -** "SCCS". For example, in the -d example -** above, the path then gets modified to -** "/usr/src/sys/dev/SCCS/s.bio.c". In -** more common usage (without the -d flag), -** "prog.c" would get modified to -** "SCCS/s.prog.c". In both cases, the -** "s." gets automatically prepended. -** -r run as the real user. -** -** Commands: -** admin, -** get, -** delta, -** rmdel, -** cdc, -** etc. Straight out of SCCS; only difference -** is that pathnames get modified as -** described above. -** enter Front end doing "sccs admin -i<name> <name>" -** create Macro for "enter" followed by "get". -** edit Macro for "get -e". -** unedit Removes a file being edited, knowing -** about p-files, etc. -** delget Macro for "delta" followed by "get". -** deledit Macro for "delta" followed by "get -e". -** branch Macro for "get -b -e", followed by "delta -** -s -n", followd by "get -e -t -g". -** diffs "diff" the specified version of files -** and the checked-out version. -** print Macro for "prs -e" followed by "get -p -m". -** tell List what files are being edited. -** info Print information about files being edited. -** clean Remove all files that can be -** regenerated from SCCS files. -** check Like info, but return exit status, for -** use in makefiles. -** fix Remove a top delta & reedit, but save -** the previous changes in that delta. -** -** Compilation Flags: -** UIDUSER -- determine who the user is by looking at the -** uid rather than the login name -- for machines -** where SCCS gets the user in this way. -** SCCSDIR -- if defined, forces the -d flag to take on -** this value. This is so that the setuid -** aspects of this program cannot be abused. -** This flag also disables the -p flag. -** SCCSPATH -- the default for the -p flag. -** MYNAME -- the title this program should print when it -** gives error messages. -** -** Compilation Instructions: -** cc -O -n -s sccs.c -** The flags listed above can be -D defined to simplify -** recompilation for variant versions. -** -** Author: -** Eric Allman, UCB/INGRES -** Copyright 1980 Regents of the University of California -*/ - - -/******************* Configuration Information ********************/ - -# ifndef SCCSPATH -# define SCCSPATH "SCCS" /* pathname in which to find s-files */ -# endif NOT SCCSPATH - -# ifndef MYNAME -# define MYNAME "sccs" /* name used for printing errors */ -# endif NOT MYNAME - -/**************** End of Configuration Information ****************/ - -typedef char bool; -# define TRUE 1 -# define FALSE 0 - -# define bitset(bit, word) ((bool) ((bit) & (word))) - -struct sccsprog -{ - char *sccsname; /* name of SCCS routine */ - short sccsoper; /* opcode, see below */ - short sccsflags; /* flags, see below */ - char *sccspath; /* pathname of binary implementing */ -}; - -/* values for sccsoper */ -# define PROG 0 /* call a program */ -# define CMACRO 1 /* command substitution macro */ -# define FIX 2 /* fix a delta */ -# define CLEAN 3 /* clean out recreatable files */ -# define UNEDIT 4 /* unedit a file */ -# define SHELL 5 /* call a shell file (like PROG) */ -# define DIFFS 6 /* diff between sccs & file out */ -# define DODIFF 7 /* internal call to diff program */ -# define ENTER 8 /* enter new files */ - -/* bits for sccsflags */ -# define NO_SDOT 0001 /* no s. on front of args */ -# define REALUSER 0002 /* protected (e.g., admin) */ - -/* modes for the "clean", "info", "check" ops */ -# define CLEANC 0 /* clean command */ -# define INFOC 1 /* info command */ -# define CHECKC 2 /* check command */ -# define TELLC 3 /* give list of files being edited */ - -/* -** Description of commands known to this program. -** First argument puts the command into a class. Second arg is -** info regarding treatment of this command. Third arg is a -** list of flags this command accepts from macros, etc. Fourth -** arg is the pathname of the implementing program, or the -** macro definition, or the arg to a sub-algorithm. -*/ - -struct sccsprog SccsProg[] = { - "admin", PROG, REALUSER, _PATH_SCCSADMIN, - "cdc", PROG, 0, _PATH_SCCSRMDEL, - "comb", PROG, 0, _PATH_SCCSCOMB, - "delta", PROG, 0, _PATH_SCCSDELTA, - "get", PROG, 0, _PATH_SCCSGET, - "help", PROG, NO_SDOT, _PATH_SCCSHELP, - "prs", PROG, 0, _PATH_SCCSPRS, - "prt", PROG, 0, _PATH_SCCSPRT, - "rmdel", PROG, REALUSER, _PATH_SCCSRMDEL, - "val", PROG, 0, _PATH_SCCSVAL, - "what", PROG, NO_SDOT, _PATH_SCCSWHAT, - "sccsdiff", SHELL, REALUSER, _PATH_SCCSDIFF, - "edit", CMACRO, NO_SDOT, "get -e", - "delget", CMACRO, NO_SDOT, "delta:mysrp/get:ixbeskcl -t", - "deledit", CMACRO, NO_SDOT, - "delta:mysrp -n/get:ixbskcl -e -t -g", - "fix", FIX, NO_SDOT, NULL, - "clean", CLEAN, REALUSER|NO_SDOT, - (char *) CLEANC, - "info", CLEAN, REALUSER|NO_SDOT, - (char *) INFOC, - "check", CLEAN, REALUSER|NO_SDOT, - (char *) CHECKC, - "tell", CLEAN, REALUSER|NO_SDOT, - (char *) TELLC, - "unedit", UNEDIT, NO_SDOT, NULL, - "diffs", DIFFS, NO_SDOT|REALUSER, - NULL, - "-diff", DODIFF, NO_SDOT|REALUSER, - _PATH_SCCSBDIFF, - "print", CMACRO, 0, "prs -e/get -p -m -s", - "branch", CMACRO, NO_SDOT, - "get:ixrc -e -b/delta: -s -n -ybranch-place-holder/get:pl -e -t -g", - "enter", ENTER, NO_SDOT, NULL, - "create", CMACRO, NO_SDOT, "enter/get:ixbeskcl -t", - NULL, -1, 0, NULL -}; - -/* one line from a p-file */ -struct pfile -{ - char *p_osid; /* old SID */ - char *p_nsid; /* new SID */ - char *p_user; /* user who did edit */ - char *p_date; /* date of get */ - char *p_time; /* time of get */ - char *p_aux; /* extra info at end */ -}; - -char *SccsPath = SCCSPATH; /* pathname of SCCS files */ -# ifdef SCCSDIR -char *SccsDir = SCCSDIR; /* directory to begin search from */ -# else -char *SccsDir = ""; -# endif -char MyName[] = MYNAME; /* name used in messages */ -int OutFile = -1; /* override output file for commands */ -bool RealUser; /* if set, running as real user */ -# ifdef DEBUG -bool Debug; /* turn on tracing */ -# endif -# ifndef V6 -extern char *getenv(); -# endif V6 - -extern char *sys_siglist[]; - -char *gstrcat(), *strcat(); -char *gstrncat(), *strncat(); -char *gstrcpy(), *strcpy(); -#define FBUFSIZ BUFSIZ -#define PFILELG 120 - -main(argc, argv) - int argc; - char **argv; -{ - register char *p; - extern struct sccsprog *lookup(); - register int i; -# ifndef V6 -# ifndef SCCSDIR - register struct passwd *pw; - extern struct passwd *getpwnam(); - char buf[FBUFSIZ]; - - /* pull "SccsDir" out of the environment (possibly) */ - p = getenv("PROJECTDIR"); - if (p != NULL && p[0] != '\0') - { - if (p[0] == '/') - SccsDir = p; - else - { - pw = getpwnam(p); - if (pw == NULL) - { - usrerr("user %s does not exist", p); - exit(EX_USAGE); - } - gstrcpy(buf, pw->pw_dir, sizeof(buf)); - gstrcat(buf, "/src", sizeof(buf)); - if (access(buf, 0) < 0) - { - gstrcpy(buf, pw->pw_dir, sizeof(buf)); - gstrcat(buf, "/source", sizeof(buf)); - if (access(buf, 0) < 0) - { - usrerr("project %s has no source!", p); - exit(EX_USAGE); - } - } - SccsDir = buf; - } - } -# endif SCCSDIR -# endif V6 - - /* - ** Detect and decode flags intended for this program. - */ - - if (argc < 2) - { - fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); - exit(EX_USAGE); - } - argv[argc] = NULL; - - if (lookup(argv[0]) == NULL) - { - while ((p = *++argv) != NULL) - { - if (*p != '-') - break; - switch (*++p) - { - case 'r': /* run as real user */ - setuid(getuid()); - RealUser++; - break; - -# ifndef SCCSDIR - case 'p': /* path of sccs files */ - SccsPath = ++p; - if (SccsPath[0] == '\0' && argv[1] != NULL) - SccsPath = *++argv; - break; - - case 'd': /* directory to search from */ - SccsDir = ++p; - if (SccsDir[0] == '\0' && argv[1] != NULL) - SccsDir = *++argv; - break; -# endif - -# ifdef DEBUG - case 'T': /* trace */ - Debug++; - break; -# endif - - default: - usrerr("unknown option -%s", p); - break; - } - } - if (SccsPath[0] == '\0') - SccsPath = "."; - } - - i = command(argv, FALSE, ""); - exit(i); -} - -/* -** COMMAND -- look up and perform a command -** -** This routine is the guts of this program. Given an -** argument vector, it looks up the "command" (argv[0]) -** in the configuration table and does the necessary stuff. -** -** Parameters: -** argv -- an argument vector to process. -** forkflag -- if set, fork before executing the command. -** editflag -- if set, only include flags listed in the -** sccsklets field of the command descriptor. -** arg0 -- a space-seperated list of arguments to insert -** before argv. -** -** Returns: -** zero -- command executed ok. -** else -- error status. -** -** Side Effects: -** none. -*/ - -command(argv, forkflag, arg0) - char **argv; - bool forkflag; - char *arg0; -{ - register struct sccsprog *cmd; - register char *p; - char buf[FBUFSIZ]; - extern struct sccsprog *lookup(); - char *nav[1000]; - char **np; - register char **ap; - register int i; - register char *q; - extern bool unedit(); - int rval = 0; - extern char *index(); - extern char *makefile(); - char *editchs; - extern char *tail(); - -# ifdef DEBUG - if (Debug) - { - printf("command:\n\t\"%s\"\n", arg0); - for (np = argv; *np != NULL; np++) - printf("\t\"%s\"\n", *np); - } -# endif - - /* - ** Copy arguments. - ** Copy from arg0 & if necessary at most one arg - ** from argv[0]. - */ - - np = ap = &nav[1]; - editchs = NULL; - for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) - { - *np++ = q; - while (*p == ' ') - p++; - while (*p != ' ' && *p != '\0' && *p != '/' && *p != ':') - *q++ = *p++; - *q++ = '\0'; - if (*p == ':') - { - editchs = q; - while (*++p != '\0' && *p != '/' && *p != ' ') - *q++ = *p; - *q++ = '\0'; - } - } - *np = NULL; - if (*ap == NULL) - *np++ = *argv++; - - /* - ** Look up command. - ** At this point, *ap is the command name. - */ - - cmd = lookup(*ap); - if (cmd == NULL) - { - usrerr("Unknown command \"%s\"", *ap); - return (EX_USAGE); - } - - /* - ** Copy remaining arguments doing editing as appropriate. - */ - - for (; *argv != NULL; argv++) - { - p = *argv; - if (*p == '-') - { - if (p[1] == '\0' || editchs == NULL || index(editchs, p[1]) != NULL) - *np++ = p; - } - else - { - if (!bitset(NO_SDOT, cmd->sccsflags)) - p = makefile(p); - if (p != NULL) - *np++ = p; - } - } - *np = NULL; - - /* - ** Interpret operation associated with this command. - */ - - switch (cmd->sccsoper) - { - case SHELL: /* call a shell file */ - *ap = cmd->sccspath; - *--ap = "sh"; - rval = callprog(_PATH_BSHELL, cmd->sccsflags, ap, forkflag); - break; - - case PROG: /* call an sccs prog */ - rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); - break; - - case CMACRO: /* command macro */ - /* step through & execute each part of the macro */ - for (p = cmd->sccspath; *p != '\0'; p++) - { - q = p; - while (*p != '\0' && *p != '/') - p++; - rval = command(&ap[1], *p != '\0', q); - if (rval != 0) - break; - } - break; - - case FIX: /* fix a delta */ - if (ap[1]==0 || strncmp(ap[1], "-r", 2)!=0) - { - usrerr("-r flag needed for fix command"); - rval = EX_USAGE; - break; - } - - /* get the version with all changes */ - rval = command(&ap[1], TRUE, "get -k"); - - /* now remove that version from the s-file */ - if (rval == 0) - rval = command(&ap[1], TRUE, "rmdel:r"); - - /* and edit the old version (but don't clobber new vers) */ - if (rval == 0) - rval = command(&ap[2], FALSE, "get -e -g"); - break; - - case CLEAN: - rval = clean((int) cmd->sccspath, ap); - break; - - case UNEDIT: - for (argv = np = &ap[1]; *argv != NULL; argv++) - { - if (unedit(*argv)) - *np++ = *argv; - } - *np = NULL; - - /* get all the files that we unedited successfully */ - if (np > &ap[1]) - rval = command(&ap[1], FALSE, "get"); - break; - - case DIFFS: /* diff between s-file & edit file */ - /* find the end of the flag arguments */ - for (np = &ap[1]; *np != NULL && **np == '-'; np++) - continue; - argv = np; - - /* for each file, do the diff */ - p = argv[1]; - while (*np != NULL) - { - /* messy, but we need a null terminated argv */ - *argv = *np++; - argv[1] = NULL; - i = dodiff(ap, tail(*argv)); - if (rval == 0) - rval = i; - argv[1] = p; - } - break; - - case DODIFF: /* internal diff call */ - setuid(getuid()); - for (np = ap; *np != NULL; np++) - { - if ((*np)[0] == '-' && (*np)[1] == 'C') - (*np)[1] = 'c'; - } - - /* insert "-" argument */ - np[1] = NULL; - np[0] = np[-1]; - np[-1] = "-"; - - /* execute the diff program of choice */ -# ifndef V6 - execvp("diff", ap); -# endif - execv(cmd->sccspath, argv); - syserr("cannot exec %s", cmd->sccspath); - exit(EX_OSERR); - - case ENTER: /* enter new sccs files */ - /* skip over flag arguments */ - for (np = &ap[1]; *np != NULL && **np == '-'; np++) - continue; - argv = np; - - /* do an admin for each file */ - p = argv[1]; - while (*np != NULL) - { - printf("\n%s:\n", *np); - strcpy(buf, "-i"); - gstrcat(buf, *np, sizeof(buf)); - ap[0] = buf; - argv[0] = tail(*np); - argv[1] = NULL; - rval = command(ap, TRUE, "admin"); - argv[1] = p; - if (rval == 0) - { - strcpy(buf, ","); - gstrcat(buf, tail(*np), sizeof(buf)); - if (link(*np, buf) >= 0) - unlink(*np); - } - np++; - } - break; - - default: - syserr("oper %d", cmd->sccsoper); - exit(EX_SOFTWARE); - } -# ifdef DEBUG - if (Debug) - printf("command: rval=%d\n", rval); -# endif - return (rval); -} - -/* -** LOOKUP -- look up an SCCS command name. -** -** Parameters: -** name -- the name of the command to look up. -** -** Returns: -** ptr to command descriptor for this command. -** NULL if no such entry. -** -** Side Effects: -** none. -*/ - -struct sccsprog * -lookup(name) - char *name; -{ - register struct sccsprog *cmd; - - for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) - { - if (strcmp(cmd->sccsname, name) == 0) - return (cmd); - } - return (NULL); -} - -/* -** CALLPROG -- call a program -** -** Used to call the SCCS programs. -** -** Parameters: -** progpath -- pathname of the program to call. -** flags -- status flags from the command descriptors. -** argv -- an argument vector to pass to the program. -** forkflag -- if true, fork before calling, else just -** exec. -** -** Returns: -** The exit status of the program. -** Nothing if forkflag == FALSE. -** -** Side Effects: -** Can exit if forkflag == FALSE. -*/ - -callprog(progpath, flags, argv, forkflag) - char *progpath; - short flags; - char **argv; - bool forkflag; -{ - register int i; - register int wpid; - auto int st; - register int sigcode; - register int coredumped; - register char *sigmsg; - auto char sigmsgbuf[10+1]; /* "Signal 127" + terminating '\0' */ - -# ifdef DEBUG - if (Debug) - { - printf("callprog:\n"); - for (i = 0; argv[i] != NULL; i++) - printf("\t\"%s\"\n", argv[i]); - } -# endif - - if (*argv == NULL) - return (-1); - - /* - ** Fork if appropriate. - */ - - if (forkflag) - { -# ifdef DEBUG - if (Debug) - printf("Forking\n"); -# endif - i = fork(); - if (i < 0) - { - syserr("cannot fork"); - exit(EX_OSERR); - } - else if (i > 0) - { - while ((wpid = wait(&st)) != -1 && wpid != i) - ; - if ((sigcode = st & 0377) == 0) - st = (st >> 8) & 0377; - else - { - coredumped = sigcode & 0200; - sigcode &= 0177; - if (sigcode != SIGINT && sigcode != SIGPIPE) - { - if (sigcode < NSIG) - sigmsg = sys_siglist[sigcode]; - else - { - sprintf(sigmsgbuf, "Signal %d", - sigcode); - sigmsg = sigmsgbuf; - } - fprintf(stderr, "sccs: %s: %s%s", argv[0], - sigmsg, - coredumped ? " - core dumped": ""); - } - st = EX_SOFTWARE; - } - if (OutFile >= 0) - { - close(OutFile); - OutFile = -1; - } - return (st); - } - } - else if (OutFile >= 0) - { - syserr("callprog: setting stdout w/o forking"); - exit(EX_SOFTWARE); - } - - /* set protection as appropriate */ - if (bitset(REALUSER, flags)) - setuid(getuid()); - - /* change standard input & output if needed */ - if (OutFile >= 0) - { - close(1); - dup(OutFile); - close(OutFile); - } - - /* call real SCCS program */ - execv(progpath, argv); - syserr("cannot execute %s", progpath); - exit(EX_UNAVAILABLE); - /*NOTREACHED*/ -} - -/* -** MAKEFILE -- make filename of SCCS file -** -** If the name passed is already the name of an SCCS file, -** just return it. Otherwise, munge the name into the name -** of the actual SCCS file. -** -** There are cases when it is not clear what you want to -** do. For example, if SccsPath is an absolute pathname -** and the name given is also an absolute pathname, we go -** for SccsPath (& only use the last component of the name -** passed) -- this is important for security reasons (if -** sccs is being used as a setuid front end), but not -** particularly intuitive. -** -** Parameters: -** name -- the file name to be munged. -** -** Returns: -** The pathname of the sccs file. -** NULL on error. -** -** Side Effects: -** none. -*/ - -char * -makefile(name) - char *name; -{ - register char *p; - char buf[3*FBUFSIZ]; - extern char *malloc(); - extern char *rindex(); - extern bool safepath(); - extern bool isdir(); - register char *q; - - p = rindex(name, '/'); - if (p == NULL) - p = name; - else - p++; - - /* - ** Check to see that the path is "safe", i.e., that we - ** are not letting some nasty person use the setuid part - ** of this program to look at or munge some presumably - ** hidden files. - */ - - if (SccsDir[0] == '/' && !safepath(name)) - return (NULL); - - /* - ** Create the base pathname. - */ - - /* first the directory part */ - if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) - { - gstrcpy(buf, SccsDir, sizeof(buf)); - gstrcat(buf, "/", sizeof(buf)); - } - else - gstrcpy(buf, "", sizeof(buf)); - - /* then the head of the pathname */ - gstrncat(buf, name, p - name, sizeof(buf)); - q = &buf[strlen(buf)]; - - /* now copy the final part of the name, in case useful */ - gstrcpy(q, p, sizeof(buf)); - - /* so is it useful? */ - if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) - { - /* sorry, no; copy the SCCS pathname & the "s." */ - gstrcpy(q, SccsPath, sizeof(buf)); - gstrcat(buf, "/s.", sizeof(buf)); - - /* and now the end of the name */ - gstrcat(buf, p, sizeof(buf)); - } - - /* if i haven't changed it, why did I do all this? */ - if (strcmp(buf, name) == 0) - p = name; - else - { - /* but if I have, squirrel it away */ - p = malloc(strlen(buf) + 1); - if (p == NULL) - { - perror("Sccs: no mem"); - exit(EX_OSERR); - } - strcpy(p, buf); - } - - return (p); -} - -/* -** ISDIR -- return true if the argument is a directory. -** -** Parameters: -** name -- the pathname of the file to check. -** -** Returns: -** TRUE if 'name' is a directory, FALSE otherwise. -** -** Side Effects: -** none. -*/ - -bool -isdir(name) - char *name; -{ - struct stat stbuf; - - return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); -} - -/* -** SAFEPATH -- determine whether a pathname is "safe" -** -** "Safe" pathnames only allow you to get deeper into the -** directory structure, i.e., full pathnames and ".." are -** not allowed. -** -** Parameters: -** p -- the name to check. -** -** Returns: -** TRUE -- if the path is safe. -** FALSE -- if the path is not safe. -** -** Side Effects: -** Prints a message if the path is not safe. -*/ - -bool -safepath(p) - register char *p; -{ - extern char *index(); - - if (*p != '/') - { - while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) - { - p = index(p, '/'); - if (p == NULL) - return (TRUE); - p++; - } - } - - printf("You may not use full pathnames or \"..\"\n"); - return (FALSE); -} - -/* -** CLEAN -- clean out recreatable files -** -** Any file for which an "s." file exists but no "p." file -** exists in the current directory is purged. -** -** Parameters: -** mode -- tells whether this came from a "clean", "info", or -** "check" command. -** argv -- the rest of the argument vector. -** -** Returns: -** none. -** -** Side Effects: -** Removes files in the current directory. -** Prints information regarding files being edited. -** Exits if a "check" command. -*/ - -clean(mode, argv) - int mode; - char **argv; -{ - struct direct *dir; - char buf[FBUFSIZ]; - char *bufend; - register DIR *dirp; - register char *basefile; - bool gotedit; - bool gotpfent; - FILE *pfp; - bool nobranch = FALSE; - extern struct pfile *getpfent(); - register struct pfile *pf; - register char **ap; - extern char *username(); - char *usernm = NULL; - char *subdir = NULL; - char *cmdname; - - /* - ** Process the argv - */ - - cmdname = *argv; - for (ap = argv; *++ap != NULL; ) - { - if (**ap == '-') - { - /* we have a flag */ - switch ((*ap)[1]) - { - case 'b': - nobranch = TRUE; - break; - - case 'u': - if ((*ap)[2] != '\0') - usernm = &(*ap)[2]; - else if (ap[1] != NULL && ap[1][0] != '-') - usernm = *++ap; - else - usernm = username(); - break; - } - } - else - { - if (subdir != NULL) - usrerr("too many args"); - else - subdir = *ap; - } - } - - /* - ** Find and open the SCCS directory. - */ - - gstrcpy(buf, SccsDir, sizeof(buf)); - if (buf[0] != '\0') - gstrcat(buf, "/", sizeof(buf)); - if (subdir != NULL) - { - gstrcat(buf, subdir, sizeof(buf)); - gstrcat(buf, "/", sizeof(buf)); - } - gstrcat(buf, SccsPath, sizeof(buf)); - bufend = &buf[strlen(buf)]; - - dirp = opendir(buf); - if (dirp == NULL) - { - usrerr("cannot open %s", buf); - return (EX_NOINPUT); - } - - /* - ** Scan the SCCS directory looking for s. files. - ** gotedit tells whether we have tried to clean any - ** files that are being edited. - */ - - gotedit = FALSE; - while (dir = readdir(dirp)) { - if (strncmp(dir->d_name, "s.", 2) != 0) - continue; - - /* got an s. file -- see if the p. file exists */ - gstrcpy(bufend, "/p.", sizeof(buf)); - basefile = bufend + 3; - gstrcpy(basefile, &dir->d_name[2], sizeof(buf)); - - /* - ** open and scan the p-file. - ** 'gotpfent' tells if we have found a valid p-file - ** entry. - */ - - pfp = fopen(buf, "r"); - gotpfent = FALSE; - if (pfp != NULL) - { - /* the file exists -- report it's contents */ - while ((pf = getpfent(pfp)) != NULL) - { - if (nobranch && isbranch(pf->p_nsid)) - continue; - if (usernm != NULL && strcmp(usernm, pf->p_user) != 0 && mode != CLEANC) - continue; - gotedit = TRUE; - gotpfent = TRUE; - if (mode == TELLC) - { - printf("%s\n", basefile); - break; - } - printf("%12s: being edited: ", basefile); - putpfent(pf, stdout); - } - fclose(pfp); - } - - /* the s. file exists and no p. file exists -- unlink the g-file */ - if (mode == CLEANC && !gotpfent) - { - char unlinkbuf[FBUFSIZ]; - gstrcpy(unlinkbuf, &dir->d_name[2], sizeof(unlinkbuf)); - unlink(unlinkbuf); - } - } - - /* cleanup & report results */ - closedir(dirp); - if (!gotedit && mode == INFOC) - { - printf("Nothing being edited"); - if (nobranch) - printf(" (on trunk)"); - if (usernm == NULL) - printf("\n"); - else - printf(" by %s\n", usernm); - } - if (mode == CHECKC) - exit(gotedit); - return (EX_OK); -} - -/* -** ISBRANCH -- is the SID a branch? -** -** Parameters: -** sid -- the sid to check. -** -** Returns: -** TRUE if the sid represents a branch. -** FALSE otherwise. -** -** Side Effects: -** none. -*/ - -isbranch(sid) - char *sid; -{ - register char *p; - int dots; - - dots = 0; - for (p = sid; *p != '\0'; p++) - { - if (*p == '.') - dots++; - if (dots > 1) - return (TRUE); - } - return (FALSE); -} - -/* -** UNEDIT -- unedit a file -** -** Checks to see that the current user is actually editting -** the file and arranges that s/he is not editting it. -** -** Parameters: -** fn -- the name of the file to be unedited. -** -** Returns: -** TRUE -- if the file was successfully unedited. -** FALSE -- if the file was not unedited for some -** reason. -** -** Side Effects: -** fn is removed -** entries are removed from pfile. -*/ - -bool -unedit(fn) - char *fn; -{ - register FILE *pfp; - char *cp, *pfn; - static char tfn[] = _PATH_TMP; - FILE *tfp; - register char *q; - bool delete = FALSE; - bool others = FALSE; - char *myname; - extern char *username(); - struct pfile *pent; - extern struct pfile *getpfent(); - char buf[PFILELG]; - extern char *makefile(), *rindex(), *tail(); - - /* make "s." filename & find the trailing component */ - pfn = makefile(fn); - if (pfn == NULL) - return (FALSE); - q = rindex(pfn, '/'); - if (q == NULL) - q = &pfn[-1]; - if (q[1] != 's' || q[2] != '.') - { - usrerr("bad file name \"%s\"", fn); - return (FALSE); - } - - /* turn "s." into "p." & try to open it */ - *++q = 'p'; - - pfp = fopen(pfn, "r"); - if (pfp == NULL) - { - printf("%12s: not being edited\n", fn); - return (FALSE); - } - - /* create temp file for editing p-file */ - mktemp(tfn); - tfp = fopen(tfn, "w"); - if (tfp == NULL) - { - usrerr("cannot create \"%s\"", tfn); - exit(EX_OSERR); - } - - /* figure out who I am */ - myname = username(); - - /* - ** Copy p-file to temp file, doing deletions as needed. - */ - - while ((pent = getpfent(pfp)) != NULL) - { - if (strcmp(pent->p_user, myname) == 0) - { - /* a match */ - delete++; - } - else - { - /* output it again */ - putpfent(pent, tfp); - others++; - } - } - - /* - * Before changing anything, make sure we can remove - * the file in question (assuming it exists). - */ - if (delete) { - extern int errno; - - cp = tail(fn); - errno = 0; - if (access(cp, 0) < 0 && errno != ENOENT) - goto bad; - if (errno == 0) - /* - * This is wrong, but the rest of the program - * has built in assumptions about "." as well, - * so why make unedit a special case? - */ - if (access(".", 2) < 0) { - bad: - printf("%12s: can't remove\n", cp); - fclose(tfp); - fclose(pfp); - unlink(tfn); - return (FALSE); - } - } - /* do final cleanup */ - if (others) - { - /* copy it back (perhaps it should be linked?) */ - if (freopen(tfn, "r", tfp) == NULL) - { - syserr("cannot reopen \"%s\"", tfn); - exit(EX_OSERR); - } - if (freopen(pfn, "w", pfp) == NULL) - { - usrerr("cannot create \"%s\"", pfn); - return (FALSE); - } - while (fgets(buf, sizeof buf, tfp) != NULL) - fputs(buf, pfp); - } - else - { - /* it's empty -- remove it */ - unlink(pfn); - } - fclose(tfp); - fclose(pfp); - unlink(tfn); - - /* actually remove the g-file */ - if (delete) - { - /* - * Since we've checked above, we can - * use the return from unlink to - * determine if the file existed or not. - */ - if (unlink(cp) >= 0) - printf("%12s: removed\n", cp); - return (TRUE); - } - else - { - printf("%12s: not being edited by you\n", fn); - return (FALSE); - } -} - -/* -** DODIFF -- diff an s-file against a g-file -** -** Parameters: -** getv -- argv for the 'get' command. -** gfile -- name of the g-file to diff against. -** -** Returns: -** Result of get. -** -** Side Effects: -** none. -*/ - -dodiff(getv, gfile) - char **getv; - char *gfile; -{ - int pipev[2]; - int rval; - register int i; - register int pid; - auto int st; - extern int errno; - sig_t osig; - - printf("\n------- %s -------\n", gfile); - fflush(stdout); - - /* create context for diff to run in */ - if (pipe(pipev) < 0) - { - syserr("dodiff: pipe failed"); - exit(EX_OSERR); - } - if ((pid = fork()) < 0) - { - syserr("dodiff: fork failed"); - exit(EX_OSERR); - } - else if (pid > 0) - { - /* in parent; run get */ - OutFile = pipev[1]; - close(pipev[0]); - rval = command(&getv[1], TRUE, "get:rcixt -s -k -p"); - osig = signal(SIGINT, SIG_IGN); - while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) - errno = 0; - signal(SIGINT, osig); - /* ignore result of diff */ - } - else - { - /* in child, run diff */ - if (close(pipev[1]) < 0 || close(0) < 0 || - dup(pipev[0]) != 0 || close(pipev[0]) < 0) - { - syserr("dodiff: magic failed"); - exit(EX_OSERR); - } - command(&getv[1], FALSE, "-diff:elsfhbC"); - } - return (rval); -} - -/* -** TAIL -- return tail of filename. -** -** Parameters: -** fn -- the filename. -** -** Returns: -** a pointer to the tail of the filename; e.g., given -** "cmd/ls.c", "ls.c" is returned. -** -** Side Effects: -** none. -*/ - -char * -tail(fn) - register char *fn; -{ - register char *p; - - for (p = fn; *p != 0; p++) - if (*p == '/' && p[1] != '\0' && p[1] != '/') - fn = &p[1]; - return (fn); -} - -/* -** GETPFENT -- get an entry from the p-file -** -** Parameters: -** pfp -- p-file file pointer -** -** Returns: -** pointer to p-file struct for next entry -** NULL on EOF or error -** -** Side Effects: -** Each call wipes out results of previous call. -*/ - -struct pfile * -getpfent(pfp) - FILE *pfp; -{ - static struct pfile ent; - static char buf[PFILELG]; - register char *p; - extern char *nextfield(); - - if (fgets(buf, sizeof buf, pfp) == NULL) - return (NULL); - - ent.p_osid = p = buf; - ent.p_nsid = p = nextfield(p); - ent.p_user = p = nextfield(p); - ent.p_date = p = nextfield(p); - ent.p_time = p = nextfield(p); - ent.p_aux = p = nextfield(p); - - return (&ent); -} - - -char * -nextfield(p) - register char *p; -{ - if (p == NULL || *p == '\0') - return (NULL); - while (*p != ' ' && *p != '\n' && *p != '\0') - p++; - if (*p == '\n' || *p == '\0') - { - *p = '\0'; - return (NULL); - } - *p++ = '\0'; - return (p); -} - /* -** PUTPFENT -- output a p-file entry to a file -** -** Parameters: -** pf -- the p-file entry -** f -- the file to put it on. -** -** Returns: -** none. -** -** Side Effects: -** pf is written onto file f. -*/ - -putpfent(pf, f) - register struct pfile *pf; - register FILE *f; -{ - fprintf(f, "%s %s %s %s %s", pf->p_osid, pf->p_nsid, - pf->p_user, pf->p_date, pf->p_time); - if (pf->p_aux != NULL) - fprintf(f, " %s", pf->p_aux); - else - fprintf(f, "\n"); -} - -/* -** USRERR -- issue user-level error -** -** Parameters: -** f -- format string. -** p1-p3 -- parameters to a printf. -** -** Returns: -** -1 -** -** Side Effects: -** none. -*/ - -/*VARARGS1*/ -usrerr(f, p1, p2, p3) - char *f; -{ - fprintf(stderr, "\n%s: ", MyName); - fprintf(stderr, f, p1, p2, p3); - fprintf(stderr, "\n"); - - return (-1); -} - -/* -** SYSERR -- print system-generated error. -** -** Parameters: -** f -- format string to a printf. -** p1, p2, p3 -- parameters to f. -** -** Returns: -** never. -** -** Side Effects: -** none. -*/ - -/*VARARGS1*/ -syserr(f, p1, p2, p3) - char *f; -{ - extern int errno; - - fprintf(stderr, "\n%s SYSERR: ", MyName); - fprintf(stderr, f, p1, p2, p3); - fprintf(stderr, "\n"); - if (errno == 0) - exit(EX_SOFTWARE); - else - { - perror(NULL); - exit(EX_OSERR); - } -} - /* -** USERNAME -- return name of the current user -** -** Parameters: -** none -** -** Returns: -** name of current user -** -** Side Effects: -** none -*/ - -char * -username() -{ -# ifdef UIDUSER - extern struct passwd *getpwuid(); - register struct passwd *pw; - - pw = getpwuid(getuid()); - if (pw == NULL) - { - syserr("who are you? (uid=%d)", getuid()); - exit(EX_OSERR); - } - return (pw->pw_name); -# else - extern char *getlogin(); - register char *p; - - p = getenv("USER"); - if (p == NULL || p[0] == '\0') - p = getlogin(); - return (p); -# endif UIDUSER -} - -/* -** Guarded string manipulation routines; the last argument -** is the length of the buffer into which the strcpy or strcat -** is to be done. -*/ -char *gstrcat(to, from, length) - char *to, *from; - int length; -{ - if (strlen(from) + strlen(to) >= length) { - gstrbotch(to, from); - } - return(strcat(to, from)); -} - -char *gstrncat(to, from, n, length) - char *to, *from; - int n; - int length; -{ - if (n + strlen(to) >= length) { - gstrbotch(to, from); - } - return(strncat(to, from, n)); -} - -char *gstrcpy(to, from, length) - char *to, *from; - int length; -{ - if (strlen(from) >= length) { - gstrbotch(from, (char *)0); - } - return(strcpy(to, from)); -} -gstrbotch(str1, str2) - char *str1, *str2; -{ - usrerr("Filename(s) too long: %s %s", str1, str2); -} diff --git a/usr.bin/sed/COPYING b/usr.bin/sed/COPYING deleted file mode 100644 index 9a17037581..0000000000 --- a/usr.bin/sed/COPYING +++ /dev/null @@ -1,249 +0,0 @@ - - GNU GENERAL PUBLIC LICENSE - Version 1, February 1989 - - Copyright (C) 1989 Free Software Foundation, Inc. - 675 Mass Ave, Cambridge, MA 02139, USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The license agreements of most software companies try to keep users -at the mercy of those companies. By contrast, our General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. The -General Public License applies to the Free Software Foundation's -software and to any other program whose authors commit to using it. -You can use it for your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Specifically, the General Public License is designed to make -sure that you have the freedom to give away or sell copies of free -software, that you receive source code or can get it if you want it, -that you can change the software or use pieces of it in new free -programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of a such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must tell them their rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any program or other work which -contains a notice placed by the copyright holder saying it may be -distributed under the terms of this General Public License. The -"Program", below, refers to any such program or work, and a "work based -on the Program" means either the Program or any work containing the -Program or a portion of it, either verbatim or with modifications. Each -licensee is addressed as "you". - - 1. You may copy and distribute verbatim copies of the Program's source -code as you receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice and -disclaimer of warranty; keep intact all the notices that refer to this -General Public License and to the absence of any warranty; and give any -other recipients of the Program a copy of this General Public License -along with the Program. You may charge a fee for the physical act of -transferring a copy. - - 2. You may modify your copy or copies of the Program or any portion of -it, and copy and distribute such modifications under the terms of Paragraph -1 above, provided that you also do the following: - - a) cause the modified files to carry prominent notices stating that - you changed the files and the date of any change; and - - b) cause the whole of any work that you distribute or publish, that - in whole or in part contains the Program or any part thereof, either - with or without modifications, to be licensed at no charge to all - third parties under the terms of this General Public License (except - that you may choose to grant warranty protection to some or all - third parties, at your option). - - c) If the modified program normally reads commands interactively when - run, you must cause it, when started running for such interactive use - in the simplest and most usual way, to print or display an - announcement including an appropriate copyright notice and a notice - that there is no warranty (or else, saying that you provide a - warranty) and that users may redistribute the program under these - conditions, and telling the user how to view a copy of this General - Public License. - - d) You may charge a fee for the physical act of transferring a - copy, and you may at your option offer warranty protection in - exchange for a fee. - -Mere aggregation of another independent work with the Program (or its -derivative) on a volume of a storage or distribution medium does not bring -the other work under the scope of these terms. - - 3. You may copy and distribute the Program (or a portion or derivative of -it, under Paragraph 2) in object code or executable form under the terms of -Paragraphs 1 and 2 above provided that you also do one of the following: - - a) accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of - Paragraphs 1 and 2 above; or, - - b) accompany it with a written offer, valid for at least three - years, to give any third party free (except for a nominal charge - for the cost of distribution) a complete machine-readable copy of the - corresponding source code, to be distributed under the terms of - Paragraphs 1 and 2 above; or, - - c) accompany it with the information you received as to where the - corresponding source code may be obtained. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form alone.) - -Source code for a work means the preferred form of the work for making -modifications to it. For an executable file, complete source code means -all the source code for all modules it contains; but, as a special -exception, it need not include source code for modules which are standard -libraries that accompany the operating system on which the executable -file runs, or for standard header files or definitions files that -accompany that operating system. - - 4. You may not copy, modify, sublicense, distribute or transfer the -Program except as expressly provided under this General Public License. -Any attempt otherwise to copy, modify, sublicense, distribute or transfer -the Program is void, and will automatically terminate your rights to use -the Program under this License. However, parties who have received -copies, or rights to use copies, from you under this General Public -License will not have their licenses terminated so long as such parties -remain in full compliance. - - 5. By copying, distributing or modifying the Program (or any work based -on the Program) you indicate your acceptance of this license to do so, -and all its terms and conditions. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the original -licensor to copy, distribute or modify the Program subject to these -terms and conditions. You may not impose any further restrictions on the -recipients' exercise of the rights granted herein. - - 7. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of the license which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -the license, you may choose any version ever published by the Free Software -Foundation. - - 8. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - Appendix: How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to humanity, the best way to achieve this is to make it -free software which everyone can redistribute and change under these -terms. - - To do so, attach the following notices to the program. It is safest to -attach them to the start of each source file to most effectively convey -the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - <one line to give the program's name and a brief idea of what it does.> - Copyright (C) 19yy <name of author> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19xx name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the -appropriate parts of the General Public License. Of course, the -commands you use may be called something other than `show w' and `show -c'; they could even be mouse-clicks or menu items--whatever suits your -program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - program `Gnomovision' (a program to direct compilers to make passes - at assemblers) written by James Hacker. - - <signature of Ty Coon>, 1 April 1989 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/usr.bin/sed/ChangeLog b/usr.bin/sed/ChangeLog deleted file mode 100644 index 289fb4e570..0000000000 --- a/usr.bin/sed/ChangeLog +++ /dev/null @@ -1,194 +0,0 @@ -Thu Aug 8 00:15:33 1991 David J. MacKenzie (djm at bleen) - - * Version 1.08. - - * sed.c (compile_filename): If reading a file fails, read - /dev/null instead. It's what Unix and POSIX do, effectively. - - * sed.c (compile_regex): The 'slash' character doesn't - terminate the regex if it's in a character class. - - * sed.c (main): If given no args, or bad option, print usage - message. - (usage): New function. - - * sed.c (execute_program): Amount written for 'P' command was - wrong. From stephend@ksr.com (Stephen Davis). - -Wed Aug 7 16:51:14 1991 David J. MacKenzie (djm at apple-gunkies) - - * sed.c (append_pattern_space): Check for buffer full before - instead of after writing to buffer. Don't need to test for - EOF initially anymore, due to the next change. - (execute_program): For 'n' and 'N' commands, if eof is reached - in input, quit the script like Unix sed does. - Fix memory allocation problems for 'a' and 'r' commands. - (compile_program): Fix off by one error in processing comments. - All of the above are from Tapani Tarvainen, tarvaine@tukki.jyu.fi. - - * sed.c (setup_jump): Use isblank instead of testing for ' ' - or '\t', for POSIX locales. - - * utils.c (ck_strdup): Renamed from strdup. - * sed.c: Change callers. - - * sed.c, utils.c: Clean up declarations and includes to get - rid of compiler warnings. - - * sed.c (main): Add long-named options. Don't complain if -n - is given twice. - -Fri Aug 2 12:33:16 1991 David J. MacKenzie (djm at apple-gunkies) - - * configure: Support +srcdir arg. Create config.status and - remove it and Makefile if interrupted while creating them. - * Makefile.in: Change DESTDIR to prefix. - -Mon Jul 15 13:07:39 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) - - * sed.c (main): Add -V option to print version number. - (USAGE): Mention -V. - -Mon Jul 8 01:42:22 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu) - - * sed.c: Define bcopy in terms of memcpy if STDC_HEADERS as - well as if USG. - (compile_filename): Don't glob filename (for 'r' and 'w' - commands). Unix sed doesn't do it and it's not very useful, - since it can only match 0 or 1 files. - (execute_program): Change '\a' to 007 since some compilers - don't recognize \a. - * utils.c: New file; code moved from sed.c. - * Replace Makefile with Makefile.in and configure. - Update README. - -Tue Mar 26 13:00:48 EST 1991 Jay Fenlason (hack@gnu.ai.mit.edu) - - * sed.c (match_address) Added a trivial cast for portability. - -Mon Feb 25 13:23:29 EST 1991 Jay Fenlason (hack@ai.mit.edu) - - * sed.c Changed 's' command to work with latest version of regex() - routines, which mysteriously changed somewhere in there. . . - A one-line patch from David Eckelkamp (eckelkamp@mcc.com). - - Initialize the fastmap in the hopes that it'll make sed faster. - -Thu Feb 21 13:42:27 EST 1991 Jay Fenlason (hack@ai.mti.edu) - - * sed.c Change panic to compile with other __STDC__ compilers. - -Wed Jan 30 10:46:38 EST 1991 Jay Fenlason (hack@ai.mit.edu) - - * sed.c Changed version number. Made new release. - -Tue Nov 27 15:34:51 EST 1990 Jay Fenlason (hack@ai.mit.edu) - - * sed.c (setup_jump) Don't blow chunks if there isn't a label - after a b or t command. - - (main) Don't panic if it a branch command doesn't have - a label to branch to. - - (main) Collect all the -e arguments together and parse them - all at once. This way, -e { -e mumble -e } will work. - - All these small patches from David Schmidt (davids@isc-br.isc-br.com) - -Tue Sep 11 12:51:37 EDT 1990 Jay Fenlason (hack@ai.mit.edu) - - * sed.c Changed some function forward declarations to use VOID * - instead of char * - -Mon Jul 16 11:12:54 EDT 1990 Jay Fenlason (hack@ai.mit.edu) - - * sed.c (ck_malloc) Use malloc(1) instead of malloc(0) if given - a request for zero bytes. - -Tue Jun 5 02:05:37 1990 David J. MacKenzie (djm at albert.ai.mit.edu) - - * sed.c: Remove excess newlines from calls to panic. - Reformat some comments to fit in 79 columns. - Base whether to use void * on __STDC__, not __GNU__. - (main): Add missing arg when printing usage message. - Print usage if given invalid arg. - (panic) [__STDC__]: Add missing ", ...". - (compile_filename): Print correct error message if glob_filename - returns NULL. - -Thu Apr 5 21:41:12 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) - - * sed.c (execute_program, case 'r'): When need to realloc append.text, - multiply append.alloc by 2 instead of adding - cur_cmd->x.cmd_txt.text_len. - -Tue Mar 6 15:55:35 EST 1990 Jay Fenlason (hack@ai.mit.edu) - - * sed.c (compile_regex) Allocate 10 bytes extra space needed by - re_compile_pattern. - -Sun Feb 25 16:32:10 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) - - * sed.c (execute_program, case 'l'): Print \00 instead of \0. - Print backslash as \\ not \. - Print \xx instead of /xx. - -Thu Feb 1 14:02:28 EST 1990 hack@wookumz - - * sed.c (memchr) Use () inside inner loop so it will work correctly. - A two character patch from Robert A Bruce (rab@allspice.berkeley.edu) - -Wed Sep 27 18:47:39 EDT 1989 hack@ai.mit.edu - - * sed.c (compile_regex) New function. When compiling regex, - turn ^ into \` and $ into \' so that they won't match on embedded - newlines. UN*X pattern matching is a crock. - (compile_program, compile_address) call compile_regex. - -Mon Sep 18 10:15:32 EDT 1989 hack@ai.mit.edu - - * sed.c (compile_program): define translate as unsigned char * so - that y command will work on non-ascii characters. - - Changed version number to 1.06. - -Thu Sep 14 15:57:08 EDT 1989 hack@ai.mit.edu - - * sed.c (compile_program) Let programs use ; to terminate } as - well as newline. - - (read_file) Print an error msg to stderr if it can't open an - input file. - -Thu Mar 23 18:04:46 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) - - * Makefile, sed.c: Added new copyright notice. - - * Makefile: Make distributions which follow the symlinks. - -hack@ai.mit.edu - - 1.05 Fixed error in 'r' (now does things in the right order) - - 1.04 Fixed s/re/rep/[number] - - 1.03 Fixes from Mike Haertel for regexps that match the - empty string, and for Ritchie stdio (non-sticky EOF) - - 1.02 Fixed 't', 'b', ':' to trim leading spaces and tabs - Fixed \\ in replacement of 's' command - Added comments - - 1.01 Added s/re/rep/[digits] - added #n as first line of script - added filename globbing - added 'l' command - All in the name of POSIX - - 1.00 Began (thinking about) distributing this file - -Local Variables: -mode: indented-text -left-margin: 8 -version-control: never -End: diff --git a/usr.bin/sed/Makefile b/usr.bin/sed/Makefile index 5b116636bd..1046ad3ea2 100644 --- a/usr.bin/sed/Makefile +++ b/usr.bin/sed/Makefile @@ -1,10 +1,9 @@ +# @(#)Makefile 5.1 (Berkeley) 8/24/92 PROG= sed -SRCS= sed.c utils.c regex.c getopt.c getopt1.c -CFLAGS+=-I${.CURDIR} # -DSTDC_HEADERS -NOMAN=noman - -sed.o regex.o: regex.h -sed.o getopt1.o: getopt.h +SRCS= compile.c main.c misc.c process.c +CFLAGS+=-I${.CURDIR} -DHISTORIC_PRACTICE +LDADD+= -lgnuregex +DPADD+= /usr/lib/libgnuregex.a .include <bsd.prog.mk> diff --git a/usr.bin/sed/Makefile.gnu b/usr.bin/sed/Makefile.gnu deleted file mode 100644 index b4ad89dcce..0000000000 --- a/usr.bin/sed/Makefile.gnu +++ /dev/null @@ -1,84 +0,0 @@ -# Generated automatically from Makefile.in by configure. -# Makefile for GNU SED, a batch editor. -# Copyright (C) 1987-1991 Free Software Foundation, Inc. -# -# This file is part of GNU SED. -# -# GNU SED is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# GNU SED is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with GNU SED; see the file COPYING. If not, write to -# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - -SHELL = /bin/sh - -#### Start of system configuration section. #### - -srcdir = . - - -CC = gcc -O -INSTALL = install -c - -# Things you might add to DEFS: -# -DSTDC_HEADERS If you have ANSI C headers and libraries. -# -DUSG If you have System V/ANSI C string -# and memory functions and headers. -# -DCHAR_UNSIGNED If type `char' is unsigned. -# -DNO_VFPRINTF If you lack vprintf function (but have _doprnt). - -DEFS = -LIBS = - -CFLAGS = -I$(srcdir) $(DEFS) -LDFLAGS = - -prefix = /usr/local - -# Where to install the executable. -bindir = $(prefix)/gnubin - -#### End of system configuration section. #### - -OBJS = sed.o utils.o regex.o getopt.o getopt1.o -SRCS = sed.c utils.c regex.c getopt.c getopt1.c -DISTFILES = COPYING ChangeLog README Makefile.in configure \ -regex.h getopt.h $(SRCS) - -all: sed - -sed: $(OBJS) - $(CC) -o $@ $(LDFLAGS) $(OBJS) $(LIBS) - -sed.o regex.o: regex.h -sed.o getopt1.o: getopt.h - -install: all - $(INSTALL) sed $(bindir) - -TAGS: $(SRCS) - etags $(SRCS) - -clean: - rm -f sed $(OBJS) core - -distclean: clean - rm -f TAGS Makefile config.status - -realclean: distclean - -dist: $(DISTFILES) - echo sed-`sed -e '/version_string/!d' -e 's/[^0-9.]*\([0-9.]*\).*/\1/' -e q sed.c` > .fname - rm -rf `cat .fname` - mkdir `cat .fname` - ln $(DISTFILES) `cat .fname` - tar chZf `cat .fname`.tar.Z `cat .fname` - rm -rf `cat .fname` .fname diff --git a/usr.bin/sed/POSIX b/usr.bin/sed/POSIX new file mode 100644 index 0000000000..467b31db3e --- /dev/null +++ b/usr.bin/sed/POSIX @@ -0,0 +1,205 @@ +# @(#)POSIX 5.9 (Berkeley) 8/28/92 + +Comments on the IEEE P1003.2 Draft 12 + Part 2: Shell and Utilities + Section 4.55: sed - Stream editor + +Diomidis Spinellis <dds@doc.ic.ac.uk> +Keith Bostic <bostic@cs.berkeley.edu> + +In the following paragraphs, "wrong" usually means "inconsistent with +historic practice", as most of the following comments refer to +undocumented inconsistencies between the historical versions of sed and +the POSIX 1003.2 standard. All the comments are notes taken while +implementing a POSIX-compatible version of sed, and should not be +interpreted as official opinions or criticism towards the POSIX committee. +All uses of "POSIX" refer to section 4.55, Draft 12 of POSIX 1003.2. + + 1. 32V and BSD derived implementations of sed strip the text + arguments of the a, c and i commands of their initial blanks, + i.e. + + #!/bin/sed -f + a\ + foo\ + \ indent\ + bar + + produces: + + foo + indent + bar + + POSIX does not specify this behavior as the System V versions of + sed do not do this stripping. The argument against stripping is + that it is difficult to write sed scripts that have leading blanks + if they are stripped. The argument for stripping is that it is + difficult to write readable sed scripts unless indentation is allowed + and ignored, and leading whitespace is obtainable by entering a + backslash in front of it. This implementation follows the BSD + historic practice. + + 2. Historical versions of sed required that the w flag be the last + flag to an s command as it takes an additional argument. This + is obvious, but not specified in POSIX. + + 3. Historical versions of sed required that whitespace follow a w + flag to an s command. This is not specified in POSIX. This + implementation permits whitespace but does not require it. + + 4. Historical versions of sed permitted any number of whitespace + characters to follow the w command. This is not specified in + POSIX. This implementation permits whitespace but does not + require it. + + 5. The rule for the l command differs from historic practice. Table + 2-15 includes the various ANSI C escape sequences, including \\ + for backslash. Some historical versions of sed displayed two + digit octal numbers, too, not three as specified by POSIX. POSIX + is a cleanup, and is followed by this implementation. + + 6. The POSIX specification for ! does not specify that for a single + command the command must not contain an address specification + whereas the command list can contain address specifications. The + specification for ! implies that "3!/hello/p" works, and it never + has, historically. Note, + + 3!{ + /hello/p + } + + does work. + + 7. POSIX does not specify what happens with consecutive ! commands + (e.g. /foo/!!!p). Historic implementations allow any number of + !'s without changing the behaviour. (It seems logical that each + one might reverse the behaviour.) This implementation follows + historic practice. + + 8. Historic versions of sed permitted commands to be separated + by semi-colons, e.g. 'sed -ne '1p;2p;3q' printed the first + three lines of a file. This is not specified by POSIX. + Note, the ; command separator is not allowed for the commands + a, c, i, w, r, :, b, t, # and at the end of a w flag in the s + command. This implementation follows historic practice and + implements the ; separator. + + 9. Historic versions of sed terminated the script if EOF was reached + during the execution of the 'n' command, i.e.: + + sed -e ' + n + i\ + hello + ' </dev/null + + did not produce any output. POSIX does not specify this behavior. + This implementation follows historic practice. + +10. POSIX does not specify that the q command causes all lines that + have been appended to be output and that the pattern space is + printed before exiting. This implementation follows historic + practice. + +11. Historical implementations do not output the change text of a c + command in the case of an address range whose first line number + is greater than the second (e.g. 3,1). POSIX requires that the + text be output. Since the historic behavior doesn't seem to have + any particular purpose, this implementation follows the POSIX + behavior. + +12. POSIX does not specify whether address ranges are checked and + reset if a command is not executed due to a jump. The following + program will behave in different ways depending on whether the + 'c' command is triggered at the third line, i.e. will the text + be output even though line 3 of the input will never logically + encounter that command. + + 2,4b + 1,3c\ + text + + Historic implementations, and this implementation, do not output + the text in the above example. The general rule, therefore, + is that a range whose second address is never matched extends to + the end of the input. + +13. Historical implementations allow an output suppressing #n at the + beginning of -e arguments as well as in a script file. POSIX + does not specify this. This implementation follows historical + practice. + +14. POSIX does not explicitly specify how sed behaves if no script is + specified. Since the sed Synopsis permits this form of the command, + and the language in the Description section states that the input + is output, it seems reasonable that it behave like the cat(1) + command. Historic sed implementations behave differently for "ls | + sed", where they produce no output, and "ls | sed -e#", where they + behave like cat. This implementation behaves like cat in both cases. + +15. The POSIX requirement to open all w files at the beginning makes + sed behave nonintuitively when the w commands are preceded by + addresses or are within conditional blocks. This implementation + follows historic practice and POSIX, by default, and provides the + -a option which opens the files only when they are needed. + +16. POSIX does not specify how escape sequences other than \n and \D + (where D is the delimiter character) are to be treated. This is + reasonable, however, it also doesn't state that the backslash is + to be discarded from the output regardless. A strict reading of + POSIX would be that "echo xyz | sed s/./\a" would display "\ayz". + As historic sed implementations always discarded the backslash, + this implementation does as well. + +17. POSIX specifies that an address can be "empty". This implies + that constructs like ",d" or "1,d" and ",5d" are allowed. This + is not true for historic implementations or this implementation + of sed. + +18. The b t and : commands are documented in POSIX to ignore leading + white space, but no mention is made of trailing white space. + Historic implementations of sed assigned different locations to + the labels "x" and "x ". This is not useful, and leads to subtle + programming errors, but it is historic practice and changing it + could theoretically break working scripts. This implementation + follows historic practice. + +19. Although POSIX specifies that reading from files that do not exist + from within the script must not terminate the script, it does not + specify what happens if a write command fails. Historic practice + is to fail immediately if the file cannot be opened or written. + This implementation follows historic practice. + +20. Historic practice is that the \n construct can be used for either + string1 or string2 of the y command. This is not specified by + POSIX. This implementation follows historic practice. + +21. POSIX does not specify if the "Nth occurrence" of an RE in a + substitute command is an overlapping or a non-overlapping one, + i.e. what is the result of s/a*/A/2 on the pattern "aaaaa aaaaa". + Historical practice is to drop core or only do non-overlapping + RE's. This implementation only does non-overlapping RE's. + +22. Historic implementations of sed ignore the RE delimiter characters + within character classes. This is not specified in POSIX. This + implementation follows historic practice. + +23. Historic implementations handle empty RE's in a special way: the + empty RE is interpreted as if it were the last RE encountered, + whether in an address or elsewhere. POSIX does not document this + behavior. For example the command: + + sed -e /abc/s//XXX/ + + substitutes XXX for the pattern abc. The semantics of "the last + RE" can be defined in two different ways: + + 1. The last RE encountered when compiling (lexical/static scope). + 2. The last RE encountered while running (dynamic scope). + + While many historical implementations fail on programs depending + on scope differences, the SunOS version exhibited dynamic scope + behaviour. This implementation does dynamic scoping, as this seems + the most useful and in order to remain consistent with historical + practice. diff --git a/usr.bin/sed/README b/usr.bin/sed/README deleted file mode 100644 index 85d1fa0f69..0000000000 --- a/usr.bin/sed/README +++ /dev/null @@ -1,48 +0,0 @@ -This directory contains GNU sed. Please report all bugs and comments -to bug-gnu-utils@prep.ai.mit.edu. - -This sed may run slower than some UN*X seds. This is because it uses -the regular-expression routines from Emacs, which are rather complete -and powerful, but not as fast as they could be. If you really care -about speed, use perl instead. - -To compile: - -1. Type `sh configure'. This shell script attempts to guess correct -values for various system-dependent variables used during compilation, -and creates the file `Makefile'. This takes a minute or so. - -If you want to compile in a different directory from the one -containing the source code, `cd' to that directory and run `configure' -with the option `+srcdir=DIR', where DIR is the directory that -contains the source code. The object files and executables will be -put in the current directory. This option only works with versions of -`make' that support the VPATH variable. `configure' ignores any other -arguments you give it. - -If your system requires unusual options for compilation or linking -that `configure' doesn't know about, you can give `configure' initial -values for variables by setting them in the environment; in -Bourne-compatible shells, you can do that on the command line like -this: -$ CC='gcc -traditional' LIBS=-lposix sh configure - -2. If you want to change the directories where the program will be -installed, or the optimization options, edit `Makefile' and change -those values. If you have an unusual system that needs special -compilation options that `configure' doesn't know about, and you -didn't pass them in the environment when running `configure', you -should add them to `Makefile' now. Alternately, teach `configure' how -to figure out that it is being run on a system where they are needed, -and mail the diffs to the address listed at the top of this file so we -can include them in the next release. - -3. Type `make'. - -4. If the program compiles successfully, type `make install' to -install it. - -5. After you have installed the program, you can remove the binary -from the source directory by typing `make clean'. Type `make -distclean' if you also want to remove `Makefile', for instance if you -are going to recompile sed next on another type of machine. diff --git a/usr.bin/sed/compile.c b/usr.bin/sed/compile.c new file mode 100644 index 0000000000..d898ca52ca --- /dev/null +++ b/usr.bin/sed/compile.c @@ -0,0 +1,738 @@ +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)compile.c 5.6 (Berkeley) 11/2/92"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "defs.h" +#include "extern.h" + +static char *compile_addr __P((char *, struct s_addr *)); +static char *compile_ccl __P((char **, char *)); +static char *compile_delimited __P((char *, char *)); +static char *compile_flags __P((char *, struct s_subst *)); +static char *compile_re __P((char *, regex_t **)); +static char *compile_subst __P((char *, struct s_subst *)); +static char *compile_text __P((void)); +static char *compile_tr __P((char *, char **)); +static struct s_command + **compile_stream __P((char *, struct s_command **, char *)); +static char *duptoeol __P((char *)); +static struct s_command + *findlabel __P((struct s_command *, struct s_command *)); +static void fixuplabel __P((struct s_command *, struct s_command *, + struct s_command *)); + +/* + * Command specification. This is used to drive the command parser. + */ +struct s_format { + char code; /* Command code */ + int naddr; /* Number of address args */ + enum e_args args; /* Argument type */ +}; + +static struct s_format cmd_fmts[] = { + {'{', 2, GROUP}, + {'a', 1, TEXT}, + {'b', 2, BRANCH}, + {'c', 2, TEXT}, + {'d', 2, EMPTY}, + {'D', 2, EMPTY}, + {'g', 2, EMPTY}, + {'G', 2, EMPTY}, + {'h', 2, EMPTY}, + {'H', 2, EMPTY}, + {'i', 1, TEXT}, + {'l', 2, EMPTY}, + {'n', 2, EMPTY}, + {'N', 2, EMPTY}, + {'p', 2, EMPTY}, + {'P', 2, EMPTY}, + {'q', 1, EMPTY}, + {'r', 1, RFILE}, + {'s', 2, SUBST}, + {'t', 2, BRANCH}, + {'w', 2, WFILE}, + {'x', 2, EMPTY}, + {'y', 2, TR}, + {'!', 2, NONSEL}, + {':', 0, LABEL}, + {'#', 0, COMMENT}, + {'=', 1, EMPTY}, + {'\0', 0, COMMENT}, +}; + +/* The compiled program. */ +struct s_command *prog; + +/* + * Compile the program into prog. + * Initialise appends. + */ +void +compile() +{ + *compile_stream(NULL, &prog, NULL) = NULL; + fixuplabel(prog, prog, NULL); + appends = xmalloc(sizeof(struct s_appends) * appendnum); + match = xmalloc((maxnsub + 1) * sizeof(regmatch_t)); +} + +#define EATSPACE() do { \ + if (p) \ + while (*p && isascii(*p) && isspace(*p)) \ + p++; \ + } while (0) + +static struct s_command ** +compile_stream(terminator, link, p) + char *terminator; + struct s_command **link; + register char *p; +{ + static char lbuf[_POSIX2_LINE_MAX + 1]; /* To save stack */ + struct s_command *cmd, *cmd2; + struct s_format *fp; + int naddr; /* Number of addresses */ + + if (p != NULL) + goto semicolon; + for (;;) { + if ((p = cu_fgets(lbuf, sizeof(lbuf))) == NULL) { + if (terminator != NULL) + err(COMPILE, "unexpected EOF (pending }'s)"); + return (link); + } + +semicolon: EATSPACE(); + if (p && (*p == '#' || *p == '\0')) + continue; + if (*p == '}') { + if (terminator == NULL) + err(COMPILE, "unexpected }"); + return (link); + } + *link = cmd = xmalloc(sizeof(struct s_command)); + link = &cmd->next; + cmd->nonsel = cmd->inrange = 0; + /* First parse the addresses */ + naddr = 0; + cmd->a1 = cmd->a2 = NULL; + +/* Valid characters to start an address */ +#define addrchar(c) (strchr("0123456789/\\$", (c))) + if (addrchar(*p)) { + naddr++; + cmd->a1 = xmalloc(sizeof(struct s_addr)); + p = compile_addr(p, cmd->a1); + EATSPACE(); /* EXTENSION */ + if (*p == ',') { + naddr++; + p++; + EATSPACE(); /* EXTENSION */ + cmd->a2 = xmalloc(sizeof(struct s_addr)); + p = compile_addr(p, cmd->a2); + } + } + +nonsel: /* Now parse the command */ + EATSPACE(); + if (!*p) + err(COMPILE, "command expected"); + cmd->code = *p; + for (fp = cmd_fmts; fp->code; fp++) + if (fp->code == *p) + break; + if (!fp->code) + err(COMPILE, "invalid command code %c", *p); + if (naddr > fp->naddr) + err(COMPILE, +"command %c expects up to %d address(es), found %d", *p, fp->naddr, naddr); + switch (fp->args) { + case NONSEL: /* ! */ + cmd->nonsel = ! cmd->nonsel; + p++; + goto nonsel; + case GROUP: /* { */ + p++; + EATSPACE(); + if (!*p) + p = NULL; + cmd2 = xmalloc(sizeof(struct s_command)); + cmd2->code = '}'; + *compile_stream("}", &cmd->u.c, p) = cmd2; + cmd->next = cmd2; + link = &cmd2->next; + break; + case EMPTY: /* d D g G h H l n N p P q x = \0 */ + p++; + EATSPACE(); + if (*p == ';') { + p++; + link = &cmd->next; + goto semicolon; + } + if (*p) + err(COMPILE, +"extra characters at the end of %c command", cmd->code); + break; + case TEXT: /* a c i */ + p++; + EATSPACE(); + if (*p != '\\') + err(COMPILE, +"command %c expects \\ followed by text", cmd->code); + p++; + EATSPACE(); + if (*p) + err(COMPILE, +"extra characters after \\ at the end of %c command", cmd->code); + cmd->t = compile_text(); + break; + case COMMENT: /* \0 # */ + break; + case WFILE: /* w */ + p++; + EATSPACE(); + if (*p == '\0') + err(COMPILE, "filename expected"); + cmd->t = duptoeol(p); + if (aflag) + cmd->u.fd = -1; + else if ((cmd->u.fd = open(p, + O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, + DEFFILEMODE)) == -1) + err(FATAL, "%s: %s\n", p, strerror(errno)); + break; + case RFILE: /* r */ + p++; + EATSPACE(); + if (*p == '\0') + err(COMPILE, "filename expected"); + else + cmd->t = duptoeol(p); + break; + case BRANCH: /* b t */ + p++; + EATSPACE(); + if (*p == '\0') + cmd->t = NULL; + else + cmd->t = duptoeol(p); + break; + case LABEL: /* : */ + p++; + EATSPACE(); + cmd->t = duptoeol(p); + if (strlen(p) == 0) + err(COMPILE, "empty label"); + break; + case SUBST: /* s */ + p++; + if (*p == '\0' || *p == '\\') + err(COMPILE, +"substitute pattern can not be delimited by newline or backslash"); + cmd->u.s = xmalloc(sizeof(struct s_subst)); + p = compile_re(p, &cmd->u.s->re); + if (p == NULL) + err(COMPILE, "unterminated substitute pattern"); + --p; + p = compile_subst(p, cmd->u.s); + p = compile_flags(p, cmd->u.s); + EATSPACE(); + if (*p == ';') { + p++; + link = &cmd->next; + goto semicolon; + } + break; + case TR: /* y */ + p++; + p = compile_tr(p, (char **)&cmd->u.y); + EATSPACE(); + if (*p == ';') { + p++; + link = &cmd->next; + goto semicolon; + } + if (*p) + err(COMPILE, +"extra text at the end of a transform command"); + break; + } + } +} + +/* + * Get a delimited string. P points to the delimeter of the string; d points + * to a buffer area. Newline and delimiter escapes are processed; other + * escapes are ignored. + * + * Returns a pointer to the first character after the final delimiter or NULL + * in the case of a non-terminated string. The character array d is filled + * with the processed string. + */ +static char * +compile_delimited(p, d) + char *p, *d; +{ + char c; + + c = *p++; + if (c == '\0') + return (NULL); + else if (c == '\\') + err(COMPILE, "\\ can not be used as a string delimiter"); + else if (c == '\n') + err(COMPILE, "newline can not be used as a string delimiter"); + while (*p) { + if (*p == '[') { + if ((d = compile_ccl(&p, d)) == NULL) + err(COMPILE, "unbalanced brackets ([])"); + continue; + } else if (*p == '\\' && p[1] == c) + p++; + else if (*p == '\\' && p[1] == 'n') { + *d++ = '\n'; + p += 2; + continue; + } else if (*p == '\\' && p[1] == '\\') + *d++ = *p++; + else if (*p == c) { + *d = '\0'; + return (p + 1); + } + *d++ = *p++; + } + return (NULL); +} + + +/* compile_ccl: expand a POSIX character class */ +static char * +compile_ccl(sp, t) + char **sp; + char *t; +{ + int c, d; + char *s = *sp; + + *t++ = *s++; + if (*s == '^') + *t++ = *s++; + if (*s == ']') + *t++ = *s++; + for (; *s && (*t = *s) != ']'; s++, t++) + if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '=')) { + *++t = *++s, t++, s++; + for (c = *s; (*t = *s) != ']' || c != d; s++, t++) + if ((c = *s) == '\0') + return NULL; + } else if (*s == '\\' && s[1] == 'n') + *t = '\n', s++; + return (*s == ']') ? *sp = ++s, ++t : NULL; +} + +/* + * Get a regular expression. P points to the delimiter of the regular + * expression; repp points to the address of a regexp pointer. Newline + * and delimiter escapes are processed; other escapes are ignored. + * Returns a pointer to the first character after the final delimiter + * or NULL in the case of a non terminated regular expression. The regexp + * pointer is set to the compiled regular expression. + * Cflags are passed to regcomp. + */ +static char * +compile_re(p, repp) + char *p; + regex_t **repp; +{ + int eval; + char re[_POSIX2_LINE_MAX + 1]; + + p = compile_delimited(p, re); + if (p && strlen(re) == 0) { + *repp = NULL; + return (p); + } + *repp = xmalloc(sizeof(regex_t)); + if (p && (eval = regcomp(*repp, re, 0)) != 0) + err(COMPILE, "RE error: %s", strregerror(eval, *repp)); + if (maxnsub < (*repp)->re_nsub) + maxnsub = (*repp)->re_nsub; + return (p); +} + +/* + * Compile the substitution string of a regular expression and set res to + * point to a saved copy of it. Nsub is the number of parenthesized regular + * expressions. + */ +static char * +compile_subst(p, s) + char *p; + struct s_subst *s; +{ + static char lbuf[_POSIX2_LINE_MAX + 1]; + int asize, ref, size; + char c, *text, *op, *sp; + + c = *p++; /* Terminator character */ + if (c == '\0') + return (NULL); + + s->maxbref = 0; + s->linenum = linenum; + asize = 2 * _POSIX2_LINE_MAX + 1; + text = xmalloc(asize); + size = 0; + do { + op = sp = text + size; + for (; *p; p++) { + if (*p == '\\') { + p++; + if (strchr("123456789", *p) != NULL) { + *sp++ = '\\'; + ref = *p - '0'; + if (s->re != NULL && + ref > s->re->re_nsub) + err(COMPILE, +"\\%c not defined in the RE", *p); + if (s->maxbref < ref) + s->maxbref = ref; + } else if (*p == '&' || *p == '\\') + *sp++ = '\\'; + } else if (*p == c) { + p++; + *sp++ = '\0'; + size += sp - op; + s->new = xrealloc(text, size); + return (p); + } else if (*p == '\n') { + err(COMPILE, +"unescaped newline inside substitute pattern"); + /* NOTREACHED */ + } + *sp++ = *p; + } + size += sp - op; + if (asize - size < _POSIX2_LINE_MAX + 1) { + asize *= 2; + text = xmalloc(asize); + } + } while (cu_fgets(p = lbuf, sizeof(lbuf))); + err(COMPILE, "unterminated substitute in regular expression"); + /* NOTREACHED */ +} + +/* + * Compile the flags of the s command + */ +static char * +compile_flags(p, s) + char *p; + struct s_subst *s; +{ + int gn; /* True if we have seen g or n */ + char wfile[_POSIX2_LINE_MAX + 1], *q; + + s->n = 1; /* Default */ + s->p = 0; + s->wfile = NULL; + s->wfd = -1; + for (gn = 0;;) { + EATSPACE(); /* EXTENSION */ + switch (*p) { + case 'g': + if (gn) + err(COMPILE, +"more than one number or 'g' in substitute flags"); + gn = 1; + s->n = 0; + break; + case '\0': + case '\n': + case ';': + return (p); + case 'p': + s->p = 1; + break; + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + if (gn) + err(COMPILE, +"more than one number or 'g' in substitute flags"); + gn = 1; + /* XXX Check for overflow */ + s->n = (int)strtol(p, &p, 10); + break; + case 'w': + p++; +#ifdef HISTORIC_PRACTICE + if (*p != ' ') { + err(WARNING, "space missing before w wfile"); + return (p); + } +#endif + EATSPACE(); + q = wfile; + while (*p) { + if (*p == '\n') + break; + *q++ = *p++; + } + *q = '\0'; + if (q == wfile) + err(COMPILE, "no wfile specified"); + s->wfile = strdup(wfile); + if (!aflag && (s->wfd = open(wfile, + O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, + DEFFILEMODE)) == -1) + err(FATAL, "%s: %s\n", wfile, strerror(errno)); + return (p); + default: + err(COMPILE, + "bad flag in substitute command: '%c'", *p); + break; + } + p++; + } +} + +/* + * Compile a translation set of strings into a lookup table. + */ +static char * +compile_tr(p, transtab) + char *p; + char **transtab; +{ + int i; + char *lt, *op, *np; + char old[_POSIX2_LINE_MAX + 1]; + char new[_POSIX2_LINE_MAX + 1]; + + if (*p == '\0' || *p == '\\') + err(COMPILE, +"transform pattern can not be delimited by newline or backslash"); + p = compile_delimited(p, old); + if (p == NULL) { + err(COMPILE, "unterminated transform source string"); + return (NULL); + } + p = compile_delimited(--p, new); + if (p == NULL) { + err(COMPILE, "unterminated transform target string"); + return (NULL); + } + EATSPACE(); + if (strlen(new) != strlen(old)) { + err(COMPILE, "transform strings are not the same length"); + return (NULL); + } + /* We assume characters are 8 bits */ + lt = xmalloc(UCHAR_MAX); + for (i = 0; i <= UCHAR_MAX; i++) + lt[i] = (char)i; + for (op = old, np = new; *op; op++, np++) + lt[(u_char)*op] = *np; + *transtab = lt; + return (p); +} + +/* + * Compile the text following an a or i command. + */ +static char * +compile_text() +{ + int asize, size; + char *text, *p, *op, *s; + char lbuf[_POSIX2_LINE_MAX + 1]; + + asize = 2 * _POSIX2_LINE_MAX + 1; + text = xmalloc(asize); + size = 0; + while (cu_fgets(lbuf, sizeof(lbuf))) { + op = s = text + size; + p = lbuf; + EATSPACE(); + for (; *p; p++) { + if (*p == '\\') + p++; + *s++ = *p; + } + size += s - op; + if (p[-2] != '\\') { + *s = '\0'; + break; + } + if (asize - size < _POSIX2_LINE_MAX + 1) { + asize *= 2; + text = xmalloc(asize); + } + } + return (xrealloc(text, size + 1)); +} + +/* + * Get an address and return a pointer to the first character after + * it. Fill the structure pointed to according to the address. + */ +static char * +compile_addr(p, a) + char *p; + struct s_addr *a; +{ + char *end; + + switch (*p) { + case '\\': /* Context address */ + ++p; + /* FALLTHROUGH */ + case '/': /* Context address */ + p = compile_re(p, &a->u.r); + if (p == NULL) + err(COMPILE, "unterminated regular expression"); + a->type = AT_RE; + return (p); + + case '$': /* Last line */ + a->type = AT_LAST; + return (p + 1); + /* Line number */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + a->type = AT_LINE; + a->u.l = strtol(p, &end, 10); + return (end); + default: + err(COMPILE, "expected context address"); + return (NULL); + } +} + +/* + * Return a copy of all the characters up to \n or \0 + */ +static char * +duptoeol(s) + register char *s; +{ + size_t len; + char *start; + + for (start = s; *s != '\0' && *s != '\n'; ++s); + *s = '\0'; + len = s - start + 1; + return (memmove(xmalloc(len), start, len)); +} + +/* + * Find the label contained in the command l in the command linked list cp. + * L is excluded from the search. Return NULL if not found. + */ +static struct s_command * +findlabel(l, cp) + struct s_command *l, *cp; +{ + struct s_command *r; + + for (; cp; cp = cp->next) + if (cp->code == ':' && cp != l && strcmp(l->t, cp->t) == 0) + return (cp); + else if (cp->code == '{' && (r = findlabel(l, cp->u.c))) + return (r); + return (NULL); +} + +/* + * Convert goto label names to addresses. + * Detect duplicate labels. + * Set appendnum to the number of a and r commands in the script. + * Free the memory used by labels in b and t commands (but not by :) + * Root is a pointer to the script linked list; cp points to the + * search start. + * TODO: Remove } nodes + */ +static void +fixuplabel(root, cp, end) + struct s_command *root, *cp, *end; +{ + struct s_command *cp2; + + for (; cp != end; cp = cp->next) + switch (cp->code) { + case ':': + if (findlabel(cp, root)) + err(COMPILE2, "duplicate label %s", cp->t); + break; + case 'a': + case 'r': + appendnum++; + break; + case 'b': + case 't': + if (cp->t == NULL) { + cp->u.c = NULL; + break; + } + if ((cp2 = findlabel(cp, root)) == NULL) + err(COMPILE2, "undefined label '%s'", cp->t); + free(cp->t); + cp->u.c = cp2; + break; + case '{': + fixuplabel(root, cp->u.c, cp->next); + break; + } +} diff --git a/usr.bin/sed/compile.test b/usr.bin/sed/compile.test new file mode 100644 index 0000000000..28a5e7ce00 --- /dev/null +++ b/usr.bin/sed/compile.test @@ -0,0 +1,25 @@ +#!/bin/sh +SED=./sed +PRINTF=printf + +# tests that a delimiter okay in ccl, i.e., s/[/]// deletes a / +abc=`$PRINTF 'hello/world\n' | $SED 's/[/]/x/;s/\n//'` +[ ! X"$abc" = Xhelloxworld ] && { echo "(1) failed: $abc"; } + +# tests that a \ is not a general escape character in ccl, i.e., s/[\]// +# deletes a \ +abc=`$PRINTF 'hello\\world\n' | $SED 's/[\]/x/'` +[ ! X"$abc" = Xhelloxworld ] && { echo "(2) failed: $abc"; } + +# tests that a \n is mapped to a newline in ccl, i.e., s/[\n]// +# deletes a newline +abc=`$PRINTF 'hello\nworld\n' | $SED 'N; s/[\n]/x/'` +[ ! X"$abc" = Xhelloxworld ] && { echo "(3) failed: $abc"; } + +# tests that a \\ does not map to a \ in ccl, i.e., s/[\\n]// +# deletes a newline - not a \ or an n +abc=`$PRINTF 'hello\n\\world\n' | $SED 'N; s/[\\n]/x/;s/[\]/y/'` +[ ! X"$abc" = Xhelloxyworld ] && { echo "(4) failed: $abc"; } + +abc=`$PRINTF 'helloworld\n' | $SED 's/[[:alpha:]]/x/'` +[ ! X"$abc" = Xxelloworld ] && { echo "(5) failed: $abc"; } diff --git a/usr.bin/sed/defs.h b/usr.bin/sed/defs.h new file mode 100644 index 0000000000..948ba44548 --- /dev/null +++ b/usr.bin/sed/defs.h @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)defs.h 5.3 (Berkeley) 8/28/92 + */ + +/* + * Types of address specifications + */ +enum e_atype { + AT_RE, /* Line that match RE */ + AT_LINE, /* Specific line */ + AT_LAST, /* Last line */ +}; + +/* + * Format of an address + */ +struct s_addr { + enum e_atype type; /* Address type */ + union { + u_long l; /* Line number */ + regex_t *r; /* Regular expression */ + } u; +}; + +/* + * Substitution command + */ +struct s_subst { + int n; /* Occurrence to subst. */ + int p; /* True if p flag */ + char *wfile; /* NULL if no wfile */ + int wfd; /* Cached file descriptor */ + regex_t *re; /* Regular expression */ + int maxbref; /* Largest backreference. */ + u_long linenum; /* Line number. */ + char *new; /* Replacement text */ +}; + + +/* + * An internally compiled command. + * Initialy, label references are stored in u.t, on a second pass they + * are updated to pointers. + */ +struct s_command { + struct s_command *next; /* Pointer to next command */ + struct s_addr *a1, *a2; /* Start and end address */ + char *t; /* Text for : a c i r w */ + union { + struct s_command *c; /* Command(s) for b t { */ + struct s_subst *s; /* Substitute command */ + u_char *y; /* Replace command array */ + int fd; /* File descriptor for w */ + } u; + char code; /* Command code */ + u_int nonsel:1; /* True if ! */ + u_int inrange:1; /* True if in range */ +}; + +/* + * Types of command arguments recognised by the parser + */ +enum e_args { + EMPTY, /* d D g G h H l n N p P q x = \0 */ + TEXT, /* a c i */ + NONSEL, /* ! */ + GROUP, /* { */ + COMMENT, /* # */ + BRANCH, /* b t */ + LABEL, /* : */ + RFILE, /* r */ + WFILE, /* w */ + SUBST, /* s */ + TR /* y */ +}; + +/* + * Structure containing things to append before a line is read + */ +struct s_appends { + enum {AP_STRING, AP_FILE} type; + char *s; +}; + +enum e_spflag { + APPEND, /* Append to the contents. */ + APPENDNL, /* Append, with newline. */ + REPLACE, /* Replace the contents. */ +}; + +/* + * Structure for a space (process, hold, otherwise). + */ +typedef struct { + char *space; /* Current space pointer. */ + size_t len; /* Current length. */ + int deleted; /* If deleted. */ + char *back; /* Backing memory. */ + size_t blen; /* Backing memory length. */ +} SPACE; + +/* + * Error severity codes: + */ +#define FATAL 0 /* Exit immediately with 1 */ +#define ERROR 1 /* Continue, but change exit value */ +#define WARNING 2 /* Just print the warning */ +#define COMPILE 3 /* Print error, count and finish script */ +#define COMPILE2 3 /* Print error, count and finish script */ diff --git a/usr.bin/sed/extern.h b/usr.bin/sed/extern.h new file mode 100644 index 0000000000..32b68a1ee9 --- /dev/null +++ b/usr.bin/sed/extern.h @@ -0,0 +1,59 @@ +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 5.5 (Berkeley) 8/30/92 + */ + +extern struct s_command *prog; +extern struct s_appends *appends; +extern regmatch_t *match; +extern size_t maxnsub; +extern u_long linenum; +extern int appendnum; +extern int lastline; +extern int aflag, eflag, nflag; +extern char *fname; + +void compile __P((void)); +char *cu_fgets __P((char *, int)); +void err __P((int, const char *, ...)); +int mf_fgets __P((SPACE *, enum e_spflag)); +void process __P((void)); +char *strregerror __P((int, regex_t *)); +void *xmalloc __P((u_int)); +void *xrealloc __P((void *, u_int)); +void cfclose __P((struct s_command *, struct s_command *)); +void cspace __P((SPACE *, char *, size_t, enum e_spflag)); diff --git a/usr.bin/sed/getopt.c b/usr.bin/sed/getopt.c deleted file mode 100644 index 8c0ba2e29f..0000000000 --- a/usr.bin/sed/getopt.c +++ /dev/null @@ -1,594 +0,0 @@ -/* Getopt for GNU. - Copyright (C) 1987, 1989, 1990, 1991 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* AIX requires this to be the first thing in the file. */ -#ifdef __GNUC__ -#define alloca __builtin_alloca -#else /* not __GNUC__ */ -#ifdef sparc -#include <alloca.h> -#else -#ifdef _AIX -#pragma alloca -#else -char *alloca (); -#endif -#endif /* sparc */ -#endif /* not __GNUC__ */ - -#ifndef __STDC__ -#define const -#endif - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of `argv' so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. - - Setting the environment variable _POSIX_OPTION_ORDER disables permutation. - Then the behavior is completely standard. - - GNU application programs can use a third alternative mode in which - they can distinguish the relative order of options and other arguments. */ - -#include <stdio.h> - -#if defined(STDC_HEADERS) || defined(__GNU_LIBRARY__) -#include <stdlib.h> -#else /* STDC_HEADERS or __GNU_LIBRARY__ */ -char *getenv (); -char *malloc (); -#endif /* STDC_HEADERS or __GNU_LIBRARY__ */ - -#if defined(USG) || defined(STDC_HEADERS) || defined(__GNU_LIBRARY__) -#include <string.h> -#define bcopy(s, d, n) memcpy ((d), (s), (n)) -#define index strchr -#else /* USG or STDC_HEADERS or __GNU_LIBRARY__ */ -#ifdef VMS -#include <string.h> -#else /* VMS */ -#include <strings.h> -#endif /* VMS */ -/* Declaring bcopy causes errors on systems whose declarations are different. - If the declaration is omitted, everything works fine. */ -#endif /* USG or STDC_HEADERS or __GNU_LIBRARY__ */ - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -char *optarg = 0; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns EOF, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -int optind = 0; - -/* The next char to be scanned in the option-element - in which the last option character we returned was found. - This allows us to pick up the scan where we left off. - - If this is zero, or a null string, it means resume the scan - by advancing to the next ARGV-element. */ - -static char *nextchar; - -/* Callers store zero here to inhibit the error message - for unrecognized options. */ - -int opterr = 1; - -/* Describe how to deal with options that follow non-option ARGV-elements. - - If the caller did not specify anything, - the default is REQUIRE_ORDER if the environment variable - _POSIX_OPTION_ORDER is defined, PERMUTE otherwise. - - REQUIRE_ORDER means don't recognize them as options; - stop option processing when the first non-option is seen. - This is what Unix does. - This mode of operation is selected by either setting the environment - variable _POSIX_OPTION_ORDER, or using `+' as the first character - of the list of option characters. - - PERMUTE is the default. We permute the contents of ARGV as we scan, - so that eventually all the non-options are at the end. This allows options - to be given in any order, even with programs that were not written to - expect this. - - RETURN_IN_ORDER is an option available to programs that were written - to expect options and other ARGV-elements in any order and that care about - the ordering of the two. We describe each non-option ARGV-element - as if it were the argument of an option with character code 1. - Using `-' as the first character of the list of option characters - selects this mode of operation. - - The special argument `--' forces an end of option-scanning regardless - of the value of `ordering'. In the case of RETURN_IN_ORDER, only - `--' can cause `getopt' to return EOF with `optind' != ARGC. */ - -static enum -{ - REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER -} ordering; - -/* Describe the long-named options requested by the application. - _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an - element containing a name which is zero. - The field `has_arg' is 1 if the option takes an argument, - 2 if it takes an optional argument. */ - -struct option -{ - char *name; - int has_arg; - int *flag; - int val; -}; - -const struct option *_getopt_long_options; - -int _getopt_long_only = 0; - -/* Index in _GETOPT_LONG_OPTIONS of the long-named option actually found. - Only valid when a long-named option was found. */ - -int option_index; - -/* Handle permutation of arguments. */ - -/* Describe the part of ARGV that contains non-options that have - been skipped. `first_nonopt' is the index in ARGV of the first of them; - `last_nonopt' is the index after the last of them. */ - -static int first_nonopt; -static int last_nonopt; - -/* Exchange two adjacent subsequences of ARGV. - One subsequence is elements [first_nonopt,last_nonopt) - which contains all the non-options that have been skipped so far. - The other is elements [last_nonopt,optind), which contains all - the options processed since those non-options were skipped. - - `first_nonopt' and `last_nonopt' are relocated so that they describe - the new indices of the non-options in ARGV after they are moved. */ - -static void -exchange (argv) - char **argv; -{ - int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); - char **temp = (char **) alloca (nonopts_size); - - /* Interchange the two blocks of data in ARGV. */ - - bcopy (&argv[first_nonopt], temp, nonopts_size); - bcopy (&argv[last_nonopt], &argv[first_nonopt], - (optind - last_nonopt) * sizeof (char *)); - bcopy (temp, &argv[first_nonopt + optind - last_nonopt], nonopts_size); - - /* Update records for the slots the non-options now occupy. */ - - first_nonopt += (optind - last_nonopt); - last_nonopt = optind; -} - -/* Scan elements of ARGV (whose length is ARGC) for option characters - given in OPTSTRING. - - If an element of ARGV starts with '-', and is not exactly "-" or "--", - then it is an option element. The characters of this element - (aside from the initial '-') are option characters. If `getopt' - is called repeatedly, it returns successively each of the option characters - from each of the option elements. - - If `getopt' finds another option character, it returns that character, - updating `optind' and `nextchar' so that the next call to `getopt' can - resume the scan with the following option character or ARGV-element. - - If there are no more option characters, `getopt' returns `EOF'. - Then `optind' is the index in ARGV of the first ARGV-element - that is not an option. (The ARGV-elements have been permuted - so that those that are not options now come last.) - - OPTSTRING is a string containing the legitimate option characters. - If an option character is seen that is not listed in OPTSTRING, - return '?' after printing an error message. If you set `opterr' to - zero, the error message is suppressed but we still return '?'. - - If a char in OPTSTRING is followed by a colon, that means it wants an arg, - so the following text in the same ARGV-element, or the text of the following - ARGV-element, is returned in `optarg'. Two colons mean an option that - wants an optional arg; if there is text in the current ARGV-element, - it is returned in `optarg', otherwise `optarg' is set to zero. - - If OPTSTRING starts with `-' or `+', it requests different methods of - handling the non-option ARGV-elements. - See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - - Long-named options begin with `+' instead of `-'. - Their names may be abbreviated as long as the abbreviation is unique - or is an exact match for some defined option. If they have an - argument, it follows the option name in the same ARGV-element, separated - from the option name by a `=', or else the in next ARGV-element. - When `getopt' finds a long-named option, it returns 0 if that option's - `flag' field is nonzero, the value of the option's `val' field - otherwise. */ - -int -getopt (argc, argv, optstring) - int argc; - char **argv; - const char *optstring; -{ - optarg = 0; - - /* Initialize the internal data when the first call is made. - Start processing options with ARGV-element 1 (since ARGV-element 0 - is the program name); the sequence of previously skipped - non-option ARGV-elements is empty. */ - - if (optind == 0) - { - first_nonopt = last_nonopt = optind = 1; - - nextchar = 0; - - /* Determine how to handle the ordering of options and nonoptions. */ - - if (optstring[0] == '-') - { - ordering = RETURN_IN_ORDER; - ++optstring; - } - else if (optstring[0] == '+') - { - ordering = REQUIRE_ORDER; - ++optstring; - } - else if (getenv ("_POSIX_OPTION_ORDER") != 0) - ordering = REQUIRE_ORDER; - else - ordering = PERMUTE; - } - - if (nextchar == 0 || *nextchar == 0) - { - if (ordering == PERMUTE) - { - /* If we have just processed some options following some non-options, - exchange them so that the options come first. */ - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange (argv); - else if (last_nonopt != optind) - first_nonopt = optind; - - /* Now skip any additional non-options - and extend the range of non-options previously skipped. */ - - while (optind < argc - && (argv[optind][0] != '-' - || argv[optind][1] == 0) - && (_getopt_long_options == 0 - || argv[optind][0] != '+' - || argv[optind][1] == 0)) - optind++; - last_nonopt = optind; - } - - /* Special ARGV-element `--' means premature end of options. - Skip it like a null option, - then exchange with previous non-options as if it were an option, - then skip everything else like a non-option. */ - - if (optind != argc && !strcmp (argv[optind], "--")) - { - optind++; - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange (argv); - else if (first_nonopt == last_nonopt) - first_nonopt = optind; - last_nonopt = argc; - - optind = argc; - } - - /* If we have done all the ARGV-elements, stop the scan - and back over any non-options that we skipped and permuted. */ - - if (optind == argc) - { - /* Set the next-arg-index to point at the non-options - that we previously skipped, so the caller will digest them. */ - if (first_nonopt != last_nonopt) - optind = first_nonopt; - return EOF; - } - - /* If we have come to a non-option and did not permute it, - either stop the scan or describe it to the caller and pass it by. */ - - if ((argv[optind][0] != '-' || argv[optind][1] == 0) - && (_getopt_long_options == 0 - || argv[optind][0] != '+' || argv[optind][1] == 0)) - { - if (ordering == REQUIRE_ORDER) - return EOF; - optarg = argv[optind++]; - return 1; - } - - /* We have found another option-ARGV-element. - Start decoding its characters. */ - - nextchar = argv[optind] + 1; - } - - if (_getopt_long_options != 0 - && (argv[optind][0] == '+' - || (_getopt_long_only && argv[optind][0] == '-')) - ) - { - const struct option *p; - char *s = nextchar; - int exact = 0; - int ambig = 0; - const struct option *pfound = 0; - int indfound; - - while (*s && *s != '=') - s++; - - /* Test all options for either exact match or abbreviated matches. */ - for (p = _getopt_long_options, option_index = 0; p->name; - p++, option_index++) - if (!strncmp (p->name, nextchar, s - nextchar)) - { - if (s - nextchar == strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == 0) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else - /* Second nonexact match found. */ - ambig = 1; - } - - if (ambig && !exact) - { - fprintf (stderr, "%s: option `%s' is ambiguous\n", - argv[0], argv[optind]); - nextchar += strlen (nextchar); - optind++; - return '?'; - } - - if (pfound != 0) - { - option_index = indfound; - optind++; - if (*s) - { - if (pfound->has_arg > 0) - optarg = s + 1; - else - { - fprintf (stderr, - "%s: option `%c%s' doesn't allow an argument\n", - argv[0], argv[optind - 1][0], pfound->name); - nextchar += strlen (nextchar); - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (optind < argc) - optarg = argv[optind++]; - else - { - fprintf (stderr, "%s: option `%s' requires an argument\n", - argv[0], argv[optind - 1]); - nextchar += strlen (nextchar); - return '?'; - } - } - nextchar += strlen (nextchar); - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - /* Can't find it as a long option. If this is getopt_long_only, - and the option starts with '-' and is a valid short - option, then interpret it as a short option. Otherwise it's - an error. */ - if (_getopt_long_only == 0 || argv[optind][0] == '+' || - index (optstring, *nextchar) == 0) - { - if (opterr != 0) - fprintf (stderr, "%s: unrecognized option `%c%s'\n", - argv[0], argv[optind][0], nextchar); - nextchar += strlen (nextchar); - optind++; - return '?'; - } - } - - /* Look at and handle the next option-character. */ - - { - char c = *nextchar++; - char *temp = index (optstring, c); - - /* Increment `optind' when we start to process its last character. */ - if (*nextchar == 0) - optind++; - - if (temp == 0 || c == ':') - { - if (opterr != 0) - { - if (c < 040 || c >= 0177) - fprintf (stderr, "%s: unrecognized option, character code 0%o\n", - argv[0], c); - else - fprintf (stderr, "%s: unrecognized option `-%c'\n", - argv[0], c); - } - return '?'; - } - if (temp[1] == ':') - { - if (temp[2] == ':') - { - /* This is an option that accepts an argument optionally. */ - if (*nextchar != 0) - { - optarg = nextchar; - optind++; - } - else - optarg = 0; - nextchar = 0; - } - else - { - /* This is an option that requires an argument. */ - if (*nextchar != 0) - { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - optind++; - } - else if (optind == argc) - { - if (opterr != 0) - fprintf (stderr, "%s: option `-%c' requires an argument\n", - argv[0], c); - c = '?'; - } - else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - optarg = argv[optind++]; - nextchar = 0; - } - } - return c; - } -} - -#ifdef TEST - -/* Compile with -DTEST to make an executable for use in testing - the above definition of `getopt'. */ - -int -main (argc, argv) - int argc; - char **argv; -{ - int c; - int digit_optind = 0; - - while (1) - { - int this_option_optind = optind ? optind : 1; - - c = getopt (argc, argv, "abc:d:0123456789"); - if (c == EOF) - break; - - switch (c) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 && digit_optind != this_option_optind) - printf ("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf ("option %c\n", c); - break; - - case 'a': - printf ("option a\n"); - break; - - case 'b': - printf ("option b\n"); - break; - - case 'c': - printf ("option c with value `%s'\n", optarg); - break; - - case '?': - break; - - default: - printf ("?? getopt returned character code 0%o ??\n", c); - } - } - - if (optind < argc) - { - printf ("non-option ARGV-elements: "); - while (optind < argc) - printf ("%s ", argv[optind++]); - printf ("\n"); - } - - exit (0); -} - -#endif /* TEST */ diff --git a/usr.bin/sed/getopt.h b/usr.bin/sed/getopt.h deleted file mode 100644 index 151379fc69..0000000000 --- a/usr.bin/sed/getopt.h +++ /dev/null @@ -1,102 +0,0 @@ -/* declarations for getopt - Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -extern char *optarg; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns EOF, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -extern int optind; - -/* Callers store zero here to inhibit the error message `getopt' prints - for unrecognized options. */ - -extern int opterr; - -/* Describe the long-named options requested by the application. - _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an - element containing a name which is zero. - - The field `has_arg' is: - 0 if the option does not take an argument, - 1 if the option requires an argument, - 2 if the option takes an optional argument. - - If the field `flag' is nonzero, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. - - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ - -struct option -{ - char *name; - int has_arg; - int *flag; - int val; -}; - -#ifdef __STDC__ -extern const struct option *_getopt_long_options; -#else -extern struct option *_getopt_long_options; -#endif - -/* If nonzero, '-' can introduce long-named options. - Set by getopt_long_only. */ - -extern int _getopt_long_only; - -/* The index in GETOPT_LONG_OPTIONS of the long-named option found. - Only valid when a long-named option has been found by the most - recent call to `getopt'. */ - -extern int option_index; - -#ifdef __STDC__ -int getopt (int argc, char **argv, const char *shortopts); -int getopt_long (int argc, char **argv, const char *shortopts, - const struct option *longopts, int *longind); -int getopt_long_only (int argc, char **argv, const char *shortopts, - const struct option *longopts, int *longind); -void envopt(int *pargc, char ***pargv, char *optstr); -#else -int getopt (); -int getopt_long (); -int getopt_long_only (); -void envopt(); -#endif diff --git a/usr.bin/sed/getopt1.c b/usr.bin/sed/getopt1.c deleted file mode 100644 index 781673c6f2..0000000000 --- a/usr.bin/sed/getopt1.c +++ /dev/null @@ -1,158 +0,0 @@ -/* Getopt for GNU. - Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include "getopt.h" - -#ifndef __STDC__ -#define const -#endif - -#if !defined (NULL) -#define NULL 0 -#endif - -int -getopt_long (argc, argv, options, long_options, opt_index) - int argc; - char **argv; - const char *options; - const struct option *long_options; - int *opt_index; -{ - int val; - - _getopt_long_options = long_options; - val = getopt (argc, argv, options); - if (val == 0 && opt_index != NULL) - *opt_index = option_index; - return val; -} - -/* Like getopt_long, but '-' as well as '+' can indicate a long option. - If an option that starts with '-' doesn't match a long option, - but does match a short option, it is parsed as a short option - instead. */ - -int -getopt_long_only (argc, argv, options, long_options, opt_index) - int argc; - char **argv; - const char *options; - const struct option *long_options; - int *opt_index; -{ - int val; - - _getopt_long_options = long_options; - _getopt_long_only = 1; - val = getopt (argc, argv, options); - if (val == 0 && opt_index != NULL) - *opt_index = option_index; - return val; -} - - -#ifdef TEST - -#include <stdio.h> - -int -main (argc, argv) - int argc; - char **argv; -{ - int c; - int digit_optind = 0; - - while (1) - { - int this_option_optind = optind ? optind : 1; - char *name = '\0'; - int option_index = 0; - static struct option long_options[] = - { - {"add", 1, 0, 0}, - {"append", 0, 0, 0}, - {"delete", 1, 0, 0}, - {"verbose", 0, 0, 0}, - {"create", 0, 0, 0}, - {"file", 1, 0, 0}, - {0, 0, 0, 0} - }; - - c = getopt_long (argc, argv, "abc:d:0123456789", - long_options, &option_index); - if (c == EOF) - break; - - switch (c) - { - case 0: - printf ("option %s", (long_options[option_index]).name); - if (optarg) - printf (" with arg %s", optarg); - printf ("\n"); - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 && digit_optind != this_option_optind) - printf ("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf ("option %c\n", c); - break; - - case 'a': - printf ("option a\n"); - break; - - case 'b': - printf ("option b\n"); - break; - - case 'c': - printf ("option c with value `%s'\n", optarg); - break; - - case '?': - break; - - default: - printf ("?? getopt returned character code 0%o ??\n", c); - } - } - - if (optind < argc) - { - printf ("non-option ARGV-elements: "); - while (optind < argc) - printf ("%s ", argv[optind++]); - printf ("\n"); - } - - exit (0); -} - -#endif /* TEST */ diff --git a/usr.bin/sed/hanoi.sed b/usr.bin/sed/hanoi.sed new file mode 100644 index 0000000000..c003e1906c --- /dev/null +++ b/usr.bin/sed/hanoi.sed @@ -0,0 +1,102 @@ +# Towers of Hanoi in sed. +# +# @(#)hanoi.sed 5.1 (Berkeley) 10/10/90 +# +# +# Ex: +# Run "sed -f hanoi.sed", and enter: +# +# :abcd: : :<CR><CR> +# +# note -- TWO carriage returns, a peculiarity of sed), this will output the +# sequence of states involved in moving 4 rings, the largest called "a" and +# the smallest called "d", from the first to the second of three towers, so +# that the rings on any tower at any time are in descending order of size. +# You can start with a different arrangement and a different number of rings, +# say :ce:b:ax: and it will give the shortest procedure for moving them all +# to the middle tower. The rules are: the names of the rings must all be +# lower-case letters, they must be input within 3 fields (representing the +# towers) and delimited by 4 colons, such that the letters within each field +# are in alphabetical order (i.e. rings are in descending order of size). +# +# For the benefit of anyone who wants to figure out the script, an "internal" +# line of the form +# b:0abx:1a2b3 :2 :3x2 +# has the following meaning: the material after the three markers :1, :2, +# and :3 represents the three towers; in this case the current set-up is +# ":ab : :x :". The numbers after a, b and x in these fields indicate +# that the next time it gets a chance, it will move a to tower 2, move b +# to tower 3, and move x to tower 2. The string after :0 just keeps track +# of the alphabetical order of the names of the rings. The b at the +# beginning means that it is now dealing with ring b (either about to move +# it, or re-evaluating where it should next be moved to). +# +# Although this version is "limited" to 26 rings because of the size of the +# alphabet, one could write a script using the same idea in which the rings +# were represented by arbitrary [strings][within][brackets], and in place of +# the built-in line of the script giving the order of the letters of the +# alphabet, it would accept from the user a line giving the ordering to be +# assumed, e.g. [ucbvax][decvax][hplabs][foo][bar]. +# +# George Bergman +# Math, UC Berkeley 94720 USA + +# cleaning, diagnostics +s/ *//g +/^$/d +/[^a-z:]/{a\ +Illegal characters: use only a-z and ":". Try again. +d +} +/^:[a-z]*:[a-z]*:[a-z]*:$/!{a\ +Incorrect format: use\ +\ : string1 : string2 : string3 :<CR><CR>\ +Try again. +d +} +/\([a-z]\).*\1/{a\ +Repeated letters not allowed. Try again. +d +} +# initial formatting +h +s/[a-z]/ /g +G +s/^:\( *\):\( *\):\( *\):\n:\([a-z]*\):\([a-z]*\):\([a-z]*\):$/:1\4\2\3:2\5\1\3:3\6\1\2:0/ +s/[a-z]/&2/g +s/^/abcdefghijklmnopqrstuvwxyz/ +:a +s/^\(.\).*\1.*/&\1/ +s/.// +/^[^:]/ba +s/\([^0]*\)\(:0.*\)/\2\1:/ +s/^[^0]*0\(.\)/\1&/ +:b +# outputting current state without markers +h +s/.*:1/:/ +s/[123]//gp +g +:c +# establishing destinations +/^\(.\).*\1:1/td +/^\(.\).*:1[^:]*\11/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\31/ +/^\(.\).*:1[^:]*\12/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\33/ +/^\(.\).*:1[^:]*\13/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\32/ +/^\(.\).*:2[^:]*\11/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\33/ +/^\(.\).*:2[^:]*\12/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\32/ +/^\(.\).*:2[^:]*\13/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\31/ +/^\(.\).*:3[^:]*\11/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\32/ +/^\(.\).*:3[^:]*\12/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\31/ +/^\(.\).*:3[^:]*\13/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\33/ +bc +# iterate back to find smallest out-of-place ring +:d +s/^\(.\)\(:0[^:]*\([^:]\)\1.*:\([123]\)[^:]*\1\)\4/\3\2\4/ +td +# move said ring (right, resp. left) +s/^\(.\)\(.*\)\1\([23]\)\(.*:\3[^ ]*\) /\1\2 \4\1\3/ +s/^\(.\)\(.*:\([12]\)[^ ]*\) \(.*\)\1\3/\1\2\1\3\4 / +tb +s/.*/Done! Try another, or end with ^D./p +d diff --git a/usr.bin/sed/main.c b/usr.bin/sed/main.c new file mode 100644 index 0000000000..f4fc96f702 --- /dev/null +++ b/usr.bin/sed/main.c @@ -0,0 +1,352 @@ +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 5.6 (Berkeley) 8/30/92"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <regex.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "defs.h" +#include "extern.h" + +/* + * Linked list of units (strings and files) to be compiled + */ +struct s_compunit { + struct s_compunit *next; + enum e_cut {CU_FILE, CU_STRING} type; + char *s; /* Pointer to string or fname */ +}; + +/* + * Linked list pointer to compilation units and pointer to current + * next pointer. + */ +static struct s_compunit *script, **cu_nextp = &script; + +/* + * Linked list of files to be processed + */ +struct s_flist { + char *fname; + struct s_flist *next; +}; + +/* + * Linked list pointer to files and pointer to current + * next pointer. + */ +static struct s_flist *files, **fl_nextp = &files; + +int aflag, eflag, nflag; + +/* + * Current file and line number; line numbers restart across compilation + * units, but span across input files. + */ +char *fname; /* File name. */ +u_long linenum; +int lastline; /* TRUE on the last line of the last file */ + +static void add_compunit __P((enum e_cut, char *)); +static void add_file __P((char *)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int c, fflag; + + fflag = 0; + while ((c = getopt(argc, argv, "ae:f:n")) != EOF) + switch (c) { + case 'a': + aflag = 1; + break; + case 'e': + eflag = 1; + add_compunit(CU_STRING, optarg); + break; + case 'f': + fflag = 1; + add_compunit(CU_FILE, optarg); + break; + case 'n': + nflag = 1; + break; + default: + case '?': + (void)fprintf(stderr, +"usage:\tsed script [-an] [file ...]\n\tsed [-an] [-e script] ... [-f scipt_file] ... [file ...]\n"); + exit(1); + } + argc -= optind; + argv += optind; + + /* First usage case; script is the first arg */ + if (!eflag && !fflag && *argv) { + add_compunit(CU_STRING, *argv); + argv++; + } + + compile(); + + /* Continue with first and start second usage */ + if (*argv) + for (; *argv; argv++) + add_file(*argv); + else + add_file(NULL); + process(); + cfclose(prog, NULL); + if (fclose(stdout)) + err(FATAL, "stdout: %s", strerror(errno)); + exit (0); +} + +/* + * Like fgets, but go through the chain of compilation units chaining them + * together. Empty strings and files are ignored. + */ +char * +cu_fgets(buf, n) + char *buf; + int n; +{ + static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF; + static FILE *f; /* Current open file */ + static char *s; /* Current pointer inside string */ + static char string_ident[30]; + char *p; + +again: + switch (state) { + case ST_EOF: + if (script == NULL) + return (NULL); + linenum = 0; + switch (script->type) { + case CU_FILE: + if ((f = fopen(script->s, "r")) == NULL) + err(FATAL, + "%s: %s", script->s, strerror(errno)); + fname = script->s; + state = ST_FILE; + goto again; + case CU_STRING: + if ((snprintf(string_ident, + sizeof(string_ident), "\"%s\"", script->s)) >= + sizeof(string_ident) - 1) + (void)strcpy(string_ident + + sizeof(string_ident) - 6, " ...\""); + fname = string_ident; + s = script->s; + state = ST_STRING; + goto again; + } + case ST_FILE: + if ((p = fgets(buf, n, f)) != NULL) { + linenum++; + if (linenum == 1 && buf[0] == '#' && buf[1] == 'n') + nflag = 1; + return (p); + } + script = script->next; + (void)fclose(f); + state = ST_EOF; + goto again; + case ST_STRING: + if (linenum == 0 && s[0] == '#' && s[1] == 'n') + nflag = 1; + p = buf; + for (;;) { + if (n-- <= 1) { + *p = '\0'; + linenum++; + return (buf); + } + switch (*s) { + case '\0': + state = ST_EOF; + if (s == script->s) { + script = script->next; + goto again; + } else { + script = script->next; + *p = '\0'; + linenum++; + return (buf); + } + case '\n': + *p++ = '\n'; + *p = '\0'; + s++; + linenum++; + return (buf); + default: + *p++ = *s++; + } + } + } + /* NOTREACHED */ +} + +/* + * Like fgets, but go through the list of files chaining them together. + * Set len to the length of the line. + */ +int +mf_fgets(sp, spflag) + SPACE *sp; + enum e_spflag spflag; +{ + static FILE *f; /* Current open file */ + size_t len; + char c, *p; + + if (f == NULL) + /* Advance to first non-empty file */ + for (;;) { + if (files == NULL) { + lastline = 1; + return (0); + } + if (files->fname == NULL) { + f = stdin; + fname = "stdin"; + } else { + fname = files->fname; + if ((f = fopen(fname, "r")) == NULL) + err(FATAL, "%s: %s", + fname, strerror(errno)); + } + if ((c = getc(f)) != EOF) { + (void)ungetc(c, f); + break; + } + (void)fclose(f); + files = files->next; + } + + if (lastline) { + sp->len = 0; + return (0); + } + + /* + * Use fgetline so that we can handle essentially infinite input + * data. Can't use the pointer into the stdio buffer as the process + * space because the ungetc() can cause it to move. + */ + p = fgetline(f, &len); + if (ferror(f)) + err(FATAL, "%s: %s", fname, strerror(errno ? errno : EIO)); + cspace(sp, p, len, spflag); + + linenum++; + /* Advance to next non-empty file */ + while ((c = getc(f)) == EOF) { + (void)fclose(f); + files = files->next; + if (files == NULL) { + lastline = 1; + return (1); + } + if (files->fname == NULL) { + f = stdin; + fname = "stdin"; + } else { + fname = files->fname; + if ((f = fopen(fname, "r")) == NULL) + err(FATAL, "%s: %s", fname, strerror(errno)); + } + } + (void)ungetc(c, f); + return (1); +} + +/* + * Add a compilation unit to the linked list + */ +static void +add_compunit(type, s) + enum e_cut type; + char *s; +{ + struct s_compunit *cu; + + cu = xmalloc(sizeof(struct s_compunit)); + cu->type = type; + cu->s = s; + cu->next = NULL; + *cu_nextp = cu; + cu_nextp = &cu->next; +} + +/* + * Add a file to the linked list + */ +static void +add_file(s) + char *s; +{ + struct s_flist *fp; + + fp = xmalloc(sizeof(struct s_flist)); + fp->next = NULL; + *fl_nextp = fp; + fp->fname = s; + fl_nextp = &fp->next; +} diff --git a/usr.bin/sed/math.sed b/usr.bin/sed/math.sed new file mode 100644 index 0000000000..7e5bff7585 --- /dev/null +++ b/usr.bin/sed/math.sed @@ -0,0 +1,163 @@ +# +# @(#)math.sed 5.1 (Berkeley) 2/20/91 +# +# Addition and multiplication in sed. +# ++ for a limited time only do (expr) too!!! +# +# Kevin S Braunsdorf, PUCC UNIX Group, ksb@cc.purdue.edu. +# +# Ex: +# echo "4+7*3" | sed -f %f + +# make sure the expression is well formed +s/[ ]//g +/[+*\/-]$/{ + a\ + poorly formed expression, operator on the end + q +} +/^[+*\/]/{ + a\ + poorly formed expression, leading operator + q +} + +# fill hold space with done token +x +s/^.*/done/ +x + +# main loop, process operators (*, + and () ) +: loop +/^\+/{ + s/// + b loop +} +/^\(.*\)(\([^)]*\))\(.*\)$/{ + H + s//\2/ + x + s/^\(.*\)\n\(.*\)(\([^()]*\))\(.*\)$/()\2@\4@\1/ + x + b loop +} +/^[0-9]*\*/b mul +/^\([0-9]*\)\+\([0-9+*]*\*[0-9]*\)$/{ + s//\2+\1/ + b loop +} +/^[0-9]*\+/{ + s/$/=/ + b add +} +x +/^done$/{ + x + p + d +} +/^()/{ + s/// + x + G + s/\(.*\)\n\([^@]*\)@\([^@]*\)@\(.*\)/\2\1\3/ + x + s/[^@]*@[^@]*@\(.*\)/\1/ + x + b loop +} +i\ +help, stack problem +p +x +p +q + +# turn mul into add until 1*x -> x +: mul +/^0*1\*/{ + s/// + b loop +} +/^\([0-9]*\)0\*/{ + s/^\([0-9]*\)0\*\([0-9]*\)/\1*\20/ + b mul +} +s/^\([0-9]*\)1\*/\10*/ +s/^\([0-9]*\)2\*/\11*/ +s/^\([0-9]*\)3\*/\12*/ +s/^\([0-9]*\)4\*/\13*/ +s/^\([0-9]*\)5\*/\14*/ +s/^\([0-9]*\)6\*/\15*/ +s/^\([0-9]*\)7\*/\16*/ +s/^\([0-9]*\)8\*/\17*/ +s/^\([0-9]*\)9\*/\18*/ +s/\*\([0-9*]*\)/*\1+\1/ +b mul + +# get rid of a plus term until 0+x -> x +: add +/^\+\([0-9+*]*\)=/{ + s//\1/ + b loop +} +/^\([0-9*]*\)\+=/{ + s//\1/ + b loop +} +/^\([0-9]*\)\+\([0-9*+]*\)\+=/{ + s//\2+\1/ + b loop +} +/^\([0-9]*\)0\+\([0-9]*\)\([0-9]\)=/{ + s//\1+\2=\3/ + b add +} +/^\([0-9]*\)\([0-9]\)\+\([0-9]*\)0=/{ + s//\1+\3=\2/ + b add +} +/^\([0-9]*\)0\+\([0-9*+]*\)\+\([0-9]*\)\([0-9]\)=/{ + s//\1+\2+\3=\4/ + b add +} +/^\([0-9]*\)\([0-9]\)\+\([0-9*+]*\)\+\([0-9]*\)0=/{ + s//\1+\3+\4=\2/ + b add +} +s/^\([0-9]*\)1\+/\10+/ +s/^\([0-9]*\)2\+/\11+/ +s/^\([0-9]*\)3\+/\12+/ +s/^\([0-9]*\)4\+/\13+/ +s/^\([0-9]*\)5\+/\14+/ +s/^\([0-9]*\)6\+/\15+/ +s/^\([0-9]*\)7\+/\16+/ +s/^\([0-9]*\)8\+/\17+/ +s/^\([0-9]*\)9\+/\18+/ + +s/9=\([0-9]*\)$/_=\1/ +s/8=\([0-9]*\)$/9=\1/ +s/7=\([0-9]*\)$/8=\1/ +s/6=\([0-9]*\)$/7=\1/ +s/5=\([0-9]*\)$/6=\1/ +s/4=\([0-9]*\)$/5=\1/ +s/3=\([0-9]*\)$/4=\1/ +s/2=\([0-9]*\)$/3=\1/ +s/1=\([0-9]*\)$/2=\1/ +/_/{ + s//_0/ + : inc + s/9_/_0/ + s/8_/9/ + s/7_/8/ + s/6_/7/ + s/5_/6/ + s/4_/5/ + s/3_/4/ + s/2_/3/ + s/1_/2/ + s/0_/1/ + s/\+_/+1/ + /_/b inc +} +b add diff --git a/usr.bin/sed/misc.c b/usr.bin/sed/misc.c new file mode 100644 index 0000000000..807821334c --- /dev/null +++ b/usr.bin/sed/misc.c @@ -0,0 +1,141 @@ +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)misc.c 5.3 (Berkeley) 8/26/92"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "defs.h" +#include "extern.h" + +/* + * malloc with result test + */ +void * +xmalloc(size) + u_int size; +{ + void *p; + + if ((p = malloc(size)) == NULL) + err(FATAL, "%s", strerror(errno)); + return (p); +} + +/* + * realloc with result test + */ +void * +xrealloc(p, size) + void *p; + u_int size; +{ + if (p == NULL) /* Compatibility hack. */ + return (xmalloc(size)); + + if ((p = realloc(p, size)) == NULL) + err(FATAL, "%s", strerror(errno)); + return (p); +} + +/* + * Return a string for a regular expression error passed. This is a overkill, + * because of the silly semantics of regerror (we can never know the size of + * the buffer). + */ +char * +strregerror(errcode, preg) + int errcode; + regex_t *preg; +{ + static char *oe; + size_t s; + + if (oe != NULL) + free(oe); + s = regerror(errcode, preg, "", 0); + oe = xmalloc(s); + (void)regerror(errcode, preg, oe, s); + return (oe); +} + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif +/* + * Error reporting function + */ +void +#if __STDC__ +err(int severity, const char *fmt, ...) +#else +err(severity, fmt, va_alist) + int severity; + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "sed: "); + switch (severity) { + case WARNING: + case COMPILE: + (void)fprintf(stderr, "%lu: %s: ", linenum, fname); + } + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + if (severity == WARNING) + return; + exit(1); + /* NOTREACHED */ +} diff --git a/usr.bin/sed/process.c b/usr.bin/sed/process.c new file mode 100644 index 0000000000..f1a4e9e9f2 --- /dev/null +++ b/usr.bin/sed/process.c @@ -0,0 +1,614 @@ +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)process.c 5.10 (Berkeley) 12/2/92"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/uio.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "defs.h" +#include "extern.h" + +static SPACE HS = {""}, PS, SS; +#define pd PS.deleted +#define ps PS.space +#define psl PS.len +#define hs HS.space +#define hsl HS.len + +static inline int applies __P((struct s_command *)); +static void flush_appends __P((void)); +static void lputs __P((char *)); +static inline int regexec_e __P((regex_t *, const char *, int, int)); +static void regsub __P((SPACE *, char *, char *)); +static int substitute __P((struct s_command *)); + +struct s_appends *appends; /* Array of pointers to strings to append. */ +static int appendx; /* Index into appends array. */ +int appendnum; /* Size of appends array. */ + +static int lastaddr; /* Set by applies if last address of a range. */ +static int sdone; /* If any substitutes since last line input. */ + /* Iov structure for 'w' commands. */ +static struct iovec iov[2] = { NULL, 0, "\n", 1 }; + +static regex_t *defpreg; +size_t maxnsub; +regmatch_t *match; + +void +process() +{ + struct s_command *cp; + SPACE tspace; + size_t len; + int r; + char oldc, *p; + + for (linenum = 0; mf_fgets(&PS, REPLACE);) { + pd = 0; + cp = prog; +redirect: + while (cp != NULL) { + if (!applies(cp)) { + cp = cp->next; + continue; + } + switch (cp->code) { + case '{': + cp = cp->u.c; + goto redirect; + case 'a': + if (appendx >= appendnum) + appends = xrealloc(appends, + sizeof(struct s_appends) * + (appendnum *= 2)); + appends[appendx].type = AP_STRING; + appends[appendx].s = cp->t; + appendx++; + break; + case 'b': + cp = cp->u.c; + goto redirect; + case 'c': + pd = 1; + psl = 0; + if (cp->a2 == NULL || lastaddr) + (void)printf("%s", cp->t); + break; + case 'd': + pd = 1; + goto new; + case 'D': + if (pd) + goto new; + if ((p = strchr(ps, '\n')) == NULL) + pd = 1; + else { + psl -= (p - ps) - 1; + memmove(ps, p + 1, psl); + } + goto new; + case 'g': + cspace(&PS, hs, hsl, REPLACE); + break; + case 'G': + cspace(&PS, hs, hsl, APPENDNL); + break; + case 'h': + cspace(&HS, ps, psl, REPLACE); + break; + case 'H': + cspace(&HS, ps, psl, APPENDNL); + break; + case 'i': + (void)printf("%s", cp->t); + break; + case 'l': + lputs(ps); + break; + case 'n': + if (!nflag && !pd) + (void)printf("%s\n", ps); + flush_appends(); + r = mf_fgets(&PS, REPLACE); +#ifdef HISTORIC_PRACTICE + if (!r) + exit(0); +#endif + pd = 0; + break; + case 'N': + flush_appends(); + if (!mf_fgets(&PS, APPENDNL)) { + if (!nflag && !pd) + (void)printf("%s\n", ps); + exit(0); + } + break; + case 'p': + if (pd) + break; + (void)printf("%s\n", ps); + break; + case 'P': + if (pd) + break; + if ((p = strchr(ps, '\n')) != NULL) { + oldc = *p; + *p = '\0'; + } + (void)printf("%s\n", ps); + if (p != NULL) + *p = oldc; + break; + case 'q': + if (!nflag && !pd) + (void)printf("%s\n", ps); + flush_appends(); + exit(0); + case 'r': + if (appendx >= appendnum) + appends = xrealloc(appends, + sizeof(struct s_appends) * + (appendnum *= 2)); + appends[appendx].type = AP_FILE; + appends[appendx].s = cp->t; + appendx++; + break; + case 's': + sdone |= substitute(cp); + break; + case 't': + if (sdone) { + sdone = 0; + cp = cp->u.c; + goto redirect; + } + break; + case 'w': + if (pd) + break; + if (cp->u.fd == -1 && (cp->u.fd = open(cp->t, + O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, + DEFFILEMODE)) == -1) + err(FATAL, "%s: %s\n", + cp->t, strerror(errno)); + iov[0].iov_base = ps; + iov[0].iov_len = psl; + if (writev(cp->u.fd, iov, 2) != psl + 1) + err(FATAL, "%s: %s\n", + cp->t, strerror(errno)); + break; + case 'x': + tspace = PS; + PS = HS; + HS = tspace; + break; + case 'y': + if (pd) + break; + for (p = ps, len = psl; len--; ++p) + *p = cp->u.y[*p]; + break; + case ':': + case '}': + break; + case '=': + (void)printf("%lu\n", linenum); + } + cp = cp->next; + } /* for all cp */ + +new: if (!nflag && !pd) + (void)printf("%s\n", ps); + flush_appends(); + } /* for all lines */ +} + +/* + * TRUE if the address passed matches the current program state + * (lastline, linenumber, ps). + */ +#define MATCH(a) \ + (a)->type == AT_RE ? regexec_e((a)->u.r, ps, 0, 1) : \ + (a)->type == AT_LINE ? linenum == (a)->u.l : lastline + +/* + * Return TRUE if the command applies to the current line. Sets the inrange + * flag to process ranges. Interprets the non-select (``!'') flag. + */ +static inline int +applies(cp) + struct s_command *cp; +{ + int r; + + lastaddr = 0; + if (cp->a1 == NULL && cp->a2 == NULL) + r = 1; + else if (cp->a2) + if (cp->inrange) { + if (MATCH(cp->a2)) { + cp->inrange = 0; + lastaddr = 1; + } + r = 1; + } else if (MATCH(cp->a1)) { + /* + * If the second address is a number less than or + * equal to the line number first selected, only + * one line shall be selected. + * -- POSIX 1003.2 + */ + if (cp->a2->type == AT_LINE && + linenum >= cp->a2->u.l) + lastaddr = 1; + else + cp->inrange = 1; + r = 1; + } else + r = 0; + else + r = MATCH(cp->a1); + return (cp->nonsel ? ! r : r); +} + +/* + * substitute -- + * Do substitutions in the pattern space. Currently, we build a + * copy of the new pattern space in the substitute space structure + * and then swap them. + */ +static int +substitute(cp) + struct s_command *cp; +{ + SPACE tspace; + regex_t *re; + size_t re_off; + size_t re_eoff; + int n; + char *s; + char *eos; + + s = ps; + eos = s + strlen(s); + re = cp->u.s->re; + if (re == NULL) { + if (defpreg != NULL && cp->u.s->maxbref > defpreg->re_nsub) { + linenum = cp->u.s->linenum; + err(COMPILE, "\\%d not defined in the RE", + cp->u.s->maxbref); + } + } + if (!regexec_e(re, s, 0, 0)) + return (0); + + SS.len = 0; /* Clean substitute space. */ + n = cp->u.s->n; + switch (n) { + case 0: /* Global */ + do { + /* Locate start of replaced string. */ + re_off = match[0].rm_so; + re_eoff = match[0].rm_eo; + /* Copy leading retained string. */ + cspace(&SS, s, re_off, APPEND); + /* Add in regular expression. */ + regsub(&SS, s, cp->u.s->new); + /* Move past this match. */ + s += match[0].rm_eo; + } while(*s && re_eoff && regexec_e(re, s, REG_NOTBOL, 0)); + if (eos - s > 0 && !re_eoff) + err(FATAL, "infinite substitution loop"); + /* Copy trailing retained string. */ + cspace(&SS, s, strlen(s), APPEND); + break; + default: /* Nth occurrence */ + while (--n) { + s += match[0].rm_eo; + if (!regexec_e(re, s, REG_NOTBOL, 0)) + return (0); + } + /* FALLTHROUGH */ + case 1: /* 1st occurrence */ + /* Locate start of replaced string. */ + re_off = match[0].rm_so + (s - ps); + /* Copy leading retained string. */ + cspace(&SS, ps, re_off, APPEND); + /* Add in regular expression. */ + regsub(&SS, s, cp->u.s->new); + /* Copy trailing retained string. */ + s += match[0].rm_eo; + cspace(&SS, s, strlen(s), APPEND); + break; + } + + /* + * Swap the substitute space and the pattern space, and make sure + * that any leftover pointers into stdio memory get lost. + */ + tspace = PS; + PS = SS; + SS = tspace; + SS.space = SS.back; + + /* Handle the 'p' flag. */ + if (cp->u.s->p) + (void)printf("%s\n", ps); + + /* Handle the 'w' flag. */ + if (cp->u.s->wfile && !pd) { + if (cp->u.s->wfd == -1 && (cp->u.s->wfd = open(cp->u.s->wfile, + O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1) + err(FATAL, "%s: %s\n", cp->u.s->wfile, strerror(errno)); + iov[0].iov_base = ps; + iov[0].iov_len = psl; + if (writev(cp->u.s->wfd, iov, 2) != psl + 1) + err(FATAL, "%s: %s\n", cp->u.s->wfile, strerror(errno)); + } + return (1); +} + +/* + * Flush append requests. Always called before reading a line, + * therefore it also resets the substitution done (sdone) flag. + */ +static void +flush_appends() +{ + FILE *f; + int count, i; + char buf[8 * 1024]; + + for (i = 0; i < appendx; i++) + switch (appends[i].type) { + case AP_STRING: + (void)printf("%s", appends[i].s); + break; + case AP_FILE: + /* + * Read files probably shouldn't be cached. Since + * it's not an error to read a non-existent file, + * it's possible that another program is interacting + * with the sed script through the file system. It + * would be truly bizarre, but possible. It's probably + * not that big a performance win, anyhow. + */ + if ((f = fopen(appends[i].s, "r")) == NULL) + break; + while (count = fread(buf, 1, sizeof(buf), f)) + (void)fwrite(buf, 1, count, stdout); + (void)fclose(f); + break; + } + if (ferror(stdout)) + err(FATAL, "stdout: %s", strerror(errno ? errno : EIO)); + appendx = sdone = 0; +} + +static void +lputs(s) + register char *s; +{ + register int count; + register char *escapes, *p; + struct winsize win; + static int termwidth = -1; + + if (termwidth == -1) + if (p = getenv("COLUMNS")) + termwidth = atoi(p); + else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 && + win.ws_col > 0) + termwidth = win.ws_col; + else + termwidth = 60; + + for (count = 0; *s; ++s) { + if (count >= termwidth) { + (void)printf("\\\n"); + count = 0; + } + if (isascii(*s) && isprint(*s) && *s != '\\') { + (void)putchar(*s); + count++; + } else { + escapes = "\\\a\b\f\n\r\t\v"; + (void)putchar('\\'); + if (p = strchr(escapes, *s)) { + (void)putchar("\\abfnrtv"[p - escapes]); + count += 2; + } else { + (void)printf("%03o", (u_char)*s); + count += 4; + } + } + } + (void)putchar('$'); + (void)putchar('\n'); + if (ferror(stdout)) + err(FATAL, "stdout: %s", strerror(errno ? errno : EIO)); +} + +static inline int +regexec_e(preg, string, eflags, nomatch) + regex_t *preg; + const char *string; + int eflags, nomatch; +{ + int eval; + + if (preg == NULL) { + if (defpreg == NULL) + err(FATAL, "first RE may not be empty"); + } else + defpreg = preg; + + eval = regexec(defpreg, string, + nomatch ? 0 : maxnsub + 1, match, eflags); + switch(eval) { + case 0: + return (1); + case REG_NOMATCH: + return (0); + } + err(FATAL, "RE error: %s", strregerror(eval, defpreg)); + /* NOTREACHED */ +} + +/* + * regsub - perform substitutions after a regexp match + * Based on a routine by Henry Spencer + */ +static void +regsub(sp, string, src) + SPACE *sp; + char *string, *src; +{ + register int len, no; + register char c, *dst; + +#define NEEDSP(reqlen) \ + if (sp->len >= sp->blen - (reqlen) - 1) { \ + sp->blen += (reqlen) + 1024; \ + sp->space = sp->back = xrealloc(sp->back, sp->blen); \ + dst = sp->space + sp->len; \ + } + + dst = sp->space + sp->len; + while ((c = *src++) != '\0') { + if (c == '&') + no = 0; + else if (c == '\\' && isdigit(*src)) + no = *src++ - '0'; + else + no = -1; + if (no < 0) { /* Ordinary character. */ + if (c == '\\' && (*src == '\\' || *src == '&')) + c = *src++; + NEEDSP(1); + *dst++ = c; + ++sp->len; + } else if (match[no].rm_so != -1 && match[no].rm_eo != -1) { + len = match[no].rm_eo - match[no].rm_so; + NEEDSP(len); + memmove(dst, string + match[no].rm_so, len); + dst += len; + sp->len += len; + } + } + NEEDSP(1); + *dst = '\0'; +} + +/* + * aspace -- + * Append the source space to the destination space, allocating new + * space as necessary. + */ +void +cspace(sp, p, len, spflag) + SPACE *sp; + char *p; + size_t len; + enum e_spflag spflag; +{ + size_t tlen; + + /* + * Make sure SPACE has enough memory and ramp up quickly. Appends + * need two extra bytes, one for the newline, one for a terminating + * NULL. + */ +/* tlen = sp->len + len + spflag == APPENDNL ? 2 : 1; */ + tlen = sp->len + len + (spflag == APPENDNL ? 2 : 1); /* XXX */ + if (tlen > sp->blen) { + sp->blen = tlen + 1024; + sp->space = sp->back = xrealloc(sp->back, sp->blen); + } + + if (spflag == APPENDNL) + sp->space[sp->len++] = '\n'; + else if (spflag == REPLACE) + sp->len = 0; + + memmove(sp->space + sp->len, p, len); + sp->space[sp->len += len] = '\0'; +} + +/* + * Close all cached opened files and report any errors + */ +void +cfclose(cp, end) + register struct s_command *cp, *end; +{ + + for (; cp != end; cp = cp->next) + switch(cp->code) { + case 's': + if (cp->u.s->wfd != -1 && close(cp->u.s->wfd)) + err(FATAL, + "%s: %s", cp->u.s->wfile, strerror(errno)); + cp->u.s->wfd = -1; + break; + case 'w': + if (cp->u.fd != -1 && close(cp->u.fd)) + err(FATAL, "%s: %s", cp->t, strerror(errno)); + cp->u.fd = -1; + break; + case '{': + cfclose(cp->u.c, cp->next); + break; + } +} diff --git a/usr.bin/sed/regex.c b/usr.bin/sed/regex.c deleted file mode 100644 index a24d05cec8..0000000000 --- a/usr.bin/sed/regex.c +++ /dev/null @@ -1,2781 +0,0 @@ -/* Extended regular expression matching and search library. - Copyright (C) 1985, 1989-90 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -/* To test, compile with -Dtest. This Dtestable feature turns this into - a self-contained program which reads a pattern, describes how it - compiles, then reads a string and searches for it. - - On the other hand, if you compile with both -Dtest and -Dcanned you - can run some tests we've already thought of. */ - -/* AIX requires the alloca decl to be the first thing in the file. */ -#ifdef __GNUC__ -#define alloca __builtin_alloca -#else -#ifdef sparc -#include <alloca.h> -#else -#ifdef _AIX -#pragma alloca -#else -char *alloca (); -#endif -#endif -#endif - -#ifdef emacs - -/* The `emacs' switch turns on certain special matching commands - that make sense only in emacs. */ - -#include "config.h" -#include "lisp.h" -#include "buffer.h" -#include "syntax.h" - -#else /* not emacs */ - -#if defined (USG) || defined (STDC_HEADERS) -#ifndef BSTRING -#include <string.h> -#define bcopy(s,d,n) memcpy((d),(s),(n)) -#define bcmp(s1,s2,n) memcmp((s1),(s2),(n)) -#define bzero(s,n) memset((s),0,(n)) -#endif -#endif - -#ifdef STDC_HEADERS -#include <stdlib.h> -#else -char *malloc (); -char *realloc (); -#endif - -/* Define the syntax stuff, so we can do the \<, \>, etc. */ - -/* This must be nonzero for the wordchar and notwordchar pattern - commands in re_match_2. */ -#ifndef Sword -#define Sword 1 -#endif - -#define SYNTAX(c) re_syntax_table[c] - - -#ifdef SYNTAX_TABLE - -char *re_syntax_table; - -#else /* not SYNTAX_TABLE */ - -static char re_syntax_table[256]; - - -static void -init_syntax_once () -{ - register int c; - static int done = 0; - - if (done) - return; - - bzero (re_syntax_table, sizeof re_syntax_table); - - for (c = 'a'; c <= 'z'; c++) - re_syntax_table[c] = Sword; - - for (c = 'A'; c <= 'Z'; c++) - re_syntax_table[c] = Sword; - - for (c = '0'; c <= '9'; c++) - re_syntax_table[c] = Sword; - - done = 1; -} - -#endif /* SYNTAX_TABLE */ -#endif /* emacs */ - -/* We write fatal error messages on standard error. */ -#include <stdio.h> - -/* isalpha(3) etc. are used for the character classes. */ -#include <ctype.h> -/* Sequents are missing isgraph. */ -#ifndef isgraph -#define isgraph(c) (isprint((c)) && !isspace((c))) -#endif - -/* Get the interface, including the syntax bits. */ -#include "regex.h" - - -/* These are the command codes that appear in compiled regular - expressions, one per byte. Some command codes are followed by - argument bytes. A command code can specify any interpretation - whatsoever for its arguments. Zero-bytes may appear in the compiled - regular expression. - - The value of `exactn' is needed in search.c (search_buffer) in emacs. - So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of - `exactn' we use here must also be 1. */ - -enum regexpcode - { - unused=0, - exactn=1, /* Followed by one byte giving n, then by n literal bytes. */ - begline, /* Fail unless at beginning of line. */ - endline, /* Fail unless at end of line. */ - jump, /* Followed by two bytes giving relative address to jump to. */ - on_failure_jump, /* Followed by two bytes giving relative address of - place to resume at in case of failure. */ - finalize_jump, /* Throw away latest failure point and then jump to - address. */ - maybe_finalize_jump, /* Like jump but finalize if safe to do so. - This is used to jump back to the beginning - of a repeat. If the command that follows - this jump is clearly incompatible with the - one at the beginning of the repeat, such that - we can be sure that there is no use backtracking - out of repetitions already completed, - then we finalize. */ - dummy_failure_jump, /* Jump, and push a dummy failure point. This - failure point will be thrown away if an attempt - is made to use it for a failure. A + construct - makes this before the first repeat. Also - use it as an intermediary kind of jump when - compiling an or construct. */ - succeed_n, /* Used like on_failure_jump except has to succeed n times; - then gets turned into an on_failure_jump. The relative - address following it is useless until then. The - address is followed by two bytes containing n. */ - jump_n, /* Similar to jump, but jump n times only; also the relative - address following is in turn followed by yet two more bytes - containing n. */ - set_number_at, /* Set the following relative location to the - subsequent number. */ - anychar, /* Matches any (more or less) one character. */ - charset, /* Matches any one char belonging to specified set. - First following byte is number of bitmap bytes. - Then come bytes for a bitmap saying which chars are in. - Bits in each byte are ordered low-bit-first. - A character is in the set if its bit is 1. - A character too large to have a bit in the map - is automatically not in the set. */ - charset_not, /* Same parameters as charset, but match any character - that is not one of those specified. */ - start_memory, /* Start remembering the text that is matched, for - storing in a memory register. Followed by one - byte containing the register number. Register numbers - must be in the range 0 through RE_NREGS. */ - stop_memory, /* Stop remembering the text that is matched - and store it in a memory register. Followed by - one byte containing the register number. Register - numbers must be in the range 0 through RE_NREGS. */ - duplicate, /* Match a duplicate of something remembered. - Followed by one byte containing the index of the memory - register. */ - before_dot, /* Succeeds if before point. */ - at_dot, /* Succeeds if at point. */ - after_dot, /* Succeeds if after point. */ - begbuf, /* Succeeds if at beginning of buffer. */ - endbuf, /* Succeeds if at end of buffer. */ - wordchar, /* Matches any word-constituent character. */ - notwordchar, /* Matches any char that is not a word-constituent. */ - wordbeg, /* Succeeds if at word beginning. */ - wordend, /* Succeeds if at word end. */ - wordbound, /* Succeeds if at a word boundary. */ - notwordbound,/* Succeeds if not at a word boundary. */ - syntaxspec, /* Matches any character whose syntax is specified. - followed by a byte which contains a syntax code, - e.g., Sword. */ - notsyntaxspec /* Matches any character whose syntax differs from - that specified. */ - }; - - -/* Number of failure points to allocate space for initially, - when matching. If this number is exceeded, more space is allocated, - so it is not a hard limit. */ - -#ifndef NFAILURES -#define NFAILURES 80 -#endif - -#ifdef CHAR_UNSIGNED -#define SIGN_EXTEND_CHAR(c) ((c)>(char)127?(c)-256:(c)) /* for IBM RT */ -#endif -#ifndef SIGN_EXTEND_CHAR -#define SIGN_EXTEND_CHAR(x) (x) -#endif - - -/* Store NUMBER in two contiguous bytes starting at DESTINATION. */ -#define STORE_NUMBER(destination, number) \ - { (destination)[0] = (number) & 0377; \ - (destination)[1] = (number) >> 8; } - -/* Same as STORE_NUMBER, except increment the destination pointer to - the byte after where the number is stored. Watch out that values for - DESTINATION such as p + 1 won't work, whereas p will. */ -#define STORE_NUMBER_AND_INCR(destination, number) \ - { STORE_NUMBER(destination, number); \ - (destination) += 2; } - - -/* Put into DESTINATION a number stored in two contingous bytes starting - at SOURCE. */ -#define EXTRACT_NUMBER(destination, source) \ - { (destination) = *(source) & 0377; \ - (destination) += SIGN_EXTEND_CHAR (*(char *)((source) + 1)) << 8; } - -/* Same as EXTRACT_NUMBER, except increment the pointer for source to - point to second byte of SOURCE. Note that SOURCE has to be a value - such as p, not, e.g., p + 1. */ -#define EXTRACT_NUMBER_AND_INCR(destination, source) \ - { EXTRACT_NUMBER (destination, source); \ - (source) += 2; } - - -/* Specify the precise syntax of regexps for compilation. This provides - for compatibility for various utilities which historically have - different, incompatible syntaxes. - - The argument SYNTAX is a bit-mask comprised of the various bits - defined in regex.h. */ - -int -re_set_syntax (syntax) - int syntax; -{ - int ret; - - ret = obscure_syntax; - obscure_syntax = syntax; - return ret; -} - -/* Set by re_set_syntax to the current regexp syntax to recognize. */ -int obscure_syntax = 0; - - - -/* Macros for re_compile_pattern, which is found below these definitions. */ - -#define CHAR_CLASS_MAX_LENGTH 6 - -/* Fetch the next character in the uncompiled pattern, translating it if - necessary. */ -#define PATFETCH(c) \ - {if (p == pend) goto end_of_pattern; \ - c = * (unsigned char *) p++; \ - if (translate) c = translate[c]; } - -/* Fetch the next character in the uncompiled pattern, with no - translation. */ -#define PATFETCH_RAW(c) \ - {if (p == pend) goto end_of_pattern; \ - c = * (unsigned char *) p++; } - -#define PATUNFETCH p-- - - -/* If the buffer isn't allocated when it comes in, use this. */ -#define INIT_BUF_SIZE 28 - -/* Make sure we have at least N more bytes of space in buffer. */ -#define GET_BUFFER_SPACE(n) \ - { \ - while (b - bufp->buffer + (n) >= bufp->allocated) \ - EXTEND_BUFFER; \ - } - -/* Make sure we have one more byte of buffer space and then add CH to it. */ -#define BUFPUSH(ch) \ - { \ - GET_BUFFER_SPACE (1); \ - *b++ = (char) (ch); \ - } - -/* Extend the buffer by twice its current size via reallociation and - reset the pointers that pointed into the old allocation to point to - the correct places in the new allocation. If extending the buffer - results in it being larger than 1 << 16, then flag memory exhausted. */ -#define EXTEND_BUFFER \ - { char *old_buffer = bufp->buffer; \ - if (bufp->allocated == (1L<<16)) goto too_big; \ - bufp->allocated *= 2; \ - if (bufp->allocated > (1L<<16)) bufp->allocated = (1L<<16); \ - bufp->buffer = (char *) realloc (bufp->buffer, bufp->allocated); \ - if (bufp->buffer == 0) \ - goto memory_exhausted; \ - b = (b - old_buffer) + bufp->buffer; \ - if (fixup_jump) \ - fixup_jump = (fixup_jump - old_buffer) + bufp->buffer; \ - if (laststart) \ - laststart = (laststart - old_buffer) + bufp->buffer; \ - begalt = (begalt - old_buffer) + bufp->buffer; \ - if (pending_exact) \ - pending_exact = (pending_exact - old_buffer) + bufp->buffer; \ - } - -/* Set the bit for character C in a character set list. */ -#define SET_LIST_BIT(c) (b[(c) / BYTEWIDTH] |= 1 << ((c) % BYTEWIDTH)) - -/* Get the next unsigned number in the uncompiled pattern. */ -#define GET_UNSIGNED_NUMBER(num) \ - { if (p != pend) \ - { \ - PATFETCH (c); \ - while (isdigit (c)) \ - { \ - if (num < 0) \ - num = 0; \ - num = num * 10 + c - '0'; \ - if (p == pend) \ - break; \ - PATFETCH (c); \ - } \ - } \ - } - -/* Subroutines for re_compile_pattern. */ -static void store_jump (), insert_jump (), store_jump_n (), - insert_jump_n (), insert_op_2 (); - - -/* re_compile_pattern takes a regular-expression string - and converts it into a buffer full of byte commands for matching. - - PATTERN is the address of the pattern string - SIZE is the length of it. - BUFP is a struct re_pattern_buffer * which points to the info - on where to store the byte commands. - This structure contains a char * which points to the - actual space, which should have been obtained with malloc. - re_compile_pattern may use realloc to grow the buffer space. - - The number of bytes of commands can be found out by looking in - the `struct re_pattern_buffer' that bufp pointed to, after - re_compile_pattern returns. */ - -char * -re_compile_pattern (pattern, size, bufp) - char *pattern; - int size; - struct re_pattern_buffer *bufp; -{ - register char *b = bufp->buffer; - register char *p = pattern; - char *pend = pattern + size; - register unsigned c, c1; - char *p1; - unsigned char *translate = (unsigned char *) bufp->translate; - - /* Address of the count-byte of the most recently inserted `exactn' - command. This makes it possible to tell whether a new exact-match - character can be added to that command or requires a new `exactn' - command. */ - - char *pending_exact = 0; - - /* Address of the place where a forward-jump should go to the end of - the containing expression. Each alternative of an `or', except the - last, ends with a forward-jump of this sort. */ - - char *fixup_jump = 0; - - /* Address of start of the most recently finished expression. - This tells postfix * where to find the start of its operand. */ - - char *laststart = 0; - - /* In processing a repeat, 1 means zero matches is allowed. */ - - char zero_times_ok; - - /* In processing a repeat, 1 means many matches is allowed. */ - - char many_times_ok; - - /* Address of beginning of regexp, or inside of last \(. */ - - char *begalt = b; - - /* In processing an interval, at least this many matches must be made. */ - int lower_bound; - - /* In processing an interval, at most this many matches can be made. */ - int upper_bound; - - /* Place in pattern (i.e., the {) to which to go back if the interval - is invalid. */ - char *beg_interval = 0; - - /* Stack of information saved by \( and restored by \). - Four stack elements are pushed by each \(: - First, the value of b. - Second, the value of fixup_jump. - Third, the value of regnum. - Fourth, the value of begalt. */ - - int stackb[40]; - int *stackp = stackb; - int *stacke = stackb + 40; - int *stackt; - - /* Counts \('s as they are encountered. Remembered for the matching \), - where it becomes the register number to put in the stop_memory - command. */ - - int regnum = 1; - - bufp->fastmap_accurate = 0; - -#ifndef emacs -#ifndef SYNTAX_TABLE - /* Initialize the syntax table. */ - init_syntax_once(); -#endif -#endif - - if (bufp->allocated == 0) - { - bufp->allocated = INIT_BUF_SIZE; - if (bufp->buffer) - /* EXTEND_BUFFER loses when bufp->allocated is 0. */ - bufp->buffer = (char *) realloc (bufp->buffer, INIT_BUF_SIZE); - else - /* Caller did not allocate a buffer. Do it for them. */ - bufp->buffer = (char *) malloc (INIT_BUF_SIZE); - if (!bufp->buffer) goto memory_exhausted; - begalt = b = bufp->buffer; - } - - while (p != pend) - { - PATFETCH (c); - - switch (c) - { - case '$': - { - char *p1 = p; - /* When testing what follows the $, - look past the \-constructs that don't consume anything. */ - if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) - while (p1 != pend) - { - if (*p1 == '\\' && p1 + 1 != pend - && (p1[1] == '<' || p1[1] == '>' - || p1[1] == '`' || p1[1] == '\'' -#ifdef emacs - || p1[1] == '=' -#endif - || p1[1] == 'b' || p1[1] == 'B')) - p1 += 2; - else - break; - } - if (obscure_syntax & RE_TIGHT_VBAR) - { - if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS) && p1 != pend) - goto normal_char; - /* Make operand of last vbar end before this `$'. */ - if (fixup_jump) - store_jump (fixup_jump, jump, b); - fixup_jump = 0; - BUFPUSH (endline); - break; - } - /* $ means succeed if at end of line, but only in special contexts. - If validly in the middle of a pattern, it is a normal character. */ - - if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) && p1 != pend) - goto invalid_pattern; - if (p1 == pend || *p1 == '\n' - || (obscure_syntax & RE_CONTEXT_INDEP_OPS) - || (obscure_syntax & RE_NO_BK_PARENS - ? *p1 == ')' - : *p1 == '\\' && p1[1] == ')') - || (obscure_syntax & RE_NO_BK_VBAR - ? *p1 == '|' - : *p1 == '\\' && p1[1] == '|')) - { - BUFPUSH (endline); - break; - } - goto normal_char; - } - case '^': - /* ^ means succeed if at beg of line, but only if no preceding - pattern. */ - - if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) && laststart) - goto invalid_pattern; - if (laststart && p - 2 >= pattern && p[-2] != '\n' - && !(obscure_syntax & RE_CONTEXT_INDEP_OPS)) - goto normal_char; - if (obscure_syntax & RE_TIGHT_VBAR) - { - if (p != pattern + 1 - && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) - goto normal_char; - BUFPUSH (begline); - begalt = b; - } - else - BUFPUSH (begline); - break; - - case '+': - case '?': - if ((obscure_syntax & RE_BK_PLUS_QM) - || (obscure_syntax & RE_LIMITED_OPS)) - goto normal_char; - handle_plus: - case '*': - /* If there is no previous pattern, char not special. */ - if (!laststart) - { - if (obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) - goto invalid_pattern; - else if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) - goto normal_char; - } - /* If there is a sequence of repetition chars, - collapse it down to just one. */ - zero_times_ok = 0; - many_times_ok = 0; - while (1) - { - zero_times_ok |= c != '+'; - many_times_ok |= c != '?'; - if (p == pend) - break; - PATFETCH (c); - if (c == '*') - ; - else if (!(obscure_syntax & RE_BK_PLUS_QM) - && (c == '+' || c == '?')) - ; - else if ((obscure_syntax & RE_BK_PLUS_QM) - && c == '\\') - { - int c1; - PATFETCH (c1); - if (!(c1 == '+' || c1 == '?')) - { - PATUNFETCH; - PATUNFETCH; - break; - } - c = c1; - } - else - { - PATUNFETCH; - break; - } - } - - /* Star, etc. applied to an empty pattern is equivalent - to an empty pattern. */ - if (!laststart) - break; - - /* Now we know whether or not zero matches is allowed - and also whether or not two or more matches is allowed. */ - if (many_times_ok) - { - /* If more than one repetition is allowed, put in at the - end a backward relative jump from b to before the next - jump we're going to put in below (which jumps from - laststart to after this jump). */ - GET_BUFFER_SPACE (3); - store_jump (b, maybe_finalize_jump, laststart - 3); - b += 3; /* Because store_jump put stuff here. */ - } - /* On failure, jump from laststart to b + 3, which will be the - end of the buffer after this jump is inserted. */ - GET_BUFFER_SPACE (3); - insert_jump (on_failure_jump, laststart, b + 3, b); - pending_exact = 0; - b += 3; - if (!zero_times_ok) - { - /* At least one repetition is required, so insert a - dummy-failure before the initial on-failure-jump - instruction of the loop. This effects a skip over that - instruction the first time we hit that loop. */ - GET_BUFFER_SPACE (6); - insert_jump (dummy_failure_jump, laststart, laststart + 6, b); - b += 3; - } - break; - - case '.': - laststart = b; - BUFPUSH (anychar); - break; - - case '[': - if (p == pend) - goto invalid_pattern; - while (b - bufp->buffer - > bufp->allocated - 3 - (1 << BYTEWIDTH) / BYTEWIDTH) - EXTEND_BUFFER; - - laststart = b; - if (*p == '^') - { - BUFPUSH (charset_not); - p++; - } - else - BUFPUSH (charset); - p1 = p; - - BUFPUSH ((1 << BYTEWIDTH) / BYTEWIDTH); - /* Clear the whole map */ - bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH); - - if ((obscure_syntax & RE_HAT_NOT_NEWLINE) && b[-2] == charset_not) - SET_LIST_BIT ('\n'); - - - /* Read in characters and ranges, setting map bits. */ - while (1) - { - /* Don't translate while fetching, in case it's a range bound. - When we set the bit for the character, we translate it. */ - PATFETCH_RAW (c); - - /* If set, \ escapes characters when inside [...]. */ - if ((obscure_syntax & RE_AWK_CLASS_HACK) && c == '\\') - { - PATFETCH(c1); - SET_LIST_BIT (c1); - continue; - } - if (c == ']') - { - if (p == p1 + 1) - { - /* If this is an empty bracket expression. */ - if ((obscure_syntax & RE_NO_EMPTY_BRACKETS) - && p == pend) - goto invalid_pattern; - } - else - /* Stop if this isn't merely a ] inside a bracket - expression, but rather the end of a bracket - expression. */ - break; - } - /* Get a range. */ - if (p[0] == '-' && p[1] != ']') - { - PATFETCH (c1); - /* Don't translate the range bounds while fetching them. */ - PATFETCH_RAW (c1); - - if ((obscure_syntax & RE_NO_EMPTY_RANGES) && c > c1) - goto invalid_pattern; - - if ((obscure_syntax & RE_NO_HYPHEN_RANGE_END) - && c1 == '-' && *p != ']') - goto invalid_pattern; - - while (c <= c1) - { - /* Translate each char that's in the range. */ - if (translate) - SET_LIST_BIT (translate[c]); - else - SET_LIST_BIT (c); - c++; - } - } - else if ((obscure_syntax & RE_CHAR_CLASSES) - && c == '[' && p[0] == ':') - { - /* Longest valid character class word has six characters. */ - char str[CHAR_CLASS_MAX_LENGTH]; - PATFETCH (c); - c1 = 0; - /* If no ] at end. */ - if (p == pend) - goto invalid_pattern; - while (1) - { - /* Don't translate the ``character class'' characters. */ - PATFETCH_RAW (c); - if (c == ':' || c == ']' || p == pend - || c1 == CHAR_CLASS_MAX_LENGTH) - break; - str[c1++] = c; - } - str[c1] = '\0'; - if (p == pend - || c == ']' /* End of the bracket expression. */ - || p[0] != ']' - || p + 1 == pend - || (strcmp (str, "alpha") != 0 - && strcmp (str, "upper") != 0 - && strcmp (str, "lower") != 0 - && strcmp (str, "digit") != 0 - && strcmp (str, "alnum") != 0 - && strcmp (str, "xdigit") != 0 - && strcmp (str, "space") != 0 - && strcmp (str, "print") != 0 - && strcmp (str, "punct") != 0 - && strcmp (str, "graph") != 0 - && strcmp (str, "cntrl") != 0)) - { - /* Undo the ending character, the letters, and leave - the leading : and [ (but set bits for them). */ - c1++; - while (c1--) - PATUNFETCH; - SET_LIST_BIT ('['); - SET_LIST_BIT (':'); - } - else - { - /* The ] at the end of the character class. */ - PATFETCH (c); - if (c != ']') - goto invalid_pattern; - for (c = 0; c < (1 << BYTEWIDTH); c++) - { - if ((strcmp (str, "alpha") == 0 && isalpha (c)) - || (strcmp (str, "upper") == 0 && isupper (c)) - || (strcmp (str, "lower") == 0 && islower (c)) - || (strcmp (str, "digit") == 0 && isdigit (c)) - || (strcmp (str, "alnum") == 0 && isalnum (c)) - || (strcmp (str, "xdigit") == 0 && isxdigit (c)) - || (strcmp (str, "space") == 0 && isspace (c)) - || (strcmp (str, "print") == 0 && isprint (c)) - || (strcmp (str, "punct") == 0 && ispunct (c)) - || (strcmp (str, "graph") == 0 && isgraph (c)) - || (strcmp (str, "cntrl") == 0 && iscntrl (c))) - SET_LIST_BIT (c); - } - } - } - else if (translate) - SET_LIST_BIT (translate[c]); - else - SET_LIST_BIT (c); - } - - /* Discard any character set/class bitmap bytes that are all - 0 at the end of the map. Decrement the map-length byte too. */ - while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) - b[-1]--; - b += b[-1]; - break; - - case '(': - if (! (obscure_syntax & RE_NO_BK_PARENS)) - goto normal_char; - else - goto handle_open; - - case ')': - if (! (obscure_syntax & RE_NO_BK_PARENS)) - goto normal_char; - else - goto handle_close; - - case '\n': - if (! (obscure_syntax & RE_NEWLINE_OR)) - goto normal_char; - else - goto handle_bar; - - case '|': - if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) - && (! laststart || p == pend)) - goto invalid_pattern; - else if (! (obscure_syntax & RE_NO_BK_VBAR)) - goto normal_char; - else - goto handle_bar; - - case '{': - if (! ((obscure_syntax & RE_NO_BK_CURLY_BRACES) - && (obscure_syntax & RE_INTERVALS))) - goto normal_char; - else - goto handle_interval; - - case '\\': - if (p == pend) goto invalid_pattern; - PATFETCH_RAW (c); - switch (c) - { - case '(': - if (obscure_syntax & RE_NO_BK_PARENS) - goto normal_backsl; - handle_open: - if (stackp == stacke) goto nesting_too_deep; - - /* Laststart should point to the start_memory that we are about - to push (unless the pattern has RE_NREGS or more ('s). */ - *stackp++ = b - bufp->buffer; - if (regnum < RE_NREGS) - { - BUFPUSH (start_memory); - BUFPUSH (regnum); - } - *stackp++ = fixup_jump ? fixup_jump - bufp->buffer + 1 : 0; - *stackp++ = regnum++; - *stackp++ = begalt - bufp->buffer; - fixup_jump = 0; - laststart = 0; - begalt = b; - break; - - case ')': - if (obscure_syntax & RE_NO_BK_PARENS) - goto normal_backsl; - handle_close: - if (stackp == stackb) goto unmatched_close; - begalt = *--stackp + bufp->buffer; - if (fixup_jump) - store_jump (fixup_jump, jump, b); - if (stackp[-1] < RE_NREGS) - { - BUFPUSH (stop_memory); - BUFPUSH (stackp[-1]); - } - stackp -= 2; - fixup_jump = *stackp ? *stackp + bufp->buffer - 1 : 0; - laststart = *--stackp + bufp->buffer; - break; - - case '|': - if ((obscure_syntax & RE_LIMITED_OPS) - || (obscure_syntax & RE_NO_BK_VBAR)) - goto normal_backsl; - handle_bar: - if (obscure_syntax & RE_LIMITED_OPS) - goto normal_char; - /* Insert before the previous alternative a jump which - jumps to this alternative if the former fails. */ - GET_BUFFER_SPACE (6); - insert_jump (on_failure_jump, begalt, b + 6, b); - pending_exact = 0; - b += 3; - /* The alternative before the previous alternative has a - jump after it which gets executed if it gets matched. - Adjust that jump so it will jump to the previous - alternative's analogous jump (put in below, which in - turn will jump to the next (if any) alternative's such - jump, etc.). The last such jump jumps to the correct - final destination. */ - if (fixup_jump) - store_jump (fixup_jump, jump, b); - - /* Leave space for a jump after previous alternative---to be - filled in later. */ - fixup_jump = b; - b += 3; - - laststart = 0; - begalt = b; - break; - - case '{': - if (! (obscure_syntax & RE_INTERVALS) - /* Let \{ be a literal. */ - || ((obscure_syntax & RE_INTERVALS) - && (obscure_syntax & RE_NO_BK_CURLY_BRACES)) - /* If it's the string "\{". */ - || (p - 2 == pattern && p == pend)) - goto normal_backsl; - handle_interval: - beg_interval = p - 1; /* The {. */ - /* If there is no previous pattern, this isn't an interval. */ - if (!laststart) - { - if (obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) - goto invalid_pattern; - else - goto normal_backsl; - } - /* It also isn't an interval if not preceded by an re - matching a single character or subexpression, or if - the current type of intervals can't handle back - references and the previous thing is a back reference. */ - if (! (*laststart == anychar - || *laststart == charset - || *laststart == charset_not - || *laststart == start_memory - || (*laststart == exactn && laststart[1] == 1) - || (! (obscure_syntax & RE_NO_BK_REFS) - && *laststart == duplicate))) - { - if (obscure_syntax & RE_NO_BK_CURLY_BRACES) - goto normal_char; - - /* Posix extended syntax is handled in previous - statement; this is for Posix basic syntax. */ - if (obscure_syntax & RE_INTERVALS) - goto invalid_pattern; - - goto normal_backsl; - } - lower_bound = -1; /* So can see if are set. */ - upper_bound = -1; - GET_UNSIGNED_NUMBER (lower_bound); - if (c == ',') - { - GET_UNSIGNED_NUMBER (upper_bound); - if (upper_bound < 0) - upper_bound = RE_DUP_MAX; - } - if (upper_bound < 0) - upper_bound = lower_bound; - if (! (obscure_syntax & RE_NO_BK_CURLY_BRACES)) - { - if (c != '\\') - goto invalid_pattern; - PATFETCH (c); - } - if (c != '}' || lower_bound < 0 || upper_bound > RE_DUP_MAX - || lower_bound > upper_bound - || ((obscure_syntax & RE_NO_BK_CURLY_BRACES) - && p != pend && *p == '{')) - { - if (obscure_syntax & RE_NO_BK_CURLY_BRACES) - goto unfetch_interval; - else - goto invalid_pattern; - } - - /* If upper_bound is zero, don't want to succeed at all; - jump from laststart to b + 3, which will be the end of - the buffer after this jump is inserted. */ - - if (upper_bound == 0) - { - GET_BUFFER_SPACE (3); - insert_jump (jump, laststart, b + 3, b); - b += 3; - } - - /* Otherwise, after lower_bound number of succeeds, jump - to after the jump_n which will be inserted at the end - of the buffer, and insert that jump_n. */ - else - { /* Set to 5 if only one repetition is allowed and - hence no jump_n is inserted at the current end of - the buffer; then only space for the succeed_n is - needed. Otherwise, need space for both the - succeed_n and the jump_n. */ - - unsigned slots_needed = upper_bound == 1 ? 5 : 10; - - GET_BUFFER_SPACE (slots_needed); - /* Initialize the succeed_n to n, even though it will - be set by its attendant set_number_at, because - re_compile_fastmap will need to know it. Jump to - what the end of buffer will be after inserting - this succeed_n and possibly appending a jump_n. */ - insert_jump_n (succeed_n, laststart, b + slots_needed, - b, lower_bound); - b += 5; /* Just increment for the succeed_n here. */ - - /* More than one repetition is allowed, so put in at - the end of the buffer a backward jump from b to the - succeed_n we put in above. By the time we've gotten - to this jump when matching, we'll have matched once - already, so jump back only upper_bound - 1 times. */ - - if (upper_bound > 1) - { - store_jump_n (b, jump_n, laststart, upper_bound - 1); - b += 5; - /* When hit this when matching, reset the - preceding jump_n's n to upper_bound - 1. */ - BUFPUSH (set_number_at); - GET_BUFFER_SPACE (2); - STORE_NUMBER_AND_INCR (b, -5); - STORE_NUMBER_AND_INCR (b, upper_bound - 1); - } - /* When hit this when matching, set the succeed_n's n. */ - GET_BUFFER_SPACE (5); - insert_op_2 (set_number_at, laststart, b, 5, lower_bound); - b += 5; - } - pending_exact = 0; - beg_interval = 0; - break; - - - unfetch_interval: - /* If an invalid interval, match the characters as literals. */ - if (beg_interval) - p = beg_interval; - else - { - fprintf (stderr, - "regex: no interval beginning to which to backtrack.\n"); - exit (1); - } - - beg_interval = 0; - PATFETCH (c); /* normal_char expects char in `c'. */ - goto normal_char; - break; - -#ifdef emacs - case '=': - BUFPUSH (at_dot); - break; - - case 's': - laststart = b; - BUFPUSH (syntaxspec); - PATFETCH (c); - BUFPUSH (syntax_spec_code[c]); - break; - - case 'S': - laststart = b; - BUFPUSH (notsyntaxspec); - PATFETCH (c); - BUFPUSH (syntax_spec_code[c]); - break; -#endif /* emacs */ - - case 'w': - laststart = b; - BUFPUSH (wordchar); - break; - - case 'W': - laststart = b; - BUFPUSH (notwordchar); - break; - - case '<': - BUFPUSH (wordbeg); - break; - - case '>': - BUFPUSH (wordend); - break; - - case 'b': - BUFPUSH (wordbound); - break; - - case 'B': - BUFPUSH (notwordbound); - break; - - case '`': - BUFPUSH (begbuf); - break; - - case '\'': - BUFPUSH (endbuf); - break; - - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (obscure_syntax & RE_NO_BK_REFS) - goto normal_char; - c1 = c - '0'; - if (c1 >= regnum) - { - if (obscure_syntax & RE_NO_EMPTY_BK_REF) - goto invalid_pattern; - else - goto normal_char; - } - /* Can't back reference to a subexpression if inside of it. */ - for (stackt = stackp - 2; stackt > stackb; stackt -= 4) - if (*stackt == c1) - goto normal_char; - laststart = b; - BUFPUSH (duplicate); - BUFPUSH (c1); - break; - - case '+': - case '?': - if (obscure_syntax & RE_BK_PLUS_QM) - goto handle_plus; - else - goto normal_backsl; - break; - - default: - normal_backsl: - /* You might think it would be useful for \ to mean - not to translate; but if we don't translate it - it will never match anything. */ - if (translate) c = translate[c]; - goto normal_char; - } - break; - - default: - normal_char: /* Expects the character in `c'. */ - if (!pending_exact || pending_exact + *pending_exact + 1 != b - || *pending_exact == 0177 || *p == '*' || *p == '^' - || ((obscure_syntax & RE_BK_PLUS_QM) - ? *p == '\\' && (p[1] == '+' || p[1] == '?') - : (*p == '+' || *p == '?')) - || ((obscure_syntax & RE_INTERVALS) - && ((obscure_syntax & RE_NO_BK_CURLY_BRACES) - ? *p == '{' - : (p[0] == '\\' && p[1] == '{')))) - { - laststart = b; - BUFPUSH (exactn); - pending_exact = b; - BUFPUSH (0); - } - BUFPUSH (c); - (*pending_exact)++; - } - } - - if (fixup_jump) - store_jump (fixup_jump, jump, b); - - if (stackp != stackb) goto unmatched_open; - - bufp->used = b - bufp->buffer; - return 0; - - invalid_pattern: - return "Invalid regular expression"; - - unmatched_open: - return "Unmatched \\("; - - unmatched_close: - return "Unmatched \\)"; - - end_of_pattern: - return "Premature end of regular expression"; - - nesting_too_deep: - return "Nesting too deep"; - - too_big: - return "Regular expression too big"; - - memory_exhausted: - return "Memory exhausted"; -} - - -/* Store a jump of the form <OPCODE> <relative address>. - Store in the location FROM a jump operation to jump to relative - address FROM - TO. OPCODE is the opcode to store. */ - -static void -store_jump (from, opcode, to) - char *from, *to; - char opcode; -{ - from[0] = opcode; - STORE_NUMBER(from + 1, to - (from + 3)); -} - - -/* Open up space before char FROM, and insert there a jump to TO. - CURRENT_END gives the end of the storage not in use, so we know - how much data to copy up. OP is the opcode of the jump to insert. - - If you call this function, you must zero out pending_exact. */ - -static void -insert_jump (op, from, to, current_end) - char op; - char *from, *to, *current_end; -{ - register char *pfrom = current_end; /* Copy from here... */ - register char *pto = current_end + 3; /* ...to here. */ - - while (pfrom != from) - *--pto = *--pfrom; - store_jump (from, op, to); -} - - -/* Store a jump of the form <opcode> <relative address> <n> . - - Store in the location FROM a jump operation to jump to relative - address FROM - TO. OPCODE is the opcode to store, N is a number the - jump uses, say, to decide how many times to jump. - - If you call this function, you must zero out pending_exact. */ - -static void -store_jump_n (from, opcode, to, n) - char *from, *to; - char opcode; - unsigned n; -{ - from[0] = opcode; - STORE_NUMBER (from + 1, to - (from + 3)); - STORE_NUMBER (from + 3, n); -} - - -/* Similar to insert_jump, but handles a jump which needs an extra - number to handle minimum and maximum cases. Open up space at - location FROM, and insert there a jump to TO. CURRENT_END gives the - end of the storage in use, so we know how much data to copy up. OP is - the opcode of the jump to insert. - - If you call this function, you must zero out pending_exact. */ - -static void -insert_jump_n (op, from, to, current_end, n) - char op; - char *from, *to, *current_end; - unsigned n; -{ - register char *pfrom = current_end; /* Copy from here... */ - register char *pto = current_end + 5; /* ...to here. */ - - while (pfrom != from) - *--pto = *--pfrom; - store_jump_n (from, op, to, n); -} - - -/* Open up space at location THERE, and insert operation OP followed by - NUM_1 and NUM_2. CURRENT_END gives the end of the storage in use, so - we know how much data to copy up. - - If you call this function, you must zero out pending_exact. */ - -static void -insert_op_2 (op, there, current_end, num_1, num_2) - char op; - char *there, *current_end; - int num_1, num_2; -{ - register char *pfrom = current_end; /* Copy from here... */ - register char *pto = current_end + 5; /* ...to here. */ - - while (pfrom != there) - *--pto = *--pfrom; - - there[0] = op; - STORE_NUMBER (there + 1, num_1); - STORE_NUMBER (there + 3, num_2); -} - - - -/* Given a pattern, compute a fastmap from it. The fastmap records - which of the (1 << BYTEWIDTH) possible characters can start a string - that matches the pattern. This fastmap is used by re_search to skip - quickly over totally implausible text. - - The caller must supply the address of a (1 << BYTEWIDTH)-byte data - area as bufp->fastmap. - The other components of bufp describe the pattern to be used. */ - -void -re_compile_fastmap (bufp) - struct re_pattern_buffer *bufp; -{ - unsigned char *pattern = (unsigned char *) bufp->buffer; - int size = bufp->used; - register char *fastmap = bufp->fastmap; - register unsigned char *p = pattern; - register unsigned char *pend = pattern + size; - register int j, k; - unsigned char *translate = (unsigned char *) bufp->translate; - - unsigned char *stackb[NFAILURES]; - unsigned char **stackp = stackb; - - unsigned is_a_succeed_n; - - bzero (fastmap, (1 << BYTEWIDTH)); - bufp->fastmap_accurate = 1; - bufp->can_be_null = 0; - - while (p) - { - is_a_succeed_n = 0; - if (p == pend) - { - bufp->can_be_null = 1; - break; - } -#ifdef SWITCH_ENUM_BUG - switch ((int) ((enum regexpcode) *p++)) -#else - switch ((enum regexpcode) *p++) -#endif - { - case exactn: - if (translate) - fastmap[translate[p[1]]] = 1; - else - fastmap[p[1]] = 1; - break; - - case begline: - case before_dot: - case at_dot: - case after_dot: - case begbuf: - case endbuf: - case wordbound: - case notwordbound: - case wordbeg: - case wordend: - continue; - - case endline: - if (translate) - fastmap[translate['\n']] = 1; - else - fastmap['\n'] = 1; - - if (bufp->can_be_null != 1) - bufp->can_be_null = 2; - break; - - case jump_n: - case finalize_jump: - case maybe_finalize_jump: - case jump: - case dummy_failure_jump: - EXTRACT_NUMBER_AND_INCR (j, p); - p += j; - if (j > 0) - continue; - /* Jump backward reached implies we just went through - the body of a loop and matched nothing. - Opcode jumped to should be an on_failure_jump. - Just treat it like an ordinary jump. - For a * loop, it has pushed its failure point already; - If so, discard that as redundant. */ - - if ((enum regexpcode) *p != on_failure_jump - && (enum regexpcode) *p != succeed_n) - continue; - p++; - EXTRACT_NUMBER_AND_INCR (j, p); - p += j; - if (stackp != stackb && *stackp == p) - stackp--; - continue; - - case on_failure_jump: - handle_on_failure_jump: - EXTRACT_NUMBER_AND_INCR (j, p); - *++stackp = p + j; - if (is_a_succeed_n) - EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */ - continue; - - case succeed_n: - is_a_succeed_n = 1; - /* Get to the number of times to succeed. */ - p += 2; - /* Increment p past the n for when k != 0. */ - EXTRACT_NUMBER_AND_INCR (k, p); - if (k == 0) - { - p -= 4; - goto handle_on_failure_jump; - } - continue; - - case set_number_at: - p += 4; - continue; - - case start_memory: - case stop_memory: - p++; - continue; - - case duplicate: - bufp->can_be_null = 1; - fastmap['\n'] = 1; - case anychar: - for (j = 0; j < (1 << BYTEWIDTH); j++) - if (j != '\n') - fastmap[j] = 1; - if (bufp->can_be_null) - return; - /* Don't return; check the alternative paths - so we can set can_be_null if appropriate. */ - break; - - case wordchar: - for (j = 0; j < (1 << BYTEWIDTH); j++) - if (SYNTAX (j) == Sword) - fastmap[j] = 1; - break; - - case notwordchar: - for (j = 0; j < (1 << BYTEWIDTH); j++) - if (SYNTAX (j) != Sword) - fastmap[j] = 1; - break; - -#ifdef emacs - case syntaxspec: - k = *p++; - for (j = 0; j < (1 << BYTEWIDTH); j++) - if (SYNTAX (j) == (enum syntaxcode) k) - fastmap[j] = 1; - break; - - case notsyntaxspec: - k = *p++; - for (j = 0; j < (1 << BYTEWIDTH); j++) - if (SYNTAX (j) != (enum syntaxcode) k) - fastmap[j] = 1; - break; -#endif /* not emacs */ - - case charset: - for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) - if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) - { - if (translate) - fastmap[translate[j]] = 1; - else - fastmap[j] = 1; - } - break; - - case charset_not: - /* Chars beyond end of map must be allowed */ - for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++) - if (translate) - fastmap[translate[j]] = 1; - else - fastmap[j] = 1; - - for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) - if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) - { - if (translate) - fastmap[translate[j]] = 1; - else - fastmap[j] = 1; - } - break; - } - - /* Get here means we have successfully found the possible starting - characters of one path of the pattern. We need not follow this - path any farther. Instead, look at the next alternative - remembered in the stack. */ - if (stackp != stackb) - p = *stackp--; - else - break; - } -} - - - -/* Like re_search_2, below, but only one string is specified, and - doesn't let you say where to stop matching. */ - -int -re_search (pbufp, string, size, startpos, range, regs) - struct re_pattern_buffer *pbufp; - char *string; - int size, startpos, range; - struct re_registers *regs; -{ - return re_search_2 (pbufp, (char *) 0, 0, string, size, startpos, range, - regs, size); -} - - -/* Using the compiled pattern in PBUFP->buffer, first tries to match the - virtual concatenation of STRING1 and STRING2, starting first at index - STARTPOS, then at STARTPOS + 1, and so on. RANGE is the number of - places to try before giving up. If RANGE is negative, it searches - backwards, i.e., the starting positions tried are STARTPOS, STARTPOS - - 1, etc. STRING1 and STRING2 are of SIZE1 and SIZE2, respectively. - In REGS, return the indices of the virtual concatenation of STRING1 - and STRING2 that matched the entire PBUFP->buffer and its contained - subexpressions. Do not consider matching one past the index MSTOP in - the virtual concatenation of STRING1 and STRING2. - - The value returned is the position in the strings at which the match - was found, or -1 if no match was found, or -2 if error (such as - failure stack overflow). */ - -int -re_search_2 (pbufp, string1, size1, string2, size2, startpos, range, - regs, mstop) - struct re_pattern_buffer *pbufp; - char *string1, *string2; - int size1, size2; - int startpos; - register int range; - struct re_registers *regs; - int mstop; -{ - register char *fastmap = pbufp->fastmap; - register unsigned char *translate = (unsigned char *) pbufp->translate; - int total_size = size1 + size2; - int endpos = startpos + range; - int val; - - /* Check for out-of-range starting position. */ - if (startpos < 0 || startpos > total_size) - return -1; - - /* Fix up range if it would eventually take startpos outside of the - virtual concatenation of string1 and string2. */ - if (endpos < -1) - range = -1 - startpos; - else if (endpos > total_size) - range = total_size - startpos; - - /* Update the fastmap now if not correct already. */ - if (fastmap && !pbufp->fastmap_accurate) - re_compile_fastmap (pbufp); - - /* If the search isn't to be a backwards one, don't waste time in a - long search for a pattern that says it is anchored. */ - if (pbufp->used > 0 && (enum regexpcode) pbufp->buffer[0] == begbuf - && range > 0) - { - if (startpos > 0) - return -1; - else - range = 1; - } - - while (1) - { - /* If a fastmap is supplied, skip quickly over characters that - cannot possibly be the start of a match. Note, however, that - if the pattern can possibly match the null string, we must - test it at each starting point so that we take the first null - string we get. */ - - if (fastmap && startpos < total_size && pbufp->can_be_null != 1) - { - if (range > 0) /* Searching forwards. */ - { - register int lim = 0; - register unsigned char *p; - int irange = range; - if (startpos < size1 && startpos + range >= size1) - lim = range - (size1 - startpos); - - p = ((unsigned char *) - &(startpos >= size1 ? string2 - size1 : string1)[startpos]); - - while (range > lim && !fastmap[translate - ? translate[*p++] - : *p++]) - range--; - startpos += irange - range; - } - else /* Searching backwards. */ - { - register unsigned char c; - - if (string1 == 0 || startpos >= size1) - c = string2[startpos - size1]; - else - c = string1[startpos]; - - c &= 0xff; - if (translate ? !fastmap[translate[c]] : !fastmap[c]) - goto advance; - } - } - - if (range >= 0 && startpos == total_size - && fastmap && pbufp->can_be_null == 0) - return -1; - - val = re_match_2 (pbufp, string1, size1, string2, size2, startpos, - regs, mstop); - if (val >= 0) - return startpos; - if (val == -2) - return -2; - -#ifdef C_ALLOCA - alloca (0); -#endif /* C_ALLOCA */ - - advance: - if (!range) - break; - else if (range > 0) - { - range--; - startpos++; - } - else - { - range++; - startpos--; - } - } - return -1; -} - - - -#ifndef emacs /* emacs never uses this. */ -int -re_match (pbufp, string, size, pos, regs) - struct re_pattern_buffer *pbufp; - char *string; - int size, pos; - struct re_registers *regs; -{ - return re_match_2 (pbufp, (char *) 0, 0, string, size, pos, regs, size); -} -#endif /* not emacs */ - - -/* The following are used for re_match_2, defined below: */ - -/* Roughly the maximum number of failure points on the stack. Would be - exactly that if always pushed MAX_NUM_FAILURE_ITEMS each time we failed. */ - -int re_max_failures = 2000; - -/* Routine used by re_match_2. */ -static int bcmp_translate (); - - -/* Structure and accessing macros used in re_match_2: */ - -struct register_info -{ - unsigned is_active : 1; - unsigned matched_something : 1; -}; - -#define IS_ACTIVE(R) ((R).is_active) -#define MATCHED_SOMETHING(R) ((R).matched_something) - - -/* Macros used by re_match_2: */ - - -/* I.e., regstart, regend, and reg_info. */ - -#define NUM_REG_ITEMS 3 - -/* We push at most this many things on the stack whenever we - fail. The `+ 2' refers to PATTERN_PLACE and STRING_PLACE, which are - arguments to the PUSH_FAILURE_POINT macro. */ - -#define MAX_NUM_FAILURE_ITEMS (RE_NREGS * NUM_REG_ITEMS + 2) - - -/* We push this many things on the stack whenever we fail. */ - -#define NUM_FAILURE_ITEMS (last_used_reg * NUM_REG_ITEMS + 2) - - -/* This pushes most of the information about the current state we will want - if we ever fail back to it. */ - -#define PUSH_FAILURE_POINT(pattern_place, string_place) \ - { \ - short last_used_reg, this_reg; \ - \ - /* Find out how many registers are active or have been matched. \ - (Aside from register zero, which is only set at the end.) */ \ - for (last_used_reg = RE_NREGS - 1; last_used_reg > 0; last_used_reg--)\ - if (regstart[last_used_reg] != (unsigned char *) -1) \ - break; \ - \ - if (stacke - stackp < NUM_FAILURE_ITEMS) \ - { \ - unsigned char **stackx; \ - unsigned int len = stacke - stackb; \ - if (len > re_max_failures * MAX_NUM_FAILURE_ITEMS) \ - return -2; \ - \ - /* Roughly double the size of the stack. */ \ - stackx = (unsigned char **) alloca (2 * len \ - * sizeof (unsigned char *));\ - /* Only copy what is in use. */ \ - bcopy (stackb, stackx, len * sizeof (char *)); \ - stackp = stackx + (stackp - stackb); \ - stackb = stackx; \ - stacke = stackb + 2 * len; \ - } \ - \ - /* Now push the info for each of those registers. */ \ - for (this_reg = 1; this_reg <= last_used_reg; this_reg++) \ - { \ - *stackp++ = regstart[this_reg]; \ - *stackp++ = regend[this_reg]; \ - *stackp++ = (unsigned char *) ®_info[this_reg]; \ - } \ - \ - /* Push how many registers we saved. */ \ - *stackp++ = (unsigned char *) last_used_reg; \ - \ - *stackp++ = pattern_place; \ - *stackp++ = string_place; \ - } - - -/* This pops what PUSH_FAILURE_POINT pushes. */ - -#define POP_FAILURE_POINT() \ - { \ - int temp; \ - stackp -= 2; /* Remove failure points. */ \ - temp = (int) *--stackp; /* How many regs pushed. */ \ - temp *= NUM_REG_ITEMS; /* How much to take off the stack. */ \ - stackp -= temp; /* Remove the register info. */ \ - } - - -#define MATCHING_IN_FIRST_STRING (dend == end_match_1) - -/* Is true if there is a first string and if PTR is pointing anywhere - inside it or just past the end. */ - -#define IS_IN_FIRST_STRING(ptr) \ - (size1 && string1 <= (ptr) && (ptr) <= string1 + size1) - -/* Call before fetching a character with *d. This switches over to - string2 if necessary. */ - -#define PREFETCH \ - while (d == dend) \ - { \ - /* end of string2 => fail. */ \ - if (dend == end_match_2) \ - goto fail; \ - /* end of string1 => advance to string2. */ \ - d = string2; \ - dend = end_match_2; \ - } - - -/* Call this when have matched something; it sets `matched' flags for the - registers corresponding to the subexpressions of which we currently - are inside. */ -#define SET_REGS_MATCHED \ - { unsigned this_reg; \ - for (this_reg = 0; this_reg < RE_NREGS; this_reg++) \ - { \ - if (IS_ACTIVE(reg_info[this_reg])) \ - MATCHED_SOMETHING(reg_info[this_reg]) = 1; \ - else \ - MATCHED_SOMETHING(reg_info[this_reg]) = 0; \ - } \ - } - -/* Test if at very beginning or at very end of the virtual concatenation - of string1 and string2. If there is only one string, we've put it in - string2. */ - -#define AT_STRINGS_BEG (d == (size1 ? string1 : string2) || !size2) -#define AT_STRINGS_END (d == end2) - -#define AT_WORD_BOUNDARY \ - (AT_STRINGS_BEG || AT_STRINGS_END || IS_A_LETTER (d - 1) != IS_A_LETTER (d)) - -/* We have two special cases to check for: - 1) if we're past the end of string1, we have to look at the first - character in string2; - 2) if we're before the beginning of string2, we have to look at the - last character in string1; we assume there is a string1, so use - this in conjunction with AT_STRINGS_BEG. */ -#define IS_A_LETTER(d) \ - (SYNTAX ((d) == end1 ? *string2 : (d) == string2 - 1 ? *(end1 - 1) : *(d))\ - == Sword) - - -/* Match the pattern described by PBUFP against the virtual - concatenation of STRING1 and STRING2, which are of SIZE1 and SIZE2, - respectively. Start the match at index POS in the virtual - concatenation of STRING1 and STRING2. In REGS, return the indices of - the virtual concatenation of STRING1 and STRING2 that matched the - entire PBUFP->buffer and its contained subexpressions. Do not - consider matching one past the index MSTOP in the virtual - concatenation of STRING1 and STRING2. - - If pbufp->fastmap is nonzero, then it had better be up to date. - - The reason that the data to match are specified as two components - which are to be regarded as concatenated is so this function can be - used directly on the contents of an Emacs buffer. - - -1 is returned if there is no match. -2 is returned if there is an - error (such as match stack overflow). Otherwise the value is the - length of the substring which was matched. */ - -int -re_match_2 (pbufp, string1_arg, size1, string2_arg, size2, pos, regs, mstop) - struct re_pattern_buffer *pbufp; - char *string1_arg, *string2_arg; - int size1, size2; - int pos; - struct re_registers *regs; - int mstop; -{ - register unsigned char *p = (unsigned char *) pbufp->buffer; - - /* Pointer to beyond end of buffer. */ - register unsigned char *pend = p + pbufp->used; - - unsigned char *string1 = (unsigned char *) string1_arg; - unsigned char *string2 = (unsigned char *) string2_arg; - unsigned char *end1; /* Just past end of first string. */ - unsigned char *end2; /* Just past end of second string. */ - - /* Pointers into string1 and string2, just past the last characters in - each to consider matching. */ - unsigned char *end_match_1, *end_match_2; - - register unsigned char *d, *dend; - register int mcnt; /* Multipurpose. */ - unsigned char *translate = (unsigned char *) pbufp->translate; - unsigned is_a_jump_n = 0; - - /* Failure point stack. Each place that can handle a failure further - down the line pushes a failure point on this stack. It consists of - restart, regend, and reg_info for all registers corresponding to the - subexpressions we're currently inside, plus the number of such - registers, and, finally, two char *'s. The first char * is where to - resume scanning the pattern; the second one is where to resume - scanning the strings. If the latter is zero, the failure point is a - ``dummy''; if a failure happens and the failure point is a dummy, it - gets discarded and the next next one is tried. */ - - unsigned char *initial_stack[MAX_NUM_FAILURE_ITEMS * NFAILURES]; - unsigned char **stackb = initial_stack; - unsigned char **stackp = stackb; - unsigned char **stacke = &stackb[MAX_NUM_FAILURE_ITEMS * NFAILURES]; - - - /* Information on the contents of registers. These are pointers into - the input strings; they record just what was matched (on this - attempt) by a subexpression part of the pattern, that is, the - regnum-th regstart pointer points to where in the pattern we began - matching and the regnum-th regend points to right after where we - stopped matching the regnum-th subexpression. (The zeroth register - keeps track of what the whole pattern matches.) */ - - unsigned char *regstart[RE_NREGS]; - unsigned char *regend[RE_NREGS]; - - /* The is_active field of reg_info helps us keep track of which (possibly - nested) subexpressions we are currently in. The matched_something - field of reg_info[reg_num] helps us tell whether or not we have - matched any of the pattern so far this time through the reg_num-th - subexpression. These two fields get reset each time through any - loop their register is in. */ - - struct register_info reg_info[RE_NREGS]; - - - /* The following record the register info as found in the above - variables when we find a match better than any we've seen before. - This happens as we backtrack through the failure points, which in - turn happens only if we have not yet matched the entire string. */ - - unsigned best_regs_set = 0; - unsigned char *best_regstart[RE_NREGS]; - unsigned char *best_regend[RE_NREGS]; - - /* Initialize subexpression text positions to -1 to mark ones that no - \( or ( and \) or ) has been seen for. Also set all registers to - inactive and mark them as not having matched anything or ever - failed. */ - for (mcnt = 0; mcnt < RE_NREGS; mcnt++) - { - regstart[mcnt] = regend[mcnt] = (unsigned char *) -1; - IS_ACTIVE (reg_info[mcnt]) = 0; - MATCHED_SOMETHING (reg_info[mcnt]) = 0; - } - - if (regs) - for (mcnt = 0; mcnt < RE_NREGS; mcnt++) - regs->start[mcnt] = regs->end[mcnt] = -1; - - /* Set up pointers to ends of strings. - Don't allow the second string to be empty unless both are empty. */ - if (size2 == 0) - { - string2 = string1; - size2 = size1; - string1 = 0; - size1 = 0; - } - end1 = string1 + size1; - end2 = string2 + size2; - - /* Compute where to stop matching, within the two strings. */ - if (mstop <= size1) - { - end_match_1 = string1 + mstop; - end_match_2 = string2; - } - else - { - end_match_1 = end1; - end_match_2 = string2 + mstop - size1; - } - - /* `p' scans through the pattern as `d' scans through the data. `dend' - is the end of the input string that `d' points within. `d' is - advanced into the following input string whenever necessary, but - this happens before fetching; therefore, at the beginning of the - loop, `d' can be pointing at the end of a string, but it cannot - equal string2. */ - - if (size1 != 0 && pos <= size1) - d = string1 + pos, dend = end_match_1; - else - d = string2 + pos - size1, dend = end_match_2; - - - /* This loops over pattern commands. It exits by returning from the - function if match is complete, or it drops through if match fails - at this starting point in the input data. */ - - while (1) - { - is_a_jump_n = 0; - /* End of pattern means we might have succeeded. */ - if (p == pend) - { - /* If not end of string, try backtracking. Otherwise done. */ - if (d != end_match_2) - { - if (stackp != stackb) - { - /* More failure points to try. */ - - unsigned in_same_string = - IS_IN_FIRST_STRING (best_regend[0]) - == MATCHING_IN_FIRST_STRING; - - /* If exceeds best match so far, save it. */ - if (! best_regs_set - || (in_same_string && d > best_regend[0]) - || (! in_same_string && ! MATCHING_IN_FIRST_STRING)) - { - best_regs_set = 1; - best_regend[0] = d; /* Never use regstart[0]. */ - - for (mcnt = 1; mcnt < RE_NREGS; mcnt++) - { - best_regstart[mcnt] = regstart[mcnt]; - best_regend[mcnt] = regend[mcnt]; - } - } - goto fail; - } - /* If no failure points, don't restore garbage. */ - else if (best_regs_set) - { - restore_best_regs: - /* Restore best match. */ - d = best_regend[0]; - - for (mcnt = 0; mcnt < RE_NREGS; mcnt++) - { - regstart[mcnt] = best_regstart[mcnt]; - regend[mcnt] = best_regend[mcnt]; - } - } - } - - /* If caller wants register contents data back, convert it - to indices. */ - if (regs) - { - regs->start[0] = pos; - if (MATCHING_IN_FIRST_STRING) - regs->end[0] = d - string1; - else - regs->end[0] = d - string2 + size1; - for (mcnt = 1; mcnt < RE_NREGS; mcnt++) - { - if (regend[mcnt] == (unsigned char *) -1) - { - regs->start[mcnt] = -1; - regs->end[mcnt] = -1; - continue; - } - if (IS_IN_FIRST_STRING (regstart[mcnt])) - regs->start[mcnt] = regstart[mcnt] - string1; - else - regs->start[mcnt] = regstart[mcnt] - string2 + size1; - - if (IS_IN_FIRST_STRING (regend[mcnt])) - regs->end[mcnt] = regend[mcnt] - string1; - else - regs->end[mcnt] = regend[mcnt] - string2 + size1; - } - } - return d - pos - (MATCHING_IN_FIRST_STRING - ? string1 - : string2 - size1); - } - - /* Otherwise match next pattern command. */ -#ifdef SWITCH_ENUM_BUG - switch ((int) ((enum regexpcode) *p++)) -#else - switch ((enum regexpcode) *p++) -#endif - { - - /* \( [or `(', as appropriate] is represented by start_memory, - \) by stop_memory. Both of those commands are followed by - a register number in the next byte. The text matched - within the \( and \) is recorded under that number. */ - case start_memory: - regstart[*p] = d; - IS_ACTIVE (reg_info[*p]) = 1; - MATCHED_SOMETHING (reg_info[*p]) = 0; - p++; - break; - - case stop_memory: - regend[*p] = d; - IS_ACTIVE (reg_info[*p]) = 0; - - /* If just failed to match something this time around with a sub- - expression that's in a loop, try to force exit from the loop. */ - if ((! MATCHED_SOMETHING (reg_info[*p]) - || (enum regexpcode) p[-3] == start_memory) - && (p + 1) != pend) - { - register unsigned char *p2 = p + 1; - mcnt = 0; - switch (*p2++) - { - case jump_n: - is_a_jump_n = 1; - case finalize_jump: - case maybe_finalize_jump: - case jump: - case dummy_failure_jump: - EXTRACT_NUMBER_AND_INCR (mcnt, p2); - if (is_a_jump_n) - p2 += 2; - break; - } - p2 += mcnt; - - /* If the next operation is a jump backwards in the pattern - to an on_failure_jump, exit from the loop by forcing a - failure after pushing on the stack the on_failure_jump's - jump in the pattern, and d. */ - if (mcnt < 0 && (enum regexpcode) *p2++ == on_failure_jump) - { - EXTRACT_NUMBER_AND_INCR (mcnt, p2); - PUSH_FAILURE_POINT (p2 + mcnt, d); - goto fail; - } - } - p++; - break; - - /* \<digit> has been turned into a `duplicate' command which is - followed by the numeric value of <digit> as the register number. */ - case duplicate: - { - int regno = *p++; /* Get which register to match against */ - register unsigned char *d2, *dend2; - - /* Where in input to try to start matching. */ - d2 = regstart[regno]; - - /* Where to stop matching; if both the place to start and - the place to stop matching are in the same string, then - set to the place to stop, otherwise, for now have to use - the end of the first string. */ - - dend2 = ((IS_IN_FIRST_STRING (regstart[regno]) - == IS_IN_FIRST_STRING (regend[regno])) - ? regend[regno] : end_match_1); - while (1) - { - /* If necessary, advance to next segment in register - contents. */ - while (d2 == dend2) - { - if (dend2 == end_match_2) break; - if (dend2 == regend[regno]) break; - d2 = string2, dend2 = regend[regno]; /* end of string1 => advance to string2. */ - } - /* At end of register contents => success */ - if (d2 == dend2) break; - - /* If necessary, advance to next segment in data. */ - PREFETCH; - - /* How many characters left in this segment to match. */ - mcnt = dend - d; - - /* Want how many consecutive characters we can match in - one shot, so, if necessary, adjust the count. */ - if (mcnt > dend2 - d2) - mcnt = dend2 - d2; - - /* Compare that many; failure if mismatch, else move - past them. */ - if (translate - ? bcmp_translate (d, d2, mcnt, translate) - : bcmp (d, d2, mcnt)) - goto fail; - d += mcnt, d2 += mcnt; - } - } - break; - - case anychar: - PREFETCH; /* Fetch a data character. */ - /* Match anything but a newline, maybe even a null. */ - if ((translate ? translate[*d] : *d) == '\n' - || ((obscure_syntax & RE_DOT_NOT_NULL) - && (translate ? translate[*d] : *d) == '\000')) - goto fail; - SET_REGS_MATCHED; - d++; - break; - - case charset: - case charset_not: - { - int not = 0; /* Nonzero for charset_not. */ - register int c; - if (*(p - 1) == (unsigned char) charset_not) - not = 1; - - PREFETCH; /* Fetch a data character. */ - - if (translate) - c = translate[*d]; - else - c = *d; - - if (c < *p * BYTEWIDTH - && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) - not = !not; - - p += 1 + *p; - - if (!not) goto fail; - SET_REGS_MATCHED; - d++; - break; - } - - case begline: - if ((size1 != 0 && d == string1) - || (size1 == 0 && size2 != 0 && d == string2) - || (d && d[-1] == '\n') - || (size1 == 0 && size2 == 0)) - break; - else - goto fail; - - case endline: - if (d == end2 - || (d == end1 ? (size2 == 0 || *string2 == '\n') : *d == '\n')) - break; - goto fail; - - /* `or' constructs are handled by starting each alternative with - an on_failure_jump that points to the start of the next - alternative. Each alternative except the last ends with a - jump to the joining point. (Actually, each jump except for - the last one really jumps to the following jump, because - tensioning the jumps is a hassle.) */ - - /* The start of a stupid repeat has an on_failure_jump that points - past the end of the repeat text. This makes a failure point so - that on failure to match a repetition, matching restarts past - as many repetitions have been found with no way to fail and - look for another one. */ - - /* A smart repeat is similar but loops back to the on_failure_jump - so that each repetition makes another failure point. */ - - case on_failure_jump: - on_failure: - EXTRACT_NUMBER_AND_INCR (mcnt, p); - PUSH_FAILURE_POINT (p + mcnt, d); - break; - - /* The end of a smart repeat has a maybe_finalize_jump back. - Change it either to a finalize_jump or an ordinary jump. */ - case maybe_finalize_jump: - EXTRACT_NUMBER_AND_INCR (mcnt, p); - { - register unsigned char *p2 = p; - /* Compare what follows with the beginning of the repeat. - If we can establish that there is nothing that they would - both match, we can change to finalize_jump. */ - while (p2 + 1 != pend - && (*p2 == (unsigned char) stop_memory - || *p2 == (unsigned char) start_memory)) - p2 += 2; /* Skip over reg number. */ - if (p2 == pend) - p[-3] = (unsigned char) finalize_jump; - else if (*p2 == (unsigned char) exactn - || *p2 == (unsigned char) endline) - { - register int c = *p2 == (unsigned char) endline ? '\n' : p2[2]; - register unsigned char *p1 = p + mcnt; - /* p1[0] ... p1[2] are an on_failure_jump. - Examine what follows that. */ - if (p1[3] == (unsigned char) exactn && p1[5] != c) - p[-3] = (unsigned char) finalize_jump; - else if (p1[3] == (unsigned char) charset - || p1[3] == (unsigned char) charset_not) - { - int not = p1[3] == (unsigned char) charset_not; - if (c < p1[4] * BYTEWIDTH - && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) - not = !not; - /* `not' is 1 if c would match. */ - /* That means it is not safe to finalize. */ - if (!not) - p[-3] = (unsigned char) finalize_jump; - } - } - } - p -= 2; /* Point at relative address again. */ - if (p[-1] != (unsigned char) finalize_jump) - { - p[-1] = (unsigned char) jump; - goto nofinalize; - } - /* Note fall through. */ - - /* The end of a stupid repeat has a finalize_jump back to the - start, where another failure point will be made which will - point to after all the repetitions found so far. */ - - /* Take off failure points put on by matching on_failure_jump - because didn't fail. Also remove the register information - put on by the on_failure_jump. */ - case finalize_jump: - POP_FAILURE_POINT (); - /* Note fall through. */ - - /* Jump without taking off any failure points. */ - case jump: - nofinalize: - EXTRACT_NUMBER_AND_INCR (mcnt, p); - p += mcnt; - break; - - case dummy_failure_jump: - /* Normally, the on_failure_jump pushes a failure point, which - then gets popped at finalize_jump. We will end up at - finalize_jump, also, and with a pattern of, say, `a+', we - are skipping over the on_failure_jump, so we have to push - something meaningless for finalize_jump to pop. */ - PUSH_FAILURE_POINT (0, 0); - goto nofinalize; - - - /* Have to succeed matching what follows at least n times. Then - just handle like an on_failure_jump. */ - case succeed_n: - EXTRACT_NUMBER (mcnt, p + 2); - /* Originally, this is how many times we HAVE to succeed. */ - if (mcnt) - { - mcnt--; - p += 2; - STORE_NUMBER_AND_INCR (p, mcnt); - } - else if (mcnt == 0) - { - p[2] = unused; - p[3] = unused; - goto on_failure; - } - else - { - fprintf (stderr, "regex: the succeed_n's n is not set.\n"); - exit (1); - } - break; - - case jump_n: - EXTRACT_NUMBER (mcnt, p + 2); - /* Originally, this is how many times we CAN jump. */ - if (mcnt) - { - mcnt--; - STORE_NUMBER(p + 2, mcnt); - goto nofinalize; /* Do the jump without taking off - any failure points. */ - } - /* If don't have to jump any more, skip over the rest of command. */ - else - p += 4; - break; - - case set_number_at: - { - register unsigned char *p1; - - EXTRACT_NUMBER_AND_INCR (mcnt, p); - p1 = p + mcnt; - EXTRACT_NUMBER_AND_INCR (mcnt, p); - STORE_NUMBER (p1, mcnt); - break; - } - - /* Ignore these. Used to ignore the n of succeed_n's which - currently have n == 0. */ - case unused: - break; - - case wordbound: - if (AT_WORD_BOUNDARY) - break; - goto fail; - - case notwordbound: - if (AT_WORD_BOUNDARY) - goto fail; - break; - - case wordbeg: - /* Have to check if AT_STRINGS_BEG before looking at d - 1. */ - if (IS_A_LETTER (d) && (AT_STRINGS_BEG || !IS_A_LETTER (d - 1))) - break; - goto fail; - - case wordend: - /* Have to check if AT_STRINGS_BEG before looking at d - 1. */ - if (!AT_STRINGS_BEG && IS_A_LETTER (d - 1) - && (!IS_A_LETTER (d) || AT_STRINGS_END)) - break; - goto fail; - -#ifdef emacs - case before_dot: - if (PTR_CHAR_POS (d) >= point) - goto fail; - break; - - case at_dot: - if (PTR_CHAR_POS (d) != point) - goto fail; - break; - - case after_dot: - if (PTR_CHAR_POS (d) <= point) - goto fail; - break; - - case wordchar: - mcnt = (int) Sword; - goto matchsyntax; - - case syntaxspec: - mcnt = *p++; - matchsyntax: - PREFETCH; - if (SYNTAX (*d++) != (enum syntaxcode) mcnt) goto fail; - SET_REGS_MATCHED; - break; - - case notwordchar: - mcnt = (int) Sword; - goto matchnotsyntax; - - case notsyntaxspec: - mcnt = *p++; - matchnotsyntax: - PREFETCH; - if (SYNTAX (*d++) == (enum syntaxcode) mcnt) goto fail; - SET_REGS_MATCHED; - break; - -#else /* not emacs */ - - case wordchar: - PREFETCH; - if (!IS_A_LETTER (d)) - goto fail; - SET_REGS_MATCHED; - break; - - case notwordchar: - PREFETCH; - if (IS_A_LETTER (d)) - goto fail; - SET_REGS_MATCHED; - break; - -#endif /* not emacs */ - - case begbuf: - if (AT_STRINGS_BEG) - break; - goto fail; - - case endbuf: - if (AT_STRINGS_END) - break; - goto fail; - - case exactn: - /* Match the next few pattern characters exactly. - mcnt is how many characters to match. */ - mcnt = *p++; - /* This is written out as an if-else so we don't waste time - testing `translate' inside the loop. */ - if (translate) - { - do - { - PREFETCH; - if (translate[*d++] != *p++) goto fail; - } - while (--mcnt); - } - else - { - do - { - PREFETCH; - if (*d++ != *p++) goto fail; - } - while (--mcnt); - } - SET_REGS_MATCHED; - break; - } - continue; /* Successfully executed one pattern command; keep going. */ - - /* Jump here if any matching operation fails. */ - fail: - if (stackp != stackb) - /* A restart point is known. Restart there and pop it. */ - { - short last_used_reg, this_reg; - - /* If this failure point is from a dummy_failure_point, just - skip it. */ - if (!stackp[-2]) - { - POP_FAILURE_POINT (); - goto fail; - } - - d = *--stackp; - p = *--stackp; - if (d >= string1 && d <= end1) - dend = end_match_1; - /* Restore register info. */ - last_used_reg = (short) *--stackp; - - /* Make the ones that weren't saved -1 or 0 again. */ - for (this_reg = RE_NREGS - 1; this_reg > last_used_reg; this_reg--) - { - regend[this_reg] = (unsigned char *) -1; - regstart[this_reg] = (unsigned char *) -1; - IS_ACTIVE (reg_info[this_reg]) = 0; - MATCHED_SOMETHING (reg_info[this_reg]) = 0; - } - - /* And restore the rest from the stack. */ - for ( ; this_reg > 0; this_reg--) - { - reg_info[this_reg] = *(struct register_info *) *--stackp; - regend[this_reg] = *--stackp; - regstart[this_reg] = *--stackp; - } - } - else - break; /* Matching at this starting point really fails. */ - } - - if (best_regs_set) - goto restore_best_regs; - return -1; /* Failure to match. */ -} - - -static int -bcmp_translate (s1, s2, len, translate) - unsigned char *s1, *s2; - register int len; - unsigned char *translate; -{ - register unsigned char *p1 = s1, *p2 = s2; - while (len) - { - if (translate [*p1++] != translate [*p2++]) return 1; - len--; - } - return 0; -} - - - -/* Entry points compatible with 4.2 BSD regex library. */ - -#ifndef emacs - -static struct re_pattern_buffer re_comp_buf; - -char * -re_comp (s) - char *s; -{ - if (!s) - { - if (!re_comp_buf.buffer) - return "No previous regular expression"; - return 0; - } - - if (!re_comp_buf.buffer) - { - if (!(re_comp_buf.buffer = (char *) malloc (200))) - return "Memory exhausted"; - re_comp_buf.allocated = 200; - if (!(re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH))) - return "Memory exhausted"; - } - return re_compile_pattern (s, strlen (s), &re_comp_buf); -} - -int -re_exec (s) - char *s; -{ - int len = strlen (s); - return 0 <= re_search (&re_comp_buf, s, len, 0, len, - (struct re_registers *) 0); -} -#endif /* not emacs */ - - - -#ifdef test - -#include <stdio.h> - -/* Indexed by a character, gives the upper case equivalent of the - character. */ - -char upcase[0400] = - { 000, 001, 002, 003, 004, 005, 006, 007, - 010, 011, 012, 013, 014, 015, 016, 017, - 020, 021, 022, 023, 024, 025, 026, 027, - 030, 031, 032, 033, 034, 035, 036, 037, - 040, 041, 042, 043, 044, 045, 046, 047, - 050, 051, 052, 053, 054, 055, 056, 057, - 060, 061, 062, 063, 064, 065, 066, 067, - 070, 071, 072, 073, 074, 075, 076, 077, - 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107, - 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, - 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, - 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137, - 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107, - 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, - 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, - 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177, - 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, - 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, - 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, - 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, - 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, - 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, - 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, - 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, - 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, - 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, - 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, - 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, - 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, - 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, - 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, - 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377 - }; - -#ifdef canned - -#include "tests.h" - -typedef enum { extended_test, basic_test } test_type; - -/* Use this to run the tests we've thought of. */ - -void -main () -{ - test_type t = extended_test; - - if (t == basic_test) - { - printf ("Running basic tests:\n\n"); - test_posix_basic (); - } - else if (t == extended_test) - { - printf ("Running extended tests:\n\n"); - test_posix_extended (); - } -} - -#else /* not canned */ - -/* Use this to run interactive tests. */ - -void -main (argc, argv) - int argc; - char **argv; -{ - char pat[80]; - struct re_pattern_buffer buf; - int i; - char c; - char fastmap[(1 << BYTEWIDTH)]; - - /* Allow a command argument to specify the style of syntax. */ - if (argc > 1) - obscure_syntax = atoi (argv[1]); - - buf.allocated = 40; - buf.buffer = (char *) malloc (buf.allocated); - buf.fastmap = fastmap; - buf.translate = upcase; - - while (1) - { - gets (pat); - - if (*pat) - { - re_compile_pattern (pat, strlen(pat), &buf); - - for (i = 0; i < buf.used; i++) - printchar (buf.buffer[i]); - - putchar ('\n'); - - printf ("%d allocated, %d used.\n", buf.allocated, buf.used); - - re_compile_fastmap (&buf); - printf ("Allowed by fastmap: "); - for (i = 0; i < (1 << BYTEWIDTH); i++) - if (fastmap[i]) printchar (i); - putchar ('\n'); - } - - gets (pat); /* Now read the string to match against */ - - i = re_match (&buf, pat, strlen (pat), 0, 0); - printf ("Match value %d.\n", i); - } -} - -#endif - - -#ifdef NOTDEF -print_buf (bufp) - struct re_pattern_buffer *bufp; -{ - int i; - - printf ("buf is :\n----------------\n"); - for (i = 0; i < bufp->used; i++) - printchar (bufp->buffer[i]); - - printf ("\n%d allocated, %d used.\n", bufp->allocated, bufp->used); - - printf ("Allowed by fastmap: "); - for (i = 0; i < (1 << BYTEWIDTH); i++) - if (bufp->fastmap[i]) - printchar (i); - printf ("\nAllowed by translate: "); - if (bufp->translate) - for (i = 0; i < (1 << BYTEWIDTH); i++) - if (bufp->translate[i]) - printchar (i); - printf ("\nfastmap is%s accurate\n", bufp->fastmap_accurate ? "" : "n't"); - printf ("can %s be null\n----------", bufp->can_be_null ? "" : "not"); -} -#endif /* NOTDEF */ - -printchar (c) - char c; -{ - if (c < 040 || c >= 0177) - { - putchar ('\\'); - putchar (((c >> 6) & 3) + '0'); - putchar (((c >> 3) & 7) + '0'); - putchar ((c & 7) + '0'); - } - else - putchar (c); -} - -error (string) - char *string; -{ - puts (string); - exit (1); -} -#endif /* test */ diff --git a/usr.bin/sed/regex.h b/usr.bin/sed/regex.h deleted file mode 100644 index b88abf9c35..0000000000 --- a/usr.bin/sed/regex.h +++ /dev/null @@ -1,253 +0,0 @@ -/* Definitions for data structures callers pass the regex library. - - Copyright (C) 1985, 1989-90 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - - -#ifndef __REGEXP_LIBRARY -#define __REGEXP_LIBRARY - -/* Define number of parens for which we record the beginnings and ends. - This affects how much space the `struct re_registers' type takes up. */ -#ifndef RE_NREGS -#define RE_NREGS 10 -#endif - -#define BYTEWIDTH 8 - - -/* Maximum number of duplicates an interval can allow. */ -#define RE_DUP_MAX ((1 << 15) - 1) - - -/* This defines the various regexp syntaxes. */ -extern int obscure_syntax; - - -/* The following bits are used in the obscure_syntax variable to choose among - alternative regexp syntaxes. */ - -/* If this bit is set, plain parentheses serve as grouping, and backslash - parentheses are needed for literal searching. - If not set, backslash-parentheses are grouping, and plain parentheses - are for literal searching. */ -#define RE_NO_BK_PARENS 1 - -/* If this bit is set, plain | serves as the `or'-operator, and \| is a - literal. - If not set, \| serves as the `or'-operator, and | is a literal. */ -#define RE_NO_BK_VBAR (1 << 1) - -/* If this bit is not set, plain + or ? serves as an operator, and \+, \? are - literals. - If set, \+, \? are operators and plain +, ? are literals. */ -#define RE_BK_PLUS_QM (1 << 2) - -/* If this bit is set, | binds tighter than ^ or $. - If not set, the contrary. */ -#define RE_TIGHT_VBAR (1 << 3) - -/* If this bit is set, then treat newline as an OR operator. - If not set, treat it as a normal character. */ -#define RE_NEWLINE_OR (1 << 4) - -/* If this bit is set, then special characters may act as normal - characters in some contexts. Specifically, this applies to: - ^ -- only special at the beginning, or after ( or |; - $ -- only special at the end, or before ) or |; - *, +, ? -- only special when not after the beginning, (, or |. - If this bit is not set, special characters (such as *, ^, and $) - always have their special meaning regardless of the surrounding - context. */ -#define RE_CONTEXT_INDEP_OPS (1 << 5) - -/* If this bit is not set, then \ before anything inside [ and ] is taken as - a real \. - If set, then such a \ escapes the following character. This is a - special case for awk. */ -#define RE_AWK_CLASS_HACK (1 << 6) - -/* If this bit is set, then \{ and \} or { and } serve as interval operators. - If not set, then \{ and \} and { and } are treated as literals. */ -#define RE_INTERVALS (1 << 7) - -/* If this bit is not set, then \{ and \} serve as interval operators and - { and } are literals. - If set, then { and } serve as interval operators and \{ and \} are - literals. */ -#define RE_NO_BK_CURLY_BRACES (1 << 8) - -/* If this bit is set, then character classes are supported; they are: - [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], - [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. - If not set, then character classes are not supported. */ -#define RE_CHAR_CLASSES (1 << 9) - -/* If this bit is set, then the dot re doesn't match a null byte. - If not set, it does. */ -#define RE_DOT_NOT_NULL (1 << 10) - -/* If this bit is set, then [^...] doesn't match a newline. - If not set, it does. */ -#define RE_HAT_NOT_NEWLINE (1 << 11) - -/* If this bit is set, back references are recognized. - If not set, they aren't. */ -#define RE_NO_BK_REFS (1 << 12) - -/* If this bit is set, back references must refer to a preceding - subexpression. If not set, a back reference to a nonexistent - subexpression is treated as literal characters. */ -#define RE_NO_EMPTY_BK_REF (1 << 13) - -/* If this bit is set, bracket expressions can't be empty. - If it is set, they can be empty. */ -#define RE_NO_EMPTY_BRACKETS (1 << 14) - -/* If this bit is set, then *, +, ? and { cannot be first in an re or - immediately after a |, or a (. Furthermore, a | cannot be first or - last in an re, or immediately follow another | or a (. Also, a ^ - cannot appear in a nonleading position and a $ cannot appear in a - nontrailing position (outside of bracket expressions, that is). */ -#define RE_CONTEXTUAL_INVALID_OPS (1 << 15) - -/* If this bit is set, then +, ? and | aren't recognized as operators. - If it's not, they are. */ -#define RE_LIMITED_OPS (1 << 16) - -/* If this bit is set, then an ending range point has to collate higher - or equal to the starting range point. - If it's not set, then when the ending range point collates higher - than the starting range point, the range is just considered empty. */ -#define RE_NO_EMPTY_RANGES (1 << 17) - -/* If this bit is set, then a hyphen (-) can't be an ending range point. - If it isn't, then it can. */ -#define RE_NO_HYPHEN_RANGE_END (1 << 18) - - -/* Define combinations of bits for the standard possibilities. */ -#define RE_SYNTAX_POSIX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR \ - | RE_CONTEXT_INDEP_OPS) -#define RE_SYNTAX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR \ - | RE_CONTEXT_INDEP_OPS | RE_AWK_CLASS_HACK) -#define RE_SYNTAX_EGREP (RE_NO_BK_PARENS | RE_NO_BK_VBAR \ - | RE_CONTEXT_INDEP_OPS | RE_NEWLINE_OR) -#define RE_SYNTAX_GREP (RE_BK_PLUS_QM | RE_NEWLINE_OR) -#define RE_SYNTAX_EMACS 0 -#define RE_SYNTAX_POSIX_BASIC (RE_INTERVALS | RE_BK_PLUS_QM \ - | RE_CHAR_CLASSES | RE_DOT_NOT_NULL \ - | RE_HAT_NOT_NEWLINE | RE_NO_EMPTY_BK_REF \ - | RE_NO_EMPTY_BRACKETS | RE_LIMITED_OPS \ - | RE_NO_EMPTY_RANGES | RE_NO_HYPHEN_RANGE_END) - -#define RE_SYNTAX_POSIX_EXTENDED (RE_INTERVALS | RE_NO_BK_CURLY_BRACES \ - | RE_NO_BK_VBAR | RE_NO_BK_PARENS \ - | RE_HAT_NOT_NEWLINE | RE_CHAR_CLASSES \ - | RE_NO_EMPTY_BRACKETS | RE_CONTEXTUAL_INVALID_OPS \ - | RE_NO_BK_REFS | RE_NO_EMPTY_RANGES \ - | RE_NO_HYPHEN_RANGE_END) - - -/* This data structure is used to represent a compiled pattern. */ - -struct re_pattern_buffer - { - char *buffer; /* Space holding the compiled pattern commands. */ - long allocated; /* Size of space that `buffer' points to. */ - long used; /* Length of portion of buffer actually occupied */ - char *fastmap; /* Pointer to fastmap, if any, or zero if none. */ - /* re_search uses the fastmap, if there is one, - to skip over totally implausible characters. */ - char *translate; /* Translate table to apply to all characters before - comparing, or zero for no translation. - The translation is applied to a pattern when it is - compiled and to data when it is matched. */ - char fastmap_accurate; - /* Set to zero when a new pattern is stored, - set to one when the fastmap is updated from it. */ - char can_be_null; /* Set to one by compiling fastmap - if this pattern might match the null string. - It does not necessarily match the null string - in that case, but if this is zero, it cannot. - 2 as value means can match null string - but at end of range or before a character - listed in the fastmap. */ - }; - - -/* search.c (search_buffer) needs this one value. It is defined both in - regex.c and here. */ -#define RE_EXACTN_VALUE 1 - - -/* Structure to store register contents data in. - - Pass the address of such a structure as an argument to re_match, etc., - if you want this information back. - - For i from 1 to RE_NREGS - 1, start[i] records the starting index in - the string of where the ith subexpression matched, and end[i] records - one after the ending index. start[0] and end[0] are analogous, for - the entire pattern. */ - -struct re_registers - { - int start[RE_NREGS]; - int end[RE_NREGS]; - }; - - - -#ifdef __STDC__ - -extern char *re_compile_pattern (char *, int, struct re_pattern_buffer *); -/* Is this really advertised? */ -extern void re_compile_fastmap (struct re_pattern_buffer *); -extern int re_search (struct re_pattern_buffer *, char*, int, int, int, - struct re_registers *); -extern int re_search_2 (struct re_pattern_buffer *, char *, int, - char *, int, int, int, - struct re_registers *, int); -extern int re_match (struct re_pattern_buffer *, char *, int, int, - struct re_registers *); -extern int re_match_2 (struct re_pattern_buffer *, char *, int, - char *, int, int, struct re_registers *, int); - -/* 4.2 bsd compatibility. */ -extern char *re_comp (char *); -extern int re_exec (char *); - -#else /* !__STDC__ */ - -extern char *re_compile_pattern (); -/* Is this really advertised? */ -extern void re_compile_fastmap (); -extern int re_search (), re_search_2 (); -extern int re_match (), re_match_2 (); - -/* 4.2 bsd compatibility. */ -extern char *re_comp (); -extern int re_exec (); - -#endif /* __STDC__ */ - - -#ifdef SYNTAX_TABLE -extern char *re_syntax_table; -#endif - -#endif /* !__REGEXP_LIBRARY */ diff --git a/usr.bin/sed/sed.1 b/usr.bin/sed/sed.1 new file mode 100644 index 0000000000..8fb1d52844 --- /dev/null +++ b/usr.bin/sed/sed.1 @@ -0,0 +1,513 @@ +.\" Copyright (c) 1992 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)sed.1 5.2 (Berkeley) 8/24/92 +.\" +.Dd "August 24, 1992" +.Dt SED 1 +.Os +.Sh NAME +.Nm sed +.Nd stream editor +.Sh SYNOPSIS +.Nm sed +.Op Fl an +.Ar command +.Op Ar file ... +.Nm sed +.Op Fl an +.Op Fl e Ar command +.Op Fl f Ar command_file +.Op Ar file ... +.Sh DESCRIPTION +The +.Nm sed +utility reads the specified files, or the standard input if no files +are specified, modifying the input as specified by a list of commands. +The input is then written to the standard output. +.Pp +A single command may be specified as the first argument to +.Nm sed . +Multiple commands may be specified by using the +.Fl e +or +.Fl f +options. +All commands are applied to the input in the order they are specified +regardless of their origin. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl a +The files listed as parameters for the +.Dq w +functions are created (or truncated) before any processing begins, +by default. +The +.Fl a +option causes +.Nm sed +to delay opening each file until a command containing the related +.Dq w +function is applied to a line of input. +.It Fl e Ar command +Append the editing commands specified by the +.Ar command +argument +to the list of commands. +.It Fl f Ar command_file +Append the editing commands found in the file +.Ar command_file +to the list of commands. +The editing commands should each be listed on a separate line. +.It Fl n +By default, each line of input is echoed to the standard output after +all of the commands have been applied to it. +The +.Fl n +option suppresses this behavior. +.El +.Pp +The form of a +.Nm sed +command is as follows: +.sp +.Dl [address[,address]]function[arguments] +.sp +Whitespace may be inserted before the first address and the function +portions of the command. +.Pp +Normally, +.Nm sed +cyclically copies a line of input, not including its terminating newline +character, into a +.Em "pattern space" , +(unless there is something left after a +.Dq D +function), +applies all of the commands with addresses that select that pattern space, +copies the pattern space to the standard output, appending a newline, and +deletes the pattern space. +.Pp +Some of the functions use a +.Em "hold space" +to save all or part of the pattern space for subsequent retrieval. +.Sh "Sed Addresses" +An address is not required, but if specified must be a number (that counts +input lines +cumulatively across input files), a dollar +.Po +.Dq $ +.Pc +character that addresses the last line of input, or a context address +(which consists of a regular expression preceded and followed by a +delimiter). +.Pp +A command line with no addresses selects every pattern space. +.Pp +A command line with one address selects all of the pattern spaces +that match the address. +.Pp +A command line with two addresses selects the inclusive range from +the first pattern space that matches the first address through the next +pattern space that matches the second. +(If the second address is a number less than or equal to the line number +first selected, only that line is selected.) +Starting at the first line following the selected range, +.Nm sed +starts looking again for the first address. +.Pp +Editing commands can be applied to non-selected pattern spaces by use +of the exclamation character +.Po +.Dq ! +.Pc +function. +.Sh "Sed Regular Expressions" +The +.Nm sed +regular expressions are basic regular expressions (BRE's, see +.Xr regex 3 +for more information). +In addition, +.Nm sed +has the following two additions to BRE's: +.sp +.Bl -enum -compact +.It +In a context address, any character other than a backslash +.Po +.Dq \e +.Pc +or newline character may be used to delimit the regular expression. +Also, putting a backslash character before the delimiting character +causes the character to be treated literally. +For example, in the context address \exabc\exdefx, the RE delimiter +is an +.Dq x +and the second +.Dq x +stands for itself, so that the regular expression is +.Dq abcxdef . +.sp +.It +The escape sequence \en matches a newline character embedded in the +pattern space. +You can't, however, use a literal newline character in an address or +in the substitute command. +.El +.Pp +One special feature of +.Nm sed +regular expressions is that they can default to the last regular +expression used. +If a regular expression is empty, i.e. just the delimiter characters +are specified, the last regular expression encountered is used instead. +The last regular expression is defined as the last regular expression +used as part of an address or substitute command, and at run-time, not +compile-time. +For example, the command +.Dq /abc/s//XXX/ +will substitute +.Dq XXX +for the pattern +.Dq abc . +.Sh "Sed Functions" +In the following list of commands, the maximum number of permissible +addresses for each command is indicated by [0addr], [1addr], or [2addr], +representing zero, one, or two addresses. +.Pp +The argument +.Em text +consists of one or more lines. +To embed a newline in the text, precede it with a backslash. +Other backslashes in text are deleted and the following character +taken literally. +.Pp +The +.Dq r +and +.Dq w +functions take an optional file parameter, which should be separated +from the function letter by white space. +Each file given as an argument to +.Nm sed +is created (or its contents truncated) before any input processing begins. +.Pp +The +.Dq b , +.Dq r , +.Dq s , +.Dq t , +.Dq w , +.Dq y , +.Dq ! , +and +.Dq \&: +functions all accept additional arguments. +The following synopses indicate which arguments have to be separated from +the function letters by white space characters. +.Pp +Two of the functions take a function-list. +This is a list of +.Nm sed +functions separated by newlines, as follows: +.Bd -literal -offset indent +{ function + function + ... + function +} +.Ed +.Pp +The +.Dq { +can be preceded by white space and can be followed by white space. +The function can be preceded by white space. +The terminating +.Dq } +must be preceded by a newline an optional white space. +.sp +.Bl -tag -width "XXXXXX" -compact +.It [2addr] function-list +Execute function-list only when the pattern space is selected. +.sp +.It [1addr]a\e +.It text +.br +Write +.Em text +to standard output immediately before each attempt to read a line of input, +whether by executing the +.Dq N +function or by beginning a new cycle. +.sp +.It [2addr]b[lable] +Branch to the +.Dq \&: +function with the specified label. +If the label is not specified, branch to the end of the script. +.sp +.It [2addr]c\e +.It text +.br +Delete the pattern space. +With 0 or 1 address or at the end of a 2-address range, +.Em text +is written to the standard output. +.sp +.It [2addr]d +Delete the pattern space and start the next cycle. +.sp +.It [2addr]D +Delete the initial segment of the pattern space through the first +newline character and start the next cycle. +.sp +.It [2addr]g +Replace the contents of the pattern space with the contents of the +hold space. +.sp +.It [2addr]G +Append a newline character followed by the contents of the hold space +to the pattern space. +.sp +.It [2addr]h +Replace the contents of the hold space with the contents of the +pattern space. +.sp +.It [2addr]H +Append a newline character followed by the contents of the pattern space +to the hold space. +.sp +.It [1addr]i\e +.It text +.br +Write +.Em text +to the standard output. +.sp +.It [2addr]l +(The letter ell.) +Write the pattern space to the standard output in a visually unambiguous +form. +This form is as follows: +.sp +.Bl -tag -width "carriage-returnXX" -offset indent -compact +.It backslash +\e +.It alert +\ea +.It form-feed +\ef +.It newline +\en +.It carriage-return +\er +.It tab +\et +.It vertical tab +\ev +.El +.Pp +Nonprintable characters are written as three-digit octal numbers (with a +preceding backslash) for each byte in the character (most significant byte +first). +Long lines are folded, with the point of folding indicated by displaying +a backslash followed by a newline. +The end of each line is marked with a +.Dq $ . +.sp +.It [2addr]n +Write the pattern space to the standard output if the default output has +not been suppressed, and replace the pattern space with the next line of +input. +.sp +.It [2addr]N +Append the next line of input to the pattern space, using an embedded +newline character to separate the appended material from the original +contents. +Note that the current line number changes. +.sp +.It [2addr]p +Write the pattern space to standard output. +.sp +.It [2addr]P +Write the pattern space, up to the first newline character to the +standard output. +.sp +.It [1addr]q +Branch to the end of the script and quit without starting a new cycle. +.sp +.It [1addr]r file +Copy the contents of +.Em file +to the standard output immediately before the next attempt to read a +line of input. +If +.Em file +cannot be read for any reason, it is silently ignored and no error +condition is set. +.sp +.It [2addr]s/regular expression/replacement/flags +Substitute the replacement string for the first instance of the regular +expression in the pattern space. +Any character other than backslash or newline can be used instead of +a slash to delimit the RE and the replacement. +Within the RE and the replacement, the RE delimiter itself can be used as +a literal character if it is preceded by a backslash. +.Pp +An ampersand +.Po +.Dq & +.Pc +appearing in the replacement is replaced by the string matching the RE. +The special meaning of +.Dq & +in this context can be suppressed by preceding it by backslash. +The string +.Dq \e# , +where +.Dq # +is a digit, is replaced by the text matched +by the corresponding backreference expression (see +.Xr re_format 7 ). +.Pp +A line can be split by substituting a newline character into it. +To specify a newline character in the replacement string, precede it with +a backslash. +.Pp +The value of +.Em flags +in the substitute function is zero or more of the following: +.Bl -tag -width "XXXXXX" -offset indent +.It "0 ... 9" +Make the substitution only for the N'th occurrence of the regular +expression in the pattern space. +.It g +Make the substitution for all non-overlapping matches of the +regular expression, not just the first one. +.It p +Write the pattern space to standard output if a replacement was made. +If the replacement string is identical to that which it replaces, it +is still considered to have been a replacement. +.It w Em file +Append the pattern space to +.Em file +if a replacement was made. +If the replacement string is identical to that which it replaces, it +is still considered to have been a replacement. +.El +.sp +.It [2addr]t [label] +Branch to the +.Dq : +function bearing the label if any substitutions have been made since the +most recent reading of an input line or execution of a +.Dq t +function. +If no label is specified, branch to the end of the script. +.sp +.It [2addr]w Em file +Append the pattern space to the +.Em file . +.sp +.It [2addr]x +Swap the contents of the pattern and hold spaces. +.sp +.It [2addr]y/string1/string2/ +Replace all occurrences of characters in +.Em string1 +in the pattern space with the corresponding characters from +.Em string2 . +Any character other than a backslash or newline can be used instead of +a slash to delimit the strings. +Within +.Em string1 +and +.Em string2 , +the delimiter itself can be used as a literal character if it is preceded +by a backslash. +.sp +.It [2addr]!function +.It [2addr]!function-list +Apply the function or function-list only to the lines that are +.Em not +selected by the address(es). +.sp +.It [0addr]:label +This function does nothing; it bears a label to which the +.Dq b +and +.Dq t +commands may branch. +.sp +.It [1addr]= +Write the line number to the standard output followed by a newline +character. +.sp +.It [0addr] +Empty lines are ignored. +.sp +.It [0addr]# +The +.Dq # +and the remainder of the line are ignored (treated as a comment), with +the single exception that if the first two characters in the file are +.Dq #n , +the default output is suppressed. +This is the same as specifying the +.Fl n +option on the command line. +.El +.Pp +The +.Nm sed +utility exits 0 on success and >0 if an error occurs. +.Sh SEE ALSO +.Xr awk 1 , +.Xr ed 1 , +.Xr grep 1 , +.Xr regex 3 , +.Xr re_format 7 +.Sh HISTORY +A +.Nm sed +command appeared in +.At v7 . +.Sh STANDARDS +The +.Nm sed +function is expected to be a superset of the +.St -p1003.2 +specification. diff --git a/usr.bin/sed/sed.c b/usr.bin/sed/sed.c deleted file mode 100644 index e1cf70f326..0000000000 --- a/usr.bin/sed/sed.c +++ /dev/null @@ -1,1554 +0,0 @@ -/* GNU SED, a batch stream editor. - Copyright (C) 1989-1991 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifdef __STDC__ -#define VOID void -#else -#define VOID char -#endif - -#define _GNU_SOURCE -#include <ctype.h> -#ifndef isblank -#define isblank(c) ((c) == ' ' || (c) == '\t') -#endif -#include <stdio.h> -#include <regex.h> -#include <getopt.h> -#if defined(STDC_HEADERS) -#include <stdlib.h> -#endif -#if defined(USG) || defined(STDC_HEADERS) -#include <string.h> -#include <memory.h> -#define bcopy(s, d, n) (memcpy((d), (s), (n))) -#else -#include <strings.h> -VOID *memchr(); -#endif - -char *version_string = "GNU sed version 1.08"; - -/* Struct vector is used to describe a chunk of a sed program. There is one - vector for the main program, and one for each { } pair. */ -struct vector { - struct sed_cmd *v; - int v_length; - int v_allocated; - struct vector *up_one; - struct vector *next_one; -}; - - -/* Goto structure is used to hold both GOTO's and labels. There are two - separate lists, one of goto's, called 'jumps', and one of labels, called - 'labels'. - the V element points to the descriptor for the program-chunk in which the - goto was encountered. - the v_index element counts which element of the vector actually IS the - goto/label. The first element of the vector is zero. - the NAME element is the null-terminated name of the label. - next is the next goto/label in the list. */ - -struct sed_label { - struct vector *v; - int v_index; - char *name; - struct sed_label *next; -}; - -/* ADDR_TYPE is zero for a null address, - one if addr_number is valid, or - two if addr_regex is valid, - three, if the address is '$' - - Other values are undefined. - */ - -#define ADDR_NULL 0 -#define ADDR_NUM 1 -#define ADDR_REGEX 2 -#define ADDR_LAST 3 - -struct addr { - int addr_type; - struct re_pattern_buffer *addr_regex; - int addr_number; -}; - - -/* Aflags: If the low order bit is set, a1 has been - matched; apply this command until a2 matches. - If the next bit is set, apply this command to all - lines that DON'T match the address(es). - */ - -#define A1_MATCHED_BIT 01 -#define ADDR_BANG_BIT 02 - - -struct sed_cmd { - struct addr a1,a2; - int aflags; - - char cmd; - - union { - /* This structure is used for a, i, and c commands */ - struct { - char *text; - int text_len; - } cmd_txt; - - /* This is used for b and t commands */ - struct sed_cmd *label; - - /* This for r and w commands */ - FILE *io_file; - - /* This for the hairy s command */ - /* For the flags var: - low order bit means the 'g' option was given, - next bit means the 'p' option was given, - and the next bit means a 'w' option was given, - and wio_file contains the file to write to. */ - -#define S_GLOBAL_BIT 01 -#define S_PRINT_BIT 02 -#define S_WRITE_BIT 04 -#define S_NUM_BIT 010 - - struct { - struct re_pattern_buffer *regx; - char *replacement; - int replace_length; - int flags; - int numb; - FILE *wio_file; - } cmd_regex; - - /* This for the y command */ - unsigned char *translate; - - /* For { and } */ - struct vector *sub; - struct sed_label *jump; - } x; -}; - -/* Sed operates a line at a time. */ -struct line { - char *text; /* Pointer to line allocated by malloc. */ - int length; /* Length of text. */ - int alloc; /* Allocated space for text. */ -}; - -/* This structure holds information about files opend by the 'r', 'w', - and 's///w' commands. In paticular, it holds the FILE pointer to - use, the file's name, a flag that is non-zero if the file is being - read instead of written. */ - -#define NUM_FPS 32 -struct { - FILE *phile; - char *name; - int readit; -} file_ptrs[NUM_FPS]; - - -#if defined(__STDC__) -# define P_(s) s -#else -# define P_(s) () -#endif - -void panic P_((char *str, ...)); -char *__fp_name P_((FILE *fp)); -FILE *ck_fopen P_((char *name, char *mode)); -void ck_fwrite P_((char *ptr, int size, int nmemb, FILE *stream)); -void ck_fclose P_((FILE *stream)); -VOID *ck_malloc P_((int size)); -VOID *ck_realloc P_((VOID *ptr, int size)); -char *ck_strdup P_((char *str)); -VOID *init_buffer P_((void)); -void flush_buffer P_((VOID *bb)); -int size_buffer P_((VOID *b)); -void add_buffer P_((VOID *bb, char *p, int n)); -void add1_buffer P_((VOID *bb, int ch)); -char *get_buffer P_((VOID *bb)); - -void compile_string P_((char *str)); -void compile_file P_((char *str)); -struct vector *compile_program P_((struct vector *vector)); -void bad_prog P_((char *why)); -int inchar P_((void)); -void savchar P_((int ch)); -int compile_address P_((struct addr *addr)); -void compile_regex P_((int slash)); -struct sed_label *setup_jump P_((struct sed_label *list, struct sed_cmd *cmd, struct vector *vec)); -FILE *compile_filename P_((int readit)); -void read_file P_((char *name)); -void execute_program P_((struct vector *vec)); -int match_address P_((struct addr *addr)); -int read_pattern_space P_((void)); -void append_pattern_space P_((void)); -void line_copy P_((struct line *from, struct line *to)); -void line_append P_((struct line *from, struct line *to)); -void str_append P_((struct line *to, char *string, int length)); -void usage P_((void)); - -extern char *myname; - -/* If set, don't write out the line unless explictly told to */ -int no_default_output = 0; - -/* Current input line # */ -int input_line_number = 0; - -/* Are we on the last input file? */ -int last_input_file = 0; - -/* Have we hit EOF on the last input file? This is used to decide if we - have hit the '$' address yet. */ -int input_EOF = 0; - -/* non-zero if a quit command has been executed. */ -int quit_cmd = 0; - -/* Have we done any replacements lately? This is used by the 't' command. */ -int replaced = 0; - -/* How many '{'s are we executing at the moment */ -int program_depth = 0; - -/* The complete compiled SED program that we are going to run */ -struct vector *the_program = 0; - -/* information about labels and jumps-to-labels. This is used to do - the required backpatching after we have compiled all the scripts. */ -struct sed_label *jumps = 0; -struct sed_label *labels = 0; - -/* The 'current' input line. */ -struct line line; - -/* An input line that's been stored by later use by the program */ -struct line hold; - -/* A 'line' to append to the current line when it comes time to write it out */ -struct line append; - - -/* When we're reading a script command from a string, 'prog_start' and - 'prog_end' point to the beginning and end of the string. This - would allow us to compile script strings that contain nulls, except - that script strings are only read from the command line, which is - null-terminated */ -char *prog_start; -char *prog_end; - -/* When we're reading a script command from a string, 'prog_cur' points - to the current character in the string */ -char *prog_cur; - -/* This is the name of the current script file. - It is used for error messages. */ -char *prog_name; - -/* This is the current script file. If it is zero, we are reading - from a string stored in 'prog_start' instead. If both 'prog_file' - and 'prog_start' are zero, we're in trouble! */ -FILE *prog_file; - -/* this is the number of the current script line that we're compiling. It is - used to give out useful and informative error messages. */ -int prog_line = 1; - -/* This is the file pointer that we're currently reading data from. It may - be stdin */ -FILE *input_file; - -/* If this variable is non-zero at exit, one or more of the input - files couldn't be opened. */ - -int bad_input = 0; - -/* 'an empty regular expression is equivalent to the last regular - expression read' so we have to keep track of the last regex used. - Here's where we store a pointer to it (it is only malloc()'d once) */ -struct re_pattern_buffer *last_regex; - -/* Various error messages we may want to print */ -static char ONE_ADDR[] = "Command only uses one address"; -static char NO_ADDR[] = "Command doesn't take any addresses"; -static char LINE_JUNK[] = "Extra characters after command"; -static char BAD_EOF[] = "Unexpected End-of-file"; -static char NO_REGEX[] = "No previous regular expression"; - -static struct option longopts[] = -{ - {"expression", 1, NULL, 'e'}, - {"file", 1, NULL, 'f'}, - {"quiet", 0, NULL, 'n'}, - {"silent", 0, NULL, 'n'}, - {"version", 0, NULL, 'V'}, - {NULL, 0, NULL, 0} -}; - -/* Yes, the main program, which parses arguments, and does the right - thing with them; it also inits the temporary storage, etc. */ -void -main(argc,argv) -int argc; -char **argv; -{ - int opt; - char *e_strings = NULL; - int compiled = 0; - struct sed_label *go,*lbl; - - myname=argv[0]; - while((opt=getopt_long(argc,argv,"ne:f:V", longopts, (int *) 0)) - !=EOF) { - switch(opt) { - case 'n': - no_default_output = 1; - break; - case 'e': - if(e_strings == NULL) { - e_strings=ck_malloc(strlen(optarg)+2); - strcpy(e_strings,optarg); - } else { - e_strings=ck_realloc(e_strings,strlen(e_strings)+strlen(optarg)+2); - strcat(e_strings,optarg); - } - strcat(e_strings,"\n"); - compiled = 1; - break; - case 'f': - compile_file(optarg); - compiled = 1; - break; - case 'V': - fprintf(stderr, "%s\n", version_string); - break; - default: - usage(); - } - } - if(e_strings) { - compile_string(e_strings); - free(e_strings); - } - if(!compiled) { - if (optind == argc) - usage(); - compile_string(argv[optind++]); - } - - for(go=jumps;go;go=go->next) { - for(lbl=labels;lbl;lbl=lbl->next) - if(!strcmp(lbl->name,go->name)) - break; - if(*go->name && !lbl) - panic("Can't find label for jump to '%s'",go->name); - go->v->v[go->v_index].x.jump=lbl; - } - - line.length=0; - line.alloc=50; - line.text=ck_malloc(50); - - append.length=0; - append.alloc=50; - append.text=ck_malloc(50); - - hold.length=0; - hold.alloc=50; - hold.text=ck_malloc(50); - - if(argc<=optind) { - last_input_file++; - read_file("-"); - } else while(optind<argc) { - if(optind==argc-1) - last_input_file++; - read_file(argv[optind]); - optind++; - if(quit_cmd) - break; - } - if(bad_input) - exit(2); - exit(0); -} - -/* 'str' is a string (from the command line) that contains a sed command. - Compile the command, and add it to the end of 'the_program' */ -void -compile_string(str) -char *str; -{ - prog_file = 0; - prog_line=0; - prog_start=prog_cur=str; - prog_end=str+strlen(str); - the_program=compile_program(the_program); -} - -/* 'str' is the name of a file containing sed commands. Read them in - and add them to the end of 'the_program' */ -void -compile_file(str) -char *str; -{ - int ch; - - prog_start=prog_cur=prog_end=0; - prog_name=str; - prog_line=1; - if(str[0]=='-' && str[1]=='\0') - prog_file=stdin; - else - prog_file=ck_fopen(str,"r"); - ch=getc(prog_file); - if(ch=='#') { - ch=getc(prog_file); - if(ch=='n') - no_default_output++; - while(ch!=EOF && ch!='\n') - ch=getc(prog_file); - } else if(ch!=EOF) - ungetc(ch,prog_file); - the_program=compile_program(the_program); -} - -#define MORE_CMDS 40 - -/* Read a program (or a subprogram within '{' '}' pairs) in and store - the compiled form in *'vector' Return a pointer to the new vector. */ -struct vector * -compile_program(vector) -struct vector *vector; -{ - struct sed_cmd *cur_cmd; - int ch; - int slash; - VOID *b; - unsigned char *string; - int num; - - if(!vector) { - vector=(struct vector *)ck_malloc(sizeof(struct vector)); - vector->v=(struct sed_cmd *)ck_malloc(MORE_CMDS*sizeof(struct sed_cmd)); - vector->v_allocated=MORE_CMDS; - vector->v_length=0; - vector->up_one = 0; - vector->next_one = 0; - } - for(;;) { - skip_comment: - do ch=inchar(); - while(ch!=EOF && (isblank(ch) || ch=='\n' || ch==';')); - if(ch==EOF) - break; - savchar(ch); - - if(vector->v_length==vector->v_allocated) { - vector->v=(struct sed_cmd *)ck_realloc((VOID *)vector->v,(vector->v_length+MORE_CMDS)*sizeof(struct sed_cmd)); - vector->v_allocated+=MORE_CMDS; - } - cur_cmd=vector->v+vector->v_length; - vector->v_length++; - - cur_cmd->a1.addr_type=0; - cur_cmd->a2.addr_type=0; - cur_cmd->aflags=0; - cur_cmd->cmd=0; - - if(compile_address(&(cur_cmd->a1))) { - ch=inchar(); - if(ch==',') { - do ch=inchar(); - while(ch!=EOF && isblank(ch)); - savchar(ch); - if(compile_address(&(cur_cmd->a2))) - ; - else - bad_prog("Unexpected ','"); - } else - savchar(ch); - } - ch=inchar(); - if(ch==EOF) - break; - new_cmd: - switch(ch) { - case '#': - if(cur_cmd->a1.addr_type!=0) - bad_prog(NO_ADDR); - do ch=inchar(); - while(ch!=EOF && ch!='\n'); - vector->v_length--; - goto skip_comment; - case '!': - if(cur_cmd->aflags & ADDR_BANG_BIT) - bad_prog("Multiple '!'s"); - cur_cmd->aflags|= ADDR_BANG_BIT; - do ch=inchar(); - while(ch!=EOF && isblank(ch)); - if(ch==EOF) - bad_prog(BAD_EOF); -#if 0 - savchar(ch); -#endif - goto new_cmd; - case 'a': - case 'i': - if(cur_cmd->a2.addr_type!=0) - bad_prog(ONE_ADDR); - /* Fall Through */ - case 'c': - cur_cmd->cmd=ch; - if(inchar()!='\\' || inchar()!='\n') - bad_prog(LINE_JUNK); - b=init_buffer(); - while((ch=inchar())!=EOF && ch!='\n') { - if(ch=='\\') - ch=inchar(); - add1_buffer(b,ch); - } - if(ch!=EOF) - add1_buffer(b,ch); - num=size_buffer(b); - string=(unsigned char *)ck_malloc(num); - bcopy(get_buffer(b),string,num); - flush_buffer(b); - cur_cmd->x.cmd_txt.text_len=num; - cur_cmd->x.cmd_txt.text=(char *)string; - break; - case '{': - cur_cmd->cmd=ch; - program_depth++; -#if 0 - while((ch=inchar())!=EOF && ch!='\n') - if(!isblank(ch)) - bad_prog(LINE_JUNK); -#endif - cur_cmd->x.sub=compile_program((struct vector *)0); - /* FOO JF is this the right thing to do? */ - break; - case '}': - if(!program_depth) - bad_prog("Unexpected '}'"); - --(vector->v_length); - while((ch=inchar())!=EOF && ch!='\n' && ch!=';') - if(!isblank(ch)) - bad_prog(LINE_JUNK); - return vector; - case ':': - cur_cmd->cmd=ch; - if(cur_cmd->a1.addr_type!=0) - bad_prog(": doesn't want any addresses"); - labels=setup_jump(labels,cur_cmd,vector); - break; - case 'b': - case 't': - cur_cmd->cmd=ch; - jumps=setup_jump(jumps,cur_cmd,vector); - break; - case 'q': - case '=': - if(cur_cmd->a2.addr_type) - bad_prog(ONE_ADDR); - /* Fall Through */ - case 'd': - case 'D': - case 'g': - case 'G': - case 'h': - case 'H': - case 'l': - case 'n': - case 'N': - case 'p': - case 'P': - case 'x': - cur_cmd->cmd=ch; - do ch=inchar(); - while(ch!=EOF && isblank(ch) && ch!='\n' && ch!=';'); - if(ch!='\n' && ch!=';' && ch!=EOF) - bad_prog(LINE_JUNK); - break; - - case 'r': - if(cur_cmd->a2.addr_type!=0) - bad_prog(ONE_ADDR); - /* FALL THROUGH */ - case 'w': - cur_cmd->cmd=ch; - cur_cmd->x.io_file=compile_filename(ch=='r'); - break; - - case 's': - cur_cmd->cmd=ch; - slash=inchar(); - compile_regex(slash); - - cur_cmd->x.cmd_regex.regx=last_regex; - - b=init_buffer(); - while((ch=inchar())!=EOF && ch!=slash) { - if(ch=='\\') { - int ci; - - ci=inchar(); - if(ci!=EOF) { - if(ci!='\n') - add1_buffer(b,ch); - add1_buffer(b,ci); - } - } else - add1_buffer(b,ch); - } - cur_cmd->x.cmd_regex.replace_length=size_buffer(b); - cur_cmd->x.cmd_regex.replacement=ck_malloc(cur_cmd->x.cmd_regex.replace_length); - bcopy(get_buffer(b),cur_cmd->x.cmd_regex.replacement,cur_cmd->x.cmd_regex.replace_length); - flush_buffer(b); - - cur_cmd->x.cmd_regex.flags=0; - cur_cmd->x.cmd_regex.numb=0; - - if(ch==EOF) - break; - do { - ch=inchar(); - switch(ch) { - case 'p': - if(cur_cmd->x.cmd_regex.flags&S_PRINT_BIT) - bad_prog("multiple 'p' options to 's' command"); - cur_cmd->x.cmd_regex.flags|=S_PRINT_BIT; - break; - case 'g': - if(cur_cmd->x.cmd_regex.flags&S_NUM_BIT) - cur_cmd->x.cmd_regex.flags&= ~S_NUM_BIT; - if(cur_cmd->x.cmd_regex.flags&S_GLOBAL_BIT) - bad_prog("multiple 'g' options to 's' command"); - cur_cmd->x.cmd_regex.flags|=S_GLOBAL_BIT; - break; - case 'w': - cur_cmd->x.cmd_regex.flags|=S_WRITE_BIT; - cur_cmd->x.cmd_regex.wio_file=compile_filename(0); - ch='\n'; - break; - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - case '8': case '9': - if(cur_cmd->x.cmd_regex.flags&S_NUM_BIT) - bad_prog("multiple number options to 's' command"); - if((cur_cmd->x.cmd_regex.flags&S_GLOBAL_BIT)==0) - cur_cmd->x.cmd_regex.flags|=S_NUM_BIT; - num = 0; - while(isdigit(ch)) { - num=num*10+ch-'0'; - ch=inchar(); - } - savchar(ch); - cur_cmd->x.cmd_regex.numb=num; - break; - case '\n': - case ';': - case EOF: - break; - default: - bad_prog("Unknown option to 's'"); - break; - } - } while(ch!=EOF && ch!='\n' && ch!=';'); - if(ch==EOF) - break; - break; - - case 'y': - cur_cmd->cmd=ch; - string=(unsigned char *)ck_malloc(256); - for(num=0;num<256;num++) - string[num]=num; - b=init_buffer(); - slash=inchar(); - while((ch=inchar())!=EOF && ch!=slash) - add1_buffer(b,ch); - cur_cmd->x.translate=string; - string=(unsigned char *)get_buffer(b); - for(num=size_buffer(b);num;--num) { - ch=inchar(); - if(ch==EOF) - bad_prog(BAD_EOF); - if(ch==slash) - bad_prog("strings for y command are different lengths"); - cur_cmd->x.translate[*string++]=ch; - } - flush_buffer(b); - if(inchar()!=slash || ((ch=inchar())!=EOF && ch!='\n' && ch!=';')) - bad_prog(LINE_JUNK); - break; - - default: - bad_prog("Unknown command"); - } - } - return vector; -} - -/* Complain about a programming error and exit. */ -void -bad_prog(why) -char *why; -{ - if(prog_line) - fprintf(stderr,"%s: file %s line %d: %s\n",myname,prog_name,prog_line,why); - else - fprintf(stderr,"%s: %s\n",myname,why); - exit(1); -} - -/* Read the next character from the program. Return EOF if there isn't - anything to read. Keep prog_line up to date, so error messages can - be meaningful. */ -int -inchar() -{ - int ch; - if(prog_file) { - if(feof(prog_file)) - return EOF; - else - ch=getc(prog_file); - } else { - if(!prog_cur) - return EOF; - else if(prog_cur==prog_end) { - ch=EOF; - prog_cur=0; - } else - ch= *prog_cur++; - } - if(ch=='\n' && prog_line) - prog_line++; - return ch; -} - -/* unget 'ch' so the next call to inchar will return it. 'ch' must not be - EOF or anything nasty like that. */ -void -savchar(ch) -int ch; -{ - if(ch==EOF) - return; - if(ch=='\n' && prog_line>1) - --prog_line; - if(prog_file) - ungetc(ch,prog_file); - else - *--prog_cur=ch; -} - - -/* Try to read an address for a sed command. If it succeeeds, - return non-zero and store the resulting address in *'addr'. - If the input doesn't look like an address read nothing - and return zero. */ -int -compile_address(addr) -struct addr *addr; -{ - int ch; - int num; - - ch=inchar(); - - if(isdigit(ch)) { - num=ch-'0'; - while((ch=inchar())!=EOF && isdigit(ch)) - num=num*10+ch-'0'; - while(ch!=EOF && isblank(ch)) - ch=inchar(); - savchar(ch); - addr->addr_type=ADDR_NUM; - addr->addr_number = num; - return 1; - } else if(ch=='/') { - addr->addr_type=ADDR_REGEX; - compile_regex('/'); - addr->addr_regex=last_regex; - do ch=inchar(); - while(ch!=EOF && isblank(ch)); - savchar(ch); - return 1; - } else if(ch=='$') { - addr->addr_type=ADDR_LAST; - do ch=inchar(); - while(ch!=EOF && isblank(ch)); - savchar(ch); - return 1; - } else - savchar(ch); - return 0; -} - -void -compile_regex (slash) - int slash; -{ - VOID *b; - int ch; - int in_char_class = 0; - - b=init_buffer(); - while((ch=inchar())!=EOF && (ch!=slash || in_char_class)) { - if(ch=='^') { - if(size_buffer(b)==0) { - add1_buffer(b,'\\'); - add1_buffer(b,'`'); - } else - add1_buffer(b,ch); - continue; - } else if(ch=='$') { - ch=inchar(); - savchar(ch); - if(ch==slash) { - add1_buffer(b,'\\'); - add1_buffer(b,'\''); - } else - add1_buffer(b,'$'); - continue; - } else if(ch == '[') { - add1_buffer(b,ch); - in_char_class = 1; - continue; - } else if(ch == ']') { - add1_buffer(b,ch); - in_char_class = 0; - continue; - } else if(ch!='\\') { - add1_buffer(b,ch); - continue; - } - ch=inchar(); - switch(ch) { - case 'n': - add1_buffer(b,'\n'); - break; -#if 0 - case 'b': - add1_buffer(b,'\b'); - break; - case 'f': - add1_buffer(b,'\f'); - break; - case 'r': - add1_buffer(b,'\r'); - break; - case 't': - add1_buffer(b,'\t'); - break; -#endif /* 0 */ - case EOF: - break; - default: - add1_buffer(b,'\\'); - add1_buffer(b,ch); - break; - } - } - if(ch==EOF) - bad_prog(BAD_EOF); - if(size_buffer(b)) { - last_regex=(struct re_pattern_buffer *)ck_malloc(sizeof(struct re_pattern_buffer)); - last_regex->allocated=size_buffer(b)+10; - last_regex->buffer=ck_malloc(last_regex->allocated); - last_regex->fastmap=ck_malloc(256); - last_regex->translate=0; - re_compile_pattern(get_buffer(b),size_buffer(b),last_regex); - } else if(!last_regex) - bad_prog(NO_REGEX); - flush_buffer(b); -} - -/* Store a label (or label reference) created by a ':', 'b', or 't' - comand so that the jump to/from the lable can be backpatched after - compilation is complete */ -struct sed_label * -setup_jump(list,cmd,vec) -struct sed_label *list; -struct sed_cmd *cmd; -struct vector *vec; -{ - struct sed_label *tmp; - VOID *b; - int ch; - - b=init_buffer(); - while((ch=inchar()) != EOF && isblank(ch)) - ; - while(ch!=EOF && ch!='\n') { - add1_buffer(b,ch); - ch=inchar(); - } - savchar(ch); - add1_buffer(b,'\0'); - tmp=(struct sed_label *)ck_malloc(sizeof(struct sed_label)); - tmp->v=vec; - tmp->v_index=cmd-vec->v; - tmp->name=ck_strdup(get_buffer(b)); - tmp->next=list; - flush_buffer(b); - return tmp; -} - -/* read in a filename for a 'r', 'w', or 's///w' command, and - update the internal structure about files. The file is - opened if it isn't already open. */ -FILE * -compile_filename(readit) - int readit; -{ - char *file_name; - int n; - VOID *b; - int ch; - - if(inchar()!=' ') - bad_prog("missing ' ' before filename"); - b=init_buffer(); - while((ch=inchar())!=EOF && ch!='\n') - add1_buffer(b,ch); - add1_buffer(b,'\0'); - file_name=get_buffer(b); - for(n=0;n<NUM_FPS;n++) { - if(!file_ptrs[n].name) - break; - if(!strcmp(file_ptrs[n].name,file_name)) { - if(file_ptrs[n].readit!=readit) - bad_prog("Can't open file for both reading and writing"); - flush_buffer(b); - return file_ptrs[n].phile; - } - } - if(n<NUM_FPS) { - file_ptrs[n].name=ck_strdup(file_name); - file_ptrs[n].readit=readit; - if (!readit) - file_ptrs[n].phile=ck_fopen(file_name,"a"); - else if (access(file_name, 4) == 0) - file_ptrs[n].phile=ck_fopen(file_name,"r"); - else - file_ptrs[n].phile=ck_fopen("/dev/null", "r"); - flush_buffer(b); - return file_ptrs[n].phile; - } else { - bad_prog("Hopelessely evil compiled in limit on number of open files. re-compile sed"); - return 0; - } -} - -/* Parse a filename given by a 'r' 'w' or 's///w' command. */ -void -read_file(name) -char *name; -{ - if(*name=='-' && name[1]=='\0') - input_file=stdin; - else { - input_file=fopen(name,"r"); - if(input_file==0) { - extern int errno; - extern char *sys_errlist[]; - extern int sys_nerr; - - char *ptr; - - ptr=(errno>=0 && errno<sys_nerr) ? sys_errlist[errno] : "Unknown error code"; - bad_input++; - fprintf(stderr,"%s: can't read %s: %s\n",myname,name,ptr); - - return; - } - } - while(read_pattern_space()) { - execute_program(the_program); - if(!no_default_output) - ck_fwrite(line.text,1,line.length,stdout); - if(append.length) { - ck_fwrite(append.text,1,append.length,stdout); - append.length=0; - } - if(quit_cmd) - break; - } - ck_fclose(input_file); -} - -/* Execute the program 'vec' on the current input line. */ -void -execute_program(vec) -struct vector *vec; -{ - struct sed_cmd *cur_cmd; - int n; - int addr_matched; - static int end_cycle; - - int start; - int remain; - int offset; - - static struct line tmp; - struct line t; - char *rep,*rep_end,*rep_next,*rep_cur; - - struct re_registers regs; - int count = 0; - - end_cycle = 0; - - for(cur_cmd=vec->v,n=vec->v_length;n;cur_cmd++,n--) { - - exe_loop: - addr_matched=0; - if(cur_cmd->aflags&A1_MATCHED_BIT) { - addr_matched=1; - if(match_address(&(cur_cmd->a2))) - cur_cmd->aflags&=~A1_MATCHED_BIT; - } else if(match_address(&(cur_cmd->a1))) { - addr_matched=1; - if(cur_cmd->a2.addr_type!=ADDR_NULL) - cur_cmd->aflags|=A1_MATCHED_BIT; - } - if(cur_cmd->aflags&ADDR_BANG_BIT) - addr_matched= !addr_matched; - if(!addr_matched) - continue; - switch(cur_cmd->cmd) { - case '{': /* Execute sub-program */ - execute_program(cur_cmd->x.sub); - break; - - case ':': /* Executing labels is easy. */ - break; - - case '=': - printf("%d\n",input_line_number); - break; - - case 'a': - while(append.alloc-append.length<cur_cmd->x.cmd_txt.text_len) { - append.alloc *= 2; - append.text=ck_realloc(append.text,append.alloc); - } - bcopy(cur_cmd->x.cmd_txt.text,append.text+append.length,cur_cmd->x.cmd_txt.text_len); - append.length+=cur_cmd->x.cmd_txt.text_len; - break; - - case 'b': - if(!cur_cmd->x.jump) - end_cycle++; - else { - struct sed_label *j = cur_cmd->x.jump; - - n= j->v->v_length - j->v_index; - cur_cmd= j->v->v + j->v_index; - goto exe_loop; - } - break; - - case 'c': - line.length=0; - if(!(cur_cmd->aflags&A1_MATCHED_BIT)) - ck_fwrite(cur_cmd->x.cmd_txt.text,1,cur_cmd->x.cmd_txt.text_len,stdout); - end_cycle++; - break; - - case 'd': - line.length=0; - end_cycle++; - break; - - case 'D': - { - char *tmp; - int newlength; - - tmp=memchr(line.text,'\n',line.length); - newlength=line.length-(tmp-line.text); - if(newlength) - memmove(line.text,tmp,newlength); - line.length=newlength; - } - end_cycle++; - break; - - case 'g': - line_copy(&hold,&line); - break; - - case 'G': - line_append(&hold,&line); - break; - - case 'h': - line_copy(&line,&hold); - break; - - case 'H': - line_append(&line,&hold); - break; - - case 'i': - ck_fwrite(cur_cmd->x.cmd_txt.text,1,cur_cmd->x.cmd_txt.text_len,stdout); - break; - - case 'l': - { - char *tmp; - int n; - int width = 0; - - n=line.length; - tmp=line.text; - /* Use --n so this'll skip the trailing newline */ - while(--n) { - if(width>77) { - width=0; - putchar('\n'); - } - if(*tmp == '\\') { - printf("\\\\"); - width+=2; - } else if(isprint(*tmp)) { - putchar(*tmp); - width++; - } else switch(*tmp) { -#if 0 - /* Should print \00 instead of \0 because (a) POSIX requires it, and - (b) this way \01 is unambiguous. */ - case '\0': - printf("\\0"); - width+=2; - break; -#endif - case 007: - printf("\\a"); - width+=2; - break; - case '\b': - printf("\\b"); - width+=2; - break; - case '\f': - printf("\\f"); - width+=2; - break; - case '\n': - printf("\\n"); - width+=2; - break; - case '\r': - printf("\\r"); - width+=2; - break; - case '\t': - printf("\\t"); - width+=2; - break; - case '\v': - printf("\\v"); - width+=2; - break; - default: - printf("\\%02x",(*tmp)&0xFF); - width+=2; - break; - } - tmp++; - } - putchar('\n'); - } - break; - - case 'n': - if (feof(input_file)) goto quit; - ck_fwrite(line.text,1,line.length,stdout); - read_pattern_space(); - break; - - case 'N': - if (feof(input_file)) goto quit; - append_pattern_space(); - break; - - case 'p': - ck_fwrite(line.text,1,line.length,stdout); - break; - - case 'P': - { - char *tmp; - - tmp=memchr(line.text,'\n',line.length); - ck_fwrite(line.text, 1, - tmp ? tmp - line.text + 1 - : line.length, stdout); - } - break; - - case 'q': quit: - quit_cmd++; - end_cycle++; - break; - - case 'r': - { - int n = 0; - - rewind(cur_cmd->x.io_file); - do { - append.length += n; - if(append.length==append.alloc) { - append.alloc *= 2; - append.text = ck_realloc(append.text, append.alloc); - } - } while((n=fread(append.text+append.length,sizeof(char),append.alloc-append.length,cur_cmd->x.io_file))>0); - if(ferror(cur_cmd->x.io_file)) - panic("Read error on input file to 'r' command"); - } - break; - - case 's': - if(!tmp.alloc) { - tmp.alloc=50; - tmp.text=ck_malloc(50); - } - count=0; - start = 0; - remain=line.length-1; - tmp.length=0; - rep = cur_cmd->x.cmd_regex.replacement; - rep_end=rep+cur_cmd->x.cmd_regex.replace_length; - - while((offset = re_search(cur_cmd->x.cmd_regex.regx, - line.text, - line.length-1, - start, - remain, - ®s))>=0) { - count++; - if(offset-start) - str_append(&tmp,line.text+start,offset-start); - - if(cur_cmd->x.cmd_regex.flags&S_NUM_BIT) { - if(count!=cur_cmd->x.cmd_regex.numb) { - str_append(&tmp,line.text+regs.start[0],regs.end[0]-regs.start[0]); - start = (offset == regs.end[0] ? offset + 1 : regs.end[0]); - remain = (line.length-1) - start; - continue; - } - } - - for(rep_next=rep_cur=rep;rep_next<rep_end;rep_next++) { - if(*rep_next=='&') { - if(rep_next-rep_cur) - str_append(&tmp,rep_cur,rep_next-rep_cur); - str_append(&tmp,line.text+regs.start[0],regs.end[0]-regs.start[0]); - rep_cur=rep_next+1; - } else if(*rep_next=='\\') { - if(rep_next-rep_cur) - str_append(&tmp,rep_cur,rep_next-rep_cur); - rep_next++; - if(rep_next!=rep_end) { - int n; - - if(*rep_next>='0' && *rep_next<='9') { - n= *rep_next -'0'; - str_append(&tmp,line.text+regs.start[n],regs.end[n]-regs.start[n]); - } else - str_append(&tmp,rep_next,1); - } - rep_cur=rep_next+1; - } - } - if(rep_next-rep_cur) - str_append(&tmp,rep_cur,rep_next-rep_cur); - if (offset == regs.end[0]) { - str_append(&tmp, line.text + offset, 1); - ++regs.end[0]; - } - start = regs.end[0]; - - remain = (line.length-1) - start; - if(remain<0) - break; - if(!(cur_cmd->x.cmd_regex.flags&S_GLOBAL_BIT)) - break; - } - if(!count) - break; - replaced=1; - str_append(&tmp,line.text+start,remain+1); - t.text=line.text; - t.length=line.length; - t.alloc=line.alloc; - line.text=tmp.text; - line.length=tmp.length; - line.alloc=tmp.alloc; - tmp.text=t.text; - tmp.length=t.length; - tmp.alloc=t.alloc; - if(cur_cmd->x.cmd_regex.flags&S_WRITE_BIT) - ck_fwrite(line.text,1,line.length,cur_cmd->x.cmd_regex.wio_file); - if(cur_cmd->x.cmd_regex.flags&S_PRINT_BIT) - ck_fwrite(line.text,1,line.length,stdout); - break; - - case 't': - if(replaced) { - replaced = 0; - if(!cur_cmd->x.jump) - end_cycle++; - else { - struct sed_label *j = cur_cmd->x.jump; - - n= j->v->v_length - j->v_index; - cur_cmd= j->v->v + j->v_index; - goto exe_loop; - } - } - break; - - case 'w': - ck_fwrite(line.text,1,line.length,cur_cmd->x.io_file); - break; - - case 'x': - { - struct line tmp; - - tmp=line; - line=hold; - hold=tmp; - } - break; - - case 'y': - { - unsigned char *p,*e; - - for(p=(unsigned char *)(line.text),e=p+line.length;p<e;p++) - *p=cur_cmd->x.translate[*p]; - } - break; - - default: - panic("INTERNAL ERROR: Bad cmd %c",cur_cmd->cmd); - } - if(end_cycle) - break; - } -} - - -/* Return non-zero if the current line matches the address - pointed to by 'addr'. */ -int -match_address(addr) -struct addr *addr; -{ - switch(addr->addr_type) { - case ADDR_NULL: - return 1; - case ADDR_NUM: - return (input_line_number==addr->addr_number); - - case ADDR_REGEX: - return (re_search(addr->addr_regex, - line.text, - line.length-1, - 0, - line.length-1, - (struct re_registers *)0)>=0) ? 1 : 0; - - case ADDR_LAST: - return (input_EOF) ? 1 : 0; - - default: - panic("INTERNAL ERROR: bad address type"); - break; - } - return -1; -} - -/* Read in the next line of input, and store it in the - pattern space. Return non-zero if this is the last line of input */ - -int -read_pattern_space() -{ - int n; - char *p; - int ch; - - p=line.text; - n=line.alloc; - - if(feof(input_file)) - return 0; - input_line_number++; - replaced=0; - for(;;) { - if(n==0) { - line.text=ck_realloc(line.text,line.alloc*2); - p=line.text+line.alloc; - n=line.alloc; - line.alloc*=2; - } - ch=getc(input_file); - if(ch==EOF) { - if(n==line.alloc) - return 0; - *p++='\n'; - --n; - line.length=line.alloc-n; - if(last_input_file) - input_EOF++; - return 1; - } - *p++=ch; - --n; - if(ch=='\n') { - line.length=line.alloc-n; - break; - } - } - ch=getc(input_file); - if(ch!=EOF) - ungetc(ch,input_file); - else if(last_input_file) - input_EOF++; - return 1; -} - -/* Inplement the 'N' command, which appends the next line of input to - the pattern space. */ -void -append_pattern_space() -{ - char *p; - int n; - int ch; - - p=line.text+line.length; - n=line.alloc-line.length; - - input_line_number++; - replaced=0; - for(;;) { - ch=getc(input_file); - if(ch==EOF) { - if(n==line.alloc) - return; - *p++='\n'; - --n; - line.length=line.alloc-n; - if(last_input_file) - input_EOF++; - return; - } - *p++=ch; - --n; - if(ch=='\n') { - line.length=line.alloc-n; - break; - } - if(n==0) { - line.text=ck_realloc(line.text,line.alloc*2); - p=line.text+line.alloc; - n=line.alloc; - line.alloc*=2; - } - } - ch=getc(input_file); - if(ch!=EOF) - ungetc(ch,input_file); - else if(last_input_file) - input_EOF++; -} - -/* Copy the contents of the line 'from' into the line 'to'. - This destroys the old contents of 'to'. It will still work - if the line 'from' contains nulls. */ -void -line_copy(from,to) -struct line *from,*to; -{ - if(from->length>to->alloc) { - to->alloc=from->length; - to->text=ck_realloc(to->text,to->alloc); - } - bcopy(from->text,to->text,from->length); - to->length=from->length; -} - -/* Append the contents of the line 'from' to the line 'to'. - This routine will work even if the line 'from' contains nulls */ -void -line_append(from,to) -struct line *from,*to; -{ - if(from->length>(to->alloc-to->length)) { - to->alloc+=from->length; - to->text=ck_realloc(to->text,to->alloc); - } - bcopy(from->text,to->text+to->length,from->length); - to->length+=from->length; -} - -/* Append 'length' bytes from 'string' to the line 'to' - This routine *will* append bytes with nulls in them, without - failing. */ -void -str_append(to,string,length) -struct line *to; -char *string; -int length; -{ - if(length>to->alloc-to->length) { - to->alloc+=length; - to->text=ck_realloc(to->text,to->alloc); - } - bcopy(string,to->text+to->length,length); - to->length+=length; -} - -void -usage() -{ - fprintf(stderr, "\ -Usage: %s [-nV] [+quiet] [+silent] [+version] [-e script] [-f script-file]\n\ - [+expression=script] [+file=script-file] [file...]\n", myname); - exit(4); -} diff --git a/usr.bin/sed/sed.test b/usr.bin/sed/sed.test new file mode 100644 index 0000000000..cce9b43a42 --- /dev/null +++ b/usr.bin/sed/sed.test @@ -0,0 +1,545 @@ +#!/bin/sh - +# +# Copyright (c) 1992 Diomidis Spinellis. +# Copyright (c) 1992 The Regents of the University of California. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)sed.test 5.6 (Berkeley) 8/28/92 +# + +# sed Regression Tests +# +# The following files are created: +# lines[1-4], script1, script2 +# Two directories *.out contain the test results + +main() +{ + BASE=/usr/old/bin/sed + BASELOG=sed.out + TEST=../obj/sed + TESTLOG=nsed.out + DICT=/usr/share/dict/words + + test_error | more + + awk 'END { for (i = 1; i < 15; i++) print "l1_" i}' </dev/null >lines1 + awk 'END { for (i = 1; i < 10; i++) print "l2_" i}' </dev/null >lines2 + + exec 4>&1 5>&2 + + # Set these flags to get messages about known problems + BSD=1 + GNU=0 + SUN=0 + tests $BASE $BASELOG + + BSD=0 + GNU=0 + SUN=0 + tests $TEST $TESTLOG + exec 1>&4 2>&5 + diff -c $BASELOG $TESTLOG | more +} + +tests() +{ + SED=$1 + DIR=$2 + rm -rf $DIR + mkdir $DIR + MARK=100 + + test_args + test_addr + echo Testing commands + test_group + test_acid + test_branch + test_pattern + test_print + test_subst +} + +mark() +{ + MARK=`expr $MARK + 1` + exec 1>&4 2>&5 + exec >"$DIR/${MARK}_$1" + echo "Test $1:$MARK" + # Uncomment this line to match tests with sed error messages + echo "Test $1:$MARK" >&5 +} + +test_args() +{ + mark '1.1' + echo Testing argument parsing + echo First type + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED 's/^/e1_/p' lines1 + fi + mark '1.2' ; $SED -n 's/^/e1_/p' lines1 + mark '1.3' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED 's/^/e1_/p' <lines1 + fi + mark '1.4' ; $SED -n 's/^/e1_/p' <lines1 + echo Second type + mark '1.4.1' + if [ $SUN -eq 1 ] ; then + echo SunOS sed fails this + fi + $SED -e '' <lines1 + echo 's/^/s1_/p' >script1 + echo 's/^/s2_/p' >script2 + mark '1.5' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -f script1 lines1 + fi + mark '1.6' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -f script1 <lines1 + fi + mark '1.7' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -e 's/^/e1_/p' lines1 + fi + mark '1.8' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -e 's/^/e1_/p' <lines1 + fi + mark '1.9' ; $SED -n -f script1 lines1 + mark '1.10' ; $SED -n -f script1 <lines1 + mark '1.11' ; $SED -n -e 's/^/e1_/p' lines1 + mark '1.12' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -n -e 's/^/e1_/p' <lines1 + fi + mark '1.13' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -e 's/^/e1_/p' -e 's/^/e2_/p' lines1 + fi + mark '1.14' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -f script1 -f script2 lines1 + fi + mark '1.15' + if [ $GNU -eq 1 -o $SUN -eq 1 ] ; then + echo GNU and SunOS sed fail this following older POSIX draft + else + $SED -e 's/^/e1_/p' -f script1 lines1 + fi + mark '1.16' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -e 's/^/e1_/p' lines1 lines1 + fi + # POSIX D11.2:11251 + mark '1.17' ; $SED p <lines1 lines1 +cat >script1 <<EOF +#n +# A comment + +p +EOF + mark '1.18' ; $SED -f script1 <lines1 lines1 +} + +test_addr() +{ + echo Testing address ranges + mark '2.1' ; $SED -n -e '4p' lines1 + mark '2.2' ; $SED -n -e '20p' lines1 lines2 + mark '2.3' ; $SED -n -e '$p' lines1 + mark '2.4' ; $SED -n -e '$p' lines1 lines2 + mark '2.5' ; $SED -n -e '$a\ +hello' /dev/null + mark '2.6' ; $SED -n -e '$p' lines1 /dev/null lines2 + # Should not print anything + mark '2.7' ; $SED -n -e '20p' lines1 + mark '2.8' ; $SED -n -e '0p' lines1 + mark '2.9' ; $SED -n '/l1_7/p' lines1 + mark '2.10' ; $SED -n ' /l1_7/ p' lines1 + mark '2.11' + if [ $BSD -eq 1 ] ; then + echo BSD sed fails this test + fi + if [ $GNU -eq 1 ] ; then + echo GNU sed fails this + fi + $SED -n '\_l1\_7_p' lines1 + mark '2.12' ; $SED -n '1,4p' lines1 + mark '2.13' ; $SED -n '1,$p' lines1 lines2 + mark '2.14' ; $SED -n '1,/l2_9/p' lines1 lines2 + mark '2.15' ; $SED -n '/4/,$p' lines1 lines2 + mark '2.16' ; $SED -n '/4/,20p' lines1 lines2 + mark '2.17' ; $SED -n '/4/,/10/p' lines1 lines2 + mark '2.18' ; $SED -n '/l2_3/,/l1_8/p' lines1 lines2 + mark '2.19' + if [ $GNU -eq 1 ] ; then + echo GNU sed fails this + fi + $SED -n '12,3p' lines1 lines2 + mark '2.20' + if [ $GNU -eq 1 ] ; then + echo GNU sed fails this + fi + $SED -n '/l1_7/,3p' lines1 lines2 +} + +test_group() +{ + echo Brace and other grouping + mark '3.1' ; $SED -e ' +4,12 { + s/^/^/ + s/$/$/ + s/_/T/ +}' lines1 + mark '3.2' ; $SED -e ' +4,12 { + s/^/^/ + /6/,/10/ { + s/$/$/ + /8/ s/_/T/ + } +}' lines1 + mark '3.3' ; $SED -e ' +4,12 !{ + s/^/^/ + /6/,/10/ !{ + s/$/$/ + /8/ !s/_/T/ + } +}' lines1 + mark '3.4' ; $SED -e '4,12!s/^/^/' lines1 +} + +test_acid() +{ + echo Testing a c d and i commands + mark '4.1' ; $SED -n -e ' +s/^/before_i/p +20i\ +inserted +s/^/after_i/p +' lines1 lines2 + mark '4.2' ; $SED -n -e ' +5,12s/^/5-12/ +s/^/before_a/p +/5-12/a\ +appended +s/^/after_a/p +' lines1 lines2 + mark '4.3' + if [ $GNU -eq 1 ] ; then + echo GNU sed fails this + fi + $SED -n -e ' +s/^/^/p +/l1_/a\ +appended +8,10N +s/$/$/p +' lines1 lines2 + mark '4.4' ; $SED -n -e ' +c\ +hello +' lines1 + mark '4.5' ; $SED -n -e ' +8c\ +hello +' lines1 + mark '4.6' ; $SED -n -e ' +3,14c\ +hello +' lines1 +# SunOS and GNU sed behave differently. We follow POSIX +# mark '4.7' ; $SED -n -e ' +#8,3c\ +#hello +#' lines1 + mark '4.8' ; $SED d <lines1 +} + +test_branch() +{ + echo Testing labels and branching + mark '5.1' ; $SED -n -e ' +b label4 +:label3 +s/^/label3_/p +b end +:label4 +2,12b label1 +b label2 +:label1 +s/^/label1_/p +b +:label2 +s/^/label2_/p +b label3 +:end +' lines1 + mark '5.2' + if [ $BSD -eq 1 ] ; then + echo BSD sed fails this test + fi + $SED -n -e ' +s/l1_/l2_/ +t ok +b +:ok +s/^/tested /p +' lines1 lines2 +# SunOS sed behaves differently here. Clarification needed. +# mark '5.3' ; $SED -n -e ' +#5,8b inside +#1,5 { +# s/^/^/p +# :inside +# s/$/$/p +#} +#' lines1 +# Check that t clears the substitution done flag + mark '5.4' ; $SED -n -e ' +1,8s/^/^/ +t l1 +:l1 +t l2 +s/$/$/p +b +:l2 +s/^/ERROR/ +' lines1 +# Check that reading a line clears the substitution done flag + mark '5.5' + if [ $BSD -eq 1 ] ; then + echo BSD sed fails this test + fi + $SED -n -e ' +t l2 +1,8s/^/^/p +2,7N +b +:l2 +s/^/ERROR/p +' lines1 + mark '5.6' ; $SED 5q lines1 + mark '5.7' ; $SED -e ' +5i\ +hello +5q' lines1 +} + +test_pattern() +{ +echo Pattern space commands +# Check that the pattern space is deleted + mark '6.1' ; $SED -n -e ' +c\ +changed +p +' lines1 + mark '6.2' ; $SED -n -e ' +4d +p +' lines1 +# SunOS sed refused to print here +# mark '6.3' ; $SED -e ' +#N +#N +#N +#D +#P +#4p +#' lines1 + mark '6.4' ; $SED -e ' +2h +3H +4g +5G +6x +6p +6x +6p +' lines1 + mark '6.5' ; $SED -e '4n' lines1 + mark '6.6' ; $SED -n -e '4n' lines1 +} + +test_print() +{ + echo Testing print and file routines + awk 'END {for (i = 1; i < 256; i++) printf("%c", i);print "\n"}' \ + </dev/null >lines3 + # GNU and SunOS sed behave differently here + mark '7.1' + if [ $BSD -eq 1 ] ; then + echo 'BSD sed drops core on this one; TEST SKIPPED' + else + $SED -n l lines3 + fi + mark '7.2' ; $SED -e '/l2_/=' lines1 lines2 + rm -f lines4 + mark '7.3' ; $SED -e '3,12w lines4' lines1 + echo w results + cat lines4 + mark '7.4' ; $SED -e '4r lines2' lines1 + mark '7.5' ; $SED -e '5r /dev/dds' lines1 + mark '7.6' ; $SED -e '6r /dev/null' lines1 + mark '7.7' + if [ $BSD -eq 1 -o $GNU -eq 1 -o $SUN -eq 1 ] ; then + echo BSD, GNU and SunOS cannot pass this one + else + sed '200q' $DICT | sed 's$.*$s/^/&/w tmpdir/&$' >script1 + rm -rf tmpdir + mkdir tmpdir + $SED -f script1 lines1 + cat tmpdir/* + rm -rf tmpdir + fi + mark '7.8' + if [ $BSD -eq 1 ] ; then + echo BSD sed cannot pass 7.7 + else + echo line1 > lines3 + echo "" >> lines3 + $SED -n -e '$p' lines3 /dev/null + fi + +} + +test_subst() +{ + echo Testing substitution commands + mark '8.1' ; $SED -e 's/./X/g' lines1 + mark '8.2' ; $SED -e 's,.,X,g' lines1 +# GNU and SunOS sed thinks we are escaping . as wildcard, not as separator +# mark '8.3' ; $SED -e 's.\..X.g' lines1 +# POSIX does not say that this should work +# mark '8.4' ; $SED -e 's/[/]/Q/' lines1 + mark '8.4' ; $SED -e 's/[\/]/Q/' lines1 + mark '8.5' ; $SED -e 's_\__X_' lines1 + mark '8.6' ; $SED -e 's/./(&)/g' lines1 + mark '8.7' ; $SED -e 's/./(\&)/g' lines1 + mark '8.8' ; $SED -e 's/\(.\)\(.\)\(.\)/x\3x\2x\1/g' lines1 + mark '8.9' ; $SED -e 's/_/u0\ +u1\ +u2/g' lines1 + mark '8.10' + if [ $BSD -eq 1 -o $GNU -eq 1 ] ; then + echo 'BSD/GNU sed do not understand digit flags on s commands' + fi + $SED -e 's/./X/4' lines1 + rm -f lines4 + mark '8.11' ; $SED -e 's/1/X/w lines4' lines1 + echo s wfile results + cat lines4 + mark '8.12' ; $SED -e 's/[123]/X/g' lines1 + mark '8.13' ; $SED -e 'y/0123456789/9876543210/' lines1 + mark '8.14' ; + if [ $BSD -eq 1 -o $GNU -eq 1 -o $SUN -eq 1 ] ; then + echo BSD/GNU/SUN sed fail this test + else + $SED -e 'y10\123456789198765432\101' lines1 + fi + mark '8.15' ; $SED -e '1N;2y/\n/X/' lines1 + mark '8.16' + if [ $BSD -eq 1 ] ; then + echo 'BSD sed does not handle branch defined REs' + else + echo 'eeefff' | $SED -e 'p' -e 's/e/X/p' -e ':x' \ + -e 's//Y/p' -e '/f/bx' + fi +} + +test_error() +{ + exec 0>&3 4>&1 5>&2 + exec 0</dev/null + exec 2>&1 + set -x + $TEST -x && exit 1 + $TEST -f && exit 1 + $TEST -e && exit 1 + $TEST -f /dev/dds && exit 1 + $TEST p /dev/dds && exit 1 + $TEST -f /bin/sh && exit 1 + $TEST '{' && exit 1 + $TEST '{' && exit 1 + $TEST '/hello/' && exit 1 + $TEST '1,/hello/' && exit 1 + $TEST -e '-5p' && exit 1 + $TEST '/jj' && exit 1 + $TEST 'a hello' && exit 1 + $TEST 'a \ hello' && exit 1 + $TEST 'b foo' && exit 1 + $TEST 'd hello' && exit 1 + $TEST 's/aa' && exit 1 + $TEST 's/aa/' && exit 1 + $TEST 's/a/b' && exit 1 + $TEST 's/a/b/c/d' && exit 1 + $TEST 's/a/b/ 1 2' && exit 1 + $TEST 's/a/b/ 1 g' && exit 1 + $TEST 's/a/b/w' && exit 1 + $TEST 'y/aa' && exit 1 + $TEST 'y/aa/b/' && exit 1 + $TEST 'y/aa/' && exit 1 + $TEST 'y/a/b' && exit 1 + $TEST 'y/a/b/c/d' && exit 1 + $TEST '!' && exit 1 + $TEST supercalifrangolisticexprialidociussupercalifrangolisticexcius + set +x + exec 0>&3 1>&4 2>&5 +} + +main diff --git a/usr.bin/sed/utils.c b/usr.bin/sed/utils.c deleted file mode 100644 index 2d39cfc9cb..0000000000 --- a/usr.bin/sed/utils.c +++ /dev/null @@ -1,359 +0,0 @@ -/* Functions from hack's utils library. - Copyright (C) 1989-1991 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* These routines were written as part of a library (by hack), but since most - people don't have the library, here they are. */ - -#ifdef __STDC__ -#define VOID void -#else -#define VOID char -#endif - -#include <stdio.h> -#if defined(USG) || defined(STDC_HEADERS) -#include <string.h> -#define bcopy(src, dst, len) memcpy((dst), (src), (len)) -#else -#endif -#if defined(STDC_HEADERS) -#include <stdlib.h> -#else -VOID *malloc(); -VOID *realloc(); -#endif - -VOID *ck_malloc(); - -char *myname; - -#ifdef __STDC__ -#include <stdarg.h> - -/* Print an error message and exit */ -void -panic(char *str, ...) -{ - va_list iggy; - - fprintf(stderr,"%s: ",myname); - va_start(iggy,str); -#ifdef NO_VFPRINTF - _doprnt(str,&iggy,stderr); -#else - vfprintf(stderr,str,iggy); -#endif - va_end(iggy); - putc('\n',stderr); - exit(4); -} - -#else -#include <varargs.h> - -void -panic(str,va_alist) -char *str; -va_dcl -{ - va_list iggy; - - fprintf(stderr,"%s: ",myname); - va_start(iggy); -#ifdef NO_VFPRINTF - _doprnt(str,&iggy,stderr); -#else - vfprintf(stderr,str,iggy); -#endif - va_end(iggy); - putc('\n',stderr); - exit(4); -} - -#endif - -/* Store information about files opened with ck_fopen - so that error messages from ck_fread, etc can print the - name of the file that had the error */ -#define N_FILE 32 - -struct id { - FILE *fp; - char *name; -}; - -static struct id __id_s[N_FILE]; - -/* Internal routine to get a filename from __id_s */ -char * -__fp_name(fp) -FILE *fp; -{ - int n; - - for(n=0;n<N_FILE;n++) { - if(__id_s[n].fp==fp) - return __id_s[n].name; - } - return "{Unknown file pointer}"; -} - -/* Panic on failing fopen */ -FILE * -ck_fopen(name,mode) -char *name; -char *mode; -{ - FILE *ret; - int n; - - ret=fopen(name,mode); - if(ret==(FILE *)0) - panic("Couldn't open file %s",name); - for(n=0;n<N_FILE;n++) { - if(ret==__id_s[n].fp) { - free((VOID *)__id_s[n].name); - __id_s[n].name=(char *)ck_malloc(strlen(name)+1); - strcpy(__id_s[n].name,name); - break; - } - } - if(n==N_FILE) { - for(n=0;n<N_FILE;n++) - if(__id_s[n].fp==(FILE *)0) - break; - if(n==N_FILE) - panic("Internal error: too many files open"); - __id_s[n].fp=ret; - __id_s[n].name=(char *)ck_malloc(strlen(name)+1); - strcpy(__id_s[n].name,name); - } - return ret; -} - -/* Panic on failing fwrite */ -void -ck_fwrite(ptr,size,nmemb,stream) -char *ptr; -int size,nmemb; -FILE *stream; -{ - if(fwrite(ptr,size,nmemb,stream)!=nmemb) - panic("couldn't write %d items to %s",nmemb,__fp_name(stream)); -} - -/* Panic on failing fclose */ -void -ck_fclose(stream) -FILE *stream; -{ - if(fclose(stream)==EOF) - panic("Couldn't close %s",__fp_name(stream)); -} - -/* Panic on failing malloc */ -VOID * -ck_malloc(size) -int size; -{ - VOID *ret; - - if(!size) - size++; - ret=malloc(size); - if(ret==(VOID *)0) - panic("Couldn't allocate memory"); - return ret; -} - -/* Panic on failing realloc */ -VOID * -ck_realloc(ptr,size) -VOID *ptr; -int size; -{ - VOID *ret; - - ret=realloc(ptr,size); - if(ret==(VOID *)0) - panic("Couldn't re-allocate memory"); - return ret; -} - -/* Return a malloc()'d copy of a string */ -char * -ck_strdup(str) -char *str; -{ - char *ret; - - ret=(char *)ck_malloc(strlen(str)+2); - strcpy(ret,str); - return ret; -} - -#if !defined(USG) && !defined(STDC_HEADERS) -/* - * memchr - search for a byte - * - */ - -VOID * -memchr(s, ucharwanted, size) -VOID *s; -int ucharwanted; -int size; -{ - register char *scan; - register n; - register uc; - - scan = (char *)s; - uc = (ucharwanted&0xFF); - for (n = size; n > 0; n--) - if (((*scan)&0xFF) == uc) - return((VOID *)scan); - else - scan++; - - return 0; -} -#endif - -#if !defined(STDC_HEADERS) -/* - * memmove - copy bytes, being careful about overlap. - */ - -VOID * -memmove(dst, src, size) -VOID *dst; -VOID *src; -int size; -{ - register char *d; - register char *s; - register int n; - - if (size <= 0) - return(dst); - - s = (char *)src; - d = (char *)dst; - if (s <= d && s + (size-1) >= d) { - /* Overlap, must copy right-to-left. */ - s += size-1; - d += size-1; - for (n = size; n > 0; n--) - *d-- = *s--; - } else - for (n = size; n > 0; n--) - *d++ = *s++; - - return(dst); -} -#endif - -/* Implement a variable sized buffer of 'stuff'. We don't know what it is, - nor do we care, as long as it doesn't mind being aligned by malloc. */ - -struct buffer { - int allocated; - int length; - char *b; -}; - -#define MIN_ALLOCATE 50 - -VOID * -init_buffer() -{ - struct buffer *b; - - b=(struct buffer *)ck_malloc(sizeof(struct buffer)); - b->allocated=MIN_ALLOCATE; - b->b=(char *)ck_malloc(MIN_ALLOCATE); - b->length=0; - return (VOID *)b; -} - -void -flush_buffer(bb) -VOID *bb; -{ - struct buffer *b; - - b=(struct buffer *)bb; - free(b->b); - b->b=0; - b->allocated=0; - b->length=0; - free(b); -} - -int -size_buffer(b) -VOID *b; -{ - struct buffer *bb; - - bb=(struct buffer *)b; - return bb->length; -} - -void -add_buffer(bb,p,n) -VOID *bb; -char *p; -int n; -{ - struct buffer *b; - - b=(struct buffer *)bb; - if(b->length+n>b->allocated) { - b->allocated*=2; - b->b=(char *)ck_realloc(b->b,b->allocated); - } - bcopy(p,b->b+b->length,n); - b->length+=n; -} - -void -add1_buffer(bb,ch) -VOID *bb; -int ch; -{ - struct buffer *b; - - b=(struct buffer *)bb; - if(b->length+1>b->allocated) { - b->allocated*=2; - b->b=(char *)ck_realloc(b->b,b->allocated); - } - b->b[b->length]=ch; - b->length++; -} - -char * -get_buffer(bb) -VOID *bb; -{ - struct buffer *b; - - b=(struct buffer *)bb; - return b->b; -} diff --git a/usr.bin/shar/Makefile b/usr.bin/shar/Makefile index 3cb889a45a..c01956adbb 100644 --- a/usr.bin/shar/Makefile +++ b/usr.bin/shar/Makefile @@ -1,14 +1,11 @@ # @(#)Makefile 5.1 (Berkeley) 5/23/90 -MAN1= shar.0 +MAN1= shar.1 all shar: ${MAN1} clean depend lint tags: -cleandir: - rm -f ${MAN1} - beforeinstall: install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ ${.CURDIR}/shar.sh ${DESTDIR}/usr/bin/shar diff --git a/usr.bin/showmount/Makefile b/usr.bin/showmount/Makefile index ee60854783..957f3c5e08 100644 --- a/usr.bin/showmount/Makefile +++ b/usr.bin/showmount/Makefile @@ -1,6 +1,7 @@ # @(#)Makefile 6.2 (Berkeley) 5/11/90 PROG= showmount +MAN8= showmount.8 DPADD= ${LIBRPC} LDADD= -lrpc diff --git a/usr.bin/size/size.1 b/usr.bin/size/size.1 index d757bf31c9..56406a4f7a 100644 --- a/usr.bin/size/size.1 +++ b/usr.bin/size/size.1 @@ -29,9 +29,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)size.1 6.6 (Berkeley) 7/1/91 +.\" @(#)size.1 6.7 (Berkeley) 3/2/92 .\" -.Dd July 1, 1991 +.Dd March 2, 1992 .Dt SIZE 1 .Os .Sh NAME @@ -42,15 +42,15 @@ .Op Ar object_file ... .Sh DESCRIPTION .Nm Size -displays the text, data and bss segment sizes of the given +displays the text, data and bss segment sizes of the specified .Ar object_file -in bytes (decimal), and the sum of the three segments (hexidecimal and -decimal). -If +in bytes (in decimal), and the sum of the three segments (in +decimal and hexidecimal). +If no .Ar object_file -is not given, +is specified .Nm -automatically searches for the file +attempts to report on the file .Pa a.out . .Sh SEE ALSO .Xr a.out 5 diff --git a/usr.bin/size/size.c b/usr.bin/size/size.c index 8cf278f1f1..f9edda6fd0 100644 --- a/usr.bin/size/size.c +++ b/usr.bin/size/size.c @@ -38,51 +38,111 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)size.c 4.7 (Berkeley) 6/1/90"; +static char sccsid[] = "@(#)size.c 5.1 (Berkeley) 3/2/92"; #endif /* not lint */ #include <sys/param.h> #include <sys/file.h> +#include <errno.h> #include <a.out.h> +#include <unistd.h> +#include <stdlib.h> #include <stdio.h> +#include <string.h> +void err __P((const char *, ...)); +int show __P((int, char *)); +void usage __P((void)); + +int main(argc, argv) int argc; - char **argv; + char *argv[]; { + int ch, eval; + + while ((ch = getopt(argc, argv, "")) != EOF) + switch(ch) { + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + eval = 0; + if (*argv) + do { + eval |= show(argc, *argv); + } while (*++argv); + else + eval |= show(1, "a.out"); + exit(eval); +} + +int +show(count, name) + int count; + char *name; +{ + static int first = 1; struct exec head; u_long total; - int exval, fd, first; + int fd; - if (!*argv[1]) - *argv = "a.out"; - else - ++argv; - for (first = 1, exval = 0; *argv; ++argv) { - if ((fd = open(*argv, O_RDONLY, 0)) < 0) { - fprintf(stderr, "size: "); - perror(*argv); - exval = 1; - continue; - } - if (read(fd, (char *)&head, sizeof(head)) != sizeof(head) || - N_BADMAG(head)) { - fprintf(stderr, "size: %s: not in a.out format.\n", - *argv); - exval = 1; - continue; - } - (void)close(fd); - if (first) { - first = 0; - printf("text\tdata\tbss\tdec\thex\n"); - } - total = head.a_text + head.a_data + head.a_bss; - printf("%lu\t%lu\t%lu\t%lu\t%lx", head.a_text, head.a_data, - head.a_bss, total, total); - if (argc > 2) - printf("\t%s", *argv); - printf("\n"); + if ((fd = open(name, O_RDONLY, 0)) < 0) { + err("%s: %s", name, strerror(errno)); + return (1); + } + if (read(fd, &head, sizeof(head)) != sizeof(head) || N_BADMAG(head)) { + err("%s: not in a.out format", name); + return (1); + } + (void)close(fd); + + if (first) { + first = 0; + (void)printf("text\tdata\tbss\tdec\thex\n"); } - exit(exval); + total = head.a_text + head.a_data + head.a_bss; + (void)printf("%lu\t%lu\t%lu\t%lu\t%lx", head.a_text, head.a_data, + head.a_bss, total, total); + if (count > 1) + (void)printf("\t%s", name); + (void)printf("\n"); + return (0); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: size [file ...]\n"); + exit(1); +} + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "size: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); } diff --git a/usr.bin/strip/Makefile b/usr.bin/strip/Makefile index bfb2b484b5..aea2ce36e2 100644 --- a/usr.bin/strip/Makefile +++ b/usr.bin/strip/Makefile @@ -1,4 +1,5 @@ -# @(#)Makefile 5.4 (Berkeley) 5/11/90 +# from: @(#)Makefile 5.4 (Berkeley) 5/11/90 +# $Id$ PROG= strip @@ -6,6 +7,5 @@ install: maninstall install -c -o ${BINOWN} -g ${BINOWN} -m ${BINMODE} strip \ ${DESTDIR}${BINDIR} ./strip ${DESTDIR}${BINDIR}/strip - rm -f ./strip .include <bsd.prog.mk> diff --git a/usr.bin/strip/strip.1 b/usr.bin/strip/strip.1 index d52cb4adc6..5aba1a900e 100644 --- a/usr.bin/strip/strip.1 +++ b/usr.bin/strip/strip.1 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)strip.1 6.6 (Berkeley) 5/26/91 +.\" from: @(#)strip.1 6.6 (Berkeley) 5/26/91 +.\" $Id: strip.1,v 1.3 1993/08/27 02:33:34 brezak Exp $ .\" .Dd May 26, 1991 .Dt STRIP 1 @@ -40,6 +41,7 @@ .Sh SYNOPSIS .Nm strip .Op Fl d +.Op Fl x .Ar file ... .Sh DESCRIPTION The @@ -54,6 +56,8 @@ The options are as follows: .Bl -tag -width Ds .It Fl d Delete only debugging and empty symbols. +.It Fl x +Delete only debugging, compiler identification, and local symbols. .El .Pp .Nm Strip diff --git a/usr.bin/strip/strip.c b/usr.bin/strip/strip.c index 182e8f5b6f..570d048f5c 100644 --- a/usr.bin/strip/strip.c +++ b/usr.bin/strip/strip.c @@ -38,11 +38,13 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)strip.c 5.7 (Berkeley) 5/26/91"; +/*static char sccsid[] = "from: @(#)strip.c 5.8 (Berkeley) 11/6/91";*/ +static char rcsid[] = "$Id: strip.c,v 1.6 1993/09/07 16:12:15 brezak Exp $"; #endif /* not lint */ #include <sys/types.h> #include <sys/stat.h> +#include <sys/mman.h> #include <fcntl.h> #include <errno.h> #include <a.out.h> @@ -54,13 +56,15 @@ static char sccsid[] = "@(#)strip.c 5.7 (Berkeley) 5/26/91"; typedef struct exec EXEC; typedef struct nlist NLIST; +#define strx n_un.n_strx + void err __P((const char *fmt, ...)); void s_stab __P((const char *, int, EXEC *)); void s_sym __P((const char *, int, EXEC *)); void usage __P((void)); -int eval; - +int xflag = 0; + main(argc, argv) int argc; char *argv[]; @@ -72,8 +76,11 @@ main(argc, argv) char *fn; sfcn = s_sym; - while ((ch = getopt(argc, argv, "d")) != EOF) + while ((ch = getopt(argc, argv, "dx")) != EOF) switch(ch) { + case 'x': + xflag = 1; + /*FALLTHROUGH*/ case 'd': sfcn = s_stab; break; @@ -98,7 +105,7 @@ main(argc, argv) if (close(fd)) err("%s: %s", fn, strerror(errno)); } - exit(eval); + exit(0); } void @@ -107,7 +114,6 @@ s_sym(fn, fd, ep) int fd; register EXEC *ep; { - static int pagesize = -1; register off_t fsize; /* If no symbols or data/text relocation info, quit. */ @@ -119,11 +125,7 @@ s_sym(fn, fd, ep) * and NMAGIC formats have the text/data immediately following the * header. ZMAGIC format wastes the rest of of header page. */ - if (ep->a_magic == ZMAGIC) - fsize = pagesize == -1 ? (pagesize = getpagesize()) : pagesize; - else - fsize = sizeof(EXEC); - fsize += ep->a_text + ep->a_data; + fsize = N_RELOFF(*ep); /* Set symbol size and relocation info values to 0. */ ep->a_syms = ep->a_trsize = ep->a_drsize = 0; @@ -141,117 +143,84 @@ s_stab(fn, fd, ep) int fd; EXEC *ep; { + register int cnt, len, nsymcnt; + register char *nstr, *nstrbase, *p, *strbase; + register NLIST *sym, *nsym; struct stat sb; - register NLIST *bsym2, *sym1, *sym2; - register u_long nsym1, nsym2; - register char *p, *bstr2, *str1, *str2; - register int len, symlen; - off_t fsize; - int nb; - char *bp; + NLIST *symbase; /* Quit if no symbols. */ if (ep->a_syms == 0) return; - bsym2 = NULL; - bp = bstr2 = NULL; + /* Map the file. */ + if (fstat(fd, &sb) || + (ep = (EXEC *)mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, fd, (off_t)0)) == (EXEC *)-1) + err("%s: %s", fn, strerror(errno)); - /* Read the file into memory. XXX mmap */ - if (fstat(fd, &sb)) - goto syserr; - if ((bp = malloc(sb.st_size)) == NULL) - goto syserr; - if (lseek(fd, 0L, SEEK_SET) == -1) - goto syserr; - if ((nb = read(fd, bp, (int)sb.st_size)) == -1) - goto syserr; - if (nb != sb.st_size) { - errno = EIO; - goto syserr; - } + /* + * Initialize old and new symbol pointers. They both point to the + * beginning of the symbol table in memory, since we're deleting + * entries. + */ + sym = nsym = symbase = (NLIST *)((char *)ep + N_SYMOFF(*ep)); /* - * Allocate space for new symbol and string tables. Allocate before - * reading the symbol tables so we can do it all in a single pass. - * This loses if there weren't any symbols to strip, but that's life. + * Allocate space for the new string table, initialize old and + * new string pointers. Handle the extra long at the beginning + * of the string table. */ - sym1 = (NLIST *)(bp + N_SYMOFF(*ep)); - if ((bsym2 = sym2 = malloc((u_int)ep->a_syms)) == NULL) { - err("%s", strerror(errno)); - goto mem; - } - str1 = bp + N_STROFF(*ep); - if ((bstr2 = malloc((u_int)*(u_long *)str1)) == NULL) { + strbase = (char *)ep + N_STROFF(*ep); + if ((nstrbase = malloc((u_int)*(u_long *)strbase)) == NULL) err("%s", strerror(errno)); - goto mem; - } - str2 = bstr2 + sizeof(u_long); - symlen = sizeof(u_long); + nstr = nstrbase + sizeof(u_long); /* * Read through the symbol table. For each non-debugging symbol, - * copy it into the new symbol and string tables. Keep track of - * how many symbols are copied and the length of the new string - * table. + * copy it and save its string in the new string table. Keep + * track of the number of symbols. */ -#define strx n_un.n_strx - nsym2 = 0; - for (nsym1 = ep->a_syms / sizeof(NLIST); nsym1--; ++sym1) - if (!(sym1->n_type & N_STAB) && sym1->strx) { - *sym2 = *sym1; - sym2->strx = str2 - bstr2; - p = str1 + sym1->strx; + for (cnt = ep->a_syms / sizeof(NLIST); cnt--; ++sym) + if (!(sym->n_type & N_STAB) && sym->strx) { + *nsym = *sym; + nsym->strx = nstr - nstrbase; + p = strbase + sym->strx; + if (xflag && + (!(sym->n_type & N_EXT) || + (sym->n_type & ~N_EXT) == N_FN || + strcmp(p, "gcc_compiled.") == 0 || + strcmp(p, "gcc2_compiled.") == 0 || + strcmp(p, "___gnu_compiled_c") == 0)) { + continue; + } len = strlen(p) + 1; - bcopy(p, str2, len); - symlen += len; - str2 += len; - ++sym2; - ++nsym2; + bcopy(p, nstr, len); + nstr += len; + ++nsym; } - /* If no debugging symbols, quit. */ - if (!nsym2) - goto mem; - /* Fill in new symbol table size. */ - ep->a_syms = nsym2 * sizeof(NLIST); - - /* Write out the header. */ - if (lseek(fd, 0L, SEEK_SET) == -1 || - write(fd, ep, sizeof(EXEC)) != sizeof(EXEC)) - goto syserr; - - /* Write out the symbol table. */ - if (lseek(fd, N_SYMOFF(*ep), SEEK_SET) == -1 || - write(fd, bsym2, ep->a_syms) != ep->a_syms) - goto syserr; + ep->a_syms = (nsym - symbase) * sizeof(NLIST); /* Fill in the new size of the string table. */ - *(u_long *)bstr2 = symlen; + *(u_long *)nstrbase = len = nstr - nstrbase; - /* Write out the string table. */ - if (write(fd, bstr2, symlen) != symlen) - goto syserr; + /* + * Copy the new string table into place. Nsym should be pointing + * at the address past the last symbol entry. + */ + bcopy(nstrbase, (void *)nsym, len); /* Truncate to the current length. */ - if ((fsize = lseek(fd, 0L, SEEK_CUR)) == -1) - goto syserr; - if (ftruncate(fd, fsize)) -syserr: err("%s: %s", fn, strerror(errno)); - -mem: if (bp) - free(bp); - if (bstr2) - free(bstr2); - if (bsym2) - free(bsym2); + if (ftruncate(fd, (char *)nsym + len - (char *)ep)) + err("%s: %s", fn, strerror(errno)); + munmap((caddr_t)ep, sb.st_size); } void usage() { - (void)fprintf(stderr, "usage: strip [-d] file ...\n"); exit(1); } @@ -281,5 +250,5 @@ err(fmt, va_alist) (void)vfprintf(stderr, fmt, ap); va_end(ap); (void)fprintf(stderr, "\n"); - eval = 1; + exit(1); } diff --git a/usr.bin/su/Makefile b/usr.bin/su/Makefile index 32bdcd2274..f86450c37c 100644 --- a/usr.bin/su/Makefile +++ b/usr.bin/su/Makefile @@ -1,5 +1,12 @@ # @(#)Makefile 5.5 (Berkeley) 5/11/90 + +.if exists(/usr/lib/libcrypt.a) +#CFLAGS+=-DKERBEROS +DPADD+= ${LIBCRYPT} #${LIBKRB} +LDADD+= -lcrypt #-lkrb +.endif + PROG= su BINOWN= root BINMODE=4555 diff --git a/usr.bin/su/su.c b/usr.bin/su/su.c index 8a8edc802a..e024d818ec 100644 --- a/usr.bin/su/su.c +++ b/usr.bin/su/su.c @@ -163,11 +163,7 @@ main(argc, argv) /* if target requires a password, verify it */ if (*pwd->pw_passwd) { p = getpass("Password:"); -#ifdef DES if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) { -#else - if (strcmp(pwd->pw_passwd, p)) { -#endif fprintf(stderr, "Sorry\n"); syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s%s", username, diff --git a/usr.bin/symorder/symorder.1 b/usr.bin/symorder/symorder.1 index f113bcafd4..bc9ea866a1 100644 --- a/usr.bin/symorder/symorder.1 +++ b/usr.bin/symorder/symorder.1 @@ -61,7 +61,7 @@ Restrict the symbol table to the symbols listed in .Pp This program was specifically designed to cut down on the overhead of getting symbols from -.Pa /vmunix. +.Pa /386bsd. .Sh DIAGNOSTICS The .Nm symorder diff --git a/usr.bin/syscons/Makefile b/usr.bin/syscons/Makefile new file mode 100644 index 0000000000..f50f5a4ed0 --- /dev/null +++ b/usr.bin/syscons/Makefile @@ -0,0 +1,4 @@ +PROG = syscons +CFLAGS += -Wall -I${.CURDIR} + +.include <bsd.prog.mk> diff --git a/usr.bin/syscons/syscons.1 b/usr.bin/syscons/syscons.1 new file mode 100644 index 0000000000..f224f67b0d --- /dev/null +++ b/usr.bin/syscons/syscons.1 @@ -0,0 +1,96 @@ +.\" +.\" syscons - a utility for manipulating the syscons driver +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" @(#)syscons.1 +.\" +.TH syscons 1 "August 24, 1993" "" "FreeBSD" + +.SH NAME +syscons - a utility for manipulating the syscons VTY driver. +.SH SYNOPSIS +.na +.B syscons +.RB [options] +.SH DESCRIPTION +The +.B syscons +command is used to set various options for the syscons console driver, +such as font, keymap, current screen, etc. +.SH OPTIONS +.TP +The following command line options are supported. +.TP +.B \-v +Turns on verbose output. +.TP +.BI "\-s\ " N|off +Sets the screensaver timeout to +.I N +seconds, or turns it +.I off +. +.TP +.BI "\-m\ " 80x25|80x50 +Set screen size to 80x25 or 80x50 chars. +.TP +.BI "\-r\ " [ delay.repeat | slow | fast | normal ] +Set keyboard +.I delay +and +.I repeat +rates, or use presets for +.I slow, +.I fast +or +.I normal. +.TP +.BI "\-S\ " scrmap +Install screen output map file from +.I scrmap +.TP +.BI "\-k\ " mapfile +Install keyboard map file from +.I mapfile +.TP +.BI "\-f\ " size\ file +Load font +.I file +for +.I size +(currently, only 8, 14 or 16). +.TP +.BI "\-t\ " scr# +Set current virtual screen to be +.I scr# +.TP +.BI "\-F\ " FN#\ string +Set function key +.I FN# +to send +.I string +. +.PP +.SH FILES +/usr/share/syscons/fonts +/usr/share/syscons/keymaps +/usr/share/syscons/scrnmaps +.PP +.SH BUGS +Sure to be some. +.SH "SEE ALSO" +.BR /sys/i386/conf/SYSCONS +.SH AUTHORS +Christoph M. Robitschko +.TP +Sxren Schmidt (syscons driver & original code). +.TP +Jordan Hubbard (this man page). diff --git a/usr.bin/syscons/syscons.c b/usr.bin/syscons/syscons.c new file mode 100644 index 0000000000..0c2a79e1c6 --- /dev/null +++ b/usr.bin/syscons/syscons.c @@ -0,0 +1,500 @@ +/* + * Copyright (c) 1993 Christoph M. Robitschko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christoph M. Robitschko + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <machine/console.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <sys/errno.h> + +#include "syscons.h" + +int verbose = 0; +char *prgname; + + +__BEGIN_DECLS +int is_syscons __P((int)); +void screensaver __P((char *)); +void linemode __P((char *)); +void keyrate __P((char *)); +void keymap __P((char *)); +void mapscr __P((char *)); +void loadfont __P((char *, char *)); +void switchto __P((char *)); +void setfkey __P((char *, char *)); +void displayit __P((void)); +void usage __P((void)); +char *mkfullname __P((const char *, const char *, const char *)); +char *nextarg __P((int, char **, int *, int)); +__END_DECLS + + + +void +main(argc, argv) +int argc; +char **argv; +{ +extern char *optarg; +extern int optind; +int opt; + + + prgname = argv[0]; + if (!is_syscons(0)) + exit(1); + while((opt = getopt(argc, argv, "s:m:r:k:f:t:F:S:vd")) != -1) + switch(opt) { + case 's': + screensaver(optarg); + break; + case 'm': + linemode(optarg); + break; + case 'r': + keyrate(optarg); + break; + case 'k': + keymap(optarg); + break; + case 'S': + mapscr(optarg); + break; + case 'f': + loadfont(optarg, nextarg(argc, argv, &optind, 'f')); + break; + case 't': + switchto(optarg); + break; + case 'F': + setfkey(optarg, nextarg(argc, argv, &optind, 'F')); + break; + case 'v': + verbose ++; + break; + case 'd': + displayit(); + break; + default: + usage(); + exit(1); + } + if ((optind != argc) || (argc == 1)) { + usage(); + exit(1); + } + exit(0); +} + + +int +is_syscons(fd) +int fd; +{ +vtmode_t mode; + + /* Try to find out if we are running on a syscons VT */ + if (ioctl(fd, VT_GETMODE, &mode) == 0) + return(1); + if (errno == ENOTTY) { + printf("You must be running this from a syscons vty for it to work.\n\n"); + printf("If you are currently using pccons (default) and wish to have virtual\n"); + printf("consoles, then rebuild your kernel after replacing the line:\n\n"); + printf("\tdevice pc0 at isa? port \042IO_KBD\042 tty irq 1 vector pcrint\n"); + printf("\nwith:\n\n"); + printf("\tdevice sc0 at isa? port \042IO_KBD\042 tty irq 1 vector scintr\n"); + printf("\n(Then install the new kernel and reboot your system)\n"); + } + else + perror("getting console state"); + return(0); +} + + + +void +usage(void) +{ +const char usagestr[] = {"\ +Usage: syscons -v (be verbose)\n\ + -s {TIME|off} (set screensaver timeout to TIME seconds)\n\ + -m {80x25|80x50} (set screen to 25 or 50 lines)\n\ + -r DELAY.REPEAT (set keyboard delay & repeat rate)\n\ + -r fast (set keyboard delay & repeat to fast)\n\ + -r slow (set keyboard delay & repeat to slow)\n\ + -r normal (set keyboard delay & repeat to normal)\n\ + -k MAPFILE (load keyboard map file)\n\ + -f SIZE FILE (load font file of size 8, 14 or 16)\n\ + -t SCRNUM (switch to specified VT)\n\ + -F NUM STRING (set function key NUM to send STRING)\n\ + -S SCRNMAP (load screen map file)\n\ +"}; + fprintf(stderr, usagestr); +} + + +char * +nextarg(ac, av, indp, oc) +int ac; +char **av; +int *indp; +int oc; +{ + if (*indp < ac) + return(av[(*indp)++]); + fprintf(stderr, "%s: option requires two arguments -- %c\n", av[0], oc); + usage(); + exit(1); + return(""); +} + + + +char * +mkfullname(s1, s2, s3) +const char *s1, *s2, *s3; +{ +static char *buf = NULL; +static int bufl = 0; +int f; + + + f = strlen(s1) + strlen(s2) + strlen(s3) + 1; + if (f > bufl) + if (buf) + buf = (char *)realloc(buf, f); + else + buf = (char *)malloc(f); + if (!buf) { + bufl = 0; + return(NULL); + } + + bufl = f; + strcpy(buf, s1); + strcat(buf, s2); + strcat(buf, s3); + return(buf); +} + + +void +screensaver(opt) +char *opt; +{ +int nsec; +char *s1; + + + if (!strcmp(opt, "off")) + nsec = 0; + else { + nsec = strtol(opt, &s1, 0); + if ((nsec < 0) || (*opt == '\0') || (*s1 != '\0')) { + fprintf(stderr, "argument to -s must be a positive integer.\n"); + return; + } + } + + if (ioctl(0, CONS_BLANKTIME, &nsec) == -1) + perror("setting screensaver period"); +} + + +void +linemode(opt) +char *opt; +{ +unsigned long req; + + if (!strcmp(opt, "80x25")) + req = CONS_80x25TEXT; + else if (!strcmp(opt, "80x50")) + req = CONS_80x50TEXT; + else { + fprintf(stderr, "Unknown mode to -m: %s\n", opt); + return; + } + + if (ioctl(0, req, NULL) == -1) + perror("Setting line mode"); +} + + +void +keyrate(opt) +char *opt; +{ +const int delays[] = {250, 500, 750, 1000}; +const int repeats[] = { 34, 38, 42, 46, 50, 55, 59, 63, + 68, 76, 84, 92, 100, 110, 118, 126, + 136, 152, 168, 184, 200, 220, 236, 252, + 272, 304, 336, 368, 400, 440, 472, 504}; +const int ndelays = (sizeof(delays) / sizeof(int)); +const int nrepeats = (sizeof(repeats) / sizeof(int)); +struct { + int rep:5; + int del:2; + int pad:1; + } rate; + + if (!strcmp(opt, "slow")) + rate.del = 3, rate.rep = 31; + else if (!strcmp(opt, "normal")) + rate.del = 1, rate.rep = 15; + else if (!strcmp(opt, "fast")) + rate.del = rate.rep = 0; + else { + int n; + int delay, repeat; + char *v1; + + + delay = strtol(opt, &v1, 0); + if ((delay < 0) || (*v1 != '.')) + goto badopt; + opt = ++v1; + repeat = strtol(opt, &v1, 0); + if ((repeat < 0) || (*opt == '\0') || (*v1 != '\0')) { +badopt: + fprintf(stderr, "argument to -r must be DELAY.REPEAT\n"); + return; + } + for (n = 0; n < ndelays - 1; n++) + if (delay <= delays[n]) + break; + rate.del = n; + for (n = 0; n < nrepeats - 1; n++) + if (repeat <= repeats[n]) + break; + rate.rep = n; + } + + if (verbose) + fprintf(stderr, "setting keyboard rate to %d.%d\n", delays[rate.del], repeats[rate.rep]); + if (ioctl(0, KDSETRAD, rate) == -1) + perror("setting keyboard rate"); +} + + +void +keymap(opt) +char *opt; +{ +char *mapfn; +int mapfd; +keymap_t map; +int f; +const char *prefix[] = {"", "", KEYMAP_PATH, NULL}; +const char *postfix[] = {"", ".map", ".map"}; + + + mapfd = -1; + for (f = 0; prefix[f]; f++) { + mapfn = mkfullname(prefix[f], opt, postfix[f]); + mapfd = open(mapfn, O_RDONLY, 0); + if (verbose) + fprintf(stderr, "trying to open keymap file %s ... %s\n", mapfn, (mapfd==-1?"failed":"OK")); + if (mapfd >= 0) + break; + } + if (mapfd == -1) { + perror("Keymap file not found"); + return; + } + if ((read(mapfd, &map, sizeof(map)) != sizeof(map)) || + (read(mapfd, &map, 1) != 0)) { + fprintf(stderr, "\"%s\" is not in keymap format.\n", opt); + (void) close(mapfd); + return; + } + (void) close(mapfd); + if (ioctl(0, PIO_KEYMAP, &map) == -1) + perror("setting keyboard map"); +} + +void +mapscr(opt) +char *opt; +{ +char *mapfn; +int mapfd; +scrmap_t map; +int f; +const char *prefix[] = {"", "", SCRNMAP_PATH, NULL}; +const char *postfix[] = {"", ".scr", ".scr"}; + + + mapfd = -1; + for (f = 0; prefix[f]; f++) { + mapfn = mkfullname(prefix[f], opt, postfix[f]); + mapfd = open(mapfn, O_RDONLY, 0); + if (verbose) + fprintf(stderr, "trying to open scrnmap file %s ... %s\n", mapfn, (mapfd==-1?"failed":"OK")); + if (mapfd >= 0) + break; + } + if (mapfd == -1) { + perror("Scrnmap file not found"); + return; + } + if ((read(mapfd, &map, sizeof(map)) != sizeof(map)) || + (read(mapfd, &map, 1) != 0)) { + fprintf(stderr, "\"%s\" is not in scrnmap format.\n", opt); + (void) close(mapfd); + return; + } + (void) close(mapfd); + if (ioctl(0, PIO_SCRNMAP, &map) == -1) + perror("setting screen map"); +} + +void +loadfont(sizec, fname) +char *sizec; +char *fname; +{ +char *fontfn; +int fontfd; +void *font; +char *v1; +int ind, f; +const struct { + int fsize; + int msize; + unsigned long req; + } fontinfo[] = { + { 8, sizeof(fnt8_t), PIO_FONT8x8}, + { 14, sizeof(fnt14_t), PIO_FONT8x14}, + { 16, sizeof(fnt16_t), PIO_FONT8x16}, + { 0 }}; +const char *prefix[] = {"", "", FONT_PATH, FONT_PATH, NULL}; +const char *postfix[] = {"", ".fnt", "", ".fnt"}; + + + + f = strtol(sizec, &v1, 0); + for (ind = 0; fontinfo[ind].fsize; ind++) + if (fontinfo[ind].fsize == f) + break; + if ((fontinfo[ind].fsize == 0) || (*v1 != '\0')) { + fprintf(stderr, "%s is an unsupported font size.\n", sizec); + return; + } + font = (void *)malloc(fontinfo[ind].msize); + if (!font) { + fprintf(stderr, "loading font: Out of memory.\n"); + return; + } + + fontfd = -1; fontfn = ""; + for (f = 0; prefix[f]; f++) { + fontfn = mkfullname(prefix[f], fname, postfix[f]); + fontfd = open(fontfn, O_RDONLY, 0); + if (verbose) + fprintf(stderr, "trying to open font file %s ... %s\n", fontfn, (fontfd==-1?"failed":"OK")); + if (fontfd >= 0) + break; + } + if (fontfd == -1) { + perror("Font file not found"); + return; + } + if ((read(fontfd, font, fontinfo[ind].msize) != fontinfo[ind].msize) || + (read(fontfd, font, 1) != 0)) { + fprintf(stderr, "\"%s\" is not a font with size %s.\n", fontfn, sizec); + (void) close(fontfd); + return; + } + + if (ioctl(0, fontinfo[ind].req, font) == -1) + perror("Setting font"); +} + + +void +switchto(opt) +char *opt; +{ +int scrno; +char *v1; + + + scrno = strtol(opt, &v1, 0); + if ((scrno < 1) || (scrno > 12) || (*v1 != '\0')) { + fprintf(stderr, "argument to -t must be between 1 and 12.\n"); + return; + } + if (ioctl(0, VT_ACTIVATE, scrno) == -1) + perror("switching to new VT"); +} + + +void +setfkey(knumc, str) +char *knumc; +char *str; +{ +fkeyarg_t fkey; +char *v1; +long keynum; + + keynum = strtol(knumc, &v1, 0); + if (keynum < 0 || keynum > 59 || *v1 != '\0') { + fprintf(stderr, "function key number must be between 0 and 59.\n"); + return; + } + fkey.keynum = keynum; + if ((fkey.flen = strlen(str)) > MAXFK) { + fprintf(stderr, "function key string too long (%d > %d)\n", fkey.flen, MAXFK); + return; + } + strcpy(fkey.keydef, str); + if (verbose) + fprintf(stderr, "setting F%d to \"%s\"\n", fkey.keynum, str); + fkey.keynum--; + if (ioctl(0, SETFKEY, &fkey) == -1) + perror("setting function key"); +} + + + +void +displayit(void) +{ + if (verbose) + printf("absolutely "); + printf("nothing to display.\n"); +} diff --git a/usr.bin/syscons/syscons.h b/usr.bin/syscons/syscons.h new file mode 100644 index 0000000000..c894928132 --- /dev/null +++ b/usr.bin/syscons/syscons.h @@ -0,0 +1,6 @@ +/* Where everything goes by default - ALL PATHS MUST END WITH A '/'! */ + +#define KEYMAP_PATH "/usr/share/syscons/keymaps/" +#define FONT_PATH "/usr/share/syscons/fonts/" +#define SCRNMAP_PATH "/usr/share/syscons/scrnmaps/" + diff --git a/usr.bin/tail/COPYING b/usr.bin/tail/COPYING deleted file mode 100644 index 9a17037581..0000000000 --- a/usr.bin/tail/COPYING +++ /dev/null @@ -1,249 +0,0 @@ - - GNU GENERAL PUBLIC LICENSE - Version 1, February 1989 - - Copyright (C) 1989 Free Software Foundation, Inc. - 675 Mass Ave, Cambridge, MA 02139, USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The license agreements of most software companies try to keep users -at the mercy of those companies. By contrast, our General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. The -General Public License applies to the Free Software Foundation's -software and to any other program whose authors commit to using it. -You can use it for your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Specifically, the General Public License is designed to make -sure that you have the freedom to give away or sell copies of free -software, that you receive source code or can get it if you want it, -that you can change the software or use pieces of it in new free -programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of a such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must tell them their rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any program or other work which -contains a notice placed by the copyright holder saying it may be -distributed under the terms of this General Public License. The -"Program", below, refers to any such program or work, and a "work based -on the Program" means either the Program or any work containing the -Program or a portion of it, either verbatim or with modifications. Each -licensee is addressed as "you". - - 1. You may copy and distribute verbatim copies of the Program's source -code as you receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice and -disclaimer of warranty; keep intact all the notices that refer to this -General Public License and to the absence of any warranty; and give any -other recipients of the Program a copy of this General Public License -along with the Program. You may charge a fee for the physical act of -transferring a copy. - - 2. You may modify your copy or copies of the Program or any portion of -it, and copy and distribute such modifications under the terms of Paragraph -1 above, provided that you also do the following: - - a) cause the modified files to carry prominent notices stating that - you changed the files and the date of any change; and - - b) cause the whole of any work that you distribute or publish, that - in whole or in part contains the Program or any part thereof, either - with or without modifications, to be licensed at no charge to all - third parties under the terms of this General Public License (except - that you may choose to grant warranty protection to some or all - third parties, at your option). - - c) If the modified program normally reads commands interactively when - run, you must cause it, when started running for such interactive use - in the simplest and most usual way, to print or display an - announcement including an appropriate copyright notice and a notice - that there is no warranty (or else, saying that you provide a - warranty) and that users may redistribute the program under these - conditions, and telling the user how to view a copy of this General - Public License. - - d) You may charge a fee for the physical act of transferring a - copy, and you may at your option offer warranty protection in - exchange for a fee. - -Mere aggregation of another independent work with the Program (or its -derivative) on a volume of a storage or distribution medium does not bring -the other work under the scope of these terms. - - 3. You may copy and distribute the Program (or a portion or derivative of -it, under Paragraph 2) in object code or executable form under the terms of -Paragraphs 1 and 2 above provided that you also do one of the following: - - a) accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of - Paragraphs 1 and 2 above; or, - - b) accompany it with a written offer, valid for at least three - years, to give any third party free (except for a nominal charge - for the cost of distribution) a complete machine-readable copy of the - corresponding source code, to be distributed under the terms of - Paragraphs 1 and 2 above; or, - - c) accompany it with the information you received as to where the - corresponding source code may be obtained. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form alone.) - -Source code for a work means the preferred form of the work for making -modifications to it. For an executable file, complete source code means -all the source code for all modules it contains; but, as a special -exception, it need not include source code for modules which are standard -libraries that accompany the operating system on which the executable -file runs, or for standard header files or definitions files that -accompany that operating system. - - 4. You may not copy, modify, sublicense, distribute or transfer the -Program except as expressly provided under this General Public License. -Any attempt otherwise to copy, modify, sublicense, distribute or transfer -the Program is void, and will automatically terminate your rights to use -the Program under this License. However, parties who have received -copies, or rights to use copies, from you under this General Public -License will not have their licenses terminated so long as such parties -remain in full compliance. - - 5. By copying, distributing or modifying the Program (or any work based -on the Program) you indicate your acceptance of this license to do so, -and all its terms and conditions. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the original -licensor to copy, distribute or modify the Program subject to these -terms and conditions. You may not impose any further restrictions on the -recipients' exercise of the rights granted herein. - - 7. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of the license which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -the license, you may choose any version ever published by the Free Software -Foundation. - - 8. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - Appendix: How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to humanity, the best way to achieve this is to make it -free software which everyone can redistribute and change under these -terms. - - To do so, attach the following notices to the program. It is safest to -attach them to the start of each source file to most effectively convey -the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - <one line to give the program's name and a brief idea of what it does.> - Copyright (C) 19yy <name of author> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19xx name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the -appropriate parts of the General Public License. Of course, the -commands you use may be called something other than `show w' and `show -c'; they could even be mouse-clicks or menu items--whatever suits your -program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - program `Gnomovision' (a program to direct compilers to make passes - at assemblers) written by James Hacker. - - <signature of Ty Coon>, 1 April 1989 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/usr.bin/tail/Makefile b/usr.bin/tail/Makefile index a2dd23f16b..0a8d455f00 100644 --- a/usr.bin/tail/Makefile +++ b/usr.bin/tail/Makefile @@ -1,6 +1,6 @@ +# @(#)Makefile 5.4 (Berkeley) 7/21/91 PROG= tail -SRCS= tail.c getopt.c getopt1.c -CFLAGS+=-I${.CURDIR} +SRCS= forward.c misc.c read.c reverse.c tail.c .include <bsd.prog.mk> diff --git a/usr.bin/tail/extern.h b/usr.bin/tail/extern.h new file mode 100644 index 0000000000..58d4f9f455 --- /dev/null +++ b/usr.bin/tail/extern.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 5.1 (Berkeley) 7/21/91 + */ + +#define WR(p, size) \ + if (write(STDOUT_FILENO, p, size) != size) \ + oerr(); + +enum STYLE { NOTSET = 0, FBYTES, FLINES, RBYTES, RLINES, REVERSE }; + +void forward __P((FILE *, enum STYLE, long, struct stat *)); +void reverse __P((FILE *, enum STYLE, long, struct stat *)); + +void bytes __P((FILE *, off_t)); +void lines __P((FILE *, off_t)); + +void err __P((const char *fmt, ...)); +void ierr __P((void)); +void oerr __P((void)); + +extern int fflag, rflag, rval; +extern char *fname; diff --git a/usr.bin/tail/forward.c b/usr.bin/tail/forward.c new file mode 100644 index 0000000000..5aba99fc2f --- /dev/null +++ b/usr.bin/tail/forward.c @@ -0,0 +1,202 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Edward Sze-Tyan Wang. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)forward.c 5.4 (Berkeley) 2/12/92"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "extern.h" + +static void rlines __P((FILE *, long, struct stat *)); + +/* + * forward -- display the file, from an offset, forward. + * + * There are eight separate cases for this -- regular and non-regular + * files, by bytes or lines and from the beginning or end of the file. + * + * FBYTES byte offset from the beginning of the file + * REG seek + * NOREG read, counting bytes + * + * FLINES line offset from the beginning of the file + * REG read, counting lines + * NOREG read, counting lines + * + * RBYTES byte offset from the end of the file + * REG seek + * NOREG cyclically read characters into a wrap-around buffer + * + * RLINES + * REG mmap the file and step back until reach the correct offset. + * NOREG cyclically read lines into a wrap-around array of buffers + */ +void +forward(fp, style, off, sbp) + FILE *fp; + enum STYLE style; + long off; + struct stat *sbp; +{ + register int ch; + struct timeval second; + fd_set zero; + + switch(style) { + case FBYTES: + if (off == 0) + break; + if (S_ISREG(sbp->st_mode)) { + if (sbp->st_size < off) + off = sbp->st_size; + if (fseek(fp, off, SEEK_SET) == -1) + ierr(); + } else while (off--) + if ((ch = getc(fp)) == EOF) { + if (ferror(fp)) + ierr(); + break; + } + break; + case FLINES: + if (off == 0) + break; + for (;;) { + if ((ch = getc(fp)) == EOF) { + if (ferror(fp)) + ierr(); + break; + } + if (ch == '\n' && !--off) + break; + } + break; + case RBYTES: + if (S_ISREG(sbp->st_mode)) { + if (sbp->st_size >= off && + fseek(fp, -off, SEEK_END) == -1) + ierr(); + } else if (off == 0) { + while (getc(fp) != EOF); + if (ferror(fp)) + ierr(); + } else + bytes(fp, off); + break; + case RLINES: + if (S_ISREG(sbp->st_mode)) + if (!off) { + if (fseek(fp, 0L, SEEK_END) == -1) + ierr(); + } else + rlines(fp, off, sbp); + else if (off == 0) { + while (getc(fp) != EOF); + if (ferror(fp)) + ierr(); + } else + lines(fp, off); + break; + } + + /* + * We pause for one second after displaying any data that has + * accumulated since we read the file. + */ + if (fflag) { + FD_ZERO(&zero); + second.tv_sec = 1; + second.tv_usec = 0; + } + + for (;;) { + while ((ch = getc(fp)) != EOF) + if (putchar(ch) == EOF) + oerr(); + if (ferror(fp)) + ierr(); + (void)fflush(stdout); + if (!fflag) + break; + /* Sleep is eight system calls. Do it fast. */ + if (select(0, &zero, &zero, &zero, &second) == -1) + err("select: %s", strerror(errno)); + clearerr(fp); + } +} + +/* + * rlines -- display the last offset lines of the file. + */ +static void +rlines(fp, off, sbp) + FILE *fp; + long off; + struct stat *sbp; +{ + register off_t size; + register char *p; + + if (!(size = sbp->st_size)) + return; + + if ((p = mmap(NULL, + size, PROT_READ, MAP_FILE, fileno(fp), (off_t)0)) == (caddr_t)-1) + err("%s", strerror(errno)); + + /* Last char is special, ignore whether newline or not. */ + for (p += size - 1; --size;) + if (*--p == '\n' && !--off) { + ++p; + break; + } + + /* Set the file pointer to reflect the length displayed. */ + size = sbp->st_size - size; + WR(p, size); + if (fseek(fp, sbp->st_size, SEEK_SET) == -1) + ierr(); +} diff --git a/usr.bin/tail/getopt.c b/usr.bin/tail/getopt.c deleted file mode 100644 index d7677b4163..0000000000 --- a/usr.bin/tail/getopt.c +++ /dev/null @@ -1,595 +0,0 @@ -/* Getopt for GNU. - Copyright (C) 1987, 1989, 1990 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifdef __STDC__ -#define CONST const -#else -#define CONST -#endif - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of `argv' so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. - - Setting the environment variable _POSIX_OPTION_ORDER disables permutation. - Then the behavior is completely standard. - - GNU application programs can use a third alternative mode in which - they can distinguish the relative order of options and other arguments. */ - -#include <stdio.h> - -/* If compiled with GNU C, use the built-in alloca */ -#ifdef __GNUC__ -#define alloca __builtin_alloca -#else /* not __GNUC__ */ -#ifdef sparc -#include <alloca.h> -#else -char *alloca (); -#endif -#endif /* not __GNUC__ */ - -#if defined(STDC_HEADERS) || defined(__GNU_LIBRARY__) -#include <stdlib.h> -#include <string.h> -#define bcopy(s, d, n) memcpy ((d), (s), (n)) -#define index strchr -#else - -#ifdef USG -#include <string.h> -#define bcopy(s, d, n) memcpy ((d), (s), (n)) -#define index strchr -#else -#ifdef VMS -#include <string.h> -#else -#include <strings.h> -#endif -void bcopy (); -#endif - -char *getenv (); -char *malloc (); -#endif - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -char *optarg = 0; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns EOF, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -int optind = 0; - -/* The next char to be scanned in the option-element - in which the last option character we returned was found. - This allows us to pick up the scan where we left off. - - If this is zero, or a null string, it means resume the scan - by advancing to the next ARGV-element. */ - -static char *nextchar; - -/* Callers store zero here to inhibit the error message - for unrecognized options. */ - -int opterr = 1; - -/* Describe how to deal with options that follow non-option ARGV-elements. - - If the caller did not specify anything, - the default is REQUIRE_ORDER if the environment variable - _POSIX_OPTION_ORDER is defined, PERMUTE otherwise. - - REQUIRE_ORDER means don't recognize them as options; - stop option processing when the first non-option is seen. - This is what Unix does. - This mode of operation is selected by either setting the environment - variable _POSIX_OPTION_ORDER, or using `+' as the first character - of the list of option characters. - - PERMUTE is the default. We permute the contents of ARGV as we scan, - so that eventually all the non-options are at the end. This allows options - to be given in any order, even with programs that were not written to - expect this. - - RETURN_IN_ORDER is an option available to programs that were written - to expect options and other ARGV-elements in any order and that care about - the ordering of the two. We describe each non-option ARGV-element - as if it were the argument of an option with character code 1. - Using `-' as the first character of the list of option characters - selects this mode of operation. - - The special argument `--' forces an end of option-scanning regardless - of the value of `ordering'. In the case of RETURN_IN_ORDER, only - `--' can cause `getopt' to return EOF with `optind' != ARGC. */ - -static enum -{ - REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER -} ordering; - -/* Describe the long-named options requested by the application. - _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an - element containing a name which is zero. - The field `has_arg' is 1 if the option takes an argument, - 2 if it takes an optional argument. */ - -struct option -{ - char *name; - int has_arg; - int *flag; - int val; -}; - -CONST struct option *_getopt_long_options; - -int _getopt_long_only = 0; - -/* Index in _GETOPT_LONG_OPTIONS of the long-named option actually found. - Only valid when a long-named option was found. */ - -int option_index; - -/* Handle permutation of arguments. */ - -/* Describe the part of ARGV that contains non-options that have - been skipped. `first_nonopt' is the index in ARGV of the first of them; - `last_nonopt' is the index after the last of them. */ - -static int first_nonopt; -static int last_nonopt; - -/* Exchange two adjacent subsequences of ARGV. - One subsequence is elements [first_nonopt,last_nonopt) - which contains all the non-options that have been skipped so far. - The other is elements [last_nonopt,optind), which contains all - the options processed since those non-options were skipped. - - `first_nonopt' and `last_nonopt' are relocated so that they describe - the new indices of the non-options in ARGV after they are moved. */ - -static void -exchange (argv) - char **argv; -{ - int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); - char **temp = (char **) alloca (nonopts_size); - - /* Interchange the two blocks of data in ARGV. */ - - bcopy (&argv[first_nonopt], temp, nonopts_size); - bcopy (&argv[last_nonopt], &argv[first_nonopt], - (optind - last_nonopt) * sizeof (char *)); - bcopy (temp, &argv[first_nonopt + optind - last_nonopt], nonopts_size); - - /* Update records for the slots the non-options now occupy. */ - - first_nonopt += (optind - last_nonopt); - last_nonopt = optind; -} - -/* Scan elements of ARGV (whose length is ARGC) for option characters - given in OPTSTRING. - - If an element of ARGV starts with '-', and is not exactly "-" or "--", - then it is an option element. The characters of this element - (aside from the initial '-') are option characters. If `getopt' - is called repeatedly, it returns successively each of the option characters - from each of the option elements. - - If `getopt' finds another option character, it returns that character, - updating `optind' and `nextchar' so that the next call to `getopt' can - resume the scan with the following option character or ARGV-element. - - If there are no more option characters, `getopt' returns `EOF'. - Then `optind' is the index in ARGV of the first ARGV-element - that is not an option. (The ARGV-elements have been permuted - so that those that are not options now come last.) - - OPTSTRING is a string containing the legitimate option characters. - If an option character is seen that is not listed in OPTSTRING, - return '?' after printing an error message. If you set `opterr' to - zero, the error message is suppressed but we still return '?'. - - If a char in OPTSTRING is followed by a colon, that means it wants an arg, - so the following text in the same ARGV-element, or the text of the following - ARGV-element, is returned in `optarg'. Two colons mean an option that - wants an optional arg; if there is text in the current ARGV-element, - it is returned in `optarg', otherwise `optarg' is set to zero. - - If OPTSTRING starts with `-' or `+', it requests different methods of - handling the non-option ARGV-elements. - See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - - Long-named options begin with `+' instead of `-'. - Their names may be abbreviated as long as the abbreviation is unique - or is an exact match for some defined option. If they have an - argument, it follows the option name in the same ARGV-element, separated - from the option name by a `=', or else the in next ARGV-element. - When `getopt' finds a long-named option, it returns 0 if that option's - `flag' field is nonzero, the value of the option's `val' field - otherwise. */ - -int -getopt (argc, argv, optstring) - int argc; - char **argv; - CONST char *optstring; -{ - optarg = 0; - - /* Initialize the internal data when the first call is made. - Start processing options with ARGV-element 1 (since ARGV-element 0 - is the program name); the sequence of previously skipped - non-option ARGV-elements is empty. */ - - if (optind == 0) - { - first_nonopt = last_nonopt = optind = 1; - - nextchar = 0; - - /* Determine how to handle the ordering of options and nonoptions. */ - - if (optstring[0] == '-') - { - ordering = RETURN_IN_ORDER; - ++optstring; - } - else if (optstring[0] == '+') - { - ordering = REQUIRE_ORDER; - ++optstring; - } - else if (getenv ("_POSIX_OPTION_ORDER") != 0) - ordering = REQUIRE_ORDER; - else - ordering = PERMUTE; - } - - if (nextchar == 0 || *nextchar == 0) - { - if (ordering == PERMUTE) - { - /* If we have just processed some options following some non-options, - exchange them so that the options come first. */ - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange (argv); - else if (last_nonopt != optind) - first_nonopt = optind; - - /* Now skip any additional non-options - and extend the range of non-options previously skipped. */ - - while (optind < argc - && (argv[optind][0] != '-' - || argv[optind][1] == 0) - && (_getopt_long_options == 0 - || argv[optind][0] != '+' - || argv[optind][1] == 0)) - optind++; - last_nonopt = optind; - } - - /* Special ARGV-element `--' means premature end of options. - Skip it like a null option, - then exchange with previous non-options as if it were an option, - then skip everything else like a non-option. */ - - if (optind != argc && !strcmp (argv[optind], "--")) - { - optind++; - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange (argv); - else if (first_nonopt == last_nonopt) - first_nonopt = optind; - last_nonopt = argc; - - optind = argc; - } - - /* If we have done all the ARGV-elements, stop the scan - and back over any non-options that we skipped and permuted. */ - - if (optind == argc) - { - /* Set the next-arg-index to point at the non-options - that we previously skipped, so the caller will digest them. */ - if (first_nonopt != last_nonopt) - optind = first_nonopt; - return EOF; - } - - /* If we have come to a non-option and did not permute it, - either stop the scan or describe it to the caller and pass it by. */ - - if ((argv[optind][0] != '-' || argv[optind][1] == 0) - && (_getopt_long_options == 0 - || argv[optind][0] != '+' || argv[optind][1] == 0)) - { - if (ordering == REQUIRE_ORDER) - return EOF; - optarg = argv[optind++]; - return 1; - } - - /* We have found another option-ARGV-element. - Start decoding its characters. */ - - nextchar = argv[optind] + 1; - } - - if (_getopt_long_options != 0 - && (argv[optind][0] == '+' - || (_getopt_long_only && argv[optind][0] == '-')) - ) - { - CONST struct option *p; - char *s = nextchar; - int exact = 0; - int ambig = 0; - CONST struct option *pfound = 0; - int indfound; - - while (*s && *s != '=') - s++; - - /* Test all options for either exact match or abbreviated matches. */ - for (p = _getopt_long_options, option_index = 0; p->name; - p++, option_index++) - if (!strncmp (p->name, nextchar, s - nextchar)) - { - if (s - nextchar == strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == 0) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else - /* Second nonexact match found. */ - ambig = 1; - } - - if (ambig && !exact) - { - fprintf (stderr, "%s: option `%s' is ambiguous\n", - argv[0], argv[optind]); - nextchar += strlen (nextchar); - optind++; - return '?'; - } - - if (pfound != 0) - { - option_index = indfound; - optind++; - if (*s) - { - if (pfound->has_arg > 0) - optarg = s + 1; - else - { - fprintf (stderr, - "%s: option `%c%s' doesn't allow an argument\n", - argv[0], argv[optind - 1][0], pfound->name); - nextchar += strlen (nextchar); - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (optind < argc) - optarg = argv[optind++]; - else - { - fprintf (stderr, "%s: option `%s' requires an argument\n", - argv[0], argv[optind - 1]); - nextchar += strlen (nextchar); - return '?'; - } - } - nextchar += strlen (nextchar); - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - /* Can't find it as a long option. If this is getopt_long_only, - and the option starts with '-' and is a valid short - option, then interpret it as a short option. Otherwise it's - an error. */ - if (_getopt_long_only == 0 || argv[optind][0] == '+' || - index (optstring, *nextchar) == 0) - { - if (opterr != 0) - fprintf (stderr, "%s: unrecognized option `%c%s'\n", - argv[0], argv[optind][0], nextchar); - nextchar += strlen (nextchar); - optind++; - return '?'; - } - } - - /* Look at and handle the next option-character. */ - - { - char c = *nextchar++; - char *temp = index (optstring, c); - - /* Increment `optind' when we start to process its last character. */ - if (*nextchar == 0) - optind++; - - if (temp == 0 || c == ':') - { - if (opterr != 0) - { - if (c < 040 || c >= 0177) - fprintf (stderr, "%s: unrecognized option, character code 0%o\n", - argv[0], c); - else - fprintf (stderr, "%s: unrecognized option `-%c'\n", - argv[0], c); - } - return '?'; - } - if (temp[1] == ':') - { - if (temp[2] == ':') - { - /* This is an option that accepts an argument optionally. */ - if (*nextchar != 0) - { - optarg = nextchar; - optind++; - } - else - optarg = 0; - nextchar = 0; - } - else - { - /* This is an option that requires an argument. */ - if (*nextchar != 0) - { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - optind++; - } - else if (optind == argc) - { - if (opterr != 0) - fprintf (stderr, "%s: option `-%c' requires an argument\n", - argv[0], c); - c = '?'; - } - else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - optarg = argv[optind++]; - nextchar = 0; - } - } - return c; - } -} - -#ifdef TEST - -/* Compile with -DTEST to make an executable for use in testing - the above definition of `getopt'. */ - -int -main (argc, argv) - int argc; - char **argv; -{ - int c; - int digit_optind = 0; - - while (1) - { - int this_option_optind = optind ? optind : 1; - - c = getopt (argc, argv, "abc:d:0123456789"); - if (c == EOF) - break; - - switch (c) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 && digit_optind != this_option_optind) - printf ("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf ("option %c\n", c); - break; - - case 'a': - printf ("option a\n"); - break; - - case 'b': - printf ("option b\n"); - break; - - case 'c': - printf ("option c with value `%s'\n", optarg); - break; - - case '?': - break; - - default: - printf ("?? getopt returned character code 0%o ??\n", c); - } - } - - if (optind < argc) - { - printf ("non-option ARGV-elements: "); - while (optind < argc) - printf ("%s ", argv[optind++]); - printf ("\n"); - } - - exit (0); -} - -#endif /* TEST */ diff --git a/usr.bin/tail/getopt.h b/usr.bin/tail/getopt.h deleted file mode 100644 index 661acb9273..0000000000 --- a/usr.bin/tail/getopt.h +++ /dev/null @@ -1,102 +0,0 @@ -/* declarations for getopt - Copyright (C) 1989, 1990 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -extern char *optarg; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns EOF, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -extern int optind; - -/* Callers store zero here to inhibit the error message `getopt' prints - for unrecognized options. */ - -extern int opterr; - -/* Describe the long-named options requested by the application. - _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an - element containing a name which is zero. - - The field `has_arg' is: - 0 if the option does not take an argument, - 1 if the option requires an argument, - 2 if the option takes an optional argument. - - If the field `flag' is nonzero, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. - - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ - -struct option -{ - char *name; - int has_arg; - int *flag; - int val; -}; - -#ifdef __STDC__ -extern const struct option *_getopt_long_options; -#else -extern struct option *_getopt_long_options; -#endif - -/* If nonzero, '-' can introduce long-named options. - Set by getopt_long_only. */ - -extern int _getopt_long_only; - -/* The index in GETOPT_LONG_OPTIONS of the long-named option found. - Only valid when a long-named option has been found by the most - recent call to `getopt'. */ - -extern int option_index; - -#ifdef __STDC__ -int getopt (int argc, char **argv, const char *shortopts); -int getopt_long (int argc, char **argv, const char *shortopts, - const struct option *longopts, int *longind); -int getopt_long_only (int argc, char **argv, const char *shortopts, - const struct option *longopts, int *longind); -void envopt(int *pargc, char ***pargv, char *optstr); -#else -int getopt (); -int getopt_long (); -int getopt_long_only (); -void envopt(); -#endif diff --git a/usr.bin/tail/getopt1.c b/usr.bin/tail/getopt1.c deleted file mode 100644 index bb73d86e31..0000000000 --- a/usr.bin/tail/getopt1.c +++ /dev/null @@ -1,160 +0,0 @@ -/* Getopt for GNU. - Copyright (C) 1987, 1989 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include "getopt.h" - -#ifdef __STDC__ -#define CONST const -#else -#define CONST -#endif - -#if !defined (NULL) -#define NULL 0 -#endif - -int -getopt_long (argc, argv, options, long_options, opt_index) - int argc; - char **argv; - CONST char *options; - CONST struct option *long_options; - int *opt_index; -{ - int val; - - _getopt_long_options = long_options; - val = getopt (argc, argv, options); - if (val == 0 && opt_index != NULL) - *opt_index = option_index; - return val; -} - -/* Like getopt_long, but '-' as well as '+' can indicate a long option. - If an option that starts with '-' doesn't match a long option, - but does match a short option, it is parsed as a short option - instead. */ - -int -getopt_long_only (argc, argv, options, long_options, opt_index) - int argc; - char **argv; - CONST char *options; - CONST struct option *long_options; - int *opt_index; -{ - int val; - - _getopt_long_options = long_options; - _getopt_long_only = 1; - val = getopt (argc, argv, options); - if (val == 0 && opt_index != NULL) - *opt_index = option_index; - return val; -} - - -#ifdef TEST - -#include <stdio.h> - -int -main (argc, argv) - int argc; - char **argv; -{ - int c; - int digit_optind = 0; - - while (1) - { - int this_option_optind = optind ? optind : 1; - char *name = '\0'; - int option_index = 0; - static struct option long_options[] = - { - {"add", 1, 0, 0}, - {"append", 0, 0, 0}, - {"delete", 1, 0, 0}, - {"verbose", 0, 0, 0}, - {"create", 0, 0, 0}, - {"file", 1, 0, 0}, - {0, 0, 0, 0} - }; - - c = getopt_long (argc, argv, "abc:d:0123456789", - long_options, &option_index); - if (c == EOF) - break; - - switch (c) - { - case 0: - printf ("option %s", (long_options[option_index]).name); - if (optarg) - printf (" with arg %s", optarg); - printf ("\n"); - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 && digit_optind != this_option_optind) - printf ("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf ("option %c\n", c); - break; - - case 'a': - printf ("option a\n"); - break; - - case 'b': - printf ("option b\n"); - break; - - case 'c': - printf ("option c with value `%s'\n", optarg); - break; - - case '?': - break; - - default: - printf ("?? getopt returned character code 0%o ??\n", c); - } - } - - if (optind < argc) - { - printf ("non-option ARGV-elements: "); - while (optind < argc) - printf ("%s ", argv[optind++]); - printf ("\n"); - } - - exit (0); -} - -#endif /* TEST */ diff --git a/usr.bin/tail/misc.c b/usr.bin/tail/misc.c new file mode 100644 index 0000000000..7deec07166 --- /dev/null +++ b/usr.bin/tail/misc.c @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Edward Sze-Tyan Wang. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)misc.c 5.1 (Berkeley) 7/21/91"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "extern.h" + +void +ierr() +{ + err("%s: %s", fname, strerror(errno)); +} + +void +oerr() +{ + err("stdout: %s", strerror(errno)); +} + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "tail: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/usr.bin/tail/read.c b/usr.bin/tail/read.c new file mode 100644 index 0000000000..9a213e3304 --- /dev/null +++ b/usr.bin/tail/read.c @@ -0,0 +1,195 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Edward Sze-Tyan Wang. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)read.c 5.1 (Berkeley) 7/21/91"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "extern.h" + +/* + * bytes -- read bytes to an offset from the end and display. + * + * This is the function that reads to a byte offset from the end of the input, + * storing the data in a wrap-around buffer which is then displayed. If the + * rflag is set, the data is displayed in lines in reverse order, and this + * routine has the usual nastiness of trying to find the newlines. Otherwise, + * it is displayed from the character closest to the beginning of the input to + * the end. + */ +void +bytes(fp, off) + register FILE *fp; + long off; +{ + register int ch, len, tlen; + register char *ep, *p, *t; + int wrap; + char *sp; + + if ((sp = p = malloc(off)) == NULL) + err("%s", strerror(errno)); + + for (wrap = 0, ep = p + off; (ch = getc(fp)) != EOF;) { + *p = ch; + if (++p == ep) { + wrap = 1; + p = sp; + } + } + if (ferror(fp)) + ierr(); + + if (rflag) { + for (t = p - 1, len = 0; t >= sp; --t, ++len) + if (*t == '\n' && len) { + WR(t + 1, len); + len = 0; + } + if (wrap) { + tlen = len; + for (t = ep - 1, len = 0; t >= p; --t, ++len) + if (*t == '\n') { + if (len) { + WR(t + 1, len); + len = 0; + } + if (tlen) { + WR(sp, tlen); + tlen = 0; + } + } + if (len) + WR(t + 1, len); + if (tlen) + WR(sp, tlen); + } + } else { + if (wrap && (len = ep - p)) + WR(p, len); + if (len = p - sp) + WR(sp, len); + } +} + +/* + * lines -- read lines to an offset from the end and display. + * + * This is the function that reads to a line offset from the end of the input, + * storing the data in an array of buffers which is then displayed. If the + * rflag is set, the data is displayed in lines in reverse order, and this + * routine has the usual nastiness of trying to find the newlines. Otherwise, + * it is displayed from the line closest to the beginning of the input to + * the end. + */ +void +lines(fp, off) + register FILE *fp; + long off; +{ + struct { + u_int blen; + u_int len; + char *l; + } *lines; + register int ch; + register char *p; + u_int blen, cnt, recno; + int wrap; + char *sp; + + if ((lines = malloc(off * sizeof(*lines))) == NULL) + err("%s", strerror(errno)); + + sp = NULL; + blen = cnt = recno = wrap = 0; + + while ((ch = getc(fp)) != EOF) { + if (++cnt > blen) { + if ((sp = realloc(sp, blen += 1024)) == NULL) + err("%s", strerror(errno)); + p = sp + cnt - 1; + } + *p++ = ch; + if (ch == '\n') { + if (lines[recno].blen < cnt) { + lines[recno].blen = cnt + 256; + if ((lines[recno].l = realloc(lines[recno].l, + lines[recno].blen)) == NULL) + err("%s", strerror(errno)); + } + bcopy(sp, lines[recno].l, lines[recno].len = cnt); + cnt = 0; + p = sp; + if (++recno == off) { + wrap = 1; + recno = 0; + } + } + } + if (ferror(fp)) + ierr(); + if (cnt) { + lines[recno].l = sp; + lines[recno].len = cnt; + if (++recno == off) { + wrap = 1; + recno = 0; + } + } + + if (rflag) { + for (cnt = recno - 1; cnt >= 0; --cnt) + WR(lines[cnt].l, lines[cnt].len); + if (wrap) + for (cnt = off - 1; cnt >= recno; --cnt) + WR(lines[cnt].l, lines[cnt].len); + } else { + if (wrap) + for (cnt = recno; cnt < off; ++cnt) + WR(lines[cnt].l, lines[cnt].len); + for (cnt = 0; cnt < recno; ++cnt) + WR(lines[cnt].l, lines[cnt].len); + } +} diff --git a/usr.bin/tail/reverse.c b/usr.bin/tail/reverse.c new file mode 100644 index 0000000000..90e989976b --- /dev/null +++ b/usr.bin/tail/reverse.c @@ -0,0 +1,247 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Edward Sze-Tyan Wang. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)reverse.c 5.3 (Berkeley) 2/12/92"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "extern.h" + +static void r_buf __P((FILE *)); +static void r_reg __P((FILE *, enum STYLE, long, struct stat *)); + +/* + * reverse -- display input in reverse order by line. + * + * There are six separate cases for this -- regular and non-regular + * files by bytes, lines or the whole file. + * + * BYTES display N bytes + * REG mmap the file and display the lines + * NOREG cyclically read characters into a wrap-around buffer + * + * LINES display N lines + * REG mmap the file and display the lines + * NOREG cyclically read lines into a wrap-around array of buffers + * + * FILE display the entire file + * REG mmap the file and display the lines + * NOREG cyclically read input into a linked list of buffers + */ +void +reverse(fp, style, off, sbp) + FILE *fp; + enum STYLE style; + long off; + struct stat *sbp; +{ + if (style != REVERSE && off == 0) + return; + + if (S_ISREG(sbp->st_mode)) + r_reg(fp, style, off, sbp); + else + switch(style) { + case FBYTES: + bytes(fp, off); + break; + case FLINES: + lines(fp, off); + break; + case REVERSE: + r_buf(fp); + break; + } +} + +/* + * r_reg -- display a regular file in reverse order by line. + */ +static void +r_reg(fp, style, off, sbp) + FILE *fp; + register enum STYLE style; + long off; + struct stat *sbp; +{ + register off_t size; + register int llen; + register char *p; + int fd; + + if (!(size = sbp->st_size)) + return; + + fd = fileno(fp); + if ((p = + mmap(NULL, size, PROT_READ, MAP_FILE, fd, (off_t)0)) == (caddr_t)-1) + err("%s", strerror(errno)); + p += size - 1; + + if (style == RBYTES && off < size) + size = off; + + /* Last char is special, ignore whether newline or not. */ + for (llen = 1; --size; ++llen) + if (*--p == '\n') { + WR(p + 1, llen); + llen = 0; + if (style == RLINES && !--off) { + ++p; + break; + } + } + if (llen) + WR(p, llen); +} + +typedef struct bf { + struct bf *next; + struct bf *prev; + int len; + char *l; +} BF; + +/* + * r_buf -- display a non-regular file in reverse order by line. + * + * This is the function that saves the entire input, storing the data in a + * doubly linked list of buffers and then displays them in reverse order. + * It has the usual nastiness of trying to find the newlines, as there's no + * guarantee that a newline occurs anywhere in the file, let alone in any + * particular buffer. If we run out of memory, input is discarded (and the + * user warned). + */ +static void +r_buf(fp) + FILE *fp; +{ + register BF *mark, *tl, *tr; + register int ch, len, llen; + register char *p; + off_t enomem; + +#define BSZ (128 * 1024) + for (mark = NULL, enomem = 0;;) { + /* + * Allocate a new block and link it into place in a doubly + * linked list. If out of memory, toss the LRU block and + * keep going. + */ + if (enomem || (tl = malloc(sizeof(BF))) == NULL || + (tl->l = malloc(BSZ)) == NULL) { + if (!mark) + err("%s", strerror(errno)); + tl = enomem ? tl->next : mark; + enomem += tl->len; + } else if (mark) { + tl->next = mark; + tl->prev = mark->prev; + mark->prev->next = tl; + mark->prev = tl; + } else + mark->next = mark->prev = (mark = tl); + + /* Fill the block with input data. */ + for (p = tl->l, len = 0; + len < BSZ && (ch = getc(fp)) != EOF; ++len) + *p++ = ch; + + /* + * If no input data for this block and we tossed some data, + * recover it. + */ + if (!len) { + if (enomem) + enomem -= tl->len; + tl = tl->prev; + break; + } + + tl->len = len; + if (ch == EOF) + break; + } + + if (enomem) { + (void)fprintf(stderr, + "tail: warning: %ld bytes discarded\n", enomem); + rval = 1; + } + + /* + * Step through the blocks in the reverse order read. The last char + * is special, ignore whether newline or not. + */ + for (mark = tl;;) { + for (p = tl->l + (len = tl->len) - 1, llen = 0; len--; + --p, ++llen) + if (*p == '\n') { + if (llen) { + WR(p + 1, llen); + llen = 0; + } + if (tl == mark) + continue; + for (tr = tl->next; tr->len; tr = tr->next) { + WR(tr->l, tr->len); + tr->len = 0; + if (tr == mark) + break; + } + } + tl->len = llen; + if ((tl = tl->prev) == mark) + break; + } + tl = tl->next; + if (tl->len) { + WR(tl->l, tl->len); + tl->len = 0; + } + while ((tl = tl->next)->len) { + WR(tl->l, tl->len); + tl->len = 0; + } +} diff --git a/usr.bin/tail/tail.1 b/usr.bin/tail/tail.1 index 1ccba607c8..1fcf0813e7 100644 --- a/usr.bin/tail/tail.1 +++ b/usr.bin/tail/tail.1 @@ -1,75 +1,158 @@ -.TH TAIL 1L \" -*- nroff -*- -.SH NAME -tail \- output the last part of files -.SH SYNOPSIS -.B tail -[\-c [+]N[bkm]] [\-n [+]N] [\-fqv] [\-\-bytes=[+]N[bkm]] [\-\-lines=[+]N] -[\-\-follow] [\-\-quiet] [\-\-silent] [\-\-verbose] [file...] - -.B tail -[{\-,+}Nbcfklmqv] [file...] -.SH DESCRIPTION -This manual page -documents the GNU version of -.BR tail . -.B tail -prints the last part (10 lines by default) of each given file; it -reads from standard input if no files are given or when a filename of -`\-' is encountered. If more than one file is given, it prints a -header consisting of the file's name enclosed in `==>' and `<==' -before the output for each file. -.PP -The GNU -.B tail -can output any amount of data, unlike the Unix version, which uses a -fixed size buffer. It has no -.I \-r -option (print in reverse). Reversing a file is really a different job -from printing the end of a file; the BSD -.B tail -can only reverse files that are at most as large as its buffer, which -is typically 32k. A reliable and more versatile way to reverse files is -the GNU -.B tac -command. -.SS OPTIONS -.PP -.B tail -accepts two option formats: the new one, in which numbers are -arguments to the option letters, and the old one, in which a `+' or -`\-' and optional number precede any option letters. -.PP -If a number (`N') starts with a `+', -.B tail -begins printing with the Nth item from the start of each file, instead -of from the end. -.TP -.I "\-c N, \-\-bytes N" -Tail by N bytes. N is a nonzero integer, optionally followed by one -of the following characters to specify a different unit. -.RS -.IP b +.\" Copyright (c) 1980, 1990, 1991 Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)tail.1 6.8 (Berkeley) 2/12/92 +.\" +.Dd February 12, 1992 +.Dt TAIL 1 +.Os BSD 4 +.Sh NAME +.Nm tail +.Nd display the last part of a file +.Sh SYNOPSIS +.Nm tail +.Op Fl f Li | Fl r +.Oo +.Fl b Ar number | +.Fl c Ar number | +.Fl n Ar number +.Oc +.Op Ar file +.Sh DESCRIPTION +The +.Nm tail +utility displays the contents of +.Ar file +or, by default, its standard input, to the standard output. +.Pp +The display begins at a byte, line or 512-byte block location in the +input. +Numbers having a leading plus (``+'') sign are relative to the beginning +of the input, for example, +.Dq -c +2 +starts the display at the second +byte of the input. +Numbers having a leading minus (``-'') sign or no explicit sign are +relative to the end of the input, for example, +.Dq -n 2 +displays the last two lines of the input. +The default starting location is +.Dq -n 10 , +or the last 10 lines of the input. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl b Ar number +The location is +.Ar number 512-byte blocks. -.IP k -1-kilobyte blocks. -.IP m -1-megabyte blocks. -.RE -.TP -.I "\-f, \-\-follow" -Loop forever trying to read more characters at the end of the file, on -the assumption that the file is growing. Ignored if reading from a -pipe. Cannot be used if more than one file is given. -.TP -.I "\-l, \-n N, \-\-lines N" -Tail by N lines. -.TP -.I "\-q, \-\-quiet, \-\-silent" -Never print filename headers. -.TP -.I "\-v, \-\-verbose" -Always print filename headers. -.PP -The long-named options can be introduced with `+' as well as `\-\-', -for compatibility with previous releases. Eventually support for `+' -will be removed, because it is incompatible with the POSIX.2 standard. +.It Fl c Ar number +The location is +.Ar number +bytes. +.It Fl f +The +.Fl f +option causes +.Nm tail +to not stop when end of file is reached, but rather to wait for additional +data to be appended to the input. +The +.Fl f +option is ignored on pipes but not on FIFO's. +.It Fl n Ar number +The location is +.Ar number +lines. +.It Fl r +The +.Fl r +option causes the input to be displayed in reverse order, by line. +Additionally, this option changes the meaning of the +.Fl b , +.Fl c +and +.Fl n +options. +When the +.Fl r +option is specified, these options specify the number of bytes, lines +or 512-byte blocks to display, instead of the bytes, lines or blocks +from the beginning or end of the input from which to begin the display. +The default for the +.Fl r +option is to display all of the input. +.El +.Pp +The +.Nm tail +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr cat 1 , +.Xr head 1 , +.Xr sed 1 +.Sh STANDARDS +The +.Nm tail +utility is expected to be a superset of the POSIX 1003.2 +specification. +In particular, the +.Fl b +and +.Fl r +options are extensions to that standard. +.Pp +The historic command line syntax of +.Nm tail +is supported by this implementation. +The only difference between this implementation and historic versions +of +.Nm tail , +once the command line syntax translation has been done, is that the +.Fl b , +.Fl c +and +.Fl n +options modify the +.Fl r +option, i.e. ``-r -c 4'' displays the last 4 characters of the last line +of the input, while the historic tail (using the historic syntax ``-4cr'') +would ignore the +.Fl c +option and display the last 4 lines of the input. +.Sh HISTORY +A +.Nm tail +command appeared in +.At v7 . diff --git a/usr.bin/tail/tail.c b/usr.bin/tail/tail.c index bd67d8bc8e..e022934d2a 100644 --- a/usr.bin/tail/tail.c +++ b/usr.bin/tail/tail.c @@ -1,906 +1,279 @@ -/* tail -- output last part of file(s) - Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc. +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Edward Sze-Tyan Wang. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1991 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tail.c 5.7 (Berkeley) 2/12/92"; +#endif /* not lint */ - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Can display any amount of data, unlike the Unix version, which uses - a fixed size buffer and therefore can only deliver a limited number - of lines. - - Options: - -b Tail by N 512-byte blocks. - -c, --bytes=N[bkm] Tail by N bytes - [or 512-byte blocks, kilobytes, or megabytes]. - -f, --follow Loop forever trying to read more characters at the - end of the file, on the assumption that the file - is growing. Ignored if reading from a pipe. - Cannot be used if more than one file is given. - -k Tail by N kilobytes. - -N, -l, -n, --lines=N Tail by N lines. - -m Tail by N megabytes. - -q, --quiet, --silent Never print filename headers. - -v, --verbose Always print filename headers. - - If a number (N) starts with a `+', begin printing with the Nth item - from the start of each file, instead of from the end. - - Reads from standard input if no files are given or when a filename of - ``-'' is encountered. - By default, filename headers are printed only more than one file - is given. - By default, prints the last 10 lines (tail -n 10). - - Original version by Paul Rubin <phr@ocf.berkeley.edu>. - Extensions by David MacKenzie <djm@ai.mit.edu>. */ - -#include <stdio.h> -#include <getopt.h> -#include <ctype.h> #include <sys/types.h> -/*#include "system.h"*/ -#include <sys/file.h> #include <sys/stat.h> +#include <errno.h> #include <unistd.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> -#include <errno.h> - -#ifdef isascii -#define ISDIGIT(c) (isascii ((c)) && isdigit ((c))) -#else -#define ISDIGIT(c) (isdigit ((c))) -#endif - -/* Number of items to tail. */ -#define DEFAULT_NUMBER 10 - -/* Size of atomic reads. */ -#define BUFSIZE (512 * 8) +#include "extern.h" -/* Number of bytes per item we are printing. - If 0, tail in lines. */ -int unit_size; +int fflag, rflag, rval; +char *fname; -/* If nonzero, read from end of file until killed. */ -int forever; +static void obsolete __P((char **)); +static void usage __P((void)); -/* If nonzero, count from start of file instead of end. */ -int from_start; - -/* If nonzero, print filename headers. */ -int print_headers; - -/* When to print the filename banners. */ -enum header_mode +main(argc, argv) + int argc; + char **argv; { - multiple_files, always, never -}; - -int file_lines (); -int pipe_bytes (); -int pipe_lines (); -int start_bytes (); -int start_lines (); -int tail (); -int tail_bytes (); -int tail_file (); -int tail_lines (); -long atou(); -void dump_remainder (); -void error (int status, int errnum, char *message, ...); -void parse_unit (); -void usage (); -void write_header (); - -/* The name this program was run with. */ -char *program_name; - -/* Nonzero if we have ever read standard input. */ -int have_read_stdin; - -struct option long_options[] = -{ - {"bytes", 1, NULL, 'c'}, - {"follow", 0, NULL, 'f'}, - {"lines", 1, NULL, 'n'}, - {"quiet", 0, NULL, 'q'}, - {"silent", 0, NULL, 'q'}, - {"verbose", 0, NULL, 'v'}, - {NULL, 0, NULL, 0} -}; - -void -main (argc, argv) - int argc; - char **argv; -{ - enum header_mode header_mode = multiple_files; - int exit_status = 0; - /* If from_start, the number of items to skip before printing; otherwise, - the number of items at the end of the file to print. Initially, -1 - means the value has not been set. */ - long number = -1; - int c; /* Option character. */ - - program_name = argv[0]; - have_read_stdin = 0; - unit_size = 0; - forever = from_start = print_headers = 0; + struct stat sb; + FILE *fp; + long off; + enum STYLE style; + int ch; + char *p; + + /* + * Tail's options are weird. First, -n10 is the same as -n-10, not + * -n+10. Second, the number options are 1 based and not offsets, + * so -n+1 is the first line, and -c-1 is the last byte. Third, the + * number options for the -r option specify the number of things that + * get displayed, not the starting point in the file. The one major + * incompatibility in this version as compared to historical versions + * is that the 'r' option couldn't be modified by the -lbc options, + * i.e. it was always done in lines. This version treats -rc as a + * number of characters in reverse order. Finally, the default for + * -r is the entire file, not 10 lines. + */ +#define ARG(units, forward, backward) { \ + if (style) \ + usage(); \ + off = strtol(optarg, &p, 10) * (units); \ + if (*p) \ + err("illegal offset -- %s", optarg); \ + switch(optarg[0]) { \ + case '+': \ + if (off) \ + off -= (units); \ + style = (forward); \ + break; \ + case '-': \ + off = -off; \ + /* FALLTHROUGH */ \ + default: \ + style = (backward); \ + break; \ + } \ +} - if (argc > 1 - && ((argv[1][0] == '-' && ISDIGIT (argv[1][1])) - || (argv[1][0] == '+' && (ISDIGIT (argv[1][1]) || argv[1][1] == 0)))) - { - /* Old option syntax: a dash or plus, one or more digits (zero digits - are acceptable with a plus), and one or more option letters. */ - if (argv[1][0] == '+') - from_start = 1; - if (argv[1][1] != 0) - { - for (number = 0, ++argv[1]; ISDIGIT (*argv[1]); ++argv[1]) - number = number * 10 + *argv[1] - '0'; - /* Parse any appended option letters. */ - while (*argv[1]) - { - switch (*argv[1]) - { + obsolete(argv); + style = NOTSET; + while ((ch = getopt(argc, argv, "b:c:fn:r")) != EOF) + switch(ch) { case 'b': - unit_size = 512; - break; - + ARG(512, FBYTES, RBYTES); + break; case 'c': - unit_size = 1; - break; - + ARG(1, FBYTES, RBYTES); + break; case 'f': - forever = 1; - break; - - case 'k': - unit_size = 1024; - break; - - case 'l': - unit_size = 0; - break; - - case 'm': - unit_size = 1048576; - break; - - case 'q': - header_mode = never; - break; - - case 'v': - header_mode = always; - break; - + fflag = 1; + break; + case 'n': + ARG(1, FLINES, RLINES); + break; + case 'r': + rflag = 1; + break; + case '?': default: - error (0, 0, "unrecognized option `-%c'", *argv[1]); - usage (); + usage(); } - ++argv[1]; - } - } - /* Make the options we just parsed invisible to getopt. */ - argv[1] = argv[0]; - argv++; - argc--; - } - - while ((c = getopt_long (argc, argv, "c:n:fqv", long_options, (int *) 0)) - != EOF) - { - switch (c) - { - case 'c': - unit_size = 1; - parse_unit (optarg); - goto getnum; - case 'n': - unit_size = 0; - getnum: - if (*optarg == '+') - { - from_start = 1; - ++optarg; - } - else if (*optarg == '-') - ++optarg; - number = atou (optarg); - if (number == -1) - error (1, 0, "invalid number `%s'", optarg); - break; - - case 'f': - forever = 1; - break; - - case 'q': - header_mode = never; - break; - - case 'v': - header_mode = always; - break; - - default: - usage (); - } - } - - if (number == -1) - number = DEFAULT_NUMBER; - - /* To start printing with item `number' from the start of the file, skip - `number' - 1 items. `tail +0' is actually meaningless, but for Unix - compatibility it's treated the same as `tail +1'. */ - if (from_start) - { - if (number) - --number; - } - - if (unit_size > 1) - number *= unit_size; - - if (optind < argc - 1 && forever) - error (1, 0, "cannot follow the ends of multiple files"); - - if (header_mode == always - || (header_mode == multiple_files && optind < argc - 1)) - print_headers = 1; - - if (optind == argc) - exit_status |= tail_file ("-", number); - - for (; optind < argc; ++optind) - exit_status |= tail_file (argv[optind], number); - - if (have_read_stdin && close (0) < 0) - error (1, errno, "-"); - if (close (1) < 0) - error (1, errno, "write error"); - exit (exit_status); -} - -/* Display the last NUMBER units of file FILENAME. - "-" for FILENAME means the standard input. - Return 0 if successful, 1 if an error occurred. */ - -int -tail_file (filename, number) - char *filename; - long number; -{ - int fd; - - if (!strcmp (filename, "-")) - { - have_read_stdin = 1; - filename = "standard input"; - if (print_headers) - write_header (filename); - return tail (filename, 0, number); - } - else - { - fd = open (filename, O_RDONLY); - if (fd >= 0) - { - int errors; - - if (print_headers) - write_header (filename); - errors = tail (filename, fd, number); - if (close (fd) == 0) - return errors; - } - error (0, errno, "%s", filename); - return 1; - } -} - -void -write_header (filename) - char *filename; -{ - static int first_file = 1; - - if (first_file) - { - write (1, "==> ", 4); - first_file = 0; - } - else - write (1, "\n==> ", 5); - write (1, filename, strlen (filename)); - write (1, " <==\n", 5); -} - -/* Display the last NUMBER units of file FILENAME, open for reading - in FD. - Return 0 if successful, 1 if an error occurred. */ - -int -tail (filename, fd, number) - char *filename; - int fd; - long number; -{ - if (unit_size) - return tail_bytes (filename, fd, number); - else - return tail_lines (filename, fd, number); -} - -/* Display the last part of file FILENAME, open for reading in FD, - using NUMBER characters. - Return 0 if successful, 1 if an error occurred. */ - -int -tail_bytes (filename, fd, number) - char *filename; - int fd; - long number; -{ - struct stat stats; - - /* Use fstat instead of checking for errno == ESPIPE because - lseek doesn't work on some special files but doesn't return an - error, either. */ - if (fstat (fd, &stats)) - { - error (0, errno, "%s", filename); - return 1; - } - - if (from_start) - { - if (S_ISREG (stats.st_mode)) - lseek (fd, number, SEEK_SET); - else if (start_bytes (filename, fd, number)) - return 1; - dump_remainder (filename, fd); - } - else - { - if (S_ISREG (stats.st_mode)) - { - if (lseek (fd, 0L, SEEK_END) <= number) - /* The file is shorter than we want, or just the right size, so - print the whole file. */ - lseek (fd, 0L, SEEK_SET); - else - /* The file is longer than we want, so go back. */ - lseek (fd, -number, SEEK_END); - dump_remainder (filename, fd); - } - else - return pipe_bytes (filename, fd, number); - } - return 0; -} - -/* Display the last part of file FILENAME, open for reading on FD, - using NUMBER lines. - Return 0 if successful, 1 if an error occurred. */ - -int -tail_lines (filename, fd, number) - char *filename; - int fd; - long number; -{ - struct stat stats; - long length; - - if (fstat (fd, &stats)) - { - error (0, errno, "%s", filename); - return 1; - } - - if (from_start) - { - if (start_lines (filename, fd, number)) - return 1; - dump_remainder (filename, fd); - } - else - { - if (S_ISREG (stats.st_mode)) - { - length = lseek (fd, 0L, SEEK_END); - if (length != 0 && file_lines (filename, fd, number, length)) - return 1; - dump_remainder (filename, fd); + argc -= optind; + argv += optind; + + /* + * If displaying in reverse, don't permit follow option, and convert + * style values. + */ + if (rflag) { + if (fflag) + usage(); + if (style == FBYTES) + style = RBYTES; + if (style == FLINES) + style = RLINES; } - else - return pipe_lines (filename, fd, number); - } - return 0; -} - -/* Print the last NUMBER lines from the end of file FD. - Go backward through the file, reading `BUFSIZE' bytes at a time (except - probably the first), until we hit the start of the file or have - read NUMBER newlines. - POS starts out as the length of the file (the offset of the last - byte of the file + 1). - Return 0 if successful, 1 if an error occurred. */ -int -file_lines (filename, fd, number, pos) - char *filename; - int fd; - long number; - long pos; -{ - char buffer[BUFSIZE]; - int bytes_read; - int i; /* Index into `buffer' for scanning. */ - - if (number == 0) - return 0; - - /* Set `bytes_read' to the size of the last, probably partial, buffer; - 0 < `bytes_read' <= `BUFSIZE'. */ - bytes_read = pos % BUFSIZE; - if (bytes_read == 0) - bytes_read = BUFSIZE; - /* Make `pos' a multiple of `BUFSIZE' (0 if the file is short), so that all - reads will be on block boundaries, which might increase efficiency. */ - pos -= bytes_read; - lseek (fd, pos, SEEK_SET); - bytes_read = read (fd, buffer, bytes_read); - if (bytes_read == -1) - { - error (0, errno, "%s", filename); - return 1; - } - - /* Count the incomplete line on files that don't end with a newline. */ - if (bytes_read && buffer[bytes_read - 1] != '\n') - --number; + /* + * If style not specified, the default is the whole file for -r, and + * the last 10 lines if not -r. + */ + if (style == NOTSET) + if (rflag) { + off = 0; + style = REVERSE; + } else { + off = 10; + style = RLINES; + } - do - { - /* Scan backward, counting the newlines in this bufferfull. */ - for (i = bytes_read - 1; i >= 0; i--) - { - /* Have we counted the requested number of newlines yet? */ - if (buffer[i] == '\n' && number-- == 0) - { - /* If this newline wasn't the last character in the buffer, - print the text after it. */ - if (i != bytes_read - 1) - write (1, &buffer[i + 1], bytes_read - (i + 1)); - return 0; - } + if (fname = *argv) { + if ((fp = fopen(fname, "r")) == NULL) + ierr(); + } else { + fp = stdin; + fname = "stdin"; } - /* Not enough newlines in that bufferfull. */ - if (pos == 0) - { - /* Not enough lines in the file; print the entire file. */ - lseek (fd, 0L, SEEK_SET); - return 0; - } - pos -= BUFSIZE; - lseek (fd, pos, SEEK_SET); - } - while ((bytes_read = read (fd, buffer, BUFSIZE)) > 0); - if (bytes_read == -1) - { - error (0, errno, "%s", filename); - return 1; - } - return 0; -} - -/* Print the last NUMBER lines from the end of the standard input, - open for reading as pipe FD. - Buffer the text as a linked list of LBUFFERs, adding them as needed. - Return 0 if successful, 1 if an error occured. */ - -int -pipe_lines (filename, fd, number) - char *filename; - int fd; - long number; -{ - struct linebuffer - { - int nbytes, nlines; - char buffer[BUFSIZE]; - struct linebuffer *next; - }; - typedef struct linebuffer LBUFFER; - LBUFFER *first, *last, *tmp; - int i; /* Index into buffers. */ - int total_lines = 0; /* Total number of newlines in all buffers. */ - int errors = 0; - first = last = (LBUFFER *) malloc (sizeof (LBUFFER)); - first->nbytes = first->nlines = 0; - first->next = NULL; - tmp = (LBUFFER *) malloc (sizeof (LBUFFER)); + if (fstat(fileno(fp), &sb)) + ierr(); - /* Input is always read into a fresh buffer. */ - while ((tmp->nbytes = read (fd, tmp->buffer, BUFSIZE)) > 0) - { - tmp->nlines = 0; - tmp->next = NULL; - - /* Count the number of newlines just read. */ - for (i = 0; i < tmp->nbytes; i++) - if (tmp->buffer[i] == '\n') - ++tmp->nlines; - total_lines += tmp->nlines; - - /* If there is enough room in the last buffer read, just append the new - one to it. This is because when reading from a pipe, `nbytes' can - often be very small. */ - if (tmp->nbytes + last->nbytes < BUFSIZE) - { - bcopy (tmp->buffer, &last->buffer[last->nbytes], tmp->nbytes); - last->nbytes += tmp->nbytes; - last->nlines += tmp->nlines; + /* + * Determine if input is a pipe. 4.4BSD will set the SOCKET + * bit in the st_mode field for pipes. Fix this then. + */ + if (lseek(fileno(fp), 0L, SEEK_CUR) == -1 && errno == ESPIPE) { + errno = 0; + fflag = 0; /* POSIX.2 requires this. */ } - else - { - /* If there's not enough room, link the new buffer onto the end of - the list, then either free up the oldest buffer for the next - read if that would leave enough lines, or else malloc a new one. - Some compaction mechanism is possible but probably not - worthwhile. */ - last = last->next = tmp; - if (total_lines - first->nlines > number) - { - tmp = first; - total_lines -= first->nlines; - first = first->next; - } - else - tmp = (LBUFFER *) malloc (sizeof (LBUFFER)); - } - } - if (tmp->nbytes == -1) - { - error (0, errno, "%s", filename); - errors = 1; - free ((char *) tmp); - goto free_lbuffers; - } - - free ((char *) tmp); - /* This prevents a core dump when the pipe contains no newlines. */ - if (number == 0) - goto free_lbuffers; - - /* Count the incomplete line on files that don't end with a newline. */ - if (last->buffer[last->nbytes - 1] != '\n') - { - ++last->nlines; - ++total_lines; - } - - /* Run through the list, printing lines. First, skip over unneeded - buffers. */ - for (tmp = first; total_lines - tmp->nlines > number; tmp = tmp->next) - total_lines -= tmp->nlines; - - /* Find the correct beginning, then print the rest of the file. */ - if (total_lines > number) - { - char *cp; - - /* Skip `total_lines' - `number' newlines. We made sure that - `total_lines' - `number' <= `tmp->nlines'. */ - cp = tmp->buffer; - for (i = total_lines - number; i; --i) - while (*cp++ != '\n') - /* Do nothing. */ ; - i = cp - tmp->buffer; - } - else - i = 0; - write (1, &tmp->buffer[i], tmp->nbytes - i); - - for (tmp = tmp->next; tmp; tmp = tmp->next) - write (1, tmp->buffer, tmp->nbytes); - -free_lbuffers: - while (first) - { - tmp = first->next; - free ((char *) first); - first = tmp; - } - return errors; + if (rflag) + reverse(fp, style, off, &sb); + else + forward(fp, style, off, &sb); + exit(rval); } -/* Print the last NUMBER characters from the end of pipe FD. - This is a stripped down version of pipe_lines. - Return 0 if successful, 1 if an error occurred. */ - -int -pipe_bytes (filename, fd, number) - char *filename; - int fd; - long number; +/* + * Convert the obsolete argument form into something that getopt can handle. + * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't + * the option argument for a -b, -c or -n option gets converted. + */ +static void +obsolete(argv) + char **argv; { - struct charbuffer - { - int nbytes; - char buffer[BUFSIZE]; - struct charbuffer *next; - }; - typedef struct charbuffer CBUFFER; - CBUFFER *first, *last, *tmp; - int i; /* Index into buffers. */ - int total_bytes = 0; /* Total characters in all buffers. */ - int errors = 0; - - first = last = (CBUFFER *) malloc (sizeof (CBUFFER)); - first->nbytes = 0; - first->next = NULL; - tmp = (CBUFFER *) malloc (sizeof (CBUFFER)); - - /* Input is always read into a fresh buffer. */ - while ((tmp->nbytes = read (fd, tmp->buffer, BUFSIZE)) > 0) - { - tmp->next = NULL; + register char *ap, *p, *t; + int len; + char *start; + + while (ap = *++argv) { + /* Return if "--" or not an option of any form. */ + if (ap[0] != '-') { + if (ap[0] != '+') + return; + } else if (ap[1] == '-') + return; + + switch(*++ap) { + /* Old-style option. */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + + /* Malloc space for dash, new option and argument. */ + len = strlen(*argv); + if ((start = p = malloc(len + 3)) == NULL) + err("%s", strerror(errno)); + *p++ = '-'; + + /* + * Go to the end of the option argument. Save off any + * trailing options (-3lf) and translate any trailing + * output style characters. + */ + t = *argv + len - 1; + if (*t == 'f' || *t == 'r') { + *p++ = *t; + *t-- = '\0'; + } + switch(*t) { + case 'b': + *p++ = 'b'; + *t = '\0'; + break; + case 'c': + *p++ = 'c'; + *t = '\0'; + break; + case 'l': + *t = '\0'; + /* FALLTHROUGH */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + *p++ = 'n'; + break; + default: + err("illegal option -- %s", *argv); + } + *p++ = *argv[0]; + (void)strcpy(p, ap); + *argv = start; + continue; + + /* + * Options w/ arguments, skip the argument and continue + * with the next option. + */ + case 'b': + case 'c': + case 'n': + if (!ap[1]) + ++argv; + /* FALLTHROUGH */ + /* Options w/o arguments, continue with the next option. */ + case 'f': + case 'r': + continue; - total_bytes += tmp->nbytes; - /* If there is enough room in the last buffer read, just append the new - one to it. This is because when reading from a pipe, `nbytes' can - often be very small. */ - if (tmp->nbytes + last->nbytes < BUFSIZE) - { - bcopy (tmp->buffer, &last->buffer[last->nbytes], tmp->nbytes); - last->nbytes += tmp->nbytes; - } - else - { - /* If there's not enough room, link the new buffer onto the end of - the list, then either free up the oldest buffer for the next - read if that would leave enough characters, or else malloc a new - one. Some compaction mechanism is possible but probably not - worthwhile. */ - last = last->next = tmp; - if (total_bytes - first->nbytes > number) - { - tmp = first; - total_bytes -= first->nbytes; - first = first->next; - } - else - { - tmp = (CBUFFER *) malloc (sizeof (CBUFFER)); - } + /* Illegal option, return and let getopt handle it. */ + default: + return; + } } - } - if (tmp->nbytes == -1) - { - error (0, errno, "%s", filename); - errors = 1; - free ((char *) tmp); - goto free_cbuffers; - } - - free ((char *) tmp); - - /* Run through the list, printing characters. First, skip over unneeded - buffers. */ - for (tmp = first; total_bytes - tmp->nbytes > number; tmp = tmp->next) - total_bytes -= tmp->nbytes; - - /* Find the correct beginning, then print the rest of the file. - We made sure that `total_bytes' - `number' <= `tmp->nbytes'. */ - if (total_bytes > number) - i = total_bytes - number; - else - i = 0; - write (1, &tmp->buffer[i], tmp->nbytes - i); - - for (tmp = tmp->next; tmp; tmp = tmp->next) - write (1, tmp->buffer, tmp->nbytes); - -free_cbuffers: - while (first) - { - tmp = first->next; - free ((char *) first); - first = tmp; - } - return errors; -} - -/* Skip NUMBER characters from the start of pipe FD, and print - any extra characters that were read beyond that. - Return 1 on error, 0 if ok. */ - -int -start_bytes (filename, fd, number) - char *filename; - int fd; - long number; -{ - char buffer[BUFSIZE]; - int bytes_read = 0; - - while (number > 0 && (bytes_read = read (fd, buffer, BUFSIZE)) > 0) - number -= bytes_read; - if (bytes_read == -1) - { - error (0, errno, "%s", filename); - return 1; - } - else if (number < 0) - write (1, &buffer[bytes_read + number], -number); - return 0; } -/* Skip NUMBER lines at the start of file or pipe FD, and print - any extra characters that were read beyond that. - Return 1 on error, 0 if ok. */ - -int -start_lines (filename, fd, number) - char *filename; - int fd; - long number; +static void +usage() { - char buffer[BUFSIZE]; - int bytes_read = 0; - int bytes_to_skip = 0; - - while (number && (bytes_read = read (fd, buffer, BUFSIZE)) > 0) - { - bytes_to_skip = 0; - while (bytes_to_skip < bytes_read) - if (buffer[bytes_to_skip++] == '\n' && --number == 0) - break; - } - if (bytes_read == -1) - { - error (0, errno, "%s", filename); - return 1; - } - else if (bytes_to_skip < bytes_read) - write (1, &buffer[bytes_to_skip], bytes_read - bytes_to_skip); - return 0; -} - -/* Display file FILENAME from the current position in FD - to the end. If `forever' is nonzero, keep reading from the - end of the file until killed. */ - -void -dump_remainder (filename, fd) - char *filename; - int fd; -{ - char buffer[BUFSIZE]; - int bytes_read; - -output: - while ((bytes_read = read (fd, buffer, BUFSIZE)) > 0) - write (1, buffer, bytes_read); - if (bytes_read == -1) - error (1, errno, "%s", filename); - if (forever) - { - sleep (1); - goto output; - } -} - -void -parse_unit (str) - char *str; -{ - int arglen = strlen (str); - - if (arglen == 0) - return; - - switch (str[arglen - 1]) - { - case 'b': - unit_size = 512; - str[arglen - 1] = '\0'; - break; - case 'k': - unit_size = 1024; - str[arglen - 1] = '\0'; - break; - case 'm': - unit_size = 1048576; - str[arglen - 1] = '\0'; - break; - } -} - -/* Convert STR, a string of ASCII digits, into an unsigned integer. - Return -1 if STR does not represent a valid unsigned integer. */ - -long -atou (str) - char *str; -{ - unsigned long value; - - for (value = 0; ISDIGIT (*str); ++str) - value = value * 10 + *str - '0'; - return *str ? -1 : value; -} - -void -usage () -{ - fprintf (stderr, "\ -Usage: %s [-c [+]N[bkm]] [-n [+]N] [-fqv] [--bytes=[+]N[bkm]] [--lines=[+]N]\n\ - [--follow] [--quiet] [--silent] [--verbose] [file...]\n\ - %s [{-,+}Nbcfklmqv] [file...]\n", program_name, program_name); - exit (1); -} -/* error.c -- error handler for noninteractive utilities - Copyright (C) 1990, 1991 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* David MacKenzie */ - - -#include <stdarg.h> -#define VA_START(args, lastarg) va_start(args, lastarg) - -/* Print the program name and error message MESSAGE, which is a printf-style - format string with optional args. - If ERRNUM is nonzero, print its corresponding system error message. - Exit with status STATUS if it is nonzero. */ -/* VARARGS */ -void -error (int status, int errnum, char *message, ...) -{ - extern char *program_name; - va_list args; - - fprintf (stderr, "%s: ", program_name); - va_start (args, message); - vfprintf (stderr, message, args); - va_end (args); - if (errnum) - fprintf (stderr, ": %s", strerror (errnum)); - putc ('\n', stderr); - fflush (stderr); - if (status) - exit (status); + (void)fprintf(stderr, + "usage: tail [-f | -r] [-b # | -c # | -n #] [file]\n"); + exit(1); } diff --git a/usr.bin/telnet/Makefile b/usr.bin/telnet/Makefile index 028793301c..96bf01563d 100644 --- a/usr.bin/telnet/Makefile +++ b/usr.bin/telnet/Makefile @@ -1,35 +1,4 @@ -# -# Copyright (c) 1990 The Regents of the University of California. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# + # @(#)Makefile 5.6 (Berkeley) 3/5/91 # @@ -38,9 +7,15 @@ PROG= telnet CFLAGS+=-DTERMCAP -DKLUDGELINEMODE -DUSE_TERMIO CFLAGS+=-I${.CURDIR}/../../lib +.if exists(/usr/lib/libcrypt.a) +#CFLAGS+= -DENCRYPT +DPADD+= ${LIBCRYPT} +LDADD+= -lcrypt +.endif + +LDADD+= -ltermcap -ltelnet +DPADD+= ${LIBTERMCAP} ${LIBTELNET} -LDADD= -ltermcap -ltelnet -DPADD= ${LIBTERMCAP} SRCS= authenc.c commands.c main.c network.c ring.c sys_bsd.c telnet.c \ terminal.c tn3270.c utilities.c diff --git a/usr.bin/telnet/commands.c b/usr.bin/telnet/commands.c index 1d8e6a8c8e..863064f04a 100644 --- a/usr.bin/telnet/commands.c +++ b/usr.bin/telnet/commands.c @@ -291,7 +291,6 @@ sendcmd(argc, argv) int argc; char **argv; { - int what; /* what we are sending this time */ int count; /* how many bytes we are going to need to send */ int i; int question = 0; /* was at least one argument a question */ @@ -364,8 +363,8 @@ sendcmd(argc, argv) (s->narg > 1) ? argv[i+2] : 0); i += s->narg; } else { - NET2ADD(IAC, what); - printoption("SENT", IAC, what); + NET2ADD(IAC, s->what); + printoption("SENT", IAC, s->what); } } return (count == success); diff --git a/usr.bin/tftp/main.c b/usr.bin/tftp/main.c index 45c4f14e28..48ee1879fd 100644 --- a/usr.bin/tftp/main.c +++ b/usr.bin/tftp/main.c @@ -61,7 +61,7 @@ static char sccsid[] = "@(#)main.c 5.10 (Berkeley) 3/1/91"; #define TIMEOUT 5 /* secs between rexmt's */ -struct sockaddr_in sin; +struct sockaddr_in s_in; int f; short port; int trace; @@ -127,7 +127,7 @@ char *rindex(); main(argc, argv) char *argv[]; { - struct sockaddr_in sin; + struct sockaddr_in s_in; int top; sp = getservbyname("tftp", "udp"); @@ -140,9 +140,9 @@ main(argc, argv) perror("tftp: socket"); exit(3); } - bzero((char *)&sin, sizeof (sin)); - sin.sin_family = AF_INET; - if (bind(f, (struct sockaddr *)&sin, sizeof (sin)) < 0) { + bzero((char *)&s_in, sizeof (s_in)); + s_in.sin_family = AF_INET; + if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) { perror("tftp: bind"); exit(1); } @@ -180,13 +180,13 @@ setpeer(argc, argv) } host = gethostbyname(argv[1]); if (host) { - sin.sin_family = host->h_addrtype; - bcopy(host->h_addr, &sin.sin_addr, host->h_length); + s_in.sin_family = host->h_addrtype; + bcopy(host->h_addr, &s_in.sin_addr, host->h_length); strcpy(hostname, host->h_name); } else { - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = inet_addr(argv[1]); - if (sin.sin_addr.s_addr == -1) { + s_in.sin_family = AF_INET; + s_in.sin_addr.s_addr = inet_addr(argv[1]); + if (s_in.sin_addr.s_addr == -1) { connected = 0; printf("%s: unknown host\n", argv[1]); return; @@ -312,8 +312,8 @@ put(argc, argv) herror((char *)NULL); return; } - bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); - sin.sin_family = hp->h_addrtype; + bcopy(hp->h_addr, (caddr_t)&s_in.sin_addr, hp->h_length); + s_in.sin_family = hp->h_addrtype; connected = 1; strcpy(hostname, hp->h_name); } @@ -331,7 +331,7 @@ put(argc, argv) if (verbose) printf("putting %s to %s:%s [%s]\n", cp, hostname, targ, mode); - sin.sin_port = port; + s_in.sin_port = port; sendfile(fd, targ, mode); return; } @@ -349,7 +349,7 @@ put(argc, argv) if (verbose) printf("putting %s to %s:%s [%s]\n", argv[n], hostname, targ, mode); - sin.sin_port = port; + s_in.sin_port = port; sendfile(fd, targ, mode); } } @@ -405,8 +405,8 @@ get(argc, argv) herror((char *)NULL); continue; } - bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); - sin.sin_family = hp->h_addrtype; + bcopy(hp->h_addr, (caddr_t)&s_in.sin_addr, hp->h_length); + s_in.sin_family = hp->h_addrtype; connected = 1; strcpy(hostname, hp->h_name); } @@ -420,7 +420,7 @@ get(argc, argv) if (verbose) printf("getting from %s:%s to %s [%s]\n", hostname, src, cp, mode); - sin.sin_port = port; + s_in.sin_port = port; recvfile(fd, src, mode); break; } @@ -433,7 +433,7 @@ get(argc, argv) if (verbose) printf("getting from %s:%s to %s [%s]\n", hostname, src, cp, mode); - sin.sin_port = port; + s_in.sin_port = port; recvfile(fd, src, mode); } } diff --git a/usr.bin/tftp/tftp.c b/usr.bin/tftp/tftp.c index 6abc24b795..2e9114ce38 100644 --- a/usr.bin/tftp/tftp.c +++ b/usr.bin/tftp/tftp.c @@ -55,7 +55,7 @@ static char sccsid[] = "@(#)tftp.c 5.10 (Berkeley) 3/1/91"; extern int errno; -extern struct sockaddr_in sin; /* filled in by main */ +extern struct sockaddr_in s_in; /* filled in by main */ extern int f; /* the opened socket */ extern int trace; extern int verbose; @@ -122,7 +122,7 @@ send_data: if (trace) tpacket("sent", dp, size + 4); n = sendto(f, dp, size + 4, 0, - (struct sockaddr *)&sin, sizeof (sin)); + (struct sockaddr *)&s_in, sizeof (s_in)); if (n != size + 4) { perror("tftp: sendto"); goto abort; @@ -140,7 +140,7 @@ send_data: perror("tftp: recvfrom"); goto abort; } - sin.sin_port = from.sin_port; /* added */ + s_in.sin_port = from.sin_port; /* added */ if (trace) tpacket("received", ap, n); /* should verify packet came from server */ @@ -220,8 +220,8 @@ recvfile(fd, name, mode) send_ack: if (trace) tpacket("sent", ap, size); - if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&sin, - sizeof (sin)) != size) { + if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&s_in, + sizeof (s_in)) != size) { alarm(0); perror("tftp: sendto"); goto abort; @@ -239,7 +239,7 @@ send_ack: perror("tftp: recvfrom"); goto abort; } - sin.sin_port = from.sin_port; /* added */ + s_in.sin_port = from.sin_port; /* added */ if (trace) tpacket("received", dp, n); /* should verify client address */ @@ -279,7 +279,7 @@ send_ack: abort: /* ok to ack, since user */ ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ ap->th_block = htons((u_short)block); - (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&sin, sizeof (sin)); + (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&s_in, sizeof (s_in)); write_behind(file, convert); /* flush last buffer */ fclose(file); stopclock(); @@ -348,8 +348,8 @@ nak(error) length = strlen(pe->e_msg) + 4; if (trace) tpacket("sent", tp, length); - if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&sin, - sizeof (sin)) != length) + if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&s_in, + sizeof (s_in)) != length) perror("nak"); } diff --git a/usr.bin/tip/Makefile b/usr.bin/tip/Makefile index e68e838b9c..7600fad2ba 100644 --- a/usr.bin/tip/Makefile +++ b/usr.bin/tip/Makefile @@ -27,6 +27,8 @@ # writes on local side # BUFSIZ buffer sizing from stdio, must be fed # explicitly to remcap.c if not 1024 +# PULSE_DIAL use pulse dialing instead of touch-tone +# ATX5 basic responses w/BUSY signal detection PROG= tip CFLAGS+=-I${.CURDIR} -DV831 -DVENTEL -DHAYES -DCOURIER -DDEFBR=2400 \ @@ -35,8 +37,8 @@ CFLAGS+=-I${.CURDIR} -DV831 -DVENTEL -DHAYES -DCOURIER -DDEFBR=2400 \ BINOWN= uucp BINGRP= dialer BINMODE=4510 -LINKS= ${BINDIR}/tip ${BINDIR}/cu -MLINKS= tip.1 cu.1 +#LINKS= ${BINDIR}/tip ${BINDIR}/cu +#MLINKS= tip.1 cu.1 SRCS= acu.c acutab.c cmds.c cmdtab.c cu.c hunt.c log.c partab.c remcap.c \ remote.c tip.c tipout.c uucplock.c value.c vars.c biz22.c courier.c \ df.c dn11.c hayes.c v3451.c v831.c ventel.c diff --git a/usr.bin/tip/aculib/courier.c b/usr.bin/tip/aculib/courier.c index c7bb4c1d97..567c74f63e 100644 --- a/usr.bin/tip/aculib/courier.c +++ b/usr.bin/tip/aculib/courier.c @@ -215,7 +215,7 @@ again: if (strncmp(dialer_buf, "CONNECT", sizeof("CONNECT")-1) != 0) break; - for (bm = baud_msg ; bm ; bm++) + for (bm = baud_msg ; bm->msg ; bm++) if (strcmp(bm->msg, dialer_buf+sizeof("CONNECT")-1) == 0) { if (ioctl(FD, TIOCGETP, &sb) < 0) { diff --git a/usr.bin/tip/aculib/hayes.c b/usr.bin/tip/aculib/hayes.c index 80de7caac1..8a82202bc6 100644 --- a/usr.bin/tip/aculib/hayes.c +++ b/usr.bin/tip/aculib/hayes.c @@ -92,16 +92,24 @@ hay_dialer(num, acu) fflush(stdout); ioctl(FD, TIOCHPCL, 0); ioctl(FD, TIOCFLUSH, 0); /* get rid of garbage */ - write(FD, "ATv0\r", 5); /* tell modem to use short status codes */ +#ifdef ATX5 + write(FD, "ATm0x5v0\r", 9); /* mute speaker; short status codes */ +#else /* ATX5 */ + write(FD, "ATx0v0\r", 7); /* short status codes */ +#endif /* ATX5 */ gobble("\r"); gobble("\r"); - write(FD, "ATTD", 4); /* send dial command */ +#ifdef PULSE_DIAL + write(FD, "ATD", 3); /* send dial command */ +#else /* PULSE_DIAL */ + write(FD, "ATDT", 4); /* send dial command */ +#endif /* PULSE_DIAL */ write(FD, num, strlen(num)); state = DIALING; write(FD, "\r", 1); connected = 0; if (gobble("\r")) { - if ((dummy = gobble("01234")) != '1') + if ((dummy = gobble("012345678")) != '1') error_rep(dummy); else connected = 1; @@ -178,15 +186,18 @@ gobble(match) return (0); } alarm(number(value(DIALTIMEOUT))); - read(FD, &c, 1); - alarm(0); - c &= 0177; + if (read(FD, &c, 1)) { + alarm(0); + c &= 0177; #ifdef DEBUG - printf("%c 0x%x ", c, c); + printf("%c 0x%x ", c, c); #endif - for (i = 0; i < strlen(match); i++) - if (c == match[i]) - status = c; + for (i = 0; i < strlen(match); i++) + if (c == match[i]) + status = c; + } else { + status = *match; /* a hack to make some things work */ + } } while (status == 0); signal(SIGALRM, SIG_DFL); #ifdef DEBUG @@ -225,6 +236,18 @@ error_rep(c) printf("CONNECT 1200"); break; + case '6': + printf("NO DIALTONE"); + break; + + case '7': + printf("BUSY"); + break; + + case '8': + printf("NO ANSWER"); + break; + default: printf("Unknown Modem error: %c (0x%x)", c, c); } @@ -296,7 +319,8 @@ hay_sync() dumbuf[len] = '\0'; printf("hay_sync: (\"%s\") %d\n\r", dumbuf, retry); #endif - } + } else + return(1); ioctl(FD, TIOCCDTR, 0); ioctl(FD, TIOCSDTR, 0); } diff --git a/usr.bin/tip/remcap.c b/usr.bin/tip/remcap.c index c57356940c..5e6b56a3dc 100644 --- a/usr.bin/tip/remcap.c +++ b/usr.bin/tip/remcap.c @@ -78,9 +78,9 @@ char *RM; static char *tbuf; static int hopcount; /* detect infinite loops in termcap, init 0 */ -char *tskip(); +static char *tskip(); char *tgetstr(); -char *tdecode(); +static char *tdecode(); char *getenv(); static char *remotefile; diff --git a/usr.bin/tip/tip.1 b/usr.bin/tip/tip.1 index f1764c6da9..95d12e07ca 100644 --- a/usr.bin/tip/tip.1 +++ b/usr.bin/tip/tip.1 @@ -35,8 +35,8 @@ .Dt TIP 1 .Os BSD 4 .Sh NAME -.Nm tip , -.Nm cu +.Nm tip +.\" .Nm cu .Nd connect to a remote system .Sh SYNOPSIS .Nm tip @@ -47,31 +47,31 @@ .Op Fl v .Fl Ns Ns Ar speed .Ar phone\-number -.Nm cu -.Ar phone\-number -.Op Fl t -.Op Fl s Ar speed -.Op Fl a Ar acu -.Op Fl l Ar line -.Op Fl # +.\" .Nm cu +.\" .Ar phone\-number +.\" .Op Fl t +.\" .Op Fl s Ar speed +.\" .Op Fl a Ar acu +.\" .Op Fl l Ar line +.\" .Op Fl # .Sh DESCRIPTION .Nm Tip -and -.Ar cu -establish a full-duplex connection to another machine, +.\" and +.\" .Nm cu +establishes a full-duplex connection to another machine, giving the appearance of being logged in directly on the remote cpu. It goes without saying that you must have a login on the machine (or equivalent) to which you wish to connect. -The preferred interface is -.Nm tip . -The -.Ar cu -interface is included for those people attached to the -``call -.Ux Ns '' -command of version 7. This manual page -describes only -.Nm tip . +.\" The preferred interface is +.\" .Nm tip . +.\" The +.\" .Nm cu +.\" interface is included for those people attached to the +.\" ``call +.\" .Ux Ns '' +.\" command of version 7. This manual page +.\" describes only +.\" .Nm tip . .Pp Available Option: .Bl -tag -width indent @@ -426,6 +426,7 @@ Lock file to avoid conflicts with .Sh DIAGNOSTICS Diagnostics are, hopefully, self explanatory. .Sh SEE ALSO +.Xr cu 1 , .Xr remote 5 , .Xr phones 5 .Sh HISTORY diff --git a/usr.bin/tip/tipout.c b/usr.bin/tip/tipout.c index 7f0cba94c2..37edea8463 100644 --- a/usr.bin/tip/tipout.c +++ b/usr.bin/tip/tipout.c @@ -132,7 +132,7 @@ tipout() cnt = read(FD, buf, BUFSIZ); if (cnt <= 0) { /* lost carrier */ - if (cnt < 0 && errno == EIO) { + if ((cnt < 0 && errno == EIO) || (cnt == 0)) { sigblock(sigmask(SIGTERM)); intTERM(); /*NOTREACHED*/ diff --git a/usr.bin/tn3270/Makefile b/usr.bin/tn3270/Makefile new file mode 100644 index 0000000000..796835a478 --- /dev/null +++ b/usr.bin/tn3270/Makefile @@ -0,0 +1,8 @@ +# $Header: /b/source/CVS/src/usr.bin/tn3270/Makefile,v 1.2 1993/04/27 06:47:28 cgd Exp $ + +.if !make(install) +SUBDIR += tools +.endif +SUBDIR += tn3270 mset + +.include <bsd.subdir.mk> diff --git a/usr.bin/tn3270/Makefile.inc b/usr.bin/tn3270/Makefile.inc new file mode 100644 index 0000000000..ed0cced4df --- /dev/null +++ b/usr.bin/tn3270/Makefile.inc @@ -0,0 +1,6 @@ +# $Header: /b/source/CVS/src/usr.bin/tn3270/Makefile.inc,v 1.3 1993/06/18 05:41:32 cgd Exp $ + +CFLAGS += -DTERMCAP -DSRCRT -DKLUDGELINEMODE -DUSE_TERMIO -DTN3270 +KBD = unix.kbd + +.include "${.CURDIR}/../../Makefile.inc" diff --git a/usr.bin/tn3270/api/makefile b/usr.bin/tn3270/api/makefile deleted file mode 100644 index e92c56aaf0..0000000000 --- a/usr.bin/tn3270/api/makefile +++ /dev/null @@ -1,101 +0,0 @@ -# @(#)makefile 4.5 (Berkeley) 4/26/91 -# -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 1 00084 -# -------------------- ----- ---------------------- -# -# 28 Feb 93 Nick Handel Use correct mk template file. -# -DEBUG_FLAGS = #-g - -CFLAGS = ${DEBUG_FLAGS} -#CFLAGS = -I obj ${DEBUG_FLAGS} - -PRINT = lpr -p - -KBD = 3270pc.kbd -KBD = unix.kbd - -SRCS = apilib.c api_bsd.c api_exch.c asc_ebc.c astosc.c dctype.c \ - disp_asc.c ebc_disp.c - -ALLH = apilib.h api_exch.h asc_ebc.h astosc.h disp_asc.h dctype.h ebc_disp.h - - -ALLHC= ${ALLH} ${SRCS} -ALLPRINT = ${ALLHC} - -ALLSOURCE = ${ALLPRINT} makefile makefile.mak - -CLEANFILES = disp_out asc_disp.out astosc.out disp_asc.out \ - astosc.OUT disp_asc.OUT \ - test* test.o t1* t1.o t2* t2.o - -#LIBDIR = ${.CURDIR}/obj -#LINTLIBDIR = ${.CURDIR}/obj -LIB = api - -.DEFAULT: - sccs get $< - -sccsclean: - -sccs clean - -sccs get makefile - -action: - ${ACTION} - -test: apilib.a test.o - ${CC} ${CFLAGS} -o $@ test.o apilib.a - -t1: apilib.a t1.o - ${CC} ${CFLAGS} -o $@ t1.o apilib.a - -t2: apilib.a t2.o - ${CC} ${CFLAGS} -o $@ t2.o apilib.a - -print: - ${PRINT} ${ALLPRINT} - -clist: ${SRCS} - @for i in ${SRCS} ; \ - do (echo ${DIRPATH}$$i); done - -hclist: ${ALLHC} - @for i in ${ALLHC} ; \ - do (echo ${DIRPATH}$$i); done - -sourcelist: ${ALLSOURCE} - @for i in ${ALLSOURCE}; \ - do (echo ${DIRPATH}$$i); done - -astosc.OUT: ../ctlr/function.h ../ctlr/hostctlr.h ../ctlr/$(KBD) - (cd ${.CURDIR}/../tools; make mkastosc ) - -rm $@ astosc.out - ../tools/mkastosc ${.CURDIR}/../ctlr/hostctlr.h \ - ${.CURDIR}/../ctlr/function.h < \ - ${.CURDIR}/../ctlr/$(KBD) > $@ - ln $@ astosc.out - -asc_disp.out: ebc_disp.o - (cd ${.CURDIR}/../tools; make mkastods ) - -rm $@ - ../tools/mkastods > $@ - -disp_asc.OUT: ebc_disp.o - (cd ${.CURDIR}/../tools; make mkdstoas ) - -rm $@ disp_asc.out - ../tools/mkdstoas > $@ - ln $@ disp_asc.out - -# Some special dependencies... -astosc.o: astosc.OUT -disp_asc.o: disp_asc.OUT asc_disp.out - -.MAIN: myall -# Actually do some work -.include <bsd.lib.mk> - -myall: lib${LIB}.a - ranlib lib${LIB}.a diff --git a/usr.bin/tn3270/ascii/makefile b/usr.bin/tn3270/ascii/makefile deleted file mode 100644 index 99c2480a9d..0000000000 --- a/usr.bin/tn3270/ascii/makefile +++ /dev/null @@ -1,141 +0,0 @@ -# @(#)makefile 4.3 (Berkeley) 5/8/91 - -# msdos versus unix defines -O = .o -#PC_O = .obj - -X = -#PC_X = .exe - -L = -#PC_L = -link - -CC = cc -#PC_CC = cl - -MV = mv -#PC_MV = rename - -RM = rm -f -#PC_RM= erase - -LINT_ARGS = -#PC_LINT_ARGS = -DLINT_ARGS - -DEBUG_FLAGS = #-g -#PC_DEBUG_FLAGS = -Zi -Od - -AR = ar -AR1 = cr -AR2 = -AR3 = -#PC_AR = lib -#PC_AR1 = -#PC_AR2 = + -#PC_AR3 = ";" - -RANLIB = ranlib -#PC_RANLIB = echo "Done with " - -PRINT = print - -DEFINES = ${LINT_ARGS} - -INCLUDES = -I. - -OPTIMIZE = -O -OPTIMIZE = ${DEBUG_FLAGS} - -CFLAGS = $(OPTIMIZE) $(INCLUDES) $(DEFINES) - -# Lint flags -LINTFLAGS = -hbxaz - -KBD = 3270pc.kbd -KBD = unix.kbd - - -# The source files... -ALLH = map3270.h state.h - -ALLC = map3270.c mset.c termin.c - -# We don't include mset$O here... -ALLO = map3270$O termin$O - - -ALLHC= ${ALLH} ${ALLC} -ALLPRINT = default.map ${ALLHC} - -ALLSOURCE = ${ALLPRINT} makefile makefile.mak - -.s.o: - /lib/cpp -E $< | as -o $@ - -#.c.obj: -# ${CC} ${CFLAGS} -c $< - -asciilib.a: $(ALLO) - ${RM} $@ - for i in ${ALLO}; do (${AR} ${AR1} $@ ${AR2} $$i${AR3}); done - ${RANLIB} $@ - -clean: - for i in $(ALLO) errs makefile.bak asciilib.a; \ - do (${RM} $$i); done - -sccsclean: - -sccs clean - -sccs get makefile - -clist: ${ALLC} - @for i in ${ALLC} ; \ - do (echo ${DIRPATH}$$i); done - -hclist: ${ALLHC} - @for i in ${ALLHC} ; \ - do (echo ${DIRPATH}$$i); done - -sourcelist: ${ALLSOURCE} - @for i in ${ALLSOURCE}; \ - do (echo ${DIRPATH}$$i); done - -print: - ${PRINT} ${ALLPRINT} - -tags: ${ALLC} ${ALLH} - ctags -t ${ALLC} ${ALLH} - -action: - ${ACTION} - -lint: - lint ${LINTFLAGS} ${INCLUDES} ${DEFINES} ${ALLC} - -.DEFAULT: - sccs get $< - -depend: - grep '^#include' ${ALLC} | grep -v '<' | \ - sed -e 's/:[^"]*"\([^"]*\)".*/: \1/' \ - -e 's/\.c/$$O/' | \ - awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \ - else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \ - else rec = rec " " $$2 } } \ - END { print rec } ' > makedep - echo '$$r makedep' >>eddep - echo '/^# DO NOT DELETE THIS LINE/+1,$$d' >eddep - echo '$$r makedep' >>eddep - echo 'w' >>eddep - -rm -f makefile.bak - cp makefile makefile.bak - ed - makefile < eddep - rm eddep makedep - -# DO NOT DELETE THIS LINE - -map3270$O: state.h map3270.h ../general/globals.h default.map -mset$O: ../ctlr/function.h state.h map3270.h ../api/astosc.h -mset$O: ../general/globals.h -termin$O: ../general/general.h ../ctlr/function.h ../ctlr/externs.h -termin$O: ../ctlr/declare.h ../api/astosc.h state.h ../general/globals.h diff --git a/usr.bin/tn3270/ctlr/makefile b/usr.bin/tn3270/ctlr/makefile deleted file mode 100644 index 3b36755191..0000000000 --- a/usr.bin/tn3270/ctlr/makefile +++ /dev/null @@ -1,156 +0,0 @@ -# @(#)makefile 4.3 (Berkeley) 5/8/91 - -# msdos versus unix defines -O = .o -#PC_O = .obj - -X = -#PC_X = .exe - -L = -#PC_L = -link - -CC = cc -#PC_CC = cl - -MV = mv -#PC_MV = rename - -RM = rm -f -#PC_RM= erase - -LINT_ARGS = -#PC_LINT_ARGS = -DLINT_ARGS - -DEBUG_FLAGS = #-g -#PC_DEBUG_FLAGS = -Zi -Od - -AR = ar -AR1 = cr -AR2 = -AR3 = -#PC_AR = lib -#PC_AR1 = -#PC_AR2 = + -#PC_AR3 = ";" - -RANLIB = ranlib -#PC_RANLIB = echo "Done with " - -PRINT = print - -DEFINES = ${LINT_ARGS} - -INCLUDES = -I. - -OPTIMIZE = -O -OPTIMIZE = ${DEBUG_FLAGS} - -CFLAGS = $(OPTIMIZE) $(INCLUDES) $(DEFINES) - -# Lint flags -LINTFLAGS = -hbxaz - -# Which keyboard are we emulating. -KBD = ${.CURDIR}/3180.kbd -KBD = ${.CURDIR}/3270pc.kbd -KBD = ${.CURDIR}/unix.kbd - -# The source files... -ALLH = api.h declare.h externs.h function.h hostctlr.h oia.h \ - options.h screen.h scrnctlr.h - -ALLC = api.c function.c inbound.c oia.c options.c outbound.c - -# Note: NO function.o! -ALLO = api$O inbound$O oia$O options$O outbound$O - -ALLHC= ${ALLH} ${ALLC} -ALLPRINT = 3180.kbd 3270pc.kbd unix.kbd ${ALLHC} - -ALLSOURCE = ${ALLPRINT} makefile makefile.mak - -.s.o: - /lib/cpp -E $< | as -o $@ - -#.c.obj: -# ${CC} ${CFLAGS} -c $< - -ctlrlib.a: ${ALLO} - ${RM} $@ - for i in ${ALLO}; do (${AR} ${AR1} $@ ${AR2} $$i${AR3}); done - ${RANLIB} $@ - -clean: - for i in $(ALLO) mset tn3270 prt3270 m4.out errs \ - makefile.bak ctlrlib.a kbd.out TMPfunc.out; \ - do (${RM} $$i); done - -sccsclean: - -sccs clean - -sccs get makefile - -clist: ${ALLC} - @for i in ${ALLC} ; \ - do (echo ${DIRPATH}$$i); done - -hclist: ${ALLHC} - @for i in ${ALLHC} ; \ - do (echo ${DIRPATH}$$i); done - -sourcelist: ${ALLSOURCE} - @for i in ${ALLSOURCE}; \ - do (echo ${DIRPATH}$$i); done - -print: - ${PRINT} ${ALLPRINT} - -tags: ${ALLC} ${ALLH} - ctags -t ${ALLC} ${ALLH} - -action: - ${ACTION} - -lint: - lint ${LINTFLAGS} ${INCLUDES} ${DEFINES} -DTN3270 ${ALLC} -lcurses - -.DEFAULT: - sccs get $< - -kbd.out: $(KBD) hostctlr.h - (cd ${.CURDIR}/../tools; make mkhits$X ) - ${RM} $@ TMPfunc.out - $(CC) $(CFLAGS) -E ${.CURDIR}/function.c > TMPfunc.out - ../tools/mkhits ${.CURDIR}/../ctlr/hostctlr.h \ - ../ctlr/TMPfunc.out < $(KBD) > $@ - ${RM} TMPfunc.out - -depend: - grep '^#include' ${ALLC} | grep -v '<' | \ - sed -e 's/:[^"]*"\([^"]*\)".*/: \1/' \ - -e 's/\.c/$$O/' | \ - awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \ - else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \ - else rec = rec " " $$2 } } \ - END { print rec } ' > makedep - echo '$$r makedep' >>eddep - echo '/^# DO NOT DELETE THIS LINE/+1,$$d' >eddep - echo '$$r makedep' >>eddep - echo 'w' >>eddep - -rm -f makefile.bak - cp makefile makefile.bak - ed - makefile < eddep - rm eddep makedep - -# DO NOT DELETE THIS LINE - -api$O: api.h ../general/general.h ../api/disp_asc.h screen.h oia.h -api$O: ../general/globals.h -function$O: function.h -inbound$O: ../general/general.h function.h hostctlr.h oia.h scrnctlr.h screen.h -inbound$O: options.h ../api/dctype.h ../api/ebc_disp.h ../general/globals.h -inbound$O: externs.h declare.h kbd.out -oia$O: ../general/general.h oia.h ../general/globals.h -options$O: options.h ../general/globals.h declare.h -outbound$O: ../general/general.h hostctlr.h oia.h screen.h ../api/ebc_disp.h -outbound$O: ../general/globals.h externs.h declare.h diff --git a/usr.bin/tn3270/general/makefile b/usr.bin/tn3270/general/makefile deleted file mode 100644 index 2a7d4db072..0000000000 --- a/usr.bin/tn3270/general/makefile +++ /dev/null @@ -1,157 +0,0 @@ -# @(#)makefile 4.3 (Berkeley) 4/26/91 - -# msdos versus unix defines -O = .o -#PC_O = .obj - -X = -#PC_X = .exe - -L = -#PC_L = -link - -CC = cc -#PC_CC = cl - -MV = mv -#PC_MV = rename - -RM = rm -f -#PC_RM= erase - -LINT_ARGS = -#PC_LINT_ARGS = -DLINT_ARGS - -DEBUG_FLAGS = -g -#PC_DEBUG_FLAGS = -Zi -Od - -AR = ar -AR1 = cr -AR2 = -AR3 = -#PC_AR = lib -#PC_AR1 = -#PC_AR2 = + -#PC_AR3 = ";" - -RANLIB = ranlib -#PC_RANLIB = echo "Done with " - -PRINT = print - -DEFINES = ${LINT_ARGS} - -INCLUDES = -I. - -OPTIMIZE = -O -OPTIMIZE = ${DEBUG_FLAGS} - -CFLAGS = $(OPTIMIZE) $(INCLUDES) $(DEFINES) - -# Lint flags -LINTFLAGS = -hbxaz -# How to install the bloody thing... - -DESTDIR= - -BINDIR = $(DESTDIR)/usr/ucb -ETCDIR = $(DESTDIR)/etc -MANDIR = $(DESTDIR)/usr/man/man - -# Names for the terminal libraries... -LIBCURSES = -lcurses -LIBTERM = -ltermlib - -# The source files... -ALLH = general.h globals.h - -ALLC = globals.c - -# -# In a vax environment, we use vaxbsubs.s, which gives us a fair amount -# of increased performance. We have provided genbsubs.c, which perform -# (more or less) the same function. -SUBS = vaxbsubs.s -SUBS = genbsubs.c - -SUBSO = vaxbsubs$O -SUBSO = genbsubs$O - -ALLS = vaxbsubs.s - -ALLHC= ${ALLH} ${ALLC} -ALLPRINT = ${ALLHC} vaxbsubs.s genbsubs.c - -ALLSOURCE = ${ALLPRINT} makefile makefile.mak - -ALLS = - -ALLO = globals$O ${SUBSO} - -.s.o: - /lib/cpp -E $< | as -o $@ - -#.c.obj: -# ${CC} ${CFLAGS} -c $< - -generallib.a: ${ALLO} - ${RM} $@ - for i in ${ALLO}; do (${AR} ${AR1} $@ ${AR2} $$i${AR3}); done - ${RANLIB} $@ - -clean: - for i in $(ALLO) errs makefile.bak generallib.a; \ - do (${RM} $$i); done - -sccsclean: - -sccs clean - -sccs get makefile - -clist: ${ALLC} - @for i in ${ALLC} ; \ - do (echo ${DIRPATH}$$i); done - -hclist: ${ALLHC} - @for i in ${ALLHC} ; \ - do (echo ${DIRPATH}$$i); done - -sourcelist: ${ALLSOURCE} - @for i in ${ALLSOURCE}; \ - do (echo ${DIRPATH}$$i); done - -print: - ${PRINT} ${ALLPRINT} - -tags: ${ALLC} ${ALLH} - ctags -t ${ALLC} ${ALLH} - -action: - ${ACTION} - -lint: - lint ${LINTFLAGS} ${INCLUDES} ${DEFINES} ${ALLC} - -.DEFAULT: - sccs get $< - -depend: - grep '^#include' ${ALLC} ${ALLH} | grep -v '<' | \ - sed -e 's/:[^"]*"\([^"]*\)".*/: \1/' \ - -e 's/\.c/$$O/' | \ - awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \ - else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \ - else rec = rec " " $$2 } } \ - END { print rec } ' > makedep - echo '$$r makedep' >>eddep - echo '/^# DO NOT DELETE THIS LINE/+1,$$d' >eddep - echo '$$r makedep' >>eddep - echo 'w' >>eddep - -rm -f makefile.bak - cp makefile makefile.bak - ed - makefile < eddep - rm eddep makedep - -# DO NOT DELETE THIS LINE - -globals$O: ../ctlr/hostctlr.h ../ctlr/oia.h ../ctlr/options.h ../ctlr/screen.h -globals$O: globals.h ../general/general.h diff --git a/usr.bin/tn3270/makefile b/usr.bin/tn3270/makefile deleted file mode 100644 index 2c9aefd229..0000000000 --- a/usr.bin/tn3270/makefile +++ /dev/null @@ -1,294 +0,0 @@ -# @(#)makefile 4.9 (Berkeley) 5/8/91 - -# Makefile for tn3270 and friends... -# -# This is the makefile for tn3270. Note that we use the 4.3+ telnet -# (compiled with special options; see below) to provide the telnet -# support we need. -# -# The following are the defines that may be passed (via the cc -# -D option) to the compiler. -# -# TN3270 - This is to be linked with tn3270. Necessary -# for creating tn3270. Only for compiling -# telnet. -# -# NOT43 - Allows the program to compile and run on -# a 4.2BSD system. -# -# PUTCHAR - Within tn3270, on a NOT43 system, -# allows the use of the 4.3 curses -# (greater speed updating the screen). -# You need the 4.3 curses for this to work. -# -# FD_SETSIZE - On whichever system, if this isn't defined, -# we patch over the FD_SET, etc., macros with -# some homebrewed ones. -# -# SO_OOBINLINE - This is a socket option which we would like -# to set to allow TCP urgent data to come -# to us "inline". This is NECESSARY for -# CORRECT operation, and desireable for -# simpler operation. -# -# LNOFLSH - Detects the presence of the LNOFLSH bit -# in the tty structure. -# -# -# -# TERMCAP - Define this if your system is termcap based, -# otherwise a terminfo based system is assumed. -# -# SRCRT - Includes code to allow you to specify -# source routes. -# Format is: -# [!]@hop1@hop2...[@|:]dst -# Leading ! means strict source route. -# -# NOSTRNCASECMP - Define this if you do not have strncasecmp() in -# your C libarary. -# -# USE_TERMIO - Define this if you have System V termio -# structures. What is here is how things -# are on Cray computers. -# -# KLUDGELINEMODE - Define this to get the kludged up version -# of linemode that was in 4.3BSD. This is a -# good thing to have around for talking to -# older systems. -# -# -# Here are some which are used throughout the system: -# -# unix - Compiles in unix specific stuff. -# -# msdos - Compiles in msdos specific stuff. -# - -# msdos versus unix defines -O = .o -#PC_O = .obj - -X = -#PC_X = .exe - -L = -#PC_L = -link - -CC = cc -#PC_CC = cl - -MV = mv -#PC_MV = rename - -RM = rm -f -#PC_RM= erase - -LINT_ARGS = -#PC_LINT_ARGS = -DLINT_ARGS - -DEBUG_FLAGS = #-g -#PC_DEBUG_FLAGS = -Zi -Od - -AR = ar -AR1 = cr -AR2 = -AR3 = -#PC_AR = lib -#PC_AR1 = -#PC_AR2 = + -#PC_AR3 = ";" - -RANLIB = ranlib -#PC_RANLIB = echo "Done with " - - -PRINT = print -ACTION = @sccs tell - -DEFINES= ${LINT_ARGS} -DTERMCAP -DSRCRT -DKLUDGELINEMODE -DUSE_TERMIO - -INCLUDES = -I. -I.. -I../../lib -I../../../lib - -OPTIMIZE = -O -OPTIMIZE = ${DEBUG_FLAGS} - -CFLAGS = $(OPTIMIZE) $(INCLUDES) $(DEFINES) - -# Lint flags -LINTFLAGS = -hbxaz -# How to install the bloody thing... - -DESTDIR= - -BINDIR = $(DESTDIR)/usr/bin -MAN1DIR = $(DESTDIR)/usr/share/man/cat1 -MAN5DIR = $(DESTDIR)/usr/share/man/cat5 - -# Names for the terminal libraries... -LIBCURSES = -lcurses -LIBTERM = -ltermlib - -#PC_LIBCURSES = -#PC_LIBTERM = - -# The source files... -ALLH = telextrn.h - -MSMAIN = ${.CURDIR}/ascii/mset.c - -ALLC = - -ALLO = mset$O - -ALLHC= ${ALLH} ${ALLC} -ALLPRINT = ${ALLHC} - -ALLSOURCE = ${ALLPRINT} makefile makefile.mak makefile_4.2 README \ - mset.1 tn3270.1 map3270.5 - -SYS = sys_curses -#PC_SYS = sys_dos - -# The places where the various components live... - -SUBDIR = ${.CURDIR}/api ${.CURDIR}/ascii ${.CURDIR}/ctlr \ - ${.CURDIR}/general ${.CURDIR}/${SYS} ${.CURDIR}/telnet - -# The following are directories we don't do regular make's in, but -# we do make everywhere, print, and sourcelist in. - -EXTRADIR = ${.CURDIR}/tools - -# The libraries we use. The order here is important. -# syslib.a and ctlrlib.a should come first, then the rest. -SUBLIB = ${SYS}/syslib.a ctlr/ctlrlib.a \ - ascii/asciilib.a general/generallib.a - -.s.o: - /lib/cpp -E $< | as -o $@ - -#.c.obj: -# ${CC} ${CFLAGS} -c $< - -all: FRC tn3270$X mset$X - -FRC: - for i in ${SUBDIR}; \ - do (cd $$i; make ${MFLAGS} "CFLAGS=${CFLAGS}"); done - -tn3270$X: telnet/telprog.o ${SUBLIB} api/libapi.a - ${CC} ${CFLAGS} -o tn3270 telnet/telprog.o \ - $L ${SUBLIB} api/libapi.a $(LIBCURSES) $(LIBTERM) \ - -ltelnet - -#PC_tn3270$X: -#PC_ link <@< -#PC_ telnet -#PC_ tn3270 -#PC_ nul -#PC_ ${SUBLIB} api/libapi.a+ -#PC_ \lib\ublib\ubtcp -#PC_ _PC_< - -mset$X: mset$O ascii/map3270$O - ${CC} ${CFLAGS} -o mset mset$O ascii/map3270$O $L api/libapi.a - -mset$O: $(MSMAIN) - $(CC) $(CFLAGS) -c $(MSMAIN) - -install: tn3270$X mset$X tn3270.0 mset.0 map3270.0 - install -m 755 -o bin -g bin -s tn3270 $(BINDIR) - install -m 755 -o bin -g bin -s mset $(BINDIR) - install -c -o bin -g bin -m 444 tn3270.0 mset.0 $(MAN1DIR) - install -c -o bin -g bin -m 444 map3270.0 $(MAN5DIR) - -action: - ${ACTION} - -clist: ${ALLHC} - @for i in ${SUBDIR}; \ - do (cd $$i; make ${MFLAGS} "DIRPATH=${DIRPATH}$$i/" \ - clist); done - -hclist: ${ALLHC} - @for i in ${SUBDIR}; \ - do (cd $$i; make ${MFLAGS} "DIRPATH=${DIRPATH}$$i/" \ - hclist); done - -everywhere: action - for i in ${SUBDIR} ${EXTRADIR}; \ - do (echo "[$$i]"; cd $$i; make ${MFLAGS} action \ - "ACTION=${ACTION}"); done - -cleandir clean: - for i in $(ALLO) mset tn3270 errs makefile.bak; \ - do (${RM} $$i); done - for i in ${SUBDIR} ${EXTRADIR}; \ - do (cd $$i; make ${MFLAGS} clean); done - -sccsclean: - -sccs clean - -sccs get makefile - for i in ${SUBDIR} ${EXTRADIR}; \ - do (cd $$i; make ${MFLAGS} sccsclean); done - -print: - ${PRINT} ${ALLPRINT} - for i in ${SUBDIR} ${EXTRADIR}; \ - do (cd $$i; make ${MFLAGS} "PRINT=${PRINT}" print); done - -tags: ${ALLC} ${ALLH} - ctags -t `make ${MFLAGS} hclist` - -sourcelist: ${ALLSOURCE} - @for i in ${ALLSOURCE}; \ - do (echo ${DIRPATH}$$i); done - @for i in ${SUBDIR} ${EXTRADIR}; \ - do (cd $$i; make ${MFLAGS} "DIRPATH=${DIRPATH}$$i/" \ - sourcelist); done - -lint: - lint ${LINTFLAGS} -Itelnet ${INCLUDES} ${DEFINES} -DTN3270 \ - `make clist` -lcurses - -lintmset: - lint ${LINTFLAGS} ${INCLUDES} ${DEFINES} ${MSMAIN} \ - ascii/map3270.c -lcurses - -makefiles.pc: tools/mkmake - for i in . ${SUBDIR} ${EXTRADIR}; \ - do (sed -e "s/lib\.a/.lib/g" -e "s/^#PC_//" < $$i/makefile | \ - ./tools/mkmake | \ - sed -e "sx/x\\\\xg" -e "s/[ ]*_PC_//" > $$i/makefile.mak); \ - done - -tools/mkmake: - (cd tools; make mkmake) - -.DEFAULT: - sccs get $< - -depend: thisdepend - for i in ${SUBDIR}; do (cd $$i; make ${MFLAGS} depend); done - -thisdepend: - echo > eddep.c - grep '^#include' ${ALLC} eddep.c | grep -v '<' | \ - sed -e 's/:[^"]*"\([^"]*\)".*/: \1/' \ - -e 's/\.c/$$O/' | \ - awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \ - else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \ - else rec = rec " " $$2 } } \ - END { print rec } ' > makedep - echo '$$r makedep' >>eddep - echo '/^# DO NOT DELETE THIS LINE/+1,$$d' >eddep - echo '$$r makedep' >>eddep - echo 'w' >>eddep - -rm -f makefile.bak - cp makefile makefile.bak - ed - makefile < eddep - rm eddep makedep eddep.c - -# DO NOT DELETE THIS LINE - diff --git a/usr.bin/tn3270/map3270.0 b/usr.bin/tn3270/map3270.0 deleted file mode 100644 index 0e1c52f5e6..0000000000 --- a/usr.bin/tn3270/map3270.0 +++ /dev/null @@ -1,396 +0,0 @@ - - - -MAP3270(5) 1991 MAP3270(5) - - -NNAAMMEE - map3270 - database for mapping ascii keystrokes into IBM - 3270 keys - -SSYYNNOOPPSSIISS - mmaapp33227700 - -DDEESSCCRRIIPPTTIIOONN - When emulating IBM-syle 3270 terminals under UNIX (see - _t_n_3_2_7_0(1)), a mapping must be performed between sequences - of keys hit on a user's (ascii) keyboard, and the keys - that are available on a 3270. For example, a 3270 has a - key labeled EEEEOOFF which erases the contents of the current - field from the location of the cursor to the end. In - order to accomplish this function, the terminal user and a - program emulating a 3270 must agree on what keys will be - typed to invoke the EEEEOOFF function. - - The requirements for these sequences are: - - 1) that the first character of the sequence be outside of the - standard ascii printable characters; - - 2) that no sequence _b_e an initial part of another (although - sequences may _s_h_a_r_e initial parts). - - -FFOORRMMAATT - The file consists of entries for various keyboards. The - first part of an entry lists the names of the keyboards - which use that entry. These names will often be the same - as in /_e_t_c/_t_e_r_m_c_a_p (see _t_e_r_m_c_a_p(5)); however, note that - often the terminals from various termcap entries will all - use the same _m_a_p_3_2_7_0 entry; for example, both 925 and - 925vb (for 925 with visual bells) would probably use the - same _m_a_p_3_2_7_0 entry. Additionally, there are occasions - when the terminal type defines a window manager, and it - will then be necessary to specify a keyboard name (via the - KKEEYYBBDD environment variable) as the name of the entry. - After the names, separated by vertical bars (`|'), comes a - left brace (`{'); the definitions; and, finally, a right - brace (`}'). - - Each definition consists of a reserved keyword (see list - below) which identifies the 3270 function (extended as - defined below), followed by an equal sign (`='), followed - by the various ways to generate this particular function, - followed by a semi-colon (`;'). Each way is a sequence of - strings of _p_r_i_n_t_a_b_l_e ascii characters enclosed inside sin- - gle quotes (`''); various ways (alternatives) are sepa- - rated by vertical bars (`|'). - - Inside the single quotes, a few characters are special. A - caret (`^') specifies that the next character is the - - - -31, July 1 - - - - - -MAP3270(5) 1991 MAP3270(5) - - - ``control'' character of whatever the character is. So, - `^a' represents control-a, ie: hexadecimal 1 (note that - `^A' would generate the same code). To generate rruubboouutt - (DEL), one enters `^?'. To represent a control character - inside a file requires using the caret to represent a con- - trol sequence; simply typing control-A will not work. - Note: the ctrl-caret sequence (to generate a hexadecimal - 1E) is represented as `^^' (not `^\^'). - - In addition to the caret, a letter may be preceeded by a - backslash (`\'). Since this has little effect for most - characters, its use is usually not recommended. For the - case of a single quote (`''), the backslash prevents that - single quote from terminating the string. For the case of - a caret (`^'), the backslash prevents the caret from hav- - ing its special meaning. To have the backslash be part of - the string, it is necessary to place two backslashes - ('\\') in the file. - - In addition, the following characters are special: - - `\E' means an escape character; - `\n' means newline; - `\t' means tab; - `\r' means carriage return. - - It is not necessary for each character in a string to be - enclosed within single quotes. `\E\E\E' means three - escape characters. - - Comments, which may appear anywhere on a line, begin with - a hash mark (`#'), and terminate at the end of that line. - However, comments cannot begin inside a quoted string; a - hash mark inside a quoted string has no special meaning. - - -33227700 KKEEYYSS SSUUPPPPOORRTTEEDD - The following is the list of 3270 key names that are sup- - ported in this file. Note that some of the keys don't - really exist on a 3270. In particular, the developers of - this file have relied extensively on the work at the Yale - University Computer Center with their 3270 emulator which - runs in an IBM Series/1 front end. The following list - corresponds closely to the functions that the developers - of the Yale code offer in their product. - - IInn tthhee ffoolllloowwiinngg lliisstt,, tthhee ssttaarrrreedd ((""**"")) ffuunnccttiioonnss aarree nnoott - ssuuppppoorrtteedd bbyy _t_n_3_2_7_0(1). An unsupported function will - cause _t_n_3_2_7_0(_1) to send a (possibly visual) bell sequence - to the user's terminal. - - 3270 Key Name Functional description - - (*)LPRT local print - - - -31, July 2 - - - - - -MAP3270(5) 1991 MAP3270(5) - - - DP dup character - FM field mark character - CURSEL cursor select - CENTSIGN EBCDIC cent sign - RESHOW redisplay the screen - EINP erase input - EEOF erase end of field - DELETE delete character - INSRT toggle insert mode - TAB field tab - BTAB field back tab - COLTAB column tab - COLBAK column back tab - INDENT indent one tab stop - UNDENT undent one tab stop - NL new line - HOME home the cursor - UP up cursor - DOWN down cursor - RIGHT right cursor - LEFT left cursor - SETTAB set a column tab - DELTAB delete a columntab - SETMRG set left margin - SETHOM set home position - CLRTAB clear all column tabs - (*)APLON apl on - (*)APLOFF apl off - (*)APLEND treat input as ascii - (*)PCON xon/xoff on - (*)PCOFF xon/xoff off - DISC disconnect (suspend) - (*)INIT new terminal type - (*)ALTK alternate keyboard dvorak - FLINP flush input - ERASE erase last character - WERASE erase last word - FERASE erase field - SYNCH we are in synch with the user - RESET reset key-unlock keyboard - MASTER_RESET reset, unlock and redisplay - (*)XOFF please hold output - (*)XON please give me output - ESCAPE enter telnet command mode - WORDTAB tab to beginning of next word - WORDBACKTAB tab to beginning of current/last word - WORDEND tab to end of current/next word - FIELDEND tab to last non-blank of current/next - unprotected (writable) field. - - PA1 program attention 1 - PA2 program attention 2 - PA3 program attention 3 - - - - -31, July 3 - - - - - -MAP3270(5) 1991 MAP3270(5) - - - CLEAR local clear of the 3270 screen - TREQ test request - ENTER enter key - - PFK1 program function key 1 - PFK2 program function key 2 - etc. etc. - PFK36 program function key 36 - -AA SSAAMMPPLLEE EENNTTRRYY - The following entry is used by - tn3270(1) when unable to locate a reasonable version in the - user's environment and in /etc/map3270: - - name { # actual name comes from TERM variable - clear = '^z'; - flinp = '^x'; - enter = '^m'; - delete = '^d' | '^?'; # note that '^?' is delete (rubout) - synch = '^r'; - reshow = '^v'; - eeof = '^e'; - tab = '^i'; - btab = '^b'; - nl = '^n'; - left = '^h'; - right = '^l'; - up = '^k'; - down = '^j'; - einp = '^w'; - reset = '^t'; - xoff = '^s'; - xon = '^q'; - escape = '^c'; - ferase = '^u'; - insrt = ' '; - # program attention keys - pa1 = '^p1'; pa2 = '^p2'; pa3 = '^p3'; - # program function keys - pfk1 = '\E1'; pfk2 = '\E2'; pfk3 = '\E3'; pfk4 = '\E4'; - pfk5 = '\E5'; pfk6 = '\E6'; pfk7 = '\E7'; pfk8 = '\E8'; - pfk9 = '\E9'; pfk10 = '\E0'; pfk11 = '\E-'; pfk12 = '\E='; - pfk13 = '\E!'; pfk14 = '\E@'; pfk15 = '\E#'; pfk16 = '\E$'; - pfk17 = '\E%'; pfk18 = '\E'; pfk19 = '\E&'; pfk20 = '\E*'; - pfk21 = '\E('; pfk22 = '\E)'; pfk23 = '\E_'; pfk24 = '\E+'; - } - -IIBBMM 33227700 KKEEYY DDEEFFIINNIITTOONNSS FFOORR AANN AABBOOVVEE DDEEFFIINNIITTIIOONN - The charts below show the proper keys to emulate each 3270 - function when using the default key mapping supplied with - _t_n_3_2_7_0(1) and _m_s_e_t(1). - - Command Keys IBM 3270 Key Default Key(s) - Enter RETURN - - - -31, July 4 - - - - - -MAP3270(5) 1991 MAP3270(5) - - - Clear control-z - Cursor Movement Keys - New Line control-n or - Home - Tab control-i - Back Tab control-b - Cursor Left control-h - Cursor Right control-l - Cursor Up control-k - Cursor Down control-j or - LINE FEED - Edit Control Keys - Delete Char control-d or - RUB - Erase EOF control-e - Erase Input control-w - Insert Mode ESC Space - End Insert ESC Space - Program Function Keys - PF1 ESC 1 - PF2 ESC 2 - ... ... - PF10 ESC 0 - PF11 ESC - - PF12 ESC = - PF13 ESC ! - PF14 ESC @ - ... ... - PF24 ESC + - Program Attention Keys - PA1 control-p 1 - PA2 control-p 2 - PA3 control-p 3 - Local Control Keys - Reset After Error control-r - Purge Input Buffer control-x - Keyboard Unlock control-t - Redisplay Screen control-v - Other Keys - Erase current field control-u - -FFIILLEESS - /etc/map3270 - -SSEEEE AALLSSOO - tn3270(1), mset(1), _Y_a_l_e _A_S_C_I_I _T_e_r_m_i_n_a_l _C_o_m_m_u_n_i_c_a_t_i_o_n _S_y_s- - _t_e_m _I_I _P_r_o_g_r_a_m _D_e_s_c_r_i_p_t_i_o_n/_O_p_e_r_a_t_o_r'_s _M_a_n_u_a_l (IBM - SB30-1911) - -AAUUTTHHOORR - Greg Minshall - -BBUUGGSS - _T_n_3_2_7_0 doesn't yet understand how to process all the - - - -31, July 5 - - - - - -MAP3270(5) 1991 MAP3270(5) - - - functions available in _m_a_p_3_2_7_0; when such a function is - requested _t_n_3_2_7_0 will beep at you. - - The definition of "word" (for "word erase", "word tab") - should be a run-time option. Currently it is defined as - the kernel tty driver defines it (strings of non- - whitespace); more than one person would rather use the - "vi" definition (strings of specials, strings of alphanu- - meric). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -31, July 6 - - diff --git a/usr.bin/tn3270/mset.0 b/usr.bin/tn3270/mset.0 deleted file mode 100644 index 42a55b2230..0000000000 --- a/usr.bin/tn3270/mset.0 +++ /dev/null @@ -1,132 +0,0 @@ -MSET(1) 386BSD Reference Manual MSET(1) - -NNAAMMEE - mmsseett - retrieve ASCII to IBM 3270 keyboard map - -SSYYNNOOPPSSIISS - mmsseett [--ppiicckkyy] [--sshheellll] [_k_e_y_b_o_a_r_d_n_a_m_e] - -DDEESSCCRRIIPPTTIIOONN - MMsseett retrieves mapping information for the ASCII keyboard to IBM 3270 - terminal special functions. Normally, these mappings are found in - /_u_s_r/_s_h_a_r_e/_m_i_s_c/_m_a_p_3_2_7_0 (see map3270(5)). This information is used by - the tn3270 command (see tn3270(1)). - - The default mmsseett output can be used to store the mapping information in - the process environment in order to avoid scanning mmaapp33227700 each time - ttnn33227700 is invoked. To do this, place the following command in your - ._l_o_g_i_n file: - - set noglob; setenv MAP3270 "`mset`"; unset noglob - - If the _k_e_y_b_o_a_r_d_n_a_m_e argument is not supplied, mmsseett attempts to determine - the name of the keyboard the user is using, by checking the KEYBD envi- - ronment variable. If the KEYBD environment variable is not set, then - mmsseett uses the user's terminal type from the environment variable TERM as - the keyboard name. Normally, mmsseett then uses the file map3270(5) to find - the keyboard mapping for that terminal. However, if the environment - variable MAP3270 exists and contains the entry for the specified key- - board, then that definition is used. If the value of MAP3270 begins with - a slash (`/') then it is assumed to be the full pathname of an alternate - mapping file and that file is searched first. In any case, if the map- - ping for the keyboard is not found in the environment, nor in an alter- - nate map file, nor in the standard map file, then the same search is per- - formed for an entry for a keyboard with the name _u_n_k_n_o_w_n. If that search - also fails, then a default mapping is used. - - The arguments to mmsseett are: - - --ppiicckkyy When processing the various _m_a_p_3_2_7_0 entries (for the user's - keyboard, and all those encountered before the one for the - user's keyboard), mmsseett normally will not complain about en- - tries for unknown functions (like ``PFX1''; the --ppiicckkyy argu- - ment causes mmsseett to issue warning messages about these un- - known entries. - - --sshheellll If the _m_a_p_3_2_7_0 entry is longer than the shell's 1024 environ- - mental variable length limit, the default mmsseett output cannot - be used to store the mapping information in the process envi- - ronment to avoid scanning _m_a_p_3_2_7_0 each time ttnn33227700 is in- - voked. The --sshheellll argument causes mmsseett to generate shell - commands to set the environmental variables MAP3270, - MAP3270A, and so on, breaking up the entry to fit within the - shell environmental variable length limit. To set these - variables, place the following command in your ._l_o_g_i_n file: - - mset -shell > tmp ; source tmp ; /bin/rm tmp - - _k_e_y_b_o_a_r_d_n_a_m_e - When searching for the _m_a_p_3_2_7_0 entry that matches the user's - keyboard, mmsseett will use _k_e_y_b_o_a_r_d_n_a_m_e instead of determining - the keyboard name from the KEYBD or TERM environmental vari- - ables. - -FFIILLEESS - - /usr/share/misc/map3270 keyboard mapping for known keyboards - -SSEEEE AALLSSOO - tn3270(1), map3270(5) - -HHIISSTTOORRYY - The mmsseett command appeared in 4.3BSD. - -4.3 Berkeley Distribution July 27, 1991 3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/usr.bin/tn3270/mset/Makefile b/usr.bin/tn3270/mset/Makefile new file mode 100644 index 0000000000..61cf62693c --- /dev/null +++ b/usr.bin/tn3270/mset/Makefile @@ -0,0 +1,47 @@ +# $Header: /a/cvs/386BSD/src/usr.bin/tn3270/mset/Makefile,v 1.2 1993/07/07 20:30:18 nate Exp $ + +.include <../../Makefile.inc> + +CFLAGS += -I${.CURDIR} -I. + +MAN1 = mset.1 +MAN5 = map3270.5 + +SRCS += astosc.c map3270.c mset.c + +# this and the dependency hacks below to make 'depend' target +# work right... + +DEPSRCS += astosc.OUT map3270.c mset.c + +PROG = mset + +astosc.o: astosc.OUT +CLEANFILES += astosc.OUT astosc.out +.if exists(${.CURDIR}/../tools/mkastosc/obj) +OBJ_DIR=obj +.else +OBJ_DIR=. +.endif + +astosc.OUT: ${.CURDIR}/../ctlr/hostctlr.h ${.CURDIR}/../ctlr/function.h \ + ${.CURDIR}/../ctlr/${KBD} ${.CURDIR}/../tools/mkastosc/${OBJ_DIR}/mkastosc + ${.CURDIR}/../tools/mkastosc/${OBJ_DIR}/mkastosc \ + ${.CURDIR}/../ctlr/hostctlr.h ${.CURDIR}/../ctlr/function.h \ + < ${.CURDIR}/../ctlr/${KBD} > ${.TARGET} + /bin/rm -f astosc.out + ln -s astosc.OUT astosc.out + +# astosc.out + +${.CURDIR}/../tools/mkastosc/${OBJ_DIR}/mkastosc: + cd ${.CURDIR}/../tools/mkastosc; make + + +depend: .depend +.depend: ${DEPSRCS} + mkdep ${MKDEP} ${CFLAGS:M-[ID]*} ${.ALLSRC:M*.c} + +.include <bsd.prog.mk> + +.PATH: ${.CURDIR}/../api ${.CURDIR}/../ascii diff --git a/usr.bin/tn3270/map3270.5 b/usr.bin/tn3270/mset/map3270.5 similarity index 100% rename from usr.bin/tn3270/map3270.5 rename to usr.bin/tn3270/mset/map3270.5 diff --git a/usr.bin/tn3270/mset.1 b/usr.bin/tn3270/mset/mset.1 similarity index 100% rename from usr.bin/tn3270/mset.1 rename to usr.bin/tn3270/mset/mset.1 diff --git a/usr.bin/tn3270/sys_curses/makefile b/usr.bin/tn3270/sys_curses/makefile deleted file mode 100644 index 9b027a960f..0000000000 --- a/usr.bin/tn3270/sys_curses/makefile +++ /dev/null @@ -1,133 +0,0 @@ -# @(#)makefile 4.3 (Berkeley) 4/26/91 - -# msdos versus unix defines -O = .o -#PC_O = .obj - -X = -#PC_X = .exe - -L = -#PC_L = -link - -CC = cc -#PC_CC = cl - -MV = mv -#PC_MV = rename - -RM = rm -f -#PC_RM= erase - -LINT_ARGS = -#PC_LINT_ARGS = -DLINT_ARGS - -DEBUG_FLAGS = -g -#PC_DEBUG_FLAGS = -Zi -Od - -AR = ar -AR1 = cr -AR2 = -AR3 = -#PC_AR = lib -#PC_AR1 = -#PC_AR2 = + -#PC_AR3 = ";" - -RANLIB = ranlib -#PC_RANLIB = echo "Done with " - -PRINT = print - -DEFINES = ${LINT_ARGS} - -INCLUDES = -I. - -OPTIMIZE = -O -OPTIMIZE = ${DEBUG_FLAGS} - -CFLAGS = $(OPTIMIZE) $(INCLUDES) $(DEFINES) - -# Lint flags -LINTFLAGS = -hbxaz - -ALLH = terminal.h - -ALLC = system.c termout.c - -ALLO = system$O termout$O - -ALLHC= ${ALLH} ${ALLC} -ALLPRINT = ${ALLHC} - -ALLSOURCE = ${ALLPRINT} makefile - -#.c.obj: -# ${CC} ${CFLAGS} -c $< - -syslib.a: $(ALLO) - ${RM} $@ - for i in ${ALLO}; do (${AR} ${AR1} $@ ${AR2} $$i${AR3}); done - ${RANLIB} $@ - -clean: - for i in $(ALLO) errs makefile.bak syslib.a; \ - do (${RM} $$i); done - -sccsclean: - -sccs clean - -sccs get makefile - -clist: ${ALLC} - @for i in ${ALLC} ; \ - do (echo ${DIRPATH}$$i); done - -hclist: ${ALLHC} - @for i in ${ALLHC} ; \ - do (echo ${DIRPATH}$$i); done - -sourcelist: ${ALLSOURCE} - @for i in ${ALLSOURCE}; \ - do (echo ${DIRPATH}$$i); done - -print: - ${PRINT} ${ALLPRINT} - -tags: ${ALLC} ${ALLH} - ctags -t ${ALLC} ${ALLH} - -action: - ${ACTION} - -lint: - lint ${LINTFLAGS} ${INCLUDES} ${DEFINES} -DTN3270 \ - ${TNMAIN} ${MOSTC} -lcurses - lint ${LINTFLAGS} ${INCLUDES} ${DEFINES} ${MSMAIN} map3270.c -lcurses - -.DEFAULT: - sccs get $< - -depend: - grep '^#include' ${ALLC} ${ALLH} | grep -v '<' | \ - sed -e 's/:[^"]*"\([^"]*\)".*/: \1/' \ - -e 's/\.c/$$O/' | \ - awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \ - else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \ - else rec = rec " " $$2 } } \ - END { print rec } ' > makedep - echo '$$r makedep' >>eddep - echo '/^# DO NOT DELETE THIS LINE/+1,$$d' >eddep - echo '$$r makedep' >>eddep - echo 'w' >>eddep - -rm -f makefile.bak - cp makefile makefile.bak - ed - makefile < eddep - rm eddep makedep - -# DO NOT DELETE THIS LINE - -system$O: ../general/general.h ../ctlr/api.h ../api/api_exch.h -system$O: ../general/globals.h -termout$O: ../general/general.h terminal.h ../api/disp_asc.h ../ctlr/hostctlr.h -termout$O: ../ctlr/externs.h ../ctlr/declare.h ../ctlr/oia.h ../ctlr/screen.h -termout$O: ../general/globals.h ../telextrn.h diff --git a/usr.bin/tn3270/telnet/Makefile b/usr.bin/tn3270/telnet/Makefile deleted file mode 100644 index f64f473305..0000000000 --- a/usr.bin/tn3270/telnet/Makefile +++ /dev/null @@ -1,87 +0,0 @@ -# @(#)Makefile 4.5 (Berkeley) 4/26/91 - -# The following is the telnet makefile for tn3270, using the shared telnet -# sources. - -# -# TERMCAP Define this if your system is termcap based, -# otherwise a terminfo based system is assumed. -# -# SRCRT Includes code to allow you to specify source routes. -# Format is: -# [!]@hop1@hop2...[@|:]dst -# Leading ! means strict source route. -# -# NOSTRNCASECMP Define this if you do not have strncasecmp() in -# your C libarary. -# -# USE_TERMIO Define this if you have System V termio structures. -# What is here is how things are on Cray computers. -# -# KLUDGELINEMODE Define this to get the kludged up version of linemode -# that was in 4.3BSD. This is a good thing to have -# around for talking to older systems. -# - -DEFINES= -DTERMCAP -DSRCRT -DKLUDGELINEMODE -DUSE_TERMIO - - -VPATH = ${.CURDIR}/../../telnet -XINCLUDES= -I${.CURDIR}/../../telnet -I${.CURDIR} -INCLUDES= -XDEFINES = -DTN3270 -OPTIMIZE= -O -CFLAGS = ${OPTIMIZE} ${INCLUDES} ${DEFINES} -XCFLAGS= ${XINCLUDES} ${XDEFINES} -LD = ld -LDFLAGS = -r -PRINT = print -ACTION = sccs tell -LIBC= /usr/lib/libc.a -ALLH= defines.h externs.h fdset.h general.h ring.h types.h -SRCS= commands.c main.c network.c ring.c \ - sys_bsd.c telnet.c terminal.c \ - tn3270.c utilities.c -ALLHC= ${ALLH} ${SRCS} -ALLPRINT = ${ALLHC} -ALLSOURCE= ${ALLHC} Makefile Makefile_ultrix -OBJS= commands.o main.o network.o ring.o sys_bsd.o \ - telnet.o terminal.o tn3270.o utilities.o - -.c.o: - ${CC} -c ${CFLAGS} ${XCFLAGS} $< - -telprog.o: ${OBJS} ${LIBC} - ${LD} ${LDFLAGS} -o $@ ${OBJS} - -clean: FRC - rm -f telprog.o ${OBJS} core telnet - -depend: FRC ${SRCS} - mkdep ${CFLAGS} ${SRCS} - -lint: FRC ${SRCS} - lint ${CFLAGS} ${SRCS} - -tags: FRC ${ALLHC} - ctags ${ALLHC} - -print: FRC ${ALLPRINT} - ${PRINT} ${ALLPRINT} - -action: FRC - ${ACTION} - -clist: FRC ${SRCS} - @for i in ${SRCS} ; \ - do (echo ${DIRPATH}$$i); done - -hclist: FRC ${ALLHC} - @for i in ${ALLHC} ; \ - do (echo ${DIRPATH}$$i); done - -sourcelist: FRC ${ALLSOURCE} - @for i in ${ALLSOURCE} ../../telnet/Makefile ; \ - do (echo ${DIRPATH}$$i); done - -FRC: diff --git a/usr.bin/tn3270/tn3270.0 b/usr.bin/tn3270/tn3270.0 deleted file mode 100644 index 9e4fa1943d..0000000000 --- a/usr.bin/tn3270/tn3270.0 +++ /dev/null @@ -1,198 +0,0 @@ -TN3270(1) 386BSD Reference Manual TN3270(1) - -NNAAMMEE - ttnn33227700 - full-screen remote login to IBM VM/CMS - -SSYYNNOOPPSSIISS - ttnn33227700 [--dd] [--nn _f_i_l_e_n_a_m_e] [--tt _c_o_m_m_a_n_d_n_a_m_e] [_s_y_s_n_a_m_e [port]] - -DDEESSCCRRIIPPTTIIOONN - TTnn33227700 permits a full-screen, full-duplex connection from a UNIX machine - to an IBM (or compatible) machine. TTnn33227700 gives the appearance of being - logged in to the remote machine from an IBM 3270 terminal. Of course, - you must have an account on the machine to which you connect in order to - log in. TTnn33227700 looks to the user in many respects like the Yale ASCII - Terminal Communication System II. TTnn33227700 is actually a modification of - the Arpanet TELNET user interface (see telnet(1)) which will, in certain - circumstances, interpret and generate raw 3270 control streams. - - The flags to ttnn33227700 are as follows: - - --dd Turn on socket-level tracing (for super-user only) --nn_f_i_l_e_n_a_m_e - Specify a file to receive network trace data output (from - commands "toggle netdata" and "toggle options", see - telnet(1)); the default is for output to be directed to the - standard error file. --tt_c_o_m_m_a_n_d_n_a_m_e Specify a UNIX command to - process IBM 4994 style transparent mode data received from - the remote IBM machine. - - _s_y_s_n_a_m_e The name of the remote system. If the remote name is NOT - specified, the user will be prompted for a command (see be- - low). - - _p_o_r_t The port to connect to on the remote system. Normally, - ttnn33227700 attempts to connect to the standard TELNET port (port - 23) on the remote machine. - - When ttnn33227700 first connects to the remote system, it will negotiate to go - into 3270 mode. Part of this negotiation involves telling the remote - system what model 3270 it is emulating. In all cases, ttnn33227700 emulates a - 3278 terminal. To decide which specific model, ttnn33227700 looks at the num- - ber of lines and columns on the actual terminal (as defined in the TERM - environment variable; see termcap(5)). The terminal (or window in which - ttnn33227700 is running, on multiple window systems) must have at least 80 - columns and 24 lines, or ttnn33227700 will not go into emulation mode. If the - terminal does have at least 80 columns and at least 24 lines, the follow- - ing table describes the emulation: - - minimum_size emulated - (rows*columns) terminal - -------------- ------------ - 27*132 3278 model 5 - 43*80 3278 model 4 - 32*80 3278 model 3 - 24*80 3278 model 2. - - Emulation of the 3270 terminal is done in the UNIX process. This emula- - tion involves mapping 3270-style commands from the host into appropriate - sequences to control the user's terminal screen. TTnn33227700 uses curses(3) - and the /_u_s_r/_s_h_a_r_e/_m_i_s_c/_t_e_r_m_c_a_p file to do this. The emulation also in- - volves simulating the special 3270 keyboard keys (program function keys, - etc.) by mapping sequences of keystrokes from the ASCII keyboard into - appropriate 3270 control strings. This mapping is terminal dependent and - is specified in a description file, /_u_s_r/_s_h_a_r_e/_m_i_s_c/_m_a_p_3_2_7_0, (see - map3270(5)) or in an environment variable MAP3270 (and, if necessary, - MAP3270A, MAP3270B, and so on - see mset(1)). Any special function keys - on the ASCII keyboard are used whenever possible. If an entry for the - user's terminal is not found, ttnn33227700 looks for an entry for the terminal - type _u_n_k_n_o_w_n. If this is not found, ttnn33227700 uses a default keyboard map- - ping (see map3270(5)). - - The first character of each special keyboard mapping sequence is either - an ASCII escape (ESC), a control character, or an ASCII delete (DEL). If - the user types an unrecognized function key sequence, ttnn33227700 sends an - ASCII bell (BEL), or a visual bell if defined in the user's termcap en- - try, to the user's terminal and nothing is sent to the IBM host. - - If ttnn33227700 is invoked without specifying a remote host system name, it en- - ters local command mode, indicated by the prompt ``tn3270> ''. In this - mode, ttnn33227700 accepts and executes all the commands of telnet(1), plus - one additional command: - - ttrraannssccoomm Specify UNIX command for IBM 4994 style transparent mode - processing. - - TTnn33227700 command mode may also be entered, after connecting to a host, by - typing a special escape sequence. If ttnn33227700 has succeeded in negotiating - 3270 mode with the remote host, the escape sequence will be as defined by - the map3270 (see map3270(5)) entry for the user's terminal type (typi- - cally control-C); otherwise the escape sequence will initially be set to - the single character `^]' (control right square bracket). - - While in command mode, any host login session is still alive but tem- - porarily suspended. The host login session may be resumed by entering an - empty line (press the RETURN key) in response to the command prompt. A - session may be terminated by logging off the foreign host, or by typing - ``quit'' or ``close'' while in local command mode. - -FFIILLEESS - /usr/share/misc/termcap - /usr/share/misc/map3270 - -NNOOTTEESS - The IBM 4994 style transparent mode command is invoked when ttnn33227700 re- - ceives IBM 4994 style transparent output from the remote host. Output - and input pipes are created for communication between the two processes. - The pipes are closed when a 3270 clear command is received from the re- - mote hosts, signalling the end of transparent mode output. Transparent - mode is necessary for sending ASCII control characters over the 3270 ter- - minal connection; ASCII graphics terminal support is accomplished this - way. Developers of ttrraannssccoomm commands should note that the ttrraannssccoomm stdin - pipe end will be in CBREAK mode, with ECHO and CRMOD turned off. - -EENNVVIIRROONNMMEENNTT - TTnn33227700 checks the following environment variables: TERM, MAP3270, - MAP3270[A...]. Information on these can be found in mset(1). TTnn33227700 also - checks SHELL, KEYBD and API3270. - -SSEEEE AALLSSOO - mset(1), telnet(1), curses(3), termcap(3), termcap(5), map3270(5), - - "Yale ASCII Terminal Communication", _S_y_s_t_e_m _I_I _P_r_o_g_r_a_m - _D_e_s_c_r_i_p_t_i_o_n/_O_p_e_r_a_t_o_r'_s _M_a_n_u_a_l, IBM SB30-1911. - -HHIISSTTOORRYY - The ttnn33227700 command appeared in 4.3BSD. - -BBUUGGSS - Tn3270 is slow and uses system resources prodigiously. - - Not all 3270 functions are supported, nor all Yale enhancements. - - - Error conditions (attempting to enter data in a protected field, for ex- - ample) should cause a message to be sent to the user's terminal instead - of just ringing a bell. - -4.3 Berkeley Distribution July 27, 1991 4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/usr.bin/tn3270/tn3270/Makefile b/usr.bin/tn3270/tn3270/Makefile new file mode 100644 index 0000000000..009fd298f5 --- /dev/null +++ b/usr.bin/tn3270/tn3270/Makefile @@ -0,0 +1,115 @@ +# This mess is to make tn3270 work with Berkeley Make +# We also need to check for obj/no_obj directories + +.if exists(${.CURDIR}/../tools/mkastosc/obj) +SC_OBJ_DIR=obj +.else +SC_OBJ_DIR=. +.endif + +.if exists(${.CURDIR}/../tools/mkastods/obj) +DS_OBJ_DIR=obj +.else +DS_OBJ_DIR=. +.endif + +.if exists(${.CURDIR}/../tools/mkdstoas/obj) +AS_OBJ_DIR=obj +.else +AS_OBJ_DIR=. +.endif + +.if exists(${.CURDIR}/../tools/mkhits/obj) +HITS_OBJ_DIR=obj +.else +HITS_OBJ_DIR=. +.endif + +.include <../../Makefile.inc> + +CFLAGS += -I${.CURDIR} -I. +LDADD += -lcurses -ltermcap -ltelnet +DPADD += ${LIBCURSES} /usr/lib/libtermcap.a /usr/lib/libtelnet.a + +MAN1 = tn3270.1 + +SRCS += apilib.c api_bsd.c api_exch.c asc_ebc.c astosc.c dctype.c +SRCS += disp_asc.c ebc_disp.c +SRCS += map3270.c termin.c +SRCS += api.c function.c inbound.c oia.c options.c outbound.c +SRCS += genbsubs.c globals.c system.c termout.c +SRCS += commands.c main.c network.c ring.c sys_bsd.c telnet.c terminal.c +SRCS += tn3270.c utilities.c + +# this and the dependency hacks below to make 'depend' target +# work right... + +DEPSRCS += astosc.OUT asc_disp.OUT disp_asc.OUT kbd.OUT +DEPSRCS += apilib.c api_bsd.c api_exch.c asc_ebc.c dctype.c +DEPSRCS += ebc_disp.c +DEPSRCS += map3270.c termin.c +DEPSRCS += api.c function.c inbound.c oia.c options.c outbound.c +DEPSRCS += genbsubs.c globals.c system.c termout.c +DEPSRCS += commands.c main.c network.c ring.c sys_bsd.c telnet.c terminal.c +DEPSRCS += tn3270.c utilities.c + +PROG = tn3270 + +astosc.o: astosc.OUT +CLEANFILES += astosc.OUT astosc.out + +astosc.OUT: ${.CURDIR}/../ctlr/hostctlr.h ${.CURDIR}/../ctlr/function.h \ + ${.CURDIR}/../ctlr/${KBD} ${.CURDIR}/../tools/mkastosc/${SC_OBJ_DIR}/mkastosc + ${.CURDIR}/../tools/mkastosc/${SC_OBJ_DIR}/mkastosc ${.CURDIR}/../ctlr/hostctlr.h \ + ${.CURDIR}/../ctlr/function.h < ${.CURDIR}/../ctlr/${KBD} \ + > ${.TARGET} + /bin/rm -f astosc.out + ln -s astosc.OUT astosc.out + +disp_asc.o: asc_disp.OUT disp_asc.OUT +CLEANFILES += asc_disp.OUT asc_disp.out disp_asc.OUT disp_asc.out +asc_disp.OUT: ${.CURDIR}/../tools/mkastods/${DS_OBJ_DIR}/mkastods + ${.CURDIR}/../tools/mkastods/${DS_OBJ_DIR}/mkastods > ${.TARGET} + /bin/rm -f asc_disp.out + ln -s asc_disp.OUT asc_disp.out + +disp_asc.OUT: ${.CURDIR}/../tools/mkdstoas/${AS_OBJ_DIR}/mkdstoas + ${.CURDIR}/../tools/mkdstoas/${AS_OBJ_DIR}/mkdstoas > ${.TARGET} + /bin/rm -f disp_asc.out + ln -s disp_asc.OUT disp_asc.out + +inbound.o: kbd.OUT +CLEANFILES += kbd.OUT kbd.out TMPfunc.out +kbd.OUT: ${.CURDIR}/../ctlr/hostctlr.h ${.CURDIR}/../ctlr/${KBD} \ + ${.CURDIR}/../tools/mkhits/${HITS_OBJ_DIR}/mkhits + ${CC} ${CFLAGS} -E ${.CURDIR}/../ctlr/function.c > TMPfunc.out + ${.CURDIR}/../tools/mkhits/${HITS_OBJ_DIR}/mkhits ${.CURDIR}/../ctlr/hostctlr.h \ + TMPfunc.out < ${.CURDIR}/../ctlr/${KBD} > ${.TARGET} + /bin/rm -f kbd.out + ln -s kbd.OUT kbd.out + +# astosc.out +# asc_disp.out disp_asc.out +# default.map +# kbd.out + +${.CURDIR}/../tools/mkastosc/${SC_OBJ_DIR}/mkastosc: + cd ${.CURDIR}/../tools/mkastosc; make +${.CURDIR}/../tools/mkastods/${DS_OBJ_DIR}/mkastods: + cd ${.CURDIR}/../tools/mkastods; make +${.CURDIR}/../tools/mkdstoas/${AS_OBJ_DIR}/mkdstoas: + cd ${.CURDIR}/../tools/mkdstoas; make +${.CURDIR}/../tools/mkhits/${HITS_OBJ_DIR}/mkhits: + cd ${.CURDIR}/../tools/mkhits; make + + +depend: .depend +.depend: ${DEPSRCS} + mkdep ${MKDEP} ${CFLAGS:M-[ID]*} ${.ALLSRC:M*.c} + +.include <bsd.prog.mk> + +.PATH: ${.CURDIR}/../api ${.CURDIR}/../ascii ${.CURDIR}/../ctlr +.PATH: ${.CURDIR}/../general ${.CURDIR}/../sys_curses ${.CURDIR}/../../telnet + + diff --git a/usr.bin/tn3270/tn3270.1 b/usr.bin/tn3270/tn3270/tn3270.1 similarity index 100% rename from usr.bin/tn3270/tn3270.1 rename to usr.bin/tn3270/tn3270/tn3270.1 diff --git a/usr.bin/tn3270/tools/Makefile b/usr.bin/tn3270/tools/Makefile new file mode 100644 index 0000000000..0bb971a5f5 --- /dev/null +++ b/usr.bin/tn3270/tools/Makefile @@ -0,0 +1,5 @@ +# $Header: /b/source/CVS/src/usr.bin/tn3270/tools/Makefile,v 1.2 1993/04/27 06:47:40 cgd Exp $ + +SUBDIR = mkhits mkastosc mkastods mkdstoas mkdctype + +.include <bsd.subdir.mk> diff --git a/usr.bin/tn3270/tools/makefile b/usr.bin/tn3270/tools/makefile deleted file mode 100644 index c793c476a9..0000000000 --- a/usr.bin/tn3270/tools/makefile +++ /dev/null @@ -1,183 +0,0 @@ -# @(#)makefile 4.3 (Berkeley) 4/26/91 - -# msdos versus unix defines -O = .o -#PC_O = .obj - -X = -#PC_X = .exe - -L = -#PC_L = -link - -CC = cc -#PC_CC = cl - -MV = mv -#PC_MV = rename - -RM = rm -f -#PC_RM= erase - -LINT_ARGS = -#PC_LINT_ARGS = -DLINT_ARGS - -DEBUG_FLAGS = -g -#PC_DEBUG_FLAGS = -Zi -Od - -AR = ar -AR1 = cr -AR2 = -AR3 = -#PC_AR = lib -#PC_AR1 = -#PC_AR2 = + -#PC_AR3 = ";" - -RANLIB = ranlib -#PC_RANLIB = echo "Done with " - -PRINT = print - -DEFINES = ${LINT_ARGS} - -INCLUDES = -I. - -OPTIMIZE = -O -OPTIMIZE = ${DEBUG_FLAGS} - -CFLAGS = $(OPTIMIZE) $(INCLUDES) $(DEFINES) - -# Lint flags -LINTFLAGS = -hbxaz - -ALLH = dohits.h ectype.h - -ALLY = mkmake.y - -ALLC = dohits.c ectype.c mkastods.c mkastosc.c mkdctype.c \ - mkdstoas.c mkhits.c prt3270.c - -ALLO = prt3270$O mkdctype$O ectype$O \ - mkastods$O mkdstoas$O mkhits$O mkmake$O dohits$O mkastosc$O - -ALLPRINT = ${ALLH} ${ALLC} ${ALLY} - -ALLSOURCE = ${ALLPRINT} makefile makefile.mak - -MKMAKE = mkmake$X -#PC_MKMAKE = - -ALLEXE = mkastods$X mkastosc$X mkdctype$X mkdstoas$X \ - mkhits$X ${MKMAKE} prt3270$X - -#.c.obj: -# ${CC} ${CFLAGS} -c $< - -tools: - @echo Need to specify WHICH tool ... - -all: ${ALLEXE} - @echo done. - -prt3270$X: prt3270$O ../general/globals$O ../api/asc_ebc$O \ - ../api/astosc$O ../ctlr/kbd.out - ${CC} ${CFLAGS} -o prt3270 prt3270$O ../general/globals$O \ - ../api/asc_ebc$O ../api/astosc$O - -mkastosc$X: mkastosc$O dohits$O ../api/asc_ebc$O ../api/ebc_disp$O - $(CC) $(CFLAGS) -o mkastosc mkastosc$O dohits$O \ - ../api/asc_ebc$O ../api/ebc_disp$O - -mkastods$X: mkastods$O ../api/asc_ebc$O ../api/ebc_disp$O - $(CC) $(CFLAGS) -o mkastods mkastods$O ../api/asc_ebc$O \ - ../api/ebc_disp$O - -mkdstoas$X: mkdstoas$O ../api/asc_ebc$O ../api/ebc_disp$O - $(CC) $(CFLAGS) -o mkdstoas mkdstoas$O ../api/asc_ebc$O \ - ../api/ebc_disp$O - -mkhits$X: mkhits$O dohits$O ../api/asc_ebc$O ../api/ebc_disp$O - $(CC) $(CFLAGS) -o mkhits mkhits$O dohits$O ../api/asc_ebc$O \ - ../api/ebc_disp$O - -mkmake: mkmake$O - ${CC} ${CFLAGS} -o mkmake mkmake$O - -mkdctype$X: mkdctype$O ../api/ebc_disp$O ectype$O - $(CC) $(CFLAGS) -o mkdctype mkdctype$O ../api/ebc_disp$O ectype$O - -../api/astosc$O: - (cd ${.CURDIR}/../api; make astosc$O) - -../api/asc_ebc$O: - (cd ${.CURDIR}/../api; make asc_ebc$O) - -../ctlr/kbd.out: - (cd ${.CURDIR}/../ctlr; make kbd.out) - -../api/ebc_disp$O: - (cd ${.CURDIR}/../api; make ebc_disp$O) - -../general/globals$O: - (cd ${.CURDIR}/../general; make globals$O) - -sourcelist: ${ALLSOURCE} - @for i in ${ALLSOURCE}; \ - do (echo ${DIRPATH}$$i); done - -print: - ${PRINT} ${ALLPRINT} - -tags: ${ALLC} ${ALLH} - ctags -t ${ALLC} ${ALLH} - -action: - ${ACTION} - -clean: - for i in errs makefile.bak y.tab.c y.output ${ALLO} ${ALLEXE}; \ - do (${RM} $$i); done - -sccsclean: - -sccs clean - -sccs get makefile - -lint: - lint ${LINTFLAGS} ${INCLUDES} ${DEFINES} -DTN3270 \ - ${TNMAIN} ${MOSTC} -lcurses - lint ${LINTFLAGS} ${INCLUDES} ${DEFINES} ${MSMAIN} map3270.c -lcurses - -.DEFAULT: - sccs get $< - -depend: - grep '^#include' ${ALLC} | grep -v '<' | \ - sed -e 's/:[^"]*"\([^"]*\)".*/: \1/' \ - -e 's/\.c/$$O/' | \ - awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \ - else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \ - else rec = rec " " $$2 } } \ - END { print rec } ' > makedep - echo '$$r makedep' >>eddep - echo '/^# DO NOT DELETE THIS LINE/+1,$$d' >eddep - echo '$$r makedep' >>eddep - echo 'w' >>eddep - -rm -f makefile.bak - cp makefile makefile.bak - ed - makefile < eddep - rm eddep makedep - -# DO NOT DELETE THIS LINE - -dohits$O: ../general/general.h ../api/asc_ebc.h ../api/ebc_disp.h -dohits$O: ../ctlr/function.h dohits.h -ectype$O: ectype.h -mkastods$O: ../api/asc_ebc.h ../api/ebc_disp.h -mkastosc$O: ../general/general.h ../ctlr/function.h dohits.h -mkdctype$O: ../api/ebc_disp.h ectype.h -mkdstoas$O: ../api/asc_ebc.h ../api/ebc_disp.h -mkhits$O: ../ctlr/function.h dohits.h -prt3270$O: ../general/general.h ../api/asc_ebc.h ../ctlr/hostctlr.h -prt3270$O: ../ctlr/screen.h ../ctlr/function.h ../api/astosc.h -prt3270$O: ../general/globals.h ../ctlr/kbd.out diff --git a/usr.bin/tn3270/tools/mkastods/Makefile b/usr.bin/tn3270/tools/mkastods/Makefile new file mode 100644 index 0000000000..0e1f7fef60 --- /dev/null +++ b/usr.bin/tn3270/tools/mkastods/Makefile @@ -0,0 +1,12 @@ +# $Header: /b/source/CVS/src/usr.bin/tn3270/tools/mkastods/Makefile,v 1.3 1993/04/27 06:47:42 cgd Exp $ + +CFLAGS += -I${.CURDIR}/.. -I. + +NOMAN = I mean it! + +SRCS = mkastods.c asc_ebc.c ebc_disp.c +PROG = mkastods + +.include <bsd.prog.mk> + +.PATH: ${.CURDIR}/../../api diff --git a/usr.bin/tn3270/tools/mkastods.c b/usr.bin/tn3270/tools/mkastods/mkastods.c similarity index 100% rename from usr.bin/tn3270/tools/mkastods.c rename to usr.bin/tn3270/tools/mkastods/mkastods.c diff --git a/usr.bin/tn3270/tools/mkastosc/Makefile b/usr.bin/tn3270/tools/mkastosc/Makefile new file mode 100644 index 0000000000..29130eb9bb --- /dev/null +++ b/usr.bin/tn3270/tools/mkastosc/Makefile @@ -0,0 +1,12 @@ +# $Header: /b/source/CVS/src/usr.bin/tn3270/tools/mkastosc/Makefile,v 1.3 1993/04/27 06:47:44 cgd Exp $ + +CFLAGS += -I${.CURDIR}/../mkhits -I${.CURDIR}/.. -I. + +NOMAN = I mean it! + +SRCS = mkastosc.c dohits.c asc_ebc.c ebc_disp.c +PROG = mkastosc + +.include <bsd.prog.mk> + +.PATH: ${.CURDIR}/../mkhits ${.CURDIR}/../../api diff --git a/usr.bin/tn3270/tools/mkastosc.c b/usr.bin/tn3270/tools/mkastosc/mkastosc.c similarity index 100% rename from usr.bin/tn3270/tools/mkastosc.c rename to usr.bin/tn3270/tools/mkastosc/mkastosc.c diff --git a/usr.bin/tn3270/tools/mkdctype/Makefile b/usr.bin/tn3270/tools/mkdctype/Makefile new file mode 100644 index 0000000000..44ba3cfedf --- /dev/null +++ b/usr.bin/tn3270/tools/mkdctype/Makefile @@ -0,0 +1,12 @@ +# $Header: /b/source/CVS/src/usr.bin/tn3270/tools/mkdctype/Makefile,v 1.3 1993/04/27 06:47:46 cgd Exp $ + +CFLAGS += -I${.CURDIR}/.. -I. + +NOMAN = I mean it! + +SRCS = mkdctype.c ebc_disp.c ectype.c +PROG = mkdctype + +.include <bsd.prog.mk> + +.PATH: ${.CURDIR}/../../api diff --git a/usr.bin/tn3270/tools/ectype.c b/usr.bin/tn3270/tools/mkdctype/ectype.c similarity index 100% rename from usr.bin/tn3270/tools/ectype.c rename to usr.bin/tn3270/tools/mkdctype/ectype.c diff --git a/usr.bin/tn3270/tools/ectype.h b/usr.bin/tn3270/tools/mkdctype/ectype.h similarity index 100% rename from usr.bin/tn3270/tools/ectype.h rename to usr.bin/tn3270/tools/mkdctype/ectype.h diff --git a/usr.bin/tn3270/tools/mkdctype.c b/usr.bin/tn3270/tools/mkdctype/mkdctype.c similarity index 100% rename from usr.bin/tn3270/tools/mkdctype.c rename to usr.bin/tn3270/tools/mkdctype/mkdctype.c diff --git a/usr.bin/tn3270/tools/mkdstoas/Makefile b/usr.bin/tn3270/tools/mkdstoas/Makefile new file mode 100644 index 0000000000..4f7f033984 --- /dev/null +++ b/usr.bin/tn3270/tools/mkdstoas/Makefile @@ -0,0 +1,12 @@ +# $Header: /b/source/CVS/src/usr.bin/tn3270/tools/mkdstoas/Makefile,v 1.3 1993/04/27 06:47:48 cgd Exp $ + +CFLAGS += -I${.CURDIR}/.. -I. + +NOMAN = I mean it! + +SRCS = mkdstoas.c asc_ebc.c ebc_disp.c +PROG = mkdstoas + +.include <bsd.prog.mk> + +.PATH: ${.CURDIR}/../../api diff --git a/usr.bin/tn3270/tools/mkdstoas.c b/usr.bin/tn3270/tools/mkdstoas/mkdstoas.c similarity index 100% rename from usr.bin/tn3270/tools/mkdstoas.c rename to usr.bin/tn3270/tools/mkdstoas/mkdstoas.c diff --git a/usr.bin/tn3270/tools/mkhits/Makefile b/usr.bin/tn3270/tools/mkhits/Makefile new file mode 100644 index 0000000000..5bbc17db8f --- /dev/null +++ b/usr.bin/tn3270/tools/mkhits/Makefile @@ -0,0 +1,12 @@ +# $Header: /b/source/CVS/src/usr.bin/tn3270/tools/mkhits/Makefile,v 1.3 1993/04/27 06:47:50 cgd Exp $ + +CFLAGS += -I${.CURDIR}/.. -I. + +NOMAN = I mean it! + +SRCS = mkhits.c dohits.c asc_ebc.c ebc_disp.c +PROG = mkhits + +.include <bsd.prog.mk> + +.PATH: ${.CURDIR}/../../api diff --git a/usr.bin/tn3270/tools/dohits.c b/usr.bin/tn3270/tools/mkhits/dohits.c similarity index 100% rename from usr.bin/tn3270/tools/dohits.c rename to usr.bin/tn3270/tools/mkhits/dohits.c diff --git a/usr.bin/tn3270/tools/dohits.h b/usr.bin/tn3270/tools/mkhits/dohits.h similarity index 100% rename from usr.bin/tn3270/tools/dohits.h rename to usr.bin/tn3270/tools/mkhits/dohits.h diff --git a/usr.bin/tn3270/tools/mkhits.c b/usr.bin/tn3270/tools/mkhits/mkhits.c similarity index 100% rename from usr.bin/tn3270/tools/mkhits.c rename to usr.bin/tn3270/tools/mkhits/mkhits.c diff --git a/usr.bin/touch/touch.1 b/usr.bin/touch/touch.1 index f5bfd854d5..f65e0e7c16 100644 --- a/usr.bin/touch/touch.1 +++ b/usr.bin/touch/touch.1 @@ -32,44 +32,142 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)touch.1 6.4 (Berkeley) 6/27/91 +.\" @(#)touch.1 6.5 (Berkeley) 3/6/93 .\" -.Dd June 27, 1991 +.Dd March 6, 1993 .Dt TOUCH 1 .Os .Sh NAME .Nm touch -.Nd update date last modified of a file +.Nd change file access and modification times .Sh SYNOPSIS .Nm touch -.Op Fl c -.Op Fl f +.Op Fl acfm +.Op Fl r Ar file +.Op Fl t [[CC]YY]MMDDhhmm[.SS] +.Op Fl T seconds .Ar file ... .Sh DESCRIPTION The .Nm touch -utility -changes the modification and or access times -of the given -.Ar file . +utility sets the modification and access times of files to the +current time of day. +If the file doesn't exist, it is created with default permissions. .Pp -Available options: +The following options are available: .Bl -tag -width Ds +.It Fl a +Change the access time of the file. +The modification time of the file is not changed unless the +.Fl m +flag is also specified. .It Fl c -Do not create a specified file if it does not exist. -Do not write any diagnostic messages concerning this -condition. +Do not create the file if it does not exist. +The +.Nm touch +utility does not treat this as an error. +No error messages are displayed and the exit value is not affected. .It Fl f -Attempt to force the +Attempt to force the update, even if the file permissions do not +currently permit it. +.It Fl m +Change the modification time of the file. +The access time of the file is not changed unless the +.Fl a +flag is also specified. +.It Fl r +Use the access and modifications times from the specified file +instead of the current time of day. +.It Fl T +Change the access and modification times to the specified time. +The time is seconds since the start of the epoch. (The same value returned +by time(3).) +.It Fl t +Change the access and modification times to the specified time. +The argument should be in the form +.Dq [[CC]YY]MMDDhhmm[.SS] +where each pair of letters represents the following: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It Ar CC +The first two digits of the year (the century). +.It Ar YY +The second two digits of the year. +If +.Dq YY +is specified, but +.Dq CC +is not, a value for +.Dq YY +between 69 and 99 results in a +.Dq YY +value of 19. +Otherwise, a +.Dq YY +value of 20 is used. +.It Ar MM +The month of the year, from 1 to 12. +.It Ar DD +the day of the month, from 1 to 31. +.It Ar hh +The hour of the day, from 0 to 23. +.It Ar mm +The minute of the hour, from 0 to 59. +.It Ar SS +The second of the minute, from 0 to 61. +.El +.Pp +If the +.Dq CC +and +.Dq YY +letter pairs are not specified, the values default to the current +year. +If the +.Dq SS +letter pair is not specified, the value defaults to 0. +.El +.Pp +The .Nm touch -in spite of read and write permissions on a -.Ar file . +utility exits 0 on success, and >0 if an error occurs. .Sh SEE ALSO -.Xr utimes 2 +.Xr utimes 2 , +.Xr time 3 +.Sh COMPATIBILITY +The obsolescent form of +.Nm touch , +where a time format is specified as the first argument, is supported. +When no +.Fl r +or +.Fl t +option is specified, there are at least two arguments, and the first +argument is a string of digits either eight or ten characters in length, +the first argument is interprested as a time specification of the form +.Dq MMDDhhmm[YY] . +.Pp +The +.Dq MM , +.Dq DD , +.Dq hh +and +.Dq mm +letter pairs are treated as their counterparts specified to the +.Fl t +option. +If the +.Dq YY +letter pair is in the range 69 to 99, the year is set to 1969 to 1999, +otherwise, the year is set in the 21st century. .Sh HISTORY A -.Nm +.Nm touch command appeared in .At v7 . -This command is -.Ud . +.Sh STANDARDS +The +.Nm touch +function is expected to be a superset of the +.St -p1003.2 +specification. diff --git a/usr.bin/touch/touch.c b/usr.bin/touch/touch.c index cb7bffeaac..c3f917ec64 100644 --- a/usr.bin/touch/touch.c +++ b/usr.bin/touch/touch.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1988 Regents of the University of California. + * Copyright (c) 1993 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,129 +33,325 @@ #ifndef lint char copyright[] = -"@(#) Copyright (c) 1988 Regents of the University of California.\n\ +"@(#) Copyright (c) 1993 Regents of the University of California.\n\ All rights reserved.\n"; #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)touch.c 4.8 (Berkeley) 6/1/90"; +static char sccsid[] = "@(#)touch.c 5.5 (Berkeley) 3/7/93"; #endif /* not lint */ -/* - * Attempt to set the modify date of a file to the current date. If the - * file exists, read and write its first character. If the file doesn't - * exist, create it, unless -c option prevents it. If the file is read-only, - * -f forces chmod'ing and touch'ing. - */ #include <sys/types.h> -#include <sys/file.h> #include <sys/stat.h> +#include <sys/time.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> #include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +int rw __P((char *, struct stat *, int)); +void stime_arg1 __P((char *, struct timeval *)); +void stime_arg2 __P((char *, int, struct timeval *)); +void stime_file __P((char *, struct timeval *)); +void usage __P((void)); -static int dontcreate; /* set if -c option */ -static int force; /* set if -f option */ +/*temporary we aren't 4.4 portability hack*/ +#define TIMET_TO_TIMEVAL(timevalp, tp) (timevalp)->tv_sec = *(tp); +int main(argc, argv) int argc; - char **argv; + char *argv[]; { - extern int optind; - int ch, retval; + struct stat sb; + struct timeval tv[2]; + int aflag, cflag, fflag, mflag, ch, fd, len, rval, timeset; + char *p; - dontcreate = force = retval = 0; - while ((ch = getopt(argc, argv, "cf")) != EOF) - switch((char)ch) { + aflag = cflag = fflag = mflag = timeset = 0; + if (gettimeofday(&tv[0], NULL)) + err(1, "gettimeofday"); + + while ((ch = getopt(argc, argv, "acfmr:t:T:")) != EOF) + switch(ch) { + case 'a': + aflag = 1; + break; case 'c': - dontcreate = 1; + cflag = 1; break; case 'f': - force = 1; + fflag = 1; + break; + case 'm': + mflag = 1; + break; + case 'r': + timeset = 1; + stime_file(optarg, tv); + break; + case 't': + timeset = 1; + stime_arg1(optarg, tv); + break; + case 'T': + timeset = 1; + tv[0].tv_sec = atoi(optarg); + tv[0].tv_usec = 0; + tv[1] = tv[0]; break; case '?': default: usage(); } - if (!*(argv += optind)) + argc -= optind; + argv += optind; + + /* Default is both -a and -m. */ + if (aflag == 0 && mflag == 0) + aflag = mflag = 1; + + /* + * If no -r or -t flag, at least two operands, the first of which + * is an 8 or 10 digit number, use the obsolete time specification. + */ + if (!timeset && argc > 1) { + (void)strtol(argv[0], &p, 10); + len = p - argv[0]; + if (*p == '\0' && (len == 8 || len == 10)) { + timeset = 1; + stime_arg2(argv[0], len == 10, tv); + argv++; + } + } + + /* Otherwise use the current time of day. */ + if (!timeset) + tv[1] = tv[0]; + + if (*argv == NULL) usage(); - do { - retval |= touch(*argv); - } while (*++argv); - exit(retval); -} -touch(filename) - char *filename; -{ - struct stat statbuffer; + for (rval = 0; *argv; ++argv) { + /* See if the file exists. */ + if (stat(*argv, &sb)) + if (!cflag) { + /* Create the file. */ + fd = open(*argv, + O_WRONLY | O_CREAT, DEFFILEMODE); + if (fd == -1 || fstat(fd, &sb) || close(fd)) { + rval = 1; + warn("%s", *argv); + continue; + } + + /* If using the current time, we're done. */ + if (!timeset) + continue; + } else + continue; + + if (!aflag) +/* TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec);*/ + TIMET_TO_TIMEVAL(&tv[0], &sb.st_atime); + if (!mflag) +/* TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);*/ + TIMET_TO_TIMEVAL(&tv[1], &sb.st_mtime); - if (stat(filename, &statbuffer) == -1) { - if (!dontcreate) - return(readwrite(filename, 0L)); - fprintf(stderr, "touch: %s: does not exist\n", filename); - return(1); + + /* Try utimes(2). */ + if (!utimes(*argv, tv)) + continue; + + /* If the user specified a time, nothing else we can do. */ + if (timeset) { + rval = 1; + warn("%s", *argv); + } + + /* + * System V and POSIX 1003.1 require that a NULL argument + * set the access/modification times to the current time. + * The permission checks are different, too, in that the + * ability to write the file is sufficient. Take a shot. + */ + if (!utimes(*argv, NULL)) + continue; + + /* Try reading/writing. */ + if (rw(*argv, &sb, fflag)) + rval = 1; } - if ((statbuffer.st_mode & S_IFMT) != S_IFREG) { - fprintf(stderr, "touch: %s: can only touch regular files\n", - filename); - return(1); + exit(rval); +} + +#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; + +void +stime_arg1(arg, tvp) + char *arg; + struct timeval *tvp; +{ + struct tm *t; + int yearset; + char *p; + /* Start with the current time. */ + if ((t = localtime(&tvp[0].tv_sec)) == NULL) + err(1, "localtime"); + /* [[CC]YY]MMDDhhmm[.SS] */ + if ((p = strchr(arg, '.')) == NULL) + t->tm_sec = 0; /* Seconds defaults to 0. */ + else { + if (strlen(p + 1) != 2) + goto terr; + *p++ = '\0'; + t->tm_sec = ATOI2(p); } - if (!access(filename, R_OK | W_OK)) - return(readwrite(filename,statbuffer.st_size)); - if (force) { - int retval; - - if (chmod(filename, 0666)) { - fprintf(stderr, "touch: %s: couldn't chmod: ", - filename); - perror((char *)NULL); - return(1); - } - retval = readwrite(filename, statbuffer.st_size); - if (chmod(filename, statbuffer.st_mode)) { - fprintf(stderr, "touch: %s: couldn't chmod back: ", - filename); - perror((char *)NULL); - return(1); + + yearset = 0; + switch(strlen(arg)) { + case 12: /* CCYYMMDDhhmm */ + t->tm_year = ATOI2(arg); + t->tm_year *= 100; + yearset = 1; + /* FALLTHOUGH */ + case 10: /* YYMMDDhhmm */ + if (yearset) { + yearset = ATOI2(arg); + t->tm_year += yearset; + } else { + yearset = ATOI2(arg); + if (yearset < 69) + t->tm_year = yearset + 2000; + else + t->tm_year = yearset + 1900; } - return(retval); + t->tm_year -= 1900; /* Convert to UNIX time. */ + /* FALLTHROUGH */ + case 8: /* MMDDhhmm */ + t->tm_mon = ATOI2(arg); + --t->tm_mon; /* Convert from 01-12 to 00-11 */ + t->tm_mday = ATOI2(arg); + t->tm_hour = ATOI2(arg); + t->tm_min = ATOI2(arg); + break; + default: + goto terr; } - fprintf(stderr, "touch: %s: cannot touch\n", filename); - return(1); + + t->tm_isdst = -1; /* Figure out DST. */ + tvp[0].tv_sec = tvp[1].tv_sec = mktime(t); + if (tvp[0].tv_sec == -1) +terr: errx(1, + "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); + + tvp[0].tv_usec = tvp[1].tv_usec = 0; } -readwrite(filename, size) - char *filename; - off_t size; +void +stime_arg2(arg, year, tvp) + char *arg; + int year; + struct timeval *tvp; { - int filedescriptor; - char first; - off_t lseek(); - - if (size) { - filedescriptor = open(filename, O_RDWR, 0); - if (filedescriptor == -1) { -error: fprintf(stderr, "touch: %s: ", filename); - perror((char *)NULL); - return(1); - } - if (read(filedescriptor, &first, 1) != 1) - goto error; - if (lseek(filedescriptor, 0L, 0) == -1) - goto error; - if (write(filedescriptor, &first, 1) != 1) - goto error; + struct tm *t; + /* Start with the current time. */ + if ((t = localtime(&tvp[0].tv_sec)) == NULL) + err(1, "localtime"); + + t->tm_mon = ATOI2(arg); /* MMDDhhmm[yy] */ + --t->tm_mon; /* Convert from 01-12 to 00-11 */ + t->tm_mday = ATOI2(arg); + t->tm_hour = ATOI2(arg); + t->tm_min = ATOI2(arg); + if (year) + t->tm_year = ATOI2(arg); + + t->tm_isdst = -1; /* Figure out DST. */ + tvp[0].tv_sec = tvp[1].tv_sec = mktime(t); + if (tvp[0].tv_sec == -1) + errx(1, + "out of range or illegal time specification: MMDDhhmm[yy]"); + + tvp[0].tv_usec = tvp[1].tv_usec = 0; +} + +void +stime_file(fname, tvp) + char *fname; + struct timeval *tvp; +{ + struct stat sb; + + if (stat(fname, &sb)) + err(1, "%s", fname); +/* TIMESPEC_TO_TIMEVAL(tvp, &sb.st_atimespec);*/ + TIMET_TO_TIMEVAL(tvp, &sb.st_atime); +/* TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtimespec);*/ + TIMET_TO_TIMEVAL(tvp + 1, &sb.st_mtime); +} + +int +rw(fname, sbp, force) + char *fname; + struct stat *sbp; + int force; +{ + int fd, needed_chmod, rval; + u_char byte; + + /* Try regular files and directories. */ + if (!S_ISREG(sbp->st_mode) && !S_ISDIR(sbp->st_mode)) { + warnx("%s: %s", fname, strerror(EFTYPE)); + return (1); + } + + needed_chmod = rval = 0; + if ((fd = open(fname, O_RDWR, 0)) == -1) { + if (!force || chmod(fname, DEFFILEMODE)) + goto err; + if ((fd = open(fname, O_RDWR, 0)) == -1) + goto err; + needed_chmod = 1; + } + + if (sbp->st_size != 0) { + if (read(fd, &byte, sizeof(byte)) != sizeof(byte)) + goto err; + if (lseek(fd, (off_t)0, SEEK_SET) == -1) + goto err; + if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) + goto err; } else { - filedescriptor = creat(filename, 0666); - if (filedescriptor == -1) - goto error; + if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) { +err: rval = 1; + warn("%s", fname); + } else if (ftruncate(fd, (off_t)0)) { + rval = 1; + warn("%s: file modified", fname); + } + } + + if (close(fd) && rval != 1) { + rval = 1; + warn("%s", fname); + } + if (needed_chmod && chmod(fname, sbp->st_mode) && rval != 1) { + rval = 1; + warn("%s: permissions modified", fname); } - if (close(filedescriptor) == -1) - goto error; - return(0); + return (rval); } +void usage() { - fprintf(stderr, "usage: touch [-cf] file ...\n"); + (void)fprintf(stderr, + "usage: touch [-acfm] [-r file] [-t time] [-T time] file ...\n"); exit(1); } diff --git a/usr.bin/tr/Makefile b/usr.bin/tr/Makefile index ede6a72d0d..4522b56f45 100644 --- a/usr.bin/tr/Makefile +++ b/usr.bin/tr/Makefile @@ -1,5 +1,6 @@ -# @(#)Makefile 5.2 (Berkeley) 5/11/90 +# @(#)Makefile 5.3 (Berkeley) 10/27/91 PROG= tr +SRCS= str.c tr.c .include <bsd.prog.mk> diff --git a/usr.bin/tr/extern.h b/usr.bin/tr/extern.h new file mode 100644 index 0000000000..318f665269 --- /dev/null +++ b/usr.bin/tr/extern.h @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 5.2 (Berkeley) 10/27/91 + */ + +typedef struct { + enum { STRING1, STRING2 } which; + enum { EOS, INFINITE, NORMAL, RANGE, SEQUENCE, SET } state; + int cnt; /* character count */ + int lastch; /* last character */ + int equiv[2]; /* equivalence set */ + int *set; /* set of characters */ + char *str; /* user's string */ +} STR; + +#include <limits.h> +#define NCHARS (UCHAR_MAX + 1) /* Number of possible characters. */ +#define OOBCH (UCHAR_MAX + 1) /* Out of band character value. */ + +void err __P((const char *fmt, ...)); +int next __P((STR *)); diff --git a/usr.bin/tr/str.c b/usr.bin/tr/str.c new file mode 100644 index 0000000000..daa719510f --- /dev/null +++ b/usr.bin/tr/str.c @@ -0,0 +1,351 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)str.c 5.9 (Berkeley) 3/4/93"; +#endif /* not lint */ + +#include <sys/cdefs.h> +#include <sys/types.h> + +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "extern.h" + +static int backslash __P((STR *)); +static int bracket __P((STR *)); +static int c_class __P((const void *, const void *)); +static void genclass __P((STR *)); +static void genequiv __P((STR *)); +static int genrange __P((STR *)); +static void genseq __P((STR *)); + +int +next(s) + register STR *s; +{ + register int ch; + + switch (s->state) { + case EOS: + return (0); + case INFINITE: + return (1); + case NORMAL: + switch (ch = *s->str) { + case '\0': + s->state = EOS; + return (0); + case '\\': + s->lastch = backslash(s); + break; + case '[': + if (bracket(s)) + return (next(s)); + /* FALLTHROUGH */ + default: + ++s->str; + s->lastch = ch; + break; + } + + /* We can start a range at any time. */ + if (s->str[0] == '-' && genrange(s)) + return (next(s)); + return (1); + case RANGE: + if (s->cnt-- == 0) { + s->state = NORMAL; + return (next(s)); + } + ++s->lastch; + return (1); + case SEQUENCE: + if (s->cnt-- == 0) { + s->state = NORMAL; + return (next(s)); + } + return (1); + case SET: + if ((s->lastch = s->set[s->cnt++]) == OOBCH) { + s->state = NORMAL; + return (next(s)); + } + return (1); + } + /* NOTREACHED */ +} + +static int +bracket(s) + register STR *s; +{ + register char *p; + + switch (s->str[1]) { + case ':': /* "[:class:]" */ + if ((p = strstr(s->str + 2, ":]")) == NULL) + return (0); + *p = '\0'; + s->str += 2; + genclass(s); + s->str = p + 2; + return (1); + case '=': /* "[=equiv=]" */ + if ((p = strstr(s->str + 2, "=]")) == NULL) + return (0); + s->str += 2; + genequiv(s); + return (1); + default: /* "[\###*n]" or "[#*n]" */ + if ((p = strpbrk(s->str + 2, "*]")) == NULL) + return (0); + if (p[0] != '*' || index(p, ']') == NULL) + return (0); + s->str += 1; + genseq(s); + return (1); + } + /* NOTREACHED */ +} + +int isalnum __P((int)), + isalpha __P((int)), + isblank __P((int)), + isspace __P((int)), + iscntrl __P((int)), + isdigit __P((int)), + isgraph __P((int)), + islower __P((int)), + isprint __P((int)), + ispunct __P((int)), + isupper __P((int)), + isxdigit __P((int)); + + +static int isblank(x) /* until 4.4 */ + int x; +{ + if ((x == ' ') || (x== '\t')) return 1; + return 0; +} + + +typedef struct { + char *name; + int (*func) __P((int)); + int *set; +} CLASS; + +static CLASS classes[] = { + { "alnum", isalnum, }, + { "alpha", isalpha, }, + { "blank", isblank, }, + { "cntrl", iscntrl, }, + { "digit", isdigit, }, + { "graph", isgraph, }, + { "lower", islower, }, + { "print", isupper, }, + { "punct", ispunct, }, + { "space", isspace, }, + { "upper", isupper, }, + { "xdigit", isxdigit, }, +}; + +static void +genclass(s) + STR *s; +{ + register int cnt, (*func) __P((int)); + CLASS *cp, tmp; + int *p; + + tmp.name = s->str; + if ((cp = (CLASS *)bsearch(&tmp, classes, sizeof(classes) / + sizeof(CLASS), sizeof(CLASS), c_class)) == NULL) + err("unknown class %s", s->str); + + if ((cp->set = p = malloc((NCHARS + 1) * sizeof(int))) == NULL) + err("%s", strerror(errno)); + bzero(p, (NCHARS + 1) * sizeof(int)); + for (cnt = 0, func = cp->func; cnt < NCHARS; ++cnt) + if ((func)(cnt)) + *p++ = cnt; + *p = OOBCH; + + s->cnt = 0; + s->state = SET; + s->set = cp->set; +} + +static int +c_class(a, b) + const void *a, *b; +{ + return (strcmp(((CLASS *)a)->name, ((CLASS *)b)->name)); +} + +/* + * English doesn't have any equivalence classes, so for now + * we just syntax check and grab the character. + */ +static void +genequiv(s) + STR *s; +{ + if (*s->str == '\\') { + s->equiv[0] = backslash(s); + if (*s->str != '=') + err("misplaced equivalence equals sign"); + } else { + s->equiv[0] = s->str[0]; + if (s->str[1] != '=') + err("misplaced equivalence equals sign"); + } + s->str += 2; + s->cnt = 0; + s->state = SET; + s->set = s->equiv; +} + +static int +genrange(s) + STR *s; +{ + int stopval; + char *savestart; + + savestart = s->str; + stopval = *++s->str == '\\' ? backslash(s) : *s->str++; + if (stopval < s->lastch) { + s->str = savestart; + return (0); + } + s->cnt = stopval - s->lastch + 1; + s->state = RANGE; + --s->lastch; + return (1); +} + +static void +genseq(s) + STR *s; +{ + char *ep; + + if (s->which == STRING1) + err("sequences only valid in string2"); + + if (*s->str == '\\') + s->lastch = backslash(s); + else + s->lastch = *s->str++; + if (*s->str != '*') + err("misplaced sequence asterisk"); + + switch (*++s->str) { + case '\\': + s->cnt = backslash(s); + break; + case ']': + s->cnt = 0; + ++s->str; + break; + default: + if (isdigit(*s->str)) { + s->cnt = strtol(s->str, &ep, 0); + if (*ep == ']') { + s->str = ep + 1; + break; + } + } + err("illegal sequence count"); + /* NOTREACHED */ + } + + s->state = s->cnt ? SEQUENCE : INFINITE; +} + +/* Use the #defines isXXX() here, DON'T use them above. */ +#include <ctype.h> + +/* + * Translate \??? into a character. Up to 3 octal digits, if no digits either + * an escape code or a literal character. + */ +static int +backslash(s) + register STR *s; +{ + register int ch, cnt, val; + + for (cnt = val = 0;;) { + ch = *++s->str; + if (!isascii(ch) || !isdigit(ch)) + break; + val = val * 8 + ch - '0'; + if (++cnt == 3) { + ++s->str; + break; + } + } + if (cnt) + return (val); + if (ch != '\0') + ++s->str; + switch (ch) { + case 'a': /* escape characters */ + return ('\7'); + case 'b': + return ('\b'); + case 'f': + return ('\f'); + case 'n': + return ('\n'); + case 'r': + return ('\r'); + case 't': + return ('\t'); + case 'v': + return ('\13'); + case '\0': /* \" -> \ */ + s->state = EOS; + return ('\\'); + default: /* \x" -> x */ + return (ch); + } +} diff --git a/usr.bin/tr/tr.1 b/usr.bin/tr/tr.1 index 2e2c36917e..40120a6475 100644 --- a/usr.bin/tr/tr.1 +++ b/usr.bin/tr/tr.1 @@ -32,9 +32,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)tr.1 6.6 (Berkeley) 7/30/91 +.\" @(#)tr.1 6.9 (Berkeley) 10/27/91 .\" -.Dd July 30, 1991 +.Dd October 27, 1991 .Dt TR 1 .Os .Sh NAME @@ -42,180 +42,251 @@ .Nd Translate Characters. .Sh SYNOPSIS .Nm tr +.Op Fl cs +.Ar string1 string2 +.Nm tr +.Op Fl c +.Fl d +.Ar string1 +.Nm tr .Op Fl c -.Op Fl d | Fl s +.Fl s +.Ar string1 +.Nm tr +.Op Fl c +.Fl ds .Ar string1 string2 .Sh DESCRIPTION -The tr utility copies the standard input to the standard -output with substitution or deletion of selected characters. -The options specified and the -.Ar string1 -and -.Ar string2 -operands -control translations that occur while copying characters. +The +.Nm tr +utility copies the standard input to the standard output with substitution +or deletion of selected characters. .Pp The following options are available: .Bl -tag -width Ds .It Fl c -Complements the set of characters in string1 with -respect to the universe of characters whose -.Tn ISO -646 -[4] codes are 00 through 0377 octal. +Complements the set of characters in +.Ar string1 , +that is ``-c ab'' includes every character except for ``a'' and ``b''. .It Fl d -Deletes all input characters in -.Ar string1 . +The +.Fl d +option causes characters to be deleted from the input. .It Fl s -Squeezes all output strings of one or more -instances of a single character in -.Ar string2 -to a -single instance of that character. -Input characters found -in +The +.Fl s +option squeezes multiple occurrences of the characters listed in the last +operand (either .Ar string1 -are mapped into the corresponding characters -of -.Ar string2 . -When +or +.Ar string2 ) +in the input into a single instance of the character. +This occurs after all deletion and translation is completed. +.El +.Pp +In the first synopsis form, the characters in +.Ar string1 +are translated into the characters in .Ar string2 -is shorter than -.Ar string1 , string2 -is extended to the length of +where the first character in .Ar string1 -by duplicating the last character of -.Ar string2 . -If +is translated into the first character in .Ar string2 -is explicitly a string of zero -length, +and so on. +If +.Ar string1 +is longer than +.Ar string2 , +the last character found in .Ar string2 -is padded with -.Tn NUL -characters. -.El +is duplicated until +.Ar string1 +is exhausted. .Pp -The following operands are available: -.Bl -tag -width stringx -.It Ar string1 -.It Ar string2 -Translation character strings. -.El +In the second synopsis form, the characters in +.Ar string1 +are deleted from the input. .Pp +In the third synopsis form, the characters in +.Ar string1 +are compressed as described for the +.Fl s +option. .Pp -If -the -.Fl c -option is given in conjunction with the -.Fl d -option, then only those characters found in +In the fourth synopsis form, the characters in .Ar string1 -is copied to the standard output. +are deleted from the input, and the characters in +.Ar string2 +are compressed as described for the +.Fl s +option. .Pp The following conventions can be used in .Ar string1 -or +and .Ar string2 -or both to specify characters, character ranges, character -classes, or collating elements: -.Bl -tag -width [[:equiv:]] +to specify sets of characters: +.Bl -tag -width [:equiv:] .It character -Represents that character. +Any character not described by one of the following conventions +represents itself. .It \eoctal -A backslash followed by 1, 2, or 3 octal -digits represents a character with that -encoded value. -If a \eoctal sequence is -followed by digits, the backslash and up to -three digits are interpreted to prepare a -character; subsequent digits are interpreted -as individual characters. +A backslash followed by 1, 2 or 3 octal digits represents a character +with that encoded value. +To follow an octal sequence with a digit as a character, left zero-pad +the octal sequence to the full 3 octal digits. .It \echaracter -A backslash followed by any character except -an octal digit represents that character. -.It [c-c] -Represents the range of ordered elements -between the range endpoints, inclusive. -.\" ; as -.\" defined by the current setting of the -.\" setlocale() category -.\" .Em LC_COLLATE . -.\" The collating -.\" elements are ordered in ascending collating -.\" sequence, and the first endpoint shall precede -.\" the second in the collating sequence. -.It [[:class:]] -Represents all characters belonging to the -defined character class. -.\" as defined by the -.\" current setting of the setlocale() category -.\" .Ev LC_CTYPE . -Allowable names for class are: -.Bl -column alpha upper lower digit xdigit -offset indent -alpha upper lower digit xdigit alnum -space punct print graph cntrl +A backslash followed by certain special characters maps to special +values. +.sp +.Bl -column +.It \ea <alert character> +.It \eb <backspace> +.It \ef <form-feed> +.It \en <newline> +.It \er <carriage return> +.It \et <tab> +.It \ev <vertical tab> .El +.sp +A backslash followed by any other character maps to that character. +.It c-c +Represents the range of characters between the range endpoints, inclusively. +.It [:class:] +Represents all characters belonging to the defined character class. +Class names are: +.sp +.Bl -column +.It alnum <alphanumeric characters> +.It alpha <alphabetic characters> +.It cntrl <control characters> +.It digit <numeric characters> +.It graph <graphic characters> +.It lower <lower-case alphabetic characters> +.It print <printable characters> +.It punct <punctuation characters> +.It space <space characters> +.It upper <upper-case characters> +.It xdigit <hexadecimal characters> +.El +.Pp +\." All classes may be used in +\." .Ar string1 , +\." and in +\." .Ar string2 +\." when both the +\." .Fl d +\." and +\." .Fl s +\." options are specified. +\." Otherwise, only the classes ``upper'' and ``lower'' may be used in +\." .Ar string2 +\." and then only when the corresponding class (``upper'' for ``lower'' +\." and vice-versa) is specified in the same relative position in +\." .Ar string1 . +\." .Pp +With the exception of the ``upper'' and ``lower'' classes, characters +in the classes are in unspecified order. +In the ``upper'' and ``lower'' classes, characters are entered in +ascending order. .Pp -The characters placed in ascending order. -.\" .Em LC_COLLATE . -.\" (Characters not defined in the -.\" current collating sequence are ordered -.\" according to the collation sequence's rules -.\" for ordering of undefined characters.) -.It [[=equiv=]] -Represents all characters or collating (sorting) -elements belonging to the same equivalence class -as equiv. -.\" -.\" -.\" .Em LC_COLLATE . +For specific information as to which ASCII characters are included +in these classes, see +.Xr ctype 3 +and related manual pages. +.It [=equiv=] +Represents all characters or collating (sorting) elements belonging to +the same equivalence class as +.Ar equiv . If -there is a secondary ordering within the -equivalence class, the characters are ordered -in ascending sequence. -Otherwise, they are -ordered after their encoded values. -.It [[.cs.]] -Represents a collating symbol. -Multicharacter -collating symbols shall be represented as -collating symbols to distinguish them from a -string of the same characters. -.It [x*n] -Represents n repeated occurrences of the -character or collating symbol x. +there is a secondary ordering within the equivalence class, the characters +are ordered in ascending sequence. +Otherwise, they are ordered after their encoded values. +An example of an equivalence class might be ``c'' and ``ch'' in Spanish; +English has no equivalence classes. +.It [#*n] +Represents +.Ar n +repeated occurrences of the character represented by +.Ar # . This expression is only valid when it occurs in .Ar string2 . -If n is omitted or is zero, it is -be interpreted as large enough to extend the -.Ar string2 Ns -based -sequence to the length of the -.Ar string1 Ns -based -sequence. -If n has a leading -zero, it shall be interpreted as an octal -value. -Otherwise, it shall be interpreted as -a decimal value. +If +.Ar n +is omitted or is zero, it is be interpreted as large enough to extend +.Ar string2 +sequence to the length of +.Ar string1 . +If +.Ar n +has a leading zero, it is interpreted as an octal value, otherwise, +it's interpreted as a decimal value. .El .Pp -Characters belonging to an equivalence class occupy the same -position in the sequence, ordered after secondary ordering. +The +.Nm tr +utility exits 0 on success, and >0 if an error occurs. +.Sh EXAMPLES +The following examples are shown as given to the shell: +.sp +Create a list of the words in file1, one per line, where a word is taken to +be a maximal string of letters. +.sp +.D1 Li "tr -cs \*q[:alpha:]\*q \*q\en\*q < file1" +.sp +Translate the contents of file1 to upper-case. +.sp +.D1 Li "tr \*q[:lower:]\*q \*q[:upper:]\*q < file1" +.sp +Strip out non-printable characters from file1. +.sp +.D1 Li "tr -cd \*q[:print:]\*q < file1" +.Sh COMPATIBILITY +System V has historically implemented character ranges using the syntax +``[c-c]'' instead of the ``c-c'' used by historic BSD implementations and +standardized by POSIX. +System V shell scripts should work under this implementation as long as +the range is intended to map in another range, i.e. the command +``tr [a-z] [A-Z]'' will work as it will map the ``['' character in +.Ar string1 +to the ``['' character in +.Ar string2. +However, if the shell script is deleting or squeezing characters as in +the command ``tr -d [a-z]'', the characters ``['' and ``]'' will be +included in the deletion or compression list which would not have happened +under an historic System V implementation. +Additionally, any scripts that depended on the sequence ``a-z'' to +represent the three characters ``a'', ``-'' and ``z'' will have to be +rewritten as ``a\e-z''. .Pp The .Nm tr -utility exits with one of the following values: -.Bl -tag -width Ds -.It 0 -All input was processed successfully. -.It 1 -An error occurred. -.El +utility has historically not permitted the manipulation of NUL bytes in +its input and, additionally, stripped NUL's from its input stream. +This implementation has removed this behavior as a bug. +.Pp +The +.Nm tr +utility has historically been extremely forgiving of syntax errors, +for example, the +.Fl c +and +.Fl s +options were ignored unless two strings were specified. +This implementation will not permit illegal syntax. .Sh STANDARDS The .Nm tr utility is expected to be .St -p1003.2 compatible. +It should be noted that the feature wherein the last character of +.Ar string2 +is duplicated if +.Ar string2 +has less characters than +.Ar string1 +is permitted by POSIX but is not required. +Shell scripts attempting to be portable to other POSIX systems should use +the ``[#*]'' convention instead of relying on this behavior. diff --git a/usr.bin/tr/tr.c b/usr.bin/tr/tr.c index 95652f32c5..4b270cf694 100644 --- a/usr.bin/tr/tr.c +++ b/usr.bin/tr/tr.c @@ -38,31 +38,63 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)tr.c 4.7 (Berkeley) 7/23/90"; +static char sccsid[] = "@(#)tr.c 5.4 (Berkeley) 3/3/93"; #endif /* not lint */ #include <sys/types.h> #include <stdio.h> -#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include "extern.h" -#define NCHARS 256 /* size of u_char */ -#define OOBCH 257 /* out of band value */ +static int string1[NCHARS] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* ASCII */ + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +}, string2[NCHARS]; -typedef struct { - char *str; - int lastch, endrange; - enum { NORM, INRANGE, EOS } state; -} STR; +STR s1 = { STRING1, NORMAL, 0, OOBCH, { 0, OOBCH }, NULL, NULL }; +STR s2 = { STRING2, NORMAL, 0, OOBCH, { 0, OOBCH }, NULL, NULL }; +static void setup __P((int *, char *, STR *, int)); +static void usage __P((void)); + +int main(argc, argv) int argc; char **argv; { - extern int optind; - STR s1, s2; - register int ch, indx, lastch; - int cflag, dflag, sflag; - u_char *tp, tab[NCHARS], squeeze[NCHARS]; + register int ch, cnt, lastch, *p; + int cflag, dflag, sflag, isstring2; cflag = dflag = sflag = 0; while ((ch = getopt(argc, argv, "cds")) != EOF) @@ -78,151 +110,178 @@ main(argc, argv) break; case '?': default: - fprintf(stderr, - "usage: tr [-cds] [string1 [string2]]\n"); - exit(1); + usage(); } argc -= optind; argv += optind; + switch(argc) { + case 0: + default: + usage(); + /* NOTREACHED */ + case 1: + isstring2 = 0; + break; + case 2: + isstring2 = 1; + break; + } + /* - * the original tr was amazingly tolerant of the command line. - * Neither -c or -s have any effect unless there are two strings. - * Extra arguments are silently ignored. Bag this noise, they - * should all be errors. + * tr -ds [-c] string1 string2 + * Delete all characters (or complemented characters) in string1. + * Squeeze all characters in string2. */ - if (argc < 2 && !dflag) { - while ((ch = getchar()) != EOF) - putchar(ch); + if (dflag && sflag) { + if (!isstring2) + usage(); + + setup(string1, argv[0], &s1, cflag); + setup(string2, argv[1], &s2, 0); + + for (lastch = OOBCH; (ch = getchar()) != EOF;) + if (!string1[ch] && (!string2[ch] || lastch != ch)) { + lastch = ch; + (void)putchar(ch); + } exit(0); } - bzero(tab, NCHARS); - if (sflag) { - s1.str = argv[1]; - s1.state = NORM; - s1.lastch = OOBCH; - while (next(&s1)) - squeeze[s1.lastch] = 1; - } + /* + * tr -d [-c] string1 + * Delete all characters (or complemented characters) in string1. + */ if (dflag) { - s1.str = argv[0]; - s1.state = NORM; - s1.lastch = OOBCH; - while (next(&s1)) - tab[s1.lastch] = 1; - if (cflag) - for (tp = tab, indx = 0; indx < NCHARS; ++tp, ++indx) - *tp = !*tp; - if (sflag) - for (lastch = OOBCH; (ch = getchar()) != EOF;) { - if (tab[ch] || (squeeze[ch] && lastch == ch)) - continue; + if (isstring2) + usage(); + + setup(string1, argv[0], &s1, cflag); + + while ((ch = getchar()) != EOF) + if (!string1[ch]) + (void)putchar(ch); + exit(0); + } + + /* + * tr -s [-c] string1 + * Squeeze all characters (or complemented characters) in string1. + */ + if (sflag && !isstring2) { + setup(string1, argv[0], &s1, cflag); + + for (lastch = OOBCH; (ch = getchar()) != EOF;) + if (!string1[ch] || lastch != ch) { lastch = ch; - putchar(ch); - } - else - while ((ch = getchar()) != EOF) - if (!tab[ch]) - putchar(ch); - } else { - s1.str = argv[0]; - s2.str = argv[1]; - s1.state = s2.state = NORM; - s1.lastch = s2.lastch = OOBCH; - if (cflag) { - /* - * if cflag is set, tr just pretends it only got one - * character in string2. As reasonable as anything - * else. Should really be an error. - */ - while (next(&s2)); - lastch = s2.lastch; - for (tp = tab, indx = 0; indx < NCHARS; ++tp, ++indx) - *tp = lastch; - while (next(&s1)) - tab[s1.lastch] = s1.lastch; - } else { - for (tp = tab, indx = 0; indx < NCHARS; ++tp, ++indx) - *tp = indx; - while (next(&s1)) { - (void)next(&s2); - tab[s1.lastch] = s2.lastch; + (void)putchar(ch); } + exit(0); + } + + /* + * tr [-cs] string1 string2 + * Replace all characters (or complemented characters) in string1 with + * the character in the same position in string2. If the -s option is + * specified, squeeze all the characters in string2. + */ + if (!isstring2) + usage(); + + s1.str = argv[0]; + s2.str = argv[1]; + + if (cflag) + for (cnt = NCHARS, p = string1; cnt--;) + *p++ = OOBCH; + + if (!next(&s2)) + err("empty string2"); + + /* If string2 runs out of characters, use the last one specified. */ + if (sflag) + while (next(&s1)) { + string1[s1.lastch] = ch = s2.lastch; + string2[ch] = 1; + (void)next(&s2); + } + else + while (next(&s1)) { + string1[s1.lastch] = ch = s2.lastch; + (void)next(&s2); } - if (sflag) - for (lastch = OOBCH; (ch = getchar()) != EOF;) { - ch = tab[ch]; - if (squeeze[ch] && lastch == ch) - continue; + + if (cflag) + for (cnt = 0, p = string1; cnt < NCHARS; ++p, ++cnt) + *p = *p == OOBCH ? ch : cnt; + + if (sflag) + for (lastch = OOBCH; (ch = getchar()) != EOF;) { + ch = string1[ch]; + if (!string2[ch] || lastch != ch) { lastch = ch; - putchar(ch); + (void)putchar(ch); } - else - while ((ch = getchar()) != EOF) - putchar((int)tab[ch]); - } - exit(0); + } + else + while ((ch = getchar()) != EOF) + (void)putchar(string1[ch]); + exit (0); } -next(s) - register STR *s; +static void +setup(string, arg, str, cflag) + int *string; + char *arg; + STR *str; + int cflag; { - register int ch; - - if (s->state == EOS) - return(0); - if (s->state == INRANGE) { - if (++s->lastch == s->endrange) - s->state = NORM; - return(1); - } - if (!(ch = *s->str++)) { - s->state = EOS; - return(0); - } - if (ch == '\\') { /* \### */ - s->lastch = tran(s); - return(1); - } - if (ch == '-') { /* ranges */ - if (s->lastch == OOBCH) /* "-a" */ - goto fail2; - if (!(ch = *s->str++)) /* "a-" */ - goto fail1; - if (ch == '\\') /* \### */ - ch = tran(s); - if (s->lastch > ch) { /* "z-a" */ -fail1: --s->str; -fail2: s->lastch = '-'; - return(1); - } - if (s->lastch == ch) /* "a-a" */ - return(next(s)); - s->state = INRANGE; /* "a-z" */ - s->endrange = ch; - return(1); - } - s->lastch = ch; - return(1); + register int cnt, *p; + + str->str = arg; + bzero(string, NCHARS * sizeof(int)); + while (next(str)) + string[str->lastch] = 1; + if (cflag) + for (p = string, cnt = NCHARS; cnt--; ++p) + *p = !*p; } -/* - * Translate \-escapes. Up to 3 octal digits => char; no digits => literal. - * Unadorned backslash "\" is like \000. - */ -tran(s) - register STR *s; +static void +usage() { - register int ch, cnt = 0, val = 0; + (void)fprintf(stderr, "usage: tr [-cs] string1 string2\n"); + (void)fprintf(stderr, " tr [-c] -d string1\n"); + (void)fprintf(stderr, " tr [-c] -s string1\n"); + (void)fprintf(stderr, " tr [-c] -ds string1 string2\n"); + exit(1); +} - for (;;) { - ch = *s->str++; - if (!isascii(ch) || !isdigit(ch) || ++cnt > 3) - break; - val = val * 8 + ch - '0'; - } - if (cnt || ch == 0) - s->str--; - return (cnt ? val : ch); +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "tr: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(1); + /* NOTREACHED */ } diff --git a/usr.bin/tset/set.c b/usr.bin/tset/set.c index b6a1851282..4e00ae7358 100644 --- a/usr.bin/tset/set.c +++ b/usr.bin/tset/set.c @@ -32,7 +32,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)set.c 5.2 (Berkeley) 12/24/91"; +static char sccsid[] = "@(#)set.c 5.3 (Berkeley) 12/1/92"; #endif /* not lint */ #include <termios.h> diff --git a/usr.bin/tset/term.c b/usr.bin/tset/term.c index 403eb2d795..d1ee0168c7 100644 --- a/usr.bin/tset/term.c +++ b/usr.bin/tset/term.c @@ -32,7 +32,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)term.c 5.1 (Berkeley) 12/22/91"; +static char sccsid[] = "@(#)term.c 5.5 (Berkeley) 5/11/92"; #endif /* not lint */ #include <sys/types.h> @@ -59,7 +59,7 @@ get_termcap_entry(userarg, tcapbufp) { struct ttyent *t; int rval; - char *base, *ttype, *ttypath; + char *p, *ttype, *ttypath; if (userarg) { ttype = userarg; @@ -72,11 +72,11 @@ get_termcap_entry(userarg, tcapbufp) /* Try ttyname(3); check for dialup or other mapping. */ if (ttypath = ttyname(STDERR_FILENO)) { - if (base = rindex(ttypath, '/')) - ++base; + if (p = rindex(ttypath, '/')) + ++p; else - base = ttypath; - if ((t = getttynam(base))) { + p = ttypath; + if ((t = getttynam(p))) { ttype = t->ty_type; goto map; } @@ -88,18 +88,22 @@ get_termcap_entry(userarg, tcapbufp) map: ttype = mapped(ttype); /* - * Remove TERMCAP from the environment so we get a real entry from - * /etc/termcap. This prevents us from being fooled by out of date - * stuff in the environment. + * If not a path, remove TERMCAP from the environment so we get a + * real entry from /etc/termcap. This prevents us from being fooled + * by out of date stuff in the environment. */ -found: unsetenv("TERMCAP"); +found: if ((p = getenv("TERMCAP")) != NULL && *p != '/') + unsetenv("TERMCAP"); /* * ttype now contains a pointer to the type of the terminal. * If the first character is '?', ask the user. */ if (ttype[0] == '?') - ttype = askuser(ttype + 1); + if (ttype[1] != '\0') + ttype = askuser(ttype + 1); + else + ttype = askuser(NULL); /* Find the termcap entry. If it doesn't exist, ask the user. */ while ((rval = tgetent(tbuf, ttype)) == 0) { @@ -121,6 +125,11 @@ askuser(dflt) static char answer[256]; char *p; + /* We can get recalled; if so, don't continue uselessly. */ + if (feof(stdin) || ferror(stdin)) { + (void)fprintf(stderr, "\n"); + exit(1); + } for (;;) { if (dflt) (void)fprintf(stderr, "Terminal type? [%s] ", dflt); @@ -128,11 +137,19 @@ askuser(dflt) (void)fprintf(stderr, "Terminal type? "); (void)fflush(stderr); - if (fgets(answer, sizeof(answer), stdin) == NULL) - continue; + if (fgets(answer, sizeof(answer), stdin) == NULL) { + if (dflt == NULL) { + (void)fprintf(stderr, "\n"); + exit(1); + } + return (dflt); + } if (p = index(answer, '\n')) *p = '\0'; - return (answer[0] ? answer : dflt); + if (answer[0]) + return (answer); + if (dflt != NULL) + return (dflt); } } diff --git a/usr.bin/tset/tset.1 b/usr.bin/tset/tset.1 index a292b1dc05..c3cb74645e 100644 --- a/usr.bin/tset/tset.1 +++ b/usr.bin/tset/tset.1 @@ -29,9 +29,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)tset.1 6.10 (Berkeley) 12/24/91 +.\" @(#)tset.1 6.11 (Berkeley) 12/28/91 .\" -.Dd December 24, 1991 +.Dd December 28, 1991 .Dt TSET 1 .Os BSD 4 .Sh NAME @@ -39,7 +39,7 @@ .Nd terminal initialization .Sh SYNOPSIS .Nm tset -.Op Fl IQrs +.Op Fl IQrSs .Op Fl .Op Fl e Ar ch .Op Fl i Ar ch @@ -48,7 +48,7 @@ .Op Ar terminal .br .Nm reset -.Op Fl IQrs +.Op Fl IQrSs .Op Fl .Op Fl e Ar ch .Op Fl i Ar ch @@ -130,16 +130,20 @@ Set the line kill character to .It Fl m Specify a mapping from a port type to a terminal. See below for more information. +.It Fl Q +Don't display any values for the erase, interrupt and line kill characters. .It Fl r Print the terminal type to the standard error output. +.It Fl S +Print the terminal type and the termcap entry to the standard output. +See the section below on setting the environment for details. .It Fl s Print the sequence of shell commands to initialize the environment variables .Ev TERM and .Ev TERMCAP to the standard output. -.It Fl Q -Don't display any values for the erase, interrupt and line kill characters. +See the section below on setting the environment for details. .El .Pp The arguments for the @@ -154,22 +158,40 @@ notation, i.e. control-h may be specified as or .Dq Li ^h . .Sh SETTING THE ENVIRONMENT -It is often desirable to set the terminal type and information about the -terminal's capabilities in the shell's environment. -This is done with the +It is often desirable to enter the terminal type and information about +the terminal's capabilities into the shell's environment. +This is done using the +.Fl S +and .Fl s -option; when this option is specified, the commands to enter the information -into the shell's environment are output to the standard output. +options. +.Pp +When the +.Fl S +option is specified, the terminal type and the termcap entry are written +to the standard output, separated by a space and without a terminating +newline. +This can be assigned to an array by +.Nm csh +and +.Nm ksh +users and then used like any other shell array. +.Pp +When the +.Fl s +option is specified, the commands to enter the information into the +shell's environment are written to the standard output. If the .Ev SHELL -environmental variable ends in ``csh'', the output commands are for the -.Xr csh 1 , +environmental variable ends in ``csh'', the commands are for the +.Nm csh , otherwise, they are for -.Xr sh 1 . -Note, the output commands for the +.Xr sh . +Note, the .Nm csh -set and unset the shell variable -.Dq noglob . +commands set and unset the shell variable +.Dq noglob , +leaving it unset. The following line in the .Pa .login or @@ -178,6 +200,20 @@ files will initialize the environment correctly: .Bd -literal -offset indent eval \`tset -s options ... \` .Ed +.Pp +To demonstrate a simple use of the +.Fl S +option, the following lines in the +.Pa .login +file have an equivalent effect: +.Bd -literal -offset indent +set noglob +set term=(`tset -S options ...`) +setenv TERM $term[1] +setenv TERMCAP "$term[2]" +unset term +unset noglob +.Ed .Sh TERMINAL TYPE MAPPING When the terminal is not hardwired into the system (or the current system information is incorrect) the terminal type derived from the @@ -244,7 +280,7 @@ If the port type and baud rate match the mapping, the terminal type specified in the mapping replaces the current type. If more than one mapping is specified, the first applicable mapping is used. .Pp -For example, consider the following: +For example, consider the following mapping: .Dq Li dialup>9600:vt100 . The port type is .Dq Li dialup , @@ -315,7 +351,6 @@ The .Fl A , .Fl E , .Fl h , -.Fl S , .Fl u and .Fl v diff --git a/usr.bin/tset/tset.c b/usr.bin/tset/tset.c index d55fb31d3d..4966b049dd 100644 --- a/usr.bin/tset/tset.c +++ b/usr.bin/tset/tset.c @@ -38,10 +38,11 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)tset.c 5.19 (Berkeley) 12/24/91"; +static char sccsid[] = "@(#)tset.c 5.22 (Berkeley) 2/11/93"; #endif /* not lint */ #include <sys/types.h> +#include <sys/ioctl.h> #include <termios.h> #include <errno.h> #include <unistd.h> @@ -57,16 +58,10 @@ void usage __P((void)); struct termios mode, oldmode; -int dosetenv; /* output TERMCAP strings */ int erasechar; /* new erase character */ int intrchar; /* new interrupt character */ int isreset; /* invoked as reset */ int killchar; /* new kill character */ -int noinit; /* don't output initialization string */ -int noset; /* only report term type */ -int quiet; /* don't display ctrl key settings */ -int showterm; /* display term on stderr */ - int lines, columns; /* window size */ int @@ -77,7 +72,7 @@ main(argc, argv) #ifdef TIOCGWINSZ struct winsize win; #endif - int ch, csh, usingupper; + int ch, noinit, noset, quiet, Sflag, sflag, showterm, usingupper; char savech, *p, *t, *tcapbuf, *ttype; if (tcgetattr(STDERR_FILENO, &mode) < 0) @@ -97,9 +92,10 @@ main(argc, argv) } obsolete(argv); - while ((ch = getopt(argc, argv, "-a:d:e:Ii:k:m:np:Qrs")) != EOF) { + noinit = noset = quiet = Sflag = sflag = showterm = 0; + while ((ch = getopt(argc, argv, "-a:d:e:Ii:k:m:np:QSrs")) != EOF) { switch (ch) { - case '-': /* OBSOLETE: display term only */ + case '-': /* display term only */ noset = 1; break; case 'a': /* OBSOLETE: map identifier to type */ @@ -113,7 +109,7 @@ main(argc, argv) optarg[1] == '?' ? '\177' : CTRL(optarg[1]) : optarg[0]; break; - case 'I': /* no initialization */ + case 'I': /* no initialization strings */ noinit = 1; break; case 'i': /* interrupt character */ @@ -134,14 +130,17 @@ main(argc, argv) case 'p': /* OBSOLETE: map identifier to type */ add_mapping("plugboard", optarg); break; - case 'Q': /* be quiet */ + case 'Q': /* don't output control key settings */ quiet = 1; break; + case 'S': /* output TERM/TERMCAP strings */ + Sflag = 1; + break; case 'r': /* display term on stderr */ showterm = 1; break; - case 's': /* print commands to set environment */ - dosetenv = 1; + case 's': /* output TERM/TERMCAP strings */ + sflag = 1; break; case '?': default: @@ -181,15 +180,10 @@ main(argc, argv) tcsetattr(STDERR_FILENO, TCSADRAIN, &mode); } - /* - * The termcap file generally has a two-character name first in each - * entry followed by more descriptive names. If we ended up with the - * first one, we switch to the second one for setting or reporting - * information. - */ - p = strpbrk(tcapbuf, "|:"); - if (p && *p != ':' && !strncmp(ttype, tcapbuf, p - tcapbuf)) { - t = ++p; + /* Get the terminal name from the entry. */ + p = tcapbuf; + if (p != NULL && *p != ':') { + t = p; if (p = strpbrk(p, "|:")) { savech = *p; *p = '\0'; @@ -215,24 +209,29 @@ main(argc, argv) } } - if (!dosetenv) - exit(0); + if (Sflag) { + (void)printf("%s ", ttype); + wrtermcap(tcapbuf); + } + + if (sflag) { + /* + * Figure out what shell we're using. A hack, we look for an + * environmental variable SHELL ending in "csh". + */ + if ((p = getenv("SHELL")) && + !strcmp(p + strlen(p) - 3, "csh")) { + p = "set noglob;\nsetenv TERM %s;\nsetenv TERMCAP '"; + t = "';\nunset noglob;\n"; + } else { + p = "TERM=%s;\nTERMCAP='"; + t = "';\nexport TERMCAP TERM;\n"; + } + (void)printf(p, ttype); + wrtermcap(tcapbuf); + (void)printf(t); + } - /* - * Figure out what shell we're using. A hack, we look for a $SHELL - * ending in "csh". - */ - csh = (p = getenv("SHELL")) && !strcmp(p + strlen(p) - 3, "csh"); - if (csh) - (void)printf("set noglob;\nsetenv TERM %s;\nsetenv TERMCAP '", - ttype); - else - (void)printf("TERM=%s;\nTERMCAP='", ttype); - wrtermcap(tcapbuf); - if (csh) - (void)printf("';\nunset noglob;\n"); - else - (void)printf("';\nexport TERMCAP TERM;\n"); exit(0); } @@ -299,6 +298,6 @@ void usage() { (void)fprintf(stderr, -"usage: tset [-IQrs] [-] [-e ch] [-i ch] [-k ch] [-m mapping] [terminal]\n"); +"usage: tset [-IQrSs] [-] [-e ch] [-i ch] [-k ch] [-m mapping] [terminal]\n"); exit(1); } diff --git a/usr.bin/uname/Makefile b/usr.bin/uname/Makefile new file mode 100644 index 0000000000..c538d74b53 --- /dev/null +++ b/usr.bin/uname/Makefile @@ -0,0 +1,6 @@ +# from: @(#)Makefile 5.3 (Berkeley) 5/11/90 +# $Id: Makefile,v 1.2 1993/07/30 23:49:50 mycroft Exp $ + +PROG= uname + +.include <bsd.prog.mk> diff --git a/usr.bin/uname/uname.1 b/usr.bin/uname/uname.1 new file mode 100644 index 0000000000..eeb1ed8ac3 --- /dev/null +++ b/usr.bin/uname/uname.1 @@ -0,0 +1,85 @@ +.\" Copyright (c) 1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)du.1 6.13 (Berkeley) 6/20/91 +.\" $Id: uname.1,v 1.3 1993/08/28 03:20:25 jtc Exp $ +.\" +.Dd March 29, 1992 +.Dt UNAME 1 +.Os +.Sh NAME +.Nm uname +.Nd Print name of current UNIX system +.Sh SYNOPSIS +.Nm uname +.Op Fl amnsrv +.Sh DESCRIPTION +The +.Nm uname +prints the current system name of the UNIX system on +the standard output file. It is mainly useful to determine +which system one is using. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl a +Behave as though all of the options +.Fl mnrsv +were specified. +.It Fl m +print the machine hardware name. +.It Fl n +print the nodename (the nodename may be a name +that the system is known by to a communications +network). +.It Fl s +print the operating system name. +.It Fl r +print the operating system release. +.It Fl v +print the operating system version. +.El +.Pp +If no options are specified, +.Nm uname +prints the operating system name as if the +.Fl s +option had been specified. +.Sh SEE ALSO +.Xr hostname 1 , +.Xr machine 1 , +.Xr uname 2 +.Sh STANDARDS +The +.Nm uname +utility conforms to +.St -p1003.2-92 . + diff --git a/usr.bin/uname/uname.c b/usr.bin/uname/uname.c new file mode 100644 index 0000000000..6de43caa14 --- /dev/null +++ b/usr.bin/uname/uname.c @@ -0,0 +1,80 @@ +/* + * uname - print system information. Jeff Comstock - Bloomington, MN USA 1992 + * Usage: uname [-asnrvm] + * -s prints system name + * -n prints nodename + * -r prints software release + * -v prints os version + * -m prints machine name + * -a prinst all the above information + */ + +#ifndef lint +static char rcsid[] = "$Id: uname.c,v 1.3 1993/08/07 07:58:19 cgd Exp $"; +#endif /* not lint */ + +#include <stdio.h> +#include <unistd.h> +#include <sysexits.h> +#include <sys/utsname.h> + +#define SYSNAME 0 +#define NODENAME 1 +#define RELEASE 2 +#define VERSION 3 +#define MACHINE 4 + +struct utsname u; + +struct utstab { + char *str; + int requested; +} uttab[] = { + { u.sysname, 0 }, + { u.nodename, 0 }, + { u.release, 0 }, + { u.version, 0 }, + { u.machine, 0 } +}; + +main(int argc, char **argv) { +char *opts="amnrsv"; +register int c,space, all=0; + + if ( ! uname(&u) ) { + if ( argc == 1 ) { + puts(u.sysname); + } else { + while ( (c = getopt(argc,argv,opts)) != -1 ) { + switch ( c ) { + case 'a' : all++; + break; + case 'm' : uttab[MACHINE].requested++; + break; + case 'n' : uttab[NODENAME].requested++; + break; + case 'r' : uttab[RELEASE].requested++; + break; + case 's' : uttab[SYSNAME].requested++; + break; + case 'v' : uttab[VERSION].requested++; + break; + } + } + space=0; + for(c=0; c <= MACHINE; c++) { + if ( uttab[c].requested || all ) { + if ( space ) + putchar(' '); + printf("%s", uttab[c].str); + space++; + } + } + puts(""); + } + exit (EX_OK); + } else { + perror("uname"); + exit (EX_OSERR); + } +} diff --git a/usr.bin/uniq/uniq.1 b/usr.bin/uniq/uniq.1 index 27c1efcfb7..b4e8398d76 100644 --- a/usr.bin/uniq/uniq.1 +++ b/usr.bin/uniq/uniq.1 @@ -32,14 +32,14 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)uniq.1 6.4 (Berkeley) 6/27/91 +.\" @(#)uniq.1 6.5 (Berkeley) 1/9/92 .\" -.Dd June 27, 1991 +.Dd January 9, 1992 .Dt UNIQ .Os .Sh NAME .Nm uniq -.Nd Report or filter out repeated lines in a file. +.Nd report or filter out repeated lines in a file .Sh SYNOPSIS .Nm uniq .Op Fl c | Fl d | Fl u @@ -49,118 +49,82 @@ .Ar input_file .Op Ar output_file .Oc -.Pp -Deprecated Version: -.Pp -.Nm uniq -.Op Fl cdu -.Op Fl Ns Ar n -.Op Cm \(pl Ns Ar n -.Oo -.Ar input_file -.Op Ar output_file -.Oc .Sh DESCRIPTION The .Nm uniq -utility reads an input file or the standard input -comparing adjacent -lines, and writes one copy of each input line on the output. -The second and succeeding copies of repeated adjacent input -lines are not written. -Repeated lines in the input are not detected if they are -not adjacent, so it is important to -.Xr sort 1 -the files first. +utility reads the standard input comparing adjacent lines, and writes +a copy of each unique input line to the standard output. +The second and succeeding copies of identical adjacent input lines are +not written. +Repeated lines in the input will not be detected if they are not adjacent, +so it may be necessary to sort the files first. .Pp The following options are available: .Bl -tag -width Ds .It Fl c -Precede each output line with a count of the number -of times the line occurred in the input. For example: -.Bd -literal -offset indent -duplicate_count line_number -.Ed -.Pp -where the duplicate count is a blank padded field of -up to four digits followed by a space. +Precede each output line with the count of the number of times the line +occurred in the input, followed by a single space. .It Fl d -Suppress the writing of lines that are not repeated -in the input. +Don't output lines that are not repeated in the input. .It Fl f Ar fields -Ignore the first fields on each input line when -doing comparisons, where fields is a positive -decimal integer. -A field is a string of non-blank -characters separated from adjacent fields +Ignore the first +.Ar fields +in each input line when doing comparisons. +A field is a string of non-blank characters separated from adjacent fields by blanks. +Field numbers are one based, i.e. the first field is field one. .It Fl s Ar chars -Ignore the first chars characters when doing -comparisons, where chars is a positive decimal -integer. +Ignore the first +.Ar chars +characters in each input line when doing comparisons. If specified in conjunction with the .Fl f -option, the first chars characters after the first -fields fields will be ignored. +option, the first +.Ar chars +characters after the first +.Ar fields +fields will be ignored. +Character numbers are one based, i.e. the first character is character one. .It Fl u -Suppress the writing of lines that are repeated in -the input. -.It Fl Ns Ar n -(Deprecated; replaced by -.Fl f ) . -Ignore the first n -fields on each input line when doing comparisons, -where n is a number. -A field is a string of non-blank -characters separated from adjacent fields -by blanks. -.It Cm \&\(pl Ns Ar n -(Deprecated; replaced by -.Fl s ) . -Ignore the first -.Ar m -characters when doing comparisons, where -.Ar m -is a -number. +Don't output lines that are repeated in the input. +.\".It Fl Ns Ar n +.\"(Deprecated; replaced by +.\".Fl f ) . +.\"Ignore the first n +.\"fields on each input line when doing comparisons, +.\"where n is a number. +.\"A field is a string of non-blank +.\"characters separated from adjacent fields +.\"by blanks. +.\".It Cm \&\(pl Ns Ar n +.\"(Deprecated; replaced by +.\".Fl s ) . +.\"Ignore the first +.\".Ar m +.\"characters when doing comparisons, where +.\".Ar m +.\"is a +.\"number. .El .Pp -The following operands are available: -.Bl -tag -width output_filex -.It Ar input_file -A pathname of the input file. -If the -.Ar input_file -operand is not specified, the standard input is -used. -.It Ar output_file -A pathname of the output file. -This name shall -always be different from -.Ar input_file. -If the -.Ar output_file -operand is not specified, the standard -output is used. If -.Ar output_file -is created and an error occurs or a sugnal is caught the -.Ar output_file -is not removed. -.El -.\" .Pp -.\" The following environment variables affect the execution of -.\" uniq: -.\" .Tl Em LC_CTYPE -.\" The locale for character classification, used to -.\" determine the characters constituting a blank in -.\" the current locale. +If additional arguments are specified on the command line, the first +such argument is used as the name of an input file, the second is used +as the name of an output file. .Pp The -.Xr uniq +.Nm uniq utility exits 0 on success, and >0 if an error occurs. +.Sh COMPATIBILITY +The historic +.Cm \&\(pl Ns Ar number +and +.Fl Ns Ar number +options have been deprecated but are still supported in this implementation. +.Sh SEE ALSO +.Xr sort 1 .Sh STANDARDS The -.Xr uniq +.Nm uniq utility is expected to be .St -p1003.2 compatible. diff --git a/usr.bin/uniq/uniq.c b/usr.bin/uniq/uniq.c index 90cafbb539..f0f71137a4 100644 --- a/usr.bin/uniq/uniq.c +++ b/usr.bin/uniq/uniq.c @@ -41,28 +41,39 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)uniq.c 5.2 (Berkeley) 6/1/90"; +static char sccsid[] = "@(#)uniq.c 5.4 (Berkeley) 1/9/92"; #endif /* not lint */ +#include <errno.h> #include <stdio.h> #include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#define MAXLINELEN (8 * 1024) int cflag, dflag, uflag; int numchars, numfields, repeats; -#define MAXLINELEN (2048 + 1) +void err __P((const char *, ...)); +FILE *file __P((char *, char *)); +void show __P((FILE *, char *)); +char *skip __P((char *)); +void obsolete __P((char *[])); +void usage __P((void)); -main (argc,argv) +int +main (argc, argv) int argc; - char **argv; + char *argv[]; { - extern int optind; - FILE *ifp, *ofp, *file(); - int ch; register char *t1, *t2; - char *prevline, *thisline, *malloc(), *skip(); + FILE *ifp, *ofp; + int ch; + char *prevline, *thisline, *p; - while ((ch = getopt(argc, argv, "-cdu123456789")) != EOF) + obsolete(argv); + while ((ch = getopt(argc, argv, "-cdf:s:u")) != EOF) switch (ch) { case '-': --optind; @@ -73,18 +84,19 @@ main (argc,argv) case 'd': dflag = 1; break; + case 'f': + numfields = strtol(optarg, &p, 10); + if (numfields < 0 || *p) + err("illegal field skip value: %s", optarg); + break; + case 's': + numchars = strtol(optarg, &p, 10); + if (numchars < 0 || *p) + err("illegal character skip value: %s", optarg); + break; case 'u': uflag = 1; break; - /* - * since -n is a valid option that could be picked up by - * getopt, but is better handled by the +n and -n code, we - * break out. - */ - case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - --optind; - goto done; case '?': default: usage(); @@ -93,29 +105,13 @@ main (argc,argv) done: argc -= optind; argv +=optind; - /* if no flags are set, default is -d -u */ + /* If no flags are set, default is -d -u. */ if (cflag) { if (dflag || uflag) usage(); } else if (!dflag && !uflag) dflag = uflag = 1; - /* because of the +, getopt is messed up */ - for (; **argv == '+' || **argv == '-'; ++argv, --argc) - switch (**argv) { - case '+': - if ((numchars = atoi(*argv + 1)) < 0) - goto negerr; - break; - case '-': - if ((numfields = atoi(*argv + 1)) < 0) { -negerr: (void)fprintf(stderr, - "uniq: negative field/char skip value.\n"); - usage(); - } - break; - } - switch(argc) { case 0: ifp = stdin; @@ -135,10 +131,12 @@ negerr: (void)fprintf(stderr, prevline = malloc(MAXLINELEN); thisline = malloc(MAXLINELEN); - (void)fgets(prevline, MAXLINELEN, ifp); + if (fgets(prevline, MAXLINELEN, ifp) == (char *) NULL) { + exit(0); + } while (fgets(thisline, MAXLINELEN, ifp)) { - /* if requested get the chosen fields + character offsets */ + /* If requested get the chosen fields + character offsets. */ if (numfields || numchars) { t1 = skip(thisline); t2 = skip(prevline); @@ -147,15 +145,14 @@ negerr: (void)fprintf(stderr, t2 = prevline; } - /* if different, print; set previous to new value */ + /* If different, print; set previous to new value. */ if (strcmp(t1, t2)) { show(ofp, prevline); t1 = prevline; prevline = thisline; thisline = t1; repeats = 0; - } - else + } else ++repeats; } show(ofp, prevline); @@ -164,9 +161,10 @@ negerr: (void)fprintf(stderr, /* * show -- - * output a line depending on the flags and number of repetitions + * Output a line depending on the flags and number of repetitions * of the line. */ +void show(ofp, str) FILE *ofp; char *str; @@ -201,16 +199,74 @@ file(name, mode) { FILE *fp; - if (!(fp = fopen(name, mode))) { - (void)fprintf(stderr, "uniq: can't open %s.\n", name); - exit(1); - } + if ((fp = fopen(name, mode)) == NULL) + err("%s: %s", name, strerror(errno)); return(fp); } +void +obsolete(argv) + char *argv[]; +{ + int len; + char *ap, *p, *start; + + while (ap = *++argv) { + /* Return if "--" or not an option of any form. */ + if (ap[0] != '-') { + if (ap[0] != '+') + return; + } else if (ap[1] == '-') + return; + if (!isdigit(ap[1])) + continue; + /* + * Digit signifies an old-style option. Malloc space for dash, + * new option and argument. + */ + len = strlen(ap); + if ((start = p = malloc(len + 3)) == NULL) + err("%s", strerror(errno)); + *p++ = '-'; + *p++ = ap[0] == '+' ? 's' : 'f'; + (void)strcpy(p, ap + 1); + *argv = start; + } +} + +void usage() { (void)fprintf(stderr, - "usage: uniq [-c | -du] [- #fields] [+ #chars] [input [output]]\n"); + "usage: uniq [-c | -du] [-f fields] [-s chars] [input [output]]\n"); + exit(1); +} + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "uniq: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); exit(1); + /* NOTREACHED */ } diff --git a/usr.bin/uuencode/Makefile b/usr.bin/uuencode/Makefile index cedd7bff83..a81a05b898 100644 --- a/usr.bin/uuencode/Makefile +++ b/usr.bin/uuencode/Makefile @@ -1,8 +1,8 @@ # @(#)Makefile 5.1 (Berkeley) 5/11/90 PROG= uuencode -MAN1= uuencode.0 -MAN5= uuencode.format.0 +MAN1= uuencode.1 +MAN5= uuencode.format.5 MLINKS= uuencode.1 uudecode.1 .include <bsd.prog.mk> diff --git a/usr.bin/vacation/vacation.c b/usr.bin/vacation/vacation.c index 5a1b95036d..b60da2e071 100644 --- a/usr.bin/vacation/vacation.c +++ b/usr.bin/vacation/vacation.c @@ -147,8 +147,8 @@ main(argc, argv) exit(1); } - db = hash_open(VDB, O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0), - S_IRUSR|S_IWUSR, (HASHINFO *)NULL); + db = dbopen(VDB, O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0), + S_IRUSR|S_IWUSR, DB_HASH, (HASHINFO *)NULL); if (!db) { syslog(LOG_NOTICE, "vacation: %s: %s\n", VDB, strerror(errno)); exit(1); @@ -345,7 +345,7 @@ setinterval(interval) key.size = sizeof(VIT); data.data = &interval; data.size = sizeof(interval); - (void)(db->put)(db, &key, &data, R_PUT); + (void)(db->put)(db, &key, &data, 0); } /* @@ -362,7 +362,7 @@ setreply() (void)time(&now); data.data = &now; data.size = sizeof(now); - (void)(db->put)(db, &key, &data, R_PUT); + (void)(db->put)(db, &key, &data, 0); } /* diff --git a/usr.bin/vgrind/Makefile b/usr.bin/vgrind/Makefile index fa7dde8bf2..c1cf27a449 100644 --- a/usr.bin/vgrind/Makefile +++ b/usr.bin/vgrind/Makefile @@ -2,8 +2,8 @@ PROG= vfontedpr SRCS= regexp.c vfontedpr.c vgrindefs.c -MAN1= vgrind.0 -MAN5= vgrindefs.0 +MAN1= vgrind.1 +MAN5= vgrindefs.5 BINDIR= /usr/libexec beforeinstall: diff --git a/usr.bin/vgrind/vgrind.1 b/usr.bin/vgrind/vgrind.1 index 4e4a796b26..b517e1a40b 100644 --- a/usr.bin/vgrind/vgrind.1 +++ b/usr.bin/vgrind/vgrind.1 @@ -54,7 +54,7 @@ .Nm Vgrind formats the program sources which are arguments in a nice style using -.Xr troff 1 +.Xr troff 1 . Comments are placed in italics, keywords in bold face, and the name of the current function is listed down the margin of each page as it is encountered. @@ -175,7 +175,7 @@ language descriptions .El .Sh SEE ALSO .Xr vlp 1 , -.Xr vtroff 1 , +.Xr troff 1 , .Xr vgrindefs 5 .Sh BUGS Vfontedpr assumes that a certain programming style is followed: diff --git a/usr.bin/vgrind/vgrind.sh b/usr.bin/vgrind/vgrind.sh index d7fa30ba4d..7e9fedfb30 100644 --- a/usr.bin/vgrind/vgrind.sh +++ b/usr.bin/vgrind/vgrind.sh @@ -116,10 +116,10 @@ if (-r index) then else if ("$head" != "") then $vf $options -h "$head" $files | \ - sh -c "vtroff -rx1 $voptions -i -mvgrind 2>> xindex" + sh -c "troff -rx1 $voptions -i -mvgrind 2>> xindex" else $vf $options $files | \ - sh -c "vtroff -rx1 $voptions -i -mvgrind 2>> xindex" + sh -c "troff -rx1 $voptions -i -mvgrind 2>> xindex" endif endif sort -df +0 -2 xindex >index @@ -133,9 +133,9 @@ else endif else if ("$head" != "") then - $vf $options -h "$head" $files | vtroff -i $voptions -mvgrind + $vf $options -h "$head" $files | troff -i $voptions -mvgrind else - $vf $options $files | vtroff -i $voptions -mvgrind + $vf $options $files | troff -i $voptions -mvgrind endif endif endif diff --git a/usr.bin/vgrind/vgrindefs.c b/usr.bin/vgrind/vgrindefs.c index 18b0715ed5..68dcf78741 100644 --- a/usr.bin/vgrind/vgrindefs.c +++ b/usr.bin/vgrind/vgrindefs.c @@ -57,9 +57,9 @@ static char sccsid[] = "@(#)vgrindefs.c 5.3 (Berkeley) 6/1/90"; static char *tbuf; static char *filename; static int hopcount; /* detect infinite loops in termcap, init 0 */ -char *tskip(); +static char *tskip(); char *tgetstr(); -char *tdecode(); +static char *tdecode(); char *getenv(); /* diff --git a/usr.bin/vmstat/Makefile b/usr.bin/vmstat/Makefile index a344ce37b6..520bb0210c 100644 --- a/usr.bin/vmstat/Makefile +++ b/usr.bin/vmstat/Makefile @@ -2,7 +2,7 @@ PROG= vmstat CFLAGS+=-I/sys -MAN8= vmstat.0 +MAN8= vmstat.8 BINGRP= kmem BINMODE=2555 DPADD= names.c ${LIBUTIL} diff --git a/usr.bin/vmstat/vmstat.8 b/usr.bin/vmstat/vmstat.8 index f2e15a7d96..c17a47c9a9 100644 --- a/usr.bin/vmstat/vmstat.8 +++ b/usr.bin/vmstat/vmstat.8 @@ -78,7 +78,7 @@ instead of the default ``/dev/kmem''. .TP \-N Extract the name list from the specified system instead of the default -``/vmunix''. +``/386bsd''. .TP \-m Report on the usage of kernel dynamic memory listed first by size of @@ -188,7 +188,7 @@ Others vary every second and running the output for a while will make it apparent which are recomputed every second. .SH FILES .ta \w'/dev/kmem 'u -/vmunix default kernel namelist +/386bsd default kernel namelist .br /dev/kmem default memory file .SH SEE ALSO diff --git a/usr.bin/vmstat/vmstat.c b/usr.bin/vmstat/vmstat.c index 9c6bce5c83..d65721f4de 100644 --- a/usr.bin/vmstat/vmstat.c +++ b/usr.bin/vmstat/vmstat.c @@ -745,7 +745,7 @@ cpustats() void dointr() { - register long *intrcnt, inttotal, uptime; + register unsigned long *intrcnt, inttotal, uptime; register int nintr, inamlen; register char *intrname; @@ -760,17 +760,17 @@ dointr() } kread(X_INTRCNT, intrcnt, (size_t)nintr); kread(X_INTRNAMES, intrname, (size_t)inamlen); - (void)printf("interrupt total rate\n"); + (void)printf("%-12s %10s %8s\n", "interrupt", "count", "rate"); inttotal = 0; nintr /= sizeof(long); while (--nintr >= 0) { if (*intrcnt) - (void)printf("%-12s %8ld %8ld\n", intrname, + (void)printf("%-12s %10lu %8lu\n", intrname, *intrcnt, *intrcnt / uptime); intrname += strlen(intrname) + 1; inttotal += *intrcnt++; } - (void)printf("Total %8ld %8ld\n", inttotal, inttotal / uptime); + (void)printf("%-12s %10lu %8lu\n", "Total", inttotal, inttotal / uptime); } /* @@ -807,11 +807,11 @@ domem() kread(X_KMEMSTAT, kmemstats, sizeof(kmemstats)); (void)printf("\nMemory statistics by type\n"); (void)printf( -" Type In Use MemUse HighUse Limit Requests TypeLimit KernLimit\n"); +" Type In Use MemUse HighUse Limit Requests TypeLimit KernLimit\n"); for (i = 0, ks = &kmemstats[0]; i < M_LAST; i++, ks++) { if (ks->ks_calls == 0) continue; - (void)printf("%10s %6ld %7ldK %8ldK %5ldK %8ld %6u %9u\n", + (void)printf("%11s %7ld %7ldK %8ldK %5ldK %8ld %6u %9u\n", kmemnames[i] ? kmemnames[i] : "undefined", ks->ks_inuse, (ks->ks_memuse + 1023) / 1024, (ks->ks_maxused + 1023) / 1024, diff --git a/usr.bin/w/Makefile b/usr.bin/w/Makefile index f348d27df2..ba57585c5c 100644 --- a/usr.bin/w/Makefile +++ b/usr.bin/w/Makefile @@ -3,7 +3,7 @@ PROG= w SRCS= attime.c w.c proc_compare.c CFLAGS+=-I/sys -MAN1= w.0 uptime.0 +MAN1= w.1 uptime.1 DPADD= ${LIBUTIL} LDADD= -lutil BINGRP= kmem diff --git a/usr.bin/w/uptime.1 b/usr.bin/w/uptime.1 index 6feb2191bd..6a686564f8 100644 --- a/usr.bin/w/uptime.1 +++ b/usr.bin/w/uptime.1 @@ -47,8 +47,8 @@ It is, essentially, the first line of a .Xr w 1 command. .Sh FILES -.Bl -tag -width /vmunix -.It Pa /vmunix +.Bl -tag -width /386bsd +.It Pa /386bsd system name list .El .Sh SEE ALSO diff --git a/usr.bin/w/w.c b/usr.bin/w/w.c index ee86f2aed2..44625b0556 100644 --- a/usr.bin/w/w.c +++ b/usr.bin/w/w.c @@ -322,6 +322,8 @@ main(argc, argv) attime(&ep->utmp.ut_time)); if (ep->idle >= 36 * 60) printf(" %ddays ", (ep->idle + 12 * 60) / (24 * 60)); + else if (ep->idle == 0) + printf(" - "); else prttime(ep->idle, " "); printf("%.*s\n", argwidth, ep->args); diff --git a/usr.bin/whereis/whereis.c b/usr.bin/whereis/whereis.c index c767b74dc8..27137f6ce5 100644 --- a/usr.bin/whereis/whereis.c +++ b/usr.bin/whereis/whereis.c @@ -49,95 +49,69 @@ static char sccsid[] = "@(#)whereis.c 5.5 (Berkeley) 4/18/91"; static char *bindirs[] = { "/bin", "/sbin", - "/usr/ucb", "/usr/bin", - "/usr/sbin", - "/usr/old", - "/usr/contrib", "/usr/games", - "/usr/local", - "/usr/libexec", + "/usr/gnu", "/usr/include", - "/usr/hosts", - "/usr/share", /*?*/ - "/etc", -#ifdef notdef - /* before reorg */ + "/usr/libexec", + "/usr/sbin", + "/usr/share", + "/usr/X386/bin", + "/usr/local/bin", "/etc", - "/bin", - "/usr/bin", - "/usr/games", - "/lib", - "/usr/ucb", - "/usr/lib", - "/usr/local", - "/usr/new", - "/usr/old", - "/usr/hosts", - "/usr/include", -#endif 0 }; /* This needs to be redone - man pages live with sources */ static char *mandirs[] = { - "/usr/man/man1", - "/usr/man/man2", - "/usr/man/man3", - "/usr/man/man4", - "/usr/man/man5", - "/usr/man/man6", - "/usr/man/man7", - "/usr/man/man8", - "/usr/man/manl", - "/usr/man/mann", - "/usr/man/mano", + "/usr/share/man/man1", + "/usr/share/man/man2", + "/usr/share/man/man3", + "/usr/share/man/man4", + "/usr/share/man/man5", + "/usr/share/man/man6", + "/usr/share/man/man7", + "/usr/share/man/man8", + "/usr/X386/man/man1", + "/usr/X386/man/man2", + "/usr/X386/man/man3", + "/usr/X386/man/man4", + "/usr/X386/man/man5", + "/usr/X386/man/man6", + "/usr/X386/man/man7", + "/usr/X386/man/man8", + "/usr/X386/man/cat1", + "/usr/X386/man/cat2", + "/usr/X386/man/cat3", + "/usr/X386/man/cat4", + "/usr/X386/man/cat5", + "/usr/X386/man/cat6", + "/usr/X386/man/cat7", + "/usr/X386/man/cat8", + "/usr/local/man/man1", + "/usr/local/man/man2", + "/usr/local/man/man3", + "/usr/local/man/man4", + "/usr/local/man/man5", + "/usr/local/man/man6", + "/usr/local/man/man7", + "/usr/local/man/man8", 0 }; static char *srcdirs[] = { "/usr/src/bin", - "/usr/src/sbin", - "/usr/src/etc", - "/usr/src/pgrm", - "/usr/src/usr.bin", - "/usr/src/usr.sbin", - "/usr/src/usr.ucb", - "/usr/src/usr.new", - "/usr/src/usr.lib", - "/usr/src/libexec", - "/usr/src/libdata", - "/usr/src/share", "/usr/src/contrib", - "/usr/src/athena", - "/usr/src/devel", - "/usr/src/games", - "/usr/src/local", - "/usr/src/man", - "/usr/src/root", - "/usr/src/old", - "/usr/src/include", - /* still need libs */ -#ifdef notdef /* before reorg */ - "/usr/src/bin", - "/usr/src/usr.bin", "/usr/src/etc", - "/usr/src/ucb", "/usr/src/games", - "/usr/src/usr.lib", - "/usr/src/lib", - "/usr/src/local", - "/usr/src/new", - "/usr/src/old", + "/usr/src/gnu", "/usr/src/include", - "/usr/src/lib/libc/gen", - "/usr/src/lib/libc/stdio", - "/usr/src/lib/libc/sys", - "/usr/src/lib/libc/net/common", - "/usr/src/lib/libc/net/inet", - "/usr/src/lib/libc/net/misc", - "/usr/src/ucb/pascal", - "/usr/src/ucb/pascal/utilities", - "/usr/src/undoc", -#endif + "/usr/src/lib", + "/usr/src/libexec", + "/usr/src/sbin", + "/usr/src/share", + "/usr/src/usr.bin", + "/usr/src/usr.sbin", + "/usr/src/share/man", + "/usr/local/src", 0 }; diff --git a/usr.bin/which/Makefile b/usr.bin/which/Makefile index d7cdce698a..1e17feb112 100644 --- a/usr.bin/which/Makefile +++ b/usr.bin/which/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.5 (Berkeley) 7/1/90 -MAN1= which.0 +MAN1= which.1 beforeinstall: install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ diff --git a/usr.bin/window/Makefile b/usr.bin/window/Makefile index d4cef4c150..4019676f1b 100644 --- a/usr.bin/window/Makefile +++ b/usr.bin/window/Makefile @@ -14,7 +14,7 @@ SRCS= char.c cmd.c cmd1.c cmd2.c cmd3.c cmd4.c cmd5.c cmd6.c cmd7.c \ wwopen.c wwprintf.c wwpty.c wwputc.c wwputs.c wwredraw.c \ wwredrawwin.c wwrint.c wwscroll.c wwsize.c wwspawn.c wwsuspend.c \ wwtty.c wwunframe.c wwupdate.c wwwrite.c xx.c xxflush.c compress.c -MAN= window.0 +MAN1= window.1 DPADD= ${LIBTERMCAP} LDADD= -ltermcap diff --git a/usr.bin/window/wwinit.c b/usr.bin/window/wwinit.c index 7ed318b050..033864c003 100644 --- a/usr.bin/window/wwinit.c +++ b/usr.bin/window/wwinit.c @@ -55,9 +55,9 @@ wwinit() wwhead.ww_back = &wwhead; s = sigblock(sigmask(SIGIO)); - if (signal(SIGIO, wwrint) == BADSIG || - signal(SIGCHLD, wwchild) == BADSIG || - signal(SIGPIPE, SIG_IGN) == BADSIG) { + if (signal(SIGIO, wwrint) == SIG_ERR || + signal(SIGCHLD, wwchild) == SIG_ERR || + signal(SIGPIPE, SIG_IGN) == SIG_ERR) { wwerrno = WWE_SYS; return -1; } diff --git a/usr.bin/xargs/xargs.1 b/usr.bin/xargs/xargs.1 index cebb45dfcd..4b057a4cea 100644 --- a/usr.bin/xargs/xargs.1 +++ b/usr.bin/xargs/xargs.1 @@ -35,127 +35,138 @@ .\" .\" @(#)xargs.1 5.5 (Berkeley) 6/27/91 .\" -.TH XARGS 1 "June 27, 1991" -.UC 7 -.SH NAME -xargs \- construct argument list(s) and execute utility. -.SH SYNOPSIS -.ft B -xargs [\-ft] [[\-x] \-n number] [\-s size] [utility [argument ...]] -.ft R -.SH DESCRIPTION +.Dd June 27, 1991 +.Dt XARGS 1 +.Os +.Sh NAME +.Nm xargs +.Nd construct argument list(s) and execute utility. +.Sh SYNOPSIS +.Nm xargs +.Op Fl t +.Oo +.Op Fl x +.Fl n Ar number +.Oc +.Op Fl s Ar size +.Oo +.Ar utility +.Op Ar argument Ar ... +.Oc +.Sh DESCRIPTION The -.I xargs +.Nm xargs utility reads space, tab, newline and end-of-file delimited arguments from the standard input and executes the specified -.I utility +.Ar utility with them as arguments. -.PP +.Pp The utility and any arguments specified on the command line are given to the -.I utility +.Ar utility upon each invocation, followed by some number of the arguments read from standard input. The -.I utility +.Ar utility is repeatedly executed until standard input is exhausted. -.PP -Spaces, tabs and newlines may be embedded in arguments using single (`` ' '') -or double (``"'') quotes or backslashes (``\e''). +.Pp +Spaces, tabs and newlines may be embedded in arguments using single +or double quotes or backslashes. Single quotes escape all non-single quote characters, excluding newlines, up to the matching single quote. Double quotes escape all non-double quote characters, excluding newlines, up to the matching double quote. Any single character, including newlines, may be escaped by a backslash. -.PP +.Pp The options are as follows: -.TP -\-f -Force -.I xargs -to ignore the exit status returned by -.IR utility . -By default, -.I xargs -will exit immediately if -.I utility -exits with a non-zero exit status. -This does not include ignoring -.I utility -exiting due to a signal or without calling -.IR exit (2). -.TP -\-n number +.Bl -tag -width indent +.It Fl n Ar number Set the maximum number of arguments taken from standard input for each invocation of the utility. An invocation of -.I utility +.Ar utility will use less than -.I number +.Ar number standard input arguments if the number of bytes accumulated (see the -.I \-s +.Fl s option) exceeds the specified -.I size +.Ar size or there are fewer than -.I number +.Ar number arguments remaining for the last invocation of -.IR utility . +.Ar utility . The current default value for -.I number +.Ar number is 5000. -.TP -\-s size +.It Fl s Ar size Set the maximum number of bytes for the command line length provided to -.IR utility . +.Ar utility . The sum of the length of the utility name and the arguments passed to -.I utility +.Ar utility (including NULL terminators) will be less than or equal to this number. The current default value for -.I size -is ARG_MAX - 2048. -.TP -\-t +.Ar size +is +.Dv ARG_MAX +- 2048. +.It Fl t Echo the command to be executed to standard error immediately before it is executed. -.TP -\-x +.It Fl x Force -.I xargs +.Nm xargs to terminate immediately if a command line containing -.I number +.Ar number arguments will not fit in the specified (or default) command line length. -.PP +.El +.Pp If no -.I utility +.Ar utility is specified, -.IR echo (1) +.Xr echo 1 is used. -.PP +.Pp Undefined behavior may occur if -.I utility +.Ar utility reads from the standard input. -.PP -.I Xargs -exits with an exit status of 0 if no error occurs. -If -.I utility -cannot be invoked, is terminated by a signal or terminates without -calling -.IR exit (2), -.I xargs -exits with an exit status of 127. -If -.I utility -exits with an exit status other than 0, -.I xargs -exits with that exit status. -Otherwise, -.I xargs -exits with an exit status of 1. -.SH "SEE ALSO" -.IR echo (1), -.IR find (1) -.SH STANDARDS +.Sh DIAGNOSTICS +.Nm xargs +exits with one of the following values: +.Bl -tag -width Ds -compact +.It 0 +All invocations of +.Ar utility +returned a zero exit status. +.It 123 +One or more invocations of +.Ar utility +returned a nonzero exit status. +.It 124 +The +.Ar utility +exited with a 255 exit status. +.It 125 +The +.Ar utility +was killed or stopped by a signal. +.It 126 +The +.Ar utility +was found but could not be invoked. +.It 127 The -.I xargs -utility is expected to be POSIX 1003.2 compliant. +.Ar utility +could not be found. +.It 1 +Some other error occured. +.El +.Sh "SEE ALSO" +.Xr echo 1 , +.Xr find 1 +.Sh STANDARDS +.Nm xargs +is expected to be +.St -p1003.2 +compliant. +.Sh HISTORY +The meaning of 123, 124, and 125 exit values were taken from GNU xargs. diff --git a/usr.bin/xargs/xargs.c b/usr.bin/xargs/xargs.c index e4d3a234a8..3bae9b6999 100644 --- a/usr.bin/xargs/xargs.c +++ b/usr.bin/xargs/xargs.c @@ -54,7 +54,8 @@ static char sccsid[] = "@(#)xargs.c 5.11 (Berkeley) 6/19/91"; #include <limits.h> #include "pathnames.h" -int fflag, tflag; +int exit_status = 0; +int tflag; void err __P((const char *, ...)); void run(), usage(); @@ -85,11 +86,8 @@ main(argc, argv) nargs = 5000; nline = ARG_MAX - 4 * 1024; nflag = xflag = 0; - while ((ch = getopt(argc, argv, "fn:s:tx")) != EOF) + while ((ch = getopt(argc, argv, "n:s:tx")) != EOF) switch(ch) { - case 'f': - fflag = 1; - break; case 'n': nflag = 1; if ((nargs = atoi(optarg)) <= 0) @@ -165,13 +163,13 @@ main(argc, argv) case EOF: /* No arguments since last exec. */ if (p == bbp) - exit(0); + exit(exit_status); /* Nothing since end of last argument. */ if (argp == p) { *xp = NULL; run(av); - exit(0); + exit(exit_status); } goto arg1; case ' ': @@ -203,7 +201,7 @@ arg2: *p = '\0'; *xp = NULL; run(av); if (ch == EOF) - exit(0); + exit(exit_status); p = bbp; xp = bxp; } else @@ -272,31 +270,52 @@ run(argv) err("vfork: %s", strerror(errno)); case 0: execvp(argv[0], argv); + noinvoke = (errno == ENOENT) ? 127 : 126; (void)fprintf(stderr, "xargs: %s: %s.\n", argv[0], strerror(errno)); - noinvoke = 1; _exit(1); } pid = waitpid(pid, &status, 0); if (pid == -1) err("waitpid: %s", strerror(errno)); + /* * If we couldn't invoke the utility or the utility didn't exit - * properly, quit with 127. - * Otherwise, if not specified otherwise, and the utility exits - * non-zero, exit with that value. + * properly, quit with 127 or 126 respectively. */ - if (noinvoke || !WIFEXITED(status) || WIFSIGNALED(status)) - exit(127); - if (!fflag && WEXITSTATUS(status)) - exit(WEXITSTATUS(status)); + if (noinvoke) + exit(noinvoke); + + /* + * According to POSIX, we have to exit if the utility exits with + * a 255 status, or is interrupted by a signal. xargs is allowed + * to return any exit status between 1 and 125 in these cases, but + * we'll use 124 and 125, the same values used by GNU xargs. + */ + if (WIFEXITED(status)) { + if (WEXITSTATUS (status) == 255) { + fprintf (stderr, "xargs: %s exited with status 255\n", + argv[0]); + exit(124); + } else if (WEXITSTATUS (status) != 0) { + exit_status = 123; + } + } else if (WIFSTOPPED (status)) { + fprintf (stderr, "xargs: %s terminated by signal %d\n", + argv[0], WSTOPSIG (status)); + exit(125); + } else if (WIFSIGNALED (status)) { + fprintf (stderr, "xargs: %s terminated by signal %d\n", + argv[0], WTERMSIG (status)); + exit(125); + } } void usage() { (void)fprintf(stderr, -"usage: xargs [-ft] [[-x] -n number] [-s size] [utility [argument ...]]\n"); +"usage: xargs [-t] [[-x] -n number] [-s size] [utility [argument ...]]\n"); exit(1); } diff --git a/usr.bin/xinstall/Makefile b/usr.bin/xinstall/Makefile index 89f716a54e..6688f2e2d9 100644 --- a/usr.bin/xinstall/Makefile +++ b/usr.bin/xinstall/Makefile @@ -1,11 +1,11 @@ # @(#)Makefile 5.5 (Berkeley) 6/24/90 PROG= xinstall -MAN1= install.0 +MAN1= install.1 CLEANFILES=xinstall install: maninstall - install ${STRIP} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + install ${COPY} ${STRIP} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ ${PROG} ${DESTDIR}${BINDIR}/install .include <bsd.prog.mk> diff --git a/usr.bin/yacc/Makefile b/usr.bin/yacc/Makefile index 71633a3e40..f5c4b9de72 100644 --- a/usr.bin/yacc/Makefile +++ b/usr.bin/yacc/Makefile @@ -3,7 +3,7 @@ PROG= yacc SRCS= closure.c error.c lalr.c lr0.c main.c mkpar.c output.c reader.c \ skeleton.c symtab.c verbose.c warshall.c -MAN1= yacc.0 yyfix.0 +MAN1= yacc.1 yyfix.1 beforeinstall: install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ diff --git a/usr.bin/yacc/NO_WARRANTY b/usr.bin/yacc/NO_WARRANTY new file mode 100644 index 0000000000..06e8d93a2c --- /dev/null +++ b/usr.bin/yacc/NO_WARRANTY @@ -0,0 +1,3 @@ + Berkeley Yacc is distributed with no warranty whatever. The author +and any other contributors take no responsibility for the consequences of +its use. diff --git a/usr.bin/yacc/README b/usr.bin/yacc/README index 309c2311ed..091f233436 100644 --- a/usr.bin/yacc/README +++ b/usr.bin/yacc/README @@ -1,24 +1,23 @@ - Berkeley Yacc is an LALR(1) parser generator. Berkeley Yacc has been -made as compatible as possible with AT&T Yacc. Berkeley Yacc can accept -any input specification that conforms to the AT&T Yacc documentation. -Specifications that take advantage of undocumented features of AT&T Yacc -probably will be rejected. + Berkeley Yacc is an LALR(1) parser generator. Berkeley Yacc has been made +as compatible as possible with AT&T Yacc. Berkeley Yacc can accept any input +specification that conforms to the AT&T Yacc documentation. Specifications +that take advantage of undocumented features of AT&T Yacc will probably be +rejected. - Berkeley Yacc is distributed with no warranty whatever. Any program -of this size is almost certain to contain errors. Neither the author nor -any contributor takes responsibility for any consequences of its use. + Berkeley Yacc is distributed with no warranty whatever. The code is certain +to contain errors. Neither the author nor any contributor takes responsibility +for any consequences of its use. - Berkeley Yacc is in the public domain. The data structures and -algorithms used in Berkeley Yacc were either taken from documents -available to the general public or were inventions of the author. -Anyone may freely distribute source or binary forms of Berkeley Yacc -whether unchanged or modified. Distributors may charge whatever fees -they can obtain for Berkeley Yacc. Programs generated by Berkeley Yacc -may be distributed freely. + Berkeley Yacc is in the public domain. The data structures and algorithms +used in Berkeley Yacc are all either taken from documents available to the +general public or are inventions of the author. Anyone may freely distribute +source or binary forms of Berkeley Yacc whether unchanged or modified. +Distributers may charge whatever fees they can obtain for Berkeley Yacc. +Programs generated by Berkeley Yacc may be distributed freely. Please report bugs to - corbett@berkeley.edu + robert.corbett@eng.Sun.COM Include a small example if possible. Please include the banner string from skeleton.c with the bug report. Do not expect rapid responses. diff --git a/usr.bin/yacc/closure.c b/usr.bin/yacc/closure.c index 4527415599..7c8f957915 100644 --- a/usr.bin/yacc/closure.c +++ b/usr.bin/yacc/closure.c @@ -1,43 +1,3 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Robert Paul Corbett. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)closure.c 5.2 (Berkeley) 6/1/90"; -#endif /* not lint */ - #include "defs.h" short *itemset; @@ -86,58 +46,56 @@ set_EFF() set_first_derives() { - register unsigned *rrow; - register unsigned *vrow; - register int j; - register unsigned mask; - register unsigned cword; - register short *rp; - - int rule; - int i; - int rulesetsize; - int varsetsize; - - rulesetsize = WORDSIZE(nrules); - varsetsize = WORDSIZE(nvars); - first_derives = NEW2(nvars * rulesetsize, unsigned) - ntokens * rulesetsize; - - set_EFF(); - - rrow = first_derives + ntokens * rulesetsize; - for (i = start_symbol; i < nsyms; i++) + register unsigned *rrow; + register unsigned *vrow; + register int j; + register unsigned k; + register unsigned cword; + register short *rp; + + int rule; + int i; + int rulesetsize; + int varsetsize; + + rulesetsize = WORDSIZE(nrules); + varsetsize = WORDSIZE(nvars); + first_derives = NEW2(nvars * rulesetsize, unsigned) - ntokens * rulesetsize; + + set_EFF(); + + rrow = first_derives + ntokens * rulesetsize; + for (i = start_symbol; i < nsyms; i++) { - vrow = EFF + ((i - ntokens) * varsetsize); - cword = *vrow++; - mask = 1; - for (j = start_symbol; j < nsyms; j++) + vrow = EFF + ((i - ntokens) * varsetsize); + k = BITS_PER_WORD; + for (j = start_symbol; j < nsyms; k++, j++) { - if (cword & mask) + if (k >= BITS_PER_WORD) { - rp = derives[j]; - while ((rule = *rp++) >= 0) - { - SETBIT(rrow, rule); - } + cword = *vrow++; + k = 0; } - mask <<= 1; - if (mask == 0) + if (cword & (1 << k)) { - cword = *vrow++; - mask = 1; + rp = derives[j]; + while ((rule = *rp++) >= 0) + { + SETBIT(rrow, rule); + } } } - vrow += varsetsize; - rrow += rulesetsize; + vrow += varsetsize; + rrow += rulesetsize; } #ifdef DEBUG - print_first_derives(); + print_first_derives(); #endif - FREE(EFF); + FREE(EFF); } @@ -147,7 +105,7 @@ int n; { register int ruleno; register unsigned word; - register unsigned mask; + register unsigned i; register short *csp; register unsigned *dsp; register unsigned *rsp; @@ -183,27 +141,22 @@ int n; for (rsp = ruleset; rsp < rsend; ++rsp) { word = *rsp; - if (word == 0) - ruleno += BITS_PER_WORD; - else + if (word) { - mask = 1; - while (mask) + for (i = 0; i < BITS_PER_WORD; ++i) { - if (word & mask) + if (word & (1 << i)) { - itemno = rrhs[ruleno]; + itemno = rrhs[ruleno+i]; while (csp < csend && *csp < itemno) *itemsetend++ = *csp++; *itemsetend++ = itemno; while (csp < csend && *csp == itemno) ++csp; } - - mask <<= 1; - ++ruleno; } } + ruleno += BITS_PER_WORD; } while (csp < csend) @@ -239,10 +192,10 @@ int n; print_EFF() { - register int i, j, k; + register int i, j; register unsigned *rowp; register unsigned word; - register unsigned mask; + register unsigned k; printf("\n\nEpsilon Free Firsts\n"); @@ -252,18 +205,17 @@ print_EFF() rowp = EFF + ((i - start_symbol) * WORDSIZE(nvars)); word = *rowp++; - mask = 1; - for (j = 0; j < nvars; j++) + k = BITS_PER_WORD; + for (j = 0; j < nvars; k++, j++) { - if (word & mask) - printf(" %s", symbol_name[start_symbol + j]); - - mask <<= 1; - if (mask == 0) + if (k >= BITS_PER_WORD) { word = *rowp++; - mask = 1; + k = 0; } + + if (word & (1 << k)) + printf(" %s", symbol_name[start_symbol + j]); } } } @@ -271,31 +223,29 @@ print_EFF() print_first_derives() { - register int i; - register int j; - register unsigned *rp; - register unsigned cword; - register unsigned mask; + register int i; + register int j; + register unsigned *rp; + register unsigned cword; + register unsigned k; - printf("\n\n\nFirst Derives\n"); + printf("\n\n\nFirst Derives\n"); - for (i = start_symbol; i < nsyms; i++) + for (i = start_symbol; i < nsyms; i++) { - printf("\n%s derives\n", symbol_name[i]); - rp = first_derives + i * WORDSIZE(nrules); - cword = *rp++; - mask = 1; - for (j = 0; j <= nrules; j++) + printf("\n%s derives\n", symbol_name[i]); + rp = first_derives + i * WORDSIZE(nrules); + k = BITS_PER_WORD; + for (j = 0; j <= nrules; k++, j++) { - if (cword & mask) - printf(" %d\n", j); - - mask <<= 1; - if (mask == 0) - { + if (k >= BITS_PER_WORD) + { cword = *rp++; - mask = 1; - } + k = 0; + } + + if (cword & (1 << k)) + printf(" %d\n", j); } } diff --git a/usr.bin/yacc/defs.h b/usr.bin/yacc/defs.h index 5bd1d4cc37..023af48984 100644 --- a/usr.bin/yacc/defs.h +++ b/usr.bin/yacc/defs.h @@ -1,41 +1,3 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Robert Paul Corbett. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)defs.h 5.5 (Berkeley) 1/20/91 - */ - #include <assert.h> #include <ctype.h> #include <stdio.h> @@ -221,6 +183,7 @@ extern char lflag; extern char rflag; extern char tflag; extern char vflag; +extern char *symbol_prefix; extern char *myname; extern char *cptr; diff --git a/usr.bin/yacc/error.c b/usr.bin/yacc/error.c index 54a49ad01b..3ed564c629 100644 --- a/usr.bin/yacc/error.c +++ b/usr.bin/yacc/error.c @@ -1,43 +1,3 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Robert Paul Corbett. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)error.c 5.3 (Berkeley) 6/1/90"; -#endif /* not lint */ - /* routines for printing error messages */ #include "defs.h" diff --git a/usr.bin/yacc/lalr.c b/usr.bin/yacc/lalr.c index bf9aec846b..640ddc4ca7 100644 --- a/usr.bin/yacc/lalr.c +++ b/usr.bin/yacc/lalr.c @@ -1,43 +1,3 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Robert Paul Corbett. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)lalr.c 5.3 (Berkeley) 6/1/90"; -#endif /* not lint */ - #include "defs.h" typedef diff --git a/usr.bin/yacc/lr0.c b/usr.bin/yacc/lr0.c index 43106ea6cf..3ee42a8840 100644 --- a/usr.bin/yacc/lr0.c +++ b/usr.bin/yacc/lr0.c @@ -1,42 +1,3 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Robert Paul Corbett. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)lr0.c 5.3 (Berkeley) 1/20/91"; -#endif /* not lint */ #include "defs.h" diff --git a/usr.bin/yacc/main.c b/usr.bin/yacc/main.c index 213a011cbb..f17e19b435 100644 --- a/usr.bin/yacc/main.c +++ b/usr.bin/yacc/main.c @@ -1,49 +1,3 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Robert Paul Corbett. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1989 The Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)main.c 5.4 (Berkeley) 2/26/91"; -#endif /* not lint */ - #include <signal.h> #include "defs.h" @@ -53,6 +7,7 @@ char rflag; char tflag; char vflag; +char *symbol_prefix; char *file_prefix = "y"; char *myname = "yacc"; char *temp_form = "yacc.XXXXXXX"; @@ -116,7 +71,6 @@ int k; } -void onintr() { done(1); @@ -142,7 +96,7 @@ set_signals() usage() { - fprintf(stderr, "usage: %s [-dlrtv] [-b file_prefix] filename\n", myname); + fprintf(stderr, "usage: %s [-dlrtv] [-b file_prefix] [-p symbol_prefix] filename\n", myname); exit(1); } @@ -187,6 +141,15 @@ char *argv[]; lflag = 1; break; + case 'p': + if (*++s) + symbol_prefix = s; + else if (++i < argc) + symbol_prefix = argv[i]; + else + usage(); + continue; + case 'r': rflag = 1; break; diff --git a/usr.bin/yacc/mkpar.c b/usr.bin/yacc/mkpar.c index 42ead14514..e1aef60fec 100644 --- a/usr.bin/yacc/mkpar.c +++ b/usr.bin/yacc/mkpar.c @@ -1,42 +1,3 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Robert Paul Corbett. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)mkpar.c 5.3 (Berkeley) 1/20/91"; -#endif /* not lint */ #include "defs.h" @@ -393,3 +354,4 @@ free_parser() FREE(parser); } + diff --git a/usr.bin/yacc/output.c b/usr.bin/yacc/output.c index d0bc44ba6a..88c77b52f6 100644 --- a/usr.bin/yacc/output.c +++ b/usr.bin/yacc/output.c @@ -1,43 +1,3 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Robert Paul Corbett. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)output.c 5.6 (Berkeley) 1/20/91"; -#endif /* not lint */ - #include "defs.h" static int nvectors; @@ -62,6 +22,7 @@ output() free_itemsets(); free_shifts(); free_reductions(); + output_prefix(); output_stored_text(); output_defines(); output_rule_data(); @@ -79,13 +40,73 @@ output() } +output_prefix() +{ + if (symbol_prefix == NULL) + symbol_prefix = "yy"; + else + { + ++outline; + fprintf(code_file, "#define yyparse %sparse\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yylex %slex\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyerror %serror\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yychar %schar\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyval %sval\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yylval %slval\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yydebug %sdebug\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yynerrs %snerrs\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyerrflag %serrflag\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyss %sss\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyssp %sssp\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyvs %svs\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyvsp %svsp\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yylhs %slhs\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yylen %slen\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yydefred %sdefred\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yydgoto %sdgoto\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yysindex %ssindex\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyrindex %srindex\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yygindex %sgindex\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yytable %stable\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yycheck %scheck\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyname %sname\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyrule %srule\n", symbol_prefix); + } + ++outline; + fprintf(code_file, "#define YYPREFIX \"%s\"\n", symbol_prefix); +} + + output_rule_data() { register int i; register int j; - fprintf(output_file, "short yylhs[] = {%42d,", + fprintf(output_file, "short %slhs[] = {%42d,", symbol_prefix, symbol_value[start_symbol]); j = 10; @@ -105,7 +126,7 @@ output_rule_data() if (!rflag) outline += 2; fprintf(output_file, "\n};\n"); - fprintf(output_file, "short yylen[] = {%42d,", 2); + fprintf(output_file, "short %slen[] = {%42d,", symbol_prefix, 2); j = 10; for (i = 3; i < nrules; i++) @@ -130,7 +151,7 @@ output_yydefred() { register int i, j; - fprintf(output_file, "short yydefred[] = {%39d,", + fprintf(output_file, "short %sdefred[] = {%39d,", symbol_prefix, (defred[0] ? defred[0] - 2 : 0)); j = 10; @@ -272,7 +293,7 @@ goto_actions() state_count = NEW2(nstates, short); k = default_goto(start_symbol + 1); - fprintf(output_file, "short yydgoto[] = {%40d,", k); + fprintf(output_file, "short %sdgoto[] = {%40d,", symbol_prefix, k); save_column(start_symbol + 1, k); j = 10; @@ -596,7 +617,7 @@ output_base() { register int i, j; - fprintf(output_file, "short yysindex[] = {%39d,", base[0]); + fprintf(output_file, "short %ssindex[] = {%39d,", symbol_prefix, base[0]); j = 10; for (i = 1; i < nstates; i++) @@ -614,7 +635,7 @@ output_base() } if (!rflag) outline += 2; - fprintf(output_file, "\n};\nshort yyrindex[] = {%39d,", + fprintf(output_file, "\n};\nshort %srindex[] = {%39d,", symbol_prefix, base[nstates]); j = 10; @@ -633,7 +654,7 @@ output_base() } if (!rflag) outline += 2; - fprintf(output_file, "\n};\nshort yygindex[] = {%39d,", + fprintf(output_file, "\n};\nshort %sgindex[] = {%39d,", symbol_prefix, base[2*nstates]); j = 10; @@ -665,7 +686,8 @@ output_table() ++outline; fprintf(code_file, "#define YYTABLESIZE %d\n", high); - fprintf(output_file, "short yytable[] = {%40d,", table[0]); + fprintf(output_file, "short %stable[] = {%40d,", symbol_prefix, + table[0]); j = 10; for (i = 1; i <= high; i++) @@ -694,7 +716,8 @@ output_check() register int i; register int j; - fprintf(output_file, "short yycheck[] = {%40d,", check[0]); + fprintf(output_file, "short %scheck[] = {%40d,", symbol_prefix, + check[0]); j = 10; for (i = 1; i <= high; i++) @@ -796,7 +819,8 @@ output_defines() if (union_file == NULL) open_error(union_file_name); while ((c = getc(union_file)) != EOF) putc(c, defines_file); - fprintf(defines_file, " YYSTYPE;\nextern YYSTYPE yylval;\n"); + fprintf(defines_file, " YYSTYPE;\nextern YYSTYPE %slval;\n", + symbol_prefix); } } @@ -861,7 +885,7 @@ output_debug() symnam[0] = "end-of-file"; if (!rflag) ++outline; - fprintf(output_file, "#if YYDEBUG\nchar *yyname[] = {"); + fprintf(output_file, "#if YYDEBUG\nchar *%sname[] = {", symbol_prefix); j = 80; for (i = 0; i <= max; ++i) { @@ -987,7 +1011,7 @@ output_debug() FREE(symnam); if (!rflag) ++outline; - fprintf(output_file, "char *yyrule[] = {\n"); + fprintf(output_file, "char *%srule[] = {\n", symbol_prefix); for (i = 2; i < nrules; ++i) { fprintf(output_file, "\"%s :", symbol_name[rlhs[i]]); diff --git a/usr.bin/yacc/reader.c b/usr.bin/yacc/reader.c index 3b724d3b84..059a04371c 100644 --- a/usr.bin/yacc/reader.c +++ b/usr.bin/yacc/reader.c @@ -1,43 +1,3 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Robert Paul Corbett. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)reader.c 5.7 (Berkeley) 1/20/91"; -#endif /* not lint */ - #include "defs.h" /* The line size must be a positive integer. One hundred was chosen */ diff --git a/usr.bin/yacc/skeleton.c b/usr.bin/yacc/skeleton.c index bccdfc90b1..d2b6c9f09f 100644 --- a/usr.bin/yacc/skeleton.c +++ b/usr.bin/yacc/skeleton.c @@ -1,57 +1,28 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Robert Paul Corbett. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)skeleton.c 5.6 (Berkeley) 1/27/91"; -#endif /* not lint */ - #include "defs.h" -/* The banner used here should be replaced with an #ident directive */ -/* if the target C compiler supports #ident directives. */ +/* The definition of yysccsid in the banner should be replaced with */ +/* a #pragma ident directive if the target C compiler supports */ +/* #pragma ident directives. */ /* */ /* If the skeleton is changed, the banner should be changed so that */ -/* the altered version can easily be distinguished from the original. */ +/* the altered version can be easily distinguished from the original. */ +/* */ +/* The #defines included with the banner are there because they are */ +/* useful in subsequent code. The macros #defined in the header or */ +/* the body either are not useful outside of semantic actions or */ +/* are conditional. */ char *banner[] = { "#ifndef lint", - "static char yysccsid[] = \"@(#)yaccpar 1.8 (Berkeley) 01/20/90\";", + "static char yysccsid[] = \"@(#)yaccpar 1.9 (Berkeley) 02/21/93\";", "#endif", "#define YYBYACC 1", + "#define YYMAJOR 1", + "#define YYMINOR 9", + "#define yyclearin (yychar=(-1))", + "#define yyerrok (yyerrflag=0)", + "#define YYRECOVERING (yyerrflag!=0)", 0 }; @@ -77,12 +48,9 @@ char *tables[] = char *header[] = { - "#define yyclearin (yychar=(-1))", - "#define yyerrok (yyerrflag=0)", "#ifdef YYSTACKSIZE", - "#ifndef YYMAXDEPTH", + "#undef YYMAXDEPTH", "#define YYMAXDEPTH YYSTACKSIZE", - "#endif", "#else", "#ifdef YYMAXDEPTH", "#define YYSTACKSIZE YYMAXDEPTH", @@ -109,6 +77,7 @@ char *header[] = char *body[] = { "#define YYABORT goto yyabort", + "#define YYREJECT goto yyabort", "#define YYACCEPT goto yyaccept", "#define YYERROR goto yyerrlab", "int", @@ -146,8 +115,8 @@ char *body[] = " yys = 0;", " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];", " if (!yys) yys = \"illegal-symbol\";", - " printf(\"yydebug: state %d, reading %d (%s)\\n\", yystate,", - " yychar, yys);", + " printf(\"%sdebug: state %d, reading %d (%s)\\n\",", + " YYPREFIX, yystate, yychar, yys);", " }", "#endif", " }", @@ -156,8 +125,8 @@ char *body[] = " {", "#if YYDEBUG", " if (yydebug)", - " printf(\"yydebug: state %d, shifting to state %d\\n\",", - " yystate, yytable[yyn]);", + " printf(\"%sdebug: state %d, shifting to state %d\\n\",", + " YYPREFIX, yystate, yytable[yyn]);", "#endif", " if (yyssp >= yyss + yystacksize - 1)", " {", @@ -197,8 +166,8 @@ char *body[] = " {", "#if YYDEBUG", " if (yydebug)", - " printf(\"yydebug: state %d, error recovery shifting\\", - " to state %d\\n\", *yyssp, yytable[yyn]);", + " printf(\"%sdebug: state %d, error recovery shifting\\", + " to state %d\\n\", YYPREFIX, *yyssp, yytable[yyn]);", "#endif", " if (yyssp >= yyss + yystacksize - 1)", " {", @@ -212,9 +181,9 @@ char *body[] = " {", "#if YYDEBUG", " if (yydebug)", - " printf(\"yydebug: error recovery discarding state %d\ + " printf(\"%sdebug: error recovery discarding state %d\ \\n\",", - " *yyssp);", + " YYPREFIX, *yyssp);", "#endif", " if (yyssp <= yyss) goto yyabort;", " --yyssp;", @@ -231,9 +200,9 @@ char *body[] = " yys = 0;", " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];", " if (!yys) yys = \"illegal-symbol\";", - " printf(\"yydebug: state %d, error recovery discards token %d\ + " printf(\"%sdebug: state %d, error recovery discards token %d\ (%s)\\n\",", - " yystate, yychar, yys);", + " YYPREFIX, yystate, yychar, yys);", " }", "#endif", " yychar = (-1);", @@ -242,8 +211,8 @@ char *body[] = "yyreduce:", "#if YYDEBUG", " if (yydebug)", - " printf(\"yydebug: state %d, reducing by rule %d (%s)\\n\",", - " yystate, yyn, yyrule[yyn]);", + " printf(\"%sdebug: state %d, reducing by rule %d (%s)\\n\",", + " YYPREFIX, yystate, yyn, yyrule[yyn]);", "#endif", " yym = yylen[yyn];", " yyval = yyvsp[1-yym];", @@ -264,8 +233,8 @@ char *trailer[] = " {", "#if YYDEBUG", " if (yydebug)", - " printf(\"yydebug: after reduction, shifting from state 0 to\\", - " state %d\\n\", YYFINAL);", + " printf(\"%sdebug: after reduction, shifting from state 0 to\\", + " state %d\\n\", YYPREFIX, YYFINAL);", "#endif", " yystate = YYFINAL;", " *++yyssp = YYFINAL;", @@ -279,8 +248,8 @@ char *trailer[] = " yys = 0;", " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];", " if (!yys) yys = \"illegal-symbol\";", - " printf(\"yydebug: state %d, reading %d (%s)\\n\",", - " YYFINAL, yychar, yys);", + " printf(\"%sdebug: state %d, reading %d (%s)\\n\",", + " YYPREFIX, YYFINAL, yychar, yys);", " }", "#endif", " }", @@ -294,8 +263,8 @@ char *trailer[] = " yystate = yydgoto[yym];", "#if YYDEBUG", " if (yydebug)", - " printf(\"yydebug: after reduction, shifting from state %d \\", - "to state %d\\n\", *yyssp, yystate);", + " printf(\"%sdebug: after reduction, shifting from state %d \\", + "to state %d\\n\", YYPREFIX, *yyssp, yystate);", "#endif", " if (yyssp >= yyss + yystacksize - 1)", " {", @@ -318,13 +287,20 @@ char *trailer[] = write_section(section) char *section[]; { + register int c; register int i; - register FILE *fp; + register char *s; + register FILE *f; - fp = code_file; - for (i = 0; section[i]; ++i) + f = code_file; + for (i = 0; s = section[i]; ++i) { ++outline; - fprintf(fp, "%s\n", section[i]); + while (c = *s) + { + putc(c, f); + ++s; + } + putc('\n', f); } } diff --git a/usr.bin/yacc/symtab.c b/usr.bin/yacc/symtab.c index 0c5f55c535..c7c6ac03a7 100644 --- a/usr.bin/yacc/symtab.c +++ b/usr.bin/yacc/symtab.c @@ -1,45 +1,6 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Robert Paul Corbett. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)symtab.c 5.3 (Berkeley) 6/1/90"; -#endif /* not lint */ - #include "defs.h" + /* TABLE_SIZE is the number of entries in the symbol table. */ /* TABLE_SIZE must be a power of two. */ diff --git a/usr.bin/yacc/test/error.tab.c b/usr.bin/yacc/test/error.tab.c index c339417c90..ffc6e3700e 100644 --- a/usr.bin/yacc/test/error.tab.c +++ b/usr.bin/yacc/test/error.tab.c @@ -1,6 +1,13 @@ #ifndef lint -char yysccsid[] = "@(#)yaccpar 1.3 (Berkeley) 01/21/90"; +static char yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93"; #endif +#define YYBYACC 1 +#define YYMAJOR 1 +#define YYMINOR 9 +#define yyclearin (yychar=(-1)) +#define yyerrok (yyerrflag=0) +#define YYRECOVERING (yyerrflag!=0) +#define YYPREFIX "yy" #define YYERRCODE 256 short yylhs[] = { -1, 0, @@ -43,13 +50,15 @@ char *yyrule[] = { #ifndef YYSTYPE typedef int YYSTYPE; #endif -#define yyclearin (yychar=(-1)) -#define yyerrok (yyerrflag=0) -#ifndef YYSTACKSIZE +#ifdef YYSTACKSIZE +#undef YYMAXDEPTH +#define YYMAXDEPTH YYSTACKSIZE +#else #ifdef YYMAXDEPTH #define YYSTACKSIZE YYMAXDEPTH #else -#define YYSTACKSIZE 300 +#define YYSTACKSIZE 500 +#define YYMAXDEPTH 500 #endif #endif int yydebug; @@ -60,15 +69,16 @@ short *yyssp; YYSTYPE *yyvsp; YYSTYPE yyval; YYSTYPE yylval; -#define yystacksize YYSTACKSIZE short yyss[YYSTACKSIZE]; YYSTYPE yyvs[YYSTACKSIZE]; +#define yystacksize YYSTACKSIZE #line 4 "error.y" main(){printf("yyparse() = %d\n",yyparse());} yylex(){return-1;} yyerror(s)char*s;{printf("%s\n",s);} -#line 70 "error.tab.c" +#line 80 "error.tab.c" #define YYABORT goto yyabort +#define YYREJECT goto yyabort #define YYACCEPT goto yyaccept #define YYERROR goto yyerrlab int @@ -82,9 +92,7 @@ yyparse() if (yys = getenv("YYDEBUG")) { yyn = *yys; - if (yyn == '0') - yydebug = 0; - else if (yyn >= '1' && yyn <= '9') + if (yyn >= '0' && yyn <= '9') yydebug = yyn - '0'; } #endif @@ -108,8 +116,8 @@ yyloop: yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; - printf("yydebug: state %d, reading %d (%s)\n", yystate, - yychar, yys); + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, yystate, yychar, yys); } #endif } @@ -118,8 +126,8 @@ yyloop: { #if YYDEBUG if (yydebug) - printf("yydebug: state %d, shifting to state %d\n", - yystate, yytable[yyn]); + printf("%sdebug: state %d, shifting to state %d\n", + YYPREFIX, yystate, yytable[yyn]); #endif if (yyssp >= yyss + yystacksize - 1) { @@ -159,8 +167,8 @@ yyinrecovery: { #if YYDEBUG if (yydebug) - printf("yydebug: state %d, error recovery shifting\ - to state %d\n", *yyssp, yytable[yyn]); + printf("%sdebug: state %d, error recovery shifting\ + to state %d\n", YYPREFIX, *yyssp, yytable[yyn]); #endif if (yyssp >= yyss + yystacksize - 1) { @@ -174,8 +182,8 @@ yyinrecovery: { #if YYDEBUG if (yydebug) - printf("yydebug: error recovery discarding state %d\n", - *yyssp); + printf("%sdebug: error recovery discarding state %d\n", + YYPREFIX, *yyssp); #endif if (yyssp <= yyss) goto yyabort; --yyssp; @@ -192,8 +200,8 @@ yyinrecovery: yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; - printf("yydebug: state %d, error recovery discards token %d (%s)\n", - yystate, yychar, yys); + printf("%sdebug: state %d, error recovery discards token %d (%s)\n", + YYPREFIX, yystate, yychar, yys); } #endif yychar = (-1); @@ -202,8 +210,8 @@ yyinrecovery: yyreduce: #if YYDEBUG if (yydebug) - printf("yydebug: state %d, reducing by rule %d (%s)\n", - yystate, yyn, yyrule[yyn]); + printf("%sdebug: state %d, reducing by rule %d (%s)\n", + YYPREFIX, yystate, yyn, yyrule[yyn]); #endif yym = yylen[yyn]; yyval = yyvsp[1-yym]; @@ -216,10 +224,10 @@ yyreduce: yym = yylhs[yyn]; if (yystate == 0 && yym == 0) { -#ifdef YYDEBUG +#if YYDEBUG if (yydebug) - printf("yydebug: after reduction, shifting from state 0 to\ - state %d\n", YYFINAL); + printf("%sdebug: after reduction, shifting from state 0 to\ + state %d\n", YYPREFIX, YYFINAL); #endif yystate = YYFINAL; *++yyssp = YYFINAL; @@ -233,8 +241,8 @@ yyreduce: yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; - printf("yydebug: state %d, reading %d (%s)\n", - YYFINAL, yychar, yys); + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, YYFINAL, yychar, yys); } #endif } @@ -246,10 +254,10 @@ yyreduce: yystate = yytable[yyn]; else yystate = yydgoto[yym]; -#ifdef YYDEBUG +#if YYDEBUG if (yydebug) - printf("yydebug: after reduction, shifting from state %d \ -to state %d\n", *yyssp, yystate); + printf("%sdebug: after reduction, shifting from state %d \ +to state %d\n", YYPREFIX, *yyssp, yystate); #endif if (yyssp >= yyss + yystacksize - 1) { diff --git a/usr.bin/yacc/test/error.tab.h b/usr.bin/yacc/test/error.tab.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/usr.bin/yacc/test/ftp.tab.c b/usr.bin/yacc/test/ftp.tab.c index 5f2d8c0604..c9794edc03 100644 --- a/usr.bin/yacc/test/ftp.tab.c +++ b/usr.bin/yacc/test/ftp.tab.c @@ -1,6 +1,62 @@ #ifndef lint -char yysccsid[] = "@(#)yaccpar 1.3 (Berkeley) 01/21/90"; +static char yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93"; #endif +#define YYBYACC 1 +#define YYMAJOR 1 +#define YYMINOR 9 +#define yyclearin (yychar=(-1)) +#define yyerrok (yyerrflag=0) +#define YYRECOVERING (yyerrflag!=0) +#define YYPREFIX "yy" +#line 26 "ftp.y" + +#ifndef lint +static char sccsid[] = "@(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <arpa/ftp.h> + +#include <stdio.h> +#include <signal.h> +#include <ctype.h> +#include <pwd.h> +#include <setjmp.h> +#include <syslog.h> +#include <sys/stat.h> +#include <time.h> + +extern struct sockaddr_in data_dest; +extern int logged_in; +extern struct passwd *pw; +extern int guest; +extern int logging; +extern int type; +extern int form; +extern int debug; +extern int timeout; +extern int maxtimeout; +extern int pdata; +extern char hostname[], remotehost[]; +extern char proctitle[]; +extern char *globerr; +extern int usedefault; +extern int transflag; +extern char tmpline[]; +char **glob(); + +static int cmd_type; +static int cmd_form; +static int cmd_bytesz; +char cbuf[512]; +char *fromname; + +char *index(); +#line 60 "ftp.tab.c" #define A 257 #define B 258 #define C 259 @@ -65,55 +121,6 @@ char yysccsid[] = "@(#)yaccpar 1.3 (Berkeley) 01/21/90"; #define CHMOD 318 #define LEXERR 319 #define YYERRCODE 256 -#line 26 "ftp.y" - -#ifndef lint -static char sccsid[] = "@(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89"; -#endif /* not lint */ - -#include <sys//param.h> -#include <sys//socket.h> - -#include <netinet//in.h> - -#include <arpa//ftp.h> - -#include <stdio.h> -#include <signal.h> -#include <ctype.h> -#include <pwd.h> -#include <setjmp.h> -#include <syslog.h> -#include <sys//stat.h> -#include <time.h> - -extern struct sockaddr_in data_dest; -extern int logged_in; -extern struct passwd *pw; -extern int guest; -extern int logging; -extern int type; -extern int form; -extern int debug; -extern int timeout; -extern int maxtimeout; -extern int pdata; -extern char hostname[], remotehost[]; -extern char proctitle[]; -extern char *globerr; -extern int usedefault; -extern int transflag; -extern char tmpline[]; -char **glob(); - -static int cmd_type; -static int cmd_form; -static int cmd_bytesz; -char cbuf[512]; -char *fromname; - -char *index(); -#line 116 "ftp.tab.c" short yylhs[] = { -1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -351,13 +358,15 @@ char *yyrule[] = { #ifndef YYSTYPE typedef int YYSTYPE; #endif -#define yyclearin (yychar=(-1)) -#define yyerrok (yyerrflag=0) -#ifndef YYSTACKSIZE +#ifdef YYSTACKSIZE +#undef YYMAXDEPTH +#define YYMAXDEPTH YYSTACKSIZE +#else #ifdef YYMAXDEPTH #define YYSTACKSIZE YYMAXDEPTH #else -#define YYSTACKSIZE 300 +#define YYSTACKSIZE 500 +#define YYMAXDEPTH 500 #endif #endif int yydebug; @@ -368,9 +377,9 @@ short *yyssp; YYSTYPE *yyvsp; YYSTYPE yyval; YYSTYPE yylval; -#define yystacksize YYSTACKSIZE short yyss[YYSTACKSIZE]; YYSTYPE yyvs[YYSTACKSIZE]; +#define yystacksize YYSTACKSIZE #line 658 "ftp.y" extern jmp_buf errcatch; @@ -895,8 +904,9 @@ char *filename; reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); } } -#line 898 "ftp.tab.c" +#line 908 "ftp.tab.c" #define YYABORT goto yyabort +#define YYREJECT goto yyabort #define YYACCEPT goto yyaccept #define YYERROR goto yyerrlab int @@ -910,9 +920,7 @@ yyparse() if (yys = getenv("YYDEBUG")) { yyn = *yys; - if (yyn == '0') - yydebug = 0; - else if (yyn >= '1' && yyn <= '9') + if (yyn >= '0' && yyn <= '9') yydebug = yyn - '0'; } #endif @@ -936,8 +944,8 @@ yyloop: yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; - printf("yydebug: state %d, reading %d (%s)\n", yystate, - yychar, yys); + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, yystate, yychar, yys); } #endif } @@ -946,8 +954,8 @@ yyloop: { #if YYDEBUG if (yydebug) - printf("yydebug: state %d, shifting to state %d\n", - yystate, yytable[yyn]); + printf("%sdebug: state %d, shifting to state %d\n", + YYPREFIX, yystate, yytable[yyn]); #endif if (yyssp >= yyss + yystacksize - 1) { @@ -987,8 +995,8 @@ yyinrecovery: { #if YYDEBUG if (yydebug) - printf("yydebug: state %d, error recovery shifting\ - to state %d\n", *yyssp, yytable[yyn]); + printf("%sdebug: state %d, error recovery shifting\ + to state %d\n", YYPREFIX, *yyssp, yytable[yyn]); #endif if (yyssp >= yyss + yystacksize - 1) { @@ -1002,8 +1010,8 @@ yyinrecovery: { #if YYDEBUG if (yydebug) - printf("yydebug: error recovery discarding state %d\n", - *yyssp); + printf("%sdebug: error recovery discarding state %d\n", + YYPREFIX, *yyssp); #endif if (yyssp <= yyss) goto yyabort; --yyssp; @@ -1020,8 +1028,8 @@ yyinrecovery: yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; - printf("yydebug: state %d, error recovery discards token %d (%s)\n", - yystate, yychar, yys); + printf("%sdebug: state %d, error recovery discards token %d (%s)\n", + YYPREFIX, yystate, yychar, yys); } #endif yychar = (-1); @@ -1030,8 +1038,8 @@ yyinrecovery: yyreduce: #if YYDEBUG if (yydebug) - printf("yydebug: state %d, reducing by rule %d (%s)\n", - yystate, yyn, yyrule[yyn]); + printf("%sdebug: state %d, reducing by rule %d (%s)\n", + YYPREFIX, yystate, yyn, yyrule[yyn]); #endif yym = yylen[yyn]; yyval = yyvsp[1-yym]; @@ -1497,7 +1505,7 @@ break; case 49: #line 493 "ftp.y" { - *(char **)&(yyval ) = ""; + *(char **)&(yyval) = ""; } break; case 52: @@ -1515,19 +1523,19 @@ break; case 53: #line 516 "ftp.y" { - yyval = FORM_N; + yyval = FORM_N; } break; case 54: #line 520 "ftp.y" { - yyval = FORM_T; + yyval = FORM_T; } break; case 55: #line 524 "ftp.y" { - yyval = FORM_C; + yyval = FORM_C; } break; case 56: @@ -1588,37 +1596,37 @@ break; case 64: #line 572 "ftp.y" { - yyval = STRU_F; + yyval = STRU_F; } break; case 65: #line 576 "ftp.y" { - yyval = STRU_R; + yyval = STRU_R; } break; case 66: #line 580 "ftp.y" { - yyval = STRU_P; + yyval = STRU_P; } break; case 67: #line 586 "ftp.y" { - yyval = MODE_S; + yyval = MODE_S; } break; case 68: #line 590 "ftp.y" { - yyval = MODE_B; + yyval = MODE_B; } break; case 69: #line 594 "ftp.y" { - yyval = MODE_C; + yyval = MODE_C; } break; case 70: @@ -1630,14 +1638,14 @@ case 70: * This is a valid reply in some cases but not in others. */ if (logged_in && yyvsp[0] && strncmp((char *) yyvsp[0], "~", 1) == 0) { - *(char **)&(yyval ) = *glob((char *) yyvsp[0]); + *(char **)&(yyval) = *glob((char *) yyvsp[0]); if (globerr != NULL) { reply(550, globerr); - yyval = NULL; + yyval = NULL; } free((char *) yyvsp[0]); } else - yyval = yyvsp[0]; + yyval = yyvsp[0]; } break; case 72: @@ -1662,21 +1670,21 @@ case 72: multby *= 8; dec /= 10; } - yyval = ret; + yyval = ret; } break; case 73: #line 647 "ftp.y" { if (logged_in) - yyval = 1; + yyval = 1; else { reply(530, "Please login with USER and PASS."); - yyval = 0; + yyval = 0; } } break; -#line 1679 "ftp.tab.c" +#line 1688 "ftp.tab.c" } yyssp -= yym; yystate = *yyssp; @@ -1684,10 +1692,10 @@ break; yym = yylhs[yyn]; if (yystate == 0 && yym == 0) { -#ifdef YYDEBUG +#if YYDEBUG if (yydebug) - printf("yydebug: after reduction, shifting from state 0 to\ - state %d\n", YYFINAL); + printf("%sdebug: after reduction, shifting from state 0 to\ + state %d\n", YYPREFIX, YYFINAL); #endif yystate = YYFINAL; *++yyssp = YYFINAL; @@ -1701,8 +1709,8 @@ break; yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; - printf("yydebug: state %d, reading %d (%s)\n", - YYFINAL, yychar, yys); + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, YYFINAL, yychar, yys); } #endif } @@ -1714,10 +1722,10 @@ break; yystate = yytable[yyn]; else yystate = yydgoto[yym]; -#ifdef YYDEBUG +#if YYDEBUG if (yydebug) - printf("yydebug: after reduction, shifting from state %d \ -to state %d\n", *yyssp, yystate); + printf("%sdebug: after reduction, shifting from state %d \ +to state %d\n", YYPREFIX, *yyssp, yystate); #endif if (yyssp >= yyss + yystacksize - 1) { diff --git a/usr.bin/yacc/test/ftp.y b/usr.bin/yacc/test/ftp.y new file mode 100644 index 0000000000..a9ee9cd95c --- /dev/null +++ b/usr.bin/yacc/test/ftp.y @@ -0,0 +1,1180 @@ +/* + * Copyright (c) 1985, 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89 + */ + +/* + * Grammar for FTP commands. + * See RFC 959. + */ + +%{ + +#ifndef lint +static char sccsid[] = "@(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <arpa/ftp.h> + +#include <stdio.h> +#include <signal.h> +#include <ctype.h> +#include <pwd.h> +#include <setjmp.h> +#include <syslog.h> +#include <sys/stat.h> +#include <time.h> + +extern struct sockaddr_in data_dest; +extern int logged_in; +extern struct passwd *pw; +extern int guest; +extern int logging; +extern int type; +extern int form; +extern int debug; +extern int timeout; +extern int maxtimeout; +extern int pdata; +extern char hostname[], remotehost[]; +extern char proctitle[]; +extern char *globerr; +extern int usedefault; +extern int transflag; +extern char tmpline[]; +char **glob(); + +static int cmd_type; +static int cmd_form; +static int cmd_bytesz; +char cbuf[512]; +char *fromname; + +char *index(); +%} + +%token + A B C E F I + L N P R S T + + SP CRLF COMMA STRING NUMBER + + USER PASS ACCT REIN QUIT PORT + PASV TYPE STRU MODE RETR STOR + APPE MLFL MAIL MSND MSOM MSAM + MRSQ MRCP ALLO REST RNFR RNTO + ABOR DELE CWD LIST NLST SITE + STAT HELP NOOP MKD RMD PWD + CDUP STOU SMNT SYST SIZE MDTM + + UMASK IDLE CHMOD + + LEXERR + +%start cmd_list + +%% + +cmd_list: /* empty */ + | cmd_list cmd + = { + fromname = (char *) 0; + } + | cmd_list rcmd + ; + +cmd: USER SP username CRLF + = { + user((char *) $3); + free((char *) $3); + } + | PASS SP password CRLF + = { + pass((char *) $3); + free((char *) $3); + } + | PORT SP host_port CRLF + = { + usedefault = 0; + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + reply(200, "PORT command successful."); + } + | PASV CRLF + = { + passive(); + } + | TYPE SP type_code CRLF + = { + switch (cmd_type) { + + case TYPE_A: + if (cmd_form == FORM_N) { + reply(200, "Type set to A."); + type = cmd_type; + form = cmd_form; + } else + reply(504, "Form must be N."); + break; + + case TYPE_E: + reply(504, "Type E not implemented."); + break; + + case TYPE_I: + reply(200, "Type set to I."); + type = cmd_type; + break; + + case TYPE_L: +#if NBBY == 8 + if (cmd_bytesz == 8) { + reply(200, + "Type set to L (byte size 8)."); + type = cmd_type; + } else + reply(504, "Byte size must be 8."); +#else /* NBBY == 8 */ + UNIMPLEMENTED for NBBY != 8 +#endif /* NBBY == 8 */ + } + } + | STRU SP struct_code CRLF + = { + switch ($3) { + + case STRU_F: + reply(200, "STRU F ok."); + break; + + default: + reply(504, "Unimplemented STRU type."); + } + } + | MODE SP mode_code CRLF + = { + switch ($3) { + + case MODE_S: + reply(200, "MODE S ok."); + break; + + default: + reply(502, "Unimplemented MODE type."); + } + } + | ALLO SP NUMBER CRLF + = { + reply(202, "ALLO command ignored."); + } + | ALLO SP NUMBER SP R SP NUMBER CRLF + = { + reply(202, "ALLO command ignored."); + } + | RETR check_login SP pathname CRLF + = { + if ($2 && $4 != NULL) + retrieve((char *) 0, (char *) $4); + if ($4 != NULL) + free((char *) $4); + } + | STOR check_login SP pathname CRLF + = { + if ($2 && $4 != NULL) + store((char *) $4, "w", 0); + if ($4 != NULL) + free((char *) $4); + } + | APPE check_login SP pathname CRLF + = { + if ($2 && $4 != NULL) + store((char *) $4, "a", 0); + if ($4 != NULL) + free((char *) $4); + } + | NLST check_login CRLF + = { + if ($2) + send_file_list("."); + } + | NLST check_login SP STRING CRLF + = { + if ($2 && $4 != NULL) + send_file_list((char *) $4); + if ($4 != NULL) + free((char *) $4); + } + | LIST check_login CRLF + = { + if ($2) + retrieve("/bin/ls -lgA", ""); + } + | LIST check_login SP pathname CRLF + = { + if ($2 && $4 != NULL) + retrieve("/bin/ls -lgA %s", (char *) $4); + if ($4 != NULL) + free((char *) $4); + } + | STAT check_login SP pathname CRLF + = { + if ($2 && $4 != NULL) + statfilecmd((char *) $4); + if ($4 != NULL) + free((char *) $4); + } + | STAT CRLF + = { + statcmd(); + } + | DELE check_login SP pathname CRLF + = { + if ($2 && $4 != NULL) + delete((char *) $4); + if ($4 != NULL) + free((char *) $4); + } + | RNTO SP pathname CRLF + = { + if (fromname) { + renamecmd(fromname, (char *) $3); + free(fromname); + fromname = (char *) 0; + } else { + reply(503, "Bad sequence of commands."); + } + free((char *) $3); + } + | ABOR CRLF + = { + reply(225, "ABOR command successful."); + } + | CWD check_login CRLF + = { + if ($2) + cwd(pw->pw_dir); + } + | CWD check_login SP pathname CRLF + = { + if ($2 && $4 != NULL) + cwd((char *) $4); + if ($4 != NULL) + free((char *) $4); + } + | HELP CRLF + = { + help(cmdtab, (char *) 0); + } + | HELP SP STRING CRLF + = { + register char *cp = (char *)$3; + + if (strncasecmp(cp, "SITE", 4) == 0) { + cp = (char *)$3 + 4; + if (*cp == ' ') + cp++; + if (*cp) + help(sitetab, cp); + else + help(sitetab, (char *) 0); + } else + help(cmdtab, (char *) $3); + } + | NOOP CRLF + = { + reply(200, "NOOP command successful."); + } + | MKD check_login SP pathname CRLF + = { + if ($2 && $4 != NULL) + makedir((char *) $4); + if ($4 != NULL) + free((char *) $4); + } + | RMD check_login SP pathname CRLF + = { + if ($2 && $4 != NULL) + removedir((char *) $4); + if ($4 != NULL) + free((char *) $4); + } + | PWD check_login CRLF + = { + if ($2) + pwd(); + } + | CDUP check_login CRLF + = { + if ($2) + cwd(".."); + } + | SITE SP HELP CRLF + = { + help(sitetab, (char *) 0); + } + | SITE SP HELP SP STRING CRLF + = { + help(sitetab, (char *) $5); + } + | SITE SP UMASK check_login CRLF + = { + int oldmask; + + if ($4) { + oldmask = umask(0); + (void) umask(oldmask); + reply(200, "Current UMASK is %03o", oldmask); + } + } + | SITE SP UMASK check_login SP octal_number CRLF + = { + int oldmask; + + if ($4) { + if (($6 == -1) || ($6 > 0777)) { + reply(501, "Bad UMASK value"); + } else { + oldmask = umask($6); + reply(200, + "UMASK set to %03o (was %03o)", + $6, oldmask); + } + } + } + | SITE SP CHMOD check_login SP octal_number SP pathname CRLF + = { + if ($4 && ($8 != NULL)) { + if ($6 > 0777) + reply(501, + "CHMOD: Mode value must be between 0 and 0777"); + else if (chmod((char *) $8, $6) < 0) + perror_reply(550, (char *) $8); + else + reply(200, "CHMOD command successful."); + } + if ($8 != NULL) + free((char *) $8); + } + | SITE SP IDLE CRLF + = { + reply(200, + "Current IDLE time limit is %d seconds; max %d", + timeout, maxtimeout); + } + | SITE SP IDLE SP NUMBER CRLF + = { + if ($5 < 30 || $5 > maxtimeout) { + reply(501, + "Maximum IDLE time must be between 30 and %d seconds", + maxtimeout); + } else { + timeout = $5; + (void) alarm((unsigned) timeout); + reply(200, + "Maximum IDLE time set to %d seconds", + timeout); + } + } + | STOU check_login SP pathname CRLF + = { + if ($2 && $4 != NULL) + store((char *) $4, "w", 1); + if ($4 != NULL) + free((char *) $4); + } + | SYST CRLF + = { +#ifdef unix +#ifdef BSD + reply(215, "UNIX Type: L%d Version: BSD-%d", + NBBY, BSD); +#else /* BSD */ + reply(215, "UNIX Type: L%d", NBBY); +#endif /* BSD */ +#else /* unix */ + reply(215, "UNKNOWN Type: L%d", NBBY); +#endif /* unix */ + } + + /* + * SIZE is not in RFC959, but Postel has blessed it and + * it will be in the updated RFC. + * + * Return size of file in a format suitable for + * using with RESTART (we just count bytes). + */ + | SIZE check_login SP pathname CRLF + = { + if ($2 && $4 != NULL) + sizecmd((char *) $4); + if ($4 != NULL) + free((char *) $4); + } + + /* + * MDTM is not in RFC959, but Postel has blessed it and + * it will be in the updated RFC. + * + * Return modification time of file as an ISO 3307 + * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx + * where xxx is the fractional second (of any precision, + * not necessarily 3 digits) + */ + | MDTM check_login SP pathname CRLF + = { + if ($2 && $4 != NULL) { + struct stat stbuf; + if (stat((char *) $4, &stbuf) < 0) + perror_reply(550, "%s", (char *) $4); + else if ((stbuf.st_mode&S_IFMT) != S_IFREG) { + reply(550, "%s: not a plain file.", + (char *) $4); + } else { + register struct tm *t; + struct tm *gmtime(); + t = gmtime(&stbuf.st_mtime); + reply(213, + "19%02d%02d%02d%02d%02d%02d", + t->tm_year, t->tm_mon+1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + } + } + if ($4 != NULL) + free((char *) $4); + } + | QUIT CRLF + = { + reply(221, "Goodbye."); + dologout(0); + } + | error CRLF + = { + yyerrok; + } + ; +rcmd: RNFR check_login SP pathname CRLF + = { + char *renamefrom(); + + if ($2 && $4) { + fromname = renamefrom((char *) $4); + if (fromname == (char *) 0 && $4) { + free((char *) $4); + } + } + } + ; + +username: STRING + ; + +password: /* empty */ + = { + *(char **)&($$) = ""; + } + | STRING + ; + +byte_size: NUMBER + ; + +host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER + = { + register char *a, *p; + + a = (char *)&data_dest.sin_addr; + a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; + p = (char *)&data_dest.sin_port; + p[0] = $9; p[1] = $11; + data_dest.sin_family = AF_INET; + } + ; + +form_code: N + = { + $$ = FORM_N; + } + | T + = { + $$ = FORM_T; + } + | C + = { + $$ = FORM_C; + } + ; + +type_code: A + = { + cmd_type = TYPE_A; + cmd_form = FORM_N; + } + | A SP form_code + = { + cmd_type = TYPE_A; + cmd_form = $3; + } + | E + = { + cmd_type = TYPE_E; + cmd_form = FORM_N; + } + | E SP form_code + = { + cmd_type = TYPE_E; + cmd_form = $3; + } + | I + = { + cmd_type = TYPE_I; + } + | L + = { + cmd_type = TYPE_L; + cmd_bytesz = NBBY; + } + | L SP byte_size + = { + cmd_type = TYPE_L; + cmd_bytesz = $3; + } + /* this is for a bug in the BBN ftp */ + | L byte_size + = { + cmd_type = TYPE_L; + cmd_bytesz = $2; + } + ; + +struct_code: F + = { + $$ = STRU_F; + } + | R + = { + $$ = STRU_R; + } + | P + = { + $$ = STRU_P; + } + ; + +mode_code: S + = { + $$ = MODE_S; + } + | B + = { + $$ = MODE_B; + } + | C + = { + $$ = MODE_C; + } + ; + +pathname: pathstring + = { + /* + * Problem: this production is used for all pathname + * processing, but only gives a 550 error reply. + * This is a valid reply in some cases but not in others. + */ + if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { + *(char **)&($$) = *glob((char *) $1); + if (globerr != NULL) { + reply(550, globerr); + $$ = NULL; + } + free((char *) $1); + } else + $$ = $1; + } + ; + +pathstring: STRING + ; + +octal_number: NUMBER + = { + register int ret, dec, multby, digit; + + /* + * Convert a number that was read as decimal number + * to what it would be if it had been read as octal. + */ + dec = $1; + multby = 1; + ret = 0; + while (dec) { + digit = dec%10; + if (digit > 7) { + ret = -1; + break; + } + ret += digit * multby; + multby *= 8; + dec /= 10; + } + $$ = ret; + } + ; + +check_login: /* empty */ + = { + if (logged_in) + $$ = 1; + else { + reply(530, "Please login with USER and PASS."); + $$ = 0; + } + } + ; + +%% + +extern jmp_buf errcatch; + +#define CMD 0 /* beginning of command */ +#define ARGS 1 /* expect miscellaneous arguments */ +#define STR1 2 /* expect SP followed by STRING */ +#define STR2 3 /* expect STRING */ +#define OSTR 4 /* optional SP then STRING */ +#define ZSTR1 5 /* SP then optional STRING */ +#define ZSTR2 6 /* optional STRING after SP */ +#define SITECMD 7 /* SITE command */ +#define NSTR 8 /* Number followed by a string */ + +struct tab { + char *name; + short token; + short state; + short implemented; /* 1 if command is implemented */ + char *help; +}; + +struct tab cmdtab[] = { /* In order defined in RFC 765 */ + { "USER", USER, STR1, 1, "<sp> username" }, + { "PASS", PASS, ZSTR1, 1, "<sp> password" }, + { "ACCT", ACCT, STR1, 0, "(specify account)" }, + { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, + { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, + { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, + { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, + { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, + { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, + { "STRU", STRU, ARGS, 1, "(specify file structure)" }, + { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, + { "RETR", RETR, STR1, 1, "<sp> file-name" }, + { "STOR", STOR, STR1, 1, "<sp> file-name" }, + { "APPE", APPE, STR1, 1, "<sp> file-name" }, + { "MLFL", MLFL, OSTR, 0, "(mail file)" }, + { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, + { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, + { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, + { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, + { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, + { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, + { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, + { "REST", REST, ARGS, 0, "(restart command)" }, + { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, + { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, + { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, + { "DELE", DELE, STR1, 1, "<sp> file-name" }, + { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, + { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, + { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, + { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, + { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, + { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, + { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, + { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, + { "NOOP", NOOP, ARGS, 1, "" }, + { "MKD", MKD, STR1, 1, "<sp> path-name" }, + { "XMKD", MKD, STR1, 1, "<sp> path-name" }, + { "RMD", RMD, STR1, 1, "<sp> path-name" }, + { "XRMD", RMD, STR1, 1, "<sp> path-name" }, + { "PWD", PWD, ARGS, 1, "(return current directory)" }, + { "XPWD", PWD, ARGS, 1, "(return current directory)" }, + { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, + { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, + { "STOU", STOU, STR1, 1, "<sp> file-name" }, + { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, + { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, + { NULL, 0, 0, 0, 0 } +}; + +struct tab sitetab[] = { + { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, + { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, + { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, + { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, + { NULL, 0, 0, 0, 0 } +}; + +struct tab * +lookup(p, cmd) + register struct tab *p; + char *cmd; +{ + + for (; p->name != NULL; p++) + if (strcmp(cmd, p->name) == 0) + return (p); + return (0); +} + +#include <arpa/telnet.h> + +/* + * getline - a hacked up version of fgets to ignore TELNET escape codes. + */ +char * +getline(s, n, iop) + char *s; + register FILE *iop; +{ + register c; + register char *cs; + + cs = s; +/* tmpline may contain saved command from urgent mode interruption */ + for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { + *cs++ = tmpline[c]; + if (tmpline[c] == '\n') { + *cs++ = '\0'; + if (debug) + syslog(LOG_DEBUG, "command: %s", s); + tmpline[0] = '\0'; + return(s); + } + if (c == 0) + tmpline[0] = '\0'; + } + while ((c = getc(iop)) != EOF) { + c &= 0377; + if (c == IAC) { + if ((c = getc(iop)) != EOF) { + c &= 0377; + switch (c) { + case WILL: + case WONT: + c = getc(iop); + printf("%c%c%c", IAC, DONT, 0377&c); + (void) fflush(stdout); + continue; + case DO: + case DONT: + c = getc(iop); + printf("%c%c%c", IAC, WONT, 0377&c); + (void) fflush(stdout); + continue; + case IAC: + break; + default: + continue; /* ignore command */ + } + } + } + *cs++ = c; + if (--n <= 0 || c == '\n') + break; + } + if (c == EOF && cs == s) + return (NULL); + *cs++ = '\0'; + if (debug) + syslog(LOG_DEBUG, "command: %s", s); + return (s); +} + +static int +toolong() +{ + time_t now; + extern char *ctime(); + extern time_t time(); + + reply(421, + "Timeout (%d seconds): closing control connection.", timeout); + (void) time(&now); + if (logging) { + syslog(LOG_INFO, + "User %s timed out after %d seconds at %s", + (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); + } + dologout(1); +} + +yylex() +{ + static int cpos, state; + register char *cp, *cp2; + register struct tab *p; + int n; + char c, *strpbrk(); + char *copy(); + + for (;;) { + switch (state) { + + case CMD: + (void) signal(SIGALRM, toolong); + (void) alarm((unsigned) timeout); + if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { + reply(221, "You could at least say goodbye."); + dologout(0); + } + (void) alarm(0); +#ifdef SETPROCTITLE + if (strncasecmp(cbuf, "PASS", 4) != NULL) + setproctitle("%s: %s", proctitle, cbuf); +#endif /* SETPROCTITLE */ + if ((cp = index(cbuf, '\r'))) { + *cp++ = '\n'; + *cp = '\0'; + } + if ((cp = strpbrk(cbuf, " \n"))) + cpos = cp - cbuf; + if (cpos == 0) + cpos = 4; + c = cbuf[cpos]; + cbuf[cpos] = '\0'; + upper(cbuf); + p = lookup(cmdtab, cbuf); + cbuf[cpos] = c; + if (p != 0) { + if (p->implemented == 0) { + nack(p->name); + longjmp(errcatch,0); + /* NOTREACHED */ + } + state = p->state; + *(char **)&yylval = p->name; + return (p->token); + } + break; + + case SITECMD: + if (cbuf[cpos] == ' ') { + cpos++; + return (SP); + } + cp = &cbuf[cpos]; + if ((cp2 = strpbrk(cp, " \n"))) + cpos = cp2 - cbuf; + c = cbuf[cpos]; + cbuf[cpos] = '\0'; + upper(cp); + p = lookup(sitetab, cp); + cbuf[cpos] = c; + if (p != 0) { + if (p->implemented == 0) { + state = CMD; + nack(p->name); + longjmp(errcatch,0); + /* NOTREACHED */ + } + state = p->state; + *(char **)&yylval = p->name; + return (p->token); + } + state = CMD; + break; + + case OSTR: + if (cbuf[cpos] == '\n') { + state = CMD; + return (CRLF); + } + /* FALLTHROUGH */ + + case STR1: + case ZSTR1: + dostr1: + if (cbuf[cpos] == ' ') { + cpos++; + state = state == OSTR ? STR2 : ++state; + return (SP); + } + break; + + case ZSTR2: + if (cbuf[cpos] == '\n') { + state = CMD; + return (CRLF); + } + /* FALLTHROUGH */ + + case STR2: + cp = &cbuf[cpos]; + n = strlen(cp); + cpos += n - 1; + /* + * Make sure the string is nonempty and \n terminated. + */ + if (n > 1 && cbuf[cpos] == '\n') { + cbuf[cpos] = '\0'; + *(char **)&yylval = copy(cp); + cbuf[cpos] = '\n'; + state = ARGS; + return (STRING); + } + break; + + case NSTR: + if (cbuf[cpos] == ' ') { + cpos++; + return (SP); + } + if (isdigit(cbuf[cpos])) { + cp = &cbuf[cpos]; + while (isdigit(cbuf[++cpos])) + ; + c = cbuf[cpos]; + cbuf[cpos] = '\0'; + yylval = atoi(cp); + cbuf[cpos] = c; + state = STR1; + return (NUMBER); + } + state = STR1; + goto dostr1; + + case ARGS: + if (isdigit(cbuf[cpos])) { + cp = &cbuf[cpos]; + while (isdigit(cbuf[++cpos])) + ; + c = cbuf[cpos]; + cbuf[cpos] = '\0'; + yylval = atoi(cp); + cbuf[cpos] = c; + return (NUMBER); + } + switch (cbuf[cpos++]) { + + case '\n': + state = CMD; + return (CRLF); + + case ' ': + return (SP); + + case ',': + return (COMMA); + + case 'A': + case 'a': + return (A); + + case 'B': + case 'b': + return (B); + + case 'C': + case 'c': + return (C); + + case 'E': + case 'e': + return (E); + + case 'F': + case 'f': + return (F); + + case 'I': + case 'i': + return (I); + + case 'L': + case 'l': + return (L); + + case 'N': + case 'n': + return (N); + + case 'P': + case 'p': + return (P); + + case 'R': + case 'r': + return (R); + + case 'S': + case 's': + return (S); + + case 'T': + case 't': + return (T); + + } + break; + + default: + fatal("Unknown state in scanner."); + } + yyerror((char *) 0); + state = CMD; + longjmp(errcatch,0); + } +} + +upper(s) + register char *s; +{ + while (*s != '\0') { + if (islower(*s)) + *s = toupper(*s); + s++; + } +} + +char * +copy(s) + char *s; +{ + char *p; + extern char *malloc(), *strcpy(); + + p = malloc((unsigned) strlen(s) + 1); + if (p == NULL) + fatal("Ran out of memory."); + (void) strcpy(p, s); + return (p); +} + +help(ctab, s) + struct tab *ctab; + char *s; +{ + register struct tab *c; + register int width, NCMDS; + char *type; + + if (ctab == sitetab) + type = "SITE "; + else + type = ""; + width = 0, NCMDS = 0; + for (c = ctab; c->name != NULL; c++) { + int len = strlen(c->name); + + if (len > width) + width = len; + NCMDS++; + } + width = (width + 8) &~ 7; + if (s == 0) { + register int i, j, w; + int columns, lines; + + lreply(214, "The following %scommands are recognized %s.", + type, "(* =>'s unimplemented)"); + columns = 76 / width; + if (columns == 0) + columns = 1; + lines = (NCMDS + columns - 1) / columns; + for (i = 0; i < lines; i++) { + printf(" "); + for (j = 0; j < columns; j++) { + c = ctab + j * lines + i; + printf("%s%c", c->name, + c->implemented ? ' ' : '*'); + if (c + lines >= &ctab[NCMDS]) + break; + w = strlen(c->name) + 1; + while (w < width) { + putchar(' '); + w++; + } + } + printf("\r\n"); + } + (void) fflush(stdout); + reply(214, "Direct comments to ftp-bugs@%s.", hostname); + return; + } + upper(s); + c = lookup(ctab, s); + if (c == (struct tab *)0) { + reply(502, "Unknown command %s.", s); + return; + } + if (c->implemented) + reply(214, "Syntax: %s%s %s", type, c->name, c->help); + else + reply(214, "%s%-*s\t%s; unimplemented.", type, width, + c->name, c->help); +} + +sizecmd(filename) +char *filename; +{ + switch (type) { + case TYPE_L: + case TYPE_I: { + struct stat stbuf; + if (stat(filename, &stbuf) < 0 || + (stbuf.st_mode&S_IFMT) != S_IFREG) + reply(550, "%s: not a plain file.", filename); + else + reply(213, "%lu", stbuf.st_size); + break;} + case TYPE_A: { + FILE *fin; + register int c, count; + struct stat stbuf; + fin = fopen(filename, "r"); + if (fin == NULL) { + perror_reply(550, filename); + return; + } + if (fstat(fileno(fin), &stbuf) < 0 || + (stbuf.st_mode&S_IFMT) != S_IFREG) { + reply(550, "%s: not a plain file.", filename); + (void) fclose(fin); + return; + } + + count = 0; + while((c=getc(fin)) != EOF) { + if (c == '\n') /* will get expanded to \r\n */ + count++; + count++; + } + (void) fclose(fin); + + reply(213, "%ld", count); + break;} + default: + reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); + } +} diff --git a/usr.bin/yacc/verbose.c b/usr.bin/yacc/verbose.c index 33ae265ee2..2c7cc52c77 100644 --- a/usr.bin/yacc/verbose.c +++ b/usr.bin/yacc/verbose.c @@ -1,45 +1,7 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Robert Paul Corbett. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)verbose.c 5.3 (Berkeley) 1/20/91"; -#endif /* not lint */ #include "defs.h" + static short *null_rules; verbose() @@ -364,3 +326,4 @@ int stateno; fprintf(verbose_file, "\t%s goto %d\n", symbol_name[as], k); } } + diff --git a/usr.bin/yacc/warshall.c b/usr.bin/yacc/warshall.c index 69193c3191..b449e4e274 100644 --- a/usr.bin/yacc/warshall.c +++ b/usr.bin/yacc/warshall.c @@ -1,43 +1,3 @@ -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Robert Paul Corbett. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)warshall.c 5.3 (Berkeley) 6/1/90"; -#endif /* not lint */ - #include "defs.h" transitive_closure(R, n) @@ -45,7 +5,7 @@ unsigned *R; int n; { register int rowsize; - register unsigned mask; + register unsigned i; register unsigned *rowj; register unsigned *rp; register unsigned *rend; @@ -58,7 +18,7 @@ int n; relend = R + n*rowsize; cword = R; - mask = 1; + i = 0; rowi = R; while (rowi < relend) { @@ -67,7 +27,7 @@ int n; while (rowj < relend) { - if (*ccol & mask) + if (*ccol & (1 << i)) { rp = rowi; rend = rowj + rowsize; @@ -82,10 +42,9 @@ int n; ccol += rowsize; } - mask <<= 1; - if (mask == 0) + if (++i >= BITS_PER_WORD) { - mask = 1; + i = 0; cword++; } @@ -98,7 +57,7 @@ unsigned *R; int n; { register int rowsize; - register unsigned mask; + register unsigned i; register unsigned *rp; register unsigned *relend; @@ -107,15 +66,14 @@ int n; rowsize = WORDSIZE(n); relend = R + n*rowsize; - mask = 1; + i = 0; rp = R; while (rp < relend) { - *rp |= mask; - mask <<= 1; - if (mask == 0) + *rp |= (1 << i); + if (++i >= BITS_PER_WORD) { - mask = 1; + i = 0; rp++; } diff --git a/usr.bin/yacc/yacc.1 b/usr.bin/yacc/yacc.1 index 5b8d79765d..742633d595 100644 --- a/usr.bin/yacc/yacc.1 +++ b/usr.bin/yacc/yacc.1 @@ -1,174 +1,111 @@ -.\" Copyright (c) 1989, 1990 The Regents of the University of California. -.\" All rights reserved. +.\" %W% %R% (Berkeley) %E% .\" -.\" This code is derived from software contributed to Berkeley by -.\" Robert Paul Corbett. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)yacc.1 5.7 (Berkeley) 7/30/91 -.\" -.Dd July 30, 1991 -.Dt YACC 1 -.Os -.Sh NAME -.Nm yacc -.Nd an -.Tn LALR(1) -parser generator -.Sh SYNOPSIS -.Nm yacc -.Op Fl dlrtv -.Op Fl b Ar prefix -.Ar filename -.Sh DESCRIPTION -.Nm Yacc +.TH YACC 1 "July\ 15,\ 1990" +.UC 6 +.SH NAME +Yacc \- an LALR(1) parser generator +.SH SYNOPSIS +.B yacc [ -dlrtv ] [ -b +.I file_prefix +.B ] [ -p +.I symbol_prefix +.B ] +.I filename +.SH DESCRIPTION +.I Yacc reads the grammar specification in the file -.Ar filename -and generates an -.Tn LR(1) -parser for it. -The parsers consist of a set of -.Tn LALR(1) -parsing tables and a driver routine +.I filename +and generates an LR(1) parser for it. +The parsers consist of a set of LALR(1) parsing tables and a driver routine written in the C programming language. -.Nm Yacc +.I Yacc normally writes the parse tables and the driver routine to the file -.Pa y.tab.c . -.Pp +.IR y.tab.c. +.PP The following options are available: -.Bl -tag -width Ar -.It Fl b Ar prefix +.RS +.TP +\fB-b \fIfile_prefix\fR The -.Fl b +.B -b option changes the prefix prepended to the output file names to the string denoted by -.Ar prefix . +.IR file_prefix. The default prefix is the character -.Ar y . -.It Fl d -The -.Fl d -option causes the header file -.Pa y.tab.h +.IR y. +.TP +.B -d +The \fB-d\fR option causes the header file +.IR y.tab.h to be written. -.It Fl l +.TP +.B -l If the -.Fl l +.B -l option is not specified, -.Nm yacc +.I yacc will insert \#line directives in the generated code. The \#line directives let the C compiler relate errors in the generated code to the user's original code. -If the -.Fl l -option is specified, -.Nm yacc +If the \fB-l\fR option is specified, +.I yacc will not insert the \#line directives. \&\#line directives specified by the user will be retained. -.It Fl r +.TP +\fB-p \fIsymbol_prefix\fR +The +.B -p +option changes the prefix prepended to yacc-generated symbols to +the string denoted by +.IR symbol_prefix. +The default prefix is the string +.IR yy. +.TP +.B -r The -.Fl r +.B -r option causes -.Nm yacc -to produce separate files for code and tables. -The code file is named -.Pa y.code.c , +.I yacc +to produce separate files for code and tables. The code file +is named +.IR y.code.c, and the tables file is named -.Pa y.tab.c . -.It Fl t +.IR y.tab.c. +.TP +.B -t The -.Fl t +.B -t option changes the preprocessor directives generated by -.Nm yacc +.I yacc so that debugging statements will be incorporated in the compiled code. -.It Fl v +.TP +.B -v The -.Fl v +.B -v option causes a human-readable description of the generated parser to be written to the file -.Pa y.output . -.Pp -.Sh ENVIRONMENT -The following environment variable is referenced by -.Nm yacc : -.Bl -tag -width TMPDIR -.It Ev TMPDIR -If the environment variable -.Ev TMPDIR -is set, the string denoted by -.Ev TMPDIR -will be used as the name of the directory where the temporary +.IR y.output. +.RE +.PP +If the environment variable TMPDIR is set, the string denoted by +TMPDIR will be used as the name of the directory where the temporary files are created. -.El -.Sh TABLES -The names of the tables generated by this version of -.Nm yacc -are -.Dq yylhs , -.Dq yylen , -.Dq yydefred , -.Dq yydgoto , -.Dq yysindex , -.Dq yyrindex , -.Dq yygindex , -.Dq yytable , -and -.Dq yycheck . -Two additional tables, -.Dq yyname -and -.Dq yyrule , -are created if -.Dv YYDEBUG -is defined and non-zero. -.Sh FILES -.Bl -tag -width /tmp/yacc.uXXXXXXXX -compact -.It Pa y.code.c -.It Pa y.tab.c -.It Pa y.tab.h -.It Pa y.output -.It Pa /tmp/yacc.aXXXXXX -.It Pa /tmp/yacc.tXXXXXX -.It Pa /tmp/yacc.uXXXXXX -.El -.Sh DIAGNOSTICS +.SH FILES +.IR y.code.c +.br +.IR y.tab.c +.br +.IR y.tab.h +.br +.IR y.output +.br +.IR /tmp/yacc.aXXXXXX +.br +.IR /tmp/yacc.tXXXXXX +.br +.IR /tmp/yacc.uXXXXXX +.SH DIAGNOSTICS If there are rules that are never reduced, the number of such rules is -written to the standard error. -If there are any -.Tn LALR(1) -conflicts, the number of conflicts is also written -to the standard error. -.Sh SEE ALSO -.Xr yyfix 1 -.Sh STANDARDS -The -.Nm yacc -utility conforms to -.St -p1003.2 . +reported on standard error. +If there are any LALR(1) conflicts, the number of conflicts is reported +on standard error. diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 36b13bce61..c5525dabfa 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -1,20 +1,10 @@ # @(#)Makefile 5.6.1.2 (Berkeley) 5/8/91 -# -# PATCHES MAGIC LEVEL PATCH THAT GOT US HERE -# -------------------- ----- ---------------------- -# CURRENT PATCH LEVEL: 3 00162 -# -------------------- ----- ---------------------- -# -# 09 Mar 93 Rodney W. Grimes Added rwhod to SUBDIR, moved portmap -# into alphabetic order. -# 03 Mar 93 Rodney W. Grimes Added 11 new programs from Net/2 -# 26 May 93 Rodney W. Grimes Added keymap for codrv -# SUBDIR= accton arp chown chroot config dbsym dev_mkdb diskpart \ - edquota flcopy gettable htable inetd iostat keymap kgmon kvm_mkdb lpr \ - mtree named portmap pwd_mkdb quotaon repquota rmt rwhod\ - sendmail sliplogin syslogd timed traceroute trpt trsp update vipw + edquota flcopy gettable htable inetd iostat kgmon kvm_mkdb lpr \ + mtree named portmap pppstats pwd_mkdb quotaon repquota rmt rwhod\ + sendmail sliplogin swapinfo syslogd timed traceroute trpt trsp \ + update vipw .if make(clean) || make(cleandir) SUBDIR+=bad144 diff --git a/usr.sbin/XNSrouted/Makefile b/usr.sbin/XNSrouted/Makefile new file mode 100644 index 0000000000..1722c6fed4 --- /dev/null +++ b/usr.sbin/XNSrouted/Makefile @@ -0,0 +1,10 @@ +# From: @(#)Makefile 5.14 (Berkeley) 2/26/91 +# $Id$ + +PROG= XNSrouted +MAN8= XNSrouted.8 +SRCS= af.c if.c input.c main.c output.c startup.c tables.c timer.c trace.c +DPADD= ${LIBUTIL} +LDADD= -lutil + +.include <bsd.prog.mk> diff --git a/usr.sbin/XNSrouted/XNSrouted.8 b/usr.sbin/XNSrouted/XNSrouted.8 new file mode 100644 index 0000000000..144cb1d404 --- /dev/null +++ b/usr.sbin/XNSrouted/XNSrouted.8 @@ -0,0 +1,186 @@ +.\" Copyright (c) 1986, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)XNSrouted.8 6.4 (Berkeley) 3/16/91 +.\" +.Dd March 16, 1991 +.Dt XNSROUTED 8 +.Os BSD 4.3 +.Sh NAME +.Nm XNSrouted +.Nd NS Routing Information Protocol daemon +.Sh SYNOPSIS +.Nm XNSrouted +.Op Fl q +.Op Fl s +.Op Fl t +.Op Ar logfile +.Sh DESCRIPTION +.Nm XNSrouted +is invoked at boot time to manage the Xerox NS routing tables. +The NS routing daemon uses the Xerox NS Routing +Information Protocol in maintaining up to date kernel routing +table entries. +.Pp +Available options: +.Bl -tag -width logfile +.It Fl q +Do not supply routing information (opposite of +.Fl s +option below). +.It Fl s +Forces +.Nm XNSrouted +to supply routing information whether it is acting as an internetwork +router or not. +.It Fl t +All packets sent or received are +printed on the standard output. In addition, +.Nm XNSrouted +will not divorce itself from the controlling terminal +so that interrupts from the keyboard will kill the process. +.It Ar logfile +Name of file in which +.Nm XNSrouted Ns 's +actions should be logged. This log contains information +about any changes to the routing tables and a history of +recent messages sent and received which are related to +the changed route. +.El +.Pp +In normal operation +.Nm XNSrouted +listens +for routing information packets. If the host is connected to +multiple NS networks, it periodically supplies copies +of its routing tables to any directly connected hosts +and networks. +.Pp +When +.Nm XNSrouted +is started, it uses the +.Dv SIOCGIFCONF +.Xr ioctl 2 +to find those +directly connected interfaces configured into the +system and marked +.Dq up +(the software loopback interface +is ignored). If multiple interfaces +are present, it is assumed the host will forward packets +between networks. +.Nm XNSrouted +then transmits a +.Em request +packet on each interface (using a broadcast packet if +the interface supports it) and enters a loop, listening +for +.Em request +and +.Em response +packets from other hosts. +.Pp +When a +.Em request +packet is received, +.Nm XNSrouted +formulates a reply based on the information maintained in its +internal tables. The +.Em response +packet generated contains a list of known routes, each marked +with a +.Dq hop count +metric (a count of 16, or greater, is +considered +.Dq infinite ) . +The metric associated with each +route returned provides a metric +.Em relative to the sender . +.Pp +.Em Response +packets received by +.Nm XNSrouted +are used to update the routing tables if one of the following +conditions is satisfied: +.Bl -bullet +.It +No routing table entry exists for the destination network +or host, and the metric indicates the destination is ``reachable'' +(i.e. the hop count is not infinite). +.It +The source host of the packet is the same as the router in the +existing routing table entry. That is, updated information is +being received from the very internetwork router through which +packets for the destination are being routed. +.It +The existing entry in the routing table has not been updated for +some time (defined to be 90 seconds) and the route is at least +as cost effective as the current route. +.It +The new route describes a shorter route to the destination than +the one currently stored in the routing tables; the metric of +the new route is compared against the one stored in the table +to decide this. +.El +.Pp +When an update is applied, +.Nm XNSrouted +records the change in its internal tables and generates a +.Em response +packet to all directly connected hosts and networks. +.Xr Routed 8 +waits a short period +of time (no more than 30 seconds) before modifying the kernel's +routing tables to allow possible unstable situations to settle. +.Pp +In addition to processing incoming packets, +.Nm XNSrouted +also periodically checks the routing table entries. +If an entry has not been updated for 3 minutes, the entry's metric +is set to infinity and marked for deletion. Deletions are delayed +an additional 60 seconds to insure the invalidation is propagated +to other routers. +.Pp +Hosts acting as internetwork routers gratuitously supply their +routing tables every 30 seconds to all directly connected hosts +and networks. +.Sh SEE ALSO +.Xr idp 4 +.Rs +.%T "Internet Transport Protocols" +.%R "XSIS 028112" +.%Q "Xerox System Integration Standard" +.Re +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/usr.sbin/XNSrouted/af.c b/usr.sbin/XNSrouted/af.c new file mode 100644 index 0000000000..d131e87739 --- /dev/null +++ b/usr.sbin/XNSrouted/af.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)af.c 5.11 (Berkeley) 2/26/91"; +#endif /* not lint */ + +#include "defs.h" + +/* + * Address family support routines + */ +int null_hash(), null_netmatch(), null_output(), + null_portmatch(), null_portcheck(), + null_checkhost(), null_ishost(), null_canon(); +int xnnet_hash(), xnnet_netmatch(), xnnet_output(), + xnnet_portmatch(), + xnnet_checkhost(), xnnet_ishost(), xnnet_canon(); +#define NIL \ + { null_hash, null_netmatch, null_output, \ + null_portmatch, null_portcheck, null_checkhost, \ + null_ishost, null_canon } +#define XNSNET \ + { xnnet_hash, xnnet_netmatch, xnnet_output, \ + xnnet_portmatch, xnnet_portmatch, xnnet_checkhost, \ + xnnet_ishost, xnnet_canon } + +struct afswitch afswitch[AF_MAX] = + { NIL, NIL, NIL, NIL, NIL, NIL, XNSNET, NIL, NIL, NIL, NIL }; + +struct sockaddr_ns xnnet_default = { sizeof(struct sockaddr_ns), AF_NS }; + +union ns_net ns_anynet; +union ns_net ns_zeronet; + +xnnet_hash(sns, hp) + register struct sockaddr_ns *sns; + struct afhash *hp; +{ + register long hash = 0; + register u_short *s = sns->sns_addr.x_host.s_host; + union ns_net_u net; + + net.net_e = sns->sns_addr.x_net; + hp->afh_nethash = net.long_e; + hash = *s++; hash <<= 8; hash += *s++; hash <<= 8; hash += *s; + hp->afh_hosthash = hash; +} + +xnnet_netmatch(sxn1, sxn2) + struct sockaddr_ns *sxn1, *sxn2; +{ + return (ns_neteq(sxn1->sns_addr, sxn2->sns_addr)); +} + +/* + * Verify the message is from the right port. + */ +xnnet_portmatch(sns) + register struct sockaddr_ns *sns; +{ + + return (ntohs(sns->sns_addr.x_port) == IDPPORT_RIF ); +} + + +/* + * xns output routine. + */ +#ifdef DEBUG +int do_output = 0; +#endif +xnnet_output(flags, sns, size) + int flags; + struct sockaddr_ns *sns; + int size; +{ + struct sockaddr_ns dst; + + dst = *sns; + sns = &dst; + if (sns->sns_addr.x_port == 0) + sns->sns_addr.x_port = htons(IDPPORT_RIF); +#ifdef DEBUG + if(do_output || ntohs(msg->rip_cmd) == RIPCMD_REQUEST) +#endif + /* + * Kludge to allow us to get routes out to machines that + * don't know their addresses yet; send to that address on + * ALL connected nets + */ + if (ns_neteqnn(sns->sns_addr.x_net, ns_zeronet)) { + extern struct interface *ifnet; + register struct interface *ifp; + + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + sns->sns_addr.x_net = + satons_addr(ifp->int_addr).x_net; + (void) sendto(s, msg, size, flags, + (struct sockaddr *)sns, sizeof (*sns)); + } + return; + } + + (void) sendto(s, msg, size, flags, + (struct sockaddr *)sns, sizeof (*sns)); +} + +/* + * Return 1 if we want this route. + * We use this to disallow route net G entries for one for multiple + * point to point links. + */ +xnnet_checkhost(sns) + struct sockaddr_ns *sns; +{ + register struct interface *ifp = if_ifwithnet(sns); + /* + * We want this route if there is no more than one + * point to point interface with this network. + */ + if (ifp == 0 || (ifp->int_flags & IFF_POINTOPOINT)==0) return (1); + return (ifp->int_sq.n == ifp->int_sq.p); +} + +/* + * Return 1 if the address is + * for a host, 0 for a network. + */ +xnnet_ishost(sns) +struct sockaddr_ns *sns; +{ + register u_short *s = sns->sns_addr.x_host.s_host; + + if ((s[0]==0xffff) && (s[1]==0xffff) && (s[2]==0xffff)) + return (0); + else + return (1); +} + +xnnet_canon(sns) + struct sockaddr_ns *sns; +{ + + sns->sns_addr.x_port = 0; +} + +/*ARGSUSED*/ +null_hash(addr, hp) + struct sockaddr *addr; + struct afhash *hp; +{ + + hp->afh_nethash = hp->afh_hosthash = 0; +} + +/*ARGSUSED*/ +null_netmatch(a1, a2) + struct sockaddr *a1, *a2; +{ + + return (0); +} + +/*ARGSUSED*/ +null_output(s, f, a1, n) + int s, f; + struct sockaddr *a1; + int n; +{ + + ; +} + +/*ARGSUSED*/ +null_portmatch(a1) + struct sockaddr *a1; +{ + + return (0); +} + +/*ARGSUSED*/ +null_portcheck(a1) + struct sockaddr *a1; +{ + + return (0); +} + +/*ARGSUSED*/ +null_ishost(a1) + struct sockaddr *a1; +{ + + return (0); +} + +/*ARGSUSED*/ +null_checkhost(a1) + struct sockaddr *a1; +{ + + return (0); +} + +/*ARGSUSED*/ +null_canon(a1) + struct sockaddr *a1; +{ + + ; +} diff --git a/usr.sbin/XNSrouted/af.h b/usr.sbin/XNSrouted/af.h new file mode 100644 index 0000000000..d2b27c5313 --- /dev/null +++ b/usr.sbin/XNSrouted/af.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)af.h 5.1 (Berkeley) 6/4/85 (routed/af.h) + * + * @(#)af.h 5.2 (Berkeley) 6/1/90 + */ + +/* + * Routing table management daemon. + */ + +/* + * Per address family routines. + */ +struct afswitch { + int (*af_hash)(); /* returns keys based on address */ + int (*af_netmatch)(); /* verifies net # matching */ + int (*af_output)(); /* interprets address for sending */ + int (*af_portmatch)(); /* packet from some other router? */ + int (*af_portcheck)(); /* packet from privileged peer? */ + int (*af_checkhost)(); /* tells if address for host or net */ + int (*af_ishost)(); /* tells if address is valid */ + int (*af_canon)(); /* canonicalize address for compares */ +}; + +/* + * Structure returned by af_hash routines. + */ +struct afhash { + u_int afh_hosthash; /* host based hash */ + u_int afh_nethash; /* network based hash */ +}; + +struct afswitch afswitch[AF_MAX]; /* table proper */ diff --git a/usr.sbin/XNSrouted/defs.h b/usr.sbin/XNSrouted/defs.h new file mode 100644 index 0000000000..5414d8c08c --- /dev/null +++ b/usr.sbin/XNSrouted/defs.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)defs.h 5.9 (Berkeley) 2/26/91 + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <net/route.h> +#include <netns/ns.h> +#include <netns/idp.h> +#if defined(vax) || defined(pdp11) +#define xnnet(x) ((u_long) (x)->rip_dst[1] << 16 | (u_long) (x)->rip_dst[0] ) +#else +#define xnnet(x) ((u_long) (x)->rip_dst[0] << 16 | (u_long) (x)->rip_dst[1] ) +#endif +#define IDPPORT_RIF 1 + +#include <stdio.h> +#include <syslog.h> + +#include "protocol.h" +#include "trace.h" +#include "interface.h" +#include "table.h" +#include "af.h" + + +/* + * When we find any interfaces marked down we rescan the + * kernel every CHECK_INTERVAL seconds to see if they've + * come up. + */ +#define CHECK_INTERVAL (5*60) + +#define equal(a1, a2) \ + (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0) +#define min(a,b) ((a)>(b)?(b):(a)) + +struct sockaddr_ns addr; /* Daemon's Address */ +int s; /* Socket to listen on */ +int kmem; +int supplier; /* process should supply updates */ +int install; /* if 1 call kernel */ +int lookforinterfaces; /* if 1 probe kernel for new up interfaces */ +int performnlist; /* if 1 check if /vmunix has changed */ +int externalinterfaces; /* # of remote and local interfaces */ +int timeval; /* local idea of time */ +int noteremoterequests; /* squawk on requests from non-local nets */ + +char packet[MAXPACKETSIZE+sizeof(struct idp)+1]; +struct rip *msg; + +char **argv0; + +int sndmsg(); +int supply(); +int cleanup(); diff --git a/usr.sbin/XNSrouted/if.c b/usr.sbin/XNSrouted/if.c new file mode 100644 index 0000000000..0f597a66fa --- /dev/null +++ b/usr.sbin/XNSrouted/if.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * static char sccsid[] = "@(#)if.c 5.1 (Berkeley) 6/4/85"; (routed/if.c) + */ + +#ifndef lint +static char sccsid[] = "@(#)if.c 5.2 (Berkeley) 6/1/90"; +#endif /* not lint */ + +/* + * Routing Table Management Daemon + */ +#include "defs.h" + +extern struct interface *ifnet; + +/* + * Find the interface with address addr. + */ +struct interface * +if_ifwithaddr(addr) + struct sockaddr *addr; +{ + register struct interface *ifp; + +#define same(a1, a2) \ + (bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0) + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (ifp->int_flags & IFF_REMOTE) + continue; + if (ifp->int_addr.sa_family != addr->sa_family) + continue; + if (same(&ifp->int_addr, addr)) + break; + if ((ifp->int_flags & IFF_BROADCAST) && + same(&ifp->int_broadaddr, addr)) + break; + } + return (ifp); +} + +/* + * Find the point-to-point interface with destination address addr. + */ +struct interface * +if_ifwithdstaddr(addr) + struct sockaddr *addr; +{ + register struct interface *ifp; + + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if ((ifp->int_flags & IFF_POINTOPOINT) == 0) + continue; + if (same(&ifp->int_dstaddr, addr)) + break; + } + return (ifp); +} + +/* + * Find the interface on the network + * of the specified address. + */ +struct interface * +if_ifwithnet(addr) + register struct sockaddr *addr; +{ + register struct interface *ifp; + register int af = addr->sa_family; + register int (*netmatch)(); + + if (af >= AF_MAX) + return (0); + netmatch = afswitch[af].af_netmatch; + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (ifp->int_flags & IFF_REMOTE) + continue; + if (af != ifp->int_addr.sa_family) + continue; + if ((*netmatch)(addr, &ifp->int_addr)) + break; + } + return (ifp); +} + +/* + * Find an interface from which the specified address + * should have come from. Used for figuring out which + * interface a packet came in on -- for tracing. + */ +struct interface * +if_iflookup(addr) + struct sockaddr *addr; +{ + register struct interface *ifp, *maybe; + register int af = addr->sa_family; + register int (*netmatch)(); + + if (af >= AF_MAX) + return (0); + maybe = 0; + netmatch = afswitch[af].af_netmatch; + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (ifp->int_addr.sa_family != af) + continue; + if (same(&ifp->int_addr, addr)) + break; + if ((ifp->int_flags & IFF_BROADCAST) && + same(&ifp->int_broadaddr, addr)) + break; + if (maybe == 0 && (*netmatch)(addr, &ifp->int_addr)) + maybe = ifp; + } + if (ifp == 0) + ifp = maybe; + return (ifp); +} diff --git a/usr.sbin/XNSrouted/input.c b/usr.sbin/XNSrouted/input.c new file mode 100644 index 0000000000..7dc64fa5cc --- /dev/null +++ b/usr.sbin/XNSrouted/input.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)input.c 5.9 (Berkeley) 6/1/90"; +#endif /* not lint */ + +/* + * XNS Routing Table Management Daemon + */ +#include "defs.h" + +struct sockaddr * +xns_nettosa(net) +union ns_net net; +{ + static struct sockaddr_ns sxn; + extern char ether_broadcast_addr[6]; + + bzero(&sxn, sizeof (struct sockaddr_ns)); + sxn.sns_family = AF_NS; + sxn.sns_len = sizeof (sxn); + sxn.sns_addr.x_net = net; + sxn.sns_addr.x_host = *(union ns_host *)ether_broadcast_addr; + return( (struct sockaddr *)&sxn); + +} + +/* + * Process a newly received packet. + */ +rip_input(from, size) + struct sockaddr *from; + int size; +{ + struct rt_entry *rt; + struct netinfo *n; + struct interface *ifp; + int newsize; + struct afswitch *afp; + + + ifp = 0; + TRACE_INPUT(ifp, from, size); + if (from->sa_family >= AF_MAX) + return; + afp = &afswitch[from->sa_family]; + + size -= sizeof (u_short) /* command */; + n = msg->rip_nets; + + switch (ntohs(msg->rip_cmd)) { + + case RIPCMD_REQUEST: + newsize = 0; + while (size > 0) { + if (size < sizeof (struct netinfo)) + break; + size -= sizeof (struct netinfo); + + /* + * A single entry with rip_dst == DSTNETS_ALL and + * metric ``infinity'' means ``all routes''. + */ + if (ns_neteqnn(n->rip_dst, ns_anynet) && + ntohs(n->rip_metric) == HOPCNT_INFINITY && + size == 0) { + ifp = if_ifwithnet(from); + supply(from, 0, ifp); + return; + } + /* + * request for specific nets + */ + rt = rtlookup(xns_nettosa(n->rip_dst)); + if (ftrace) { + fprintf(ftrace, + "specific request for %s", + xns_nettoa(n->rip_dst)); + fprintf(ftrace, + " yields route %x\n", + rt); + } + n->rip_metric = htons( rt == 0 ? HOPCNT_INFINITY : + min(rt->rt_metric+1, HOPCNT_INFINITY)); + n++; + newsize += sizeof (struct netinfo); + } + if (newsize > 0) { + msg->rip_cmd = htons(RIPCMD_RESPONSE); + newsize += sizeof (u_short); + /* should check for if with dstaddr(from) first */ + (*afp->af_output)(0, from, newsize); + ifp = if_ifwithnet(from); + TRACE_OUTPUT(ifp, from, newsize); + if (ftrace) { + fprintf(ftrace, + "request arrived on interface %s\n", + ifp->int_name); + } + } + return; + + case RIPCMD_RESPONSE: + /* verify message came from a router */ + if ((*afp->af_portmatch)(from) == 0) + return; + (*afp->af_canon)(from); + /* are we talking to ourselves? */ + if (ifp = if_ifwithaddr(from)) { + rt = rtfind(from); + if (rt == 0 || (rt->rt_state & RTS_INTERFACE) == 0) + addrouteforif(ifp); + else + rt->rt_timer = 0; + return; + } + /* Update timer for interface on which the packet arrived. + * If from other end of a point-to-point link that isn't + * in the routing tables, (re-)add the route. + */ + if ((rt = rtfind(from)) && (rt->rt_state & RTS_INTERFACE)) { + if(ftrace) fprintf(ftrace, "Got route\n"); + rt->rt_timer = 0; + } else if (ifp = if_ifwithdstaddr(from)) { + if(ftrace) fprintf(ftrace, "Got partner\n"); + addrouteforif(ifp); + } + for (; size > 0; size -= sizeof (struct netinfo), n++) { + struct sockaddr *sa; + if (size < sizeof (struct netinfo)) + break; + if ((unsigned) ntohs(n->rip_metric) >= HOPCNT_INFINITY) + continue; + rt = rtfind(sa = xns_nettosa(n->rip_dst)); + if (rt == 0) { + rtadd(sa, from, ntohs(n->rip_metric), 0); + continue; + } + + /* + * Update if from gateway and different, + * from anywhere and shorter, or getting stale and equivalent. + */ + if ((equal(from, &rt->rt_router) && + ntohs(n->rip_metric) != rt->rt_metric ) || + (unsigned) ntohs(n->rip_metric) < rt->rt_metric || + (rt->rt_timer > (EXPIRE_TIME/2) && + rt->rt_metric == ntohs(n->rip_metric))) { + rtchange(rt, from, ntohs(n->rip_metric)); + rt->rt_timer = 0; + } + } + return; + } +} diff --git a/usr.sbin/XNSrouted/interface.h b/usr.sbin/XNSrouted/interface.h new file mode 100644 index 0000000000..7cb416699a --- /dev/null +++ b/usr.sbin/XNSrouted/interface.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)interface.h 5.5 (Berkeley) 6/1/90 + */ + +/* + * Routing table management daemon. + */ + +/* + * An ``interface'' is similar to an ifnet structure, + * except it doesn't contain q'ing info, and it also + * handles ``logical'' interfaces (remote gateways + * that we want to keep polling even if they go down). + * The list of interfaces which we maintain is used + * in supplying the gratuitous routing table updates. + * We list only one address for each interface, the AF_XNS one. + */ +#define NIFADDR 3 +struct interface { + struct interface *int_next; + struct sockaddr int_addr; /* address on this host */ + union { + struct sockaddr intu_broadaddr; + struct sockaddr intu_dstaddr; + } int_intu; +#define int_broadaddr int_intu.intu_broadaddr /* broadcast address */ +#define int_dstaddr int_intu.intu_dstaddr /* other end of p-to-p link */ + int int_metric; /* init's routing entry */ + int int_flags; /* see below */ + struct ifdebug int_input, int_output; /* packet tracing stuff */ + int int_ipackets; /* input packets received */ + int int_opackets; /* output packets sent */ + char *int_name; /* from kernel if structure */ + u_short int_transitions; /* times gone up-down */ +/*XNS Specific entry */ + struct sameq { + struct sameq *n; /* q of other pt-to-pt links */ + struct sameq *p; /* with same net # */ + } int_sq; +}; + +/* + * 0x1 to 0x10 are reused from the kernel's ifnet definitions, + * the others agree with the RTS_ flags defined elsewhere. + */ +#define IFF_UP 0x1 /* interface is up */ +#define IFF_BROADCAST 0x2 /* broadcast address valid */ +#define IFF_DEBUG 0x4 /* turn on debugging */ +#define IFF_ROUTE 0x8 /* routing entry installed */ +#define IFF_POINTOPOINT 0x10 /* interface is point-to-point link */ + +#define IFF_PASSIVE 0x2000 /* can't tell if up/down */ +#define IFF_INTERFACE 0x4000 /* hardware interface */ +#define IFF_REMOTE 0x8000 /* interface isn't on this machine */ + +struct interface *if_ifwithaddr(); +struct interface *if_ifwithdstaddr(); +struct interface *if_ifwithnet(); +struct interface *if_iflookup(); diff --git a/usr.sbin/XNSrouted/main.c b/usr.sbin/XNSrouted/main.c new file mode 100644 index 0000000000..f4ddf28583 --- /dev/null +++ b/usr.sbin/XNSrouted/main.c @@ -0,0 +1,245 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1985 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 5.11 (Berkeley) 2/26/91"; +#endif /* not lint */ + +/* + * XNS Routing Information Protocol Daemon + */ +#include "defs.h" +#include <sys/time.h> + +#include <net/if.h> + +#include <errno.h> +#include <nlist.h> +#include <signal.h> +#include <paths.h> + +int supplier = -1; /* process should supply updates */ +extern int gateway; + +struct rip *msg = (struct rip *) &packet[sizeof (struct idp)]; +void hup(), fkexit(), timer(); + +main(argc, argv) + int argc; + char *argv[]; +{ + int cc; + struct sockaddr from; + u_char retry; + + argv0 = argv; + argv++, argc--; + while (argc > 0 && **argv == '-') { + if (strcmp(*argv, "-s") == 0) { + supplier = 1; + argv++, argc--; + continue; + } + if (strcmp(*argv, "-q") == 0) { + supplier = 0; + argv++, argc--; + continue; + } + if (strcmp(*argv, "-R") == 0) { + noteremoterequests++; + argv++, argc--; + continue; + } + if (strcmp(*argv, "-t") == 0) { + tracepackets++; + argv++, argc--; + ftrace = stderr; + tracing = 1; + continue; + } + if (strcmp(*argv, "-g") == 0) { + gateway = 1; + argv++, argc--; + continue; + } + if (strcmp(*argv, "-l") == 0) { + gateway = -1; + argv++, argc--; + continue; + } + fprintf(stderr, + "usage: xnsrouted [ -s ] [ -q ] [ -t ] [ -g ] [ -l ]\n"); + exit(1); + } + + +#ifndef DEBUG + if (!tracepackets) + daemon(0, 0); +#endif + openlog("XNSrouted", LOG_PID, LOG_DAEMON); + + ns_anynet.s_net[0] = -1; ns_anynet.s_net[1] = -1; + addr.sns_family = AF_NS; + addr.sns_len = sizeof(addr); + addr.sns_port = htons(IDPPORT_RIF); + s = getsocket(SOCK_DGRAM, 0, &addr); + if (s < 0) + exit(1); + /* + * Any extra argument is considered + * a tracing log file. + */ + if (argc > 0) + traceon(*argv); + /* + * Collect an initial view of the world by + * snooping in the kernel. Then, send a request packet on all + * directly connected networks to find out what + * everyone else thinks. + */ + rtinit(); + ifinit(); + if (supplier < 0) + supplier = 0; + /* request the state of the world */ + msg->rip_cmd = htons(RIPCMD_REQUEST); + msg->rip_nets[0].rip_dst = ns_anynet; + msg->rip_nets[0].rip_metric = htons(HOPCNT_INFINITY); + toall(sndmsg); + signal(SIGALRM, timer); + signal(SIGHUP, hup); + signal(SIGINT, hup); + signal(SIGEMT, fkexit); + timer(); + + + for (;;) + process(s); + +} + +process(fd) + int fd; +{ + struct sockaddr from; + int fromlen = sizeof (from), cc, omask; + struct idp *idp = (struct idp *)packet; + + cc = recvfrom(fd, packet, sizeof (packet), 0, &from, &fromlen); + if (cc <= 0) { + if (cc < 0 && errno != EINTR) + syslog(LOG_ERR, "recvfrom: %m"); + return; + } + if (tracepackets > 1 && ftrace) { + fprintf(ftrace,"rcv %d bytes on %s ", cc, xns_ntoa(&idp->idp_dna)); + fprintf(ftrace," from %s\n", xns_ntoa(&idp->idp_sna)); + } + + if (noteremoterequests && !ns_neteqnn(idp->idp_sna.x_net, ns_zeronet) + && !ns_neteq(idp->idp_sna, idp->idp_dna)) + { + syslog(LOG_ERR, + "net of interface (%s) != net on ether (%s)!\n", + xns_nettoa(idp->idp_dna.x_net), + xns_nettoa(idp->idp_sna.x_net)); + } + + /* We get the IDP header in front of the RIF packet*/ + cc -= sizeof (struct idp); +#define mask(s) (1<<((s)-1)) + omask = sigblock(mask(SIGALRM)); + rip_input(&from, cc); + sigsetmask(omask); +} + +getsocket(type, proto, sns) + int type, proto; + struct sockaddr_ns *sns; +{ + int domain = sns->sns_family; + int retry, s, on = 1; + + retry = 1; + while ((s = socket(domain, type, proto)) < 0 && retry) { + syslog(LOG_ERR, "socket: %m"); + sleep(5 * retry); + retry <<= 1; + } + if (retry == 0) + return (-1); + while (bind(s, (struct sockaddr *)sns, sizeof (*sns)) < 0 && retry) { + syslog(LOG_ERR, "bind: %m"); + sleep(5 * retry); + retry <<= 1; + } + if (retry == 0) + return (-1); + if (domain==AF_NS) { + struct idp idp; + if (setsockopt(s, 0, SO_HEADERS_ON_INPUT, &on, sizeof(on))) { + syslog(LOG_ERR, "setsockopt SEE HEADERS: %m"); + exit(1); + } + idp.idp_pt = NSPROTO_RI; + if (setsockopt(s, 0, SO_DEFAULT_HEADERS, &idp, sizeof(idp))) { + syslog(LOG_ERR, "setsockopt SET HEADER: %m"); + exit(1); + } + } + if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) { + syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); + exit(1); + } + return (s); +} + +/* + * Fork and exit on EMT-- for profiling. + */ +void +fkexit() +{ + if (fork() == 0) + exit(0); +} diff --git a/usr.sbin/XNSrouted/output.c b/usr.sbin/XNSrouted/output.c new file mode 100644 index 0000000000..cdea11f2b7 --- /dev/null +++ b/usr.sbin/XNSrouted/output.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)output.c 5.8 (Berkeley) 2/26/91"; +#endif /* not lint */ + +/* + * Routing Table Management Daemon + */ +#include "defs.h" + +/* + * Apply the function "f" to all non-passive + * interfaces. If the interface supports the + * use of broadcasting use it, otherwise address + * the output to the known router. + */ +toall(f) + int (*f)(); +{ + register struct interface *ifp; + register struct sockaddr *dst; + register int flags; + extern struct interface *ifnet; + + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (ifp->int_flags & IFF_PASSIVE) + continue; + dst = ifp->int_flags & IFF_BROADCAST ? &ifp->int_broadaddr : + ifp->int_flags & IFF_POINTOPOINT ? &ifp->int_dstaddr : + &ifp->int_addr; + flags = ifp->int_flags & IFF_INTERFACE ? MSG_DONTROUTE : 0; + (*f)(dst, flags, ifp); + } +} + +/* + * Output a preformed packet. + */ +/*ARGSUSED*/ +sndmsg(dst, flags, ifp) + struct sockaddr *dst; + int flags; + struct interface *ifp; +{ + + (*afswitch[dst->sa_family].af_output) + (flags, dst, sizeof (struct rip)); + TRACE_OUTPUT(ifp, dst, sizeof (struct rip)); +} + +/* + * Supply dst with the contents of the routing tables. + * If this won't fit in one packet, chop it up into several. + */ +supply(dst, flags, ifp) + struct sockaddr *dst; + int flags; + struct interface *ifp; +{ + register struct rt_entry *rt; + register struct rthash *rh; + register struct netinfo *nn; + register struct netinfo *n = msg->rip_nets; + struct rthash *base = hosthash; + struct sockaddr_ns *sns = (struct sockaddr_ns *) dst; + int (*output)() = afswitch[dst->sa_family].af_output; + int doinghost = 1, size, metric; + union ns_net net; + + msg->rip_cmd = ntohs(RIPCMD_RESPONSE); +again: + for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) + for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { + size = (char *)n - (char *)msg; + if (size > MAXPACKETSIZE - sizeof (struct netinfo)) { + (*output)(flags, dst, size); + TRACE_OUTPUT(ifp, dst, size); + n = msg->rip_nets; + } + sns = (struct sockaddr_ns *)&rt->rt_dst; + if ((rt->rt_flags & (RTF_HOST|RTF_GATEWAY)) == RTF_HOST) + sns = (struct sockaddr_ns *)&rt->rt_router; + metric = min(rt->rt_metric + 1, HOPCNT_INFINITY); + net = sns->sns_addr.x_net; + /* + * Make sure that we don't put out a two net entries + * for a pt to pt link (one for the G route, one for the if) + * This is a kludge, and won't work if there are lots of nets. + */ + for (nn = msg->rip_nets; nn < n; nn++) { + if (ns_neteqnn(net, nn->rip_dst)) { + if (metric < ntohs(nn->rip_metric)) + nn->rip_metric = htons(metric); + goto next; + } + } + n->rip_dst = net; + n->rip_metric = htons(metric); + n++; + next:; + } + if (doinghost) { + doinghost = 0; + base = nethash; + goto again; + } + if (n != msg->rip_nets) { + size = (char *)n - (char *)msg; + (*output)(flags, dst, size); + TRACE_OUTPUT(ifp, dst, size); + } +} diff --git a/usr.sbin/XNSrouted/protocol.h b/usr.sbin/XNSrouted/protocol.h new file mode 100644 index 0000000000..9bf422b606 --- /dev/null +++ b/usr.sbin/XNSrouted/protocol.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)protocol.h 5.6 (Berkeley) 6/1/90 + */ + +/* + * Xerox Routing Information Protocol + * + */ + +struct netinfo { + union ns_net rip_dst; /* destination net */ + u_short rip_metric; /* cost of route */ +}; + +struct rip { + u_short rip_cmd; /* request/response */ + struct netinfo rip_nets[1]; /* variable length */ +}; + +/* + * Packet types. + */ +#define RIPCMD_REQUEST 1 /* want info */ +#define RIPCMD_RESPONSE 2 /* responding to request */ + +#define RIPCMD_MAX 3 +#ifdef RIPCMDS +char *ripcmds[RIPCMD_MAX] = + { "#0", "REQUEST", "RESPONSE" }; +#endif + +#define HOPCNT_INFINITY 16 /* per Xerox NS */ +#define DSTNETS_ALL 0xffffffff /* per Xerox NS */ +#define MAXPACKETSIZE 512 /* max broadcast size */ + +extern union ns_net ns_anynet; +extern union ns_net ns_zeronet; + +/* + * Timer values used in managing the routing table. + * Every update forces an entry's timer to be reset. After + * EXPIRE_TIME without updates, the entry is marked invalid, + * but held onto until GARBAGE_TIME so that others may + * see it "be deleted". + */ +#define TIMER_RATE 30 /* alarm clocks every 30 seconds */ + +#define SUPPLY_INTERVAL 30 /* time to supply tables */ + +#define EXPIRE_TIME 180 /* time to mark entry invalid */ +#define GARBAGE_TIME 240 /* time to garbage collect */ diff --git a/usr.sbin/XNSrouted/startup.c b/usr.sbin/XNSrouted/startup.c new file mode 100644 index 0000000000..8887c1fc47 --- /dev/null +++ b/usr.sbin/XNSrouted/startup.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)startup.c 5.11 (Berkeley) 2/26/91"; +#endif /* not lint */ + +/* + * Routing Table Management Daemon + */ +#include "defs.h" +#include <sys/ioctl.h> +#include <net/if.h> +#include <nlist.h> +#include <stdlib.h> + +struct interface *ifnet; +int lookforinterfaces = 1; +int performnlist = 1; +int gateway = 0; +int externalinterfaces = 0; /* # of remote and local interfaces */ +char ether_broadcast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + +/* + * Find the network interfaces which have configured themselves. + * If the interface is present but not yet up (for example an + * ARPANET IMP), set the lookforinterfaces flag so we'll + * come back later and look again. + */ +ifinit() +{ + struct interface ifs, *ifp; + int s; + struct ifconf ifc; + char buf[BUFSIZ], *cp, *cplim; + struct ifreq ifreq, *ifr; + u_long i; + + if ((s = socket(AF_NS, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "socket: %m"); + exit(1); + } + ifc.ifc_len = sizeof (buf); + ifc.ifc_buf = buf; + if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) { + syslog(LOG_ERR, "ioctl (get interface configuration)"); + close(s); + exit(1); + } + ifr = ifc.ifc_req; + lookforinterfaces = 0; +#ifdef RTM_ADD +#define max(a, b) (a > b ? a : b) +#define size(p) max((p).sa_len, sizeof(p)) +#else +#define size(p) (sizeof (p)) +#endif + cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */ + for (cp = buf; cp < cplim; + cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) { + ifr = (struct ifreq *)cp; + bzero((char *)&ifs, sizeof(ifs)); + ifs.int_addr = ifr->ifr_addr; + ifreq = *ifr; + if (ioctl(s, SIOCGIFFLAGS, (char *)&ifreq) < 0) { + syslog(LOG_ERR, "ioctl (get interface flags)"); + continue; + } + ifs.int_flags = ifreq.ifr_flags | IFF_INTERFACE; + if ((ifs.int_flags & IFF_UP) == 0 || + ifr->ifr_addr.sa_family == AF_UNSPEC) { + lookforinterfaces = 1; + continue; + } + if (ifs.int_addr.sa_family != AF_NS) + continue; + if (ifs.int_flags & IFF_POINTOPOINT) { + if (ioctl(s, SIOCGIFDSTADDR, (char *)&ifreq) < 0) { + syslog(LOG_ERR, "ioctl (get dstaddr): %m"); + continue; + } + ifs.int_dstaddr = ifreq.ifr_dstaddr; + } + if (ifs.int_flags & IFF_BROADCAST) { + if (ioctl(s, SIOCGIFBRDADDR, (char *)&ifreq) < 0) { + syslog(LOG_ERR, "ioctl (get broadaddr: %m"); + continue; + } + ifs.int_broadaddr = ifreq.ifr_broadaddr; + } + /* + * already known to us? + * what makes a POINTOPOINT if unique is its dst addr, + * NOT its source address + */ + if ( ((ifs.int_flags & IFF_POINTOPOINT) && + if_ifwithdstaddr(&ifs.int_dstaddr)) || + ( ((ifs.int_flags & IFF_POINTOPOINT) == 0) && + if_ifwithaddr(&ifs.int_addr))) + continue; + /* no one cares about software loopback interfaces */ + if (strncmp(ifr->ifr_name,"lo", 2)==0) + continue; + ifp = (struct interface *)malloc(sizeof (struct interface)); + if (ifp == 0) { + syslog(LOG_ERR,"XNSrouted: out of memory\n"); + break; + } + *ifp = ifs; + /* + * Count the # of directly connected networks + * and point to point links which aren't looped + * back to ourself. This is used below to + * decide if we should be a routing ``supplier''. + */ + if ((ifs.int_flags & IFF_POINTOPOINT) == 0 || + if_ifwithaddr(&ifs.int_dstaddr) == 0) + externalinterfaces++; + /* + * If we have a point-to-point link, we want to act + * as a supplier even if it's our only interface, + * as that's the only way our peer on the other end + * can tell that the link is up. + */ + if ((ifs.int_flags & IFF_POINTOPOINT) && supplier < 0) + supplier = 1; + ifp->int_name = malloc(strlen(ifr->ifr_name) + 1); + if (ifp->int_name == 0) { + syslog(LOG_ERR,"XNSrouted: out of memory\n"); + exit(1); + } + strcpy(ifp->int_name, ifr->ifr_name); + ifp->int_metric = 0; + ifp->int_next = ifnet; + ifnet = ifp; + traceinit(ifp); + addrouteforif(ifp); + } + if (externalinterfaces > 1 && supplier < 0) + supplier = 1; + close(s); +} + +addrouteforif(ifp) + struct interface *ifp; +{ + struct sockaddr_ns net; + struct sockaddr *dst; + int state, metric; + struct rt_entry *rt; + + if (ifp->int_flags & IFF_POINTOPOINT) { + int (*match)(); + register struct interface *ifp2 = ifnet; + register struct interface *ifprev = ifnet; + + dst = &ifp->int_dstaddr; + + /* Search for interfaces with the same net */ + ifp->int_sq.n = ifp->int_sq.p = &(ifp->int_sq); + match = afswitch[dst->sa_family].af_netmatch; + if (match) + for (ifp2 = ifnet; ifp2; ifp2 =ifp2->int_next) { + if (ifp->int_flags & IFF_POINTOPOINT == 0) + continue; + if ((*match)(&ifp2->int_dstaddr,&ifp->int_dstaddr)) { + insque(&ifp2->int_sq,&ifp->int_sq); + break; + } + } + } else { + dst = &ifp->int_broadaddr; + } + rt = rtlookup(dst); + if (rt) + rtdelete(rt); + if (tracing) + fprintf(stderr, "Adding route to interface %s\n", ifp->int_name); + if (ifp->int_transitions++ > 0) + syslog(LOG_ERR, "re-installing interface %s", ifp->int_name); + rtadd(dst, &ifp->int_addr, ifp->int_metric, + ifp->int_flags & (IFF_INTERFACE|IFF_PASSIVE|IFF_REMOTE)); +} + diff --git a/usr.sbin/XNSrouted/table.h b/usr.sbin/XNSrouted/table.h new file mode 100644 index 0000000000..159dc4e642 --- /dev/null +++ b/usr.sbin/XNSrouted/table.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)table.h 5.1 (Berkeley) 6/4/85 (routed/table.h) + * + * @(#)table.h 5.3 (Berkeley) 6/1/90 + */ + +/* + * Routing table management daemon. + */ + +/* + * Routing table structure; differs a bit from kernel tables. + * + * Note: the union below must agree in the first 4 members + * so the ioctl's will work. + */ +struct rthash { + struct rt_entry *rt_forw; + struct rt_entry *rt_back; +}; + +#ifdef RTM_ADD +#define rtentry ortentry +#endif + +struct rt_entry { + struct rt_entry *rt_forw; + struct rt_entry *rt_back; + union { + struct rtentry rtu_rt; + struct { + u_long rtu_hash; + struct sockaddr rtu_dst; + struct sockaddr rtu_router; + short rtu_flags; + short rtu_state; + int rtu_timer; + int rtu_metric; + struct interface *rtu_ifp; + } rtu_entry; + } rt_rtu; +}; + +#define rt_rt rt_rtu.rtu_rt /* pass to ioctl */ +#define rt_hash rt_rtu.rtu_entry.rtu_hash /* for net or host */ +#define rt_dst rt_rtu.rtu_entry.rtu_dst /* match value */ +#define rt_router rt_rtu.rtu_entry.rtu_router /* who to forward to */ +#define rt_flags rt_rtu.rtu_entry.rtu_flags /* kernel flags */ +#define rt_timer rt_rtu.rtu_entry.rtu_timer /* for invalidation */ +#define rt_state rt_rtu.rtu_entry.rtu_state /* see below */ +#define rt_metric rt_rtu.rtu_entry.rtu_metric /* cost of route */ +#define rt_ifp rt_rtu.rtu_entry.rtu_ifp /* interface to take */ + +#define ROUTEHASHSIZ 32 /* must be a power of 2 */ +#define ROUTEHASHMASK (ROUTEHASHSIZ - 1) + +/* + * "State" of routing table entry. + */ +#define RTS_CHANGED 0x1 /* route has been altered recently */ +#define RTS_PASSIVE IFF_PASSIVE /* don't time out route */ +#define RTS_INTERFACE IFF_INTERFACE /* route is for network interface */ +#define RTS_REMOTE IFF_REMOTE /* route is for ``remote'' entity */ + +struct rthash nethash[ROUTEHASHSIZ]; +struct rthash hosthash[ROUTEHASHSIZ]; +struct rt_entry *rtlookup(); +struct rt_entry *rtfind(); diff --git a/usr.sbin/XNSrouted/tables.c b/usr.sbin/XNSrouted/tables.c new file mode 100644 index 0000000000..38ce4386fe --- /dev/null +++ b/usr.sbin/XNSrouted/tables.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)tables.c 5.9 (Berkeley) 6/1/90"; +#endif /* not lint */ + +/* + * Routing Table Management Daemon + */ +#include "defs.h" +#include <sys/ioctl.h> +#include <errno.h> + +#ifndef DEBUG +#define DEBUG 0 +#endif + +extern char *xns_ntoa(); +#define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));} + +int install = !DEBUG; /* if 1 call kernel */ +int delete = 1; +/* + * Lookup dst in the tables for an exact match. + */ +struct rt_entry * +rtlookup(dst) + struct sockaddr *dst; +{ + register struct rt_entry *rt; + register struct rthash *rh; + register u_int hash; + struct afhash h; + int doinghost = 1; + + if (dst->sa_family >= AF_MAX) + return (0); + (*afswitch[dst->sa_family].af_hash)(dst, &h); + hash = h.afh_hosthash; + rh = &hosthash[hash & ROUTEHASHMASK]; +again: + for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { + if (rt->rt_hash != hash) + continue; + if (equal(&rt->rt_dst, dst)) + return (rt); + } + if (doinghost) { + doinghost = 0; + hash = h.afh_nethash; + rh = &nethash[hash & ROUTEHASHMASK]; + goto again; + } + return (0); +} + +/* + * Find a route to dst as the kernel would. + */ +struct rt_entry * +rtfind(dst) + struct sockaddr *dst; +{ + register struct rt_entry *rt; + register struct rthash *rh; + register u_int hash; + struct afhash h; + int af = dst->sa_family; + int doinghost = 1, (*match)(); + + if (af >= AF_MAX) + return (0); + (*afswitch[af].af_hash)(dst, &h); + hash = h.afh_hosthash; + rh = &hosthash[hash & ROUTEHASHMASK]; + +again: + for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { + if (rt->rt_hash != hash) + continue; + if (doinghost) { + if (equal(&rt->rt_dst, dst)) + return (rt); + } else { + if (rt->rt_dst.sa_family == af && + (*match)(&rt->rt_dst, dst)) + return (rt); + } + } + if (doinghost) { + doinghost = 0; + hash = h.afh_nethash; + rh = &nethash[hash & ROUTEHASHMASK]; + match = afswitch[af].af_netmatch; + goto again; + } + return (0); +} + +rtadd(dst, gate, metric, state) + struct sockaddr *dst, *gate; + int metric, state; +{ + struct afhash h; + register struct rt_entry *rt; + struct rthash *rh; + int af = dst->sa_family, flags; + u_int hash; + + FIXLEN(dst); + FIXLEN(gate); + if (af >= AF_MAX) + return; + (*afswitch[af].af_hash)(dst, &h); + flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0; + if (flags & RTF_HOST) { + hash = h.afh_hosthash; + rh = &hosthash[hash & ROUTEHASHMASK]; + } else { + hash = h.afh_nethash; + rh = &nethash[hash & ROUTEHASHMASK]; + } + rt = (struct rt_entry *)malloc(sizeof (*rt)); + if (rt == 0) + return; + rt->rt_hash = hash; + rt->rt_dst = *dst; + rt->rt_router = *gate; + rt->rt_metric = metric; + rt->rt_timer = 0; + rt->rt_flags = RTF_UP | flags; + rt->rt_state = state | RTS_CHANGED; + rt->rt_ifp = if_ifwithnet(&rt->rt_router); + if (metric) + rt->rt_flags |= RTF_GATEWAY; + insque(rt, rh); + TRACE_ACTION(ADD, rt); + /* + * If the ioctl fails because the gateway is unreachable + * from this host, discard the entry. This should only + * occur because of an incorrect entry in /etc/gateways. + */ + if (install && ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) { + if (errno != EEXIST) + perror("SIOCADDRT"); + if (errno == ENETUNREACH) { + TRACE_ACTION(DELETE, rt); + remque(rt); + free((char *)rt); + } + } +} + +rtchange(rt, gate, metric) + struct rt_entry *rt; + struct sockaddr *gate; + short metric; +{ + int doioctl = 0, metricchanged = 0; + struct rtentry oldroute; + + FIXLEN(gate); + if (!equal(&rt->rt_router, gate)) + doioctl++; + if (metric != rt->rt_metric) + metricchanged++; + if (doioctl || metricchanged) { + TRACE_ACTION(CHANGE FROM, rt); + if (doioctl) { + oldroute = rt->rt_rt; + rt->rt_router = *gate; + } + rt->rt_metric = metric; + if ((rt->rt_state & RTS_INTERFACE) && metric) { + rt->rt_state &= ~RTS_INTERFACE; + syslog(LOG_ERR, + "changing route from interface %s (timed out)", + rt->rt_ifp->int_name); + } + if (metric) + rt->rt_flags |= RTF_GATEWAY; + else + rt->rt_flags &= ~RTF_GATEWAY; + rt->rt_state |= RTS_CHANGED; + TRACE_ACTION(CHANGE TO, rt); + } + if (doioctl && install) { +#ifndef RTM_ADD + if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) + syslog(LOG_ERR, "SIOCADDRT dst %s, gw %s: %m", + xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr), + xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr)); + if (delete && ioctl(s, SIOCDELRT, (char *)&oldroute) < 0) + perror("SIOCDELRT"); +#else + if (delete && ioctl(s, SIOCDELRT, (char *)&oldroute) < 0) + perror("SIOCDELRT"); + if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) + syslog(LOG_ERR, "SIOCADDRT dst %s, gw %s: %m", + xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr), + xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr)); +#endif + } +} + +rtdelete(rt) + struct rt_entry *rt; +{ + + struct sockaddr *sa = &(rt->rt_rt.rt_gateway); + FIXLEN(sa); +#undef rt_dst + sa = &(rt->rt_rt.rt_dst); + FIXLEN(sa); + if (rt->rt_state & RTS_INTERFACE) { + syslog(LOG_ERR, "deleting route to interface %s (timed out)", + rt->rt_ifp->int_name); + } + TRACE_ACTION(DELETE, rt); + if (install && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt)) + perror("SIOCDELRT"); + remque(rt); + free((char *)rt); +} + +rtinit() +{ + register struct rthash *rh; + + for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) + rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; + for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++) + rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; +} diff --git a/usr.sbin/XNSrouted/timer.c b/usr.sbin/XNSrouted/timer.c new file mode 100644 index 0000000000..f420bc074e --- /dev/null +++ b/usr.sbin/XNSrouted/timer.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)timer.c 5.7 (Berkeley) 2/26/91"; +#endif /* not lint */ + +/* + * Routing Table Management Daemon + */ +#include "defs.h" + +int timeval = -TIMER_RATE; + +/* + * Timer routine. Performs routing information supply + * duties and manages timers on routing table entries. + */ +void +timer() +{ + register struct rthash *rh; + register struct rt_entry *rt; + struct rthash *base = hosthash; + int doinghost = 1, timetobroadcast; + + timeval += TIMER_RATE; + if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0) + ifinit(); + timetobroadcast = supplier && (timeval % SUPPLY_INTERVAL) == 0; +again: + for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) { + rt = rh->rt_forw; + for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { + /* + * We don't advance time on a routing entry for + * a passive gateway or that for our only interface. + * The latter is excused because we don't act as + * a routing information supplier and hence would + * time it out. This is fair as if it's down + * we're cut off from the world anyway and it's + * not likely we'll grow any new hardware in + * the mean time. + */ + if (!(rt->rt_state & RTS_PASSIVE) && + (supplier || !(rt->rt_state & RTS_INTERFACE))) + rt->rt_timer += TIMER_RATE; + if (rt->rt_timer >= EXPIRE_TIME) + rt->rt_metric = HOPCNT_INFINITY; + if (rt->rt_timer >= GARBAGE_TIME) { + rt = rt->rt_back; + /* Perhaps we should send a REQUEST for this route? */ + rtdelete(rt->rt_forw); + continue; + } + if (rt->rt_state & RTS_CHANGED) { + rt->rt_state &= ~RTS_CHANGED; + /* don't send extraneous packets */ + if (!supplier || timetobroadcast) + continue; + msg->rip_cmd = htons(RIPCMD_RESPONSE); + msg->rip_nets[0].rip_dst = + (satons_addr(rt->rt_dst)).x_net; + msg->rip_nets[0].rip_metric = + htons(min(rt->rt_metric+1, HOPCNT_INFINITY)); + toall(sndmsg); + } + } + } + if (doinghost) { + doinghost = 0; + base = nethash; + goto again; + } + if (timetobroadcast) + toall(supply); + alarm(TIMER_RATE); +} + +/* + * On hangup, let everyone know we're going away. + */ +void +hup() +{ + register struct rthash *rh; + register struct rt_entry *rt; + struct rthash *base = hosthash; + int doinghost = 1; + + if (supplier) { +again: + for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) { + rt = rh->rt_forw; + for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) + rt->rt_metric = HOPCNT_INFINITY; + } + if (doinghost) { + doinghost = 0; + base = nethash; + goto again; + } + toall(supply); + } + exit(1); +} diff --git a/usr.sbin/XNSrouted/tools/query.c b/usr.sbin/XNSrouted/tools/query.c new file mode 100644 index 0000000000..2e6374d1f6 --- /dev/null +++ b/usr.sbin/XNSrouted/tools/query.c @@ -0,0 +1,232 @@ +/*- + * Copyright (c) 1983, 1986 The Regents of the University of California. + * All rights reserved. + * + * This code includes software contributed to Berkeley by + * Bill Nesheim at Cornell University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1983, 1986 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)query.c 5.8 (Berkeley) 4/16/91"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <netns/ns.h> +#include <netns/idp.h> +#include <errno.h> +#include <stdio.h> +#include <netdb.h> +#include "../protocol.h" +#define IDPPORT_RIF 1 + +#define WTIME 5 /* Time to wait for responses */ + +int s; +int timedout, timeout(); +char packet[MAXPACKETSIZE]; +extern int errno; +struct sockaddr_ns myaddr = {sizeof(myaddr), AF_NS}; +char *ns_ntoa(); +struct ns_addr ns_addr(); +main(argc, argv) +int argc; +char *argv[]; +{ + int cc, count, bits; + struct sockaddr from; + int fromlen = sizeof(from); + struct timeval notime; + + if (argc < 2) { + printf("usage: query hosts...\n"); + exit(1); + } + s = getsocket(SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + exit(2); + } + + argv++, argc--; + query(argv,argc); + + /* + * Listen for returning packets; + * may be more than one packet per host. + */ + bits = 1 << s; + bzero(¬ime, sizeof(notime)); + signal(SIGALRM, timeout); + alarm(WTIME); + while (!timedout || + select(20, &bits, 0, 0, ¬ime) > 0) { + struct nspacket { + struct idp hdr; + char data[512]; + } response; + cc = recvfrom(s, &response, sizeof (response), 0, + &from, &fromlen); + if (cc <= 0) { + if (cc < 0) { + if (errno == EINTR) + continue; + perror("recvfrom"); + (void) close(s); + exit(1); + } + continue; + } + rip_input(&from, response.data, cc); + count--; + } +} +static struct sockaddr_ns router = {sizeof(myaddr), AF_NS}; +static struct ns_addr zero_addr; +static short allones[] = {-1, -1, -1}; + +query(argv,argc) +char **argv; +{ + register struct rip *msg = (struct rip *)packet; + char *host = *argv; + int flags = 0; + struct ns_addr specific; + + if (bcmp(*argv, "-r", 3) == 0) { + flags = MSG_DONTROUTE; argv++; argc--; + } + host = *argv; + router.sns_addr = ns_addr(host); + router.sns_addr.x_port = htons(IDPPORT_RIF); + if (ns_hosteq(zero_addr, router.sns_addr)) { + router.sns_addr.x_host = *(union ns_host *) allones; + } + msg->rip_cmd = htons(RIPCMD_REQUEST); + msg->rip_nets[0].rip_dst = *(union ns_net *) allones; + msg->rip_nets[0].rip_metric = htons(HOPCNT_INFINITY); + if (argc > 0) { + specific = ns_addr(*argv); + msg->rip_nets[0].rip_dst = specific.x_net; + specific.x_host = zero_addr.x_host; + specific.x_port = zero_addr.x_port; + printf("Net asked for was %s\n", ns_ntoa(specific)); + } + if (sendto(s, packet, sizeof (struct rip), flags, + &router, sizeof(router)) < 0) + perror(host); +} + +/* + * Handle an incoming routing packet. + */ +rip_input(from, msg, size) + struct sockaddr_ns *from; + register struct rip *msg; + int size; +{ + struct netinfo *n; + char *name; + int lna, net, subnet; + struct hostent *hp; + struct netent *np; + static struct ns_addr work; + + if (htons(msg->rip_cmd) != RIPCMD_RESPONSE) + return; + printf("from %s\n", ns_ntoa(from->sns_addr)); + size -= sizeof (struct idp); + size -= sizeof (short); + n = msg->rip_nets; + while (size > 0) { + union ns_net_u net; + if (size < sizeof (struct netinfo)) + break; + net.net_e = n->rip_dst; + printf("\t%d, metric %d\n", ntohl(net.long_e), + ntohs(n->rip_metric)); + size -= sizeof (struct netinfo), n++; + } +} + +timeout() +{ + timedout = 1; +} +getsocket(type, proto) + int type, proto; +{ + struct sockaddr_ns *sns = &myaddr; + int domain = sns->sns_family; + int retry, s, on = 1; + + retry = 1; + while ((s = socket(domain, type, proto)) < 0 && retry) { + perror("socket"); + sleep(5 * retry); + retry <<= 1; + } + if (retry == 0) + return (-1); + while (bind(s, sns, sizeof (*sns), 0) < 0 && retry) { + perror("bind"); + sleep(5 * retry); + retry <<= 1; + } + if (retry == 0) + return (-1); + if (domain==AF_NS) { + struct idp idp; + if (setsockopt(s, 0, SO_HEADERS_ON_INPUT, &on, sizeof(on))) { + perror("setsockopt SEE HEADERS"); + exit(1); + } + idp.idp_pt = NSPROTO_RI; + if (setsockopt(s, 0, SO_DEFAULT_HEADERS, &idp, sizeof(idp))) { + perror("setsockopt SET HEADERS"); + exit(1); + } + } + if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) { + perror("setsockopt SO_BROADCAST"); + exit(1); + } + return (s); +} diff --git a/usr.sbin/XNSrouted/trace.c b/usr.sbin/XNSrouted/trace.c new file mode 100644 index 0000000000..51728733a3 --- /dev/null +++ b/usr.sbin/XNSrouted/trace.c @@ -0,0 +1,313 @@ +/* + * Copyright (c) 1985 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)trace.c 5.11 (Berkeley) 2/26/91"; +#endif /* not lint */ + +/* + * Routing Table Management Daemon + */ +#define RIPCMDS +#include <stdlib.h> +#include "defs.h" + +#define NRECORDS 50 /* size of circular trace buffer */ +#ifdef DEBUG +FILE *ftrace = stdout; +int tracing = 1; +#else DEBUG +FILE *ftrace = NULL; +int tracing = 0; +#endif + +char *xns_ntoa(); + +traceinit(ifp) + register struct interface *ifp; +{ + static int iftraceinit(); + + if (iftraceinit(ifp, &ifp->int_input) && + iftraceinit(ifp, &ifp->int_output)) + return; + tracing = 0; + syslog(LOG_ERR, "traceinit: can't init %s\n", ifp->int_name); +} + +static +iftraceinit(ifp, ifd) + struct interface *ifp; + register struct ifdebug *ifd; +{ + register struct iftrace *t; + + ifd->ifd_records = + (struct iftrace *)malloc(NRECORDS * sizeof (struct iftrace)); + if (ifd->ifd_records == 0) + return (0); + ifd->ifd_front = ifd->ifd_records; + ifd->ifd_count = 0; + for (t = ifd->ifd_records; t < ifd->ifd_records + NRECORDS; t++) { + t->ift_size = 0; + t->ift_packet = 0; + } + ifd->ifd_if = ifp; + return (1); +} + +traceon(file) + char *file; +{ + + if (ftrace != NULL) + return; + ftrace = fopen(file, "a"); + if (ftrace == NULL) + return; + dup2(fileno(ftrace), 1); + dup2(fileno(ftrace), 2); + tracing = 1; +} + +traceoff() +{ + if (!tracing) + return; + if (ftrace != NULL) + fclose(ftrace); + ftrace = NULL; + tracing = 0; +} + +trace(ifd, who, p, len, m) + register struct ifdebug *ifd; + struct sockaddr *who; + char *p; + int len, m; +{ + register struct iftrace *t; + + if (ifd->ifd_records == 0) + return; + t = ifd->ifd_front++; + if (ifd->ifd_front >= ifd->ifd_records + NRECORDS) + ifd->ifd_front = ifd->ifd_records; + if (ifd->ifd_count < NRECORDS) + ifd->ifd_count++; + if (t->ift_size > 0 && t->ift_packet) + free(t->ift_packet); + t->ift_packet = 0; + t->ift_stamp = time(0); + t->ift_who = *who; + if (len > 0) { + t->ift_packet = malloc(len); + if (t->ift_packet) + bcopy(p, t->ift_packet, len); + else + len = 0; + } + t->ift_size = len; + t->ift_metric = m; +} + +traceaction(fd, action, rt) + FILE *fd; + char *action; + struct rt_entry *rt; +{ + struct sockaddr_ns *dst, *gate; + static struct bits { + int t_bits; + char *t_name; + } flagbits[] = { + { RTF_UP, "UP" }, + { RTF_GATEWAY, "GATEWAY" }, + { RTF_HOST, "HOST" }, + { 0 } + }, statebits[] = { + { RTS_PASSIVE, "PASSIVE" }, + { RTS_REMOTE, "REMOTE" }, + { RTS_INTERFACE,"INTERFACE" }, + { RTS_CHANGED, "CHANGED" }, + { 0 } + }; + register struct bits *p; + register int first; + char *cp; + struct interface *ifp; + + if (fd == NULL) + return; + fprintf(fd, "%s ", action); + dst = (struct sockaddr_ns *)&rt->rt_dst; + gate = (struct sockaddr_ns *)&rt->rt_router; + fprintf(fd, "dst %s, ", xns_ntoa(&dst->sns_addr)); + fprintf(fd, "router %s, metric %d, flags", + xns_ntoa(&gate->sns_addr), rt->rt_metric); + cp = " %s"; + for (first = 1, p = flagbits; p->t_bits > 0; p++) { + if ((rt->rt_flags & p->t_bits) == 0) + continue; + fprintf(fd, cp, p->t_name); + if (first) { + cp = "|%s"; + first = 0; + } + } + fprintf(fd, " state"); + cp = " %s"; + for (first = 1, p = statebits; p->t_bits > 0; p++) { + if ((rt->rt_state & p->t_bits) == 0) + continue; + fprintf(fd, cp, p->t_name); + if (first) { + cp = "|%s"; + first = 0; + } + } + putc('\n', fd); + if (!tracepackets && (rt->rt_state & RTS_PASSIVE) == 0 && rt->rt_ifp) + dumpif(fd, rt->rt_ifp); + fflush(fd); +} + +dumpif(fd, ifp) + register struct interface *ifp; + FILE *fd; +{ + if (ifp->int_input.ifd_count || ifp->int_output.ifd_count) { + fprintf(fd, "*** Packet history for interface %s ***\n", + ifp->int_name); + dumptrace(fd, "to", &ifp->int_output); + dumptrace(fd, "from", &ifp->int_input); + fprintf(fd, "*** end packet history ***\n"); + } +} + +dumptrace(fd, dir, ifd) + FILE *fd; + char *dir; + register struct ifdebug *ifd; +{ + register struct iftrace *t; + char *cp = !strcmp(dir, "to") ? "Output" : "Input"; + + if (ifd->ifd_front == ifd->ifd_records && + ifd->ifd_front->ift_size == 0) { + fprintf(fd, "%s: no packets.\n", cp); + return; + } + fprintf(fd, "%s trace:\n", cp); + t = ifd->ifd_front - ifd->ifd_count; + if (t < ifd->ifd_records) + t += NRECORDS; + for ( ; ifd->ifd_count; ifd->ifd_count--, t++) { + if (t >= ifd->ifd_records + NRECORDS) + t = ifd->ifd_records; + if (t->ift_size == 0) + continue; + fprintf(fd, "%.24s: metric=%d\n", ctime(&t->ift_stamp), + t->ift_metric); + dumppacket(fd, dir, &t->ift_who, t->ift_packet, t->ift_size); + } +} + +dumppacket(fd, dir, who, cp, size) + FILE *fd; + struct sockaddr_ns *who; /* should be sockaddr */ + char *dir, *cp; + register int size; +{ + register struct rip *msg = (struct rip *)cp; + register struct netinfo *n; + char *xns_nettoa(); + + if (msg->rip_cmd && ntohs(msg->rip_cmd) < RIPCMD_MAX) + fprintf(fd, "%s %s %s#%x", ripcmds[ntohs(msg->rip_cmd)], + dir, xns_ntoa(&who->sns_addr), ntohs(who->sns_addr.x_port)); + else { + fprintf(fd, "Bad cmd 0x%x %s %s#%x\n", ntohs(msg->rip_cmd), + dir, xns_ntoa(&who->sns_addr), ntohs(who->sns_addr.x_port)); + fprintf(fd, "size=%d cp=%x packet=%x\n", size, cp, packet); + return; + } + switch (ntohs(msg->rip_cmd)) { + + case RIPCMD_REQUEST: + case RIPCMD_RESPONSE: + fprintf(fd, ":\n"); + size -= sizeof (u_short); + n = msg->rip_nets; + for (; size > 0; n++, size -= sizeof (struct netinfo)) { + if (size < sizeof (struct netinfo)) + break; + fprintf(fd, "\tnet %s metric %d\n", + xns_nettoa(n->rip_dst), + ntohs(n->rip_metric)); + } + break; + + } +} + +union ns_net_u net; + +char * +xns_nettoa(val) +union ns_net val; +{ + static char buf[100]; + net.net_e = val; + (void)sprintf(buf, "%lx", ntohl(net.long_e)); + return (buf); +} + + +char * +xns_ntoa(addr) +struct ns_addr *addr; +{ + static char buf[100]; + + (void)sprintf(buf, "%s#%x:%x:%x:%x:%x:%x", + xns_nettoa(addr->x_net), + addr->x_host.c_host[0], addr->x_host.c_host[1], + addr->x_host.c_host[2], addr->x_host.c_host[3], + addr->x_host.c_host[4], addr->x_host.c_host[5]); + + return(buf); +} diff --git a/usr.sbin/XNSrouted/trace.h b/usr.sbin/XNSrouted/trace.h new file mode 100644 index 0000000000..be48553d6c --- /dev/null +++ b/usr.sbin/XNSrouted/trace.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * This file includes significant work done at Cornell University by + * Bill Nesheim. That work included by permission. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)trace.h 5.6 (Berkeley) 6/1/90 + */ + +/* + * Xerox Routing Information Protocol. + */ + +/* + * Trace record format. + */ +struct iftrace { + time_t ift_stamp; /* time stamp */ + struct sockaddr ift_who; /* from/to */ + char *ift_packet; /* pointer to packet */ + short ift_size; /* size of packet */ + short ift_metric; /* metric */ +}; + +/* + * Per interface packet tracing buffers. An incoming and + * outgoing circular buffer of packets is maintained, per + * interface, for debugging. Buffers are dumped whenever + * an interface is marked down. + */ +struct ifdebug { + struct iftrace *ifd_records; /* array of trace records */ + struct iftrace *ifd_front; /* next empty trace record */ + int ifd_count; /* number of unprinted records */ + struct interface *ifd_if; /* for locating stuff */ +}; + +/* + * Packet tracing stuff. + */ +int tracepackets; /* watch packets as they go by */ +int tracing; /* on/off */ +FILE *ftrace; /* output trace file */ + +#define TRACE_ACTION(action, route) { \ + if (tracing) \ + traceaction(ftrace, "action", route); \ + } +#define TRACE_INPUT(ifp, src, size) { \ + if (tracing) { \ + ifp = if_iflookup(src); \ + if (ifp) \ + trace(&ifp->int_input, src, &packet[sizeof(struct idp)], size, \ + ntohl(ifp->int_metric)); \ + } \ + if (tracepackets && ftrace) \ + dumppacket(ftrace, "from", src, &packet[sizeof(struct idp)], size); \ + } +#define TRACE_OUTPUT(ifp, dst, size) { \ + if (tracing) { \ + ifp = if_iflookup(dst); \ + if (ifp) \ + trace(&ifp->int_output, dst, &packet[sizeof(struct idp)], size, ifp->int_metric); \ + } \ + if (tracepackets && ftrace) \ + dumppacket(ftrace, "to", dst, &packet[sizeof(struct idp)], size); \ + } diff --git a/usr.sbin/arp/Makefile b/usr.sbin/arp/Makefile index 3c99778966..23fdda88f2 100644 --- a/usr.sbin/arp/Makefile +++ b/usr.sbin/arp/Makefile @@ -5,6 +5,6 @@ BINGRP= kmem BINMODE=2555 DPADD= ${LIBUTIL} LDADD= -lutil -MAN8= arp.0 +MAN8= arp.8 .include <bsd.prog.mk> diff --git a/usr.sbin/arp/arp.8 b/usr.sbin/arp/arp.8 index 5b7e197d24..ceb7aa24ba 100644 --- a/usr.sbin/arp/arp.8 +++ b/usr.sbin/arp/arp.8 @@ -42,7 +42,7 @@ .Ar hostname .Nm arp .Fl a -.Op Ar vmunix +.Op Ar 386bsd .Op Ar kmem .Nm arp .Fl d Ar hostname @@ -75,9 +75,9 @@ entries by reading the table from the file .Ar kmem (default /dev/kmem) based on the kernel file -.Ar vmunix +.Ar 386bsd (default -.Pa /vmunix ) . +.Pa /386bsd ) . .It Fl d A super-user may delete an entry for the host called .Ar hostname diff --git a/usr.sbin/bad144/Makefile b/usr.sbin/bad144/Makefile index 4b6dac83f1..ebc720a35a 100644 --- a/usr.sbin/bad144/Makefile +++ b/usr.sbin/bad144/Makefile @@ -1,7 +1,8 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= bad144 -MAN8= bad144.0 -MANSUBDIR=/i386 +MAN8= bad144.8 +MLINKS= bad144.8 ../bad144.8 +MANSUBDIR=/${MACHINE} .include <bsd.prog.mk> diff --git a/usr.sbin/bad144/bad144.8 b/usr.sbin/bad144/bad144.8 index 97e136e544..229eafede9 100644 --- a/usr.sbin/bad144/bad144.8 +++ b/usr.sbin/bad144/bad144.8 @@ -172,7 +172,7 @@ errors, or the special .Tn SSE (skip sector) errors of RM80-type disks. This means that none of these errors can occur when reading the file -.Pa /vmunix +.Pa /386bsd to boot. Sectors 0-15 of the disk drive must also not have any of these errors. .Pp diff --git a/usr.sbin/bad144/bad144.c b/usr.sbin/bad144/bad144.c index 6bae2c689f..6246b455b5 100644 --- a/usr.sbin/bad144/bad144.c +++ b/usr.sbin/bad144/bad144.c @@ -55,6 +55,9 @@ static char sccsid[] = "@(#)bad144.c 5.19 (Berkeley) 4/11/91"; * RP06 sectors are marked as bad by inverting the format bit in the * header; on other drives the valid-sector bit is cleared. */ + +#define DKTYPENAMES + #include <sys/param.h> #include <sys/dkbad.h> #include <sys/ioctl.h> @@ -64,6 +67,7 @@ static char sccsid[] = "@(#)bad144.c 5.19 (Berkeley) 4/11/91"; #include <stdio.h> #include <paths.h> +#include <string.h> #define RETRIES 10 /* number of retries on reading old sectors */ #ifdef __386BSD__ @@ -72,7 +76,7 @@ static char sccsid[] = "@(#)bad144.c 5.19 (Berkeley) 4/11/91"; #define RAWPART "c" /* disk partition containing badsector tables */ #endif -int fflag, add, copy, verbose, nflag; +int fflag, add, copy, verbose, nflag, sflag; int compare(); int dups; int badfile = -1; /* copy of badsector table to use, -1 if any */ @@ -80,12 +84,29 @@ int badfile = -1; /* copy of badsector table to use, -1 if any */ struct dkbad curbad, oldbad; #define DKBAD_MAGIC 0x4321 +char *buf; char label[BBSIZE]; daddr_t size, getold(), badsn(); struct disklabel *dp; char name[BUFSIZ]; char *malloc(); off_t lseek(); +int bstart, bend; /* start and ending block numbers */ +char *p; /* temp dev name pointer */ +char devname[BUFSIZ]; + +#ifdef __386BSD__ +static char * parts[] = { + "ROOT ", + "SWAP ", + "386BSD", + "DISK ", + "DOS? ", + "USR2 ", + "USR1 ", + "USR " +}; +#endif main(argc, argv) int argc; @@ -93,8 +114,9 @@ main(argc, argv) { register struct bt_bad *bt; daddr_t sn, bn[126]; - int i, f, nbad, new, bad, errs; + int i, j, f, nbad, new, bad, errs; + setbuf(stdout, NULL); argc--, argv++; while (argc > 0 && **argv == '-') { (*argv)++; @@ -118,6 +140,10 @@ main(argc, argv) nflag++; verbose++; break; + case 's': /* scan partition */ + sflag++; + verbose++; + break; default: if (**argv >= '0' && **argv <= '4') { badfile = **argv - '0'; @@ -131,40 +157,30 @@ main(argc, argv) } if (argc < 1) { usage: - fprintf(stderr, - "usage: bad144 [ -f ] disk [ snum [ bn ... ] ]\n"); - fprintf(stderr, - "to read or overwrite bad-sector table, e.g.: bad144 hp0\n"); - fprintf(stderr, - "or bad144 -a [ -f ] [ -c ] disk bn ...\n"); + fprintf(stderr, "usage: bad144 [ -f ] disk [ snum [ bn ... ] ]\n"); + fprintf(stderr, "to read or overwrite bad-sector table, e.g., bad144 hp0\n"); + fprintf(stderr, "or bad144 -a [ -f ] [ -c ] disk bn ...\n"); fprintf(stderr, "where options are:\n"); fprintf(stderr, "\t-a add new bad sectors to the table\n"); fprintf(stderr, "\t-f reformat listed sectors as bad\n"); fprintf(stderr, "\t-c copy original sector to replacement\n"); + fprintf(stderr, "\t-s scan partition for bad sectors\n"); exit(1); } if (argv[0][0] != '/') - (void)sprintf(name, "%s/r%s%s", _PATH_DEV, argv[0], RAWPART); + (void)sprintf(name, "%sr%s%s", _PATH_DEV, argv[0], RAWPART); else strcpy(name, argv[0]); f = open(name, argc == 1? O_RDONLY : O_RDWR); if (f < 0) Perror(name); -#ifdef was - if (read(f, label, sizeof(label)) < 0) - Perror("read"); - for (dp = (struct disklabel *)(label + LABELOFFSET); - dp < (struct disklabel *) - (label + sizeof(label) - sizeof(struct disklabel)); - dp = (struct disklabel *)((char *)dp + 64)) - if (dp->d_magic == DISKMAGIC && dp->d_magic2 == DISKMAGIC) - break; -#else + p = strrchr(name, '/'); + strcpy(devname,++p); + devname[strlen(p)-1] = '\0'; /* obtain label and adjust to fit */ - dp = &label; + dp = (struct disklabel *)&label; if (ioctl(f, DIOCGDINFO, dp) < 0) Perror("ioctl DIOCGDINFO"); -#endif if (dp->d_magic != DISKMAGIC || dp->d_magic2 != DISKMAGIC /* dkcksum(lp) != 0 */ ) { fprintf(stderr, "Bad pack magic number (pack is unlabeled)\n"); @@ -181,18 +197,125 @@ usage: exit(1); } /* are we inside a DOS partition? */ + if (verbose) { + printf("device /dev/%s is a %s disk\n", devname, dktypenames[dp->d_type]); + j = dp->d_secpercyl; + for (i=0; i < dp->d_npartitions; i++) { + if (dp->d_partitions[i].p_size == 0) + continue; + printf("%s%c %s offset: %7d (%4d), size: %7d (%4d), type = %s\n", + devname, 'a'+i, parts[i], dp->d_partitions[i].p_offset, + dp->d_partitions[i].p_offset/j, + dp->d_partitions[i].p_size, + dp->d_partitions[i].p_size/j, + fstypenames[dp->d_partitions[i].p_fstype]); + } + } if (dp->d_partitions[0].p_offset) { - /* yes, rules change. assume bad tables at end of partition C, - which maps all of DOS partition we are within -wfj */ - size = dp->d_partitions[2].p_offset + dp->d_partitions[2].p_size; + /* + * yes, rules change. assume bad tables at end of partition C, + * which maps all of DOS partition we are within -wfj + */ + size = dp->d_partitions[2].p_offset + + dp->d_partitions[2].p_size; } else #endif - size = dp->d_nsectors * dp->d_ntracks * dp->d_ncylinders; + size = dp->d_nsectors * dp->d_ntracks * dp->d_ncylinders; + bstart = 0; + if (dp->d_partitions[2].p_size) { + bstart = dp->d_partitions[2].p_offset; + } + /* determine where to stop scanning */ + bend = dp->d_partitions[2].p_size + dp->d_partitions[2].p_offset; + bend -= (dp->d_nsectors + 126); + if (verbose) { + printf("secs: %d, tracks: %d, cyl: %d, sec/cyl: %d, start: %d, end: %d\n", + dp->d_nsectors, dp->d_ntracks, dp->d_ncylinders, + dp->d_secpercyl, bstart, bend); + } + if (sflag) { /* search for bad sectors */ + int curr_sec, tries, n; + int spc = dp->d_secpercyl; + int ss = dp->d_secsize; + int trk = dp->d_nsectors; + int step; + + if (buf == (char *)NULL) { + buf = malloc((unsigned)(trk*ss)); + if (buf == (char *)NULL) { + fprintf(stderr, "Out of memory\n"); + exit(20); + } + } + + printf("Starting scan of /dev/%sc at cylinder %d\n", devname, bstart/spc); + /* seek to start of parition c, we are in d */ + for (tries = 0; tries < RETRIES; tries++) { + if (lseek(f, bstart * ss, L_SET) < 0) { + Perror("lseek"); + } else + break; + } + step = trk; + for (curr_sec = bstart; curr_sec < bend; curr_sec += step) { + int gotone = 0; + + if (verbose) { + if ((curr_sec % spc) == 0) + printf("\r%4d(%7d)", + curr_sec/spc, curr_sec); + } + for (tries = 0; tries < RETRIES; tries++) { + if (lseek(f, curr_sec * ss, L_SET) < 0) { + fprintf(stderr, + "\nbad144: can't seek sector, %d\n", + curr_sec); + gotone = 1; + } else + break; + } + if (gotone) { + fprintf(stderr, + "\nbad144: bad sector (seek), %d\n", + curr_sec); + step = 1; + continue; + } + if (step == trk) { + if ((n = read(f, buf, (ss*trk))) == (ss*trk)) { + continue; + } + } + /* switch to single sector reads */ + lseek(f, curr_sec * ss, L_SET); + step = 1; + for (tries = 0; tries < RETRIES; tries++) { + if ((n = read(f, buf, ss)) != ss) { + fprintf(stderr, + "\nbad144: can't read sector, + %d\n", curr_sec); + gotone = 1; + lseek(f, curr_sec * ss, L_SET); + } else { + if ((curr_sec % trk) == 0) { + step = trk; + } + break; + } + } + if (gotone) { + fprintf(stderr, + "\nbad144: bad sector (read), %d\n", + curr_sec); + continue; + } + } + } argc--; argv++; if (argc == 0) { sn = getold(f, &oldbad); - printf("bad block information at sector %d in %s:\n", + printf("\nbad block information at sector %d in %s:\n", sn, name); printf("cartridge serial number: %d(10)\n", oldbad.bt_csn); switch (oldbad.bt_flag) { @@ -232,7 +355,7 @@ usage: printf("Had %d bad sectors, adding %d\n", i, argc); if (i + argc > 126) { printf("bad144: not enough room for %d more sectors\n", - argc); + argc); printf("limited to 126 by information format\n"); exit(1); } @@ -284,8 +407,7 @@ usage: qsort((char *)curbad.bt_bad, nbad, sizeof (struct bt_bad), compare); if (dups) { - fprintf(stderr, -"bad144: bad sectors have been duplicated; can't add existing sectors\n"); + fprintf(stderr, "bad144: bad sectors have been duplicated; can't add existing sectors\n"); exit(3); } shift(f, nbad, nbad-new); @@ -296,13 +418,13 @@ usage: i = badfile * 2; for (; i < 10 && i < dp->d_nsectors; i += 2) { if (lseek(f, dp->d_secsize * (size - dp->d_nsectors + i), - L_SET) < 0) + L_SET) < 0) Perror("lseek"); if (verbose) printf("write badsect file at %d\n", - size - dp->d_nsectors + i); + size - dp->d_nsectors + i); if (nflag == 0 && write(f, (caddr_t)&curbad, sizeof(curbad)) != - sizeof(curbad)) { + sizeof(curbad)) { char msg[80]; (void)sprintf(msg, "bad144: write bad sector file %d", i/2); @@ -372,7 +494,7 @@ checkold() if (oldbad.bt_flag != DKBAD_MAGIC) { fprintf(stderr, "bad144: %s: bad flag in bad-sector table\n", - name); + name); errors++; } if (oldbad.bt_mbz != 0) { @@ -384,29 +506,29 @@ checkold() if (bt->bt_cyl == 0xffff && bt->bt_trksec == 0xffff) break; if ((bt->bt_cyl >= dp->d_ncylinders) || - ((bt->bt_trksec >> 8) >= dp->d_ntracks) || - ((bt->bt_trksec & 0xff) >= dp->d_nsectors)) { + ((bt->bt_trksec >> 8) >= dp->d_ntracks) || + ((bt->bt_trksec & 0xff) >= dp->d_nsectors)) { fprintf(stderr, - "bad144: cyl/trk/sect out of range in existing entry: "); + "bad144: cyl/trk/sect out of range in existing entry: "); fprintf(stderr, "sn=%d, cn=%d, tn=%d, sn=%d\n", - badsn(bt), bt->bt_cyl, bt->bt_trksec>>8, - bt->bt_trksec & 0xff); + badsn(bt), bt->bt_cyl, bt->bt_trksec>>8, + bt->bt_trksec & 0xff); errors++; } sn = (bt->bt_cyl * dp->d_ntracks + - (bt->bt_trksec >> 8)) * - dp->d_nsectors + (bt->bt_trksec & 0xff); + (bt->bt_trksec >> 8)) * + dp->d_nsectors + (bt->bt_trksec & 0xff); if (i > 0 && sn < lsn && !warned) { - fprintf(stderr, - "bad144: bad sector file is out of order\n"); - errors++; - warned++; + fprintf(stderr, + "bad144: bad sector file is out of order\n"); + errors++; + warned++; } if (i > 0 && sn == lsn) { - fprintf(stderr, - "bad144: bad sector file contains duplicates (sn %d)\n", - sn); - errors++; + fprintf(stderr, + "bad144: bad sector file contains duplicates (sn %d)\n", + sn); + errors++; } lsn = sn; } @@ -431,19 +553,19 @@ shift(f, new, old) new--; old--; while (new >= 0 && new != old) { if (old < 0 || - compare(&curbad.bt_bad[new], &oldbad.bt_bad[old]) > 0) { + compare(&curbad.bt_bad[new], &oldbad.bt_bad[old]) > 0) { /* * Insert new replacement here-- copy original * sector if requested and possible, * otherwise write a zero block. */ if (!copy || - !blkcopy(f, badsn(&curbad.bt_bad[new]), repl - new)) + !blkcopy(f, badsn(&curbad.bt_bad[new]), repl - new)) blkzero(f, repl - new); } else { if (blkcopy(f, repl - old, repl - new) == 0) - fprintf(stderr, - "Can't copy replacement sector %d to %d\n", + fprintf(stderr, + "Can't copy replacement sector %d to %d\n", repl-old, repl-new); old--; } @@ -451,8 +573,6 @@ shift(f, new, old) } } -char *buf; - /* * Copy disk sector s1 to s2. */ @@ -534,7 +654,7 @@ badsn(bt) register struct bt_bad *bt; { return ((bt->bt_cyl*dp->d_ntracks + (bt->bt_trksec>>8)) * dp->d_nsectors - + (bt->bt_trksec&0xff)); + + (bt->bt_trksec&0xff)); } #ifdef vax @@ -591,7 +711,7 @@ hpupformat(fp, dp, blk, buf, count) if (count < sizeof(struct hpuphdr)) { hdr->hpup_cyl = (HPUP_OKSECT | HPUP_16BIT) | - (blk / (dp->d_nsectors * dp->d_ntracks)); + (blk / (dp->d_nsectors * dp->d_ntracks)); sect = blk % (dp->d_nsectors * dp->d_ntracks); hdr->hpup_track = (u_char)(sect / dp->d_nsectors); hdr->hpup_sect = (u_char)(sect % dp->d_nsectors); @@ -610,7 +730,7 @@ rp06format(fp, dp, blk, buf, count) if (count < sizeof(struct rp06hdr)) { fprintf(stderr, "Can't read header on blk %d, can't reformat\n", - blk); + blk); return (-1); } return (0); @@ -662,7 +782,7 @@ format(fd, blk) if (ioctl(fd, DIOCRFORMAT, &fop) < 0) perror("bad144: read format"); if (fp->f_routine && - (*fp->f_routine)(fp, dp, blk, buf, fop.df_count) != 0) + (*fp->f_routine)(fp, dp, blk, buf, fop.df_count) != 0) return; if (fp->f_bic) { struct hpuphdr *xp = (struct hpuphdr *)buf; diff --git a/usr.sbin/chown/Makefile b/usr.sbin/chown/Makefile index 20a3b59ee5..c0b492c459 100644 --- a/usr.sbin/chown/Makefile +++ b/usr.sbin/chown/Makefile @@ -2,8 +2,8 @@ PROG= chown CFLAGS+=-DSUPPORT_DOT -MAN1= chgrp.0 -MAN8= chown.0 +MAN1= chgrp.1 +MAN8= chown.8 LINKS= ${BINDIR}/chown /usr/bin/chgrp .include <bsd.prog.mk> diff --git a/usr.sbin/chroot/Makefile b/usr.sbin/chroot/Makefile index 12a397352b..7c565b2619 100644 --- a/usr.sbin/chroot/Makefile +++ b/usr.sbin/chroot/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= chroot -MAN8= chroot.0 +MAN8= chroot.8 .include <bsd.prog.mk> diff --git a/usr.sbin/config/Makefile b/usr.sbin/config/Makefile index ee1d7fa444..02e1d8a0a7 100644 --- a/usr.sbin/config/Makefile +++ b/usr.sbin/config/Makefile @@ -4,7 +4,7 @@ PROG= config CFLAGS+=-I. -I${.CURDIR} SRCS= config.c main.c lang.c mkioconf.c mkmakefile.c mkglue.c mkheaders.c \ mkswapconf.c -MAN8= config.0 +MAN8= config.8 DPADD= ${LIBLN} LDADD= -lln CLEANFILES+=y.tab.h lang.c config.c y.tab.c diff --git a/usr.sbin/config/config.8 b/usr.sbin/config/config.8 index 9798b537e7..1712217a18 100644 --- a/usr.sbin/config/config.8 +++ b/usr.sbin/config/config.8 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)config.8 6.5 (Berkeley) 3/16/91 +.\" from: @(#)config.8 6.5 (Berkeley) 3/16/91 +.\" $Id: config.8,v 1.6 1993/08/07 07:53:27 cgd Exp $ .\" .Dd March 16, 1991 .Dt CONFIG 8 @@ -39,7 +40,7 @@ .Nd build system configuration files .Sh SYNOPSIS .Nm config -.Op Fl p +.Op Fl gp .Ar SYSTEM_NAME .Sh DESCRIPTION .Pp @@ -61,6 +62,12 @@ section below) Available option and operand: .Pp .Bl -tag -width SYSTEM_NAME +.It Fl g +If the +.Fl g +option is supplied, +.Nm config +will configure a system for debugging. .It Fl p If the .Fl p @@ -70,6 +77,7 @@ will configure a system for profiling; for example, .Xr kgmon 8 and .Xr gprof 1 . + .It Ar SYSTEM_NAME specifies the name of the system configuration file containing device specifications, configuration options @@ -80,24 +88,22 @@ and other system parameters for one system configuration. should be run from the .Pa conf subdirectory of the system source (usually -.Pa /sys/conf ) . +.Pa /sys/ARCH/conf ) . .Nm Config assumes the directory -.Pa ../SYSTEM_NAME +.Pa ../../compile/SYSTEM_NAME exists and places all output files there. The output of .Nm config consists of a number of files; for the -.Tn VAX , +.Tn i386 , they are: .Pa ioconf.c , a description of what I/O devices are attached to the system; -.Pa ubglue.s , +.Pa vector.s , a set of interrupt service routines for devices -attached to the -.Tn UNIBUS ; -.Pa ubvec.s , +attached to the bus plus offsets into a structure used for counting per-device interrupts; .Pa Makefile , used by @@ -128,20 +134,20 @@ should be run again. Attempts to compile a system that had configuration errors are likely to fail. .Sh FILES -.Bl -tag -width /sys/conf/Makefile.vax -compact -.It Pa /sys/conf/Makefile.vax -generic makefile for the -.Tn VAX +.Bl -tag -width /sys/i386/conf/Makefile.i386 -compact .It Pa /sys/conf/files list of common files system is built from -.It Pa /sys/conf/files.vax +.It Pa /sys/i386/conf/Makefile.i386 +generic makefile for the +.Tn i386 +.It Pa /sys/i386/conf/files.i386 list of -.Tn VAX +.Tn i386 specific files -.It Pa /sys/conf/devices.vax +.It Pa /sys/i386/conf/devices.i386 name to major device mapping file for the -.Tn VAX -.It Pa /sys/conf/files. Ns Em ERNIE +.Tn i386 +.It Pa /sys/i386/conf/files. Ns Em ERNIE list of files specific to .Em ERNIE system diff --git a/usr.sbin/config/config.h b/usr.sbin/config/config.h index 8959710cde..726bcc3e53 100644 --- a/usr.sbin/config/config.h +++ b/usr.sbin/config/config.h @@ -191,6 +191,8 @@ int dst; int profiling; int debugging; +int maxfdescs; int maxusers; +u_int loadaddress; #define eq(a,b) (!strcmp(a,b)) diff --git a/usr.sbin/config/config.y b/usr.sbin/config/config.y index 76aa58e31a..31a584db82 100644 --- a/usr.sbin/config/config.y +++ b/usr.sbin/config/config.y @@ -31,6 +31,7 @@ %token MACHINE %token MAJOR %token MASTER +%token MAXFDESCS %token MAXUSERS %token MINOR %token MINUS @@ -200,6 +201,8 @@ Config_spec: = { zone = -$3; dst = $5; check_tz(); } | TIMEZONE MINUS FPNUMBER DST = { zone = -$3; dst = 1; check_tz(); } | + MAXFDESCS NUMBER + = { maxfdescs = $2; }; | MAXUSERS NUMBER = { maxusers = $2; }; @@ -219,12 +222,18 @@ System_parameter_list: ; System_parameter: - swap_spec + addr_spec + | swap_spec | root_spec | dump_spec | arg_spec ; +addr_spec: + AT NUMBER + = { loadaddress = $2; }; + ; + swap_spec: SWAP optional_on swap_device_list ; diff --git a/usr.sbin/config/lang.l b/usr.sbin/config/lang.l index a69d4bb6e6..9e85a4cdc7 100644 --- a/usr.sbin/config/lang.l +++ b/usr.sbin/config/lang.l @@ -78,6 +78,7 @@ struct kt { { "major", MAJOR }, { "makeoptions", MAKEOPTIONS }, { "master", MASTER }, + { "maxfdescs", MAXFDESCS }, { "maxusers", MAXUSERS }, { "minor", MINOR }, #if MACHINE_I386 diff --git a/usr.sbin/config/main.c b/usr.sbin/config/main.c index fe1b21e6df..2ea5c3d7ec 100644 --- a/usr.sbin/config/main.c +++ b/usr.sbin/config/main.c @@ -101,6 +101,7 @@ usage: fputs("usage: config [-gp] sysname\n", stderr); exit(2); } + loadaddress = -1; dtab = NULL; confp = &conf_list; if (yyparse()) diff --git a/usr.sbin/config/mkglue.c b/usr.sbin/config/mkglue.c index 5ea03cfc9b..8702448d62 100644 --- a/usr.sbin/config/mkglue.c +++ b/usr.sbin/config/mkglue.c @@ -29,22 +29,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00158 - * -------------------- ----- ---------------------- - * - * 26 Mar 93 Rodney W. Grimes Added interrupt counters for vmstat, - * also false and stray counter names. - * 26 Apr 93 Bruce Evans Support for intr-0.1 - * 17 May 93 Rodney W. Grimes Changed id_num to be unique. Misc - * other changes to make intr-0.1 work - * with GENERICISA. - * Interrupt counter names are now built - * using BUILD_VECTORS, no longer output - * by mkglue.c - * */ #ifndef lint diff --git a/usr.sbin/config/mkioconf.c b/usr.sbin/config/mkioconf.c index 70c142e658..1ec61d6cee 100644 --- a/usr.sbin/config/mkioconf.c +++ b/usr.sbin/config/mkioconf.c @@ -29,20 +29,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 3 00158 - * -------------------- ----- ---------------------- - * - * 27 Feb 93 Chris Demetriou Add proper flag handling. - * 08 Apr 93 Phil Sutherland Add support for devices without irq's - * Rodney W. Grimes Cleaned up indents, fixed table formats - * 25 Apr 93 Bruce Evans Support for intr-0.1 - * 25 Apr 93 Rodney W. Grimes Reduce duplicate code with a common - * isa_devtab() routine. - * 16 May 93 Rodney W. Grimes Give warning on irq 2 and remap it to - * irq 9. */ #ifndef lint @@ -639,7 +625,7 @@ i386_ioconf() fprintf(fp, "#include \"sys/param.h\"\n"); fprintf(fp, "#include \"sys/buf.h\"\n"); fprintf(fp, "\n"); - fprintf(fp, "#define V(s) V##s\n"); + fprintf(fp, "#define V(s)\t__CONCAT(V,s)\n"); fprintf(fp, "#define C (caddr_t)\n\n"); /* * First print the isa initialization structures diff --git a/usr.sbin/config/mkmakefile.c b/usr.sbin/config/mkmakefile.c index 8eb79176ba..86654bfc10 100644 --- a/usr.sbin/config/mkmakefile.c +++ b/usr.sbin/config/mkmakefile.c @@ -29,15 +29,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 2 00096 - * -------------------- ----- ---------------------- - * - * 29 Jun 92 Chris G. Demetriou Fix Version number update - * 15 Feb 93 Julian Elischer allow comments (leading #) in - * files and files.i386 */ #ifndef lint @@ -55,6 +46,8 @@ static char sccsid[] = "@(#)mkmakefile.c 5.33 (Berkeley) 7/1/91"; #include "y.tab.h" #include "config.h" +#define DEF_MAXFDESCS 2048 + #define next_word(fp, wd) \ { register char *word = get_word(fp); \ if (word == (char *)EOF) \ @@ -183,6 +176,10 @@ makefile() up = &users[MACHINE_VAX-1]; } else up = &users[machine-1]; + if (maxfdescs == 0) { + printf("maxfdescs not specified; %d assumed\n", DEF_MAXFDESCS); + maxfdescs = DEF_MAXFDESCS; + } if (maxusers == 0) { printf("maxusers not specified; %d assumed\n", up->u_default); maxusers = up->u_default; @@ -191,8 +188,11 @@ makefile() maxusers = up->u_min; } else if (maxusers > up->u_max) printf("warning: maxusers > %d (%d)\n", up->u_max, maxusers); - fprintf(ofp, "PARAM=-DTIMEZONE=%d -DDST=%d -DMAXUSERS=%d\n", - zone, dst, maxusers); + fprintf(ofp, "PARAM=-DTIMEZONE=%d -DDST=%d -DMAXUSERS=%d -DMAXFDESCS=%d\n", + zone, dst, maxusers, maxfdescs); + if (loadaddress != -1) { + fprintf(ofp, "LOAD_ADDRESS=%X\n", loadaddress); + } for (op = mkopt; op; op = op->op_next) fprintf(ofp, "%s=%s\n", op->op_name, op->op_value); if (debugging) @@ -596,7 +596,7 @@ do_systemspec(f, fl, first) fprintf(f, "%s: ${SYSTEM_DEP} swap%s.o", fl->f_needs, fl->f_fn); if (first) /* 29 Jun 92*/ - fprintf(f, " newvers"); + fprintf(f, " vers.o"); fprintf(f, "\n\t${SYSTEM_LD_HEAD}\n"); fprintf(f, "\t${SYSTEM_LD} swap%s.o\n", fl->f_fn); fprintf(f, "\t${SYSTEM_LD_TAIL}\n\n"); diff --git a/usr.sbin/config/mkswapconf.c b/usr.sbin/config/mkswapconf.c index c0307a5f4a..f6a237cb06 100644 --- a/usr.sbin/config/mkswapconf.c +++ b/usr.sbin/config/mkswapconf.c @@ -29,13 +29,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00096 - * -------------------- ----- ---------------------- - * - * 15 Feb 93 Julian Elischer Allow comments (leading #) in device file */ #ifndef lint diff --git a/usr.sbin/dbsym/Makefile b/usr.sbin/dbsym/Makefile index 95ab527a9d..e1fbf2c2cc 100644 --- a/usr.sbin/dbsym/Makefile +++ b/usr.sbin/dbsym/Makefile @@ -1,3 +1,5 @@ +# $Id$ + PROG= dbsym NOMAN= noman diff --git a/usr.sbin/dbsym/dbsym.c b/usr.sbin/dbsym/dbsym.c index 46514b4c1e..7eb3de5ea7 100644 --- a/usr.sbin/dbsym/dbsym.c +++ b/usr.sbin/dbsym/dbsym.c @@ -1,12 +1,21 @@ /* Written by Pace Willisson (pace@blitz.com) * and placed in the public domain. */ + +#ifndef lint +static char rcsid[] = "$Id: dbsym.c,v 1.8 1993/08/02 17:57:02 mycroft Exp $"; +#endif /* not lint */ + #include <stdio.h> +#include <stdlib.h> #include <a.out.h> +#include <stab.h> +#include <machine/param.h> -char *malloc (); +#define FILE_OFFSET(vadr) (((vadr) - text_adr) - N_DATADDR(hdr) + \ + N_DATOFF(hdr) + N_TXTADDR(hdr)) -#define FILE_OFFSET(vadr) (((vadr) & ~0xff000000)-N_DATADDR(hdr)+N_DATOFF(hdr)) +u_int text_adr = KERNBASE; struct nlist *old_syms; int num_old_syms; @@ -24,10 +33,13 @@ int db_symtab_adr; int avail; +int force = 0; +int zap_locals = 0; +int debugging = 0; usage () { - fprintf (stderr, "usage: dbsym file\n"); + fprintf (stderr, "usage: dbsym [-fgx] [-T addr] file\n"); exit (1); } @@ -46,8 +58,22 @@ char **argv; int len; - while ((c = getopt (argc, argv, "")) != EOF) { + while ((c = getopt (argc, argv, "fgxT:")) != EOF) { switch (c) { + case 'f': + force = 1; + break; + case 'g': + debugging = 1; + break; + case 'x': + zap_locals = 1; + break; + case 'T': + text_adr = strtoul(optarg, &p, 16); + if (*p) + err("illegal text address: %s", optarg); + break; default: usage (); } @@ -113,17 +139,44 @@ char **argv; nsp = new_syms; for (i = 0, sp = old_syms; i < num_old_syms; i++, sp++) { + if (zap_locals && !(sp->n_type & N_EXT)) + continue; + if (sp->n_type & N_STAB) + switch (sp->n_type & ~N_EXT) { + case N_SLINE: + if (debugging) + *nsp++ = *sp; + continue; + case N_FUN: + case N_PSYM: + case N_SO: + if (!debugging) + continue; + goto skip_tests; + break; + default: + continue; + } + + if ((sp->n_type & ~N_EXT) == N_UNDF) + continue; + + if (!debugging && (sp->n_type & ~N_EXT) == N_FN) continue; + if (sp->n_un.n_strx == 0) continue; - if (sp->n_value < 0xfe000000) + if (sp->n_value < text_adr) continue; - if (sp->n_value >= 0xff000000) + if (sp->n_value > (text_adr + hdr.a_text + hdr.a_data + + hdr.a_bss)) continue; + skip_tests: + name = old_strtab + sp->n_un.n_strx; len = strlen (name); @@ -131,12 +184,15 @@ char **argv; if (len == 0) continue; - if (len >= 2 && name[len - 2] == '.' && name[len - 1] == 'o') + if (strcmp (name, "gcc_compiled.") == 0) continue; - if (strcmp (name, "gcc_compiled.") == 0) + if (strcmp (name, "gcc2_compiled.") == 0) continue; + if (strcmp (name, "___gnu_compiled_c") == 0) + continue; + *nsp = *sp; nsp->n_un.n_strx = new_strtab_size; @@ -150,10 +206,12 @@ char **argv; db_symtabsize_adr = sp->n_value; } - if (db_symtab_adr == 0 || db_symtabsize_adr == 0) { - fprintf (stderr, "couldn't find db_symtab symbols\n"); - exit (1); - } + if (db_symtab_adr == 0 || db_symtabsize_adr == 0) + if (!force) { + fprintf (stderr, "couldn't find db_symtab symbols\n"); + exit (1); + } else + exit (0); *(int *)new_strtab = new_strtab_size; num_new_syms = nsp - new_syms; diff --git a/usr.sbin/dev_mkdb/Makefile b/usr.sbin/dev_mkdb/Makefile index 92e43176a1..3421eff60b 100644 --- a/usr.sbin/dev_mkdb/Makefile +++ b/usr.sbin/dev_mkdb/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.1 (Berkeley) 2/12/91 PROG= dev_mkdb -MAN8= dev_mkdb.0 +MAN8= dev_mkdb.8 .include <bsd.prog.mk> diff --git a/usr.sbin/dev_mkdb/dev_mkdb.c b/usr.sbin/dev_mkdb/dev_mkdb.c index 3e98ea3d41..46f9cf7ab2 100644 --- a/usr.sbin/dev_mkdb/dev_mkdb.c +++ b/usr.sbin/dev_mkdb/dev_mkdb.c @@ -92,7 +92,7 @@ main(argc, argv) (void)snprintf(dbtmp, sizeof(dbtmp), "%s/dev.tmp", _PATH_VARRUN); (void)snprintf(dbname, sizeof(dbtmp), "%s/dev.db", _PATH_VARRUN); - db = hash_open(dbtmp, O_CREAT|O_WRONLY|O_EXCL, DEFFILEMODE, + db = dbopen(dbtmp, O_CREAT|O_RDWR|O_EXCL, DEFFILEMODE, DB_HASH, (HASHINFO *)NULL); if (!db) error(dbtmp); diff --git a/usr.sbin/diskpart/Makefile b/usr.sbin/diskpart/Makefile index f88e14874e..1803909b70 100644 --- a/usr.sbin/diskpart/Makefile +++ b/usr.sbin/diskpart/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= diskpart -MAN8= diskpart.0 +MAN8= diskpart.8 .include <bsd.prog.mk> diff --git a/usr.sbin/diskpart/diskpart.c b/usr.sbin/diskpart/diskpart.c index 19f2ba6bea..213becb9e2 100644 --- a/usr.sbin/diskpart/diskpart.c +++ b/usr.sbin/diskpart/diskpart.c @@ -123,7 +123,7 @@ main(argc, argv) argc--, argv++; if (argc < 1) { fprintf(stderr, - "usage: disktab [ -p ] [ -d ] [ -s size ] disk-type\n"); + "usage: diskpart [ -p ] [ -d ] [ -s size ] disk-type\n"); exit(1); } if (argc > 0 && strcmp(*argv, "-p") == 0) { diff --git a/usr.sbin/edquota/Makefile b/usr.sbin/edquota/Makefile index 5dbcdac550..2d074521e1 100644 --- a/usr.sbin/edquota/Makefile +++ b/usr.sbin/edquota/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= edquota -MAN8= edquota.0 +MAN8= edquota.8 .include <bsd.prog.mk> diff --git a/usr.sbin/flcopy/Makefile b/usr.sbin/flcopy/Makefile index 8f07327b14..2cfc59848c 100644 --- a/usr.sbin/flcopy/Makefile +++ b/usr.sbin/flcopy/Makefile @@ -2,6 +2,6 @@ PROG= flcopy MANSUBDIR=/vax -MAN8= flcopy.0 +MAN8= flcopy.8 .include <bsd.prog.mk> diff --git a/usr.sbin/gettable/Makefile b/usr.sbin/gettable/Makefile index 9e9c8fae3f..0b82a72d4e 100644 --- a/usr.sbin/gettable/Makefile +++ b/usr.sbin/gettable/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= gettable -MAN8= gettable.0 +MAN8= gettable.8 .include <bsd.prog.mk> diff --git a/usr.sbin/gettable/gettable.c b/usr.sbin/gettable/gettable.c index 3524af9206..0f7c2411f0 100644 --- a/usr.sbin/gettable/gettable.c +++ b/usr.sbin/gettable/gettable.c @@ -56,7 +56,7 @@ static char sccsid[] = "@(#)gettable.c 5.6 (Berkeley) 3/2/91"; #define equaln(s1, s2, n) (!strncmp(s1, s2, n)) -struct sockaddr_in sin; +struct sockaddr_in s_in; char buf[BUFSIZ]; char *outfile = OUTFILE; @@ -102,19 +102,19 @@ main(argc, argv) host = hp->h_name; if (argc > 0) outfile = *argv; - sin.sin_family = hp->h_addrtype; + s_in.sin_family = hp->h_addrtype; s = socket(hp->h_addrtype, SOCK_STREAM, 0); if (s < 0) { perror("gettable: socket"); exit(4); } - if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) { + if (bind(s, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) { perror("gettable: bind"); exit(5); } - bcopy(hp->h_addr, &sin.sin_addr, hp->h_length); - sin.sin_port = sp->s_port; - if (connect(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) { + bcopy(hp->h_addr, &s_in.sin_addr, hp->h_length); + s_in.sin_port = sp->s_port; + if (connect(s, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) { perror("gettable: connect"); exit(6); } diff --git a/usr.sbin/htable/Makefile b/usr.sbin/htable/Makefile index e74dee36ce..178fdab659 100644 --- a/usr.sbin/htable/Makefile +++ b/usr.sbin/htable/Makefile @@ -2,7 +2,7 @@ PROG= htable SRCS= htable.c parse.c scan.c -MAN8= htable.0 +MAN8= htable.8 CFLAGS+=-I. -I${.CURDIR} CLEANFILES+=parse.c scan.c y.tab.h diff --git a/usr.sbin/inetd/Makefile b/usr.sbin/inetd/Makefile index 20ace73f16..8096163bee 100644 --- a/usr.sbin/inetd/Makefile +++ b/usr.sbin/inetd/Makefile @@ -1,8 +1,11 @@ -# @(#)Makefile 5.5 (Berkeley) 6/29/90 +# from: @(#)Makefile 5.5 (Berkeley) 6/29/90 +# $Id: Makefile,v 1.4 1993/09/23 17:33:35 jtc Exp $ PROG= inetd -LDADD= -lutil -MAN8= inetd.0 +MAN8= inetd.8 MLINKS= inetd.8 inetd.5 +DPADD= ${LIBUTIL} ${LIBRPC} +LDADD= -lutil -lrpc + .include <bsd.prog.mk> diff --git a/usr.sbin/inetd/inetd.8 b/usr.sbin/inetd/inetd.8 index 5dbb1982cc..17e233a2c9 100644 --- a/usr.sbin/inetd/inetd.8 +++ b/usr.sbin/inetd/inetd.8 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)inetd.8 6.7 (Berkeley) 3/16/91 +.\" from: @(#)inetd.8 6.7 (Berkeley) 3/16/91 +.\" $Id: inetd.8,v 1.2 1993/09/23 17:31:37 jtc Exp $ .\" .Dd March 16, 1991 .Dt INETD 8 @@ -41,6 +42,7 @@ .Sh SYNOPSIS .Nm inetd .Op Fl d +.Op Fl l .Op Ar configuration file .Sh DESCRIPTION .Nm Inetd @@ -64,6 +66,8 @@ The option available for .Bl -tag -width Ds .It Fl d Turns on debugging. +.It Fl l +Turns on logging. .El .Pp Upon execution, @@ -87,6 +91,20 @@ server program server program arguments .Ed .Pp +To specify an +.Em Sun-RPC +based service, the entry would contain these fields. +.Pp +.Bd -unfilled -offset indent -compact +service name/version +socket type +rpc/protocol +wait/nowait +user +server program +server program arguments +.Ed +.Pp The .Em service-name entry is the name of a valid service in @@ -98,7 +116,19 @@ services (discussed below), the service name .Em must be the official name of the service (that is, the first entry in -.Pa /etc/services ) . +.Pa /etc/services ) . +When used to specify a +.Em Sun-RPC +based service, this field is a valid RPC service name in +the file +.Pa /etc/rpc . +The part on the right of the +.Dq / +is the RPC version number. This +can simply be a single numeric argument or a range of versions. +A range is bounded by the low version to the high version - +.Dq rusers/1-3 . + .Pp The .Em socket-type @@ -120,6 +150,12 @@ Examples might be .Dq tcp or .Dq udp . +Rpc based services are specified with the +.Dq rpc/tcp +or +.Dq rpc/udp +service type. + .Pp The .Em wait/nowait @@ -201,6 +237,15 @@ details of these services, consult the appropriate .Tn RFC from the Network Information Center. .Pp +When given the +.Fl l +option +.Nm Inetd +will log an entry to syslog each time an +.Xr accept 2 +is made, which notes the +service selected and the IP-number of the remote requestor. +.Pp .Nm Inetd rereads its configuration file when it receives a hangup signal, .Dv SIGHUP . @@ -220,3 +265,8 @@ The .Nm command appeared in .Bx 4.3 . +Support for +.Em Sun-RPC +based services is modelled after that +provided by +.Em Sun-OS 4.1 . diff --git a/usr.sbin/inetd/inetd.c b/usr.sbin/inetd/inetd.c index 0340273b1a..9e0a5d4455 100644 --- a/usr.sbin/inetd/inetd.c +++ b/usr.sbin/inetd/inetd.c @@ -38,7 +38,8 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)inetd.c 5.30 (Berkeley) 6/3/91"; +/*static char sccsid[] = "from: @(#)inetd.c 5.30 (Berkeley) 6/3/91";*/ +static char rcsid[] = "$Id: inetd.c,v 1.3 1993/10/11 21:47:50 jkh Exp $"; #endif /* not lint */ /* @@ -63,7 +64,7 @@ static char sccsid[] = "@(#)inetd.c 5.30 (Berkeley) 6/3/91"; * Inetd uses a configuration file which is read at startup * and, possibly, at some later time in response to a hangup signal. * The configuration file is ``free format'' with fields given in the - * order shown below. Continuation lines for an entry must being with + * order shown below. Continuation lines for an entry must begin with * a space or tab. All fields must be present in each entry. * * service name must be in /etc/services @@ -74,6 +75,15 @@ static char sccsid[] = "@(#)inetd.c 5.30 (Berkeley) 6/3/91"; * server program full path name * server program arguments maximum of MAXARGS (20) * + * For RPC services + * service name/version must be in /etc/rpc + * socket type stream/dgram/raw/rdm/seqpacket + * protocol must be in /etc/protocols + * wait/nowait single-threaded/multi-threaded + * user user to run daemon as + * server program full path name + * server program arguments maximum of MAXARGS (20) + * * Comment lines are indicated by a `#' in column 1. */ #include <sys/param.h> @@ -94,7 +104,10 @@ static char sccsid[] = "@(#)inetd.c 5.30 (Berkeley) 6/3/91"; #include <syslog.h> #include <pwd.h> #include <stdio.h> +#include <stdlib.h> +#include <unistd.h> #include <string.h> +#include <rpc/rpc.h> #include "pathnames.h" #define TOOMANY 40 /* don't start more than TOOMANY */ @@ -103,18 +116,14 @@ static char sccsid[] = "@(#)inetd.c 5.30 (Berkeley) 6/3/91"; #define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM)) -extern int errno; - -void config(), reapchild(), retry(); -char *index(); -char *malloc(); - int debug = 0; +int log = 0; int nsock, maxsock; fd_set allsock; int options; int timingout; struct servent *sp; +struct rpcent *rpc; struct servtab { char *se_service; /* name of service */ @@ -129,42 +138,80 @@ struct servtab { char *se_argv[MAXARGV+1]; /* program arguments */ int se_fd; /* open descriptor */ struct sockaddr_in se_ctrladdr;/* bound address */ + int se_rpc; /* ==1 if this is an RPC service */ + int se_rpc_prog; /* RPC program number */ + u_int se_rpc_lowvers; /* RPC low version */ + u_int se_rpc_highvers; /* RPC high version */ int se_count; /* number started since se_time */ struct timeval se_time; /* start of se_count */ struct servtab *se_next; } *servtab; -int echo_stream(), discard_stream(), machtime_stream(); -int daytime_stream(), chargen_stream(); -int echo_dg(), discard_dg(), machtime_dg(), daytime_dg(), chargen_dg(); +/* Externals */ + +/* should be in <rpc/rpc.h> ?? */ +int pmap_unset(u_long prognum,u_long versnum); +int pmap_set(u_long prognum,u_long versnum,int protocol,u_short port); + +/* comes from -lutil ?? */ +void daemon(); + +/* Prototypes */ +void reapchild(); +void config(); +void unregisterrpc(struct servtab *sep); +void retry(); +void setup(struct servtab *sep); +struct servtab * enter(struct servtab *cp); +int setconfig(); +void endconfig(); +struct servtab * getconfigent(); +void freeconfig(struct servtab *cp); +char * skip(char **cpp); +char * nextline(FILE *fd); +char * newstr(char *cp); +void setproctitle(char *a, int s); +void echo_stream(int s,struct servtab *sep); +void echo_dg(int s,struct servtab *sep); +void discard_stream(int s,struct servtab *sep); +void discard_dg(int s,struct servtab *sep); +void initring(); +void chargen_stream(int s,struct servtab *sep); +void chargen_dg(int s,struct servtab *sep); +long machtime(); +void machtime_stream(int s,struct servtab *sep); +void machtime_dg(int s,struct servtab *sep); +void daytime_stream(int s,struct servtab *sep); +void daytime_dg(int s,struct servtab *sep); +void print_service(char *action, struct servtab *sep); struct biltin { char *bi_service; /* internally provided service name */ int bi_socktype; /* type of socket supported */ short bi_fork; /* 1 if should fork before call */ short bi_wait; /* 1 if should wait for child */ - int (*bi_fn)(); /* function which performs it */ + void (*bi_fn)(); /* function which performs it */ } biltins[] = { /* Echo received data */ - "echo", SOCK_STREAM, 1, 0, echo_stream, - "echo", SOCK_DGRAM, 0, 0, echo_dg, + { "echo", SOCK_STREAM, 1, 0, echo_stream }, + { "echo", SOCK_DGRAM, 0, 0, echo_dg }, /* Internet /dev/null */ - "discard", SOCK_STREAM, 1, 0, discard_stream, - "discard", SOCK_DGRAM, 0, 0, discard_dg, + { "discard", SOCK_STREAM, 1, 0, discard_stream }, + { "discard", SOCK_DGRAM, 0, 0, discard_dg }, /* Return 32 bit time since 1970 */ - "time", SOCK_STREAM, 0, 0, machtime_stream, - "time", SOCK_DGRAM, 0, 0, machtime_dg, + { "time", SOCK_STREAM, 0, 0, machtime_stream }, + { "time", SOCK_DGRAM, 0, 0, machtime_dg }, /* Return human-readable time */ - "daytime", SOCK_STREAM, 0, 0, daytime_stream, - "daytime", SOCK_DGRAM, 0, 0, daytime_dg, + { "daytime", SOCK_STREAM, 0, 0, daytime_stream }, + { "daytime", SOCK_DGRAM, 0, 0, daytime_dg }, /* Familiar character generator */ - "chargen", SOCK_STREAM, 1, 0, chargen_stream, - "chargen", SOCK_DGRAM, 0, 0, chargen_dg, - 0 + { "chargen", SOCK_STREAM, 1, 0, chargen_stream }, + { "chargen", SOCK_DGRAM, 0, 0, chargen_dg }, + { 0 } }; #define NUMINT (sizeof(intab) / sizeof(struct inent)) @@ -172,18 +219,17 @@ char *CONFIG = _PATH_INETDCONF; char **Argv; char *LastArg; -main(argc, argv, envp) - int argc; - char *argv[], *envp[]; +int +main(int argc, char **argv, char **envp) { - extern char *optarg; - extern int optind; - register struct servtab *sep; - register struct passwd *pwd; - register int tmpint; + struct servtab *sep; + struct passwd *pwd; + int tmpint; struct sigvec sv; int ch, pid, dofork; char buf[50]; + struct sockaddr_in peer; + int i; Argv = argv; if (envp == 0 || *envp == 0) @@ -192,12 +238,15 @@ main(argc, argv, envp) envp++; LastArg = envp[-1] + strlen(envp[-1]); - while ((ch = getopt(argc, argv, "d")) != EOF) + while ((ch = getopt(argc, argv, "dl")) != EOF) switch(ch) { case 'd': debug = 1; options |= SO_DEBUG; break; + case 'l': + log = 1; + break; case '?': default: fprintf(stderr, "usage: inetd [-d]"); @@ -267,6 +316,19 @@ main(argc, argv, envp) sep->se_service); continue; } + if(log) { + i = sizeof peer; + if(getpeername(ctrl,(struct sockaddr *)&peer,&i)) { + syslog(LOG_WARNING, + "getpeername(for %s): %m", + sep->se_service); + continue; + } + syslog(LOG_INFO,"%s from %s", + sep->se_service, + inet_ntoa(peer.sin_addr)); + + } } else ctrl = sep->se_fd; (void) sigblock(SIGBLOCK); @@ -365,7 +427,7 @@ reapchild() { int status; int pid; - register struct servtab *sep; + struct servtab *sep; for (;;) { pid = wait3(&status, WNOHANG, (struct rusage *)0); @@ -392,8 +454,7 @@ reapchild() void config() { - register struct servtab *sep, *cp, **sepp; - struct servtab *getconfigent(), *enter(); + struct servtab *sep, *cp, **sepp; long omask; if (!setconfig()) { @@ -402,7 +463,7 @@ config() } for (sep = servtab; sep; sep = sep->se_next) sep->se_checked = 0; - while (cp = getconfigent()) { + while ((cp = getconfigent())) { for (sep = servtab; sep; sep = sep->se_next) if (strcmp(sep->se_service, cp->se_service) == 0 && strcmp(sep->se_proto, cp->se_proto) == 0) @@ -437,21 +498,43 @@ config() print_service("ADD ", sep); } sep->se_checked = 1; - sp = getservbyname(sep->se_service, sep->se_proto); - if (sp == 0) { - syslog(LOG_ERR, "%s/%s: unknown service", - sep->se_service, sep->se_proto); - if (sep->se_fd != -1) - (void) close(sep->se_fd); - sep->se_fd = -1; - continue; - } - if (sp->s_port != sep->se_ctrladdr.sin_port) { - sep->se_ctrladdr.sin_port = sp->s_port; - if (sep->se_fd != -1) - (void) close(sep->se_fd); - sep->se_fd = -1; + if (!sep->se_rpc) { + sp = getservbyname(sep->se_service, sep->se_proto); + if (sp == 0) { + syslog(LOG_ERR, "%s/%s: unknown service", + sep->se_service, sep->se_proto); + if (sep->se_fd != -1) + (void) close(sep->se_fd); + sep->se_fd = -1; + continue; + } + if (sp->s_port != sep->se_ctrladdr.sin_port) { + sep->se_ctrladdr.sin_port = sp->s_port; + if (sep->se_fd != -1) + (void) close(sep->se_fd); + sep->se_fd = -1; + } } + else { + rpc = getrpcbyname(sep->se_service); + if (rpc == 0) { + syslog(LOG_ERR, "%s/%s unknown RPC service.", + sep->se_service, sep->se_proto); + if (sep->se_fd != -1) + (void) close(sep->se_fd); + sep->se_fd = -1; + continue; + } + if (rpc->r_number != sep->se_rpc_prog) { + if (sep->se_rpc_prog) + unregisterrpc(sep); + sep->se_rpc_prog = rpc->r_number; + if (sep->se_fd != -1) + (void) close(sep->se_fd); + sep->se_fd = -1; + } + } + if (sep->se_fd == -1) setup(sep); } @@ -461,7 +544,7 @@ config() */ omask = sigblock(SIGBLOCK); sepp = &servtab; - while (sep = *sepp) { + while ((sep = *sepp)) { if (sep->se_checked) { sepp = &sep->se_next; continue; @@ -474,16 +557,45 @@ config() } if (debug) print_service("FREE", sep); + if (sep->se_rpc && sep->se_rpc_prog > 0) + unregisterrpc(sep); freeconfig(sep); free((char *)sep); } (void) sigsetmask(omask); } +void +unregisterrpc(struct servtab *sep) +{ + int i; + struct servtab *sepp; + long omask; + + omask = sigblock(SIGBLOCK); + for (sepp = servtab; sepp; sepp = sepp->se_next) { + if (sepp == sep) + continue; + if (sep->se_checked == 0 || + !sepp->se_rpc || + sep->se_rpc_prog != sepp->se_rpc_prog) + continue; + return; + } + if (debug) + print_service("UNREG", sep); + for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) + pmap_unset(sep->se_rpc_prog, i); + if (sep->se_fd != -1) + (void) close(sep->se_fd); + sep->se_fd = -1; + (void) sigsetmask(omask); +} + void retry() { - register struct servtab *sep; + struct servtab *sep; timingout = 0; for (sep = servtab; sep; sep = sep->se_next) @@ -491,8 +603,8 @@ retry() setup(sep); } -setup(sep) - register struct servtab *sep; +void +setup(struct servtab *sep) { int on = 1; @@ -521,6 +633,27 @@ setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) } return; } + if (sep->se_rpc) { + int i, len; + if (getsockname(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr, + &len) < 0) { + syslog(LOG_ERR, "%s/%s: getsockname: %m", + sep->se_service, sep->se_proto); + (void) close(sep->se_fd); + sep->se_fd = -1; + return; + } + if (debug) + print_service("REG ", sep); + for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) { + pmap_unset(sep->se_rpc_prog, i); + pmap_set(sep->se_rpc_prog, i, + (sep->se_socktype == SOCK_DGRAM) + ? IPPROTO_UDP : IPPROTO_TCP, + ntohs(sep->se_ctrladdr.sin_port)); + } + + } if (sep->se_socktype == SOCK_STREAM) listen(sep->se_fd, 10); FD_SET(sep->se_fd, &allsock); @@ -530,10 +663,9 @@ setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) } struct servtab * -enter(cp) - struct servtab *cp; +enter(struct servtab *cp) { - register struct servtab *sep; + struct servtab *sep; long omask; sep = (struct servtab *)malloc(sizeof (*sep)); @@ -553,8 +685,8 @@ enter(cp) FILE *fconfig = NULL; struct servtab serv; char line[256]; -char *skip(), *nextline(); +int setconfig() { @@ -566,6 +698,7 @@ setconfig() return (fconfig != NULL); } +void endconfig() { if (fconfig) { @@ -577,16 +710,20 @@ endconfig() struct servtab * getconfigent() { - register struct servtab *sep = &serv; + struct servtab *sep = &serv; int argc; - char *cp, *arg, *newstr(); - + char *cp, *arg; + char *versp; + more: while ((cp = nextline(fconfig)) && *cp == '#') ; if (cp == NULL) return ((struct servtab *)0); - sep->se_service = newstr(skip(&cp)); + arg = skip(&cp); + if (arg == NULL) + goto more; + sep->se_service = newstr(arg); arg = skip(&cp); if (strcmp(arg, "stream") == 0) sep->se_socktype = SOCK_STREAM; @@ -601,12 +738,42 @@ more: else sep->se_socktype = -1; sep->se_proto = newstr(skip(&cp)); + sep->se_rpc = 0; + if (strncmp(sep->se_proto, "rpc/", 4) == 0) { + sep->se_proto += 4; + sep->se_rpc = 1; + sep->se_rpc_prog = sep->se_rpc_lowvers = sep->se_rpc_lowvers = 0; + sep->se_ctrladdr.sin_family = AF_INET; + sep->se_ctrladdr.sin_port = 0; + sep->se_ctrladdr.sin_addr.s_addr = htonl(INADDR_ANY); + if ((versp = rindex(sep->se_service, '/'))) { + *versp++ = '\0'; + switch (sscanf(versp, "%d-%d", + &sep->se_rpc_lowvers, + &sep->se_rpc_highvers)) { + case 2: + break; + case 1: + sep->se_rpc_highvers = + sep->se_rpc_lowvers; + break; + default: + syslog(LOG_ERR, "bad RPC version specifier; %s\n", sep->se_service); + freeconfig(sep); + goto more; + } + } + else { + sep->se_rpc_lowvers = + sep->se_rpc_highvers = 1; + } + } arg = skip(&cp); sep->se_wait = strcmp(arg, "wait") == 0; sep->se_user = newstr(skip(&cp)); sep->se_server = newstr(skip(&cp)); if (strcmp(sep->se_server, "internal") == 0) { - register struct biltin *bi; + struct biltin *bi; for (bi = biltins; bi->bi_service; bi++) if (bi->bi_socktype == sep->se_socktype && @@ -630,8 +797,8 @@ more: return (sep); } -freeconfig(cp) - register struct servtab *cp; +void +freeconfig(struct servtab *cp) { int i; @@ -649,10 +816,9 @@ freeconfig(cp) } char * -skip(cpp) - char **cpp; +skip(char **cpp) { - register char *cp = *cpp; + char *cp = *cpp; char *start; again: @@ -664,7 +830,7 @@ again: c = getc(fconfig); (void) ungetc(c, fconfig); if (c == ' ' || c == '\t') - if (cp = nextline(fconfig)) + if ((cp = nextline(fconfig))) goto again; *cpp = (char *)0; return ((char *)0); @@ -679,8 +845,7 @@ again: } char * -nextline(fd) - FILE *fd; +nextline(FILE *fd) { char *cp; @@ -693,21 +858,19 @@ nextline(fd) } char * -newstr(cp) - char *cp; +newstr(char *cp) { - if (cp = strdup(cp ? cp : "")) + if ((cp = strdup(cp ? cp : ""))) return(cp); syslog(LOG_ERR, "strdup: %m"); exit(-1); } -setproctitle(a, s) - char *a; - int s; +void +setproctitle(char *a, int s) { int size; - register char *cp; + char *cp; struct sockaddr_in sin; char buf[80]; @@ -729,24 +892,22 @@ setproctitle(a, s) #define BUFSIZE 8192 /* ARGSUSED */ -echo_stream(s, sep) /* Echo service -- echo data back */ - int s; - struct servtab *sep; +void +echo_stream(int s,struct servtab *sep) /* Echo service -- echo data back */ { char buffer[BUFSIZE]; int i; setproctitle(sep->se_service, s); while ((i = read(s, buffer, sizeof(buffer))) > 0 && - write(s, buffer, i) > 0) + write(s, buffer, i) == i) ; exit(0); } /* ARGSUSED */ -echo_dg(s, sep) /* Echo service -- echo data back */ - int s; - struct servtab *sep; +void +echo_dg(int s,struct servtab *sep) /* Echo service -- echo data back */ { char buffer[BUFSIZE]; int i, size; @@ -759,9 +920,8 @@ echo_dg(s, sep) /* Echo service -- echo data back */ } /* ARGSUSED */ -discard_stream(s, sep) /* Discard service -- ignore data */ - int s; - struct servtab *sep; +void +discard_stream(int s,struct servtab *sep) /* Discard service -- ignore data */ { int ret; char buffer[BUFSIZE]; @@ -777,9 +937,8 @@ discard_stream(s, sep) /* Discard service -- ignore data */ } /* ARGSUSED */ -discard_dg(s, sep) /* Discard service -- ignore data */ - int s; - struct servtab *sep; +void +discard_dg(int s,struct servtab *sep) /* Discard service -- ignore data */ { char buffer[BUFSIZE]; @@ -791,9 +950,10 @@ discard_dg(s, sep) /* Discard service -- ignore data */ char ring[128]; char *endring; +void initring() { - register int i; + int i; endring = ring; @@ -803,11 +963,10 @@ initring() } /* ARGSUSED */ -chargen_stream(s, sep) /* Character generator */ - int s; - struct servtab *sep; +void +chargen_stream(int s,struct servtab *sep) /* Character generator */ { - register char *rs; + char *rs; int len; char text[LINESIZ+2]; @@ -836,9 +995,8 @@ chargen_stream(s, sep) /* Character generator */ } /* ARGSUSED */ -chargen_dg(s, sep) /* Character generator */ - int s; - struct servtab *sep; +void +chargen_dg(int s,struct servtab *sep) /* Character generator */ { struct sockaddr sa; static char *rs; @@ -884,13 +1042,12 @@ machtime() fprintf(stderr, "Unable to get time of day\n"); return (0L); } - return (htonl((long)tv.tv_sec + 2208988800)); + return (htonl((long)tv.tv_sec + 2208988800U)); } /* ARGSUSED */ -machtime_stream(s, sep) - int s; - struct servtab *sep; +void +machtime_stream(int s,struct servtab *sep) { long result; @@ -899,9 +1056,8 @@ machtime_stream(s, sep) } /* ARGSUSED */ -machtime_dg(s, sep) - int s; - struct servtab *sep; +void +machtime_dg(int s,struct servtab *sep) { long result; struct sockaddr sa; @@ -915,13 +1071,11 @@ machtime_dg(s, sep) } /* ARGSUSED */ -daytime_stream(s, sep) /* Return human-readable time of day */ - int s; - struct servtab *sep; +void +daytime_stream(int s,struct servtab *sep) /* Return human-readable time of day */ { char buffer[256]; - time_t time(), clock; - char *ctime(); + time_t clock; clock = time((time_t *) 0); @@ -930,15 +1084,13 @@ daytime_stream(s, sep) /* Return human-readable time of day */ } /* ARGSUSED */ -daytime_dg(s, sep) /* Return human-readable time of day */ - int s; - struct servtab *sep; +void +daytime_dg(int s,struct servtab *sep) /* Return human-readable time of day */ { char buffer[256]; - time_t time(), clock; + time_t clock; struct sockaddr sa; int size; - char *ctime(); clock = time((time_t *) 0); @@ -953,12 +1105,18 @@ daytime_dg(s, sep) /* Return human-readable time of day */ * print_service: * Dump relevant information to stderr */ -print_service(action, sep) - char *action; - struct servtab *sep; +void +print_service(char *action, struct servtab *sep) { - fprintf(stderr, - "%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n", - action, sep->se_service, sep->se_proto, - sep->se_wait, sep->se_user, (int)sep->se_bi, sep->se_server); + if (sep->se_rpc) + fprintf(stderr, + "%s: %s rpcnum=%d, rpcvers=%d/%d, wait=%d, user=%s builtin=%x server=%s\n", + action, sep->se_service, + sep->se_rpc_prog, sep->se_rpc_lowvers, sep->se_rpc_highvers, + sep->se_wait, sep->se_user, (int)sep->se_bi, sep->se_server); + else + fprintf(stderr, + "%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n", + action, sep->se_service, sep->se_proto, + sep->se_wait, sep->se_user, (int)sep->se_bi, sep->se_server); } diff --git a/usr.sbin/inetd/pathnames.h b/usr.sbin/inetd/pathnames.h index 520f77a0aa..0ef89ff3dd 100644 --- a/usr.sbin/inetd/pathnames.h +++ b/usr.sbin/inetd/pathnames.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pathnames.h 5.3 (Berkeley) 6/1/90 + * from: @(#)pathnames.h 5.3 (Berkeley) 6/1/90 + * $Id: pathnames.h,v 1.2 1993/08/01 17:59:32 mycroft Exp $ */ #include <paths.h> diff --git a/usr.sbin/iostat/Makefile b/usr.sbin/iostat/Makefile index 2046f513ba..f96cad5e46 100644 --- a/usr.sbin/iostat/Makefile +++ b/usr.sbin/iostat/Makefile @@ -2,7 +2,7 @@ PROG= iostat CFLAGS+=-I/sys -I${.CURDIR}/../../usr.bin/vmstat -MAN8= iostat.0 +MAN8= iostat.8 DPADD= ${LIBUTIL} LDADD= -lutil BINGRP= kmem diff --git a/usr.sbin/iostat/iostat.8 b/usr.sbin/iostat/iostat.8 index 8e49fd5e8c..b9b804c71e 100644 --- a/usr.sbin/iostat/iostat.8 +++ b/usr.sbin/iostat/iostat.8 @@ -66,7 +66,7 @@ instead of the default ``/dev/kmem''. .TP \-N Extract the name list from the specified system instead of the default -``/vmunix''. +``/386bsd''. .TP \-w Pause @@ -118,7 +118,7 @@ id % of cpu time in idle mode .fi .SH FILES .ta \w'/dev/kmem 'u -/vmunix default kernel namelist +/386bsd default kernel namelist .br /dev/kmem default memory file .SH SEE ALSO diff --git a/usr.sbin/keymap/Makefile b/usr.sbin/keymap/Makefile deleted file mode 100644 index e95375059f..0000000000 --- a/usr.sbin/keymap/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# @(#)Makefile 5.13 (Berkeley) 5/11/90 - -SUBDIR= lib keymap - -.include <bsd.subdir.mk> - diff --git a/usr.sbin/keymap/keymap/Makefile b/usr.sbin/keymap/keymap/Makefile deleted file mode 100644 index e299a3ad63..0000000000 --- a/usr.sbin/keymap/keymap/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# @(#)Makefile 5.4 (Berkeley) 6/5/91 -# Made it also look in lib/obj for the library.. rgrimes 93/05/26 -# this is still not right but it gets me past build world for now... - -PROG= keymap -SRCS= keymap.c -BINDIR= /usr/bin - -DPADD= ${.CURDIR}/../lib/libconsutil.a -LDADD= -lconsutil -LDFLAGS+= -L${.CURDIR}/../lib -LDFLAGS+= -L${.CURDIR}/../lib/obj - -MAN5= keycap.0 -MAN8= keymap.0 - -.include <bsd.prog.mk> diff --git a/usr.sbin/keymap/keymap/README.keycap b/usr.sbin/keymap/keymap/README.keycap deleted file mode 100644 index 1c03243334..0000000000 --- a/usr.sbin/keymap/keymap/README.keycap +++ /dev/null @@ -1,214 +0,0 @@ -This file is a preliminary explanation of the keycap data base format. - -Read termcap(5) first to get a feeling what you have to do. - -There are numeric entries, boolean entries and string entries. - -Boolean entries: - - de Sets the default mapping built into the kernel - t0 Clears the CTRL map - l0 Clears the ALTGR map - D1..D127 Disables the given key (1..127) entirely - -Numeric Entries: -These are entered as 'xx#num', e.g. l1#60 sets an ALT function to key 60. - - m1..m9 Key numbers of the META Keys (you may have up to 9 keys - with this function, which is overdimensioned) - l1..l9 Key numbers of the ALTGR keys (up to 9) - h1..h9 Key numbers of the SHIFT keys (up to 9) - t1..t9 Key numbers of the CTRL keys (up to 9) - ca Key number of the CAPS LOCK key - sh Key number of the SHIFT LOCK key - nl Key number of the NUM LOCK key - sc Key number of the SCROLL LOCK key - ag Key number of the ALTGR LOCK key - p1-p9 Key number of a diacritical prefix (see below) - la Behaviour of the CAPS LED: - 0: Standard: CAPS LED follows CAPS LOCK - 1: CAPS LED follows SHIFT LOCK - 2: CAPS LED follows ALTGR LOCK - ck Key number of the COMPOSE key (not yet) - -Please note that the default mapping already has all these keys (except -ALTGR, ALTGR LOCK and SHIFT LOCK) in the proper locations. - -String entries: -These are associated with a key number. They consist of a -letter and upto three digits, describing the key number, followed by an -equal symbol (=) and a string of up to 15(16) characters. The string is -delimited by a colon (:), which begins the next entry. -If the first character is a tilde (~), this key is influenced by -one of the CAPS LOCK keys. This is default for the letters A-Z, but -can be extended to national letter keys. -This means that the colon and the tilde, if in the first position cannot be -entered directly. Instead, there is an octal escape, like in the C language. -The escape starts with the character backslash \ and is followed by exactly -three octal characters. The usual symbols \\, \n, \r, \t, \f are also -recognized. - -The following key declarators exist: - -K1..K127 strings to be emitted without any modifier key (first key layer) -S1..S127 strings to be emitted with SHIFT (first key layer) -C1..C127 strings to be emitted with CTRL -M1..M127 strings to be emitted with META - -A1..A127 strings to be emitted with ALTGR (second key layer) -X1..X127 strings to be emitted with ALTGRLOCK (second key layer) - -Other string entries: - fn0=path Pathname of an ISO-LATIN1 font - fn1=path Pathname of an additional font - - -Diacritical prefixes: -Normally, keys like ` ' ^ ~ have their regular meaning and return the -corresponding ASCII code. If the corresponding key gets the "diacritical -prefix attribute", it behaves different: Returning the ASCII code is retained -until a second character. If this is a character, which may have an -additional diacritical mark, then the corresponding diacritical character -is returned. If the character is a blank, the original ASCII code of the -prefix is returned. Otherwise the prefix is absorbed. Example: -To get the A with an accent grave, press first ` , then A. - -Compose key: - not yet, but RSN. - - - -See /usr/share/misc/keycap for examples. - - -The default mapping is the following: - -keynum key type unshift shift ctrl -/* 1*/ KBD_ASCII, "`", "~", "`", -/* 2*/ KBD_ASCII, "1", "!", "!", -/* 3*/ KBD_ASCII, "2", "@", "\000", -/* 4*/ KBD_ASCII, "3", "#", "#", -/* 5*/ KBD_ASCII, "4", "$", "$", -/* 6*/ KBD_ASCII, "5", "%", "%", -/* 7*/ KBD_ASCII, "6", "^", "\036", -/* 8*/ KBD_ASCII, "7", "&", "&", -/* 9*/ KBD_ASCII, "8", "*", "\010", -/* 10*/ KBD_ASCII, "9", "(", "(", -/* 11*/ KBD_ASCII, "0", ")", ")", -/* 12*/ KBD_ASCII, "-", "_", "\037", -/* 13*/ KBD_ASCII, "=", "+", "+", -/* 14*/ KBD_NONE, "", "", "", -/* 15*/ KBD_ASCII, "\177", "\177", "\010", -/* 16*/ KBD_ASCII, "\t", "\177\t","\t", -/* 17*/ KBD_ASCII, "q", "Q", "\021", -/* 18*/ KBD_ASCII, "w", "W", "\027", -/* 19*/ KBD_ASCII, "e", "E", "\005", -/* 20*/ KBD_ASCII, "r", "R", "\022", -/* 21*/ KBD_ASCII, "t", "T", "\024", -/* 22*/ KBD_ASCII, "y", "Y", "\031", -/* 23*/ KBD_ASCII, "u", "U", "\025", -/* 24*/ KBD_ASCII, "i", "I", "\011", -/* 25*/ KBD_ASCII, "o", "O", "\017", -/* 26*/ KBD_ASCII, "p", "P", "\020", -/* 27*/ KBD_ASCII, "[", "{", "\033", -/* 28*/ KBD_ASCII, "]", "}", "\035", -/* 29*/ KBD_ASCII, "\\", "|", "\034", -/* 30*/ KBD_CAPS, "", "", "", -/* 31*/ KBD_ASCII, "a", "A", "\001", -/* 32*/ KBD_ASCII, "s", "S", "\023", -/* 33*/ KBD_ASCII, "d", "D", "\004", -/* 34*/ KBD_ASCII, "f", "F", "\006", -/* 35*/ KBD_ASCII, "g", "G", "\007", -/* 36*/ KBD_ASCII, "h", "H", "\010", -/* 37*/ KBD_ASCII, "j", "J", "\n", -/* 38*/ KBD_ASCII, "k", "K", "\013", -/* 39*/ KBD_ASCII, "l", "L", "\014", -/* 40*/ KBD_ASCII, ";", ":", ";", -/* 41*/ KBD_ASCII, "'", "\"", "'", -/* 42*/ KBD_ASCII, "\\", "|", "\034", /* special */ -/* 43*/ KBD_ASCII, "\r", "\r", "\n", /* RETURN */ -/* 44*/ KBD_SHIFT, "", "", "", /* SHIFT left */ -/* 45*/ KBD_NONE, "", "", "", -/* 46*/ KBD_ASCII, "z", "Z", "\032", -/* 47*/ KBD_ASCII, "x", "X", "\030", -/* 48*/ KBD_ASCII, "c", "C", "\003", -/* 49*/ KBD_ASCII, "v", "V", "\026", -/* 50*/ KBD_ASCII, "b", "B", "\002", -/* 51*/ KBD_ASCII, "n", "N", "\016", -/* 52*/ KBD_ASCII, "m", "M", "\r", -/* 53*/ KBD_ASCII, ",", "<", "<", -/* 54*/ KBD_ASCII, ".", ">", ">", -/* 55*/ KBD_ASCII, "/", "?", "\177", -/* 56*/ KBD_NONE, "", "", "", -/* 57*/ KBD_SHIFT, "", "", "", /* SHIFT right */ -/* 58*/ KBD_CTL, "", "", "", /* CTL left */ -/* 59*/ KBD_NONE, "", "", "", -/* 60*/ KBD_META, "", "", "", /* ALT left */ -/* 61*/ KBD_ASCII, " ", " ", " ", /* SPACE */ -/* 62*/ KBD_META, "", "", "", /* ALT right */ -/* 63*/ KBD_NONE, "", "", "", -/* 64*/ KBD_CTL, "", "", "", /* CTL right */ -/* 65*/ KBD_NONE, "", "", "", -/* 66*/ KBD_NONE, "", "", "", -/* 67*/ KBD_NONE, "", "", "", -/* 68*/ KBD_NONE, "", "", "", -/* 69*/ KBD_NONE, "", "", "", -/* 70*/ KBD_NONE, "", "", "", -/* 71*/ KBD_NONE, "", "", "", -/* 72*/ KBD_NONE, "", "", "", -/* 73*/ KBD_NONE, "", "", "", -/* 74*/ KBD_NONE, "", "", "", -/* 75*/ KBD_FUNC, "\033[L", "\033|a", "\033|b", /* INS */ -/* 76*/ KBD_FUNC, "\177", "\033|c", "\033|d", /* DEL */ -/* 77*/ KBD_NONE, "", "", "", -/* 78*/ KBD_NONE, "", "", "", -/* 79*/ KBD_FUNC, "\033[D", "\033|e", "\033|f", /* CU <- */ -/* 80*/ KBD_FUNC, "\033[H", "\033|g", "\033|h", /* HOME */ -/* 81*/ KBD_FUNC, "\033[F", "\033|i", "\033|j", /* END */ -/* 82*/ KBD_NONE, "", "", "", -/* 83*/ KBD_FUNC, "\033[A", "\033|k", "\033|l", /* CU ^ */ -/* 84*/ KBD_FUNC, "\033[B", "\033|m", "\033|n", /* CU v */ -/* 85*/ KBD_FUNC, "\033[I", "\033|o", "\033|p", /* PG UP */ -/* 86*/ KBD_FUNC, "\033[G", "\033|q", "\033|r", /* PG DN */ -/* 87*/ KBD_NONE, "", "", "", -/* 88*/ KBD_NONE, "", "", "", -/* 89*/ KBD_FUNC, "\033[C", "\033|s", "\033|t", /* CU -> */ -/* 90*/ KBD_NUM, "", "", "", -/* 91*/ KBD_KP, "7", "\033[H", "7", -/* 92*/ KBD_KP, "4", "\033[D", "4", -/* 93*/ KBD_KP, "1", "\033[F", "1", -/* 94*/ KBD_NONE, "", "", "", -/* 95*/ KBD_KP, "/", "/", "/", -/* 96*/ KBD_KP, "8", "\033[A", "8", -/* 97*/ KBD_KP, "5", "\033[E", "5", -/* 98*/ KBD_KP, "2", "\033[B", "2", -/* 99*/ KBD_KP, "0", "\033[L", "0", -/*100*/ KBD_KP, "*", "*", "*", -/*101*/ KBD_KP, "9", "\033[I", "9", -/*102*/ KBD_KP, "6", "\033[C", "6", -/*103*/ KBD_KP, "3", "\033[G", "3", -/*104*/ KBD_KP, ".", "\177", ".", -/*105*/ KBD_KP, "-", "-", "-", -/*106*/ KBD_KP, "+", "+", "+", -/*107*/ KBD_NONE, "", "", "", -/*108*/ KBD_ASCII, "\r", "\r", "\n", /* RETURN */ -/*109*/ KBD_NONE, "", "", "", -/*110*/ KBD_ASCII, "\033", "\033", "\033", -/*111*/ KBD_NONE, "", "", "", -/*112*/ KBD_FUNC, "\033[M", "\033[Y", "\033[k", /* F1 */ -/*113*/ KBD_FUNC, "\033[N", "\033[Z", "\033[l", /* F2 */ -/*114*/ KBD_FUNC, "\033[O", "\033[a", "\033[m", /* F3 */ -/*115*/ KBD_FUNC, "\033[P", "\033[b", "\033[n", /* F4 */ -/*116*/ KBD_FUNC, "\033[Q", "\033[c", "\033[o", /* F5 */ -/*117*/ KBD_FUNC, "\033[R", "\033[d", "\033[p", /* F6 */ -/*118*/ KBD_FUNC, "\033[S", "\033[e", "\033[q", /* F7 */ -/*119*/ KBD_FUNC, "\033[T", "\033[f", "\033[r", /* F8 */ -/*120*/ KBD_FUNC, "\033[U", "\033[g", "\033[s", /* F9 */ -/*121*/ KBD_FUNC, "\033[V", "\033[h", "\033[t", /* F10 */ -/*122*/ KBD_FUNC, "\033[W", "\033[i", "\033[u", /* F11 */ -/*123*/ KBD_FUNC, "\033[X", "\033[j", "\033[v", /* F12 */ -/*124*/ KBD_KP, "\033[w", "\033[x", "\033[y", -/*125*/ KBD_SCROLL, "", "", "", -/*126*/ KBD_BREAK, "", "", "", -/*127*/ KBD_NONE, "", "", "", - diff --git a/usr.sbin/keymap/keymap/keycap.5 b/usr.sbin/keymap/keymap/keycap.5 deleted file mode 100644 index 44edfe6823..0000000000 --- a/usr.sbin/keymap/keymap/keycap.5 +++ /dev/null @@ -1,208 +0,0 @@ -.\" Copyright (c) 1983, 1991 The Regents of the University of California. -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)keycap.5 1.1 (Contributed to 386bsd) 4/27/93 -.\" -.Dd April 27, 1993 -.Dt KEYCAP 5 -.Os 386bsd -.Sh NAME -.Nm keycap -.Nd keyboard configuration data base -.Sh SYNOPSIS -.Nm keycap -.Sh DESCRIPTION -The -.Nm keycap -file describes national keyboard mappings for the console driver -.Xr co 4 . -This file is accessed by the -.Xr keymap 8 -utility. -Each entry in the data base -is used to describe one class of keyboards. -.Pp -There is a default keyboard class, -.Em default , -which corresponds to the standard builtin mapping of the -.Xr co 4 -driver. -.Sh CAPABILITIES -Refer to -.Xr termcap 5 -for a description of the file layout. -The -.Em default -column below lists defaults obtained if there is -no entry in the table obtained, nor one in the special -.Em default -table. -.Bl -column Symbol D0..D127 -column Type false -.It Symbol Type Default Description -.It "ag num 0 key number of the ALTGRLOCK key -.It "A1..A127 str 0 map a string to the ALTGR key layer -.It "C1..C127 str none map a string to the CTRL key layer -.It "ca num 0 key number of the CAPS LOCK key -.It "de bool false sets the default mapping (top precedence) -.It "D0..D127 bool false disables the given key (1..127) entirely -.It "fn0 str none pathname of first loadable font -.It "fn1 str none pathname of second loadable font -.It "fe0 num none font encoding first font -.It "fe1 num none font encoding second font -.It "h1..h9 num 0 key numbers of the SHIFT keys (up to 9) -.It "K1..K127 str none map a string to the UNSHIFTED key -.It "l0 bool false clears the ALTGR map -.It "l1..l9 num 0 key numbers of the ALTGR keys (up to 9) -.It "la num 0 defines the operation of the CAPSLOCK LED -.It "m1..m9 num 0 key numbers of the META Keys (up to 9) -.It "M1..M127 str 0 map a string to the META key layer -.It "nl num 0 key number of the NUM LOCK key -.It "p1..p9 num 0 key number of a diacritical prefix -.It "S1..S127 str none map a string to the SHIFTED key layer -.It "sc num 0 key number of the SCROLL LOCK key -.It "sh num 0 key number of the SHIFT LOCK key -.It "t0 bool false clears the CTRL map -.It "t1..t9 num 0 key numbers of the CTRL keys (up to 9) -.It "tc str none allows extension of capabilities -.It "X1..X127 str none map a string to the SHIFTED-ALTGR key layer -.El -.Pp -The -.Em tc=capability -entry allows the extension of a keyboard entry. If, for instance, -the keyboard type -.Em foo , -differs from the similar type -.Em bar -in that the first has the capslock function on key 55, you might write -an entry for -.Em foo -in the following way: -.Pp -.Bd -literal -foo|the foo keyboard:\e - ca#55:tc=bar: -.Ed -.Sh String Entries for the keys (K*,S*,C*,A*): -These allow mappings of strings to keys. Examples: -.Pp -.Bl -tag -width K123=abcdef -compact -.It Em K123=abcdef -maps the string "abcdef" to key 123 -.It Em K123=^D -maps CTRL-D to key 123 -.It Em K123=\e072 -maps the colon \: to key 123 (only way to do it) -.It Em K123=\e176 -maps the tilde \~ to key 123 (only way to do it) -.It Em K123=\e077 -maps the question mark ? to key 123 (only way to do it) -.El -.Pp -The maximum allowed length of string is 15 bytes. -.Pp -.Sh Special alphabetic characters (new since 0.1.2): -The CAPSLOCK key if present on your keyboard influences alphabetic -keys only. This is obvious for the Latin alphabet only. If you use -another than the latin alphabet, the alphabetic characters must -be identified otherwise. If a key string starts with a tilde \~ -the following code inherits the ALPHA attribute, i.e. it is -influenced by the CAPSLOCK key. -.Pp -.Sh Locking keys (new since 0.1.2): -The ALTGR key layer (A,X) has its own LOCK key which can be -assigned with the -.Em ag -capability. To have an indicator which -layer is currently active, the CAPSLOCK LED may be redefined -to follow the ALTGRLOCK key -.Em (la#2) -rather than the CAPSLOCK key -.Em (la#0) -or SHIFTLOCK key -.Em (la#1). -.Pp -.Sh Special hotkeys (new since 0.1.2): -The -.Xr co 4 -driver has support for some hotkeys that are processed -.Em before -the normal character interpretation. Hotkey functions can be assigned to -any key like any standard string. To encode them, a two character -sequence starting with a question mark ? is used. Any of the six layers -K,S,A,C,X,M can be used, with the M (META) layer denoting SYSKEY, -and the X layer denoting CTRL-ALT. SYSKEY is a prefix key. -Note that a hotkey mapped to some key layer will suppress the -underlying standard function. The following table shows the hotkeys, -their encoding, their default mapping and their meaning: - -.Bl -column Name VTY-PREV -column Encoding ?X -column Default CTRL-ALT-DEL -.It "Name Encoding Default Description -.It "--- ?- none deletes a default hotkey -.It "RESET ?R CTRL-ALT-DEL do a CPU reset -.It "DEBUG ?D CTRL-ALT-ESC call the kernel debugger (if conf'd) -.It "VTY-PREV ?P none switch to previous vty in sequence -.It "VTY-NEXT ?N none switch to previous vty in sequence -.It "VTY-0 ?0 CTRL-ALT-F1 switch to vty 0 -.It "... .. ... ... -.It "VTY-9 ?9 CTRL-ALT-F10 switch to vty 9 (if conf'd) -.It "VTY-10 ?A CTRL-ALT-F11 switch to vty 10 (if conf'd) -.It "VTY-11 ?B CTRL-ALT-F12 switch to vty 11 (if conf'd) -.El -.Pp -.Sh Diacritical Prefixes: -Normally, keys like ` ' ^ ~ have their regular meaning and return the -corresponding ASCII code. If the corresponding key gets the -.Em diacritical prefix attribute , -it behaves different: Returning the ASCII code is retained -until a second character. If this is a character, which may have an -additional diacritical mark, then the corresponding diacritical character -is returned. If the character is a blank, the original ASCII code of the -prefix is returned. Otherwise the prefix is absorbed. Example: -To get the A with an accent grave, press first ` , then A. -In the data base, these diacritical keys are entered by their corresponding -octal number. -.Pp -.Sh SEE ALSO -.Xr co 4 , -.Xr keymap 8. -.Sh BUGS -In general, the database is downward compatible to keycap 0.1.1 and -pccons clones, but there is no guarantee that it works as expected. -Upgrade to the new -.Xr co 4 -driver (release 0.1.2 or later) to get full support for all the features. -Furthermore, there is no good way to express ISO-LATIN1 or other -characters >127 other than by their octal representation. -.Sh HISTORY -The -.Nm keycap -file format is a new feature of 386bsd 0.1 and later. diff --git a/usr.sbin/keymap/keymap/keymap.8 b/usr.sbin/keymap/keymap/keymap.8 deleted file mode 100644 index 1410886cb2..0000000000 --- a/usr.sbin/keymap/keymap/keymap.8 +++ /dev/null @@ -1,120 +0,0 @@ -.\" Copyright (c) 1983, 1990 The Regents of the University of California. -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)keymap.8 6.8 (Contributed to 386bsd) 8/01/92 -.\" -.Dd August 1, 1992 -.Dt KEYMAP 8 -.Os BSD 4.2 -.Sh NAME -.Nm keymap -.Nd list and change keyboard settings -.Sh SYNOPSIS -.Nm keymap -.Op Fl l -.Op Fl t Ns Ar +/- -.Op Fl d Ar delay -.Op Fl r Ar rate -.Op Fl m Ar keyboardmap -.Op Fl s Ar screensave -.Nm keymap -.Op Fl T -.Nm keymap -.Op Fl a Ar keystr Ar keycode -.Sh DESCRIPTION -.Nm keymap -allows listing of current keyboard map ( -.Xr kbd 4 , -) loading of a new keyboard map, and enabling, disabling and modification -of actual key repetition rate and delay. -.Pp -The following options are available: -.Bl -tag -width flag -.It Fl l -Displays the current keymap. Notice that when -.Ar keyboardmap -is given, -.Fl l -returns the setting before the new map is set. -.It Fl t Ns Ar +/- -This enables ( -.Ar + -) or disables ( -.Ar - -) the repetition of keys (typematic). Key repetition is a feature -built into the keyboard itself. After a specified -.Fl delay -the last key that is hold down is repeatedly sent with a specified -.Fl rate. -.It Fl d Ar delay -Specifies the delay after which the last key will be repeated. Valid -values are 0..3, corresponding to delays of 250,500,750,1000 ms. -.It Fl r Ar rate -Specifies the repetition rate. Valid argument values are 0..31, with -0 being the fastest (~30 keys/sec) and 31 being the slowest (~2 keys/sec). -The formula to translate this value into a rate in nonlinear. -.It Fl m Ar keyboardmap -When this optional argument is given, the keyboard capability database -.Xr keycap 5 -is searched for the entry -.Ar keyboardmap -and the corresponding entry is loaded. -.It Fl s Ar screensave -Specifies the timeout value for the screensaver function. The time has to -be given in seconds, e.g. 600 describes a delay of 10 minutes. The -value 0 disables the screen saver entirely. The default value the system -is started with is 10 minutes. Screen saving is suspended when the -XServer (see -.Xr X386 -) is active. -.Sh FILES -.Bl -tag -width /usr/share/misc/keycap -compact -.It Pa /usr/share/misc/keycap -Keyboard capabilities data base. -.It Pa /dev/kbd -Keyboard raw device. -.El -.Sh SEE ALSO -.Xr kbd 4 , -.Xr keycap 5 -.Sh BUGS -.Nm Keymap -detects several inconsistencies in the -.Xr keycap 5 -database. Processing is stopped in case of error. The keyboard remain in -an undefined state. To restore a defined setting, execute -.Bd -literal -offset indent -keymap -m default -.Ed -.Pp -.Sh HISTORY -.Nm Keymap -was developed from scratch for 386bsd. diff --git a/usr.sbin/keymap/keymap/keymap.c b/usr.sbin/keymap/keymap/keymap.c deleted file mode 100644 index 15e00dd571..0000000000 --- a/usr.sbin/keymap/keymap/keymap.c +++ /dev/null @@ -1,929 +0,0 @@ -/* - * Keymap utility, - * - loads a specific national keyboard mapping into the - * keyboard driver - * - displays the current mapping - * - sets/displays key repetition rate - * - enables/disables screensaver - * - * Contributed to 386bsd 0.1 and later versions - * - * Copyright 1992,1993 by Holger Veit - * May be freely used with Bill Jolitz's port of - * 386bsd and may be included in a 386bsd collection - * as long as binary and source are available and reproduce the above - * copyright. - * - * You may freely modify this code and contribute improvements based - * on this code as long as you don't claim to be the original author. - * Commercial use of this source requires permittance of the copyright - * holder. A general license for 386bsd will override this restriction. - * - * Use at your own risk. The copyright holder or any person who makes - * this code available for the public (administrators of public archives - * for instance) are not responsible for any harm to hardware or software - * that might happen due to wrong application or program faults. - * - * You must have the codriver driver in the same package generated - * into the 386bsd kernel, otherwise this program does not work. - * - * @(#)keymap.c 1.1 (386bsd contribution) 24-oct-92 - */ -/* Oh, this has become a hack, this could have been done better. hv */ - -/* - * -hv- Holger Veit - *-vak- Serge Vakulenko - * - * 29/07/92 -hv- First version - * 24/10/92 -hv- fixes + screensaver - * 01/12/92-vak- -T and -a flags, new output format, - * new keycap extensions for Meta and ShiftAltgr keys. - * 04/27/93 -hv- extensions for special function keys - * - */ - - /* Usage: - * - * keymap [-l] [-t+|-t-] [-d#] [-r#] [-m mapping] - * or - * keymap -T - * or - * keymap -a "keydef" keyid - * - * -l list the current key mapping - * -t+ enable key repetition (typematic) - * -t- disable key repetion - * -d# set delay after which a key is repeated (includes -t+) - * -r# set rate of repetition of keys (includes -t+) - * -s# set screensaver timeout (in seconds, 0 to disable) - * -m mapping load a key map from the keycap file - * -T key testing - * - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/ioctl_pc.h> -#include "pathnames.h" - -extern int kgetent(char*, char*); -extern int kgetnum(char*); -extern int kgetflag(char*); -extern char *kgetstr(char*,char**); - -extern char *optarg; -extern int optind; -extern int opterr; - -int kbd = -1; -char *progname; -struct kbd_ovlkey kmap[128]; - -main(int argc,char *argv[]) -{ - - int lf = 0, - Tf = 0, - tf = 0, - af = 0, - df = 0, delay = -1, - rf = 0, rate = -1, - sf = 0, stime = 0, - mf = 0; - char *map; - int c; - - progname = argv[0]; - - while ((c=getopt(argc,argv,"lTat:d:r:m:s:"))!=EOF) { - - switch(c) { - case 'l': - lf++; - break; - case 'T': - Tf++; - break; - case 'a': - af++; - break; - case 't': - tf = *optarg; - break; - case 'd': - df++; - tf='+'; - delay = atoi(optarg); - break; - case 'r': - rf++; - tf='+'; - rate = atoi(optarg); - break; - case 'm': - mf++; - map = argv[optind-1]; - break; - case 's': - sf++; - stime = atoi(optarg); - break; - case '?': - default: - usage(); - } - } - - if ((lf+tf+df+rf+mf+sf+Tf+af)==0) - usage(); - if (Tf && (lf+tf+df+rf+mf+sf+af)!=0) - usage(); - if (af) { - if ((lf+tf+df+rf+mf+sf+Tf)!=0) - usage(); - if (optind >= argc - 1) - usage(); - } - - /* we use /dev/kbd here, because this will block if the keymap - * command is tried from an xterm. Use xmodmap/xset for these - * functions - */ - if ((kbd=open(_PATH_KEYBOARD, 0)) < 0) - fatal("Open keyboard"); - - if (Tf) keynum(); - else if (af) assign (argv[optind], argv[optind+1]); - else { - if (lf) loadkmap(); - if (tf || df || rf) tpm(tf,delay,rate); - if (mf) mapkey(map); - if (sf) saver(stime); - } - - close(kbd); - - if (lf) list(); - - exit(0); -} - -fatal(char *msg) -{ - fprintf(stderr,"%s: ",progname); - perror(msg); - exit(1); -} - -error(char *msg) -{ - fprintf(stderr,"%s: %s\n",progname,msg); - exit(1); -} - -usage() -{ - fprintf (stderr, "Usage:\n"); - fprintf (stderr, "\t%s [-l] [-t+|-t-] [-d#] [-r#] [-s#] [-m mapping]\n", - progname); - fprintf (stderr, "\t -l list the current keyboard map\n"); - fprintf (stderr, "\t -t[+-] enable/disable key repetition\n"); - fprintf (stderr, "\t -d# set key repetition delay, 0..3 * 250ms\n"); - fprintf (stderr, "\t -r# set key repetition rate, 0..31, 0 fastest\n"); - fprintf (stderr, "\t -s# set screen saver delay, in seconds\n"); - fprintf (stderr, "\t -m ent remap keyboard according to the keycap(5) entry\n"); - fprintf (stderr, "or\n\t%s -T\n", progname); - fprintf (stderr, "\t test key codes\n"); - fprintf (stderr, "or\n\t%s -a \"keydef\" keyid\n", progname); - fprintf (stderr, "\t remap the single keyid, F1..F12, KP0..KP9, KP+..KP/, or 0xNUM\n"); - fprintf (stderr, "\t with zero or one prefix: SHIFT-, CTRL-, ALTGR-, META\n"); - exit(1); -} - -/* for regular keys */ -static char *type2str[] = { -/*0*/ "none ", -/*1*/ "shift ", -/*2*/ "meta ", -/*3*/ "numlock ", -/*4*/ "ctrl ", -/*5*/ "capslock", -/*6*/ "ascii ", -/*7*/ "scroll ", -/*8*/ "function", -/*9*/ "keypad ", -/*10*/ "break ", -/*11*/ "altgr ", -/*12*/ "shftlock", -/*13*/ "altgrlck", -}; - -/* convert to escseq */ -char *trl(XCHAR *s) -{ -#if XCHAR != u_char -ERROR! FIX ME! -#endif - static char s1[4*16+1]; - static char s2[5]; - int i; - - s1[0] = 0; - for (i=0; s[i]; i++) { - if (s[i]>' ' && s[i]<0177) { - s2[0] = s[i]; - s2[1] = 0; - } else if ((s[i] & 0xff) < ' ') - sprintf(s2, "^%c", s[i]+'@'); - else - sprintf(s2, "\\%03o", s[i] & 0xff); - strcat(s1,s2); - } - return s1; -} - -loadkmap() -{ - int i; - struct kbd_ovlkey *ke; - - /* read the keys */ - for (i=0, ke=kmap; i<128; ke++,i++) { - ke->keynum = i; - if (ioctl(kbd,KBDGCKEY,ke)<0) - fatal("Get kbd code"); - } -} - -list() -{ - int i; - struct kbd_ovlkey *ke; - int has_altgr= 0, - has_shftlock = 0; - u_short kt; -#if XCHAR != u_char -ERROR! FIX ME! -#endif - char *p; - - /* scan the saved keys */ - for (i=0, ke=kmap; i<128; ke++,i++) { - switch (ke->type & KBD_MASK) { - case KBD_SHFTLOCK: - has_shftlock = i; - break; - case KBD_ALTGR: - case KBD_ALTGRLOCK: - has_altgr = i; - break; - } - } - - /* get the entry of key 0. This is a nonexisting key for some - * reasons, so we can use it to hold the current keyboard name - */ - if (kmap[0].unshift[0]) - printf("Current mapping is: %s\n",kmap[0].unshift); - - printf(" # type unshift shifted ctrl meta"); - if (has_altgr) - printf(" altgr shiftaltgr"); - printf("\n"); - - for (i=1, ke=&kmap[1]; i<128; ke++,i++) { - kt = ke->type; - if (kt) { - printf("%3d%c %8s", i, (kt & KBD_OVERLOAD) ? '*' : ' ', - type2str[kt & KBD_MASK]); - printf("%c%-10s", (kt & KBD_DOCAPS) ? '~' : ' ', - trl (ke->unshift)); - printf(" %-10s", trl (ke->shift)); - if (ke->ctrl [0]) - p = trl (ke->ctrl); - else if ((kt&KBD_MASK) == KBD_ASCII || - (kt&KBD_MASK) == KBD_FUNC) - p = "^@"; - else - p = ""; - printf(" %-10s", p); - if (ke->meta [0]) - p = (char*)trl (ke->meta); - else if ((kt&KBD_MASK) == KBD_ASCII || - (kt&KBD_MASK) == KBD_FUNC) { - XCHAR buf [KBDMAXOVLKEYSIZE+1]; - XC_strcpy (buf, ke->unshift); - buf[0] ^= 0x80; - p = (char*)trl (buf); - } else - p = ""; - if (has_altgr && ke->altgr [0]) { - printf(" %-10s", p); - printf("%c%-10s", (kt & KBD_DOALTCAPS) ? '~' : ' ', - (char*)trl (ke->altgr)); - printf(" %s", trl (ke->shiftaltgr)); - } else - printf(" %s", p); - putchar('\n'); - } - } -} - -tpm(int onoff, int delay, int rate) -{ - int current_tpm; - int new_tpm; - int tpm_sw; - - if (onoff == '-') { - tpm_sw = KBD_REPEATOFF; - if (ioctl(kbd,KBDSREPSW,&tpm_sw)<0) - fatal("Set key repetition OFF"); - return; - } - else if (onoff=='+') { - tpm_sw = KBD_REPEATON; - if (ioctl(kbd,KBDSREPSW,&tpm_sw)<0) - fatal("Set key repetition ON"); - } - else - usage(); - - /* do we need to set new values */ - if (delay == -1 && rate == -1) return; - - /* get current setting */ - if (ioctl(kbd,KBDGTPMAT,¤t_tpm)<0) - fatal("Get typematic value"); - - delay = (delay == -1) ? (current_tpm & 0x60) : ((delay & 0x3)<<5); - rate = (rate == -1) ? (current_tpm & 0x1F) : (rate & 0x1F); - - new_tpm = delay | rate; - - /* set the new setting */ - if (ioctl(kbd,KBDSTPMAT,&new_tpm)<0) - fatal("Set typematic value"); - - return; -} - -saver(int stime) -{ - int fv; - - if ((fv=open(_PATH_VIDEO,0)) <= 0) - fatal("Open video device"); - - if (ioctl(fv,VGASBLANK,&stime)<0) - fatal("Set timeout value"); - - close(fv); -} - -char keyflag[128]; - -checkdupandset(k) -{ - if (keyflag[k]) - error("Duplicate key def"); - keyflag[k]=1; -} - -mapkey(char *map) -{ - struct kbd_ovlkey key; - char cap[1024]; - int i,k,n,setflag; - char code[5]; - char *ap1; - char diac[9]; - int m0flag=0,a0flag,c0flag,ledflag,clflag; - XCHAR *ap; - -#define NSTANDARD 7 - struct { - XCHAR *addr; - char ch; - char modifier; - } standard[] = { - 0, 'D', KBD_NOEXT, - &key.unshift[0], 'K', KBD_EXT_N, - &key.shift[0], 'S', KBD_EXT_S, - &key.ctrl[0], 'C', KBD_EXT_C, - &key.meta[0], 'M', KBD_EXT_SK, - &key.altgr[0], 'A', KBD_EXT_A, - &key.shiftaltgr[0], 'X', KBD_EXT_CA, - }; - -#define NSPECIAL 4 - struct { - u_short typ; - char ch; - } special[] = { - KBD_META, 'm', - KBD_ALTGR, 'l', - KBD_SHIFT, 'h', - KBD_CTL, 't' - }; - -#define NLOCKKEYS 5 - struct { - char *ch; - u_short typ; - } lockkeys[] = - { - "ca", KBD_CAPS, - "sh", KBD_SHFTLOCK, - "nl", KBD_NUM, - "sc", KBD_SCROLL, - "ag", KBD_ALTGRLOCK - }; - - /* try to find the entry */ - switch(kgetent(cap,map)) { - case -1: - error("Keycap database not found"); - case 0: - error("No such keymap entry"); - } - - /* set default mapping */ - if (ioctl(kbd,KBDDEFAULT)<0) - error("Cannot reset to default mapping"); - - /* load fonts */ - loadfonts(); - - /* DE flag present? */ - if (kgetflag("de")) return; - - /* C0 flag */ - c0flag = kgetflag("c0"); - - /* A0 flag */ - a0flag = kgetflag("a0"); - - /* Caps LED assignments */ - ledflag = kgetnum("la"); - if (ledflag == -1) - ledflag = 0; - ledflag |= KBD_CAPSINIT; - - for (i=0; i<128; i++) keyflag[i] = 0; - - /* check for locking keys */ - for (k=0; k<NLOCKKEYS; k++) { - sprintf(code,"%s",lockkeys[k].ch); - n = kgetnum(code); - if (n > 0) { - checkdupandset(n); - key.keynum = n; - key.type = lockkeys[k].typ; - key.shift[0] = 0; - key.ctrl[0] = 0; - key.altgr[0] = 0; - key.shiftaltgr[0] = 0; - if (ioctl(kbd,KBDSCKEY,&key)<0) - error("Cannot set lockkey"); - } - } - - /* check for special keys */ - for (k=0; k<NSPECIAL; k++) { - for (i=1; i<10; i++) { - sprintf(code,"%c%d",special[k].ch,i); - n = kgetnum(code); - if (n >= 0) { - checkdupandset(n); - key.keynum = n; - key.type = special[k].typ; - if (ioctl(kbd,KBDSCKEY,&key)<0) - error("Cannot set specialkey"); - } - } - } - - /* check diacritical flags */ - for (i=0; i<9; i++) { - sprintf(code,"p%d",i+1); - n = kgetnum(code); - if (n>0) diac[i] = n; - else diac[i] = 0; - } - - /* handle and collect standard keys */ - for (i=1; i<128; i++) { - setflag = 0; - key.keynum = i; - if (ioctl(kbd,KBDGOKEY,&key)<0) - error("Cannot get key setting"); - - /* XXX this will defeat the different semantic of - * KBD_KP, KBD_ASCII, etc. Will be corrected some day - */ - key.type &= ~KBD_MASK; - key.type |= KBD_ASCII; - for (k=0; k<9 && diac[k]; k++) - if (diac[k]==i) { - key.type |= KBD_DIACPFX; - break; - } - - for (k=0; k<NSTANDARD; k++) { - sprintf(code,"%c%d",standard[k].ch,i); - if (k==0 && kgetflag(code)) { - /* delete a key */ - key.type = KBD_NONE; - setflag = 1; - goto setit; - } else - { - ap = standard[k].addr; -#if XCHAR==u_char - if (ap1=kgetstr(code,&(char*)ap)) { -#else -ERROR! FIX ME! -#endif - if (strlen(ap1)>KBDMAXOVLKEYSIZE) - error("String too long in keymap"); - ap = standard[k].addr; - - /* process alphalock flag */ - if (*ap=='~') { - if (ap==key.unshift) { - XC_strcpy (ap, ap+1); - key.type |= KBD_DOCAPS; - } else if (ap==key.altgr) { - XC_strcpy(ap, ap+1); - key.type |= KBD_DOALTCAPS; - } - /* process special hotkeys */ - } else if (*ap=='?') { - int modifier; - int func; - struct kbd_hotkey s; - - /* get the function */ - switch (ap[1]) { - case '-': func = KBD_HOTKEYDELETE; break; - case 'R': func = KBD_RESETKEY; break; - case 'D': func = KBD_DEBUGKEY; break; - case 'P': func = KBD_VTYDOWN; break; - case 'N': func = KBD_VTYUP; break; - case 'B': func = KBD_VTY11; break; - case 'A': func = KBD_VTY10; break; - default: - if (ap[1]>='0' && ap[1]<=9) - func = ap[1]-'0'; - else continue; - } - /* convert the layer to trigger */ - if (modifier) modifier = standard[k].modifier; - s.key = i; - s.modifier = modifier; - s.function = func; - if (ioctl(kbd,KBDSSPECF,&s) <0) - error("KBDSSPECF ioctl failed"); - continue; - } - setflag = 1; - } - } - } - -setit: if (setflag) { - checkdupandset(i); - if (! key.meta [0]) { - key.meta [0] = key.unshift [0] ^ 0x80; - key.meta [1] = 0; - } - if (ioctl(kbd,KBDSCKEY,&key)<0) - error("Cannot set stdkey"); - } - } - - /* set the behavior for unassigned keys (clear layer) */ - clflag = a0flag | (c0flag<<1) | (m0flag<<2); - if (ioctl(kbd,KBDSCLRLYR,&clflag) < 0) - error("KBDSCLRLYR ioctl failed"); - - /* now mark the current keyboard setting in the 0 entry */ - key.keynum = 0; - key.type = KBD_NONE; - - XC_strncpy(key.unshift,map,KBDMAXOVLKEYSIZE); - key.unshift[KBDMAXOVLKEYSIZE] = 0; - key.shift[0] = 0; - key.ctrl[0] = 0; - key.altgr[0] = 0; - key.shiftaltgr[0] = 0; - - if (ioctl(kbd,KBDSCKEY,&key)<0) - error("Cannot set keymap code"); - - if (ioctl(kbd,KBDSCAPSLED,&ledflag)<0) - error("Cannot assign LEDs"); -} - -loadfonts() -{ - char *s,fp[255]; - int n; - - s = fp; - if (kgetstr("fn0",&s)) - if (setfont(fp,1,0)) return; - s = fp; - if (kgetstr("fn1",&s)) - if (setfont(fp,1,1)) return; -} - -/* - * simple program to test KEY NUMBERS - * - * leave this program by hitting the ESC key twice, followed by the SPACE bar - */ - -/* should be fixed on all keyboards */ -#define IBMKEY_ESC 110 -#define IBMKEY_SPACE 61 - -keynum () -{ - int state = 0; - char c; - - printf ("Key number test.\n"); - printf ("Press different combinations of keys.\n"); - printf ("Leave program with sequence ESC,ESC,SPACE (with release).\n"); - for(;;) { - if (read (kbd, &c, 1) != 1) - continue; - printf ("Got key %02x %s\n", c & 0xff, - c & 0x80 ? "(release)" : ""); - if (c & 0x80) continue; - c &= 0x7f; - switch (c) { - case IBMKEY_ESC: - if (state < 2) /* ESC */ - state++; - else - state = 0; - break; - case IBMKEY_SPACE: - if (state == 2) - return; - default: - state = 0; - break; - } - } -} - -#define SHIFTFLAG 0x0100 -#define CTRLFLAG 0x0200 -#define ALTFLAG 0x0400 -#define SHIFTALTFLAG 0x0800 -#define METAFLAG 0x1000 - -/* The code in codrv uses a double \0\0 to identify a NULL byte to return - * from a pressed key, but we want to have nothing for this key, and - * don't want to disable this key entirely - */ -static XCHAR notanullbyte[] = { 0,-1,0 }; -#define NOTANULLBYTE notanullbyte - -char *parse_bsl(s) - char *s; -{ - char *p,*q; - int n,i; - static char buf [16]; - - q = buf; - for (p=s; *p; p++) { - if (*p != '\\') - *q++ = *p; - else { /* start parsing backslash */ - p++; - switch (*p) { - case 'n': - n = '\n'; - break; - case 't': - n = '\t'; - break; - case 'f': - n = '\f'; - break; - case 'b': - n = '\b'; - break; - case '\\': - n = '\\'; - break; - case '\'': - n = '\''; - break; - case '\"': - n = '\"'; - break; - /* could add some more here */ - - default: - for (i=n=0; i<3; i++,p++) { - if (*p>='0' && *p<='9') { - n *= 8; - n += *p - '0'; - } else break; - } - --p; - } - *q++ = n; - } - } - *q = 0; - return (buf); -} - -assign(keydef,keycode) - char *keydef; - char *keycode; -{ - int keynum; - int defflag=0,delflag=0; - struct kbd_ovlkey keynow,keyorg; - - if (strlen(keydef) > 15) { - fprintf(stderr,"%s: String \"%s\" too long\n", - progname,keydef); - exit(1); - } - - keynum = def2num(keycode); - if (keynum<0) { - fprintf(stderr,"%s: No such key: \"%s\"\n", - progname,keycode); - exit(1); - } - - /* check whether the key should be deleted or returned to default */ - if (!strcmp(keydef,"DEFAULT")) defflag = 1; - else if (!strcmp(keydef,"DELETE")) delflag = 1; - - keydef = parse_bsl(keydef); - - keynow.keynum = keyorg.keynum = keynum & 0x7F; - if (ioctl(kbd,KBDGCKEY,&keynow)<0 || - ioctl(kbd,KBDGOKEY,&keyorg)<0) { - perror("KBDG*KEY"); - exit(1); - } - - switch(keynum&0x7F00) { - case SHIFTFLAG: - if (delflag) - XC_strncpy(keynow.shift,NOTANULLBYTE,3); - else if (defflag) - XC_strcpy(keynow.shift,keyorg.shift); - else - XC_strcpy(keynow.shift,keydef); - break; - case METAFLAG: - if (delflag) - XC_strncpy(keynow.meta,NOTANULLBYTE,3); - else if (defflag) - XC_strcpy(keynow.meta,keyorg.meta); - else - XC_strcpy(keynow.meta,keydef); - break; - case CTRLFLAG: - if (delflag) - XC_strncpy(keynow.ctrl,NOTANULLBYTE,3); - else if (defflag) - XC_strcpy(keynow.ctrl,keyorg.ctrl); - else - XC_strcpy(keynow.ctrl,keydef); - break; - case ALTFLAG: - if (delflag) - XC_strncpy(keynow.altgr,NOTANULLBYTE,3); - else if (defflag) - XC_strcpy(keynow.altgr,keyorg.altgr); - else - XC_strcpy(keynow.altgr,keydef); - break; - case SHIFTALTFLAG: - if (delflag) - XC_strncpy(keynow.shiftaltgr,NOTANULLBYTE,3); - else if (defflag) - XC_strcpy(keynow.shiftaltgr,keyorg.shiftaltgr); - else - XC_strcpy(keynow.shiftaltgr,keydef); - break; - case 0: - if (delflag) - XC_strncpy(keynow.unshift,NOTANULLBYTE,3); - else if (defflag) - XC_strcpy(keynow.unshift,keyorg.unshift); - else - XC_strcpy(keynow.unshift,keydef); - } - - if (ioctl(kbd,KBDSCKEY,&keynow)<0) { - perror("KBDSCKEY"); - exit(1); - } -} - -struct xlattable { - char *str; - int num; -} tbl [] = { - /* std. */ /* X11 naming */ - "f1", 112, - "f2", 113, - "f3", 114, - "f4", 115, - "f5", 116, - "f6", 117, - "f7", 118, - "f8", 119, - "f9", 120, - "f10", 121, - "f11", 122, - "f12", 123, - "kp0", 99, "kp_0", 99, - "kp1", 93, "kp_1", 93, - "kp2", 98, "kp_2", 98, - "kp3", 103, "kp_3", 103, - "kp4", 92, "kp_4", 92, - "kp5", 97, "kp_5", 97, - "kp6", 102, "kp_6", 102, - "kp7", 91, "kp_7", 91, - "kp8", 96, "kp_8", 96, - "kp9", 101, "kp_9", 101, - "kp.", 104, "kp_decimal", 104, - "kp+", 106, "kp_add", 106, - "kp-", 105, "kp_subtract", 105, - "kp*", 100, "kp_multiply", 100, - "kp/", 95, "kp_divide", 95, - "shift", SHIFTFLAG, - "ctrl", CTRLFLAG, - "meta", METAFLAG, - "altgr", ALTFLAG, - "shiftaltgr", SHIFTALTFLAG, - - /* and some misspellings: */ - "sh", SHIFTFLAG, - "ctl", CTRLFLAG, - "^", CTRLFLAG, - "alt", ALTFLAG, - "shalt", SHIFTALTFLAG, - "meta", ALTFLAG, - "shmeta", SHIFTALTFLAG, - - /* must be last */ - 0, 0, -}; - -int def2num(keycode) - char *keycode; -{ - int i,k; - char *token,*strtok(),*ep; - int keynum = 0; - int flag1 = 0,flag2 = 0; - - /* look for '-' */ - token = strtok(keycode,"-"); - while (token) { - k = strtol(token,&ep,0); - if (ep!=token) { /* got a number */ - keynum |= k; - flag2++; - break; - } - /* reserved word? */ - for (i=0; tbl[i].str; i++) { - if (!strcasecmp(keycode,tbl[i].str)) { - keynum |= tbl[i].num; - if (tbl[i].num < 128) flag2++; - else flag1++; - } - } - token=strtok(NULL,"-"); - } - - /* invalid */ - if (flag1 > 1 || flag2 != 1) return -1; - if ((keynum & 0x7F)==0) return -1; - - return keynum; -} - diff --git a/usr.sbin/keymap/keymap/pathnames.h b/usr.sbin/keymap/keymap/pathnames.h deleted file mode 100644 index 25e80f147f..0000000000 --- a/usr.sbin/keymap/keymap/pathnames.h +++ /dev/null @@ -1,20 +0,0 @@ -/* Copyright 1992 by Holger Veit - * May be freely used and modified with Bill Jolitz's port of - * 386bsd and may be included in a 386bsd collection - * as long as binary and source reproduce the above copyright - * Commercial use of this source requires permittance of the copyright - * holder. - * Use at your own risk. The copyright holder or any person who makes - * this code available for the public (administrators of public archives - * for instance) are not responsible for any harm to hardware or software - * that might happen due to wrong application or program faults. - * - * @(#)pathnames.h 1.0 (386bsd contribution) 1.8.92 - */ - -#include <paths.h> - -#define _PATH_KEYCAP "/usr/share/misc/keycap" -#define _PATH_KEYBOARD "/dev/kbd" -#define _PATH_VIDEO "/dev/vga" - diff --git a/usr.sbin/keymap/lib/Makefile b/usr.sbin/keymap/lib/Makefile deleted file mode 100644 index 9d2e581938..0000000000 --- a/usr.sbin/keymap/lib/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# @(#)Makefile 5.10 (Berkeley) 5/6/91 - -LIB= consutil -SRCS= keycap.c setfont.c whichcons.c xc_fns.c -MAN3= keycap.0 setfont.0 whichcons.0 -MAN4= co.0 -MAN5= vgafont.0 - -MLINKS+=keycap.3 kgetent.3 -MLINKS+=keycap.3 kgetflag.3 -MLINKS+=keycap.3 kgetnum.3 -MLINKS+=keycap.3 kgetstr.3 -MLINKS+=co.4 kbd.4 -MLINKS+=co.4 vga.4 - -.include <bsd.lib.mk> diff --git a/usr.sbin/keymap/lib/co.4 b/usr.sbin/keymap/lib/co.4 deleted file mode 100644 index cb0083c57f..0000000000 --- a/usr.sbin/keymap/lib/co.4 +++ /dev/null @@ -1,262 +0,0 @@ -.\" Contributed to 386bsd 0.1 and later versions -.\" -.\" Copyright 1992,1993 by Holger Veit -.\" May be freely used with Bill Jolitz's port of -.\" 386bsd and may be included in a 386bsd collection -.\" as long as binary and source are available and reproduce the above -.\" copyright. -.\" -.\" You may freely modify this code and contribute improvements based -.\" on this code as long as you don't claim to be the original author. -.\" Commercial use of this source requires permittance of the copyright -.\" holder. A general license for 386bsd will override this restriction. -.\" -.\" Use at your own risk. The copyright holder or any person who makes -.\" this code available for the public (administrators of public archives -.\" for instance) are not responsible for any harm to hardware or software -.\" that might happen due to wrong application or program faults. -.\" -.\" You must have the codriver driver in the same package generated -.\" into the 386bsd kernel, otherwise this program does not work. -.\" -.\" @(#)co.4 1.1 (contributed to 386bsd) 06/05/92 -.\" -.Dd May 6, 1993 -.Dt CONSOLE 3 -.Os 386bsd 0.2 -.Sh NAME -.Nm co -.Nd new console driver -.Sh SYNOPSIS -.Cd device co0 at isa? port 0x60 tty "irq 1 vector cointr" -.Pp -.Fd #include <sys/ioctl.h> -.Fd #include <sys/ioctl_pc.h> -.Sh DESCRIPTION -The -.Em co -console driver (also called -.Em codrv ) -replaces the old outdated -.Em pccons -console driver. Several features and improvements have been built in: -.Bl -bullet -compact -.It -IBM AT key numbering scheme, not the incompatible and complex -scan code handling -.It -/dev/kbd raw device -.It -XFree86 1.2 and greater support -.It -National language keyboard support (see -.Xr keycap 5 ) -including overloadable keys (any key, not only function keys) -.It -Programmable keyboard repetition rate ("typematic") -.It -Programmable keyboard LEDs -.It -Programmable Beep (pitch and duration) -.It -Recognition of several SVGA cards, recognition of MDA/CGA/EGA/VGA -.It -Programmable cursor shape -.It -Standard ISO-Latin 1 character set -.It -Loadable national font (in addition to standard Latin 1) -.It -Programmable screen blanking -.It -Multiple virtual tty support -.El -.Pp -The console driver is a single interrupt driver which is divided into a -number of logical and physical devices: -.Pp -.Bl -tag -width /dev/console -compact -.It Pa /dev/tty00 -The console interface. This device behaves like a tty device, and delivers -7/8 bit ASCII characters on request. -.It Pa /dev/tty01-XX -Switchable virtual terminals (vty's, often also called -virtual consoles). Up to 12 may be generated in the -kernel. Any of them may be monitored by -.Xr getty 8 . -.It Pa /dev/console -The console redirector. This is the console switcher in the closer sense. -When the keyboard/video is present, it redirects console requests to the -.Pa /dev/tty00 -device, otherwise to the first available serial interface. The -.Pa /dev/tty00 -and -.Pa /dev/console -devices should not be confused, the first is a physically existing unit, the -second one is a logical redirecting device. When accessing the console, -use -.Pa /dev/console -only, but connect -.Pa /dev/tty00 to a getty (e.g. in /etc/ttys). -.It Pa /dev/kbd -The raw keynum device. This device is noramlly opened by the XServer (see -.Xr X 1 ) -only; this device is exclusive and suppresses input from -.Pa /dev/console -when opened. -.It Pa /dev/vga -The video device. In the current version of -.Em codrv , -.Pa /dev/kbd -and -.Pa /dev/vga -are basically the same device, however, -.Pa /dev/vga -is not exclusive, but does not allow reading keystrokes from the keyboard. -.El -.Pp -All devices share the same set of ioctl functions as described below, but -this may change in the future. For compatibility, -.Em tty -related ioctls (e.g. TIOC*, CONS*) should be applied to -.Pa /dev/console -only, -.Em keyboard -related ioctls (KBD*, CONS*) to -.Pa /dev/kbd , -and -.Em video -related ones (VGA*, CONS*) to -.Pa /dev/vga . -.Pp -The following set of ioctls is understood. For the exact calling convention -see -.Pa /usr/include/sys/ioctl_pc.h . -Ioctls marked with -.Em # -are not recommended for general use and lead to unexpected effects. -.Pp -.Bl -tag -width #CONSOLE_X_MODE_OFF -compact -.It CONSGINFO -Get console capabilities flags -.Pp -.It KBDCOLDRESET -Reset the keyboard (default keymap, default repeat rate) -.It #KBDWARMRESET -Reset the keyboard controller only (not general use) -.It KBDGTPMAT -Get current key repetition rate information -.It KBDSTPMAT -Set new key repetition rate -.It KBDGREPSW -Get current key repetition flag (allows entirely disabling repetition) -.It KBDSREPSW -Set new key repetition flag (allows entirely disabling repetition) -.It KBDGLEDS -Get keyboard LED status (NumLock, CapsLock, ScrollLock) -.It KBDSLEDS -Set keyboard LED status (NumLock, CapsLock, ScrollLock) -.It KBDGLOCK -Get keyboard lock key status (NumLock, CapsLock, ScrollLock, -AltGrLock, ShiftLock) -.It KBDSLOCK -Set keyboard lock key status (NumLock, CapsLock, ScrollLock, -AltGrLock, ShiftLock) -.It KBDGETBEEP -Get the current beep pitch and duration (for actual or any vty) -.It KBDSETBEEP -Set the current beep pitch and duration (for actual or any vty) -.It KBDBELL -Play a tone of given pitch and duration -.It KBDGCKEY -Get current keymap for a single key -.It KBDSCKEY -Set new keymap for a single key -.It KBDGOKEY -Get original (default) keymap for a single key -.It KBDRMKEY -Remove a single key assignment -.It KBDDEFAULT -Remove all key assignments -.It KBDSCLRLYR -Clear a keyboard layer -.It KBDGCAPSLED -Get the behaviour of the CAPSLOCK LED. -.It KBDSCAPSLED -Set the behaviour of the CAPSLOCK LED to follow the CapsLock, -ShiftLock or AltGrLock state. -.It KBDGSPECF -Get the behaviour of a "hot key" (a key processed before the -normal ASCII interpretation) -.It KBDSSPECF -Set the behaviour of a "hot key" -.Pp -.It #CONSOLE_X_MODE -Set XServer mode (priviledged and not for general use) -.It #CONSOLE_X_MODE_ON -Set XServer mode (priviledged, compatibility, may disappear in future) -.It #CONSOLE_X_MODE_OFF -Set XServer mode (priviledged, compatibility, may disappear in future) -.Pp -.It VGASCURSOR -Set cursor shape -.It VGAGCURSOR -Get current cursor shape -.It #VGAGINFO -Get video information -.It VGAGBLANK -Get display blanking timeout -.It VGASBLANK -Set display blanking timeout -.It VGAGBLOCK -Get a rectangular area of text screen -.It VGASBLOCK -Set a rectangular area of text screen -.It VGAGPAGE -Get screen page data -.It VGASPAGE -Set screen page data -.It #VGASFONTMAP -Load a font into video memory (EGA or better only) -.It #VGAGFONTMAP -Read a font from video memory (EGA or better only) -.It #VGATAKECTRL -Signal to the kernel that process wants exclusive access to the video -resource -.It #VGAIVECTRL -Signal to the kernel that process releases video resource -.El -.Sh FILES -.Bl -tag -width /sys/i386/isa/codrv/co_codrv1.c -compact -.It Pa /sys/i386/isa/codrv/co_cons.c -Console tty support kernel routines -.It Pa /sys/i386/isa/codrv/co_kbd.c -Keyboard kernel routines -.It Pa /sys/i386/isa/codrv/co_vga.c -Video kernel routines -.It Pa /sys/i386/isa/codrv/co_codrv1.c -Ioctl processor -.It Pa /sys/i386/isa/codrv/co_pc3.c -PC3 Terminal emulation -.It Pa /sys/i386/isa/codrv/co_vty.c -Virtual terminal support -.It Pa /usr/share/misc/keycap -standard keyboard capability data base -.El -.Sh SEE ALSO -.Xr keycap 5 -.Sh HISTORY -The -.Em codrv -driver was derived from the -.Em pccons -driver and was heavily modified since then. -.Sh BUGS -The terminal emulation is still "pc3", for compatibility issues, and -may be changed in future. -.Pp -The naming convention VGA* is misleading, although VGA cards have become -cheap enough to be standard even in low cost systems. -.Pp -Much is still under construction. - diff --git a/usr.sbin/keymap/lib/keycap.3 b/usr.sbin/keymap/lib/keycap.3 deleted file mode 100644 index 92a692ba92..0000000000 --- a/usr.sbin/keymap/lib/keycap.3 +++ /dev/null @@ -1,137 +0,0 @@ -.\" Contributed to 386bsd 0.1 and later versions -.\" -.\" Copyright 1992 by Holger Veit -.\" May be freely used with Bill Jolitz's port of -.\" 386bsd and may be included in a 386bsd collection -.\" as long as binary and source are available and reproduce the above -.\" copyright. -.\" -.\" You may freely modify this code and contribute improvements based -.\" on this code as long as you don't claim to be the original author. -.\" Commercial use of this source requires permittance of the copyright -.\" holder. A general license for 386bsd will override this restriction. -.\" -.\" Use at your own risk. The copyright holder or any person who makes -.\" this code available for the public (administrators of public archives -.\" for instance) are not responsible for any harm to hardware or software -.\" that might happen due to wrong application or program faults. -.\" -.\" You must have the codriver driver in the same package generated -.\" into the 386bsd kernel, otherwise this program does not work. -.\" -.\" @(#)keycap.3 1.0 (contributed to 386bsd) 10/25/92 -.\" -.Dd October 25, 1992 -.Dt CONSUTIL 3 -.Os 386bsd 0.2 -.Sh NAME -.Nm kgetent , -.Nm kgetnum , -.Nm kgetflag , -.Nm kgetstr -.Nd keyboard independent operation routines -.Sh SYNOPSIS -.Fn kgetent "char *bp" "char *name" -.Fn kgetnum "char *id" -.Fn kgetflag "char *id" -.Ft char * -.Fn kgetstr "char *id" "char *area" -.Sh DESCRIPTION -These functions extract and use capabilities from a keyboard capability data -base, usually -.Pa /usr/share/misc/keycap , -the format of which is described in -.Xr keycap 5 . -.Pp -The -.Fn kgetent -function -extracts the entry for a keyboard entry -.Fa name -into the buffer at -.Fa bp . -The -.Fa bp -argument -should be a character buffer of size -1024 and must be retained through all subsequent calls to -.Fn kgetnum , -.Fn kgetflag , -and -.Fn kgetstr . -The -.Fn kgetent -function -returns \-1 if none of the -.Nm keycap -data base files could be opened, -0 if the keyboard name given does not have an entry, -and 1 if all goes well. -It will look in the environment for a -.Ev KEYCAP -variable. -If found, and the value does not begin with a slash, -and the keyboard type -.Fa name -is the same as the environment string -.Ev KEYBOARD , -the -.Ev KEYCAP -string is used instead of reading a -.Nm keycap -file. -If it does begin with a slash, the string is used as a path name -of the -.Nm keycap -file to search. -If -.Ev KEYCAP -does not begin with a slash, -.Fn kgetent -searches the file -.Pp -The -.Fn kgetnum -function -gets the numeric value of capability -.Fa id , -returning \-1 if is not given for the keyboard -The -.Fn kgetflag -function -returns 1 if the specified capability is present in -the keyboard's entry, 0 if it is not. -The -.Fn kgetstr -function -returns the string value of the capability -.Fa id , -places it in the buffer at -.Fa area , -and advances the -.Fa area -pointer. -It decodes the abbreviations for this field described in -.Xr keycap 5 . -The -.Fn kgetstr -function -returns -.Dv NULL -if the capability was not found. -.Pp -.Sh FILES -.Bl -tag -width /usr/share/misc/keycap -compact -.It Pa /usr/lib/libconsutil.a -.Fl l Ar consutil -keycap utilities library -.It Pa /usr/share/misc/keycap -standard keyboard capability data base -.El -.Sh SEE ALSO -.Xr co 4 , -.Xr keycap 5 -.Sh HISTORY -The -.Nm -keyboard capability data base is a new feature of 386bsd 0.2 and later. diff --git a/usr.sbin/keymap/lib/keycap.c b/usr.sbin/keymap/lib/keycap.c deleted file mode 100644 index 82210f254c..0000000000 --- a/usr.sbin/keymap/lib/keycap.c +++ /dev/null @@ -1,487 +0,0 @@ -/* - * Copyright (c) 1983 Regents of the University of California. - * All rights reserved. - * - * Parts Copyright 1992 by Holger Veit - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This code has been derived from the PRINTCAP source code that is part - * of 386bsd which underlies the above Copyright. Changes have been made - * by Holger Veit (-hv-) - * - * 29/07/92 -hv- copied and adapted from printcap common_source - * - * -hv-: Hey, after gettytab, termcap and printcap this is the fourth time, - * that this code has been adapted. Now, we should perhaps think about - * a universal capabilities library which unites all four and allows different - * data bases to be checked with a unified interface. This must be - * documented of course in great detail. Any volunteers? - * - * "Suddenly, it was so quiet, one could almost hear a byte drop to - * the floor..." - * "1000 methods to get rid of hackers" - * by H. Veit, to appear RSN :-) - */ - -#ifndef lint -static char sccsid[] = "@(#)keycap.c 1.0 (Contributed to 386bsd) 7/29/92"; -#endif /* not lint */ - -#include <ctype.h> -#include <stdio.h> -#include "pathnames.h" - -#ifndef BUFSIZ -#define BUFSIZ 1024 -#endif -#define MAXHOP 32 /* max number of tc= indirections */ - -/* - * keycap - routines for dealing with the keyboard capability data base - * - * BUG: Should use a "last" pointer in tbuf, so that searching - * for capabilities alphabetically would not be a n**2/2 - * process when large numbers of capabilities are given. - * Note: If we add a last pointer now we will screw up the - * tc capability. We really should compile termcap. - * - * Essentially all the work here is scanning and decoding escapes - * in string capabilities. We don't use stdio because the editor - * doesn't, and because living w/o it is not hard. - */ - -#define KEYCAP - -#ifdef KEYCAP -#define tgetent kgetent -#define tskip kskip -#define tgetstr kgetstr -#define tdecode kdecode -#define tgetnum kgetnum -#define tgetflag kgetflag -#define tdecode kdecode -#define tnchktc knchktc -#define tnamatch knamatch -#define V6 -#endif - -static FILE *pfp = NULL; /* keycap data base file pointer */ -static char *tbuf; -static int hopcount; /* detect infinite loops in keycap, init 0 */ -char *tskip(); -char *tgetstr(); -char *tdecode(); -char *getenv(); - -/* - * - * -hv- added to understand capability names longer than 2 chars - * which is very important if we do not want to crypt the key codes - */ -static char *nmatch(id,cstr) - char *id,*cstr; -{ - register n = strlen(id); - register char *c = cstr+n; - - if (strncmp(id,cstr,n)==0 && - (*c==':' || *c=='|' || *c=='=' || *c=='#' || *c=='@')) { - return c; - } - return 0; -} - -#ifdef NOTUSED -/* - * Similar to tgetent except it returns the next entry instead of - * doing a lookup. - */ -getprent(bp) - register char *bp; -{ - register int c, skip = 0; - - if (pfp == NULL && (pfp = fopen(_PATH_KEYCAP, "r")) == NULL) - return(-1); - tbuf = bp; - for (;;) { - switch (c = getc(pfp)) { - case EOF: - fclose(pfp); - pfp = NULL; - return(0); - case '\n': - if (bp == tbuf) { - skip = 0; - continue; - } - if (bp[-1] == '\\') { - bp--; - continue; - } - *bp = '\0'; - return(1); - case '#': - if (bp == tbuf) - skip++; - default: - if (skip) - continue; - if (bp >= tbuf+BUFSIZ) { - write(2, "Keycap entry too long\n", 23); - *bp = '\0'; - return(1); - } - *bp++ = c; - } - } -} - -endprent() -{ - if (pfp != NULL) - fclose(pfp); -} -#endif - -/* - * Get an entry for keyboard name in buffer bp, - * from the keycap file. Parse is very rudimentary; - * we just notice escaped newlines. - * - */ -tgetent(bp, name) - char *bp, *name; -{ - - register char *cp; - register int c; - register int i = 0, cnt = 0; - char ibuf[BUFSIZ]; - char *cp2; - int tf; - - tbuf = bp; - tf = 0; - - cp = getenv("KEYCAP"); - /* - * KEYCAP can have one of two things in it. It can be the - * name of a file to use instead of /usr/share/misc/keycap. In this - * case it better start with a "/". Or it can be an entry to - * use so we don't have to read the file. In this case it - * has to already have the newlines crunched out. - */ - if (cp && *cp) { - if (*cp!='/') { - cp2 = getenv("KEYBOARD"); - if (cp2==(char *) 0 || strcmp(name,cp2)==0) { - strcpy(bp,cp); - return(tnchktc()); - } else { - tf = open(_PATH_KEYCAP, 0); - } - } else - tf = open(cp, 0); - } - if (tf==0) - tf = open(_PATH_KEYCAP, 0); - - if (tf < 0) - return (-1); - for (;;) { - cp = bp; - for (;;) { - if (i == cnt) { - cnt = read(tf, ibuf, BUFSIZ); - if (cnt <= 0) { - close(tf); - return (0); - } - i = 0; - } - c = ibuf[i++]; - if (c == '\n') { - if (cp > bp && cp[-1] == '\\'){ - cp--; - continue; - } - break; - } - if (cp >= bp+BUFSIZ) { - write(2,"Keycap entry too long\n", 22); - break; - } else - *cp++ = c; - } - *cp = 0; - - /* - * The real work for the match. - */ - if (tnamatch(name)) { - close(tf); - return(tnchktc()); - } - } -} - -/* - * tnchktc: check the last entry, see if it's tc=xxx. If so, - * recursively find xxx and append that entry (minus the names) - * to take the place of the tc=xxx entry. This allows keycap - * entries to say "like an HP2621 but doesn't turn on the labels". - * Note that this works because of the left to right scan. - */ -tnchktc() -{ - register char *p, *q; - char tcname[16]; /* name of similar keyboard */ - char tcbuf[BUFSIZ]; - char *holdtbuf = tbuf; - int l; - - p = tbuf + strlen(tbuf) - 2; /* before the last colon */ - while (*--p != ':') - if (p<tbuf) { - write(2, "Bad keycap entry\n", 17); - return (0); - } - p++; - /* p now points to beginning of last field */ - if (p[0] != 't' || p[1] != 'c') - return(1); - strcpy(tcname,p+3); - q = tcname; - while (q && *q != ':') - q++; - *q = 0; - if (++hopcount > MAXHOP) { - write(2, "Infinite tc= loop\n", 18); - return (0); - } - if (tgetent(tcbuf, tcname) != 1) - return(0); - for (q=tcbuf; *q != ':'; q++) - ; - l = p - holdtbuf + strlen(q); - if (l > BUFSIZ) { - write(2, "Keycap entry too long\n", 22); - q[BUFSIZ - (p-tbuf)] = 0; - } - strcpy(p, q+1); - tbuf = holdtbuf; - return(1); -} - -/* - * Tnamatch deals with name matching. The first field of the keycap - * entry is a sequence of names separated by |'s, so we compare - * against each such name. The normal : terminator after the last - * name (before the first field) stops us. - */ -tnamatch(np) - char *np; -{ - register char *Np, *Bp; - - Bp = tbuf; - if (*Bp == '#' || *Bp == 0) - return(0); - for (;;) { - for (Np = np; *Np && *Bp == *Np; Bp++, Np++) - continue; - if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) - return (1); - while (*Bp && *Bp != ':' && *Bp != '|') - Bp++; - if (*Bp == 0 || *Bp == ':') - return (0); - Bp++; - } -} - -/* - * Skip to the next field. Notice that this is very dumb, not - * knowing about \: escapes or any such. If necessary, :'s can be put - * into the keycap file in octal. - */ -static char * -tskip(bp) - register char *bp; -{ - - while (*bp && *bp != ':') - bp++; - if (*bp == ':') - bp++; - return (bp); -} - -/* - * Return the (numeric) option id. - * Numeric options look like - * li#80 - * i.e. the option string is separated from the numeric value by - * a # character. If the option is not found we return -1. - * Note that we handle octal numbers beginning with 0. - */ -tgetnum(id) - char *id; -{ - register int i, base; - register char *bp = tbuf,*xp; - - for (;;) { - bp = tskip(bp); - if (*bp == 0) - return (-1); - if ((xp=nmatch(id,bp)) == 0) - continue; - bp = xp; /* we have an entry */ - if (*bp == '@') - return(-1); - if (*bp != '#') - continue; - bp++; - base = 10; - if (*bp == '0') - base = 8; - i = 0; - while (isdigit(*bp)) - i *= base, i += *bp++ - '0'; - return (i); - } -} - -/* - * Handle a flag option. - * Flag options are given "naked", i.e. followed by a : or the end - * of the buffer. Return 1 if we find the option, or 0 if it is - * not given. - */ -tgetflag(id) - char *id; -{ - register char *bp = tbuf,*xp; - - for (;;) { - bp = tskip(bp); - if (!*bp) - return (0); - if ((xp=nmatch(id,bp)) != 0) { - bp = xp; - if (!*bp || *bp == ':') - return (1); - else if (*bp == '@') - return(0); - } - } - /*NOTREACHED*/ -} - -/* - * Get a string valued option. - * These are given as - * cl=^Z - * Much decoding is done on the strings, and the strings are - * placed in area, which is a ref parameter which is updated. - * No checking on area overflow. - */ -char * -tgetstr(id, area) - char *id, **area; -{ - register char *bp = tbuf,*xp; - - for (;;) { - bp = tskip(bp); - if (!*bp) - return (0); - if ((xp = nmatch(id,bp)) == 0) - continue; - bp = xp; - if (*bp == '@') - return(0); - if (*bp != '=') - continue; - bp++; - return (tdecode(bp, area)); - } -} - -/* - * Tdecode does the grung work to decode the - * string capability escapes. - */ -static char * -tdecode(str, area) - register char *str; - char **area; -{ - register char *cp; - register int c; - register char *dp; - int i; - - cp = *area; - while ((c = *str++) && c != ':') { - switch (c) { - - case '^': - c = *str++ & 037; - break; - - case '\\': - dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; - c = *str++; -nextc: - if (*dp++ == c) { - c = *dp++; - break; - } - dp++; - if (*dp) - goto nextc; - if (isdigit(c)) { - c -= '0', i = 2; - do - c <<= 3, c |= *str++ - '0'; - while (--i && isdigit(*str)); - } - break; - } - *cp++ = c; - } - *cp++ = 0; - str = *area; - *area = cp; - return (str); -} diff --git a/usr.sbin/keymap/lib/pathnames.h b/usr.sbin/keymap/lib/pathnames.h deleted file mode 100644 index 5f55296da9..0000000000 --- a/usr.sbin/keymap/lib/pathnames.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Contributed to 386bsd 0.1 and later versions - * - * Copyright 1992 by Holger Veit - * May be freely used with Bill Jolitz's port of - * 386bsd and may be included in a 386bsd collection - * as long as binary and source are available and reproduce the above - * copyright. - * - * You may freely modify this code and contribute improvements based - * on this code as long as you don't claim to be the original author. - * Commercial use of this source requires permittance of the copyright - * holder. A general license for 386bsd will override this restriction. - * - * Use at your own risk. The copyright holder or any person who makes - * this code available for the public (administrators of public archives - * for instance) are not responsible for any harm to hardware or software - * that might happen due to wrong application or program faults. - * - * You must have the codriver driver in the same package generated - * into the 386bsd kernel, otherwise this program does not work. - * - * @(#)pathnames.h 1.0 (386bsd contribution) 1.8.92 - */ - -#include <paths.h> - -#define _PATH_KEYCAP "/usr/share/misc/keycap" -#define _PATH_CONSFONT "/usr/share/font" -#define _PATH_KEYBOARD "/dev/kbd" -#define _PATH_VIDEO "/dev/vga" diff --git a/usr.sbin/keymap/lib/setfont.3 b/usr.sbin/keymap/lib/setfont.3 deleted file mode 100644 index ad6fb24b69..0000000000 --- a/usr.sbin/keymap/lib/setfont.3 +++ /dev/null @@ -1,93 +0,0 @@ -.\" Contributed to 386bsd 0.1 and later versions -.\" -.\" Copyright 1992 by Holger Veit -.\" May be freely used with Bill Jolitz's port of -.\" 386bsd and may be included in a 386bsd collection -.\" as long as binary and source are available and reproduce the above -.\" copyright. -.\" -.\" You may freely modify this code and contribute improvements based -.\" on this code as long as you don't claim to be the original author. -.\" Commercial use of this source requires permittance of the copyright -.\" holder. A general license for 386bsd will override this restriction. -.\" -.\" Use at your own risk. The copyright holder or any person who makes -.\" this code available for the public (administrators of public archives -.\" for instance) are not responsible for any harm to hardware or software -.\" that might happen due to wrong application or program faults. -.\" -.\" You must have the codriver driver in the same package generated -.\" into the 386bsd kernel, otherwise this program does not work. -.\" -.\" @(#)setfont.3 1.0 (contributed to 386bsd) 10/25/92 -.\" -.Dd October 25, 1992 -.Dt CONSUTIL 3 -.Os 386bsd 0.2 -.Sh NAME -.Nm setfont -.Nd font loading support routine -.Sh SYNOPSIS -.Fd #include <sys/ioctl.h> -.Fd #include <sys/ioctl_pc.h> -.Fn setfont "const char *path" "int verbose" "int fpage" -.Sh DESCRIPTION -.Fn Setfont -is the recommended high level interface to load fonts into the new style -console driver -.Xr co 4 . -The -.Fn setfont -routine loads a bdf font (bitmap distribution format, -as used by X11 Release 5), checks its validity, and then loads it -into the console driver into the font page -.Fa fpage . -Currently, only pages 0 and 1 are valid. The argument -.Fa verbose -writes error messages, if any, to the -.Dv stderr (see -.Xr stdio 3 ) -output. If -.Fa verbose -is 0, output is suppressed. -.Sh RETURN VALUES -On successful execution, -.Fn setfont -returns 0, otherwise -1 is returned, and the global variable -.Va errno -is set to hold the error code. -.Sh ERRORS -The following error codes are used: -.Bl -tag -width Er -.It Bq Er ENOENT -The font file -.Fa path could not be opened. -.It Bq Er EFTYPE -The file contains an improper or unsupported entry, or is not a bdf file. -.It Bq Er EINVAL -One of the parameters of a proper entry is not in an acceptable range, e.g. -the bitmap is larger than 8x16 pixels. -.El -There are more error codes which are related to the failure of the -.Dv VGASFNTMAP -ioctl (see -.Xr co 4 ). -These errors occur with an improper configured or outdated kernel. -.Pp -.Sh FILES -.Bl -tag -width *.bdf -compact -.It Pa /usr/lib/libconsutil.a -.Fl l Ar consutil -library containing setfont -.El -.Sh SEE ALSO -.Xr co 4 , -.Xr vgafont 5 -.Sh HISTORY -The -.Nm -font loading function -.Fn setfont -is a new feature of the console driver -.Xr co 4 -of 386bsd 0.2 and later. diff --git a/usr.sbin/keymap/lib/setfont.c b/usr.sbin/keymap/lib/setfont.c deleted file mode 100644 index 21bcab83aa..0000000000 --- a/usr.sbin/keymap/lib/setfont.c +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Contributed to 386bsd 0.1 and later versions - * - * Copyright 1992 by Holger Veit - * May be freely used with Bill Jolitz's port of - * 386bsd and may be included in a 386bsd collection - * as long as binary and source are available and reproduce the above - * copyright. - * - * You may freely modify this code and contribute improvements based - * on this code as long as you don't claim to be the original author. - * Commercial use of this source requires permittance of the copyright - * holder. A general license for 386bsd will override this restriction. - * - * Use at your own risk. The copyright holder or any person who makes - * this code available for the public (administrators of public archives - * for instance) are not responsible for any harm to hardware or software - * that might happen due to wrong application or program faults. - * - * You must have the codriver driver in the same package generated - * into the 386bsd kernel, otherwise this program does not work. - * - * @(#)setfont.c 1.1 (386bsd contribution) 01/10/93 - */ -#include <stdio.h> -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/ioctl_pc.h> -#include "pathnames.h" - -static char *getparam(); - -#define ISOFONT 0 -#define PC8FONT 0xFFFE - -/* setfont returns the following codes: - * 0: ok - * -1: error - * - * ENOENT: cant open font file - * EFTYPE: invalid file or improper entry - * EINVAL: valid parameter, but cannot be accepted - * e.g. a 100x100 pixel font - */ -setfont(char *path,int verbose,int fpage) -{ - FILE *fd; - - int registry; - int font_defchar; - char line[133]; /* this size is traditional */ - char *token,*param; - int incdef=0,inpixdef=0; - int i; - char *defflags; - struct fmap fmap; - struct fchar *fchar,*defchar; - int count; - int fv; - int which; - - /* find console type */ - which = whichcons(); - if (which != 4) { - if (verbose) printf("setfont: no kernel support\n"); - errno = ENOENT; - return -1; - } - - /* initialize fontmap structure */ - fmap.page = fpage; - fmap.nr = -1; - fmap.x = -1; - fmap.y = -1; - fmap.start= 0; - fmap.fntmap = 0; - - /* initialize parameters */ - registry= - font_defchar = -1; - - /* open font file */ - if ((fd = fopen(path,"r"))==NULL) { - if (verbose) - fprintf(stderr,"setfont: cannot open file %s\n", path); - return -1; - } - - while (fgets(line,132,fd) != NULL) { - - /* process line */ - token = strtok(line," \n\t"); - - if (!incdef) { - if (!strcasecmp(token,"CHARS")) { - param = strtok(NULL," \n\t"); - fmap.nr = atoi(param); - fmap.fntmap = (struct fchar*)malloc( - fmap.nr*sizeof(struct fchar)); - defflags = malloc(fmap.nr); - fchar = fmap.fntmap; - if (!fmap.fntmap || !defflags) { - if (verbose) - printf("setfont: malloc error\n"); - errno = ENOMEM; - return -1; - } - continue; - } - if (!strcasecmp(token,"STARTFONT")) continue; - if (!strcasecmp(token,"COMMENT")) continue; - if (!strcasecmp(token,"FONT")) continue; - if (!strcasecmp(token,"SIZE")) { - param = strtok(NULL," \n\t"); - if (atoi(param) > VGA_MAXY) goto einval; - continue; - } - if (!strcasecmp(token,"FONTBOUNDINGBOX")) { - param = strtok(NULL," \n\t"); - fmap.x = atoi(param); - param = strtok(NULL," \n\t"); - fmap.y = atoi(param); - if (fmap.x > VGA_MAXX || fmap.y > VGA_MAXY) - goto einval; - continue; - } - if (!strcasecmp(token,"STARTPROPERTIES")) continue; - if (!strcasecmp(token,"FONTNAME_REGISTRY")) continue; - if (!strcasecmp(token,"FOUNDRY")) continue; - if (!strcasecmp(token,"FAMILY_NAME")) { - param = getparam(); - if (strcasecmp(param,"fixed")) - goto einval; - continue; - } - if (!strcasecmp(token,"WEIGHT_NAME")) continue; - if (!strcasecmp(token,"SLANT")) continue; - if (!strcasecmp(token,"SETWIDTH_NAME")) continue; - if (!strcasecmp(token,"ADD_STYLE_NAME")) continue; - if (!strcasecmp(token,"PIXEL_SIZE")) { - param = strtok(NULL," \n\t"); - if (atoi(param) > VGA_MAXX) goto einval; - continue; - } - if (!strcasecmp(token,"POINT_SIZE")) continue; - if (!strcasecmp(token,"RESOLUTION_X")) continue; - if (!strcasecmp(token,"RESOLUTION_Y")) continue; - if (!strcasecmp(token,"SPACING")) continue; - if (!strcasecmp(token,"AVERAGE_WIDTH")) continue; - if (!strcasecmp(token,"CHARSET_REGISTRY")) { - param = getparam(); - if (!strcasecmp(param,"ISO8859")) - registry = ISOFONT; - else if (!strcasecmp(param,"IBMPC8")) - registry = PC8FONT; - else - goto einval; - continue; - } - if (!strcasecmp(token,"CHARSET_ENCODING")) continue; - if (!strcasecmp(token,"DEFAULT_CHAR")) { - param = strtok(NULL," \n\t"); - font_defchar = atoi(param); - continue; - } - if (!strcasecmp(token,"FONT_DESCENT")) continue; - if (!strcasecmp(token,"FONT_ASCENT")) continue; - if (!strcasecmp(token,"COPYRIGHT")) continue; - if (!strcasecmp(token,"ENDPROPERTIES")) continue; - if (!strcasecmp(token,"STARTCHAR")) { - if (!fmap.fntmap) goto einval; - incdef = 1; - continue; - } - } - else if (!inpixdef) { - /* character definition */ - if (!strcasecmp(token,"COMMENT")) continue; - if (!strcasecmp(token,"BBX")) continue; - if (!strcasecmp(token,"ENDFONT")) break; - if (!strcasecmp(token,"STARTCHAR")) { - fchar++; - continue; - } - if (!strcasecmp(token,"ENCODING")) { - param = strtok(NULL," \n\t"); - fchar->encoding = atoi(param); - defflags[fchar->encoding] = 1; - continue; - } - if (!strcasecmp(token,"SWIDTH")) continue; - if (!strcasecmp(token,"DWIDTH")) continue; - if (!strcasecmp(token,"BBX")) continue; - if (!strcasecmp(token,"BITMAP")) { - inpixdef = 1; - count = 0; - continue; - } - } else - { - if (!strcasecmp(token,"COMMENT")) continue; - if (!strcasecmp(token,"ENDCHAR")) { - inpixdef = 0; - continue; - } - /* in bitmap definition - * XXX accept 2 hex digits maximum - * change with GFX console - */ - token[2]=0; - fchar->map[count++] = strtol(token,NULL,16); - continue; - } - if (verbose) { - fprintf(stderr,"setfont: invalid string %s\n",token); - } - errno = EFTYPE; - return -1; - } - - /* end font */ - fclose(fd); - - /* fill default positions */ - if (font_defchar >= 0) { - defchar = fmap.fntmap+font_defchar; - for (i=0; i<fmap.nr; i++) { - if (defflags[i]==0) - bcopy(defchar,fmap.fntmap+i,sizeof(struct fchar)); - } - } - - /* some hack */ - fmap.start = registry; - - /* set ioctl */ - if ((fv=open(_PATH_VIDEO,0)) <= 0) { - if (verbose) perror("setfont: open video device"); - return -1; - } - - if (ioctl(fv,VGASFONTMAP,&fmap)<0) { - if (verbose) perror("setfont: load font"); - return -1; - } - close(fv); - return 0; - -einval: - if (verbose) { - fprintf(stderr,"setfont: invalid parameter in %s\n", - token); - } - fclose(fd); - - errno = EINVAL; - return -1; -} - -static char *getparam() -{ - char *s,*param = strtok(NULL," \t\n"); - if (param && param[0]=='\"') { - for (s= ++param; *s; s++) - if (*s=='\"') { *s=0; break; } - } - return param; -} diff --git a/usr.sbin/keymap/lib/vgafont.5 b/usr.sbin/keymap/lib/vgafont.5 deleted file mode 100644 index 7cd2449b39..0000000000 --- a/usr.sbin/keymap/lib/vgafont.5 +++ /dev/null @@ -1,134 +0,0 @@ -.\" Contributed to 386bsd 0.1 and later versions -.\" -.\" Copyright 1992 by Holger Veit -.\" May be freely used with Bill Jolitz's port of -.\" 386bsd and may be included in a 386bsd collection -.\" as long as binary and source are available and reproduce the above -.\" copyright. -.\" -.\" You may freely modify this code and contribute improvements based -.\" on this code as long as you don't claim to be the original author. -.\" Commercial use of this source requires permittance of the copyright -.\" holder. A general license for 386bsd will override this restriction. -.\" -.\" Use at your own risk. The copyright holder or any person who makes -.\" this code available for the public (administrators of public archives -.\" for instance) are not responsible for any harm to hardware or software -.\" that might happen due to wrong application or program faults. -.\" -.\" You must have the codriver driver in the same package generated -.\" into the 386bsd kernel, otherwise this program does not work. -.\" -.\" @(#)vgafont.5 1.0 (contributed to 386bsd) 10/25/92 -.\" -.Dd October 25, 1992 -.Dt CONSOLE 5 -.Os -.Sh NAME -.Nm vgafont -.Nd setfont bdf font format -.Sh DESCRIPTION -This document describes the differences between the font format loadable by -the -.Xr setfont 3 -routine and the original bdf (bitmap distribution format) as used by -the X11 Windows System Release 5. -.Pp -In general, -.Fn setfont -understands all keywords of the bdf file, but only uses a subset of them. -.Pp -The main restriction for usability of fonts is that the console is -operated in text mode whereas the X11 Windows system uses -graphics to display a character. Thus, a usable -font underlies the following limits: -.Pp -.Bl -enum -compact -.It -The bitmap of a character must be smaller than 8x16. Note that this fits for -a 3x5 font for instance; it is, however, not recommended to load such small -fonts, because this font is displayed in a 8x16 pixel map anyway, and thus -might look quite funny. -.It -The font should have fixed width, however, proportional fonts may be -loaded into the fixed grid, but look quite ugly then. -.It -A character set may contain a maximum of 256 entries. Together with the -facility of the console driver to load two fonts, there may be 512 different -characters displayed simultaneously. This is sufficient for most -latin character sets (if you want to do word processing or TeX, use a -graphical video system). -.It -X11 uses a 16 bit encoding scheme similiar to the ISO 10646-1 Draft standard -to express a character code. 386bsd is currently based on a 8 bit scheme. -To allow more than 256 character codes and retaining 8 bit compatibility, -the console driver uses a pair of special codes to switch between fonts. -The code -.Em 0x0e (SO) -enables the second font, the code -.Em 0x0f (SI) -switches back to the first (default) font. If no second font is loaded, -SI and SO are ignored. -.Pp -This handling of fonts and encoding is experimental, and may be modified -in the future, if a better scheme (UniCode, or ISO-DIS 10646 1.2) is -available and standardized. -.Pp -.Sh EXAMPLE -The following is a sample bdf font skeleton which is accepted by -.Fn setfont . -It should not be misunderstood as an official description of the bdf font -format. -.Pp -.Bd -literal -COMMENT This is a comment and may occur anywhere -STARTFONT 2.1 ignored -FONT vga ignored -SIZE 16 75 75 first number used as box length -FONTBOUNDINGBOX 8 16 0 -4 first two numbers used as box size -STARTPROPERTIES 3 ignored -FONT_DESCENT 4 ignored -FONT_ASCENT 12 ignored -DEFAULT_CHAR 0 used to fill empty slots -ENDPROPERTIES used -CHARS 256 used -STARTCHAR C0000 ignored -ENCODING 0 used -SWIDTH 666 0 ignored -DWIDTH 8 0 ignored -BBX 8 16 0 -4 used -BITMAP used -00 bitmap must fit length and width -00 -00 -7c -c6 -c6 -de -de -de -dc -c0 -7c -00 -00 -00 -00 -ENDCHAR used -... Startchar-Endchar repeated -ENDFONT used -.Ed -.Pp -.Sh Files -.Bl -tag -width *.bdf -compact -.It Pa *.bdf -bdf font file -.El -.Sh SEE ALSO -.Xr co 4 , -.Xr keycap 5 -.Sh HISTORY -The bdf font format is part of the MIT X11 distribution. -.Sh BUGS -The problem of languages writing right-to-left is not yet handled. Currently, -words in such a language must be prepared as an inverse character string. diff --git a/usr.sbin/keymap/lib/whichcons.3 b/usr.sbin/keymap/lib/whichcons.3 deleted file mode 100644 index eacbe3e55e..0000000000 --- a/usr.sbin/keymap/lib/whichcons.3 +++ /dev/null @@ -1,68 +0,0 @@ -.\" Contributed to 386bsd 0.1 and later versions -.\" -.\" Copyright 1992 by Holger Veit -.\" May be freely used with Bill Jolitz's port of -.\" 386bsd and may be included in a 386bsd collection -.\" as long as binary and source are available and reproduce the above -.\" copyright. -.\" -.\" You may freely modify this code and contribute improvements based -.\" on this code as long as you don't claim to be the original author. -.\" Commercial use of this source requires permittance of the copyright -.\" holder. A general license for 386bsd will override this restriction. -.\" -.\" Use at your own risk. The copyright holder or any person who makes -.\" this code available for the public (administrators of public archives -.\" for instance) are not responsible for any harm to hardware or software -.\" that might happen due to wrong application or program faults. -.\" -.\" You must have the codriver driver in the same package generated -.\" into the 386bsd kernel, otherwise this program does not work. -.\" -.\" @(#)whichcons.3 1.0 (contributed to 386bsd) 01/10/93 -.\" -.Dd JAnuary 10, 1993 -.Dt CONSUTIL 3 -.Os 386bsd 0.2 -.Sh NAME -.Nm whichcons -.Nd determine the actual console type -.Sh SYNOPSIS -.Fn int whichcons() -.Sh DESCRIPTION -.Fn Whichcons -tries to identify the console driver built into the kernel. -Currently there are different, concurring console drivers with different -features. To allow an application program to adapt to -specific properties of a driver, -.Fn whichcons -can be used to find out the actual driver. -.Sh RETURN VALUES -On successful execution, -.Fn Whichcons -returns a positive integer, describing the detected driver. -The following codes are defined: -.Bl -tag -width 000 -compact -.It 0 -unknown driver -.It 1 -PCCONS driver (obsolete) -.It 2 -PCCONS driver, patched for X11 -.It 3 -CODRV driver, version 0.1.0 or 0.1.1 (obsolete) -.It 4 -CODRV driver, version 0.1.2 and later -.It 5 -PCCONS driver with improved X11 support -.El -.Sh SEE ALSO -.Xr co 4 -.Sh HISTORY -The -.Nm -The -.Fn whichcons -call is an intermediate feature and will exist as long there are -concurrent console driver version in the experimental 386bsd 0.X -operating system. diff --git a/usr.sbin/keymap/lib/whichcons.c b/usr.sbin/keymap/lib/whichcons.c deleted file mode 100644 index aad436c5e1..0000000000 --- a/usr.sbin/keymap/lib/whichcons.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Contributed to 386bsd 0.1 and later versions - * - * Copyright 1992 by Holger Veit - * May be freely used with Bill Jolitz's port of - * 386bsd and may be included in a 386bsd collection - * as long as binary and source are available and reproduce the above - * copyright. - * - * You may freely modify this code and contribute improvements based - * on this code as long as you don't claim to be the original author. - * Commercial use of this source requires permittance of the copyright - * holder. A general license for 386bsd will override this restriction. - * - * Use at your own risk. The copyright holder or any person who makes - * this code available for the public (administrators of public archives - * for instance) are not responsible for any harm to hardware or software - * that might happen due to wrong application or program faults. - * - * You must have the codrv-0.1.1 or later driver in the same package - * generated into the 386bsd kernel, otherwise this program does not work. - * - * @(#)whichcons.c 1.0 (386bsd contribution) 01/10/93 - */ - -#include "pathnames.h" -#include <sys/types.h> -#include <sys/ioctl.h> - -#define COMPAT_CO011 -#define COMPAT_PCCONS -#include <sys/ioctl_pc.h> - -/* - * find out, which console driver is there - * - * returns: - * -1: error - * 0: unknown - * 1: pccons.c - * 2: pccons with X support - * 3: codrv-0.1.1 - * 4: codrv-0.1.2 or later - * 5: Rich's version (hopefully) - */ -#define UNKNOWN 0 -#define PCCONS 1 -#define PCCONSX 2 -#define CODRV011 3 -#define CODRV01X 4 -#define PCCONSR 5 - -int whichcons() -{ - int fd; - struct consinfo ci; - struct oldconsinfo oci; - unsigned long *ip; - - fd = open(_PATH_KEYBOARD,0); - if (fd < 0) { - /* no /dev/kbd, can be pccons with or without X */ - fd = open(_PATH_CONSOLE,0); - if (fd < 0) return UNKNOWN; - } - - ip = 0; - /* try to get console info structure */ - if (ioctl(fd,CONSGINFO,&ci) >= 0) - ip = &ci.info1; - else if (ioctl(fd,OLDCONSGINFO,&oci) >= 0) - ip = &oci.info1; - if (ip) { - close(fd); - - /* good, this can be a new driver */ - if (*ip & CONS_ISPC) - return (*ip & CONS_HASOX386) ? PCCONSX : PCCONSR; - else if (*ip & CONS_ISCO) - return (*ip & CONS_CODRV2) ? CODRV01X : CODRV011; - else - /* who dared to use these fields */ - return UNKNOWN; - } - - /* check whether console X MODE exists */ - if (ioctl(fd,CONSOLE_X_MODE_OFF,0) < 0) { - close(fd); - return PCCONS; - } - - close(fd); - return PCCONSX; -} - diff --git a/usr.sbin/keymap/lib/xc_fns.c b/usr.sbin/keymap/lib/xc_fns.c deleted file mode 100644 index c4723580fd..0000000000 --- a/usr.sbin/keymap/lib/xc_fns.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Contributed to 386bsd 0.1 and later versions - * - * Copyright 1992 by Holger Veit - * May be freely used with Bill Jolitz's port of - * 386bsd and may be included in a 386bsd collection - * as long as binary and source are available and reproduce the above - * copyright. - * - * You may freely modify this code and contribute improvements based - * on this code as long as you don't claim to be the original author. - * Commercial use of this source requires permittance of the copyright - * holder. A general license for 386bsd will override this restriction. - * - * Use at your own risk. The copyright holder or any person who makes - * this code available for the public (administrators of public archives - * for instance) are not responsible for any harm to hardware or software - * that might happen due to wrong application or program faults. - * - * You must have the codrv-0.1.1 or later driver in the same package - * generated into the 386bsd kernel, otherwise this program does not work. - * - * @(#)xc_fns.c 1.0 (386bsd contribution) 01/10/93 - */ - -/* these are EXPERIMENTAL routines to replace the standard str* functions - * These functions work with the XCHAR data type. - * note that still the problem of the NULL byte exists - * - * This is not intended to be a proposal for multibyte characters. - * - */ -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/ioctl_pc.h> - - -XCHAR *XC_strcpy(XCHAR *t,XCHAR *f) -{ - XCHAR *p = t; - while (*t++ = *f++); - return p; -} - -XCHAR *XC_strncpy(XCHAR *t,XCHAR *f,int n) -{ - XCHAR *p = t; - while (n>0) { *t++ = *f++, n--; } - return p; -} - -int XC_strlen(XCHAR *s) -{ - int i = 0; - while (!*s++) i++; - return i; -} - -XCHAR *XC_char2XCHAR(XCHAR *t,char *f,int n) -{ - while (n>=0) { *t++ = (XCHAR)*f; f++; n--; } - - return t; -} - -XCHAR *XC_strcat(XCHAR *t,XCHAR *f) -{ - XCHAR *p = t; - - while (!*t) t++; - while (*t++ = *f++); - return p; -} diff --git a/usr.sbin/kgmon/Makefile b/usr.sbin/kgmon/Makefile index 3e8d00f03c..341fb6d6cc 100644 --- a/usr.sbin/kgmon/Makefile +++ b/usr.sbin/kgmon/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= kgmon -MAN8= kgmon.0 +MAN8= kgmon.8 .include <bsd.prog.mk> diff --git a/usr.sbin/kgmon/kgmon.8 b/usr.sbin/kgmon/kgmon.8 index 0378cd533d..3ced5369b5 100644 --- a/usr.sbin/kgmon/kgmon.8 +++ b/usr.sbin/kgmon/kgmon.8 @@ -82,7 +82,7 @@ Extract values associated with the name list from the specified core instead of the default ``/dev/kmem''. .It Fl N Extract the name list from the specified system instead of the -default ``/vmunix''. +default ``/386bsd''. .El .Pp If neither @@ -98,7 +98,7 @@ the operating system profile buffers will be dumped, and profiling will be immediately resumed. .Sh FILES .Bl -tag -width /dev/kmemx -compact -.It Pa /vmunix +.It Pa /386bsd the default system .It Pa /dev/kmem the default memory diff --git a/usr.sbin/kvm_mkdb/Makefile b/usr.sbin/kvm_mkdb/Makefile index 51dff35eb8..f87a60fb79 100644 --- a/usr.sbin/kvm_mkdb/Makefile +++ b/usr.sbin/kvm_mkdb/Makefile @@ -1,7 +1,8 @@ -# @(#)Makefile 5.1 (Berkeley) 2/12/91 +# from: @(#)Makefile 5.1 (Berkeley) 2/12/91 +# $Id$ PROG= kvm_mkdb SRCS= kvm_mkdb.c nlist.c -MAN8= kvm_mkdb.0 +MAN8= kvm_mkdb.8 .include <bsd.prog.mk> diff --git a/usr.sbin/kvm_mkdb/kvm_mkdb.8 b/usr.sbin/kvm_mkdb/kvm_mkdb.8 index 00644b44d2..00c298b24f 100644 --- a/usr.sbin/kvm_mkdb/kvm_mkdb.8 +++ b/usr.sbin/kvm_mkdb/kvm_mkdb.8 @@ -29,7 +29,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)kvm_mkdb.8 5.2 (Berkeley) 3/16/91 +.\" from: @(#)kvm_mkdb.8 5.2 (Berkeley) 3/16/91 +.\" $Id$ .\" .Dd March 16, 1991 .Dt KVM_MKDB 8 @@ -44,7 +45,7 @@ creates a database in .Pa /var/run containing information about -.Pa /vmunix +.Pa /386bsd and perhaps other fairly static information about the current system. Various library routines consult this database. @@ -53,9 +54,10 @@ used by the .Xr kvm_nlist 3 function. .Sh FILES -.Bl -tag -width /var/run/kvm_vmunix.{dir,pag} -compact -.It Pa /vmunix -.It Pa /var/run/kvm_vmunix.{dir,pag} +.Bl -tag -width /var/run/kvm_386bsd.db -compact +.It Pa /386bsd +default system namelist +.It Pa /var/run/kvm_386bsd.db .El .Sh SEE ALSO .Xr kvm_nlist 3 diff --git a/usr.sbin/kvm_mkdb/kvm_mkdb.c b/usr.sbin/kvm_mkdb/kvm_mkdb.c index ff6fa1cdf2..7f7ca4e06b 100644 --- a/usr.sbin/kvm_mkdb/kvm_mkdb.c +++ b/usr.sbin/kvm_mkdb/kvm_mkdb.c @@ -38,7 +38,8 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)kvm_mkdb.c 5.11 (Berkeley) 4/27/91"; +/*static char sccsid[] = "from: @(#)kvm_mkdb.c 5.11 (Berkeley) 4/27/91";*/ +static char rcsid[] = "$Id"; #endif /* not lint */ #include <sys/param.h> @@ -71,14 +72,14 @@ main(argc, argv) argc -= optind; argv += optind; - nlistpath = argc > 1 ? argv[0] : _PATH_UNIX; + nlistpath = argc > 0 ? argv[0] : _PATH_UNIX; nlistname = basename(nlistpath); (void)sprintf(dbtemp, "%s/kvm_%s.tmp", _PATH_VARRUN, nlistname); (void)sprintf(dbname, "%s/kvm_%s.db", _PATH_VARRUN, nlistname); (void)umask(0); - db = hash_open(dbtemp, O_CREAT|O_WRONLY|O_EXCL, - S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, NULL); + db = dbopen(dbtemp, O_CREAT|O_RDWR|O_EXCL, + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, DB_HASH, NULL); if (!db) { (void)fprintf(stderr, "kvm_mkdb: %s: %s\n", dbtemp, strerror(errno)); diff --git a/usr.sbin/kvm_mkdb/nlist.c b/usr.sbin/kvm_mkdb/nlist.c index aa4097977e..f2ee8a1999 100644 --- a/usr.sbin/kvm_mkdb/nlist.c +++ b/usr.sbin/kvm_mkdb/nlist.c @@ -29,17 +29,11 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00032 - * -------------------- ----- ---------------------- - * - * 05 Aug 92 David Greenman Fix kernel namelist db create/use */ #ifndef lint -static char sccsid[] = "@(#)nlist.c 5.4 (Berkeley) 4/27/91"; +/*static char sccsid[] = "from: @(#)nlist.c 5.4 (Berkeley) 4/27/91";*/ +static char rcsid[] = "$Id"; #endif /* not lint */ #include <sys/param.h> @@ -146,7 +140,7 @@ create_knlist(name, db) rel_off = nbuf.n_value & ~KERNBASE; #endif #ifdef i386 - rel_off = ((nbuf.n_value & ~KERNBASE) + CLBYTES); + rel_off = nbuf.n_value - ebuf.a_entry + CLBYTES; #endif /* * When loaded, data is rounded to next page cluster @@ -197,6 +191,6 @@ badfmt(p) char *p; { (void)fprintf(stderr, - "symorder: %s: %s: %s\n", kfile, p, strerror(EFTYPE)); + "kvm_mkdb: %s: %s: %s\n", kfile, p, strerror(EFTYPE)); exit(1); } diff --git a/usr.sbin/lpr/lpc/Makefile b/usr.sbin/lpr/lpc/Makefile index ba81b3273d..68a9ec78ee 100644 --- a/usr.sbin/lpr/lpc/Makefile +++ b/usr.sbin/lpr/lpc/Makefile @@ -2,7 +2,7 @@ PROG= lpc CFLAGS+=-I${.CURDIR}/../common_source -MAN8= lpc.0 +MAN8= lpc.8 SRCS= lpc.c cmds.c cmdtab.c startdaemon.c common.c printcap.c BINGRP= daemon BINMODE=2555 diff --git a/usr.sbin/lpr/lpd/Makefile b/usr.sbin/lpr/lpd/Makefile index af796d79c9..745bb06f74 100644 --- a/usr.sbin/lpr/lpd/Makefile +++ b/usr.sbin/lpr/lpd/Makefile @@ -3,7 +3,7 @@ PROG= lpd CFLAGS+=-I${.CURDIR}/../common_source LDADD= -lutil -MAN8= lpd.0 +MAN8= lpd.8 SRCS= lpd.c printjob.c recvjob.c displayq.c rmjob.c startdaemon.c \ lpdchar.c common.c printcap.c .PATH: ${.CURDIR}/../common_source diff --git a/usr.sbin/lpr/lpd/printjob.c b/usr.sbin/lpr/lpd/printjob.c index 8805fc7bd2..716224c83b 100644 --- a/usr.sbin/lpr/lpd/printjob.c +++ b/usr.sbin/lpr/lpd/printjob.c @@ -265,9 +265,15 @@ printit(file) */ for (i = 0; i < 4; i++) strcpy(fonts[i], ifonts[i]); +/* + * XXX + * I don't know why these are here. They screw up the printcap + * initializations already done by init() -- EWS + * + strcpy(width+2, "0"); strcpy(indent+2, "0"); - + */ /* * read the control file for work to do * @@ -481,9 +487,10 @@ print(format, file) av[0] = "pr"; av[1] = width; av[2] = length; - av[3] = "-h"; - av[4] = *title ? title : " "; - av[5] = 0; + av[3] = "-f"; /* XXX use form feeds */ + av[4] = "-h"; + av[5] = *title ? title : " "; + av[6] = 0; fo = ofd; goto start; } @@ -494,7 +501,7 @@ print(format, file) for (n = 3; n < NOFILE; n++) (void) close(n); execl(_PATH_PR, "pr", width, length, - "-h", *title ? title : " ", 0); + "-f", "-h", *title ? title : " ", 0); syslog(LOG_ERR, "cannot execl %s", _PATH_PR); exit(2); } diff --git a/usr.sbin/lpr/pac/Makefile b/usr.sbin/lpr/pac/Makefile index 079b67f432..586995a933 100644 --- a/usr.sbin/lpr/pac/Makefile +++ b/usr.sbin/lpr/pac/Makefile @@ -2,7 +2,7 @@ PROG= pac CFLAGS+=-I${.CURDIR}/../common_source -MAN8= pac.0 +MAN8= pac.8 SRCS= pac.c printcap.c .PATH: ${.CURDIR}/../common_source diff --git a/usr.sbin/mtree/Makefile b/usr.sbin/mtree/Makefile index 4fdee69920..176dbea76a 100644 --- a/usr.sbin/mtree/Makefile +++ b/usr.sbin/mtree/Makefile @@ -1,7 +1,8 @@ -# @(#)Makefile 5.4 (Berkeley) 5/25/90 +# @(#)Makefile 5.6 (Berkeley) 2/19/92 PROG= mtree -SRCS= compare.c create.c mtree.c spec.c verify.c -.PATH: ${.CURDIR} +SRCS= compare.c crc.c create.c misc.c mtree.c spec.c verify.c +MAN8= mtree.8 +.PATH: ${.CURDIR}/../../usr.bin/cksum .include <bsd.prog.mk> diff --git a/usr.sbin/mtree/compare.c b/usr.sbin/mtree/compare.c index 6de8e14603..f33695a08c 100644 --- a/usr.sbin/mtree/compare.c +++ b/usr.sbin/mtree/compare.c @@ -32,172 +32,220 @@ */ #ifndef lint -static char sccsid[] = "@(#)compare.c 5.7 (Berkeley) 5/25/90"; +static char sccsid[] = "@(#)compare.c 5.10 (Berkeley) 3/31/92"; #endif /* not lint */ #include <sys/param.h> #include <sys/stat.h> +#include <fcntl.h> #include <fts.h> #include <errno.h> #include <stdio.h> #include <time.h> +#include <unistd.h> #include "mtree.h" +#include "extern.h" +extern int uflag; + +static char *ftype __P((u_int)); + +#define INDENTNAMELEN 8 #define LABEL \ - if (!label++) \ - (void)printf("%s: ", RP(p)); \ + if (!label++) { \ + len = printf("%s: ", RP(p)); \ + if (len > INDENTNAMELEN) { \ + tab = "\t"; \ + (void)printf("\n"); \ + } else { \ + tab = ""; \ + (void)printf("%*s", INDENTNAMELEN - len, ""); \ + } \ + } +int compare(name, s, p) char *name; register NODE *s; register FTSENT *p; { - extern int exitval, uflag; - int label; - char *ftype(), *inotype(), *rlink(); + extern int uflag; + u_long len, val; + int fd, label; + char *cp, *tab; label = 0; switch(s->type) { case F_BLOCK: - if (!S_ISBLK(p->fts_statb.st_mode)) + if (!S_ISBLK(p->fts_statp->st_mode)) goto typeerr; break; case F_CHAR: - if (!S_ISCHR(p->fts_statb.st_mode)) + if (!S_ISCHR(p->fts_statp->st_mode)) goto typeerr; break; case F_DIR: - if (!S_ISDIR(p->fts_statb.st_mode)) + if (!S_ISDIR(p->fts_statp->st_mode)) goto typeerr; break; case F_FIFO: - if (!S_ISFIFO(p->fts_statb.st_mode)) + if (!S_ISFIFO(p->fts_statp->st_mode)) goto typeerr; break; case F_FILE: - if (!S_ISREG(p->fts_statb.st_mode)) + if (!S_ISREG(p->fts_statp->st_mode)) goto typeerr; break; case F_LINK: - if (!S_ISLNK(p->fts_statb.st_mode)) + if (!S_ISLNK(p->fts_statp->st_mode)) goto typeerr; break; case F_SOCK: - if (!S_ISFIFO(p->fts_statb.st_mode)) { + if (!S_ISSOCK(p->fts_statp->st_mode)) { typeerr: LABEL; - (void)printf("\n\ttype (%s, %s)", - ftype(s->type), inotype(p->fts_statb.st_mode)); + (void)printf("\ttype (%s, %s)\n", + ftype(s->type), inotype(p->fts_statp->st_mode)); } break; } - if (s->flags & F_MODE && s->st_mode != (p->fts_statb.st_mode & MBITS)) { + /* Set the uid/gid first, then set the mode. */ + if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) { LABEL; - (void)printf("\n\tpermissions (%#o, %#o%s", - s->st_mode, p->fts_statb.st_mode & MBITS, uflag ? "" : ")"); + (void)printf("%suser (%u, %u", + tab, s->st_uid, p->fts_statp->st_uid); if (uflag) - if (chmod(p->fts_accpath, s->st_mode)) - (void)printf(", not modified: %s)", + if (chown(p->fts_accpath, s->st_uid, -1)) + (void)printf(", not modified: %s)\n", strerror(errno)); else - (void)printf(", modified)"); + (void)printf(", modified)\n"); + else + (void)printf(")\n"); + tab = "\t"; } - if (s->flags & F_OWNER && s->st_uid != p->fts_statb.st_uid) { + if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) { LABEL; - (void)printf("\n\towner (%u, %u%s", - s->st_uid, p->fts_statb.st_uid, uflag ? "" : ")"); + (void)printf("%sgid (%u, %u", + tab, s->st_gid, p->fts_statp->st_gid); if (uflag) - if (chown(p->fts_accpath, s->st_uid, -1)) + if (chown(p->fts_accpath, -1, s->st_gid)) (void)printf(", not modified: %s)", strerror(errno)); else (void)printf(", modified)"); + else + (void)printf(")\n"); + tab = "\t"; } - if (s->flags & F_GROUP && s->st_gid != p->fts_statb.st_gid) { + if (s->flags & F_MODE && + s->st_mode != (p->fts_statp->st_mode & MBITS)) { LABEL; - (void)printf("\n\tgroup (%u, %u%s", - s->st_gid, p->fts_statb.st_gid, uflag ? "" : ")"); + (void)printf("%spermissions (%#o, %#o", + tab, s->st_mode, p->fts_statp->st_mode & MBITS); if (uflag) - if (chown(p->fts_accpath, -1, s->st_gid)) + if (chmod(p->fts_accpath, s->st_mode)) (void)printf(", not modified: %s)", strerror(errno)); else (void)printf(", modified)"); + else + (void)printf(")\n"); + tab = "\t"; } if (s->flags & F_NLINK && s->type != F_DIR && - s->st_nlink != p->fts_statb.st_nlink) { + s->st_nlink != p->fts_statp->st_nlink) { LABEL; - (void)printf("\n\tlink count (%u, %u)", - s->st_nlink, p->fts_statb.st_nlink); + (void)printf("%slink count (%u, %u)\n", + tab, s->st_nlink, p->fts_statp->st_nlink); + tab = "\t"; } - if (s->flags & F_SIZE && s->st_size != p->fts_statb.st_size) { + if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) { LABEL; - (void)printf("\n\tsize (%ld, %ld)", - s->st_size, p->fts_statb.st_size); + (void)printf("%ssize (%ld, %ld)\n", + tab, s->st_size, p->fts_statp->st_size); + tab = "\t"; } - if (s->flags & F_SLINK) { - char *cp; - - if (strcmp(cp = rlink(name), s->slink)) { + if (s->flags & F_TIME && s->st_mtime != p->fts_statp->st_mtime) { + LABEL; + (void)printf("%smodification time (%.24s, ", + tab, ctime(&s->st_mtime)); + (void)printf("%.24s)\n", ctime(&p->fts_statp->st_mtime)); + tab = "\t"; + } + if (s->flags & F_CKSUM) + if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) { LABEL; - (void)printf("\n\tlink ref (%s, %s)", cp, s->slink); + (void)printf("%scksum: %s: %s\n", + tab, p->fts_accpath, strerror(errno)); + tab = "\t"; + } else if (crc(fd, &val, &len)) { + (void)close(fd); + LABEL; + (void)printf("%scksum: %s: %s\n", + tab, p->fts_accpath, strerror(errno)); + tab = "\t"; + } else { + (void)close(fd); + if (s->cksum != val) { + LABEL; + (void)printf("%scksum (%lu, %lu)\n", + tab, s->cksum, val); + } + tab = "\t"; } - } - if (s->flags & F_TIME && s->st_mtime != p->fts_statb.st_mtime) { + if (s->flags & F_SLINK && strcmp(cp = rlink(name), s->slink)) { LABEL; - (void)printf("\n\tmodification time (%.24s, ", - ctime(&s->st_mtime)); - (void)printf("%.24s)", ctime(&p->fts_statb.st_mtime)); - } - if (label) { - exitval = 2; - putchar('\n'); + (void)printf("%slink ref (%s, %s)\n", tab, cp, s->slink); } + return (label); } char * inotype(type) - mode_t type; + u_int type; { switch(type & S_IFMT) { case S_IFBLK: - return("block"); + return ("block"); case S_IFCHR: - return("char"); + return ("char"); case S_IFDIR: - return("dir"); + return ("dir"); + case S_IFIFO: + return ("fifo"); case S_IFREG: - return("file"); + return ("file"); case S_IFLNK: - return("link"); + return ("link"); case S_IFSOCK: - return("socket"); + return ("socket"); default: - return("unknown"); + return ("unknown"); } /* NOTREACHED */ } -char * +static char * ftype(type) u_int type; { switch(type) { case F_BLOCK: - return("block"); + return ("block"); case F_CHAR: - return("char"); + return ("char"); case F_DIR: - return("dir"); + return ("dir"); case F_FIFO: - return("fifo"); + return ("fifo"); case F_FILE: - return("file"); + return ("file"); case F_LINK: - return("link"); + return ("link"); case F_SOCK: - return("socket"); + return ("socket"); default: - return("unknown"); + return ("unknown"); } /* NOTREACHED */ } @@ -206,15 +254,11 @@ char * rlink(name) char *name; { - register int len; static char lbuf[MAXPATHLEN]; + register int len; - len = readlink(name, lbuf, sizeof(lbuf)); - if (len == -1) { - (void)fprintf(stderr, "mtree: %s: %s.\n", - name, strerror(errno)); - exit(1); - } + if ((len = readlink(name, lbuf, sizeof(lbuf))) == -1) + err("%s: %s", name, strerror(errno)); lbuf[len] = '\0'; - return(lbuf); + return (lbuf); } diff --git a/usr.sbin/mtree/create.c b/usr.sbin/mtree/create.c index 649da1fd0a..ec21de6f6d 100644 --- a/usr.sbin/mtree/create.c +++ b/usr.sbin/mtree/create.c @@ -32,203 +32,258 @@ */ #ifndef lint -static char sccsid[] = "@(#)create.c 5.16 (Berkeley) 3/12/91"; +static char sccsid[] = "@(#)create.c 5.19 (Berkeley) 3/2/92"; #endif /* not lint */ #include <sys/param.h> #include <sys/stat.h> #include <time.h> +#include <fcntl.h> #include <fts.h> #include <dirent.h> +#include <grp.h> +#include <pwd.h> #include <errno.h> +#include <unistd.h> #include <stdio.h> #include "mtree.h" +#include "extern.h" -#define LABEL \ - if (label++) \ - (void)putchar(' '); \ +#define INDENTNAMELEN 15 +#define MAXLINELEN 80 -int ftsoptions = FTS_PHYSICAL; +extern int crc_total, ftsoptions; +extern int dflag, sflag; +extern u_short keys; +extern char fullpath[MAXPATHLEN]; +static gid_t gid; +static uid_t uid; +static mode_t mode; + +static int dsort __P((const FTSENT **, const FTSENT **)); +static void output __P((int *, const char *, ...)); +static int statd __P((FTS *, FTSENT *, uid_t *, gid_t *, mode_t *)); +static void statf __P((FTSENT *)); + +void cwalk() { - extern int dflag; register FTS *t; register FTSENT *p; - register int cnt, label, notset; time_t clock; - uid_t uid; - gid_t gid; - mode_t mode; - int tabs, dsort(); - char *argv[2]; - char curp[MAXPATHLEN], *inotype(), *getlogin(), *rlink(); - - if (!getwd(curp)) { - (void)fprintf(stderr, "mtree: %s\n", curp); - exit(1); - } + char *argv[2], host[MAXHOSTNAMELEN]; + (void)time(&clock); - (void)printf("#\t fs: %s\n#\t by: %s\n#\tdate: %s\n", - curp, getlogin(), ctime(&clock)); + (void)gethostname(host, sizeof(host)); + (void)printf( + "#\t user: %s\n#\tmachine: %s\n#\t tree: %s\n#\t date: %s", + getlogin(), host, fullpath, ctime(&clock)); argv[0] = "."; - argv[1] = (char *)NULL; - if (!(t = fts_open(argv, ftsoptions, dsort))) { - (void)fprintf(stderr, - "mtree: fts_open: %s.\n", strerror(errno)); - exit(1); - } - while (p = fts_read(t)) { + argv[1] = NULL; + if ((t = fts_open(argv, ftsoptions, dsort)) == NULL) + err("fts_open: %s", strerror(errno)); + while (p = fts_read(t)) switch(p->fts_info) { case FTS_D: - if (dflag) - notset = 1; - else - notset = - statdir(t, p, &uid, &gid, &mode, &tabs); - if (!strcmp(p->fts_name, ".")) - continue; + (void)printf("\n# %s\n", p->fts_path); + statd(t, p, &uid, &gid, &mode); + statf(p); break; case FTS_DP: - if (p->fts_level <= 0) - continue; - for (cnt = p->fts_level - 1; cnt-- > 0; ) - (void)putchar('\t'); - (void)printf("..\n"); - continue; + if (p->fts_level > 0) + (void)printf("# %s\n..\n\n", p->fts_path); + break; case FTS_DNR: case FTS_ERR: case FTS_NS: - (void)fprintf(stderr, "mtree: %s: %s.\n", - p->fts_path, strerror(errno)); - continue; + (void)fprintf(stderr, + "mtree: %s: %s\n", p->fts_path, strerror(errno)); + break; default: - if (dflag) - continue; + if (!dflag) + statf(p); + break; + } + (void)fts_close(t); + if (sflag && keys & F_CKSUM) + (void)fprintf(stderr, + "mtree: %s checksum: %lu\n", fullpath, crc_total); +} - for (cnt = p->fts_level - 1; cnt-- > 0; ) - (void)putchar('\t'); - (void)printf("%s", p->fts_name); - if (p->fts_info == FTS_D) - (void)putchar('\t'); - else { - if (tabs > 1 && p->fts_namelen < 8) - (void)putchar('\t'); - (void)putchar('\t'); - } +static void +statf(p) + FTSENT *p; +{ + struct group *gr; + struct passwd *pw; + u_long len, val; + int fd, indent; - label = 0; - if (!S_ISREG(p->fts_statb.st_mode) || notset) { - LABEL; - (void)printf("type=%s", inotype(p->fts_statb.st_mode)); - } - if (p->fts_statb.st_uid != uid || notset) { - LABEL; - (void)printf("owner=%u", p->fts_statb.st_uid); - } - if (p->fts_statb.st_gid != gid || notset) { - LABEL; - (void)printf("group=%u", p->fts_statb.st_gid); - } - if ((p->fts_statb.st_mode & MBITS) != mode || notset) { - LABEL; - (void)printf("mode=%#o", p->fts_statb.st_mode & MBITS); - } - if (p->fts_statb.st_nlink != 1 || notset) { - LABEL; - (void)printf("nlink=%u", p->fts_statb.st_nlink); - } - LABEL; - (void)printf("size=%ld", p->fts_statb.st_size); - LABEL; - (void)printf("time=%ld", p->fts_statb.st_mtime); - - if (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE) { - LABEL; - (void)printf("link=%s", rlink(p->fts_accpath)); - } - (void)putchar('\n'); + if (S_ISDIR(p->fts_statp->st_mode)) + indent = printf("%s", p->fts_name); + else + indent = printf(" %s", p->fts_name); + + if (indent > INDENTNAMELEN) + indent = MAXLINELEN; + else + indent += printf("%*s", INDENTNAMELEN - indent, ""); + + if (!S_ISREG(p->fts_statp->st_mode)) + output(&indent, "type=%s", inotype(p->fts_statp->st_mode)); + if (keys & (F_UID | F_UNAME) && p->fts_statp->st_uid != uid) + if (keys & F_UNAME && (pw = getpwuid(p->fts_statp->st_uid))) + output(&indent, "uname=%s", pw->pw_name); + else /* if (keys & F_UID) */ + output(&indent, "uid=%u", p->fts_statp->st_uid); + if (keys & (F_GID | F_GNAME) && p->fts_statp->st_gid != gid) + if (keys & F_GNAME && (gr = getgrgid(p->fts_statp->st_gid))) + output(&indent, "gid=%s", gr->gr_name); + else /* if (keys & F_GID) */ + output(&indent, "gid=%u", p->fts_statp->st_gid); + if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode) + output(&indent, "mode=%#o", p->fts_statp->st_mode & MBITS); + if (keys & F_NLINK && p->fts_statp->st_nlink != 1) + output(&indent, "nlink=%u", p->fts_statp->st_nlink); + if (keys & F_SIZE) + output(&indent, "size=%ld", p->fts_statp->st_size); + if (keys & F_TIME) + output(&indent, "time=%ld", p->fts_statp->st_mtime); + if (keys & F_CKSUM && S_ISREG(p->fts_statp->st_mode)) { + if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0 || + crc(fd, &val, &len)) + err("%s: %s", p->fts_accpath, strerror(errno)); + (void)close(fd); + output(&indent, "cksum=%lu", val); } - (void)fts_close(t); + if (keys & F_SLINK && + (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) + output(&indent, "link=%s", rlink(p->fts_accpath)); + (void)putchar('\n'); } #define MAXGID 5000 #define MAXUID 5000 #define MAXMODE MBITS + 1 -statdir(t, parent, puid, pgid, pmode, tabs) +static int +statd(t, parent, puid, pgid, pmode) FTS *t; FTSENT *parent; uid_t *puid; gid_t *pgid; mode_t *pmode; - int *tabs; { register FTSENT *p; - register gid_t gid; - register uid_t uid; - register mode_t mode; + register gid_t sgid; + register uid_t suid; + register mode_t smode; + struct group *gr; + struct passwd *pw; gid_t savegid; uid_t saveuid; mode_t savemode; u_short maxgid, maxuid, maxmode, g[MAXGID], u[MAXUID], m[MAXMODE]; - if (!(p = fts_children(t))) { - if (errno) { - (void)fprintf(stderr, "mtree: %s: %s.\n", - RP(parent), strerror(errno)); - exit(1); - } - return(1); + if ((p = fts_children(t, 0)) == NULL) { + if (errno) + err("%s: %s", RP(parent), strerror(errno)); + return (1); } bzero(g, sizeof(g)); bzero(u, sizeof(u)); bzero(m, sizeof(m)); - *tabs = 1; maxuid = maxgid = maxmode = 0; for (; p; p = p->fts_link) { - mode = p->fts_statb.st_mode & MBITS; - if (mode < MAXMODE && ++m[mode] > maxmode) { - savemode = mode; - maxmode = m[mode]; + smode = p->fts_statp->st_mode & MBITS; + if (smode < MAXMODE && ++m[smode] > maxmode) { + savemode = smode; + maxmode = m[smode]; } - gid = p->fts_statb.st_gid; - if (gid < MAXGID && ++g[gid] > maxgid) { - savegid = gid; - maxgid = g[gid]; + sgid = p->fts_statp->st_gid; + if (sgid < MAXGID && ++g[sgid] > maxgid) { + savegid = sgid; + maxgid = g[sgid]; } - uid = p->fts_statb.st_uid; - if (uid < MAXUID && ++u[uid] > maxuid) { - saveuid = uid; - maxuid = u[uid]; + suid = p->fts_statp->st_uid; + if (suid < MAXUID && ++u[suid] > maxuid) { + saveuid = suid; + maxuid = u[suid]; } - if (p->fts_namelen > 7) - *tabs = 2; } - (void)printf("\n/set group=%u mode=%#o nlink=1 owner=%u type=file\n", - savegid, savemode, saveuid); + (void)printf("/set type=file"); + if (keys & F_GID) + (void)printf(" gid=%u", savegid); + if (keys & F_GNAME) + if ((gr = getgrgid(savegid)) != NULL) + (void)printf(" gname=%s", gr->gr_name); + else + (void)printf(" gid=%u", savegid); + if (keys & F_UNAME) + if ((pw = getpwuid(saveuid)) != NULL) + (void)printf(" uname=%s", pw->pw_name); + else + (void)printf(" uid=%u", saveuid); + if (keys & F_UID) + (void)printf(" uid=%u", saveuid); + if (keys & F_MODE) + (void)printf(" mode=%#o", savemode); + if (keys & F_NLINK) + (void)printf(" nlink=1"); + (void)printf("\n"); *puid = saveuid; *pgid = savegid; *pmode = savemode; - return(0); + return (0); } -dsort(p1, p2) - FTSENT **p1, **p2; +static int +dsort(a, b) + const FTSENT **a, **b; { - register FTSENT *a, *b; + if (S_ISDIR((*a)->fts_statp->st_mode)) { + if (!S_ISDIR((*b)->fts_statp->st_mode)) + return (1); + } else if (S_ISDIR((*b)->fts_statp->st_mode)) + return (-1); + return (strcmp((*a)->fts_name, (*b)->fts_name)); +} - a = *p1; - b = *p2; +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif - if (S_ISDIR(a->fts_statb.st_mode)) { - if (!S_ISDIR(b->fts_statb.st_mode)) - return(1); - } else if (S_ISDIR(b->fts_statb.st_mode)) - return(-1); - return(strcmp(a->fts_name, b->fts_name)); +void +#if __STDC__ +output(int *offset, const char *fmt, ...) +#else +output(offset, fmt, va_alist) + int *offset; + char *fmt; + va_dcl +#endif +{ + va_list ap; + int len; + char buf[1024]; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + len = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + if (*offset + len > MAXLINELEN - 3) { + (void)printf(" \\\n%*s", INDENTNAMELEN, ""); + *offset = INDENTNAMELEN; + } + *offset += printf(" %s", buf) + 1; } diff --git a/usr.sbin/mtree/extern.h b/usr.sbin/mtree/extern.h new file mode 100644 index 0000000000..1593165aec --- /dev/null +++ b/usr.sbin/mtree/extern.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 5.2 (Berkeley) 4/17/92 + */ + +int compare __P((char *, NODE *, FTSENT *)); +int crc __P((int, u_long *, u_long *)); +void cwalk __P((void)); +void err __P((const char *, ...)); +char *inotype __P((u_int)); +u_int parsekey __P((char *, int *)); +char *rlink __P((char *)); +NODE *spec __P((void)); +int verify __P((void)); diff --git a/usr.sbin/mtree/misc.c b/usr.sbin/mtree/misc.c new file mode 100644 index 0000000000..4546991e47 --- /dev/null +++ b/usr.sbin/mtree/misc.c @@ -0,0 +1,125 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)misc.c 5.2 (Berkeley) 4/17/92 + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fts.h> +#include <stdio.h> +#include "mtree.h" +#include "extern.h" + +extern int lineno; + +typedef struct _key { + char *name; /* key name */ + u_int val; /* value */ + +#define NEEDVALUE 0x01 + u_int flags; +} KEY; + +/* NB: the following table must be sorted lexically. */ +static KEY keylist[] = { + "cksum", F_CKSUM, NEEDVALUE, + "gid", F_GID, NEEDVALUE, + "gname", F_GNAME, NEEDVALUE, + "ignore", F_IGN, 0, + "link", F_SLINK, NEEDVALUE, + "mode", F_MODE, NEEDVALUE, + "nlink", F_NLINK, NEEDVALUE, + "size", F_SIZE, NEEDVALUE, + "time", F_TIME, NEEDVALUE, + "type", F_TYPE, NEEDVALUE, + "uid", F_UID, NEEDVALUE, + "uname", F_UNAME, NEEDVALUE, +}; + +u_int +parsekey(name, needvaluep) + char *name; + int *needvaluep; +{ + KEY *k, tmp; + int keycompare __P((const void *, const void *)); + + tmp.name = name; + k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY), + sizeof(KEY), keycompare); + if (k == NULL) + err("unknown keyword %s", name); + + if (needvaluep) + *needvaluep = k->flags & NEEDVALUE ? 1 : 0; + return (k->val); +} + +int +keycompare(a, b) + const void *a, *b; +{ + return (strcmp(((KEY *)a)->name, ((KEY *)b)->name)); +} + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "mtree: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + if (lineno) + (void)fprintf(stderr, + "mtree: failed at line %d of the specification\n", lineno); + exit(1); + /* NOTREACHED */ +} diff --git a/usr.sbin/mtree/mtree.1 b/usr.sbin/mtree/mtree.1 deleted file mode 100644 index 64fbb42e0b..0000000000 --- a/usr.sbin/mtree/mtree.1 +++ /dev/null @@ -1,227 +0,0 @@ -.\" Copyright (c) 1989, 1990 The Regents of the University of California. -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)mtree.1 5.10 (Berkeley) 7/30/91 -.\" -.Dd July 30, 1991 -.Dt MTREE 8 -.Os -.Sh NAME -.Nm mtree -.Nd map a directory hierarchy -.Sh SYNOPSIS -.Nm mtree -.Op Fl cderux -.Op Fl f Ar spec -.Op Fl p Ar path -.Sh DESCRIPTION -The utility -.Nm mtree -compares a directory hierarchy against a specification for a -directory hierarchy. -By default, the specification is read from the standard input. -.Nm Mtree -verifies that the tree rooted in the current directory matches the -specification. -.Pp -Messages are written to standard output for any files whose -characteristics do not match those of the specification, or which are -missing from either the specification or the tree. -.Pp -The options are as follows: -.Bl -tag -width flag -.It Fl c -Print a specification for the tree to standard output. -.It Fl d -Ignore everything except directory type files. -.It Fl e -Don't object to files that are in the tree but not in the specification. -.It Fl f -Read the specification from -.Ar file , -instead of from standard input. -.It Fl p -Traverse the tree rooted in -.Ar path , -instead of the current directory. -.It Fl r -Remove any files in the tree that are not described in the -specification. -.It Fl u -Modify the owner, group, and permissions of existing files to match -the specification, as well as create any missing directories. -Owner, group, and permissions must all be specified for missing -directories to be created. -.It Fl x -Don't descend below any mount points. -.El -.Pp -Specifications are mostly composed of ``keywords'', i.e. strings that -that specify values relating to files. -No keywords have default values, and if a keyword has no set value no -checks based on it are performed. -.Pp -Currently supported keywords are as follows: -.Bl -tag -width Cm -.It Cm cksum -The checksum of the file using the algorithm specified by -the program -.Xr cksum 1 . -.It Cm ignore -Causes the hierarchy below the file to be ignored. -.It Cm group -The group of the file; may be either numeric or symbolic. -.It Cm mode -The current file's permissions as an absolute (octal) or symbolic -value (see -.Xr chmod 1 ) . -.It Cm nlink -The number of hard links the file is expected to have. -.It Cm owner -The owner of the file; may be either numeric or symbolic. -.It Cm size -The size, in bytes, of the file. -.It Cm link -The file a symbolic link is expected to reference. -.It Cm time -The last modification time of the file. -.It Cm type -The type of the file; may be set to any one of the following: -.Bl -tag -width Cm -compact -.It Cm block -block special device -.It Cm char -character special device -.It Cm dir -directory -.It Cm fifo -fifo -.It Cm file -regular file -.It Cm link -symbolic link -.It Cm socket -socket -.El -.El -.Pp -There are four types of lines in a specification. -.Pp -The first type of line sets a ``global'' value for a keyword, and -consists of a leading ``/set'' followed by whitespace, followed by -sets of keyword/value pairs, separated by whitespace. -Keyword/value pairs consist of a keyword, followed by a equals sign -(``=''), followed by a value, without intervening whitespace. -Once a keyword has been set, its value remains unchanged until either -set again or unset. -.Pp -The second type of line unsets keywords and consists of a leading -``/unset'', followed by whitespace, followed by one or more keywords, -separated by whitespace. -.Pp -The third type of line is a file specification and consists of a file -name, followed by whitespace, followed by zero or more whitespace -separated keyword/value pairs. -The file name may be preceded by any number of whitespace characters. -The file name may contain any of the standard file name matching -characters (``['', ``]'', ``?'' or ``*''), in which case files -in the hierarchy will be associated with the first pattern that -they match. -.Pp -Each of the keyword/value pairs consist of a keyword, followed by an -equals sign (``=''), followed by the keyword's value, without intervening -whitespace. -These values override, without changing, the global value of the -corresponding keyword. -.Pp -All paths are relative. -Specifying a directory will cause subsequent files to be searched -for in that directory hierarchy. -Which brings us to the last type of line in a specification: a line -containing only the string -.Dq Nm \&.. -causes the current directory -path to ascend one level. -.Pp -Empty lines and lines whose first non-whitespace character is a hash -mark (``#'') are ignored. -.Pp -.Nm Mtree -exits with a status of 0 on success and >0 if an error occurred or the -tree did not match the specification. -.Sh FILES -.Bl -tag -width /etc/mtree -compact -.It Pa /etc/mtree -system specification directory -.El -.Sh SEE ALSO -.Xr chmod 1 , -.Xr chown 1 , -.Xr chgrp 1 , -.Xr cksum 1 , -.Xr find 1 , -.Xr stat 2 , -.Xr fts 3 , -.Xr mkproto 8 -.Sh BUGS -The -.Cm cksum -keyword is not yet implemented. -.Pp -The -.Cm time -keyword should be specifiable in human readable terms. -.Sh EXAMPLE -.Bd -literal -offset indent -compact -# fs: /a/staff/rick/mybin -# by: rick -# date: Fri May 25 12:26:57 1990 - -/set group=staff mode=0555 nlink=1 owner=rick type=file -[ nlink=2 size=6144 -adb size=53248 -df group=operator mode=02555 size=20480 -ps group=kmem mode=02555 size=54272 -rcp owner=root mode=04555 size=79872 -test nlink=2 size=6144 - -/set group=wheel mode=0444 nlink=1 owner=rick type=file -manpages type=dir mode=0775 nlink=2 size=1024 -adb.man size=9473 -df.man size=5263 -tar.man size=3324 -\&.. -.Ed -.Sh HISTORY -The -.Nm mtree -utility appeared in -.Bx 4.3 Reno . diff --git a/usr.sbin/mtree/mtree.8 b/usr.sbin/mtree/mtree.8 new file mode 100644 index 0000000000..b32a548513 --- /dev/null +++ b/usr.sbin/mtree/mtree.8 @@ -0,0 +1,249 @@ +.\" Copyright (c) 1989, 1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mtree.8 5.11 (Berkeley) 12/11/91 +.\" +.Dd December 11, 1991 +.Dt MTREE 8 +.Os +.Sh NAME +.Nm mtree +.Nd map a directory hierarchy +.Sh SYNOPSIS +.Nm mtree +.Op Fl cderux +.Op Fl f Ar spec +.Op Fl K Ar keywords +.Op Fl k Ar keywords +.Op Fl p Ar path +.Op Fl s Ar seed +.Sh DESCRIPTION +The utility +.Nm mtree +compares the file hierarchy rooted in the current directory against a +specification read from the standard input. +Messages are written to the standard output for any files whose +characteristics do not match the specification's, or which are +missing from either the file hierarchy or the specification. +.Pp +The options are as follows: +.Bl -tag -width flag +.It Fl c +Print a specification for the file hierarchy to the standard output. +.It Fl d +Ignore everything except directory type files. +.It Fl e +Don't complain about files that are in the file hierarchy, but not in the +specification. +.It Fl f +Read the specification from +.Ar file , +instead of from the standard input. +.It Fl K +Add the specified (whitespace or comma separated) keywords to the current +set of keywords. +.It Fl k +Use the ``type'' keyword plus the specified (whitespace or comma separated) +keywords instead of the current set of keywords. +.It Fl p +Use the file hierarchy rooted in +.Ar path , +instead of the current directory. +.It Fl r +Remove any files in the file hierarchy that are not described in the +specification. +.It Fl s +Display a single checksum to the standard error output that represents all +of the files for which the keyword +.Cm cksum +was specified. +The checksum is seeded with the specified value. +.It Fl u +Modify the owner, group, and permissions of existing files to match +the specification and create any missing directories. +User, group, and permissions must all be specified for missing directories +to be created. +.It Fl x +Don't descend below mount points in the file hierarchy. +.El +.Pp +Specifications are mostly composed of ``keywords'', i.e. strings that +that specify values relating to files. +No keywords have default values, and if a keyword has no value set, no +checks based on it are performed. +.Pp +Currently supported keywords are as follows: +.Bl -tag -width Cm +.It Cm cksum +The checksum of the file using the default algorithm specified by +the +.Xr cksum 1 +utility. +.It Cm ignore +Ignore any file hierarchy below this file. +.It Cm gid +The file group as a numeric value. +.It Cm gname +The file group as a symbolic name. +.It Cm mode +The current file's permissions as a numeric (octal) or symbolic +value. +.It Cm nlink +The number of hard links the file is expected to have. +.It Cm uid +The file owner as a numeric value. +.It Cm uname +The file group as a symbolic name. +.It Cm size +The size, in bytes, of the file. +.It Cm link +The file the symbolic link is expected to reference. +.It Cm time +The last modification time of the file. +.It Cm type +The type of the file; may be set to any one of the following: +.sp +.Bl -tag -width Cm -compact +.It Cm block +block special device +.It Cm char +character special device +.It Cm dir +directory +.It Cm fifo +fifo +.It Cm file +regular file +.It Cm link +symbolic link +.It Cm socket +socket +.El +.El +.Pp +The default set of keywords are +.Cm gid , +.Cm mode , +.Cm nlink , +.Cm size , +.Cm slink , +.Cm time , +and +.Cm uid . +.Pp +There are four types of lines in a specification. +.Pp +The first type of line sets a global value for a keyword, and consists of +the string ``/set'' followed by whitespace, followed by sets of keyword/value +pairs, separated by whitespace. +Keyword/value pairs consist of a keyword, followed by an equals sign +(``=''), followed by a value, without whitespace characters. +Once a keyword has been set, its value remains unchanged until either +reset or unset. +.Pp +The second type of line unsets keywords and consists of the string +``/unset'', followed by whitespace, followed by one or more keywords, +separated by whitespace. +.Pp +The third type of line is a file specification and consists of a file +name, followed by whitespace, followed by zero or more whitespace +separated keyword/value pairs. +The file name may be preceded by whitespace characters. +The file name may contain any of the standard file name matching +characters (``['', ``]'', ``?'' or ``*''), in which case files +in the hierarchy will be associated with the first pattern that +they match. +.Pp +Each of the keyword/value pairs consist of a keyword, followed by an +equals sign (``=''), followed by the keyword's value, without +whitespace characters. +These values override, without changing, the global value of the +corresponding keyword. +.Pp +All paths are relative. +Specifying a directory will cause subsequent files to be searched +for in that directory hierarchy. +Which brings us to the last type of line in a specification: a line +containing only the string +.Dq Nm \&.. +causes the current directory +path to ascend one level. +.Pp +Empty lines and lines whose first non-whitespace character is a hash +mark (``#'') are ignored. +.Pp +The +.Nm mtree +utility exits with a status of 0 on success, 1 if any error occurred, +and 2 if the file hierarchy did not match the specification. +.Sh EXAMPLES +To detect system binaries that have been ``trojan horsed'', it is recommended +that +.Nm mtree +be run on the file systems, and a copy of the results stored on a different +machine, or, at least, in encrypted form. +The seed for the +.Fl s +option should not be an obvious value and the final checksum should not be +stored on-line under any circumstances! +Then, periodically, +.Nm mtree +should be run against the on-line specifications and the final checksum +compared with the previous value. +While it is possible for the bad guys to change the on-line specifications +to conform to their modified binaries, it shouldn't be possible for them +to make it produce the same final checksum value. +If the final checksum value changes, the off-line copies of the specification +can be used to detect which of the binaries have actually been modified. +.Pp +The +.Fl d +and +.Fl u +options can be used in combination to create directory hierarchies +for distributions and other such things. +.Sh FILES +.Bl -tag -width /etc/mtree -compact +.It Pa /etc/mtree +system specification directory +.El +.Sh SEE ALSO +.Xr chmod 1 , +.Xr chown 1 , +.Xr chgrp 1 , +.Xr cksum 1 , +.Xr stat 2 , +.Xr fts 3 , +.Sh HISTORY +The +.Nm mtree +utility appeared in +.Bx 4.3 Reno . diff --git a/usr.sbin/mtree/mtree.c b/usr.sbin/mtree/mtree.c index a46e4fd65a..7dea6def10 100644 --- a/usr.sbin/mtree/mtree.c +++ b/usr.sbin/mtree/mtree.c @@ -38,31 +38,40 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)mtree.c 5.8 (Berkeley) 5/25/90"; +static char sccsid[] = "@(#)mtree.c 5.10 (Berkeley) 4/17/92"; #endif /* not lint */ #include <sys/param.h> #include <sys/stat.h> #include <errno.h> +#include <unistd.h> #include <stdio.h> #include <fts.h> #include "mtree.h" +#include "extern.h" -NODE *root; -int exitval; -int cflag, dflag, eflag, rflag, uflag; +int crc_total; +int ftsoptions = FTS_PHYSICAL; +int cflag, dflag, eflag, rflag, sflag, uflag; +u_short keys; +char fullpath[MAXPATHLEN]; + +static void usage __P((void)); + +int main(argc, argv) int argc; - char **argv; + char *argv[]; { - extern int ftsoptions, optind; + extern int optind; extern char *optarg; int ch; - char *dir; + char *dir, *p; - dir = (char *)NULL; - while ((ch = getopt(argc, argv, "cdef:p:rux")) != EOF) + dir = NULL; + keys = KEYDEFAULT; + while ((ch = getopt(argc, argv, "cdef:K:k:p:rs:ux")) != EOF) switch((char)ch) { case 'c': cflag = 1; @@ -74,11 +83,19 @@ main(argc, argv) eflag = 1; break; case 'f': - if (!(freopen(optarg, "r", stdin))) { - (void)fprintf(stderr, - "mtree: can't read %s.\n", optarg); - exit(1); - } + if (!(freopen(optarg, "r", stdin))) + err("%s: %s", optarg, strerror(errno)); + break; + case 'K': + while ((p = strsep(&optarg, " \t,")) != NULL) + if (*p != '\0') + keys |= parsekey(p, NULL); + break; + case 'k': + keys = F_TYPE; + while ((p = strsep(&optarg, " \t,")) != NULL) + if (*p != '\0') + keys |= parsekey(p, NULL); break; case 'p': dir = optarg; @@ -86,6 +103,11 @@ main(argc, argv) case 'r': rflag = 1; break; + case 's': + sflag = 1; + crc_total = ~strtol(optarg, &p, 0); + if (*p) + err("illegal seed value -- %s", optarg); case 'u': uflag = 1; break; @@ -97,28 +119,28 @@ main(argc, argv) usage(); } argc -= optind; + argv += optind; + if (argc) usage(); - if (!cflag) - spec(); + if (dir && chdir(dir)) + err("%s: %s", dir, strerror(errno)); - if (dir && chdir(dir)) { - (void)fprintf(stderr, - "mtree: %s: %s\n", dir, strerror(errno)); - exit(1); - } + if ((cflag || sflag) && !getwd(fullpath)) + err("%s", fullpath); - if (cflag) + if (cflag) { cwalk(); - else - verify(); - exit(exitval); + exit(0); + } + exit(verify()); } +static void usage() { (void)fprintf(stderr, - "usage: mtree [-cderux] [-p path] [-f spec]\n"); +"usage: mtree [-cderux] [-f spec] [-K key] [-k key] [-p path] [-s seed]\n"); exit(1); } diff --git a/usr.sbin/mtree/mtree.h b/usr.sbin/mtree/mtree.h index 9dd416e9bb..3c98af2161 100644 --- a/usr.sbin/mtree/mtree.h +++ b/usr.sbin/mtree/mtree.h @@ -30,12 +30,17 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)mtree.h 5.7 (Berkeley) 5/25/90 + * @(#)mtree.h 5.9 (Berkeley) 2/19/92 */ #include <string.h> #include <stdlib.h> +#define KEYDEFAULT \ + (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | F_TIME | F_UID) + +#define MISMATCHEXIT 2 + typedef struct _node { struct _node *parent, *child; /* up, down */ struct _node *prev, *next; /* left, right */ @@ -43,37 +48,41 @@ typedef struct _node { time_t st_mtime; /* last modification time */ u_long cksum; /* check sum */ char *slink; /* symbolic link reference */ - uid_t st_uid; /* owner */ - gid_t st_gid; /* group */ + uid_t st_uid; /* uid */ + gid_t st_gid; /* gid */ #define MBITS (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO) mode_t st_mode; /* mode */ nlink_t st_nlink; /* link count */ -#define F_BLOCK 0x001 /* block special */ -#define F_CHAR 0x002 /* char special */ -#define F_DIR 0x004 /* directory */ -#define F_FIFO 0x008 /* fifo */ -#define F_FILE 0x010 /* regular file */ -#define F_LINK 0x020 /* symbolic link */ -#define F_SOCK 0x040 /* socket */ - u_short type; /* file type */ - #define F_CKSUM 0x0001 /* check sum */ #define F_DONE 0x0002 /* directory done */ -#define F_GROUP 0x0004 /* group */ -#define F_IGN 0x0008 /* ignore */ -#define F_MAGIC 0x0010 /* name has magic chars */ -#define F_MODE 0x0020 /* mode */ -#define F_NLINK 0x0040 /* number of links */ -#define F_OWNER 0x0080 /* owner */ +#define F_GID 0x0004 /* gid */ +#define F_GNAME 0x0008 /* group name */ +#define F_IGN 0x0010 /* ignore */ +#define F_MAGIC 0x0020 /* name has magic chars */ +#define F_MODE 0x0040 /* mode */ +#define F_NLINK 0x0080 /* number of links */ #define F_SIZE 0x0100 /* size */ #define F_SLINK 0x0200 /* link count */ #define F_TIME 0x0400 /* modification time */ #define F_TYPE 0x0800 /* file type */ -#define F_VISIT 0x1000 /* file visited */ +#define F_UID 0x1000 /* uid */ +#define F_UNAME 0x2000 /* user name */ +#define F_VISIT 0x4000 /* file visited */ u_short flags; /* items set */ +#define F_BLOCK 0x001 /* block special */ +#define F_CHAR 0x002 /* char special */ +#define F_DIR 0x004 /* directory */ +#define F_FIFO 0x008 /* fifo */ +#define F_FILE 0x010 /* regular file */ +#define F_LINK 0x020 /* symbolic link */ +#define F_SOCK 0x040 /* socket */ + u_char type; /* file type */ + char name[1]; /* file name (must be last) */ } NODE; -#define RP(p) (p->fts_path + 2) +#define RP(p) \ + ((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \ + (p)->fts_path + 2 : (p)->fts_path) diff --git a/usr.sbin/mtree/spec.c b/usr.sbin/mtree/spec.c index 384d5937f5..2fb3286801 100644 --- a/usr.sbin/mtree/spec.c +++ b/usr.sbin/mtree/spec.c @@ -32,84 +32,116 @@ */ #ifndef lint -static char sccsid[] = "@(#)spec.c 5.14 (Berkeley) 3/2/91"; +static char sccsid[] = "@(#)spec.c 5.17 (Berkeley) 4/17/92"; #endif /* not lint */ #include <sys/types.h> +#include <sys/stat.h> +#include <fts.h> #include <pwd.h> #include <grp.h> -#include <stdio.h> #include <errno.h> +#include <unistd.h> +#include <stdio.h> #include <ctype.h> #include "mtree.h" +#include "extern.h" -extern NODE *root; /* root of the tree */ +int lineno; /* Current spec line number. */ -static int lineno; /* current spec line number */ +static void set __P((char *, NODE *)); +static void unset __P((char *, NODE *)); +NODE * spec() { register NODE *centry, *last; register char *p; - NODE ginfo, *emalloc(); + NODE ginfo, *root; + int c_cur, c_next; char buf[2048]; - bzero((void *)&ginfo, sizeof(ginfo)); - for (lineno = 1; fgets(buf, sizeof(buf), stdin); ++lineno) { - if (!(p = index(buf, '\n'))) { - (void)fprintf(stderr, - "mtree: line %d too long.\n", lineno); - exit(1); + root = NULL; + bzero(&ginfo, sizeof(ginfo)); + c_cur = c_next = 0; + for (lineno = 1; fgets(buf, sizeof(buf), stdin); + ++lineno, c_cur = c_next, c_next = 0) { + /* Skip empty lines. */ + if (buf[0] == '\n') + continue; + + /* Find end of line. */ + if ((p = index(buf, '\n')) == NULL) + err("line %d too long", lineno); + + /* See if next line is continuation line. */ + if (p[-1] == '\\') { + --p; + c_next = 1; } + + /* Null-terminate the line. */ *p = '\0'; + + /* Skip leading whitespace. */ for (p = buf; *p && isspace(*p); ++p); + + /* If nothing but whitespace or comment char, continue. */ if (!*p || *p == '#') continue; - /* grab file name, "$", "set", or "unset" */ - if (!(p = strtok(p, "\n\t "))) - specerr(); +#ifdef DEBUG + (void)fprintf(stderr, "line %d: {%s}\n", lineno, p); +#endif + if (c_cur) { + set(p, centry); + continue; + } + + /* Grab file name, "$", "set", or "unset". */ + if ((p = strtok(p, "\n\t ")) == NULL) + err("missing field"); if (p[0] == '/') switch(p[1]) { case 's': if (strcmp(p + 1, "set")) break; - set(&ginfo); + set(NULL, &ginfo); continue; case 'u': if (strcmp(p + 1, "unset")) break; - unset(&ginfo); + unset(NULL, &ginfo); continue; } - if (index(p, '/')) { - (void)fprintf(stderr, - "mtree: file names may not contain slashes.\n"); - specerr(); - } + if (index(p, '/')) + err("slash character in file name"); if (!strcmp(p, "..")) { - /* don't go up, if haven't gone down */ + /* Don't go up, if haven't gone down. */ if (!root) - noparent(); + goto noparent; if (last->type != F_DIR || last->flags & F_DONE) { if (last == root) - noparent(); + goto noparent; last = last->parent; } last->flags |= F_DONE; continue; + +noparent: err("no parent node"); } - centry = emalloc(sizeof(NODE) + strlen(p)); + if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) + err("%s", strerror(errno)); *centry = ginfo; (void)strcpy(centry->name, p); #define MAGIC "?*[" if (strpbrk(p, MAGIC)) centry->flags |= F_MAGIC; - set(centry); + set(NULL, centry); if (!root) { last = root = centry; @@ -123,58 +155,68 @@ spec() last = last->next = centry; } } + return (root); } -set(ip) +static void +set(t, ip) + char *t; register NODE *ip; { register int type; register char *kw, *val; - gid_t getgroup(); - uid_t getowner(); - long atol(), strtol(); + struct group *gr; + struct passwd *pw; + mode_t *m; + int value; + char *ep; - while (kw = strtok((char *)NULL, "= \t\n")) { - ip->flags |= type = key(kw); - val = strtok((char *)NULL, " \t\n"); - if (!val) - specerr(); + for (; kw = strtok(t, "= \t\n"); t = NULL) { + ip->flags |= type = parsekey(kw, &value); + if (value && (val = strtok(NULL, " \t\n")) == NULL) + err("missing value"); switch(type) { case F_CKSUM: - ip->cksum = atol(val); + ip->cksum = strtoul(val, &ep, 10); + if (*ep) + err("invalid checksum %s", val); + break; + case F_GID: + ip->st_gid = strtoul(val, &ep, 10); + if (*ep) + err("invalid gid %s", val); break; - case F_GROUP: - ip->st_gid = getgroup(val); + case F_GNAME: + if ((gr = getgrnam(val)) == NULL) + err("unknown group %s", val); + ip->st_gid = gr->gr_gid; break; case F_IGN: /* just set flag bit */ break; - case F_MODE: { - mode_t *m, *setmode(); - - if (!(m = setmode(val))) { - (void)fprintf(stderr, - "mtree: invalid file mode %s.\n", val); - specerr(); - } + case F_MODE: + if ((m = setmode(val)) == NULL) + err("invalid file mode %s", val); ip->st_mode = getmode(m, 0); break; - } case F_NLINK: - ip->st_nlink = atoi(val); - break; - case F_OWNER: - ip->st_uid = getowner(val); + ip->st_nlink = strtoul(val, &ep, 10); + if (*ep) + err("invalid link count %s", val); break; case F_SIZE: - ip->st_size = atol(val); + ip->st_size = strtoul(val, &ep, 10); + if (*ep) + err("invalid size %s", val); break; case F_SLINK: - if (!(ip->slink = strdup(val))) - nomem(); + if ((ip->slink = strdup(val)) == NULL) + err("%s", strerror(errno)); break; case F_TIME: - ip->st_mtime = atol(val); + ip->st_mtime = strtoul(val, &ep, 10); + if (*ep) + err("invalid time %s", val); break; case F_TYPE: switch(*val) { @@ -205,139 +247,30 @@ set(ip) ip->type = F_SOCK; break; default: - (void)fprintf(stderr, - "mtree: unknown file type %s.\n", val); - specerr(); + err("unknown file type %s", val); } break; + case F_UID: + ip->st_uid = strtoul(val, &ep, 10); + if (*ep) + err("invalid uid %s", val); + break; + case F_UNAME: + if ((pw = getpwnam(val)) == NULL) + err("unknown user %s", val); + ip->st_uid = pw->pw_uid; + break; } } } -unset(ip) +static void +unset(t, ip) + char *t; register NODE *ip; { register char *p; - while (p = strtok((char *)NULL, "\n\t ")) - ip->flags &= ~key(p); -} - -key(p) - char *p; -{ - switch(*p) { - case 'c': - if (!strcmp(p, "cksum")) - return(F_CKSUM); - break; - case 'g': - if (!strcmp(p, "group")) - return(F_GROUP); - break; - case 'i': - if (!strcmp(p, "ignore")) - return(F_IGN); - break; - case 'l': - if (!strcmp(p, "link")) - return(F_SLINK); - break; - case 'm': - if (!strcmp(p, "mode")) - return(F_MODE); - break; - case 'n': - if (!strcmp(p, "nlink")) - return(F_NLINK); - break; - case 'o': - if (!strcmp(p, "owner")) - return(F_OWNER); - break; - case 's': - if (!strcmp(p, "size")) - return(F_SIZE); - break; - case 't': - if (!strcmp(p, "type")) - return(F_TYPE); - if (!strcmp(p, "time")) - return(F_TIME); - break; - } - (void)fprintf(stderr, "mtree: unknown keyword %s.\n", p); - specerr(); - /* NOTREACHED */ -} - - -uid_t -getowner(p) - register char *p; -{ - struct passwd *pw; - int val; - - if (isdigit(*p)) { - if ((val = atoi(p)) >= 0) - return((uid_t)val); - (void)fprintf(stderr, "mtree: illegal uid value %s.\n", p); - } else if (pw = getpwnam(p)) - return(pw->pw_uid); - else - (void)fprintf(stderr, "mtree: unknown user %s.\n", p); - specerr(); - /* NOTREACHED */ -} - -gid_t -getgroup(p) - register char *p; -{ - struct group *gr; - int val; - - if (isdigit(*p)) { - if ((val = atoi(p)) >= 0) - return((gid_t)val); - (void)fprintf(stderr, "mtree: illegal gid value %s.\n", p); - } else if (gr = getgrnam(p)) - return(gr->gr_gid); - else - (void)fprintf(stderr, "mtree: unknown group %s.\n", p); - specerr(); - /* NOTREACHED */ -} - -noparent() -{ - (void)fprintf(stderr, "mtree: no parent node.\n"); - specerr(); -} - -specerr() -{ - (void)fprintf(stderr, - "mtree: line %d of the specification is incorrect.\n", lineno); - exit(1); -} - -NODE * -emalloc(size) - int size; -{ - void *p; - - /* NOSTRICT */ - if (!(p = malloc((u_int)size))) - nomem(); - bzero(p, size); - return((NODE *)p); -} - -nomem() -{ - (void)fprintf(stderr, "mtree: %s.\n", strerror(ENOMEM)); - exit(1); + while (p = strtok(t, "\n\t ")) + ip->flags &= ~parsekey(p, NULL); } diff --git a/usr.sbin/mtree/verify.c b/usr.sbin/mtree/verify.c index 42467db118..d093126222 100644 --- a/usr.sbin/mtree/verify.c +++ b/usr.sbin/mtree/verify.c @@ -32,7 +32,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)verify.c 5.9 (Berkeley) 3/12/91"; +static char sccsid[] = "@(#)verify.c 5.11 (Berkeley) 4/17/92"; #endif /* not lint */ #include <sys/param.h> @@ -40,56 +40,65 @@ static char sccsid[] = "@(#)verify.c 5.9 (Berkeley) 3/12/91"; #include <dirent.h> #include <fts.h> #include <unistd.h> +#include <fnmatch.h> #include <errno.h> #include <stdio.h> #include "mtree.h" +#include "extern.h" -extern NODE *root; +extern int crc_total, ftsoptions; +extern int dflag, eflag, rflag, sflag, uflag; +extern char fullpath[MAXPATHLEN]; +static NODE *root; static char path[MAXPATHLEN]; +static void miss __P((NODE *, char *)); +static int vwalk __P((void)); + +int verify() { - vwalk(); + int rval; + + root = spec(); + rval = vwalk(); miss(root, path); + return (rval); } +static int vwalk() { - extern int ftsoptions, dflag, eflag, rflag; register FTS *t; register FTSENT *p; register NODE *ep, *level; + int ftsdepth, specdepth, rval; char *argv[2]; - int ftsdepth = 0, specdepth = 0; argv[0] = "."; - argv[1] = (char *)NULL; - if (!(t = fts_open(argv, ftsoptions, (int (*)())NULL))) { - (void)fprintf(stderr, - "mtree: fts_open: %s.\n", strerror(errno)); - exit(1); - } + argv[1] = NULL; + if ((t = fts_open(argv, ftsoptions, NULL)) == NULL) + err("fts_open: %s", strerror(errno)); level = root; + ftsdepth = specdepth = rval = 0; while (p = fts_read(t)) { switch(p->fts_info) { case FTS_D: - if (!strcmp(p->fts_name, ".")) - continue; - ftsdepth++; + ++ftsdepth; break; case FTS_DP: - ftsdepth--; + --ftsdepth; if (specdepth > ftsdepth) { for (level = level->parent; level->prev; level = level->prev); - specdepth--; + --specdepth; } continue; case FTS_DNR: case FTS_ERR: case FTS_NS: - (void)fprintf(stderr, "mtree: %s: %s.\n", + (void)fprintf(stderr, "mtree: %s: %s\n", RP(p), strerror(errno)); continue; default: @@ -99,18 +108,17 @@ vwalk() for (ep = level; ep; ep = ep->next) if (ep->flags & F_MAGIC && fnmatch(ep->name, - p->fts_name, FNM_PATHNAME|FNM_QUOTE) || + p->fts_name, FNM_PATHNAME) || !strcmp(ep->name, p->fts_name)) { ep->flags |= F_VISIT; - if (ep->flags & F_IGN) { + if (compare(ep->name, ep, p)) + rval = MISMATCHEXIT; + if (ep->flags & F_IGN) (void)fts_set(t, p, FTS_SKIP); - continue; - } - compare(ep->name, ep, p); - if (ep->child && ep->type == F_DIR && + else if (ep->child && ep->type == F_DIR && p->fts_info == FTS_D) { level = ep->child; - specdepth++; + ++specdepth; } break; } @@ -131,13 +139,17 @@ vwalk() (void)fts_set(t, p, FTS_SKIP); } (void)fts_close(t); + if (sflag) + (void)fprintf(stderr, + "mtree: %s checksum: %lu\n", fullpath, crc_total); + return (rval); } +static void miss(p, tail) register NODE *p; register char *tail; { - extern int dflag, uflag; register int create; register char *tp; @@ -154,9 +166,12 @@ miss(p, tail) create = 0; if (!(p->flags & F_VISIT) && uflag) -#define MINBITS (F_GROUP|F_MODE|F_OWNER) - if ((p->flags & MINBITS) != MINBITS) - (void)printf(" (not created -- group, mode or owner not specified)"); + if (!(p->flags & (F_UID | F_UNAME))) + (void)printf(" (not created: user not specified)"); + else if (!(p->flags & (F_GID | F_GNAME))) + (void)printf(" (not created: group not specified)"); + else if (!(p->flags & F_MODE)) + (void)printf(" (not created: mode not specified)"); else if (mkdir(path, S_IRWXU)) (void)printf(" (not created: %s)", strerror(errno)); @@ -176,7 +191,7 @@ miss(p, tail) if (!create) continue; if (chown(path, p->st_uid, p->st_gid)) { - (void)printf("%s: owner/group/mode not modified: %s\n", + (void)printf("%s: user/group/mode not modified: %s\n", path, strerror(errno)); continue; } diff --git a/usr.sbin/named/Makefile b/usr.sbin/named/Makefile index 9da21e264c..f7b9545b1b 100644 --- a/usr.sbin/named/Makefile +++ b/usr.sbin/named/Makefile @@ -3,7 +3,7 @@ ### -DALLOW_T_UNSPEC -Dmalloc=rt_malloc -Dfree=rt_free ### ALLOC=storage.o PROG= named -MAN8= named.0 +MAN8= named.8 CFLAGS= -O -DDEBUG -DSTATS LDADD= -lutil SRCS= db_dump.c db_glue.c db_load.c db_lookup.c db_reload.c db_save.c \ diff --git a/usr.sbin/named/tools/nslookup/Makefile b/usr.sbin/named/tools/nslookup/Makefile index beae27a7a0..c2d7e9c1ee 100644 --- a/usr.sbin/named/tools/nslookup/Makefile +++ b/usr.sbin/named/tools/nslookup/Makefile @@ -3,7 +3,7 @@ PROG= nslookup SRCS= main.c getinfo.c debug.c send.c skip.c list.c subr.c OBJS+= commands.o -MAN8= nslookup.0 +MAN8= nslookup.8 LFLAGS= -I CFLAGS+=-I${.CURDIR} DPADD= ${LIBLN} diff --git a/usr.sbin/portmap/Makefile b/usr.sbin/portmap/Makefile index 4b7cf7edd5..d47fb0a0c5 100644 --- a/usr.sbin/portmap/Makefile +++ b/usr.sbin/portmap/Makefile @@ -3,6 +3,6 @@ PROG= portmap DPADD= ${LIBRPC} ${LIBUTIL} LDADD= -lrpc -lutil -MAN8= portmap.0 +MAN8= portmap.8 .include <bsd.prog.mk> diff --git a/usr.sbin/pppstats/Makefile b/usr.sbin/pppstats/Makefile new file mode 100644 index 0000000000..9122ec3a11 --- /dev/null +++ b/usr.sbin/pppstats/Makefile @@ -0,0 +1,16 @@ +# +# $Id$ +# +PROG= pppstat +SRCS= pppstats.c + +CFLAGS= -DNO_DRAND48 -DKVMLIB -DPPP + +DPADD+= ${LIBUTIL} +LDADD+= -lutil + +NOMAN= noman + +BINGRP= kmem + +.include <bsd.prog.mk> diff --git a/usr.sbin/pppstats/pppstats.c b/usr.sbin/pppstats/pppstats.c new file mode 100644 index 0000000000..ed88a9b356 --- /dev/null +++ b/usr.sbin/pppstats/pppstats.c @@ -0,0 +1,346 @@ +/* + * print PPP statistics: + * pppstats [-i interval] [-v] [interface] [system] [core] + * + * Brad Parker (brad@cayman.com) 6/92 + * + * from the original "slstats" by Van Jaconson + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * - Initial distribution. + */ + +#include <sys/param.h> +#include <sys/mbuf.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/file.h> +#ifndef KVMLIB +#include <machine/pte.h> +#endif +#ifdef sun +#include <kvm.h> +#endif +#include <ctype.h> +#include <errno.h> +#include <nlist.h> +#include <stdio.h> +#include <signal.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> + +#define VJC 1 +#include <net/slcompress.h> +#include <net/if_ppp.h> + +#ifdef STREAMS +#include <sys/stream.h> +#include "ppp_str.h" +#endif + +#ifdef STREAMS +struct nlist nl[] = { +#define N_SOFTC 0 + { "_pii" }, + "", +}; +#else +struct nlist nl[] = { +#define N_SOFTC 0 + { "_ppp_softc" }, + "", +}; +#endif + +#ifndef KVMLIB +struct pte *Sysmap; +int kmem; +extern off_t lseek(); +#endif + +#ifdef sun +kvm_t *kd; +#endif + +#ifdef sun +char *system = "/vmunix"; +#else +char *system = "/386bsd"; +#endif + +#ifndef KVMLIB +char *kmemf = "/dev/kmem"; +#else +char *kmemf; +#endif +int kflag; +int vflag; +unsigned interval = 5; +int unit; + +extern char *malloc(); + + +main(argc, argv) + int argc; + char *argv[]; +{ + --argc; ++argv; + while (argc > 0) { + if (strcmp(argv[0], "-v") == 0) { + ++vflag; + ++argv, --argc; + continue; + } + if (strcmp(argv[0], "-i") == 0 && argv[1] && + isdigit(argv[1][0])) { + interval = atoi(argv[1]); + if (interval <= 0) + usage(); + ++argv, --argc; + ++argv, --argc; + continue; + } + if (isdigit(argv[0][0])) { + unit = atoi(argv[0]); + if (unit < 0) + usage(); + ++argv, --argc; + continue; + } + if (kflag) + usage(); + + system = *argv; + ++argv, --argc; + if (argc > 0) { + kmemf = *argv++; + --argc; + kflag++; + } + } +#ifndef KVMLIB + if (nlist(system, nl) < 0 || nl[0].n_type == 0) { + fprintf(stderr, "%s: no namelist\n", system); + exit(1); + } + kmem = open(kmemf, O_RDONLY); + if (kmem < 0) { + perror(kmemf); + exit(1); + } + if (kflag) { + off_t off; + + Sysmap = (struct pte *) + malloc((u_int)(nl[N_SYSSIZE].n_value * sizeof(struct pte))); + if (!Sysmap) { + fputs("netstat: can't get memory for Sysmap.\n", stderr); + exit(1); + } + off = nl[N_SYSMAP].n_value & ~KERNBASE; + (void)lseek(kmem, off, L_SET); + (void)read(kmem, (char *)Sysmap, + (int)(nl[N_SYSSIZE].n_value * sizeof(struct pte))); + } +#else +#ifdef sun + /* SunOS */ + if ((kd = kvm_open(system, kmemf, (char *)0, O_RDONLY, NULL)) == NULL) { + perror("kvm_open"); + exit(1); + } +#else + /* BSD4.3+ */ + if (kvm_openfiles(system, kmemf, (char *)0) == -1) { + fprintf(stderr, "kvm_openfiles: %s", kvm_geterr()); + exit(1); + } +#endif + +#ifdef sun + if (kvm_nlist(kd, nl)) { +#else + if (kvm_nlist(nl)) { +#endif + fprintf(stderr, "pppstats: can't find symbols in nlist\n"); + exit(1); + } +#endif + intpr(); + exit(0); +} + +#ifndef KVMLIB +/* + * Seek into the kernel for a value. + */ +off_t +klseek(fd, base, off) + int fd, off; + off_t base; +{ + if (kflag) { + /* get kernel pte */ + base &= ~KERNBASE; + base = ctob(Sysmap[btop(base)].pg_pfnum) + (base & PGOFSET); + } + return (lseek(fd, base, off)); +} +#endif + +usage() +{ + fprintf(stderr,"usage: pppstats [-i interval] [-v] [unit] [system] [core]\n"); + exit(1); +} + +u_char signalled; /* set if alarm goes off "early" */ + +#define V(offset) ((line % 20)? sc->offset - osc->offset : sc->offset) + +/* + * Print a running summary of interface statistics. + * Repeat display every interval seconds, showing statistics + * collected over that interval. Assumes that interval is non-zero. + * First line printed at top of screen is always cumulative. + */ +intpr() +{ + register int line = 0; + int oldmask; +#ifdef __STDC__ + void catchalarm(int); +#else + void catchalarm(); +#endif + +#ifdef STREAMS +#define STRUCT struct ppp_if_info +#else +#define STRUCT struct ppp_softc +#endif + + STRUCT *sc, *osc; + + nl[N_SOFTC].n_value += unit * sizeof(struct ppp_softc); + sc = (STRUCT *)malloc(sizeof(STRUCT)); + osc = (STRUCT *)malloc(sizeof(STRUCT)); + + bzero((char *)osc, sizeof(STRUCT)); + + while (1) { +#ifndef KVMLIB + if (klseek(kmem, (off_t)nl[N_SOFTC].n_value, 0) < 0) + if(errno != EINTR) + perror("kmem seek"); + if (read(kmem, (char *)sc, sizeof(STRUCT)) <= 0) + perror("kmem read"); +#else +#ifdef sun + if (kvm_read(kd, nl[N_SOFTC].n_value, +#else + if (kvm_read(nl[N_SOFTC].n_value, +#endif + sc, sizeof(STRUCT)) != + sizeof(STRUCT)) + perror("kvm_read"); +#endif + + (void)signal(SIGALRM, catchalarm); + signalled = 0; + (void)alarm(interval); + + if ((line % 20) == 0) { + printf("%6.6s %6.6s %6.6s %6.6s %6.6s", + "in", "pack", "comp", "uncomp", "err"); + if (vflag) + printf(" %6.6s %6.6s", "toss", "ip"); + printf(" | %6.6s %6.6s %6.6s %6.6s %6.6s", + "out", "pack", "comp", "uncomp", "ip"); + if (vflag) + printf(" %6.6s %6.6s", "search", "miss"); + putchar('\n'); + } + +#ifdef STREAMS +#define COMP pii_sc_comp +#define STATS pii_ifnet +#else +#define COMP sc_comp +#define STATS sc_if +#endif + + printf("%6d %6d %6d %6d %6d", +#if BSD > 43 + V(STATS.if_ibytes), +#else + 0, +#endif + V(STATS.if_ipackets), + V(COMP.sls_compressedin), + V(COMP.sls_uncompressedin), + V(COMP.sls_errorin)); + if (vflag) + printf(" %6d %6d", + V(COMP.sls_tossed), + V(STATS.if_ipackets) - + V(COMP.sls_compressedin) - + V(COMP.sls_uncompressedin) - + V(COMP.sls_errorin)); + printf(" | %6d %6d %6d %6d %6d", +#if BSD > 43 + V(STATS.if_obytes), +#else + 0, +#endif + V(STATS.if_opackets), + V(COMP.sls_compressed), + V(COMP.sls_packets) - V(COMP.sls_compressed), + V(STATS.if_opackets) - V(COMP.sls_packets)); + if (vflag) + printf(" %6d %6d", + V(COMP.sls_searches), + V(COMP.sls_misses)); + + putchar('\n'); + fflush(stdout); + line++; + oldmask = sigblock(sigmask(SIGALRM)); + if (! signalled) { + sigpause(0); + } + sigsetmask(oldmask); + signalled = 0; + (void)alarm(interval); + bcopy((char *)sc, (char *)osc, sizeof(STRUCT)); + } +} + +/* + * Called if an interval expires before sidewaysintpr has completed a loop. + * Sets a flag to not wait for the alarm. + */ +void catchalarm(arg) +int arg; +{ + signalled = 1; +} diff --git a/usr.sbin/pwd_mkdb/Makefile b/usr.sbin/pwd_mkdb/Makefile index cc72b12845..645728b350 100644 --- a/usr.sbin/pwd_mkdb/Makefile +++ b/usr.sbin/pwd_mkdb/Makefile @@ -2,6 +2,6 @@ PROG= pwd_mkdb SRCS= pw_scan.c pwd_mkdb.c -MAN8= pwd_mkdb.0 +MAN8= pwd_mkdb.8 .include <bsd.prog.mk> diff --git a/usr.sbin/pwd_mkdb/pwd_mkdb.8 b/usr.sbin/pwd_mkdb/pwd_mkdb.8 index 88e0e72a7a..2ab2cee6ca 100644 --- a/usr.sbin/pwd_mkdb/pwd_mkdb.8 +++ b/usr.sbin/pwd_mkdb/pwd_mkdb.8 @@ -38,7 +38,7 @@ pwd_mkdb \- generate the password databases .SH SYNOPSIS .nf .ft B -pwd_mkdb [ \-p ] file +pwd_mkdb [ \-p ] [ \-d directory ] file .SH DESCRIPTION .I Pwd_mkdb creates @@ -56,6 +56,9 @@ The options are as follows: .TP \-p Create a Version 7 style password file and install it into ``/etc/password''. +.TP +\-d +Store databases into specified destination directory instead of ``/etc''. .PP The two databases differ in that the secure version contains the user's encrypted password and the insecure version has an asterisk (``*'') diff --git a/usr.sbin/pwd_mkdb/pwd_mkdb.c b/usr.sbin/pwd_mkdb/pwd_mkdb.c index dd54b73da6..b90daee248 100644 --- a/usr.sbin/pwd_mkdb/pwd_mkdb.c +++ b/usr.sbin/pwd_mkdb/pwd_mkdb.c @@ -51,6 +51,7 @@ static char sccsid[] = "@(#)pwd_mkdb.c 5.5 (Berkeley) 5/6/91"; #include <limits.h> #include <stdio.h> #include <string.h> +#include <stdlib.h> #define INSECURE 1 #define SECURE 2 @@ -62,6 +63,7 @@ char *progname = "pwd_mkdb"; static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean; static struct passwd pwd; /* password structure */ static char *pname; /* password file name */ +static char prefix[MAXPATHLEN]; main(argc, argv) int argc; @@ -76,14 +78,19 @@ main(argc, argv) DBT data, key; int ch, cnt, tfd; char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024]; + char buf2[MAXPATHLEN]; + strcpy(prefix, _PATH_PWD); makeold = 0; - while ((ch = getopt(argc, argv, "pv")) != EOF) + while ((ch = getopt(argc, argv, "d:pv")) != EOF) switch(ch) { + case 'd': + strcpy(prefix, optarg); + break; case 'p': /* create V7 "file.orig" */ makeold = 1; break; - case 'v': /* backward compatible */ + case 'v': /* backward compatible */ break; case '?': default: @@ -113,15 +120,15 @@ main(argc, argv) error(pname); /* Open the temporary insecure password database. */ - (void)sprintf(buf, "%s.tmp", _PATH_MP_DB); - dp = hash_open(buf, O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE, NULL); + (void)sprintf(buf, "%s/%s.tmp", prefix, _MP_DB); + dp = dbopen(buf, O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, NULL); if (!dp) error(buf); clean = FILE_INSECURE; /* Open the temporary encrypted password database. */ - (void)sprintf(buf, "%s.tmp", _PATH_SMP_DB); - edp = hash_open(buf, O_WRONLY|O_CREAT|O_EXCL, PERM_SECURE, NULL); + (void)sprintf(buf, "%s/%s.tmp", prefix, _SMP_DB); + edp = dbopen(buf, O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, NULL); if (!edp) error(buf); clean = FILE_SECURE; @@ -255,13 +262,16 @@ main(argc, argv) (void)fclose(fp); /* Install as the real password files. */ - (void)sprintf(buf, "%s.tmp", _PATH_MP_DB); - mv(buf, _PATH_MP_DB); - (void)sprintf(buf, "%s.tmp", _PATH_SMP_DB); - mv(buf, _PATH_SMP_DB); + (void)sprintf(buf, "%s/%s.tmp", prefix, _MP_DB); + (void)sprintf(buf2, "%s/%s", prefix, _MP_DB); + mv(buf, buf2); + (void)sprintf(buf, "%s/%s.tmp", prefix, _SMP_DB); + (void)sprintf(buf2, "%s/%s", prefix, _SMP_DB); + mv(buf, buf2); if (makeold) { + (void)sprintf(buf2, "%s/%s", prefix, _PASSWD); (void)sprintf(buf, "%s.orig", pname); - mv(buf, _PATH_PASSWD); + mv(buf, buf2); } /* * Move the master password LAST -- chpass(1), passwd(1) and vipw(8) @@ -269,7 +279,8 @@ main(argc, argv) * The rename means that everything is unlocked, as the original file * can no longer be accessed. */ - mv(pname, _PATH_MASTERPASSWD); + (void)sprintf(buf, "%s/%s", prefix, _MASTERPASSWD); + mv(pname, buf); exit(0); } @@ -335,17 +346,17 @@ cleanup() (void)unlink(buf); /* FALLTHROUGH */ case FILE_SECURE: - (void)sprintf(buf, "%s.tmp", _PATH_SMP_DB); + (void)sprintf(buf, "%s/%s.tmp", prefix, _SMP_DB); (void)unlink(buf); /* FALLTHROUGH */ case FILE_INSECURE: - (void)sprintf(buf, "%s.tmp", _PATH_MP_DB); + (void)sprintf(buf, "%s/%s.tmp", prefix, _MP_DB); (void)unlink(buf); } } usage() { - (void)fprintf(stderr, "usage: pwd_mkdb [-p] file\n"); + (void)fprintf(stderr, "usage: pwd_mkdb [-p] [-d <dest dir>] file\n"); exit(1); } diff --git a/usr.sbin/quotaon/Makefile b/usr.sbin/quotaon/Makefile index 233cbb9037..a2e3697de9 100644 --- a/usr.sbin/quotaon/Makefile +++ b/usr.sbin/quotaon/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= quotaon -MAN8= quotaon.0 +MAN8= quotaon.8 MLINKS= quotaon.8 quotaoff.8 LINKS= ${BINDIR}/quotaon ${BINDIR}/quotaoff diff --git a/usr.sbin/repquota/Makefile b/usr.sbin/repquota/Makefile index dd965af7bb..7e7996345b 100644 --- a/usr.sbin/repquota/Makefile +++ b/usr.sbin/repquota/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= repquota -MAN8= repquota.0 +MAN8= repquota.8 .include <bsd.prog.mk> diff --git a/usr.sbin/rmt/Makefile b/usr.sbin/rmt/Makefile index 2cfcb9ed48..42b5c568ab 100644 --- a/usr.sbin/rmt/Makefile +++ b/usr.sbin/rmt/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 5.4 (Berkeley) 5/11/90 PROG= rmt -MAN8= rmt.0 +MAN8= rmt.8 .include <bsd.prog.mk> diff --git a/usr.sbin/routed/Makefile b/usr.sbin/routed/Makefile index 553b68b241..0fc8bc839c 100644 --- a/usr.sbin/routed/Makefile +++ b/usr.sbin/routed/Makefile @@ -3,7 +3,7 @@ PROG= routed SRCS= af.c if.c input.c main.c output.c startup.c tables.c timer.c \ trace.c inet.c -MAN8= routed.0 +MAN8= routed.8 SUBDIR= query trace DPADD= ${LIBUTIL} ${LIBCOMPAT} LDADD= -lutil diff --git a/usr.sbin/rwhod/Makefile b/usr.sbin/rwhod/Makefile index 607c2ca5fc..389936982b 100644 --- a/usr.sbin/rwhod/Makefile +++ b/usr.sbin/rwhod/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= rwhod -MAN8= rwhod.0 +MAN8= rwhod.8 DPADD= ${LIBUTIL} LDADD= -lutil diff --git a/usr.sbin/rwhod/rwhod.8 b/usr.sbin/rwhod/rwhod.8 index ad80f2c2a7..d2daeed2c7 100644 --- a/usr.sbin/rwhod/rwhod.8 +++ b/usr.sbin/rwhod/rwhod.8 @@ -126,7 +126,7 @@ Status messages are generated approximately once every performs an .Xr nlist 3 on -.Pa /vmunix +.Pa /386bsd every 30 minutes to guard against the possibility that this file is not the system image currently operating. diff --git a/usr.sbin/rwhod/rwhod.c b/usr.sbin/rwhod/rwhod.c index 41dfc20a08..0cf0ec39d9 100644 --- a/usr.sbin/rwhod/rwhod.c +++ b/usr.sbin/rwhod/rwhod.c @@ -67,7 +67,7 @@ static char sccsid[] = "@(#)rwhod.c 5.20 (Berkeley) 3/2/91"; */ #define AL_INTERVAL (3 * 60) -struct sockaddr_in sin; +struct sockaddr_in s_in; char myname[MAXHOSTNAMELEN]; @@ -155,9 +155,9 @@ main() syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); exit(1); } - sin.sin_family = AF_INET; - sin.sin_port = sp->s_port; - if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) { + s_in.sin_family = AF_INET; + s_in.sin_port = sp->s_port; + if (bind(s, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) { syslog(LOG_ERR, "bind: %m"); exit(1); } @@ -382,7 +382,7 @@ configure(s) char buf[BUFSIZ], *cp, *cplim; struct ifconf ifc; struct ifreq ifreq, *ifr; - struct sockaddr_in *sin; + struct sockaddr_in *s_in; register struct neighbor *np; ifc.ifc_len = sizeof (buf); @@ -458,8 +458,8 @@ configure(s) np->n_addr, np->n_addrlen); } /* gag, wish we could get rid of Internet dependencies */ - sin = (struct sockaddr_in *)np->n_addr; - sin->sin_port = sp->s_port; + s_in = (struct sockaddr_in *)np->n_addr; + s_in->sin_port = sp->s_port; np->n_next = neighbors; neighbors = np; } @@ -476,10 +476,10 @@ sendto(s, buf, cc, flags, to, tolen) { register struct whod *w = (struct whod *)buf; register struct whoent *we; - struct sockaddr_in *sin = (struct sockaddr_in *)to; + struct sockaddr_in *s_in = (struct sockaddr_in *)to; char *interval(); - printf("sendto %x.%d\n", ntohl(sin->sin_addr), ntohs(sin->sin_port)); + printf("sendto %x.%d\n", ntohl(s_in->sin_addr), ntohs(s_in->sin_port)); printf("hostname %s %s\n", w->wd_hostname, interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up")); printf("load %4.2f, %4.2f, %4.2f\n", diff --git a/usr.sbin/sendmail/CHANGES-R5-R8 b/usr.sbin/sendmail/CHANGES-R5-R8 new file mode 100644 index 0000000000..c62aba7485 --- /dev/null +++ b/usr.sbin/sendmail/CHANGES-R5-R8 @@ -0,0 +1,307 @@ + SUMMARY OF CHANGES BETWEEN SENDMAIL V5 and V8 + +The following is a summary of the changes between the last commonly +available version of sendmail from Berkeley (5.67) and the latest +version (8.1). I can't guarantee that it is complete. + + +Connection Caching + + Instead of closing SMTP connections immediately, those connections + are cached for possible future use. The advent of MX records made + this effective for mailing lists; in addition, substantial performance + improvements can be expected for queue processing. + +MX Piggybacking + + If two hosts with different names in a single message happen to + have the same set of MX hosts, they can be sent in the same + transaction. Version 8 notices this and tries to batch the messages. + +RFC 1123 Changes + + A number of changes have been made to make sendmail ``conditionally + compliant'' (that is, it satisfies all of the MUST clauses and most + but not all of the SHOULD clauses in RFC 1123). + + The major areas of change are (numbers are RFC 1123 section numbers): + + 5.2.7 Response to RCPT command is fast. + 5.2.8 Numeric IP addresses are logged in Received: lines. + 5.2.17 Self domain literal is properly handled. + 5.3.2 Better control over individual timeouts. + 5.3.3 Error messages are sent as From:<>. + 5.3.3 Error messages are never sent to <>. + 5.3.3 Route-addrs are pruned. + + The areas in which sendmail is not ``unconditionally compliant'' are: + + 5.2.6 Sendmail does do header munging. + 5.2.10 Sendmail doesn't always use the exact SMTP message + text from RFC 821. + 5.3.1.1 Sendmail doesn't guarantee only one connect for each + host on queue runs. + 5.3.1.1 Sendmail doesn't always provide an adequate limit + on concurrency. + +Extended SMTP Support + + Version 8 includes both sending and receiving support for Extended + SMTP support as defined by RFC 1425 (basic) and RFC 1427 (SIZE); + and limited support for RFC 1426 (BODY). + +Eight-Bit Clean + + Previous versions of sendmail used the 0200 bit for quoting. This + version avoids that use. However, for compatibility with RFC 822, + you can set option `7' to get seven bit stripping. + + Individual mailers can still produce seven bit out put using the + `7' mailer flag. + +User Database + + The user database is an as-yet experimental attempt to provide + unified large-site name support. We are installing it at Berkeley; + future versions may show significant modifications. + +Improved BIND Support + + The BIND support, particularly for MX records, had a number of + annoying ``features'' which have been removed in this release. In + particular, these more tightly bind (pun intended) the name server + to sendmail, so that the name server resolution rules are incorporated + directly into sendmail. + +Keyed Files + + Generalized keyed files is an idea taken directly from IDA sendmail + (albeit with a completely different implementation). They can be + useful on large sites. + + Version 8 also understands YP. + +Multi-Word Classes + + Classes can now be multiple words. For example, + + CShofmann.CS.Berkeley.EDU + + allows you to match the entire string ``hofmann.CS.Berkeley.EDU'' + using the single construct ``$=S''. + +Deferred Macro Expansion + + The $&x construct has been adopted from IDA . + +IDENT Protocol Support + + The IDENT protocol as defined in RFC 1413 is supported. + +Parsing Bug Fixes + + A number of small bugs having to do with things like backslash-escaped + quotes inside of comments have been fixed. + +Separate Envelope/Header Processing + + Since the From: line is passed in separately from the envelope + sender, these have both been made visible; the $g macro is set to + the envelope sender during processing of mailer argument vectors + and the header sender during processing of headers. + + It is also possible to specify separate per-mailer envelope and + header processing. The SenderRWSet and RecipientRWset arguments + for mailers can be specified as ``envelope/header'' to give different + rewritings for envelope versus header addresses. + +Owner-List Propagates to Envelope + + When an alias has an associated owner-list name, that alias is used + to change the envelope sender address. This will cause downstream + errors to be returned to that owner. + +Dynamic Header Allocation + + The fixed size limit on header lines has been eliminated. + +New Command Line Flags + + The -B flag has been added to pass in body type information. + + The -p flag has been added to pass in protocol information. + + The -X flag has been added to allow logging of all protocol in and + out of sendmail for debugging. + +Enhanced Command Line Flags + + The -q flag can limit limit a queue run to specific recipients, + senders, or queue ids using -qRsubstring, -qSsubstring, or + -qIsubstring respectively. + +New and Old Configuration Line Types + + The `T' (Trusted users) configuration line has been deleted. It + will still be accepted but will be ignored. + + The `K' line has been added to declare database maps. + + The `V' line has been added to declare the configuration version + level. + + The `M' (mailer) line takes a D= field to specify execution + directory. + +New Options + + Several new options have been added, many to support new features, + others to allow tuning that was previously available only by + recompiling. Briefly: + + b Insist on a minimum number of disk blocks. + + C Delivery checkpoint interval. + + E Default error message. + + G Enable GECOS matching. + + h Maximum hop count. + + j Send errors in MIME-encapsulated format. + + J Forward file path. + + k Connection cache size + + K Connection cache lifetime. + + l Enable Errors-To: header. These headers violate RFC 1123; + this option is included to provide back compatibility with + old versions of sendmail. + + O Incoming daemon options (e.g., use alternate SMTP port). + + p Privacy options. + + R Don't prune route-addrs. + + U User database spec. + + V Fallback ``MX'' host. + + 7 Do not run eight bit clean. + +Extended Options + + The `r' (read timeout), `I' (use BIND), and `T' (queue timeout) + options have been extended to pass in more information. + + The `A' (alias file) option has been extended to allow multiple + alias files of different types. + +New Mailer Flags + + a Try to use ESMTP. It will fall back to SMTP if the initial + EHLO packet is rejected. + + b Ensure a blank line at the end of messages. + + c Strip all comments from addresses; this should only be used as + a last resort when dealing with cranky mailers. + + g Never use the null sender as the envelope sender, even when + running SMTP. This violates RFC 1123. + + 7 Strip all output to this mailer to 7 bits. + +New Pre-Defined Macros + + $k UUCP node name from uname(2). + + $m Domain part of our full hostname. + + $_ RFC 1413-provided sender address. + +New LHS Token + + Version 8 allows `$@' on the Left Hand Side of an `R' line to match + zero tokens. This is intended to be used to match the null input. + +Bigger Defaults + + Version 8 allows up to 100 rulesets instead of 30. It is recommended + that rulesets 0-9 be reserved for sendmail's dedicated use in future + releases. + + The total number of MX records that can be used has been raised to + 20. + + The number of queued messages that can be handled at one time has + been raised from 600 to 1000. + +Different Default Tuning Parameters + + Version 8 has changed the default parameters for tuning queue costs + to make the number of recipients more important than the size of + the message (for small messages). This is reasonable if you are + connected with reasonably fast links. + +Auto-Quoting in Addresses + + Previously, the ``Full Name <email address>'' syntax would generate + incorrect protocol output if ``Full Name'' had special characters + such as dot. This version puts quotes around such names. + +Symbolic Names On Error Mailer + + Several names have been built in to the $@ portion of the $#error + mailer. + +SMTP VRFY Doesn't Expand + + Previous versions of sendmail treated VRFY and EXPN the same. In + this version, VRFY doesn't expand aliases or follow .forward files. + + As an optimization, if you run with your default delivery mode + being queue-only, the RCPT command will also not chase aliases and + .forward files. It will chase them when it processes the queue. + +[IPC] Mailers Allow Multiple Hosts + + When an address resolves to a mailer that has ``[IPC]'' as its + ``Path'', the $@ part (host name) can be a colon-separated list of + hosts instead of a single hostname. This asks sendmail to search + the list for the first entry that is available exactly as though + it were an MX record. The intent is to route internal traffic + through internal networks without publishing an MX record to the + net. MX expansion is still done on the individual items. + +Aliases Extended + + The implementation has been merged with maps. Among other things, + this supports NIS-based aliases. + +Portability and Security Enhancements + + A number of internal changes have been made to enhance portability. + + Several fixes have been made to increase the paranoia factor. + +Miscellaneous Enhancements + + Sendmail writes a /etc/sendmail.pid file with the current process id. + + Two people using the same program (e.g., submit) are considered + "different" so that duplicate elimination doesn't delete one of + them. + + The mailstats program prints mailer names and gets the location of + the sendmail.st file from /etc/sendmail.cf. + + Many minor bugs have been fixed, such as handling of backslashes + inside of quotes. + + A hook has been added to allow rewriting of local addresses after + aliasing. diff --git a/usr.sbin/sendmail/Makefile b/usr.sbin/sendmail/Makefile index 8bdeac2c5d..d75173b7ed 100644 --- a/usr.sbin/sendmail/Makefile +++ b/usr.sbin/sendmail/Makefile @@ -1,10 +1,27 @@ -# @(#)Makefile 4.16 (Berkeley) 5/11/90 +# @(#)Makefile 8.1 (Berkeley) 6/7/93 -SUBDIR= mailstats # praliases +SUBDIR= mailstats makemap praliases +VER= XX # don't trivially install sendmail .if !make(install) SUBDIR+=src .endif +tar: Files.base Files.cf Files.misc Files.xdoc + (cd src; ${MAKE}) + (cd doc; PRINTER=ps ${MAKE}) + (cd cf/cf; ${MAKE}) + pax -w -x tar -L -f sendmail.${VER}.base.tar `cat Files.base` + compress sendmail.${VER}.base.tar + pax -w -x tar -L -f sendmail.${VER}.cf.tar `cat Files.cf` + compress sendmail.${VER}.cf.tar + pax -w -x tar -L -f sendmail.${VER}.misc.tar `cat Files.misc` + compress sendmail.${VER}.misc.tar + pax -w -x tar -L -f sendmail.${VER}.xdoc.tar `cat Files.xdoc` + compress sendmail.${VER}.xdoc.tar + +ftp: sendmail.${VER}.base.tar.Z sendmail.${VER}.cf.tar.Z sendmail.${VER}.misc.tar.Z sendmail.${VER}.xdoc.tar.Z + rcp sendmail.${VER}.*.tar.Z RELEASE_NOTES barad-dur:/disks/barad-dur/ftp/sendmail/. + .include <bsd.subdir.mk> diff --git a/usr.sbin/sendmail/READ_ME b/usr.sbin/sendmail/READ_ME index b2f7ac5821..de494f0a47 100644 --- a/usr.sbin/sendmail/READ_ME +++ b/usr.sbin/sendmail/READ_ME @@ -1,9 +1,94 @@ /*- - * @(#)READ_ME 4.4 (Berkeley) 4/20/91 + * @(#)READ_ME 8.1 (Berkeley) 6/7/93 */ -aux Auxiliary programs, probably all out-of-date. -mail.local Source for mail.local(8) (the old /bin/mail). -doc Documentation. -rmail Source for rmail(8). + SENDMAIL RELEASE 8 + +This directory has the latest sendmail software from Berkeley. See +doc/op/op.me for a summary of changes since 5.67. + +Report any bugs to sendmail@CS.Berkeley.EDU. + +The latest version of sendmail is kept on FTP.CS.Berkeley.EDU, directory +/ucb/sendmail; check there for the latest revision. + +There are several related RFCs that you may wish to read -- they are +available via anonymous FTP to several sites, including nic.ddn.mil +(directory rfc), ftp.nisc.sri.com (rfc), nis.nsf.net (RFC), +nisc.jvnc.net (rfc), venera.isi.edu (in-notes), and wuarchive.wustl.edu +(info/rfc). They can also be retrieved via electronic mail by sending +email to one of: + + mail-server@nisc.sri.com + Put "send rfcNNN" in message body + nis-info@nis.nsf.net + Put "send RFCnnn.TXT-1" in message body + sendrfc@jvnc.net + Put "RFCnnn" as Subject: line + +Important RFCs for electronic mail are: + + RFC821 SMTP protocol + RFC822 Mail header format + RFC974 MX routing + RFC976 UUCP mail format + RFC1123 Host requirements (modifies 821, 822, and 974) + RFC1413 Identification server + RFC1341 MIME: Multipurpose Internet Mail Extensions + RFC1344 Implications of MIME for Internet Mail Gateways + +Other standards that may be of interest (but which are less directly +relevant to sendmail) are: + + RFC987 Mapping between RFC822 and X.400 + RFC1049 Content-Type header field (extension to RFC822) + +Unfortunately, for a variety of reasons the Makefiles are for the new +Berkeley "make" and will not work on the old, traditional make. I urge +you to get this make from Net2 (available on many public FTP archives). +Failing that, some directories have a "Makefile.dist" that will work on +older versions of make (but don't have the niceties included). + +Similar comments apply to the man pages -- they use the new Berkeley +-mandoc macros instead of the -man macros. You can get these from +Net2 as well. + +IF YOU WANT TO RUN THE NEW BERKELEY DB SOFTWARE: **** DO NOT **** +use the version that was on the Net2 tape -- it has a number of +nefarious bugs that were bad enough when I got them; you shouldn't have +to go through the same thing. Instead, get a new version via public +FTP from ftp.CS.Berkeley.EDU, file ucb/4bsd/db.tar.Z. This software +is highly recommended; it gets rid of several stupid limits, it's much +faster, and the interface is nicer to animals and plants. You will +also probably find that you have to add -I/where/you/put/db/include +to the sendmail makefile to get db.h to work properly. + +The structure of this directory tree is: + +cf Source for Berkeley configuration files. These are + different than what you've seen before. They are a + fairly dramatic rewrite, requiring the new sendmail + (since they use new features). +contrib Some contributed tools to help with sendmail. THESE + ARE NOT SUPPORTED by Berkeley -- contact the original + authors if you have problems. (This directory is not + on the 4.4BSD tape.) +doc Documentation. If you are getting source, read + op.me -- it's long, but worth it. +mailstats Statistics printing program. It has the pathname of + sendmail.st compiled in, so if you've changed that, + beware. This isn't all that useful. +makemap A program that creates the keyed maps used by the $( ... $) + construct in sendmail. It is primitive but effective. + It takes a very simple input format, so you will probably + expect to preprocess must human-convenient formats + using sed scripts before this program will like them. + But it should be functionally complete. +praliases A program to print the DBM version of the aliases file. + It hasn't been converted to understand the new Berkeley + DB format (which we are using). +rmail Source for rmail(8). This is used as a delivery + agent for for UUCP, and could presumably be used by + other non-socket oriented mailers. Older versions of + rmail are probably deficient. src Source for the sendmail program itself. diff --git a/usr.sbin/sendmail/RELEASE_NOTES b/usr.sbin/sendmail/RELEASE_NOTES new file mode 100644 index 0000000000..79846b83b4 --- /dev/null +++ b/usr.sbin/sendmail/RELEASE_NOTES @@ -0,0 +1,1310 @@ +8.3/8.3 93/07/13 + Fix (I hope) setuid problems introduced in 8.2 that caused + messages like "Cannot create qfXXXXXX: Invalid argument" + or "Cannot reopen dfXXXXXX: Permission denied". This + involved a new compile flag "HASSETEUID" that takes + the place of the old _POSIX_SAVED_IDS -- it turns out + that the POSIX interface is broken enough to break + some systems badly. This includes some fixes for + HP-UX. Also fixes problems where the real uid is + not reset properly on startup (from Neil Rickert). + Fix a problem that caused timed out messages to not report the + addresses that timed out. Error messages are also more + "user friendly". + Drop required bandwidth on connections from 64 bytes/sec to + 16 bytes/sec. + Further Solaris portability changes -- doesn't require the BSD + compatibility library. This also adds a new + "HASGETDTABLESIZE" compile flag which can be used if + you want to use getdtablesize(2) instead of sysconf(2). + These are loosely based on changes from David Meyer at + University of Oregon. This now seems to work, at least + for quick test cases. + Fix a problem that can cause duplicate error messages to be + sent if you are in SMTP, you send to multiple addresses, + and at least one of those addresses is good and points + to an account that has a .forward file (whew!). + Fix a problem causing messages to be discarded if checkcompat() + returned EX_TEMPFAIL (because it didn't properly mark + the "to" address). Problem noted by John Myers. + Fix dfopen to return NULL if the open failed; I was depending + on fdopen(-1) returning NULL, which isn't the case. This + isn't serious, but does result in wierd error diagnoses. + From Michael Corrigan. + CONFIG: add UUCP_MAX_SIZE M4 macro to set the maximum size of + messages sent through UUCP-family mailers. Suggested + by Bill Wisner of The Well. + CONFIG: if both MAILER(uucp) and MAILER(smtp) are specified, + include a "uucp-dom" mailer that uses domain-style + addressing. Suggested by Bill Wisner. + CONFIG: Add LOCAL_SHELL_FLAGS and LOCAL_SHELL_ARGS to match + LOCAL_MAILER_FLAGS and LOCAL_MAILER_ARGS. Suggested by + Christophe Wolfhugel. + CONFIG: Add OSTYPE(aix3). From Christophe Wolfhugel. + +8.2/8.2 93/07/11 + Don't drop out on config file parse errors in -bt mode. + On older configuration files, assume option "l" (use Errors-To + header) for back compatibility. NOTE: this DOES NOT + imply an endorsement of the Errors-To: header in any way. + Accept -x flag on AIX-3 as well as OSF/1. Why, why, why??? + Don't log errors on EHLO -- it isn't a "real" error for an old + SMTP server to give an error on this command, and + logging it in the transcript can be confusing. Fix + from Bill Wisner. + IRIX compatibility changes provided by Dan Rich + <drich@sandman.lerc.nasa.gov>. + Solaris 2 compatibility changes. Provided by Bob Cunningham + <bob@kahala.soest.hawaii.edu>, John Oleynick + <juo@klinzhai.rutgers.edu> + Debugging: -d17 was overloaded (hostsignature and usersmtp.c); + move usersmtp (smtpinit and smtpmailfrom) to -d18 to + match the other flags in that file. + Flush transcript before fork in mailfile(). From Eric Wassenaar. + Save h_errno in mci struct and improve error message display. + Changes from Eric Wassenaar. + Open /dev/null for the transcript if the create of the xf file + failed; this avoids at least one possible null pointer + reference in very wierd cases. From Eric Wassenaar. + Clean up statistics gathering; it was over-reporting because of + forks. From Eric Wassenaar. + Fix problem that causes old Return-Path: line to override new + Return-Path: line (conf.c needs H_FORCE to avoid + re-using old value). From Motonori Nakamura. + Fix broken -m flag in K definition -- even if -m (match only) + was specified, it would still replace the key with the + value. Noted by Rick McCarty of Texas Instruments. + If the name server timed out over several days, no "timed out" + message would ever be sent back. The timeout code + has been moved from markfailure() to dropenvelope() + so that all such failures should be diagnosted. Pointed + out by Christophe Wolfhugel and others. + Relax safefile() constraints: directories in an include or + forward path must be readable by self if the controlling + user owns the entry, readable by all otherwise (e.g., + when reading your .forward file, you have to own and + have X permssion in it; everyone needs X permission in + the root and directories leading up to your home); + include files must be readable by anyone, but need not + be owned by you. + If _POSIX_SAVED_IDS is defined, setuid to the owner before + reading a .forward file; this gets around some problems + on NFS mounts if root permission is not exported and + the user's home directory isn't x'able. + Additional NeXT portability enhancements from Axel Zinser. + Additional HP-UX portability enhancements from Brian Bullen. + Add a timeout around SMTP message writes; this assumes you can + get throughput of at least 64 bytes/second. Note that + this does not impact the "datafinal" default, which + is separate; this is just intended to work around + network clogs that will occur before the final dot + is sent. From Eric Wassenaar. + Change map code to set the "include null" flag adaptively -- + it initially tries both, but if it finds anything + matching without a null it never tries again with a + null and vice versa. If -N is specified, it never + tries without the null and creates new maps with a + null byte. If -O is specified, it never tries with + the null (for efficiency). If -N and -O are specified, + you get -NO (get it?) lookup at all, so this would + be a bad idea. If you don't specify either -N or -O, + it adapts. + Fix recognition of "same from address" so that MH submissions + will insert the appropriate full name information; + this used to work and got broken somewhere along the + way. + Some changes to eliminate some unnecessary SYSERRs in the + log. For example, if you lost a connection, don't + bother reporting that fact on the connection you lost. + Add some "extended debugging" flags to try to track down + why we get occassional problems with file descriptor + one being closed when execing a mailer; it seems to + only happen when there has been another error in the + same transaction. This requires XDEBUG, defined + by default in conf.h. + Add "-X filename" command line flag, which logs both sides of + all SMTP transactions. This is intended ONLY for + debugging bad implementations of other mailers; start + it up, send a message from a mailer that is failing, + and then kill it off and examine the indicated log. + This output is not intended to be particularly human + readable. This also adds the HASSETVBUF compile + flag, defaulted on if your compiler defines __STDC__. + CONFIG: change SMART_HOST to override an SMTP mailer. If you + have a local net that should get direct connects, you + will need to use LOCAL_NET_CONFIG to catch these hosts. + See cf/README for an example. + CONFIG: add LOCAL_MAILER_ARGS (default: `mail -d $u') to handle + sites that don't use the -d flag. + CONFIG: hide recipient addresses as well as sender addresses + behind $M if FEATURE(allmasquerade) is specified; this + has been requested by several people, but can break + local aliases. For example, if you mail to "localalias" + this will be rewritten as "localalias@masqueradehost"; + although initial delivery will work, replies will be + broken. Use it sparingly. + CONFIG: add FEATURE(domaintable). This maps unqualified domains + to qualified domains in headers. I believe this is + largely equivalent to the IDA feature of the same name. + CONFIG: use $U as UUCP name instead of $k. This permits you + to override the "system name" as your UUCP name -- + in particular, to use domain-ized UUCP names. From + Bill Wisner of The Well. + CONFIG: create new mailer "esmtp" that always tries EHLO + first. This is currently unused in the config files, + but could be used in a mailertable entry. + +8.1C/8.1B 93/06/27 + Serious security bug fix: it was possible to read any file on + the system, regardless of ownership and permissions. + If a subroutine returns a fully qualified address, return it + immediately instead of feeding it back into rewriting. + This fixes a problem with mailertable lookups. + CONFIG: fix some M4 frotz (concat => CONCAT) + +8.1B/8.1A 93/06/12 + Serious bug fix: pattern matching backup algorithm stepped by + two tokens in classes instead of one. Found by Claus + Assmann at University of Kiel, Germany. + +8.1A/8.1A 93/06/08 + Another mailertable fix.... + +8.1/8.1 93/06/07 + 4.4BSD freeze. No semantic changes. + +6.65/6.34 93/06/06 + Fix some lintish problems. + Fix some cases where server SMTP behaved poorly when handed bogus + input, pointed out by Eric Wassenaar. + CONFIG: fix some more (sigh) mailertable bugs -- thanks to + Motonori Nakamura of Kyoto University (again). + +6.64/6.33 93/06/05 + Don't send 050 (-v) information after the 250 response to a QUIT + command in srvrsmtp -- clients usually close the connection + at this point, and it causes bogus error messages. + Don't send messages that have errors on input (such as unbalanced + parentheses) during SMTP transactions, since a return + message has (probably) already been sent. + Give better diagnostics on timeouts during network reads, including + information similar to the SMTP phase. + Fix bug that caused SMTP messages to deliver synchronously; this + happened after the DATA 250, and hence caused reading the + next command to be delayed. + Ignore Errors-To: header unless 'l' (lower case el) header is + specified. The Errors-To: header violates RFC 1123. + Errors-To: was only needed to take the place of the + envelope sender in the days when most Unix mailers + didn't understand about the two kinds of senders. + Don't send warning messages in response to automatically generated + messages (that is, those From:<>). + CONFIG: fix some rather stupid typos in the mailertable code + pointed out by Motonori Nakamura of Kyoto University. + CONFIG: add confUSE_ERRORS_TO configuration option. + CONFIG: if ALWAYS_ADD_DOMAIN is selected, try to use $M + (masquerade name) instead of $j. + CONFIG: don't add dots to relay names (added in 6.29); it breaks + several things, and can be simulated by dot terminating + the names of relays. For example, use: + DBbit.net.relay. + (note the trailing dot). + +6.63/6.32 93/06/01 + Fix prototypes to eliminate chars in argument lists -- some + compilers are pissy about this. + Log protocol ($r) and body type if set so we can determine if + the adaptive algorithms are working. + Pessimize on locking of database files (particularly for NEWDB + databases) during opens. There were problems with + processes opening the file while it was rebuilt; since + NEWDB caches heavily, the reader opened an empty file, + which is an error. If your system has the ability to + lock atomically on open, this works properly; otherwise, + there are race conditions. + Check mod time on .pag file instead of .dir in NDBM aliases + because the .dir file doesn't get updated for small + alias files. From John Gardiner Myers of CMU. + More Solaris portability -- it now compiles on Solaris, but + hangs up in gethostbyname(). + Move setting of RES_DEBUG flag before first myhostname() call + so we can see name server traffic on that call. + Fsync() queue files. + Fix a problem that causes -bi to try to rebuild maps other than + the alias file(s). + Fix a problem that caused udb to reject entries from any but + the first database listed. + Rearrange doc subdirectory for 4.4BSD release tape. + CONFIG: put $r into the Received line. This was an oversight. + CONFIG: fix typo (call to ruleset 99 should have been rulset 90). + CONFIG: move "auxiliary" subroutines to be in ruleset 90-99 + range -- in the long run, single digit rulesets may + become reserved for builtin use by sendmail. + CONFIG: fix major problem that causes host aliases (that is, + anything in $=w != $j) to not be recognized. This has + been around since 6.30. + +6.62/6.31 93/05/28 + BETA RELEASE + Fix recursive syserr (if there is an error printing a syserr + message). This makes the code much less eager to consider + a write error as serious. This also includes some + heuristics to be clever about closed connections. + Lock NEWDB files during gets. This requires version 1.5 or later + of the db library. If you have an older version, you + can use -DOLD_NEWDB. This will go away in a few weeks. + Fix problem causing aliases that use host maps to get overwritten. + Do appropriate byte swapping on port numbers in ident protocol + code. Fix from Allan Johannesen of WPI. + Defer opening of map files to the same time as alias files so that + the daemon will tend to pick up new versions more promptly. + Prototype a bunch more functions. + Some Solaris 2.1 changes (still doesn't link though). + Try to simplify Makefiles by including more subordinate #defines + in conf.h (based on OS type). + CONFIG: check for domains if FEATURE(mailertable) is defined. + For example, if the host name is "knecht.cs.berkeley.edu" + it will search the following mailertable keys: + knecht.cs.berkeley.edu + .cs.berkeley.edu + .berkeley.edu + .edu + This could be used to replace the special relays for bitnet + and similar nets. + +6.61/6.30 93/05/24 + Fix problem that prevented appending dots on canonified host + names. This breaks tons of config files -- very + important fix. + Fix improper pointer dereference in response to HELO command. + Fix core dump if debugging set in map_rewrite. + CONFIG: add FEATURE(always_add_domain) to always attach the + local domain (only impacts local mail). + CONFIG: try to avoid turning names into $j -- although + technically a host can only have one "canonical name", + it seems to be common practice to have several. + +6.60/6.29 93/05/22 + Major change: merge alias databases with maps. This expands and + changes the map class interface but fixes a bunch of bugs. + The important user-visible change is that the file name + in a K line now does not include the ".db" extension; this + is added automatically. Also, the -d (NIS domain) flag is + missing from the K config line; use @domain instead. + When compiling, the *_MAP names are gone -- just compile + in NDBM, NEWDB, and/or NIS support. + Announce mailer/host/user triple on -bv flag -- from Brian + Bullen of Stirling University. + Don't send more than one line in response to HELO -- it confuses + Pony Express, which then behaves very badly. However, + this change does send two line 220 greetings, with the + second line reading "ESMTP spoken here". The usersmtp + module recognizes this and goes into ESMTP mode regardless + of the setting of the "a" mailer flag. Thus, "a" means + "always try EHLO". + AIX portability changes (thanks to Christophe Wolfhugel of + Herve Schauer Consultants (Paris) for providing me with + an INSA account for this purpose). Lightly tested. Use + -D_AIX3. This probably breaks compatibility with some + older systems (e.g., 4.2bsd) but still works on SunOS + 4.1.2, Ultrix 4.2A, HP-UX 8.07, OSF/1 T1.3, and AIX 3.2.3. + Fix a problem causing an error message loop if the output channel + is hosed. + Add the Makefiles that I use for various environments -- some are + Berkeley make versions and some are old make versions. + My makefile for the NeXT box has gotten lost, alas! + PRALIASES: support for printing NEWDB databases. From + Michael J. Corrigan of U.C. San Diego. + CONFIG: don't pass pseudo-domains to $[ ... $] (if you have + a wildcard MX it can have wierd results). From + Christophe Wolfhugel. + CONFIG: dot terminate relay hostnames in S0. From Christophe + Wolfhugel. + +6.59/6.28 93/05/13 + Log version with SMTP daemon startup message. + Adjust setproctitle to work on NetBSD and BSD/386. + Fix null pointer reference in MX fallback code. + A bunch of minor fixes from Eric Wassenaar: + If deliver cannot execv the mailer, return EX_OSERR + instead of EX_TEMPFAIL (to give better + error messages). + Consistently malloc e_message. + Catch degenerate case of calling returntosender() + with an empty returnq. + MIME reformatting. + +6.58/6.28 93/05/13 + Fix bug that can cause incorrect verbose display of user smtp + messages. + Disable SMTP VERB command if PRIV_NOEXPN is set (since this + could reveal the same information. + Allow failure when reading SMTP greeting message to go on to + next MX host. + Add "MIME-Version: 1.0" header if using MIME (this was NOT + included in RFC 1344, but Bill King of Allan-Bradley + Company forwarded me email from Nathaniel Borenstein + claiming that it was an inadvertent omission). + Don't use Content-Type: X-message-header. According to John + Myers of CMU, many MIME readers will completely ignore + the data if they don't recognize it. Instead, just + add a blank line to make it a legal (empty) message. + Fix problem causing dots to keep getting appended to cached + hostnames. This can cause buffer overrun conditions. + The problem was found by Erik Forsberg of Retix, + although I used a different bug fix than he provided. + Fix parsing of split header/envelope rewriting specs -- from + Eric Forsberg. + Fix from Eric Wassenaar to correct To: lists in error messages. + +6.57/6.28 93/05/11 + Fix minor glitch causing extra ctladdrs to be output to queue + file. Just an annoyance. + Cache results of name server canonification lookups to avoid + backed up queue runs. + Major rewrite of alias.c: considerable cleanup, plus sample + (untested) support for NIS aliases. The "A" option + can now be a comma separated list (or be repeated) -- + that is, you can have multiple alias databases. Each + database can have the syntax ``class:file''; if no class + is specified, the "implicit" class is assumed. Implicit + searches through a list of compiled in types -- hash, + dbm, nis, and stab. Alias files are searched in the + order they are listed. For example: + OAhash:/etc/aliases.local,/etc/aliases + OAnis:mail.aliases@my.nis.domain + first searches the hash database /etc/aliases.local, + then the regular /etc/aliases database, then the NIS + map "mail.aliases" in the NIS domain "my.nis.domain". + If in Verbose mode (probably from VERB command) run SMTP job + in foreground and don't do RCPT optimizations. + Add udb :mailsender as equivalent to owner- for regular aliases. + Delete option 8; add option 7 that means the opposite. That is, + default to 8-bit mode; a special option is needed to + force sendmail into 7 bit mode. + Send error messages in encapsulated MIME format. + New compile flag "NIS" that turns on NIS alias and NIS map + support. + Add "j" option to send error messages in MIME (RFC 1341) + encapsulated message format per RFC 1344. The + syntax is pretty ugly if you don't have MIME-aware + user agents. + Clean up message handling (for display in mailq output). + New setproctitle implementation for 4.4bsd. + Create files (such as ~/dead.letter) using mode FileMode (the + F option value) instead of 0666. + Fix bug causing output of EXPN command to not be fully qualified. + This may cause some problems with UUCP addresses that + will require some config file assistance -- specifically, + the $: part has to include the host name for this output + to make sense. + Fix a problem that sometimes diagnosed errors and still sent the + message if the header syntax was bad. + Fix a bug that caused an error message to be emailed when sendmail + was operating in -bv mode. + Add "ListenQueueSize" keyword to daemon options option (OO) to + set the queue size parameter passed to listen(). You + will normally have to tweak your kernel to up this. + Strip spaces off of beginning of message-id before logging (in + case it was folded across lines). + Tweak compile flags in daemon.c -- there were some cases where + it wouldn't work without NETINET. + Change *file* mailer to output all the usual default headers + (From, Date, Message-Id). It gets used when sending + back error messages. + CONFIG: explicitly catch and diagnose list:; syntax in ruleset + zero -- this is not a valid recipient syntax according + to RFC 821. + CONFIG: add confMIME_FORMAT_ERRORS to send error messages in + MIME format. Defaults to on. + CONFIG: add SMTP_MAILER_FLAGS and UUCP_MAILER_FLAGS to augment + the flags for those mailers. + +6.56/6.27 93/05/01 + Fix problem that causes the fallback mail to postmaster + (case ESM_POSTMASTER in savemail()) to not look at + aliases (ugh). + Some more HPUX tweaking (compile flag hpux => __hpux so it + still works in ANSI mode). + Don't try to flock non-regular files when mailing to a file. + In particular, this was a problem if you tried to + send to /dev/null. + Fix a wierd bug that can cause senders to be queued as + recipients if the name server is down when the mail + is initially sent. This hack just ignores sender + deletion (essentially, it sets the MeToo flag) if there + is a TEMPFAIL during processing of the sender address. + Obscure. + Fix a dangling else problem -- from Brian Bullen from University + of Stirling, UK. + Add the "b" mailer flag to force a blank line on the end of + messages. Some brilliant versions of /bin/mail insist + on this but do not add it themselves. + Add the "g" mailer flag to prevent user SMTP from sending + "MAIL From:<>". This is only intended to be a + transitional gesture, and should not be used if at + all possible. It appears that Berkeley and IDA + config files have always handled this properly; the + UK config kit apparently does not. + Don't lowercase and then capitalize header field names -- leave + them with original capitalization. Fixes from Bill + King of Allen-Bradley Company. + Further cleanup and improved reporting of error messages, + particularly conditions that cause messages to be + requeued for future delivery. + Tweak syslog priorities in some cases. + CONFIG: clean up route-addr on UUCP addresses. + +6.55/6.25 93/04/27 + HPUX 8.07 compatibility changes in getla() -- I had to make + these changes to get it to work at Berkeley, although + others seem to have been working before (???). + Various patches to XLA code. + Fix problem that causes setuid bit on files to be ignored from + SMTP or in queue runs. Problem noted by Jason Ornstein + of Under The Wire, Inc. + Fix problem that can cause CNAMEs to be ignored. + Generalize getmxrr to match local host in $=w instead of a + single name passed in. + Some cleanup from Eric Wassenaar: + Use FileMailer instead of ProgMailer in two places. + Eliminate duplicate 8th-bit stripping in commaize. + Fix a problem with mis-parsing of backslash escapes + under some circumstances. + NIS map fix (was always including trailing null character) + from Mike Glendinning of Ingres UK. + Add "a" mailer flag to try using ESMTP. It tries the EHLO + command and if that fails falls back to regular SMTP. + Also parses EHLO option keywords. If host supports + SIZE extension, this is added to the MAIL FROM: + command. + Extend "b" option to include a second value which is the + maximum message size this server is willing to accept. + For example, a value of "10/1000000" says that there + must be ten blocks free, and sendmail will reject + any message larger than one megabyte. + Some portability hooks for NeXT (this could be applicable + to Mach in general). You have to create an empty + file called "unistd.h" to get it to compile. + Adjust config values (MAXLINE, MAXATOM, and PSBUFSIZE) to + be more generous. + Add X400-Received: to the list of headers tagged with H_TRACE + in conf.c. From Bill King, Allen-Bradley Co. + +6.54/6.25 93/04/19 + Fix problem that caused redefinition of SMTP and QUEUE compile + flags. Pointed out by Jon Forrest of the Sequoia 2000 + project at Berkeley. + Properly handle \! hack -- it was treating host\!user as one + token (host!user) instead of three (host, !, user). + Fix from Eric Wassenaar of NIKHEF-H. + Fix compilation problem in getauthinfo() if IDENTPROTO is off. + Turn off DEFNAMES and DNSRCH when getting the hostsignature + (i.e., MX records) in level 1 configuration files; this + matches the old behaviour. From Nakamura Motonori of + Kyoto University. + Improve error message printing -- if sent through an alias, + error messages include the name of the alias in the + message. Unfortunately, in order to make this work + properly in queue runs, this changes the format of the + C line in the qf file. The relatively uselessness of + the previous information was pointed out to me by + Allan E Johannesen of WPI. + Add XLA compile flag to add hooks to Christophe Wolfhugel's + extended load average code. This is still in very early + form. For information regarding the guts of the xla + code, contact Christophe.Wolfhugel@grasp.insa-lyon.fr. + Additional hooks for detecting tempfails in rewriting rules + (that is, in map lookups). + +6.53/6.25 93/04/15 + Properly diagnose ruleset zero returning null (instead of a mailer + triple). From Nakamura Motonori of Kyoto University. + More generalization of socket code for other protocols. + Shorten timeouts on reverse name lookups -- since they are done + during connection establishment, long timeouts here can + cause higher level timeouts. This mainly serves to accept + mail from hosts that do not have proper reverse (PTR) DNS + records set up. + Reset e_statmsg before each mailer invocation to avoid bogus + messages in the log. + Redefine $r, $s, and $_ in error envelopes so you don't get + incorrect cruft in the error message. Problem noted by + Nakamura Motonori of Kyoto University. + Fix a problem that can cause failure to return errors to Postmaster + in certain cases. From Nakamura Motonori. + Fix a problem that can cause some systems to give duplicate error + messages when a bad syntax address such as "<a" is presented + to an SMTP server. It doesn't seem to occur on all + machines. From Nakamura Motonori. + Default IDENTPROTO off for Ultrix and HPUX, which apparently have + the interesting "feature" that when they receive a "Host + unreachable" message they closes all open connections to + that host. However, some firewall gateways send this message + if you try to connect to an unauthorized port, such as the + IDENT port (113). Thus, no email can be received from such + hosts. There is some evidence that versions of Ultrix before + 4.3 do not have this problem. Thanks to Tom Ivar Helbekkmo + for pointing out this behaviour to me and to Michael Corrigan + of U.C. San Diego for informing me about the HPUX problem. + Allow IPC mailers to return a colon-separated list of hosts in the + $@ clause; these are searched in order as though they were + MX records. + When sending an error report, print the list of addresses tagged + as bad. Requested by Allan E Johannesen of WPI. + Change map function calls to return a status code. This gets + passed back as the result of rewrite. Parseaddr marks + the address as a QUEUEUP address if the return code is + EX_TEMPFAIL. All this to queue properly if the name + server is down. This code is not well tested. This code + changes the interface to map lookup functions (a fifth + parameter, int *statp, is added). Feature requested by + Dan Oscarsson. + Don't delete quotes (in the dequote map) if there are spaces in + the string, since this would cause them to be replaced by + the SpaceSub character. + Accept BODY=8BITMIME on SMTP MAIL command. This isn't advertised + because the 8BIT to 7BIT translation doesn't exist yet. + This does add a "bodytype" field to both envelope and + queue file and a -B command line flag to pass the type in + during direct invocations. + Discard return error messages only on responses to responses to + responses, not on responses to responses. That is, the + algorithm is to try return to sender, then return to + postmaster, then discard. Previously it discarded + immediately if the return to sender pass failed. + CONFIG: back out change to hide unqualified hostnames behind %-hack. + This screws up local aliases and .forward files. + CONFIG: add FEATURE(nocanonify) to turn off calls to $[ ... $]; + some sites only handle completely canonified names. + Requested by John Gardiner Myers of CMU. + CONFIG: some UUCP code was still included even if FEATURE(nouucp) + was specified. + +6.52/6.24 93/04/10 + Clean up some minor glitches on error return messages pointed out + by Nakamura Motonori of Kyoto University. + Fix reply() to not reset SmtpReplyBuffer on fatal errors; this + was supposed to reset SmtpMsg Buffer. This makes the + client side code virtually useless. Reported by Allan + E Johannesen of WPI and Phil Brandenberger of Swarthmore. + Better debug messages if fuzzy is disabled, suggested by Allan + E Johannesen of WPI. + Offset SmtpReplyBuffer by four in usersmtp when checking for + loopback. From Eric Wassenaar. + Don't set $s until after runinchild in srvrsmtp -- otherwise + it gets cleared. From Eric Wassenaar. + Implement IDA-style $&x for deferred macro expansion. + More POSIX compatibility. + CONFIG: Hide unqualified hostnames behind %-hack using $s as the + actual sender. This is only done if $r is non-null, that + is, if this is not locally submitted mail. + CONFIG: Add FEATURE(bitdomain) allowing mapping of BITNET host + names to internet domains. A program contributed by + John Gardiner Myers of CMU to create the maps is included + in the contrib directory (in the "misc" tar file). + CONFIG: Add FEATURE(uucpdomain) for a similar mapping for UUCP + hosts. There is currently no tool to create this map. + +6.51/6.23 93/04/04 + Add D= mailer flag to specify a path of possible working directories + in which to execute the mailer. This is intended for the + prog mailer; some shells can get upset if they don't have + access to the current directory. + Add RFC 1413 (IDENT) protocol support. This is only very loosely + tested. This adds a $_ macro to be the authenticated + info (in ``user@domain [address]'' form) and debug flag + 9 to trace the protocol. + Check for loopbacks in usersmtp instead of srvrsmtp -- there is no + reason for a local agent to not be talking to the localhost + (although the inverse is not true). + Add a few hooks for automated map rebuilding. This is certainly + not done yet. + CONFIG: Have prog mailer specify a path of ``D=$z:/'' -- that is, + user's home directory then the root. + CONFIG: Log RFC 1413 identification in Received: line. + +6.50/6.22 93/04/01 + Fixes to requeueing code to make it compute priority, nrcpts, + and the like properly. + +6.49/6.22 93/04/01 + Diagnose incorrect privacy flags. Suggested by Bryan Costales + of ICSI. + Some ANSI C fixes. + Arrange to quote backslashes as well as other special characters + in the phrase part of a route-addr. + Some fixes to FallBackMX code suggested by Nakamura Motonori of + Kyoto University. + More vigorous zeroing of CurHostAddr to avoid logging of bogus + host addresses when you are actually just printing + information from the MCI structure; problem noted by + Michael Corrigan of U.C. San Diego. + Don't ignore rest of queue if any job is not runnable. This can + also cause an incorrect job to be lost. Fix from + Eric Wassenaar. + Always respond "quickly" to RCPT command; do alias expansion and + the like later. This also means that mail for lists that + have errors will be acccepted, and an error sent back + later. This is done by instantiating the queue file + and then immediately running and requeueing it. + +6.48/6.22 93/03/30 + Fix incorrect diagnosis of infinite loop in ruleset. Problem noted + by several people. + Improve information printed when infinite loops are discovered. + Zero CurHostAddr to fix erroneous internet addresses in log when no + addresses can be bound. Pointed out by Nakamura Motonori + of Kyoto University. + "Probe" SMTP connections using RSET instead of NOOP "just in case". + Suggested by John Gardiner Myers of CMU. + Don't warn about -f if you are setting sender to yourself. + +6.47/6.22 93/03/29 + Fix incompatible call to endmailer in smtpquit which causes core + dumps. Noted by Allan E Johannesen of WPI. + HPUX portability changes from Michael J. Corrigan of UC San Diego. + Require MAIL before RCPT command in srvrsmtp.c. This had been + intentional from the 821 draft days when the order wasn't + clear, but is silly now. + Fix bug in nis_magic routine that was initializing parameters + incorrectly. Fix from Takahiro Kanbe of Fuji Xerox + Information Systems Co., Ltd. + Change default for PrivacyFlags in conf.c to 0 -- since it always + "or"s in new values, there was no way to turn off the + AuthWarning stuff. + Add O option to set SMTP daemon options. + Add V option to set fallback MX host. This always sorts at lower + priority than anything it gets from the name server. It + should only be used for environments with very bad network + connectivity. Requested by several people. + Log sending info. It's not clear this is a good idea. + CONFIG: fix typo in mailertable code. Noted by Phil Brandenberger + of Swarthmore. + CONFIG: add confDAEMON_OPTIONS and confFALLBACK_MX to set options + O and V, respectively. + +6.46/6.21 93/03/26 + Fix botch in server SMTP that broke transactions that did not + use HELO first (like MH). Fix from Michael Corrigan + of U.C. San Diego. + Fall back to other MX records if there is an error anywhere + in delivery (actually on MAIL or DATA -- RCPT is harder). + Suggested by John Gardiner Myers and Nakamura Motonori. + Revert to non-prototypes -- it turns out that our ANSI C + compiler is more forgiving than most others about + mixing prototyped extern declarations with non-prototyped + function definitions. + Fix a problem with multi-word class matching pointed out by + Neil Rickert. Given: + CX b a.b.c + R$+ $=X $+ $: $1 < $2 > $3 + the input "user@a.b.c" failed instead of being properly + rewritten as "user@a.<b>.c". + Neil also convinced me that it was correct that $~ should match + only one token -- the problem is that it's always possible + to add another token, so $~ matches far too eagerly. + +6.45/6.21 93/03/25 + Implement multi-word classes (properly!). + +6.44/6.21 93/03/25 + Add X-Authentication-Warning: headers to clue users into possible + attempts to forge mail. This is on the authwarnings + privacy flag, but is the default. Suggested by Bryan + Costales of ICSI. + Pass default units for convtime in so they can be more reasonable. + Allow config files to always add a new Comments: header (i.e., + they will be added even if an old one already exists). + Suggested by Bryan Costales of ICSI. + Allow config files to delete an existing Return-Path: header. + These should only be added at final delivery. Suggested + by Bryan Costales of ICSI. + Some debugging additions. Suggested by Bryan Costales of ICSI. + Clean up logging of Family 0 addresses. Noted by David Muir + Sharnoff and others. + Add a "dequote" map class. This allows config files to strip + quotes off of addresses. Note that this is not a builtin + map, just a class -- so you have to define the map + using the K line. + Fix a bug in the queueup() loop getting a locked tf where in + very odd cases it can fall off the bottom and core dump. + Of course, it was P{r Emanuelsson who found it.... + Open a new transcript when splitting an envelope. Problem found + by Allan E Johannesen of WPI. + Improved error output in endmailer if the mailer core dumps. + CONFIG: Fix typo in UUCP mailer definition. + CONFIG: Default several of the new options on: eight bit input, + privacy flags set to "authwarnings", and message warning + set to 4h. + CONFIG: Use dequote map. + +6.43/6.20 93/03/23 + Fix problem with assumption of an sa_len field in a generic + sockaddr -- it turns out that most vendors haven't + picked up this (very important) fix. + Change compilation flags for daemon code -- select one or both + of NETINET or NETISO, but don't ever set DAEMON manually. + CONFIG: add FEATURE(mailertable) to do IDA-style mailertables. + +6.42/6.19 93/03/19 + Use Postmaster as default fallback return address, not root. + POSIX changes for file descriptor handling. + Diagnose errors writing new queue file. + If you change the owner using an owner- alias, also change the + error mode to EM_MAIL so that errors don't get dropped + into an inappropriate directory. Problem noted by + Allan E Johannesen of WPI. + If you are su'ed to root, send email as who you really are, not + as root. From Brian Kantor of U.C. San Diego. + Allow warning messages to be sent after a configurable interval + has passed without delivery. The message is sent only + once per envelope. This changes the format of the qf + file to have an F line, and the format of the T option + to accept take the format "return/warn" (both intervals). + Don't force all local names to lower case -- this was left over + from the wierd handling of case mapping on aliases. It + is now driven (as expected) by the "u" mailer flag. + Problem noted by P{r Emanuelsson. + Fix problem that caused headers on returned email to be trashed; + they were getting freed, but are still accessible via + BlankEnvelope. + Fix problem that caused bogus ids to be created on returned + mail. + Add support for ISO and other non-INET networking. This is by + no means finished yet. This does assume a lot of other + system support, like a version of gethostbyname that + returns non-AF_INET addresses. + CONFIG: change default on prog mailer to keep upper case in + user names (i.e., in the program command line). + CONFIG: strip trailing dots off of hosts in uucp mailer before + convert to bang format. + CONFIG: create new "relay" mailer for $R (LOCAL_RELAY) and $H + (MAIL_HUB) delivery that doesn't add local domain. Note + that this violates 821, but is probably "more correct" + for what we are trying to do. Problem pointed out by + Michael Graff of Iowa State. + +6.41/6.18 93/03/18 + Clean up unnecessary creates of queue ids (i.e., empty qf files) + when not needed, such as when starting up an SMTP + connection. + Fix problem where split envelopes aren't instantiated in the queue. + This is quite a serious bug. + Owner- aliases had problems with leading spaces causing a + premature delimitation. + +6.40/6.18 93/03/18 + Have ending 250 (after DATA) include the id; suggested by + Brian Kantor of UC San Diego. + Add logging on envelope splitting. + Change queue ids to have one more letter encoding the hour of + the day so that during a single day there is a greater + likelihood of uniqueness; requested by Brian Kantor. + +6.39/6.18 93/03/18 + Fix minor compile problem if LOCKF is defined. + Define size of tobuf in conf.h. Observed by Toshinari Takahashi + of Toshiba. + Restore e_sender -- this is equivalent to e_from.q_paddr without + decorations such as angle brackets and comments. + OSF/1 on Alpha changes from Allan E Johannesen of WPI. + CONFIG: fix typo in S3 for list syntax (;: => :;). Thanks to + Christopher Hoover for noting the problem. + +6.38/6.17 93/03/17 + Pass envelope to disconnect to avoid another use of CurEnv, which + can apparently end up being null at inopportune times. + Log "received from" as "relay=" for consistency (suggested by + John Gardiner Myers). + Fix major bug in header handling: if no From: line existed in + the header (so sendmail inserts one), and the sender is + an alias that has an owner, the From: line shows the + owner (as well as the envelope). Fixed by early binding + the headers (which will change debugging output). + HPUX portability patches from Michael J. Corrigan of UC San Diego. + Some attempts to adapt better to out of open file conditions. + Some changes to ctladdr handling in queue files. + +6.37/6.17 93/03/16 + MAJOR CHANGE: delete e_sender and e_returnpath (why are these + different from e_from?) and $< macro. + Log correct IP address in relay= field even if the connection + times out. + Log "received from [RESPONSE]" on EF_RESPONSE messages (from + John Gardiner Myers). + Fixes to SysExMsg logging (sometimes just got "message: %s" + instead of "message: error message"), noted by Eric + Wassenaar. Also reported by Nakamura Motonori. + Improvements to MX piggybacking code, from Nakamura Motonori. + Fix case where CurHostName points to an auto variable that has + been deallocated (from Nakamura Motonori). + Fix bug causing newlines to be included in aliases if option + "n" (check alias RHS) is set; bug noted by David Muir + Sharnoff. + Fix problem causing user names that should be mapped to lower + case to not be mapped if they are sent during a queue + run. This greatly simplifies the case mapping code. + Problem noted by Allan E Johannesen of WPI. + Don't do recipient address rewriting in buildaddr. This + improperly did recipient rewriting on sender addresses, + and just seems bogus in general -- but the change could + break some .cf files. + Pass TZ envariable to child processes for System V. + CONFIG: allow LOCAL_RULE_1 and LOCAL_RULE_2 if you want to + define those rulesets. + KNOWN PROBLEM: I have seen some problems on SunOS that causes + the User Data Base to give errors on some addresses. I + have tracked the problem back at least as far as 93.02.15 + (version 6.22). Running with debugging on makes it + go away, so I conclude that it is referencing uninitialized + stack data. I haven't been able to track this down yet. + +6.36/6.16 93/03/08 + Allow local mailer to specify $@host -- this lets you assign the + "foo" part of jgm+foo to $h for passing in to the local + mailer. + Additional debug printing in getcanonname (show query type). + Don't add the e_fromdomain on sender addresses -- this interacts + wierdly with the owner- code. + Improve delivery logging to not log obvious or meaningless stuff. + Include numeric IP address in Received: lines per RFC 1123 section + 5.2.8. + Fixed a bug in checking stat() return value if restrictmailq is + set. Also, check the entire group set instead of just the + primary group. Both from John Gardiner Myers. + Don't have usrerr automatically print errno, since this is often + misleading. + Use transienterror() in makeconnection after connect() fails and + in openmailer after execve() fails (from Eric Wassenaar). + Also moved transienterror() from util.c to conf.c. + Clean up from= logging on response messages. + Undo patch allowing prescan to return a null vector -- it breaks + too many things. + Config: FEATURE(notsticky) lets you use UDB for everything coming + in to the machine, even if it is specifically targetted + to this machine. Without it, UDB is bypassed if the user + name is fully qualified. + Config: fix another minor botch with <> (local mailer wasn't + mapping them properly). + +6.35/6.15 93/03/05 + Fix getrealhostname to return null if sinlen <= 0 -- this can + occur if stdin is a pipe. + Avoid infinite loop in getcanonname if name server return + NO_DATA (for example). + Config: avoid having C flag qualify list syntax and error syntax. + +6.34/6.14 93/03/05 + Fix logging in deliver to not pass too many parameters to Ultrix + versions of syslog. + Don't write the pid file until after the daemon has actually + opened and conditioned the connection. + Consider addresses "different" if their q_uids differ (so that + two users forwarding to the same program will be seen + as different, rather than the same). + Fix problem with bad parameters in main() -- they set ExitStat + but don't exit. + Fix null pointer references through RealHostName -- painfully + discovered by Allan E Johannesen of WPI. + Fix bug causing user@@localhost to core dump (yuch). + Config: don't put two @host.dom.ain on users in $=E in SMTP + mailer. Also, catch user@ (no host) in ruleset 0. + +6.33/6.13 93/03/03 + Config: add confCW_FILE as the name of the cw configuration file + (defaults to /etc/sendmail.cw). From P{r Emanuelsson. + Allow prescan to return a pointer to an empty list -- this is + not an error. Also, clean up error reporting to avoid + double errors (prescan reports once, then the caller + reports again). + Changes to avoid trusting T_ANY queries -- run them, but if you + don't get the info you expected, do T_A and T_MX queries + anyhow. This also fixes an oversight where _res.options + bits were being ignored. + If PRIV_NOVRFY is set, use 252 response code instead of 502 per + RFC 1123 section 5.2.3. It's not 100% clear that this + is correct, but it probably works better with stupid + mailers that do a VRFY and only check the first digit. + +6.32/6.12 93/03/02 + Fix uninitialized variable "protocol" in smtp code. + Include <unistd.h> in sendmail.h -- move towards POSIX/ANSI. + Additional hooks for RFC 1427 (ESMTP SIZE extension). This + includes requiring that enoughspace() know the system + block size, which will undoubtedly break most ports. + Trace flag 19 in use for srvrsmtp.c. + Additional logging -- notably the sending mailer name. This + also changes the delivery logging to strict field=value + syntax. + Fix some problems with messages getting sent even to addresses + that had been marked bad -- from Eric Wassenaar. + More WIDE changes: accept host name inside [...] as non-MXed + host. This is intended ONLY for use inside firewalled + environments, where the MX points at the gateway. + Change .cf file conventions so that mapping for <> addresses + don't have an @ in them (to avoid confusing the C mailer + flag). Pointed out by Neil Rickert. + Config extensions for Sam Leffler's FlexFAX software. + +6.31/6.10 93/02/28 + Fix some more bugs in alias owner code -- there were some wierd + cases where an error in a non-aliased name would override + the return info in an aliased name with an owner. + Changes from WIDE Project, forwarded to me by Nakamura Motonori: + Log actual delivery host (after MX et al); from + yasuhiro@dcl.co.jp. + Log daemon startup. + Deliver Postmaster copies without a body. + Better logging of SMTP senders. + Send all program email as daemon even when local. + As requested in various forms from many people, accept -qIstring + to limit queue runs to jobs with queue-id matching string. + Similarly for -qRstring for recipients, -qSstring for + senders. + Initial hooks for ESMTP support (see RFC 1425). + Fixed a syntax error in the UUCP mailer specification that caused + core dumps on startup. + Check for missing A= or P= arguments in mailer definitions. + +6.30/6.10 93/02/27 + Require FROZENCONFIG compilation flag to include frozen + configuration code. Frozen configuration is really + not a very good idea any more, particularly in shared + library environments. + Do better checking of errno after opens of :include: and .forward + files to defer delivery on network and other transient + errors. Suggestion from Craig Everhart. + Fix minor botch in read timeout macro processing. + Add FEATURE(nouucp) to config files for sites that know absolutely + nothing about UUCP. + Add built cf files to distribution tape and clarify how to build + them if you don't have the Berkeley make. + Some sizeof(long) portability changes for the Alpha, from Allan + E Johannesen. + Add "restrictmailq" privacy flag -- if set, only people in the same + group as your queue directory can print the queue. If you + set this, be sure you also restrict access to log files.... + Fix another bug in owner-list stuff that can cause data files to + be "lost". + Fix a bug with queue runs that cause forwards to yourself to go + into alias/forwarding loops. I'm still iffy about this + fix. + Fix from Eric Wassenaar for suppression of return message code. + +6.29/6.9 93/02/24 + Fix yet another problem in alias owner code -- put the wrong return + address on the enclosed return-to-sender letter. + +6.28/6.9 93/02/24 + Fix botch in alias owner code that caused it to not operate if the + error was detected locally. + +6.27/6.9 93/02/24 + M_LOCAL => M_LOCALMAILER to avoid conflict with Ultrix include + file <sys/mount.h>. + Miscellaneous bug fixes from Eric Wassenaar: + sendmail -bv -t logs the from line even though in verify + mode only. + sendmail -v can go into queue mode if shouldqueue returns + TRUE. + Add route-addr pruning per RFC 1123 section 5.3.3. This can be + disabled using the "R" option. + Delete (always undocumented) -R flag (save original recipients); + there are ways to syslog(3) these now. + Clean up SMTP reply codes -- specify them as needed in the code, + instead of in conf.c -- this was needed during the NCP to + TCP transition, but seems silly now. This also changes + parameters to message and nmessage. + Have mailstats read the .cf file to find the sendmail.st file and + get text versions of mailer names. An initial version of + this code was provided by Tuominen Keijo (although the + comments indicate the good bits were written by "E.V."). + Add yet more System V compatibility hacks. + Fix bug in VRFY code (assumes everything must be a local user). + Allow specification of any of the hard-wired pathnames in the + Makefile. + Delete concept of "trusted users" -- this really didn't provide + any security anyway, and caused some problems. + Delete last vestige of support for the word "at" as an equivalent + to the character "@". + Propagate owner-foo alias information into the envelope sender. + Based on code from John Gardiner Myers. This is a major + semantic change -- beware! + Allow $@ on LHS to indicate "match zero" -- this is used to match + the null expression. + +6.26/6.8 93/02/21 + Don't "lose" queue runs. Very important fix from (who else?) + Eric Wassenaar. + Completely reset state on RSET command -- from Eric Wassenaar. + Send error messages and return receipts using an envelope sender + of <> regardless of the setting of $n. Rewriting rules + can undo this if they feel the necessity, as might be + needed for networks that don't understand the syntax. + This is permitted by RFC 821 section 3.6 and required by + RFC 1123 section 5.3.3. THIS REQUIRES VERSION 4 CONFIG + FILES because the rulesets must be able to parse <> + properly. + Don't ever send error messages to "<>" -- they will get sent to + the local postmaster or dumped in /usr/tmp/dead.letter + instead. Per RFC 1123 section 5.3.3. + Explicitly check for email to yourself as a dotted quad. You + have to call $[ [ ... ] $] to get this. + Up the message timeout to five days per RFC 1123 section 5.3.1.1. + Make all read timeouts individually configurable, as strongly + recommended by RFC 1123 section 5.3.2. + Use f_bavail (blocks available to regular users) instead of f_bfree + (blocks available to superuser) in free block checks. + Change $d macro to be the current time, not the origination time, + since this is consistent with how it is used now. + Generalization of enoughspace from Eric Wassenaar covering + SGI, Apollo, HPUX, Ultrix, and SunOS. + Ignore process group signals -- some front ends can do this if + you kill a window too quickly. From Eric Wassenaar. + Change umask to 022. + +6.25/6.8 93/02/20 + Close all cached connections before calling mailers and after + forking for delivery (caused double closes which resulted + in false errors). + Add FEATURE(redirect) in config files -- this allows you to alias + old addresses to a pointer to the new address that will + give a 551 error message, but not deliver the mail. + Some code changes to make the 551 errors look pretty. + Names of M4 program paths in config files have changed -- they + are all XXX_MAILER_PATH now, to match XXX_MAILER_FLAGS. + Fix a bug in the QSELFREF code having to do with empty .forward + files, reported by Eric Wassenaar. + Add option "p" (privacy flags); this allows you to tune how + picky the SMTP server will be. This also adds the + confPRIVACY_FLAGS M4 macro in the config files. + Add option "b" (minimum blocks free). If there are fewer than + this number of blocks free on the filesystem containing + the queue directory, the SMTP MAIL command will return + a 452 response and ask you to try again later. This + also adds the confMIN_FREE_BLOCKS M4 macro in the config + files. + Made VRFY just verify (doesn't expand aliases and .forward files); + EXPN does full expansion. RCPT in queue-only mode also + doesn't chase aliases and .forward. + +6.24/6.7 93/02/19 + Increase the number of domain search entries in domain.c to allow + for the extra "" entry indicating the root domain. + Reported by Nakamura Motonori of Kyoto U. + Add a "SMART_HOST" in the configs for UUCP-connected sites that + want to forward all mail with extra "@"s to that site. + Also allows SMART_HOST, LOCAL_RELAY, and MAIL_HUB to + be specified as ``mailer:hostname'' to use an alternate + mailer. + Clarified and updated some wording in the Operations Guide. + Add the "c" mailer flag -- this suppresses all comment parts of + addresses (requested by John Curran of NEARnet). + Have -v print prompts in -bt mode even if stdin is not a terminal + (default behaviour is to be silent if not reading from + a terminal). Suggested by Bryan Costales, ICSI. + Move the metacharacters from C0 space (\001-\037) into C1 space + (\201-\237). This also fixes a bunch of potential bugs + with G1 characters (\240-\276) in headers relating to + negative numbers passed to isspace() et al. + Add YP_LAST_MODIFIED and YP_MASTER_NAME to DBM version of alias + database if YPCOMPAT is #defined. Enhancement from + Takahiro Kanbe of Fuji Xerox Information Systems Co., Ltd. + Add "list" Precedence (-30); this can be used with old sendmails + which will map to precedence 0 (which will return error + messages). Suggested by Stephen R. van den Berg. + Many bug fixes from Eric Wassenaar of the National Institute for + Nuclear and High-Energy Physics, Amsterdam: + Clear timeouts properly on open failures in include(). + Don't dereference through NULL if no home directory found. + Re-establish SIGCHLD signal on System 5 in reapchild(). + Avoid NULL pointer reference on -pFOO flag. + Properly handle backslash escapes in comments. + Correctly check reply status on SMTP NOOP command. + Properly save SMTP error message if peer gives + "Service Shutting Down" message. + Avoid writing to the transcript if it couldn't be opened. + Signal errors in SMTP children to parent properly. + Handle self references in a list more globally (include a + QSELFREF bit in the address flags). This enhancement + was suggested by Eric Wassenaar. + Use initgroups() in hpux, even though it's System-V based. The + HASINITGROUPS compile flag can set this on other systems. + This HPUX behaviour was pointed out by Eric Wassenaar. + +6.23/6.6 93/02/16 + Clean up handling of LogLevel to make it easier to figure out + what's on what level. + Change log levels to have some consistency: + 1 serious system failures, security problems + 2 lost communications, protocol failures + 3 other serious failures + 4 minor errors + 5 message collection + 6 vrfy logging, creation of return-to-sender + 7 delivery failures + 8 delivery successes + 9 delivery tempfails (queue ups) + 10 database expansion + >64 debugging + Allow IDA-style separated processing on S= and R= in Mailer + definition lines. Note that rulesets 1 and 2 are + still used for both addresses as before. Bruce Lilly + gave a convincing argument that RFC976 insists on + this behaviour. + Added some time zones to arpatounix -- they may not be in the + standards, but they are in use. However, I may delete + arpatounix entirely -- there appears to be no reason + for it to exist. + Change to UUCP mailer (in cf directory) to try to do a saner job. + I'm still not certain about this mailer in general. + +6.22/6.5 93/02/15 + Fix bug that prevents saving letters in ~/dead.letter. + Don't add angle brackets in VRFY command if angle brackets already + exist in the address. + Fix bogus error message in udbexpand. + Null terminate host buffers in buildaddr (broken in 6.21) -- + IMPORTANT FIX!! + +6.21/6.5 93/02/15 + Fix another incorrect error message in alias.c, found by Azuma + Okamoto. + Fix a couple of problems in the more-configurable config files, + found by Tom Ivar Helbekkmo. + Fix problem with quoted :include: entries. + Don't duplicate the filename on verbose printing of .forward and + :include: contents. + Extend size of prescan buffer (to allow bigger addresses). Also, + detect some buffer overflows. + Log user SMTP protocol errors (log level 4). + +6.20/6.4 93/02/14 + Fix another problem in the MCI state machine caused when there + were errors generated from the other end to commands + other than RCPT. + +6.19/6.4 93/02/14 + Include load average support for DEC Alpha running OSF/1. + Fix multiple-response problem with errors in MAIL From: line. + Fix SMTP reply codes for invalid address syntaxes (give 501; + never give multiple error messages for a single message). + Fix problem where a cached connection timeout rejects all + later connects to that host. + Fix incorrect error message if alias.c is compiled with DBM only. + Additional changes to fix nested conditionals (from Bruce Lilly). + Recover more gracefully from operating system failures, particularly + NULL returns from openmailer (from Noritoshi Demizu, + OMRON Corporation). + Log forward, alias, and userdb expand operations on log level 10; + concept suggested by P{r (Pell) Emanuelsson. + Changes for HPUX 8.07 compatibility. + +6.18/6.4 93/02/12 + Allow any config option to be set using an M4 define. + Change UNAME compile flag to HASUNAME for IDA compatibility + (besides, it's a better name). + Note in README that on SunOS it must be linked -Bstatic. + Fairly major change in domain.c to handle wildcard MX records + more rationally. NOTE: the "w" option (no wildcard MX + records match local domain) has been eliminated. + Fix some unset variable references pointed out by Bruce Lilly. + Fix host name in process titles when using cached connection. + +6.17/6.3 93/01/28 + Fix System 5 compatibility changes to be compatible with the rest + of the world. + +6.16/6.3 93/01/28 + Experimental fix for problem handling errors in the SMTP + protocol in conjunction with connection caching. + System 5 compatibility changes. + +6.15/6.3 93/01/26 + Fix a bug that causes local mail delivered using -odq to be + eliminated as a duplicate (because it matched the + ctladdr, now passed in as a C line). These changes + are pretty tricky...... + +6.14/6.3 93/01/25 + Add debugging for some MCI errors. + +6.13/6.3 93/01/22 + Fix -e compatibility flag to take a value. + Fix a couple of minor compilation warnings on Sun cc. + Improve error messages in a few cases to be more self-explanatory. + +6.12/6.3 93/01/21 + Fix yet-another problem with environment handling, pointed out + by Yoshitaka Tokugawa and Tom Ivar Helbekkmo. + Some heuristics to try to limit resource exhaustion problems + if a downstream host has been down for a long time. + Fix problem with incorrect host name being logged in "Connection + timed out" messages (from Tom Ivar Helbekkmo). + Fix some ANSI C problems (from Takahiro Kanbe). + Properly log message sender on returned mail during queue run. + Count number of recipients properly. + Fix a problem in yp map code. + Diagnose "message timed out" (from Nakamura Motonori). + +6.11/6.3 93/01/20 + Fix problem with address delimitor inside quotes. + Define $k and $=k to be the UUCP name (from the uname call) + based on code from Bruce Lilly. + +6.10/6.2 93/01/18 + Implement arpatounix (largely code from Bruce Lilly). + Log more info (suggested by John Myers). + Allow nested $?...$|...$. (inspired by code from Bruce Lilly of + Sony US). + POSIX compatibility (noted by Keith Bostic). + Handle SMTP MAIL command errors properly (urged by several people, + notably John Myers of CMU). + Do early diagnosis of .cf errors (notably referencing a RHS + substitution that isn't on the LHS). + Adjust checkpointing to better handle batched recipients, suggested + by John Myers. + Fix miscellaneous bugs. + (config files:) Implement MAIL_HUB for all local mail (to handle + NFS-mounted directories) as urged by Tom Ivar Helbekkmo + of the Norwegian School of Economics. + +6.9/6.1 93/01/13 + Environment handling simplification/bug fix -- child processes + get a minimal, fixed environment. This avoids different + behaviour in queue runs. + Handle commas inside comments properly. + Properly limit large messages submitted in -obq mode. + +6.8/6.1 93/01/10 + Check mtime of thaw file against .cf and sendmail binary, based on + code from John Myers. + +6.7/6.1 93/01/10 + MX piggybacking, based on code from John Myers@CMU. + Allow checkcompat to return -1 to mean tempfail. + Bug fix in m_mno computation. + +6.6/6.1 93/01/09 + Tuning of queueing functions as recommended by John Gardiner Myers. + Return mail headers (no body) on messages with negative precedence. + Minor other bug fixes. + +6.5/6.1 93/01/03 + Fix botch causing queued headers to have ?XX? prefixes. + +6.4/6.1 93/01/02 + Changes to recognize special mailer types (e.g., file) early. + +6.3/6.1 93/01/01 + Pass timeouts to sfgets. + Check for control characters in addresses. + Fixed deferred error reporting. + Report duplicate aliases. + Handle mixed case recursive aliases. + Misc bug fixes. + +6.2/6.1 92/12/30 + Put return-receipt-to on a conf.c flag (but don't set it). + Fix minor syslog problem. diff --git a/usr.sbin/sendmail/aux/addr.c b/usr.sbin/sendmail/aux/addr.c deleted file mode 100644 index c14333f8d3..0000000000 --- a/usr.sbin/sendmail/aux/addr.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1988 Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)addr.c 5.3 (Berkeley) 6/1/90"; -#endif /* not lint */ - -# include "postbox.h" - -/* -** PUTONQ -- put an address node on the end of a queue -** -** Parameters: -** a -- the address to put on the queue. -** q -- the queue to put it on. -** -** Returns: -** none -** -** Side Effects: -** none -** -** Called By: -** alias -** recipient -*/ - -putonq(a, q) - register ADDRESS *a; - register ADDRESS *q; -{ - if (q->q_prev == NULL) - { - q->q_prev = q->q_next = a; - a->q_prev = NULL; - } - else - { - a->q_prev = q->q_prev; - q->q_prev->q_next = a; - q->q_prev = a; - } - a->q_next = NULL; -} - /* -** TKOFFQ -- remove address node from queue -** -** Takes a node off of a queue, from anyplace in the queue. -** -** Parameters: -** a -- the node to remove. -** q -- the queue to remove it from. -** -** Returns: -** none -** -** Side Effects: -** none -** -** Called By: -** alias -*/ - -tkoffq(a, q) - register ADDRESS *a; - register ADDRESS *q; -{ - if (a->q_prev != NULL) - a->q_prev->q_next = a->q_next; - else - q->q_next = a->q_next; - if (a->q_next != NULL) - a->q_next->q_prev = a->q_prev; - else - q->q_prev = a->q_prev; -} - /* -** SAMEADDR -- Determine if tow addresses are the same -** -** This is not just a straight comparison -- if the mailer doesn't -** care about the host we just ignore it, etc. -** -** Parameters: -** a, b -- pointers to the internal forms to compare. -** wildflg -- if TRUE, 'a' may have no user specified, -** in which case it is to match anything. -** -** Returns: -** TRUE -- they represent the same mailbox. -** FALSE -- they don't. -** -** Side Effects: -** none. -** -** Called By: -** recipient -** alias -*/ - -bool -sameaddr(a, b, wildflg) - register ADDRESS *a; - register ADDRESS *b; - bool wildflg; -{ - /* if they don't have the same mailer, forget it */ - if (a->q_mailer != b->q_mailer) - return (FALSE); - - /* if the user isn't the same, we can drop out */ - if ((!wildflg || a->q_user[0] != '\0') && strcmp(a->q_user, b->q_user) != 0) - return (FALSE); - - /* if the mailer ignores hosts, we have succeeded! */ - if (bitset(M_NOHOST, Mailer[a->q_mailer]->m_flags)) - return (TRUE); - - /* otherwise compare hosts (but be careful for NULL ptrs) */ - if (a->q_host == NULL || b->q_host == NULL) - return (FALSE); - if (strcmp(a->q_host, b->q_host) != 0) - return (FALSE); - - return (TRUE); -} diff --git a/usr.sbin/sendmail/aux/arpa.c b/usr.sbin/sendmail/aux/arpa.c deleted file mode 100644 index 1ec8e6dcf5..0000000000 --- a/usr.sbin/sendmail/aux/arpa.c +++ /dev/null @@ -1,704 +0,0 @@ -/* - * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1988 Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)arpa.c 5.4 (Berkeley) 6/1/90"; -#endif /* not lint */ - -# include <stdio.h> -# include <ctype.h> -# include <signal.h> -# include <sysexits.h> -# include <whoami.h> -# include <useful.h> - -char Version[] = "@(#)Arpa-mailer version 5.4 of 6/1/90"; - -# define void int - -/* -** ARPA MAILER -- Queue ARPANET mail for eventual delivery -** -** The standard input is stuck away in the outgoing arpanet -** mail queue for delivery by the true arpanet mailer. -** -** CUSTOMIZED FOR THE C/70 -** -** Usage: -** /usr/lib/mailers/arpa from host user -** -** Positional Parameters: -** from -- the person sending the mail. -** host -- the host to send the mail to. -** user -- the user to send the mail to. -** -** Flags: -** -T -- debug flag. -** -** Files: -** /usr/spool/netmail/* -- the queue file. -** -** Return Codes: -** 0 -- all messages successfully mailed. -** 2 -- user or host unknown. -** 3 -- service unavailable, probably temporary -** file system condition. -** 4 -- syntax error in address. -** -** Compilation Flags: -** SPOOLDIR -- the spool directory -** -** Compilation Instructions: -** cc -n -O -s arpa-mailer.c -o arpa-mailer -lX -** chmod 755 arpa-mailer -** mv arpa-mailer /usr/lib/mailers/arpa -** -** Author: -** Eric Allman, UCB/INGRES (eric@berkeley) -*/ - -# ifdef C70 -# define SPOOLDIR "/usr/netmail" -# else -# define SPOOLDIR "/usr/spool/netmail" -# endif - - - - -char *From; /* person sending this mail */ -char *To; /* current "To:" person */ -int State; /* the current state (for exit codes) */ -# ifdef DEBUG -bool Tflag; /* -T given */ -# endif DEBUG -char FromHost[200]; /* string to prepend to addresses */ - /* -** MAIN -- Main program for arpa mailer -** -** Processes arguments, and calls sendmail successively on -** the To: list. -** -** Algorithm: -** Scan for debug flag. -** Catch interrupt signals. -** Collect input file name and from person. -** If more than one person in the to list, and -** if the input file is not a real file, -** collect input into a temp file. -** For each person in the to list -** Send to that person. -** -** Parameters: -** argc -** argv -- as usual -** -** Returns: -** via exit -** -** Side Effects: -** Mail gets sent. -** -** Author: -** Eric Allman UCB/INGRES. -*/ - -main(argc, argv) - int argc; - char **argv; -{ - register int i; - register char *p; - register int ifd; - char buf[512]; - extern int done(); - extern char *locv(); - register char *q; - char *lastmark; - - State = 3; - if (signal(SIGINT, SIG_IGN) != SIG_IGN) - (void) signal(SIGINT, done); - - /* process flags */ - argv[argc] = 0; -# ifdef DEBUG - if (strcmp(argv[1], "-T") == 0) - { - Tflag++; - argv++; - argc--; - printf("%s\n", Version); - } -# endif DEBUG - - if (argc != 4) - { - rexit(EX_SOFTWARE); - } - - /* decode parameters */ - From = argv[1]; - lastmark = &FromHost[-1]; - for (p = From, q = FromHost; (*q = *p) != '\0'; p++, q++) - { - if (*p == ':') - *q = *p = '.'; - if (*q == '.' || *q == '!' || *q == '@') - lastmark = q; - } - lastmark[1] = '\0'; - - /* start sending mail */ - State = sendmail(argv[2], argv[3]); - - /* all done, clean up */ - done(); -} - /* -** DONE -- Finish up, remove temp files, etc. -** -** This does basic cleanup on interrupt, error, or -** normal termination. It uses "State" to tell which -** is happening. -** -** Parameters: -** none -** -** Returns: -** none -** -** Side Effects: -** Exit(State). -*/ - -done() -{ - rexit(State); -} - -/* -** REXIT -- exit, reporting error code if -T given -** -** Parameters: -** e -- error code to exit with; see sysexits.h -** -** Returns: -** none -** -** Side Effects: -** Exit(e). -*/ -rexit(e) -{ - -# ifdef DEBUG - if (Tflag) - fprintf(stderr, "arpa-mail: return code %d\n", e); -# endif - exit(e); -} - /* -** SENDMAIL -- Queue up mail for the arpanet mailer. -** -** The mail is inserted with proper headers into the -** arpanet queue directory. -** -** Algorithm: -** decode "to" address -** if error, exit. -** create a spool file name. -** output the header information to spool file, -** separate names in To:, CC: fields with commas. -** copy the mail to the spool file. -** -** Parameters: -** host -- the host to send to. -** user -- the user to send to. -** -** Returns: -** none -** -** Side Effects: -** the mail is copied into a file in the network -** queue directory (/usr/spool/netmail). -*/ - -sendmail(host, user) - char *host; - char *user; -{ - char spoolfile[50]; /* gets the spool file name */ - register int i; - register char *p; - static int callnum; /* for the final letter on spoolfile */ - char buf[512]; - register FILE *sfp; /* spool file */ - register int c; - extern char *matchhdr(); - - /* verify that the host exists */ - (void) strcpy(buf, "/dev/net/"); - (void) strcat(buf, host); -# ifndef C70 -#ifdef DEBUG - if (!Tflag) -#endif DEBUG - if (host[0] == '\0' || access(buf, 0) < 0) - return (EX_NOHOST); -# endif C70 - - /* - ** Create spool file name. - ** Format is "username000nnX", where username is - ** padded on the right with zeros and nn (the process - ** id) is padded on the left with zeros; X is a unique - ** sequence character. - */ - -# ifdef DEBUG - if (Tflag) - (void) strcpy(spoolfile, "arpa.out"); - else -# endif DEBUG - (void) sprintf(spoolfile, "%s/arpamail%05d%c", SPOOLDIR, getpid(), 'a' + callnum++); - - /* create spool file */ - sfp = fopen(spoolfile, "w"); - if (sfp == NULL) - { - spoolerr: - return (EX_OSERR); - } -# ifdef DEBUG - if (!Tflag) -# endif DEBUG - (void) chmod(spoolfile, 0400); - - /* - ** Output mailer control lines. - ** These lines are as follows: - ** /dev/net/<hostname> {target host} - ** user-name {at target host} - ** /mnt/eric {pathname of sender; not used} - ** eric {name of user who is sending} - ** These are different (but close) on the C/70. - */ - -# ifdef C70 - fputs(host, sfp); - fputs(":", sfp); - fputs(user, sfp); - fputs(":", sfp); - fputs(From, sfp); - fputs(":\n", sfp); -# else - fputs(buf, sfp); - fputs("\n", sfp); - fputs(user, sfp); - fputs("\n\n", sfp); - fputs(From, sfp); - fputs("\n", sfp); -# endif - - /* - ** Output the mail - ** Check the first line for the date. If not found, - ** assume the message is not in arpanet standard format - ** and output a "Date:" and "From:" header. - */ - - if (fgets(buf, sizeof buf, stdin) == NULL) - { - /* no message */ - (void) unlink(spoolfile); - return (EX_OK); - } - if (strncmp("From ", buf, 5) == 0) - { - /* strip Unix "From" line */ - /* should save the date here */ - (void) fgets(buf, sizeof buf, stdin); - } - while (matchhdr(buf, "mail-from") != NULL || - matchhdr(buf, "sender-path") != NULL || - matchhdr(buf, "received") != NULL || - matchhdr(buf, "via") != NULL) - { - fputs(buf, sfp); - (void) fgets(buf, sizeof buf, stdin); - } - if (matchhdr(buf, "date") == NULL) - putdate(sfp); - else - { - fputs(buf, sfp); - (void) fgets(buf, sizeof buf, stdin); - } - if (matchhdr(buf, "from") == NULL) - putfrom(sfp); - else - { - /* hack to support sendmail -- for a while */ - if (index(buf, '@') == NULL) - putfrom(sfp); - else - fputs(buf, sfp); - (void) fgets(buf, sizeof buf, stdin); - } - if (!ishdr(buf)) - { - if (buf[0] != '\n') - putc('\n', sfp); - goto hdrdone; - } - - /* - ** At this point, we have a message with REAL headers. - ** We look at each head line and insert commas if it - ** is a To: or Cc: field. - */ - - do - { - if (!ishdr(buf)) - break; - if (!matchhdr(buf, "to") && !matchhdr(buf, "cc")) - { - fputs(buf, sfp); - continue; - } - /* gotcha! */ - fixaddr(buf, 1, sfp); - while (isspace(c = peekc(stdin)) && c != '\n') - { - (void) fgets(buf, BUFSIZ, stdin); - fixaddr(buf, 0, sfp); - } - } while (fgets(buf, BUFSIZ, stdin) != NULL); - -hdrdone: - /* output the rest of the header & the body of the letter */ - do - { - fputs(buf, sfp); - if (ferror(sfp)) - goto spoolerr; - } while (fgets(buf, sizeof buf, stdin) != NULL); - - /* all done! */ - (void) fclose(sfp); - return (EX_OK); -} - /* -** FIXADDR -- Output header line with needed commas. -** -** Parameters: -** buf -- header line -** first -- true if this is not a continuation -** -** Returns: -** none -** -** Side effects: -** The contents of buf is copied onto the spool file with -** with the right commas interlaced -** -** Called by: -** sendmail -*/ - -fixaddr(buf, first, spf) - char buf[]; - register FILE *spf; -{ - register char *cp; - register int c; - char word[BUFSIZ], word2[BUFSIZ]; - char *gword(); - static char wsep[] = ", "; - - cp = buf; - if (first) - { - while (*cp != ':' && *cp) - putc(*cp++, spf); - if (*cp == ':') - { - fputs(": ", spf); - cp++; - } - } - else - while (*cp && isspace(*cp)) - putc(*cp++, spf); - cp = gword(word, cp); - if (strlen(word) == 0) - { - putc('\n', spf); - goto test; - } - for (;;) - { - cp = gword(word2, cp); - if (strlen(word2) == 0) - { - putaddr(word, spf); - break; - } - if (strcmp(word2, "%") == 0) - word2[0] = '@'; - if (strcmp(word2, "@") && strcmp(word2, "at")) - { - putaddr(word, spf); - fputs(wsep, spf); - (void) strcpy(word, word2); - continue; - } - fputs(word, spf); - if (word2[0] == '@') - putc('@', spf); - else - fputs(" at ", spf); - cp = gword(word, cp); - fputs(word, spf); - cp = gword(word, cp); - if (strlen(word)) - fputs(wsep, spf); - } - -test: - c = peekc(stdin); - if (isspace(c) && c != '\n') - fputs(",\n", spf); - else - putc('\n', spf); -} - /* -** PUTADDR -- output address onto file -** -** Putaddr prepends the network header onto the address -** unless one already exists. -** -** Parameters: -** name -- the name to output. -** fp -- the file to put it on. -** -** Returns: -** none. -** -** Side Effects: -** name is put onto fp. -*/ - -putaddr(name, fp) - char *name; - FILE *fp; -{ - register char *p; - - if (strlen(name) == 0) - return; - for (p = name; *p != '\0' && *p != ':' && *p != '.' && *p != '@' && - *p != '!' && *p != '^'; p++) - continue; - if (*p == ':') - *p = '.'; - else if (*p == '\0') - fputs(FromHost, fp); - fputs(name, fp); - if (*p != '@') - fputs("@Berkeley", fp); -} - /* -** PEEKC -- peek at next character in input file -** -** Parameters: -** fp -- stdio file buffer -** -** Returns: -** the next character in the input or EOF -** -** Side effects: -** None. -*/ -peekc(fp) - register FILE *fp; -{ - register int c; - - c = getc(fp); - (void) ungetc(c, fp); - return(c); -} - - /* -** GWORD -- get the next liberal word from a string -** -** Parameters: -** buf -- place to put scanned word -** p -- place to start looking for word -** -** Returns: -** updated value of p or 0 if no more left after this -** -** Side effects: -** buf gets the liberal word scanned. -** buf will be length 0 if there is no more input, -** or if p was passed as 0 -*/ -char * -gword(buf, p) - char buf[]; - register char *p; -{ - register char *sp, *dp; - int atfound = 0; /* weither or not a '@' found in the scan */ - - (void) strcpy(buf, ""); - if (p == 0) - return(0); - sp = p; - while (*sp && (isspace(*sp) || *sp == ',')) - sp++; - dp = buf; - if (*sp != '%' && *sp != '@') - { - while (*sp && !isspace(*sp) && *sp != ',' ) - { - if ( *sp == '@' || *sp == '%' ) - atfound++; - *dp++ = *sp++; - } - if ( atfound ) - { - dp--; - while ( *dp != '@' && *dp != '%' ) - dp--,sp--; - sp--; - } - - } - else - *dp++ = *sp++; - *dp = 0; - if (*sp == 0) - return(0); - return(sp); -} - /* -** ISHDR -- see if the passed line is a ARPA style header line -** -** Parameters: -** buf -- header line -** -** Returns: -** non-zero if the line is a header line, else zero -** -** Side effects: -** none -** -** Called by: -** sendmail -*/ -ishdr(buf) - char buf[]; -{ - register char *p; - - p = buf; - - /* check for continuation lines */ - if (isspace(*p)) - return (1); - else - { - while (*p != ':' && !isspace(*p)) - p++; - while (isspace(*p)) - p++; - if (*p != ':') - p = 0; - } - return(p != 0); -} - /* -** PUTDATE -- Put the date field into the message. -** -** Parameters: -** fp -- file to put it onto. -** -** Returns: -** none -** -** Side Effects: -** output onto fp. -*/ - -putdate(fp) - register FILE *fp; -{ - extern char *arpadate(); - - fputs("Date: ", fp); - fputs(arpadate(NULL), fp); - fputs("\n", fp); -} - /* -** PUTFROM -- Put the from field into the message. -** -** Parameters: -** fp -- file to put it onto. -** -** Returns: -** none -** -** Side Effects: -** output onto fp. -*/ - -putfrom(fp) - register FILE *fp; -{ - - fputs("From: ", fp); - fputs(From, fp); - fputs("@Berkeley\n", fp); -} diff --git a/usr.sbin/sendmail/aux/mail-dm.c b/usr.sbin/sendmail/aux/mail-dm.c deleted file mode 100644 index 57becd8e36..0000000000 --- a/usr.sbin/sendmail/aux/mail-dm.c +++ /dev/null @@ -1,467 +0,0 @@ -/*- - * Copyright (c) 1981 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1981 The Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)mail-dm.c 4.2 (Berkeley) 4/19/91"; -#endif /* not lint */ - -#include <arpa/netopen.h> -#include "srvrftp.h" -#include <statbuf.h> -#include <arpa/hostnames.h> -#include <io_buf.h> -#include <arpa/mail.h> -#include <ident.h> -#include <signal.h> -#include <log.h> -extern int fout; - -/* -Name: - mail - -Function: - handle the MAIL <user> command over the command connection - -Algorithm: - see if we have a known user - - if mailbox file can't be gotten - return - tell him it is ok to go ahead with mail - - while he doesn't type a period - read and write data - say completed - -Parameters: - username in arg - -Returns: - nothing - -Globals: - arg - username= - -Calls: - strmove - getuser - loguser - openmail - closemail - getline - chown (sys) - time (sys) - printf (sys) - getch (util) - putch (util) - -Called by: - main thru command array - -History: - initial coding Mark Kampe UCLA-ATS - modified 4/13/76 by S. F. Holmgren for Illinois version - modified 6/30/76 by S. F. Holmgren to call getmbox - modified 10/18/76 by J. S. Kravitz to improve net mail header - chown removed by R. Balocca @ CAC, Sunday 1977 February 20 - getline removed and limit on line length removed by using - getch and putch added by R. Balocca @ CAC, 1977 March 8 Tuesday - Fixed oversight in above (forgot to translate <crlf> to <lf>) - 1977 March 10 Thursday by Rick Balocca @ CAC - Added openmail & closemail, added logging, and fixed several - bugs on or about 12/21/79 by Eric Allman, UCB/INGRES. - Changed to always accept mail -- bad mail will be sent back -- - 1/9/80 by Eric Allman, UCB/INGRES. - Don't print out 350 enter mail or 256 mail accepted messages -- - sendmail will do that. 8/19/81 Eric Allman UCB/INGRES. -*/ -#define gt (c = getch()) -mail() -{ - register char *p; /* general use */ - register int c; - int i; - - /* extern struct io_buf obuf; */ - - /* get to open mailbox file descriptor */ - fflush(&fout); - if( (fout = openmail(arg, 0)) < 0 ) - return; - - for(;;) /* while no error or <crlf>.<crlf> */ - { - /* we are at beginning of line */ - - if(gt=='.') /*"."*/ - { - if(gt=='\r') /*".\r"*/ - { - if(gt=='\n') /*".\r\n"*/ - { - /* end of message */ - break; - } - else - { /*".\r"c*/ - putch('.'); - putch('\r'); - } - } - else /*"."c"*/ - putch('.'); - } - /*"-"*/ - /* c */ - for(;;) - { - for(; c != '\r'; gt) - { - if( c < 0 ) - { - fflush(&fout); - write(fout, "\n***** Sender aborted connection *****\n", 39); - goto out; - } - else - putch(c); - } - - /*"\r"*/ - if( gt == '\n' ) - { /*"\r\n"*/ -crlf: - putch('\n'); - break; - } - else - { /*"\r"c*/ -crc: - putch('\r'); - if(c=='\0') - gt; /* "\r\0" */ - /* is arpa escape for "\r" */ - } - } - } - -out: - fflush(&fout); - closemail(fout); -} - - /* -Name: - datamail - -Function: - handle the MLFL command - -Algorithm: - fork - make sure we have a valid user - say bad user and exit - send sock command - open data connection - get open mailbox file descriptor - call rcvdata to receive mail - -Parameters: - username in arg - -Returns: - nothing - -Globals: - arg - -Calls: - fork (sys) - strmove - netreply - sendsock - dataconnection - getmbox - rcvdata - printf (sys) - time (sys) - -Called by: - main thru command array - -History: - initial coding 4/13/76 by S. F. Holmgren - modified 10/18/76 by J. S. Kravitz to put net mail header - chown removed by R. Balocca @ CAC, Sunday 1977 February 20 -*/ -datamail() -{ - register netdata; - /* register mboxfid; */ - register int i; - - i = fork(); - if (i < 0) - { - netreply("455 Mail server temporarily unavailable\r\n"); - return; - } - else if (i == 0) - { - fflush(&fout); - if ((fout = openmail(arg, 1)) < 0) - exit(3); - - /* send sock command */ - sendsock( U4 ); - - /* open data connection */ - netdata = dataconnection( U4 ); - - /* say its ok to proceed */ - numreply( NUM250 ); - - /* get data from net connection and copy to mail file */ - /* rcvdata( netdata,mboxfid ); */ - if (rcvdata(netdata, fout) < 0) - exit(1); - - /* close the mail, see if ok; if so say ok */ - fflush(&fout); - closemail(fout); - - exit( 0 ); - } -} - /* -** OPENMAIL -- Open a channel to the mail server -** -** Gets the mail server started up ready to handle our -** mail. -** -** Algorithm: -** See if the user is specified. -** If not, send to user "root". -** See if the user exists. -** If not, signal error 450 and return. -** Fork. -** Create a pipe -** Signal "unavailable" and exit on failure. -** Fork. -** Signal "unavailable" and exit on failure -** In child: -** Call mailer: /etc/delivermail is preferred. -** In parent: -** Avoid pipe signals in case delivermail dies. -** Save the childs pid. -** Return file descriptor. -** -** Notes: -** The check to see if the user actually exists should -** go away so that we can do real mail forwarding. -** -** Parameters: -** who -- the user to send the mail to. -** mode -- 0 -- called from mail -** 1 -- called from mlfl -** -** Returns: -** File descriptor to send mail to. -** -1 on failure. -** -** Side Effects: -** Forks /etc/delivermail or /bin/mail or /usr/bin/mail. -** Becomes "network" in the child. -** -** Requires: -** strmove -** getuser -** netreply -** pipe (sys) -** fork (sys) -** close (sys) -** dup (sys) -** execl (sys) -** signal (sys) -** exit (sys) -** -** Called By: -** mail -** datamail -** -** History: -** 1/9/80 -- Added 050 & 455 reply messages if execl's -** fail. Eric Allman UCB/INGRES. -** 11/26/79 -- Modified to map upper case to lower -** case. Eric Allman UCB/INGRES. -** 11/10/79 -- Written by Eric Allman UCB/INGRES -** 3/6/80 -- Dropped case mapping; delivermail does -** that now. EPA UCB/INGRES. -** 8/19/81 -- Added "mode" parameter; call sendmail -** instead of delivermail. EPA -*/ - -int Mail_pid; -char *Mail_user; - -openmail(who, mode) - char *who; - int mode; -{ - register char *w; - register int i; - int pvect[2]; - register char *p; - - w = who; - if (w == 0) - w = "root"; - Mail_user = w; - - /* see if the user exists */ - strmove(w, username); - - /* try to get a pipe to the mailer */ - if (pipe(pvect) < 0) - { - unavailable: - netreply("455 Mail server temporarily unavailable\r\n"); - return (-1); - } - - /* fork */ - i = fork(); - if (i < 0) - { - /* failure */ - close(pvect[0]); - close(pvect[1]); - goto unavailable; - } - else if (i == 0) - { - /* child */ - close(pvect[1]); - close(0); - dup(pvect[0]); - close(pvect[0]); - setuid(NETUID); - - /* try to call something to deliver the mail */ - execl("/etc/sendmail", "sendmail", "-v", mode == 1 ? "-af" : "-am", w, 0); - - /* doesn't seem to be anything around */ - netreply("455 Mail server unavailable\r\n"); - exit(3); - } - - /* else parent */ - signal(SIGPIPE, SIG_IGN); - Mail_pid = i; - close(pvect[0]); - return (pvect[1]); -} - /* -** CLOSEMAIL -- Close the mail file and get actual status -** -** The mail file is closed. -** -** Algorithm: -** Wait for the mailer to die. -** If it wasn't there, be non-comittal. -** If it died a violent death, give error. -** -** Parameters: -** fd -- the file descriptor of the mail file. -** -** Returns: -** none. -** -** Side Effects: -** mailer is soaked up. -** -** Requires: -** close (sys) -** wait (sys) -** -** Called By: -** mail -** datamail -** -** History: -** 1/9/80 -- Changed to not check for errors in mailing, -** since these will be mailed back. -** 11/10/79 -- Written by Eric Allman UCB/INGRES. -*/ - -closemail(fd) - int fd; -{ - auto int st; - register int i; - - /* close the pipe -- mail should go away */ - close(fd); - - /* wait for its body */ - while ((i = wait(&st)) != Mail_pid) - { - if (i < 0) - { - /* how did this happen? */ - logmsg(LOG_ERR, "mail from host %d to %s: no child", - openparams.o_frnhost & 0377, Mail_user); - goto unavailable; - } - } - - /* 'st' is now the status of the mailer */ - if ((st & 0377) != 0) - { - logmsg(LOG_ERR, "mail from host %d to %s: status %o", - openparams.o_frnhost & 0377, Mail_user, st); -unavailable: - netreply("455 Mail not delivered -- local system error\r\n"); - return (-1); - } - - return (0); -} diff --git a/usr.sbin/sendmail/aux/matchhdr.c b/usr.sbin/sendmail/aux/matchhdr.c deleted file mode 100644 index 822f21cfbd..0000000000 --- a/usr.sbin/sendmail/aux/matchhdr.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1988 Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)matchhdr.c 5.4 (Berkeley) 6/1/90"; -#endif /* not lint */ - -# include <stdio.h> -# include <ctype.h> -# include <useful.h> - -SCCSID(@(#)matchhdr.c 5.4 6/1/90); - -/* -** MATCHHDR -- Match header line -** -** Matches a header line in arpanet format (case and white -** space is ignored). -** -** This routine is used by arpa-mailer and sendmail. -** -** Parameters: -** line -- the line to match against. -** pat -- the pattern to match against; must be in -** lower case. -** -** Returns: -** address of the 'value' of the pattern (the beginning -** of the non-white string following the delim). -** NULL if none found. -** -** Side Effects: -** none -** -** Called By: -** maketemp -** sendmail [arpa.c] -** -** Deficiencies: -** It doesn't handle folded lines. -*/ - -char * -matchhdr(line, pat) - char *line; - char *pat; -{ - register char *p; - register char *q; - - for (q = pat, p = line; *q != '\0'; p++, q++) - if (lowercase(*p) != *q) - return (NULL); - while (isspace(*p)) - p++; - if (*p != ':') - return (NULL); - while (isspace(*++p)) - continue; - return (*p == '\0' ? NULL : p); -} - /* -** LOWERCASE -- Convert a character to lower case -** -** If the argument is an upper case letter, it is converted -** to a lower case letter, otherwise it is passed through -** unchanged. -** -** Parameters: -** c -- the character to check. -** -** Returns: -** c converted to lower case. -** -** Side Effects: -** none -** -** Called By: -** matchhdr -*/ - -lowercase(c) - register char c; -{ - if (isupper(c)) - c -= 'A' - 'a'; - return (c); -} diff --git a/usr.sbin/sendmail/aux/newaliases.c b/usr.sbin/sendmail/aux/newaliases.c deleted file mode 100644 index 5e7f81d919..0000000000 --- a/usr.sbin/sendmail/aux/newaliases.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1988 Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)newaliases.c 5.4 (Berkeley) 6/1/90"; -#endif /* not lint */ - -# include <stdio.h> -# include <ctype.h> -# include "sendmail.h" -# include "pathnames.h" - -static char SccsId[] = "@(#)newaliases.c 5.4 6/1/90"; - -typedef struct { char *dptr; int dsize; } datum; -char *aliases = ALIASFILE; -char dirbuf[100]; -char pagbuf[100]; -int LineNo; -char *To; -int ExitStat; -int Errors; -HDR *Header; -struct mailer *Mailer[MAXMAILERS+1]; -int NextMailer = 0; -# ifdef DEBUG -int Debug; -# endif DEBUG - -main(argc, argv) - int argc; - char *argv[]; -{ - int f; - char line[BUFSIZ]; - register char *p; - char *p2; - char *rhs; - int naliases, bytes, longest; - datum key, content; - bool skipping; - ADDRESS al, bl; - extern char *prescan(); - extern ADDRESS *parse(); - bool contin; - char *cffile = _PATH_SENDMAILCF; - -# ifdef DEBUG - if (argc > 1 && strcmp(argv[1], "-T") == 0) - { - Debug = 100; - argc--; - argv++; - } -# endif DEBUG - if (argc > 1) - aliases = argv[1]; - if (argc > 2) - cffile = argv[2]; - readcf(cffile); - - (void) strcpy(dirbuf, aliases); - (void) strcat(dirbuf, ".dir"); - (void) strcpy(pagbuf, aliases); - (void) strcat(pagbuf, ".pag"); - f = creat(dirbuf, 0666); - if (f < 0) { - perror(dirbuf); - exit(1); - } - (void) close(f); - f = creat(pagbuf, 0666); - if (f < 0) { - perror(pagbuf); - exit(1); - } - (void) close(f); - if (dbminit(aliases) < 0) - exit(1); - if (freopen(aliases, "r", stdin) == 0) { - perror(aliases); - exit(1); - } - - /* read and interpret lines */ - LineNo = 0; - naliases = 0; - bytes = 0; - longest = 0; - skipping = FALSE; - while (fgets(line, sizeof (line), stdin) != NULL) - { - LineNo++; - switch (line[0]) - { - case '#': - case '\n': - case '\0': - skipping = FALSE; - continue; - - case ' ': - case '\t': - if (!skipping) - usrerr("Non-continuation line starts with space"); - skipping = TRUE; - continue; - } - skipping = FALSE; - - /* process the LHS */ - for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) - continue; - if (*p == '\0' || *p == '\n') - { - syntaxerr: - usrerr("missing colon"); - continue; - } - *p++ = '\0'; - if (parse(line, &al, 1) == NULL) - { - *--p = ':'; - goto syntaxerr; - } - rhs = p; - contin = FALSE; - for (;;) - { - register char c; - - /* do parsing & compression of addresses */ - c = *p; - while (c != '\0') - { - p2 = p; - while (*p != '\n' && *p != ',' && *p != '\0') - p++; - c = *p; - *p++ = '\0'; - if (*p2 == '\0') - { - p[-1] = c; - continue; - } - parse(p2, &bl, -1); - contin = (c == ','); - p[-1] = c; - while (isspace(*p)) - p++; - } - - /* see if there should be a continuation line */ - if (!contin) - break; - - /* read continuation line */ - p--; - if (fgets(p, sizeof line - (p - line), stdin) == NULL) - break; - LineNo++; - - if (!isspace(*p)) - usrerr("continuation line missing"); - } - if (al.q_mailer != MN_LOCAL) - { - usrerr("cannot alias non-local names"); - continue; - } - naliases++; - key.dsize = strlen(al.q_user) + 1; - key.dptr = al.q_user; - content.dsize = strlen(rhs) + 1; - if (content.dsize > longest) - longest = content.dsize; - content.dptr = rhs; - bytes += key.dsize + content.dsize; - if (store(key, content), 0) - /* if (f = store(key, content)) */ - usrerr("Dbm internal error return %d from store\n", f); - } - fprintf(stderr, "%d aliases, %d bytes, longest %d bytes, %d errors\n", - naliases, bytes, longest, Errors); - - exit(ExitStat); -} - -usrerr(fmt, a, b, c, d, e) - char *fmt; -{ - Errors++; - fprintf(stderr, "line %d: ", LineNo); - fprintf(stderr, fmt, a, b, c, d, e); - fprintf(stderr, "\n"); - return (-1); -} - -syserr(fmt, a, b, c, d, e) - char *fmt; -{ - return (usrerr(fmt, a, b, c, d, e)); -} diff --git a/usr.sbin/sendmail/cf/KEY b/usr.sbin/sendmail/cf/KEY deleted file mode 100644 index 86eb0437c8..0000000000 --- a/usr.sbin/sendmail/cf/KEY +++ /dev/null @@ -1,16 +0,0 @@ - -Macro Class ------ ----- -A Internet relay -- -B BITNET relay -- -C CSNET relay -- -D domain name -I -- "Fake" (Internal) domains, i.e. - UUCP, BITNET, CSNET. -N -- hosts registered with the NIC -R UUCP relay host -- -U This host's UUCP name UUCP aliases -V None List of our UUCP neighbors -W Host to forward to for $=W UUCP hosts on $W -X Host to forward to for $=X UUCP hosts on $X -Y Host to forward to for $=Y UUCP hosts on $Y diff --git a/usr.sbin/sendmail/cf/M4_KEY b/usr.sbin/sendmail/cf/M4_KEY deleted file mode 100644 index e7eb7aa4af..0000000000 --- a/usr.sbin/sendmail/cf/M4_KEY +++ /dev/null @@ -1,55 +0,0 @@ -M4 Macros - -EXTERNAL_VERSION ``#' `%W%' (Berkeley) `%G%'' - - The SCCS id of the "outside" file. - -INTERNET_ALIASES `Cwcad ucbcad' - - If defined, the line which lists the w class. If not defined, -defaults to `Cwmonet ucbmonet' with a warning to replace this line -with your hostname. - -UUCP_NAME `DUucbcad' - - If defined, includes UUCP code. Used to define your official -UUCP hostname. - -UUCP_ALIASES `CUucbcad' - - Included only if UUCP_NAME is defined, lists aliases for UUCP. - -UUCP_HOSTS_FILE `../machdep/uucp.cad.m4' - - Included only if UUCP_NAME is defined. Defines class V of -local UUCP hosts by including the appropriate m4 file. - -SMTPUUCP `../machdep/smtpuucp.cad.m4' - - If defined, use SMTP to resolve local UUCP connections (while -keepign bangist format). Should be defined to be the file that -contains thef ruleset 0 additions. - -INTERNET_RELAY `DAcad.Berkeley.EDU' - - If defined, this will be the machine behind which non-NIC registered -hosts are hidden, resuling in addresses of the form - - user%host@INTERNET_RELAY - -If not defined, defaults to ucbvax.Berkeley.EDU. - -UUCP_RELAY `DRcad' - - If defined, this will be the machine to which non-local UUCP traffic -is forwarded. If not defined, defaults to ucbvax. - -BITNET_RELAY `DBjade.Berkeley.EDU' - - If defined, this will be the machien to which BITNET traffic -is forwarded. If not defined, it defaults to jade.Berkeley.EDU. - -CSNET_RELAY `DCrelay.cs.net' - - If defined, this will be the machine to which CSNET traffic is -forwarded. If not defined, it defaults to relay.cs.net. diff --git a/usr.sbin/sendmail/cf/README b/usr.sbin/sendmail/cf/README index cda1a6e8cc..c648a638cd 100644 --- a/usr.sbin/sendmail/cf/README +++ b/usr.sbin/sendmail/cf/README @@ -1,392 +1,914 @@ -INTRODUCTION: -------------- -This document describes (in some detail, though undoubtedly not enough!) -the sendmail configuration files currently in use at Berkeley as distributed -with version 5.61 of sendmail. It discusses the configuration file -directory hierarchy, how the files are processed by m4(1), what functionality -the files provide, what m4(1) options can be used to produce specific -results, and goes through an example or two. It concludes with a list -of things that will change in the next release of the configuration files. -This is a working draft; your comments are appreciated. Please send your -suggestions to Phil Lapsley, phil@ucbvax.berkeley.edu, ...!ucbvax!phil. + NEW SENDMAIL CONFIGURATION FILES + + Eric Allman <eric@CS.Berkeley.EDU> + + @(#)README 8.3 (Berkeley) 7/13/93 + + +This document describes the sendmail configuration files being used +at Berkeley. These use features in the new (R6) sendmail, and although +there is an ``OLDSENDMAIL'' mode, they haven't really been tested on +old versions of sendmail and cannot be expected to work well. + +These configuration files are probably not as general as previous +versions, and don't handle as many of the wierd cases automagically. +I was able to simplify by them for two reasons. First, the network +has become more consistent -- for example, at this point, everyone +on the internet is supposed to be running a name server, so hacks to +handle NIC-registered hosts can go away. Second, I assumed that a +subdomain would be running SMTP internally -- UUCP is presumed to be +a long-haul protocol. I realize that this is not universal, but it +does describe the vast majority of sites with which I am familiar, +including those outside the US. + +Of course, the downside of this is that if you do live in a wierd +world, things are going to get wierder for you. I'm sorry about that, +but at the time we at Berkeley had a problem, and it seemed like the +right thing to do. + +This package requires a post-V7 version of m4; if you are running the +4.2bsd, SysV.2, or 7th Edition version, I suggest finding a friend with +a newer version. You can m4-expand on their system, then run locally. +SunOS's /usr/5bin/m4 or BSD-Net/2's m4 both work. GNU m4 (which is a +language unto itself) also works, but I don't intend to work so hard +to keep this up in the future. [Note to GNU folks: the construct +"define(`FOO')" should work without my having to add a null value.] + +IF YOU DON'T HAVE A BERKELEY MAKE, don't despair! Just run +"m4 foo.mc > foo.cf" -- that should be all you need. + +To get started, you may want to look at tcpproto.mc (for TCP-only +sites) and uucpproto.m4 (for UUCP-only sites). Others are versions +that we use at Berkeley, although not all are in current use. For +example, ucbarpa has gone away, but I've left ucbarpa.mc in because +it demonstrates some interesting techniques. + +I'm not pretending that this README describes everything that these +configuration files can do; clever people can probably tweak them +to great effect. But it should get you started. + + ++--------------------------+ +| INTRODUCTION AND EXAMPLE | ++--------------------------+ + +Configuration files are contained in the subdirectory "cf", with a +suffix ".mc". They must be run through "m4" to produce a ".cf" file. + +Let's examine a typical .mc file (cf/cs-exposed.mc): + + divert(-1) + # + # Copyright (c) 1983 Eric P. Allman + # Copyright (c) 1988 The Regents of the University of California. + # All rights reserved. + # + # Redistribution and use in source and binary forms are permitted + # provided that the above copyright notice and this paragraph are + # duplicated in all such forms and that any documentation, + # advertising materials, and other materials related to such + # distribution and use acknowledge that the software was developed + # by the University of California, Berkeley. The name of the + # University may not be used to endorse or promote products derived + # from this software without specific prior written permission. + # THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + # IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + # WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + # + +The divert(-1) will delete the crud in the resulting output file. +The copyright notice is what your lawyers require. Our lawyers require +the one that I've included in my files. A copyleft is a copyright by +another name. + +The next line MUST be + + include(`../m4/cf.m4') + +This will pull in the M4 macros you will need to make sense of +everything else. As the saying goes, don't think about it, just +do it. If you don't do it, don't bother reading the rest of this +file. + + VERSIONID(`<SCCS or RCS version id>') + +VERSIONID is a macro that stuffs the version information into the +resulting file. We use SCCS; you could use RCS, something else, or +omit it completely. This is not the same as the version id included +in SMTP greeting messages -- this is defined in m4/version.m4. + + DOMAIN(cs.exposed) + +This example exposes the host inside of the CS subdomain -- that is, +it doesn't try to hide the name of the workstation to the outside +world. Changing this to DOMAIN(cs.hidden) would have made outgoing +messages refer to "<username>@CS.Berkeley.EDU" instead of using the +local hostname. Internaly this is effected by using +"MASQUERADE_AS(CS.Berkeley.EDU)". + + MAILER(smtp) + +These describe the mailers used at the default CS site site. The +local mailer is always included automatically. + + ++--------+ +| OSTYPE | ++--------+ + +Note that cf/cs-exposed.mc omits an OSTYPE macro -- this assumes +default Computer Science Division environment. There are several +explicit environments available: bsd4.3, bsd4.4, hpux, irix, osf1, +riscos4.5, sunos3.5, sunos4.1, and ultrix4.1. These change things +like the location of the alias file and queue directory. Some of +these files are identical to one another. + +Operating system definitions are easy to write. They may define +the following variables (everything defaults, so an ostype file +may be empty). + +ALIAS_FILE [/etc/aliases] The location of the text version + of the alias file(s). It can be a comma-separated + list of names. +HELP_FILE [/usr/lib/sendmail.hf] The name of the file + containing information printed in response to + the SMTP HELP command. +QUEUE_DIR [/var/spool/mqueue] The directory containing + queue files. +STATUS_FILE [/etc/sendmail.st] The file containing status + information. +LOCAL_MAILER_PATH [/usr/libexec/mail.local] The program used to + deliver local mail. +LOCAL_MAILER_FLAGS [rn] The flags used by the local mailer. The + flags lsDFMm are always included. +LOCAL_MAILER_ARGS [mail -d $u] The arguments passed to deliver local + mail. +LOCAL_SHELL_PATH [/bin/sh] The shell used to deliver piped email. +LOCAL_SHELL_FLAGS [eu] The flags used by the shell mailer. The + flags lsDFM are always included. +LOCAL_SHELL_ARGS [sh -c $u] The arguments passed to deliver "prog" + mail. +USENET_MAILER_PATH [/usr/lib/news/inews] The name of the program + used to submit news. +USENET_MAILER_FLAGS [rlsDFMmn] The mailer flags for the usenet mailer. +USENET_MAILER_ARGS [-m -h -n] The command line arguments for the + usenet mailer. +SMTP_MAILER_FLAGS [undefined] Flags added to SMTP mailer. +UUCP_MAILER_FLAGS [undefined] Flags added to UUCP mailer. +UUCP_MAILER_ARGS [uux - -r -z -a$f -gC $h!rmail ($u)] The arguments + passed to the UUCP mailer. +UUCP_MAX_SIZE [100000] The maximum size message accepted for + transmission by the UUCP mailers. +HOSTMAP_SPEC [dbm -o /etc/hostmap] The value for the builtin + hostmap key definition. You can redefine this + to change the class, flags, and filename of + the hostmap. The default flag (-o) makes this + map optional. + +In addition, the following boolean flags may be defined -- the value +is ignored. + +NEED_DOMAIN If set, the $j macro is defined as $w.$D. + If not set, $j is defined as $w. If this is + set, the domain must be defined using the line + DD<domainname> (probably in the domain file, + but possibly in the .mc file). You will only + need this if you define your system hostname + without a domain (type "hostname" -- if it + has no dots in the output, you qualify) AND + if you are not running the nameserver AND if + the first (canonical) name in /etc/hosts for + your machine has no domain -- OR if you are + running Ultrix or OSF/1 sendmail. Either of + these is probably a mistake. + ++---------+ +| DOMAINS | ++---------+ + +You will probably want to collect domain-dependent defines into one +file, referenced by the DOMAIN macro. For example, our Berkeley +domain file includes definitions for several internal distinguished +hosts: + +UUCP_RELAY The host that will forward UUCP-addressed email. + If not defined, all UUCP sites must be directly + connected. +BITNET_RELAY The host that will forward BITNET-addressed email. + If not defined, the .BITNET pseudo-domain won't work. +CSNET_RELAY The host that will forward CSNET-addressed email. + If not defined, the .CSNET pseudo-domain won't work. +LOCAL_RELAY The site that will handle unqualified names -- that + is, names with out an @domain extension. If not set, + they are assumed to belong on this machine. This + allows you to have a central site to store a + company- or department-wide alias database. This + only works at small sites, and there are better + methods. + +The domain file can also be used to define a domain name, if needed +(using "DD<domain>") and set certain site-wide features. If all hosts +at your site masquerade behind one email name, you could also use +MASQUERADE_AS here. + +You do not have to define a domain -- in particular, if you are a +single machine sitting off somewhere, it is probably more work than +it's worth. This is just a mechanism for combining "domain dependent +knowledge" into one place. + ++---------+ +| MAILERS | ++---------+ + +There are fewer mailers supported in this version than the previous +version, owing mostly to a simpler world. + +local The local and prog mailers. You will almost always + need these; the only exception is if you relay ALL + your mail to another site. This mailer is included + automatically. + +smtp The Simple Mail Transport Protocol mailer. This does + not hide hosts behind a gateway or another other + such hack; it assumes a world where everyone is + running the name server. This file actually defines + three mailers: "smtp" for regular (old-style) SMTP to + other servers, "esmtp" for extended SMTP to other + servers, and "relay" for transmission to our + RELAY_HOST or MAILER_HUB. + +uucp The Unix-to-Unix Copy Program mailer. Actually, this + defines two mailers, "uucp" and "suucp". The latter + is for when you know that the UUCP mailer at the other + end can handle multiple recipients in one transfer. + When you invoke this, sendmail looks for all names in + the $=U class and sends them to the uucp mailer; all + names in the $=Y class are sent to suucp. Note that + this is a function of what version of rmail runs on + the receiving end, and hence may be out of your control. + +usenet Usenet (network news) delivery. If this is specified, + an extra rule is added to ruleset 0 that forwards all + local email for users named ``group.usenet'' to the + ``inews'' program. Note that this works for all groups, + and may be considered a security problem. + +fax Facsimile transmission. This is experimental and based + on Sam Leffler's FlexFAX software. For more information, + see below. + + ++----------+ +| FEATURES | ++----------+ + +Special features can be requested using the "FEATURE" macro. For +example, the .mc line: + + FEATURE(use_cw_file) + +tells sendmail that you want to have it read an /etc/sendmail.cw +file to get values for class $=w. The FEATURE may contain a single +optional parameter -- for example: + + FEATURE(mailertable, dbm /usr/lib/mailertable) + +Available features are: + +use_cw_file Read the file /etc/sendmail.cw file to get alternate + names for this host. This might be used if you were + on a host that MXed for a dynamic set of other + hosts. If the set is static, just including the line + "Cw<name1> <name2> ..." is probably superior. + The actual filename can be overridden by redefining + confCW_FILE. +redirect Reject all mail addressed to "address.REDIRECT" with + a ``551 User not local; please try <address>'' message. + If this is set, you can alias people who have left + to their new address with ".REDIRECT" appended. +nouucp Don't do anything special with UUCP addresses at all. +nocanonify Don't pass addresses to $[ ... $] for canonification. + This would generally only be used by sites that only + act as mail gateways or which have user agents that do + full canonification themselves. +notsticky By default, email sent to "user@local.host" are marked + as "sticky" -- that is, the local addresses aren't + matched against UDB and don't go through ruleset 5. + This features disables this treatment. It would + normally be used on network gateway machines. +mailertable Include a "mailer table" which can be used to override + routing for particular domains. The argument of the + FEATURE may be the key definition. If none is specified, + the definition used is: + hash /etc/mailertable -o + Keys in this database are fully qualified domain names + or partial domains preceded by a dot -- for example, + "vangogh.CS.Berkeley.EDU" or ".CS.Berkeley.EDU". + Values must be of the form: + mailer:domain + where "mailer" is the internal mailer name, and "domain" + is where to send the message. These maps are not + reflected into the message header. +domaintable Include a "domain table" which can be used to provide + full domains on unqualified (single word) hosts. The + argument of the FEATURE may be the key definition. If + none is specified, the definition used is: + hash /etc/domaintable -o + The key in this table is the unqualified host name; the + value is the fully qualified domain. Anything in the + domaintable is reflected into headers; that is, this + is done in ruleset 3. +bitdomain Look up bitnet hosts in a table to try to turn them into + internet addresses. The table can be built using the + bitdomain program contributed by John Gardiner Meyers. + The argument of the FEATURE may be the key definition; if + none is specified, the definition used is: + hash /etc/bitdomain.db -o + Keys are the bitnet hostname; values are the corresponding + internet hostname. +uucpdomain Similar feature for UUCP hosts. The default map definition + is: + hash /etc/uudomain.db -o + At the moment there is no automagic tool to build this + database. +always_add_domain + Include the local host domain even on locally delivered + mail. Normally it is not added unless it is already + present. +allmasquerade If masquerading is enabled (using MASQUERADE_AS), this + feature will cause recipient addresses to also masquerade + as being from the masquerade host. Normally they get + the local hostname. Although this may be right for + ordinary users, it can break local aliases. For example, + if you send to "localalias", the originating sendmail will + find that alias and send to all members, but send the + message with "To: localalias@masqueradehost". Since that + alias likely does not exist, replies will fail. Use this + feature ONLY if you can guarantee that the ENTIRE + namespace on your masquerade host supersets all the + local entries. + +Other FEATUREs should be defined, but I was trying to keep these +config files fairly lean and mean. + + ++-------+ +| HACKS | ++-------+ + +Some things just can't be called features. To make this clear, +they go in the hack subdirectory and are referenced using the HACK +macro. These will tend to be site-dependent. The release +includes the Berkeley-dependent "cssubdomain" hack (that makes +sendmail accept local names in either Berkeley.EDU or CS.Berkeley.EDU; +this is intended as a short-term aid while we move hosts into +subdomains. + + ++--------------------+ +| SITE CONFIGURATION | ++--------------------+ + +Complex sites will need more local configuration information, such as +lists of UUCP hosts they speak with directly. This can get a bit more +tricky. For an example of a "complex" site, see cf/ucbvax.mc. + +The SITECONFIG macro allows you to indirectly reference site-dependent +configuration information stored in the siteconfig subdirectory. For +example, the line + + SITECONFIG(uucp.ucbvax, ucbvax, U) + +reads the file uucp.ucbvax for local connection information. The +second parameter is the local name (in this case just "ucbvax" since +it is locally connected, and hence a UUCP hostname) and the name of +the class in which to store the host information. Another SITECONFIG +line reads + + SITECONFIG(uucp.ucbarpa, ucbarpa.Berkeley.EDU, W) + +This says that the file uucp.ucbarpa contains the list of UUCP sites +connected to ucbarpa.Berkeley.EDU. The $=W class will be used to +store this list. [The machine ucbarpa is gone now, but I've left +this out-of-date configuration file around to demonstrate how you +might do this.] + +The siteconfig file (e.g., siteconfig/uucp.ucbvax.m4) contains nothing +more than a sequence of SITE macros describing connectivity. For +example: + + SITE(cnmat) + SITE(sgi olympus) + +The second example demonstrates that you can use two names on the +same line; these are usually aliases for the same host (or are at +least in the same company). + + ++-------------------+ +| TWEAKING RULESETS | ++-------------------+ +For more complex configurations, you can define special rules. +The macro LOCAL_RULE_3 introduces rules that are used in canonicalizing +the names. Any modifications made here are reflected in the header. -HIERARCHY: ----------- +A common use is to convert old UUCP addreses to SMTP addresses using +the UUCPSMTP macro. For example: -The "cf" subdirectory is organized as follows: + LOCAL_RULE_3 + UUCPSMTP(decvax, decvax.dec.com) + UUCPSMTP(research, research.att.com) - cf - | - +---------------+---------------+ - | | | - sitedep m4 cf - | | / \ - *.m4 *.m4 *.mc *.cf +will cause addresses of the form "decvax!user" and "research!user" +to be converted to "user@decvax.dec.com" and "user@research.att.com" +respectively. -where the directories contain the following: +This could also be used to look hosts in a database map: - sitedep Site dependent parts of configuration files - in m4(1) include files. + LOCAL_RULE_3 + R$* < @ $+ > $* $: $1 < @ $(hostmap $2 $) > $3 - m4 Site independent parts of configuration files - in m4(1) include files. +This map would be defined in the LOCAL_CONFIG portion, as shown below. - cf Includes "master configuration" (.mc) files - that include m4 files from ../m4 and ../sitedep. - .mc files are processed by m4(1) and result in - configuration files (.cf). +Similarly, LOCAL_RULE_0 can be used to introduce new parsing rules. +For example, new rules are needed to parse hostnames that you accept +via MX records. For example, you might have: -In the remainder of this document, all path names are referenced -relative to the top level "cf" directory. Hence, the m4 subdirectory -is called "cf/m4". + LOCAL_RULE_0 + R$+ < @ cnmat.Berkeley.EDU > $#uucp $@ cnmat $: $1 +You would use this if you had installed an MX record for cnmat.Berkeley.EDU +pointing at this host; this rule catches the message and forwards it on +using UUCP. -WHERE m4(1) FITS INTO ALL OF THIS: ----------------------------------- +You can also tweak rulesets 1 and 2 using LOCAL_RULE_1 and LOCAL_RULE_2. +These rulesets are normally empty. -Configuration files are built by typing "make" in cf/cf. This -runs m4(1) on the .mc files in cf/cf and produces .cf files. +A similar macro is LOCAL_CONFIG. This introduces lines added after the +boilerplate option setting but before rulesets, and can be used to +declare local database maps or whatever. For example: -The philosophy at Berkeley is to place as much information into -one .mc file as possible, and then use m4 conditional substitution -(ifdef, for example) to produce other configuration files from it. -This results in a substantial reduction of duplicated code that must -be maintained separately. + LOCAL_CONFIG + Khostmap hash /etc/hostmap.db + Kyplocal nis -m hosts.byname -The main master configuration file that contains all this information -is "cf/proto.mc". This file has a number of m4 conditional substitutions -that can be controlled by other .mc files that include "cf/proto.mc". -For example, most hosts at Berkeley have only SMTP (TCP) connections -to other hosts. A file called "ucbproto.cf" takes care of these -machines by defining the m4 flags needed to produce only SMTP mailer -definitions from "cf/proto.mc". Since this is default behavior, very -little needs to be defined. ++---------------------------+ +| MASQUERADING AND RELAYING | ++---------------------------+ -But some hosts at Berkeley (e.g., cad.Berkeley.EDU) have both SMTP -connections and UUCP connections as well. Thus, they need to -produce a configuration file that contains both SMTP and UUCP mailer -definitions, and they need to include a list of directly connected -UUCP neighbors. The file "cf/cad.mc" does this by setting the m4 -flags for "cf/proto.mc" that say "(1) I am a UUCP host with hostname "ucbcad", -(2) a list of my UUCP neighbors can be found in ../sitedep/uucp.cad.m4". +You can have your host masquerade as another using -Thus, there are many .mc files in cf/cf that simply define m4 flags and -then include "cf/proto.mc" to produce a specific configuration file. + MASQUERADE_AS(host.domain) -Note: +This causes outgoing SMTP mail to be labelled as coming from the +indicated domain, rather than $j. One normally masquerades as one +of your own subdomains (for example, it's unlikely that I would +choose to masquerade as an MIT site). - IT IS STRONGLY SUGGESTED THAT YOU, THE SYSTEM MANAGER, - CONTINUE TO MAINTAIN CONFIGURATION FILES BY USING THIS - m4(1) METHOD. TRYING TO MAINTAIN MULTIPLE .CF FILES - ON SEPARATE MACHINES WILL LEAD TO INSANITY. +there are always users that need to be "exposed" -- that is, their +internal site name should be displayed instead of the masquerade name. +Root is an example. You can add users to this list using + EXPOSED_USER(usernames) -With that out of the way, we should now examine the functionality -provided by the supplied sendmail configuration files, and then -talk in detail about the m4(1) options that control this. +This adds users to class E; you could also use something like -FUNCTIONALITY PROVIDED BY THE SUPPLIED SENDMAIL CONFIGURATIONS: ---------------------------------------------------------------- - -Mailers: --------- - -The sendmail configuration files come with the following mailers: + FE/etc/sendmail.cE - local For delivery of messages to users on the - local machine. +You can also arrange to relay all unqualified names (that is, names +without @host) to a relay host. For example, if you have a central +email server, you might relay to that host so that users don't have +to have .forward files or aliases. You can do this using - tcp For SMTP delivery of messages across the - the Internet. - - tcpld For SMTP delivery of messages within the - local domain. + define(`LOCAL_RELAY', mailer:hostname) - uucp For delivery of messages to a UUCP neighbor. +The ``mailer:'' can be omitted, in which case the mailer defaults to +"smtp". There are some user names that you don't want relayed, perhaps +because of local aliases. A common example is root, which may be +locally aliased. You can add entries to this list using - smtpuucp For delivery of messages to a UUCP neighbor - with whom we also share Internet connectivity. + LOCAL_USER(usernames) -The tcp/tcpld mailers and the smtpuucp mailers deserve a little more -explanation. +This adds users to class L; you could also use something like -The "tcp" and "tcpld" mailers: ------------------------------- + FL/etc/sendmail.cL -As regards tcp and tcpld: in theory, there should be only one mailer -here, called "smtp", that deals with addresses in the form "user@host.domain". -Everyone on the Internet would use this, regardless of what domain -they were in. Host name lookups would be performed via the domain naming -system (DNS), and no central registry of machine names would be necessary. +If you want all mail sent to a centralized hub, as for a shared +/var/spool/mail scheme, use -Unfortunately, this is not the case. The MILNET community is still -in transition towards the DNS, and until this transition is complete, -they do not have to use the nameserver. Rather, they can "legally" -still use the host table supplied by SRI-NIC to translate names to -addresses. This means that to be strictly legal, we must send out -messages in the form "user@host.domain" ONLY FOR machines that are -registered with SRI NIC. Machines that are not registered with the -NIC must be "hidden" behind a relay machine, e.g., -user%unregistered_host@registered_host.domain. This, when MILNET folks -reply to this, the mail passes through "registered_host.domain" first. + define(`MAIL_HUB', mailer:hostname) -Currently this "hiding" behind NIC registered hosts is performed by -the "tcp" mailer. +Again, ``mailer:'' defaults to "smtp". If you define both LOCAL_RELAY +and MAIL_HUB, unqualified names and names in class L will be sent to +the LOCAL_RELAY and other local names will be sent to MAIL_HUB. For +example, if are on machine mastodon.CS.Berkeley.EDU, the following +combinations of settings will have the indicated effects: -Since you don't want to register all the hosts at your site with -the NIC (and they don't want you to!), the "tcpld" mailer was created. -The idea here is that you KNOW all the hosts in your local domain -use the nameserver, so there is no need to hide mail behind a NIC -registered relay -- that would only slow things down. So the "tcpld" -does not do any hiding of unregistered hosts behind a registered relay. +email sent to.... eric eric@mastodon.CS.Berkeley.EDU -Eventually the "tcpld" mailer will become the "smtp" mailer mentioned above. +LOCAL_RELAY set to mail.CS.Berkeley.EDU (delivered locally) +mail.CS.Berkeley.EDU -The "smtpuucp" mailer: ----------------------- +MAIL_HUB set to mammoth.CS.Berkeley.EDU mammoth.CS.Berkeley.EDU +mammoth.CS.Berkeley.EDU -The "smtpuucp" mailer is another fun one. As time progresses, many -hosts with whom one has UUCP connections join the Internet. Unfortunately, -if the UUCP connection existed for any length of time, people are -probably used to using it, complete with the bangist syntax that comes -with UUCP. As a result, many sites elect to keep their -UUCP connections even though they have TCP/IP connectivity to the sites -in question. This turns out not be such a good idea because of the -double-queuing incurred by UUCP, explained in the following example. +Both LOCAL_RELAY and mail.CS.Berkeley.EDU mammoth.CS.Berkeley.EDU +MAIL_HUB set as above -For concreteness, consider the following scenario: ucbvax.Berkeley.EDU -(UUCP host "ucbvax") and decvax.dec.com (UUCP host "decvax") have shared -a cross county UUCP link that is heavily used by many people. Let's say -that a piece of mail is routed through the ucbvax-decvax link via UUCP. -The queueing process is: ++-------------------------------+ +| NON-SMTP BASED CONFIGURATIONS | ++-------------------------------+ -step host action ------ ----- ------ -1 ucbvax incoming mail is accepted via UUCP -2 ucbvax uuxqt is queued to invoke sendmail. -3 ucbvax sendmail parses the message. -4 ucbvax sendmail passes the message to the UUCP - mailer for delivery to decvax. -5 ucbvax message is placed in the outgoing UUCP queue for decvax +These configuration files are designed primarily for use by SMTP-based +sites. I don't pretend that they are well tuned for UUCP-only or +UUCP-primarily nodes (the latter is defined as a small local net +connected to the rest of the world via UUCP). However, there is one +hook to handle some special cases. -6 decvax uucico takes the message from ucbvax -7 decvax uuxqt is queued to invoke sendmail -8 decvax sendmail parses the message -9 decvax sendmail passes the message to someplace else. +You can define a ``smart host'' that understands a richer address syntax +using: -Note that the message transited the inbound UUCP queue on ucbvax, the sendmail -queue on ucbvax, the outbound UUCP queue on ucbvax, the inbound UUCP queue -on decvax, etc. + define(`SMART_HOST', mailer:hostname) -Now, if decvax and ucbvax have SMTP connectivity, the session is more -manageable: +In this case, the ``mailer:'' defaults to "suucp". Any messages that +can't be handled using the usual UUCP rules are passed to this host. -step host action ------ ----- ------ -1 ucbvax incoming mail is accepted via UUCP -2 ucbvax uuxqt is queued to invoke sendmail. -3 ucbvax sendmail parses the message -4a ucbvax sendmail delivers it to decvax.dec.com via SMTP. +If you are on a local SMTP-based net that connects to the outside +world via UUCP, you can use LOCAL_NET_CONFIG to add appropriate rules. +For example: -a decvax sendmail accepts the message from ucbvax via SMTP. -8 decvax sendmail parses the message. -9 decvax sendmail passes it to someplace else. + define(`SMART_HOST', suucp:uunet) + LOCAL_NET_CONFIG + R$* < @ $* .$m. > $* $#smtp $@ $2.$m. $: $1 < @ $2.$m. > $3 -Note that old steps (5) and (7), the UUCP queueing, are avoided entirely. +This will cause all names that end in your domain name ($m) via +SMTP; anything else will be sent via suucp (smart UUCP) to uunet. +If you have FEATURE(nocanonify), you may need to omit the dots after +the $m. If you are running a local DNS inside your domain which is +not otherwise connected to the outside world, you probably want to +use: -The only trick to the "smtpuucp" mailer is that even though it uses -SMTP to communicate with its neighbors, it must use the UUCP syntax -and rewriting rulesets so that the operation is transparent to the -outside world. This is easy enough to do. + define(`SMART_HOST', smtp:fire.wall.com) + LOCAL_NET_CONFIG + R$* < @ $* . > $* $#smtp $@ $2. $: $1 < @ $2. > $3 -Other functionality: -------------------- +That is, send directly only to things you found in your DNS lookup; +anything else goes through SMART_HOST. -Aside from the mailers supplied, the basic configuration files -support the following features: - * Domains. Addresses of the form "user@host.domain" are - considered to be the basic type of address we deal with. ++------------------+ +| FlexFAX SOFTWARE | ++------------------+ - * Fake domains. Addresses of the form: +Sam Leffler's FlexFAX software is still in beta test -- but he expects a +public version out "later this week" [as of 3/1/93]. The following +blurb is direct from Sam: - user@host.BITNET - and - user@host.CSNET + $Header: /freefall/a/cvs/386BSD/src/usr.sbin/sendmail/cf/README,v 1.3 1993/07/30 17:20:26 nate Exp $ - are forwarded to a CSNET relay host and a BITNET relay host, - respectively. + How To Obtain This Software (in case all you get is this file) - Note: this feature exists for convenience. As CSNET and - BITNET convert to domains, it will go away. In particular, - "user@host.CSNET" is slated to get the axe in the next release. + The source code is available for public ftp on + sgi.com sgi/fax/v2.1beta.tar.Z + (192.48.153.1) -m4(1) OPTIONS USED IN THE .MC FILES: ------------------------------------- + You can also obtain inst'able images for Silicon Graphics machines from + sgi.com sgi/fax/v2.1beta.inst.tar + (192.48.153.1) -The following options, from "most important" to "trivial", can be -used to control what configuration file you get from running m4(1) -on "cf/proto.mc". As explained earlier, the standard practice is to -create an ".mc" file for a particular configuration that contains -all the m4(1) macro definitions/flags you want, and then have -that .mc file include "mc/proto.mc". + For example, + % ftp -n sgi.com + .... + ftp> user anonymous + ... <type in password> + ftp> cd sgi/fax + ftp> binary + ftp> get v2.1beta.tar.Z -Macro name Example (you must include the ` and ')! ----------- --------------------------------------- - -DOMAIN `DDBerkeley.EDU' - - This MUST be defined to be your Internet domain. - -INTERNET_RELAY `DAcad.Berkeley.EDU' - - If defined, this will be the machine behind which non-NIC registered -hosts are hidden, resulting in addresses of the form - - user%host@internet_relay.domain - -e.g., - - moore%prefect@cad.Berkeley.EDU - - If not defined, outgoing addresses will always be of the form - - user@host.domain - -regardless of whether "host.domain" is registered with the NIC. - -UUCP_NAME `DUucbcad' - - If defined, includes the UUCP mailer and assumes you have local -UUCP connections. The UUCP_NAME macro is used to define your -canonical UUCP hostname. See also: UUCP_ALIASES and UUCP_HOSTS_FILE. - -UUCP_ALIASES `CUucbcad cad' - - Used only if UUCP_NAME is defined, this macro lists any UUCP -aliases for your machine. See also: UUCP_NAME and UUCP_HOSTS_FILE. - -UUCP_HOSTS_FILE `../sitedep/uucp.cad.m4' - - Used only if UUCP_NAME is defined. Defines class V of -local UUCP hosts by including the appropriate m4 file. See also: -UUCP_NAME and UUCP_ALIASES. - -UUCP_RELAY `DRcad.Berkeley.EDU' - - If defined, this will be the machine to which non-local UUCP traffic -is forwarded. That is, any address that ends in ".UUCP" that is -not listed in the V class (as defined by UUCP_HOSTS_FILE) will be -forwarded to the UUCP_RELAY host via the "tcpld" mailer. - -UUCP_ONLY 1 - - If defined, makes sure that no TCP/IP based mailers will be -put in the resulting configuration file. Normally undefined. - -SMTPUUCP `../sitedep/smtpuucp.cad.m4' - - If defined, use SMTP to resolve certain UUCP connections (while -keeping UUCP syntax). Should be defined to be a file that -contains the list of pairs of UUCP names and their corresponding -Internet names. See "machdep/smtpuucp.ucbvax.m4" for an example -of what this should look like. - -BITNET_RELAY `DBjade.Berkeley.EDU' - - If defined, this will be the machine to which BITNET traffic -(i.e., mail to user@host.bitnet) is forwarded. If not defined, -addresses of the form "user@host.bitnet" will bounce. - -CSNET_RELAY `DCrelay.cs.net' - - If defined, this will be the machine to which CSNET traffic -(i.e., mail to user@host.csnet) is forwarded. If not defined, addresses -of that form will bounce. - -ARPAKLUDGE `1' - - If defined, this turns on a kludge to reduce mail load on your -INTERNET_GATEWAY. What is done is the following: if mail is outgoing -to a machine in the ".arpa" domain and we're not registered with the -NIC, we hide ourselves behind our INTERNET_GATEWAY. If the machine -to which mail is being delivered is NOT in the ".arpa" domain, we -assume they use the domain name system and can reply to our address -- -hence, we don't hide anywhere. - -AN EXAMPLE OR TWO: ------------------- - -Let's consider a hypothetical system at Foo, Inc. Foo, Inc. is on the -ARPA Internet and is the proud owner of the domain "foo.com". Machines -in "foo.com" exchange mail with other hosts on the Internet via SMTP. -Foo, Inc. also has customers with whom they have UUCP links -- these -links are all on the machine "uucp-gw.foo.com". Mail to any address -that ends in ".UUCP" should be forwarded to "uucp-gw.foo.com". They -also have a gateway to the bitnet through their local machine -"bitnet-gw.foo.com"; mail to any address ending in ".bitnet" should go -there. They intend to take advantage of the kind folks at CSNET by -forwarding mail to "host.csnet" to the machine "relay.cs.net". - -The master configuration file for a generic machine on the corporate -ethernet might look like this: - -define(DOMAIN, `DDfoo.com') -define(UUCP_RELAY, `DRuucp-gw.foo.com') -define(BITNET_RELAY, `DBbitnet-gw.foo.com') -define(CSNET_RELAY, `DCrelay.cs.net') -include(proto.mc) - -And that's it! The system manager at "foo.com" would simply install -this in the "cf" subdirectory, add a production to the make file, -and type "make foo.cf". This would run m4(1) on the .mc file and -we're done. - -Now let's turn to the machine "uucp-gw.foo.com". It obviously needs -to have the UUCP mailer compiled in, and it needs a list of UUCP -neighbors with whom it is connected. Of course, it also needs a TCP/IP -(SMTP) based mailer so it can talk to the rest of the company. -On the UUCP network, "uucp-gw.foo.com" is known as "foo". -The master configuration file might be: - -define(DOMAIN, `DDfoo.com') -define(UUCP_NAME, `DUfoo') -define(UUCP_ALIASES, `CUfoo') -define(UUCP_HOSTS_FILE, `../sitedep/uucp.foo.m4') -define(BITNET_RELAY, `DBbitnet-gw.foo.com') -define(CSNET_RELAY, `DCrelay.cs.net') -include(proto.mc) - -Note that we didn't define UUCP_RELAY here, since we ARE the UUCP relay. - -The file "../sitedep/uucp.foo.m4" contains a list of our UUCP neighbors: - -CV oink -CV bletch -CV spam - -indicating that "uucp-gw.foo.com" is directly connected to three other -machines via UUCP: "oink", "bletch", and "spam." - - -WHAT WILL BE CHANGING IN THE NEXT RELEASE: ------------------------------------------- - -In the next release, the following things should change: - -1. The MILNET should have gotten its act together. This means - that the "tcp" mailer goes away. The "tcpld" mailer will - be renamed "smtp". The "N" class (NIC registered hosts) - gets axed. The ARPAKLUDGE and INTERNET_RELAY m4(1) options - will disappear as well. - -2. The CSNET "fake domain" (i.e., user@host.csnet) will cease - to be supported. - -3. The "smtp" mailer rulesets (currently 17/27) will be rewritten, - along with much of rulesets 0 and 3. + If you cannot use FTP at all, there is a service called "ftpmail" + available from gateekeeper.dec.com: you can send e-mail to this + machine and it will use FTP to retrieve files for you and send you the + files back again via e-mail. To find out more about the ftpmail + service, send a message to "ftpmail@gatekeeper.dec.com" whose body + consists of the single line "help". + + Internal to Silicon Graphics there are inst'able images on the host + flake.asd in the directory /d/dist. Thus you can do something like: + + % inst -f flake.asd.sgi.com:/d/dist/flexfax + + to install the software on your machine. + + The external distributions come in a compressed or uncompressed tar + file. To extract the source distribution: + + % zcat v2.1beta.tar.Z | tar xf - + + (uncompress and extract individual files in current directory). To + unpack and install the client portion of the inst'able distribution: + + % mkdir dist + % cd dist; tar xf ../v2.1beta.inst.tar; cd .. + % inst -f dist/flexfax + ... + inst> go + + (Note, the dist subdirectory is because some versions of inst fail if + the files are in the current directory.) Server binaries is also + included in the inst'able images as flexfax.server.*. It is not + installed by default, so to get it also you need to extract the do: + + % inst -f flexfax + ... + inst> install flexfax.server.* + inst> go + + The SGI binaries were built for Version 4.0.5 of the IRIX operating + system. They should work w/o problem on earlier versions of the + system, but I have not fully tested this. Also, note that to install a + server on an SGI machine, you need to have installed the Display + PostScript execution environment product (dps_eoe). Otherwise, the fax + server will not be able to convert PostScript to facsimile for + transmission. + + If you are working from the source distribution, look at the file README + in the top of the source tree. If you are working from the inst images, + you need to run faxaddmodem to setup and configure your fax modem. Do + man faxaddmodem for more information. + +Also from Sam: + + A mailing list for users of this software is located on sgi.com. + If you want to join this mailing list or have a list-related request + such as getting your name removed from it, send a request to + + flexfax-request@sgi.com + + Submissions (including bug reports) should be directed to: + + flexfax@sgi.com + + ++--------------------------------+ +| TWEAKING CONFIGURATION OPTIONS | ++--------------------------------+ + +There are a large number of configuration options that don't normally +need to be changed. However, if you feel you need to tweak them, you +can define the following M4 variables. This list is shown in four +columns: the name you define, the default value for that definition, +the option or macro that is affected (either Ox for an option or Dx +for a macro), and a brief description. Greater detail of the semantics +can be found in the Installation and Operations Guide. + +Some options are likely to be deprecated in future versions -- that is, +the option is only included to provide back-compatibility. These are +marked with "*". + +M4 Variable Name Default Mac/Opt Description +confMAILER_NAME MAILER-DAEMON Dn The sender name used for + internally generated + outgoing messages. +confFROM_LINE From $g $d Dl The From_ line used when + sending to files or programs. +confFROM_HEADER $?x$x <$g>$|$g$. The format of an internally + Dq generated From: address. +confOPERATORS .:%@!^/[] Do Address operator characters. +confSTMP_LOGIN_MSG $j Sendmail $v/$Z ready at $b + De The initial (spontaneous) + SMTP greeting message. +confSEVEN_BIT_INPUT False O7 Force input to seven bits? +confALIAS_WAIT 10 Oa Wait (in minutes) for alias + file rebuild. +confMIN_FREE_BLOCKS 4 Ob Minimum number of free blocks + on queue filesystem to accept + SMTP mail. +confBLANK_SUB . OB Blank (space) substitution + character. +confCON_EXPENSIVE False Oc Connect immediately to + mailers marked expensive? +confCHECKPOINT_INTERVAL 10 OC Checkpoint queue files + every N recipients. +confDELIVERY_MODE background Od Default delivery mode. +confAUTO_REBUILD False OD Automatically rebuild + alias file if needed. +confERROR_MODE (undefined) Oe Error message mode. +confERROR_MESSAGE (undefined) OE Error message header/file. +confSAVE_FROM_LINES False Of Save extra leading + From_ lines. +confTEMP_FILE_MODE 0600 OF Temporary file mode. +confDEF_GROUP_ID 1 Og Default group id. +confMATCH_GECOS False OG Match GECOS field. +confMAX_HOP 17 Oh Maximum hop count. +confIGNORE_DOTS False Oi * Ignore dot as terminator + for incoming messages? +confBIND_OPTS (empty) OI Default options for BIND. +confMIME_FORMAT_ERRORS True Oj * Send error messages as MIME- + encapsulated messages per + RFC 1344. +confMCI_CACHE_SIZE 2 Ok Size of open connection cache. +confMCI_CACHE_TIMEOUT 5m OK Open connection cache timeout. +confUSE_ERRORS_TO False Ol * Use the Errors-To: header to + deliver error messages. This + should not be necessary because + of general acceptance of the + envelope/header distinction. +confLOG_LEVEL 9 OL Log level. +confME_TOO False Om Include sender in group + expansions. +confCHECK_ALIASES True On Check RHS of aliases when + running newaliases. +confOLD_STYLE_HEADERS True Oo * Assume that headers without + special chars are old style. +confDAEMON_OPTIONS (undefined) OO SMTP daemon options. +confPRIVACY_FLAGS authwarnings Op Privacy flags. +confCOPY_ERRORS_TO (undefined) OP Address for additional copies + of all error messages. +confQUEUE_FACTOR (undefined) Oq Slope of queue-only function +confREAD_TIMEOUT (undefined) Or SMTP read timeouts. +confSAFE_QUEUE True Os * Commit all messages to disk + before forking. +confMESSAGE_TIMEOUT 5d/4h OT Timeout for messages before + sending error/warning message. +confTIME_ZONE USE_SYSTEM Ot Time zone info -- can be + USE_SYSTEM to use the system's + idea, USE_TZ to use the user's + TZ envariable, or something + else to force that value. +confDEF_USER_ID 1 Ou Default user id. +confUSERDB_SPEC (undefined) OU User database specification. +confFALLBACK_MX (undefined) OV Fallback MX host. +confQUEUE_LA 8 Ox Load average at which queue-only + function kicks in. +confREFUSE_LA 12 OX Load average at which incoming + SMTP connections are refused. +confWORK_RECIPIENT_FACTOR + (undefined) Oy Cost of each recipient. +confSEPARATE_PROC False OY Run all deliveries in a + separate process. +confWORK_CLASS_FACTOR (undefined) Oz Priority multiplier for class. +confWORK_TIME_FACTOR (undefined) OZ Cost of each delivery attempt. +confCW_FILE /etc/sendmail.cw Name of file used to get the + Fw local additions to the $=w + class. + + ++-----------+ +| HIERARCHY | ++-----------+ + +Within this directory are several subdirectories, to wit: + +m4 General support routines. These are typically + very important and should not be changed without + very careful consideration. + +cf The configuration files themselves. They have + ".mc" suffixes, and must be run through m4 to + become complete. The resulting output should + have a ".cf" suffix. + +ostype Definitions describing a particular operating + system type. These should always be referenced + using the OSTYPE macro in the .mc file. Examples + include "bsd4.3", "bsd4.4", "sunos3.5", and + "sunos4.1". + +domain Definitions describing a particular domain, referenced + using the DOMAIN macro in the .mc file. These are + site dependent; for example, we contribute "cs.exposed.m4" + and "cs.hidden.m4" which both describe hosts in the + CS.Berkeley.EDU subdomain; the former displays the local + hostname (e.g., mammoth.CS.Berkeley.EDU), whereas the + latter does its best to hide the identity of the local + workstation inside the CS subdomain. + +mailer Descriptions of mailers. These are referenced using + the MAILER macro in the .mc file. + +sh Shell files used when building the .cf file from the + .mc file in the cf subdirectory. + +feature These hold special orthogonal features that you might + want to include. They should be referenced using + the FEATURE macro. + +hack Local hacks. These can be referenced using the HACK + macro. They shouldn't be of more than voyeuristic + interest outside the .Berkeley.EDU domain, but who knows? + We've all got our own peccadilloes. + +siteconfig Site configuration -- e.g., tables of locally connected + UUCP sites. + + ++------------------------+ +| ADMINISTRATIVE DETAILS | ++------------------------+ + +The following sections detail usage of certain internal parts of the +sendmail.cf file. Read them carefully if you are trying to modify +the current model. If you find the above descriptions adequate, these +should be {boring, confusing, tedious, ridiculous} (pick one or more). + +RULESETS (* means built in to sendmail) + + 0 * Parsing + 1 * Sender rewriting + 2 * Recipient rewriting + 3 * Canonicalization + 4 * Post cleanup + 5 * Local address rewrite (after aliasing) + 1x mailer rules (sender qualification) + 2x mailer rules (recipient qualification) + 90 Mailertable host stripping + 96 Bottom half of Ruleset 3 (ruleset 6 in old sendmail) + 97 Hook for recursive ruleset 0 call (ruleset 7 in old sendmail) + + +MAILERS + + 0 local, prog local and program mailers + 1 smtp SMTP channel + 2 uucp UNIX-to-UNIX Copy Program + 3 netnews Network News delivery + 4 fax Sam Leffler's FlexFAX software + + +MACROS + + A + B Bitnet Relay + C CSNET Relay + D The local domain -- usually not needed + E + F FAX Relay + G + H mail Hub (for mail clusters) + I + J + K + L + M Masquerade (who I claim to be) + N + O + P + Q + R Relay (for unqualified names) + S Smart Host + T + U my UUCP name (if I have a UUCP connection) + V UUCP Relay (class V hosts) + W UUCP Relay (class W hosts) + X UUCP Relay (class X hosts) + Y UUCP Relay (all other hosts) + Z Version number + + +CLASSES + + A + B + C + D + E addresses that should not seem to come from $M + F hosts we forward for + G + H + I + J + K + L addresses that should not be forwarded to $R + M + N + O operators that indicate network operations (cannot be in local names) + P top level pseudo-domains: BITNET, FAX, UUCP, etc. + Q + R + S + T + U locally connected UUCP hosts + V UUCP hosts connected to relay $V + W UUCP hosts connected to relay $W + X UUCP hosts connected to relay $X + Y locally connected smart UUCP hosts + Z + . the class containing only a dot + + +M4 DIVERSIONS + + 1 Local host detection and resolution + 2 Local Ruleset 3 additions + 3 Local Ruleset 0 additions + 4 UUCP Ruleset 0 additions + 5 locally interpreted names (overrides $R) + 6 local configuration (at top of file) + 7 mailer definitions + 8 special local name recognition (late in ruleset 3) + 9 special local rulesets (1 and 2) diff --git a/usr.sbin/sendmail/cf/cf/Makefile b/usr.sbin/sendmail/cf/cf/Makefile index 1c5f3679dc..37ca97b01a 100644 --- a/usr.sbin/sendmail/cf/cf/Makefile +++ b/usr.sbin/sendmail/cf/cf/Makefile @@ -1,79 +1,54 @@ -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -###################################################################### -# -# Makefile for Sendmail UCB configuration files -# -###################################################################### +# @(#)Makefile 8.1 (Berkeley) 6/7/93 +M4= m4 +#M4= /usr/src/usr.bin/m4/obj/m4 +CHMOD= chmod +ROMODE= 444 +RM= rm -f -NSSRCS = cad.mc cadgroup.mc cogsci.mc okeeffe.mc ucbarpa.mc ucbvax.mc \ - cc.mc cchem.mc ic.mc id.mc - -GENSRCS = proto.mc - -NSALL = cad.cf cadgroup.cf cogsci.cf okeeffe.cf ucbarpa.cf ucbvax.cf \ - cc.cf cchem.cf ic.cf id.cf - -PROTOS = ucbtcp.cf uucpproto.cf tcpproto.cf tcpuucpproto.cf - -EXP = x-ucbtcp_fw.cf - -UUCP = ../m4/uucpm.m4 ../m4/suucpm.m4 ../m4/rule5.m4 \ - ../m4/smtpuucpm.m4 - -ALL = $(NSALL) $(PROTOS) $(EXP) - -GET = sccs get - -BLDFILE = buildinfo - -.SUFFIXES: .mc .cf +.SUFFIXES: .mc .cf .mc.cf: - rm -f $(BLDFILE) - echo "# built by `whoami` on `date`" > $(BLDFILE) - echo "# in `pwd` on `hostname`" >> $(BLDFILE) - m4 < $*.mc > $*.cf - rm -f $(BLDFILE) + $(RM) $@ + (cd ${.CURDIR} && $(M4) ${@:R}.mc) > $@ + $(CHMOD) $(ROMODE) $@ + +ALL= cs-hidden.cf cs-exposed.cf \ + hpux-cs-exposed.cf hpux-cs-hidden.cf \ + sunos3.5-cs-exposed.cf sunos3.5-cs-hidden.cf \ + sunos4.1-cs-exposed.cf sunos4.1-cs-hidden.cf \ + ultrix4.1-cs-exposed.cf ultrix4.1-cs-hidden.cf \ + mail.cs.cf mail.eecs.cf ucbvax.cf vangogh.cf \ + chez.cf knecht.cf cogsci.cf alpha.cf s2k.cf auspex.cf \ + python.cf sun-lamp.cf boat-anchor.cf\ + tcpproto.cf uucpproto.cf all: $(ALL) -clean: - rm -f $(ALL) a.out core make.out - rm -f ,* - -# -# Standard files included by all -# -$(ALL): ../m4/nsmacros.m4 ../m4/nsclasses.m4 ../sitedep/nicregistered.m4 \ - ../m4/version.m4 ../m4/boilerplate.m4 ../m4/prewriterule.m4 \ - ../m4/postwriterule.m4 ../m4/rule3.m4 ../m4/localm.m4 ../m4/nstcpm.m4 \ - ../m4/nstcpldm.m4 ../m4/rule0.m4 ../m4/fake_domains.m4 - -# -# Special include files used only by specific hosts -# -cad.cf: proto.mc $(UUCP) ../sitedep/uucp.cad.m4 \ - ../sitedep/smtpuucp.cad.m4 -cadgroup.cf: proto.mc -cc.cf: proto.mc -cchem.cf: proto.mc -ic.cf: proto.mc $(UUCP) ../sitedep/uucp.ic.m4 -id.cf: proto.mc $(UUCP) ../sitedep/uucp.id.m4 -cogsci.cf: proto.mc $(UUCP) ../sitedep/uucp.cogsci.m4 -okeeffe.cf: proto.mc $(UUCP) ../sitedep/uucp.okeeffe.m4 -ucbarpa.cf: proto.mc $(UUCP) ../sitedep/uucp.ucbarpa.m4 -ucbvax.cf: $(UUCP) ../sitedep/uucp.cad.m4 ../sitedep/uucp.cogsci.m4 \ - ../sitedep/uucp.ucbarpa.m4 ../sitedep/uucp.ucbvax.m4 \ - ../sitedep/smtpuucp.ucbvax.m4 -# prototypes -uucpproto.cf: proto.mc $(UUCP) ../sitedep/uucp.proto.m4 -tcpproto.cf: proto.mc +clean cleandir: + $(RM) $(ALL) core + +depend install: + +# this is overkill, but.... +M4FILES=../domain/cs.exposed.m4 \ + ../domain/cs.hidden.m4 \ + ../domain/Berkeley.m4 \ + ../feature/use_cw_file.m4 \ + ../hack/cssubdomain.m4 \ + ../m4/cf.m4 \ + ../m4/proto.m4 \ + ../m4/version.m4 \ + ../mailer/local.m4 \ + ../mailer/smtp.m4 \ + ../mailer/uucp.m4 \ + ../ostype/bsd4.3.m4 \ + ../ostype/bsd4.4.m4 \ + ../ostype/riscos4.5.m4 \ + ../ostype/sunos3.5.m4 \ + ../ostype/sunos4.1.m4 \ + ../ostype/ultrix4.1.m4 \ + +$(ALL): $(M4FILES) + +.include <bsd.prog.mk> diff --git a/usr.sbin/sendmail/cf/cf/README b/usr.sbin/sendmail/cf/cf/README deleted file mode 100644 index 9d114685cc..0000000000 --- a/usr.sbin/sendmail/cf/cf/README +++ /dev/null @@ -1,32 +0,0 @@ -This directory contains cf files for these machines: - - cad /* cad.berkeley.edu */ - cadgroup /* any cadgroup machine */ - cc /* computer center microvax */ - cchem /* any machine in college of chemistry */ - cogsci /* cogsci (to deal with uucp link) */ - ic /* ic (to deal with uucp link) */ - okeeffe /* okeeffe (to deal with uucp link) */ - ucbarpa /* ucbarpa (to deal with uucp link) */ - ucbvax /* ucbvax (to deal with uucp link) */ - - ucbproto.cf /* any Berkeley nameserver/hosttable machine */ - - tcpproto.cf /* any TCP/IP (SMTP) based machine */ - uucproto.cf /* any UUCP only machine */ - tcpuucpproto.cf /* a UUCP hub on a TCP/IP (SMTP) network */ - -Most all of the good stuff is contained in proto.mc. For all -machines except ucbvax, all the .mc files do is define some m4 -constants and include proto.mc. - -To make a file, say - - make cad.cf - -for example. - -N.B.: if you intend to maintain configuration files for more than -one or two machines, you should probably create ".mc" files along -the lines of the machine names above and use m4. If you just try -to modify ".cf" files you'll eventually go insane. diff --git a/usr.sbin/sendmail/cf/cf/alpha.mc b/usr.sbin/sendmail/cf/cf/alpha.mc new file mode 100644 index 0000000000..026fed11ff --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/alpha.mc @@ -0,0 +1,41 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)alpha.mc 8.1 (Berkeley) 6/7/93') +OSTYPE(osf1)dnl +DOMAIN(s2k)dnl +MAILER(local)dnl +MAILER(smtp)dnl diff --git a/usr.sbin/sendmail/cf/cf/auspex.mc b/usr.sbin/sendmail/cf/cf/auspex.mc new file mode 100644 index 0000000000..961c139e2c --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/auspex.mc @@ -0,0 +1,42 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)auspex.mc 8.1 (Berkeley) 6/7/93') +OSTYPE(sunos4.1)dnl +DOMAIN(cs.hidden)dnl +FEATURE(use_cw_file)dnl +MAILER(local)dnl +MAILER(smtp)dnl diff --git a/usr.sbin/sendmail/cf/cf/boat-anchor.mc b/usr.sbin/sendmail/cf/cf/boat-anchor.mc new file mode 100644 index 0000000000..37417950d0 --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/boat-anchor.mc @@ -0,0 +1,47 @@ +divert(-1) +# +# Copyright (c) 1993 Adam Glass +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988 The Regents of the University of California. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)boat-anchor.mc $Revision: 1.1 $') +OSTYPE(bsd4.4)dnl +MAILER(local)dnl +MAILER(smtp)dnl +define(`UUCP_RELAY', ucbvax.Berkeley.EDU)dnl +define(`BITNET_RELAY', mailhost.Berkeley.EDU)dnl +define(`CSNET_RELAY', mailhost.Berkeley.EDU)dnl +define(`LOCAL_RELAY', sun-lamp.CS.Berkeley.EDU) +define(`MAIL_HUB', sun-lamp.CS.Berkeley.EDU) +define(`confCHECKPOINT_INTERVAL', 4)dnl diff --git a/usr.sbin/sendmail/cf/cf/cad.cf b/usr.sbin/sendmail/cf/cf/cad.cf deleted file mode 100644 index 91697096e2..0000000000 --- a/usr.sbin/sendmail/cf/cf/cad.cf +++ /dev/null @@ -1,627 +0,0 @@ -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)cad.mc 1.11 (Berkeley) 1/24/89 -# @(#)proto.mc 1.20 (Berkeley) 1/25/89 -# -# built by phil on Wed Jan 25 11:46:36 PST 1989 -# in /usr/src/local/nettables/sendmail.cf/cf on monet.Berkeley.EDU -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -############################################################ -############################################################ - - -################## -# local info # -################## - -# file containing our internet aliases -Fw/usr/lib/sendmail.cw - -# uucp hostnames -DUucbcad -CUucbcad - -# local UUCP connections -CV a -CV ames -CV analog -CV ardent dana -CV arthur -CV biosys -CV black -CV boulder -CV evans -CV harpo -CV harris -CV hpda -CV hpsrla -CV island -CV jupiter -CV masscomp masscom -CV nsc -CV oakhill -CV octal -CV sda -CV stellar -CV teklds -CV tektronix tektron -CV binky tonto -CV toshiba tsb0 -CV ucbcad -CV ucbvax -CV voder -CV vogon - - -############################# -### Setup Information ### -############################# - - -###################### -# General Macros # -###################### - -# local domain name -DDBerkeley.EDU - - - -# UUCP relay host -DRucbvax.Berkeley.EDU - -# csnet relay host -DCrelay.cs.net - -# bitnet relay host -DBjade.Berkeley.EDU - -# my official hostname -Dj$w - - - -############### -# Classes # -############### - -# Internal ("fake") domains that we use in rewriting -CIUUCP BITNET CSNET - - - - -###################### -# Version Number # -###################### - -DZ1.34 - - -###################### -# Special macros # -###################### - -# my name -DnMAILER-DAEMON -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$Z ready at $b - -############### -# Options # -############### - -# location of alias file -OA/usr/lib/aliases -# wait up to ten minutes for alias file rebuild -Oa10 -# substitution for space (blank) characters -OB. -# (don't) connect to "expensive" mailers -#Oc -# default delivery mode (deliver in background) -Odbackground -# temporary file mode -OF0600 -# default GID -Og1 -# location of help file -OH/usr/lib/sendmail.hf -# log level -OL9 -# default network name -ONARPA -# default messages to old style -Oo -# queue directory -OQ/usr/spool/mqueue -# read timeout -- violates protocols -Or2h -# status file -OS/usr/lib/sendmail.st -# queue up everything before starting transmission -Os -# default timeout interval -OT3d -# time zone names (V6 only) -OtPST,PDT -# default UID -Ou1 -# wizard's password -OW* -# load average at which we just queue messages -Ox8 -# load average at which we refuse connections -OX12 - -########################### -# Message precedences # -########################### - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -##################### -# Trusted users # -##################### - -Troot -Tdaemon -Tuucp - -######################### -# Format of headers # -######################### - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$Z) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -# HPosted-Date: $a -# H?l?Received-Date: $b -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - - - -########################### -### Rewriting Rules ### -########################### - - -################################ -# Sender Field Pre-rewriting # -################################ -S1 -#R$*<$*>$* $1$2$3 defocus - -################################### -# Recipient Field Pre-rewriting # -################################### -S2 -#R$*<$*>$* $1$2$3 defocus - - - -################################# -# Final Output Post-rewriting # -################################# -S4 - -R@ $@ handle <> error addr - -# resolve numeric addresses to name if possible -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 lookup numeric internet addr - -# externalize local domain info -R$*<$+>$* $1$2$3 defocus -R@$+:@$+:$+ @$1,@$2:$3 <route-addr> canonical - -# UUCP must always be presented in old form -R$+@$-.UUCP $2!$1 u@h.UUCP => h!u - -# delete duplicate local names -R$+%$=w@$=w $1@$w u%host@host => u@host -R$+%$=w@$=w.$D $1@$w u%host@host => u@host - - -########################### -# Name Canonicalization # -########################### -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -- note RFC733 heuristic here -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 basic RFC821/822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+ @$1:$2 change all "," to ":" - -# localize and dispose of route-based addresses -R@$+:$+ $@$>6<@$1>:$2 handle <route-addr> - -# more miscellaneous cleanup -R$+ $:$>8$1 host dependent cleanup -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style addresses to a domain-based address -R$+^$+ $1!$2 convert ^ to ! -R$-!$+ $@$>6$2<@$1.UUCP> resolve uucp names -R$+.$-!$+ $@$>6$3<@$1.$2> domain uucps -R$+!$+ $@$>6$2<@$1.UUCP> uucp subdomains -R$+%$+ $:$>9$1%$2 user%host -R$+<@$+> $@$>6$1<@$2> already canonical -R$-.$+ $@$>6$2<@$1> host.user - - -################################# -# special local conversions # -################################# - -S6 -R$*<@$=w>$* $:$1<@$w>$3 get into u@$w form -R$*<@$=w.$D>$* $:$1<@$w>$3 -R$*<@$=U.UUCP>$* $:$1<@$w>$3 - -################################ -# Change rightmost % to @. # -################################ - -S9 -R$*%$* $1@$2 First make them all @'s. -R$*@$*@$* $1%$2@$3 Undo all but the last. -R$*@$* $@$1<@$2> Put back the brackets. - - - -################### -### Mailers ### -################### - - -############################################################ -############################################################ -##### -##### Local and Program Mailer specification -##### -############################################################ -############################################################ - -Mlocal, P=/bin/mail, F=rlsDFMmn, S=10, R=20, A=mail -d $u -Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u - -S10 -R@ $n errors to mailer-daemon - - -############################################################ -############################################################ -##### -##### Local Domain SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to remain -##### the local domain. Hence, they can refer to hosts that are -##### not registered in the NIC host table. -##### -############################################################ -############################################################ - -Mtcpld, P=[IPC], F=mDFMueXLC, S=17, R=27, A=IPC $h, E=\r\n - -S17 - -# cleanup forwarding a bit -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 canonicalize -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.bitnet -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S27 - -# cleanup -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### Internet SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to leave -##### the local domain -- hence, they must be canonical according to -##### RFC822 etc. This means that machines not registered with -##### the NIC must be hidden behind our Internet relay. -##### -############################################################ -############################################################ - -Mtcp, P=[IPC], F=mDFMueXLC, S=14, R=24, A=IPC $h, E=\r\n - -S14 - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output internal ("fake") domains as "user%host@relay" - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S24 - -# put in <> kludge -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere..... -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# Hide fake domains behind relays - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### UUCP Mailer specification -##### -############################################################ -############################################################ - - -Muucp, P=/usr/bin/uux, F=DFMhuU, S=13, R=23, M=100000, - A=uux - -r -z -a$f -gC $h!rmail ($u) - -S13 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$+> $2!$1 uucpize (no @'s in addr) -R$w!$+ $1 strip local name -R$+ $:$U!$1 stick on our host name -R$=U!$-%$- $:$1!$2@$3.$D ucbvax!user@host.domain - -S23 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$w> $U!$1 a!b@here -> here!a!b -R$=U!$+ $2 here!a!b -> a!b -# sanity ... should not happen. -R$=U.$D!$+ $2 strip local name.domain - - -############################################################ -############################################################ -##### -##### Provide Backward Compatibility -##### -############################################################ -############################################################ - -##################################################### -# General code to convert back to old style names # -##################################################### -S5 - -R$+<@$w> $1 strip host -R$+<@$-.UUCP> $2!$1 u@host.UUCP => host!u - - -############################################################ -############################################################ -##### -##### SMTP UUCP Mailer specification -##### -##### This mailer sends UUCP traffic over an SMTP connection. -##### Obviously, we only want to do this with UUCP hosts with -##### whom we have SMTP connectivity. The idea here is to -##### avoid having to double queue (once for sendmail, once -##### for UUCP) when there's no need. Since we need to -##### preserve uucp-ness (e.g., bangs), we use the UUCP mailer -##### rewriting rulesets. -##### -############################################################ -############################################################ - -Msmtpuucp, P=[IPC], F=mDFMueXLC, S=13, R=23, A=IPC $h, E=\r\n - - - - -##################### -### Rule Zero ### -##################### - - -############################################################ -############################################################ -##### -##### RULESET ZERO PREAMBLE -##### -##### The beginning of ruleset zero is constant through all -##### configurations. -##### -############################################################ -############################################################ - -S0 - -# first make canonical -R$*<$*>$* $1$2$3 defocus -R$+ $:$>3$1 make canonical - -# handle special cases -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 numeric internet addr -R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec -R$+ $:$>6$1 -R$-<@$w> $#local$:$1 -R@ $#error$:Invalid address handle <> form - -# canonicalize using the nameserver if not internal domain -R$*<@$*.$~I>$* $:$1<@$[$2.$3$]>$4 -R$*<@$->$* $:$1<@$[$2$]>$3 -R$*<@$->$* $:$1<@$2.$D>$3 if nameserver fails - -# now delete the local info -R<@$w>:$* $@$>0$1 @here:... -> ... -R$*<@$w> $@$>0$1 ...@here -> ... - -################################## -# End of ruleset zero preamble # -################################## - - -############################################### -### Machine dependent part of Rule Zero ### -############################################### - -# resolve SMTP UUCP connections - -R<@ames.uucp>:$+ $#smtpuucp$@ames.arc.nasa.gov$:$2 -R$*<@ames.uucp> $#smtpuucp$@ames.arc.nasa.gov$:$1 -R<@ucbvax.uucp>:$+ $#smtpuucp$@ucbvax.berkeley.edu$:$2 -R$*<@ucbvax.uucp> $#smtpuucp$@ucbvax.berkeley.edu$:$1 - - -# resolve local UUCP connections -R<@$=V.UUCP>:$+ $#uucp$@$1$:$2 @host.UUCP:... -R$+<@$=V.UUCP> $#uucp$@$2$:$1 user@host.UUCP - - -# resolve fake top level domains by forwarding to other hosts -R$*<@$+.BITNET>$* $#tcp$@$B$:$1<@$2.BITNET>$3 user@host.BITNET -R$*<@$+.CSNET>$* $#tcp$@$C$:$1<@$2.CSNET>$3 user@host.CSNET - - -# forward non-local UUCP traffic to our UUCP relay -R$*<@$*.UUCP>$* $#tcpld$@$R$:$1<@$2.UUCP> uucp mail - -# hide behind our internet relay when talking to people in the arpa domain -R$*<@$*.arpa>$* $#tcp$@$2.arpa$:$1<@$2.arpa>$3 user@host.arpa - -# but speak domains to them if they speak domains too -R$*<@$*>$* $#tcpld$@$2$:$1<@$2>$3 user@host.domain - -# remaining names must be local -R$+ $#local$:$1 everything else diff --git a/usr.sbin/sendmail/cf/cf/cad.mc b/usr.sbin/sendmail/cf/cf/cad.mc deleted file mode 100644 index 493a806131..0000000000 --- a/usr.sbin/sendmail/cf/cf/cad.mc +++ /dev/null @@ -1,18 +0,0 @@ -divert(10)dnl -# -# cad configuration file -# -# @(#)cad.mc 1.11 (Berkeley) 1/24/89 -# -define(DOMAIN, `DDBerkeley.EDU') -define(UUCP_NAME, DUucbcad) -define(UUCP_ALIASES, CUucbcad) -define(UUCP_HOSTS_FILE, ../sitedep/uucp.cad.m4) -define(UUCP_RELAY, DRucbvax.Berkeley.EDU) -define(SMTPUUCP, ../sitedep/smtpuucp.cad.m4) -define(BITNET_RELAY, `DBjade.Berkeley.EDU') -define(CSNET_RELAY, `DCrelay.cs.net') -define(ARPAKLUDGE, `1'); -define(EXTERNAL_VERSION, ``#' `@(#)cad.mc 1.11' (Berkeley) `1/24/89'') -divert(0)dnl -include(proto.mc)dnl diff --git a/usr.sbin/sendmail/cf/cf/cadgroup.cf b/usr.sbin/sendmail/cf/cf/cadgroup.cf deleted file mode 100644 index 0df9fbb62e..0000000000 --- a/usr.sbin/sendmail/cf/cf/cadgroup.cf +++ /dev/null @@ -1,547 +0,0 @@ -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)cadgroup.mc 1.7 (Berkeley) 1/3/89 -# @(#)proto.mc 1.20 (Berkeley) 1/25/89 -# -# built by phil on Wed Jan 25 11:46:46 PST 1989 -# in /usr/src/local/nettables/sendmail.cf/cf on monet.Berkeley.EDU -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -############################################################ -############################################################ - - -################## -# local info # -################## - -# file containing our internet aliases -Fw/usr/lib/sendmail.cw - - - -############################# -### Setup Information ### -############################# - - -###################### -# General Macros # -###################### - -# local domain name -DDBerkeley.EDU - -# Internet relay host -- machines in our domain that are not -# registered with the NIC will be "hidden" behind this relay machine -# with the % kludge, although SMTP delivery will still be performed -# by the sending machine. Someday this will go away. -DAcad.Berkeley.EDU - -# UUCP relay host -DRcad.Berkeley.EDU - -# csnet relay host -DCrelay.cs.net - -# bitnet relay host -DBjade.Berkeley.EDU - -# my official hostname -Dj$w - - - -############### -# Classes # -############### - -# Internal ("fake") domains that we use in rewriting -CIUUCP BITNET CSNET - - - -############################################################ -############################################################ -##### -##### BERKELEY HOSTS REGISTERED WITH THE NIC -##### -############################################################ -############################################################ - - -CNbach ucbbach -CNbizet ucbbizet -CNcad ucbcad -CNdegas ucbdegas -CNeast ucbeast -CNernie ucbernie -CNesvax ucbesvax -CNjade ucbjade -CNji ucbji -CNmike ucbmike -CNmonet ucbmonet -CNpostgres -CNrenoir ucbrenoir -CNucbarpa -CNucbvax -CNviolet ucbviolet - - -###################### -# Version Number # -###################### - -DZ1.34 - - -###################### -# Special macros # -###################### - -# my name -DnMAILER-DAEMON -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$Z ready at $b - -############### -# Options # -############### - -# location of alias file -OA/usr/lib/aliases -# wait up to ten minutes for alias file rebuild -Oa10 -# substitution for space (blank) characters -OB. -# (don't) connect to "expensive" mailers -#Oc -# default delivery mode (deliver in background) -Odbackground -# temporary file mode -OF0600 -# default GID -Og1 -# location of help file -OH/usr/lib/sendmail.hf -# log level -OL9 -# default network name -ONARPA -# default messages to old style -Oo -# queue directory -OQ/usr/spool/mqueue -# read timeout -- violates protocols -Or2h -# status file -OS/usr/lib/sendmail.st -# queue up everything before starting transmission -Os -# default timeout interval -OT3d -# time zone names (V6 only) -OtPST,PDT -# default UID -Ou1 -# wizard's password -OW* -# load average at which we just queue messages -Ox8 -# load average at which we refuse connections -OX12 - -########################### -# Message precedences # -########################### - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -##################### -# Trusted users # -##################### - -Troot -Tdaemon -Tuucp - -######################### -# Format of headers # -######################### - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$Z) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -# HPosted-Date: $a -# H?l?Received-Date: $b -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - - - -########################### -### Rewriting Rules ### -########################### - - -################################ -# Sender Field Pre-rewriting # -################################ -S1 -#R$*<$*>$* $1$2$3 defocus - -################################### -# Recipient Field Pre-rewriting # -################################### -S2 -#R$*<$*>$* $1$2$3 defocus - - - -################################# -# Final Output Post-rewriting # -################################# -S4 - -R@ $@ handle <> error addr - -# resolve numeric addresses to name if possible -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 lookup numeric internet addr - -# externalize local domain info -R$*<$+>$* $1$2$3 defocus -R@$+:@$+:$+ @$1,@$2:$3 <route-addr> canonical - -# UUCP must always be presented in old form -R$+@$-.UUCP $2!$1 u@h.UUCP => h!u - -# delete duplicate local names -R$+%$=w@$=w $1@$w u%host@host => u@host -R$+%$=w@$=w.$D $1@$w u%host@host => u@host - - -########################### -# Name Canonicalization # -########################### -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -- note RFC733 heuristic here -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 basic RFC821/822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+ @$1:$2 change all "," to ":" - -# localize and dispose of route-based addresses -R@$+:$+ $@$>6<@$1>:$2 handle <route-addr> - -# more miscellaneous cleanup -R$+ $:$>8$1 host dependent cleanup -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style addresses to a domain-based address -R$+^$+ $1!$2 convert ^ to ! -R$-!$+ $@$>6$2<@$1.UUCP> resolve uucp names -R$+.$-!$+ $@$>6$3<@$1.$2> domain uucps -R$+!$+ $@$>6$2<@$1.UUCP> uucp subdomains -R$+%$+ $:$>9$1%$2 user%host -R$+<@$+> $@$>6$1<@$2> already canonical -R$-.$+ $@$>6$2<@$1> host.user - - -################################# -# special local conversions # -################################# - -S6 -R$*<@$=w>$* $:$1<@$w>$3 get into u@$w form -R$*<@$=w.$D>$* $:$1<@$w>$3 -R$*<@$=U.UUCP>$* $:$1<@$w>$3 - -################################ -# Change rightmost % to @. # -################################ - -S9 -R$*%$* $1@$2 First make them all @'s. -R$*@$*@$* $1%$2@$3 Undo all but the last. -R$*@$* $@$1<@$2> Put back the brackets. - - - -################### -### Mailers ### -################### - - -############################################################ -############################################################ -##### -##### Local and Program Mailer specification -##### -############################################################ -############################################################ - -Mlocal, P=/bin/mail, F=rlsDFMmn, S=10, R=20, A=mail -d $u -Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u - -S10 -R@ $n errors to mailer-daemon - - -############################################################ -############################################################ -##### -##### Local Domain SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to remain -##### the local domain. Hence, they can refer to hosts that are -##### not registered in the NIC host table. -##### -############################################################ -############################################################ - -Mtcpld, P=[IPC], F=mDFMueXLC, S=17, R=27, A=IPC $h, E=\r\n - -S17 - -# cleanup forwarding a bit -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 canonicalize -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.bitnet -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S27 - -# cleanup -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### Internet SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to leave -##### the local domain -- hence, they must be canonical according to -##### RFC822 etc. This means that machines not registered with -##### the NIC must be hidden behind our Internet relay. -##### -############################################################ -############################################################ - -Mtcp, P=[IPC], F=mDFMueXLC, S=14, R=24, A=IPC $h, E=\r\n - -S14 - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output internal ("fake") domains as "user%host@relay" - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S24 - -# put in <> kludge -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere..... -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# Hide fake domains behind relays - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - - - -##################### -### Rule Zero ### -##################### - - -############################################################ -############################################################ -##### -##### RULESET ZERO PREAMBLE -##### -##### The beginning of ruleset zero is constant through all -##### configurations. -##### -############################################################ -############################################################ - -S0 - -# first make canonical -R$*<$*>$* $1$2$3 defocus -R$+ $:$>3$1 make canonical - -# handle special cases -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 numeric internet addr -R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec -R$+ $:$>6$1 -R$-<@$w> $#local$:$1 -R@ $#error$:Invalid address handle <> form - -# canonicalize using the nameserver if not internal domain -R$*<@$*.$~I>$* $:$1<@$[$2.$3$]>$4 -R$*<@$->$* $:$1<@$[$2$]>$3 -R$*<@$->$* $:$1<@$2.$D>$3 if nameserver fails - -# now delete the local info -R<@$w>:$* $@$>0$1 @here:... -> ... -R$*<@$w> $@$>0$1 ...@here -> ... - -################################## -# End of ruleset zero preamble # -################################## - - -############################################### -### Machine dependent part of Rule Zero ### -############################################### - - - - - -# resolve fake top level domains by forwarding to other hosts -R$*<@$+.BITNET>$* $#tcp$@$B$:$1<@$2.BITNET>$3 user@host.BITNET -R$*<@$+.CSNET>$* $#tcp$@$C$:$1<@$2.CSNET>$3 user@host.CSNET - - -# forward non-local UUCP traffic to our UUCP relay -R$*<@$*.UUCP>$* $#tcpld$@$R$:$1<@$2.UUCP> uucp mail - -# hide behind our internet relay when talking to people in the arpa domain -R$*<@$*.arpa>$* $#tcp$@$2.arpa$:$1<@$2.arpa>$3 user@host.arpa - -# but speak domains to them if they speak domains too -R$*<@$*>$* $#tcpld$@$2$:$1<@$2>$3 user@host.domain - -# remaining names must be local -R$+ $#local$:$1 everything else diff --git a/usr.sbin/sendmail/cf/cf/cadgroup.mc b/usr.sbin/sendmail/cf/cf/cadgroup.mc deleted file mode 100644 index b9fa5d680d..0000000000 --- a/usr.sbin/sendmail/cf/cf/cadgroup.mc +++ /dev/null @@ -1,15 +0,0 @@ -divert(10)dnl -# -# cad group configuration file -# -# @(#)cadgroup.mc 1.7 (Berkeley) 1/3/89 -# -define(DOMAIN, `DDBerkeley.EDU') -define(INTERNET_RELAY, `DAcad.Berkeley.EDU') -define(UUCP_RELAY, `DRcad.Berkeley.EDU') -define(BITNET_RELAY, `DBjade.Berkeley.EDU') -define(CSNET_RELAY, `DCrelay.cs.net') -define(ARPAKLUDGE, `1') -define(EXTERNAL_VERSION, ``#' `@(#)cadgroup.mc 1.7' (Berkeley) `1/3/89'') -divert(0)dnl -include(proto.mc)dnl diff --git a/usr.sbin/sendmail/cf/cf/cc.cf b/usr.sbin/sendmail/cf/cf/cc.cf deleted file mode 100644 index 3a3bc3bb3d..0000000000 --- a/usr.sbin/sendmail/cf/cf/cc.cf +++ /dev/null @@ -1,547 +0,0 @@ -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)cc.mc 1.2 (Berkeley) 1/3/89 -# @(#)proto.mc 1.20 (Berkeley) 1/25/89 -# -# built by phil on Wed Jan 25 11:47:29 PST 1989 -# in /usr/src/local/nettables/sendmail.cf/cf on monet.Berkeley.EDU -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -############################################################ -############################################################ - - -################## -# local info # -################## - -# file containing our internet aliases -Fw/usr/lib/sendmail.cw - - - -############################# -### Setup Information ### -############################# - - -###################### -# General Macros # -###################### - -# local domain name -DDBerkeley.EDU - -# Internet relay host -- machines in our domain that are not -# registered with the NIC will be "hidden" behind this relay machine -# with the % kludge, although SMTP delivery will still be performed -# by the sending machine. Someday this will go away. -DAviolet.Berkeley.EDU - -# UUCP relay host -DRucbvax.Berkeley.EDU - -# csnet relay host -DCrelay.cs.net - -# bitnet relay host -DBjade.Berkeley.EDU - -# my official hostname -Dj$w - - - -############### -# Classes # -############### - -# Internal ("fake") domains that we use in rewriting -CIUUCP BITNET CSNET - - - -############################################################ -############################################################ -##### -##### BERKELEY HOSTS REGISTERED WITH THE NIC -##### -############################################################ -############################################################ - - -CNbach ucbbach -CNbizet ucbbizet -CNcad ucbcad -CNdegas ucbdegas -CNeast ucbeast -CNernie ucbernie -CNesvax ucbesvax -CNjade ucbjade -CNji ucbji -CNmike ucbmike -CNmonet ucbmonet -CNpostgres -CNrenoir ucbrenoir -CNucbarpa -CNucbvax -CNviolet ucbviolet - - -###################### -# Version Number # -###################### - -DZ1.34 - - -###################### -# Special macros # -###################### - -# my name -DnMAILER-DAEMON -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$Z ready at $b - -############### -# Options # -############### - -# location of alias file -OA/usr/lib/aliases -# wait up to ten minutes for alias file rebuild -Oa10 -# substitution for space (blank) characters -OB. -# (don't) connect to "expensive" mailers -#Oc -# default delivery mode (deliver in background) -Odbackground -# temporary file mode -OF0600 -# default GID -Og1 -# location of help file -OH/usr/lib/sendmail.hf -# log level -OL9 -# default network name -ONARPA -# default messages to old style -Oo -# queue directory -OQ/usr/spool/mqueue -# read timeout -- violates protocols -Or2h -# status file -OS/usr/lib/sendmail.st -# queue up everything before starting transmission -Os -# default timeout interval -OT3d -# time zone names (V6 only) -OtPST,PDT -# default UID -Ou1 -# wizard's password -OW* -# load average at which we just queue messages -Ox8 -# load average at which we refuse connections -OX12 - -########################### -# Message precedences # -########################### - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -##################### -# Trusted users # -##################### - -Troot -Tdaemon -Tuucp - -######################### -# Format of headers # -######################### - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$Z) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -# HPosted-Date: $a -# H?l?Received-Date: $b -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - - - -########################### -### Rewriting Rules ### -########################### - - -################################ -# Sender Field Pre-rewriting # -################################ -S1 -#R$*<$*>$* $1$2$3 defocus - -################################### -# Recipient Field Pre-rewriting # -################################### -S2 -#R$*<$*>$* $1$2$3 defocus - - - -################################# -# Final Output Post-rewriting # -################################# -S4 - -R@ $@ handle <> error addr - -# resolve numeric addresses to name if possible -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 lookup numeric internet addr - -# externalize local domain info -R$*<$+>$* $1$2$3 defocus -R@$+:@$+:$+ @$1,@$2:$3 <route-addr> canonical - -# UUCP must always be presented in old form -R$+@$-.UUCP $2!$1 u@h.UUCP => h!u - -# delete duplicate local names -R$+%$=w@$=w $1@$w u%host@host => u@host -R$+%$=w@$=w.$D $1@$w u%host@host => u@host - - -########################### -# Name Canonicalization # -########################### -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -- note RFC733 heuristic here -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 basic RFC821/822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+ @$1:$2 change all "," to ":" - -# localize and dispose of route-based addresses -R@$+:$+ $@$>6<@$1>:$2 handle <route-addr> - -# more miscellaneous cleanup -R$+ $:$>8$1 host dependent cleanup -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style addresses to a domain-based address -R$+^$+ $1!$2 convert ^ to ! -R$-!$+ $@$>6$2<@$1.UUCP> resolve uucp names -R$+.$-!$+ $@$>6$3<@$1.$2> domain uucps -R$+!$+ $@$>6$2<@$1.UUCP> uucp subdomains -R$+%$+ $:$>9$1%$2 user%host -R$+<@$+> $@$>6$1<@$2> already canonical -R$-.$+ $@$>6$2<@$1> host.user - - -################################# -# special local conversions # -################################# - -S6 -R$*<@$=w>$* $:$1<@$w>$3 get into u@$w form -R$*<@$=w.$D>$* $:$1<@$w>$3 -R$*<@$=U.UUCP>$* $:$1<@$w>$3 - -################################ -# Change rightmost % to @. # -################################ - -S9 -R$*%$* $1@$2 First make them all @'s. -R$*@$*@$* $1%$2@$3 Undo all but the last. -R$*@$* $@$1<@$2> Put back the brackets. - - - -################### -### Mailers ### -################### - - -############################################################ -############################################################ -##### -##### Local and Program Mailer specification -##### -############################################################ -############################################################ - -Mlocal, P=/bin/mail, F=rlsDFMmn, S=10, R=20, A=mail -d $u -Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u - -S10 -R@ $n errors to mailer-daemon - - -############################################################ -############################################################ -##### -##### Local Domain SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to remain -##### the local domain. Hence, they can refer to hosts that are -##### not registered in the NIC host table. -##### -############################################################ -############################################################ - -Mtcpld, P=[IPC], F=mDFMueXLC, S=17, R=27, A=IPC $h, E=\r\n - -S17 - -# cleanup forwarding a bit -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 canonicalize -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.bitnet -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S27 - -# cleanup -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### Internet SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to leave -##### the local domain -- hence, they must be canonical according to -##### RFC822 etc. This means that machines not registered with -##### the NIC must be hidden behind our Internet relay. -##### -############################################################ -############################################################ - -Mtcp, P=[IPC], F=mDFMueXLC, S=14, R=24, A=IPC $h, E=\r\n - -S14 - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output internal ("fake") domains as "user%host@relay" - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S24 - -# put in <> kludge -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere..... -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# Hide fake domains behind relays - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - - - -##################### -### Rule Zero ### -##################### - - -############################################################ -############################################################ -##### -##### RULESET ZERO PREAMBLE -##### -##### The beginning of ruleset zero is constant through all -##### configurations. -##### -############################################################ -############################################################ - -S0 - -# first make canonical -R$*<$*>$* $1$2$3 defocus -R$+ $:$>3$1 make canonical - -# handle special cases -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 numeric internet addr -R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec -R$+ $:$>6$1 -R$-<@$w> $#local$:$1 -R@ $#error$:Invalid address handle <> form - -# canonicalize using the nameserver if not internal domain -R$*<@$*.$~I>$* $:$1<@$[$2.$3$]>$4 -R$*<@$->$* $:$1<@$[$2$]>$3 -R$*<@$->$* $:$1<@$2.$D>$3 if nameserver fails - -# now delete the local info -R<@$w>:$* $@$>0$1 @here:... -> ... -R$*<@$w> $@$>0$1 ...@here -> ... - -################################## -# End of ruleset zero preamble # -################################## - - -############################################### -### Machine dependent part of Rule Zero ### -############################################### - - - - - -# resolve fake top level domains by forwarding to other hosts -R$*<@$+.BITNET>$* $#tcp$@$B$:$1<@$2.BITNET>$3 user@host.BITNET -R$*<@$+.CSNET>$* $#tcp$@$C$:$1<@$2.CSNET>$3 user@host.CSNET - - -# forward non-local UUCP traffic to our UUCP relay -R$*<@$*.UUCP>$* $#tcpld$@$R$:$1<@$2.UUCP> uucp mail - -# hide behind our internet relay when talking to people in the arpa domain -R$*<@$*.arpa>$* $#tcp$@$2.arpa$:$1<@$2.arpa>$3 user@host.arpa - -# but speak domains to them if they speak domains too -R$*<@$*>$* $#tcpld$@$2$:$1<@$2>$3 user@host.domain - -# remaining names must be local -R$+ $#local$:$1 everything else diff --git a/usr.sbin/sendmail/cf/cf/cc.mc b/usr.sbin/sendmail/cf/cf/cc.mc deleted file mode 100644 index d36e7edccb..0000000000 --- a/usr.sbin/sendmail/cf/cf/cc.mc +++ /dev/null @@ -1,15 +0,0 @@ -divert(10)dnl -# -# Computer Center configuration file -# -# @(#)cc.mc 1.2 (Berkeley) 1/3/89 -# -define(INTERNET_RELAY, `DAviolet.Berkeley.EDU') -define(DOMAIN, `DDBerkeley.EDU') -define(UUCP_RELAY, `DRucbvax.Berkeley.EDU') -define(BITNET_RELAY, `DBjade.Berkeley.EDU') -define(CSNET_RELAY, `DCrelay.cs.net') -define(ARPAKLUDGE, `1') -define(EXTERNAL_VERSION, ``#' `@(#)cc.mc 1.2' (Berkeley) `1/3/89'') -divert(0)dnl -include(proto.mc)dnl diff --git a/usr.sbin/sendmail/cf/cf/cchem.cf b/usr.sbin/sendmail/cf/cf/cchem.cf deleted file mode 100644 index 34a461ab6d..0000000000 --- a/usr.sbin/sendmail/cf/cf/cchem.cf +++ /dev/null @@ -1,547 +0,0 @@ -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)cchem.mc 1.2 (Berkeley) 1/3/89 -# @(#)proto.mc 1.20 (Berkeley) 1/25/89 -# -# built by phil on Wed Jan 25 11:47:38 PST 1989 -# in /usr/src/local/nettables/sendmail.cf/cf on monet.Berkeley.EDU -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -############################################################ -############################################################ - - -################## -# local info # -################## - -# file containing our internet aliases -Fw/usr/lib/sendmail.cw - - - -############################# -### Setup Information ### -############################# - - -###################### -# General Macros # -###################### - -# local domain name -DDCChem.Berkeley.EDU - -# Internet relay host -- machines in our domain that are not -# registered with the NIC will be "hidden" behind this relay machine -# with the % kludge, although SMTP delivery will still be performed -# by the sending machine. Someday this will go away. -DAviolet.Berkeley.EDU - -# UUCP relay host -DRucbvax.Berkeley.EDU - -# csnet relay host -DCrelay.cs.net - -# bitnet relay host -DBjade.Berkeley.EDU - -# my official hostname -Dj$w - - - -############### -# Classes # -############### - -# Internal ("fake") domains that we use in rewriting -CIUUCP BITNET CSNET - - - -############################################################ -############################################################ -##### -##### BERKELEY HOSTS REGISTERED WITH THE NIC -##### -############################################################ -############################################################ - - -CNbach ucbbach -CNbizet ucbbizet -CNcad ucbcad -CNdegas ucbdegas -CNeast ucbeast -CNernie ucbernie -CNesvax ucbesvax -CNjade ucbjade -CNji ucbji -CNmike ucbmike -CNmonet ucbmonet -CNpostgres -CNrenoir ucbrenoir -CNucbarpa -CNucbvax -CNviolet ucbviolet - - -###################### -# Version Number # -###################### - -DZ1.34 - - -###################### -# Special macros # -###################### - -# my name -DnMAILER-DAEMON -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$Z ready at $b - -############### -# Options # -############### - -# location of alias file -OA/usr/lib/aliases -# wait up to ten minutes for alias file rebuild -Oa10 -# substitution for space (blank) characters -OB. -# (don't) connect to "expensive" mailers -#Oc -# default delivery mode (deliver in background) -Odbackground -# temporary file mode -OF0600 -# default GID -Og1 -# location of help file -OH/usr/lib/sendmail.hf -# log level -OL9 -# default network name -ONARPA -# default messages to old style -Oo -# queue directory -OQ/usr/spool/mqueue -# read timeout -- violates protocols -Or2h -# status file -OS/usr/lib/sendmail.st -# queue up everything before starting transmission -Os -# default timeout interval -OT3d -# time zone names (V6 only) -OtPST,PDT -# default UID -Ou1 -# wizard's password -OW* -# load average at which we just queue messages -Ox8 -# load average at which we refuse connections -OX12 - -########################### -# Message precedences # -########################### - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -##################### -# Trusted users # -##################### - -Troot -Tdaemon -Tuucp - -######################### -# Format of headers # -######################### - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$Z) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -# HPosted-Date: $a -# H?l?Received-Date: $b -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - - - -########################### -### Rewriting Rules ### -########################### - - -################################ -# Sender Field Pre-rewriting # -################################ -S1 -#R$*<$*>$* $1$2$3 defocus - -################################### -# Recipient Field Pre-rewriting # -################################### -S2 -#R$*<$*>$* $1$2$3 defocus - - - -################################# -# Final Output Post-rewriting # -################################# -S4 - -R@ $@ handle <> error addr - -# resolve numeric addresses to name if possible -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 lookup numeric internet addr - -# externalize local domain info -R$*<$+>$* $1$2$3 defocus -R@$+:@$+:$+ @$1,@$2:$3 <route-addr> canonical - -# UUCP must always be presented in old form -R$+@$-.UUCP $2!$1 u@h.UUCP => h!u - -# delete duplicate local names -R$+%$=w@$=w $1@$w u%host@host => u@host -R$+%$=w@$=w.$D $1@$w u%host@host => u@host - - -########################### -# Name Canonicalization # -########################### -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -- note RFC733 heuristic here -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 basic RFC821/822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+ @$1:$2 change all "," to ":" - -# localize and dispose of route-based addresses -R@$+:$+ $@$>6<@$1>:$2 handle <route-addr> - -# more miscellaneous cleanup -R$+ $:$>8$1 host dependent cleanup -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style addresses to a domain-based address -R$+^$+ $1!$2 convert ^ to ! -R$-!$+ $@$>6$2<@$1.UUCP> resolve uucp names -R$+.$-!$+ $@$>6$3<@$1.$2> domain uucps -R$+!$+ $@$>6$2<@$1.UUCP> uucp subdomains -R$+%$+ $:$>9$1%$2 user%host -R$+<@$+> $@$>6$1<@$2> already canonical -R$-.$+ $@$>6$2<@$1> host.user - - -################################# -# special local conversions # -################################# - -S6 -R$*<@$=w>$* $:$1<@$w>$3 get into u@$w form -R$*<@$=w.$D>$* $:$1<@$w>$3 -R$*<@$=U.UUCP>$* $:$1<@$w>$3 - -################################ -# Change rightmost % to @. # -################################ - -S9 -R$*%$* $1@$2 First make them all @'s. -R$*@$*@$* $1%$2@$3 Undo all but the last. -R$*@$* $@$1<@$2> Put back the brackets. - - - -################### -### Mailers ### -################### - - -############################################################ -############################################################ -##### -##### Local and Program Mailer specification -##### -############################################################ -############################################################ - -Mlocal, P=/bin/mail, F=rlsDFMmn, S=10, R=20, A=mail -d $u -Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u - -S10 -R@ $n errors to mailer-daemon - - -############################################################ -############################################################ -##### -##### Local Domain SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to remain -##### the local domain. Hence, they can refer to hosts that are -##### not registered in the NIC host table. -##### -############################################################ -############################################################ - -Mtcpld, P=[IPC], F=mDFMueXLC, S=17, R=27, A=IPC $h, E=\r\n - -S17 - -# cleanup forwarding a bit -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 canonicalize -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.bitnet -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S27 - -# cleanup -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### Internet SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to leave -##### the local domain -- hence, they must be canonical according to -##### RFC822 etc. This means that machines not registered with -##### the NIC must be hidden behind our Internet relay. -##### -############################################################ -############################################################ - -Mtcp, P=[IPC], F=mDFMueXLC, S=14, R=24, A=IPC $h, E=\r\n - -S14 - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output internal ("fake") domains as "user%host@relay" - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S24 - -# put in <> kludge -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere..... -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# Hide fake domains behind relays - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - - - -##################### -### Rule Zero ### -##################### - - -############################################################ -############################################################ -##### -##### RULESET ZERO PREAMBLE -##### -##### The beginning of ruleset zero is constant through all -##### configurations. -##### -############################################################ -############################################################ - -S0 - -# first make canonical -R$*<$*>$* $1$2$3 defocus -R$+ $:$>3$1 make canonical - -# handle special cases -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 numeric internet addr -R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec -R$+ $:$>6$1 -R$-<@$w> $#local$:$1 -R@ $#error$:Invalid address handle <> form - -# canonicalize using the nameserver if not internal domain -R$*<@$*.$~I>$* $:$1<@$[$2.$3$]>$4 -R$*<@$->$* $:$1<@$[$2$]>$3 -R$*<@$->$* $:$1<@$2.$D>$3 if nameserver fails - -# now delete the local info -R<@$w>:$* $@$>0$1 @here:... -> ... -R$*<@$w> $@$>0$1 ...@here -> ... - -################################## -# End of ruleset zero preamble # -################################## - - -############################################### -### Machine dependent part of Rule Zero ### -############################################### - - - - - -# resolve fake top level domains by forwarding to other hosts -R$*<@$+.BITNET>$* $#tcp$@$B$:$1<@$2.BITNET>$3 user@host.BITNET -R$*<@$+.CSNET>$* $#tcp$@$C$:$1<@$2.CSNET>$3 user@host.CSNET - - -# forward non-local UUCP traffic to our UUCP relay -R$*<@$*.UUCP>$* $#tcpld$@$R$:$1<@$2.UUCP> uucp mail - -# hide behind our internet relay when talking to people in the arpa domain -R$*<@$*.arpa>$* $#tcp$@$2.arpa$:$1<@$2.arpa>$3 user@host.arpa - -# but speak domains to them if they speak domains too -R$*<@$*>$* $#tcpld$@$2$:$1<@$2>$3 user@host.domain - -# remaining names must be local -R$+ $#local$:$1 everything else diff --git a/usr.sbin/sendmail/cf/cf/cchem.mc b/usr.sbin/sendmail/cf/cf/cchem.mc deleted file mode 100644 index 28c7300bc6..0000000000 --- a/usr.sbin/sendmail/cf/cf/cchem.mc +++ /dev/null @@ -1,15 +0,0 @@ -divert(10)dnl -# -# College of Chemistry configuration file -# -# @(#)cchem.mc 1.2 (Berkeley) 1/3/89 -# -define(DOMAIN, `DDCChem.Berkeley.EDU') -define(INTERNET_RELAY, `DAviolet.Berkeley.EDU') -define(UUCP_RELAY, `DRucbvax.Berkeley.EDU') -define(BITNET_RELAY, `DBjade.Berkeley.EDU') -define(CSNET_RELAY, `DCrelay.cs.net') -define(ARPAKLUDGE, `1') -define(EXTERNAL_VERSION, ``#' `@(#)cchem.mc 1.2' (Berkeley) `1/3/89'') -divert(0)dnl -include(proto.mc)dnl diff --git a/usr.sbin/sendmail/cf/cf/chez.mc b/usr.sbin/sendmail/cf/cf/chez.mc new file mode 100644 index 0000000000..13f951977f --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/chez.mc @@ -0,0 +1,44 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)chez.mc 8.1 (Berkeley) 6/7/93') +OSTYPE(bsd4.4)dnl +DOMAIN(cs.exposed)dnl +define(`LOCAL_RELAY', vangogh.CS.Berkeley.EDU)dnl +define(`MASQUERADE_NAME', vangogh.CS.Berkeley.EDU)dnl +MAILER(local)dnl +MAILER(smtp)dnl +Fw/etc/sendmail.cw diff --git a/usr.sbin/sendmail/cf/cf/cogsci.cf b/usr.sbin/sendmail/cf/cf/cogsci.cf deleted file mode 100644 index 45b6808725..0000000000 --- a/usr.sbin/sendmail/cf/cf/cogsci.cf +++ /dev/null @@ -1,610 +0,0 @@ -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)cogsci.mc 1.6 (Berkeley) 1/3/89 -# @(#)proto.mc 1.20 (Berkeley) 1/25/89 -# -# built by phil on Wed Jan 25 11:46:54 PST 1989 -# in /usr/src/local/nettables/sendmail.cf/cf on monet.Berkeley.EDU -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -############################################################ -############################################################ - - -################## -# local info # -################## - -# file containing our internet aliases -Fw/usr/lib/sendmail.cw - -# uucp hostnames -DUcogsci -CUcogsci - -# local UUCP connections -CV capmkt -CV contessa -CV emind -CV hoptoad -CV nkainc -CV well -CV ferdy - - -############################# -### Setup Information ### -############################# - - -###################### -# General Macros # -###################### - -# local domain name -DDBerkeley.EDU - -# Internet relay host -- machines in our domain that are not -# registered with the NIC will be "hidden" behind this relay machine -# with the % kludge, although SMTP delivery will still be performed -# by the sending machine. Someday this will go away. -DAucbvax.Berkeley.EDU - -# UUCP relay host -DRucbvax.Berkeley.EDU - -# csnet relay host -DCrelay.cs.net - -# bitnet relay host -DBjade.Berkeley.EDU - -# my official hostname -Dj$w - - - -############### -# Classes # -############### - -# Internal ("fake") domains that we use in rewriting -CIUUCP BITNET CSNET - - - -############################################################ -############################################################ -##### -##### BERKELEY HOSTS REGISTERED WITH THE NIC -##### -############################################################ -############################################################ - - -CNbach ucbbach -CNbizet ucbbizet -CNcad ucbcad -CNdegas ucbdegas -CNeast ucbeast -CNernie ucbernie -CNesvax ucbesvax -CNjade ucbjade -CNji ucbji -CNmike ucbmike -CNmonet ucbmonet -CNpostgres -CNrenoir ucbrenoir -CNucbarpa -CNucbvax -CNviolet ucbviolet - - -###################### -# Version Number # -###################### - -DZ1.34 - - -###################### -# Special macros # -###################### - -# my name -DnMAILER-DAEMON -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$Z ready at $b - -############### -# Options # -############### - -# location of alias file -OA/usr/lib/aliases -# wait up to ten minutes for alias file rebuild -Oa10 -# substitution for space (blank) characters -OB. -# (don't) connect to "expensive" mailers -#Oc -# default delivery mode (deliver in background) -Odbackground -# temporary file mode -OF0600 -# default GID -Og1 -# location of help file -OH/usr/lib/sendmail.hf -# log level -OL9 -# default network name -ONARPA -# default messages to old style -Oo -# queue directory -OQ/usr/spool/mqueue -# read timeout -- violates protocols -Or2h -# status file -OS/usr/lib/sendmail.st -# queue up everything before starting transmission -Os -# default timeout interval -OT3d -# time zone names (V6 only) -OtPST,PDT -# default UID -Ou1 -# wizard's password -OW* -# load average at which we just queue messages -Ox8 -# load average at which we refuse connections -OX12 - -########################### -# Message precedences # -########################### - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -##################### -# Trusted users # -##################### - -Troot -Tdaemon -Tuucp - -######################### -# Format of headers # -######################### - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$Z) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -# HPosted-Date: $a -# H?l?Received-Date: $b -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - - - -########################### -### Rewriting Rules ### -########################### - - -################################ -# Sender Field Pre-rewriting # -################################ -S1 -#R$*<$*>$* $1$2$3 defocus - -################################### -# Recipient Field Pre-rewriting # -################################### -S2 -#R$*<$*>$* $1$2$3 defocus - - - -################################# -# Final Output Post-rewriting # -################################# -S4 - -R@ $@ handle <> error addr - -# resolve numeric addresses to name if possible -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 lookup numeric internet addr - -# externalize local domain info -R$*<$+>$* $1$2$3 defocus -R@$+:@$+:$+ @$1,@$2:$3 <route-addr> canonical - -# UUCP must always be presented in old form -R$+@$-.UUCP $2!$1 u@h.UUCP => h!u - -# delete duplicate local names -R$+%$=w@$=w $1@$w u%host@host => u@host -R$+%$=w@$=w.$D $1@$w u%host@host => u@host - - -########################### -# Name Canonicalization # -########################### -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -- note RFC733 heuristic here -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 basic RFC821/822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+ @$1:$2 change all "," to ":" - -# localize and dispose of route-based addresses -R@$+:$+ $@$>6<@$1>:$2 handle <route-addr> - -# more miscellaneous cleanup -R$+ $:$>8$1 host dependent cleanup -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style addresses to a domain-based address -R$+^$+ $1!$2 convert ^ to ! -R$-!$+ $@$>6$2<@$1.UUCP> resolve uucp names -R$+.$-!$+ $@$>6$3<@$1.$2> domain uucps -R$+!$+ $@$>6$2<@$1.UUCP> uucp subdomains -R$+%$+ $:$>9$1%$2 user%host -R$+<@$+> $@$>6$1<@$2> already canonical -R$-.$+ $@$>6$2<@$1> host.user - - -################################# -# special local conversions # -################################# - -S6 -R$*<@$=w>$* $:$1<@$w>$3 get into u@$w form -R$*<@$=w.$D>$* $:$1<@$w>$3 -R$*<@$=U.UUCP>$* $:$1<@$w>$3 - -################################ -# Change rightmost % to @. # -################################ - -S9 -R$*%$* $1@$2 First make them all @'s. -R$*@$*@$* $1%$2@$3 Undo all but the last. -R$*@$* $@$1<@$2> Put back the brackets. - - - -################### -### Mailers ### -################### - - -############################################################ -############################################################ -##### -##### Local and Program Mailer specification -##### -############################################################ -############################################################ - -Mlocal, P=/bin/mail, F=rlsDFMmn, S=10, R=20, A=mail -d $u -Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u - -S10 -R@ $n errors to mailer-daemon - - -############################################################ -############################################################ -##### -##### Local Domain SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to remain -##### the local domain. Hence, they can refer to hosts that are -##### not registered in the NIC host table. -##### -############################################################ -############################################################ - -Mtcpld, P=[IPC], F=mDFMueXLC, S=17, R=27, A=IPC $h, E=\r\n - -S17 - -# cleanup forwarding a bit -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 canonicalize -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.bitnet -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S27 - -# cleanup -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### Internet SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to leave -##### the local domain -- hence, they must be canonical according to -##### RFC822 etc. This means that machines not registered with -##### the NIC must be hidden behind our Internet relay. -##### -############################################################ -############################################################ - -Mtcp, P=[IPC], F=mDFMueXLC, S=14, R=24, A=IPC $h, E=\r\n - -S14 - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output internal ("fake") domains as "user%host@relay" - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S24 - -# put in <> kludge -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere..... -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# Hide fake domains behind relays - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### UUCP Mailer specification -##### -############################################################ -############################################################ - - -Muucp, P=/usr/bin/uux, F=DFMhuU, S=13, R=23, M=100000, - A=uux - -r -z -a$f -gC $h!rmail ($u) - -S13 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$+> $2!$1 uucpize (no @'s in addr) -R$w!$+ $1 strip local name -R$+ $:$U!$1 stick on our host name -R$=U!$-%$- $:$1!$2@$3.$D ucbvax!user@host.domain - -S23 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$w> $U!$1 a!b@here -> here!a!b -R$=U!$+ $2 here!a!b -> a!b -# sanity ... should not happen. -R$=U.$D!$+ $2 strip local name.domain - - -############################################################ -############################################################ -##### -##### Provide Backward Compatibility -##### -############################################################ -############################################################ - -##################################################### -# General code to convert back to old style names # -##################################################### -S5 - -R$+<@$w> $1 strip host -R$+<@$-.UUCP> $2!$1 u@host.UUCP => host!u - - - -##################### -### Rule Zero ### -##################### - - -############################################################ -############################################################ -##### -##### RULESET ZERO PREAMBLE -##### -##### The beginning of ruleset zero is constant through all -##### configurations. -##### -############################################################ -############################################################ - -S0 - -# first make canonical -R$*<$*>$* $1$2$3 defocus -R$+ $:$>3$1 make canonical - -# handle special cases -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 numeric internet addr -R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec -R$+ $:$>6$1 -R$-<@$w> $#local$:$1 -R@ $#error$:Invalid address handle <> form - -# canonicalize using the nameserver if not internal domain -R$*<@$*.$~I>$* $:$1<@$[$2.$3$]>$4 -R$*<@$->$* $:$1<@$[$2$]>$3 -R$*<@$->$* $:$1<@$2.$D>$3 if nameserver fails - -# now delete the local info -R<@$w>:$* $@$>0$1 @here:... -> ... -R$*<@$w> $@$>0$1 ...@here -> ... - -################################## -# End of ruleset zero preamble # -################################## - - -############################################### -### Machine dependent part of Rule Zero ### -############################################### - - - -# resolve local UUCP connections -R<@$=V.UUCP>:$+ $#uucp$@$1$:$2 @host.UUCP:... -R$+<@$=V.UUCP> $#uucp$@$2$:$1 user@host.UUCP - - -# resolve fake top level domains by forwarding to other hosts -R$*<@$+.BITNET>$* $#tcp$@$B$:$1<@$2.BITNET>$3 user@host.BITNET -R$*<@$+.CSNET>$* $#tcp$@$C$:$1<@$2.CSNET>$3 user@host.CSNET - - -# forward non-local UUCP traffic to our UUCP relay -R$*<@$*.UUCP>$* $#tcpld$@$R$:$1<@$2.UUCP> uucp mail - -# hide behind our internet relay when talking to people in the arpa domain -R$*<@$*.arpa>$* $#tcp$@$2.arpa$:$1<@$2.arpa>$3 user@host.arpa - -# but speak domains to them if they speak domains too -R$*<@$*>$* $#tcpld$@$2$:$1<@$2>$3 user@host.domain - -# remaining names must be local -R$+ $#local$:$1 everything else - diff --git a/usr.sbin/sendmail/cf/cf/cogsci.mc b/usr.sbin/sendmail/cf/cf/cogsci.mc index 62a2841abc..4faa46d822 100644 --- a/usr.sbin/sendmail/cf/cf/cogsci.mc +++ b/usr.sbin/sendmail/cf/cf/cogsci.mc @@ -1,18 +1,41 @@ -divert(10)dnl +divert(-1) # -# cogsci configuration file +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. # -# @(#)cogsci.mc 1.6 (Berkeley) 1/3/89 +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. # -define(DOMAIN, `DDBerkeley.EDU') -define(UUCP_NAME, DUcogsci) -define(UUCP_ALIASES, CUcogsci) -define(UUCP_HOSTS_FILE, ../sitedep/uucp.cogsci.m4) -define(INTERNET_RELAY, `DAucbvax.Berkeley.EDU') -define(UUCP_RELAY, DRucbvax.Berkeley.EDU) -define(BITNET_RELAY, `DBjade.Berkeley.EDU') -define(CSNET_RELAY, `DCrelay.cs.net') -define(ARPAKLUDGE, `1') -define(EXTERNAL_VERSION, ``#' `@(#)cogsci.mc 1.6' (Berkeley) `1/3/89'') -divert(0)dnl -include(proto.mc) +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)cogsci.mc 8.1 (Berkeley) 6/7/93') +DOMAIN(cs.exposed)dnl +MAILER(smtp)dnl +MAILER(uucp)dnl +SITECONFIG(uucp.cogsci, Ucogsci, U) diff --git a/usr.sbin/sendmail/cf/cf/cs-exposed.mc b/usr.sbin/sendmail/cf/cf/cs-exposed.mc new file mode 100644 index 0000000000..62072b7e14 --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/cs-exposed.mc @@ -0,0 +1,40 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)cs-exposed.mc 8.1 (Berkeley) 6/7/93') +DOMAIN(cs.exposed)dnl +MAILER(local)dnl +MAILER(smtp)dnl diff --git a/usr.sbin/sendmail/cf/cf/cs-hidden.mc b/usr.sbin/sendmail/cf/cf/cs-hidden.mc new file mode 100644 index 0000000000..216062ce9e --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/cs-hidden.mc @@ -0,0 +1,40 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)cs-hidden.mc 8.1 (Berkeley) 6/7/93') +DOMAIN(cs.hidden)dnl +MAILER(local)dnl +MAILER(smtp)dnl diff --git a/usr.sbin/sendmail/cf/cf/freefall.mc b/usr.sbin/sendmail/cf/cf/freefall.mc new file mode 100644 index 0000000000..a27e9f86c4 --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/freefall.mc @@ -0,0 +1,47 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988 The Regents of the University of California. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)freefall.mc $Revision: 1.1 $') +OSTYPE(bsd4.4)dnl +MAILER(local)dnl +MAILER(smtp)dnl +define(`UUCP_RELAY', ucbvax.Berkeley.EDU)dnl +define(`BITNET_RELAY', mailhost.Berkeley.EDU)dnl +define(`CSNET_RELAY', mailhost.Berkeley.EDU)dnl +define(`confCHECKPOINT_INTERVAL', 4)dnl +define(`confAUTO_REBUILD', True)dnl + + diff --git a/usr.sbin/sendmail/cf/cf/hpux-cs-exposed.mc b/usr.sbin/sendmail/cf/cf/hpux-cs-exposed.mc new file mode 100644 index 0000000000..4f61ffda45 --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/hpux-cs-exposed.mc @@ -0,0 +1,41 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)hpux-cs-exposed.mc 8.1 (Berkeley) 6/7/93') +OSTYPE(hpux)dnl +DOMAIN(cs.exposed)dnl +MAILER(local)dnl +MAILER(smtp)dnl diff --git a/usr.sbin/sendmail/cf/cf/hpux-cs-hidden.mc b/usr.sbin/sendmail/cf/cf/hpux-cs-hidden.mc new file mode 100644 index 0000000000..33cf580d39 --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/hpux-cs-hidden.mc @@ -0,0 +1,41 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)hpux-cs-hidden.mc 8.1 (Berkeley) 6/7/93') +OSTYPE(hpux)dnl +DOMAIN(cs.hidden)dnl +MAILER(local)dnl +MAILER(smtp)dnl diff --git a/usr.sbin/sendmail/cf/cf/ic.cf b/usr.sbin/sendmail/cf/cf/ic.cf deleted file mode 100644 index c9f5e44abb..0000000000 --- a/usr.sbin/sendmail/cf/cf/ic.cf +++ /dev/null @@ -1,603 +0,0 @@ -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)ic.mc 1.2 (Berkeley) 1/3/89 -# @(#)proto.mc 1.20 (Berkeley) 1/25/89 -# -# built by phil on Wed Jan 25 11:47:46 PST 1989 -# in /usr/src/local/nettables/sendmail.cf/cf on monet.Berkeley.EDU -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -############################################################ -############################################################ - - -################## -# local info # -################## - -# file containing our internet aliases -Fw/usr/lib/sendmail.cw - -# uucp hostnames -DUic -CUic ucbic - -# local UUCP connections -CV foo - - -############################# -### Setup Information ### -############################# - - -###################### -# General Macros # -###################### - -# local domain name -DDBerkeley.EDU - -# Internet relay host -- machines in our domain that are not -# registered with the NIC will be "hidden" behind this relay machine -# with the % kludge, although SMTP delivery will still be performed -# by the sending machine. Someday this will go away. -DAcad.Berkeley.EDU - -# UUCP relay host -DRcad.Berkeley.EDU - -# csnet relay host -DCrelay.cs.net - -# bitnet relay host -DBjade.Berkeley.EDU - -# my official hostname -Dj$w - - - -############### -# Classes # -############### - -# Internal ("fake") domains that we use in rewriting -CIUUCP BITNET CSNET - - - -############################################################ -############################################################ -##### -##### BERKELEY HOSTS REGISTERED WITH THE NIC -##### -############################################################ -############################################################ - - -CNbach ucbbach -CNbizet ucbbizet -CNcad ucbcad -CNdegas ucbdegas -CNeast ucbeast -CNernie ucbernie -CNesvax ucbesvax -CNjade ucbjade -CNji ucbji -CNmike ucbmike -CNmonet ucbmonet -CNpostgres -CNrenoir ucbrenoir -CNucbarpa -CNucbvax -CNviolet ucbviolet - - -###################### -# Version Number # -###################### - -DZ1.34 - - -###################### -# Special macros # -###################### - -# my name -DnMAILER-DAEMON -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$Z ready at $b - -############### -# Options # -############### - -# location of alias file -OA/usr/lib/aliases -# wait up to ten minutes for alias file rebuild -Oa10 -# substitution for space (blank) characters -OB. -# (don't) connect to "expensive" mailers -#Oc -# default delivery mode (deliver in background) -Odbackground -# temporary file mode -OF0600 -# default GID -Og1 -# location of help file -OH/usr/lib/sendmail.hf -# log level -OL9 -# default network name -ONARPA -# default messages to old style -Oo -# queue directory -OQ/usr/spool/mqueue -# read timeout -- violates protocols -Or2h -# status file -OS/usr/lib/sendmail.st -# queue up everything before starting transmission -Os -# default timeout interval -OT3d -# time zone names (V6 only) -OtPST,PDT -# default UID -Ou1 -# wizard's password -OW* -# load average at which we just queue messages -Ox8 -# load average at which we refuse connections -OX12 - -########################### -# Message precedences # -########################### - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -##################### -# Trusted users # -##################### - -Troot -Tdaemon -Tuucp - -######################### -# Format of headers # -######################### - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$Z) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -# HPosted-Date: $a -# H?l?Received-Date: $b -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - - - -########################### -### Rewriting Rules ### -########################### - - -################################ -# Sender Field Pre-rewriting # -################################ -S1 -#R$*<$*>$* $1$2$3 defocus - -################################### -# Recipient Field Pre-rewriting # -################################### -S2 -#R$*<$*>$* $1$2$3 defocus - - - -################################# -# Final Output Post-rewriting # -################################# -S4 - -R@ $@ handle <> error addr - -# resolve numeric addresses to name if possible -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 lookup numeric internet addr - -# externalize local domain info -R$*<$+>$* $1$2$3 defocus -R@$+:@$+:$+ @$1,@$2:$3 <route-addr> canonical - -# UUCP must always be presented in old form -R$+@$-.UUCP $2!$1 u@h.UUCP => h!u - -# delete duplicate local names -R$+%$=w@$=w $1@$w u%host@host => u@host -R$+%$=w@$=w.$D $1@$w u%host@host => u@host - - -########################### -# Name Canonicalization # -########################### -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -- note RFC733 heuristic here -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 basic RFC821/822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+ @$1:$2 change all "," to ":" - -# localize and dispose of route-based addresses -R@$+:$+ $@$>6<@$1>:$2 handle <route-addr> - -# more miscellaneous cleanup -R$+ $:$>8$1 host dependent cleanup -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style addresses to a domain-based address -R$+^$+ $1!$2 convert ^ to ! -R$-!$+ $@$>6$2<@$1.UUCP> resolve uucp names -R$+.$-!$+ $@$>6$3<@$1.$2> domain uucps -R$+!$+ $@$>6$2<@$1.UUCP> uucp subdomains -R$+%$+ $:$>9$1%$2 user%host -R$+<@$+> $@$>6$1<@$2> already canonical -R$-.$+ $@$>6$2<@$1> host.user - - -################################# -# special local conversions # -################################# - -S6 -R$*<@$=w>$* $:$1<@$w>$3 get into u@$w form -R$*<@$=w.$D>$* $:$1<@$w>$3 -R$*<@$=U.UUCP>$* $:$1<@$w>$3 - -################################ -# Change rightmost % to @. # -################################ - -S9 -R$*%$* $1@$2 First make them all @'s. -R$*@$*@$* $1%$2@$3 Undo all but the last. -R$*@$* $@$1<@$2> Put back the brackets. - - - -################### -### Mailers ### -################### - - -############################################################ -############################################################ -##### -##### Local and Program Mailer specification -##### -############################################################ -############################################################ - -Mlocal, P=/bin/mail, F=rlsDFMmn, S=10, R=20, A=mail -d $u -Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u - -S10 -R@ $n errors to mailer-daemon - - -############################################################ -############################################################ -##### -##### Local Domain SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to remain -##### the local domain. Hence, they can refer to hosts that are -##### not registered in the NIC host table. -##### -############################################################ -############################################################ - -Mtcpld, P=[IPC], F=mDFMueXLC, S=17, R=27, A=IPC $h, E=\r\n - -S17 - -# cleanup forwarding a bit -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 canonicalize -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.bitnet -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S27 - -# cleanup -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### Internet SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to leave -##### the local domain -- hence, they must be canonical according to -##### RFC822 etc. This means that machines not registered with -##### the NIC must be hidden behind our Internet relay. -##### -############################################################ -############################################################ - -Mtcp, P=[IPC], F=mDFMueXLC, S=14, R=24, A=IPC $h, E=\r\n - -S14 - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output internal ("fake") domains as "user%host@relay" - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S24 - -# put in <> kludge -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere..... -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# Hide fake domains behind relays - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### UUCP Mailer specification -##### -############################################################ -############################################################ - - -Muucp, P=/usr/bin/uux, F=DFMhuU, S=13, R=23, M=100000, - A=uux - -r -z -a$f -gC $h!rmail ($u) - -S13 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$+> $2!$1 uucpize (no @'s in addr) -R$w!$+ $1 strip local name -R$+ $:$U!$1 stick on our host name -R$=U!$-%$- $:$1!$2@$3.$D ucbvax!user@host.domain - -S23 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$w> $U!$1 a!b@here -> here!a!b -R$=U!$+ $2 here!a!b -> a!b -# sanity ... should not happen. -R$=U.$D!$+ $2 strip local name.domain - - -############################################################ -############################################################ -##### -##### Provide Backward Compatibility -##### -############################################################ -############################################################ - -##################################################### -# General code to convert back to old style names # -##################################################### -S5 - -R$+<@$w> $1 strip host -R$+<@$-.UUCP> $2!$1 u@host.UUCP => host!u - - - -##################### -### Rule Zero ### -##################### - - -############################################################ -############################################################ -##### -##### RULESET ZERO PREAMBLE -##### -##### The beginning of ruleset zero is constant through all -##### configurations. -##### -############################################################ -############################################################ - -S0 - -# first make canonical -R$*<$*>$* $1$2$3 defocus -R$+ $:$>3$1 make canonical - -# handle special cases -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 numeric internet addr -R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec -R$+ $:$>6$1 -R$-<@$w> $#local$:$1 -R@ $#error$:Invalid address handle <> form - -# canonicalize using the nameserver if not internal domain -R$*<@$*.$~I>$* $:$1<@$[$2.$3$]>$4 -R$*<@$->$* $:$1<@$[$2$]>$3 -R$*<@$->$* $:$1<@$2.$D>$3 if nameserver fails - -# now delete the local info -R<@$w>:$* $@$>0$1 @here:... -> ... -R$*<@$w> $@$>0$1 ...@here -> ... - -################################## -# End of ruleset zero preamble # -################################## - - -############################################### -### Machine dependent part of Rule Zero ### -############################################### - - - -# resolve local UUCP connections -R<@$=V.UUCP>:$+ $#uucp$@$1$:$2 @host.UUCP:... -R$+<@$=V.UUCP> $#uucp$@$2$:$1 user@host.UUCP - - -# resolve fake top level domains by forwarding to other hosts -R$*<@$+.BITNET>$* $#tcp$@$B$:$1<@$2.BITNET>$3 user@host.BITNET -R$*<@$+.CSNET>$* $#tcp$@$C$:$1<@$2.CSNET>$3 user@host.CSNET - - -# forward non-local UUCP traffic to our UUCP relay -R$*<@$*.UUCP>$* $#tcpld$@$R$:$1<@$2.UUCP> uucp mail - -# hide behind our internet relay when talking to people in the arpa domain -R$*<@$*.arpa>$* $#tcp$@$2.arpa$:$1<@$2.arpa>$3 user@host.arpa - -# but speak domains to them if they speak domains too -R$*<@$*>$* $#tcpld$@$2$:$1<@$2>$3 user@host.domain - -# remaining names must be local -R$+ $#local$:$1 everything else diff --git a/usr.sbin/sendmail/cf/cf/ic.mc b/usr.sbin/sendmail/cf/cf/ic.mc deleted file mode 100644 index e45fe9211e..0000000000 --- a/usr.sbin/sendmail/cf/cf/ic.mc +++ /dev/null @@ -1,18 +0,0 @@ -divert(10)dnl -# -# ic configuration file -# -# @(#)ic.mc 1.2 (Berkeley) 1/3/89 -# -define(DOMAIN, `DDBerkeley.EDU') -define(UUCP_NAME, DUic) -define(UUCP_ALIASES, CUic ucbic) -define(UUCP_HOSTS_FILE, ../sitedep/uucp.ic.m4) -define(INTERNET_RELAY, `DAcad.Berkeley.EDU') -define(UUCP_RELAY, `DRcad.Berkeley.EDU') -define(BITNET_RELAY, `DBjade.Berkeley.EDU') -define(CSNET_RELAY, `DCrelay.cs.net') -define(ARPAKLUDGE, `1') -define(EXTERNAL_VERSION, ``#' `@(#)ic.mc 1.2' (Berkeley) `1/3/89'') -divert(0)dnl -include(proto.mc)dnl diff --git a/usr.sbin/sendmail/cf/cf/id.cf b/usr.sbin/sendmail/cf/cf/id.cf deleted file mode 100644 index 7aa504c6db..0000000000 --- a/usr.sbin/sendmail/cf/cf/id.cf +++ /dev/null @@ -1,615 +0,0 @@ -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# based on: @(#)ic.mc 1.2 (Berkeley) 1/3/89 -# @(#)proto.mc 1.20 (Berkeley) 1/25/89 -# -# built by rich on Mon May 11 19:31:56 CDT 1992 -# in /home/rich/sendmail/idcf/cf on id -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -############################################################ -############################################################ - - -################## -# local info # -################## - -# file containing our internet aliases -Fw/etc/sendmail.cw - -# uucp hostnames -DUid -CUid uid - -# local UUCP connections -CVjpunix - - -############################# -### Setup Information ### -############################# - - -###################### -# General Macros # -###################### - -# local domain name -DDjpunix.hou.tx.us - -# Internet relay host -- machines in our domain that are not -# registered with the NIC will be "hidden" behind this relay machine -# with the % kludge, although SMTP delivery will still be performed -# by the sending machine. Someday this will go away. -DAjpunix.hou.tx.us - -# UUCP relay host -DRjpunix - -# csnet relay host -DCrelay.cs.net - -# bitnet relay host -DBricevm1.Rice.EDU - -# my official hostname -Dj$w - - - -############### -# Classes # -############### - -# Internal ("fake") domains that we use in rewriting -CIUUCP BITNET CSNET - - - -############################################################ -############################################################ -##### -##### BERKELEY HOSTS REGISTERED WITH THE NIC -##### -############################################################ -############################################################ - - -CNbach ucbbach -CNbizet ucbbizet -CNcad ucbcad -CNdegas ucbdegas -CNeast ucbeast -CNernie ucbernie -CNesvax ucbesvax -CNjade ucbjade -CNji ucbji -CNmike ucbmike -CNmonet ucbmonet -CNpostgres -CNrenoir ucbrenoir -CNucbarpa -CNucbvax -CNviolet ucbviolet - - -###################### -# Version Number # -###################### - -DZ1.34 - - -###################### -# Special macros # -###################### - -# my name -DnMAILER-DAEMON -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$Z ready at $b - -############### -# Options # -############### - -# location of alias file -OA/etc/aliases -# wait up to ten minutes for alias file rebuild -Oa10 -# substitution for space (blank) characters -OB. -# (don't) connect to "expensive" mailers -#Oc -# default delivery mode (deliver in background) -Odbackground -# temporary file mode -OF0600 -# default GID -Og1 -# location of help file -OH/usr/share/misc/sendmail.hf -# log level -OL9 -# default network name -ONARPA -# default messages to old style -Oo -# queue directory -OQ/var/spool/mqueue -# read timeout -- violates protocols -Or2h -# status file -OS/var/log/sendmail.st -# queue up everything before starting transmission -Os -# default timeout interval -OT3d -# time zone names (V6 only) -OtPST,PDT -# default UID -Ou1 -# wizard's password -OW* -# load average at which we just queue messages -Ox8 -# load average at which we refuse connections -OX12 - -########################### -# Message precedences # -########################### - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -##################### -# Trusted users # -##################### - -Troot -Tdaemon -Tuucp - -######################### -# Format of headers # -######################### - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$Z) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -# HPosted-Date: $a -# H?l?Received-Date: $b -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - - - -########################### -### Rewriting Rules ### -########################### - - -################################ -# Sender Field Pre-rewriting # -################################ -S1 -#R$*<$*>$* $1$2$3 defocus - -################################### -# Recipient Field Pre-rewriting # -################################### -S2 -#R$*<$*>$* $1$2$3 defocus - - - -################################# -# Final Output Post-rewriting # -################################# -S4 - -R@ $@ handle <> error addr - -# resolve numeric addresses to name if possible -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 lookup numeric internet addr - -# externalize local domain info -R$*<$+>$* $1$2$3 defocus -R@$+:@$+:$+ @$1,@$2:$3 <route-addr> canonical - -# UUCP must always be presented in old form -R$+@$-.UUCP $2!$1 u@h.UUCP => h!u - -# delete duplicate local names -R$+%$=w@$=w $1@$w u%host@host => u@host -R$+%$=w@$=w.$D $1@$w u%host@host => u@host - - -########################### -# Name Canonicalization # -########################### -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -- note RFC733 heuristic here -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 basic RFC821/822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+ @$1:$2 change all "," to ":" - -# localize and dispose of route-based addresses -R@$+:$+ $@$>6<@$1>:$2 handle <route-addr> - -# more miscellaneous cleanup -R$+ $:$>8$1 host dependent cleanup -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style addresses to a domain-based address -R$+^$+ $1!$2 convert ^ to ! -R$-!$+ $@$>6$2<@$1.UUCP> resolve uucp names -R$+.$-!$+ $@$>6$3<@$1.$2> domain uucps -R$+!$+ $@$>6$2<@$1.UUCP> uucp subdomains -R$+%$+ $:$>9$1%$2 user%host -R$+<@$+> $@$>6$1<@$2> already canonical -R$-.$+ $@$>6$2<@$1> host.user - - -################################# -# special local conversions # -################################# - -S6 -R$*<@$=w>$* $:$1<@$w>$3 get into u@$w form -R$*<@$=w.$D>$* $:$1<@$w>$3 -R$*<@$=U.UUCP>$* $:$1<@$w>$3 - -################################ -# Change rightmost % to @. # -################################ - -S9 -R$*%$* $1@$2 First make them all @'s. -R$*@$*@$* $1%$2@$3 Undo all but the last. -R$*@$* $@$1<@$2> Put back the brackets. - - - -################### -### Mailers ### -################### - - -############################################################ -############################################################ -##### -##### Local and Program Mailer specification -##### -############################################################ -############################################################ - -Mlocal, P=/usr/libexec/mail.local, F=lsDFMmn, S=10, R=20, A=mail -r $g -d $u -Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u - -S10 -R@ $n errors to mailer-daemon - - -############################################################ -############################################################ -##### -##### Local Domain SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to remain -##### the local domain. Hence, they can refer to hosts that are -##### not registered in the NIC host table. -##### -############################################################ -############################################################ - -Mtcpld, P=[IPC], F=mDFMueXLC, S=17, R=27, A=IPC $h, E=\r\n - -S17 - -# cleanup forwarding a bit -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 canonicalize -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.bitnet -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S27 - -# cleanup -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### Internet SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to leave -##### the local domain -- hence, they must be canonical according to -##### RFC822 etc. This means that machines not registered with -##### the NIC must be hidden behind our Internet relay. -##### -############################################################ -############################################################ - -Mtcp, P=[IPC], F=mDFMueXLC, S=14, R=24, A=IPC $h, E=\r\n - -S14 - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output internal ("fake") domains as "user%host@relay" - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S24 - -# put in <> kludge -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere..... -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# Hide fake domains behind relays - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - - -############################################################ -############################################################ -##### -##### Provide Backward Compatibility -##### -############################################################ -############################################################ - -##################################################### -# General code to convert back to old style names # -##################################################### -S5 - -R$+<@$w> $1 strip host -R$+<@$-.UUCP> $2!$1 u@host.UUCP => host!u - -############################################################ -############################################################ -##### -##### UUCP Mailer specification -##### -############################################################ -############################################################ - - -Muucp, P=/usr/local/bin/uux, F=DFMhuU, S=13, R=23, M=100000, - A=uux - -r -z -a$f -gC $h!rmail ($u) - -S13 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$+> $2!$1 uucpize (no @'s in addr) -R$w!$+ $1 strip local name -R$+ $:$U!$1 stick on our host name -R$=U!$-%$- $:$1!$2@$3.$D ucbvax!user@host.domain - -S23 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$w> $U!$1 a!b@here -> here!a!b -R$=U!$+ $2 here!a!b -> a!b -# sanity ... should not happen. -R$=U.$D!$+ $2 strip local name.domain - - - -##################### -### Rule Zero ### -##################### - - -############################################################ -############################################################ -##### -##### RULESET ZERO PREAMBLE -##### -##### The beginning of ruleset zero is constant through all -##### configurations. -##### -############################################################ -############################################################ - -S0 - -# first make canonical -R$*<$*>$* $1$2$3 defocus -R$+ $:$>3$1 make canonical - -# handle special cases -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 numeric internet addr -R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec -R$+ $:$>6$1 -R$-<@$w> $#local$:$1 -R@ $#error$:Invalid address handle <> form - -# canonicalize using the nameserver if not internal domain -R$*<@$*.$~I>$* $:$1<@$[$2.$3$]>$4 -R$*<@$->$* $:$1<@$[$2$]>$3 -R$*<@$->$* $:$1<@$2.$D>$3 if nameserver fails - -# now delete the local info -R<@$w>:$* $@$>0$1 @here:... -> ... -R$*<@$w> $@$>0$1 ...@here -> ... - -################################## -# End of ruleset zero preamble # -################################## - - -############################################### -### Machine dependent part of Rule Zero ### -############################################### - - - -# resolve local UUCP connections -R<@$=V.UUCP>:$+ $#uucp$@$1$:$2 @host.UUCP:... -R$+<@$=V.UUCP> $#uucp$@$2$:$1 user@host.UUCP - - -# resolve fake top level domains by forwarding to other hosts - - -# forward non-local UUCP traffic to our UUCP relay -R$*<@$*.UUCP>$* $#tcpld$@$R$:$1<@$2.UUCP> uucp mail - -# hide behind our internet relay when talking to people in the arpa domain -R$*<@$*.arpa>$* $#tcp$@$2.arpa$:$1<@$2.arpa>$3 user@host.arpa - -# but speak domains to them if they speak domains too -R$*<@$*>$* $#tcpld$@$2$:$1<@$2>$3 user@host.domain -# -# Handle "fake" top level domains, by sending to a smarter -# host. Currently, we support: -# -# user@host.bitnet -> $B user@host.bitnet -# user@host.csnet -> $C user@host.csnet -# -# Eventually, all these should Go Away. -# -# @(#)fake_domains.m4 1.5 (Berkeley) 1/3/89 -# -R$*<@$+.BITNET>$* $#tcp$@$B$:$1<@$2.BITNET>$3 user@host.BITNET -R$*<@$+.CSNET>$* $#tcp$@$C$:$1<@$2.CSNET>$3 user@host.CSNET - - -# remaining names must be local -R$+ $#local$:$1 everything else diff --git a/usr.sbin/sendmail/cf/cf/id.mc b/usr.sbin/sendmail/cf/cf/id.mc deleted file mode 100644 index df1fa85e2a..0000000000 --- a/usr.sbin/sendmail/cf/cf/id.mc +++ /dev/null @@ -1,17 +0,0 @@ -divert(10)dnl -# -# ic configuration file -# -# From: @(#)ic.mc 1.2 (Berkeley) 1/3/89 -# -define(UUCP_NAME, DUid) -define(UUCP_ALIASES, CUid uid) -define(UUCP_HOSTS_FILE, ../sitedep/uucp.id.m4) -define(INTERNET_RELAY, `DAjpunix') -define(UUCP_RELAY, `DRjpunix') -define(BITNET_RELAY, `DBricevm1.Rice.EDU') -define(CSNET_RELAY, `DCrelay.cs.net') -define(ARPAKLUDGE, `1') -define(EXTERNAL_VERSION, ``#' `based on: @(#)ic.mc 1.2' (Berkeley) `1/3/89'') -divert(0)dnl -include(proto.mc)dnl diff --git a/usr.sbin/sendmail/cf/cf/kappa.cf b/usr.sbin/sendmail/cf/cf/kappa.cf deleted file mode 100644 index 722534e673..0000000000 --- a/usr.sbin/sendmail/cf/cf/kappa.cf +++ /dev/null @@ -1,649 +0,0 @@ -########################################################################### -########################################################################### -##### -##### SENDMAIL CONFIGURATION FILE FOR NON-GATEWAY MACHINES -##### -##### Use this configuration file only on machines that -##### are neither the Internet gateway nor the UUCP -##### gateway. There are separate configuration files -##### for these gateway functions. -##### -########################################################################### -########################################################################### - - - -########## -## -## Miscellaneous configuration. -## -## Define various macros and classes that define the local -## mail and network configuration. -## -########## - -# Official domain name. -DDrice.edu - -# List of domain names that we are (at least sort of) in. $D isn't -# included here since it is multiple "words" long. It is handled -# separately when needed. -CDarpa uucp -# Bogus Domains that we aren't in, but people keep sending us mail there. -# Would you believe we even get addresses like something.rice.csnet? Sigh... -CDrice csnet csnet-east csnet-west east west mmdf - -# List of pseudo host names to treat as local (alone or in $D or $=D). -CLrice localhost loopback local - -# Name of Internet mail gateway to the outside world. -DGrice.edu -# Name of uucp mail gateway for local connections. -DViapetus.rice.edu - -# Name of UUCP routing gateway. -DWseismo.css.gov - -# UUCP hostname. -DUrice - -# Non-Internet pseudo domain names. These will not be canonicalized. -CNUUCP CSNET BITNET MAILNET MFENET CSN CHUNET JUNET COSAC SPAN - - - -########## -## -## Critical macro definitions. -## -## These macros are used to import information from the -## configuration file into sendmail. -## -########## - -# SMTP daemon name. -DnMAILER-DAEMON -# UNIX header format. -DlFrom $g $d -# Delimiter (operator) characters. -Do.:%@!^=/[] -# Format of a total name. -Dq$?x$x <$g>$|$g$. -#Dq$g$?x ($x)$. -# My official hostname ($w is already official). -Dj$w -# SMTP login message. -De$j SMTP open for business at $b. - -########## -## -## Option definitions -## -## These define the default values for various sendmail options. -## These can be overridden on the senmail command line with -## the "-o" flag. -## -########## - -# Location of alias file. -OA/usr/lib/aliases -# Wait up to ten minutes for alias file rebuild. -Oa10 -# Default delivery mode (deliver in background). -Odbackground -# Don't connect to "expensive" mailers. -Oc -# Temporary file mode. -OF0600 -# Location of help file. -OH/usr/lib/sendmail.hf -# log level -OL9 -# Default network name. ??? -ON$D -# Messages might be in old style. -Oo -# Queue directory. -OQ/usr/spool/mqueue -# Read timeout -- violates protocols. -Or2h -# Status file. -OS/etc/sendmail.st -# Queue up everything before starting transmission. -Os -# Default timeout interval. -OT7d -# Default UID. -Ou1 -# Default GID. -Og1 -# Wizard's password. -OW* -# Load average at which we just queue messages. -Ox8 -# Load average at which we refuse connections. -OX12 - -########## -## -## Message precedences -## -########## - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -########## -## -## Trusted users -## -########## - -Troot -Tuucp -Tdaemon - -########## -## -## Format of headers -## -########## - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($i); $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - -########## -## -## Rewriting rules -## -## These are applied automatically by sendmail in the following order: -## -## +--> 0 --> 2 --> R --> 4 --> resolved address -## | -## | +--> 1 --> S --+ -## addr --> 3 ---> D --| |--> 4 --> message -## +--> 2 --> R --+ -## -## D - sender domaion addition -## S - mailer specific sender rewriting -## R - mailer specific recipient rewriting -## -########## - -########## -## -## Ruleset 0 -## -## Resolve addresses to specific mailer. -## -########## - -S0 - -# Focus on the next host name in the path. If we are already focused, -# though, just use it and don't do it again. Also, there's no need -# to try to focus on a single token, so optimize this out; these are -# just simple local addresses. -R$* $:<>$1 add a marker -R<>$*<@$*>$* $:$1<@$2>$3 focused, so unmark -R<>$- $:$1 single, so unmark -R<>$* $:$>3$1 focus - -# Handle special cases. -R@ $#local$:$n handle <> form - -# Remove our own host name if it's there and start over. -R$+<@$j> $@$>0$1 @thishost -R$+<@$w> $@$>0$1 @thishost -R$+<@$w.$=D> $@$>0$1 @thishost.local-domain -R$+<@$w.$=D.$=D> $@$>0$1 @thishost.local.local -R$+<@$w.$D> $@$>0$1 @thishost.local-domain -R$+<@$=w.$=D> $@$>0$1 @thishost.local-domain -R$+<@$=w.$=D.$=D> $@$>0$1 @thishost.local.local -R$+<@$=w.$D> $@$>0$1 @thishost.local-domain -R$+<@$=w> $@$>0$1 @thishost -R<@$j>:$+ $@$>0$1 @thishost -R<@$w>:$+ $@$>0$1 @thishost -R<@$w.$=D>:$+ $@$>0$2 @thishost.local-domain -R<@$w.$=D.$=D>:$+ $@$>0$3 @thishost.local.local -R<@$w.$D>:$+ $@$>0$1 @thishost.local-domain -R<@$=w.$=D>:$+ $@$>0$3 @thishost.local-domain -R<@$=w.$=D.$=D>:$+ $@$>0$4 @thishost.local.local -R<@$=w.$D>:$+ $@$>0$2 @thishost.local-domain -R<@$=w>:$+ $@$>0$2 @thishost - -# Treat the various forms of the names $=L as all local, too. -R$+<@$=L.$D> $@$>0$1 c@local -R$+<@$D> $@$>0$1 c@local -R$+<@$=L.$=D> $@$>0$1 c@local -R$+<@$=L.$=D.$=D> $@$>0$1 c@local -R$+<@$=L> $@$>0$1 c@local -R<@$=L.$D>:$+ $@$>0$2 @local:b@c -R<@$D>:$+ $@$>0$1 @local:b@c -R<@$=L.$=D>:$+ $@$>0$3 @local:b@c -R<@$=L.$=D.$=D>:$+ $@$>0$4 @local:b@c -R<@$=L>:$+ $@$>0$2 @local:b@c - -# For known local hosts, send directly there (both normal and route addrs). -# For compatibility with older mailers in the domain that don't accept -# the .$D form of their name, we use only the simple name without the .$D in -# the $: clause here. This shouldn't be a problem since these addresses -# don't leave the local domain. -R$*<@$-.$D>$* $#domain$@$2.$D$:$1<@$2>$3 - - -# Send all UUCP addresses to the UUCP gateway. -R$*<@$+.UUCP>$* $#domain$@$V$:$1<@$2.UUCP>$3 - - - -# Other non-local names will be passed off to the gateway. -R$*<@$*>$* $#domain$@$G$:$1<@$2>$3 user@somewhere - - -# Everything else is a local name. -R$+ $#local$:$1 local names - -########## -## -## Ruleset 1 -## -## Sender address pre-rewriting. -## -########## - -S1 - -########## -## -## Ruleset 2 -## -## Recipient address pre-rewriting. -## -## Since the "$:" part of what ruleset 0 returns is passed through -## rulesets 2, R, and 4, this rule MUST focus on the next host -## name in the path. We don't need (and don't want) all of the -## canonicalization of ruleset 3, though, so we just pass it through -## ruleset 9 only. -## -########## - -S2 - -# Focus on the next host name in the path. If we are already focused, -# though, just use it and don't do it again. Also, there's no need -# to try to focus on a single token, so optimize this out; these are -# just simple local addresses. -R$* $:<>$1 add a marker -R<>$*<@$*>$* $:$1<@$2>$3 focused, so unmark -R<>$- $:$1 single, so unmark -R<>$* $:$>9$1 focus - -########## -## -## Ruleset 3 -## -## Name canonicalization. -## -## This rule can not strip off @local-host-name since this is applied -## before the sender domain addition based on the 'C' mailer flag. -## Instead, this is deferred until ruleset 9, which is called at -## the beginning of rulesets 0, 1, and 2. -## -########## - -S3 - -# Handle "from:<>" special case. -R<> $@@ turn into magic token - -# Basic textual canonicalization -- note RFC733 heuristic here. -R<$*> $1 <> nested on outside -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 flush 822 comments -R$+ at $+ $1@$2 "at" -> "@" - -# Get rid of list-name addresses. ??? -#R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax - -# Make sure <@a,@b,@c:user@d> syntax is easy to parse. -# This is undone in ruleset 4 before it is seen outside of sendmail. -R@$+,@$+ @$1:@$2 change ",@" to ":@" - -# Focus on the next host name in the path. This places <> around the -# next @hostname. -R$+ $:$>9$1 focus on domain - -# Map old UUCP connections into Internet addresses. -R$*<@lbl-csam.UUCP>$* $:$1<@LBL-CSAM.ARPA>$2 -R$*<@ut-sally.UUCP>$* $:$1<@SALLY.UTEXAS.EDU>$2 -R$*<@ut-ngp.UUCP>$* $:$1<@NGP.UTEXAS.EDU>$2 -R$*<@cornell.UUCP>$* $:$1<@CU-ARPA.CS.CORNELL.EDU>$2 -R$*<@cmucsg.UUCP>$* $:$1<@G.CS.CMU.EDU>$2 - -# Handle UUCP name changes. -R$*<@parsec.UUCP>$* $:$1<@convex.UUCP>$2 -R$*<@drilltech.UUCP>$* $:$1<@drillsys.UUCP>$2 -R$*<@drilltec.UUCP>$* $:$1<@drillsys.UUCP>$2 -R$*<@drillte.UUCP>$* $:$1<@drillsys.UUCP>$2 - -# Translate a numeric Internet address back into a name if possible. -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 try it -R$*<@[$+]>$* $@$1<@[$2]>$3 couldn't get it - -# Canonicalize local host names that explicitly have our domain. -R$*<@$-.$D>$* $:$1<@$[$2.$D$]>$3 - -# Canonicalize local host names that don't explicitly have our domain. -# For names with no domain or those in the domains we are sort of in, -# try canonicalizing the name, but put back the old name if its not found. -R$*<@$->$* $:$1<@$2><$2>$3 hide old domain -R$*<@$-.>$* $:$1<@$2><$2.>$3 hide old domain -R$*<@$-.$=D>$* $:$1<@$2><$2.$3>$4 hide old domain -R$*<@$-.$=D.$=D>$* $:$1<@$2><$2.$3.$4>$5 hide old domain -R$*<@$-><$*>$* $:$1<@$[$2$]><$3>$4 map simple name -R$*<@$+.$D><$*>$* $:$1<@$2.$D>$4 keep result -R$*<@$*><$*>$* $:$1<@$3>$4 put old domain back - -# Map old-style pseudo domains to the new official names. -R$*<@$+.OZ>$* $:$1<@$2.OZ.AU>$3 Australia -R$*<@$+.DEC>$* $:$1<@$2.DEC.COM>$3 Digital Equipment Corp -R$*<@$+.ATT>$* $:$1<@$2.ATT.COM>$3 AT&T - -# Leave non Internet addresses alone now and just return them. -R$*<@$+.$=N>$* $@$1<@$2.$3>$4 - -# Try to canonicalize anything that's left. -R$*<@$+>$* $@$1<@$[$2$]>$3 try canonicalization - -########## -## -## Ruleset 4 -## -## Final output post-rewriting. -## -########## - -S4 - -R@ $@ handle <> error addr - -R$*<@$*.>$* $:$1<@$2>$3 remove trailing dot - -R$*<$*>$* $:$1$2$3 defocus - -# Turn addresses with mixed !'s and @'s into all !-form. -# Sigh.... removed since other mailers don't understand it.... ??? -#R$*!$*@$* $:$>6$1!$2@$3 mixed ! and @ -#R$*@$*!$* $:$>6$1@$2!$3 mixed @ and ! - -# Turn route address back into correct form. -R@$+:@$+ @$1,@$2 @a:@route => @a,@route - -########## -## -## Ruleset 6 (and rulesets 7 and 8) -## -## Convert address into UUCP !-style address. -## -## Ruleset 6 is called to do the conversion. This simply sets up the -## initial arguments for ruleset 7 to do recursion on. Ruleset 7 -## then recurses on itself for each element in the path, building up -## the resultant path in !-format one element at a time in the -## leading <>s. Ruleset 8 is used as a subroutine of ruleset 7 -## to focus on the next element in the path. This is just a copy -## of the domain focusing done in ruleset 3, but the initial -## path-so-far in <>s is allowed for. -## -########## - -# Set up for initial recursion by ruleset 7. Each new element of the -# path will be added to this "<>". -S6 -R$* $@$>7<>$1 set up for recursion - -# Recurse for each element of the path, translating it and adding it to -# the path-so-far in the leading "<>". -S7 -R<$*>$*<$*>$* $:<$1>$2$3$4 defocus -R<$*>$+ $:$>8<$1>$2 focus on next element -R<$*><@$+.UUCP>:$+ $@$>7<$1!$2>$3 move into result -R<$*><@$+>:$+ $@$>7<$1!$2>$3 move into result -R<$*>$+<@$+.UUCP> $@$>7<$1!$3>$2 move into result -R<$*>$+<@$+> $@$>7<$1!$3>$2 move into result -R<$*>$+ $:<$1!$2> get leftovers -R<!$+> $:<$1> remove leading ! -R<$+> $:$1 remove markers - -# Focus on the next element of the input path. -S8 - -# Localize and dispose of route-based addresses. -R<$*>@$+:$+ $@<$1><@$2>:$3 handle <route-addr> - -# Focus on the right-most domain in the address. -R<$*>$+@$+ $:<$1>$2<@$3> focus on domain -R<$*>$+<$+@$+> <$1>$2$3<@$4> move gaze right -R<$*>$+<$+%$+> <$1>$2$3<%$4> move gaze right -R<$*>$+<@$+> $@<$1>$2<@$3> already canonical -R<$*>$+<%$+> $@<$1>$2<@$3> already canonical -R<$*>$+%$+ $:<$1>$2<%$3> focus on domain -R<$*>$+<$+%$+> <$1>$2$3<%$4> move gaze right -R<$*>$+<%$+> $@<$1>$2<@$3> already canonical - -# Convert old-style UUCP addresses to domain-based addresses. -R<$*>$+^$+ <$1>$2!$3 convert ^ to ! -R<$*>$+!$+ $:<$1><$2>!$3 focus on path -R<$*><$+!$+>$+ <$1><$2>!$3$4 move gaze left -R<$*><$+.$*>!$+ $@<$1>$4<@$2.$3> x.y!b => b@x.y -R<$*><$+>!$+ $@<$1>$3<@$2.UUCP> a!b => b@a.UUCP - -# Convert old-style BITNET addresses to domain-based addresses. -R<$*>$-=$+ $@<$1>$3<@$2.BITNET> a=b => b@a.BITNET - -# Convert old-style Berknet-syntax addresses to domain-based addresses. -R<$*>$-:$+ $@<$1>$3<@$2> host:user - -# Anything else is a local address, so just leave it alone - -########## -## -## Ruleset 9 -## -## Focus on the next host name in the path. -## -########## - -S9 - -R$*<$*>$* $1$2$3 defocus - -# Localize and dispose of route-based addresses. -R@$+:$+ $@<@$1>:$2 handle <route-addr> - -# Focus on the right-most domain in the address. -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<$+%$+> $1$2<%$3> move gaze right -R$+<@$+> $@$1<@$2> already canonical -R$+<%$+> $@$1<@$2> already canonical -R$+%$+ $:$1<%$2> focus on domain -R$+<$+%$+> $1$2<%$3> move gaze right -R$+<%$+> $@$1<@$2> already canonical - -# Convert old-style UUCP addresses to domain-based addresses. -R$+^$+ $1!$2 convert ^ to ! -R$+!$+ $:<$1>!$2 focus on path -R<$+!$+>$+ <$1>!$2$3 move gaze left -R<$+.$*>!$+ $@$3<@$1.$2> x.y!b => b@x.y -R<$+>!$+ $@$2<@$1.UUCP> a!b => b@a.UUCP - -# Convert old-style BITNET addresses to domain-based addresses. -R$-=$+ $@$2<@$1.BITNET> a=b => b@a.BITNET - -# Convert old-style Berknet-syntax addresses to domain-based addresses. -R$-:$+ $@$2<@$1> host:user - -# Anything else is a local address, so just leave it alone. - -########## -## -## Local and program mailer specifications -## -########## - -# Mailer flags specified: -# l final delivery will be performed -# m accepts multiple recipients -# n do not insert Unix-style "From " line -# r mailer wants "-r" flag for sender -# s strip quote off before calling mailer -# D wants "Date:" header line -# F wants "From:" header line -# M wants "Message-id:" header line -# -Mlocal, P=/bin/mail, F=lmnrsDFM, S=10, R=20, A=mail -d $u - -# Mailer flags specified: -# e mailer is expensive -# l final delivery will be performed -# s strip quote off before calling mailer -# D wants "Date:" header line -# F wants "From:" header line -# M wants "Message-id:" header line -# -Mprog, P=/bin/sh, F=elsDFM, S=10, R=20, A=sh -c $u - -S10 -# Convert special <> token back into a name. -R@ $n errors from myself -# Make sure we have a host name on the address. ??? -#R$*<@$+>$* $@$1<@$2>$3 already ok -#R$+ $@$1<@$j> tack on our hostname - -S20 -# Convert special <> token back into a name. -R@ $n errors to myself -# Make sure we have a host name on the address. ??? -#R$*<@$+>$* $@$1<@$2>$3 already ok -#R$+ $@$1<@$j> tack on our hostname - -########## -## -## Intra-domain mailer specification -## -## This is used for all mail within our domain. -## -########## - -# Mailer flags specified: -# m accepts multiple recipients -# s strip quote off before calling mailer -# u preserve upper case in usernames -# C add sender domain when received from mailer -# D wants "Date:" header line -# F wants "From:" header line -# M wants "Message-id:" header line -# X use RFC821 hidden dot algorithm -# -Mdomain, P=[IPC], F=msuCDFMX, S=11, R=21, A=IPC $h, E=\r\n - -S11 -R$*<@$+>$* $@$1<@$2>$3 already ok -R$+ $@$1<@$j> tack on our hostname - -S21 -R$*<@$+>$* $@$1<@$2>$3 already ok -R$+ $@$1<@$j> tack on our hostname - -########## -## -## World mailer specification -## -## This is used for all mail to the outside world. -## -########## - -# Mailer flags specified: -# m accepts multiple recipients -# s strip quote off before calling mailer -# u preserve upper case in usernames -# C add sender domain when received from mailer -# D wants "Date:" header line -# F wants "From:" header line -# L limit line lengths from RFC821 -# M wants "Message-id:" header line -# X use RFC821 hidden dot algorithm -# -Mworld, P=[IPC], F=msuCDFLMX, S=12, R=22, A=IPC $h, E=\r\n - -S12 - -# Make it look like all mail to the outside world is from the gateway. -R$*<@$-.$D>$* $@$1<@$G>$3 known local names -R$*<@$D>$* $@$1<@$G>$2 known local names -R$*<@$=L>$* $@$1<@$G>$3 known local names - -R$*<@$+>$* $@$1<@$2>$3 already has hostname -R$+ $@$1<@$G> tack on gateway name - -S22 -R$*<@$+>$* $@$1<@$2>$3 already has hostname -R$+ $@$1<@$j> tack on our hostname - -########## -## -## UUCP mailer specification -## -########## - -# Mailer flags specified: -# h preserve upper case in hostnames -# s strip quote off before calling mailer -# u preserve upper case in usernames -# C add sender domain when received from mailer -# D wants "Date:" header line -# F wants "From:" header line -# M wants "Message-id:" header line -# U use ugly UUCP-style Unix-style "From " line -# -Muucp, P=/usr/bin/uux, F=hsuCDFMU, S=13, R=23, - A=uux - -r -n -a$g -gC $h!rmail ($u) - -S13 - -R$+ $:$>6$1 turn into all ! syntax - -# Make it look like all mail from local hosts is from the gateway. -R$+!$+ $:<$1>!$2 focus on path -R<$+!$+>$+ <$1>!$2$3 move gaze left -R<$-.$D>!$+ $@$U!$2 known local names -R<$D>!$+ $@$U!$1 known local names -R<$=L>!$+ $@$U!$2 known local names -R$*<$*>$* $:$1$2$3 defocus - -R$+ $@$U!$1 add our UUCP name - -S23 -R$+ $:$>6$1 turn into all !'s - -# Turn local host names into our UUCP name. This is the name that we -# are known by to the UUCP host that we are now sending to. -R$+!$+ $:<$1>!$2 focus on path -R<$+!$+>$+ <$1>!$2$3 move gaze left -R<$-.$D>!$+ $@$U!$2 known local names -R<$D>!$+ $@$U!$1 known local names -R<$=L>!$+ $@$U!$2 known local names -R$*<$*>$* $:$1$2$3 defocus diff --git a/usr.sbin/sendmail/cf/cf/knecht.mc b/usr.sbin/sendmail/cf/cf/knecht.mc new file mode 100644 index 0000000000..0cd17fab68 --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/knecht.mc @@ -0,0 +1,44 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)knecht.mc 8.1 (Berkeley) 6/7/93') +OSTYPE(ultrix4.1)dnl +DOMAIN(cs.exposed)dnl +define(`LOCAL_RELAY', CS.Berkeley.EDU)dnl +MAILER(smtp)dnl + +# our local domain +DDCS.Berkeley.EDU diff --git a/usr.sbin/sendmail/cf/cf/lamprey.cf b/usr.sbin/sendmail/cf/cf/lamprey.cf deleted file mode 100644 index 33f8bf2d5f..0000000000 --- a/usr.sbin/sendmail/cf/cf/lamprey.cf +++ /dev/null @@ -1,311 +0,0 @@ -########################################################### -# -# SENDMAIL CONFIGURATION FILE FOR SUBSIDIARY MACHINES -# -# You should install this file as /etc/sendmail.cf -# if your machine is a subsidiary machine (that is, some -# other machine in your domain is the main mail-relaying -# machine). Then edit the file to customize it for your -# network configuration. -# -# See the manual "System Administration for the Sun Workstation". -# Look at "Setting Up The Mail Routing System" in the chapter on -# Communications. The Sendmail references in the back of the -# manual are also very useful. -# -# @(#)subsidiary.mc 1.11 88/02/08 SMI; from UCB arpa.mc 3.25 2/24/83 -# - -# local UUCP connections -- not forwarded to mailhost -CV - -# my official hostname -Dj$w - -# major relay mailer -DMether - -# major relay host -DRbeach.gal.utexas.edu -CRbeach.gal.utexas.edu - -################################################# -# -# General configuration information - -# local domain names -# -# These can now be determined from the domainname system call. -# The first component of the NIS domain name is stripped off unless -# it begins with a dot or a plus sign. -# If your NIS domain is not inside the domain name you would like to have -# appear in your mail headers, add a "Dm" line to define your domain name. -# The Dm value is what is used in outgoing mail. The Cm values are -# accepted in incoming mail. By default Cm is set from Dm, but you might -# want to have more than one Cm line to recognize more than one domain -# name on incoming mail during a transition. -# Example: -# DmCS.Podunk.EDU -# Cm cs cs.Podunk.EDU -# -DmGal.UTexas.Edu -Cm Gal Gal.UTexas.Edu - -# known hosts in this domain are obtained from gethostbyname() call - -# Version number of configuration file -DVSMI-4.1 - - -### Standard macros - -# name used for error messages -DnMailer-Daemon -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$V ready at $b - -### Options - -# Remote mode - send through server if mailbox directory is mounted -OR -# location of alias file -OA/etc/aliases -# default delivery mode (deliver in background) -Odbackground -# rebuild the alias file automagically -OD -# temporary file mode -- 0600 for secure mail, 0644 for permissive -OF0600 -# default GID -Og1 -# location of help file -OH/usr/lib/sendmail.hf -# log level -OL9 -# default messages to old style -Oo -# Cc my postmaster on error replies I generate -OPPostmaster -# queue directory -OQ/usr/spool/mqueue -# read timeout for SMTP protocols -Or15m -# status file -- none -OS/etc/sendmail.st -# queue up everything before starting transmission, for safety -Os -# return queued mail after this long -OT3d -# default UID -Ou1 - -### Message precedences -Pfirst-class=0 -Pspecial-delivery=100 -Pjunk=-100 - -### Trusted users -T root daemon uucp - -### Format of headers -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$V) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> -HErrors-To: - -########################### -### Rewriting rules ### -########################### - - -# Sender Field Pre-rewriting -S1 -# None needed. - -# Recipient Field Pre-rewriting -S2 -# None needed. - -# Name Canonicalization - -# Internal format of names within the rewriting rules is: -# anything<@host.domain.domain...>anything -# We try to get every kind of name into this format, except for local -# names, which have no host part. The reason for the "<>" stuff is -# that the relevant host name could be on the front of the name (for -# source routing), or on the back (normal form). We enclose the one that -# we want to route on in the <>'s to make it easy to find. -# -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -R$*<$+>$* $2 basic RFC822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+:$+ @$1:$2:$3 change all "," to ":" -R@$+:$+ $@$>6<@$1>:$2 src route canonical - -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style names to domain-based names -# All old-style names parse from left to right, without precedence. -R$-!$+ $@$>6$2<@$1.uucp> uucphost!user -R$-.$+!$+ $@$>6$3<@$1.$2> host.domain!user -R$+%$+ $@$>3$1@$2 user%host - -# Final Output Post-rewriting -S4 -R$+<@$+.uucp> $2!$1 u@h.uucp => h!u -R$+ $: $>9 $1 Clean up addr -R$*<$+>$* $1$2$3 defocus - - -# Clean up an name for passing to a mailer -# (but leave it focused) -S9 -R$=w!@ $@$w!$n -R@ $@$n handle <> error addr -R$*<$*LOCAL>$* $1<$2$m>$3 change local info -R<@$+>$*:$+:$+ <@$1>$2,$3:$4 <route-addr> canonical - - -####################### -# Rewriting rules - -# special local conversions -S6 -R$*<@$*$=m>$* $1<@$2LOCAL>$4 convert local domain - -# Local and Program Mailer specification - -Mlocal, P=/bin/mail, F=rlsDFMmnP, S=10, R=20, A=mail -d $u -Mprog, P=/bin/sh, F=lsDFMeuP, S=10, R=20, A=sh -c $u - -S10 -# None needed. - -S20 -# None needed. - -############################################################ -##### -##### Ethernet Mailer specification -##### -##### Messages processed by this configuration are assumed to remain -##### in the same domain. This really has nothing particular to do -##### with Ethernet - the name is historical. - -Mether, P=[TCP], F=msDFMuCX, S=11, R=21, A=TCP $h -S11 -R$*<@$+>$* $@$1<@$2>$3 already ok -R$+ $@$1<@$w> tack on our hostname - -S21 -# None needed. - - - -########################################################## -# General code to convert back to old style UUCP names -S5 -R$+<@LOCAL> $@ $w!$1 name@LOCAL => sun!name -R$+<@$-.LOCAL> $@ $2!$1 u@h.LOCAL => h!u -R$+<@$+.uucp> $@ $2!$1 u@h.uucp => h!u -R$+<@$*> $@ $2!$1 u@h => h!u -# Route-addrs do not work here. Punt til uucp-mail comes up with something. -R<@$+>$* $@ @$1$2 just defocus and punt -R$*<$*>$* $@ $1$2$3 Defocus strange stuff - -# UUCP Mailer specification - -Muucp, P=/usr/bin/uux, F=msDFMhuU, S=13, R=23, - A=uux - -r -a$f $h!rmail ($u) - -# Convert uucp sender (From) field -S13 -R$+ $:$>5$1 convert to old style -R$=w!$+ $2 strip local name -R$+ $:$w!$1 stick on real host name - -# Convert uucp recipient (To, Cc) fields -S23 -R$+ $:$>5$1 convert to old style - - -##### RULESET ZERO PREAMBLE - -# Ruleset 30 just calls rulesets 3 then 0. -S30 -R$* $: $>3 $1 First canonicalize -R$* $@ $>0 $1 Then rerun ruleset 0 - -S0 -# On entry, the address has been canonicalized and focused by ruleset 3. -# Handle special cases..... -R@ $#local $:$n handle <> form -# Earlier releases special-cased the [x.y.z.a] format, but SunOS 4.1 or later -# should handle these properly on input. - -# now delete redundant local info -R$*<$*$=w.LOCAL>$* $1<$2>$4 thishost.LOCAL -R$*<@LOCAL>$* $1<@$m>$2 host == domain gateway -R$*<$*$=w.uucp>$* $1<$2>$4 thishost.uucp -R$*<$*$=w>$* $1<$2>$4 thishost - -# arrange for local names to be fully qualified -R$*<@$%y>$* $1<@$2.LOCAL>$3 user@etherhost - -# For numeric spec, you can't pass spec on to receiver, since old rcvr's -# were not smart enough to know that [x.y.z.a] is their own name. -R<@[$+]>:$* $:$>9 <@[$1]>:$2 Clean it up, then... -R<@[$+]>:$* $#ether $@[$1] $:$2 numeric internet spec -R<@[$+]>,$* $#ether $@[$1] $:$2 numeric internet spec -R$*<@[$+]> $#ether $@[$2] $:$1 numeric internet spec - -R$*<$*.>$* $1<$2>$3 drop trailing dot -R<@>:$* $@$>30$1 retry after route strip -R$*<@> $@$>30$1 strip null trash & retry - - -################################################ -### Machine dependent part of ruleset zero ### -################################################ - -# resolve names we can handle locally -R<@$=V.uucp>:$+ $:$>9 $1 First clean up, then... -R<@$=V.uucp>:$+ $#uucp $@$1 $:$2 @host.uucp:... -R$+<@$=V.uucp> $#uucp $@$2 $:$1 user@host.uucp - -# optimize names of known ethernet hosts -R$*<@$%y.LOCAL>$* $#ether $@$2 $:$1<@$2>$3 user@host.here - -# other non-local names will be kicked upstairs -R$+ $:$>9 $1 Clean up, keep <> -R$*<@$+>$* $#$M $@$R $:$1<@$2>$3 user@some.where -R$*@$* $#$M $@$R $:$1<@$2> strangeness with @ - -# Local names with % are really not local! -R$+%$+ $@$>30$1@$2 turn % => @, retry - -# everything else is a local name -R$+ $#local $:$1 local names diff --git a/usr.sbin/sendmail/cf/cf/mail.cs.mc b/usr.sbin/sendmail/cf/cf/mail.cs.mc new file mode 100644 index 0000000000..cb447c12d1 --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/mail.cs.mc @@ -0,0 +1,54 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)mail.cs.mc 8.1 (Berkeley) 6/7/93') +OSTYPE(ultrix4.1)dnl +DOMAIN(cs.exposed)dnl +FEATURE(notsticky)dnl +MAILER(local)dnl +MAILER(smtp)dnl +define(`confUSERDB_SPEC', ``/usr/local/lib/users.cs.db,/usr/local/lib/users.eecs.db'')dnl +DDBerkeley.EDU + +# hosts for which we accept and forward mail (must be in .Berkeley.EDU) +CF CS +FF/etc/sendmail.cw + +LOCAL_RULE_0 +R< @ $=F . $D . > : $* $@ $>7 $2 @here:... -> ... +R$* $=O $* < @ $=F . $D . > $@ $>7 $1 $2 $3 ...@here -> ... + +R$* < @ $=F . $D . > $#local $: $1 use UDB diff --git a/usr.sbin/sendmail/cf/cf/mail.eecs.mc b/usr.sbin/sendmail/cf/cf/mail.eecs.mc new file mode 100644 index 0000000000..3b6200c3a8 --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/mail.eecs.mc @@ -0,0 +1,54 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)mail.eecs.mc 8.1 (Berkeley) 6/7/93') +OSTYPE(ultrix4.1)dnl +DOMAIN(eecs.hidden)dnl +FEATURE(notsticky)dnl +MAILER(local)dnl +MAILER(smtp)dnl +define(`confUSERDB_SPEC', `/usr/local/lib/users.cs.db,/usr/local/lib/users.eecs.db')dnl +DDBerkeley.EDU + +# hosts for which we accept and forward mail (must be in .Berkeley.EDU) +CF EECS +FF/etc/sendmail.cw + +LOCAL_RULE_0 +R< @ $=F . $D . > : $* $@ $>7 $2 @here:... -> ... +R$* $=O $* < @ $=F . $D . > $@ $>7 $1 $2 $3 ...@here -> ... + +R$* < @ $=F . $D . > $#local $: $1 use UDB diff --git a/usr.sbin/sendmail/cf/cf/okeeffe.cf b/usr.sbin/sendmail/cf/cf/okeeffe.cf deleted file mode 100644 index e8d3d41379..0000000000 --- a/usr.sbin/sendmail/cf/cf/okeeffe.cf +++ /dev/null @@ -1,606 +0,0 @@ -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)okeeffe.mc 1.3 (Berkeley) 1/3/89 -# @(#)proto.mc 1.20 (Berkeley) 1/25/89 -# -# built by phil on Wed Jan 25 11:47:02 PST 1989 -# in /usr/src/local/nettables/sendmail.cf/cf on monet.Berkeley.EDU -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -############################################################ -############################################################ - - -################## -# local info # -################## - -# file containing our internet aliases -Fw/usr/lib/sendmail.cw - -# uucp hostnames -DUokeeffe -CUokeeffe - -# local UUCP connections -CV blia -CV ccicpg -CV mjk -CV pixar -CV zulu - - -############################# -### Setup Information ### -############################# - - -###################### -# General Macros # -###################### - -# local domain name -DDBerkeley.EDU - -# Internet relay host -- machines in our domain that are not -# registered with the NIC will be "hidden" behind this relay machine -# with the % kludge, although SMTP delivery will still be performed -# by the sending machine. Someday this will go away. -DAucbvax.Berkeley.EDU - -# UUCP relay host -DRucbvax.Berkeley.EDU - -# csnet relay host -DCrelay.cs.net - -# bitnet relay host -DBjade.Berkeley.EDU - -# my official hostname -Dj$w - - - -############### -# Classes # -############### - -# Internal ("fake") domains that we use in rewriting -CIUUCP BITNET CSNET - - - -############################################################ -############################################################ -##### -##### BERKELEY HOSTS REGISTERED WITH THE NIC -##### -############################################################ -############################################################ - - -CNbach ucbbach -CNbizet ucbbizet -CNcad ucbcad -CNdegas ucbdegas -CNeast ucbeast -CNernie ucbernie -CNesvax ucbesvax -CNjade ucbjade -CNji ucbji -CNmike ucbmike -CNmonet ucbmonet -CNpostgres -CNrenoir ucbrenoir -CNucbarpa -CNucbvax -CNviolet ucbviolet - - -###################### -# Version Number # -###################### - -DZ1.34 - - -###################### -# Special macros # -###################### - -# my name -DnMAILER-DAEMON -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$Z ready at $b - -############### -# Options # -############### - -# location of alias file -OA/usr/lib/aliases -# wait up to ten minutes for alias file rebuild -Oa10 -# substitution for space (blank) characters -OB. -# (don't) connect to "expensive" mailers -#Oc -# default delivery mode (deliver in background) -Odbackground -# temporary file mode -OF0600 -# default GID -Og1 -# location of help file -OH/usr/lib/sendmail.hf -# log level -OL9 -# default network name -ONARPA -# default messages to old style -Oo -# queue directory -OQ/usr/spool/mqueue -# read timeout -- violates protocols -Or2h -# status file -OS/usr/lib/sendmail.st -# queue up everything before starting transmission -Os -# default timeout interval -OT3d -# time zone names (V6 only) -OtPST,PDT -# default UID -Ou1 -# wizard's password -OW* -# load average at which we just queue messages -Ox8 -# load average at which we refuse connections -OX12 - -########################### -# Message precedences # -########################### - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -##################### -# Trusted users # -##################### - -Troot -Tdaemon -Tuucp - -######################### -# Format of headers # -######################### - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$Z) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -# HPosted-Date: $a -# H?l?Received-Date: $b -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - - - -########################### -### Rewriting Rules ### -########################### - - -################################ -# Sender Field Pre-rewriting # -################################ -S1 -#R$*<$*>$* $1$2$3 defocus - -################################### -# Recipient Field Pre-rewriting # -################################### -S2 -#R$*<$*>$* $1$2$3 defocus - - - -################################# -# Final Output Post-rewriting # -################################# -S4 - -R@ $@ handle <> error addr - -# resolve numeric addresses to name if possible -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 lookup numeric internet addr - -# externalize local domain info -R$*<$+>$* $1$2$3 defocus -R@$+:@$+:$+ @$1,@$2:$3 <route-addr> canonical - -# UUCP must always be presented in old form -R$+@$-.UUCP $2!$1 u@h.UUCP => h!u - -# delete duplicate local names -R$+%$=w@$=w $1@$w u%host@host => u@host -R$+%$=w@$=w.$D $1@$w u%host@host => u@host - - -########################### -# Name Canonicalization # -########################### -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -- note RFC733 heuristic here -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 basic RFC821/822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+ @$1:$2 change all "," to ":" - -# localize and dispose of route-based addresses -R@$+:$+ $@$>6<@$1>:$2 handle <route-addr> - -# more miscellaneous cleanup -R$+ $:$>8$1 host dependent cleanup -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style addresses to a domain-based address -R$+^$+ $1!$2 convert ^ to ! -R$-!$+ $@$>6$2<@$1.UUCP> resolve uucp names -R$+.$-!$+ $@$>6$3<@$1.$2> domain uucps -R$+!$+ $@$>6$2<@$1.UUCP> uucp subdomains -R$+%$+ $:$>9$1%$2 user%host -R$+<@$+> $@$>6$1<@$2> already canonical -R$-.$+ $@$>6$2<@$1> host.user - - -################################# -# special local conversions # -################################# - -S6 -R$*<@$=w>$* $:$1<@$w>$3 get into u@$w form -R$*<@$=w.$D>$* $:$1<@$w>$3 -R$*<@$=U.UUCP>$* $:$1<@$w>$3 - -################################ -# Change rightmost % to @. # -################################ - -S9 -R$*%$* $1@$2 First make them all @'s. -R$*@$*@$* $1%$2@$3 Undo all but the last. -R$*@$* $@$1<@$2> Put back the brackets. - - - -################### -### Mailers ### -################### - - -############################################################ -############################################################ -##### -##### Local and Program Mailer specification -##### -############################################################ -############################################################ - -Mlocal, P=/bin/mail, F=rlsDFMmn, S=10, R=20, A=mail -d $u -Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u - -S10 -R@ $n errors to mailer-daemon - - -############################################################ -############################################################ -##### -##### Local Domain SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to remain -##### the local domain. Hence, they can refer to hosts that are -##### not registered in the NIC host table. -##### -############################################################ -############################################################ - -Mtcpld, P=[IPC], F=mDFMueXLC, S=17, R=27, A=IPC $h, E=\r\n - -S17 - -# cleanup forwarding a bit -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 canonicalize -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.bitnet -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S27 - -# cleanup -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### Internet SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to leave -##### the local domain -- hence, they must be canonical according to -##### RFC822 etc. This means that machines not registered with -##### the NIC must be hidden behind our Internet relay. -##### -############################################################ -############################################################ - -Mtcp, P=[IPC], F=mDFMueXLC, S=14, R=24, A=IPC $h, E=\r\n - -S14 - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output internal ("fake") domains as "user%host@relay" - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S24 - -# put in <> kludge -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere..... -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# Hide fake domains behind relays - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### UUCP Mailer specification -##### -############################################################ -############################################################ - - -Muucp, P=/usr/bin/uux, F=DFMhuU, S=13, R=23, M=100000, - A=uux - -r -z -a$f -gC $h!rmail ($u) - -S13 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$+> $2!$1 uucpize (no @'s in addr) -R$w!$+ $1 strip local name -R$+ $:$U!$1 stick on our host name -R$=U!$-%$- $:$1!$2@$3.$D ucbvax!user@host.domain - -S23 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$w> $U!$1 a!b@here -> here!a!b -R$=U!$+ $2 here!a!b -> a!b -# sanity ... should not happen. -R$=U.$D!$+ $2 strip local name.domain - - -############################################################ -############################################################ -##### -##### Provide Backward Compatibility -##### -############################################################ -############################################################ - -##################################################### -# General code to convert back to old style names # -##################################################### -S5 - -R$+<@$w> $1 strip host -R$+<@$-.UUCP> $2!$1 u@host.UUCP => host!u - - - -##################### -### Rule Zero ### -##################### - - -############################################################ -############################################################ -##### -##### RULESET ZERO PREAMBLE -##### -##### The beginning of ruleset zero is constant through all -##### configurations. -##### -############################################################ -############################################################ - -S0 - -# first make canonical -R$*<$*>$* $1$2$3 defocus -R$+ $:$>3$1 make canonical - -# handle special cases -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 numeric internet addr -R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec -R$+ $:$>6$1 -R$-<@$w> $#local$:$1 -R@ $#error$:Invalid address handle <> form - -# canonicalize using the nameserver if not internal domain -R$*<@$*.$~I>$* $:$1<@$[$2.$3$]>$4 -R$*<@$->$* $:$1<@$[$2$]>$3 -R$*<@$->$* $:$1<@$2.$D>$3 if nameserver fails - -# now delete the local info -R<@$w>:$* $@$>0$1 @here:... -> ... -R$*<@$w> $@$>0$1 ...@here -> ... - -################################## -# End of ruleset zero preamble # -################################## - - -############################################### -### Machine dependent part of Rule Zero ### -############################################### - - - -# resolve local UUCP connections -R<@$=V.UUCP>:$+ $#uucp$@$1$:$2 @host.UUCP:... -R$+<@$=V.UUCP> $#uucp$@$2$:$1 user@host.UUCP - - -# resolve fake top level domains by forwarding to other hosts -R$*<@$+.BITNET>$* $#tcp$@$B$:$1<@$2.BITNET>$3 user@host.BITNET -R$*<@$+.CSNET>$* $#tcp$@$C$:$1<@$2.CSNET>$3 user@host.CSNET - - -# forward non-local UUCP traffic to our UUCP relay -R$*<@$*.UUCP>$* $#tcpld$@$R$:$1<@$2.UUCP> uucp mail - -# resolve SMTP traffic -R$*<@$*.$D>$* $#tcpld$@$2.$D$:$1<@$2.$D>$3 user@host.ourdomain -R$*<@$+>$* $#tcp$@$2$:$1<@$2>$3 user@host.ourdomain - -# remaining names must be local -R$+ $#local$:$1 everything else - diff --git a/usr.sbin/sendmail/cf/cf/okeeffe.mc b/usr.sbin/sendmail/cf/cf/okeeffe.mc deleted file mode 100644 index 15f862948a..0000000000 --- a/usr.sbin/sendmail/cf/cf/okeeffe.mc +++ /dev/null @@ -1,17 +0,0 @@ -divert(10)dnl -# -# okeeffe configuration file -# -# @(#)okeeffe.mc 1.3 (Berkeley) 1/3/89 -# -define(DOMAIN, `DDBerkeley.EDU') -define(UUCP_NAME, DUokeeffe) -define(UUCP_ALIASES, CUokeeffe) -define(UUCP_HOSTS_FILE, ../sitedep/uucp.okeeffe.m4) -define(INTERNET_RELAY, `DAucbvax.Berkeley.EDU') -define(UUCP_RELAY, DRucbvax.Berkeley.EDU) -define(BITNET_RELAY, `DBjade.Berkeley.EDU') -define(CSNET_RELAY, `DCrelay.cs.net') -define(EXTERNAL_VERSION, ``#' `@(#)okeeffe.mc 1.3' (Berkeley) `1/3/89'') -divert(0)dnl -include(proto.mc) diff --git a/usr.sbin/sendmail/cf/cf/proto.mc b/usr.sbin/sendmail/cf/cf/proto.mc deleted file mode 100644 index f56d937363..0000000000 --- a/usr.sbin/sendmail/cf/cf/proto.mc +++ /dev/null @@ -1,113 +0,0 @@ -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -ifdef(`EXTERNAL_VERSION', EXTERNAL_VERSION, `#') -# @(#)proto.mc 1.20 (Berkeley) 1/25/89 -# -sinclude(buildinfo)dnl -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -############################################################ -############################################################ - - -################## -# local info # -################## - -`#' file containing our internet aliases -Fw/etc/sendmail.cw - -ifdef(`UUCP_NAME', -`#' uucp hostnames -UUCP_NAME -UUCP_ALIASES - -`#' local UUCP connections -`include(UUCP_HOSTS_FILE)') - -############################# -### Setup Information ### -############################# - -include(../m4/nsmacros.m4) -include(../m4/nsclasses.m4) -ifdef(`INTERNET_RELAY', -`include(../sitedep/nicregistered.m4)') -include(../m4/version.m4) -include(../m4/boilerplate.m4) - -########################### -### Rewriting Rules ### -########################### - -include(../m4/prewriterule.m4) -include(../m4/postwriterule.m4) -include(../m4/rule3.m4) - -################### -### Mailers ### -################### - -include(../m4/localm.m4) -ifdef(`UUCP_ONLY',, -`include(../m4/nstcpldm.m4)') -include(../m4/nstcpm.m4) -ifdef(`UUCP_NAME', -`include(../m4/uucpm.m4)' -`include(../m4/rule5.m4)') -ifdef(`SMTPUUCP', -`include(../m4/smtpuucpm.m4)') - -##################### -### Rule Zero ### -##################### - -include(../m4/rule0.m4) - -############################################### -### Machine dependent part of Rule Zero ### -############################################### - -ifdef(`SMTPUUCP', -`# resolve SMTP UUCP connections' -`include(SMTPUUCP)') - -ifdef(`UUCP_NAME', -`# resolve local UUCP connections' -`R<@$=V.UUCP>:$+ $#uucp$@$1$:$2 @host.UUCP:...' -`R$+<@$=V.UUCP> $#uucp$@$2$:$1 user@host.UUCP' -) - -ifdef(`UUCP_ONLY',, -`#' resolve fake top level domains by forwarding to other hosts -`include(../m4/fake_domains.m4)' - -`ifdef(`UUCP_RELAY', -`#' forward non-local UUCP traffic to our UUCP relay -R$*<@$*.UUCP>$* $`#'tcpld$@$R$:$1<@$2.UUCP> uucp mail)' - -`ifdef(`ARPAKLUDGE', -`#' hide behind our internet relay when talking to people in the arpa domain -R$*<@$*.arpa>$* $`#'tcp$@$2.arpa$:$1<@$2.arpa>$3 user@host.arpa - -`#' but speak domains to them if they speak domains too -R$*<@$*>$* $`#'tcpld$@$2$:$1<@$2>$3 user@host.domain, -`#' resolve SMTP traffic -`ifdef(`INTERNET_RELAY', -R$*<@$*.$D>$* $`#'tcpld$@$2.$D$:$1<@$2.$D>$3 user@host.ourdomain -R$*<@$+>$* $`#'tcp$@$2$:$1<@$2>$3 user@host.ourdomain, -R$*<@$+>$* $`#'tcpld$@$2$:$1<@$2>$3 user@host.domain)')') - -# remaining names must be local -R$+ $#local$:$1 everything else diff --git a/usr.sbin/sendmail/cf/cf/python.mc b/usr.sbin/sendmail/cf/cf/python.mc new file mode 100644 index 0000000000..ac23e61313 --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/python.mc @@ -0,0 +1,52 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)python.mc 8.1 (Berkeley) 6/7/93') +OSTYPE(bsd4.4)dnl +DOMAIN(cs.exposed)dnl +define(`LOCAL_RELAY', vangogh.CS.Berkeley.EDU)dnl +define(`MASQUERADE_NAME', vangogh.CS.Berkeley.EDU)dnl +MAILER(local)dnl +MAILER(smtp)dnl + +# accept mail sent to the domain head +DDBostic.COM + +LOCAL_RULE_0 +# accept mail sent to the domain head +R< @ $D . > : $* $@ $>7 $1 @here:... -> ... +R$* $=O $* < @ $D . > $@ $>7 $1 $2 $3 ...@here -> ... +R$* < @ $D . > $#local $: $1 user@here -> user diff --git a/usr.sbin/sendmail/cf/cf/s2k.mc b/usr.sbin/sendmail/cf/cf/s2k.mc new file mode 100644 index 0000000000..e65fc9f54f --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/s2k.mc @@ -0,0 +1,42 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)s2k.mc 8.1 (Berkeley) 6/7/93') +OLDSENDMAIL +OSTYPE(ultrix4.1)dnl +DOMAIN(s2k)dnl +MAILER(local)dnl +MAILER(smtp)dnl diff --git a/usr.sbin/sendmail/cf/cf/sun-lamp.mc b/usr.sbin/sendmail/cf/cf/sun-lamp.mc new file mode 100644 index 0000000000..9a722dada0 --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/sun-lamp.mc @@ -0,0 +1,49 @@ +divert(-1) +# +# Copyright (c) 1993 Adam Glass +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988 The Regents of the University of California. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)sun-lamp.mc $Revision: 1.1 $') +OSTYPE(bsd4.4)dnl +MAILER(local)dnl +MAILER(smtp)dnl +define(`UUCP_RELAY', ucbvax.Berkeley.EDU)dnl +define(`BITNET_RELAY', mailhost.Berkeley.EDU)dnl +define(`CSNET_RELAY', mailhost.Berkeley.EDU)dnl +define(`confCHECKPOINT_INTERVAL', 4)dnl +define(`confAUTO_REBUILD', True)dnl +Cwlamp + + diff --git a/usr.sbin/sendmail/cf/cf/sunos3.5-cs-exposed.mc b/usr.sbin/sendmail/cf/cf/sunos3.5-cs-exposed.mc new file mode 100644 index 0000000000..46d04d96e2 --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/sunos3.5-cs-exposed.mc @@ -0,0 +1,41 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)sunos3.5-cs-exposed.mc 8.1 (Berkeley) 6/7/93') +OSTYPE(sunos3.5)dnl +DOMAIN(cs.exposed)dnl +MAILER(local)dnl +MAILER(smtp)dnl diff --git a/usr.sbin/sendmail/cf/cf/sunos3.5-cs-hidden.mc b/usr.sbin/sendmail/cf/cf/sunos3.5-cs-hidden.mc new file mode 100644 index 0000000000..a3d6f20efb --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/sunos3.5-cs-hidden.mc @@ -0,0 +1,41 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)sunos3.5-cs-hidden.mc 8.1 (Berkeley) 6/7/93') +OSTYPE(sunos3.5)dnl +DOMAIN(cs.hidden)dnl +MAILER(local)dnl +MAILER(smtp)dnl diff --git a/usr.sbin/sendmail/cf/cf/sunos4.1-cs-exposed.mc b/usr.sbin/sendmail/cf/cf/sunos4.1-cs-exposed.mc new file mode 100644 index 0000000000..7c94ba5ab7 --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/sunos4.1-cs-exposed.mc @@ -0,0 +1,41 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)sunos4.1-cs-exposed.mc 8.1 (Berkeley) 6/7/93') +OSTYPE(sunos4.1)dnl +DOMAIN(cs.exposed)dnl +MAILER(local)dnl +MAILER(smtp)dnl diff --git a/usr.sbin/sendmail/cf/cf/sunos4.1-cs-hidden.mc b/usr.sbin/sendmail/cf/cf/sunos4.1-cs-hidden.mc new file mode 100644 index 0000000000..8e1dbb9684 --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/sunos4.1-cs-hidden.mc @@ -0,0 +1,41 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)sunos4.1-cs-hidden.mc 8.1 (Berkeley) 6/7/93') +OSTYPE(sunos4.1)dnl +DOMAIN(cs.hidden)dnl +MAILER(local)dnl +MAILER(smtp)dnl diff --git a/usr.sbin/sendmail/cf/cf/tcpproto.cf b/usr.sbin/sendmail/cf/cf/tcpproto.cf deleted file mode 100644 index ebe1238ae7..0000000000 --- a/usr.sbin/sendmail/cf/cf/tcpproto.cf +++ /dev/null @@ -1,517 +0,0 @@ -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)tcpproto.mc 1.2 (Berkeley) 1/24/89 -# @(#)proto.mc 1.20 (Berkeley) 1/25/89 -# -# built by phil on Wed Jan 25 11:48:18 PST 1989 -# in /usr/src/local/nettables/sendmail.cf/cf on monet.Berkeley.EDU -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -############################################################ -############################################################ - - -################## -# local info # -################## - -# file containing our internet aliases -Fw/usr/lib/sendmail.cw - - - -############################# -### Setup Information ### -############################# - - -###################### -# General Macros # -###################### - -# local domain name -DDYOUR_DOMAIN_GOES_HERE - - - -# UUCP relay host -DRYOUR_UUCP_RELAY_GOES_HERE - -# csnet relay host -DCYOUR_CSNET_RELAY_GOES_HERE - -# bitnet relay host -DBYOUR_BITNET_RELAY_GOES_HERE - -# my official hostname -Dj$w - - - -############### -# Classes # -############### - -# Internal ("fake") domains that we use in rewriting -CIUUCP BITNET CSNET - - - - -###################### -# Version Number # -###################### - -DZ1.34 - - -###################### -# Special macros # -###################### - -# my name -DnMAILER-DAEMON -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$Z ready at $b - -############### -# Options # -############### - -# location of alias file -OA/usr/lib/aliases -# wait up to ten minutes for alias file rebuild -Oa10 -# substitution for space (blank) characters -OB. -# (don't) connect to "expensive" mailers -#Oc -# default delivery mode (deliver in background) -Odbackground -# temporary file mode -OF0600 -# default GID -Og1 -# location of help file -OH/usr/lib/sendmail.hf -# log level -OL9 -# default network name -ONARPA -# default messages to old style -Oo -# queue directory -OQ/usr/spool/mqueue -# read timeout -- violates protocols -Or2h -# status file -OS/usr/lib/sendmail.st -# queue up everything before starting transmission -Os -# default timeout interval -OT3d -# time zone names (V6 only) -OtPST,PDT -# default UID -Ou1 -# wizard's password -OW* -# load average at which we just queue messages -Ox8 -# load average at which we refuse connections -OX12 - -########################### -# Message precedences # -########################### - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -##################### -# Trusted users # -##################### - -Troot -Tdaemon -Tuucp - -######################### -# Format of headers # -######################### - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$Z) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -# HPosted-Date: $a -# H?l?Received-Date: $b -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - - - -########################### -### Rewriting Rules ### -########################### - - -################################ -# Sender Field Pre-rewriting # -################################ -S1 -#R$*<$*>$* $1$2$3 defocus - -################################### -# Recipient Field Pre-rewriting # -################################### -S2 -#R$*<$*>$* $1$2$3 defocus - - - -################################# -# Final Output Post-rewriting # -################################# -S4 - -R@ $@ handle <> error addr - -# resolve numeric addresses to name if possible -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 lookup numeric internet addr - -# externalize local domain info -R$*<$+>$* $1$2$3 defocus -R@$+:@$+:$+ @$1,@$2:$3 <route-addr> canonical - -# UUCP must always be presented in old form -R$+@$-.UUCP $2!$1 u@h.UUCP => h!u - -# delete duplicate local names -R$+%$=w@$=w $1@$w u%host@host => u@host -R$+%$=w@$=w.$D $1@$w u%host@host => u@host - - -########################### -# Name Canonicalization # -########################### -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -- note RFC733 heuristic here -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 basic RFC821/822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+ @$1:$2 change all "," to ":" - -# localize and dispose of route-based addresses -R@$+:$+ $@$>6<@$1>:$2 handle <route-addr> - -# more miscellaneous cleanup -R$+ $:$>8$1 host dependent cleanup -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style addresses to a domain-based address -R$+^$+ $1!$2 convert ^ to ! -R$-!$+ $@$>6$2<@$1.UUCP> resolve uucp names -R$+.$-!$+ $@$>6$3<@$1.$2> domain uucps -R$+!$+ $@$>6$2<@$1.UUCP> uucp subdomains -R$+%$+ $:$>9$1%$2 user%host -R$+<@$+> $@$>6$1<@$2> already canonical -R$-.$+ $@$>6$2<@$1> host.user - - -################################# -# special local conversions # -################################# - -S6 -R$*<@$=w>$* $:$1<@$w>$3 get into u@$w form -R$*<@$=w.$D>$* $:$1<@$w>$3 -R$*<@$=U.UUCP>$* $:$1<@$w>$3 - -################################ -# Change rightmost % to @. # -################################ - -S9 -R$*%$* $1@$2 First make them all @'s. -R$*@$*@$* $1%$2@$3 Undo all but the last. -R$*@$* $@$1<@$2> Put back the brackets. - - - -################### -### Mailers ### -################### - - -############################################################ -############################################################ -##### -##### Local and Program Mailer specification -##### -############################################################ -############################################################ - -Mlocal, P=/bin/mail, F=rlsDFMmn, S=10, R=20, A=mail -d $u -Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u - -S10 -R@ $n errors to mailer-daemon - - -############################################################ -############################################################ -##### -##### Local Domain SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to remain -##### the local domain. Hence, they can refer to hosts that are -##### not registered in the NIC host table. -##### -############################################################ -############################################################ - -Mtcpld, P=[IPC], F=mDFMueXLC, S=17, R=27, A=IPC $h, E=\r\n - -S17 - -# cleanup forwarding a bit -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 canonicalize -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.bitnet -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S27 - -# cleanup -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### Internet SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to leave -##### the local domain -- hence, they must be canonical according to -##### RFC822 etc. This means that machines not registered with -##### the NIC must be hidden behind our Internet relay. -##### -############################################################ -############################################################ - -Mtcp, P=[IPC], F=mDFMueXLC, S=14, R=24, A=IPC $h, E=\r\n - -S14 - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output internal ("fake") domains as "user%host@relay" - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S24 - -# put in <> kludge -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere..... -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# Hide fake domains behind relays - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - - - -##################### -### Rule Zero ### -##################### - - -############################################################ -############################################################ -##### -##### RULESET ZERO PREAMBLE -##### -##### The beginning of ruleset zero is constant through all -##### configurations. -##### -############################################################ -############################################################ - -S0 - -# first make canonical -R$*<$*>$* $1$2$3 defocus -R$+ $:$>3$1 make canonical - -# handle special cases -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 numeric internet addr -R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec -R$+ $:$>6$1 -R$-<@$w> $#local$:$1 -R@ $#error$:Invalid address handle <> form - -# canonicalize using the nameserver if not internal domain -R$*<@$*.$~I>$* $:$1<@$[$2.$3$]>$4 -R$*<@$->$* $:$1<@$[$2$]>$3 -R$*<@$->$* $:$1<@$2.$D>$3 if nameserver fails - -# now delete the local info -R<@$w>:$* $@$>0$1 @here:... -> ... -R$*<@$w> $@$>0$1 ...@here -> ... - -################################## -# End of ruleset zero preamble # -################################## - - -############################################### -### Machine dependent part of Rule Zero ### -############################################### - - - - - -# resolve fake top level domains by forwarding to other hosts -R$*<@$+.BITNET>$* $#tcp$@$B$:$1<@$2.BITNET>$3 user@host.BITNET -R$*<@$+.CSNET>$* $#tcp$@$C$:$1<@$2.CSNET>$3 user@host.CSNET - - -# forward non-local UUCP traffic to our UUCP relay -R$*<@$*.UUCP>$* $#tcpld$@$R$:$1<@$2.UUCP> uucp mail - -# hide behind our internet relay when talking to people in the arpa domain -R$*<@$*.arpa>$* $#tcp$@$2.arpa$:$1<@$2.arpa>$3 user@host.arpa - -# but speak domains to them if they speak domains too -R$*<@$*>$* $#tcpld$@$2$:$1<@$2>$3 user@host.domain - -# remaining names must be local -R$+ $#local$:$1 everything else diff --git a/usr.sbin/sendmail/cf/cf/tcpproto.mc b/usr.sbin/sendmail/cf/cf/tcpproto.mc index 1b7bd403e2..7fcb65ab08 100644 --- a/usr.sbin/sendmail/cf/cf/tcpproto.mc +++ b/usr.sbin/sendmail/cf/cf/tcpproto.mc @@ -1,14 +1,40 @@ -divert(10)dnl +divert(-1) # -# Prototype configuration file for systems on TCP/IP (SMTP) based networks +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. # -# @(#)tcpproto.mc 1.2 (Berkeley) 1/24/89 +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. # -define(DOMAIN, `DDYOUR_DOMAIN_GOES_HERE') -define(UUCP_RELAY, DRYOUR_UUCP_RELAY_GOES_HERE) -define(BITNET_RELAY, `DBYOUR_BITNET_RELAY_GOES_HERE') -define(CSNET_RELAY, `DCYOUR_CSNET_RELAY_GOES_HERE') -define(ARPAKLUDGE, `1') -define(EXTERNAL_VERSION, ``#' `@(#)tcpproto.mc 1.2' (Berkeley) `1/24/89'') -divert(0)dnl -include(proto.mc)dnl +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)tcpproto.mc 8.1 (Berkeley) 6/7/93') +FEATURE(nouucp) +MAILER(local) +MAILER(smtp) diff --git a/usr.sbin/sendmail/cf/cf/tcpuucpproto.cf b/usr.sbin/sendmail/cf/cf/tcpuucpproto.cf deleted file mode 100644 index 173f1cf744..0000000000 --- a/usr.sbin/sendmail/cf/cf/tcpuucpproto.cf +++ /dev/null @@ -1,572 +0,0 @@ -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)tcpuucpproto.mc 1.2 (Berkeley) 1/24/89 -# @(#)proto.mc 1.20 (Berkeley) 1/25/89 -# -# built by phil on Wed Jan 25 11:48:29 PST 1989 -# in /usr/src/local/nettables/sendmail.cf/cf on monet.Berkeley.EDU -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -############################################################ -############################################################ - - -################## -# local info # -################## - -# file containing our internet aliases -Fw/usr/lib/sendmail.cw - -# uucp hostnames -DUYOUR_UUCP_NAME_GOES_HERE -CUYOUR_UUCP_ALIASES_GO_HERE - -# local UUCP connections -CV YOUR -CV UUCP -CV NEIGHBORS -CV GO -CV HERE - - -############################# -### Setup Information ### -############################# - - -###################### -# General Macros # -###################### - -# local domain name -DDYOUR_DOMAIN_GOES_HERE - - - - - -# csnet relay host -DCYOUR_CSNET_RELAY_GOES_HERE - -# bitnet relay host -DBYOUR_BITNET_RELAY_GOES_HERE - -# my official hostname -Dj$w - - - -############### -# Classes # -############### - -# Internal ("fake") domains that we use in rewriting -CIUUCP BITNET CSNET - - - - -###################### -# Version Number # -###################### - -DZ1.34 - - -###################### -# Special macros # -###################### - -# my name -DnMAILER-DAEMON -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$Z ready at $b - -############### -# Options # -############### - -# location of alias file -OA/usr/lib/aliases -# wait up to ten minutes for alias file rebuild -Oa10 -# substitution for space (blank) characters -OB. -# (don't) connect to "expensive" mailers -#Oc -# default delivery mode (deliver in background) -Odbackground -# temporary file mode -OF0600 -# default GID -Og1 -# location of help file -OH/usr/lib/sendmail.hf -# log level -OL9 -# default network name -ONARPA -# default messages to old style -Oo -# queue directory -OQ/usr/spool/mqueue -# read timeout -- violates protocols -Or2h -# status file -OS/usr/lib/sendmail.st -# queue up everything before starting transmission -Os -# default timeout interval -OT3d -# time zone names (V6 only) -OtPST,PDT -# default UID -Ou1 -# wizard's password -OW* -# load average at which we just queue messages -Ox8 -# load average at which we refuse connections -OX12 - -########################### -# Message precedences # -########################### - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -##################### -# Trusted users # -##################### - -Troot -Tdaemon -Tuucp - -######################### -# Format of headers # -######################### - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$Z) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -# HPosted-Date: $a -# H?l?Received-Date: $b -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - - - -########################### -### Rewriting Rules ### -########################### - - -################################ -# Sender Field Pre-rewriting # -################################ -S1 -#R$*<$*>$* $1$2$3 defocus - -################################### -# Recipient Field Pre-rewriting # -################################### -S2 -#R$*<$*>$* $1$2$3 defocus - - - -################################# -# Final Output Post-rewriting # -################################# -S4 - -R@ $@ handle <> error addr - -# resolve numeric addresses to name if possible -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 lookup numeric internet addr - -# externalize local domain info -R$*<$+>$* $1$2$3 defocus -R@$+:@$+:$+ @$1,@$2:$3 <route-addr> canonical - -# UUCP must always be presented in old form -R$+@$-.UUCP $2!$1 u@h.UUCP => h!u - -# delete duplicate local names -R$+%$=w@$=w $1@$w u%host@host => u@host -R$+%$=w@$=w.$D $1@$w u%host@host => u@host - - -########################### -# Name Canonicalization # -########################### -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -- note RFC733 heuristic here -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 basic RFC821/822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+ @$1:$2 change all "," to ":" - -# localize and dispose of route-based addresses -R@$+:$+ $@$>6<@$1>:$2 handle <route-addr> - -# more miscellaneous cleanup -R$+ $:$>8$1 host dependent cleanup -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style addresses to a domain-based address -R$+^$+ $1!$2 convert ^ to ! -R$-!$+ $@$>6$2<@$1.UUCP> resolve uucp names -R$+.$-!$+ $@$>6$3<@$1.$2> domain uucps -R$+!$+ $@$>6$2<@$1.UUCP> uucp subdomains -R$+%$+ $:$>9$1%$2 user%host -R$+<@$+> $@$>6$1<@$2> already canonical -R$-.$+ $@$>6$2<@$1> host.user - - -################################# -# special local conversions # -################################# - -S6 -R$*<@$=w>$* $:$1<@$w>$3 get into u@$w form -R$*<@$=w.$D>$* $:$1<@$w>$3 -R$*<@$=U.UUCP>$* $:$1<@$w>$3 - -################################ -# Change rightmost % to @. # -################################ - -S9 -R$*%$* $1@$2 First make them all @'s. -R$*@$*@$* $1%$2@$3 Undo all but the last. -R$*@$* $@$1<@$2> Put back the brackets. - - - -################### -### Mailers ### -################### - - -############################################################ -############################################################ -##### -##### Local and Program Mailer specification -##### -############################################################ -############################################################ - -Mlocal, P=/bin/mail, F=rlsDFMmn, S=10, R=20, A=mail -d $u -Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u - -S10 -R@ $n errors to mailer-daemon - - -############################################################ -############################################################ -##### -##### Local Domain SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to remain -##### the local domain. Hence, they can refer to hosts that are -##### not registered in the NIC host table. -##### -############################################################ -############################################################ - -Mtcpld, P=[IPC], F=mDFMueXLC, S=17, R=27, A=IPC $h, E=\r\n - -S17 - -# cleanup forwarding a bit -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 canonicalize -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.bitnet -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S27 - -# cleanup -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### Internet SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to leave -##### the local domain -- hence, they must be canonical according to -##### RFC822 etc. This means that machines not registered with -##### the NIC must be hidden behind our Internet relay. -##### -############################################################ -############################################################ - -Mtcp, P=[IPC], F=mDFMueXLC, S=14, R=24, A=IPC $h, E=\r\n - -S14 - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output internal ("fake") domains as "user%host@relay" - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S24 - -# put in <> kludge -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere..... -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# Hide fake domains behind relays - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### UUCP Mailer specification -##### -############################################################ -############################################################ - - -Muucp, P=/usr/bin/uux, F=DFMhuU, S=13, R=23, M=100000, - A=uux - -r -z -a$f -gC $h!rmail ($u) - -S13 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$+> $2!$1 uucpize (no @'s in addr) -R$w!$+ $1 strip local name -R$+ $:$U!$1 stick on our host name -R$=U!$-%$- $:$1!$2@$3.$D ucbvax!user@host.domain - -S23 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$w> $U!$1 a!b@here -> here!a!b -R$=U!$+ $2 here!a!b -> a!b -# sanity ... should not happen. -R$=U.$D!$+ $2 strip local name.domain - - -############################################################ -############################################################ -##### -##### Provide Backward Compatibility -##### -############################################################ -############################################################ - -##################################################### -# General code to convert back to old style names # -##################################################### -S5 - -R$+<@$w> $1 strip host -R$+<@$-.UUCP> $2!$1 u@host.UUCP => host!u - - - -##################### -### Rule Zero ### -##################### - - -############################################################ -############################################################ -##### -##### RULESET ZERO PREAMBLE -##### -##### The beginning of ruleset zero is constant through all -##### configurations. -##### -############################################################ -############################################################ - -S0 - -# first make canonical -R$*<$*>$* $1$2$3 defocus -R$+ $:$>3$1 make canonical - -# handle special cases -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 numeric internet addr -R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec -R$+ $:$>6$1 -R$-<@$w> $#local$:$1 -R@ $#error$:Invalid address handle <> form - -# canonicalize using the nameserver if not internal domain -R$*<@$*.$~I>$* $:$1<@$[$2.$3$]>$4 -R$*<@$->$* $:$1<@$[$2$]>$3 -R$*<@$->$* $:$1<@$2.$D>$3 if nameserver fails - -# now delete the local info -R<@$w>:$* $@$>0$1 @here:... -> ... -R$*<@$w> $@$>0$1 ...@here -> ... - -################################## -# End of ruleset zero preamble # -################################## - - -############################################### -### Machine dependent part of Rule Zero ### -############################################### - - - -# resolve local UUCP connections -R<@$=V.UUCP>:$+ $#uucp$@$1$:$2 @host.UUCP:... -R$+<@$=V.UUCP> $#uucp$@$2$:$1 user@host.UUCP - - -# resolve fake top level domains by forwarding to other hosts -R$*<@$+.BITNET>$* $#tcp$@$B$:$1<@$2.BITNET>$3 user@host.BITNET -R$*<@$+.CSNET>$* $#tcp$@$C$:$1<@$2.CSNET>$3 user@host.CSNET - - - - -# resolve SMTP traffic -R$*<@$+>$* $#tcpld$@$2$:$1<@$2>$3 user@host.domain - -# remaining names must be local -R$+ $#local$:$1 everything else diff --git a/usr.sbin/sendmail/cf/cf/tcpuucpproto.mc b/usr.sbin/sendmail/cf/cf/tcpuucpproto.mc deleted file mode 100644 index e4d672064b..0000000000 --- a/usr.sbin/sendmail/cf/cf/tcpuucpproto.mc +++ /dev/null @@ -1,15 +0,0 @@ -divert(10)dnl -# -# Prototype configuration file for a UUCP hub on a TCP/IP (SMTP) based network -# -# @(#)tcpuucpproto.mc 1.2 (Berkeley) 1/24/89 -# -define(DOMAIN, `DDYOUR_DOMAIN_GOES_HERE') -define(BITNET_RELAY, `DBYOUR_BITNET_RELAY_GOES_HERE') -define(CSNET_RELAY, `DCYOUR_CSNET_RELAY_GOES_HERE') -define(UUCP_NAME, `DUYOUR_UUCP_NAME_GOES_HERE') -define(UUCP_ALIASES, CUYOUR_UUCP_ALIASES_GO_HERE) -define(UUCP_HOSTS_FILE, ../sitedep/uucp.proto.m4) -define(EXTERNAL_VERSION, ``#' `@(#)tcpuucpproto.mc 1.2' (Berkeley) `1/24/89'') -divert(0)dnl -include(proto.mc)dnl diff --git a/usr.sbin/sendmail/cf/cf/telemuse.cf b/usr.sbin/sendmail/cf/cf/telemuse.cf deleted file mode 100644 index bedd9d1b1a..0000000000 --- a/usr.sbin/sendmail/cf/cf/telemuse.cf +++ /dev/null @@ -1,649 +0,0 @@ -########################################################################### -########################################################################### -##### -##### SENDMAIL CONFIGURATION FILE FOR NON-GATEWAY MACHINES -##### -##### Use this configuration file only on machines that -##### are neither the Internet gateway nor the UUCP -##### gateway. There are separate configuration files -##### for these gateway functions. -##### -########################################################################### -########################################################################### - - - -########## -## -## Miscellaneous configuration. -## -## Define various macros and classes that define the local -## mail and network configuration. -## -########## - -# Official domain name. -DDTeleMuse.com - -# List of domain names that we are (at least sort of) in. $D isn't -# included here since it is multiple "words" long. It is handled -# separately when needed. -#CDarpa uucp -# Bogus Domains that we aren't in, but people keep sending us mail there. -# Would you believe we even get addresses like something.rice.csnet? Sigh... -#CDrice csnet csnet-east csnet-west east west mmdf - -# List of pseudo host names to treat as local (alone or in $D or $=D). -CLlocalhost loopback local - -# Name of Internet mail gateway to the outside world. -DGtelemuse.com -# Name of uucp mail gateway for local connections. -DVtelemuse.com - -# Name of UUCP routing gateway. -#DWseismo.css.gov - -# UUCP hostname. -DUtelemuse - -# Non-Internet pseudo domain names. These will not be canonicalized. -CNUUCP CSNET BITNET MAILNET MFENET CSN CHUNET JUNET COSAC SPAN - - - -########## -## -## Critical macro definitions. -## -## These macros are used to import information from the -## configuration file into sendmail. -## -########## - -# SMTP daemon name. -DnMAILER-DAEMON -# UNIX header format. -DlFrom $g $d -# Delimiter (operator) characters. -Do.:%@!^=/[] -# Format of a total name. -Dq$?x$x <$g>$|$g$. -#Dq$g$?x ($x)$. -# My official hostname ($w is already official). -Dj$w -# SMTP login message. -De$j SMTP open for business at $b. - -########## -## -## Option definitions -## -## These define the default values for various sendmail options. -## These can be overridden on the senmail command line with -## the "-o" flag. -## -########## - -# Location of alias file. -OA/etc/aliases -# Wait up to ten minutes for alias file rebuild. -Oa10 -# Default delivery mode (deliver in background). -Odbackground -# Don't connect to "expensive" mailers. -Oc -# Temporary file mode. -OF0600 -# Location of help file. -OH/usr/share/misc/sendmail.hf -# log level -OL9 -# Default network name. ??? -ON$D -# Messages might be in old style. -Oo -# Queue directory. -OQ/var/spool/mqueue -# Read timeout -- violates protocols. -Or2h -# Status file. -OS/etc/sendmail.st -# Queue up everything before starting transmission. -Os -# Default timeout interval. -OT7d -# Default UID. -Ou1 -# Default GID. -Og1 -# Wizard's password. -OW* -# Load average at which we just queue messages. -Ox8 -# Load average at which we refuse connections. -OX12 - -########## -## -## Message precedences -## -########## - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -########## -## -## Trusted users -## -########## - -Troot -Tuucp -Tdaemon - -########## -## -## Format of headers -## -########## - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($i); $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - -########## -## -## Rewriting rules -## -## These are applied automatically by sendmail in the following order: -## -## +--> 0 --> 2 --> R --> 4 --> resolved address -## | -## | +--> 1 --> S --+ -## addr --> 3 ---> D --| |--> 4 --> message -## +--> 2 --> R --+ -## -## D - sender domaion addition -## S - mailer specific sender rewriting -## R - mailer specific recipient rewriting -## -########## - -########## -## -## Ruleset 0 -## -## Resolve addresses to specific mailer. -## -########## - -S0 - -# Focus on the next host name in the path. If we are already focused, -# though, just use it and don't do it again. Also, there's no need -# to try to focus on a single token, so optimize this out; these are -# just simple local addresses. -R$* $:<>$1 add a marker -R<>$*<@$*>$* $:$1<@$2>$3 focused, so unmark -R<>$- $:$1 single, so unmark -R<>$* $:$>3$1 focus - -# Handle special cases. -R@ $#local$:$n handle <> form - -# Remove our own host name if it's there and start over. -R$+<@$j> $@$>0$1 @thishost -R$+<@$w> $@$>0$1 @thishost -R$+<@$w.$=D> $@$>0$1 @thishost.local-domain -R$+<@$w.$=D.$=D> $@$>0$1 @thishost.local.local -R$+<@$w.$D> $@$>0$1 @thishost.local-domain -R$+<@$=w.$=D> $@$>0$1 @thishost.local-domain -R$+<@$=w.$=D.$=D> $@$>0$1 @thishost.local.local -R$+<@$=w.$D> $@$>0$1 @thishost.local-domain -R$+<@$=w> $@$>0$1 @thishost -R<@$j>:$+ $@$>0$1 @thishost -R<@$w>:$+ $@$>0$1 @thishost -R<@$w.$=D>:$+ $@$>0$2 @thishost.local-domain -R<@$w.$=D.$=D>:$+ $@$>0$3 @thishost.local.local -R<@$w.$D>:$+ $@$>0$1 @thishost.local-domain -R<@$=w.$=D>:$+ $@$>0$3 @thishost.local-domain -R<@$=w.$=D.$=D>:$+ $@$>0$4 @thishost.local.local -R<@$=w.$D>:$+ $@$>0$2 @thishost.local-domain -R<@$=w>:$+ $@$>0$2 @thishost - -# Treat the various forms of the names $=L as all local, too. -R$+<@$=L.$D> $@$>0$1 c@local -R$+<@$D> $@$>0$1 c@local -R$+<@$=L.$=D> $@$>0$1 c@local -R$+<@$=L.$=D.$=D> $@$>0$1 c@local -R$+<@$=L> $@$>0$1 c@local -R<@$=L.$D>:$+ $@$>0$2 @local:b@c -R<@$D>:$+ $@$>0$1 @local:b@c -R<@$=L.$=D>:$+ $@$>0$3 @local:b@c -R<@$=L.$=D.$=D>:$+ $@$>0$4 @local:b@c -R<@$=L>:$+ $@$>0$2 @local:b@c - -# For known local hosts, send directly there (both normal and route addrs). -# For compatibility with older mailers in the domain that don't accept -# the .$D form of their name, we use only the simple name without the .$D in -# the $: clause here. This shouldn't be a problem since these addresses -# don't leave the local domain. -R$*<@$-.$D>$* $#domain$@$2.$D$:$1<@$2>$3 - - -# Send all UUCP addresses to the UUCP gateway. -R$*<@$+.UUCP>$* $#domain$@$V$:$1<@$2.UUCP>$3 - - - -# Other non-local names will be passed off to the gateway. -R$*<@$*>$* $#domain$@$G$:$1<@$2>$3 user@somewhere - - -# Everything else is a local name. -R$+ $#local$:$1 local names - -########## -## -## Ruleset 1 -## -## Sender address pre-rewriting. -## -########## - -S1 - -########## -## -## Ruleset 2 -## -## Recipient address pre-rewriting. -## -## Since the "$:" part of what ruleset 0 returns is passed through -## rulesets 2, R, and 4, this rule MUST focus on the next host -## name in the path. We don't need (and don't want) all of the -## canonicalization of ruleset 3, though, so we just pass it through -## ruleset 9 only. -## -########## - -S2 - -# Focus on the next host name in the path. If we are already focused, -# though, just use it and don't do it again. Also, there's no need -# to try to focus on a single token, so optimize this out; these are -# just simple local addresses. -R$* $:<>$1 add a marker -R<>$*<@$*>$* $:$1<@$2>$3 focused, so unmark -R<>$- $:$1 single, so unmark -R<>$* $:$>9$1 focus - -########## -## -## Ruleset 3 -## -## Name canonicalization. -## -## This rule can not strip off @local-host-name since this is applied -## before the sender domain addition based on the 'C' mailer flag. -## Instead, this is deferred until ruleset 9, which is called at -## the beginning of rulesets 0, 1, and 2. -## -########## - -S3 - -# Handle "from:<>" special case. -R<> $@@ turn into magic token - -# Basic textual canonicalization -- note RFC733 heuristic here. -R<$*> $1 <> nested on outside -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 flush 822 comments -R$+ at $+ $1@$2 "at" -> "@" - -# Get rid of list-name addresses. ??? -#R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax - -# Make sure <@a,@b,@c:user@d> syntax is easy to parse. -# This is undone in ruleset 4 before it is seen outside of sendmail. -R@$+,@$+ @$1:@$2 change ",@" to ":@" - -# Focus on the next host name in the path. This places <> around the -# next @hostname. -R$+ $:$>9$1 focus on domain - -# Map old UUCP connections into Internet addresses. -R$*<@lbl-csam.UUCP>$* $:$1<@LBL-CSAM.ARPA>$2 -R$*<@ut-sally.UUCP>$* $:$1<@SALLY.UTEXAS.EDU>$2 -R$*<@ut-ngp.UUCP>$* $:$1<@NGP.UTEXAS.EDU>$2 -R$*<@cornell.UUCP>$* $:$1<@CU-ARPA.CS.CORNELL.EDU>$2 -R$*<@cmucsg.UUCP>$* $:$1<@G.CS.CMU.EDU>$2 - -# Handle UUCP name changes. -R$*<@parsec.UUCP>$* $:$1<@convex.UUCP>$2 -R$*<@drilltech.UUCP>$* $:$1<@drillsys.UUCP>$2 -R$*<@drilltec.UUCP>$* $:$1<@drillsys.UUCP>$2 -R$*<@drillte.UUCP>$* $:$1<@drillsys.UUCP>$2 - -# Translate a numeric Internet address back into a name if possible. -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 try it -R$*<@[$+]>$* $@$1<@[$2]>$3 couldn't get it - -# Canonicalize local host names that explicitly have our domain. -R$*<@$-.$D>$* $:$1<@$[$2.$D$]>$3 - -# Canonicalize local host names that don't explicitly have our domain. -# For names with no domain or those in the domains we are sort of in, -# try canonicalizing the name, but put back the old name if its not found. -R$*<@$->$* $:$1<@$2><$2>$3 hide old domain -R$*<@$-.>$* $:$1<@$2><$2.>$3 hide old domain -R$*<@$-.$=D>$* $:$1<@$2><$2.$3>$4 hide old domain -R$*<@$-.$=D.$=D>$* $:$1<@$2><$2.$3.$4>$5 hide old domain -R$*<@$-><$*>$* $:$1<@$[$2$]><$3>$4 map simple name -R$*<@$+.$D><$*>$* $:$1<@$2.$D>$4 keep result -R$*<@$*><$*>$* $:$1<@$3>$4 put old domain back - -# Map old-style pseudo domains to the new official names. -R$*<@$+.OZ>$* $:$1<@$2.OZ.AU>$3 Australia -R$*<@$+.DEC>$* $:$1<@$2.DEC.COM>$3 Digital Equipment Corp -R$*<@$+.ATT>$* $:$1<@$2.ATT.COM>$3 AT&T - -# Leave non Internet addresses alone now and just return them. -R$*<@$+.$=N>$* $@$1<@$2.$3>$4 - -# Try to canonicalize anything that's left. -R$*<@$+>$* $@$1<@$[$2$]>$3 try canonicalization - -########## -## -## Ruleset 4 -## -## Final output post-rewriting. -## -########## - -S4 - -R@ $@ handle <> error addr - -R$*<@$*.>$* $:$1<@$2>$3 remove trailing dot - -R$*<$*>$* $:$1$2$3 defocus - -# Turn addresses with mixed !'s and @'s into all !-form. -# Sigh.... removed since other mailers don't understand it.... ??? -#R$*!$*@$* $:$>6$1!$2@$3 mixed ! and @ -#R$*@$*!$* $:$>6$1@$2!$3 mixed @ and ! - -# Turn route address back into correct form. -R@$+:@$+ @$1,@$2 @a:@route => @a,@route - -########## -## -## Ruleset 6 (and rulesets 7 and 8) -## -## Convert address into UUCP !-style address. -## -## Ruleset 6 is called to do the conversion. This simply sets up the -## initial arguments for ruleset 7 to do recursion on. Ruleset 7 -## then recurses on itself for each element in the path, building up -## the resultant path in !-format one element at a time in the -## leading <>s. Ruleset 8 is used as a subroutine of ruleset 7 -## to focus on the next element in the path. This is just a copy -## of the domain focusing done in ruleset 3, but the initial -## path-so-far in <>s is allowed for. -## -########## - -# Set up for initial recursion by ruleset 7. Each new element of the -# path will be added to this "<>". -S6 -R$* $@$>7<>$1 set up for recursion - -# Recurse for each element of the path, translating it and adding it to -# the path-so-far in the leading "<>". -S7 -R<$*>$*<$*>$* $:<$1>$2$3$4 defocus -R<$*>$+ $:$>8<$1>$2 focus on next element -R<$*><@$+.UUCP>:$+ $@$>7<$1!$2>$3 move into result -R<$*><@$+>:$+ $@$>7<$1!$2>$3 move into result -R<$*>$+<@$+.UUCP> $@$>7<$1!$3>$2 move into result -R<$*>$+<@$+> $@$>7<$1!$3>$2 move into result -R<$*>$+ $:<$1!$2> get leftovers -R<!$+> $:<$1> remove leading ! -R<$+> $:$1 remove markers - -# Focus on the next element of the input path. -S8 - -# Localize and dispose of route-based addresses. -R<$*>@$+:$+ $@<$1><@$2>:$3 handle <route-addr> - -# Focus on the right-most domain in the address. -R<$*>$+@$+ $:<$1>$2<@$3> focus on domain -R<$*>$+<$+@$+> <$1>$2$3<@$4> move gaze right -R<$*>$+<$+%$+> <$1>$2$3<%$4> move gaze right -R<$*>$+<@$+> $@<$1>$2<@$3> already canonical -R<$*>$+<%$+> $@<$1>$2<@$3> already canonical -R<$*>$+%$+ $:<$1>$2<%$3> focus on domain -R<$*>$+<$+%$+> <$1>$2$3<%$4> move gaze right -R<$*>$+<%$+> $@<$1>$2<@$3> already canonical - -# Convert old-style UUCP addresses to domain-based addresses. -R<$*>$+^$+ <$1>$2!$3 convert ^ to ! -R<$*>$+!$+ $:<$1><$2>!$3 focus on path -R<$*><$+!$+>$+ <$1><$2>!$3$4 move gaze left -R<$*><$+.$*>!$+ $@<$1>$4<@$2.$3> x.y!b => b@x.y -R<$*><$+>!$+ $@<$1>$3<@$2.UUCP> a!b => b@a.UUCP - -# Convert old-style BITNET addresses to domain-based addresses. -R<$*>$-=$+ $@<$1>$3<@$2.BITNET> a=b => b@a.BITNET - -# Convert old-style Berknet-syntax addresses to domain-based addresses. -R<$*>$-:$+ $@<$1>$3<@$2> host:user - -# Anything else is a local address, so just leave it alone - -########## -## -## Ruleset 9 -## -## Focus on the next host name in the path. -## -########## - -S9 - -R$*<$*>$* $1$2$3 defocus - -# Localize and dispose of route-based addresses. -R@$+:$+ $@<@$1>:$2 handle <route-addr> - -# Focus on the right-most domain in the address. -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<$+%$+> $1$2<%$3> move gaze right -R$+<@$+> $@$1<@$2> already canonical -R$+<%$+> $@$1<@$2> already canonical -R$+%$+ $:$1<%$2> focus on domain -R$+<$+%$+> $1$2<%$3> move gaze right -R$+<%$+> $@$1<@$2> already canonical - -# Convert old-style UUCP addresses to domain-based addresses. -R$+^$+ $1!$2 convert ^ to ! -R$+!$+ $:<$1>!$2 focus on path -R<$+!$+>$+ <$1>!$2$3 move gaze left -R<$+.$*>!$+ $@$3<@$1.$2> x.y!b => b@x.y -R<$+>!$+ $@$2<@$1.UUCP> a!b => b@a.UUCP - -# Convert old-style BITNET addresses to domain-based addresses. -R$-=$+ $@$2<@$1.BITNET> a=b => b@a.BITNET - -# Convert old-style Berknet-syntax addresses to domain-based addresses. -R$-:$+ $@$2<@$1> host:user - -# Anything else is a local address, so just leave it alone. - -########## -## -## Local and program mailer specifications -## -########## - -# Mailer flags specified: -# l final delivery will be performed -# m accepts multiple recipients -# n do not insert Unix-style "From " line -# r mailer wants "-r" flag for sender -# s strip quote off before calling mailer -# D wants "Date:" header line -# F wants "From:" header line -# M wants "Message-id:" header line -# -Mlocal, P=/bin/mail, F=lmnrsDFM, S=10, R=20, A=mail -d $u - -# Mailer flags specified: -# e mailer is expensive -# l final delivery will be performed -# s strip quote off before calling mailer -# D wants "Date:" header line -# F wants "From:" header line -# M wants "Message-id:" header line -# -Mprog, P=/bin/sh, F=elsDFM, S=10, R=20, A=sh -c $u - -S10 -# Convert special <> token back into a name. -R@ $n errors from myself -# Make sure we have a host name on the address. ??? -#R$*<@$+>$* $@$1<@$2>$3 already ok -#R$+ $@$1<@$j> tack on our hostname - -S20 -# Convert special <> token back into a name. -R@ $n errors to myself -# Make sure we have a host name on the address. ??? -#R$*<@$+>$* $@$1<@$2>$3 already ok -#R$+ $@$1<@$j> tack on our hostname - -########## -## -## Intra-domain mailer specification -## -## This is used for all mail within our domain. -## -########## - -# Mailer flags specified: -# m accepts multiple recipients -# s strip quote off before calling mailer -# u preserve upper case in usernames -# C add sender domain when received from mailer -# D wants "Date:" header line -# F wants "From:" header line -# M wants "Message-id:" header line -# X use RFC821 hidden dot algorithm -# -Mdomain, P=[IPC], F=msuCDFMX, S=11, R=21, A=IPC $h, E=\r\n - -S11 -R$*<@$+>$* $@$1<@$2>$3 already ok -R$+ $@$1<@$j> tack on our hostname - -S21 -R$*<@$+>$* $@$1<@$2>$3 already ok -R$+ $@$1<@$j> tack on our hostname - -########## -## -## World mailer specification -## -## This is used for all mail to the outside world. -## -########## - -# Mailer flags specified: -# m accepts multiple recipients -# s strip quote off before calling mailer -# u preserve upper case in usernames -# C add sender domain when received from mailer -# D wants "Date:" header line -# F wants "From:" header line -# L limit line lengths from RFC821 -# M wants "Message-id:" header line -# X use RFC821 hidden dot algorithm -# -Mworld, P=[IPC], F=msuCDFLMX, S=12, R=22, A=IPC $h, E=\r\n - -S12 - -# Make it look like all mail to the outside world is from the gateway. -R$*<@$-.$D>$* $@$1<@$G>$3 known local names -R$*<@$D>$* $@$1<@$G>$2 known local names -R$*<@$=L>$* $@$1<@$G>$3 known local names - -R$*<@$+>$* $@$1<@$2>$3 already has hostname -R$+ $@$1<@$G> tack on gateway name - -S22 -R$*<@$+>$* $@$1<@$2>$3 already has hostname -R$+ $@$1<@$j> tack on our hostname - -########## -## -## UUCP mailer specification -## -########## - -# Mailer flags specified: -# h preserve upper case in hostnames -# s strip quote off before calling mailer -# u preserve upper case in usernames -# C add sender domain when received from mailer -# D wants "Date:" header line -# F wants "From:" header line -# M wants "Message-id:" header line -# U use ugly UUCP-style Unix-style "From " line -# -Muucp, P=/usr/bin/uux, F=hsuCDFMU, S=13, R=23, - A=uux - -r -n -a$g -gC $h!rmail ($u) - -S13 - -R$+ $:$>6$1 turn into all ! syntax - -# Make it look like all mail from local hosts is from the gateway. -R$+!$+ $:<$1>!$2 focus on path -R<$+!$+>$+ <$1>!$2$3 move gaze left -R<$-.$D>!$+ $@$U!$2 known local names -R<$D>!$+ $@$U!$1 known local names -R<$=L>!$+ $@$U!$2 known local names -R$*<$*>$* $:$1$2$3 defocus - -R$+ $@$U!$1 add our UUCP name - -S23 -R$+ $:$>6$1 turn into all !'s - -# Turn local host names into our UUCP name. This is the name that we -# are known by to the UUCP host that we are now sending to. -R$+!$+ $:<$1>!$2 focus on path -R<$+!$+>$+ <$1>!$2$3 move gaze left -R<$-.$D>!$+ $@$U!$2 known local names -R<$D>!$+ $@$U!$1 known local names -R<$=L>!$+ $@$U!$2 known local names -R$*<$*>$* $:$1$2$3 defocus diff --git a/usr.sbin/sendmail/cf/cf/ucbarpa.cf b/usr.sbin/sendmail/cf/cf/ucbarpa.cf deleted file mode 100644 index d5a41fc39b..0000000000 --- a/usr.sbin/sendmail/cf/cf/ucbarpa.cf +++ /dev/null @@ -1,607 +0,0 @@ -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)ucbarpa.mc 1.3 (Berkeley) 1/3/89 -# @(#)proto.mc 1.20 (Berkeley) 1/25/89 -# -# built by phil on Wed Jan 25 11:47:11 PST 1989 -# in /usr/src/local/nettables/sendmail.cf/cf on monet.Berkeley.EDU -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -############################################################ -############################################################ - - -################## -# local info # -################## - -# file containing our internet aliases -Fw/usr/lib/sendmail.cw - -# uucp hostnames -DUucbarpa -CUucbarpa - -# local UUCP connections -CV endotsew -CV fateman -CV franz -CV interlan -CV metron - - -############################# -### Setup Information ### -############################# - - -###################### -# General Macros # -###################### - -# local domain name -DDBerkeley.EDU - -# Internet relay host -- machines in our domain that are not -# registered with the NIC will be "hidden" behind this relay machine -# with the % kludge, although SMTP delivery will still be performed -# by the sending machine. Someday this will go away. -DAucbvax.Berkeley.EDU - -# UUCP relay host -DRucbvax.Berkeley.EDU - -# csnet relay host -DCrelay.cs.net - -# bitnet relay host -DBjade.Berkeley.EDU - -# my official hostname -Dj$w - - - -############### -# Classes # -############### - -# Internal ("fake") domains that we use in rewriting -CIUUCP BITNET CSNET - - - -############################################################ -############################################################ -##### -##### BERKELEY HOSTS REGISTERED WITH THE NIC -##### -############################################################ -############################################################ - - -CNbach ucbbach -CNbizet ucbbizet -CNcad ucbcad -CNdegas ucbdegas -CNeast ucbeast -CNernie ucbernie -CNesvax ucbesvax -CNjade ucbjade -CNji ucbji -CNmike ucbmike -CNmonet ucbmonet -CNpostgres -CNrenoir ucbrenoir -CNucbarpa -CNucbvax -CNviolet ucbviolet - - -###################### -# Version Number # -###################### - -DZ1.34 - - -###################### -# Special macros # -###################### - -# my name -DnMAILER-DAEMON -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$Z ready at $b - -############### -# Options # -############### - -# location of alias file -OA/usr/lib/aliases -# wait up to ten minutes for alias file rebuild -Oa10 -# substitution for space (blank) characters -OB. -# (don't) connect to "expensive" mailers -#Oc -# default delivery mode (deliver in background) -Odbackground -# temporary file mode -OF0600 -# default GID -Og1 -# location of help file -OH/usr/lib/sendmail.hf -# log level -OL9 -# default network name -ONARPA -# default messages to old style -Oo -# queue directory -OQ/usr/spool/mqueue -# read timeout -- violates protocols -Or2h -# status file -OS/usr/lib/sendmail.st -# queue up everything before starting transmission -Os -# default timeout interval -OT3d -# time zone names (V6 only) -OtPST,PDT -# default UID -Ou1 -# wizard's password -OW* -# load average at which we just queue messages -Ox8 -# load average at which we refuse connections -OX12 - -########################### -# Message precedences # -########################### - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -##################### -# Trusted users # -##################### - -Troot -Tdaemon -Tuucp - -######################### -# Format of headers # -######################### - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$Z) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -# HPosted-Date: $a -# H?l?Received-Date: $b -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - - - -########################### -### Rewriting Rules ### -########################### - - -################################ -# Sender Field Pre-rewriting # -################################ -S1 -#R$*<$*>$* $1$2$3 defocus - -################################### -# Recipient Field Pre-rewriting # -################################### -S2 -#R$*<$*>$* $1$2$3 defocus - - - -################################# -# Final Output Post-rewriting # -################################# -S4 - -R@ $@ handle <> error addr - -# resolve numeric addresses to name if possible -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 lookup numeric internet addr - -# externalize local domain info -R$*<$+>$* $1$2$3 defocus -R@$+:@$+:$+ @$1,@$2:$3 <route-addr> canonical - -# UUCP must always be presented in old form -R$+@$-.UUCP $2!$1 u@h.UUCP => h!u - -# delete duplicate local names -R$+%$=w@$=w $1@$w u%host@host => u@host -R$+%$=w@$=w.$D $1@$w u%host@host => u@host - - -########################### -# Name Canonicalization # -########################### -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -- note RFC733 heuristic here -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 basic RFC821/822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+ @$1:$2 change all "," to ":" - -# localize and dispose of route-based addresses -R@$+:$+ $@$>6<@$1>:$2 handle <route-addr> - -# more miscellaneous cleanup -R$+ $:$>8$1 host dependent cleanup -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style addresses to a domain-based address -R$+^$+ $1!$2 convert ^ to ! -R$-!$+ $@$>6$2<@$1.UUCP> resolve uucp names -R$+.$-!$+ $@$>6$3<@$1.$2> domain uucps -R$+!$+ $@$>6$2<@$1.UUCP> uucp subdomains -R$+%$+ $:$>9$1%$2 user%host -R$+<@$+> $@$>6$1<@$2> already canonical -R$-.$+ $@$>6$2<@$1> host.user - - -################################# -# special local conversions # -################################# - -S6 -R$*<@$=w>$* $:$1<@$w>$3 get into u@$w form -R$*<@$=w.$D>$* $:$1<@$w>$3 -R$*<@$=U.UUCP>$* $:$1<@$w>$3 - -################################ -# Change rightmost % to @. # -################################ - -S9 -R$*%$* $1@$2 First make them all @'s. -R$*@$*@$* $1%$2@$3 Undo all but the last. -R$*@$* $@$1<@$2> Put back the brackets. - - - -################### -### Mailers ### -################### - - -############################################################ -############################################################ -##### -##### Local and Program Mailer specification -##### -############################################################ -############################################################ - -Mlocal, P=/bin/mail, F=rlsDFMmn, S=10, R=20, A=mail -d $u -Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u - -S10 -R@ $n errors to mailer-daemon - - -############################################################ -############################################################ -##### -##### Local Domain SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to remain -##### the local domain. Hence, they can refer to hosts that are -##### not registered in the NIC host table. -##### -############################################################ -############################################################ - -Mtcpld, P=[IPC], F=mDFMueXLC, S=17, R=27, A=IPC $h, E=\r\n - -S17 - -# cleanup forwarding a bit -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 canonicalize -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.bitnet -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S27 - -# cleanup -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### Internet SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to leave -##### the local domain -- hence, they must be canonical according to -##### RFC822 etc. This means that machines not registered with -##### the NIC must be hidden behind our Internet relay. -##### -############################################################ -############################################################ - -Mtcp, P=[IPC], F=mDFMueXLC, S=14, R=24, A=IPC $h, E=\r\n - -S14 - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output internal ("fake") domains as "user%host@relay" - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S24 - -# put in <> kludge -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere..... -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# Hide fake domains behind relays - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### UUCP Mailer specification -##### -############################################################ -############################################################ - - -Muucp, P=/usr/bin/uux, F=DFMhuU, S=13, R=23, M=100000, - A=uux - -r -z -a$f -gC $h!rmail ($u) - -S13 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$+> $2!$1 uucpize (no @'s in addr) -R$w!$+ $1 strip local name -R$+ $:$U!$1 stick on our host name -R$=U!$-%$- $:$1!$2@$3.$D ucbvax!user@host.domain - -S23 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$w> $U!$1 a!b@here -> here!a!b -R$=U!$+ $2 here!a!b -> a!b -# sanity ... should not happen. -R$=U.$D!$+ $2 strip local name.domain - - -############################################################ -############################################################ -##### -##### Provide Backward Compatibility -##### -############################################################ -############################################################ - -##################################################### -# General code to convert back to old style names # -##################################################### -S5 - -R$+<@$w> $1 strip host -R$+<@$-.UUCP> $2!$1 u@host.UUCP => host!u - - - -##################### -### Rule Zero ### -##################### - - -############################################################ -############################################################ -##### -##### RULESET ZERO PREAMBLE -##### -##### The beginning of ruleset zero is constant through all -##### configurations. -##### -############################################################ -############################################################ - -S0 - -# first make canonical -R$*<$*>$* $1$2$3 defocus -R$+ $:$>3$1 make canonical - -# handle special cases -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 numeric internet addr -R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec -R$+ $:$>6$1 -R$-<@$w> $#local$:$1 -R@ $#error$:Invalid address handle <> form - -# canonicalize using the nameserver if not internal domain -R$*<@$*.$~I>$* $:$1<@$[$2.$3$]>$4 -R$*<@$->$* $:$1<@$[$2$]>$3 -R$*<@$->$* $:$1<@$2.$D>$3 if nameserver fails - -# now delete the local info -R<@$w>:$* $@$>0$1 @here:... -> ... -R$*<@$w> $@$>0$1 ...@here -> ... - -################################## -# End of ruleset zero preamble # -################################## - - -############################################### -### Machine dependent part of Rule Zero ### -############################################### - - - -# resolve local UUCP connections -R<@$=V.UUCP>:$+ $#uucp$@$1$:$2 @host.UUCP:... -R$+<@$=V.UUCP> $#uucp$@$2$:$1 user@host.UUCP - - -# resolve fake top level domains by forwarding to other hosts -R$*<@$+.BITNET>$* $#tcp$@$B$:$1<@$2.BITNET>$3 user@host.BITNET -R$*<@$+.CSNET>$* $#tcp$@$C$:$1<@$2.CSNET>$3 user@host.CSNET - - -# forward non-local UUCP traffic to our UUCP relay -R$*<@$*.UUCP>$* $#tcpld$@$R$:$1<@$2.UUCP> uucp mail - -# hide behind our internet relay when talking to people in the arpa domain -R$*<@$*.arpa>$* $#tcp$@$2.arpa$:$1<@$2.arpa>$3 user@host.arpa - -# but speak domains to them if they speak domains too -R$*<@$*>$* $#tcpld$@$2$:$1<@$2>$3 user@host.domain - -# remaining names must be local -R$+ $#local$:$1 everything else diff --git a/usr.sbin/sendmail/cf/cf/ucbarpa.mc b/usr.sbin/sendmail/cf/cf/ucbarpa.mc index c1d0aa828b..21f35fdace 100644 --- a/usr.sbin/sendmail/cf/cf/ucbarpa.mc +++ b/usr.sbin/sendmail/cf/cf/ucbarpa.mc @@ -1,18 +1,43 @@ -divert(10)dnl +divert(-1) # -# ucbarpa configuration file +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. # -# @(#)ucbarpa.mc 1.3 (Berkeley) 1/3/89 +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. # -define(DOMAIN, `DDBerkeley.EDU') -define(UUCP_NAME, DUucbarpa) -define(UUCP_ALIASES, CUucbarpa) -define(UUCP_HOSTS_FILE, ../sitedep/uucp.ucbarpa.m4) -define(INTERNET_RELAY, `DAucbvax.Berkeley.EDU') -define(UUCP_RELAY, DRucbvax.Berkeley.EDU) -define(BITNET_RELAY, `DBjade.Berkeley.EDU') -define(CSNET_RELAY, `DCrelay.cs.net') -define(ARPAKLUDGE, `1') -define(EXTERNAL_VERSION, ``#' `@(#)ucbarpa.mc 1.3' (Berkeley) `1/3/89'') -divert(0)dnl -include(proto.mc)dnl +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)ucbarpa.mc 8.1 (Berkeley) 6/7/93') +DOMAIN(cs.exposed)dnl +OSTYPE(bsd4.4)dnl +MAILER(local)dnl +MAILER(smtp)dnl +MAILER(uucp)dnl +SITECONFIG(uucp.ucbarpa, ucbarpa, U) diff --git a/usr.sbin/sendmail/cf/cf/ucbtcp.cf b/usr.sbin/sendmail/cf/cf/ucbtcp.cf deleted file mode 100644 index a6d6cb097c..0000000000 --- a/usr.sbin/sendmail/cf/cf/ucbtcp.cf +++ /dev/null @@ -1,547 +0,0 @@ -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)ucbtcp.mc 1.3 (Berkeley) 1/3/89 -# @(#)proto.mc 1.20 (Berkeley) 1/25/89 -# -# built by phil on Wed Jan 25 11:47:55 PST 1989 -# in /usr/src/local/nettables/sendmail.cf/cf on monet.Berkeley.EDU -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -############################################################ -############################################################ - - -################## -# local info # -################## - -# file containing our internet aliases -Fw/usr/lib/sendmail.cw - - - -############################# -### Setup Information ### -############################# - - -###################### -# General Macros # -###################### - -# local domain name -DDBerkeley.EDU - -# Internet relay host -- machines in our domain that are not -# registered with the NIC will be "hidden" behind this relay machine -# with the % kludge, although SMTP delivery will still be performed -# by the sending machine. Someday this will go away. -DAucbvax.Berkeley.EDU - -# UUCP relay host -DRucbvax.Berkeley.EDU - -# csnet relay host -DCrelay.cs.net - -# bitnet relay host -DBjade.Berkeley.EDU - -# my official hostname -Dj$w - - - -############### -# Classes # -############### - -# Internal ("fake") domains that we use in rewriting -CIUUCP BITNET CSNET - - - -############################################################ -############################################################ -##### -##### BERKELEY HOSTS REGISTERED WITH THE NIC -##### -############################################################ -############################################################ - - -CNbach ucbbach -CNbizet ucbbizet -CNcad ucbcad -CNdegas ucbdegas -CNeast ucbeast -CNernie ucbernie -CNesvax ucbesvax -CNjade ucbjade -CNji ucbji -CNmike ucbmike -CNmonet ucbmonet -CNpostgres -CNrenoir ucbrenoir -CNucbarpa -CNucbvax -CNviolet ucbviolet - - -###################### -# Version Number # -###################### - -DZ1.34 - - -###################### -# Special macros # -###################### - -# my name -DnMAILER-DAEMON -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$Z ready at $b - -############### -# Options # -############### - -# location of alias file -OA/usr/lib/aliases -# wait up to ten minutes for alias file rebuild -Oa10 -# substitution for space (blank) characters -OB. -# (don't) connect to "expensive" mailers -#Oc -# default delivery mode (deliver in background) -Odbackground -# temporary file mode -OF0600 -# default GID -Og1 -# location of help file -OH/usr/lib/sendmail.hf -# log level -OL9 -# default network name -ONARPA -# default messages to old style -Oo -# queue directory -OQ/usr/spool/mqueue -# read timeout -- violates protocols -Or2h -# status file -OS/usr/lib/sendmail.st -# queue up everything before starting transmission -Os -# default timeout interval -OT3d -# time zone names (V6 only) -OtPST,PDT -# default UID -Ou1 -# wizard's password -OW* -# load average at which we just queue messages -Ox8 -# load average at which we refuse connections -OX12 - -########################### -# Message precedences # -########################### - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -##################### -# Trusted users # -##################### - -Troot -Tdaemon -Tuucp - -######################### -# Format of headers # -######################### - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$Z) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -# HPosted-Date: $a -# H?l?Received-Date: $b -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - - - -########################### -### Rewriting Rules ### -########################### - - -################################ -# Sender Field Pre-rewriting # -################################ -S1 -#R$*<$*>$* $1$2$3 defocus - -################################### -# Recipient Field Pre-rewriting # -################################### -S2 -#R$*<$*>$* $1$2$3 defocus - - - -################################# -# Final Output Post-rewriting # -################################# -S4 - -R@ $@ handle <> error addr - -# resolve numeric addresses to name if possible -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 lookup numeric internet addr - -# externalize local domain info -R$*<$+>$* $1$2$3 defocus -R@$+:@$+:$+ @$1,@$2:$3 <route-addr> canonical - -# UUCP must always be presented in old form -R$+@$-.UUCP $2!$1 u@h.UUCP => h!u - -# delete duplicate local names -R$+%$=w@$=w $1@$w u%host@host => u@host -R$+%$=w@$=w.$D $1@$w u%host@host => u@host - - -########################### -# Name Canonicalization # -########################### -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -- note RFC733 heuristic here -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 basic RFC821/822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+ @$1:$2 change all "," to ":" - -# localize and dispose of route-based addresses -R@$+:$+ $@$>6<@$1>:$2 handle <route-addr> - -# more miscellaneous cleanup -R$+ $:$>8$1 host dependent cleanup -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style addresses to a domain-based address -R$+^$+ $1!$2 convert ^ to ! -R$-!$+ $@$>6$2<@$1.UUCP> resolve uucp names -R$+.$-!$+ $@$>6$3<@$1.$2> domain uucps -R$+!$+ $@$>6$2<@$1.UUCP> uucp subdomains -R$+%$+ $:$>9$1%$2 user%host -R$+<@$+> $@$>6$1<@$2> already canonical -R$-.$+ $@$>6$2<@$1> host.user - - -################################# -# special local conversions # -################################# - -S6 -R$*<@$=w>$* $:$1<@$w>$3 get into u@$w form -R$*<@$=w.$D>$* $:$1<@$w>$3 -R$*<@$=U.UUCP>$* $:$1<@$w>$3 - -################################ -# Change rightmost % to @. # -################################ - -S9 -R$*%$* $1@$2 First make them all @'s. -R$*@$*@$* $1%$2@$3 Undo all but the last. -R$*@$* $@$1<@$2> Put back the brackets. - - - -################### -### Mailers ### -################### - - -############################################################ -############################################################ -##### -##### Local and Program Mailer specification -##### -############################################################ -############################################################ - -Mlocal, P=/bin/mail, F=rlsDFMmn, S=10, R=20, A=mail -d $u -Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u - -S10 -R@ $n errors to mailer-daemon - - -############################################################ -############################################################ -##### -##### Local Domain SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to remain -##### the local domain. Hence, they can refer to hosts that are -##### not registered in the NIC host table. -##### -############################################################ -############################################################ - -Mtcpld, P=[IPC], F=mDFMueXLC, S=17, R=27, A=IPC $h, E=\r\n - -S17 - -# cleanup forwarding a bit -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 canonicalize -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.bitnet -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S27 - -# cleanup -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### Internet SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to leave -##### the local domain -- hence, they must be canonical according to -##### RFC822 etc. This means that machines not registered with -##### the NIC must be hidden behind our Internet relay. -##### -############################################################ -############################################################ - -Mtcp, P=[IPC], F=mDFMueXLC, S=14, R=24, A=IPC $h, E=\r\n - -S14 - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output internal ("fake") domains as "user%host@relay" - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S24 - -# put in <> kludge -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere..... -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# Hide fake domains behind relays - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - - - -##################### -### Rule Zero ### -##################### - - -############################################################ -############################################################ -##### -##### RULESET ZERO PREAMBLE -##### -##### The beginning of ruleset zero is constant through all -##### configurations. -##### -############################################################ -############################################################ - -S0 - -# first make canonical -R$*<$*>$* $1$2$3 defocus -R$+ $:$>3$1 make canonical - -# handle special cases -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 numeric internet addr -R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec -R$+ $:$>6$1 -R$-<@$w> $#local$:$1 -R@ $#error$:Invalid address handle <> form - -# canonicalize using the nameserver if not internal domain -R$*<@$*.$~I>$* $:$1<@$[$2.$3$]>$4 -R$*<@$->$* $:$1<@$[$2$]>$3 -R$*<@$->$* $:$1<@$2.$D>$3 if nameserver fails - -# now delete the local info -R<@$w>:$* $@$>0$1 @here:... -> ... -R$*<@$w> $@$>0$1 ...@here -> ... - -################################## -# End of ruleset zero preamble # -################################## - - -############################################### -### Machine dependent part of Rule Zero ### -############################################### - - - - - -# resolve fake top level domains by forwarding to other hosts -R$*<@$+.BITNET>$* $#tcp$@$B$:$1<@$2.BITNET>$3 user@host.BITNET -R$*<@$+.CSNET>$* $#tcp$@$C$:$1<@$2.CSNET>$3 user@host.CSNET - - -# forward non-local UUCP traffic to our UUCP relay -R$*<@$*.UUCP>$* $#tcpld$@$R$:$1<@$2.UUCP> uucp mail - -# hide behind our internet relay when talking to people in the arpa domain -R$*<@$*.arpa>$* $#tcp$@$2.arpa$:$1<@$2.arpa>$3 user@host.arpa - -# but speak domains to them if they speak domains too -R$*<@$*>$* $#tcpld$@$2$:$1<@$2>$3 user@host.domain - -# remaining names must be local -R$+ $#local$:$1 everything else diff --git a/usr.sbin/sendmail/cf/cf/ucbtcp.mc b/usr.sbin/sendmail/cf/cf/ucbtcp.mc deleted file mode 100644 index c36aed86ca..0000000000 --- a/usr.sbin/sendmail/cf/cf/ucbtcp.mc +++ /dev/null @@ -1,15 +0,0 @@ -divert(10)dnl -# -# Generic configuration file for Berkeley machines. -# -# @(#)ucbtcp.mc 1.3 (Berkeley) 1/3/89 -# -define(DOMAIN, `DDBerkeley.EDU') -define(INTERNET_RELAY, `DAucbvax.Berkeley.EDU') -define(UUCP_RELAY, DRucbvax.Berkeley.EDU) -define(BITNET_RELAY, `DBjade.Berkeley.EDU') -define(CSNET_RELAY, `DCrelay.cs.net') -define(ARPAKLUDGE, `1') -define(EXTERNAL_VERSION, ``#' `@(#)ucbtcp.mc 1.3' (Berkeley) `1/3/89'') -divert(0)dnl -include(proto.mc)dnl diff --git a/usr.sbin/sendmail/cf/cf/ucbvax.cf b/usr.sbin/sendmail/cf/cf/ucbvax.cf deleted file mode 100644 index a1cbfb9a47..0000000000 --- a/usr.sbin/sendmail/cf/cf/ucbvax.cf +++ /dev/null @@ -1,837 +0,0 @@ -############################################################ -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)ucbvax.mc 1.39 (Berkeley) 1/3/89 -# -# built by phil on Wed Jan 25 11:47:20 PST 1989 -# in /usr/src/local/nettables/sendmail.cf/cf on monet.Berkeley.EDU -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -##### This one is the big daddy. There is no "upstairs" -##### to bounce a message to -- except perhaps the arpanet. -##### -##### -############################################################ -############################################################ - - - -###################### -### local info ### -###################### - -# internet hostnames -Cwucbvax vax k UCB-VAX Berkeley UCB-C70 UCB - -# UUCP hostnames -DUucbvax -CUucbvax - -# local UUCP connections -CV Padova -CV Shasta -CV alice -CV allegra -CV amdcad -CV anlams -CV att -CV attunix -CV avsd -CV bellcore bellcor -CV calma -CV cithep -CV craylab -CV decusj -CV decvax -CV decwrl -CV dssovax -CV eagle -CV ecovax -CV floyd -CV geoff -CV harpo -CV ho3e2 -CV hpda -CV hplabs -CV ibmsupt ibmuupa ibmpa -CV iiasa70 -CV imagen -CV isunix menlo70 -CV kentmth -CV kentvax -CV lbl-csam lbl-csa -CV lime -CV mebazf -CV molbio -CV mothra -CV mseonyx -CV mtxinu -CV nbires -CV pixar -CV pur-ee -CV purdue -CV pwbd -CV research researc -CV sdcarl -CV sdcsvax -CV sftig -CV sgi olympus -CV sii -CV srivisi -CV ssyx -CV sun -CV tektronix tektron -CV tolerant toleran -CV trwrb -CV twg -CV ucbcad -CV ucdavis -CV ucivax -CV ucla-se ucla-cs -CV ucsbcsl ucsbhub -CV ucscc -CV ucsd -CV ucsfcgl -CV ucsfmis -CV ulysses -CV unisoft -CV unmvax -CV usenix -CV uw -CV uwvax -CV vax135 -CV voder -CV wheps -CV whuxle -CV whuxlj -CV xprin -CV zehntel -CV zilog - -# UUCP connections on ucbarpa -DWucbarpa.Berkeley.EDU -CW endotsew -CW fateman -CW franz -CW interlan -CW metron - -# UUCP connections on ucbcad -DXcad.Berkeley.EDU -CX a -CX ames -CX analog -CX ardent dana -CX arthur -CX biosys -CX black -CX boulder -CX evans -CX harpo -CX harris -CX hpda -CX hpsrla -CX island -CX jupiter -CX masscomp masscom -CX nsc -CX oakhill -CX octal -CX sda -CX stellar -CX teklds -CX tektronix tektron -CX binky tonto -CX toshiba tsb0 -CX ucbcad -CX ucbvax -CX voder -CX vogon - -# UUCP connections on cogsci -DYcogsci.Berkeley.EDU -CY capmkt -CY contessa -CY emind -CY hoptoad -CY nkainc -CY well -CY ferdy - -# known uucp connections with a smart uucp -CMdecvax - -# we have full sendmail support here -Oa - -############################# -### Setup Information ### -############################# - - -###################### -# General Macros # -###################### - -# local domain name -# YOUR DOMAIN NAME GOES HERE! -DDYOUR_DOMAIN_NAME - - - - - - - - - -# my official hostname -Dj$w - - - -############### -# Classes # -############### - -# Internal ("fake") domains that we use in rewriting -CIUUCP BITNET CSNET - - - -############################################################ -############################################################ -##### -##### BERKELEY HOSTS REGISTERED WITH THE NIC -##### -############################################################ -############################################################ - - -CNbach ucbbach -CNbizet ucbbizet -CNcad ucbcad -CNdegas ucbdegas -CNeast ucbeast -CNernie ucbernie -CNesvax ucbesvax -CNjade ucbjade -CNji ucbji -CNmike ucbmike -CNmonet ucbmonet -CNpostgres -CNrenoir ucbrenoir -CNucbarpa -CNucbvax -CNviolet ucbviolet - - -###################### -# Version Number # -###################### - -DZ1.34 - - -###################### -# Special macros # -###################### - -# my name -DnMAILER-DAEMON -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$Z ready at $b - -############### -# Options # -############### - -# location of alias file -OA/usr/lib/aliases -# wait up to ten minutes for alias file rebuild -Oa10 -# substitution for space (blank) characters -OB. -# (don't) connect to "expensive" mailers -#Oc -# default delivery mode (deliver in background) -Odbackground -# temporary file mode -OF0600 -# default GID -Og1 -# location of help file -OH/usr/lib/sendmail.hf -# log level -OL9 -# default network name -ONARPA -# default messages to old style -Oo -# queue directory -OQ/usr/spool/mqueue -# read timeout -- violates protocols -Or2h -# status file -OS/usr/lib/sendmail.st -# queue up everything before starting transmission -Os -# default timeout interval -OT3d -# time zone names (V6 only) -OtPST,PDT -# default UID -Ou1 -# wizard's password -OW* -# load average at which we just queue messages -Ox8 -# load average at which we refuse connections -OX12 - -########################### -# Message precedences # -########################### - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -##################### -# Trusted users # -##################### - -Troot -Tdaemon -Tuucp - -######################### -# Format of headers # -######################### - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$Z) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -# HPosted-Date: $a -# H?l?Received-Date: $b -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - - - -########################### -### Rewriting Rules ### -########################### - - -################################ -# Sender Field Pre-rewriting # -################################ -S1 -#R$*<$*>$* $1$2$3 defocus - -################################### -# Recipient Field Pre-rewriting # -################################### -S2 -#R$*<$*>$* $1$2$3 defocus - - - -################################# -# Final Output Post-rewriting # -################################# -S4 - -R@ $@ handle <> error addr - -# resolve numeric addresses to name if possible -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 lookup numeric internet addr - -# externalize local domain info -R$*<$+>$* $1$2$3 defocus -R@$+:@$+:$+ @$1,@$2:$3 <route-addr> canonical - -# UUCP must always be presented in old form -R$+@$-.UUCP $2!$1 u@h.UUCP => h!u - -# delete duplicate local names -R$+%$=w@$=w $1@$w u%host@host => u@host -R$+%$=w@$=w.$D $1@$w u%host@host => u@host - - -# addition to Post-rewrite Rule -R$+%$=w@$=w.EDU $1@$w u%UCB@UCB.edu => u@UCB.berk.edu -R$+%$=w@$=w.$=w.EDU $1@$w u%UCB@UCB.berk.edu => u@UCB - - -########################### -# Name Canonicalization # -########################### -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -- note RFC733 heuristic here -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 basic RFC821/822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+ @$1:$2 change all "," to ":" - -# localize and dispose of route-based addresses -R@$+:$+ $@$>6<@$1>:$2 handle <route-addr> - -# more miscellaneous cleanup -R$+ $:$>8$1 host dependent cleanup -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style addresses to a domain-based address -R$+^$+ $1!$2 convert ^ to ! -R$-!$+ $@$>6$2<@$1.UUCP> resolve uucp names -R$+.$-!$+ $@$>6$3<@$1.$2> domain uucps -R$+!$+ $@$>6$2<@$1.UUCP> uucp subdomains -R$+%$+ $:$>9$1%$2 user%host -R$+<@$+> $@$>6$1<@$2> already canonical -R$-.$+ $@$>6$2<@$1> host.user - - -################################# -# special local conversions # -################################# - -S6 -R$*<@$=w>$* $:$1<@$w>$3 get into u@$w form -R$*<@$=w.$D>$* $:$1<@$w>$3 -R$*<@$=U.UUCP>$* $:$1<@$w>$3 - -################################ -# Change rightmost % to @. # -################################ - -S9 -R$*%$* $1@$2 First make them all @'s. -R$*@$*@$* $1%$2@$3 Undo all but the last. -R$*@$* $@$1<@$2> Put back the brackets. - - - -############################################################ -############################################################ -##### -##### Provide Backward Compatibility -##### -############################################################ -############################################################ - -##################################################### -# General code to convert back to old style names # -##################################################### -S5 - -R$+<@$w> $1 strip host -R$+<@$-.UUCP> $2!$1 u@host.UUCP => host!u - - -################### -### Mailers ### -################### - - -############################################################ -############################################################ -##### -##### Local and Program Mailer specification -##### -############################################################ -############################################################ - -Mlocal, P=/bin/mail, F=rlsDFMmn, S=10, R=20, A=mail -d $u -Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u - -S10 -R@ $n errors to mailer-daemon - - - -############################################################ -############################################################ -##### -##### UUCP Smart Mailer specification -##### (handles multiple recipients) -##### -############################################################ -############################################################ - - - -Msuucp, P=/usr/bin/uux, F=mDFMhuU, S=13, R=23, M=100000, - A=uux - -r $h!rmail ($u) - - - - -############################################################ -############################################################ -##### -##### UUCP Mailer specification -##### -############################################################ -############################################################ - - -Muucp, P=/usr/bin/uux, F=DFMhuU, S=13, R=23, M=100000, - A=uux - -r -z -a$f -gC $h!rmail ($u) - -S13 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$+> $2!$1 uucpize (no @'s in addr) -R$w!$+ $1 strip local name -R$+ $:$U!$1 stick on our host name -R$=U!$-%$- $:$1!$2@$3.$D ucbvax!user@host.domain - -S23 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$w> $U!$1 a!b@here -> here!a!b -R$=U!$+ $2 here!a!b -> a!b -# sanity ... should not happen. -R$=U.$D!$+ $2 strip local name.domain - - -############################################################ -############################################################ -##### -##### SMTP UUCP Mailer specification -##### -##### This mailer sends UUCP traffic over an SMTP connection. -##### Obviously, we only want to do this with UUCP hosts with -##### whom we have SMTP connectivity. The idea here is to -##### avoid having to double queue (once for sendmail, once -##### for UUCP) when there's no need. Since we need to -##### preserve uucp-ness (e.g., bangs), we use the UUCP mailer -##### rewriting rulesets. -##### -############################################################ -############################################################ - -Msmtpuucp, P=[IPC], F=mDFMueXLC, S=13, R=23, A=IPC $h, E=\r\n - - - - -############################################################ -############################################################ -##### -##### Internet SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to leave -##### the local domain -- hence, they must be canonical according to -##### RFC822 etc. This means that machines not registered with -##### the NIC must be hidden behind our Internet relay. -##### -############################################################ -############################################################ - -Mtcp, P=[IPC], F=mDFMueXLC, S=14, R=24, A=IPC $h, E=\r\n - -S14 - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output internal ("fake") domains as "user%host@relay" - - - -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S24 - -# put in <> kludge -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere..... -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# Hide fake domains behind relays - - - -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### Local Domain SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to remain -##### the local domain. Hence, they can refer to hosts that are -##### not registered in the NIC host table. -##### -############################################################ -############################################################ - -Mtcpld, P=[IPC], F=mDFMueXLC, S=17, R=27, A=IPC $h, E=\r\n - -S17 - -# cleanup forwarding a bit -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 canonicalize -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - - - -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S27 - -# cleanup -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - - - -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -##################### -### Rule Zero ### -##################### - - -############################################################ -############################################################ -##### -##### RULESET ZERO PREAMBLE -##### -##### The beginning of ruleset zero is constant through all -##### configurations. -##### -############################################################ -############################################################ - -S0 - -# first make canonical -R$*<$*>$* $1$2$3 defocus -R$+ $:$>3$1 make canonical - -# handle special cases -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 numeric internet addr -R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec -R$+ $:$>6$1 -R$-<@$w> $#local$:$1 -R@ $#error$:Invalid address handle <> form - -# canonicalize using the nameserver if not internal domain -R$*<@$*.$~I>$* $:$1<@$[$2.$3$]>$4 -R$*<@$->$* $:$1<@$[$2$]>$3 -R$*<@$->$* $:$1<@$2.$D>$3 if nameserver fails - -# now delete the local info -R<@$w>:$* $@$>0$1 @here:... -> ... -R$*<@$w> $@$>0$1 ...@here -> ... - -################################## -# End of ruleset zero preamble # -################################## - - -################################################ -### Machine dependent part of ruleset zero ### -################################################ - -# resolve SMTP UUCP connections - -R<@bellcore.uucp>:$+ $#smtpuucp$@bellcore.com$:$2 -R$*<@bellcore.uucp> $#smtpuucp$@bellcore.com$:$1 -R<@decvax.uucp>:$+ $#smtpuucp$@decvax.dec.com$:$2 -R$*<@decvax.uucp> $#smtpuucp$@decvax.dec.com$:$1 -R<@decwrl.uucp>:$+ $#smtpuucp$@decwrl.dec.com$:$2 -R$*<@decwrl.uucp> $#smtpuucp$@decwrl.dec.com$:$1 -R<@hplabs.uucp>:$+ $#smtpuucp$@hplabs.hp.com$:$2 -R$*<@hplabs.uucp> $#smtpuucp$@hplabs.hp.com$:$1 -R<@lbl-csam.uucp>:$+ $#smtpuucp$@lbl-csam.arpa$:$2 -R$*<@lbl-csam.uucp> $#smtpuucp$@lbl-csam.arpa$:$1 -R<@pur-ee.uucp>:$+ $#smtpuucp$@ecn.purdue.edu$:$2 -R$*<@pur-ee.uucp> $#smtpuucp$@ecn.purdue.edu$:$1 -R<@purdue.uucp>:$+ $#smtpuucp$@purdue.edu$:$2 -R$*<@purdue.uucp> $#smtpuucp$@purdue.edu$:$1 -R<@sdcarl.uucp>:$+ $#smtpuucp$@sdcarl.ucsd.edu$:$2 -R$*<@sdcarl.uucp> $#smtpuucp$@sdcarl.ucsd.edu$:$1 -R<@sun.uucp>:$+ $#smtpuucp$@sun.com$:$2 -R$*<@sun.uucp> $#smtpuucp$@sun.com$:$1 -R<@ucbcad.uucp>:$+ $#smtpuucp$@cad.berkeley.edu$:$2 -R$*<@ucbcad.uucp> $#smtpuucp$@cad.berkeley.edu$:$1 -R<@ucdavis.uucp>:$+ $#smtpuucp$@ucdavis.ucdavis.edu$:$2 -R$*<@ucdavis.uucp> $#smtpuucp$@ucdavis.ucdavis.edu$:$1 -R<@ucla-se.uucp>:$+ $#smtpuucp$@seas.ucla.edu$:$2 -R$*<@ucla-se.uucp> $#smtpuucp$@seas.ucla.edu$:$1 -R<@ucscc.uucp>:$+ $#smtpuucp$@c.ucsc.edu$:$2 -R$*<@ucscc.uucp> $#smtpuucp$@c.ucsc.edu$:$1 -R<@ucsfcgl.uucp>:$+ $#smtpuucp$@cgl.ucsf.edu$:$2 -R$*<@ucsfcgl.uucp> $#smtpuucp$@cgl.ucsf.edu$:$1 -R<@uwvax.uucp>:$+ $#smtpuucp$@spool.cs.wisc.edu$:$2 -R$*<@uwvax.uucp> $#smtpuucp$@spool.cs.wisc.edu$:$1 - - -# resolve local UUCP links -R<@$=V.UUCP>:$+ $#uucp$@$1$:$1:$2 @host.UUCP: ... -R$+<@$=V.UUCP> $#uucp$@$2$:$1 user@host.UUCP - -# resolve explicit arpanet names (to avoid with machine name "arpa" below) -R$*<@$*$-.ARPA>$* $#tcp$@$3.ARPA$:$1<@$2$3.ARPA>$4 user@domain.ARPA - -# resolve fake top level domains by forwarding to other hosts - - - - -# resolve non-local UUCP links -R$*<@$=W.UUCP>$* $#tcpld$@$W$:$1<@$2.UUCP>$3 user@host.UUCP -R$*<@$=X.UUCP>$* $#tcpld$@$X$:$1<@$2.UUCP>$3 user@host.UUCP -R$*<@$=Y.UUCP>$* $#tcpld$@$Y$:$1<@$2.UUCP>$3 user@host.UUCP - -# this uucp stuff is wrong for domain uucp addresses -# - we should pass the whole "host.domain" to uucp so it can -# find the best route. But that depends on a uucp router -# which doesn't exist here yet, so for now, we'll settle for -# trying to route to the domain (pretending its a host). -# Suitable L.sys entries can make this work. If it doesn't -# then returned mail will just say "dom unknown", which is true .. - -# resolve smart UUCP links -R<@$=M.$-.UUCP>:$+ $#suucp$@$2$:@$1.$2.UUCP:$3 @host.domain.UUCP: ... -R<@$=M.UUCP>:$+ $#suucp$@$1$:$2 @host.UUCP: ... -R$+<@$=M.$-.UUCP> $#suucp$@$3$:$1@$2.$3.UUCP user@host.domain.UUCP -R$+<@$=M.UUCP> $#suucp$@$2$:$1 user@host.UUCP - -# local domain sites -R$*<@$*.$D>$* $#tcpld$@$2.$D$:$1<@$2.$D>$3 user@host.our.domain -R$*<@$->$* $#tcpld$@$2.$D$:$1<@$2.$D>$3 user@host -R$*<@$-.UUCP>$* $#tcpld$@$2.$D$:$1<@$2.$D>$3 user@host.UUCP - -# other non-local names will be kicked upstairs -R$*<@$+>$* $#tcp$@$2$:$1<@$2>$3 user@some.where - -# remaining names must be local -R$+ $#local$:$1 everything else - -######################################## -### Host dependent address cleanup ### -######################################## - -S8 -R$*$=U!$+@$+ $3@$4 drop uucp forward diff --git a/usr.sbin/sendmail/cf/cf/ucbvax.mc b/usr.sbin/sendmail/cf/cf/ucbvax.mc index 3bd26a7d2b..bae55d577e 100644 --- a/usr.sbin/sendmail/cf/cf/ucbvax.mc +++ b/usr.sbin/sendmail/cf/cf/ucbvax.mc @@ -1,161 +1,101 @@ -############################################################ +divert(-1) # -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. # -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. # -# @(#)ucbvax.mc 1.39 (Berkeley) 1/3/89 +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. # -sinclude(buildinfo)dnl -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -##### This one is the big daddy. There is no "upstairs" -##### to bounce a message to -- except perhaps the arpanet. -##### -##### -############################################################ -############################################################ - - - -###################### -### local info ### -###################### - -# internet hostnames -Cwucbvax vax k UCB-VAX Berkeley UCB-C70 UCB - -# UUCP hostnames -DUucbvax -CUucbvax - -# local UUCP connections -include(../sitedep/uucp.ucbvax.m4)dnl - -# UUCP connections on ucbarpa -DWucbarpa.Berkeley.EDU -define(`CV', CW)dnl -include(../sitedep/uucp.ucbarpa.m4)dnl -undefine(`CV')dnl - -# UUCP connections on ucbcad -DXcad.Berkeley.EDU -define(`CV', CX)dnl -include(../sitedep/uucp.cad.m4)dnl -undefine(`CV')dnl - -# UUCP connections on cogsci -DYcogsci.Berkeley.EDU -define(`CV', CY)dnl -include(../sitedep/uucp.cogsci.m4)dnl -undefine(`CV')dnl - -# known uucp connections with a smart uucp -CMdecvax - -# we have full sendmail support here -Oa - -############################# -### Setup Information ### -############################# - -include(../m4/nsmacros.m4) -include(../m4/nsclasses.m4) -include(../sitedep/nicregistered.m4) -include(../m4/version.m4) -include(../m4/boilerplate.m4) - -########################### -### Rewriting Rules ### -########################### - -include(../m4/prewriterule.m4) -include(../m4/postwriterule.m4) - -# addition to Post-rewrite Rule -R$+%$=w@$=w.EDU $1@$w u%UCB@UCB.edu => u@UCB.berk.edu -R$+%$=w@$=w.$=w.EDU $1@$w u%UCB@UCB.berk.edu => u@UCB - -include(../m4/rule3.m4) -include(../m4/rule5.m4) - -################### -### Mailers ### -################### - -include(../m4/localm.m4) -define(`m4UUCP',TRUE) -include(../m4/suucpm.m4) -include(../m4/uucpm.m4) -include(../m4/smtpuucpm.m4) -include(../m4/nstcpm.m4) -include(../m4/nstcpldm.m4) - -##################### -### Rule Zero ### -##################### - -include(../m4/rule0.m4) - -################################################ -### Machine dependent part of ruleset zero ### -################################################ - -# resolve SMTP UUCP connections -include(../sitedep/smtpuucp.ucbvax.m4) - -# resolve local UUCP links -R<@$=V.UUCP>:$+ $#uucp$@$1$:$1:$2 @host.UUCP: ... -R$+<@$=V.UUCP> $#uucp$@$2$:$1 user@host.UUCP - -# resolve explicit arpanet names (to avoid with machine name "arpa" below) -R$*<@$*$-.ARPA>$* $#tcp$@$3.ARPA$:$1<@$2$3.ARPA>$4 user@domain.ARPA - -# resolve fake top level domains by forwarding to other hosts -include(../m4/fake_domains.m4) - -# resolve non-local UUCP links -R$*<@$=W.UUCP>$* $#tcpld$@$W$:$1<@$2.UUCP>$3 user@host.UUCP -R$*<@$=X.UUCP>$* $#tcpld$@$X$:$1<@$2.UUCP>$3 user@host.UUCP -R$*<@$=Y.UUCP>$* $#tcpld$@$Y$:$1<@$2.UUCP>$3 user@host.UUCP - -# this uucp stuff is wrong for domain uucp addresses -# - we should pass the whole "host.domain" to uucp so it can -# find the best route. But that depends on a uucp router -# which doesn't exist here yet, so for now, we'll settle for -# trying to route to the domain (pretending its a host). -# Suitable L.sys entries can make this work. If it doesn't -# then returned mail will just say "dom unknown", which is true .. - -# resolve smart UUCP links -R<@$=M.$-.UUCP>:$+ $#suucp$@$2$:@$1.$2.UUCP:$3 @host.domain.UUCP: ... -R<@$=M.UUCP>:$+ $#suucp$@$1$:$2 @host.UUCP: ... -R$+<@$=M.$-.UUCP> $#suucp$@$3$:$1@$2.$3.UUCP user@host.domain.UUCP -R$+<@$=M.UUCP> $#suucp$@$2$:$1 user@host.UUCP - -# local domain sites -R$*<@$*.$D>$* $#tcpld$@$2.$D$:$1<@$2.$D>$3 user@host.our.domain -R$*<@$->$* $#tcpld$@$2.$D$:$1<@$2.$D>$3 user@host -R$*<@$-.UUCP>$* $#tcpld$@$2.$D$:$1<@$2.$D>$3 user@host.UUCP - -# other non-local names will be kicked upstairs -R$*<@$+>$* $#tcp$@$2$:$1<@$2>$3 user@some.where - -# remaining names must be local -R$+ $#local$:$1 everything else - -######################################## -### Host dependent address cleanup ### -######################################## -S8 -R$*$=U!$+@$+ $3@$4 drop uucp forward +include(`../m4/cf.m4') +VERSIONID(`@(#)ucbvax.mc 8.1 (Berkeley) 6/7/93') +OSTYPE(bsd4.3) +DOMAIN(cs.hidden) +FEATURE(notsticky) +MAILER(local) +MAILER(smtp) +MAILER(uucp) +undefine(`UUCP_RELAY')dnl +DDBerkeley.EDU + +# names for which we act as a local forwarding agent +CF CS +FF/etc/sendmail.cw + +# local UUCP connections, and our local uucp name +SITECONFIG(uucp.ucbvax, ucbvax, U) + +# remote UUCP connections, and the machine they are on +SITECONFIG(uucp.ucbarpa, ucbarpa.Berkeley.EDU, W) + +SITECONFIG(uucp.cogsci, cogsci.Berkeley.EDU, X) + +LOCAL_RULE_3 +# map old UUCP names into Internet names +UUCPSMTP(bellcore, bellcore.com) +UUCPSMTP(decvax, decvax.dec.com) +UUCPSMTP(decwrl, decwrl.dec.com) +UUCPSMTP(hplabs, hplabs.hp.com) +UUCPSMTP(lbl-csam, lbl-csam.arpa) +UUCPSMTP(pur-ee, ecn.purdue.edu) +UUCPSMTP(purdue, purdue.edu) +UUCPSMTP(research, research.att.com) +UUCPSMTP(sdcarl, sdcarl.ucsd.edu) +UUCPSMTP(sdcsvax, sdcsvax.ucsd.edu) +UUCPSMTP(ssyx, ssyx.ucsc.edu) +UUCPSMTP(sun, sun.com) +UUCPSMTP(ucdavis, ucdavis.ucdavis.edu) +UUCPSMTP(ucivax, ics.uci.edu) +UUCPSMTP(ucla-cs, cs.ucla.edu) +UUCPSMTP(ucla-se, seas.ucla.edu) +UUCPSMTP(ucsbcsl, ucsbcsl.ucsb.edu) +UUCPSMTP(ucscc, c.ucsc.edu) +UUCPSMTP(ucsd, ucsd.edu) +UUCPSMTP(ucsfcgl, cgl.ucsf.edu) +UUCPSMTP(unmvax, unmvax.cs.unm.edu) +UUCPSMTP(uwvax, spool.cs.wisc.edu) + +LOCAL_RULE_0 + +# make sure we handle the local domain as absolute +R$* < @ $* $D > $* $: $1 < @ $2 $D . > $3 + +# handle names we forward for as though they were local, so we will use UDB +R< @ $=F . $D . > : $* $@ $>7 $2 @here:... -> ... +R< @ $D . > : $* $@ $>7 $1 @here:... -> ... +R$* $=O $* < @ $=F . $D . > $@ $>7 $1 $2 $3 ...@here -> ... +R$* $=O $* < @ $D . > $@ $>7 $1 $2 $3 ...@here -> ... + +R$* < @ $=F . $D . > $#local $: $1 use UDB + +# handle local UUCP connections in the Berkeley.EDU domain +R$+<@cnmat.$D . > $#uucp$@cnmat$:$1 +R$+<@cnmat.CS.$D . > $#uucp$@cnmat$:$1 +R$+<@craig.$D . > $#uucp$@craig$:$1 +R$+<@craig.CS.$D . > $#uucp$@craig$:$1 diff --git a/usr.sbin/sendmail/cf/cf/udb.mc b/usr.sbin/sendmail/cf/cf/udb.mc new file mode 100644 index 0000000000..624d2d4071 --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/udb.mc @@ -0,0 +1,41 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)udb.mc 8.1 (Berkeley) 6/7/93') +OSTYPE(sunos4.1)dnl +DOMAIN(cs.hidden)dnl +MAILER(smtp)dnl +define(`USERDB_FILE', `/home/auspex/a/staff/gnn/UDB/UI')dnl diff --git a/usr.sbin/sendmail/cf/cf/uid.cf b/usr.sbin/sendmail/cf/cf/uid.cf deleted file mode 100644 index 1c357d03e6..0000000000 --- a/usr.sbin/sendmail/cf/cf/uid.cf +++ /dev/null @@ -1,480 +0,0 @@ -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)uucpproto.mc 1.2 (Berkeley) 1/24/89 -# @(#)proto.mc 1.20 (Berkeley) 1/25/89 -# -# built by rich on Mon May 11 20:00:12 CDT 1992 -# in /home/rich/sendmail/idcf/cf on id -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -############################################################ -############################################################ - - -################## -# local info # -################## - -# file containing our internet aliases -Fw/etc/sendmail.cw - -# uucp hostnames -DUid -CUid uid - -# local UUCP connections -CVjpunix - - -############################# -### Setup Information ### -############################# - - -###################### -# General Macros # -###################### - -# local domain name -DDUUCP - - - - - - - - - -# my official hostname -Dj$w - - - -############### -# Classes # -############### - -# Internal ("fake") domains that we use in rewriting -CIUUCP BITNET CSNET - - - - -###################### -# Version Number # -###################### - -DZ1.34 - - -###################### -# Special macros # -###################### - -# my name -DnMAILER-DAEMON -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$Z ready at $b - -############### -# Options # -############### - -# location of alias file -OA/etc/aliases -# wait up to ten minutes for alias file rebuild -Oa10 -# substitution for space (blank) characters -OB. -# (don't) connect to "expensive" mailers -#Oc -# default delivery mode (deliver in background) -Odbackground -# temporary file mode -OF0600 -# default GID -Og1 -# location of help file -OH/usr/share/misc/sendmail.hf -# log level -OL9 -# default network name -ONARPA -# default messages to old style -Oo -# queue directory -OQ/var/spool/mqueue -# read timeout -- violates protocols -Or2h -# status file -OS/var/log/sendmail.st -# queue up everything before starting transmission -Os -# default timeout interval -OT3d -# time zone names (V6 only) -OtPST,PDT -# default UID -Ou1 -# wizard's password -OW* -# load average at which we just queue messages -Ox8 -# load average at which we refuse connections -OX12 - -########################### -# Message precedences # -########################### - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -##################### -# Trusted users # -##################### - -Troot -Tdaemon -Tuucp - -######################### -# Format of headers # -######################### - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$Z) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -# HPosted-Date: $a -# H?l?Received-Date: $b -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - - - -########################### -### Rewriting Rules ### -########################### - - -################################ -# Sender Field Pre-rewriting # -################################ -S1 -#R$*<$*>$* $1$2$3 defocus - -################################### -# Recipient Field Pre-rewriting # -################################### -S2 -#R$*<$*>$* $1$2$3 defocus - - - -################################# -# Final Output Post-rewriting # -################################# -S4 - -R@ $@ handle <> error addr - -# resolve numeric addresses to name if possible -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 lookup numeric internet addr - -# externalize local domain info -R$*<$+>$* $1$2$3 defocus -R@$+:@$+:$+ @$1,@$2:$3 <route-addr> canonical - -# UUCP must always be presented in old form -R$+@$-.UUCP $2!$1 u@h.UUCP => h!u - -# delete duplicate local names -R$+%$=w@$=w $1@$w u%host@host => u@host -R$+%$=w@$=w.$D $1@$w u%host@host => u@host - - -########################### -# Name Canonicalization # -########################### -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -- note RFC733 heuristic here -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 basic RFC821/822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+ @$1:$2 change all "," to ":" - -# localize and dispose of route-based addresses -R@$+:$+ $@$>6<@$1>:$2 handle <route-addr> - -# more miscellaneous cleanup -R$+ $:$>8$1 host dependent cleanup -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style addresses to a domain-based address -R$+^$+ $1!$2 convert ^ to ! -R$-!$+ $@$>6$2<@$1.UUCP> resolve uucp names -R$+.$-!$+ $@$>6$3<@$1.$2> domain uucps -R$+!$+ $@$>6$2<@$1.UUCP> uucp subdomains -R$+%$+ $:$>9$1%$2 user%host -R$+<@$+> $@$>6$1<@$2> already canonical -R$-.$+ $@$>6$2<@$1> host.user - - -################################# -# special local conversions # -################################# - -S6 -R$*<@$=w>$* $:$1<@$w>$3 get into u@$w form -R$*<@$=w.$D>$* $:$1<@$w>$3 -R$*<@$=U.UUCP>$* $:$1<@$w>$3 - -################################ -# Change rightmost % to @. # -################################ - -S9 -R$*%$* $1@$2 First make them all @'s. -R$*@$*@$* $1%$2@$3 Undo all but the last. -R$*@$* $@$1<@$2> Put back the brackets. - - - -################### -### Mailers ### -################### - - -############################################################ -############################################################ -##### -##### Local and Program Mailer specification -##### -############################################################ -############################################################ - -Mlocal, P=/usr/libexec/mail.local, F=lsDFMmn, S=10, R=20, A=mail -r $g -d $u -Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u - -S10 -R@ $n errors to mailer-daemon - - - -############################################################ -############################################################ -##### -##### Internet SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to leave -##### the local domain -- hence, they must be canonical according to -##### RFC822 etc. This means that machines not registered with -##### the NIC must be hidden behind our Internet relay. -##### -############################################################ -############################################################ - -Mtcp, P=[IPC], F=mDFMueXLC, S=14, R=24, A=IPC $h, E=\r\n - -S14 - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output internal ("fake") domains as "user%host@relay" - - - -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S24 - -# put in <> kludge -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere..... -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# Hide fake domains behind relays - - - -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - - -############################################################ -############################################################ -##### -##### Provide Backward Compatibility -##### -############################################################ -############################################################ - -##################################################### -# General code to convert back to old style names # -##################################################### -S5 - -R$+<@$w> $1 strip host -R$+<@$-.UUCP> $2!$1 u@host.UUCP => host!u - -############################################################ -############################################################ -##### -##### UUCP Mailer specification -##### -############################################################ -############################################################ - - -Muucp, P=/usr/local/bin/uux, F=DFMhuU, S=13, R=23, M=100000, - A=uux - -r -z -a$f -gC $h!rmail ($u) - -S13 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$+> $2!$1 uucpize (no @'s in addr) -R$w!$+ $1 strip local name -R$+ $:$U!$1 stick on our host name -R$=U!$-%$- $:$1!$2@$3.$D ucbvax!user@host.domain - -S23 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$w> $U!$1 a!b@here -> here!a!b -R$=U!$+ $2 here!a!b -> a!b -# sanity ... should not happen. -R$=U.$D!$+ $2 strip local name.domain - - - -##################### -### Rule Zero ### -##################### - - -############################################################ -############################################################ -##### -##### RULESET ZERO PREAMBLE -##### -##### The beginning of ruleset zero is constant through all -##### configurations. -##### -############################################################ -############################################################ - -S0 - -# first make canonical -R$*<$*>$* $1$2$3 defocus -R$+ $:$>3$1 make canonical - -# handle special cases -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 numeric internet addr -R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec -R$+ $:$>6$1 -R$-<@$w> $#local$:$1 -R@ $#error$:Invalid address handle <> form - -# canonicalize using the nameserver if not internal domain -R$*<@$*.$~I>$* $:$1<@$[$2.$3$]>$4 -R$*<@$->$* $:$1<@$[$2$]>$3 -R$*<@$->$* $:$1<@$2.$D>$3 if nameserver fails - -# now delete the local info -R<@$w>:$* $@$>0$1 @here:... -> ... -R$*<@$w> $@$>0$1 ...@here -> ... - -################################## -# End of ruleset zero preamble # -################################## - - -############################################### -### Machine dependent part of Rule Zero ### -############################################### - - - -# resolve local UUCP connections -R<@$=V.UUCP>:$+ $#uucp$@$1$:$2 @host.UUCP:... -R$+<@$=V.UUCP> $#uucp$@$2$:$1 user@host.UUCP - - - - -# remaining names must be local -R$+ $#local$:$1 everything else diff --git a/usr.sbin/sendmail/cf/cf/uid.mc b/usr.sbin/sendmail/cf/cf/uid.mc deleted file mode 100644 index 219e798ec9..0000000000 --- a/usr.sbin/sendmail/cf/cf/uid.mc +++ /dev/null @@ -1,14 +0,0 @@ -divert(10)dnl -# -# Prototype configuration file for systems only on UUCP networks -# -# @(#)uucpproto.mc 1.2 (Berkeley) 1/24/89 -# -define(DOMAIN, `DDUUCP') -define(UUCP_NAME, DUid) -define(UUCP_ALIASES, CUid uid) -define(UUCP_HOSTS_FILE, ../sitedep/uucp.proto.m4) -define(UUCP_ONLY, 1) -define(EXTERNAL_VERSION, ``#' `@(#)uucpproto.mc 1.2' (Berkeley) `1/24/89'') -divert(0)dnl -include(proto.mc)dnl diff --git a/usr.sbin/sendmail/cf/cf/ultrix4.1-cs-exposed.mc b/usr.sbin/sendmail/cf/cf/ultrix4.1-cs-exposed.mc new file mode 100644 index 0000000000..093590fff1 --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/ultrix4.1-cs-exposed.mc @@ -0,0 +1,41 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)ultrix4.1-cs-exposed.mc 8.1 (Berkeley) 6/7/93') +OSTYPE(ultrix4.1)dnl +DOMAIN(cs.exposed)dnl +MAILER(local)dnl +MAILER(smtp)dnl diff --git a/usr.sbin/sendmail/cf/cf/ultrix4.1-cs-hidden.mc b/usr.sbin/sendmail/cf/cf/ultrix4.1-cs-hidden.mc new file mode 100644 index 0000000000..ea25375924 --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/ultrix4.1-cs-hidden.mc @@ -0,0 +1,41 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)ultrix4.1-cs-hidden.mc 8.1 (Berkeley) 6/7/93') +OSTYPE(ultrix4.1)dnl +DOMAIN(cs.hidden)dnl +MAILER(local)dnl +MAILER(smtp)dnl diff --git a/usr.sbin/sendmail/cf/cf/uucpproto.cf b/usr.sbin/sendmail/cf/cf/uucpproto.cf deleted file mode 100644 index 3f90153846..0000000000 --- a/usr.sbin/sendmail/cf/cf/uucpproto.cf +++ /dev/null @@ -1,484 +0,0 @@ -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)uucpproto.mc 1.2 (Berkeley) 1/24/89 -# @(#)proto.mc 1.20 (Berkeley) 1/25/89 -# -# built by phil on Wed Jan 25 11:48:03 PST 1989 -# in /usr/src/local/nettables/sendmail.cf/cf on monet.Berkeley.EDU -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -############################################################ -############################################################ - - -################## -# local info # -################## - -# file containing our internet aliases -Fw/usr/lib/sendmail.cw - -# uucp hostnames -DUYOUR_UUCP_HOSTNAME_HERE -CUYOUR_UUCP_ALIASES_HERE - -# local UUCP connections -CV YOUR -CV UUCP -CV NEIGHBORS -CV GO -CV HERE - - -############################# -### Setup Information ### -############################# - - -###################### -# General Macros # -###################### - -# local domain name -DDUUCP - - - - - - - - - -# my official hostname -Dj$w - - - -############### -# Classes # -############### - -# Internal ("fake") domains that we use in rewriting -CIUUCP BITNET CSNET - - - - -###################### -# Version Number # -###################### - -DZ1.34 - - -###################### -# Special macros # -###################### - -# my name -DnMAILER-DAEMON -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$Z ready at $b - -############### -# Options # -############### - -# location of alias file -OA/usr/lib/aliases -# wait up to ten minutes for alias file rebuild -Oa10 -# substitution for space (blank) characters -OB. -# (don't) connect to "expensive" mailers -#Oc -# default delivery mode (deliver in background) -Odbackground -# temporary file mode -OF0600 -# default GID -Og1 -# location of help file -OH/usr/lib/sendmail.hf -# log level -OL9 -# default network name -ONARPA -# default messages to old style -Oo -# queue directory -OQ/usr/spool/mqueue -# read timeout -- violates protocols -Or2h -# status file -OS/usr/lib/sendmail.st -# queue up everything before starting transmission -Os -# default timeout interval -OT3d -# time zone names (V6 only) -OtPST,PDT -# default UID -Ou1 -# wizard's password -OW* -# load average at which we just queue messages -Ox8 -# load average at which we refuse connections -OX12 - -########################### -# Message precedences # -########################### - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -##################### -# Trusted users # -##################### - -Troot -Tdaemon -Tuucp - -######################### -# Format of headers # -######################### - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$Z) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -# HPosted-Date: $a -# H?l?Received-Date: $b -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - - - -########################### -### Rewriting Rules ### -########################### - - -################################ -# Sender Field Pre-rewriting # -################################ -S1 -#R$*<$*>$* $1$2$3 defocus - -################################### -# Recipient Field Pre-rewriting # -################################### -S2 -#R$*<$*>$* $1$2$3 defocus - - - -################################# -# Final Output Post-rewriting # -################################# -S4 - -R@ $@ handle <> error addr - -# resolve numeric addresses to name if possible -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 lookup numeric internet addr - -# externalize local domain info -R$*<$+>$* $1$2$3 defocus -R@$+:@$+:$+ @$1,@$2:$3 <route-addr> canonical - -# UUCP must always be presented in old form -R$+@$-.UUCP $2!$1 u@h.UUCP => h!u - -# delete duplicate local names -R$+%$=w@$=w $1@$w u%host@host => u@host -R$+%$=w@$=w.$D $1@$w u%host@host => u@host - - -########################### -# Name Canonicalization # -########################### -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -- note RFC733 heuristic here -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 basic RFC821/822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+ @$1:$2 change all "," to ":" - -# localize and dispose of route-based addresses -R@$+:$+ $@$>6<@$1>:$2 handle <route-addr> - -# more miscellaneous cleanup -R$+ $:$>8$1 host dependent cleanup -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style addresses to a domain-based address -R$+^$+ $1!$2 convert ^ to ! -R$-!$+ $@$>6$2<@$1.UUCP> resolve uucp names -R$+.$-!$+ $@$>6$3<@$1.$2> domain uucps -R$+!$+ $@$>6$2<@$1.UUCP> uucp subdomains -R$+%$+ $:$>9$1%$2 user%host -R$+<@$+> $@$>6$1<@$2> already canonical -R$-.$+ $@$>6$2<@$1> host.user - - -################################# -# special local conversions # -################################# - -S6 -R$*<@$=w>$* $:$1<@$w>$3 get into u@$w form -R$*<@$=w.$D>$* $:$1<@$w>$3 -R$*<@$=U.UUCP>$* $:$1<@$w>$3 - -################################ -# Change rightmost % to @. # -################################ - -S9 -R$*%$* $1@$2 First make them all @'s. -R$*@$*@$* $1%$2@$3 Undo all but the last. -R$*@$* $@$1<@$2> Put back the brackets. - - - -################### -### Mailers ### -################### - - -############################################################ -############################################################ -##### -##### Local and Program Mailer specification -##### -############################################################ -############################################################ - -Mlocal, P=/bin/mail, F=rlsDFMmn, S=10, R=20, A=mail -d $u -Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u - -S10 -R@ $n errors to mailer-daemon - - - -############################################################ -############################################################ -##### -##### Internet SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to leave -##### the local domain -- hence, they must be canonical according to -##### RFC822 etc. This means that machines not registered with -##### the NIC must be hidden behind our Internet relay. -##### -############################################################ -############################################################ - -Mtcp, P=[IPC], F=mDFMueXLC, S=14, R=24, A=IPC $h, E=\r\n - -S14 - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output internal ("fake") domains as "user%host@relay" - - - -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S24 - -# put in <> kludge -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere..... -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# Hide fake domains behind relays - - - -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### UUCP Mailer specification -##### -############################################################ -############################################################ - - -Muucp, P=/usr/bin/uux, F=DFMhuU, S=13, R=23, M=100000, - A=uux - -r -z -a$f -gC $h!rmail ($u) - -S13 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$+> $2!$1 uucpize (no @'s in addr) -R$w!$+ $1 strip local name -R$+ $:$U!$1 stick on our host name -R$=U!$-%$- $:$1!$2@$3.$D ucbvax!user@host.domain - -S23 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$w> $U!$1 a!b@here -> here!a!b -R$=U!$+ $2 here!a!b -> a!b -# sanity ... should not happen. -R$=U.$D!$+ $2 strip local name.domain - - -############################################################ -############################################################ -##### -##### Provide Backward Compatibility -##### -############################################################ -############################################################ - -##################################################### -# General code to convert back to old style names # -##################################################### -S5 - -R$+<@$w> $1 strip host -R$+<@$-.UUCP> $2!$1 u@host.UUCP => host!u - - - -##################### -### Rule Zero ### -##################### - - -############################################################ -############################################################ -##### -##### RULESET ZERO PREAMBLE -##### -##### The beginning of ruleset zero is constant through all -##### configurations. -##### -############################################################ -############################################################ - -S0 - -# first make canonical -R$*<$*>$* $1$2$3 defocus -R$+ $:$>3$1 make canonical - -# handle special cases -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 numeric internet addr -R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec -R$+ $:$>6$1 -R$-<@$w> $#local$:$1 -R@ $#error$:Invalid address handle <> form - -# canonicalize using the nameserver if not internal domain -R$*<@$*.$~I>$* $:$1<@$[$2.$3$]>$4 -R$*<@$->$* $:$1<@$[$2$]>$3 -R$*<@$->$* $:$1<@$2.$D>$3 if nameserver fails - -# now delete the local info -R<@$w>:$* $@$>0$1 @here:... -> ... -R$*<@$w> $@$>0$1 ...@here -> ... - -################################## -# End of ruleset zero preamble # -################################## - - -############################################### -### Machine dependent part of Rule Zero ### -############################################### - - - -# resolve local UUCP connections -R<@$=V.UUCP>:$+ $#uucp$@$1$:$2 @host.UUCP:... -R$+<@$=V.UUCP> $#uucp$@$2$:$1 user@host.UUCP - - - - -# remaining names must be local -R$+ $#local$:$1 everything else diff --git a/usr.sbin/sendmail/cf/cf/uucpproto.mc b/usr.sbin/sendmail/cf/cf/uucpproto.mc index 86751fcb62..9fe749e247 100644 --- a/usr.sbin/sendmail/cf/cf/uucpproto.mc +++ b/usr.sbin/sendmail/cf/cf/uucpproto.mc @@ -1,14 +1,39 @@ -divert(10)dnl +divert(-1) # -# Prototype configuration file for systems only on UUCP networks +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. # -# @(#)uucpproto.mc 1.2 (Berkeley) 1/24/89 +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. # -define(DOMAIN, `DDUUCP') -define(UUCP_NAME, DUYOUR_UUCP_HOSTNAME_HERE) -define(UUCP_ALIASES, CUYOUR_UUCP_ALIASES_HERE) -define(UUCP_HOSTS_FILE, ../sitedep/uucp.proto.m4) -define(UUCP_ONLY, 1) -define(EXTERNAL_VERSION, ``#' `@(#)uucpproto.mc 1.2' (Berkeley) `1/24/89'') -divert(0)dnl -include(proto.mc)dnl +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)uucpproto.mc 8.1 (Berkeley) 6/7/93') +MAILER(local)dnl +MAILER(uucp)dnl diff --git a/usr.sbin/sendmail/cf/cf/vangogh.mc b/usr.sbin/sendmail/cf/cf/vangogh.mc new file mode 100644 index 0000000000..edf98656e3 --- /dev/null +++ b/usr.sbin/sendmail/cf/cf/vangogh.mc @@ -0,0 +1,43 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +include(`../m4/cf.m4') +VERSIONID(`@(#)vangogh.mc 8.1 (Berkeley) 6/7/93') +DOMAIN(cs.exposed)dnl +OSTYPE(bsd4.4)dnl +MAILER(local)dnl +MAILER(smtp)dnl +define(`MCI_CACHE_SIZE', 5) +Cw okeeffe diff --git a/usr.sbin/sendmail/cf/domain/Berkeley.m4 b/usr.sbin/sendmail/cf/domain/Berkeley.m4 new file mode 100644 index 0000000000..fc72e0761c --- /dev/null +++ b/usr.sbin/sendmail/cf/domain/Berkeley.m4 @@ -0,0 +1,40 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +divert(0) +VERSIONID(`@(#)Berkeley.m4 8.1 (Berkeley) 6/7/93') +define(`UUCP_RELAY', `ucbvax.Berkeley.EDU')dnl +define(`BITNET_RELAY', `jade.Berkeley.EDU')dnl +define(`CSNET_RELAY', `Relay.Prime.COM')dnl +FEATURE(redirect)dnl diff --git a/usr.sbin/sendmail/cf/domain/cs.exposed.m4 b/usr.sbin/sendmail/cf/domain/cs.exposed.m4 new file mode 100644 index 0000000000..43c07becdb --- /dev/null +++ b/usr.sbin/sendmail/cf/domain/cs.exposed.m4 @@ -0,0 +1,40 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +divert(0) +VERSIONID(`@(#)cs.exposed.m4 8.1 (Berkeley) 6/7/93') +DOMAIN(Berkeley)dnl +HACK(cssubdomain)dnl +define(`confUSERDB_SPEC', + `/usr/sww/share/lib/users.cs.db,/usr/sww/share/lib/users.eecs.db')dnl diff --git a/usr.sbin/sendmail/cf/domain/cs.hidden.m4 b/usr.sbin/sendmail/cf/domain/cs.hidden.m4 new file mode 100644 index 0000000000..3d9721af7a --- /dev/null +++ b/usr.sbin/sendmail/cf/domain/cs.hidden.m4 @@ -0,0 +1,38 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +divert(0) +VERSIONID(`@(#)cs.hidden.m4 8.1 (Berkeley) 6/7/93') +DOMAIN(cs.exposed)dnl +MASQUERADE_AS(CS.Berkeley.EDU)dnl diff --git a/usr.sbin/sendmail/cf/domain/eecs.hidden.m4 b/usr.sbin/sendmail/cf/domain/eecs.hidden.m4 new file mode 100644 index 0000000000..bbdc01afb1 --- /dev/null +++ b/usr.sbin/sendmail/cf/domain/eecs.hidden.m4 @@ -0,0 +1,38 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +divert(0) +VERSIONID(`@(#)eecs.hidden.m4 8.1 (Berkeley) 6/7/93') +DOMAIN(Berkeley)dnl +MASQUERADE_AS(EECS.Berkeley.EDU)dnl diff --git a/usr.sbin/sendmail/cf/domain/s2k.m4 b/usr.sbin/sendmail/cf/domain/s2k.m4 new file mode 100644 index 0000000000..25b931f511 --- /dev/null +++ b/usr.sbin/sendmail/cf/domain/s2k.m4 @@ -0,0 +1,38 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +divert(0) +VERSIONID(`@(#)s2k.m4 8.1 (Berkeley) 6/7/93') +DOMAIN(cs.exposed)dnl +MASQUERADE_AS(postgres.Berkeley.EDU)dnl diff --git a/usr.sbin/sendmail/cf/feature/allmasquerade.m4 b/usr.sbin/sendmail/cf/feature/allmasquerade.m4 new file mode 100644 index 0000000000..ec6700db3f --- /dev/null +++ b/usr.sbin/sendmail/cf/feature/allmasquerade.m4 @@ -0,0 +1,41 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)allmasquerade.m4 8.1 (Berkeley) 7/7/93') +divert(-1) + + +define(`_ALL_MASQUERADE_') diff --git a/usr.sbin/sendmail/cf/feature/always_add_domain.m4 b/usr.sbin/sendmail/cf/feature/always_add_domain.m4 new file mode 100644 index 0000000000..dd572c8130 --- /dev/null +++ b/usr.sbin/sendmail/cf/feature/always_add_domain.m4 @@ -0,0 +1,40 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)always_add_domain.m4 8.1 (Berkeley) 6/7/93') +divert(-1) + +define(`_ALWAYS_ADD_DOMAIN_', 1) diff --git a/usr.sbin/sendmail/cf/feature/bitdomain.m4 b/usr.sbin/sendmail/cf/feature/bitdomain.m4 new file mode 100644 index 0000000000..91ee8aeb7e --- /dev/null +++ b/usr.sbin/sendmail/cf/feature/bitdomain.m4 @@ -0,0 +1,49 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)bitdomain.m4 8.1 (Berkeley) 6/7/93') +divert(-1) + + +PUSHDIVERT(6) +Kbitdomain ifelse(_ARG_, `', `hash /etc/bitdomain -o', `_ARG_') +POPDIVERT + + +PUSHDIVERT(8) +# handle BITNET mapping +R$* < @ $+ .BITNET > $* $: $1 < @ $(bitdomain $2 $: $2.BITNET $) > $3 +POPDIVERT diff --git a/usr.sbin/sendmail/cf/feature/domaintable.m4 b/usr.sbin/sendmail/cf/feature/domaintable.m4 new file mode 100644 index 0000000000..5c059e9a69 --- /dev/null +++ b/usr.sbin/sendmail/cf/feature/domaintable.m4 @@ -0,0 +1,40 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)domaintable.m4 8.1 (Berkeley) 7/8/93') +divert(-1) + +define(`DOMAIN_TABLE', ifelse(_ARG_, `', `hash /etc/domaintable -o', `_ARG_'))dnl diff --git a/usr.sbin/sendmail/cf/feature/mailertable.m4 b/usr.sbin/sendmail/cf/feature/mailertable.m4 new file mode 100644 index 0000000000..89e17483e6 --- /dev/null +++ b/usr.sbin/sendmail/cf/feature/mailertable.m4 @@ -0,0 +1,40 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)mailertable.m4 8.1 (Berkeley) 6/7/93') +divert(-1) + +define(`MAILER_TABLE', ifelse(_ARG_, `', `hash /etc/mailertable -o', `_ARG_'))dnl diff --git a/usr.sbin/sendmail/cf/feature/nocanonify.m4 b/usr.sbin/sendmail/cf/feature/nocanonify.m4 new file mode 100644 index 0000000000..0157e6b9e4 --- /dev/null +++ b/usr.sbin/sendmail/cf/feature/nocanonify.m4 @@ -0,0 +1,40 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)nocanonify.m4 8.1 (Berkeley) 6/7/93') +divert(-1) + +define(`_NO_CANONIFY_', 1) diff --git a/usr.sbin/sendmail/cf/feature/notsticky.m4 b/usr.sbin/sendmail/cf/feature/notsticky.m4 new file mode 100644 index 0000000000..5118923671 --- /dev/null +++ b/usr.sbin/sendmail/cf/feature/notsticky.m4 @@ -0,0 +1,40 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)notsticky.m4 8.1 (Berkeley) 6/7/93') +divert(-1) + +define(`_LOCAL_NOT_STICKY_', 1) diff --git a/usr.sbin/sendmail/cf/feature/nouucp.m4 b/usr.sbin/sendmail/cf/feature/nouucp.m4 new file mode 100644 index 0000000000..8723437b3c --- /dev/null +++ b/usr.sbin/sendmail/cf/feature/nouucp.m4 @@ -0,0 +1,40 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)nouucp.m4 8.1 (Berkeley) 6/7/93') +divert(-1) + +define(`_NO_UUCP_', 1) diff --git a/usr.sbin/sendmail/cf/feature/redirect.m4 b/usr.sbin/sendmail/cf/feature/redirect.m4 new file mode 100644 index 0000000000..941fad896e --- /dev/null +++ b/usr.sbin/sendmail/cf/feature/redirect.m4 @@ -0,0 +1,48 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)redirect.m4 8.1 (Berkeley) 6/7/93') +divert(-1) + + +PUSHDIVERT(3) +# addresses sent to foo@host.REDIRECT will give a 551 error code +R$* < @ $+ .REDIRECT > $# error $@ NOUSER $: "551 User not local; please try " <$1@$2> +POPDIVERT + +PUSHDIVERT(6) +CPREDIRECT +POPDIVERT diff --git a/usr.sbin/sendmail/cf/feature/use_cw_file.m4 b/usr.sbin/sendmail/cf/feature/use_cw_file.m4 new file mode 100644 index 0000000000..33b5ad56ed --- /dev/null +++ b/usr.sbin/sendmail/cf/feature/use_cw_file.m4 @@ -0,0 +1,46 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)use_cw_file.m4 8.1 (Berkeley) 6/7/93') +divert(-1) + +# if defined, the sendmail.cf will read the /etc/sendmail.cw file +# to find alternate names for this host. Typically only used when +# several hosts have been squashed into one another at high speed. + +define(`USE_CW_FILE', `') + +divert(0) diff --git a/usr.sbin/sendmail/cf/feature/uucpdomain.m4 b/usr.sbin/sendmail/cf/feature/uucpdomain.m4 new file mode 100644 index 0000000000..98396fdc5b --- /dev/null +++ b/usr.sbin/sendmail/cf/feature/uucpdomain.m4 @@ -0,0 +1,49 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)uucpdomain.m4 8.1 (Berkeley) 6/7/93') +divert(-1) + + +PUSHDIVERT(6) +Kuudomain ifelse(_ARG_, `', `hash /etc/uudomain -o', `_ARG_') +POPDIVERT + + +PUSHDIVERT(8) +# handle UUCP mapping +R$* < @ $+ .UUCP > $* $: $1 < @ $(uudomain $2 $: $2.UUCP $) > $3 +POPDIVERT diff --git a/usr.sbin/sendmail/cf/hack/cssubdomain.m4 b/usr.sbin/sendmail/cf/hack/cssubdomain.m4 new file mode 100644 index 0000000000..4f270c05f0 --- /dev/null +++ b/usr.sbin/sendmail/cf/hack/cssubdomain.m4 @@ -0,0 +1,44 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +divert(0) +VERSIONID(`@(#)cssubdomain.m4 8.1 (Berkeley) 6/7/93') + +divert(2) +# find possible (old & new) versions of our name via short circuit hack +# (this code should exist ONLY during the transition from .Berkeley.EDU +# names to .CS.Berkeley.EDU names -- probably not more than a few months) +R$* < @ $=w .CS.Berkeley.EDU > $* $: $1 < @ $j > $3 +R$* < @ $=w .Berkeley.EDU> $* $: $1 < @ $j > $3 +divert(0) diff --git a/usr.sbin/sendmail/cf/m4/README b/usr.sbin/sendmail/cf/m4/README deleted file mode 100644 index 4d29fd3f20..0000000000 --- a/usr.sbin/sendmail/cf/m4/README +++ /dev/null @@ -1,6 +0,0 @@ - -These files contain templates and include files for the .cf files. -They should be reasonably generic. All the machine dependent stuff -is in ../machdep. - -Note that these files expect to be processed by m4. diff --git a/usr.sbin/sendmail/cf/m4/boilerplate.m4 b/usr.sbin/sendmail/cf/m4/boilerplate.m4 deleted file mode 100644 index cf0448f297..0000000000 --- a/usr.sbin/sendmail/cf/m4/boilerplate.m4 +++ /dev/null @@ -1,110 +0,0 @@ -divert(10) -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)boilerplate.m4 1.2 (Berkeley) 1/3/89 -# -divert(0) -###################### -# Special macros # -###################### - -# my name -DnMAILER-DAEMON -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$Z ready at $b - -############### -# Options # -############### - -# location of alias file -OA/etc/aliases -# wait up to ten minutes for alias file rebuild -Oa10 -# substitution for space (blank) characters -OB. -# (don't) connect to "expensive" mailers -#Oc -# default delivery mode (deliver in background) -Odbackground -# temporary file mode -OF0600 -# default GID -Og1 -# location of help file -OH/usr/share/misc/sendmail.hf -# log level -OL9 -# default network name -ONARPA -# default messages to old style -Oo -# queue directory -OQ/var/spool/mqueue -# read timeout -- violates protocols -Or2h -# status file -OS/var/log/sendmail.st -# queue up everything before starting transmission -Os -# default timeout interval -OT3d -# time zone names (V6 only) -OtPST,PDT -# default UID -Ou1 -# wizard's password -OW* -# load average at which we just queue messages -Ox8 -# load average at which we refuse connections -OX12 - -########################### -# Message precedences # -########################### - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -##################### -# Trusted users # -##################### - -Troot -Tdaemon -Tuucp - -######################### -# Format of headers # -######################### - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$Z) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -# HPosted-Date: $a -# H?l?Received-Date: $b -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - diff --git a/usr.sbin/sendmail/cf/m4/cf.m4 b/usr.sbin/sendmail/cf/m4/cf.m4 new file mode 100644 index 0000000000..4e4ca929b0 --- /dev/null +++ b/usr.sbin/sendmail/cf/m4/cf.m4 @@ -0,0 +1,147 @@ +divert(0)dnl +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + + +###################################################################### +###################################################################### +##### +##### SENDMAIL CONFIGURATION FILE +##### +define(`TEMPFILE', maketemp(/tmp/cfXXXXXX))dnl +syscmd(sh ../sh/makeinfo.sh > TEMPFILE)dnl +include(TEMPFILE)dnl +syscmd(rm -f TEMPFILE)dnl +##### +###################################################################### +###################################################################### + +divert(-1) + +changecom() +undefine(`format') +ifdef(`pushdef', `', + `errprint(`You need a newer version of M4, at least as new as +System V or GNU') + include(NoSuchFile)') +define(`PUSHDIVERT', `pushdef(`__D__', divnum)divert($1)') +define(`POPDIVERT', `divert(__D__)popdef(`__D__')') +define(`OSTYPE', `PUSHDIVERT(-1)define(`_ARG_', $2)include(../ostype/$1.m4)POPDIVERT`'') +define(`MAILER', +`ifdef(`_MAILER_$1_', `dnl`'', +`define(`_MAILER_$1_', `')PUSHDIVERT(7)include(../mailer/$1.m4)POPDIVERT`'')') +define(`DOMAIN', `PUSHDIVERT(-1)define(`_ARG_', $2)include(../domain/$1.m4)POPDIVERT`'') +define(`FEATURE', `PUSHDIVERT(-1)define(`_ARG_', $2)include(../feature/$1.m4)POPDIVERT`'') +define(`HACK', `PUSHDIVERT(-1)define(`_ARG_', $2)include(../hack/$1.m4)POPDIVERT`'') +define(`OLDSENDMAIL', `define(`_OLD_SENDMAIL_', `')') +define(`VERSIONID', ``##### $1 #####'') +define(`LOCAL_RULE_0', `divert(3)') +define(`LOCAL_RULE_1', +`divert(9)dnl +####################################### +### Ruleset 1 -- Sender Rewriting ### +####################################### + +S1 +') +define(`LOCAL_RULE_2', +`divert(9)dnl +########################################## +### Ruleset 2 -- Recipient Rewriting ### +########################################## + +S2 +') +define(`LOCAL_RULE_3', `divert(2)') +define(`LOCAL_CONFIG', `divert(6)') +define(`LOCAL_NET_CONFIG', `define(`_LOCAL_RULES_', 1)divert(1)') +define(`UUCPSMTP', `R DOL(*) < @ $1 .UUCP > DOL(*) DOL(1) < @ $2 > DOL(2)') +define(`CONCAT', `$1$2$3$4$5$6$7') +define(`DOL', ``$'$1') +define(`SITECONFIG', +`CONCAT(D, $3, $2) +define(`_CLASS_$3_', `')dnl +ifelse($3, U, Cw$2, `dnl') +define(`SITE', `ifelse(CONCAT($'2`, $3), SU, + CONCAT(CY, $'1`), + CONCAT(C, $3, $'1`))') +sinclude(../siteconfig/$1.m4)') +define(`EXPOSED_USER', `PUSHDIVERT(5)CE$1 +POPDIVERT`'dnl') +define(`LOCAL_USER', `PUSHDIVERT(5)CL$1 +POPDIVERT`'dnl') +define(`MASQUERADE_AS', `define(`MASQUERADE_NAME', $1)') + +m4wrap(`include(`../m4/proto.m4')') + +# set up default values for options +define(`confMAILER_NAME', ``MAILER-DAEMON'') +define(`confFROM_LINE', `From $g $d') +define(`confOPERATORS', `.:%@!^/[]') +define(`confSMTP_LOGIN_MSG', `$j Sendmail $v/$Z ready at $b') +define(`confSEVEN_BIT_INPUT', `False') +define(`confALIAS_WAIT', `10') +define(`confMIN_FREE_BLOCKS', `4') +define(`confBLANK_SUB', `.') +define(`confCON_EXPENSIVE', `False') +define(`confCHECKPOINT_INTERVAL', `10') +define(`confDELIVERY_MODE', `background') +define(`confAUTO_REBUILD', `False') +define(`confSAVE_FROM_LINES', `False') +define(`confTEMP_FILE_MODE', `0600') +define(`confMATCH_GECOS', `False') +define(`confDEF_GROUP_ID', `1') +define(`confMAX_HOP', `17') +define(`confIGNORE_DOTS', `False') +define(`confBIND_OPTS', `') +define(`confMCI_CACHE_SIZE', `2') +define(`confMCI_CACHE_TIMEOUT', `5m') +define(`confUSE_ERRORS_TO', `False') +define(`confLOG_LEVEL', `9') +define(`confME_TOO', `False') +define(`confCHECK_ALIASES', `True') +define(`confOLD_STYLE_HEADERS', `True') +define(`confPRIVACY_FLAGS', `authwarnings') +define(`confSAFE_QUEUE', `True') +define(`confMESSAGE_TIMEOUT', `5d/4h') +define(`confTIME_ZONE', `USE_SYSTEM') +define(`confDEF_USER_ID', `1') +define(`confQUEUE_LA', `8') +define(`confREFUSE_LA', `12') +define(`confSEPARATE_PROC', `False') +define(`confCW_FILE', `/etc/sendmail.cw') +define(`confMIME_FORMAT_ERRORS', `True') + +divert(0)dnl +VERSIONID(`@(#)cf.m4 8.1 (Berkeley) 6/7/93') diff --git a/usr.sbin/sendmail/cf/m4/fake_domains.m4 b/usr.sbin/sendmail/cf/m4/fake_domains.m4 deleted file mode 100644 index 3d8620d237..0000000000 --- a/usr.sbin/sendmail/cf/m4/fake_domains.m4 +++ /dev/null @@ -1,17 +0,0 @@ -divert(10)dnl -# -# Handle "fake" top level domains, by sending to a smarter -# host. Currently, we support: -# -# user@host.bitnet -> $B user@host.bitnet -# user@host.csnet -> $C user@host.csnet -# -# Eventually, all these should Go Away. -# -# @(#)fake_domains.m4 1.5 (Berkeley) 1/3/89 -# -divert(0)dnl -ifdef(`BITNET_RELAY', -R$*<@$+.BITNET>$* $`#'tcp$@$B$:$1<@$2.BITNET>$3 user@host.BITNET) -ifdef(`CSNET_RELAY', -R$*<@$+.CSNET>$* $`#'tcp$@$C$:$1<@$2.CSNET>$3 user@host.CSNET) diff --git a/usr.sbin/sendmail/cf/m4/localm.m4 b/usr.sbin/sendmail/cf/m4/localm.m4 deleted file mode 100644 index e99a6d6800..0000000000 --- a/usr.sbin/sendmail/cf/m4/localm.m4 +++ /dev/null @@ -1,26 +0,0 @@ -divert(10) -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)localm.m4 1.1 (Berkeley) 8/8/85 -# -divert(0) -############################################################ -############################################################ -##### -##### Local and Program Mailer specification -##### -############################################################ -############################################################ - -Mlocal, P=/usr/libexec/mail.local, F=lsDFMmn, S=10, R=20, A=mail -r $g -d $u -Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u - -S10 -R@ $n errors to mailer-daemon diff --git a/usr.sbin/sendmail/cf/m4/nsclasses.m4 b/usr.sbin/sendmail/cf/m4/nsclasses.m4 deleted file mode 100644 index b78713154b..0000000000 --- a/usr.sbin/sendmail/cf/m4/nsclasses.m4 +++ /dev/null @@ -1,20 +0,0 @@ -divert(10) -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)nsclasses.m4 1.8 (Berkeley) 1/3/89 -# -divert(0) -############### -# Classes # -############### - -# Internal ("fake") domains that we use in rewriting -CIUUCP BITNET CSNET - diff --git a/usr.sbin/sendmail/cf/m4/nsmacros.m4 b/usr.sbin/sendmail/cf/m4/nsmacros.m4 deleted file mode 100644 index b0a5292c7a..0000000000 --- a/usr.sbin/sendmail/cf/m4/nsmacros.m4 +++ /dev/null @@ -1,45 +0,0 @@ -divert(10) -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)nsmacros.m4 1.9 (Berkeley) 1/3/89 -# -divert(0) -###################### -# General Macros # -###################### - -# local domain name -ifdef(`DOMAIN', -DOMAIN, -`#' YOUR DOMAIN NAME GOES HERE! -DDYOUR_DOMAIN_NAME) - -ifdef(`INTERNET_RELAY', -`#' Internet relay host -- machines in our domain that are not -`#' registered with the NIC will be "hidden" behind this relay machine -`#' with the % kludge`,' although SMTP delivery will still be performed -`#' by the sending machine. Someday this will go away. -INTERNET_RELAY) - -ifdef(`UUCP_RELAY', -`#' UUCP relay host -UUCP_RELAY) - -ifdef(`CSNET_RELAY', -`#' csnet relay host -CSNET_RELAY) - -ifdef(`BITNET_RELAY', -`#' bitnet relay host -BITNET_RELAY) - -# my official hostname -Dj$w - diff --git a/usr.sbin/sendmail/cf/m4/nstcpldm.m4 b/usr.sbin/sendmail/cf/m4/nstcpldm.m4 deleted file mode 100644 index 5381729369..0000000000 --- a/usr.sbin/sendmail/cf/m4/nstcpldm.m4 +++ /dev/null @@ -1,94 +0,0 @@ -divert(10) -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)nstcpldm.m4 1.9 (Berkeley) 1/24/89 -# -divert(0) -############################################################ -############################################################ -##### -##### Local Domain SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to remain -##### the local domain. Hence, they can refer to hosts that are -##### not registered in the NIC host table. -##### -############################################################ -############################################################ - -Mtcpld, P=[IPC], F=mDFMueXLC, S=17, R=27, A=IPC $h, E=\r\n - -S17 - -# cleanup forwarding a bit -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 canonicalize -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -ifdef(`BITNET_RELAY', -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.bitnet) -ifdef(`CSNET_RELAY', -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET) -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S27 - -# cleanup -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -ifdef(`BITNET_RELAY', -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET) -ifdef(`CSNET_RELAY', -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET) -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - diff --git a/usr.sbin/sendmail/cf/m4/nstcpm.m4 b/usr.sbin/sendmail/cf/m4/nstcpm.m4 deleted file mode 100644 index 8ade7721ab..0000000000 --- a/usr.sbin/sendmail/cf/m4/nstcpm.m4 +++ /dev/null @@ -1,89 +0,0 @@ -divert(10) -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)nstcpm.m4 1.21 (Berkeley) 1/3/89 -# -divert(0) -############################################################ -############################################################ -##### -##### Internet SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to leave -##### the local domain -- hence, they must be canonical according to -##### RFC822 etc. This means that machines not registered with -##### the NIC must be hidden behind our Internet relay. -##### -############################################################ -############################################################ - -Mtcp, P=[IPC], F=mDFMueXLC, S=14, R=24, A=IPC $h, E=\r\n - -S14 - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output internal ("fake") domains as "user%host@relay" - -ifdef(`BITNET_RELAY', -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET) -ifdef(`CSNET_RELAY', -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET) -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S24 - -# put in <> kludge -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere..... -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# Hide fake domains behind relays - -ifdef(`BITNET_RELAY', -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET) -ifdef(`CSNET_RELAY', -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET) -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - diff --git a/usr.sbin/sendmail/cf/m4/postwriterule.m4 b/usr.sbin/sendmail/cf/m4/postwriterule.m4 deleted file mode 100644 index 185a1b9dae..0000000000 --- a/usr.sbin/sendmail/cf/m4/postwriterule.m4 +++ /dev/null @@ -1,33 +0,0 @@ -divert(10) -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)postwriterule.m4 1.8 (Berkeley) 1/3/89 -# -divert(0) -################################# -# Final Output Post-rewriting # -################################# -S4 - -R@ $@ handle <> error addr - -# resolve numeric addresses to name if possible -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 lookup numeric internet addr - -# externalize local domain info -R$*<$+>$* $1$2$3 defocus -R@$+:@$+:$+ @$1,@$2:$3 <route-addr> canonical - -# UUCP must always be presented in old form -R$+@$-.UUCP $2!$1 u@h.UUCP => h!u - -# delete duplicate local names -R$+%$=w@$=w $1@$w u%host@host => u@host -R$+%$=w@$=w.$D $1@$w u%host@host => u@host diff --git a/usr.sbin/sendmail/cf/m4/prewriterule.m4 b/usr.sbin/sendmail/cf/m4/prewriterule.m4 deleted file mode 100644 index 907c4972a0..0000000000 --- a/usr.sbin/sendmail/cf/m4/prewriterule.m4 +++ /dev/null @@ -1,25 +0,0 @@ -divert(10) -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)prewriterule.m4 1.1 (Berkeley) 8/8/85 -# -divert(0) -################################ -# Sender Field Pre-rewriting # -################################ -S1 -#R$*<$*>$* $1$2$3 defocus - -################################### -# Recipient Field Pre-rewriting # -################################### -S2 -#R$*<$*>$* $1$2$3 defocus - diff --git a/usr.sbin/sendmail/cf/m4/proto.m4 b/usr.sbin/sendmail/cf/m4/proto.m4 new file mode 100644 index 0000000000..39cbd62850 --- /dev/null +++ b/usr.sbin/sendmail/cf/m4/proto.m4 @@ -0,0 +1,656 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +divert(0) + +VERSIONID(`@(#)proto.m4 8.2 (Berkeley) 7/11/93') + +MAILER(local)dnl + +ifdef(`_OLD_SENDMAIL_', +`define(`_SET_96_', 6)dnl +define(`_SET_97_', 7)dnl', +`# level 4 config file format +V4 +define(`_SET_96_', 96)dnl +define(`_SET_97_', 97)dnl') + +################## +# local info # +################## + +CP. + +Cwlocalhost +ifdef(`USE_CW_FILE', +`# file containing names of hosts for which we receive email +CONCAT(`Fw', confCW_FILE)', `dnl') + +ifdef(`UUCP_RELAY', +`# UUCP relay host +CONCAT(DY, UUCP_RELAY) +CPUUCP + +')dnl +ifdef(`BITNET_RELAY', +`# BITNET relay host +CONCAT(DB, BITNET_RELAY) +CPBITNET + +')dnl +ifdef(`CSNET_RELAY', +`# CSNET relay host +CONCAT(DC, CSNET_RELAY) +CPCSNET + +')dnl +ifdef(`FAX_RELAY', +`# FAX relay host +CONCAT(DF, FAX_RELAY) +CPFAX + +')dnl +ifdef(`SMART_HOST', +`# "Smart" UUCP relay host +CONCAT(DS, SMART_HOST) + +')dnl +ifdef(`MAILER_TABLE', +`# Mailer table (overriding domains) +Kmailertable MAILER_TABLE + +')dnl +ifdef(`DOMAIN_TABLE', +`# Domain table (adding domains) +Kdomaintable DOMAIN_TABLE + +')dnl +# who I send unqualified names to (null means deliver locally) +CONCAT(DR, ifdef(`LOCAL_RELAY', LOCAL_RELAY)) + +# who gets all local email traffic ($R has precedence for unqualified names) +CONCAT(DH, ifdef(`MAIL_HUB', MAIL_HUB)) + +# my official hostname ($w or $w.$D) +CONCAT(Dj$w, ifdef(`NEED_DOMAIN', .$D)) + +# who I masquerade as (can be $j) +CONCAT(DM, ifdef(`MASQUERADE_NAME', MASQUERADE_NAME, $j)) + +# class L: names that should be delivered locally, even if we have a relay +# class E: names that should be exposed as from this host, even if we masquerade +CLroot +CEroot +undivert(5)dnl + +# operators that cannot be in local usernames (i.e., network indicators) +CO @ % ifdef(`_NO_UUCP_', `', `!') + +# a class with just dot (for identifying canonical names) +C.. + +ifdef(`_OLD_SENDMAIL_', `dnl', +`# dequoting map +Kdequote dequote') + +undivert(6)dnl + +###################### +# Special macros # +###################### + +# SMTP initial login message +CONCAT(De, confSMTP_LOGIN_MSG) + +# UNIX initial From header format +CONCAT(Dl, confFROM_LINE) + +# my name for error messages +CONCAT(Dn, confMAILER_NAME) + +# delimiter (operator) characters +CONCAT(Do, confOPERATORS) + +# format of a total name +CONCAT(Dq, ifdef(`confFROM_HEADER', confFROM_HEADER, + ifdef(`_OLD_SENDMAIL_', `$g$?x ($x)$.', `$?x$x <$g>$|$g$.'))) +include(`../m4/version.m4') + +############### +# Options # +############### + +# strip message body to 7 bits on input? +CONCAT(O7, confSEVEN_BIT_INPUT) + +# wait (in minutes) for alias file rebuild +CONCAT(Oa, confALIAS_WAIT) + +# location of alias file +CONCAT(OA, ifdef(`ALIAS_FILE', ALIAS_FILE, /etc/aliases)) + +# minimum number of free blocks on filesystem +CONCAT(Ob, confMIN_FREE_BLOCKS) + +# substitution for space (blank) characters +CONCAT(OB, confBLANK_SUB) + +# connect to "expensive" mailers on initial submission? +CONCAT(Oc, confCON_EXPENSIVE) + +# checkpoint queue runs after every N successful deliveries +CONCAT(OC, confCHECKPOINT_INTERVAL) + +# default delivery mode +CONCAT(Od, confDELIVERY_MODE) + +# automatically rebuild the alias database? +CONCAT(OD, confAUTO_REBUILD) + +# error message header/file */ +ifdef(`confERROR_MESSAGE', + CONCAT(OE, confERROR_MESSAGE), + #OE/etc/sendmail.oE) + +# error mode +ifdef(`confERROR_MODE', + CONCAT(Oe, confERROR_MODE), + #Oep) + +# save Unix-style "From_" lines at top of header? +CONCAT(Of, confSAVE_FROM_LINES) + +# temporary file mode +CONCAT(OF, confTEMP_FILE_MODE) + +# match recipients against GECOS field? +CONCAT(OG, confMATCH_GECOS) + +# default GID +CONCAT(Og, confDEF_GROUP_ID) + +# maximum hop count +CONCAT(Oh, confMAX_HOP) + +# location of help file +CONCAT(OH, ifdef(`HELP_FILE', HELP_FILE, /usr/lib/sendmail.hf)) + +# ignore dots as terminators in incoming messages? +CONCAT(Oi, confIGNORE_DOTS) + +# Insist that the BIND name server be running to resolve names +ifdef(`confBIND_OPTS', + CONCAT(OI, confBIND_OPTS), + #OI) + +# deliver MIME-encapsulated error messages? +CONCAT(Oj, confMIME_FORMAT_ERRORS) + +# Forward file search path +ifdef(`confFORWARD_PATH', + CONCAT(OJ, confFORWARD_PATH), + #OJ/var/forward/$u:$z/.forward.$w:$z/.forward) + +# open connection cache size +CONCAT(Ok, confMCI_CACHE_SIZE) + +# open connection cache timeout +CONCAT(OK, confMCI_CACHE_TIMEOUT) + +# use Errors-To: header? +CONCAT(Ol, confUSE_ERRORS_TO) + +# log level +CONCAT(OL, confLOG_LEVEL) + +# send to me too, even in an alias expansion? +CONCAT(Om, confME_TOO) + +# verify RHS in newaliases? +CONCAT(On, confCHECK_ALIASES) + +# default messages to old style headers if no special punctuation? +CONCAT(Oo, confOLD_STYLE_HEADERS) + +# SMTP daemon options +ifdef(`confDAEMON_OPTIONS', + CONCAT(OO, confDAEMON_OPTIONS), + #OOPort=esmtp) + +# privacy flags +CONCAT(Op, confPRIVACY_FLAGS) + +# who (if anyone) should get extra copies of error messages +ifdef(`confCOPY_ERRORS_TO', + CONCAT(OP, confCOPY_ERRORS_TO), + #OPPostmaster) + +# slope of queue-only function +ifdef(`confQUEUE_FACTOR', + CONCAT(Oq, confQUEUE_FACTOR), + #Oq600000) + +# queue directory +CONCAT(OQ, ifdef(`QUEUE_DIR', QUEUE_DIR, /var/spool/mqueue)) + +# read timeout -- now OK per RFC 1123 section 5.3.2 +ifdef(`confREAD_TIMEOUT', + CONCAT(Or, confREAD_TIMEOUT), + #Ordatablock=10m) + +# queue up everything before forking? +CONCAT(Os, confSAFE_QUEUE) + +# status file +CONCAT(OS, ifdef(`STATUS_FILE', STATUS_FILE, /etc/sendmail.st)) + +# default message timeout interval +CONCAT(OT, confMESSAGE_TIMEOUT) + +# time zone handling: +# if undefined, use system default +# if defined but null, use TZ envariable passed in +# if defined and non-null, use that info +ifelse(confTIME_ZONE, `USE_SYSTEM', `#Ot', + confTIME_ZONE, `USE_TZ', `', + `CONCAT(Ot, confTIME_ZONE)') + +# default UID +CONCAT(Ou, confDEF_USER_ID) + +# list of locations of user database file (null means no lookup) +OU`'ifdef(`confUSERDB_SPEC', `confUSERDB_SPEC') + +# fallback MX host +ifdef(`confFALLBACK_MX', + CONCAT(OV, confFALLBACK_MX), + #OVfall.back.host.net) + +# load average at which we just queue messages +CONCAT(Ox, confQUEUE_LA) + +# load average at which we refuse connections +CONCAT(OX, confREFUSE_LA) + +# work recipient factor +ifdef(`confWORK_RECIPIENT_FACTOR', + CONCAT(Oy, confWORK_RECIPIENT_FACTOR), + #Oy30000) + +# deliver each queued job in a separate process? +CONCAT(OY, confSEPARATE_PROC) + +# work class factor +ifdef(`confWORK_CLASS_FACTOR', + CONCAT(Oz, confWORK_CLASS_FACTOR), + #Oz1800) + +# work time factor +ifdef(`confWORK_TIME_FACTOR', + CONCAT(OZ, confWORK_TIME_FACTOR), + #OZ90000) + +########################### +# Message precedences # +########################### + +Pfirst-class=0 +Pspecial-delivery=100 +Plist=-30 +Pbulk=-60 +Pjunk=-100 + +##################### +# Trusted users # +##################### + +Troot +Tdaemon +Tuucp + +######################### +# Format of headers # +######################### + +H?P?Return-Path: $g +HReceived: $?sfrom $s $.$?_($_) $.by $j ($v/$Z)$?r with $r$. id $i; $b +H?D?Resent-Date: $a +H?D?Date: $a +H?F?Resent-From: $q +H?F?From: $q +H?x?Full-Name: $x +HSubject: +# HPosted-Date: $a +# H?l?Received-Date: $b +H?M?Resent-Message-Id: <$t.$i@$j> +H?M?Message-Id: <$t.$i@$j> +# +###################################################################### +###################################################################### +##### +##### REWRITING RULES +##### +###################################################################### +###################################################################### + +undivert(9)dnl + +########################################### +### Rulset 3 -- Name Canonicalization ### +########################################### +S3 + +# handle null input and list syntax (translate to <@> special case) +R$@ $@ <@> +R$*:;$* $@ $1 :; <@> + +# basic textual canonicalization -- note RFC733 heuristic here +R$*<$*>$*<$*>$* <$2>$3$4$5 strip multiple <> <> +R$*<$*<$+>$*>$* <$3>$5 2-level <> nesting +R$*<>$* $@ <@> MAIL FROM:<> case +R$*<$+>$* $2 basic RFC821/822 parsing + +# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later +R@ $+ , $+ @ $1 : $2 change all "," to ":" + +# localize and dispose of route-based addresses +R@ $+ : $+ $@ $>_SET_96_ < @$1 > : $2 handle <route-addr> + +# find focus for list syntax +R $+ : $* ; @ $+ $@ $>_SET_96_ $1 : $2 ; < @ $3 > list syntax +R $+ : $* ; $@ $1 : $2; list syntax + +# find focus for @ syntax addresses +R$+ @ $+ $: $1 < @ $2 > focus on domain +R$+ < $+ @ $+ > $1 $2 < @ $3 > move gaze right +R$+ < @ $+ > $@ $>_SET_96_ $1 < @ $2 > already canonical + +ifdef(`_NO_UUCP_', `dnl', +`# convert old-style addresses to a domain-based address +R$- ! $+ $@ $>_SET_96_ $2 < @ $1 .UUCP > resolve uucp names +R$+ . $- ! $+ $@ $>_SET_96_ $3 < @ $1 . $2 > domain uucps +R$+ ! $+ $@ $>_SET_96_ $2 < @ $1 .UUCP > uucp subdomains') + +# if we have % signs, take the rightmost one +R$* % $* $1 @ $2 First make them all @s. +R$* @ $* @ $* $1 % $2 @ $3 Undo all but the last. +R$* @ $* $@ $>_SET_96_ $1 < @ $2 > Insert < > and finish + +# else we must be a local name + + +################################################ +### Ruleset _SET_96_ -- bottom half of ruleset 3 ### +################################################ + +# At this point, everything should be in a "local_part<@domain>extra" format. +S`'_SET_96_ + +# handle special cases for local names +R$* < @ localhost > $* $: $1 < @ $j . > $2 no domain at all +R$* < @ localhost . $m > $* $: $1 < @ $j . > $2 local domain +ifdef(`_NO_UUCP_', `dnl', +`R$* < @ localhost . UUCP > $* $: $1 < @ $j . > $2 .UUCP domain') +ifdef(`DOMAIN_TABLE', ` +# look up unqualified domains in the domain table +R$* < @ $- > $* $: $1 < @ $(domaintable $2 $) > $3', +`dnl') +undivert(2)dnl + +ifdef(`_NO_UUCP_', `dnl', +`ifdef(`UUCP_RELAY', +`# pass UUCP addresses straight through +R$* < @ $+ . UUCP > $* $@ $1 < @ $2 . UUCP > $3', +`# if really UUCP, handle it immediately +ifdef(`_CLASS_U_', +`R$* < @ $=U . UUCP > $* $@ $1 < @ $2 . UUCP > $3', `dnl') +ifdef(`_CLASS_V_', +`R$* < @ $=V . UUCP > $* $@ $1 < @ $2 . UUCP > $3', `dnl') +ifdef(`_CLASS_W_', +`R$* < @ $=W . UUCP > $* $@ $1 < @ $2 . UUCP > $3', `dnl') +ifdef(`_CLASS_X_', +`R$* < @ $=X . UUCP > $* $@ $1 < @ $2 . UUCP > $3', `dnl') +ifdef(`_CLASS_Y_', +`R$* < @ $=Y . UUCP > $* $@ $1 < @ $2 . UUCP > $3', `dnl') + +# try UUCP traffic as a local address +R$* < @ $+ . UUCP > $* $: $1 < @ $[ $2 $] . UUCP > $3 +ifdef(`_OLD_SENDMAIL_', +`R$* < @ $+ . $+ . UUCP > $* $@ $1 < @ $2 . $3 . > $4', +`R$* < @ $+ . . UUCP > $* $@ $1 < @ $2 . > $3')') +') +ifdef(`_NO_CANONIFY_', +`# make sure local host names appear canonical +R$* < @ $=w > $* $: $1 < @ $2 . > $3', +`# pass to name server to make hostname canonical +R$* < @ $* $~P > $* $: $1 < @ $[ $2 $3 $] > $4') + +undivert(8)dnl + +# if this is the local hostname, make sure we treat is as canonical +R$* < @ $j > $* $: $1 < @ $j . > $2 + + +################################################## +### Ruleset 4 -- Final Output Post-rewriting ### +################################################## +S4 + +R$*<@> $@ $1 handle <> and list:; + +# resolve numeric addresses to name if possible +R$* < @ [ $+ ] > $* $: $1 < @ $[ [$2] $] > $3 lookup numeric internet addr + +# strip trailing dot off possibly canonical name +R$* < @ $+ . > $* $1 < @ $2 > $3 + +# externalize local domain info +R$* < $+ > $* $1 $2 $3 defocus +R@ $+ : @ $+ : $+ @ $1 , @ $2 : $3 <route-addr> canonical +R@ $* $@ @ $1 ... and exit + +ifdef(`_NO_UUCP_', `dnl', +`# UUCP must always be presented in old form +R$+ @ $- . UUCP $2!$1 u@h.UUCP => h!u') + +# delete duplicate local names +R$+ % $=w @ $=w $1 @ $j u%host@host => u@host + + + +############################################################## +### Ruleset _SET_97_ -- recanonicalize and call ruleset zero ### +### (used for recursive calls) ### +############################################################## + +S`'_SET_97_ +R$* $: $>3 $1 +R$* $@ $>0 $1 + + +###################################### +### Ruleset 0 -- Parse Address ### +###################################### + +S0 + +R<@> $#local $: <> special case error msgs +R$*:;<@> $#error $@ USAGE $: "list:; syntax illegal for recipient addresses" + +ifdef(`_MAILER_smtp_', +`# handle numeric address spec +R$* < @ [ $+ ] > $* $: $1 < @ $[ [$2] $] > $3 numeric internet addr +R$* < @ [ $+ ] > $* $#smtp $@ [$2] $: $1 @ [$2] $3 numeric internet spec', +`dnl') + +# now delete the local info -- note $=O to find characters that cause forwarding +R$* < @ > $* $@ $>_SET_97_ $1 user@ => user +R< @ $=w . > : $* $@ $>_SET_97_ $2 @here:... -> ... +R$* $=O $* < @ $=w . > $@ $>_SET_97_ $1 $2 $3 ...@here -> ... +ifdef(`MAILER_TABLE', +` +# try mailer table lookup +R$* <@ $+ > $* $: < $2 > $1 < @ $2 > $3 extract host name +R< $+ . > $* $: < $1 > $2 strip trailing dot +R< $+ > $* $: < $(mailertable $1 $) > $2 lookup +R< $- : $+ > $* $# $1 $@ $2 $: $3 check -- resolved? +R< $+ > $* $: $>90 <$1> $2 try domain', +`dnl') + +# short circuit local delivery so forwarded email works +ifdef(`_LOCAL_NOT_STICKY_', +`R$=L < @ $=w . > $#local $: @ $1 special local names +R$+ < @ $=w . > $#local $: $1 dispose directly', +`R$+ < @ $=w . > $: $1 < @ $2 @ $H > first try hub +ifdef(`_OLD_SENDMAIL_', +`R$+ < $+ @ $-:$+ > $# $3 $@ $4 $: $1 < $2 > yep .... +R$+ < $+ @ $+ > $#relay $@ $3 $: $1 < $2 > yep .... +R$+ < $+ @ > $#local $: $1 nope, local address', +`R$+ < $+ @ $+ > $#local $: $1 yep .... +R$+ < $+ @ > $#local $: @ $1 nope, local address')') +undivert(3)dnl +undivert(4)dnl + +ifdef(`_NO_UUCP_', `dnl', +`# resolve remotely connected UUCP links (if any) +ifdef(`_CLASS_V_', +`R$* < @ $=V . UUCP > $* $#smtp $@ $V $: <@ $V> : $1 @ $2.UUCP $3', + `dnl') +ifdef(`_CLASS_W_', +`R$* < @ $=W . UUCP > $* $#smtp $@ $W $: <@ $W> : $1 @ $2.UUCP $3', + `dnl') +ifdef(`_CLASS_X_', +`R$* < @ $=X . UUCP > $* $#smtp $@ $X $: <@ $X> : $1 @ $2.UUCP $3', + `dnl')') + +# resolve fake top level domains by forwarding to other hosts +ifdef(`BITNET_RELAY', +`R$*<@$+.BITNET>$* $#smtp $@ $B $: $1 <@$2.BITNET> $3 user@host.BITNET', + `dnl') +ifdef(`CSNET_RELAY', +`R$*<@$+.CSNET>$* $#smtp $@ $C $: $1 <@$2.CSNET> $3 user@host.CSNET', + `dnl') +ifdef(`_MAILER_fax_', +`R$+ < @ $+ .FAX > $#fax $@ $2 $: $1 user@host.FAX', +`ifdef(`FAX_RELAY', +`R$*<@$+.FAX>$* $#smtp $@ $F $: $1 <@$2.FAX> $3 user@host.FAX', + `dnl')') + +ifdef(`UUCP_RELAY', +`# forward non-local UUCP traffic to our UUCP relay +R$*<@$*.UUCP>$* $#smtp $@ $Y $: <@ $Y> : $1 @ $2.UUCP $3 uucp mail', +`ifdef(`_MAILER_uucp_', +`# forward other UUCP traffic straight to UUCP +R< @ $+ .UUCP > : $+ $#uucp $@ $1 $: $2 @host.UUCP:... +R$+ < @ $+ .UUCP > $#uucp $@ $2 $: $1 user@host.UUCP', + `dnl')') + +ifdef(`_MAILER_USENET_', ` +# addresses sent to net.group.USENET will get forwarded to a newsgroup +R$+ . USENET $# usenet $: $1', +`dnl') + +ifdef(`_LOCAL_RULES_', +`# figure out what should stay in our local mail system +undivert(1)', `dnl') + +ifdef(`SMART_HOST', ` +# pass names that still have a host to a smarthost +R$* < @ $* > $* $: < $S > $1 < @ $2 > $3 glue on smarthost name +R<$-:$+> $* < @$* > $* $# $1 $@ $2 $: $3 < @ $4 > $5 if non-null, use it +R<$+> $* < @$* > $* $#suucp $@ $1 $: $2 < @ $3 > $4 if non-null, use it +R<> $* < @ $* > $* $1 < @ $2 > $3 else strip off gunk', + +`ifdef(`_LOCAL_RULES_', ` +# reject messages that have host names we do not understand +R$* < @ $* > $* $#error $@ NOHOST $: Unrecognized host name $2', +`ifdef(`_MAILER_smtp_', +`# deal with other remote names +R$* < @ $* > $* $#smtp $@ $2 $: $1 < @ $2 > $3 user@host.domain')')') + +ifdef(`_OLD_SENDMAIL_', +`# forward remaining names to local relay, if any +R$=L $#local $: $1 special local names +R$+ $: $1 < @ $R > append relay +R$+ < @ > $: $1 < @ $H > no relay, try hub +R$+ < @ > $#local $: $1 no relay or hub: local +R$+ < @ $=w > $#local $: $1 we are relay/hub: local +R$+ < @ $-:$+ > $# $2 $@ $3 $: $1 deliver to relay/hub +R$+ < @ $+ > $#relay $@ $2 $: $1 deliver to relay/hub', + +`# if this is quoted, strip the quotes and try again +R$+ $: $(dequote $1 $) strip quotes +R$* $=O $* $@ $>_SET_97_ $1 $2 $3 try again + +# handle locally delivered names +R$=L $#local $: @ $1 special local names +R$+ $#local $: $1 regular local names + +########################################################################### +### Ruleset 5 -- special rewriting after aliases have been expanded ### +### (new sendmail only) ### +########################################################################### + +S5 + +# see if we have a relay or a hub +R$+ $: $1 < @ $R > +R$+ < @ > $: $1 < @ $H > no relay, try hub +R$+ < @ $=w > $@ $1 we are relay/hub: local +R$+ < @ $-:$+ > $# $2 $@ $3 $: $1 send to relay or hub +ifdef(`_MAILER_smtp_', +`R$+ < @ $+ > $#relay $@ $2 $: $1 send to relay or hub')') +ifdef(`MAILER_TABLE', +` + +########################################################################### +### Ruleset 90 -- try domain part of mailertable entry ### +### (new sendmail only) ### +########################################################################### + +S90 +R<$- . $+ > $* $: < $(mailertable .$2 $) > $3 lookup +R<$- : $+ > $* $# $1 $@ $2 $: $3 check -- resolved? +R< . $+ > $* $@ $>90 <$1> $2 no -- strip & try again +R<$*> $* $@ $2 no match', +`dnl') +# +###################################################################### +###################################################################### +##### +`##### MAILER DEFINITIONS' +##### +###################################################################### +###################################################################### +undivert(7)dnl diff --git a/usr.sbin/sendmail/cf/m4/rule0.m4 b/usr.sbin/sendmail/cf/m4/rule0.m4 deleted file mode 100644 index d456ebda99..0000000000 --- a/usr.sbin/sendmail/cf/m4/rule0.m4 +++ /dev/null @@ -1,49 +0,0 @@ -divert(10) -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)rule0.m4 1.10 (Berkeley) 1/3/89 -# -divert(0) -############################################################ -############################################################ -##### -##### RULESET ZERO PREAMBLE -##### -##### The beginning of ruleset zero is constant through all -##### configurations. -##### -############################################################ -############################################################ - -S0 - -# first make canonical -R$*<$*>$* $1$2$3 defocus -R$+ $:$>3$1 make canonical - -# handle special cases -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 numeric internet addr -R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec -R$+ $:$>6$1 -R$-<@$w> $#local$:$1 -R@ $#error$:Invalid address handle <> form - -# canonicalize using the nameserver if not internal domain -R$*<@$*.$~I>$* $:$1<@$[$2.$3$]>$4 -R$*<@$->$* $:$1<@$[$2$]>$3 -R$*<@$->$* $:$1<@$2.$D>$3 if nameserver fails - -# now delete the local info -R<@$w>:$* $@$>0$1 @here:... -> ... -R$*<@$w> $@$>0$1 ...@here -> ... - -################################## -# End of ruleset zero preamble # -################################## diff --git a/usr.sbin/sendmail/cf/m4/rule3.m4 b/usr.sbin/sendmail/cf/m4/rule3.m4 deleted file mode 100644 index 392c0fe5c6..0000000000 --- a/usr.sbin/sendmail/cf/m4/rule3.m4 +++ /dev/null @@ -1,68 +0,0 @@ -divert(10) -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)rule3.m4 1.17 (Berkeley) 1/3/89 -# -divert(0) -########################### -# Name Canonicalization # -########################### -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -- note RFC733 heuristic here -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 basic RFC821/822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+ @$1:$2 change all "," to ":" - -# localize and dispose of route-based addresses -R@$+:$+ $@$>6<@$1>:$2 handle <route-addr> - -# more miscellaneous cleanup -R$+ $:$>8$1 host dependent cleanup -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style addresses to a domain-based address -R$+^$+ $1!$2 convert ^ to ! -R$-!$+ $@$>6$2<@$1.UUCP> resolve uucp names -R$+.$-!$+ $@$>6$3<@$1.$2> domain uucps -R$+!$+ $@$>6$2<@$1.UUCP> uucp subdomains -R$+%$+ $:$>9$1%$2 user%host -R$+<@$+> $@$>6$1<@$2> already canonical -R$-.$+ $@$>6$2<@$1> host.user - - -################################# -# special local conversions # -################################# - -S6 -R$*<@$=w>$* $:$1<@$w>$3 get into u@$w form -R$*<@$=w.$D>$* $:$1<@$w>$3 -R$*<@$=U.UUCP>$* $:$1<@$w>$3 - -################################ -# Change rightmost % to @. # -################################ - -S9 -R$*%$* $1@$2 First make them all @'s. -R$*@$*@$* $1%$2@$3 Undo all but the last. -R$*@$* $@$1<@$2> Put back the brackets. - diff --git a/usr.sbin/sendmail/cf/m4/rule5.m4 b/usr.sbin/sendmail/cf/m4/rule5.m4 deleted file mode 100644 index a6a1bbbc89..0000000000 --- a/usr.sbin/sendmail/cf/m4/rule5.m4 +++ /dev/null @@ -1,28 +0,0 @@ -divert(10) -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)rule5.m4 1.4 (Berkeley) 11/4/87 -# -divert(0) -############################################################ -############################################################ -##### -##### Provide Backward Compatibility -##### -############################################################ -############################################################ - -##################################################### -# General code to convert back to old style names # -##################################################### -S5 - -R$+<@$w> $1 strip host -R$+<@$-.UUCP> $2!$1 u@host.UUCP => host!u diff --git a/usr.sbin/sendmail/cf/m4/smtpuucpm.m4 b/usr.sbin/sendmail/cf/m4/smtpuucpm.m4 deleted file mode 100644 index 4dcfcc553f..0000000000 --- a/usr.sbin/sendmail/cf/m4/smtpuucpm.m4 +++ /dev/null @@ -1,34 +0,0 @@ -divert(10) -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)smtpuucpm.m4 1.1 (Berkeley) 1/3/89 -# -divert(0) -############################################################ -############################################################ -##### -##### SMTP UUCP Mailer specification -##### -##### This mailer sends UUCP traffic over an SMTP connection. -##### Obviously, we only want to do this with UUCP hosts with -##### whom we have SMTP connectivity. The idea here is to -##### avoid having to double queue (once for sendmail, once -##### for UUCP) when there's no need. Since we need to -##### preserve uucp-ness (e.g., bangs), we use the UUCP mailer -##### rewriting rulesets. -##### -############################################################ -############################################################ - -Msmtpuucp, P=[IPC], F=mDFMueXLC, S=13, R=23, A=IPC $h, E=\r\n - -define(SMTPUUCPPAIR, -`R<@$1.uucp>:$+ $`#'smtpuucp$@$2$:`$'2 -R$*<@$1.uucp> $`#'smtpuucp$@$2$:`$'1') diff --git a/usr.sbin/sendmail/cf/m4/suucpm.m4 b/usr.sbin/sendmail/cf/m4/suucpm.m4 deleted file mode 100644 index 837ec4c4c2..0000000000 --- a/usr.sbin/sendmail/cf/m4/suucpm.m4 +++ /dev/null @@ -1,43 +0,0 @@ -divert(10) -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)suucpm.m4 1.3 (Berkeley) 1/3/89 -# -divert(0) -############################################################ -############################################################ -##### -##### UUCP Smart Mailer specification -##### (handles multiple recipients) -##### -############################################################ -############################################################ - - - -Msuucp, P=/usr/bin/uux, F=mDFMhuU, S=13, R=23, M=100000, - A=uux - -r $h!rmail ($u) - -ifdef(`m4UUCP',`divert(10)',) - -S13 -R$+ $:$>5$1 convert to old style -R$=w!$+ $2 strip local name -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$+> $2!$1 uucpize (no @'s in addr) -R$+ $:$U!$1 stick on our host name - -S23 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$2.$D>$3 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$w> $w!$1 uucp!u@local -> local!uucp!u - -ifdef(`m4UUCP',`divert(0)',) diff --git a/usr.sbin/sendmail/cf/m4/uucpm.m4 b/usr.sbin/sendmail/cf/m4/uucpm.m4 deleted file mode 100644 index 0d572c0029..0000000000 --- a/usr.sbin/sendmail/cf/m4/uucpm.m4 +++ /dev/null @@ -1,42 +0,0 @@ -divert(10) -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)uucpm.m4 1.11 (Berkeley) 1/3/89 -# -divert(0) -############################################################ -############################################################ -##### -##### UUCP Mailer specification -##### -############################################################ -############################################################ - - -Muucp, P=/usr/local/bin/uux, F=DFMhuU, S=13, R=23, M=100000, - A=uux - -r -z -a$f -gC $h!rmail ($u) - -S13 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$+> $2!$1 uucpize (no @'s in addr) -R$w!$+ $1 strip local name -R$+ $:$U!$1 stick on our host name -R$=U!$-%$- $:$1!$2@$3.$D ucbvax!user@host.domain - -S23 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$w> $U!$1 a!b@here -> here!a!b -R$=U!$+ $2 here!a!b -> a!b -# sanity ... should not happen. -R$=U.$D!$+ $2 strip local name.domain diff --git a/usr.sbin/sendmail/cf/m4/version.m4 b/usr.sbin/sendmail/cf/m4/version.m4 index e301aacba0..174b7da4b6 100644 --- a/usr.sbin/sendmail/cf/m4/version.m4 +++ b/usr.sbin/sendmail/cf/m4/version.m4 @@ -1,17 +1,39 @@ -divert(10) +divert(-1) # -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. # -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +VERSIONID(`@(#)version.m4 8.3 (Berkeley) 7/13/93') # -# @(#)version.m4 1.34 (Berkeley) 12/24/88 divert(0) -###################### -# Version Number # -###################### - -DZ1.34 +# Configuration version number +DZ8.3 diff --git a/usr.sbin/sendmail/cf/mailer/fax.m4 b/usr.sbin/sendmail/cf/mailer/fax.m4 new file mode 100644 index 0000000000..3d9068db98 --- /dev/null +++ b/usr.sbin/sendmail/cf/mailer/fax.m4 @@ -0,0 +1,48 @@ +PUSHDIVERT(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# This assumes you already have Sam Leffler's FAX software. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +ifdef(`FAX_MAILER_PATH',, + `define(`FAX_MAILER_PATH', /usr/local/lib/fax/mailfax)') +POPDIVERT +#################################### +### FAX Mailer specification ### +#################################### + +VERSIONID(`@(#)fax.m4 8.1 (Berkeley) 6/7/93') + +Mfax, P=FAX_MAILER_PATH, F=DFMhu, S=14, R=24, M=100000, + A=mailfax $u $h $f diff --git a/usr.sbin/sendmail/cf/mailer/local.m4 b/usr.sbin/sendmail/cf/mailer/local.m4 new file mode 100644 index 0000000000..8c96851266 --- /dev/null +++ b/usr.sbin/sendmail/cf/mailer/local.m4 @@ -0,0 +1,60 @@ +PUSHDIVERT(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +ifdef(`LOCAL_MAILER_FLAGS',, `define(`LOCAL_MAILER_FLAGS', `rn')') +ifdef(`LOCAL_MAILER_PATH',, `define(`LOCAL_MAILER_PATH', /usr/libexec/mail.local)') +ifdef(`LOCAL_MAILER_ARGS',, `define(`LOCAL_MAILER_ARGS', `mail -d $u')') +ifdef(`LOCAL_SHELL_FLAGS',, `define(`LOCAL_SHELL_FLAGS', `eu')') +ifdef(`LOCAL_SHELL_PATH',, `define(`LOCAL_SHELL_PATH', /bin/sh)') +ifdef(`LOCAL_SHELL_ARGS',, `define(`LOCAL_SHELL_ARGS', `sh -c $u')') +POPDIVERT + +################################################## +### Local and Program Mailer specification ### +################################################## + +VERSIONID(`@(#)local.m4 8.3 (Berkeley) 7/13/93') + +Mlocal, P=LOCAL_MAILER_PATH, F=CONCAT(`lsDFMm', LOCAL_MAILER_FLAGS), S=10, R=20, + A=LOCAL_MAILER_ARGS +Mprog, P=LOCAL_SHELL_PATH, F=CONCAT(`lsDFM', LOCAL_SHELL_FLAGS), S=10, R=20, D=$z:/, + A=LOCAL_SHELL_ARGS + +S10 +R<@> $n errors to mailer-daemon +ifdef(`_ALWAYS_ADD_DOMAIN_', +`R$* < @ $* > $* $@ $1 < @ $2 > $3 already fully qualified +R$* $: $1 @ $M add local qualification +R$* @ $: $1 @ $j if $M not defined', +`dnl') diff --git a/usr.sbin/sendmail/cf/mailer/smtp.m4 b/usr.sbin/sendmail/cf/mailer/smtp.m4 new file mode 100644 index 0000000000..9119451e3f --- /dev/null +++ b/usr.sbin/sendmail/cf/mailer/smtp.m4 @@ -0,0 +1,96 @@ +PUSHDIVERT(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +ifdef(`SMTP_MAILER_FLAGS',, + `define(`SMTP_MAILER_FLAGS', + `ifdef(`_OLD_SENDMAIL_', `L', `')')') +POPDIVERT +##################################### +### SMTP Mailer specification ### +##################################### + +VERSIONID(`@(#)smtp.m4 8.3 (Berkeley) 7/11/93') + +Msmtp, P=[IPC], F=CONCAT(mDFMueXC, SMTP_MAILER_FLAGS), S=11, R=ifdef(`_ALL_MASQUERADE_', `11', `21'), E=\r\n, + ifdef(`_OLD_SENDMAIL_',, `L=990, ')A=IPC $h +Mesmtp, P=[IPC], F=CONCAT(mDFMueXCa, SMTP_MAILER_FLAGS), S=11, R=ifdef(`_ALL_MASQUERADE_', `11', `21'), E=\r\n, + ifdef(`_OLD_SENDMAIL_',, `L=990, ')A=IPC $h +Mrelay, P=[IPC], F=CONCAT(mDFMueXCa, SMTP_MAILER_FLAGS), S=11, R=19, E=\r\n, + ifdef(`_OLD_SENDMAIL_',, `L=2040, ')A=IPC $h + +S11 + +# do sender/recipient common rewriting +R$+ $: $>19 $1 + +# if already @ qualified, we are done +R$* < @ $* > $* $@ $1 < @ $2 > $3 already qualified + +# do not qualify list:; syntax +R$* :; <@> $@ $1 :; + +# unqualified names (e.g., "eric") "come from" $M +R$=E $@ $1 < @ $j> show exposed names +R$+ $: $1 < @ $M > user w/o host +R$+ <@> $: $1 < @ $j > in case $M undefined + +ifdef(`_ALL_MASQUERADE_', `dnl', +`S21 + +# do sender/recipient common rewriting +R$+ $: $>19 $1 + +# if already @ qualified, we are done +R$* < @ $* > $* $@ $1 < @ $2 > $3 already qualified + +# do not qualify list:; syntax +R$* :; <@> $@ $1 :; + +# unqualified names (e.g., "eric") are qualified by local host +R$+ $: $1 < @ $j >') + +S19 + +# pass <route-addr>s through +R< @ $+ > $* $@ < @ $1 > $2 resolve <route-addr> + +# output fake domains as user%fake@relay +ifdef(`BITNET_RELAY', +`R$+ <@ $+ . BITNET > $: $1 % $2 .BITNET < @ $B > user@host.BITNET', + `dnl') +ifdef(`CSNET_RELAY', +`R$+ <@ $+ . CSNET > $: $1 % $2 .CSNET < @ $C > user@host.CSNET', + `dnl') +ifdef(`_NO_UUCP_', `dnl', +`R$+ <@ $+ . UUCP > $: $2 ! $1 < @ $j > user@host.UUCP') diff --git a/usr.sbin/sendmail/cf/mailer/usenet.m4 b/usr.sbin/sendmail/cf/mailer/usenet.m4 new file mode 100644 index 0000000000..4a2fddaae1 --- /dev/null +++ b/usr.sbin/sendmail/cf/mailer/usenet.m4 @@ -0,0 +1,47 @@ +PUSHDIVERT(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +ifdef(`USENET_MAILER_PATH',, `define(`USENET_MAILER_PATH', /usr/lib/news/inews)') +ifdef(`USENET_MAILER_FLAGS',, `define(`USENET_MAILER_FLAGS', `rlsDFMmn')') +ifdef(`USENET_MAILER_ARGS',, `define(`USENET_MAILER_ARGS', `-m -h -n')') +POPDIVERT +#################################### +### USENET Mailer specification ### +#################################### + +VERSIONID(`@(#)usenet.m4 8.1 (Berkeley) 6/7/93') + +Musenet, P=USENET_MAILER_PATH, F=USENET_MAILER_FLAGS, S=10, R=20, + A=inews USENET_MAILER_ARGS $u diff --git a/usr.sbin/sendmail/cf/mailer/uucp.m4 b/usr.sbin/sendmail/cf/mailer/uucp.m4 new file mode 100644 index 0000000000..f97e3dcc31 --- /dev/null +++ b/usr.sbin/sendmail/cf/mailer/uucp.m4 @@ -0,0 +1,93 @@ +PUSHDIVERT(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +ifdef(`UUCP_MAILER_PATH',, `define(`UUCP_MAILER_PATH', /usr/bin/uux)') +ifdef(`UUCP_MAILER_ARGS',, `define(`UUCP_MAILER_ARGS', `uux - -r -z -a$f -gC $h!rmail ($u)')') +ifdef(`UUCP_MAILER_FLAGS',, `define(`UUCP_MAILER_FLAGS', `')') +ifdef(`UUCP_MAX_SIZE',, `define(`UUCP_MAX_SIZE', 100000)') +POPDIVERT +##################################### +### UUCP Mailer specification ### +##################################### + +VERSIONID(`@(#)uucp.m4 8.4 (Berkeley) 7/13/93') + +# old UUCP mailer +Muucp, P=UUCP_MAILER_PATH, F=CONCAT(DFMhuU, UUCP_MAILER_FLAGS), S=12, R=22, M=UUCP_MAX_SIZE, + A=UUCP_MAILER_ARGS + +# smart UUCP mailer (handles multiple addresses) +Msuucp, P=UUCP_MAILER_PATH, F=CONCAT(mDFMhuU, UUCP_MAILER_FLAGS), S=12, R=22, M=UUCP_MAX_SIZE, + A=UUCP_MAILER_ARGS + +ifdef(`_MAILER_smtp_', +`# domain-ized UUCP mailer +Muucp-dom, P=UUCP_MAILER_PATH, F=CONCAT(mDFMhu, UUCP_MAILER_FLAGS), S=11, R=21, M=UUCP_MAX_SIZE, + A=UUCP_MAILER_ARGS') + + +# sender rewriting +S12 + +# handle error address as a special case +R<@> $n errors to mailer-daemon + +# do not qualify list:; syntax +R$* :; <@> $@ $1 :; + +R$* < @ $* . > $1 < @ $2 > strip trailing dots +R$* < @ $j > $1 strip local name +R$* < @ $- . UUCP > $2 ! $1 convert to UUCP format +R$* < @ $+ > $2 ! $1 convert to UUCP format +R$+ $: $U ! $1 prepend our name + +# recipient rewriting +S22 + +# don't touch list:; syntax +R$* :; <@> $@ $1 ;: + +R$* < @ $* . > $1 < @ $2 > strip trailing dots +R$* < @ $j > $1 strip local name +R$* < @ $- . UUCP > $2 ! $1 convert to UUCP format +R$* < @ $+ > $2 ! $1 convert to UUCP format + +PUSHDIVERT(4) +# resolve locally connected UUCP links +R< @ $=Y . UUCP > : $+ $#suucp $@ $1 $: $2 @host.UUCP: ... +R< @ $=U . UUCP > : $+ $#uucp $@ $1 $: $2 @host.UUCP: ... +R$+ < @ $=Y . UUCP > $#suucp $@ $2 $: $1 user@host.UUCP +R$+ < @ $=U . UUCP > $#uucp $@ $2 $: $1 user@host.UUCP +POPDIVERT diff --git a/usr.sbin/sendmail/cf/ostype/aix3.m4 b/usr.sbin/sendmail/cf/ostype/aix3.m4 new file mode 100644 index 0000000000..3eda5634de --- /dev/null +++ b/usr.sbin/sendmail/cf/ostype/aix3.m4 @@ -0,0 +1,40 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)aix3.m4 8.2 (Berkeley) 7/13/93') +define(`LOCAL_MAILER_PATH', /bin/bellmail)dnl +define(`LOCAL_MAILER_ARGS', mail $u)dnl +define(`LOCAL_MAILER_FLAGS', `n')dnl diff --git a/usr.sbin/sendmail/cf/ostype/bsd4.3.m4 b/usr.sbin/sendmail/cf/ostype/bsd4.3.m4 new file mode 100644 index 0000000000..3c519ca94e --- /dev/null +++ b/usr.sbin/sendmail/cf/ostype/bsd4.3.m4 @@ -0,0 +1,38 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)bsd4.3.m4 8.1 (Berkeley) 6/7/93') +define(`QUEUE_DIR', /usr/spool/mqueue)dnl diff --git a/usr.sbin/sendmail/cf/ostype/bsd4.4.m4 b/usr.sbin/sendmail/cf/ostype/bsd4.4.m4 new file mode 100644 index 0000000000..bdaa1fc5fe --- /dev/null +++ b/usr.sbin/sendmail/cf/ostype/bsd4.4.m4 @@ -0,0 +1,41 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# + +divert(0) +VERSIONID(`@(#)bsd4.4.m4 8.1 (Berkeley) 6/7/93') +define(`HELP_FILE', /usr/share/misc/sendmail.hf)dnl +define(`STATUS_FILE', /var/log/sendmail.st)dnl +define(`LOCAL_MAILER_PATH', /usr/libexec/mail.local)dnl diff --git a/usr.sbin/sendmail/cf/ostype/hpux.m4 b/usr.sbin/sendmail/cf/ostype/hpux.m4 new file mode 100644 index 0000000000..ea1c05b0c6 --- /dev/null +++ b/usr.sbin/sendmail/cf/ostype/hpux.m4 @@ -0,0 +1,39 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)hpux.m4 8.1 (Berkeley) 6/7/93') +define(`_HPUX_', `')dnl +define(`LOCAL_MAILER_FLAGS', `')dnl diff --git a/usr.sbin/sendmail/cf/ostype/irix.m4 b/usr.sbin/sendmail/cf/ostype/irix.m4 new file mode 100644 index 0000000000..7e4e8dc6f4 --- /dev/null +++ b/usr.sbin/sendmail/cf/ostype/irix.m4 @@ -0,0 +1,38 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)irix.m4 8.1 (Berkeley) 6/7/93') +define(`LOCAL_MAILER_FLAGS', Ehu)dnl diff --git a/usr.sbin/sendmail/cf/ostype/osf1.m4 b/usr.sbin/sendmail/cf/ostype/osf1.m4 new file mode 100644 index 0000000000..fbc4832026 --- /dev/null +++ b/usr.sbin/sendmail/cf/ostype/osf1.m4 @@ -0,0 +1,41 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)osf1.m4 8.1 (Berkeley) 6/7/93') +ifdef(`_OLD_SENDMAIL_', `define(`NEED_DOMAIN', `')')dnl +define(`ALIAS_FILE', /usr/adm/sendmail/aliases)dnl +define(`STATUS_FILE', /usr/adm/sendmail/sendmail.st)dnl +define(`HELP_FILE', /usr/share/lib/sendmail.hf)dnl diff --git a/usr.sbin/sendmail/cf/ostype/riscos4.5.m4 b/usr.sbin/sendmail/cf/ostype/riscos4.5.m4 new file mode 100644 index 0000000000..847cb0e392 --- /dev/null +++ b/usr.sbin/sendmail/cf/ostype/riscos4.5.m4 @@ -0,0 +1,37 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)riscos4.5.m4 8.1 (Berkeley) 6/7/93') diff --git a/usr.sbin/sendmail/cf/ostype/solaris2.1.m4 b/usr.sbin/sendmail/cf/ostype/solaris2.1.m4 new file mode 100644 index 0000000000..21142cae1e --- /dev/null +++ b/usr.sbin/sendmail/cf/ostype/solaris2.1.m4 @@ -0,0 +1,17 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# %sccs.include.redist.sh% +# + +divert(0) +VERSIONID(`%W% (Berkeley) %G%') +divert(-1) + +define(`ALIAS_FILE', /etc/mail/aliases) +define(`HELP_FILE', /var/lib/sendmail.hf) +define(`STATUS_FILE', /etc/mail/sendmail.st) +define(`LOCAL_MAILER_FLAGS', `fSn') diff --git a/usr.sbin/sendmail/cf/ostype/sunos3.5.m4 b/usr.sbin/sendmail/cf/ostype/sunos3.5.m4 new file mode 100644 index 0000000000..fe76931880 --- /dev/null +++ b/usr.sbin/sendmail/cf/ostype/sunos3.5.m4 @@ -0,0 +1,37 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)sunos3.5.m4 8.1 (Berkeley) 6/7/93') diff --git a/usr.sbin/sendmail/cf/ostype/sunos4.1.m4 b/usr.sbin/sendmail/cf/ostype/sunos4.1.m4 new file mode 100644 index 0000000000..cfa7a9a02a --- /dev/null +++ b/usr.sbin/sendmail/cf/ostype/sunos4.1.m4 @@ -0,0 +1,37 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)sunos4.1.m4 8.1 (Berkeley) 6/7/93') diff --git a/usr.sbin/sendmail/cf/ostype/ultrix4.1.m4 b/usr.sbin/sendmail/cf/ostype/ultrix4.1.m4 new file mode 100644 index 0000000000..87c4425bc1 --- /dev/null +++ b/usr.sbin/sendmail/cf/ostype/ultrix4.1.m4 @@ -0,0 +1,38 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +divert(0) +VERSIONID(`@(#)ultrix4.1.m4 8.1 (Berkeley) 6/7/93') +ifdef(`_OLD_SENDMAIL_', `define(`NEED_DOMAIN', `')')dnl diff --git a/usr.sbin/sendmail/cf/sh/makeinfo.sh b/usr.sbin/sendmail/cf/sh/makeinfo.sh new file mode 100644 index 0000000000..c7c3633e17 --- /dev/null +++ b/usr.sbin/sendmail/cf/sh/makeinfo.sh @@ -0,0 +1,39 @@ +#!/bin/sh +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)makeinfo.sh 8.1 (Berkeley) 6/7/93 +# + +echo '#####' built by `whoami` on `date` +echo '#####' in `pwd` on `hostname` diff --git a/usr.sbin/sendmail/cf/siteconfig/uucp.cogsci.m4 b/usr.sbin/sendmail/cf/siteconfig/uucp.cogsci.m4 new file mode 100644 index 0000000000..33c7151178 --- /dev/null +++ b/usr.sbin/sendmail/cf/siteconfig/uucp.cogsci.m4 @@ -0,0 +1,6 @@ +SITE(contessa) +SITE(emind) +SITE(hoptoad) +SITE(nkainc) +SITE(well) +SITE(ferdy) diff --git a/usr.sbin/sendmail/cf/siteconfig/uucp.old.arpa.m4 b/usr.sbin/sendmail/cf/siteconfig/uucp.old.arpa.m4 new file mode 100644 index 0000000000..81d5e9443d --- /dev/null +++ b/usr.sbin/sendmail/cf/siteconfig/uucp.old.arpa.m4 @@ -0,0 +1,4 @@ +SITE(endotsew) +SITE(fateman) +SITE(interlan) +SITE(metron) diff --git a/usr.sbin/sendmail/cf/siteconfig/uucp.ucbarpa.m4 b/usr.sbin/sendmail/cf/siteconfig/uucp.ucbarpa.m4 new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/usr.sbin/sendmail/cf/siteconfig/uucp.ucbarpa.m4 @@ -0,0 +1 @@ + diff --git a/usr.sbin/sendmail/cf/siteconfig/uucp.ucbvax.m4 b/usr.sbin/sendmail/cf/siteconfig/uucp.ucbvax.m4 new file mode 100644 index 0000000000..ee2c34f60d --- /dev/null +++ b/usr.sbin/sendmail/cf/siteconfig/uucp.ucbvax.m4 @@ -0,0 +1,73 @@ +SITE(Padova) +SITE(Shasta) +SITE(alice) +SITE(allegra) +SITE(amdcad) +SITE(att) +SITE(attunix) +SITE(avsd) +SITE(bellcore bellcor) +SITE(calma) +SITE(cithep) +SITE(cnmat) +SITE(craig) +SITE(craylab) +SITE(decusj) +SITE(decvax, S) +SITE(decwrl) +SITE(dssovax) +SITE(eagle) +SITE(ecovax) +SITE(floyd) +SITE(franz) +SITE(geoff) +SITE(harpo) +SITE(ho3e2) +SITE(hpda) +SITE(hplabs) +SITE(ibmsupt ibmuupa ibmpa) +SITE(iiasa70) +SITE(imagen) +SITE(isunix menlo70) +SITE(kentmth) +SITE(lbl-csam lbl-csa) +SITE(lime) +SITE(mothra) +SITE(mseonyx) +SITE(mtxinu) +SITE(pixar) +SITE(pur-ee) +SITE(purdue) +SITE(pwbd) +SITE(sdcarl) +SITE(sftig) +SITE(sgi olympus) +SITE(sii) +SITE(srivisi) +SITE(ssyx) +SITE(sun) +SITE(trwrb) +SITE(twg) +SITE(ucivax) +SITE(ucla-se) +SITE(ucla-cs) +SITE(ucsbcsl ucsbhub) +SITE(ucscc) +SITE(ucsd) +SITE(ucsfcgl) +SITE(ucsfmis) +SITE(ulysses) +SITE(unisoft) +SITE(unmvax) +SITE(usenix) +SITE(uw) +SITE(uwvax) +SITE(vax135) +SITE(voder) +SITE(wheps) +SITE(whuxle) +SITE(whuxlj) +SITE(xicomp) +SITE(xprin) +SITE(zehntel) +SITE(zilog) diff --git a/usr.sbin/sendmail/cf/sitedep/README b/usr.sbin/sendmail/cf/sitedep/README deleted file mode 100644 index e33ca3bd1f..0000000000 --- a/usr.sbin/sendmail/cf/sitedep/README +++ /dev/null @@ -1,19 +0,0 @@ - -Everything in this directory is site dependent. It contains info -such as UUCP host lists, SMTPUUCP host lists, etc. The idea here -is that there is one .m4 file for every generic type of configuration -file, and can be one .m4 file for hosts that have oddball needs. - - -On okeeffe: - -To edit one of these files (say to add a uucp host), do - - sccs edit file - -make your changes, and then do - - sccs delget file - -Then cd to ../cf and do a "make". - diff --git a/usr.sbin/sendmail/cf/sitedep/nicregistered.m4 b/usr.sbin/sendmail/cf/sitedep/nicregistered.m4 deleted file mode 100644 index 9ca55730e3..0000000000 --- a/usr.sbin/sendmail/cf/sitedep/nicregistered.m4 +++ /dev/null @@ -1,38 +0,0 @@ -divert(10) -# -# Sendmail -# Copyright (c) 1983 Eric P. Allman -# Berkeley, California -# -# Copyright (c) 1983 Regents of the University of California. -# All rights reserved. The Berkeley software License Agreement -# specifies the terms and conditions for redistribution. -# -# @(#)nicregistered.m4 1.7 (Berkeley) 3/31/88 -# -divert(0) -############################################################ -############################################################ -##### -##### BERKELEY HOSTS REGISTERED WITH THE NIC -##### -############################################################ -############################################################ - - -CNbach ucbbach -CNbizet ucbbizet -CNcad ucbcad -CNdegas ucbdegas -CNeast ucbeast -CNernie ucbernie -CNesvax ucbesvax -CNjade ucbjade -CNji ucbji -CNmike ucbmike -CNmonet ucbmonet -CNpostgres -CNrenoir ucbrenoir -CNucbarpa -CNucbvax -CNviolet ucbviolet diff --git a/usr.sbin/sendmail/cf/sitedep/smtpuucp.cad.m4 b/usr.sbin/sendmail/cf/sitedep/smtpuucp.cad.m4 deleted file mode 100644 index d77dc4e656..0000000000 --- a/usr.sbin/sendmail/cf/sitedep/smtpuucp.cad.m4 +++ /dev/null @@ -1,11 +0,0 @@ -divert(10)dnl -# -# SMTP UUCP connections on cad. -# The macro SMTPUUCPPAIR is defined in m4/uucpm.m4. -# -# @(#)smtpuucp.cad.m4 1.1 (Berkeley) 3/31/88 -# -divert(0)dnl - -SMTPUUCPPAIR(ames, ames.arc.nasa.gov) -SMTPUUCPPAIR(ucbvax, ucbvax.berkeley.edu) diff --git a/usr.sbin/sendmail/cf/sitedep/smtpuucp.ucbvax.m4 b/usr.sbin/sendmail/cf/sitedep/smtpuucp.ucbvax.m4 deleted file mode 100644 index e1a0824119..0000000000 --- a/usr.sbin/sendmail/cf/sitedep/smtpuucp.ucbvax.m4 +++ /dev/null @@ -1,35 +0,0 @@ -divert(10)dnl -# -# SMTP UUCP connections on ucbvax. -# The macro SMTPUUCPPAIR is defined in m4/uucpm.m4. -# -# @(#)smtpuucp.ucbvax.m4 1.5 (Berkeley) 12/2/88 -# -#SMTPUUCPPAIR(Padova -#SMTPUUCPPAIR(cithep -#SMTPUUCPPAIR(harpo, harpo.ucdavis.edu) -#SMTPUUCPPAIR(sdcsvax, ucsd.edu) -#SMTPUUCPPAIR(ucivax -#SMTPUUCPPAIR(ucsbcsl -#SMTPUUCPPAIR(ucsd -#SMTPUUCPPAIR(ucsfcgl -#SMTPUUCPPAIR(ucsfmis -#SMTPUUCPPAIR(unmvax -#SMTPUUCPPAIR(uw -divert(0)dnl - -SMTPUUCPPAIR(bellcore, bellcore.com) -SMTPUUCPPAIR(decvax, decvax.dec.com) -SMTPUUCPPAIR(decwrl, decwrl.dec.com) -SMTPUUCPPAIR(hplabs, hplabs.hp.com) -SMTPUUCPPAIR(lbl-csam, lbl-csam.arpa) -SMTPUUCPPAIR(pur-ee, ecn.purdue.edu) -SMTPUUCPPAIR(purdue, purdue.edu) -SMTPUUCPPAIR(sdcarl, sdcarl.ucsd.edu) -SMTPUUCPPAIR(sun, sun.com) -SMTPUUCPPAIR(ucbcad, cad.berkeley.edu) -SMTPUUCPPAIR(ucdavis, ucdavis.ucdavis.edu) -SMTPUUCPPAIR(ucla-se, seas.ucla.edu) -SMTPUUCPPAIR(ucscc, c.ucsc.edu) -SMTPUUCPPAIR(ucsfcgl, cgl.ucsf.edu) -SMTPUUCPPAIR(uwvax, spool.cs.wisc.edu) diff --git a/usr.sbin/sendmail/cf/sitedep/uucp.cad.m4 b/usr.sbin/sendmail/cf/sitedep/uucp.cad.m4 deleted file mode 100644 index 5c340c8440..0000000000 --- a/usr.sbin/sendmail/cf/sitedep/uucp.cad.m4 +++ /dev/null @@ -1,36 +0,0 @@ -divert(10)dnl -# -# UUCP connections on cad -# -# @(#)uucp.cad.m4 1.15 (Berkeley) 9/30/88 -# -divert(0)dnl -CV a -CV ames -CV analog -CV ardent dana -CV arthur -CV biosys -CV black -CV boulder -CV evans -CV harpo -CV harris -CV hpda -CV hpsrla -CV island -CV jupiter -CV masscomp masscom -CV nsc -CV oakhill -CV octal -CV sda -CV stellar -CV teklds -CV tektronix tektron -CV binky tonto -CV toshiba tsb0 -CV ucbcad -CV ucbvax -CV voder -CV vogon diff --git a/usr.sbin/sendmail/cf/sitedep/uucp.cogsci.m4 b/usr.sbin/sendmail/cf/sitedep/uucp.cogsci.m4 deleted file mode 100644 index 60a617f28d..0000000000 --- a/usr.sbin/sendmail/cf/sitedep/uucp.cogsci.m4 +++ /dev/null @@ -1,14 +0,0 @@ -divert(10)dnl -# -# UUCP connections on cogsci -# -# @(#)uucp.cogsci.m4 1.14 (Berkeley) 9/29/88 -# -divert(0)dnl -CV capmkt -CV contessa -CV emind -CV hoptoad -CV nkainc -CV well -CV ferdy diff --git a/usr.sbin/sendmail/cf/sitedep/uucp.ic.m4 b/usr.sbin/sendmail/cf/sitedep/uucp.ic.m4 deleted file mode 100644 index 2759c4c1eb..0000000000 --- a/usr.sbin/sendmail/cf/sitedep/uucp.ic.m4 +++ /dev/null @@ -1,8 +0,0 @@ -divert(10)dnl -# -# UUCP connections on ic -# -# @(#)uucp.ic.m4 1.1 (Berkeley) 9/16/88 -# -divert(0)dnl -CV foo diff --git a/usr.sbin/sendmail/cf/sitedep/uucp.id.m4 b/usr.sbin/sendmail/cf/sitedep/uucp.id.m4 deleted file mode 100644 index 2287f408e4..0000000000 --- a/usr.sbin/sendmail/cf/sitedep/uucp.id.m4 +++ /dev/null @@ -1,6 +0,0 @@ -divert(10)dnl -# -# UUCP connections on id -# -divert(0)dnl -CVjpunix diff --git a/usr.sbin/sendmail/cf/sitedep/uucp.okeeffe.m4 b/usr.sbin/sendmail/cf/sitedep/uucp.okeeffe.m4 deleted file mode 100644 index 92b9ab316e..0000000000 --- a/usr.sbin/sendmail/cf/sitedep/uucp.okeeffe.m4 +++ /dev/null @@ -1,12 +0,0 @@ -divert(10)dnl -# -# UUCP connections on okeeffe -# -# @(#)uucp.okeeffe.m4 1.1 (Berkeley) 10/28/87 -# -divert(0)dnl -CV blia -CV ccicpg -CV mjk -CV pixar -CV zulu diff --git a/usr.sbin/sendmail/cf/sitedep/uucp.proto.m4 b/usr.sbin/sendmail/cf/sitedep/uucp.proto.m4 deleted file mode 100644 index 52734c2aff..0000000000 --- a/usr.sbin/sendmail/cf/sitedep/uucp.proto.m4 +++ /dev/null @@ -1,8 +0,0 @@ -divert(10)dnl -# -# Prototype UUCP neighbor file -# -# %W% (Berkeley) %G% -# -divert(0)dnl -CVjpunix diff --git a/usr.sbin/sendmail/cf/sitedep/uucp.ucbarpa.m4 b/usr.sbin/sendmail/cf/sitedep/uucp.ucbarpa.m4 deleted file mode 100644 index 2009d23b08..0000000000 --- a/usr.sbin/sendmail/cf/sitedep/uucp.ucbarpa.m4 +++ /dev/null @@ -1,12 +0,0 @@ -divert(10)dnl -# -# UUCP connections on ucbarpa -# -# @(#)uucp.ucbarpa.m4 1.4 (Berkeley) 7/5/87 -# -divert(0)dnl -CV endotsew -CV fateman -CV franz -CV interlan -CV metron diff --git a/usr.sbin/sendmail/cf/sitedep/uucp.ucbvax.m4 b/usr.sbin/sendmail/cf/sitedep/uucp.ucbvax.m4 deleted file mode 100644 index 8e28e73887..0000000000 --- a/usr.sbin/sendmail/cf/sitedep/uucp.ucbvax.m4 +++ /dev/null @@ -1,86 +0,0 @@ -divert(10)dnl -# -# UUCP connections on ucbvax -# -# @(#)uucp.ucbvax.m4 1.18 (Berkeley) 12/7/88 -# -divert(0)dnl -CV Padova -CV Shasta -CV alice -CV allegra -CV amdcad -CV anlams -CV att -CV attunix -CV avsd -CV bellcore bellcor -CV calma -CV cithep -CV craylab -CV decusj -CV decvax -CV decwrl -CV dssovax -CV eagle -CV ecovax -CV floyd -CV geoff -CV harpo -CV ho3e2 -CV hpda -CV hplabs -CV ibmsupt ibmuupa ibmpa -CV iiasa70 -CV imagen -CV isunix menlo70 -CV kentmth -CV kentvax -CV lbl-csam lbl-csa -CV lime -CV mebazf -CV molbio -CV mothra -CV mseonyx -CV mtxinu -CV nbires -CV pixar -CV pur-ee -CV purdue -CV pwbd -CV research researc -CV sdcarl -CV sdcsvax -CV sftig -CV sgi olympus -CV sii -CV srivisi -CV ssyx -CV sun -CV tektronix tektron -CV tolerant toleran -CV trwrb -CV twg -CV ucbcad -CV ucdavis -CV ucivax -CV ucla-se ucla-cs -CV ucsbcsl ucsbhub -CV ucscc -CV ucsd -CV ucsfcgl -CV ucsfmis -CV ulysses -CV unisoft -CV unmvax -CV usenix -CV uw -CV uwvax -CV vax135 -CV voder -CV wheps -CV whuxle -CV whuxlj -CV xprin -CV zehntel -CV zilog diff --git a/usr.sbin/sendmail/contrib/README b/usr.sbin/sendmail/contrib/README new file mode 100644 index 0000000000..dcf5c8fe7e --- /dev/null +++ b/usr.sbin/sendmail/contrib/README @@ -0,0 +1,10 @@ +Everything in this directory (except this file) has been contributed. +We will not fix bugs in these programs. Contact the original author +for assistance. + +Some of these are patches to sendmail itself. You may need to take +care -- some of the patches may be out of date with the latest release +of sendmail. Also, the previous comment applies -- patches belong to +the original author, not to me. + +Eric Allman, 26 May 1993 diff --git a/usr.sbin/sendmail/contrib/bitdomain.c b/usr.sbin/sendmail/contrib/bitdomain.c new file mode 100644 index 0000000000..4fad761754 --- /dev/null +++ b/usr.sbin/sendmail/contrib/bitdomain.c @@ -0,0 +1,409 @@ +/* + * By John G. Myers, jgm+@cmu.edu + * Version 1.1 + * + * Process a BITNET "internet.listing" file, producing output + * suitable for input to makemap. + * + * The input file can be obtained via anonymous FTP to bitnic.educom.edu. + * Change directory to "netinfo" and get the file internet.listing + * The file is updated monthly. + * + * Feed the output of this program to "makemap hash /etc/bitdomain.db" + * to create the table used by the "FEATURE(bitdomain)" config file macro. + * If your sendmail does not have the db library compiled in, you can instead + * use "makemap dbm /etc/bitdomain" and + * "FEATURE(bitdomain,`dbm -o /etc/bitdomain')" + * + * The bitdomain table should be rebuilt monthly. + */ + +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv.h> +#include <netdb.h> +#include <ctype.h> +#include <string.h> + +/* don't use sizeof because sizeof(long) is different on 64-bit machines */ +#define SHORTSIZE 2 /* size of a short (really, must be 2) */ +#define LONGSIZE 4 /* size of a long (really, must be 4) */ + +typedef union +{ + HEADER qb1; + char qb2[PACKETSZ]; +} querybuf; + +extern int h_errno; +extern char *malloc(); +extern char *optarg; +extern int optind; + +char *lookup(); + +main(argc, argv) +int argc; +char **argv; +{ + int opt; + + while ((opt = getopt(argc, argv, "o:")) != EOF) { + switch (opt) { + case 'o': + if (!freopen(optarg, "w", stdout)) { + perror(optarg); + exit(1); + } + break; + + default: + fprintf(stderr, "usage: %s [-o outfile] [internet.listing]\n", + argv[0]); + exit(1); + } + } + + if (optind < argc) { + if (!freopen(argv[optind], "r", stdin)) { + perror(argv[optind]); + exit(1); + } + } + readfile(stdin); + finish(); + exit(0); +} + +/* + * Parse and process an input file + */ +readfile(infile) +FILE *infile; +{ + int skippingheader = 1; + char buf[1024], *node, *hostname, *p; + + while (fgets(buf, sizeof(buf), infile)) { + for (p = buf; *p && isspace(*p); p++); + if (!*p) { + skippingheader = 0; + continue; + } + if (skippingheader) continue; + + node = p; + for (; *p && !isspace(*p); p++) { + if (isupper(*p)) *p = tolower(*p); + } + if (!*p) { + fprintf(stderr, "%-8s: no domain name in input file\n", node); + continue; + } + *p++ = '\0'; + + for (; *p && isspace(*p); p++) ; + if (!*p) { + fprintf(stderr, "%-8s no domain name in input file\n", node); + continue; + } + + hostname = p; + for (; *p && !isspace(*p); p++) { + if (isupper(*p)) *p = tolower(*p); + } + *p = '\0'; + + /* Chop off any trailing .bitnet */ + if (strlen(hostname) > 7 && + !strcmp(hostname+strlen(hostname)-7, ".bitnet")) { + hostname[strlen(hostname)-7] = '\0'; + } + entry(node, hostname, sizeof(buf)-(hostname - buf)); + } +} + +/* + * Process a single entry in the input file. + * The entry tells us that "node" expands to "domain". + * "domain" can either be a domain name or a bitnet node name + * The buffer pointed to by "domain" may be overwritten--it + * is of size "domainlen". + */ +entry(node, domain, domainlen) +char *node; +char *domain; +char *domainlen; +{ + char *otherdomain, *p, *err; + + /* See if we have any remembered information about this node */ + otherdomain = lookup(node); + + if (otherdomain && strchr(otherdomain, '.')) { + /* We already have a domain for this node */ + if (!strchr(domain, '.')) { + /* + * This entry is an Eric Thomas FOO.BITNET kludge. + * He doesn't want LISTSERV to do transitive closures, so we + * do them instead. Give the the domain expansion for "node" + * (which is in "otherdomian") to FOO (which is in "domain") + * if "domain" doesn't have a domain expansion already. + */ + p = lookup(domain); + if (!p || !index(p, '.')) remember(domain, otherdomain); + } + } + else { + if (!strchr(domain, '.') || valhost(domain, domainlen)) { + remember(node, domain); + if (otherdomain) { + /* + * We previously mapped the node "node" to the node + * "otherdomain". If "otherdomain" doesn't already + * have a domain expansion, give it the expansion "domain". + */ + p = lookup(otherdomain); + if (!p || !index(p, '.')) remember(otherdomain, domain); + } + } + else { + switch (h_errno) { + case HOST_NOT_FOUND: + err = "not registered in DNS"; + break; + + case TRY_AGAIN: + err = "temporary DNS lookup failure"; + break; + + case NO_RECOVERY: + err = "non-recoverable nameserver error"; + break; + + case NO_DATA: + err = "registered in DNS, but not mailable"; + break; + + default: + err = "unknown nameserver error"; + break; + } + + fprintf(stderr, "%-8s %s %s\n", node, domain, err); + } + } +} + +/* + * Validate whether the mail domain "host" is registered in the DNS. + * If "host" is a CNAME, it is expanded in-place if the expansion fits + * into the buffer of size "hbsize". Returns nonzero if it is, zero + * if it is not. A BIND error code is left in h_errno. + */ +int +valhost(host, hbsize) + char *host; + int hbsize; +{ + register u_char *eom, *ap; + register int n; + HEADER *hp; + querybuf answer; + int ancount, qdcount; + int ret; + int type; + int qtype; + char nbuf[1024]; + + if ((_res.options & RES_INIT) == 0 && res_init() == -1) + return (0); + + _res.options &= ~(RES_DNSRCH|RES_DEFNAMES); + _res.retrans = 30; + _res.retry = 10; + + qtype = T_ANY; + + for (;;) { + h_errno = NO_DATA; + ret = res_querydomain(host, "", C_IN, qtype, + &answer, sizeof(answer)); + if (ret <= 0) + { + if (errno == ECONNREFUSED || h_errno == TRY_AGAIN) + { + /* the name server seems to be down */ + h_errno = TRY_AGAIN; + return 0; + } + + if (h_errno != HOST_NOT_FOUND) + { + /* might have another type of interest */ + if (qtype == T_ANY) + { + qtype = T_A; + continue; + } + else if (qtype == T_A) + { + qtype = T_MX; + continue; + } + } + + /* otherwise, no record */ + return 0; + } + + /* + ** This might be a bogus match. Search for A, MX, or + ** CNAME records. + */ + + hp = (HEADER *) &answer; + ap = (u_char *) &answer + sizeof(HEADER); + eom = (u_char *) &answer + ret; + + /* skip question part of response -- we know what we asked */ + for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ) + { + if ((ret = dn_skipname(ap, eom)) < 0) + { + return 0; /* ???XXX??? */ + } + } + + for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n) + { + n = dn_expand((u_char *) &answer, eom, ap, + (u_char *) nbuf, sizeof nbuf); + if (n < 0) + break; + ap += n; + GETSHORT(type, ap); + ap += SHORTSIZE + LONGSIZE; + GETSHORT(n, ap); + switch (type) + { + case T_MX: + case T_A: + return 1; + + case T_CNAME: + /* value points at name */ + if ((ret = dn_expand((u_char *)&answer, + eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0) + break; + if (strlen(nbuf) < hbsize) { + (void)strcpy(host, nbuf); + } + return 1; + + default: + /* not a record of interest */ + continue; + } + } + + /* + ** If this was a T_ANY query, we may have the info but + ** need an explicit query. Try T_A, then T_MX. + */ + + if (qtype == T_ANY) + qtype = T_A; + else if (qtype == T_A) + qtype = T_MX; + else + return 0; + } +} + +struct entry { + struct entry *next; + char *node; + char *domain; +}; +struct entry *firstentry; + +/* + * Find any remembered information about "node" + */ +char *lookup(node) +char *node; +{ + struct entry *p; + + for (p = firstentry; p; p = p->next) { + if (!strcmp(node, p->node)) { + return p->domain; + } + } + return 0; +} + +/* + * Mark the node "node" as equivalent to "domain". "domain" can either + * be a bitnet node or a domain name--if it is the latter, the mapping + * will be written to stdout. + */ +remember(node, domain) +char *node; +char *domain; +{ + struct entry *p; + + if (strchr(domain, '.')) { + fprintf(stdout, "%-8s %s\n", node, domain); + } + + for (p = firstentry; p; p = p->next) { + if (!strcmp(node, p->node)) { + p->domain = malloc(strlen(domain)+1); + if (!p->domain) { + goto outofmemory; + } + strcpy(p->domain, domain); + return; + } + } + + p = (struct entry *)malloc(sizeof(struct entry)); + if (!p) goto outofmemory; + + p->next = firstentry; + firstentry = p; + p->node = malloc(strlen(node)+1); + p->domain = malloc(strlen(domain)+1); + if (!p->node || !p->domain) goto outofmemory; + strcpy(p->node, node); + strcpy(p->domain, domain); + return; + + outofmemory: + fprintf(stderr, "Out of memory\n"); + exit(1); +} + +/* + * Walk through the database, looking for any cases where we know + * node FOO is equivalent to node BAR and node BAR has a domain name. + * For those cases, give FOO the same domain name as BAR. + */ +finish() +{ + struct entry *p; + char *domain; + + for (p = firstentry; p; p = p->next) { + if (!strchr(p->domain, '.') && (domain = lookup(p->domain))) { + remember(p->node, domain); + } + } +} + diff --git a/usr.sbin/sendmail/contrib/expn.pl b/usr.sbin/sendmail/contrib/expn.pl new file mode 100644 index 0000000000..4012699cac --- /dev/null +++ b/usr.sbin/sendmail/contrib/expn.pl @@ -0,0 +1,1239 @@ +#!/usr/local/bin/perl +'di'; +'ig00'; +# THIS PROGRAM IS ITS OWN MANUAL PAGE. INSTALL IN man & bin. +# groff cannot handle the wrapman constructs, so if you use +# groff, you must cut the manual part out and install it +# separately. + +# hardcoded constants, should work fine for BSD-based systems +$AF_INET = 2; +$SOCK_STREAM = 1; +$sockaddr = 'S n a4 x8'; + +# system requirements: +# must have 'nslookup' and 'hostname' programs. + +# version 3.2, 5/5/93 + +# TODO: +# CERNVM.CERN.CH needs simple logins for the expn command. +# format with groff. +# less magic should apply to command-line addresses +# less magic should apply to local addresses + +# Checklist: (hard addresses) +# harry@hofmann.cs.Berkeley.EDU -> harry@tenet (.berkeley.edu) +# bks@cs.berkeley.edu -> shiva.CS (.berkeley.edu) +# dan@tc.cornell.edu -> brown@tiberius (.tc.cornell.edu) + +############################################################################# +# +# Copyright (c) 1993 David Muir Sharnoff +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the David Muir Sharnoff. +# 4. The name of David Sharnoff may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE DAVID MUIR SHARNOFF ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL DAVID MUIR SHARNOFF BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# This copyright notice derrived from material copyrighted by the Regents +# of the University of California. +# +# Contributions accepted. +# +############################################################################# + +# overall structure: +# in an effort to not trace each address individually, but rather +# ask each server in turn a whole bunch of questions, addresses to +# be expanded are queued up. +# +# This means that all account w.r.t. an address must be stored in +# various arrays. Generally these arrays are indexed by the +# string "$addr *** $server" where $addr is the address to be +# expanded "foo" or maybe "foo@bar" and $server is the hostname +# of the SMTP server to contact. +# + +# important global variables: +# +# @hosts : list of servers still to be contacted +# $server : name of the current we are currently looking at +# @users = $users{@hosts[0]} : addresses to expand at this server +# $u = $users[0] : the current address being expanded +# $names{"$users[0] *** $server"} : the 'name' associated with the address +# $mxbacktrace{"$users[0] *** $server"} : record of mx expansion +# $mx_secondary{$server} : other mx relays at the same priority +# $domainify_fallback{"$users[0] *** $server"} : alternative names to try +# instead of $server if $server doesn't work +# $temporary_redirect{"$users[0] *** $server"} : when trying alternates, +# temporarily channel all tries along current path +# $giveup{$server} : do not bother expanding addresses at $server +# $verbose : -v +# $watch : -w +# $vw : -v or -w +# $debug : -d +# $valid : -a +# $levels : -1 +# S : the socket connection to $server + +$have_nslookup = 1; # we have the nslookup program +$port = 'smtp'; +$av0 = $0; +$0 = "$av0 - running hostname"; +$ENV{'PATH'} .= ":/usr/etc" unless $ENV{'PATH'} =~ m,/usr/etc,; +chop($hostname = `hostname`); +select(STDERR); + +$usage = "Usage: $av0 [-1avwd] user[@host] [user2[host2] ...]"; +$0 = "$av0 - parsing args"; +for $a (@ARGV) { + die $usage if $a eq "-"; + while ($a =~ s/^(-.*)([1avwd])/$1/) { + eval '$'."flag_$2 += 1"; + } + next if $a eq "-"; + die $usage if $a =~ /^-/; + &expn(&parse($a,$hostname,undef,1,1)); +} +$verbose = $flag_v; +$watch = $flag_w; +$vw = $flag_v + $flag_w; +$debug = $flag_d; +$valid = $flag_a; +$levels = $flag_1; + +die $usage unless @hosts; +if ($valid) { + if ($valid == 1) { + $validRequirement = 0.8; + } elsif ($valid == 2) { + $validRequirement = 1.0; + } elsif ($valid == 3) { + $validRequirement = 0.9; + } else { + $validRequirement = (1 - (1/($valid-3))); + print "validRequirement = $validRequirement\n" if $debug; + } +} + +$0 = "$av0 - building local socket"; +($name,$aliases,$proto) = getprotobyname('tcp'); +($name,$aliases,$port) = getservbyname($port,'tcp') + unless $port =~ /^\d+/; +($name,$aliases,$type,$len,$thisaddr) = gethostbyname($hostname); +$this = pack($sockaddr, $AF_INET, 0, $thisaddr); + +HOST: +while (@hosts) { + $server = shift(@hosts); + @users = split(' ',$users{$server}); + delete $users{$server}; + + # is this server already known to be bad? + $0 = "$av0 - looking up $server"; + if ($giveup{$server}) { + &giveup('mx domainify',$giveup{$server}); + next; + } + + # do we already have an mx record for this host? + next HOST if &mxredirect($server,*users); + + # look it up, or try for an mx. + $0 = "$av0 - gethostbyname($server)"; + + + ($name,$aliases,$type,$len,$thataddr) = gethostbyname($server); + # if we can't get an A record, try for an MX record. + unless($thataddr) { + &mxlookup(1,$server,"$server: could not resolve name",*users); + next HOST; + } + + # get a connection, or look for an mx + $0 = "$av0 - socket to $server"; + $that = pack($sockaddr, $AF_INET, $port, $thataddr); + socket(S, $AF_INET, $SOCK_STREAM, $proto) + || die "socket: $!"; + $0 = "$av0 - bind to $server"; + bind(S, $this) + || die "bind $hostname,0: $!"; + $0 = "$av0 - connect to $server"; + print "debug = $debug server = $server\n" if $debug > 8; + if (! connect(S, $that) || ($debug == 10 && $server =~ /relay\d.UU.NET$/i)) { + $0 = "$av0 - $server: could not connect: $!\n"; + $emsg = $!; + unless (&mxlookup(0,$server,"$server: could not connect: $!",*users)) { + &giveup('mx',"$server: Could not connect: $emsg"); + } + next HOST; + } + select((select(S),$| = 1)[0]); # don't buffer output to S + + # read the greeting + $0 = "$av0 - talking to $server"; + while(<S>) { + print if $watch; + if (/^(\d+)([- ])/) { + if ($1 != 220) { + $0 = "$av0 - bad numeric responce from $server"; + &toss($2); + print STDERR "$server: NOT 220 greeting: $_" + if ($debug || $vw); + if (&mxlookup(0,$server,"$server: did not respond with a 220 greeting",*users)) { + close(S); + next HOST; + } + } + last if ($2 eq " "); + } else { + $0 = "$av0 - bad responce from $server"; + print STDERR "$server: NOT 220 greeting: $_" + if ($debug || $vw); + unless (&mxlookup(0,$server,"$server: did not respond with SMTP codes",*users)) { + &giveup('',"$server: did not talk SMTP"); + } + close(S); + next HOST; + } + } + + # if this causes problems, remove it + $0 = "$av0 - sending helo to $server"; + &ps("helo $hostname"); + while(<S>) { + print if $watch; + last if /^\d+ /; + } + + # try the users, one by one + USER: + while(@users) { + $u = shift(@users); + $0 = "$av0 - expanding $u [\@$server]"; + + # do we already have a name for this user? + $oldname = $names{"$u *** $server"}; + + print &compact($u,$server)." ->\n" if ($verbose && ! $valid); + if ($valid) { + # + # when running with -a, we delay taking any action + # on the results of our query until we have looked + # at the complete output. @toFinal stores expansions + # that will be final if we take them. @toExpn stores + # expnansions that are not final. @isValid keeps + # track of our ability to send mail to each of the + # expansions. + # + @isValid = (); + @toFinal = (); + @toExpn = (); + } + &ps("expn $u"); + $said_something = 0; + while($s = <S>) { + $said_something = 1; + + # make sure the server is talking the right language + if ($s =~ /^(\d+)([- ])/) { + if ($1 != 250 && $1 != 550) { + &toss($2); + &ps("vrfy $u"); + $s = <S>; + if ($s =~ /^(\d+)/) { + if ($1 != 250 && $1 != 550) { + &toss($2); + &giveup('',"$server: expn/vrfy not implemented",$u); + last USER; + } + } + } + } + + $s =~ s/[\n\r]//g; + $0 = "$av0 - parsing $server: $s"; + print "$s\n" if $watch; + if ($s =~ /^250([- ])(.+)/) { + ($done,$addr) = ($1,$2); + ($newhost, $newaddr, $newname) = &parse($addr,$server,$oldname); + print "($newhost, $newaddr, $newname) = &parse($addr, $server, $oldname)\n" if $debug; + if (! $newhost) { + # no expansion is possible w/o a new server to call + if ($valid) { + push(@isValid, &validAddr($newaddr)); + push(@toFinal,$newaddr,$server,$newname); + } else { + &verbose(&final($newaddr,$server,$newname)); + } + } else { + $newmxhost = &mx($newhost,$newaddr); + print "$newmxhost = &mx($newhost)\n" + if ($debug && $newhost ne $newmxhost); + $0 = "$av0 - parsing $newaddr [@$newmxhost]"; + print "levels = $levels, level{$u *** $server} = ".$level{"$u *** $server"}."\n" if ($debug > 1); + # If the new server is the current one, + # it would have expanded things for us + # if it could have. Mx records must be + # followed to compare server names. + # We are also done if the recursion + # count has been exceeded. + if (&trhost($newmxhost) eq &trhost($server) || ($levels && $level{"$u *** $server"} >= $levels)) { + if ($valid) { + push(@isValid, &validAddr($newaddr)); + push(@toFinal,$newaddr,$newmxhost,$newname); + } else { + &verbose(&final($newaddr,$newmxhost,$newname)); + } + } else { + # more work to do... + if ($valid) { + push(@isValid, &validAddr($newaddr)); + push(@toExpn,$newmxhost,$newaddr,$newname,$level{"$u *** $server"}); + } else { + &verbose(&expn($newmxhost,$newaddr,$newname,$level{"$u *** $server"})); + } + } + } + last if ($done eq " "); + next; + } + # 550 is a known code... Should the be + # included in -a output? Might be a bug + # here. Does it matter? Can assume that + # there won't be UNKNOWN USER responces + # mixed with valid users? + if ($s =~ /^(550)([- ])/) { + if ($valid) { + print STDERR "\@$server:$u ($oldname) USER UNKNOWN\n"; + } else { + &verbose(&final($u,$server,$oldname,"USER UNKNOWN")); + } + last if ($2 eq " "); + next; + } + &giveup('',"$server: did not grok '$s'",$u); + last USER; + } + if (! $said_something) { + &giveup('',"$server: lost connection",$u); + last USER; + } + if ($valid) { + # + # now we decide if we are going to take these + # expansions or roll them back. + # + $avgValid = &average(@isValid); + print "avgValid = $avgValid\n" if $debug; + if ($avgValid >= $validRequirement) { + print &compact($u,$server)." ->\n" if $verbose; + while (@toExpn) { + &verbose(&expn(splice(@toExpn,0,4))); + } + while (@toFinal) { + &verbose(&final(splice(@toFinal,0,3))); + } + } else { + print "Tossing some valid to avoid invalid ".&compact($u,$server)."\n" if ($avgValid > 0.0 && ($vw || $debug)); + print &compact($u,$server)." ->\n" if $verbose; + &verbose(&final($u,$server,$newname)); + } + } + } + + $0 = "$av0 - sending 'quit' to $server"; + &ps("quit"); + while(<S>) { + print if $watch; + last if /^\d+ /; + } + close(S); +} + +$0 = "$av0 - printing final results"; +print "----------\n" if $vw; +select(STDOUT); +for $f (sort @final) { + print "$f\n"; +} +unlink("/tmp/expn$$"); +exit(0); + + +# abandon all attempts deliver to $server +# register the current addresses as the final ones +sub giveup +{ + local($redirect_okay,$reason,$user) = @_; + local($us,@so,$nh,@remaining_users); + + $0 = "$av0 - giving up on $server: $reason"; + # + # add back a user if we gave up in the middle + # + push(@users,$user) if $user; + # + # don't bother with this system anymore + # + unless ($giveup{$server}) { + $giveup{$server} = $reason; + print STDERR "$reason\n"; + } + print "Giveup!!! redirect okay = $redirect_okay; $reason\n" if $debug; + # + # Wait! + # Before giving up, see if there is a chance that + # there is another host to redirect to! + # (Kids, don't do this at home! Hacking is a dangerous + # crime and you could end up behind bars.) + # + for $u (@users) { + if ($redirect_okay =~ /\bmx\b/) { + next if &try_fallback('mx',$u,*server, + *mx_secondary, + *already_mx_fellback); + } + if ($redirect_okay =~ /\bdomainify\b/) { + next if &try_fallback('domainify',$u,*server, + *domainify_fallback, + *already_domainify_fellback); + } + push(@remaining_users,$u); + } + @users = @remaining_users; + for $u (@users) { + print &compact($u,$server)." ->\n" if ($verbose && $valid && $u); + &verbose(&final($u,$server,$names{"$u *** $server"},$reason)); + } +} +# +# This routine is used only within &giveup. It checks to +# see if we really have to giveup or if there is a second +# chance because we did something before that can be +# backtracked. +# +# %fallback{"$user *** $host"} tracks what is able to fallback +# %fellback{"$user *** $host"} tracks what has fallen back +# +# If there is a valid backtrack, then queue up the new possibility +# +sub try_fallback +{ + local($method,$user,*host,*fall_table,*fellback) = @_; + local($us,$fallhost,$oldhost,$ft,$i); + + if ($debug > 8) { + print "Fallback table $method:\n"; + for $i (sort keys %fall_table) { + print "\t'$i'\t\t'$fall_table{$i}'\n"; + } + print "Fellback table $method:\n"; + for $i (sort keys %fellback) { + print "\t'$i'\t\t'$fellback{$i}'\n"; + } + print "U: $user H: $host\n"; + } + + $us = "$user *** $host"; + if (defined $fellback{$us}) { + # + # Undo a previous fallback so that we can try again + # Nest fallbacks are avoided because they could + # lead to infinite loops + # + $fallhost = $fellback{$us}; + print "Already $method fell back from $us -> \n" if $debug; + $us = "$user *** $fallhost"; + $oldhost = $fallhost; + } elsif (($method eq 'mx') && (defined $mxbacktrace{$us}) && (defined $mx_secondary{$mxbacktrace{$us}})) { + print "Fallback an MX expansion $us -> \n" if $debug; + $oldhost = $mxbacktrace{$us}; + } else { + print "Oldhost(host, $us) = " if $debug; + $oldhost = $host; + } + print "$oldhost\n" if $debug; + if (((defined $fall_table{$us}) && ($ft = $us)) || ((defined $fall_table{$oldhost}) && ($ft = $oldhost))) { + print "$method Fallback = ".$fall_table{$ft}."\n" if $debug; + local(@so,$newhost); + @so = split(' ',$fall_table{$ft}); + $newhost = shift(@so); + print "Falling back ($method) $us -> $newhost (from $oldhost)\n" if $debug; + if ($method eq 'mx') { + if (! defined ($mxbacktrace{"$user *** $newhost"})) { + if (defined $mxbacktrace{"$user *** $oldhost"}) { + print "resetting oldhost $oldhost to the original: " if $debug; + $oldhost = $mxbacktrace{"$user *** $oldhost"}; + print "$oldhost\n" if $debug; + } + $mxbacktrace{"$user *** $newhost"} = $oldhost; + print "mxbacktrace $user *** $newhost -> $oldhost\n" if $debug; + } + $mx{&trhost($oldhost)} = $newhost; + } else { + $temporary_redirect{$us} = $newhost; + } + if (@so) { + print "Can still $method $us: @so\n" if $debug; + $fall_table{$ft} = join(' ',@so); + } else { + print "No more fallbacks for $us\n" if $debug; + delete $fall_table{$ft}; + } + if (defined $create_host_backtrack{$us}) { + $create_host_backtrack{"$user *** $newhost"} + = $create_host_backtrack{$us}; + } + $fellback{"$user *** $newhost"} = $oldhost; + &expn($newhost,$user,$names{$us},$level{$us}); + return 1; + } + delete $temporary_redirect{$us}; + $host = $oldhost; + return 0; +} +# return 1 if you could send mail to the address as is. +sub validAddr +{ + local($addr) = @_; + $res = &do_validAddr($addr); + print "validAddr($addr) = $res\n" if $debug; + $res; +} +sub do_validAddr +{ + local($addr) = @_; + local($urx) = "[-A-Za-z_.0-9+]+"; + + # \u + return 0 if ($addr =~ /^\\/); + # ?@h + return 1 if ($addr =~ /.\@$urx$/); + # @h:? + return 1 if ($addr =~ /^\@$urx\:./); + # h!u + return 1 if ($addr =~ /^$urx!./); + # u + return 1 if ($addr =~ /^$urx$/); + # ? + print "validAddr($addr) = ???\n" if $debug; + return 0; +} +# returns ($new_smtp_server,$new_address,$new_name) +# given a responce from a SMTP server ($newaddr), the +# current host ($server), the old "name" and a flag that +# indicates if it is being called during the initial +# command line parsing ($parsing_args) +sub parse +{ + local($newaddr,$context_host,$old_name,$parsing_args) = @_; + local(@names) = $old_name; + local($urx) = "[-A-Za-z_.0-9+]+"; + + # + # first, separate out the address part. + # + + # + # [NAME] <ADDR [(NAME)]> + # [NAME] <[(NAME)] ADDR + # ADDR [(NAME)] + # (NAME) ADDR + # [(NAME)] <ADDR> + # + if ($newaddr =~ /^\<(.*)\>$/) { + print "<A:$1>\n" if $debug; + $newaddr = &trim($1); + print "na = $newaddr\n" if $debug; + } + if ($newaddr =~ /^([^\<\>]*)\<([^\<\>]*)\>([^\<\>]*)$/) { + # address has a < > pair in it. + print "N:$1 <A:$2> N:$3\n" if $debug; + $newaddr = &trim($2); + unshift(@names, &trim($3,$1)); + print "na = $newaddr\n" if $debug; + } + if ($newaddr =~ /^([^\(\)]*)\(([^\(\)]*)\)([^\(\)]*)$/) { + # address has a ( ) pair in it. + print "A:$1 (N:$2) A:$3\n" if $debug; + unshift(@names,&trim($2)); + local($f,$l) = (&trim($1),&trim($3)); + if (($f && $l) || !($f || $l)) { + # address looks like: + # foo (bar) baz or (bar) + # not allowed! + print STDERR "Could not parse $newaddr\n" if $vw; + return(undef,$newaddr,&firstname(@names)); + } + $newaddr = $f if $f; + $newaddr = $l if $l; + print "newaddr now = $newaddr\n" if $debug; + } + # + # @foo:bar + # j%k@l + # a@b + # b!a + # a + # + if ($newaddr =~ /^\@($urx)\:(.+)$/) { + print "(\@:)" if $debug; + # this is a bit of a cheat, but it seems necessary + return (&domainify($1,$context_host,$2),$2,&firstname(@names)); + } + if ($newaddr =~ /^(.+)\@($urx)$/) { + print "(\@)" if $debug; + return (&domainify($2,$context_host,$newaddr),$newaddr,&firstname(@names)); + } + if ($parsing_args) { + if ($newaddr =~ /^($urx)\!(.+)$/) { + return (&domainify($1,$context_host,$newaddr),$newaddr,&firstname(@names)); + } + if ($newaddr =~ /^($urx)$/) { + return ($context_host,$newaddr,&firstname(@names)); + } + print STDERR "Could not parse $newaddr\n"; + } + print "(?)" if $debug; + return(undef,$newaddr,&firstname(@names)); +} +# return $u (@$server) unless $u includes reference to $server +sub compact +{ + local($u, $server) = @_; + local($se) = $server; + local($sp); + $se =~ s/(\W)/\\$1/g; + $sp = " (\@$server)"; + if ($u !~ /$se/i) { + return "$u$sp"; + } + return $u; +} +# remove empty (spaces don't count) members from an array +sub trim +{ + local(@v) = @_; + local($v,@r); + for $v (@v) { + $v =~ s/^\s+//; + $v =~ s/\s+$//; + push(@r,$v) if ($v =~ /\S/); + } + return(@r); +} +# using the host part of an address, and the server name, add the +# servers' domain to the address if it doesn't already have a +# domain. Since this sometimes failes, save a back reference so +# it can be unrolled. +sub domainify +{ + local($host,$domain_host,$u) = @_; + local($domain,$newhost); + + # cut of trailing dots + $host =~ s/\.$//; + $domain_host =~ s/\.$//; + + if ($domain_host !~ /\./) { + # + # domain host isn't, keep $host whatever it is + # + print "domainify($host,$domain_host) = $host\n" if $debug; + return $host; + } + + # + # There are several weird situtations that need to be + # accounted for. They have to do with domain relay hosts. + # + # Examples: + # host server "right answer" + # + # shiva.cs cs.berkeley.edu shiva.cs.berkeley.edu + # shiva cs.berkeley.edu shiva.cs.berekley.edu + # cumulus reed.edu @reed.edu:cumulus.uucp + # tiberius tc.cornell.edu tiberius.tc.cornell.edu + # + # The first try must always be to cut the domain part out of + # the server and tack it onto the host. + # + # A reasonable second try is to tack the whole server part onto + # the host and for each possible repeated element, eliminate + # just that part. + # + # These extra "guesses" get put into the %domainify_fallback + # array. They will be used to give addresses a second chance + # in the &giveup routine + # + + local(%fallback); + + local($long); + $long = "$host $domain_host"; + $long =~ tr/A-Z/a-z/; + print "long = $long\n" if $debug; + if ($long =~ s/^([^ ]+\.)([^ ]+) \2(\.[^ ]+\.[^ ]+)/$1$2$3/) { + # matches shiva.cs cs.berkeley.edu and returns shiva.cs.berkeley.edu + print "condensed fallback $host $domain_host -> $long\n" if $debug; + $fallback{$long} = 9; + } + + local($fh); + $fh = $domain_host; + while ($fh =~ /\./) { + print "FALLBACK $host.$fh = 1\n" if $debug > 7; + $fallback{"$host.$fh"} = 1; + $fh =~ s/^[^\.]+\.//; + } + + $fallback{"$host.$domain_host"} = 2; + + ($domain = $domain_host) =~ s/^[^\.]+//; + $fallback{"$host$domain"} = 6 + if ($domain =~ /\./); + + if ($host =~ /\./) { + # + # Host is already okay, but let's look for multiple + # interpretations + # + print "domainify($host,$domain_host) = $host\n" if $debug; + delete $fallback{$host}; + $domainify_fallback{"$u *** $host"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback; + return $host; + } + + $domain = ".$domain_host" + if ($domain !~ /\..*\./); + $newhost = "$host$domain"; + + $create_host_backtrack{"$u *** $newhost"} = $domain_host; + print "domainify($host,$domain_host) = $newhost\n" if $debug; + delete $fallback{$newhost}; + $domainify_fallback{"$u *** $newhost"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback; + if ($debug) { + print "fallback = "; + print $domainify_fallback{"$u *** $newhost"} + if defined($domainify_fallback{"$u *** $newhost"}); + print "\n"; + } + return $newhost; +} +# return the first non-empty element of an array +sub firstname +{ + local(@names) = @_; + local($n); + while(@names) { + $n = shift(@names); + return $n if $n =~ /\S/; + } + return undef; +} +# queue up more addresses to expand +sub expn +{ + local($host,$addr,$name,$level) = @_; + if ($host) { + $host = &trhost($host); + + if (($debug > 3) || (defined $giveup{$host})) { + unshift(@hosts,$host) unless $users{$host}; + } else { + push(@hosts,$host) unless $users{$host}; + } + $users{$host} .= " $addr"; + $names{"$addr *** $host"} = $name; + $level{"$addr *** $host"} = $level + 1; + print "expn($host,$addr,$name)\n" if $debug; + return "\t$addr\n"; + } else { + return &final($addr,'NONE',$name); + } +} +# compute the numerical average value of an array +sub average +{ + local(@e) = @_; + return 0 unless @e; + local($e,$sum); + for $e (@e) { + $sum += $e; + } + $sum / @e; +} +# print to the server (also to stdout, if -w) +sub ps +{ + local($p) = @_; + print ">>> $p\n" if $watch; + print S "$p\n"; +} +# return case-adjusted name for a host (for comparison purposes) +sub trhost +{ + # treat foo.bar as an alias for Foo.BAR + local($host) = @_; + local($trhost) = $host; + $trhost =~ tr/A-Z/a-z/; + if ($trhost{$trhost}) { + $host = $trhost{$trhost}; + } else { + $trhost{$trhost} = $host; + } + $trhost{$trhost}; +} +# re-queue users if an mx record dictates a redirect +# don't allow a user to be redirected more than once +sub mxredirect +{ + local($server,*users) = @_; + local($u,$nserver,@still_there); + + $nserver = &mx($server); + + if (&trhost($nserver) ne &trhost($server)) { + $0 = "$av0 - mx redirect $server -> $nserver\n"; + for $u (@users) { + if (defined $mxbacktrace{"$u *** $nserver"}) { + push(@still_there,$u); + } else { + $mxbacktrace{"$u *** $nserver"} = $server; + print "mxbacktrace{$u *** $nserver} = $server\n" + if ($debug > 1); + &expn($nserver,$u,$names{"$u *** $server"}); + } + } + @users = @still_there; + if (! @users) { + return $nserver; + } else { + return undef; + } + } + return undef; +} +# follow mx records, return a hostname +# also follow temporary redirections comming from &domainify and +# &mxlookup +sub mx +{ + local($h,$u) = @_; + + for (;;) { + if (defined $mx{&trhost($h)} && $h ne $mx{&trhost($h)}) { + $0 = "$av0 - mx expand $h"; + $h = $mx{&trhost($h)}; + return $h; + } + if ($u) { + if (defined $temporary_redirect{"$u *** $h"}) { + $0 = "$av0 - internal redirect $h"; + print "Temporary redirect taken $u *** $h -> " if $debug; + $h = $temporary_redirect{"$u *** $h"}; + print "$h\n" if $debug; + next; + } + $htr = &trhost($h); + if (defined $temporary_redirect{"$u *** $htr"}) { + $0 = "$av0 - internal redirect $h"; + print "temporary redirect taken $u *** $h -> " if $debug; + $h = $temporary_redirect{"$u *** $htr"}; + print "$h\n" if $debug; + next; + } + } + return $h; + } +} +# look up mx records with the name server. +# re-queue expansion requests if possible +# optionally give up on this host. +sub mxlookup +{ + local($lastchance,$server,$giveup,*users) = @_; + local(*T); + local(*NSLOOKUP); + local($nh, $pref,$cpref); + local($o0) = $0; + local($nserver); + local($name,$aliases,$type,$len,$thataddr); + local(%fallback); + + return 1 if &mxredirect($server,*users); + + if ((defined $mx{$server}) || (! $have_nslookup)) { + return 0 unless $lastchance; + &giveup('mx domainify',$giveup); + return 0; + } + + $0 = "$av0 - nslookup of $server"; + open(T,">/tmp/expn$$") || die "open > /tmp/expn$$: $!\n"; + print T "set querytype=MX\n"; + print T "$server\n"; + close(T); + $cpref = 1.0E12; + undef $nserver; + open(NSLOOKUP,"nslookup < /tmp/expn$$ 2>&1 |") || die "open nslookup: $!"; + while(<NSLOOKUP>) { + print if ($debug > 2); + if (/mail exchanger = ([-A-Za-z_.0-9+]+)/) { + $nh = $1; + if (/preference = (\d+)/) { + $pref = $1; + if ($pref < $cpref) { + $nserver = $nh; + $cpref = $pref; + } elsif ($pref) { + $fallback{$pref} .= " $nh"; + } + } + } + if (/Non-existent domain/) { + # + # These addresss are hosed. Kaput! Dead! + # However, if we created the address in the + # first place then there is a chance of + # salvation. + # + 1 while(<NSLOOKUP>); + close(NSLOOKUP); + return 0 unless $lastchance; + &giveup('domainify',"$server: Non-existent domain",undef,1); + return 0; + } + + } + close(NSLOOKUP); + unlink("/tmp/expn$$"); + unless ($nserver) { + $0 = "$o0 - finished mxlookup"; + return 0 unless $lastchance; + &giveup('mx domainify',"$server: Could not resolve address"); + return 0; + } + + # provide fallbacks in case $nserver doesn't work out + if (defined $fallback{$cpref}) { +# for $u (@users) { +# print "mx_secondary{$u *** $nserver} = ".$fallback{$cpref}."\n" +# if $debug; +# $mx_secondary{"$u *** $nserver"} = $fallback{$cpref}; +# } + $mx_secondary{$server} = $fallback{$cpref}; + } + + $0 = "$av0 - gethostbyname($nserver)"; + ($name,$aliases,$type,$len,$thataddr) = gethostbyname($nserver); + + unless ($thataddr) { + $0 = $o0; + return 0 unless $lastchance; + &giveup('mx domainify',"$nserver: could not resolve address"); + return 0; + } + print "MX($server) = $nserver\n" if $debug; + print "$server -> $nserver\n" if $vw && !$debug; + $mx{&trhost($server)} = $nserver; + # redeploy the users + unless (&mxredirect($server,*users)) { + return 0 unless $lastchance; + &giveup('mx domainify',"$nserver: only one level of mx redirect allowed"); + return 0; + } + $0 = "$o0 - finished mxlookup"; + return 1; +} +# if mx expansion did not help to resolve an address +# (ie: foo@bar became @baz:foo@bar, then undo the +# expansion). +# this is only used by &final +sub mxunroll +{ + local(*host,*addr) = @_; + local($r) = 0; + print "looking for mxbacktrace{$addr *** $host}\n" + if ($debug > 1); + while (defined $mxbacktrace{"$addr *** $host"}) { + print "Unrolling MX expnasion: \@$host:$addr -> " + if ($debug || $verbose); + $host = $mxbacktrace{"$addr *** $host"}; + print "\@$host:$addr\n" + if ($debug || $verbose); + $r = 1; + } + return 1 if $r; + $addr = "\@$host:$addr" + if ($host =~ /\./); + return 0; +} +# register a completed expnasion. Make the final address as +# simple as possible. +sub final +{ + local($addr,$host,$name,$error) = @_; + local($he); + local($hb,$hr); + local($au,$ah); + + if ($error =~ /Non-existent domain/) { + # + # If we created the domain, then let's undo the + # damage... + # + if (defined $create_host_backtrack{"$addr *** $host"}) { + while (defined $create_host_backtrack{"$addr *** $host"}) { + print "Un&domainifying($host) = " if $debug; + $host = $create_host_backtrack{"$addr *** $host"}; + print "$host\n" if $debug; + } + $error = "$host: could not locate"; + } else { + # + # If we only want valid addresses, toss out + # bad host names. + # + if ($valid) { + print STDERR "\@$host:$addr ($name) Non-existent domain\n"; + return ""; + } + } + } + + MXUNWIND: { + $0 = "$av0 - final parsing of \@$host:$addr"; + ($he = $host) =~ s/(\W)/\\$1/g; + if ($addr !~ /@/) { + # addr does not contain any host + $addr = "$addr@$host"; + } elsif ($addr !~ /$he/i) { + # if host part really something else, use the something + # else. + if ($addr =~ m/(.*)\@([^\@]+)$/) { + ($au,$ah) = ($1,$2); + print "au = $au ah = $ah\n" if $debug; + if (defined $temporary_redirect{"$addr *** $ah"}) { + $addr = "$au\@".$temporary_redirect{"$addr *** $ah"}; + print "Rewrite! to $addr\n" if $debug; + next MXUNWIND; + } + } + # addr does not contain full host + if ($valid) { + if ($host =~ /^([^\.]+)(\..+)$/) { + # host part has a . in it - foo.bar + ($hb, $hr) = ($1, $2); + if ($addr =~ /\@([^\.\@]+)$/ && ($1 eq $hb)) { + # addr part has not . + # and matches beginning of + # host part -- tack on a + # domain name. + $addr .= $hr; + } else { + &mxunroll(*host,*addr) + && redo MXUNWIND; + } + } else { + &mxunroll(*host,*addr) + && redo MXUNWIND; + } + } else { + $addr = "${addr}[\@$host]" + if ($host =~ /\./); + } + } + } + $name = "$name " if $name; + $error = " $error" if $error; + if ($valid) { + push(@final,"$name<$addr>"); + } else { + push(@final,"$name<$addr>$error"); + } + "\t$name<$addr>$error\n"; +} +# read the rest of the current smtp daemon's responce (and toss it away) +sub toss +{ + local($done) = @_; + print $s if $watch; + while(($done eq "-") && ($s = <S>) && ($s =~ /^\d+([- ])/)) { + print $s if $watch; + $done = $1; + } +} +# print args if verbose. Return them in any case +sub verbose +{ + local(@tp) = @_; + print "@tp" if $verbose; +} +# to pass perl -w: +@tp; +$flag_a; +$flag_d; +$flag_1; +%already_domainify_fellback; +%already_mx_fellback; +################### BEGIN PERL/TROFF TRANSITION +.00; + +'di \\ " finish diversion--previous line must be blank +.nr nl 0-1 \\ " fake up transition to first page again +.nr % 0 \\ " start at page 1 +'; __END__ +.\" ############### END PERL/TROFF TRANSITION +.TH EXPN 1 "March 11, 1993" +.AT 3 +.SH NAME +expn \- recursively expand mail aliases +.SH SYNOPSIS +.B expn +.RI [ -a ] +.RI [ -v ] +.RI [ -w ] +.RI [ -d ] +.IR user [@ hostname ] +.RI [ user [@ hostname ]]... +.SH DESCRIPTION +.B expn +will use the SMTP +.B expn +and +.B vrfy +commands to expand mail aliases. +It will first look up the addresses you provide on the command line. +If those expand into addresses on other systems, it will +connect to the other systems and expand again. It will keep +doing this until no further expansion is possible. +.SH OPTIONS +The default output of +.B expn +can contain many lines which are not valid +email addresses. With the +.I -aa +flag, only expansions that result in legal addresses +are used. Since many mailing lists have an illegal +address or two, the single +.IR -a , +address, flag specifies that a few illegal addresses can +be mixed into the results. More +.I -a +flags vary the ratio. Read the source to track down +the formula. With the +.I -a +option, you should be able to construct a new mailing +list out of an existing one. +.LP +If you wish to limit the number of levels deep that +.B expn +will recurse as it traces addresses, use the +.I -1 +option. For each +.I -1 +another level will be traversed. So, +.I -111 +will traverse no more than three levels deep. +.LP +The normal mode of operation for +.B expn +is to do all of its work silently. +The following options make it more verbose. +It is not necessary to make it verbose to see what it is +doing because as it works, it changes its +.BR argv [0] +variable to reflect its current activity. +To see how it is expanding things, the +.IR -v , +verbose, flag will cause +.B expn +to show each address before +and after translation as it works. +The +.IR -w , +watch, flag will cause +.B expn +to show you its conversations with the mail daemons. +Finally, the +.IR -d , +debug, flag will expose many of the inner workings so that +it is possible to eliminate bugs. +.SH ENVIRONMENT +No enviroment variables are used. +.SH FILES +.PD 0 +.B /tmp/expn$$ +.B temporary file used as input to +.BR nslookup . +.SH SEE ALSO +.BR aliases (5), +.BR sendmail (8), +.BR nslookup (8), +RFC 823, and RFC 1123. +.SH BUGS +Not all mail daemons will implement +.B expn +or +.BR vrfy . +It is not possible to verify addresses that are served +by such daemons. +.LP +When attempting to connect to a system to verify an address, +.B expn +only tries one IP address. Most mail daemons +will try harder. +.LP +It is assumed that you are running domain names and that +the +.BR nslookup (8) +program is available. If not, +.B expn +will not be able to verify many addresses. It will also pause +for a long time unless you change the code where it says +.I $have_nslookup = 1 +to read +.I $have_nslookup = +.IR 0 . +.LP +Lastly, +.B expn +does not handle every valid address. If you have an example, +please submit a bug report. +.SH CREDITS +In 1986 or so, Jon Broome wrote a program of the same name +that did about the same thing. It has since suffered bit rot +and Jon Broome has dropped off the face of the earth! +(Jon, if you are out there, drop me a line) +.SH AVAILABILITY +The latest version of +.B expn +is available through anonymous ftp to +.IR idiom.berkeley.ca.us . +.SH AUTHOR +.I David Muir Sharnoff\ \ \ \ <muir@idiom.berkeley.ca.us> diff --git a/usr.sbin/sendmail/contrib/mmuegel b/usr.sbin/sendmail/contrib/mmuegel new file mode 100644 index 0000000000..f4cfbd80f6 --- /dev/null +++ b/usr.sbin/sendmail/contrib/mmuegel @@ -0,0 +1,2073 @@ +Return-Path: mmuegel@cssmp.corp.mot.com +Received: from hofmann.CS.Berkeley.EDU by auspex.Berkeley.EDU (ALPHA-6.30/6.9) id AA02096; Sun, 11 Apr 1993 19:50:02 -0700 +Received: from motgate.mot.com by hofmann.CS.Berkeley.EDU (ALPHA-6.35/6.16) id AA14977; Sun, 11 Apr 1993 19:49:57 -0700 +Received: from pobox.mot.com ([129.188.137.100]) by motgate.mot.com with SMTP (5.65c/IDA-1.4.4/MOT-2.13 for <eric@cs.berkeley.edu>) + id AA05603; Sun, 11 Apr 1993 21:49:54 -0500 +Received: from cssmp.corp.mot.com by pobox.mot.com with SMTP (5.65c/IDA-1.4.4/MOT-2.12 for <eric@cs.berkeley.edu>) + id AA08281; Sun, 11 Apr 1993 21:49:51 -0500 +Received: by cssmp.corp.mot.com (5.65c/IDA-1.4.4/MOT-2.12 for eric@cs.berkeley.edu) + id AA02812; Sun, 11 Apr 1993 21:49:48 -0500 +From: "Michael S. Muegel" <mmuegel@cssmp.corp.mot.com> +Message-Id: <199304120249.AA02812@cssmp.corp.mot.com> +Subject: Sendmail tools README +To: eric@cs.berkeley.edu (Eric Allman) +Date: Sun, 11 Apr 1993 21:49:48 -0500 (CDT) +Cc: costales@icsi.berkeley.edu (Bryan Costales) +X-Mailer: ELM [version 2.4 PL17] +Mime-Version: 1.0 +Content-Type: text/plain; charset=US-ASCII +Content-Transfer-Encoding: 7bit +Content-Length: 67910 + + +As promised, here is a new distribution with a decent README. + +Cheers, +-Mike + +---- Cut Here and feed the following to sh ---- +#!/bin/sh +# This is a shell archive (produced by shar 3.49) +# To extract the files from this archive, save it to a file, remove +# everything above the "!/bin/sh" line above, and type "sh file_name". +# +# made 04/12/1993 02:34 UTC by mmuegel@mot.com (Michael S. Muegel) +# Source directory /usr/var/rtmp/shar2336 +# +# existing files will NOT be overwritten unless -c is specified +# +# This shar contains: +# length mode name +# ------ ---------- ------------------------------------------ +# 4367 -r--r--r-- README +# 11619 -r--r--r-- libs/date.pl +# 3243 -r--r--r-- libs/elapsed.pl +# 4379 -r--r--r-- libs/mail.pl +# 6953 -r--r--r-- libs/mqueue.pl +# 7030 -r--r--r-- libs/newgetopts.pl +# 4718 -r--r--r-- libs/strings1.pl +# 1637 -r--r--r-- libs/timespec.pl +# 5229 -r--r--r-- man/cqueue.1 +# 2097 -r--r--r-- man/postclip.1 +# 6702 -r-xr-xr-x src/cqueue +# 1900 -r-xr-xr-x src/postclip +# +# ============= README ============== +if test -f 'README' -a X"$1" != X"-c"; then + echo 'x - skipping README (File already exists)' +else +echo 'x - extracting README (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'README' && +------------------------------------------------------------------------------- +Document Revision Control Information: +X $Author: glass $ +X $Source: /b/source/CVS/src/usr.sbin/sendmail/contrib/mmuegel,v $ +X $Revision: 1.3 $ of $Date: 1993/07/20 18:11:05 $ +------------------------------------------------------------------------------- +X +1. Introduction +--------------- +X +These tools may be of use to those sites using sendmail. Both are written in +Perl. Our site, Mot.COM, receives a ton of mail being a top-level domain +gateway. We have over 24 domains under us. Needless to say, we must have +a robust mail system or my head, and others, would be on the chopping block. +X +2. Description +-------------- +X +The first tool, cqueue, checks the sendmail queue for problems. We use +it to flag problems with subdomain mail servers (and even our own servers +once in a while ;-). We run it via a cron job every hour during the day. +You may find this too frequent, however. +X +The other program, postclip, is used to "filter" non-deliverable NDNs that +get sent to our Postmaster account now and then. This ensures privacy of +e-mail and helps avoid disk problems from huge NDNs. It is different than +a brute force "just keep the header" approach because it tries hard to keep +other parts of the message that look like non-delivery information. +X +Both have been used for some time at our site with no problems. Everything +you need should be in this distribution: source, manual pages, and support +libs. See the manual pages for a complete description of each tool. +X +3. Installation +--------------- +X +No fancy Makefile simply because these tools are all under a large +hierarchy at my site. Installation should be a snap, however. Install +the nroff(1) man(5) manual pages from the man subdirectory to the +appropriate directory on your system. This might be something like +/usr/local/man/man1. +X +Next, install all of the Perl libraries located in the lib subdirectory +to your Perl library area. /usr/local/lib/perl is a good bet. The person +who installed Perl at your site will be able to tell you for sure. +X +Finally, you need to install the programs. Note that cqueue wants to +run setuid root by default. This is because the sendmail queue is normally +only readable by root or some special group. In order to let any user +run this suidperl is used. suidperl allows a Perl program to run with the +privileges of another user. +X +You will have to edit both the cqueue and postclip programs to change +the #! line at the top of each. Just change the pathname to whatever is +appropriate on your system. Note that Larry Wall's fixin program from +the Camel book can also be used to do this. It is very handy. It changes +#! lines by looking at your PATH. +X +If you do not have suidperl on your system change the #! line in cqueue +to reference perl instead of suidperl. +X +You may also wish to change some constants in cqueue. $DEF_QUEUE should be +changed to your queue directory if it is not /usr/spool/mqueue. $DEF_TIME +could be changed easy enough also. It is the time spec for the time duration +after which a mail message will be reported on if the -a option has not been +specified. See the manual page for more information and the format of this +constant (same as the -t argument). Then again, neither of these has to +be changed. Command line options are there to override their default +values. +X +After you have edited the programs as necessary, all that remains is to +install them to some executable directory. Install postclip mode 555 +and cqueue mode 4555 with owner root (if using suidperl) or mode 555 +(if not using suidperl). +X +4. Gripes, Comments, Etc +------------------------ +X +If you start using either of these let me know. I have other mail tools I +will likely post in the future if these prove useful. Also, if you think +something is just plain dumb/wrong/stupid let me know! +X +Cheers, +-Mike +X +-- ++----------------------------------------------------------------------------+ +| Michael S. Muegel | Internet E-Mail: mmuegel@mot.com | +| UNIX Applications Startup Group | Moto Dist E-Mail: X10090 | +| Corporate Information Office | Voice: (708) 576-0507 | +| Motorola | Fax: (708) 576-4153 | ++----------------------------------------------------------------------------+ +SHAR_EOF +chmod 0444 README || +echo 'restore of README failed' +Wc_c="`wc -c < 'README'`" +test 4367 -eq "$Wc_c" || + echo 'README: original size 4367, current size' "$Wc_c" +fi +# ============= libs/date.pl ============== +if test ! -d 'libs'; then + echo 'x - creating directory libs' + mkdir 'libs' +fi +if test -f 'libs/date.pl' -a X"$1" != X"-c"; then + echo 'x - skipping libs/date.pl (File already exists)' +else +echo 'x - extracting libs/date.pl (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'libs/date.pl' && +;# +;# Name +;# date.pl - Perl emulation of (the output side of) date(1) +;# +;# Synopsis +;# require "date.pl"; +;# $Date = &date(time); +;# $Date = &date(time, $format); +;# +;# Description +;# This package implements the output formatting functions of date(1) in +;# Perl. The format options are based on those supported by Ultrix 4.0 +;# plus a couple of additions from SunOS 4.1.1 and elsewhere: +;# +;# %a abbreviated weekday name - Sun to Sat +;# %A full weekday name - Sunday to Saturday +;# %b abbreviated month name - Jan to Dec +;# %B full month name - January to December +;# %c date and time in local format [+] +;# %C date and time in long local format [+] +;# %d day of month - 01 to 31 +;# %D date as mm/dd/yy +;# %e day of month (space padded) - ` 1' to `31' +;# %E day of month (with suffix: 1st, 2nd, 3rd...) +;# %f month of year (space padded) - ` 1' to `12' +;# %h abbreviated month name - Jan to Dec +;# %H hour - 00 to 23 +;# %i hour (space padded) - ` 1' to `12' +;# %I hour - 01 to 12 +;# %j day of the year (Julian date) - 001 to 366 +;# %k hour (space padded) - ` 0' to `23' +;# %l date in ls(1) format +;# %m month of year - 01 to 12 +;# %M minute - 00 to 59 +;# %n insert a newline character +;# %p AM or PM +;# %r time in AM/PM notation +;# %R time as HH:MM +;# %S second - 00 to 59 +;# %t insert a tab character +;# %T time as HH:MM:SS +;# %u date/time in date(1) required format +;# %U week number, Sunday as first day of week - 00 to 53 +;# %V date-time in SysV touch format (mmddHHMMyy) +;# %w day of week - 0 (Sunday) to 6 +;# %W week number, Monday as first day of week - 00 to 53 +;# %x date in local format [+] +;# %X time in local format [+] +;# %y last 2 digits of year - 00 to 99 +;# %Y all 4 digits of year ~ 1700 to 2000 odd ? +;# %z time zone from TZ environment variable w/ a trailing space +;# %Z time zone from TZ environment variable +;# %% insert a `%' character +;# %+ insert a `+' character +;# +;# [+]: These may need adjustment to fit local conventions, see below. +;# +;# For the sake of compatibility, a leading `+' in the format +;# specificaiton is removed if present. +;# +;# Remarks +;# This is version 3.3 of date.pl +;# +;# An extension of `ctime.pl' by Waldemar Kebsch (kebsch.pad@nixpbe.UUCP), +;# as modified by Marion Hakanson (hakanson@ogicse.ogi.edu). +;# +;# Unlike date(1), unknown format tags are silently replaced by "". +;# +;# defaultTZ is a blatant hack, but I wanted to be able to get date(1) +;# like behaviour by default and there does'nt seem to be an easy (read +;# portable) way to get the local TZ name back... +;# +;# For a cheap date, try... +;# +;# #!/usr/local/bin/perl +;# require "date.pl"; +;# exit print (&date(time, shift @ARGV) . "\n") ? 0 : 1; +;# +;# This package is redistributable under the same terms as apply to +;# the Perl 4.0 release. See the COPYING file in your Perl kit for +;# more information. +;# +;# Please send any bug reports or comments to tmcgonigal@gvc.com +;# +;# Modification History +;# Nmemonic Version Date Who +;# +;# NONE 1.0 02feb91 Terry McGonigal (tmcgonigal@gvc.com) +;# Created from ctime.pl +;# +;# NONE 2.0 07feb91 tmcgonigal +;# Added some of Marion Hakanson (hakanson@ogicse.ogi.edu)'s ctime.pl +;# TZ handling changes. +;# +;# NONE 2.1 09feb91 tmcgonigal +;# Corrected week number calculations. +;# +;# NONE 2.2 21oct91 tmcgonigal +;# Added ls(1) date format, `%l'. +;# +;# NONE 2.3 06nov91 tmcgonigal +;# Added SysV touch(1) date-time format, `%V' (pretty thin as +;# mnemonics go, I know, but `t' and `T' were both gone already!) +;# +;# NONE 2.4 05jan92 tmcgonigal +;# Corrected slight (cosmetic) problem with %V replacment string +;# +;# NONE 3.0 09jul92 tmcgonigal +;# Fixed a couple of problems with &ls as pointed out by +;# Thomas Richter (richter@ki1.chemie.fu-berlin.de), thanks Thomas! +;# Also added a couple of SunOS 4.1.1 strftime-ish formats, %i and %k +;# for space padded hours (` 1' to `12' and ` 0' to `23' respectivly), +;# and %C for locale long date/time format. Changed &mH to take a +;# pad char parameter to make to evaled code for %i and %k simpler. +;# Added %E for suffixed day-of-month (ie 1st, 3rd, 4th etc). +;# +;# NONE 3.1 16jul92 tmcgonigal +;# Added `%u' format to generate date/time in date(1) required +;# format (ie '%y%m%d%H%M.%S'). +;# +;# NONE 3.2 23jan93 tmcgonigal +;# Added `%f' format to generate space padded month numbers, added +;# `%E' to the header comments, it seems to have been left out (and +;# I'm sure I wanted to use it at some point in the past...). +;# +;# NONE 3.3 03feb93 tmcgonigal +;# Corrected some problems with AM/PM handling pointed out by +;# Michael S. Muegel (mmuegel@mot.com). Thanks Michael, I hope +;# this is the behaviour you were looking for, it seems more +;# correct to me... +;# +;# SccsId = "%W% %E%" +;# +package date; +X +# Months of the year +@MoY = ('January', 'Febuary', 'March', 'April', 'May', 'June', +X 'July', 'August', 'September','October', 'November', 'December'); +X +# days of the week +@DoW = ('Sunday', 'Monday', 'Tuesday', 'Wednesday', +X 'Thursday', 'Friday', 'Saturday'); +X +# CUSTOMIZE - defaults +$defaultTZ = 'CST'; # time zone (hack!) +$defaultFMT = '%a %h %e %T %z%Y'; # format (ala date(1)) +X +# CUSTOMIZE - `local' formats +$locTF = '%T'; # time (as HH:MM:SS) +$locDF = '%D'; # date (as mm/dd/yy) +$locDTF = '%a %b %d %T %Y'; # date/time (as dow mon dd HH:MM:SS yyyy) +$locLDTF = '%i:%M:%S %p %A %B %E %Y'; # long date/time (as HH:MM:SS a/p day month dom yyyy) +X +# Time zone info +$TZ; # wkno needs this info too +X +# define the known format tags as associative keys with their associated +# replacement strings as values. Each replacement string should be +# an eval-able expresion assigning a value to $rep. These expressions are +# eval-ed, then the value of $rep is substituted into the supplied +# format (if any). +%Tags = ( '%a', q|($rep = $DoW[$wday])=~ s/^(...).*/\1/|, # abbr. weekday name - Sun to Sat +X '%A', q|$rep = $DoW[$wday]|, # full weekday name - Sunday to Saturday +X '%b', q|($rep = $MoY[$mon]) =~ s/^(...).*/\1/|, # abbr. month name - Jan to Dec +X '%B', q|$rep = $MoY[$mon]|, # full month name - January to December +X '%c', q|$rep = $locDTF; 1|, # date/time in local format +X '%C', q|$rep = $locLDTF; 1|, # date/time in local long format +X '%d', q|$rep = &date'pad($mday, 2, "0")|, # day of month - 01 to 31 +X '%D', q|$rep = '%m/%d/%y'|, # date as mm/dd/yy +X '%e', q|$rep = &date'pad($mday, 2, " ")|, # day of month (space padded) ` 1' to `31' +X '%E', q|$rep = &date'dsuf($mday)|, # day of month (w/suffix) `1st' to `31st' +X '%f', q|$rep = &date'pad($mon+1, 2, " ")|, # month of year (space padded) ` 1' to `12' +X '%h', q|$rep = '%b'|, # abbr. month name (same as %b) +X '%H', q|$rep = &date'pad($hour, 2, "0")|, # hour - 00 to 23 +X '%i', q|$rep = &date'ampmH($hour, " ")|, # hour (space padded ` 1' to `12' +X '%I', q|$rep = &date'ampmH($hour, "0")|, # hour - 01 to 12 +X '%j', q|$rep = &date'pad($yday+1, 3, "0")|, # Julian date 001 - 366 +X '%k', q|$rep = &date'pad($hour, 2, " ")|, # hour (space padded) ` 0' to `23' +X '%l', q|$rep = '%b %d ' . &date'ls($year)|, # ls(1) style date +X '%m', q|$rep = &date'pad($mon+1, 2, "0")|, # month of year - 01 to 12 +X '%M', q|$rep = &date'pad($min, 2, "0")|, # minute - 00 to 59 +X '%n', q|$rep = "\n"|, # insert a newline +X '%p', q|$rep = &date'ampmD($hour)|, # insert `AM' or `PM' +X '%r', q|$rep = '%I:%M:%S %p'|, # time in AM/PM notation +X '%R', q|$rep = '%H:%M'|, # time as HH:MM +X '%S', q|$rep = &date'pad($sec, 2, "0")|, # second - 00 to 59 +X '%t', q|$rep = "\t"|, # insert a tab +X '%T', q|$rep = '%H:%M:%S'|, # time as HH:MM:SS +X '%u', q|$rep = '%y%m%d%H%M.%S'|, # daaate/time in date(1) required format +X '%U', q|$rep = &date'wkno($yday, 0)|, # week number (weeks start on Sun) - 00 to 53 +X '%V', q|$rep = '%m%d%H%M%y'|, # SysV touch(1) date-time format (mmddHHMMyy) +X '%w', q|$rep = $wday; 1|, # day of week - Sunday = 0 +X '%W', q|$rep = &date'wkno($yday, 1)|, # week number (weeks start on Mon) - 00 to 53 +X '%x', q|$rep = $locDF; 1|, # date in local format +X '%X', q|$rep = $locTF; 1|, # time in local format +X '%y', q|($rep = $year) =~ s/..(..)/\1/|, # last 2 digits of year - 00 to 99 +X '%Y', q|$rep = "$year"; 1|, # full year ~ 1700 to 2000 odd +X '%z', q|$rep = $TZ eq "" ? "" : "$TZ "|, # time zone from TZ env var (w/trail. space) +X '%Z', q|$rep = $TZ; 1|, # time zone from TZ env. var. +X '%%', q|$rep = '%'; $adv=1|, # insert a `%' +X '%+', q|$rep = '+'| # insert a `+' +); +X +sub main'date { +X local($time, $format) = @_; +X local($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst); +X local($pos, $tag, $rep, $adv) = (0, "", "", 0); +X +X # default to date/ctime format or strip leading `+'... +X if ($format eq "") { +X $format = $defaultFMT; +X } elsif ($format =~ /^\+/) { +X $format = $'; +X } +X +X # Use local time if can't find a TZ in the environment +X $TZ = defined($ENV{'TZ'}) ? $ENV{'TZ'} : $defaultTZ; +X ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = +X &gettime ($TZ, $time); +X +X # Hack to deal with 'PST8PDT' format of TZ +X # Note that this can't deal with all the esoteric forms, but it +X # does recognize the most common: [:]STDoff[DST[off][,rule]] +X if ($TZ =~ /^([^:\d+\-,]{3,})([+-]?\d{1,2}(:\d{1,2}){0,2})([^\d+\-,]{3,})?/) { +X $TZ = $isdst ? $4 : $1; +X } +X +X # watch out in 2070... +X $year += ($year < 70) ? 2000 : 1900; +X +X # now loop throught the supplied format looking for tags... +X while (($pos = index ($format, '%')) != -1) { +X +X # grab the format tag +X $tag = substr($format, $pos, 2); +X $adv = 0; # for `%%' processing +X +X # do we have a replacement string? +X if (defined $Tags{$tag}) { +X +X # trap dead evals... +X if (! eval $Tags{$tag}) { +X print STDERR "date.pl: internal error: eval for $tag failed.\n"; +X return ""; +X } +X } else { +X $rep = ""; +X } +X +X # do the substitution +X substr ($format, $pos, 2) =~ s/$tag/$rep/; +X $pos++ if ($adv); +X } +X +X $format; +} +X +# dsuf - add `st', `nd', `rd', `th' to a date (ie 1st, 22nd, 29th) +sub dsuf { +X local ($mday) = @_; +X +X return $mday . 'st' if ($mday =~ m/.*1$/); +X return $mday . 'nd' if ($mday =~ m/.*2$/); +X return $mday . 'rd' if ($mday =~ m/.*3$/); +X return $mday . 'th'; +} +X +# weekno - figure out week number +sub wkno { +X local ($yday, $firstweekday) = @_; +X local ($jan1, @jan1, $wks); +X local ($now) = time; +X +X # figure out the `time' value for January 1 +X $jan1 = $now - ((&gettime ($TZ, $now))[7] * 86400); # 86400 sec/day +X +X # figure out what day of the week January 1 was +X @jan1= &gettime ($TZ, $jan1); +X +X # and calculate the week number +X $wks = (($yday + ($jan1[6] - $firstweekday)) + 1)/ 7; +X $wks += (($wks - int($wks) > 0.0) ? 1 : 0); +X +X # supply zero padding +X &pad (int($wks), 2, "0"); +} +X +# ampmH - figure out am/pm (1 - 12) mode hour value, padded with $p (0 or ' ') +sub ampmH { local ($h, $p) = @_; &pad($h>12 ? $h-12 : ($h ? $h : 12), 2, $p); } +X +# ampmD - figure out am/pm designator +sub ampmD { shift @_ >= 12 ? "PM" : "AM"; } +X +# gettime - get the time via {local,gmt}time +sub gettime { ((shift @_) eq 'GMT') ? gmtime(shift @_) : localtime(shift @_); } +X +# ls - generate the time/year portion of an ls(1) style date +sub ls { +X return ((&gettime ($TZ, time))[5] == @_[0]) ? "%R" : " %Y"; +} +X +# pad - pad $in with leading $pad until lenght $len +sub pad { +X local ($in, $len, $pad) = @_; +X local ($out) = "$in"; +X +X $out = $pad . $out until (length ($out) == $len); +X return $out; +} +X +1; +SHAR_EOF +chmod 0444 libs/date.pl || +echo 'restore of libs/date.pl failed' +Wc_c="`wc -c < 'libs/date.pl'`" +test 11619 -eq "$Wc_c" || + echo 'libs/date.pl: original size 11619, current size' "$Wc_c" +fi +# ============= libs/elapsed.pl ============== +if test -f 'libs/elapsed.pl' -a X"$1" != X"-c"; then + echo 'x - skipping libs/elapsed.pl (File already exists)' +else +echo 'x - extracting libs/elapsed.pl (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'libs/elapsed.pl' && +;# NAME +;# elapsed.pl - convert seconds to elapsed time format +;# +;# AUTHOR +;# Michael S. Muegel <mmuegel@mot.com> +;# +;# RCS INFORMATION +;# $Author: glass $ +;# $Source: /b/source/CVS/src/usr.sbin/sendmail/contrib/mmuegel,v $ +;# $Revision: 1.3 $ of $Date: 1993/07/20 18:11:05 $ +X +package elapsed; +X +# Time field types +$DAYS = 1; +$HOURS = 2; +$MINUTES = 3; +$SECONDS = 4; +X +# The array contains four records each with four fields. The fields are, +# in order: +# +# Type Specifies what kind of time field this is. Once of +# $DAYS, $HOURS, $MINUTES, or $SECONDS. +# +# Multiplier Specifies what time field this is via the minimum +# number of seconds this time field may specify. For +# example, the minutes field would be non-zero +# when there are 60 or more seconds. +# +# Separator How to separate this time field from the next +# *greater* field. +# +# Format sprintf() format specifier on how to print this +# time field. +@MULT_AND_SEPS = ($DAYS, 60 * 60 * 24, "+", "%d", +X $HOURS, 60 * 60, ":", "%d", +X $MINUTES, 60, ":", "%02d", +X $SECONDS, 1, "", "%02d" +X ); +X +;############################################################################### +;# Seconds_To_Elapsed +;# +;# Coverts a seconds count to form [d+]h:mm:ss. If $Collapse +;# is true then the result is compacted somewhat. The string returned +;# will be of the form [d+][[h:]mm]:ss. +;# +;# Arguments: +;# $Seconds, $Collapse +;# +;# Examples: +;# &Seconds_To_Elapsed (0, 0) -> 0:00:00 +;# &Seconds_To_Elapsed (0, 1) -> :00 +;# +;# &Seconds_To_Elapsed (119, 0) -> 0:01:59 +;# &Seconds_To_Elapsed (119, 1) -> 01:59 +;# +;# &Seconds_To_Elapsed (3601, 0) -> 1:00:01 +;# &Seconds_To_Elapsed (3601, 1) -> 1:00:01 +;# +;# &Seconds_To_Elapsed (86401, 0) -> 1+0:00:01 +;# &Seconds_To_Elapsed (86401, 1) -> 1+:01 +;# +;# Returns: +;# $Elapsed +;############################################################################### +sub main'Seconds_To_Elapsed +{ +X local ($Seconds, $Collapse) = @_; +X local ($Type, $Multiplier, @Multipliers, $Separator, $DHMS_Used, +X $Elapsed, @Mult_And_Seps, $Print_Field); +X +X $Multiplier = 1; +X @Mult_And_Seps = @MULT_AND_SEPS; +X +X # Keep subtracting the number of seconds corresponding to a time field +X # from the number of seconds passed to the function. +X while (1) +X { +X ($Type, $Multiplier, $Separator, $Format) = splice (@Mult_And_Seps, 0, 4); +X last if (! $Multiplier); +X $Seconds -= $DHMS_Used * $Multiplier +X if ($DHMS_Used = int ($Seconds / $Multiplier)); +X +X # Figure out if we should print this field +X if ($Type == $DAYS) +X { +X $Print_Field = $DHMS_Used; +X } +X +X elsif ($Collapse) +X { +X if ($Type == $HOURS) +X { +X $Print_Field = $DHMS_Used; +X } +X elsif ($Type == $MINUTES) +X { +X $Print_Field = $DHMS_Used || $Printed_Field {$HOURS}; +X } +X else +X { +X $Format = ":%02d" +X if (! $Printed_Field {$MINUTES}); +X $Print_Field = 1; +X }; +X } +X +X else +X { +X $Print_Field = 1; +X }; +X +X $Printed_Field {$Type} = $Print_Field; +X $Elapsed .= sprintf ("$Format%s", $DHMS_Used, $Separator) +X if ($Print_Field); +X }; +X +X return ($Elapsed); +}; +X +1; +SHAR_EOF +chmod 0444 libs/elapsed.pl || +echo 'restore of libs/elapsed.pl failed' +Wc_c="`wc -c < 'libs/elapsed.pl'`" +test 3243 -eq "$Wc_c" || + echo 'libs/elapsed.pl: original size 3243, current size' "$Wc_c" +fi +# ============= libs/mail.pl ============== +if test -f 'libs/mail.pl' -a X"$1" != X"-c"; then + echo 'x - skipping libs/mail.pl (File already exists)' +else +echo 'x - extracting libs/mail.pl (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'libs/mail.pl' && +;# NAME +;# mail.pl - perl function(s) to handle mail processing +;# +;# AUTHOR +;# Michael S. Muegel (mmuegel@mot.com) +;# +;# RCS INFORMATION +;# $Author: glass $ +;# $Header: /b/source/CVS/src/usr.sbin/sendmail/contrib/mmuegel,v 1.3 1993/07/20 18:11:05 glass Exp $ +X +package mail; +X +# Mailer statement to eval. $Users, $Subject, and $Verbose are substituted +# via eval +$BIN_MAILER = "/usr/ucb/mail \$Verbose -s '\$Subject' \$Users"; +X +# Sendmail command to use when $Use_Sendmail is true. +$SENDMAIL = '/usr/lib/sendmail $Verbose $Users'; +X +;############################################################################### +;# Send_Mail +;# +;# Sends $Message to $Users with a subject of $Subject. If $Message_Is_File +;# is true then $Message is assumed to be a filename pointing to the mail +;# message. This is a new option and thus the backwards-compatible hack. +;# $Users should be a space separated list of mail-ids. +;# +;# If everything went OK $Status will be 1 and $Error_Msg can be ignored; +;# otherwise, $Status will be 0 and $Error_Msg will contain an error message. +;# +;# If $Use_Sendmail is 1 then sendmail is used to send the message. Normally +;# a mailer such as Mail is used. By specifiying this you can include +;# headers in addition to text in either $Message or $Message_Is_File. +;# If either $Message or $Message_Is_File contain a Subject: header then +;# $Subject is ignored; otherwise, a Subject: header is automatically created. +;# Similar to the Subject: header, if a To: header does not exist one +;# is automatically created from the $Users argument. The mail is still +;# sent, however, to the recipients listed in $Users. This is keeping with +;# normal sendmail usage (header vs. envelope). +;# +;# In both bin mailer and sendmail modes $Verbose will turn on verbose mode +;# (normally just sendmail verbose mode output). +;# +;# Arguments: +;# $Users, $Subject, $Message, $Message_Is_File, $Verbose, $Use_Sendmail +;# +;# Returns: +;# $Status, $Error_Msg +;############################################################################### +sub main'Send_Mail +{ +X local ($Users, $Subject, $Message, $Message_Is_File, $Verbose, +X $Use_Sendmail) = @_; +X local ($BIN_MAILER_HANDLE, $Mailer_Command, $Header_Found, %Header_Map, +X $Header_Extra, $Mailer); +X +X # If the message is contained in a file read it in so we can have one +X # consistent interface +X if ($Message_Is_File) +X { +X undef $/; +X $Message_Is_File = 0; +X open (Message) || return (0, "error reading $Message: $!"); +X $Message = <Message>; +X close (Message); +X }; +X +X # If sendmail mode see if we need to add some headers +X if ($Use_Sendmail) +X { +X # Determine if a header block is included in the message and what headers +X # are there +X foreach (split (/\n/, $Message)) +X { +X last if ($_ eq ""); +X $Header_Found = $Header_Map {$1} = 1 if (/^([A-Z]\S*): /); +X }; +X +X # Add some headers? +X if (! $Header_Map {"To"}) +X { +X $Header_Extra .= "To: " . join (", ", $Users) . "\n"; +X }; +X if (($Subject ne "") && (! $Header_Map {"Subject"})) +X { +X $Header_Extra .= "Subject: $Subject\n"; +X }; +X +X # Add the required blank line between header/body if there where no +X # headers to begin with +X if ($Header_Found) +X { +X $Message = "$Header_Extra$Message"; +X } +X else +X { +X $Message = "$Header_Extra\n$Message"; +X }; +X }; +X +X # Get a string that is the mail command +X $Verbose = ($Verbose) ? "-v" : ""; +X $Mailer = ($Use_Sendmail) ? $SENDMAIL : $BIN_MAILER; +X eval "\$Mailer = \"$Mailer\""; +X return (0, "error setting \$Mailer: $@") if ($@); +X +X # need to catch SIGPIPE in case the $Mailer call fails +X $SIG {'PIPE'} = "mail'Cleanup"; +X +X # Open mailer +X return (0, "can not open mail program: $Mailer") if (! open (MAILER, "| $Mailer")); +X +X # Send off the mail! +X print MAILER $Message; +X close (MAILER); +X return (0, "error running mail program: $Mailer") if ($?); +X +X # Everything must have went AOK +X return (1); +}; +X +;############################################################################### +;# Cleanup +;# +;# Simply here so we can catch SIGPIPE and not exit. +;# +;# Globals: +;# None +;# +;# Arguments: +;# None +;# +;# Returns: +;# Nothing exciting +;############################################################################### +sub Cleanup +{ +}; +X +1; +SHAR_EOF +chmod 0444 libs/mail.pl || +echo 'restore of libs/mail.pl failed' +Wc_c="`wc -c < 'libs/mail.pl'`" +test 4379 -eq "$Wc_c" || + echo 'libs/mail.pl: original size 4379, current size' "$Wc_c" +fi +# ============= libs/mqueue.pl ============== +if test -f 'libs/mqueue.pl' -a X"$1" != X"-c"; then + echo 'x - skipping libs/mqueue.pl (File already exists)' +else +echo 'x - extracting libs/mqueue.pl (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'libs/mqueue.pl' && +;# NAME +;# mqueue.pl - functions to work with the sendmail queue +;# +;# DESCRIPTION +;# Both Get_Queue_IDs and Parse_Control_File are available to get +;# information about the sendmail queue. The cqueue program is a good +;# example of how these functions work. +;# +;# AUTHOR +;# Michael S. Muegel (mmuegel@mot.com) +;# +;# RCS INFORMATION +;# $Author: glass $ +;# $Source: /b/source/CVS/src/usr.sbin/sendmail/contrib/mmuegel,v $ +;# $Revision: 1.3 $ of $Date: 1993/07/20 18:11:05 $ +X +package mqueue; +X +;############################################################################### +;# Get_Queue_IDs +;# +;# Will figure out the queue IDs in $Queue that have both control and data +;# files. They are returned in @Valid_IDs. Those IDs that have a +;# control file and no data file are saved to the array globbed by +;# *Missing_Control_IDs. Likewise, those IDs that have a data file and no +;# control file are saved to the array globbed by *Missing_Data_IDs. +;# +;# If $Skip_Locked is true they a message that has a lock file is skipped +;# and will not show up in any of the arrays. +;# +;# If everything went AOK then $Status is 1; otherwise, $Status is 0 and +;# $Msg tells what went wrong. +;# +;# Globals: +;# None +;# +;# Arguments: +;# $Queue, $Skip_Locked, *Missing_Control_IDs, *Missing_Data_IDs +;# +;# Returns: +;# $Status, $Msg, @Valid_IDs +;############################################################################### +sub main'Get_Queue_IDs +{ +X local ($Queue, $Skip_Locked, *Missing_Control_IDs, +X *Missing_Data_IDs) = @_; +X local (*QUEUE, @Files, %Lock_IDs, %Data_IDs, %Control_IDs, $_); +X +X # Make sure that the * argument @arrays ar empty +X @Missing_Control_IDs = @Missing_Data_IDs = (); +X +X # Save each data, lock, and queue file in @Files +X opendir (QUEUE, $Queue) || return (0, "error getting directory listing of $Queue"); +X @Files = grep (/^(df|lf|qf)/, readdir (QUEUE)); +X closedir (QUEUE); +X +X # Create indexed list of data and control files. IF $Skip_Locked is true +X # then skip either if there is a lock file present. +X if ($Skip_Locked) +X { +X grep ((s/^lf//) && ($Lock_IDs {$_} = 1), @Files); +X grep ((s/^df//) && (! $Lock_IDs {$_}) && ($Data_IDs {$_} = 1), @Files); +X grep ((s/^qf//) && (! $Lock_IDs {$_}) && ($Control_IDs {$_} = 1), @Files); +X } +X else +X { +X grep ((s/^df//) && ($Data_IDs {$_} = 1), @Files); +X grep ((s/^qf//) && ($Control_IDs {$_} = 1), @Files); +X }; +X +X # Find missing control and data files and remove them from the lists of each +X @Missing_Control_IDs = sort (grep ((! $Control_IDs {$_}) && (delete $Data_IDs {$_}), keys (%Data_IDs))); +X @Missing_Data_IDs = sort (grep ((! $Data_IDs {$_} && (delete $Control_IDs {$_})), keys (%Control_IDs))); +X +X +X # Return the IDs in an appartently random order +X return (1, "", keys (%Control_IDs)); +}; +X +X +;############################################################################### +;# Parse_Control_File +;# +;# Will pase a sendmail queue control file for useful information. See the +;# Sendmail Installtion and Operation Guide (SMM:07) for a complete +;# explanation of each field. +;# +;# The following globbed variables are set (or cleared) by this function: +;# +;# $Sender The sender's address. +;# +;# @Recipients One or more addresses for the recipient of the mail. +;# +;# @Errors_To One or more addresses for addresses to which mail +;# delivery errors should be sent. +;# +;# $Creation_Time The job creation time in time(3) format. That is, +;# seconds since 00:00:00 GMT 1/1/70. +;# +;# $Priority An integer representing the current message priority. +;# This is used to order the queue. Higher numbers mean +;# lower priorities. +;# +;# $Status_Message The status of the mail message. It can contain any +;# text. +;# +;# @Headers Message headers unparsed but in their original order. +;# Headers that span multiple lines are not mucked with, +;# embedded \ns will be evident. +;# +;# In all e-mail addresses bounding <> pairs are stripped. +;# +;# If everything went AOK then $Status is 1. If the message with queue ID +;# $Queue_ID just does not exist anymore -1 is returned. This is very +;# possible and should be allowed for. Otherwise, $Status is 0 and $Msg +;# tells what went wrong. +;# +;# Globals: +;# None +;# +;# Arguments: +;# $Queue, $Queue_ID, *Sender, *Recipients, *Errors_To, *Creation_Time, +;# *Priority, *Status_Message, *Headers +;# +;# Returns: +;# $Status, $Msg +;############################################################################### +sub main'Parse_Control_File +{ +X local ($Queue, $Queue_ID, *Sender, *Recipients, *Errors_To, *Creation_Time, +X *Priority, *Status_Message, *Headers) = @_; +X local (*Control, $_, $Not_Empty); +X +X # Required variables and the associated control. If empty at the end of +X # parsing we return a bad status. +X @REQUIRED_INFO = ('$Creation_Time', 'T', '$Sender', 'S', '@Recipients', 'R', +X '$Priority', 'P'); +X +X # Open up the control file for read +X $Control = "$Queue/qf$Queue_ID"; +X if (! open (Control)) +X { +X return (-1) if ((-x $Queue) && (! -f "$Queue/qf$Queue_ID") && +X (! -f "$Queue/df$Queue_ID")); +X return (0, "error opening $Control for read: $!"); +X }; +X +X # Reset the globbed variables just in case +X $Sender = $Creation_Time = $Priority = $Status_Message = ""; +X @Recipients = @Errors_To = @Headers = (); +X +X # Look for a few things in the control file +X READ: while (<Control>) +X { +X $Not_Empty = 1; +X chop; +X +X PARSE: +X { +X if (/^T(\d+)$/) +X { +X $Creation_Time = $1; +X } +X elsif (/^S(<)?([^>]+)/) +X { +X $Sender = $2; +X } +X elsif (/^R(<)?([^>]+)/) +X { +X push (@Recipients, $2); +X } +X elsif (/^E(<)?([^>]+)/) +X { +X push (@Errors_To, $2); +X } +X elsif (/^M(.*)/) +X { +X $Status_Message = $1; +X } +X elsif (/^P(\d+)$/) +X { +X $Priority = $1; +X } +X elsif (/^H(.*)/) +X { +X $Header = $1; +X while (<Control>) +X { +X chop; +X last if (/^[A-Z]/); +X $Header .= "\n$_"; +X }; +X push (@Headers, $Header); +X redo PARSE if ($_); +X last if (eof); +X }; +X }; +X }; +X +X # If the file was empty scream bloody murder +X return (0, "empty control file") if (! $Not_Empty); +X +X # Yell if we could not find a required field +X while (($Var, $Control) = splice (@REQUIRED_INFO, 0, 2)) +X { +X eval "return (0, 'required control field $Control not found') +X if (! $Var)"; +X return (0, "error checking \$Var: $@") if ($@); +X }; +X +X # Everything went AOK +X return (1); +}; +X +1; +SHAR_EOF +chmod 0444 libs/mqueue.pl || +echo 'restore of libs/mqueue.pl failed' +Wc_c="`wc -c < 'libs/mqueue.pl'`" +test 6953 -eq "$Wc_c" || + echo 'libs/mqueue.pl: original size 6953, current size' "$Wc_c" +fi +# ============= libs/newgetopts.pl ============== +if test -f 'libs/newgetopts.pl' -a X"$1" != X"-c"; then + echo 'x - skipping libs/newgetopts.pl (File already exists)' +else +echo 'x - extracting libs/newgetopts.pl (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'libs/newgetopts.pl' && +;# NAME +;# newgetopts.pl - a better newgetopt (which is a better getopts which is +;# a better getopt ;-) +;# +;# AUTHOR +;# Mike Muegel (mmuegel@mot.com) +;# +;# $Author: glass $ +;# $Header: /b/source/CVS/src/usr.sbin/sendmail/contrib/mmuegel,v 1.3 1993/07/20 18:11:05 glass Exp $ +X +;############################################################################### +;# New_Getopts +;# +;# Does not care about order of switches, options, and arguments like +;# getopts.pl. Thus all non-switches/options will be kept in ARGV even if they +;# are not at the end. If $Pass_Invalid is set all unkown options will be +;# passed back to the caller by keeping them in @ARGV. This is useful when +;# parsing a command line for your script while ignoring options that you +;# may pass to another script. If this is set New_Getopts tries to maintain +;# the switch clustering on the unkown switches. +;# +;# Accepts the special argument -usage to print the Usage string. Also accepts +;# the special option -version which prints the contents of the string +;# $VERSION. $VERSION may or may not have an embeded \n in it. If -usage +;# or -version are specified a status of -1 is returned. Note that the usage +;# option is only accepted if the usage string is not null. +;# +;# $Switches is just like the formal arguemnt of getopts.pl. $Usage is a usage +;# string with or without a trailing \n. *Switch_To_Order is an optional +;# pointer to the name of an associative array which will contain a mapping of +;# switch names to the order in which (if at all) the argument was entered. +;# +;# For example, if @ARGV contains -v, -x, test: +;# +;# $Switch_To_Order {"v"} = 1; +;# $Switch_To_Order {"x"} = 2; +;# +;# Note that in the case of multiple occurances of an option $Switch_To_Order +;# will store each occurance of the argument via a string that emulates +;# an array. This is done by using join ($;, ...). You can retrieve the +;# array by using split (/$;/, ...). +;# +;# *Split_ARGV is an optional pointer to an array which will conatin the +;# original switches along with their values. For the example used above +;# Split_ARGV would contain: +;# +;# @Split_ARGV = ("v", "", "x", "test"); +;# +;# Another exciting ;-) feature that newgetopts has. Along with creating the +;# normal $opt_ scalars for the last value of an argument the list @opt_ is +;# created. It is an array which contains all the values of arguments to the +;# basename of the variable. They are stored in the order which they occured +;# on the command line starting with $[. Note that blank arguments are stored +;# as "". Along with providing support for multiple options on the command +;# line this also provides a method of counting the number of times an option +;# was specified via $#opt_. +;# +;# Automatically resets all $opt_, @opt_, %Switch_To_Order, and @Split_ARGV +;# variables so that New_Getopts may be called more than once from within +;# the same program. Thus, if $opt_v is set upon entry to New_Getopts and +;# -v is not in @ARGV $opt_v will not be set upon exit. +;# +;# Arguments: +;# $Switches, $Usage, $Pass_Invalid, *Switch_To_Order, *Split_ARGV +;# +;# Returns: +;# -1, 0, or 1 depending on status (printed Usage/Version, OK, not OK) +;############################################################################### +sub New_Getopts +{ +X local($taint_argumentative, $Usage, $Pass_Invalid, *Switch_To_Order, +X *Split_ARGV) = @_; +X local(@args,$_,$first,$rest,$errs, @leftovers, @current_leftovers, +X %Switch_Found); +X local($[, $*, $Script_Name, $argumentative); +X +X # Untaint the argument cluster so that we can use this with taintperl +X $taint_argumentative =~ /^(.*)$/; +X $argumentative = $1; +X +X # Clear anything that might still be set from a previous New_Getopts +X # call. +X @Split_ARGV = (); +X +X # Get the basename of the calling script +X ($Script_Name = $0) =~ s/.*\///; +X +X # Make Usage have a trailing \n +X $Usage .= "\n" if ($Usage !~ /\n$/); +X +X @args = split( / */, $argumentative ); +X +X # Clear anything that might still be set from a previous New_Getopts call. +X foreach $first (@args) +X { +X next if ($first eq ":"); +X delete $Switch_Found {$first}; +X delete $Switch_To_Order {$first}; +X eval "undef \@opt_$first; undef \$opt_$first;"; +X }; +X +X while (@ARGV) +X { +X # Let usage through +X if (($ARGV[0] eq "-usage") && ($Usage ne "\n")) +X { +X print $Usage; +X exit (-1); +X } +X +X elsif ($ARGV[0] eq "-version") +X { +X if ($VERSION) +X { +X print $VERSION; +X print "\n" if ($VERSION !~ /\n$/); +X } +X else +X { +X warn "${Script_Name}: no version information available, sorry\n"; +X } +X exit (-1); +X } +X +X elsif (($_ = $ARGV[0]) =~ /^-(.)(.*)/) +X { +X ($first,$rest) = ($1,$2); +X $pos = index($argumentative,$first); +X +X $Switch_To_Order {$first} = join ($;, split (/$;/, $Switch_To_Order {$first}), ++$Order); +X +X if($pos >= $[) +X { +X if($args[$pos+1] eq ':') +X { +X shift(@ARGV); +X if($rest eq '') +X { +X $rest = shift(@ARGV); +X } +X +X eval "\$opt_$first = \$rest;"; +X eval "push (\@opt_$first, \$rest);"; +X push (@Split_ARGV, $first, $rest); +X } +X else +X { +X eval "\$opt_$first = 1"; +X eval "push (\@opt_$first, '');"; +X push (@Split_ARGV, $first, ""); +X +X if($rest eq '') +X { +X shift(@ARGV); +X } +X else +X { +X $ARGV[0] = "-$rest"; +X } +X } +X } +X +X else +X { +X # Save any other switches if $Pass_Valid +X if ($Pass_Invalid) +X { +X push (@current_leftovers, $first); +X } +X else +X { +X warn "${Script_Name}: unknown option: $first\n"; +X ++$errs; +X }; +X if($rest ne '') +X { +X $ARGV[0] = "-$rest"; +X } +X else +X { +X shift(@ARGV); +X } +X } +X } +X +X else +X { +X push (@leftovers, shift (@ARGV)); +X }; +X +X # Save any other switches if $Pass_Valid +X if ((@current_leftovers) && ($rest eq '')) +X { +X push (@leftovers, "-" . join ("", @current_leftovers)); +X @current_leftovers = (); +X }; +X }; +X +X # Automatically print Usage if a warning was given +X @ARGV = @leftovers; +X if ($errs != 0) +X { +X warn $Usage; +X return (0); +X } +X else +X { +X return (1); +X } +X +} +X +1; +SHAR_EOF +chmod 0444 libs/newgetopts.pl || +echo 'restore of libs/newgetopts.pl failed' +Wc_c="`wc -c < 'libs/newgetopts.pl'`" +test 7030 -eq "$Wc_c" || + echo 'libs/newgetopts.pl: original size 7030, current size' "$Wc_c" +fi +# ============= libs/strings1.pl ============== +if test -f 'libs/strings1.pl' -a X"$1" != X"-c"; then + echo 'x - skipping libs/strings1.pl (File already exists)' +else +echo 'x - extracting libs/strings1.pl (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'libs/strings1.pl' && +;# NAME +;# strings1.pl - FUN with strings #1 +;# +;# NOTES +;# I wrote Format_Text_Block when I just started programming Perl so +;# it is probably not very Perlish code. Center is more like it :-). +;# +;# AUTHOR +;# Michael S. Muegel (mmuegel@mot.com) +;# +;# RCS INFORMATION +;# $Author: glass $ +;# $Header: /b/source/CVS/src/usr.sbin/sendmail/contrib/mmuegel,v 1.3 1993/07/20 18:11:05 glass Exp $ +X +package strings1; +X +;###############################################################################;# Center +;# +;# Center $Text assuming the output should be $Columns wide. $Text can span +;# multiple lines, of course :-). Lines within $Text that contain only +;# whitespace are not centered and are instead collapsed. This may save time +;# when printing them later. +;# +;# Arguments: +;# $Text, $Columns +;# +;# Returns: +;# $Centered_Text +;############################################################################### +sub main'Center +{ +X local ($_, $Columns) = @_; +X local ($*) = 1; +X +X s@^(.*)$@" " x (($Columns - length ($1)) / 2) . $1@eg; +X s/^[\t ]*$//g; +X return ($_); +}; +X +;############################################################################### +;# Format_Text_Block +;# +;# Formats a text string to be printed to the display or other similar device. +;# Text in $String will be fomratted such that the following hold: +;# +;# + $String contains the (possibly) multi-line text to print. It is +;# automatically word-wrapped to fit in $Columns. +;# +;# + \n'd are maintained and are not folded. +;# +;# + $Offset is pre-pended before each separate line of text. +;# +;# + If $Offset_Once is $TRUE $Offset will only appear on the first line. +;# All other lines will be indented to match the amount of whitespace of +;# $Offset. +;# +;# + If $Bullet_Indent is $TRUE $Offset will only be applied to the begining +;# of lines as they occured in the original $String. Lines that are created +;# by this routine will always be indented by blank spaces. +;# +;# + If $Columns is 0 no word-wrap is done. This might be useful to still +;# to offset each line in a buffer. +;# +;# + If $Split_Expr is supplied the string is split on it. If not supplied +;# the string is split on " \t\/\-\,\." by default. +;# +;# + If $Offset_Blank is $TRUE then empty lines will have $Offset pre-pended +;# to them. Otherwise, they will still empty. +;# +;# This is a realy workhorse routine that I use in many places because of its +;# veratility. +;# +;# Arguments: +;# $String, $Offset, $Offset_Once, $Bullet_Indent, $Columns, $Split_Expr, +;# $Offset_Blank +;# +;# Returns: +;# $Buffer +;############################################################################### +sub main'Format_Text_Block +{ +X local ($String, $Real_Offset, $Offset_Once, $Bullet_Indent, $Columns, +X $Split_Expr, $Offset_Blank) = @_; +X +X local ($New_Line, $Line, $Chars_Per_Line, $Space_Offset, $Buffer, +X $Next_New_Line, $Num_Lines, $Num_Offsets, $Offset); +X local ($*) = 0; +X local ($BLANK_TAG) = "__FORMAT_BLANK__"; +X local ($Blank_Offset) = $Real_Offset if ($Offset_Blank); +X +X # What should we split on? +X $Split_Expr = " \\t\\/\\-\\,\\." if (! $Split_Expr); +X +X # Pre-process the string - convert blank lines to __FORMAT_BLANK__ sequence +X $String =~ s/\n\n/\n$BLANK_TAG\n/g; +X $String =~ s/^\n/$BLANK_TAG\n/g; +X $String =~ s/\n$/\n$BLANK_TAG/g; +X +X # If bad $Columns/$Offset combo or no $Columns make a VERRRYYY wide $Column +X $Offset = $Real_Offset; +X $Chars_Per_Line = 16000 if (($Chars_Per_Line = $Columns - length ($Offset)) <= 0); +X $Space_Offset = " " x length ($Offset); +X +X # Get a buffer +X foreach $Line (split ("\n", $String)) +X { +X $Offset = $Real_Offset if ($Bullet_Indent); +X +X # Find where to split the line +X if ($Line ne $BLANK_TAG) +X { +X $New_Line = ""; +X while ($Line =~ /^([$Split_Expr]*)([^$Split_Expr]+)/) +X { +X if (length ("$New_Line$&") >= $Chars_Per_Line) +X { +X $Next_New_Line = $+; +X $New_Line = "$Offset$New_Line$1"; +X $Buffer .= "\n" if ($Num_Lines++); +X $Buffer .= $New_Line; +X $Offset = $Space_Offset if (($Offset) && ($Offset_Once)); +X $New_Line = $Next_New_Line; +X ++$Num_Lines; +X } +X else +X { +X $New_Line .= $&; +X }; +X $Line = $'; +X }; +X +X $Buffer .= "\n" if ($Num_Lines++); +X $Buffer .= "$Offset$New_Line$Line"; +X $Offset = $Space_Offset if (($Offset) && ($Offset_Once)); +X } +X +X else +X { +X $Buffer .= "\n$Blank_Offset"; +X }; +X }; +X +X return ($Buffer); +X +}; +X +1; +SHAR_EOF +chmod 0444 libs/strings1.pl || +echo 'restore of libs/strings1.pl failed' +Wc_c="`wc -c < 'libs/strings1.pl'`" +test 4718 -eq "$Wc_c" || + echo 'libs/strings1.pl: original size 4718, current size' "$Wc_c" +fi +# ============= libs/timespec.pl ============== +if test -f 'libs/timespec.pl' -a X"$1" != X"-c"; then + echo 'x - skipping libs/timespec.pl (File already exists)' +else +echo 'x - extracting libs/timespec.pl (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'libs/timespec.pl' && +;# NAME +;# timespec.pl - convert a pre-defined time specifyer to seconds +;# +;# AUTHOR +;# Michael S. Muegel (mmuegel@mot.com) +;# +;# RCS INFORMATION +;# $Author: glass $ +;# $Header: /b/source/CVS/src/usr.sbin/sendmail/contrib/mmuegel,v 1.3 1993/07/20 18:11:05 glass Exp $ +X +package timespec; +X +%TIME_SPEC_TO_SECONDS = ("s", 1, +X "m", 60, +X "h", 60 * 60, +X "d", 60 * 60 * 24 +X ); +X +$VALID_TIME_SPEC_EXPR = "[" . join ("", keys (%TIME_SPEC_TO_SECONDS)) . "]"; +X +;############################################################################### +;# Time_Spec_To_Seconds +;# +;# Converts a string of the form: +;# +;# (<number>(s|m|h|d))+ +;# +;# to seconds. The second part of the time spec specifies seconds, minutes, +;# hours, or days, respectfully. The first part is the number of those untis. +;# There can be any number of such specifiers. As an example, 1h30m means 1 +;# hour and 30 minutes. +;# +;# If the parsing went OK then $Status is 1, $Msg is undefined, and $Seconds +;# is $Time_Spec converted to seconds. If something went wrong then $Status +;# is 0 and $Msg explains what went wrong. +;# +;# Arguments: +;# $Time_Spec +;# +;# Returns: +;# $Status, $Msg, $Seconds +;############################################################################### +sub main'Time_Spec_To_Seconds +{ +X $Time_Spec = $_[0]; +X +X $Seconds = 0; +X while ($Time_Spec =~ /^(\d+)($VALID_TIME_SPEC_EXPR)/) +X { +X $Seconds += $1 * $TIME_SPEC_TO_SECONDS {$2}; +X $Time_Spec = $'; +X }; +X +X return (0, "error parsing time spec: $Time_Spec") if ($Time_Spec ne ""); +X return (1, "", $Seconds); +X +}; +X +X +1; +SHAR_EOF +chmod 0444 libs/timespec.pl || +echo 'restore of libs/timespec.pl failed' +Wc_c="`wc -c < 'libs/timespec.pl'`" +test 1637 -eq "$Wc_c" || + echo 'libs/timespec.pl: original size 1637, current size' "$Wc_c" +fi +# ============= man/cqueue.1 ============== +if test ! -d 'man'; then + echo 'x - creating directory man' + mkdir 'man' +fi +if test -f 'man/cqueue.1' -a X"$1" != X"-c"; then + echo 'x - skipping man/cqueue.1 (File already exists)' +else +echo 'x - extracting man/cqueue.1 (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'man/cqueue.1' && +.TH CQUEUE 1L +\" +\" $Author: glass $ +\" $Header: /b/source/CVS/src/usr.sbin/sendmail/contrib/mmuegel,v 1.3 1993/07/20 18:11:05 glass Exp $ +\" +.ds mp \fBcqueue\fR +.de IB +.IP \(bu 2 +.. +.SH NAME +\*(mp - check sendmail queue for problems +.SH SYNOPSIS +.IP \*(mp 7 +[ \fB-abdms\fR ] [ \fB-q\fR \fIqueue-dir\fI ] [ \fB-t\fR \fItime\fR ] +[ \fB-u\fR \fIusers\fR ] [ \fB-w\fR \fIwidth\fR ] +.SH DESCRIPTION +Reports on problems in the sendmail queue. With no options this simply +means listing messages that have been in the queue longer than a default +period along with a summary of queue mail by host and status message. +.SH OPTIONS +.IP \fB-a\fR 14 +Report on all messages in the queue. This is equivalent to saying \fB-t\fR 0s. +You may like this command so much that you use it as a replacement for +\fBmqueue\fR. For example: +.sp 1 +.RS +.RS +\fBalias mqueue cqueue -a\fR +.RE +.RE +.IP \fB-b\fR 14 +Also report on bogus queue files. Those are files that +have data files and no control files or vice versa. +.IP \fB-d\fR +Print a detailed report of mail messages that have been queued longer than +the specified or default time. Information that is presented includes: +.RS +.RS +.IB +Sendmail queue identifier. +.IB +Date the message was first queued. +.IB +Sender of the message. +.IB +One or more recipients of the message. +.IB +An optional status of the message. This usually indicates why the message +has not been delivered. +.RE +.RE +.IP \fB-m\fR 14 +Mail off the results if any problems were found. +Normaly results are printed to stdout. If this option +is specified they are mailed to one or more users. Results +are not printed to stdout in this case. Results are \fBonly\fR +mailed if \*(mp found something wrong. +.IP "\fB-q\fR \fIqueue-dir\fI" +The sendmail mail queue directory. Default is \fB/usr/spool/mqueue\fR or +some other site configured value. +.IP "\fB-t\fR \fItime\fR" +List messages that have been in the queue longer than +\fItime\fR. Time should of the form: +.sp 1 +.RS +.RS +(<number>(s|m|h|d))+ +.sp 1 +.RE +.RE +.RS 14 +The second portion of the above definition +specifies seconds, minutes, hours, or +days, respectfully. The first portion is the number of +those units. There can be any number of such specifiers. +As an example, 1h30m means 1 hour and 30 minutes. +.sp 1 +The default is 2 hours. +.RE +.IP \fB-s\fR 14 +Print a summary of messages that have been queued longer than +the specified or default time. Two separate types of summaries are printed. +The first summarizes the queue messages by destination host. The host name +is gleaned from the recipient addresses for each message. +Thus the actual host names for this summary should be taken with a grain +of salt since ruleset 0 has not been applied to the address the host was +taken from nor were MX records consulted. It would be possible to add +this; however, the execution time of the script would increase +dramatically. The second summary is by status message. +.IP "\fB-u\fR \fIusers\fR" +Specify list of users to send a mail report to other than +the invoker. This option is only valid when \fB-m\fR has been +specified. Multiple recipients may be separated by spaces. +.IP "\fB-w\fR \fIwidth\fR" +Specify the page width to which the output should tailored. \fIwidth\fR +should be an integer representing some character position. The default is +80 or some other site configured value. Output is folded neatly to match +\fIwidth\fR. +.SH EXAMPLES +.nf +% \fBdate\fR +Tue Jan 19 12:07:20 CST 1993 +X +% \fBcqueue -t 21h45m -w 70\fR +X +Summary of messages in queue longer than 21:45:00 by destination +host: +X +X Number of +X Messages Destination Host +X --------- ---------------- +X 2 cigseg.rtsg.mot.com +X 1 mnesouth.corp.mot.com +X --------- +X 3 +X +Summary of messages in queue longer than 21:45:00 by status message: +X +X Number of +X Messages Status Message +X --------- -------------- +X 1 Deferred: Connection refused by mnesouth.corp.mot.com +X 2 Deferred: Host Name Lookup Failure +X --------- +X 3 +X +Detail of messages in queue longer than 21:45:00 sorted by creation +date: +X +X ID: AA20573 +X Date: 02:09:27 PM 01/18/93 +X Sender: melrose-place-owner@ferkel.ucsb.edu +X Recipient: pbaker@cigseg.rtsg.mot.com +X Status: Deferred: Host Name Lookup Failure +X +X ID: AA20757 +X Date: 02:11:30 PM 01/18/93 +X Sender: 90210-owner@ferkel.ucsb.edu +X Recipient: pbaker@cigseg.rtsg.mot.com +X Status: Deferred: Host Name Lookup Failure +X +X ID: AA21110 +X Date: 02:17:01 PM 01/18/93 +X Sender: rd_lap_wg@mdd.comm.mot.com +X Recipient: jim_mathis@mnesouth.corp.mot.com +X Status: Deferred: Connection refused by mnesouth.corp.mot.com +.fi +.SH AUTHOR +.nf +Michael S. Muegel (mmuegel@mot.com) +UNIX Applications Startup Group +Corporate Information Office, Schaumburg, IL +Motorola, Inc. +.fi +.SH COPYRIGHT NOTICE +Copyright 1993, Motorola, Inc. +.sp 1 +Permission to use, copy, modify and distribute without charge this +software, documentation, etc. is granted, provided that this +comment and the author's name is retained. The author nor Motorola assume any +responsibility for problems resulting from the use of this software. +.SH SEE ALSO +.nf +\fBsendmail(8)\fR +\fISendmail Installation and Operation Guide\fR. +.fi +SHAR_EOF +chmod 0444 man/cqueue.1 || +echo 'restore of man/cqueue.1 failed' +Wc_c="`wc -c < 'man/cqueue.1'`" +test 5229 -eq "$Wc_c" || + echo 'man/cqueue.1: original size 5229, current size' "$Wc_c" +fi +# ============= man/postclip.1 ============== +if test -f 'man/postclip.1' -a X"$1" != X"-c"; then + echo 'x - skipping man/postclip.1 (File already exists)' +else +echo 'x - extracting man/postclip.1 (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'man/postclip.1' && +.TH POSTCLIP 1L +\" +\" $Author: glass $ +\" $Header: /b/source/CVS/src/usr.sbin/sendmail/contrib/mmuegel,v 1.3 1993/07/20 18:11:05 glass Exp $ +\" +.ds mp \fBpostclip\fR +.SH NAME +\*(mp - send only the headers to Postmaster +.SH SYNOPSIS +\*(mp [ \fB-v\fR ] [ \fIto\fR ... ] +.SH DESCRIPTION +\*(mp will forward non-delivery reports to a postmaster after deleting the body +of the message. This keeps bounced mail private and helps to avoid disk space problems. \*(mp tries its best to keep as much of the header trail as possible. +Hopefully only the original body of the message will be filtered. Only messages +that have a subject that begins with 'Returned mail:' are filtered. This +ensures that other mail is not accidently mucked with. Finally, note that +\fBsendmail\fR is used to deliver the message after it has been (possibly) +filtered. All of the original headers will remain intact. +.sp 1 +You can use this with any \fBsendmail\fR by modifying the Postmaster alias. +If you use IDA \fBsendmail\fR you could add the following to <machine>.m4: +.sp 1 +.RS +define(POSTMASTERBOUNCE, mailer-errors) +.RE +.sp 1 +In the aliases file, add a line similar to the following: +.sp 1 +.RS +mailer-errors: "|/usr/local/bin/postclip postmaster" +.RE +.SH OPTIONS +.IP \fB-v\fR +Be verbose about delivery. Probably only useful when debugging \*(mp. +.IP \fIto\fR +A list of one or more e-mail ids to send the modified +Postmaster messages to. If none are specified postmaster +is used. +.SH AUTHOR +.nf +Michael S. Muegel (mmuegel@mot.com) +UNIX Applications Startup Group +Corporate Information Office, Schaumburg, IL +Motorola, Inc. +.fi +.SH CREDITS +The original idea to filter Postmaster mail was taken from a script by +Christopher Davis <ckd@eff.org>. +.SH COPYRIGHT NOTICE +Copyright 1992, Motorola, Inc. +.sp 1 +Permission to use, copy, modify and distribute without charge this +software, documentation, etc. is granted, provided that this +comment and the author's name is retained. The author nor Motorola assume any +responsibility for problems resulting from the use of this software. +.SH SEE ALSO +.nf +\fBsendmail(8)\fR +.fi +SHAR_EOF +chmod 0444 man/postclip.1 || +echo 'restore of man/postclip.1 failed' +Wc_c="`wc -c < 'man/postclip.1'`" +test 2097 -eq "$Wc_c" || + echo 'man/postclip.1: original size 2097, current size' "$Wc_c" +fi +# ============= src/cqueue ============== +if test ! -d 'src'; then + echo 'x - creating directory src' + mkdir 'src' +fi +if test -f 'src/cqueue' -a X"$1" != X"-c"; then + echo 'x - skipping src/cqueue (File already exists)' +else +echo 'x - extracting src/cqueue (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'src/cqueue' && +#!/usr/local/ustart/bin/suidperl +X +# NAME +# cqueue - check sendmail queue for problems +# +# SYNOPSIS +# Type cqueue -usage +# +# AUTHOR +# Michael S. Muegel <mmuegel@mot.com> +# +# RCS INFORMATION +# $Author: glass $ +# $Header: /b/source/CVS/src/usr.sbin/sendmail/contrib/mmuegel,v 1.3 1993/07/20 18:11:05 glass Exp $ +X +# So that date.pl does not yell (Domain/OS version does a ``) +$ENV{'PATH'} = ""; +X +# A better getopts routine +require "newgetopts.pl"; +require "timespec.pl"; +require "mail.pl"; +require "date.pl"; +require "mqueue.pl"; +require "strings1.pl"; +require "elapsed.pl"; +X +($Script_Name = $0) =~ s/.*\///; +X +# Some defaults you may want to change +$DEF_TIME = "2h"; +$DEF_QUEUE = "/usr/spool/mqueue"; +$DEF_COLUMNS = 80; +$DATE_FORMAT = "%r %D"; +X +# Constants that probably should not be changed +$USAGE = "Usage: $Script_Name [ -abdms ] [ -q queue-dir ] [ -t time ] [ -u user ] [ -w width ]\n"; +$VERSION = "${Script_Name} by \$Author: glass $; \$Revision: 1.3 $ of \$Date: 1993/07/20 18:11:05 $"; +$SWITCHES = "abdmst:u:q:w:"; +$SPLIT_EXPR = '\s,\.@!%:'; +$ADDR_PART_EXPR = '[^!@%]+'; +X +# Let getopts parse for switches +$Status = &New_Getopts ($SWITCHES, $USAGE); +exit (0) if ($Status == -1); +exit (1) if (! $Status); +X +# Check args +die "${Script_Name}: -u only valid with -m\n" if (($opt_u) && (! $opt_m)); +die "${Script_Name}: -a not valid with -t option\n" if ($opt_a && $opt_t); +$opt_u = getlogin || (getpwuid ($<))[0] || $ENV{"USER"} || die "${Script_Name}: can not determine who you are!\n" if (! $opt_u); +X +# Set defaults +$opt_t = "0s" if ($opt_a); +$opt_t = $DEF_TIME if ($opt_t eq ""); +$opt_w = $DEF_COLUMNS if ($opt_w eq ""); +$opt_q = $DEF_QUEUE if ($opt_q eq ""); +$opt_s = $opt_d = 1 if (! ($opt_s || $opt_d)); +X +# Untaint the users to mail to +$opt_u =~ /^(.*)$/; +$Users = $1; +X +# Convert time option to seconds and seconds to elapsed form +die "${Script_Name}: $Msg\n" if (! (($Status, $Msg, $Seconds) = &Time_Spec_To_Seconds ($opt_t))[0]); +$Elapsed = &Seconds_To_Elapsed ($Seconds, 1); +$Time_Info = " longer than $Elapsed" if ($Seconds); +X +# Get the current time +$Current_Time = time; +$Current_Date = &date ($Current_Time, $DATE_FORMAT); +X +($Status, $Msg, @Queue_IDs) = &Get_Queue_IDs ($opt_q, 1, @Missing_Control_IDs, +X @Missing_Data_IDs); +die "$Script_Name: $Msg\n" if (! $Status); +X +# Yell about missing data/control files? +if ($opt_b) +{ +X +X $Report = "\nMessages missing control files:\n\n " . +X join ("\n ", @Missing_Control_IDs) . +X "\n" +X if (@Missing_Control_IDs); +X +X $Report .= "\nMessages missing data files:\n\n " . +X join ("\n ", @Missing_Data_IDs) . +X "\n" +X if (@Missing_Data_IDs); +}; +X +# See if any mail messages are older than $Seconds +foreach $Queue_ID (@Queue_IDs) +{ +X # Get lots of info about this sendmail message via the control file +X ($Status, $Msg) = &Parse_Control_File ($opt_q, $Queue_ID, *Sender, +X *Recipients, *Errors_To, *Creation_Time, *Priority, *Status_Message, +X *Headers); +X next if ($Status == -1); +X if (! $Status) +X { +X warn "$Script_Name: $Queue_ID: $Msg\n"; +X next; +X }; +X +X # Report on message if it is older than $Seconds +X if ($Current_Time - $Creation_Time >= $Seconds) +X { +X # Build summary by host information. Keep track of each host destination +X # encountered. +X if ($opt_s) +X { +X %Host_Map = (); +X foreach (@Recipients) +X { +X if ((/@($ADDR_PART_EXPR)$/) || (/($ADDR_PART_EXPR)!$ADDR_PART_EXPR$/)) +X { +X ($Host = $1) =~ tr/A-Z/a-z/; +X $Host_Map {$Host} = 1; +X } +X else +X { +X warn "$Script_Name: could not find host part from $_; contact author\n"; +X }; +X }; +X +X # For each unique target host add to its stats +X grep ($Host_Queued {$_}++, keys (%Host_Map)); +X +X # Build summary by message information. +X $Message_Queued {$Status_Message}++ if ($Status_Message); +X }; +X +X # Build long report information for this creation time (there may be +X # more than one message created at the same time) +X if ($opt_d) +X { +X $Creation_Date = &date ($Creation_Time, $DATE_FORMAT); +X $Recipient_Info = &Format_Text_Block (join (", ", @Recipients), +X " Recipient: ", 1, 0, $opt_w, $SPLIT_EXPR); +X $Time_To_Report {$Creation_Time} .= <<"EOS"; +X +X ID: $Queue_ID +X Date: $Creation_Date +X Sender: $Sender +$Recipient_Info +EOS +X +X # Add the status message if available to long report +X if ($Status_Message) +X { +X $Time_To_Report {$Creation_Time} .= &Format_Text_Block ($Status_Message, +X " Status: ", 1, 0, $opt_w, $SPLIT_EXPR) . "\n"; +X }; +X }; +X }; +X +}; +X +# Add the summary report by target host? +if ($opt_s) +{ +X foreach $Host (sort (keys (%Host_Queued))) +X { +X $Host_Report .= &Format_Text_Block ($Host, +X sprintf (" %-9d ", $Host_Queued{$Host}), 1, 0, $opt_w, +X $SPLIT_EXPR) . "\n"; +X $Num_Hosts += $Host_Queued{$Host}; +X }; +X if ($Host_Report) +X { +X chop ($Host_Report); +X $Report .= &Format_Text_Block("\nSummary of messages in queue$Time_Info by destination host:\n", "", 0, 0, $opt_w); +X +X $Report .= <<"EOS"; +X +X Number of +X Messages Destination Host +X --------- ---------------- +$Host_Report +X --------- +X $Num_Hosts +EOS +X }; +}; +X +# Add the summary by message report? +if ($opt_s) +{ +X foreach $Message (sort (keys (%Message_Queued))) +X { +X $Message_Report .= &Format_Text_Block ($Message, +X sprintf (" %-9d ", $Message_Queued{$Message}), 1, 0, $opt_w, +X $SPLIT_EXPR) . "\n"; +X $Num_Messages += $Message_Queued{$Message}; +X }; +X if ($Message_Report) +X { +X chop ($Message_Report); +X $Report .= &Format_Text_Block ("\nSummary of messages in queue$Time_Info by status message:\n", "", 0, 0, $opt_w); +X +X $Report .= <<"EOS"; +X +X Number of +X Messages Status Message +X --------- -------------- +$Message_Report +X --------- +X $Num_Messages +EOS +X }; +}; +X +# Add the detailed message reports? +if ($opt_d) +{ +X foreach $Time (sort { $a <=> $b} (keys (%Time_To_Report))) +X { +X $Report .= &Format_Text_Block ("\nDetail of messages in queue$Time_Info sorted by creation date:\n","", 0, 0, $opt_w) if (! $Detailed_Header++); +X $Report .= $Time_To_Report {$Time}; +X }; +}; +X +# Now mail or print the report +if ($Report) +{ +X $Report .= "\n"; +X if ($opt_m) +X { +X ($Status, $Msg) = &Send_Mail ($Users, "sendmail queue report for $Current_Date", $Report, 0); +X die "${Script_Name}: $Msg" if (! $Status); +X } +X +X else +X { +X print $Report; +X }; +X +}; +X +# I am outta here... +exit (0); +SHAR_EOF +chmod 0555 src/cqueue || +echo 'restore of src/cqueue failed' +Wc_c="`wc -c < 'src/cqueue'`" +test 6702 -eq "$Wc_c" || + echo 'src/cqueue: original size 6702, current size' "$Wc_c" +fi +# ============= src/postclip ============== +if test -f 'src/postclip' -a X"$1" != X"-c"; then + echo 'x - skipping src/postclip (File already exists)' +else +echo 'x - extracting src/postclip (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'src/postclip' && +#!/usr/local/bin/perl +X +# NAME +# postclip - send only the headers to Postmaster +# +# SYNOPSIS +# postclip [ -v ] [ to ... ] +# +# AUTHOR +# Michael S. Muegel <mmuegel@mot.com> +# +# RCS INFORMATION +# $Source: /b/source/CVS/src/usr.sbin/sendmail/contrib/mmuegel,v $ +# $Revision: 1.3 $ of $Date: 1993/07/20 18:11:05 $ +X +# We use this to send off the mail +require "newgetopts.pl"; +require "mail.pl"; +X +# Get the basename of the script +($Script_Name = $0) =~ s/.*\///; +X +# Some famous constants +$USAGE = "Usage: $Script_Name [ -v ] [ to ... ]\n"; +$VERSION = "${Script_Name} by \$Author: glass $; \$Revision: 1.3 $ of \$Date: 1993/07/20 18:11:05 $"; +$SWITCHES = "v"; +X +# Let getopts parse for switches +$Status = &New_Getopts ($SWITCHES, $USAGE); +exit (0) if ($Status == -1); +exit (1) if (! $Status); +X +# Who should we send the modified mail to? +@ARGV = ("postmaster") if (! @ARGV); +$Users = join (" ", @ARGV); +@ARGV = (); +X +# Suck in the original header and save a few interesting lines +while (<>) +{ +X $Buffer .= $_ if (! /^From /); +X $Subject = $1 if (/^Subject:\s+(.*)$/); +X $From = $1 if (/^From:\s+(.*)$/); +X last if (/^$/); +}; +X +# Do not filter the message unless it has a subject and the subject indicates +# it is an NDN +if ($Subject && ($Subject =~ /^returned mail/i)) +{ +X # Slurp input by paragraph. Keep track of the last time we saw what +X # appeared to be NDN text. We keep this. +X $/ = "\n\n"; +X $* = 1; +X while (<>) +X { +X push (@Paragraphs, $_); +X $Last_Error_Para = $#Paragraphs +X if (/unsent message follows/i || /was not delivered because/); +X }; +X +X # Now save the NDN text into $Buffer +X $Buffer .= join ("", @Paragraphs [0..$Last_Error_Para]); +} +X +else +{ +X undef $/; +X $Buffer .= <>; +}; +X +# Send off the (possibly) modified mail +($Status, $Msg) = &Send_Mail ($Users, "", $Buffer, 0, $opt_v, 1); +die "$Script_Name: $Msg\n" if (! $Status); +SHAR_EOF +chmod 0555 src/postclip || +echo 'restore of src/postclip failed' +Wc_c="`wc -c < 'src/postclip'`" +test 1900 -eq "$Wc_c" || + echo 'src/postclip: original size 1900, current size' "$Wc_c" +fi +exit 0 + +-- ++----------------------------------------------------------------------------+ +| Michael S. Muegel | Internet E-Mail: mmuegel@mot.com | +| UNIX Applications Startup Group | Moto Dist E-Mail: X10090 | +| Corporate Information Office | Voice: (708) 576-0507 | +| Motorola | Fax: (708) 576-4153 | ++----------------------------------------------------------------------------+ diff --git a/usr.sbin/sendmail/contrib/rcpt-streaming b/usr.sbin/sendmail/contrib/rcpt-streaming new file mode 100644 index 0000000000..329a978286 --- /dev/null +++ b/usr.sbin/sendmail/contrib/rcpt-streaming @@ -0,0 +1,297 @@ +(Message /home/auspex/a/staff/eric/.mh/inbox:2575) +From: John Gardiner Myers <jgm+@cmu.edu> +Subject: contrib/rcpt-streaming +Date: Fri, 4 Jun 1993 13:54:06 -0400 (EDT) +To: sendmail@cs.berkeley.edu + +This patch implements "RCPT streaming" in sendmail version 6. This +patch is not an official part of sendmail. Please report all problems +with this patch to jgm+@cmu.edu. + +RCPT streming avoids network round trips by sending all RCPT commands +for a single SMTP transaction together. Sendmail then waits for all +the replies, matching them up with the apropriate addresses. + +Apply to the sendmail src directory (your line numbers may vary) and +compile with -DRCPTSTREAM + +diff -cr src.orig/deliver.c src/deliver.c +*** src.orig/deliver.c Thu May 27 14:38:22 1993 +--- src/deliver.c Fri Jun 4 13:50:02 1993 +*************** +*** 1325,1330 **** +--- 1325,1345 ---- + register int i; + + /* send the recipient list */ ++ #ifdef RCPTSTREAM ++ /*********************************************************************** ++ * ++ * RCPT streaming code by John G Myers, jgm+@cmu.edu ++ * This is not supported by the maintainer of sendmail. ++ * Report all bugs concerning RCPT streaming to jgm+@cmu.edu ++ * ++ *********************************************************************** ++ */ ++ for (to = tochain; to != NULL; to = to->q_tchain) ++ { ++ smtpstreammessage("RCPT To:<%s>", m, mci, ++ to->q_user); ++ } ++ #endif + tobuf[0] = '\0'; + for (to = tochain; to != NULL; to = to->q_tchain) + { +diff -cr src.orig/usersmtp.c src/usersmtp.c +*** src.orig/usersmtp.c Thu May 27 14:38:09 1993 +--- src/usersmtp.c Fri Jun 4 13:48:24 1993 +*************** +*** 44,49 **** +--- 44,61 ---- + + # include <sysexits.h> + # include <errno.h> ++ #ifdef RCPTSTREAM ++ /*********************************************************************** ++ * ++ * RCPT streaming code by John G Myers, jgm+@cmu.edu ++ * This is not supported by the maintainer of sendmail. ++ * Report all bugs concerning RCPT streaming to jgm+@cmu.edu ++ * ++ *********************************************************************** ++ */ ++ # include <sys/types.h> ++ # include <sys/time.h> ++ #endif + + # ifdef SMTP + +*************** +*** 62,67 **** +--- 74,87 ---- + char SmtpError[MAXLINE] = ""; /* save failure error messages */ + int SmtpPid; /* pid of mailer */ + ++ #ifdef RCPTSTREAM ++ char *SmtpStreamBuf; /* buffer for streaming output */ ++ int SmtpStreamBufSize = 0; /* allocated size of buffer */ ++ char *SmtpStreamStart; /* pointer to text not yet written */ ++ int SmtpStreamLen = 0; /* # chars not yet written */ ++ #endif ++ ++ + #ifdef __STDC__ + extern smtpmessage(char *f, MAILER *m, MCI *mci, ...); + #endif +*************** +*** 404,410 **** +--- 424,432 ---- + { + register int r; + ++ #ifndef RCPTSTREAM + smtpmessage("RCPT To:<%s>", m, mci, to->q_user); ++ #endif + + SmtpPhase = mci->mci_phase = "RCPT wait"; + setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); +*************** +*** 626,631 **** +--- 648,657 ---- + bool firstline = TRUE; + char junkbuf[MAXLINE]; + ++ #ifdef RCPTSTREAM ++ extern char MsgBuf[]; /* err.c */ ++ #endif ++ + if (mci->mci_out != NULL) + (void) fflush(mci->mci_out); + +*************** +*** 641,646 **** +--- 667,709 ---- + register char *p; + extern time_t curtime(); + ++ #ifdef RCPTSTREAM ++ if (SmtpStreamLen > 0) { ++ int outfd; ++ ++ outfd = fileno(mci->mci_out); ++ ++ nonblock(outfd, TRUE); ++ r = write(outfd, SmtpStreamStart, SmtpStreamLen); ++ nonblock(outfd, FALSE); ++ if (r == -1 && errno != EAGAIN ++ #ifdef EWOULDBLOCK ++ && errno != EWOULDBLOCK ++ #endif ++ ) { ++ ++ mci->mci_errno = errno; ++ message("451 streamreply: write error to %s", ++ mci->mci_host); ++ ++ /* if debugging, pause so we can see state */ ++ if (tTd(18, 100)) ++ pause(); ++ # ifdef LOG ++ if (LogLevel > 0) ++ syslog(LOG_INFO, "%s", &MsgBuf[4]); ++ # endif /* LOG */ ++ /* stop trying to write output */ ++ SmtpStreamLen = 0; ++ continue; ++ } ++ if (r > 0) { ++ SmtpStreamStart += r; ++ SmtpStreamLen -= r; ++ } ++ } ++ #endif /* RCPTSTREAM */ ++ + /* actually do the read */ + if (e->e_xfp != NULL) + (void) fflush(e->e_xfp); /* for debugging */ +*************** +*** 742,747 **** +--- 805,880 ---- + + return (r); + } ++ ++ #ifdef RCPTSTREAM ++ /* ++ ** SMTPSTREAMMESSAGE -- buffer message to be streamed to server ++ ** ++ ** Parameters: ++ ** f -- format ++ ** m -- the mailer to control formatting. ++ ** a, b, c -- parameters ++ ** ++ ** Returns: ++ ** none. ++ ** ++ ** Side Effects: ++ ** stores message in SmtpStreamBuf ++ */ ++ ++ /*VARARGS1*/ ++ #ifdef __STDC__ ++ smtpstreammessage(char *f, MAILER *m, MCI *mci, ...) ++ #else ++ smtpstreammessage(f, m, mci, va_alist) ++ char *f; ++ MAILER *m; ++ MCI *mci; ++ va_dcl ++ #endif ++ { ++ VA_LOCAL_DECL ++ int len; ++ ++ VA_START(mci); ++ (void) vsprintf(SmtpMsgBuffer, f, ap); ++ VA_END; ++ ++ if (tTd(18, 1) || Verbose) ++ nmessage(">>> %s", SmtpMsgBuffer); ++ ++ if (mci->mci_out == NULL) { ++ if (tTd(18, 1)) printf("smtpstreammessage: NULL mci_out\n"); ++ return; ++ } ++ ++ strcat(SmtpMsgBuffer, m == NULL ? "\r\n" : m->m_eol); ++ len = strlen(SmtpMsgBuffer); ++ ++ if (SmtpStreamLen == 0) { ++ if (SmtpStreamBufSize == 0) { ++ SmtpStreamBufSize = MAXLINE; ++ SmtpStreamBuf = xalloc(SmtpStreamBufSize); ++ } ++ SmtpStreamStart = SmtpStreamBuf; ++ } ++ ++ if (SmtpStreamBufSize - SmtpStreamLen < len + 1) { ++ int start = SmtpStreamStart - SmtpStreamBuf; ++ SmtpStreamBufSize += MAXLINE; ++ SmtpStreamBuf = realloc(SmtpStreamBuf, SmtpStreamBufSize); ++ if (!SmtpStreamBuf) { ++ syserr("Out of memory!!"); ++ abort(); ++ /* exit(EX_UNAVAILABLE); */ ++ } ++ SmtpStreamStart = SmtpStreamBuf + start; ++ } ++ ++ strcpy(SmtpStreamBuf + SmtpStreamLen, SmtpMsgBuffer); ++ SmtpStreamLen += len; ++ } ++ #endif /* RCPTSTREAM */ + /* + ** SMTPMESSAGE -- send message to server + ** +Only in src: usersmtp.c.orig +Only in src: usersmtp.c~ +Only in src: usersmtp.o +diff -cr src.orig/util.c src/util.c +*** src.orig/util.c Thu May 27 14:38:20 1993 +--- src/util.c Wed Jun 2 16:39:15 1993 +*************** +*** 955,960 **** +--- 955,1004 ---- + return (FALSE); + return (TRUE); + } ++ ++ #ifdef RCPTSTREAM ++ /*********************************************************************** ++ * ++ * RCPT streaming code by John G Myers, jgm+@cmu.edu ++ * This is not supported by the maintainer of sendmail. ++ * Report all bugs concerning RCPT streaming to jgm+@cmu.edu ++ * ++ *********************************************************************** ++ */ ++ ++ #include <fcntl.h> ++ ++ /* ++ ** NONBLOCK -- set or clear non-blocking mode on file descriptor ++ ** ++ ** Parameters: ++ ** fd -- the file descriptor ++ ** mode -- TRUE to set non-blocking mode ++ ** FALSE to clear non-blocking mode ++ ** ++ ** Returns: ++ ** none ++ ** ++ ** Side Effects: ++ ** modifies nonblocking status of fd ++ */ ++ ++ nonblock(fd, mode) ++ int fd; ++ bool mode; ++ { ++ int flags; ++ ++ flags = fcntl(fd, F_GETFL, 0); ++ if (mode) { ++ flags |= FNONBIO; ++ } ++ else { ++ flags &= ~FNONBIO; ++ } ++ fcntl(fd, F_SETFL, flags); ++ } ++ #endif + /* + ** STRCONTAINEDIN -- tell if one string is contained in another + ** +Only in src: util.c.orig +Only in src: util.o +Only in src: version.o diff --git a/usr.sbin/sendmail/contrib/xla/README b/usr.sbin/sendmail/contrib/xla/README new file mode 100644 index 0000000000..a72fd03c83 --- /dev/null +++ b/usr.sbin/sendmail/contrib/xla/README @@ -0,0 +1,207 @@ + XLA - Extended Load Average design for Sendmail R6 + -------------------------------------------------- + + Christophe Wolfhugel - Herve Schauer Consultants + wolf@grasp.insa-lyon.fr, wolf@hsc-sec.fr + + +WARNING: this extension is supplied as a contribution to Sendmail. +Should you have trouble, questions, please contact me directly, and +*not* the Sendmail development team. + + +ABSTRACT + +Sendmail currently furnishes a limitation mecanism which is based on +the system load average, when available. Experience has prooven that +this was not sufficiant for some particular situations, for example +if you have slow and/or overloaded links. This can easily cause both +system and network congestions with Sendmail having to handle a large +number of simultaneous sessions on the same overloaded link, causing +most of the SMTP sessions to timeout after a long time. The system +load average is also generally too slow to react when your system +gets a burst of incoming or outgoing SMTP sessions which on some +stations can easily cause system unavailabilities. + +The extended load average module has been designed in order to furnish +a way of limitation the load generated by Sendmail to both your +system and your network. This design can be used either alone or as +complementary to the system load average if your system supports it. + +Limitation is based on the number of incoming/outgoing SMTP sessions, +and remote hosts are classified in classes. The system administrator +will define a maximum number of incoming SMTP sessions as well as +a maximum total (incoming + outgoing) sessions for each class of +hosts. A class can be either an individual machine or a network. + +When the limit is reached for a given class, all incoming SMTP +connections will be politely refused. When the limit is reached for +all classes, the SMTP connections will be refused by the system +(which one could consider as less politely :)). +On outgoing mail, messages will be queued for delayed processing. + +The extended load average parameters are given in the Sendmail +configuration file, and when not present, Sendmail behaves the +usual way. + + +COMPILATION + +Copy the xla.c module in the src sub-directory, edit the Makefile +in order to define XLA (-DXLA). Also add the xla.[co] module name +in the list of files so that it gets compiled. + +Regenerate sendmail by removing all objects, or at least those +containing references to XLA (this list may vary, so use grep to +get the module list). This will generate a new sendmail executable +containing the xla code. + +Debugging level 59 has been assigned to this module and when used +it provides some output (sendmail -d59.x). Please check the source +code to see which levels are supported. + + +CONFIGURATION + +The extended average uses a new set of configuration lines in the +sendmail.cf file. All newly introduced line begin with the letter L +(capital L). + +Before detailling the syntax, first an example (this can be placed +at any section of the sendmail.cf file, note that the order is +important). Fields are separated by (one or more) tabs/spaces. + +# File name used to store the counters +L/etc/sendmail.la +# Classes definition +# Lname #queue #reject +L*.insa-lyon.fr 8 3 +L*.univ-lyon1.fr 6 4 +L* 15 16 + +The first line defines the working file which will be used in order +to have the occurences of Sendmail read and update the counters. The +format of this file is described in the "Design" section. +This line is mandatory and the specified file must be absolute (ie +begin with a slash). + +Then you can specify one or more classes. The last class (*) is also +mandatory and should be in last position as the first match will stop +the search and if there is no match the behavior of Sendmail is unknown. + +Each class has three fields separated by one or more tabs/spaces. + +L{mask} {queue_#} {refuse_#} + +The {mask} is a simple mask. It can be either an explicit host name +(like grasp.insa-lyon.fr) or a mask starting with "*." or just "*". +No other variants are allowed. + +Lgrasp.insa-lyon.fr will match exactely any session to/from this host. + +L*.insa-lyon.fr will match any session to/from any machine in the + insa-lyon.fr domain as well as from the machine + named "insa-lyon.fr" if it exists. + +L* will match any session, and thus should also be + last in the list to act as a catchup line. + +The {queue_#} is the maximum number of SMTP sessions in the given class +for both incoming and outgoing messages. The {refuse_#} indicates when +to refuse incoming messages for this class. The interaction between +those counters is somewhat subtle. It seems logical that a standard +configuration has {queue_#} >= {refuse_#}, and in fact in most +configurations they can be equal (that's why what I use in my environment). +Thus, this is not mandatory. If {queue_#} < {refuse_#} outgoing messages +will be lower priority than incoming messages and once a class gets loaded +the outgoing messages are blocked first. + +I use very low values in some situations, for example I have a customer +connected to the Internet via a 9600 bps line, they also have internal +users sending burst of messages (10, 20 small messages coming in just +one or two seconds). Both situations were unsupportable. The line is +too slow to handle many simultaneous connections and the mail server +does not have the ressources to handle such a heavy load (it's a 12 Megs +Sun 3 also doing Usenet news). + +I have defined following section in the configuration file, and experience +shows the benefits for everyone. Fake domain for the example: customer.fr. + +L/etc/sendmail.la +L*.customer.fr 8 8 +L* 3 3 + +This means that there might not be more than 8 simultaneous SMTP sessions +between the mail server and any other internal host. This is to protect +the station from heavy loads like users (or applications !) sending +several tenths of messages in just a few seconds). +No more than 3 SMTP sessions are authorized with any other host, this is +to save the load of the slow 9600 line to the Internet. + +Drawback is that is you have 3 * 2 Megs sessions established from/to the +outside, all your mail will be held until one slot gets available, on +a 9600 bps line just make your counts, il blocks your line during over +one hour. + + +DESIGN + +Sendmail will analyze the "L" lines in the configuration file during +startup (or read the initialized structure from the frozen file). +When started in daemon mode (and only there), any existing working file +will be cleared and a new one is created. Each class gets a record in +the sendmail.la work file. The size of this record is a short integer +(generally two bytes) and represents the count of active sessions in +the given class. Read/Write operations in this file are done in +one operation (as anyway the size is far below one disk sector). The +file is locked with Sendmail's lockfile() function priori to any +access. + +Handling incoming SMTP sessions. + +There is interaction is two points in the Sendmail source code. First +on the listen system call: if all slots in all classes are in use, +a listen(0) is done so that the system rejects any incoming SMTP session. +This avois to fork and then reject the connexion. + +If there are some free slots, nothing better than accepting the +connection, then forking can be done. The child process then checks if +the adequate class is full or not. If full, it rejects the connection +with a "421 Too many sessions" diagnostic to the sender (which should +then appear when the remote users do a mailq). If the treshold {reject_#} +is not reached, the connection is accepted and the counter is sendmail.la +is updated. + +Handling outgoing SMTP sessions. + +As soon as Sendmail needs to connect to a distant host, the adequate class +is checked against {queue_#} and if no slots are available, the message is +queued for further processing. + +Sendmail's connection caching. + +Sendmail-R6 introduces a new design: connection caching, ie several SMTP +sessions can be opened at the same time. This could cause some problems +when sending mail, as after having a few connections opened, all slots +could be in use and generate a partial delivery of the message. In +order to deal with this, xla.c uses following design "for a given +sendmail process, only the first connection in a given class is counted". +This can be done because sendmail does not do parralel message sending +on the different channels. + +End of connection. + +As soon as a connection is closed, the counters will be automatically +updated. + + + +Please look at the code to understand of all this works. Comments, +suggestions, questions welcome. + + + + Christophe Wolfhugel + Herve Schauer Consultants + Paris, France + May 23, 1993 diff --git a/usr.sbin/sendmail/contrib/xla/xla.c b/usr.sbin/sendmail/contrib/xla/xla.c new file mode 100644 index 0000000000..1b3c30847e --- /dev/null +++ b/usr.sbin/sendmail/contrib/xla/xla.c @@ -0,0 +1,528 @@ +/* + * (C) Copyright 1993, Herve Schauer Consultants + * + * This module written by Christophe.Wolfhugel@hsc-sec.fr + * is to be used under the same conditions and terms (and absence + * or warranties) than the other modules of the Sendmail package. + * + * ABSOLUTELY NO WARRANTY. USE AT YOUR OWN RISKS. + * + */ + + +#ifdef XLA + +#ifndef MAXLARULES +# define MAXLARULES 20 +#endif + +# include "sendmail.h" + +typedef struct { + short queue; /* # of connexions to have queueing */ + short reject; /* # of conn. to reject */ + short num; /* # of increments this process */ + char *mask; /* Mask (domain) */ + } XLARULE; + +char *XlaFname; /* Work file name */ +char XlaHostName[1024]; /* Temporary */ +int XlaNext; /* # of XlaRules */ +pid_t XlaPid; /* Pid updating the tables */ +XLARULE XlaRules[MAXLARULES]; /* The rules themselves */ +short XlaCtr[MAXLARULES]; /* Counter (for work file only) */ + +extern bool lockfile(); + +/* +** XLAMATCH -- Matches a fnmatch like expression. +** +** Parameters: +** mask -- mask to match the string too; +** name -- string. +** +** Mask can either be a plain string or a simplified fnmatch like mask: +** *.string or string.* +** No other alternatives are accepted. +** +** Returns: +** none. +** +** Side Effects: +** none. +*/ + +bool +XlaMatch(mask, name) + char *mask, *name; +{ + int l1, l2; + + l1 = strlen(mask); l2 = strlen(name); + if (l1 == 1 && mask[0] == '*') return(TRUE); + if (mask[0] == '*' && mask[1] == '.') { + if (l2 < (l1 - 2)) return(FALSE); + if (strcasecmp(&mask[2], name) == 0) return(TRUE); + if (strcasecmp(&mask[1], name + l2 - l1 + 1) == 0) return(TRUE); + return(FALSE); + } + if (l1 < 3) return(FALSE); + if (mask[l1 -1] == '*') { + if (l2 < l1 - 1) return(FALSE); + if (strncasecmp(mask, name, l1 - 1) == 0) return(TRUE); + return(FALSE); + } + if (strcasecmp(mask, name) == 0) return(TRUE); + return(FALSE); +} + +/* +** XLAZERO -- Zeroes the used variables +** +** Just initializes some variables, called once at sendmail +** startup. +** +** Parameters: +** none. +** +** Returns: +** none. +** +** Side Effects: +** none. +*/ + +xla_zero() +{ + if (tTd(59, 1)) { + printf("xla_zero\n"); + } + XlaFname = NULL; + XlaNext = 0; + XlaPid = 0; + memset(&XlaRules[0], 0, sizeof(XLARULE) * MAXLARULES); +} + + +/* +** XLAINIT -- initialized extended load average stuff +** +** This routine handles the L lines appearing in the configuration +** file. +** +** L/etc/sendmail.la indicates the working file +** Lmask #1 #2 Xtended LA to apply to mask +** #1 = Queueing # of connections +** #2 = Reject connections. +** +** Parameters: +** line -- the cf file line to parse. +** +** Returns: +** none. +** +** Side Effects: +** Builds several internal tables. +*/ + +xla_init(line) + char *line; +{ + char *s; + + if (tTd(59, 1)) { + printf("xla_init line: %s\n", line); + } + if (XlaFname == NULL && *line == '/') { /* Work file name */ + XlaFname = newstr(line); + if (tTd(59, 10)) + printf("xla_init: fname = %s\n", XlaFname); + return; + } + if (XlaNext == MAXLARULES) { + syserr("too many xla rules defined (%d max)", MAXLARULES); + return; + } + s = strtok(line, " \t"); + if (s == NULL) { + syserr("xla: line unparseable"); + return; + } + XlaRules[XlaNext].mask = newstr(s); + s = strtok(NULL, " \t"); + if (s == NULL) { + syserr("xla: line unparseable"); + return; + } + XlaRules[XlaNext].queue = atoi(s); + s = strtok(NULL, " \t"); + if (s == NULL) { + syserr("xla: line unparseable"); + return; + } + XlaRules[XlaNext].reject = atoi(s); + if (tTd(59, 10)) + printf("xla_init: rule #%d = %s q=%d r=%d\n", XlaNext, + XlaRules[XlaNext].mask, + XlaRules[XlaNext].queue, XlaRules[XlaNext].reject); + XlaNext++; +} + + +/* +** XLACREATEFILE -- Create the working file +** +** Tries to create the working file, called each time sendmail is +** invoked with the -bd option. +** +** Parameters: +** none. +** +** Returns: +** none. +** +** Side Effects: +** Creates the working file (sendmail.la) and zeroes it. +*/ + +xla_create_file() +{ + int fd, i; + + if (tTd(59, 1)) + printf("xla_create_file:\n"); + if (XlaFname == NULL) return; + fd = open(XlaFname, O_RDWR|O_CREAT, 0644); + if (fd == -1) { + XlaFname = NULL; + syserr("xla_create_file: open failed"); + return; + } + if (!lockfile(fd, XlaFname, LOCK_EX)) { + close(fd); + XlaFname = NULL; + syserr("xla_create_file: can't set lock"); + return; + } + if (ftruncate(fd, 0) == -1) { + close(fd); + XlaFname = NULL; + syserr("xla_create_file: can't truncate XlaFname"); + return; + } + if (write(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) { + XlaFname == NULL; + syserr("xla_create_file: can't write XlaFname"); + } + close(fd); +} + + +/* +** XLASMTPOK -- Checks if all slots are in use +** +** Check is there are still some slots available for an SMTP +** connection. +** +** Parameters: +** none. +** +** Returns: +** TRUE -- slots are available; +** FALSE -- no more slots. +** +** Side Effects: +** Reads a file, uses a lock and updates sendmail.la if a slot +** is free for use. +*/ + +bool +xla_smtp_ok() +{ + int fd, i; + + if (tTd(59, 1)) + printf("xla_smtp_ok:\n"); + if (XlaFname == NULL) return(TRUE); + fd = open(XlaFname, O_RDWR, 0644); + if (fd == -1) { + XlaFname = NULL; + syserr("xla_smtp_ok: open failed"); + return(TRUE); + } + if (!lockfile(fd, XlaFname, LOCK_EX)) { + close(fd); + XlaFname = NULL; + syserr("xla_smtp_ok: can't set lock"); + return(TRUE); + } + if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) { + close(fd); + XlaFname = NULL; + syserr("xla_smtp_ok: can't read XlaFname"); + return(TRUE); + } + close(fd); + for (i = 0; i < XlaNext; i++) { + if (XlaCtr[i] < XlaRules[i].reject) + return(TRUE); + } + return(FALSE); +} + + +/* +** XLAHOSTOK -- Can we accept a connection from this host +** +** Check the quota for the indicated host +** +** Parameters: +** name -- host name or IP# (string) +** +** Returns: +** TRUE -- we can accept the connection; +** FALSE -- we must refuse the connection.1 +** +** Side Effects: +** Reads and writes a file, uses a lock and still updates +** sendmail.la is a slot gets assigned. +*/ + +bool +xla_host_ok(name) + char *name; +{ + int fd, i; + + if (tTd(59, 1)) + printf("xla_host_ok:\n"); + if (XlaFname == NULL) return(TRUE); + fd = open(XlaFname, O_RDWR, 0644); + if (fd == -1) { + XlaFname = NULL; + syserr("xla_host_ok: open failed"); + return(TRUE); + } + XlaPid = getpid(); + if (!lockfile(fd, XlaFname, LOCK_EX)) { + close(fd); + XlaFname = NULL; + syserr("xla_host_ok: can't set lock"); + return(TRUE); + } + if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) { + close(fd); + XlaFname = NULL; + syserr("xla_smtp_ok: can't read XlaFname"); + return(TRUE); + } + strncpy(XlaHostName, name, sizeof(XlaHostName) -1); + XlaHostName[sizeof(XlaHostName) -1] = 0; + i = strlen(name) - 1; + if (i >= 0 && XlaHostName[i] == '.') XlaHostName[i] = 0; + for (i = 0; i < XlaNext; i++) { + if (XlaMatch(XlaRules[i].mask, XlaHostName)) { + if (XlaCtr[i] < XlaRules[i].reject) { + if (XlaRules[i].num++ == 0) { + XlaCtr[i]++; + lseek(fd, i*sizeof(XlaCtr[i]), SEEK_SET); + if (write(fd, &XlaCtr[i], sizeof(XlaCtr[i])) != sizeof(XlaCtr[i])) + XlaFname = NULL; + } + close(fd); + return(TRUE); + } + close(fd); + return(FALSE); + } + } + close(fd); + return(TRUE); +} + +/* +** XLANOQUEUEOK -- Can we sent this message to the remote host +** +** Check if we can send to the remote host +** +** Parameters: +** name -- host name or IP# (string) +** +** Returns: +** TRUE -- we can send the message to the remote site; +** FALSE -- we can't connect the remote host, queue. +** +** Side Effects: +** Reads and writes a file, uses a lock. +** And still updates the sendmail.la file. +*/ + +bool +xla_noqueue_ok(name) + char *name; +{ + int fd, i; + + if (tTd(59, 1)) + printf("xla_noqueue_ok:\n"); + if (XlaFname == NULL) return(TRUE); + fd = open(XlaFname, O_RDWR, 0644); + if (fd == -1) { + XlaFname = NULL; + syserr("xla_noqueue_ok: open failed"); + return(TRUE); + } + if (!lockfile(fd, XlaFname, LOCK_EX)) { + close(fd); + XlaFname = NULL; + syserr("xla_noqueue_ok: can't set lock"); + return(TRUE); + } + XlaPid = getpid(); + if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) { + close(fd); + XlaFname = NULL; + syserr("xla_noqueue_ok: can't read XlaFname"); + return(TRUE); + } + strncpy(XlaHostName, name, sizeof(XlaHostName) -1); + XlaHostName[sizeof(XlaHostName) -1] = 0; + i = strlen(name) - 1; + if (i >= 0 && XlaHostName[i] == '.') XlaHostName[i] = 0; + for (i = 0; i < XlaNext; i++) { + if (XlaMatch(XlaRules[i].mask, XlaHostName)) { + if (XlaCtr[i] < XlaRules[i].queue) { + if (XlaRules[i].num++ == 0) { + XlaCtr[i]++; + lseek(fd, i*sizeof(XlaCtr[i]), SEEK_SET); + if (write(fd, &XlaCtr[i], sizeof(XlaCtr[i])) != sizeof(XlaCtr[i])) + XlaFname = NULL; + } + close(fd); + return(TRUE); + } + close(fd); + return(FALSE); + } + } + close(fd); + return(TRUE); +} + + +/* +** XLAHOSTEND -- Notice that a connection is terminated. +** +** Updates the counters to reflect the end of an SMTP session +** (in or outgoing). +** +** Parameters: +** name -- host name or IP# (string) +** +** Returns: +** none. +** +** Side Effects: +** Reads and writes a file, uses a lock. +** And still updates sendmail.la. +*/ + +xla_host_end(name) + char *name; +{ + int fd, i; + + if (tTd(59, 1)) + printf("xla_host_end:\n"); + if (XlaFname == NULL || XlaPid != getpid()) return; + fd = open(XlaFname, O_RDWR, 0644); + if (fd == -1) { + XlaFname = NULL; + syserr("xla_host_end: open failed"); + return; + } + if (!lockfile(fd, XlaFname, LOCK_EX)) { + close(fd); + XlaFname = NULL; + syserr("xla_host_end: can't set lock"); + return; + } + if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) { + close(fd); + XlaFname = NULL; + syserr("xla_host_end: can't read XlaFname"); + return(TRUE); + } + strncpy(XlaHostName, name, sizeof(XlaHostName) -1); + XlaHostName[sizeof(XlaHostName) -1] = 0; + i = strlen(name) - 1; + if (i >= 0 && XlaHostName[i] == '.') XlaHostName[i] = 0; + for (i = 0; i < XlaNext; i++) { + if (XlaMatch(XlaRules[i].mask, XlaHostName)) { + if (XlaRules[i].num > 0 && XlaRules[i].num-- == 1) { + if (XlaCtr[i]) XlaCtr[i]--; + lseek(fd, i*sizeof(XlaCtr[i]), SEEK_SET); + if (write(fd, &XlaCtr[i], sizeof(XlaCtr[i])) + != sizeof(XlaCtr[i])) + XlaFname = NULL; + } + close(fd); + return; + } + } + close(fd); +} + +/* +** XLAALLEND -- Mark all connections as closed. +** +** Generally due to an emergency exit. +** +** Parameters: +** name -- host name or IP# (string) +** +** Returns: +** none. +** +** Side Effects: +** Reads and writes a file, uses a lock. +** And guess what: updates sendmail.la. +*/ + +xla_all_end() +{ + int fd, i; + + if (tTd(59, 1)) + printf("xla_all_end:\n"); + if (XlaFname == NULL || XlaPid != getpid()) return; + fd = open(XlaFname, O_RDWR, 0644); + if (fd == -1) { + XlaFname = NULL; + syserr("xla_all_end: open failed"); + return; + } + if (!lockfile(fd, XlaFname, LOCK_EX)) { + close(fd); + XlaFname = NULL; + syserr("xla_all_end: can't set lock"); + return; + } + if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) { + close(fd); + XlaFname = NULL; + syserr("xla_all_end: can't read XlaFname"); + return(TRUE); + } + for (i = 0; i < XlaNext; i++) { + if (XlaCtr[i] > 0 && XlaRules[i].num > 0) { + XlaCtr[i]--; XlaRules[i].num = 0; + } + } + lseek(fd, 0, SEEK_SET); + if (write(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) { + XlaFname = NULL; + } + close(fd); +} +#endif /* XLA */ diff --git a/usr.sbin/sendmail/doc/intro/intro.me b/usr.sbin/sendmail/doc/intro/intro.me new file mode 100644 index 0000000000..5907004efc --- /dev/null +++ b/usr.sbin/sendmail/doc/intro/intro.me @@ -0,0 +1,1478 @@ +.\" Copyright (c) 1983 Eric P. Allman +.\" Copyright (c) 1988, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)intro.me 8.1 (Berkeley) 6/8/93 +.\" +.\" pic -Pxx intro.me | ditroff -me -Pxx +.eh 'SMM:9-%''SENDMAIL \*- An Internetwork Mail Router' +.oh 'SENDMAIL \*- An Internetwork Mail Router''SMM:9-%' +.nr si 3n +.if n .ls 2 +.+c +.(l C +.sz 14 +SENDMAIL \*- An Internetwork Mail Router +.sz +.sp +Eric Allman\(dg +.sp 0.5 +.i +University of California, Berkeley +Mammoth Project +.)l +.sp +.(l F +.ce +ABSTRACT +.sp \n(psu +Routing mail through a heterogenous internet presents many new +problems. Among the worst of these is that of address mapping. +Historically, this has been handled on an +.i "ad hoc" +basis. However, +this approach has become unmanageable as internets grow. +.sp \n(psu +Sendmail acts a unified "post office" to which all mail can be +submitted. Address interpretation is controlled by a production +system, which can parse both domain-based addressing and old-style +.i "ad hoc" +addresses. +The production system is powerful +enough to rewrite addresses in the message header to conform to the +standards of a number of common target networks, including old +(NCP/RFC733) Arpanet, new (TCP/RFC822) Arpanet, UUCP, and Phonenet. +Sendmail also implements an SMTP server, message +queueing, and aliasing. +.)l +.sp 2 +.(f +\(dgA considerable part of this work +was done while under the employ +of the INGRES Project +at the University of California at Berkeley +and at Britton Lee. +.)f +.pp +.i Sendmail +implements a general internetwork mail routing facility, +featuring aliasing and forwarding, +automatic routing to network gateways, +and flexible configuration. +.pp +In a simple network, +each node has an address, +and resources can be identified +with a host-resource pair; +in particular, +the mail system can refer to users +using a host-username pair. +Host names and numbers have to be administered by a central authority, +but usernames can be assigned locally to each host. +.pp +In an internet, +multiple networks with different characterstics +and managements +must communicate. +In particular, +the syntax and semantics of resource identification change. +Certain special cases can be handled trivially +by +.i "ad hoc" +techniques, +such as +providing network names that appear local to hosts +on other networks, +as with the Ethernet at Xerox PARC. +However, the general case is extremely complex. +For example, +some networks require point-to-point routing, +which simplifies the database update problem +since only adjacent hosts must be entered +into the system tables, +while others use end-to-end addressing. +Some networks use a left-associative syntax +and others use a right-associative syntax, +causing ambiguity in mixed addresses. +.pp +Internet standards seek to eliminate these problems. +Initially, these proposed expanding the address pairs +to address triples, +consisting of +{network, host, resource} +triples. +Network numbers must be universally agreed upon, +and hosts can be assigned locally +on each network. +The user-level presentation was quickly expanded +to address domains, +comprised of a local resource identification +and a hierarchical domain specification +with a common static root. +The domain technique +separates the issue of physical versus logical addressing. +For example, +an address of the form +.q "eric@a.cc.berkeley.arpa" +describes only the logical +organization of the address space. +.pp +.i Sendmail +is intended to help bridge the gap +between the totally +.i "ad hoc" +world +of networks that know nothing of each other +and the clean, tightly-coupled world +of unique network numbers. +It can accept old arbitrary address syntaxes, +resolving ambiguities using heuristics +specified by the system administrator, +as well as domain-based addressing. +It helps guide the conversion of message formats +between disparate networks. +In short, +.i sendmail +is designed to assist a graceful transition +to consistent internetwork addressing schemes. +.sp +.pp +Section 1 discusses the design goals for +.i sendmail . +Section 2 gives an overview of the basic functions of the system. +In section 3, +details of usage are discussed. +Section 4 compares +.i sendmail +to other internet mail routers, +and an evaluation of +.i sendmail +is given in section 5, +including future plans. +.sh 1 "DESIGN GOALS" +.pp +Design goals for +.i sendmail +include: +.np +Compatibility with the existing mail programs, +including Bell version 6 mail, +Bell version 7 mail +[UNIX83], +Berkeley +.i Mail +[Shoens79], +BerkNet mail +[Schmidt79], +and hopefully UUCP mail +[Nowitz78a, Nowitz78b]. +ARPANET mail +[Crocker77a, Postel77] +was also required. +.np +Reliability, in the sense of guaranteeing +that every message is correctly delivered +or at least brought to the attention of a human +for correct disposal; +no message should ever be completely lost. +This goal was considered essential +because of the emphasis on mail in our environment. +It has turned out to be one of the hardest goals to satisfy, +especially in the face of the many anomalous message formats +produced by various ARPANET sites. +For example, +certain sites generate improperly formated addresses, +occasionally +causing error-message loops. +Some hosts use blanks in names, +causing problems with +UNIX mail programs that assume that an address +is one word. +The semantics of some fields +are interpreted slightly differently +by different sites. +In summary, +the obscure features of the ARPANET mail protocol +really +.i are +used and +are difficult to support, +but must be supported. +.np +Existing software to do actual delivery +should be used whenever possible. +This goal derives as much from political and practical considerations +as technical. +.np +Easy expansion to +fairly complex environments, +including multiple +connections to a single network type +(such as with multiple UUCP or Ether nets +[Metcalfe76]). +This goal requires consideration of the contents of an address +as well as its syntax +in order to determine which gateway to use. +For example, +the ARPANET is bringing up the +TCP protocol to replace the old NCP protocol. +No host at Berkeley runs both TCP and NCP, +so it is necessary to look at the ARPANET host name +to determine whether to route mail to an NCP gateway +or a TCP gateway. +.np +Configuration should not be compiled into the code. +A single compiled program should be able to run as is at any site +(barring such basic changes as the CPU type or the operating system). +We have found this seemingly unimportant goal +to be critical in real life. +Besides the simple problems that occur when any program gets recompiled +in a different environment, +many sites like to +.q fiddle +with anything that they will be recompiling anyway. +.np +.i Sendmail +must be able to let various groups maintain their own mailing lists, +and let individuals specify their own forwarding, +without modifying the system alias file. +.np +Each user should be able to specify which mailer to execute +to process mail being delivered for him. +This feature allows users who are using specialized mailers +that use a different format to build their environment +without changing the system, +and facilitates specialized functions +(such as returning an +.q "I am on vacation" +message). +.np +Network traffic should be minimized +by batching addresses to a single host where possible, +without assistance from the user. +.pp +These goals motivated the architecture illustrated in figure 1. +.(z +.hl +.ie t \ +\{\ +.ie !"\*(.T"" \ +\{\ +.PS +boxht = 0.5i +boxwid = 1.0i + + down +S: [ + right + S1: box "sender1" + move + box "sender2" + move + S3: box "sender3" + ] + arrow +SM: box "sendmail" wid 2i ht boxht + arrow +M: [ + right + M1: box "mailer1" + move + box "mailer2" + move + M3: box "mailer3" + ] + + arrow from S.S1.s to 1/2 between SM.nw and SM.n + arrow from S.S3.s to 1/2 between SM.n and SM.ne + + arrow from 1/2 between SM.sw and SM.s to M.M1.n + arrow from 1/2 between SM.s and SM.se to M.M3.n +.PE +.\} +.el \ +. sp 18 +.\} +.el \{\ +.(c ++---------+ +---------+ +---------+ +| sender1 | | sender2 | | sender3 | ++---------+ +---------+ +---------+ + | | | + +----------+ + +----------+ + | | | + v v v + +-------------+ + | sendmail | + +-------------+ + | | | + +----------+ + +----------+ + | | | + v v v ++---------+ +---------+ +---------+ +| mailer1 | | mailer2 | | mailer3 | ++---------+ +---------+ +---------+ +.)c +.\} + +.ce +Figure 1 \*- Sendmail System Structure. +.hl +.)z +The user interacts with a mail generating and sending program. +When the mail is created, +the generator calls +.i sendmail , +which routes the message to the correct mailer(s). +Since some of the senders may be network servers +and some of the mailers may be network clients, +.i sendmail +may be used as an internet mail gateway. +.sh 1 "OVERVIEW" +.sh 2 "System Organization" +.pp +.i Sendmail +neither interfaces with the user +nor does actual mail delivery. +Rather, +it collects a message +generated by a user interface program (UIP) +such as Berkeley +.i Mail , +MS +[Crocker77b], +or MH +[Borden79], +edits the message as required by the destination network, +and calls appropriate mailers +to do mail delivery or queueing for network transmission\**. +.(f +\**except when mailing to a file, +when +.i sendmail +does the delivery directly. +.)f +This discipline allows the insertion of new mailers +at minimum cost. +In this sense +.i sendmail +resembles the Message Processing Module (MPM) +of [Postel79b]. +.sh 2 "Interfaces to the Outside World" +.pp +There are three ways +.i sendmail +can communicate with the outside world, +both in receiving and in sending mail. +These are using the conventional UNIX +argument vector/return status, +speaking SMTP over a pair of UNIX pipes, +and speaking SMTP over an interprocess(or) channel. +.sh 3 "Argument vector/exit status" +.pp +This technique is the standard UNIX method +for communicating with the process. +A list of recipients is sent in the argument vector, +and the message body is sent on the standard input. +Anything that the mailer prints +is simply collected and sent back to the sender +if there were any problems. +The exit status from the mailer is collected +after the message is sent, +and a diagnostic is printed if appropriate. +.sh 3 "SMTP over pipes" +.pp +The SMTP protocol +[Postel82] +can be used to run an interactive lock-step interface +with the mailer. +A subprocess is still created, +but no recipient addresses are passed to the mailer +via the argument list. +Instead, they are passed one at a time +in commands sent to the processes standard input. +Anything appearing on the standard output +must be a reply code +in a special format. +.sh 3 "SMTP over an IPC connection" +.pp +This technique is similar to the previous technique, +except that it uses a 4.2bsd IPC channel +[UNIX83]. +This method is exceptionally flexible +in that the mailer need not reside +on the same machine. +It is normally used to connect to a sendmail process +on another machine. +.sh 2 "Operational Description" +.pp +When a sender wants to send a message, +it issues a request to +.i sendmail +using one of the three methods described above. +.i Sendmail +operates in two distinct phases. +In the first phase, +it collects and stores the message. +In the second phase, +message delivery occurs. +If there were errors during processing +during the second phase, +.i sendmail +creates and returns a new message describing the error +and/or returns an status code +telling what went wrong. +.sh 3 "Argument processing and address parsing" +.pp +If +.i sendmail +is called using one of the two subprocess techniques, +the arguments +are first scanned +and option specifications are processed. +Recipient addresses are then collected, +either from the command line +or from the SMTP +RCPT command, +and a list of recipients is created. +Aliases are expanded at this step, +including mailing lists. +As much validation as possible of the addresses +is done at this step: +syntax is checked, and local addresses are verified, +but detailed checking of host names and addresses +is deferred until delivery. +Forwarding is also performed +as the local addresses are verified. +.pp +.i Sendmail +appends each address +to the recipient list after parsing. +When a name is aliased or forwarded, +the old name is retained in the list, +and a flag is set that tells the delivery phase +to ignore this recipient. +This list is kept free from duplicates, +preventing alias loops +and duplicate messages deliverd to the same recipient, +as might occur if a person is in two groups. +.sh 3 "Message collection" +.pp +.i Sendmail +then collects the message. +The message should have a header at the beginning. +No formatting requirements are imposed on the message +except that they must be lines of text +(i.e., binary data is not allowed). +The header is parsed and stored in memory, +and the body of the message is saved +in a temporary file. +.pp +To simplify the program interface, +the message is collected even if no addresses were valid. +The message will be returned with an error. +.sh 3 "Message delivery" +.pp +For each unique mailer and host in the recipient list, +.i sendmail +calls the appropriate mailer. +Each mailer invocation sends to all users receiving the message on one host. +Mailers that only accept one recipient at a time +are handled properly. +.pp +The message is sent to the mailer +using one of the same three interfaces +used to submit a message to sendmail. +Each copy of the message is +prepended by a customized header. +The mailer status code is caught and checked, +and a suitable error message given as appropriate. +The exit code must conform to a system standard +or a generic message +(\c +.q "Service unavailable" ) +is given. +.sh 3 "Queueing for retransmission" +.pp +If the mailer returned an status that +indicated that it might be able to handle the mail later, +.i sendmail +will queue the mail and try again later. +.sh 3 "Return to sender" +.pp +If errors occur during processing, +.i sendmail +returns the message to the sender for retransmission. +The letter can be mailed back +or written in the file +.q dead.letter +in the sender's home directory\**. +.(f +\**Obviously, if the site giving the error is not the originating +site, the only reasonable option is to mail back to the sender. +Also, there are many more error disposition options, +but they only effect the error message \*- the +.q "return to sender" +function is always handled in one of these two ways. +.)f +.sh 2 "Message Header Editing" +.pp +Certain editing of the message header +occurs automatically. +Header lines can be inserted +under control of the configuration file. +Some lines can be merged; +for example, +a +.q From: +line and a +.q Full-name: +line can be merged under certain circumstances. +.sh 2 "Configuration File" +.pp +Almost all configuration information is read at runtime +from an ASCII file, +encoding +macro definitions +(defining the value of macros used internally), +header declarations +(telling sendmail the format of header lines that it will process specially, +i.e., lines that it will add or reformat), +mailer definitions +(giving information such as the location and characteristics +of each mailer), +and address rewriting rules +(a limited production system to rewrite addresses +which is used to parse and rewrite the addresses). +.pp +To improve performance when reading the configuration file, +a memory image can be provided. +This provides a +.q compiled +form of the configuration file. +.sh 1 "USAGE AND IMPLEMENTATION" +.sh 2 "Arguments" +.pp +Arguments may be flags and addresses. +Flags set various processing options. +Following flag arguments, +address arguments may be given, +unless we are running in SMTP mode. +Addresses follow the syntax in RFC822 +[Crocker82] +for ARPANET +address formats. +In brief, the format is: +.np +Anything in parentheses is thrown away +(as a comment). +.np +Anything in angle brackets (\c +.q "<\|>" ) +is preferred +over anything else. +This rule implements the ARPANET standard that addresses of the form +.(b +user name <machine-address> +.)b +will send to the electronic +.q machine-address +rather than the human +.q "user name." +.np +Double quotes +(\ "\ ) +quote phrases; +backslashes quote characters. +Backslashes are more powerful +in that they will cause otherwise equivalent phrases +to compare differently \*- for example, +.i user +and +.i +"user" +.r +are equivalent, +but +.i \euser +is different from either of them. +.pp +Parentheses, angle brackets, and double quotes +must be properly balanced and nested. +The rewriting rules control remaining parsing\**. +.(f +\**Disclaimer: Some special processing is done +after rewriting local names; see below. +.)f +.sh 2 "Mail to Files and Programs" +.pp +Files and programs are legitimate message recipients. +Files provide archival storage of messages, +useful for project administration and history. +Programs are useful as recipients in a variety of situations, +for example, +to maintain a public repository of systems messages +(such as the Berkeley +.i msgs +program, +or the MARS system +[Sattley78]). +.pp +Any address passing through the initial parsing algorithm +as a local address +(i.e, not appearing to be a valid address for another mailer) +is scanned for two special cases. +If prefixed by a vertical bar (\c +.q \^|\^ ) +the rest of the address is processed as a shell command. +If the user name begins with a slash mark (\c +.q /\^ ) +the name is used as a file name, +instead of a login name. +.pp +Files that have setuid or setgid bits set +but no execute bits set +have those bits honored if +.i sendmail +is running as root. +.sh 2 "Aliasing, Forwarding, Inclusion" +.pp +.i Sendmail +reroutes mail three ways. +Aliasing applies system wide. +Forwarding allows each user to reroute incoming mail +destined for that account. +Inclusion directs +.i sendmail +to read a file for a list of addresses, +and is normally used +in conjunction with aliasing. +.sh 3 "Aliasing" +.pp +Aliasing maps names to address lists using a system-wide file. +This file is indexed to speed access. +Only names that parse as local +are allowed as aliases; +this guarantees a unique key +(since there are no nicknames for the local host). +.sh 3 "Forwarding" +.pp +After aliasing, +recipients that are local and valid +are checked for the existence of a +.q .forward +file in their home directory. +If it exists, +the message is +.i not +sent to that user, +but rather to the list of users in that file. +Often +this list will contain only one address, +and the feature will be used for network mail forwarding. +.pp +Forwarding also permits a user to specify a private incoming mailer. +For example, +forwarding to: +.(b +"\^|\|/usr/local/newmail myname" +.)b +will use a different incoming mailer. +.sh 3 "Inclusion" +.pp +Inclusion is specified in RFC 733 [Crocker77a] syntax: +.(b +:Include: pathname +.)b +An address of this form reads the file specified by +.i pathname +and sends to all users listed in that file. +.pp +The intent is +.i not +to support direct use of this feature, +but rather to use this as a subset of aliasing. +For example, +an alias of the form: +.(b +project: :include:/usr/project/userlist +.)b +is a method of letting a project maintain a mailing list +without interaction with the system administration, +even if the alias file is protected. +.pp +It is not necessary to rebuild the index on the alias database +when a :include: list is changed. +.sh 2 "Message Collection" +.pp +Once all recipient addresses are parsed and verified, +the message is collected. +The message comes in two parts: +a message header and a message body, +separated by a blank line. +.pp +The header is formatted as a series of lines +of the form +.(b + field-name: field-value +.)b +Field-value can be split across lines by starting the following +lines with a space or a tab. +Some header fields have special internal meaning, +and have appropriate special processing. +Other headers are simply passed through. +Some header fields may be added automatically, +such as time stamps. +.pp +The body is a series of text lines. +It is completely uninterpreted and untouched, +except that lines beginning with a dot +have the dot doubled +when transmitted over an SMTP channel. +This extra dot is stripped by the receiver. +.sh 2 "Message Delivery" +.pp +The send queue is ordered by receiving host +before transmission +to implement message batching. +Each address is marked as it is sent +so rescanning the list is safe. +An argument list is built as the scan proceeds. +Mail to files is detected during the scan of the send list. +The interface to the mailer +is performed using one of the techniques +described in section 2.2. +.pp +After a connection is established, +.i sendmail +makes the per-mailer changes to the header +and sends the result to the mailer. +If any mail is rejected by the mailer, +a flag is set to invoke the return-to-sender function +after all delivery completes. +.sh 2 "Queued Messages" +.pp +If the mailer returns a +.q "temporary failure" +exit status, +the message is queued. +A control file is used to describe the recipients to be sent to +and various other parameters. +This control file is formatted as a series of lines, +each describing a sender, +a recipient, +the time of submission, +or some other salient parameter of the message. +The header of the message is stored +in the control file, +so that the associated data file in the queue +is just the temporary file that was originally collected. +.sh 2 "Configuration" +.pp +Configuration is controlled primarily by a configuration file +read at startup. +.i Sendmail +should not need to be recomplied except +.np +To change operating systems +(V6, V7/32V, 4BSD). +.np +To remove or insert the DBM +(UNIX database) +library. +.np +To change ARPANET reply codes. +.np +To add headers fields requiring special processing. +.lp +Adding mailers or changing parsing +(i.e., rewriting) +or routing information +does not require recompilation. +.pp +If the mail is being sent by a local user, +and the file +.q .mailcf +exists in the sender's home directory, +that file is read as a configuration file +after the system configuration file. +The primary use of this feature is to add header lines. +.pp +The configuration file encodes macro definitions, +header definitions, +mailer definitions, +rewriting rules, +and options. +.sh 3 Macros +.pp +Macros can be used in three ways. +Certain macros transmit +unstructured textual information +into the mail system, +such as the name +.i sendmail +will use to identify itself in error messages. +Other macros transmit information from +.i sendmail +to the configuration file +for use in creating other fields +(such as argument vectors to mailers); +e.g., the name of the sender, +and the host and user +of the recipient. +Other macros are unused internally, +and can be used as shorthand in the configuration file. +.sh 3 "Header declarations" +.pp +Header declarations inform +.i sendmail +of the format of known header lines. +Knowledge of a few header lines +is built into +.i sendmail , +such as the +.q From: +and +.q Date: +lines. +.pp +Most configured headers +will be automatically inserted +in the outgoing message +if they don't exist in the incoming message. +Certain headers are suppressed by some mailers. +.sh 3 "Mailer declarations" +.pp +Mailer declarations tell +.i sendmail +of the various mailers available to it. +The definition specifies the internal name of the mailer, +the pathname of the program to call, +some flags associated with the mailer, +and an argument vector to be used on the call; +this vector is macro-expanded before use. +.sh 3 "Address rewriting rules" +.pp +The heart of address parsing in +.i sendmail +is a set of rewriting rules. +These are an ordered list of pattern-replacement rules, +(somewhat like a production system, +except that order is critical), +which are applied to each address. +The address is rewritten textually until it is either rewritten +into a special canonical form +(i.e., +a (mailer, host, user) +3-tuple, +such as {arpanet, usc-isif, postel} +representing the address +.q "postel@usc-isif" ), +or it falls off the end. +When a pattern matches, +the rule is reapplied until it fails. +.pp +The configuration file also supports the editing of addresses +into different formats. +For example, +an address of the form: +.(b +ucsfcgl!tef +.)b +might be mapped into: +.(b +tef@ucsfcgl.UUCP +.)b +to conform to the domain syntax. +Translations can also be done in the other direction. +.sh 3 "Option setting" +.pp +There are several options that can be set +from the configuration file. +These include the pathnames of various support files, +timeouts, +default modes, +etc. +.sh 1 "COMPARISON WITH OTHER MAILERS" +.sh 2 "Delivermail" +.pp +.i Sendmail +is an outgrowth of +.i delivermail . +The primary differences are: +.np +Configuration information is not compiled in. +This change simplifies many of the problems +of moving to other machines. +It also allows easy debugging of new mailers. +.np +Address parsing is more flexible. +For example, +.i delivermail +only supported one gateway to any network, +whereas +.i sendmail +can be sensitive to host names +and reroute to different gateways. +.np +Forwarding and +:include: +features eliminate the requirement that the system alias file +be writable by any user +(or that an update program be written, +or that the system administration make all changes). +.np +.i Sendmail +supports message batching across networks +when a message is being sent to multiple recipients. +.np +A mail queue is provided in +.i sendmail. +Mail that cannot be delivered immediately +but can potentially be delivered later +is stored in this queue for a later retry. +The queue also provides a buffer against system crashes; +after the message has been collected +it may be reliably redelivered +even if the system crashes during the initial delivery. +.np +.i Sendmail +uses the networking support provided by 4.2BSD +to provide a direct interface networks such as the ARPANET +and/or Ethernet +using SMTP (the Simple Mail Transfer Protocol) +over a TCP/IP connection. +.sh 2 "MMDF" +.pp +MMDF +[Crocker79] +spans a wider problem set than +.i sendmail . +For example, +the domain of +MMDF includes a +.q "phone network" +mailer, whereas +.i sendmail +calls on preexisting mailers in most cases. +.pp +MMDF and +.i sendmail +both support aliasing, +customized mailers, +message batching, +automatic forwarding to gateways, +queueing, +and retransmission. +MMDF supports two-stage timeout, +which +.i sendmail +does not support. +.pp +The configuration for MMDF +is compiled into the code\**. +.(f +\**Dynamic configuration tables are currently being considered +for MMDF; +allowing the installer to select either compiled +or dynamic tables. +.)f +.pp +Since MMDF does not consider backwards compatibility +as a design goal, +the address parsing is simpler but much less flexible. +.pp +It is somewhat harder to integrate a new channel\** +.(f +\**The MMDF equivalent of a +.i sendmail +.q mailer. +.)f +into MMDF. +In particular, +MMDF must know the location and format +of host tables for all channels, +and the channel must speak a special protocol. +This allows MMDF to do additional verification +(such as verifying host names) +at submission time. +.pp +MMDF strictly separates the submission and delivery phases. +Although +.i sendmail +has the concept of each of these stages, +they are integrated into one program, +whereas in MMDF they are split into two programs. +.sh 2 "Message Processing Module" +.pp +The Message Processing Module (MPM) +discussed by Postel [Postel79b] +matches +.i sendmail +closely in terms of its basic architecture. +However, +like MMDF, +the MPM includes the network interface software +as part of its domain. +.pp +MPM also postulates a duplex channel to the receiver, +as does MMDF, +thus allowing simpler handling of errors +by the mailer +than is possible in +.i sendmail . +When a message queued by +.i sendmail +is sent, +any errors must be returned to the sender +by the mailer itself. +Both MPM and MMDF mailers +can return an immediate error response, +and a single error processor can create an appropriate response. +.pp +MPM prefers passing the message as a structured object, +with type-length-value tuples\**. +.(f +\**This is similar to the NBS standard. +.)f +Such a convention requires a much higher degree of cooperation +between mailers than is required by +.i sendmail . +MPM also assumes a universally agreed upon internet name space +(with each address in the form of a net-host-user tuple), +which +.i sendmail +does not. +.sh 1 "EVALUATIONS AND FUTURE PLANS" +.pp +.i Sendmail +is designed to work in a nonhomogeneous environment. +Every attempt is made to avoid imposing unnecessary constraints +on the underlying mailers. +This goal has driven much of the design. +One of the major problems +has been the lack of a uniform address space, +as postulated in [Postel79a] +and [Postel79b]. +.pp +A nonuniform address space implies that a path will be specified +in all addresses, +either explicitly (as part of the address) +or implicitly +(as with implied forwarding to gateways). +This restriction has the unpleasant effect of making replying to messages +exceedingly difficult, +since there is no one +.q address +for any person, +but only a way to get there from wherever you are. +.pp +Interfacing to mail programs +that were not initially intended to be applied +in an internet environment +has been amazingly successful, +and has reduced the job to a manageable task. +.pp +.i Sendmail +has knowledge of a few difficult environments +built in. +It generates ARPANET FTP/SMTP compatible error messages +(prepended with three-digit numbers +[Neigus73, Postel74, Postel82]) +as necessary, +optionally generates UNIX-style +.q From +lines on the front of messages for some mailers, +and knows how to parse the same lines on input. +Also, +error handling has an option customized for BerkNet. +.pp +The decision to avoid doing any type of delivery where possible +(even, or perhaps especially, local delivery) +has turned out to be a good idea. +Even with local delivery, +there are issues of the location of the mailbox, +the format of the mailbox, +the locking protocol used, +etc., +that are best decided by other programs. +One surprisingly major annoyance in many internet mailers +is that the location and format of local mail is built in. +The feeling seems to be that local mail is so common +that it should be efficient. +This feeling is not born out by +our experience; +on the contrary, +the location and format of mailboxes seems to vary widely +from system to system. +.pp +The ability to automatically generate a response to incoming mail +(by forwarding mail to a program) +seems useful +(\c +.q "I am on vacation until late August...." ) +but can create problems +such as forwarding loops +(two people on vacation whose programs send notes back and forth, +for instance) +if these programs are not well written. +A program could be written to do standard tasks correctly, +but this would solve the general case. +.pp +It might be desirable to implement some form of load limiting. +I am unaware of any mail system that addresses this problem, +nor am I aware of any reasonable solution at this time. +.pp +The configuration file is currently practically inscrutable; +considerable convenience could be realized +with a higher-level format. +.pp +It seems clear that common protocols will be changing soon +to accommodate changing requirements and environments. +These changes will include modifications to the message header +(e.g., [NBS80]) +or to the body of the message itself +(such as for multimedia messages +[Postel80]). +Experience indicates that +these changes should be relatively trivial to integrate +into the existing system. +.pp +In tightly coupled environments, +it would be nice to have a name server +such as Grapvine +[Birrell82] +integrated into the mail system. +This would allow a site such as +.q Berkeley +to appear as a single host, +rather than as a collection of hosts, +and would allow people to move transparently among machines +without having to change their addresses. +Such a facility +would require an automatically updated database +and some method of resolving conflicts. +Ideally this would be effective even without +all hosts being under +a single management. +However, +it is not clear whether this feature +should be integrated into the +aliasing facility +or should be considered a +.q "value added" +feature outside +.i sendmail +itself. +.pp +As a more interesting case, +the CSNET name server +[Solomon81] +provides an facility that goes beyond a single +tightly-coupled environment. +Such a facility would normally exist outside of +.i sendmail +however. +.sh 0 "ACKNOWLEDGEMENTS" +.pp +Thanks are due to Kurt Shoens for his continual cheerful +assistance and good advice, +Bill Joy for pointing me in the correct direction +(over and over), +and Mark Horton for more advice, +prodding, +and many of the good ideas. +Kurt and Eric Schmidt are to be credited +for using +.i delivermail +as a server for their programs +(\c +.i Mail +and BerkNet respectively) +before any sane person should have, +and making the necessary modifications +promptly and happily. +Eric gave me considerable advice about the perils +of network software which saved me an unknown +amount of work and grief. +Mark did the original implementation of the DBM version +of aliasing, installed the VFORK code, +wrote the current version of +.i rmail , +and was the person who really convinced me +to put the work into +.i delivermail +to turn it into +.i sendmail . +Kurt deserves accolades for using +.i sendmail +when I was myself afraid to take the risk; +how a person can continue to be so enthusiastic +in the face of so much bitter reality is beyond me. +.pp +Kurt, +Mark, +Kirk McKusick, +Marvin Solomon, +and many others have reviewed this paper, +giving considerable useful advice. +.pp +Special thanks are reserved for Mike Stonebraker at Berkeley +and Bob Epstein at Britton-Lee, +who both knowingly allowed me to put so much work into this +project +when there were so many other things I really should +have been working on. +.+c +.ce +REFERENCES +.nr ii 1.5i +.ip [Birrell82] +Birrell, A. D., +Levin, R., +Needham, R. M., +and +Schroeder, M. D., +.q "Grapevine: An Exercise in Distributed Computing." +In +.ul +Comm. A.C.M. 25, +4, +April 82. +.ip [Borden79] +Borden, S., +Gaines, R. S., +and +Shapiro, N. Z., +.ul +The MH Message Handling System: Users' Manual. +R-2367-PAF. +Rand Corporation. +October 1979. +.ip [Crocker77a] +Crocker, D. H., +Vittal, J. J., +Pogran, K. T., +and +Henderson, D. A. Jr., +.ul +Standard for the Format of ARPA Network Text Messages. +RFC 733, +NIC 41952. +In [Feinler78]. +November 1977. +.ip [Crocker77b] +Crocker, D. H., +.ul +Framework and Functions of the MS Personal Message System. +R-2134-ARPA, +Rand Corporation, +Santa Monica, California. +1977. +.ip [Crocker79] +Crocker, D. H., +Szurkowski, E. S., +and +Farber, D. J., +.ul +An Internetwork Memo Distribution Facility \*- MMDF. +6th Data Communication Symposium, +Asilomar. +November 1979. +.ip [Crocker82] +Crocker, D. H., +.ul +Standard for the Format of Arpa Internet Text Messages. +RFC 822. +Network Information Center, +SRI International, +Menlo Park, California. +August 1982. +.ip [Metcalfe76] +Metcalfe, R., +and +Boggs, D., +.q "Ethernet: Distributed Packet Switching for Local Computer Networks" , +.ul +Communications of the ACM 19, +7. +July 1976. +.ip [Feinler78] +Feinler, E., +and +Postel, J. +(eds.), +.ul +ARPANET Protocol Handbook. +NIC 7104, +Network Information Center, +SRI International, +Menlo Park, California. +1978. +.ip [NBS80] +National Bureau of Standards, +.ul +Specification of a Draft Message Format Standard. +Report No. ICST/CBOS 80-2. +October 1980. +.ip [Neigus73] +Neigus, N., +.ul +File Transfer Protocol for the ARPA Network. +RFC 542, NIC 17759. +In [Feinler78]. +August, 1973. +.ip [Nowitz78a] +Nowitz, D. A., +and +Lesk, M. E., +.ul +A Dial-Up Network of UNIX Systems. +Bell Laboratories. +In +UNIX Programmer's Manual, Seventh Edition, +Volume 2. +August, 1978. +.ip [Nowitz78b] +Nowitz, D. A., +.ul +Uucp Implementation Description. +Bell Laboratories. +In +UNIX Programmer's Manual, Seventh Edition, +Volume 2. +October, 1978. +.ip [Postel74] +Postel, J., +and +Neigus, N., +Revised FTP Reply Codes. +RFC 640, NIC 30843. +In [Feinler78]. +June, 1974. +.ip [Postel77] +Postel, J., +.ul +Mail Protocol. +NIC 29588. +In [Feinler78]. +November 1977. +.ip [Postel79a] +Postel, J., +.ul +Internet Message Protocol. +RFC 753, +IEN 85. +Network Information Center, +SRI International, +Menlo Park, California. +March 1979. +.ip [Postel79b] +Postel, J. B., +.ul +An Internetwork Message Structure. +In +.ul +Proceedings of the Sixth Data Communications Symposium, +IEEE. +New York. +November 1979. +.ip [Postel80] +Postel, J. B., +.ul +A Structured Format for Transmission of Multi-Media Documents. +RFC 767. +Network Information Center, +SRI International, +Menlo Park, California. +August 1980. +.ip [Postel82] +Postel, J. B., +.ul +Simple Mail Transfer Protocol. +RFC821 +(obsoleting RFC788). +Network Information Center, +SRI International, +Menlo Park, California. +August 1982. +.ip [Schmidt79] +Schmidt, E., +.ul +An Introduction to the Berkeley Network. +University of California, Berkeley California. +1979. +.ip [Shoens79] +Shoens, K., +.ul +Mail Reference Manual. +University of California, Berkeley. +In UNIX Programmer's Manual, +Seventh Edition, +Volume 2C. +December 1979. +.ip [Sluizer81] +Sluizer, S., +and +Postel, J. B., +.ul +Mail Transfer Protocol. +RFC 780. +Network Information Center, +SRI International, +Menlo Park, California. +May 1981. +.ip [Solomon81] +Solomon, M., Landweber, L., and Neuhengen, D., +.q "The Design of the CSNET Name Server." +CS-DN-2, +University of Wisconsin, Madison. +November 1981. +.ip [Su82] +Su, Zaw-Sing, +and +Postel, Jon, +.ul +The Domain Naming Convention for Internet User Applications. +RFC819. +Network Information Center, +SRI International, +Menlo Park, California. +August 1982. +.ip [UNIX83] +.ul +The UNIX Programmer's Manual, Seventh Edition, +Virtual VAX-11 Version, +Volume 1. +Bell Laboratories, +modified by the University of California, +Berkeley, California. +March, 1983. diff --git a/usr.sbin/sendmail/doc/intro/intro.ps b/usr.sbin/sendmail/doc/intro/intro.ps new file mode 100644 index 0000000000..8514f59883 --- /dev/null +++ b/usr.sbin/sendmail/doc/intro/intro.ps @@ -0,0 +1,1295 @@ +%!PS-Adobe-3.0 +%%Creator: groff version 1.08 +%%DocumentNeededResources: font Times-Roman +%%+ font Times-Italic +%%+ font Times-Bold +%%DocumentSuppliedResources: procset grops 1.08 0 +%%Pages: 13 +%%PageOrder: Ascend +%%Orientation: Portrait +%%EndComments +%%BeginProlog +%%BeginResource: procset grops 1.08 0 +/setpacking where{ +pop +currentpacking +true setpacking +}if +/grops 120 dict dup begin +/SC 32 def +/A/show load def +/B{0 SC 3 -1 roll widthshow}bind def +/C{0 exch ashow}bind def +/D{0 exch 0 SC 5 2 roll awidthshow}bind def +/E{0 rmoveto show}bind def +/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def +/G{0 rmoveto 0 exch ashow}bind def +/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/I{0 exch rmoveto show}bind def +/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def +/K{0 exch rmoveto 0 exch ashow}bind def +/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/M{rmoveto show}bind def +/N{rmoveto 0 SC 3 -1 roll widthshow}bind def +/O{rmoveto 0 exch ashow}bind def +/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/Q{moveto show}bind def +/R{moveto 0 SC 3 -1 roll widthshow}bind def +/S{moveto 0 exch ashow}bind def +/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/SF{ +findfont exch +[exch dup 0 exch 0 exch neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/MF{ +findfont +[5 2 roll +0 3 1 roll +neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/level0 0 def +/RES 0 def +/PL 0 def +/LS 0 def +/PLG{ +gsave newpath clippath pathbbox grestore +exch pop add exch pop +}bind def +/BP{ +/level0 save def +1 setlinecap +1 setlinejoin +72 RES div dup scale +LS{ +90 rotate +}{ +0 PL translate +}ifelse +1 -1 scale +}bind def +/EP{ +level0 restore +showpage +}bind def +/DA{ +newpath arcn stroke +}bind def +/SN{ +transform +.25 sub exch .25 sub exch +round .25 add exch round .25 add exch +itransform +}bind def +/DL{ +SN +moveto +SN +lineto stroke +}bind def +/DC{ +newpath 0 360 arc closepath +}bind def +/TM matrix def +/DE{ +TM currentmatrix pop +translate scale newpath 0 0 .5 0 360 arc closepath +TM setmatrix +}bind def +/RC/rcurveto load def +/RL/rlineto load def +/ST/stroke load def +/MT/moveto load def +/CL/closepath load def +/FL{ +currentgray exch setgray fill setgray +}bind def +/BL/fill load def +/LW/setlinewidth load def +/RE{ +findfont +dup maxlength 1 index/FontName known not{1 add}if dict begin +{ +1 index/FID ne{def}{pop pop}ifelse +}forall +/Encoding exch def +dup/FontName exch def +currentdict end definefont pop +}bind def +/DEFS 0 def +/EBEGIN{ +moveto +DEFS begin +}bind def +/EEND/end load def +/CNT 0 def +/level1 0 def +/PBEGIN{ +/level1 save def +translate +div 3 1 roll div exch scale +neg exch neg exch translate +0 setgray +0 setlinecap +1 setlinewidth +0 setlinejoin +10 setmiterlimit +[]0 setdash +/setstrokeadjust where{ +pop +false setstrokeadjust +}if +/setoverprint where{ +pop +false setoverprint +}if +newpath +/CNT countdictstack def +userdict begin +/showpage{}def +}bind def +/PEND{ +clear +countdictstack CNT sub{end}repeat +level1 restore +}bind def +end def +/setpacking where{ +pop +setpacking +}if +%%EndResource +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Italic +%%IncludeResource: font Times-Bold +grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL +792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron +/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space +/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft +/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four +/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/A/B/C +/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash +/bracketright/circumflex/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q +/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/tilde/.notdef/quotesinglbase +/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger +/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut +/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash +/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar +/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus +/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu +/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright +/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde +/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute +/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis +/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls +/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute +/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve +/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex +/udieresis/yacute/thorn/ydieresis]def/Times-Bold@0 ENC0/Times-Bold RE +/Times-Italic@0 ENC0/Times-Italic RE/Times-Roman@0 ENC0/Times-Roman RE +%%EndProlog +%%Page: 1 1 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 14/Times-Roman@0 SF(SENDMAIL \212 An Internetw)159.172 141 Q +(ork Mail Router)-.14 E/F1 10/Times-Roman@0 SF(Eric Allman\207)260.92 165 Q/F2 +10/Times-Italic@0 SF(Univer)220.2 183 Q(sity of California, Berk)-.1 E(ele)-.1 +E(y)-.3 E(Mammoth Pr)251.98 195 Q(oject)-.45 E F1(ABSTRA)262.085 227.4 Q(CT)-.4 +E 1.41(Routing mail through a heterogenous internet presents man)112 243.6 R +3.91(yn)-.15 G 1.91 -.25(ew p)372.55 243.6 T 3.91(roblems. Among).25 F .297 +(the w)112 255.6 R .297(orst of these is that of address mapping.)-.1 F +(Historically)5.297 E 2.797(,t)-.65 G .298(his has been handled on an)355.03 +255.6 R F2(ad hoc)112 267.6 Q F1 2.5(basis. Ho)2.5 F(we)-.25 E -.15(ve)-.25 G +.8 -.4(r, t).15 H(his approach has become unmanageable as internets gro).4 E +-.65(w.)-.25 G .15(Sendmail acts a uni\214ed "post of)112 283.8 R .15 +(\214ce" to which all mail can be submitted.)-.25 F .15(Address inter)5.15 F(-) +-.2 E .426(pretation is controlled by a production system, which can parse bot\ +h domain-based ad-)112 295.8 R .423(dressing and old-style)112 307.8 R F2 .423 +(ad hoc)2.923 F F1 2.923(addresses. The)2.923 F .422(production system is po) +2.922 F .422(werful enough to)-.25 F(re)112 319.8 Q 1.357(write addresses in t\ +he message header to conform to the standards of a number of)-.25 F 1.15 +(common tar)112 331.8 R 1.15(get netw)-.18 F 1.15 +(orks, including old \(NCP/RFC733\) Arpanet, ne)-.1 F 3.65(w\()-.25 G +(TCP/RFC822\))405.65 331.8 Q 1.119(Arpanet, UUCP)112 343.8 R 3.619(,a)-1.11 G +1.119(nd Phonenet.)186.448 343.8 R 1.119(Sendmail also implements an SMTP serv) +6.119 F(er)-.15 E 3.619(,m)-.4 G(essage)437.9 343.8 Q(queueing, and aliasing.) +112 355.8 Q F2(Sendmail)97 400.2 Q F1 .501(implements a general internetw)3 F +.501(ork mail routing f)-.1 F(acility)-.1 E 3.001(,f)-.65 G .501 +(eaturing aliasing and forw)369.847 400.2 R(arding,)-.1 E +(automatic routing to netw)72 412.2 Q(ork g)-.1 E(ate)-.05 E -.1(wa)-.25 G +(ys, and \215e).1 E(xible con\214guration.)-.15 E .624(In a simple netw)97 +428.4 R .624(ork, each node has an address, and resources can be identi\214ed \ +with a host-resource)-.1 F .374(pair; in particular)72 440.4 R 2.874(,t)-.4 G +.374(he mail system can refer to users using a host-username pair)149.932 440.4 +R 5.374(.H)-.55 G .375(ost names and numbers)409.276 440.4 R(ha)72 452.4 Q .3 +-.15(ve t)-.2 H 2.5(ob).15 G 2.5(ea)108.31 452.4 S +(dministered by a central authority)119.69 452.4 Q 2.5(,b)-.65 G +(ut usernames can be assigned locally to each host.)263.82 452.4 Q .649 +(In an internet, multiple netw)97 468.6 R .649(orks with dif)-.1 F .649 +(ferent characterstics and managements must communicate.)-.25 F .389 +(In particular)72 480.6 R 2.889(,t)-.4 G .389 +(he syntax and semantics of resource identi\214cation change.)129.308 480.6 R +.39(Certain special cases can be han-)5.389 F 1.033(dled tri)72 492.6 R 1.033 +(vially by)-.25 F F2 1.033(ad hoc)3.533 F F1 1.032(techniques, such as pro) +3.533 F 1.032(viding netw)-.15 F 1.032 +(ork names that appear local to hosts on other)-.1 F(netw)72 504.6 Q 1.454 +(orks, as with the Ethernet at Xerox P)-.1 F 3.955(ARC. Ho)-.92 F(we)-.25 E +-.15(ve)-.25 G 4.755 -.4(r, t).15 H 1.455(he general case is e).4 F 1.455 +(xtremely comple)-.15 F 3.955(x. F)-.15 F(or)-.15 E -.15(ex)72 516.6 S .192 +(ample, some netw).15 F .192(orks require point-to-point routing, which simpli\ +\214es the database update problem since)-.1 F .618(only adjacent hosts must b\ +e entered into the system tables, while others use end-to-end addressing.)72 +528.6 R(Some)5.618 E(netw)72 540.6 Q .123(orks use a left-associati)-.1 F .423 +-.15(ve s)-.25 H .123(yntax and others use a right-associati).15 F .423 -.15 +(ve s)-.25 H .123(yntax, causing ambiguity in mix).15 F(ed)-.15 E(addresses.)72 +552.6 Q .678(Internet standards seek to eliminate these problems.)97 568.8 R +(Initially)5.678 E 3.178(,t)-.65 G .679(hese proposed e)353.134 568.8 R .679 +(xpanding the address)-.15 F .65(pairs to address triples, consisting of {netw) +72 580.8 R .649(ork, host, resource} triples.)-.1 F(Netw)5.649 E .649 +(ork numbers must be uni)-.1 F -.15(ve)-.25 G -.2(r-).15 G 1.452 +(sally agreed upon, and hosts can be assigned locally on each netw)72 592.8 R +3.952(ork. The)-.1 F(user)3.952 E(-le)-.2 E -.15(ve)-.25 G 3.952(lp).15 G 1.452 +(resentation w)440.718 592.8 R(as)-.1 E 2.352(quickly e)72 604.8 R 2.352(xpand\ +ed to address domains, comprised of a local resource identi\214cation and a hi\ +erarchical)-.15 F .256(domain speci\214cation with a common static root.)72 +616.8 R .257(The domain technique separates the issue of ph)5.257 F .257 +(ysical v)-.05 F(er)-.15 E(-)-.2 E .807(sus logical addressing.)72 628.8 R -.15 +(Fo)5.807 G 3.307(re).15 G .807 +(xample, an address of the form \231eric@a.cc.berk)191.028 628.8 R(ele)-.1 E +-.65(y.)-.15 G .807(arpa\232 describes only the).65 F(logical or)72 640.8 Q +-.05(ga)-.18 G(nization of the address space.).05 E F2(Sendmail)97 657 Q F1 +.493(is intended to help bridge the g)2.992 F .493(ap between the totally)-.05 +F F2 .493(ad hoc)2.993 F F1 -.1(wo)2.993 G .493(rld of netw).1 F .493 +(orks that kno)-.1 F(w)-.25 E .855 +(nothing of each other and the clean, tightly-coupled w)72 669 R .854 +(orld of unique netw)-.1 F .854(ork numbers.)-.1 F .854(It can accept old)5.854 +F .32 LW 76 678.6 72 678.6 DL 80 678.6 76 678.6 DL 84 678.6 80 678.6 DL 88 +678.6 84 678.6 DL 92 678.6 88 678.6 DL 96 678.6 92 678.6 DL 100 678.6 96 678.6 +DL 104 678.6 100 678.6 DL 108 678.6 104 678.6 DL 112 678.6 108 678.6 DL 116 +678.6 112 678.6 DL 120 678.6 116 678.6 DL 124 678.6 120 678.6 DL 128 678.6 124 +678.6 DL 132 678.6 128 678.6 DL 136 678.6 132 678.6 DL 140 678.6 136 678.6 DL +144 678.6 140 678.6 DL 148 678.6 144 678.6 DL 152 678.6 148 678.6 DL 156 678.6 +152 678.6 DL 160 678.6 156 678.6 DL 164 678.6 160 678.6 DL 168 678.6 164 678.6 +DL 172 678.6 168 678.6 DL 176 678.6 172 678.6 DL 180 678.6 176 678.6 DL 184 +678.6 180 678.6 DL 188 678.6 184 678.6 DL 192 678.6 188 678.6 DL 196 678.6 192 +678.6 DL 200 678.6 196 678.6 DL 204 678.6 200 678.6 DL 208 678.6 204 678.6 DL +212 678.6 208 678.6 DL 216 678.6 212 678.6 DL/F3 8/Times-Roman@0 SF .557 +(\207A considerable part of this w)93.6 690.6 R .557(ork w)-.08 F .557 +(as done while under the emplo)-.08 F 2.557(yo)-.08 G 2.556(ft)323.116 690.6 S +.556(he INGRES Project at the Uni)330.56 690.6 R -.12(ve)-.2 G .556 +(rsity of California at).12 F(Berk)72 700.2 Q(ele)-.08 E 2(ya)-.12 G +(nd at Britton Lee.)106.232 700.2 Q/F4 10/Times-Bold@0 SF +(SENDMAIL \212 An Inter)72 756 Q(netw)-.15 E(ork Mail Router)-.1 E(SMM:9-1) +462.9 756 Q EP +%%Page: 2 2 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 192.28(SMM:9-2 SENDMAIL)72 60 R 2.5<8a41>2.5 G 2.5(nI) +383.99 60 S(nter)395.94 60 Q(netw)-.15 E(ork Mail Router)-.1 E/F1 10 +/Times-Roman@0 SF .632(arbitrary address syntax)72 96 R .633(es, resolving amb\ +iguities using heuristics speci\214ed by the system administrator)-.15 F 3.133 +(,a)-.4 G(s)500.11 96 Q .348(well as domain-based addressing.)72 108 R .347 +(It helps guide the con)5.347 F -.15(ve)-.4 G .347 +(rsion of message formats between disparate net-).15 F -.1(wo)72 120 S 3.394 +(rks. In).1 F(short,)3.394 E/F2 10/Times-Italic@0 SF(sendmail)3.394 E F1 .894 +(is designed to assist a graceful transition to consistent internetw)3.394 F +.895(ork addressing)-.1 F(schemes.)72 132 Q .153 +(Section 1 discusses the design goals for)97 160.2 R F2(sendmail)2.653 E F1 +5.153(.S)C .152(ection 2 gi)308.214 160.2 R -.15(ve)-.25 G 2.652(sa).15 G 2.652 +(no)370.76 160.2 S -.15(ve)383.262 160.2 S(rvie).15 E 2.652(wo)-.25 G 2.652(ft) +422.724 160.2 S .152(he basic functions)431.486 160.2 R .644(of the system.)72 +172.2 R .644(In section 3, details of usage are discussed.)5.644 F .644 +(Section 4 compares)5.644 F F2(sendmail)3.144 E F1 .645(to other internet)3.144 +F(mail routers, and an e)72 184.2 Q -.25(va)-.25 G(luation of).25 E F2 +(sendmail)2.5 E F1(is gi)2.5 E -.15(ve)-.25 G 2.5(ni).15 G 2.5(ns)283.3 184.2 S +(ection 5, including future plans.)294.69 184.2 Q F0 2.5(1. DESIGN)72 208.2 R +(GO)2.5 E(ALS)-.4 E F1(Design goals for)112 224.4 Q F2(sendmail)2.5 E F1 +(include:)2.5 E 12.5(\(1\) Compatibility)92 240.6 R 1.363(with the e)3.864 F +1.363(xisting mail programs, including Bell v)-.15 F 1.363 +(ersion 6 mail, Bell v)-.15 F 1.363(ersion 7)-.15 F 1.202(mail [UNIX83], Berk) +118.66 252.6 R(ele)-.1 E(y)-.15 E F2(Mail)3.702 E F1 1.202 +([Shoens79], BerkNet mail [Schmidt79], and hopefully UUCP)3.702 F(mail [No) +118.66 264.6 Q(witz78a, No)-.25 E 2.5(witz78b]. ARP)-.25 F(ANET mail [Crock) +-.92 E(er77a, Postel77] w)-.1 E(as also required.)-.1 E 12.5(\(2\) Reliability) +92 280.8 R 4.003(,i)-.65 G 4.003(nt)169.523 280.8 S 1.502 +(he sense of guaranteeing that e)181.306 280.8 R -.15(ve)-.25 G 1.502 +(ry message is correctly deli).15 F -.15(ve)-.25 G 1.502(red or at least).15 F +.368 +(brought to the attention of a human for correct disposal; no message should e) +118.66 292.8 R -.15(ve)-.25 G 2.868(rb).15 G 2.868(ec)452.252 292.8 S +(ompletely)464 292.8 Q 2.541(lost. This)118.66 304.8 R .041(goal w)2.541 F .041 +(as considered essential because of the emphasis on mail in our en)-.1 F 2.54 +(vironment. It)-.4 F 1.754 +(has turned out to be one of the hardest goals to satisfy)118.66 316.8 R 4.255 +(,e)-.65 G 1.755(specially in the f)363.75 316.8 R 1.755(ace of the man)-.1 F +(y)-.15 E .978(anomalous message formats produced by v)118.66 328.8 R .977 +(arious ARP)-.25 F .977(ANET sites.)-.92 F -.15(Fo)5.977 G 3.477(re).15 G .977 +(xample, certain sites)420.116 328.8 R .069 +(generate improperly formated addresses, occasionally causing error)118.66 +340.8 R .069(-message loops.)-.2 F .069(Some hosts)5.069 F .063(use blanks in \ +names, causing problems with UNIX mail programs that assume that an address is) +118.66 352.8 R .111(one w)118.66 364.8 R 2.611(ord. The)-.1 F .111 +(semantics of some \214elds are interpreted slightly dif)2.611 F .112 +(ferently by dif)-.25 F .112(ferent sites.)-.25 F(In)5.112 E(summary)118.66 +376.8 Q 3.023(,t)-.65 G .523(he obscure features of the ARP)163.533 376.8 R +.523(ANET mail protocol really)-.92 F F2(ar)3.023 E(e)-.37 E F1 .522 +(used and are dif)3.023 F(\214cult)-.25 E(to support, b)118.66 388.8 Q +(ut must be supported.)-.2 E 12.5(\(3\) Existing)92 405 R(softw)2.938 E .438 +(are to do actual deli)-.1 F -.15(ve)-.25 G .439(ry should be used whene).15 F +-.15(ve)-.25 G 2.939(rp).15 G 2.939(ossible. This)387.654 405 R .439(goal deri) +2.939 F -.15(ve)-.25 G 2.939(sa).15 G(s)500.11 405 Q +(much from political and practical considerations as technical.)118.66 417 Q +12.5(\(4\) Easy)92 433.2 R -.15(ex)2.899 G .399(pansion to f).15 F .399 +(airly comple)-.1 F 2.898(xe)-.15 G -.4(nv)261.064 433.2 S .398 +(ironments, including multiple connections to a single net-).4 F -.1(wo)118.66 +445.2 S .115 +(rk type \(such as with multiple UUCP or Ether nets [Metcalfe76]\).).1 F .115 +(This goal requires consid-)5.115 F .587(eration of the contents of an address\ + as well as its syntax in order to determine which g)118.66 457.2 R(ate)-.05 E +-.1(wa)-.25 G(y).1 E 1.018(to use.)118.66 469.2 R -.15(Fo)6.018 G 3.518(re).15 +G 1.018(xample, the ARP)173.354 469.2 R 1.019 +(ANET is bringing up the TCP protocol to replace the old NCP)-.92 F 4.791 +(protocol. No)118.66 481.2 R 2.291(host at Berk)4.791 F(ele)-.1 E 4.791(yr)-.15 +G 2.291(uns both TCP and NCP)256.235 481.2 R 4.791(,s)-1.11 G 4.79(oi)369.37 +481.2 S 4.79(ti)381.94 481.2 S 4.79(sn)392.29 481.2 S 2.29 +(ecessary to look at the)405.97 481.2 R(ARP)118.66 493.2 Q .016 +(ANET host name to determine whether to route mail to an NCP g)-.92 F(ate)-.05 +E -.1(wa)-.25 G 2.517(yo).1 G 2.517(raT)435.569 493.2 S .017(CP g)454.483 493.2 +R(ate)-.05 E -.1(wa)-.25 G -.65(y.).1 G 12.5(\(5\) Con\214guration)92 509.4 R +.145(should not be compiled into the code.)2.645 F 2.645(As)5.145 G .145 +(ingle compiled program should be able)346.905 509.4 R .91(to run as is at an) +118.66 521.4 R 3.41(ys)-.15 G .91 +(ite \(barring such basic changes as the CPU type or the operating system\).) +200.63 521.4 R 2.61 -.8(We h)118.66 533.4 T -2.25 -.2(av e).8 H 1.009 +(found this seemingly unimportant goal to be critical in real life.)3.71 F +1.009(Besides the simple)6.009 F .66(problems that occur when an)118.66 545.4 R +3.16(yp)-.15 G .66(rogram gets recompiled in a dif)249.84 545.4 R .66 +(ferent en)-.25 F .66(vironment, man)-.4 F 3.16(ys)-.15 G(ites)490.11 545.4 Q +(lik)118.66 557.4 Q 2.5(et)-.1 G 2.5<6f99>138.84 557.4 S(\214ddle\232 with an) +150.78 557.4 Q(ything that the)-.15 E 2.5(yw)-.15 G(ill be recompiling an) +282.42 557.4 Q(yw)-.15 E(ay)-.1 E(.)-.65 E(\(6\))92 573.6 Q F2(Sendmail)118.66 +573.6 Q F1 .184(must be able to let v)2.684 F .184 +(arious groups maintain their o)-.25 F .184(wn mailing lists, and let indi)-.25 +F(viduals)-.25 E(specify their o)118.66 585.6 Q(wn forw)-.25 E +(arding, without modifying the system alias \214le.)-.1 E 12.5(\(7\) Each)92 +601.8 R .313(user should be able to specify which mailer to e)2.813 F -.15(xe) +-.15 G .313(cute to process mail being deli).15 F -.15(ve)-.25 G .314(red for) +.15 F 3.098(him. This)118.66 613.8 R .598(feature allo)3.098 F .598 +(ws users who are using specialized mailers that use a dif)-.25 F .598 +(ferent format to)-.25 F -.2(bu)118.66 625.8 S .25(ild their en).2 F .25 +(vironment without changing the system, and f)-.4 F .25 +(acilitates specialized functions \(such)-.1 F(as returning an \231I am on v) +118.66 637.8 Q(acation\232 message\).)-.25 E 12.5(\(8\) Netw)92 654 R 1.553 +(ork traf)-.1 F 1.552(\214c should be minimized by batching addresses to a sin\ +gle host where possible,)-.25 F(without assistance from the user)118.66 666 Q +(.)-.55 E .374(These goals moti)112 682.2 R -.25(va)-.25 G .374 +(ted the architecture illustrated in \214gure 1.).25 F .375 +(The user interacts with a mail gen-)5.375 F .491(erating and sending program.) +87 694.2 R .491(When the mail is created, the generator calls)5.491 F F2 +(sendmail)2.99 E F1 2.99(,w)C .49(hich routes the)444.14 694.2 R .84 +(message to the correct mailer\(s\).)87 706.2 R .841 +(Since some of the senders may be netw)5.84 F .841(ork serv)-.1 F .841 +(ers and some of the)-.15 F(mailers may be netw)87 718.2 Q(ork clients,)-.1 E +F2(sendmail)2.5 E F1(may be used as an internet mail g)2.5 E(ate)-.05 E -.1(wa) +-.25 G -.65(y.).1 G EP +%%Page: 3 3 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(SENDMAIL \212 An Inter)72 60 Q(netw)-.15 E +(ork Mail Router)-.1 E(SMM:9-3)462.9 60 Q .4 LW 77 108 72 108 DL 79 108 74 108 +DL 84 108 79 108 DL 89 108 84 108 DL 94 108 89 108 DL 99 108 94 108 DL 104 108 +99 108 DL 109 108 104 108 DL 114 108 109 108 DL 119 108 114 108 DL 124 108 119 +108 DL 129 108 124 108 DL 134 108 129 108 DL 139 108 134 108 DL 144 108 139 108 +DL 149 108 144 108 DL 154 108 149 108 DL 159 108 154 108 DL 164 108 159 108 DL +169 108 164 108 DL 174 108 169 108 DL 179 108 174 108 DL 184 108 179 108 DL 189 +108 184 108 DL 194 108 189 108 DL 199 108 194 108 DL 204 108 199 108 DL 209 108 +204 108 DL 214 108 209 108 DL 219 108 214 108 DL 224 108 219 108 DL 229 108 224 +108 DL 234 108 229 108 DL 239 108 234 108 DL 244 108 239 108 DL 249 108 244 108 +DL 254 108 249 108 DL 259 108 254 108 DL 264 108 259 108 DL 269 108 264 108 DL +274 108 269 108 DL 279 108 274 108 DL 284 108 279 108 DL 289 108 284 108 DL 294 +108 289 108 DL 299 108 294 108 DL 304 108 299 108 DL 309 108 304 108 DL 314 108 +309 108 DL 319 108 314 108 DL 324 108 319 108 DL 329 108 324 108 DL 334 108 329 +108 DL 339 108 334 108 DL 344 108 339 108 DL 349 108 344 108 DL 354 108 349 108 +DL 359 108 354 108 DL 364 108 359 108 DL 369 108 364 108 DL 374 108 369 108 DL +379 108 374 108 DL 384 108 379 108 DL 389 108 384 108 DL 394 108 389 108 DL 399 +108 394 108 DL 404 108 399 108 DL 409 108 404 108 DL 414 108 409 108 DL 419 108 +414 108 DL 424 108 419 108 DL 429 108 424 108 DL 434 108 429 108 DL 439 108 434 +108 DL 444 108 439 108 DL 449 108 444 108 DL 454 108 449 108 DL 459 108 454 108 +DL 464 108 459 108 DL 469 108 464 108 DL 474 108 469 108 DL 479 108 474 108 DL +484 108 479 108 DL 489 108 484 108 DL 494 108 489 108 DL 499 108 494 108 DL 504 +108 499 108 DL/F1 10/Times-Roman@0 SF(sender1)164.45 155.6 Q 144 135.6 144 +171.6 DL 216 135.6 144 135.6 DL 216 171.6 216 135.6 DL 144 171.6 216 171.6 DL +(sender2)272.45 155.6 Q 252 135.6 252 171.6 DL 324 135.6 252 135.6 DL 324 171.6 +324 135.6 DL 252 171.6 324 171.6 DL(sender3)380.45 155.6 Q 360 135.6 360 171.6 +DL 432 135.6 360 135.6 DL 432 171.6 432 135.6 DL 360 171.6 432 171.6 DL 288 +207.6 288 171.6 DL 288 207.6 286.2 200.4 DL 288 207.6 289.8 200.4 DL(sendmail) +269.945 227.6 Q 216 207.6 216 243.6 DL 360 207.6 216 207.6 DL 360 243.6 360 +207.6 DL 216 243.6 360 243.6 DL 288 279.6 288 243.6 DL 288 279.6 286.2 272.4 DL +288 279.6 289.8 272.4 DL(mailer1)164.725 299.6 Q 144 279.6 144 315.6 DL 216 +279.6 144 279.6 DL 216 315.6 216 279.6 DL 144 315.6 216 315.6 DL(mailer2) +272.725 299.6 Q 252 279.6 252 315.6 DL 324 279.6 252 279.6 DL 324 315.6 324 +279.6 DL 252 315.6 324 315.6 DL(mailer3)380.725 299.6 Q 360 279.6 360 315.6 DL +432 279.6 360 279.6 DL 432 315.6 432 279.6 DL 360 315.6 432 315.6 DL 252 207.6 +180 171.6 DL 252 207.6 244.728 206.016 DL 252 207.6 246.384 202.776 DL 324 +207.6 396 171.6 DL 324 207.6 329.616 202.776 DL 324 207.6 331.272 206.016 DL +180 279.6 252 243.6 DL 180 279.6 185.616 274.776 DL 180 279.6 187.272 278.016 +DL 396 279.6 324 243.6 DL 396 279.6 388.728 278.016 DL 396 279.6 390.384 +274.776 DL(Figure 1 \212 Sendmail System Structure.)208 346.8 Q 77 358.8 72 +358.8 DL 79 358.8 74 358.8 DL 84 358.8 79 358.8 DL 89 358.8 84 358.8 DL 94 +358.8 89 358.8 DL 99 358.8 94 358.8 DL 104 358.8 99 358.8 DL 109 358.8 104 +358.8 DL 114 358.8 109 358.8 DL 119 358.8 114 358.8 DL 124 358.8 119 358.8 DL +129 358.8 124 358.8 DL 134 358.8 129 358.8 DL 139 358.8 134 358.8 DL 144 358.8 +139 358.8 DL 149 358.8 144 358.8 DL 154 358.8 149 358.8 DL 159 358.8 154 358.8 +DL 164 358.8 159 358.8 DL 169 358.8 164 358.8 DL 174 358.8 169 358.8 DL 179 +358.8 174 358.8 DL 184 358.8 179 358.8 DL 189 358.8 184 358.8 DL 194 358.8 189 +358.8 DL 199 358.8 194 358.8 DL 204 358.8 199 358.8 DL 209 358.8 204 358.8 DL +214 358.8 209 358.8 DL 219 358.8 214 358.8 DL 224 358.8 219 358.8 DL 229 358.8 +224 358.8 DL 234 358.8 229 358.8 DL 239 358.8 234 358.8 DL 244 358.8 239 358.8 +DL 249 358.8 244 358.8 DL 254 358.8 249 358.8 DL 259 358.8 254 358.8 DL 264 +358.8 259 358.8 DL 269 358.8 264 358.8 DL 274 358.8 269 358.8 DL 279 358.8 274 +358.8 DL 284 358.8 279 358.8 DL 289 358.8 284 358.8 DL 294 358.8 289 358.8 DL +299 358.8 294 358.8 DL 304 358.8 299 358.8 DL 309 358.8 304 358.8 DL 314 358.8 +309 358.8 DL 319 358.8 314 358.8 DL 324 358.8 319 358.8 DL 329 358.8 324 358.8 +DL 334 358.8 329 358.8 DL 339 358.8 334 358.8 DL 344 358.8 339 358.8 DL 349 +358.8 344 358.8 DL 354 358.8 349 358.8 DL 359 358.8 354 358.8 DL 364 358.8 359 +358.8 DL 369 358.8 364 358.8 DL 374 358.8 369 358.8 DL 379 358.8 374 358.8 DL +384 358.8 379 358.8 DL 389 358.8 384 358.8 DL 394 358.8 389 358.8 DL 399 358.8 +394 358.8 DL 404 358.8 399 358.8 DL 409 358.8 404 358.8 DL 414 358.8 409 358.8 +DL 419 358.8 414 358.8 DL 424 358.8 419 358.8 DL 429 358.8 424 358.8 DL 434 +358.8 429 358.8 DL 439 358.8 434 358.8 DL 444 358.8 439 358.8 DL 449 358.8 444 +358.8 DL 454 358.8 449 358.8 DL 459 358.8 454 358.8 DL 464 358.8 459 358.8 DL +469 358.8 464 358.8 DL 474 358.8 469 358.8 DL 479 358.8 474 358.8 DL 484 358.8 +479 358.8 DL 489 358.8 484 358.8 DL 494 358.8 489 358.8 DL 499 358.8 494 358.8 +DL 504 358.8 499 358.8 DL F0 2.5(2. O)72 394.8 R(VER)-.5 E(VIEW)-.55 E 2.5 +(2.1. System)87 418.8 R(Or)2.5 E(ganization)-.1 E/F2 10/Times-Italic@0 SF +(Sendmail)127 435 Q F1 .874(neither interf)3.374 F .874 +(aces with the user nor does actual mail deli)-.1 F -.15(ve)-.25 G(ry).15 E +5.873(.R)-.65 G(ather)431.241 435 Q 3.373(,i)-.4 G 3.373(tc)459.484 435 S .873 +(ollects a)470.077 435 R .619(message generated by a user interf)102 447 R .619 +(ace program \(UIP\) such as Berk)-.1 F(ele)-.1 E(y)-.15 E F2(Mail)3.12 E F1 +3.12(,M)C 3.12(S[)427.6 447 S(Crock)439.61 447 Q .62(er77b], or)-.1 F 1.428 +(MH [Borden79], edits the message as required by the destination netw)102 459 R +1.427(ork, and calls appropriate)-.1 F .28(mailers to do mail deli)102 473 R +-.15(ve)-.25 G .281(ry or queueing for netw).15 F .281(ork transmission)-.1 F +/F3 7/Times-Roman@0 SF(1)364.275 469 Q F1 5.281(.T)367.775 473 S .281 +(his discipline allo)381.666 473 R .281(ws the inser)-.25 F(-)-.2 E 1.354 +(tion of ne)102 485 R 3.854(wm)-.25 G 1.354(ailers at minimum cost.)161.642 485 +R 1.354(In this sense)6.354 F F2(sendmail)3.853 E F1 1.353 +(resembles the Message Processing)3.853 F(Module \(MPM\) of [Postel79b].)102 +497 Q F0 2.5(2.2. Interfaces)87 521 R(to the Outside W)2.5 E(orld)-.75 E F1 +.041(There are three w)127 537.2 R(ays)-.1 E F2(sendmail)2.541 E F1 .041 +(can communicate with the outside w)2.541 F .042(orld, both in recei)-.1 F .042 +(ving and)-.25 F 1.195(in sending mail.)102 549.2 R 1.194 +(These are using the con)6.194 F -.15(ve)-.4 G 1.194(ntional UNIX ar).15 F +1.194(gument v)-.18 F 1.194(ector/return status, speaking)-.15 F(SMTP o)102 +561.2 Q -.15(ve)-.15 G 2.5(rap).15 G(air of UNIX pipes, and speaking SMTP o) +162.53 561.2 Q -.15(ve)-.15 G 2.5(ra).15 G 2.5(ni)348.03 561.2 S +(nterprocess\(or\) channel.)358.31 561.2 Q F0 2.5(2.2.1. Ar)102 585.2 R +(gument v)-.1 E(ector/exit status)-.1 E F1 .52(This technique is the standard \ +UNIX method for communicating with the process.)142 601.4 R 3.02(Al)5.52 G(ist) +494.55 601.4 Q .442(of recipients is sent in the ar)117 613.4 R .441(gument v) +-.18 F(ector)-.15 E 2.941(,a)-.4 G .441 +(nd the message body is sent on the standard input.)299.491 613.4 R(An)117 +625.4 Q .351(ything that the mailer prints is simply collected and sent back t\ +o the sender if there were an)-.15 F(y)-.15 E 2.621(problems. The)117 637.4 R +-.15(ex)2.621 G .121(it status from the mailer is collected after the message \ +is sent, and a diagnostic).15 F(is printed if appropriate.)117 649.4 Q .32 LW +76 678.8 72 678.8 DL 80 678.8 76 678.8 DL 84 678.8 80 678.8 DL 88 678.8 84 +678.8 DL 92 678.8 88 678.8 DL 96 678.8 92 678.8 DL 100 678.8 96 678.8 DL 104 +678.8 100 678.8 DL 108 678.8 104 678.8 DL 112 678.8 108 678.8 DL 116 678.8 112 +678.8 DL 120 678.8 116 678.8 DL 124 678.8 120 678.8 DL 128 678.8 124 678.8 DL +132 678.8 128 678.8 DL 136 678.8 132 678.8 DL 140 678.8 136 678.8 DL 144 678.8 +140 678.8 DL 148 678.8 144 678.8 DL 152 678.8 148 678.8 DL 156 678.8 152 678.8 +DL 160 678.8 156 678.8 DL 164 678.8 160 678.8 DL 168 678.8 164 678.8 DL 172 +678.8 168 678.8 DL 176 678.8 172 678.8 DL 180 678.8 176 678.8 DL 184 678.8 180 +678.8 DL 188 678.8 184 678.8 DL 192 678.8 188 678.8 DL 196 678.8 192 678.8 DL +200 678.8 196 678.8 DL 204 678.8 200 678.8 DL 208 678.8 204 678.8 DL 212 678.8 +208 678.8 DL 216 678.8 212 678.8 DL/F4 5/Times-Roman@0 SF(1)93.6 689.2 Q/F5 8 +/Times-Roman@0 SF -.12(ex)3.2 K(cept when mailing to a \214le, when).12 E/F6 8 +/Times-Italic@0 SF(sendmail)2 E F5(does the deli)2 E -.12(ve)-.2 G(ry directly) +.12 E(.)-.52 E EP +%%Page: 4 4 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 192.28(SMM:9-4 SENDMAIL)72 60 R 2.5<8a41>2.5 G 2.5(nI) +383.99 60 S(nter)395.94 60 Q(netw)-.15 E(ork Mail Router)-.1 E 2.5(2.2.2. SMTP) +102 96 R -.1(ove)2.5 G 2.5(rp).1 G(ipes)186.52 96 Q/F1 10/Times-Roman@0 SF .774 +(The SMTP protocol [Postel82] can be used to run an interacti)142 112.2 R 1.074 +-.15(ve l)-.25 H .774(ock-step interf).15 F .774(ace with)-.1 F .507 +(the mailer)117 124.2 R 5.507(.A)-.55 G .506(subprocess is still created, b) +175.461 124.2 R .506(ut no recipient addresses are passed to the mailer via)-.2 +F .075(the ar)117 136.2 R .075(gument list.)-.18 F .075(Instead, the)5.075 F +2.575(ya)-.15 G .075 +(re passed one at a time in commands sent to the processes stan-)249.805 136.2 +R .19(dard input.)117 148.2 R(An)5.19 E .19(ything appearing on the standard o\ +utput must be a reply code in a special format.)-.15 F F0 2.5(2.2.3. SMTP)102 +172.2 R -.1(ove)2.5 G 2.5(ra).1 G 2.5(nI)185.96 172.2 S(PC connection)197.91 +172.2 Q F1 .366(This technique is similar to the pre)142 188.4 R .366 +(vious technique, e)-.25 F .366(xcept that it uses a 4.2bsd IPC chan-)-.15 F +.953(nel [UNIX83].)117 200.4 R .953(This method is e)5.953 F .953 +(xceptionally \215e)-.15 F .952 +(xible in that the mailer need not reside on the)-.15 F(same machine.)117 212.4 +Q(It is normally used to connect to a sendmail process on another machine.)5 E +F0 2.5(2.3. Operational)87 236.4 R(Description)2.5 E F1 .228(When a sender w) +127 252.6 R .228(ants to send a message, it issues a request to)-.1 F/F2 10 +/Times-Italic@0 SF(sendmail)2.729 E F1 .229(using one of the three)2.729 F +1.028(methods described abo)102 264.6 R -.15(ve)-.15 G(.).15 E F2(Sendmail) +6.028 E F1 1.028(operates in tw)3.528 F 3.528(od)-.1 G 1.028(istinct phases.) +325.706 264.6 R 1.028(In the \214rst phase, it collects)6.028 F .612 +(and stores the message.)102 276.6 R .612(In the second phase, message deli) +5.612 F -.15(ve)-.25 G .612(ry occurs.).15 F .612(If there were errors during) +5.612 F 1.59(processing during the second phase,)102 288.6 R F2(sendmail)4.09 E +F1 1.59(creates and returns a ne)4.09 F 4.09(wm)-.25 G 1.59 +(essage describing the)415.84 288.6 R +(error and/or returns an status code telling what went wrong.)102 300.6 Q F0 +2.5(2.3.1. Ar)102 324.6 R(gument pr)-.1 E(ocessing and addr)-.18 E(ess parsing) +-.18 E F1(If)142 340.8 Q F2(sendmail)3.321 E F1 .821 +(is called using one of the tw)3.321 F 3.322(os)-.1 G .822 +(ubprocess techniques, the ar)320.66 340.8 R .822(guments are \214rst)-.18 F +.797(scanned and option speci\214cations are processed.)117 352.8 R .796 +(Recipient addresses are then collected, either)5.796 F .717(from the command \ +line or from the SMTP RCPT command, and a list of recipients is created.)117 +364.8 R .347(Aliases are e)117 376.8 R .347 +(xpanded at this step, including mailing lists.)-.15 F .347(As much v)5.347 F +.346(alidation as possible of the)-.25 F 1.001 +(addresses is done at this step: syntax is check)117 388.8 R 1.002 +(ed, and local addresses are v)-.1 F 1.002(eri\214ed, b)-.15 F 1.002 +(ut detailed)-.2 F .709 +(checking of host names and addresses is deferred until deli)117 400.8 R -.15 +(ve)-.25 G(ry).15 E 5.708(.F)-.65 G(orw)388.946 400.8 Q .708 +(arding is also performed)-.1 F(as the local addresses are v)117 412.8 Q +(eri\214ed.)-.15 E F2(Sendmail)142 429 Q F1 .307 +(appends each address to the recipient list after parsing.)2.807 F .307 +(When a name is aliased)5.307 F .322(or forw)117 441 R .322(arded, the old nam\ +e is retained in the list, and a \215ag is set that tells the deli)-.1 F -.15 +(ve)-.25 G .322(ry phase to).15 F .479(ignore this recipient.)117 453 R .479 +(This list is k)5.479 F .479(ept free from duplicates, pre)-.1 F -.15(ve)-.25 G +.48(nting alias loops and duplicate).15 F(messages deli)117 465 Q -.15(ve)-.25 +G(rd to the same recipient, as might occur if a person is in tw).15 E 2.5(og) +-.1 G(roups.)428.12 465 Q F0 2.5(2.3.2. Message)102 489 R(collection)2.5 E F2 +(Sendmail)142 505.2 Q F1 .454(then collects the message.)2.954 F .454 +(The message should ha)5.454 F .754 -.15(ve a h)-.2 H .453(eader at the be).15 +F(ginning.)-.15 E .778(No formatting requirements are imposed on the message e) +117 517.2 R .778(xcept that the)-.15 F 3.278(ym)-.15 G .778(ust be lines of te) +427.708 517.2 R(xt)-.15 E .78(\(i.e., binary data is not allo)117 529.2 R 3.28 +(wed\). The)-.25 F .779(header is parsed and stored in memory)3.28 F 3.279(,a) +-.65 G .779(nd the body of)443.613 529.2 R(the message is sa)117 541.2 Q -.15 +(ve)-.2 G 2.5(di).15 G 2.5(nat)204.97 541.2 S(emporary \214le.)222.19 541.2 Q +3.227 -.8(To s)142 557.4 T 1.627(implify the program interf).8 F 1.628 +(ace, the message is collected e)-.1 F -.15(ve)-.25 G 4.128(ni).15 G 4.128(fn) +420.536 557.4 S 4.128(oa)432.994 557.4 S 1.628(ddresses were)446.562 557.4 R +-.25(va)117 569.4 S 2.5(lid. The).25 F(message will be returned with an error) +2.5 E(.)-.55 E F0 2.5(2.3.3. Message)102 593.4 R(deli)2.5 E -.1(ve)-.1 G(ry).1 +E F1 -.15(Fo)142 609.6 S 2.618(re).15 G .117 +(ach unique mailer and host in the recipient list,)162.798 609.6 R F2(sendmail) +2.617 E F1 .117(calls the appropriate mailer)2.617 F(.)-.55 E .619 +(Each mailer in)117 621.6 R -.2(vo)-.4 G .619(cation sends to all users recei) +.2 F .619(ving the message on one host.)-.25 F .62(Mailers that only)5.62 F +(accept one recipient at a time are handled properly)117 633.6 Q(.)-.65 E .47 +(The message is sent to the mailer using one of the same three interf)142 649.8 +R .47(aces used to submit a)-.1 F 1.465(message to sendmail.)117 661.8 R 1.465 +(Each cop)6.465 F 3.965(yo)-.1 G 3.965(ft)263.925 661.8 S 1.465 +(he message is prepended by a customized header)274 661.8 R 6.465(.T)-.55 G(he) +494.56 661.8 Q 1.455(mailer status code is caught and check)117 673.8 R 1.455 +(ed, and a suitable error message gi)-.1 F -.15(ve)-.25 G 3.955(na).15 G 3.955 +(sa)448.115 673.8 S(ppropriate.)460.4 673.8 Q .589(The e)117 685.8 R .589(xit \ +code must conform to a system standard or a generic message \(\231Service una) +-.15 F -.25(va)-.2 G(ilable\232\)).25 E(is gi)117 697.8 Q -.15(ve)-.25 G(n.).15 +E EP +%%Page: 5 5 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(SENDMAIL \212 An Inter)72 60 Q(netw)-.15 E +(ork Mail Router)-.1 E(SMM:9-5)462.9 60 Q 2.5(2.3.4. Queueing)102 96 R -.25(fo) +2.5 G 2.5(rr).25 G(etransmission)192.4 96 Q/F1 10/Times-Roman@0 SF .209(If the\ + mailer returned an status that indicated that it might be able to handle the \ +mail later)142 112.2 R(,)-.4 E/F2 10/Times-Italic@0 SF(sendmail)117 124.2 Q F1 +(will queue the mail and try ag)2.5 E(ain later)-.05 E(.)-.55 E F0 2.5 +(2.3.5. Retur)102 148.2 R 2.5(nt)-.15 G 2.5(os)165.73 148.2 S(ender)177.12 +148.2 Q F1 .588(If errors occur during processing,)142 164.4 R F2(sendmail) +3.088 E F1 .589(returns the message to the sender for retrans-)3.088 F 3.133 +(mission. The)117 176.4 R .632(letter can be mailed back or written in the \ +\214le \231dead.letter\232 in the sender')3.133 F 3.132(sh)-.55 G(ome)486.78 +176.4 Q(directory)117 190.4 Q/F3 7/Times-Roman@0 SF(2)153.1 186.4 Q F1(.)156.6 +190.4 Q F0 2.5(2.4. Message)87 214.4 R(Header Editing)2.5 E F1 1.756 +(Certain editing of the message header occurs automatically)127 230.6 R 6.756 +(.H)-.65 G 1.756(eader lines can be inserted)391.456 230.6 R .41 +(under control of the con\214guration \214le.)102 242.6 R .41 +(Some lines can be mer)5.41 F .41(ged; for e)-.18 F .41 +(xample, a \231From:\232 line and)-.15 F 2.5<6199>102 254.6 S +(Full-name:\232 line can be mer)113.38 254.6 Q +(ged under certain circumstances.)-.18 E F0 2.5(2.5. Con\214guration)87 278.6 R +(File)2.5 E F1 .798(Almost all con\214guration information is read at runtime \ +from an ASCII \214le, encoding macro)127 294.8 R .679 +(de\214nitions \(de\214ning the v)102 306.8 R .678 +(alue of macros used internally\), header declarations \(telling sendmail the) +-.25 F 1.009(format of header lines that it will process specially)102 318.8 R +3.509(,i)-.65 G 1.009(.e., lines that it will add or reformat\), mailer)320.398 +318.8 R .478(de\214nitions \(gi)102 330.8 R .478(ving information such as the \ +location and characteristics of each mailer\), and address)-.25 F(re)102 342.8 +Q .428(writing rules \(a limited production system to re)-.25 F .429 +(write addresses which is used to parse and re)-.25 F(write)-.25 E +(the addresses\).)102 354.8 Q 2.828 -.8(To i)127 371 T(mpro).8 E 1.528 -.15 +(ve p)-.15 H 1.228(erformance when reading the con\214guration \214le, a memor\ +y image can be pro-).15 F 2.5(vided. This)102 383 R(pro)2.5 E +(vides a \231compiled\232 form of the con\214guration \214le.)-.15 E F0 2.5 +(3. USA)72 407 R(GE AND IMPLEMENT)-.55 E -.95(AT)-.9 G(ION).95 E 2.5(3.1. Ar)87 +431 R(guments)-.1 E F1(Ar)127 447.2 Q .376 +(guments may be \215ags and addresses.)-.18 F .377(Flags set v)5.377 F .377 +(arious processing options.)-.25 F -.15(Fo)5.377 G(llo).15 E .377(wing \215ag) +-.25 F(ar)102 459.2 Q .281(guments, address ar)-.18 F .281(guments may be gi) +-.18 F -.15(ve)-.25 G .281(n, unless we are running in SMTP mode.).15 F .28 +(Addresses fol-)5.28 F(lo)102 471.2 Q 2.5(wt)-.25 G(he syntax in RFC822 [Crock) +122.03 471.2 Q(er82] for ARP)-.1 E(ANET address formats.)-.92 E +(In brief, the format is:)5 E 12.5(\(1\) An)107 487.4 R +(ything in parentheses is thro)-.15 E(wn a)-.25 E -.1(wa)-.15 G 2.5(y\().1 G +(as a comment\).)299.65 487.4 Q 12.5(\(2\) An)107 503.6 R .051 +(ything in angle brack)-.15 F .051(ets \(\231<)-.1 F .051 +(>\232\) is preferred o)1.666 F -.15(ve)-.15 G 2.551(ra).15 G -.15(ny)348.064 +503.6 S .051(thing else.).15 F .051(This rule implements the)5.051 F(ARP)133.66 +515.6 Q(ANET standard that addresses of the form)-.92 E +(user name <machine-address>)173.66 531.8 Q(will send to the electronic \231ma\ +chine-address\232 rather than the human \231user name.)133.66 548 Q<9a>-.7 E +12.5(\(3\) Double)107 564.2 R 2.246(quotes \()4.746 F -2.754 2.5("\) q)2.5 H +2.246(uote phrases; backslashes quote characters.)224.188 564.2 R 2.246 +(Backslashes are more)7.246 F(po)133.66 576.2 Q .654(werful in that the)-.25 F +3.154(yw)-.15 G .655(ill cause otherwise equi)229.196 576.2 R -.25(va)-.25 G +.655(lent phrases to compare dif).25 F .655(ferently \212 for)-.25 F -.15(ex) +133.66 588.2 S(ample,).15 E F2(user)2.5 E F1(and)2.5 E F2("user")2.5 E F1 +(are equi)2.5 E -.25(va)-.25 G(lent, b).25 E(ut)-.2 E F2(\\user)2.5 E F1 +(is dif)2.5 E(ferent from either of them.)-.25 E -.15(Pa)127 604.4 S 1.12 +(rentheses, angle brack).15 F 1.12 +(ets, and double quotes must be properly balanced and nested.)-.1 F(The)6.12 E +(re)102 618.4 Q(writing rules control remaining parsing)-.25 E F3(3)266.17 +614.4 Q F1(.)269.67 618.4 Q .32 LW 76 646 72 646 DL 80 646 76 646 DL 84 646 80 +646 DL 88 646 84 646 DL 92 646 88 646 DL 96 646 92 646 DL 100 646 96 646 DL 104 +646 100 646 DL 108 646 104 646 DL 112 646 108 646 DL 116 646 112 646 DL 120 646 +116 646 DL 124 646 120 646 DL 128 646 124 646 DL 132 646 128 646 DL 136 646 132 +646 DL 140 646 136 646 DL 144 646 140 646 DL 148 646 144 646 DL 152 646 148 646 +DL 156 646 152 646 DL 160 646 156 646 DL 164 646 160 646 DL 168 646 164 646 DL +172 646 168 646 DL 176 646 172 646 DL 180 646 176 646 DL 184 646 180 646 DL 188 +646 184 646 DL 192 646 188 646 DL 196 646 192 646 DL 200 646 196 646 DL 204 646 +200 646 DL 208 646 204 646 DL 212 646 208 646 DL 216 646 212 646 DL/F4 5 +/Times-Roman@0 SF(2)93.6 656.4 Q/F5 8/Times-Roman@0 SF(Ob)3.2 I(viously)-.12 E +2.226(,i)-.52 G 2.226(ft)135.246 659.6 S .226(he site gi)142.36 659.6 R .226(v\ +ing the error is not the originating site, the only reasonable option is to ma\ +il back to the sender)-.2 F 4.227(.A)-.44 G(lso,)492.664 659.6 Q .191 +(there are man)72 669.2 R 2.191(ym)-.12 G .19(ore error disposition options, b) +128.213 669.2 R .19(ut the)-.16 F 2.19(yo)-.12 G .19(nly ef)255.514 669.2 R .19 +(fect the error message \212 the \231return to sender\232 function is al)-.2 F +-.08(wa)-.08 G .19(ys han-).08 F(dled in one of these tw)72 678.8 Q 2(ow)-.08 G +(ays.)156.272 678.8 Q F4(3)93.6 689.2 Q F5 +(Disclaimer: Some special processing is done after re)3.2 I +(writing local names; see belo)-.2 E -.52(w.)-.2 G EP +%%Page: 6 6 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 192.28(SMM:9-6 SENDMAIL)72 60 R 2.5<8a41>2.5 G 2.5(nI) +383.99 60 S(nter)395.94 60 Q(netw)-.15 E(ork Mail Router)-.1 E 2.5(3.2. Mail)87 +96 R(to Files and Pr)2.5 E(ograms)-.18 E/F1 10/Times-Roman@0 SF .609 +(Files and programs are le)127 112.2 R .609(gitimate message recipients.)-.15 F +.609(Files pro)5.609 F .609(vide archi)-.15 F -.25(va)-.25 G 3.109(ls).25 G .61 +(torage of mes-)445.02 112.2 R .124 +(sages, useful for project administration and history)102 124.2 R 5.124(.P)-.65 +G .124(rograms are useful as recipients in a v)318.308 124.2 R .124(ariety of) +-.25 F .69(situations, for e)102 136.2 R .691(xample, to maintain a public rep\ +ository of systems messages \(such as the Berk)-.15 F(ele)-.1 E(y)-.15 E/F2 10 +/Times-Italic@0 SF(msgs)102 148.2 Q F1(program, or the MARS system [Sattle)2.5 +E(y78]\).)-.15 E(An)127 164.4 Q 3.188(ya)-.15 G .688(ddress passing through th\ +e initial parsing algorithm as a local address \(i.e, not appear)151.698 164.4 +R(-)-.2 E .276(ing to be a v)102 176.4 R .276 +(alid address for another mailer\) is scanned for tw)-.25 F 2.776(os)-.1 G .277 +(pecial cases.)362.128 176.4 R .277(If pre\214x)5.277 F .277(ed by a v)-.15 F +(erti-)-.15 E .18(cal bar \(\231)102 188.4 R .833<7c9a>.833 G 2.68(\)t)-.833 G +.179(he rest of the address is processed as a shell command.)156.456 188.4 R +.179(If the user name be)5.179 F .179(gins with a)-.15 F(slash mark \(\231/)102 +200.4 Q(\232\) the name is used as a \214le name, instead of a login name.).833 +E .241(Files that ha)127 216.6 R .541 -.15(ve s)-.2 H .241 +(etuid or setgid bits set b).15 F .241(ut no e)-.2 F -.15(xe)-.15 G .241 +(cute bits set ha).15 F .541 -.15(ve t)-.2 H .241(hose bits honored if).15 F F2 +(send-)2.742 E(mail)102 228.6 Q F1(is running as root.)2.5 E F0 2.5 +(3.3. Aliasing,)87 252.6 R -.25(Fo)2.5 G(rwarding, Inclusion).25 E F2(Sendmail) +127 268.8 Q F1 1.075(reroutes mail three w)3.575 F 3.575(ays. Aliasing)-.1 F +1.074(applies system wide.)3.575 F -.15(Fo)6.074 G(rw).15 E 1.074(arding allo) +-.1 F 1.074(ws each)-.25 F .233 +(user to reroute incoming mail destined for that account.)102 280.8 R .233 +(Inclusion directs)5.233 F F2(sendmail)2.733 E F1 .233(to read a \214le for) +2.733 F 2.5(al)102 292.8 S +(ist of addresses, and is normally used in conjunction with aliasing.)111.72 +292.8 Q F0 2.5(3.3.1. Aliasing)102 316.8 R F1 1.554 +(Aliasing maps names to address lists using a system-wide \214le.)142 333 R +1.553(This \214le is inde)6.553 F -.15(xe)-.15 G 4.053(dt).15 G(o)499 333 Q 1.1 +(speed access.)117 345 R 1.101(Only names that parse as local are allo)6.1 F +1.101(wed as aliases; this guarantees a unique)-.25 F -.1(ke)117 357 S 2.5(y\() +-.05 G(since there are no nicknames for the local host\).)137.02 357 Q F0 2.5 +(3.3.2. F)102 381 R(orwarding)-.25 E F1 .651 +(After aliasing, recipients that are local and v)142 397.2 R .651 +(alid are check)-.25 F .65(ed for the e)-.1 F .65(xistence of a \231.for)-.15 F +(-)-.2 E -.1(wa)117 409.2 S .493(rd\232 \214le in their home directory).1 F +5.493(.I)-.65 G 2.994(fi)264.178 409.2 S 2.994(te)273.282 409.2 S .494 +(xists, the message is)283.346 409.2 R F2(not)2.994 E F1 .494 +(sent to that user)2.994 F 2.994(,b)-.4 G .494(ut rather to)459.132 409.2 R .37 +(the list of users in that \214le.)117 421.2 R .37 +(Often this list will contain only one address, and the feature will be)5.37 F +(used for netw)117 433.2 Q(ork mail forw)-.1 E(arding.)-.1 E -.15(Fo)142 449.4 +S(rw).15 E 1.151(arding also permits a user to specify a pri)-.1 F -.25(va)-.25 +G 1.152(te incoming mailer).25 F 6.152(.F)-.55 G 1.152(or e)437.346 449.4 R +1.152(xample, for)-.15 F(-)-.2 E -.1(wa)117 461.4 S(rding to:).1 E -2.5 .833 +("| /)157 477.6 T(usr/local/ne)-.833 E(wmail myname")-.25 E(will use a dif)117 +493.8 Q(ferent incoming mailer)-.25 E(.)-.55 E F0 2.5(3.3.3. Inclusion)102 +517.8 R F1(Inclusion is speci\214ed in RFC 733 [Crock)142 534 Q(er77a] syntax:) +-.1 E(:Include: pathname)157 550.2 Q .391 +(An address of this form reads the \214le speci\214ed by)117 566.4 R F2 +(pathname)2.891 E F1 .391(and sends to all users listed in that)2.891 F +(\214le.)117 578.4 Q .644(The intent is)142 594.6 R F2(not)3.144 E F1 .644 +(to support direct use of this feature, b)3.144 F .644 +(ut rather to use this as a subset of)-.2 F 2.5(aliasing. F)117 606.6 R(or e) +-.15 E(xample, an alias of the form:)-.15 E +(project: :include:/usr/project/userlist)157 622.8 Q 1.93(is a method of letti\ +ng a project maintain a mailing list without interaction with the system)117 +639 R(administration, e)117 651 Q -.15(ve)-.25 G 2.5(ni).15 G 2.5(ft)203.54 651 +S(he alias \214le is protected.)212.15 651 Q 2.024(It is not necessary to reb) +142 667.2 R 2.024(uild the inde)-.2 F 4.524(xo)-.15 G 4.524(nt)317.822 667.2 S +2.025(he alias database when a :include: list is)330.126 667.2 R(changed.)117 +679.2 Q EP +%%Page: 7 7 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(SENDMAIL \212 An Inter)72 60 Q(netw)-.15 E +(ork Mail Router)-.1 E(SMM:9-7)462.9 60 Q 2.5(3.4. Message)87 96 R(Collection) +2.5 E/F1 10/Times-Roman@0 SF .857 +(Once all recipient addresses are parsed and v)127 112.2 R .857 +(eri\214ed, the message is collected.)-.15 F .856(The message)5.857 F +(comes in tw)102 124.2 Q 2.5(op)-.1 G +(arts: a message header and a message body)162.73 124.2 Q 2.5(,s)-.65 G +(eparated by a blank line.)343.42 124.2 Q +(The header is formatted as a series of lines of the form)127 140.4 Q +(\214eld-name: \214eld-v)178 156.6 Q(alue)-.25 E(Field-v)102 172.8 Q 1.366 +(alue can be split across lines by starting the follo)-.25 F 1.366 +(wing lines with a space or a tab)-.25 F 6.366(.S)-.4 G(ome)486.78 172.8 Q .211 +(header \214elds ha)102 184.8 R .511 -.15(ve s)-.2 H .211 +(pecial internal meaning, and ha).15 F .511 -.15(ve a)-.2 H .211 +(ppropriate special processing.).15 F .21(Other headers)5.21 F +(are simply passed through.)102 196.8 Q +(Some header \214elds may be added automatically)5 E 2.5(,s)-.65 G +(uch as time stamps.)413.53 196.8 Q .86(The body is a series of te)127 213 R +.861(xt lines.)-.15 F .861(It is completely uninterpreted and untouched, e) +5.861 F .861(xcept that)-.15 F 1.43(lines be)102 225 R 1.43 +(ginning with a dot ha)-.15 F 1.729 -.15(ve t)-.2 H 1.429 +(he dot doubled when transmitted o).15 F -.15(ve)-.15 G 3.929(ra).15 G 3.929 +(nS)407.213 225 S 1.429(MTP channel.)421.702 225 R(This)6.429 E -.15(ex)102 237 +S(tra dot is stripped by the recei).15 E -.15(ve)-.25 G -.55(r.).15 G F0 2.5 +(3.5. Message)87 261 R(Deli)2.5 E -.1(ve)-.1 G(ry).1 E F1 .028 +(The send queue is ordered by recei)127 277.2 R .029 +(ving host before transmission to implement message batch-)-.25 F 3.07 +(ing. Each)102 289.2 R .57(address is mark)3.07 F .57 +(ed as it is sent so rescanning the list is safe.)-.1 F .57(An ar)5.57 F .57 +(gument list is b)-.18 F .57(uilt as)-.2 F 1.138(the scan proceeds.)102 301.2 R +1.139(Mail to \214les is detected during the scan of the send list.)6.139 F +1.139(The interf)6.139 F 1.139(ace to the)-.1 F +(mailer is performed using one of the techniques described in section 2.2.)102 +313.2 Q .996(After a connection is established,)127 329.4 R/F2 10 +/Times-Italic@0 SF(sendmail)3.496 E F1(mak)3.495 E .995(es the per)-.1 F .995 +(-mailer changes to the header and)-.2 F .236(sends the result to the mailer) +102 341.4 R 5.236(.I)-.55 G 2.736(fa)228.406 341.4 S .537 -.15(ny m)238.912 +341.4 T .237(ail is rejected by the mailer).15 F 2.737(,a\215)-.4 G .237 +(ag is set to in)386.628 341.4 R -.2(vo)-.4 G .437 -.1(ke t).2 H .237 +(he return-).1 F(to-sender function after all deli)102 353.4 Q -.15(ve)-.25 G +(ry completes.).15 E F0 2.5(3.6. Queued)87 377.4 R(Messages)2.5 E F1 .163 +(If the mailer returns a \231temporary f)127 393.6 R .163(ailure\232 e)-.1 F +.162(xit status, the message is queued.)-.15 F 2.662(Ac)5.162 G .162 +(ontrol \214le is)455.336 393.6 R .85 +(used to describe the recipients to be sent to and v)102 405.6 R .851 +(arious other parameters.)-.25 F .851(This control \214le is for)5.851 F(-)-.2 +E 1.011(matted as a series of lines, each describing a sender)102 417.6 R 3.511 +(,ar)-.4 G 1.011(ecipient, the time of submission, or some)333.494 417.6 R .776 +(other salient parameter of the message.)102 429.6 R .776 +(The header of the message is stored in the control \214le, so)5.776 F(that th\ +e associated data \214le in the queue is just the temporary \214le that w)102 +441.6 Q(as originally collected.)-.1 E F0 2.5(3.7. Con\214guration)87 465.6 R +F1 .493(Con\214guration is controlled primarily by a con\214guration \214le re\ +ad at startup.)127 481.8 R F2(Sendmail)5.492 E F1(should)2.992 E +(not need to be recomplied e)102 493.8 Q(xcept)-.15 E 12.5(\(1\) T)107 510 R +2.5(oc)-.8 G(hange operating systems \(V6, V7/32V)150.91 510 Q 2.5(,4)-1.29 G +(BSD\).)313.21 510 Q 12.5(\(2\) T)107 526.2 R 2.5(or)-.8 G(emo)149.8 526.2 Q .3 +-.15(ve o)-.15 H 2.5(ri).15 G(nsert the DBM \(UNIX database\) library)192.27 +526.2 Q(.)-.65 E 12.5(\(3\) T)107 542.4 R 2.5(oc)-.8 G(hange ARP)150.91 542.4 Q +(ANET reply codes.)-.92 E 12.5(\(4\) T)107 558.6 R 2.5(oa)-.8 G +(dd headers \214elds requiring special processing.)150.91 558.6 Q .434 +(Adding mailers or changing parsing \(i.e., re)102 574.8 R .435 +(writing\) or routing information does not require recom-)-.25 F(pilation.)102 +586.8 Q 1.317(If the mail is being sent by a local user)127 603 R 3.817(,a)-.4 +G 1.317(nd the \214le \231.mailcf\232 e)303.914 603 R 1.317 +(xists in the sender')-.15 F 3.817(sh)-.55 G(ome)486.78 603 Q(directory)102 615 +Q 2.721(,t)-.65 G .221(hat \214le is read as a con\214guration \214le after th\ +e system con\214guration \214le.)145.451 615 R .222(The primary use)5.222 F +(of this feature is to add header lines.)102 627 Q 3.25(The con\214guration \ +\214le encodes macro de\214nitions, header de\214nitions, mailer de\214nitions\ +,)127 643.2 R(re)102 655.2 Q(writing rules, and options.)-.25 E F0 2.5 +(3.7.1. Macr)102 679.2 R(os)-.18 E F1 .332(Macros can be used in three w)142 +695.4 R 2.833(ays. Certain)-.1 F .333(macros transmit unstructured te)2.833 F +.333(xtual informa-)-.15 F .07(tion into the mail system, such as the name)117 +707.4 R F2(sendmail)2.57 E F1 .07 +(will use to identify itself in error messages.)2.57 F 1.247 +(Other macros transmit information from)117 719.4 R F2(sendmail)3.747 E F1 +1.247(to the con\214guration \214le for use in creating)3.747 F EP +%%Page: 8 8 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 192.28(SMM:9-8 SENDMAIL)72 60 R 2.5<8a41>2.5 G 2.5(nI) +383.99 60 S(nter)395.94 60 Q(netw)-.15 E(ork Mail Router)-.1 E/F1 10 +/Times-Roman@0 SF .312(other \214elds \(such as ar)117 96 R .312(gument v)-.18 +F .312(ectors to mailers\); e.g., the name of the sender)-.15 F 2.811(,a)-.4 G +.311(nd the host and)442.237 96 R .848(user of the recipient.)117 108 R .848 +(Other macros are unused internally)5.848 F 3.348(,a)-.65 G .848 +(nd can be used as shorthand in the)361.142 108 R(con\214guration \214le.)117 +120 Q F0 2.5(3.7.2. Header)102 144 R(declarations)2.5 E F1 .355 +(Header declarations inform)142 160.2 R/F2 10/Times-Italic@0 SF(sendmail)2.854 +E F1 .354(of the format of kno)2.854 F .354(wn header lines.)-.25 F(Kno)5.354 E +.354(wledge of)-.25 F 2.5(af)117 172.2 S .5 -.25(ew h)127.27 172.2 T +(eader lines is b).25 E(uilt into)-.2 E F2(sendmail)2.5 E F1 2.5(,s)C +(uch as the \231From:\232 and \231Date:\232 lines.)284.59 172.2 Q 1.201(Most c\ +on\214gured headers will be automatically inserted in the outgoing message if \ +the)142 188.4 R(y)-.15 E(don')117 200.4 Q 2.5(te)-.18 G +(xist in the incoming message.)144.72 200.4 Q +(Certain headers are suppressed by some mailers.)5 E F0 2.5(3.7.3. Mailer)102 +224.4 R(declarations)2.5 E F1 1.756(Mailer declarations tell)142 240.6 R F2 +(sendmail)4.256 E F1 1.756(of the v)4.256 F 1.756(arious mailers a)-.25 F -.25 +(va)-.2 G 1.756(ilable to it.).25 F 1.755(The de\214nition)6.755 F .119 +(speci\214es the internal name of the mailer)117 252.6 R 2.619(,t)-.4 G .12 +(he pathname of the program to call, some \215ags associ-)285.183 252.6 R 2.036 +(ated with the mailer)117 264.6 R 4.536(,a)-.4 G 2.036(nd an ar)213.894 264.6 R +2.036(gument v)-.18 F 2.036(ector to be used on the call; this v)-.15 F 2.035 +(ector is macro-)-.15 F -.15(ex)117 276.6 S(panded before use.).15 E F0 2.5 +(3.7.4. Addr)102 300.6 R(ess r)-.18 E(ewriting rules)-.18 E F1 .458 +(The heart of address parsing in)142 316.8 R F2(sendmail)2.959 E F1 .459 +(is a set of re)2.959 F .459(writing rules.)-.25 F .459(These are an ordered) +5.459 F .561(list of pattern-replacement rules, \(some)117 328.8 R .561 +(what lik)-.25 F 3.061(eap)-.1 G .561(roduction system, e)328.867 328.8 R .56 +(xcept that order is criti-)-.15 F 1.905 +(cal\), which are applied to each address.)117 340.8 R 1.905(The address is re) +6.905 F 1.906(written te)-.25 F 1.906(xtually until it is either)-.15 F(re)117 +352.8 Q .308(written into a special canonical form \(i.e., a \(mailer)-.25 F +2.807(,h)-.4 G .307(ost, user\) 3-tuple, such as {arpanet, usc-)342.118 352.8 R +.64(isif, postel} representing the address \231postel@usc-isif\232\), or it f) +117 364.8 R .641(alls of)-.1 F 3.141(ft)-.25 G .641(he end.)406.466 364.8 R +.641(When a pattern)5.641 F(matches, the rule is reapplied until it f)117 376.8 +Q(ails.)-.1 E 1.222 +(The con\214guration \214le also supports the editing of addresses into dif)142 +393 R 1.221(ferent formats.)-.25 F -.15(Fo)6.221 G(r).15 E -.15(ex)117 405 S +(ample, an address of the form:).15 E(ucsfcgl!tef)157 421.2 Q +(might be mapped into:)117 437.4 Q(tef@ucsfcgl.UUCP)157 453.6 Q +(to conform to the domain syntax.)117 469.8 Q -.35(Tr)5 G +(anslations can also be done in the other direction.).35 E F0 2.5 +(3.7.5. Option)102 493.8 R(setting)2.5 E F1 1.168(There are se)142 510 R -.15 +(ve)-.25 G 1.169(ral options that can be set from the con\214guration \214le.) +.15 F 1.169(These include the)6.169 F(pathnames of v)117 522 Q +(arious support \214les, timeouts, def)-.25 E(ault modes, etc.)-.1 E F0 2.5 +(4. COMP)72 546 R(ARISON WITH O)-.74 E(THER MAILERS)-.4 E 2.5(4.1. Deli)87 570 +R -.1(ve)-.1 G(rmail).1 E F2(Sendmail)127 586.2 Q F1(is an outgro)2.5 E(wth of) +-.25 E F2(delivermail)2.5 E F1 5(.T)C(he primary dif)301.18 586.2 Q +(ferences are:)-.25 E 12.5(\(1\) Con\214guration)107 602.4 R .273 +(information is not compiled in.)2.773 F .272(This change simpli\214es man) +5.273 F 2.772(yo)-.15 G 2.772(ft)445.686 602.4 S .272(he problems)454.568 602.4 +R(of mo)133.66 614.4 Q(ving to other machines.)-.15 E(It also allo)5 E +(ws easy deb)-.25 E(ugging of ne)-.2 E 2.5(wm)-.25 G(ailers.)413.89 614.4 Q +12.5(\(2\) Address)107 630.6 R .681(parsing is more \215e)3.181 F 3.182 +(xible. F)-.15 F .682(or e)-.15 F(xample,)-.15 E F2(delivermail)3.182 E F1 .682 +(only supported one g)3.182 F(ate)-.05 E -.1(wa)-.25 G 3.182(yt).1 G(o)499 +630.6 Q(an)133.66 642.6 Q 2.817(yn)-.15 G(etw)155.767 642.6 Q .317 +(ork, whereas)-.1 F F2(sendmail)2.817 E F1 .317(can be sensiti)2.817 F .616 +-.15(ve t)-.25 H 2.816(oh).15 G .316(ost names and reroute to dif)345.224 642.6 +R .316(ferent g)-.25 F(ate-)-.05 E -.1(wa)133.66 654.6 S(ys.).1 E 12.5(\(3\) F) +107 670.8 R(orw)-.15 E 1.627(arding and :include: features eliminate the requi\ +rement that the system alias \214le be)-.1 F .074(writable by an)133.66 682.8 R +2.574(yu)-.15 G .073 +(ser \(or that an update program be written, or that the system administration) +203.442 682.8 R(mak)133.66 694.8 Q 2.5(ea)-.1 G(ll changes\).)162.16 694.8 Q +(\(4\))107 711 Q F2(Sendmail)133.66 711 Q F1 .4 +(supports message batching across netw)2.9 F .401 +(orks when a message is being sent to mul-)-.1 F(tiple recipients.)133.66 723 Q +EP +%%Page: 9 9 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(SENDMAIL \212 An Inter)72 60 Q(netw)-.15 E +(ork Mail Router)-.1 E(SMM:9-9)462.9 60 Q/F1 10/Times-Roman@0 SF 12.5(\(5\) A) +107 96 R .875(mail queue is pro)3.375 F .874(vided in)-.15 F/F2 10 +/Times-Italic@0 SF(sendmail.)3.374 E F1 .874(Mail that cannot be deli)5.874 F +-.15(ve)-.25 G .874(red immediately b).15 F .874(ut can)-.2 F 1.063 +(potentially be deli)133.66 108 R -.15(ve)-.25 G 1.064 +(red later is stored in this queue for a later retry).15 F 6.064(.T)-.65 G +1.064(he queue also pro-)427.218 108 R .896(vides a b)133.66 120 R(uf)-.2 E +.896(fer ag)-.25 F .895 +(ainst system crashes; after the message has been collected it may be reli-) +-.05 F(ably redeli)133.66 132 Q -.15(ve)-.25 G(red e).15 E -.15(ve)-.25 G 2.5 +(ni).15 G 2.5(ft)224.22 132 S(he system crashes during the initial deli)232.83 +132 Q -.15(ve)-.25 G(ry).15 E(.)-.65 E(\(6\))107 148.2 Q F2(Sendmail)133.66 +148.2 Q F1 .197(uses the netw)2.696 F .197(orking support pro)-.1 F .197 +(vided by 4.2BSD to pro)-.15 F .197(vide a direct interf)-.15 F .197(ace net-) +-.1 F -.1(wo)133.66 160.2 S .07(rks such as the ARP).1 F .07 +(ANET and/or Ethernet using SMTP \(the Simple Mail T)-.92 F .07(ransfer Proto-) +-.35 F(col\) o)133.66 172.2 Q -.15(ve)-.15 G 2.5(raT).15 G(CP/IP connection.) +184.73 172.2 Q F0 2.5(4.2. MMDF)87 196.2 R F1 .957(MMDF [Crock)127 212.4 R .957 +(er79] spans a wider problem set than)-.1 F F2(sendmail)3.458 E F1 5.958(.F)C +.958(or e)395.058 212.4 R .958(xample, the domain of)-.15 F .721 +(MMDF includes a \231phone netw)102 224.4 R .721(ork\232 mailer)-.1 F 3.221(,w) +-.4 G(hereas)290.516 224.4 Q F2(sendmail)3.221 E F1 .721(calls on pree)3.221 F +.72(xisting mailers in most)-.15 F(cases.)102 236.4 Q .175(MMDF and)127 252.6 R +F2(sendmail)2.675 E F1 .175 +(both support aliasing, customized mailers, message batching, automatic)2.675 F +(forw)102 264.6 Q .792(arding to g)-.1 F(ate)-.05 E -.1(wa)-.25 G .792 +(ys, queueing, and retransmission.).1 F .792(MMDF supports tw)5.792 F .792 +(o-stage timeout, which)-.1 F F2(sendmail)102 276.6 Q F1(does not support.)2.5 +E(The con\214guration for MMDF is compiled into the code)127 294.8 Q/F3 7 +/Times-Roman@0 SF(4)348.65 290.8 Q F1(.)352.15 294.8 Q .037 +(Since MMDF does not consider backw)127 311 R .037 +(ards compatibility as a design goal, the address parsing)-.1 F(is simpler b) +102 323 Q(ut much less \215e)-.2 E(xible.)-.15 E 1.159(It is some)127 341.2 R +1.159(what harder to inte)-.25 F 1.159(grate a ne)-.15 F 3.659(wc)-.25 G +(hannel)302.802 341.2 Q F3(5)329.462 337.2 Q F1 1.159(into MMDF)336.621 341.2 R +6.16(.I)-.8 G 3.66(np)397.59 341.2 S(articular)411.25 341.2 Q 3.66(,M)-.4 G +1.16(MDF must)459.22 341.2 R(kno)102 353.2 Q 3.225(wt)-.25 G .725(he location \ +and format of host tables for all channels, and the channel must speak a speci\ +al)129.975 353.2 R 2.525(protocol. This)102 365.2 R(allo)2.525 E .025 +(ws MMDF to do additional v)-.25 F .025(eri\214cation \(such as v)-.15 F .025 +(erifying host names\) at submis-)-.15 F(sion time.)102 377.2 Q 1.761 +(MMDF strictly separates the submission and deli)127 393.4 R -.15(ve)-.25 G +1.761(ry phases.).15 F(Although)6.761 E F2(sendmail)4.261 E F1 1.76(has the) +4.261 F .784(concept of each of these stages, the)102 405.4 R 3.284(ya)-.15 G +.784(re inte)260.068 405.4 R .785(grated into one program, whereas in MMDF the) +-.15 F 3.285(ya)-.15 G(re)496.23 405.4 Q(split into tw)102 417.4 Q 2.5(op)-.1 G +(rograms.)162.19 417.4 Q F0 2.5(4.3. Message)87 441.4 R(Pr)2.5 E +(ocessing Module)-.18 E F1 .925 +(The Message Processing Module \(MPM\) discussed by Postel [Postel79b] matches) +127 457.6 R F2(sendmail)3.425 E F1 1.364 +(closely in terms of its basic architecture.)102 469.6 R(Ho)6.364 E(we)-.25 E +-.15(ve)-.25 G 2.164 -.4(r, l).15 H(ik).4 E 3.864(eM)-.1 G(MDF)347.526 469.6 Q +3.864(,t)-.8 G 1.365(he MPM includes the netw)377.54 469.6 R(ork)-.1 E(interf) +102 481.6 Q(ace softw)-.1 E(are as part of its domain.)-.1 E .408 +(MPM also postulates a duple)127 497.8 R 2.907(xc)-.15 G .407 +(hannel to the recei)256.937 497.8 R -.15(ve)-.25 G 1.207 -.4(r, a).15 H 2.907 +(sd).4 G .407(oes MMDF)365.362 497.8 R 2.907(,t)-.8 G .407(hus allo)419.546 +497.8 R .407(wing simpler)-.25 F .302 +(handling of errors by the mailer than is possible in)102 509.8 R F2(sendmail) +2.802 E F1 5.302(.W)C .302(hen a message queued by)362.24 509.8 R F2(sendmail) +2.802 E F1 .23(is sent, an)102 521.8 R 2.73(ye)-.15 G .23 +(rrors must be returned to the sender by the mailer itself.)154.2 521.8 R .229 +(Both MPM and MMDF mail-)5.229 F .883(ers can return an immediate error respon\ +se, and a single error processor can create an appropriate)102 533.8 R +(response.)102 545.8 Q 2.24 +(MPM prefers passing the message as a structured object, with type-length-v)127 +564 R 2.24(alue tuples)-.25 F F3(6)498 560 Q F1(.)501.5 564 Q .874(Such a con) +102 576 R -.15(ve)-.4 G .874(ntion requires a much higher de).15 F .875 +(gree of cooperation between mailers than is required)-.15 F(by)102 588 Q F2 +(sendmail)2.796 E F1 5.296(.M)C .296(PM also assumes a uni)167.592 588 R -.15 +(ve)-.25 G .296(rsally agreed upon internet name space \(with each address).15 +F(in the form of a net-host-user tuple\), which)102 600 Q F2(sendmail)2.5 E F1 +(does not.)2.5 E .32 LW 76 642 72 642 DL 80 642 76 642 DL 84 642 80 642 DL 88 +642 84 642 DL 92 642 88 642 DL 96 642 92 642 DL 100 642 96 642 DL 104 642 100 +642 DL 108 642 104 642 DL 112 642 108 642 DL 116 642 112 642 DL 120 642 116 642 +DL 124 642 120 642 DL 128 642 124 642 DL 132 642 128 642 DL 136 642 132 642 DL +140 642 136 642 DL 144 642 140 642 DL 148 642 144 642 DL 152 642 148 642 DL 156 +642 152 642 DL 160 642 156 642 DL 164 642 160 642 DL 168 642 164 642 DL 172 642 +168 642 DL 176 642 172 642 DL 180 642 176 642 DL 184 642 180 642 DL 188 642 184 +642 DL 192 642 188 642 DL 196 642 192 642 DL 200 642 196 642 DL 204 642 200 642 +DL 208 642 204 642 DL 212 642 208 642 DL 216 642 212 642 DL/F4 5/Times-Roman@0 +SF(4)93.6 652.4 Q/F5 8/Times-Roman@0 SF .179 +(Dynamic con\214guration tables are currently being considered for MMDF; allo) +3.2 J .18(wing the installer to select either compiled or dy-)-.2 F +(namic tables.)72 665.2 Q F4(5)93.6 675.6 Q F5(The MMDF equi)3.2 I -.2(va)-.2 G +(lent of a).2 E/F6 8/Times-Italic@0 SF(sendmail)2 E F5(\231mailer)2 E -.56 +<2e9a>-.44 G F4(6)93.6 689.2 Q F5(This is similar to the NBS standard.)3.2 I EP +%%Page: 10 10 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 187.28(SMM:9-10 SENDMAIL)72 60 R 2.5<8a41>2.5 G 2.5(nI) +383.99 60 S(nter)395.94 60 Q(netw)-.15 E(ork Mail Router)-.1 E 2.5(5. EV)72 96 +R(ALU)-1.35 E -.95(AT)-.6 G(IONS AND FUTURE PLANS).95 E/F1 10/Times-Italic@0 SF +(Sendmail)112 112.2 Q/F2 10/Times-Roman@0 SF 1.851(is designed to w)4.351 F +1.851(ork in a nonhomogeneous en)-.1 F 4.352(vironment. Ev)-.4 F 1.852 +(ery attempt is made to)-.15 F -.2(avo)87 124.2 S 1.037 +(id imposing unnecessary constraints on the underlying mailers.).2 F 1.036 +(This goal has dri)6.036 F -.15(ve)-.25 G 3.536(nm).15 G 1.036(uch of the) +461.938 124.2 R 2.723(design. One)87 136.2 R .223(of the major problems has be\ +en the lack of a uniform address space, as postulated in [Pos-)2.723 F +(tel79a] and [Postel79b].)87 148.2 Q 2.647(An)112 164.4 S .147(onuniform addre\ +ss space implies that a path will be speci\214ed in all addresses, either e) +126.867 164.4 R(xplicitly)-.15 E .472 +(\(as part of the address\) or implicitly \(as with implied forw)87 176.4 R +.473(arding to g)-.1 F(ate)-.05 E -.1(wa)-.25 G 2.973(ys\). This).1 F .473 +(restriction has the)2.973 F .493(unpleasant ef)87 188.4 R .493 +(fect of making replying to messages e)-.25 F .493(xceedingly dif)-.15 F .493 +(\214cult, since there is no one \231address\232)-.25 F(for an)87 200.4 Q 2.5 +(yp)-.15 G(erson, b)122.95 200.4 Q(ut only a w)-.2 E +(ay to get there from where)-.1 E -.15(ve)-.25 G 2.5(ry).15 G(ou are.)324.7 +200.4 Q(Interf)112 216.6 Q .448(acing to mail programs that were not initially\ + intended to be applied in an internet en)-.1 F(viron-)-.4 E(ment has been ama\ +zingly successful, and has reduced the job to a manageable task.)87 228.6 Q F1 +(Sendmail)112 244.8 Q F2 2.906(has kno)5.406 F 2.906(wledge of a fe)-.25 F +5.406(wd)-.25 G(if)271.126 244.8 Q 2.906(\214cult en)-.25 F 2.906(vironments b) +-.4 F 2.906(uilt in.)-.2 F 2.905(It generates ARP)7.906 F(ANET)-.92 E .648(FTP\ +/SMTP compatible error messages \(prepended with three-digit numbers [Neigus73\ +, Postel74, Pos-)87 256.8 R .771(tel82]\) as necessary)87 268.8 R 3.271(,o)-.65 +G .771(ptionally generates UNIX-style \231From\232 lines on the front of messa\ +ges for some)177.523 268.8 R 1.669(mailers, and kno)87 280.8 R 1.669(ws ho)-.25 +F 4.169(wt)-.25 G 4.169(op)195.666 280.8 S 1.669(arse the same lines on input.) +209.835 280.8 R 1.67(Also, error handling has an option cus-)6.67 F +(tomized for BerkNet.)87 292.8 Q 1.482(The decision to a)112 309 R -.2(vo)-.2 G +1.482(id doing an).2 F 3.982(yt)-.15 G 1.481(ype of deli)254.222 309 R -.15(ve) +-.25 G 1.481(ry where possible \(e).15 F -.15(ve)-.25 G 1.481 +(n, or perhaps especially).15 F(,)-.65 E .574(local deli)87 321 R -.15(ve)-.25 +G .574(ry\) has turned out to be a good idea.).15 F(Ev)5.574 E .574 +(en with local deli)-.15 F -.15(ve)-.25 G(ry).15 E 3.074(,t)-.65 G .575 +(here are issues of the loca-)394.776 321 R .469(tion of the mailbox, the form\ +at of the mailbox, the locking protocol used, etc., that are best decided by)87 +333 R .038(other programs.)87 345 R .038(One surprisingly major anno)5.038 F +.038(yance in man)-.1 F 2.538(yi)-.15 G .038 +(nternet mailers is that the location and for)333.684 345 R(-)-.2 E .138 +(mat of local mail is b)87 357 R .138(uilt in.)-.2 F .137 +(The feeling seems to be that local mail is so common that it should be ef) +5.137 F<8c2d>-.25 E 3.045(cient. This)87 369 R .545 +(feeling is not born out by our e)3.045 F .545(xperience; on the contrary)-.15 +F 3.045(,t)-.65 G .545(he location and format of mail-)376.575 369 R(box)87 381 +Q(es seems to v)-.15 E(ary widely from system to system.)-.25 E .681 +(The ability to automatically generate a response to incoming mail \(by forw) +112 397.2 R .68(arding mail to a pro-)-.1 F .435 +(gram\) seems useful \(\231I am on v)87 409.2 R .435 +(acation until late August....)-.25 F 2.935(\232\) b)-.7 F .435 +(ut can create problems such as forw)-.2 F(ard-)-.1 E .143(ing loops \(tw)87 +421.2 R 2.643(op)-.1 G .143(eople on v)152.609 421.2 R .143(acation whose prog\ +rams send notes back and forth, for instance\) if these pro-)-.25 F .732 +(grams are not well written.)87 433.2 R 3.232(Ap)5.732 G .732 +(rogram could be written to do standard tasks correctly)218.592 433.2 R 3.233 +(,b)-.65 G .733(ut this w)450.404 433.2 R(ould)-.1 E(solv)87 445.2 Q 2.5(et) +-.15 G(he general case.)113.24 445.2 Q .225 +(It might be desirable to implement some form of load limiting.)112 461.4 R +2.725(Ia)5.225 G 2.724(mu)380.8 461.4 S(na)396.304 461.4 Q -.1(wa)-.15 G .224 +(re of an).1 F 2.724(ym)-.15 G .224(ail system)463.496 461.4 R +(that addresses this problem, nor am I a)87 473.4 Q -.1(wa)-.15 G(re of an).1 E +2.5(yr)-.15 G(easonable solution at this time.)294.05 473.4 Q .113(The con\214\ +guration \214le is currently practically inscrutable; considerable con)112 +489.6 R -.15(ve)-.4 G .114(nience could be real-).15 F(ized with a higher)87 +501.6 Q(-le)-.2 E -.15(ve)-.25 G 2.5(lf).15 G(ormat.)186.93 501.6 Q .778(It se\ +ems clear that common protocols will be changing soon to accommodate changing \ +require-)112 517.8 R 2.774(ments and en)87 529.8 R 5.274(vironments. These)-.4 +F 2.774(changes will include modi\214cations to the message header \(e.g.,) +5.274 F .859([NBS80]\) or to the body of the message itself \(such as for mult\ +imedia messages [Postel80]\).)87 541.8 R(Experi-)5.859 E +(ence indicates that these changes should be relati)87 553.8 Q -.15(ve)-.25 G +(ly tri).15 E(vial to inte)-.25 E(grate into the e)-.15 E(xisting system.)-.15 +E .811(In tightly coupled en)112 570 R .812(vironments, it w)-.4 F .812 +(ould be nice to ha)-.1 F 1.112 -.15(ve a n)-.2 H .812(ame serv).15 F .812 +(er such as Grapvine [Bir)-.15 F(-)-.2 E .095(rell82] inte)87 582 R .095 +(grated into the mail system.)-.15 F .095(This w)5.095 F .095(ould allo)-.1 F +2.594(was)-.25 G .094(ite such as \231Berk)330.768 582 R(ele)-.1 E .094 +(y\232 to appear as a single)-.15 F 2.606 +(host, rather than as a collection of hosts, and w)87 594 R 2.606(ould allo)-.1 +F 5.106(wp)-.25 G 2.606(eople to mo)352.786 594 R 2.906 -.15(ve t)-.15 H 2.606 +(ransparently among).15 F 1.664(machines without ha)87 606 R 1.664 +(ving to change their addresses.)-.2 F 1.664(Such a f)6.664 F 1.664(acility w) +-.1 F 1.663(ould require an automatically)-.1 F .428 +(updated database and some method of resolving con\215icts.)87 618 R .428 +(Ideally this w)5.428 F .428(ould be ef)-.1 F(fecti)-.25 E .728 -.15(ve e)-.25 +H -.15(ve)-.1 G 2.928(nw).15 G(ithout)480.66 618 Q .184 +(all hosts being under a single management.)87 630 R(Ho)5.184 E(we)-.25 E -.15 +(ve)-.25 G .984 -.4(r, i).15 H 2.684(ti).4 G 2.683(sn)317.576 630 S .183 +(ot clear whether this feature should be inte-)329.149 630 R +(grated into the aliasing f)87 642 Q(acility or should be considered a \231v) +-.1 E(alue added\232 feature outside)-.25 E F1(sendmail)2.5 E F2(itself.)2.5 E +.79(As a more interesting case, the CSNET name serv)112 658.2 R .791 +(er [Solomon81] pro)-.15 F .791(vides an f)-.15 F .791(acility that goes)-.1 F +(be)87 670.2 Q .375(yond a single tightly-coupled en)-.15 F 2.875 +(vironment. Such)-.4 F 2.875(af)2.875 G .375(acility w)308.675 670.2 R .374 +(ould normally e)-.1 F .374(xist outside of)-.15 F F1(sendmail)2.874 E F2(ho)87 +682.2 Q(we)-.25 E -.15(ve)-.25 G -.55(r.).15 G EP +%%Page: 11 11 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(SENDMAIL \212 An Inter)72 60 Q(netw)-.15 E +(ork Mail Router)-.1 E(SMM:9-11)457.9 60 Q -.55(AC)72 96 S(KNO).55 E +(WLEDGEMENTS)-.5 E/F1 10/Times-Roman@0 SF 1.203(Thanks are due to K)97 112.2 R +1.204 +(urt Shoens for his continual cheerful assistance and good advice, Bill Jo)-.15 +F 3.704(yf)-.1 G(or)495.67 112.2 Q .102 +(pointing me in the correct direction \(o)72 124.2 R -.15(ve)-.15 G 2.602(ra) +.15 G .102(nd o)244.324 124.2 R -.15(ve)-.15 G .102 +(r\), and Mark Horton for more advice, prodding, and man).15 F(y)-.15 E .453 +(of the good ideas.)72 136.2 R -.15(Ku)5.453 G .453 +(rt and Eric Schmidt are to be credited for using).15 F/F2 10/Times-Italic@0 SF +(delivermail)2.953 E F1 .453(as a serv)2.953 F .453(er for their pro-)-.15 F +1.663(grams \()72 148.2 R F2(Mail)A F1 1.663(and BerkNet respecti)4.163 F -.15 +(ve)-.25 G 1.663(ly\) before an).15 F 4.163(ys)-.15 G 1.663 +(ane person should ha)291.091 148.2 R -.15(ve)-.2 G 4.163(,a).15 G 1.662 +(nd making the necessary)400.423 148.2 R .078 +(modi\214cations promptly and happily)72 160.2 R 5.078(.E)-.65 G .078(ric g) +228.332 160.2 R -2.25 -.2(av e)-.05 H .079 +(me considerable advice about the perils of netw)2.778 F .079(ork softw)-.1 F +(are)-.1 E .179(which sa)72 172.2 R -.15(ve)-.2 G 2.679(dm).15 G 2.679(ea) +131.998 172.2 S 2.679(nu)143.557 172.2 S(nkno)156.236 172.2 Q .178 +(wn amount of w)-.25 F .178(ork and grief.)-.1 F .178 +(Mark did the original implementation of the DBM)5.178 F -.15(ve)72 184.2 S +.341(rsion of aliasing, installed the VFORK code, wrote the current v).15 F +.341(ersion of)-.15 F F2(rmail)2.841 E F1 2.841(,a)C .341(nd w)411.083 184.2 R +.342(as the person who)-.1 F .61(really con)72 196.2 R .61 +(vinced me to put the w)-.4 F .61(ork into)-.1 F F2(delivermail)3.109 E F1 .609 +(to turn it into)3.109 F F2(sendmail)3.109 E F1 5.609(.K)C .609(urt deserv) +398.753 196.2 R .609(es accolades for)-.15 F(using)72 208.2 Q F2(sendmail)2.57 +E F1 .07(when I w)2.57 F .07(as myself afraid to tak)-.1 F 2.57(et)-.1 G .07 +(he risk; ho)271.01 208.2 R 2.57(wap)-.25 G .07 +(erson can continue to be so enthusiastic in)334.92 208.2 R(the f)72 220.2 Q +(ace of so much bitter reality is be)-.1 E(yond me.)-.15 E -.15(Ku)97 236.4 S +1.505(rt, Mark, Kirk McK).15 F 1.505(usick, Marvin Solomon, and man)-.15 F +4.005(yo)-.15 G 1.504(thers ha)345.79 236.4 R 1.804 -.15(ve r)-.2 H -.25(ev).15 +G(ie).25 E 1.504(wed this paper)-.25 F 4.004(,g)-.4 G -.25(iv)483.69 236.4 S +(ing).25 E(considerable useful advice.)72 248.4 Q .846 +(Special thanks are reserv)97 264.6 R .846(ed for Mik)-.15 F 3.346(eS)-.1 G +(tonebrak)256.786 264.6 Q .846(er at Berk)-.1 F(ele)-.1 E 3.347(ya)-.15 G .847 +(nd Bob Epstein at Britton-Lee, who)356.995 264.6 R .542(both kno)72 276.6 R +.542(wingly allo)-.25 F .542(wed me to put so much w)-.25 F .541 +(ork into this project when there were so man)-.1 F 3.041(yo)-.15 G .541 +(ther things I)454.588 276.6 R(really should ha)72 288.6 Q .3 -.15(ve b)-.2 H +(een w).15 E(orking on.)-.1 E EP +%%Page: 12 12 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF(REFERENCES)256.605 132 Q 62.73([Birrell82] Birrell,)72 +148.2 R 1.084(A. D., Le)3.584 F 1.084(vin, R., Needham, R. M., and Schroeder) +-.25 F 3.584(,M)-.4 G 3.585(.D)433.49 148.2 S 1.085(., \231Grape)446.795 148.2 +R(vine:)-.25 E(An Ex)180 160.2 Q(ercise in Distrib)-.15 E(uted Computing.)-.2 E +5<9a49>-.7 G(n)348.66 160.2 Q/F1 10/Times-Italic@0 SF(Comm. A.C.M. 25,)2.5 E F0 +(4, April 82.)2.5 E 59.4([Borden79] Borden,)72 176.4 R .796 +(S., Gaines, R. S., and Shapiro, N. Z.,)3.296 F F1 .795(The MH Messa)3.295 F +.995 -.1(ge H)-.1 H .795(andling Sys-).1 F(tem: User)180 188.4 Q(s' Manual.)-.1 +E F0(R-2367-P)5 E(AF)-.92 E 5(.R)-.8 G(and Corporation.)332.06 188.4 Q +(October 1979.)5 E([Crock)72 204.6 Q 52.29(er77a] Crock)-.1 F(er)-.1 E 2.508 +(,D)-.4 G 2.508(.H)223.938 204.6 S .008(., V)236.166 204.6 R .009 +(ittal, J. J., Pogran, K. T)-.6 F .009(., and Henderson, D. A. Jr)-.74 F(.,) +-.55 E F1(Standar)2.509 E 2.509(df)-.37 G(or)495.11 204.6 Q .955(the F)180 +216.6 R .955(ormat of ARP)-1.05 F 3.454(AN)-.9 G .954(etwork T)272.978 216.6 R +-.2(ex)-.92 G 3.454(tM).2 G(essa)331.536 216.6 Q -.1(ge)-.1 G(s.).1 E F0 .954 +(RFC 733, NIC 41952.)5.954 F .954(In [Fein-)5.954 F 2.5(ler78]. No)180 228.6 R +-.15(ve)-.15 G(mber 1977.).15 E([Crock)72 244.8 Q 51.73(er77b] Crock)-.1 F(er) +-.1 E 3.04(,D)-.4 G 3.04(.H)224.47 244.8 S(.,)237.23 244.8 Q F1 -1.55 -.55 +(Fr a)3.04 H(me).55 E .54(work and Functions of the MS P)-.15 F(er)-.8 E .54 +(sonal Messa)-.1 F .74 -.1(ge S)-.1 H(ystem.).1 E F0(R-2134-ARP)180 256.8 Q +(A, Rand Corporation, Santa Monica, California.)-.92 E(1977.)5 E([Crock)72 273 +Q 56.73(er79] Crock)-.1 F(er)-.1 E 2.557(,D)-.4 G 2.557(.H)223.987 273 S .056 +(., Szurk)236.264 273 R -.25(ow)-.1 G .056(ski, E. S., and F).25 F(arber)-.15 E +2.556(,D)-.4 G 2.556(.J)374.85 273 S(.,)383.796 273 Q F1 .056 +(An Internetwork Memo Dis-)2.556 F(trib)180 285 Q 1.341(ution F)-.2 F 1.341 +(acility \212 MMDF)-.75 F(.)-1.35 E F0 1.341 +(6th Data Communication Symposium, Asilomar)6.341 F(.)-.55 E(No)180 297 Q -.15 +(ve)-.15 G(mber 1979.).15 E([Crock)72 313.2 Q 56.73(er82] Crock)-.1 F(er)-.1 E +3.383(,D)-.4 G 3.383(.H)224.813 313.2 S(.,)237.916 313.2 Q F1(Standar)3.383 E +3.383(df)-.37 G .883(or the F)288.762 313.2 R .882(ormat of Arpa Internet T) +-1.05 F -.2(ex)-.92 G 3.382(tM).2 G(essa)446.368 313.2 Q -.1(ge)-.1 G(s.).1 E +F0(RFC)5.882 E 4.197(822. Netw)180 325.2 R 1.697(ork Information Center)-.1 F +4.197(,S)-.4 G 1.698(RI International, Menlo P)333.768 325.2 R 1.698 +(ark, California.)-.15 F(August 1982.)180 337.2 Q 53.3([Metcalfe76] Metcalfe,) +72 353.4 R .727(R., and Boggs, D., \231Ethernet: Distrib)3.227 F .727(uted P) +-.2 F(ack)-.15 E .727(et Switching for Local)-.1 F(Computer Netw)180 365.4 Q +(orks\232,)-.1 E F1(Communications of the A)2.5 E(CM 19,)-.3 E F0 2.5(7. July) +2.5 F(1976.)2.5 E 60.51([Feinler78] Feinler)72 381.6 R 4.438(,E)-.4 G 1.938 +(., and Postel, J.)220.978 381.6 R(\(eds.\),)6.938 E F1(ARP)4.438 E 1.938 +(ANET Pr)-.9 F 1.938(otocol Handbook.)-.45 F F0 1.938(NIC 7104,)6.938 F(Netw) +180 393.6 Q(ork Information Center)-.1 E 2.5(,S)-.4 G +(RI International, Menlo P)304.48 393.6 Q(ark, California.)-.15 E(1978.)5 E +69.39([NBS80] National)72 409.8 R 1.46(Bureau of Standards,)3.96 F F1 1.46 +(Speci\214cation of a Dr)3.96 F 1.46(aft Messa)-.15 F 1.66 -.1(ge F)-.1 H 1.46 +(ormat Stan-)-.95 F(dar)180 421.8 Q(d.)-.37 E F0(Report No. ICST/CBOS 80-2.)5 E +(October 1980.)5 E 60.51([Neigus73] Neigus,)72 438 R(N.,)5.186 E F1 -.45(Fi) +5.186 G 2.686(le T).45 F -.15(ra)-.55 G 2.686(nsfer Pr).15 F 2.686 +(otocol for the ARP)-.45 F 5.187(AN)-.9 G(etwork.)402.599 438 Q F0 2.687 +(RFC 542, NIC)7.687 F 2.5(17759. In)180 450 R 2.5([Feinler78]. August,)2.5 F +(1973.)2.5 E([No)72 466.2 Q 55.21(witz78a] No)-.25 F 1.633 +(witz, D. A., and Lesk, M. E.,)-.25 F F1 4.132(AD)4.132 G 1.632 +(ial-Up Network of UNIX Systems.)338.9 466.2 R F0(Bell)6.632 E 5.403 +(Laboratories. In)180 478.2 R 2.904(UNIX Programmer')5.403 F 5.404(sM)-.55 G +2.904(anual, Se)356.024 478.2 R -.15(ve)-.25 G 2.904(nth Edition, V).15 F 2.904 +(olume 2.)-1.29 F(August, 1978.)180 490.2 Q([No)72 506.4 Q 54.65(witz78b] No) +-.25 F .633(witz, D. A.,)-.25 F F1 .632(Uucp Implementation Description.)3.132 +F F0 .632(Bell Laboratories.)5.632 F .632(In UNIX)5.632 F(Programmer')180 518.4 +Q 2.5(sM)-.55 G(anual, Se)248.05 518.4 Q -.15(ve)-.25 G(nth Edition, V).15 E +(olume 2.)-1.29 E(October)5 E 2.5(,1)-.4 G(978.)431.22 518.4 Q 64.39 +([Postel74] Postel,)72 534.6 R .24(J., and Neigus, N., Re)2.74 F .241 +(vised FTP Reply Codes.)-.25 F .241(RFC 640, NIC 30843.)5.241 F(In)5.241 E 2.5 +([Feinler78]. June,)180 546.6 R(1974.)2.5 E 64.39([Postel77] Postel,)72 562.8 R +(J.,)2.5 E F1(Mail Pr)2.5 E(otocol.)-.45 E F0(NIC 29588.)5 E(In [Feinler78].)5 +E(No)5 E -.15(ve)-.15 G(mber 1977.).15 E 59.95([Postel79a] Postel,)72 579 R +(J.,)3.144 E F1 .644(Internet Messa)3.144 F .844 -.1(ge P)-.1 H -.45(ro).1 G +(tocol.).45 E F0 .644(RFC 753, IEN 85.)5.644 F(Netw)5.644 E .644 +(ork Information)-.1 F(Center)180 591 Q 2.5(,S)-.4 G(RI International, Menlo P) +216.82 591 Q(ark, California.)-.15 E(March 1979.)5 E 59.39([Postel79b] Postel,) +72 607.2 R 1.305(J. B.,)3.805 F F1 1.305(An Internetwork Messa)3.805 F 1.505 +-.1(ge S)-.1 H(tructur).1 E -.15(e.)-.37 G F0(In)6.456 E F1(Pr)3.806 E 1.306 +(oceedings of the Sixth)-.45 F(Data Communications Symposium,)180 619.2 Q F0 +2.5(IEEE. Ne)2.5 F 2.5(wY)-.25 G 2.5(ork. No)379.74 619.2 R -.15(ve)-.15 G +(mber 1979.).15 E 64.39([Postel80] Postel,)72 635.4 R .639(J. B.,)3.139 F F1 +3.139(AS)3.139 G(tructur)248.676 635.4 Q .639(ed F)-.37 F .639(ormat for T) +-1.05 F -.15(ra)-.55 G .639(nsmission of Multi-Media Documents.).15 F F0 .418 +(RFC 767.)180 647.4 R(Netw)5.419 E .419(ork Information Center)-.1 F 2.919(,S) +-.4 G .419(RI International, Menlo P)350.474 647.4 R .419(ark, Califor)-.15 F +(-)-.2 E 2.5(nia. August)180 659.4 R(1980.)2.5 E 64.39([Postel82] Postel,)72 +675.6 R 2.05(J. B.,)4.55 F F1 2.05(Simple Mail T)4.55 F -.15(ra)-.55 G 2.05 +(nsfer Pr).15 F(otocol.)-.45 E F0 2.05(RFC821 \(obsoleting RFC788\).)7.05 F +(Netw)180 687.6 Q .273(ork Information Center)-.1 F 2.774(,S)-.4 G .274 +(RI International, Menlo P)305.3 687.6 R .274(ark, California.)-.15 F(August) +5.274 E(1982.)180 699.6 Q/F2 10/Times-Bold@0 SF 187.28(SMM:9-12 SENDMAIL)72 756 +R 2.5<8a41>2.5 G 2.5(nI)383.99 756 S(nter)395.94 756 Q(netw)-.15 E +(ork Mail Router)-.1 E EP +%%Page: 13 13 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(SENDMAIL \212 An Inter)72 60 Q(netw)-.15 E +(ork Mail Router)-.1 E(SMM:9-13)457.9 60 Q/F1 10/Times-Roman@0 SF 55.5 +([Schmidt79] Schmidt,)72 96 R(E.,)2.972 E/F2 10/Times-Italic@0 SF .472(An Intr) +2.972 F .472(oduction to the Berk)-.45 F(ele)-.1 E 2.972(yN)-.3 G(etwork.) +369.664 96 Q F1(Uni)5.472 E -.15(ve)-.25 G .472(rsity of California,).15 F +(Berk)180 108 Q(ele)-.1 E 2.5(yC)-.15 G 2.5(alifornia. 1979.)225.02 108 R 59.95 +([Shoens79] Shoens,)72 124.2 R(K.,)4.894 E F2 2.394(Mail Refer)4.894 F 2.394 +(ence Manual.)-.37 F F1(Uni)7.394 E -.15(ve)-.25 G 2.395 +(rsity of California, Berk).15 F(ele)-.1 E 6.195 -.65(y. I)-.15 H(n).65 E +(UNIX Programmer')180 136.2 Q 2.5(sM)-.55 G(anual, Se)275.54 136.2 Q -.15(ve) +-.25 G(nth Edition, V).15 E(olume 2C.)-1.29 E(December 1979.)5 E 60.51 +([Sluizer81] Sluizer)72 152.4 R 2.872(,S)-.4 G .372(., and Postel, J. B.,) +218.862 152.4 R F2 .372(Mail T)2.872 F -.15(ra)-.55 G .372(nsfer Pr).15 F +(otocol.)-.45 E F1 .371(RFC 780.)5.371 F(Netw)5.371 E .371(ork Infor)-.1 F(-) +-.2 E(mation Center)180 164.4 Q 2.5(,S)-.4 G(RI International, Menlo P)247.1 +164.4 Q(ark, California.)-.15 E(May 1981.)5 E 52.72([Solomon81] Solomon,)72 +180.6 R .96(M., Landweber)3.46 F 3.46(,L)-.4 G .96 +(., and Neuhengen, D., \231The Design of the CSNET)296.08 180.6 R(Name Serv)180 +192.6 Q(er)-.15 E 3.9 -.7(.\232 C)-.55 H(S-DN-2, Uni).7 E -.15(ve)-.25 G +(rsity of W).15 E(isconsin, Madison.)-.4 E(No)5 E -.15(ve)-.15 G(mber 1981.).15 +E 78.28([Su82] Su,)72 208.8 R(Za)4.344 E 1.844(w-Sing, and Postel, Jon,)-.15 F +F2 1.844(The Domain Naming Con)4.344 F 1.844(vention for Internet)-.4 F 1.717 +(User Applications.)180 220.8 R F1 4.217(RFC819. Netw)6.717 F 1.717 +(ork Information Center)-.1 F 4.217(,S)-.4 G 1.718(RI International,)436.182 +220.8 R(Menlo P)180 232.8 Q(ark, California.)-.15 E(August 1982.)5 E([UNIX83]) +72 249 Q F2 2.12(The UNIX Pr)180 249 R -.1(og)-.45 G -.15(ra).1 G(mmer').15 E +4.62(sM)-.4 G 2.12(anual, Se)298.3 249 R 2.12(venth Edition,)-.15 F F1 -.6(Vi) +4.62 G 2.12(rtual V).6 F 2.12(AX-11 V)-1.35 F(ersion,)-1.11 E -1.29(Vo)180 261 +S 1.027(lume 1.)1.29 F 1.027(Bell Laboratories, modi\214ed by the Uni)6.027 F +-.15(ve)-.25 G 1.027(rsity of California, Berk).15 F(e-)-.1 E(le)180 273 Q 1.3 +-.65(y, C)-.15 H 2.5(alifornia. March,).65 F(1983.)2.5 E EP +%%Trailer +end +%%EOF diff --git a/usr.sbin/sendmail/doc/op/op.me b/usr.sbin/sendmail/doc/op/op.me new file mode 100644 index 0000000000..5a747c52ec --- /dev/null +++ b/usr.sbin/sendmail/doc/op/op.me @@ -0,0 +1,6567 @@ +.\" Copyright (c) 1983 Eric P. Allman +.\" Copyright (c) 1983, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)op.me 8.2 (Berkeley) 7/11/93 +.\" +.\" eqn op.me | pic | troff -me +.eh 'SMM:08-%''Sendmail Installation and Operation Guide' +.oh 'Sendmail Installation and Operation Guide''SMM:08-%' +.\" SD is lib if sendmail is installed in /usr/lib, sbin if in /usr/sbin +.ds SD sbin +.nr si 3n +.de $0 +.(x +.in \\$3u*3n +.ti -3n +\\$2. \\$1 +.)x +.. +.de $C +.(x +.in 0 +\\$1 \\$2. \\$3 +.)x +.. +.+c +.(l C +.sz 16 +.b SENDMAIL +.sz 12 +.sp +.b "INSTALLATION AND OPERATION GUIDE" +.sz 10 +.sp +.r +Eric Allman +University of California, Berkeley +Mammoth Project +eric@CS.Berkeley.EDU +.sp +Version 8.2 +.sp +For Sendmail Version 8.2 +.)l +.sp 2 +.pp +.i Sendmail +implements a general purpose internetwork mail routing facility +under the UNIX* +.(f +*UNIX is a trademark of Bell Laboratories. +.)f +operating system. +It is not tied to any one transport protocol \*- +its function may be likened to a crossbar switch, +relaying messages from one domain into another. +In the process, +it can do a limited amount of message header editing +to put the message into a format that is appropriate +for the receiving domain. +All of this is done under the control of a configuration file. +.pp +Due to the requirements of flexibility +for +.i sendmail , +the configuration file can seem somewhat unapproachable. +However, there are only a few basic configurations +for most sites, +for which standard configuration files have been supplied. +Most other configurations +can be built by adjusting an existing configuration files +incrementally. +.pp +.i Sendmail +is based on +RFC822 (Internet Mail Format Protocol), +RFC821 (Simple Mail Transport Protocol), +RFC1123 (Internet Host Requirements), +and +RFC1425 (SMTP Service Extensions). +However, since +.i sendmail +is designed to work in a wider world, +in many cases it can be configured to exceed these protocols. +These cases are described herein. +.pp +Although +.i sendmail +is intended to run +without the need for monitoring, +it has a number of features +that may be used to monitor or adjust the operation +under unusual circumstances. +These features are described. +.pp +Section one describes how to do a basic +.i sendmail +installation. +Section two +explains the day-to-day information you should know +to maintain your mail system. +If you have a relatively normal site, +these two sections should contain sufficient information +for you to install +.i sendmail +and keep it happy. +Section three +describes some parameters that may be safely tweaked. +Section four +has information regarding the command line arguments. +Section five +contains the nitty-gritty information about the configuration +file. +This section is for masochists +and people who must write their own configuration file. +Section six +gives a brief description of differences +in this version of +.i sendmail . +The appendixes give a brief +but detailed explanation of a number of features +not described in the rest of the paper. +.bp 5 +.sh 1 "BASIC INSTALLATION" +.pp +There are two basic steps to installing sendmail. +The hard part is to build the configuration table. +This is a file that sendmail reads when it starts up +that describes the mailers it knows about, +how to parse addresses, +how to rewrite the message header, +and the settings of various options. +Although the configuration table is quite complex, +a configuration can usually be built +by adjusting an existing off-the-shelf configuration. +The second part is actually doing the installation, +i.e., creating the necessary files, etc. +.pp +The remainder of this section will describe the installation of sendmail +assuming you can use one of the existing configurations +and that the standard installation parameters are acceptable. +All pathnames and examples +are given from the root of the +.i sendmail +subtree, +normally +.i /usr/src/usr.\*(SD/sendmail +on 4.4BSD. +.pp +If you are loading this off the tape, +continue with the next session. +If you have a running binary already on your system, +you should probably skip to section 1.2. +.sh 2 "Compiling Sendmail" +.pp +All sendmail source is in the +.i src +subdirectory. +If you are running on a 4.4BSD system, +compile by typing +.q make . +On other systems, you may have to make some other adjustments. +.sh 3 "Old versions of make" +.pp +If you are not running the new version of +.b make +you will probably have to use +.(b +make \-f Makefile.dist +.)b +This file does not assume several new syntaxes, +including the +.q += +syntax in macro definition +and the +.q ".include" +syntax. +.sh 3 "Compilation flags" +.pp +.i Sendmail +supports two different formats +for the +.i aliases +database. +These formats are: +.nr ii 1i +.ip NDBM +The ``new DBM'' format, +available on nearly all systems around today. +This was the preferred format prior to 4.4BSD. +It allows such complex things as multiple databases +and closing a currently open database. +.ip NEWDB +The new database package from Berkeley. +If you have this, use it. +It allows +long records, +multiple open databases, +real in-memory caching, +and so forth. +You can define this in conjunction with one of the other two; +if you do, +old databases are read, +but when a new database is created it will be in NEWDB format. +As a nasty hack, +if you have NEWDB, NDBM, and YPCOMPAT defined, +and if the file +.i /var/yp/Makefile +exists and is readable, +.i sendmail +will create both new and old versions of the alias file +during a +.i newalias +command. +This is required because the Sun NIS/YP system +reads the DBM version of the alias file. +It's ugly as sin, +but it works. +.lp +If neither of these are defined, +.i sendmail +reads the alias file into memory on every invocation. +This can be slow and should be avoided. +.pp +System V based systems can define +SYSTEM5 +to make several small adjustments. +This changes the handling of timezones +and uses the much less efficient +.i lockf +call in preference to +.i flock . +These can be specified separately using the compilation flags +SYS5TZ +and +LOCKF +respectively. +.pp +If you don't have the +.i unsetenv +routine in your system library, define the UNSETENV compilation flag. +.pp +You may also have to define the compilation variable LA_TYPE +to describe how your load average is computed. +This and other flags are detailed in section 6.1. +.sh 3 "Compilation and installation" +.pp +After making the local system configuration described above, +You should be able to compile and install the system. +Compilation can be performed using +.q make\** +.(f +\**where you may have to replace +.q make +with +.q "make \-f Makefile.dist" +as appropriate. +.)f +in the +.b sendmail/src +directory. +You may be able to install using +.(b +make install +.)b +This should install the binary in +/usr/\*(SD +and create links from +/usr/bin/newaliases +and +/usr/bin/mailq +to +/usr/\*(SD/sendmail. +On 4.4BSD systems it will also format and install man pages. +.sh 2 "Configuration Files" +.pp +.i Sendmail +cannot operate without a configuration file. +The configuration defines the mail systems understood at this site, +how to access them, +how to forward email to remote mail systems, +and a number of tuning parameters. +This configuration file is detailed +in the later portion of this document. +.pp +The +.i sendmail +configuration can be daunting at first. +The world is complex, +and the mail configuration reflects that. +The distribution includes an m4-based configuration package +that hides a lot of the complexity. +.pp +These configuration files are simpler than old versions +largely because the world has become simpler; +in particular, +text-based host files are officially eliminated, +obviating the need to +.q hide +hosts behind a registered internet gateway. +.pp +These files also assume that most of your neighbors +use domain-based UUCP addressing; +that is, +instead of naming hosts as +.q host!user +they will use +.q host.domain!user . +The configuration files can be customized to work around this, +but it is more complex. +.pp +I haven't tested these yet on an isolated LAN environment +with a single UUCP connection to the outside world. +If you are in such an environment, +please send comments to +sendmail@okeeffe.CS.Berkeley.EDU. +.pp +Our configuration files are processed by +.i m4 +to facilitate local customization; +the directory +.i cf +of the +sendmail +distribution directory +contains the source files. +This directory contains several subdirectories: +.nr ii 1i +.ip cf +Both site-dependent and site-independent descriptions of hosts. +These can be literal host names +(e.g., +.q ucbvax.mc ) +when the hosts are gateways +or more general descriptions +(such as +.q "tcpproto.mc" +as a general description of an SMTP-connected host +or +.q "uucpproto.mc" +as a general description of a UUCP-connected host). +Files ending +.b \&.mc +(``Master Configuration'') +are the input descriptions; +the output is in the corresponding +.b \&.cf +file. +The general structure of these files is described below. +.ip domain +Site-dependent subdomain descriptions. +These are tied to the way your organization wants to do addressing. +For example, +.b domain/cs.exposed.m4 +is our description for hosts in the CS.Berkeley.EDU subdomain +that want their individual hostname to be externally visible; +.b domain/cs.hidden.m4 +is the same except that the hostname is hidden +(everything looks like it comes from CS.Berkeley.EDU). +These are referenced using the +.sm DOMAIN +.b m4 +macro in the +.b \&.mc +file. +.ip feature +Definitions of specific features that some particular host in your site +might want. +These are referenced using the +.sm FEATURE +.b m4 +macro. +An example feature is +use_cw_file +(which tells sendmail to read an /etc/sendmail.cw file on startup +to find the set of local names). +.ip hack +Local hacks, referenced using the +.sm HACK +.b m4 +macro. +Try to avoid these. +The point of having them here is to make it clear that they smell. +.ip m4 +Site-independent +.i m4 (1) +include files that have information common to all configuration files. +This can be thought of as a +.q #include +directory. +.ip mailer +Definitions of mailers, +referenced using the +.sm MAILER +.b m4 +macro. +Defined mailer types in this distribution are +fax, +local, +smtp, +uucp, +and usenet. +.ip ostype +Definitions describing various operating system environments +(such as the location of support files). +These are referenced using the +.sm OSTYPE +.b m4 +macro. +.ip sh +Shell files used by the +.b m4 +build process. +You shouldn't have to mess with these. +.ip siteconfig +Local site configuration information, +such as UUCP connectivity. +They normally contain lists of site information, for example: +.(b +SITE(contessa) +SITE(hoptoad) +SITE(nkainc) +SITE(well) +.)b +They are referenced using the SITECONFIG macro: +.(b +SITECONFIG(site.config.file, name_of_site, X) +.)b +where +.i X +is the macro/class name to use. +It can be U +(indicating locally connected hosts) +or one of W, X, or Y +for up to three remote UUCP hubs. +.pp +If you are in a new domain +(e.g., a company), +you will probably want to create a +cf/domain +file for your domain. +This consists primarily of relay definitions: +for example, Berkeley's domain definition +defines relays for +BitNET, +CSNET, +and UUCP. +Of these, +only the UUCP relay is particularly specific +to Berkeley. +All of these are internet-style domain names. +Please check to make certain they are reasonable for your domain. +.pp +Subdomains at Berkeley are also represented in the +cf/domain +directory. +For example, +the domain +cs-exposed +is the Computer Science subdomain with the local hostname shown +to other users; +cs-hidden +makes users appear to be from the CS.Berkeley.EDU subdomain +(with no local host information included). +You will probably have to update this directory +to be appropriate for your domain. +.pp +You will have to use or create +.b \&.mc +files in the +.i cf/cf +subdirectory for your hosts. +This is detailed in the +cf/README +file. +.sh 2 "Details of Installation Files" +.pp +This subsection describes the files that +comprise the +sendmail +installation. +.sh 3 "/usr/\*(SD/sendmail" +.pp +The binary for sendmail is located in /usr/\*(SD\**. +.(f +\**This is usually +/usr/sbin +on 4.4BSD and newer systems; +many systems install it in +/usr/lib. +I understand it is in /usr/ucblib +on System V Release 4. +.)f +It should be setuid root. +For security reasons, +/, /usr, and /usr/\*(SD +should be owned by root, mode 755\**. +.(f +\**Some vendors ship them owned by bin; +this creates a security hole that is not actually related to +.i sendmail . +Other important directories that should have restrictive ownerships +and permissions are +/bin, /usr/bin, /etc, /usr/etc, /lib, and /usr/lib. +.)f +.sh 3 "/etc/sendmail.cf" +.pp +This is the configuration file for sendmail. +This and the frozen configuration file +are the only two non-library file names compiled into sendmail\**. +.(f +\**The system libraries can reference other files; +in particular, system library subroutines that +sendmail +calls probably reference +.i /etc/passwd +and +.i /etc/resolv.conf . +.)f +Some older systems install it in +.b /usr/lib/sendmail.cf . +.pp +If you want to move this file, +change +.i src/pathnames.h . +.pp +The configuration file is normally created +using the distribution files described above. +If you have a particularly unusual system configuration +you may need to create a special version. +The format of this file is detailed in later sections +of this document. +.sh 3 "/usr/ucb/newaliases" +.pp +If you are running delivermail, +it is critical that the +.i newaliases +command be replaced. +This can just be a link to +.i sendmail : +.(b +rm \-f /usr/ucb/newaliases +ln /usr/\*(SD/sendmail /usr/ucb/newaliases +.)b +This can be installed in whatever search path you prefer +for your system. +.sh 3 "/var/spool/mqueue" +.pp +The directory +.i /var/spool/mqueue +should be created to hold the mail queue. +This directory should be mode 700 +and owned by root. +.pp +The actual path of this directory +is defined in the +.b Q +option of the +.i sendmail.cf +file. +.sh 3 "/etc/aliases*" +.pp +The system aliases are held in +.q /etc/aliases . +A sample is given in +.q lib/aliases +which includes some aliases which +.i must +be defined: +.(b +cp lib/aliases /etc/aliases +.i "edit /etc/aliases" +.)b +You should extend this file with any aliases that are apropos to your system. +.pp +Normally +.i sendmail +looks at a version of these files maintained by the +.i dbm \|(3) +or +.i db \|(3) +routines. +These are stored either in +.q /etc/aliases.dir +and +.q /etc/aliases.pag +or +.q /etc/aliases.db +depending on which database package you are using. +These can initially be created as empty files, +but they will have to be initialized promptly. +These should be mode 644: +.(b +cp /dev/null /etc/aliases.dir +cp /dev/null /etc/aliases.pag +chmod 644 /etc/aliases.* +newaliases +.)b +The +.i db +routines preset the mode reasonably, +so this step can be skipped. +The actual path of this file +is defined in the +.b A +option of the +.i sendmail.cf +file. +.sh 3 "/etc/sendmail.fc" +.pp +If you intend to install the frozen version of the configuration file +(for quick startup) +you should create the file /etc/sendmail.fc +and initialize it. +This step may be safely skipped. +.(b +cp /dev/null /etc/sendmail.fc +chmod 644 /etc/sendmail.fc +/usr/\*(SD/sendmail \-bz +.)b +In general, freeze files are not worth doing +unless your disks are much faster than your CPU; +this is seldom true any more. +.pp +If your +.i sendmail +was not compiled with +.sm FROZENCONFIG +defined, the +.b \-bz +flag will be ignored. +.sh 3 "/etc/rc" +.pp +It will be necessary to start up the sendmail daemon when your system reboots. +This daemon performs two functions: +it listens on the SMTP socket for connections +(to receive mail from a remote system) +and it processes the queue periodically +to insure that mail gets delivered when hosts come up. +.pp +Add the following lines to +.q /etc/rc +(or +.q /etc/rc.local +as appropriate) +in the area where it is starting up the daemons: +.(b +if [ \-f /usr/\*(SD/sendmail \-a \-f /etc/sendmail.cf ]; then + (cd /var/spool/mqueue; rm \-f [lnx]f*) + /usr/\*(SD/sendmail \-bd \-q30m & + echo \-n ' sendmail' >/dev/console +fi +.)b +The +.q cd +and +.q rm +commands insure that all lock files have been removed; +extraneous lock files may be left around +if the system goes down in the middle of processing a message. +The line that actually invokes +.i sendmail +has two flags: +.q \-bd +causes it to listen on the SMTP port, +and +.q \-q30m +causes it to run the queue every half hour. +.pp +Some people use a more complex startup script, +removing zero length qf files and df files for which there is no qf file. +For example: +.(b +# remove zero length qf files +for qffile in qf* +do + if [ \-r $qffile ] + then + if [ ! \-s $qffile ] + then + echo \-n " <zero: $qffile>" > /dev/console + rm \-f $qffile + fi + fi +done +# rename tf files to be qf if the qf does not exist +for tffile in tf* +do + qffile=`echo $tffile | sed 's/t/q/'` + if [ \-r $tffile \-a ! \-f $qffile ] + then + echo \-n " <recovering: $tffile>" > /dev/console + mv $tffile $qffile + else + echo \-n " <extra: $tffile>" > /dev/console + rm \-f $tffile + fi +done +# remove bogus qf files +for dffile in df* +do + qffile=`echo $dffile | sed 's/d/q/'` + if [ \-r $dffile \-a ! \-f $qffile ] + then + echo \-n " <incomplete: $dffile>" > /dev/console + rm \-f $dffile + fi +done +fi +.)b +.pp +If you are not running a version of UNIX +that supports Berkeley TCP/IP, +do not include the +.b \-bd +flag. +.sh 3 "/usr/lib/sendmail.hf" +.pp +This is the help file used by the SMTP +.b HELP +command. +It should be copied from +.q lib/sendmail.hf : +.(b +cp lib/sendmail.hf /usr/lib +.)b +The actual path of this file +is defined in the +.b H +option of the +.i sendmail.cf +file. +.sh 3 "/etc/sendmail.st" +.pp +If you wish to collect statistics +about your mail traffic, +you should create the file +.q /etc/sendmail.st : +.(b +cp /dev/null /etc/sendmail.st +chmod 666 /etc/sendmail.st +.)b +This file does not grow. +It is printed with the program +.q mailstats/mailstats.c. +The actual path of this file +is defined in the +.b S +option of the +.i sendmail.cf +file. +.sh 3 "/usr/ucb/newaliases" +.pp +If +.i sendmail +is invoked as +.q newaliases, +it will simulate the +.b \-bi +flag +(i.e., will rebuild the alias database; +see below). +This should be a link to /usr/\*(SD/sendmail. +.sh 3 "/usr/ucb/mailq" +.pp +If +.i sendmail +is invoked as +.q mailq, +it will simulate the +.b \-bp +flag +(i.e., +.i sendmail +will print the contents of the mail queue; +see below). +This should be a link to /usr/\*(SD/sendmail. +.sh 1 "NORMAL OPERATIONS" +.sh 2 "``Quick'' Configuration Startup" +.pp +if the +.sm FROZENCONFIG +option is included during compilation, +a precompiled (``frozen'') version of the configuration file +can be created using the +.b \-bz +flag. +This is really only worthwhile doing +if you are on a slow processor with a relatively fast I/O system +(a VAX 11/750 is a good example). +Since it creates other problems, +I recommend against using the frozen configuration +on most current architectures. +.pp +To create the freeze file, use +.(b +/usr/\*(SD/sendmail \-bz +.)b +This creates the frozen configuration file +.i /etc/sendmail.fc . +This file is an image of +.i sendmail 's +data space after reading in the configuration file. +If this file exists, +it is used instead of +.i /etc/sendmail.cf +.i sendmail.fc +must be rebuilt manually every time +.i sendmail.cf +is changed. +.pp +The frozen configuration file will be ignored +if a +.b \-C +flag is specified +or if sendmail detects that it is out of date. +However, the heuristics are not strong +so this should not be trusted. +.sh 2 "The System Log" +.pp +The system log is supported by the +.i syslogd \|(8) +program. +.sh 3 "Format" +.pp +Each line in the system log +consists of a timestamp, +the name of the machine that generated it +(for logging from several machines +over the local area network), +the word +.q sendmail: , +and a message. +.sh 3 "Levels" +.pp +If you have +.i syslogd \|(8) +or an equivalent installed, +you will be able to do logging. +There is a large amount of information that can be logged. +The log is arranged as a succession of levels. +At the lowest level +only extremely strange situations are logged. +At the highest level, +even the most mundane and uninteresting events +are recorded for posterity. +As a convention, +log levels under ten +are considered generally +.q useful; +log levels above 64 +are reserved for debugging purposes. +Levels from 11\-64 are reserved for verbose information +that some sites might want. +.pp +A complete description of the log levels +is given in section 4.6. +.sh 2 "The Mail Queue" +.pp +The mail queue should be processed transparently. +However, you may find that manual intervention is sometimes necessary. +For example, +if a major host is down for a period of time +the queue may become clogged. +Although sendmail ought to recover gracefully when the host comes up, +you may find performance unacceptably bad in the meantime. +.sh 3 "Printing the queue" +.pp +The contents of the queue can be printed +using the +.i mailq +command +(or by specifying the +.b \-bp +flag to sendmail): +.(b +mailq +.)b +This will produce a listing of the queue id's, +the size of the message, +the date the message entered the queue, +and the sender and recipients. +.sh 3 "Forcing the queue" +.pp +.i Sendmail +should run the queue automatically +at intervals. +The algorithm is to read and sort the queue, +and then to attempt to process all jobs in order. +When it attempts to run the job, +.i sendmail +first checks to see if the job is locked. +If so, it ignores the job. +.pp +There is no attempt to insure that only one queue processor +exists at any time, +since there is no guarantee that a job cannot take forever +to process +(however, +.i sendmail +does include heuristics to try to abort jobs +that are taking absurd amounts of time; +technically, this violates RFC 821, but is blessed by RFC 1123). +Due to the locking algorithm, +it is impossible for one job to freeze the entire queue. +However, +an uncooperative recipient host +or a program recipient +that never returns +can accumulate many processes in your system. +Unfortunately, +there is no completely general way to solve this. +.pp +In some cases, +you may find that a major host going down +for a couple of days +may create a prohibitively large queue. +This will result in +.i sendmail +spending an inordinate amount of time +sorting the queue. +This situation can be fixed by moving the queue to a temporary place +and creating a new queue. +The old queue can be run later when the offending host returns to service. +.pp +To do this, +it is acceptable to move the entire queue directory: +.(b +cd /var/spool +mv mqueue omqueue; mkdir mqueue; chmod 700 mqueue +.)b +You should then kill the existing daemon +(since it will still be processing in the old queue directory) +and create a new daemon. +.pp +To run the old mail queue, +run the following command: +.(b +/usr/\*(SD/sendmail \-oQ/var/spool/omqueue \-q +.)b +The +.b \-oQ +flag specifies an alternate queue directory +and the +.b \-q +flag says to just run every job in the queue. +If you have a tendency toward voyeurism, +you can use the +.b \-v +flag to watch what is going on. +.pp +When the queue is finally emptied, +you can remove the directory: +.(b +rmdir /var/spool/omqueue +.)b +.sh 2 "The Alias Database" +.pp +The alias database exists in two forms. +One is a text form, +maintained in the file +.i /etc/aliases. +The aliases are of the form +.(b +name: name1, name2, ... +.)b +Only local names may be aliased; +e.g., +.(b +eric@prep.ai.MIT.EDU: eric@CS.Berkeley.EDU +.)b +will not have the desired effect. +Aliases may be continued by starting any continuation lines +with a space or a tab. +Blank lines and lines beginning with a sharp sign +(\c +.q # ) +are comments. +.pp +The second form is processed by the +.i dbm \|(3) +(or +.i db \|(3)) +library. +This form is in the files +.i /etc/aliases.dir +and +.i /etc/aliases.pag. +This is the form that +.i sendmail +actually uses to resolve aliases. +This technique is used to improve performance. +.pp +You can also use +.sm NIS -based +alias files. +For example, the specification: +.(b +OA/etc/aliases +OAnis:mail.aliases@my.nis.domain +.)b +will first search the /etc/aliases file +and then the map named +.q mail.aliases +in +.q my.nis.domain . +.pp +Additional flags can be added after the colon +exactly like a +.b K +line \(em for example: +.(b +OAnis:-N mail.aliases@my.nis.domain +.)b +will search the appropriate NIS map and always include null bytes in the key. +.sh 3 "Rebuilding the alias database" +.pp +The DB or DBM version of the database +may be rebuilt explicitly by executing the command +.(b +newaliases +.)b +This is equivalent to giving +.i sendmail +the +.b \-bi +flag: +.(b +/usr/\*(SD/sendmail \-bi +.)b +.pp +If the +.q D +option is specified in the configuration, +.i sendmail +will rebuild the alias database automatically +if possible +when it is out of date. +Auto-rebuild can be dangerous +on heavily loaded machines +with large alias files; +if it might take more than five minutes +to rebuild the database, +there is a chance that several processes will start the rebuild process +simultaneously. +.pp +If you have multiple aliases databases specified, +the +.b \-bi +flag rebuilds all the database types it understands +(for example, it can rebuild dbm databases but not nis databases). +.sh 3 "Potential problems" +.pp +There are a number of problems that can occur +with the alias database. +They all result from a +.i sendmail +process accessing the DBM version +while it is only partially built. +This can happen under two circumstances: +One process accesses the database +while another process is rebuilding it, +or the process rebuilding the database dies +(due to being killed or a system crash) +before completing the rebuild. +.pp +Sendmail has two techniques to try to relieve these problems. +First, it ignores interrupts while rebuilding the database; +this avoids the problem of someone aborting the process +leaving a partially rebuilt database. +Second, +at the end of the rebuild +it adds an alias of the form +.(b +@: @ +.)b +(which is not normally legal). +Before sendmail will access the database, +it checks to insure that this entry exists\**. +.(f +\**The +.q a +option is required in the configuration +for this action to occur. +This should normally be specified +unless you are running +.i delivermail +in parallel with +.i sendmail. +.)f +.sh 3 "List owners" +.pp +If an error occurs on sending to a certain address, +say +.q \fIx\fP , +.i sendmail +will look for an alias +of the form +.q owner-\fIx\fP +to receive the errors. +This is typically useful +for a mailing list +where the submitter of the list +has no control over the maintenance of the list itself; +in this case the list maintainer would be the owner of the list. +For example: +.(b +unix-wizards: eric@ucbarpa, wnj@monet, nosuchuser, + sam@matisse +owner-unix-wizards: eric@ucbarpa +.)b +would cause +.q eric@ucbarpa +to get the error that will occur +when someone sends to +unix-wizards +due to the inclusion of +.q nosuchuser +on the list. +.pp +List owners also cause the envelope sender address to be modified. +The contents of the owner alias are used if they point to a single user, +otherwise the name of the alias itself is used. +For this reason, and to obey Internet conventions, +a typical scheme would be: +.(b +list: some, set, of, addresses +list-request: list-admin-1, list-admin-2, ... +owner-list: list-request +.)b +.sh 2 "User Information Database" +.pp +If you have a version of +.i sendmail +with the user information database +compiled in, +and you have specified one or more databases using the +.b U +option, +the databases will be searched for a +.i user :maildrop +entry. +If found, the mail will be sent to the specified address. +.pp +If the first token passed to user part of the +.q local +mailer is an at sign, +the at sign will be stripped off +and this step will be skipped. +.sh 2 "Per-User Forwarding (.forward Files)" +.pp +As an alternative to the alias database, +any user may put a file with the name +.q .forward +in his or her home directory. +If this file exists, +.i sendmail +redirects mail for that user +to the list of addresses listed in the .forward file. +For example, if the home directory for user +.q mckusick +has a .forward file with contents: +.(b +mckusick@ernie +kirk@calder +.)b +then any mail arriving for +.q mckusick +will be redirected to the specified accounts. +.pp +Actually, the configuration file defines a sequence of filenames to check. +By default, this is the user's .forward file, +but can be defined to be more generally using the +.b J +option. +If you change this, +you will have to inform your user base of the change; +\&.forward is pretty well incorporated into the collective subconscious. +.sh 2 "Special Header Lines" +.pp +Several header lines have special interpretations +defined by the configuration file. +Others have interpretations built into +.i sendmail +that cannot be changed without changing the code. +These builtins are described here. +.sh 3 "Return-Receipt-To:" +.pp +If this header is sent, +a message will be sent to any specified addresses +when the final delivery is complete, +that is, +when successfully delivered to a mailer with the +.b l +flag (local delivery) set in the mailer descriptor. +.sh 3 "Errors-To:" +.pp +If errors occur anywhere during processing, +this header will cause error messages to go to +the listed addresses. +This is intended for mailing lists. +.pp +The Errors-To: header was created in the bad old days +when UUCP didn't understand the distinction between an envelope and a header; +this was a hack to provide what should now be passed +as the envelope sender address. +It should go away. +It is only used if the +.b l +option is set. +.sh 3 "Apparently-To:" +.pp +If a message comes in with no recipients listed in the message +(in a To:, Cc:, or Bcc: line) +then +.i sendmail +will add an +.q "Apparently-To:" +header line for any recipients it is aware of. +This is not put in as a standard recipient line +to warn any recipients that the list is not complete. +.pp +At least one recipient line is required under RFC 822. +.sh 2 "IDENT Protocol Support" +.pp +.i Sendmail +supports the IDENT protocol as defined in RFC 1413. +Although this enhances identification +of the author of an email message +by doing a ``call back'' to the originating system to include +the owner of a particular TCP connection +in the audit trail +it is in no sense perfect; +a determined forger can easily spoof the IDENT protocol. +The following description is excerpted from RFC 1413: +.ba +5 +6. Security Considerations +.lp +The information returned by this protocol is at most as trustworthy +as the host providing it OR the organization operating the host. For +example, a PC in an open lab has few if any controls on it to prevent +a user from having this protocol return any identifier the user +wants. Likewise, if the host has been compromised the information +returned may be completely erroneous and misleading. +.lp +The Identification Protocol is not intended as an authorization or +access control protocol. At best, it provides some additional +auditing information with respect to TCP connections. At worst, it +can provide misleading, incorrect, or maliciously incorrect +information. +.lp +The use of the information returned by this protocol for other than +auditing is strongly discouraged. Specifically, using Identification +Protocol information to make access control decisions - either as the +primary method (i.e., no other checks) or as an adjunct to other +methods may result in a weakening of normal host security. +.lp +An Identification server may reveal information about users, +entities, objects or processes which might normally be considered +private. An Identification server provides service which is a rough +analog of the CallerID services provided by some phone companies and +many of the same privacy considerations and arguments that apply to +the CallerID service apply to Identification. If you wouldn't run a +"finger" server due to privacy considerations you may not want to run +this protocol. +.ba +.sh 1 "ARGUMENTS" +.pp +The complete list of arguments to +.i sendmail +is described in detail in Appendix A. +Some important arguments are described here. +.sh 2 "Queue Interval" +.pp +The amount of time between forking a process +to run through the queue +is defined by the +.b \-q +flag. +If you run in mode +.b f +or +.b a +this can be relatively large, +since it will only be relevant +when a host that was down comes back up. +If you run in +.b q +mode +it should be relatively short, +since it defines the maximum amount of time that a message +may sit in the queue. +.pp +RFC 1123 section 5.3.1.1 says that this value should be at least 30 minutes +(although that probably doesn't make sense if you use ``queue-only'' mode). +.sh 2 "Daemon Mode" +.pp +If you allow incoming mail over an IPC connection, +you should have a daemon running. +This should be set by your +.i /etc/rc +file using the +.b \-bd +flag. +The +.b \-bd +flag and the +.b \-q +flag may be combined in one call: +.(b +/usr/\*(SD/sendmail \-bd \-q30m +.)b +.sh 2 "Forcing the Queue" +.pp +In some cases you may find that the queue has gotten clogged for some reason. +You can force a queue run +using the +.b \-q +flag (with no value). +It is entertaining to use the +.b \-v +flag (verbose) +when this is done to watch what happens: +.(b +/usr/\*(SD/sendmail \-q \-v +.)b +.pp +You can also limit the jobs to those with a particular queue identifier, +sender, or recipient +using one of the queue modifiers. +For example, +.q \-qRberkeley +restricts the queue run to jobs that have the string +.q berkeley +somewhere in one of the recipient addresses. +Similarly, +.q \-qSstring +limits the run to particular senders and +.q \-qIstring +limits it to particular identifiers. +.sh 2 "Debugging" +.pp +There are a fairly large number of debug flags +built into +.i sendmail . +Each debug flag has a number and a level, +where higher levels means to print out more information. +The convention is that levels greater than nine are +.q absurd, +i.e., +they print out so much information that you wouldn't normally +want to see them except for debugging that particular piece of code. +Debug flags are set using the +.b \-d +option; +the syntax is: +.(b +.ta \w'debug-option 'u +debug-flag: \fB\-d\fP debug-list +debug-list: debug-option [ , debug-option ] +debug-option: debug-range [ . debug-level ] +debug-range: integer | integer \- integer +debug-level: integer +.)b +where spaces are for reading ease only. +For example, +.(b +\-d12 Set flag 12 to level 1 +\-d12.3 Set flag 12 to level 3 +\-d3-17 Set flags 3 through 17 to level 1 +\-d3-17.4 Set flags 3 through 17 to level 4 +.)b +For a complete list of the available debug flags +you will have to look at the code +(they are too dynamic to keep this documentation up to date). +.sh 2 "Trying a Different Configuration File" +.pp +An alternative configuration file +can be specified using the +.b \-C +flag; for example, +.(b +/usr/\*(SD/sendmail \-Ctest.cf +.)b +uses the configuration file +.i test.cf +instead of the default +.i /etc/sendmail.cf. +If the +.b \-C +flag has no value +it defaults to +.i sendmail.cf +in the current directory. +.sh 2 "Changing the Values of Options" +.pp +Options can be overridden using the +.b \-o +flag. +For example, +.(b +/usr/\*(SD/sendmail \-oT2m +.)b +sets the +.b T +(timeout) option to two minutes +for this run only. +.pp +Some options have security implications. +Sendmail allows you to set these, +but refuses to run as root thereafter. +.sh 2 "Logging Traffic" +.pp +Many SMTP implementations do not fully implement the protocol. +For example, some personal computer based SMTPs +do not understand continuation lines in reply codes. +These can be very hard to trace. +If you suspect such a problem, you can set traffic logging using the +.b \-X +flag. +For example, +.(b +/usr/\*(SD/sendmail \-X /tmp/traffic -bd +.)b +will log all traffic in the file +.i /tmp/traffic . +.pp +This logs a lot of data very quickly and should never be used +during normal operations. +After starting up such a daemon, +force the errant implementation to send a message to your host. +All message traffic in and out of +.i sendmail , +including the incoming SMTP traffic, +will be logged in this file. +.sh 1 "TUNING" +.pp +There are a number of configuration parameters +you may want to change, +depending on the requirements of your site. +Most of these are set +using an option in the configuration file. +For example, +the line +.q OT5d +sets option +.q T +to the value +.q 5d +(five days). +.pp +Most of these options have appropriate defaults for most sites. +However, +sites having very high mail loads may find they need to tune them +as appropriate for their mail load. +In particular, +sites experiencing a large number of small messages, +many of which are delivered to many recipients, +may find that they need to adjust the parameters +dealing with queue priorities. +.sh 2 "Timeouts" +.pp +All time intervals are set +using a scaled syntax. +For example, +.q 10m +represents ten minutes, whereas +.q 2h30m +represents two and a half hours. +The full set of scales is: +.(b +.ta 4n +s seconds +m minutes +h hours +d days +w weeks +.)b +.sh 3 "Queue interval" +.pp +The argument to the +.b \-q +flag +specifies how often a sub-daemon will run the queue. +This is typically set to between fifteen minutes +and one hour. +RFC 1123 section 5.3.1.1 recommends that this be at least 30 minutes. +.sh 3 "Read timeouts" +.pp +It is possible to time out when reading the standard input +or when reading from a remote SMTP server. +These timeouts are set using the +.b r +option in the configuration file. +The argument is a list of +.i keyword=value +pairs. +The recognized keywords, their default values, and the minimum values +allowed by RFC 1123 section 5.3.2 are: +.nr ii 1i +.ip initial +The wait for the initial 220 greeting message +[5m, 5m]. +.ip helo +The wait for a reply from a HELO or EHLO command +[5m, unspecified]. +This may require a host name lookup, so +five minutes is probably a reasonable minimum. +.ip mail\(dg +The wait for a reply from a MAIL command +[10m, 5m]. +.ip rcpt\(dg +The wait for a reply from a RCPT command +[1h, 5m]. +This should be long +because it could be pointing at a list +that takes a long time to expand. +.ip datainit\(dg +The wait for a reply from a DATA command +[5m, 2m]. +.ip datablock\(dg +The wait for reading a data block +(that is, the body of the message). +[1h, 3m]. +This should be long because it also applies to programs +piping input to +.i sendmail +which have no guarantee of promptness. +.ip datafinal\(dg +The wait for a reply from the dot terminating a message. +[1h, 10m]. +If this is shorter than the time actually needed +for the receiver to deliver the message, +duplicates will be generated. +This is discussed in RFC 1047. +.ip rset +The wait for a reply from a RSET command +[5m, unspecified]. +.ip quit +The wait for a reply from a QUIT command +[2m, unspecified]. +.ip misc +The wait for a reply from miscellaneous (but short) commands +such as NOOP (no-operation) and VERB (go into verbose mode). +[2m, unspecified]. +.ip command\(dg +In server SMTP, +the time to wait for another command. +[1h, 5m]. +.lp +For compatibility with old configuration files, +if no ``keyword='' is specified, +all the timeouts marked with \(dg are set to the indicated value. +.pp +Many of the RFC 1123 minimum values +may well be too short. +.i Sendmail +was designed to the RFC 822 protocols, +which did not specify read timeouts; +hence, +.i sendmail +does not guarantee to reply to messages promptly. +In particular, a +.q RCPT +command specifying a mailing list +will expand and verify the entire list; +a large list on a slow system +may take more than five minutes\**. +.(f +\**This verification includes looking up every address +with the name server; +this involves network delays, +and can in some cases can be considerable. +.)f +I recommend a one hour timeout \*- +since this failure is rare, +a long timeout is not onerous +and may ultimately help reduce network load. +.pp +For example, the line: +.(b +Orcommand=25m,datablock=3h +.)b +sets the server SMTP command timeout to 25 minutes +and the input data block timeout to three hours. +.sh 3 "Message timeouts" +.pp +After sitting in the queue for a few days, +a message will time out. +This is to insure that at least the sender is aware +of the inability to send a message. +The timeout is typically set to three days. +This timeout is set using the +.b T +option in the configuration file. +.pp +The time of submission is set in the queue, +rather than the amount of time left until timeout. +As a result, you can flush messages that have been hanging +for a short period +by running the queue +with a short message timeout. +For example, +.(b +/usr/\*(SD/sendmail \-oT1d \-q +.)b +will run the queue +and flush anything that is one day old. +.pp +Since this option is global, +and since you can not +.i "a priori" +know how long another host outside your domain will be down, +a five day timeout is recommended. +This allows a recipient to fix the problem even if it occurs +at the beginning of a long weekend. +RFC 1123 section 5.3.1.1 says that this parameter +should be ``at least 4\-5 days''. +.pp +The +.b T +option can also take a second timeout indicating a time after which +a warning message should be sent; +the two timeouts are separated by a slash. +For example, the value +.(b +5d/4h +.)b +causes email to fail after five days, +but a warning message will be sent after four hours. +This should be large enough that the message will have been tried +several times. +.sh 2 "Forking During Queue Runs" +.pp +By setting the +.b Y +option, +.i sendmail +will fork before each individual message +while running the queue. +This will prevent +.i sendmail +from consuming large amounts of memory, +so it may be useful in memory-poor environments. +However, if the +.b Y +option is not set, +.i sendmail +will keep track of hosts that are down during a queue run, +which can improve performance dramatically. +.pp +If the +.b Y +option is set, +.i sendmail +can not use connection caching. +.sh 2 "Queue Priorities" +.pp +Every message is assigned a priority when it is first instantiated, +consisting of the message size (in bytes) +offset by the message class times the +.q "work class factor" +and the number of recipients times the +.q "work recipient factor." +The priority is used to order the queue. +Higher numbers for the priority mean that the message will be processed later +when running the queue. +.pp +The message size is included so that large messages are penalized +relative to small messages. +The message class allows users to send +.q "high priority" +messages by including a +.q Precedence: +field in their message; +the value of this field is looked up in the +.b P +lines of the configuration file. +Since the number of recipients affects the amount of load a message presents +to the system, +this is also included into the priority. +.pp +The recipient and class factors +can be set in the configuration file using the +.b y +and +.b z +options respectively. +They default to 30000 (for the recipient factor) +and 1800 +(for the class factor). +The initial priority is: +.EQ +pri = size - (class times bold z) + (nrcpt times bold y) +.EN +(Remember, higher values for this parameter actually mean +that the job will be treated with lower priority.) +.pp +The priority of a job can also be adjusted each time it is processed +(that is, each time an attempt is made to deliver it) +using the +.q "work time factor," +set by the +.b Z +option. +This is added to the priority, +so it normally decreases the precedence of the job, +on the grounds that jobs that have failed many times +will tend to fail again in the future. +The +.b Z +option defaults to 90000. +.sh 2 "Load Limiting" +.pp +.i Sendmail +can be asked to queue (but not deliver) +mail if the system load average gets too high +using the +.b x +option. +When the load average exceeds the value of the +.b x +option, +the delivery mode is set to +.b q +(queue only) +if the +.i "Queue Factor" +(\c +.b q +option) +divided by the difference in the current load average and the +.b x +option +plus one +exceeds the priority of the message \(em +that is, the message is queued iff: +.EQ +pri > { bold q } over { LA - { bold x } + 1 } +.EN +The +.b q +option defaults to 200000, +so each point of load average is worth 200000 +priority points +(as described above). +.pp +For drastic cases, +the +.b X +option defines a load average at which sendmail will refuse +to accept network connections. +Locally generated mail +(including incoming UUCP mail) +is still accepted. +.sh 2 "Delivery Mode" +.pp +There are a number of delivery modes that +.i sendmail +can operate in, +set by the +.q d +configuration option. +These modes +specify how quickly mail will be delivered. +Legal modes are: +.(b +.ta 4n +i deliver interactively (synchronously) +b deliver in background (asynchronously) +q queue only (don't deliver) +.)b +There are tradeoffs. +Mode +.q i +passes the maximum amount of information to the sender, +but is hardly ever necessary. +Mode +.q q +puts the minimum load on your machine, +but means that delivery may be delayed for up to the queue interval. +Mode +.q b +is probably a good compromise. +However, this mode can cause large numbers of processes +if you have a mailer that takes a long time to deliver a message. +.pp +If you run in mode +.q q +(queue only) +.i sendmail +will not expand aliases and follow .forward files +upon initial receipt of the mail. +This speeds up the response to RCPT commands. +.sh 2 "Log Level" +.pp +The level of logging can be set for sendmail. +The default using a standard configuration table is level 9. +The levels are as follows: +.nr ii 0.5i +.ip 0 +No logging. +.ip 1 +Serious system failures and potential security problems. +.ip 2 +Lost communications (network problems) and protocol failures. +.ip 3 +Other serious failures. +.ip 4 +Minor failures. +.ip 5 +Message collection statistics. +.ip 6 +Creation of error messages, +VRFY and EXPN commands. +.ip 7 +Delivery failures (host or user unknown, etc.). +.ip 8 +Successful deliveries. +.ip 9 +Messages being deferred +(due to a host being down, etc.). +.ip 10 +Database expansion (alias, forward, and userdb lookups). +.ip 15 +Automatic alias database rebuilds. +.ip 20 +Logs attempts to run locked queue files. +These are not errors, +but can be useful to note if your queue appears to be clogged. +.ip 30 +Lost locks (only if using lockf instead of flock). +.lp +Additionally, +values above 64 are reserved for extremely verbose debuggging output. +No normal site would ever set these. +.sh 2 "File Modes" +.pp +There are a number of files +that may have a number of modes. +The modes depend on what functionality you want +and the level of security you require. +.sh 3 "To suid or not to suid?" +.pp +.i Sendmail +can safely be made +setuid to root. +At the point where it is about to +.i exec \|(2) +a mailer, +it checks to see if the userid is zero; +if so, +it resets the userid and groupid to a default +(set by the +.b u +and +.b g +options). +(This can be overridden +by setting the +.b S +flag to the mailer +for mailers that are trusted +and must be called as root.) +However, +this will cause mail processing +to be accounted +(using +.i sa \|(8)) +to root +rather than to the user sending the mail. +.sh 3 "Should my alias database be writable?" +.pp +At Berkeley +we have the alias database +(/etc/aliases*) +mode 644. +While this is not as flexible as if the database +were more 666, it avoids potential security problems +with a globally writable database. +.pp +The database that +.i sendmail +actually used +is represented by the two files +.i aliases.dir +and +.i aliases.pag +(both in /etc) +(or +.i aliases.db +if you are running with the new Berkeley database primitives). +The mode on these files should match the mode +on /etc/aliases. +If +.i aliases +is writable +and the +DBM +files +(\c +.i aliases.dir +and +.i aliases.pag ) +are not, +users will be unable to reflect their desired changes +through to the actual database. +However, +if +.i aliases +is read-only +and the DBM files are writable, +a slightly sophisticated user +can arrange to steal mail anyway. +.pp +If your DBM files are not writable by the world +or you do not have auto-rebuild enabled +(with the +.q D +option), +then you must be careful to reconstruct the alias database +each time you change the text version: +.(b +newaliases +.)b +If this step is ignored or forgotten +any intended changes will also be ignored or forgotten. +.sh 2 "Connection Caching" +.pp +When processing the queue, +.b sendmail +will try to keep the last few open connections open +to avoid startup and shutdown costs. +This only applies to IPC connections. +.pp +When trying to open a connection +the cache is first searched. +If an open connection is found, it is probed to see if it is still active +by sending a +.sm NOOP +command. +It is not an error if this fails; +instead, the connection is closed and reopened. +.pp +Two parameters control the connection cache. +The +.b k +option defines the number of simultaneous open connections +that will be permitted. +If it is set to zero, +connections will be closed as quickly as possible. +The default is one. +This should be set as appropriate for your system size; +it will limit the amount of system resources that +.b sendmail +will use during queue runs. +.pp +The +.b K +option specifies the maximum time that any cached connection +will be permitted to idle. +When the idle time exceeds this value +the connection is closed. +This number should be small +(under ten minutes) +to prevent you from grabbing too many resources +from other hosts. +The default is five minutes. +.sh 2 "Name Server Access" +.pp +If your system supports the name server, +then the probability is that +.i sendmail +will be using it regardless of how you configure sendmail. +However, if you have nameserver support +which you are not using, +sendmail will get a +.q "connection refused" +message when it tries to connect to the name server +(either by calling +.i gethostbyname +or by trying to look up the MX records). +If the +.b I +option is set, +.i sendmail +will interpret this to mean a temporary failure; +otherwise, it ignores the name server data. +If your name server is running properly, +the setting of this option is not relevant; +however, it is important that it be set properly +to make error handling work properly. +.pp +This option also allows you to tweak name server options. +The command line takes a series of flags as documented in +.i resolver (3) +(with the leading +.q RES_ +deleted). +Each can be preceded by an optional `+' or `\(mi'. +For example, the line +.(b +OITrue +AAONLY \(miDNSRCH +.)b +turns on the AAONLY (accept authoritative answers only) +and turns off the DNSRCH (search the domain path) options. +Most resolver libraries default DNSRCH, DEFNAMES, and RECURSE +flags on and all others off. +Note the use of the initial ``True'' \*- +this is for compatibility with previous versions of sendmail, +but is not otherwise necessary. +.pp +Version level 1 configurations +turn DNSRCH and DEFNAMES off when doing delivery lookups, +but leave them on everywhere else. +Version 8 of +.i sendmail +ignores them when doing canonification lookups +(that is, when using $[ ... $]), +and always does the search. +If you don't want to do automatic name extension, +don't call $[ ... $]. +.pp +The search rules for $[ ... $] are somewhat different than usual. +If the name (that is, the ``...'') +has at least one dot, it always tries the unmodified name first. +If that fails, it tries the reduced search path, +and lastly tries the unmodified name +(but only for names without a dot, +since names with a dot have already been tried). +This allows names such as +``utc.CS'' +to match the site in Czechoslovakia +rather than the site in your local Computer Science department. +It also prefers A and CNAME records over MX records \*- +that is, if it finds an MX record it makes note of it, +but keeps looking. +This way, if you have a wildcard MX record matching your domain, +it will not assume that all names match. +.sh 2 "Moving the Per-User Forward Files" +.pp +Some sites mount each user's home directory +from a local disk on their workstation, +so that local access is fast. +However, the result is that .forward file lookups are slow. +In some cases, +mail can even be delivered on machines inappropriately +because of a file server being down. +The performance can be especially bad if you run the automounter. +.pp +The +.b J +option allows you to set a path of forward files. +For example, the config file line +.(b +OJ/var/forward/$u:$z/.forward +.)b +would first look for a file with the same name as the user's login +in /var/forward; +if that is not found (or is inaccessible) +the file +.q \&.forward +in the user's home directory is searched. +A truly perverse site could also search by sender +by using $r, $s, or $f. +.pp +If you create a directory such as /var/forward, +it should be mode 1777 +(that is, the sticky bit should be set). +Users should create the files mode 644. +.sh 2 "Free Space" +.pp +On systems that have the +.i statfs (2) +system call, +you can specify a minimum number of free blocks on the queue filesystem +using the +.b b +option. +If there are fewer than the indicated number of blocks free +on the filesystem on which the queue is mounted +the SMTP server will reject mail +with the +452 error code. +This invites the SMTP client to try again later. +.pp +Beware of setting this option too high; +it can cause rejection of email +when that mail would be processed without difficulty. +.pp +This option can also specify an advertised +.q "maximum message size" +for hosts that speak ESMTP. +.sh 2 "Privacy Flags" +.pp +The +.b p +option allows you to set certain +``privacy'' +flags. +Actually, many of them don't give you any extra privacy, +rather just insisting that client SMTP servers +use the HELO command +before using certain commands. +.pp +The option takes a series of flag names; +the final privacy is the inclusive or of those flags. +For example: +.(b +Op needmailhelo, noexpn +.)b +insists that the HELO or EHLO command be used before a MAIL command is accepted +and disables the EXPN command. +.pp +The +.q restrictmailq +option restricts printing the queue to the group that owns the queue directory. +It is absurd to set this if you don't also protect the logs. +.sh 2 "Send to Me Too" +.pp +Normally, +.i sendmail +deletes the (envelope) sender from any list expansions. +For example, if +.q matt +sends to a list that contains +.q matt +as one of the members he won't get a copy of the message. +If the +.b \-m +(me too) +command line flag, or if the +.b m +option is set in the configuration file, +this behaviour is supressed. +Some sites like to run the +.sm SMTP +daemon with +.b \-m . +.sh 1 "THE WHOLE SCOOP ON THE CONFIGURATION FILE" +.pp +This section describes the configuration file +in detail, +including hints on how to write one of your own +if you have to. +.pp +There is one point that should be made clear immediately: +the syntax of the configuration file +is designed to be reasonably easy to parse, +since this is done every time +.i sendmail +starts up, +rather than easy for a human to read or write. +On the +.q "future project" +list is a +configuration-file compiler. +.pp +An overview of the configuration file +is given first, +followed by details of the semantics. +.sh 2 "Configuration File Lines" +.pp +The configuration file is organized as a series of lines, +each of which begins with a single character +defining the semantics for the rest of the line. +Lines beginning with a space or a tab +are continuation lines +(although the semantics are not well defined in many places). +Blank lines and lines beginning with a sharp symbol +(`#') +are comments. +.sh 3 "R and S \*- rewriting rules" +.pp +The core of address parsing +are the rewriting rules. +These are an ordered production system. +.i Sendmail +scans through the set of rewriting rules +looking for a match on the left hand side +(LHS) +of the rule. +When a rule matches, +the address is replaced by the right hand side +(RHS) +of the rule. +.pp +There are several sets of rewriting rules. +Some of the rewriting sets are used internally +and must have specific semantics. +Other rewriting sets +do not have specifically assigned semantics, +and may be referenced by the mailer definitions +or by other rewriting sets. +.pp +The syntax of these two commands are: +.(b F +.b S \c +.i n +.)b +Sets the current ruleset being collected to +.i n . +If you begin a ruleset more than once +it deletes the old definition. +.(b F +.b R \c +.i lhs +.i rhs +.i comments +.)b +The +fields must be separated +by at least one tab character; +there may be embedded spaces +in the fields. +The +.i lhs +is a pattern that is applied to the input. +If it matches, +the input is rewritten to the +.i rhs . +The +.i comments +are ignored. +.pp +Macro expansions of the form +.b $ \c +.i x +are performed when the configuration file is read. +Expansions of the form +.b $& \c +.i x +are performed at run time using a somewhat less general algorithm. +This for is intended only for referencing internally defined macros +such as +.b $h +that are changed at runtime. +.sh 4 "The left hand side" +.pp +The left hand side of rewriting rules contains a pattern. +Normal words are simply matched directly. +Metasyntax is introduced using a dollar sign. +The metasymbols are: +.(b +.ta \w'\fB$=\fP\fIx\fP 'u +\fB$*\fP Match zero or more tokens +\fB$+\fP Match one or more tokens +\fB$\-\fP Match exactly one token +\fB$=\fP\fIx\fP Match any phrase in class \fIx\fP +\fB$~\fP\fIx\fP Match any word not in class \fIx\fP +.)b +If any of these match, +they are assigned to the symbol +.b $ \c +.i n +for replacement on the right hand side, +where +.i n +is the index in the LHS. +For example, +if the LHS: +.(b +$\-:$+ +.)b +is applied to the input: +.(b +UCBARPA:eric +.)b +the rule will match, and the values passed to the RHS will be: +.(b +.ta 4n +$1 UCBARPA +$2 eric +.)b +.pp +Additionally, the LHS can include +.b $@ +to match zero tokens. +This is +.i not +bound to a +.b $ \c +.i N +on the RHS, and is normally only used when it stands alone +in order to match the null input. +.sh 4 "The right hand side" +.pp +When the left hand side of a rewriting rule matches, +the input is deleted and replaced by the right hand side. +Tokens are copied directly from the RHS +unless they begin with a dollar sign. +Metasymbols are: +.(b +.ta \w'$#mailer\0\0\0'u +\fB$\fP\fIn\fP Substitute indefinite token \fIn\fP from LHS +\fB$[\fP\fIname\fP\fB$]\fP Canonicalize \fIname\fP +\fB$(\fP\fImap key\fP \fB$@\fP\fIarguments\fP \fB$:\fP\fIdefault\fP \fB$)\fP + Generalized keyed mapping function +\fB$>\fP\fIn\fP \*(lqCall\*(rq ruleset \fIn\fP +\fB$#\fP\fImailer\fP Resolve to \fImailer\fP +\fB$@\fP\fIhost\fP Specify \fIhost\fP +\fB$:\fP\fIuser\fP Specify \fIuser\fP +.)b +.pp +The +.b $ \c +.i n +syntax substitutes the corresponding value from a +.b $+ , +.b $\- , +.b $* , +.b $= , +or +.b $~ +match on the LHS. +It may be used anywhere. +.pp +A host name enclosed between +.b $[ +and +.b $] +is looked up using the +.i gethostent \|(3) +routines and replaced by the canonical name\**. +.(f +\**This is actually +completely equivalent +to $(host \fIhostname\fP$). +In particular, a +.b $: +default can be used. +.)f +For example, +.q $[csam$] +might become +.q lbl-csam.arpa +and +.q $[[128.32.130.2]$] +would become +.q vangogh.CS.Berkeley.EDU. +.i Sendmail +recognizes it's numeric IP address +without calling the name server +and replaces it with it's canonical name. +.pp +The +.b $( +\&... +.b $) +syntax is a more general form of lookup; +it uses a named map instead of an implicit map. +If no lookup is found, the indicted +.i default +is inserted; +if no default is specified and no lookup matches, +the value is left unchanged. +.pp +The +.b $> \c +.i n +syntax +causes the remainder of the line to be substituted as usual +and then passed as the argument to ruleset +.i n . +The final value of ruleset +.i n +then becomes +the substitution for this rule. +.pp +The +.b $# +syntax should +.i only +be used in ruleset zero. +It causes evaluation of the ruleset to terminate immediately, +and signals to sendmail that the address has completely resolved. +The complete syntax is: +.(b +\fB$#\fP\fImailer\fP \fB$@\fP\fIhost\fP \fB$:\fP\fIuser\fP +.)b +This specifies the +{mailer, host, user} +3-tuple necessary to direct the mailer. +If the mailer is local +the host part may be omitted\**. +.(f +\**You may want to use it for special +.q "per user" +extensions. +For example, at CMU you can send email to +.q jgm+foo ; +the part after the plus sign +is not part of the user name, +and is passed to the local mailer for local use. +.)f +The +.i mailer +must be a single word, +but the +.i host +and +.i user +may be multi-part. +If the +.i mailer +is the builtin IPC mailer, +the +.i host +may be a colon-separated list of hosts +that are searched in order for the first working address +(exactly like MX records). +.pp +A RHS may also be preceded by a +.b $@ +or a +.b $: +to control evaluation. +A +.b $@ +prefix causes the ruleset to return with the remainder of the RHS +as the value. +A +.b $: +prefix causes the rule to terminate immediately, +but the ruleset to continue; +this can be used to avoid continued application of a rule. +The prefix is stripped before continuing. +.pp +The +.b $@ +and +.b $: +prefixes may precede a +.b $> +spec; +for example: +.(b +.ta 8n +R$+ $: $>7 $1 +.)b +matches anything, +passes that to ruleset seven, +and continues; +the +.b $: +is necessary to avoid an infinite loop. +.pp +Substitution occurs in the order described, +that is, +parameters from the LHS are substituted, +hostnames are canonicalized, +.q subroutines +are called, +and finally +.b $# , +.b $@ , +and +.b $: +are processed. +.sh 4 "Semantics of rewriting rule sets" +.pp +There are five rewriting sets +that have specific semantics. +These are related as depicted by figure 2. +.(z +.hl +.ie n \{\ +.(c + +---+ + -->| 0 |-->resolved address + / +---+ + / +---+ +---+ + / ---->| 1 |-->| S |-- + +---+ / +---+ / +---+ +---+ \e +---+ +addr-->| 3 |-->| D |-- --->| 4 |-->msg + +---+ +---+ \e +---+ +---+ / +---+ + --->| 2 |-->| R |-- + +---+ +---+ +.)c + +.\} +.el .ie !"\*(.T"" \ +\{\ +.PS +boxwid = 0.3i +boxht = 0.3i +movewid = 0.3i +moveht = 0.3i +linewid = 0.3i +lineht = 0.3i + + box invis "addr"; arrow +Box3: box "3" +A1: arrow +BoxD: box "D"; line; L1: Here +C: [ + C1: arrow; box "1"; arrow; box "S"; line; E1: Here + move to C1 down 0.5; right + C2: arrow; box "2"; arrow; box "R"; line; E2: Here + ] with .w at L1 + (0.5, 0) + move to C.e right 0.5 +L4: arrow; box "4"; arrow; box invis "msg" + line from L1 to C.C1 + line from L1 to C.C2 + line from C.E1 to L4 + line from C.E2 to L4 + move to BoxD.n up 0.6; right +Box0: arrow; box "0" + arrow; box invis "resolved address" width 1.3 + line from 1/3 of the way between A1 and BoxD.w to Box0 +.PE +.\} +.el .sp 2i +.ce +Figure 2 \*- Rewriting set semantics +.(c +D \*- sender domain addition +S \*- mailer-specific sender rewriting +R \*- mailer-specific recipient rewriting +.)c +.hl +.)z +.pp +Ruleset three +should turn the address into +.q "canonical form." +This form should have the basic syntax: +.(b +local-part@host-domain-spec +.)b +If no +.q @ +sign is specified, +then the +host-domain-spec +.i may +be appended from the +sender address +(if the +.b C +flag is set in the mailer definition +corresponding to the +.i sending +mailer). +Ruleset three +is applied by sendmail +before doing anything with any address. +.pp +Ruleset zero +is applied after ruleset three +to addresses that are going to actually specify recipients. +It must resolve to a +.i "{mailer, host, user}" +triple. +The +.i mailer +must be defined in the mailer definitions +from the configuration file. +The +.i host +is defined into the +.b $h +macro +for use in the argv expansion of the specified mailer. +.pp +Rulesets one and two +are applied to all sender and recipient addresses respectively. +They are applied before any specification +in the mailer definition. +They must never resolve. +.pp +Ruleset four is applied to all addresses +in the message. +It is typically used +to translate internal to external form. +.sh 4 "IPC mailers" +.pp +Some special processing occurs +if the ruleset zero resolves to an IPC mailer +(that is, a mailer that has +.q [IPC] +listed as the Path in the +.b M +configuration line. +The host name passed after +.q $@ +has MX expansion performed; +this looks the name up in DNS to find alternate delivery sites. +.pp +The host name can also be provided as a dotted quad in square brackets; +for example: +.(b +[128.32.149.78] +.)b +This causes direct conversion of the numeric value +to a TCP/IP host address. +.pp +The host name passed in after the +.q $@ +may also be a colon-separated list of hosts. +Each is separately MX expanded and the results are concatenated +to make (essentially) one long MX list. +The intent here is to create +.q fake +MX records that are not published in DNS +for private internal networks. +.pp +As a final special case, the host name can be passed in +as a text string +in square brackets: +.(b +[ucbvax.berkeley.edu] +.)b +This form avoids the MX mapping. +.b N.B.: +This is intended only for situations where you have a network firewall, +so that your MX record points to a gateway machine; +this machine could then do direct delivery to machines +within your local domain. +Use of this feature directly violates RFC 1123 section 5.3.5: +it should not be used lightly. +.sh 3 "D \*- define macro" +.pp +Macros are named with a single character. +These may be selected from the entire ASCII set, +but user-defined macros +should be selected from the set of upper case letters only. +Lower case letters +and special symbols +are used internally. +.pp +The syntax for macro definitions is: +.(b F +.b D \c +.i x\|val +.)b +where +.i x +is the name of the macro +and +.i val +is the value it should have. +.pp +Macros are interpolated +using the construct +.b $ \c +.i x , +where +.i x +is the name of the macro to be interpolated. +This interpolation is done when the configuration file is read, +except in +.b M +lines. +The special construct +.b $& \c +.i x +can be used in +.b R +lines to get deferred interpolation. +.pp +Conditionals can be specified using the syntax: +.(b +$?x text1 $| text2 $. +.)b +This interpolates +.i text1 +if the macro +.b $x +is set, +and +.i text2 +otherwise. +The +.q else +(\c +.b $| ) +clause may be omitted. +.pp +Lower case macro names are reserved to have +special semantics, +used to pass information in or out of sendmail, +and special characters are reserved to +provide conditionals, etc. +Upper case names +(that is, +.b $A +through +.b $Z ) +are specifically reserved for configuration file authors. +.pp +The following macros +.i must +be defined to transmit information into +.i sendmail: +.(b +.ta 4n +e The SMTP entry message +j The \*(lqofficial\*(rq domain name for this site +l The format of the UNIX from line +n The name of the daemon (for error messages) +o The set of "operators" in addresses +q default format of sender address +.)b +The +.b $e +macro is printed out when SMTP starts up. +The first word must be the +.b $j +macro. +The +.b $j +macro +should be in RFC821 format. +The +.b $l +and +.b $n +macros can be considered constants +except under terribly unusual circumstances. +The +.b $o +macro consists of a list of characters +which will be considered tokens +and which will separate tokens +when doing parsing. +For example, if +.q @ +were in the +.b $o +macro, then the input +.q a@b +would be scanned as three tokens: +.q a, +.q @, +and +.q b. +Finally, the +.b $q +macro specifies how an address should appear in a message +when it is defaulted. +For example, on our system these definitions are: +.(b +De$j Sendmail $v/$Z ready at $b +DnMAILER-DAEMON +DlFrom $g $d +Do.:%@!^/[] +Dq$?x$x <$g>$|$g$. +Dj$w +.)b +An acceptable alternative for the +.b $q +macro is +.q "$g$?x ($x)$." . +These correspond to the following two formats: +.(b +Eric Allman <eric@CS.Berkeley.EDU> +eric@CS.Berkeley.EDU (Eric Allman) +.)b +.i Sendmail +properly quotes names that have special characters +if the first form is used. +.pp +Some macros are defined by +.i sendmail +for interpolation into argv's for mailers +or for other contexts. +These macros are: +.(b +a The origination date in RFC 822 format +b The current date in RFC 822 format +c The hop count +d The date in UNIX (ctime) format +f The sender (from) address +g The sender address relative to the recipient +h The recipient host +i The queue id +k The UUCP node name (from the uname system call) +m The domain part of the \fIgethostname\fP return value +p Sendmail's pid +r Protocol used to receive the message +s Sender's host name +t A numeric representation of the current time +u The recipient user +v The version number of sendmail +w The hostname of this site +x The full name of the sender +z The home directory of the recipient +_ The validated sender address +.)b +.pp +There are three types of dates that can be used. +The +.b $a +and +.b $b +macros are in RFC 822 format; +.b $a +is the time as extracted from the +.q Date: +line of the message +(if there was one), +and +.b $b +is the current date and time +(used for postmarks). +If no +.q Date: +line is found in the incoming message, +.b $a +is set to the current time also. +The +.b $d +macro is equivalent to the +.b $b +macro in UNIX +(ctime) +format. +.pp +The +.b $f +macro is the id of the sender +as originally determined; +when mailing to a specific host +the +.b $g +macro is set to the address of the sender +.ul +relative to the recipient. +For example, +if I send to +.q bollard@matisse.CS.Berkeley.EDU +from the machine +.q vangogh.CS.Berkeley.EDU +the +.b $f +macro will be +.q eric +and the +.b $g +macro will be +.q eric@vangogh.CS.Berkeley.EDU. +.pp +The +.b $x +macro is set to the full name of the sender. +This can be determined in several ways. +It can be passed as flag to +.i sendmail. +The second choice is the value of the +.q Full-name: +line in the header if it exists, +and the third choice is the comment field +of a +.q From: +line. +If all of these fail, +and if the message is being originated locally, +the full name is looked up in the +.i /etc/passwd +file. +.pp +When sending, +the +.b $h , +.b $u , +and +.b $z +macros get set to the host, user, and home directory +(if local) +of the recipient. +The first two are set from the +.b $@ +and +.b $: +part of the rewriting rules, respectively. +.pp +The +.b $p +and +.b $t +macros are used to create unique strings +(e.g., for the +.q Message-Id: +field). +The +.b $i +macro is set to the queue id on this host; +if put into the timestamp line +it can be extremely useful for tracking messages. +The +.b $v +macro is set to be the version number of +.i sendmail ; +this is normally put in timestamps +and has been proven extremely useful for debugging. +The +.b $w +macro is set to the name of this host +if it can be determined. +The +.b $c +field is set to the +.q "hop count," +i.e., the number of times this message has been processed. +This can be determined +by the +.b \-h +flag on the command line +or by counting the timestamps in the message. +.pp +The +.b $r +and +.b $s +fields are set to the protocol used to communicate with sendmail +and the sending hostname. +The +.b $_ +is set to a validated sender host name. +If the sender is running an RFC 1413 compliant IDENT server, +it will include the user name on that host. +.sh 3 "C and F \*- define classes" +.pp +Classes of phrases may be defined +to match on the left hand side of rewriting rules, +where a +.q phrase +is a sequence of characters that do not contain space characters. +For example +a class of all local names for this site +might be created +so that attempts to send to oneself +can be eliminated. +These can either be defined directly in the configuration file +or read in from another file. +Classes may be given names +from the set of upper case letters. +Lower case letters and special characters +are reserved for system use. +.pp +The syntax is: +.(b F +.b C \c +.i c\|phrase1 +.i phrase2... +.br +.b F \c +.i c\|file +.)b +The first form defines the class +.i c +to match any of the named words. +It is permissible to split them among multiple lines; +for example, the two forms: +.(b +CHmonet ucbmonet +.)b +and +.(b +CHmonet +CHucbmonet +.)b +are equivalent. +The second form +reads the elements of the class +.i c +from the named +.i file . +.pp +The +.b $~ +(match entries not in class) +only matches a single word; +multi-word entries in the class are ignored in this context. +.pp +The class +.b $=w +is set to be the set of all names +this host is known by. +This can be used to match local hostnames. +.pp +The class +.b $=k +is set to be the same as +.b $k , +that is, the UUCP node name. +.sh 3 "M \*- define mailer" +.pp +Programs and interfaces to mailers +are defined in this line. +The format is: +.(b F +.b M \c +.i name , +{\c +.i field =\c +.i value \|}* +.)b +where +.i name +is the name of the mailer +(used internally only) +and the +.q field=name +pairs define attributes of the mailer. +Fields are: +.(b +.ta 1i +Path The pathname of the mailer +Flags Special flags for this mailer +Sender A rewriting set for sender addresses +Recipient A rewriting set for recipient addresses +Argv An argument vector to pass to this mailer +Eol The end-of-line string for this mailer +Maxsize The maximum message length to this mailer +Linelimit The maximum line length in the message body +Directory The working directory for the mailer +.)b +Only the first character of the field name is checked. +.pp +The following flags may be set in the mailer description. +Any other flags may be used freely +to conditionally assign headers to messages +destined for particular mailers. +.nr ii 4n +.ip a +Run Extended SMTP (ESMTP) protocol (defined in RFCs 1425, 1426, and 1427). +.ip b +Force a blank line on the end of a message. +This is intended to work around some stupid versions of +/bin/mail +that require a blank line, but do not provide it themselves. +It would not normally be used on network mail. +.ip c +Do not include comments in addresses. +This should only be used if you have to work around +a remote mailer that gets confused by comments. +.ip C +If mail is +.i received +from a mailer with this flag set, +any addresses in the header that do not have an at sign +(\c +.q @ ) +after being rewritten by ruleset three +will have the +.q @domain +clause from the sender +tacked on. +This allows mail with headers of the form: +.(b +From: usera@hosta +To: userb@hostb, userc +.)b +to be rewritten as: +.(b +From: usera@hosta +To: userb@hostb, userc@hosta +.)b +automatically. +.ip D +This mailer wants a +.q Date: +header line. +.ip e +This mailer is expensive to connect to, +so try to avoid connecting normally; +any necessary connection will occur during a queue run. +.ip E +Escape lines beginning with +.q From +in the message with a `>' sign. +.ip f +The mailer wants a +.b \-f +.i from +flag, +but only if this is a network forward operation +(i.e., +the mailer will give an error +if the executing user +does not have special permissions). +.ip F +This mailer wants a +.q From: +header line. +.ip g +Normally, +.i sendmail +sends internally generated email (e.g., error messages) +using the null return address\** +.(f +\**Actually, this only applies to SMTP, +which uses the ``MAIL FROM:<>'' command. +.)f +as required by RFC 1123. +However, some mailers don't accept a null return address. +If necessary, +you can set the +.b g +flag to prevent +.i sendmail +from obeying the standards; +error messages will be sent as from the MAILER-DAEMON +(actually, the value of the +.b $n +macro). +.ip h +Upper case should be preserved in host names +for this mailer. +.ip I +This mailer will be speaking SMTP +to another +.i sendmail +\*- +as such it can use special protocol features. +This option is not required +(i.e., +if this option is omitted the transmission will still operate successfully, +although perhaps not as efficiently as possible). +.ip l +This mailer is local +(i.e., +final delivery will be performed). +.ip L +Limit the line lengths as specified in RFC821. +This deprecated option should be replaced by the +.b L= +mail declaration. +For historic reasons, the +.b L +flag also sets the +.b 7 +flag. +.ip m +This mailer can send to multiple users +on the same host +in one transaction. +When a +.b $u +macro occurs in the +.i argv +part of the mailer definition, +that field will be repeated as necessary +for all qualifying users. +.ip M +This mailer wants a +.q Message-Id: +header line. +.ip n +Do not insert a UNIX-style +.q From +line on the front of the message. +.ip p +Use the route-addr style reverse-path in the SMTP +.q "MAIL FROM:" +command +rather than just the return address; +although this is required in RFC821 section 3.1, +many hosts do not process reverse-paths properly. +Reverse-paths are officially discouraged by RFC 1123. +.ip P +This mailer wants a +.q Return-Path: +line. +.ip r +Same as +.b f , +but sends a +.b \-r +flag. +.ip s +Strip quote characters off of the address +before calling the mailer. +.ip S +Don't reset the userid +before calling the mailer. +This would be used in a secure environment +where +.i sendmail +ran as root. +This could be used to avoid forged addresses. +This flag is suppressed if given from an +.q unsafe +environment +(e.g, a user's mail.cf file). +.ip u +Upper case should be preserved in user names +for this mailer. +.ip U +This mailer wants Unix-style +.q From +lines with the ugly UUCP-style +.q "remote from <host>" +on the end. +.ip x +This mailer wants a +.q Full-Name: +header line. +.ip X +This mailer want to use the hidden dot algorithm +as specified in RFC821; +basically, +any line beginning with a dot +will have an extra dot prepended +(to be stripped at the other end). +This insures that lines in the message containing a dot +will not terminate the message prematurely. +.ip 7 +Strip all output to seven bits. +This is the default if the +.b L +flag is set. +Note that setting this is not +sufficient to get full eight bit data passed through +.i sendmail . +If the +.b 7 +option is set, this is essentially always set, +since the eighth bit was stripped on input. +.pp +The mailer with the special name +.q error +can be used to generate a user error. +The (optional) host field is an exit status to be returned, +and the user field is a message to be printed. +The exit status may be numeric or one of the values +USAGE, NOUSER, NOHOST, UNAVAILABLE, SOFTWARE, TEMPFAIL, PROTOCOL, or CONFIG +to return the corresponding EX_ exit code. +For example, the entry: +.(b +$#error $@ NOHOST $: Host unknown in this domain +.)b +on the RHS of a rule +will cause the specified error to be generated +and the +.q "Host unknown" +exit status to be returned +if the LHS matches. +This mailer is only functional in ruleset zero. +.pp +The mailer named +.q local +.i must +be defined in every configuration file. +This is used to deliver local mail, +and is treated specially in several ways. +Additionally, three other mailers named +.q prog , +.q *file* , +and +.q *include* +may be defined to tune the delivery of messages to programs, +files, +and :include: lists respectively. +They default to: +.(b +Mprog, P=/bin/sh, F=lsD, A=sh \-c $u +M*file*, P=/dev/null, F=lsDFMPEu, A=FILE +M*include*, P=/dev/null, F=su, A=INCLUDE +.)b +.pp +The Sender and Recipient rewriting sets +may either be a simple integer +or may be two integers separated by a slash; +if so, the first rewriting set is applied to envelope +addresses +and the second is applied to headers. +.pp +The Directory +is actually a colon-separated path of directories to try. +For example, the definition +.q D=$z:/ +first tries to execute in the recipient's home directory; +if that is not available, +it tries to execute in the root of the filesystem. +This is intended to be used only on the +.q prog +mailer, +since some shells (such as +.i csh ) +refuse to execute if they cannot read the home directory. +Since the queue directory is not normally readable by normal users +.i csh +scripts as recipients can fail. +.sh 3 "H \*- define header" +.pp +The format of the header lines that sendmail inserts into the message +are defined by the +.b H +line. +The syntax of this line is: +.(b F +.b H [\c +.b ? \c +.i mflags \c +.b ? ]\c +.i hname \c +.b : +.i htemplate +.)b +Continuation lines in this spec +are reflected directly into the outgoing message. +The +.i htemplate +is macro expanded before insertion into the message. +If the +.i mflags +(surrounded by question marks) +are specified, +at least one of the specified flags +must be stated in the mailer definition +for this header to be automatically output. +If one of these headers is in the input +it is reflected to the output +regardless of these flags. +.pp +Some headers have special semantics +that will be described below. +.sh 3 "O \*- set option" +.pp +There are a number of +.q random +options that +can be set from a configuration file. +Options are represented by single characters. +The syntax of this line is: +.(b F +.b O \c +.i o\|value +.)b +This sets option +.i o +to be +.i value . +Depending on the option, +.i value +may be a string, an integer, +a boolean +(with legal values +.q t , +.q T , +.q f , +or +.q F ; +the default is TRUE), +or +a time interval. +.pp +The options supported are: +.nr ii 1i +.ip a\fIN\fP +If set, +wait up to +.i N +minutes for an +.q @:@ +entry to exist in the alias database +before starting up. +If it does not appear in +.i N +minutes, +rebuild the database +(if the +.b D +option is also set) +or issue a warning. +.ip "A\fIspec, spec, ...\fP" +Specify possible alias file(s). +Each +.i spec +should be in the format +``\c +.i class \c +.b : +.i file '' +where +.i class \c +.b : +is optional and defaults to ``implicit''. +Depending on how +.b sendmail +is compiled, valid classes are +.q implicit +(search through a compiled-in list of alias file types, +for back compatibility), +.q hash +(if +.sm NEWDB +is specified), +.q dbm +(if +.sm NDBM +is specified), +.q stab +(internal symbol table \*- not normally used +unless you have no other database lookup), +or +.q nis +(if +.sm NIS +is specified). +If a list of +.i spec s +are provided, +.i sendmail +searches them in order. +.ip b\fIN\fP/\fIM\fP +Insist on at least +.i N +blocks free on the filesystem that holds the queue files +before accepting email via SMTP. +If there is insufficient space +.i sendmail +gives a 452 response +to the MAIL command. +This invites the sender to try again later. +The optional +.i M +is a maximum message size advertised in the ESMTP EHLO response. +It is currently otherwise unused. +.ip B\fIc\fP +Set the blank substitution character to +.i c . +Unquoted spaces in addresses are replaced by this character. +Defaults to space (i.e., no change is made). +.ip c +If an outgoing mailer is marked as being expensive, +don't connect immediately. +This requires that queueing be compiled in, +since it will depend on a queue run process to +actually send the mail. +.ip C\fIN\fP +Checkpoints the queue every +.i N +(default 10) +addresses sent. +If your system crashes during delivery to a large list, +this prevents retransmission to any but the last +.I N +recipients. +.ip d\fIx\fP +Deliver in mode +.i x . +Legal modes are: +.(b +.ta 4n +i Deliver interactively (synchronously) +b Deliver in background (asynchronously) +q Just queue the message (deliver during queue run) +.)b +Defaults to ``b'' if no option is specified, +``i'' if it is specified but given no argument +(i.e., ``Od'' is equivalent to ``Odi''). +.ip D +If set, +rebuild the alias database if necessary and possible. +If this option is not set, +.i sendmail +will never rebuild the alias database +unless explicitly requested +using +.b \-bi . +.ip e\fIx\fP +Dispose of errors using mode +.i x . +The values for +.i x +are: +.(b +p Print error messages (default) +q No messages, just give exit status +m Mail back errors +w Write back errors (mail if user not logged in) +e Mail back errors and give zero exit stat always +.)b +.ip E\fIfile/message\fP +Prepend error messages with the indicated message. +If it begins with a slash, +it is assumed to be the pathname of a file +containing a message (this is the recommended setting). +Otherwise, it is a literal message. +The error file might contain the name, email address, and/or phone number +of a local postmaster who could provide assistance +in to end users. +If the option is missing or null, +or if it names a file which does not exist or which is not readable, +no message is printed. +.ip f +Save +Unix-style +.q From +lines at the front of headers. +Normally they are assumed redundant +and discarded. +.ip F\fImode\fP +The file mode for queue files. +.ip g\fIn\fP +Set the default group id +for mailers to run in +to +.i n . +Defaults to 1. +.ip G +Allow fuzzy matching on the GECOS field. +If this flag is set, +and the usual user name lookups fail +(that is, there is no alias with this name and a +.i getpwnam +fails), +sequentially search the password file +for a matching entry in the GECOS field. +This also requires that MATCHGECOS +be turned on during compilation. +This option is not recommended. +.ip h\fIN\fP +The maximum hop count. +Messages that have been processed more than +.i N +times are assumed to be in a loop and are rejected. +Defaults to 25. +.ip H\fIfile\fP +Specify the help file +for SMTP. +.ip i +Ignore dots in incoming messages. +This is always disabled (that is, dots are always accepted) +when reading SMTP mail. +.ip I +Insist that the BIND name server be running +to resolve host names. +If this is not set and the name server is not running, +the +.i /etc/hosts +file will be considered complete. +In general, you do want to set this option +if your +.i /etc/hosts +file does not include all hosts known to you +or if you are using the MX (mail forwarding) feature of the BIND name server. +The name server will still be consulted +even if this option is not set, but +.i sendmail +will feel free to resort to reading +.i /etc/hosts +if the name server is not available. +Thus, you should +.i never +set this option if you do not run the name server. +.ip j +If set, send error messages in MIME format +(see RFC1341 and RFC1344 for details). +.ip J\fIpath\fP +Set the path for searching for users' .forward files. +The default is +.q $z/.forward . +Some sites that use the automounter may prefer to change this to +.q /var/forward/$u +to search a file with the same name as the user in a system directory. +It can also be set to a sequence of paths separated by colons; +.i sendmail +stops at the first file it can successfully and safely open. +For example, +.q /var/forward/$u:$z/.forward +will search first in /var/forward/\c +.i username +and then in +.i ~username /.forward +(but only if the first file does not exist). +.ip k\fIN\fP +The maximum number of open connections that will be cached at a time. +The default is one. +This delays closing the the current connection until +either this invocation of sendmail needs to connect to another host +or it terminates. +Setting it to zero defaults to the old behavior, +that is, connections are closed immediately. +.ip K\fItimeout\fP +The maximum amount of time a cached connection will be permitted to idle +without activity. +If this time is exceeded, +the connection is immediately closed. +This value should be small (on the order of ten minutes). +Before +.b sendmail +uses a cached connection, +it always sends a NOOP (no operation) command +to check the connection; +if this fails, it reopens the connection. +This keeps your end from failing if the other end times out. +The point of this option is to be a good network neighbor +and avoid using up excessive resources +on the other end. +The default is five minutes. +.ip l +If there is an +.q Errors-To: +header, send error messages to the addresses listed there. +They normally go to the envelope sender. +Use of this option causes sendmail to violate RFC 1123. +.ip L\fIn\fP +Set the default log level to +.i n . +Defaults to 9. +.ip m +Send to me too, +even if I am in an alias expansion. +.ip M\fIx\|value\fP +Set the macro +.i x +to +.i value . +This is intended only for use from the command line. +.ip n +Validate the RHS of aliases when rebuilding the alias database. +.ip o +Assume that the headers may be in old format, +i.e., +spaces delimit names. +This actually turns on +an adaptive algorithm: +if any recipient address contains a comma, parenthesis, +or angle bracket, +it will be assumed that commas already exist. +If this flag is not on, +only commas delimit names. +Headers are always output with commas between the names. +.ip O\fIoptions\fP +Set server SMTP options. +The options are +.i key=value +pairs. +Known keys are: +.(b +.ta 1i +Port Name/number of listening port (defaults to "smtp") +Addr Address mask (defaults INADDR_ANY) +Family Address family (defaults to INET) +Listen Size of listen queue (defaults to 10) +.)b +The +.i Addr ess +mask may be a numeric address in dot notation +or a network name. +.ip p\fI\|opt,opt,...\fP +Set the privacy +.i opt ions. +``Privacy'' is really a misnomer; +many of these are just a way of insisting on stricter adherence +to the SMTP protocol. +The +.i opt ions +can be selected from: +.(b +.ta \w'needvrfyhelo'u+3n +public Allow open access +needmailhelo Insist on HELO or EHLO command before MAIL +needexpnhelo Insist on HELO or EHLO command before EXPN +noexpn Disallow EXPN entirely +needvrfyhelo Insist on HELO or EHLO command before VRFY +novrfy Disallow VRFY entirely +restrictmailq Restrict mailq command +goaway Disallow essentially all SMTP status queries +.)b +The +.q goaway +pseudo-flag sets all flags except +.q restrictmailq . +If mailq is restricted, +only people in the same group as the queue directory +can print the queue. +.ip P\fIpostmaster\fP +If set, +copies of error messages will be sent to the named +.i postmaster . +Only the header of the failed message is sent. +Since most errors are user problems, +this is probably not a good idea on large sites, +and arguably contains all sorts of privacy violations, +but it seems to be popular with certain operating systems vendors. +.ip q\fIfactor\fP +Use +.i factor +as the multiplier in the map function +to decide when to just queue up jobs rather than run them. +This value is divided by the difference between the current load average +and the load average limit +(\c +.b x +flag) +to determine the maximum message priority +that will be sent. +Defaults to 600000. +.ip Q\fIdir\fP +Use the named +.i dir +as the queue directory. +.ip r\|\fItimeouts\fP +Timeout reads after +.i time +interval. +The +.i timeouts +argument is a list of +.i keyword=value +pairs. +The recognized timeouts and their default values, and their +minimum values specified in RFC 1123 section 5.3.2 are: +.(b +.ta \w'datafinal'u+3n +initial wait for initial greeting message [5m, 5m] +helo reply to HELO or EHLO command [5m, none] +mail reply to MAIL command [10m, 5m] +rcpt reply to RCPT command [1h, 5m] +datainit reply to DATA command [5m, 2m] +datablock data block read [1h, 3m] +datafinal reply to final ``.'' in data [1h, 10m] +rset reply to RSET command [5m, none] +quit reply to QUIT command [2m, none] +misc reply to NOOP and VERB commands [2m, none] +command command read [1h, 5m] +.)b +All but +.q command +apply to client SMTP. +For back compatibility, +a timeout with no ``keyword='' part +will set all of the longer values. +.ip R +Normally, +.i sendmail +tries to eliminate any unnecessary explicit routes +when sending an error message +(as discussed in RFC 1123 \(sc 5.2.6). +For example, +when sending an error message to +.(b +<@known1,@known2,@unknown:user@known3> +.)b +.i sendmail +will strip off the +.q @known1 +in order to make the route as direct as possible. +However, if the +.b R +option is set, this will be disabled, +and the mail will be sent to the first address in the route, +even if later addresses are known. +This may be useful if you are caught behind a firewall. +.ip s +Be super-safe when running things, +i.e., +always instantiate the queue file, +even if you are going to attempt immediate delivery. +.i Sendmail +always instantiates the queue file +before returning control the the client +under any circumstances. +.ip S\fIfile\fP +Log statistics in the named +.i file . +.ip t\fIS,D\fP +Set the local time zone name to +.i S +for standard time and +.i D +for daylight time; +this is only used under version six. +.ip T\fIrtime/wtime\fP +Set the queue timeout to +.i rtime . +After this interval, +messages that have not been successfully sent +will be returned to the sender. +Defaults to five days. +The optional +.i wtime +is the time after which a warning message is sent. +If it is missing or zero +then no warning messages are sent. +.ip u\fIn\fP +Set the default userid for mailers to +.i n . +Mailers without the +.i S +flag in the mailer definition +will run as this user. +Defaults to 1. +.ip U\fIudbspec\fP +The user database specification. +.ip v +Run in verbose mode. +If this is set, +.i sendmail +adjusts options +.b c +(don't connect to expensive mailers) +and +.b d +(delivery mode) +so that all mail is delivered completely +in a single job +so that you can see the entire delivery process. +Option +.b v +should +.i never +be set in the configuration file; +it is intended for command line use only. +.ip V\fIfallbackhost\fP +If specified, the +.i fallbackhost +acts like a very low priority MX +on every host. +This is intended to be used by sites with poor network connectivity. +.ip x\fILA\fP +When the system load average exceeds +.i LA , +just queue messages +(i.e., don't try to send them). +Defaults to 8. +.ip X\fILA\fP +When the system load average exceeds +.i LA , +refuse incoming SMTP connections. +Defaults to 12. +.ip y\fIfact\fP +The indicated +.i fact or +is added to the priority (thus +.i lowering +the priority of the job) +for each recipient, +i.e., this value penalizes jobs with large numbers of recipients. +Defaults to 30000. +.ip Y +If set, +deliver each job that is run from the queue in a separate process. +Use this option if you are short of memory, +since the default tends to consume considerable amounts of memory +while the queue is being processed. +.ip z\fIfact\fP +The indicated +.i fact or +is multiplied by the message class +(determined by the Precedence: field in the user header +and the +.b P +lines in the configuration file) +and subtracted from the priority. +Thus, messages with a higher Priority: will be favored. +Defaults to 1800. +.ip Z\fIfact\fP +The +.i fact or +is added to the priority +every time a job is processed. +Thus, +each time a job is processed, +its priority will be decreased by the indicated value. +In most environments this should be positive, +since hosts that are down are all too often down for a long time. +Defaults to 90000. +.ip 7 +Strip input to seven bits for compatibility with old systems. +This shouldn't be necessary. +.lp +All options can be specified on the command line using the +\-o flag, +but most will cause +.i sendmail +to relinquish its setuid permissions. +The options that will not cause this are +b, d, e, E, i, L, m, o, p, r, s, v, C, and 7. +Also, M (define macro) when defining the r or s macros +is also considered +.q safe . +.sh 3 "P \*- precedence definitions" +.pp +Values for the +.q "Precedence:" +field may be defined using the +.b P +control line. +The syntax of this field is: +.(b +\fBP\fP\fIname\fP\fB=\fP\fInum\fP +.)b +When the +.i name +is found in a +.q Precedence: +field, +the message class is set to +.i num . +Higher numbers mean higher precedence. +Numbers less than zero +have the special property +that if an error occurs during processing +the body of the message will not be returned; +this is expected to be used for +.q "bulk" +mail such as through mailing lists. +The default precedence is zero. +For example, +our list of precedences is: +.(b +Pfirst-class=0 +Pspecial-delivery=100 +Plist=\-30 +Pbulk=\-60 +Pjunk=\-100 +.)b +People writing mailing list exploders +are encouraged to use +.q "Precedence: list" . +Older versions of +.i sendmail +(which discarded all error returns for negative precedences) +didn't recognize this name, giving it a default precedence of zero. +This allows list maintainers to see error returns +on both old and new versions of +.i sendmail . +.sh 3 "V \*- configuration version level" +.pp +To provide compatibility with old configuration files, +the +.b V +line has been added to define some very basic semantics +of the configuration file. +These are not intended to be long term supports; +rather, they describe compatibility features +which will probably be removed in future releases. +.pp +.q Old +configuration files are defined as version level one. +Version level two files make the following changes: +.np +Host name canonification ($[ ... $]) +appends a dot if the name is recognized; +this gives the config file a way of finding out if anything matched. +(Actually, this just initializes the +.q host +map with the +.q \-a. +flag \*- you can reset it to anything you prefer +by declaring the map explicitly.) +.np +Default host name extension is consistent throughout processing; +version level one configurations turned off domain extension +(that is, adding the local domain name) +during certain points in processing. +Version level two configurations are expected to include a trailing dot +to indicate that the name is already canonical. +.np +Local names that are not aliases +are passed through a new distinguished ruleset five; +this can be used to append a local relay. +This behaviour can be prevented by resolving the local name +with an initial `@'. +That is, something that resolves to a local mailer and a user name of +.q vikki +will be passed through ruleset five, +but a user name of +.q @vikki +will have the `@' stripped, +will not be passed through ruleset five, +but will otherwise be treated the same as the prior example. +The expectation is that this might be used to implement a policy +where mail sent to +.q vikki +was handled by a central hub, +but mail sent to +.q vikki@localhost +was delivered directly. +.pp +Version level three files +allow # initiated comments on all lines. +Exceptions are backslash escaped # marks +and the $# syntax. +.sh 3 "K \*- key file declaration" +.pp +Special maps can be defined using the line: +.(b +Kmapname mapclass arguments +.)b +The +.i mapname +is the handle by which this map is referenced in the rewriting rules. +The +.i mapclass +is the name of a type of map; +these are compiled in to sendmail. +The +.i arguments +are interpreted depending on the class; +typically, +there would be a single argument naming the file containing the map. +.pp +Maps are referenced using the syntax: +.(b +$( \fImap\fP \fIkey\fP $@ \fIarguments\fP $: \fIdefault\fP $) +.)b +where either or both of the +.i arguments +or +.i default +portion may be omitted. +The +.i arguments +may appear more than once. +The indicated +.i key +and +.i arguments +are passed to the appropriate mapping function. +If it returns a value, it replaces the input. +If it does not return a value and the +.i default +is specified, the +.i default +replaces the input. +Otherwise, the input is unchanged. +.pp +During replacement of either a map value or default +the string +.q %\fIn\fP +(where +.i n +is a digit) +is replaced by the corresponding +.i argument . +Argument zero +is always the database key. +For example, the rule +.(b +.ta 1.5i +R$- ! $+ $: $(uucp $1 $@ $2 $: %1 @ %0 . UUCP $) +.)b +Looks up the UUCP name in a (user defined) UUCP map; +if not found it turns it into +.q \&.UUCP +form. +The database might contain records like: +.(b +decvax %1@%0.DEC.COM +research %1@%0.ATT.COM +.)b +.pp +The built in map with both name and class +.q host +is the host name canonicalization lookup. +Thus, +the syntax: +.(b +$(host \fIhostname\fP$) +.)b +is equivalent to: +.(b +$[\fIhostname\fP$] +.)b +.pp +There are four predefined database lookup classes: +.q dbm , +.q btree , +.q hash , +and +.q nis . +The first requires that sendmail be compiled with the +.b ndbm +library; +the second two require the +.b db +library, +and the third requires that sendmail be compiled with NIS support. +All four accept as arguments the some optional flags +and a filename (or a mapname for NIS). +Known flags are: +.ip "\-o" +Indicates that this map is optional \*- that is, +if it cannot be opened, +no error is produced, +and sendmail will behave as if the map existed but was empty. +.ip "\-N" +Normally when maps are written, +the trailing null byte is not included as part of the key. +If this flag is indicated it will be included. +During lookups, only the null-byte-included form will be searched. +See also +.b \-O. +.ip "\-O" +If neither +.b \-N +or +.b \-O +are specified, +.i sendmail +uses an adaptive algorithm to decide whether or not to look for null bytes +on the end of keys. +It starts by trying both; +if it finds any key with a null byte it never tries again without a null byte +and vice versa. +If this flag is specified, +it never tries with a null byte; +this can speed matches but is never necessary. +If both +.b \-N +and +.b \-O +are specified, +.i sendmail +will never try any matches at all \(em +that is, everything will appear to fail. +.ip "\-a\fIx\fP" +Append the character +.i x +on successful matches. +For example, the default +.i host +map appends a dot on successful matches. +.ip "\-f" +Fold upper to lower case before looking up the key. +.ip "\-m" +Match only (without replacing the value). +If you only care about the existence of a key and not the value +(as you might when searching the NIS map +.q hosts.byname +for example), +this flag prevents the map from substituting the value. +However, +The \-a argument is still appended on a match, +and the default is still taken if the match fails. +.pp +The +.i dbm +map appends the strings +.q \&.pag +and +.q \&.dir +to the given filename; +the two +.i db -based +maps append +.q \&.db . +.pp +The program +.i makemap (8) +can be used to build any of the three database-oriented maps. +It takes the following flags: +.ip \-f +Do not fold upper to lower case in the map. +.ip \-N +Include null bytes in keys. +.ip \-o +Append to an existing (old) file. +.ip \-r +Allow replacement of existing keys; +normally, re-inserting an existing key is an error. +.ip \-v +Print what is happening. +.pp +There are also two builtin maps that are, +strictly speaking, +not database lookups. +.pp +The +.q host +map does host domain canonification; +given a host name it calls the name server +to find the canonical name for that host. +.pp +The +.q dequote +map strips double quotes (") from a name. +It does not strip backslashes. +It will not strip quotes if the resulting string +would contain unscannable syntax +(that is, basic errors like unbalanced angle brackets; +more sophisticated errors such as unknown hosts are not checked). +The intent is for use when trying to accept mail from systems such as +DECnet +that routinely quote odd syntax such as +.(b +"49ers::ubell" +.)b +A typical usage is probably something like: +.(b +Kdequote dequote + +\&... + +R$\- $: $(dequote $1 $) +R$\- $+ $: $>3 $1 $2 +.)b +Care must be taken to prevent unexpected results; +for example, +.(b +"|someprogram < input > output" +.)b +will have quotes stripped, +but the result is probably not what you had in mind. +Fortunately these cases are rare. +.pp +New classes can be added in the routine +.b setupmaps +in file +.b conf.c . +.sh 2 "Building a Configuration File From Scratch" +.pp +Building a configuration table from scratch is an extremely difficult job. +Fortunately, +it is almost never necessary to do so; +nearly every situation that may come up +may be resolved by changing an existing table. +In any case, +it is critical that you understand what it is that you are trying to do +and come up with a philosophy for the configuration table. +This section is intended to explain what the real purpose +of a configuration table is +and to give you some ideas +for what your philosophy might be. +.pp +.b "Do not even consider" +writing your own configuration file +without carefully studying +RFC 821, 822, and 1123. +You should also read RFC 976 +if you are doing UUCP exchange. +.sh 3 "What you are trying to do" +.pp +The configuration table has three major purposes. +The first and simplest +is to set up the environment for +.i sendmail . +This involves setting the options, +defining a few critical macros, +etc. +Since these are described in other places, +we will not go into more detail here. +.pp +The second purpose is to rewrite addresses in the message. +This should typically be done in two phases. +The first phase maps addresses in any format +into a canonical form. +This should be done in ruleset three. +The second phase maps this canonical form +into the syntax appropriate for the receiving mailer. +.i Sendmail +does this in three subphases. +Rulesets one and two +are applied to all sender and recipient addresses respectively. +After this, +you may specify per-mailer rulesets +for both sender and recipient addresses; +this allows mailer-specific customization. +Finally, +ruleset four is applied to do any default conversion +to external form. +.pp +The third purpose +is to map addresses into the actual set of instructions +necessary to get the message delivered. +Ruleset zero must resolve to the internal form, +which is in turn used as a pointer to a mailer descriptor. +The mailer descriptor describes the interface requirements +of the mailer. +.sh 3 "Philosophy" +.pp +The particular philosophy you choose will depend heavily +on the size and structure of your organization. +I will present a few possible philosophies here. +There are as many philosophies as there are config designers; +feel free to develop your own. +.pp +One general point applies to all of these philosophies: +it is almost always a mistake +to try to do full host route resolution. +For example, +if you are on a UUCP-only site +and you are trying to get names of the form +.q user@host +to the Internet, +it does not pay to route them to +.q xyzvax!decvax!ucbvax!c70!user@host +since you then depend on several links not under your control, +some of which are likely to misparse it anyway. +The best approach to this problem +is to simply forward the message for +.q user@host +to +.q xyzvax +and let xyzvax +worry about it from there. +In summary, +just get the message closer to the destination, +rather than determining the full path. +.sh 4 "Large site, many hosts \*- minimum information" +.pp +Berkeley is an example of a large site, +i.e., more than two or three hosts +and multiple mail connections. +We have decided that the only reasonable philosophy +in our environment +is to designate one host as the guru for our site. +It must be able to resolve any piece of mail it receives. +The other sites should have the minimum amount of information +they can get away with. +In addition, +any information they do have +should be hints rather than solid information. +.pp +For example, +a typical site on our local ether network is +.q monet +(actually +.q monet.CS.Berkeley.EDU ). +When monet receives mail for delivery, +it checks whether it knows +that the destination host is directly reachable; +if so, mail is sent to that host. +If it receives mail for any unknown host, +it just passes it directly to +.q ucbvax.CS.Berkeley.EDU , +our master host. +Ucbvax may determine that the host name is illegal +and reject the message, +or may be able to do delivery. +However, it is important to note that when a new mail connection is added, +the only host that +.i must +have its tables updated +is ucbvax; +the others +.i may +be updated if convenient, +but this is not critical. +.pp +This picture is slightly muddied +due to network connections that are not actually located +on ucbvax. +For example, +some UUCP connections are currently on +.q ucbarpa. +However, +monet +.i "does not" +know about this; +the information is hidden totally between ucbvax and ucbarpa. +Mail going from monet to a UUCP host +is transferred via the ethernet +from monet to ucbvax, +then via the ethernet from ucbvax to ucbarpa, +and then is submitted to UUCP. +Although this involves some extra hops, +we feel this is an acceptable tradeoff. +.pp +An interesting point is that it would be possible +to update monet +to send appropriate UUCP mail directly to ucbarpa +if the load got too high; +if monet failed to note a host as connected to ucbarpa +it would go via ucbvax as before, +and if monet incorrectly sent a message to ucbarpa +it would still be sent by ucbarpa +to ucbvax as before. +The only problem that can occur is loops, +for example, +if ucbarpa thought that ucbvax had the UUCP connection +and vice versa. +For this reason, +updates should +.i always +happen to the master host first. +.pp +This philosophy results as much from the need +to have a single source for the configuration files +(typically built using +.i m4 \|(1) +or some similar tool) +as any logical need. +Maintaining more than three separate tables by hand +is essentially an impossible job. +.sh 4 "Small site \*- complete information" +.pp +A small site +(two or three hosts and few external connections) +may find it more reasonable to have complete information +at each host. +This would require that each host +know exactly where each network connection is, +possibly including the names of each host on that network. +As long as the site remains small +and the the configuration remains relatively static, +the update problem will probably not be too great. +.sh 4 "Single host" +.pp +This is in some sense the trivial case. +The only major issue is trying to insure that you don't +have to know too much about your environment. +For example, +if you have a UUCP connection +you might find it useful to know about the names of hosts +connected directly to you, +but this is really not necessary +since this may be determined from the syntax. +.sh 4 "A completely different philosophy" +.pp +This is adapted from Bruce Lilly. +Any errors in interpretation are mine. +.pp +Do minimal changes in ruleset 3: +fix some common but unambiguous errors (e.g. trailing dot on domains) and +hide bang paths foo!bar into bar@foo.UUCP. +The resulting "canonical" form is any valid RFC822/RFC1123/RFC976 address. +.pp +Ruleset 0 does the bulk of the work. +It removes the trailing "@.UUCP" that hides bang paths, +strips anything not needed to resolve, +e.g. the phrase from phrase <route-addr> and from named groups, +rejects unparseable addresses using $#error, +and finally +resolves to a mailer/host/user triple. +Ruleset 0 is rather lengthy +as it has to handle 3 basic address forms: +RFC976 bang paths, +RFC1123 %-hacks +(including vanilla RFC822 local-part@domain), +and RFC822 source routes. +It's also complicated by having to handle named lists. +.pp +The header rewriting rulesets 1 and 2 +remove the trailing "@.UUCP" that hides bang paths. +Ruleset 2 also strips the $# mailer $@ host (for test mode). +.pp +Ruleset 4 does absolutely nothing. +.pp +The per-mailer rewriting rulesets conform the envelope and +header addresses to the requirements of the specific +mailer. +.pp +Lots of rulesets-as-subroutines are used. +.pp +As a result, header addresses are subject to minimal munging +(per RFC1123), and the general plan is per RFC822 sect. 3.4.10. +.sh 3 "Relevant issues" +.pp +The canonical form you use +should almost certainly be as specified in +the Internet protocols +RFC819 and RFC822. +Copies of these RFC's are included on the +.i sendmail +tape +as +.i doc/rfc819.lpr +and +.i doc/rfc822.lpr . +.pp +RFC822 +describes the format of the mail message itself. +.i Sendmail +follows this RFC closely, +to the extent that many of the standards described in this document +can not be changed without changing the code. +In particular, +the following characters have special interpretations: +.(b +< > ( ) " \e +.)b +Any attempt to use these characters for other than their RFC822 +purpose in addresses is probably doomed to disaster. +.pp +RFC819 +describes the specifics of the domain-based addressing. +This is touched on in RFC822 as well. +Essentially each host is given a name +which is a right-to-left dot qualified pseudo-path +from a distinguished root. +The elements of the path need not be physical hosts; +the domain is logical rather than physical. +For example, +at Berkeley +one legal host might be +.q a.CC.Berkeley.EDU ; +reading from right to left, +.q EDU +is a top level domain +comprising educational institutions, +.q Berkeley +is a logical domain name, +.q CC +represents the Computer Center, +(in this case a strictly logical entity), +and +.q a +is a host in the Computer Center. +.pp +Beware when reading RFC819 +that there are a number of errors in it. +.sh 3 "How to proceed" +.pp +Once you have decided on a philosophy, +it is worth examining the available configuration tables +to decide if any of them are close enough +to steal major parts of. +Even under the worst of conditions, +there is a fair amount of boiler plate that can be collected safely. +.pp +The next step is to build ruleset three. +This will be the hardest part of the job. +Beware of doing too much to the address in this ruleset, +since anything you do will reflect through +to the message. +In particular, +stripping of local domains is best deferred, +since this can leave you with addresses with no domain spec at all. +Since +.i sendmail +likes to append the sending domain to addresses with no domain, +this can change the semantics of addresses. +Also try to avoid +fully qualifying domains in this ruleset. +Although technically legal, +this can lead to unpleasantly and unnecessarily long addresses +reflected into messages. +The Berkeley configuration files +define ruleset nine +to qualify domain names and strip local domains. +This is called from ruleset zero +to get all addresses into a cleaner form. +.pp +Once you have ruleset three finished, +the other rulesets should be relatively trivial. +If you need hints, +examine the supplied configuration tables. +.sh 3 "Testing the rewriting rules \*- the \-bt flag" +.pp +When you build a configuration table, +you can do a certain amount of testing +using the +.q "test mode" +of +.i sendmail . +For example, +you could invoke +.i sendmail +as: +.(b +sendmail \-bt \-Ctest.cf +.)b +which would read the configuration file +.q test.cf +and enter test mode. +In this mode, +you enter lines of the form: +.(b +rwset address +.)b +where +.i rwset +is the rewriting set you want to use +and +.i address +is an address to apply the set to. +Test mode shows you the steps it takes +as it proceeds, +finally showing you the address it ends up with. +You may use a comma separated list of rwsets +for sequential application of rules to an input. +For example: +.(b +3,1,21,4 monet:bollard +.)b +first applies ruleset three to the input +.q monet:bollard. +Ruleset one is then applied to the output of ruleset three, +followed similarly by rulesets twenty-one and four. +.pp +If you need more detail, +you can also use the +.q \-d21 +flag to turn on more debugging. +For example, +.(b +sendmail \-bt \-d21.99 +.)b +turns on an incredible amount of information; +a single word address +is probably going to print out several pages worth of information. +.pp +You should be warned that internally, +.b sendmail +applies ruleset 3 to all addresses. +In this version of sendmail, you will have to do that manually. +For example, older versions allowed you to use +.(b +0 bruce@broadcast.sony.com +.)b +This version requires that you use: +.(b +3,0 bruce@broadcast.sony.com +.)b +.sh 3 "Building mailer descriptions" +.pp +To add an outgoing mailer to your mail system, +you will have to define the characteristics of the mailer. +.pp +Each mailer must have an internal name. +This can be arbitrary, +except that the names +.q local +and +.q prog +must be defined. +.pp +The pathname of the mailer must be given in the P field. +If this mailer should be accessed via an IPC connection, +use the string +.q [IPC] +instead. +.pp +The F field defines the mailer flags. +You should specify an +.q f +or +.q r +flag to pass the name of the sender as a +.b \-f +or +.b \-r +flag respectively. +These flags are only passed if they were passed to +.i sendmail, +so that mailers that give errors under some circumstances +can be placated. +If the mailer is not picky +you can just specify +.q "\-f $g" +in the argv template. +If the mailer must be called as +.b root +the +.q S +flag should be given; +this will not reset the userid +before calling the mailer\**. +.(f +\**\c +.i Sendmail +must be running setuid to root +for this to work. +.)f +If this mailer is local +(i.e., will perform final delivery +rather than another network hop) +the +.q l +flag should be given. +Quote characters +(backslashes and " marks) +can be stripped from addresses if the +.q s +flag is specified; +if this is not given +they are passed through. +If the mailer is capable of sending to more than one user +on the same host +in a single transaction +the +.q m +flag should be stated. +If this flag is on, +then the argv template containing +.b $u +will be repeated for each unique user +on a given host. +The +.q e +flag will mark the mailer as being +.q expensive, +which will cause +.i sendmail +to defer connection +until a queue run\**. +.(f +\**The +.q c +configuration option must be given +for this to be effective. +.)f +.pp +An unusual case is the +.q C +flag. +This flag applies to the mailer that the message is received from, +rather than the mailer being sent to; +if set, +the domain spec of the sender +(i.e., the +.q @host.domain +part) +is saved +and is appended to any addresses in the message +that do not already contain a domain spec. +For example, +a message of the form: +.(b +From: eric@vangogh.CS.Berkeley.EDU +To: wnj@monet.CS.Berkeley.EDU, mckusick +.)b +will be modified to: +.(b +From: eric@vangogh.CS.Berkeley.EDU +To: wnj@monet.CS.Berkeley.EDU, mckusick@vangogh.CS.Berkeley.EDU +.)b +.i "if and only if" +the +.q C +flag is defined in the mailer corresponding to +.q eric@vangogh.CS.Berkeley.EDU. +.pp +Other flags are described +in Appendix C. +.pp +The S and R fields in the mailer description +are per-mailer rewriting sets +to be applied to sender and recipient addresses +respectively. +These are applied after the sending domain is appended +and the general rewriting sets +(numbers one and two) +are applied, +but before the output rewrite +(ruleset four) +is applied. +A typical use is to append the current domain +to addresses that do not already have a domain. +For example, +a header of the form: +.(b +From: eric +.)b +might be changed to be: +.(b +From: eric@vangogh.CS.Berkeley.EDU +.)b +or +.(b +From: ucbvax!eric +.)b +depending on the domain it is being shipped into. +These sets can also be used +to do special purpose output rewriting +in cooperation with ruleset four. +.pp +The E field defines the string to use +as an end-of-line indication. +A string containing only newline is the default. +The usual backslash escapes +(\er, \en, \ef, \eb) +may be used. +.pp +Finally, +an argv template is given as the E field. +It may have embedded spaces. +If there is no argv with a +.b $u +macro in it, +.i sendmail +will speak SMTP +to the mailer. +If the pathname for this mailer is +.q [IPC], +the argv should be +.(b +IPC $h [ \fIport\fP ] +.)b +where +.i port +is the optional port number +to connect to. +.pp +For example, +the specifications: +.(b +.ta \w'Mlocal, 'u +\w'P=/bin/mail, 'u +\w'F=rlsm, 'u +\w'S=10, 'u +\w'R=20, 'u +Mlocal, P=/bin/mail, F=rlsm S=10, R=20, A=mail \-d $u +Mether, P=[IPC], F=meC, S=11, R=21, A=IPC $h, M=100000 +.)b +specifies a mailer to do local delivery +and a mailer for ethernet delivery. +The first is called +.q local, +is located in the file +.q /bin/mail, +takes a picky +.b \-r +flag, +does local delivery, +quotes should be stripped from addresses, +and multiple users can be delivered at once; +ruleset ten +should be applied to sender addresses in the message +and ruleset twenty +should be applied to recipient addresses; +the argv to send to a message will be the word +.q mail, +the word +.q \-d, +and words containing the name of the receiving user. +If a +.b \-r +flag is inserted +it will be between the words +.q mail +and +.q \-d. +The second mailer is called +.q ether, +it should be connected to via an IPC connection, +it can handle multiple users at once, +connections should be deferred, +and any domain from the sender address +should be appended to any receiver name +without a domain; +sender addresses should be processed by ruleset eleven +and recipient addresses by ruleset twenty-one. +There is a 100,000 byte limit on messages passed through this mailer. +.sh 2 "The User Database" +.pp +If you have a version of sendmail with the user database package +compiled in, +the handling of sender and recipient addresses +is modified. +.pp +The location of this database is controlled with the +.b U +option. +.sh 3 "Structure of the user database" +.pp +The database is a sorted (BTree-based) structure. +User records are stored with the key: +.(b +\fIuser-name\fP\fB:\fP\fIfield-name\fP +.)b +The sorted database format ensures that user records are clustered together. +Meta-information is always stored with a leading colon. +.pp +Field names define both the syntax and semantics of the value. +Defined fields include: +.nr ii 1i +.ip maildrop +The delivery address for this user. +There may be multiple values of this record. +In particular, +mailing lists will have one +.i maildrop +record for each user on the list. +.ip "mailname" +The outgoing mailname for this user. +For each outgoing name, +there should be an appropriate +.i maildrop +record for that name to allow return mail. +See also +.i :default:mailname . +.ip mailsender +Changes any mail sent to this address to have the indicated envelope sender. +This is intended for mailing lists, +and will normally be the name of an appropriate -request address. +It is very similar to the owner-\c +.i list +syntax in the alias file. +.ip fullname +The full name of the user. +.ip office-address +The office address for this user. +.ip office-phone +The office phone number for this user. +.ip office-fax +The office FAX number for this user. +.ip home-address +The home address for this user. +.ip home-phone +The home phone number for this user. +.ip home-fax +The home FAX number for this user. +.ip project +A (short) description of the project this person is affiliated with. +In the University this is often just the name of their graduate advisor. +.ip plan +A pointer to a file from which plan information can be gathered. +.pp +As of this writing, +only a few of these fields are actually being used by sendmail: +.i maildrop +and +.i mailname . +A +.i finger +program that uses the other fields is planned. +.sh 3 "User database semantics" +.pp +When the rewriting rules submit an address to the local mailer, +the user name is passed through the alias file. +If no alias is found (or if the alias points back to the same address), +the name (with +.q :maildrop +appended) +is then used as a key in the user database. +If no match occurs (or if the maildrop points at the same address), +forwarding is tried. +.pp +If the first token of the user name returned by ruleset 0 +is an +.q @ +sign, the user database lookup is skipped. +The intent is that the user database will act as a set of defaults +for a cluster (in our case, the Computer Science Division); +mail sent to a specific machine should ignore these defaults. +.pp +When mail is sent, +the name of the sending user is looked up in the database. +If that user has a +.q mailname +record, +the value of that record is used as their outgoing name. +For example, I might have a record: +.(b +eric:mailname Eric.Allman@CS.Berkeley.EDU +.)b +This would cause my outgoing mail to be sent as Eric.Allman. +.pp +If a +.q maildrop +is found for the user, +but no corresponding +.q maildrop +record exists, +the record +.q :default:mailname +is consulted. +If present, this is the name of a host to override the local host. +For example, in our case we would set it to +.q CS.Berkeley.EDU . +The effect is that anyone known in the database +gets their outgoing mail stamped as +.q user@CS.Berkeley.EDU , +but people not listed in the database use the local hostname. +.sh 1 "OTHER CONFIGURATION" +.pp +There are some configuration changes that can be made by +recompiling +.i sendmail . +This section describes what changes can be made +and what has to be modified to make them. +.sh 2 "Parameters in src/Makefile" +.pp +These parameters are intended to describe the compilation environment, +not site policy, +and should normally be defined in src/Makefile. +.ip NDBM +If set, +the new version of the DBM library +that allows multiple databases will be used. +If neither NDBM nor NEWDB are set, +a much less efficient method of alias lookup is used. +.ip NEWDB +If set, use the new database package from Berkeley (from 4.4BSD). +This package is substantially faster than DBM or NDBM. +If NEWDB and NDBM are both set, +sendmail will read DBM files, +but will create and use NEWDB files. +.ip YPCOMPAT +If set together with +.i both +NEWDB and NDBM, +.i sendmail +will create both DBM and NEWDB files if and only if +the file /var/yp/Makefile +exists and is readable. +This is intended for compatibility with Sun Microsystems' +.i mkalias +program used on YP masters. +.ip _AIX3 +Compile for IBM AIX 3.x. +This has only been tested on 3.2.3. +.ip SYSTEM5 +Set all of the compilation parameters appropriate for System V. +.ip LOCKF +Use System V +.b lockf +instead of Berkeley +.b flock . +Due to the highly unusual semantics of locks +across forks in +.b lockf , +this should never be used unless absolutely necessary. +Set by default if +SYSTEM5 is set. +.ip SYS5TZ +Use System V +time zone semantics. +.ip HASINITGROUPS +Set this if your system has the +.i initgroups() +call +(if you have multiple group support). +This is the default if SYSTEM5 is +.i not +defined or if you are on HPUX. +.ip HASUNAME +Set this if you have the +.i uname (2) +system call (or corresponding library routine). +Set by default if +SYSTEM5 +is set. +.ip HASSTATFS +Set this if you have the +.i statfs (2) +system call. +This will allow you to give a temporary failure +message to incoming SMTP email +when you are low on disk space. +It is set by default on 4.4BSD and OSF/1 systems. +.ip HASUSTAT +Set if you have the +.i ustat (2) +system call. +This is an alternative implementation of disk space control. +You should only set one of HASSTATFS or HASUSTAT; +the first is preferred. +.ip _PATH_SENDMAILCF +The pathname of the sendmail.cf file. +.ip _PATH_SENDMAILFC +The pathname of the sendmail.fc file. +.ip _PATH_SENDMAILPID +The pathname of the sendmail.pid file. +.ip LA_TYPE +The load average type. +Details are described below. +.lp +The are four built-in ways of computing the load average. +.i Sendmail +tries to auto-configure them based on imperfect guesses; +you can select one using the +.i cc +option +.b \-DLA_TYPE= \c +.i type , +where +.i type +is: +.ip LA_INT +The kernel stores the load average in the kernel as an array of long integers. +The actual values are scaled by a factor FSCALE +(default 256). +.ip LA_FLOAT +The kernel stores the load average in the kernel as an array of +double precision floats. +.ip LA_SUBR +Call the +.i getloadavg +routine to get the load average as an array of doubles. +.ip LA_ZERO +Always return zero as the load average. +This is the fallback case. +.lp +If type +.sm LA_INT +or +.sm LA_FLOAT +is specified, +you may also need to specify +.sm _PATH_UNIX +(the path to your system binary) +and +.sm LA_AVENRUN +(the name of the variable containing the load average in the kernel; +usually +.q _avenrun +or +.q avenrun ). +.sh 2 "Parameters in src/conf.h" +.pp +Parameters and compilation options +are defined in conf.h. +Most of these need not normally be tweaked; +common parameters are all in sendmail.cf. +However, the sizes of certain primitive vectors, etc., +are included in this file. +The numbers following the parameters +are their default value. +.nr ii 1.2i +.ip "MAXLINE [1024]" +The maximum line length of any input line. +If message lines exceed this length +they will still be processed correctly; +however, header lines, +configuration file lines, +alias lines, +etc., +must fit within this limit. +.ip "MAXNAME [256]" +The maximum length of any name, +such as a host or a user name. +.ip "MAXPV [40]" +The maximum number of parameters to any mailer. +This limits the number of recipients that may be passed in one transaction. +It can be set to any arbitrary number above about 10, +since +.i sendmail +will break up a delivery into smaller batches as needed. +A higher number may reduce load on your system, however. +.ip "MAXATOM [100]" +The maximum number of atoms +(tokens) +in a single address. +For example, +the address +.q "eric@CS.Berkeley.EDU" +is seven atoms. +.ip "MAXMAILERS [25]" +The maximum number of mailers that may be defined +in the configuration file. +.ip "MAXRWSETS [100]" +The maximum number of rewriting sets +that may be defined. +.ip "MAXPRIORITIES [25]" +The maximum number of values for the +.q Precedence: +field that may be defined +(using the +.b P +line in sendmail.cf). +.ip "MAXUSERENVIRON [40]" +The maximum number of items in the user environment +that will be passed to subordinate mailers. +.ip "QUEUESIZE [1000]" +The maximum number of entries that will be processed +in a single queue run. +.ip "MAXMXHOSTS [20]" +The maximum number of MX records we will accept for any single host. +.ip "MAXIPADDR [16]" +The maximum number of numeric IP addresses we will accept +for this host. +This does not limit the number the number of addresses for other hosts. +.lp +A number of other compilation options exist. +These specify whether or not specific code should be compiled in. +.nr ii 1.2i +.ip DEBUG +If set, debugging information is compiled in. +To actually get the debugging output, +the +.b \-d +flag must be used. +.b "WE STRONGLY RECOMMEND THAT THIS BE LEFT ON." +Some people, believing that it was a security hole +(it was, once) +have turned it off and thus crippled debuggers. +.ip NETINET +If set, +support for Internet protocol networking is compiled in. +Previous versions of +.b sendmail +referred to this as +.sm DAEMON ; +this old usage is now incorrect. +.ip NETISO +If set, +support for ISO protocol networking is compiled in +(it may be appropriate to #define this in the Makefile instead of conf.h). +.ip LOG +If set, +the +.i syslog +routine in use at some sites is used. +This makes an informational log record +for each message processed, +and makes a higher priority log record +for internal system errors. +.ip MATCHGECOS +Compile in the code to do ``fuzzy matching'' on the GECOS field +in /etc/passwd. +This also requires that option G be turned on. +.ip NAMED_BIND +Compile in code to use the +Berkeley Internet Name Domain (BIND) server +to resolve TCP/IP host names. +.ip NOTUNIX +If you are using a non-UNIX mail format, +you can set this flag to turn off special processing +of UNIX-style +.q "From " +lines. +.ip QUEUE +This flag should be set to compile in the queueing code. +If this is not set, +mailers must accept the mail immediately +or it will be returned to the sender. +.ip SETPROCTITLE +If defined, +.i sendmail +will change its +.i argv +array to indicate its current status. +This can be used in conjunction with the +.i ps +command to find out just what it's up to. +.ip SMTP +If set, +the code to handle user and server SMTP will be compiled in. +This is only necessary if your machine has some mailer +that speaks SMTP +(this means most machines everywhere). +.ip UGLYUUCP +If you have a UUCP host adjacent to you which is not running +a reasonable version of +.i rmail , +you will have to set this flag to include the +.q "remote from sysname" +info on the from line. +Otherwise, UUCP gets confused about where the mail came from. +.ip USERDB +Include the +.b experimental +Berkeley user information database package. +This adds a new level of local name expansion +between aliasing and forwarding. +It also uses the NEWDB package. +This may change in future releases. +.ip IDENTPROTO +Compile in the IDENT protocol as defined in RFC 1413. +This defaults on for all systems except Ultrix, +which apparently has the interesting +.q feature +that when it receives a +.q "host unreachable" +message it closes all open connections to that host. +Since some firewall gateways send this error code +when you access an unauthorized port (such as 113, used by IDENT), +Ultrix cannot receive email from such hosts. +.sh 2 "Configuration in src/conf.c" +.pp +The following changes can be made in conf.c. +.sh 3 "Built-in Header Semantics" +.pp +Not all header semantics are defined in the configuration file. +Header lines that should only be included by certain mailers +(as well as other more obscure semantics) +must be specified in the +.i HdrInfo +table in +.i conf.c . +This table contains the header name +(which should be in all lower case) +and a set of header control flags (described below), +The flags are: +.ip H_ACHECK +Normally when the check is made to see if a header line is compatible +with a mailer, +.i sendmail +will not delete an existing line. +If this flag is set, +.i sendmail +will delete +even existing header lines. +That is, +if this bit is set and the mailer does not have flag bits set +that intersect with the required mailer flags +in the header definition in +sendmail.cf, +the header line is +.i always +deleted. +.ip H_EOH +If this header field is set, +treat it like a blank line, +i.e., +it will signal the end of the header +and the beginning of the message text. +.ip H_FORCE +Add this header entry +even if one existed in the message before. +If a header entry does not have this bit set, +.i sendmail +will not add another header line if a header line +of this name already existed. +This would normally be used to stamp the message +by everyone who handled it. +.ip H_TRACE +If set, +this is a timestamp +(trace) +field. +If the number of trace fields in a message +exceeds a preset amount +the message is returned +on the assumption that it has an aliasing loop. +.ip H_RCPT +If set, +this field contains recipient addresses. +This is used by the +.b \-t +flag to determine who to send to +when it is collecting recipients from the message. +.ip H_FROM +This flag indicates that this field +specifies a sender. +The order of these fields in the +.i HdrInfo +table specifies +.i sendmail's +preference +for which field to return error messages to. +.nr ii 5n +.lp +Let's look at a sample +.i HdrInfo +specification: +.(b +.ta 4n +\w'"return-receipt-to", 'u +struct hdrinfo HdrInfo[] = +\&{ + /* originator fields, most to least significant */ + "resent-sender", H_FROM, + "resent-from", H_FROM, + "sender", H_FROM, + "from", H_FROM, + "full-name", H_ACHECK, + /* destination fields */ + "to", H_RCPT, + "resent-to", H_RCPT, + "cc", H_RCPT, + /* message identification and control */ + "message", H_EOH, + "text", H_EOH, + /* trace fields */ + "received", H_TRACE|H_FORCE, + + NULL, 0, +}; +.)b +This structure indicates that the +.q To: , +.q Resent-To: , +and +.q Cc: +fields +all specify recipient addresses. +Any +.q Full-Name: +field will be deleted unless the required mailer flag +(indicated in the configuration file) +is specified. +The +.q Message: +and +.q Text: +fields will terminate the header; +these are used by random dissenters around the network world. +The +.q Received: +field will always be added, +and can be used to trace messages. +.pp +There are a number of important points here. +First, +header fields are not added automatically just because they are in the +.i HdrInfo +structure; +they must be specified in the configuration file +in order to be added to the message. +Any header fields mentioned in the configuration file but not +mentioned in the +.i HdrInfo +structure have default processing performed; +that is, +they are added unless they were in the message already. +Second, +the +.i HdrInfo +structure only specifies cliched processing; +certain headers are processed specially by ad hoc code +regardless of the status specified in +.i HdrInfo . +For example, +the +.q Sender: +and +.q From: +fields are always scanned on ARPANET mail +to determine the sender\**; +.(f +\**Actually, this is no longer true in SMTP; +this information is contained in the envelope. +The older ARPANET protocols did not completely distinguish +envelope from header. +.)f +this is used to perform the +.q "return to sender" +function. +The +.q "From:" +and +.q "Full-Name:" +fields are used to determine the full name of the sender +if possible; +this is stored in the macro +.b $x +and used in a number of ways. +.sh 3 "Restricting Use of Email" +.pp +If it is necessary to restrict mail through a relay, +the +.i checkcompat +routine can be modified. +This routine is called for every recipient address. +It returns an exit status +indicating the status of the message. +The status +.sm EX_OK +accepts the address, +.sm EX_TEMPFAIL +queues the message for a later try, +and other values +(commonly +.sm EX_UNAVAILABLE ) +reject the message. +It is up to +.i checkcompat +to print an error message +(using +.i usrerr ) +if the message is rejected. +For example, +.i checkcompat +could read: +.(b +.re +.sz -1 +.ta 4n +4n +4n +4n +4n +4n +4n +int +checkcompat(to, e) + register ADDRESS *to; + register ENVELOPE *e; +\&{ + register STAB *s; + + s = stab("private", ST_MAILER, ST_FIND); + if (s != NULL && e\->e_from.q_mailer != LocalMailer && + to->q_mailer == s->s_mailer) + { + usrerr("No private net mail allowed through this machine"); + return (EX_UNAVAILABLE); + } + if (MsgSize > 50000 && to\->q_mailer != LocalMailer) + { + usrerr("Message too large for non-local delivery"); + NoReturn = TRUE; + return (EX_UNAVAILABLE); + } + return (EX_OK); +} +.sz +.)b +This would reject messages greater than 50000 bytes +unless they were local. +The +.i NoReturn +flag can be sent to suppress the return of the actual body +of the message in the error return. +The actual use of this routine is highly dependent on the +implementation, +and use should be limited. +.sh 3 "Load Average Computation" +.pp +The routine +.i getla +should return an approximation of the current system load average +as an integer. +There are four versions included on compilation flags +as described above. +.sh 3 "New Database Map Classes" +.pp +New key maps can be added by creating a class initialization function +and a lookup function. +These are then added to the routine +.i setupmaps. +.pp +The initialization function is called as +.(b +\fIxxx\fP_map_init(MAP *map, char *mapname, char *args) +.)b +The +.i map +is an internal data structure. +The +.i mapname +is the name of the map (used for error messages). +The +.i args +is a pointer to the rest of the configuration file line; +flags and filenames can be extracted from this line. +The initialization function must return +.sm TRUE +if it successfully opened the map, +.sm FALSE +otherwise. +.pp +The lookup function is called as +.(b +\fIxxx\fP_map_lookup(MAP *map, char buf[], int bufsize, char **av, int *statp) +.)b +The +.i map +defines the map internally. +The parameters +.i buf +and +.i bufsize +have the input key. +This may be (and often is) used destructively. +The +.i av +is a list of arguments passed in from the rewrite line. +The lookup function should return a pointer to the new value. +IF the map lookup fails, +.i *statp +should be set to an exit status code; +in particular, it should be set to +.sm EX_TEMPFAIL +if recovery is to be attempted by the higher level code. +.sh 3 "Queueing Function" +.pp +The routine +.i shouldqueue +is called to decide if a message should be queued +or processed immediately. +Typically this compares the message priority to the current load average. +The default definition is: +.(b +bool +shouldqueue(pri, ctime) + long pri; + time_t ctime; +{ + if (CurrentLA < QueueLA) + return (FALSE); + if (CurrentLA >= RefuseLA) + return (TRUE); + return (pri > (QueueFactor / (CurrentLA \- QueueLA + 1))); +} +.)b +If the current load average +(global variable +.i CurrentLA , +which is set before this function is called) +is less than the low threshold load average +(option +.b x , +variable +.i QueueLA ), +.i shouldqueue +returns +.sm FALSE +immediately +(that is, it should +.i not +queue). +If the current load average exceeds the high threshold load average +(option +.b X , +variable +.i RefuseLA ), +.i shouldqueue +returns +.sm TRUE +immediately. +Otherwise, it computes the function based on the message priority, +the queue factor +(option +.b q , +global variable +.i QueueFactor ), +and the current and threshold load averages. +.pp +An implementation wishing to take the actual age of the message into account +can also use the +.i ctime +parameter, +which is the time that the message was first submitted to +.i sendmail . +Note that the +.i pri +parameter is already weighted +by the number of times the message has been tried +(although this tends to lower the priority of the message with time); +the expectation is that the +.i ctime +would be used as an +.q "escape clause" +to ensure that messages are eventually processed. +.sh 3 "Refusing Incoming SMTP Connections" +.pp +The function +.i refuseconnections +returns +.sm TRUE +if incoming SMTP connections should be refused. +The current implementation is based exclusively on the current load average +and the refuse load average option +(option +.b X , +global variable +.i RefuseLA ): +.(b +bool +refuseconnections() +{ + return (CurrentLA >= RefuseLA); +} +.)b +A more clever implementation +could look at more system resources. +.sh 3 "Load Average Computation" +.pp +The routine +.i getla +returns the current load average (as a rounded integer). +The distribution includes several possible implementations. +.sh 2 "Configuration in src/daemon.c" +.pp +The file +.i src/daemon.c +contains a number of routines that are dependent +on the local networking environment. +The version supplied assumes you have BSD style sockets. +.pp +In previous releases, +we recommended that you modify the routine +.i maphostname +if you wanted to generalize +.b $[ +\&...\& +.b $] +lookups. +We now recommend that you create a new keyed map instead. +.sh 1 "CHANGES IN VERSION 8" +.pp +The following summarizes changes +since the last commonly available version of +.b sendmail +(5.67): +.sh 2 "Connection Caching" +.pp +Instead of closing SMTP connections immediately, +those connections are cached for possible future use. +The advent of MX records made this effective for mailing lists; +in addition, +substantial performance improvements can be expected for queue processing. +.sh 2 "MX Piggybacking" +.pp +If two hosts with different names in a single message +happen to have the same set of MX hosts, +they can be sent in the same transaction. +Version 8 notices this and tries to batch the messages. +.sh 2 "RFC 1123 Compliance" +.pp +A number of changes have been made to make +.i sendmail +.q "conditionally compliant" +(that is, +.i sendmail +satisfies all of the +.q MUST +clauses and most but not all of the +.q SHOULD +clauses in RFC 1123). +.pp +The major areas of change are (numbers are RFC 1123 section numbers): +.nr ii \w'5.3.1.1\0\0'u +.ip 5.2.7 +Response to RCPT command is fast. +.ip 5.2.8 +Numeric IP addresses are logged in Received: lines. +.ip 5.2.17 +Self domain literal is properly handled. +.ip 5.3.2 +Better control over individual timeouts. +.ip 5.3.3 +Error messages are sent as +.q From:<> . +.ip 5.3.3 +Error messages are never sent to +.q <> . +.ip 5.3.3 +Route-addrs are pruned. +.lp +The areas in which +.i sendmail +is not +.q "unconditionally compliant" +are: +.ip 5.2.6 +.i Sendmail +does do header munging. +.ip 5.2.10 +.i Sendmail +doesn't always use the exact SMTP message text +as listed in RFC 821. +.ip 5.3.1.1 +.i Sendmail +doesn't guarantee only one connect for each host in queue runs. +.ip 5.3.1.1 +.i Sendmail +doesn't always provide adequate concurrency limits. +.sh 2 "Extended SMTP Support" +.pp +Version 8 includes both sending and receiving support for Extended +SMTP support as defined by RFC 1425 (basic) and RFC 1427 (SIZE); +and limited support for RFC 1426 (BODY). +.sh 2 "Eight-Bit Clean" +.pp +Previous versions of +.b sendmail +used the 0200 bit for quoting. +This version avoids that use. +However, for compatibility with RFC 822, +you can set option `7' to get seven bit stripping. +.pp +Individual mailers can still produce seven bit out put using the +`7' mailer flag. +.sh 2 "User Database" +.pp +The user database is an as-yet experimental attempt +to provide unified large-site name support. +We are installing it at Berkeley; +future versions may show significant modifications. +.sh 2 "Improved BIND Support" +.pp +The BIND support, +particularly for MX records, +had a number of annoying +.q features +which have been removed in this release. +In particular, +these more tightly bind (pun intended) the name server to sendmail, +so that the name server resolution rules are incorporated directly into +.b sendmail . +.sh 2 "Keyed Files" +.pp +Generalized keyed files is an idea taken directly from +.sm IDA +.b sendmail +(albeit with a completely different implementation). +They can be useful on large sites. +.pp +Version 8 also understands YP. +.sh 2 "Multi-Word Classes" +.pp +Classes can now be multiple words. +For example, +.(b +CShofmann.CS.Berkeley.EDU +.)b +allows you to match the entire string +.q hofmann.CS.Berkeley.EDU +using the single construct +.q $=S . +.sh 2 "Deferred Macro Expansion" +.pp +The +.b $& \c +.i x +construct has been adopted from +.sm IDA . +.sh 2 "IDENT Protocol Support" +.pp +The IDENT protocol as defined in RFC 1413 is supported. +.sh 2 "Parsing Bug Fixes" +.pp +A number of small bugs having to do with things like +backslash-escaped quotes inside of comments +have been fixed. +.sh 2 "Separate Envelope/Header Processing" +.pp +Since the From: line is passed in separately from the envelope sender, +these have both been made visible; +the +.b $g +macro is set to the envelope sender during processing +of mailer argument vectors +and the header sender during processing of headers. +.pp +It is also possible to specify separate per-mailer +envelope and header processing. +The +.b S enderRWSet +and +.b R ecipientRWset +arguments for mailers +can be specified as +.i envelope/header +to give different rewritings for envelope versus header addresses. +.sh 2 "Owner-List Propagates to Envelope" +.pp +When an alias has an associated owner\-list name, +that alias is used to change the envelope sender address. +This will cause downstream errors to be returned to that owner. +.sh 2 "Dynamic Header Allocation" +.pp +The fixed size limit on header lines has been eliminated. +.sh 2 "New Command Line Flags" +.pp +The +.b \-B +flag has been added to pass in body type information. +.pp +The +.b \-p +flag has been added +to pass in protocol information. +.pp +The +.b \-X +flag has been added +to allow logging of all protocol in and out of +.i sendmail +for debugging. +.sh 2 "Enhanced Command Line Flags" +.pp +The +.b \-q +flag can limit limit a queue run to specific recipients, senders, or queue ids +using +.b \-qR\c +.i substring , +.b \-qS\c +.i substring , +or +.b \-qI\c +.i substring +respectively. +.sh 2 "New and Old Configuration Line Types" +.pp +The +.b T +(Trusted users) configuration line has been deleted. +It will still be accepted but will be ignored. +.pp +The +.b K +line has been added to declare database maps. +.pp +The +.b V +line has been added to declare the configuration version level. +.pp +The +.b M +line has a +.q D= +field that lets you change into a temporary directory while that mailer +is running. +.sh 2 "New Options" +.pp +Several new options have been added, +many to support new features, +others to allow tuning that was previously available +only by recompiling. +They are described in detail in Section 5.1.5. +Briefly, +.nr ii 0.5i +.ip b +Insist on a minimum number of disk blocks. +.ip C +Set checkpoint interval. +.ip E +Default error message. +.ip G +Enable GECOS matching. +.ip h +Maximum hop count. +.ip j +Send errors in MIME-encapsulated format. +.ip J +Forward file path. +.ip k +Connection cache size +.ip K +Connection cache lifetime. +.ip l +Enable Errors-To: header. +These headers violate RFC 1123; +this option is included to provide back compatibility +with old versions of sendmail. +.ip O +Set incoming SMTP daemon options, such as an alternate SMTP port. +.ip p +Privacy options. +.ip R +Don't prune route-addrs. +.ip U +User database spec. +.ip V +Fallback +.q MX +host. +.ip 7 +Do not run eight bit clean. +.sh 2 "Extended Options" +.pp +The +.b r +(read timeout), +.b I +(use BIND), +and +.b T +(queue timeout) +options have been extended to pass in more information. +.sh 2 "New Mailer Flags" +.pp +Several new mailer flags have been added. +.ip a +Try to use ESMTP when creating a connection. +If this is not set, +.i sendmail +will still try if the other end hints that it knows about ESMTP +in its greeting message; +this flag says to try even if it doesn't hint. +If the EHLO (extended hello) +command fails, +.i sendmail +falls back to old SMTP. +.ip b +Ensure that there is a blank line at the end of all messages. +.ip c +Strip all comments from addresses; +this should only be used as a last resort +when dealing with cranky mailers. +.ip g +Never use the null sender as the envelope sender, +even when running SMTP. +Although this violates RFC 1123, +it may be necessary when you must deal with some obnoxious old hosts. +.ip 7 +Strip all output to 7 bits. +.sh 2 "New Pre-Defined Macros" +.pp +The following macros are pre-defined: +.ip $k +The UUCP node name, +nominally from +.i uname (2) +call. +.ip $m +The domain part of our full hostname. +.ip $_ +The RFC 1413-provided sender address. +.sh 2 "New LHS Token" +.pp +Version 8 allows +.b $@ +on the Left Hand Side of an +.q R +line to match zero tokens. +This is intended to be used to match the null input. +.sh 2 "Bigger Defaults" +.pp +Version 8 allows up to 100 rulesets instead of 30. +It is recommended that rulesets 0\-9 be reserved for +.i sendmail 's +dedicated use in future releases. +.pp +The total number of MX records that can be used has been raised to 20. +.pp +The number of queued messages that can be handled at one time +has been raised from 600 to 1000. +.sh 2 "Different Default Tuning Parameters" +.pp +Version 8 has changed the default parameters +for tuning queue costs +to make the number of recipients more important +than the size of the message (for small messages). +This is reasonable if you are connected with reasonably fast links. +.sh 2 "Auto-Quoting in Addresses" +.pp +Previously, the +.q "Full Name <email address>" +syntax would generate incorrect protocol output +if +.q "Full Name" +had special characters such as dot. +This version puts quotes around such names. +.sh 2 "Symbolic Names On Error Mailer" +.pp +Several names have been built in to the $@ portion of the $#error +mailer. +.sh 2 "SMTP VRFY Doesn't Expand" +.pp +Previous versions of +.i sendmail +treated VRFY and EXPN the same. +In this version, +VRFY doesn't expand aliases or follow .forward files. +.pp +As an optimization, if you run with your default delivery mode being +queue-only, +the RCPT command will also not chase aliases and .forward files. +It will chase them when it processes the queue. +.sh 2 "[IPC] Mailers Allow Multiple Hosts" +.pp +When an address resolves to a mailer that has +.q [IPC] +as its +.q Path , +the $@ part (host name) +can be a colon-separated list of hosts instead of a single hostname. +This asks sendmail to search the list for the first entry that is available +exactly as though it were an MX record. +The intent is to route internal traffic through internal networks +without publishing an MX record to the net. +MX expansion is still done on the individual items. +.sh 2 "Aliases Extended" +.pp +The implementation has been merged with maps. +Among other things, +this supports NIS-based aliases. +.sh 2 "Portability and Security Enhancements" +.pp +A number of internal changes have been made to enhance portability. +.pp +Several fixes have been made to increase the paranoia factor. +.sh 2 "Miscellaneous Changes" +.pp +.i Sendmail +writes a +.i /etc/sendmail.pid +file with the current process id of the SMTP daemon. +.pp +Two people using the same program in their .forward file +are considered different +so that duplicate elimination doesn't delete one of them. +.pp +The +.i mailstats +program prints mailer names +and gets the location of the +.i sendmail.st +file from +.i /etc/sendmail.cf . +.pp +Many minor bugs have been fixed, such as handling of backslashes +inside of quotes. +.pp +A hook (ruleset 5) has been added +to allow rewriting of local addresses after aliasing. +.sh 1 "ACKNOWLEDGEMENTS" +.pp +I've worked on +.i sendmail +for many years, +and many employers have been remarkably patient +about letting me work on a large project +that was not part of my official job. +This includes time on the INGRES Project at Berkeley, +at Britton Lee, +and again on the Mammoth Project at Berkeley. +.pp +Much of the second wave of improvements +should be credited to Bryan Costales of ICSI. +As he passed me drafts of his book on +.i sendmail +I was inspired to start working on things again. +Bryan was also available to bounce ideas off of. +.pp +Many, many people contributed chunks of code and ideas to +.i sendmail . +It has proven to be a group network effort. +Version 8 in particular was a group project. +The following people made notable contributions: +.(l +Keith Bostic, CSRG, University of California, Berkeley +Michael J. Corrigan, University of California, San Diego +Bryan Costales, International Computer Science Institute +P{r (Pell) Emanuelsson +Craig Everhart, Transarc Corporation +Tom Ivar Helbekkmo, Norwegian School of Economics +Allan E. Johannesen, WPI +Takahiro Kanbe, FujiXerox +Brian Kantor, University of California, San Diego +Bruce Lilly, Sony U.S. +Nakamura Motonori, Kyoto University +John Gardiner Myers, Carnegie Mellon University +Neil Rickert, Northern Illinois University +Eric Wassenaar, National Institute for Nuclear and High Energy Physics, Amsterdam +Christophe Wolfhugel, Herve Schauer Consultants (Paris) +.)l +I apologize for anyone I have omitted, misspelled, misattributed, or +otherwise missed. +Many other people have contributed ideas, comments, and encouragement. +I appreciate their contribution as well. +.++ A +.+c "COMMAND LINE FLAGS" +.ba 0 +.nr ii 1i +.pp +Arguments must be presented with flags before addresses. +The flags are: +.ip \-b\fIx\fP +Set operation mode to +.i x . +Operation modes are: +.(b +.ta 4n +m Deliver mail (default) +s Speak SMTP on input side +d Run as a daemon +t Run in test mode +v Just verify addresses, don't collect or deliver +i Initialize the alias database +p Print the mail queue +z Freeze the configuration file +.)b +.ip \-B\fItype\fP +Indicate body type. +.ip \-C\fIfile\fP +Use a different configuration file. +.i Sendmail +runs as the invoking user (rather than root) +when this flag is specified. +.ip \-d\fIlevel\fP +Set debugging level. +.ip "\-f\ \fIaddr\fP" +The sender's machine address is +.i addr . +.ip \-F\fIname\fP +Sets the full name of this user to +.i name . +.ip "\-h\ \fIcnt\fP" +Sets the +.q "hop count" +to +.i cnt . +This represents the number of times this message has been processed +by +.i sendmail +(to the extent that it is supported by the underlying networks). +.i Cnt +is incremented during processing, +and if it reaches +MAXHOP +(currently 30) +.i sendmail +throws away the message with an error. +.ip \-n +Don't do aliasing or forwarding. +.ip "\-r\ \fIaddr\fP" +An obsolete form of +.b \-f . +.ip \-o\fIx\|value\fP +Set option +.i x +to the specified +.i value . +These options are described in Appendix B. +.ip \-p\fIprotocol\fP +Set the sending protocol. +Programs are encouraged to set this. +The protocol field can be in the form +.i protocol \c +.b : \c +.i host +to set both the sending protocol and sending host. +For example, +.q \-pUUCP:uunet +sets the sending protocol to UUCP +and the sending host to uunet. +(Some existing programs use \-oM to set the r and s macros; +this is equivalent to using \-p.) +.ip \-q\fItime\fP +Try to process the queued up mail. +If the time is given, +a sendmail will run through the queue at the specified interval +to deliver queued mail; +otherwise, it only runs once. +.ip \-q\fIXstring\fP +Run the queue once, +limiting the jobs to those matching +.i Xstring . +The key letter +.i X +can be +.b I +to limit based on queue identifier, +.b R +to limit based on recipient, +or +.b S +to limit based on sender. +A particular queued job is accepted if one of the corresponding addresses +contains the indicated +.i string . +.ip \-t +Read the header for +.q To: , +.q Cc: , +and +.q Bcc: +lines, and send to everyone listed in those lists. +The +.q Bcc: +line will be deleted before sending. +Any addresses in the argument vector will be deleted +from the send list. +.ip "\-X \fIlogfile\fP" +Log all traffic in and out of sendmail in the indicated +.i logfile +for debugging mailer problems. +This produces a lot of data very quickly and should be used sparingly. +.pp +There are a number of options that may be specified as +primitive flags +(provided for compatibility with +.i delivermail ). +These are the e, i, m, and v options. +Also, +the f option +may be specified as the +.b \-s +flag. +.+c "QUEUE FILE FORMATS" +.pp +This appendix describes the format of the queue files. +These files live in the directory defined by the +.b Q +option in the +.i sendmail.cf +file, usually +.i /var/spool/mqueue +or +.i /usr/spool/mqueue . +.pp +All queue files have the name +\fIx\fP\|\fBf\fP\fIAAA99999\fP +where +.i AAA99999 +is the +.i id +for this message +and the +.i x +is a type. +The first letter of the id encodes the hour of the day +that the message was received by the system +(with A being the hour between midnight and 1:00AM). +All files with the same id collectively define one message. +.pp +The types are: +.nr ii 0.5i +.ip d +The data file. +The message body (excluding the header) is kept in this file. +.ip l +The lock file. +If this file exists, +the job is currently being processed, +and a queue run will not process the file. +For that reason, +an extraneous +.b lf +file can cause a job to apparently disappear +(it will not even time out!). +[Actually, this file is obsolete on most systems that support the +.b flock +or +.b lockf +system calls.] +.ip n +This file is created when an id is being created. +It is a separate file to insure that no mail can ever be destroyed +due to a race condition. +It should exist for no more than a few milliseconds +at any given time. +[This is only used on old versions of +sendmail; +it is not used +on newer versions.] +.ip q +The queue control file. +This file contains the information necessary to process the job. +.ip t +A temporary file. +These are an image of the +.b qf +file when it is being rebuilt. +It should be renamed to a +.b qf +file very quickly. +.ip x +A transcript file, +existing during the life of a session +showing everything that happens +during that session. +.pp +The +.b qf +file is structured as a series of lines +each beginning with a code letter. +The lines are as follows: +.ip D +The name of the data file. +There may only be one of these lines. +.ip H +A header definition. +There may be any number of these lines. +The order is important: +they represent the order in the final message. +These use the same syntax +as header definitions in the configuration file. +.ip C +The controlling address. +The syntax is +.q localuser:aliasname . +Recipient addresses following this line +will be flagged so that deliveries will be run as the +.i localuser +(a user name from the /etc/passwd file); +.i aliasname +is the name of the alias that expanded to this address +(used for printing messages). +.ip R +A recipient address. +This will normally be completely aliased, +but is actually realiased when the job is processed. +There will be one line +for each recipient. +.ip S +The sender address. +There may only be one of these lines. +.ip E +An error address. +If any such lines exist, +they represent the addresses that should receive error messages. +.ip T +The job creation time. +This is used to compute when to time out the job. +.ip P +The current message priority. +This is used to order the queue. +Higher numbers mean lower priorities. +The priority changes +as the message sits in the queue. +The initial priority depends on the message class +and the size of the message. +.ip M +A message. +This line is printed by the +.i mailq +command, +and is generally used to store status information. +It can contain any text. +.ip F +Flag bits, represented as one letter per flag. +Defined flag bits are +.b r +indicating that this is a response message +and +.b w +indicating that a warning message has been sent +announcing that the mail has been delayed. +.ip $ +A macro definition. +The values of certain macros +(as of this writing, only +.b $r +and +.b $s ) +are passed through to the queue run phase. +.ip B +The body type. +The remainder of the line is a text string defining the body type. +If this field is missing, +the body type is assumed to be +.q "undefined" +and no special processing is attempted. +Legal values are +.q 7BIT +and +.q 8BITMIME . +.pp +As an example, +the following is a queue file sent to +.q eric@mammoth.Berkeley.EDU +and +.q bostic@okeeffe.CS.Berkeley.EDU \**: +.(f +\**This example is contrived and probably inaccurate for your environment. +Glance over it to get an idea; +nothing can replace looking at what your own system generates. +.)f +.(b +P835771 +T404261372 +DdfAAA13557 +Seric +Eowner-sendmail@vangogh.CS.Berkeley.EDU +Ceric:sendmail@vangogh.CS.Berkeley.EDU +Reric@mammoth.Berkeley.EDU +Rbostic@okeeffe.CS.Berkeley.EDU +H?P?return-path: <owner-sendmail@vangogh.CS.Berkeley.EDU> +Hreceived: by vangogh.CS.Berkeley.EDU (5.108/2.7) id AAA06703; + Fri, 17 Jul 92 00:28:55 -0700 +Hreceived: from mail.CS.Berkeley.EDU by vangogh.CS.Berkeley.EDU (5.108/2.7) + id AAA06698; Fri, 17 Jul 92 00:28:54 -0700 +Hreceived: from [128.32.31.21] by mail.CS.Berkeley.EDU (5.96/2.5) + id AA22777; Fri, 17 Jul 92 03:29:14 -0400 +Hreceived: by foo.bar.baz.de (5.57/Ultrix3.0-C) + id AA22757; Fri, 17 Jul 92 09:31:25 GMT +H?F?from: eric@foo.bar.baz.de (Eric Allman) +H?x?full-name: Eric Allman +Hmessage-id: <9207170931.AA22757@foo.bar.baz.de> +HTo: sendmail@vangogh.CS.Berkeley.EDU +Hsubject: this is an example message +.)b +This shows the name of the data file, +the person who sent the message, +the submission time +(in seconds since January 1, 1970), +the message priority, +the message class, +the recipients, +and the headers for the message. +.+c "SUMMARY OF SUPPORT FILES" +.pp +This is a summary of the support files +that +.i sendmail +creates or generates. +Many of these can be changed by editing the sendmail.cf file; +check there to find the actual pathnames. +.nr ii 1i +.ip "/usr/\*(SD/sendmail" +The binary of +.i sendmail . +.ip /usr/bin/newaliases +A link to /usr/\*(SD/sendmail; +causes the alias database to be rebuilt. +Running this program is completely equivalent to giving +.i sendmail +the +.b \-bi +flag. +.ip /usr/bin/mailq +Prints a listing of the mail queue. +This program is equivalent to using the +.b \-bp +flag to +.i sendmail . +.ip /etc/sendmail.cf +The configuration file, +in textual form. +.ip /etc/sendmail.fc +The configuration file +represented as a memory image. +.ip /usr/lib/sendmail.hf +The SMTP help file. +.ip /etc/sendmail.st +A statistics file; need not be present. +.ip /etc/sendmail.pid +Created in daemon mode; +it contains the process id of the current SMTP daemon. +If you use this in scripts; +use ``head \-1'' to get just the first line; +later versions of +.i sendmail +may add information to subsequent lines. +.ip /etc/aliases +The textual version of the alias file. +.ip /etc/aliases.{pag,dir} +The alias file in +.i dbm \|(3) +format. +.ip /var/spool/mqueue +The directory in which the mail queue +and temporary files reside. +.ip /var/spool/mqueue/qf* +Control (queue) files for messages. +.ip /var/spool/mqueue/df* +Data files. +.ip /var/spool/mqueue/tf* +Temporary versions of the qf files, +used during queue file rebuild. +.ip /var/spool/mqueue/xf* +A transcript of the current session. +.\".ro +.\".ls 1 +.\".tp +.\".sp 2i +.\".in 0 +.\".ce 100 +.\".sz 24 +.\".b SENDMAIL +.\".sz 14 +.\".sp +.\"INSTALLATION AND OPERATION GUIDE +.\".sp +.\".sz 10 +.\"Eric Allman +.\"Britton-Lee, Inc. +.\".sp +.\"Version 8.2 +.\".ce 0 +.pn 2 +.bp +.ce +.sz 12 +TABLE OF CONTENTS +.sz 10 +.sp +.\" remove some things to avoid "out of temp file space" problem +.rm sh +.rm (x +.rm )x +.rm ip +.rm pp +.rm lp +.rm he +.rm fo +.rm eh +.rm oh +.rm ef +.rm of +.xp diff --git a/usr.sbin/sendmail/doc/op/op.ps b/usr.sbin/sendmail/doc/op/op.ps new file mode 100644 index 0000000000..45a80cdbc1 --- /dev/null +++ b/usr.sbin/sendmail/doc/op/op.ps @@ -0,0 +1,5173 @@ +%!PS-Adobe-3.0 +%%Creator: groff version 1.08 +%%DocumentNeededResources: font Times-Bold +%%+ font Times-Roman +%%+ font Times-Italic +%%+ font Symbol +%%DocumentSuppliedResources: procset grops 1.08 0 +%%Pages: 61 +%%PageOrder: Ascend +%%Orientation: Portrait +%%EndComments +%%BeginProlog +%%BeginResource: procset grops 1.08 0 +/setpacking where{ +pop +currentpacking +true setpacking +}if +/grops 120 dict dup begin +/SC 32 def +/A/show load def +/B{0 SC 3 -1 roll widthshow}bind def +/C{0 exch ashow}bind def +/D{0 exch 0 SC 5 2 roll awidthshow}bind def +/E{0 rmoveto show}bind def +/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def +/G{0 rmoveto 0 exch ashow}bind def +/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/I{0 exch rmoveto show}bind def +/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def +/K{0 exch rmoveto 0 exch ashow}bind def +/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/M{rmoveto show}bind def +/N{rmoveto 0 SC 3 -1 roll widthshow}bind def +/O{rmoveto 0 exch ashow}bind def +/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/Q{moveto show}bind def +/R{moveto 0 SC 3 -1 roll widthshow}bind def +/S{moveto 0 exch ashow}bind def +/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/SF{ +findfont exch +[exch dup 0 exch 0 exch neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/MF{ +findfont +[5 2 roll +0 3 1 roll +neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/level0 0 def +/RES 0 def +/PL 0 def +/LS 0 def +/PLG{ +gsave newpath clippath pathbbox grestore +exch pop add exch pop +}bind def +/BP{ +/level0 save def +1 setlinecap +1 setlinejoin +72 RES div dup scale +LS{ +90 rotate +}{ +0 PL translate +}ifelse +1 -1 scale +}bind def +/EP{ +level0 restore +showpage +}bind def +/DA{ +newpath arcn stroke +}bind def +/SN{ +transform +.25 sub exch .25 sub exch +round .25 add exch round .25 add exch +itransform +}bind def +/DL{ +SN +moveto +SN +lineto stroke +}bind def +/DC{ +newpath 0 360 arc closepath +}bind def +/TM matrix def +/DE{ +TM currentmatrix pop +translate scale newpath 0 0 .5 0 360 arc closepath +TM setmatrix +}bind def +/RC/rcurveto load def +/RL/rlineto load def +/ST/stroke load def +/MT/moveto load def +/CL/closepath load def +/FL{ +currentgray exch setgray fill setgray +}bind def +/BL/fill load def +/LW/setlinewidth load def +/RE{ +findfont +dup maxlength 1 index/FontName known not{1 add}if dict begin +{ +1 index/FID ne{def}{pop pop}ifelse +}forall +/Encoding exch def +dup/FontName exch def +currentdict end definefont pop +}bind def +/DEFS 0 def +/EBEGIN{ +moveto +DEFS begin +}bind def +/EEND/end load def +/CNT 0 def +/level1 0 def +/PBEGIN{ +/level1 save def +translate +div 3 1 roll div exch scale +neg exch neg exch translate +0 setgray +0 setlinecap +1 setlinewidth +0 setlinejoin +10 setmiterlimit +[]0 setdash +/setstrokeadjust where{ +pop +false setstrokeadjust +}if +/setoverprint where{ +pop +false setoverprint +}if +newpath +/CNT countdictstack def +userdict begin +/showpage{}def +}bind def +/PEND{ +clear +countdictstack CNT sub{end}repeat +level1 restore +}bind def +end def +/setpacking where{ +pop +setpacking +}if +%%EndResource +%%IncludeResource: font Times-Bold +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Italic +%%IncludeResource: font Symbol +grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL +792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron +/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space +/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft +/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four +/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/A/B/C +/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash +/bracketright/circumflex/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q +/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/tilde/.notdef/quotesinglbase +/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger +/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut +/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash +/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar +/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus +/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu +/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright +/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde +/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute +/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis +/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls +/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute +/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve +/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex +/udieresis/yacute/thorn/ydieresis]def/Times-Italic@0 ENC0/Times-Italic RE +/Times-Roman@0 ENC0/Times-Roman RE/Times-Bold@0 ENC0/Times-Bold RE +%%EndProlog +%%Page: 1 1 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 16/Times-Bold@0 SF(SENDMAIL)244.888 143.4 Q/F1 12/Times-Bold@0 SF(INST) +170.172 172.2 Q(ALLA)-1.08 E(TION AND OPERA)-1.14 E(TION GUIDE)-1.14 E/F2 10 +/Times-Roman@0 SF(Eric Allman)263.42 196.2 Q(Uni)219.725 208.2 Q -.15(ve)-.25 G +(rsity of California, Berk).15 E(ele)-.1 E(y)-.15 E(Mammoth Project)251.75 +220.2 Q(eric@CS.Berk)239.41 232.2 Q(ele)-.1 E -.65(y.)-.15 G(EDU).65 E -1.11 +(Ve)265.225 256.2 S(rsion 8.2)1.11 E -.15(Fo)236.965 280.2 S 2.5(rS).15 G +(endmail V)258.765 280.2 Q(ersion 8.2)-1.11 E/F3 10/Times-Italic@0 SF(Sendmail) +97 324.6 Q F2 .699(implements a general purpose internetw)3.199 F .698 +(ork mail routing f)-.1 F .698(acility under the UNIX* operat-)-.1 F .378 +(ing system.)72 336.6 R .378(It is not tied to an)5.378 F 2.878(yo)-.15 G .378 +(ne transport protocol \212 its function may be lik)208.214 336.6 R .378 +(ened to a crossbar switch,)-.1 F 1.036 +(relaying messages from one domain into another)72 348.6 R 6.036(.I)-.55 G +3.536(nt)284.502 348.6 S 1.036 +(he process, it can do a limited amount of message)295.818 348.6 R .604(header\ + editing to put the message into a format that is appropriate for the recei)72 +360.6 R .604(ving domain.)-.25 F .604(All of this is)5.604 F +(done under the control of a con\214guration \214le.)72 372.6 Q .711 +(Due to the requirements of \215e)97 388.8 R .711(xibility for)-.15 F F3 +(sendmail)3.211 E F2 3.211(,t)C .71(he con\214guration \214le can seem some) +311.688 388.8 R .71(what unap-)-.25 F 2.893(proachable. Ho)72 400.8 R(we)-.25 E +-.15(ve)-.25 G 1.193 -.4(r, t).15 H .393(here are only a fe).4 F 2.893(wb)-.25 +G .394(asic con\214gurations for most sites, for which standard con\214gu-) +253.381 400.8 R .646(ration \214les ha)72 412.8 R .946 -.15(ve b)-.2 H .646 +(een supplied.).15 F .645(Most other con\214gurations can be b)5.646 F .645 +(uilt by adjusting an e)-.2 F .645(xisting con\214gura-)-.15 F +(tion \214les incrementally)72 424.8 Q(.)-.65 E F3(Sendmail)97 441 Q F2 .15 +(is based on RFC822 \(Internet Mail F)2.65 F .15 +(ormat Protocol\), RFC821 \(Simple Mail T)-.15 F .15(ransport Pro-)-.35 F .129 +(tocol\), RFC1123 \(Internet Host Requirements\), and RFC1425 \(SMTP Service E\ +xtensions\).)72 453 R(Ho)5.129 E(we)-.25 E -.15(ve)-.25 G .929 -.4(r, s).15 H +(ince).4 E F3(sendmail)72 465 Q F2 .749(is designed to w)3.249 F .749 +(ork in a wider w)-.1 F .749(orld, in man)-.1 F 3.25(yc)-.15 G .75 +(ases it can be con\214gured to e)309.31 465 R .75(xceed these proto-)-.15 F +2.5(cols. These)72 477 R(cases are described herein.)2.5 E(Although)97 493.2 Q +F3(sendmail)3.548 E F2 1.047(is intended to run without the need for monitorin\ +g, it has a number of features)3.548 F 1.972(that may be used to monitor or ad\ +just the operation under unusual circumstances.)72 505.2 R 1.972 +(These features are)6.972 F(described.)72 517.2 Q .817 +(Section one describes ho)97 533.4 R 3.317(wt)-.25 G 3.317(od)211.668 533.4 S +3.317(oa)224.985 533.4 S(basic)-.001 E F3(sendmail)3.316 E F2 3.316 +(installation. Section)3.316 F(tw)3.316 E 3.316(oe)-.1 G .816 +(xplains the day-to-day)412.938 533.4 R .282(information you should kno)72 +545.4 R 2.782(wt)-.25 G 2.782(om)196.768 545.4 S .282 +(aintain your mail system.)212.33 545.4 R .282(If you ha)5.282 F .583 -.15 +(ve a r)-.2 H(elati).15 E -.15(ve)-.25 G .283(ly normal site, these tw).15 F(o) +-.1 E .635(sections should contain suf)72 557.4 R .635 +(\214cient information for you to install)-.25 F F3(sendmail)3.135 E F2 .634 +(and k)3.135 F .634(eep it happ)-.1 F 4.434 -.65(y. S)-.1 H .634(ection three) +.65 F .925(describes some parameters that may be safely tweak)72 569.4 R 3.425 +(ed. Section)-.1 F .925(four has information re)3.425 F -.05(ga)-.15 G .925 +(rding the com-).05 F .886(mand line ar)72 581.4 R 3.386(guments. Section)-.18 +F<8c76>3.386 E 3.386(ec)-.15 G .885 +(ontains the nitty-gritty information about the con\214guration \214le.)221.92 +581.4 R(This)5.885 E .501 +(section is for masochists and people who must write their o)72 593.4 R .501 +(wn con\214guration \214le.)-.25 F .501(Section six gi)5.501 F -.15(ve)-.25 G +3.002(sab).15 G(rief)490.12 593.4 Q .355(description of dif)72 605.4 R .355 +(ferences in this v)-.25 F .355(ersion of)-.15 F F3(sendmail)2.855 E F2 5.355 +(.T)C .355(he appendix)298.85 605.4 R .355(es gi)-.15 F .654 -.15(ve a b)-.25 H +.354(rief b).15 F .354(ut detailed e)-.2 F(xplanation)-.15 E +(of a number of features not described in the rest of the paper)72 617.4 Q(.) +-.55 E .32 LW 76 680.4 72 680.4 DL 80 680.4 76 680.4 DL 84 680.4 80 680.4 DL 88 +680.4 84 680.4 DL 92 680.4 88 680.4 DL 96 680.4 92 680.4 DL 100 680.4 96 680.4 +DL 104 680.4 100 680.4 DL 108 680.4 104 680.4 DL 112 680.4 108 680.4 DL 116 +680.4 112 680.4 DL 120 680.4 116 680.4 DL 124 680.4 120 680.4 DL 128 680.4 124 +680.4 DL 132 680.4 128 680.4 DL 136 680.4 132 680.4 DL 140 680.4 136 680.4 DL +144 680.4 140 680.4 DL 148 680.4 144 680.4 DL 152 680.4 148 680.4 DL 156 680.4 +152 680.4 DL 160 680.4 156 680.4 DL 164 680.4 160 680.4 DL 168 680.4 164 680.4 +DL 172 680.4 168 680.4 DL 176 680.4 172 680.4 DL 180 680.4 176 680.4 DL 184 +680.4 180 680.4 DL 188 680.4 184 680.4 DL 192 680.4 188 680.4 DL 196 680.4 192 +680.4 DL 200 680.4 196 680.4 DL 204 680.4 200 680.4 DL 208 680.4 204 680.4 DL +212 680.4 208 680.4 DL 216 680.4 212 680.4 DL/F4 8/Times-Roman@0 SF +(*UNIX is a trademark of Bell Laboratories.)93.6 692.4 Q/F5 10/Times-Bold@0 SF +(Sendmail Installation and Operation Guide)72 756 Q(SMM:08-1)457.9 756 Q EP +%%Page: 5 2 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-5)457.9 60 Q 2.5(1. B)72 96 R(ASIC INST)-.3 E(ALLA)-.9 E(TION)-.95 E/F1 +10/Times-Roman@0 SF .233(There are tw)112 112.2 R 2.733(ob)-.1 G .233 +(asic steps to installing sendmail.)175.629 112.2 R .233(The hard part is to b) +5.233 F .234(uild the con\214guration table.)-.2 F 1.186(This is a \214le that\ + sendmail reads when it starts up that describes the mailers it kno)87 124.2 R +1.185(ws about, ho)-.25 F 3.685(wt)-.25 G(o)499 124.2 Q .714 +(parse addresses, ho)87 136.2 R 3.214(wt)-.25 G 3.214(or)178.312 136.2 S -.25 +(ew)189.856 136.2 S .715(rite the message header).25 F 3.215(,a)-.4 G .715 +(nd the settings of v)306.745 136.2 R .715(arious options.)-.25 F .715 +(Although the)5.715 F .852(con\214guration table is quite comple)87 148.2 R +.852(x, a con\214guration can usually be b)-.15 F .852(uilt by adjusting an e) +-.2 F .852(xisting of)-.15 F(f-)-.25 E 1.077(the-shelf con\214guration.)87 +160.2 R 1.078(The second part is actually doing the installation, i.e., creati\ +ng the necessary)6.077 F(\214les, etc.)87 172.2 Q .192(The remainder of this s\ +ection will describe the installation of sendmail assuming you can use one)112 +188.4 R 1.431(of the e)87 200.4 R 1.432(xisting con\214gurations and that the \ +standard installation parameters are acceptable.)-.15 F 1.432(All path-)6.432 F +8.62(names and e)87 212.4 R 8.62(xamples are gi)-.15 F -.15(ve)-.25 G 11.12(nf) +.15 G 8.62(rom the root of the)257.57 212.4 R/F2 10/Times-Italic@0 SF(sendmail) +378.16 212.4 Q F1 8.62(subtree, normally)425.39 212.4 R F2(/usr/sr)87 224.4 Q +(c/usr)-.37 E(.sbin/sendmail)-1.11 E F1(on 4.4BSD.)2.5 E .511 +(If you are loading this of)112 240.6 R 3.011(ft)-.25 G .511 +(he tape, continue with the ne)222.576 240.6 R .511(xt session.)-.15 F .511 +(If you ha)5.511 F .811 -.15(ve a r)-.2 H .512(unning binary).15 F +(already on your system, you should probably skip to section 1.2.)87 252.6 Q F0 +2.5(1.1. Compiling)87 276.6 R(Sendmail)2.5 E F1 .435 +(All sendmail source is in the)127 292.8 R F2(sr)2.935 E(c)-.37 E F1 +(subdirectory)2.934 E 5.434(.I)-.65 G 2.934(fy)321.658 292.8 S .434 +(ou are running on a 4.4BSD system, com-)332.922 292.8 R +(pile by typing \231mak)102 304.8 Q 2.5(e\232. On)-.1 F +(other systems, you may ha)2.5 E .3 -.15(ve t)-.2 H 2.5(om).15 G(ak)348.75 +304.8 Q 2.5(es)-.1 G(ome other adjustments.)368.92 304.8 Q F0 2.5(1.1.1. Old) +102 328.8 R -.1(ve)2.5 G(rsions of mak).1 E(e)-.1 E F1 +(If you are not running the ne)142 345 Q 2.5(wv)-.25 G(ersion of)270.74 345 Q +F0(mak)2.5 E(e)-.1 E F1(you will probably ha)2.5 E .3 -.15(ve t)-.2 H 2.5(ou) +.15 G(se)444.16 345 Q(mak)157 361.2 Q 2.5<65ad>-.1 G 2.5(fM)186.7 361.2 S(ak) +201.42 361.2 Q(e\214le.dist)-.1 E .885(This \214le does not assume se)117 377.4 +R -.15(ve)-.25 G .885(ral ne).15 F 3.385(ws)-.25 G(yntax)280.025 377.4 Q .885 +(es, including the \231+=\232 syntax in macro de\214nition)-.15 F +(and the \231.include\232 syntax.)117 389.4 Q F0 2.5(1.1.2. Compilation)102 +413.4 R(\215ags)2.5 E F2(Sendmail)142 429.6 Q F1(supports tw)2.5 E 2.5(od)-.1 G +(if)240.51 429.6 Q(ferent formats for the)-.25 E F2(aliases)2.5 E F1 2.5 +(database. These)2.5 F(formats are:)2.5 E 39.5(NDBM The)117 445.8 R -.74(``) +3.167 G(ne).74 E 3.167(wD)-.25 G(BM')240.434 445.8 Q 3.167('f)-.74 G .667 +(ormat, a)268.411 445.8 R -.25(va)-.2 G .666 +(ilable on nearly all systems around today).25 F 5.666(.T)-.65 G(his)492.33 +445.8 Q -.1(wa)189 457.8 S 3.54(st).1 G 1.041 +(he preferred format prior to 4.4BSD.)210.77 457.8 R 1.041(It allo)6.041 F +1.041(ws such comple)-.25 F 3.541(xt)-.15 G 1.041(hings as)470.459 457.8 R +(multiple databases and closing a currently open database.)189 469.8 Q 32.84 +(NEWDB The)117 486 R(ne)3.324 E 3.324(wd)-.25 G .824(atabase package from Berk) +232.608 486 R(ele)-.1 E 4.624 -.65(y. I)-.15 H 3.324(fy).65 G .824(ou ha) +382.718 486 R 1.124 -.15(ve t)-.2 H .824(his, use it.).15 F .823(It allo)5.823 +F(ws)-.25 E .839 +(long records, multiple open databases, real in-memory caching, and so forth.) +189 498 R -1.1(Yo)189 510 S 3.582(uc)1.1 G 1.081 +(an de\214ne this in conjunction with one of the other tw)213.142 510 R 1.081 +(o; if you do, old)-.1 F .692(databases are read, b)189 522 R .693 +(ut when a ne)-.2 F 3.193(wd)-.25 G .693 +(atabase is created it will be in NEWDB)341.679 522 R 4.286(format. As)189 534 +R 4.286(an)4.286 G 1.786(asty hack, if you ha)254.068 534 R 2.085 -.15(ve N)-.2 +H 1.785(EWDB, NDBM, and YPCOMP).15 F -1.11(AT)-.92 G 1.162 +(de\214ned, and if the \214le)189 546 R F2(/var/yp/Mak)3.663 E(e\214le)-.1 E F1 +-.15(ex)3.663 G 1.163(ists and is readable,).15 F F2(sendmail)3.663 E F1(will) +3.663 E .345(create both ne)189 558 R 2.845(wa)-.25 G .345(nd old v)260.035 558 +R .345(ersions of the alias \214le during a)-.15 F F2(ne)2.844 E(walias)-.15 E +F1(command.)2.844 E 1.219 +(This is required because the Sun NIS/YP system reads the DBM v)189 570 R 1.22 +(ersion of)-.15 F(the alias \214le.)189 582 Q(It')5 E 2.5(su)-.55 G +(gly as sin, b)265.11 582 Q(ut it w)-.2 E(orks.)-.1 E 1.112 +(If neither of these are de\214ned,)117 598.2 R F2(sendmail)3.612 E F1 1.112 +(reads the alias \214le into memory on e)3.612 F -.15(ve)-.25 G 1.112(ry in).15 +F -.2(vo)-.4 G(cation.).2 E(This can be slo)117 610.2 Q 2.5(wa)-.25 G +(nd should be a)191.18 610.2 Q -.2(vo)-.2 G(ided.).2 E .719 +(System V based systems can de\214ne SYSTEM5 to mak)142 626.4 R 3.219(es)-.1 G +-2.15 -.25(ev e)378.081 626.4 T .719(ral small adjustments.).25 F(This)5.72 E +1.076(changes the handling of timezones and uses the much less ef)117 638.4 R +(\214cient)-.25 E F2(loc)3.576 E(kf)-.2 E F1 1.076(call in preference to)3.576 +F F2(\215oc)117 650.4 Q(k)-.2 E F1 7.224(.T)C 2.224(hese can be speci\214ed se\ +parately using the compilation \215ags SYS5TZ and LOCKF)151.514 650.4 R +(respecti)117 662.4 Q -.15(ve)-.25 G(ly).15 E(.)-.65 E 1.647(If you don')142 +678.6 R 4.147(th)-.18 G -2.25 -.2(av e)202.031 678.6 T(the)4.347 E F2(unseten) +4.147 E(v)-.4 E F1 1.647(routine in your system library)4.147 F 4.147(,d)-.65 G +1.647(e\214ne the UNSETENV)411.277 678.6 R(compilation \215ag.)117 690.6 Q -1.1 +(Yo)142 706.8 S 3.854(um)1.1 G 1.354(ay also ha)169.754 706.8 R 1.654 -.15 +(ve t)-.2 H 3.854(od).15 G 1.355(e\214ne the compilation v)242.03 706.8 R 1.355 +(ariable LA_TYPE to describe ho)-.25 F 3.855(wy)-.25 G(our)490.67 706.8 Q +(load a)117 718.8 Q -.15(ve)-.2 G(rage is computed.).15 E +(This and other \215ags are detailed in section 6.1.)5 E EP +%%Page: 6 3 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 198.36(SMM:08-6 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E 2.5(1.1.3. Compilation)102 96 R +(and installation)2.5 E/F1 10/Times-Roman@0 SF .309 +(After making the local system con\214guration described abo)142 112.2 R -.15 +(ve)-.15 G 2.808(,Y).15 G .308(ou should be able to com-)398.86 112.2 R .87 +(pile and install the system.)117 126.2 R .87 +(Compilation can be performed using \231mak)5.87 F(e)-.1 E/F2 7/Times-Roman@0 +SF(1)412.24 122.2 Q F1 3.37<9a69>415.74 126.2 S 3.37(nt)426.33 126.2 S(he) +437.48 126.2 Q F0(sendmail/sr)3.37 E(c)-.18 E F1(directory)117 138.2 Q 5(.Y) +-.65 G(ou may be able to install using)166.07 138.2 Q(mak)157 154.4 Q 2.5(ei) +-.1 G(nstall)183.84 154.4 Q 3.346 +(This should install the binary in /usr/sbin and create links from /usr/bin/ne) +117 170.6 R -.1(wa)-.25 G 3.345(liases and).1 F 1.576 +(/usr/bin/mailq to /usr/sbin/sendmail.)117 182.6 R 1.577 +(On 4.4BSD systems it will also format and install man)6.576 F(pages.)117 194.6 +Q F0 2.5(1.2. Con\214guration)87 218.6 R(Files)2.5 E/F3 10/Times-Italic@0 SF +(Sendmail)127 234.8 Q F1 .355(cannot operate without a con\214guration \214le.) +2.855 F .355(The con\214guration de\214nes the mail sys-)5.355 F .286 +(tems understood at this site, ho)102 246.8 R 2.786(wt)-.25 G 2.786(oa)239.854 +246.8 S .286(ccess them, ho)252.08 246.8 R 2.786(wt)-.25 G 2.786(of)323.788 +246.8 S(orw)334.904 246.8 Q .286(ard email to remote mail systems, and)-.1 F +3.114(an)102 258.8 S .614(umber of tuning parameters.)114.554 258.8 R .614 +(This con\214guration \214le is detailed in the later portion of this docu-) +5.614 F(ment.)102 270.8 Q(The)127 287 Q F3(sendmail)2.764 E F1 .264 +(con\214guration can be daunting at \214rst.)2.764 F .264(The w)5.264 F .264 +(orld is comple)-.1 F .264(x, and the mail con-)-.15 F .109 +(\214guration re\215ects that.)102 299 R .109(The distrib)5.109 F .108 +(ution includes an m4-based con\214guration package that hides a lot)-.2 F +(of the comple)102 311 Q(xity)-.15 E(.)-.65 E .47 +(These con\214guration \214les are simpler than old v)127 327.2 R .47 +(ersions lar)-.15 F .47(gely because the w)-.18 F .47(orld has become)-.1 F +1.449(simpler; in particular)102 339.2 R 3.949(,t)-.4 G -.15(ex)197.607 339.2 S +1.449(t-based host \214les are of).15 F 1.448(\214cially eliminated, ob)-.25 F +1.448(viating the need to \231hide\232)-.15 F(hosts behind a re)102 351.2 Q +(gistered internet g)-.15 E(ate)-.05 E -.1(wa)-.25 G -.65(y.).1 G .092(These \ +\214les also assume that most of your neighbors use domain-based UUCP addressi\ +ng; that)127 367.4 R .361(is, instead of naming hosts as \231host!user\232 the) +102 379.4 R 2.861(yw)-.15 G .361(ill use \231host.domain!user\232.)299.438 +379.4 R .36(The con\214guration \214les)5.36 F(can be customized to w)102 391.4 +Q(ork around this, b)-.1 E(ut it is more comple)-.2 E(x.)-.15 E 2.828(Ih)127 +407.6 S -2.25 -.2(av e)138.158 407.6 T(n').2 E 2.828(tt)-.18 G .328 +(ested these yet on an isolated LAN en)168.226 407.6 R .328 +(vironment with a single UUCP connection to)-.4 F 4.409(the outside w)102 419.6 +R 6.909(orld. If)-.1 F 4.409(you are in such an en)6.909 F 4.408 +(vironment, please send comments to send-)-.4 F(mail@ok)102 431.6 Q(eef)-.1 E +(fe.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G(EDU.).65 E .657 +(Our con\214guration \214les are processed by)127 447.8 R F3(m4)3.158 E F1 .658 +(to f)3.158 F .658(acilitate local customization; the directory)-.1 F F3(cf) +3.158 E F1 .397(of the sendmail distrib)102 459.8 R .396 +(ution directory contains the source \214les.)-.2 F .396 +(This directory contains se)5.396 F -.15(ve)-.25 G .396(ral sub-).15 F +(directories:)102 471.8 Q 61.73(cf Both)102 488 R .56 +(site-dependent and site-independent descriptions of hosts.)3.06 F .56 +(These can be lit-)5.56 F .445(eral host names \(e.g., \231ucb)174 500 R -.25 +(va)-.15 G .445(x.mc\232\) when the hosts are g).25 F(ate)-.05 E -.1(wa)-.25 G +.445(ys or more general).1 F 3.589(descriptions \(such as \231tcpproto.mc\232 \ +as a general description of an SMTP-)174 512 R .536(connected host or \231uucp\ +proto.mc\232 as a general description of a UUCP-connected)174 524 R 3.291 +(host\). Files)174 536 R(ending)3.291 E F0(.mc)3.291 E F1(\(`)3.291 E .791 +(`Master Con\214guration')-.74 F .791('\) are the input descriptions; the)-.74 +F 2.14(output is in the corresponding)174 548 R F0(.cf)4.64 E F1 4.64 +(\214le. The)4.64 F 2.14(general structure of these \214les is)4.64 F +(described belo)174 560 Q -.65(w.)-.25 G 39.5(domain Site-dependent)102 576.2 R +.428(subdomain descriptions.)2.928 F .428(These are tied to the w)5.428 F .428 +(ay your or)-.1 F -.05(ga)-.18 G(niza-).05 E .292(tion w)174 588.2 R .292 +(ants to do addressing.)-.1 F -.15(Fo)5.292 G 2.792(re).15 G(xample,)313.122 +588.2 Q F0(domain/cs.exposed.m4)2.792 E F1 .292(is our descrip-)2.792 F .442 +(tion for hosts in the CS.Berk)174 600.2 R(ele)-.1 E -.65(y.)-.15 G .443 +(EDU subdomain that w).65 F .443(ant their indi)-.1 F .443(vidual host-)-.25 F +.963(name to be e)174 612.2 R .963(xternally visible;)-.15 F F0 +(domain/cs.hidden.m4)3.463 E F1 .963(is the same e)3.463 F .962(xcept that the) +-.15 F 2.627(hostname is hidden \(e)174 624.2 R -.15(ve)-.25 G 2.628 +(rything looks lik).15 F 5.128(ei)-.1 G 5.128(tc)362.036 624.2 S 2.628 +(omes from CS.Berk)374.384 624.2 R(ele)-.1 E -.65(y.)-.15 G(EDU\).).65 E +(These are referenced using the)174 636.2 Q/F4 9/Times-Roman@0 SF(DOMAIN)2.5 E +F0(m4)2.5 E F1(macro in the)2.5 E F0(.mc)2.5 E F1(\214le.)2.5 E 41.74 +(feature De\214nitions)102 652.4 R .728 +(of speci\214c features that some particular host in your site might w)3.229 F +(ant.)-.1 E 2.466(These are referenced using the)174 664.4 R F4(FEA)4.966 E +(TURE)-.999 E F0(m4)4.966 E F1 4.966(macro. An)4.966 F -.15(ex)4.967 G 2.467 +(ample feature is).15 F 1.316(use_cw_\214le \(which tells sendmail to read an \ +/etc/sendmail.cw \214le on startup to)174 676.4 R .32 LW 76 686 72 686 DL 80 +686 76 686 DL 84 686 80 686 DL 88 686 84 686 DL 92 686 88 686 DL 96 686 92 686 +DL 100 686 96 686 DL 104 686 100 686 DL 108 686 104 686 DL 112 686 108 686 DL +116 686 112 686 DL 120 686 116 686 DL 124 686 120 686 DL 128 686 124 686 DL 132 +686 128 686 DL 136 686 132 686 DL 140 686 136 686 DL 144 686 140 686 DL 148 686 +144 686 DL 152 686 148 686 DL 156 686 152 686 DL 160 686 156 686 DL 164 686 160 +686 DL 168 686 164 686 DL 172 686 168 686 DL 176 686 172 686 DL 180 686 176 686 +DL 184 686 180 686 DL 188 686 184 686 DL 192 686 188 686 DL 196 686 192 686 DL +200 686 196 686 DL 204 686 200 686 DL 208 686 204 686 DL 212 686 208 686 DL 216 +686 212 686 DL/F5 5/Times-Roman@0 SF(1)93.6 696.4 Q/F6 8/Times-Roman@0 SF +(where you may ha)3.2 I .24 -.12(ve t)-.16 H 2(or).12 G(eplace \231mak)175.132 +699.6 Q(e\232 with \231mak)-.08 E 2<65ad>-.08 G 2(fM)267.452 699.6 S(ak)279.228 +699.6 Q(e\214le.dist\232 as appropriate.)-.08 E EP +%%Page: 7 4 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-7)457.9 60 Q/F1 10/Times-Roman@0 SF(\214nd the set of local names\).) +174 96 Q 50.62(hack Local)102 112.2 R 1.886(hacks, referenced using the)4.386 F +/F2 9/Times-Roman@0 SF(HA)4.386 E(CK)-.36 E F0(m4)4.386 E F1 4.386(macro. T) +4.386 F 1.886(ry to a)-.35 F -.2(vo)-.2 G 1.886(id these.).2 F(The)6.887 E +(point of ha)174 124.2 Q(ving them here is to mak)-.2 E 2.5(ei)-.1 G 2.5(tc) +325.91 124.2 S(lear that the)335.63 124.2 Q 2.5(ys)-.15 G(mell.)394.08 124.2 Q +56.72(m4 Site-independent)102 140.4 R/F3 10/Times-Italic@0 SF(m4)2.538 E F1 +.038(\(1\) include \214les that ha)B .338 -.15(ve i)-.2 H .038 +(nformation common to all con\214gu-).15 F(ration \214les.)174 152.4 Q +(This can be thought of as a \231#include\232 directory)5 E(.)-.65 E 43.95 +(mailer De\214nitions)102 168.6 R .917(of mailers, referenced using the)3.417 F +F2(MAILER)3.417 E F0(m4)3.417 E F1 3.418(macro. De\214ned)3.418 F(mailer)3.418 +E(types in this distrib)174 180.6 Q(ution are f)-.2 E +(ax, local, smtp, uucp, and usenet.)-.1 E 43.39(ostype De\214nitions)102 196.8 +R 1.157(describing v)3.657 F 1.157(arious operating system en)-.25 F 1.156 +(vironments \(such as the loca-)-.4 F(tion of support \214les\).)174 208.8 Q +(These are referenced using the)5 E F2(OSTYPE)2.5 E F0(m4)2.5 E F1(macro.)2.5 E +60.61(sh Shell)102 225 R(\214les used by the)2.5 E F0(m4)2.5 E F1 -.2(bu)2.5 G +(ild process.).2 E -1.1(Yo)5 G 2.5(us)1.1 G(houldn')362.97 225 Q 2.5(th)-.18 G +-2.25 -.2(av e)404.18 225 T(to mess with these.)2.7 E 30.61(sitecon\214g Local) +102 241.2 R .49(site con\214guration information, such as UUCP connecti)2.99 F +(vity)-.25 E 5.49(.T)-.65 G(he)450.61 241.2 Q 2.99(yn)-.15 G(ormally)472.89 +241.2 Q(contain lists of site information, for e)174 253.2 Q(xample:)-.15 E +(SITE\(contessa\))214 269.4 Q(SITE\(hoptoad\))214 281.4 Q(SITE\(nkainc\))214 +293.4 Q(SITE\(well\))214 305.4 Q(The)174 321.6 Q 2.5(ya)-.15 G +(re referenced using the SITECONFIG macro:)201.34 321.6 Q +(SITECONFIG\(site.con\214g.\214le, name_of_site, X\))214 337.8 Q(where)174 354 +Q F3(X)2.704 E F1 .204(is the macro/class name to use.)2.704 F .203 +(It can be U \(indicating locally connected)5.204 F(hosts\) or one of W)174 366 +Q 2.5(,X)-.92 G 2.5(,o)259.73 366 S 2.5(rYf)269.73 366 S +(or up to three remote UUCP hubs.)288.61 366 Q .756(If you are in a ne)127 +382.2 R 3.256(wd)-.25 G .756(omain \(e.g., a compan)214.036 382.2 R .757 +(y\), you will probably w)-.15 F .757(ant to create a cf/domain)-.1 F .871 +(\214le for your domain.)102 394.2 R .871 +(This consists primarily of relay de\214nitions: for e)5.871 F .87 +(xample, Berk)-.15 F(ele)-.1 E(y')-.15 E 3.37(sd)-.55 G(omain)479 394.2 Q .16 +(de\214nition de\214nes relays for BitNET)102 406.2 R 2.66(,C)-.74 G(SNET) +257.61 406.2 Q 2.66(,a)-.74 G .16(nd UUCP)291.47 406.2 R 5.16(.O)-1.11 G 2.66 +(ft)344.57 406.2 S .16(hese, only the UUCP relay is particu-)353.34 406.2 R .46 +(larly speci\214c to Berk)102 418.2 R(ele)-.1 E 4.26 -.65(y. A)-.15 H .46 +(ll of these are internet-style domain names.).65 F .46(Please check to mak) +5.46 F 2.96(ec)-.1 G(er)493.1 418.2 Q(-)-.2 E(tain the)102 430.2 Q 2.5(ya)-.15 +G(re reasonable for your domain.)143.51 430.2 Q 1.406(Subdomains at Berk)127 +446.4 R(ele)-.1 E 3.906(ya)-.15 G 1.407 +(re also represented in the cf/domain directory)235.678 446.4 R 6.407(.F)-.65 G +1.407(or e)439.406 446.4 R 1.407(xample, the)-.15 F 1.491(domain cs-e)102 458.4 +R 1.491(xposed is the Computer Science subdomain with the local hostname sho) +-.15 F 1.49(wn to other)-.25 F 1.41(users; cs-hidden mak)102 470.4 R 1.411 +(es users appear to be from the CS.Berk)-.1 F(ele)-.1 E -.65(y.)-.15 G 1.411 +(EDU subdomain \(with no local).65 F 1.084(host information included\).)102 +482.4 R -1.1(Yo)6.084 G 3.584(uw)1.1 G 1.084(ill probably ha)246.336 482.4 R +1.384 -.15(ve t)-.2 H 3.584(ou).15 G 1.083 +(pdate this directory to be appropriate for)335.872 482.4 R(your domain.)102 +494.4 Q -1.1(Yo)127 510.6 S 4.372(uw)1.1 G 1.872(ill ha)154.712 510.6 R 2.172 +-.15(ve t)-.2 H 4.372(ou).15 G 1.872(se or create)207.478 510.6 R F0(.mc)4.372 +E F1 1.872(\214les in the)4.372 F F3(cf/cf)4.372 E F1 1.873 +(subdirectory for your hosts.)4.373 F 1.873(This is)6.873 F +(detailed in the cf/README \214le.)102 522.6 Q F0 2.5(1.3. Details)87 546.6 R +(of Installation Files)2.5 E F1(This subsection describes the \214les that com\ +prise the sendmail installation.)127 562.8 Q F0 2.5(1.3.1. /usr/sbin/sendmail) +102 586.8 R F1 .079(The binary for sendmail is located in /usr/sbin)142 605 R +/F4 7/Times-Roman@0 SF(2)326.703 601 Q F1 5.079(.I)330.203 605 S 2.579(ts) +341.112 605 S .08(hould be setuid root.)350.361 605 R -.15(Fo)5.08 G 2.58(rs) +.15 G .08(ecurity rea-)458.11 605 R(sons, /, /usr)117 619 Q 2.5(,a)-.4 G +(nd /usr/sbin should be o)171.6 619 Q(wned by root, mode 755)-.25 E F4(3)364.4 +615 Q F1(.)367.9 619 Q .32 LW 76 646 72 646 DL 80 646 76 646 DL 84 646 80 646 +DL 88 646 84 646 DL 92 646 88 646 DL 96 646 92 646 DL 100 646 96 646 DL 104 646 +100 646 DL 108 646 104 646 DL 112 646 108 646 DL 116 646 112 646 DL 120 646 116 +646 DL 124 646 120 646 DL 128 646 124 646 DL 132 646 128 646 DL 136 646 132 646 +DL 140 646 136 646 DL 144 646 140 646 DL 148 646 144 646 DL 152 646 148 646 DL +156 646 152 646 DL 160 646 156 646 DL 164 646 160 646 DL 168 646 164 646 DL 172 +646 168 646 DL 176 646 172 646 DL 180 646 176 646 DL 184 646 180 646 DL 188 646 +184 646 DL 192 646 188 646 DL 196 646 192 646 DL 200 646 196 646 DL 204 646 200 +646 DL 208 646 204 646 DL 212 646 208 646 DL 216 646 212 646 DL/F5 5 +/Times-Roman@0 SF(2)93.6 656.4 Q/F6 8/Times-Roman@0 SF .385 +(This is usually /usr/sbin on 4.4BSD and ne)3.2 J .385(wer systems; man)-.2 F +2.385(ys)-.12 G .385(ystems install it in /usr/lib)302.966 659.6 R 4.384(.I) +-.32 G .384(understand it is in /usr/ucblib on)398.744 659.6 R +(System V Release 4.)72 669.2 Q F5(3)93.6 679.6 Q F6 .15(Some v)3.2 J .15 +(endors ship them o)-.12 F .15 +(wned by bin; this creates a security hole that is not actually related to)-.2 +F/F7 8/Times-Italic@0 SF(sendmail)2.15 E F6 4.15(.O)C .149(ther important di-) +447.262 682.8 R(rectories that should ha)72 692.4 Q .24 -.12(ve r)-.16 H +(estricti).12 E .24 -.12(ve o)-.2 H(wnerships and permissions are /bin, /usr/b\ +in, /etc, /usr/etc, /lib, and /usr/lib)-.08 E(.)-.32 E EP +%%Page: 8 5 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 198.36(SMM:08-8 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E 2.5(1.3.2. /etc/sendmail.cf)102 96 R/F1 +10/Times-Roman@0 SF .78(This is the con\214guration \214le for sendmail.)142 +112.2 R .781(This and the frozen con\214guration \214le are the)5.781 F 2.178 +(only tw)117 126.2 R 4.678(on)-.1 G 2.178 +(on-library \214le names compiled into sendmail)164.036 126.2 R/F2 7 +/Times-Roman@0 SF(4)354.636 122.2 Q F1 7.179(.S)358.136 126.2 S 2.179 +(ome older systems install it in)373.375 126.2 R F0(/usr/lib/sendmail.cf)117 +138.2 Q F1(.)A(If you w)142 154.4 Q(ant to mo)-.1 E .3 -.15(ve t)-.15 H +(his \214le, change).15 E/F3 10/Times-Italic@0 SF(sr)2.5 E(c/pathnames.h)-.37 E +F1(.)A .721(The con\214guration \214le is normally created using the distrib) +142 170.6 R .721(ution \214les described abo)-.2 F -.15(ve)-.15 G 5.72(.I).15 G +(f)500.67 170.6 Q .64(you ha)117 182.6 R .94 -.15(ve a p)-.2 H .64 +(articularly unusual system con\214guration you may need to create a special v) +.15 F(ersion.)-.15 E +(The format of this \214le is detailed in later sections of this document.)117 +194.6 Q F0 2.5(1.3.3. /usr/ucb/newaliases)102 218.6 R F1 1.59 +(If you are running deli)142 234.8 R -.15(ve)-.25 G 1.589 +(rmail, it is critical that the).15 F F3(ne)4.089 E(waliases)-.15 E F1 1.589 +(command be replaced.)4.089 F(This can just be a link to)117 246.8 Q F3 +(sendmail)2.5 E F1(:)A(rm \255f /usr/ucb/ne)157 263 Q -.1(wa)-.25 G(liases).1 E +(ln /usr/sbin/sendmail /usr/ucb/ne)157 275 Q -.1(wa)-.25 G(liases).1 E +(This can be installed in whate)117 291.2 Q -.15(ve)-.25 G 2.5(rs).15 G +(earch path you prefer for your system.)254.91 291.2 Q F0 2.5(1.3.4. /v)102 +315.2 R(ar/spool/mqueue)-.1 E F1 .217(The directory)142 331.4 R F3 +(/var/spool/mqueue)2.717 E F1 .218(should be created to hold the mail queue.) +2.717 F .218(This directory)5.218 F(should be mode 700 and o)117 343.4 Q +(wned by root.)-.25 E(The actual path of this directory is de\214ned in the)142 +359.6 Q F0(Q)2.5 E F1(option of the)2.5 E F3(sendmail.cf)2.5 E F1(\214le.)2.5 E +F0 2.5(1.3.5. /etc/aliases*)102 383.6 R F1 1.493 +(The system aliases are held in \231/etc/aliases\232.)142 399.8 R 3.992(As) +6.492 G 1.492(ample is gi)350.01 399.8 R -.15(ve)-.25 G 3.992(ni).15 G 3.992 +<6e99>417.696 399.8 S 1.492(lib/aliases\232 which)431.128 399.8 R +(includes some aliases which)117 411.8 Q F3(must)2.5 E F1(be de\214ned:)2.5 E +(cp lib/aliases /etc/aliases)157 428 Q F3(edit /etc/aliases)157 440 Q F1 -1.1 +(Yo)117 456.2 S 2.5(us)1.1 G(hould e)139.51 456.2 Q(xtend this \214le with an) +-.15 E 2.5(ya)-.15 G(liases that are apropos to your system.)267.54 456.2 Q +(Normally)142 472.4 Q F3(sendmail)3.609 E F1 1.109(looks at a v)3.609 F 1.109 +(ersion of these \214les maintained by the)-.15 F F3(dbm)3.609 E F1 1.11 +(\(3\) or)1.666 F F3(db)3.61 E F1(\(3\))1.666 E 3.46(routines. These)117 484.4 +R .96(are stored either in \231/etc/aliases.dir\232 and \231/etc/aliases.pag\ +\232 or \231/etc/aliases.db\232)3.46 F 1.022 +(depending on which database package you are using.)117 496.4 R 1.022 +(These can initially be created as empty)6.022 F(\214les, b)117 508.4 Q(ut the) +-.2 E 2.5(yw)-.15 G(ill ha)180.54 508.4 Q .3 -.15(ve t)-.2 H 2.5(ob).15 G 2.5 +(ei)227.69 508.4 S(nitialized promptly)237.41 508.4 Q 5(.T)-.65 G +(hese should be mode 644:)326.76 508.4 Q(cp /de)157 524.6 Q +(v/null /etc/aliases.dir)-.25 E(cp /de)157 536.6 Q(v/null /etc/aliases.pag)-.25 +E(chmod 644 /etc/aliases.*)157 548.6 Q(ne)157 560.6 Q -.1(wa)-.25 G(liases).1 E +(The)117 576.8 Q F3(db)2.79 E F1 .29(routines preset the mode reasonably)2.79 F +2.79(,s)-.65 G 2.79(ot)301.68 576.8 S .29(his step can be skipped.)312.25 576.8 +R .29(The actual path of this)5.29 F(\214le is de\214ned in the)117 588.8 Q F0 +(A)2.5 E F1(option of the)2.5 E F3(sendmail.cf)2.5 E F1(\214le.)2.5 E F0 2.5 +(1.3.6. /etc/sendmail.fc)102 612.8 R F1 .609 +(If you intend to install the frozen v)142 629 R .61 +(ersion of the con\214guration \214le \(for quick startup\) you)-.15 F +(should create the \214le /etc/sendmail.fc and initialize it.)117 641 Q +(This step may be safely skipped.)5 E .32 LW 76 669.2 72 669.2 DL 80 669.2 76 +669.2 DL 84 669.2 80 669.2 DL 88 669.2 84 669.2 DL 92 669.2 88 669.2 DL 96 +669.2 92 669.2 DL 100 669.2 96 669.2 DL 104 669.2 100 669.2 DL 108 669.2 104 +669.2 DL 112 669.2 108 669.2 DL 116 669.2 112 669.2 DL 120 669.2 116 669.2 DL +124 669.2 120 669.2 DL 128 669.2 124 669.2 DL 132 669.2 128 669.2 DL 136 669.2 +132 669.2 DL 140 669.2 136 669.2 DL 144 669.2 140 669.2 DL 148 669.2 144 669.2 +DL 152 669.2 148 669.2 DL 156 669.2 152 669.2 DL 160 669.2 156 669.2 DL 164 +669.2 160 669.2 DL 168 669.2 164 669.2 DL 172 669.2 168 669.2 DL 176 669.2 172 +669.2 DL 180 669.2 176 669.2 DL 184 669.2 180 669.2 DL 188 669.2 184 669.2 DL +192 669.2 188 669.2 DL 196 669.2 192 669.2 DL 200 669.2 196 669.2 DL 204 669.2 +200 669.2 DL 208 669.2 204 669.2 DL 212 669.2 208 669.2 DL 216 669.2 212 669.2 +DL/F4 5/Times-Roman@0 SF(4)93.6 679.6 Q/F5 8/Times-Roman@0 SF .589 +(The system libraries can reference other \214les; in particular)3.2 J 2.589 +(,s)-.32 G .588 +(ystem library subroutines that sendmail calls probably reference)294.809 682.8 +R/F6 8/Times-Italic@0 SF(/etc/passwd)72 692.4 Q F5(and)2 E F6(/etc/r)2 E(esolv) +-.296 E(.conf)-.592 E F5(.)A EP +%%Page: 9 6 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-9)457.9 60 Q/F1 10/Times-Roman@0 SF(cp /de)157 96 Q +(v/null /etc/sendmail.fc)-.25 E(chmod 644 /etc/sendmail.fc)157 108 Q +(/usr/sbin/sendmail \255bz)157 120 Q 1.028 +(In general, freeze \214les are not w)117 136.2 R 1.027 +(orth doing unless your disks are much f)-.1 F 1.027(aster than your CPU;)-.1 F +(this is seldom true an)117 148.2 Q 2.5(ym)-.15 G(ore.)217.13 148.2 Q 1.32 +(If your)142 164.4 R/F2 10/Times-Italic@0 SF(sendmail)3.82 E F1 -.1(wa)3.821 G +3.821(sn).1 G 1.321(ot compiled with)238.832 164.4 R/F3 9/Times-Roman@0 SF(FR) +3.821 E(OZENCONFIG)-.36 E F1 1.321(de\214ned, the)3.821 F F0(\255bz)3.821 E F1 +1.321(\215ag will be)3.821 F(ignored.)117 176.4 Q F0 2.5(1.3.7. /etc/r)102 +200.4 R(c)-.18 E F1 .155(It will be necessary to start up the sendmail daemon \ +when your system reboots.)142 216.6 R .155(This dae-)5.155 F 1.537 +(mon performs tw)117 228.6 R 4.037(of)-.1 G 1.537 +(unctions: it listens on the SMTP sock)201.221 228.6 R 1.537 +(et for connections \(to recei)-.1 F 1.838 -.15(ve m)-.25 H(ail).15 E .442(fro\ +m a remote system\) and it processes the queue periodically to insure that mai\ +l gets deli)117 240.6 R -.15(ve)-.25 G(red).15 E(when hosts come up.)117 252.6 +Q .505(Add the follo)142 268.8 R .505(wing lines to \231/etc/rc\232 \(or \231/\ +etc/rc.local\232 as appropriate\) in the area where it)-.25 F +(is starting up the daemons:)117 280.8 Q +(if [ \255f /usr/sbin/sendmail \255a \255f /etc/sendmail.cf ]; then)157 297 Q +(\(cd /v)193 309 Q(ar/spool/mqueue; rm \255f [lnx]f*\))-.25 E +(/usr/sbin/sendmail \255bd \255q30m &)193 321 Q(echo \255n ' sendmail' >/de)193 +333 Q(v/console)-.25 E<8c>157 345 Q .174 +(The \231cd\232 and \231rm\232 commands insure that all lock \214les ha)117 +361.2 R .473 -.15(ve b)-.2 H .173(een remo).15 F -.15(ve)-.15 G .173(d; e).15 F +.173(xtraneous lock \214les)-.15 F .004 +(may be left around if the system goes do)117 373.2 R .005 +(wn in the middle of processing a message.)-.25 F .005(The line that)5.005 F +2.294(actually in)117 385.2 R -.2(vo)-.4 G -.1(ke).2 G(s).1 E F2(sendmail)4.794 +E F1 2.294(has tw)4.794 F 4.794<6f8d>-.1 G 2.293 +(ags: \231\255bd\232 causes it to listen on the SMTP port, and)272.94 385.2 R +(\231\255q30m\232 causes it to run the queue e)117 397.2 Q -.15(ve)-.25 G +(ry half hour).15 E(.)-.55 E .378(Some people use a more comple)142 413.4 R +2.879(xs)-.15 G .379(tartup script, remo)285.209 413.4 R .379 +(ving zero length qf \214les and df \214les)-.15 F +(for which there is no qf \214le.)117 425.4 Q -.15(Fo)5 G 2.5(re).15 G(xample:) +253.9 425.4 Q EP +%%Page: 10 7 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-10 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF 2.5(#r)157 96 S +(emo)167.83 96 Q .3 -.15(ve z)-.15 H(ero length qf \214les).15 E(for qf)157 108 +Q(\214le in qf*)-.25 E(do)157 120 Q(if [ \255r $qf)193 132 Q(\214le ])-.25 E +(then)193 144 Q(if [ ! \255s $qf)229 156 Q(\214le ])-.25 E(then)229 168 Q +(echo \255n " <zero: $qf)265 180 Q(\214le>" > /de)-.25 E(v/console)-.25 E +(rm \255f $qf)265 192 Q(\214le)-.25 E<8c>229 204 Q<8c>193 216 Q(done)157 228 Q +2.5(#r)157 240 S(ename tf \214les to be qf if the qf does not e)167.83 240 Q +(xist)-.15 E(for tf)157 252 Q(\214le in tf*)-.25 E(do)157 264 Q(qf)193 276 Q +(\214le=`echo $tf)-.25 E(\214le | sed ')-.25 E(s/t/q/'`)-.55 E(if [ \255r $tf) +193 288 Q(\214le \255a ! \255f $qf)-.25 E(\214le ])-.25 E(then)193 300 Q +(echo \255n " <reco)229 312 Q -.15(ve)-.15 G(ring: $tf).15 E(\214le>" > /de) +-.25 E(v/console)-.25 E(mv $tf)229 324 Q(\214le $qf)-.25 E(\214le)-.25 E(else) +193 336 Q(echo \255n " <e)229 348 Q(xtra: $tf)-.15 E(\214le>" > /de)-.25 E +(v/console)-.25 E(rm \255f $tf)229 360 Q(\214le)-.25 E<8c>193 372 Q(done)157 +384 Q 2.5(#r)157 396 S(emo)167.83 396 Q .3 -.15(ve b)-.15 H(ogus qf \214les).15 +E(for df)157 408 Q(\214le in df*)-.25 E(do)157 420 Q(qf)193 432 Q +(\214le=`echo $df)-.25 E(\214le | sed ')-.25 E(s/d/q/'`)-.55 E(if [ \255r $df) +193 444 Q(\214le \255a ! \255f $qf)-.25 E(\214le ])-.25 E(then)193 456 Q +(echo \255n " <incomplete: $df)229 468 Q(\214le>" > /de)-.25 E(v/console)-.25 E +(rm \255f $df)229 480 Q(\214le)-.25 E<8c>193 492 Q(done)157 504 Q<8c>157 516 Q +.756(If you are not running a v)142 536.4 R .755 +(ersion of UNIX that supports Berk)-.15 F(ele)-.1 E 3.255(yT)-.15 G(CP/IP) +416.725 536.4 Q 3.255(,d)-1.11 G 3.255(on)450.27 536.4 S .755(ot include) +463.525 536.4 R(the)117 548.4 Q F0(\255bd)2.5 E F1(\215ag.)2.5 E F0 2.5 +(1.3.8. /usr/lib/sendmail.hf)102 572.4 R F1 2.078 +(This is the help \214le used by the SMTP)142 588.6 R F0(HELP)4.578 E F1 4.578 +(command. It)4.578 F 2.078(should be copied from)4.578 F +(\231lib/sendmail.hf\232:)117 600.6 Q(cp lib/sendmail.hf /usr/lib)157 616.8 Q +(The actual path of this \214le is de\214ned in the)117 633 Q F0(H)2.5 E F1 +(option of the)2.5 E/F2 10/Times-Italic@0 SF(sendmail.cf)2.5 E F1(\214le.)2.5 E +F0 2.5(1.3.9. /etc/sendmail.st)102 657 R F1 3.04 +(If you wish to collect statistics about your mail traf)142 673.2 R 3.04 +(\214c, you should create the \214le)-.25 F(\231/etc/sendmail.st\232:)117 685.2 +Q(cp /de)157 701.4 Q(v/null /etc/sendmail.st)-.25 E(chmod 666 /etc/sendmail.st) +157 713.4 Q EP +%%Page: 11 8 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-11)452.9 60 Q/F1 10/Times-Roman@0 SF .715(This \214le does not gro)117 +96 R 4.516 -.65(w. I)-.25 H 3.216(ti).65 G 3.216(sp)231.502 96 S .716 +(rinted with the program \231mailstats/mailstats.c.)243.608 96 R 5.716<9a54>-.7 +G .716(he actual path)447.028 96 R(of this \214le is de\214ned in the)117 108 Q +F0(S)2.5 E F1(option of the)2.5 E/F2 10/Times-Italic@0 SF(sendmail.cf)2.5 E F1 +(\214le.)2.5 E F0 2.5(1.3.10. /usr/ucb/newaliases)102 132 R F1(If)142 148.2 Q +F2(sendmail)3.256 E F1 .756(is in)3.256 F -.2(vo)-.4 G -.1(ke).2 G 3.256(da).1 +G 3.256<7399>240.424 148.2 S(ne)252.01 148.2 Q -.1(wa)-.25 G(liases,).1 E 3.255 +<9a69>-.7 G 3.255(tw)307.255 148.2 S .755(ill simulate the)320.51 148.2 R F0 +(\255bi)3.255 E F1 .755(\215ag \(i.e., will reb)3.255 F .755(uild the)-.2 F +(alias database; see belo)117 160.2 Q 2.5(w\). This)-.25 F +(should be a link to /usr/sbin/sendmail.)2.5 E F0 2.5(1.3.11. /usr/ucb/mailq) +102 184.2 R F1(If)142 200.4 Q F2(sendmail)3.439 E F1 .939(is in)3.439 F -.2(vo) +-.4 G -.1(ke).2 G 3.439(da).1 G 3.439<7399>241.156 200.4 S(mailq,)252.925 200.4 +Q 3.439<9a69>-.7 G 3.439(tw)288.164 200.4 S .939(ill simulate the)301.603 200.4 +R F0(\255bp)3.439 E F1 .94(\215ag \(i.e.,)3.44 F F2(sendmail)3.44 E F1 .94 +(will print)3.44 F(the contents of the mail queue; see belo)117 212.4 Q 2.5 +(w\). This)-.25 F(should be a link to /usr/sbin/sendmail.)2.5 E F0 2.5 +(2. NORMAL)72 236.4 R(OPERA)2.5 E(TIONS)-.95 E 2.5(2.1. `)87 260.4 R(`Quick') +-.63 E 2.5('C)-.63 G(on\214guration Startup)154.9 260.4 Q F1 .523(if the)127 +276.6 R/F3 9/Times-Roman@0 SF(FR)3.023 E(OZENCONFIG)-.36 E F1 .523 +(option is included during compilation, a precompiled \(`)3.023 F(`frozen')-.74 +E .522('\) v)-.74 F(er)-.15 E(-)-.2 E .068 +(sion of the con\214guration \214le can be created using the)102 288.6 R F0 +(\255bz)2.568 E F1 2.568(\215ag. This)2.568 F .069(is really only w)2.568 F +.069(orthwhile doing)-.1 F .432(if you are on a slo)102 300.6 R 2.932(wp)-.25 G +.432(rocessor with a relati)190.994 300.6 R -.15(ve)-.25 G .432(ly f).15 F .432 +(ast I/O system \(a V)-.1 F .432(AX 11/750 is a good e)-1.35 F(xample\).)-.15 E +.131(Since it creates other problems, I recommend ag)102 312.6 R .132 +(ainst using the frozen con\214guration on most current)-.05 F(architectures.) +102 324.6 Q 1.6 -.8(To c)127 340.8 T(reate the freeze \214le, use).8 E +(/usr/sbin/sendmail \255bz)142 357 Q .762 +(This creates the frozen con\214guration \214le)102 373.2 R F2 +(/etc/sendmail.fc)3.262 E F1 5.761(.T)C .761(his \214le is an image of)348.403 +373.2 R F2(sendmail)3.261 E F1 1.861 -.55('s d)D(ata).55 E .692 +(space after reading in the con\214guration \214le.)102 385.2 R .693 +(If this \214le e)5.692 F .693(xists, it is used instead of)-.15 F F2 +(/etc/sendmail.cf)3.193 E(sendmail.fc)102 397.2 Q F1(must be reb)2.5 E +(uilt manually e)-.2 E -.15(ve)-.25 G(ry time).15 E F2(sendmail.cf)2.5 E F1 +(is changed.)2.5 E .953(The frozen con\214guration \214le will be ignored if a) +127 413.4 R F0<ad43>3.452 E F1 .952 +(\215ag is speci\214ed or if sendmail detects)3.452 F(that it is out of date.) +102 425.4 Q(Ho)5 E(we)-.25 E -.15(ve)-.25 G .8 -.4(r, t).15 H +(he heuristics are not strong so this should not be trusted.).4 E F0 2.5 +(2.2. The)87 449.4 R(System Log)2.5 E F1(The system log is supported by the)127 +465.6 Q F2(syslo)2.5 E(gd)-.1 E F1(\(8\) program.)1.666 E F0 2.5(2.2.1. F)102 +489.6 R(ormat)-.25 E F1 .574(Each line in the system log consists of a timesta\ +mp, the name of the machine that gener)142 505.8 R(-)-.2 E .849 +(ated it \(for logging from se)117 517.8 R -.15(ve)-.25 G .849(ral machines o) +.15 F -.15(ve)-.15 G 3.349(rt).15 G .848(he local area netw)316.942 517.8 R +.848(ork\), the w)-.1 F .848(ord \231sendmail:\232,)-.1 F(and a message.)117 +529.8 Q F0 2.5(2.2.2. Le)102 553.8 R -.1(ve)-.15 G(ls).1 E F1 .204(If you ha) +142 570 R -.15(ve)-.2 G F2(syslo)2.854 E(gd)-.1 E F1 .204(\(8\) or an equi) +1.666 F -.25(va)-.25 G .205(lent installed, you will be able to do logging.).25 +F .205(There is)5.205 F 2.788(al)117 582 S(ar)127.008 582 Q .287 +(ge amount of information that can be logged.)-.18 F .287 +(The log is arranged as a succession of le)5.287 F -.15(ve)-.25 G(ls.).15 E .65 +(At the lo)117 594 R .65(west le)-.25 F -.15(ve)-.25 G 3.15(lo).15 G .65(nly e) +201.72 594 R .651(xtremely strange situations are logged.)-.15 F .651 +(At the highest le)5.651 F -.15(ve)-.25 G .651(l, e).15 F -.15(ve)-.25 G 3.151 +(nt).15 G(he)494.56 594 Q .826(most mundane and uninteresting e)117 606 R -.15 +(ve)-.25 G .825(nts are recorded for posterity).15 F 5.825(.A)-.65 G 3.325(sac) +400.27 606 S(on)419.69 606 Q -.15(ve)-.4 G .825(ntion, log le).15 F -.15(ve) +-.25 G(ls).15 E .2(under ten are considered generally \231useful;\232 log le) +117 618 R -.15(ve)-.25 G .201(ls abo).15 F .501 -.15(ve 6)-.15 H 2.701(4a).15 G +.201(re reserv)381.566 618 R .201(ed for deb)-.15 F .201(ugging pur)-.2 F(-)-.2 +E 2.5(poses. Le)117 630 R -.15(ve)-.25 G(ls from 11\25564 are reserv).15 E +(ed for v)-.15 E(erbose information that some sites might w)-.15 E(ant.)-.1 E +2.5(Ac)142 646.2 S(omplete description of the log le)156.16 646.2 Q -.15(ve) +-.25 G(ls is gi).15 E -.15(ve)-.25 G 2.5(ni).15 G 2.5(ns)340.35 646.2 S +(ection 4.6.)351.74 646.2 Q F0 2.5(2.3. The)87 670.2 R(Mail Queue)2.5 E F1 .263 +(The mail queue should be processed transparently)127 686.4 R 5.262(.H)-.65 G +-.25(ow)342.868 686.4 S -2.15 -.25(ev e).25 H 1.062 -.4(r, y).25 H .262 +(ou may \214nd that manual inter).4 F(-)-.2 E -.15(ve)102 698.4 S .081 +(ntion is sometimes necessary).15 F 5.081(.F)-.65 G .081(or e)240.254 698.4 R +.081(xample, if a major host is do)-.15 F .081 +(wn for a period of time the queue)-.25 F .268(may become clogged.)102 710.4 R +.268(Although sendmail ought to reco)5.268 F -.15(ve)-.15 G 2.768(rg).15 G .268 +(racefully when the host comes up, you)348.254 710.4 R +(may \214nd performance unacceptably bad in the meantime.)102 722.4 Q EP +%%Page: 12 9 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-12 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E 2.5(2.3.1. Printing)102 96 R(the queue) +2.5 E/F1 10/Times-Roman@0 SF .526 +(The contents of the queue can be printed using the)142 112.2 R/F2 10 +/Times-Italic@0 SF(mailq)3.026 E F1 .526(command \(or by specifying the)3.026 F +F0(\255bp)117 124.2 Q F1(\215ag to sendmail\):)2.5 E(mailq)157 140.4 Q 1.673 +(This will produce a listing of the queue id')117 156.6 R 1.673 +(s, the size of the message, the date the message)-.55 F +(entered the queue, and the sender and recipients.)117 168.6 Q F0 2.5(2.3.2. F) +102 192.6 R(or)-.25 E(cing the queue)-.18 E F2(Sendmail)142 208.8 Q F1 1.137 +(should run the queue automatically at interv)3.637 F 3.638(als. The)-.25 F +1.138(algorithm is to read and)3.638 F .355 +(sort the queue, and then to attempt to process all jobs in order)117 220.8 R +5.355(.W)-.55 G .355(hen it attempts to run the job,)384.37 220.8 R F2 +(sendmail)117 232.8 Q F1(\214rst checks to see if the job is lock)2.5 E 2.5 +(ed. If)-.1 F(so, it ignores the job)2.5 E(.)-.4 E .338 +(There is no attempt to insure that only one queue processor e)142 249 R .338 +(xists at an)-.15 F 2.838(yt)-.15 G .339(ime, since there)440.282 249 R .095 +(is no guarantee that a job cannot tak)117 261 R 2.595(ef)-.1 G(ore)272.07 261 +Q -.15(ve)-.25 G 2.595(rt).15 G 2.595(op)302.585 261 S .094(rocess \(ho)315.18 +261 R(we)-.25 E -.15(ve)-.25 G -.4(r,).15 G F2(sendmail)2.994 E F1 .094 +(does include heuris-)2.594 F 1.086 +(tics to try to abort jobs that are taking absurd amounts of time; technically) +117 273 R 3.587(,t)-.65 G 1.087(his violates RFC)435.146 273 R .462(821, b)117 +285 R .461(ut is blessed by RFC 1123\).)-.2 F .461 +(Due to the locking algorithm, it is impossible for one job to)5.461 F 1.086 +(freeze the entire queue.)117 297 R(Ho)6.086 E(we)-.25 E -.15(ve)-.25 G 1.886 +-.4(r, a).15 H 3.586(nu).4 G(ncooperati)279.346 297 Q 1.386 -.15(ve r)-.25 H +1.086(ecipient host or a program recipient that).15 F(ne)117 309 Q -.15(ve)-.25 +G 3.351(rr).15 G .851(eturns can accumulate man)145.491 309 R 3.351(yp)-.15 G +.851(rocesses in your system.)269.825 309 R(Unfortunately)5.851 E 3.351(,t)-.65 +G .85(here is no com-)439.52 309 R(pletely general w)117 321 Q(ay to solv)-.1 E +2.5(et)-.15 G(his.)234.23 321 Q .082 +(In some cases, you may \214nd that a major host going do)142 337.2 R .083 +(wn for a couple of days may create)-.25 F 2.925(ap)117 349.2 S(rohibiti) +129.365 349.2 Q -.15(ve)-.25 G .425(ly lar).15 F .425(ge queue.)-.18 F .424 +(This will result in)5.425 F F2(sendmail)2.924 E F1 .424 +(spending an inordinate amount of time)2.924 F 1.084(sorting the queue.)117 +361.2 R 1.084(This situation can be \214x)6.084 F 1.084(ed by mo)-.15 F 1.085 +(ving the queue to a temporary place and)-.15 F .023(creating a ne)117 373.2 R +2.523(wq)-.25 G 2.523(ueue. The)182.629 373.2 R .022 +(old queue can be run later when the of)2.523 F .022 +(fending host returns to service.)-.25 F 1.6 -.8(To d)142 389.4 T 2.5(ot).8 G +(his, it is acceptable to mo)170.09 389.4 Q .3 -.15(ve t)-.15 H +(he entire queue directory:).15 E(cd /v)157 405.6 Q(ar/spool)-.25 E +(mv mqueue omqueue; mkdir mqueue; chmod 700 mqueue)157 417.6 Q -1.1(Yo)117 +433.8 S 2.708(us)1.1 G .208(hould then kill the e)139.718 433.8 R .209 +(xisting daemon \(since it will still be processing in the old queue direc-) +-.15 F(tory\) and create a ne)117 445.8 Q 2.5(wd)-.25 G(aemon.)213.1 445.8 Q +1.6 -.8(To r)142 462 T(un the old mail queue, run the follo).8 E(wing command:) +-.25 E(/usr/sbin/sendmail \255oQ/v)157 478.2 Q(ar/spool/omqueue \255q)-.25 E +(The)117 494.4 Q F0(\255oQ)2.868 E F1 .367 +(\215ag speci\214es an alternate queue directory and the)2.868 F F0<ad71>2.867 +E F1 .367(\215ag says to just run e)2.867 F -.15(ve)-.25 G .367(ry job in).15 F +.593(the queue.)117 506.4 R .593(If you ha)5.593 F .893 -.15(ve a t)-.2 H +(endenc).15 E 3.093(yt)-.15 G -2.1 -.25(ow a)263.111 506.4 T .593(rd v).25 F +-.1(oy)-.2 G .593(eurism, you can use the).1 F F0<ad76>3.094 E F1 .594 +(\215ag to w)3.094 F .594(atch what is)-.1 F(going on.)117 518.4 Q +(When the queue is \214nally emptied, you can remo)142 534.6 Q .3 -.15(ve t) +-.15 H(he directory:).15 E(rmdir /v)157 550.8 Q(ar/spool/omqueue)-.25 E F0 2.5 +(2.4. The)87 579 R(Alias Database)2.5 E F1 .361(The alias database e)127 595.2 +R .361(xists in tw)-.15 F 2.861(of)-.1 G 2.861(orms. One)261.116 595.2 R .361 +(is a te)2.861 F .36(xt form, maintained in the \214le)-.15 F F2(/etc/aliases.) +2.86 E F1(The aliases are of the form)102 607.2 Q(name: name1, name2, ...)142 +623.4 Q(Only local names may be aliased; e.g.,)102 639.6 Q(eric@prep.ai.MIT)142 +655.8 Q(.EDU: eric@CS.Berk)-.74 E(ele)-.1 E -.65(y.)-.15 G(EDU).65 E .348 +(will not ha)102 672 R .648 -.15(ve t)-.2 H .348(he desired ef).15 F 2.849 +(fect. Aliases)-.25 F .349(may be continued by starting an)2.849 F 2.849(yc) +-.15 G .349(ontinuation lines with a)408.783 672 R(space or a tab)102 684 Q 5 +(.B)-.4 G(lank lines and lines be)170.47 684 Q +(ginning with a sharp sign \(\231#\232\) are comments.)-.15 E 1.593 +(The second form is processed by the)127 700.2 R F2(dbm)4.093 E F1 1.593 +(\(3\) \(or)1.666 F F2(db)4.093 E F1 1.593(\(3\)\) library)1.666 F 6.593(.T) +-.65 G 1.593(his form is in the \214les)409.085 700.2 R F2(/etc/aliases.dir)102 +712.2 Q F1(and)3.028 E F2(/etc/aliases.pa)3.028 E -.15(g.)-.1 G F1 .528 +(This is the form that)5.678 F F2(sendmail)3.029 E F1 .529 +(actually uses to resolv)3.029 F 3.029(ea)-.15 G(liases.)479.28 712.2 Q +(This technique is used to impro)102 724.2 Q .3 -.15(ve p)-.15 H(erformance.) +.15 E EP +%%Page: 13 10 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-13)452.9 60 Q/F1 10/Times-Roman@0 SF -1.1(Yo)127 96 S 2.5(uc)1.1 G +(an also use)150.06 96 Q/F2 9/Times-Roman@0 SF(NIS)2.5 E F1 +(-based alias \214les.)A -.15(Fo)5 G 2.5(re).15 G(xample, the speci\214cation:) +305.069 96 Q -.35(OA)142 112.2 S(/etc/aliases).35 E -.35(OA)142 124.2 S +(nis:mail.aliases@my).35 E(.nis.domain)-.65 E(will \214rst search the /etc/ali\ +ases \214le and then the map named \231mail.aliases\232 in \231my)102 140.4 Q +(.nis.domain\232.)-.65 E(Additional \215ags can be added after the colon e)127 +156.6 Q(xactly lik)-.15 E 2.5(ea)-.1 G F0(K)A F1(line \212 for e)2.5 E(xample:) +-.15 E -.35(OA)142 172.8 S(nis:-N mail.aliases@my).35 E(.nis.domain)-.65 E +(will search the appropriate NIS map and al)102 189 Q -.1(wa)-.1 G +(ys include null bytes in the k).1 E -.15(ey)-.1 G(.)-.5 E F0 2.5(2.4.1. Reb) +102 213 R(uilding the alias database)-.2 E F1 .542(The DB or DBM v)142 229.2 R +.542(ersion of the database may be reb)-.15 F .542(uilt e)-.2 F .542 +(xplicitly by e)-.15 F -.15(xe)-.15 G .542(cuting the com-).15 F(mand)117 241.2 +Q(ne)157 257.4 Q -.1(wa)-.25 G(liases).1 E(This is equi)117 273.6 Q -.25(va) +-.25 G(lent to gi).25 E(ving)-.25 E/F3 10/Times-Italic@0 SF(sendmail)2.5 E F1 +(the)2.5 E F0(\255bi)2.5 E F1(\215ag:)2.5 E(/usr/sbin/sendmail \255bi)157 289.8 +Q .259(If the \231D\232 option is speci\214ed in the con\214guration,)142 310.2 +R F3(sendmail)2.759 E F1 .26(will reb)2.759 F .26(uild the alias database)-.2 F +1.921(automatically if possible when it is out of date.)117 322.2 R(Auto-reb) +6.921 E 1.92(uild can be dangerous on hea)-.2 F(vily)-.2 E 1.45 +(loaded machines with lar)117 334.2 R 1.45(ge alias \214les; if it might tak) +-.18 F 3.951(em)-.1 G 1.451(ore than \214v)355.654 334.2 R 3.951(em)-.15 G +1.451(inutes to reb)420.127 334.2 R 1.451(uild the)-.2 F +(database, there is a chance that se)117 346.2 Q -.15(ve)-.25 G +(ral processes will start the reb).15 E(uild process simultaneously)-.2 E(.) +-.65 E 1.77(If you ha)142 362.4 R 2.07 -.15(ve m)-.2 H 1.77 +(ultiple aliases databases speci\214ed, the).15 F F0(\255bi)4.27 E F1 1.77 +(\215ag reb)4.27 F 1.77(uilds all the database)-.2 F +(types it understands \(for e)117 374.4 Q(xample, it can reb)-.15 E +(uild dbm databases b)-.2 E(ut not nis databases\).)-.2 E F0 2.5(2.4.2. P)102 +398.4 R(otential pr)-.2 E(oblems)-.18 E F1 1.131 +(There are a number of problems that can occur with the alias database.)142 +414.6 R(The)6.131 E 3.631(ya)-.15 G 1.131(ll result)472.589 414.6 R 1.104 +(from a)117 426.6 R F3(sendmail)3.604 E F1 1.104(process accessing the DBM v) +3.604 F 1.103(ersion while it is only partially b)-.15 F 3.603(uilt. This)-.2 F +(can)3.603 E 1.248(happen under tw)117 438.6 R 3.748(oc)-.1 G 1.248 +(ircumstances: One process accesses the database while another process is) +199.234 438.6 R(reb)117 450.6 Q .518(uilding it, or the process reb)-.2 F .518 +(uilding the database dies \(due to being killed or a system crash\))-.2 F +(before completing the reb)117 462.6 Q(uild.)-.2 E .792(Sendmail has tw)142 +478.8 R 3.292(ot)-.1 G .792(echniques to try to relie)220.666 478.8 R 1.092 +-.15(ve t)-.25 H .792(hese problems.).15 F .793(First, it ignores interrupts) +5.793 F .045(while reb)117 490.8 R .045(uilding the database; this a)-.2 F -.2 +(vo)-.2 G .045(ids the problem of someone aborting the process lea).2 F .045 +(ving a)-.2 F(partially reb)117 502.8 Q(uilt database.)-.2 E +(Second, at the end of the reb)5 E(uild it adds an alias of the form)-.2 E +(@: @)157 519 Q .336(\(which is not normally le)117 535.2 R -.05(ga)-.15 G +2.836(l\). Before).05 F .336 +(sendmail will access the database, it checks to insure that)2.836 F +(this entry e)117 549.2 Q(xists)-.15 E/F4 7/Times-Roman@0 SF(5)179.63 545.2 Q +F1(.)183.13 549.2 Q F0 2.5(2.4.3. List)102 573.2 R -.1(ow)2.5 G(ners).1 E F1 .4 +(If an error occurs on sending to a certain address, say \231)142 589.4 R F3(x) +A F1<9a2c>A F3(sendmail)2.901 E F1 .401(will look for an alias)2.901 F .418 +(of the form \231o)117 601.4 R(wner)-.25 E(-)-.2 E F3(x)A F1 2.918<9a74>C 2.918 +(or)212.632 601.4 S(ecei)223.88 601.4 Q .718 -.15(ve t)-.25 H .418(he errors.) +.15 F .417(This is typically useful for a mailing list where the)5.418 F 1.116 +(submitter of the list has no control o)117 613.4 R -.15(ve)-.15 G 3.617(rt).15 +G 1.117(he maintenance of the list itself; in this case the list)288.4 613.4 R +(maintainer w)117 625.4 Q(ould be the o)-.1 E(wner of the list.)-.25 E -.15(Fo) +5 G 2.5(re).15 G(xample:)309.38 625.4 Q .32 LW 76 669.2 72 669.2 DL 80 669.2 76 +669.2 DL 84 669.2 80 669.2 DL 88 669.2 84 669.2 DL 92 669.2 88 669.2 DL 96 +669.2 92 669.2 DL 100 669.2 96 669.2 DL 104 669.2 100 669.2 DL 108 669.2 104 +669.2 DL 112 669.2 108 669.2 DL 116 669.2 112 669.2 DL 120 669.2 116 669.2 DL +124 669.2 120 669.2 DL 128 669.2 124 669.2 DL 132 669.2 128 669.2 DL 136 669.2 +132 669.2 DL 140 669.2 136 669.2 DL 144 669.2 140 669.2 DL 148 669.2 144 669.2 +DL 152 669.2 148 669.2 DL 156 669.2 152 669.2 DL 160 669.2 156 669.2 DL 164 +669.2 160 669.2 DL 168 669.2 164 669.2 DL 172 669.2 168 669.2 DL 176 669.2 172 +669.2 DL 180 669.2 176 669.2 DL 184 669.2 180 669.2 DL 188 669.2 184 669.2 DL +192 669.2 188 669.2 DL 196 669.2 192 669.2 DL 200 669.2 196 669.2 DL 204 669.2 +200 669.2 DL 208 669.2 204 669.2 DL 212 669.2 208 669.2 DL 216 669.2 212 669.2 +DL/F5 5/Times-Roman@0 SF(5)93.6 679.6 Q/F6 8/Times-Roman@0 SF .389(The \231a\ +\232 option is required in the con\214guration for this action to occur)3.2 J +4.389(.T)-.44 G .389(his should normally be speci\214ed unless you are run-) +334.24 682.8 R(ning)72 692.4 Q/F7 8/Times-Italic@0 SF(delivermail)2 E F6 +(in parallel with)2 E F7(sendmail.)2 E EP +%%Page: 14 11 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-14 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF +(unix-wizards: eric@ucbarpa, wnj@monet, nosuchuser)157 96 Q(,)-.4 E +(sam@matisse)193 108 Q -.25(ow)157 120 S(ner).25 E(-unix-wizards: eric@ucbarpa) +-.2 E -.1(wo)117 136.2 S 1.959(uld cause \231eric@ucbarpa\232 to get the error\ + that will occur when someone sends to unix-).1 F +(wizards due to the inclusion of \231nosuchuser\232 on the list.)117 148.2 Q +.958(List o)142 164.4 R .958(wners also cause the en)-.25 F -.15(ve)-.4 G .959 +(lope sender address to be modi\214ed.).15 F .959(The contents of the)5.959 F +-.25(ow)117 176.4 S .429(ner alias are used if the).25 F 2.929(yp)-.15 G .429 +(oint to a single user)236.364 176.4 R 2.928(,o)-.4 G .428 +(therwise the name of the alias itself is used.)326.436 176.4 R -.15(Fo)117 +188.4 S 2.5(rt).15 G(his reason, and to obe)136.02 188.4 Q 2.5(yI)-.15 G +(nternet con)233.63 188.4 Q -.15(ve)-.4 G(ntions, a typical scheme w).15 E +(ould be:)-.1 E 18.49(list: some,)157 204.6 R(set, of, addresses)2.5 E 22.28 +(list-request: list-admin-1,)157 216.6 R(list-admin-2, ...)2.5 E -.25(ow)157 +228.6 S(ner).25 E 26.62(-list: list-request)-.2 F F0 2.5(2.5. User)87 256.8 R +(Inf)2.5 E(ormation Database)-.25 E F1 1.059(If you ha)127 273 R 1.359 -.15 +(ve a ve)-.2 H 1.059(rsion of).15 F/F2 10/Times-Italic@0 SF(sendmail)3.559 E F1 +1.06(with the user information database compiled in, and you)3.559 F(ha)102 285 +Q 2.206 -.15(ve s)-.2 H 1.906(peci\214ed one or more databases using the).15 F +F0(U)4.406 E F1 1.905(option, the databases will be searched for a)4.406 F F2 +(user)102 297 Q F1(:maildrop entry)A 5(.I)-.65 G 2.5(ff)191.34 297 S +(ound, the mail will be sent to the speci\214ed address.)200.5 297 Q 1.288 +(If the \214rst tok)127 313.2 R 1.288(en passed to user part of the \231local\ +\232 mailer is an at sign, the at sign will be)-.1 F(stripped of)102 325.2 Q +2.5(fa)-.25 G(nd this step will be skipped.)155.07 325.2 Q F0 2.5(2.6. P)87 +349.2 R(er)-.2 E(-User F)-.37 E(orwarding \(.f)-.25 E(orward Files\))-.25 E F1 +.121(As an alternati)127 365.4 R .421 -.15(ve t)-.25 H 2.621(ot).15 G .121 +(he alias database, an)210.404 365.4 R 2.621(yu)-.15 G .12 +(ser may put a \214le with the name \231.forw)304.878 365.4 R .12 +(ard\232 in his)-.1 F .205(or her home directory)102 377.4 R 5.205(.I)-.65 G +2.705(ft)199.92 377.4 S .205(his \214le e)208.735 377.4 R(xists,)-.15 E F2 +(sendmail)2.705 E F1 .205 +(redirects mail for that user to the list of addresses)2.705 F .909 +(listed in the .forw)102 389.4 R .908(ard \214le.)-.1 F -.15(Fo)5.908 G 3.408 +(re).15 G .908 +(xample, if the home directory for user \231mckusick\232 has a .forw)233.98 +389.4 R(ard)-.1 E(\214le with contents:)102 401.4 Q(mckusick@ernie)142 417.6 Q +(kirk@calder)142 429.6 Q(then an)102 445.8 Q 2.5(ym)-.15 G(ail arri)146.29 +445.8 Q +(ving for \231mckusick\232 will be redirected to the speci\214ed accounts.)-.25 +E(Actually)127 462 Q 3.374(,t)-.65 G .874 +(he con\214guration \214le de\214nes a sequence of \214lenames to check.) +169.444 462 R .875(By def)5.875 F .875(ault, this is)-.1 F .688(the user')102 +474 R 3.188(s.)-.55 G(forw)146.426 474 Q .688(ard \214le, b)-.1 F .687 +(ut can be de\214ned to be more generally using the)-.2 F F0(J)3.187 E F1 3.187 +(option. If)3.187 F .687(you change)3.187 F .393(this, you will ha)102 486 R +.693 -.15(ve t)-.2 H 2.893(oi).15 G .393 +(nform your user base of the change; .forw)193.064 486 R .393 +(ard is pretty well incorporated into)-.1 F(the collecti)102 498 Q .3 -.15 +(ve s)-.25 H(ubconscious.).15 E F0 2.5(2.7. Special)87 522 R(Header Lines)2.5 E +F1(Se)127 538.2 Q -.15(ve)-.25 G 1.898(ral header lines ha).15 F 2.197 -.15 +(ve s)-.2 H 1.897 +(pecial interpretations de\214ned by the con\214guration \214le.).15 F(Others) +6.897 E(ha)102 550.2 Q 1.205 -.15(ve i)-.2 H .905(nterpretations b).15 F .905 +(uilt into)-.2 F F2(sendmail)3.405 E F1 .906 +(that cannot be changed without changing the code.)3.405 F(These)5.906 E -.2 +(bu)102 562.2 S(iltins are described here.).2 E F0 2.5(2.7.1. Retur)102 586.2 R +(n-Receipt-T)-.15 E(o:)-.92 E F1 1.371 +(If this header is sent, a message will be sent to an)142 602.4 R 3.87(ys)-.15 +G 1.37(peci\214ed addresses when the \214nal)366.88 602.4 R(deli)117 614.4 Q +-.15(ve)-.25 G .367(ry is complete, that is, when successfully deli).15 F -.15 +(ve)-.25 G .368(red to a mailer with the).15 F F0(l)2.868 E F1 .368 +(\215ag \(local deli)2.868 F(v-)-.25 E(ery\) set in the mailer descriptor)117 +626.4 Q(.)-.55 E F0 2.5(2.7.2. Err)102 650.4 R(ors-T)-.18 E(o:)-.92 E F1 .22 +(If errors occur an)142 666.6 R .22 +(ywhere during processing, this header will cause error messages to go to)-.15 +F(the listed addresses.)117 678.6 Q(This is intended for mailing lists.)5 E +.384(The Errors-T)142 694.8 R .384(o: header w)-.8 F .384 +(as created in the bad old days when UUCP didn')-.1 F 2.885(tu)-.18 G .385 +(nderstand the)450.015 694.8 R .89(distinction between an en)117 706.8 R -.15 +(ve)-.4 G .89(lope and a header; this w).15 F .889(as a hack to pro)-.1 F .889 +(vide what should no)-.15 F 3.389(wb)-.25 G(e)499.56 706.8 Q(passed as the en) +117 718.8 Q -.15(ve)-.4 G(lope sender address.).15 E(It should go a)5 E -.1(wa) +-.15 G 3.8 -.65(y. I).1 H 2.5(ti).65 G 2.5(so)365.22 718.8 S(nly used if the) +376.61 718.8 Q F0(l)2.5 E F1(option is set.)2.5 E EP +%%Page: 15 12 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-15)452.9 60 Q 2.5(2.7.3. A)102 96 R(ppar)-.25 E(ently-T)-.18 E(o:)-.92 +E/F1 10/Times-Roman@0 SF .22 +(If a message comes in with no recipients listed in the message \(in a T)142 +112.2 R .22(o:, Cc:, or Bcc: line\))-.8 F(then)117 124.2 Q/F2 10/Times-Italic@0 +SF(sendmail)2.79 E F1 .29(will add an \231)2.79 F(Apparently-T)-.8 E .289 +(o:\232 header line for an)-.8 F 2.789(yr)-.15 G .289(ecipients it is a)378.086 +124.2 R -.1(wa)-.15 G .289(re of.).1 F .289(This is)5.289 F +(not put in as a standard recipient line to w)117 136.2 Q(arn an)-.1 E 2.5(yr) +-.15 G(ecipients that the list is not complete.)319.77 136.2 Q +(At least one recipient line is required under RFC 822.)142 152.4 Q F0 2.5 +(2.8. IDENT)87 176.4 R(Pr)2.5 E(otocol Support)-.18 E F2(Sendmail)127 192.6 Q +F1 1.835(supports the IDENT protocol as de\214ned in RFC 1413.)4.335 F 1.835 +(Although this enhances)6.835 F .29 +(identi\214cation of the author of an email message by doing a `)102 204.6 R +.289(`call back')-.74 F 2.789('t)-.74 G 2.789(ot)396.174 204.6 S .289 +(he originating system to)406.743 204.6 R .468(include the o)102 216.6 R .469(\ +wner of a particular TCP connection in the audit trail it is in no sense perfe\ +ct; a deter)-.25 F(-)-.2 E 1.294(mined for)102 228.6 R 1.294 +(ger can easily spoof the IDENT protocol.)-.18 F 1.294(The follo)6.294 F 1.294 +(wing description is e)-.25 F 1.293(xcerpted from)-.15 F(RFC 1413: 6.)102 240.6 +Q(Security Considerations)5 E .005 +(The information returned by this protocol is at most as trustw)127 256.8 R +(orth)-.1 E 2.506(ya)-.05 G 2.506(st)400.5 256.8 S .006(he host pro)409.676 +256.8 R .006(viding it OR)-.15 F .274(the or)127 268.8 R -.05(ga)-.18 G .274 +(nization operating the host.).05 F -.15(Fo)5.274 G 2.774(re).15 G .273 +(xample, a PC in an open lab has fe)295.314 268.8 R 2.773(wi)-.25 G 2.773(fa) +448.614 268.8 S .573 -.15(ny c)459.157 268.8 T(ontrols).15 E .986(on it to pre) +127 280.8 R -.15(ve)-.25 G .986(nt a user from ha).15 F .986 +(ving this protocol return an)-.2 F 3.486(yi)-.15 G .987 +(denti\214er the user w)378.052 280.8 R 3.487(ants. Lik)-.1 F(e-)-.1 E 1.441(w\ +ise, if the host has been compromised the information returned may be complete\ +ly erro-)127 292.8 R(neous and misleading.)127 304.8 Q .521(The Identi\214cati\ +on Protocol is not intended as an authorization or access control protocol.)127 +321 R(At)5.521 E 1.037(best, it pro)127 333 R 1.037 +(vides some additional auditing information with respect to TCP connections.) +-.15 F(At)6.036 E -.1(wo)127 345 S(rst, it can pro).1 E +(vide misleading, incorrect, or maliciously incorrect information.)-.15 E 1.006 +(The use of the information returned by this protocol for other than auditing \ +is strongly dis-)127 361.2 R 2.697(couraged. Speci\214cally)127 373.2 R 2.697 +(,u)-.65 G .197(sing Identi\214cation Protocol information to mak)228.114 373.2 +R 2.697(ea)-.1 G .197(ccess control deci-)429.186 373.2 R .514(sions - either \ +as the primary method \(i.e., no other checks\) or as an adjunct to other meth\ +ods)127 385.2 R(may result in a weak)127 397.2 Q(ening of normal host security) +-.1 E(.)-.65 E 1.779(An Identi\214cation serv)127 413.4 R 1.778(er may re)-.15 +F -.15(ve)-.25 G 1.778 +(al information about users, entities, objects or processes).15 F .336 +(which might normally be considered pri)127 425.4 R -.25(va)-.25 G 2.836 +(te. An).25 F .337(Identi\214cation serv)2.836 F .337(er pro)-.15 F .337 +(vides service which)-.15 F .806 +(is a rough analog of the CallerID services pro)127 437.4 R .806 +(vided by some phone companies and man)-.15 F 3.306(yo)-.15 G(f)500.67 437.4 Q +1.397(the same pri)127 449.4 R -.25(va)-.25 G 1.697 -.15(cy c).25 H 1.397 +(onsiderations and ar).15 F 1.398 +(guments that apply to the CallerID service apply to)-.18 F 3.546 +(Identi\214cation. If)127 461.4 R 1.046(you w)3.546 F(ouldn')-.1 E 3.546(tr) +-.18 G 1.045(un a "\214nger" serv)260.334 461.4 R 1.045(er due to pri)-.15 F +-.25(va)-.25 G 1.345 -.15(cy c).25 H 1.045(onsiderations you may).15 F(not w) +127 473.4 Q(ant to run this protocol.)-.1 E F0 2.5(3. ARGUMENTS)72 497.4 R F1 +.017(The complete list of ar)112 513.6 R .017(guments to)-.18 F F2(sendmail) +2.517 E F1 .017(is described in detail in Appendix A.)2.517 F .018 +(Some important)5.018 F(ar)87 525.6 Q(guments are described here.)-.18 E F0 2.5 +(3.1. Queue)87 549.6 R(Inter)2.5 E -.1(va)-.1 G(l).1 E F1 .455(The amount of t\ +ime between forking a process to run through the queue is de\214ned by the)127 +565.8 R F0<ad71>2.955 E F1 2.705(\215ag. If)102 577.8 R .206(you run in mode) +2.705 F F0(f)2.706 E F1(or)2.706 E F0(a)2.706 E F1 .206(this can be relati) +2.706 F -.15(ve)-.25 G .206(ly lar).15 F .206(ge, since it will only be rele) +-.18 F -.25(va)-.25 G .206(nt when a host).25 F .07(that w)102 589.8 R .07 +(as do)-.1 F .07(wn comes back up.)-.25 F .07(If you run in)5.07 F F0(q)2.57 E +F1 .07(mode it should be relati)2.57 F -.15(ve)-.25 G .07 +(ly short, since it de\214nes the).15 F +(maximum amount of time that a message may sit in the queue.)102 601.8 Q 1.335 +(RFC 1123 section 5.3.1.1 says that this v)127 618 R 1.336 +(alue should be at least 30 minutes \(although that)-.25 F(probably doesn')102 +630 Q 2.5(tm)-.18 G(ak)179.59 630 Q 2.5(es)-.1 G(ense if you use `)199.76 630 Q +(`queue-only')-.74 E 2.5('m)-.74 G(ode\).)329.08 630 Q F0 2.5(3.2. Daemon)87 +654 R(Mode)2.5 E F1 .085(If you allo)127 670.2 R 2.585(wi)-.25 G .085 +(ncoming mail o)181.165 670.2 R -.15(ve)-.15 G 2.585(ra).15 G 2.585(nI)263.61 +670.2 S .085(PC connection, you should ha)274.525 670.2 R .384 -.15(ve a d)-.2 +H .084(aemon running.).15 F(This)5.084 E .069(should be set by your)102 682.2 R +F2(/etc/r)2.569 E(c)-.37 E F1 .069(\214le using the)2.569 F F0(\255bd)2.569 E +F1 2.57(\215ag. The)2.569 F F0(\255bd)2.57 E F1 .07(\215ag and the)2.57 F F0 +<ad71>2.57 E F1 .07(\215ag may be combined)2.57 F(in one call:)102 694.2 Q +(/usr/sbin/sendmail \255bd \255q30m)142 710.4 Q EP +%%Page: 16 13 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-16 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E 2.5(3.3. F)87 96 R(or)-.25 E +(cing the Queue)-.18 E/F1 10/Times-Roman@0 SF .04(In some cases you may \214nd\ + that the queue has gotten clogged for some reason.)127 112.2 R -1.1(Yo)5.04 G +2.54(uc)1.1 G .04(an force)471.48 112.2 R 3.184(aq)102 124.2 S .684 +(ueue run using the)114.624 124.2 R F0<ad71>3.184 E F1 .684(\215ag \(with no v) +3.184 F 3.184(alue\). It)-.25 F .684(is entertaining to use the)3.184 F F0 +<ad76>3.185 E F1 .685(\215ag \(v)3.185 F .685(erbose\) when)-.15 F +(this is done to w)102 136.2 Q(atch what happens:)-.1 E +(/usr/sbin/sendmail \255q \255v)142 152.4 Q -1.1(Yo)127 172.8 S 4.004(uc)1.1 G +1.504(an also limit the jobs to those with a particular queue identi\214er) +151.564 172.8 R 4.004(,s)-.4 G(ender)428.362 172.8 Q 4.004(,o)-.4 G 4.004(rr) +461.676 172.8 S(ecipient)472.34 172.8 Q .686 +(using one of the queue modi\214ers.)102 184.8 R -.15(Fo)5.687 G 3.187(re).15 G +.687(xample, \231\255qRberk)265.654 184.8 R(ele)-.1 E .687 +(y\232 restricts the queue run to jobs that)-.15 F(ha)102 196.8 Q .526 -.15 +(ve t)-.2 H .226(he string \231berk).15 F(ele)-.1 E .226(y\232 some)-.15 F .225 +(where in one of the recipient addresses.)-.25 F(Similarly)5.225 E 2.725<2c99> +-.65 G .225(\255qSstring\232 lim-)441.185 196.8 R(its the run to particular se\ +nders and \231\255qIstring\232 limits it to particular identi\214ers.)102 208.8 +Q F0 2.5(3.4. Deb)87 232.8 R(ugging)-.2 E F1 1.365(There are a f)127 249 R +1.365(airly lar)-.1 F 1.365(ge number of deb)-.18 F 1.365(ug \215ags b)-.2 F +1.365(uilt into)-.2 F/F2 10/Times-Italic@0 SF(sendmail)3.865 E F1 6.365(.E)C +1.365(ach deb)417.65 249 R 1.365(ug \215ag has a)-.2 F 1.116(number and a le) +102 261 R -.15(ve)-.25 G 1.116(l, where higher le).15 F -.15(ve)-.25 G 1.116 +(ls means to print out more information.).15 F 1.116(The con)6.116 F -.15(ve) +-.4 G 1.116(ntion is).15 F .293(that le)102 273 R -.15(ve)-.25 G .293 +(ls greater than nine are \231absurd,).15 F 2.794<9a69>-.7 G .294(.e., the) +274.014 273 R 2.794(yp)-.15 G .294(rint out so much information that you w) +313.612 273 R(ouldn')-.1 E(t)-.18 E .692(normally w)102 285 R .692 +(ant to see them e)-.1 F .692(xcept for deb)-.15 F .692 +(ugging that particular piece of code.)-.2 F(Deb)5.692 E .691 +(ug \215ags are set)-.2 F(using the)102 297 Q F0<ad64>2.5 E F1 +(option; the syntax is:)2.5 E(deb)142 313.2 Q(ug-\215ag:)-.2 E F0<ad64>200.13 +313.2 Q F1(deb)2.5 E(ug-list)-.2 E(deb)142 325.2 Q 13.05(ug-list: deb)-.2 F +(ug-option [ , deb)-.2 E(ug-option ])-.2 E(deb)142 337.2 Q -.28(ug-option: deb) +-.2 F(ug-range [ . deb)-.2 E(ug-le)-.2 E -.15(ve)-.25 G 2.5(l]).15 G(deb)142 +349.2 Q 3.07(ug-range: inte)-.2 F(ger | inte)-.15 E(ger \255 inte)-.15 E(ger) +-.15 E(deb)142 361.2 Q(ug-le)-.2 E -.15(ve)-.25 G 6.24(l: inte).15 F(ger)-.15 E +(where spaces are for reading ease only)102 377.4 Q 5(.F)-.65 G(or e)268.64 +377.4 Q(xample,)-.15 E 34.99(\255d12 Set)142 393.6 R(\215ag 12 to le)2.5 E -.15 +(ve)-.25 G 2.5(l1).15 G 27.49(\255d12.3 Set)142 405.6 R(\215ag 12 to le)2.5 E +-.15(ve)-.25 G 2.5(l3).15 G 26.66(\255d3-17 Set)142 417.6 R +(\215ags 3 through 17 to le)2.5 E -.15(ve)-.25 G 2.5(l1).15 G 19.16 +(\255d3-17.4 Set)142 429.6 R(\215ags 3 through 17 to le)2.5 E -.15(ve)-.25 G +2.5(l4).15 G -.15(Fo)102 445.8 S 4.065(rac).15 G 1.565(omplete list of the a) +132.75 445.8 R -.25(va)-.2 G 1.565(ilable deb).25 F 1.565 +(ug \215ags you will ha)-.2 F 1.865 -.15(ve t)-.2 H 4.065(ol).15 G 1.566 +(ook at the code \(the)380.895 445.8 R 4.066(ya)-.15 G 1.566(re too)479.384 +445.8 R(dynamic to k)102 457.8 Q(eep this documentation up to date\).)-.1 E F0 +2.5(3.5. T)87 481.8 R(rying a Differ)-.74 E(ent Con\214guration File)-.18 E F1 +(An alternati)127 498 Q .3 -.15(ve c)-.25 H +(on\214guration \214le can be speci\214ed using the).15 E F0<ad43>2.5 E F1 +(\215ag; for e)2.5 E(xample,)-.15 E(/usr/sbin/sendmail \255Ctest.cf)142 514.2 Q +.429(uses the con\214guration \214le)102 530.4 R F2(test.cf)2.928 E F1 .428 +(instead of the def)2.928 F(ault)-.1 E F2(/etc/sendmail.cf)2.928 E(.)-.15 E F1 +.428(If the)5.428 F F0<ad43>2.928 E F1 .428(\215ag has no v)2.928 F(alue)-.25 E +(it def)102 542.4 Q(aults to)-.1 E F2(sendmail.cf)2.5 E F1 +(in the current directory)2.5 E(.)-.65 E F0 2.5(3.6. Changing)87 566.4 R(the V) +2.5 E(alues of Options)-.92 E F1(Options can be o)127 582.6 Q -.15(ve)-.15 G +(rridden using the).15 E F0<ad6f>2.5 E F1 2.5(\215ag. F)2.5 F(or e)-.15 E +(xample,)-.15 E(/usr/sbin/sendmail \255oT2m)142 598.8 Q(sets the)102 615 Q F0 +(T)2.5 E F1(\(timeout\) option to tw)2.5 E 2.5(om)-.1 G +(inutes for this run only)246.63 615 Q(.)-.65 E .181(Some options ha)127 631.2 +R .481 -.15(ve s)-.2 H .181(ecurity implications.).15 F .181(Sendmail allo) +5.181 F .182(ws you to set these, b)-.25 F .182(ut refuses to run)-.2 F +(as root thereafter)102 643.2 Q(.)-.55 E F0 2.5(3.7. Logging)87 667.2 R -.74 +(Tr)2.5 G(af\214c).74 E F1(Man)127 683.4 Q 3.255(yS)-.15 G .754 +(MTP implementations do not fully implement the protocol.)158.995 683.4 R -.15 +(Fo)5.754 G 3.254(re).15 G .754(xample, some per)428.542 683.4 R(-)-.2 E 1.178 +(sonal computer based SMTPs do not understand continuation lines in reply code\ +s.)102 695.4 R 1.178(These can be)6.178 F -.15(ve)102 707.4 S .13 +(ry hard to trace.).15 F .13(If you suspect such a problem, you can set traf) +5.13 F .13(\214c logging using the)-.25 F F0<ad58>2.63 E F1 2.63(\215ag. F)2.63 +F(or)-.15 E -.15(ex)102 719.4 S(ample,).15 E EP +%%Page: 17 14 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-17)452.9 60 Q/F1 10/Times-Roman@0 SF +(/usr/sbin/sendmail \255X /tmp/traf)142 96 Q(\214c -bd)-.25 E +(will log all traf)102 112.2 Q(\214c in the \214le)-.25 E/F2 10/Times-Italic@0 +SF(/tmp/tr)2.5 E(af)-.15 E<8c63>-.18 E F1(.)A .128(This logs a lot of data v) +127 128.4 R .128(ery quickly and should ne)-.15 F -.15(ve)-.25 G 2.628(rb).15 G +2.628(eu)345.888 128.4 S .128(sed during normal operations.)357.956 128.4 R +(After)5.129 E 1.326(starting up such a daemon, force the errant implementatio\ +n to send a message to your host.)102 140.4 R(All)6.326 E .504(message traf)102 +152.4 R .504(\214c in and out of)-.25 F F2(sendmail)3.004 E F1 3.004(,i)C .504 +(ncluding the incoming SMTP traf)265.258 152.4 R .505 +(\214c, will be logged in this)-.25 F(\214le.)102 164.4 Q F0 2.5(4. TUNING)72 +188.4 R F1 1.922(There are a number of con\214guration parameters you may w)112 +204.6 R 1.922(ant to change, depending on the)-.1 F .366 +(requirements of your site.)87 216.6 R .367 +(Most of these are set using an option in the con\214guration \214le.)5.366 F +-.15(Fo)5.367 G 2.867(re).15 G(xample,)472.06 216.6 Q(the line \231O)87 228.6 Q +(T5d\232 sets option \231T\232 to the v)-.4 E(alue \2315d\232 \(\214v)-.25 E +2.5(ed)-.15 G(ays\).)312.55 228.6 Q .735(Most of these options ha)112 244.8 R +1.035 -.15(ve a)-.2 H .735(ppropriate def).15 F .735(aults for most sites.)-.1 +F(Ho)5.735 E(we)-.25 E -.15(ve)-.25 G 1.535 -.4(r, s).15 H .735(ites ha).4 F +.735(ving v)-.2 F .735(ery high)-.15 F .045(mail loads may \214nd the)87 256.8 +R 2.545(yn)-.15 G .046(eed to tune them as appropriate for their mail load.) +193.465 256.8 R .046(In particular)5.046 F 2.546(,s)-.4 G .046(ites e)459.394 +256.8 R(xperi-)-.15 E 1.088(encing a lar)87 268.8 R 1.088 +(ge number of small messages, man)-.18 F 3.588(yo)-.15 G 3.587(fw)294.504 268.8 +S 1.087(hich are deli)308.641 268.8 R -.15(ve)-.25 G 1.087(red to man).15 F +3.587(yr)-.15 G 1.087(ecipients, may \214nd)425.996 268.8 R(that the)87 280.8 Q +2.5(yn)-.15 G(eed to adjust the parameters dealing with queue priorities.) +129.07 280.8 Q F0 2.5(4.1. T)87 304.8 R(imeouts)-.18 E F1 .582(All time interv) +127 321 R .583(als are set using a scaled syntax.)-.25 F -.15(Fo)5.583 G 3.083 +(re).15 G .583(xample, \23110m\232 represents ten minutes,)346.138 321 R +(whereas \2312h30m\232 represents tw)102 333 Q 2.5(oa)-.1 G(nd a half hours.) +241.3 333 Q(The full set of scales is:)5 E 16.11(ss)142 349.2 S(econds)165.89 +349.2 Q 12.22(mm)142 361.2 S(inutes)169.78 361.2 Q 15(hh)142 373.2 S(ours)167 +373.2 Q 15(dd)142 385.2 S(ays)167 385.2 Q 12.78(ww)142 397.2 S(eeks)169.22 +397.2 Q F0 2.5(4.1.1. Queue)102 425.4 R(inter)2.5 E -.1(va)-.1 G(l).1 E F1 .18 +(The ar)142 441.6 R .18(gument to the)-.18 F F0<ad71>2.68 E F1 .18 +(\215ag speci\214es ho)2.68 F 2.68(wo)-.25 G .18 +(ften a sub-daemon will run the queue.)319.25 441.6 R .18(This is)5.18 F .967 +(typically set to between \214fteen minutes and one hour)117 453.6 R 5.968(.R) +-.55 G .968(FC 1123 section 5.3.1.1 recommends)350.968 453.6 R +(that this be at least 30 minutes.)117 465.6 Q F0 2.5(4.1.2. Read)102 489.6 R +(timeouts)2.5 E F1 .51(It is possible to time out when reading the standard in\ +put or when reading from a remote)142 505.8 R .324(SMTP serv)117 517.8 R(er) +-.15 E 5.324(.T)-.55 G .324(hese timeouts are set using the)183.608 517.8 R F0 +(r)2.824 E F1 .324(option in the con\214guration \214le.)2.824 F .324(The ar) +5.324 F(gument)-.18 E .843(is a list of)117 529.8 R F2 -.1(ke)3.342 G(ywor)-.2 +E(d=value)-.37 E F1 3.342(pairs. The)3.342 F .842(recognized k)3.342 F -.15(ey) +-.1 G -.1(wo).15 G .842(rds, their def).1 F .842(ault v)-.1 F .842 +(alues, and the mini-)-.25 F(mum v)117 541.8 Q(alues allo)-.25 E +(wed by RFC 1123 section 5.3.2 are:)-.25 E 46.16(initial The)117 558 R -.1(wa) +2.5 G(it for the initial 220 greeting message [5m, 5m].).1 E 52.28(helo The)117 +574.2 R -.1(wa)4.226 G 1.727 +(it for a reply from a HELO or EHLO command [5m, unspeci\214ed].).1 F .1 +(This may require a host name lookup, so \214v)189 586.2 R 2.6(em)-.15 G .1 +(inutes is probably a reasonable)380.29 586.2 R(minimum.)189 598.2 Q 46.72 +(mail\207 The)117 614.4 R -.1(wa)2.5 G +(it for a reply from a MAIL command [10m, 5m].).1 E 48.95(rcpt\207 The)117 +630.6 R -.1(wa)3.481 G .981(it for a reply from a RCPT command [1h, 5m].).1 F +.982(This should be long)5.982 F +(because it could be pointing at a list that tak)189 642.6 Q +(es a long time to e)-.1 E(xpand.)-.15 E 34.5(datainit\207 The)117 658.8 R -.1 +(wa)2.5 G(it for a reply from a D).1 E -1.21 -1.11(AT A)-.4 H +(command [5m, 2m].)3.61 E 25.62(datablock\207 The)117 675 R -.1(wa)2.696 G .196 +(it for reading a data block \(that is, the body of the message\).).1 F .196 +([1h, 3m].)5.196 F .621 +(This should be long because it also applies to programs piping input to)189 +687 R F2(send-)3.121 E(mail)189 699 Q F1(which ha)2.5 E .3 -.15(ve n)-.2 H 2.5 +(og).15 G(uarantee of promptness.)274.75 699 Q EP +%%Page: 18 15 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-18 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF 30.06 +(data\214nal\207 The)117 96 R -.1(wa)2.806 G .306 +(it for a reply from the dot terminating a message.).1 F .306([1h, 10m].)5.306 +F .306(If this is)5.306 F .883 +(shorter than the time actually needed for the recei)189 108 R -.15(ve)-.25 G +3.384(rt).15 G 3.384(od)412.878 108 S(eli)426.262 108 Q -.15(ve)-.25 G 3.384 +(rt).15 G .884(he message,)454.796 108 R(duplicates will be generated.)189 120 +Q(This is discussed in RFC 1047.)5 E 55.06(rset The)117 136.2 R -.1(wa)2.5 G +(it for a reply from a RSET command [5m, unspeci\214ed].).1 E 53.94(quit The) +117 152.4 R -.1(wa)2.5 G(it for a reply from a Q).1 E +(UIT command [2m, unspeci\214ed].)-.1 E 50.61(misc The)117 168.6 R -.1(wa)2.761 +G .261(it for a reply from miscellaneous \(b).1 F .261 +(ut short\) commands such as NOOP)-.2 F(\(no-operation\) and VERB \(go into v) +189 180.6 Q(erbose mode\).)-.15 E([2m, unspeci\214ed].)5 E 25.06 +(command\207 In)117 196.8 R(serv)2.5 E(er SMTP)-.15 E 2.5(,t)-1.11 G +(he time to w)259.4 196.8 Q(ait for another command.)-.1 E([1h, 5m].)5 E -.15 +(Fo)117 213 S 3.633(rc).15 G 1.134 +(ompatibility with old con\214guration \214les, if no `)138.813 213 R(`k)-.74 E +-.15(ey)-.1 G -.1(wo).15 G(rd=').1 E 3.634('i)-.74 G 3.634(ss)390.854 213 S +1.134(peci\214ed, all the timeouts)402.268 213 R(mark)117 225 Q +(ed with \207 are set to the indicated v)-.1 E(alue.)-.25 E(Man)142 241.2 Q +2.501(yo)-.15 G 2.501(ft)172.681 241.2 S .001(he RFC 1123 minimum v)181.292 +241.2 R .001(alues may well be too short.)-.25 F/F2 10/Times-Italic@0 SF +(Sendmail)5 E F1 -.1(wa)2.5 G 2.5(sd).1 G(esigned to)463.17 241.2 Q .066 +(the RFC 822 protocols, which did not specify read timeouts; hence,)117 253.2 R +F2(sendmail)2.567 E F1 .067(does not guarantee)2.567 F .438 +(to reply to messages promptly)117 265.2 R 5.438(.I)-.65 G 2.938(np)249.92 +265.2 S(articular)262.858 265.2 Q 2.938(,a\231)-.4 G .438 +(RCPT\232 command specifying a mailing list will)313.034 265.2 R -.15(ex)117 +279.2 S .205(pand and v).15 F .205(erify the entire list; a lar)-.15 F .205 +(ge list on a slo)-.18 F 2.705(ws)-.25 G .205(ystem may tak)339.81 279.2 R +2.705(em)-.1 G .205(ore than \214v)413.375 279.2 R 2.705(em)-.15 G(inutes) +474.11 279.2 Q/F3 7/Times-Roman@0 SF(6)498 275.2 Q F1(.)501.5 279.2 Q 3.036(Ir) +117 291.2 S .536(ecommend a one hour timeout \212 since this f)126.696 291.2 R +.536(ailure is rare, a long timeout is not onerous and)-.1 F +(may ultimately help reduce netw)117 303.2 Q(ork load.)-.1 E -.15(Fo)142 319.4 +S 2.5(re).15 G(xample, the line:)162.53 319.4 Q(Orcommand=25m,datablock=3h)157 +335.6 Q .344(sets the serv)117 351.8 R .344(er SMTP command timeout to 25 minu\ +tes and the input data block timeout to three)-.15 F(hours.)117 363.8 Q F0 2.5 +(4.1.3. Message)102 387.8 R(timeouts)2.5 E F1 .237 +(After sitting in the queue for a fe)142 404 R 2.737(wd)-.25 G .237 +(ays, a message will time out.)289.726 404 R .238(This is to insure that at) +5.238 F .283(least the sender is a)117 416 R -.1(wa)-.15 G .282 +(re of the inability to send a message.).1 F .282 +(The timeout is typically set to three)5.282 F 2.5(days. This)117 428 R +(timeout is set using the)2.5 E F0(T)2.5 E F1 +(option in the con\214guration \214le.)2.5 E .413(The time of submission is se\ +t in the queue, rather than the amount of time left until time-)142 444.2 R +3.263(out. As)117 456.2 R 3.263(ar)3.263 G .763 +(esult, you can \215ush messages that ha)163.449 456.2 R 1.062 -.15(ve b)-.2 H +.762(een hanging for a short period by running).15 F +(the queue with a short message timeout.)117 468.2 Q -.15(Fo)5 G 2.5(re).15 G +(xample,)302.79 468.2 Q(/usr/sbin/sendmail \255oT1d \255q)157 484.4 Q +(will run the queue and \215ush an)117 500.6 Q(ything that is one day old.)-.15 +E 1.077(Since this option is global, and since you can not)142 516.8 R F2 3.577 +(ap)3.577 G(riori)364.395 516.8 Q F1(kno)3.577 E 3.577(wh)-.25 G 1.577 -.25 +(ow l)416.859 516.8 T 1.077(ong another host).25 F .476 +(outside your domain will be do)117 528.8 R .475(wn, a \214v)-.25 F 2.975(ed) +-.15 G .475(ay timeout is recommended.)291.785 528.8 R .475(This allo)5.475 F +.475(ws a recipient)-.25 F 1.579(to \214x the problem e)117 540.8 R -.15(ve) +-.25 G 4.079(ni).15 G 4.079(fi)222.545 540.8 S 4.079(to)232.734 540.8 S 1.579 +(ccurs at the be)244.593 540.8 R 1.58(ginning of a long week)-.15 F 4.08 +(end. RFC)-.1 F 1.58(1123 section)4.08 F +(5.3.1.1 says that this parameter should be `)117 552.8 Q +(`at least 4\2555 days')-.74 E('.)-.74 E(The)142 569 Q F0(T)2.711 E F1 .211 +(option can also tak)2.711 F 2.711(eas)-.1 G .21 +(econd timeout indicating a time after which a w)263.637 569 R .21(arning mes-) +-.1 F(sage should be sent; the tw)117 581 Q 2.5(ot)-.1 G +(imeouts are separated by a slash.)234.67 581 Q -.15(Fo)5 G 2.5(re).15 G +(xample, the v)391.28 581 Q(alue)-.25 E(5d/4h)157 597.2 Q .971 +(causes email to f)117 613.4 R .971(ail after \214v)-.1 F 3.471(ed)-.15 G .971 +(ays, b)245.326 613.4 R .971(ut a w)-.2 F .971 +(arning message will be sent after four hours.)-.1 F(This)5.972 E +(should be lar)117 625.4 Q(ge enough that the message will ha)-.18 E .3 -.15 +(ve b)-.2 H(een tried se).15 E -.15(ve)-.25 G(ral times.).15 E .32 LW 76 669.2 +72 669.2 DL 80 669.2 76 669.2 DL 84 669.2 80 669.2 DL 88 669.2 84 669.2 DL 92 +669.2 88 669.2 DL 96 669.2 92 669.2 DL 100 669.2 96 669.2 DL 104 669.2 100 +669.2 DL 108 669.2 104 669.2 DL 112 669.2 108 669.2 DL 116 669.2 112 669.2 DL +120 669.2 116 669.2 DL 124 669.2 120 669.2 DL 128 669.2 124 669.2 DL 132 669.2 +128 669.2 DL 136 669.2 132 669.2 DL 140 669.2 136 669.2 DL 144 669.2 140 669.2 +DL 148 669.2 144 669.2 DL 152 669.2 148 669.2 DL 156 669.2 152 669.2 DL 160 +669.2 156 669.2 DL 164 669.2 160 669.2 DL 168 669.2 164 669.2 DL 172 669.2 168 +669.2 DL 176 669.2 172 669.2 DL 180 669.2 176 669.2 DL 184 669.2 180 669.2 DL +188 669.2 184 669.2 DL 192 669.2 188 669.2 DL 196 669.2 192 669.2 DL 200 669.2 +196 669.2 DL 204 669.2 200 669.2 DL 208 669.2 204 669.2 DL 212 669.2 208 669.2 +DL 216 669.2 212 669.2 DL/F4 5/Times-Roman@0 SF(6)93.6 679.6 Q/F5 8 +/Times-Roman@0 SF .344(This v)3.2 J .344(eri\214cation includes looking up e) +-.12 F -.12(ve)-.2 G .344(ry address with the name serv).12 F .344(er; this in) +-.12 F -.16(vo)-.32 G(lv).16 E .344(es netw)-.12 F .343 +(ork delays, and can in some cases)-.08 F(can be considerable.)72 692.4 Q EP +%%Page: 19 16 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-19)452.9 60 Q 2.5(4.2. F)87 96 R(orking During Queue Runs)-.25 E/F1 10 +/Times-Roman@0 SF .303(By setting the)127 112.2 R F0(Y)2.802 E F1(option,)2.802 +E/F2 10/Times-Italic@0 SF(sendmail)2.802 E F1 .302(will fork before each indi) +2.802 F .302(vidual message while running the)-.25 F 2.513(queue. This)102 +124.2 R .013(will pre)2.513 F -.15(ve)-.25 G(nt).15 E F2(sendmail)2.513 E F1 +.013(from consuming lar)2.513 F .013(ge amounts of memory)-.18 F 2.513(,s)-.65 +G 2.513(oi)421.993 124.2 S 2.513(tm)432.286 124.2 S .014(ay be useful in) +445.359 124.2 R .592(memory-poor en)102 136.2 R 3.092(vironments. Ho)-.4 F(we) +-.25 E -.15(ve)-.25 G 1.392 -.4(r, i).15 H 3.092(ft).4 G(he)275.388 136.2 Q F0 +(Y)3.092 E F1 .591(option is not set,)3.091 F F2(sendmail)3.091 E F1 .591 +(will k)3.091 F .591(eep track of hosts)-.1 F(that are do)102 148.2 Q +(wn during a queue run, which can impro)-.25 E .3 -.15(ve p)-.15 H +(erformance dramatically).15 E(.)-.65 E(If the)127 164.4 Q F0(Y)2.5 E F1 +(option is set,)2.5 E F2(sendmail)2.5 E F1(can not use connection caching.)2.5 +E F0 2.5(4.3. Queue)87 188.4 R(Priorities)2.5 E F1(Ev)127 204.6 Q 1.128(ery me\ +ssage is assigned a priority when it is \214rst instantiated, consisting of th\ +e message)-.15 F .003(size \(in bytes\) of)102 216.6 R .002 +(fset by the message class times the \231w)-.25 F .002(ork class f)-.1 F .002 +(actor\232 and the number of recipients)-.1 F .637(times the \231w)102 228.6 R +.637(ork recipient f)-.1 F(actor)-.1 E 4.537 -.7(.\232 T)-.55 H .638 +(he priority is used to order the queue.).7 F .638(Higher numbers for the)5.638 +F(priority mean that the message will be processed later when running the queu\ +e.)102 240.6 Q .329(The message size is included so that lar)127 256.8 R .328 +(ge messages are penalized relati)-.18 F .628 -.15(ve t)-.25 H 2.828(os).15 G +.328(mall messages.)443.122 256.8 R .285(The message class allo)102 268.8 R +.285(ws users to send \231high priority\232 messages by including a \231Preced\ +ence:\232 \214eld)-.25 F .008(in their message; the v)102 280.8 R .008 +(alue of this \214eld is look)-.25 F .007(ed up in the)-.1 F F0(P)2.507 E F1 +.007(lines of the con\214guration \214le.)2.507 F .007(Since the)5.007 F 1.966 +(number of recipients af)102 292.8 R 1.967 +(fects the amount of load a message presents to the system, this is also)-.25 F +(included into the priority)102 304.8 Q(.)-.65 E .895 +(The recipient and class f)127 321 R .895 +(actors can be set in the con\214guration \214le using the)-.1 F F0(y)3.394 E +F1(and)3.394 E F0(z)3.394 E F1(options)3.394 E(respecti)102 333 Q -.15(ve)-.25 +G(ly).15 E 5.962(.T)-.65 G(he)163.842 333 Q 3.462(yd)-.15 G(ef)186.594 333 Q +.962(ault to 30000 \(for the recipient f)-.1 F .963 +(actor\) and 1800 \(for the class f)-.1 F 3.463(actor\). The)-.1 F +(initial priority is:)102 345 Q F2(pri)244.54 363 Q/F3 10/Symbol SF(=)3.16 E F1 +(\()2.8 E F2(class).2 E F3<b4>2.47 E F0(z\))2.2 E F3(+)2.2 E F1(\()2.2 E F2 +(nrcpt).36 E F3<b4>2.88 E F0(y\))2.2 E F1(\(Remember)102 381 Q 3.328(,h)-.4 G +.828(igher v)159.638 381 R .828 +(alues for this parameter actually mean that the job will be treated with lo) +-.25 F(wer)-.25 E(priority)102 393 Q(.\))-.65 E 1.519(The priority of a job ca\ +n also be adjusted each time it is processed \(that is, each time an)127 409.2 +R .256(attempt is made to deli)102 421.2 R -.15(ve)-.25 G 2.756(ri).15 G .256 +(t\) using the \231w)212.04 421.2 R .256(ork time f)-.1 F(actor)-.1 E 1.656 -.7 +(,\232 s)-.4 H .256(et by the).7 F F0(Z)2.756 E F1 2.756(option. This)2.756 F +.256(is added to the)2.756 F(priority)102 433.2 Q 2.702(,s)-.65 G 2.703(oi) +140.442 433.2 S 2.703(tn)150.925 433.2 S .203 +(ormally decreases the precedence of the job, on the grounds that jobs that ha) +161.408 433.2 R .503 -.15(ve f)-.2 H(ailed).05 E(man)102 445.2 Q 2.5(yt)-.15 G +(imes will tend to f)129.35 445.2 Q(ail ag)-.1 E(ain in the future.)-.05 E(The) +5 E F0(Z)2.5 E F1(option def)2.5 E(aults to 90000.)-.1 E F0 2.5(4.4. Load)87 +469.2 R(Limiting)2.5 E F2(Sendmail)127 485.4 Q F1 .102(can be ask)2.602 F .101 +(ed to queue \(b)-.1 F .101(ut not deli)-.2 F -.15(ve)-.25 G .101 +(r\) mail if the system load a).15 F -.15(ve)-.2 G .101(rage gets too high).15 +F .626(using the)102 497.4 R F0(x)3.126 E F1 3.126(option. When)3.126 F .626 +(the load a)3.126 F -.15(ve)-.2 G .626(rage e).15 F .626(xceeds the v)-.15 F +.626(alue of the)-.25 F F0(x)3.126 E F1 .626(option, the deli)3.126 F -.15(ve) +-.25 G .627(ry mode is).15 F .987(set to)102 509.4 R F0(q)3.487 E F1 .987 +(\(queue only\) if the)3.487 F F2 .987(Queue F)3.487 F(actor)-.75 E F1(\()3.487 +E F0(q)A F1 .987(option\) di)3.487 F .986(vided by the dif)-.25 F .986 +(ference in the current load)-.25 F -2.25 -.2(av e)102 521.4 T 1.268 +(rage and the).2 F F0(x)3.769 E F1 1.269(option plus one e)3.769 F 1.269 +(xceeds the priority of the message \212 that is, the message is)-.15 F +(queued if)102 533.4 Q(f:)-.25 E F2(pri)269.76 554.63 Q F1(>)3.16 E F0(q)312.48 +547.63 Q F2(LA)294.81 561.63 Q F3(-)2.23 E F0(x)2.2 E F3(+)2.2 E .4 LW 336.29 +552.03 294.23 552.03 DL F1(1)331.29 561.63 Q(The)102 576.13 Q F0(q)3.143 E F1 +.643(option def)3.143 F .642(aults to 200000, so each point of load a)-.1 F +-.15(ve)-.2 G .642(rage is w).15 F .642(orth 200000 priority points \(as)-.1 F +(described abo)102 588.13 Q -.15(ve)-.15 G(\).).15 E -.15(Fo)127 604.33 S 2.886 +(rd).15 G .386(rastic cases, the)148.626 604.33 R F0(X)2.887 E F1 .387 +(option de\214nes a load a)2.887 F -.15(ve)-.2 G .387 +(rage at which sendmail will refuse to accept).15 F(netw)102 616.33 Q +(ork connections.)-.1 E +(Locally generated mail \(including incoming UUCP mail\) is still accepted.)5 E +F0 2.5(4.5. Deli)87 640.33 R -.1(ve)-.1 G(ry Mode).1 E F1 .417 +(There are a number of deli)127 656.53 R -.15(ve)-.25 G .416(ry modes that).15 +F F2(sendmail)2.916 E F1 .416 +(can operate in, set by the \231d\232 con\214gura-)2.916 F(tion option.)102 +668.53 Q(These modes specify ho)5 E 2.5(wq)-.25 G(uickly mail will be deli) +263.96 668.53 Q -.15(ve)-.25 G 2.5(red. Le).15 F -.05(ga)-.15 G 2.5(lm).05 G +(odes are:)418.9 668.53 Q 17.22(id)142 684.73 S(eli)167 684.73 Q -.15(ve)-.25 G +2.5(ri).15 G(nteracti)194.65 684.73 Q -.15(ve)-.25 G(ly \(synchronously\)).15 E +15(bd)142 696.73 S(eli)167 696.73 Q -.15(ve)-.25 G 2.5(ri).15 G 2.5(nb)194.65 +696.73 S(ackground \(asynchronously\))207.15 696.73 Q 15(qq)142 708.73 S +(ueue only \(don')167 708.73 Q 2.5(td)-.18 G(eli)240.42 708.73 Q -.15(ve)-.25 G +(r\)).15 E 1.49(There are tradeof)102 724.93 R 3.99(fs. Mode)-.25 F 1.491 +(\231i\232 passes the maximum amount of information to the sender)3.99 F 3.991 +(,b)-.4 G 1.491(ut is)485.559 724.93 R EP +%%Page: 20 17 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-20 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF .433(hardly e) +102 96 R -.15(ve)-.25 G 2.933(rn).15 G(ecessary)155.226 96 Q 5.433(.M)-.65 G +.433(ode \231q\232 puts the minimum load on your machine, b)205.269 96 R .432 +(ut means that deli)-.2 F -.15(ve)-.25 G(ry).15 E .437 +(may be delayed for up to the queue interv)102 108 R 2.937(al. Mode)-.25 F .437 +(\231b\232 is probably a good compromise.)2.937 F(Ho)5.437 E(we)-.25 E -.15(ve) +-.25 G -.4(r,).15 G .033(this mode can cause lar)102 120 R .032 +(ge numbers of processes if you ha)-.18 F .332 -.15(ve a m)-.2 H .032 +(ailer that tak).15 F .032(es a long time to deli)-.1 F -.15(ve)-.25 G(r).15 E +2.5(am)102 132 S(essage.)116.72 132 Q 1.208 +(If you run in mode \231q\232 \(queue only\))127 148.2 R/F2 10/Times-Italic@0 +SF(sendmail)3.708 E F1 1.208(will not e)3.708 F 1.208(xpand aliases and follo) +-.15 F 3.708(w.)-.25 G(forw)472.45 148.2 Q(ard)-.1 E +(\214les upon initial receipt of the mail.)102 160.2 Q +(This speeds up the response to RCPT commands.)5 E F0 2.5(4.6. Log)87 184.2 R +(Le)2.5 E -.1(ve)-.15 G(l).1 E F1 .19(The le)127 200.4 R -.15(ve)-.25 G 2.69 +(lo).15 G 2.69(fl)171.97 200.4 S .189(ogging can be set for sendmail.)180.77 +200.4 R .189(The def)5.189 F .189(ault using a standard con\214guration table) +-.1 F(is le)102 212.4 Q -.15(ve)-.25 G 2.5(l9).15 G 5(.T)137.71 212.4 S(he le) +151.32 212.4 Q -.15(ve)-.25 G(ls are as follo).15 E(ws:)-.25 E 31(0N)102 228.6 +S 2.5(ol)145.22 228.6 S(ogging.)155.5 228.6 Q 31(1S)102 244.8 S +(erious system f)143.56 244.8 Q(ailures and potential security problems.)-.1 E +31(2L)102 261 S(ost communications \(netw)144.11 261 Q +(ork problems\) and protocol f)-.1 E(ailures.)-.1 E 31(3O)102 277.2 S +(ther serious f)145.22 277.2 Q(ailures.)-.1 E 31(4M)102 293.4 S(inor f)146.89 +293.4 Q(ailures.)-.1 E 31(5M)102 309.6 S(essage collection statistics.)146.89 +309.6 Q 31(6C)102 325.8 S(reation of error messages, VRFY and EXPN commands.) +144.67 325.8 Q 31(7D)102 342 S(eli)145.22 342 Q -.15(ve)-.25 G(ry f).15 E +(ailures \(host or user unkno)-.1 E(wn, etc.\).)-.25 E 31(8S)102 358.2 S +(uccessful deli)143.56 358.2 Q -.15(ve)-.25 G(ries.).15 E 31(9M)102 374.4 S +(essages being deferred \(due to a host being do)146.89 374.4 Q(wn, etc.\).) +-.25 E 23.5(10 Database)102 390.6 R -.15(ex)2.5 G(pansion \(alias, forw).15 E +(ard, and userdb lookups\).)-.1 E 23.5(15 Automatic)102 406.8 R +(alias database reb)2.5 E(uilds.)-.2 E 23.5(20 Logs)102 423 R .603 +(attempts to run lock)3.102 F .603(ed queue \214les.)-.1 F .603 +(These are not errors, b)5.603 F .603(ut can be useful to note if)-.2 F +(your queue appears to be clogged.)138 435 Q 23.5(30 Lost)102 451.2 R +(locks \(only if using lockf instead of \215ock\).)2.5 E(Additionally)102 467.4 +Q 3.684(,v)-.65 G 1.184(alues abo)162.844 467.4 R 1.484 -.15(ve 6)-.15 H 3.684 +(4a).15 G 1.183(re reserv)232.466 467.4 R 1.183(ed for e)-.15 F 1.183 +(xtremely v)-.15 F 1.183(erbose deb)-.15 F 1.183(uggging output.)-.2 F 1.183 +(No normal)6.183 F(site w)102 479.4 Q(ould e)-.1 E -.15(ve)-.25 G 2.5(rs).15 G +(et these.)168.99 479.4 Q F0 2.5(4.7. File)87 503.4 R(Modes)2.5 E F1 .813 +(There are a number of \214les that may ha)127 519.6 R 1.113 -.15(ve a n)-.2 H +.813(umber of modes.).15 F .813(The modes depend on what)5.813 F +(functionality you w)102 531.6 Q(ant and the le)-.1 E -.15(ve)-.25 G 2.5(lo).15 +G 2.5(fs)253.15 531.6 S(ecurity you require.)262.87 531.6 Q F0 2.5(4.7.1. T)102 +555.6 R 2.5(os)-.92 G(uid or not to suid?)146.64 555.6 Q F2(Sendmail)142 571.8 +Q F1 .934(can safely be made setuid to root.)3.434 F .934 +(At the point where it is about to)5.934 F F2 -.2(ex)3.433 G(ec).2 E F1 .933 +(\(2\) a)1.666 F(mailer)117 583.8 Q 2.582(,i)-.4 G 2.582(tc)150.012 583.8 S +.082(hecks to see if the userid is zero; if so, it resets the userid and group\ +id to a def)159.814 583.8 R .083(ault \(set)-.1 F .577(by the)117 595.8 R F0(u) +3.077 E F1(and)3.077 E F0(g)3.077 E F1 3.077(options\). \(This)3.077 F .576 +(can be o)3.076 F -.15(ve)-.15 G .576(rridden by setting the).15 F F0(S)3.076 E +F1 .576(\215ag to the mailer for mailers)3.076 F 1.531 +(that are trusted and must be called as root.\))117 607.8 R(Ho)6.531 E(we)-.25 +E -.15(ve)-.25 G 2.331 -.4(r, t).15 H 1.532 +(his will cause mail processing to be).4 F(accounted \(using)117 619.8 Q F2(sa) +2.5 E F1(\(8\)\) to root rather than to the user sending the mail.)1.666 E F0 +2.5(4.7.2. Should)102 643.8 R(my alias database be writable?)2.5 E F1 .058 +(At Berk)142 660 R(ele)-.1 E 2.558(yw)-.15 G 2.558(eh)200.186 660 S -2.25 -.2 +(av e)212.184 660 T .058(the alias database \(/etc/aliases*\) mode 644.)2.758 F +.058(While this is not as \215e)5.058 F(x-)-.15 E 1.718 +(ible as if the database were more 666, it a)117 672 R -.2(vo)-.2 G 1.719 +(ids potential security problems with a globally).2 F(writable database.)117 +684 Q 1.191(The database that)142 700.2 R F2(sendmail)3.691 E F1 1.191 +(actually used is represented by the tw)3.691 F 3.69<6f8c>-.1 G(les)429.12 +700.2 Q F2(aliases.dir)3.69 E F1(and)3.69 E F2(aliases.pa)117 712.2 Q(g)-.1 E +F1 .158(\(both in /etc\) \(or)2.658 F F2(aliases.db)2.658 E F1 .159 +(if you are running with the ne)2.659 F 2.659(wB)-.25 G(erk)412.852 712.2 Q +(ele)-.1 E 2.659(yd)-.15 G .159(atabase prim-)449.691 712.2 R(iti)117 724.2 Q +-.15(ve)-.25 G 3.607(s\). The).15 F 1.107 +(mode on these \214les should match the mode on /etc/aliases.)3.607 F(If)6.106 +E F2(aliases)3.606 E F1 1.106(is writable)3.606 F EP +%%Page: 21 18 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-21)452.9 60 Q/F1 10/Times-Roman@0 SF 1.624(and the DBM \214les \()117 +96 R/F2 10/Times-Italic@0 SF(aliases.dir)A F1(and)4.124 E F2(aliases.pa)4.124 E +(g)-.1 E F1 4.124(\)a)C 1.624(re not, users will be unable to re\215ect their) +324.648 96 R .72(desired changes through to the actual database.)117 108 R(Ho) +5.719 E(we)-.25 E -.15(ve)-.25 G 1.519 -.4(r, i).15 H(f).4 E F2(aliases)3.219 E +F1 .719(is read-only and the DBM)3.219 F(\214les are writable, a slightly soph\ +isticated user can arrange to steal mail an)117 120 Q(yw)-.15 E(ay)-.1 E(.)-.65 +E .62(If your DBM \214les are not writable by the w)142 136.2 R .621 +(orld or you do not ha)-.1 F .921 -.15(ve a)-.2 H(uto-reb).15 E .621 +(uild enabled)-.2 F .564(\(with the \231D\232 option\), then you must be caref\ +ul to reconstruct the alias database each time you)117 148.2 R(change the te) +117 160.2 Q(xt v)-.15 E(ersion:)-.15 E(ne)157 176.4 Q -.1(wa)-.25 G(liases).1 E +(If this step is ignored or for)117 192.6 Q(gotten an)-.18 E 2.5(yi)-.15 G +(ntended changes will also be ignored or for)273.32 192.6 Q(gotten.)-.18 E F0 +2.5(4.8. Connection)87 216.6 R(Caching)2.5 E F1 .493 +(When processing the queue,)127 232.8 R F0(sendmail)2.993 E F1 .493 +(will try to k)2.993 F .493(eep the last fe)-.1 F 2.994(wo)-.25 G .494 +(pen connections open to)405.588 232.8 R -.2(avo)102 244.8 S +(id startup and shutdo).2 E(wn costs.)-.25 E +(This only applies to IPC connections.)5 E .286 +(When trying to open a connection the cache is \214rst searched.)127 261 R .286 +(If an open connection is found,)5.286 F .92 +(it is probed to see if it is still acti)102 273 R 1.22 -.15(ve b)-.25 H 3.42 +(ys).15 G .92(ending a)270.89 273 R/F3 9/Times-Roman@0 SF(NOOP)3.42 E F1 3.42 +(command. It)3.42 F .92(is not an error if this f)3.42 F(ails;)-.1 E +(instead, the connection is closed and reopened.)102 285 Q -1 -.8(Tw o)127 +301.2 T .207(parameters control the connection cache.)3.507 F(The)5.207 E F0(k) +2.707 E F1 .207(option de\214nes the number of simultane-)2.707 F 1.819 +(ous open connections that will be permitted.)102 313.2 R 1.82 +(If it is set to zero, connections will be closed as)6.819 F .796 +(quickly as possible.)102 325.2 R .796(The def)5.796 F .796(ault is one.)-.1 F +.796(This should be set as appropriate for your system size; it)5.796 F +(will limit the amount of system resources that)102 337.2 Q F0(sendmail)2.5 E +F1(will use during queue runs.)2.5 E(The)127 353.4 Q F0(K)3.647 E F1 1.148 +(option speci\214es the maximum time that an)3.647 F 3.648(yc)-.15 G 1.148 +(ached connection will be permitted to)347.45 353.4 R 2.896(idle. When)102 +365.4 R .396(the idle time e)2.896 F .396(xceeds this v)-.15 F .396 +(alue the connection is closed.)-.25 F .395(This number should be small)5.395 F +.162(\(under ten minutes\) to pre)102 377.4 R -.15(ve)-.25 G .163 +(nt you from grabbing too man).15 F 2.663(yr)-.15 G .163 +(esources from other hosts.)347.485 377.4 R .163(The def)5.163 F(ault)-.1 E +(is \214v)102 389.4 Q 2.5(em)-.15 G(inutes.)136.3 389.4 Q F0 2.5(4.9. Name)87 +413.4 R(Ser)2.5 E -.1(ve)-.1 G 2.5(rA).1 G(ccess)172.33 413.4 Q F1 .422 +(If your system supports the name serv)127 429.6 R(er)-.15 E 2.921(,t)-.4 G +.421(hen the probability is that)297.151 429.6 R F2(sendmail)2.921 E F1 .421 +(will be using it)2.921 F(re)102 441.6 Q -.05(ga)-.15 G .153(rdless of ho).05 F +2.653(wy)-.25 G .153(ou con\214gure sendmail.)180.599 441.6 R(Ho)5.153 E(we) +-.25 E -.15(ve)-.25 G .954 -.4(r, i).15 H 2.654(fy).4 G .154(ou ha)331.956 +441.6 R .454 -.15(ve n)-.2 H(ameserv).15 E .154(er support which you are)-.15 F +.979(not using, sendmail will get a \231connection refused\232 message when it\ + tries to connect to the name)102 453.6 R(serv)102 465.6 Q .591 +(er \(either by calling)-.15 F F2 -.1(ge)3.091 G(thostbyname).1 E F1 .591 +(or by trying to look up the MX records\).)3.091 F .591(If the)5.591 F F0(I) +3.091 E F1 .592(option is)3.092 F(set,)102 477.6 Q F2(sendmail)3.34 E F1 .839 +(will interpret this to mean a temporary f)3.34 F .839 +(ailure; otherwise, it ignores the name serv)-.1 F(er)-.15 E 2.59(data. If)102 +489.6 R .09(your name serv)2.59 F .09(er is running properly)-.15 F 2.59(,t) +-.65 G .09(he setting of this option is not rele)291.6 489.6 R -.25(va)-.25 G +.09(nt; ho).25 F(we)-.25 E -.15(ve)-.25 G .89 -.4(r, i).15 H 2.59(ti).4 G(s) +500.11 489.6 Q(important that it be set properly to mak)102 501.6 Q 2.5(ee)-.1 +G(rror handling w)269.66 501.6 Q(ork properly)-.1 E(.)-.65 E .633 +(This option also allo)127 517.8 R .633(ws you to tweak name serv)-.25 F .633 +(er options.)-.15 F .632(The command line tak)5.633 F .632(es a series)-.1 F +.442(of \215ags as documented in)102 529.8 R F2 -.37(re)2.942 G(solver).37 E F1 +.442(\(3\) \(with the leading \231RES_\232 deleted\).)B .442 +(Each can be preceded by)5.442 F(an optional `+' or `)102 541.8 Q/F4 10/Symbol +SF(-)A F1 2.5('. F)B(or e)-.15 E(xample, the line)-.15 E(OIT)142 558 Q(rue +AA) +-.35 E(ONL)-.55 E(Y)-1 E F4(-)2.5 E F1(DNSRCH)A .862(turns on the AA)102 574.2 +R(ONL)-.55 E 3.362(Y\()-1 G .862(accept authoritati)201.658 574.2 R 1.162 -.15 +(ve a)-.25 H .861(nswers only\) and turns of).15 F 3.361(ft)-.25 G .861 +(he DNSRCH \(search the)402.827 574.2 R 2.039(domain path\) options.)102 586.2 +R 2.039(Most resolv)7.039 F 2.039(er libraries def)-.15 F 2.039 +(ault DNSRCH, DEFN)-.1 F 2.039(AMES, and RECURSE)-.35 F .187 +(\215ags on and all others of)102 598.2 R 2.687(f. Note)-.25 F .186 +(the use of the initial `)2.686 F(`T)-.74 E(rue')-.35 E 2.686('\212t)-.74 G +.186(his is for compatibility with pre)365.82 598.2 R(vi-)-.25 E(ous v)102 +610.2 Q(ersions of sendmail, b)-.15 E(ut is not otherwise necessary)-.2 E(.) +-.65 E -1.11(Ve)127 626.4 S 2.256(rsion le)1.11 F -.15(ve)-.25 G 4.756(l1c).15 +G 2.256(on\214gurations turn DNSRCH and DEFN)200.298 626.4 R 2.257(AMES of)-.35 +F 4.757(fw)-.25 G 2.257(hen doing deli)424.896 626.4 R -.15(ve)-.25 G(ry).15 E +2.06(lookups, b)102 638.4 R 2.06(ut lea)-.2 F 2.36 -.15(ve t)-.2 H 2.06 +(hem on e).15 F -.15(ve)-.25 G 2.06(rywhere else.).15 F -1.11(Ve)7.06 G 2.06 +(rsion 8 of)1.11 F F2(sendmail)4.56 E F1 2.06(ignores them when doing)4.56 F +.313(canoni\214cation lookups \(that is, when using $[ ... $]\), and al)102 +650.4 R -.1(wa)-.1 G .313(ys does the search.).1 F .313(If you don')5.313 F +2.813(tw)-.18 G(ant)491.78 650.4 Q(to do automatic name e)102 662.4 Q +(xtension, don')-.15 E 2.5(tc)-.18 G(all $[ ... $].)261.93 662.4 Q .189 +(The search rules for $[ ... $] are some)127 678.6 R .189(what dif)-.25 F .189 +(ferent than usual.)-.25 F .189(If the name \(that is, the `)5.189 F(`...)-.74 +E -.74('')-.7 G(\)).74 E .109(has at least one dot, it al)102 690.6 R -.1(wa) +-.1 G .109(ys tries the unmodi\214ed name \214rst.).1 F .11(If that f)5.11 F +.11(ails, it tries the reduced search)-.1 F .124 +(path, and lastly tries the unmodi\214ed name \(b)102 702.6 R .124 +(ut only for names without a dot, since names with a dot)-.2 F(ha)102 714.6 Q +.788 -.15(ve a)-.2 H .488(lready been tried\).).15 F .488(This allo)5.488 F +.489(ws names such as `)-.25 F(`utc.CS')-.74 E 2.989('t)-.74 G 2.989(om)362.805 +714.6 S .489(atch the site in Czechoslo)378.574 714.6 R -.25(va)-.15 G(kia).25 +E EP +%%Page: 22 19 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-22 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF 1.588 +(rather than the site in your local Computer Science department.)102 96 R 1.587 +(It also prefers A and CN)6.587 F(AME)-.35 E .512(records o)102 108 R -.15(ve) +-.15 G 3.012(rM).15 G 3.012(Xr)163.814 108 S .512 +(ecords \212 that is, if it \214nds an MX record it mak)177.376 108 R .513 +(es note of it, b)-.1 F .513(ut k)-.2 F .513(eeps looking.)-.1 F 1.542(This w) +102 120 R(ay)-.1 E 4.042(,i)-.65 G 4.042(fy)149.054 120 S 1.541(ou ha)161.426 +120 R 1.841 -.15(ve a w)-.2 H 1.541 +(ildcard MX record matching your domain, it will not assume that all).15 F +(names match.)102 132 Q F0 2.5(4.10. Mo)87 156 R(ving the P)-.1 E(er)-.2 E +(-User F)-.37 E(orward Files)-.25 E F1 .772(Some sites mount each user')127 +172.2 R 3.272(sh)-.55 G .772(ome directory from a local disk on their w)256.13 +172.2 R .772(orkstation, so that)-.1 F .576(local access is f)102 184.2 R 3.076 +(ast. Ho)-.1 F(we)-.25 E -.15(ve)-.25 G 1.376 -.4(r, t).15 H .575 +(he result is that .forw).4 F .575(ard \214le lookups are slo)-.1 F 4.375 -.65 +(w. I)-.25 H 3.075(ns).65 G .575(ome cases, mail)439.25 184.2 R .216(can e)102 +196.2 R -.15(ve)-.25 G 2.716(nb).15 G 2.716(ed)144.792 196.2 S(eli)156.948 +196.2 Q -.15(ve)-.25 G .216 +(red on machines inappropriately because of a \214le serv).15 F .216 +(er being do)-.15 F 2.716(wn. The)-.25 F(perfor)2.716 E(-)-.2 E +(mance can be especially bad if you run the automounter)102 208.2 Q(.)-.55 E +(The)127 224.4 Q F0(J)2.5 E F1(option allo)2.5 E(ws you to set a path of forw) +-.25 E(ard \214les.)-.1 E -.15(Fo)5 G 2.5(re).15 G +(xample, the con\214g \214le line)366.6 224.4 Q(OJ/v)142 240.6 Q(ar/forw)-.25 E +(ard/$u:$z/.forw)-.1 E(ard)-.1 E -.1(wo)102 256.8 S .208 +(uld \214rst look for a \214le with the same name as the user').1 F 2.707(sl) +-.55 G .207(ogin in /v)343.191 256.8 R(ar/forw)-.25 E .207 +(ard; if that is not found)-.1 F .129 +(\(or is inaccessible\) the \214le \231.forw)102 268.8 R .129 +(ard\232 in the user')-.1 F 2.629(sh)-.55 G .13(ome directory is searched.) +311.901 268.8 R 2.63(At)5.13 G .13(ruly perv)435.02 268.8 R .13(erse site)-.15 +F(could also search by sender by using $r)102 280.8 Q 2.5(,$)-.4 G(s, or $f.) +269.07 280.8 Q .69(If you create a directory such as /v)127 297 R(ar/forw)-.25 +E .69(ard, it should be mode 1777 \(that is, the stick)-.1 F 3.19(yb)-.15 G(it) +498.44 297 Q(should be set\).)102 309 Q +(Users should create the \214les mode 644.)5 E F0 2.5(4.11. Fr)87 333 R +(ee Space)-.18 E F1 1.122(On systems that ha)127 349.2 R 1.422 -.15(ve t)-.2 H +(he).15 E/F2 10/Times-Italic@0 SF(statfs)3.622 E F1 1.123 +(\(2\) system call, you can specify a minimum number of free)B .61 +(blocks on the queue \214lesystem using the)102 361.2 R F0(b)3.11 E F1 3.11 +(option. If)3.11 F .61(there are fe)3.11 F .61 +(wer than the indicated number of)-.25 F .406 +(blocks free on the \214lesystem on which the queue is mounted the SMTP serv) +102 373.2 R .407(er will reject mail with)-.15 F(the 452 error code.)102 385.2 +Q(This in)5 E(vites the SMTP client to try ag)-.4 E(ain later)-.05 E(.)-.55 E +(Be)127 401.4 Q -.1(wa)-.25 G .746(re of setting this option too high; it can \ +cause rejection of email when that mail w).1 F(ould)-.1 E +(be processed without dif)102 413.4 Q(\214culty)-.25 E(.)-.65 E 1.772 +(This option can also specify an adv)127 429.6 R 1.773 +(ertised \231maximum message size\232 for hosts that speak)-.15 F(ESMTP)102 +441.6 Q(.)-1.11 E F0 2.5(4.12. Pri)87 465.6 R -.1(va)-.1 G(cy Flags).1 E F1 +(The)127 481.8 Q F0(p)3.591 E F1 1.091(option allo)3.591 F 1.091 +(ws you to set certain `)-.25 F(`pri)-.74 E -.25(va)-.25 G -.15(cy).25 G 2.571 +-.74('' \215).15 H 3.591(ags. Actually).74 F 3.59(,m)-.65 G(an)409.27 481.8 Q +3.59(yo)-.15 G 3.59(ft)432.15 481.8 S 1.09(hem don')441.85 481.8 R 3.59(tg)-.18 +G -2.15 -.25(iv e)492.18 481.8 T .254(you an)102 493.8 R 2.754(ye)-.15 G .254 +(xtra pri)141.088 493.8 R -.25(va)-.25 G -.15(cy).25 G 2.754(,r)-.5 G .254 +(ather just insisting that client SMTP serv)196.666 493.8 R .254 +(ers use the HELO command before)-.15 F(using certain commands.)102 505.8 Q +.124(The option tak)127 522 R .124 +(es a series of \215ag names; the \214nal pri)-.1 F -.25(va)-.25 G .424 -.15 +(cy i).25 H 2.624(st).15 G .124(he inclusi)367.708 522 R .424 -.15(ve o)-.25 H +2.624(ro).15 G 2.624(ft)434.06 522 S .123(hose \215ags.)442.794 522 R -.15(Fo) +5.123 G(r).15 E -.15(ex)102 534 S(ample:).15 E(Op needmailhelo, noe)142 550.2 Q +(xpn)-.15 E .928(insists that the HELO or EHLO command be used before a MAIL c\ +ommand is accepted and dis-)102 566.4 R(ables the EXPN command.)102 578.4 Q +.244(The \231restrictmailq\232 option restricts printing the queue to the grou\ +p that o)127 594.6 R .244(wns the queue direc-)-.25 F(tory)102 606.6 Q 5(.I) +-.65 G 2.5(ti)128.29 606.6 S 2.5(sa)136.35 606.6 S +(bsurd to set this if you don')147.18 606.6 Q 2.5(ta)-.18 G +(lso protect the logs.)266.72 606.6 Q F0 2.5(4.13. Send)87 630.6 R(to Me T)2.5 +E(oo)-.92 E F1(Normally)127 646.8 Q(,)-.65 E F2(sendmail)3.423 E F1 .923 +(deletes the \(en)3.423 F -.15(ve)-.4 G .923(lope\) sender from an).15 F 3.423 +(yl)-.15 G .924(ist e)375.484 646.8 R 3.424(xpansions. F)-.15 F .924(or e)-.15 +F .924(xample, if)-.15 F .761(\231matt\232 sends to a list that contains \231m\ +att\232 as one of the members he w)102 658.8 R(on')-.1 E 3.261(tg)-.18 G .761 +(et a cop)416.705 658.8 R 3.261(yo)-.1 G 3.261(ft)462.488 658.8 S .761(he mes-) +471.859 658.8 R 3.066(sage. If)102 670.8 R(the)3.066 E F0<ad6d>3.066 E F1 .566 +(\(me too\) command line \215ag, or if the)3.066 F F0(m)3.067 E F1 .567 +(option is set in the con\214guration \214le, this)3.067 F(beha)102 682.8 Q +(viour is supressed.)-.2 E(Some sites lik)5 E 2.5(et)-.1 G 2.5(or)265.58 682.8 +S(un the)276.41 682.8 Q/F3 9/Times-Roman@0 SF(SMTP)2.5 E F1(daemon with)2.5 E +F0<ad6d>2.5 E F1(.)A EP +%%Page: 23 20 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-23)452.9 60 Q 2.5(5. THE)72 96 R(WHOLE SCOOP ON THE CONFIGURA)2.5 E +(TION FILE)-.95 E/F1 10/Times-Roman@0 SF .264(This section describes the con\ +\214guration \214le in detail, including hints on ho)112 112.2 R 2.764(wt)-.25 +G 2.763(ow)426.298 112.2 S .263(rite one of your)441.281 112.2 R -.25(ow)87 +124.2 S 2.5(ni).25 G 2.5(fy)109.25 124.2 S(ou ha)120.08 124.2 Q .3 -.15(ve t) +-.2 H(o.).15 E .648(There is one point that should be made clear immediately: \ +the syntax of the con\214guration \214le is)112 140.4 R 1.077 +(designed to be reasonably easy to parse, since this is done e)87 152.4 R -.15 +(ve)-.25 G 1.076(ry time).15 F/F2 10/Times-Italic@0 SF(sendmail)3.576 E F1 +1.076(starts up, rather than)3.576 F(easy for a human to read or write.)87 +164.4 Q +(On the \231future project\232 list is a con\214guration-\214le compiler)5 E(.) +-.55 E(An o)112 180.6 Q -.15(ve)-.15 G(rvie).15 E 2.5(wo)-.25 G 2.5(ft)170.88 +180.6 S(he con\214guration \214le is gi)179.49 180.6 Q -.15(ve)-.25 G 2.5<6e8c> +.15 G(rst, follo)301.59 180.6 Q(wed by details of the semantics.)-.25 E F0 2.5 +(5.1. Con\214guration)87 204.6 R(File Lines)2.5 E F1 1.315 +(The con\214guration \214le is or)127 220.8 R -.05(ga)-.18 G 1.316 +(nized as a series of lines, each of which be).05 F 1.316(gins with a single) +-.15 F .742(character de\214ning the semantics for the rest of the line.)102 +232.8 R .742(Lines be)5.742 F .741(ginning with a space or a tab are)-.15 F +1.148 +(continuation lines \(although the semantics are not well de\214ned in man)102 +244.8 R 3.649(yp)-.15 G 3.649(laces\). Blank)407.513 244.8 R 1.149(lines and) +3.649 F(lines be)102 256.8 Q(ginning with a sharp symbol \(`#'\) are comments.) +-.15 E F0 2.5(5.1.1. R)102 280.8 R(and S \212 r)2.5 E(ewriting rules)-.18 E F1 +.407(The core of address parsing are the re)142 297 R .406(writing rules.)-.25 +F .406(These are an ordered production sys-)5.406 F(tem.)117 309 Q F2(Sendmail) +5.282 E F1 .282(scans through the set of re)2.782 F .283 +(writing rules looking for a match on the left hand side)-.25 F .132 +(\(LHS\) of the rule.)117 321 R .131(When a rule matches, the address is repla\ +ced by the right hand side \(RHS\) of)5.131 F(the rule.)117 333 Q 1.125 +(There are se)142 349.2 R -.15(ve)-.25 G 1.125(ral sets of re).15 F 1.126 +(writing rules.)-.25 F 1.126(Some of the re)6.126 F 1.126 +(writing sets are used internally)-.25 F .21(and must ha)117 361.2 R .51 -.15 +(ve s)-.2 H .21(peci\214c semantics.).15 F .21(Other re)5.21 F .21 +(writing sets do not ha)-.25 F .51 -.15(ve s)-.2 H .21 +(peci\214cally assigned seman-).15 F +(tics, and may be referenced by the mailer de\214nitions or by other re)117 +373.2 Q(writing sets.)-.25 E(The syntax of these tw)142 389.4 Q 2.5(oc)-.1 G +(ommands are:)244.38 389.4 Q F0(S)157 405.6 Q F2(n)A F1 .277 +(Sets the current ruleset being collected to)117 421.8 R F2(n)2.778 E F1 5.278 +(.I)C 2.778(fy)302.52 421.8 S .278(ou be)313.628 421.8 R .278 +(gin a ruleset more than once it deletes the)-.15 F(old de\214nition.)117 433.8 +Q F0(R)157 450 Q F2(lhs rhs comments)A F1 .303(The \214elds must be separated \ +by at least one tab character; there may be embedded spaces in the)117 466.2 R +2.738(\214elds. The)117 478.2 R F2(lhs)2.738 E F1 .238 +(is a pattern that is applied to the input.)2.738 F .239 +(If it matches, the input is re)5.239 F .239(written to the)-.25 F F2(rhs)117 +490.2 Q F1 5(.T)C(he)143.39 490.2 Q F2(comments)2.5 E F1(are ignored.)2.5 E +2.266(Macro e)142 506.4 R 2.266(xpansions of the form)-.15 F F0($)4.766 E F2(x) +A F1 2.265(are performed when the con\214guration \214le is read.)4.765 F .08 +(Expansions of the form)117 518.4 R F0($&)2.58 E F2(x)A F1 .081 +(are performed at run time using a some)2.58 F .081 +(what less general algorithm.)-.25 F .639 +(This for is intended only for referencing internally de\214ned macros such as) +117 530.4 R F0($h)3.138 E F1 .638(that are changed)3.138 F(at runtime.)117 +542.4 Q F0 2.5(5.1.1.1. The)117 566.4 R(left hand side)2.5 E F1 1.617 +(The left hand side of re)157 582.6 R 1.617(writing rules contains a pattern.) +-.25 F 1.617(Normal w)6.617 F 1.617(ords are simply)-.1 F(matched directly)132 +594.6 Q 5(.M)-.65 G(etasyntax is introduced using a dollar sign.)214.67 594.6 Q +(The metasymbols are:)5 E F0($*)172 610.8 Q F1(Match zero or more tok)192.14 +610.8 Q(ens)-.1 E F0($+)172 622.8 Q F1(Match one or more tok)9.44 E(ens)-.1 E +F0<24ad>172 634.8 Q F1(Match e)9.44 E(xactly one tok)-.15 E(en)-.1 E F0($=)172 +646.8 Q F2(x)A F1(Match an)5 E 2.5(yp)-.15 G(hrase in class)241.98 646.8 Q F2 +(x)2.5 E F0($~)172 658.8 Q F2(x)A F1(Match an)7.37 E 2.5(yw)-.15 G +(ord not in class)244.1 658.8 Q F2(x)2.5 E F1 .499(If an)132 675 R 2.999(yo) +-.15 G 2.999(ft)163.948 675 S .499(hese match, the)173.057 675 R 2.999(ya)-.15 +G .499(re assigned to the symbol)248.274 675 R F0($)2.999 E F2(n)A F1 .498 +(for replacement on the right hand)2.999 F(side, where)132 687 Q F2(n)2.5 E F1 +(is the inde)2.5 E 2.5(xi)-.15 G 2.5(nt)238.78 687 S(he LHS.)249.06 687 Q -.15 +(Fo)5 G 2.5(re).15 G(xample, if the LHS:)307.92 687 Q($\255:$+)172 703.2 Q +(is applied to the input:)132 719.4 Q EP +%%Page: 24 21 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-24 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF(UCB)172 96 Q +(ARP)-.35 E(A:eric)-.92 E(the rule will match, and the v)132 112.2 Q +(alues passed to the RHS will be:)-.25 E 7.5($1 UCB)172 128.4 R(ARP)-.35 E(A) +-.92 E 7.5($2 eric)172 140.4 R(Additionally)157 160.8 Q 3.398(,t)-.65 G .898 +(he LHS can include)215.588 160.8 R F0($@)3.398 E F1 .898(to match zero tok) +3.398 F 3.398(ens. This)-.1 F(is)3.398 E/F2 10/Times-Italic@0 SF(not)3.398 E F1 +.898(bound to a)3.398 F F0($)132 172.8 Q F2(N)A F1 .837(on the RHS, and is nor\ +mally only used when it stands alone in order to match the null)3.338 F(input.) +132 184.8 Q F0 2.5(5.1.1.2. The)117 208.8 R(right hand side)2.5 E F1 .525 +(When the left hand side of a re)157 225 R .526 +(writing rule matches, the input is deleted and replaced)-.25 F .932 +(by the right hand side.)132 237 R -.8(To)5.932 G -.1(ke).8 G .932 +(ns are copied directly from the RHS unless the).1 F 3.432(yb)-.15 G -.15(eg) +457.848 237 S .931(in with a).15 F(dollar sign.)132 249 Q(Metasymbols are:)5 E +F0($)172 265.2 Q F2(n)A F1(Substitute inde\214nite tok)222.55 265.2 Q(en)-.1 E +F2(n)2.5 E F1(from LHS)2.5 E F0($[)172 277.2 Q F2(name)A F0($])A F1 +(Canonicalize)222.55 277.2 Q F2(name)2.5 E F0($\()172 289.2 Q F2(map k)A -.3 +(ey)-.1 G F0($@)2.8 E F2(ar)A(guments)-.37 E F0($:)2.5 E F2(default)A F0($\)) +2.5 E F1(Generalized k)222.55 301.2 Q -.15(ey)-.1 G(ed mapping function).15 E +F0($>)172 313.2 Q F2(n)A F1(\231Call\232 ruleset)222.55 313.2 Q F2(n)2.5 E F0 +($#)172 325.2 Q F2(mailer)A F1(Resolv)222.55 325.2 Q 2.5(et)-.15 G(o)259.9 +325.2 Q F2(mailer)2.5 E F0($@)172 337.2 Q F2(host)A F1(Specify)222.55 337.2 Q +F2(host)2.5 E F0($:)172 349.2 Q F2(user)A F1(Specify)222.55 349.2 Q F2(user)2.5 +E F1(The)157 369.6 Q F0($)3.012 E F2(n)A F1 .512 +(syntax substitutes the corresponding v)3.012 F .513(alue from a)-.25 F F0($+) +3.013 E F1(,)A F0<24ad>3.013 E F1(,)A F0($*)3.013 E F1(,)A F0($=)3.013 E F1 +3.013(,o)C(r)461.874 369.6 Q F0($~)3.013 E F1(match)3.013 E(on the LHS.)132 +381.6 Q(It may be used an)5 E(ywhere.)-.15 E 2.701(Ah)157 397.8 S .201 +(ost name enclosed between)171.921 397.8 R F0($[)2.7 E F1(and)2.7 E F0($])2.7 E +F1 .2(is look)2.7 F .2(ed up using the)-.1 F F2 -.1(ge)2.7 G(thostent).1 E F1 +.2(\(3\) routines)1.666 F 3.332(and replaced by the canonical name)132 411.8 R +/F3 7/Times-Roman@0 SF(7)291.672 407.8 Q F1 8.333(.F)295.172 411.8 S 3.333 +(or e)311.415 411.8 R 3.333(xample, \231$[csam$]\232 might become \231lbl-)-.15 +F 1.924(csam.arpa\232 and \231$[[128.32.130.2]$]\232 w)132 423.8 R 1.923 +(ould become \231v)-.1 F(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G(EDU.) +.65 E<9a>-.7 E F2(Send-)6.923 E(mail)132 435.8 Q F1 .435(recognizes it')2.935 F +2.935(sn)-.55 G .436(umeric IP address without calling the name serv)218.575 +435.8 R .436(er and replaces it with)-.15 F(it')132 447.8 Q 2.5(sc)-.55 G +(anonical name.)151.17 447.8 Q(The)157 464 Q F0($\()2.862 E F1(...)2.862 E F0 +($\))5.361 E F1 .361 +(syntax is a more general form of lookup; it uses a named map instead of)2.861 +F .124(an implicit map.)132 476 R .125(If no lookup is found, the indicted) +5.124 F F2(default)2.625 E F1 .125(is inserted; if no def)2.625 F .125 +(ault is speci-)-.1 F(\214ed and no lookup matches, the v)132 488 Q +(alue is left unchanged.)-.25 E(The)157 504.2 Q F0($>)3.572 E F2(n)A F1 1.071 +(syntax causes the remainder of the line to be substituted as usual and then) +3.572 F .571(passed as the ar)132 516.2 R .572(gument to ruleset)-.18 F F2(n) +3.072 E F1 5.572(.T)C .572(he \214nal v)288.852 516.2 R .572(alue of ruleset) +-.25 F F2(n)3.072 E F1 .572(then becomes the substitu-)3.072 F +(tion for this rule.)132 528.2 Q(The)157 544.4 Q F0($#)3.097 E F1 .597 +(syntax should)3.097 F F2(only)3.097 E F1 .597(be used in ruleset zero.)3.097 F +.596(It causes e)5.597 F -.25(va)-.25 G .596(luation of the ruleset).25 F .606 +(to terminate immediately)132 556.4 R 3.106(,a)-.65 G .607 +(nd signals to sendmail that the address has completely resolv)243.158 556.4 R +(ed.)-.15 E(The complete syntax is:)132 568.4 Q F0($#)172 584.6 Q F2(mailer)A +F0($@)2.5 E F2(host)A F0($:)2.5 E F2(user)A F1 .394 +(This speci\214es the {mailer)132 600.8 R 2.894(,h)-.4 G .394 +(ost, user} 3-tuple necessary to direct the mailer)245.466 600.8 R 5.394(.I) +-.55 G 2.894(ft)447.548 600.8 S .394(he mailer is)456.552 600.8 R .136 +(local the host part may be omitted)132 614.8 R F3(8)268.916 610.8 Q F1 5.136 +(.T)272.416 614.8 S(he)286.162 614.8 Q F2(mailer)2.636 E F1 .135 +(must be a single w)2.636 F .135(ord, b)-.1 F .135(ut the)-.2 F F2(host)2.635 E +F1(and)2.635 E F2(user)2.635 E F1 .251(may be multi-part.)132 626.8 R .252 +(If the)5.252 F F2(mailer)2.752 E F1 .252(is the b)2.752 F .252 +(uiltin IPC mailer)-.2 F 2.752(,t)-.4 G(he)369.72 626.8 Q F2(host)2.752 E F1 +.252(may be a colon-separated)2.752 F 2.439 +(list of hosts that are searched in order for the \214rst w)132 638.8 R 2.438 +(orking address \(e)-.1 F 2.438(xactly lik)-.15 F 4.938(eM)-.1 G(X)496.78 638.8 +Q(records\).)132 650.8 Q .32 LW 76 660.4 72 660.4 DL 80 660.4 76 660.4 DL 84 +660.4 80 660.4 DL 88 660.4 84 660.4 DL 92 660.4 88 660.4 DL 96 660.4 92 660.4 +DL 100 660.4 96 660.4 DL 104 660.4 100 660.4 DL 108 660.4 104 660.4 DL 112 +660.4 108 660.4 DL 116 660.4 112 660.4 DL 120 660.4 116 660.4 DL 124 660.4 120 +660.4 DL 128 660.4 124 660.4 DL 132 660.4 128 660.4 DL 136 660.4 132 660.4 DL +140 660.4 136 660.4 DL 144 660.4 140 660.4 DL 148 660.4 144 660.4 DL 152 660.4 +148 660.4 DL 156 660.4 152 660.4 DL 160 660.4 156 660.4 DL 164 660.4 160 660.4 +DL 168 660.4 164 660.4 DL 172 660.4 168 660.4 DL 176 660.4 172 660.4 DL 180 +660.4 176 660.4 DL 184 660.4 180 660.4 DL 188 660.4 184 660.4 DL 192 660.4 188 +660.4 DL 196 660.4 192 660.4 DL 200 660.4 196 660.4 DL 204 660.4 200 660.4 DL +208 660.4 204 660.4 DL 212 660.4 208 660.4 DL 216 660.4 212 660.4 DL/F4 5 +/Times-Roman@0 SF(7)93.6 670.8 Q/F5 8/Times-Roman@0 SF +(This is actually completely equi)3.2 I -.2(va)-.2 G(lent to $\(host).2 E/F6 8 +/Times-Italic@0 SF(hostname)2 E F5 2($\). In)B(particular)2 E 2(,a)-.32 G/F7 8 +/Times-Bold@0 SF($:)A F5(def)2 E(ault can be used.)-.08 E F4(8)93.6 684.4 Q F5 +-.88(Yo)3.2 K 2.207(um).88 G .207(ay w)117.427 687.6 R .208 +(ant to use it for special \231per user\232 e)-.08 F 2.208(xtensions. F)-.12 F +.208(or e)-.12 F .208 +(xample, at CMU you can send email to \231jgm+foo\232; the part af-)-.12 F(ter\ + the plus sign is not part of the user name, and is passed to the local mailer\ + for local use.)72 697.2 Q EP +%%Page: 25 22 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-25)452.9 60 Q/F1 10/Times-Roman@0 SF 3.794(AR)157 96 S 1.294 +(HS may also be preceded by a)174.684 96 R F0($@)3.795 E F1 1.295(or a)3.795 F +F0($:)3.795 E F1 1.295(to control e)3.795 F -.25(va)-.25 G 3.795(luation. A).25 +F F0($@)3.795 E F1(pre\214x)3.795 E .611 +(causes the ruleset to return with the remainder of the RHS as the v)132 108 R +3.11(alue. A)-.25 F F0($:)3.11 E F1 .61(pre\214x causes)3.11 F .431 +(the rule to terminate immediately)132 120 R 2.931(,b)-.65 G .431 +(ut the ruleset to continue; this can be used to a)276.625 120 R -.2(vo)-.2 G +.432(id con-).2 F(tinued application of a rule.)132 132 Q +(The pre\214x is stripped before continuing.)5 E(The)157 148.2 Q F0($@)2.5 E F1 +(and)2.5 E F0($:)2.5 E F1(pre\214x)2.5 E(es may precede a)-.15 E F0($>)2.5 E F1 +(spec; for e)2.5 E(xample:)-.15 E 20.19(R$+ $:)172 164.4 R($>7 $1)2.5 E .256 +(matches an)132 180.6 R .256(ything, passes that to ruleset se)-.15 F -.15(ve) +-.25 G .256(n, and continues; the).15 F F0($:)2.756 E F1 .256 +(is necessary to a)2.756 F -.2(vo)-.2 G .256(id an).2 F(in\214nite loop.)132 +192.6 Q .051(Substitution occurs in the order described, that is, parameters f\ +rom the LHS are substi-)157 208.8 R .556(tuted, hostnames are canonicalized, \ +\231subroutines\232 are called, and \214nally)132 220.8 R F0($#)3.056 E F1(,)A +F0($@)3.056 E F1 3.056(,a)C(nd)467.348 220.8 Q F0($:)3.056 E F1(are)3.056 E +(processed.)132 232.8 Q F0 2.5(5.1.1.3. Semantics)117 256.8 R(of r)2.5 E +(ewriting rule sets)-.18 E F1 2.921(There are \214v)157 273 R 5.421(er)-.15 G +-.25(ew)226.973 273 S 2.922(riting sets that ha).25 F 3.222 -.15(ve s)-.2 H +2.922(peci\214c semantics.).15 F 2.922(These are related as)7.922 F +(depicted by \214gure 2.)132 285 Q 1.092 +(Ruleset three should turn the address into \231canonical form.)157 301.2 R +6.091<9a54>-.7 G 1.091(his form should ha)416.917 301.2 R -.15(ve)-.2 G +(the basic syntax:)132 313.2 Q(local-part@host-domain-spec)172 329.4 Q 1.295 +(If no \231@\232 sign is speci\214ed, then the host-domain-spec)132 345.6 R/F2 +10/Times-Italic@0 SF(may)3.796 E F1 1.296(be appended from the sender)3.796 F +1.284(address \(if the)132 357.6 R F0(C)3.784 E F1 1.284 +(\215ag is set in the mailer de\214nition corresponding to the)3.784 F F2 +(sending)3.784 E F1(mailer\).)3.784 E +(Ruleset three is applied by sendmail before doing an)132 369.6 Q +(ything with an)-.15 E 2.5(ya)-.15 G(ddress.)411.39 369.6 Q .506(Ruleset zero \ +is applied after ruleset three to addresses that are going to actually spec-) +157 385.8 R .296(ify recipients.)132 397.8 R .296(It must resolv)5.296 F 2.796 +(et)-.15 G 2.796(oa)258.04 397.8 S F2({mailer)A 2.796(,h)-1.11 G .296 +(ost, user})312.368 397.8 R F1 2.795(triple. The)2.796 F F2(mailer)2.795 E F1 +.295(must be de\214ned in)2.795 F .56 +(the mailer de\214nitions from the con\214guration \214le.)132 409.8 R(The) +5.561 E F2(host)3.061 E F1 .561(is de\214ned into the)3.061 F F0($h)3.061 E F1 +.561(macro for)3.061 F(use in the ar)132 421.8 Q(gv e)-.18 E +(xpansion of the speci\214ed mailer)-.15 E(.)-.55 E 1.357(Rulesets one and tw) +157 438 R 3.857(oa)-.1 G 1.357 +(re applied to all sender and recipient addresses respecti)254.538 438 R -.15 +(ve)-.25 G(ly).15 E(.)-.65 E(The)132 450 Q 2.5(ya)-.15 G(re applied before an) +159.34 450 Q 2.5(ys)-.15 G(peci\214cation in the mailer de\214nition.)250.27 +450 Q(The)5 E 2.5(ym)-.15 G(ust ne)429 450 Q -.15(ve)-.25 G 2.5(rr).15 G(esolv) +470.81 450 Q(e.)-.15 E .4 LW 77 483.6 72 483.6 DL 79 483.6 74 483.6 DL 84 483.6 +79 483.6 DL 89 483.6 84 483.6 DL 94 483.6 89 483.6 DL 99 483.6 94 483.6 DL 104 +483.6 99 483.6 DL 109 483.6 104 483.6 DL 114 483.6 109 483.6 DL 119 483.6 114 +483.6 DL 124 483.6 119 483.6 DL 129 483.6 124 483.6 DL 134 483.6 129 483.6 DL +139 483.6 134 483.6 DL 144 483.6 139 483.6 DL 149 483.6 144 483.6 DL 154 483.6 +149 483.6 DL 159 483.6 154 483.6 DL 164 483.6 159 483.6 DL 169 483.6 164 483.6 +DL 174 483.6 169 483.6 DL 179 483.6 174 483.6 DL 184 483.6 179 483.6 DL 189 +483.6 184 483.6 DL 194 483.6 189 483.6 DL 199 483.6 194 483.6 DL 204 483.6 199 +483.6 DL 209 483.6 204 483.6 DL 214 483.6 209 483.6 DL 219 483.6 214 483.6 DL +224 483.6 219 483.6 DL 229 483.6 224 483.6 DL 234 483.6 229 483.6 DL 239 483.6 +234 483.6 DL 244 483.6 239 483.6 DL 249 483.6 244 483.6 DL 254 483.6 249 483.6 +DL 259 483.6 254 483.6 DL 264 483.6 259 483.6 DL 269 483.6 264 483.6 DL 274 +483.6 269 483.6 DL 279 483.6 274 483.6 DL 284 483.6 279 483.6 DL 289 483.6 284 +483.6 DL 294 483.6 289 483.6 DL 299 483.6 294 483.6 DL 304 483.6 299 483.6 DL +309 483.6 304 483.6 DL 314 483.6 309 483.6 DL 319 483.6 314 483.6 DL 324 483.6 +319 483.6 DL 329 483.6 324 483.6 DL 334 483.6 329 483.6 DL 339 483.6 334 483.6 +DL 344 483.6 339 483.6 DL 349 483.6 344 483.6 DL 354 483.6 349 483.6 DL 359 +483.6 354 483.6 DL 364 483.6 359 483.6 DL 369 483.6 364 483.6 DL 374 483.6 369 +483.6 DL 379 483.6 374 483.6 DL 384 483.6 379 483.6 DL 389 483.6 384 483.6 DL +394 483.6 389 483.6 DL 399 483.6 394 483.6 DL 404 483.6 399 483.6 DL 409 483.6 +404 483.6 DL 414 483.6 409 483.6 DL 419 483.6 414 483.6 DL 424 483.6 419 483.6 +DL 429 483.6 424 483.6 DL 434 483.6 429 483.6 DL 439 483.6 434 483.6 DL 444 +483.6 439 483.6 DL 449 483.6 444 483.6 DL 454 483.6 449 483.6 DL 459 483.6 454 +483.6 DL 464 483.6 459 483.6 DL 469 483.6 464 483.6 DL 474 483.6 469 483.6 DL +479 483.6 474 483.6 DL 484 483.6 479 483.6 DL 489 483.6 484 483.6 DL 494 483.6 +489 483.6 DL 499 483.6 494 483.6 DL 504 483.6 499 483.6 DL(addr)91.915 578 Q +133.2 576 111.6 576 DL 133.2 576 126 577.8 DL 133.2 576 126 574.2 DL(3)141.5 +578 Q 133.2 565.2 133.2 586.8 DL 154.8 565.2 133.2 565.2 DL 154.8 586.8 154.8 +565.2 DL 133.2 586.8 154.8 586.8 DL 176.4 576 154.8 576 DL 176.4 576 169.2 +577.8 DL 176.4 576 169.2 574.2 DL(D)183.59 578 Q 176.4 565.2 176.4 586.8 DL 198 +565.2 176.4 565.2 DL 198 586.8 198 565.2 DL 176.4 586.8 198 586.8 DL 219.6 576 +198 576 DL 277.2 558 255.6 558 DL 277.2 558 270 559.8 DL 277.2 558 270 556.2 DL +(1)285.5 560 Q 277.2 547.2 277.2 568.8 DL 298.8 547.2 277.2 547.2 DL 298.8 +568.8 298.8 547.2 DL 277.2 568.8 298.8 568.8 DL 320.4 558 298.8 558 DL 320.4 +558 313.2 559.8 DL 320.4 558 313.2 556.2 DL(S)328.42 560 Q 320.4 547.2 320.4 +568.8 DL 342 547.2 320.4 547.2 DL 342 568.8 342 547.2 DL 320.4 568.8 342 568.8 +DL 363.6 558 342 558 DL 277.2 594 255.6 594 DL 277.2 594 270 595.8 DL 277.2 594 +270 592.2 DL(2)285.5 596 Q 277.2 583.2 277.2 604.8 DL 298.8 583.2 277.2 583.2 +DL 298.8 604.8 298.8 583.2 DL 277.2 604.8 298.8 604.8 DL 320.4 594 298.8 594 DL +320.4 594 313.2 595.8 DL 320.4 594 313.2 592.2 DL(R)327.865 596 Q 320.4 583.2 +320.4 604.8 DL 342 583.2 320.4 583.2 DL 342 604.8 342 583.2 DL 320.4 604.8 342 +604.8 DL 363.6 594 342 594 DL 421.2 576 399.6 576 DL 421.2 576 414 577.8 DL +421.2 576 414 574.2 DL(4)429.5 578 Q 421.2 565.2 421.2 586.8 DL 442.8 565.2 +421.2 565.2 DL 442.8 586.8 442.8 565.2 DL 421.2 586.8 442.8 586.8 DL 464.4 576 +442.8 576 DL 464.4 576 457.2 577.8 DL 464.4 576 457.2 574.2 DL(msg)466.865 578 +Q 255.6 558 219.6 576 DL 255.6 594 219.6 576 DL 399.6 576 363.6 558 DL 399.6 +576 363.6 594 DL 208.8 522 187.2 522 DL 208.8 522 201.6 523.8 DL 208.8 522 +201.6 520.2 DL(0)217.1 524 Q 208.8 511.2 208.8 532.8 DL 230.4 511.2 208.8 511.2 +DL 230.4 532.8 230.4 511.2 DL 208.8 532.8 230.4 532.8 DL 252 522 230.4 522 DL +252 522 244.8 523.8 DL 252 522 244.8 520.2 DL(resolv)265.69 524 Q(ed address) +-.15 E 187.2 522 162 576 DL(Figure 2 \212 Re)216.045 624 Q +(writing set semantics)-.25 E 2.5(D\212s)209.35 636 S(ender domain addition) +235.46 636 Q 2.5(S\212m)209.35 648 S(ailer)237.69 648 Q(-speci\214c sender re) +-.2 E(writing)-.25 E 2.5(R\212m)209.35 660 S(ailer)238.8 660 Q +(-speci\214c recipient re)-.2 E(writing)-.25 E 77 672 72 672 DL 79 672 74 672 +DL 84 672 79 672 DL 89 672 84 672 DL 94 672 89 672 DL 99 672 94 672 DL 104 672 +99 672 DL 109 672 104 672 DL 114 672 109 672 DL 119 672 114 672 DL 124 672 119 +672 DL 129 672 124 672 DL 134 672 129 672 DL 139 672 134 672 DL 144 672 139 672 +DL 149 672 144 672 DL 154 672 149 672 DL 159 672 154 672 DL 164 672 159 672 DL +169 672 164 672 DL 174 672 169 672 DL 179 672 174 672 DL 184 672 179 672 DL 189 +672 184 672 DL 194 672 189 672 DL 199 672 194 672 DL 204 672 199 672 DL 209 672 +204 672 DL 214 672 209 672 DL 219 672 214 672 DL 224 672 219 672 DL 229 672 224 +672 DL 234 672 229 672 DL 239 672 234 672 DL 244 672 239 672 DL 249 672 244 672 +DL 254 672 249 672 DL 259 672 254 672 DL 264 672 259 672 DL 269 672 264 672 DL +274 672 269 672 DL 279 672 274 672 DL 284 672 279 672 DL 289 672 284 672 DL 294 +672 289 672 DL 299 672 294 672 DL 304 672 299 672 DL 309 672 304 672 DL 314 672 +309 672 DL 319 672 314 672 DL 324 672 319 672 DL 329 672 324 672 DL 334 672 329 +672 DL 339 672 334 672 DL 344 672 339 672 DL 349 672 344 672 DL 354 672 349 672 +DL 359 672 354 672 DL 364 672 359 672 DL 369 672 364 672 DL 374 672 369 672 DL +379 672 374 672 DL 384 672 379 672 DL 389 672 384 672 DL 394 672 389 672 DL 399 +672 394 672 DL 404 672 399 672 DL 409 672 404 672 DL 414 672 409 672 DL 419 672 +414 672 DL 424 672 419 672 DL 429 672 424 672 DL 434 672 429 672 DL 439 672 434 +672 DL 444 672 439 672 DL 449 672 444 672 DL 454 672 449 672 DL 459 672 454 672 +DL 464 672 459 672 DL 469 672 464 672 DL 474 672 469 672 DL 479 672 474 672 DL +484 672 479 672 DL 489 672 484 672 DL 494 672 489 672 DL 499 672 494 672 DL 504 +672 499 672 DL EP +%%Page: 26 23 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-26 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF .265 +(Ruleset four is applied to all addresses in the message.)157 96 R .266 +(It is typically used to translate)5.265 F(internal to e)132 108 Q +(xternal form.)-.15 E F0 2.5(5.1.1.4. IPC)117 132 R(mailers)2.5 E F1 .333 +(Some special processing occurs if the ruleset zero resolv)157 148.2 R .332 +(es to an IPC mailer \(that is, a)-.15 F .241 +(mailer that has \231[IPC]\232 listed as the P)132 160.2 R .241(ath in the)-.15 +F F0(M)2.741 E F1 .241(con\214guration line.)2.741 F .242(The host name passed) +5.242 F .885(after \231$@\232 has MX e)132 172.2 R .885 +(xpansion performed; this looks the name up in DNS to \214nd alternate)-.15 F +(deli)132 184.2 Q -.15(ve)-.25 G(ry sites.).15 E(The host name can also be pro) +157 200.4 Q(vided as a dotted quad in square brack)-.15 E(ets; for e)-.1 E +(xample:)-.15 E([128.32.149.78])172 216.6 Q(This causes direct con)132 232.8 Q +-.15(ve)-.4 G(rsion of the numeric v).15 E(alue to a TCP/IP host address.)-.25 +E .894(The host name passed in after the \231$@\232 may also be a colon-separa\ +ted list of hosts.)157 249 R .63(Each is separately MX e)132 261 R .629 +(xpanded and the results are concatenated to mak)-.15 F 3.129(e\()-.1 G .629 +(essentially\) one)440.881 261 R .378(long MX list.)132 273 R .378 +(The intent here is to create \231f)5.378 F(ak)-.1 E .378 +(e\232 MX records that are not published in DNS)-.1 F(for pri)132 285 Q -.25 +(va)-.25 G(te internal netw).25 E(orks.)-.1 E .17 +(As a \214nal special case, the host name can be passed in as a te)157 301.2 R +.17(xt string in square brack-)-.15 F(ets:)132 313.2 Q([ucb)172 329.4 Q -.25 +(va)-.15 G(x.berk).25 E(ele)-.1 E -.65(y.)-.15 G(edu]).65 E 1.244(This form a) +132 345.6 R -.2(vo)-.2 G 1.244(ids the MX mapping.).2 F F0(N.B.:)6.244 E F1 +1.245(This is intended only for situations where you)3.744 F(ha)132 357.6 Q +.814 -.15(ve a n)-.2 H(etw).15 E .514(ork \214re)-.1 F -.1(wa)-.25 G .514 +(ll, so that your MX record points to a g).1 F(ate)-.05 E -.1(wa)-.25 G 3.014 +(ym).1 G .514(achine; this machine)420.762 357.6 R 1.603 +(could then do direct deli)132 369.6 R -.15(ve)-.25 G 1.604 +(ry to machines within your local domain.).15 F 1.604(Use of this feature)6.604 +F(directly violates RFC 1123 section 5.3.5: it should not be used lightly)132 +381.6 Q(.)-.65 E F0 2.5(5.1.2. D)102 405.6 R 2.5<8a64>2.5 G(e\214ne macr)157.28 +405.6 Q(o)-.18 E F1 .547(Macros are named with a single character)142 421.8 R +5.546(.T)-.55 G .546(hese may be selected from the entire ASCII)325.498 421.8 R +.892(set, b)117 433.8 R .892(ut user)-.2 F .892 +(-de\214ned macros should be selected from the set of upper case letters only) +-.2 F 5.892(.L)-.65 G -.25(ow)484.26 433.8 S(er).25 E +(case letters and special symbols are used internally)117 445.8 Q(.)-.65 E +(The syntax for macro de\214nitions is:)142 462 Q F0(D)157 478.2 Q/F2 10 +/Times-Italic@0 SF 1.666(xv)C(al)-1.666 E F1(where)117 494.4 Q F2(x)2.5 E F1 +(is the name of the macro and)2.5 E F2(val)2.5 E F1(is the v)2.5 E +(alue it should ha)-.25 E -.15(ve)-.2 G(.).15 E 1.085 +(Macros are interpolated using the construct)142 510.6 R F0($)3.585 E F2(x)A F1 +3.585(,w)C(here)346.775 510.6 Q F2(x)3.585 E F1 1.085 +(is the name of the macro to be)3.585 F 3.45(interpolated. This)117 522.6 R .95 +(interpolation is done when the con\214guration \214le is read, e)3.45 F .95 +(xcept in)-.15 F F0(M)3.45 E F1(lines.)3.45 E(The special construct)117 534.6 Q +F0($&)2.5 E F2(x)A F1(can be used in)2.5 E F0(R)2.5 E F1 +(lines to get deferred interpolation.)2.5 E +(Conditionals can be speci\214ed using the syntax:)142 550.8 Q($?x te)157 567 Q +(xt1 $| te)-.15 E(xt2 $.)-.15 E .246(This interpolates)117 583.2 R F2(te)2.746 +E(xt1)-.2 E F1 .246(if the macro)2.746 F F0($x)2.745 E F1 .245(is set, and) +2.745 F F2(te)2.745 E(xt2)-.2 E F1 2.745(otherwise. The)2.745 F .245 +(\231else\232 \()2.745 F F0($|)A F1 2.745(\)c)C .245(lause may be)451.3 583.2 R +(omitted.)117 595.2 Q(Lo)142 611.4 Q .261(wer case macro names are reserv)-.25 +F .261(ed to ha)-.15 F .562 -.15(ve s)-.2 H .262 +(pecial semantics, used to pass information).15 F 1.163 +(in or out of sendmail, and special characters are reserv)117 623.4 R 1.163 +(ed to pro)-.15 F 1.163(vide conditionals, etc.)-.15 F(Upper)6.162 E +(case names \(that is,)117 635.4 Q F0($A)2.5 E F1(through)2.5 E F0($Z)2.5 E F1 +2.5(\)a)C(re speci\214cally reserv)267.53 635.4 Q +(ed for con\214guration \214le authors.)-.15 E(The follo)142 651.6 Q +(wing macros)-.25 E F2(must)2.5 E F1(be de\214ned to transmit information into) +2.5 E F2(sendmail:)2.5 E EP +%%Page: 27 24 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-27)452.9 60 Q/F1 10/Times-Roman@0 SF 15.56(eT)157 96 S +(he SMTP entry message)183.11 96 Q 17.22(jT)157 108 S(he \231of)183.11 108 Q +(\214cial\232 domain name for this site)-.25 E 17.22(lT)157 120 S +(he format of the UNIX from line)183.11 120 Q 15(nT)157 132 S +(he name of the daemon \(for error messages\))183.11 132 Q 15(oT)157 144 S +(he set of "operators" in addresses)183.11 144 Q 15(qd)157 156 S(ef)182 156 Q +(ault format of sender address)-.1 E(The)117 172.2 Q F0($e)2.656 E F1 .157 +(macro is printed out when SMTP starts up.)2.656 F .157(The \214rst w)5.157 F +.157(ord must be the)-.1 F F0($j)2.657 E F1 2.657(macro. The)2.657 F F0($j) +2.657 E F1 .536(macro should be in RFC821 format.)117 184.2 R(The)5.536 E F0 +($l)3.036 E F1(and)3.036 E F0($n)3.036 E F1 .536 +(macros can be considered constants e)3.036 F(xcept)-.15 E .782 +(under terribly unusual circumstances.)117 196.2 R(The)5.783 E F0($o)3.283 E F1 +.783(macro consists of a list of characters which will)3.283 F .498 +(be considered tok)117 208.2 R .498(ens and which will separate tok)-.1 F .498 +(ens when doing parsing.)-.1 F -.15(Fo)5.498 G 2.998(re).15 G .497 +(xample, if \231@\232)441.866 208.2 R .996(were in the)117 220.2 R F0($o)3.496 +E F1 .996(macro, then the input \231a@b\232 w)3.496 F .997 +(ould be scanned as three tok)-.1 F .997(ens: \231a,)-.1 F 3.497<9a99>-.7 G(@,) +470.613 220.2 Q 3.497<9a61>-.7 G(nd)494 220.2 Q<9962>117 232.2 Q 5.595 -.7 +(.\232 F)-.4 H(inally).7 E 4.195(,t)-.65 G(he)176.14 232.2 Q F0($q)4.195 E F1 +1.695(macro speci\214es ho)4.195 F 4.194(wa)-.25 G 4.194(na)297.954 232.2 S +1.694(ddress should appear in a message when it is)311.588 232.2 R(def)117 +244.2 Q 2.5(aulted. F)-.1 F(or e)-.15 E +(xample, on our system these de\214nitions are:)-.15 E +(De$j Sendmail $v/$Z ready at $b)157 260.4 Q(DnMAILER-D)157 272.4 Q(AEMON)-.4 E +(DlFrom $g)157 284.4 Q($d)5 E(Do.:%@!^/[])157 296.4 Q(Dq$?x$x <$g>$|$g$.)157 +308.4 Q(Dj$w)157 320.4 Q .067(An acceptable alternati)117 336.6 R .367 -.15 +(ve f)-.25 H .067(or the).15 F F0($q)2.567 E F1 .067 +(macro is \231$g$?x \($x\)$.)2.567 F 2.567(\232. These)-.7 F .068 +(correspond to the follo)2.567 F(wing)-.25 E(tw)117 348.6 Q 2.5(of)-.1 G +(ormats:)137.73 348.6 Q(Eric Allman <eric@CS.Berk)157 364.8 Q(ele)-.1 E -.65 +(y.)-.15 G(EDU>).65 E(eric@CS.Berk)157 376.8 Q(ele)-.1 E -.65(y.)-.15 G +(EDU \(Eric Allman\)).65 E/F2 10/Times-Italic@0 SF(Sendmail)117 393 Q F1 +(properly quotes names that ha)2.5 E .3 -.15(ve s)-.2 H +(pecial characters if the \214rst form is used.).15 E .24 +(Some macros are de\214ned by)142 409.2 R F2(sendmail)2.739 E F1 .239 +(for interpolation into ar)2.739 F(gv')-.18 E 2.739(sf)-.55 G .239 +(or mailers or for other)414.734 409.2 R(conte)117 421.2 Q 2.5(xts. These)-.15 +F(macros are:)2.5 E 15.56(aT)157 437.4 S(he origination date in RFC 822 format) +183.11 437.4 Q 15(bT)157 449.4 S(he current date in RFC 822 format)183.11 449.4 +Q 15.56(cT)157 461.4 S(he hop count)183.11 461.4 Q 15(dT)157 473.4 S +(he date in UNIX \(ctime\) format)183.11 473.4 Q 16.67(fT)157 485.4 S +(he sender \(from\) address)183.11 485.4 Q 15(gT)157 497.4 S +(he sender address relati)183.11 497.4 Q .3 -.15(ve t)-.25 H 2.5(ot).15 G +(he recipient)301.29 497.4 Q 15(hT)157 509.4 S(he recipient host)183.11 509.4 Q +17.22(iT)157 521.4 S(he queue id)183.11 521.4 Q 15(kT)157 533.4 S +(he UUCP node name \(from the uname system call\))183.11 533.4 Q 12.22(mT)157 +545.4 S(he domain part of the)183.11 545.4 Q F2 -.1(ge)2.5 G(thostname).1 E F1 +(return v)2.5 E(alue)-.25 E 15(pS)157 557.4 S(endmail')182.56 557.4 Q 2.5(sp) +-.55 G(id)228.95 557.4 Q 16.67(rP)157 569.4 S(rotocol used to recei)182.56 +569.4 Q .3 -.15(ve t)-.25 H(he message).15 E 16.11(sS)157 581.4 S(ender')182.56 +581.4 Q 2.5(sh)-.55 G(ost name)218.94 581.4 Q 17.22(tA)157 593.4 S +(numeric representation of the current time)186.72 593.4 Q 15(uT)157 605.4 S +(he recipient user)183.11 605.4 Q 15(vT)157 617.4 S(he v)183.11 617.4 Q +(ersion number of sendmail)-.15 E 12.78(wT)157 629.4 S +(he hostname of this site)183.11 629.4 Q 15(xT)157 641.4 S +(he full name of the sender)183.11 641.4 Q 15.56(zT)157 653.4 S +(he home directory of the recipient)183.11 653.4 Q 15(_T)157 665.4 S(he v) +183.11 665.4 Q(alidated sender address)-.25 E .918 +(There are three types of dates that can be used.)142 685.8 R(The)5.918 E F0 +($a)3.418 E F1(and)3.418 E F0($b)3.418 E F1 .918(macros are in RFC 822)3.418 F +(format;)117 697.8 Q F0($a)3.047 E F1 .547(is the time as e)3.047 F .547 +(xtracted from the \231Date:\232 line of the message \(if there w)-.15 F .546 +(as one\), and)-.1 F F0($b)117 709.8 Q F1 .145 +(is the current date and time \(used for postmarks\).)2.645 F .145 +(If no \231Date:\232 line is found in the incoming)5.145 F(message,)117 721.8 Q +F0($a)2.547 E F1 .047(is set to the current time also.)2.547 F(The)5.046 E F0 +($d)2.546 E F1 .046(macro is equi)2.546 F -.25(va)-.25 G .046(lent to the).25 F +F0($b)2.546 E F1 .046(macro in UNIX)2.546 F EP +%%Page: 28 25 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-28 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF +(\(ctime\) format.)117 96 Q(The)142 112.2 Q F0($f)3.114 E F1 .614(macro is the\ + id of the sender as originally determined; when mailing to a speci\214c)3.114 +F .602(host the)117 124.2 R F0($g)3.102 E F1 .602 +(macro is set to the address of the sender)3.102 F/F2 10/Times-Italic@0 SF -.37 +(re)3.101 G .601(lative to the r).37 F(ecipient.)-.37 E F1 -.15(Fo)5.601 G +3.101(re).15 G .601(xample, if I)456.418 124.2 R 1.65 +(send to \231bollard@matisse.CS.Berk)117 136.2 R(ele)-.1 E -.65(y.)-.15 G 1.65 +(EDU\232 from the machine \231v).65 F(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.) +-.15 G(EDU\232).65 E(the)117 148.2 Q F0($f)2.5 E F1 +(macro will be \231eric\232 and the)2.5 E F0($g)2.5 E F1 +(macro will be \231eric@v)2.5 E(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G +(EDU.).65 E<9a>-.7 E(The)142 164.4 Q F0($x)3.838 E F1 1.338 +(macro is set to the full name of the sender)3.838 F 6.337(.T)-.55 G 1.337 +(his can be determined in se)369.135 164.4 R -.15(ve)-.25 G(ral).15 E -.1(wa) +117 176.4 S 2.952(ys. It).1 F .453(can be passed as \215ag to)2.953 F F2 +(sendmail.)2.953 E F1 .453(The second choice is the v)5.453 F .453 +(alue of the \231Full-name:\232)-.25 F .513(line in the header if it e)117 +188.4 R .512 +(xists, and the third choice is the comment \214eld of a \231From:\232 line.) +-.15 F .512(If all)5.512 F 1.148(of these f)117 200.4 R 1.148 +(ail, and if the message is being originated locally)-.1 F 3.648(,t)-.65 G +1.149(he full name is look)369.678 200.4 R 1.149(ed up in the)-.1 F F2 +(/etc/passwd)117 212.4 Q F1(\214le.)2.5 E .439(When sending, the)142 228.6 R F0 +($h)2.939 E F1(,)A F0($u)2.939 E F1 2.938(,a)C(nd)256.964 228.6 Q F0($z)2.938 E +F1 .438(macros get set to the host, user)2.938 F 2.938(,a)-.4 G .438 +(nd home directory \(if)417.426 228.6 R 1.454(local\) of the recipient.)117 +240.6 R 1.454(The \214rst tw)6.454 F 3.954(oa)-.1 G 1.454(re set from the) +278.438 240.6 R F0($@)3.955 E F1(and)3.955 E F0($:)3.955 E F1 1.455 +(part of the re)3.955 F 1.455(writing rules,)-.25 F(respecti)117 252.6 Q -.15 +(ve)-.25 G(ly).15 E(.)-.65 E(The)142 268.8 Q F0($p)2.806 E F1(and)2.806 E F0 +($t)2.806 E F1 .306(macros are used to create unique strings \(e.g., for the \ +\231Message-Id:\232 \214eld\).)2.806 F(The)117 280.8 Q F0($i)2.537 E F1 .037(m\ +acro is set to the queue id on this host; if put into the timestamp line it ca\ +n be e)2.537 F(xtremely)-.15 E .407(useful for tracking messages.)117 292.8 R +(The)5.407 E F0($v)2.907 E F1 .407(macro is set to be the v)2.907 F .407 +(ersion number of)-.15 F F2(sendmail)2.907 E F1 2.907(;t)C .407(his is)482.753 +292.8 R 2.109(normally put in timestamps and has been pro)117 304.8 R -.15(ve) +-.15 G 4.609(ne).15 G 2.109(xtremely useful for deb)334.512 304.8 R 4.61 +(ugging. The)-.2 F F0($w)4.61 E F1 1.222 +(macro is set to the name of this host if it can be determined.)117 316.8 R +(The)6.221 E F0($c)3.721 E F1 1.221(\214eld is set to the \231hop)3.721 F +(count,)117 328.8 Q 3.332<9a69>-.7 G .833 +(.e., the number of times this message has been processed.)151.572 328.8 R .833 +(This can be determined by)5.833 F(the)117 340.8 Q F0<ad68>2.5 E F1 +(\215ag on the command line or by counting the timestamps in the message.)2.5 E +(The)142 357 Q F0($r)3.427 E F1(and)3.427 E F0($s)3.427 E F1 .926 +(\214elds are set to the protocol used to communicate with sendmail and the) +3.427 F .968(sending hostname.)117 369 R(The)5.968 E F0($_)3.468 E F1 .969 +(is set to a v)3.469 F .969(alidated sender host name.)-.25 F .969 +(If the sender is running an)5.969 F(RFC 1413 compliant IDENT serv)117 381 Q +(er)-.15 E 2.5(,i)-.4 G 2.5(tw)267.55 381 S +(ill include the user name on that host.)280.05 381 Q F0 2.5(5.1.3. C)102 405 R +(and F \212 de\214ne classes)2.5 E F1 .197 +(Classes of phrases may be de\214ned to match on the left hand side of re)142 +421.2 R .196(writing rules, where)-.25 F 2.79<6199>117 433.2 S .291 +(phrase\232 is a sequence of characters that do not contain space characters.) +128.67 433.2 R -.15(Fo)5.291 G 2.791(re).15 G .291(xample a class)445.098 433.2 +R .356(of all local names for this site might be created so that attempts to s\ +end to oneself can be elimi-)117 445.2 R 2.89(nated. These)117 457.2 R .39(can\ + either be de\214ned directly in the con\214guration \214le or read in from an\ +other \214le.)2.89 F .797(Classes may be gi)117 469.2 R -.15(ve)-.25 G 3.297 +(nn).15 G .796(ames from the set of upper case letters.)213.668 469.2 R(Lo) +5.796 E .796(wer case letters and special)-.25 F(characters are reserv)117 +481.2 Q(ed for system use.)-.15 E(The syntax is:)142 497.4 Q F0(C)157 513.6 Q +F2 1.666(cp)C(hr)-1.666 E(ase1 phr)-.15 E(ase2...)-.15 E F0(F)157 525.6 Q F2 +1.666<638c>C(le)-1.666 E F1 1.114(The \214rst form de\214nes the class)117 +541.8 R F2(c)3.614 E F1 1.114(to match an)3.614 F 3.614(yo)-.15 G 3.615(ft) +319.63 541.8 S 1.115(he named w)329.355 541.8 R 3.615(ords. It)-.1 F 1.115 +(is permissible to split)3.615 F(them among multiple lines; for e)117 553.8 Q +(xample, the tw)-.15 E 2.5(of)-.1 G(orms:)317.57 553.8 Q(CHmonet ucbmonet)157 +570 Q(and)117 586.2 Q(CHmonet)157 602.4 Q(CHucbmonet)157 614.4 Q(are equi)117 +630.6 Q -.25(va)-.25 G 2.5(lent. The).25 F +(second form reads the elements of the class)2.5 E F2(c)2.5 E F1 +(from the named)2.5 E F2(\214le)2.5 E F1(.)A(The)142 646.8 Q F0($~)3.113 E F1 +.613(\(match entries not in class\) only matches a single w)3.113 F .612 +(ord; multi-w)-.1 F .612(ord entries in the)-.1 F +(class are ignored in this conte)117 658.8 Q(xt.)-.15 E .383(The class)142 675 +R F0($=w)2.883 E F1 .384(is set to be the set of all names this host is kno) +2.883 F .384(wn by)-.25 F 5.384(.T)-.65 G .384(his can be used to)431.364 675 R +(match local hostnames.)117 687 Q(The class)142 703.2 Q F0($=k)2.5 E F1 +(is set to be the same as)2.5 E F0($k)2.5 E F1 2.5(,t)C +(hat is, the UUCP node name.)312.69 703.2 Q EP +%%Page: 29 26 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-29)452.9 60 Q 2.5(5.1.4. M)102 96 R 2.5<8a64>2.5 G(e\214ne mailer)159.5 +96 Q/F1 10/Times-Roman@0 SF(Programs and interf)142 112.2 Q +(aces to mailers are de\214ned in this line.)-.1 E(The format is:)5 E F0(M)157 +128.4 Q/F2 10/Times-Italic@0 SF(name)A F1 2.5(,{)C F2(\214eld)197.9 128.4 Q F1 +(=)A F2(value)A F1(}*)1.666 E(where)117 144.6 Q F2(name)3.244 E F1 .744(is the\ + name of the mailer \(used internally only\) and the \231\214eld=name\232 pair\ +s de\214ne)3.244 F(attrib)117 156.6 Q(utes of the mailer)-.2 E 5(.F)-.55 G +(ields are:)220.13 156.6 Q -.15(Pa)157 172.8 S 51.87(th The).15 F +(pathname of the mailer)2.5 E 47.83(Flags Special)157 184.8 R +(\215ags for this mailer)2.5 E 41.73(Sender A)157 196.8 R(re)2.5 E +(writing set for sender addresses)-.25 E 31.17(Recipient A)157 208.8 R(re)2.5 E +(writing set for recipient addresses)-.25 E(Ar)157 220.8 Q 49.13(gv An)-.18 F +(ar)2.5 E(gument v)-.18 E(ector to pass to this mailer)-.15 E 55.61(Eol The)157 +232.8 R(end-of-line string for this mailer)2.5 E 35.62(Maxsize The)157 244.8 R +(maximum message length to this mailer)2.5 E 32.27(Linelimit The)157 256.8 R +(maximum line length in the message body)2.5 E 31.18(Directory The)157 268.8 R +-.1(wo)2.5 G(rking directory for the mailer).1 E +(Only the \214rst character of the \214eld name is check)117 285 Q(ed.)-.1 E +1.144(The follo)142 301.2 R 1.144 +(wing \215ags may be set in the mailer description.)-.25 F(An)6.144 E 3.644(yo) +-.15 G 1.144(ther \215ags may be used)409.994 301.2 R(freely to conditionally \ +assign headers to messages destined for particular mailers.)117 313.2 Q 15.56 +(aR)117 329.4 S(un Extended SMTP \(ESMTP\) protocol \(de\214ned in RFCs 1425, \ +1426, and 1427\).)143.67 329.4 Q 15(bF)117 345.6 S .674 +(orce a blank line on the end of a message.)142.41 345.6 R .674 +(This is intended to w)5.674 F .674(ork around some stupid)-.1 F -.15(ve)137 +357.6 S .851(rsions of /bin/mail that require a blank line, b).15 F .851 +(ut do not pro)-.2 F .852(vide it themselv)-.15 F 3.352(es. It)-.15 F -.1(wo) +3.352 G(uld).1 E(not normally be used on netw)137 369.6 Q(ork mail.)-.1 E 15.56 +(cD)117 385.8 S 4.166(on)144.22 385.8 S 1.666 +(ot include comments in addresses.)158.386 385.8 R 1.665 +(This should only be used if you ha)6.665 F 1.965 -.15(ve t)-.2 H 4.165(ow).15 +G(ork)490.67 385.8 Q(around a remote mailer that gets confused by comments.)137 +397.8 Q 13.33(CI)117 414 S 3.06(fm)140.33 414 S .56(ail is)154.5 414 R F2 -.37 +(re)3.06 G(ceived).37 E F1 .56(from a mailer with this \215ag set, an)3.06 F +3.06(ya)-.15 G .56(ddresses in the header that do not)367.33 414 R(ha)137 426 Q +.331 -.15(ve a)-.2 H 2.531(na).15 G 2.531(ts)174.472 426 S .031 +(ign \(\231@\232\) after being re)183.673 426 R .031 +(written by ruleset three will ha)-.25 F .33 -.15(ve t)-.2 H .03 +(he \231@domain\232 clause).15 F(from the sender tack)137 438 Q(ed on.)-.1 E +(This allo)5 E(ws mail with headers of the form:)-.25 E(From: usera@hosta)177 +454.2 Q -.8(To)177 466.2 S 2.5(:u).8 G(serb@hostb, userc)197.59 466.2 Q +(to be re)137 482.4 Q(written as:)-.25 E(From: usera@hosta)177 498.6 Q -.8(To) +177 510.6 S 2.5(:u).8 G(serb@hostb, userc@hosta)197.59 510.6 Q(automatically) +137 526.8 Q(.)-.65 E 12.78(DT)117 543 S(his mailer w)143.11 543 Q +(ants a \231Date:\232 header line.)-.1 E 15.56(eT)117 559.2 S .562 +(his mailer is e)143.11 559.2 R(xpensi)-.15 E .862 -.15(ve t)-.25 H 3.062(oc) +.15 G .562(onnect to, so try to a)253.97 559.2 R -.2(vo)-.2 G .562 +(id connecting normally; an).2 F 3.063(yn)-.15 G(ecessary)470.13 559.2 Q +(connection will occur during a queue run.)137 571.2 Q 13.89(EE)117 587.4 S +(scape lines be)143.11 587.4 Q +(ginning with \231From\232 in the message with a `>' sign.)-.15 E 16.67(fT)117 +603.6 S .969(he mailer w)143.11 603.6 R .969(ants a)-.1 F F0<ad66>3.469 E F2 +(fr)3.469 E(om)-.45 E F1 .969(\215ag, b)3.469 F .969(ut only if this is a netw) +-.2 F .969(ork forw)-.1 F .968(ard operation \(i.e., the)-.1 F(mailer will gi) +137 615.6 Q .3 -.15(ve a)-.25 H 2.5(ne).15 G(rror if the e)218.81 615.6 Q -.15 +(xe)-.15 G(cuting user does not ha).15 E .3 -.15(ve s)-.2 H +(pecial permissions\).).15 E 14.44(FT)117 631.8 S(his mailer w)143.11 631.8 Q +(ants a \231From:\232 header line.)-.1 E 15(gN)117 648 S(ormally)144.22 648 Q +(,)-.65 E F2(sendmail)3.529 E F1 1.029 +(sends internally generated email \(e.g., error messages\) using the null)3.529 +F .006(return address)137 662 R/F3 7/Times-Roman@0 SF(9)193.376 658 Q F1 .006 +(as required by RFC 1123.)199.382 662 R(Ho)5.006 E(we)-.25 E -.15(ve)-.25 G +.806 -.4(r, s).15 H .006(ome mailers don').4 F 2.505(ta)-.18 G .005 +(ccept a null return)431.505 662 R 5.372(address. If)137 674 R(necessary)5.372 +E 5.372(,y)-.65 G 2.872(ou can set the)240.486 674 R F0(g)5.372 E F1 2.872 +(\215ag to pre)5.372 F -.15(ve)-.25 G(nt).15 E F2(sendmail)5.372 E F1 2.873 +(from obe)5.372 F 2.873(ying the)-.15 F .32 LW 76 683.6 72 683.6 DL 80 683.6 76 +683.6 DL 84 683.6 80 683.6 DL 88 683.6 84 683.6 DL 92 683.6 88 683.6 DL 96 +683.6 92 683.6 DL 100 683.6 96 683.6 DL 104 683.6 100 683.6 DL 108 683.6 104 +683.6 DL 112 683.6 108 683.6 DL 116 683.6 112 683.6 DL 120 683.6 116 683.6 DL +124 683.6 120 683.6 DL 128 683.6 124 683.6 DL 132 683.6 128 683.6 DL 136 683.6 +132 683.6 DL 140 683.6 136 683.6 DL 144 683.6 140 683.6 DL 148 683.6 144 683.6 +DL 152 683.6 148 683.6 DL 156 683.6 152 683.6 DL 160 683.6 156 683.6 DL 164 +683.6 160 683.6 DL 168 683.6 164 683.6 DL 172 683.6 168 683.6 DL 176 683.6 172 +683.6 DL 180 683.6 176 683.6 DL 184 683.6 180 683.6 DL 188 683.6 184 683.6 DL +192 683.6 188 683.6 DL 196 683.6 192 683.6 DL 200 683.6 196 683.6 DL 204 683.6 +200 683.6 DL 208 683.6 204 683.6 DL 212 683.6 208 683.6 DL 216 683.6 212 683.6 +DL/F4 5/Times-Roman@0 SF(9)93.6 694 Q/F5 8/Times-Roman@0 SF(Actually)3.2 I 2 +(,t)-.52 G(his only applies to SMTP)129.356 697.2 Q 2(,w)-.888 G +(hich uses the `)219.588 697.2 Q(`MAIL FR)-.592 E(OM:<>')-.32 E 2('c)-.592 G +(ommand.)333.98 697.2 Q EP +%%Page: 30 27 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-30 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF .212 +(standards; error messages will be sent as from the MAILER-D)137 96 R .211 +(AEMON \(actually)-.4 F 2.711(,t)-.65 G .211(he v)470.439 96 R(alue)-.25 E +(of the)137 108 Q F0($n)2.5 E F1(macro\).)2.5 E 15(hU)117 124.2 S +(pper case should be preserv)144.22 124.2 Q(ed in host names for this mailer) +-.15 E(.)-.55 E 16.67(IT)117 140.4 S .092 +(his mailer will be speaking SMTP to another)143.11 140.4 R/F2 10 +/Times-Italic@0 SF(sendmail)2.592 E F1 2.593<8a61>2.593 G 2.593(ss)381.242 +140.4 S .093(uch it can use special proto-)391.615 140.4 R .319(col features.) +137 152.4 R .319(This option is not required \(i.e., if this option is omitted\ + the transmission will)5.319 F(still operate successfully)137 164.4 Q 2.5(,a) +-.65 G(lthough perhaps not as ef)244.11 164.4 Q(\214ciently as possible\).)-.25 +E 17.22(lT)117 180.6 S(his mailer is local \(i.e., \214nal deli)143.11 180.6 Q +-.15(ve)-.25 G(ry will be performed\).).15 E 13.89(LL)117 196.8 S .69 +(imit the line lengths as speci\214ed in RFC821.)143.11 196.8 R .69 +(This deprecated option should be replaced)5.69 F(by the)137 208.8 Q F0(L=)2.5 +E F1(mail declaration.)2.5 E -.15(Fo)5 G 2.5(rh).15 G(istoric reasons, the) +272.54 208.8 Q F0(L)2.5 E F1(\215ag also sets the)2.5 E F0(7)2.5 E F1(\215ag.) +2.5 E 12.22(mT)117 225 S 1.273 +(his mailer can send to multiple users on the same host in one transaction.) +143.11 225 R 1.273(When a)6.273 F F0($u)3.773 E F1 .621(macro occurs in the)137 +237 R F2(ar)3.121 E(gv)-.37 E F1 .621 +(part of the mailer de\214nition, that \214eld will be repeated as neces-)3.121 +F(sary for all qualifying users.)137 249 Q 11.11(MT)117 265.2 S(his mailer w) +143.11 265.2 Q(ants a \231Message-Id:\232 header line.)-.1 E 15(nD)117 281.4 S +2.5(on)144.22 281.4 S +(ot insert a UNIX-style \231From\232 line on the front of the message.)156.72 +281.4 Q 15(pU)117 297.6 S .702(se the route-addr style re)144.22 297.6 R -.15 +(ve)-.25 G .702(rse-path in the SMTP \231MAIL FR).15 F .701 +(OM:\232 command rather than)-.4 F .421 +(just the return address; although this is required in RFC821 section 3.1, man) +137 309.6 R 2.922(yh)-.15 G .422(osts do not)459.816 309.6 R(process re)137 +321.6 Q -.15(ve)-.25 G(rse-paths properly).15 E 5(.R)-.65 G -2.15 -.25(ev e) +272.3 321.6 T(rse-paths are of).25 E(\214cially discouraged by RFC 1123.)-.25 E +14.44(PT)117 337.8 S(his mailer w)143.11 337.8 Q(ants a \231Return-P)-.1 E +(ath:\232 line.)-.15 E 16.67(rS)117 354 S(ame as)142.56 354 Q F0(f)2.5 E F1 2.5 +(,b)C(ut sends a)185.68 354 Q F0<ad72>2.5 E F1(\215ag.)2.5 E 16.11(sS)117 370.2 +S(trip quote characters of)142.56 370.2 Q 2.5(fo)-.25 G 2.5(ft)245.61 370.2 S +(he address before calling the mailer)254.22 370.2 Q(.)-.55 E 14.44(SD)117 +386.4 S(on')144.22 386.4 Q 3.443(tr)-.18 G .943 +(eset the userid before calling the mailer)166.923 386.4 R 5.943(.T)-.55 G .943 +(his w)344.324 386.4 R .942(ould be used in a secure en)-.1 F(viron-)-.4 E .49 +(ment where)137 398.4 R F2(sendmail)2.99 E F1 .49(ran as root.)2.99 F .491 +(This could be used to a)5.491 F -.2(vo)-.2 G .491(id for).2 F .491 +(ged addresses.)-.18 F .491(This \215ag)5.491 F(is suppressed if gi)137 410.4 Q +-.15(ve)-.25 G 2.5(nf).15 G(rom an \231unsafe\232 en)228.81 410.4 Q +(vironment \(e.g, a user')-.4 E 2.5(sm)-.55 G(ail.cf \214le\).)410.31 410.4 Q +15(uU)117 426.6 S(pper case should be preserv)144.22 426.6 Q +(ed in user names for this mailer)-.15 E(.)-.55 E 12.78(UT)117 442.8 S 2.997 +(his mailer w)143.11 442.8 R 2.996 +(ants Unix-style \231From\232 lines with the ugly UUCP-style \231remote from) +-.1 F(<host>\232 on the end.)137 454.8 Q 15(xT)117 471 S(his mailer w)143.11 +471 Q(ants a \231Full-Name:\232 header line.)-.1 E 12.78(XT)117 487.2 S 1.22 +(his mailer w)143.11 487.2 R 1.22 +(ant to use the hidden dot algorithm as speci\214ed in RFC821; basically)-.1 F +3.72(,a)-.65 G -.15(ny)494.15 487.2 S .225(line be)137 499.2 R .225 +(ginning with a dot will ha)-.15 F .525 -.15(ve a)-.2 H 2.725(ne).15 G .224 +(xtra dot prepended \(to be stripped at the other end\).)296.47 499.2 R .525(T\ +his insures that lines in the message containing a dot will not terminate the \ +message pre-)137 511.2 R(maturely)137 523.2 Q(.)-.65 E 15(7S)117 539.4 S .152 +(trip all output to se)142.56 539.4 R -.15(ve)-.25 G 2.652(nb).15 G 2.652 +(its. This)240.42 539.4 R .152(is the def)2.652 F .152(ault if the)-.1 F F0(L) +2.652 E F1 .152(\215ag is set.)2.652 F .152(Note that setting this is)5.152 F +.078(not suf)137 551.4 R .079 +(\214cient to get full eight bit data passed through)-.25 F F2(sendmail)2.579 E +F1 5.079(.I)C 2.579(ft)398.437 551.4 S(he)407.126 551.4 Q F0(7)2.579 E F1 .079 +(option is set, this is)2.579 F(essentially al)137 563.4 Q -.1(wa)-.1 G +(ys set, since the eighth bit w).1 E(as stripped on input.)-.1 E 2.122(The mai\ +ler with the special name \231error\232 can be used to generate a user error) +142 579.6 R 7.122(.T)-.55 G(he)494.56 579.6 Q .246 +(\(optional\) host \214eld is an e)117 591.6 R .247 +(xit status to be returned, and the user \214eld is a message to be printed.) +-.15 F .337(The e)117 603.6 R .337(xit status may be numeric or one of the v) +-.15 F .336(alues USA)-.25 F .336(GE, NOUSER, NOHOST)-.4 F 2.836(,U)-.74 G -.35 +(NA)465.4 603.6 S -1.35(VA)-1 G(IL-)1.35 E .828(ABLE, SOFTW)117 615.6 R .828 +(ARE, TEMPF)-1.2 F .828(AIL, PR)-.74 F -1.88 -.4(OT O)-.4 H .828 +(COL, or CONFIG to return the corresponding EX_).4 F -.15(ex)117 627.6 S +(it code.).15 E -.15(Fo)5 G 2.5(re).15 G(xample, the entry:)181.26 627.6 Q +($#error $@ NOHOST $: Host unkno)157 643.8 Q(wn in this domain)-.25 E .261(on \ +the RHS of a rule will cause the speci\214ed error to be generated and the \ +\231Host unkno)117 660 R .261(wn\232 e)-.25 F(xit)-.15 E +(status to be returned if the LHS matches.)117 672 Q +(This mailer is only functional in ruleset zero.)5 E 1.563 +(The mailer named \231local\232)142 688.2 R F2(must)4.063 E F1 1.564 +(be de\214ned in e)4.063 F -.15(ve)-.25 G 1.564(ry con\214guration \214le.).15 +F 1.564(This is used to)6.564 F(deli)117 700.2 Q -.15(ve)-.25 G 4.039(rl).15 G +1.539(ocal mail, and is treated specially in se)151.189 700.2 R -.15(ve)-.25 G +1.538(ral w).15 F 4.038(ays. Additionally)-.1 F 4.038(,t)-.65 G 1.538 +(hree other mailers)428.724 700.2 R 1.367(named \231prog\232, \231*\214le*\232\ +, and \231*include*\232 may be de\214ned to tune the deli)117 712.2 R -.15(ve) +-.25 G 1.368(ry of messages to).15 F +(programs, \214les, and :include: lists respecti)117 724.2 Q -.15(ve)-.25 G(ly) +.15 E 5(.T)-.65 G(he)315.38 724.2 Q 2.5(yd)-.15 G(ef)337.17 724.2 Q(ault to:) +-.1 E EP +%%Page: 31 28 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-31)452.9 60 Q/F1 10/Times-Roman@0 SF +(Mprog, P=/bin/sh, F=lsD, A=sh \255c $u)157 96 Q(M*\214le*, P=/de)157 108 Q +(v/null, F=lsDFMPEu, A=FILE)-.25 E(M*include*, P=/de)157 120 Q +(v/null, F=su, A=INCLUDE)-.25 E 1.264(The Sender and Recipient re)142 140.4 R +1.263(writing sets may either be a simple inte)-.25 F 1.263(ger or may be tw) +-.15 F(o)-.1 E(inte)117 152.4 Q .046 +(gers separated by a slash; if so, the \214rst re)-.15 F .047 +(writing set is applied to en)-.25 F -.15(ve)-.4 G .047(lope addresses and the) +.15 F(second is applied to headers.)117 164.4 Q 1.259 +(The Directory is actually a colon-separated path of directories to try)142 +180.6 R 6.258(.F)-.65 G 1.258(or e)439.704 180.6 R 1.258(xample, the)-.15 F +.143(de\214nition \231D=$z:/\232 \214rst tries to e)117 192.6 R -.15(xe)-.15 G +.143(cute in the recipient').15 F 2.643(sh)-.55 G .144 +(ome directory; if that is not a)353.327 192.6 R -.25(va)-.2 G(ilable,).25 E +.781(it tries to e)117 204.6 R -.15(xe)-.15 G .781 +(cute in the root of the \214lesystem.).15 F .78 +(This is intended to be used only on the \231prog\232)5.781 F(mailer)117 216.6 +Q 2.898(,s)-.4 G .398(ince some shells \(such as)151.438 216.6 R/F2 10 +/Times-Italic@0 SF(csh)2.898 E F1 2.898(\)r)C .398(efuse to e)279.356 216.6 R +-.15(xe)-.15 G .398(cute if the).15 F 2.898(yc)-.15 G .398 +(annot read the home directory)380.586 216.6 R(.)-.65 E .416 +(Since the queue directory is not normally readable by normal users)117 228.6 R +F2(csh)2.916 E F1 .416(scripts as recipients can)2.916 F -.1(fa)117 240.6 S +(il.).1 E F0 2.5(5.1.5. H)102 264.6 R 2.5<8a64>2.5 G(e\214ne header)157.84 +264.6 Q F1 .198(The format of the header lines that sendmail inserts into the \ +message are de\214ned by the)142 280.8 R F0(H)2.699 E F1 2.5(line. The)117 +292.8 R(syntax of this line is:)2.5 E F0(H)157 309 Q F1([)A F0(?)A F2(m\215a)A +(gs)-.1 E F0(?)A F1(])A F2(hname)A F0(:)A F2(htemplate)2.5 E F1 .691(Continuat\ +ion lines in this spec are re\215ected directly into the outgoing message.)117 +325.2 R(The)5.69 E F2(htemplate)3.19 E F1 1.566(is macro e)117 337.2 R 1.567 +(xpanded before insertion into the message.)-.15 F 1.567(If the)6.567 F F2 +(m\215a)4.067 E(gs)-.1 E F1 1.567(\(surrounded by question)4.067 F .219(marks\ +\) are speci\214ed, at least one of the speci\214ed \215ags must be stated in \ +the mailer de\214nition for)117 349.2 R .093 +(this header to be automatically output.)117 361.2 R .093 +(If one of these headers is in the input it is re\215ected to the)5.093 F +(output re)117 373.2 Q -.05(ga)-.15 G(rdless of these \215ags.).05 E +(Some headers ha)142 389.4 Q .3 -.15(ve s)-.2 H +(pecial semantics that will be described belo).15 E -.65(w.)-.25 G F0 2.5 +(5.1.6. O)102 413.4 R 2.5<8a73>2.5 G(et option)156.17 413.4 Q F1 .045(There ar\ +e a number of \231random\232 options that can be set from a con\214guration \ +\214le.)142 429.6 R(Options)5.045 E(are represented by single characters.)117 +441.6 Q(The syntax of this line is:)5 E F0(O)157 457.8 Q F2 1.666(ov)C(alue) +-1.666 E F1 1.054(This sets option)117 474 R F2(o)3.554 E F1 1.054(to be)3.554 +F F2(value)3.554 E F1 6.054(.D)C 1.054(epending on the option,)256.318 474 R F2 +(value)3.555 E F1 1.055(may be a string, an inte)3.555 F(ger)-.15 E 3.555(,a) +-.4 G(boolean \(with le)117 486 Q -.05(ga)-.15 G 2.5(lv).05 G +(alues \231t\232, \231T\232, \231f\232, or \231F\232; the def)201.26 486 Q +(ault is TR)-.1 E(UE\), or a time interv)-.4 E(al.)-.25 E +(The options supported are:)142 502.2 Q(a)117 518.4 Q F2(N)A F1 .655(If set, w) +189 518.4 R .655(ait up to)-.1 F F2(N)3.155 E F1 .655 +(minutes for an \231@:@\232 entry to e)3.155 F .655(xist in the alias database) +-.15 F .474(before starting up.)189 530.4 R .474(If it does not appear in)5.474 +F F2(N)2.974 E F1 .475(minutes, reb)2.974 F .475(uild the database \(if)-.2 F +(the)189 542.4 Q F0(D)2.5 E F1(option is also set\) or issue a w)2.5 E(arning.) +-.1 E(A)117 558.6 Q F2 .507(spec, spec, ...)B F1 .507 +(Specify possible alias \214le\(s\).)190.014 558.6 R(Each)5.507 E F2(spec)3.006 +E F1 .506(should be in the format `)3.006 F(`)-.74 E F2(class)A F0(:)A F2 +(\214le)3.006 E F1 -.74('')C(where)189 570.6 Q F2(class)2.947 E F0(:)A F1 .447 +(is optional and def)2.947 F .447(aults to `)-.1 F(`implicit')-.74 E 2.947 +('. Depending)-.74 F .448(on ho)2.948 F(w)-.25 E F0(send-)2.948 E(mail)189 +582.6 Q F1 1.224(is compiled, v)3.724 F 1.224 +(alid classes are \231implicit\232 \(search through a compiled-in)-.25 F .193 +(list of alias \214le types, for back compatibility\), \231hash\232 \(if)189 +594.6 R/F3 9/Times-Roman@0 SF(NEWDB)2.693 E F1 .193(is speci\214ed\),)2.693 F +.882(\231dbm\232 \(if)189 606.6 R F3(NDBM)3.382 E F1 .882 +(is speci\214ed\), \231stab\232 \(internal symbol table \212 not normally)3.382 +F .475(used unless you ha)189 618.6 R .775 -.15(ve n)-.2 H 2.975(oo).15 G .476 +(ther database lookup\), or \231nis\232 \(if)295.735 618.6 R F3(NIS)2.976 E F1 +.476(is speci\214ed\).)2.976 F(If a list of)189 630.6 Q F2(spec)2.5 E F1 2.5 +(sa)C(re pro)259.26 630.6 Q(vided,)-.15 E F2(sendmail)2.5 E F1 +(searches them in order)2.5 E(.)-.55 E(b)117 646.8 Q F2(N)A F1(/)A F2(M)A F1 +1.589(Insist on at least)189 646.8 R F2(N)4.089 E F1 1.588 +(blocks free on the \214lesystem that holds the queue \214les)4.089 F .19 +(before accepting email via SMTP)189 658.8 R 5.19(.I)-1.11 G 2.69(ft)334.09 +658.8 S .19(here is insuf)342.89 658.8 R .19(\214cient space)-.25 F F2 +(sendmail)2.69 E F1(gi)2.69 E -.15(ve)-.25 G(s).15 E 3.67(a4)189 670.8 S 1.17 +(52 response to the MAIL command.)202.11 670.8 R 1.17(This in)6.17 F 1.17 +(vites the sender to try ag)-.4 F(ain)-.05 E(later)189 682.8 Q 5.986(.T)-.55 G +.986(he optional)220.816 682.8 R F2(M)3.486 E F1 .987 +(is a maximum message size adv)3.486 F .987(ertised in the ESMTP)-.15 F +(EHLO response.)189 694.8 Q(It is currently otherwise unused.)5 E(B)117 711 Q +F2(c)A F1 1.445(Set the blank substitution character to)189 711 R F2(c)3.945 E +F1 6.444(.U)C 1.444(nquoted spaces in addresses are)371.594 711 R +(replaced by this character)189 723 Q 5(.D)-.55 G(ef)305.63 723 Q +(aults to space \(i.e., no change is made\).)-.1 E EP +%%Page: 32 29 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-32 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF 67.56(cI)117 96 +S 3.892(fa)192.33 96 S 3.892(no)203.992 96 S 1.393(utgoing mailer is mark) +217.884 96 R 1.393(ed as being e)-.1 F(xpensi)-.15 E -.15(ve)-.25 G 3.893(,d) +.15 G(on')415.294 96 Q 3.893(tc)-.18 G 1.393(onnect immedi-)439.557 96 R(ately) +189 108 Q 6.164(.T)-.65 G 1.164 +(his requires that queueing be compiled in, since it will depend on a)222.564 +108 R(queue run process to actually send the mail.)189 120 Q(C)117 136.2 Q/F2 +10/Times-Italic@0 SF(N)A F1 1.49(Checkpoints the queue e)189 136.2 R -.15(ve) +-.25 G(ry).15 E F2(N)3.99 E F1(\(def)3.99 E 1.49(ault 10\) addresses sent.)-.1 +F 1.49(If your system)6.49 F .785(crashes during deli)189 148.2 R -.15(ve)-.25 +G .785(ry to a lar).15 F .785(ge list, this pre)-.18 F -.15(ve)-.25 G .785 +(nts retransmission to an).15 F 3.285(yb)-.15 G(ut)496.22 148.2 Q +(the last recipients.)189 160.2 Q(d)117 176.4 Q F2(x)A F1(Deli)189 176.4 Q -.15 +(ve)-.25 G 2.5(ri).15 G 2.5(nm)223.87 176.4 S(ode)239.15 176.4 Q F2(x)2.5 E F1 +5(.L)C -2.25 -.15(eg a)274.14 176.4 T 2.5(lm).15 G(odes are:)300.88 176.4 Q +17.22(iD)229 192.6 S(eli)256.22 192.6 Q -.15(ve)-.25 G 2.5(ri).15 G(nteracti) +283.87 192.6 Q -.15(ve)-.25 G(ly \(synchronously\)).15 E 15(bD)229 204.6 S(eli) +256.22 204.6 Q -.15(ve)-.25 G 2.5(ri).15 G 2.5(nb)283.87 204.6 S +(ackground \(asynchronously\))296.37 204.6 Q 15(qJ)229 216.6 S +(ust queue the message \(deli)252.89 216.6 Q -.15(ve)-.25 G 2.5(rd).15 G +(uring queue run\))382.74 216.6 Q(Def)189 232.8 Q 1.32(aults to `)-.1 F(`b') +-.74 E 3.82('i)-.74 G 3.82(fn)261.64 232.8 S 3.82(oo)273.79 232.8 S 1.32 +(ption is speci\214ed, `)287.61 232.8 R(`i')-.74 E 3.82('i)-.74 G 3.82(fi) +385.57 232.8 S 3.82(ti)395.5 232.8 S 3.82(ss)404.88 232.8 S 1.32(peci\214ed b) +416.48 232.8 R 1.32(ut gi)-.2 F -.15(ve)-.25 G 3.82(nn).15 G(o)499 232.8 Q(ar) +189 244.8 Q(gument \(i.e., `)-.18 E(`Od')-.74 E 2.5('i)-.74 G 2.5(se)278.98 +244.8 S(qui)289.81 244.8 Q -.25(va)-.25 G(lent to `).25 E(`Odi')-.74 E('\).) +-.74 E 64.78(DI)117 261 S 2.736(fs)192.33 261 S .236(et, reb)202.286 261 R .236 +(uild the alias database if necessary and possible.)-.2 F .235 +(If this option is not)5.236 F(set,)189 273 Q F2(sendmail)3.385 E F1 .885 +(will ne)3.385 F -.15(ve)-.25 G 3.385(rr).15 G(eb)292.96 273 Q .885 +(uild the alias database unless e)-.2 F .885(xplicitly requested)-.15 F(using) +189 285 Q F0(\255bi)2.5 E F1(.)A(e)117 301.2 Q F2(x)A F1 +(Dispose of errors using mode)189 301.2 Q F2(x)2.5 E F1 5(.T)C(he v)327.31 +301.2 Q(alues for)-.25 E F2(x)2.5 E F1(are:)2.5 E 15(pP)229 317.4 S +(rint error messages \(def)254.56 317.4 Q(ault\))-.1 E 15(qN)229 329.4 S 2.5 +(om)256.22 329.4 S(essages, just gi)271.5 329.4 Q .3 -.15(ve ex)-.25 H +(it status).15 E 12.22(mM)229 341.4 S(ail back errors)257.89 341.4 Q 12.78(wW) +229 353.4 S(rite back errors \(mail if user not logged in\))258.44 353.4 Q +15.56(eM)229 365.4 S(ail back errors and gi)257.89 365.4 Q .3 -.15(ve z)-.25 H +(ero e).15 E(xit stat al)-.15 E -.1(wa)-.1 G(ys).1 E(E)117 385.8 Q F2 +(\214le/messa)A -.1(ge)-.1 G F1 .549 +(Prepend error messages with the indicated message.)189 385.8 R .549(If it be) +5.549 F .549(gins with a slash,)-.15 F .107(it is assumed to be the pathname o\ +f a \214le containing a message \(this is the rec-)189 397.8 R 1.317 +(ommended setting\).)189 409.8 R 1.316(Otherwise, it is a literal message.) +6.317 F 1.316(The error \214le might)6.316 F .99 +(contain the name, email address, and/or phone number of a local postmaster)189 +421.8 R .429(who could pro)189 433.8 R .429(vide assistance in to end users.) +-.15 F .428(If the option is missing or null,)5.429 F .342 +(or if it names a \214le which does not e)189 445.8 R .342 +(xist or which is not readable, no message)-.15 F(is printed.)189 457.8 Q 68.67 +(fS)117 474 S -2.25 -.2(av e)194.56 474 T 2.399 +(Unix-style \231From\232 lines at the front of headers.)5.1 F 2.399 +(Normally the)7.399 F 4.899(ya)-.15 G(re)496.23 474 Q +(assumed redundant and discarded.)189 486 Q(F)117 502.2 Q F2(mode)A F1 +(The \214le mode for queue \214les.)189 502.2 Q(g)117 518.4 Q F2(n)A F1 +(Set the def)189 518.4 Q(ault group id for mailers to run in to)-.1 E F2(n)2.5 +E F1 5(.D)C(ef)397.77 518.4 Q(aults to 1.)-.1 E 64.78(GA)117 534.6 S(llo)196.22 +534.6 Q 3.491(wf)-.25 G .991(uzzy matching on the GECOS \214eld.)220.571 534.6 +R .992(If this \215ag is set, and the usual)5.991 F .794(user name lookups f) +189 546.6 R .793(ail \(that is, there is no alias with this name and a)-.1 F F2 +-.1(ge)3.293 G(tpw-).1 E(nam)189 558.6 Q F1 -.1(fa)3.701 G 1.201 +(ils\), sequentially search the passw).1 F 1.202 +(ord \214le for a matching entry in the)-.1 F 1.446(GECOS \214eld.)189 570.6 R +1.446(This also requires that MA)6.446 F 1.446(TCHGECOS be turned on during) +-1.11 F 2.5(compilation. This)189 582.6 R(option is not recommended.)2.5 E(h) +117 598.8 Q F2(N)A F1 1.273(The maximum hop count.)189 598.8 R 1.274 +(Messages that ha)6.273 F 1.574 -.15(ve b)-.2 H 1.274(een processed more than) +.15 F F2(N)3.774 E F1(times are assumed to be in a loop and are rejected.)189 +610.8 Q(Def)5 E(aults to 25.)-.1 E(H)117 627 Q F2(\214le)A F1 +(Specify the help \214le for SMTP)189 627 Q(.)-1.11 E 69.22(iI)117 643.2 S +1.015(gnore dots in incoming messages.)192.33 643.2 R 1.014(This is al)6.014 F +-.1(wa)-.1 G 1.014(ys disabled \(that is, dots are).1 F(al)189 655.2 Q -.1(wa) +-.1 G(ys accepted\) when reading SMTP mail.).1 E 68.67(II)117 671.4 S .619 +(nsist that the BIND name serv)192.33 671.4 R .619(er be running to resolv)-.15 +F 3.119(eh)-.15 G .62(ost names.)421.52 671.4 R .62(If this is)5.62 F .945 +(not set and the name serv)189 683.4 R .945(er is not running, the)-.15 F F2 +(/etc/hosts)3.445 E F1 .945(\214le will be consid-)3.445 F .187(ered complete.) +189 695.4 R .188(In general, you do w)5.187 F .188 +(ant to set this option if your)-.1 F F2(/etc/hosts)2.688 E F1(\214le)2.688 E +.412(does not include all hosts kno)189 707.4 R .412 +(wn to you or if you are using the MX \(mail for)-.25 F(-)-.2 E -.1(wa)189 +719.4 S 2.03(rding\) feature of the BIND name serv).1 F(er)-.15 E 7.03(.T)-.55 +G 2.03(he name serv)385.96 719.4 R 2.03(er will still be)-.15 F EP +%%Page: 33 30 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-33)452.9 60 Q/F1 10/Times-Roman@0 SF .56(consulted e)189 96 R -.15(ve) +-.25 G 3.06(ni).15 G 3.06(ft)254.71 96 S .56(his option is not set, b)263.88 96 +R(ut)-.2 E/F2 10/Times-Italic@0 SF(sendmail)3.06 E F1 .56 +(will feel free to resort to)3.06 F(reading)189 108 Q F2(/etc/hosts)3.052 E F1 +.552(if the name serv)3.052 F .553(er is not a)-.15 F -.25(va)-.2 G 3.053 +(ilable. Thus,).25 F .553(you should)3.053 F F2(ne)3.053 E(ver)-.15 E F1 +(set this option if you do not run the name serv)189 120 Q(er)-.15 E(.)-.55 E +69.22(jI)117 136.2 S 3.129(fs)192.33 136.2 S .628 +(et, send error messages in MIME format \(see RFC1341 and RFC1344 for)202.679 +136.2 R(details\).)189 148.2 Q(J)117 164.4 Q F2(path)A F1 4.923 +(Set the path for searching for users' .forw)189 164.4 R 4.923(ard \214les.)-.1 +F 4.923(The def)9.923 F 4.923(ault is)-.1 F(\231$z/.forw)189 176.4 Q 2.869 +(ard\232. Some)-.1 F .368 +(sites that use the automounter may prefer to change this)2.869 F .676 +(to \231/v)189 188.4 R(ar/forw)-.25 E .676 +(ard/$u\232 to search a \214le with the same name as the user in a sys-)-.1 F +.925(tem directory)189 200.4 R 5.925(.I)-.65 G 3.425(tc)254.63 200.4 S .924 +(an also be set to a sequence of paths separated by colons;)265.275 200.4 R F2 +(sendmail)189 212.4 Q F1 .645 +(stops at the \214rst \214le it can successfully and safely open.)3.145 F -.15 +(Fo)5.646 G 3.146(re).15 G(xam-)483.45 212.4 Q 1.535(ple, \231/v)189 224.4 R +(ar/forw)-.25 E(ard/$u:$z/.forw)-.1 E 1.535(ard\232 will search \214rst in /v) +-.1 F(ar/forw)-.25 E(ard/)-.1 E F2(username)A F1(and then in)189 236.4 Q F2 +(~username)2.5 E F1(/.forw)A(ard \(b)-.1 E +(ut only if the \214rst \214le does not e)-.2 E(xist\).)-.15 E(k)117 252.6 Q F2 +(N)A F1 .196 +(The maximum number of open connections that will be cached at a time.)189 +252.6 R(The)5.197 E(def)189 264.6 Q .567(ault is one.)-.1 F .567 +(This delays closing the the current connection until either this)5.567 F(in) +189 276.6 Q -.2(vo)-.4 G .516 +(cation of sendmail needs to connect to another host or it terminates.).2 F +(Set-)5.516 E 1.959(ting it to zero def)189 288.6 R 1.959 +(aults to the old beha)-.1 F(vior)-.2 E 4.458(,t)-.4 G 1.958 +(hat is, connections are closed)379.248 288.6 R(immediately)189 300.6 Q(.)-.65 +E(K)117 316.8 Q F2(timeout)A F1 .882 +(The maximum amount of time a cached connection will be permitted to idle)189 +316.8 R 2.746(without acti)189 328.8 R(vity)-.25 E 7.746(.I)-.65 G 5.246(ft) +267.482 328.8 S 2.746(his time is e)278.838 328.8 R 2.746 +(xceeded, the connection is immediately)-.15 F 4.422(closed. This)189 340.8 R +-.25(va)4.422 G 1.923(lue should be small \(on the order of ten minutes\).).25 +F(Before)6.923 E F0(sendmail)189 352.8 Q F1 1.084 +(uses a cached connection, it al)3.584 F -.1(wa)-.1 G 1.083 +(ys sends a NOOP \(no operation\)).1 F 2.058 +(command to check the connection; if this f)189 364.8 R 2.058 +(ails, it reopens the connection.)-.1 F .478(This k)189 376.8 R .478 +(eeps your end from f)-.1 F .478(ailing if the other end times out.)-.1 F .478 +(The point of this)5.478 F 3.099(option is to be a good netw)189 388.8 R 3.099 +(ork neighbor and a)-.1 F -.2(vo)-.2 G 3.1(id using up e).2 F(xcessi)-.15 E +-.15(ve)-.25 G(resources on the other end.)189 400.8 Q(The def)5 E +(ault is \214v)-.1 E 2.5(em)-.15 G(inutes.)383.99 400.8 Q 69.22(lI)117 417 S +3.14(ft)192.33 417 S .64(here is an \231Errors-T)201.58 417 R .64 +(o:\232 header)-.8 F 3.14(,s)-.4 G .64 +(end error messages to the addresses listed)333.53 417 R 3.95(there. The)189 +429 R 3.95(yn)-.15 G 1.451(ormally go to the en)247.29 429 R -.15(ve)-.4 G +1.451(lope sender).15 F 6.451(.U)-.55 G 1.451(se of this option causes)405.426 +429 R(sendmail to violate RFC 1123.)189 441 Q(L)117 457.2 Q F2(n)A F1 +(Set the def)189 457.2 Q(ault log le)-.1 E -.15(ve)-.25 G 2.5(lt).15 G(o)288.77 +457.2 Q F2(n)2.5 E F1 5(.D)C(ef)315.99 457.2 Q(aults to 9.)-.1 E 64.22(mS)117 +473.4 S(end to me too, e)194.56 473.4 Q -.15(ve)-.25 G 2.5(ni).15 G 2.5(fIa) +278.04 473.4 S 2.5(mi)294.14 473.4 S 2.5(na)307.2 473.4 S 2.5(na)319.14 473.4 S +(lias e)331.08 473.4 Q(xpansion.)-.15 E(M)117 489.6 Q F2 1.666(xv)C(alue)-1.666 +E F1 1.313(Set the macro)189 489.6 R F2(x)3.812 E F1(to)3.812 E F2(value)3.812 +E F1 6.312(.T)C 1.312(his is intended only for use from the command)306.854 +489.6 R(line.)189 501.6 Q 67(nV)117 517.8 S +(alidate the RHS of aliases when reb)195.11 517.8 Q +(uilding the alias database.)-.2 E 67(oA)117 534 S 1.786 +(ssume that the headers may be in old format, i.e., spaces delimit names.) +196.22 534 R .433(This actually turns on an adapti)189 546 R .733 -.15(ve a) +-.25 H .433(lgorithm: if an).15 F 2.932(yr)-.15 G .432 +(ecipient address contains)403.156 546 R 5.089(ac)189 558 S 2.589 +(omma, parenthesis, or angle brack)202.969 558 R 2.589 +(et, it will be assumed that commas)-.1 F .485(already e)189 570 R 2.985 +(xist. If)-.15 F .485(this \215ag is not on, only commas delimit names.)2.985 F +.484(Headers are)5.484 F(al)189 582 Q -.1(wa)-.1 G +(ys output with commas between the names.).1 E(O)117 598.2 Q F2(options)A F1 +(Set serv)189 598.2 Q(er SMTP options.)-.15 E(The options are)5 E F2 -.1(ke)2.5 +G(y=value)-.2 E F1 2.5(pairs. Kno)2.5 F(wn k)-.25 E -.15(ey)-.1 G 2.5(sa).15 G +(re:)488.82 598.2 Q 52.83(Port Name/number)229 614.4 R(of listening port \(def) +2.5 E(aults to "smtp"\))-.1 E 48.95(Addr Address)229 626.4 R(mask \(def)2.5 E +(aults IN)-.1 E(ADDR_ANY\))-.35 E -.15(Fa)229 638.4 S 41.31(mily Address).15 F +-.1(fa)2.5 G(mily \(def).1 E(aults to INET\))-.1 E 44.5(Listen Size)229 650.4 R +(of listen queue \(def)2.5 E(aults to 10\))-.1 E(The)189 666.6 Q F2(Addr)4.113 +E F1 1.614(ess mask may be a numeric address in dot notation or a netw)B(ork) +-.1 E(name.)189 678.6 Q(p)117 694.8 Q F2(opt,opt,...)1.666 E F1 1.221 +(Set the pri)189 694.8 R -.25(va)-.25 G -.15(cy).25 G F2(opt)3.871 E F1 3.721 +(ions. `)B(`Pri)-.74 E -.25(va)-.25 G -.15(cy).25 G 2.701 -.74('' i).15 H 3.721 +(sr).74 G 1.221(eally a misnomer; man)351.856 694.8 R 3.721(yo)-.15 G 3.72(ft) +460.47 694.8 S 1.22(hese are)470.3 694.8 R 2.418(just a w)189 706.8 R 2.418 +(ay of insisting on stricter adherence to the SMTP protocol.)-.1 F(The)7.419 E +F2(opt)189 718.8 Q F1(ions can be selected from:)A EP +%%Page: 34 31 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-34 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF 40.26 +(public Allo)229 96 R 2.5(wo)-.25 G(pen access)329.01 96 Q 11.38 +(needmailhelo Insist)229 108 R(on HELO or EHLO command before MAIL)2.5 E(neede) +229 120 Q 9.87(xpnhelo Insist)-.15 F(on HELO or EHLO command before EXPN)2.5 E +(noe)229 132 Q 35.97(xpn Disallo)-.15 F 2.5(wE)-.25 G(XPN entirely)341.23 132 Q +12.5(needvrfyhelo Insist)229 144 R(on HELO or EHLO command before VRFY)2.5 E +(no)229 156 Q 38.75(vrfy Disallo)-.15 F 2.5(wV)-.25 G(RFY entirely)342.34 156 Q +14.71(restrictmailq Restrict)229 168 R(mailq command)2.5 E(goa)229 180 Q -.1 +(wa)-.15 G 36.91(yD).1 G(isallo)303.98 180 Q 2.5(we)-.25 G +(ssentially all SMTP status queries)339.56 180 Q 1.768(The \231goa)189 196.2 R +-.1(wa)-.15 G 1.768(y\232 pseudo-\215ag sets all \215ags e).1 F 1.768 +(xcept \231restrictmailq\232.)-.15 F 1.768(If mailq is)6.768 F .688(restricted\ +, only people in the same group as the queue directory can print the)189 208.2 +R(queue.)189 220.2 Q(P)117 236.4 Q/F2 10/Times-Italic@0 SF(postmaster)A F1 +1.115(If set, copies of error messages will be sent to the named)189 236.4 R F2 +(postmaster)3.614 E F1 6.114(.O)C(nly)491.22 236.4 Q .397(the header of the f) +189 248.4 R .398(ailed message is sent.)-.1 F .398 +(Since most errors are user problems,)5.398 F .564 +(this is probably not a good idea on lar)189 260.4 R .563(ge sites, and ar)-.18 +F .563(guably contains all sorts)-.18 F .05(of pri)189 272.4 R -.25(va)-.25 G +.35 -.15(cy v).25 H .05(iolations, b).15 F .05 +(ut it seems to be popular with certain operating systems)-.2 F -.15(ve)189 +284.4 S(ndors.).15 E(q)117 300.6 Q F2(factor)A F1(Use)189 300.6 Q F2(factor) +3.098 E F1 .597 +(as the multiplier in the map function to decide when to just queue)3.098 F +.425(up jobs rather than run them.)189 312.6 R .425(This v)5.425 F .425 +(alue is di)-.25 F .426(vided by the dif)-.25 F .426(ference between)-.25 F +1.064(the current load a)189 324.6 R -.15(ve)-.2 G 1.064(rage and the load a) +.15 F -.15(ve)-.2 G 1.064(rage limit \().15 F F0(x)A F1 1.063 +(\215ag\) to determine the)3.564 F(maximum message priority that will be sent.) +189 336.6 Q(Def)5 E(aults to 600000.)-.1 E(Q)117 352.8 Q F2(dir)A F1 +(Use the named)189 352.8 Q F2(dir)2.5 E F1(as the queue directory)2.5 E(.)-.65 +E(r)117 369 Q F2(timeouts)1.666 E F1 -.35(Ti)189 369 S 3.938(meout reads after) +.35 F F2(time)6.438 E F1(interv)6.438 E 6.438(al. The)-.25 F F2(timeouts)6.438 +E F1(ar)6.438 E 3.938(gument is a list of)-.18 F F2 -.1(ke)189 381 S(ywor)-.2 E +(d=value)-.37 E F1 3.61(pairs. The)3.61 F 1.11 +(recognized timeouts and their def)3.61 F 1.11(ault v)-.1 F 1.11(alues, and) +-.25 F(their minimum v)189 393 Q +(alues speci\214ed in RFC 1123 section 5.3.2 are:)-.25 E 23.6(initial w)229 +409.2 R(ait for initial greeting message [5m, 5m])-.1 E 29.72(helo reply)229 +421.2 R(to HELO or EHLO command [5m, none])2.5 E 29.16(mail reply)229 433.2 R +(to MAIL command [10m, 5m])2.5 E 31.39(rcpt reply)229 445.2 R +(to RCPT command [1h, 5m])2.5 E 16.94(datainit reply)229 457.2 R(to D)2.5 E +-1.21 -1.11(AT A)-.4 H(command [5m, 2m])3.61 E 8.06(datablock data)229 469.2 R +(block read [1h, 3m])2.5 E 12.5(data\214nal reply)229 481.2 R(to \214nal `)2.5 +E(`.)-.74 E 1.48 -.74('' i)-.7 H 2.5(nd).74 G(ata [1h, 10m])363.47 481.2 Q 32.5 +(rset reply)229 493.2 R(to RSET command [5m, none])2.5 E 31.38(quit reply)229 +505.2 R(to Q)2.5 E(UIT command [2m, none])-.1 E 28.05(misc reply)229 517.2 R +(to NOOP and VERB commands [2m, none])2.5 E 7.5(command command)229 529.2 R +(read [1h, 5m])2.5 E .798(All b)189 545.4 R .798 +(ut \231command\232 apply to client SMTP)-.2 F 5.798(.F)-1.11 G .798 +(or back compatibility)373.406 545.4 R 3.299(,at)-.65 G(imeout)476.22 545.4 Q +(with no `)189 557.4 Q(`k)-.74 E -.15(ey)-.1 G -.1(wo).15 G(rd=').1 E 2.5('p) +-.74 G(art will set all of the longer v)281.4 557.4 Q(alues.)-.25 E 65.33(RN) +117 573.6 S(ormally)196.22 573.6 Q(,)-.65 E F2(sendmail)4.154 E F1 1.653 +(tries to eliminate an)4.154 F 4.153(yu)-.15 G 1.653(nnecessary e)371.721 573.6 +R 1.653(xplicit routes when)-.15 F .931 +(sending an error message \(as discussed in RFC 1123 \247 5.2.6\).)189 585.6 R +-.15(Fo)5.931 G 3.431(re).15 G(xample,)472.06 585.6 Q +(when sending an error message to)189 597.6 Q(<@kno)229 613.8 Q(wn1,@kno)-.25 E +(wn2,@unkno)-.25 E(wn:user@kno)-.25 E(wn3>)-.25 E F2(sendmail)189 630 Q F1 .46 +(will strip of)2.96 F 2.96(ft)-.25 G .46(he \231@kno)284.48 630 R .46 +(wn1\232 in order to mak)-.25 F 2.96(et)-.1 G .46(he route as direct as)422.74 +630 R 3.429(possible. Ho)189 642 R(we)-.25 E -.15(ve)-.25 G 1.729 -.4(r, i).15 +H 3.429(ft).4 G(he)284.057 642 Q F0(R)3.429 E F1 .929 +(option is set, this will be disabled, and the mail)3.429 F .362 +(will be sent to the \214rst address in the route, e)189 654 R -.15(ve)-.25 G +2.862(ni).15 G 2.862(fl)391.452 654 S .362(ater addresses are kno)400.424 654 R +(wn.)-.25 E(This may be useful if you are caught behind a \214re)189 666 Q -.1 +(wa)-.25 G(ll.).1 E 68.11(sB)117 682.2 S 2.729(es)195.67 682.2 S(uper)206.729 +682.2 Q .229(-safe when running things, i.e., al)-.2 F -.1(wa)-.1 G .229 +(ys instantiate the queue \214le, e).1 F -.15(ve)-.25 G(n).15 E .739 +(if you are going to attempt immediate deli)189 694.2 R -.15(ve)-.25 G(ry).15 E +(.)-.65 E F2(Sendmail)5.739 E F1(al)3.239 E -.1(wa)-.1 G .739(ys instantiates) +.1 F(the queue \214le before returning control the the client under an)189 +706.2 Q 2.5(yc)-.15 G(ircumstances.)444.07 706.2 Q EP +%%Page: 35 32 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-35)452.9 60 Q/F1 10/Times-Roman@0 SF(S)117 96 Q/F2 10/Times-Italic@0 SF +(\214le)A F1(Log statistics in the named)189 96 Q F2(\214le)2.5 E F1(.)A(t)117 +112.2 Q F2(S,D)A F1 .798(Set the local time zone name to)189 112.2 R F2(S)3.299 +E F1 .799(for standard time and)3.299 F F2(D)3.299 E F1 .799 +(for daylight time;)3.299 F(this is only used under v)189 124.2 Q(ersion six.) +-.15 E(T)117 140.4 Q F2(rtime/wtime)A F1 1.604(Set the queue timeout to)189 +140.4 R F2(rtime)4.103 E F1 6.603(.A)C 1.603(fter this interv)334.172 140.4 R +1.603(al, messages that ha)-.25 F 1.903 -.15(ve n)-.2 H(ot).15 E 1.251 +(been successfully sent will be returned to the sender)189 152.4 R 6.252(.D) +-.55 G(ef)422.724 152.4 Q 1.252(aults to \214v)-.1 F 3.752(ed)-.15 G(ays.) +488.17 152.4 Q .546(The optional)189 164.4 R F2(wtime)3.046 E F1 .546 +(is the time after which a w)3.046 F .546(arning message is sent.)-.1 F .546 +(If it is)5.546 F(missing or zero then no w)189 176.4 Q +(arning messages are sent.)-.1 E(u)117 192.6 Q F2(n)A F1 .175(Set the def)189 +192.6 R .175(ault userid for mailers to)-.1 F F2(n)2.675 E F1 5.175(.M)C .175 +(ailers without the)355.28 192.6 R F2(S)2.676 E F1 .176(\215ag in the mailer) +2.676 F(de\214nition will run as this user)189 204.6 Q 5(.D)-.55 G(ef)322.34 +204.6 Q(aults to 1.)-.1 E(U)117 220.8 Q F2(udbspec)A F1 +(The user database speci\214cation.)189 220.8 Q 67(vR)117 237 S .412(un in v) +195.67 237 R .412(erbose mode.)-.15 F .412(If this is set,)5.412 F F2(sendmail) +2.911 E F1 .411(adjusts options)2.911 F F0(c)2.911 E F1(\(don')2.911 E 2.911 +(tc)-.18 G(onnect)477.34 237 Q .427(to e)189 249 R(xpensi)-.15 E .727 -.15 +(ve m)-.25 H .427(ailers\) and).15 F F0(d)2.927 E F1(\(deli)2.928 E -.15(ve) +-.25 G .428(ry mode\) so that all mail is deli).15 F -.15(ve)-.25 G .428 +(red com-).15 F .048 +(pletely in a single job so that you can see the entire deli)189 261 R -.15(ve) +-.25 G .048(ry process.).15 F(Option)5.048 E F0(v)2.548 E F1(should)189 273 Q +F2(ne)3.389 E(ver)-.15 E F1 .889 +(be set in the con\214guration \214le; it is intended for command line)3.389 F +(use only)189 285 Q(.)-.65 E(V)117 301.2 Q F2(fallbac)A(khost)-.2 E F1 .964 +(If speci\214ed, the)189 301.2 R F2(fallbac)3.464 E(khost)-.2 E F1 .964 +(acts lik)3.464 F 3.464(eav)-.1 G .964(ery lo)358.608 301.2 R 3.464(wp)-.25 G +.964(riority MX on e)398.056 301.2 R -.15(ve)-.25 G .963(ry host.).15 F +(This is intended to be used by sites with poor netw)189 313.2 Q(ork connecti) +-.1 E(vity)-.25 E(.)-.65 E(x)117 329.4 Q F2(LA)A F1 .108 +(When the system load a)189 329.4 R -.15(ve)-.2 G .108(rage e).15 F(xceeds)-.15 +E F2(LA)2.608 E F1 2.608(,j)C .109(ust queue messages \(i.e., don')367.546 +329.4 R 2.609(tt)-.18 G(ry)495.67 329.4 Q(to send them\).)189 341.4 Q(Def)5 E +(aults to 8.)-.1 E(X)117 357.6 Q F2(LA)A F1 1.251(When the system load a)189 +357.6 R -.15(ve)-.2 G 1.251(rage e).15 F(xceeds)-.15 E F2(LA)3.751 E F1 3.751 +(,r)C 1.251(efuse incoming SMTP connec-)376.097 357.6 R 2.5(tions. Def)189 +369.6 R(aults to 12.)-.1 E(y)117 385.8 Q F2(fact)A F1 .621(The indicated)189 +385.8 R F2(fact)3.121 E F1 .621(or is added to the priority \(thus)B F2 +(lowering)3.122 E F1 .622(the priority of the)3.122 F 1.384 +(job\) for each recipient, i.e., this v)189 397.8 R 1.383 +(alue penalizes jobs with lar)-.25 F 1.383(ge numbers of)-.18 F 2.5 +(recipients. Def)189 409.8 R(aults to 30000.)-.1 E 64.78(YI)117 426 S 3.346(fs) +192.33 426 S .846(et, deli)202.896 426 R -.15(ve)-.25 G 3.346(re).15 G .847 +(ach job that is run from the queue in a separate process.)251.118 426 R(Use) +5.847 E .037(this option if you are short of memory)189 438 R 2.536(,s)-.65 G +.036(ince the def)350.024 438 R .036(ault tends to consume con-)-.1 F +(siderable amounts of memory while the queue is being processed.)189 450 Q(z) +117 466.2 Q F2(fact)A F1 1.644(The indicated)189 466.2 R F2(fact)4.144 E F1 +1.645(or is multiplied by the message class \(determined by the)B .923 +(Precedence: \214eld in the user header and the)189 478.2 R F0(P)3.423 E F1 +.923(lines in the con\214guration \214le\))3.423 F .819 +(and subtracted from the priority)189 490.2 R 5.819(.T)-.65 G .819 +(hus, messages with a higher Priority: will)333.255 490.2 R(be f)189 502.2 Q +-.2(avo)-.1 G 2.5(red. Def).2 F(aults to 1800.)-.1 E(Z)117 518.4 Q F2(fact)A F1 +(The)189 518.4 Q F2(fact)3.346 E F1 .846(or is added to the priority e)B -.15 +(ve)-.25 G .846(ry time a job is processed.).15 F .845(Thus, each)5.845 F .942 +(time a job is processed, its priority will be decreased by the indicated v)189 +530.4 R(alue.)-.25 E .297(In most en)189 542.4 R .296 +(vironments this should be positi)-.4 F -.15(ve)-.25 G 2.796(,s).15 G .296 +(ince hosts that are do)378.614 542.4 R .296(wn are all)-.25 F(too often do)189 +554.4 Q(wn for a long time.)-.25 E(Def)5 E(aults to 90000.)-.1 E 67(7S)117 +570.6 S .278(trip input to se)194.56 570.6 R -.15(ve)-.25 G 2.778(nb).15 G .278 +(its for compatibility with old systems.)275.272 570.6 R .279(This shouldn') +5.279 F 2.779(tb)-.18 G(e)499.56 570.6 Q(necessary)189 582.6 Q(.)-.65 E .78 +(All options can be speci\214ed on the command line using the \255o \215ag, b) +117 598.8 R .779(ut most will cause)-.2 F F2(send-)3.279 E(mail)117 610.8 Q F1 +.324(to relinquish its setuid permissions.)2.824 F .325 +(The options that will not cause this are b, d, e, E, i, L,)5.325 F .217 +(m, o, p, r)117 622.8 R 2.717(,s)-.4 G 2.717(,v)162.468 622.8 S 2.717(,C) +172.035 622.8 S 2.717(,a)183.922 622.8 S .217(nd 7.)193.579 622.8 R .216 +(Also, M \(de\214ne macro\) when de\214ning the r or s macros is also consid-) +5.216 F(ered \231safe\232.)117 634.8 Q F0 2.5(5.1.7. P)102 658.8 R 2.5<8a70>2.5 +G -.18(re)156.17 658.8 S(cedence de\214nitions).18 E F1 -1.11(Va)142 675 S .304 +(lues for the \231Precedence:\232 \214eld may be de\214ned using the)1.11 F F0 +(P)2.805 E F1 .305(control line.)2.805 F .305(The syntax of)5.305 F +(this \214eld is:)117 687 Q F0(P)157 703.2 Q F2(name)A F0(=)A F2(num)A F1 1.779 +(When the)117 719.4 R F2(name)4.279 E F1 1.779 +(is found in a \231Precedence:\232 \214eld, the message class is set to)4.279 F +F2(num)4.278 E F1 6.778(.H)C(igher)483.45 719.4 Q EP +%%Page: 36 33 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-36 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF .81 +(numbers mean higher precedence.)117 96 R .81(Numbers less than zero ha)5.81 F +1.11 -.15(ve t)-.2 H .81(he special property that if an).15 F .486(error occur\ +s during processing the body of the message will not be returned; this is e)117 +108 R .485(xpected to)-.15 F 1.704(be used for \231b)117 120 R 1.705 +(ulk\232 mail such as through mailing lists.)-.2 F 1.705(The def)6.705 F 1.705 +(ault precedence is zero.)-.1 F -.15(Fo)6.705 G(r).15 E -.15(ex)117 132 S +(ample, our list of precedences is:).15 E(P\214rst-class=0)157 148.2 Q +(Pspecial-deli)157 160.2 Q -.15(ve)-.25 G(ry=100).15 E(Plist=\25530)157 172.2 Q +(Pb)157 184.2 Q(ulk=\25560)-.2 E(Pjunk=\255100)157 196.2 Q .8 +(People writing mailing list e)117 212.4 R .799 +(xploders are encouraged to use \231Precedence: list\232.)-.15 F .799(Older v) +5.799 F(ersions)-.15 E(of)117 224.4 Q/F2 10/Times-Italic@0 SF(sendmail)3.759 E +F1 1.259(\(which discarded all error returns for ne)3.759 F -.05(ga)-.15 G(ti) +.05 E 1.559 -.15(ve p)-.25 H 1.259(recedences\) didn').15 F 3.76(tr)-.18 G 1.26 +(ecognize this)450.25 224.4 R .255(name, gi)117 236.4 R .255(ving it a def)-.25 +F .255(ault precedence of zero.)-.1 F .254(This allo)5.254 F .254 +(ws list maintainers to see error returns on)-.25 F(both old and ne)117 248.4 Q +2.5(wv)-.25 G(ersions of)193.26 248.4 Q F2(sendmail)2.5 E F1(.)A F0 2.5 +(5.1.8. V)102 272.4 R 2.5<8a63>2.5 G(on\214guration v)156.16 272.4 Q(ersion le) +-.1 E -.1(ve)-.15 G(l).1 E F1 2.11 -.8(To p)142 288.6 T(ro).8 E .51 +(vide compatibility with old con\214guration \214les, the)-.15 F F0(V)3.01 E F1 +.51(line has been added to de\214ne)3.01 F .173(some v)117 300.6 R .173 +(ery basic semantics of the con\214guration \214le.)-.15 F .172 +(These are not intended to be long term sup-)5.173 F 1.84(ports; rather)117 +312.6 R 4.34(,t)-.4 G(he)176.66 312.6 Q 4.34(yd)-.15 G 1.84 +(escribe compatibility features which will probably be remo)200.29 312.6 R -.15 +(ve)-.15 G 4.34(di).15 G 4.34(nf)470.78 312.6 S(uture)483.45 312.6 Q(releases.) +117 324.6 Q .031(\231Old\232 con\214guration \214les are de\214ned as v)142 +340.8 R .031(ersion le)-.15 F -.15(ve)-.25 G 2.531(lo).15 G 2.531(ne. V)359.438 +340.8 R .031(ersion le)-1.11 F -.15(ve)-.25 G 2.53(lt).15 G .23 -.1(wo \214) +433.84 340.8 T .03(les mak).1 F 2.53(et)-.1 G(he)494.56 340.8 Q(follo)117 352.8 +Q(wing changes:)-.25 E 12.5(\(1\) Host)122 369 R .757(name canoni\214cation \(\ +$[ ... $]\) appends a dot if the name is recognized; this gi)3.256 F -.15(ve) +-.25 G(s).15 E .903(the con\214g \214le a w)148.66 381 R .903 +(ay of \214nding out if an)-.1 F .903(ything matched.)-.15 F(\(Actually)5.903 E +3.403(,t)-.65 G .902(his just initializes)432.186 381 R .424 +(the \231host\232 map with the \231\255a.)148.66 393 R 5.424<9a8d>-.7 G .424 +(ag \212 you can reset it to an)280.014 393 R .424(ything you prefer by declar) +-.15 F(-)-.2 E(ing the map e)148.66 405 Q(xplicitly)-.15 E(.\))-.65 E 12.5 +(\(2\) Def)122 421.2 R .436(ault host name e)-.1 F .435 +(xtension is consistent throughout processing; v)-.15 F .435(ersion le)-.15 F +-.15(ve)-.25 G 2.935(lo).15 G .435(ne con-)473.855 421.2 R .828 +(\214gurations turned of)148.66 433.2 R 3.328(fd)-.25 G .828(omain e)243.384 +433.2 R .828(xtension \(that is, adding the local domain name\) during)-.15 F +.597(certain points in processing.)148.66 445.2 R -1.11(Ve)5.597 G .597 +(rsion le)1.11 F -.15(ve)-.25 G 3.097(lt).15 G .797 -.1(wo c)326.822 445.2 T +.597(on\214gurations are e).1 F .596(xpected to include a)-.15 F +(trailing dot to indicate that the name is already canonical.)148.66 457.2 Q +12.5(\(3\) Local)122 473.4 R .176 +(names that are not aliases are passed through a ne)2.675 F 2.676(wd)-.25 G +.176(istinguished ruleset \214v)388.892 473.4 R .176(e; this)-.15 F .797 +(can be used to append a local relay)148.66 485.4 R 5.797(.T)-.65 G .797 +(his beha)307.676 485.4 R .796(viour can be pre)-.2 F -.15(ve)-.25 G .796 +(nted by resolving the).15 F .62(local name with an initial `@'.)148.66 497.4 R +.621(That is, something that resolv)5.62 F .621(es to a local mailer and a)-.15 +F .844(user name of \231vikki\232 will be passed through ruleset \214v)148.66 +509.4 R .843(e, b)-.15 F .843(ut a user name of \231@vikki\232)-.2 F .328 +(will ha)148.66 521.4 R .628 -.15(ve t)-.2 H .328 +(he `@' stripped, will not be passed through ruleset \214v).15 F .328(e, b)-.15 +F .328(ut will otherwise be)-.2 F 1.509(treated the same as the prior e)148.66 +533.4 R 4.009(xample. The)-.15 F -.15(ex)4.009 G 1.508 +(pectation is that this might be used to).15 F .907(implement a polic)148.66 +545.4 R 3.407(yw)-.15 G .907(here mail sent to \231vikki\232 w)238.171 545.4 R +.908(as handled by a central hub, b)-.1 F .908(ut mail)-.2 F +(sent to \231vikki@localhost\232 w)148.66 557.4 Q(as deli)-.1 E -.15(ve)-.25 G +(red directly).15 E(.)-.65 E -1.11(Ve)142 573.6 S .229(rsion le)1.11 F -.15(ve) +-.25 G 2.729(lt).15 G .229(hree \214les allo)199.828 573.6 R 2.729(w#i)-.25 G +.228(nitiated comments on all lines.)274.374 573.6 R .228 +(Exceptions are backslash)5.228 F(escaped # marks and the $# syntax.)117 585.6 +Q F0 2.5(5.1.9. K)102 609.6 R 2.5<8a6b>2.5 G(ey \214le declaration)157.74 609.6 +Q F1(Special maps can be de\214ned using the line:)142 625.8 Q +(Kmapname mapclass ar)157 642 Q(guments)-.18 E(The)117 658.2 Q F2(mapname)3.443 +E F1 .944(is the handle by which this map is referenced in the re)3.443 F .944 +(writing rules.)-.25 F(The)5.944 E F2(map-)3.444 E(class)117 670.2 Q F1 .301 +(is the name of a type of map; these are compiled in to sendmail.)2.801 F(The) +5.3 E F2(ar)2.8 E(guments)-.37 E F1 .3(are inter)2.8 F(-)-.2 E .569 +(preted depending on the class; typically)117 682.2 R 3.069(,t)-.65 G .569 +(here w)286.134 682.2 R .569(ould be a single ar)-.1 F .57 +(gument naming the \214le con-)-.18 F(taining the map.)117 694.2 Q +(Maps are referenced using the syntax:)142 710.4 Q EP +%%Page: 37 34 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-37)452.9 60 Q/F1 10/Times-Roman@0 SF($\()157 96 Q/F2 10/Times-Italic@0 +SF(map k)2.5 E -.3(ey)-.1 G F1($@)2.8 E F2(ar)2.5 E(guments)-.37 E F1($:)2.5 E +F2(default)2.5 E F1($\))2.5 E .797(where either or both of the)117 112.2 R F2 +(ar)3.297 E(guments)-.37 E F1(or)3.297 E F2(default)3.297 E F1 .796 +(portion may be omitted.)3.297 F(The)5.796 E F2(ar)3.296 E(guments)-.37 E F1 +(may)3.296 E .205(appear more than once.)117 124.2 R .205(The indicated)5.205 F +F2 -.1(ke)2.705 G(y)-.2 E F1(and)2.705 E F2(ar)2.705 E(guments)-.37 E F1 .205 +(are passed to the appropriate mapping)2.705 F 2.503(function. If)117 136.2 R +.003(it returns a v)2.503 F .003(alue, it replaces the input.)-.25 F .003 +(If it does not return a v)5.003 F .003(alue and the)-.25 F F2(default)2.503 E +F1(is)2.503 E(speci\214ed, the)117 148.2 Q F2(default)2.5 E F1 +(replaces the input.)2.5 E(Otherwise, the input is unchanged.)5 E .159 +(During replacement of either a map v)142 164.4 R .159(alue or def)-.25 F .159 +(ault the string \231%)-.1 F F2(n)A F1 2.66<9a28>C(where)421.82 164.4 Q F2(n) +2.66 E F1 .16(is a digit\) is)2.66 F .204(replaced by the corresponding)117 +176.4 R F2(ar)2.704 E(gument)-.37 E F1 5.204(.A)C -.18(rg)294 176.4 S .204 +(ument zero is al).18 F -.1(wa)-.1 G .204(ys the database k).1 F -.15(ey)-.1 G +5.203(.F)-.5 G .203(or e)468.127 176.4 R(xam-)-.15 E(ple, the rule)117 188.4 Q +(R$- ! $+)157 204.6 Q($: $\(uucp $1 $@ $2 $: %1 @ %0 . UUCP $\))265 204.6 Q +.436(Looks up the UUCP name in a \(user de\214ned\) UUCP map; if not found it \ +turns it into \231.UUCP\232)117 220.8 R 2.5(form. The)117 232.8 R +(database might contain records lik)2.5 E(e:)-.1 E(decv)157 249 Q 77.43 +(ax %1@%0.DEC.COM)-.25 F 72.19(research %1@%0.A)157 261 R(TT)-1.11 E(.COM)-.74 +E 2.065(The b)142 281.4 R 2.064(uilt in map with both name and class \231host\ +\232 is the host name canonicalization)-.2 F 2.5(lookup. Thus,)117 293.4 R +(the syntax:)2.5 E($\(host)157 309.6 Q F2(hostname)2.5 E F1($\))A(is equi)117 +325.8 Q -.25(va)-.25 G(lent to:).25 E($[)157 342 Q F2(hostname)A F1($])A 1.783 +(There are four prede\214ned database lookup classes: \231dbm\232, \231btree\ +\232, \231hash\232, and \231nis\232.)142 362.4 R .569 +(The \214rst requires that sendmail be compiled with the)117 374.4 R F0(ndbm) +3.069 E F1 .568(library; the second tw)3.069 F 3.068(or)-.1 G .568(equire the) +463.722 374.4 R F0(db)117 386.4 Q F1(library)3.198 E 3.198(,a)-.65 G .698 +(nd the third requires that sendmail be compiled with NIS support.)167.466 +386.4 R .698(All four accept)5.698 F(as ar)117 398.4 Q +(guments the some optional \215ags and a \214lename \(or a mapname for NIS\).) +-.18 E(Kno)5 E(wn \215ags are:)-.25 E 58.86(\255o Indicates)117 414.6 R .21 +(that this map is optional \212 that is, if it cannot be opened, no error is) +2.71 F(produced, and sendmail will beha)189 426.6 Q .3 -.15(ve a)-.2 H 2.5(si) +.15 G 2.5(ft)348.9 426.6 S(he map e)357.51 426.6 Q(xisted b)-.15 E(ut w)-.2 E +(as empty)-.1 E(.)-.65 E 56.64(\255N Normally)117 442.8 R .515 +(when maps are written, the trailing null byte is not included as part)3.015 F +.8(of the k)189 454.8 R -.15(ey)-.1 G 5.8(.I)-.5 G 3.3(ft)241.32 454.8 S .799 +(his \215ag is indicated it will be included.)250.73 454.8 R .799 +(During lookups, only)5.799 F(the null-byte-included form will be searched.)189 +466.8 Q(See also)5 E F0(\255O.)2.5 E F1 56.64(\255O If)117 483 R(neither)4.388 +E F0<ad4e>4.388 E F1(or)4.388 E F0<ad4f>4.388 E F1 1.888(are speci\214ed,)4.388 +F F2(sendmail)4.388 E F1 1.889(uses an adapti)4.388 F 2.189 -.15(ve a)-.25 H +1.889(lgorithm to).15 F 1.025 +(decide whether or not to look for null bytes on the end of k)189 495 R -.15 +(ey)-.1 G 3.525(s. It).15 F 1.025(starts by)3.525 F .922 +(trying both; if it \214nds an)189 507 R 3.422(yk)-.15 G 1.222 -.15(ey w)304.29 +507 T .922(ith a null byte it ne).15 F -.15(ve)-.25 G 3.422(rt).15 G .922 +(ries ag)422.684 507 R .922(ain without a)-.05 F .124(null byte and vice v)189 +519 R 2.623(ersa. If)-.15 F .123(this \215ag is speci\214ed, it ne)2.623 F -.15 +(ve)-.25 G 2.623(rt).15 G .123(ries with a null byte;)421.288 519 R .748 +(this can speed matches b)189 531 R .748(ut is ne)-.2 F -.15(ve)-.25 G 3.249 +(rn).15 G(ecessary)341.667 531 Q 5.749(.I)-.65 G 3.249(fb)386.466 531 S(oth) +398.045 531 Q F0<ad4e>3.249 E F1(and)3.249 E F0<ad4f>3.249 E F1 .749 +(are speci-)3.249 F(\214ed,)189 543 Q F2(sendmail)4.349 E F1 1.849(will ne) +4.349 F -.15(ve)-.25 G 4.349(rt).15 G 1.849(ry an)300.156 543 R 4.349(ym)-.15 G +1.848(atches at all \212 that is, e)339.254 543 R -.15(ve)-.25 G 1.848 +(rything will).15 F(appear to f)189 555 Q(ail.)-.1 E<ad61>117 571.2 Q F2(x)A F1 +.649(Append the character)189 571.2 R F2(x)3.149 E F1 .649 +(on successful matches.)3.149 F -.15(Fo)5.649 G 3.149(re).15 G .649 +(xample, the def)406.052 571.2 R(ault)-.1 E F2(host)3.15 E F1 +(map appends a dot on successful matches.)189 583.2 Q 60.53(\255f F)117 599.4 R +(old upper to lo)-.15 E(wer case before looking up the k)-.25 E -.15(ey)-.1 G +(.)-.5 E 56.08(\255m Match)117 615.6 R .085(only \(without replacing the v) +2.585 F 2.585(alue\). If)-.25 F .085(you only care about the e)2.585 F +(xistence)-.15 E 2.618(of a k)189 627.6 R 2.918 -.15(ey a)-.1 H 2.618 +(nd not the v).15 F 2.619(alue \(as you might when searching the NIS map)-.25 F +.447(\231hosts.byname\232 for e)189 639.6 R .447(xample\), this \215ag pre)-.15 +F -.15(ve)-.25 G .447(nts the map from substituting the).15 F -.25(va)189 651.6 +S 4.935(lue. Ho).25 F(we)-.25 E -.15(ve)-.25 G 3.235 -.4(r, T).15 H 2.436 +(he \255a ar).4 F 2.436(gument is still appended on a match, and the)-.18 F +(def)189 663.6 Q(ault is still tak)-.1 E(en if the match f)-.1 E(ails.)-.1 E +(The)142 679.8 Q F2(dbm)3.874 E F1 1.374 +(map appends the strings \231.pag\232 and \231.dir\232 to the gi)3.874 F -.15 +(ve)-.25 G 3.874<6e8c>.15 G 1.374(lename; the tw)420.268 679.8 R(o)-.1 E F2(db) +3.874 E F1(-)A(based maps append \231.db\232.)117 691.8 Q .022(The program)142 +708 R F2(mak)2.522 E(emap)-.1 E F1 .023(\(8\) can be used to b)B .023(uild an) +-.2 F 2.523(yo)-.15 G 2.523(ft)353.095 708 S .023 +(he three database-oriented maps.)361.728 708 R(It)5.023 E(tak)117 720 Q +(es the follo)-.1 E(wing \215ags:)-.25 E EP +%%Page: 38 35 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-38 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF 60.53(\255f Do) +117 96 R(not fold upper to lo)2.5 E(wer case in the map.)-.25 E 56.64 +(\255N Include)117 112.2 R(null bytes in k)2.5 E -.15(ey)-.1 G(s.).15 E 58.86 +(\255o Append)117 128.4 R(to an e)2.5 E(xisting \(old\) \214le.)-.15 E 60.53 +(\255r Allo)117 144.6 R 3.479(wr)-.25 G .979(eplacement of e)220.559 144.6 R +.979(xisting k)-.15 F -.15(ey)-.1 G .979(s; normally).15 F 3.479(,r)-.65 G .979 +(e-inserting an e)385.494 144.6 R .979(xisting k)-.15 F 1.279 -.15(ey i)-.1 H +(s).15 E(an error)189 156.6 Q(.)-.55 E 58.86(\255v Print)117 172.8 R +(what is happening.)2.5 E(There are also tw)142 189 Q 2.5(ob)-.1 G +(uiltin maps that are, strictly speaking, not database lookups.)223.34 189 Q +1.563(The \231host\232 map does host domain canoni\214cation; gi)142 205.2 R +-.15(ve)-.25 G 4.063(nah).15 G 1.563(ost name it calls the name)392.585 205.2 R +(serv)117 217.2 Q(er to \214nd the canonical name for that host.)-.15 E .106 +(The \231dequote\232 map strips double quotes \("\) from a name.)142 233.4 R +.106(It does not strip backslashes.)5.106 F(It)5.106 E 1.838 +(will not strip quotes if the resulting string w)117 245.4 R 1.838 +(ould contain unscannable syntax \(that is, basic)-.1 F .601(errors lik)117 +257.4 R 3.101(eu)-.1 G .601(nbalanced angle brack)166.422 257.4 R .601 +(ets; more sophisticated errors such as unkno)-.1 F .6(wn hosts are not)-.25 F +(check)117 269.4 Q 3.398(ed\). The)-.1 F .899 +(intent is for use when trying to accept mail from systems such as DECnet that) +3.398 F(routinely quote odd syntax such as)117 281.4 Q("49ers::ubell")157 297.6 +Q 2.5(At)117 313.8 S(ypical usage is probably something lik)129.5 313.8 Q(e:) +-.1 E(Kdequote dequote)157 330 Q(...)157 354 Q 88.19(R$\255 $:)157 378 R +($\(dequote $1 $\))2.5 E(R$\255 $+)157 390 Q($: $>3 $1 $2)265 390 Q +(Care must be tak)117 406.2 Q(en to pre)-.1 E -.15(ve)-.25 G(nt une).15 E +(xpected results; for e)-.15 E(xample,)-.15 E("|someprogram < input > output") +157 422.4 Q .084(will ha)117 438.6 R .384 -.15(ve q)-.2 H .083 +(uotes stripped, b).15 F .083 +(ut the result is probably not what you had in mind.)-.2 F -.15(Fo)5.083 G .083 +(rtunately these).15 F(cases are rare.)117 450.6 Q(Ne)142 466.8 Q 2.5(wc)-.25 G +(lasses can be added in the routine)167.57 466.8 Q F0(setupmaps)2.5 E F1 +(in \214le)2.5 E F0(conf)2.5 E(.c)-.15 E F1(.)A F0 2.5(5.2. Building)87 490.8 R +2.5(aC)2.5 G(on\214guration File Fr)160.91 490.8 Q(om Scratch)-.18 E F1 1.517 +(Building a con\214guration table from scratch is an e)127 507 R 1.518 +(xtremely dif)-.15 F 1.518(\214cult job)-.25 F 6.518(.F)-.4 G(ortunately) +441.334 507 Q 4.018(,i)-.65 G 4.018(ti)490.532 507 S(s)500.11 507 Q 1.855 +(almost ne)102 519 R -.15(ve)-.25 G 4.355(rn).15 G 1.855 +(ecessary to do so; nearly e)164.19 519 R -.15(ve)-.25 G 1.855 +(ry situation that may come up may be resolv).15 F 1.855(ed by)-.15 F .416 +(changing an e)102 531 R .416(xisting table.)-.15 F .416(In an)5.416 F 2.916 +(yc)-.15 G .416 +(ase, it is critical that you understand what it is that you are try-)248.616 +531 R 1.151(ing to do and come up with a philosoph)102 543 R 3.651(yf)-.05 G +1.151(or the con\214guration table.)281.472 543 R 1.151 +(This section is intended to)6.151 F -.15(ex)102 555 S .67 +(plain what the real purpose of a con\214guration table is and to gi).15 F .97 +-.15(ve y)-.25 H .67(ou some ideas for what your).15 F(philosoph)102 567 Q 2.5 +(ym)-.05 G(ight be.)156.68 567 Q F0 1.32(Do not e)127 583.2 R -.1(ve)-.15 G +3.82(nc).1 G(onsider)188.2 583.2 Q F1 1.32(writing your o)3.82 F 1.32 +(wn con\214guration \214le without carefully studying RFC)-.25 F +(821, 822, and 1123.)102 595.2 Q -1.1(Yo)5 G 2.5(us)1.1 G +(hould also read RFC 976 if you are doing UUCP e)208.95 595.2 Q(xchange.)-.15 E +F0 2.5(5.2.1. What)102 619.2 R -.25(yo)2.5 G 2.5(ua).25 G .36 -.18(re t)178.7 +619.2 T(rying to do).18 E F1 .82 +(The con\214guration table has three major purposes.)142 635.4 R .821 +(The \214rst and simplest is to set up the)5.821 F(en)117 647.4 Q .35 +(vironment for)-.4 F/F2 10/Times-Italic@0 SF(sendmail)2.85 E F1 5.35(.T)C .35 +(his in)234.58 647.4 R -.2(vo)-.4 G(lv).2 E .35 +(es setting the options, de\214ning a fe)-.15 F 2.85(wc)-.25 G .35 +(ritical macros, etc.)429.43 647.4 R(Since these are described in other places\ +, we will not go into more detail here.)117 659.4 Q .283 +(The second purpose is to re)142 675.6 R .284(write addresses in the message.) +-.25 F .284(This should typically be done)5.284 F .214(in tw)117 687.6 R 2.714 +(op)-.1 G 2.713(hases. The)150.108 687.6 R .213 +(\214rst phase maps addresses in an)2.713 F 2.713(yf)-.15 G .213 +(ormat into a canonical form.)337.182 687.6 R .213(This should)5.213 F .156 +(be done in ruleset three.)117 699.6 R .157 +(The second phase maps this canonical form into the syntax appropriate)5.156 F +1.998(for the recei)117 711.6 R 1.997(ving mailer)-.25 F(.)-.55 E F2(Sendmail) +6.997 E F1 1.997(does this in three subphases.)4.497 F 1.997 +(Rulesets one and tw)6.997 F 4.497(oa)-.1 G(re)496.23 711.6 Q .043 +(applied to all sender and recipient addresses respecti)117 723.6 R -.15(ve) +-.25 G(ly).15 E 5.043(.A)-.65 G .043(fter this, you may specify per)357.904 +723.6 R(-mailer)-.2 E EP +%%Page: 39 36 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-39)452.9 60 Q/F1 10/Times-Roman@0 SF 2.723 +(rulesets for both sender and recipient addresses; this allo)117 96 R 2.723 +(ws mailer)-.25 F 2.723(-speci\214c customization.)-.2 F(Finally)117 108 Q 2.5 +(,r)-.65 G(uleset four is applied to do an)153.02 108 Q 2.5(yd)-.15 G(ef)283.69 +108 Q(ault con)-.1 E -.15(ve)-.4 G(rsion to e).15 E(xternal form.)-.15 E .785(\ +The third purpose is to map addresses into the actual set of instructions nece\ +ssary to get)142 124.2 R .154(the message deli)117 136.2 R -.15(ve)-.25 G 2.654 +(red. Ruleset).15 F .154(zero must resolv)2.654 F 2.654(et)-.15 G 2.654(ot) +321.658 136.2 S .153(he internal form, which is in turn used as a)332.092 136.2 +R .446(pointer to a mailer descriptor)117 148.2 R 5.446(.T)-.55 G .446 +(he mailer descriptor describes the interf)248.38 148.2 R .447 +(ace requirements of the)-.1 F(mailer)117 160.2 Q(.)-.55 E F0 2.5 +(5.2.2. Philosoph)102 184.2 R(y)-.15 E F1 1.481(The particular philosoph)142 +200.4 R 3.981(yy)-.05 G 1.481(ou choose will depend hea)257.213 200.4 R 1.481 +(vily on the size and structure of)-.2 F .55(your or)117 212.4 R -.05(ga)-.18 G +3.05(nization. I).05 F .55(will present a fe)3.05 F 3.05(wp)-.25 G .55 +(ossible philosophies here.)283.39 212.4 R .55(There are as man)5.55 F 3.05(yp) +-.15 G(hiloso-)476.22 212.4 Q +(phies as there are con\214g designers; feel free to de)117 224.4 Q -.15(ve) +-.25 G(lop your o).15 E(wn.)-.25 E .388 +(One general point applies to all of these philosophies: it is almost al)142 +240.6 R -.1(wa)-.1 G .388(ys a mistak).1 F 2.888(et)-.1 G 2.888(ot)485.002 +240.6 S(ry)495.67 240.6 Q .176(to do full host route resolution.)117 252.6 R +-.15(Fo)5.176 G 2.676(re).15 G .176 +(xample, if you are on a UUCP-only site and you are trying)267.652 252.6 R +1.223(to get names of the form \231user@host\232 to the Internet, it does not \ +pay to route them to \231xyz-)117 264.6 R -.25(va)117 276.6 S(x!decv).25 E +(ax!ucb)-.25 E -.25(va)-.15 G .304 +(x!c70!user@host\232 since you then depend on se).25 F -.15(ve)-.25 G .305 +(ral links not under your con-).15 F .996(trol, some of which are lik)117 288.6 +R .996(ely to misparse it an)-.1 F(yw)-.15 E(ay)-.1 E 5.996(.T)-.65 G .996 +(he best approach to this problem is to)347.32 288.6 R 1.048(simply forw)117 +300.6 R 1.048(ard the message for \231user@host\232 to \231xyzv)-.1 F 1.049 +(ax\232 and let xyzv)-.25 F 1.049(ax w)-.25 F 1.049(orry about it from)-.1 F +3.606(there. In)117 312.6 R(summary)3.606 E 3.606(,j)-.65 G 1.106 +(ust get the message closer to the destination, rather than determining the) +202.988 312.6 R(full path.)117 324.6 Q F0 2.5(5.2.2.1. Lar)117 348.6 R +(ge site, many hosts \212 minimum inf)-.1 E(ormation)-.25 E F1(Berk)157 364.8 Q +(ele)-.1 E 3.018(yi)-.15 G 3.018(sa)198.648 364.8 S 3.018(ne)209.996 364.8 S +.518(xample of a lar)222.304 364.8 R .518(ge site, i.e., more than tw)-.18 F +3.018(oo)-.1 G 3.018(rt)400.266 364.8 S .519(hree hosts and multiple)409.394 +364.8 R .444(mail connections.)132 376.8 R 2.044 -.8(We h)5.444 H -2.25 -.2 +(av e).8 H .443(decided that the only reasonable philosoph)3.144 F 2.943(yi) +-.05 G 2.943(no)429.634 376.8 S .443(ur en)442.577 376.8 R(vironment)-.4 E .312 +(is to designate one host as the guru for our site.)132 388.8 R .312 +(It must be able to resolv)5.312 F 2.812(ea)-.15 G .612 -.15(ny p)438.504 388.8 +T .312(iece of mail).15 F 1.083(it recei)132 400.8 R -.15(ve)-.25 G 3.583 +(s. The).15 F 1.083(other sites should ha)3.583 F 1.383 -.15(ve t)-.2 H 1.083 +(he minimum amount of information the).15 F 3.582(yc)-.15 G 1.082(an get) +478.758 400.8 R -2.3 -.15(aw a)132 412.8 T 2.635(yw).15 G 2.635(ith. In)162.705 +412.8 R .135(addition, an)2.635 F 2.635(yi)-.15 G .135(nformation the)249.485 +412.8 R 2.635(yd)-.15 G 2.635(oh)321.265 412.8 S -2.25 -.2(av e)333.9 412.8 T +.136(should be hints rather than solid infor)2.835 F(-)-.2 E(mation.)132 424.8 +Q -.15(Fo)157 441 S 6.71(re).15 G 4.209 +(xample, a typical site on our local ether netw)181.74 441 R 4.209 +(ork is \231monet\232 \(actually)-.1 F(\231monet.CS.Berk)132 453 Q(ele)-.1 E +-.65(y.)-.15 G 3.887(EDU\232\). When).65 F 1.387(monet recei)3.887 F -.15(ve) +-.25 G 3.887(sm).15 G 1.387(ail for deli)354.258 453 R -.15(ve)-.25 G(ry).15 E +3.887(,i)-.65 G 3.887(tc)424.579 453 S 1.387(hecks whether it)435.686 453 R +(kno)132 465 Q 1.342(ws that the destination host is directly reachable; if so\ +, mail is sent to that host.)-.25 F 1.342(If it)6.342 F(recei)132 477 Q -.15 +(ve)-.25 G 2.915(sm).15 G .415(ail for an)175.055 477 R 2.915(yu)-.15 G(nkno) +224.75 477 Q .415(wn host, it just passes it directly to \231ucb)-.25 F -.25 +(va)-.15 G(x.CS.Berk).25 E(ele)-.1 E -.65(y.)-.15 G(EDU\232,).65 E .178 +(our master host.)132 489 R(Ucb)5.178 E -.25(va)-.15 G 2.678(xm).25 G .177 +(ay determine that the host name is ille)242.852 489 R -.05(ga)-.15 G 2.677(la) +.05 G .177(nd reject the message,)415.159 489 R .754(or may be able to do deli) +132 501 R -.15(ve)-.25 G(ry).15 E 5.754(.H)-.65 G -.25(ow)268.146 501 S -2.15 +-.25(ev e).25 H 1.554 -.4(r, i).25 H 3.254(ti).4 G 3.254(si)313.874 501 S .754 +(mportant to note that when a ne)323.798 501 R 3.254(wm)-.25 G .754(ail con-) +472.976 501 R .164(nection is added, the only host that)132 513 R/F2 10 +/Times-Italic@0 SF(must)2.664 E F1(ha)2.664 E .464 -.15(ve i)-.2 H .164 +(ts tables updated is ucb).15 F -.25(va)-.15 G .164(x; the others).25 F F2(may) +2.664 E F1(be)2.664 E(updated if con)132 525 Q -.15(ve)-.4 G(nient, b).15 E +(ut this is not critical.)-.2 E 2.121 +(This picture is slightly muddied due to netw)157 541.2 R 2.122 +(ork connections that are not actually)-.1 F 2.362(located on ucb)132 553.2 R +-.25(va)-.15 G 4.862(x. F).25 F 2.362(or e)-.15 F 2.362 +(xample, some UUCP connections are currently on \231ucbarpa.)-.15 F<9a>-.7 E +(Ho)132 565.2 Q(we)-.25 E -.15(ve)-.25 G 1.044 -.4(r, m).15 H(onet).4 E F2 .244 +(does not)2.744 F F1(kno)2.744 E 2.744(wa)-.25 G .245 +(bout this; the information is hidden totally between ucb)266.34 565.2 R -.25 +(va)-.15 G(x).25 E 1.045(and ucbarpa.)132 577.2 R 1.045 +(Mail going from monet to a UUCP host is transferred via the ethernet from) +6.045 F 1.43(monet to ucb)132 589.2 R -.25(va)-.15 G 1.43 +(x, then via the ethernet from ucb).25 F -.25(va)-.15 G 3.931(xt).25 G 3.931 +(ou)355.704 589.2 S 1.431(cbarpa, and then is submitted to)369.635 589.2 R +(UUCP)132 601.2 Q 5(.A)-1.11 G(lthough this in)172.28 601.2 Q -.2(vo)-.4 G(lv) +.2 E(es some e)-.15 E(xtra hops, we feel this is an acceptable tradeof)-.15 E +(f.)-.25 E .826(An interesting point is that it w)157 617.4 R .826 +(ould be possible to update monet to send appropriate)-.1 F .127 +(UUCP mail directly to ucbarpa if the load got too high; if monet f)132 629.4 R +.127(ailed to note a host as con-)-.1 F .353(nected to ucbarpa it w)132 641.4 R +.353(ould go via ucb)-.1 F -.25(va)-.15 G 2.853(xa).25 G 2.852(sb)305.954 641.4 +S .352(efore, and if monet incorrectly sent a message)317.696 641.4 R .395 +(to ucbarpa it w)132 653.4 R .396(ould still be sent by ucbarpa to ucb)-.1 F +-.25(va)-.15 G 2.896(xa).25 G 2.896(sb)356.654 653.4 S 2.896(efore. The)368.44 +653.4 R .396(only problem that can)2.896 F .901(occur is loops, for e)132 665.4 +R .901(xample, if ucbarpa thought that ucb)-.15 F -.25(va)-.15 G 3.401(xh).25 G +.9(ad the UUCP connection and)383.75 665.4 R(vice v)132 677.4 Q 2.5(ersa. F) +-.15 F(or this reason, updates should)-.15 E F2(always)2.5 E F1 +(happen to the master host \214rst.)2.5 E .144(This philosoph)157 693.6 R 2.644 +(yr)-.05 G .145(esults as much from the need to ha)227.798 693.6 R .445 -.15 +(ve a s)-.2 H .145(ingle source for the con\214gu-).15 F .289 +(ration \214les \(typically b)132 705.6 R .289(uilt using)-.2 F F2(m4)2.789 E +F1 .289(\(1\) or some similar tool\) as an)1.666 F 2.789(yl)-.15 G .288 +(ogical need.)410.664 705.6 R(Maintain-)5.288 E +(ing more than three separate tables by hand is essentially an impossible job) +132 717.6 Q(.)-.4 E EP +%%Page: 40 37 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-40 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E 2.5(5.2.2.2. Small)117 96 R +(site \212 complete inf)2.5 E(ormation)-.25 E/F1 10/Times-Roman@0 SF 3.356(As) +157 112.2 S .856(mall site \(tw)171.466 112.2 R 3.356(oo)-.1 G 3.356(rt)236.434 +112.2 S .856(hree hosts and fe)245.9 112.2 R 3.356(we)-.25 G .856 +(xternal connections\) may \214nd it more rea-)330.564 112.2 R .435 +(sonable to ha)132 124.2 R .735 -.15(ve c)-.2 H .435 +(omplete information at each host.).15 F .435(This w)5.435 F .435 +(ould require that each host kno)-.1 F(w)-.25 E -.15(ex)132 136.2 S .185 +(actly where each netw).15 F .185 +(ork connection is, possibly including the names of each host on that)-.1 F +(netw)132 148.2 Q 4.341(ork. As)-.1 F 1.841 +(long as the site remains small and the the con\214guration remains relati) +4.341 F -.15(ve)-.25 G(ly).15 E +(static, the update problem will probably not be too great.)132 160.2 Q F0 2.5 +(5.2.2.3. Single)117 184.2 R(host)2.5 E F1 .117(This is in some sense the tri) +157 200.4 R .117(vial case.)-.25 F .117 +(The only major issue is trying to insure that you)5.117 F(don')132 212.4 Q +3.425(th)-.18 G -2.25 -.2(av e)161.355 212.4 T .925(to kno)3.625 F 3.425(wt) +-.25 G .925(oo much about your en)217.69 212.4 R 3.425(vironment. F)-.4 F .925 +(or e)-.15 F .924(xample, if you ha)-.15 F 1.224 -.15(ve a U)-.2 H(UCP).15 E +.614(connection you might \214nd it useful to kno)132 224.4 R 3.115(wa)-.25 G +.615(bout the names of hosts connected directly to)318.885 224.4 R(you, b)132 +236.4 Q +(ut this is really not necessary since this may be determined from the syntax.) +-.2 E F0 2.5(5.2.2.4. A)117 260.4 R(completely differ)2.5 E(ent philosoph)-.18 +E(y)-.15 E F1(This is adapted from Bruce Lilly)157 276.6 Q 5(.A)-.65 G .3 -.15 +(ny e)301.89 276.6 T(rrors in interpretation are mine.).15 E .065 +(Do minimal changes in ruleset 3: \214x some common b)157 292.8 R .064 +(ut unambiguous errors \(e.g. trail-)-.2 F 2.758 +(ing dot on domains\) and hide bang paths foo!bar into bar@foo.UUCP)132 304.8 R +7.759(.T)-1.11 G 2.759(he resulting)454.301 304.8 R("canonical" form is an)132 +316.8 Q 2.5(yv)-.15 G(alid RFC822/RFC1123/RFC976 address.)233.63 316.8 Q 1.388 +(Ruleset 0 does the b)157 333 R 1.387(ulk of the w)-.2 F 3.887(ork. It)-.1 F +(remo)3.887 E -.15(ve)-.15 G 3.887(st).15 G 1.387 +(he trailing "@.UUCP" that hides)367.472 333 R .66(bang paths, strips an)132 +345 R .661(ything not needed to resolv)-.15 F .661 +(e, e.g. the phrase from phrase <route-addr>)-.15 F .497 +(and from named groups, rejects unparseable addresses using $#error)132 357 R +2.996(,a)-.4 G .496(nd \214nally resolv)419.052 357 R .496(es to)-.15 F 4.324 +(am)132 369 S 1.824(ailer/host/user triple.)148.544 369 R 1.824 +(Ruleset 0 is rather length)6.824 F 4.325(ya)-.05 G 4.325(si)360.965 369 S +4.325(th)371.96 369 S 1.825(as to handle 3 basic address)384.065 369 R 5.373 +(forms: RFC976 bang paths, RFC1123 %-hacks \(including v)132 381 R 5.373 +(anilla RFC822 local-)-.25 F .136(part@domain\), and RFC822 source routes.)132 +393 R(It')5.137 E 2.637(sa)-.55 G .137(lso complicated by ha)329.508 393 R .137 +(ving to handle named)-.2 F(lists.)132 405 Q .617(The header re)157 421.2 R +.616(writing rulesets 1 and 2 remo)-.25 F .916 -.15(ve t)-.15 H .616 +(he trailing "@.UUCP" that hides bang).15 F 2.5(paths. Ruleset)132 433.2 R 2.5 +(2a)2.5 G(lso strips the $# mailer $@ host \(for test mode\).)205.05 433.2 Q +(Ruleset 4 does absolutely nothing.)157 449.4 Q 1.316(The per)157 465.6 R 1.316 +(-mailer re)-.2 F 1.316(writing rulesets conform the en)-.25 F -.15(ve)-.4 G +1.317(lope and header addresses to the).15 F +(requirements of the speci\214c mailer)132 477.6 Q(.)-.55 E +(Lots of rulesets-as-subroutines are used.)157 493.8 Q .35(As a result, header\ + addresses are subject to minimal munging \(per RFC1123\), and the)157 510 R +(general plan is per RFC822 sect. 3.4.10.)132 522 Q F0 2.5(5.2.3. Rele)102 546 +R -.1(va)-.15 G(nt issues).1 E F1 .584(The canonical form you use should almos\ +t certainly be as speci\214ed in the Internet proto-)142 562.2 R 2.604 +(cols RFC819 and RFC822.)117 574.2 R 2.604(Copies of these RFC')7.604 F 5.104 +(sa)-.55 G 2.603(re included on the)347.852 574.2 R/F2 10/Times-Italic@0 SF +(sendmail)5.103 E F1 2.603(tape as)5.103 F F2(doc/rfc819.lpr)117 586.2 Q F1 +(and)2.5 E F2(doc/rfc822.lpr)2.5 E F1(.)A 2.04 +(RFC822 describes the format of the mail message itself.)142 602.4 R F2 +(Sendmail)7.04 E F1(follo)4.54 E 2.04(ws this RFC)-.25 F(closely)117 614.4 Q +2.984(,t)-.65 G 2.984(ot)152.944 614.4 S .483(he e)163.708 614.4 R .483 +(xtent that man)-.15 F 2.983(yo)-.15 G 2.983(ft)251.44 614.4 S .483 +(he standards described in this document can not be changed)260.533 614.4 R +(without changing the code.)117 626.4 Q(In particular)5 E 2.5(,t)-.4 G +(he follo)286.85 626.4 Q(wing characters ha)-.25 E .3 -.15(ve s)-.2 H +(pecial interpretations:).15 E 2.5(<>\(\)"\\)157 642.6 S(An)117 658.8 Q 3.036 +(ya)-.15 G .537(ttempt to use these characters for other than their RFC822 pur\ +pose in addresses is proba-)141.546 658.8 R(bly doomed to disaster)117 670.8 Q +(.)-.55 E 1.327 +(RFC819 describes the speci\214cs of the domain-based addressing.)142 687 R +1.326(This is touched on in)6.327 F 1.439(RFC822 as well.)117 699 R 1.439 +(Essentially each host is gi)6.439 F -.15(ve)-.25 G 3.939(nan).15 G 1.44 +(ame which is a right-to-left dot quali\214ed)333.711 699 R .232 +(pseudo-path from a distinguished root.)117 711 R .232 +(The elements of the path need not be ph)5.232 F .232(ysical hosts; the)-.05 F +2.365(domain is logical rather than ph)117 723 R 4.866(ysical. F)-.05 F 2.366 +(or e)-.15 F 2.366(xample, at Berk)-.15 F(ele)-.1 E 4.866(yo)-.15 G 2.366 +(ne le)406.406 723 R -.05(ga)-.15 G 4.866(lh).05 G 2.366(ost might be)449.818 +723 R EP +%%Page: 41 38 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-41)452.9 60 Q/F1 10/Times-Roman@0 SF(\231a.CC.Berk)117 96 Q(ele)-.1 E +-.65(y.)-.15 G .366 +(EDU\232; reading from right to left, \231EDU\232 is a top le).65 F -.15(ve) +-.25 G 2.865(ld).15 G .365(omain comprising edu-)410.5 96 R .561 +(cational institutions, \231Berk)117 108 R(ele)-.1 E .562 +(y\232 is a logical domain name, \231CC\232 represents the Computer Cen-)-.15 F +(ter)117 120 Q 2.5(,\()-.4 G(in this case a strictly logical entity\), and \ +\231a\232 is a host in the Computer Center)135.48 120 Q(.)-.55 E(Be)142 136.2 Q +-.1(wa)-.25 G(re when reading RFC819 that there are a number of errors in it.) +.1 E F0 2.5(5.2.4. Ho)102 160.2 R 2.5(wt)-.1 G 2.5(op)155.23 160.2 S -.18(ro) +168.29 160.2 S(ceed).18 E F1 .335(Once you ha)142 176.4 R .635 -.15(ve d)-.2 H +.335(ecided on a philosoph).15 F 1.635 -.65(y, i)-.05 H 2.835(ti).65 G 2.834 +(sw)319.44 176.4 S .334(orth e)333.284 176.4 R .334(xamining the a)-.15 F -.25 +(va)-.2 G .334(ilable con\214guration).25 F .174(tables to decide if an)117 +188.4 R 2.674(yo)-.15 G 2.674(ft)212.98 188.4 S .174 +(hem are close enough to steal major parts of.)221.764 188.4 R(Ev)5.174 E .175 +(en under the w)-.15 F .175(orst of)-.1 F(conditions, there is a f)117 200.4 Q +(air amount of boiler plate that can be collected safely)-.1 E(.)-.65 E .33 +(The ne)142 216.6 R .33(xt step is to b)-.15 F .33(uild ruleset three.)-.2 F +.329(This will be the hardest part of the job)5.33 F 5.329(.B)-.4 G -2.1 -.25 +(ew a)469.321 216.6 T .329(re of).25 F .781 +(doing too much to the address in this ruleset, since an)117 228.6 R .781 +(ything you do will re\215ect through to the)-.15 F 2.744(message. In)117 240.6 +R(particular)2.744 E 2.744(,s)-.4 G .243 +(tripping of local domains is best deferred, since this can lea)216.752 240.6 R +.543 -.15(ve y)-.2 H .243(ou with).15 F 1.234 +(addresses with no domain spec at all.)117 252.6 R(Since)6.235 E/F2 10 +/Times-Italic@0 SF(sendmail)3.735 E F1(lik)3.735 E 1.235 +(es to append the sending domain to)-.1 F .83 +(addresses with no domain, this can change the semantics of addresses.)117 +264.6 R .83(Also try to a)5.83 F -.2(vo)-.2 G .83(id fully).2 F .342 +(qualifying domains in this ruleset.)117 276.6 R .342(Although technically le) +5.342 F -.05(ga)-.15 G .343(l, this can lead to unpleasantly and).05 F 1.287 +(unnecessarily long addresses re\215ected into messages.)117 288.6 R 1.287 +(The Berk)6.287 F(ele)-.1 E 3.787(yc)-.15 G 1.287 +(on\214guration \214les de\214ne)406.426 288.6 R .093 +(ruleset nine to qualify domain names and strip local domains.)117 300.6 R .093 +(This is called from ruleset zero to)5.093 F +(get all addresses into a cleaner form.)117 312.6 Q .318(Once you ha)142 328.8 +R .618 -.15(ve r)-.2 H .318 +(uleset three \214nished, the other rulesets should be relati).15 F -.15(ve) +-.25 G .318(ly tri).15 F 2.817(vial. If)-.25 F(you)2.817 E(need hints, e)117 +340.8 Q(xamine the supplied con\214guration tables.)-.15 E F0 2.5(5.2.5. T)102 +364.8 R(esting the r)-.92 E(ewriting rules \212 the \255bt \215ag)-.18 E F1 +1.075(When you b)142 381 R 1.075(uild a con\214guration table, you can do a ce\ +rtain amount of testing using the)-.2 F(\231test mode\232 of)117 393 Q F2 +(sendmail)2.5 E F1 5(.F)C(or e)226.84 393 Q(xample, you could in)-.15 E -.2(vo) +-.4 G -.1(ke).2 G F2(sendmail)2.6 E F1(as:)2.5 E(sendmail \255bt \255Ctest.cf) +157 409.2 Q .904(which w)117 425.4 R .903 +(ould read the con\214guration \214le \231test.cf\232 and enter test mode.)-.1 +F .903(In this mode, you enter)5.903 F(lines of the form:)117 437.4 Q +(rwset address)157 453.6 Q(where)117 469.8 Q F2(rwset)3.376 E F1 .876 +(is the re)3.376 F .876(writing set you w)-.25 F .876(ant to use and)-.1 F F2 +(addr)3.376 E(ess)-.37 E F1 .877(is an address to apply the set to.)3.376 F -.7 +(Te)117 481.8 S .17(st mode sho).7 F .17(ws you the steps it tak)-.25 F .169 +(es as it proceeds, \214nally sho)-.1 F .169(wing you the address it ends up) +-.25 F 3.635(with. Y)117 493.8 R 1.135(ou may use a comma separated list of rw\ +sets for sequential application of rules to an)-1.1 F 2.5(input. F)117 505.8 R +(or e)-.15 E(xample:)-.15 E(3,1,21,4 monet:bollard)157 522 Q .386 +(\214rst applies ruleset three to the input \231monet:bollard.)117 538.2 R +5.385<9a52>-.7 G .385(uleset one is then applied to the output)347.145 538.2 R +(of ruleset three, follo)117 550.2 Q +(wed similarly by rulesets twenty-one and four)-.25 E(.)-.55 E .202(If you nee\ +d more detail, you can also use the \231\255d21\232 \215ag to turn on more deb) +142 566.4 R 2.702(ugging. F)-.2 F(or)-.15 E -.15(ex)117 578.4 S(ample,).15 E +(sendmail \255bt \255d21.99)157 594.6 Q .754 +(turns on an incredible amount of information; a single w)117 610.8 R .753 +(ord address is probably going to print)-.1 F(out se)117 622.8 Q -.15(ve)-.25 G +(ral pages w).15 E(orth of information.)-.1 E -1.1(Yo)142 639 S 3.075(us)1.1 G +.575(hould be w)165.085 639 R .575(arned that internally)-.1 F(,)-.65 E F0 +(sendmail)3.075 E F1 .575(applies ruleset 3 to all addresses.)3.075 F .575 +(In this)5.575 F -.15(ve)117 651 S 1.23(rsion of sendmail, you will ha).15 F +1.53 -.15(ve t)-.2 H 3.73(od).15 G 3.73(ot)281.21 651 S 1.23(hat manually) +292.72 651 R 6.23(.F)-.65 G 1.23(or e)359.38 651 R 1.23(xample, older v)-.15 F +1.23(ersions allo)-.15 F(wed)-.25 E(you to use)117 663 Q 2.5(0b)157 679.2 S +(ruce@broadcast.son)169.5 679.2 Q -.65(y.)-.15 G(com).65 E(This v)117 695.4 Q +(ersion requires that you use:)-.15 E(3,0 bruce@broadcast.son)157 711.6 Q -.65 +(y.)-.15 G(com).65 E EP +%%Page: 42 39 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-42 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E 2.5(5.2.6. Building)102 96 R +(mailer descriptions)2.5 E/F1 10/Times-Roman@0 SF 1.886 -.8(To a)142 112.2 T +.287(dd an outgoing mailer to your mail system, you will ha).8 F .587 -.15 +(ve t)-.2 H 2.787(od).15 G .287(e\214ne the characteristics)409.566 112.2 R +(of the mailer)117 124.2 Q(.)-.55 E 1.481(Each mailer must ha)142 140.4 R 1.781 +-.15(ve a)-.2 H 3.981(ni).15 G 1.481(nternal name.)257.645 140.4 R 1.481 +(This can be arbitrary)6.481 F 3.98(,e)-.65 G 1.48(xcept that the names)417.63 +140.4 R(\231local\232 and \231prog\232 must be de\214ned.)117 152.4 Q .127 +(The pathname of the mailer must be gi)142 168.6 R -.15(ve)-.25 G 2.628(ni).15 +G 2.628(nt)317.038 168.6 S .128(he P \214eld.)327.446 168.6 R .128 +(If this mailer should be accessed)5.128 F +(via an IPC connection, use the string \231[IPC]\232 instead.)117 180.6 Q .021 +(The F \214eld de\214nes the mailer \215ags.)142 196.8 R -1.1(Yo)5.021 G 2.521 +(us)1.1 G .021(hould specify an \231f\232 or \231r\232 \215ag to pass the name) +311.06 196.8 R .465(of the sender as a)117 208.8 R F0<ad66>2.965 E F1(or)2.965 +E F0<ad72>2.965 E F1 .465(\215ag respecti)2.965 F -.15(ve)-.25 G(ly).15 E 5.465 +(.T)-.65 G .465(hese \215ags are only passed if the)306.95 208.8 R 2.966(yw) +-.15 G .466(ere passed to)451.418 208.8 R/F2 10/Times-Italic@0 SF(sendmail,)117 +220.8 Q F1 1.705(so that mailers that gi)4.205 F 2.005 -.15(ve e)-.25 H 1.705 +(rrors under some circumstances can be placated.).15 F 1.705(If the)6.705 F +1.362(mailer is not pick)117 232.8 R 3.862(yy)-.15 G 1.362 +(ou can just specify \231\255f $g\232 in the ar)204.518 232.8 R 1.362 +(gv template.)-.18 F 1.363(If the mailer must be)6.362 F 1.708(called as)117 +244.8 R F0 -.18(ro)4.207 G(ot).18 E F1 1.707(the \231S\232 \215ag should be gi) +4.207 F -.15(ve)-.25 G 1.707 +(n; this will not reset the userid before calling the).15 F(mailer)117 258.8 Q +/F3 7/Times-Roman@0 SF(10)142.55 254.8 Q F1 5.112(.I)149.55 258.8 S 2.612(ft) +160.492 258.8 S .112(his mailer is local \(i.e., will perform \214nal deli) +169.214 258.8 R -.15(ve)-.25 G .112(ry rather than another netw).15 F .112 +(ork hop\))-.1 F .728(the \231l\232 \215ag should be gi)117 270.8 R -.15(ve) +-.25 G 3.227(n. Quote).15 F .727 +(characters \(backslashes and " marks\) can be stripped from)3.227 F .268 +(addresses if the \231s\232 \215ag is speci\214ed; if this is not gi)117 282.8 +R -.15(ve)-.25 G 2.769(nt).15 G(he)344.247 282.8 Q 2.769(ya)-.15 G .269 +(re passed through.)365.746 282.8 R .269(If the mailer is)5.269 F .67(capable \ +of sending to more than one user on the same host in a single transaction the \ +\231m\232 \215ag)117 294.8 R 1.176(should be stated.)117 306.8 R 1.176 +(If this \215ag is on, then the ar)6.176 F 1.177(gv template containing)-.18 F +F0($u)3.677 E F1 1.177(will be repeated for)3.677 F .089 +(each unique user on a gi)117 318.8 R -.15(ve)-.25 G 2.589(nh).15 G 2.589 +(ost. The)235.994 318.8 R .089 +(\231e\232 \215ag will mark the mailer as being \231e)2.589 F(xpensi)-.15 E +-.15(ve)-.25 G 1.488 -.7(,\232 w).15 H(hich).7 E(will cause)117 332.8 Q F2 +(sendmail)2.5 E F1(to defer connection until a queue run)2.5 E F3(11)345.57 +328.8 Q F1(.)352.57 332.8 Q 2.037(An unusual case is the \231C\232 \215ag.)142 +349 R 2.037(This \215ag applies to the mailer that the message is)7.037 F +(recei)117 361 Q -.15(ve)-.25 G 2.654(df).15 G .153(rom, rather than the maile\ +r being sent to; if set, the domain spec of the sender \(i.e., the)156.454 361 +R 1.519(\231@host.domain\232 part\) is sa)117 373 R -.15(ve)-.2 G 4.019(da).15 +G 1.519(nd is appended to an)252.746 373 R 4.019(ya)-.15 G 1.52 +(ddresses in the message that do not)354.341 373 R +(already contain a domain spec.)117 385 Q -.15(Fo)5 G 2.5(re).15 G +(xample, a message of the form:)266.11 385 Q(From: eric@v)157 401.2 Q +(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G(EDU).65 E -.8(To)157 413.2 S +2.5(:w).8 G(nj@monet.CS.Berk)179.81 413.2 Q(ele)-.1 E -.65(y.)-.15 G +(EDU, mckusick).65 E(will be modi\214ed to:)117 429.4 Q(From: eric@v)157 445.6 +Q(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G(EDU).65 E -.8(To)157 457.6 S +2.5(:w).8 G(nj@monet.CS.Berk)179.81 457.6 Q(ele)-.1 E -.65(y.)-.15 G +(EDU, mckusick@v).65 E(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G(EDU).65 E +F2 9.365(if and only if)117 473.8 R F1 9.364 +(the \231C\232 \215ag is de\214ned in the mailer corresponding to)207.8 473.8 R +(\231eric@v)117 485.8 Q(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G(EDU.).65 +E<9a>-.7 E(Other \215ags are described in Appendix C.)142 502 Q .538 +(The S and R \214elds in the mailer description are per)142 518.2 R .538 +(-mailer re)-.2 F .538(writing sets to be applied to)-.25 F 2.253 +(sender and recipient addresses respecti)117 530.2 R -.15(ve)-.25 G(ly).15 E +7.253(.T)-.65 G 2.252(hese are applied after the sending domain is)312.995 +530.2 R .546(appended and the general re)117 542.2 R .547 +(writing sets \(numbers one and tw)-.25 F .547(o\) are applied, b)-.1 F .547 +(ut before the out-)-.2 F .458(put re)117 554.2 R .458 +(write \(ruleset four\) is applied.)-.25 F 2.958(At)5.458 G .457 +(ypical use is to append the current domain to addresses)279.646 554.2 R +(that do not already ha)117 566.2 Q .3 -.15(ve a d)-.2 H 2.5(omain. F).15 F +(or e)-.15 E(xample, a header of the form:)-.15 E(From: eric)157 582.4 Q +(might be changed to be:)117 598.6 Q(From: eric@v)157 614.8 Q(angogh.CS.Berk) +-.25 E(ele)-.1 E -.65(y.)-.15 G(EDU).65 E(or)117 631 Q(From: ucb)157 647.2 Q +-.25(va)-.15 G(x!eric).25 E 1.312 +(depending on the domain it is being shipped into.)117 663.4 R 1.313 +(These sets can also be used to do special)6.312 F .32 LW 76 673 72 673 DL 80 +673 76 673 DL 84 673 80 673 DL 88 673 84 673 DL 92 673 88 673 DL 96 673 92 673 +DL 100 673 96 673 DL 104 673 100 673 DL 108 673 104 673 DL 112 673 108 673 DL +116 673 112 673 DL 120 673 116 673 DL 124 673 120 673 DL 128 673 124 673 DL 132 +673 128 673 DL 136 673 132 673 DL 140 673 136 673 DL 144 673 140 673 DL 148 673 +144 673 DL 152 673 148 673 DL 156 673 152 673 DL 160 673 156 673 DL 164 673 160 +673 DL 168 673 164 673 DL 172 673 168 673 DL 176 673 172 673 DL 180 673 176 673 +DL 184 673 180 673 DL 188 673 184 673 DL 192 673 188 673 DL 196 673 192 673 DL +200 673 196 673 DL 204 673 200 673 DL 208 673 204 673 DL 212 673 208 673 DL 216 +673 212 673 DL/F4 5/Times-Roman@0 SF(10)93.6 683.4 Q/F5 8/Times-Italic@0 SF +(Sendmail)3.2 I/F6 8/Times-Roman@0 SF +(must be running setuid to root for this to w)2 E(ork.)-.08 E F4(11)93.6 697 Q +F6(The \231c\232 con\214guration option must be gi)3.2 I -.12(ve)-.2 G 2(nf).12 +G(or this to be ef)242.04 700.2 Q(fecti)-.2 E -.12(ve)-.2 G(.).12 E EP +%%Page: 43 40 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-43)452.9 60 Q/F1 10/Times-Roman@0 SF(purpose output re)117 96 Q +(writing in cooperation with ruleset four)-.25 E(.)-.55 E .228 +(The E \214eld de\214nes the string to use as an end-of-line indication.)142 +112.2 R 2.728(As)5.228 G .228(tring containing only)419.654 112.2 R(ne)117 +124.2 Q(wline is the def)-.25 E 2.5(ault. The)-.1 F +(usual backslash escapes \(\\r)2.5 E 2.5(,\\)-.4 G(n, \\f, \\b\) may be used.) +342.87 124.2 Q(Finally)142 140.4 Q 2.648(,a)-.65 G 2.648(na)179.278 140.4 S +-.18(rg)191.366 140.4 S 2.648(vt).18 G .149(emplate is gi)209.944 140.4 R -.15 +(ve)-.25 G 2.649(na).15 G 2.649(st)282.481 140.4 S .149(he E \214eld.)291.8 +140.4 R .149(It may ha)5.149 F .449 -.15(ve e)-.2 H .149(mbedded spaces.).15 F +.149(If there is)5.149 F .204(no ar)117 152.4 R .204(gv with a)-.18 F F0($u) +2.704 E F1 .204(macro in it,)2.704 F/F2 10/Times-Italic@0 SF(sendmail)2.704 E +F1 .204(will speak SMTP to the mailer)2.704 F 5.203(.I)-.55 G 2.703(ft)412.648 +152.4 S .203(he pathname for this)421.461 152.4 R(mailer is \231[IPC],)117 +164.4 Q 2.5<9a74>-.7 G(he ar)192.4 164.4 Q(gv should be)-.18 E(IPC $h [)157 +180.6 Q F2(port)2.5 E F1(])2.5 E(where)117 196.8 Q F2(port)2.5 E F1 +(is the optional port number to connect to.)2.5 E -.15(Fo)142 213 S 2.5(re).15 +G(xample, the speci\214cations:)162.53 213 Q(Mlocal, P=/bin/mail, F=rlsm)157 +229.2 Q(S=10, R=20, A=mail \255d $u)5 E(Mether)157 241.2 Q 2.35(,P)-.4 G 13.9 +(=[IPC], F=meC,)195.89 241.2 R(S=11, R=21, A=IPC $h, M=100000)1.39 E 1.643 +(speci\214es a mailer to do local deli)117 257.4 R -.15(ve)-.25 G 1.644 +(ry and a mailer for ethernet deli).15 F -.15(ve)-.25 G(ry).15 E 6.644(.T)-.65 +G 1.644(he \214rst is called)436.018 257.4 R(\231local,)117 269.4 Q 2.649<9a69> +-.7 G 2.649(sl)152.549 269.4 S .149(ocated in the \214le \231/bin/mail,)161.868 +269.4 R 2.649<9a74>-.7 G(ak)283.573 269.4 Q .149(es a pick)-.1 F(y)-.15 E F0 +<ad72>2.649 E F1 .148(\215ag, does local deli)2.649 F -.15(ve)-.25 G(ry).15 E +2.648(,q)-.65 G .148(uotes should)453.572 269.4 R 1.016 +(be stripped from addresses, and multiple users can be deli)117 281.4 R -.15 +(ve)-.25 G 1.017(red at once; ruleset ten should be).15 F 1.417(applied to sen\ +der addresses in the message and ruleset twenty should be applied to recipient) +117 293.4 R .122(addresses; the ar)117 305.4 R .123 +(gv to send to a message will be the w)-.18 F .123(ord \231mail,)-.1 F 2.623 +<9a74>-.7 G .123(he w)383.125 305.4 R .123(ord \231\255d,)-.1 F 2.623<9a61>-.7 +G .123(nd w)446.644 305.4 R .123(ords con-)-.1 F 1.484 +(taining the name of the recei)117 317.4 R 1.484(ving user)-.25 F 6.484(.I)-.55 +G 3.984(fa)288.498 317.4 S F0<ad72>A F1 1.484 +(\215ag is inserted it will be between the w)3.984 F(ords)-.1 E .288 +(\231mail\232 and \231\255d.)117 329.4 R 5.288<9a54>-.7 G .289 +(he second mailer is called \231ether)196.394 329.4 R 1.689 -.7(,\232 i)-.4 H +2.789(ts).7 G .289(hould be connected to via an IPC con-)348.947 329.4 R .932(\ +nection, it can handle multiple users at once, connections should be deferred,\ + and an)117 341.4 R 3.432(yd)-.15 G(omain)479 341.4 Q 1.458 +(from the sender address should be appended to an)117 353.4 R 3.958(yr)-.15 G +(ecei)340.2 353.4 Q -.15(ve)-.25 G 3.958(rn).15 G 1.458 +(ame without a domain; sender)377.628 353.4 R .74 +(addresses should be processed by ruleset ele)117 365.4 R -.15(ve)-.25 G 3.24 +(na).15 G .74(nd recipient addresses by ruleset twenty-one.)320.34 365.4 R +(There is a 100,000 byte limit on messages passed through this mailer)117 377.4 +Q(.)-.55 E F0 2.5(5.3. The)87 401.4 R(User Database)2.5 E F1 .108(If you ha)127 +417.6 R .408 -.15(ve a ve)-.2 H .109 +(rsion of sendmail with the user database package compiled in, the handling of) +.15 F(sender and recipient addresses is modi\214ed.)102 429.6 Q +(The location of this database is controlled with the)127 445.8 Q F0(U)2.5 E F1 +(option.)2.5 E F0 2.5(5.3.1. Structur)102 469.8 R 2.5(eo)-.18 G 2.5(ft)177.92 +469.8 S(he user database)187.08 469.8 Q F1(The database is a sorted \(BT)142 +486 Q(ree-based\) structure.)-.35 E(User records are stored with the k)5 E -.15 +(ey)-.1 G(:).15 E F2(user)157 502.2 Q(-name)-.2 E F0(:)A F2(\214eld-name)A F1 +.128 +(The sorted database format ensures that user records are clustered together) +117 518.4 R 5.128(.M)-.55 G .128(eta-information is)432.492 518.4 R(al)117 +530.4 Q -.1(wa)-.1 G(ys stored with a leading colon.).1 E +(Field names de\214ne both the syntax and semantics of the v)142 546.6 Q 2.5 +(alue. De\214ned)-.25 F(\214elds include:)2.5 E 33.39(maildrop The)117 562.8 R +(deli)4.872 E -.15(ve)-.25 G 2.372(ry address for this user).15 F 7.372(.T)-.55 +G 2.373(here may be multiple v)349.472 562.8 R 2.373(alues of this)-.25 F 2.675 +(record. In)189 574.8 R(particular)2.675 E 2.675(,m)-.4 G .175 +(ailing lists will ha)284.095 574.8 R .475 -.15(ve o)-.2 H(ne).15 E F2(maildr) +2.675 E(op)-.45 E F1 .175(record for each user)2.675 F(on the list.)189 586.8 Q +30.06(mailname The)117 603 R 1.026(outgoing mailname for this user)3.526 F +6.026(.F)-.55 G 1.027(or each outgoing name, there should)353.336 603 R .08 +(be an appropriate)189 615 R F2(maildr)2.58 E(op)-.45 E F1 .08 +(record for that name to allo)2.58 F 2.58(wr)-.25 G .08(eturn mail.)422.38 615 +R .08(See also)5.08 F F2(:default:mailname)189 627 Q F1(.)A 25.62 +(mailsender Changes)117 643.2 R(an)3.447 E 3.447(ym)-.15 G .947 +(ail sent to this address to ha)252.404 643.2 R 1.248 -.15(ve t)-.2 H .948 +(he indicated en).15 F -.15(ve)-.4 G .948(lope sender).15 F(.)-.55 E .498(This\ + is intended for mailing lists, and will normally be the name of an appro-)189 +655.2 R .754(priate -request address.)189 667.2 R .754(It is v)5.754 F .755 +(ery similar to the o)-.15 F(wner)-.25 E(-)-.2 E F2(list)A F1 .755 +(syntax in the alias)3.255 F(\214le.)189 679.2 Q 33.95(fullname The)117 695.4 R +(full name of the user)2.5 E(.)-.55 E(of)117 711.6 Q 13.66(\214ce-address The) +-.25 F(of)2.5 E(\214ce address for this user)-.25 E(.)-.55 E EP +%%Page: 44 41 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-44 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF(of)117 96 Q +19.21(\214ce-phone The)-.25 F(of)2.5 E(\214ce phone number for this user)-.25 E +(.)-.55 E(of)117 112.2 Q(\214ce-f)-.25 E 30.98(ax The)-.1 F(of)2.5 E(\214ce F) +-.25 E(AX number for this user)-.74 E(.)-.55 E 13.96(home-address The)117 128.4 +R(home address for this user)2.5 E(.)-.55 E 19.51(home-phone The)117 144.6 R +(home phone number for this user)2.5 E(.)-.55 E(home-f)117 160.8 Q 31.28 +(ax The)-.1 F(home F)2.5 E(AX number for this user)-.74 E(.)-.55 E 41.73 +(project A)117 177 R .856 +(\(short\) description of the project this person is af)3.356 F .855 +(\214liated with.)-.25 F .855(In the Uni-)5.855 F -.15(ve)189 189 S +(rsity this is often just the name of their graduate advisor).15 E(.)-.55 E +52.28(plan A)117 205.2 R +(pointer to a \214le from which plan information can be g)2.5 E(athered.)-.05 E +.924(As of this writing, only a fe)142 221.4 R 3.424(wo)-.25 G 3.424(ft)273.208 +221.4 S .925(hese \214elds are actually being used by sendmail:)282.742 221.4 R +/F2 10/Times-Italic@0 SF(mail-)3.425 E(dr)117 233.4 Q(op)-.45 E F1(and)2.5 E F2 +(mailname)2.5 E F1 5(.A)C F2(\214ng)211.54 233.4 Q(er)-.1 E F1 +(program that uses the other \214elds is planned.)2.5 E F0 2.5(5.3.2. User)102 +257.4 R(database semantics)2.5 E F1 .996(When the re)142 273.6 R .995 +(writing rules submit an address to the local mailer)-.25 F 3.495(,t)-.4 G .995 +(he user name is passed)408.93 273.6 R .78(through the alias \214le.)117 285.6 +R .781 +(If no alias is found \(or if the alias points back to the same address\), the) +5.78 F 1.778(name \(with \231:maildrop\232 appended\) is then used as a k)117 +297.6 R 2.077 -.15(ey i)-.1 H 4.277(nt).15 G 1.777(he user database.)375.985 +297.6 R 1.777(If no match)6.777 F +(occurs \(or if the maildrop points at the same address\), forw)117 309.6 Q +(arding is tried.)-.1 E .55(If the \214rst tok)142 325.8 R .551(en of the user\ + name returned by ruleset 0 is an \231@\232 sign, the user database)-.1 F .626 +(lookup is skipped.)117 337.8 R .625 +(The intent is that the user database will act as a set of def)5.626 F .625 +(aults for a cluster)-.1 F 1.533(\(in our case, the Computer Science Di)117 +349.8 R 1.533(vision\); mail sent to a speci\214c machine should ignore)-.25 F +(these def)117 361.8 Q(aults.)-.1 E .351 +(When mail is sent, the name of the sending user is look)142 378 R .351 +(ed up in the database.)-.1 F .351(If that user)5.351 F .04 +(has a \231mailname\232 record, the v)117 390 R .041 +(alue of that record is used as their outgoing name.)-.25 F -.15(Fo)5.041 G +2.541(re).15 G .041(xample, I)466.189 390 R(might ha)117 402 Q .3 -.15(ve a r) +-.2 H(ecord:).15 E 25.94(eric:mailname Eric.Allman@CS.Berk)157 418.2 R(ele)-.1 +E -.65(y.)-.15 G(EDU).65 E(This w)117 434.4 Q +(ould cause my outgoing mail to be sent as Eric.Allman.)-.1 E .757 +(If a \231maildrop\232 is found for the user)142 450.6 R 3.257(,b)-.4 G .757 +(ut no corresponding \231maildrop\232 record e)301.588 450.6 R .757(xists, the) +-.15 F 1.127(record \231:def)117 462.6 R 1.127(ault:mailname\232 is consulted.) +-.1 F 1.127(If present, this is the name of a host to o)6.127 F -.15(ve)-.15 G +1.128(rride the).15 F .625(local host.)117 474.6 R -.15(Fo)5.625 G 3.125(re).15 +G .625(xample, in our case we w)185.515 474.6 R .625 +(ould set it to \231CS.Berk)-.1 F(ele)-.1 E -.65(y.)-.15 G 3.125(EDU\232. The) +.65 F(ef)3.125 E .625(fect is that)-.25 F(an)117 486.6 Q .881(yone kno)-.15 F +.882(wn in the database gets their outgoing mail stamped as \231user@CS.Berk) +-.25 F(ele)-.1 E -.65(y.)-.15 G(EDU\232,).65 E -.2(bu)117 498.6 S 2.5(tp).2 G +(eople not listed in the database use the local hostname.)137.08 498.6 Q F0 2.5 +(6. O)72 522.6 R(THER CONFIGURA)-.4 E(TION)-.95 E F1 .907 +(There are some con\214guration changes that can be made by recompiling)112 +538.8 R F2(sendmail)3.407 E F1 5.907(.T)C .906(his section)460.594 538.8 R +(describes what changes can be made and what has to be modi\214ed to mak)87 +550.8 Q 2.5(et)-.1 G(hem.)387.95 550.8 Q F0 2.5(6.1. P)87 574.8 R +(arameters in sr)-.1 E(c/Mak)-.18 E(e\214le)-.1 E F1 .92 +(These parameters are intended to describe the compilation en)127 591 R .92 +(vironment, not site polic)-.4 F 2.22 -.65(y, a)-.15 H(nd).65 E +(should normally be de\214ned in src/Mak)102 603 Q(e\214le.)-.1 E 39.5(NDBM If) +102 619.2 R .665(set, the ne)3.165 F 3.165(wv)-.25 G .664 +(ersion of the DBM library that allo)240.41 619.2 R .664 +(ws multiple databases will be)-.25 F 2.542(used. If)174 631.2 R .042 +(neither NDBM nor NEWDB are set, a much less ef)2.542 F .043 +(\214cient method of alias)-.25 F(lookup is used.)174 643.2 Q 32.84(NEWDB If) +102 659.4 R .142(set, use the ne)2.642 F 2.642(wd)-.25 G .142 +(atabase package from Berk)254.44 659.4 R(ele)-.1 E 2.641(y\()-.15 G .141 +(from 4.4BSD\).)385.817 659.4 R .141(This package)5.141 F .266 +(is substantially f)174 671.4 R .267(aster than DBM or NDBM.)-.1 F .267 +(If NEWDB and NDBM are both set,)5.267 F(sendmail will read DBM \214les, b)174 +683.4 Q(ut will create and use NEWDB \214les.)-.2 E(YPCOMP)102 699.6 Q 19.3 +-1.11(AT I)-.92 H 3.684(fs)1.11 G 1.184(et together with)188.234 699.6 R F2 +(both)3.684 E F1 1.183(NEWDB and NDBM,)3.683 F F2(sendmail)3.683 E F1 1.183 +(will create both DBM)3.683 F 1.067 +(and NEWDB \214les if and only if the \214le /v)174 711.6 R(ar/yp/Mak)-.25 E +1.067(e\214le e)-.1 F 1.067(xists and is readable.)-.15 F .501 +(This is intended for compatibility with Sun Microsystems')174 723.6 R F2 +(mkalias)3.001 E F1 .501(program used)3.001 F EP +%%Page: 45 42 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-45)452.9 60 Q/F1 10/Times-Roman@0 SF(on YP masters.)174 96 Q 41.73 +(_AIX3 Compile)102 112.2 R(for IBM AIX 3.x.)2.5 E +(This has only been tested on 3.2.3.)5 E 25.05(SYSTEM5 Set)102 128.4 R +(all of the compilation parameters appropriate for System V)2.5 E(.)-1.29 E +36.72(LOCKF Use)102 144.6 R .299(System V)2.799 F F0(lockf)2.799 E F1 .299 +(instead of Berk)2.799 F(ele)-.1 E(y)-.15 E F0(\215ock)2.799 E F1 5.299(.D)C .3 +(ue to the highly unusual seman-)375.012 144.6 R .052 +(tics of locks across forks in)174 156.6 R F0(lockf)2.552 E F1 2.551(,t)C .051 +(his should ne)314.903 156.6 R -.15(ve)-.25 G 2.551(rb).15 G 2.551(eu)387.706 +156.6 S .051(sed unless absolutely nec-)399.697 156.6 R(essary)174 168.6 Q 5 +(.S)-.65 G(et by def)211.4 168.6 Q(ault if SYSTEM5 is set.)-.1 E 33.94 +(SYS5TZ Use)102 184.8 R(System V time zone semantics.)2.5 E(HASINITGR)102 201 Q +(OUPS)-.4 E .812(Set this if your system has the)174 213 R/F2 10/Times-Italic@0 +SF(initgr)3.312 E(oups\(\))-.45 E F1 .812(call \(if you ha)3.312 F 1.112 -.15 +(ve m)-.2 H .813(ultiple group sup-).15 F 2.5(port\). This)174 225 R +(is the def)2.5 E(ault if SYSTEM5 is)-.1 E F2(not)2.5 E F1 +(de\214ned or if you are on HPUX.)2.5 E(HASUN)102 241.2 Q 13.19(AME Set)-.35 F +.89(this if you ha)3.39 F 1.19 -.15(ve t)-.2 H(he).15 E F2(uname)3.39 E F1 .89 +(\(2\) system call \(or corresponding library routine\).)B(Set by def)174 253.2 +Q(ault if SYSTEM5 is set.)-.1 E(HASST)102 269.4 Q -1.11(AT)-.93 G 15.42(FS Set) +1.11 F .202(this if you ha)2.701 F .502 -.15(ve t)-.2 H(he).15 E F2(statfs) +2.702 E F1 .202(\(2\) system call.)B .202(This will allo)5.202 F 2.702(wy)-.25 +G .202(ou to gi)425.822 269.4 R .502 -.15(ve a t)-.25 H(empo-).15 E .108 +(rary f)174 281.4 R .108(ailure message to incoming SMTP email when you are lo) +-.1 F 2.608(wo)-.25 G 2.608(nd)441.188 281.4 S .107(isk space.)453.796 281.4 R +(It)5.107 E(is set by def)174 293.4 Q(ault on 4.4BSD and OSF/1 systems.)-.1 E +(HASUST)102 309.6 Q 21.54 -1.11(AT S)-.93 H .593(et if you ha)1.11 F .894 -.15 +(ve t)-.2 H(he).15 E F2(ustat)3.094 E F1 .594(\(2\) system call.)B .594 +(This is an alternati)5.594 F .894 -.15(ve i)-.25 H .594(mplementation of).15 F +.525(disk space control.)174 321.6 R -1.1(Yo)5.525 G 3.025(us)1.1 G .525 +(hould only set one of HASST)278.32 321.6 R -1.11(AT)-.93 G .525(FS or HASUST) +1.11 F -.83 -1.11(AT ;)-.93 H(the)4.135 E(\214rst is preferred.)174 333.6 Q(_P) +102 349.8 Q -1.11(AT)-.92 G(H_SENDMAILCF)1.11 E +(The pathname of the sendmail.cf \214le.)174 361.8 Q(_P)102 378 Q -1.11(AT)-.92 +G(H_SENDMAILFC)1.11 E(The pathname of the sendmail.fc \214le.)174 390 Q(_P)102 +406.2 Q -1.11(AT)-.92 G(H_SENDMAILPID)1.11 E +(The pathname of the sendmail.pid \214le.)174 418.2 Q 26.17(LA_TYPE The)102 +434.4 R(load a)2.5 E -.15(ve)-.2 G(rage type.).15 E(Details are described belo) +5 E -.65(w.)-.25 G 1.146(The are four b)102 450.6 R 1.146(uilt-in w)-.2 F 1.146 +(ays of computing the load a)-.1 F -.15(ve)-.2 G(rage.).15 E F2(Sendmail)6.147 +E F1 1.147(tries to auto-con\214gure them)3.647 F .267 +(based on imperfect guesses; you can select one using the)102 462.6 R F2(cc) +2.766 E F1(option)2.766 E F0(\255DLA_TYPE=)2.766 E F2(type)A F1 2.766(,w)C +(here)467.364 462.6 Q F2(type)2.766 E F1(is:)102 474.6 Q 34.51(LA_INT The)102 +490.8 R -.1(ke)2.978 G .478(rnel stores the load a).1 F -.15(ve)-.2 G .479 +(rage in the k).15 F .479(ernel as an array of long inte)-.1 F 2.979(gers. The) +-.15 F(actual v)174 502.8 Q(alues are scaled by a f)-.25 E(actor FSCALE \(def) +-.1 E(ault 256\).)-.1 E(LA_FLO)102 519 Q 22.63 -1.11(AT T)-.35 H 1.118(he k) +1.11 F 1.117(ernel stores the load a)-.1 F -.15(ve)-.2 G 1.117(rage in the k) +.15 F 1.117(ernel as an array of double precision)-.1 F(\215oats.)174 531 Q +25.05(LA_SUBR Call)102 547.2 R(the)2.5 E F2 -.1(ge)2.5 G(tloadavg).1 E F1 +(routine to get the load a)2.5 E -.15(ve)-.2 G(rage as an array of doubles.).15 +E(LA_ZER)102 563.4 Q 27.96(OA)-.4 G -.1(lwa)181.22 563.4 S +(ys return zero as the load a).1 E -.15(ve)-.2 G 2.5(rage. This).15 F(is the f) +2.5 E(allback case.)-.1 E .738(If type)102 579.6 R/F3 9/Times-Roman@0 SF +(LA_INT)3.238 E F1(or)3.238 E F3(LA_FLO)3.238 E -.999(AT)-.315 G F1 .738 +(is speci\214ed, you may also need to specify)4.237 F F3(_P)3.239 E -.999(AT) +-.828 G(H_UNIX).999 E F1 .739(\(the path to)3.239 F .269 +(your system binary\) and)102 591.6 R F3(LA_A)2.769 E(VENR)-1.215 E(UN)-.36 E +F1 .269(\(the name of the v)2.769 F .269(ariable containing the load a)-.25 F +-.15(ve)-.2 G .269(rage in the).15 F -.1(ke)102 603.6 S(rnel; usually \231_a).1 +E -.15(ve)-.2 G(nrun\232 or \231a).15 E -.15(ve)-.2 G(nrun\232\).).15 E F0 2.5 +(6.2. P)87 627.6 R(arameters in sr)-.1 E(c/conf)-.18 E(.h)-.15 E F1 -.15(Pa)127 +643.8 S .895(rameters and compilation options are de\214ned in conf.h.).15 F +.896(Most of these need not normally)5.895 F .193(be tweak)102 655.8 R .192 +(ed; common parameters are all in sendmail.cf.)-.1 F(Ho)5.192 E(we)-.25 E -.15 +(ve)-.25 G .992 -.4(r, t).15 H .192(he sizes of certain primiti).4 F .492 -.15 +(ve ve)-.25 H(c-).15 E(tors, etc., are included in this \214le.)102 667.8 Q +(The numbers follo)5 E(wing the parameters are their def)-.25 E(ault v)-.1 E +(alue.)-.25 E 1.909(MAXLINE [1024])102 684 R 1.909 +(The maximum line length of an)190.309 684 R 4.409(yi)-.15 G 1.909(nput line.) +338.273 684 R 1.91(If message lines e)6.909 F 1.91(xceed this)-.15 F .575 +(length the)188.4 696 R 3.075(yw)-.15 G .575 +(ill still be processed correctly; ho)243.84 696 R(we)-.25 E -.15(ve)-.25 G +1.375 -.4(r, h).15 H .575(eader lines, con\214gura-).4 F +(tion \214le lines, alias lines, etc., must \214t within this limit.)188.4 708 +Q EP +%%Page: 46 43 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-46 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF(MAXN)102 96 Q +(AME [256])-.35 E(The maximum length of an)9.82 E 2.5(yn)-.15 G +(ame, such as a host or a user name.)309.63 96 Q .23(MAXPV [40])102 112.2 R +.231(The maximum number of parameters to an)188.63 112.2 R 2.731(ym)-.15 G +(ailer)376.455 112.2 Q 5.231(.T)-.55 G .231(his limits the number of)407.516 +112.2 R .376(recipients that may be passed in one transaction.)188.4 124.2 R +.375(It can be set to an)5.376 F 2.875(ya)-.15 G(rbitrary)474.01 124.2 Q .875 +(number abo)188.4 136.2 R 1.175 -.15(ve a)-.15 H .876(bout 10, since).15 F/F2 +10/Times-Italic@0 SF(sendmail)3.376 E F1 .876(will break up a deli)3.376 F -.15 +(ve)-.25 G .876(ry into smaller).15 F .887(batches as needed.)188.4 148.2 R +3.387(Ah)5.887 G .887(igher number may reduce load on your system, ho)285.808 +148.2 R(w-)-.25 E -2.15 -.25(ev e)188.4 160.2 T -.55(r.).25 G(MAXA)102 176.4 Q +-.18(TO)-1.11 G 2.558(M[).18 G 8.26(100] The)159.368 176.4 R .058 +(maximum number of atoms \(tok)2.558 F .059(ens\) in a single address.)-.1 F +-.15(Fo)5.059 G 2.559(re).15 G .059(xample, the)457.281 176.4 R +(address \231eric@CS.Berk)188.4 188.4 Q(ele)-.1 E -.65(y.)-.15 G(EDU\232 is se) +.65 E -.15(ve)-.25 G 2.5(na).15 G(toms.)367.93 188.4 Q .113(MAXMAILERS [25])102 +204.6 R .112(The maximum number of mailers that may be de\214ned in the con\ +\214guration \214le.).02 F(MAXR)102 220.8 Q(WSETS [100])-.55 E +(The maximum number of re).01 E(writing sets that may be de\214ned.)-.25 E +(MAXPRIORITIES [25])102 237 Q 2.481(The maximum number of v)188.4 249 R 2.482 +(alues for the \231Precedence:\232 \214eld that may be)-.25 F +(de\214ned \(using the)188.4 261 Q F0(P)2.5 E F1(line in sendmail.cf\).)2.5 E +(MAXUSERENVIR)102 277.2 Q(ON [40])-.4 E .399 +(The maximum number of items in the user en)188.4 289.2 R .399 +(vironment that will be passed to)-.4 F(subordinate mailers.)188.4 301.2 Q -.1 +(QU)102 317.4 S(EUESIZE [1000]).1 E +(The maximum number of entries that will be processed in a single queue run.) +2.35 E(MAXMXHOSTS [20])102 333.6 Q +(The maximum number of MX records we will accept for an)188.4 345.6 Q 2.5(ys) +-.15 G(ingle host.)439.03 345.6 Q(MAXIP)102 361.8 Q .968(ADDR [16])-.92 F .968 +(The maximum number of numeric IP addresses we will accept for this host.)7.61 +F(This does not limit the number the number of addresses for other hosts.)188.4 +373.8 Q 2.851(An)102 390 S .351(umber of other compilation options e)117.071 +390 R 2.851(xist. These)-.15 F .35 +(specify whether or not speci\214c code should be)2.851 F(compiled in.)102 402 +Q(DEB)102 418.2 Q 49.56(UG If)-.1 F 1.226(set, deb)3.726 F 1.226 +(ugging information is compiled in.)-.2 F 2.827 -.8(To a)6.226 H 1.227 +(ctually get the deb).8 F(ugging)-.2 E .4(output, the)188.4 430.2 R F0<ad64>2.9 +E F1 .4(\215ag must be used.)2.9 F F0 .4(WE STR)5.4 F(ONGL)-.3 E 2.9(YR)-.92 G +.4(ECOMMEND THA)412.05 430.2 R(T)-.95 E .97(THIS BE LEFT ON.)188.4 442.2 R F1 +.97(Some people, belie)5.97 F .97(ving that it w)-.25 F .97 +(as a security hole \(it)-.1 F -.1(wa)188.4 454.2 S(s, once\) ha).1 E .3 -.15 +(ve t)-.2 H(urned it of).15 E 2.5(fa)-.25 G(nd thus crippled deb)309.05 454.2 Q +(uggers.)-.2 E 41.69(NETINET If)102 470.4 R .829 +(set, support for Internet protocol netw)3.33 F .829(orking is compiled in.)-.1 +F(Pre)5.829 E .829(vious v)-.25 F(er)-.15 E(-)-.2 E .006(sions of)188.4 482.4 R +F0(sendmail)2.506 E F1 .006(referred to this as)2.506 F/F3 9/Times-Roman@0 SF +-.36(DA)2.506 G(EMON).36 E F1 2.506(;t)C .006(his old usage is no)382.57 482.4 +R 2.506(wi)-.25 G(ncorrect.)468.74 482.4 Q 48.35(NETISO If)102 498.6 R .143 +(set, support for ISO protocol netw)2.643 F .142 +(orking is compiled in \(it may be appropri-)-.1 F +(ate to #de\214ne this in the Mak)188.4 510.6 Q(e\214le instead of conf.h\).) +-.1 E 63.35(LOG If)102 526.8 R .5(set, the)3 F F2(syslo)3 E(g)-.1 E F1 .5 +(routine in use at some sites is used.)3 F .5(This mak)5.5 F .5(es an informa-) +-.1 F .504(tional log record for each message processed, and mak)188.4 538.8 R +.504(es a higher priority log)-.1 F(record for internal system errors.)188.4 +550.8 Q(MA)102 567 Q 16.12(TCHGECOS Compile)-1.11 F 3.555(in the code to do `) +6.055 F 3.555(`fuzzy matching')-.74 F 6.055('o)-.74 G 6.055(nt)404.22 567 S +3.555(he GECOS \214eld in)418.055 567 R 2.5(/etc/passwd. This)188.4 579 R +(also requires that option G be turned on.)2.5 E -.35(NA)102 595.2 S 18.15 +(MED_BIND Compile).35 F .413(in code to use the Berk)2.913 F(ele)-.1 E 2.912 +(yI)-.15 G .412(nternet Name Domain \(BIND\) serv)342.41 595.2 R .412(er to) +-.15 F(resolv)188.4 607.2 Q 2.5(eT)-.15 G(CP/IP host names.)225.74 607.2 Q(NO) +102 623.4 Q 38.76(TUNIX If)-.4 F .247 +(you are using a non-UNIX mail format, you can set this \215ag to turn of)2.747 +F 2.748(fs)-.25 G(pe-)491.23 623.4 Q +(cial processing of UNIX-style \231From \232 lines.)188.4 635.4 Q -.1(QU)102 +651.6 S 50.12(EUE This).1 F 1.559 +(\215ag should be set to compile in the queueing code.)4.06 F 1.559 +(If this is not set,)6.559 F +(mailers must accept the mail immediately or it will be returned to the sender) +188.4 663.6 Q(.)-.55 E(SETPR)102 679.8 Q 12.63(OCTITLE If)-.4 F(de\214ned,)3.88 +E F2(sendmail)3.88 E F1 1.381(will change its)3.881 F F2(ar)3.881 E(gv)-.37 E +F1 1.381(array to indicate its current status.)3.881 F .207 +(This can be used in conjunction with the)188.4 691.8 R F2(ps)2.707 E F1 .206 +(command to \214nd out just what it')2.707 F(s)-.55 E(up to.)188.4 703.8 Q EP +%%Page: 47 44 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-47)452.9 60 Q/F1 10/Times-Roman@0 SF 57.78(SMTP If)102 96 R .756 +(set, the code to handle user and serv)3.256 F .756 +(er SMTP will be compiled in.)-.15 F .756(This is)5.756 F 2.507 +(only necessary if your machine has some mailer that speaks SMTP \(this)188.4 +108 R(means most machines e)188.4 120 Q -.15(ve)-.25 G(rywhere\).).15 E(UGL)102 +136.2 Q 30.46(YUUCP If)-1 F 1.023(you ha)3.523 F 1.323 -.15(ve a U)-.2 H 1.024 +(UCP host adjacent to you which is not running a reasonable).15 F -.15(ve)188.4 +148.2 S .112(rsion of).15 F/F2 10/Times-Italic@0 SF(rmail)2.612 E F1 2.612(,y)C +.112(ou will ha)263.026 148.2 R .412 -.15(ve t)-.2 H 2.612(os).15 G .112 +(et this \215ag to include the \231remote from sys-)329.234 148.2 R .031 +(name\232 info on the from line.)188.4 160.2 R .032 +(Otherwise, UUCP gets confused about where the)5.032 F(mail came from.)188.4 +172.2 Q 44.45(USERDB Include)102 188.4 R(the)3.449 E F0(experimental)3.449 E F1 +(Berk)3.449 E(ele)-.1 E 3.449(yu)-.15 G .949(ser information database package.) +341.356 188.4 R(This)5.948 E .27(adds a ne)188.4 200.4 R 2.77(wl)-.25 G -2.15 +-.25(ev e)238.67 200.4 T 2.77(lo).25 G 2.77(fl)262.7 200.4 S .27(ocal name e) +271.58 200.4 R .27(xpansion between aliasing and forw)-.15 F 2.77(arding. It) +-.1 F(also uses the NEWDB package.)188.4 212.4 Q +(This may change in future releases.)5 E(IDENTPR)102 228.6 Q -1.88 -.4(OT O)-.4 +H .376(Compile in the IDENT protocol as de\214ned in RFC 1413.)188.4 228.6 R +.375(This def)5.375 F .375(aults on for)-.1 F 1.053(all systems e)188.4 240.6 R +1.053(xcept Ultrix, which apparently has the interesting \231feature\232 that) +-.15 F .83(when it recei)188.4 252.6 R -.15(ve)-.25 G 3.33(sa\231).15 G .83 +(host unreachable\232 message it closes all open connections)270.18 252.6 R +1.921(to that host.)188.4 264.6 R 1.921(Since some \214re)6.921 F -.1(wa)-.25 G +1.922(ll g).1 F(ate)-.05 E -.1(wa)-.25 G 1.922 +(ys send this error code when you).1 F 2.055 +(access an unauthorized port \(such as 113, used by IDENT\), Ultrix cannot) +188.4 276.6 R(recei)188.4 288.6 Q .3 -.15(ve e)-.25 H(mail from such hosts.).15 +E F0 2.5(6.3. Con\214guration)87 312.6 R(in sr)2.5 E(c/conf)-.18 E(.c)-.15 E F1 +(The follo)127 328.8 Q(wing changes can be made in conf.c.)-.25 E F0 2.5 +(6.3.1. Built-in)102 352.8 R(Header Semantics)2.5 E F1 1.248 +(Not all header semantics are de\214ned in the con\214guration \214le.)142 369 +R 1.248(Header lines that should)6.248 F .305(only be included by certain mail\ +ers \(as well as other more obscure semantics\) must be speci\214ed)117 381 R +.046(in the)117 393 R F2(HdrInfo)2.546 E F1 .046(table in)2.546 F F2(conf)2.546 +E(.c)-.15 E F1 5.046(.T)C .047 +(his table contains the header name \(which should be in all lo)246.836 393 R +(wer)-.25 E(case\) and a set of header control \215ags \(described belo)117 405 +Q(w\), The \215ags are:)-.25 E(H_A)117 421.2 Q 30.97(CHECK Normally)-.4 F .007 +(when the check is made to see if a header line is compatible with)2.508 F 2.94 +(am)203.4 433.2 S(ailer)218.56 433.2 Q(,)-.4 E F2(sendmail)2.94 E F1 .441 +(will not delete an e)2.94 F .441(xisting line.)-.15 F .441 +(If this \215ag is set,)5.441 F F2(send-)2.941 E(mail)203.4 445.2 Q F1 .152 +(will delete e)2.652 F -.15(ve)-.25 G 2.652(ne).15 G .152 +(xisting header lines.)293.998 445.2 R .152 +(That is, if this bit is set and the)5.152 F 1.425(mailer does not ha)203.4 +457.2 R 1.725 -.15(ve \215)-.2 H 1.425 +(ag bits set that intersect with the required mailer).15 F 2.204 +(\215ags in the header de\214nition in sendmail.cf, the header line is)203.4 +469.2 R F2(always)4.703 E F1(deleted.)203.4 481.2 Q 51.13(H_EOH If)117 497.4 R +.206(this header \214eld is set, treat it lik)2.705 F 2.706(eab)-.1 G .206 +(lank line, i.e., it will signal the end)363.948 497.4 R +(of the header and the be)203.4 509.4 Q(ginning of the message te)-.15 E(xt.) +-.15 E 39.45(H_FORCE Add)117 525.6 R 2.039(this header entry e)4.539 F -.15(ve) +-.25 G 4.539(ni).15 G 4.539(fo)326.225 525.6 S 2.038(ne e)339.094 525.6 R 2.038 +(xisted in the message before.)-.15 F 2.038(If a)7.038 F 2.188 +(header entry does not ha)203.4 537.6 R 2.488 -.15(ve t)-.2 H 2.188 +(his bit set,).15 F F2(sendmail)4.688 E F1 2.189(will not add another)4.689 F +.62(header line if a header line of this name already e)203.4 549.6 R 3.12 +(xisted. This)-.15 F -.1(wo)3.12 G .62(uld nor).1 F(-)-.2 E +(mally be used to stamp the message by e)203.4 561.6 Q -.15(ve)-.25 G +(ryone who handled it.).15 E(H_TRA)117 577.8 Q 39.3(CE If)-.4 F 1.043 +(set, this is a timestamp \(trace\) \214eld.)3.543 F 1.044 +(If the number of trace \214elds in a)6.043 F .706(message e)203.4 589.8 R .705 +(xceeds a preset amount the message is returned on the assump-)-.15 F +(tion that it has an aliasing loop.)203.4 601.8 Q 46.67(H_RCPT If)117 618 R +.332(set, this \214eld contains recipient addresses.)2.832 F .332 +(This is used by the)5.332 F F0<ad74>2.832 E F1 .333(\215ag to)2.833 F 1.349 +(determine who to send to when it is collecting recipients from the mes-)203.4 +630 R(sage.)203.4 642 Q(H_FR)117 658.2 Q 43.74(OM This)-.4 F 1.673 +(\215ag indicates that this \214eld speci\214es a sender)4.173 F 6.674(.T)-.55 +G 1.674(he order of these)432.058 658.2 R .883(\214elds in the)203.4 670.2 R F2 +(HdrInfo)3.383 E F1 .883(table speci\214es)3.383 F F2(sendmail')3.383 E(s)-.4 E +F1 .883(preference for which \214eld)3.383 F(to return error messages to.)203.4 +682.2 Q(Let')117 698.4 Q 2.5(sl)-.55 G(ook at a sample)142.28 698.4 Q F2 +(HdrInfo)2.5 E F1(speci\214cation:)2.5 E EP +%%Page: 48 45 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-48 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF(struct hdrinfo) +157 96 Q(HdrInfo[] =)258.19 96 Q({)157 108 Q +(/* originator \214elds, most to least signi\214cant)189.5 120 Q(*/)5 E 14.72 +("resent-sender", H_FR)177 132 R(OM,)-.4 E 21.38("resent-from", H_FR)177 144 R +(OM,)-.4 E 41.93("sender", H_FR)177 156 R(OM,)-.4 E 48.59("from", H_FR)177 168 +R(OM,)-.4 E 29.15("full-name", H_A)177 180 R(CHECK,)-.4 E +(/* destination \214elds */)189.5 192 Q 60.25("to", H_RCPT)177 204 R(,)-.74 E +33.04("resent-to", H_RCPT)177 216 R(,)-.74 E 59.15("cc", H_RCPT)177 228 R(,) +-.74 E(/* message identi\214cation and control */)189.5 240 Q 34.15 +("message", H_EOH,)177 252 R("te)177 264 Q 53.18(xt", H_EOH,)-.15 F +(/* trace \214elds */)189.5 276 Q("recei)177 288 Q -.15(ve)-.25 G 34.56 +(d", H_TRA).15 F(CE|H_FORCE,)-.4 E 49.53(NULL, 0,)177 312 R(};)157 324 Q 2.435 +(This structure indicates that the \231T)117 340.2 R 2.435 +(o:\232, \231Resent-T)-.8 F 2.435 +(o:\232, and \231Cc:\232 \214elds all specify recipient)-.8 F 3.162 +(addresses. An)117 352.2 R 3.162<7999>-.15 G .661(Full-Name:\232 \214eld will \ +be deleted unless the required mailer \215ag \(indicated in)188.154 352.2 R +.245(the con\214guration \214le\) is speci\214ed.)117 364.2 R .245 +(The \231Message:\232 and \231T)5.245 F -.15(ex)-.7 G .246 +(t:\232 \214elds will terminate the header;).15 F 1.936 +(these are used by random dissenters around the netw)117 376.2 R 1.936(ork w) +-.1 F 4.436(orld. The)-.1 F(\231Recei)4.436 E -.15(ve)-.25 G 1.936 +(d:\232 \214eld will).15 F(al)117 388.2 Q -.1(wa)-.1 G +(ys be added, and can be used to trace messages.).1 E .445 +(There are a number of important points here.)142 404.4 R .446 +(First, header \214elds are not added automati-)5.446 F .657 +(cally just because the)117 416.4 R 3.157(ya)-.15 G .657(re in the)216.678 +416.4 R/F2 10/Times-Italic@0 SF(HdrInfo)3.157 E F1 .657(structure; the)3.157 F +3.157(ym)-.15 G .656(ust be speci\214ed in the con\214guration)358.23 416.4 R +.727(\214le in order to be added to the message.)117 428.4 R(An)5.728 E 3.228 +(yh)-.15 G .728(eader \214elds mentioned in the con\214guration \214le)312.982 +428.4 R -.2(bu)117 440.4 S 3.24(tn).2 G .74(ot mentioned in the)137.82 440.4 R +F2(HdrInfo)3.24 E F1 .74(structure ha)3.24 F 1.04 -.15(ve d)-.2 H(ef).15 E .74 +(ault processing performed; that is, the)-.1 F 3.24(ya)-.15 G(re)496.23 440.4 Q +1.374(added unless the)117 452.4 R 3.874(yw)-.15 G 1.374 +(ere in the message already)201.792 452.4 R 6.375(.S)-.65 G 1.375(econd, the) +326.595 452.4 R F2(HdrInfo)3.875 E F1 1.375(structure only speci\214es)3.875 F +.324 +(cliched processing; certain headers are processed specially by ad hoc code re) +117 464.4 R -.05(ga)-.15 G .324(rdless of the sta-).05 F .48 +(tus speci\214ed in)117 476.4 R F2(HdrInfo)2.98 E F1 5.48(.F)C .481(or e)226.55 +476.4 R .481(xample, the \231Sender:\232 and \231From:\232 \214elds are al)-.15 +F -.1(wa)-.1 G .481(ys scanned on).1 F(ARP)117 490.4 Q .75 +(ANET mail to determine the sender)-.92 F/F3 7/Times-Roman@0 SF(12)282.31 486.4 +Q F1 3.251(;t)289.31 490.4 S .751 +(his is used to perform the \231return to sender\232 func-)298.121 490.4 R +2.977(tion. The)117 502.4 R .476(\231From:\232 and \231Full-Name:\232 \214elds\ + are used to determine the full name of the sender if)2.977 F +(possible; this is stored in the macro)117 514.4 Q F0($x)2.5 E F1 +(and used in a number of w)2.5 E(ays.)-.1 E F0 2.5(6.3.2. Restricting)102 538.4 +R(Use of Email)2.5 E F1 .149 +(If it is necessary to restrict mail through a relay)142 554.6 R 2.649(,t)-.65 +G(he)339.75 554.6 Q F2 -.15(ch)2.65 G(ec).15 E(kcompat)-.2 E F1 .15 +(routine can be modi\214ed.)2.65 F .163(This routine is called for e)117 566.6 +R -.15(ve)-.25 G .163(ry recipient address.).15 F .163(It returns an e)5.163 F +.163(xit status indicating the status of)-.15 F .895(the message.)117 578.6 R +.895(The status)5.895 F/F4 9/Times-Roman@0 SF(EX_OK)3.395 E F1 .895 +(accepts the address,)3.395 F F4(EX_TEMPF)3.395 E(AIL)-.666 E F1 .895 +(queues the message for a)3.395 F .264(later try)117 590.6 R 2.764(,a)-.65 G +.264(nd other v)157.698 590.6 R .264(alues \(commonly)-.25 F F4(EX_UN)2.764 E +-1.215(AVA)-.315 G(ILABLE)1.215 E F1 2.764(\)r)C .264(eject the message.) +358.375 590.6 R .263(It is up to)5.264 F F2 -.15(ch)2.763 G(ec).15 E(k-)-.2 E +(compat)117 602.6 Q F1 .429(to print an error message \(using)2.929 F F2(usr) +2.929 E(err)-.37 E F1 2.929(\)i)C 2.929(ft)315.032 602.6 S .43 +(he message is rejected.)324.071 602.6 R -.15(Fo)5.43 G 2.93(re).15 G(xample,) +443.39 602.6 Q F2 -.15(ch)2.93 G(ec).15 E(k-)-.2 E(compat)117 614.6 Q F1 +(could read:)2.5 E .32 LW 76 669.2 72 669.2 DL 80 669.2 76 669.2 DL 84 669.2 80 +669.2 DL 88 669.2 84 669.2 DL 92 669.2 88 669.2 DL 96 669.2 92 669.2 DL 100 +669.2 96 669.2 DL 104 669.2 100 669.2 DL 108 669.2 104 669.2 DL 112 669.2 108 +669.2 DL 116 669.2 112 669.2 DL 120 669.2 116 669.2 DL 124 669.2 120 669.2 DL +128 669.2 124 669.2 DL 132 669.2 128 669.2 DL 136 669.2 132 669.2 DL 140 669.2 +136 669.2 DL 144 669.2 140 669.2 DL 148 669.2 144 669.2 DL 152 669.2 148 669.2 +DL 156 669.2 152 669.2 DL 160 669.2 156 669.2 DL 164 669.2 160 669.2 DL 168 +669.2 164 669.2 DL 172 669.2 168 669.2 DL 176 669.2 172 669.2 DL 180 669.2 176 +669.2 DL 184 669.2 180 669.2 DL 188 669.2 184 669.2 DL 192 669.2 188 669.2 DL +196 669.2 192 669.2 DL 200 669.2 196 669.2 DL 204 669.2 200 669.2 DL 208 669.2 +204 669.2 DL 212 669.2 208 669.2 DL 216 669.2 212 669.2 DL/F5 5/Times-Roman@0 +SF(12)93.6 679.6 Q/F6 8/Times-Roman@0 SF(Actually)3.2 I 2.632(,t)-.52 G .632 +(his is no longer true in SMTP; this information is contained in the en)132.488 +682.8 R -.12(ve)-.32 G 2.631(lope. The).12 F .631(older ARP)2.631 F .631 +(ANET protocols did)-.736 F(not completely distinguish en)72 692.4 Q -.12(ve) +-.32 G(lope from header).12 E(.)-.44 E EP +%%Page: 49 46 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-49)452.9 60 Q/F1 9/Times-Roman@0 SF(int)157 94.8 Q +(checkcompat\(to, e\))157 105.6 Q(re)175 116.4 Q(gister ADDRESS *to;)-.135 E +(re)175 127.2 Q(gister ENVELOPE *e;)-.135 E({)157 138 Q(re)175 148.8 Q +(gister ST)-.135 E(AB *s;)-.837 E 2.25(s=s)175 170.4 S(tab\("pri)191.578 170.4 +Q -.225(va)-.225 G(te", ST_MAILER, ST_FIND\);).225 E +(if \(s != NULL && e\255>e_from.q_mailer != LocalMailer &&)175 181.2 Q +(to->q_mailer == s->s_mailer\))184 192 Q({)175 202.8 Q(usrerr\("No pri)193 +213.6 Q -.225(va)-.225 G(te net mail allo).225 E(wed through this machine"\);) +-.225 E(return \(EX_UN)193 224.4 Q -1.215(AVA)-.315 G(ILABLE\);)1.215 E(})175 +235.2 Q(if \(MsgSize > 50000 && to\255>q_mailer != LocalMailer\))175 246 Q({) +175 256.8 Q(usrerr\("Message too lar)193 267.6 Q(ge for non-local deli)-.162 E +-.135(ve)-.225 G(ry"\);).135 E(NoReturn = TR)193 278.4 Q(UE;)-.36 E +(return \(EX_UN)193 289.2 Q -1.215(AVA)-.315 G(ILABLE\);)1.215 E(})175 300 Q +(return \(EX_OK\);)175 310.8 Q(})157 321.6 Q/F2 10/Times-Roman@0 SF .205 +(This w)117 337.8 R .205 +(ould reject messages greater than 50000 bytes unless the)-.1 F 2.705(yw)-.15 G +.205(ere local.)387.09 337.8 R(The)5.205 E/F3 10/Times-Italic@0 SF(NoReturn) +2.705 E F2(\215ag)2.705 E 1.196(can be sent to suppress the return of the actu\ +al body of the message in the error return.)117 349.8 R(The)6.197 E(actual use\ + of this routine is highly dependent on the implementation, and use should be \ +limited.)117 361.8 Q F0 2.5(6.3.3. Load)102 385.8 R -.6 -1(Av e)2.5 H +(rage Computation)1 E F2 .18(The routine)142 402 R F3 -.1(ge)2.68 G(tla).1 E F2 +.18(should return an approximation of the current system load a)2.68 F -.15(ve) +-.2 G .18(rage as an).15 F(inte)117 414 Q(ger)-.15 E 5(.T)-.55 G +(here are four v)157.68 414 Q +(ersions included on compilation \215ags as described abo)-.15 E -.15(ve)-.15 G +(.).15 E F0 2.5(6.3.4. New)102 438 R(Database Map Classes)2.5 E F2(Ne)142 454.2 +Q 2.875(wk)-.25 G .675 -.15(ey m)168.405 454.2 T .375(aps can be added by crea\ +ting a class initialization function and a lookup func-).15 F 2.5(tion. These) +117 466.2 R(are then added to the routine)2.5 E F3(setupmaps.)2.5 E F2 +(The initialization function is called as)142 482.4 Q F3(xxx)157 498.6 Q F2 +(_map_init\(MAP *map, char *mapname, char *ar)A(gs\))-.18 E(The)117 514.8 Q F3 +(map)2.555 E F2 .055(is an internal data structure.)2.555 F(The)5.055 E F3 +(mapname)2.555 E F2 .054(is the name of the map \(used for error mes-)2.554 F +2.819(sages\). The)117 526.8 R F3(ar)2.819 E(gs)-.37 E F2 .32(is a pointer to \ +the rest of the con\214guration \214le line; \215ags and \214lenames can be) +2.819 F -.15(ex)117 538.8 S .675(tracted from this line.).15 F .675 +(The initialization function must return)5.675 F F1(TR)3.175 E(UE)-.36 E F2 +.674(if it successfully opened)3.174 F(the map,)117 550.8 Q F1 -.666(FA)2.5 G +(LSE).666 E F2(otherwise.)2.5 E(The lookup function is called as)142 567 Q F3 +(xxx)157 583.2 Q F2(_map_lookup\(MAP *map, char b)A(uf[], int b)-.2 E +(ufsize, char **a)-.2 E 1.3 -.65(v, i)-.2 H(nt *statp\)).65 E(The)117 599.4 Q +F3(map)3.475 E F2 .975(de\214nes the map internally)3.475 F 5.975(.T)-.65 G +.975(he parameters)277.18 599.4 R F3 -.2(bu)3.475 G(f).2 E F2(and)3.475 E F3 +-.2(bu)3.475 G(fsize).2 E F2(ha)3.476 E 1.276 -.15(ve t)-.2 H .976(he input k) +.15 F -.15(ey)-.1 G 5.976(.T)-.5 G(his)492.33 599.4 Q .043 +(may be \(and often is\) used destructi)117 611.4 R -.15(ve)-.25 G(ly).15 E +5.043(.T)-.65 G(he)289.831 611.4 Q F3(av)2.543 E F2 .043(is a list of ar)2.543 +F .042(guments passed in from the re)-.18 F(write)-.25 E 3.654(line. The)117 +623.4 R 1.154(lookup function should return a pointer to the ne)3.654 F 3.655 +(wv)-.25 G 3.655(alue. IF)378.335 623.4 R 1.155(the map lookup f)3.655 F(ails,) +-.1 E F3(*statp)117 635.4 Q F2 1.272(should be set to an e)3.772 F 1.272 +(xit status code; in particular)-.15 F 3.772(,i)-.4 G 3.771(ts)357.652 635.4 S +1.271(hould be set to)368.093 635.4 R F1(EX_TEMPF)3.771 E(AIL)-.666 E F2(if) +3.771 E(reco)117 647.4 Q -.15(ve)-.15 G(ry is to be attempted by the higher le) +.15 E -.15(ve)-.25 G 2.5(lc).15 G(ode.)308.76 647.4 Q F0 2.5(6.3.5. Queueing) +102 671.4 R(Function)2.5 E F2 .782(The routine)142 687.6 R F3(shouldqueue)3.282 +E F2 .783(is called to decide if a message should be queued or processed)3.283 +F(immediately)117 699.6 Q 6.619(.T)-.65 G 1.618 +(ypically this compares the message priority to the current load a)180.779 +699.6 R -.15(ve)-.2 G 4.118(rage. The).15 F(def)117 711.6 Q +(ault de\214nition is:)-.1 E EP +%%Page: 50 47 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-50 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF(bool)157 96 Q +(shouldqueue\(pri, ctime\))157 108 Q(long pri;)175 120 Q(time_t ctime;)175 132 +Q({)157 144 Q(if \(CurrentLA < QueueLA\))175 156 Q(return \(F)193 168 Q +(ALSE\);)-.74 E(if \(CurrentLA >= RefuseLA\))175 180 Q(return \(TR)193 192 Q +(UE\);)-.4 E(return \(pri > \(QueueF)175 204 Q +(actor / \(CurrentLA \255 QueueLA + 1\)\)\);)-.15 E(})157 216 Q 2.062 +(If the current load a)117 232.2 R -.15(ve)-.2 G 2.062(rage \(global v).15 F +(ariable)-.25 E/F2 10/Times-Italic@0 SF(Curr)4.562 E(entLA)-.37 E F1 4.562(,w)C +2.062(hich is set before this function is)361.636 232.2 R 1.058 +(called\) is less than the lo)117 244.2 R 3.558(wt)-.25 G 1.058 +(hreshold load a)234.198 244.2 R -.15(ve)-.2 G 1.058(rage \(option).15 F F0(x) +3.557 E F1 3.557(,v)C(ariable)375.526 244.2 Q F2(QueueLA)3.557 E F1(\),)A F2 +(shouldqueue)3.557 E F1(returns)117 256.2 Q/F3 9/Times-Roman@0 SF -.666(FA) +2.586 G(LSE).666 E F1 .086(immediately \(that is, it should)2.586 F F2(not) +2.586 E F1 2.586(queue\). If)2.586 F .086(the current load a)2.586 F -.15(ve) +-.2 G .087(rage e).15 F .087(xceeds the)-.15 F .588(high threshold load a)117 +268.2 R -.15(ve)-.2 G .588(rage \(option).15 F F0(X)3.087 E F1 3.087(,v)C +(ariable)281.846 268.2 Q F2(RefuseLA)3.087 E F1(\),)A F2(shouldqueue)3.087 E F1 +(returns)3.087 E F3(TR)3.087 E(UE)-.36 E F1(immedi-)3.087 E(ately)117 280.2 Q +7.125(.O)-.65 G 2.125 +(therwise, it computes the function based on the message priority)152.635 280.2 +R 4.626(,t)-.65 G 2.126(he queue f)438.208 280.2 R(actor)-.1 E(\(option)117 +292.2 Q F0(q)2.5 E F1 2.5(,g)C(lobal v)163.95 292.2 Q(ariable)-.25 E F2(QueueF) +2.5 E(actor)-.75 E F1(\), and the current and threshold load a)A -.15(ve)-.2 G +(rages.).15 E 1.067(An implementation wishing to tak)142 308.4 R 3.567(et)-.1 G +1.066(he actual age of the message into account can also)293.625 308.4 R 1.41 +(use the)117 320.4 R F2(ctime)3.91 E F1(parameter)3.91 E 3.91(,w)-.4 G 1.41 +(hich is the time that the message w)229.15 320.4 R 1.41 +(as \214rst submitted to)-.1 F F2(sendmail)3.91 E F1(.)A .929(Note that the)117 +332.4 R F2(pri)3.428 E F1 .928 +(parameter is already weighted by the number of times the message has been) +3.428 F .395(tried \(although this tends to lo)117 344.4 R .395 +(wer the priority of the message with time\); the e)-.25 F .395 +(xpectation is that)-.15 F(the)117 356.4 Q F2(ctime)2.674 E F1 -.1(wo)2.674 G +.174(uld be used as an \231escape clause\232 to ensure that messages are e).1 F +-.15(ve)-.25 G .174(ntually processed.).15 F F0 2.5(6.3.6. Refusing)102 380.4 R +(Incoming SMTP Connections)2.5 E F1 1.148(The function)142 396.6 R F2 -.37(re) +3.648 G(fuseconnections).37 E F1(returns)3.648 E F3(TR)3.648 E(UE)-.36 E F1 +1.148(if incoming SMTP connections should be)3.648 F 3.564(refused. The)117 +408.6 R 1.063(current implementation is based e)3.563 F(xclusi)-.15 E -.15(ve) +-.25 G 1.063(ly on the current load a).15 F -.15(ve)-.2 G 1.063(rage and the) +.15 F(refuse load a)117 420.6 Q -.15(ve)-.2 G(rage option \(option).15 E F0(X) +2.5 E F1 2.5(,g)C(lobal v)273.56 420.6 Q(ariable)-.25 E F2(RefuseLA)2.5 E F1 +(\):)A(bool)157 436.8 Q(refuseconnections\(\))157 448.8 Q({)157 460.8 Q +(return \(CurrentLA >= RefuseLA\);)175 472.8 Q(})157 484.8 Q 2.5(Am)117 501 S +(ore cle)134.5 501 Q -.15(ve)-.25 G 2.5(ri).15 G +(mplementation could look at more system resources.)179.08 501 Q F0 2.5 +(6.3.7. Load)102 525 R -.6 -1(Av e)2.5 H(rage Computation)1 E F1 .243 +(The routine)142 541.2 R F2 -.1(ge)2.743 G(tla).1 E F1 .243 +(returns the current load a)2.743 F -.15(ve)-.2 G .243 +(rage \(as a rounded inte).15 F 2.743(ger\). The)-.15 F(distrib)2.744 E(ution) +-.2 E(includes se)117 553.2 Q -.15(ve)-.25 G(ral possible implementations.).15 +E F0 2.5(6.4. Con\214guration)87 577.2 R(in sr)2.5 E(c/daemon.c)-.18 E F1 .4 +(The \214le)127 593.4 R F2(sr)2.9 E(c/daemon.c)-.37 E F1 .4 +(contains a number of routines that are dependent on the local netw)2.9 F(ork-) +-.1 E(ing en)102 605.4 Q 2.5(vironment. The)-.4 F -.15(ve)2.5 G +(rsion supplied assumes you ha).15 E .3 -.15(ve B)-.2 H(SD style sock).15 E +(ets.)-.1 E 2.16(In pre)127 621.6 R 2.16 +(vious releases, we recommended that you modify the routine)-.25 F F2 +(maphostname)4.66 E F1 2.16(if you)4.66 F -.1(wa)102 633.6 S 1.919 +(nted to generalize).1 F F0($[)4.418 E F1(...)4.418 E F0($])4.418 E F1 4.418 +(lookups. W)4.418 F 4.418(en)-.8 G 2.418 -.25(ow r)293.906 633.6 T 1.918 +(ecommend that you create a ne).25 F 4.418(wk)-.25 G -.15(ey)463.632 633.6 S +1.918(ed map).15 F(instead.)102 645.6 Q F0 2.5(7. CHANGES)72 669.6 R +(IN VERSION 8)2.5 E F1 2.661(The follo)112 685.8 R 2.662 +(wing summarizes changes since the last commonly a)-.25 F -.25(va)-.2 G 2.662 +(ilable v).25 F 2.662(ersion of)-.15 F F0(sendmail)5.162 E F1(\(5.67\):)87 +697.8 Q EP +%%Page: 51 48 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-51)452.9 60 Q 2.5(7.1. Connection)87 96 R(Caching)2.5 E/F1 10 +/Times-Roman@0 SF .398(Instead of closing SMTP connections immediately)127 +112.2 R 2.897(,t)-.65 G .397(hose connections are cached for possible)339.005 +112.2 R .597(future use.)102 124.2 R .597(The adv)5.597 F .597 +(ent of MX records made this ef)-.15 F(fecti)-.25 E .897 -.15(ve f)-.25 H .598 +(or mailing lists; in addition, substantial).15 F(performance impro)102 136.2 Q +-.15(ve)-.15 G(ments can be e).15 E(xpected for queue processing.)-.15 E F0 2.5 +(7.2. MX)87 160.2 R(Piggybacking)2.5 E F1 1.258(If tw)127 176.4 R 3.757(oh)-.1 +G 1.257(osts with dif)161.075 176.4 R 1.257 +(ferent names in a single message happen to ha)-.25 F 1.557 -.15(ve t)-.2 H +1.257(he same set of MX).15 F .94(hosts, the)102 188.4 R 3.44(yc)-.15 G .94 +(an be sent in the same transaction.)153.45 188.4 R -1.11(Ve)5.94 G .94 +(rsion 8 notices this and tries to batch the mes-)1.11 F(sages.)102 200.4 Q F0 +2.5(7.3. RFC)87 224.4 R(1123 Compliance)2.5 E F1 3.463(An)127 240.6 S .963 +(umber of changes ha)142.683 240.6 R 1.262 -.15(ve b)-.2 H .962 +(een made to mak).15 F(e)-.1 E/F2 10/Times-Italic@0 SF(sendmail)3.462 E F1 .962 +(\231conditionally compliant\232 \(that is,)3.462 F F2(sendmail)102 252.6 Q F1 +.049(satis\214es all of the \231MUST\232 clauses and most b)2.549 F .05 +(ut not all of the \231SHOULD\232 clauses in RFC)-.2 F(1123\).)102 264.6 Q +(The major areas of change are \(numbers are RFC 1123 section numbers\):)127 +280.8 Q 15(5.2.7 Response)102 297 R(to RCPT command is f)2.5 E(ast.)-.1 E 15 +(5.2.8 Numeric)102 313.2 R(IP addresses are logged in Recei)2.5 E -.15(ve)-.25 +G(d: lines.).15 E 10(5.2.17 Self)102 329.4 R +(domain literal is properly handled.)2.5 E 15(5.3.2 Better)102 345.6 R +(control o)2.5 E -.15(ve)-.15 G 2.5(ri).15 G(ndi)220.02 345.6 Q +(vidual timeouts.)-.25 E 15(5.3.3 Error)102 361.8 R +(messages are sent as \231From:<>\232.)2.5 E 15(5.3.3 Error)102 378 R +(messages are ne)2.5 E -.15(ve)-.25 G 2.5(rs).15 G(ent to \231<>\232.)246.28 +378 Q 15(5.3.3 Route-addrs)102 394.2 R(are pruned.)2.5 E(The areas in which)102 +410.4 Q F2(sendmail)2.5 E F1(is not \231unconditionally compliant\232 are:)2.5 +E(5.2.6)102 426.6 Q F2(Sendmail)139.5 426.6 Q F1(does do header munging.)2.5 E +(5.2.10)102 442.8 Q F2(Sendmail)139.5 442.8 Q F1(doesn')2.5 E 2.5(ta)-.18 G -.1 +(lwa)215.42 442.8 S(ys use the e).1 E(xact SMTP message te)-.15 E +(xt as listed in RFC 821.)-.15 E(5.3.1.1)102 459 Q F2(Sendmail)139.5 459 Q F1 +(doesn')2.5 E 2.5(tg)-.18 G +(uarantee only one connect for each host in queue runs.)215.98 459 Q(5.3.1.1) +102 475.2 Q F2(Sendmail)139.5 475.2 Q F1(doesn')2.5 E 2.5(ta)-.18 G -.1(lwa) +215.42 475.2 S(ys pro).1 E(vide adequate concurrenc)-.15 E 2.5(yl)-.15 G +(imits.)366.54 475.2 Q F0 2.5(7.4. Extended)87 499.2 R(SMTP Support)2.5 E F1 +-1.11(Ve)127 515.4 S .155(rsion 8 includes both sending and recei)1.11 F .154 +(ving support for Extended SMTP support as de\214ned)-.25 F(by RFC 1425 \(basi\ +c\) and RFC 1427 \(SIZE\); and limited support for RFC 1426 \(BOD)102 527.4 Q +(Y\).)-.55 E F0 2.5(7.5. Eight-Bit)87 551.4 R(Clean)2.5 E F1(Pre)127 567.6 Q +1.104(vious v)-.25 F 1.104(ersions of)-.15 F F0(sendmail)3.604 E F1 1.104 +(used the 0200 bit for quoting.)3.604 F 1.105(This v)6.104 F 1.105(ersion a) +-.15 F -.2(vo)-.2 G 1.105(ids that use.).2 F(Ho)102 579.6 Q(we)-.25 E -.15(ve) +-.25 G .8 -.4(r, f).15 H +(or compatibility with RFC 822, you can set option `7' to get se).4 E -.15(ve) +-.25 G 2.5(nb).15 G(it stripping.)418.86 579.6 Q(Indi)127 595.8 Q +(vidual mailers can still produce se)-.25 E -.15(ve)-.25 G 2.5(nb).15 G +(it out put using the `7' mailer \215ag.)300.77 595.8 Q F0 2.5(7.6. User)87 +619.8 R(Database)2.5 E F1 1.073(The user database is an as-yet e)127 636 R +1.072(xperimental attempt to pro)-.15 F 1.072(vide uni\214ed lar)-.15 F 1.072 +(ge-site name sup-)-.18 F 2.5(port. W)102 648 R 2.5(ea)-.8 G +(re installing it at Berk)145.63 648 Q(ele)-.1 E(y; future v)-.15 E +(ersions may sho)-.15 E 2.5(ws)-.25 G(igni\214cant modi\214cations.)363.57 648 +Q F0 2.5(7.7. Impr)87 672 R -.1(ove)-.18 G 2.5(dB).1 G(IND Support)158.01 672 Q +F1 .489(The BIND support, particularly for MX records, had a number of anno)127 +688.2 R .49(ying \231features\232 which)-.1 F(ha)102 700.2 Q 1.212 -.15(ve b) +-.2 H .912(een remo).15 F -.15(ve)-.15 G 3.412(di).15 G 3.412(nt)187.116 700.2 +S .912(his release.)198.308 700.2 R .912(In particular)5.912 F 3.412(,t)-.4 G +.912(hese more tightly bind \(pun intended\) the name)307.916 700.2 R(serv)102 +712.2 Q(er to sendmail, so that the name serv)-.15 E +(er resolution rules are incorporated directly into)-.15 E F0(sendmail)2.5 E F1 +(.)A EP +%%Page: 52 49 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-52 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E 2.5(7.8. K)87 96 R(ey)-.25 E(ed Files) +-.1 E/F1 10/Times-Roman@0 SF .206(Generalized k)127 112.2 R -.15(ey)-.1 G .206 +(ed \214les is an idea tak).15 F .206(en directly from)-.1 F/F2 9/Times-Roman@0 +SF(ID)2.706 E(A)-.36 E F0(sendmail)2.706 E F1 .207(\(albeit with a completely) +2.706 F(dif)102 124.2 Q(ferent implementation\).)-.25 E(The)5 E 2.5(yc)-.15 G +(an be useful on lar)239.63 124.2 Q(ge sites.)-.18 E -1.11(Ve)127 140.4 S +(rsion 8 also understands YP)1.11 E(.)-1.11 E F0 2.5(7.9. Multi-W)87 164.4 R +(ord Classes)-.75 E F1(Classes can no)127 180.6 Q 2.5(wb)-.25 G 2.5(em)200.35 +180.6 S(ultiple w)215.07 180.6 Q 2.5(ords. F)-.1 F(or e)-.15 E(xample,)-.15 E +(CShofmann.CS.Berk)142 196.8 Q(ele)-.1 E -.65(y.)-.15 G(EDU).65 E(allo)102 213 +Q 2.664(ws you to match the entire string \231hofmann.CS.Berk)-.25 F(ele)-.1 E +-.65(y.)-.15 G 2.663(EDU\232 using the single construct).65 F(\231$=S\232.)102 +225 Q F0 2.5(7.10. Deferr)87 249 R(ed Macr)-.18 E 2.5(oE)-.18 G(xpansion)189.94 +249 Q F1(The)127 265.2 Q F0($&)2.5 E/F3 10/Times-Italic@0 SF(x)A F1 +(construct has been adopted from)2.5 E F2(ID)2.5 E(A)-.36 E F1(.)A F0 2.5 +(7.11. IDENT)87 289.2 R(Pr)2.5 E(otocol Support)-.18 E F1 +(The IDENT protocol as de\214ned in RFC 1413 is supported.)127 305.4 Q F0 2.5 +(7.12. P)87 329.4 R(arsing Bug Fixes)-.1 E F1 4.03(An)127 345.6 S 1.53 +(umber of small b)143.25 345.6 R 1.53(ugs ha)-.2 F 1.53 +(ving to do with things lik)-.2 F 4.03(eb)-.1 G 1.53 +(ackslash-escaped quotes inside of)364.72 345.6 R(comments ha)102 357.6 Q .3 +-.15(ve b)-.2 H(een \214x).15 E(ed.)-.15 E F0 2.5(7.13. Separate)87 381.6 R(En) +2.5 E -.1(ve)-.4 G(lope/Header Pr).1 E(ocessing)-.18 E F1 .854 +(Since the From: line is passed in separately from the en)127 397.8 R -.15(ve) +-.4 G .854(lope sender).15 F 3.354(,t)-.4 G .854(hese ha)420.978 397.8 R 1.154 +-.15(ve b)-.2 H .854(oth been).15 F .427(made visible; the)102 409.8 R F0($g) +2.927 E F1 .427(macro is set to the en)2.927 F -.15(ve)-.4 G .428 +(lope sender during processing of mailer ar).15 F .428(gument v)-.18 F(ec-)-.15 +E(tors and the header sender during processing of headers.)102 421.8 Q .085 +(It is also possible to specify separate per)127 438 R .085(-mailer en)-.2 F +-.15(ve)-.4 G .084(lope and header processing.).15 F(The)5.084 E F0(S)2.584 E +F1(ender)A(-)-.2 E -.55(RW)102 450 S .512(Set and).55 F F0(R)3.012 E F1 +(ecipientR)A .512(Wset ar)-.55 F .512 +(guments for mailers can be speci\214ed as)-.18 F F3(en)3.013 E(velope/header) +-.4 E F1 .513(to gi)3.013 F .813 -.15(ve d)-.25 H(if-).15 E(ferent re)102 462 Q +(writings for en)-.25 E -.15(ve)-.4 G(lope v).15 E(ersus header addresses.)-.15 +E F0 2.5(7.14. Owner)87 486 R(-List Pr)-.37 E(opagates to En)-.18 E -.1(ve)-.4 +G(lope).1 E F1 1.001(When an alias has an associated o)127 502.2 R 1 +(wner\255list name, that alias is used to change the en)-.25 F -.15(ve)-.4 G +(lope).15 E(sender address.)102 514.2 Q(This will cause do)5 E +(wnstream errors to be returned to that o)-.25 E(wner)-.25 E(.)-.55 E F0 2.5 +(7.15. Dynamic)87 538.2 R(Header Allocation)2.5 E F1(The \214x)127 554.4 Q +(ed size limit on header lines has been eliminated.)-.15 E F0 2.5(7.16. New)87 +578.4 R(Command Line Flags)2.5 E F1(The)127 594.6 Q F0<ad42>2.5 E F1 +(\215ag has been added to pass in body type information.)2.5 E(The)127 610.8 Q +F0<ad70>2.5 E F1(\215ag has been added to pass in protocol information.)2.5 E +(The)127 627 Q F0<ad58>2.6 E F1 .1(\215ag has been added to allo)2.6 F 2.6(wl) +-.25 G .1(ogging of all protocol in and out of)279.89 627 R F3(sendmail)2.6 E +F1 .1(for deb)2.6 F(ug-)-.2 E(ging.)102 639 Q F0 2.5(7.17. Enhanced)87 663 R +(Command Line Flags)2.5 E F1(The)127 679.2 Q F0<ad71>4.007 E F1 1.507(\215ag c\ +an limit limit a queue run to speci\214c recipients, senders, or queue ids usi\ +ng)4.007 F F0(\255qR)102 691.2 Q F3(substring)A F0 2.5<2cad>C(qS)168.41 691.2 Q +F3(substring)A F0 2.5(,o)C 2.5<72ad>226.76 691.2 S(qI)239.4 691.2 Q F3 +(substring)A F0 -.18(re)2.5 G(specti).18 E -.1(ve)-.1 G(ly).1 E(.)-.7 E EP +%%Page: 53 50 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-53)452.9 60 Q 2.5(7.18. New)87 96 R(and Old Con\214guration Line T)2.5 +E(ypes)-.74 E/F1 10/Times-Roman@0 SF(The)127 112.2 Q F0(T)2.766 E F1(\(T)2.766 +E .267(rusted users\) con\214guration line has been deleted.)-.35 F .267 +(It will still be accepted b)5.267 F .267(ut will be)-.2 F(ignored.)102 124.2 Q +(The)127 140.4 Q F0(K)2.5 E F1(line has been added to declare database maps.) +2.5 E(The)127 156.6 Q F0(V)2.5 E F1 +(line has been added to declare the con\214guration v)2.5 E(ersion le)-.15 E +-.15(ve)-.25 G(l.).15 E(The)127 172.8 Q F0(M)2.797 E F1 .296(line has a \231D=\ +\232 \214eld that lets you change into a temporary directory while that mailer) +2.797 F(is running.)102 184.8 Q F0 2.5(7.19. New)87 208.8 R(Options)2.5 E F1 +(Se)127 225 Q -.15(ve)-.25 G .9(ral ne).15 F 3.4(wo)-.25 G .9(ptions ha)184.8 +225 R 1.2 -.15(ve b)-.2 H .9(een added, man).15 F 3.4(yt)-.15 G 3.4(os)314.89 +225 S .9(upport ne)327.18 225 R 3.4(wf)-.25 G .9(eatures, others to allo)379.83 +225 R 3.4(wt)-.25 G(uning)481.22 225 Q 1.187(that w)102 237 R 1.187(as pre)-.1 +F 1.187(viously a)-.25 F -.25(va)-.2 G 1.187(ilable only by recompiling.).25 F +(The)6.186 E 3.686(ya)-.15 G 1.186(re described in detail in Section 5.1.5.) +345.514 237 R(Brie\215y)102 249 Q(,)-.65 E 31(bI)102 265.2 S +(nsist on a minimum number of disk blocks.)141.33 265.2 Q 29.33(CS)102 281.4 S +(et checkpoint interv)143.56 281.4 Q(al.)-.25 E 29.89(ED)102 297.6 S(ef)145.22 +297.6 Q(ault error message.)-.1 E 28.78(GE)102 313.8 S(nable GECOS matching.) +144.11 313.8 Q 31(hM)102 330 S(aximum hop count.)146.89 330 Q 33.22(jS)102 +346.2 S(end errors in MIME-encapsulated format.)143.56 346.2 Q 32.11(JF)102 +362.4 S(orw)143.41 362.4 Q(ard \214le path.)-.1 E 31(kC)102 378.6 S +(onnection cache size)144.67 378.6 Q 28.78(KC)102 394.8 S +(onnection cache lifetime.)144.67 394.8 Q 33.22(lE)102 411 S .333 +(nable Errors-T)144.11 411 R .333(o: header)-.8 F 5.334(.T)-.55 G .334 +(hese headers violate RFC 1123; this option is included to pro-)252.89 411 R +(vide back compatibility with old v)138 423 Q(ersions of sendmail.)-.15 E 28.78 +(OS)102 439.2 S +(et incoming SMTP daemon options, such as an alternate SMTP port.)143.56 439.2 +Q 31(pP)102 455.4 S(ri)143.56 455.4 Q -.25(va)-.25 G .3 -.15(cy o).25 H +(ptions.).15 E 29.33(RD)102 471.6 S(on')145.22 471.6 Q 2.5(tp)-.18 G +(rune route-addrs.)168.65 471.6 Q 28.78(UU)102 487.8 S(ser database spec.) +145.22 487.8 Q 28.78(VF)102 504 S(allback \231MX\232 host.)143.41 504 Q 31(7D) +102 520.2 S 2.5(on)145.22 520.2 S(ot run eight bit clean.)157.72 520.2 Q F0 2.5 +(7.20. Extended)87 544.2 R(Options)2.5 E F1(The)127 560.4 Q F0(r)3.764 E F1 +1.264(\(read timeout\),)3.764 F F0(I)3.764 E F1 1.264(\(use BIND\), and)3.764 F +F0(T)3.764 E F1 1.264(\(queue timeout\) options ha)3.764 F 1.564 -.15(ve b)-.2 +H 1.264(een e).15 F 1.264(xtended to)-.15 F(pass in more information.)102 572.4 +Q F0 2.5(7.21. New)87 596.4 R(Mailer Flags)2.5 E F1(Se)127 612.6 Q -.15(ve)-.25 +G(ral ne).15 E 2.5(wm)-.25 G(ailer \215ags ha)185.78 612.6 Q .3 -.15(ve b)-.2 H +(een added.).15 E 31.56(aT)102 628.8 S .636 +(ry to use ESMTP when creating a connection.)143.76 628.8 R .636 +(If this is not set,)5.636 F/F2 10/Times-Italic@0 SF(sendmail)3.136 E F1 .636 +(will still try if)3.136 F .221(the other end hints that it kno)138 640.8 R .22 +(ws about ESMTP in its greeting message; this \215ag says to try)-.25 F -2.15 +-.25(ev e)138 652.8 T 2.595(ni).25 G 2.595(fi)161.855 652.8 S 2.595(td)170.56 +652.8 S(oesn')180.935 652.8 Q 2.595(th)-.18 G 2.595(int. If)212.79 652.8 R .095 +(the EHLO \(e)2.595 F .095(xtended hello\) command f)-.15 F(ails,)-.1 E F2 +(sendmail)2.596 E F1 -.1(fa)2.596 G .096(lls back to).1 F(old SMTP)138 664.8 Q +(.)-1.11 E 31(bE)102 681 S +(nsure that there is a blank line at the end of all messages.)144.11 681 Q +31.56(cS)102 697.2 S .68(trip all comments from addresses; this should only be\ + used as a last resort when dealing)143.56 697.2 R(with crank)138 709.2 Q 2.5 +(ym)-.15 G(ailers.)195.62 709.2 Q EP +%%Page: 54 51 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 193.36(SMM:08-54 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF 31(gN)102 96 S +-2.15 -.25(ev e)145.22 96 T 2.64(ru).25 G .14(se the null sender as the en) +169.67 96 R -.15(ve)-.4 G .141(lope sender).15 F 2.641(,e)-.4 G -.15(ve)341.495 +96 S 2.641(nw).15 G .141(hen running SMTP)365.646 96 R 5.141(.A)-1.11 G .141 +(lthough this)456.349 96 R 1.521(violates RFC 1123, it may be necessary when y\ +ou must deal with some obnoxious old)138 108 R(hosts.)138 120 Q 31(7S)102 136.2 +S(trip all output to 7 bits.)143.56 136.2 Q F0 2.5(7.22. New)87 160.2 R(Pr)2.5 +E(e-De\214ned Macr)-.18 E(os)-.18 E F1(The follo)127 176.4 Q +(wing macros are pre-de\214ned:)-.25 E 23.5($k The)102 192.6 R +(UUCP node name, nominally from)2.5 E/F2 10/Times-Italic@0 SF(uname)2.5 E F1 +(\(2\) call.)A 20.72($m The)102 208.8 R(domain part of our full hostname.)2.5 E +23.5($_ The)102 225 R(RFC 1413-pro)2.5 E(vided sender address.)-.15 E F0 2.5 +(7.23. New)87 249 R(LHS T)2.5 E(ok)-.92 E(en)-.1 E F1 -1.11(Ve)127 265.2 S +1.375(rsion 8 allo)1.11 F(ws)-.25 E F0($@)3.875 E F1 1.376 +(on the Left Hand Side of an \231R\232 line to match zero tok)3.875 F 3.876 +(ens. This)-.1 F(is)3.876 E(intended to be used to match the null input.)102 +277.2 Q F0 2.5(7.24. Bigger)87 301.2 R(Defaults)2.5 E F1 -1.11(Ve)127 317.4 S +1.284(rsion 8 allo)1.11 F 1.284(ws up to 100 rulesets instead of 30.)-.25 F +1.283(It is recommended that rulesets 0\2559 be)6.284 F(reserv)102 329.4 Q +(ed for)-.15 E F2(sendmail)2.5 E F1 1.1 -.55('s d)D +(edicated use in future releases.).55 E +(The total number of MX records that can be used has been raised to 20.)127 +345.6 Q .335(The number of queued messages that can be handled at one time has\ + been raised from 600 to)127 361.8 R(1000.)102 373.8 Q F0 2.5(7.25. Differ)87 +397.8 R(ent Default T)-.18 E(uning P)-.92 E(arameters)-.1 E F1 -1.11(Ve)127 414 +S .8(rsion 8 has changed the def)1.11 F .8 +(ault parameters for tuning queue costs to mak)-.1 F 3.3(et)-.1 G .8 +(he number of)449.08 414 R .712(recipients more important than the size of the\ + message \(for small messages\).)102 426 R .712(This is reasonable if)5.712 F +(you are connected with reasonably f)102 438 Q(ast links.)-.1 E F0 2.5(7.26. A) +87 462 R(uto-Quoting in Addr)-.5 E(esses)-.18 E F1(Pre)127 478.2 Q(viously)-.25 +E 2.611(,t)-.65 G .111(he \231Full Name <email address>\232 syntax w)176.771 +478.2 R .111(ould generate incorrect protocol output)-.1 F +(if \231Full Name\232 had special characters such as dot.)102 490.2 Q(This v)5 +E(ersion puts quotes around such names.)-.15 E F0 2.5(7.27. Symbolic)87 514.2 R +(Names On Err)2.5 E(or Mailer)-.18 E F1(Se)127 530.4 Q -.15(ve)-.25 G +(ral names ha).15 E .3 -.15(ve b)-.2 H(een b).15 E +(uilt in to the $@ portion of the $#error mailer)-.2 E(.)-.55 E F0 2.5 +(7.28. SMTP)87 554.4 R(VRFY Doesn't Expand)2.5 E F1(Pre)127 570.6 Q 1.437 +(vious v)-.25 F 1.437(ersions of)-.15 F F2(sendmail)3.937 E F1 1.438 +(treated VRFY and EXPN the same.)3.937 F 1.438(In this v)6.438 F 1.438 +(ersion, VRFY)-.15 F(doesn')102 582.6 Q 2.5(te)-.18 G(xpand aliases or follo) +138.05 582.6 Q 2.5(w.)-.25 G(forw)235.84 582.6 Q(ard \214les.)-.1 E .664 +(As an optimization, if you run with your def)127 598.8 R .663(ault deli)-.1 F +-.15(ve)-.25 G .663(ry mode being queue-only).15 F 3.163(,t)-.65 G .663 +(he RCPT)466.387 598.8 R 1.09(command will also not chase aliases and .forw)102 +610.8 R 1.09(ard \214les.)-.1 F 1.09(It will chase them when it processes the) +6.09 F(queue.)102 622.8 Q F0 2.5(7.29. [IPC])87 646.8 R(Mailers Allo)2.5 E 2.5 +(wM)-.1 G(ultiple Hosts)210.49 646.8 Q F1 .448(When an address resolv)127 663 R +.448(es to a mailer that has \231[IPC]\232 as its \231P)-.15 F .447 +(ath\232, the $@ part \(host name\))-.15 F .137 +(can be a colon-separated list of hosts instead of a single hostname.)102 675 R +.138(This asks sendmail to search the)5.138 F .161 +(list for the \214rst entry that is a)102 687 R -.25(va)-.2 G .161(ilable e).25 +F .16(xactly as though it were an MX record.)-.15 F .16(The intent is to route) +5.16 F .737(internal traf)102 699 R .738(\214c through internal netw)-.25 F +.738(orks without publishing an MX record to the net.)-.1 F .738(MX e)5.738 F +(xpan-)-.15 E(sion is still done on the indi)102 711 Q(vidual items.)-.25 E EP +%%Page: 55 52 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-55)452.9 60 Q 2.5(7.30. Aliases)87 96 R(Extended)2.5 E/F1 10 +/Times-Roman@0 SF 1.457(The implementation has been mer)127 112.2 R 1.457 +(ged with maps.)-.18 F 1.456(Among other things, this supports NIS-)6.457 F +(based aliases.)102 124.2 Q F0 2.5(7.31. P)87 148.2 R +(ortability and Security Enhancements)-.2 E F1 2.5(An)127 164.4 S +(umber of internal changes ha)141.72 164.4 Q .3 -.15(ve b)-.2 H +(een made to enhance portability).15 E(.)-.65 E(Se)127 180.6 Q -.15(ve)-.25 G +(ral \214x).15 E(es ha)-.15 E .3 -.15(ve b)-.2 H +(een made to increase the paranoia f).15 E(actor)-.1 E(.)-.55 E F0 2.5 +(7.32. Miscellaneous)87 204.6 R(Changes)2.5 E/F2 10/Times-Italic@0 SF(Sendmail) +127 220.8 Q F1(writes a)2.5 E F2(/etc/sendmail.pid)2.5 E F1 +(\214le with the current process id of the SMTP daemon.)2.5 E -1 -.8(Tw o)127 +237 T 1.646(people using the same program in their .forw)4.946 F 1.647 +(ard \214le are considered dif)-.1 F 1.647(ferent so that)-.25 F +(duplicate elimination doesn')102 249 Q 2.5(td)-.18 G(elete one of them.)225.98 +249 Q(The)127 265.2 Q F2(mailstats)3.181 E F1 .681 +(program prints mailer names and gets the location of the)3.181 F F2 +(sendmail.st)3.18 E F1 .68(\214le from)3.18 F F2(/etc/sendmail.cf)102 277.2 Q +F1(.)A(Man)127 293.4 Q 2.5(ym)-.15 G(inor b)160.46 293.4 Q(ugs ha)-.2 E .3 -.15 +(ve b)-.2 H(een \214x).15 E +(ed, such as handling of backslashes inside of quotes.)-.15 E 2.5(Ah)127 309.6 +S(ook \(ruleset 5\) has been added to allo)141.72 309.6 Q 2.5(wr)-.25 G -.25 +(ew)304.21 309.6 S(riting of local addresses after aliasing.).25 E F0 2.5(8. A) +72 333.6 R(CKNO)-.55 E(WLEDGEMENTS)-.5 E F1(I')112 349.8 Q 2.036 -.15(ve w)-.5 +H(ork).05 E 1.737(ed on)-.1 F F2(sendmail)4.237 E F1 1.737(for man)4.237 F +4.237(yy)-.15 G 1.737(ears, and man)267.501 349.8 R 4.237(ye)-.15 G(mplo) +339.762 349.8 Q 1.737(yers ha)-.1 F 2.037 -.15(ve b)-.2 H 1.737 +(een remarkably patient).15 F .404(about letting me w)87 361.8 R .404 +(ork on a lar)-.1 F .404(ge project that w)-.18 F .403(as not part of my of)-.1 +F .403(\214cial job)-.25 F 5.403(.T)-.4 G .403(his includes time on the)407.388 +361.8 R(INGRES Project at Berk)87 373.8 Q(ele)-.1 E 1.3 -.65(y, a)-.15 H 2.5 +(tB).65 G(ritton Lee, and ag)222.75 373.8 Q(ain on the Mammoth Project at Berk) +-.05 E(ele)-.1 E -.65(y.)-.15 G .453(Much of the second w)112 390 R -2.25 -.2 +(av e)-.1 H .453(of impro)3.153 F -.15(ve)-.15 G .453 +(ments should be credited to Bryan Costales of ICSI.).15 F .454(As he)5.454 F +.781(passed me drafts of his book on)87 402 R F2(sendmail)3.281 E F1 3.281(Iw) +3.281 G .781(as inspired to start w)274.741 402 R .781(orking on things ag)-.1 +F 3.281(ain. Bryan)-.05 F -.1(wa)3.281 G(s).1 E(also a)87 414 Q -.25(va)-.2 G +(ilable to bounce ideas of).25 E 2.5(fo)-.25 G(f.)227.38 414 Q(Man)112 430.2 Q +2.856 -.65(y, m)-.15 H(an).65 E 4.056(yp)-.15 G 1.556(eople contrib)172.212 +430.2 R 1.556(uted chunks of code and ideas to)-.2 F F2(sendmail)4.056 E F1 +6.556(.I)C 4.056(th)418.476 430.2 S 1.557(as pro)430.312 430.2 R -.15(ve)-.15 G +4.057(nt).15 G 4.057(ob)477.006 430.2 S 4.057(ea)491.063 430.2 S .464 +(group netw)87 442.2 R .464(ork ef)-.1 F 2.964(fort. V)-.25 F .464 +(ersion 8 in particular w)-1.11 F .463(as a group project.)-.1 F .463 +(The follo)5.463 F .463(wing people made notable)-.25 F(contrib)87 454.2 Q +(utions:)-.2 E -.25(Ke)127 470.4 S(ith Bostic, CSRG, Uni).25 E -.15(ve)-.25 G +(rsity of California, Berk).15 E(ele)-.1 E(y)-.15 E(Michael J. Corrig)127 482.4 +Q(an, Uni)-.05 E -.15(ve)-.25 G(rsity of California, San Die).15 E(go)-.15 E +(Bryan Costales, International Computer Science Institute)127 494.4 Q +(P{r \(Pell\) Emanuelsson)127 506.4 Q(Craig Ev)127 518.4 Q(erhart, T)-.15 E +(ransarc Corporation)-.35 E -.8(To)127 530.4 S 2.5(mI).8 G -.25(va)150.92 530.4 +S 2.5(rH).25 G(elbekkmo, Norwe)173.16 530.4 Q(gian School of Economics)-.15 E +(Allan E. Johannesen, WPI)127 542.4 Q -.8(Ta)127 554.4 S +(kahiro Kanbe, FujiXerox).8 E(Brian Kantor)127 566.4 Q 2.5(,U)-.4 G(ni)191.31 +566.4 Q -.15(ve)-.25 G(rsity of California, San Die).15 E(go)-.15 E +(Bruce Lilly)127 578.4 Q 2.5(,S)-.65 G(on)182.74 578.4 Q 2.5(yU)-.15 G(.S.) +207.31 578.4 Q(Nakamura Motonori, K)127 590.4 Q(yoto Uni)-.25 E -.15(ve)-.25 G +(rsity).15 E(John Gardiner Myers, Carne)127 602.4 Q(gie Mellon Uni)-.15 E -.15 +(ve)-.25 G(rsity).15 E(Neil Rick)127 614.4 Q(ert, Northern Illinois Uni)-.1 E +-.15(ve)-.25 G(rsity).15 E(Eric W)127 626.4 Q(assenaar)-.8 E 2.5(,N)-.4 G +(ational Institute for Nuclear and High Ener)200.49 626.4 Q(gy Ph)-.18 E +(ysics, Amsterdam)-.05 E(Christophe W)127 638.4 Q(olfhugel, Herv)-.8 E 2.5(eS) +-.15 G(chauer Consultants \(P)252.7 638.4 Q(aris\))-.15 E 2.687(Ia)87 654.6 S +.187(pologize for an)97.457 654.6 R .188(yone I ha)-.15 F .488 -.15(ve o)-.2 H +.188(mitted, misspelled, misattrib).15 F .188(uted, or otherwise missed.)-.2 F +(Man)5.188 E 2.688(yo)-.15 G .188(ther peo-)467.992 654.6 R(ple ha)87 666.6 Q +.3 -.15(ve c)-.2 H(ontrib).15 E(uted ideas, comments, and encouragement.)-.2 E +2.5(Ia)5 G(ppreciate their contrib)338.06 666.6 Q(ution as well.)-.2 E EP +%%Page: 56 53 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 12/Times-Bold@0 SF 3(APPENDIX A)257.172 98.4 R(COMMAND LINE FLA)224.832 +141.6 Q(GS)-.66 E/F1 10/Times-Roman@0 SF(Ar)97 201 Q +(guments must be presented with \215ags before addresses.)-.18 E +(The \215ags are:)5 E<ad62>72 217.2 Q/F2 10/Times-Italic@0 SF(x)A F1 +(Set operation mode to)144 217.2 Q F2(x)2.5 E F1 5(.O)C(peration modes are:) +253.71 217.2 Q 12.22(mD)184 233.4 S(eli)211.22 233.4 Q -.15(ve)-.25 G 2.5(rm) +.15 G(ail \(def)243.87 233.4 Q(ault\))-.1 E 16.11(sS)184 245.4 S +(peak SMTP on input side)209.56 245.4 Q 15(dR)184 257.4 S(un as a daemon)210.67 +257.4 Q 17.22(tR)184 269.4 S(un in test mode)210.67 269.4 Q 15(vJ)184 281.4 S +(ust v)207.89 281.4 Q(erify addresses, don')-.15 E 2.5(tc)-.18 G +(ollect or deli)319.48 281.4 Q -.15(ve)-.25 G(r).15 E 17.22(iI)184 293.4 S +(nitialize the alias database)207.33 293.4 Q 15(pP)184 305.4 S +(rint the mail queue)209.56 305.4 Q 15.56(zF)184 317.4 S +(reeze the con\214guration \214le)209.56 317.4 Q<ad42>72 337.8 Q F2(type)A F1 +(Indicate body type.)144 337.8 Q<ad43>72 354 Q F2(\214le)A F1 .947(Use a dif) +144 354 R .946(ferent con\214guration \214le.)-.25 F F2(Sendmail)5.946 E F1 +.946(runs as the in)3.446 F -.2(vo)-.4 G .946(king user \(rather than root\)).2 +F(when this \215ag is speci\214ed.)144 366 Q<ad64>72 382.2 Q F2(le)A(vel)-.15 E +F1(Set deb)144 382.2 Q(ugging le)-.2 E -.15(ve)-.25 G(l.).15 E<ad66>72 398.4 Q +F2(addr)2.5 E F1(The sender')144 398.4 Q 2.5(sm)-.55 G(achine address is)205.1 +398.4 Q F2(addr)2.5 E F1(.)A<ad46>72 414.6 Q F2(name)A F1 +(Sets the full name of this user to)144 414.6 Q F2(name)2.5 E F1(.)A<ad68>72 +430.8 Q F2(cnt)2.5 E F1 .725(Sets the \231hop count\232 to)144 430.8 R F2(cnt) +3.225 E F1 5.725(.T)C .726 +(his represents the number of times this message has been)269.45 430.8 R .02 +(processed by)144 442.8 R F2(sendmail)2.52 E F1 .02(\(to the e)2.52 F .02 +(xtent that it is supported by the underlying netw)-.15 F(orks\).)-.1 E F2(Cnt) +5.02 E F1 1.521 +(is incremented during processing, and if it reaches MAXHOP \(currently 30\)) +144 454.8 R F2(sendmail)4.021 E F1(thro)144 466.8 Q(ws a)-.25 E -.1(wa)-.15 G +2.5(yt).1 G(he message with an error)199.6 466.8 Q(.)-.55 E 58.86(\255n Don')72 +483 R 2.5(td)-.18 G 2.5(oa)174.65 483 S(liasing or forw)186.59 483 Q(arding.) +-.1 E<ad72>72 499.2 Q F2(addr)2.5 E F1(An obsolete form of)144 499.2 Q/F3 10 +/Times-Bold@0 SF<ad66>2.5 E F1(.)A<ad6f>72 515.4 Q F2 1.666(xv)C(alue)-1.666 E +F1(Set option)144 515.4 Q F2(x)2.5 E F1(to the speci\214ed)2.5 E F2(value)2.5 E +F1 5(.T)C(hese options are described in Appendix B.)292.6 515.4 Q<ad70>72 531.6 +Q F2(pr)A(otocol)-.45 E F1 .401(Set the sending protocol.)144 531.6 R .401 +(Programs are encouraged to set this.)5.401 F .4(The protocol \214eld can be) +5.401 F .114(in the form)144 543.6 R F2(pr)2.614 E(otocol)-.45 E F3(:)A F2 +(host)A F1 .114(to set both the sending protocol and sending host.)2.614 F -.15 +(Fo)5.115 G 2.615(re).15 G(xample,)472.06 543.6 Q 2.147(\231\255pUUCP:uunet\ +\232 sets the sending protocol to UUCP and the sending host to uunet.)144 555.6 +R .973(\(Some e)144 567.6 R .974 +(xisting programs use \255oM to set the r and s macros; this is equi)-.15 F +-.25(va)-.25 G .974(lent to using).25 F(\255p.\))144 579.6 Q<ad71>72 595.8 Q F2 +(time)A F1 -.35(Tr)144 595.8 S 3.168(yt).35 G 3.167(op)164.038 595.8 S .667 +(rocess the queued up mail.)177.205 595.8 R .667(If the time is gi)5.667 F -.15 +(ve)-.25 G .667(n, a sendmail will run through the).15 F +(queue at the speci\214ed interv)144 607.8 Q(al to deli)-.25 E -.15(ve)-.25 G +2.5(rq).15 G(ueued mail; otherwise, it only runs once.)310.82 607.8 Q<ad71>72 +624 Q F2(Xstring)A F1 .312 +(Run the queue once, limiting the jobs to those matching)144 624 R F2(Xstring) +2.813 E F1 5.313(.T)C .313(he k)416.325 624 R .613 -.15(ey l)-.1 H(etter).15 E +F2(X)2.813 E F1 .313(can be)2.813 F F3(I)144 636 Q F1 .671 +(to limit based on queue identi\214er)3.171 F(,)-.4 E F3(R)3.171 E F1 .67 +(to limit based on recipient, or)3.171 F F3(S)3.17 E F1 .67(to limit based on) +3.17 F(sender)144 648 Q 6.053(.A)-.55 G 1.054 +(particular queued job is accepted if one of the corresponding addresses con-) +188.876 648 R(tains the indicated)144 660 Q F2(string)2.5 E F1(.)A 61.08 +(\255t Read)72 676.2 R .752(the header for \231T)3.252 F .752 +(o:\232, \231Cc:\232, and \231Bcc:\232 lines, and send to e)-.8 F -.15(ve)-.25 +G .752(ryone listed in those).15 F 2.539(lists. The)144 688.2 R .039 +(\231Bcc:\232 line will be deleted before sending.)2.539 F(An)5.039 E 2.539(ya) +-.15 G .04(ddresses in the ar)385.31 688.2 R .04(gument v)-.18 F(ec-)-.15 E +(tor will be deleted from the send list.)144 700.2 Q F3 193.36 +(SMM:08-56 Sendmail)72 756 R(Installation and Operation Guide)2.5 E EP +%%Page: 57 54 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-57)452.9 60 Q/F1 10/Times-Roman@0 SF<ad58>72 96 Q/F2 10/Times-Italic@0 +SF(lo)3.18 E(g\214le)-.1 E F1 .68(Log all traf)144.68 96 R .679 +(\214c in and out of sendmail in the indicated)-.25 F F2(lo)3.179 E(g\214le)-.1 +E F1 .679(for deb)3.179 F .679(ugging mailer prob-)-.2 F 2.5(lems. This)144 108 +R(produces a lot of data v)2.5 E(ery quickly and should be used sparingly)-.15 +E(.)-.65 E 1.118 +(There are a number of options that may be speci\214ed as primiti)97 124.2 R +1.418 -.15(ve \215)-.25 H 1.118(ags \(pro).15 F 1.118(vided for compatibility) +-.15 F(with)72 136.2 Q F2(delivermail)2.5 E F1 2.5(\). These)B +(are the e, i, m, and v options.)2.5 E +(Also, the f option may be speci\214ed as the)5 E F0<ad73>2.5 E F1(\215ag.)2.5 +E EP +%%Page: 58 55 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 12/Times-Bold@0 SF 3(APPENDIX B)250.002 98.4 R -.12(QU)220.29 141.6 S +(EUE FILE FORMA).12 E(TS)-1.14 E/F1 10/Times-Roman@0 SF .292 +(This appendix describes the format of the queue \214les.)97 201 R .292 +(These \214les li)5.292 F .592 -.15(ve i)-.25 H 2.792(nt).15 G .291 +(he directory de\214ned by the)395.636 201 R/F2 10/Times-Bold@0 SF(Q)72 213 Q +F1(option in the)2.5 E/F3 10/Times-Italic@0 SF(sendmail.cf)2.5 E F1 +(\214le, usually)2.5 E F3(/var/spool/mqueue)2.5 E F1(or)2.5 E F3 +(/usr/spool/mqueue)2.5 E F1(.)A .229(All queue \214les ha)97 229.2 R .529 -.15 +(ve t)-.2 H .229(he name).15 F F3(x)2.729 E F2(f)1.666 E F3(AAA99999)A F1 +(where)2.73 E F3(AAA99999)2.73 E F1 .23(is the)2.73 F F3(id)2.73 E F1 .23 +(for this message and the)2.73 F F3(x)2.73 E F1 .23(is a)2.73 F 3.601 +(type. The)72 241.2 R 1.101 +(\214rst letter of the id encodes the hour of the day that the message w)3.601 +F 1.101(as recei)-.1 F -.15(ve)-.25 G 3.601(db).15 G 3.601(yt)451.798 241.2 S +1.101(he system)463.179 241.2 R .551 +(\(with A being the hour between midnight and 1:00AM\).)72 253.2 R .552 +(All \214les with the same id collecti)5.552 F -.15(ve)-.25 G .552 +(ly de\214ne one).15 F(message.)72 265.2 Q(The types are:)97 281.4 Q 31(dT)72 +297.6 S(he data \214le.)114.11 297.6 Q(The message body \(e)5 E +(xcluding the header\) is k)-.15 E(ept in this \214le.)-.1 E 33.22(lT)72 313.8 +S .312(he lock \214le.)114.11 313.8 R .312(If this \214le e)5.312 F .311 +(xists, the job is currently being processed, and a queue run will not pro-) +-.15 F .523(cess the \214le.)108 325.8 R -.15(Fo)5.524 G 3.024(rt).15 G .524 +(hat reason, an e)183.274 325.8 R(xtraneous)-.15 E F2(lf)3.024 E F1 .524 +(\214le can cause a job to apparently disappear \(it will)3.024 F .285(not e) +108 337.8 R -.15(ve)-.25 G 2.785(nt).15 G .284(ime out!\).)147.61 337.8 R +([Actually)5.284 E 2.784(,t)-.65 G .284 +(his \214le is obsolete on most systems that support the)237.802 337.8 R F2 +(\215ock)2.784 E F1(or)2.784 E F2(lockf)2.784 E F1(system calls.])108 349.8 Q +31(nT)72 366 S .348(his \214le is created when an id is being created.)114.11 +366 R .348(It is a separate \214le to insure that no mail can e)5.348 F -.15 +(ve)-.25 G(r).15 E .805(be destro)108 378 R .805(yed due to a race condition.) +-.1 F .805(It should e)5.805 F .805(xist for no more than a fe)-.15 F 3.305(wm) +-.25 G .805(illiseconds at an)433.1 378 R(y)-.15 E(gi)108 390 Q -.15(ve)-.25 G +2.5(nt).15 G 2.5(ime. [This)135.1 390 R(is only used on old v)2.5 E +(ersions of sendmail; it is not used on ne)-.15 E(wer v)-.25 E(ersions.])-.15 E +31(qT)72 406.2 S(he queue control \214le.)114.11 406.2 Q +(This \214le contains the information necessary to process the job)5 E(.)-.4 E +33.22(tA)72 422.4 S .344(temporary \214le.)118.064 422.4 R .344 +(These are an image of the)5.344 F F2(qf)2.844 E F1 .344 +(\214le when it is being reb)2.844 F 2.845(uilt. It)-.2 F .345 +(should be renamed)2.845 F(to a)108 434.4 Q F2(qf)2.5 E F1(\214le v)2.5 E +(ery quickly)-.15 E(.)-.65 E 31(xA)72 450.6 S .567(transcript \214le, e)118.287 +450.6 R .567(xisting during the life of a session sho)-.15 F .566(wing e)-.25 F +-.15(ve)-.25 G .566(rything that happens during that).15 F(session.)108 462.6 Q +(The)97 478.8 Q F2(qf)3.333 E F1 .833 +(\214le is structured as a series of lines each be)3.333 F .834 +(ginning with a code letter)-.15 F 5.834(.T)-.55 G .834(he lines are as fol-) +427.354 478.8 R(lo)72 490.8 Q(ws:)-.25 E 28.78(DT)72 507 S +(he name of the data \214le.)114.11 507 Q +(There may only be one of these lines.)5 E 28.78(HA)72 523.2 S .33 +(header de\214nition.)118.05 523.2 R .33(There may be an)5.33 F 2.829(yn)-.15 G +.329(umber of these lines.)274.289 523.2 R .329(The order is important: the) +5.329 F 2.829(yr)-.15 G(epre-)483.46 523.2 Q .046 +(sent the order in the \214nal message.)108 535.2 R .046 +(These use the same syntax as header de\214nitions in the con\214gu-)5.046 F +(ration \214le.)108 547.2 Q 29.33(CT)72 563.4 S .575(he controlling address.) +114.11 563.4 R .575(The syntax is \231localuser:aliasname\232.)5.575 F .575 +(Recipient addresses follo)5.575 F .575(wing this)-.25 F 2.814 +(line will be \215agged so that deli)108 575.4 R -.15(ve)-.25 G 2.814 +(ries will be run as the).15 F F3(localuser)5.314 E F1 2.814 +(\(a user name from the)5.314 F .562(/etc/passwd \214le\);)108 587.4 R F3 +(aliasname)3.062 E F1 .561(is the name of the alias that e)3.062 F .561 +(xpanded to this address \(used for print-)-.15 F(ing messages\).)108 599.4 Q +29.33(RA)72 615.6 S .705(recipient address.)118.425 615.6 R .705 +(This will normally be completely aliased, b)5.705 F .705 +(ut is actually realiased when the)-.2 F(job is processed.)108 627.6 Q +(There will be one line for each recipient.)5 E 30.44(ST)72 643.8 S +(he sender address.)114.11 643.8 Q(There may only be one of these lines.)5 E +29.89(EA)72 660 S 3.742(ne)115.22 660 S 1.242(rror address.)128.402 660 R 1.242 +(If an)6.242 F 3.742(ys)-.15 G 1.241(uch lines e)218.19 660 R 1.241(xist, the) +-.15 F 3.741(yr)-.15 G 1.241(epresent the addresses that should recei)308.124 +660 R 1.541 -.15(ve e)-.25 H(rror).15 E(messages.)108 672 Q 29.89(TT)72 688.2 S +(he job creation time.)114.11 688.2 Q +(This is used to compute when to time out the job)5 E(.)-.4 E 30.44(PT)72 704.4 +S .113(he current message priority)114.11 704.4 R 5.113(.T)-.65 G .113 +(his is used to order the queue.)236.662 704.4 R .114(Higher numbers mean lo) +5.114 F .114(wer priori-)-.25 F 3.677(ties. The)108 716.4 R 1.176 +(priority changes as the message sits in the queue.)3.677 F 1.176 +(The initial priority depends on the)6.176 F F2 193.36(SMM:08-58 Sendmail)72 +756 R(Installation and Operation Guide)2.5 E EP +%%Page: 59 56 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-59)452.9 60 Q/F1 10/Times-Roman@0 SF +(message class and the size of the message.)108 96 Q 27.11(MA)72 112.2 S 2.703 +(message. This)117.923 112.2 R .203(line is printed by the)2.703 F/F2 10 +/Times-Italic@0 SF(mailq)2.703 E F1 .204 +(command, and is generally used to store status infor)2.704 F(-)-.2 E 2.5 +(mation. It)108 124.2 R(can contain an)2.5 E 2.5(yt)-.15 G -.15(ex)219.78 124.2 +S(t.).15 E 30.44(FF)72 140.4 S .044 +(lag bits, represented as one letter per \215ag.)113.56 140.4 R .043 +(De\214ned \215ag bits are)5.043 F F0(r)2.543 E F1 .043 +(indicating that this is a response)2.543 F .142(message and)108 152.4 R F0(w) +2.642 E F1 .142(indicating that a w)2.642 F .143 +(arning message has been sent announcing that the mail has been)-.1 F(delayed.) +108 164.4 Q 31($A)72 180.6 S .83(macro de\214nition.)118.55 180.6 R .83(The v) +5.83 F .829(alues of certain macros \(as of this writing, only)-.25 F F0($r) +3.329 E F1(and)3.329 E F0($s)3.329 E F1 3.329(\)a)C .829(re passed)466.241 +180.6 R(through to the queue run phase.)108 192.6 Q 29.33(BT)72 208.8 S .924 +(he body type.)114.11 208.8 R .925(The remainder of the line is a te)5.924 F +.925(xt string de\214ning the body type.)-.15 F .925(If this \214eld is)5.925 F +.009(missing, the body type is assumed to be \231unde\214ned\232 and no specia\ +l processing is attempted.)108 220.8 R(Le)5.008 E -.05(ga)-.15 G(l).05 E -.25 +(va)108 232.8 S(lues are \2317BIT\232 and \2318BITMIME\232.).25 E 4.072 +(As an e)97 249 R 4.072(xample, the follo)-.15 F 4.073 +(wing is a queue \214le sent to \231eric@mammoth.Berk)-.25 F(ele)-.1 E -.65(y.) +-.15 G 4.073(EDU\232 and).65 F(\231bostic@ok)72 263 Q(eef)-.1 E(fe.CS.Berk)-.25 +E(ele)-.1 E -.65(y.)-.15 G(EDU\232).65 E/F3 7/Times-Roman@0 SF(1)219.09 259 Q +F1(:)222.59 263 Q(P835771)112 279.2 Q(T404261372)112 291.2 Q(DdfAAA13557)112 +303.2 Q(Seric)112 315.2 Q(Eo)112 327.2 Q(wner)-.25 E(-sendmail@v)-.2 E +(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G(EDU).65 E(Ceric:sendmail@v)112 +339.2 Q(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G(EDU).65 E +(Reric@mammoth.Berk)112 351.2 Q(ele)-.1 E -.65(y.)-.15 G(EDU).65 E(Rbostic@ok) +112 363.2 Q(eef)-.1 E(fe.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G(EDU).65 E +(H?P?return-path: <o)112 375.2 Q(wner)-.25 E(-sendmail@v)-.2 E(angogh.CS.Berk) +-.25 E(ele)-.1 E -.65(y.)-.15 G(EDU>).65 E(Hrecei)112 387.2 Q -.15(ve)-.25 G +(d: by v).15 E(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G +(EDU \(5.108/2.7\) id AAA06703;).65 E(Fri, 17 Jul 92 00:28:55 -0700)132 399.2 Q +(Hrecei)112 411.2 Q -.15(ve)-.25 G(d: from mail.CS.Berk).15 E(ele)-.1 E -.65 +(y.)-.15 G(EDU by v).65 E(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G +(EDU \(5.108/2.7\)).65 E(id AAA06698; Fri, 17 Jul 92 00:28:54 -0700)132 423.2 Q +(Hrecei)112 435.2 Q -.15(ve)-.25 G(d: from [128.32.31.21] by mail.CS.Berk).15 E +(ele)-.1 E -.65(y.)-.15 G(EDU \(5.96/2.5\)).65 E +(id AA22777; Fri, 17 Jul 92 03:29:14 -0400)132 447.2 Q(Hrecei)112 459.2 Q -.15 +(ve)-.25 G(d: by foo.bar).15 E(.baz.de \(5.57/Ultrix3.0-C\))-.55 E +(id AA22757; Fri, 17 Jul 92 09:31:25 GMT)132 471.2 Q(H?F?from: eric@foo.bar)112 +483.2 Q(.baz.de \(Eric Allman\))-.55 E(H?x?full-name: Eric Allman)112 495.2 Q +(Hmessage-id: <9207170931.AA22757@foo.bar)112 507.2 Q(.baz.de>)-.55 E(HT)112 +519.2 Q(o: sendmail@v)-.8 E(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G(EDU) +.65 E(Hsubject: this is an e)112 531.2 Q(xample message)-.15 E 1.083(This sho) +72 547.4 R 1.084(ws the name of the data \214le, the person who sent the messa\ +ge, the submission time \(in seconds)-.25 F .26 +(since January 1, 1970\), the message priority)72 559.4 R 2.76(,t)-.65 G .259 +(he message class, the recipients, and the headers for the mes-)257.03 559.4 R +(sage.)72 571.4 Q .32 LW 76 669.2 72 669.2 DL 80 669.2 76 669.2 DL 84 669.2 80 +669.2 DL 88 669.2 84 669.2 DL 92 669.2 88 669.2 DL 96 669.2 92 669.2 DL 100 +669.2 96 669.2 DL 104 669.2 100 669.2 DL 108 669.2 104 669.2 DL 112 669.2 108 +669.2 DL 116 669.2 112 669.2 DL 120 669.2 116 669.2 DL 124 669.2 120 669.2 DL +128 669.2 124 669.2 DL 132 669.2 128 669.2 DL 136 669.2 132 669.2 DL 140 669.2 +136 669.2 DL 144 669.2 140 669.2 DL 148 669.2 144 669.2 DL 152 669.2 148 669.2 +DL 156 669.2 152 669.2 DL 160 669.2 156 669.2 DL 164 669.2 160 669.2 DL 168 +669.2 164 669.2 DL 172 669.2 168 669.2 DL 176 669.2 172 669.2 DL 180 669.2 176 +669.2 DL 184 669.2 180 669.2 DL 188 669.2 184 669.2 DL 192 669.2 188 669.2 DL +196 669.2 192 669.2 DL 200 669.2 196 669.2 DL 204 669.2 200 669.2 DL 208 669.2 +204 669.2 DL 212 669.2 208 669.2 DL 216 669.2 212 669.2 DL/F4 5/Times-Roman@0 +SF(1)93.6 679.6 Q/F5 8/Times-Roman@0 SF .719(This e)3.2 J .719 +(xample is contri)-.12 F -.12(ve)-.2 G 2.719(da).12 G .719 +(nd probably inaccurate for your en)186.968 682.8 R 2.719(vironment. Glance) +-.32 F -.12(ove)2.718 G 2.718(ri).12 G 2.718(tt)384.998 682.8 S 2.718(og) +392.164 682.8 S .718(et an idea; nothing can replace)402.882 682.8 R +(looking at what your o)72 692.4 Q(wn system generates.)-.2 E EP +%%Page: 60 57 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 12/Times-Bold@0 SF 3(APPENDIX C)249.672 98.4 R(SUMMAR)198.282 141.6 Q 3(YO) +-.42 G 3(FS)274.182 141.6 S(UPPOR)291.186 141.6 Q 3(TF)-.48 G(ILES)350.37 141.6 +Q/F1 10/Times-Roman@0 SF 1.519(This is a summary of the support \214les that)97 +201 R/F2 10/Times-Italic@0 SF(sendmail)4.019 E F1 1.52(creates or generates.) +4.019 F(Man)6.52 E 4.02(yo)-.15 G 4.02(ft)444.74 201 S 1.52(hese can be)454.87 +201 R(changed by editing the sendmail.cf \214le; check there to \214nd the act\ +ual pathnames.)72 213 Q(/usr/sbin/sendmail)72 229.2 Q(The binary of)144 241.2 Q +F2(sendmail)2.5 E F1(.)A(/usr/bin/ne)72 257.4 Q -.1(wa)-.25 G(liases).1 E 3.735 +(Al)144 269.4 S 1.235 +(ink to /usr/sbin/sendmail; causes the alias database to be reb)157.735 269.4 R +3.734(uilt. Running)-.2 F 1.234(this pro-)3.734 F(gram is completely equi)144 +281.4 Q -.25(va)-.25 G(lent to gi).25 E(ving)-.25 E F2(sendmail)2.5 E F1(the) +2.5 E/F3 10/Times-Bold@0 SF(\255bi)2.5 E F1(\215ag.)2.5 E 13.38 +(/usr/bin/mailq Prints)72 297.6 R 3.702(al)3.702 G 1.202 +(isting of the mail queue.)181.964 297.6 R 1.203(This program is equi)6.202 F +-.25(va)-.25 G 1.203(lent to using the).25 F F3(\255bp)3.703 E F1 1.203 +(\215ag to)3.703 F F2(sendmail)144 309.6 Q F1(.)A 5.9(/etc/sendmail.cf The)72 +325.8 R(con\214guration \214le, in te)2.5 E(xtual form.)-.15 E 5.9 +(/etc/sendmail.fc The)72 342 R +(con\214guration \214le represented as a memory image.)2.5 E +(/usr/lib/sendmail.hf)72 358.2 Q(The SMTP help \214le.)144 370.2 Q 7 +(/etc/sendmail.st A)72 386.4 R(statistics \214le; need not be present.)2.5 E +.89(/etc/sendmail.pid Created)72 402.6 R .318 +(in daemon mode; it contains the process id of the current SMTP daemon.)2.818 F +.318(If you)5.318 F .337(use this in scripts; use `)144 414.6 R .337 +(`head \2551')-.74 F 2.838('t)-.74 G 2.838(og)285.78 414.6 S .338 +(et just the \214rst line; later v)298.618 414.6 R .338(ersions of)-.15 F F2 +(sendmail)2.838 E F1(may)2.838 E(add information to subsequent lines.)144 426.6 +Q 25.62(/etc/aliases The)72 442.8 R(te)2.5 E(xtual v)-.15 E +(ersion of the alias \214le.)-.15 E(/etc/aliases.{pag,dir})72 459 Q +(The alias \214le in)144 471 Q F2(dbm)2.5 E F1(\(3\) format.)1.666 E(/v)72 +487.2 Q(ar/spool/mqueue)-.25 E +(The directory in which the mail queue and temporary \214les reside.)144 499.2 +Q(/v)72 515.4 Q(ar/spool/mqueue/qf*)-.25 E +(Control \(queue\) \214les for messages.)144 527.4 Q(/v)72 543.6 Q +(ar/spool/mqueue/df*)-.25 E(Data \214les.)144 555.6 Q(/v)72 571.8 Q +(ar/spool/mqueue/tf*)-.25 E -.7(Te)144 583.8 S(mporary v).7 E +(ersions of the qf \214les, used during queue \214le reb)-.15 E(uild.)-.2 E(/v) +72 600 Q(ar/spool/mqueue/xf*)-.25 E 2.5(At)144 612 S +(ranscript of the current session.)156.5 612 Q F3 193.36(SMM:08-60 Sendmail)72 +756 R(Installation and Operation Guide)2.5 E EP +%%Page: 2 58 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 198.36(SMM:08-2 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 12/Times-Roman@0 SF -1.116(TA) +263.226 98.4 S(BLE OF CONTENTS)1.116 E/F2 10/Times-Roman@0 SF 2.5(1. B)72 124.8 +R(ASIC INST)-.35 E(ALLA)-.93 E 1.18(TION .....................................\ +..........................................................................) +-1.11 F(5)499 124.8 Q 2.5(1.1. Compiling)87 139.2 R .43(Sendmail .............\ +..............................................................................\ +...................)2.5 F(5)499 139.2 Q 2.5(1.1.1. Old)102 153.6 R -.15(ve)2.5 +G(rsions of mak).15 E 2.93(e.)-.1 G 28.5(.....................................\ +.............................................................. 5)220.5 153.6 R +2.5(1.1.2. Compilation)102 168 R 2.1(\215ags .................................\ +........................................................................)2.5 F +(5)499 168 Q 2.5(1.1.3. Compilation)102 182.4 R(and installation)2.5 E 28.5(..\ +..............................................................................\ +........ 6)4.6 F 2.5(1.2. Con\214guration)87 196.8 R .99(Files ...............\ +..............................................................................\ +...................)2.5 F(6)499 196.8 Q 2.5(1.3. Details)87 211.2 R +(of Installation Files)2.5 E 28.5(............................................\ +....................................................... 7)4.89 F 2.5 +(1.3.1. /usr/sbin/sendmail)102 225.6 R 28.5(..................................\ +....................................................................... 7)2.66 +F 2.5(1.3.2. /etc/sendmail.cf)102 240 R 28.5(.................................\ +........................................................................... 8) +4.9 F 2.5(1.3.3. /usr/ucb/ne)102 254.4 R -.1(wa)-.25 G .53(liases ............\ +..............................................................................\ +.............).1 F(8)499 254.4 Q 2.5(1.3.4. /v)102 268.8 R 1.81(ar/spool/mqueu\ +e ............................................................................\ +............................)-.25 F(8)499 268.8 Q 2.5(1.3.5. /etc/aliases*)102 +283.2 R 28.5(.................................................................\ +................................................. 8)4.62 F 2.5 +(1.3.6. /etc/sendmail.fc)102 297.6 R 28.5(....................................\ +........................................................................ 8)4.9 +F 2.5(1.3.7. /etc/rc)102 312 R 28.5(..........................................\ +..............................................................................\ +.... 9)3.51 F 2.5(1.3.8. /usr/lib/sendmail.hf)102 326.4 R 23.5(...............\ +..............................................................................\ +.......... 10)2.94 F 2.5(1.3.9. /etc/sendmail.st)102 340.8 R 23.5(............\ +..............................................................................\ +................... 10)3.5 F 2.5(1.3.10. /usr/ucb/ne)102 355.2 R -.1(wa)-.25 G +.53(liases ...................................................................\ +..................................).1 F(11)494 355.2 Q 2.5 +(1.3.11. /usr/ucb/mailq)102 369.6 R 23.5(.....................................\ +........................................................................ 11) +3.22 F 2.5(2. NORMAL)72 384 R(OPERA)2.5 E 1.56(TIONS .........................\ +..............................................................................\ +......)-1.11 F(11)494 384 Q 2.5(2.1. `)87 398.4 R(`Quick')-.74 E 2.5('C)-.74 G +(on\214guration Startup)152.45 398.4 Q 23.5(..................................\ +........................................................... 11)2.77 F 2.5 +(2.2. The)87 412.8 R(System Log)2.5 E 23.5(...................................\ +..............................................................................\ +... 11)4.89 F 2.5(2.2.1. F)102 427.2 R 2.26(ormat ............................\ +..............................................................................\ +................)-.15 F(11)494 427.2 Q 2.5(2.2.2. Le)102 441.6 R -.15(ve)-.25 G +2.24(ls ......................................................................\ +.....................................................).15 F(11)494 441.6 Q 2.5 +(2.3. The)87 456 R(Mail Queue)2.5 E 23.5(.....................................\ +..............................................................................\ +.. 11)2.96 F 2.5(2.3.1. Printing)102 470.4 R(the queue)2.5 E 23.5(............\ +..............................................................................\ +............... 12)2.67 F 2.5(2.3.2. F)102 484.8 R(orcing the queue)-.15 E 23.5 +(.............................................................................\ +............................ 12)3.94 F 2.5(2.4. The)87 499.2 R(Alias Database) +2.5 E 23.5(...................................................................\ +............................................. 12)2.69 F 2.5(2.4.1. Reb)102 +513.6 R(uilding the alias database)-.2 E 23.5(................................\ +....................................................... 13)4.27 F 2.5 +(2.4.2. Potential)102 528 R .72(problems .....................................\ +...................................................................)2.5 F(13) +494 528 Q 2.5(2.4.3. List)102 542.4 R -.25(ow)2.5 G 1.81(ners ................\ +..............................................................................\ +.....................).25 F(13)494 542.4 Q 2.5(2.5. User)87 556.8 R +(Information Database)2.5 E 23.5(.............................................\ +....................................................... 14)2.7 F 2.5(2.6. Per) +87 571.2 R(-User F)-.2 E(orw)-.15 E(arding \(.forw)-.1 E(ard Files\))-.1 E 23.5 +(.............................................................................\ +...... 14)4.09 F 2.5(2.7. Special)87 585.6 R(Header Lines)2.5 E 23.5(.........\ +..............................................................................\ +...................... 14)2.97 F 2.5(2.7.1. Return-Receipt-T)102 600 R .98(o: \ +..............................................................................\ +.........................)-.8 F(14)494 600 Q 2.5(2.7.2. Errors-T)102 614.4 R +2.09(o: ......................................................................\ +...............................................)-.8 F(14)494 614.4 Q 2.5 +(2.7.3. Apparently-T)102 628.8 R 2.09(o: .....................................\ +........................................................................)-.8 F +(15)494 628.8 Q 2.5(2.8. IDENT)87 643.2 R(Protocol Support)2.5 E 23.5(........\ +..............................................................................\ +................. 15)2.95 F 2.5(3. ARGUMENTS)72 657.6 R 23.5(.................\ +..............................................................................\ +................................ 15)3.78 F 2.5(3.1. Queue)87 672 R(Interv)2.5 E +1.55(al ......................................................................\ +.................................................)-.25 F(15)494 672 Q 2.5 +(3.2. Daemon)87 686.4 R 1.29(Mode ............................................\ +...........................................................................)2.5 +F(15)494 686.4 Q 2.5(3.3. F)87 700.8 R(orcing the Queue)-.15 E 23.5(..........\ +..............................................................................\ +......................... 16)4.22 F 2.5(3.4. Deb)87 715.2 R 1.76(ugging ......\ +..............................................................................\ +.........................................)-.2 F(16)494 715.2 Q EP +%%Page: 3 59 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-3)457.9 60 Q/F1 10/Times-Roman@0 SF 2.5(3.5. T)87 96 R(rying a Dif)-.35 +E(ferent Con\214guration File)-.25 E 23.5(....................................\ +............................................... 16)4.67 F 2.5(3.6. Changing)87 +110.4 R(the V)2.5 E(alues of Options)-1.11 E 23.5(............................\ +................................................................ 16)3.23 F 2.5 +(3.7. Logging)87 124.8 R -.35(Tr)2.5 G(af).35 E .5(\214c .....................\ +..............................................................................\ +...................)-.25 F(16)494 124.8 Q 2.5(4. TUNING)72 139.2 R 23.5(......\ +..............................................................................\ +..................................................... 17)2.68 F 2.5(4.1. T)87 +153.6 R 1.07(imeouts .........................................................\ +.......................................................................)-.35 F +(17)494 153.6 Q 2.5(4.1.1. Queue)102 168 R(interv)2.5 E 2.1(al ...............\ +..............................................................................\ +.................)-.25 F(17)494 168 Q 2.5(4.1.2. Read)102 182.4 R 1(timeouts .\ +..............................................................................\ +................................)2.5 F(17)494 182.4 Q 2.5(4.1.3. Message)102 +196.8 R 1.56(timeouts ........................................................\ +.................................................)2.5 F(18)494 196.8 Q 2.5 +(4.2. F)87 211.2 R(orking During Queue Runs)-.15 E 23.5(......................\ +........................................................................... 19) +4.49 F 2.5(4.3. Queue)87 225.6 R .73(Priorities ..............................\ +..............................................................................\ +.........)2.5 F(19)494 225.6 Q 2.5(4.4. Load)87 240 R .44(Limiting ...........\ +..............................................................................\ +...............................)2.5 F(19)494 240 Q 2.5(4.5. Deli)87 254.4 R +-.15(ve)-.25 G(ry Mode).15 E 23.5(............................................\ +........................................................................... 19) +3.08 F 2.5(4.6. Log)87 268.8 R(Le)2.5 E -.15(ve)-.25 G 2.52(l.).15 G 23.5(....\ +..............................................................................\ +............................................ 20)153 268.8 R 2.5(4.7. File)87 +283.2 R .72(Modes ............................................................\ +.................................................................)2.5 F(20)494 +283.2 Q 2.5(4.7.1. T)102 297.6 R 2.5(os)-.8 G(uid or not to suid?)146.2 297.6 Q +23.5(.........................................................................\ +........................ 20)6.52 F 2.5(4.7.2. Should)102 312 R +(my alias database be writable?)2.5 E 23.5 +(........................................................................ 20) +5.47 F 2.5(4.8. Connection)87 326.4 R 1.56(Caching ...........................\ +..............................................................................\ +.....)2.5 F(21)494 326.4 Q 2.5(4.9. Name)87 340.8 R(Serv)2.5 E(er Access)-.15 E +23.5(.........................................................................\ +..................................... 21)2.85 F 2.5(4.10. Mo)87 355.2 R +(ving the Per)-.15 E(-User F)-.2 E(orw)-.15 E(ard Files)-.1 E 23.5(...........\ +......................................................................... 22) +3.84 F 2.5(4.11. Free)87 369.6 R 1.85(Space ..................................\ +..............................................................................\ +...........)2.5 F(22)494 369.6 Q 2.5(4.12. Pri)87 384 R -.25(va)-.25 G .3 -.15 +(cy F).25 H 1.93(lags ........................................................\ +...............................................................).15 F(22)494 +384 Q 2.5(4.13. Send)87 398.4 R(to Me T)2.5 E 2.08(oo ........................\ +..............................................................................\ +.............)-.8 F(22)494 398.4 Q 2.5(5. THE)72 412.8 R +(WHOLE SCOOP ON THE CONFIGURA)2.5 E(TION FILE)-1.11 E 23.5 +(........................................................ 23)4.64 F 2.5 +(5.1. Con\214guration)87 427.2 R(File Lines)2.5 E 23.5(.......................\ +..............................................................................\ +... 23)2.66 F 2.5(5.1.1. R)102 441.6 R(and S \212 re)2.5 E(writing rules)-.25 E +23.5(.........................................................................\ +................... 23)3.48 F 2.5(5.1.1.1. The)117 456 R(left hand side)2.5 E +23.5(.........................................................................\ +....................... 23)4.07 F 2.5(5.1.1.2. The)117 470.4 R(right hand side) +2.5 E 23.5(...................................................................\ +........................... 24)3.51 F 2.5(5.1.1.3. Semantics)117 484.8 R(of re) +2.5 E(writing rule sets)-.25 E 23.5 +(.......................................................................... 25) +4.6 F 2.5(5.1.1.4. IPC)117 499.2 R 1(mailers .................................\ +.........................................................................)2.5 F +(26)494 499.2 Q 2.5(5.1.2. D)102 513.6 R 2.5<8a64>2.5 G(e\214ne macro)156.72 +513.6 Q 23.5(.................................................................\ +....................................... 26)4.35 F 2.5(5.1.3. C)102 528 R +(and F \212 de\214ne classes)2.5 E 23.5(......................................\ +....................................................... 28)4.62 F 2.5(5.1.4. M) +102 542.4 R 2.5<8a64>2.5 G(e\214ne mailer)158.39 542.4 Q 23.5(................\ +..............................................................................\ +......... 29)4.62 F 2.5(5.1.5. H)102 556.8 R 2.5<8a64>2.5 G(e\214ne header) +156.72 556.8 Q 23.5(..........................................................\ +.............................................. 31)2.69 F 2.5(5.1.6. O)102 571.2 +R 2.5<8a73>2.5 G(et option)155.61 571.2 Q 23.5(...............................\ +..............................................................................\ + 31)4.61 F 2.5(5.1.7. P)102 585.6 R 2.5<8a70>2.5 G(recedence de\214nitions) +155.06 585.6 Q 23.5(..........................................................\ +................................ 35)3.24 F 2.5(5.1.8. V)102 600 R 2.5<8a63>2.5 +G(on\214guration v)156.16 600 Q(ersion le)-.15 E -.15(ve)-.25 G 4.62(l.).15 G +23.5(.........................................................................\ +........ 36)265.5 600 R 2.5(5.1.9. K)102 614.4 R 2.5<8a6b>2.5 G .3 -.15 +(ey \214)156.62 614.4 T(le declaration).15 E 23.5(............................\ +.................................................................. 36)4.88 F +2.5(5.2. Building)87 628.8 R 2.5(aC)2.5 G(on\214guration File From Scratch) +158.12 628.8 Q 23.5 +(......................................................................... 38) +3.77 F 2.5(5.2.1. What)102 643.2 R(you are trying to do)2.5 E 23.5(...........\ +..............................................................................\ +.... 38)2.96 F 2.5(5.2.2. Philosoph)102 657.6 R 3.54(y.)-.05 G 23.5(..........\ +..............................................................................\ +........................... 39)180.5 657.6 R 2.5(5.2.2.1. Lar)117 672 R +(ge site, man)-.18 E 2.5(yh)-.15 G(osts \212 minimum information)226.1 672 Q +23.5(................................................ 39)2.72 F 2.5 +(5.2.2.2. Small)117 686.4 R(site \212 complete information)2.5 E 23.5 +(.................................................................... 40)4.89 F +2.5(5.2.2.3. Single)117 700.8 R 1.27(host ....................................\ +.......................................................................)2.5 F +(40)494 700.8 Q 2.5(5.2.2.4. A)117 715.2 R(completely dif)2.5 E +(ferent philosoph)-.25 E 3.26(y.)-.05 G 23.5 +(..................................................................... 40)295.5 +715.2 R EP +%%Page: 4 60 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF 198.36(SMM:08-4 Sendmail)72 60 R +(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF 2.5(5.2.3. Rele) +102 96 R -.25(va)-.25 G(nt issues).25 E 23.5(.................................\ +............................................................................ 4\ +0)4.56 F 2.5(5.2.4. Ho)102 110.4 R 2.5(wt)-.25 G 2.5(op)153.97 110.4 S 2.38(ro\ +ceed .........................................................................\ +...................................)166.47 110.4 R(41)494 110.4 Q 2.5(5.2.5. T) +102 124.8 R(esting the re)-.7 E(writing rules \212 the \255bt \215ag)-.25 E +23.5(.................................................................... 41) +2.99 F 2.5(5.2.6. Building)102 139.2 R(mailer descriptions)2.5 E 23.5(........\ +..............................................................................\ +.. 42)4.61 F 2.5(5.3. The)87 153.6 R(User Database)2.5 E 23.5(................\ +..............................................................................\ +.................. 43)4.92 F 2.5(5.3.1. Structure)102 168 R +(of the user database)2.5 E 23.5(.............................................\ +.......................................... 43)2.7 F 2.5(5.3.2. User)102 182.4 R +(database semantics)2.5 E 23.5(...............................................\ +................................................ 44)3.25 F 2.5(6. O)72 196.8 R +(THER CONFIGURA)-.4 E 1.97(TION ..............................................\ +...........................................................)-1.11 F(44)494 +196.8 Q 2.5(6.1. P)87 211.2 R(arameters in src/Mak)-.15 E 1.55(e\214le .......\ +..............................................................................\ +...............)-.1 F(44)494 211.2 Q 2.5(6.2. P)87 225.6 R +(arameters in src/conf.h)-.15 E 23.5(.........................................\ +............................................................... 45)4.23 F 2.5 +(6.3. Con\214guration)87 240 R(in src/conf.c)2.5 E 23.5(......................\ +..............................................................................\ + 47)3.51 F 2.5(6.3.1. Built-in)102 254.4 R(Header Semantics)2.5 E 23.5(.......\ +..............................................................................\ +...... 47)4.9 F 2.5(6.3.2. Restricting)102 268.8 R(Use of Email)2.5 E 23.5(...\ +..............................................................................\ +............. 48)4.34 F 2.5(6.3.3. Load)102 283.2 R -1.17 -.74(Av e)2.5 H +(rage Computation).74 E 23.5(.................................................\ +......................................... 49)2.74 F 2.5(6.3.4. Ne)102 297.6 R +2.5(wD)-.25 G(atabase Map Classes)157.85 297.6 Q 23.5(........................\ +................................................................. 49)4.89 F 2.5 +(6.3.5. Queueing)102 312 R 1.56(Function .....................................\ +..................................................................)2.5 F(49)494 +312 Q 2.5(6.3.6. Refusing)102 326.4 R(Incoming SMTP Connections)2.5 E 23.5 +(....................................................................... 50) +2.94 F 2.5(6.3.7. Load)102 340.8 R -1.17 -.74(Av e)2.5 H(rage Computation).74 E +23.5(.........................................................................\ +................. 50)2.74 F 2.5(6.4. Con\214guration)87 355.2 R +(in src/daemon.c)2.5 E 23.5(..................................................\ +............................................ 50)4.62 F 2.5(7. CHANGES)72 369.6 +R(IN VERSION 8)2.5 E 23.5(....................................................\ +...................................................... 50)4.9 F 2.5 +(7.1. Connection)87 384 R 1.56(Caching .......................................\ +.......................................................................)2.5 F +(51)494 384 Q 2.5(7.2. MX)87 398.4 R 2.39(Piggybacking .......................\ +..............................................................................\ +............)2.5 F(51)494 398.4 Q 2.5(7.3. RFC)87 412.8 R(1123 Compliance)2.5 E +23.5(.........................................................................\ +................................. 51)3.77 F 2.5(7.4. Extended)87 427.2 R +(SMTP Support)2.5 E 23.5(.....................................................\ +.................................................. 51)2.94 F 2.5 +(7.5. Eight-Bit)87 441.6 R .44(Clean .........................................\ +.............................................................................) +2.5 F(51)494 441.6 Q 2.5(7.6. User)87 456 R .47(Database .....................\ +..............................................................................\ +.....................)2.5 F(51)494 456 Q 2.5(7.7. Impro)87 470.4 R -.15(ve)-.15 +G 2.5(dB).15 G(IND Support)154.75 470.4 Q 23.5(...............................\ +........................................................................ 51) +3.81 F 2.5(7.8. K)87 484.8 R -.15(ey)-.25 G(ed Files).15 E 23.5(..............\ +..............................................................................\ +................................ 52)3.35 F 2.5(7.9. Multi-W)87 499.2 R +(ord Classes)-.8 E 23.5(......................................................\ +......................................................... 52)3.47 F 2.5 +(7.10. Deferred)87 513.6 R(Macro Expansion)2.5 E 23.5(........................\ +......................................................................... 52) +4.65 F 2.5(7.11. IDENT)87 528 R(Protocol Support)2.5 E 23.5(..................\ +..............................................................................\ +..... 52)2.95 F 2.5(7.12. P)87 542.4 R(arsing Bug Fix)-.15 E .46(es ..........\ +..............................................................................\ +........................)-.15 F(52)494 542.4 Q 2.5(7.13. Separate)87 556.8 R +(En)2.5 E -.15(ve)-.4 G(lope/Header Processing).15 E 23.5(....................\ +............................................................ 52)4.37 F 2.5 +(7.14. Owner)87 571.2 R(-List Propag)-.2 E(ates to En)-.05 E -.15(ve)-.4 G 1.27 +(lope ........................................................................\ +............).15 F(52)494 571.2 Q 2.5(7.15. Dynamic)87 585.6 R +(Header Allocation)2.5 E 23.5(................................................\ +................................................ 52)3.25 F 2.5(7.16. Ne)87 600 +R 2.5(wC)-.25 G(ommand Line Flags)139.8 600 Q 23.5(...........................\ +....................................................................... 52)3.2 +F 2.5(7.17. Enhanced)87 614.4 R(Command Line Flags)2.5 E 23.5(................\ +......................................................................... 52) +4.9 F 2.5(7.18. Ne)87 628.8 R 2.5(wa)-.25 G(nd Old Con\214guration Line T) +137.57 628.8 Q .4(ypes .......................................................\ +.......................)-.8 F(53)494 628.8 Q 2.5(7.19. Ne)87 643.2 R 2.5(wO) +-.25 G .7(ptions .............................................................\ +...........................................................)140.35 643.2 R(53) +494 643.2 Q 2.5(7.20. Extended)87 657.6 R 1.56(Options .......................\ +..............................................................................\ +...........)2.5 F(53)494 657.6 Q 2.5(7.21. Ne)87 672 R 2.5(wM)-.25 G +(ailer Flags)142.02 672 Q 23.5(...............................................\ +................................................................. 53)4.04 F 2.5 +(7.22. Ne)87 686.4 R 2.5(wP)-.25 G(re-De\214ned Macros)138.69 686.4 Q 23.5(...\ +..............................................................................\ +................... 54)4.06 F 2.5(7.23. Ne)87 700.8 R 2.5(wL)-.25 G(HS T)139.24 +700.8 Q(ok)-.8 E 1.33(en .....................................................\ +.............................................................)-.1 F(54)494 +700.8 Q 2.5(7.24. Bigger)87 715.2 R(Def)2.5 E(aults ..........................\ +..............................................................................\ +............)-.1 E(54)494 715.2 Q EP +%%Page: 5 61 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q +(SMM:08-5)457.9 60 Q/F1 10/Times-Roman@0 SF 2.5(7.25. Dif)87 96 R(ferent Def) +-.25 E(ault T)-.1 E(uning P)-.45 E 1.99(arameters ............................\ +......................................................)-.15 F(54)494 96 Q 2.5 +(7.26. Auto-Quoting)87 110.4 R(in Addresses)2.5 E 23.5(.......................\ +.......................................................................... 54) +3.51 F 2.5(7.27. Symbolic)87 124.8 R(Names On Error Mailer)2.5 E 23.5(........\ +..............................................................................\ + 54)4.91 F 2.5(7.28. SMTP)87 139.2 R(VRFY Doesn')2.5 E 2.5(tE)-.18 G 1.18(xpan\ +d ............................................................................\ +................)209.88 139.2 R(54)494 139.2 Q 2.5(7.29. [IPC])87 153.6 R +(Mailers Allo)2.5 E 2.5(wM)-.25 G(ultiple Hosts)205.91 153.6 Q 23.5(..........\ +......................................................................... 54) +3.75 F 2.5(7.30. Aliases)87 168 R 1.29(Extended ..............................\ +..............................................................................\ +.....)2.5 F(55)494 168 Q 2.5(7.31. Portability)87 182.4 R +(and Security Enhancements)2.5 E 23.5(........................................\ +....................................... 55)2.68 F 2.5(7.32. Miscellaneous)87 +196.8 R 1.29(Changes .........................................................\ +..............................................)2.5 F(55)494 196.8 Q 2.5(8. A)72 +211.2 R(CKNO)-.4 E .1(WLEDGEMENTS ............................................\ +................................................................)-.35 F(55)494 +211.2 Q(Appendix A.)72 225.6 Q(COMMAND LINE FLA)5 E 1.97(GS ..................\ +.......................................................................)-.4 F +(56)494 225.6 Q(Appendix B.)72 240 Q -.1(QU)5 G(EUE FILE FORMA).1 E 1.38(TS ..\ +..............................................................................\ +............)-1.11 F(58)494 240 Q(Appendix C.)72 254.4 Q(SUMMAR)5 E 2.5(YO)-.65 +G 2.5(FS)188.85 254.4 S(UPPOR)202.47 254.4 Q 2.5(TF)-.6 G 1.12(ILES ..........\ +....................................................................)248.27 +254.4 R(60)494 254.4 Q EP +%%Trailer +end +%%EOF diff --git a/usr.sbin/sendmail/doc/refs b/usr.sbin/sendmail/doc/refs deleted file mode 100644 index 1fa44ea834..0000000000 --- a/usr.sbin/sendmail/doc/refs +++ /dev/null @@ -1,26 +0,0 @@ -From solomon Wed Nov 3 11:56:14 1982 -Date: 3 Nov 1982 11:46:42-CST -From: solomon (Marvin Solomon) -To: eric@dbvax -Subject: fyi, some references - -Here are some references you might be interested in including: - -%A A. Birrell -%A R. Levin -%A R. Needham -%A G. Schroeder. -%T Grapevine -%J Proceedings of the 8th ACM Symposium on Operating Systems Principles, -%D December, 1981 - -%A D. C. Oppen -%A Y. K. Dalal -%T The Clearinghouse: -A decentralized agent for locating named objects in a distributed -environment -%C Xerox Office Products Division -%R Technical Report OPD-T8103 -%D October 1981 - - diff --git a/usr.sbin/sendmail/doc/rfc819.lpr b/usr.sbin/sendmail/doc/rfc819.lpr deleted file mode 100644 index d66f8d9144..0000000000 --- a/usr.sbin/sendmail/doc/rfc819.lpr +++ /dev/null @@ -1,1044 +0,0 @@ - - -Network Working Group Zaw-Sing Su (SRI) -Request for Comments: 819 Jon Postel (ISI) - August 1982 - - - - The Domain Naming Convention for Internet User Applications - - - - -1. Introduction - - For many years, the naming convention "<user>@<host>" has served the - ARPANET user community for its mail system, and the substring - "<host>" has been used for other applications such as file transfer - (FTP) and terminal access (Telnet). With the advent of network - interconnection, this naming convention needs to be generalized to - accommodate internetworking. A decision has recently been reached to - replace the simple name field, "<host>", by a composite name field, - "<domain>" [2]. This note is an attempt to clarify this generalized - naming convention, the Internet Naming Convention, and to explore the - implications of its adoption for Internet name service and user - applications. - - The following example illustrates the changes in naming convention: - - ARPANET Convention: Fred@ISIF - Internet Convention: Fred@F.ISI.ARPA - - The intent is that the Internet names be used to form a - tree-structured administrative dependent, rather than a strictly - topology dependent, hierarchy. The left-to-right string of name - components proceeds from the most specific to the most general, that - is, the root of the tree, the administrative universe, is on the - right. - - The name service for realizing the Internet naming convention is - assumed to be application independent. It is not a part of any - particular application, but rather an independent name service serves - different user applications. - -2. The Structural Model - - The Internet naming convention is based on the domain concept. The - name of a domain consists of a concatenation of one or more <simple - names>. A domain can be considered as a region of jurisdiction for - name assignment and of responsibility for name-to-address - translation. The set of domains forms a hierarchy. - - Using a graph theory representation, this hierarchy may be modeled as - a directed graph. A directed graph consists of a set of nodes and a - - -Su & Postel [Page 1] - - - -RFC 819 August 1982; - - - collection of arcs, where arcs are identified by ordered pairs of - distinct nodes [1]. Each node of the graph represents a domain. An - ordered pair (B, A), an arc from B to A, indicates that B is a - subdomain of domain A, and B is a simple name unique within A. We - will refer to B as a child of A, and A a parent of B. The directed - graph that best describes the naming hierarchy is called an - "in-tree", which is a rooted tree with all arcs directed towards the - root (Figure 1). The root of the tree represents the naming universe, - ancestor of all domains. Endpoints (or leaves) of the tree are the - lowest-level domains. - - U - / | \ - / | \ U -- Naming Universe - ^ ^ ^ I -- Intermediate Domain - | | | E -- Endpoint Domain - I E I - / \ | - ^ ^ ^ - | | | - E E I - / | \ - ^ ^ ^ - | | | - E E E - - Figure 1 - The In-Tree Model for Domain Hierarchy - - The simple name of a child in this model is necessarily unique within - its parent domain. Since the simple name of the child's parent is - unique within the child's grandparent domain, the child can be - uniquely named in its grandparent domain by the concatenation of its - simple name followed by its parent's simple name. - - For example, if the simple name of a child is "C1" then no other - child of the same parent may be named "C1". Further, if the - parent of this child is named "P1", then "P1" is a unique simple - name in the child's grandparent domain. Thus, the concatenation - C1.P1 is unique in C1's grandparent domain. - - Similarly, each element of the hierarchy is uniquely named in the - universe by its complete name, the concatenation of its simple name - and those for the domains along the trail leading to the naming - universe. - - The hierarchical structure of the Internet naming convention supports - decentralization of naming authority and distribution of name service - capability. We assume a naming authority and a name server - - -Su & Postel [Page 2] - - - -RFC 819 August 1982; - - - associated with each domain. In Sections 5 and 6 respectively the - name service and the naming authority are discussed. - - Within an endpoint domain, unique names are assigned to <user> - representing user mailboxes. User mailboxes may be viewed as - children of their respective domains. - - In reality, anomalies may exist violating the in-tree model of naming - hierarchy. Overlapping domains imply multiple parentage, i.e., an - entity of the naming hierarchy being a child of more than one domain. - It is conceivable that ISI can be a member of the ARPA domain as well - as a member of the USC domain (Figure 2). Such a relation - constitutes an anomaly to the rule of one-connectivity between any - two points of a tree. The common child and the sub-tree below it - become descendants of both parent domains. - - U - / | \ - / . \ - . . ARPA - . . | \ - USC | \ - \ | . - \ | . - ISI - - Figure 2 - Anomaly in the In-Tree Model - - Some issues resulting from multiple parentage are addressed in - Appendix B. The general implications of multiple parentage are a - subject for further investigation. - -3. Advantage of Absolute Naming - - Absolute naming implies that the (complete) names are assigned with - respect to a universal reference point. The advantage of absolute - naming is that a name thus assigned can be universally interpreted - with respect to the universal reference point. The Internet naming - convention provides absolute naming with the naming universe as its - universal reference point. - - For relative naming, an entity is named depending upon the position - of the naming entity relative to that of the named entity. A set of - hosts running the "unix" operating system exchange mail using a - method called "uucp". The naming convention employed by uucp is an - example of relative naming. The mail recipient is typically named by - a source route identifying a chain of locally known hosts linking the - - - -Su & Postel [Page 3] - - - -RFC 819 August 1982; - - - sender's host to the recipient's. A destination name can be, for - example, - - "alpha!beta!gamma!john", - - where "alpha" is presumably known to the originating host, "beta" is - known to "alpha", and so on. - - The uucp mail system has demonstrated many of the problems inherent - to relative naming. When the host names are only locally - interpretable, routing optimization becomes impossible. A reply - message may have to traverse the reverse route to the original sender - in order to be forwarded to other parties. - - Furthermore, if a message is forwarded by one of the original - recipients or passed on as the text of another message, the frame of - reference of the relative source route can be completely lost. Such - relative naming schemes have severe problems for many of the uses - that we depend upon in the ARPA Internet community. - -4. Interoperability - - To allow interoperation with a different naming convention, the names - assigned by a foreign naming convention need to be accommodated. - Given the autonomous nature of domains, a foreign naming environment - may be incorporated as a domain anywhere in the hierarchy. Within - the naming universe, the name service for a domain is provided within - that domain. Thus, a foreign naming convention can be independent of - the Internet naming convention. What is implied here is that no - standard convention for naming needs to be imposed to allow - interoperations among heterogeneous naming environments. - - For example: - - There might be a naming convention, say, in the FOO world, - something like "<user>%<host>%<area>". Communications with an - entity in that environment can be achieved from the Internet - community by simply appending ".FOO" on the end of the name in - that foreign convention. - - John%ISI-Tops20-7%California.FOO - - Another example: - - One way of accommodating the "uucp world" described in the last - section is to declare it as a foreign system. Thus, a uucp - name - - "alpha!beta!gamma!john" - - -Su & Postel [Page 4] - - - -RFC 819 August 1982; - - - might be known in the Internet community as - - "alpha!beta!gamma!john.UUCP". - - Communicating with a complex subdomain is another case which can - be treated as interoperation. A complex subdomain is a domain - with complex internal naming structure presumably unknown to the - outside world (or the outside world does not care to be concerned - with its complexity). - - For the mail system application, the names embedded in the message - text are often used by the destination for such purpose as to reply - to the original message. Thus, the embedded names may need to be - converted for the benefit of the name server in the destination - environment. - - Conversion of names on the boundary between heterogeneous naming - environments is a complex subject. The following example illustrates - some of the involved issues. - - For example: - - A message is sent from the Internet community to the FOO - environment. It may bear the "From" and "To" fields as: - - From: Fred@F.ISI.ARPA - To: John%ISI-Tops20-7%California.FOO - - where "FOO" is a domain independent of the Internet naming - environment. The interface on the boundary of the two - environments may be represented by a software module. We may - assume this interface to be an entity of the Internet community - as well as an entity of the FOO community. For the benefit of - the FOO environment, the "From" and "To" fields need to be - modified upon the message's arrival at the boundary. One may - view naming as a separate layer of protocol, and treat - conversion as a protocol translation. The matter is - complicated when the message is sent to more than one - destination within different naming environments; or the - message is destined within an environment not sharing boundary - with the originating naming environment. - - While the general subject concerning conversion is beyond the scope - of this note, a few questions are raised in Appendix D. - - - - - - - -Su & Postel [Page 5] - - - -RFC 819 August 1982; - - -5. Name Service - - Name service is a network service providing name-to-address - translation. Such service may be achieved in a number of ways. For - a simple networking environment, it can be accomplished with a single - central database containing name-to-address correspondence for all - the pertinent network entities, such as hosts. - - In the case of the old ARPANET host names, a central database is - duplicated in each individual host. The originating module of an - application process would query the local name service (e.g., make a - system call) to obtain network address for the destination host. With - the proliferation of networks and an accelerating increase in the - number of hosts participating in networking, the ever growing size, - update frequency, and the dissemination of the central database makes - this approach unmanageable. - - The hierarchical structure of the Internet naming convention supports - decentralization of naming authority and distribution of name service - capability. It readily accommodates growth of the naming universe. - It allows an arbitrary number of hierarchical layers. The addition - of a new domain adds little complexity to an existing Internet - system. - - The name service at each domain is assumed to be provided by one or - more name servers. There are two models for how a name server - completes its work, these might be called "iterative" and - "recursive". - - For an iterative name server there may be two kinds of responses. - The first kind of response is a destination address. The second - kind of response is the address of another name server. If the - response is a destination address, then the query is satisfied. If - the response is the address of another name server, then the query - must be repeated using that name server, and so on until a - destination address is obtained. - - For a recursive name server there is only one kind of response -- - a destination address. This puts an obligation on the name server - to actually make the call on another name server if it can't - answer the query itself. - - It is noted that looping can be avoided since the names presented for - translation can only be of finite concatenation. However, care - should be taken in employing mechanisms such as a pointer to the next - simple name for resolution. - - We believe that some name servers will be recursive, but we don't - believe that all will be. This means that the caller must be - - -Su & Postel [Page 6] - - - -RFC 819 August 1982; - - - prepared for either type of server. Further discussion and examples - of name service is given in Appendix C. - - The basic name service at each domain is the translation of simple - names to addresses for all of its children. However, if only this - basic name service is provided, the use of complete (or fully - qualified) names would be required. Such requirement can be - unreasonable in practice. Thus, we propose the use of partial names - in the context in which their uniqueness is preserved. By - construction, naming uniqueness is preserved within the domain of a - common ancestry. Thus, a partially qualified name is constructed by - omitting from the complete name ancestors common to the communicating - parties. When a partially qualified name leaves its context of - uniqueness it must be additionally qualified. - - The use of partially qualified names places a requirement on the - Internet name service. To satisfy this requirement, the name service - at each domain must be capable of, in addition to the basic service, - resolving simple names for all of its ancestors (including itself) - and their children. In Appendix B, the required distinction among - simple names for such resolution is addressed. - -6. Naming Authority - - Associated with each domain there must be a naming authority to - assign simple names and ensure proper distinction among simple names. - - Note that if the use of partially qualified names is allowed in a - sub-domain, the uniqueness of simple names inside that sub-domain is - insufficient to avoid ambiguity with names outside the subdomain. - Appendix B discusses simple name assignment in a sub-domain that - would allow the use of partially qualified names without ambiguity. - - Administratively, associated with each domain there is a single - person (or office) called the registrar. The registrar of the naming - universe specifies the top-level set of domains and designates a - registrar for each of these domains. The registrar for any given - domain maintains the naming authority for that domain. - -7. Network-Oriented Applications - - For user applications such as file transfer and terminal access, the - remote host needs to be named. To be compatible with ARPANET naming - convention, a host can be treated as an endpoint domain. - - Many operating systems or programming language run-time environments - provide functions or calls (JSYSs, SVCs, UUOs, SYSs, etc.) for - standard services (e.g., time-of-day, account-of-logged-in-user, - convert-number-to-string). It is likely to be very helpful if such a - - -Su & Postel [Page 7] - - - -RFC 819 August 1982; - - - function or call is developed for translating a host name to an - address. Indeed, several systems on the ARPANET already have such - facilities for translating an ARPANET host name into an ARPANET - address based on internal tables. - - We recommend that this provision of a standard function or call for - translating names to addresses be extended to accept names of - Internet convention. This will promote a consistent interface to the - users of programs involving internetwork activities. The standard - facility for translating Internet names to Internet addresses should - include all the mechanisms available on the host, such as checking a - local table or cache of recently checked names, or consulting a name - server via the Internet. - -8. Mail Relaying - - Relaying is a feature adopted by more and more mail systems. - Relaying facilitates, among other things, interoperations between - heterogeneous mail systems. The term "relay" is used to describe the - situation where a message is routed via one or more intermediate - points between the sender and the recipient. The mail relays are - normally specified explicitly as relay points in the instructions for - message delivery. Usually, each of the intermediate relays assume - responsibility for the relayed message [3]. - - A point should be made on the basic difference between mail - relaying and the uucp naming system. The difference is that - although mail relaying with absolute naming can also be considered - as a form of source routing, the names of each intermediate points - and that of the destination are universally interpretable, while - the host names along a source route of the uucp convention is - relative and thus only locally interpretable. - - The Internet naming convention explicitly allows interoperations - among heterogeneous systems. This implies that the originator of a - communication may name a destination which resides in a foreign - system. The probability is that the destination network address may - not be comprehensible to the transport system of the originator. - Thus, an implicit relaying mechanism is called for at the boundary - between the domains. The function of this implicit relay is the same - as the explicit relay. - - - - - - - - - - -Su & Postel [Page 8] - - - -RFC 819 August 1982; - - -9. Implementation - - The Actual Domains - - The initial set of top-level names include: - - ARPA - - This represents the set of organizations involved in the - Internet system through the authority of the U.S. Defense - Advanced Research Projects Agency. This includes all the - research and development hosts on the ARPANET and hosts on - many other nets as well. But note very carefully that the - top-level domain "ARPA" does not map one-to-one with the - ARPANET -- domains are administrative, not topological. - - Transition - - In the transition from the ARPANET naming convention to the - Internet naming convention, a host name may be used as a simple - name for an endpoint domain. Thus, if "USC-ISIF" is an ARPANET - host name, then "USC-ISIF.ARPA" is the name of an Internet domain. - -10. Summary - - A hierarchical naming convention based on the domain concept has been - adopted by the Internet community. It is an absolute naming - convention defined along administrative rather than topological - boundaries. This naming convention is adaptive for interoperations - with other naming conventions. Thus, no standard convention needs to - be imposed for interoperations among heterogeneous naming - environments. - - This Internet naming convention allows distributed name service and - naming authority functions at each domain. We have specified these - functions required at each domain. Also discussed are implications - on network-oriented applications, mail systems, and administrative - aspects of this convention. - - - - - - - - - - - - - -Su & Postel [Page 9] - - - -RFC 819 August 1982; - - -APPENDIX A - - The BNF Specification - - We present here a rather detailed "BNF" definition of the allowed - form for a computer mail "mailbox" composed of a "local-part" and a - "domain" (separated by an at sign). Clearly, the domain can be used - separately in other network-oriented applications. - - <mailbox> ::= <local-part> "@" <domain> - - <local-part> ::= <string> | <quoted-string> - - <string> ::= <char> | <char> <string> - - <quoted-string> ::= """ <qtext> """ - - <qtext> ::= "\" <x> | "\" <x> <qtext> | <q> | <q> <qtext> - - <char> ::= <c> | "\" <x> - - <domain> ::= <naming-domain> | <naming-domain> "." <domain> - - <naming-domain> ::= <simple-name> | <address> - - <simple-name> ::= <a> <ldh-str> <let-dig> - - <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str> - - <let-dig> ::= <a> | <d> - - <let-dig-hyp> ::= <a> | <d> | "-" - - <address> :: = "#" <number> | "[" <dotnum> "]" - - <number> ::= <d> | <d> <number> - - <dotnum> ::= <snum> "." <snum> "." <snum> "." <snum> - - <snum> ::= one, two, or three digits representing a decimal integer - value in the range 0 through 255 - - <a> ::= any one of the 52 alphabetic characters A through Z in upper - case and a through z in lower case - - <c> ::= any one of the 128 ASCII characters except <s> or <SP> - - <d> ::= any one of the ten digits 0 through 9 - - - -Su & Postel [Page 10] - - - -RFC 819 August 1982; - - - <q> ::= any one of the 128 ASCII characters except CR, LF, quote ("), - or backslash (\) - - <x> ::= any one of the 128 ASCII characters (no exceptions) - - <s> ::= "<", ">", "(", ")", "[", "]", "\", ".", ",", ";", ":", "@", - """, and the control characters (ASCII codes 0 through 31 inclusive - and 127) - - Note that the backslash, "\", is a quote character, which is used to - indicate that the next character is to be used literally (instead of - its normal interpretation). For example, "Joe\,Smith" could be used - to indicate a single nine character user field with comma being the - fourth character of the field. - - The simple names that make up a domain may contain both upper and - lower case letters (as well as digits and hyphen), but these names - are not case sensitive. - - Hosts are generally known by names. Sometimes a host is not known to - the translation function and communication is blocked. To bypass - this barrier two forms of addresses are also allowed for host - "names". One form is a decimal integer prefixed by a pound sign, "#". - Another form, called "dotted decimal", is four small decimal integers - separated by dots and enclosed by brackets, e.g., "[123.255.37.2]", - which indicates a 32-bit ARPA Internet Address in four 8-bit fields. - (Of course, these numeric address forms are specific to the Internet, - other forms may have to be provided if this problem arises in other - transport systems.) - - - - - - - - - - - - - - - - - - - - - - -Su & Postel [Page 11] - - - -RFC 819 August 1982; - - -APPENDIX B - - An Aside on the Assignment of Simple Names - - In the following example, there are two naming hierarchies joining at - the naming universe 'U'. One consists of domains (S, R, N, J, P, Q, - B, A); and the other (L, E, F, G, H, D, C, K, B, A). Domain B is - assumed to have multiple parentage as shown. - - U - / \ - / \ - J L - / \ - N E - / \ / \ - R P D F - / \ | \ \ - S Q C (X) G - \ / \ \ - B K H - / - A - - Figure 3 - Illustration of Requirements for the Distinction of Simple Names - - Suppose someone at A tries to initiate communication with destination - H. The fully qualified destination name would be - - H.G.F.E.L.U - - Omitting common ancestors, the partially qualified name for the - destination would be - - H.G.F - - To permit the case of partially qualified names, name server at A - needs to resolve the simple name F, i.e., F needs to be distinct from - all the other simple names in its database. - - To enable the name server of a domain to resolve simple names, a - simple name for a child needs to be assigned distinct from those of - all of its ancestors and their immediate children. However, such - distinction would not be sufficient to allow simple name resolution - at lower-level domains because lower-level domains could have - multiple parentage below the level of this domain. - - In the example above, let us assume that a name is to be assigned - - -Su & Postel [Page 12] - - - -RFC 819 August 1982; - - - to a new domain X by D. To allow name server at D to resolve - simple names, the name for X must be distinct from L, E, D, C, F, - and J. However, allowing A to resolve simple names, X needs to be - also distinct from A, B, K, as well as from Q, P, N, and R. - - The following observations can be made. - - Simple names along parallel trails (distinct trails leading from - one domain to the naming universe) must be distinct, e.g., N must - be distinct from E for B or A to properly resolve simple names. - - No universal uniqueness of simple names is called for, e.g., the - simple name S does not have to be distinct from that of E, F, G, - H, D, C, K, Q, B, or A. - - The lower the level at which a domain occurs, the more immune it - is to the requirement of naming uniqueness. - - To satisfy the required distinction of simple names for proper - resolution at all levels, a naming authority needs to ensure the - simple name to be assigned distinct from those in the name server - databases at the endpoint naming domains within its domain. As an - example, for D to assign a simple name for X, it would need to - consult databases at A and K. It is, however, acceptable to have - simple names under domain A identical with those under K. Failure of - such distinct assignment of simple names by naming authority of one - domain would jeopardize the capability of simple name resolution for - entities within the subtree under that domain. - - - - - - - - - - - - - - - - - - - - - - - -Su & Postel [Page 13] - - - -RFC 819 August 1982; - - -APPENDIX C - - Further Discussion of Name Service and Name Servers - - The name service on a system should appear to the programmer of an - application program simply as a system call or library subroutine. - Within that call or subroutine there may be several types of methods - for resolving the name string into an address. - - First, a local table may be consulted. This table may be a - complete table and may be updated frequently, or it may simply be - a cache of the few latest name to address mappings recently - determined. - - Second, a call may be made to a name server to resolve the string - into a destination address. - - Third, a call may be made to a name server to resolve the string - into a relay address. - - Whenever a name server is called it may be a recursive server or an - interactive server. - - If the server is recursive, the caller won't be able to tell if - the server itself had the information to resolve the query or - called another server recursively (except perhaps for the time it - takes). - - If the server is iterative, the caller must be prepared for either - the answer to its query, or a response indicating that it should - call on a different server. - - It should be noted that the main name service discussed in this memo - is simply a name string to address service. For some applications - there may be other services needed. - - For example, even within the Internet there are several procedures - or protocols for actually transferring mail. One need is to - determine which mail procedures a destination host can use. - Another need is to determine the name of a relay host if the - source and destination hosts do not have a common mail procedure. - These more specialized services must be specific to each - application since the answers may be application dependent, but - the basic name to address translation is application independent. - - - - - - - -Su & Postel [Page 14] - - - -RFC 819 August 1982; - - -APPENDIX D - - Further Discussion of Interoperability and Protocol Translations - - The translation of protocols from one system to another is often - quite difficult. Following are some questions that stem from - considering the translations of addresses between mail systems: - - What is the impact of different addressing environments (i.e., - environments of different address formats)? - - It is noted that the boundary of naming environment may or may not - coincide with the boundary of different mail systems. Should the - conversion of naming be independent of the application system? - - The boundary between different addressing environments may or may - not coincide with that of different naming environments or - application systems. Some generic approach appears to be - necessary. - - If the conversion of naming is to be independent of the - application system, some form of interaction appears necessary - between the interface module of naming conversion with some - application level functions, such as the parsing and modification - of message text. - - To accommodate encryption, conversion may not be desirable at all. - What then can be an alternative to conversion? - - - - - - - - - - - - - - - - - - - - - - - -Su & Postel [Page 15] - - - -RFC 819 August 1982; - - -GLOSSARY - - address - - An address is a numerical identifier for the topological location - of the named entity. - - name - - A name is an alphanumeric identifier associated with the named - entity. For unique identification, a name needs to be unique in - the context in which the name is used. A name can be mapped to an - address. - - complete (fully qualified) name - - A complete name is a concatenation of simple names representing - the hierarchical relation of the named with respect to the naming - universe, that is it is the concatenation of the simple names of - the domain structure tree nodes starting with its own name and - ending with the top level node name. It is a unique name in the - naming universe. - - partially qualified name - - A partially qualified name is an abbreviation of the complete name - omitting simple names of the common ancestors of the communicating - parties. - - simple name - - A simple name is an alphanumeric identifier unique only within its - parent domain. - - domain - - A domain defines a region of jurisdiction for name assignment and - of responsibility for name-to-address translation. - - naming universe - - Naming universe is the ancestor of all network entities. - - naming environment - - A networking environment employing a specific naming convention. - - - - - -Su & Postel [Page 16] - - - -RFC 819 August 1982; - - - name service - - Name service is a network service for name-to-address mapping. - - name server - - A name server is a network mechanism (e.g., a process) realizing - the function of name service. - - naming authority - - Naming authority is an administrative entity having the authority - for assigning simple names and responsibility for resolving naming - conflict. - - parallel relations - - A network entity may have one or more hierarchical relations with - respect to the naming universe. Such multiple relations are - parallel relations to each other. - - multiple parentage - - A network entity has multiple parentage when it is assigned a - simple name by more than one naming domain. - - - - - - - - - - - - - - - - - - - - - - - - - - -Su & Postel [Page 17] - - - -RFC 819 August 1982; - - -REFERENCES - - [1] F. Harary, "Graph Theory", Addison-Wesley, Reading, - Massachusetts, 1969. - - [2] J. Postel, "Computer Mail Meeting Notes", RFC-805, - USC/Information Sciences Institute, 8 February 1982. - - [3] J. Postel, "Simple Mail Transfer Protocol", RFC-821, - USC/Information Sciences Institute, August 1982. - - [4] D. Crocker, "Standard for the Format of ARPA Internet Text - Messages", RFC-822, Department of Electrical Engineering, University - of Delaware, August 1982. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Su & Postel [Page 18] - diff --git a/usr.sbin/sendmail/doc/rfc821.lpr b/usr.sbin/sendmail/doc/rfc821.lpr deleted file mode 100644 index d877b72cff..0000000000 --- a/usr.sbin/sendmail/doc/rfc821.lpr +++ /dev/null @@ -1,4050 +0,0 @@ - - - - RFC 821 - - - - - - SIMPLE MAIL TRANSFER PROTOCOL - - - - Jonathan B. Postel - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - August 1982 - - - - Information Sciences Institute - University of Southern California - 4676 Admiralty Way - Marina del Rey, California 90291 - - (213) 822-1511 - - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - TABLE OF CONTENTS - - 1. INTRODUCTION .................................................. 1 - - 2. THE SMTP MODEL ................................................ 2 - - 3. THE SMTP PROCEDURE ............................................ 4 - - 3.1. Mail ..................................................... 4 - 3.2. Forwarding ............................................... 7 - 3.3. Verifying and Expanding .................................. 8 - 3.4. Sending and Mailing ..................................... 11 - 3.5. Opening and Closing ..................................... 13 - 3.6. Relaying ................................................ 14 - 3.7. Domains ................................................. 17 - 3.8. Changing Roles .......................................... 18 - - 4. THE SMTP SPECIFICATIONS ...................................... 19 - - 4.1. SMTP Commands ........................................... 19 - 4.1.1. Command Semantics ..................................... 19 - 4.1.2. Command Syntax ........................................ 27 - 4.2. SMTP Replies ............................................ 34 - 4.2.1. Reply Codes by Function Group ......................... 35 - 4.2.2. Reply Codes in Numeric Order .......................... 36 - 4.3. Sequencing of Commands and Replies ...................... 37 - 4.4. State Diagrams .......................................... 39 - 4.5. Details ................................................. 41 - 4.5.1. Minimum Implementation ................................ 41 - 4.5.2. Transparency .......................................... 41 - 4.5.3. Sizes ................................................. 42 - - APPENDIX A: TCP ................................................. 44 - APPENDIX B: NCP ................................................. 45 - APPENDIX C: NITS ................................................ 46 - APPENDIX D: X.25 ................................................ 47 - APPENDIX E: Theory of Reply Codes ............................... 48 - APPENDIX F: Scenarios ........................................... 51 - - GLOSSARY ......................................................... 64 - - REFERENCES ....................................................... 67 - - - - -Network Working Group J. Postel -Request for Comments: DRAFT ISI -Replaces: RFC 788, 780, 772 August 1982 - - SIMPLE MAIL TRANSFER PROTOCOL - - -1. INTRODUCTION - - The objective of Simple Mail Transfer Protocol (SMTP) is to transfer - mail reliably and efficiently. - - SMTP is independent of the particular transmission subsystem and - requires only a reliable ordered data stream channel. Appendices A, - B, C, and D describe the use of SMTP with various transport services. - A Glossary provides the definitions of terms as used in this - document. - - An important feature of SMTP is its capability to relay mail across - transport service environments. A transport service provides an - interprocess communication environment (IPCE). An IPCE may cover one - network, several networks, or a subset of a network. It is important - to realize that transport systems (or IPCEs) are not one-to-one with - networks. A process can communicate directly with another process - through any mutually known IPCE. Mail is an application or use of - interprocess communication. Mail can be communicated between - processes in different IPCEs by relaying through a process connected - to two (or more) IPCEs. More specifically, mail can be relayed - between hosts on different transport systems by a host on both - transport systems. - - - - - - - - - - - - - - - - - - - - - - - - -Postel [Page 1] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - -2. THE SMTP MODEL - - The SMTP design is based on the following model of communication: as - the result of a user mail request, the sender-SMTP establishes a - two-way transmission channel to a receiver-SMTP. The receiver-SMTP - may be either the ultimate destination or an intermediate. SMTP - commands are generated by the sender-SMTP and sent to the - receiver-SMTP. SMTP replies are sent from the receiver-SMTP to the - sender-SMTP in response to the commands. - - Once the transmission channel is established, the SMTP-sender sends a - MAIL command indicating the sender of the mail. If the SMTP-receiver - can accept mail it responds with an OK reply. The SMTP-sender then - sends a RCPT command identifying a recipient of the mail. If the - SMTP-receiver can accept mail for that recipient it responds with an - OK reply; if not, it responds with a reply rejecting that recipient - (but not the whole mail transaction). The SMTP-sender and - SMTP-receiver may negotiate several recipients. When the recipients - have been negotiated the SMTP-sender sends the mail data, terminating - with a special sequence. If the SMTP-receiver successfully processes - the mail data it responds with an OK reply. The dialog is purposely - lock-step, one-at-a-time. - - ------------------------------------------------------------- - - - +----------+ +----------+ - +------+ | | | | - | User |<-->| | SMTP | | - +------+ | Sender- |Commands/Replies| Receiver-| - +------+ | SMTP |<-------------->| SMTP | +------+ - | File |<-->| | and Mail | |<-->| File | - |System| | | | | |System| - +------+ +----------+ +----------+ +------+ - - - Sender-SMTP Receiver-SMTP - - Model for SMTP Use - - Figure 1 - - ------------------------------------------------------------- - - The SMTP provides mechanisms for the transmission of mail; directly - from the sending user's host to the receiving user's host when the - - - -[Page 2] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - two host are connected to the same transport service, or via one or - more relay SMTP-servers when the source and destination hosts are not - connected to the same transport service. - - To be able to provide the relay capability the SMTP-server must be - supplied with the name of the ultimate destination host as well as - the destination mailbox name. - - The argument to the MAIL command is a reverse-path, which specifies - who the mail is from. The argument to the RCPT command is a - forward-path, which specifies who the mail is to. The forward-path - is a source route, while the reverse-path is a return route (which - may be used to return a message to the sender when an error occurs - with a relayed message). - - When the same message is sent to multiple recipients the SMTP - encourages the transmission of only one copy of the data for all the - recipients at the same destination host. - - The mail commands and replies have a rigid syntax. Replies also have - a numeric code. In the following, examples appear which use actual - commands and replies. The complete lists of commands and replies - appears in Section 4 on specifications. - - Commands and replies are not case sensitive. That is, a command or - reply word may be upper case, lower case, or any mixture of upper and - lower case. Note that this is not true of mailbox user names. For - some hosts the user name is case sensitive, and SMTP implementations - must take case to preserve the case of user names as they appear in - mailbox arguments. Host names are not case sensitive. - - Commands and replies are composed of characters from the ASCII - character set [1]. When the transport service provides an 8-bit byte - (octet) transmission channel, each 7-bit character is transmitted - right justified in an octet with the high order bit cleared to zero. - - When specifying the general form of a command or reply, an argument - (or special symbol) will be denoted by a meta-linguistic variable (or - constant), for example, "<string>" or "<reverse-path>". Here the - angle brackets indicate these are meta-linguistic variables. - However, some arguments use the angle brackets literally. For - example, an actual reverse-path is enclosed in angle brackets, i.e., - "<John.Smith@USC-ISI.ARPA>" is an instance of <reverse-path> (the - angle brackets are actually transmitted in the command or reply). - - - - - -Postel [Page 3] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - -3. THE SMTP PROCEDURES - - This section presents the procedures used in SMTP in several parts. - First comes the basic mail procedure defined as a mail transaction. - Following this are descriptions of forwarding mail, verifying mailbox - names and expanding mailing lists, sending to terminals instead of or - in combination with mailboxes, and the opening and closing exchanges. - At the end of this section are comments on relaying, a note on mail - domains, and a discussion of changing roles. Throughout this section - are examples of partial command and reply sequences, several complete - scenarios are presented in Appendix F. - - 3.1. MAIL - - There are three steps to SMTP mail transactions. The transaction - is started with a MAIL command which gives the sender - identification. A series of one or more RCPT commands follows - giving the receiver information. Then a DATA command gives the - mail data. And finally, the end of mail data indicator confirms - the transaction. - - The first step in the procedure is the MAIL command. The - <reverse-path> contains the source mailbox. - - MAIL <SP> FROM:<reverse-path> <CRLF> - - This command tells the SMTP-receiver that a new mail - transaction is starting and to reset all its state tables and - buffers, including any recipients or mail data. It gives the - reverse-path which can be used to report errors. If accepted, - the receiver-SMTP returns a 250 OK reply. - - The <reverse-path> can contain more than just a mailbox. The - <reverse-path> is a reverse source routing list of hosts and - source mailbox. The first host in the <reverse-path> should be - the host sending this command. - - The second step in the procedure is the RCPT command. - - RCPT <SP> TO:<forward-path> <CRLF> - - This command gives a forward-path identifying one recipient. - If accepted, the receiver-SMTP returns a 250 OK reply, and - stores the forward-path. If the recipient is unknown the - receiver-SMTP returns a 550 Failure reply. This second step of - the procedure can be repeated any number of times. - - - -[Page 4] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - The <forward-path> can contain more than just a mailbox. The - <forward-path> is a source routing list of hosts and the - destination mailbox. The first host in the <forward-path> - should be the host receiving this command. - - The third step in the procedure is the DATA command. - - DATA <CRLF> - - If accepted, the receiver-SMTP returns a 354 Intermediate reply - and considers all succeeding lines to be the message text. - When the end of text is received and stored the SMTP-receiver - sends a 250 OK reply. - - Since the mail data is sent on the transmission channel the end - of the mail data must be indicated so that the command and - reply dialog can be resumed. SMTP indicates the end of the - mail data by sending a line containing only a period. A - transparency procedure is used to prevent this from interfering - with the user's text (see Section 4.5.2). - - Please note that the mail data includes the memo header - items such as Date, Subject, To, Cc, From [2]. - - The end of mail data indicator also confirms the mail - transaction and tells the receiver-SMTP to now process the - stored recipients and mail data. If accepted, the - receiver-SMTP returns a 250 OK reply. The DATA command should - fail only if the mail transaction was incomplete (for example, - no recipients), or if resources are not available. - - The above procedure is an example of a mail transaction. These - commands must be used only in the order discussed above. - Example 1 (below) illustrates the use of these commands in a mail - transaction. - - - - - - - - - - - - - - -Postel [Page 5] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - ------------------------------------------------------------- - - Example of the SMTP Procedure - - This SMTP example shows mail sent by Smith at host Alpha.ARPA, - to Jones, Green, and Brown at host Beta.ARPA. Here we assume - that host Alpha contacts host Beta directly. - - S: MAIL FROM:<Smith@Alpha.ARPA> - R: 250 OK - - S: RCPT TO:<Jones@Beta.ARPA> - R: 250 OK - - S: RCPT TO:<Green@Beta.ARPA> - R: 550 No such user here - - S: RCPT TO:<Brown@Beta.ARPA> - R: 250 OK - - S: DATA - R: 354 Start mail input; end with <CRLF>.<CRLF> - S: Blah blah blah... - S: ...etc. etc. etc. - S: <CRLF>.<CRLF> - R: 250 OK - - The mail has now been accepted for Jones and Brown. Green did - not have a mailbox at host Beta. - - Example 1 - - ------------------------------------------------------------- - - - - - - - - - - - - - - - - -[Page 6] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - 3.2. FORWARDING - - There are some cases where the destination information in the - <forward-path> is incorrect, but the receiver-SMTP knows the - correct destination. In such cases, one of the following replies - should be used to allow the sender to contact the correct - destination. - - 251 User not local; will forward to <forward-path> - - This reply indicates that the receiver-SMTP knows the user's - mailbox is on another host and indicates the correct - forward-path to use in the future. Note that either the - host or user or both may be different. The receiver takes - responsibility for delivering the message. - - 551 User not local; please try <forward-path> - - This reply indicates that the receiver-SMTP knows the user's - mailbox is on another host and indicates the correct - forward-path to use. Note that either the host or user or - both may be different. The receiver refuses to accept mail - for this user, and the sender must either redirect the mail - according to the information provided or return an error - response to the originating user. - - Example 2 illustrates the use of these responses. - - ------------------------------------------------------------- - - Example of Forwarding - - Either - - S: RCPT TO:<Postel@USC-ISI.ARPA> - R: 251 User not local; will forward to <Postel@USC-ISIF.ARPA> - - Or - - S: RCPT TO:<Paul@USC-ISIB.ARPA> - R: 551 User not local; please try <Mockapetris@USC-ISIF.ARPA> - - Example 2 - - ------------------------------------------------------------- - - - - -Postel [Page 7] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - 3.3. VERIFYING AND EXPANDING - - SMTP provides as additional features, commands to verify a user - name or expand a mailing list. This is done with the VRFY and - EXPN commands, which have character string arguments. For the - VRFY command, the string is a user name, and the response may - include the full name of the user and must include the mailbox of - the user. For the EXPN command, the string identifies a mailing - list, and the multiline response may include the full name of the - users and must give the mailboxes on the mailing list. - - "User name" is a fuzzy term and used purposely. If a host - implements the VRFY or EXPN commands then at least local mailboxes - must be recognized as "user names". If a host chooses to - recognize other strings as "user names" that is allowed. - - In some hosts the distinction between a mailing list and an alias - for a single mailbox is a bit fuzzy, since a common data structure - may hold both types of entries, and it is possible to have mailing - lists of one mailbox. If a request is made to verify a mailing - list a positive response can be given if on receipt of a message - so addressed it will be delivered to everyone on the list, - otherwise an error should be reported (e.g., "550 That is a - mailing list, not a user"). If a request is made to expand a user - name a positive response can be formed by returning a list - containing one name, or an error can be reported (e.g., "550 That - is a user name, not a mailing list"). - - In the case of a multiline reply (normal for EXPN) exactly one - mailbox is to be specified on each line of the reply. In the case - of an ambiguous request, for example, "VRFY Smith", where there - are two Smith's the response must be "553 User ambiguous". - - The case of verifying a user name is straightforward as shown in - example 3. - - - - - - - - - - - - - - -[Page 8] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - ------------------------------------------------------------- - - Example of Verifying a User Name - - Either - - S: VRFY Smith - R: 250 Fred Smith <Smith@USC-ISIF.ARPA> - - Or - - S: VRFY Smith - R: 251 User not local; will forward to <Smith@USC-ISIQ.ARPA> - - Or - - S: VRFY Jones - R: 550 String does not match anything. - - Or - - S: VRFY Jones - R: 551 User not local; please try <Jones@USC-ISIQ.ARPA> - - Or - - S: VRFY Gourzenkyinplatz - R: 553 User ambiguous. - - Example 3 - - ------------------------------------------------------------- - - - - - - - - - - - - - - - - - -Postel [Page 9] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - The case of expanding a mailbox list requires a multiline reply as - shown in example 4. - - ------------------------------------------------------------- - - Example of Expanding a Mailing List - - Either - - S: EXPN Example-People - R: 250-Jon Postel <Postel@USC-ISIF.ARPA> - R: 250-Fred Fonebone <Fonebone@USC-ISIQ.ARPA> - R: 250-Sam Q. Smith <SQSmith@USC-ISIQ.ARPA> - R: 250-Quincy Smith <@USC-ISIF.ARPA:Q-Smith@ISI-VAXA.ARPA> - R: 250-<joe@foo-unix.ARPA> - R: 250 <xyz@bar-unix.ARPA> - - Or - - S: EXPN Executive-Washroom-List - R: 550 Access Denied to You. - - Example 4 - - ------------------------------------------------------------- - - The character string arguments of the VRFY and EXPN commands - cannot be further restricted due to the variety of implementations - of the user name and mailbox list concepts. On some systems it - may be appropriate for the argument of the EXPN command to be a - file name for a file containing a mailing list, but again there is - a variety of file naming conventions in the Internet. - - The VRFY and EXPN commands are not included in the minimum - implementation (Section 4.5.1), and are not required to work - across relays when they are implemented. - - - - - - - - - - - - - -[Page 10] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - 3.4. SENDING AND MAILING - - The main purpose of SMTP is to deliver messages to user's - mailboxes. A very similar service provided by some hosts is to - deliver messages to user's terminals (provided the user is active - on the host). The delivery to the user's mailbox is called - "mailing", the delivery to the user's terminal is called - "sending". Because in many hosts the implementation of sending is - nearly identical to the implementation of mailing these two - functions are combined in SMTP. However the sending commands are - not included in the required minimum implementation - (Section 4.5.1). Users should have the ability to control the - writing of messages on their terminals. Most hosts permit the - users to accept or refuse such messages. - - The following three command are defined to support the sending - options. These are used in the mail transaction instead of the - MAIL command and inform the receiver-SMTP of the special semantics - of this transaction: - - SEND <SP> FROM:<reverse-path> <CRLF> - - The SEND command requires that the mail data be delivered to - the user's terminal. If the user is not active (or not - accepting terminal messages) on the host a 450 reply may - returned to a RCPT command. The mail transaction is - successful if the message is delivered the terminal. - - SOML <SP> FROM:<reverse-path> <CRLF> - - The Send Or MaiL command requires that the mail data be - delivered to the user's terminal if the user is active (and - accepting terminal messages) on the host. If the user is - not active (or not accepting terminal messages) then the - mail data is entered into the user's mailbox. The mail - transaction is successful if the message is delivered either - to the terminal or the mailbox. - - SAML <SP> FROM:<reverse-path> <CRLF> - - The Send And MaiL command requires that the mail data be - delivered to the user's terminal if the user is active (and - accepting terminal messages) on the host. In any case the - mail data is entered into the user's mailbox. The mail - transaction is successful if the message is delivered the - mailbox. - - - -Postel [Page 11] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - The same reply codes that are used for the MAIL commands are used - for these commands. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[Page 12] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - 3.5. OPENING AND CLOSING - - At the time the transmission channel is opened there is an - exchange to ensure that the hosts are communicating with the hosts - they think they are. - - The following two commands are used in transmission channel - opening and closing: - - HELO <SP> <domain> <CRLF> - - QUIT <CRLF> - - In the HELO command the host sending the command identifies - itself; the command may be interpreted as saying "Hello, I am - <domain>". - - ------------------------------------------------------------- - - Example of Connection Opening - - R: 220 BBN-UNIX.ARPA Simple Mail Transfer Service Ready - S: HELO USC-ISIF.ARPA - R: 250 BBN-UNIX.ARPA - - Example 5 - - ------------------------------------------------------------- - - ------------------------------------------------------------- - - Example of Connection Closing - - S: QUIT - R: 221 BBN-UNIX.ARPA Service closing transmission channel - - Example 6 - - ------------------------------------------------------------- - - - - - - - - - - -Postel [Page 13] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - 3.6. RELAYING - - The forward-path may be a source route of the form - "@ONE,@TWO:JOE@THREE", where ONE, TWO, and THREE are hosts. This - form is used to emphasize the distinction between an address and a - route. The mailbox is an absolute address, and the route is - information about how to get there. The two concepts should not - be confused. - - Conceptually the elements of the forward-path are moved to the - reverse-path as the message is relayed from one server-SMTP to - another. The reverse-path is a reverse source route, (i.e., a - source route from the current location of the message to the - originator of the message). When a server-SMTP deletes its - identifier from the forward-path and inserts it into the - reverse-path, it must use the name it is known by in the - environment it is sending into, not the environment the mail came - from, in case the server-SMTP is known by different names in - different environments. - - If when the message arrives at an SMTP the first element of the - forward-path is not the identifier of that SMTP the element is not - deleted from the forward-path and is used to determine the next - SMTP to send the message to. In any case, the SMTP adds its own - identifier to the reverse-path. - - Using source routing the receiver-SMTP receives mail to be relayed - to another server-SMTP The receiver-SMTP may accept or reject the - task of relaying the mail in the same way it accepts or rejects - mail for a local user. The receiver-SMTP transforms the command - arguments by moving its own identifier from the forward-path to - the beginning of the reverse-path. The receiver-SMTP then becomes - a sender-SMTP, establishes a transmission channel to the next SMTP - in the forward-path, and sends it the mail. - - The first host in the reverse-path should be the host sending the - SMTP commands, and the first host in the forward-path should be - the host receiving the SMTP commands. - - Notice that the forward-path and reverse-path appear in the SMTP - commands and replies, but not necessarily in the message. That - is, there is no need for these paths and especially this syntax to - appear in the "To:" , "From:", "CC:", etc. fields of the message - header. - - If a server-SMTP has accepted the task of relaying the mail and - - - -[Page 14] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - later finds that the forward-path is incorrect or that the mail - cannot be delivered for whatever reason, then it must construct an - "undeliverable mail" notification message and send it to the - originator of the undeliverable mail (as indicated by the - reverse-path). - - This notification message must be from the server-SMTP at this - host. Of course, server-SMTPs should not send notification - messages about problems with notification messages. One way to - prevent loops in error reporting is to specify a null reverse-path - in the MAIL command of a notification message. When such a - message is relayed it is permissible to leave the reverse-path - null. A MAIL command with a null reverse-path appears as follows: - - MAIL FROM:<> - - An undeliverable mail notification message is shown in example 7. - This notification is in response to a message originated by JOE at - HOSTW and sent via HOSTX to HOSTY with instructions to relay it on - to HOSTZ. What we see in the example is the transaction between - HOSTY and HOSTX, which is the first step in the return of the - notification message. - - - - - - - - - - - - - - - - - - - - - - - - - - - -Postel [Page 15] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - ------------------------------------------------------------- - - Example Undeliverable Mail Notification Message - - S: MAIL FROM:<> - R: 250 ok - S: RCPT TO:<@HOSTX.ARPA:JOE@HOSTW.ARPA> - R: 250 ok - S: DATA - R: 354 send the mail data, end with . - S: Date: 23 Oct 81 11:22:33 - S: From: SMTP@HOSTY.ARPA - S: To: JOE@HOSTW.ARPA - S: Subject: Mail System Problem - S: - S: Sorry JOE, your message to SAM@HOSTZ.ARPA lost. - S: HOSTZ.ARPA said this: - S: "550 No Such User" - S: . - R: 250 ok - - Example 7 - - ------------------------------------------------------------- - - - - - - - - - - - - - - - - - - - - - - - - - -[Page 16] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - 3.7. DOMAINS - - Domains are a recently introduced concept in the ARPA Internet - mail system. The use of domains changes the address space from a - flat global space of simple character string host names to a - hierarchically structured rooted tree of global addresses. The - host name is replaced by a domain and host designator which is a - sequence of domain element strings separated by periods with the - understanding that the domain elements are ordered from the most - specific to the most general. - - For example, "USC-ISIF.ARPA", "Fred.Cambridge.UK", and - "PC7.LCS.MIT.ARPA" might be host-and-domain identifiers. - - Whenever domain names are used in SMTP only the official names are - used, the use of nicknames or aliases is not allowed. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Postel [Page 17] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - 3.8. CHANGING ROLES - - The TURN command may be used to reverse the roles of the two - programs communicating over the transmission channel. - - If program-A is currently the sender-SMTP and it sends the TURN - command and receives an ok reply (250) then program-A becomes the - receiver-SMTP. - - If program-B is currently the receiver-SMTP and it receives the - TURN command and sends an ok reply (250) then program-B becomes - the sender-SMTP. - - To refuse to change roles the receiver sends the 502 reply. - - Please note that this command is optional. It would not normally - be used in situations where the transmission channel is TCP. - However, when the cost of establishing the transmission channel is - high, this command may be quite useful. For example, this command - may be useful in supporting be mail exchange using the public - switched telephone system as a transmission channel, especially if - some hosts poll other hosts for mail exchanges. - - - - - - - - - - - - - - - - - - - - - - - - - - - -[Page 18] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - -4. THE SMTP SPECIFICATIONS - - 4.1. SMTP COMMANDS - - 4.1.1. COMMAND SEMANTICS - - The SMTP commands define the mail transfer or the mail system - function requested by the user. SMTP commands are character - strings terminated by <CRLF>. The command codes themselves are - alphabetic characters terminated by <SP> if parameters follow - and <CRLF> otherwise. The syntax of mailboxes must conform to - receiver site conventions. The SMTP commands are discussed - below. The SMTP replies are discussed in the Section 4.2. - - A mail transaction involves several data objects which are - communicated as arguments to different commands. The - reverse-path is the argument of the MAIL command, the - forward-path is the argument of the RCPT command, and the mail - data is the argument of the DATA command. These arguments or - data objects must be transmitted and held pending the - confirmation communicated by the end of mail data indication - which finalizes the transaction. The model for this is that - distinct buffers are provided to hold the types of data - objects, that is, there is a reverse-path buffer, a - forward-path buffer, and a mail data buffer. Specific commands - cause information to be appended to a specific buffer, or cause - one or more buffers to be cleared. - - HELLO (HELO) - - This command is used to identify the sender-SMTP to the - receiver-SMTP. The argument field contains the host name of - the sender-SMTP. - - The receiver-SMTP identifies itself to the sender-SMTP in - the connection greeting reply, and in the response to this - command. - - This command and an OK reply to it confirm that both the - sender-SMTP and the receiver-SMTP are in the initial state, - that is, there is no transaction in progress and all state - tables and buffers are cleared. - - - - - - - -Postel [Page 19] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - MAIL (MAIL) - - This command is used to initiate a mail transaction in which - the mail data is delivered to one or more mailboxes. The - argument field contains a reverse-path. - - The reverse-path consists of an optional list of hosts and - the sender mailbox. When the list of hosts is present, it - is a "reverse" source route and indicates that the mail was - relayed through each host on the list (the first host in the - list was the most recent relay). This list is used as a - source route to return non-delivery notices to the sender. - As each relay host adds itself to the beginning of the list, - it must use its name as known in the IPCE to which it is - relaying the mail rather than the IPCE from which the mail - came (if they are different). In some types of error - reporting messages (for example, undeliverable mail - notifications) the reverse-path may be null (see Example 7). - - This command clears the reverse-path buffer, the - forward-path buffer, and the mail data buffer; and inserts - the reverse-path information from this command into the - reverse-path buffer. - - RECIPIENT (RCPT) - - This command is used to identify an individual recipient of - the mail data; multiple recipients are specified by multiple - use of this command. - - The forward-path consists of an optional list of hosts and a - required destination mailbox. When the list of hosts is - present, it is a source route and indicates that the mail - must be relayed to the next host on the list. If the - receiver-SMTP does not implement the relay function it may - user the same reply it would for an unknown local user - (550). - - When mail is relayed, the relay host must remove itself from - the beginning forward-path and put itself at the beginning - of the reverse-path. When mail reaches its ultimate - destination (the forward-path contains only a destination - mailbox), the receiver-SMTP inserts it into the destination - mailbox in accordance with its host mail conventions. - - - - - -[Page 20] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - For example, mail received at relay host A with arguments - - FROM:<USERX@HOSTY.ARPA> - TO:<@HOSTA.ARPA,@HOSTB.ARPA:USERC@HOSTD.ARPA> - - will be relayed on to host B with arguments - - FROM:<@HOSTA.ARPA:USERX@HOSTY.ARPA> - TO:<@HOSTB.ARPA:USERC@HOSTD.ARPA>. - - This command causes its forward-path argument to be appended - to the forward-path buffer. - - DATA (DATA) - - The receiver treats the lines following the command as mail - data from the sender. This command causes the mail data - from this command to be appended to the mail data buffer. - The mail data may contain any of the 128 ASCII character - codes. - - The mail data is terminated by a line containing only a - period, that is the character sequence "<CRLF>.<CRLF>" (see - Section 4.5.2 on Transparency). This is the end of mail - data indication. - - The end of mail data indication requires that the receiver - must now process the stored mail transaction information. - This processing consumes the information in the reverse-path - buffer, the forward-path buffer, and the mail data buffer, - and on the completion of this command these buffers are - cleared. If the processing is successful the receiver must - send an OK reply. If the processing fails completely the - receiver must send a failure reply. - - When the receiver-SMTP accepts a message either for relaying - or for final delivery it inserts at the beginning of the - mail data a time stamp line. The time stamp line indicates - the identity of the host that sent the message, and the - identity of the host that received the message (and is - inserting this time stamp), and the date and time the - message was received. Relayed messages will have multiple - time stamp lines. - - When the receiver-SMTP makes the "final delivery" of a - message it inserts at the beginning of the mail data a - - - -Postel [Page 21] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - return path line. The return path line preserves the - information in the <reverse-path> from the MAIL command. - Here, final delivery means the message leaves the SMTP - world. Normally, this would mean it has been delivered to - the destination user, but in some cases it may be further - processed and transmitted by another mail system. - - It is possible for the mailbox in the return path be - different from the actual sender's mailbox, for example, - if error responses are to be delivered a special error - handling mailbox rather than the message senders. - - The preceding two paragraphs imply that the final mail data - will begin with a return path line, followed by one or more - time stamp lines. These lines will be followed by the mail - data header and body [2]. See Example 8. - - Special mention is needed of the response and further action - required when the processing following the end of mail data - indication is partially successful. This could arise if - after accepting several recipients and the mail data, the - receiver-SMTP finds that the mail data can be successfully - delivered to some of the recipients, but it cannot be to - others (for example, due to mailbox space allocation - problems). In such a situation, the response to the DATA - command must be an OK reply. But, the receiver-SMTP must - compose and send an "undeliverable mail" notification - message to the originator of the message. Either a single - notification which lists all of the recipients that failed - to get the message, or separate notification messages must - be sent for each failed recipient (see Example 7). All - undeliverable mail notification messages are sent using the - MAIL command (even if they result from processing a SEND, - SOML, or SAML command). - - - - - - - - - - - - - - - -[Page 22] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - ------------------------------------------------------------- - - Example of Return Path and Received Time Stamps - - Return-Path: <@GHI.ARPA,@DEF.ARPA,@ABC.ARPA:JOE@ABC.ARPA> - Received: from GHI.ARPA by JKL.ARPA ; 27 Oct 81 15:27:39 PST - Received: from DEF.ARPA by GHI.ARPA ; 27 Oct 81 15:15:13 PST - Received: from ABC.ARPA by DEF.ARPA ; 27 Oct 81 15:01:59 PST - Date: 27 Oct 81 15:01:01 PST - From: JOE@ABC.ARPA - Subject: Improved Mailing System Installed - To: SAM@JKL.ARPA - - This is to inform you that ... - - Example 8 - - ------------------------------------------------------------- - - SEND (SEND) - - This command is used to initiate a mail transaction in which - the mail data is delivered to one or more terminals. The - argument field contains a reverse-path. This command is - successful if the message is delivered to a terminal. - - The reverse-path consists of an optional list of hosts and - the sender mailbox. When the list of hosts is present, it - is a "reverse" source route and indicates that the mail was - relayed through each host on the list (the first host in the - list was the most recent relay). This list is used as a - source route to return non-delivery notices to the sender. - As each relay host adds itself to the beginning of the list, - it must use its name as known in the IPCE to which it is - relaying the mail rather than the IPCE from which the mail - came (if they are different). - - This command clears the reverse-path buffer, the - forward-path buffer, and the mail data buffer; and inserts - the reverse-path information from this command into the - reverse-path buffer. - - SEND OR MAIL (SOML) - - This command is used to initiate a mail transaction in which - the mail data is delivered to one or more terminals or - - - -Postel [Page 23] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - mailboxes. For each recipient the mail data is delivered to - the recipient's terminal if the recipient is active on the - host (and accepting terminal messages), otherwise to the - recipient's mailbox. The argument field contains a - reverse-path. This command is successful if the message is - delivered to a terminal or the mailbox. - - The reverse-path consists of an optional list of hosts and - the sender mailbox. When the list of hosts is present, it - is a "reverse" source route and indicates that the mail was - relayed through each host on the list (the first host in the - list was the most recent relay). This list is used as a - source route to return non-delivery notices to the sender. - As each relay host adds itself to the beginning of the list, - it must use its name as known in the IPCE to which it is - relaying the mail rather than the IPCE from which the mail - came (if they are different). - - This command clears the reverse-path buffer, the - forward-path buffer, and the mail data buffer; and inserts - the reverse-path information from this command into the - reverse-path buffer. - - SEND AND MAIL (SAML) - - This command is used to initiate a mail transaction in which - the mail data is delivered to one or more terminals and - mailboxes. For each recipient the mail data is delivered to - the recipient's terminal if the recipient is active on the - host (and accepting terminal messages), and for all - recipients to the recipient's mailbox. The argument field - contains a reverse-path. This command is successful if the - message is delivered to the mailbox. - - The reverse-path consists of an optional list of hosts and - the sender mailbox. When the list of hosts is present, it - is a "reverse" source route and indicates that the mail was - relayed through each host on the list (the first host in the - list was the most recent relay). This list is used as a - source route to return non-delivery notices to the sender. - As each relay host adds itself to the beginning of the list, - it must use its name as known in the IPCE to which it is - relaying the mail rather than the IPCE from which the mail - came (if they are different). - - This command clears the reverse-path buffer, the - - - -[Page 24] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - forward-path buffer, and the mail data buffer; and inserts - the reverse-path information from this command into the - reverse-path buffer. - - RESET (RSET) - - This command specifies that the current mail transaction is - to be aborted. Any stored sender, recipients, and mail data - must be discarded, and all buffers and state tables cleared. - The receiver must send an OK reply. - - VERIFY (VRFY) - - This command asks the receiver to confirm that the argument - identifies a user. If it is a user name, the full name of - the user (if known) and the fully specified mailbox are - returned. - - This command has no effect on any of the reverse-path - buffer, the forward-path buffer, or the mail data buffer. - - EXPAND (EXPN) - - This command asks the receiver to confirm that the argument - identifies a mailing list, and if so, to return the - membership of that list. The full name of the users (if - known) and the fully specified mailboxes are returned in a - multiline reply. - - This command has no effect on any of the reverse-path - buffer, the forward-path buffer, or the mail data buffer. - - HELP (HELP) - - This command causes the receiver to send helpful information - to the sender of the HELP command. The command may take an - argument (e.g., any command name) and return more specific - information as a response. - - This command has no effect on any of the reverse-path - buffer, the forward-path buffer, or the mail data buffer. - - - - - - - - -Postel [Page 25] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - NOOP (NOOP) - - This command does not affect any parameters or previously - entered commands. It specifies no action other than that - the receiver send an OK reply. - - This command has no effect on any of the reverse-path - buffer, the forward-path buffer, or the mail data buffer. - - QUIT (QUIT) - - This command specifies that the receiver must send an OK - reply, and then close the transmission channel. - - The receiver should not close the transmission channel until - it receives and replies to a QUIT command (even if there was - an error). The sender should not close the transmission - channel until it send a QUIT command and receives the reply - (even if there was an error response to a previous command). - If the connection is closed prematurely the receiver should - act as if a RSET command had been received (canceling any - pending transaction, but not undoing any previously - completed transaction), the sender should act as if the - command or transaction in progress had received a temporary - error (4xx). - - TURN (TURN) - - This command specifies that the receiver must either (1) - send an OK reply and then take on the role of the - sender-SMTP, or (2) send a refusal reply and retain the role - of the receiver-SMTP. - - If program-A is currently the sender-SMTP and it sends the - TURN command and receives an OK reply (250) then program-A - becomes the receiver-SMTP. Program-A is then in the initial - state as if the transmission channel just opened, and it - then sends the 220 service ready greeting. - - If program-B is currently the receiver-SMTP and it receives - the TURN command and sends an OK reply (250) then program-B - becomes the sender-SMTP. Program-B is then in the initial - state as if the transmission channel just opened, and it - then expects to receive the 220 service ready greeting. - - To refuse to change roles the receiver sends the 502 reply. - - - -[Page 26] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - There are restrictions on the order in which these command may - be used. - - The first command in a session must be the HELO command. - The HELO command may be used later in a session as well. If - the HELO command argument is not acceptable a 501 failure - reply must be returned and the receiver-SMTP must stay in - the same state. - - The NOOP, HELP, EXPN, and VRFY commands can be used at any - time during a session. - - The MAIL, SEND, SOML, or SAML commands begin a mail - transaction. Once started a mail transaction consists of - one of the transaction beginning commands, one or more RCPT - commands, and a DATA command, in that order. A mail - transaction may be aborted by the RSET command. There may - be zero or more transactions in a session. - - If the transaction beginning command argument is not - acceptable a 501 failure reply must be returned and the - receiver-SMTP must stay in the same state. If the commands - in a transaction are out of order a 503 failure reply must - be returned and the receiver-SMTP must stay in the same - state. - - The last command in a session must be the QUIT command. The - QUIT command can not be used at any other time in a session. - - 4.1.2. COMMAND SYNTAX - - The commands consist of a command code followed by an argument - field. Command codes are four alphabetic characters. Upper - and lower case alphabetic characters are to be treated - identically. Thus, any of the following may represent the mail - command: - - MAIL Mail mail MaIl mAIl - - This also applies to any symbols representing parameter values, - such as "TO" or "to" for the forward-path. Command codes and - the argument fields are separated by one or more spaces. - However, within the reverse-path and forward-path arguments - case is important. In particular, in some hosts the user - "smith" is different from the user "Smith". - - - - -Postel [Page 27] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - The argument field consists of a variable length character - string ending with the character sequence <CRLF>. The receiver - is to take no action until this sequence is received. - - Square brackets denote an optional argument field. If the - option is not taken, the appropriate default is implied. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[Page 28] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - The following are the SMTP commands: - - HELO <SP> <domain> <CRLF> - - MAIL <SP> FROM:<reverse-path> <CRLF> - - RCPT <SP> TO:<forward-path> <CRLF> - - DATA <CRLF> - - RSET <CRLF> - - SEND <SP> FROM:<reverse-path> <CRLF> - - SOML <SP> FROM:<reverse-path> <CRLF> - - SAML <SP> FROM:<reverse-path> <CRLF> - - VRFY <SP> <string> <CRLF> - - EXPN <SP> <string> <CRLF> - - HELP [<SP> <string>] <CRLF> - - NOOP <CRLF> - - QUIT <CRLF> - - TURN <CRLF> - - - - - - - - - - - - - - - - - - - - -Postel [Page 29] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - The syntax of the above argument fields (using BNF notation - where applicable) is given below. The "..." notation indicates - that a field may be repeated one or more times. - - <reverse-path> ::= <path> - - <forward-path> ::= <path> - - <path> ::= "<" [ <a-d-l> ":" ] <mailbox> ">" - - <a-d-l> ::= <at-domain> | <at-domain> "," <a-d-l> - - <at-domain> ::= "@" <domain> - - <domain> ::= <element> | <element> "." <domain> - - <element> ::= <name> | "#" <number> | "[" <dotnum> "]" - - <mailbox> ::= <local-part> "@" <domain> - - <local-part> ::= <dot-string> | <quoted-string> - - <name> ::= <a> <ldh-str> <let-dig> - - <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str> - - <let-dig> ::= <a> | <d> - - <let-dig-hyp> ::= <a> | <d> | "-" - - <dot-string> ::= <string> | <string> "." <dot-string> - - <string> ::= <char> | <char> <string> - - <quoted-string> ::= """ <qtext> """ - - <qtext> ::= "\" <x> | "\" <x> <qtext> | <q> | <q> <qtext> - - <char> ::= <c> | "\" <x> - - <dotnum> ::= <snum> "." <snum> "." <snum> "." <snum> - - <number> ::= <d> | <d> <number> - - <CRLF> ::= <CR> <LF> - - - - -[Page 30] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - <CR> ::= the carriage return character (ASCII code 13) - - <LF> ::= the line feed character (ASCII code 10) - - <SP> ::= the space character (ASCII code 32) - - <snum> ::= one, two, or three digits representing a decimal - integer value in the range 0 through 255 - - <a> ::= any one of the 52 alphabetic characters A through Z - in upper case and a through z in lower case - - <c> ::= any one of the 128 ASCII characters, but not any - <special> or <SP> - - <d> ::= any one of the ten digits 0 through 9 - - <q> ::= any one of the 128 ASCII characters except <CR>, - <LF>, quote ("), or backslash (\) - - <x> ::= any one of the 128 ASCII characters (no exceptions) - - <special> ::= "<" | ">" | "(" | ")" | "[" | "]" | "\" | "." - | "," | ";" | ":" | "@" """ | the control - characters (ASCII codes 0 through 31 inclusive and - 127) - - Note that the backslash, "\", is a quote character, which is - used to indicate that the next character is to be used - literally (instead of its normal interpretation). For example, - "Joe\,Smith" could be used to indicate a single nine character - user field with comma being the fourth character of the field. - - Hosts are generally known by names which are translated to - addresses in each host. Note that the name elements of domains - are the official names -- no use of nicknames or aliases is - allowed. - - Sometimes a host is not known to the translation function and - communication is blocked. To bypass this barrier two numeric - forms are also allowed for host "names". One form is a decimal - integer prefixed by a pound sign, "#", which indicates the - number is the address of the host. Another form is four small - decimal integers separated by dots and enclosed by brackets, - e.g., "[123.255.37.2]", which indicates a 32-bit ARPA Internet - Address in four 8-bit fields. - - - -Postel [Page 31] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - The time stamp line and the return path line are formally - defined as follows: - - <return-path-line> ::= "Return-Path:" <SP><reverse-path><CRLF> - - <time-stamp-line> ::= "Received:" <SP> <stamp> <CRLF> - - <stamp> ::= <from-domain> <by-domain> <opt-info> ";" - <daytime> - - <from-domain> ::= "FROM" <SP> <domain> <SP> - - <by-domain> ::= "BY" <SP> <domain> <SP> - - <opt-info> ::= [<via>] [<with>] [<id>] [<for>] - - <via> ::= "VIA" <SP> <link> <SP> - - <with> ::= "WITH" <SP> <protocol> <SP> - - <id> ::= "ID" <SP> <string> <SP> - - <for> ::= "FOR" <SP> <path> <SP> - - <link> ::= The standard names for links are registered with - the Network Information Center. - - <protocol> ::= The standard names for protocols are - registered with the Network Information Center. - - <daytime> ::= <SP> <date> <SP> <time> - - <date> ::= <dd> <SP> <mon> <SP> <yy> - - <time> ::= <hh> ":" <mm> ":" <ss> <SP> <zone> - - <dd> ::= the one or two decimal integer day of the month in - the range 1 to 31. - - <mon> ::= "JAN" | "FEB" | "MAR" | "APR" | "MAY" | "JUN" | - "JUL" | "AUG" | "SEP" | "OCT" | "NOV" | "DEC" - - <yy> ::= the two decimal integer year of the century in the - range 00 to 99. - - - - - -[Page 32] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - <hh> ::= the two decimal integer hour of the day in the - range 00 to 24. - - <mm> ::= the two decimal integer minute of the hour in the - range 00 to 59. - - <ss> ::= the two decimal integer second of the minute in the - range 00 to 59. - - <zone> ::= "UT" for Universal Time (the default) or other - time zone designator (as in [2]). - - - - ------------------------------------------------------------- - - Return Path Example - - Return-Path: <@CHARLIE.ARPA,@BAKER.ARPA:JOE@ABLE.ARPA> - - Example 9 - - ------------------------------------------------------------- - - ------------------------------------------------------------- - - Time Stamp Line Example - - Received: FROM ABC.ARPA BY XYZ.ARPA ; 22 OCT 81 09:23:59 PDT - - Received: from ABC.ARPA by XYZ.ARPA via TELENET with X25 - id M12345 for Smith@PDQ.ARPA ; 22 OCT 81 09:23:59 PDT - - Example 10 - - ------------------------------------------------------------- - - - - - - - - - - - - - -Postel [Page 33] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - 4.2. SMTP REPLIES - - Replies to SMTP commands are devised to ensure the synchronization - of requests and actions in the process of mail transfer, and to - guarantee that the sender-SMTP always knows the state of the - receiver-SMTP. Every command must generate exactly one reply. - - The details of the command-reply sequence are made explicit in - Section 5.3 on Sequencing and Section 5.4 State Diagrams. - - An SMTP reply consists of a three digit number (transmitted as - three alphanumeric characters) followed by some text. The number - is intended for use by automata to determine what state to enter - next; the text is meant for the human user. It is intended that - the three digits contain enough encoded information that the - sender-SMTP need not examine the text and may either discard it or - pass it on to the user, as appropriate. In particular, the text - may be receiver-dependent and context dependent, so there are - likely to be varying texts for each reply code. A discussion of - the theory of reply codes is given in Appendix E. Formally, a - reply is defined to be the sequence: a three-digit code, <SP>, - one line of text, and <CRLF>, or a multiline reply (as defined in - Appendix E). Only the EXPN and HELP commands are expected to - result in multiline replies in normal circumstances, however - multiline replies are allowed for any command. - - - - - - - - - - - - - - - - - - - - - - - - -[Page 34] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - 4.2.1. REPLY CODES BY FUNCTION GROUPS - - 500 Syntax error, command unrecognized - [This may include errors such as command line too long] - 501 Syntax error in parameters or arguments - 502 Command not implemented - 503 Bad sequence of commands - 504 Command parameter not implemented - - 211 System status, or system help reply - 214 Help message - [Information on how to use the receiver or the meaning of a - particular non-standard command; this reply is useful only - to the human user] - - 220 <domain> Service ready - 221 <domain> Service closing transmission channel - 421 <domain> Service not available, - closing transmission channel - [This may be a reply to any command if the service knows it - must shut down] - - 250 Requested mail action okay, completed - 251 User not local; will forward to <forward-path> - 450 Requested mail action not taken: mailbox unavailable - [E.g., mailbox busy] - 550 Requested action not taken: mailbox unavailable - [E.g., mailbox not found, no access] - 451 Requested action aborted: error in processing - 551 User not local; please try <forward-path> - 452 Requested action not taken: insufficient system storage - 552 Requested mail action aborted: exceeded storage allocation - 553 Requested action not taken: mailbox name not allowed - [E.g., mailbox syntax incorrect] - 354 Start mail input; end with <CRLF>.<CRLF> - 554 Transaction failed - - - - - - - - - - - - - -Postel [Page 35] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - 4.2.2. NUMERIC ORDER LIST OF REPLY CODES - - 211 System status, or system help reply - 214 Help message - [Information on how to use the receiver or the meaning of a - particular non-standard command; this reply is useful only - to the human user] - 220 <domain> Service ready - 221 <domain> Service closing transmission channel - 250 Requested mail action okay, completed - 251 User not local; will forward to <forward-path> - - 354 Start mail input; end with <CRLF>.<CRLF> - - 421 <domain> Service not available, - closing transmission channel - [This may be a reply to any command if the service knows it - must shut down] - 450 Requested mail action not taken: mailbox unavailable - [E.g., mailbox busy] - 451 Requested action aborted: local error in processing - 452 Requested action not taken: insufficient system storage - - 500 Syntax error, command unrecognized - [This may include errors such as command line too long] - 501 Syntax error in parameters or arguments - 502 Command not implemented - 503 Bad sequence of commands - 504 Command parameter not implemented - 550 Requested action not taken: mailbox unavailable - [E.g., mailbox not found, no access] - 551 User not local; please try <forward-path> - 552 Requested mail action aborted: exceeded storage allocation - 553 Requested action not taken: mailbox name not allowed - [E.g., mailbox syntax incorrect] - 554 Transaction failed - - - - - - - - - - - - - -[Page 36] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - 4.3. SEQUENCING OF COMMANDS AND REPLIES - - The communication between the sender and receiver is intended to - be an alternating dialogue, controlled by the sender. As such, - the sender issues a command and the receiver responds with a - reply. The sender must wait for this response before sending - further commands. - - One important reply is the connection greeting. Normally, a - receiver will send a 220 "Service ready" reply when the connection - is completed. The sender should wait for this greeting message - before sending any commands. - - Note: all the greeting type replies have the official name of - the server host as the first word following the reply code. - - For example, - - 220 <SP> USC-ISIF.ARPA <SP> Service ready <CRLF> - - The table below lists alternative success and failure replies for - each command. These must be strictly adhered to; a receiver may - substitute text in the replies, but the meaning and action implied - by the code numbers and by the specific command reply sequence - cannot be altered. - - COMMAND-REPLY SEQUENCES - - Each command is listed with its possible replies. The prefixes - used before the possible replies are "P" for preliminary (not - used in SMTP), "I" for intermediate, "S" for success, "F" for - failure, and "E" for error. The 421 reply (service not - available, closing transmission channel) may be given to any - command if the SMTP-receiver knows it must shut down. This - listing forms the basis for the State Diagrams in Section 4.4. - - CONNECTION ESTABLISHMENT - S: 220 - F: 421 - HELO - S: 250 - E: 500, 501, 504, 421 - MAIL - S: 250 - F: 552, 451, 452 - E: 500, 501, 421 - - - -Postel [Page 37] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - RCPT - S: 250, 251 - F: 550, 551, 552, 553, 450, 451, 452 - E: 500, 501, 503, 421 - DATA - I: 354 -> data -> S: 250 - F: 552, 554, 451, 452 - F: 451, 554 - E: 500, 501, 503, 421 - RSET - S: 250 - E: 500, 501, 504, 421 - SEND - S: 250 - F: 552, 451, 452 - E: 500, 501, 502, 421 - SOML - S: 250 - F: 552, 451, 452 - E: 500, 501, 502, 421 - SAML - S: 250 - F: 552, 451, 452 - E: 500, 501, 502, 421 - VRFY - S: 250, 251 - F: 550, 551, 553 - E: 500, 501, 502, 504, 421 - EXPN - S: 250 - F: 550 - E: 500, 501, 502, 504, 421 - HELP - S: 211, 214 - E: 500, 501, 502, 504, 421 - NOOP - S: 250 - E: 500, 421 - QUIT - S: 221 - E: 500 - TURN - S: 250 - F: 502 - E: 500, 503 - - - - -[Page 38] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - 4.4. STATE DIAGRAMS - - Following are state diagrams for a simple-minded SMTP - implementation. Only the first digit of the reply codes is used. - There is one state diagram for each group of SMTP commands. The - command groupings were determined by constructing a model for each - command and then collecting together the commands with - structurally identical models. - - For each command there are three possible outcomes: "success" - (S), "failure" (F), and "error" (E). In the state diagrams below - we use the symbol B for "begin", and the symbol W for "wait for - reply". - - First, the diagram that represents most of the SMTP commands: - - - 1,3 +---+ - ----------->| E | - | +---+ - | - +---+ cmd +---+ 2 +---+ - | B |---------->| W |---------->| S | - +---+ +---+ +---+ - | - | 4,5 +---+ - ----------->| F | - +---+ - - - This diagram models the commands: - - HELO, MAIL, RCPT, RSET, SEND, SOML, SAML, VRFY, EXPN, HELP, - NOOP, QUIT, TURN. - - - - - - - - - - - - - - - -Postel [Page 39] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - A more complex diagram models the DATA command: - - - +---+ DATA +---+ 1,2 +---+ - | B |---------->| W |-------------------->| E | - +---+ +---+ ------------>+---+ - 3| |4,5 | - | | | - -------------- ----- | - | | | +---+ - | ---------- -------->| S | - | | | | +---+ - | | ------------ - | | | | - V 1,3| |2 | - +---+ data +---+ --------------->+---+ - | |---------->| W | | F | - +---+ +---+-------------------->+---+ - 4,5 - - - Note that the "data" here is a series of lines sent from the - sender to the receiver with no response expected until the last - line is sent. - - - - - - - - - - - - - - - - - - - - - - - - - -[Page 40] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - 4.5. DETAILS - - 4.5.1. MINIMUM IMPLEMENTATION - - In order to make SMTP workable, the following minimum - implementation is required for all receivers: - - COMMANDS -- HELO - MAIL - RCPT - DATA - RSET - NOOP - QUIT - - 4.5.2. TRANSPARENCY - - Without some provision for data transparency the character - sequence "<CRLF>.<CRLF>" ends the mail text and cannot be sent - by the user. In general, users are not aware of such - "forbidden" sequences. To allow all user composed text to be - transmitted transparently the following procedures are used. - - 1. Before sending a line of mail text the sender-SMTP checks - the first character of the line. If it is a period, one - additional period is inserted at the beginning of the line. - - 2. When a line of mail text is received by the receiver-SMTP - it checks the line. If the line is composed of a single - period it is the end of mail. If the first character is a - period and there are other characters on the line, the first - character is deleted. - - The mail data may contain any of the 128 ASCII characters. All - characters are to be delivered to the recipient's mailbox - including format effectors and other control characters. If - the transmission channel provides an 8-bit byte (octets) data - stream, the 7-bit ASCII codes are transmitted right justified - in the octets with the high order bits cleared to zero. - - In some systems it may be necessary to transform the data as - it is received and stored. This may be necessary for hosts - that use a different character set than ASCII as their local - character set, or that store data in records rather than - - - - - -Postel [Page 41] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - strings. If such transforms are necessary, they must be - reversible -- especially if such transforms are applied to - mail being relayed. - - 4.5.3. SIZES - - There are several objects that have required minimum maximum - sizes. That is, every implementation must be able to receive - objects of at least these sizes, but must not send objects - larger than these sizes. - - - **************************************************** - * * - * TO THE MAXIMUM EXTENT POSSIBLE, IMPLEMENTATION * - * TECHNIQUES WHICH IMPOSE NO LIMITS ON THE LENGTH * - * OF THESE OBJECTS SHOULD BE USED. * - * * - **************************************************** - - user - - The maximum total length of a user name is 64 characters. - - domain - - The maximum total length of a domain name or number is 64 - characters. - - path - - The maximum total length of a reverse-path or - forward-path is 256 characters (including the punctuation - and element separators). - - command line - - The maximum total length of a command line including the - command word and the <CRLF> is 512 characters. - - reply line - - The maximum total length of a reply line including the - reply code and the <CRLF> is 512 characters. - - - - - -[Page 42] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - text line - - The maximum total length of a text line including the - <CRLF> is 1000 characters (but not counting the leading - dot duplicated for transparency). - - recipients buffer - - The maximum total number of recipients that must be - buffered is 100 recipients. - - - **************************************************** - * * - * TO THE MAXIMUM EXTENT POSSIBLE, IMPLEMENTATION * - * TECHNIQUES WHICH IMPOSE NO LIMITS ON THE LENGTH * - * OF THESE OBJECTS SHOULD BE USED. * - * * - **************************************************** - - Errors due to exceeding these limits may be reported by using - the reply codes, for example: - - 500 Line too long. - - 501 Path too long - - 552 Too many recipients. - - 552 Too much mail data. - - - - - - - - - - - - - - - - - - - -Postel [Page 43] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - -APPENDIX A - - TCP Transport service - - The Transmission Control Protocol [3] is used in the ARPA - Internet, and in any network following the US DoD standards for - internetwork protocols. - - Connection Establishment - - The SMTP transmission channel is a TCP connection established - between the sender process port U and the receiver process port - L. This single full duplex connection is used as the - transmission channel. This protocol is assigned the service - port 25 (31 octal), that is L=25. - - Data Transfer - - The TCP connection supports the transmission of 8-bit bytes. - The SMTP data is 7-bit ASCII characters. Each character is - transmitted as an 8-bit byte with the high-order bit cleared to - zero. - - - - - - - - - - - - - - - - - - - - - - - - - - - -[Page 44] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - -APPENDIX B - - NCP Transport service - - The ARPANET Host-to-Host Protocol [4] (implemented by the Network - Control Program) may be used in the ARPANET. - - Connection Establishment - - The SMTP transmission channel is established via NCP between - the sender process socket U and receiver process socket L. The - Initial Connection Protocol [5] is followed resulting in a pair - of simplex connections. This pair of connections is used as - the transmission channel. This protocol is assigned the - contact socket 25 (31 octal), that is L=25. - - Data Transfer - - The NCP data connections are established in 8-bit byte mode. - The SMTP data is 7-bit ASCII characters. Each character is - transmitted as an 8-bit byte with the high-order bit cleared to - zero. - - - - - - - - - - - - - - - - - - - - - - - - - - - -Postel [Page 45] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - -APPENDIX C - - NITS - - The Network Independent Transport Service [6] may be used. - - Connection Establishment - - The SMTP transmission channel is established via NITS between - the sender process and receiver process. The sender process - executes the CONNECT primitive, and the waiting receiver - process executes the ACCEPT primitive. - - Data Transfer - - The NITS connection supports the transmission of 8-bit bytes. - The SMTP data is 7-bit ASCII characters. Each character is - transmitted as an 8-bit byte with the high-order bit cleared to - zero. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[Page 46] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - -APPENDIX D - - X.25 Transport service - - It may be possible to use the X.25 service [7] as provided by the - Public Data Networks directly, however, it is suggested that a - reliable end-to-end protocol such as TCP be used on top of X.25 - connections. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Postel [Page 47] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - -APPENDIX E - - Theory of Reply Codes - - The three digits of the reply each have a special significance. - The first digit denotes whether the response is good, bad or - incomplete. An unsophisticated sender-SMTP will be able to - determine its next action (proceed as planned, redo, retrench, - etc.) by simply examining this first digit. A sender-SMTP that - wants to know approximately what kind of error occurred (e.g., - mail system error, command syntax error) may examine the second - digit, reserving the third digit for the finest gradation of - information. - - There are five values for the first digit of the reply code: - - 1yz Positive Preliminary reply - - The command has been accepted, but the requested action - is being held in abeyance, pending confirmation of the - information in this reply. The sender-SMTP should send - another command specifying whether to continue or abort - the action. - - [Note: SMTP does not have any commands that allow this - type of reply, and so does not have the continue or - abort commands.] - - 2yz Positive Completion reply - - The requested action has been successfully completed. A - new request may be initiated. - - 3yz Positive Intermediate reply - - The command has been accepted, but the requested action - is being held in abeyance, pending receipt of further - information. The sender-SMTP should send another command - specifying this information. This reply is used in - command sequence groups. - - 4yz Transient Negative Completion reply - - The command was not accepted and the requested action did - not occur. However, the error condition is temporary and - the action may be requested again. The sender should - - - -[Page 48] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - return to the beginning of the command sequence (if any). - It is difficult to assign a meaning to "transient" when - two different sites (receiver- and sender- SMTPs) must - agree on the interpretation. Each reply in this category - might have a different time value, but the sender-SMTP is - encouraged to try again. A rule of thumb to determine if - a reply fits into the 4yz or the 5yz category (see below) - is that replies are 4yz if they can be repeated without - any change in command form or in properties of the sender - or receiver. (E.g., the command is repeated identically - and the receiver does not put up a new implementation.) - - 5yz Permanent Negative Completion reply - - The command was not accepted and the requested action did - not occur. The sender-SMTP is discouraged from repeating - the exact request (in the same sequence). Even some - "permanent" error conditions can be corrected, so the - human user may want to direct the sender-SMTP to - reinitiate the command sequence by direct action at some - point in the future (e.g., after the spelling has been - changed, or the user has altered the account status). - - The second digit encodes responses in specific categories: - - x0z Syntax -- These replies refer to syntax errors, - syntactically correct commands that don't fit any - functional category, and unimplemented or superfluous - commands. - - x1z Information -- These are replies to requests for - information, such as status or help. - - x2z Connections -- These are replies referring to the - transmission channel. - - x3z Unspecified as yet. - - x4z Unspecified as yet. - - x5z Mail system -- These replies indicate the status of - the receiver mail system vis-a-vis the requested - transfer or other mail system action. - - The third digit gives a finer gradation of meaning in each - category specified by the second digit. The list of replies - - - -Postel [Page 49] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - illustrates this. Each reply text is recommended rather than - mandatory, and may even change according to the command with - which it is associated. On the other hand, the reply codes - must strictly follow the specifications in this section. - Receiver implementations should not invent new codes for - slightly different situations from the ones described here, but - rather adapt codes already defined. - - For example, a command such as NOOP whose successful execution - does not offer the sender-SMTP any new information will return - a 250 reply. The response is 502 when the command requests an - unimplemented non-site-specific action. A refinement of that - is the 504 reply for a command that is implemented, but that - requests an unimplemented parameter. - - The reply text may be longer than a single line; in these cases - the complete text must be marked so the sender-SMTP knows when it - can stop reading the reply. This requires a special format to - indicate a multiple line reply. - - The format for multiline replies requires that every line, - except the last, begin with the reply code, followed - immediately by a hyphen, "-" (also known as minus), followed by - text. The last line will begin with the reply code, followed - immediately by <SP>, optionally some text, and <CRLF>. - - For example: - 123-First line - 123-Second line - 123-234 text beginning with numbers - 123 The last line - - In many cases the sender-SMTP then simply needs to search for - the reply code followed by <SP> at the beginning of a line, and - ignore all preceding lines. In a few cases, there is important - data for the sender in the reply "text". The sender will know - these cases from the current context. - - - - - - - - - - - - -[Page 50] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - -APPENDIX F - - Scenarios - - This section presents complete scenarios of several types of SMTP - sessions. - - A Typical SMTP Transaction Scenario - - This SMTP example shows mail sent by Smith at host USC-ISIF, to - Jones, Green, and Brown at host BBN-UNIX. Here we assume that - host USC-ISIF contacts host BBN-UNIX directly. The mail is - accepted for Jones and Brown. Green does not have a mailbox at - host BBN-UNIX. - - ------------------------------------------------------------- - - R: 220 BBN-UNIX.ARPA Simple Mail Transfer Service Ready - S: HELO USC-ISIF.ARPA - R: 250 BBN-UNIX.ARPA - - S: MAIL FROM:<Smith@USC-ISIF.ARPA> - R: 250 OK - - S: RCPT TO:<Jones@BBN-UNIX.ARPA> - R: 250 OK - - S: RCPT TO:<Green@BBN-UNIX.ARPA> - R: 550 No such user here - - S: RCPT TO:<Brown@BBN-UNIX.ARPA> - R: 250 OK - - S: DATA - R: 354 Start mail input; end with <CRLF>.<CRLF> - S: Blah blah blah... - S: ...etc. etc. etc. - S: . - R: 250 OK - - S: QUIT - R: 221 BBN-UNIX.ARPA Service closing transmission channel - - Scenario 1 - - ------------------------------------------------------------- - - - -Postel [Page 51] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - Aborted SMTP Transaction Scenario - - ------------------------------------------------------------- - - R: 220 MIT-Multics.ARPA Simple Mail Transfer Service Ready - S: HELO ISI-VAXA.ARPA - R: 250 MIT-Multics.ARPA - - S: MAIL FROM:<Smith@ISI-VAXA.ARPA> - R: 250 OK - - S: RCPT TO:<Jones@MIT-Multics.ARPA> - R: 250 OK - - S: RCPT TO:<Green@MIT-Multics.ARPA> - R: 550 No such user here - - S: RSET - R: 250 OK - - S: QUIT - R: 221 MIT-Multics.ARPA Service closing transmission channel - - Scenario 2 - - ------------------------------------------------------------- - - - - - - - - - - - - - - - - - - - - - - - -[Page 52] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - Relayed Mail Scenario - - ------------------------------------------------------------- - - Step 1 -- Source Host to Relay Host - - R: 220 USC-ISIE.ARPA Simple Mail Transfer Service Ready - S: HELO MIT-AI.ARPA - R: 250 USC-ISIE.ARPA - - S: MAIL FROM:<JQP@MIT-AI.ARPA> - R: 250 OK - - S: RCPT TO:<@USC-ISIE.ARPA:Jones@BBN-VAX.ARPA> - R: 250 OK - - S: DATA - R: 354 Start mail input; end with <CRLF>.<CRLF> - S: Date: 2 Nov 81 22:33:44 - S: From: John Q. Public <JQP@MIT-AI.ARPA> - S: Subject: The Next Meeting of the Board - S: To: Jones@BBN-Vax.ARPA - S: - S: Bill: - S: The next meeting of the board of directors will be - S: on Tuesday. - S: John. - S: . - R: 250 OK - - S: QUIT - R: 221 USC-ISIE.ARPA Service closing transmission channel - - - - - - - - - - - - - - - - - -Postel [Page 53] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - Step 2 -- Relay Host to Destination Host - - R: 220 BBN-VAX.ARPA Simple Mail Transfer Service Ready - S: HELO USC-ISIE.ARPA - R: 250 BBN-VAX.ARPA - - S: MAIL FROM:<@USC-ISIE.ARPA:JQP@MIT-AI.ARPA> - R: 250 OK - - S: RCPT TO:<Jones@BBN-VAX.ARPA> - R: 250 OK - - S: DATA - R: 354 Start mail input; end with <CRLF>.<CRLF> - S: Received: from MIT-AI.ARPA by USC-ISIE.ARPA ; - 2 Nov 81 22:40:10 UT - S: Date: 2 Nov 81 22:33:44 - S: From: John Q. Public <JQP@MIT-AI.ARPA> - S: Subject: The Next Meeting of the Board - S: To: Jones@BBN-Vax.ARPA - S: - S: Bill: - S: The next meeting of the board of directors will be - S: on Tuesday. - S: John. - S: . - R: 250 OK - - S: QUIT - R: 221 USC-ISIE.ARPA Service closing transmission channel - - Scenario 3 - - ------------------------------------------------------------- - - - - - - - - - - - - - - - -[Page 54] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - Verifying and Sending Scenario - - ------------------------------------------------------------- - - R: 220 SU-SCORE.ARPA Simple Mail Transfer Service Ready - S: HELO MIT-MC.ARPA - R: 250 SU-SCORE.ARPA - - S: VRFY Crispin - R: 250 Mark Crispin <Admin.MRC@SU-SCORE.ARPA> - - S: SEND FROM:<EAK@MIT-MC.ARPA> - R: 250 OK - - S: RCPT TO:<Admin.MRC@SU-SCORE.ARPA> - R: 250 OK - - S: DATA - R: 354 Start mail input; end with <CRLF>.<CRLF> - S: Blah blah blah... - S: ...etc. etc. etc. - S: . - R: 250 OK - - S: QUIT - R: 221 SU-SCORE.ARPA Service closing transmission channel - - Scenario 4 - - ------------------------------------------------------------- - - - - - - - - - - - - - - - - - - - -Postel [Page 55] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - Sending and Mailing Scenarios - - First the user's name is verified, then an attempt is made to - send to the user's terminal. When that fails, the messages is - mailed to the user's mailbox. - - ------------------------------------------------------------- - - R: 220 SU-SCORE.ARPA Simple Mail Transfer Service Ready - S: HELO MIT-MC.ARPA - R: 250 SU-SCORE.ARPA - - S: VRFY Crispin - R: 250 Mark Crispin <Admin.MRC@SU-SCORE.ARPA> - - S: SEND FROM:<EAK@MIT-MC.ARPA> - R: 250 OK - - S: RCPT TO:<Admin.MRC@SU-SCORE.ARPA> - R: 450 User not active now - - S: RSET - R: 250 OK - - S: MAIL FROM:<EAK@MIT-MC.ARPA> - R: 250 OK - - S: RCPT TO:<Admin.MRC@SU-SCORE.ARPA> - R: 250 OK - - S: DATA - R: 354 Start mail input; end with <CRLF>.<CRLF> - S: Blah blah blah... - S: ...etc. etc. etc. - S: . - R: 250 OK - - S: QUIT - R: 221 SU-SCORE.ARPA Service closing transmission channel - - Scenario 5 - - ------------------------------------------------------------- - - - - - - -[Page 56] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - Doing the preceding scenario more efficiently. - - ------------------------------------------------------------- - - R: 220 SU-SCORE.ARPA Simple Mail Transfer Service Ready - S: HELO MIT-MC.ARPA - R: 250 SU-SCORE.ARPA - - S: VRFY Crispin - R: 250 Mark Crispin <Admin.MRC@SU-SCORE.ARPA> - - S: SOML FROM:<EAK@MIT-MC.ARPA> - R: 250 OK - - S: RCPT TO:<Admin.MRC@SU-SCORE.ARPA> - R: 250 User not active now, so will do mail. - - S: DATA - R: 354 Start mail input; end with <CRLF>.<CRLF> - S: Blah blah blah... - S: ...etc. etc. etc. - S: . - R: 250 OK - - S: QUIT - R: 221 SU-SCORE.ARPA Service closing transmission channel - - Scenario 6 - - ------------------------------------------------------------- - - - - - - - - - - - - - - - - - - - -Postel [Page 57] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - Mailing List Scenario - - First each of two mailing lists are expanded in separate sessions - with different hosts. Then the message is sent to everyone that - appeared on either list (but no duplicates) via a relay host. - - ------------------------------------------------------------- - - Step 1 -- Expanding the First List - - R: 220 MIT-AI.ARPA Simple Mail Transfer Service Ready - S: HELO SU-SCORE.ARPA - R: 250 MIT-AI.ARPA - - S: EXPN Example-People - R: 250-<ABC@MIT-MC.ARPA> - R: 250-Fred Fonebone <Fonebone@USC-ISIQ.ARPA> - R: 250-Xenon Y. Zither <XYZ@MIT-AI.ARPA> - R: 250-Quincy Smith <@USC-ISIF.ARPA:Q-Smith@ISI-VAXA.ARPA> - R: 250-<joe@foo-unix.ARPA> - R: 250 <xyz@bar-unix.ARPA> - - S: QUIT - R: 221 MIT-AI.ARPA Service closing transmission channel - - - - - - - - - - - - - - - - - - - - - - - - - -[Page 58] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - Step 2 -- Expanding the Second List - - R: 220 MIT-MC.ARPA Simple Mail Transfer Service Ready - S: HELO SU-SCORE.ARPA - R: 250 MIT-MC.ARPA - - S: EXPN Interested-Parties - R: 250-Al Calico <ABC@MIT-MC.ARPA> - R: 250-<XYZ@MIT-AI.ARPA> - R: 250-Quincy Smith <@USC-ISIF.ARPA:Q-Smith@ISI-VAXA.ARPA> - R: 250-<fred@BBN-UNIX.ARPA> - R: 250 <xyz@bar-unix.ARPA> - - S: QUIT - R: 221 MIT-MC.ARPA Service closing transmission channel - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Postel [Page 59] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - Step 3 -- Mailing to All via a Relay Host - - R: 220 USC-ISIE.ARPA Simple Mail Transfer Service Ready - S: HELO SU-SCORE.ARPA - R: 250 USC-ISIE.ARPA - - S: MAIL FROM:<Account.Person@SU-SCORE.ARPA> - R: 250 OK - S: RCPT TO:<@USC-ISIE.ARPA:ABC@MIT-MC.ARPA> - R: 250 OK - S: RCPT TO:<@USC-ISIE.ARPA:Fonebone@USC-ISIQA.ARPA> - R: 250 OK - S: RCPT TO:<@USC-ISIE.ARPA:XYZ@MIT-AI.ARPA> - R: 250 OK - S: RCPT - TO:<@USC-ISIE.ARPA,@USC-ISIF.ARPA:Q-Smith@ISI-VAXA.ARPA> - R: 250 OK - S: RCPT TO:<@USC-ISIE.ARPA:joe@FOO-UNIX.ARPA> - R: 250 OK - S: RCPT TO:<@USC-ISIE.ARPA:xyz@BAR-UNIX.ARPA> - R: 250 OK - S: RCPT TO:<@USC-ISIE.ARPA:fred@BBN-UNIX.ARPA> - R: 250 OK - - S: DATA - R: 354 Start mail input; end with <CRLF>.<CRLF> - S: Blah blah blah... - S: ...etc. etc. etc. - S: . - R: 250 OK - - S: QUIT - R: 221 USC-ISIE.ARPA Service closing transmission channel - - Scenario 7 - - ------------------------------------------------------------- - - - - - - - - - - - - -[Page 60] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - Forwarding Scenarios - - ------------------------------------------------------------- - - R: 220 USC-ISIF.ARPA Simple Mail Transfer Service Ready - S: HELO LBL-UNIX.ARPA - R: 250 USC-ISIF.ARPA - - S: MAIL FROM:<mo@LBL-UNIX.ARPA> - R: 250 OK - - S: RCPT TO:<fred@USC-ISIF.ARPA> - R: 251 User not local; will forward to <Jones@USC-ISI.ARPA> - - S: DATA - R: 354 Start mail input; end with <CRLF>.<CRLF> - S: Blah blah blah... - S: ...etc. etc. etc. - S: . - R: 250 OK - - S: QUIT - R: 221 USC-ISIF.ARPA Service closing transmission channel - - Scenario 8 - - ------------------------------------------------------------- - - - - - - - - - - - - - - - - - - - - - - -Postel [Page 61] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - ------------------------------------------------------------- - - Step 1 -- Trying the Mailbox at the First Host - - R: 220 USC-ISIF.ARPA Simple Mail Transfer Service Ready - S: HELO LBL-UNIX.ARPA - R: 250 USC-ISIF.ARPA - - S: MAIL FROM:<mo@LBL-UNIX.ARPA> - R: 250 OK - - S: RCPT TO:<fred@USC-ISIF.ARPA> - R: 251 User not local; will forward to <Jones@USC-ISI.ARPA> - - S: RSET - R: 250 OK - - S: QUIT - R: 221 USC-ISIF.ARPA Service closing transmission channel - - Step 2 -- Delivering the Mail at the Second Host - - R: 220 USC-ISI.ARPA Simple Mail Transfer Service Ready - S: HELO LBL-UNIX.ARPA - R: 250 USC-ISI.ARPA - - S: MAIL FROM:<mo@LBL-UNIX.ARPA> - R: 250 OK - - S: RCPT TO:<Jones@USC-ISI.ARPA> - R: OK - - S: DATA - R: 354 Start mail input; end with <CRLF>.<CRLF> - S: Blah blah blah... - S: ...etc. etc. etc. - S: . - R: 250 OK - - S: QUIT - R: 221 USC-ISI.ARPA Service closing transmission channel - - Scenario 9 - - ------------------------------------------------------------- - - - - -[Page 62] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - Too Many Recipients Scenario - - ------------------------------------------------------------- - - R: 220 BERKELEY.ARPA Simple Mail Transfer Service Ready - S: HELO USC-ISIF.ARPA - R: 250 BERKELEY.ARPA - - S: MAIL FROM:<Postel@USC-ISIF.ARPA> - R: 250 OK - - S: RCPT TO:<fabry@BERKELEY.ARPA> - R: 250 OK - - S: RCPT TO:<eric@BERKELEY.ARPA> - R: 552 Recipient storage full, try again in another transaction - - S: DATA - R: 354 Start mail input; end with <CRLF>.<CRLF> - S: Blah blah blah... - S: ...etc. etc. etc. - S: . - R: 250 OK - - S: MAIL FROM:<Postel@USC-ISIF.ARPA> - R: 250 OK - - S: RCPT TO:<eric@BERKELEY.ARPA> - R: 250 OK - - S: DATA - R: 354 Start mail input; end with <CRLF>.<CRLF> - S: Blah blah blah... - S: ...etc. etc. etc. - S: . - R: 250 OK - - S: QUIT - R: 221 BERKELEY.ARPA Service closing transmission channel - - Scenario 10 - - ------------------------------------------------------------- - - Note that a real implementation must handle many recipients as - specified in Section 4.5.3. - - - -Postel [Page 63] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - -GLOSSARY - - ASCII - - American Standard Code for Information Interchange [1]. - - command - - A request for a mail service action sent by the sender-SMTP to the - receiver-SMTP. - - domain - - The hierarchially structured global character string address of a - host computer in the mail system. - - end of mail data indication - - A special sequence of characters that indicates the end of the - mail data. In particular, the five characters carriage return, - line feed, period, carriage return, line feed, in that order. - - host - - A computer in the internetwork environment on which mailboxes or - SMTP processes reside. - - line - - A a sequence of ASCII characters ending with a <CRLF>. - - mail data - - A sequence of ASCII characters of arbitrary length, which conforms - to the standard set in the Standard for the Format of ARPA - Internet Text Messages (RFC 822 [2]). - - mailbox - - A character string (address) which identifies a user to whom mail - is to be sent. Mailbox normally consists of the host and user - specifications. The standard mailbox naming convention is defined - to be "user@domain". Additionally, the "container" in which mail - is stored. - - - - - -[Page 64] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - - receiver-SMTP process - - A process which transfers mail in cooperation with a sender-SMTP - process. It waits for a connection to be established via the - transport service. It receives SMTP commands from the - sender-SMTP, sends replies, and performs the specified operations. - - reply - - A reply is an acknowledgment (positive or negative) sent from - receiver to sender via the transmission channel in response to a - command. The general form of a reply is a completion code - (including error codes) followed by a text string. The codes are - for use by programs and the text is usually intended for human - users. - - sender-SMTP process - - A process which transfers mail in cooperation with a receiver-SMTP - process. A local language may be used in the user interface - command/reply dialogue. The sender-SMTP initiates the transport - service connection. It initiates SMTP commands, receives replies, - and governs the transfer of mail. - - session - - The set of exchanges that occur while the transmission channel is - open. - - transaction - - The set of exchanges required for one message to be transmitted - for one or more recipients. - - transmission channel - - A full-duplex communication path between a sender-SMTP and a - receiver-SMTP for the exchange of commands, replies, and mail - text. - - transport service - - Any reliable stream-oriented data communication services. For - example, NCP, TCP, NITS. - - - - - -Postel [Page 65] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - user - - A human being (or a process on behalf of a human being) wishing to - obtain mail transfer service. In addition, a recipient of - computer mail. - - word - - A sequence of printing characters. - - <CRLF> - - The characters carriage return and line feed (in that order). - - <SP> - - The space character. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[Page 66] Postel - - - -RFC 821 August 1982 - Simple Mail Transfer Protocol - - - -REFERENCES - - [1] ASCII - - ASCII, "USA Code for Information Interchange", United States of - America Standards Institute, X3.4, 1968. Also in: Feinler, E. - and J. Postel, eds., "ARPANET Protocol Handbook", NIC 7104, for - the Defense Communications Agency by SRI International, Menlo - Park, California, Revised January 1978. - - [2] RFC 822 - - Crocker, D., "Standard for the Format of ARPA Internet Text - Messages," RFC 822, Department of Electrical Engineering, - University of Delaware, August 1982. - - [3] TCP - - Postel, J., ed., "Transmission Control Protocol - DARPA Internet - Program Protocol Specification", RFC 793, USC/Information Sciences - Institute, NTIS AD Number A111091, September 1981. Also in: - Feinler, E. and J. Postel, eds., "Internet Protocol Transition - Workbook", SRI International, Menlo Park, California, March 1982. - - [4] NCP - - McKenzie,A., "Host/Host Protocol for the ARPA Network", NIC 8246, - January 1972. Also in: Feinler, E. and J. Postel, eds., "ARPANET - Protocol Handbook", NIC 7104, for the Defense Communications - Agency by SRI International, Menlo Park, California, Revised - January 1978. - - [5] Initial Connection Protocol - - Postel, J., "Official Initial Connection Protocol", NIC 7101, - 11 June 1971. Also in: Feinler, E. and J. Postel, eds., "ARPANET - Protocol Handbook", NIC 7104, for the Defense Communications - Agency by SRI International, Menlo Park, California, Revised - January 1978. - - [6] NITS - - PSS/SG3, "A Network Independent Transport Service", Study Group 3, - The Post Office PSS Users Group, February 1980. Available from - the DCPU, National Physical Laboratory, Teddington, UK. - - - - -Postel [Page 67] - - - -August 1982 RFC 821 -Simple Mail Transfer Protocol - - - - [7] X.25 - - CCITT, "Recommendation X.25 - Interface Between Data Terminal - Equipment (DTE) and Data Circuit-terminating Equipment (DCE) for - Terminals Operating in the Packet Mode on Public Data Networks," - CCITT Orange Book, Vol. VIII.2, International Telephone and - Telegraph Consultative Committee, Geneva, 1976. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[Page 68] Postel - diff --git a/usr.sbin/sendmail/doc/rfc822.lpr b/usr.sbin/sendmail/doc/rfc822.lpr deleted file mode 100644 index 35b09a3cb7..0000000000 --- a/usr.sbin/sendmail/doc/rfc822.lpr +++ /dev/null @@ -1,2901 +0,0 @@ - - - - - - - RFC # 822 - - Obsoletes: RFC #733 (NIC #41952) - - - - - - - - - - - - - STANDARD FOR THE FORMAT OF - - ARPA INTERNET TEXT MESSAGES - - - - - - - August 13, 1982 - - - - - - - Revised by - - David H. Crocker - - - Dept. of Electrical Engineering - University of Delaware, Newark, DE 19711 - Network: DCrocker @ UDel-Relay - - - - - - - - - - - - - - - - Standard for ARPA Internet Text Messages - - - TABLE OF CONTENTS - - - PREFACE .................................................... ii - - 1. INTRODUCTION ........................................... 1 - - 1.1. Scope ............................................ 1 - 1.2. Communication Framework .......................... 2 - - 2. NOTATIONAL CONVENTIONS ................................. 3 - - 3. LEXICAL ANALYSIS OF MESSAGES ........................... 5 - - 3.1. General Description .............................. 5 - 3.2. Header Field Definitions ......................... 9 - 3.3. Lexical Tokens ................................... 10 - 3.4. Clarifications ................................... 11 - - 4. MESSAGE SPECIFICATION .................................. 17 - - 4.1. Syntax ........................................... 17 - 4.2. Forwarding ....................................... 19 - 4.3. Trace Fields ..................................... 20 - 4.4. Originator Fields ................................ 21 - 4.5. Receiver Fields .................................. 23 - 4.6. Reference Fields ................................. 23 - 4.7. Other Fields ..................................... 24 - - 5. DATE AND TIME SPECIFICATION ............................ 26 - - 5.1. Syntax ........................................... 26 - 5.2. Semantics ........................................ 26 - - 6. ADDRESS SPECIFICATION .................................. 27 - - 6.1. Syntax ........................................... 27 - 6.2. Semantics ........................................ 27 - 6.3. Reserved Address ................................. 33 - - 7. BIBLIOGRAPHY ........................................... 34 - - - APPENDIX - - A. EXAMPLES ............................................... 36 - B. SIMPLE FIELD PARSING ................................... 40 - C. DIFFERENCES FROM RFC #733 .............................. 41 - D. ALPHABETICAL LISTING OF SYNTAX RULES ................... 44 - - - August 13, 1982 - i - RFC #822 - - - - - Standard for ARPA Internet Text Messages - - - PREFACE - - - By 1977, the Arpanet employed several informal standards for - the text messages (mail) sent among its host computers. It was - felt necessary to codify these practices and provide for those - features that seemed imminent. The result of that effort was - Request for Comments (RFC) #733, "Standard for the Format of ARPA - Network Text Message", by Crocker, Vittal, Pogran, and Henderson. - The specification attempted to avoid major changes in existing - software, while permitting several new features. - - This document revises the specifications in RFC #733, in - order to serve the needs of the larger and more complex ARPA - Internet. Some of RFC #733's features failed to gain adequate - acceptance. In order to simplify the standard and the software - that follows it, these features have been removed. A different - addressing scheme is used, to handle the case of inter-network - mail; and the concept of re-transmission has been introduced. - - This specification is intended for use in the ARPA Internet. - However, an attempt has been made to free it of any dependence on - that environment, so that it can be applied to other network text - message systems. - - The specification of RFC #733 took place over the course of - one year, using the ARPANET mail environment, itself, to provide - an on-going forum for discussing the capabilities to be included. - More than twenty individuals, from across the country, partici- - pated in the original discussion. The development of this - revised specification has, similarly, utilized network mail-based - group discussion. Both specification efforts greatly benefited - from the comments and ideas of the participants. - - The syntax of the standard, in RFC #733, was originally - specified in the Backus-Naur Form (BNF) meta-language. Ken L. - Harrenstien, of SRI International, was responsible for re-coding - the BNF into an augmented BNF that makes the representation - smaller and easier to understand. - - - - - - - - - - - - - August 13, 1982 - ii - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - 1. INTRODUCTION - - 1.1. SCOPE - - This standard specifies a syntax for text messages that are - sent among computer users, within the framework of "electronic - mail". The standard supersedes the one specified in ARPANET - Request for Comments #733, "Standard for the Format of ARPA Net- - work Text Messages". - - In this context, messages are viewed as having an envelope - and contents. The envelope contains whatever information is - needed to accomplish transmission and delivery. The contents - compose the object to be delivered to the recipient. This stan- - dard applies only to the format and some of the semantics of mes- - sage contents. It contains no specification of the information - in the envelope. - - However, some message systems may use information from the - contents to create the envelope. It is intended that this stan- - dard facilitate the acquisition of such information by programs. - - Some message systems may store messages in formats that - differ from the one specified in this standard. This specifica- - tion is intended strictly as a definition of what message content - format is to be passed BETWEEN hosts. - - Note: This standard is NOT intended to dictate the internal for- - mats used by sites, the specific message system features - that they are expected to support, or any of the charac- - teristics of user interface programs that create or read - messages. - - A distinction should be made between what the specification - REQUIRES and what it ALLOWS. Messages can be made complex and - rich with formally-structured components of information or can be - kept small and simple, with a minimum of such information. Also, - the standard simplifies the interpretation of differing visual - formats in messages; only the visual aspect of a message is - affected and not the interpretation of information within it. - Implementors may choose to retain such visual distinctions. - - The formal definition is divided into four levels. The bot- - tom level describes the meta-notation used in this document. The - second level describes basic lexical analyzers that feed tokens - to higher-level parsers. Next is an overall specification for - messages; it permits distinguishing individual fields. Finally, - there is definition of the contents of several structured fields. - - - - August 13, 1982 - 1 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - 1.2. COMMUNICATION FRAMEWORK - - Messages consist of lines of text. No special provisions - are made for encoding drawings, facsimile, speech, or structured - text. No significant consideration has been given to questions - of data compression or to transmission and storage efficiency, - and the standard tends to be free with the number of bits con- - sumed. For example, field names are specified as free text, - rather than special terse codes. - - A general "memo" framework is used. That is, a message con- - sists of some information in a rigid format, followed by the main - part of the message, with a format that is not specified in this - document. The syntax of several fields of the rigidly-formated - ("headers") section is defined in this specification; some of - these fields must be included in all messages. - - The syntax that distinguishes between header fields is - specified separately from the internal syntax for particular - fields. This separation is intended to allow simple parsers to - operate on the general structure of messages, without concern for - the detailed structure of individual header fields. Appendix B - is provided to facilitate construction of these parsers. - - In addition to the fields specified in this document, it is - expected that other fields will gain common use. As necessary, - the specifications for these "extension-fields" will be published - through the same mechanism used to publish this document. Users - may also wish to extend the set of fields that they use - privately. Such "user-defined fields" are permitted. - - The framework severely constrains document tone and appear- - ance and is primarily useful for most intra-organization communi- - cations and well-structured inter-organization communication. - It also can be used for some types of inter-process communica- - tion, such as simple file transfer and remote job entry. A more - robust framework might allow for multi-font, multi-color, multi- - dimension encoding of information. A less robust one, as is - present in most single-machine message systems, would more - severely constrain the ability to add fields and the decision to - include specific fields. In contrast with paper-based communica- - tion, it is interesting to note that the RECEIVER of a message - can exercise an extraordinary amount of control over the - message's appearance. The amount of actual control available to - message receivers is contingent upon the capabilities of their - individual message systems. - - - - - - August 13, 1982 - 2 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - 2. NOTATIONAL CONVENTIONS - - This specification uses an augmented Backus-Naur Form (BNF) - notation. The differences from standard BNF involve naming rules - and indicating repetition and "local" alternatives. - - 2.1. RULE NAMING - - Angle brackets ("<", ">") are not used, in general. The - name of a rule is simply the name itself, rather than "<name>". - Quotation-marks enclose literal text (which may be upper and/or - lower case). Certain basic rules are in uppercase, such as - SPACE, TAB, CRLF, DIGIT, ALPHA, etc. Angle brackets are used in - rule definitions, and in the rest of this document, whenever - their presence will facilitate discerning the use of rule names. - - 2.2. RULE1 / RULE2: ALTERNATIVES - - Elements separated by slash ("/") are alternatives. There- - fore "foo / bar" will accept foo or bar. - - 2.3. (RULE1 RULE2): LOCAL ALTERNATIVES - - Elements enclosed in parentheses are treated as a single - element. Thus, "(elem (foo / bar) elem)" allows the token - sequences "elem foo elem" and "elem bar elem". - - 2.4. *RULE: REPETITION - - The character "*" preceding an element indicates repetition. - The full form is: - - <l>*<m>element - - indicating at least <l> and at most <m> occurrences of element. - Default values are 0 and infinity so that "*(element)" allows any - number, including zero; "1*element" requires at least one; and - "1*2element" allows one or two. - - 2.5. [RULE]: OPTIONAL - - Square brackets enclose optional elements; "[foo bar]" is - equivalent to "*1(foo bar)". - - 2.6. NRULE: SPECIFIC REPETITION - - "<n>(element)" is equivalent to "<n>*<n>(element)"; that is, - exactly <n> occurrences of (element). Thus 2DIGIT is a 2-digit - number, and 3ALPHA is a string of three alphabetic characters. - - - August 13, 1982 - 3 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - 2.7. #RULE: LISTS - - A construct "#" is defined, similar to "*", as follows: - - <l>#<m>element - - indicating at least <l> and at most <m> elements, each separated - by one or more commas (","). This makes the usual form of lists - very easy; a rule such as '(element *("," element))' can be shown - as "1#element". Wherever this construct is used, null elements - are allowed, but do not contribute to the count of elements - present. That is, "(element),,(element)" is permitted, but - counts as only two elements. Therefore, where at least one ele- - ment is required, at least one non-null element must be present. - Default values are 0 and infinity so that "#(element)" allows any - number, including zero; "1#element" requires at least one; and - "1#2element" allows one or two. - - 2.8. ; COMMENTS - - A semi-colon, set off some distance to the right of rule - text, starts a comment that continues to the end of line. This - is a simple way of including useful notes in parallel with the - specifications. - - - - - - - - - - - - - - - - - - - - - - - - - - - - August 13, 1982 - 4 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - 3. LEXICAL ANALYSIS OF MESSAGES - - 3.1. GENERAL DESCRIPTION - - A message consists of header fields and, optionally, a body. - The body is simply a sequence of lines containing ASCII charac- - ters. It is separated from the headers by a null line (i.e., a - line with nothing preceding the CRLF). - - 3.1.1. LONG HEADER FIELDS - - Each header field can be viewed as a single, logical line of - ASCII characters, comprising a field-name and a field-body. - For convenience, the field-body portion of this conceptual - entity can be split into a multiple-line representation; this - is called "folding". The general rule is that wherever there - may be linear-white-space (NOT simply LWSP-chars), a CRLF - immediately followed by AT LEAST one LWSP-char may instead be - inserted. Thus, the single line - - To: "Joe & J. Harvey" <ddd @Org>, JJV @ BBN - - can be represented as: - - To: "Joe & J. Harvey" <ddd @ Org>, - JJV@BBN - - and - - To: "Joe & J. Harvey" - <ddd@ Org>, JJV - @BBN - - and - - To: "Joe & - J. Harvey" <ddd @ Org>, JJV @ BBN - - The process of moving from this folded multiple-line - representation of a header field to its single line represen- - tation is called "unfolding". Unfolding is accomplished by - regarding CRLF immediately followed by a LWSP-char as - equivalent to the LWSP-char. - - Note: While the standard permits folding wherever linear- - white-space is permitted, it is recommended that struc- - tured fields, such as those containing addresses, limit - folding to higher-level syntactic breaks. For address - fields, it is recommended that such folding occur - - - August 13, 1982 - 5 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - between addresses, after the separating comma. - - 3.1.2. STRUCTURE OF HEADER FIELDS - - Once a field has been unfolded, it may be viewed as being com- - posed of a field-name followed by a colon (":"), followed by a - field-body, and terminated by a carriage-return/line-feed. - The field-name must be composed of printable ASCII characters - (i.e., characters that have values between 33. and 126., - decimal, except colon). The field-body may be composed of any - ASCII characters, except CR or LF. (While CR and/or LF may be - present in the actual text, they are removed by the action of - unfolding the field.) - - Certain field-bodies of headers may be interpreted according - to an internal syntax that some systems may wish to parse. - These fields are called "structured fields". Examples - include fields containing dates and addresses. Other fields, - such as "Subject" and "Comments", are regarded simply as - strings of text. - - Note: Any field which has a field-body that is defined as - other than simply <text> is to be treated as a struc- - tured field. - - Field-names, unstructured field bodies and structured - field bodies each are scanned by their own, independent - "lexical" analyzers. - - 3.1.3. UNSTRUCTURED FIELD BODIES - - For some fields, such as "Subject" and "Comments", no struc- - turing is assumed, and they are treated simply as <text>s, as - in the message body. Rules of folding apply to these fields, - so that such field bodies which occupy several lines must - therefore have the second and successive lines indented by at - least one LWSP-char. - - 3.1.4. STRUCTURED FIELD BODIES - - To aid in the creation and reading of structured fields, the - free insertion of linear-white-space (which permits folding - by inclusion of CRLFs) is allowed between lexical tokens. - Rather than obscuring the syntax specifications for these - structured fields with explicit syntax for this linear-white- - space, the existence of another "lexical" analyzer is assumed. - This analyzer does not apply for unstructured field bodies - that are simply strings of text, as described above. The - analyzer provides an interpretation of the unfolded text - - - August 13, 1982 - 6 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - composing the body of the field as a sequence of lexical sym- - bols. - - These symbols are: - - - individual special characters - - quoted-strings - - domain-literals - - comments - - atoms - - The first four of these symbols are self-delimiting. Atoms - are not; they are delimited by the self-delimiting symbols and - by linear-white-space. For the purposes of regenerating - sequences of atoms and quoted-strings, exactly one SPACE is - assumed to exist, and should be used, between them. (Also, in - the "Clarifications" section on "White Space", below, note the - rules about treatment of multiple contiguous LWSP-chars.) - - So, for example, the folded body of an address field - - ":sysmail"@ Some-Group. Some-Org, - Muhammed.(I am the greatest) Ali @(the)Vegas.WBA - - - - - - - - - - - - - - - - - - - - - - - - - - - - - August 13, 1982 - 7 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - is analyzed into the following lexical symbols and types: - - :sysmail quoted string - @ special - Some-Group atom - . special - Some-Org atom - , special - Muhammed atom - . special - (I am the greatest) comment - Ali atom - @ atom - (the) comment - Vegas atom - . special - WBA atom - - The canonical representations for the data in these addresses - are the following strings: - - ":sysmail"@Some-Group.Some-Org - - and - - Muhammed.Ali@Vegas.WBA - - Note: For purposes of display, and when passing such struc- - tured information to other systems, such as mail proto- - col services, there must be NO linear-white-space - between <word>s that are separated by period (".") or - at-sign ("@") and exactly one SPACE between all other - <word>s. Also, headers should be in a folded form. - - - - - - - - - - - - - - - - - - - August 13, 1982 - 8 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - 3.2. HEADER FIELD DEFINITIONS - - These rules show a field meta-syntax, without regard for the - particular type or internal syntax. Their purpose is to permit - detection of fields; also, they present to higher-level parsers - an image of each field as fitting on one line. - - field = field-name ":" [ field-body ] CRLF - - field-name = 1*<any CHAR, excluding CTLs, SPACE, and ":"> - - field-body = field-body-contents - [CRLF LWSP-char field-body] - - field-body-contents = - <the ASCII characters making up the field-body, as - defined in the following sections, and consisting - of combinations of atom, quoted-string, and - specials tokens, or else consisting of texts> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - August 13, 1982 - 9 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - 3.3. LEXICAL TOKENS - - The following rules are used to define an underlying lexical - analyzer, which feeds tokens to higher level parsers. See the - ANSI references, in the Bibliography. - - ; ( Octal, Decimal.) - CHAR = <any ASCII character> ; ( 0-177, 0.-127.) - ALPHA = <any ASCII alphabetic character> - ; (101-132, 65.- 90.) - ; (141-172, 97.-122.) - DIGIT = <any ASCII decimal digit> ; ( 60- 71, 48.- 57.) - CTL = <any ASCII control ; ( 0- 37, 0.- 31.) - character and DEL> ; ( 177, 127.) - CR = <ASCII CR, carriage return> ; ( 15, 13.) - LF = <ASCII LF, linefeed> ; ( 12, 10.) - SPACE = <ASCII SP, space> ; ( 40, 32.) - HTAB = <ASCII HT, horizontal-tab> ; ( 11, 9.) - <"> = <ASCII quote mark> ; ( 42, 34.) - CRLF = CR LF - - LWSP-char = SPACE / HTAB ; semantics = SPACE - - linear-white-space = 1*([CRLF] LWSP-char) ; semantics = SPACE - ; CRLF => folding - - specials = "(" / ")" / "<" / ">" / "@" ; Must be in quoted- - / "," / ";" / ":" / "\" / <"> ; string, to use - / "." / "[" / "]" ; within a word. - - delimiters = specials / linear-white-space / comment - - text = <any CHAR, including bare ; => atoms, specials, - CR & bare LF, but NOT ; comments and - including CRLF> ; quoted-strings are - ; NOT recognized. - - atom = 1*<any CHAR except specials, SPACE and CTLs> - - quoted-string = <"> *(qtext/quoted-pair) <">; Regular qtext or - ; quoted chars. - - qtext = <any CHAR excepting <">, ; => may be folded - "\" & CR, and including - linear-white-space> - - domain-literal = "[" *(dtext / quoted-pair) "]" - - - - - August 13, 1982 - 10 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - dtext = <any CHAR excluding "[", ; => may be folded - "]", "\" & CR, & including - linear-white-space> - - comment = "(" *(ctext / quoted-pair / comment) ")" - - ctext = <any CHAR excluding "(", ; => may be folded - ")", "\" & CR, & including - linear-white-space> - - quoted-pair = "\" CHAR ; may quote any char - - phrase = 1*word ; Sequence of words - - word = atom / quoted-string - - - 3.4. CLARIFICATIONS - - 3.4.1. QUOTING - - Some characters are reserved for special interpretation, such - as delimiting lexical tokens. To permit use of these charac- - ters as uninterpreted data, a quoting mechanism is provided. - To quote a character, precede it with a backslash ("\"). - - This mechanism is not fully general. Characters may be quoted - only within a subset of the lexical constructs. In particu- - lar, quoting is limited to use within: - - - quoted-string - - domain-literal - - comment - - Within these constructs, quoting is REQUIRED for CR and "\" - and for the character(s) that delimit the token (e.g., "(" and - ")" for a comment). However, quoting is PERMITTED for any - character. - - Note: In particular, quoting is NOT permitted within atoms. - For example when the local-part of an addr-spec must - contain a special character, a quoted string must be - used. Therefore, a specification such as: - - Full\ Name@Domain - - is not legal and must be specified as: - - "Full Name"@Domain - - - August 13, 1982 - 11 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - 3.4.2. WHITE SPACE - - Note: In structured field bodies, multiple linear space ASCII - characters (namely HTABs and SPACEs) are treated as - single spaces and may freely surround any symbol. In - all header fields, the only place in which at least one - LWSP-char is REQUIRED is at the beginning of continua- - tion lines in a folded field. - - When passing text to processes that do not interpret text - according to this standard (e.g., mail protocol servers), then - NO linear-white-space characters should occur between a period - (".") or at-sign ("@") and a <word>. Exactly ONE SPACE should - be used in place of arbitrary linear-white-space and comment - sequences. - - Note: Within systems conforming to this standard, wherever a - member of the list of delimiters is allowed, LWSP-chars - may also occur before and/or after it. - - Writers of mail-sending (i.e., header-generating) programs - should realize that there is no network-wide definition of the - effect of ASCII HT (horizontal-tab) characters on the appear- - ance of text at another network host; therefore, the use of - tabs in message headers, though permitted, is discouraged. - - 3.4.3. COMMENTS - - A comment is a set of ASCII characters, which is enclosed in - matching parentheses and which is not within a quoted-string - The comment construct permits message originators to add text - which will be useful for human readers, but which will be - ignored by the formal semantics. Comments should be retained - while the message is subject to interpretation according to - this standard. However, comments must NOT be included in - other cases, such as during protocol exchanges with mail - servers. - - Comments nest, so that if an unquoted left parenthesis occurs - in a comment string, there must also be a matching right - parenthesis. When a comment acts as the delimiter between a - sequence of two lexical symbols, such as two atoms, it is lex- - ically equivalent with a single SPACE, for the purposes of - regenerating the sequence, such as when passing the sequence - onto a mail protocol server. Comments are detected as such - only within field-bodies of structured fields. - - If a comment is to be "folded" onto multiple lines, then the - syntax for folding must be adhered to. (See the "Lexical - - - August 13, 1982 - 12 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - Analysis of Messages" section on "Folding Long Header Fields" - above, and the section on "Case Independence" below.) Note - that the official semantics therefore do not "see" any - unquoted CRLFs that are in comments, although particular pars- - ing programs may wish to note their presence. For these pro- - grams, it would be reasonable to interpret a "CRLF LWSP-char" - as being a CRLF that is part of the comment; i.e., the CRLF is - kept and the LWSP-char is discarded. Quoted CRLFs (i.e., a - backslash followed by a CR followed by a LF) still must be - followed by at least one LWSP-char. - - 3.4.4. DELIMITING AND QUOTING CHARACTERS - - The quote character (backslash) and characters that delimit - syntactic units are not, generally, to be taken as data that - are part of the delimited or quoted unit(s). In particular, - the quotation-marks that define a quoted-string, the - parentheses that define a comment and the backslash that - quotes a following character are NOT part of the quoted- - string, comment or quoted character. A quotation-mark that is - to be part of a quoted-string, a parenthesis that is to be - part of a comment and a backslash that is to be part of either - must each be preceded by the quote-character backslash ("\"). - Note that the syntax allows any character to be quoted within - a quoted-string or comment; however only certain characters - MUST be quoted to be included as data. These characters are - the ones that are not part of the alternate text group (i.e., - ctext or qtext). - - The one exception to this rule is that a single SPACE is - assumed to exist between contiguous words in a phrase, and - this interpretation is independent of the actual number of - LWSP-chars that the creator places between the words. To - include more than one SPACE, the creator must make the LWSP- - chars be part of a quoted-string. - - Quotation marks that delimit a quoted string and backslashes - that quote the following character should NOT accompany the - quoted-string when the string is passed to processes that do - not interpret data according to this specification (e.g., mail - protocol servers). - - 3.4.5. QUOTED-STRINGS - - Where permitted (i.e., in words in structured fields) quoted- - strings are treated as a single symbol. That is, a quoted- - string is equivalent to an atom, syntactically. If a quoted- - string is to be "folded" onto multiple lines, then the syntax - for folding must be adhered to. (See the "Lexical Analysis of - - - August 13, 1982 - 13 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - Messages" section on "Folding Long Header Fields" above, and - the section on "Case Independence" below.) Therefore, the - official semantics do not "see" any bare CRLFs that are in - quoted-strings; however particular parsing programs may wish - to note their presence. For such programs, it would be rea- - sonable to interpret a "CRLF LWSP-char" as being a CRLF which - is part of the quoted-string; i.e., the CRLF is kept and the - LWSP-char is discarded. Quoted CRLFs (i.e., a backslash fol- - lowed by a CR followed by a LF) are also subject to rules of - folding, but the presence of the quoting character (backslash) - explicitly indicates that the CRLF is data to the quoted - string. Stripping off the first following LWSP-char is also - appropriate when parsing quoted CRLFs. - - 3.4.6. BRACKETING CHARACTERS - - There is one type of bracket which must occur in matched pairs - and may have pairs nested within each other: - - o Parentheses ("(" and ")") are used to indicate com- - ments. - - There are three types of brackets which must occur in matched - pairs, and which may NOT be nested: - - o Colon/semi-colon (":" and ";") are used in address - specifications to indicate that the included list of - addresses are to be treated as a group. - - o Angle brackets ("<" and ">") are generally used to - indicate the presence of a one machine-usable refer- - ence (e.g., delimiting mailboxes), possibly including - source-routing to the machine. - - o Square brackets ("[" and "]") are used to indicate the - presence of a domain-literal, which the appropriate - name-domain is to use directly, bypassing normal - name-resolution mechanisms. - - 3.4.7. CASE INDEPENDENCE - - Except as noted, alphabetic strings may be represented in any - combination of upper and lower case. The only syntactic units - - - - - - - - - August 13, 1982 - 14 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - which requires preservation of case information are: - - - text - - qtext - - dtext - - ctext - - quoted-pair - - local-part, except "Postmaster" - - When matching any other syntactic unit, case is to be ignored. - For example, the field-names "From", "FROM", "from", and even - "FroM" are semantically equal and should all be treated ident- - ically. - - When generating these units, any mix of upper and lower case - alphabetic characters may be used. The case shown in this - specification is suggested for message-creating processes. - - Note: The reserved local-part address unit, "Postmaster", is - an exception. When the value "Postmaster" is being - interpreted, it must be accepted in any mixture of - case, including "POSTMASTER", and "postmaster". - - 3.4.8. FOLDING LONG HEADER FIELDS - - Each header field may be represented on exactly one line con- - sisting of the name of the field and its body, and terminated - by a CRLF; this is what the parser sees. For readability, the - field-body portion of long header fields may be "folded" onto - multiple lines of the actual field. "Long" is commonly inter- - preted to mean greater than 65 or 72 characters. The former - length serves as a limit, when the message is to be viewed on - most simple terminals which use simple display software; how- - ever, the limit is not imposed by this standard. - - Note: Some display software often can selectively fold lines, - to suit the display terminal. In such cases, sender- - provided folding can interfere with the display - software. - - 3.4.9. BACKSPACE CHARACTERS - - ASCII BS characters (Backspace, decimal 8) may be included in - texts and quoted-strings to effect overstriking. However, any - use of backspaces which effects an overstrike to the left of - the beginning of the text or quoted-string is prohibited. - - - - - - August 13, 1982 - 15 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - 3.4.10. NETWORK-SPECIFIC TRANSFORMATIONS - - During transmission through heterogeneous networks, it may be - necessary to force data to conform to a network's local con- - ventions. For example, it may be required that a CR be fol- - lowed either by LF, making a CRLF, or by <null>, if the CR is - to stand alone). Such transformations are reversed, when the - message exits that network. - - When crossing network boundaries, the message should be - treated as passing through two modules. It will enter the - first module containing whatever network-specific transforma- - tions that were necessary to permit migration through the - "current" network. It then passes through the modules: - - o Transformation Reversal - - The "current" network's idiosyncracies are removed and - the message is returned to the canonical form speci- - fied in this standard. - - o Transformation - - The "next" network's local idiosyncracies are imposed - on the message. - - ------------------ - From ==> | Remove Net-A | - Net-A | idiosyncracies | - ------------------ - || - \/ - Conformance - with standard - || - \/ - ------------------ - | Impose Net-B | ==> To - | idiosyncracies | Net-B - ------------------ - - - - - - - - - - - - August 13, 1982 - 16 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - 4. MESSAGE SPECIFICATION - - 4.1. SYNTAX - - Note: Due to an artifact of the notational conventions, the syn- - tax indicates that, when present, some fields, must be in - a particular order. Header fields are NOT required to - occur in any particular order, except that the message - body must occur AFTER the headers. It is recommended - that, if present, headers be sent in the order "Return- - Path", "Received", "Date", "From", "Subject", "Sender", - "To", "cc", etc. - - This specification permits multiple occurrences of most - fields. Except as noted, their interpretation is not - specified here, and their use is discouraged. - - The following syntax for the bodies of various fields should - be thought of as describing each field body as a single long - string (or line). The "Lexical Analysis of Message" section on - "Long Header Fields", above, indicates how such long strings can - be represented on more than one line in the actual transmitted - message. - - message = fields *( CRLF *text ) ; Everything after - ; first null line - ; is message body - - fields = dates ; Creation time, - source ; author id & one - 1*destination ; address required - *optional-field ; others optional - - source = [ trace ] ; net traversals - originator ; original mail - [ resent ] ; forwarded - - trace = return ; path to sender - 1*received ; receipt tags - - return = "Return-path" ":" route-addr ; return address - - received = "Received" ":" ; one per relay - ["from" domain] ; sending host - ["by" domain] ; receiving host - ["via" atom] ; physical path - *("with" atom) ; link/mail protocol - ["id" msg-id] ; receiver msg id - ["for" addr-spec] ; initial form - - - August 13, 1982 - 17 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - ";" date-time ; time received - - originator = authentic ; authenticated addr - [ "Reply-To" ":" 1#address] ) - - authentic = "From" ":" mailbox ; Single author - / ( "Sender" ":" mailbox ; Actual submittor - "From" ":" 1#mailbox) ; Multiple authors - ; or not sender - - resent = resent-authentic - [ "Resent-Reply-To" ":" 1#address] ) - - resent-authentic = - = "Resent-From" ":" mailbox - / ( "Resent-Sender" ":" mailbox - "Resent-From" ":" 1#mailbox ) - - dates = orig-date ; Original - [ resent-date ] ; Forwarded - - orig-date = "Date" ":" date-time - - resent-date = "Resent-Date" ":" date-time - - destination = "To" ":" 1#address ; Primary - / "Resent-To" ":" 1#address - / "cc" ":" 1#address ; Secondary - / "Resent-cc" ":" 1#address - / "bcc" ":" #address ; Blind carbon - / "Resent-bcc" ":" #address - - optional-field = - / "Message-ID" ":" msg-id - / "Resent-Message-ID" ":" msg-id - / "In-Reply-To" ":" *(phrase / msg-id) - / "References" ":" *(phrase / msg-id) - / "Keywords" ":" #phrase - / "Subject" ":" *text - / "Comments" ":" *text - / "Encrypted" ":" 1#2word - / extension-field ; To be defined - / user-defined-field ; May be pre-empted - - msg-id = "<" addr-spec ">" ; Unique message id - - - - - - - August 13, 1982 - 18 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - extension-field = - <Any field which is defined in a document - published as a formal extension to this - specification; none will have names beginning - with the string "X-"> - - user-defined-field = - <Any field which has not been defined - in this specification or published as an - extension to this specification; names for - such fields must be unique and may be - pre-empted by published extensions> - - 4.2. FORWARDING - - Some systems permit mail recipients to forward a message, - retaining the original headers, by adding some new fields. This - standard supports such a service, through the "Resent-" prefix to - field names. - - Whenever the string "Resent-" begins a field name, the field - has the same semantics as a field whose name does not have the - prefix. However, the message is assumed to have been forwarded - by an original recipient who attached the "Resent-" field. This - new field is treated as being more recent than the equivalent, - original field. For example, the "Resent-From", indicates the - person that forwarded the message, whereas the "From" field indi- - cates the original author. - - Use of such precedence information depends upon partici- - pants' communication needs. For example, this standard does not - dictate when a "Resent-From:" address should receive replies, in - lieu of sending them to the "From:" address. - - Note: In general, the "Resent-" fields should be treated as con- - taining a set of information that is independent of the - set of original fields. Information for one set should - not automatically be taken from the other. The interpre- - tation of multiple "Resent-" fields, of the same type, is - undefined. - - In the remainder of this specification, occurrence of legal - "Resent-" fields are treated identically with the occurrence of - - - - - - - - - August 13, 1982 - 19 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - fields whose names do not contain this prefix. - - 4.3. TRACE FIELDS - - Trace information is used to provide an audit trail of mes- - sage handling. In addition, it indicates a route back to the - sender of the message. - - The list of known "via" and "with" values are registered - with the Network Information Center, SRI International, Menlo - Park, California. - - 4.3.1. RETURN-PATH - - This field is added by the final transport system that - delivers the message to its recipient. The field is intended - to contain definitive information about the address and route - back to the message's originator. - - Note: The "Reply-To" field is added by the originator and - serves to direct replies, whereas the "Return-Path" - field is used to identify a path back to the origina- - tor. - - While the syntax indicates that a route specification is - optional, every attempt should be made to provide that infor- - mation in this field. - - 4.3.2. RECEIVED - - A copy of this field is added by each transport service that - relays the message. The information in the field can be quite - useful for tracing transport problems. - - The names of the sending and receiving hosts and time-of- - receipt may be specified. The "via" parameter may be used, to - indicate what physical mechanism the message was sent over, - such as Arpanet or Phonenet, and the "with" parameter may be - used to indicate the mail-, or connection-, level protocol - that was used, such as the SMTP mail protocol, or X.25 tran- - sport protocol. - - Note: Several "with" parameters may be included, to fully - specify the set of protocols that were used. - - Some transport services queue mail; the internal message iden- - tifier that is assigned to the message may be noted, using the - "id" parameter. When the sending host uses a destination - address specification that the receiving host reinterprets, by - - - August 13, 1982 - 20 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - expansion or transformation, the receiving host may wish to - record the original specification, using the "for" parameter. - For example, when a copy of mail is sent to the member of a - distribution list, this parameter may be used to record the - original address that was used to specify the list. - - 4.4. ORIGINATOR FIELDS - - The standard allows only a subset of the combinations possi- - ble with the From, Sender, Reply-To, Resent-From, Resent-Sender, - and Resent-Reply-To fields. The limitation is intentional. - - 4.4.1. FROM / RESENT-FROM - - This field contains the identity of the person(s) who wished - this message to be sent. The message-creation process should - default this field to be a single, authenticated machine - address, indicating the AGENT (person, system or process) - entering the message. If this is not done, the "Sender" field - MUST be present. If the "From" field IS defaulted this way, - the "Sender" field is optional and is redundant with the - "From" field. In all cases, addresses in the "From" field - must be machine-usable (addr-specs) and may not contain named - lists (groups). - - 4.4.2. SENDER / RESENT-SENDER - - This field contains the authenticated identity of the AGENT - (person, system or process) that sends the message. It is - intended for use when the sender is not the author of the mes- - sage, or to indicate who among a group of authors actually - sent the message. If the contents of the "Sender" field would - be completely redundant with the "From" field, then the - "Sender" field need not be present and its use is discouraged - (though still legal). In particular, the "Sender" field MUST - be present if it is NOT the same as the "From" Field. - - The Sender mailbox specification includes a word sequence - which must correspond to a specific agent (i.e., a human user - or a computer program) rather than a standard address. This - indicates the expectation that the field will identify the - single AGENT (person, system, or process) responsible for - sending the mail and not simply include the name of a mailbox - from which the mail was sent. For example in the case of a - shared login name, the name, by itself, would not be adequate. - The local-part address unit, which refers to this agent, is - expected to be a computer system term, and not (for example) a - generalized person reference which can be used outside the - network text message context. - - - August 13, 1982 - 21 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - Since the critical function served by the "Sender" field is - identification of the agent responsible for sending mail and - since computer programs cannot be held accountable for their - behavior, it is strongly recommended that when a computer pro- - gram generates a message, the HUMAN who is responsible for - that program be referenced as part of the "Sender" field mail- - box specification. - - 4.4.3. REPLY-TO / RESENT-REPLY-TO - - This field provides a general mechanism for indicating any - mailbox(es) to which responses are to be sent. Three typical - uses for this feature can be distinguished. In the first - case, the author(s) may not have regular machine-based mail- - boxes and therefore wish(es) to indicate an alternate machine - address. In the second case, an author may wish additional - persons to be made aware of, or responsible for, replies. A - somewhat different use may be of some help to "text message - teleconferencing" groups equipped with automatic distribution - services: include the address of that service in the "Reply- - To" field of all messages submitted to the teleconference; - then participants can "reply" to conference submissions to - guarantee the correct distribution of any submission of their - own. - - Note: The "Return-Path" field is added by the mail transport - service, at the time of final deliver. It is intended - to identify a path back to the orginator of the mes- - sage. The "Reply-To" field is added by the message - originator and is intended to direct replies. - - 4.4.4. AUTOMATIC USE OF FROM / SENDER / REPLY-TO - - For systems which automatically generate address lists for - replies to messages, the following recommendations are made: - - o The "Sender" field mailbox should be sent notices of - any problems in transport or delivery of the original - messages. If there is no "Sender" field, then the - "From" field mailbox should be used. - - o The "Sender" field mailbox should NEVER be used - automatically, in a recipient's reply message. - - o If the "Reply-To" field exists, then the reply should - go to the addresses indicated in that field and not to - the address(es) indicated in the "From" field. - - - - - August 13, 1982 - 22 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - o If there is a "From" field, but no "Reply-To" field, - the reply should be sent to the address(es) indicated - in the "From" field. - - Sometimes, a recipient may actually wish to communicate with - the person that initiated the message transfer. In such - cases, it is reasonable to use the "Sender" address. - - This recommendation is intended only for automated use of - originator-fields and is not intended to suggest that replies - may not also be sent to other recipients of messages. It is - up to the respective mail-handling programs to decide what - additional facilities will be provided. - - Examples are provided in Appendix A. - - 4.5. RECEIVER FIELDS - - 4.5.1. TO / RESENT-TO - - This field contains the identity of the primary recipients of - the message. - - 4.5.2. CC / RESENT-CC - - This field contains the identity of the secondary (informa- - tional) recipients of the message. - - 4.5.3. BCC / RESENT-BCC - - This field contains the identity of additional recipients of - the message. The contents of this field are not included in - copies of the message sent to the primary and secondary reci- - pients. Some systems may choose to include the text of the - "Bcc" field only in the author(s)'s copy, while others may - also include it in the text sent to all those indicated in the - "Bcc" list. - - 4.6. REFERENCE FIELDS - - 4.6.1. MESSAGE-ID / RESENT-MESSAGE-ID - - This field contains a unique identifier (the local-part - address unit) which refers to THIS version of THIS message. - The uniqueness of the message identifier is guaranteed by the - host which generates it. This identifier is intended to be - machine readable and not necessarily meaningful to humans. A - message identifier pertains to exactly one instantiation of a - particular message; subsequent revisions to the message should - - - August 13, 1982 - 23 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - each receive new message identifiers. - - 4.6.2. IN-REPLY-TO - - The contents of this field identify previous correspon- - dence which this message answers. Note that if message iden- - tifiers are used in this field, they must use the msg-id - specification format. - - 4.6.3. REFERENCES - - The contents of this field identify other correspondence - which this message references. Note that if message identif- - iers are used, they must use the msg-id specification format. - - 4.6.4. KEYWORDS - - This field contains keywords or phrases, separated by - commas. - - 4.7. OTHER FIELDS - - 4.7.1. SUBJECT - - This is intended to provide a summary, or indicate the - nature, of the message. - - 4.7.2. COMMENTS - - Permits adding text comments onto the message without - disturbing the contents of the message's body. - - 4.7.3. ENCRYPTED - - Sometimes, data encryption is used to increase the - privacy of message contents. If the body of a message has - been encrypted, to keep its contents private, the "Encrypted" - field can be used to note the fact and to indicate the nature - of the encryption. The first <word> parameter indicates the - software used to encrypt the body, and the second, optional - <word> is intended to aid the recipient in selecting the - proper decryption key. This code word may be viewed as an - index to a table of keys held by the recipient. - - Note: Unfortunately, headers must contain envelope, as well - as contents, information. Consequently, it is neces- - sary that they remain unencrypted, so that mail tran- - sport services may access them. Since names, - addresses, and "Subject" field contents may contain - - - August 13, 1982 - 24 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - sensitive information, this requirement limits total - message privacy. - - Names of encryption software are registered with the Net- - work Information Center, SRI International, Menlo Park, Cali- - fornia. - - 4.7.4. EXTENSION-FIELD - - A limited number of common fields have been defined in - this document. As network mail requirements dictate, addi- - tional fields may be standardized. To provide user-defined - fields with a measure of safety, in name selection, such - extension-fields will never have names that begin with the - string "X-". - - Names of Extension-fields are registered with the Network - Information Center, SRI International, Menlo Park, California. - - 4.7.5. USER-DEFINED-FIELD - - Individual users of network mail are free to define and - use additional header fields. Such fields must have names - which are not already used in the current specification or in - any definitions of extension-fields, and the overall syntax of - these user-defined-fields must conform to this specification's - rules for delimiting and folding fields. Due to the - extension-field publishing process, the name of a user- - defined-field may be pre-empted - - Note: The prefatory string "X-" will never be used in the - names of Extension-fields. This provides user-defined - fields with a protected set of names. - - - - - - - - - - - - - - - - - - - August 13, 1982 - 25 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - 5. DATE AND TIME SPECIFICATION - - 5.1. SYNTAX - - date-time = [ day "," ] date time ; dd mm yy - ; hh:mm:ss zzz - - day = "Mon" / "Tue" / "Wed" / "Thu" - / "Fri" / "Sat" / "Sun" - - date = 1*2DIGIT month 2DIGIT ; day month year - ; e.g. 20 Jun 82 - - month = "Jan" / "Feb" / "Mar" / "Apr" - / "May" / "Jun" / "Jul" / "Aug" - / "Sep" / "Oct" / "Nov" / "Dec" - - time = hour zone ; ANSI and Military - - hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT] - ; 00:00:00 - 23:59:59 - - zone = "UT" / "GMT" ; Universal Time - ; North American : UT - / "EST" / "EDT" ; Eastern: - 5/ - 4 - / "CST" / "CDT" ; Central: - 6/ - 5 - / "MST" / "MDT" ; Mountain: - 7/ - 6 - / "PST" / "PDT" ; Pacific: - 8/ - 7 - / 1ALPHA ; Military: Z = UT; - ; A:-1; (J not used) - ; M:-12; N:+1; Y:+12 - / ( ("+" / "-") 4DIGIT ) ; Local differential - ; hours+min. (HHMM) - - 5.2. SEMANTICS - - If included, day-of-week must be the day implied by the date - specification. - - Time zone may be indicated in several ways. "UT" is Univer- - sal Time (formerly called "Greenwich Mean Time"); "GMT" is per- - mitted as a reference to Universal Time. The military standard - uses a single character for each zone. "Z" is Universal Time. - "A" indicates one hour earlier, and "M" indicates 12 hours ear- - lier; "N" is one hour later, and "Y" is 12 hours later. The - letter "J" is not used. The other remaining two forms are taken - from ANSI standard X3.51-1975. One allows explicit indication of - the amount of offset from UT; the other uses common 3-character - strings for indicating time zones in North America. - - - August 13, 1982 - 26 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - 6. ADDRESS SPECIFICATION - - 6.1. SYNTAX - - address = mailbox ; one addressee - / group ; named list - - group = phrase ":" [#mailbox] ";" - - mailbox = addr-spec ; simple address - / phrase route-addr ; name & addr-spec - - route-addr = "<" [route] addr-spec ">" - - route = 1#("@" domain) ":" ; path-relative - - addr-spec = local-part "@" domain ; global address - - local-part = word *("." word) ; uninterpreted - ; case-preserved - - domain = sub-domain *("." sub-domain) - - sub-domain = domain-ref / domain-literal - - domain-ref = atom ; symbolic reference - - 6.2. SEMANTICS - - A mailbox receives mail. It is a conceptual entity which - does not necessarily pertain to file storage. For example, some - sites may choose to print mail on their line printer and deliver - the output to the addressee's desk. - - A mailbox specification comprises a person, system or pro- - cess name reference, a domain-dependent string, and a name-domain - reference. The name reference is optional and is usually used to - indicate the human name of a recipient. The name-domain refer- - ence specifies a sequence of sub-domains. The domain-dependent - string is uninterpreted, except by the final sub-domain; the rest - of the mail service merely transmits it as a literal string. - - 6.2.1. DOMAINS - - A name-domain is a set of registered (mail) names. A name- - domain specification resolves to a subordinate name-domain - specification or to a terminal domain-dependent string. - Hence, domain specification is extensible, permitting any - number of registration levels. - - - August 13, 1982 - 27 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - Name-domains model a global, logical, hierarchical addressing - scheme. The model is logical, in that an address specifica- - tion is related to name registration and is not necessarily - tied to transmission path. The model's hierarchy is a - directed graph, called an in-tree, such that there is a single - path from the root of the tree to any node in the hierarchy. - If more than one path actually exists, they are considered to - be different addresses. - - The root node is common to all addresses; consequently, it is - not referenced. Its children constitute "top-level" name- - domains. Usually, a service has access to its own full domain - specification and to the names of all top-level name-domains. - - The "top" of the domain addressing hierarchy -- a child of the - root -- is indicated by the right-most field, in a domain - specification. Its child is specified to the left, its child - to the left, and so on. - - Some groups provide formal registration services; these con- - stitute name-domains that are independent logically of - specific machines. In addition, networks and machines impli- - citly compose name-domains, since their membership usually is - registered in name tables. - - In the case of formal registration, an organization implements - a (distributed) data base which provides an address-to-route - mapping service for addresses of the form: - - person@registry.organization - - Note that "organization" is a logical entity, separate from - any particular communication network. - - A mechanism for accessing "organization" is universally avail- - able. That mechanism, in turn, seeks an instantiation of the - registry; its location is not indicated in the address specif- - ication. It is assumed that the system which operates under - the name "organization" knows how to find a subordinate regis- - try. The registry will then use the "person" string to deter- - mine where to send the mail specification. - - The latter, network-oriented case permits simple, direct, - attachment-related address specification, such as: - - user@host.network - - Once the network is accessed, it is expected that a message - will go directly to the host and that the host will resolve - - - August 13, 1982 - 28 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - the user name, placing the message in the user's mailbox. - - 6.2.2. ABBREVIATED DOMAIN SPECIFICATION - - Since any number of levels is possible within the domain - hierarchy, specification of a fully qualified address can - become inconvenient. This standard permits abbreviated domain - specification, in a special case: - - For the address of the sender, call the left-most - sub-domain Level N. In a header address, if all of - the sub-domains above (i.e., to the right of) Level N - are the same as those of the sender, then they do not - have to appear in the specification. Otherwise, the - address must be fully qualified. - - This feature is subject to approval by local sub- - domains. Individual sub-domains may require their - member systems, which originate mail, to provide full - domain specification only. When permitted, abbrevia- - tions may be present only while the message stays - within the sub-domain of the sender. - - Use of this mechanism requires the sender's sub-domain - to reserve the names of all top-level domains, so that - full specifications can be distinguished from abbrevi- - ated specifications. - - For example, if a sender's address is: - - sender@registry-A.registry-1.organization-X - - and one recipient's address is: - - recipient@registry-B.registry-1.organization-X - - and another's is: - - recipient@registry-C.registry-2.organization-X - - then ".registry-1.organization-X" need not be specified in the - the message, but "registry-C.registry-2" DOES have to be - specified. That is, the first two addresses may be abbrevi- - ated, but the third address must be fully specified. - - When a message crosses a domain boundary, all addresses must - be specified in the full format, ending with the top-level - name-domain in the right-most field. It is the responsibility - of mail forwarding services to ensure that addresses conform - - - August 13, 1982 - 29 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - with this requirement. In the case of abbreviated addresses, - the relaying service must make the necessary expansions. It - should be noted that it often is difficult for such a service - to locate all occurrences of address abbreviations. For exam- - ple, it will not be possible to find such abbreviations within - the body of the message. The "Return-Path" field can aid - recipients in recovering from these errors. - - Note: When passing any portion of an addr-spec onto a process - which does not interpret data according to this stan- - dard (e.g., mail protocol servers). There must be NO - LWSP-chars preceding or following the at-sign or any - delimiting period ("."), such as shown in the above - examples, and only ONE SPACE between contiguous - <word>s. - - 6.2.3. DOMAIN TERMS - - A domain-ref must be THE official name of a registry, network, - or host. It is a symbolic reference, within a name sub- - domain. At times, it is necessary to bypass standard mechan- - isms for resolving such references, using more primitive - information, such as a network host address rather than its - associated host name. - - To permit such references, this standard provides the domain- - literal construct. Its contents must conform with the needs - of the sub-domain in which it is interpreted. - - Domain-literals which refer to domains within the ARPA Inter- - net specify 32-bit Internet addresses, in four 8-bit fields - noted in decimal, as described in Request for Comments #820, - "Assigned Numbers." For example: - - [10.0.3.19] - - Note: THE USE OF DOMAIN-LITERALS IS STRONGLY DISCOURAGED. It - is permitted only as a means of bypassing temporary - system limitations, such as name tables which are not - complete. - - The names of "top-level" domains, and the names of domains - under in the ARPA Internet, are registered with the Network - Information Center, SRI International, Menlo Park, California. - - 6.2.4. DOMAIN-DEPENDENT LOCAL STRING - - The local-part of an addr-spec in a mailbox specification - (i.e., the host's name for the mailbox) is understood to be - - - August 13, 1982 - 30 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - whatever the receiving mail protocol server allows. For exam- - ple, some systems do not understand mailbox references of the - form "P. D. Q. Bach", but others do. - - This specification treats periods (".") as lexical separators. - Hence, their presence in local-parts which are not quoted- - strings, is detected. However, such occurrences carry NO - semantics. That is, if a local-part has periods within it, an - address parser will divide the local-part into several tokens, - but the sequence of tokens will be treated as one uninter- - preted unit. The sequence will be re-assembled, when the - address is passed outside of the system such as to a mail pro- - tocol service. - - For example, the address: - - First.Last@Registry.Org - - is legal and does not require the local-part to be surrounded - with quotation-marks. (However, "First Last" DOES require - quoting.) The local-part of the address, when passed outside - of the mail system, within the Registry.Org domain, is - "First.Last", again without quotation marks. - - 6.2.5. BALANCING LOCAL-PART AND DOMAIN - - In some cases, the boundary between local-part and domain can - be flexible. The local-part may be a simple string, which is - used for the final determination of the recipient's mailbox. - All other levels of reference are, therefore, part of the - domain. - - For some systems, in the case of abbreviated reference to the - local and subordinate sub-domains, it may be possible to - specify only one reference within the domain part and place - the other, subordinate name-domain references within the - local-part. This would appear as: - - mailbox.sub1.sub2@this-domain - - Such a specification would be acceptable to address parsers - which conform to RFC #733, but do not support this newer - Internet standard. While contrary to the intent of this stan- - dard, the form is legal. - - Also, some sub-domains have a specification syntax which does - not conform to this standard. For example: - - sub-net.mailbox@sub-domain.domain - - - August 13, 1982 - 31 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - uses a different parsing sequence for local-part than for - domain. - - Note: As a rule, the domain specification should contain - fields which are encoded according to the syntax of - this standard and which contain generally-standardized - information. The local-part specification should con- - tain only that portion of the address which deviates - from the form or intention of the domain field. - - 6.2.6. MULTIPLE MAILBOXES - - An individual may have several mailboxes and wish to receive - mail at whatever mailbox is convenient for the sender to - access. This standard does not provide a means of specifying - "any member of" a list of mailboxes. - - A set of individuals may wish to receive mail as a single unit - (i.e., a distribution list). The <group> construct permits - specification of such a list. Recipient mailboxes are speci- - fied within the bracketed part (":" - ";"). A copy of the - transmitted message is to be sent to each mailbox listed. - This standard does not permit recursive specification of - groups within groups. - - While a list must be named, it is not required that the con- - tents of the list be included. In this case, the <address> - serves only as an indication of group distribution and would - appear in the form: - - name:; - - Some mail services may provide a group-list distribution - facility, accepting a single mailbox reference, expanding it - to the full distribution list, and relaying the mail to the - list's members. This standard provides no additional syntax - for indicating such a service. Using the <group> address - alternative, while listing one mailbox in it, can mean either - that the mailbox reference will be expanded to a list or that - there is a group with one member. - - 6.2.7. EXPLICIT PATH SPECIFICATION - - At times, a message originator may wish to indicate the - transmission path that a message should follow. This is - called source routing. The normal addressing scheme, used in - an addr-spec, is carefully separated from such information; - the <route> portion of a route-addr is provided for such occa- - sions. It specifies the sequence of hosts and/or transmission - - - August 13, 1982 - 32 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - services that are to be traversed. Both domain-refs and - domain-literals may be used. - - Note: The use of source routing is discouraged. Unless the - sender has special need of path restriction, the choice - of transmission route should be left to the mail tran- - sport service. - - 6.3. RESERVED ADDRESS - - It often is necessary to send mail to a site, without know- - ing any of its valid addresses. For example, there may be mail - system dysfunctions, or a user may wish to find out a person's - correct address, at that site. - - This standard specifies a single, reserved mailbox address - (local-part) which is to be valid at each site. Mail sent to - that address is to be routed to a person responsible for the - site's mail system or to a person with responsibility for general - site operation. The name of the reserved local-part address is: - - Postmaster - - so that "Postmaster@domain" is required to be valid. - - Note: This reserved local-part must be matched without sensi- - tivity to alphabetic case, so that "POSTMASTER", "postmas- - ter", and even "poStmASteR" is to be accepted. - - - - - - - - - - - - - - - - - - - - - - - - August 13, 1982 - 33 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - 7. BIBLIOGRAPHY - - - ANSI. "USA Standard Code for Information Interchange," X3.4. - American National Standards Institute: New York (1968). Also - in: Feinler, E. and J. Postel, eds., "ARPANET Protocol Hand- - book", NIC 7104. - - ANSI. "Representations of Universal Time, Local Time Differen- - tials, and United States Time Zone References for Information - Interchange," X3.51-1975. American National Standards Insti- - tute: New York (1975). - - Bemer, R.W., "Time and the Computer." In: Interface Age (Feb. - 1979). - - Bennett, C.J. "JNT Mail Protocol". Joint Network Team, Ruther- - ford and Appleton Laboratory: Didcot, England. - - Bhushan, A.K., Pogran, K.T., Tomlinson, R.S., and White, J.E. - "Standardizing Network Mail Headers," ARPANET Request for - Comments No. 561, Network Information Center No. 18516; SRI - International: Menlo Park (September 1973). - - Birrell, A.D., Levin, R., Needham, R.M., and Schroeder, M.D. - "Grapevine: An Exercise in Distributed Computing," Communica- - tions of the ACM 25, 4 (April 1982), 260-274. - - Crocker, D.H., Vittal, J.J., Pogran, K.T., Henderson, D.A. - "Standard for the Format of ARPA Network Text Message," - ARPANET Request for Comments No. 733, Network Information - Center No. 41952. SRI International: Menlo Park (November - 1977). - - Feinler, E.J. and Postel, J.B. ARPANET Protocol Handbook, Net- - work Information Center No. 7104 (NTIS AD A003890). SRI - International: Menlo Park (April 1976). - - Harary, F. "Graph Theory". Addison-Wesley: Reading, Mass. - (1969). - - Levin, R. and Schroeder, M. "Transport of Electronic Messages - through a Network," TeleInformatics 79, pp. 29-33. North - Holland (1979). Also as Xerox Palo Alto Research Center - Technical Report CSL-79-4. - - Myer, T.H. and Henderson, D.A. "Message Transmission Protocol," - ARPANET Request for Comments, No. 680, Network Information - Center No. 32116. SRI International: Menlo Park (1975). - - - August 13, 1982 - 34 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - NBS. "Specification of Message Format for Computer Based Message - Systems, Recommended Federal Information Processing Standard." - National Bureau of Standards: Gaithersburg, Maryland - (October 1981). - - NIC. Internet Protocol Transition Workbook. Network Information - Center, SRI-International, Menlo Park, California (March - 1982). - - Oppen, D.C. and Dalal, Y.K. "The Clearinghouse: A Decentralized - Agent for Locating Named Objects in a Distributed Environ- - ment," OPD-T8103. Xerox Office Products Division: Palo Alto, - CA. (October 1981). - - Postel, J.B. "Assigned Numbers," ARPANET Request for Comments, - No. 820. SRI International: Menlo Park (August 1982). - - Postel, J.B. "Simple Mail Transfer Protocol," ARPANET Request - for Comments, No. 821. SRI International: Menlo Park (August - 1982). - - Shoch, J.F. "Internetwork naming, addressing and routing," in - Proc. 17th IEEE Computer Society International Conference, pp. - 72-79, Sept. 1978, IEEE Cat. No. 78 CH 1388-8C. - - Su, Z. and Postel, J. "The Domain Naming Convention for Internet - User Applications," ARPANET Request for Comments, No. 819. - SRI International: Menlo Park (August 1982). - - - - - - - - - - - - - - - - - - - - - - - - August 13, 1982 - 35 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - APPENDIX - - - A. EXAMPLES - - A.1. ADDRESSES - - A.1.1. Alfred Neuman <Neuman@BBN-TENEXA> - - A.1.2. Neuman@BBN-TENEXA - - These two "Alfred Neuman" examples have identical seman- - tics, as far as the operation of the local host's mail sending - (distribution) program (also sometimes called its "mailer") - and the remote host's mail protocol server are concerned. In - the first example, the "Alfred Neuman" is ignored by the - mailer, as "Neuman@BBN-TENEXA" completely specifies the reci- - pient. The second example contains no superfluous informa- - tion, and, again, "Neuman@BBN-TENEXA" is the intended reci- - pient. - - Note: When the message crosses name-domain boundaries, then - these specifications must be changed, so as to indicate - the remainder of the hierarchy, starting with the top - level. - - A.1.3. "George, Ted" <Shared@Group.Arpanet> - - This form might be used to indicate that a single mailbox - is shared by several users. The quoted string is ignored by - the originating host's mailer, because "Shared@Group.Arpanet" - completely specifies the destination mailbox. - - A.1.4. Wilt . (the Stilt) Chamberlain@NBA.US - - The "(the Stilt)" is a comment, which is NOT included in - the destination mailbox address handed to the originating - system's mailer. The local-part of the address is the string - "Wilt.Chamberlain", with NO space between the first and second - words. - - A.1.5. Address Lists - - Gourmets: Pompous Person <WhoZiWhatZit@Cordon-Bleu>, - Childs@WGBH.Boston, Galloping Gourmet@ - ANT.Down-Under (Australian National Television), - Cheapie@Discount-Liquors;, - Cruisers: Port@Portugal, Jones@SEA;, - Another@Somewhere.SomeOrg - - - August 13, 1982 - 36 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - This group list example points out the use of comments and the - mixing of addresses and groups. - - A.2. ORIGINATOR ITEMS - - A.2.1. Author-sent - - George Jones logs into his host as "Jones". He sends - mail himself. - - From: Jones@Group.Org - - or - - From: George Jones <Jones@Group.Org> - - A.2.2. Secretary-sent - - George Jones logs in as Jones on his host. His secre- - tary, who logs in as Secy sends mail for him. Replies to the - mail should go to George. - - From: George Jones <Jones@Group> - Sender: Secy@Other-Group - - A.2.3. Secretary-sent, for user of shared directory - - George Jones' secretary sends mail for George. Replies - should go to George. - - From: George Jones<Shared@Group.Org> - Sender: Secy@Other-Group - - Note that there need not be a space between "Jones" and the - "<", but adding a space enhances readability (as is the case - in other examples. - - A.2.4. Committee activity, with one author - - George is a member of a committee. He wishes to have any - replies to his message go to all committee members. - - From: George Jones <Jones@Host.Net> - Sender: Jones@Host - Reply-To: The Committee: Jones@Host.Net, - Smith@Other.Org, - Doe@Somewhere-Else; - - Note that if George had not included himself in the - - - August 13, 1982 - 37 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - enumeration of The Committee, he would not have gotten an - implicit reply; the presence of the "Reply-to" field SUPER- - SEDES the sending of a reply to the person named in the "From" - field. - - A.2.5. Secretary acting as full agent of author - - George Jones asks his secretary (Secy@Host) to send a - message for him in his capacity as Group. He wants his secre- - tary to handle all replies. - - From: George Jones <Group@Host> - Sender: Secy@Host - Reply-To: Secy@Host - - A.2.6. Agent for user without online mailbox - - A friend of George's, Sarah, is visiting. George's - secretary sends some mail to a friend of Sarah in computer- - land. Replies should go to George, whose mailbox is Jones at - Registry. - - From: Sarah Friendly <Secy@Registry> - Sender: Secy-Name <Secy@Registry> - Reply-To: Jones@Registry. - - A.2.7. Agent for member of a committee - - George's secretary sends out a message which was authored - jointly by all the members of a committee. Note that the name - of the committee cannot be specified, since <group> names are - not permitted in the From field. - - From: Jones@Host, - Smith@Other-Host, - Doe@Somewhere-Else - Sender: Secy@SHost - - - - - - - - - - - - - - - August 13, 1982 - 38 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - A.3. COMPLETE HEADERS - - A.3.1. Minimum required - - Date: 26 Aug 76 1429 EDT Date: 26 Aug 76 1429 EDT - From: Jones@Registry.Org or From: Jones@Registry.Org - Bcc: To: Smith@Registry.Org - - Note that the "Bcc" field may be empty, while the "To" field - is required to have at least one address. - - A.3.2. Using some of the additional fields - - Date: 26 Aug 76 1430 EDT - From: George Jones<Group@Host> - Sender: Secy@SHOST - To: "Al Neuman"@Mad-Host, - Sam.Irving@Other-Host - Message-ID: <some.string@SHOST> - - A.3.3. About as complex as you're going to get - - Date : 27 Aug 76 0932 PDT - From : Ken Davis <KDavis@This-Host.This-net> - Subject : Re: The Syntax in the RFC - Sender : KSecy@Other-Host - Reply-To : Sam.Irving@Reg.Organization - To : George Jones <Group@Some-Reg.An-Org>, - Al.Neuman@MAD.Publisher - cc : Important folk: - Tom Softwood <Balsa@Tree.Root>, - "Sam Irving"@Other-Host;, - Standard Distribution: - /main/davis/people/standard@Other-Host, - "<Jones>standard.dist.3"@Tops-20-Host>; - Comment : Sam is away on business. He asked me to handle - his mail for him. He'll be able to provide a - more accurate explanation when he returns - next week. - In-Reply-To: <some.string@DBM.Group>, George's message - X-Special-action: This is a sample of user-defined field- - names. There could also be a field-name - "Special-action", but its name might later be - preempted - Message-ID: <4231.629.XYzi-What@Other-Host> - - - - - - - August 13, 1982 - 39 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - B. SIMPLE FIELD PARSING - - Some mail-reading software systems may wish to perform only - minimal processing, ignoring the internal syntax of structured - field-bodies and treating them the same as unstructured-field- - bodies. Such software will need only to distinguish: - - o Header fields from the message body, - - o Beginnings of fields from lines which continue fields, - - o Field-names from field-contents. - - The abbreviated set of syntactic rules which follows will - suffice for this purpose. It describes a limited view of mes- - sages and is a subset of the syntactic rules provided in the main - part of this specification. One small exception is that the con- - tents of field-bodies consist only of text: - - B.1. SYNTAX - - - message = *field *(CRLF *text) - - field = field-name ":" [field-body] CRLF - - field-name = 1*<any CHAR, excluding CTLs, SPACE, and ":"> - - field-body = *text [CRLF LWSP-char field-body] - - - B.2. SEMANTICS - - Headers occur before the message body and are terminated by - a null line (i.e., two contiguous CRLFs). - - A line which continues a header field begins with a SPACE or - HTAB character, while a line beginning a field starts with a - printable character which is not a colon. - - A field-name consists of one or more printable characters - (excluding colon, space, and control-characters). A field-name - MUST be contained on one line. Upper and lower case are not dis- - tinguished when comparing field-names. - - - - - - - - August 13, 1982 - 40 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - C. DIFFERENCES FROM RFC #733 - - The following summarizes the differences between this stan- - dard and the one specified in Arpanet Request for Comments #733, - "Standard for the Format of ARPA Network Text Messages". The - differences are listed in the order of their occurrence in the - current specification. - - C.1. FIELD DEFINITIONS - - C.1.1. FIELD NAMES - - These now must be a sequence of printable characters. They - may not contain any LWSP-chars. - - C.2. LEXICAL TOKENS - - C.2.1. SPECIALS - - The characters period ("."), left-square bracket ("["), and - right-square bracket ("]") have been added. For presentation - purposes, and when passing a specification to a system that - does not conform to this standard, periods are to be contigu- - ous with their surrounding lexical tokens. No linear-white- - space is permitted between them. The presence of one LWSP- - char between other tokens is still directed. - - C.2.2. ATOM - - Atoms may not contain SPACE. - - C.2.3. SPECIAL TEXT - - ctext and qtext have had backslash ("\") added to the list of - prohibited characters. - - C.2.4. DOMAINS - - The lexical tokens <domain-literal> and <dtext> have been - added. - - C.3. MESSAGE SPECIFICATION - - C.3.1. TRACE - - The "Return-path:" and "Received:" fields have been specified. - - - - - - August 13, 1982 - 41 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - C.3.2. FROM - - The "From" field must contain machine-usable addresses (addr- - spec). Multiple addresses may be specified, but named-lists - (groups) may not. - - C.3.3. RESENT - - The meta-construct of prefacing field names with the string - "Resent-" has been added, to indicate that a message has been - forwarded by an intermediate recipient. - - C.3.4. DESTINATION - - A message must contain at least one destination address field. - "To" and "CC" are required to contain at least one address. - - C.3.5. IN-REPLY-TO - - The field-body is no longer a comma-separated list, although a - sequence is still permitted. - - C.3.6. REFERENCE - - The field-body is no longer a comma-separated list, although a - sequence is still permitted. - - C.3.7. ENCRYPTED - - A field has been specified that permits senders to indicate - that the body of a message has been encrypted. - - C.3.8. EXTENSION-FIELD - - Extension fields are prohibited from beginning with the char- - acters "X-". - - C.4. DATE AND TIME SPECIFICATION - - C.4.1. SIMPLIFICATION - - Fewer optional forms are permitted and the list of three- - letter time zones has been shortened. - - C.5. ADDRESS SPECIFICATION - - - - - - - August 13, 1982 - 42 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - C.5.1. ADDRESS - - The use of quoted-string, and the ":"-atom-":" construct, have - been removed. An address now is either a single mailbox - reference or is a named list of addresses. The latter indi- - cates a group distribution. - - C.5.2. GROUPS - - Group lists are now required to to have a name. Group lists - may not be nested. - - C.5.3. MAILBOX - - A mailbox specification may indicate a person's name, as - before. Such a named list no longer may specify multiple - mailboxes and may not be nested. - - C.5.4. ROUTE ADDRESSING - - Addresses now are taken to be absolute, global specifications, - independent of transmission paths. The <route> construct has - been provided, to permit explicit specification of transmis- - sion path. RFC #733's use of multiple at-signs ("@") was - intended as a general syntax for indicating routing and/or - hierarchical addressing. The current standard separates these - specifications and only one at-sign is permitted. - - C.5.5. AT-SIGN - - The string " at " no longer is used as an address delimiter. - Only at-sign ("@") serves the function. - - C.5.6. DOMAINS - - Hierarchical, logical name-domains have been added. - - C.6. RESERVED ADDRESS - - The local-part "Postmaster" has been reserved, so that users can - be guaranteed at least one valid address at a site. - - - - - - - - - - - August 13, 1982 - 43 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - D. ALPHABETICAL LISTING OF SYNTAX RULES - - address = mailbox ; one addressee - / group ; named list - addr-spec = local-part "@" domain ; global address - ALPHA = <any ASCII alphabetic character> - ; (101-132, 65.- 90.) - ; (141-172, 97.-122.) - atom = 1*<any CHAR except specials, SPACE and CTLs> - authentic = "From" ":" mailbox ; Single author - / ( "Sender" ":" mailbox ; Actual submittor - "From" ":" 1#mailbox) ; Multiple authors - ; or not sender - CHAR = <any ASCII character> ; ( 0-177, 0.-127.) - comment = "(" *(ctext / quoted-pair / comment) ")" - CR = <ASCII CR, carriage return> ; ( 15, 13.) - CRLF = CR LF - ctext = <any CHAR excluding "(", ; => may be folded - ")", "\" & CR, & including - linear-white-space> - CTL = <any ASCII control ; ( 0- 37, 0.- 31.) - character and DEL> ; ( 177, 127.) - date = 1*2DIGIT month 2DIGIT ; day month year - ; e.g. 20 Jun 82 - dates = orig-date ; Original - [ resent-date ] ; Forwarded - date-time = [ day "," ] date time ; dd mm yy - ; hh:mm:ss zzz - day = "Mon" / "Tue" / "Wed" / "Thu" - / "Fri" / "Sat" / "Sun" - delimiters = specials / linear-white-space / comment - destination = "To" ":" 1#address ; Primary - / "Resent-To" ":" 1#address - / "cc" ":" 1#address ; Secondary - / "Resent-cc" ":" 1#address - / "bcc" ":" #address ; Blind carbon - / "Resent-bcc" ":" #address - DIGIT = <any ASCII decimal digit> ; ( 60- 71, 48.- 57.) - domain = sub-domain *("." sub-domain) - domain-literal = "[" *(dtext / quoted-pair) "]" - domain-ref = atom ; symbolic reference - dtext = <any CHAR excluding "[", ; => may be folded - "]", "\" & CR, & including - linear-white-space> - extension-field = - <Any field which is defined in a document - published as a formal extension to this - specification; none will have names beginning - with the string "X-"> - - - August 13, 1982 - 44 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - field = field-name ":" [ field-body ] CRLF - fields = dates ; Creation time, - source ; author id & one - 1*destination ; address required - *optional-field ; others optional - field-body = field-body-contents - [CRLF LWSP-char field-body] - field-body-contents = - <the ASCII characters making up the field-body, as - defined in the following sections, and consisting - of combinations of atom, quoted-string, and - specials tokens, or else consisting of texts> - field-name = 1*<any CHAR, excluding CTLs, SPACE, and ":"> - group = phrase ":" [#mailbox] ";" - hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT] - ; 00:00:00 - 23:59:59 - HTAB = <ASCII HT, horizontal-tab> ; ( 11, 9.) - LF = <ASCII LF, linefeed> ; ( 12, 10.) - linear-white-space = 1*([CRLF] LWSP-char) ; semantics = SPACE - ; CRLF => folding - local-part = word *("." word) ; uninterpreted - ; case-preserved - LWSP-char = SPACE / HTAB ; semantics = SPACE - mailbox = addr-spec ; simple address - / phrase route-addr ; name & addr-spec - message = fields *( CRLF *text ) ; Everything after - ; first null line - ; is message body - month = "Jan" / "Feb" / "Mar" / "Apr" - / "May" / "Jun" / "Jul" / "Aug" - / "Sep" / "Oct" / "Nov" / "Dec" - msg-id = "<" addr-spec ">" ; Unique message id - optional-field = - / "Message-ID" ":" msg-id - / "Resent-Message-ID" ":" msg-id - / "In-Reply-To" ":" *(phrase / msg-id) - / "References" ":" *(phrase / msg-id) - / "Keywords" ":" #phrase - / "Subject" ":" *text - / "Comments" ":" *text - / "Encrypted" ":" 1#2word - / extension-field ; To be defined - / user-defined-field ; May be pre-empted - orig-date = "Date" ":" date-time - originator = authentic ; authenticated addr - [ "Reply-To" ":" 1#address] ) - phrase = 1*word ; Sequence of words - - - - - August 13, 1982 - 45 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - qtext = <any CHAR excepting <">, ; => may be folded - "\" & CR, and including - linear-white-space> - quoted-pair = "\" CHAR ; may quote any char - quoted-string = <"> *(qtext/quoted-pair) <">; Regular qtext or - ; quoted chars. - received = "Received" ":" ; one per relay - ["from" domain] ; sending host - ["by" domain] ; receiving host - ["via" atom] ; physical path - *("with" atom) ; link/mail protocol - ["id" msg-id] ; receiver msg id - ["for" addr-spec] ; initial form - ";" date-time ; time received - - resent = resent-authentic - [ "Resent-Reply-To" ":" 1#address] ) - resent-authentic = - = "Resent-From" ":" mailbox - / ( "Resent-Sender" ":" mailbox - "Resent-From" ":" 1#mailbox ) - resent-date = "Resent-Date" ":" date-time - return = "Return-path" ":" route-addr ; return address - route = 1#("@" domain) ":" ; path-relative - route-addr = "<" [route] addr-spec ">" - source = [ trace ] ; net traversals - originator ; original mail - [ resent ] ; forwarded - SPACE = <ASCII SP, space> ; ( 40, 32.) - specials = "(" / ")" / "<" / ">" / "@" ; Must be in quoted- - / "," / ";" / ":" / "\" / <"> ; string, to use - / "." / "[" / "]" ; within a word. - sub-domain = domain-ref / domain-literal - text = <any CHAR, including bare ; => atoms, specials, - CR & bare LF, but NOT ; comments and - including CRLF> ; quoted-strings are - ; NOT recognized. - time = hour zone ; ANSI and Military - trace = return ; path to sender - 1*received ; receipt tags - user-defined-field = - <Any field which has not been defined - in this specification or published as an - extension to this specification; names for - such fields must be unique and may be - pre-empted by published extensions> - word = atom / quoted-string - - - - - August 13, 1982 - 46 - RFC #822 - - - - Standard for ARPA Internet Text Messages - - - zone = "UT" / "GMT" ; Universal Time - ; North American : UT - / "EST" / "EDT" ; Eastern: - 5/ - 4 - / "CST" / "CDT" ; Central: - 6/ - 5 - / "MST" / "MDT" ; Mountain: - 7/ - 6 - / "PST" / "PDT" ; Pacific: - 8/ - 7 - / 1ALPHA ; Military: Z = UT; - <"> = <ASCII quote mark> ; ( 42, 34.) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - August 13, 1982 - 47 - RFC #822 - diff --git a/usr.sbin/sendmail/doc/spell.good b/usr.sbin/sendmail/doc/spell.good deleted file mode 100644 index 18b1ca5469..0000000000 --- a/usr.sbin/sendmail/doc/spell.good +++ /dev/null @@ -1,219 +0,0 @@ -4BSD -ACHECK -ADDR -ARGV -ARPANET -Afile -Allan -Allman -Allman'SENDMAIL -Arpa -BERKEL -Backslashes -Bcc -BerkNet -Berkel -Biocca -Boggs -Borden79 -CBOS -CMU -CPU -CRLF -CSVAX -CVcsvax -Cnt -Crocker77a -Crocker77b -Crocker79 -DABerkeley -DAEMON -DBIngVAX -DRAFT'For -DUucbvax -DlFrom -DnMAILER -EOH -Ether99 -Ethernet -FTP -FTP1 -FTP2 -FULLNAME -Feinler -Feinler78 -Fmsg -Fortuitously -HdrInfo -ICST -IEN -INGRES -IP -IPC -Info -Ing70 -IngVAX -IngVax -LHS -Lesk -MAXHOP -MC -MH -MLFL -MMDF -MN -MPM -MSGID -McKusick -Metcalfe -Metcalfe76 -Mmsg -MsgSize -NBS80 -NCP -NEEDDATE -NEEDFROM -NEWFTP -NIC -NOTUNIX -Neigus -Neigus73 -Neigus78 -Nowitz -Nowitz78a -Nowitz78b -Only'DRAFT -PAF -PARC -Pogran -Postel -Postel74 -Postel77 -Postel79a -Postel79b -Postel80 -Postel81 -PostelIMS -Queueing -RFC -RFC733 -RFC780 -RHS -SENDMAIL -SMTP -Sattley78 -Schmidt79 -Schmidt99 -Sendmail -Shoens -Shoens79 -Sluizer -Sluizer81 -Stonebraker -Syserr -Szurkowski -TCP -UIP -UNIX80 -Usrerr -VAX -Vittal -ZRM -addr -ahost -arg -arpa -arpamail -arpanet -asa -auser -backslashes -bbn -bcc -berk -berkeley -berkmail -berknet -bool -cf -checkcompat -cmd -cnt -conf -csvax -daemon -dmr -drb -eric -ernie -esvax -fc -filename -formatted -ftp -fxs -hdrinfo -hostnames -info -ing70 -ingres -ingvax -instantiations -isif -joe -lA -lhs -lib -localmail -mailcf -mailer1 -mailer2 -mailer3 -mckusick -myname -namespace -newmail -pathname -postel -prog -progmail -queueing -ref -rerouting -rhs -rlsAmn -rmail -rsDxm -ruleset -runtime -sAu -samwise -sendberkmail -sender1 -sender2 -sender3 -sendmail -setgid -setname -sitename -syserr -syslog -temp -text1 -text2 -tuple -tuples -ucb -ucbvax -usc -userid -userlist -username -usernames -usrerr -uucpmail -val -vax135 -walkthrough -wnj -xlatstring diff --git a/usr.sbin/sendmail/doc/usenix.abs b/usr.sbin/sendmail/doc/usenix.abs deleted file mode 100644 index 8d8c7f4e2b..0000000000 --- a/usr.sbin/sendmail/doc/usenix.abs +++ /dev/null @@ -1,20 +0,0 @@ - SENDMAIL -- An Internetwork Mail Router - Eric Allman - Britton-Lee, Inc. - -Routing mail through a heterogenous internet presents many new -problems. Among the worst of these is that of address mapping. -Historically, this has been handled on an ad hoc basis. However, -this approach has become unmanageable as internets grow. - -Sendmail acts a unified "post office" to which all mail can be -submitted. Address interpretation is controlled by a production -system, which can parse both old and new format addresses. The -new format is "domain-based", a flexible technique that can -handle many common situations. Sendmail is not intended to perform -user interface functions. - -Sendmail will replace delivermail in the Berkeley 4.2 distribution. -Several major hosts are now or will soon be running sendmail. -This change will affect any users that route mail through a sendmail -gateway. The changes that will be user visible will be discussed. diff --git a/usr.sbin/sendmail/doc/usenix.me b/usr.sbin/sendmail/doc/usenix.me deleted file mode 100644 index 0c5b13bdec..0000000000 --- a/usr.sbin/sendmail/doc/usenix.me +++ /dev/null @@ -1,1076 +0,0 @@ -.nr si 3n -.he 'Mail Systems and Addressing in 4.2bsd''%' -.fo 'Version 4.1'USENIX \- Jan 83'Last Mod 7/25/83' -.if n .ls 2 -.+c -.(l C -.sz 14 -Mail Systems and Addressing -in 4.2bsd -.sz -.sp -Eric Allman\(dg -.sp 0.5 -.i -Britton-Lee, Inc. -1919 Addison Street, Suite 105. -Berkeley, California 94704. -.sp 0.5 -.r -eric@Berkeley.ARPA -ucbvax!eric -.)l -.sp -.(l F -.ce -ABSTRACT -.sp \n(psu -Routing mail through a heterogeneous internet presents many new -problems. -Among the worst of these is that of address mapping. -Historically, this has been handled on an ad hoc basis. -However, -this approach has become unmanageable as internets grow. -.sp \n(psu -Sendmail acts a unified -.q "post office" -to which all mail can be -submitted. -Address interpretation is controlled by a production -system, -which can parse both old and new format addresses. -The -new format is -.q "domain-based," -a flexible technique that can -handle many common situations. -Sendmail is not intended to perform -user interface functions. -.sp \n(psu -Sendmail will replace delivermail in the Berkeley 4.2 distribution. -Several major hosts are now or will soon be running sendmail. -This change will affect any users that route mail through a sendmail -gateway. -The changes that will be user visible are emphasized. -.)l -.sp 2 -.(f -\(dgA considerable part of this work -was done while under the employ -of the INGRES Project -at the University of California at Berkeley. -.)f -.pp -The mail system to appear in 4.2bsd -will contain a number of changes. -Most of these changes are based on the replacement of -.i delivermail -with a new module called -.i sendmail. -.i Sendmail -implements a general internetwork mail routing facility, -featuring aliasing and forwarding, -automatic routing to network gateways, -and flexible configuration. -Of key interest to the mail system user -will be the changes in the network addressing structure. -.pp -In a simple network, -each node has an address, -and resources can be identified -with a host-resource pair; -in particular, -the mail system can refer to users -using a host-username pair. -Host names and numbers have to be administered by a central authority, -but usernames can be assigned locally to each host. -.pp -In an internet, -multiple networks with different characteristics -and managements -must communicate. -In particular, -the syntax and semantics of resource identification change. -Certain special cases can be handled trivially -by -.i "ad hoc" -techniques, -such as -providing network names that appear local to hosts -on other networks, -as with the Ethernet at Xerox PARC. -However, the general case is extremely complex. -For example, -some networks require that the route the message takes -be explicitly specified by the sender, -simplifying the database update problem -since only adjacent hosts must be entered -into the system tables, -while others use logical addressing, -where the sender specifies the location of the recipient -but not how to get there. -Some networks use a left-associative syntax -and others use a right-associative syntax, -causing ambiguity in mixed addresses. -.pp -Internet standards seek to eliminate these problems. -Initially, these proposed expanding the address pairs -to address triples, -consisting of -{network, host, username} -triples. -Network numbers must be universally agreed upon, -and hosts can be assigned locally -on each network. -The user-level presentation was changed -to address domains, -comprised of a local resource identification -and a hierarchical domain specification -with a common static root. -The domain technique -separates the issue of physical versus logical addressing. -For example, -an address of the form -.q "eric@a.cc.berkeley.arpa" -describes the logical -organization of the address space -(user -.q eric -on host -.q a -in the Computer Center -at Berkeley) -but not the physical networks used -(for example, this could go over different networks -depending on whether -.q a -were on an ethernet -or a store-and-forward network). -.pp -.i Sendmail -is intended to help bridge the gap -between the totally -.i "ad hoc" -world -of networks that know nothing of each other -and the clean, tightly-coupled world -of unique network numbers. -It can accept old arbitrary address syntaxes, -resolving ambiguities using heuristics -specified by the system administrator, -as well as domain-based addressing. -It helps guide the conversion of message formats -between disparate networks. -In short, -.i sendmail -is designed to assist a graceful transition -to consistent internetwork addressing schemes. -.sp -.pp -Section 1 defines some of the terms -frequently left fuzzy -when working in mail systems. -Section 2 discusses the design goals for -.i sendmail . -In section 3, -the new address formats -and basic features of -.i sendmail -are described. -Section 4 discusses some of the special problems -of the UUCP network. -The differences between -.i sendmail -and -.i delivermail -are presented in section 5. -.sp -.(l F -.b DISCLAIMER: -A number of examples -in this paper -use names of actual people -and organizations. -This is not intended -to imply a commitment -or even an intellectual agreement -on the part of these people or organizations. -In particular, -Bell Telephone Laboratories (BTL), -Digital Equipment Corporation (DEC), -Lawrence Berkeley Laboratories (LBL), -Britton-Lee Incorporated (BLI), -and the University of California at Berkeley -are not committed to any of these proposals at this time. -Much of this paper -represents no more than -the personal opinions of the author. -.)l -.sh 1 "DEFINITIONS" -.pp -There are four basic concepts -that must be clearly distinguished -when dealing with mail systems: -the user (or the user's agent), -the user's identification, -the user's address, -and the route. -These are distinguished primarily by their position independence. -.sh 2 "User and Identification" -.pp -The user is the being -(a person or program) -that is creating or receiving a message. -An -.i agent -is an entity operating on behalf of the user \*- -such as a secretary who handles my mail. -or a program that automatically returns a -message such as -.q "I am at the UNICOM conference." -.pp -The identification is the tag -that goes along with the particular user. -This tag is completely independent of location. -For example, -my identification is the string -.q "Eric Allman," -and this identification does not change -whether I am located at U.C. Berkeley, -at Britton-Lee, -or at a scientific institute in Austria. -.pp -Since the identification is frequently ambiguous -(e.g., there are two -.q "Robert Henry" s -at Berkeley) -it is common to add other disambiguating information -that is not strictly part of the identification -(e.g., -Robert -.q "Code Generator" -Henry -versus -Robert -.q "System Administrator" -Henry). -.sh 2 "Address" -.pp -The address specifies a location. -As I move around, -my address changes. -For example, -my address might change from -.q eric@Berkeley.ARPA -to -.q eric@bli.UUCP -or -.q allman@IIASA.Austria -depending on my current affiliation. -.pp -However, -an address is independent of the location of anyone else. -That is, -my address remains the same to everyone who might be sending me mail. -For example, -a person at MIT and a person at USC -could both send to -.q eric@Berkeley.ARPA -and have it arrive to the same mailbox. -.pp -Ideally a -.q "white pages" -service would be provided to map user identifications -into addresses -(for example, see -[Solomon81]). -Currently this is handled by passing around -scraps of paper -or by calling people on the telephone -to find out their address. -.sh 2 "Route" -.pp -While an address specifies -.i where -to find a mailbox, -a route specifies -.i how -to find the mailbox. -Specifically, -it specifies a path -from sender to receiver. -As such, the route is potentially different -for every pair of people in the electronic universe. -.pp -Normally the route is hidden from the user -by the software. -However, -some networks put the burden of determining the route -onto the sender. -Although this simplifies the software, -it also greatly impairs the usability -for most users. -The UUCP network is an example of such a network. -.sh 1 "DESIGN GOALS" -.pp -Design goals for -.i sendmail \** -.(f -\**This section makes no distinction between -.i delivermail -and -.i sendmail. -.)f -include: -.np -Compatibility with the existing mail programs, -including Bell version 6 mail, -Bell version 7 mail, -Berkeley -.i Mail -[Shoens79], -BerkNet mail -[Schmidt79], -and hopefully UUCP mail -[Nowitz78]. -ARPANET mail -[Crocker82] -was also required. -.np -Reliability, in the sense of guaranteeing -that every message is correctly delivered -or at least brought to the attention of a human -for correct disposal; -no message should ever be completely lost. -This goal was considered essential -because of the emphasis on mail in our environment. -It has turned out to be one of the hardest goals to satisfy, -especially in the face of the many anomalous message formats -produced by various ARPANET sites. -For example, -certain sites generate improperly formated addresses, -occasionally -causing error-message loops. -Some hosts use blanks in names, -causing problems with -mail programs that assume that an address -is one word. -The semantics of some fields -are interpreted slightly differently -by different sites. -In summary, -the obscure features of the ARPANET mail protocol -really -.i are -used and -are difficult to support, -but must be supported. -.np -Existing software to do actual delivery -should be used whenever possible. -This goal derives as much from political and practical considerations -as technical. -.np -Easy expansion to -fairly complex environments, -including multiple -connections to a single network type -(such as with multiple UUCP or Ethernets). -This goal requires consideration of the contents of an address -as well as its syntax -in order to determine which gateway to use. -.np -Configuration information should not be compiled into the code. -A single compiled program should be able to run as is at any site -(barring such basic changes as the CPU type or the operating system). -We have found this seemingly unimportant goal -to be critical in real life. -Besides the simple problems that occur when any program gets recompiled -in a different environment, -many sites like to -.q fiddle -with anything that they will be recompiling anyway. -.np -.i Sendmail -must be able to let various groups maintain their own mailing lists, -and let individuals specify their own forwarding, -without modifying the system alias file. -.np -Each user should be able to specify which mailer to execute -to process mail being delivered for him. -This feature allows users who are using specialized mailers -that use a different format to build their environment -without changing the system, -and facilitates specialized functions -(such as returning an -.q "I am on vacation" -message). -.np -Network traffic should be minimized -by batching addresses to a single host where possible, -without assistance from the user. -.pp -These goals motivated the architecture illustrated in figure 1. -.(z -.hl -.ie t \ -. sp 18 -.el \{\ -.(c -+---------+ +---------+ +---------+ -| sender1 | | sender2 | | sender3 | -+---------+ +---------+ +---------+ - | | | - +----------+ + +----------+ - | | | - v v v - +-------------+ - | sendmail | - +-------------+ - | | | - +----------+ + +----------+ - | | | - v v v -+---------+ +---------+ +---------+ -| mailer1 | | mailer2 | | mailer3 | -+---------+ +---------+ +---------+ -.)c -.\} - -.ce -Figure 1 \*- Sendmail System Structure. -.hl -.)z -The user interacts with a mail generating and sending program. -When the mail is created, -the generator calls -.i sendmail , -which routes the message to the correct mailer(s). -Since some of the senders may be network servers -and some of the mailers may be network clients, -.i sendmail -may be used as an internet mail gateway. -.sh 1 "USAGE" -.sh 2 "Address Formats" -.pp -Arguments may be flags or addresses. -Flags set various processing options. -Following flag arguments, -address arguments may be given. -Addresses follow the syntax in RFC822 -[Crocker82] -for ARPANET -address formats. -In brief, the format is: -.np -Anything in parentheses is thrown away -(as a comment). -.np -Anything in angle brackets (\c -.q "<\|>" ) -is preferred -over anything else. -This rule implements the ARPANET standard that addresses of the form -.(b -user name <machine-address> -.)b -will send to the electronic -.q machine-address -rather than the human -.q "user name." -.np -Double quotes -(\ "\ ) -quote phrases; -backslashes quote characters. -Backslashes are more powerful -in that they will cause otherwise equivalent phrases -to compare differently \*- for example, -.i user -and -.i -"user" -.r -are equivalent, -but -.i \euser -is different from either of them. -This might be used -to avoid normal aliasing -or duplicate suppression algorithms. -.pp -Parentheses, angle brackets, and double quotes -must be properly balanced and nested. -The rewriting rules control remaining parsing\**. -.(f -\**Disclaimer: Some special processing is done -after rewriting local names; see below. -.)f -.pp -Although old style addresses are still accepted -in most cases, -the preferred address format -is based on ARPANET-style domain-based addresses -[Su82a]. -These addresses are based on a hierarchical, logical decomposition -of the address space. -The addresses are hierarchical in a sense -similar to the U.S. postal addresses: -the messages may first be routed to the correct state, -with no initial consideration of the city -or other addressing details. -The addresses are logical -in that each step in the hierarchy -corresponds to a set of -.q "naming authorities" -rather than a physical network. -.pp -For example, -the address: -.(l -eric@HostA.BigSite.ARPA -.)l -would first look up the domain -BigSite -in the namespace administrated by -ARPA. -A query could then be sent to -BigSite -for interpretation of -HostA. -Eventually the mail would arrive at -HostA, -which would then do final delivery -to user -.q eric. -.sh 2 "Mail to Files and Programs" -.pp -Files and programs are legitimate message recipients. -Files provide archival storage of messages, -useful for project administration and history. -Programs are useful as recipients in a variety of situations, -for example, -to maintain a public repository of systems messages -(such as the Berkeley -.i msgs -program). -.pp -Any address passing through the initial parsing algorithm -as a local address -(i.e, not appearing to be a valid address for another mailer) -is scanned for two special cases. -If prefixed by a vertical bar (\c -.q \^|\^ ) -the rest of the address is processed as a shell command. -If the user name begins with a slash mark (\c -.q /\^ ) -the name is used as a file name, -instead of a login name. -.sh 2 "Aliasing, Forwarding, Inclusion" -.pp -.i Sendmail -reroutes mail three ways. -Aliasing applies system wide. -Forwarding allows each user to reroute incoming mail -destined for that account. -Inclusion directs -.i sendmail -to read a file for a list of addresses, -and is normally used -in conjunction with aliasing. -.sh 3 "Aliasing" -.pp -Aliasing maps local addresses to address lists using a system-wide file. -This file is hashed to speed access. -Only addresses that parse as local -are allowed as aliases; -this guarantees a unique key -(since there are no nicknames for the local host). -.sh 3 "Forwarding" -.pp -After aliasing, -if an recipient address specifies a local user -.i sendmail -searches for a -.q .forward -file in the recipient's home directory. -If it exists, -the message is -.i not -sent to that user, -but rather to the list of addresses in that file. -Often -this list will contain only one address, -and the feature will be used for network mail forwarding. -.pp -Forwarding also permits a user to specify a private incoming mailer. -For example, -forwarding to: -.(b -"\^|\|/usr/local/newmail myname" -.)b -will use a different incoming mailer. -.sh 3 "Inclusion" -.pp -Inclusion is specified in RFC 733 [Crocker77] syntax: -.(b -:Include: pathname -.)b -An address of this form reads the file specified by -.i pathname -and sends to all users listed in that file. -.pp -The intent is -.i not -to support direct use of this feature, -but rather to use this as a subset of aliasing. -For example, -an alias of the form: -.(b -project: :include:/usr/project/userlist -.)b -is a method of letting a project maintain a mailing list -without interaction with the system administration, -even if the alias file is protected. -.pp -It is not necessary to rebuild the index on the alias database -when a :include: list is changed. -.sh 2 "Message Collection" -.pp -Once all recipient addresses are parsed and verified, -the message is collected. -The message comes in two parts: -a message header and a message body, -separated by a blank line. -The body is an uninterpreted -sequence of text lines. -.pp -The header is formated as a series of lines -of the form -.(b - field-name: field-value -.)b -Field-value can be split across lines by starting the following -lines with a space or a tab. -Some header fields have special internal meaning, -and have appropriate special processing. -Other headers are simply passed through. -Some header fields may be added automatically, -such as time stamps. -.sh 1 "THE UUCP PROBLEM" -.pp -Of particular interest -is the UUCP network. -The explicit routing -used in the UUCP environment -causes a number of serious problems. -First, -giving out an address -is impossible -without knowing the address of your potential correspondent. -This is typically handled -by specifying the address -relative to some -.q "well-known" -host -(e.g., -ucbvax or decvax). -Second, -it is often difficult to compute -the set of addresses -to reply to -without some knowledge -of the topology of the network. -Although it may be easy for a human being -to do this -under many circumstances, -a program does not have equally sophisticated heuristics -built in. -Third, -certain addresses will become painfully and unnecessarily long, -as when a message is routed through many hosts in the USENET. -And finally, -certain -.q "mixed domain" -addresses -are impossible to parse unambiguously \*- -e.g., -.(l -decvax!ucbvax!lbl-h!user@LBL-CSAM -.)l -might have many possible resolutions, -depending on whether the message was first routed -to decvax -or to LBL-CSAM. -.pp -To solve this problem, -the UUCP syntax -would have to be changed to use addresses -rather than routes. -For example, -the address -.q decvax!ucbvax!eric -might be expressed as -.q eric@ucbvax.UUCP -(with the hop through decvax implied). -This address would itself be a domain-based address; -for example, -an address might be of the form: -.(l -mark@d.cbosg.btl.UUCP -.)l -Hosts outside of Bell Telephone Laboratories -would then only need to know -how to get to a designated BTL relay, -and the BTL topology -would only be maintained inside Bell. -.pp -There are three major problems -associated with turning UUCP addresses -into something reasonable: -defining the namespace, -creating and propagating the necessary software, -and building and maintaining the database. -.sh 2 "Defining the Namespace" -.pp -Putting all UUCP hosts into a flat namespace -(e.g., -.q \&...@host.UUCP ) -is not practical for a number of reasons. -First, -with over 1600 sites already, -and (with the increasing availability of inexpensive microcomputers -and autodialers) -several thousand more coming within a few years, -the database update problem -is simply intractable -if the namespace is flat. -Second, -there are almost certainly name conflicts today. -Third, -as the number of sites grow -the names become ever less mnemonic. -.pp -It seems inevitable -that there be some sort of naming authority -for the set of top level names -in the UUCP domain, -as unpleasant a possibility -as that may seem. -It will simply not be possible -to have one host resolving all names. -It may however be possible -to handle this -in a fashion similar to that of assigning names of newsgroups -in USENET. -However, -it will be essential to encourage everyone -to become subdomains of an existing domain -whenever possible \*- -even though this will certainly bruise some egos. -For example, -if a new host named -.q blid -were to be added to the UUCP network, -it would probably actually be addressed as -.q d.bli.UUCP -(i.e., -as host -.q d -in the pseudo-domain -.q bli -rather than as host -.q blid -in the UUCP domain). -.sh 2 "Creating and Propagating the Software" -.pp -The software required to implement a consistent namespace -is relatively trivial. -Two modules are needed, -one to handle incoming mail -and one to handle outgoing mail. -.pp -The incoming module -must be prepared to handle either old or new style addresses. -New-style addresses -can be passed through unchanged. -Old style addresses -must be turned into new style addresses -where possible. -.pp -The outgoing module -is slightly trickier. -It must do a database lookup on the recipient addresses -(passed on the command line) -to determine what hosts to send the message to. -If those hosts do not accept new-style addresses, -it must transform all addresses in the header of the message -into old style using the database lookup. -.pp -Both of these modules -are straightforward -except for the issue of modifying the header. -It seems prudent to choose one format -for the message headers. -For a number of reasons, -Berkeley has elected to use the ARPANET protocols -for message formats. -However, -this protocol is somewhat difficult to parse. -.pp -Propagation is somewhat more difficult. -There are a large number of hosts -connected to UUCP -that will want to run completely standard systems -(for very good reasons). -The strategy is not to convert the entire network \*- -only enough of it it alleviate the problem. -.sh 2 "Building and Maintaining the Database" -.pp -This is by far the most difficult problem. -A prototype for this database -already exists, -but it is maintained by hand -and does not pretend to be complete. -.pp -This problem will be reduced considerably -if people choose to group their hosts -into subdomains. -This would require a global update -only when a new top level domain -joined the network. -A message to a host in a subdomain -could simply be routed to a known domain gateway -for further processing. -For example, -the address -.q eric@a.bli.UUCP -might be routed to the -.q bli -gateway -for redistribution; -new hosts could be added -within BLI -without notifying the rest of the world. -Of course, -other hosts -.i could -be notified as an efficiency measure. -.pp -There may be more than one domain gateway. -A domain such as BTL, -for instance, -might have a dozen gateways to the outside world; -a non-BTL site -could choose the closest gateway. -The only restriction -would be that all gateways -maintain a consistent view of the domain -they represent. -.sh 2 "Logical Structure" -.pp -Logically, -domains are organized into a tree. -There need not be a host actually associated -with each level in the tree \*- -for example, -there will be no host associated with the name -.q UUCP. -Similarly, -an organization might group names together for administrative reasons; -for example, -the name -.(l -CAD.research.BigCorp.UUCP -.)l -might not actually have a host representing -.q research. -.pp -However, -it may frequently be convenient to have a host -or hosts -that -.q represent -a domain. -For example, -if a single host exists that -represents -Berkeley, -then mail from outside Berkeley -can forward mail to that host -for further resolution -without knowing Berkeley's -(rather volatile) -topology. -This is not unlike the operation -of the telephone network. -.pp -This may also be useful -inside certain large domains. -For example, -at Berkeley it may be presumed -that most hosts know about other hosts -inside the Berkeley domain. -But if they process an address -that is unknown, -they can pass it -.q upstairs -for further examination. -Thus as new hosts are added -only one host -(the domain master) -.i must -be updated immediately; -other hosts can be updated as convenient. -.pp -Ideally this name resolution process -would be performed by a name server -(e.g., [Su82b]) -to avoid unnecessary copying -of the message. -However, -in a batch network -such as UUCP -this could result in unnecessary delays. -.sh 1 "COMPARISON WITH DELIVERMAIL" -.pp -.i Sendmail -is an outgrowth of -.i delivermail . -The primary differences are: -.np -Configuration information is not compiled in. -This change simplifies many of the problems -of moving to other machines. -It also allows easy debugging of new mailers. -.np -Address parsing is more flexible. -For example, -.i delivermail -only supported one gateway to any network, -whereas -.i sendmail -can be sensitive to host names -and reroute to different gateways. -.np -Forwarding and -:include: -features eliminate the requirement that the system alias file -be writable by any user -(or that an update program be written, -or that the system administration make all changes). -.np -.i Sendmail -supports message batching across networks -when a message is being sent to multiple recipients. -.np -A mail queue is provided in -.i sendmail. -Mail that cannot be delivered immediately -but can potentially be delivered later -is stored in this queue for a later retry. -The queue also provides a buffer against system crashes; -after the message has been collected -it may be reliably redelivered -even if the system crashes during the initial delivery. -.np -.i Sendmail -uses the networking support provided by 4.2BSD -to provide a direct interface networks such as the ARPANET -and/or Ethernet -using SMTP (the Simple Mail Transfer Protocol) -over a TCP/IP connection. -.+c -.ce -REFERENCES -.nr ii 1.5i -.ip [Crocker77] -Crocker, D. H., -Vittal, J. J., -Pogran, K. T., -and -Henderson, D. A. Jr., -.ul -Standard for the Format of ARPA Network Text Messages. -RFC 733, -NIC 41952. -In [Feinler78]. -November 1977. -.ip [Crocker82] -Crocker, D. H., -.ul -Standard for the Format of Arpa Internet Text Messages. -RFC 822. -Network Information Center, -SRI International, -Menlo Park, California. -August 1982. -.ip [Feinler78] -Feinler, E., -and -Postel, J. -(eds.), -.ul -ARPANET Protocol Handbook. -NIC 7104, -Network Information Center, -SRI International, -Menlo Park, California. -1978. -.ip [Nowitz78] -Nowitz, D. A., -and -Lesk, M. E., -.ul -A Dial-Up Network of UNIX Systems. -Bell Laboratories. -In -UNIX Programmer's Manual, Seventh Edition, -Volume 2. -August, 1978. -.ip [Schmidt79] -Schmidt, E., -.ul -An Introduction to the Berkeley Network. -University of California, Berkeley California. -1979. -.ip [Shoens79] -Shoens, K., -.ul -Mail Reference Manual. -University of California, Berkeley. -In UNIX Programmer's Manual, -Seventh Edition, -Volume 2C. -December 1979. -.ip [Solomon81] -Solomon, M., -Landweber, L., -and -Neuhengen, D., -.ul -The Design of the CSNET Name Server. -CS-DN-2. -University of Wisconsin, -Madison. -October 1981. -.ip [Su82a] -Su, Zaw-Sing, -and -Postel, Jon, -.ul -The Domain Naming Convention for Internet User Applications. -RFC819. -Network Information Center, -SRI International, -Menlo Park, California. -August 1982. -.ip [Su82b] -Su, Zaw-Sing, -.ul -A Distributed System for Internet Name Service. -RFC830. -Network Information Center, -SRI International, -Menlo Park, California. -October 1982. diff --git a/usr.sbin/sendmail/doc/usenix/usenix.me b/usr.sbin/sendmail/doc/usenix/usenix.me new file mode 100644 index 0000000000..30da01f3ca --- /dev/null +++ b/usr.sbin/sendmail/doc/usenix/usenix.me @@ -0,0 +1,1076 @@ +.nr si 3n +.he 'Mail Systems and Addressing in 4.2bsd''%' +.fo 'Version 8.1'USENIX \- Jan 83'Last Mod 6/7/93' +.if n .ls 2 +.+c +.(l C +.sz 14 +Mail Systems and Addressing +in 4.2bsd +.sz +.sp +Eric Allman\(dg +.sp 0.5 +.i +Britton-Lee, Inc. +1919 Addison Street, Suite 105. +Berkeley, California 94704. +.sp 0.5 +.r +eric@Berkeley.ARPA +ucbvax!eric +.)l +.sp +.(l F +.ce +ABSTRACT +.sp \n(psu +Routing mail through a heterogeneous internet presents many new +problems. +Among the worst of these is that of address mapping. +Historically, this has been handled on an ad hoc basis. +However, +this approach has become unmanageable as internets grow. +.sp \n(psu +Sendmail acts a unified +.q "post office" +to which all mail can be +submitted. +Address interpretation is controlled by a production +system, +which can parse both old and new format addresses. +The +new format is +.q "domain-based," +a flexible technique that can +handle many common situations. +Sendmail is not intended to perform +user interface functions. +.sp \n(psu +Sendmail will replace delivermail in the Berkeley 4.2 distribution. +Several major hosts are now or will soon be running sendmail. +This change will affect any users that route mail through a sendmail +gateway. +The changes that will be user visible are emphasized. +.)l +.sp 2 +.(f +\(dgA considerable part of this work +was done while under the employ +of the INGRES Project +at the University of California at Berkeley. +.)f +.pp +The mail system to appear in 4.2bsd +will contain a number of changes. +Most of these changes are based on the replacement of +.i delivermail +with a new module called +.i sendmail. +.i Sendmail +implements a general internetwork mail routing facility, +featuring aliasing and forwarding, +automatic routing to network gateways, +and flexible configuration. +Of key interest to the mail system user +will be the changes in the network addressing structure. +.pp +In a simple network, +each node has an address, +and resources can be identified +with a host-resource pair; +in particular, +the mail system can refer to users +using a host-username pair. +Host names and numbers have to be administered by a central authority, +but usernames can be assigned locally to each host. +.pp +In an internet, +multiple networks with different characteristics +and managements +must communicate. +In particular, +the syntax and semantics of resource identification change. +Certain special cases can be handled trivially +by +.i "ad hoc" +techniques, +such as +providing network names that appear local to hosts +on other networks, +as with the Ethernet at Xerox PARC. +However, the general case is extremely complex. +For example, +some networks require that the route the message takes +be explicitly specified by the sender, +simplifying the database update problem +since only adjacent hosts must be entered +into the system tables, +while others use logical addressing, +where the sender specifies the location of the recipient +but not how to get there. +Some networks use a left-associative syntax +and others use a right-associative syntax, +causing ambiguity in mixed addresses. +.pp +Internet standards seek to eliminate these problems. +Initially, these proposed expanding the address pairs +to address triples, +consisting of +{network, host, username} +triples. +Network numbers must be universally agreed upon, +and hosts can be assigned locally +on each network. +The user-level presentation was changed +to address domains, +comprised of a local resource identification +and a hierarchical domain specification +with a common static root. +The domain technique +separates the issue of physical versus logical addressing. +For example, +an address of the form +.q "eric@a.cc.berkeley.arpa" +describes the logical +organization of the address space +(user +.q eric +on host +.q a +in the Computer Center +at Berkeley) +but not the physical networks used +(for example, this could go over different networks +depending on whether +.q a +were on an ethernet +or a store-and-forward network). +.pp +.i Sendmail +is intended to help bridge the gap +between the totally +.i "ad hoc" +world +of networks that know nothing of each other +and the clean, tightly-coupled world +of unique network numbers. +It can accept old arbitrary address syntaxes, +resolving ambiguities using heuristics +specified by the system administrator, +as well as domain-based addressing. +It helps guide the conversion of message formats +between disparate networks. +In short, +.i sendmail +is designed to assist a graceful transition +to consistent internetwork addressing schemes. +.sp +.pp +Section 1 defines some of the terms +frequently left fuzzy +when working in mail systems. +Section 2 discusses the design goals for +.i sendmail . +In section 3, +the new address formats +and basic features of +.i sendmail +are described. +Section 4 discusses some of the special problems +of the UUCP network. +The differences between +.i sendmail +and +.i delivermail +are presented in section 5. +.sp +.(l F +.b DISCLAIMER: +A number of examples +in this paper +use names of actual people +and organizations. +This is not intended +to imply a commitment +or even an intellectual agreement +on the part of these people or organizations. +In particular, +Bell Telephone Laboratories (BTL), +Digital Equipment Corporation (DEC), +Lawrence Berkeley Laboratories (LBL), +Britton-Lee Incorporated (BLI), +and the University of California at Berkeley +are not committed to any of these proposals at this time. +Much of this paper +represents no more than +the personal opinions of the author. +.)l +.sh 1 "DEFINITIONS" +.pp +There are four basic concepts +that must be clearly distinguished +when dealing with mail systems: +the user (or the user's agent), +the user's identification, +the user's address, +and the route. +These are distinguished primarily by their position independence. +.sh 2 "User and Identification" +.pp +The user is the being +(a person or program) +that is creating or receiving a message. +An +.i agent +is an entity operating on behalf of the user \*- +such as a secretary who handles my mail. +or a program that automatically returns a +message such as +.q "I am at the UNICOM conference." +.pp +The identification is the tag +that goes along with the particular user. +This tag is completely independent of location. +For example, +my identification is the string +.q "Eric Allman," +and this identification does not change +whether I am located at U.C. Berkeley, +at Britton-Lee, +or at a scientific institute in Austria. +.pp +Since the identification is frequently ambiguous +(e.g., there are two +.q "Robert Henry" s +at Berkeley) +it is common to add other disambiguating information +that is not strictly part of the identification +(e.g., +Robert +.q "Code Generator" +Henry +versus +Robert +.q "System Administrator" +Henry). +.sh 2 "Address" +.pp +The address specifies a location. +As I move around, +my address changes. +For example, +my address might change from +.q eric@Berkeley.ARPA +to +.q eric@bli.UUCP +or +.q allman@IIASA.Austria +depending on my current affiliation. +.pp +However, +an address is independent of the location of anyone else. +That is, +my address remains the same to everyone who might be sending me mail. +For example, +a person at MIT and a person at USC +could both send to +.q eric@Berkeley.ARPA +and have it arrive to the same mailbox. +.pp +Ideally a +.q "white pages" +service would be provided to map user identifications +into addresses +(for example, see +[Solomon81]). +Currently this is handled by passing around +scraps of paper +or by calling people on the telephone +to find out their address. +.sh 2 "Route" +.pp +While an address specifies +.i where +to find a mailbox, +a route specifies +.i how +to find the mailbox. +Specifically, +it specifies a path +from sender to receiver. +As such, the route is potentially different +for every pair of people in the electronic universe. +.pp +Normally the route is hidden from the user +by the software. +However, +some networks put the burden of determining the route +onto the sender. +Although this simplifies the software, +it also greatly impairs the usability +for most users. +The UUCP network is an example of such a network. +.sh 1 "DESIGN GOALS" +.pp +Design goals for +.i sendmail \** +.(f +\**This section makes no distinction between +.i delivermail +and +.i sendmail. +.)f +include: +.np +Compatibility with the existing mail programs, +including Bell version 6 mail, +Bell version 7 mail, +Berkeley +.i Mail +[Shoens79], +BerkNet mail +[Schmidt79], +and hopefully UUCP mail +[Nowitz78]. +ARPANET mail +[Crocker82] +was also required. +.np +Reliability, in the sense of guaranteeing +that every message is correctly delivered +or at least brought to the attention of a human +for correct disposal; +no message should ever be completely lost. +This goal was considered essential +because of the emphasis on mail in our environment. +It has turned out to be one of the hardest goals to satisfy, +especially in the face of the many anomalous message formats +produced by various ARPANET sites. +For example, +certain sites generate improperly formated addresses, +occasionally +causing error-message loops. +Some hosts use blanks in names, +causing problems with +mail programs that assume that an address +is one word. +The semantics of some fields +are interpreted slightly differently +by different sites. +In summary, +the obscure features of the ARPANET mail protocol +really +.i are +used and +are difficult to support, +but must be supported. +.np +Existing software to do actual delivery +should be used whenever possible. +This goal derives as much from political and practical considerations +as technical. +.np +Easy expansion to +fairly complex environments, +including multiple +connections to a single network type +(such as with multiple UUCP or Ethernets). +This goal requires consideration of the contents of an address +as well as its syntax +in order to determine which gateway to use. +.np +Configuration information should not be compiled into the code. +A single compiled program should be able to run as is at any site +(barring such basic changes as the CPU type or the operating system). +We have found this seemingly unimportant goal +to be critical in real life. +Besides the simple problems that occur when any program gets recompiled +in a different environment, +many sites like to +.q fiddle +with anything that they will be recompiling anyway. +.np +.i Sendmail +must be able to let various groups maintain their own mailing lists, +and let individuals specify their own forwarding, +without modifying the system alias file. +.np +Each user should be able to specify which mailer to execute +to process mail being delivered for him. +This feature allows users who are using specialized mailers +that use a different format to build their environment +without changing the system, +and facilitates specialized functions +(such as returning an +.q "I am on vacation" +message). +.np +Network traffic should be minimized +by batching addresses to a single host where possible, +without assistance from the user. +.pp +These goals motivated the architecture illustrated in figure 1. +.(z +.hl +.ie t \ +. sp 18 +.el \{\ +.(c ++---------+ +---------+ +---------+ +| sender1 | | sender2 | | sender3 | ++---------+ +---------+ +---------+ + | | | + +----------+ + +----------+ + | | | + v v v + +-------------+ + | sendmail | + +-------------+ + | | | + +----------+ + +----------+ + | | | + v v v ++---------+ +---------+ +---------+ +| mailer1 | | mailer2 | | mailer3 | ++---------+ +---------+ +---------+ +.)c +.\} + +.ce +Figure 1 \*- Sendmail System Structure. +.hl +.)z +The user interacts with a mail generating and sending program. +When the mail is created, +the generator calls +.i sendmail , +which routes the message to the correct mailer(s). +Since some of the senders may be network servers +and some of the mailers may be network clients, +.i sendmail +may be used as an internet mail gateway. +.sh 1 "USAGE" +.sh 2 "Address Formats" +.pp +Arguments may be flags or addresses. +Flags set various processing options. +Following flag arguments, +address arguments may be given. +Addresses follow the syntax in RFC822 +[Crocker82] +for ARPANET +address formats. +In brief, the format is: +.np +Anything in parentheses is thrown away +(as a comment). +.np +Anything in angle brackets (\c +.q "<\|>" ) +is preferred +over anything else. +This rule implements the ARPANET standard that addresses of the form +.(b +user name <machine-address> +.)b +will send to the electronic +.q machine-address +rather than the human +.q "user name." +.np +Double quotes +(\ "\ ) +quote phrases; +backslashes quote characters. +Backslashes are more powerful +in that they will cause otherwise equivalent phrases +to compare differently \*- for example, +.i user +and +.i +"user" +.r +are equivalent, +but +.i \euser +is different from either of them. +This might be used +to avoid normal aliasing +or duplicate suppression algorithms. +.pp +Parentheses, angle brackets, and double quotes +must be properly balanced and nested. +The rewriting rules control remaining parsing\**. +.(f +\**Disclaimer: Some special processing is done +after rewriting local names; see below. +.)f +.pp +Although old style addresses are still accepted +in most cases, +the preferred address format +is based on ARPANET-style domain-based addresses +[Su82a]. +These addresses are based on a hierarchical, logical decomposition +of the address space. +The addresses are hierarchical in a sense +similar to the U.S. postal addresses: +the messages may first be routed to the correct state, +with no initial consideration of the city +or other addressing details. +The addresses are logical +in that each step in the hierarchy +corresponds to a set of +.q "naming authorities" +rather than a physical network. +.pp +For example, +the address: +.(l +eric@HostA.BigSite.ARPA +.)l +would first look up the domain +BigSite +in the namespace administrated by +ARPA. +A query could then be sent to +BigSite +for interpretation of +HostA. +Eventually the mail would arrive at +HostA, +which would then do final delivery +to user +.q eric. +.sh 2 "Mail to Files and Programs" +.pp +Files and programs are legitimate message recipients. +Files provide archival storage of messages, +useful for project administration and history. +Programs are useful as recipients in a variety of situations, +for example, +to maintain a public repository of systems messages +(such as the Berkeley +.i msgs +program). +.pp +Any address passing through the initial parsing algorithm +as a local address +(i.e, not appearing to be a valid address for another mailer) +is scanned for two special cases. +If prefixed by a vertical bar (\c +.q \^|\^ ) +the rest of the address is processed as a shell command. +If the user name begins with a slash mark (\c +.q /\^ ) +the name is used as a file name, +instead of a login name. +.sh 2 "Aliasing, Forwarding, Inclusion" +.pp +.i Sendmail +reroutes mail three ways. +Aliasing applies system wide. +Forwarding allows each user to reroute incoming mail +destined for that account. +Inclusion directs +.i sendmail +to read a file for a list of addresses, +and is normally used +in conjunction with aliasing. +.sh 3 "Aliasing" +.pp +Aliasing maps local addresses to address lists using a system-wide file. +This file is hashed to speed access. +Only addresses that parse as local +are allowed as aliases; +this guarantees a unique key +(since there are no nicknames for the local host). +.sh 3 "Forwarding" +.pp +After aliasing, +if an recipient address specifies a local user +.i sendmail +searches for a +.q .forward +file in the recipient's home directory. +If it exists, +the message is +.i not +sent to that user, +but rather to the list of addresses in that file. +Often +this list will contain only one address, +and the feature will be used for network mail forwarding. +.pp +Forwarding also permits a user to specify a private incoming mailer. +For example, +forwarding to: +.(b +"\^|\|/usr/local/newmail myname" +.)b +will use a different incoming mailer. +.sh 3 "Inclusion" +.pp +Inclusion is specified in RFC 733 [Crocker77] syntax: +.(b +:Include: pathname +.)b +An address of this form reads the file specified by +.i pathname +and sends to all users listed in that file. +.pp +The intent is +.i not +to support direct use of this feature, +but rather to use this as a subset of aliasing. +For example, +an alias of the form: +.(b +project: :include:/usr/project/userlist +.)b +is a method of letting a project maintain a mailing list +without interaction with the system administration, +even if the alias file is protected. +.pp +It is not necessary to rebuild the index on the alias database +when a :include: list is changed. +.sh 2 "Message Collection" +.pp +Once all recipient addresses are parsed and verified, +the message is collected. +The message comes in two parts: +a message header and a message body, +separated by a blank line. +The body is an uninterpreted +sequence of text lines. +.pp +The header is formated as a series of lines +of the form +.(b + field-name: field-value +.)b +Field-value can be split across lines by starting the following +lines with a space or a tab. +Some header fields have special internal meaning, +and have appropriate special processing. +Other headers are simply passed through. +Some header fields may be added automatically, +such as time stamps. +.sh 1 "THE UUCP PROBLEM" +.pp +Of particular interest +is the UUCP network. +The explicit routing +used in the UUCP environment +causes a number of serious problems. +First, +giving out an address +is impossible +without knowing the address of your potential correspondent. +This is typically handled +by specifying the address +relative to some +.q "well-known" +host +(e.g., +ucbvax or decvax). +Second, +it is often difficult to compute +the set of addresses +to reply to +without some knowledge +of the topology of the network. +Although it may be easy for a human being +to do this +under many circumstances, +a program does not have equally sophisticated heuristics +built in. +Third, +certain addresses will become painfully and unnecessarily long, +as when a message is routed through many hosts in the USENET. +And finally, +certain +.q "mixed domain" +addresses +are impossible to parse unambiguously \*- +e.g., +.(l +decvax!ucbvax!lbl-h!user@LBL-CSAM +.)l +might have many possible resolutions, +depending on whether the message was first routed +to decvax +or to LBL-CSAM. +.pp +To solve this problem, +the UUCP syntax +would have to be changed to use addresses +rather than routes. +For example, +the address +.q decvax!ucbvax!eric +might be expressed as +.q eric@ucbvax.UUCP +(with the hop through decvax implied). +This address would itself be a domain-based address; +for example, +an address might be of the form: +.(l +mark@d.cbosg.btl.UUCP +.)l +Hosts outside of Bell Telephone Laboratories +would then only need to know +how to get to a designated BTL relay, +and the BTL topology +would only be maintained inside Bell. +.pp +There are three major problems +associated with turning UUCP addresses +into something reasonable: +defining the namespace, +creating and propagating the necessary software, +and building and maintaining the database. +.sh 2 "Defining the Namespace" +.pp +Putting all UUCP hosts into a flat namespace +(e.g., +.q \&...@host.UUCP ) +is not practical for a number of reasons. +First, +with over 1600 sites already, +and (with the increasing availability of inexpensive microcomputers +and autodialers) +several thousand more coming within a few years, +the database update problem +is simply intractable +if the namespace is flat. +Second, +there are almost certainly name conflicts today. +Third, +as the number of sites grow +the names become ever less mnemonic. +.pp +It seems inevitable +that there be some sort of naming authority +for the set of top level names +in the UUCP domain, +as unpleasant a possibility +as that may seem. +It will simply not be possible +to have one host resolving all names. +It may however be possible +to handle this +in a fashion similar to that of assigning names of newsgroups +in USENET. +However, +it will be essential to encourage everyone +to become subdomains of an existing domain +whenever possible \*- +even though this will certainly bruise some egos. +For example, +if a new host named +.q blid +were to be added to the UUCP network, +it would probably actually be addressed as +.q d.bli.UUCP +(i.e., +as host +.q d +in the pseudo-domain +.q bli +rather than as host +.q blid +in the UUCP domain). +.sh 2 "Creating and Propagating the Software" +.pp +The software required to implement a consistent namespace +is relatively trivial. +Two modules are needed, +one to handle incoming mail +and one to handle outgoing mail. +.pp +The incoming module +must be prepared to handle either old or new style addresses. +New-style addresses +can be passed through unchanged. +Old style addresses +must be turned into new style addresses +where possible. +.pp +The outgoing module +is slightly trickier. +It must do a database lookup on the recipient addresses +(passed on the command line) +to determine what hosts to send the message to. +If those hosts do not accept new-style addresses, +it must transform all addresses in the header of the message +into old style using the database lookup. +.pp +Both of these modules +are straightforward +except for the issue of modifying the header. +It seems prudent to choose one format +for the message headers. +For a number of reasons, +Berkeley has elected to use the ARPANET protocols +for message formats. +However, +this protocol is somewhat difficult to parse. +.pp +Propagation is somewhat more difficult. +There are a large number of hosts +connected to UUCP +that will want to run completely standard systems +(for very good reasons). +The strategy is not to convert the entire network \*- +only enough of it it alleviate the problem. +.sh 2 "Building and Maintaining the Database" +.pp +This is by far the most difficult problem. +A prototype for this database +already exists, +but it is maintained by hand +and does not pretend to be complete. +.pp +This problem will be reduced considerably +if people choose to group their hosts +into subdomains. +This would require a global update +only when a new top level domain +joined the network. +A message to a host in a subdomain +could simply be routed to a known domain gateway +for further processing. +For example, +the address +.q eric@a.bli.UUCP +might be routed to the +.q bli +gateway +for redistribution; +new hosts could be added +within BLI +without notifying the rest of the world. +Of course, +other hosts +.i could +be notified as an efficiency measure. +.pp +There may be more than one domain gateway. +A domain such as BTL, +for instance, +might have a dozen gateways to the outside world; +a non-BTL site +could choose the closest gateway. +The only restriction +would be that all gateways +maintain a consistent view of the domain +they represent. +.sh 2 "Logical Structure" +.pp +Logically, +domains are organized into a tree. +There need not be a host actually associated +with each level in the tree \*- +for example, +there will be no host associated with the name +.q UUCP. +Similarly, +an organization might group names together for administrative reasons; +for example, +the name +.(l +CAD.research.BigCorp.UUCP +.)l +might not actually have a host representing +.q research. +.pp +However, +it may frequently be convenient to have a host +or hosts +that +.q represent +a domain. +For example, +if a single host exists that +represents +Berkeley, +then mail from outside Berkeley +can forward mail to that host +for further resolution +without knowing Berkeley's +(rather volatile) +topology. +This is not unlike the operation +of the telephone network. +.pp +This may also be useful +inside certain large domains. +For example, +at Berkeley it may be presumed +that most hosts know about other hosts +inside the Berkeley domain. +But if they process an address +that is unknown, +they can pass it +.q upstairs +for further examination. +Thus as new hosts are added +only one host +(the domain master) +.i must +be updated immediately; +other hosts can be updated as convenient. +.pp +Ideally this name resolution process +would be performed by a name server +(e.g., [Su82b]) +to avoid unnecessary copying +of the message. +However, +in a batch network +such as UUCP +this could result in unnecessary delays. +.sh 1 "COMPARISON WITH DELIVERMAIL" +.pp +.i Sendmail +is an outgrowth of +.i delivermail . +The primary differences are: +.np +Configuration information is not compiled in. +This change simplifies many of the problems +of moving to other machines. +It also allows easy debugging of new mailers. +.np +Address parsing is more flexible. +For example, +.i delivermail +only supported one gateway to any network, +whereas +.i sendmail +can be sensitive to host names +and reroute to different gateways. +.np +Forwarding and +:include: +features eliminate the requirement that the system alias file +be writable by any user +(or that an update program be written, +or that the system administration make all changes). +.np +.i Sendmail +supports message batching across networks +when a message is being sent to multiple recipients. +.np +A mail queue is provided in +.i sendmail. +Mail that cannot be delivered immediately +but can potentially be delivered later +is stored in this queue for a later retry. +The queue also provides a buffer against system crashes; +after the message has been collected +it may be reliably redelivered +even if the system crashes during the initial delivery. +.np +.i Sendmail +uses the networking support provided by 4.2BSD +to provide a direct interface networks such as the ARPANET +and/or Ethernet +using SMTP (the Simple Mail Transfer Protocol) +over a TCP/IP connection. +.+c +.ce +REFERENCES +.nr ii 1.5i +.ip [Crocker77] +Crocker, D. H., +Vittal, J. J., +Pogran, K. T., +and +Henderson, D. A. Jr., +.ul +Standard for the Format of ARPA Network Text Messages. +RFC 733, +NIC 41952. +In [Feinler78]. +November 1977. +.ip [Crocker82] +Crocker, D. H., +.ul +Standard for the Format of Arpa Internet Text Messages. +RFC 822. +Network Information Center, +SRI International, +Menlo Park, California. +August 1982. +.ip [Feinler78] +Feinler, E., +and +Postel, J. +(eds.), +.ul +ARPANET Protocol Handbook. +NIC 7104, +Network Information Center, +SRI International, +Menlo Park, California. +1978. +.ip [Nowitz78] +Nowitz, D. A., +and +Lesk, M. E., +.ul +A Dial-Up Network of UNIX Systems. +Bell Laboratories. +In +UNIX Programmer's Manual, Seventh Edition, +Volume 2. +August, 1978. +.ip [Schmidt79] +Schmidt, E., +.ul +An Introduction to the Berkeley Network. +University of California, Berkeley California. +1979. +.ip [Shoens79] +Shoens, K., +.ul +Mail Reference Manual. +University of California, Berkeley. +In UNIX Programmer's Manual, +Seventh Edition, +Volume 2C. +December 1979. +.ip [Solomon81] +Solomon, M., +Landweber, L., +and +Neuhengen, D., +.ul +The Design of the CSNET Name Server. +CS-DN-2. +University of Wisconsin, +Madison. +October 1981. +.ip [Su82a] +Su, Zaw-Sing, +and +Postel, Jon, +.ul +The Domain Naming Convention for Internet User Applications. +RFC819. +Network Information Center, +SRI International, +Menlo Park, California. +August 1982. +.ip [Su82b] +Su, Zaw-Sing, +.ul +A Distributed System for Internet Name Service. +RFC830. +Network Information Center, +SRI International, +Menlo Park, California. +October 1982. diff --git a/usr.sbin/sendmail/doc/usenix/usenix.ps b/usr.sbin/sendmail/doc/usenix/usenix.ps new file mode 100644 index 0000000000..10bd887164 --- /dev/null +++ b/usr.sbin/sendmail/doc/usenix/usenix.ps @@ -0,0 +1,1004 @@ +%!PS-Adobe-3.0 +%%Creator: groff version 1.08 +%%DocumentNeededResources: font Times-Roman +%%+ font Times-Italic +%%+ font Times-Bold +%%DocumentSuppliedResources: procset grops 1.08 0 +%%Pages: 9 +%%PageOrder: Ascend +%%Orientation: Portrait +%%EndComments +%%BeginProlog +%%BeginResource: procset grops 1.08 0 +/setpacking where{ +pop +currentpacking +true setpacking +}if +/grops 120 dict dup begin +/SC 32 def +/A/show load def +/B{0 SC 3 -1 roll widthshow}bind def +/C{0 exch ashow}bind def +/D{0 exch 0 SC 5 2 roll awidthshow}bind def +/E{0 rmoveto show}bind def +/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def +/G{0 rmoveto 0 exch ashow}bind def +/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/I{0 exch rmoveto show}bind def +/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def +/K{0 exch rmoveto 0 exch ashow}bind def +/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/M{rmoveto show}bind def +/N{rmoveto 0 SC 3 -1 roll widthshow}bind def +/O{rmoveto 0 exch ashow}bind def +/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/Q{moveto show}bind def +/R{moveto 0 SC 3 -1 roll widthshow}bind def +/S{moveto 0 exch ashow}bind def +/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/SF{ +findfont exch +[exch dup 0 exch 0 exch neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/MF{ +findfont +[5 2 roll +0 3 1 roll +neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/level0 0 def +/RES 0 def +/PL 0 def +/LS 0 def +/PLG{ +gsave newpath clippath pathbbox grestore +exch pop add exch pop +}bind def +/BP{ +/level0 save def +1 setlinecap +1 setlinejoin +72 RES div dup scale +LS{ +90 rotate +}{ +0 PL translate +}ifelse +1 -1 scale +}bind def +/EP{ +level0 restore +showpage +}bind def +/DA{ +newpath arcn stroke +}bind def +/SN{ +transform +.25 sub exch .25 sub exch +round .25 add exch round .25 add exch +itransform +}bind def +/DL{ +SN +moveto +SN +lineto stroke +}bind def +/DC{ +newpath 0 360 arc closepath +}bind def +/TM matrix def +/DE{ +TM currentmatrix pop +translate scale newpath 0 0 .5 0 360 arc closepath +TM setmatrix +}bind def +/RC/rcurveto load def +/RL/rlineto load def +/ST/stroke load def +/MT/moveto load def +/CL/closepath load def +/FL{ +currentgray exch setgray fill setgray +}bind def +/BL/fill load def +/LW/setlinewidth load def +/RE{ +findfont +dup maxlength 1 index/FontName known not{1 add}if dict begin +{ +1 index/FID ne{def}{pop pop}ifelse +}forall +/Encoding exch def +dup/FontName exch def +currentdict end definefont pop +}bind def +/DEFS 0 def +/EBEGIN{ +moveto +DEFS begin +}bind def +/EEND/end load def +/CNT 0 def +/level1 0 def +/PBEGIN{ +/level1 save def +translate +div 3 1 roll div exch scale +neg exch neg exch translate +0 setgray +0 setlinecap +1 setlinewidth +0 setlinejoin +10 setmiterlimit +[]0 setdash +/setstrokeadjust where{ +pop +false setstrokeadjust +}if +/setoverprint where{ +pop +false setoverprint +}if +newpath +/CNT countdictstack def +userdict begin +/showpage{}def +}bind def +/PEND{ +clear +countdictstack CNT sub{end}repeat +level1 restore +}bind def +end def +/setpacking where{ +pop +setpacking +}if +%%EndResource +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Italic +%%IncludeResource: font Times-Bold +grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL +792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron +/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space +/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft +/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four +/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/A/B/C +/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash +/bracketright/circumflex/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q +/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/tilde/.notdef/quotesinglbase +/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger +/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut +/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash +/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar +/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus +/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu +/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright +/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde +/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute +/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis +/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls +/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute +/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve +/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex +/udieresis/yacute/thorn/ydieresis]def/Times-Bold@0 ENC0/Times-Bold RE +/Times-Italic@0 ENC0/Times-Italic RE/Times-Roman@0 ENC0/Times-Roman RE +%%EndProlog +%%Page: 1 1 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 14/Times-Roman@0 SF(Mail Systems and Addressing)204.196 141 Q(in 4.2bsd) +262.331 157.8 Q/F1 10/Times-Roman@0 SF(Eric Allman\207)260.92 181.8 Q/F2 10 +/Times-Italic@0 SF(Britton-Lee)254.86 199.8 Q 2.5(,I)-.1 G(nc.)309.2 199.8 Q +(1919 Addison Str)225.13 211.8 Q(eet, Suite 105.)-.37 E(Berk)232.645 223.8 Q +(ele)-.1 E 1.1 -.55(y, C)-.3 H(alifornia 94704.).55 E F1(eric@Berk)244.175 +241.8 Q(ele)-.1 E -.65(y.)-.15 G(ARP).65 E(A)-.92 E(ucb)264.6 253.8 Q -.25(va) +-.15 G(x!eric).25 E(ABSTRA)262.085 286.2 Q(CT)-.4 E .966 +(Routing mail through a heterogeneous internet presents man)112 302.4 R 3.466 +(yn)-.15 G 1.466 -.25(ew p)373.438 302.4 T 3.466(roblems. Among).25 F .297 +(the w)112 314.4 R .297(orst of these is that of address mapping.)-.1 F +(Historically)5.297 E 2.797(,t)-.65 G .298(his has been handled on an)355.03 +314.4 R(ad hoc basis.)112 326.4 Q(Ho)5 E(we)-.25 E -.15(ve)-.25 G .8 -.4(r, t) +.15 H(his approach has become unmanageable as internets gro).4 E -.65(w.)-.25 G +.099(Sendmail acts a uni\214ed \231post of)112 342.6 R .098 +(\214ce\232 to which all mail can be submitted.)-.25 F .098(Address inter)5.098 +F(-)-.2 E .754(pretation is controlled by a production system, which can parse\ + both old and ne)112 354.6 R 3.255(wf)-.25 G(or)452.54 354.6 Q(-)-.2 E .242 +(mat addresses.)112 366.6 R .242(The ne)5.242 F 2.742(wf)-.25 G .242 +(ormat is \231domain-based,)216.578 366.6 R 2.742<9a618d>-.7 G -.15(ex)334.326 +366.6 S .241(ible technique that can handle).15 F(man)112 378.6 Q 2.606(yc)-.15 +G .106(ommon situations.)141.116 378.6 R .106 +(Sendmail is not intended to perform user interf)5.106 F .107(ace functions.) +-.1 F .399(Sendmail will replace deli)112 394.8 R -.15(ve)-.25 G .399 +(rmail in the Berk).15 F(ele)-.1 E 2.899(y4)-.15 G .399(.2 distrib)320.504 +394.8 R 2.899(ution. Se)-.2 F -.15(ve)-.25 G .399(ral major hosts).15 F .421 +(are no)112 406.8 R 2.921(wo)-.25 G 2.921(rw)152.022 406.8 S .421 +(ill soon be running sendmail.)165.493 406.8 R .421(This change will af)5.421 F +.422(fect an)-.25 F 2.922(yu)-.15 G .422(sers that route)407.056 406.8 R 1.5 +(mail through a sendmail g)112 418.8 R(ate)-.05 E -.1(wa)-.25 G 5.3 -.65(y. T) +.1 H 1.5(he changes that will be user visible are empha-).65 F(sized.)112 430.8 +Q .906(The mail system to appear in 4.2bsd will contain a number of changes.)97 +475.2 R .906(Most of these changes are)5.906 F .469 +(based on the replacement of)72 487.2 R F2(delivermail)2.969 E F1 .469 +(with a ne)2.969 F 2.969(wm)-.25 G .469(odule called)292.871 487.2 R F2 2.97 +(sendmail. Sendmail)2.97 F F1 .47(implements a gen-)2.97 F 1.834 +(eral internetw)72 499.2 R 1.834(ork mail routing f)-.1 F(acility)-.1 E 4.333 +(,f)-.65 G 1.833(eaturing aliasing and forw)239.739 499.2 R 1.833 +(arding, automatic routing to netw)-.1 F(ork)-.1 E -.05(ga)72 511.2 S(te).05 E +-.1(wa)-.25 G .205(ys, and \215e).1 F .205(xible con\214guration.)-.15 F .205 +(Of k)5.205 F .505 -.15(ey i)-.1 H .205 +(nterest to the mail system user will be the changes in the net-).15 F -.1(wo) +72 523.2 S(rk addressing structure.).1 E .624(In a simple netw)97 539.4 R .624 +(ork, each node has an address, and resources can be identi\214ed with a host-\ +resource)-.1 F .374(pair; in particular)72 551.4 R 2.874(,t)-.4 G .374 +(he mail system can refer to users using a host-username pair)149.932 551.4 R +5.374(.H)-.55 G .375(ost names and numbers)409.276 551.4 R(ha)72 563.4 Q .3 +-.15(ve t)-.2 H 2.5(ob).15 G 2.5(ea)108.31 563.4 S +(dministered by a central authority)119.69 563.4 Q 2.5(,b)-.65 G +(ut usernames can be assigned locally to each host.)263.82 563.4 Q .397 +(In an internet, multiple netw)97 579.6 R .396(orks with dif)-.1 F .396 +(ferent characteristics and managements must communicate.)-.25 F .389 +(In particular)72 591.6 R 2.889(,t)-.4 G .389 +(he syntax and semantics of resource identi\214cation change.)129.308 591.6 R +.39(Certain special cases can be han-)5.389 F 1.033(dled tri)72 603.6 R 1.033 +(vially by)-.25 F F2 1.033(ad hoc)3.533 F F1 1.032(techniques, such as pro) +3.533 F 1.032(viding netw)-.15 F 1.032 +(ork names that appear local to hosts on other)-.1 F(netw)72 615.6 Q 1.621 +(orks, as with the Ethernet at Xerox P)-.1 F 4.121(ARC. Ho)-.92 F(we)-.25 E +-.15(ve)-.25 G 2.421 -.4(r, t).15 H 1.622(he general case is e).4 F 1.622 +(xtremely comple)-.15 F 4.122(x. F)-.15 F(or)-.15 E -.15(ex)72 627.6 S .29 +(ample, some netw).15 F .29(orks require that the route the message tak)-.1 F +.29(es be e)-.1 F .29(xplicitly speci\214ed by the sender)-.15 F 2.79(,s)-.4 G +(im-)490.11 627.6 Q 1.618(plifying the database update problem since only adja\ +cent hosts must be entered into the system tables,)72 639.6 R .573(while other\ +s use logical addressing, where the sender speci\214es the location of the rec\ +ipient b)72 651.6 R .573(ut not ho)-.2 F 3.072(wt)-.25 G(o)499 651.6 Q 1.065 +(get there.)72 663.6 R 1.065(Some netw)6.065 F 1.066(orks use a left-associati) +-.1 F 1.366 -.15(ve s)-.25 H 1.066(yntax and others use a right-associati).15 F +1.366 -.15(ve s)-.25 H 1.066(yntax, causing).15 F .32 LW 76 673.2 72 673.2 DL +80 673.2 76 673.2 DL 84 673.2 80 673.2 DL 88 673.2 84 673.2 DL 92 673.2 88 +673.2 DL 96 673.2 92 673.2 DL 100 673.2 96 673.2 DL 104 673.2 100 673.2 DL 108 +673.2 104 673.2 DL 112 673.2 108 673.2 DL 116 673.2 112 673.2 DL 120 673.2 116 +673.2 DL 124 673.2 120 673.2 DL 128 673.2 124 673.2 DL 132 673.2 128 673.2 DL +136 673.2 132 673.2 DL 140 673.2 136 673.2 DL 144 673.2 140 673.2 DL 148 673.2 +144 673.2 DL 152 673.2 148 673.2 DL 156 673.2 152 673.2 DL 160 673.2 156 673.2 +DL 164 673.2 160 673.2 DL 168 673.2 164 673.2 DL 172 673.2 168 673.2 DL 176 +673.2 172 673.2 DL 180 673.2 176 673.2 DL 184 673.2 180 673.2 DL 188 673.2 184 +673.2 DL 192 673.2 188 673.2 DL 196 673.2 192 673.2 DL 200 673.2 196 673.2 DL +204 673.2 200 673.2 DL 208 673.2 204 673.2 DL 212 673.2 208 673.2 DL 216 673.2 +212 673.2 DL/F3 8/Times-Roman@0 SF .556(\207A considerable part of this w)93.6 +685.2 R .556(ork w)-.08 F .556(as done while under the emplo)-.08 F 2.557(yo) +-.08 G 2.557(ft)323.107 685.2 S .557(he INGRES Project at the Uni)330.552 685.2 +R -.12(ve)-.2 G .557(rsity of California at).12 F(Berk)72 694.8 Q(ele)-.08 E +-.52(y.)-.12 G/F4 10/Times-Bold@0 SF(Mail Systems and Addr)72 756 Q +(essing in 4.2bsd)-.18 E(1)499 756 Q EP +%%Page: 2 2 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Mail Systems and Addr)72 60 Q(essing in 4.2bsd)-.18 E(2) +499 60 Q/F1 10/Times-Roman@0 SF(ambiguity in mix)72 96 Q(ed addresses.)-.15 E +.679(Internet standards seek to eliminate these problems.)97 112.2 R(Initially) +5.678 E 3.178(,t)-.65 G .678(hese proposed e)353.138 112.2 R .678 +(xpanding the address)-.15 F .331 +(pairs to address triples, consisting of {netw)72 124.2 R .331 +(ork, host, username} triples.)-.1 F(Netw)5.332 E .332(ork numbers must be uni) +-.1 F -.15(ve)-.25 G -.2(r-).15 G 1.452 +(sally agreed upon, and hosts can be assigned locally on each netw)72 136.2 R +3.952(ork. The)-.1 F(user)3.952 E(-le)-.2 E -.15(ve)-.25 G 3.952(lp).15 G 1.452 +(resentation w)440.718 136.2 R(as)-.1 E .249(changed to address domains, compr\ +ised of a local resource identi\214cation and a hierarchical domain speci\214-) +72 148.2 R 1.54(cation with a common static root.)72 160.2 R 1.539 +(The domain technique separates the issue of ph)6.539 F 1.539(ysical v)-.05 F +1.539(ersus logical)-.15 F 3.001(addressing. F)72 172.2 R .501(or e)-.15 F .502 +(xample, an address of the form \231eric@a.cc.berk)-.15 F(ele)-.1 E -.65(y.) +-.15 G .502(arpa\232 describes the logical or).65 F -.05(ga)-.18 G(niza-).05 E +.443(tion of the address space \(user \231eric\232 on host \231a\232 in the Co\ +mputer Center at Berk)72 184.2 R(ele)-.1 E .443(y\) b)-.15 F .443 +(ut not the ph)-.2 F(ysical)-.05 E(netw)72 196.2 Q .934(orks used \(for e)-.1 F +.934(xample, this could go o)-.15 F -.15(ve)-.15 G 3.434(rd).15 G(if)274.722 +196.2 Q .934(ferent netw)-.25 F .935 +(orks depending on whether \231a\232 were on an)-.1 F +(ethernet or a store-and-forw)72 208.2 Q(ard netw)-.1 E(ork\).)-.1 E/F2 10 +/Times-Italic@0 SF(Sendmail)97 224.4 Q F1 .493 +(is intended to help bridge the g)2.993 F .493(ap between the totally)-.05 F F2 +.493(ad hoc)2.993 F F1 -.1(wo)2.993 G .493(rld of netw).1 F .493(orks that kno) +-.1 F(w)-.25 E .854(nothing of each other and the clean, tightly-coupled w)72 +236.4 R .854(orld of unique netw)-.1 F .855(ork numbers.)-.1 F .855 +(It can accept old)5.855 F .633(arbitrary address syntax)72 248.4 R .632(es, r\ +esolving ambiguities using heuristics speci\214ed by the system administrator) +-.15 F 3.132(,a)-.4 G(s)500.11 248.4 Q .347(well as domain-based addressing.)72 +260.4 R .347(It helps guide the con)5.347 F -.15(ve)-.4 G .347 +(rsion of message formats between disparate net-).15 F -.1(wo)72 272.4 S 3.395 +(rks. In).1 F(short,)3.395 E F2(sendmail)3.395 E F1 .894 +(is designed to assist a graceful transition to consistent internetw)3.395 F +.894(ork addressing)-.1 F(schemes.)72 284.4 Q .689 +(Section 1 de\214nes some of the terms frequently left fuzzy when w)97 312.6 R +.69(orking in mail systems.)-.1 F .69(Section 2)5.69 F .595 +(discusses the design goals for)72 324.6 R F2(sendmail)3.095 E F1 5.595(.I)C +3.095(ns)243.33 324.6 S .595(ection 3, the ne)255.315 324.6 R 3.095(wa)-.25 G +.594(ddress formats and basic features of)332.705 324.6 R F2(send-)3.094 E +(mail)72 336.6 Q F1 .893(are described.)3.393 F .893 +(Section 4 discusses some of the special problems of the UUCP netw)5.893 F +3.394(ork. The)-.1 F(dif)3.394 E(fer)-.25 E(-)-.2 E(ences between)72 348.6 Q F2 +(sendmail)2.5 E F1(and)2.5 E F2(delivermail)2.5 E F1 +(are presented in section 5.)2.5 E F0(DISCLAIMER:)112 376.8 Q F1 3.333(An)3.333 +G .833(umber of e)199.216 376.8 R .832 +(xamples in this paper use names of actual people and)-.15 F(or)112 388.8 Q +-.05(ga)-.18 G 4.572(nizations. This).05 F 2.072 +(is not intended to imply a commitment or e)4.572 F -.15(ve)-.25 G 4.573(na).15 +G 4.573(ni)409.987 388.8 S(ntellectual)422.34 388.8 Q 1.094 +(agreement on the part of these people or or)112 400.8 R -.05(ga)-.18 G 3.594 +(nizations. In).05 F(particular)3.594 E 3.594(,B)-.4 G 1.094(ell T)408.896 +400.8 R(elephone)-.7 E .656 +(Laboratories \(BTL\), Digital Equipment Corporation \(DEC\), La)112 412.8 R +.657(wrence Berk)-.15 F(ele)-.1 E 3.157(yL)-.15 G(abo-)446.23 412.8 Q 2.136 +(ratories \(LBL\), Britton-Lee Incorporated \(BLI\), and the Uni)112 424.8 R +-.15(ve)-.25 G 2.136(rsity of California at).15 F(Berk)112 436.8 Q(ele)-.1 E +3.088(ya)-.15 G .588(re not committed to an)155.378 436.8 R 3.089(yo)-.15 G +3.089(ft)261.219 436.8 S .589(hese proposals at this time.)270.418 436.8 R .589 +(Much of this paper)5.589 F +(represents no more than the personal opinions of the author)112 448.8 Q(.)-.55 +E F0 2.5(1. DEFINITIONS)72 477 R F1 .266(There are four basic concepts that mu\ +st be clearly distinguished when dealing with mail systems:)112 493.2 R .514 +(the user \(or the user')87 505.2 R 3.014(sa)-.55 G .515(gent\), the user') +182.6 505.2 R 3.015(si)-.55 G .515(denti\214cation, the user')253.025 505.2 R +3.015(sa)-.55 G .515(ddress, and the route.)354.56 505.2 R .515(These are dis-) +5.515 F(tinguished primarily by their position independence.)87 517.2 Q F0 2.5 +(1.1. User)87 541.2 R(and Identi\214cation)2.5 E F1 .264 +(The user is the being \(a person or program\) that is creating or recei)127 +557.4 R .263(ving a message.)-.25 F(An)5.263 E F2 -.1(age)2.763 G(nt).1 E F1 +.659(is an entity operating on behalf of the user \212 such as a secretary who\ + handles my mail.)102 569.4 R .66(or a pro-)5.66 F(gram that automatically ret\ +urns a message such as \231I am at the UNICOM conference.)102 581.4 Q<9a>-.7 E +.931(The identi\214cation is the tag that goes along with the particular user) +127 597.6 R 5.931(.T)-.55 G .931(his tag is completely)418.707 597.6 R .216 +(independent of location.)102 609.6 R -.15(Fo)5.216 G 2.716(re).15 G .216 +(xample, my identi\214cation is the string \231Eric Allman,)225.324 609.6 R +2.717<9a61>-.7 G .217(nd this identi-)448.006 609.6 R 1.228 +(\214cation does not change whether I am located at U.C. Berk)102 621.6 R(ele) +-.1 E 2.527 -.65(y, a)-.15 H 3.727(tB).65 G 1.227 +(ritton-Lee, or at a scienti\214c)390.502 621.6 R(institute in Austria.)102 +633.6 Q 2.379 +(Since the identi\214cation is frequently ambiguous \(e.g., there are tw)127 +649.8 R 4.879<6f99>-.1 G 2.38(Robert Henry\232s at)426.48 649.8 R(Berk)102 +661.8 Q(ele)-.1 E .316(y\) it is common to add other disambiguating informatio\ +n that is not strictly part of the iden-)-.15 F +(ti\214cation \(e.g., Robert \231Code Generator\232 Henry v)102 673.8 Q +(ersus Robert \231System Administrator\232 Henry\).)-.15 E F0 -1(Ve)72 756 S +(rsion 8.1)1 E(USENIX \255 J)249.805 756 Q(an 83)-.15 E(Last Mod 6/7/93)434.55 +756 Q EP +%%Page: 3 3 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Mail Systems and Addr)72 60 Q(essing in 4.2bsd)-.18 E(3) +499 60 Q 2.5(1.2. Addr)87 96 R(ess)-.18 E/F1 10/Times-Roman@0 SF .785 +(The address speci\214es a location.)127 112.2 R .786(As I mo)5.786 F 1.086 +-.15(ve a)-.15 H .786(round, my address changes.).15 F -.15(Fo)5.786 G 3.286 +(re).15 G .786(xample, my)455.994 112.2 R 9.712 +(address might change from \231eric@Berk)102 124.2 R(ele)-.1 E -.65(y.)-.15 G +(ARP).65 E 9.711(A\232 to \231eric@bli.UUCP\232 or \231all-)-.92 F +(man@IIASA.Austria\232 depending on my current af)102 136.2 Q(\214liation.)-.25 +E(Ho)127 152.4 Q(we)-.25 E -.15(ve)-.25 G 2.819 -.4(r, a).15 H 4.519(na).4 G +2.019(ddress is independent of the location of an)188.018 152.4 R 2.019 +(yone else.)-.15 F 2.02(That is, my address)7.02 F .385(remains the same to e) +102 164.4 R -.15(ve)-.25 G .385(ryone who might be sending me mail.).15 F -.15 +(Fo)5.385 G 2.885(re).15 G .385(xample, a person at MIT and a)379.22 164.4 R +(person at USC could both send to \231eric@Berk)102 176.4 Q(ele)-.1 E -.65(y.) +-.15 G(ARP).65 E(A\232 and ha)-.92 E .3 -.15(ve i)-.2 H 2.5(ta).15 G(rri)388.44 +176.4 Q .3 -.15(ve t)-.25 H 2.5(ot).15 G(he same mailbox.)422.48 176.4 Q .627 +(Ideally a \231white pages\232 service w)127 192.6 R .627(ould be pro)-.1 F +.627(vided to map user identi\214cations into addresses)-.15 F .444(\(for e)102 +204.6 R .444(xample, see [Solomon81]\).)-.15 F .444 +(Currently this is handled by passing around scraps of paper or by)5.444 F +(calling people on the telephone to \214nd out their address.)102 216.6 Q F0 +2.5(1.3. Route)87 240.6 R F1 .288(While an address speci\214es)127 256.8 R/F2 +10/Times-Italic@0 SF(wher)2.788 E(e)-.37 E F1 .289 +(to \214nd a mailbox, a route speci\214es)2.789 F F2(how)2.789 E F1 .289 +(to \214nd the mailbox.)2.789 F(Speci\214cally)102 268.8 Q 2.607(,i)-.65 G +2.607(ts)156.457 268.8 S .106(peci\214es a path from sender to recei)165.734 +268.8 R -.15(ve)-.25 G 3.706 -.55(r. A).15 H 2.606(ss).55 G .106 +(uch, the route is potentially dif)343.364 268.8 R .106(ferent for)-.25 F -2.15 +-.25(ev e)102 280.8 T(ry pair of people in the electronic uni).25 E -.15(ve) +-.25 G(rse.).15 E .258(Normally the route is hidden from the user by the softw) +127 297 R 2.758(are. Ho)-.1 F(we)-.25 E -.15(ve)-.25 G 1.058 -.4(r, s).15 H +.258(ome netw).4 F .258(orks put the)-.1 F -.2(bu)102 309 S 1.972 +(rden of determining the route onto the sender).2 F 6.971(.A)-.55 G 1.971 +(lthough this simpli\214es the softw)322.544 309 R 1.971(are, it also)-.1 F +(greatly impairs the usability for most users.)102 321 Q(The UUCP netw)5 E +(ork is an e)-.1 E(xample of such a netw)-.15 E(ork.)-.1 E F0 2.5(2. DESIGN)72 +345 R(GO)2.5 E(ALS)-.4 E F1(Design goals for)112 363.2 Q F2(sendmail)2.5 E/F3 7 +/Times-Roman@0 SF(1)216.71 359.2 Q F1(include:)222.71 363.2 Q 12.5 +(\(1\) Compatibility)92 379.4 R 1.363(with the e)3.863 F 1.363 +(xisting mail programs, including Bell v)-.15 F 1.363(ersion 6 mail, Bell v) +-.15 F 1.364(ersion 7)-.15 F 3.589(mail, Berk)118.66 391.4 R(ele)-.1 E(y)-.15 E +F2(Mail)6.089 E F1 3.589 +([Shoens79], BerkNet mail [Schmidt79], and hopefully UUCP mail)6.089 F([No) +118.66 403.4 Q 2.5(witz78]. ARP)-.25 F(ANET mail [Crock)-.92 E(er82] w)-.1 E +(as also required.)-.1 E 12.5(\(2\) Reliability)92 419.6 R 4.002(,i)-.65 G +4.002(nt)169.522 419.6 S 1.502(he sense of guaranteeing that e)181.304 419.6 R +-.15(ve)-.25 G 1.502(ry message is correctly deli).15 F -.15(ve)-.25 G 1.503 +(red or at least).15 F .368 +(brought to the attention of a human for correct disposal; no message should e) +118.66 431.6 R -.15(ve)-.25 G 2.868(rb).15 G 2.868(ec)452.252 431.6 S +(ompletely)464 431.6 Q 2.54(lost. This)118.66 443.6 R .04(goal w)2.54 F .041 +(as considered essential because of the emphasis on mail in our en)-.1 F 2.541 +(vironment. It)-.4 F 1.755 +(has turned out to be one of the hardest goals to satisfy)118.66 455.6 R 4.254 +(,e)-.65 G 1.754(specially in the f)363.756 455.6 R 1.754(ace of the man)-.1 F +(y)-.15 E .977(anomalous message formats produced by v)118.66 467.6 R .977 +(arious ARP)-.25 F .977(ANET sites.)-.92 F -.15(Fo)5.977 G 3.478(re).15 G .978 +(xample, certain sites)420.114 467.6 R .069 +(generate improperly formated addresses, occasionally causing error)118.66 +479.6 R .069(-message loops.)-.2 F .068(Some hosts)5.069 F .766(use blanks in \ +names, causing problems with mail programs that assume that an address is one) +118.66 491.6 R -.1(wo)118.66 503.6 S 3.924(rd. The).1 F 1.423 +(semantics of some \214elds are interpreted slightly dif)3.923 F 1.423 +(ferently by dif)-.25 F 1.423(ferent sites.)-.25 F(In)6.423 E(summary)118.66 +515.6 Q 3.022(,t)-.65 G .523(he obscure features of the ARP)163.532 515.6 R +.523(ANET mail protocol really)-.92 F F2(ar)3.023 E(e)-.37 E F1 .523 +(used and are dif)3.023 F(\214cult)-.25 E(to support, b)118.66 527.6 Q +(ut must be supported.)-.2 E 12.5(\(3\) Existing)92 543.8 R(softw)2.939 E .439 +(are to do actual deli)-.1 F -.15(ve)-.25 G .439(ry should be used whene).15 F +-.15(ve)-.25 G 2.938(rp).15 G 2.938(ossible. This)387.658 543.8 R .438 +(goal deri)2.938 F -.15(ve)-.25 G 2.938(sa).15 G(s)500.11 543.8 Q +(much from political and practical considerations as technical.)118.66 555.8 Q +12.5(\(4\) Easy)92 572 R -.15(ex)2.898 G .398(pansion to f).15 F .398 +(airly comple)-.1 F 2.898(xe)-.15 G -.4(nv)261.06 572 S .399 +(ironments, including multiple connections to a single net-).4 F -.1(wo)118.66 +584 S .63(rk type \(such as with multiple UUCP or Ethernets\).).1 F .63 +(This goal requires consideration of the)5.63 F +(contents of an address as well as its syntax in order to determine which g) +118.66 596 Q(ate)-.05 E -.1(wa)-.25 G 2.5(yt).1 G 2.5(ou)443.48 596 S(se.) +455.98 596 Q 12.5(\(5\) Con\214guration)92 612.2 R 1.048 +(information should not be compiled into the code.)3.548 F 3.549(As)6.049 G +1.049(ingle compiled program)405.802 612.2 R .084 +(should be able to run as is at an)118.66 624.2 R 2.584(ys)-.15 G .083 +(ite \(barring such basic changes as the CPU type or the operat-)256.196 624.2 +R .342(ing system\).)118.66 636.2 R 1.942 -.8(We h)5.342 H -2.25 -.2(av e).8 H +.343(found this seemingly unimportant goal to be critical in real life.)3.042 F +(Besides)5.343 E .734(the simple problems that occur when an)118.66 648.2 R +3.234(yp)-.15 G .734(rogram gets recompiled in a dif)295.568 648.2 R .733 +(ferent en)-.25 F(vironment,)-.4 E(man)118.66 660.2 Q 2.5(ys)-.15 G(ites lik) +147.12 660.2 Q 2.5(et)-.1 G 2.5<6f99>183.69 660.2 S(\214ddle\232 with an)195.63 +660.2 Q(ything that the)-.15 E 2.5(yw)-.15 G(ill be recompiling an)327.27 660.2 +Q(yw)-.15 E(ay)-.1 E(.)-.65 E .32 LW 76 678.8 72 678.8 DL 80 678.8 76 678.8 DL +84 678.8 80 678.8 DL 88 678.8 84 678.8 DL 92 678.8 88 678.8 DL 96 678.8 92 +678.8 DL 100 678.8 96 678.8 DL 104 678.8 100 678.8 DL 108 678.8 104 678.8 DL +112 678.8 108 678.8 DL 116 678.8 112 678.8 DL 120 678.8 116 678.8 DL 124 678.8 +120 678.8 DL 128 678.8 124 678.8 DL 132 678.8 128 678.8 DL 136 678.8 132 678.8 +DL 140 678.8 136 678.8 DL 144 678.8 140 678.8 DL 148 678.8 144 678.8 DL 152 +678.8 148 678.8 DL 156 678.8 152 678.8 DL 160 678.8 156 678.8 DL 164 678.8 160 +678.8 DL 168 678.8 164 678.8 DL 172 678.8 168 678.8 DL 176 678.8 172 678.8 DL +180 678.8 176 678.8 DL 184 678.8 180 678.8 DL 188 678.8 184 678.8 DL 192 678.8 +188 678.8 DL 196 678.8 192 678.8 DL 200 678.8 196 678.8 DL 204 678.8 200 678.8 +DL 208 678.8 204 678.8 DL 212 678.8 208 678.8 DL 216 678.8 212 678.8 DL/F4 5 +/Times-Roman@0 SF(1)93.6 689.2 Q/F5 8/Times-Roman@0 SF(This section mak)3.2 I +(es no distinction between)-.08 E/F6 8/Times-Italic@0 SF(delivermail)2 E F5 +(and)2 E F6(sendmail.)2 E F0 -1(Ve)72 756 S(rsion 8.1)1 E(USENIX \255 J)249.805 +756 Q(an 83)-.15 E(Last Mod 6/7/93)434.55 756 Q EP +%%Page: 4 4 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Mail Systems and Addr)72 60 Q(essing in 4.2bsd)-.18 E(4) +499 60 Q/F1 10/Times-Roman@0 SF(\(6\))92 96 Q/F2 10/Times-Italic@0 SF(Sendmail) +118.66 96 Q F1 .184(must be able to let v)2.684 F .184 +(arious groups maintain their o)-.25 F .184(wn mailing lists, and let indi)-.25 +F(viduals)-.25 E(specify their o)118.66 108 Q(wn forw)-.25 E +(arding, without modifying the system alias \214le.)-.1 E 12.5(\(7\) Each)92 +124.2 R .313(user should be able to specify which mailer to e)2.814 F -.15(xe) +-.15 G .313(cute to process mail being deli).15 F -.15(ve)-.25 G .313(red for) +.15 F 3.098(him. This)118.66 136.2 R .598(feature allo)3.098 F .598 +(ws users who are using specialized mailers that use a dif)-.25 F .598 +(ferent format to)-.25 F -.2(bu)118.66 148.2 S .25(ild their en).2 F .25 +(vironment without changing the system, and f)-.4 F .25 +(acilitates specialized functions \(such)-.1 F(as returning an \231I am on v) +118.66 160.2 Q(acation\232 message\).)-.25 E 12.5(\(8\) Netw)92 176.4 R 1.552 +(ork traf)-.1 F 1.552(\214c should be minimized by batching addresses to a sin\ +gle host where possible,)-.25 F(without assistance from the user)118.66 188.4 Q +(.)-.55 E .375(These goals moti)112 204.6 R -.25(va)-.25 G .375 +(ted the architecture illustrated in \214gure 1.).25 F .374 +(The user interacts with a mail gen-)5.375 F .49(erating and sending program.) +87 216.6 R .491(When the mail is created, the generator calls)5.49 F F2 +(sendmail)2.991 E F1 2.991(,w)C .491(hich routes the)444.138 216.6 R .841 +(message to the correct mailer\(s\).)87 228.6 R .841 +(Since some of the senders may be netw)5.841 F .84(ork serv)-.1 F .84 +(ers and some of the)-.15 F(mailers may be netw)87 240.6 Q(ork clients,)-.1 E +F2(sendmail)2.5 E F1(may be used as an internet mail g)2.5 E(ate)-.05 E -.1(wa) +-.25 G -.65(y.).1 G F0 2.5(3. USA)72 264.6 R(GE)-.55 E 2.5(3.1. Addr)87 288.6 R +(ess F)-.18 E(ormats)-.25 E F1(Ar)127 304.8 Q .886 +(guments may be \215ags or addresses.)-.18 F .886(Flags set v)5.886 F .886 +(arious processing options.)-.25 F -.15(Fo)5.886 G(llo).15 E .886(wing \215ag) +-.25 F(ar)102 316.8 Q .611(guments, address ar)-.18 F .611(guments may be gi) +-.18 F -.15(ve)-.25 G 3.111(n. Addresses).15 F(follo)3.111 E 3.111(wt)-.25 G +.611(he syntax in RFC822 [Crock)365.558 316.8 R(er82])-.1 E(for ARP)102 328.8 Q +(ANET address formats.)-.92 E(In brief, the format is:)5 E 12.5(\(1\) An)107 +345 R(ything in parentheses is thro)-.15 E(wn a)-.25 E -.1(wa)-.15 G 2.5(y\().1 +G(as a comment\).)299.65 345 Q 12.5(\(2\) An)107 361.2 R .051 +(ything in angle brack)-.15 F .051(ets \(\231<)-.1 F .051 +(>\232\) is preferred o)1.666 F -.15(ve)-.15 G 2.551(ra).15 G -.15(ny)348.064 +361.2 S .051(thing else.).15 F .051(This rule implements the)5.051 F(ARP)133.66 +373.2 Q(ANET standard that addresses of the form)-.92 E .4 LW 77 408 72 408 DL +79 408 74 408 DL 84 408 79 408 DL 89 408 84 408 DL 94 408 89 408 DL 99 408 94 +408 DL 104 408 99 408 DL 109 408 104 408 DL 114 408 109 408 DL 119 408 114 408 +DL 124 408 119 408 DL 129 408 124 408 DL 134 408 129 408 DL 139 408 134 408 DL +144 408 139 408 DL 149 408 144 408 DL 154 408 149 408 DL 159 408 154 408 DL 164 +408 159 408 DL 169 408 164 408 DL 174 408 169 408 DL 179 408 174 408 DL 184 408 +179 408 DL 189 408 184 408 DL 194 408 189 408 DL 199 408 194 408 DL 204 408 199 +408 DL 209 408 204 408 DL 214 408 209 408 DL 219 408 214 408 DL 224 408 219 408 +DL 229 408 224 408 DL 234 408 229 408 DL 239 408 234 408 DL 244 408 239 408 DL +249 408 244 408 DL 254 408 249 408 DL 259 408 254 408 DL 264 408 259 408 DL 269 +408 264 408 DL 274 408 269 408 DL 279 408 274 408 DL 284 408 279 408 DL 289 408 +284 408 DL 294 408 289 408 DL 299 408 294 408 DL 304 408 299 408 DL 309 408 304 +408 DL 314 408 309 408 DL 319 408 314 408 DL 324 408 319 408 DL 329 408 324 408 +DL 334 408 329 408 DL 339 408 334 408 DL 344 408 339 408 DL 349 408 344 408 DL +354 408 349 408 DL 359 408 354 408 DL 364 408 359 408 DL 369 408 364 408 DL 374 +408 369 408 DL 379 408 374 408 DL 384 408 379 408 DL 389 408 384 408 DL 394 408 +389 408 DL 399 408 394 408 DL 404 408 399 408 DL 409 408 404 408 DL 414 408 409 +408 DL 419 408 414 408 DL 424 408 419 408 DL 429 408 424 408 DL 434 408 429 408 +DL 439 408 434 408 DL 444 408 439 408 DL 449 408 444 408 DL 454 408 449 408 DL +459 408 454 408 DL 464 408 459 408 DL 469 408 464 408 DL 474 408 469 408 DL 479 +408 474 408 DL 484 408 479 408 DL 489 408 484 408 DL 494 408 489 408 DL 499 408 +494 408 DL 504 408 499 408 DL(Figure 1 \212 Sendmail System Structure.)208 660 +Q 77 672 72 672 DL 79 672 74 672 DL 84 672 79 672 DL 89 672 84 672 DL 94 672 89 +672 DL 99 672 94 672 DL 104 672 99 672 DL 109 672 104 672 DL 114 672 109 672 DL +119 672 114 672 DL 124 672 119 672 DL 129 672 124 672 DL 134 672 129 672 DL 139 +672 134 672 DL 144 672 139 672 DL 149 672 144 672 DL 154 672 149 672 DL 159 672 +154 672 DL 164 672 159 672 DL 169 672 164 672 DL 174 672 169 672 DL 179 672 174 +672 DL 184 672 179 672 DL 189 672 184 672 DL 194 672 189 672 DL 199 672 194 672 +DL 204 672 199 672 DL 209 672 204 672 DL 214 672 209 672 DL 219 672 214 672 DL +224 672 219 672 DL 229 672 224 672 DL 234 672 229 672 DL 239 672 234 672 DL 244 +672 239 672 DL 249 672 244 672 DL 254 672 249 672 DL 259 672 254 672 DL 264 672 +259 672 DL 269 672 264 672 DL 274 672 269 672 DL 279 672 274 672 DL 284 672 279 +672 DL 289 672 284 672 DL 294 672 289 672 DL 299 672 294 672 DL 304 672 299 672 +DL 309 672 304 672 DL 314 672 309 672 DL 319 672 314 672 DL 324 672 319 672 DL +329 672 324 672 DL 334 672 329 672 DL 339 672 334 672 DL 344 672 339 672 DL 349 +672 344 672 DL 354 672 349 672 DL 359 672 354 672 DL 364 672 359 672 DL 369 672 +364 672 DL 374 672 369 672 DL 379 672 374 672 DL 384 672 379 672 DL 389 672 384 +672 DL 394 672 389 672 DL 399 672 394 672 DL 404 672 399 672 DL 409 672 404 672 +DL 414 672 409 672 DL 419 672 414 672 DL 424 672 419 672 DL 429 672 424 672 DL +434 672 429 672 DL 439 672 434 672 DL 444 672 439 672 DL 449 672 444 672 DL 454 +672 449 672 DL 459 672 454 672 DL 464 672 459 672 DL 469 672 464 672 DL 474 672 +469 672 DL 479 672 474 672 DL 484 672 479 672 DL 489 672 484 672 DL 494 672 489 +672 DL 499 672 494 672 DL 504 672 499 672 DL F0 -1(Ve)72 756 S(rsion 8.1)1 E +(USENIX \255 J)249.805 756 Q(an 83)-.15 E(Last Mod 6/7/93)434.55 756 Q EP +%%Page: 5 5 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Mail Systems and Addr)72 60 Q(essing in 4.2bsd)-.18 E(5) +499 60 Q/F1 10/Times-Roman@0 SF(user name <machine-address>)173.66 96 Q(will s\ +end to the electronic \231machine-address\232 rather than the human \231user n\ +ame.)133.66 112.2 Q<9a>-.7 E 12.5(\(3\) Double)107 128.4 R 2.246(quotes \() +4.746 F -2.754 2.5("\) q)2.5 H 2.246 +(uote phrases; backslashes quote characters.)224.188 128.4 R 2.246 +(Backslashes are more)7.246 F(po)133.66 140.4 Q .654(werful in that the)-.25 F +3.154(yw)-.15 G .655(ill cause otherwise equi)229.196 140.4 R -.25(va)-.25 G +.655(lent phrases to compare dif).25 F .655(ferently \212 for)-.25 F -.15(ex) +133.66 152.4 S(ample,).15 E/F2 10/Times-Italic@0 SF(user)3.873 E F1(and)3.873 E +F2("user")3.872 E F1 1.372(are equi)3.872 F -.25(va)-.25 G 1.372(lent, b).25 F +(ut)-.2 E F2(\\user)3.872 E F1 1.372(is dif)3.872 F 1.372 +(ferent from either of them.)-.25 F(This)6.372 E(might be used to a)133.66 +164.4 Q -.2(vo)-.2 G(id normal aliasing or duplicate suppression algorithms.).2 +E -.15(Pa)127 180.6 S 1.12(rentheses, angle brack).15 F 1.12 +(ets, and double quotes must be properly balanced and nested.)-.1 F(The)6.12 E +(re)102 194.6 Q(writing rules control remaining parsing)-.25 E/F3 7 +/Times-Roman@0 SF(2)266.17 190.6 Q F1(.)269.67 194.6 Q .644(Although old style\ + addresses are still accepted in most cases, the preferred address format is) +127 210.8 R .299(based on ARP)102 222.8 R(ANET)-.92 E .299 +(-style domain-based addresses [Su82a].)-.92 F .299 +(These addresses are based on a hierar)5.299 F(-)-.2 E .13 +(chical, logical decomposition of the address space.)102 234.8 R .13 +(The addresses are hierarchical in a sense similar)5.13 F 1.133(to the U.S. po\ +stal addresses: the messages may \214rst be routed to the correct state, with \ +no initial)102 246.8 R .72 +(consideration of the city or other addressing details.)102 258.8 R .72 +(The addresses are logical in that each step in)5.72 F(the hierarch)102 270.8 Q +2.5(yc)-.05 G +(orresponds to a set of \231naming authorities\232 rather than a ph)161.37 +270.8 Q(ysical netw)-.05 E(ork.)-.1 E -.15(Fo)127 287 S 2.5(re).15 G +(xample, the address:)147.53 287 Q(eric@HostA.BigSite.ARP)142 303.2 Q(A)-.92 E +-.1(wo)102 319.4 S .851 +(uld \214rst look up the domain BigSite in the namespace administrated by ARP) +.1 F 3.351(A. A)-.92 F .851(query could)3.351 F 1.476 +(then be sent to BigSite for interpretation of HostA.)102 331.4 R(Ev)6.475 E +1.475(entually the mail w)-.15 F 1.475(ould arri)-.1 F 1.775 -.15(ve a)-.25 H +3.975(tH).15 G(ostA,)482.61 331.4 Q(which w)102 343.4 Q +(ould then do \214nal deli)-.1 E -.15(ve)-.25 G(ry to user \231eric.).15 E<9a> +-.7 E F0 2.5(3.2. Mail)87 367.4 R(to Files and Pr)2.5 E(ograms)-.18 E F1 .609 +(Files and programs are le)127 383.6 R .609(gitimate message recipients.)-.15 F +.609(Files pro)5.609 F .609(vide archi)-.15 F -.25(va)-.25 G 3.109(ls).25 G .61 +(torage of mes-)445.02 383.6 R .124 +(sages, useful for project administration and history)102 395.6 R 5.124(.P)-.65 +G .124(rograms are useful as recipients in a v)318.308 395.6 R .124(ariety of) +-.25 F .69(situations, for e)102 407.6 R .691(xample, to maintain a public rep\ +ository of systems messages \(such as the Berk)-.15 F(ele)-.1 E(y)-.15 E F2 +(msgs)102 419.6 Q F1(program\).)2.5 E(An)127 435.8 Q 3.188(ya)-.15 G .688(ddre\ +ss passing through the initial parsing algorithm as a local address \(i.e, not\ + appear)151.698 435.8 R(-)-.2 E .276(ing to be a v)102 447.8 R .276 +(alid address for another mailer\) is scanned for tw)-.25 F 2.776(os)-.1 G .277 +(pecial cases.)362.128 447.8 R .277(If pre\214x)5.277 F .277(ed by a v)-.15 F +(erti-)-.15 E .18(cal bar \(\231)102 459.8 R .833<7c9a>.833 G 2.68(\)t)-.833 G +.179(he rest of the address is processed as a shell command.)156.456 459.8 R +.179(If the user name be)5.179 F .179(gins with a)-.15 F(slash mark \(\231/)102 +471.8 Q(\232\) the name is used as a \214le name, instead of a login name.).833 +E F0 2.5(3.3. Aliasing,)87 495.8 R -.25(Fo)2.5 G(rwarding, Inclusion).25 E F2 +(Sendmail)127 512 Q F1 1.074(reroutes mail three w)3.574 F 3.574(ays. Aliasing) +-.1 F 1.075(applies system wide.)3.575 F -.15(Fo)6.075 G(rw).15 E 1.075 +(arding allo)-.1 F 1.075(ws each)-.25 F .233 +(user to reroute incoming mail destined for that account.)102 524 R .233 +(Inclusion directs)5.233 F F2(sendmail)2.733 E F1 .233(to read a \214le for) +2.733 F 2.5(al)102 536 S +(ist of addresses, and is normally used in conjunction with aliasing.)111.72 +536 Q F0 2.5(3.3.1. Aliasing)102 560 R F1 .065 +(Aliasing maps local addresses to address lists using a system-wide \214le.)142 +576.2 R .065(This \214le is hashed)5.065 F 1.546(to speed access.)117 588.2 R +1.545(Only addresses that parse as local are allo)6.546 F 1.545 +(wed as aliases; this guarantees a)-.25 F(unique k)117 600.2 Q .3 -.15(ey \() +-.1 H(since there are no nicknames for the local host\).).15 E F0 2.5(3.3.2. F) +102 624.2 R(orwarding)-.25 E F1 .641 +(After aliasing, if an recipient address speci\214es a local user)142 640.4 R +F2(sendmail)3.141 E F1 .641(searches for a \231.for)3.141 F(-)-.2 E -.1(wa)117 +652.4 S .413(rd\232 \214le in the recipient').1 F 2.913(sh)-.55 G .413 +(ome directory)235.335 652.4 R 5.413(.I)-.65 G 2.913(fi)302.161 652.4 S 2.913 +(te)311.184 652.4 S .413(xists, the message is)321.167 652.4 R F2(not)2.913 E +F1 .412(sent to that user)2.913 F 2.912(,b)-.4 G(ut)496.22 652.4 Q .745 +(rather to the list of addresses in that \214le.)117 664.4 R .746 +(Often this list will contain only one address, and the)5.746 F +(feature will be used for netw)117 676.4 Q(ork mail forw)-.1 E(arding.)-.1 E +.32 LW 76 686 72 686 DL 80 686 76 686 DL 84 686 80 686 DL 88 686 84 686 DL 92 +686 88 686 DL 96 686 92 686 DL 100 686 96 686 DL 104 686 100 686 DL 108 686 104 +686 DL 112 686 108 686 DL 116 686 112 686 DL 120 686 116 686 DL 124 686 120 686 +DL 128 686 124 686 DL 132 686 128 686 DL 136 686 132 686 DL 140 686 136 686 DL +144 686 140 686 DL 148 686 144 686 DL 152 686 148 686 DL 156 686 152 686 DL 160 +686 156 686 DL 164 686 160 686 DL 168 686 164 686 DL 172 686 168 686 DL 176 686 +172 686 DL 180 686 176 686 DL 184 686 180 686 DL 188 686 184 686 DL 192 686 188 +686 DL 196 686 192 686 DL 200 686 196 686 DL 204 686 200 686 DL 208 686 204 686 +DL 212 686 208 686 DL 216 686 212 686 DL/F4 5/Times-Roman@0 SF(2)93.6 696.4 Q +/F5 8/Times-Roman@0 SF(Disclaimer: Some special processing is done after re)3.2 +I(writing local names; see belo)-.2 E -.52(w.)-.2 G F0 -1(Ve)72 756 S +(rsion 8.1)1 E(USENIX \255 J)249.805 756 Q(an 83)-.15 E(Last Mod 6/7/93)434.55 +756 Q EP +%%Page: 6 6 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Mail Systems and Addr)72 60 Q(essing in 4.2bsd)-.18 E(6) +499 60 Q/F1 10/Times-Roman@0 SF -.15(Fo)142 96 S(rw).15 E 1.152 +(arding also permits a user to specify a pri)-.1 F -.25(va)-.25 G 1.151 +(te incoming mailer).25 F 6.151(.F)-.55 G 1.151(or e)437.348 96 R 1.151 +(xample, for)-.15 F(-)-.2 E -.1(wa)117 108 S(rding to:).1 E -2.5 .833("| /)157 +124.2 T(usr/local/ne)-.833 E(wmail myname")-.25 E(will use a dif)117 140.4 Q +(ferent incoming mailer)-.25 E(.)-.55 E F0 2.5(3.3.3. Inclusion)102 164.4 R F1 +(Inclusion is speci\214ed in RFC 733 [Crock)142 180.6 Q(er77] syntax:)-.1 E +(:Include: pathname)157 196.8 Q .391 +(An address of this form reads the \214le speci\214ed by)117 213 R/F2 10 +/Times-Italic@0 SF(pathname)2.891 E F1 .391 +(and sends to all users listed in that)2.891 F(\214le.)117 225 Q .645 +(The intent is)142 241.2 R F2(not)3.145 E F1 .644 +(to support direct use of this feature, b)3.145 F .644 +(ut rather to use this as a subset of)-.2 F 2.5(aliasing. F)117 253.2 R(or e) +-.15 E(xample, an alias of the form:)-.15 E +(project: :include:/usr/project/userlist)157 269.4 Q 1.93(is a method of letti\ +ng a project maintain a mailing list without interaction with the system)117 +285.6 R(administration, e)117 297.6 Q -.15(ve)-.25 G 2.5(ni).15 G 2.5(ft)203.54 +297.6 S(he alias \214le is protected.)212.15 297.6 Q 2.025 +(It is not necessary to reb)142 313.8 R 2.025(uild the inde)-.2 F 4.524(xo)-.15 +G 4.524(nt)317.828 313.8 S 2.024(he alias database when a :include: list is) +330.132 313.8 R(changed.)117 325.8 Q F0 2.5(3.4. Message)87 349.8 R(Collection) +2.5 E F1 .857(Once all recipient addresses are parsed and v)127 366 R .857 +(eri\214ed, the message is collected.)-.15 F .857(The message)5.857 F .574 +(comes in tw)102 378 R 3.074(op)-.1 G .574 +(arts: a message header and a message body)164.452 378 R 3.074(,s)-.65 G .574 +(eparated by a blank line.)349.734 378 R .573(The body is)5.574 F +(an uninterpreted sequence of te)102 390 Q(xt lines.)-.15 E +(The header is formated as a series of lines of the form)127 406.2 Q +(\214eld-name: \214eld-v)178 422.4 Q(alue)-.25 E(Field-v)102 438.6 Q 1.366 +(alue can be split across lines by starting the follo)-.25 F 1.366 +(wing lines with a space or a tab)-.25 F 6.366(.S)-.4 G(ome)486.78 438.6 Q .211 +(header \214elds ha)102 450.6 R .511 -.15(ve s)-.2 H .211 +(pecial internal meaning, and ha).15 F .511 -.15(ve a)-.2 H .211 +(ppropriate special processing.).15 F .21(Other headers)5.21 F +(are simply passed through.)102 462.6 Q +(Some header \214elds may be added automatically)5 E 2.5(,s)-.65 G +(uch as time stamps.)413.53 462.6 Q F0 2.5(4. THE)72 486.6 R(UUCP PR)2.5 E +(OBLEM)-.3 E F1 .43(Of particular interest is the UUCP netw)112 502.8 R 2.93 +(ork. The)-.1 F -.15(ex)2.93 G .43(plicit routing used in the UUCP en).15 F +(vironment)-.4 E .909(causes a number of serious problems.)87 514.8 R .909 +(First, gi)5.909 F .908(ving out an address is impossible without kno)-.25 F +.908(wing the)-.25 F .453(address of your potential correspondent.)87 526.8 R +.454(This is typically handled by specifying the address relati)5.453 F .754 +-.15(ve t)-.25 H(o).15 E 1.208(some \231well-kno)87 538.8 R 1.208 +(wn\232 host \(e.g., ucb)-.25 F -.25(va)-.15 G 3.708(xo).25 G 3.708(rd)253.47 +538.8 S(ecv)265.508 538.8 Q 3.708(ax\). Second,)-.25 F 1.207(it is often dif) +3.708 F 1.207(\214cult to compute the set of)-.25 F .157 +(addresses to reply to without some kno)87 550.8 R .157 +(wledge of the topology of the netw)-.25 F 2.657(ork. Although)-.1 F .157 +(it may be easy)2.657 F .352(for a human being to do this under man)87 562.8 R +2.851(yc)-.15 G .351(ircumstances, a program does not ha)259.713 562.8 R .651 +-.15(ve e)-.2 H .351(qually sophisticated).15 F 1.153(heuristics b)87 574.8 R +1.153(uilt in.)-.2 F 1.154(Third, certain addresses will become painfully and \ +unnecessarily long, as when a)6.153 F .406(message is routed through man)87 +586.8 R 2.906(yh)-.15 G .406(osts in the USENET)225.81 586.8 R 5.406(.A)-.74 G +.406(nd \214nally)322.804 586.8 R 2.905(,c)-.65 G .405(ertain \231mix)370.465 +586.8 R .405(ed domain\232 addresses)-.15 F +(are impossible to parse unambiguously \212 e.g.,)87 598.8 Q(decv)127 615 Q +(ax!ucb)-.25 E -.25(va)-.15 G(x!lbl-h!user@LBL-CSAM).25 E .378(might ha)87 +631.2 R .678 -.15(ve m)-.2 H(an).15 E 2.878(yp)-.15 G .379 +(ossible resolutions, depending on whether the message w)164.574 631.2 R .379 +(as \214rst routed to decv)-.1 F .379(ax or)-.25 F(to LBL-CSAM.)87 643.2 Q 2.32 +-.8(To s)112 659.4 T(olv).8 E 3.22(et)-.15 G .72 +(his problem, the UUCP syntax w)152.49 659.4 R .719(ould ha)-.1 F 1.019 -.15 +(ve t)-.2 H 3.219(ob).15 G 3.219(ec)346.956 659.4 S .719 +(hanged to use addresses rather than)359.055 659.4 R 3.718(routes. F)87 671.4 R +1.218(or e)-.15 F 1.218(xample, the address \231decv)-.15 F(ax!ucb)-.25 E -.25 +(va)-.15 G 1.218(x!eric\232 might be e).25 F 1.218(xpressed as \231eric@ucb) +-.15 F -.25(va)-.15 G(x.UUCP\232).25 E .079(\(with the hop through decv)87 +683.4 R .079(ax implied\).)-.25 F .079(This address w)5.079 F .078 +(ould itself be a domain-based address; for e)-.1 F(xam-)-.15 E +(ple, an address might be of the form:)87 695.4 Q(mark@d.cbosg.btl.UUCP)127 +711.6 Q F0 -1(Ve)72 756 S(rsion 8.1)1 E(USENIX \255 J)249.805 756 Q(an 83)-.15 +E(Last Mod 6/7/93)434.55 756 Q EP +%%Page: 7 7 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Mail Systems and Addr)72 60 Q(essing in 4.2bsd)-.18 E(7) +499 60 Q/F1 10/Times-Roman@0 SF .311(Hosts outside of Bell T)87 96 R .311 +(elephone Laboratories w)-.7 F .311(ould then only need to kno)-.1 F 2.811(wh) +-.25 G .811 -.25(ow t)402.982 96 T 2.811(og).25 G .312(et to a designated) +433.354 96 R(BTL relay)87 108 Q 2.5(,a)-.65 G(nd the BTL topology w)137.17 108 +Q(ould only be maintained inside Bell.)-.1 E .543(There are three major proble\ +ms associated with turning UUCP addresses into something reason-)112 124.2 R +.465(able: de\214ning the namespace, creating and propag)87 136.2 R .465 +(ating the necessary softw)-.05 F .466(are, and b)-.1 F .466(uilding and main-) +-.2 F(taining the database.)87 148.2 Q F0 2.5(4.1. De\214ning)87 172.2 R +(the Namespace)2.5 E F1 1.015(Putting all UUCP hosts into a \215at namespace \ +\(e.g., \231...@host.UUCP\232\) is not practical for a)127 188.4 R .222 +(number of reasons.)102 200.4 R .222(First, with o)5.222 F -.15(ve)-.15 G 2.722 +(r1).15 G .222(600 sites already)253.292 200.4 R 2.722(,a)-.65 G .222 +(nd \(with the increasing a)329.958 200.4 R -.25(va)-.2 G .222 +(ilability of ine).25 F(x-)-.15 E(pensi)102 212.4 Q 1.973 -.15(ve m)-.25 H +1.673(icrocomputers and autodialers\) se).15 F -.15(ve)-.25 G 1.672 +(ral thousand more coming within a fe).15 F 4.172(wy)-.25 G 1.672(ears, the) +469.008 212.4 R .078 +(database update problem is simply intractable if the namespace is \215at.)102 +224.4 R .078(Second, there are almost cer)5.078 F(-)-.2 E 2.446 +(tainly name con\215icts today)102 236.4 R 7.446(.T)-.65 G 2.446 +(hird, as the number of sites gro)232.794 236.4 R 4.946(wt)-.25 G 2.446 +(he names become e)386.316 236.4 R -.15(ve)-.25 G 4.946(rl).15 G(ess)491.78 +236.4 Q(mnemonic.)102 248.4 Q .534(It seems ine)127 264.6 R .535 +(vitable that there be some sort of naming authority for the set of top le)-.25 +F -.15(ve)-.25 G 3.035(ln).15 G(ames)483.45 264.6 Q .157 +(in the UUCP domain, as unpleasant a possibility as that may seem.)102 276.6 R +.157(It will simply not be possible to)5.157 F(ha)102 288.6 Q .536 -.15(ve o) +-.2 H .236(ne host resolving all names.).15 F .236(It may ho)5.236 F(we)-.25 E +-.15(ve)-.25 G 2.736(rb).15 G 2.736(ep)316.144 288.6 S .236 +(ossible to handle this in a f)328.32 288.6 R .237(ashion similar to)-.1 F +1.582(that of assigning names of ne)102 300.6 R 1.582(wsgroups in USENET)-.25 F +6.582(.H)-.74 G -.25(ow)334.758 300.6 S -2.15 -.25(ev e).25 H 2.382 -.4(r, i) +.25 H 4.082(tw).4 G 1.582(ill be essential to encourage)386.582 300.6 R -2.15 +-.25(ev e)102 312.6 T .52(ryone to become subdomains of an e).25 F .52 +(xisting domain whene)-.15 F -.15(ve)-.25 G 3.02(rp).15 G .52(ossible \212 e) +374.85 312.6 R -.15(ve)-.25 G 3.02(nt).15 G .52(hough this will)442.95 312.6 R +.077(certainly bruise some e)102 324.6 R 2.577(gos. F)-.15 F .077(or e)-.15 F +.077(xample, if a ne)-.15 F 2.577(wh)-.25 G .076 +(ost named \231blid\232 were to be added to the UUCP)310.843 324.6 R(netw)102 +336.6 Q .65(ork, it w)-.1 F .651(ould probably actually be addressed as \231d.\ +bli.UUCP\232 \(i.e., as host \231d\232 in the pseudo-)-.1 F +(domain \231bli\232 rather than as host \231blid\232 in the UUCP domain\).)102 +348.6 Q F0 2.5(4.2. Cr)87 372.6 R(eating and Pr)-.18 E(opagating the Softwar) +-.18 E(e)-.18 E F1 .078(The softw)127 388.8 R .078 +(are required to implement a consistent namespace is relati)-.1 F -.15(ve)-.25 +G .077(ly tri).15 F 2.577(vial. T)-.25 F .277 -.1(wo m)-.8 H(odules).1 E +(are needed, one to handle incoming mail and one to handle outgoing mail.)102 +400.8 Q 1.136(The incoming module must be prepared to handle either old or ne) +127 417 R 3.636(ws)-.25 G 1.136(tyle addresses.)416.448 417 R(Ne)6.136 E(w-) +-.25 E .025(style addresses can be passed through unchanged.)102 429 R .024 +(Old style addresses must be turned into ne)5.025 F 2.524(ws)-.25 G(tyle)489 +429 Q(addresses where possible.)102 441 Q 2.247 +(The outgoing module is slightly trickier)127 457.2 R 7.247(.I)-.55 G 4.747(tm) +309.932 457.2 S 2.247(ust do a database lookup on the recipient)325.239 457.2 R +.823(addresses \(passed on the command line\) to determine what hosts to send \ +the message to.)102 469.2 R .823(If those)5.823 F .023(hosts do not accept ne) +102 481.2 R .024(w-style addresses, it must transform all addresses in the hea\ +der of the message)-.25 F(into old style using the database lookup.)102 493.2 Q +1.197(Both of these modules are straightforw)127 509.4 R 1.197(ard e)-.1 F +1.197(xcept for the issue of modifying the header)-.15 F 6.197(.I)-.55 G(t) +501.22 509.4 Q .944 +(seems prudent to choose one format for the message headers.)102 521.4 R -.15 +(Fo)5.944 G 3.444(ran).15 G .944(umber of reasons, Berk)391.448 521.4 R(ele)-.1 +E(y)-.15 E .824(has elected to use the ARP)102 533.4 R .824 +(ANET protocols for message formats.)-.92 F(Ho)5.823 E(we)-.25 E -.15(ve)-.25 G +1.623 -.4(r, t).15 H .823(his protocol is some-).4 F(what dif)102 545.4 Q +(\214cult to parse.)-.25 E(Propag)127 561.6 Q 1.903(ation is some)-.05 F 1.903 +(what more dif)-.25 F 4.403(\214cult. There)-.25 F 1.903(are a lar)4.403 F +1.903(ge number of hosts connected to)-.18 F .812(UUCP that will w)102 573.6 R +.811(ant to run completely standard systems \(for v)-.1 F .811 +(ery good reasons\).)-.15 F .811(The strate)5.811 F .811(gy is)-.15 F +(not to con)102 585.6 Q -.15(ve)-.4 G(rt the entire netw).15 E +(ork \212 only enough of it it alle)-.1 E(viate the problem.)-.25 E F0 2.5 +(4.3. Building)87 609.6 R(and Maintaining the Database)2.5 E F1 .127 +(This is by f)127 625.8 R .127(ar the most dif)-.1 F .128(\214cult problem.) +-.25 F 2.628(Ap)5.128 G .128(rototype for this database already e)309.736 625.8 +R .128(xists, b)-.15 F .128(ut it is)-.2 F +(maintained by hand and does not pretend to be complete.)102 637.8 Q .701(This\ + problem will be reduced considerably if people choose to group their hosts in\ +to subdo-)127 654 R 3.219(mains. This)102 666 R -.1(wo)3.219 G .719 +(uld require a global update only when a ne).1 F 3.22(wt)-.25 G .72(op le) +356.47 666 R -.15(ve)-.25 G 3.22(ld).15 G .72(omain joined the netw)396.95 666 +R(ork.)-.1 E 2.805(Am)102 678 S .305 +(essage to a host in a subdomain could simply be routed to a kno)119.805 678 R +.304(wn domain g)-.25 F(ate)-.05 E -.1(wa)-.25 G 2.804(yf).1 G .304(or further) +465.656 678 R 3.073(processing. F)102 690 R .573(or e)-.15 F .573(xample, the \ +address \231eric@a.bli.UUCP\232 might be routed to the \231bli\232 g)-.15 F +(ate)-.05 E -.1(wa)-.25 G 3.074(yf).1 G(or)495.67 690 Q(redistrib)102 702 Q +1.376(ution; ne)-.2 F 3.876(wh)-.25 G 1.375 +(osts could be added within BLI without notifying the rest of the w)187.632 702 +R 3.875(orld. Of)-.1 F(course, other hosts)102 714 Q/F2 10/Times-Italic@0 SF +(could)2.5 E F1(be noti\214ed as an ef)2.5 E(\214cienc)-.25 E 2.5(ym)-.15 G +(easure.)321.01 714 Q F0 -1(Ve)72 756 S(rsion 8.1)1 E(USENIX \255 J)249.805 756 +Q(an 83)-.15 E(Last Mod 6/7/93)434.55 756 Q EP +%%Page: 8 8 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Bold@0 SF(Mail Systems and Addr)72 60 Q(essing in 4.2bsd)-.18 E(8) +499 60 Q/F1 10/Times-Roman@0 SF .966(There may be more than one domain g)127 96 +R(ate)-.05 E -.1(wa)-.25 G 4.767 -.65(y. A).1 H .967 +(domain such as BTL, for instance, might)4.117 F(ha)102 108 Q .653 -.15(ve a d) +-.2 H .353(ozen g).15 F(ate)-.05 E -.1(wa)-.25 G .353(ys to the outside w).1 F +.352(orld; a non-BTL site could choose the closest g)-.1 F(ate)-.05 E -.1(wa) +-.25 G 4.152 -.65(y. T).1 H(he).65 E .308(only restriction w)102 120 R .308 +(ould be that all g)-.1 F(ate)-.05 E -.1(wa)-.25 G .308 +(ys maintain a consistent vie).1 F 2.808(wo)-.25 G 2.808(ft)390.998 120 S .308 +(he domain the)399.916 120 R 2.808(yr)-.15 G(epresent.)468.18 120 Q F0 2.5 +(4.4. Logical)87 144 R(Structur)2.5 E(e)-.18 E F1(Logically)127 160.2 Q 3.803 +(,d)-.65 G 1.303(omains are or)175.983 160.2 R -.05(ga)-.18 G 1.303 +(nized into a tree.).05 F 1.303(There need not be a host actually associated) +6.303 F .462(with each le)102 172.2 R -.15(ve)-.25 G 2.962(li).15 G 2.962(nt) +168.806 172.2 S .462(he tree \212 for e)179.548 172.2 R .462 +(xample, there will be no host associated with the name \231UUCP)-.15 F -.7 +<2e9a>-1.11 G(Similarly)102 184.2 Q 3.115(,a)-.65 G 3.115(no)148.635 184.2 S +-2.19 -.18(rg a)161.75 184.2 T .614 +(nization might group names together for administrati).18 F .914 -.15(ve r)-.25 +H .614(easons; for e).15 F .614(xample, the)-.15 F(name)102 196.2 Q +(CAD.research.BigCorp.UUCP)142 212.4 Q(might not actually ha)102 228.6 Q .3 +-.15(ve a h)-.2 H(ost representing \231research.).15 E<9a>-.7 E(Ho)127 244.8 Q +(we)-.25 E -.15(ve)-.25 G 1.531 -.4(r, i).15 H 3.231(tm).4 G .731 +(ay frequently be con)184.902 244.8 R -.15(ve)-.4 G .731(nient to ha).15 F +1.031 -.15(ve a h)-.2 H .732(ost or hosts that \231represent\232 a domain.).15 +F -.15(Fo)102 256.8 S 3.466(re).15 G .966(xample, if a single host e)123.496 +256.8 R .966(xists that represents Berk)-.15 F(ele)-.1 E 2.266 -.65(y, t)-.15 H +.966(hen mail from outside Berk).65 F(ele)-.1 E 3.466(yc)-.15 G(an)494.56 256.8 +Q(forw)102 268.8 Q .796 +(ard mail to that host for further resolution without kno)-.1 F .796(wing Berk) +-.25 F(ele)-.1 E(y')-.15 E 3.296(s\()-.55 G .797(rather v)417.066 268.8 R .797 +(olatile\) topol-)-.2 F(ogy)102 280.8 Q 5(.T)-.65 G(his is not unlik)129.96 +280.8 Q 2.5(et)-.1 G(he operation of the telephone netw)198.76 280.8 Q(ork.)-.1 +E .053(This may also be useful inside certain lar)127 297 R .053(ge domains.) +-.18 F -.15(Fo)5.053 G 2.553(re).15 G .053(xample, at Berk)365.352 297 R(ele) +-.1 E 2.553(yi)-.15 G 2.553(tm)450.801 297 S .053(ay be pre-)463.914 297 R .722 +(sumed that most hosts kno)102 309 R 3.222(wa)-.25 G .722 +(bout other hosts inside the Berk)225.64 309 R(ele)-.1 E 3.223(yd)-.15 G 3.223 +(omain. But)380.825 309 R .723(if the)3.223 F 3.223(yp)-.15 G .723(rocess an) +466.347 309 R .405(address that is unkno)102 321 R .405(wn, the)-.25 F 2.905 +(yc)-.15 G .405(an pass it \231upstairs\232 for further e)229.165 321 R 2.905 +(xamination. Thus)-.15 F .405(as ne)2.905 F 2.905(wh)-.25 G .405(osts are) +473.325 321 R .488(added only one host \(the domain master\))102 333 R/F2 10 +/Times-Italic@0 SF(must)2.989 E F1 .489 +(be updated immediately; other hosts can be updated)2.989 F(as con)102 345 Q +-.15(ve)-.4 G(nient.).15 E .583(Ideally this name resolution process w)127 +361.2 R .583(ould be performed by a name serv)-.1 F .582 +(er \(e.g., [Su82b]\) to)-.15 F -.2(avo)102 373.2 S .507(id unnecessary cop).2 +F .507(ying of the message.)-.1 F(Ho)5.507 E(we)-.25 E -.15(ve)-.25 G 1.307 -.4 +(r, i).15 H 3.007(nab).4 G .507(atch netw)346.623 373.2 R .508 +(ork such as UUCP this could)-.1 F(result in unnecessary delays.)102 385.2 Q F0 +2.5(5. COMP)72 409.2 R(ARISON WITH DELIVERMAIL)-.74 E F2(Sendmail)112 425.4 Q +F1(is an outgro)2.5 E(wth of)-.25 E F2(delivermail)2.5 E F1 5(.T)C +(he primary dif)286.18 425.4 Q(ferences are:)-.25 E 12.5(\(1\) Con\214guration) +92 441.6 R .573(information is not compiled in.)3.073 F .572 +(This change simpli\214es man)5.572 F 3.072(yo)-.15 G 3.072(ft)433.684 441.6 S +.572(he problems of)442.866 441.6 R(mo)118.66 453.6 Q(ving to other machines.) +-.15 E(It also allo)5 E(ws easy deb)-.25 E(ugging of ne)-.2 E 2.5(wm)-.25 G +(ailers.)388.06 453.6 Q 12.5(\(2\) Address)92 469.8 R .491 +(parsing is more \215e)2.991 F 2.991(xible. F)-.15 F .491(or e)-.15 F(xample,) +-.15 E F2(delivermail)2.992 E F1 .492(only supported one g)2.992 F(ate)-.05 E +-.1(wa)-.25 G 2.992(yt).1 G 2.992(oa)481.718 469.8 S -.15(ny)494.15 469.8 S +(netw)118.66 481.8 Q(ork, whereas)-.1 E F2(sendmail)2.5 E F1(can be sensiti)2.5 +E .3 -.15(ve t)-.25 H 2.5(oh).15 G(ost names and reroute to dif)310.9 481.8 Q +(ferent g)-.25 E(ate)-.05 E -.1(wa)-.25 G(ys.).1 E 12.5(\(3\) F)92 498 R(orw) +-.15 E 2.878(arding and :include: features eliminate the requirement that the \ +system alias \214le be)-.1 F 1.073(writable by an)118.66 510 R 3.573(yu)-.15 G +1.073 +(ser \(or that an update program be written, or that the system administration) +191.439 510 R(mak)118.66 522 Q 2.5(ea)-.1 G(ll changes\).)147.16 522 Q(\(4\))92 +538.2 Q F2(Sendmail)118.66 538.2 Q F1 .443 +(supports message batching across netw)2.944 F .443 +(orks when a message is being sent to multiple)-.1 F(recipients.)118.66 550.2 Q +12.5(\(5\) A)92 566.4 R 1.945(mail queue is pro)4.445 F 1.946(vided in)-.15 F +F2(sendmail.)4.446 E F1 1.946(Mail that cannot be deli)6.946 F -.15(ve)-.25 G +1.946(red immediately b).15 F 1.946(ut can)-.2 F .439(potentially be deli) +118.66 578.4 R -.15(ve)-.25 G .438 +(red later is stored in this queue for a later retry).15 F 5.438(.T)-.65 G .438 +(he queue also pro)404.088 578.4 R .438(vides a)-.15 F -.2(bu)118.66 590.4 S +-.25(ff).2 G .838(er ag).25 F .839(ainst system crashes; after the message has\ + been collected it may be reliably redeli)-.05 F(v-)-.25 E(ered e)118.66 602.4 +Q -.15(ve)-.25 G 2.5(ni).15 G 2.5(ft)162.13 602.4 S +(he system crashes during the initial deli)170.74 602.4 Q -.15(ve)-.25 G(ry).15 +E(.)-.65 E(\(6\))92 618.6 Q F2(Sendmail)118.66 618.6 Q F1 1.351(uses the netw) +3.851 F 1.351(orking support pro)-.1 F 1.351(vided by 4.2BSD to pro)-.15 F 1.35 +(vide a direct interf)-.15 F 1.35(ace net-)-.1 F -.1(wo)118.66 630.6 S .283 +(rks such as the ARP).1 F .284 +(ANET and/or Ethernet using SMTP \(the Simple Mail T)-.92 F .284 +(ransfer Protocol\))-.35 F -.15(ove)118.66 642.6 S 2.5(raT).15 G +(CP/IP connection.)151.68 642.6 Q F0 -1(Ve)72 756 S(rsion 8.1)1 E +(USENIX \255 J)249.805 756 Q(an 83)-.15 E(Last Mod 6/7/93)434.55 756 Q EP +%%Page: 9 9 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF(REFERENCES)264.105 132 Q([Crock)87 148.2 Q 56.73 +(er77] Crock)-.1 F(er)-.1 E 3.535(,D)-.4 G 3.535(.H)239.965 148.2 S 1.035(., V) +253.22 148.2 R 1.035(ittal, J. J., Pogran, K. T)-.6 F 1.035 +(., and Henderson, D. A. Jr)-.74 F(.,)-.55 E/F1 10/Times-Italic@0 SF(Stan-) +3.535 E(dar)195 160.2 Q 2.627(df)-.37 G .127(or the F)218.927 160.2 R .127 +(ormat of ARP)-1.05 F 2.627(AN)-.9 G .128(etwork T)320.112 160.2 R -.2(ex)-.92 +G 2.628(tM).2 G(essa)377.018 160.2 Q -.1(ge)-.1 G(s.).1 E F0 .128 +(RFC 733, NIC 41952.)5.128 F(In [Feinler78].)195 172.2 Q(No)5 E -.15(ve)-.15 G +(mber 1977.).15 E([Crock)87 188.4 Q 56.73(er82] Crock)-.1 F(er)-.1 E 4.272(,D) +-.4 G 4.272(.H)240.702 188.4 S(.,)254.694 188.4 Q F1(Standar)4.272 E 4.272(df) +-.37 G 1.772(or the F)307.318 188.4 R 1.772(ormat of Arpa Internet T)-1.05 F +-.2(ex)-.92 G 4.271(tM).2 G(essa)471.15 188.4 Q -.1(ge)-.1 G(s.).1 E F0 .025 +(RFC 822.)195 200.4 R(Netw)5.025 E .025(ork Information Center)-.1 F 2.526(,S) +-.4 G .026(RI International, Menlo P)363.506 200.4 R .026(ark, Cali-)-.15 F 2.5 +(fornia. August)195 212.4 R(1982.)2.5 E 60.51([Feinler78] Feinler)87 228.6 R +2.938(,E)-.4 G .438(., and Postel, J.)234.478 228.6 R(\(eds.\),)5.438 E F1(ARP) +2.938 E .438(ANET Pr)-.9 F .438(otocol Handbook.)-.45 F F0 .438(NIC 7104,)5.438 +F(Netw)195 240.6 Q 3.011(ork Information Center)-.1 F 5.511(,S)-.4 G 3.012 +(RI International, Menlo P)328.513 240.6 R 3.012(ark, California.)-.15 F(1978.) +195 252.6 Q([No)87 268.8 Q 59.65(witz78] No)-.25 F .479 +(witz, D. A., and Lesk, M. E.,)-.25 F F1 2.978(AD)2.978 G .478 +(ial-Up Network of UNIX Systems.)344.67 268.8 R F0(Bell)5.478 E 3.528 +(Laboratories. In)195 280.8 R 1.029(UNIX Programmer')3.528 F 3.529(sM)-.55 G +1.029(anual, Se)363.524 280.8 R -.15(ve)-.25 G 1.029(nth Edition, V).15 F 1.029 +(olume 2.)-1.29 F(August, 1978.)195 292.8 Q 55.5([Schmidt79] Schmidt,)87 309 R +(E.,)2.631 E F1 .131(An Intr)2.631 F .131(oduction to the Berk)-.45 F(ele)-.1 E +2.631(yN)-.3 G(etwork.)382.277 309 Q F0(Uni)5.131 E -.15(ve)-.25 G .131 +(rsity of Califor).15 F(-)-.2 E(nia, Berk)195 321 Q(ele)-.1 E 2.5(yC)-.15 G 2.5 +(alifornia. 1979.)257.24 321 R 59.95([Shoens79] Shoens,)87 337.2 R(K.,)3.227 E +F1 .728(Mail Refer)3.227 F .728(ence Manual.)-.37 F F0(Uni)5.728 E -.15(ve)-.25 +G .728(rsity of California, Berk).15 F(ele)-.1 E 4.528 -.65(y. I)-.15 H(n).65 E +3.478(UNIX Programmer')195 349.2 R 5.977(sM)-.55 G 3.477(anual, Se)297.495 +349.2 R -.15(ve)-.25 G 3.477(nth Edition, V).15 F 3.477(olume 2C.)-1.29 F +(December)8.477 E(1979.)195 361.2 Q 52.72([Solomon81] Solomon,)87 377.4 R .251 +(M., Landweber)2.75 F 2.751(,L)-.4 G .251(., and Neuhengen, D.,)308.952 377.4 R +F1 .251(The Design of the CSNET)2.751 F .397(Name Server)195 389.4 R(.)-1.11 E +F0 2.896(CS-DN-2. Uni)5.397 F -.15(ve)-.25 G .396(rsity of W).15 F .396 +(isconsin, Madison.)-.4 F .396(October 1981.)5.396 F 73.84([Su82a] Su,)87 405.6 +R(Za)2.844 E .344(w-Sing, and Postel, Jon,)-.15 F F1 .344 +(The Domain Naming Con)2.844 F .344(vention for Internet)-.4 F 2.71 +(User Applications.)195 417.6 R F0 5.21(RFC819. Netw)7.71 F 2.71 +(ork Information Center)-.1 F 5.21(,S)-.4 G 2.71(RI Interna-)457.14 417.6 R +(tional, Menlo P)195 429.6 Q(ark, California.)-.15 E(August 1982.)5 E 73.28 +([Su82b] Su,)87 445.8 R(Za)4.174 E(w-Sing,)-.15 E F1 4.174(AD)4.174 G(istrib) +275.702 445.8 Q 1.675(uted System for Internet Name Service)-.2 F(.)-.15 E F0 +(RFC830.)6.675 E(Netw)195 457.8 Q 3.012(ork Information Center)-.1 F 5.512(,S) +-.4 G 3.011(RI International, Menlo P)328.516 457.8 R 3.011(ark, California.) +-.15 F(October 1982.)195 469.8 Q/F2 10/Times-Bold@0 SF(Mail Systems and Addr)72 +756 Q(essing in 4.2bsd)-.18 E(9)499 756 Q EP +%%Trailer +end +%%EOF diff --git a/usr.sbin/sendmail/mailstats/Makefile b/usr.sbin/sendmail/mailstats/Makefile index eda6ed4396..d6a04f0bdc 100644 --- a/usr.sbin/sendmail/mailstats/Makefile +++ b/usr.sbin/sendmail/mailstats/Makefile @@ -1,4 +1,4 @@ -# @(#)Makefile 5.1 (Berkeley) 5/11/90 +# @(#)Makefile 8.1 (Berkeley) 6/7/93 PROG= mailstats CFLAGS+=-I${.CURDIR}/../src diff --git a/usr.sbin/sendmail/mailstats/mailstats.c b/usr.sbin/sendmail/mailstats/mailstats.c index 27c6994dfc..648f1c6877 100644 --- a/usr.sbin/sendmail/mailstats/mailstats.c +++ b/usr.sbin/sendmail/mailstats/mailstats.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -34,19 +34,21 @@ */ #ifndef lint -char copyright[] = -"@(#) Copyright (c) 1988 Regents of the University of California.\n\ - All rights reserved.\n"; +static char copyright[] = +"@(#) Copyright (c) 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)mailstats.c 5.7 (Berkeley) 6/1/90"; +static char sccsid[] = "@(#)mailstats.c 8.1 (Berkeley) 6/7/93"; #endif /* not lint */ #include <sys/file.h> #include <sendmail.h> #include <mailstats.h> -#include "pathnames.h" +#include <pathnames.h> + +#define MNAMELEN 20 /* max length of mailer name */ main(argc, argv) int argc; @@ -56,40 +58,145 @@ main(argc, argv) extern int optind; struct statistics stat; register int i; + int mno; int ch, fd; - char *sfile, *ctime(); + char *sfile; + char *cfile; + FILE *cfp; + bool mnames; + char mtable[MAXMAILERS][MNAMELEN+1]; + char sfilebuf[100]; + char buf[MAXLINE]; + extern char *ctime(); + + cfile = _PATH_SENDMAILCF; + sfile = NULL; + mnames = TRUE; + while ((ch = getopt(argc, argv, "C:f:o")) != EOF) + { + switch (ch) + { + case 'C': + cfile = optarg; + break; - sfile = _PATH_MAILSTATS; - while ((ch = getopt(argc, argv, "f:")) != EOF) - switch((char)ch) { - case 'f': + case 'f': sfile = optarg; break; - case '?': - default: - fputs("usage: mailstats [-f file]\n", stderr); + + case 'o': + mnames = FALSE; + break; + + case '?': + default: + usage: + fputs("usage: mailstats [-C cffile] [-f stfile]\n", stderr); exit(EX_USAGE); } + } argc -= optind; argv += optind; + if (argc != 0) + goto usage; + + if ((cfp = fopen(cfile, "r")) == NULL) + { + fprintf(stderr, "mailstats: "); + perror(cfile); + exit(EX_NOINPUT); + } + + mno = 0; + (void) strcpy(mtable[mno++], "prog"); + (void) strcpy(mtable[mno++], "*file*"); + (void) strcpy(mtable[mno++], "*include*"); + + while (fgets(buf, sizeof(buf), cfp) != NULL) + { + register char *b; + char *s; + register char *m; + + b = buf; + switch (*b++) + { + case 'M': /* mailer definition */ + break; + + case 'O': /* option -- see if .st file */ + if (*b++ != 'S') + continue; + + /* yep -- save this */ + strcpy(sfilebuf, b); + b = strchr(sfilebuf, '\n'); + if (b != NULL) + *b = '\0'; + if (sfile == NULL) + sfile = sfilebuf; + + default: + continue; + } + + if (mno >= MAXMAILERS) + { + fprintf(stderr, + "Too many mailers defined, %d max.\n", + MAXMAILERS); + exit(EX_SOFTWARE); + } + m = mtable[mno]; + s = m + MNAMELEN; /* is [MNAMELEN+1] */ + while (*b != ',' && !isspace(*b) && *b != '\0' && m < s) + *m++ = *b++; + *m = '\0'; + for (i = 0; i < mno; i++) + { + if (strcmp(mtable[i], mtable[mno]) == 0) + break; + } + if (i == mno) + mno++; + } + (void) fclose(cfp); + for (; mno < MAXMAILERS; mno++) + mtable[mno][0]='\0'; + + if (sfile == NULL) + { + fprintf(stderr, "mailstats: no statistics file located\n"); + exit (EX_OSFILE); + } + if ((fd = open(sfile, O_RDONLY)) < 0) { fputs("mailstats: ", stderr); perror(sfile); exit(EX_NOINPUT); } if (read(fd, &stat, sizeof(stat)) != sizeof(stat) || - stat.stat_size != sizeof(stat)) { + stat.stat_size != sizeof(stat)) + { fputs("mailstats: file size changed.\n", stderr); exit(EX_OSERR); } printf("Statistics from %s", ctime(&stat.stat_itime)); - printf(" M msgsfr bytes_from msgsto bytes_to\n"); + printf(" M msgsfr bytes_from msgsto bytes_to%s\n", + mnames ? " Mailer" : ""); for (i = 0; i < MAXMAILERS; i++) + { if (stat.stat_nf[i] || stat.stat_nt[i]) - printf("%2d %6ld %10ldK %6ld %10ldK\n", i, + { + printf("%2d %6ld %10ldK %6ld %10ldK", i, stat.stat_nf[i], stat.stat_bf[i], stat.stat_nt[i], stat.stat_bt[i]); - exit(0); + if (mnames) + printf(" %s", mtable[i]); + printf("\n"); + } + } + exit(EX_OK); } diff --git a/usr.sbin/sendmail/mailstats/pathnames.h b/usr.sbin/sendmail/mailstats/pathnames.h deleted file mode 100644 index c8f00407d8..0000000000 --- a/usr.sbin/sendmail/mailstats/pathnames.h +++ /dev/null @@ -1,36 +0,0 @@ -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)pathnames.h 5.1 (Berkeley) 4/29/90 - */ - -#define _PATH_MAILSTATS "/var/log/sendmail.st" diff --git a/usr.sbin/sendmail/makemap/Makefile b/usr.sbin/sendmail/makemap/Makefile new file mode 100644 index 0000000000..1cc9b5970d --- /dev/null +++ b/usr.sbin/sendmail/makemap/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.1 (Berkeley) 6/7/93 + +PROG= makemap +MAN8= makemap.8 +CFLAGS+=-I${.CURDIR}/../src -DNEWDB + +.include "../../Makefile.inc" +.include <bsd.prog.mk> diff --git a/usr.sbin/sendmail/makemap/makemap.8 b/usr.sbin/sendmail/makemap/makemap.8 new file mode 100644 index 0000000000..70ee4465f3 --- /dev/null +++ b/usr.sbin/sendmail/makemap/makemap.8 @@ -0,0 +1,127 @@ +.\" Copyright (c) 1988, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)makemap.8 8.1 (Berkeley) 6/17/93 +.\" +.Dd November 16, 1992 +.Dt MAKEMAP 8 +.Os BSD 4.4 +.Sh NAME +.Nm makemap +.Nd create database maps for sendmail +.Sh SYNOPSIS +.Nm +.Op Fl N +.Op Fl f +.Op Fl o +.Op Fl r +.Op Fl v +.Ar maptype +.Ar mapname +.Sh DESCRIPTION +.Nm +creates the database maps used by the keyed map lookups in +.Xr sendmail 8 . +It reads input from the standard input +and outputs them to the indicated +.Ar mapname . +.Pp +Depending on how it is compiled, +.Nm +handles up to three different database formats, +selected using the +.Ar maptype +parameter. +They may be +.Bl -tag -width Fl +.It Li dbm +DBM format maps. +This requires the +.Xr ndbm 3 +library. +.It Li btree +B-Tree format maps. +This requires the new Berkeley +.Xr db 3 +library. +.It Li hash +Hash format maps. +This also requires the +.Xr db 3 +library. +.El +.Pp +In all cases, +.Nm +reads lines from the standard input consisting of two +words separated by white space. +The first is the database key, +the second is the value. +The value may contain +``%\fIn\fP'' +strings to indicated parameter substitution. +Literal parentheses should be doubled +(``%%''). +Blank lines and lines beginning with ``#'' are ignored. +.Ss Flags +.Bl -tag -width Fl +.It Fl N +Include the null byte that terminates strings +in the map. +This must match the \-N flag in the sendmail.cf +``K'' line. +.It Fl f +Fold all upper case letters in the key +to lower case; +this is intended to mesh with the +\-f flag in the +\fBK\fP +line in sendmail.cf. +The value is not case folded. +.It Fl o +Append to an old file. +This allows you to augment an existing file. +.It Fl r +Allow replacement of existing keys. +Normally +.Nm +complains if you repeat a key, +and does not do the insert. +.It Fl v +Verbosely print what it is doing. +.El +.Sh SEE ALSO +.Xr sendmail 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.4 . diff --git a/usr.sbin/sendmail/makemap/makemap.c b/usr.sbin/sendmail/makemap/makemap.c new file mode 100644 index 0000000000..7996c1ac8a --- /dev/null +++ b/usr.sbin/sendmail/makemap/makemap.c @@ -0,0 +1,336 @@ +/* + * Copyright (c) 1992 Eric P. Allman. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)makemap.c 8.1 (Berkeley) 6/7/93"; +#endif /* not lint */ + +#include <stdio.h> +#include <sysexits.h> +#include <sys/file.h> +#include <ctype.h> +#include <string.h> +#include "useful.h" +#include "conf.h" + +#ifdef NDBM +#include <ndbm.h> +#endif + +#ifdef NEWDB +#include <db.h> +#endif + +enum type { T_DBM, T_BTREE, T_HASH, T_ERR, T_UNKNOWN }; + +union dbent +{ +#ifdef NDBM + datum dbm; +#endif +#ifdef NEWDB + DBT db; +#endif + struct + { + char *data; + int size; + } xx; +}; + +#define BUFSIZE 1024 + +main(argc, argv) + int argc; + char **argv; +{ + char *progname; + bool inclnull = FALSE; + bool notrunc = FALSE; + bool allowreplace = FALSE; + bool verbose = FALSE; + bool foldcase = FALSE; + int exitstat; + int opt; + char *typename; + char *mapname; + int lineno; + int st; + int mode; + enum type type; + union + { +#ifdef NDBM + DBM *dbm; +#endif +#ifdef NEWDB + DB *db; +#endif + void *dbx; + } dbp; + union dbent key, val; + char ibuf[BUFSIZE]; + extern char *optarg; + extern int optind; + + progname = argv[0]; + + while ((opt = getopt(argc, argv, "Nforv")) != EOF) + { + switch (opt) + { + case 'N': + inclnull = TRUE; + break; + + case 'f': + foldcase = TRUE; + break; + + case 'o': + notrunc = TRUE; + break; + + case 'r': + allowreplace = TRUE; + break; + + case 'v': + verbose = TRUE; + break; + + default: + type = T_ERR; + break; + } + } + + argc -= optind; + argv += optind; + if (argc != 2) + type = T_ERR; + else + { + typename = argv[0]; + mapname = argv[1]; + + if (strcmp(typename, "dbm") == 0) + type = T_DBM; + else if (strcmp(typename, "btree") == 0) + type = T_BTREE; + else if (strcmp(typename, "hash") == 0) + type = T_HASH; + else + type = T_UNKNOWN; + } + + switch (type) + { + case T_ERR: + fprintf(stderr, "Usage: %s [-N] [-o] [-v] type mapname\n", progname); + exit(EX_USAGE); + + case T_UNKNOWN: + fprintf(stderr, "%s: Unknown database type %s\n", + progname, typename); + exit(EX_USAGE); + +#ifndef NDBM + case T_DBM: +#endif +#ifndef NEWDB + case T_BTREE: + case T_HASH: +#endif + fprintf(stderr, "%s: Type %s not supported in this version\n", + progname, typename); + exit(EX_UNAVAILABLE); + } + + /* + ** Create the database. + */ + + mode = O_RDWR; + if (!notrunc) + mode |= O_CREAT|O_TRUNC; + switch (type) + { +#ifdef NDBM + case T_DBM: + dbp.dbm = dbm_open(mapname, mode, 0644); + break; +#endif + +#ifdef NEWDB + case T_HASH: + dbp.db = dbopen(mapname, mode, 0644, DB_HASH, NULL); + break; + + case T_BTREE: + dbp.db = dbopen(mapname, mode, 0644, DB_BTREE, NULL); + break; +#endif + + default: + fprintf(stderr, "%s: internal error: type %d\n", progname, type); + exit(EX_SOFTWARE); + } + + if (dbp.dbx == NULL) + { + fprintf(stderr, "%s: cannot create type %s map %s\n", + progname, typename, mapname); + exit(EX_CANTCREAT); + } + + /* + ** Copy the data + */ + + lineno = 0; + exitstat = EX_OK; + while (fgets(ibuf, sizeof ibuf, stdin) != NULL) + { + register char *p; + + lineno++; + + /* + ** Parse the line. + */ + + p = strchr(ibuf, '\n'); + if (*p != '\0') + *p = '\0'; + if (ibuf[0] == '\0' || ibuf[0] == '#') + continue; + if (isspace(ibuf[0])) + { + fprintf(stderr, "%s: %s: line %d: syntax error (leading space)\n", + progname, mapname, lineno); + continue; + } + key.xx.data = ibuf; + for (p = ibuf; *p != '\0' && !isspace(*p); p++) + { + if (foldcase && isupper(*p)) + *p = tolower(*p); + } + key.xx.size = p - key.xx.data; + if (inclnull) + key.xx.size++; + if (*p != '\0') + *p++ = '\0'; + while (isspace(*p)) + p++; + if (*p == '\0') + { + fprintf(stderr, "%s: %s: line %d: no RHS for LHS %s\n", + progname, mapname, lineno, key.xx.data); + continue; + } + val.xx.data = p; + val.xx.size = strlen(p); + if (inclnull) + val.xx.size++; + + /* + ** Do the database insert. + */ + + if (verbose) + { + printf("key=`%s', val=`%s'\n", key.xx.data, val.xx.data); + } + + switch (type) + { +#ifdef NDBM + case T_DBM: + st = dbm_store(dbp.dbm, key.dbm, val.dbm, + allowreplace ? DBM_REPLACE : DBM_INSERT); + break; +#endif + +#ifdef NEWDB + case T_BTREE: + case T_HASH: + st = (*dbp.db->put)(dbp.db, &key.db, &val.db, + allowreplace ? 0 : R_NOOVERWRITE); + break; +#endif + } + + if (st < 0) + { + fprintf(stderr, "%s: %s: line %d: key %s: put error\n", + progname, mapname, lineno, key.xx.data); + perror(mapname); + exitstat = EX_IOERR; + } + else if (st > 0) + { + fprintf(stderr, "%s: %s: line %d: key %s: duplicate key\n", + progname, mapname, lineno, key.xx.data); + } + } + + /* + ** Now close the database. + */ + + switch (type) + { +#ifdef NDBM + case T_DBM: + dbm_close(dbp.dbm); + break; +#endif + +#ifdef NEWDB + case T_HASH: + case T_BTREE: + if ((*dbp.db->close)(dbp.db) < 0) + { + fprintf(stderr, "%s: %s: error on close\n", + progname, mapname); + perror(mapname); + exitstat = EX_IOERR; + } +#endif + } + + exit (exitstat); +} diff --git a/usr.sbin/sendmail/praliases/Makefile b/usr.sbin/sendmail/praliases/Makefile index a90f2d8fc7..498e1710bf 100644 --- a/usr.sbin/sendmail/praliases/Makefile +++ b/usr.sbin/sendmail/praliases/Makefile @@ -1,9 +1,8 @@ -# @(#)Makefile 5.1 (Berkeley) 5/11/90 +# @(#)Makefile 8.1 (Berkeley) 6/7/93 PROG= praliases CFLAGS+=-I${.CURDIR}/../src DPADD= ${LIBDBM} -LDADD= -ldbm NOMAN= noman .include "../../Makefile.inc" diff --git a/usr.sbin/sendmail/praliases/praliases.c b/usr.sbin/sendmail/praliases/praliases.c index 9217b6803f..b637cda551 100644 --- a/usr.sbin/sendmail/praliases/praliases.c +++ b/usr.sbin/sendmail/praliases/praliases.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,32 +33,39 @@ */ #ifndef lint -char copyright[] = -"@(#) Copyright (c) 1988 Regents of the University of California.\n\ - All rights reserved.\n"; +static char copyright[] = +"@(#) Copyright (c) 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)praliases.c 5.5 (Berkeley) 6/1/90"; +static char sccsid[] = "@(#)praliases.c 8.1 (Berkeley) 6/7/93"; #endif /* not lint */ +#include <ndbm.h> #include <sendmail.h> +#ifdef NEWDB +#include <db.h> +#endif -typedef struct { - char *dptr; - int dsize; -} datum; - - +int main(argc, argv) + int argc; char **argv; { extern char *optarg; extern int optind; - static char *filename = "/usr/lib/aliases"; - datum content, key, firstkey(), nextkey(), fetch(); + DBM *dbp; + datum content, key; + char *filename; int ch; +#ifdef NEWDB + const DB *db; + DBT newdbkey, newdbcontent; + char buf[MAXNAME]; +#endif + filename = "/etc/aliases"; while ((ch = getopt(argc, argv, "f:")) != EOF) switch((char)ch) { case 'f': @@ -66,27 +73,56 @@ main(argc, argv) break; case '?': default: - fputs("usage: praliases [-f file]\n", stderr); + (void)fprintf(stderr, "usage: praliases [-f file]\n"); exit(EX_USAGE); } argc -= optind; argv += optind; - if (dbminit(filename) < 0) - exit(EX_OSFILE); - if (!argc) - for (key = firstkey(); key.dptr; key = nextkey(key)) { - content = fetch(key); - printf("%s:%s\n", key.dptr, content.dptr); +#ifdef NEWDB + (void) strcpy(buf, filename); + (void) strcat(buf, ".db"); + if (db = dbopen(buf, O_RDONLY, 0444 , DB_HASH, NULL)) { + if (!argc) { + while(!db->seq(db, &newdbkey, &newdbcontent, R_NEXT)) + printf("%s:%s\n", newdbkey.data, + newdbcontent.data); + } + else for (; *argv; ++argv) { + newdbkey.data = *argv; + newdbkey.size = strlen(*argv) + 1; + if ( !db->get(db, &newdbkey, &newdbcontent, 0) ) + printf("%s:%s\n", newdbkey.data, + newdbcontent.data); + else + printf("%s: No such key\n", + newdbkey.data); + } + } + else { +#endif + if ((dbp = dbm_open(filename, O_RDONLY, 0)) == NULL) { + (void)fprintf(stderr, + "praliases: %s: %s\n", filename, strerror(errno)); + exit(EX_OSFILE); + } + if (!argc) + for (key = dbm_nextkey(dbp); + key.dptr != NULL; key = dbm_nextkey(dbp)) { + content = dbm_fetch(dbp, key); + (void)printf("%s:%s\n", key.dptr, content.dptr); + } + else for (; *argv; ++argv) { + key.dptr = *argv; + key.dsize = strlen(*argv) + 1; + content = dbm_fetch(dbp, key); + if (!content.dptr) + (void)printf("%s: No such key\n", key.dptr); + else + (void)printf("%s:%s\n", key.dptr, content.dptr); } - else for (; *argv; ++argv) { - key.dptr = *argv; - key.dsize = strlen(*argv) + 1; - content = fetch(key); - if (!content.dptr) - printf("%s: No such key\n", key.dptr); - else - printf("%s:%s\n", key.dptr, content.dptr); +#ifdef NEWDB } +#endif exit(EX_OK); } diff --git a/usr.sbin/sendmail/rmail/Makefile b/usr.sbin/sendmail/rmail/Makefile new file mode 100644 index 0000000000..eb2fb48008 --- /dev/null +++ b/usr.sbin/sendmail/rmail/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= rmail +MAN8= rmail.0 + +.include <bsd.prog.mk> diff --git a/usr.sbin/sendmail/rmail/rmail.8 b/usr.sbin/sendmail/rmail/rmail.8 new file mode 100644 index 0000000000..2079d4e9f8 --- /dev/null +++ b/usr.sbin/sendmail/rmail/rmail.8 @@ -0,0 +1,71 @@ +.\" Copyright (c) 1983, 1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rmail.8 6.10 (Berkeley) 4/29/93 +.\" +.Dd April 29, 1993 +.Dt RMAIL 8 +.Os BSD 4.2 +.Sh NAME +.Nm rmail +.Nd handle remote mail received via uucp +.Sh SYNOPSIS +.Nm rmail +.Ar user ... +.Sh DESCRIPTION +.Nm Rmail +interprets incoming mail received via +.Xr uucp 1 , +collapsing ``From'' lines in the form generated +by +.Xr mail.local 8 +into a single line of the form ``return-path!sender'', +and passing the processed mail on to +.Xr sendmail 8 . +.Pp +.Nm Rmail +is explicitly designed for use with +.Xr uucp +and +.Xr sendmail . +.Sh SEE ALSO +.Xr uucp 1 , +.Xr mail.local 8 , +.Xr sendmail 8 +.Sh HISTORY +The +.Nm rmail +program appeared in +.Bx 4.2 . +.Sh BUGS +.Nm Rmail +should not reside in +.Pa /bin . diff --git a/usr.sbin/sendmail/rmail/rmail.c b/usr.sbin/sendmail/rmail/rmail.c new file mode 100644 index 0000000000..aac400a396 --- /dev/null +++ b/usr.sbin/sendmail/rmail/rmail.c @@ -0,0 +1,359 @@ +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rmail.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * RMAIL -- UUCP mail server. + * + * This program reads the >From ... remote from ... lines that UUCP is so + * fond of and turns them into something reasonable. It then execs sendmail + * with various options built from these lines. + * + * The expected syntax is: + * + * <user> := [-a-z0-9]+ + * <date> := ctime format + * <site> := [-a-z0-9!]+ + * <blank line> := "^\n$" + * <from> := "From" <space> <user> <space> <date> + * [<space> "remote from" <space> <site>] + * <forward> := ">" <from> + * msg := <from> <forward>* <blank-line> <body> + * + * The output of rmail(8) compresses the <forward> lines into a single + * from path. + * + * The err(3) routine is included here deliberately to make this code + * a bit more portable. + */ +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <ctype.h> +#include <fcntl.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +void err __P((int, const char *, ...)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int errno, optind; + FILE *fp; + struct stat sb; + size_t fplen, fptlen, len; + off_t offset; + int ch, debug, i, pdes[2], pid, status; + char *addrp, *domain, *p, *t; + char *from_path, *from_sys, *from_user; + char *args[100], buf[2048], lbuf[2048]; + + debug = 0; + domain = "UUCP"; /* Default "domain". */ + while ((ch = getopt(argc, argv, "D:T")) != EOF) + switch (ch) { + case 'T': + debug = 1; + break; + case 'D': + domain = optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + from_path = from_sys = from_user = NULL; + for (offset = 0;;) { + + /* Get and nul-terminate the line. */ + if (fgets(lbuf, sizeof(lbuf), stdin) == NULL) + exit (EX_DATAERR); + if ((p = strchr(lbuf, '\n')) == NULL) + err(EX_DATAERR, "line too long"); + *p = '\0'; + + /* Parse lines until reach a non-"From" line. */ + if (!strncmp(lbuf, "From ", 5)) + addrp = lbuf + 5; + else if (!strncmp(lbuf, ">From ", 6)) + addrp = lbuf + 6; + else if (offset == 0) + err(EX_DATAERR, + "missing or empty From line: %s", lbuf); + else { + *p = '\n'; + break; + } + + if (*addrp == '\0') + err(EX_DATAERR, "corrupted From line: %s", lbuf); + + /* Use the "remote from" if it exists. */ + for (p = addrp; (p = strchr(p + 1, 'r')) != NULL;) + if (!strncmp(p, "remote from ", 12)) { + for (t = p += 12; *t && !isspace(*t); ++t); + *t = '\0'; + if (debug) + (void)fprintf(stderr, + "remote from: %s\n", p); + break; + } + + /* Else use the string up to the last bang. */ + if (p == NULL) + if (*addrp == '!') + err(EX_DATAERR, + "bang starts address: %s", addrp); + else if ((t = strrchr(addrp, '!')) != NULL) { + *t = '\0'; + p = addrp; + addrp = t + 1; + if (*addrp == '\0') + err(EX_DATAERR, + "corrupted From line: %s", lbuf); + if (debug) + (void)fprintf(stderr, "bang: %s\n", p); + } + + /* 'p' now points to any system string from this line. */ + if (p != NULL) { + /* Nul terminate it as necessary. */ + for (t = p; *t && !isspace(*t); ++t); + *t = '\0'; + + /* If the first system, copy to the from_sys string. */ + if (from_sys == NULL) { + if ((from_sys = strdup(p)) == NULL) + err(EX_TEMPFAIL, NULL); + if (debug) + (void)fprintf(stderr, + "from_sys: %s\n", from_sys); + } + + /* Concatenate to the path string. */ + len = t - p; + if (from_path == NULL) { + fplen = 0; + if ((from_path = malloc(fptlen = 256)) == NULL) + err(EX_TEMPFAIL, NULL); + } + if (fplen + len + 2 > fptlen) { + fptlen += MAX(fplen + len + 2, 256); + if ((from_path = + realloc(from_path, fptlen)) == NULL) + err(EX_TEMPFAIL, NULL); + } + memmove(from_path + fplen, p, len); + fplen += len; + from_path[fplen++] = '!'; + from_path[fplen] = '\0'; + } + + /* Save off from user's address; the last one wins. */ + for (p = addrp; *p && !isspace(*p); ++p); + *p = '\0'; + if (from_user != NULL) + free(from_user); + if ((from_user = strdup(addrp)) == NULL) + err(EX_TEMPFAIL, NULL); + + if (debug) { + if (from_path != NULL) + (void)fprintf(stderr, + "from_path: %s\n", from_path); + (void)fprintf(stderr, "from_user: %s\n", from_user); + } + + if (offset != -1) + offset = (off_t)ftell(stdin); + } + + i = 0; + args[i++] = _PATH_SENDMAIL; /* Build sendmail's argument list. */ + args[i++] = "-oee"; /* No errors, just status. */ + args[i++] = "-odq"; /* Queue it, don't try to deliver. */ + args[i++] = "-oi"; /* Ignore '.' on a line by itself. */ + + if (from_sys != NULL) { /* Set sender's host name. */ + if (strchr(from_sys, '.') == NULL) + (void)snprintf(buf, sizeof(buf), + "-oMs%s.%s", from_sys, domain); + else + (void)snprintf(buf, sizeof(buf), "-oMs%s", from_sys); + if ((args[i++] = strdup(buf)) == NULL) + err(EX_TEMPFAIL, NULL); + } + /* Set protocol used. */ + (void)snprintf(buf, sizeof(buf), "-oMr%s", domain); + if ((args[i++] = strdup(buf)) == NULL) + err(EX_TEMPFAIL, NULL); + + /* Set name of ``from'' person. */ + (void)snprintf(buf, sizeof(buf), "-f%s%s", + from_path ? from_path : "", from_user); + if ((args[i++] = strdup(buf)) == NULL) + err(EX_TEMPFAIL, NULL); + + /* + * Don't copy arguments beginning with - as they will be + * passed to sendmail and could be interpreted as flags. + */ + do { + if (*argv && **argv == '-') + err(EX_USAGE, "dash precedes argument: %s", *argv); + } while ((args[i++] = *argv++) != NULL); + + if (debug) { + (void)fprintf(stderr, "Sendmail arguments:\n"); + for (i = 0; args[i]; i++) + (void)fprintf(stderr, "\t%s\n", args[i]); + } + + /* + * If called with a regular file as standard input, seek to the right + * position in the file and just exec sendmail. Could probably skip + * skip the stat, but it's not unreasonable to believe that a failed + * seek will cause future reads to fail. + */ + if (!fstat(STDIN_FILENO, &sb) && S_ISREG(sb.st_mode)) { + if (lseek(STDIN_FILENO, offset, SEEK_SET) != offset) + err(EX_TEMPFAIL, "stdin seek"); + execv(_PATH_SENDMAIL, args); + err(EX_OSERR, "%s", _PATH_SENDMAIL); + } + + if (pipe(pdes) < 0) + err(EX_OSERR, NULL); + + switch (pid = vfork()) { + case -1: /* Err. */ + err(EX_OSERR, NULL); + case 0: /* Child. */ + if (pdes[0] != STDIN_FILENO) { + (void)dup2(pdes[0], STDIN_FILENO); + (void)close(pdes[0]); + } + (void)close(pdes[1]); + execv(_PATH_SENDMAIL, args); + _exit(127); + /* NOTREACHED */ + } + + if ((fp = fdopen(pdes[1], "w")) == NULL) + err(EX_OSERR, NULL); + (void)close(pdes[0]); + + /* Copy the file down the pipe. */ + do { + (void)fprintf(fp, "%s", lbuf); + } while (fgets(lbuf, sizeof(lbuf), stdin) != NULL); + + if (ferror(stdin)) + err(EX_TEMPFAIL, "stdin: %s", strerror(errno)); + + if (fclose(fp)) + err(EX_OSERR, NULL); + + if ((waitpid(pid, &status, 0)) == -1) + err(EX_OSERR, "%s", _PATH_SENDMAIL); + + if (!WIFEXITED(status)) + err(EX_OSERR, + "%s: did not terminate normally", _PATH_SENDMAIL); + + if (WEXITSTATUS(status)) + err(status, "%s: terminated with %d (non-zero) status", + _PATH_SENDMAIL, WEXITSTATUS(status)); + exit(EX_OK); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: rmail [-T] [-D domain] user ...\n"); + exit(EX_USAGE); +} + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#ifdef __STDC__ +err(int eval, const char *fmt, ...) +#else +err(eval, fmt, va_alist) + int eval; + const char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "rmail: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(eval); +} diff --git a/usr.sbin/sendmail/src/Makefile b/usr.sbin/sendmail/src/Makefile index 7ba330e1ee..f49a5b3e03 100644 --- a/usr.sbin/sendmail/src/Makefile +++ b/usr.sbin/sendmail/src/Makefile @@ -1,28 +1,39 @@ -# @(#)Makefile 5.21 (Berkeley) 3/9/91 +# @(#)Makefile 8.1 (Berkeley) 6/7/93 PROG= sendmail -CFLAGS+=-I${.CURDIR} -DVMUNIX -DUSE_DB +# define the database format to use for aliases et al. Can be -DNEWDB (for +# the new BSD database package -- this is preferred) or -DNDBM for the NDBM +# database package. The old putrescent V7 DBM package is no longer +# supported. +# You can define both NEWDB and NDBM during a transition period; old +# databases are read, but the new format will be used on any rebuilds. On +# really gnarly systems, you can set this to null; it will crawl like a high +# spiral snail, but it will work. +DBMDEF= -DNEWDB + +CFLAGS+=-I${.CURDIR} ${DBMDEF} -DNETISO -DNO_SYSCONF SRCS= alias.c arpadate.c clock.c collect.c conf.c convtime.c daemon.c \ - deliver.c domain.c envelope.c err.c headers.c macro.c main.c \ - parseaddr.c queue.c readcf.c recipient.c savemail.c srvrsmtp.c \ - stab.c stats.c sysexits.c trace.c usersmtp.c util.c version.c -DPADD= ${LIBUTIL} -LDADD= -lutil -MAN1= newaliases.0 -MAN5= aliases.0 -MAN8= sendmail.0 -LINKS= ${DESTDIR}/usr/sbin/sendmail ${DESTDIR}/usr/bin/newaliases \ - ${DESTDIR}/usr/sbin/sendmail ${DESTDIR}/usr/bin/mailq + deliver.c domain.c envelope.c err.c headers.c macro.c main.c map.c \ + mci.c parseaddr.c queue.c readcf.c recipient.c savemail.c srvrsmtp.c \ + stab.c stats.c sysexits.c trace.c udb.c usersmtp.c \ + util.c version.c +DPADD= ${LIBDBM} ${LIBCOMPAT} ${LIBUTIL} +LDADD= ${LIBUTIL} +MAN1= newaliases.1 +MAN5= aliases.5 +MAN8= sendmail.8 +LINKS= /usr/sbin/sendmail /usr/bin/newaliases \ + /usr/sbin/sendmail /usr/bin/mailq BINDIR= /usr/sbin BINOWN= root BINGRP= kmem BINMODE=6555 beforeinstall: - install -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \ - ${DESTDIR}/etc/sendmail.fc +# install -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \ +# ${DESTDIR}/etc/sendmail.fc install -c -o ${BINOWN} -g ${BINGRP} -m 666 /dev/null \ ${DESTDIR}/var/log/sendmail.st install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/sendmail.hf \ diff --git a/usr.sbin/sendmail/src/Makefile.AIX b/usr.sbin/sendmail/src/Makefile.AIX new file mode 100644 index 0000000000..d676e02a58 --- /dev/null +++ b/usr.sbin/sendmail/src/Makefile.AIX @@ -0,0 +1,99 @@ +# +# This Makefile is designed to work on the old "make" program. It does +# not use the obj subdirectory. It also does not install documentation +# automatically -- think of it as a quick start for sites that have the +# old make program (I recommend that you get and port the new make if you +# are going to be doing any signficant work on sendmail). +# +# This has been tested on Ultrix. +# +# @(#)Makefile.dist 6.5 (Berkeley) 2/26/93 +# + +# use O=-O (usual) or O=-g (debugging) +O= -g + +# define the database mechanism used for alias lookups: +# -DNDBM -- use new DBM +# -DNEWDB -- use new Berkeley DB +# -DNDBM -DNEWDB -- use both new DBM and new Berkeley DB +# -DNDBM -DNEWDB -DYPCOMPAT -- use both plus YP compatility +# The really old (V7) DBM library is no longer supported. +# If YPCOMPAT is defined and /var/yp/Makefile exists, sendmail will build +# both the NEWDB and DBM libraries (the DBM just for YP). +# +DBMDEF= -DNEWDB + +# define the load average calculation on your system: -DLA_TYPE=LA_INT, +# -DLA_TYPE=LA_FLOAT, -DLA_TYPE=LA_SUBR, or -DLA_TYPE=LA_ZERO +# leave undefined to use internal guess +#LADEF= -DLA_TYPE=LA_SUBR + +# define UNSETENV if you need to compile in a local version of setenv +ENVDEF= -D_AIX3 + +# see also conf.h for additional compilation flags + +# include directories +#INCDIRS=-I/usr/sww/include/db + +# library directories +#LIBDIRS=-L/usr/sww/lib + +# libraries required on your system +LIBS= -ldbm -ldb + +# location of sendmail binary (usually /usr/sbin or /usr/lib) +BINDIR= ${DESTDIR}/usr/sbin + +# location of sendmail.st file (usually /var/log or /usr/lib) +STDIR= ${DESTDIR}/var/log + +# location of sendmail.hf file (usually /usr/share/misc or /usr/lib) +HFDIR= ${DESTDIR}/usr/share/misc + +################### end of user configuration flags ###################### + +CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${LADEF} ${ENVDEF} + +OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \ + deliver.o domain.o envelope.o err.o headers.o macro.o main.o \ + map.o mci.o parseaddr.o queue.o readcf.o recipient.o \ + savemail.o srvrsmtp.o stab.o stats.o sysexits.o \ + trace.o udb.o usersmtp.o util.o version.o + +LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq +BINOWN= root +BINGRP= kmem +BINMODE=6555 + +sendmail: ${OBJS} + ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS} + +aliases.0: aliases.5 + nroff -h -mandoc aliases.5 > aliases.0 + +newaliases.0: newaliases.1 + nroff -h -mandoc newaliases.1 > newaliases.0 + +sendmail.0: sendmail.8 + nroff -h -mandoc sendmail.8 > sendmail.0 + +install: install-sendmail install-docs + +install-sendmail: sendmail + install -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR} + for i in ${LINKS}; do ; rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done + install -c -o ${BINOWN} -g ${BINGRP} -m 666 /dev/null \ + ${STDIR}/sendmail.st + install -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR} + +# doesn't actually install them -- you may want to install pre-nroff versions +install-docs: aliases.0 newaliases.0 sendmail.0 + +clean: + rm -f ${OBJS} sendmail aliases.0 newaliases.0 sendmail.0 + +# dependencies +# gross overkill, and yet still not quite enough.... +${OBJS}: sendmail.h conf.h diff --git a/usr.sbin/sendmail/src/Makefile.HPUX b/usr.sbin/sendmail/src/Makefile.HPUX new file mode 100644 index 0000000000..7394b15bbe --- /dev/null +++ b/usr.sbin/sendmail/src/Makefile.HPUX @@ -0,0 +1,107 @@ +# +# This Makefile is designed to work on the old "make" program. It does +# not use the obj subdirectory. It also does not install documentation +# automatically -- think of it as a quick start for sites that have the +# old make program (I recommend that you get and port the new make if you +# are going to be doing any signficant work on sendmail). +# +# This has been tested on Ultrix. +# +# @(#)Makefile.dist 6.5 (Berkeley) 2/26/93 +# + +# use O=-O (usual) or O=-g (debugging) +# +O is OK on 7xx, and 300xx at 9.0 +O= +O1 + +# define the database mechanism used for alias lookups: +# -DNDBM -- use new DBM +# -DNEWDB -- use new Berkeley DB +# -DNDBM -DNEWDB -- use both new DBM and new Berkeley DB +# -DNDBM -DNEWDB -DYPCOMPAT -- use both plus YP compatility +# The really old (V7) DBM library is no longer supported. +# If YPCOMPAT is defined and /var/yp/Makefile exists, sendmail will build +# both the NEWDB and DBM libraries (the DBM just for YP). +# +DBMDEF= -DNEWDB + +# define the load average calculation on your system: -DLA_TYPE=LA_INT, +# -DLA_TYPE=LA_FLOAT, -DLA_TYPE=LA_SUBR, or -DLA_TYPE=LA_ZERO +# leave undefined to use internal guess +#LADEF= -DLA_TYPE=LA_SUBR + +# define UNSETENV if you need to compile in a local version of setenv +#ENVDEF= -DUNSETENV + +# see also conf.h for additional compilation flags + +# include directories +INCDIRS=-I/usr/sww/include/db + +# library directories +LIBDIRS=-L/usr/sww/lib + +# libraries required on your system +LIBS= -ldb -ldbm + +# location of sendmail binary (usually /usr/sbin or /usr/lib) +BINDIR= ${DESTDIR}/usr/lib + +# location of sendmail.st file (usually /var/log or /usr/lib) +STDIR= ${DESTDIR}/var/log + +# location of sendmail.hf file (usually /usr/share/misc or /usr/lib) +HFDIR= ${DESTDIR}/usr/lib + +# Stirling - watch out for pollution of name space by sys/sysmacros.h +# - this hits definitions of m_flags in db.h +# currently only on 300 series. +# - sys/sysmacros.h is being included by machine/param.h +# - which is included by sys/param.h +XCFLAGS=-D_SYS_SYSMACROS_INCLUDED + +################### end of user configuration flags ###################### + +CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${LADEF} ${ENVDEF} ${XCFLAGS} + +OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \ + deliver.o domain.o envelope.o err.o headers.o macro.o main.o \ + map.o mci.o parseaddr.o queue.o readcf.o recipient.o \ + savemail.o srvrsmtp.o stab.o stats.o sysexits.o \ + trace.o udb.o usersmtp.o util.o version.o + +LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq +BINOWN= root +BINGRP= kmem +BINMODE=6555 + +sendmail: ${OBJS} + ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS} + +aliases.0: aliases.5 + nroff -h -mandoc aliases.5 > aliases.0 + +newaliases.0: newaliases.1 + nroff -h -mandoc newaliases.1 > newaliases.0 + +sendmail.0: sendmail.8 + nroff -h -mandoc sendmail.8 > sendmail.0 + +install: install-sendmail install-docs + +install-sendmail: sendmail + install -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR} + for i in ${LINKS}; do ; rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done + install -c -o ${BINOWN} -g ${BINGRP} -m 666 /dev/null \ + ${STDIR}/sendmail.st + install -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR} + +# doesn't actually install them -- you may want to install pre-nroff versions +install-docs: aliases.0 newaliases.0 sendmail.0 + +clean: + rm -f ${OBJS} sendmail aliases.0 newaliases.0 sendmail.0 + +# dependencies +# gross overkill, and yet still not quite enough.... +${OBJS}: sendmail.h conf.h diff --git a/usr.sbin/sendmail/src/Makefile.IRIX b/usr.sbin/sendmail/src/Makefile.IRIX new file mode 100644 index 0000000000..314fd0002a --- /dev/null +++ b/usr.sbin/sendmail/src/Makefile.IRIX @@ -0,0 +1,101 @@ +# +# This Makefile is designed to work on the old "make" program. It does +# not use the obj subdirectory. It also does not install documentation +# automatically -- think of it as a quick start for sites that have the +# old make program (I recommend that you get and port the new make if you +# are going to be doing any signficant work on sendmail). +# +# This has been tested on IRIX +# +# @(#)Makefile.dist 8.1 (Berkeley) 6/7/93 +# + +# use O=-O (usual) or O=-g (debugging) +O= -O +CC=gcc + +# define the database mechanisms available for map & alias lookups: +# -DNDBM -- use new DBM +# -DNEWDB -- use new Berkeley DB +# -DNDBM -DNEWDB -DYPCOMPAT -- use both plus YP compatility +# -DNIS -- include client NIS support +# The really old (V7) DBM library is no longer supported. +# If YPCOMPAT is defined and /var/yp/Makefile exists, sendmail will build +# both the NEWDB and DBM libraries (the DBM just for YP). +# +DBMDEF= -DNDBM + +# define the load average calculation on your system: -DLA_TYPE=LA_INT, +# -DLA_TYPE=LA_FLOAT, -DLA_TYPE=LA_SUBR, or -DLA_TYPE=LA_ZERO +# leave undefined to use internal guess +#LADEF= -DLA_TYPE=LA_SUBR + +# define UNSETENV if you need to compile in a local version of setenv +#ENVDEF= -DUNSETENV +ENVDEF= -DIRIX + +# see also conf.h for additional compilation flags + +# include directories +INCDIRS= + +# library directories +LIBDIRS= + +# libraries required on your system +LIBS= -lmld + +# location of sendmail binary (usually /usr/sbin or /usr/lib) +BINDIR= ${DESTDIR}/usr/lib + +# location of sendmail.st file (usually /var/log or /usr/lib) +STDIR= ${DESTDIR}/usr/lib + +# location of sendmail.hf file (usually /usr/share/misc or /usr/lib) +HFDIR= ${DESTDIR}/usr/lib + +################### end of user configuration flags ###################### + +CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${LADEF} ${ENVDEF} + +OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \ + deliver.o domain.o envelope.o err.o headers.o macro.o main.o \ + map.o mci.o parseaddr.o queue.o readcf.o recipient.o \ + savemail.o srvrsmtp.o stab.o stats.o sysexits.o \ + trace.o udb.o usersmtp.o util.o version.o + +LINKS= ${DESTDIR}/usr/bsd/newaliases ${DESTDIR}/usr/bsd/mailq +BINOWN= root +BINGRP= sys +BINMODE=6555 + +sendmail: ${OBJS} + ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS} + +aliases.0: aliases.5 + nroff -h -mandoc aliases.5 > aliases.0 + +newaliases.0: newaliases.1 + nroff -h -mandoc newaliases.1 > newaliases.0 + +sendmail.0: sendmail.8 + nroff -h -mandoc sendmail.8 > sendmail.0 + +install: install-sendmail install-docs + +install-sendmail: sendmail + install -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR} + for i in ${LINKS}; do ; rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done + install -c -o ${BINOWN} -g ${BINGRP} -m 666 /dev/null \ + ${STDIR}/sendmail.st + install -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR} + +# doesn't actually install them -- you may want to install pre-nroff versions +install-docs: aliases.0 newaliases.0 sendmail.0 + +clean: + rm -f ${OBJS} sendmail aliases.0 newaliases.0 sendmail.0 + +# dependencies +# gross overkill, and yet still not quite enough.... +${OBJS}: sendmail.h conf.h diff --git a/usr.sbin/sendmail/src/Makefile.OSF1 b/usr.sbin/sendmail/src/Makefile.OSF1 new file mode 100644 index 0000000000..dc5a425e75 --- /dev/null +++ b/usr.sbin/sendmail/src/Makefile.OSF1 @@ -0,0 +1,99 @@ +# +# This Makefile is designed to work on the old "make" program. It does +# not use the obj subdirectory. It also does not install documentation +# automatically -- think of it as a quick start for sites that have the +# old make program (I recommend that you get and port the new make if you +# are going to be doing any signficant work on sendmail). +# +# This has been tested on Ultrix. +# +# @(#)Makefile.dist 6.5 (Berkeley) 2/26/93 +# + +# use O=-O (usual) or O=-g (debugging) +O= -O + +# define the database mechanism used for alias lookups: +# -DNDBM -- use new DBM +# -DNEWDB -- use new Berkeley DB +# -DNDBM -DNEWDB -- use both new DBM and new Berkeley DB +# -DNDBM -DNEWDB -DYPCOMPAT -- use both plus YP compatility +# The really old (V7) DBM library is no longer supported. +# If YPCOMPAT is defined and /var/yp/Makefile exists, sendmail will build +# both the NEWDB and DBM libraries (the DBM just for YP). +# +DBMDEF= -DNDBM + +# define the load average calculation on your system: -DLA_TYPE=LA_INT, +# -DLA_TYPE=LA_FLOAT, -DLA_TYPE=LA_SUBR, or -DLA_TYPE=LA_ZERO +# leave undefined to use internal guess +#LADEF= -DLA_TYPE=LA_SUBR + +# define UNSETENV if you need to compile in a local version of setenv +#ENVDEF= -DUNSETENV + +# see also conf.h for additional compilation flags + +# include directories +INCDIRS=-I/usr/sww/include/db + +# library directories +LIBDIRS=-L/usr/sww/lib + +# libraries required on your system +LIBS= -ldbm + +# location of sendmail binary (usually /usr/sbin or /usr/lib) +BINDIR= ${DESTDIR}/usr/sbin + +# location of sendmail.st file (usually /var/log or /usr/lib) +STDIR= ${DESTDIR}/var/log + +# location of sendmail.hf file (usually /usr/share/misc or /usr/lib) +HFDIR= ${DESTDIR}/usr/share/misc + +################### end of user configuration flags ###################### + +CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${LADEF} ${ENVDEF} + +OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \ + deliver.o domain.o envelope.o err.o headers.o macro.o main.o \ + map.o mci.o parseaddr.o queue.o readcf.o recipient.o \ + savemail.o srvrsmtp.o stab.o stats.o sysexits.o \ + trace.o udb.o usersmtp.o util.o version.o + +LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq +BINOWN= root +BINGRP= kmem +BINMODE=6555 + +sendmail: ${OBJS} + ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS} + +aliases.0: aliases.5 + nroff -h -mandoc aliases.5 > aliases.0 + +newaliases.0: newaliases.1 + nroff -h -mandoc newaliases.1 > newaliases.0 + +sendmail.0: sendmail.8 + nroff -h -mandoc sendmail.8 > sendmail.0 + +install: install-sendmail install-docs + +install-sendmail: sendmail + install -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR} + for i in ${LINKS}; do ; rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done + install -c -o ${BINOWN} -g ${BINGRP} -m 666 /dev/null \ + ${STDIR}/sendmail.st + install -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR} + +# doesn't actually install them -- you may want to install pre-nroff versions +install-docs: aliases.0 newaliases.0 sendmail.0 + +clean: + rm -f ${OBJS} sendmail aliases.0 newaliases.0 sendmail.0 + +# dependencies +# gross overkill, and yet still not quite enough.... +${OBJS}: sendmail.h conf.h diff --git a/usr.sbin/sendmail/src/Makefile.Solaris b/usr.sbin/sendmail/src/Makefile.Solaris new file mode 100644 index 0000000000..2e77620206 --- /dev/null +++ b/usr.sbin/sendmail/src/Makefile.Solaris @@ -0,0 +1,103 @@ +# +# This Makefile is designed to work on the old "make" program. It does +# not use the obj subdirectory. It also does not install documentation +# automatically -- think of it as a quick start for sites that have the +# old make program (I recommend that you get and port the new make if you +# are going to be doing any signficant work on sendmail). +# +# This has been tested on Ultrix. +# +# @(#)Makefile.dist 6.5 (Berkeley) 2/26/93 +# + +# use O=-O (usual) or O=-g (debugging) +O= -O + +CC= gcc -V2.3.3 + +# define the database mechanism used for alias lookups: +# -DNDBM -- use new DBM +# -DNEWDB -- use new Berkeley DB +# -DNDBM -DNEWDB -- use both new DBM and new Berkeley DB +# -DNDBM -DNEWDB -DYPCOMPAT -- use both plus YP compatility +# The really old (V7) DBM library is no longer supported. +# If YPCOMPAT is defined and /var/yp/Makefile exists, sendmail will build +# both the NEWDB and DBM libraries (the DBM just for YP). +# +DBMDEF= -DNDBM + +# define the load average calculation on your system: -DLA_TYPE=LA_INT, +# -DLA_TYPE=LA_FLOAT, -DLA_TYPE=LA_SUBR, or -DLA_TYPE=LA_ZERO +# leave undefined to use internal guess +#LADEF= -DLA_TYPE=LA_SUBR + +ENVDEF= -D_PATH_SENDMAILCF=\"/etc/mail/sendmail.cf\" \ + -D_PATH_SENDMAILPID=\"/etc/sendmail/sendmail.pid\" + +# see also conf.h for additional compilation flags + +# include directories +INCDIRS=-I/usr/sww/include/db + +# library directories +LIBDIRS=-L/usr/sww/lib -L/usr/ucblib + +# libraries required on your system +#LIBS= -lresolv -lsocket -lnsl -lucb -lelf +LIBS= -lresolv -lsocket -lnsl -lelf + +# location of sendmail binary (usually /usr/sbin or /usr/lib) +BINDIR= ${DESTDIR}/usr/sbin + +# location of sendmail.st file (usually /var/log or /usr/lib) +STDIR= ${DESTDIR}/var/log + +# location of sendmail.hf file (usually /usr/share/misc or /usr/lib) +HFDIR= ${DESTDIR}/usr/share/misc + +################### end of user configuration flags ###################### + +CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${LADEF} ${ENVDEF} -DSOLARIS + +OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \ + deliver.o domain.o envelope.o err.o headers.o macro.o main.o \ + map.o mci.o parseaddr.o queue.o readcf.o recipient.o \ + savemail.o srvrsmtp.o stab.o stats.o sysexits.o \ + trace.o udb.o usersmtp.o util.o version.o + +LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq +BINOWN= root +BINGRP= sys +BINMODE=6555 +INSTALL=/usr/ucb/install + +sendmail: ${OBJS} + ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS} + +aliases.0: aliases.5 + nroff -h -mandoc aliases.5 > aliases.0 + +newaliases.0: newaliases.1 + nroff -h -mandoc newaliases.1 > newaliases.0 + +sendmail.0: sendmail.8 + nroff -h -mandoc sendmail.8 > sendmail.0 + +install: install-sendmail install-docs + +install-sendmail: sendmail + ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR} + for i in ${LINKS}; do ; rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done + ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 666 /dev/null \ + ${STDIR}/sendmail.st + ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR} + +# doesn't actually install them -- you may want to install pre-nroff versions +install-docs: aliases.0 newaliases.0 sendmail.0 + +clean: + rm -f ${OBJS} sendmail aliases.0 newaliases.0 sendmail.0 + +# dependencies +# gross overkill, and yet still not quite enough.... +${OBJS}: sendmail.h conf.h diff --git a/usr.sbin/sendmail/src/Makefile.SunOS b/usr.sbin/sendmail/src/Makefile.SunOS new file mode 100644 index 0000000000..b7be76d984 --- /dev/null +++ b/usr.sbin/sendmail/src/Makefile.SunOS @@ -0,0 +1,36 @@ +# @(#)Makefile 5.22 (Berkeley) 7/26/91 + +PROG= sendmail + +DBMDEF= -DNEWDB -DNDBM -DNIS +INCLS= -I/usr/local/include -I/usr/sww/include/db +CFLAGS+=-I${.CURDIR} ${INCLS} ${DBMDEF} ${OPTNS} + +SRCS= alias.c arpadate.c clock.c collect.c conf.c convtime.c daemon.c \ + deliver.c domain.c envelope.c err.c headers.c macro.c main.c map.c \ + mci.c parseaddr.c queue.c readcf.c recipient.c savemail.c srvrsmtp.c \ + stab.c stats.c sysexits.c trace.c udb.c usersmtp.c \ + util.c version.c +DPADD= ${LIBDBM} ${LIBCOMPAT} ${LIBUTIL} +LDADD= -L/usr/sww/lib -ldb -ldbm -lresolv -Bstatic +#LDADD= /usr/sww/build/lib/db/libdb.a -ldbm +#MAN1= newaliases.0 +#MAN5= aliases.0 +#MAN8= sendmail.0 +NOMAN= +LINKS= ${DESTDIR}/usr/sbin/sendmail ${DESTDIR}/usr/bin/newaliases \ + ${DESTDIR}/usr/sbin/sendmail ${DESTDIR}/usr/bin/mailq +BINDIR= /usr/sbin +BINOWN= root +BINGRP= kmem +BINMODE=6555 + +beforeinstall: + install -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \ + ${DESTDIR}/etc/sendmail.fc + install -c -o ${BINOWN} -g ${BINGRP} -m 666 /dev/null \ + ${DESTDIR}/var/log/sendmail.st + install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/sendmail.hf \ + ${DESTDIR}/usr/share/misc + +.include <bsd.prog.mk> diff --git a/usr.sbin/sendmail/src/Makefile.ULTRIX b/usr.sbin/sendmail/src/Makefile.ULTRIX new file mode 100644 index 0000000000..ccacfe5944 --- /dev/null +++ b/usr.sbin/sendmail/src/Makefile.ULTRIX @@ -0,0 +1,34 @@ +# @(#)Makefile 5.22 (Berkeley) 7/26/91 + +PROG= sendmail + +CFLAGS+=-I${.CURDIR} -I/usr/local/include -I/usr/sww/include/db -DNEWDB -DNDBM + +SRCS= alias.c arpadate.c clock.c collect.c conf.c convtime.c daemon.c \ + deliver.c domain.c envelope.c err.c headers.c macro.c main.c map.c \ + mci.c parseaddr.c queue.c readcf.c recipient.c savemail.c srvrsmtp.c \ + stab.c stats.c sysexits.c trace.c udb.c usersmtp.c \ + util.c version.c unsetenv.c +DPADD= ${LIBDBM} ${LIBCOMPAT} ${LIBUTIL} +LDADD= -L/usr/sww/lib -ldb +#LDADD= /usr/sww/build/lib/db/libdb.a -ldbm +#MAN1= newaliases.0 +#MAN5= aliases.0 +#MAN8= sendmail.0 +NOMAN= +LINKS= ${DESTDIR}/usr/sbin/sendmail ${DESTDIR}/usr/bin/newaliases \ + ${DESTDIR}/usr/sbin/sendmail ${DESTDIR}/usr/bin/mailq +BINDIR= /usr/sbin +BINOWN= root +BINGRP= kmem +BINMODE=6555 + +beforeinstall: + install -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \ + ${DESTDIR}/etc/sendmail.fc + install -c -o ${BINOWN} -g ${BINGRP} -m 666 /dev/null \ + ${DESTDIR}/var/log/sendmail.st + install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/sendmail.hf \ + ${DESTDIR}/usr/share/misc + +.include <bsd.prog.mk> diff --git a/usr.sbin/sendmail/src/Makefile.Utah b/usr.sbin/sendmail/src/Makefile.Utah new file mode 100644 index 0000000000..4ff06ff00b --- /dev/null +++ b/usr.sbin/sendmail/src/Makefile.Utah @@ -0,0 +1,42 @@ +# @(#)Makefile 8.1 (Berkeley) 6/7/93 + +PROG= sendmail + +# define the database format to use for aliases et al. Can be -DNEWDB (for +# the new BSD database package -- this is preferred) or -DNDBM for the NDBM +# database package. The old putrescent V7 DBM package is no longer +# supported. +# You can define both NEWDB and NDBM during a transition period; old +# databases are read, but the new format will be used on any rebuilds. On +# really gnarly systems, you can set this to null; it will crawl like a high +# spiral snail, but it will work. +DBMDEF= -DNEWDB -DNDBM -DOLD_NEWDB + +CFLAGS+=-I${.CURDIR} ${DBMDEF} -Dsetpgid=setpgrp + +SRCS= alias.c arpadate.c clock.c collect.c conf.c convtime.c daemon.c \ + deliver.c domain.c envelope.c err.c headers.c macro.c main.c map.c \ + mci.c parseaddr.c queue.c readcf.c recipient.c savemail.c srvrsmtp.c \ + stab.c stats.c sysexits.c trace.c udb.c usersmtp.c \ + util.c version.c +DPADD= ${LIBDBM} ${LIBCOMPAT} +LDADD= +MAN1= newaliases.0 +MAN5= aliases.0 +MAN8= sendmail.0 +LINKS= ${DESTDIR}/usr/sbin/sendmail ${DESTDIR}/usr/bin/newaliases \ + ${DESTDIR}/usr/sbin/sendmail ${DESTDIR}/usr/bin/mailq +BINDIR= /usr/sbin +BINOWN= root +BINGRP= kmem +BINMODE=6555 + +beforeinstall: +# install -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \ +# ${DESTDIR}/etc/sendmail.fc + install -c -o ${BINOWN} -g ${BINGRP} -m 666 /dev/null \ + ${DESTDIR}/var/log/sendmail.st + install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/sendmail.hf \ + ${DESTDIR}/usr/share/misc + +.include <bsd.prog.mk> diff --git a/usr.sbin/sendmail/src/Makefile.dist b/usr.sbin/sendmail/src/Makefile.dist new file mode 100644 index 0000000000..f85dfebdae --- /dev/null +++ b/usr.sbin/sendmail/src/Makefile.dist @@ -0,0 +1,99 @@ +# +# This Makefile is designed to work on the old "make" program. It does +# not use the obj subdirectory. It also does not install documentation +# automatically -- think of it as a quick start for sites that have the +# old make program (I recommend that you get and port the new make if you +# are going to be doing any signficant work on sendmail). +# +# This has been tested on Ultrix. +# +# @(#)Makefile.dist 8.1 (Berkeley) 6/7/93 +# + +# use O=-O (usual) or O=-g (debugging) +O= -O + +# define the database mechanisms available for map & alias lookups: +# -DNDBM -- use new DBM +# -DNEWDB -- use new Berkeley DB +# -DNDBM -DNEWDB -DYPCOMPAT -- use both plus YP compatility +# -DNIS -- include client NIS support +# The really old (V7) DBM library is no longer supported. +# If YPCOMPAT is defined and /var/yp/Makefile exists, sendmail will build +# both the NEWDB and DBM libraries (the DBM just for YP). +# +DBMDEF= -DNEWDB + +# define the load average calculation on your system: -DLA_TYPE=LA_INT, +# -DLA_TYPE=LA_FLOAT, -DLA_TYPE=LA_SUBR, or -DLA_TYPE=LA_ZERO +# leave undefined to use internal guess +#LADEF= -DLA_TYPE=LA_SUBR + +# define UNSETENV if you need to compile in a local version of setenv +#ENVDEF= -DUNSETENV + +# see also conf.h for additional compilation flags + +# include directories +INCDIRS=-I/usr/sww/include/db + +# library directories +LIBDIRS=-L/usr/sww/lib + +# libraries required on your system +LIBS= -ldb -ldbm + +# location of sendmail binary (usually /usr/sbin or /usr/lib) +BINDIR= ${DESTDIR}/usr/sbin + +# location of sendmail.st file (usually /var/log or /usr/lib) +STDIR= ${DESTDIR}/var/log + +# location of sendmail.hf file (usually /usr/share/misc or /usr/lib) +HFDIR= ${DESTDIR}/usr/share/misc + +################### end of user configuration flags ###################### + +CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${LADEF} ${ENVDEF} + +OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \ + deliver.o domain.o envelope.o err.o headers.o macro.o main.o \ + map.o mci.o parseaddr.o queue.o readcf.o recipient.o \ + savemail.o srvrsmtp.o stab.o stats.o sysexits.o \ + trace.o udb.o usersmtp.o util.o version.o + +LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq +BINOWN= root +BINGRP= kmem +BINMODE=6555 + +sendmail: ${OBJS} + ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS} + +aliases.0: aliases.5 + nroff -h -mandoc aliases.5 > aliases.0 + +newaliases.0: newaliases.1 + nroff -h -mandoc newaliases.1 > newaliases.0 + +sendmail.0: sendmail.8 + nroff -h -mandoc sendmail.8 > sendmail.0 + +install: install-sendmail install-docs + +install-sendmail: sendmail + install -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR} + for i in ${LINKS}; do ; rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done + install -c -o ${BINOWN} -g ${BINGRP} -m 666 /dev/null \ + ${STDIR}/sendmail.st + install -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR} + +# doesn't actually install them -- you may want to install pre-nroff versions +install-docs: aliases.0 newaliases.0 sendmail.0 + +clean: + rm -f ${OBJS} sendmail aliases.0 newaliases.0 sendmail.0 + +# dependencies +# gross overkill, and yet still not quite enough.... +${OBJS}: sendmail.h conf.h diff --git a/usr.sbin/sendmail/src/READ_ME b/usr.sbin/sendmail/src/READ_ME index 5384273c12..b1a0f804a6 100644 --- a/usr.sbin/sendmail/src/READ_ME +++ b/usr.sbin/sendmail/src/READ_ME @@ -30,25 +30,166 @@ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # -# @(#)READ_ME 4.6 (Berkeley) 4/23/91 +# @(#)READ_ME 8.3 (Berkeley) 7/13/93 # This directory contains the source files for sendmail. -For installation instructions, please read the document ../doc/op.me: +For detailed instructions, please read the document ../doc/op.me: - nroff -me ../doc/op.me + eqn ../doc/op.me | pic | ditroff -me + +The Makefile is for the new Berkeley make, available from ftp.uu.net +in the directory /systems/unix/bsd-sources/usr.bin/make. There is +also a Makefile.dist which is much less clever, but works on the old +traditional make. You can use this using: + + make -f Makefile.dist + +There are a couple of other Makefiles for other systems -- these are +the ones that I use, they have "Berkeley quirks" in them, and I don't +guarantee that they will work in your environment. To make it worse, +some are for the new Berkeley make, and some are for the old make. +I provide them for information only. Still, they may help you get +started. They have names like "Makefile.HPUX". + +Whereever possible, I try to make sendmail pull in the correct +compilation options needed to compile on various environments based on +automatically defined symbols. Some machines don't seem to have useful +symbols availble, requiring the following compilation flags in the +Makefile: + +SOLARIS Define this if you are running Solaris 2.0 or higher. +__NeXT__ Define this if you are on a NeXT box. (This one may + be pre-defined for you.) +_AIX3 Define this if you are IBM AIX 3.x. + +If you are a system that sendmail has already been ported to, you +probably won't have to touch these. But if you are porting, you may +have to tweak the following compilation flags in order to get +it to compile and link properly: + +UNSETENV Define this if your system library does NOT include the + "unsetenv" subroutine. +SYSTEM5 Adjust for System V. +LOCKF Set this if you do not have the flock system call -- it + will revert to System V file locking. There are some + semantic gotchas, so flock is preferred. Implied by + SYSTEM5. +SYS5TZ Use System V-style time zones. If not set, the TZ + environment variable is ignored. Implied by SYSTEM5. +HASUNAME Set if you have the "uname" system call. Implied by + SYSTEM5. +HASSTATFS Define this if you have the statfs(2) system call. It's + not a disaster to get this wrong -- but you do lose the + queue free space code. +HASUSTAT Define this if you have the ustat(2) system call. It's + not a disaster to get this wrong -- but you do lose the + queue free space code. +HASSETSID Define this if you have the setsid(2) system call. This + is implied if your system appears to be POSIX compliant. +HASINITGROUPS Define this if you have the initgroups(3) routine. +HASSETVBUF Define this if you have the setvbuf(3) library call. + If you don't, setlinebuf will be used instead. This + defaults on if your compiler defines __STDC__. +HASSETEUID Define this if you have seteuid(2) ***AND*** root can use + it to change to an arbitrary user. This second condition + is not satisfied on AIX 3.x. You may find that + your system has setreuid(2) or setresuid(2), in which + case you will also have to #define seteuid(uid) to be + the appropriate call. The important thing is that you + have a call that will set the effective uid and NOT + set the real or saved uid. Setting this improves the + security somewhat, since sendmail doesn't have to read + .forward and :include: files as root. +LA_TYPE The type of load average your kernel supports. These + can be LA_SUBR (4) if you have the getloadavg(3) routine, + LA_FLOAT (3) if you read kmem and interpret the value + as a floating point number, LA_INT (2) to interpret as + an integer. These last two have several other parameters + that they try to divine: the name of your kernel, the name + of the variable in the kernel to examine, the number of + bits of precision in a fixed point load average, and so + forth. In desparation, use LA_ZERO -- it always returns + the load average as "zero" (and does so on all architectures). + The actual code is in conf.c -- it can be tweaked if you + are brave. + +There are a bunch of features that you can decide to compile in, such +as selecting various database packages and special protocol support. +Several are assumed based on other compilation flags -- if you want to +"un-assume" something, you probably need to edit conf.h. Compilation +flags that add support for special features include: + +NDBM Include support for "new" DBM library for aliases and maps. +NEWDB Include support for Berkeley "db" package (hash & btree) + for aliases and maps. +NIS Define this to get NIS (YP) support for aliases and maps. +YPCOMPAT Define this to force building of DBM versions of alias + files even if you have NEWDB defined; this will only + occur on NIS master machines. It is independent of NIS. +USERDB Include support for the User Information Database. Implied + by NEWDB conf.h. +IDENTPROTO Define this to get IDENT (RFC 1413) protocol support. + This is assumed unless you are running on Ultrix or + HP-UX, both of which have a problem in the UDP + implementation. +MIME Include support for MIME-encapsulated error messages. +FROZENCONFIG Define this to get support for frozen configuration + files. Frozen configurations make sense if your I/O system + is fast relative to your processor. At this point this + is NOT recommended. +LOG Set this to get syslog(3) support. Defined by default + in conf.h. You want this if at all possible. +NETINET Set this to get TCP/IP support. Defined by default + in conf.h. You probably want this. +NETISO Define this to get ISO networking support. +SMTP Define this to get the SMTP code. Implied by NETINET + or NETISO. +NAMED_BIND Define this to get DNS (name daemon) support, including + MX support. The specs you must use this if you run + SMTP. Defined by default in conf.h. +QUEUE Define this to get queueing code. Implied by NETINET + or NETISO; required by SMTP. This gives you other good + stuff -- it should be on. +DAEMON Define this to get general network support. Implied by + NETINET or NETISO. Defined by default in conf.h. You + almost certainly want it on. +MATCHGECOS Permit fuzzy matching of user names against the full + name (GECOS) field in the /etc/passwd file. This should + probably be on, since you can disable it from the config + file if you want to. Defined by default in conf.h. +SETPROCTITLE Try to set the string printed by "ps" to something + informative about what sendmail is doing. Defined by + default in conf.h. + +If you are compiling on SunOS and want to use frozen configuration +files, you must use -Bstatic -- if you do not, frozen configuration +files fail in bizarre ways and you will open up several security holes. + +If you are compiling on OSF/1 (DEC Alpha), you must use -lmld. + +If you are compiling on NeXT, you will have to create an empty file +"unistd.h". + +If you use both -DNDBM and -DNEWDB, you must delete the module ndbm.o +from libdb.a and delete the file "ndbm.h" from the files that get +installed (that is, use the OLD ndbm.h, not the new ndbm.h). This +compatibility module maps ndbm calls into DB calls, and breaks things +rather badly. + +You probably want to look over the compilation options in conf.h +before you compile. These are intended to be per-site information. The following list describes the files in this directory: -Makefile The makefile used here; this is created from - makefile.m4. -Makefile.m4 A makefile template. +Makefile The makefile used here; this version only works with + the new Berkeley make. +Makefile.dist A trimmed down version of the makefile that works with + the old make. READ_ME This file. -TODO New features to be put in (maybe) at some time. -Version.c The version number and information about this - version of sendmail. Theoretically, this gets - modified on every change. +TRACEFLAGS My own personal list of the trace flags -- not guaranteed + to be particularly up to date. alias.c Does name aliasing in all forms. arpadate.c A subroutine which creates ARPANET standard dates. clock.c Routines to implement real-time oriented functions @@ -65,6 +206,8 @@ convtime.c A routine to sanely process times. daemon.c Routines to implement daemon mode. This version is specifically for Berkeley 4.1 IPC. deliver.c Routines to deliver mail. +domain.c Routines that interface with DNS (the Domain Name + System). err.c Routines to print error messages. envelope.c Routines to manipulate the envelope structure. headers.c Routines to process message headers. @@ -72,6 +215,8 @@ macro.c The macro expander. This is used internally to insert information from the configuration file. main.c The main routine to sendmail. This file also contains some miscellaneous routines. +map.c Support for database maps. +mci.c Routines that handle mail connection information caching. parseaddr.c The routines which do address parsing. queue.c Routines to implement message queueing. readcf.c The routine that reads the configuration file and @@ -86,12 +231,13 @@ sysexits.c List of error messages associated with error codes in sysexits.h. trace.c The trace package. These routines allow setting and testing of trace flags with a high granularity. -trace.h Definitions needed for the trace package. +udb.c The user database interface module. usersmtp.c Routines to implement user SMTP. util.c Some general purpose routines used by sendmail. -version.c A master file for Version.c -- it may not exist in - your distribution. +version.c The version number and information about this + version of sendmail. Theoretically, this gets + modified on every change. Eric Allman -(Version 4.6, last update 4/23/91 19:52:27) +(Version 8.3, last update 7/13/93 12:57:56) diff --git a/usr.sbin/sendmail/src/TODO b/usr.sbin/sendmail/src/TODO deleted file mode 100644 index ddd2062558..0000000000 --- a/usr.sbin/sendmail/src/TODO +++ /dev/null @@ -1,48 +0,0 @@ -(Version 4.1 of 7/25/83) - --- get rid of MAXMAILERS, just allocate them as you go; merge the stats - structure in with the MAILERS structure; change the longs in - said structure into u_longs; print the file in ASCII, lock the - file before reading/printing. - --- clean up sysexits; get rid of EX_BASE, most of sysexits.c is duplicated - in deliver.c - --- mailq; split it out, by default, just # of messages, also add sort - by machine? - --- flag so that if it has to forward mail it returns a note giving the - relay address? - - ****** Finish properly implementing SMTP: - - check correct name in HELO exchange (?) - - ***** Add an accounting package. [acct.c] - - ***** When processing the queue, batch messages on the basis of - receiving host rather than message -- so that if connections - are expensive to complete, we get the most for our money. - Unfortunately, this is a major change, and will probably - wait for the next version. - - **** Put in some more performance hooks. - - *** Integrate a null name server for future hackers. [names.c] - - *** Have "Operating System Error" give more info (e.g., - "cannot fork"). [It really seems like this is ok now -- but - this is an old delivermail gripe -- check it out more?] - - ** Per-mailer timeouts. Invalidates old .cf files. - - ** Be able to generate a "response" (edit headers, etc.) - given a message to respond to. - - * Have VRFY act as a limited name server (?). - - * Implement SEND and related commands in SMTP. - - * Extend full name matching. This should actually be done in a - complete name server. Should the name server be integrated - into sendmail (via a portal?) or should I "assume" that it - has been called by the time I am called? diff --git a/usr.sbin/sendmail/src/TRACEFLAGS b/usr.sbin/sendmail/src/TRACEFLAGS new file mode 100644 index 0000000000..3fd00b7280 --- /dev/null +++ b/usr.sbin/sendmail/src/TRACEFLAGS @@ -0,0 +1,59 @@ +0, 1 main.c main skip background fork +0, 4 main.c main canonical name, UUCP node name, a.k.a.s +0, 15 main.c main print configuration +0, 44 util.c printav print address of each string +1 main.c main print from person +2 main.c finis +3 conf.c getla +4 conf.c enoughspace +5 clock.c setevent, clrevent, tick +6 savemail.c savemail, returntosender +7 queue.c queuename +8 domain.c getmxrr, getcanonname +9 daemon.c getauthinfo IDENT protocol +9 daemon.c maphostname +10 deliver.c deliver +11 deliver.c openmailer, mailfile +12 parseaddr.c remotename +13 deliver.c sendall, sendenvelope +14 headers.c commaize +15 daemon.c getrequests +16 daemon.c makeconnection +17 deliver.c hostsignature +17 domain.c mxrand +18 usersmtp.c reply, smtpmessage, smtpinit, smtpmailfrom +19 srvrsmtp.c smtp +20 parseaddr.c parseaddr +21 parseaddr.c rewrite +22 parseaddr.c prescan +24 parseaddr.c buildaddr, allocaddr +25 recipient.c sendtolist +26 recipient.c recipient +27 alias.c alias +27 alias.c readaliases +27 alias.c forward +27 recipient.c include +28 udb.c udbexpand, udbsender +29 parseaddr.c maplocaluser +29 recipient.c recipient (local users), finduser +30 collect.c collect +30 collect.c eatfrom +31 headers.c chompheader +32 headers.c eatheader +33 headers.c crackaddr +34 headers.c putheader +35 macro.c expand, define +36 stab.c stab +37 readcf.c (many) +38 map.c initmaps +39 map.c map_rewrite +40 queue.c queueup, orderq, dowork +41 queue.c orderq +42 mci.c mci_get +45 envelope.c setsender +50 envelope.c dropenvelope +51 queue.c unlockqueue +52 main.c disconnect +53 util.c xfclose +59 Extended Load Average implementation from Christophe Wolfhugel +60 map.c diff --git a/usr.sbin/sendmail/src/alias.c b/usr.sbin/sendmail/src/alias.c index 2e26b515e8..eb579b22b9 100644 --- a/usr.sbin/sendmail/src/alias.c +++ b/usr.sbin/sendmail/src/alias.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,27 +32,18 @@ * SUCH DAMAGE. */ +# include "sendmail.h" +# include <signal.h> +# include <pwd.h> + #ifndef lint -#ifdef DBM -static char sccsid[] = "@(#)alias.c 5.22 (Berkeley) 3/2/91 (with DBM)"; -#else -#ifdef USE_DB -static char sccsid[] = "@(#)alias.c 5.22 (Berkeley) 3/2/91 (with DB)"; -#else -static char sccsid[] = "@(#)alias.c 5.22 (Berkeley) 3/2/91 (without DBM)"; -#endif -#endif +static char sccsid[] = "@(#)alias.c 8.3 (Berkeley) 7/13/93"; #endif /* not lint */ -# include <sys/types.h> -# include <sys/stat.h> -# include <signal.h> -# include <errno.h> -# include "sendmail.h" -# include <sys/file.h> -# include <pwd.h> -/* +MAP *AliasDB[MAXALIASDB + 1]; /* actual database list */ +int NAliasDBs; /* number of alias databases */ + /* ** ALIAS -- Compute aliases. ** ** Scans the alias file for an alias for the given address. @@ -63,6 +54,7 @@ static char sccsid[] = "@(#)alias.c 5.22 (Berkeley) 3/2/91 (without DBM)"; ** a -- address to alias. ** sendq -- a pointer to the head of the send queue ** to put the aliases in. +** e -- the current envelope. ** ** Returns: ** none @@ -70,56 +62,39 @@ static char sccsid[] = "@(#)alias.c 5.22 (Berkeley) 3/2/91 (without DBM)"; ** Side Effects: ** Aliases found are expanded. ** -** Notes: -** If NoAlias (the "-n" flag) is set, no aliasing is -** done. -** ** Deficiencies: ** It should complain about names that are aliased to ** nothing. */ - -#ifdef DBM -typedef struct -{ - char *dptr; - int dsize; -} DATUM; -extern DATUM fetch(); -#endif DBM -# ifdef USE_DB -# include <db.h> -/* -** DB is a database structure containing pointers to access methods. -*/ -static DB *aliasdb; -# endif USE_DB - -alias(a, sendq) +alias(a, sendq, e) register ADDRESS *a; ADDRESS **sendq; + register ENVELOPE *e; { register char *p; + int naliases; + char *owner; + char obuf[MAXNAME + 6]; extern char *aliaslookup(); if (tTd(27, 1)) printf("alias(%s)\n", a->q_paddr); /* don't realias already aliased names */ - if (bitset(QDONTSEND, a->q_flags)) + if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags)) return; - CurEnv->e_to = a->q_paddr; + if (NoAlias) + return; + + e->e_to = a->q_paddr; /* ** Look up this name */ - if (NoAlias) - p = NULL; - else - p = aliaslookup(a->q_user); + p = aliaslookup(a->q_user, e); if (p == NULL) return; @@ -131,10 +106,49 @@ alias(a, sendq) if (tTd(27, 1)) printf("%s (%s, %s) aliased to %s\n", a->q_paddr, a->q_host, a->q_user, p); - message(Arpa_Info, "aliased to %s", p); + if (bitset(EF_VRFYONLY, e->e_flags)) + { + a->q_flags |= QVERIFIED; + e->e_nrcpts++; + return; + } + message("aliased to %s", p); +#ifdef LOG + if (LogLevel > 9) + syslog(LOG_INFO, "%s: alias %s => %s", e->e_id, a->q_paddr, p); +#endif + a->q_flags &= ~QSELFREF; AliasLevel++; - sendtolist(p, a, sendq); + naliases = sendtolist(p, a, sendq, e); AliasLevel--; + if (naliases > 0 && !bitset(QSELFREF, a->q_flags)) + { + if (tTd(27, 5)) + { + printf("alias: QDONTSEND "); + printaddr(a, FALSE); + } + a->q_flags |= QDONTSEND; + } + + /* + ** Look for owner of alias + */ + + (void) strcpy(obuf, "owner-"); + if (strncmp(a->q_user, "owner-", 6) == 0) + (void) strcat(obuf, "owner"); + else + (void) strcat(obuf, a->q_user); + if (!bitnset(M_USR_UPPER, a->q_mailer->m_flags)) + makelower(obuf); + owner = aliaslookup(obuf, e); + if (owner != NULL) + { + if (strchr(owner, ',') != NULL) + owner = obuf; + a->q_owner = newstr(owner); + } } /* ** ALIASLOOKUP -- look up a name in the alias file. @@ -154,319 +168,314 @@ alias(a, sendq) */ char * -aliaslookup(name) +aliaslookup(name, e) char *name; + ENVELOPE *e; { -# ifdef DBM - DATUM rhs, lhs; - - /* create a key for fetch */ - lhs.dptr = name; - lhs.dsize = strlen(name) + 1; - rhs = fetch(lhs); - return (rhs.dptr); -# else DBM -# ifdef USE_DB - DBT rhs, lhs; - - /* create a key for fetch */ - lhs.data = name; - lhs.size = strlen(name) + 1; - aliasdb->get(aliasdb, &lhs, &rhs, 0); - return (rhs.data); -# else USE_DB - register STAB *s; - - s = stab(name, ST_ALIAS, ST_FIND); - if (s == NULL) - return (NULL); - return (s->s_alias); -# endif USE_DB -# endif DBM + register int dbno; + register MAP *map; + register char *p; + + for (dbno = 0; dbno < NAliasDBs; dbno++) + { + auto int stat; + + map = AliasDB[dbno]; + if (!bitset(MF_OPEN, map->map_mflags)) + continue; + p = (*map->map_class->map_lookup)(map, name, NULL, &stat); + if (p != NULL) + return p; + } + return NULL; } /* -** INITALIASES -- initialize for aliasing +** SETALIAS -- set up an alias map ** -** Very different depending on whether we are running DBM or not. +** Called when reading configuration file. ** ** Parameters: -** aliasfile -- location of aliases. -** init -- if set and if DBM, initialize the DBM files. +** spec -- the alias specification ** ** Returns: ** none. -** -** Side Effects: -** initializes aliases: -** if DBM: opens the database. -** if ~DBM: reads the aliases into the symbol table. */ -# define DBMMODE 0644 -# define DBEXTENSION ".db" /* extension for the database filename */ - -initaliases(aliasfile, init) - char *aliasfile; - bool init; +setalias(spec) + char *spec; { -#if defined (DBM) || defined (USE_DB) - int atcnt; - time_t modtime; - bool automatic = FALSE; - char buf[MAXNAME]; -#endif - struct stat stb; - static bool initialized = FALSE; - static int readaliases(); + register char *p; + register MAP *map; + char *class; + STAB *s; - if (initialized) - return; - initialized = TRUE; + if (tTd(27, 8)) + printf("setalias(%s)\n", spec); - if (aliasfile == NULL || stat(aliasfile, &stb) < 0) + for (p = spec; p != NULL; ) { - if (aliasfile != NULL && init) - syserr("Cannot open %s", aliasfile); - NoAlias = TRUE; - errno = 0; - return; + char aname[50]; + + while (isspace(*p)) + p++; + if (*p == '\0') + break; + spec = p; + + if (NAliasDBs >= MAXALIASDB) + { + syserr("Too many alias databases defined, %d max", MAXALIASDB); + return; + } + (void) sprintf(aname, "Alias%d", NAliasDBs); + s = stab(aname, ST_MAP, ST_ENTER); + map = &s->s_map; + AliasDB[NAliasDBs] = map; + bzero(map, sizeof *map); + + p = strpbrk(p, " ,/:"); + if (p != NULL && *p == ':') + { + /* map name */ + *p++ = '\0'; + class = spec; + spec = p; + } + else + { + class = "implicit"; + map->map_mflags = MF_OPTIONAL|MF_INCLNULL; + } + + /* find end of spec */ + if (p != NULL) + p = strchr(p, ','); + if (p != NULL) + *p++ = '\0'; + + /* look up class */ + s = stab(class, ST_MAPCLASS, ST_FIND); + if (s == NULL) + { + if (tTd(27, 1)) + printf("Unknown alias class %s\n", class); + } + else if (!bitset(MCF_ALIASOK, s->s_mapclass.map_cflags)) + { + syserr("setalias: map class %s can't handle aliases", + class); + } + else + { + map->map_class = &s->s_mapclass; + if (map->map_class->map_parse(map, spec)) + { + map->map_mflags |= MF_VALID|MF_ALIAS; + NAliasDBs++; + } + } } +} + /* +** ALIASWAIT -- wait for distinguished @:@ token to appear. +** +** This can decide to reopen or rebuild the alias file +*/ -#if defined (DBM) || defined (USE_DB) - /* - ** Check to see that the alias file is complete. - ** If not, we will assume that someone died, and it is up - ** to us to rebuild it. - */ +aliaswait(map, ext) + MAP *map; + char *ext; +{ + int atcnt; + time_t mtime; + struct stat stb; + char buf[MAXNAME]; + + if (tTd(27, 3)) + printf("aliaswait(%s:%s)\n", + map->map_class->map_cname, map->map_file); - if (!init) -# ifdef DBM - dbminit(aliasfile); -# endif -# ifdef USE_DB - { - (void) strcpy(buf, aliasfile); - (void) strcat(buf, DBEXTENSION); - if (aliasdb) aliasdb->close (aliasdb); - aliasdb = btree_open (buf, O_RDWR, DBMMODE, 0); - if (aliasdb == NULL) - { - syserr("Cannot open database %s", buf); - NoAlias = TRUE; - return; - } - } -# endif USE_DB atcnt = SafeAlias * 2; if (atcnt > 0) { - while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL) + auto int st; + + while (atcnt-- >= 0 && + map->map_class->map_lookup(map, "@", NULL, &st) == NULL) { /* - ** Reinitialize alias file in case the new - ** one is mv'ed in instead of cp'ed in. - ** - ** Only works with new DBM -- old one will - ** just consume file descriptors forever. - ** If you have a dbmclose() it can be - ** added before the sleep(30). + ** Close and re-open the alias database in case + ** the one is mv'ed instead of cp'ed in. */ -# ifdef USE_DB - if(aliasdb) - aliasdb->close (aliasdb); -# endif USE_DB + if (tTd(27, 2)) + printf("aliaswait: sleeping\n"); + + map->map_class->map_close(map); sleep(30); -# ifdef NDBM - dbminit(aliasfile); -# endif NDBM -# ifdef USE_DB - aliasdb = btree_open (buf, O_RDWR, DBMMODE, 0); - if (aliasdb == NULL) - { - syserr("Cannot open database %s", buf); - NoAlias = TRUE; - return; - } -# endif USE_DB + map->map_class->map_open(map, O_RDONLY); } } - else - atcnt = 1; - /* - ** See if the DBM version of the file is out of date with - ** the text version. If so, go into 'init' mode automatically. - ** This only happens if our effective userid owns the DBM. - ** Note the unpalatable hack to see if the stat succeeded. - */ - - modtime = stb.st_mtime; - (void) strcpy(buf, aliasfile); -# ifdef DBM - (void) strcat(buf, ".pag"); -# endif DBM -# ifdef USE_DB - (void) strcat(buf, DBEXTENSION); -# endif USE_DB - stb.st_ino = 0; - if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0)) + /* see if we need to go into auto-rebuild mode */ + if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags)) { - errno = 0; + if (tTd(27, 3)) + printf("aliaswait: not rebuildable\n"); + return; + } + if (stat(map->map_file, &stb) < 0) + { + if (tTd(27, 3)) + printf("aliaswait: no source file\n"); + return; + } + mtime = stb.st_mtime; + (void) strcpy(buf, map->map_file); + if (ext != NULL) + (void) strcat(buf, ext); + if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || atcnt < 0) + { + /* database is out of date */ if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid()) { - init = TRUE; - automatic = TRUE; - message(Arpa_Info, "rebuilding alias database"); -#ifdef LOG - if (LogLevel >= 7) - syslog(LOG_INFO, "rebuilding alias database"); -#endif LOG + message("auto-rebuilding alias database %s", buf); + rebuildaliases(map, TRUE); } else { #ifdef LOG - if (LogLevel >= 7) - syslog(LOG_INFO, "alias database out of date"); -#endif LOG - message(Arpa_Info, "Warning: alias database out of date"); + if (LogLevel > 3) + syslog(LOG_INFO, "alias database %s out of date", + buf); +#endif /* LOG */ + message("Warning: alias database %s out of date", buf); } } - - - /* - ** If necessary, load the DBM file. - ** If running without DBM, load the symbol table. - */ - - if (init) - { -#ifdef LOG - if (LogLevel >= 6) - { - extern char *username(); - - syslog(LOG_NOTICE, "alias database %srebuilt by %s", - automatic ? "auto" : "", username()); - } -#endif LOG - readaliases(aliasfile, TRUE); -# ifdef USE_DB - aliasdb->sync (aliasdb); -# endif USE_DB - } -#else /* defined (DBM) || defined (USE_DB) */ - readaliases(aliasfile, init); -#endif /* defined (DBM) || defined (USE_DB) */ } /* -** READALIASES -- read and process the alias file. -** -** This routine implements the part of initaliases that occurs -** when we are not going to use the DBM stuff. +** REBUILDALIASES -- rebuild the alias database. ** ** Parameters: -** aliasfile -- the pathname of the alias file master. -** init -- if set, initialize the DBM stuff. +** map -- the database to rebuild. +** automatic -- set if this was automatically generated. ** ** Returns: ** none. ** ** Side Effects: -** Reads aliasfile into the symbol table. -** Optionally, builds the .dir & .pag files. +** Reads the text version of the database, builds the +** DBM or DB version. */ -static -readaliases(aliasfile, init) - char *aliasfile; - bool init; +rebuildaliases(map, automatic) + register MAP *map; + bool automatic; { - register char *p; - char *rhs; - bool skipping; - int naliases, bytes, longest; FILE *af; void (*oldsigint)(); - ADDRESS al, bl; - register STAB *s; - char line[BUFSIZ]; - if ((af = fopen(aliasfile, "r")) == NULL) + if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags)) + return; + +#ifdef LOG + if (LogLevel > 7) + { + syslog(LOG_NOTICE, "alias database %s %srebuilt by %s", + map->map_file, automatic ? "auto" : "", username()); + } +#endif /* LOG */ + + /* try to lock the source file */ + if ((af = fopen(map->map_file, "r+")) == NULL) { if (tTd(27, 1)) - printf("Can't open %s\n", aliasfile); + printf("Can't open %s: %s\n", + map->map_file, errstring(errno)); errno = 0; - NoAlias++; return; } -#if defined (DBM) || defined (USE_DB) - /* see if someone else is rebuilding the alias file already */ - if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) + /* see if someone else is rebuilding the alias file */ + if (!lockfile(fileno(af), map->map_file, LOCK_EX|LOCK_NB)) { - /* yes, they are -- wait until done and then return */ - message(Arpa_Info, "Alias file is already being rebuilt"); + /* yes, they are -- wait until done */ + message("Alias file %s is already being rebuilt", + map->map_file); if (OpMode != MD_INITALIAS) { /* wait for other rebuild to complete */ - (void) flock(fileno(af), LOCK_EX); + (void) lockfile(fileno(af), map->map_file, + LOCK_EX); } (void) fclose(af); errno = 0; return; } - /* - ** If initializing, create the new DBM files. - */ + oldsigint = signal(SIGINT, SIG_IGN); - if (init) + if (map->map_class->map_open(map, O_RDWR)) { - oldsigint = signal(SIGINT, SIG_IGN); -# ifdef USE_DB - if (aliasdb) aliasdb->close (aliasdb); -# endif USE_DB - (void) strcpy(line, aliasfile); -# ifdef DBM - (void) strcat(line, ".dir"); - if (close(creat(line, DBMMODE)) < 0) - { - syserr("cannot make %s", line); - (void) signal(SIGINT, oldsigint); - return; - } - (void) strcpy(line, aliasfile); - (void) strcat(line, ".pag"); - if (close(creat(line, DBMMODE)) < 0) - { - syserr("cannot make %s", line); - (void) signal(SIGINT, oldsigint); - return; - } - dbminit(aliasfile); -# endif DBM -# ifdef USE_DB - (void) strcat(line, DBEXTENSION); - /* unconditionally remove the database file so that a - corrupt file cannot cause the following open to fail */ - unlink (line); - aliasdb = btree_open (line, O_RDWR|O_CREAT, DBMMODE, 0); - if (aliasdb == NULL) - { - syserr("Cannot open database file %s", line); - (void) signal(SIGINT, oldsigint); - return; - } -# endif USE_DB + map->map_mflags |= MF_OPEN|MF_WRITABLE; + readaliases(map, af, automatic); } -#endif /* defined (DBM) || defined (USE_DB) */ + else + { + if (tTd(27, 1)) + printf("Can't create database for %s: %s\n", + map->map_file, errstring(errno)); + if (!automatic) + syserr("Cannot create database for alias file %s", + map->map_file); + } + + /* close the file, thus releasing locks */ + fclose(af); + + /* add distinguished entries and close the database */ + if (bitset(MF_OPEN, map->map_mflags)) + map->map_class->map_close(map); + + /* restore the old signal */ + (void) signal(SIGINT, oldsigint); +} + /* +** READALIASES -- read and process the alias file. +** +** This routine implements the part of initaliases that occurs +** when we are not going to use the DBM stuff. +** +** Parameters: +** map -- the alias database descriptor. +** af -- file to read the aliases from. +** automatic -- set if this was an automatic rebuild. +** +** Returns: +** none. +** +** Side Effects: +** Reads aliasfile into the symbol table. +** Optionally, builds the .dir & .pag files. +*/ + +readaliases(map, af, automatic) + register MAP *map; + FILE *af; + int automatic; +{ + register char *p; + char *rhs; + bool skipping; + long naliases, bytes, longest; + ADDRESS al, bl; + char line[BUFSIZ]; /* ** Read and interpret lines */ - FileName = aliasfile; + FileName = map->map_file; LineNumber = 0; naliases = bytes = longest = 0; skipping = FALSE; @@ -475,7 +484,7 @@ readaliases(aliasfile, init) int lhssize, rhssize; LineNumber++; - p = index(line, '\n'); + p = strchr(line, '\n'); if (p != NULL) *p = '\0'; switch (line[0]) @@ -488,7 +497,7 @@ readaliases(aliasfile, init) case ' ': case '\t': if (!skipping) - syserr("Non-continuation line starts with space"); + syserr("554 Non-continuation line starts with space"); skipping = TRUE; continue; } @@ -496,7 +505,7 @@ readaliases(aliasfile, init) /* ** Process the LHS - ** Find the final colon, and parse the address. + ** Find the colon separator, and parse the address. ** It should resolve to a local name -- this will ** be checked later (we want to optionally do ** parsing of the RHS first to maximize error @@ -507,15 +516,14 @@ readaliases(aliasfile, init) continue; if (*p++ != ':') { - syserr("missing colon"); + syserr("554 missing colon"); continue; } - if (parseaddr(line, &al, 1, ':') == NULL) + if (parseaddr(line, &al, 1, ':', NULL, CurEnv) == NULL) { - syserr("illegal alias name"); + syserr("554 illegal alias name"); continue; } - loweraddr(&al); /* ** Process the RHS. @@ -523,32 +531,38 @@ readaliases(aliasfile, init) ** 'p' points to the text of the RHS. */ + while (isascii(*p) && isspace(*p)) + p++; rhs = p; for (;;) { register char c; + register char *nlp; + + nlp = &p[strlen(p)]; + if (nlp[-1] == '\n') + *--nlp = '\0'; - if (init && CheckAliases) + if (CheckAliases) { /* do parsing & compression of addresses */ while (*p != '\0') { - extern char *DelimChar; + auto char *delimptr; - while (isspace(*p) || *p == ',') + while ((isascii(*p) && isspace(*p)) || + *p == ',') p++; if (*p == '\0') break; - if (parseaddr(p, &bl, -1, ',') == NULL) - usrerr("%s... bad address", p); - p = DelimChar; + if (parseaddr(p, &bl, -1, ',', &delimptr, CurEnv) == NULL) + usrerr("553 %s... bad address", p); + p = delimptr; } } else { - p = &p[strlen(p)]; - if (p[-1] == '\n') - *--p = '\0'; + p = nlp; } /* see if there should be a continuation line */ @@ -562,10 +576,17 @@ readaliases(aliasfile, init) if (fgets(p, sizeof line - (p - line), af) == NULL) break; LineNumber++; + + /* check for line overflow */ + if (strchr(p, '\n') == NULL) + { + usrerr("554 alias too long"); + break; + } } if (al.q_mailer != LocalMailer) { - syserr("cannot alias non-local names"); + syserr("554 cannot alias non-local names"); continue; } @@ -573,40 +594,19 @@ readaliases(aliasfile, init) ** Insert alias into symbol table or DBM file */ - lhssize = strlen(al.q_user) + 1; - rhssize = strlen(rhs) + 1; + if (!bitnset(M_USR_UPPER, al.q_mailer->m_flags)) + makelower(al.q_user); -# ifdef DBM - if (init) - { - DATUM key, content; + lhssize = strlen(al.q_user); + rhssize = strlen(rhs); + map->map_class->map_store(map, al.q_user, rhs); - key.dsize = lhssize; - key.dptr = al.q_user; - content.dsize = rhssize; - content.dptr = rhs; - store(key, content); - } - else -# endif DBM -# ifdef USE_DB - if (init) - { - DBT key, content; - - key.size = lhssize; - key.data = al.q_user; - content.size = rhssize; - content.data = rhs; - - aliasdb->put (aliasdb, &key, &content, R_PUT); - } - else -# endif USE_DB - { - s = stab(al.q_user, ST_ALIAS, ST_ENTER); - s->s_alias = newstr(rhs); - } + if (al.q_paddr != NULL) + free(al.q_paddr); + if (al.q_host != NULL) + free(al.q_host); + if (al.q_user != NULL) + free(al.q_user); /* statistics */ naliases++; @@ -615,46 +615,16 @@ readaliases(aliasfile, init) longest = rhssize; } -# ifdef DBM - if (init) - { - /* add the distinquished alias "@" */ - DATUM key; - - key.dsize = 2; - key.dptr = "@"; - store(key, key); - - /* restore the old signal */ - (void) signal(SIGINT, oldsigint); - } -# endif DBM -# ifdef USE_DB - if (init) - { - /* add the distinquished alias "@" */ - DBT key; - - key.size = 2; - key.data = "@"; - aliasdb->put (aliasdb, &key, &key, R_PUT); - - /* restore the old signal */ - (void) signal(SIGINT, oldsigint); - } -# endif USE_DB - - /* closing the alias file drops the lock */ - (void) fclose(af); CurEnv->e_to = NULL; FileName = NULL; - message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", - naliases, longest, bytes); + if (Verbose || !automatic) + message("%s: %d aliases, longest %d bytes, %d bytes total", + map->map_file, naliases, longest, bytes); # ifdef LOG - if (LogLevel >= 8) - syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", - naliases, longest, bytes); -# endif LOG + if (LogLevel > 7) + syslog(LOG_INFO, "%s: %d aliases, longest %d bytes, %d bytes total", + map->map_file, naliases, longest, bytes); +# endif /* LOG */ } /* ** FORWARD -- Try to forward mail @@ -676,12 +646,17 @@ readaliases(aliasfile, init) ** New names are added to send queues. */ -forward(user, sendq) +forward(user, sendq, e) ADDRESS *user; ADDRESS **sendq; + register ENVELOPE *e; { - char buf[60]; - extern bool safefile(); + char *pp; + char *ep; +#ifdef HASSETEUID + register ADDRESS *ca; + uid_t saveduid, uid; +#endif if (tTd(27, 1)) printf("forward(%s)\n", user->q_paddr); @@ -689,14 +664,79 @@ forward(user, sendq) if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) return; if (user->q_home == NULL) - syserr("forward: no home"); + { + syserr("554 forward: no home"); + user->q_home = "/nosuchdirectory"; + } /* good address -- look for .forward file in home */ - define('z', user->q_home, CurEnv); - expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); - if (!safefile(buf, user->q_uid, S_IREAD)) - return; + define('z', user->q_home, e); + define('u', user->q_user, e); + define('h', user->q_host, e); + if (ForwardPath == NULL) + ForwardPath = newstr("\201z/.forward"); + +#ifdef HASSETEUID + ca = getctladdr(user); + if (ca != NULL) + uid = ca->q_uid; + else + uid = DefUid; +#endif + + for (pp = ForwardPath; pp != NULL; pp = ep) + { + int err; + char buf[MAXPATHLEN+1]; + + ep = strchr(pp, ':'); + if (ep != NULL) + *ep = '\0'; + expand(pp, buf, &buf[sizeof buf - 1], e); + if (ep != NULL) + *ep++ = ':'; + if (tTd(27, 3)) + printf("forward: trying %s\n", buf); + + if (tTd(27, 9)) + printf("forward: old uid = %d/%d\n", getuid(), geteuid()); + +#ifdef HASSETEUID + saveduid = geteuid(); + if (saveduid == 0 && uid != 0) + (void) seteuid(uid); +#endif + + if (tTd(27, 9)) + printf("forward: new uid = %d/%d\n", getuid(), geteuid()); + + err = include(buf, TRUE, user, sendq, e); + +#ifdef HASSETEUID + if (saveduid == 0 && uid != 0) + if (seteuid(saveduid) < 0) + syserr("seteuid(%d) failure (real=%d, eff=%d)", + saveduid, getuid(), geteuid()); +#endif + + if (tTd(27, 9)) + printf("forward: reset uid = %d/%d\n", getuid(), geteuid()); - /* we do have an address to forward to -- do it */ - include(buf, "forwarding", user, sendq); + if (err == 0) + break; + if (transienterror(err)) + { + /* we have to suspend this message */ + if (tTd(27, 2)) + printf("forward: transient error on %s\n", buf); +#ifdef LOG + if (LogLevel > 2) + syslog(LOG_ERR, "%s: forward %s: transient error: %s", + e->e_id, buf, errstring(err)); +#endif + message("%s: %s: message queued", buf, errstring(err)); + user->q_flags |= QQUEUEUP|QDONTSEND; + return; + } + } } diff --git a/usr.sbin/sendmail/src/aliases b/usr.sbin/sendmail/src/aliases new file mode 100644 index 0000000000..9a9508b725 --- /dev/null +++ b/usr.sbin/sendmail/src/aliases @@ -0,0 +1,52 @@ +# +# @(#)aliases 8.1 (Berkeley) 6/9/93 +# +# Aliases in this file will NOT be expanded in the header from +# Mail, but WILL be visible over networks or from /bin/mail. +# +# >>>>>>>>>> The program "newaliases" must be run after +# >> NOTE >> this file is updated for any changes to +# >>>>>>>>>> show through to sendmail. +# + +# Basic system aliases -- these MUST be present. +MAILER-DAEMON: postmaster +postmaster: root + +# General redirections for pseudo accounts. +bin: root +daemon: root +games: root +ingres: root +nobody: root +system: root +toor: root +uucp: root + +# Well-known aliases. +manager: root +dumper: root +operator: root + +# trap decode to catch security attacks +decode: root + +# OFFICIAL CSRG/BUG ADDRESSES + +# Ftp maintainer. +ftp-bugs: bigbug@cs.berkeley.edu + +# Distribution office. +bsd-dist: bsd-dist@cs.berkeley.edu + +# Fortune maintainer. +fortune: fortune@cs.berkeley.edu + +# Termcap maintainer. +termcap: termcap@cs.berkeley.edu + +# General bug address. +ucb-fixes: bigbug@cs.berkeley.edu +ucb-fixes-request: bigbug@cs.berkeley.edu +bugs: bugs@cs.berkeley.edu +# END OFFICIAL BUG ADDRESSES diff --git a/usr.sbin/sendmail/src/aliases.5 b/usr.sbin/sendmail/src/aliases.5 index 1f75b75ceb..39f380516e 100644 --- a/usr.sbin/sendmail/src/aliases.5 +++ b/usr.sbin/sendmail/src/aliases.5 @@ -1,5 +1,5 @@ -.\" Copyright (c) 1985, 1991 The Regents of the University of California. -.\" All rights reserved. +.\" Copyright (c) 1985, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -29,9 +29,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)aliases.5 6.5 (Berkeley) 5/10/91 +.\" @(#)aliases.5 8.1 (Berkeley) 6/16/93 .\" -.Dd May 10, 1991 +.Dd June 16, 1993 .Dt ALIASES 5 .Os BSD 4 .Sh NAME @@ -71,10 +71,8 @@ file in their home directory have messages forwarded to the list of users defined in that file. .Pp This is only the raw data file; the actual aliasing information is -placed into a binary format in the files -.Pa /etc/aliases.dir -and -.Pa /etc/aliases.pag +placed into a binary format in the file +.Pa /etc/aliases.db using the program .Xr newaliases 1 . A @@ -83,6 +81,7 @@ command should be executed each time the aliases file is changed for the change to take effect. .Sh SEE ALSO .Xr newaliases 1 , +.Xr dbopen 3 , .Xr dbm 3 , .Xr sendmail 8 .Rs @@ -92,9 +91,12 @@ change to take effect. .%T "SENDMAIL An Internetwork Mail Router" .Re .Sh BUGS -Because of restrictions in +If you have compiled +.Xr sendmail +with DBM support instead of NEWDB, +you may have encounter problems in .Xr dbm 3 -a single alias cannot contain more than about 1000 bytes of information. +restricting a single alias to about 1000 bytes of information. You can get longer aliases by ``chaining''; that is, make the last name in the alias be a dummy name which is a continuation alias. .Sh HISTORY diff --git a/usr.sbin/sendmail/src/arpadate.c b/usr.sbin/sendmail/src/arpadate.c index 29bf644c93..d3f9ac574a 100644 --- a/usr.sbin/sendmail/src/arpadate.c +++ b/usr.sbin/sendmail/src/arpadate.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,13 +33,10 @@ */ #ifndef lint -static char sccsid[] = "@(#)arpadate.c 5.11 (Berkeley) 6/1/90"; +static char sccsid[] = "@(#)arpadate.c 8.1 (Berkeley) 6/7/93"; #endif /* not lint */ -# include "conf.h" -# include <time.h> -# include <sys/types.h> -# include "useful.h" +# include "sendmail.h" /* ** ARPADATE -- Create date in ARPANET format @@ -79,9 +76,6 @@ arpadate(ud) time_t t; struct tm gmt; static char b[40]; - extern struct tm *localtime(), *gmtime(); - extern char *ctime(); - extern time_t time(); /* ** Get current time. @@ -120,7 +114,9 @@ arpadate(ud) *q++ = *p++; *q++ = ' '; - p = &ud[22]; /* 79 */ + p = &ud[20]; /* 1979 */ + *q++ = *p++; + *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ' '; diff --git a/usr.sbin/sendmail/src/cdefs.h b/usr.sbin/sendmail/src/cdefs.h new file mode 100644 index 0000000000..a04665e30e --- /dev/null +++ b/usr.sbin/sendmail/src/cdefs.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cdefs.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _CDEFS_H_ +#define _CDEFS_H_ + +#if defined(__cplusplus) +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS }; +#else +#define __BEGIN_DECLS +#define __END_DECLS +#endif + +/* + * The __CONCAT macro is used to concatenate parts of symbol names, e.g. + * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo. + * The __CONCAT macro is a bit tricky -- make sure you don't put spaces + * in between its arguments. __CONCAT can also concatenate double-quoted + * strings produced by the __STRING macro, but this only works with ANSI C. + */ +#if defined(__STDC__) || defined(__cplusplus) +#define __P(protos) protos /* full-blown ANSI C */ +#define __CONCAT(x,y) x ## y +#define __STRING(x) #x + +#else /* !(__STDC__ || __cplusplus) */ +#define __P(protos) () /* traditional C preprocessor */ +#define __CONCAT(x,y) x/**/y +#define __STRING(x) "x" + +#ifdef __GNUC__ +#define const __const /* GCC: ANSI C with -traditional */ +#define inline __inline +#define signed __signed +#define volatile __volatile + +#else /* !__GNUC__ */ +#define const /* delete ANSI C keywords */ +#define inline +#define signed +#define volatile +#endif /* !__GNUC__ */ +#endif /* !(__STDC__ || __cplusplus) */ + +/* + * GCC has extensions for declaring functions as `pure' (always returns + * the same value given the same inputs, i.e., has no external state and + * no side effects) and `dead' (nonreturning). These mainly affect + * optimization and warnings. Unfortunately, GCC complains if these are + * used under strict ANSI mode (`gcc -ansi -pedantic'), hence we need to + * define them only if compiling without this. + */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define __dead __volatile +#define __pure __const +#else +#define __dead +#define __pure +#endif + +#endif /* !_CDEFS_H_ */ diff --git a/usr.sbin/sendmail/src/clock.c b/usr.sbin/sendmail/src/clock.c index 890bfc0bb1..96675c096a 100644 --- a/usr.sbin/sendmail/src/clock.c +++ b/usr.sbin/sendmail/src/clock.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,12 +33,16 @@ */ #ifndef lint -static char sccsid[] = "@(#)clock.c 5.10 (Berkeley) 3/4/91"; +static char sccsid[] = "@(#)clock.c 8.2 (Berkeley) 7/13/93"; #endif /* not lint */ # include "sendmail.h" # include <signal.h> +# ifndef sigmask +# define sigmask(s) (1 << ((s) - 1)) +# endif + /* ** SETEVENT -- set an event to happen at a specific time. ** @@ -71,7 +75,7 @@ setevent(intvl, func, arg) if (intvl <= 0) { - syserr("setevent: intvl=%ld\n", intvl); + syserr("554 setevent: intvl=%ld\n", intvl); return (NULL); } @@ -162,6 +166,9 @@ tick() register time_t now; register EVENT *ev; int mypid = getpid(); +#ifdef SIG_UNBLOCK + sigset_t ss; +#endif (void) signal(SIGALRM, SIG_IGN); (void) alarm(0); @@ -186,10 +193,17 @@ tick() /* we must be careful in here because ev_func may not return */ (void) signal(SIGALRM, tick); +#ifdef SIG_UNBLOCK + /* unblock SIGALRM signal */ + sigemptyset(&ss); + sigaddset(&ss, SIGALRM); + sigprocmask(SIG_UNBLOCK, &ss, NULL); +#else #ifdef SIGVTALRM /* reset 4.2bsd signal mask to allow future alarms */ (void) sigsetmask(sigblock(0) & ~sigmask(SIGALRM)); -#endif SIGVTALRM +#endif /* SIGVTALRM */ +#endif /* SIG_UNBLOCK */ f = ev->ev_func; arg = ev->ev_arg; @@ -231,6 +245,7 @@ tick() static bool SleepDone; +unsigned int sleep(intvl) unsigned int intvl; { diff --git a/usr.sbin/sendmail/src/collect.c b/usr.sbin/sendmail/src/collect.c index a96df1562c..4398b85721 100644 --- a/usr.sbin/sendmail/src/collect.c +++ b/usr.sbin/sendmail/src/collect.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,7 +33,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)collect.c 5.9 (Berkeley) 6/1/90"; +static char sccsid[] = "@(#)collect.c 8.1 (Berkeley) 6/7/93"; #endif /* not lint */ # include <errno.h> @@ -47,8 +47,13 @@ static char sccsid[] = "@(#)collect.c 5.9 (Berkeley) 6/1/90"; ** stripped off (after important information is extracted). ** ** Parameters: -** sayok -- if set, give an ARPANET style message -** to say we are ready to collect input. +** smtpmode -- if set, we are running SMTP: give an RFC821 +** style message to say we are ready to collect +** input, and never ignore a single dot to mean +** end of message. +** requeueflag -- this message will be requeued later, so +** don't do final processing on it. +** e -- the current envelope. ** ** Returns: ** none. @@ -58,13 +63,15 @@ static char sccsid[] = "@(#)collect.c 5.9 (Berkeley) 6/1/90"; ** The from person may be set. */ -collect(sayok) - bool sayok; +collect(smtpmode, requeueflag, e) + bool smtpmode; + bool requeueflag; + register ENVELOPE *e; { register FILE *tf; - char buf[MAXFIELD], buf2[MAXFIELD]; + bool ignrdot = smtpmode ? FALSE : IgnrDot; + char buf[MAXLINE], buf2[MAXLINE]; register char *workbuf, *freebuf; - register int workbuflen; extern char *hvalue(); extern bool isheader(), flusheol(); @@ -72,27 +79,27 @@ collect(sayok) ** Create the temp file name and create the file. */ - CurEnv->e_df = newstr(queuename(CurEnv, 'd')); - if ((tf = dfopen(CurEnv->e_df, "w")) == NULL) + e->e_df = newstr(queuename(e, 'd')); + if ((tf = dfopen(e->e_df, O_WRONLY|O_CREAT, FileMode)) == NULL) { - syserr("Cannot create %s", CurEnv->e_df); + syserr("Cannot create %s", e->e_df); NoReturn = TRUE; finis(); } - (void) chmod(CurEnv->e_df, FileMode); /* ** Tell ARPANET to go ahead. */ - if (sayok) - message("354", "Enter mail, end with \".\" on a line by itself"); + if (smtpmode) + message("354 Enter mail, end with \".\" on a line by itself"); /* ** Try to read a UNIX-style From line */ - if (sfgets(buf, MAXFIELD, InChannel) == NULL) + if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock, + "initial message read") == NULL) goto readerr; fixcrlf(buf, FALSE); # ifndef NOTUNIX @@ -100,12 +107,13 @@ collect(sayok) { if (!flusheol(buf, InChannel)) goto readerr; - eatfrom(buf); - if (sfgets(buf, MAXFIELD, InChannel) == NULL) + eatfrom(buf, e); + if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock, + "message header read") == NULL) goto readerr; fixcrlf(buf, FALSE); } -# endif NOTUNIX +# endif /* NOTUNIX */ /* ** Copy InChannel to temp file & do message editing. @@ -118,6 +126,11 @@ collect(sayok) freebuf = buf2; /* `freebuf' can be used for read-ahead */ for (;;) { + char *curbuf; + int curbuffree; + register int curbuflen; + char *p; + /* first, see if the header is over */ if (!isheader(workbuf)) { @@ -132,12 +145,19 @@ collect(sayok) /* it's okay to toss '\n' now (flusheol() needed it) */ fixcrlf(workbuf, TRUE); - workbuflen = strlen(workbuf); + curbuf = workbuf; + curbuflen = strlen(curbuf); + curbuffree = MAXLINE - curbuflen; + p = curbuf + curbuflen; /* get the rest of this field */ for (;;) { - if (sfgets(freebuf, MAXFIELD, InChannel) == NULL) + int clen; + + if (sfgets(freebuf, MAXLINE, InChannel, + TimeOuts.to_datablock, + "message header read") == NULL) goto readerr; /* is this a continuation line? */ @@ -147,25 +167,32 @@ collect(sayok) if (!flusheol(freebuf, InChannel)) goto readerr; - /* yes; append line to `workbuf' if there's room */ - if (workbuflen < MAXFIELD-3) + fixcrlf(freebuf, TRUE); + clen = strlen(freebuf) + 1; + + /* if insufficient room, dynamically allocate buffer */ + if (clen >= curbuffree) { - register char *p = workbuf + workbuflen; - register char *q = freebuf; - - /* we have room for more of this field */ - fixcrlf(freebuf, TRUE); - *p++ = '\n'; workbuflen++; - while(*q != '\0' && workbuflen < MAXFIELD-1) - { - *p++ = *q++; - workbuflen++; - } - *p = '\0'; + /* reallocate buffer */ + int nbuflen = ((p - curbuf) + clen) * 2; + char *nbuf = xalloc(nbuflen); + + p = nbuf + curbuflen; + curbuffree = nbuflen - curbuflen; + bcopy(curbuf, nbuf, curbuflen); + if (curbuf != buf && curbuf != buf2) + free(curbuf); + curbuf = nbuf; } + *p++ = '\n'; + bcopy(freebuf, p, clen - 1); + p += clen - 1; + curbuffree -= clen; + curbuflen += clen; } + *p++ = '\0'; - CurEnv->e_msgsize += workbuflen; + e->e_msgsize += curbuflen; /* ** The working buffer now becomes the free buffer, since @@ -189,8 +216,15 @@ collect(sayok) ** Snarf header away. */ - if (bitset(H_EOH, chompheader(freebuf, FALSE))) + if (bitset(H_EOH, chompheader(curbuf, FALSE, e))) break; + + /* + ** If the buffer was dynamically allocated, free it. + */ + + if (curbuf != buf && curbuf != buf2) + free(curbuf); } if (tTd(30, 1)) @@ -199,7 +233,8 @@ collect(sayok) if (*workbuf == '\0') { /* throw away a blank line */ - if (sfgets(buf, MAXFIELD, InChannel) == NULL) + if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock, + "message separator read") == NULL) goto readerr; } else if (workbuf == buf2) /* guarantee `buf' contains data */ @@ -216,11 +251,11 @@ collect(sayok) fixcrlf(buf, TRUE); /* check for end-of-message */ - if (!IgnrDot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0')) + if (!ignrdot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0')) break; /* check for transparent dot */ - if (OpMode == MD_SMTP && !IgnrDot && bp[0] == '.' && bp[1] == '.') + if (OpMode == MD_SMTP && bp[0] == '.' && bp[1] == '.') bp++; /* @@ -228,34 +263,42 @@ collect(sayok) ** file, and insert a newline if missing. */ - CurEnv->e_msgsize += strlen(bp) + 1; + e->e_msgsize += strlen(bp) + 1; fputs(bp, tf); fputs("\n", tf); if (ferror(tf)) - tferror(tf); - } while (sfgets(buf, MAXFIELD, InChannel) != NULL); + tferror(tf, e); + } while (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock, + "message body read") != NULL); readerr: if (fflush(tf) != 0) - tferror(tf); + tferror(tf, e); + (void) fsync(fileno(tf)); (void) fclose(tf); /* An EOF when running SMTP is an error */ if ((feof(InChannel) || ferror(InChannel)) && OpMode == MD_SMTP) { - int usrerr(), syserr(); + char *host; + + host = RealHostName; + if (host == NULL) + host = "localhost"; + # ifdef LOG - if (RealHostName != NULL && LogLevel > 0) + if (LogLevel > 0 && feof(InChannel)) syslog(LOG_NOTICE, - "collect: unexpected close on connection from %s: %m\n", - CurEnv->e_from.q_paddr, RealHostName); + "collect: unexpected close on connection from %s, sender=%s: %m\n", + host, e->e_from.q_paddr); # endif - (feof(InChannel) ? usrerr: syserr) - ("collect: unexpected close, from=%s", CurEnv->e_from.q_paddr); + (feof(InChannel) ? usrerr : syserr) + ("451 collect: unexpected close on connection from %s, from=%s", + host, e->e_from.q_paddr); /* don't return an error indication */ - CurEnv->e_to = NULL; - CurEnv->e_flags &= ~EF_FATALERRS; + e->e_to = NULL; + e->e_flags &= ~EF_FATALERRS; /* and don't try to deliver the partial message either */ finis(); @@ -266,31 +309,42 @@ readerr: ** Examples are who is the from person & the date. */ - eatheader(CurEnv); + eatheader(e, !requeueflag); /* ** Add an Apparently-To: line if we have no recipient lines. */ - if (hvalue("to") == NULL && hvalue("cc") == NULL && - hvalue("bcc") == NULL && hvalue("apparently-to") == NULL) + if (hvalue("to", e) == NULL && hvalue("cc", e) == NULL && + hvalue("bcc", e) == NULL && hvalue("apparently-to", e) == NULL) { register ADDRESS *q; /* create an Apparently-To: field */ /* that or reject the message.... */ - for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next) + for (q = e->e_sendqueue; q != NULL; q = q->q_next) { if (q->q_alias != NULL) continue; if (tTd(30, 3)) printf("Adding Apparently-To: %s\n", q->q_paddr); - addheader("apparently-to", q->q_paddr, CurEnv); + addheader("Apparently-To", q->q_paddr, e); } } - if ((CurEnv->e_dfp = fopen(CurEnv->e_df, "r")) == NULL) - syserr("Cannot reopen %s", CurEnv->e_df); + /* check for message too large */ + if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) + { + usrerr("552 Message exceeds maximum fixed size (%ld)", + MaxMessageSize); + } + + if ((e->e_dfp = fopen(e->e_df, "r")) == NULL) + { + /* we haven't acked receipt yet, so just chuck this */ + syserr("Cannot reopen %s", e->e_df); + finis(); + } } /* ** FLUSHEOL -- if not at EOL, throw away rest of input line. @@ -311,16 +365,22 @@ flusheol(buf, fp) char *buf; FILE *fp; { - char junkbuf[MAXLINE], *sfgets(); register char *p = buf; + bool printmsg = TRUE; + char junkbuf[MAXLINE]; - while (index(p, '\n') == NULL) { - if (sfgets(junkbuf,MAXLINE,fp) == NULL) - return(FALSE); + while (strchr(p, '\n') == NULL) + { + if (printmsg) + usrerr("553 header line too long"); + printmsg = FALSE; + if (sfgets(junkbuf, MAXLINE, fp, TimeOuts.to_datablock, + "long line flush") == NULL) + return (FALSE); p = junkbuf; } - return(TRUE); + return (TRUE); } /* ** TFERROR -- signal error on writing the temporary file. @@ -336,17 +396,18 @@ flusheol(buf, fp) ** Arranges for following output to go elsewhere. */ -tferror(tf) +tferror(tf, e) FILE *tf; + register ENVELOPE *e; { if (errno == ENOSPC) { - (void) freopen(CurEnv->e_df, "w", tf); + (void) freopen(e->e_df, "w", tf); fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf); usrerr("452 Out of disk space for temp file"); } else - syserr("collect: Cannot write %s", CurEnv->e_df); + syserr("collect: Cannot write %s", e->e_df); (void) freopen("/dev/null", "w", tf); } /* @@ -380,8 +441,9 @@ char *MonthList[] = NULL }; -eatfrom(fm) +eatfrom(fm, e) char *fm; + register ENVELOPE *e; { register char *p; register char **dt; @@ -398,7 +460,8 @@ eatfrom(fm) p++; while (*p == ' ') p++; - if (!isupper(*p) || p[3] != ' ' || p[13] != ':' || p[16] != ':') + if (!(isascii(*p) && isupper(*p)) || + p[3] != ' ' || p[13] != ':' || p[16] != ':') continue; /* we have a possible date */ @@ -415,7 +478,7 @@ eatfrom(fm) break; } - if (*p != NULL) + if (*p != '\0') { char *q; extern char *arpadate(); @@ -424,10 +487,9 @@ eatfrom(fm) q = xalloc(25); (void) strncpy(q, p, 25); q[24] = '\0'; - define('d', q, CurEnv); q = arpadate(q); - define('a', newstr(q), CurEnv); + define('a', newstr(q), e); } } -# endif NOTUNIX +# endif /* NOTUNIX */ diff --git a/usr.sbin/sendmail/src/conf.c b/usr.sbin/sendmail/src/conf.c index 292a127674..f87534803f 100644 --- a/usr.sbin/sendmail/src/conf.c +++ b/usr.sbin/sendmail/src/conf.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,11 +33,12 @@ */ #ifndef lint -static char sccsid[] = "@(#)conf.c 5.28 (Berkeley) 3/12/91"; +static char sccsid[] = "@(#)conf.c 8.3 (Berkeley) 7/13/93"; #endif /* not lint */ # include <sys/ioctl.h> # include <sys/param.h> +# include <signal.h> # include <pwd.h> # include "sendmail.h" # include "pathnames.h" @@ -47,9 +48,6 @@ static char sccsid[] = "@(#)conf.c 5.28 (Berkeley) 3/12/91"; ** ** Defines the configuration of this installation. ** -** Compilation Flags: -** VMUNIX -- running on a Berkeley UNIX system. -** ** Configuration Variables: ** HdrInfo -- a table describing well-known header fields. ** Each entry has the field name and some flags, @@ -89,8 +87,9 @@ struct hdrinfo HdrInfo[] = "from", H_FROM, "reply-to", H_FROM, "full-name", H_ACHECK, - "return-receipt-to", H_FROM, - "errors-to", H_FROM, + "return-receipt-to", H_FROM /* |H_RECEIPTTO */, + "errors-to", H_FROM|H_ERRORSTO, + /* destination fields */ "to", H_RCPT, "resent-to", H_RCPT|H_RESENT, @@ -98,40 +97,60 @@ struct hdrinfo HdrInfo[] = "resent-cc", H_RCPT|H_RESENT, "bcc", H_RCPT|H_ACHECK, "resent-bcc", H_RCPT|H_ACHECK|H_RESENT, + "apparently-to", H_RCPT, + /* message identification and control */ "message-id", 0, "resent-message-id", H_RESENT, "message", H_EOH, "text", H_EOH, + /* date fields */ "date", 0, "resent-date", H_RESENT, + /* trace fields */ "received", H_TRACE|H_FORCE, + "x400-received", H_TRACE|H_FORCE, "via", H_TRACE|H_FORCE, "mail-from", H_TRACE|H_FORCE, + /* miscellaneous fields */ + "comments", H_FORCE, + "return-path", H_FORCE|H_ACHECK, + NULL, 0, }; + /* -** ARPANET error message numbers. +** Location of system files/databases/etc. */ -char Arpa_Info[] = "050"; /* arbitrary info */ -char Arpa_TSyserr[] = "451"; /* some (transient) system error */ -char Arpa_PSyserr[] = "554"; /* some (permanent) system error */ -char Arpa_Usrerr[] = "554"; /* some (fatal) user error */ +char *ConfFile = _PATH_SENDMAILCF; /* runtime configuration */ +char *FreezeFile = _PATH_SENDMAILFC; /* frozen version of above */ +char *PidFile = _PATH_SENDMAILPID; /* stores daemon proc id */ /* -** Location of system files/databases/etc. +** Privacy values */ -char *ConfFile = _PATH_SENDMAILCF; /* runtime configuration */ -char *FreezeFile = _PATH_SENDMAILFC; /* frozen version of above */ +struct prival PrivacyValues[] = +{ + "public", PRIV_PUBLIC, + "needmailhelo", PRIV_NEEDMAILHELO, + "needexpnhelo", PRIV_NEEDEXPNHELO, + "needvrfyhelo", PRIV_NEEDVRFYHELO, + "noexpn", PRIV_NOEXPN, + "novrfy", PRIV_NOVRFY, + "restrictmailq", PRIV_RESTRMAILQ, + "authwarnings", PRIV_AUTHWARNINGS, + "goaway", PRIV_GOAWAY, + NULL, 0, +}; @@ -140,7 +159,6 @@ char *FreezeFile = _PATH_SENDMAILFC; /* frozen version of above */ */ int DtableSize = 50; /* max open files; reset in 4.2bsd */ -extern int la; /* load average */ /* ** SETDEFAULTS -- set default values ** @@ -148,7 +166,7 @@ extern int la; /* load average */ ** using direct code. ** ** Parameters: -** none. +** e -- the default envelope. ** ** Returns: ** none. @@ -158,20 +176,37 @@ extern int la; /* load average */ ** default values. */ -setdefaults() +#define DAYS * 24 * 60 * 60 + +setdefaults(e) + register ENVELOPE *e; { - QueueLA = 8; - QueueFactor = 10000; - RefuseLA = 12; - SpaceSub = ' '; - WkRecipFact = 1000; - WkClassFact = 1800; - WkTimeFact = 9000; - FileMode = 0644; - DefUid = 1; - DefGid = 1; - CheckpointInterval = 10; + SpaceSub = ' '; /* option B */ + QueueLA = 8; /* option x */ + RefuseLA = 12; /* option X */ + WkRecipFact = 30000L; /* option y */ + WkClassFact = 1800L; /* option z */ + WkTimeFact = 90000L; /* option Z */ + QueueFactor = WkRecipFact * 20; /* option q */ + FileMode = (RealUid != geteuid()) ? 0644 : 0600; + /* option F */ + DefUid = 1; /* option u */ + DefGid = 1; /* option g */ + CheckpointInterval = 10; /* option C */ + MaxHopCount = 25; /* option h */ + e->e_sendmode = SM_FORK; /* option d */ + e->e_errormode = EM_PRINT; /* option e */ + SevenBit = FALSE; /* option 7 */ + MaxMciCache = 1; /* option k */ + MciCacheTimeout = 300; /* option K */ + LogLevel = 9; /* option L */ + settimeouts(NULL); /* option r */ + TimeOuts.to_q_return = 5 DAYS; /* option T */ + TimeOuts.to_q_warning = 0; /* option T */ + PrivacyFlags = 0; /* option p */ setdefuser(); + setupmaps(); + setupmailers(); } @@ -182,42 +217,140 @@ setdefaults() setdefuser() { struct passwd *defpwent; + static char defuserbuf[40]; - if (DefUser != NULL) - free(DefUser); + DefUser = defuserbuf; if ((defpwent = getpwuid(DefUid)) != NULL) - DefUser = newstr(defpwent->pw_name); + strcpy(defuserbuf, defpwent->pw_name); else - DefUser = newstr("nobody"); + strcpy(defuserbuf, "nobody"); } + /* +** HOST_MAP_INIT -- initialize host class structures +*/ +bool +host_map_init(map, args) + MAP *map; + char *args; +{ + register char *p = args; -/* -** GETRUID -- get real user id (V7) + for (;;) + { + while (isascii(*p) && isspace(*p)) + p++; + if (*p != '-') + break; + switch (*++p) + { + case 'a': + map->map_app = ++p; + break; + } + while (*p != '\0' && !(isascii(*p) && isspace(*p))) + p++; + if (*p != '\0') + *p++ = '\0'; + } + if (map->map_app != NULL) + map->map_app = newstr(map->map_app); + return TRUE; +} + /* +** SETUPMAILERS -- initialize default mailers */ -getruid() +setupmailers() { - if (OpMode == MD_DAEMON) - return (RealUid); - else - return (getuid()); -} + char buf[100]; + strcpy(buf, "prog, P=/bin/sh, F=lsD, A=sh -c $u"); + makemailer(buf); -/* -** GETRGID -- get real group id (V7). + strcpy(buf, "*file*, P=/dev/null, F=lsDFMPEu, A=FILE"); + makemailer(buf); + + strcpy(buf, "*include*, P=/dev/null, F=su, A=INCLUDE"); + makemailer(buf); +} + /* +** SETUPMAPS -- set up map classes */ -getrgid() +#define MAPDEF(name, ext, flags, parse, open, close, lookup, store) \ + { \ + extern bool parse __P((MAP *, char *)); \ + extern bool open __P((MAP *, int)); \ + extern void close __P((MAP *)); \ + extern char *lookup __P((MAP *, char *, char **, int *)); \ + extern void store __P((MAP *, char *, char *)); \ + s = stab(name, ST_MAPCLASS, ST_ENTER); \ + s->s_mapclass.map_cname = name; \ + s->s_mapclass.map_ext = ext; \ + s->s_mapclass.map_cflags = flags; \ + s->s_mapclass.map_parse = parse; \ + s->s_mapclass.map_open = open; \ + s->s_mapclass.map_close = close; \ + s->s_mapclass.map_lookup = lookup; \ + s->s_mapclass.map_store = store; \ + } + +setupmaps() { - if (OpMode == MD_DAEMON) - return (RealGid); - else - return (getgid()); + register STAB *s; + +#ifdef NEWDB + MAPDEF("hash", ".db", MCF_ALIASOK|MCF_REBUILDABLE, + map_parseargs, hash_map_open, db_map_close, + db_map_lookup, db_map_store); + MAPDEF("btree", ".db", MCF_ALIASOK|MCF_REBUILDABLE, + map_parseargs, bt_map_open, db_map_close, + db_map_lookup, db_map_store); +#endif + +#ifdef NDBM + MAPDEF("dbm", ".dir", MCF_ALIASOK|MCF_REBUILDABLE, + map_parseargs, ndbm_map_open, ndbm_map_close, + ndbm_map_lookup, ndbm_map_store); +#endif + +#ifdef NIS + MAPDEF("nis", NULL, MCF_ALIASOK, + map_parseargs, nis_map_open, nis_map_close, + nis_map_lookup, nis_map_store); +#endif + + MAPDEF("stab", NULL, MCF_ALIASOK|MCF_ALIASONLY, + map_parseargs, stab_map_open, stab_map_close, + stab_map_lookup, stab_map_store); + + MAPDEF("implicit", NULL, MCF_ALIASOK|MCF_ALIASONLY|MCF_REBUILDABLE, + map_parseargs, impl_map_open, impl_map_close, + impl_map_lookup, impl_map_store); + + /* host DNS lookup */ + MAPDEF("host", NULL, 0, + host_map_init, null_map_open, null_map_close, + host_map_lookup, null_map_store); + + /* dequote map */ + MAPDEF("dequote", NULL, 0, + dequote_init, null_map_open, null_map_close, + dequote_map, null_map_store); + +#if 0 +# ifdef USERDB + /* user database */ + MAPDEF("udb", ".db", 0, + udb_map_parse, null_map_open, null_map_close, + udb_map_lookup, null_map_store); +# endif +#endif } -/* +#undef MAPDEF + /* ** USERNAME -- return the user id of the logged in user. ** ** Parameters: @@ -246,26 +379,26 @@ username() myname = getlogin(); if (myname == NULL || myname[0] == '\0') { - - pw = getpwuid(getruid()); + pw = getpwuid(RealUid); if (pw != NULL) myname = newstr(pw->pw_name); } else { + uid_t uid = RealUid; myname = newstr(myname); if ((pw = getpwnam(myname)) == NULL || - getuid() != pw->pw_uid) + (uid != 0 && uid != pw->pw_uid)) { - pw = getpwuid(getuid()); + pw = getpwuid(uid); if (pw != NULL) myname = newstr(pw->pw_name); } } if (myname == NULL || myname[0] == '\0') { - syserr("Who are you?"); + syserr("554 Who are you?"); myname = "postmaster"; } } @@ -296,8 +429,6 @@ username() ** savemail */ -# include <sys/stat.h> - char * ttypath() { @@ -337,7 +468,7 @@ ttypath() ** forwarding or registration of users. ** ** If the hosts are found to be incompatible, an error -** message should be given using "usrerr" and FALSE should +** message should be given using "usrerr" and 0 should ** be returned. ** ** 'NoReturn' can be set to suppress the return-to-sender @@ -347,16 +478,15 @@ ttypath() ** to -- the person being sent to. ** ** Returns: -** TRUE -- ok to send. -** FALSE -- not ok. +** an exit status ** ** Side Effects: ** none (unless you include the usrerr stuff) */ -bool -checkcompat(to) +checkcompat(to, e) register ADDRESS *to; + register ENVELOPE *e; { # ifdef lint if (to == NULL) @@ -367,15 +497,15 @@ checkcompat(to) register STAB *s; s = stab("arpa", ST_MAILER, ST_FIND); - if (s != NULL && CurEnv->e_from.q_mailer != LocalMailer && + if (s != NULL && e->e_from.q_mailer != LocalMailer && to->q_mailer == s->s_mailer) { - usrerr("No ARPA mail through this machine: see your system administration"); + usrerr("553 No ARPA mail through this machine: see your system administration"); /* NoReturn = TRUE; to supress return copy */ - return (FALSE); + return (EX_UNAVAILABLE); } -# endif EXAMPLE_CODE - return (TRUE); +# endif /* EXAMPLE_CODE */ + return (EX_OK); } /* ** HOLDSIGS -- arrange to hold all signals @@ -426,61 +556,201 @@ rlsesigs() ** none. */ -#ifndef sun - -getla() -{ - double avenrun[3]; - -#ifdef notyet - if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) < 0) - return (0); - return ((int) (avenrun[0] + 0.5)); -#else - return (1); +/* try to guess what style of load average we have */ +#define LA_ZERO 1 /* always return load average as zero */ +#define LA_INT 2 /* read kmem for avenrun; interpret as int */ +#define LA_FLOAT 3 /* read kmem for avenrun; interpret as float */ +#define LA_SUBR 4 /* call getloadavg */ + +#ifndef LA_TYPE +# if defined(sun) && !defined(BSD) +# define LA_TYPE LA_INT +# endif +# if defined(mips) || defined(__alpha) + /* Ultrix or OSF/1 or RISC/os */ +# define LA_TYPE LA_INT +# define LA_AVENRUN "avenrun" +# endif +# if defined(__hpux) +# define LA_TYPE LA_FLOAT +# define LA_AVENRUN "avenrun" +# endif +# if defined(__NeXT__) +# define LA_TYPE LA_ZERO +# endif + +/* now do the guesses based on general OS type */ +# ifndef LA_TYPE +# if defined(SYSTEM5) +# define LA_TYPE LA_INT +# define LA_AVENRUN "avenrun" +# else +# if defined(BSD) +# define LA_TYPE LA_SUBR +# else +# define LA_TYPE LA_ZERO +# endif +# endif +# endif #endif -} -#else /* sun */ +#if (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) #include <nlist.h> +#ifndef LA_AVENRUN +#define LA_AVENRUN "_avenrun" +#endif + +/* _PATH_UNIX should be defined in <paths.h> */ +#ifndef _PATH_UNIX +# if defined(__hpux) +# define _PATH_UNIX "/hp-ux" +# endif +# if defined(mips) && !defined(ultrix) + /* powerful RISC/os */ +# define _PATH_UNIX "/unix" +# endif +# if defined(Solaris2) + /* Solaris 2 */ +# define _PATH_UNIX "/kernel/unix" +# endif +# if defined(SYSTEM5) +# ifndef _PATH_UNIX +# define _PATH_UNIX "/unix" +# endif +# endif +# ifndef _PATH_UNIX +# define _PATH_UNIX "/vmunix" +# endif +#endif + struct nlist Nl[] = { - { "_avenrun" }, + { LA_AVENRUN }, #define X_AVENRUN 0 { 0 }, }; +#ifndef FSHIFT +# if defined(unixpc) +# define FSHIFT 5 +# endif + +# if defined(__alpha) +# define FSHIFT 10 +# endif -extern int la; +# if (LA_TYPE == LA_INT) +# define FSHIFT 8 +# endif +#endif + +#if (LA_TYPE == LA_INT) && !defined(FSCALE) +# define FSCALE (1 << FSHIFT) +#endif getla() { static int kmem = -1; +#if LA_TYPE == LA_INT long avenrun[3]; +#else + double avenrun[3]; +#endif extern off_t lseek(); + extern int errno; if (kmem < 0) { kmem = open("/dev/kmem", 0, 0); if (kmem < 0) + { + if (tTd(3, 1)) + printf("getla: open(/dev/kmem): %s\n", + errstring(errno)); return (-1); - (void) ioctl(kmem, (int) FIOCLEX, (char *) 0); - nlist("/vmunix", Nl); - if (Nl[0].n_type == 0) + } + (void) fcntl(kmem, F_SETFD, 1); + if (nlist(_PATH_UNIX, Nl) < 0) + { + if (tTd(3, 1)) + printf("getla: nlist(%s): %s\n", _PATH_UNIX, + errstring(errno)); + return (-1); + } + if (Nl[X_AVENRUN].n_value == 0) + { + if (tTd(3, 1)) + printf("getla: nlist(%s, %s) ==> 0\n", + _PATH_UNIX, LA_AVENRUN); return (-1); + } } + if (tTd(3, 20)) + printf("getla: symbol address = %#x\n", Nl[X_AVENRUN].n_value); if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, 0) == -1 || read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun)) { /* thank you Ian */ + if (tTd(3, 1)) + printf("getla: lseek or read: %s\n", errstring(errno)); return (-1); } +#if LA_TYPE == LA_INT + if (tTd(3, 5)) + { + printf("getla: avenrun = %d", avenrun[0]); + if (tTd(3, 15)) + printf(", %d, %d", avenrun[1], avenrun[2]); + printf("\n"); + } + if (tTd(3, 1)) + printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT); return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT); +#else + if (tTd(3, 5)) + { + printf("getla: avenrun = %g", avenrun[0]); + if (tTd(3, 15)) + printf(", %g, %g", avenrun[1], avenrun[2]); + printf("\n"); + } + if (tTd(3, 1)) + printf("getla: %d\n", (int) (avenrun[0] +0.5)); + return ((int) (avenrun[0] + 0.5)); +#endif } -#endif /* sun */ +#else +#if LA_TYPE == LA_SUBR + +getla() +{ + double avenrun[3]; + + if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) < 0) + { + if (tTd(3, 1)) + perror("getla: getloadavg failed:"); + return (-1); + } + if (tTd(3, 1)) + printf("getla: %d\n", (int) (avenrun[0] +0.5)); + return ((int) (avenrun[0] + 0.5)); +} + +#else + +getla() +{ + if (tTd(3, 1)) + printf("getla: ZERO\n"); + return (0); +} + +#endif +#endif /* ** SHOULDQUEUE -- should this message be queued or sent? ** @@ -488,6 +758,7 @@ getla() ** ** Parameters: ** pri -- the priority of the message in question. +** ctime -- the message creation time. ** ** Returns: ** TRUE -- if this message should be queued up for the @@ -499,12 +770,41 @@ getla() */ bool -shouldqueue(pri) +shouldqueue(pri, ctime) long pri; + time_t ctime; { - if (la < QueueLA) + if (CurrentLA < QueueLA) return (FALSE); - return (pri > (QueueFactor / (la - QueueLA + 1))); + if (CurrentLA >= RefuseLA) + return (TRUE); + return (pri > (QueueFactor / (CurrentLA - QueueLA + 1))); +} + /* +** REFUSECONNECTIONS -- decide if connections should be refused +** +** Parameters: +** none. +** +** Returns: +** TRUE if incoming SMTP connections should be refused +** (for now). +** FALSE if we should accept new work. +** +** Side Effects: +** none. +*/ + +bool +refuseconnections() +{ +#ifdef XLA + if (!xla_smtp_ok()) + return TRUE; +#endif + + /* this is probably too simplistic */ + return (CurrentLA >= RefuseLA); } /* ** SETPROCTITLE -- set process title for ps @@ -521,34 +821,75 @@ shouldqueue(pri) ** display the title. */ +#ifdef SETPROCTITLE +# ifdef __hpux +# include <sys/pstat.h> +# endif +# ifdef BSD4_4 +# include <machine/vmparam.h> +# include <sys/exec.h> +# ifdef PS_STRINGS +# define SETPROC_STATIC static +# endif +# endif +# ifndef SETPROC_STATIC +# define SETPROC_STATIC +# endif +#endif + /*VARARGS1*/ -setproctitle(fmt, a, b, c) +#ifdef __STDC__ +setproctitle(char *fmt, ...) +#else +setproctitle(fmt, va_alist) char *fmt; + va_dcl +#endif { # ifdef SETPROCTITLE register char *p; register int i; + SETPROC_STATIC char buf[MAXLINE]; + VA_LOCAL_DECL +# ifdef __hpux + union pstun pst; +# endif extern char **Argv; extern char *LastArgv; - char buf[MAXLINE]; - (void) sprintf(buf, fmt, a, b, c); + p = buf; - /* make ps print "(sendmail)" */ - p = Argv[0]; - *p++ = '-'; + /* print sendmail: heading for grep */ + (void) strcpy(p, "sendmail: "); + p += strlen(p); + + /* print the argument string */ + VA_START(fmt); + (void) vsprintf(p, fmt, ap); + VA_END; i = strlen(buf); - if (i > LastArgv - p - 2) + +# ifdef __hpux + pst.pst_command = buf; + pstat(PSTAT_SETCMD, pst, i, 0, 0); +# else +# ifdef PS_STRINGS + PS_STRINGS->ps_nargvstr = 1; + PS_STRINGS->ps_argvstr = buf; +# else + if (i > LastArgv - Argv[0] - 2) { - i = LastArgv - p - 2; + i = LastArgv - Argv[0] - 2; buf[i] = '\0'; } - (void) strcpy(p, buf); - p += i; + (void) strcpy(Argv[0], buf); + p = &Argv[0][i]; while (p < LastArgv) *p++ = ' '; -# endif SETPROCTITLE +# endif +# endif +# endif /* SETPROCTITLE */ } /* ** REAPCHILD -- pick up the body of my child, lest it become a zombie @@ -563,22 +904,483 @@ setproctitle(fmt, a, b, c) ** Picks up extant zombies. */ -# ifdef VMUNIX # include <sys/wait.h> -# endif VMUNIX void reapchild() { +# if defined(WIFEXITED) && !defined(__NeXT__) + auto int status; + int count; + int pid; + + count = 0; + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) + { + if (count++ > 1000) + { + syslog(LOG_ALERT, "reapchild: waitpid loop: pid=%d, status=%x", + pid, status); + break; + } + } +# else # ifdef WNOHANG union wait status; - while (wait3((int *)&status, WNOHANG, (struct rusage *) NULL) > 0) + while (wait3(&status, WNOHANG, (struct rusage *) NULL) > 0) continue; -# else WNOHANG +# else /* WNOHANG */ auto int status; - while (wait((int *)&status) > 0) + while (wait(&status) > 0) continue; -# endif WNOHANG +# endif /* WNOHANG */ +# endif +# ifdef SYSTEM5 + (void) signal(SIGCHLD, reapchild); +# endif +} + /* +** UNSETENV -- remove a variable from the environment +** +** Not needed on newer systems. +** +** Parameters: +** name -- the string name of the environment variable to be +** deleted from the current environment. +** +** Returns: +** none. +** +** Globals: +** environ -- a pointer to the current environment. +** +** Side Effects: +** Modifies environ. +*/ + +#ifdef UNSETENV + +void +unsetenv(name) + char *name; +{ + extern char **environ; + register char **pp; + int len = strlen(name); + + for (pp = environ; *pp != NULL; pp++) + { + if (strncmp(name, *pp, len) == 0 && + ((*pp)[len] == '=' || (*pp)[len] == '\0')) + break; + } + + for (; *pp != NULL; pp++) + *pp = pp[1]; +} + +#endif /* UNSETENV */ + /* +** GETDTABLESIZE -- return number of file descriptors +** +** Only on non-BSD systems +** +** Parameters: +** none +** +** Returns: +** size of file descriptor table +** +** Side Effects: +** none +*/ + +#ifdef SOLARIS +# include <sys/resource.h> +#endif + +int +getdtsize() +{ +#ifdef RLIMIT_NOFILE + struct rlimit rl; + + if (getrlimit(RLIMIT_NOFILE, &rl) >= 0) + return rl.rlim_cur; +#endif + +# if defined(_SC_OPEN_MAX) && !defined(NO_SYSCONF) + return sysconf(_SC_OPEN_MAX); +# else +# ifdef HASGETDTABLESIZE + return getdtablesize(); +# else + return NOFILE; +# endif +# endif +} + /* +** UNAME -- get the UUCP name of this system. +*/ + +#ifndef HASUNAME + +int +uname(name) + struct utsname *name; +{ + FILE *file; + char *n; + + name->nodename[0] = '\0'; + + /* try /etc/whoami -- one line with the node name */ + if ((file = fopen("/etc/whoami", "r")) != NULL) + { + (void) fgets(name->nodename, NODE_LENGTH + 1, file); + (void) fclose(file); + n = strchr(name->nodename, '\n'); + if (n != NULL) + *n = '\0'; + if (name->nodename[0] != '\0') + return (0); + } + + /* try /usr/include/whoami.h -- has a #define somewhere */ + if ((file = fopen("/usr/include/whoami.h", "r")) != NULL) + { + char buf[MAXLINE]; + + while (fgets(buf, MAXLINE, file) != NULL) + if (sscanf(buf, "#define sysname \"%*[^\"]\"", + NODE_LENGTH, name->nodename) > 0) + break; + (void) fclose(file); + if (name->nodename[0] != '\0') + return (0); + } + +#ifdef TRUST_POPEN + /* + ** Popen is known to have security holes. + */ + + /* try uuname -l to return local name */ + if ((file = popen("uuname -l", "r")) != NULL) + { + (void) fgets(name, NODE_LENGTH + 1, file); + (void) pclose(file); + n = strchr(name, '\n'); + if (n != NULL) + *n = '\0'; + if (name->nodename[0] != '\0') + return (0); + } +#endif + + return (-1); +} +#endif /* HASUNAME */ + /* +** INITGROUPS -- initialize groups +** +** Stub implementation for System V style systems +*/ + +#ifndef HASINITGROUPS +# if !defined(SYSTEM5) || defined(__hpux) +# define HASINITGROUPS +# endif +#endif + +#ifndef HASINITGROUPS + +initgroups(name, basegid) + char *name; + int basegid; +{ + return 0; +} + +#endif + /* +** SETSID -- set session id (for non-POSIX systems) +*/ + +#ifndef HASSETSID + +pid_t +setsid __P ((void)) +{ +# ifdef SYSTEM5 + return setpgrp(); +# else + return 0; +# endif +} + +#endif + /* +** ENOUGHSPACE -- check to see if there is enough free space on the queue fs +** +** Only implemented if you have statfs. +** +** Parameters: +** msize -- the size to check against. If zero, we don't yet +** know how big the message will be, so just check for +** a "reasonable" amount. +** +** Returns: +** TRUE if there is enough space. +** FALSE otherwise. +*/ + +#ifndef HASSTATFS +# if defined(BSD4_4) || defined(__osf__) +# define HASSTATFS +# endif +#endif + +#ifdef HASSTATFS +# undef HASUSTAT +#endif + +#if defined(HASUSTAT) +# include <ustat.h> +#endif + +#ifdef HASSTATFS +# if defined(sgi) || defined(apollo) +# include <sys/statfs.h> +# else +# if (defined(sun) && !defined(BSD)) || defined(__hpux) +# include <sys/vfs.h> +# else +# include <sys/mount.h> +# endif +# endif +#endif + +bool +enoughspace(msize) + long msize; +{ +#if defined(HASSTATFS) || defined(HASUSTAT) +# if defined(HASUSTAT) + struct ustat fs; + struct stat statbuf; +# define FSBLOCKSIZE DEV_BSIZE +# define f_bavail f_tfree +# else +# if defined(ultrix) + struct fs_data fs; +# define f_bavail fd_bfreen +# define FSBLOCKSIZE fs.fd_bsize +# else + struct statfs fs; +# define FSBLOCKSIZE fs.f_bsize +# endif +# endif + extern int errno; + + if (MinBlocksFree <= 0 && msize <= 0) + { + if (tTd(4, 80)) + printf("enoughspace: no threshold\n"); + return TRUE; + } + +# if defined(HASUSTAT) + if (stat(QueueDir, &statbuf) == 0 && ustat(statbuf.st_dev, &fs) == 0) +# else +# if defined(sgi) || defined(apollo) + if (statfs(QueueDir, &fs, sizeof fs, 0) == 0) +# else +# if defined(ultrix) + if (statfs(QueueDir, &fs) > 0) +# else + if (statfs(QueueDir, &fs) == 0) +# endif +# endif +# endif + { + if (tTd(4, 80)) + printf("enoughspace: bavail=%ld, need=%ld\n", + fs.f_bavail, msize); + + /* convert msize to block count */ + msize = msize / FSBLOCKSIZE + 1; + if (MinBlocksFree >= 0) + msize += MinBlocksFree; + + if (fs.f_bavail < msize) + { +#ifdef LOG + if (LogLevel > 0) + syslog(LOG_ALERT, "%s: low on space (have %ld, need %ld)", + QueueDir, fs.f_bavail, msize); +#endif + return FALSE; + } + } + else if (tTd(4, 80)) + printf("enoughspace failure: min=%ld, need=%ld: %s\n", + MinBlocksFree, msize, errstring(errno)); +#endif + return TRUE; +} + /* +** TRANSIENTERROR -- tell if an error code indicates a transient failure +** +** This looks at an errno value and tells if this is likely to +** go away if retried later. +** +** Parameters: +** err -- the errno code to classify. +** +** Returns: +** TRUE if this is probably transient. +** FALSE otherwise. +*/ + +bool +transienterror(err) + int err; +{ + switch (err) + { + case EIO: /* I/O error */ + case ENXIO: /* Device not configured */ + case EAGAIN: /* Resource temporarily unavailable */ + case ENOMEM: /* Cannot allocate memory */ + case ENODEV: /* Operation not supported by device */ + case ENFILE: /* Too many open files in system */ + case EMFILE: /* Too many open files */ + case ENOSPC: /* No space left on device */ +#ifdef ETIMEDOUT + case ETIMEDOUT: /* Connection timed out */ +#endif +#ifdef ESTALE + case ESTALE: /* Stale NFS file handle */ +#endif +#ifdef ENETDOWN + case ENETDOWN: /* Network is down */ +#endif +#ifdef ENETUNREACH + case ENETUNREACH: /* Network is unreachable */ +#endif +#ifdef ENETRESET + case ENETRESET: /* Network dropped connection on reset */ +#endif +#ifdef ECONNABORTED + case ECONNABORTED: /* Software caused connection abort */ +#endif +#ifdef ECONNRESET + case ECONNRESET: /* Connection reset by peer */ +#endif +#ifdef ENOBUFS + case ENOBUFS: /* No buffer space available */ +#endif +#ifdef ESHUTDOWN + case ESHUTDOWN: /* Can't send after socket shutdown */ +#endif +#ifdef ECONNREFUSED + case ECONNREFUSED: /* Connection refused */ +#endif +#ifdef EHOSTDOWN + case EHOSTDOWN: /* Host is down */ +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: /* No route to host */ +#endif +#ifdef EDQUOT + case EDQUOT: /* Disc quota exceeded */ +#endif +#ifdef EPROCLIM + case EPROCLIM: /* Too many processes */ +#endif +#ifdef EUSERS + case EUSERS: /* Too many users */ +#endif +#ifdef EDEADLK + case EDEADLK: /* Resource deadlock avoided */ +#endif +#ifdef EISCONN + case EISCONN: /* Socket already connected */ +#endif +#ifdef EINPROGRESS + case EINPROGRESS: /* Operation now in progress */ +#endif +#ifdef EALREADY + case EALREADY: /* Operation already in progress */ +#endif +#ifdef EADDRINUSE + case EADDRINUSE: /* Address already in use */ +#endif +#ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: /* Can't assign requested address */ +#endif +#ifdef ENOSR + case ENOSR: /* Out of streams resources */ +#endif + return TRUE; + } + + /* nope, must be permanent */ + return FALSE; +} + /* +** LOCKFILE -- lock a file using flock or (shudder) lockf +** +** Parameters: +** fd -- the file descriptor of the file. +** filename -- the file name (for error messages). +** type -- type of the lock. Bits can be: +** LOCK_EX -- exclusive lock. +** LOCK_NB -- non-blocking. +** +** Returns: +** TRUE if the lock was acquired. +** FALSE otherwise. +*/ + +bool +lockfile(fd, filename, type) + int fd; + char *filename; + int type; +{ +# ifdef LOCKF + int action; + struct flock lfd; + + if (bitset(LOCK_UN, type)) + lfd.l_type = F_UNLCK; + else if (bitset(LOCK_EX, type)) + lfd.l_type = F_WRLCK; + else + lfd.l_type = F_RDLCK; + + if (bitset(LOCK_NB, type)) + action = F_SETLK; + else + action = F_SETLKW; + + lfd.l_whence = lfd.l_start = lfd.l_len = 0; + + if (fcntl(fd, action, &lfd) >= 0) + return TRUE; + + if (!bitset(LOCK_NB, type) || (errno != EACCES && errno != EAGAIN)) + syserr("cannot lockf(%s, %o)", filename, type); +# else + if (flock(fd, type) >= 0) + return TRUE; + + if (!bitset(LOCK_NB, type) || errno != EWOULDBLOCK) + syserr("cannot flock(%s, %o)", filename, type); +# endif + return FALSE; } diff --git a/usr.sbin/sendmail/src/conf.h b/usr.sbin/sendmail/src/conf.h index caf66fe9ba..e3ea487198 100644 --- a/usr.sbin/sendmail/src/conf.h +++ b/usr.sbin/sendmail/src/conf.h @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,31 +31,41 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)conf.h 5.17 (Berkeley) 6/1/90 + * @(#)conf.h 8.3 (Berkeley) 7/13/93 */ /* ** CONF.H -- All user-configurable parameters for sendmail */ +# include <sys/param.h> +# include <sys/stat.h> +# include <fcntl.h> + /* ** Table sizes, etc.... ** There shouldn't be much need to change these.... */ -# define MAXLINE 1024 /* max line length */ +# define MAXLINE 2048 /* max line length */ # define MAXNAME 256 /* max length of a name */ -# define MAXFIELD 4096 /* max total length of a hdr field */ # define MAXPV 40 /* max # of parms to mailers */ -# define MAXHOP 17 /* max value of HopCount */ -# define MAXATOM 100 /* max atoms per address */ +# define MAXATOM 200 /* max atoms per address */ # define MAXMAILERS 25 /* maximum mailers known to system */ -# define MAXRWSETS 30 /* max # of sets of rewriting rules */ +# define MAXRWSETS 100 /* max # of sets of rewriting rules */ # define MAXPRIORITIES 25 /* max values for Precedence: field */ -# define MAXTRUST 30 /* maximum number of trusted users */ -# define MAXUSERENVIRON 40 /* max # of items in user environ */ -# define QUEUESIZE 600 /* max # of jobs per queue run */ -# define MAXMXHOSTS 10 /* max # of MX records */ +# define MAXMXHOSTS 20 /* max # of MX records */ +# define SMTPLINELIM 990 /* maximum SMTP line length */ +# define MAXKEY 128 /* maximum size of a database key */ +# define MEMCHUNKSIZE 1024 /* chunk size for memory allocation */ +# define MAXUSERENVIRON 100 /* max envars saved, must be >= 3 */ +# define MAXIPADDR 16 /* max # of IP addrs for this host */ +# define MAXALIASDB 12 /* max # of alias databases */ +# define PSBUFSIZE (MAXLINE + MAXATOM) /* size of prescan buffer */ + +# ifndef QUEUESIZE +# define QUEUESIZE 1000 /* max # of jobs per queue run */ +# endif /* ** Compilation options. @@ -63,22 +73,266 @@ ** #define these if they are available; comment them out otherwise. */ -/* # define DBM 1 /* use DBM library (requires -ldbm) */ -/* # define NDBM 1 /* new DBM library available (requires DBM) */ # define LOG 1 /* enable logging */ -# define SMTP 1 /* enable user and server SMTP */ -# define QUEUE 1 /* enable queueing */ # define UGLYUUCP 1 /* output ugly UUCP From lines */ -# define DAEMON 1 /* include the daemon (requires IPC & SMTP) */ +# define NETINET 1 /* include internet support */ # define SETPROCTITLE 1 /* munge argv to display current status */ # define NAMED_BIND 1 /* use Berkeley Internet Domain Server */ +# define MATCHGECOS 1 /* match user names from gecos field */ +# define XDEBUG 1 /* enable extended debugging */ + +# ifdef NEWDB +# define USERDB 1 /* look in user database (requires NEWDB) */ +# endif + +/* +** Operating system configuration. +** +** Unless you are porting to a new OS, you shouldn't have to +** change these. +*/ + +/* general "standard C" defines */ +#ifdef __STDC__ +# define HASSETVBUF 1 /* yes, we have setvbuf in libc */ +#endif + +/* general POSIX defines */ +#ifdef _POSIX_VERSION +# define HASSETSID 1 /* has setsid(2) call */ +#endif + +/* +** Per-Operating System defines +*/ + +/* HP-UX -- tested for 8.07 */ +# ifdef __hpux +# define SYSTEM5 1 /* include all the System V defines */ +# define UNSETENV 1 /* need unsetenv(3) support */ +# define HASSETEUID 1 /* we have seteuid call */ +# define seteuid(uid) setresuid(-1, uid, -1) +# ifndef __STDC__ +# define HASSETVBUF 1 /* we have setvbuf in libc (but not __STDC__) */ +# endif +# endif + +/* IBM AIX 3.x -- actually tested for 3.2.3 */ +# ifdef _AIX3 +# define LOCKF 1 /* use System V lockf instead of flock */ +# define FORK fork /* no vfork primitive available */ +# define UNSETENV 1 /* need unsetenv(3) support */ +# define SYS5TZ 1 /* use System V style timezones */ +# endif + +/* Silicon Graphics IRIX */ +# ifdef IRIX +# define FORK fork /* no vfork primitive available */ +# define UNSETENV 1 /* need unsetenv(3) support */ +# define setpgrp BSDsetpgrp +# endif + +/* various systems from Sun Microsystems */ +#if defined(sun) && !defined(BSD) + +# define UNSETENV 1 /* need unsetenv(3) support */ + +# ifdef SOLARIS + /* Solaris 2.x */ +# define LOCKF 1 /* use System V lockf instead of flock */ +# define HASUSTAT 1 /* has the ustat(2) syscall */ +# define bcopy(s, d, l) (memmove((d), (s), (l))) +# define bzero(d, l) (memset((d), '\0', (l))) +# define bcmp(s, d, l) (memcmp((s), (d), (l))) +# include <sys/time.h> + +# else + /* SunOS 4.1.x */ +# define HASSTATFS 1 /* has the statfs(2) syscall */ +# define HASSETEUID 1 /* we have seteuid call */ +# include <vfork.h> + +# endif +#endif + +/* Digital Ultrix 4.2A or 4.3 */ +#ifdef ultrix +# define HASSTATFS 1 /* has the statfs(2) syscall */ +# define HASSETEUID 1 /* we have seteuid call */ +#endif + +/* OSF/1 (tested on Alpha) */ +#ifdef __osf__ +# define HASSETEUID 1 /* we have seteuid call */ +# define seteuid(uid) setreuid(-1, uid) +#endif + +/* NeXTstep */ +#ifdef __NeXT__ +# define sleep sleepX +# define UNSETENV 1 /* need unsetenv(3) support */ +#endif + +/* various flavors of BSD */ +#ifdef BSD +# define HASGETDTABLESIZE 1 /* we have getdtablesize(2) call */ +#endif + +#if defined(NetBSD) +#define NO_SYSCONF +#endif + +/* 4.4BSD */ +#ifdef BSD4_4 +# include <sys/cdefs.h> +# define HASSETEUID 1 /* we have seteuid(2) call */ +#endif + +/* +** End of Per-Operating System defines +*/ + +/* general System V defines */ +# ifdef SYSTEM5 +# define LOCKF 1 /* use System V lockf instead of flock */ +# define SYS5TZ 1 /* use System V style timezones */ +# define HASUNAME 1 /* use System V uname system call */ +# define NEEDGETDTABLESIZE 1 /* needs a replacement getdtablesize */ +# endif + +/* +** Due to a "feature" in some operating systems such as Ultrix 4.3 and +** HPUX 8.0, if you receive a "No route to host" message (ICMP message +** ICMP_UNREACH_HOST) on _any_ connection, all connections to that host +** are closed. Some firewalls return this error if you try to connect +** to the IDENT port (113), so you can't receive email from these hosts +** on these systems. The firewall really should use a more specific +** message such as ICMP_UNREACH_PROTOCOL or _PORT or _NET_PROHIB. +*/ + +#if !defined(ultrix) && !defined(__hpux) +# define IDENTPROTO 1 /* use IDENT proto (RFC 1413) */ +#endif + +/* +** Remaining definitions should never have to be changed. They are +** primarily to provide back compatibility for older systems -- for +** example, it includes some POSIX compatibility definitions +*/ + +/* System 5 compatibility */ +#ifndef S_ISREG +#define S_ISREG(foo) ((foo & S_IFREG) == S_IFREG) +#endif +#ifndef S_IWGRP +#define S_IWGRP 020 +#endif +#ifndef S_IWOTH +#define S_IWOTH 002 +#endif + +/* +** Older systems don't have this error code -- it should be in +** /usr/include/sysexits.h. +*/ + +# ifndef EX_CONFIG +# define EX_CONFIG 78 /* configuration error */ +# endif + +#ifndef __P +# include "cdefs.h" +#endif + +/* +** Do some required dependencies +*/ + +#if defined(NETINET) || defined(NETISO) +# define SMTP 1 /* enable user and server SMTP */ +# define QUEUE 1 /* enable queueing */ +# define DAEMON 1 /* include the daemon (requires IPC & SMTP) */ +#endif + + +/* +** Arrange to use either varargs or stdargs +*/ + +# ifdef __STDC__ + +# include <stdarg.h> + +# define VA_LOCAL_DECL va_list ap; +# define VA_START(f) va_start(ap, f) +# define VA_END va_end(ap) + +# else + +# include <varargs.h> + +# define VA_LOCAL_DECL va_list ap; +# define VA_START(f) va_start(ap) +# define VA_END va_end(ap) + +# endif + +#ifdef HASUNAME +# include <sys/utsname.h> +# ifdef newstr +# undef newstr +# endif +#else /* ! HASUNAME */ +# define NODE_LENGTH 32 +struct utsname +{ + char nodename[NODE_LENGTH+1]; +}; +#endif /* HASUNAME */ + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif + +#if !defined(SIGCHLD) && defined(SIGCLD) +# define SIGCHLD SIGCLD +#endif + +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif + +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif + +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + +#ifdef LOCKF +#define LOCK_SH 0x01 /* shared lock */ +#define LOCK_EX 0x02 /* exclusive lock */ +#define LOCK_NB 0x04 /* non-blocking lock */ +#define LOCK_UN 0x08 /* unlock */ + +#else + +# include <sys/file.h> + +#endif + +/* +** Size of tobuf (deliver.c) +** Tweak this to match your syslog implementation. It will have to +** allow for the extra information printed. +*/ + +#ifndef TOBUFSIZE +# define TOBUFSIZE (1024 - 256) +#endif - /* - * Use query type of ANY if possible (NO_WILDCARD_MX), which will - * find types CNAME, A, and MX, and will cause all existing records - * to be cached by our local server. If there is (might be) a - * wildcard MX record in the local domain or its parents that are - * searched, we can't use ANY; it would cause fully-qualified names - * to match as names in a local domain. - */ -/* # define NO_WILDCARD_MX 1 */ +/* fork routine -- set above using #ifdef _osname_ or in Makefile */ +# ifndef FORK +# define FORK vfork /* function to call to fork mailer */ +# endif diff --git a/usr.sbin/sendmail/src/convtime.c b/usr.sbin/sendmail/src/convtime.c index 499421ba73..5cb5e49cd3 100644 --- a/usr.sbin/sendmail/src/convtime.c +++ b/usr.sbin/sendmail/src/convtime.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,7 +33,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)convtime.c 5.4 (Berkeley) 6/1/90"; +static char sccsid[] = "@(#)convtime.c 8.1 (Berkeley) 6/7/93"; #endif /* not lint */ # include <ctype.h> @@ -53,6 +53,7 @@ static char sccsid[] = "@(#)convtime.c 5.4 (Berkeley) 6/1/90"; ** ** Parameters: ** p -- pointer to ascii time. +** units -- default units if none specified. ** ** Returns: ** time in seconds. @@ -62,8 +63,9 @@ static char sccsid[] = "@(#)convtime.c 5.4 (Berkeley) 6/1/90"; */ time_t -convtime(p) +convtime(p, units) char *p; + char units; { register time_t t, r; register char c; @@ -72,10 +74,13 @@ convtime(p) while (*p != '\0') { t = 0; - while (isdigit(c = *p++)) + while ((c = *p++) != '\0' && isascii(c) && isdigit(c)) t = t * 10 + (c - '0'); if (c == '\0') + { + c = units; p--; + } switch (c) { case 'w': /* weeks */ diff --git a/usr.sbin/sendmail/src/daemon.c b/usr.sbin/sendmail/src/daemon.c index dd1b4bfda5..cfbfec6b18 100644 --- a/usr.sbin/sendmail/src/daemon.c +++ b/usr.sbin/sendmail/src/daemon.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,25 +33,27 @@ */ #include <errno.h> +#include <signal.h> #include "sendmail.h" #ifndef lint #ifdef DAEMON -static char sccsid[] = "@(#)daemon.c 5.37 (Berkeley) 3/2/91 (with daemon mode)"; +static char sccsid[] = "@(#)daemon.c 8.2 (Berkeley) 7/13/93 (with daemon mode)"; #else -static char sccsid[] = "@(#)daemon.c 5.37 (Berkeley) 3/2/91 (without daemon mode)"; +static char sccsid[] = "@(#)daemon.c 8.2 (Berkeley) 7/13/93 (without daemon mode)"; #endif #endif /* not lint */ -int la; /* load average */ - #ifdef DAEMON # include <netdb.h> -# include <sys/signal.h> # include <sys/wait.h> # include <sys/time.h> -# include <sys/resource.h> + +#ifdef NAMED_BIND +# include <arpa/nameser.h> +# include <resolv.h> +#endif /* ** DAEMON.C -- routines to use when running as a daemon. @@ -73,15 +75,14 @@ int la; /* load average */ ** etc., to avoid having extra file descriptors during ** the queue run and to avoid confusing the network ** code (if it cares). -** makeconnection(host, port, outfile, infile) +** makeconnection(host, port, outfile, infile, usesecureport) ** Make a connection to the named host on the given ** port. Set *outfile and *infile to the files ** appropriate for communication. Returns zero on ** success, else an exit status describing the ** error. -** maphostname(hbuf, hbufsize) -** Convert the entry in hbuf into a canonical form. It -** may not be larger than hbufsize. +** host_map_lookup(map, hbuf, avp, pstat) +** Convert the entry in hbuf into a canonical form. */ /* ** GETREQUESTS -- open mail IPC port and get requests. @@ -101,41 +102,51 @@ int la; /* load average */ ** to the communication channel. */ -struct sockaddr_in SendmailAddress;/* internet address of sendmail */ - -int DaemonSocket = -1; /* fd describing socket */ -char *NetName; /* name of home (local?) network */ +int DaemonSocket = -1; /* fd describing socket */ +SOCKADDR DaemonAddr; /* socket for incoming */ +int ListenQueueSize = 10; /* size of listen queue */ getrequests() { int t; register struct servent *sp; int on = 1; + bool refusingconnections = TRUE; + FILE *pidf; extern void reapchild(); /* ** Set up the address for the mailer. */ - sp = getservbyname("smtp", "tcp"); - if (sp == NULL) + if (DaemonAddr.sin.sin_family == 0) + DaemonAddr.sin.sin_family = AF_INET; + if (DaemonAddr.sin.sin_addr.s_addr == 0) + DaemonAddr.sin.sin_addr.s_addr = INADDR_ANY; + if (DaemonAddr.sin.sin_port == 0) { - syserr("server \"smtp\" unknown"); - goto severe; + sp = getservbyname("smtp", "tcp"); + if (sp == NULL) + { + syserr("554 service \"smtp\" unknown"); + goto severe; + } +#ifdef _SCO_unix_ + DaemonAddr.sin.sin_port = htons(sp->s_port); +#else + DaemonAddr.sin.sin_port = sp->s_port; +#endif } - SendmailAddress.sin_family = AF_INET; - SendmailAddress.sin_addr.s_addr = INADDR_ANY; - SendmailAddress.sin_port = sp->s_port; /* ** Try to actually open the connection. */ if (tTd(15, 1)) - printf("getrequests: port 0x%x\n", SendmailAddress.sin_port); + printf("getrequests: port 0x%x\n", DaemonAddr.sin.sin_port); /* get a socket for the SMTP connection */ - DaemonSocket = socket(AF_INET, SOCK_STREAM, 0); + DaemonSocket = socket(DaemonAddr.sa.sa_family, SOCK_STREAM, 0); if (DaemonSocket < 0) { /* probably another daemon already */ @@ -143,34 +154,55 @@ getrequests() severe: # ifdef LOG if (LogLevel > 0) - syslog(LOG_ALERT, "cannot get connection"); -# endif LOG + syslog(LOG_ALERT, "problem creating SMTP socket"); +# endif /* LOG */ finis(); } /* turn on network debugging? */ - if (tTd(15, 15)) + if (tTd(15, 101)) (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on); (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on); (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof on); - if (bind(DaemonSocket, - (struct sockaddr *)&SendmailAddress, sizeof SendmailAddress) < 0) + switch (DaemonAddr.sa.sa_family) { - syserr("getrequests: cannot bind"); - (void) close(DaemonSocket); - goto severe; +# ifdef NETINET + case AF_INET: + t = sizeof DaemonAddr.sin; + break; +# endif + +# ifdef NETISO + case AF_ISO: + t = sizeof DaemonAddr.siso; + break; +# endif + + default: + t = sizeof DaemonAddr; + break; } - if (listen(DaemonSocket, 10) < 0) + + if (bind(DaemonSocket, &DaemonAddr.sa, t) < 0) { - syserr("getrequests: cannot listen"); + syserr("getrequests: cannot bind"); (void) close(DaemonSocket); goto severe; } (void) signal(SIGCHLD, reapchild); + /* write the pid to the log file for posterity */ + pidf = fopen(PidFile, "w"); + if (pidf != NULL) + { + fprintf(pidf, "%d\n", getpid()); + fclose(pidf); + } + + if (tTd(15, 1)) printf("getrequests: %d\n", DaemonSocket); @@ -178,17 +210,38 @@ getrequests() { register int pid; auto int lotherend; - extern int RefuseLA; + extern bool refuseconnections(); /* see if we are rejecting connections */ - while ((la = getla()) > RefuseLA) + CurrentLA = getla(); + if (refuseconnections()) { - setproctitle("rejecting connections: load average: %.2f", (double)la); + if (!refusingconnections) + { + /* don't queue so peer will fail quickly */ + (void) listen(DaemonSocket, 0); + refusingconnections = TRUE; + } + setproctitle("rejecting connections: load average: %d", + CurrentLA); sleep(5); + continue; + } + + if (refusingconnections) + { + /* start listening again */ + if (listen(DaemonSocket, ListenQueueSize) < 0) + { + syserr("getrequests: cannot listen"); + (void) close(DaemonSocket); + goto severe; + } + setproctitle("accepting connections"); + refusingconnections = FALSE; } /* wait for a connection */ - setproctitle("accepting connections"); do { errno = 0; @@ -221,9 +274,7 @@ getrequests() if (pid == 0) { - extern struct hostent *gethostbyaddr(); - register struct hostent *hp; - char buf[MAXNAME]; + extern char *hostnamebyanyaddr(); /* ** CHILD -- return to caller. @@ -232,33 +283,35 @@ getrequests() */ (void) signal(SIGCHLD, SIG_DFL); + OpMode = MD_SMTP; /* determine host name */ - hp = gethostbyaddr((char *) &RealHostAddr.sin_addr, sizeof RealHostAddr.sin_addr, AF_INET); - if (hp != NULL) - (void) strcpy(buf, hp->h_name); - else - { - extern char *inet_ntoa(); + RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr)); - /* produce a dotted quad */ - (void) sprintf(buf, "[%s]", - inet_ntoa(RealHostAddr.sin_addr)); +#ifdef LOG + if (LogLevel > 10) + { + /* log connection information */ + syslog(LOG_INFO, "connect from %s (%s)", + RealHostName, anynet_ntoa(&RealHostAddr)); } - - /* should we check for illegal connection here? XXX */ - - RealHostName = newstr(buf); +#endif (void) close(DaemonSocket); InChannel = fdopen(t, "r"); OutChannel = fdopen(dup(t), "w"); + + /* should we check for illegal connection here? XXX */ +#ifdef XLA + if (!xla_host_ok(RealHostName)) + { + message("421 Too many SMTP sessions for this host"); + exit(0); + } +#endif + if (tTd(15, 2)) printf("getreq: returning\n"); -# ifdef LOG - if (LogLevel > 11) - syslog(LOG_DEBUG, "connected, pid=%d", getpid()); -# endif LOG return; } @@ -287,14 +340,162 @@ clrdaemon() DaemonSocket = -1; } /* +** SETDAEMONOPTIONS -- set options for running the daemon +** +** Parameters: +** p -- the options line. +** +** Returns: +** none. +*/ + +setdaemonoptions(p) + register char *p; +{ + if (DaemonAddr.sa.sa_family == AF_UNSPEC) + DaemonAddr.sa.sa_family = AF_INET; + + while (p != NULL) + { + register char *f; + register char *v; + + while (isascii(*p) && isspace(*p)) + p++; + if (*p == '\0') + break; + f = p; + p = strchr(p, ','); + if (p != NULL) + *p++ = '\0'; + v = strchr(f, '='); + if (v == NULL) + continue; + while (isascii(*++v) && isspace(*v)) + continue; + + switch (*f) + { + case 'F': /* address family */ + if (isascii(*v) && isdigit(*v)) + DaemonAddr.sa.sa_family = atoi(v); +#ifdef NETINET + else if (strcasecmp(v, "inet") == 0) + DaemonAddr.sa.sa_family = AF_INET; +#endif +#ifdef NETISO + else if (strcasecmp(v, "iso") == 0) + DaemonAddr.sa.sa_family = AF_ISO; +#endif +#ifdef NETNS + else if (strcasecmp(v, "ns") == 0) + DaemonAddr.sa.sa_family = AF_NS; +#endif +#ifdef NETX25 + else if (strcasecmp(v, "x.25") == 0) + DaemonAddr.sa.sa_family = AF_CCITT; +#endif + else + syserr("554 Unknown address family %s in Family=option", v); + break; + + case 'A': /* address */ + switch (DaemonAddr.sa.sa_family) + { +#ifdef NETINET + case AF_INET: + if (isascii(*v) && isdigit(*v)) + DaemonAddr.sin.sin_addr.s_addr = inet_network(v); + else + { + register struct netent *np; + + np = getnetbyname(v); + if (np == NULL) + syserr("554 network \"%s\" unknown", v); + else + DaemonAddr.sin.sin_addr.s_addr = np->n_net; + } + break; +#endif + + default: + syserr("554 Address= option unsupported for family %d", + DaemonAddr.sa.sa_family); + break; + } + break; + + case 'P': /* port */ + switch (DaemonAddr.sa.sa_family) + { + short port; + +#ifdef NETINET + case AF_INET: + if (isascii(*v) && isdigit(*v)) + DaemonAddr.sin.sin_port = atoi(v); + else + { + register struct servent *sp; + + sp = getservbyname(v, "tcp"); + if (sp == NULL) + syserr("554 service \"%s\" unknown", v); + else + { +#ifdef _SCO_unix_ + DaemonAddr.sin.sin_port = htons(sp->s_port); +#else + DaemonAddr.sin.sin_port = sp->s_port; +#endif + } + } + break; +#endif + +#ifdef NETISO + case AF_ISO: + /* assume two byte transport selector */ + if (isascii(*v) && isdigit(*v)) + port = atoi(v); + else + { + register struct servent *sp; + + sp = getservbyname(v, "tcp"); + if (sp == NULL) + syserr("554 service \"%s\" unknown", v); + else + port = sp->s_port; + } + bcopy((char *) &port, TSEL(&DaemonAddr.siso), 2); + break; +#endif + + default: + syserr("554 Port= option unsupported for family %d", + DaemonAddr.sa.sa_family); + break; + } + break; + + case 'L': /* listen queue size */ + ListenQueueSize = atoi(v); + break; + } + } +} + /* ** MAKECONNECTION -- make a connection to an SMTP socket on another machine. ** ** Parameters: ** host -- the name of the host. ** port -- the port number to connect to. -** outfile -- a pointer to a place to put the outfile -** descriptor. -** infile -- ditto for infile. +** mci -- a pointer to the mail connection information +** structure to be filled in. +** usesecureport -- if set, use a low numbered (reserved) +** port to provide some rudimentary authentication. ** ** Returns: ** An exit code telling whether the connection could be @@ -304,16 +505,20 @@ clrdaemon() ** none. */ -makeconnection(host, port, outfile, infile) +SOCKADDR CurHostAddr; /* address of current host */ + +int +makeconnection(host, port, mci, usesecureport) char *host; u_short port; - FILE **outfile; - FILE **infile; + register MCI *mci; + bool usesecureport; { register int i, s; register struct hostent *hp = (struct hostent *)NULL; - extern char *inet_ntoa(); + SOCKADDR addr; int sav_errno; + int addrlen; #ifdef NAMED_BIND extern int h_errno; #endif @@ -327,28 +532,43 @@ makeconnection(host, port, outfile, infile) h_errno = 0; #endif errno = 0; + bzero(&CurHostAddr, sizeof CurHostAddr); + CurHostName = host; if (host[0] == '[') { long hid; - register char *p = index(host, ']'); + register char *p = strchr(host, ']'); if (p != NULL) { *p = '\0'; +#ifdef NETINET hid = inet_addr(&host[1]); + if (hid == -1) +#endif + { + /* try it as a host name (avoid MX lookup) */ + hp = gethostbyname(&host[1]); + *p = ']'; + goto gothostent; + } *p = ']'; } - if (p == NULL || hid == -1) + if (p == NULL) { - usrerr("Invalid numeric domain spec \"%s\"", host); + usrerr("553 Invalid numeric domain spec \"%s\"", host); return (EX_NOHOST); } - SendmailAddress.sin_addr.s_addr = hid; +#ifdef NETINET + addr.sin.sin_family = AF_INET; /*XXX*/ + addr.sin.sin_addr.s_addr = hid; +#endif } else { hp = gethostbyname(host); +gothostent: if (hp == NULL) { #ifdef NAMED_BIND @@ -359,15 +579,25 @@ makeconnection(host, port, outfile, infile) if (errno == ECONNREFUSED && UseNameServer) return (EX_TEMPFAIL); #endif - - /* - ** XXX Should look for mail forwarder record here - ** XXX if (h_errno == NO_ADDRESS). - */ - return (EX_NOHOST); } - bcopy(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length); + addr.sa.sa_family = hp->h_addrtype; + switch (hp->h_addrtype) + { +#ifdef NETINET + case AF_INET: + bcopy(hp->h_addr, + &addr.sin.sin_addr, + hp->h_length); + break; +#endif + + default: + bcopy(hp->h_addr, + addr.sa.sa_data, + hp->h_length); + break; + } i = 1; } @@ -376,100 +606,141 @@ makeconnection(host, port, outfile, infile) */ if (port != 0) - SendmailAddress.sin_port = htons(port); + port = htons(port); else { register struct servent *sp = getservbyname("smtp", "tcp"); if (sp == NULL) { - syserr("makeconnection: server \"smtp\" unknown"); - return (EX_OSFILE); + syserr("554 makeconnection: service \"smtp\" unknown"); + return (EX_OSERR); } - SendmailAddress.sin_port = sp->s_port; +#ifdef _SCO_unix_ + port = htons(sp->s_port); +#else + port = sp->s_port; +#endif + } + + switch (addr.sa.sa_family) + { +#ifdef NETINET + case AF_INET: + addr.sin.sin_port = port; + addrlen = sizeof (struct sockaddr_in); + break; +#endif + +#ifdef NETISO + case AF_ISO: + /* assume two byte transport selector */ + bcopy((char *) &port, TSEL((struct sockaddr_iso *) &addr), 2); + addrlen = sizeof (struct sockaddr_iso); + break; +#endif + + default: + syserr("Can't connect to address family %d", addr.sa.sa_family); + return (EX_NOHOST); } /* ** Try to actually open the connection. */ -again: - if (tTd(16, 1)) - printf("makeconnection (%s [%s])\n", host, - inet_ntoa(SendmailAddress.sin_addr.s_addr)); +#ifdef XLA + /* if too many connections, don't bother trying */ + if (!xla_noqueue_ok(host)) + return EX_TEMPFAIL; +#endif - s = socket(AF_INET, SOCK_STREAM, 0); - if (s < 0) + for (;;) { - syserr("makeconnection: no socket"); - sav_errno = errno; - goto failure; - } + if (tTd(16, 1)) + printf("makeconnection (%s [%s])\n", + host, anynet_ntoa(&addr)); - if (tTd(16, 1)) - printf("makeconnection: %d\n", s); + /* save for logging */ + CurHostAddr = addr; - /* turn on network debugging? */ - if (tTd(16, 14)) - { - int on = 1; - (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on); - } - if (CurEnv->e_xfp != NULL) - (void) fflush(CurEnv->e_xfp); /* for debugging */ - errno = 0; /* for debugging */ - SendmailAddress.sin_family = AF_INET; - if (connect(s, - (struct sockaddr *)&SendmailAddress, sizeof SendmailAddress) < 0) - { + if (usesecureport) + { + int rport = IPPORT_RESERVED - 1; + + s = rresvport(&rport); + } + else + { + s = socket(AF_INET, SOCK_STREAM, 0); + } + if (s < 0) + { + sav_errno = errno; + syserr("makeconnection: no socket"); + goto failure; + } + + if (tTd(16, 1)) + printf("makeconnection: fd=%d\n", s); + + /* turn on network debugging? */ + if (tTd(16, 101)) + { + int on = 1; + (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, + (char *)&on, sizeof on); + } + if (CurEnv->e_xfp != NULL) + (void) fflush(CurEnv->e_xfp); /* for debugging */ + errno = 0; /* for debugging */ + if (connect(s, (struct sockaddr *) &addr, addrlen) >= 0) + break; + + /* couldn't connect.... figure out why */ sav_errno = errno; (void) close(s); if (hp && hp->h_addr_list[i]) { - bcopy(hp->h_addr_list[i++], - (char *)&SendmailAddress.sin_addr, hp->h_length); - goto again; + if (tTd(16, 1)) + printf("Connect failed (%s); trying new address....\n", + errstring(sav_errno)); + switch (addr.sa.sa_family) + { +#ifdef NETINET + case AF_INET: + bcopy(hp->h_addr_list[i++], + &addr.sin.sin_addr, + hp->h_length); + break; +#endif + + default: + bcopy(hp->h_addr_list[i++], + addr.sa.sa_data, + hp->h_length); + break; + } + continue; } /* failure, decide if temporary or not */ failure: - switch (sav_errno) +#ifdef XLA + xla_host_end(host); +#endif + if (transienterror(sav_errno)) + return EX_TEMPFAIL; + else { - case EISCONN: - case ETIMEDOUT: - case EINPROGRESS: - case EALREADY: - case EADDRINUSE: - case EHOSTDOWN: - case ENETDOWN: - case ENETRESET: - case ENOBUFS: - case ECONNREFUSED: - case ECONNRESET: - case EHOSTUNREACH: - case ENETUNREACH: - /* there are others, I'm sure..... */ - return (EX_TEMPFAIL); - - case EPERM: - /* why is this happening? */ - syserr("makeconnection: funny failure, addr=%lx, port=%x", - SendmailAddress.sin_addr.s_addr, SendmailAddress.sin_port); - return (EX_TEMPFAIL); - - default: - { - extern char *errstring(); - - message(Arpa_Info, "%s", errstring(sav_errno)); - return (EX_UNAVAILABLE); - } + message("%s", errstring(sav_errno)); + return (EX_UNAVAILABLE); } } /* connection ok, put it into canonical form */ - *outfile = fdopen(s, "w"); - *infile = fdopen(dup(s), "r"); + mci->mci_out = fdopen(s, "w"); + mci->mci_in = fdopen(dup(s), "r"); return (EX_OK); } @@ -484,16 +755,18 @@ again: ** A list of aliases for this host. ** ** Side Effects: -** none. +** Sets the MyIpAddrs buffer to a list of my IP addresses. */ +struct in_addr MyIpAddrs[MAXIPADDR + 1]; + char ** myhostname(hostbuf, size) char hostbuf[]; int size; { + register struct hostent *hp; extern struct hostent *gethostbyname(); - struct hostent *hp; if (gethostname(hostbuf, size) < 0) { @@ -502,61 +775,495 @@ myhostname(hostbuf, size) hp = gethostbyname(hostbuf); if (hp != NULL) { - (void) strcpy(hostbuf, hp->h_name); + (void) strncpy(hostbuf, hp->h_name, size - 1); + hostbuf[size - 1] = '\0'; + + if (hp->h_addrtype == AF_INET && hp->h_length == 4) + { + register int i; + + for (i = 0; i < MAXIPADDR; i++) + { + if (hp->h_addr_list[i] == NULL) + break; + MyIpAddrs[i].s_addr = *(u_long *) hp->h_addr_list[i]; + } + MyIpAddrs[i].s_addr = 0; + } + return (hp->h_aliases); } else return (NULL); } + /* +** GETAUTHINFO -- get the real host name asociated with a file descriptor +** +** Uses RFC1413 protocol to try to get info from the other end. +** +** Parameters: +** fd -- the descriptor +** +** Returns: +** The user@host information associated with this descriptor. +** +** Side Effects: +** Sets RealHostName to the name of the host at the other end. +*/ -/* - * MAPHOSTNAME -- turn a hostname into canonical form - * - * Parameters: - * hbuf -- a buffer containing a hostname. - * hbsize -- the size of hbuf. - * - * Returns: - * none. - * - * Side Effects: - * Looks up the host specified in hbuf. If it is not - * the canonical name for that host, replace it with - * the canonical name. If the name is unknown, or it - * is already the canonical name, leave it unchanged. - */ -maphostname(hbuf, hbsize) - char *hbuf; - int hbsize; +#ifdef IDENTPROTO + +static jmp_buf CtxAuthTimeout; + +static +authtimeout() +{ + longjmp(CtxAuthTimeout, 1); +} + +#endif + +char * +getauthinfo(fd) + int fd; +{ + SOCKADDR fa; + int falen; + register char *p; +#ifdef IDENTPROTO + SOCKADDR la; + int lalen; + register struct servent *sp; + int s; + int i; + EVENT *ev; +#endif + static char hbuf[MAXNAME * 2 + 2]; + extern char *hostnamebyanyaddr(); + extern char RealUserName[]; /* main.c */ + + falen = sizeof fa; + if (getpeername(fd, &fa.sa, &falen) < 0 || falen <= 0) + { + RealHostName = "localhost"; + (void) sprintf(hbuf, "%s@localhost", RealUserName); + if (tTd(9, 1)) + printf("getauthinfo: %s\n", hbuf); + return hbuf; + } + + RealHostName = newstr(hostnamebyanyaddr(&fa)); + RealHostAddr = fa; + +#ifdef IDENTPROTO + lalen = sizeof la; + if (fa.sa.sa_family != AF_INET || + getsockname(fd, &la.sa, &lalen) < 0 || lalen <= 0 || + la.sa.sa_family != AF_INET) + { + /* no ident info */ + goto noident; + } + + /* create ident query */ + (void) sprintf(hbuf, "%d,%d\r\n", + ntohs(fa.sin.sin_port), ntohs(la.sin.sin_port)); + + /* create local address */ + bzero(&la, sizeof la); + + /* create foreign address */ + sp = getservbyname("auth", "tcp"); + if (sp != NULL) + { +#ifdef _SCO_unix_ + fa.sin.sin_port = htons(sp->s_port); +#else + fa.sin.sin_port = sp->s_port; +#endif + } + else + fa.sin.sin_port = htons(113); + + s = -1; + if (setjmp(CtxAuthTimeout) != 0) + { + if (s >= 0) + (void) close(s); + goto noident; + } + + /* put a timeout around the whole thing */ + ev = setevent((time_t) 30, authtimeout, 0); + + /* connect to foreign IDENT server */ + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + { + clrevent(ev); + goto noident; + } + if (connect(s, &fa.sa, sizeof fa.sin) < 0) + { +closeident: + (void) close(s); + clrevent(ev); + goto noident; + } + + if (tTd(9, 10)) + printf("getauthinfo: sent %s", hbuf); + + /* send query */ + if (write(s, hbuf, strlen(hbuf)) < 0) + goto closeident; + + /* get result */ + i = read(s, hbuf, sizeof hbuf); + (void) close(s); + clrevent(ev); + if (i <= 0) + goto noident; + if (hbuf[--i] == '\n' && hbuf[--i] == '\r') + i--; + hbuf[++i] = '\0'; + + if (tTd(9, 3)) + printf("getauthinfo: got %s\n", hbuf); + + /* parse result */ + p = strchr(hbuf, ':'); + if (p == NULL) + { + /* malformed response */ + goto noident; + } + while (isascii(*++p) && isspace(*p)) + continue; + if (strncasecmp(p, "userid", 6) != 0) + { + /* presumably an error string */ + goto noident; + } + p += 6; + while (isascii(*p) && isspace(*p)) + p++; + if (*p++ != ':') + { + /* either useridxx or malformed response */ + goto noident; + } + + /* p now points to the OSTYPE field */ + p = strchr(p, ':'); + if (p == NULL) + { + /* malformed response */ + goto noident; + } + + /* 1413 says don't do this -- but it's broken otherwise */ + while (isascii(*++p) && isspace(*p)) + continue; + + /* p now points to the authenticated name */ + (void) sprintf(hbuf, "%s@%s", p, RealHostName); + goto finish; + +#endif /* IDENTPROTO */ + +noident: + (void) strcpy(hbuf, RealHostName); + +finish: + if (RealHostName[0] != '[') + { + p = &hbuf[strlen(hbuf)]; + (void) sprintf(p, " [%s]", anynet_ntoa(&RealHostAddr)); + } + if (tTd(9, 1)) + printf("getauthinfo: %s\n", hbuf); + return hbuf; +} + /* +** HOST_MAP_LOOKUP -- turn a hostname into canonical form +** +** Parameters: +** map -- a pointer to this map (unused). +** name -- the (presumably unqualified) hostname. +** av -- unused -- for compatibility with other mapping +** functions. +** statp -- an exit status (out parameter) -- set to +** EX_TEMPFAIL if the name server is unavailable. +** +** Returns: +** The mapping, if found. +** NULL if no mapping found. +** +** Side Effects: +** Looks up the host specified in hbuf. If it is not +** the canonical name for that host, return the canonical +** name. +*/ + +char * +host_map_lookup(map, name, av, statp) + MAP *map; + char *name; + char **av; + int *statp; { register struct hostent *hp; u_long in_addr; - char ptr[256], *cp; - struct hostent *gethostbyaddr(); + char *cp; + int i; + register STAB *s; + char hbuf[MAXNAME]; + extern struct hostent *gethostbyaddr(); + extern int h_errno; + + /* + ** See if we have already looked up this name. If so, just + ** return it. + */ + + s = stab(name, ST_NAMECANON, ST_ENTER); + if (bitset(NCF_VALID, s->s_namecanon.nc_flags)) + { + if (tTd(9, 1)) + printf("host_map_lookup(%s) => CACHE %s\n", + name, s->s_namecanon.nc_cname); + errno = s->s_namecanon.nc_errno; + h_errno = s->s_namecanon.nc_herrno; + *statp = s->s_namecanon.nc_stat; + return s->s_namecanon.nc_cname; + } /* - * If first character is a bracket, then it is an address - * lookup. Address is copied into a temporary buffer to - * strip the brackets and to preserve hbuf if address is - * unknown. - */ - if (*hbuf != '[') { - getcanonname(hbuf, hbsize); - return; - } - if ((cp = index(strcpy(ptr, hbuf), ']')) == NULL) - return; + ** If first character is a bracket, then it is an address + ** lookup. Address is copied into a temporary buffer to + ** strip the brackets and to preserve name if address is + ** unknown. + */ + + if (*name != '[') + { + extern bool getcanonname(); + + if (tTd(9, 1)) + printf("host_map_lookup(%s) => ", name); + s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */ + (void) strcpy(hbuf, name); + if (getcanonname(hbuf, sizeof hbuf - 1)) + { + if (tTd(9, 1)) + printf("%s\n", hbuf); + cp = map_rewrite(map, hbuf, strlen(hbuf), av); + s->s_namecanon.nc_cname = newstr(cp); + return cp; + } + else + { + register struct hostent *hp; + + if (tTd(9, 1)) + printf("FAIL (%d)\n", h_errno); + s->s_namecanon.nc_errno = errno; + s->s_namecanon.nc_herrno = h_errno; + switch (h_errno) + { + case TRY_AGAIN: + if (UseNameServer) + { + char *msg = "Recipient domain nameserver timed out"; + + message(msg); + if (CurEnv->e_message == NULL) + CurEnv->e_message = newstr(msg); + } + *statp = EX_TEMPFAIL; + break; + + case HOST_NOT_FOUND: + *statp = EX_NOHOST; + break; + + case NO_RECOVERY: + *statp = EX_SOFTWARE; + break; + + default: + *statp = EX_UNAVAILABLE; + break; + } + s->s_namecanon.nc_stat = *statp; + if (*statp != EX_TEMPFAIL || UseNameServer) + return NULL; + + /* + ** Try to look it up in /etc/hosts + */ + + hp = gethostbyname(name); + if (hp == NULL) + { + /* no dice there either */ + s->s_namecanon.nc_stat = *statp = EX_NOHOST; + return NULL; + } + + s->s_namecanon.nc_stat = *statp = EX_OK; + cp = map_rewrite(map, hp->h_name, strlen(hp->h_name), av); + s->s_namecanon.nc_cname = newstr(cp); + return cp; + } + } + if ((cp = strchr(name, ']')) == NULL) + return (NULL); *cp = '\0'; - in_addr = inet_addr(&ptr[1]); + in_addr = inet_addr(&name[1]); + + /* check to see if this is one of our addresses */ + for (i = 0; MyIpAddrs[i].s_addr != 0; i++) + { + if (MyIpAddrs[i].s_addr == in_addr) + { + return map_rewrite(map, MyHostName, strlen(MyHostName), av); + } + } + + /* nope -- ask the name server */ hp = gethostbyaddr((char *)&in_addr, sizeof(struct in_addr), AF_INET); + s->s_namecanon.nc_errno = errno; + s->s_namecanon.nc_herrno = h_errno; + s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */ if (hp == NULL) - return; - if (strlen(hp->h_name) >= hbsize) - hp->h_name[hbsize - 1] = '\0'; - (void)strcpy(hbuf, hp->h_name); + { + s->s_namecanon.nc_stat = *statp = EX_NOHOST; + return (NULL); + } + + /* found a match -- copy out */ + cp = map_rewrite(map, hp->h_name, strlen(hp->h_name), av); + s->s_namecanon.nc_stat = *statp = EX_OK; + s->s_namecanon.nc_cname = newstr(cp); + return cp; +} + /* +** ANYNET_NTOA -- convert a network address to printable form. +** +** Parameters: +** sap -- a pointer to a sockaddr structure. +** +** Returns: +** A printable version of that sockaddr. +*/ + +char * +anynet_ntoa(sap) + register SOCKADDR *sap; +{ + register char *bp; + register char *ap; + int l; + static char buf[80]; + + /* check for null/zero family */ + if (sap == NULL) + return "NULLADDR"; + if (sap->sa.sa_family == 0) + return "0"; + +#ifdef NETINET + if (sap->sa.sa_family == AF_INET) + { + extern char *inet_ntoa(); + + return inet_ntoa(((struct sockaddr_in *) sap)->sin_addr); + } +#endif + + /* unknown family -- just dump bytes */ + (void) sprintf(buf, "Family %d: ", sap->sa.sa_family); + bp = &buf[strlen(buf)]; + ap = sap->sa.sa_data; + for (l = sizeof sap->sa.sa_data; --l >= 0; ) + { + (void) sprintf(bp, "%02x:", *ap++ & 0377); + bp += 3; + } + *--bp = '\0'; + return buf; +} + /* +** HOSTNAMEBYANYADDR -- return name of host based on address +** +** Parameters: +** sap -- SOCKADDR pointer +** +** Returns: +** text representation of host name. +** +** Side Effects: +** none. +*/ + +char * +hostnamebyanyaddr(sap) + register SOCKADDR *sap; +{ + register struct hostent *hp; + +#ifdef NAMED_BIND + int saveretry; + + /* shorten name server timeout to avoid higher level timeouts */ + saveretry = _res.retry; + _res.retry = 3; +#endif /* NAMED_BIND */ + + switch (sap->sa.sa_family) + { +#ifdef NETINET + case AF_INET: + hp = gethostbyaddr((char *) &sap->sin.sin_addr, + sizeof sap->sin.sin_addr, + AF_INET); + break; +#endif + +#ifdef NETISO + case AF_ISO: + hp = gethostbyaddr((char *) &sap->siso.siso_addr, + sizeof sap->siso.siso_addr, + AF_ISO); + break; +#endif + + default: + hp = gethostbyaddr(sap->sa.sa_data, + sizeof sap->sa.sa_data, + sap->sa.sa_family); + break; + } + +#ifdef NAMED_BIND + _res.retry = saveretry; +#endif /* NAMED_BIND */ + + if (hp != NULL) + return hp->h_name; + else + { + /* produce a dotted quad */ + static char buf[512]; + + (void) sprintf(buf, "[%s]", anynet_ntoa(sap)); + return buf; + } } -# else DAEMON +# else /* DAEMON */ /* code for systems without sophisticated networking */ /* @@ -585,28 +1292,61 @@ myhostname(hostbuf, size) return (NULL); } /* +** GETAUTHINFO -- get the real host name asociated with a file descriptor +** +** Parameters: +** fd -- the descriptor +** +** Returns: +** The host name associated with this descriptor, if it can +** be determined. +** NULL otherwise. +** +** Side Effects: +** none +*/ + +char * +getauthinfo(fd) + int fd; +{ + return NULL; +} + /* ** MAPHOSTNAME -- turn a hostname into canonical form ** ** Parameters: -** hbuf -- a buffer containing a hostname. -** hbsize -- the size of hbuf. +** map -- a pointer to the database map. +** name -- a buffer containing a hostname. +** avp -- a pointer to a (cf file defined) argument vector. +** statp -- an exit status (out parameter). ** ** Returns: -** none. +** mapped host name +** FALSE otherwise. ** ** Side Effects: -** Looks up the host specified in hbuf. If it is not +** Looks up the host specified in name. If it is not ** the canonical name for that host, replace it with ** the canonical name. If the name is unknown, or it ** is already the canonical name, leave it unchanged. */ /*ARGSUSED*/ -maphostname(hbuf, hbsize) - char *hbuf; - int hbsize; +char * +host_map_lookup(map, name, avp, statp) + MAP *map; + char *name; + char **avp; + char *statp; { - return; + register struct hostent *hp; + + hp = gethostbyname(name); + if (hp != NULL) + return hp->h_name; + *statp = EX_NOHOST; + return NULL; } -#endif DAEMON +#endif /* DAEMON */ diff --git a/usr.sbin/sendmail/src/deliver.c b/usr.sbin/sendmail/src/deliver.c index c8e05f1c33..ef9fd82330 100644 --- a/usr.sbin/sendmail/src/deliver.c +++ b/usr.sbin/sendmail/src/deliver.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,22 +33,537 @@ */ #ifndef lint -static char sccsid[] = "@(#)deliver.c 5.41 (Berkeley) 3/21/91"; +static char sccsid[] = "@(#)deliver.c 8.3 (Berkeley) 7/13/93"; #endif /* not lint */ #include "sendmail.h" -#include <sys/signal.h> -#include <sys/stat.h> +#include <signal.h> #include <netdb.h> -#include <fcntl.h> #include <errno.h> #ifdef NAMED_BIND -#include <sys/param.h> #include <arpa/nameser.h> #include <resolv.h> + +extern int h_errno; #endif /* +** SENDALL -- actually send all the messages. +** +** Parameters: +** e -- the envelope to send. +** mode -- the delivery mode to use. If SM_DEFAULT, use +** the current e->e_sendmode. +** +** Returns: +** none. +** +** Side Effects: +** Scans the send lists and sends everything it finds. +** Delivers any appropriate error messages. +** If we are running in a non-interactive mode, takes the +** appropriate action. +*/ + +sendall(e, mode) + ENVELOPE *e; + char mode; +{ + register ADDRESS *q; + char *owner; + int otherowners; + register ENVELOPE *ee; + ENVELOPE *splitenv = NULL; + bool announcequeueup; + + if (bitset(EF_FATALERRS, e->e_flags)) + { + /* this will get a return message -- so don't send it */ + e->e_flags |= EF_CLRQUEUE; + return; + } + + /* determine actual delivery mode */ + if (mode == SM_DEFAULT) + { + mode = e->e_sendmode; + if (mode != SM_VERIFY && + shouldqueue(e->e_msgpriority, e->e_ctime)) + mode = SM_QUEUE; + announcequeueup = mode == SM_QUEUE; + } + else + announcequeueup = FALSE; + + if (tTd(13, 1)) + { + printf("\nSENDALL: mode %c, e_from ", mode); + printaddr(&e->e_from, FALSE); + printf("sendqueue:\n"); + printaddr(e->e_sendqueue, TRUE); + } + + /* + ** Do any preprocessing necessary for the mode we are running. + ** Check to make sure the hop count is reasonable. + ** Delete sends to the sender in mailing lists. + */ + + CurEnv = e; + + if (e->e_hopcount > MaxHopCount) + { + errno = 0; + syserr("554 too many hops %d (%d max): from %s, to %s", + e->e_hopcount, MaxHopCount, e->e_from.q_paddr, + e->e_sendqueue->q_paddr); + return; + } + + /* + ** Do sender deletion. + ** + ** If the sender has the QQUEUEUP flag set, skip this. + ** This can happen if the name server is hosed when you + ** are trying to send mail. The result is that the sender + ** is instantiated in the queue as a recipient. + */ + + if (!MeToo && !bitset(QQUEUEUP, e->e_from.q_flags)) + { + if (tTd(13, 5)) + { + printf("sendall: QDONTSEND "); + printaddr(&e->e_from, FALSE); + } + e->e_from.q_flags |= QDONTSEND; + (void) recipient(&e->e_from, &e->e_sendqueue, e); + } + + /* + ** Handle alias owners. + ** + ** We scan up the q_alias chain looking for owners. + ** We discard owners that are the same as the return path. + */ + + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + { + register struct address *a; + + for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias) + continue; + if (a != NULL) + q->q_owner = a->q_owner; + + if (q->q_owner != NULL && + !bitset(QDONTSEND, q->q_flags) && + strcmp(q->q_owner, e->e_from.q_paddr) == 0) + q->q_owner = NULL; + } + + owner = ""; + otherowners = 1; + while (owner != NULL && otherowners > 0) + { + owner = NULL; + otherowners = 0; + + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + { + if (bitset(QDONTSEND, q->q_flags)) + continue; + + if (q->q_owner != NULL) + { + if (owner == NULL) + owner = q->q_owner; + else if (owner != q->q_owner) + { + if (strcmp(owner, q->q_owner) == 0) + { + /* make future comparisons cheap */ + q->q_owner = owner; + } + else + { + otherowners++; + } + owner = q->q_owner; + } + } + else + { + otherowners++; + } + } + + if (owner != NULL && otherowners > 0) + { + extern HDR *copyheader(); + extern ADDRESS *copyqueue(); + + /* + ** Split this envelope into two. + */ + + ee = (ENVELOPE *) xalloc(sizeof(ENVELOPE)); + *ee = *e; + ee->e_id = NULL; + (void) queuename(ee, '\0'); + + if (tTd(13, 1)) + printf("sendall: split %s into %s\n", + e->e_id, ee->e_id); + + ee->e_header = copyheader(e->e_header); + ee->e_sendqueue = copyqueue(e->e_sendqueue); + ee->e_errorqueue = copyqueue(e->e_errorqueue); + ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS); + setsender(owner, ee, NULL, TRUE); + if (tTd(13, 5)) + { + printf("sendall(split): QDONTSEND "); + printaddr(&ee->e_from, FALSE); + } + ee->e_from.q_flags |= QDONTSEND; + ee->e_dfp = NULL; + ee->e_xfp = NULL; + ee->e_lockfp = NULL; + ee->e_df = NULL; + ee->e_errormode = EM_MAIL; + ee->e_sibling = splitenv; + splitenv = ee; + + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + if (q->q_owner == owner) + q->q_flags |= QDONTSEND; + for (q = ee->e_sendqueue; q != NULL; q = q->q_next) + if (q->q_owner != owner) + q->q_flags |= QDONTSEND; + + if (e->e_df != NULL && mode != SM_VERIFY) + { + ee->e_dfp = NULL; + ee->e_df = newstr(queuename(ee, 'd')); + if (link(e->e_df, ee->e_df) < 0) + { + syserr("sendall: link(%s, %s)", + e->e_df, ee->e_df); + } + } + + if (mode != SM_VERIFY) + openxscript(ee); +#ifdef LOG + if (LogLevel > 4) + syslog(LOG_INFO, "%s: clone %s", + ee->e_id, e->e_id); +#endif + } + } + + if (owner != NULL) + { + setsender(owner, e, NULL, TRUE); + if (tTd(13, 5)) + { + printf("sendall(owner): QDONTSEND "); + printaddr(&e->e_from, FALSE); + } + e->e_from.q_flags |= QDONTSEND; + e->e_errormode = EM_MAIL; + } + +# ifdef QUEUE + if ((mode == SM_QUEUE || mode == SM_FORK || + (mode != SM_VERIFY && SuperSafe)) && + !bitset(EF_INQUEUE, e->e_flags)) + { + /* be sure everything is instantiated in the queue */ + queueup(e, TRUE, announcequeueup); + for (ee = splitenv; ee != NULL; ee = ee->e_sibling) + queueup(ee, TRUE, announcequeueup); + } +#endif /* QUEUE */ + + if (splitenv != NULL) + { + if (tTd(13, 1)) + { + printf("\nsendall: Split queue; remaining queue:\n"); + printaddr(e->e_sendqueue, TRUE); + } + + for (ee = splitenv; ee != NULL; ee = ee->e_sibling) + { + CurEnv = ee; + sendenvelope(ee, mode); + } + + CurEnv = e; + } + sendenvelope(e, mode); + + for (; splitenv != NULL; splitenv = splitenv->e_sibling) + dropenvelope(splitenv); +} + +sendenvelope(e, mode) + register ENVELOPE *e; + char mode; +{ + bool oldverbose; + int pid; + register ADDRESS *q; +#ifdef LOCKF + struct flock lfd; +#endif + + oldverbose = Verbose; + switch (mode) + { + case SM_VERIFY: + Verbose = TRUE; + break; + + case SM_QUEUE: + queueonly: + e->e_flags |= EF_INQUEUE|EF_KEEPQUEUE; + return; + + case SM_FORK: + if (e->e_xfp != NULL) + (void) fflush(e->e_xfp); + +# ifdef LOCKF + /* + ** Since lockf has the interesting semantic that the + ** lock is lost when we fork, we have to risk losing + ** the lock here by closing before the fork, and then + ** trying to get it back in the child. + */ + + if (e->e_lockfp != NULL) + { + (void) xfclose(e->e_lockfp, "sendenvelope", "lockfp"); + e->e_lockfp = NULL; + } +# endif /* LOCKF */ + + pid = fork(); + if (pid < 0) + { + goto queueonly; + } + else if (pid > 0) + { + /* be sure we leave the temp files to our child */ + e->e_id = e->e_df = NULL; +# ifndef LOCKF + if (e->e_lockfp != NULL) + { + (void) xfclose(e->e_lockfp, "sendenvelope", "lockfp"); + e->e_lockfp = NULL; + } +# endif + + /* close any random open files in the envelope */ + if (e->e_dfp != NULL) + { + (void) xfclose(e->e_dfp, "sendenvelope", "dfp"); + e->e_dfp = NULL; + } + if (e->e_xfp != NULL) + { + (void) xfclose(e->e_xfp, "sendenvelope", "xfp"); + e->e_xfp = NULL; + } + return; + } + + /* double fork to avoid zombies */ + if (fork() > 0) + exit(EX_OK); + + /* be sure we are immune from the terminal */ + disconnect(FALSE, e); + +# ifdef LOCKF + /* + ** Now try to get our lock back. + */ + + lfd.l_type = F_WRLCK; + lfd.l_whence = lfd.l_start = lfd.l_len = 0; + e->e_lockfp = fopen(queuename(e, 'q'), "r+"); + if (e->e_lockfp == NULL || + fcntl(fileno(e->e_lockfp), F_SETLK, &lfd) < 0) + { + /* oops.... lost it */ + if (tTd(13, 1)) + printf("sendenvelope: %s lost lock: lockfp=%x, %s\n", + e->e_id, e->e_lockfp, errstring(errno)); + +# ifdef LOG + if (LogLevel > 29) + syslog(LOG_NOTICE, "%s: lost lock: %m", + e->e_id); +# endif /* LOG */ + exit(EX_OK); + } +# endif /* LOCKF */ + + /* + ** Close any cached connections. + ** + ** We don't send the QUIT protocol because the parent + ** still knows about the connection. + ** + ** This should only happen when delivering an error + ** message. + */ + + mci_flush(FALSE, NULL); + + break; + } + + /* + ** Run through the list and send everything. + */ + + e->e_nsent = 0; + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + { + if (mode == SM_VERIFY) + { + e->e_to = q->q_paddr; + if (!bitset(QDONTSEND|QBADADDR, q->q_flags)) + { + message("deliverable: mailer %s, host %s, user %s", + q->q_mailer->m_name, + q->q_host, + q->q_user); + } + } + else if (!bitset(QDONTSEND|QBADADDR, q->q_flags)) + { +# ifdef QUEUE + /* + ** Checkpoint the send list every few addresses + */ + + if (e->e_nsent >= CheckpointInterval) + { + queueup(e, TRUE, FALSE); + e->e_nsent = 0; + } +# endif /* QUEUE */ + (void) deliver(e, q); + } + } + Verbose = oldverbose; + + /* + ** Now run through and check for errors. + */ + + if (mode == SM_VERIFY) + { + return; + } + + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + { + if (tTd(13, 3)) + { + printf("Checking "); + printaddr(q, FALSE); + } + + /* only send errors if the message failed */ + if (!bitset(QBADADDR, q->q_flags) || + bitset(QDONTSEND, q->q_flags)) + continue; + + if (tTd(13, 3)) + printf("FATAL ERRORS\n"); + + e->e_flags |= EF_FATALERRS; + + if (q->q_owner == NULL && strcmp(e->e_from.q_paddr, "<>") != 0) + (void) sendtolist(e->e_from.q_paddr, NULL, + &e->e_errorqueue, e); + } + + if (mode == SM_FORK) + finis(); +} + /* +** DOFORK -- do a fork, retrying a couple of times on failure. +** +** This MUST be a macro, since after a vfork we are running +** two processes on the same stack!!! +** +** Parameters: +** none. +** +** Returns: +** From a macro??? You've got to be kidding! +** +** Side Effects: +** Modifies the ==> LOCAL <== variable 'pid', leaving: +** pid of child in parent, zero in child. +** -1 on unrecoverable error. +** +** Notes: +** I'm awfully sorry this looks so awful. That's +** vfork for you..... +*/ + +# define NFORKTRIES 5 + +# ifndef FORK +# define FORK fork +# endif + +# define DOFORK(fORKfN) \ +{\ + register int i;\ +\ + for (i = NFORKTRIES; --i >= 0; )\ + {\ + pid = fORKfN();\ + if (pid >= 0)\ + break;\ + if (i > 0)\ + sleep((unsigned) NFORKTRIES - i);\ + }\ +} + /* +** DOFORK -- simple fork interface to DOFORK. +** +** Parameters: +** none. +** +** Returns: +** pid of child in parent. +** zero in child. +** -1 on error. +** +** Side Effects: +** returns twice, once in parent and once in child. +*/ + +dofork() +{ + register int pid; + + DOFORK(fork); + return (pid); +} + /* ** DELIVER -- Deliver a message to a list of addresses. ** ** This routine delivers to everyone on the same host as the @@ -81,20 +596,25 @@ deliver(e, firstto) register char *p; register MAILER *m; /* mailer for this recipient */ ADDRESS *ctladdr; + register MCI *mci; register ADDRESS *to = firstto; bool clever = FALSE; /* running user smtp to this mailer */ ADDRESS *tochain = NULL; /* chain of users in this mailer call */ - int rcode; /* response code */ + int rcode; /* response code */ + char *firstsig; /* signature of firstto */ + int pid; + char *curhost; + int mpvect[2]; + int rpvect[2]; char *pv[MAXPV+1]; - char tobuf[MAXLINE-50]; /* text line of to people */ + char tobuf[TOBUFSIZE]; /* text line of to people */ char buf[MAXNAME]; - char tfrombuf[MAXNAME]; /* translated from person */ - extern bool checkcompat(); - extern ADDRESS *getctladdr(); - extern char *remotename(); + char rpathbuf[MAXNAME]; /* translated return path */ + extern int checkcompat(); + extern FILE *fdopen(); errno = 0; - if (bitset(QDONTSEND, to->q_flags)) + if (bitset(QDONTSEND|QBADADDR|QQUEUEUP, to->q_flags)) return (0); #ifdef NAMED_BIND @@ -107,6 +627,8 @@ deliver(e, firstto) m = to->q_mailer; host = to->q_host; + CurEnv = e; /* just in case */ + e->e_statmsg = NULL; if (tTd(10, 1)) printf("\n--deliver, mailer=%d, host=`%s', first user=`%s'\n", @@ -122,18 +644,19 @@ deliver(e, firstto) ** This should be on a per-mailer basis. */ - if (NoConnect && !QueueRun && bitnset(M_EXPENSIVE, m->m_flags) && - !Verbose) + if (NoConnect && !bitset(EF_QUEUERUN, e->e_flags) && + bitnset(M_EXPENSIVE, m->m_flags) && !Verbose) { for (; to != NULL; to = to->q_next) { - if (bitset(QDONTSEND, to->q_flags) || to->q_mailer != m) + if (bitset(QDONTSEND|QBADADDR|QQUEUEUP, to->q_flags) || + to->q_mailer != m) continue; to->q_flags |= QQUEUEUP|QDONTSEND; e->e_to = to->q_paddr; - message(Arpa_Info, "queued"); - if (LogLevel > 4) - logdelivery("queued"); + message("queued"); + if (LogLevel > 8) + logdelivery(m, NULL, "queued", e); } e->e_to = NULL; return (0); @@ -151,10 +674,11 @@ deliver(e, firstto) */ /* rewrite from address, using rewriting rules */ - expand("\001f", buf, &buf[sizeof buf - 1], e); - (void) strcpy(tfrombuf, remotename(buf, m, TRUE, TRUE)); - - define('g', tfrombuf, e); /* translated sender address */ + rcode = EX_OK; + (void) strcpy(rpathbuf, remotename(e->e_from.q_paddr, m, + RF_SENDERADDR|RF_CANONICAL, + &rcode, e)); + define('g', rpathbuf, e); /* translated return path */ define('h', host, e); /* to host */ Errors = 0; pvp = pv; @@ -167,8 +691,7 @@ deliver(e, firstto) *pvp++ = "-f"; else *pvp++ = "-r"; - expand("\001g", buf, &buf[sizeof buf - 1], e); - *pvp++ = newstr(buf); + *pvp++ = newstr(rpathbuf); } /* @@ -180,10 +703,17 @@ deliver(e, firstto) for (mvp = m->m_argv; (p = *++mvp) != NULL; ) { - while ((p = index(p, '\001')) != NULL) - if (*++p == 'u') - break; - if (p != NULL) + /* can't use strchr here because of sign extension problems */ + while (*p != '\0') + { + if ((*p++ & 0377) == MACROEXPAND) + { + if (*p == 'u') + break; + } + } + + if (*p != '\0') break; /* this entry is safe -- go ahead and process it */ @@ -191,7 +721,7 @@ deliver(e, firstto) *pvp++ = newstr(buf); if (pvp >= &pv[MAXPV - 3]) { - syserr("Too many parameters to %s before $u", pv[0]); + syserr("554 Too many parameters to %s before $u", pv[0]); return (-1); } } @@ -208,11 +738,11 @@ deliver(e, firstto) # ifdef SMTP clever = TRUE; *pvp = NULL; -# else SMTP +# else /* SMTP */ /* oops! we don't implement SMTP */ - syserr("SMTP style mailer"); + syserr("554 SMTP style mailer"); return (EX_SOFTWARE); -# endif SMTP +# endif /* SMTP */ } /* @@ -225,6 +755,7 @@ deliver(e, firstto) tobuf[0] = '\0'; e->e_to = tobuf; ctladdr = NULL; + firstsig = hostsignature(firstto->q_mailer, firstto->q_host, e); for (; to != NULL; to = to->q_next) { /* avoid sending multiple recipients to dumb mailers */ @@ -232,9 +763,9 @@ deliver(e, firstto) break; /* if already sent or not for this host, don't send */ - if (bitset(QDONTSEND, to->q_flags) || - strcmp(to->q_host, host) != 0 || - to->q_mailer != firstto->q_mailer) + if (bitset(QDONTSEND|QBADADDR|QQUEUEUP, to->q_flags) || + to->q_mailer != firstto->q_mailer || + strcmp(hostsignature(to->q_mailer, to->q_host, e), firstsig) != 0) continue; /* avoid overflowing tobuf */ @@ -253,6 +784,11 @@ deliver(e, firstto) user = to->q_user; e->e_to = to->q_paddr; + if (tTd(10, 5)) + { + printf("deliver: QDONTSEND "); + printaddr(to, FALSE); + } to->q_flags |= QDONTSEND; /* @@ -263,13 +799,15 @@ deliver(e, firstto) if (m->m_maxsize != 0 && e->e_msgsize > m->m_maxsize) { NoReturn = TRUE; - usrerr("Message is too large; %ld bytes max", m->m_maxsize); - giveresponse(EX_UNAVAILABLE, m, e); + usrerr("552 Message is too large; %ld bytes max", m->m_maxsize); + giveresponse(EX_UNAVAILABLE, m, NULL, e); continue; } - if (!checkcompat(to)) + rcode = checkcompat(to, e); + if (rcode != EX_OK) { - giveresponse(EX_UNAVAILABLE, m, e); + markfailure(e, to, rcode); + giveresponse(rcode, m, NULL, e); continue; } @@ -280,13 +818,8 @@ deliver(e, firstto) if (bitnset(M_STRIPQ, m->m_flags)) { - stripquotes(user, TRUE); - stripquotes(host, TRUE); - } - else - { - stripquotes(user, FALSE); - stripquotes(host, FALSE); + stripquotes(user); + stripquotes(host); } /* hack attack -- delivermail compatibility */ @@ -316,16 +849,13 @@ deliver(e, firstto) ** with the others, so we fudge on the To person. */ - if (m == LocalMailer) + if (m == FileMailer) { - if (user[0] == '/') - { - rcode = mailfile(user, getctladdr(to)); - giveresponse(rcode, m, e); - if (rcode == EX_OK) - to->q_flags |= QSENT; - continue; - } + rcode = mailfile(user, getctladdr(to), e); + giveresponse(rcode, m, NULL, e); + if (rcode == EX_OK) + to->q_flags |= QSENT; + continue; } /* @@ -378,7 +908,7 @@ deliver(e, firstto) expand(*mvp, buf, &buf[sizeof buf - 1], e); *pvp++ = newstr(buf); if (pvp >= &pv[MAXPV]) - syserr("deliver: pv overflow after $u for %s", pv[0]); + syserr("554 deliver: pv overflow after $u for %s", pv[0]); } *pvp++ = NULL; @@ -390,74 +920,489 @@ deliver(e, firstto) ** If we are running SMTP, we just need to clean up. */ - if (ctladdr == NULL) - ctladdr = &e->e_from; -#ifdef NAMED_BIND - _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ -#endif -#ifdef SMTP - if (clever) + if (ctladdr == NULL && m != ProgMailer) + ctladdr = &e->e_from; +#ifdef NAMED_BIND + if (ConfigLevel < 2) + _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ +#endif + + if (tTd(11, 1)) + { + printf("openmailer:"); + printav(pv); + } + errno = 0; + + CurHostName = m->m_mailer; + + /* + ** Deal with the special case of mail handled through an IPC + ** connection. + ** In this case we don't actually fork. We must be + ** running SMTP for this to work. We will return a + ** zero pid to indicate that we are running IPC. + ** We also handle a debug version that just talks to stdin/out. + */ + + curhost = NULL; + + /* check for Local Person Communication -- not for mortals!!! */ + if (strcmp(m->m_mailer, "[LPC]") == 0) + { + mci = (MCI *) xalloc(sizeof *mci); + bzero((char *) mci, sizeof *mci); + mci->mci_in = stdin; + mci->mci_out = stdout; + mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN; + mci->mci_mailer = m; + } + else if (strcmp(m->m_mailer, "[IPC]") == 0 || + strcmp(m->m_mailer, "[TCP]") == 0) + { +#ifdef DAEMON + register int i; + register u_short port; + + CurHostName = pv[1]; + curhost = hostsignature(m, pv[1], e); + + if (curhost == NULL || curhost[0] == '\0') + { + syserr("null signature"); + rcode = EX_OSERR; + goto give_up; + } + + if (!clever) + { + syserr("554 non-clever IPC"); + rcode = EX_OSERR; + goto give_up; + } + if (pv[2] != NULL) + port = atoi(pv[2]); + else + port = 0; +tryhost: + mci = NULL; + while (*curhost != '\0') + { + register char *p; + static char hostbuf[MAXNAME]; + + mci = NULL; + + /* pull the next host from the signature */ + p = strchr(curhost, ':'); + if (p == NULL) + p = &curhost[strlen(curhost)]; + strncpy(hostbuf, curhost, p - curhost); + hostbuf[p - curhost] = '\0'; + if (*p != '\0') + p++; + curhost = p; + + /* see if we already know that this host is fried */ + CurHostName = hostbuf; + mci = mci_get(hostbuf, m); + if (mci->mci_state != MCIS_CLOSED) + { + if (tTd(11, 1)) + { + printf("openmailer: "); + mci_dump(mci); + } + CurHostName = mci->mci_host; + break; + } + mci->mci_mailer = m; + if (mci->mci_exitstat != EX_OK) + continue; + + /* try the connection */ + setproctitle("%s %s: %s", e->e_id, hostbuf, "user open"); + message("Connecting to %s (%s)...", + hostbuf, m->m_name); + i = makeconnection(hostbuf, port, mci, + bitnset(M_SECURE_PORT, m->m_flags)); + mci->mci_exitstat = i; + mci->mci_errno = errno; +#ifdef NAMED_BIND + mci->mci_herrno = h_errno; +#endif + if (i == EX_OK) + { + mci->mci_state = MCIS_OPENING; + mci_cache(mci); + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%05d == CONNECT %s\n", + getpid(), hostbuf); + break; + } + else if (tTd(11, 1)) + printf("openmailer: makeconnection => stat=%d, errno=%d\n", + i, errno); + + + /* enter status of this host */ + setstat(i); + } + mci->mci_pid = 0; +#else /* no DAEMON */ + syserr("554 openmailer: no IPC"); + if (tTd(11, 1)) + printf("openmailer: NULL\n"); + return NULL; +#endif /* DAEMON */ + } + else + { +#ifdef XDEBUG + char wbuf[MAXLINE]; + + /* make absolutely certain 0, 1, and 2 are in use */ + sprintf(wbuf, "%s... openmailer(%s)", e->e_to, m->m_name); + checkfd012(wbuf); +#endif + + if (TrafficLogFile != NULL) + { + char **av; + + fprintf(TrafficLogFile, "%05d === EXEC", getpid()); + for (av = pv; *av != NULL; av++) + fprintf(TrafficLogFile, " %s", *av); + fprintf(TrafficLogFile, "\n"); + } + + /* create a pipe to shove the mail through */ + if (pipe(mpvect) < 0) + { + syserr("%s... openmailer(%s): pipe (to mailer)", + e->e_to, m->m_name); + if (tTd(11, 1)) + printf("openmailer: NULL\n"); + rcode = EX_OSERR; + goto give_up; + } + + /* if this mailer speaks smtp, create a return pipe */ + if (clever && pipe(rpvect) < 0) + { + syserr("%s... openmailer(%s): pipe (from mailer)", + e->e_to, m->m_name); + (void) close(mpvect[0]); + (void) close(mpvect[1]); + if (tTd(11, 1)) + printf("openmailer: NULL\n"); + rcode = EX_OSERR; + goto give_up; + } + + /* + ** Actually fork the mailer process. + ** DOFORK is clever about retrying. + ** + ** Dispose of SIGCHLD signal catchers that may be laying + ** around so that endmail will get it. + */ + + if (e->e_xfp != NULL) + (void) fflush(e->e_xfp); /* for debugging */ + (void) fflush(stdout); +# ifdef SIGCHLD + (void) signal(SIGCHLD, SIG_DFL); +# endif /* SIGCHLD */ + DOFORK(FORK); + /* pid is set by DOFORK */ + if (pid < 0) + { + /* failure */ + syserr("%s... openmailer(%s): cannot fork", + e->e_to, m->m_name); + (void) close(mpvect[0]); + (void) close(mpvect[1]); + if (clever) + { + (void) close(rpvect[0]); + (void) close(rpvect[1]); + } + if (tTd(11, 1)) + printf("openmailer: NULL\n"); + rcode = EX_OSERR; + goto give_up; + } + else if (pid == 0) + { + int i; + int saveerrno; + char **ep; + char *env[MAXUSERENVIRON]; + extern char **environ; + extern int DtableSize; + + /* child -- set up input & exec mailer */ + /* make diagnostic output be standard output */ + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGHUP, SIG_IGN); + (void) signal(SIGTERM, SIG_DFL); + + /* close any other cached connections */ + mci_flush(FALSE, mci); + + /* move into some "safe" directory */ + if (m->m_execdir != NULL) + { + char *p, *q; + char buf[MAXLINE]; + + for (p = m->m_execdir; p != NULL; p = q) + { + q = strchr(p, ':'); + if (q != NULL) + *q = '\0'; + expand(p, buf, &buf[sizeof buf] - 1, e); + if (q != NULL) + *q++ = ':'; + if (tTd(11, 20)) + printf("openmailer: trydir %s\n", + buf); + if (buf[0] != '\0' && chdir(buf) >= 0) + break; + } + } + + /* arrange to filter std & diag output of command */ + if (clever) + { + (void) close(rpvect[0]); + if (dup2(rpvect[1], STDOUT_FILENO) < 0) + { + syserr("%s... openmailer(%s): cannot dup pipe %d for stdout", + e->e_to, m->m_name, rpvect[1]); + _exit(EX_OSERR); + } + (void) close(rpvect[1]); + } + else if (OpMode == MD_SMTP || HoldErrs) + { + /* put mailer output in transcript */ + if (dup2(fileno(e->e_xfp), STDOUT_FILENO) < 0) + { + syserr("%s... openmailer(%s): cannot dup xscript %d for stdout", + e->e_to, m->m_name, + fileno(e->e_xfp)); + _exit(EX_OSERR); + } + } + if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) + { + syserr("%s... openmailer(%s): cannot dup stdout for stderr", + e->e_to, m->m_name); + _exit(EX_OSERR); + } + + /* arrange to get standard input */ + (void) close(mpvect[1]); + if (dup2(mpvect[0], STDIN_FILENO) < 0) + { + syserr("%s... openmailer(%s): cannot dup pipe %d for stdin", + e->e_to, m->m_name, mpvect[0]); + _exit(EX_OSERR); + } + (void) close(mpvect[0]); + if (!bitnset(M_RESTR, m->m_flags)) + { + if (ctladdr == NULL || ctladdr->q_uid == 0) + { + (void) setgid(DefGid); + (void) initgroups(DefUser, DefGid); + (void) setuid(DefUid); + } + else + { + (void) setgid(ctladdr->q_gid); + (void) initgroups(ctladdr->q_ruser? + ctladdr->q_ruser: ctladdr->q_user, + ctladdr->q_gid); + (void) setuid(ctladdr->q_uid); + } + } + + /* arrange for all the files to be closed */ + for (i = 3; i < DtableSize; i++) + { + register int j; + if ((j = fcntl(i, F_GETFD, 0)) != -1) + (void)fcntl(i, F_SETFD, j|1); + } + + /* set up the mailer environment */ + i = 0; + env[i++] = "AGENT=sendmail"; + for (ep = environ; *ep != NULL; ep++) + { + if (strncmp(*ep, "TZ=", 3) == 0) + env[i++] = *ep; + } + env[i++] = NULL; + + /* try to execute the mailer */ + execve(m->m_mailer, pv, env); + saveerrno = errno; + syserr("Cannot exec %s", m->m_mailer); + if (m == LocalMailer || transienterror(saveerrno)) + _exit(EX_OSERR); + _exit(EX_UNAVAILABLE); + } + + /* + ** Set up return value. + */ + + mci = (MCI *) xalloc(sizeof *mci); + bzero((char *) mci, sizeof *mci); + mci->mci_mailer = m; + mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN; + mci->mci_pid = pid; + (void) close(mpvect[0]); + mci->mci_out = fdopen(mpvect[1], "w"); + if (clever) + { + (void) close(rpvect[1]); + mci->mci_in = fdopen(rpvect[0], "r"); + } + else + { + mci->mci_flags |= MCIF_TEMP; + mci->mci_in = NULL; + } + } + + /* + ** If we are in SMTP opening state, send initial protocol. + */ + + if (clever && mci->mci_state != MCIS_CLOSED) + { + smtpinit(m, mci, e); + } + if (tTd(11, 1)) + { + printf("openmailer: "); + mci_dump(mci); + } + + if (mci->mci_state != MCIS_OPEN) { - rcode = EX_OK; + /* couldn't open the mailer */ + rcode = mci->mci_exitstat; + errno = mci->mci_errno; #ifdef NAMED_BIND - if (host[0] && host[0] != '[') + h_errno = mci->mci_herrno; +#endif + if (rcode == EX_OK) { - expand("\001w", buf, &buf[sizeof(buf) - 1], e); - Nmx = getmxrr(host, MxHosts, buf, &rcode); + /* shouldn't happen */ + syserr("554 deliver: rcode=%d, mci_state=%d, sig=%s", + rcode, mci->mci_state, firstsig); + rcode = EX_SOFTWARE; } - else -#endif + else if (rcode == EX_TEMPFAIL && *curhost != '\0') { - Nmx = 1; - MxHosts[0] = host; + /* try next MX site */ + goto tryhost; } - if (Nmx >= 0) + } + else if (!clever) + { + /* + ** Format and send message. + */ + + putfromline(mci->mci_out, m, e); + (*e->e_puthdr)(mci->mci_out, m, e); + putline("\n", mci->mci_out, m); + (*e->e_putbody)(mci->mci_out, m, e, NULL); + + /* get the exit status */ + rcode = endmailer(mci, e, pv); + } + else +#ifdef SMTP + { + /* + ** Send the MAIL FROM: protocol + */ + + rcode = smtpmailfrom(m, mci, e); + if (rcode == EX_OK) { - message(Arpa_Info, "Connecting to %s (%s)...", - MxHosts[0], m->m_name); - if ((rcode = smtpinit(m, pv)) == EX_OK) { - register char *t = tobuf; - register int i; - - /* send the recipient list */ - tobuf[0] = '\0'; - for (to = tochain; to; to = to->q_tchain) { - e->e_to = to->q_paddr; - if ((i = smtprcpt(to, m)) != EX_OK) { - markfailure(e, to, i); - giveresponse(i, m, e); - } - else { - *t++ = ','; - for (p = to->q_paddr; *p; *t++ = *p++); - } - } + register char *t = tobuf; + register int i; - /* now send the data */ - if (tobuf[0] == '\0') - e->e_to = NULL; - else { - e->e_to = tobuf + 1; - rcode = smtpdata(m, e); + /* send the recipient list */ + tobuf[0] = '\0'; + for (to = tochain; to != NULL; to = to->q_tchain) + { + e->e_to = to->q_paddr; + if ((i = smtprcpt(to, m, mci, e)) != EX_OK) + { + markfailure(e, to, i); + giveresponse(i, m, mci, e); + } + else + { + *t++ = ','; + for (p = to->q_paddr; *p; *t++ = *p++) + continue; } + } - /* now close the connection */ - smtpquit(m); + /* now send the data */ + if (tobuf[0] == '\0') + { + rcode = EX_OK; + e->e_to = NULL; + if (bitset(MCIF_CACHED, mci->mci_flags)) + smtprset(m, mci, e); } + else + { + e->e_to = tobuf + 1; + rcode = smtpdata(m, mci, e); + } + + /* now close the connection */ + if (!bitset(MCIF_CACHED, mci->mci_flags)) + smtpquit(m, mci, e); + } + if (rcode != EX_OK && *curhost != '\0') + { + /* try next MX site */ + goto tryhost; } } - else -#endif /* SMTP */ +#else /* not SMTP */ { - static int sendoff(); - - message(Arpa_Info, "Connecting to %s (%s)...", host, m->m_name); - rcode = sendoff(e, m, pv, ctladdr); + syserr("554 deliver: need SMTP compiled to use clever mailer"); + rcode = EX_CONFIG; + goto give_up; } +#endif /* SMTP */ #ifdef NAMED_BIND - _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */ + if (ConfigLevel < 2) + _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */ #endif + /* arrange a return receipt if requested */ + if (e->e_receiptto != NULL && bitnset(M_LOCALMAILER, m->m_flags)) + { + e->e_flags |= EF_SENDRECEIPT; + /* do we want to send back more info? */ + } + /* ** Do final status disposal. ** We check for something in tobuf for the SMTP case. @@ -465,13 +1410,23 @@ deliver(e, firstto) ** addressees. */ + give_up: if (tobuf[0] != '\0') - giveresponse(rcode, m, e); + giveresponse(rcode, m, mci, e); for (to = tochain; to != NULL; to = to->q_tchain) + { if (rcode != EX_OK) markfailure(e, to, rcode); else + { to->q_flags |= QSENT; + e->e_nsent++; + } + } + + /* + ** Restore state and return. + */ errno = 0; define('g', (char *) NULL, e); @@ -499,152 +1454,14 @@ markfailure(e, q, rcode) register ADDRESS *q; int rcode; { + char buf[MAXLINE]; + if (rcode == EX_OK) return; - else if (rcode != EX_TEMPFAIL && rcode != EX_IOERR && rcode != EX_OSERR) - q->q_flags |= QBADADDR; - else if (curtime() > e->e_ctime + TimeOut) - { - extern char *pintvl(); - char buf[MAXLINE]; - - if (!bitset(EF_TIMEOUT, e->e_flags)) - { - (void) sprintf(buf, "Cannot send message for %s", - pintvl(TimeOut, FALSE)); - if (e->e_message != NULL) - free(e->e_message); - e->e_message = newstr(buf); - message(Arpa_Info, buf); - } - q->q_flags |= QBADADDR; - e->e_flags |= EF_TIMEOUT; - } - else + else if (rcode == EX_TEMPFAIL) q->q_flags |= QQUEUEUP; -} - /* -** DOFORK -- do a fork, retrying a couple of times on failure. -** -** This MUST be a macro, since after a vfork we are running -** two processes on the same stack!!! -** -** Parameters: -** none. -** -** Returns: -** From a macro??? You've got to be kidding! -** -** Side Effects: -** Modifies the ==> LOCAL <== variable 'pid', leaving: -** pid of child in parent, zero in child. -** -1 on unrecoverable error. -** -** Notes: -** I'm awfully sorry this looks so awful. That's -** vfork for you..... -*/ - -# define NFORKTRIES 5 -# ifdef VMUNIX -# define XFORK vfork -# else VMUNIX -# define XFORK fork -# endif VMUNIX - -# define DOFORK(fORKfN) \ -{\ - register int i;\ -\ - for (i = NFORKTRIES; --i >= 0; )\ - {\ - pid = fORKfN();\ - if (pid >= 0)\ - break;\ - if (i > 0)\ - sleep((unsigned) NFORKTRIES - i);\ - }\ -} - /* -** DOFORK -- simple fork interface to DOFORK. -** -** Parameters: -** none. -** -** Returns: -** pid of child in parent. -** zero in child. -** -1 on error. -** -** Side Effects: -** returns twice, once in parent and once in child. -*/ - -dofork() -{ - register int pid; - - DOFORK(fork); - return (pid); -} - /* -** SENDOFF -- send off call to mailer & collect response. -** -** Parameters: -** e -- the envelope to mail. -** m -- mailer descriptor. -** pvp -- parameter vector to send to it. -** ctladdr -- an address pointer controlling the -** user/groupid etc. of the mailer. -** -** Returns: -** exit status of mailer. -** -** Side Effects: -** none. -*/ -static -sendoff(e, m, pvp, ctladdr) - register ENVELOPE *e; - MAILER *m; - char **pvp; - ADDRESS *ctladdr; -{ - auto FILE *mfile; - auto FILE *rfile; - register int i; - int pid; - - /* - ** Create connection to mailer. - */ - - pid = openmailer(m, pvp, ctladdr, FALSE, &mfile, &rfile); - if (pid < 0) - return (-1); - - /* - ** Format and send message. - */ - - putfromline(mfile, m); - (*e->e_puthdr)(mfile, m, e); - putline("\n", mfile, m); - (*e->e_putbody)(mfile, m, e); - (void) fclose(mfile); - if (rfile != NULL) - (void) fclose(rfile); - - i = endmailer(pid, pvp[0]); - - /* arrange a return receipt if requested */ - if (e->e_receiptto != NULL && bitnset(M_LOCAL, m->m_flags)) - { - e->e_flags |= EF_SENDRECEIPT; - /* do we want to send back more info? */ - } - - return (i); + else if (rcode != EX_IOERR && rcode != EX_OSERR) + q->q_flags |= QBADADDR; } /* ** ENDMAILER -- Wait for mailer to terminate. @@ -656,7 +1473,9 @@ sendoff(e, m, pvp, ctladdr) ** ** Parameters: ** pid -- pid of mailer. -** name -- name of mailer (for error messages). +** e -- the current envelope. +** pv -- the parameter vector that invoked the mailer +** (for error messages). ** ** Returns: ** exit code of mailer. @@ -665,295 +1484,56 @@ sendoff(e, m, pvp, ctladdr) ** none. */ -endmailer(pid, name) - int pid; - char *name; +endmailer(mci, e, pv) + register MCI *mci; + register ENVELOPE *e; + char **pv; { int st; + /* close any connections */ + if (mci->mci_in != NULL) + (void) xfclose(mci->mci_in, pv[0], "mci_in"); + if (mci->mci_out != NULL) + (void) xfclose(mci->mci_out, pv[0], "mci_out"); + mci->mci_in = mci->mci_out = NULL; + mci->mci_state = MCIS_CLOSED; + /* in the IPC case there is nothing to wait for */ - if (pid == 0) + if (mci->mci_pid == 0) return (EX_OK); /* wait for the mailer process to die and collect status */ - st = waitfor(pid); + st = waitfor(mci->mci_pid); if (st == -1) { - syserr("endmailer %s: wait", name); - return (EX_SOFTWARE); - } - - /* see if it died a horrid death */ - if ((st & 0377) != 0) - { - syserr("mailer %s died with signal %o", name, st); - ExitStat = EX_TEMPFAIL; - return (EX_TEMPFAIL); - } - - /* normal death -- return status */ - st = (st >> 8) & 0377; - return (st); -} - /* -** OPENMAILER -- open connection to mailer. -** -** Parameters: -** m -- mailer descriptor. -** pvp -- parameter vector to pass to mailer. -** ctladdr -- controlling address for user. -** clever -- create a full duplex connection. -** pmfile -- pointer to mfile (to mailer) connection. -** prfile -- pointer to rfile (from mailer) connection. -** -** Returns: -** pid of mailer ( > 0 ). -** -1 on error. -** zero on an IPC connection. -** -** Side Effects: -** creates a mailer in a subprocess. -*/ - -openmailer(m, pvp, ctladdr, clever, pmfile, prfile) - MAILER *m; - char **pvp; - ADDRESS *ctladdr; - bool clever; - FILE **pmfile; - FILE **prfile; -{ - int pid; - int mpvect[2]; - int rpvect[2]; - FILE *mfile = NULL; - FILE *rfile = NULL; - extern FILE *fdopen(); - - if (tTd(11, 1)) - { - printf("openmailer:"); - printav(pvp); - } - errno = 0; - - CurHostName = m->m_mailer; - - /* - ** Deal with the special case of mail handled through an IPC - ** connection. - ** In this case we don't actually fork. We must be - ** running SMTP for this to work. We will return a - ** zero pid to indicate that we are running IPC. - ** We also handle a debug version that just talks to stdin/out. - */ - - /* check for Local Person Communication -- not for mortals!!! */ - if (strcmp(m->m_mailer, "[LPC]") == 0) - { - *pmfile = stdout; - *prfile = stdin; - return (0); - } - - if (strcmp(m->m_mailer, "[IPC]") == 0) - { -#ifdef HOSTINFO - register STAB *st; - extern STAB *stab(); -#endif HOSTINFO -#ifdef DAEMON - register int i, j; - register u_short port; - - CurHostName = pvp[1]; - if (!clever) - syserr("non-clever IPC"); - if (pvp[2] != NULL) - port = atoi(pvp[2]); - else - port = 0; - for (j = 0; j < Nmx; j++) - { - CurHostName = MxHosts[j]; -#ifdef HOSTINFO - /* see if we have already determined that this host is fried */ - st = stab(MxHosts[j], ST_HOST, ST_FIND); - if (st == NULL || st->s_host.ho_exitstat == EX_OK) { - if (j > 1) - message(Arpa_Info, - "Connecting to %s (%s)...", - MxHosts[j], m->m_name); - i = makeconnection(MxHosts[j], port, pmfile, prfile); - } - else - { - i = st->s_host.ho_exitstat; - errno = st->s_host.ho_errno; - } -#else HOSTINFO - i = makeconnection(MxHosts[j], port, pmfile, prfile); -#endif HOSTINFO - if (i != EX_OK) - { -#ifdef HOSTINFO - /* enter status of this host */ - if (st == NULL) - st = stab(MxHosts[j], ST_HOST, ST_ENTER); - st->s_host.ho_exitstat = i; - st->s_host.ho_errno = errno; -#endif HOSTINFO - ExitStat = i; - continue; - } - else - return (0); - } - return (-1); -#else DAEMON - syserr("openmailer: no IPC"); - return (-1); -#endif DAEMON - } - - /* create a pipe to shove the mail through */ - if (pipe(mpvect) < 0) - { - syserr("openmailer: pipe (to mailer)"); - return (-1); - } - -#ifdef SMTP - /* if this mailer speaks smtp, create a return pipe */ - if (clever && pipe(rpvect) < 0) - { - syserr("openmailer: pipe (from mailer)"); - (void) close(mpvect[0]); - (void) close(mpvect[1]); - return (-1); - } -#endif SMTP - - /* - ** Actually fork the mailer process. - ** DOFORK is clever about retrying. - ** - ** Dispose of SIGCHLD signal catchers that may be laying - ** around so that endmail will get it. - */ - - if (CurEnv->e_xfp != NULL) - (void) fflush(CurEnv->e_xfp); /* for debugging */ - (void) fflush(stdout); -# ifdef SIGCHLD - (void) signal(SIGCHLD, SIG_DFL); -# endif SIGCHLD - DOFORK(XFORK); - /* pid is set by DOFORK */ - if (pid < 0) - { - /* failure */ - syserr("openmailer: cannot fork"); - (void) close(mpvect[0]); - (void) close(mpvect[1]); -#ifdef SMTP - if (clever) - { - (void) close(rpvect[0]); - (void) close(rpvect[1]); - } -#endif SMTP - return (-1); + syserr("endmailer %s: wait", pv[0]); + return (EX_SOFTWARE); } - else if (pid == 0) - { - int i; - extern int DtableSize; - - /* child -- set up input & exec mailer */ - /* make diagnostic output be standard output */ - (void) signal(SIGINT, SIG_IGN); - (void) signal(SIGHUP, SIG_IGN); - (void) signal(SIGTERM, SIG_DFL); - /* arrange to filter standard & diag output of command */ - if (clever) - { - (void) close(rpvect[0]); - (void) close(1); - (void) dup(rpvect[1]); - (void) close(rpvect[1]); - } - else if (OpMode == MD_SMTP || HoldErrs) - { - /* put mailer output in transcript */ - (void) close(1); - (void) dup(fileno(CurEnv->e_xfp)); - } - (void) close(2); - (void) dup(1); + /* see if it died a horrid death */ + if ((st & 0377) != 0) + { + syserr("mailer %s died with signal %o", pv[0], st); - /* arrange to get standard input */ - (void) close(mpvect[1]); - (void) close(0); - if (dup(mpvect[0]) < 0) - { - syserr("Cannot dup to zero!"); - _exit(EX_OSERR); - } - (void) close(mpvect[0]); - if (!bitnset(M_RESTR, m->m_flags)) + /* log the arguments */ + if (e->e_xfp != NULL) { - if (ctladdr == NULL || ctladdr->q_uid == 0) - { - (void) setgid(DefGid); - (void) initgroups(DefUser, DefGid); - (void) setuid(DefUid); - } - else - { - (void) setgid(ctladdr->q_gid); - (void) initgroups(ctladdr->q_ruser? - ctladdr->q_ruser: ctladdr->q_user, - ctladdr->q_gid); - (void) setuid(ctladdr->q_uid); - } - } + register char **av; - /* arrange for all the files to be closed */ - for (i = 3; i < DtableSize; i++) { - register int j; - if ((j = fcntl(i, F_GETFD, 0)) != -1) - (void)fcntl(i, F_SETFD, j|1); + fprintf(e->e_xfp, "Arguments:"); + for (av = pv; *av != NULL; av++) + fprintf(e->e_xfp, " %s", *av); + fprintf(e->e_xfp, "\n"); } - /* try to execute the mailer */ - execve(m->m_mailer, pvp, UserEnviron); - syserr("Cannot exec %s", m->m_mailer); - if (m == LocalMailer || errno == EIO || errno == EAGAIN || - errno == ENOMEM || errno == EPROCLIM) - _exit(EX_TEMPFAIL); - else - _exit(EX_UNAVAILABLE); + ExitStat = EX_TEMPFAIL; + return (EX_TEMPFAIL); } - /* - ** Set up return value. - */ - - (void) close(mpvect[0]); - mfile = fdopen(mpvect[1], "w"); - if (clever) - { - (void) close(rpvect[1]); - rfile = fdopen(rpvect[0], "r"); - } else - rfile = NULL; - - *pmfile = mfile; - *prfile = rfile; - - return (pid); + /* normal death -- return status */ + st = (st >> 8) & 0377; + return (st); } /* ** GIVERESPONSE -- Interpret an error response from a mailer @@ -962,7 +1542,10 @@ openmailer(m, pvp, ctladdr, clever, pmfile, prfile) ** stat -- the status code from the mailer (high byte ** only; core dumps must have been taken care of ** already). -** m -- the mailer descriptor for this mailer. +** m -- the mailer info for this mailer. +** mci -- the mailer connection info -- can be NULL if the +** response is given before the connection is made. +** e -- the current envelope. ** ** Returns: ** none. @@ -972,32 +1555,32 @@ openmailer(m, pvp, ctladdr, clever, pmfile, prfile) ** ExitStat may be set. */ -giveresponse(stat, m, e) +giveresponse(stat, m, mci, e) int stat; register MAILER *m; + register MCI *mci; ENVELOPE *e; { - register char *statmsg; + register const char *statmsg; extern char *SysExMsg[]; register int i; extern int N_SysEx; -#ifdef NAMED_BIND - extern int h_errno; -#endif char buf[MAXLINE]; -#ifdef lint - if (m == NULL) - return; -#endif lint - /* ** Compute status message from code. */ i = stat - EX__BASE; if (stat == 0) + { statmsg = "250 Sent"; + if (e->e_statmsg != NULL) + { + (void) sprintf(buf, "%s (%s)", statmsg, e->e_statmsg); + statmsg = buf; + } + } else if (i < 0 || i > N_SysEx) { (void) sprintf(buf, "554 unknown mailer error %d", stat); @@ -1006,32 +1589,24 @@ giveresponse(stat, m, e) } else if (stat == EX_TEMPFAIL) { - (void) strcpy(buf, SysExMsg[i]); + (void) strcpy(buf, SysExMsg[i] + 1); #ifdef NAMED_BIND if (h_errno == TRY_AGAIN) - { - extern char *errstring(); - statmsg = errstring(h_errno+MAX_ERRNO); - } else #endif { if (errno != 0) - { - extern char *errstring(); - statmsg = errstring(errno); - } else { #ifdef SMTP extern char SmtpError[]; statmsg = SmtpError; -#else SMTP +#else /* SMTP */ statmsg = NULL; -#endif SMTP +#endif /* SMTP */ } } if (statmsg != NULL && statmsg[0] != '\0') @@ -1041,9 +1616,22 @@ giveresponse(stat, m, e) } statmsg = buf; } +#ifdef NAMED_BIND + else if (stat == EX_NOHOST && h_errno != 0) + { + statmsg = errstring(h_errno + MAX_ERRNO); + (void) sprintf(buf, "%s (%s)", SysExMsg[i], statmsg); + statmsg = buf; + } +#endif else { statmsg = SysExMsg[i]; + if (*statmsg++ == ':') + { + (void) sprintf(buf, "%s: %s", statmsg, errstring(errno)); + statmsg = buf; + } } /* @@ -1051,11 +1639,11 @@ giveresponse(stat, m, e) */ if (stat == EX_OK || stat == EX_TEMPFAIL) - message(Arpa_Info, &statmsg[4]); + message(&statmsg[4], errstring(errno)); else { Errors++; - usrerr(statmsg); + usrerr(statmsg, errstring(errno)); } /* @@ -1065,8 +1653,8 @@ giveresponse(stat, m, e) ** that. */ - if (LogLevel > ((stat == 0 || stat == EX_TEMPFAIL) ? 3 : 2)) - logdelivery(&statmsg[4]); + if (LogLevel > ((stat == EX_TEMPFAIL) ? 8 : (stat == EX_OK) ? 7 : 6)) + logdelivery(m, mci, &statmsg[4], e); if (stat != EX_TEMPFAIL) setstat(stat); @@ -1085,7 +1673,11 @@ giveresponse(stat, m, e) ** LOGDELIVERY -- log the delivery in the system log ** ** Parameters: -** stat -- the message to print for the status +** m -- the mailer info. Can be NULL for initial queue. +** mci -- the mailer connection info -- can be NULL if the +** log is occuring when no connection is active. +** stat -- the message to print for the status. +** e -- the current envelope. ** ** Returns: ** none @@ -1094,15 +1686,52 @@ giveresponse(stat, m, e) ** none */ -logdelivery(stat) +logdelivery(m, mci, stat, e) + MAILER *m; + register MCI *mci; char *stat; + register ENVELOPE *e; { - extern char *pintvl(); - # ifdef LOG - syslog(LOG_INFO, "%s: to=%s, delay=%s, stat=%s", CurEnv->e_id, - CurEnv->e_to, pintvl(curtime() - CurEnv->e_ctime, TRUE), stat); -# endif LOG + char buf[512]; + + (void) sprintf(buf, "delay=%s", pintvl(curtime() - e->e_ctime, TRUE)); + + if (m != NULL) + { + (void) strcat(buf, ", mailer="); + (void) strcat(buf, m->m_name); + } + + if (mci != NULL && mci->mci_host != NULL) + { +# ifdef DAEMON + extern SOCKADDR CurHostAddr; +# endif + + (void) strcat(buf, ", relay="); + (void) strcat(buf, mci->mci_host); + +# ifdef DAEMON + (void) strcat(buf, " ("); + (void) strcat(buf, anynet_ntoa(&CurHostAddr)); + (void) strcat(buf, ")"); +# endif + } + else + { + char *p = macvalue('h', e); + + if (p != NULL && p[0] != '\0') + { + (void) strcat(buf, ", relay="); + (void) strcat(buf, p); + } + } + + syslog(LOG_INFO, "%s: to=%s, %s, stat=%s", + e->e_id, e->e_to, buf, stat); +# endif /* LOG */ } /* ** PUTFROMLINE -- output a UNIX-style from line (or whatever) @@ -1125,11 +1754,12 @@ logdelivery(stat) ** outputs some text to fp. */ -putfromline(fp, m) +putfromline(fp, m, e) register FILE *fp; register MAILER *m; + ENVELOPE *e; { - char *template = "\001l\n"; + char *template = "\201l\n"; char buf[MAXLINE]; if (bitnset(M_NHDR, m->m_flags)) @@ -1141,19 +1771,19 @@ putfromline(fp, m) char *bang; char xbuf[MAXLINE]; - expand("\001g", buf, &buf[sizeof buf - 1], CurEnv); - bang = index(buf, '!'); + expand("\201g", buf, &buf[sizeof buf - 1], e); + bang = strchr(buf, '!'); if (bang == NULL) - syserr("No ! in UUCP! (%s)", buf); + syserr("554 No ! in UUCP! (%s)", buf); else { *bang++ = '\0'; - (void) sprintf(xbuf, "From %s \001d remote from %s\n", bang, buf); + (void) sprintf(xbuf, "From %s \201d remote from %s\n", bang, buf); template = xbuf; } } -# endif UGLYUUCP - expand(template, buf, &buf[sizeof buf - 1], CurEnv); +# endif /* UGLYUUCP */ + expand(template, buf, &buf[sizeof buf - 1], e); putline(buf, fp, m); } /* @@ -1163,6 +1793,8 @@ putfromline(fp, m) ** fp -- file to output onto. ** m -- a mailer descriptor to control output format. ** e -- the envelope to put out. +** separator -- if non-NULL, a message separator that must +** not be permitted in the resulting message. ** ** Returns: ** none. @@ -1171,10 +1803,11 @@ putfromline(fp, m) ** The message is written onto fp. */ -putbody(fp, m, e) +putbody(fp, m, e, separator) FILE *fp; MAILER *m; register ENVELOPE *e; + char *separator; { char buf[MAXLINE]; @@ -1202,6 +1835,14 @@ putbody(fp, m, e) if (buf[0] == 'F' && bitnset(M_ESCFROM, m->m_flags) && strncmp(buf, "From ", 5) == 0) (void) putc('>', fp); + if (buf[0] == '-' && buf[1] == '-' && separator != NULL) + { + /* possible separator */ + int sl = strlen(separator); + + if (strncmp(&buf[2], separator, sl) == 0) + (void) putc(' ', fp); + } putline(buf, fp, m); } @@ -1212,6 +1853,10 @@ putbody(fp, m, e) } } + /* some mailers want extra blank line at end of message */ + if (bitnset(M_BLANKEND, m->m_flags) && buf[0] != '\0' && buf[0] != '\n') + putline("", fp, m); + (void) fflush(fp); if (ferror(fp) && errno != EPIPE) { @@ -1246,13 +1891,23 @@ putbody(fp, m, e) ** none. */ -mailfile(filename, ctladdr) +mailfile(filename, ctladdr, e) char *filename; ADDRESS *ctladdr; + register ENVELOPE *e; { register FILE *f; register int pid; - ENVELOPE *e = CurEnv; + int mode; + + if (tTd(11, 1)) + { + printf("mailfile %s\n ctladdr=", filename); + printaddr(ctladdr, FALSE); + } + + if (e->e_xfp != NULL) + fflush(e->e_xfp); /* ** Fork so we can change permissions here. @@ -1273,59 +1928,83 @@ mailfile(filename, ctladdr) (void) signal(SIGHUP, SIG_DFL); (void) signal(SIGTERM, SIG_DFL); (void) umask(OldUmask); + if (stat(filename, &stb) < 0) - { - errno = 0; - stb.st_mode = 0666; - } + stb.st_mode = FileMode; + mode = stb.st_mode; + + /* limit the errors to those actually caused in the child */ + errno = 0; + ExitStat = EX_OK; + if (bitset(0111, stb.st_mode)) exit(EX_CANTCREAT); if (ctladdr == NULL) ctladdr = &e->e_from; + else + { + /* ignore setuid and setgid bits */ + mode &= ~(S_ISGID|S_ISUID); + } + /* we have to open the dfile BEFORE setuid */ - if (e->e_dfp == NULL && e->e_df != NULL) + if (e->e_dfp == NULL && e->e_df != NULL) { e->e_dfp = fopen(e->e_df, "r"); - if (e->e_dfp == NULL) { + if (e->e_dfp == NULL) + { syserr("mailfile: Cannot open %s for %s from %s", - e->e_df, e->e_to, e->e_from); + e->e_df, e->e_to, e->e_from); } } - if (!bitset(S_ISGID, stb.st_mode) || setgid(stb.st_gid) < 0) + if (!bitset(S_ISGID, mode) || setgid(stb.st_gid) < 0) { - if (ctladdr->q_uid == 0) { + if (ctladdr->q_uid == 0) + { (void) setgid(DefGid); (void) initgroups(DefUser, DefGid); - } else { + } + else + { (void) setgid(ctladdr->q_gid); - (void) initgroups(ctladdr->q_ruser? - ctladdr->q_ruser: ctladdr->q_user, + (void) initgroups(ctladdr->q_ruser ? + ctladdr->q_ruser : ctladdr->q_user, ctladdr->q_gid); } } - if (!bitset(S_ISUID, stb.st_mode) || setuid(stb.st_uid) < 0) + if (!bitset(S_ISUID, mode) || setuid(stb.st_uid) < 0) { if (ctladdr->q_uid == 0) (void) setuid(DefUid); else (void) setuid(ctladdr->q_uid); } - f = dfopen(filename, "a"); + FileName = filename; + LineNumber = 0; + f = dfopen(filename, O_WRONLY|O_CREAT|O_APPEND, FileMode); if (f == NULL) + { + message("554 cannot open"); exit(EX_CANTCREAT); + } - putfromline(f, ProgMailer); - (*CurEnv->e_puthdr)(f, ProgMailer, CurEnv); - putline("\n", f, ProgMailer); - (*CurEnv->e_putbody)(f, ProgMailer, CurEnv); - putline("\n", f, ProgMailer); - (void) fclose(f); + putfromline(f, FileMailer, e); + (*e->e_puthdr)(f, FileMailer, e); + putline("\n", f, FileMailer); + (*e->e_putbody)(f, FileMailer, e, NULL); + putline("\n", f, FileMailer); + if (ferror(f)) + { + message("451 I/O error"); + setstat(EX_IOERR); + } + (void) xfclose(f, "mailfile", filename); (void) fflush(stdout); /* reset ISUID & ISGID bits for paranoid systems */ (void) chmod(filename, (int) stb.st_mode); - exit(EX_OK); + exit(ExitStat); /*NOTREACHED*/ } else @@ -1342,212 +2021,142 @@ mailfile(filename, ctladdr) } } /* -** SENDALL -- actually send all the messages. +** HOSTSIGNATURE -- return the "signature" for a host. +** +** The signature describes how we are going to send this -- it +** can be just the hostname (for non-Internet hosts) or can be +** an ordered list of MX hosts. ** ** Parameters: -** e -- the envelope to send. -** mode -- the delivery mode to use. If SM_DEFAULT, use -** the current SendMode. +** m -- the mailer describing this host. +** host -- the host name. +** e -- the current envelope. ** ** Returns: -** none. +** The signature for this host. ** ** Side Effects: -** Scans the send lists and sends everything it finds. -** Delivers any appropriate error messages. -** If we are running in a non-interactive mode, takes the -** appropriate action. +** Can tweak the symbol table. */ -sendall(e, mode) +char * +hostsignature(m, host, e) + register MAILER *m; + char *host; ENVELOPE *e; - char mode; { - register ADDRESS *q; - bool oldverbose; - int pid; - int nsent; - FILE *lockfp = NULL, *queueup(); - - /* determine actual delivery mode */ - if (mode == SM_DEFAULT) - { - extern bool shouldqueue(); - - if (shouldqueue(e->e_msgpriority)) - mode = SM_QUEUE; - else - mode = SendMode; - } - - if (tTd(13, 1)) - { - printf("\nSENDALL: mode %c, sendqueue:\n", mode); - printaddr(e->e_sendqueue, TRUE); - } + register char *p; + register STAB *s; + int i; + int len; +#ifdef NAMED_BIND + int nmx; + auto int rcode; + char *hp; + char *endp; + int oldoptions; + char *mxhosts[MAXMXHOSTS + 1]; +#endif /* - ** Do any preprocessing necessary for the mode we are running. - ** Check to make sure the hop count is reasonable. - ** Delete sends to the sender in mailing lists. + ** Check to see if this uses IPC -- if not, it can't have MX records. */ - CurEnv = e; - - if (e->e_hopcount > MAXHOP) - { - errno = 0; - syserr("sendall: too many hops %d (%d max): from %s, to %s", - e->e_hopcount, MAXHOP, e->e_from, e->e_to); - return; - } - - if (!MeToo) + p = m->m_mailer; + if (strcmp(p, "[IPC]") != 0 && strcmp(p, "[TCP]") != 0) { - extern ADDRESS *recipient(); - - e->e_from.q_flags |= QDONTSEND; - (void) recipient(&e->e_from, &e->e_sendqueue); + /* just an ordinary mailer */ + return host; } -# ifdef QUEUE - if ((mode == SM_QUEUE || mode == SM_FORK || - (mode != SM_VERIFY && SuperSafe)) && - !bitset(EF_INQUEUE, e->e_flags)) - lockfp = queueup(e, TRUE, mode == SM_QUEUE); -#endif QUEUE - - oldverbose = Verbose; - switch (mode) - { - case SM_VERIFY: - Verbose = TRUE; - break; - - case SM_QUEUE: - e->e_flags |= EF_INQUEUE|EF_KEEPQUEUE; - return; - - case SM_FORK: - if (e->e_xfp != NULL) - (void) fflush(e->e_xfp); - pid = fork(); - if (pid < 0) - { - mode = SM_DELIVER; - break; - } - else if (pid > 0) - { - /* be sure we leave the temp files to our child */ - e->e_id = e->e_df = NULL; - if (lockfp != NULL) - (void) fclose(lockfp); - return; - } - - /* double fork to avoid zombies */ - if (fork() > 0) - exit(EX_OK); - - /* be sure we are immune from the terminal */ - disconnect(FALSE); + /* + ** If it is a numeric address, just return it. + */ - break; - } + if (host[0] == '[') + return host; /* - ** Run through the list and send everything. + ** Look it up in the symbol table. */ - nsent = 0; - for (q = e->e_sendqueue; q != NULL; q = q->q_next) - { - if (mode == SM_VERIFY) - { - e->e_to = q->q_paddr; - if (!bitset(QDONTSEND|QBADADDR, q->q_flags)) - message(Arpa_Info, "deliverable"); - } - else if (!bitset(QDONTSEND, q->q_flags)) - { - /* - ** Checkpoint the send list every few addresses - */ - - if (nsent >= CheckpointInterval) - { - queueup(e, TRUE, FALSE); - nsent = 0; - } - if (deliver(e, q) == EX_OK) - nsent++; - } - } - Verbose = oldverbose; + s = stab(host, ST_HOSTSIG, ST_ENTER); + if (s->s_hostsig != NULL) + return s->s_hostsig; /* - ** Now run through and check for errors. + ** Not already there -- create a signature. */ - if (mode == SM_VERIFY) { - if (lockfp != NULL) - (void) fclose(lockfp); - return; +#ifdef NAMED_BIND + if (ConfigLevel < 2) + { + oldoptions = _res.options; + _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ } - for (q = e->e_sendqueue; q != NULL; q = q->q_next) + for (hp = host; hp != NULL; hp = endp) { - register ADDRESS *qq; - - if (tTd(13, 3)) - { - printf("Checking "); - printaddr(q, FALSE); - } + endp = strchr(hp, ':'); + if (endp != NULL) + *endp = '\0'; - /* only send errors if the message failed */ - if (!bitset(QBADADDR, q->q_flags)) - continue; + nmx = getmxrr(hp, mxhosts, TRUE, &rcode); - /* we have an address that failed -- find the parent */ - for (qq = q; qq != NULL; qq = qq->q_alias) + if (nmx <= 0) { - char obuf[MAXNAME + 6]; - extern char *aliaslookup(); - - /* we can only have owners for local addresses */ - if (!bitnset(M_LOCAL, qq->q_mailer->m_flags)) - continue; - - /* see if the owner list exists */ - (void) strcpy(obuf, "owner-"); - if (strncmp(qq->q_user, "owner-", 6) == 0) - (void) strcat(obuf, "owner"); - else - (void) strcat(obuf, qq->q_user); - makelower(obuf); - if (aliaslookup(obuf) == NULL) - continue; + register MCI *mci; + extern int errno; - if (tTd(13, 4)) - printf("Errors to %s\n", obuf); + /* update the connection info for this host */ + mci = mci_get(hp, m); + mci->mci_exitstat = rcode; + mci->mci_errno = errno; +#ifdef NAMED_BIND + mci->mci_herrno = h_errno; +#endif - /* owner list exists -- add it to the error queue */ - sendtolist(obuf, (ADDRESS *) NULL, &e->e_errorqueue); - ErrorMode = EM_MAIL; - break; + /* and return the original host name as the signature */ + nmx = 1; + mxhosts[0] = hp; } - /* if we did not find an owner, send to the sender */ - if (qq == NULL && bitset(QBADADDR, q->q_flags)) - sendtolist(e->e_from.q_paddr, qq, &e->e_errorqueue); + len = 0; + for (i = 0; i < nmx; i++) + { + len += strlen(mxhosts[i]) + 1; + } + if (s->s_hostsig != NULL) + len += strlen(s->s_hostsig) + 1; + p = xalloc(len); + if (s->s_hostsig != NULL) + { + (void) strcpy(p, s->s_hostsig); + free(s->s_hostsig); + s->s_hostsig = p; + p += strlen(p); + *p++ = ':'; + } + else + s->s_hostsig = p; + for (i = 0; i < nmx; i++) + { + if (i != 0) + *p++ = ':'; + strcpy(p, mxhosts[i]); + p += strlen(p); + } + if (endp != NULL) + *endp++ = ':'; } - - /* this removes the lock on the file */ - if (lockfp != NULL) - (void) fclose(lockfp); - - if (mode == SM_FORK) - finis(); + makelower(s->s_hostsig); + if (ConfigLevel < 2) + _res.options = oldoptions; +#else + /* not using BIND -- the signature is just the host name */ + s->s_hostsig = host; +#endif + if (tTd(17, 1)) + printf("hostsignature(%s) = %s\n", host, s->s_hostsig); + return s->s_hostsig; } diff --git a/usr.sbin/sendmail/src/domain.c b/usr.sbin/sendmail/src/domain.c index 1bef478cff..fe977d29ed 100644 --- a/usr.sbin/sendmail/src/domain.c +++ b/usr.sbin/sendmail/src/domain.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1986 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,29 +36,62 @@ #ifndef lint #ifdef NAMED_BIND -static char sccsid[] = "@(#)domain.c 5.23 (Berkeley) 3/2/91 (with name server)"; +static char sccsid[] = "@(#)domain.c 8.1 (Berkeley) 6/7/93 (with name server)"; #else -static char sccsid[] = "@(#)domain.c 5.23 (Berkeley) 3/2/91 (without name server)"; +static char sccsid[] = "@(#)domain.c 8.1 (Berkeley) 6/7/93 (without name server)"; #endif #endif /* not lint */ #ifdef NAMED_BIND -#include <sys/param.h> #include <errno.h> #include <arpa/nameser.h> #include <resolv.h> #include <netdb.h> -typedef union { - HEADER qb1; - char qb2[PACKETSZ]; +typedef union +{ + HEADER qb1; + char qb2[PACKETSZ]; } querybuf; -static char hostbuf[MAXMXHOSTS*PACKETSZ]; +static char MXHostBuf[MAXMXHOSTS*PACKETSZ]; + +#ifndef MAXDNSRCH +#define MAXDNSRCH 6 /* number of possible domains to search */ +#endif + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/* don't use sizeof because sizeof(long) is different on 64-bit machines */ +#define SHORTSIZE 2 /* size of a short (really, must be 2) */ +#define LONGSIZE 4 /* size of a long (really, must be 4) */ + +#define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */ + /* +** GETMXRR -- get MX resource records for a domain +** +** Parameters: +** host -- the name of the host to MX. +** mxhosts -- a pointer to a return buffer of MX records. +** droplocalhost -- If TRUE, all MX records less preferred +** than the local host (as determined by $=w) will +** be discarded. +** rcode -- a pointer to an EX_ status code. +** +** Returns: +** The number of MX records found. +** -1 if there is an internal failure. +** If no MX records are found, mxhosts[0] is set to host +** and 1 is returned. +*/ -getmxrr(host, mxhosts, localhost, rcode) - char *host, **mxhosts, *localhost; +getmxrr(host, mxhosts, droplocalhost, rcode) + char *host; + char **mxhosts; + bool droplocalhost; int *rcode; { extern int h_errno; @@ -67,16 +100,40 @@ getmxrr(host, mxhosts, localhost, rcode) register char *bp; HEADER *hp; querybuf answer; - int ancount, qdcount, buflen, seenlocal; - u_short pref, localpref, type, prefer[MAXMXHOSTS]; + int ancount, qdcount, buflen; + bool seenlocal; + u_short pref, localpref, type; + char *fallbackMX = FallBackMX; + static bool firsttime = TRUE; + STAB *st; + u_short prefer[MAXMXHOSTS]; + int weight[MAXMXHOSTS]; + + if (fallbackMX != NULL) + { + if (firsttime && res_query(FallBackMX, C_IN, T_A, + (char *) &answer, sizeof answer) < 0) + { + /* this entry is bogus */ + fallbackMX = FallBackMX = NULL; + } + else if (droplocalhost && + (st = stab(fallbackMX, ST_CLASS, ST_FIND)) != NULL && + bitnset('w', st->s_class)) + { + /* don't use fallback for this pass */ + fallbackMX = NULL; + } + firsttime = FALSE; + } errno = 0; n = res_search(host, C_IN, T_MX, (char *)&answer, sizeof(answer)); if (n < 0) { if (tTd(8, 1)) - printf("getmxrr: res_search failed (errno=%d, h_errno=%d)\n", - errno, h_errno); + printf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", + (host == NULL) ? "<NULL>" : host, errno, h_errno); switch (h_errno) { case NO_DATA: @@ -108,174 +165,439 @@ getmxrr(host, mxhosts, localhost, rcode) cp = (u_char *)&answer + sizeof(HEADER); eom = (u_char *)&answer + n; for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) - if ((n = __dn_skipname(cp, eom)) < 0) + if ((n = dn_skipname(cp, eom)) < 0) goto punt; nmx = 0; - seenlocal = 0; - buflen = sizeof(hostbuf); - bp = hostbuf; + seenlocal = FALSE; + buflen = sizeof(MXHostBuf) - 1; + bp = MXHostBuf; ancount = ntohs(hp->ancount); - while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS) { + while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) + { if ((n = dn_expand((u_char *)&answer, eom, cp, (u_char *)bp, buflen)) < 0) break; cp += n; GETSHORT(type, cp); - cp += sizeof(u_short) + sizeof(u_long); + cp += SHORTSIZE + LONGSIZE; GETSHORT(n, cp); - if (type != T_MX) { - if (tTd(8, 1) || _res.options & RES_DEBUG) + if (type != T_MX) + { + if (tTd(8, 8) || _res.options & RES_DEBUG) printf("unexpected answer type %d, size %d\n", type, n); cp += n; continue; } GETSHORT(pref, cp); - if ((n = dn_expand((u_char *)&answer, - eom, cp, (u_char *)bp, buflen)) < 0) + if ((n = dn_expand((u_char *)&answer, eom, cp, + (u_char *)bp, buflen)) < 0) break; cp += n; - if (!strcasecmp(bp, localhost)) { - if (seenlocal == 0 || pref < localpref) + if (droplocalhost && + (st = stab(bp, ST_CLASS, ST_FIND)) != NULL && + bitnset('w', st->s_class)) + { + if (!seenlocal || pref < localpref) localpref = pref; - seenlocal = 1; + seenlocal = TRUE; continue; } + weight[nmx] = mxrand(bp); prefer[nmx] = pref; mxhosts[nmx++] = bp; - n = strlen(bp) + 1; + n = strlen(bp); bp += n; - buflen -= n; + if (bp[-1] != '.') + { + *bp++ = '.'; + n++; + } + *bp++ = '\0'; + buflen -= n + 1; } - if (nmx == 0) { -punt: mxhosts[0] = strcpy(hostbuf, host); - return(1); + if (nmx == 0) + { +punt: + mxhosts[0] = strcpy(MXHostBuf, host); + bp = &MXHostBuf[strlen(MXHostBuf)]; + if (bp[-1] != '.') + { + *bp++ = '.'; + *bp = '\0'; + } + nmx = 1; } + else + { + /* sort the records */ + for (i = 0; i < nmx; i++) + { + for (j = i + 1; j < nmx; j++) + { + if (prefer[i] > prefer[j] || + (prefer[i] == prefer[j] && weight[i] > weight[j])) + { + register int temp; + register char *temp1; - /* sort the records */ - for (i = 0; i < nmx; i++) { - for (j = i + 1; j < nmx; j++) { - if (prefer[i] > prefer[j] || - (prefer[i] == prefer[j] && rand() % 1 == 0)) { - register int temp; - register char *temp1; - - temp = prefer[i]; - prefer[i] = prefer[j]; - prefer[j] = temp; - temp1 = mxhosts[i]; - mxhosts[i] = mxhosts[j]; - mxhosts[j] = temp1; + temp = prefer[i]; + prefer[i] = prefer[j]; + prefer[j] = temp; + temp1 = mxhosts[i]; + mxhosts[i] = mxhosts[j]; + mxhosts[j] = temp1; + temp = weight[i]; + weight[i] = weight[j]; + weight[j] = temp; + } } - } - if (seenlocal && prefer[i] >= localpref) { - /* - * truncate higher pref part of list; if we're - * the best choice left, we should have realized - * awhile ago that this was a local delivery. - */ - if (i == 0) { - *rcode = EX_CONFIG; - return(-1); + if (seenlocal && prefer[i] >= localpref) + { + /* + * truncate higher pref part of list; if we're + * the best choice left, we should have realized + * awhile ago that this was a local delivery. + */ + if (i == 0) + { + *rcode = EX_CONFIG; + return (-1); + } + nmx = i; + break; } - nmx = i; - break; } } - return(nmx); + + /* if we have a default lowest preference, include that */ + if (FallBackMX != NULL && !seenlocal) + mxhosts[nmx++] = FallBackMX; + + return (nmx); } + /* +** MXRAND -- create a randomizer for equal MX preferences +** +** If two MX hosts have equal preferences we want to randomize +** the selection. But in order for signatures to be the same, +** we need to randomize the same way each time. This function +** computes a pseudo-random hash function from the host name. +** +** Parameters: +** host -- the name of the host. +** +** Returns: +** A random but repeatable value based on the host name. +** +** Side Effects: +** none. +*/ + +mxrand(host) + register char *host; +{ + int hfunc; + static unsigned int seed; + if (seed == 0) + { + seed = (int) curtime() & 0xffff; + if (seed == 0) + seed++; + } + + if (tTd(17, 9)) + printf("mxrand(%s)", host); + + hfunc = seed; + while (*host != '\0') + { + int c = *host++; + + if (isascii(c) && isupper(c)) + c = tolower(c); + hfunc = ((hfunc << 1) + c) % 2003; + } + + hfunc &= 0xff; + + if (tTd(17, 9)) + printf(" = %d\n", hfunc); + return hfunc; +} + /* +** GETCANONNAME -- get the canonical name for named host +** +** This algorithm tries to be smart about wildcard MX records. +** This is hard to do because DNS doesn't tell is if we matched +** against a wildcard or a specific MX. +** +** We always prefer A & CNAME records, since these are presumed +** to be specific. +** +** If we match an MX in one pass and lose it in the next, we use +** the old one. For example, consider an MX matching *.FOO.BAR.COM. +** A hostname bletch.foo.bar.com will match against this MX, but +** will stop matching when we try bletch.bar.com -- so we know +** that bletch.foo.bar.com must have been right. This fails if +** there was also an MX record matching *.BAR.COM, but there are +** some things that just can't be fixed. +** +** Parameters: +** host -- a buffer containing the name of the host. +** This is a value-result parameter. +** hbsize -- the size of the host buffer. +** +** Returns: +** TRUE -- if the host matched. +** FALSE -- otherwise. +*/ + +bool getcanonname(host, hbsize) char *host; int hbsize; { extern int h_errno; - register u_char *eom, *cp; + register u_char *eom, *ap; + register char *cp; register int n; HEADER *hp; querybuf answer; - u_short type; - int first, ancount, qdcount, loopcnt; - char nbuf[PACKETSZ]; + int ancount, qdcount; + int ret; + char **domain; + int type; + char **dp; + char *mxmatch; + bool amatch; + bool gotmx; + int qtype; + int loopcnt; + char nbuf[MAX(PACKETSZ, MAXDNAME*2+2)]; + char *searchlist[MAXDNSRCH+2]; + + if (tTd(8, 2)) + printf("getcanonname(%s)\n", host); + + if ((_res.options & RES_INIT) == 0 && res_init() == -1) + return (FALSE); - loopcnt = 0; -loop: /* - * Use query type of ANY if possible (NO_WILDCARD_MX), which will - * find types CNAME, A, and MX, and will cause all existing records - * to be cached by our local server. If there is (might be) a - * wildcard MX record in the local domain or its parents that are - * searched, we can't use ANY; it would cause fully-qualified names - * to match as names in a local domain. - */ -# ifdef NO_WILDCARD_MX - n = res_search(host, C_IN, T_ANY, (char *)&answer, sizeof(answer)); -# else - n = res_search(host, C_IN, T_CNAME, (char *)&answer, sizeof(answer)); -# endif - if (n < 0) { - if (tTd(8, 1)) - printf("getcanonname: res_search failed (errno=%d, h_errno=%d)\n", - errno, h_errno); - return; - } + ** Initialize domain search list. If there is at least one + ** dot in the name, search the unmodified name first so we + ** find "vse.CS" in Czechoslovakia instead of in the local + ** domain (e.g., vse.CS.Berkeley.EDU). + ** + ** Older versions of the resolver could create this + ** list by tearing apart the host name. + */ - /* find first satisfactory answer */ - hp = (HEADER *)&answer; - ancount = ntohs(hp->ancount); + loopcnt = 0; +cnameloop: + for (cp = host, n = 0; *cp; cp++) + if (*cp == '.') + n++; - /* we don't care about errors here, only if we got an answer */ - if (ancount == 0) { - if (tTd(8, 1)) - printf("rcode = %d, ancount=%d\n", hp->rcode, ancount); - return; + dp = searchlist; + if (n > 0) + *dp++ = ""; + if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options)) + { + for (domain = _res.dnsrch; *domain != NULL; ) + *dp++ = *domain++; } - cp = (u_char *)&answer + sizeof(HEADER); - eom = (u_char *)&answer + n; - for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) - if ((n = __dn_skipname(cp, eom)) < 0) - return; + else if (n == 0 && bitset(RES_DEFNAMES, _res.options)) + { + *dp++ = _res.defdname; + } + *dp = NULL; /* - * just in case someone puts a CNAME record after another record, - * check all records for CNAME; otherwise, just take the first - * name found. - */ - for (first = 1; --ancount >= 0 && cp < eom; cp += n) { - if ((n = dn_expand((u_char *)&answer, - eom, cp, (u_char *)nbuf, sizeof(nbuf))) < 0) - break; - if (first) { /* XXX */ - (void)strncpy(host, nbuf, hbsize); - host[hbsize - 1] = '\0'; - first = 0; + ** Now run through the search list for the name in question. + */ + + mxmatch = NULL; + qtype = T_ANY; + + for (dp = searchlist; *dp != NULL; ) + { + if (qtype == T_ANY) + gotmx = FALSE; + if (tTd(8, 5)) + printf("getcanonname: trying %s.%s (%s)\n", host, *dp, + qtype == T_ANY ? "ANY" : qtype == T_A ? "A" : + qtype == T_MX ? "MX" : "???"); + ret = res_querydomain(host, *dp, C_IN, qtype, + &answer, sizeof(answer)); + if (ret <= 0) + { + if (tTd(8, 7)) + printf("\tNO: errno=%d, h_errno=%d\n", + errno, h_errno); + + if (errno == ECONNREFUSED || h_errno == TRY_AGAIN) + { + /* the name server seems to be down */ + h_errno = TRY_AGAIN; + return FALSE; + } + + if (h_errno != HOST_NOT_FOUND) + { + /* might have another type of interest */ + if (qtype == T_ANY) + { + qtype = T_A; + continue; + } + else if (qtype == T_A && !gotmx) + { + qtype = T_MX; + continue; + } + } + + if (mxmatch != NULL) + { + /* we matched before -- use that one */ + break; + } + + /* otherwise, try the next name */ + dp++; + qtype = T_ANY; + continue; } - cp += n; - GETSHORT(type, cp); - cp += sizeof(u_short) + sizeof(u_long); - GETSHORT(n, cp); - if (type == T_CNAME) { - /* - * assume that only one cname will be found. More - * than one is undefined. Copy so that if dn_expand - * fails, `host' is still okay. - */ - if ((n = dn_expand((u_char *)&answer, - eom, cp, (u_char *)nbuf, sizeof(nbuf))) < 0) + else if (tTd(8, 7)) + printf("\tYES\n"); + + /* + ** This might be a bogus match. Search for A or + ** CNAME records. If we don't have a matching + ** wild card MX record, we will accept MX as well. + */ + + hp = (HEADER *) &answer; + ap = (u_char *) &answer + sizeof(HEADER); + eom = (u_char *) &answer + ret; + + /* skip question part of response -- we know what we asked */ + for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ) + { + if ((ret = dn_skipname(ap, eom)) < 0) + { + if (tTd(8, 20)) + printf("qdcount failure (%d)\n", + ntohs(hp->qdcount)); + return FALSE; /* ???XXX??? */ + } + } + + amatch = FALSE; + for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n) + { + n = dn_expand((u_char *) &answer, eom, ap, + (u_char *) nbuf, sizeof nbuf); + if (n < 0) break; - (void)strncpy(host, nbuf, hbsize); /* XXX */ - host[hbsize - 1] = '\0'; - if (++loopcnt > 8) /* never be more than 1 */ - return; - goto loop; + ap += n; + GETSHORT(type, ap); + ap += SHORTSIZE + LONGSIZE; + GETSHORT(n, ap); + switch (type) + { + case T_MX: + gotmx = TRUE; + if (**dp != '\0') + { + /* got a match -- save that info */ + if (mxmatch == NULL) + mxmatch = *dp; + continue; + } + + /* exact MX matches are as good as an A match */ + /* fall through */ + + case T_A: + /* good show */ + amatch = TRUE; + + /* continue in case a CNAME also exists */ + continue; + + case T_CNAME: + if (loopcnt++ > MAXCNAMEDEPTH) + { + syserr("DNS failure: CNAME loop for %s", + host); + continue; + } + + /* value points at name */ + if ((ret = dn_expand((u_char *)&answer, + eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0) + break; + (void)strncpy(host, nbuf, hbsize); /* XXX */ + host[hbsize - 1] = '\0'; + + /* + ** RFC 1034 section 3.6 specifies that CNAME + ** should point at the canonical name -- but + ** urges software to try again anyway. + */ + + goto cnameloop; + + default: + /* not a record of interest */ + continue; + } + } + + if (amatch) + { + /* got an A record and no CNAME */ + mxmatch = *dp; + break; + } + + /* + ** If this was a T_ANY query, we may have the info but + ** need an explicit query. Try T_A, then T_MX. + */ + + if (qtype == T_ANY) + qtype = T_A; + else if (qtype == T_A && !gotmx) + qtype = T_MX; + else + { + /* really nothing in this domain; try the next */ + qtype = T_ANY; + dp++; } } + + if (mxmatch == NULL) + return FALSE; + + /* create matching name and return */ + (void) sprintf(nbuf, "%.*s%s%.*s", MAXDNAME, host, + *mxmatch == '\0' ? "" : ".", + MAXDNAME, mxmatch); + strncpy(host, nbuf, hbsize); + host[hbsize - 1] = '\0'; + return TRUE; } #else /* not NAMED_BIND */ #include <netdb.h> +bool getcanonname(host, hbsize) char *host; int hbsize; @@ -284,12 +606,13 @@ getcanonname(host, hbsize) hp = gethostbyname(host); if (hp == NULL) - return; + return (FALSE); if (strlen(hp->h_name) >= hbsize) - return; + return (FALSE); (void) strcpy(host, hp->h_name); + return (TRUE); } #endif /* not NAMED_BIND */ diff --git a/usr.sbin/sendmail/src/envelope.c b/usr.sbin/sendmail/src/envelope.c index 24b914a4a4..6818d8a59d 100644 --- a/usr.sbin/sendmail/src/envelope.c +++ b/usr.sbin/sendmail/src/envelope.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,15 +33,12 @@ */ #ifndef lint -static char sccsid[] = "@(#)envelope.c 5.22 (Berkeley) 6/1/90"; +static char sccsid[] = "@(#)envelope.c 8.3 (Berkeley) 7/13/93"; #endif /* not lint */ -#include <sys/types.h> +#include "sendmail.h" #include <sys/time.h> -#include <sys/stat.h> #include <pwd.h> -#include <sys/file.h> -#include "sendmail.h" /* ** NEWENVELOPE -- allocate a new envelope @@ -50,6 +47,7 @@ static char sccsid[] = "@(#)envelope.c 5.22 (Berkeley) 6/1/90"; ** ** Parameters: ** e -- the new envelope to fill in. +** parent -- the envelope to be the parent of e. ** ** Returns: ** e. @@ -59,15 +57,14 @@ static char sccsid[] = "@(#)envelope.c 5.22 (Berkeley) 6/1/90"; */ ENVELOPE * -newenvelope(e) +newenvelope(e, parent) register ENVELOPE *e; -{ register ENVELOPE *parent; +{ extern putheader(), putbody(); extern ENVELOPE BlankEnvelope; - parent = CurEnv; - if (e == CurEnv) + if (e == parent && e->e_parent != NULL) parent = e->e_parent; clearenvelope(e, TRUE); if (e == CurEnv) @@ -76,7 +73,8 @@ newenvelope(e) bcopy((char *) &CurEnv->e_from, (char *) &e->e_from, sizeof e->e_from); e->e_parent = parent; e->e_ctime = curtime(); - e->e_msgpriority = parent->e_msgsize; + if (parent != NULL) + e->e_msgpriority = parent->e_msgsize; e->e_puthdr = putheader; e->e_putbody = putbody; if (CurEnv->e_xfp != NULL) @@ -98,29 +96,40 @@ newenvelope(e) ** Unlocks this queue file. */ +void dropenvelope(e) register ENVELOPE *e; { bool queueit = FALSE; register ADDRESS *q; + char *id = e->e_id; + char buf[MAXLINE]; if (tTd(50, 1)) { - printf("dropenvelope %x id=", e); + printf("dropenvelope %x: id=", e); xputs(e->e_id); - printf(" flags=%o\n", e->e_flags); + printf(", flags=%o\n", e->e_flags); + if (tTd(50, 10)) + { + printf("sendq="); + printaddr(e->e_sendqueue, TRUE); + } } -#ifdef LOG - if (LogLevel > 10) - syslog(LOG_DEBUG, "dropenvelope, id=%s, flags=%o, pid=%d", - e->e_id == NULL ? "(none)" : e->e_id, - e->e_flags, getpid()); -#endif LOG /* we must have an id to remove disk files */ - if (e->e_id == NULL) + if (id == NULL) return; +#ifdef LOG + if (LogLevel > 84) + syslog(LOG_DEBUG, "dropenvelope, id=%s, flags=%o, pid=%d", + id, e->e_flags, getpid()); +#endif /* LOG */ + + /* post statistics */ + poststats(StatFile); + /* ** Extract state information from dregs of send list. */ @@ -131,6 +140,61 @@ dropenvelope(e) queueit = TRUE; } + /* + ** See if the message timed out. + */ + + if (!queueit) + /* nothing to do */ ; + else if (curtime() > e->e_ctime + TimeOuts.to_q_return) + { + if (!bitset(EF_TIMEOUT, e->e_flags)) + { + (void) sprintf(buf, "Cannot send message for %s", + pintvl(TimeOuts.to_q_return, FALSE)); + if (e->e_message != NULL) + free(e->e_message); + e->e_message = newstr(buf); + message(buf); + } + e->e_flags |= EF_TIMEOUT|EF_CLRQUEUE; + fprintf(e->e_xfp, "Message could not be delivered for %s\n", + pintvl(TimeOuts.to_q_return, FALSE)); + fprintf(e->e_xfp, "Message will be deleted from queue\n"); + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + { + if (bitset(QQUEUEUP, q->q_flags)) + q->q_flags |= QBADADDR; + } + } + else if (TimeOuts.to_q_warning > 0 && + curtime() > e->e_ctime + TimeOuts.to_q_warning) + { + if (!bitset(EF_WARNING|EF_RESPONSE, e->e_flags) && + e->e_class >= 0 && + strcmp(e->e_from.q_paddr, "<>") != 0) + { + (void) sprintf(buf, + "warning: cannot send message for %s", + pintvl(TimeOuts.to_q_warning, FALSE)); + if (e->e_message != NULL) + free(e->e_message); + e->e_message = newstr(buf); + message(buf); + e->e_flags |= EF_WARNING|EF_TIMEOUT; + } + fprintf(e->e_xfp, + "Warning: message still undelivered after %s\n", + pintvl(TimeOuts.to_q_warning, FALSE)); + fprintf(e->e_xfp, "Will keep trying until message is %s old\n", + pintvl(TimeOuts.to_q_return, FALSE)); + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + { + if (bitset(QQUEUEUP, q->q_flags)) + q->q_flags |= QREPORT; + } + } + /* ** Send back return receipts as requested. */ @@ -139,15 +203,16 @@ dropenvelope(e) { auto ADDRESS *rlist = NULL; - sendtolist(CurEnv->e_receiptto, (ADDRESS *) NULL, &rlist); - (void) returntosender("Return receipt", rlist, FALSE); + (void) sendtolist(e->e_receiptto, (ADDRESS *) NULL, &rlist, e); + (void) returntosender("Return receipt", rlist, FALSE, e); } /* ** Arrange to send error messages if there are fatal errors. */ - if (bitset(EF_FATALERRS|EF_TIMEOUT, e->e_flags) && ErrorMode != EM_QUIET) + if (bitset(EF_FATALERRS|EF_TIMEOUT, e->e_flags) && + e->e_errormode != EM_QUIET) savemail(e); /* @@ -157,6 +222,8 @@ dropenvelope(e) if ((!queueit && !bitset(EF_KEEPQUEUE, e->e_flags)) || bitset(EF_CLRQUEUE, e->e_flags)) { + if (tTd(50, 2)) + printf("Dropping envelope\n"); if (e->e_df != NULL) xunlink(e->e_df); xunlink(queuename(e, 'q')); @@ -164,13 +231,10 @@ dropenvelope(e) else if (queueit || !bitset(EF_INQUEUE, e->e_flags)) { #ifdef QUEUE - FILE *lockfp, *queueup(); - lockfp = queueup(e, FALSE, FALSE); - if (lockfp != NULL) - (void) fclose(lockfp); -#else QUEUE - syserr("dropenvelope: queueup"); -#endif QUEUE + queueup(e, FALSE, FALSE); +#else /* QUEUE */ + syserr("554 dropenvelope: queueup"); +#endif /* QUEUE */ } /* now unlock the job */ @@ -178,10 +242,15 @@ dropenvelope(e) unlockqueue(e); /* make sure that this envelope is marked unused */ - e->e_id = e->e_df = NULL; if (e->e_dfp != NULL) - (void) fclose(e->e_dfp); + (void) xfclose(e->e_dfp, "dropenvelope", e->e_df); e->e_dfp = NULL; + e->e_id = e->e_df = NULL; + +#ifdef LOG + if (LogLevel > 74) + syslog(LOG_INFO, "%s: done", id); +#endif /* LOG */ } /* ** CLEARENVELOPE -- clear an envelope without unlocking @@ -203,6 +272,7 @@ dropenvelope(e) ** Marks the envelope as unallocated. */ +void clearenvelope(e, fullclear) register ENVELOPE *e; bool fullclear; @@ -215,13 +285,16 @@ clearenvelope(e, fullclear) { /* clear out any file information */ if (e->e_xfp != NULL) - (void) fclose(e->e_xfp); + (void) xfclose(e->e_xfp, "clearenvelope xfp", e->e_id); if (e->e_dfp != NULL) - (void) fclose(e->e_dfp); + (void) xfclose(e->e_dfp, "clearenvelope dfp", e->e_df); + e->e_xfp = e->e_dfp = NULL; } /* now clear out the data */ STRUCTCOPY(BlankEnvelope, *e); + if (Verbose) + e->e_sendmode = SM_DELIVER; bh = BlankEnvelope.e_header; nhp = &e->e_header; while (bh != NULL) @@ -249,16 +322,18 @@ clearenvelope(e, fullclear) ** forms is set. */ -initsys() +void +initsys(e) + register ENVELOPE *e; { static char cbuf[5]; /* holds hop count */ static char pbuf[10]; /* holds pid */ #ifdef TTYNAME - static char ybuf[10]; /* holds tty id */ + static char ybuf[60]; /* holds tty id */ register char *p; -#endif TTYNAME +#endif /* TTYNAME */ extern char *ttyname(); - extern char *macvalue(); + extern void settime(); extern char Version[]; /* @@ -266,8 +341,8 @@ initsys() ** I.e., an id, a transcript, and a creation time. */ - openxscript(CurEnv); - CurEnv->e_ctime = curtime(); + openxscript(e); + e->e_ctime = curtime(); /* ** Set OutChannel to something useful if stdout isn't it. @@ -276,8 +351,9 @@ initsys() ** tucked away in the transcript). */ - if (OpMode == MD_DAEMON && QueueRun) - OutChannel = CurEnv->e_xfp; + if (OpMode == MD_DAEMON && !bitset(EF_QUEUERUN, e->e_flags) && + e->e_xfp != NULL) + OutChannel = e->e_xfp; /* ** Set up some basic system macros. @@ -285,29 +361,29 @@ initsys() /* process id */ (void) sprintf(pbuf, "%d", getpid()); - define('p', pbuf, CurEnv); + define('p', pbuf, e); /* hop count */ - (void) sprintf(cbuf, "%d", CurEnv->e_hopcount); - define('c', cbuf, CurEnv); + (void) sprintf(cbuf, "%d", e->e_hopcount); + define('c', cbuf, e); /* time as integer, unix time, arpa time */ - settime(); + settime(e); #ifdef TTYNAME /* tty name */ - if (macvalue('y', CurEnv) == NULL) + if (macvalue('y', e) == NULL) { p = ttyname(2); if (p != NULL) { - if (rindex(p, '/') != NULL) - p = rindex(p, '/') + 1; + if (strrchr(p, '/') != NULL) + p = strrchr(p, '/') + 1; (void) strcpy(ybuf, p); - define('y', ybuf, CurEnv); + define('y', ybuf, e); } } -#endif TTYNAME +#endif /* TTYNAME */ } /* ** SETTIME -- set the current time. @@ -322,7 +398,9 @@ initsys() ** Sets the various time macros -- $a, $b, $d, $t. */ -settime() +void +settime(e) + register ENVELOPE *e; { register char *p; auto time_t now; @@ -331,21 +409,21 @@ settime() register struct tm *tm; extern char *arpadate(); extern struct tm *gmtime(); - extern char *macvalue(); now = curtime(); tm = gmtime(&now); - (void) sprintf(tbuf, "%02d%02d%02d%02d%02d", tm->tm_year, tm->tm_mon+1, - tm->tm_mday, tm->tm_hour, tm->tm_min); - define('t', tbuf, CurEnv); + (void) sprintf(tbuf, "%04d%02d%02d%02d%02d", tm->tm_year + 1900, + tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + define('t', tbuf, e); (void) strcpy(dbuf, ctime(&now)); - *index(dbuf, '\n') = '\0'; - if (macvalue('d', CurEnv) == NULL) - define('d', dbuf, CurEnv); + p = strchr(dbuf, '\n'); + if (p != NULL) + *p = '\0'; + define('d', dbuf, e); p = newstr(arpadate(dbuf)); - if (macvalue('a', CurEnv) == NULL) - define('a', p, CurEnv); - define('b', p, CurEnv); + if (macvalue('a', e) == NULL) + define('a', p, e); + define('b', p, e); } /* ** OPENXSCRIPT -- Open transcript file @@ -363,24 +441,29 @@ settime() ** Creates the transcript file. */ +#ifndef O_APPEND +#define O_APPEND 0 +#endif + +void openxscript(e) register ENVELOPE *e; { register char *p; int fd; -# ifdef LOG - if (LogLevel > 19) - syslog(LOG_DEBUG, "%s: openx%s", e->e_id, e->e_xfp == NULL ? "" : " (no)"); -# endif LOG if (e->e_xfp != NULL) return; p = queuename(e, 'x'); - fd = open(p, O_WRONLY|O_CREAT, 0644); + fd = open(p, O_WRONLY|O_CREAT|O_APPEND, 0644); if (fd < 0) - syserr("Can't create %s", p); - else - e->e_xfp = fdopen(fd, "w"); + { + syserr("Can't create transcript file %s", p); + fd = open("/dev/null", O_WRONLY, 0644); + if (fd < 0) + syserr("!Can't open /dev/null"); + } + e->e_xfp = fdopen(fd, "w"); } /* ** CLOSEXSCRIPT -- close the transcript file. @@ -395,12 +478,13 @@ openxscript(e) ** none. */ +void closexscript(e) register ENVELOPE *e; { if (e->e_xfp == NULL) return; - (void) fclose(e->e_xfp); + (void) xfclose(e->e_xfp, "closexscript", e->e_id); e->e_xfp = NULL; } /* @@ -427,6 +511,11 @@ closexscript(e) ** Parameters: ** from -- the person we would like to believe this message ** is from, as specified on the command line. +** e -- the envelope in which we would like the sender set. +** delimptr -- if non-NULL, set to the location of the +** trailing delimiter. +** internal -- set if this address is coming from an internal +** source such as an owner alias. ** ** Returns: ** none. @@ -435,18 +524,20 @@ closexscript(e) ** sets sendmail's notion of who the from person is. */ -setsender(from) +void +setsender(from, e, delimptr, internal) char *from; + register ENVELOPE *e; + char **delimptr; + bool internal; { register char **pvp; char *realname = NULL; register struct passwd *pw; + char delimchar; char buf[MAXNAME]; char pvpbuf[PSBUFSIZE]; extern struct passwd *getpwnam(); - extern char *macvalue(); - extern char **prescan(); - extern bool safefile(); extern char *FullName; if (tTd(45, 1)) @@ -457,102 +548,123 @@ setsender(from) ** Username can return errno != 0 on non-errors. */ - if (QueueRun || OpMode == MD_SMTP || OpMode == MD_ARPAFTP) + if (bitset(EF_QUEUERUN, e->e_flags) || OpMode == MD_SMTP) realname = from; if (realname == NULL || realname[0] == '\0') - { - extern char *username(); - realname = username(); - } - - /* - ** Determine if this real person is allowed to alias themselves. - */ - - if (from != NULL) - { - extern bool trusteduser(); - if (!trusteduser(realname) && getuid() != geteuid() && - index(from, '!') == NULL && getuid() != 0) - { - /* network sends -r regardless (why why why?) */ - /* syserr("%s, you cannot use the -f flag", realname); */ - from = NULL; - } - } + if (ConfigLevel < 2) + SuprErrs = TRUE; - SuprErrs = TRUE; - if (from == NULL || parseaddr(from, &CurEnv->e_from, 1, '\0') == NULL) + delimchar = internal ? '\0' : ' '; + if (from == NULL || + parseaddr(from, &e->e_from, 1, delimchar, delimptr, e) == NULL) { /* log garbage addresses for traceback */ - if (from != NULL) - { # ifdef LOG - if (LogLevel >= 1) - if (realname == from && RealHostName != NULL) - syslog(LOG_NOTICE, - "from=%s unparseable, received from %s", - from, RealHostName); - else - syslog(LOG_NOTICE, - "Unparseable username %s wants from=%s", - realname, from); -# endif LOG + if (from != NULL && LogLevel > 2) + { + char *p; + char ebuf[MAXNAME * 2 + 2]; + + p = macvalue('_', e); + if (p == NULL) + { + char *host = RealHostName; + if (host == NULL) + host = MyHostName; + (void) sprintf(ebuf, "%s@%s", realname, host); + p = ebuf; + } + syslog(LOG_NOTICE, + "from=%s unparseable, received from %s", + from, p); } - from = newstr(realname); - if (parseaddr(from, &CurEnv->e_from, 1, '\0') == NULL && - parseaddr("postmaster", &CurEnv->e_from, 1, '\0') == NULL) +# endif /* LOG */ + if (from != NULL) + SuprErrs = TRUE; + if (from == realname || + parseaddr(from = newstr(realname), &e->e_from, 1, ' ', NULL, e) == NULL) { - syserr("setsender: can't even parse postmaster!"); + SuprErrs = TRUE; + if (parseaddr("postmaster", &e->e_from, 1, ' ', NULL, e) == NULL) + syserr("553 setsender: can't even parse postmaster!"); } } else FromFlag = TRUE; - CurEnv->e_from.q_flags |= QDONTSEND; - loweraddr(&CurEnv->e_from); + e->e_from.q_flags |= QDONTSEND; + if (tTd(45, 5)) + { + printf("setsender: QDONTSEND "); + printaddr(&e->e_from, FALSE); + } SuprErrs = FALSE; - if (CurEnv->e_from.q_mailer == LocalMailer && - (pw = getpwnam(CurEnv->e_from.q_user)) != NULL) + pvp = NULL; + if (e->e_from.q_mailer == LocalMailer) { - /* - ** Process passwd file entry. - */ - - - /* extract home directory */ - CurEnv->e_from.q_home = newstr(pw->pw_dir); - define('z', CurEnv->e_from.q_home, CurEnv); - - /* extract user and group id */ - CurEnv->e_from.q_uid = pw->pw_uid; - CurEnv->e_from.q_gid = pw->pw_gid; +# ifdef USERDB + register char *p; + extern char *udbsender(); +# endif - /* if the user has given fullname already, don't redefine */ - if (FullName == NULL) - FullName = macvalue('x', CurEnv); - if (FullName != NULL && FullName[0] == '\0') - FullName = NULL; + if (!internal) + { + /* if the user has given fullname already, don't redefine */ + if (FullName == NULL) + FullName = macvalue('x', e); + if (FullName != NULL && FullName[0] == '\0') + FullName = NULL; + +# ifdef USERDB + p = udbsender(from); + + if (p != NULL) + { + /* + ** We have an alternate address for the sender + */ + + pvp = prescan(p, '\0', pvpbuf, NULL); + } +# endif /* USERDB */ + } - /* extract full name from passwd file */ - if (FullName == NULL && pw->pw_gecos != NULL && - strcmp(pw->pw_name, CurEnv->e_from.q_user) == 0) + if ((pw = getpwnam(e->e_from.q_user)) != NULL) { - buildfname(pw->pw_gecos, CurEnv->e_from.q_user, buf); - if (buf[0] != '\0') - FullName = newstr(buf); + /* + ** Process passwd file entry. + */ + + + /* extract home directory */ + e->e_from.q_home = newstr(pw->pw_dir); + define('z', e->e_from.q_home, e); + + /* extract user and group id */ + e->e_from.q_uid = pw->pw_uid; + e->e_from.q_gid = pw->pw_gid; + + /* extract full name from passwd file */ + if (FullName == NULL && pw->pw_gecos != NULL && + strcmp(pw->pw_name, e->e_from.q_user) == 0 && + !internal) + { + buildfname(pw->pw_gecos, e->e_from.q_user, buf); + if (buf[0] != '\0') + FullName = newstr(buf); + } } - if (FullName != NULL) - define('x', FullName, CurEnv); + if (FullName != NULL && !internal) + define('x', FullName, e); } - else + else if (!internal) { - if (CurEnv->e_from.q_home == NULL) - CurEnv->e_from.q_home = getenv("HOME"); - CurEnv->e_from.q_uid = getuid(); - CurEnv->e_from.q_gid = getgid(); + if (e->e_from.q_home == NULL) + e->e_from.q_home = getenv("HOME"); + e->e_from.q_uid = RealUid; + e->e_from.q_gid = RealGid; } /* @@ -560,57 +672,33 @@ setsender(from) ** links in the net. */ - pvp = prescan(from, '\0', pvpbuf); + if (pvp == NULL) + pvp = prescan(from, '\0', pvpbuf, NULL); if (pvp == NULL) { + /* don't need to give error -- prescan did that already */ # ifdef LOG - if (LogLevel >= 1) + if (LogLevel > 2) syslog(LOG_NOTICE, "cannot prescan from (%s)", from); # endif - usrerr("cannot prescan from (%s)", from); finis(); } - rewrite(pvp, 3); - rewrite(pvp, 1); - rewrite(pvp, 4); - cataddr(pvp, buf, sizeof buf); - define('f', newstr(buf), CurEnv); + (void) rewrite(pvp, 3, e); + (void) rewrite(pvp, 1, e); + (void) rewrite(pvp, 4, e); + cataddr(pvp, NULL, buf, sizeof buf, '\0'); + e->e_sender = newstr(buf); + define('f', e->e_sender, e); /* save the domain spec if this mailer wants it */ - if (CurEnv->e_from.q_mailer != NULL && - bitnset(M_CANONICAL, CurEnv->e_from.q_mailer->m_flags)) + if (!internal && e->e_from.q_mailer != NULL && + bitnset(M_CANONICAL, e->e_from.q_mailer->m_flags)) { extern char **copyplist(); while (*pvp != NULL && strcmp(*pvp, "@") != 0) pvp++; if (*pvp != NULL) - CurEnv->e_fromdomain = copyplist(pvp, TRUE); + e->e_fromdomain = copyplist(pvp, TRUE); } } - /* -** TRUSTEDUSER -- tell us if this user is to be trusted. -** -** Parameters: -** user -- the user to be checked. -** -** Returns: -** TRUE if the user is in an approved list. -** FALSE otherwise. -** -** Side Effects: -** none. -*/ - -bool -trusteduser(user) - char *user; -{ - register char **ulist; - extern char *TrustedUsers[]; - - for (ulist = TrustedUsers; *ulist != NULL; ulist++) - if (strcmp(*ulist, user) == 0) - return (TRUE); - return (FALSE); -} diff --git a/usr.sbin/sendmail/src/err.c b/usr.sbin/sendmail/src/err.c index 3242da035f..9ee17becf4 100644 --- a/usr.sbin/sendmail/src/err.c +++ b/usr.sbin/sendmail/src/err.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,7 +33,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)err.c 5.11 (Berkeley) 3/2/91"; +static char sccsid[] = "@(#)err.c 8.2 (Berkeley) 7/11/93"; #endif /* not lint */ # include "sendmail.h" @@ -46,6 +46,11 @@ static char sccsid[] = "@(#)err.c 5.11 (Berkeley) 3/2/91"; ** Prints an error message via printf to the diagnostic ** output. If LOG is defined, it logs it also. ** +** If the first character of the syserr message is `!' it will +** log this as an ALERT message and exit immediately. This can +** leave queue files in an indeterminate state, so it should not +** be used lightly. +** ** Parameters: ** f -- the format string ** a, b, c, d, e -- parameters @@ -67,21 +72,33 @@ char MsgBuf[BUFSIZ*2]; /* text of most recent message */ static void fmtmsg(); +void /*VARARGS1*/ -syserr(fmt, a, b, c, d, e) - char *fmt; +#ifdef __STDC__ +syserr(const char *fmt, ...) +#else +syserr(fmt, va_alist) + const char *fmt; + va_dcl +#endif { register char *p; int olderrno = errno; - extern char Arpa_PSyserr[]; - extern char Arpa_TSyserr[]; + bool panic; + VA_LOCAL_DECL + + panic = *fmt == '!'; + if (panic) + fmt++; /* format and output the error message */ if (olderrno == 0) - p = Arpa_PSyserr; + p = "554"; else - p = Arpa_TSyserr; - fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, a, b, c, d, e); + p = "451"; + VA_START(fmt); + fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap); + VA_END; puterrmsg(MsgBuf); /* determine exit status if not already set */ @@ -95,10 +112,17 @@ syserr(fmt, a, b, c, d, e) # ifdef LOG if (LogLevel > 0) - syslog(LOG_CRIT, "%s: SYSERR: %s", + syslog(panic ? LOG_ALERT : LOG_CRIT, "%s: SYSERR: %s", CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, &MsgBuf[4]); -# endif LOG +# endif /* LOG */ + if (panic) + { +#ifdef XLA + xla_all_end(); +#endif + exit(EX_OSERR); + } errno = 0; if (QuickAbort) longjmp(TopFrame, 2); @@ -120,19 +144,34 @@ syserr(fmt, a, b, c, d, e) */ /*VARARGS1*/ -usrerr(fmt, a, b, c, d, e) - char *fmt; +void +#ifdef __STDC__ +usrerr(const char *fmt, ...) +#else +usrerr(fmt, va_alist) + const char *fmt; + va_dcl +#endif { + VA_LOCAL_DECL extern char SuprErrs; - extern char Arpa_Usrerr[]; extern int errno; if (SuprErrs) return; - fmtmsg(MsgBuf, CurEnv->e_to, Arpa_Usrerr, errno, fmt, a, b, c, d, e); + VA_START(fmt); + fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap); + VA_END; puterrmsg(MsgBuf); +# ifdef LOG + if (LogLevel > 3 && LogUsrErrs) + syslog(LOG_NOTICE, "%s: %s", + CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, + &MsgBuf[4]); +# endif /* LOG */ + if (QuickAbort) longjmp(TopFrame, 1); } @@ -140,9 +179,8 @@ usrerr(fmt, a, b, c, d, e) ** MESSAGE -- print message (not necessarily an error) ** ** Parameters: -** num -- the default ARPANET error number (in ascii) -** msg -- the message (printf fmt) -- if it begins -** with a digit, this number overrides num. +** msg -- the message (printf fmt) -- it can begin with +** an SMTP reply code. If not, 050 is assumed. ** a, b, c, d, e -- printf arguments ** ** Returns: @@ -153,13 +191,22 @@ usrerr(fmt, a, b, c, d, e) */ /*VARARGS2*/ -message(num, msg, a, b, c, d, e) - register char *num; - register char *msg; +void +#ifdef __STDC__ +message(const char *msg, ...) +#else +message(msg, va_alist) + const char *msg; + va_dcl +#endif { + VA_LOCAL_DECL + errno = 0; - fmtmsg(MsgBuf, CurEnv->e_to, num, 0, msg, a, b, c, d, e); - putmsg(MsgBuf, FALSE); + VA_START(msg); + fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap); + VA_END; + putoutmsg(MsgBuf, FALSE); } /* ** NMESSAGE -- print message (not necessarily an error) @@ -180,16 +227,25 @@ message(num, msg, a, b, c, d, e) */ /*VARARGS2*/ -nmessage(num, msg, a, b, c, d, e) - register char *num; - register char *msg; +void +#ifdef __STDC__ +nmessage(const char *msg, ...) +#else +nmessage(msg, va_alist) + const char *msg; + va_dcl +#endif { + VA_LOCAL_DECL + errno = 0; - fmtmsg(MsgBuf, (char *) NULL, num, 0, msg, a, b, c, d, e); - putmsg(MsgBuf, FALSE); + VA_START(msg); + fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap); + VA_END; + putoutmsg(MsgBuf, FALSE); } /* -** PUTMSG -- output error message to transcript and channel +** PUTOUTMSG -- output error message to transcript and channel ** ** Parameters: ** msg -- message to output (in SMTP format). @@ -205,7 +261,7 @@ nmessage(num, msg, a, b, c, d, e) ** Deletes SMTP reply code number as appropriate. */ -putmsg(msg, holdmsg) +putoutmsg(msg, holdmsg) char *msg; bool holdmsg; { @@ -214,18 +270,38 @@ putmsg(msg, holdmsg) fprintf(CurEnv->e_xfp, "%s\n", msg); /* output to channel if appropriate */ - if (!holdmsg && (Verbose || msg[0] != '0')) - { - (void) fflush(stdout); - if (OpMode == MD_SMTP || OpMode == MD_ARPAFTP) - fprintf(OutChannel, "%s\r\n", msg); - else - fprintf(OutChannel, "%s\n", &msg[4]); + if (holdmsg || (!Verbose && msg[0] == '0')) + return; + + (void) fflush(stdout); + if (OpMode == MD_SMTP) + fprintf(OutChannel, "%s\r\n", msg); + else + fprintf(OutChannel, "%s\n", &msg[4]); + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), + OpMode == MD_SMTP ? msg : &msg[4]); + if (msg[3] == ' ') (void) fflush(OutChannel); - } + if (!ferror(OutChannel)) + return; + + /* error on output -- if reporting lost channel, just ignore it */ + if (feof(InChannel) || ferror(InChannel)) + return; + + /* can't call syserr, 'cause we are using MsgBuf */ + HoldErrs = TRUE; +#ifdef LOG + if (LogLevel > 0) + syslog(LOG_CRIT, + "%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\"", + CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, + CurHostName, msg); +#endif } /* -** PUTERRMSG -- like putmsg, but does special processing for error messages +** PUTERRMSG -- like putoutmsg, but does special processing for error messages ** ** Parameters: ** msg -- the message to output. @@ -241,7 +317,7 @@ puterrmsg(msg) char *msg; { /* output the message as usual */ - putmsg(msg, HoldErrs); + putoutmsg(msg, HoldErrs); /* signal the error */ Errors++; @@ -266,16 +342,17 @@ puterrmsg(msg) ** none. */ -/*VARARGS5*/ static void -fmtmsg(eb, to, num, eno, fmt, a, b, c, d, e) +fmtmsg(eb, to, num, eno, fmt, ap) register char *eb; char *to; char *num; int eno; char *fmt; + va_list ap; { char del; + char *meb; /* output the reply code */ if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2])) @@ -305,19 +382,22 @@ fmtmsg(eb, to, num, eno, fmt, a, b, c, d, e) *eb++ &= 0177; } + meb = eb; + /* output the message */ - (void) sprintf(eb, fmt, a, b, c, d, e); + (void) vsprintf(eb, fmt, ap); while (*eb != '\0') *eb++ &= 0177; /* output the error code, if any */ if (eno != 0) { - extern char *errstring(); - (void) sprintf(eb, ": %s", errstring(eno)); eb += strlen(eb); } + + if (CurEnv->e_message == NULL && strchr("45", num[0]) != NULL) + CurEnv->e_message = newstr(meb); } /* ** ERRSTRING -- return string description of error code @@ -332,19 +412,19 @@ fmtmsg(eb, to, num, eno, fmt, a, b, c, d, e) ** none. */ -char * +const char * errstring(errno) int errno; { - extern char *sys_errlist[]; + extern const char *const sys_errlist[]; extern int sys_nerr; - static char buf[100]; + static char buf[MAXLINE]; # ifdef SMTP extern char *SmtpPhase; -# endif SMTP +# endif /* SMTP */ # ifdef DAEMON -# ifdef VMUNIX +# ifdef ETIMEDOUT /* ** Handle special network error codes. ** @@ -380,12 +460,22 @@ errstring(errno) (void) sprintf(buf, "Connection refused by %s", CurHostName); return (buf); - case (TRY_AGAIN+MAX_ERRNO): - (void) sprintf(buf, "Host Name Lookup Failure"); - return (buf); +# ifdef NAMED_BIND + case HOST_NOT_FOUND + MAX_ERRNO: + return ("Name server: host not found"); + + case TRY_AGAIN + MAX_ERRNO: + return ("Name server: host name lookup failure"); + + case NO_RECOVERY + MAX_ERRNO: + return ("Name server: non-recoverable error"); + + case NO_DATA + MAX_ERRNO: + return ("Name server: no data known for name"); +# endif } -# endif VMUNIX -# endif DAEMON +# endif +# endif if (errno > 0 && errno < sys_nerr) return (sys_errlist[errno]); diff --git a/usr.sbin/sendmail/src/headers.c b/usr.sbin/sendmail/src/headers.c index 02950f0812..6679b50347 100644 --- a/usr.sbin/sendmail/src/headers.c +++ b/usr.sbin/sendmail/src/headers.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,10 +33,9 @@ */ #ifndef lint -static char sccsid[] = "@(#)headers.c 5.15 (Berkeley) 6/1/90"; +static char sccsid[] = "@(#)headers.c 8.2 (Berkeley) 7/11/93"; #endif /* not lint */ -# include <sys/param.h> # include <errno.h> # include "sendmail.h" @@ -48,6 +47,7 @@ static char sccsid[] = "@(#)headers.c 5.15 (Berkeley) 6/1/90"; ** Parameters: ** line -- header as a text line. ** def -- if set, this is a default value. +** e -- the envelope including this header. ** ** Returns: ** flags for this header. @@ -57,9 +57,10 @@ static char sccsid[] = "@(#)headers.c 5.15 (Berkeley) 6/1/90"; ** Contents of 'line' are destroyed. */ -chompheader(line, def) +chompheader(line, def, e) char *line; bool def; + register ENVELOPE *e; { register char *p; register HDR *h; @@ -69,7 +70,6 @@ chompheader(line, def) struct hdrinfo *hi; bool cond = FALSE; BITMAP mopts; - extern char *crackaddr(); if (tTd(31, 6)) printf("chompheader: %s\n", line); @@ -80,7 +80,7 @@ chompheader(line, def) if (*p == '?') { /* have some */ - register char *q = index(p + 1, *p); + register char *q = strchr(p + 1, *p); if (q != NULL) { @@ -90,23 +90,22 @@ chompheader(line, def) p = q; } else - usrerr("chompheader: syntax error, line \"%s\"", line); + usrerr("553 header syntax error, line \"%s\"", line); cond = TRUE; } /* find canonical name */ fname = p; - p = index(p, ':'); + p = strchr(p, ':'); if (p == NULL) { - syserr("chompheader: syntax error, line \"%s\"", line); + syserr("553 header syntax error, line \"%s\"", line); return (0); } fvalue = &p[1]; - while (isspace(*--p)) + while (isascii(*--p) && isspace(*p)) continue; *++p = '\0'; - makelower(fname); /* strip field value on front */ if (*fvalue == ' ') @@ -115,13 +114,13 @@ chompheader(line, def) /* see if it is a known type */ for (hi = HdrInfo; hi->hi_field != NULL; hi++) { - if (strcmp(hi->hi_field, fname) == 0) + if (strcasecmp(hi->hi_field, fname) == 0) break; } /* see if this is a resent message */ if (!def && bitset(H_RESENT, hi->hi_flags)) - CurEnv->e_flags |= EF_RESENT; + e->e_flags |= EF_RESENT; /* if this means "end of header" quit now */ if (bitset(H_EOH, hi->hi_flags)) @@ -129,19 +128,25 @@ chompheader(line, def) /* drop explicit From: if same as what we would generate -- for MH */ p = "resent-from"; - if (!bitset(EF_RESENT, CurEnv->e_flags)) + if (!bitset(EF_RESENT, e->e_flags)) p += 7; - if (!def && !QueueRun && strcmp(fname, p) == 0) + if (!def && !bitset(EF_QUEUERUN, e->e_flags) && strcasecmp(fname, p) == 0) { - if (CurEnv->e_from.q_paddr != NULL && - strcmp(fvalue, CurEnv->e_from.q_paddr) == 0) + if (tTd(31, 2)) + { + printf("comparing header from (%s) against default (%s or %s)\n", + fvalue, e->e_from.q_paddr, e->e_from.q_user); + } + if (e->e_from.q_paddr != NULL && + (strcmp(fvalue, e->e_from.q_paddr) == 0 || + strcmp(fvalue, e->e_from.q_user) == 0)) return (hi->hi_flags); } /* delete default value for this header */ - for (hp = &CurEnv->e_header; (h = *hp) != NULL; hp = &h->h_link) + for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link) { - if (strcmp(fname, h->h_field) == 0 && + if (strcasecmp(fname, h->h_field) == 0 && bitset(H_DEFAULT, h->h_flags) && !bitset(H_FORCE, h->h_flags)) h->h_value = NULL; @@ -165,10 +170,10 @@ chompheader(line, def) /* hack to see if this is a new format message */ if (!def && bitset(H_RCPT|H_FROM, h->h_flags) && - (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL || - index(fvalue, '<') != NULL || index(fvalue, ';') != NULL)) + (strchr(fvalue, ',') != NULL || strchr(fvalue, '(') != NULL || + strchr(fvalue, '<') != NULL || strchr(fvalue, ';') != NULL)) { - CurEnv->e_flags &= ~EF_OLDSTYLE; + e->e_flags &= ~EF_OLDSTYLE; } return (h->h_flags); @@ -180,7 +185,7 @@ chompheader(line, def) ** ** Parameters: ** field -- the name of the header field. -** value -- the value of the field. It must be lower-cased. +** value -- the value of the field. ** e -- the envelope to add them to. ** ** Returns: @@ -202,14 +207,14 @@ addheader(field, value, e) /* find info struct */ for (hi = HdrInfo; hi->hi_field != NULL; hi++) { - if (strcmp(field, hi->hi_field) == 0) + if (strcasecmp(field, hi->hi_field) == 0) break; } /* find current place in list -- keep back pointer? */ for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link) { - if (strcmp(field, h->h_field) == 0) + if (strcasecmp(field, h->h_field) == 0) break; } @@ -230,6 +235,7 @@ addheader(field, value, e) ** ** Parameters: ** field -- the field name. +** e -- the envelope containing the header. ** ** Returns: ** pointer to the value part. @@ -240,14 +246,16 @@ addheader(field, value, e) */ char * -hvalue(field) +hvalue(field, e) char *field; + register ENVELOPE *e; { register HDR *h; - for (h = CurEnv->e_header; h != NULL; h = h->h_link) + for (h = e->e_header; h != NULL; h = h->h_link) { - if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0) + if (!bitset(H_DEFAULT, h->h_flags) && + strcasecmp(h->h_field, field) == 0) return (h->h_value); } return (NULL); @@ -277,7 +285,7 @@ isheader(s) s++; /* following technically violates RFC822 */ - while (isspace(*s)) + while (isascii(*s) && isspace(*s)) s++; return (*s == ':'); @@ -287,6 +295,8 @@ isheader(s) ** ** Parameters: ** e -- the envelope to process. +** full -- if set, do full processing (e.g., compute +** message priority). ** ** Returns: ** none. @@ -297,21 +307,42 @@ isheader(s) ** Aborts the message if the hop count is exceeded. */ -eatheader(e) +eatheader(e, full) register ENVELOPE *e; + bool full; { register HDR *h; register char *p; int hopcnt = 0; + char *msgid; + char buf[MAXLINE]; + + /* + ** Set up macros for possible expansion in headers. + */ + + define('f', e->e_sender, e); + define('g', e->e_sender, e); if (tTd(32, 1)) printf("----- collected header -----\n"); + msgid = "<none>"; for (h = e->e_header; h != NULL; h = h->h_link) { - extern char *capitalize(); + /* do early binding */ + if (bitset(H_DEFAULT, h->h_flags) && h->h_value != NULL) + { + expand(h->h_value, buf, &buf[sizeof buf], e); + if (buf[0] != '\0') + { + h->h_value = newstr(buf); + h->h_flags &= ~H_DEFAULT; + } + } if (tTd(32, 1)) - printf("%s: %s\n", capitalize(h->h_field), h->h_value); + printf("%s: %s\n", h->h_field, h->h_value); + /* count the number of times it has been processed */ if (bitset(H_TRACE, h->h_flags)) hopcnt++; @@ -319,107 +350,110 @@ eatheader(e) /* send to this person if we so desire */ if (GrabTo && bitset(H_RCPT, h->h_flags) && !bitset(H_DEFAULT, h->h_flags) && - (!bitset(EF_RESENT, CurEnv->e_flags) || bitset(H_RESENT, h->h_flags))) + (!bitset(EF_RESENT, e->e_flags) || bitset(H_RESENT, h->h_flags))) { - sendtolist(h->h_value, (ADDRESS *) NULL, &CurEnv->e_sendqueue); + (void) sendtolist(h->h_value, (ADDRESS *) NULL, + &e->e_sendqueue, e); } - /* log the message-id */ -#ifdef LOG - if (!QueueRun && LogLevel > 8 && h->h_value != NULL && - strcmp(h->h_field, "message-id") == 0) + /* save the message-id for logging */ + if (full && h->h_value != NULL && + strcasecmp(h->h_field, "message-id") == 0) { - char buf[MAXNAME]; - - p = h->h_value; - if (bitset(H_DEFAULT, h->h_flags)) - { - expand(p, buf, &buf[sizeof buf], e); - p = buf; - } - syslog(LOG_INFO, "%s: message-id=%s", e->e_id, p); + msgid = h->h_value; + while (isascii(*msgid) && isspace(*msgid)) + msgid++; } -#endif LOG + + /* see if this is a return-receipt header */ + if (bitset(H_RECEIPTTO, h->h_flags)) + e->e_receiptto = h->h_value; + + /* see if this is an errors-to header */ + if (UseErrorsTo && bitset(H_ERRORSTO, h->h_flags)) + (void) sendtolist(h->h_value, (ADDRESS *) NULL, + &e->e_errorqueue, e); } if (tTd(32, 1)) printf("----------------------------\n"); + /* if we are just verifying (that is, sendmail -t -bv), drop out now */ + if (OpMode == MD_VERIFY) + return; + /* store hop count */ if (hopcnt > e->e_hopcount) e->e_hopcount = hopcnt; /* message priority */ - p = hvalue("precedence"); + p = hvalue("precedence", e); if (p != NULL) e->e_class = priencode(p); - if (!QueueRun) + if (full) e->e_msgpriority = e->e_msgsize - e->e_class * WkClassFact + e->e_nrcpts * WkRecipFact; - /* return receipt to */ - p = hvalue("return-receipt-to"); - if (p != NULL) - e->e_receiptto = p; - - /* errors to */ - p = hvalue("errors-to"); - if (p != NULL) - sendtolist(p, (ADDRESS *) NULL, &e->e_errorqueue); - - /* from person */ - if (OpMode == MD_ARPAFTP) - { - register struct hdrinfo *hi = HdrInfo; - - for (p = NULL; p == NULL && hi->hi_field != NULL; hi++) - { - if (bitset(H_FROM, hi->hi_flags)) - p = hvalue(hi->hi_field); - } - if (p != NULL) - setsender(p); - } - /* full name of from person */ - p = hvalue("full-name"); + p = hvalue("full-name", e); if (p != NULL) define('x', p, e); /* date message originated */ - p = hvalue("posted-date"); + p = hvalue("posted-date", e); if (p == NULL) - p = hvalue("date"); + p = hvalue("date", e); if (p != NULL) - { define('a', p, e); - /* we don't have a good way to do canonical conversion .... - define('d', newstr(arpatounix(p)), e); - .... so we will ignore the problem for the time being */ - } /* ** Log collection information. */ # ifdef LOG - if (!QueueRun && LogLevel > 1) + if (full && LogLevel > 4) { - char hbuf[100], *name = hbuf; - - if (RealHostName == NULL) - name = "local"; + char *name; + register char *sbp; + char hbuf[MAXNAME]; + char sbuf[MAXLINE]; + + if (bitset(EF_RESPONSE, e->e_flags)) + name = "[RESPONSE]"; + else if ((name = macvalue('_', e)) != NULL) + ; else if (RealHostName[0] == '[') name = RealHostName; else - (void)sprintf(hbuf, "%.90s (%s)", - RealHostName, inet_ntoa(RealHostAddr.sin_addr)); - syslog(LOG_INFO, - "%s: from=%s, size=%ld, class=%d, received from %s\n", - CurEnv->e_id, CurEnv->e_from.q_paddr, CurEnv->e_msgsize, - CurEnv->e_class, name); + { + name = hbuf; + (void) sprintf(hbuf, "%.80s", RealHostName); + if (RealHostAddr.sa.sa_family != 0) + { + p = &hbuf[strlen(hbuf)]; + (void) sprintf(p, " (%s)", + anynet_ntoa(&RealHostAddr)); + } + } + + /* some versions of syslog only take 5 printf args */ + sbp = sbuf; + sprintf(sbp, "from=%.200s, size=%ld, class=%d, pri=%ld, nrcpts=%d, msgid=%.100s", + e->e_from.q_paddr, e->e_msgsize, e->e_class, + e->e_msgpriority, e->e_nrcpts, msgid); + sbp += strlen(sbp); + if (e->e_bodytype != NULL) + { + (void) sprintf(sbp, ", bodytype=%.20s", e->e_bodytype); + sbp += strlen(sbp); + } + p = macvalue('r', e); + if (p != NULL) + (void) sprintf(sbp, ", proto=%.20s", p); + syslog(LOG_INFO, "%s: %s, relay=%s", + e->e_id, sbuf, name); } -# endif LOG +# endif /* LOG */ } /* ** PRIENCODE -- encode external priority names into internal values. @@ -457,16 +491,10 @@ priencode(p) ** identical to what it started with. However, it does leave ** something semantically identical. ** -** The process is kind of strange. There are a number of -** interesting cases: -** 1. comment <address> comment ==> comment <$g> comment -** 2. address ==> address -** 3. address (comment) ==> $g (comment) -** 4. (comment) address ==> (comment) $g -** And then there are the hard cases.... -** 5. add (comment) ress ==> $g (comment) -** 6. comment <address (comment)> ==> comment <$g (comment)> -** 7. .... etc .... +** This algorithm has been cleaned up to handle a wider range +** of cases -- notably quoted and backslash escaped strings. +** This modification makes it substantially better at preserving +** the original syntax. ** ** Parameters: ** addr -- the address to be cracked. @@ -487,132 +515,221 @@ crackaddr(addr) register char *addr; { register char *p; - register int i; - static char buf[MAXNAME]; - char *rhs; - bool gotaddr; + register char c; + int cmtlev; + int realcmtlev; + int anglelev, realanglelev; + int copylev; + bool qmode; + bool realqmode; + bool skipping; + bool putgmac = FALSE; + bool quoteit = FALSE; register char *bp; + char *buflim; + static char buf[MAXNAME]; if (tTd(33, 1)) printf("crackaddr(%s)\n", addr); - (void) strcpy(buf, ""); - rhs = NULL; - /* strip leading spaces */ - while (*addr != '\0' && isspace(*addr)) + while (*addr != '\0' && isascii(*addr) && isspace(*addr)) addr++; /* - ** See if we have anything in angle brackets. If so, that is - ** the address part, and the rest is the comment. + ** Start by assuming we have no angle brackets. This will be + ** adjusted later if we find them. */ - p = index(addr, '<'); - if (p != NULL) + bp = buf; + buflim = &buf[sizeof buf - 5]; + p = addr; + copylev = anglelev = realanglelev = cmtlev = realcmtlev = 0; + qmode = realqmode = FALSE; + + while ((c = *p++) != '\0') { - /* copy the beginning of the addr field to the buffer */ - *p = '\0'; - (void) strcpy(buf, addr); - (void) strcat(buf, "<"); - *p++ = '<'; + /* + ** If the buffer is overful, go into a special "skipping" + ** mode that tries to keep legal syntax but doesn't actually + ** output things. + */ - /* skip spaces */ - while (isspace(*p)) - p++; + skipping = bp >= buflim; - /* find the matching right angle bracket */ - addr = p; - for (i = 0; *p != '\0'; p++) + if (copylev > 0 && !skipping) + *bp++ = c; + + /* check for backslash escapes */ + if (c == '\\') { - switch (*p) - { - case '<': - i++; - break; + /* arrange to quote the address */ + if (cmtlev <= 0 && !qmode) + quoteit = TRUE; - case '>': - i--; - break; + if ((c = *p++) == '\0') + { + /* too far */ + p--; + goto putg; } - if (i < 0) - break; + if (copylev > 0 && !skipping) + *bp++ = c; + goto putg; } - /* p now points to the closing quote (or a null byte) */ - if (*p != '\0') + /* check for quoted strings */ + if (c == '"') { - /* make rhs point to the extra stuff at the end */ - rhs = p; - *p++ = '\0'; + qmode = !qmode; + if (copylev > 0 && !skipping) + realqmode = !realqmode; + continue; } - } + if (qmode) + goto putg; - /* - ** Now parse the real address part. "addr" points to the (null - ** terminated) version of what we are inerested in; rhs points - ** to the extra stuff at the end of the line, if any. - */ - - p = addr; + /* check for comments */ + if (c == '(') + { + cmtlev++; - /* now strip out comments */ - bp = &buf[strlen(buf)]; - gotaddr = FALSE; - for (; *p != '\0'; p++) - { - if (*p == '(') + /* allow space for closing paren */ + if (!skipping) + { + buflim--; + realcmtlev++; + if (copylev++ <= 0) + { + *bp++ = ' '; + *bp++ = c; + } + } + } + if (cmtlev > 0) { - /* copy to matching close paren */ - *bp++ = *p++; - for (i = 0; *p != '\0'; p++) + if (c == ')') { - *bp++ = *p; - switch (*p) + cmtlev--; + copylev--; + if (!skipping) { - case '(': - i++; - break; - - case ')': - i--; - break; + realcmtlev--; + buflim++; } - if (i < 0) - break; } continue; } + else if (c == ')') + { + /* syntax error: unmatched ) */ + if (!skipping) + bp--; + } - /* - ** If this is the first "real" character we have seen, - ** then we put the "$g" in the buffer now. - */ - if (isspace(*p)) - *bp++ = *p; - else if (!gotaddr) + /* check for characters that may have to be quoted */ + if (strchr(".'@,;:\\()", c) != NULL) { - (void) strcpy(bp, "\001g"); - bp += 2; - gotaddr = TRUE; + /* + ** If these occur as the phrase part of a <> + ** construct, but are not inside of () or already + ** quoted, they will have to be quoted. Note that + ** now (but don't actually do the quoting). + */ + + if (cmtlev <= 0 && !qmode) + quoteit = TRUE; } - } - /* hack, hack.... strip trailing blanks */ - do - { - *bp-- = '\0'; - } while (isspace(*bp)); - bp++; + /* check for angle brackets */ + if (c == '<') + { + register char *q; - /* put any right hand side back on */ - if (rhs != NULL) - { - *rhs = '>'; - (void) strcpy(bp, rhs); + /* oops -- have to change our mind */ + anglelev++; + if (!skipping) + realanglelev++; + + bp = buf; + if (quoteit) + { + *bp++ = '"'; + + /* back up over the '<' and any spaces */ + --p; + while (isascii(*--p) && isspace(*p)) + continue; + p++; + } + for (q = addr; q < p; ) + { + c = *q++; + if (bp < buflim) + { + if (quoteit && c == '"') + *bp++ = '\\'; + *bp++ = c; + } + } + if (quoteit) + { + *bp++ = '"'; + while ((c = *p++) != '<') + { + if (bp < buflim) + *bp++ = c; + } + *bp++ = c; + } + copylev = 0; + putgmac = quoteit = FALSE; + continue; + } + + if (c == '>') + { + if (anglelev > 0) + { + anglelev--; + if (!skipping) + { + realanglelev--; + buflim++; + } + } + else if (!skipping) + { + /* syntax error: unmatched > */ + if (copylev > 0) + bp--; + continue; + } + if (copylev++ <= 0) + *bp++ = c; + continue; + } + + /* must be a real address character */ + putg: + if (copylev <= 0 && !putgmac) + { + *bp++ = MACROEXPAND; + *bp++ = 'g'; + putgmac = TRUE; + } } + /* repair any syntactic damage */ + if (realqmode) + *bp++ = '"'; + while (realcmtlev-- > 0) + *bp++ = ')'; + while (realanglelev-- > 0) + *bp++ = '>'; + *bp++ = '\0'; + if (tTd(33, 1)) printf("crackaddr=>`%s'\n", buf); @@ -633,29 +750,53 @@ crackaddr(addr) ** none. */ +/* + * Macro for fast max (not available in e.g. DG/UX, 386/ix). + */ +#ifndef MAX +# define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + putheader(fp, m, e) register FILE *fp; register MAILER *m; register ENVELOPE *e; { - char buf[MAX(MAXFIELD,BUFSIZ)]; + char buf[MAX(MAXLINE,BUFSIZ)]; register HDR *h; - extern char *arpadate(); - extern char *capitalize(); - char obuf[MAX(MAXFIELD,MAXLINE)]; + char obuf[MAXLINE]; + + if (tTd(34, 1)) + printf("--- putheader, mailer = %s ---\n", m->m_name); for (h = e->e_header; h != NULL; h = h->h_link) { register char *p; extern bool bitintersect(); + if (tTd(34, 11)) + { + printf(" %s: ", h->h_field); + xputs(h->h_value); + } + if (bitset(H_CHECK|H_ACHECK, h->h_flags) && !bitintersect(h->h_mflags, m->m_flags)) + { + if (tTd(34, 11)) + printf(" (skipped)\n"); continue; + } /* handle Resent-... headers specially */ if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) + { + if (tTd(34, 11)) + printf(" (skipped (resent))\n"); continue; + } + if (tTd(34, 11)) + printf("\n"); p = h->h_value; if (bitset(H_DEFAULT, h->h_flags)) @@ -674,15 +815,15 @@ putheader(fp, m, e) if (bitset(H_FROM, h->h_flags)) oldstyle = FALSE; - commaize(h, p, fp, oldstyle, m); + commaize(h, p, fp, oldstyle, m, e); } else { /* vanilla header line */ register char *nlp; - (void) sprintf(obuf, "%s: ", capitalize(h->h_field)); - while ((nlp = index(p, '\n')) != NULL) + (void) sprintf(obuf, "%s: ", h->h_field); + while ((nlp = strchr(p, '\n')) != NULL) { *nlp = '\0'; (void) strcat(obuf, p); @@ -706,6 +847,7 @@ putheader(fp, m, e) ** oldstyle -- TRUE if this is an old style header. ** m -- a pointer to the mailer descriptor. If NULL, ** don't transform the name at all. +** e -- the envelope containing the message. ** ** Returns: ** none. @@ -714,12 +856,13 @@ putheader(fp, m, e) ** outputs "p" to file "fp". */ -commaize(h, p, fp, oldstyle, m) +commaize(h, p, fp, oldstyle, m, e) register HDR *h; register char *p; FILE *fp; bool oldstyle; register MAILER *m; + register ENVELOPE *e; { register char *obp; int opos; @@ -735,7 +878,7 @@ commaize(h, p, fp, oldstyle, m) printf("commaize(%s: %s)\n", h->h_field, p); obp = obuf; - (void) sprintf(obp, "%s: ", capitalize(h->h_field)); + (void) sprintf(obp, "%s: ", h->h_field); opos = strlen(h->h_field) + 2; obp += opos; @@ -746,9 +889,10 @@ commaize(h, p, fp, oldstyle, m) while (*p != '\0') { register char *name; + register int c; char savechar; - extern char *remotename(); - extern char *DelimChar; /* defined in prescan */ + int flags; + auto int stat; /* ** Find the end of the name. New style names @@ -759,37 +903,35 @@ commaize(h, p, fp, oldstyle, m) */ /* find end of name */ - while (isspace(*p) || *p == ',') + while ((isascii(*p) && isspace(*p)) || *p == ',') p++; name = p; for (;;) { - char *oldp; + auto char *oldp; char pvpbuf[PSBUFSIZE]; - extern bool isatword(); - extern char **prescan(); - (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf); - p = DelimChar; + (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf, &oldp); + p = oldp; /* look to see if we have an at sign */ - oldp = p; - while (*p != '\0' && isspace(*p)) + while (*p != '\0' && isascii(*p) && isspace(*p)) p++; - if (*p != '@' && !isatword(p)) + if (*p != '@') { p = oldp; break; } p += *p == '@' ? 1 : 2; - while (*p != '\0' && isspace(*p)) + while (*p != '\0' && isascii(*p) && isspace(*p)) p++; } /* at the end of one complete name */ /* strip off trailing white space */ - while (p >= name && (isspace(*p) || *p == ',' || *p == '\0')) + while (p >= name && + ((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0')) p--; if (++p == name) continue; @@ -797,7 +939,11 @@ commaize(h, p, fp, oldstyle, m) *p = '\0'; /* translate the name to be relative */ - name = remotename(name, m, bitset(H_FROM, h->h_flags), FALSE); + flags = RF_HEADERADDR|RF_ADDDOMAIN; + if (bitset(H_FROM, h->h_flags)) + flags |= RF_SENDERADDR; + stat = EX_OK; + name = remotename(name, m, flags, &stat, e); if (*name == '\0') { *p = savechar; @@ -805,7 +951,7 @@ commaize(h, p, fp, oldstyle, m) } /* output the name with nice formatting */ - opos += qstrlen(name); + opos += strlen(name); if (!firstone) opos += 2; if (opos > 78 && !firstone) @@ -816,7 +962,7 @@ commaize(h, p, fp, oldstyle, m) (void) sprintf(obp, " "); opos = strlen(obp); obp += opos; - opos += qstrlen(name); + opos += strlen(name); } else if (!firstone) { @@ -824,13 +970,8 @@ commaize(h, p, fp, oldstyle, m) obp += 2; } - /* strip off quote bits as we output */ - while (*name != '\0' && obp < &obuf[MAXLINE]) - { - if (bitset(0200, *name)) - *obp++ = '\\'; - *obp++ = *name++ & ~0200; - } + while ((c = *name++) != '\0' && obp < &obuf[MAXLINE]) + *obp++ = c; firstone = FALSE; *p = savechar; } @@ -838,27 +979,37 @@ commaize(h, p, fp, oldstyle, m) putline(obuf, fp, m); } /* -** ISATWORD -- tell if the word we are pointing to is "at". +** COPYHEADER -- copy header list +** +** This routine is the equivalent of newstr for header lists ** ** Parameters: -** p -- word to check. +** header -- list of header structures to copy. ** ** Returns: -** TRUE -- if p is the word at. -** FALSE -- otherwise. +** a copy of 'header'. ** ** Side Effects: ** none. */ -bool -isatword(p) - register char *p; +HDR * +copyheader(header) + register HDR *header; { - extern char lower(); + register HDR *newhdr; + HDR *ret; + register HDR **tail = &ret; - if (lower(p[0]) == 'a' && lower(p[1]) == 't' && - p[2] != '\0' && isspace(p[2])) - return (TRUE); - return (FALSE); + while (header != NULL) + { + newhdr = (HDR *) xalloc(sizeof(HDR)); + STRUCTCOPY(*header, *newhdr); + *tail = newhdr; + tail = &newhdr->h_link; + header = header->h_link; + } + *tail = NULL; + + return ret; } diff --git a/usr.sbin/sendmail/src/macro.c b/usr.sbin/sendmail/src/macro.c index da35b4694b..877251debb 100644 --- a/usr.sbin/sendmail/src/macro.c +++ b/usr.sbin/sendmail/src/macro.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,7 +33,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)macro.c 5.7 (Berkeley) 6/1/90"; +static char sccsid[] = "@(#)macro.c 8.1 (Berkeley) 6/7/93"; #endif /* not lint */ # include "sendmail.h" @@ -55,6 +55,7 @@ static char sccsid[] = "@(#)macro.c 5.7 (Berkeley) 6/1/90"; ** none. */ +void expand(s, buf, buflim, e) register char *s; register char *buf; @@ -66,8 +67,8 @@ expand(s, buf, buflim, e) bool skipping; /* set if conditionally skipping output */ bool recurse = FALSE; /* set if recursion required */ int i; + int iflev; /* if nesting level */ char xbuf[BUFSIZ]; - extern char *macvalue(); if (tTd(35, 24)) { @@ -77,6 +78,7 @@ expand(s, buf, buflim, e) } skipping = FALSE; + iflev = 0; if (s == NULL) s = ""; for (xp = xbuf; *s != '\0'; s++) @@ -90,22 +92,29 @@ expand(s, buf, buflim, e) q = NULL; c = *s; - switch (c) + switch (c & 0377) { case CONDIF: /* see if var set */ c = *++s; - skipping = macvalue(c, e) == NULL; + if (skipping) + iflev++; + else + skipping = macvalue(c, e) == NULL; continue; case CONDELSE: /* change state of skipping */ - skipping = !skipping; + if (iflev == 0) + skipping = !skipping; continue; case CONDFI: /* stop skipping */ - skipping = FALSE; + if (iflev == 0) + skipping = FALSE; + if (skipping) + iflev--; continue; - case '\001': /* macro interpolation */ + case MACROEXPAND: /* macro interpolation */ c = *++s; q = macvalue(c & 0177, e); if (q == NULL) @@ -126,7 +135,8 @@ expand(s, buf, buflim, e) /* copy to end of q or max space remaining in buf */ while ((c = *q++) != '\0' && xp < &xbuf[sizeof xbuf - 1]) { - if (iscntrl(c) && !isspace(c)) + /* check for any sendmail metacharacters */ + if ((c & 0340) == 0200) recurse = TRUE; *xp++ = c; } @@ -188,7 +198,9 @@ expand(s, buf, buflim, e) ** $h to host ** $i queue id ** $j official SMTP hostname, used in messages+ +** $k UUCP node name ** $l UNIX-style from line+ +** $m The domain part of our full name. ** $n name of sendmail ("MAILER-DAEMON" on local ** net typically)+ ** $o delimiters ("operators") for address tokens+ @@ -204,6 +216,7 @@ expand(s, buf, buflim, e) ** $x signature (full name) of from person ** $y the tty id of our terminal ** $z home directory of to person +** $_ RFC1413 authenticated sender address ** ** Macros marked with + must be defined in the ** configuration file and are used internally, but @@ -215,6 +228,7 @@ expand(s, buf, buflim, e) ** are available. */ +void define(n, v, e) char n; char *v; diff --git a/usr.sbin/sendmail/src/mailstats.h b/usr.sbin/sendmail/src/mailstats.h index a67ff5842f..0164d91e14 100644 --- a/usr.sbin/sendmail/src/mailstats.h +++ b/usr.sbin/sendmail/src/mailstats.h @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,7 +31,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)mailstats.h 5.4 (Berkeley) 6/1/90 + * @(#)mailstats.h 8.1 (Berkeley) 6/7/93 */ /* diff --git a/usr.sbin/sendmail/src/main.c b/usr.sbin/sendmail/src/main.c index 895a589dcd..f4191ba61b 100644 --- a/usr.sbin/sendmail/src/main.c +++ b/usr.sbin/sendmail/src/main.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,24 +33,25 @@ */ #ifndef lint -char copyright[] = -"@(#) Copyright (c) 1988 Regents of the University of California.\n\ - All rights reserved.\n"; +static char copyright[] = +"@(#) Copyright (c) 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)main.c 5.32 (Berkeley) 3/2/91"; +static char sccsid[] = "@(#)main.c 8.3 (Berkeley) 7/13/93"; #endif /* not lint */ #define _DEFINE -#include <sys/param.h> -#include <sys/file.h> +#include "sendmail.h" #include <signal.h> #include <sgtty.h> -#include "sendmail.h" +#ifdef NAMED_BIND #include <arpa/nameser.h> #include <resolv.h> +#endif +#include <pwd.h> # ifdef lint char edata, end; @@ -81,6 +82,7 @@ char edata, end; ** Eric Allman, UCB/INGRES (until 10/81) ** Britton-Lee, Inc., purveyors of fine ** database computers (from 11/81) +** Now back at UCB at the Mammoth project. ** The support of the INGRES Project and Britton-Lee is ** gratefully acknowledged. Britton-Lee in ** particular had absolutely nothing to gain from @@ -94,6 +96,9 @@ ENVELOPE BlankEnvelope; /* a "blank" envelope */ ENVELOPE MainEnvelope; /* the envelope around the basic letter */ ADDRESS NullAddress = /* a null address */ { "", "", NULL, "" }; +char *UserEnviron[MAXUSERENVIRON + 1]; + /* saved user environment */ +char RealUserName[256]; /* the actual user id on this host */ /* ** Pointers for setproctitle. @@ -105,13 +110,17 @@ ADDRESS NullAddress = /* a null address */ # ifdef SETPROCTITLE char **Argv = NULL; /* pointer to argument vector */ char *LastArgv = NULL; /* end of argv */ -# endif SETPROCTITLE +# endif /* SETPROCTITLE */ + +static void obsolete(); #ifdef DAEMON #ifndef SMTP ERROR %%%% Cannot have daemon mode without SMTP %%%% ERROR -#endif SMTP -#endif DAEMON +#endif /* SMTP */ +#endif /* DAEMON */ + +#define MAXCONFIGLEVEL 4 /* highest config version level known */ main(argc, argv, envp) int argc; @@ -119,25 +128,33 @@ main(argc, argv, envp) char **envp; { register char *p; + register char *q; char **av; extern int finis(); extern char Version[]; - char *from; + char *ep, *from; typedef int (*fnptr)(); STAB *st; register int i; + int j; bool readconfig = TRUE; bool queuemode = FALSE; /* process queue requests */ bool nothaw; + bool safecf = TRUE; static bool reenter = FALSE; - char jbuf[30]; /* holds MyHostName */ - extern bool safefile(); + char *argv0 = argv[0]; + struct passwd *pw; + struct stat stb; + char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */ + extern int DtableSize; + extern int optind; extern time_t convtime(); extern putheader(), putbody(); - extern ENVELOPE *newenvelope(); extern void intsig(); extern char **myhostname(); extern char *arpadate(); + extern char *getauthinfo(); + extern char *optarg; extern char **environ; /* @@ -153,8 +170,15 @@ main(argc, argv, envp) } reenter = TRUE; - /* Enforce use of local time */ +#ifndef SYS5TZ + /* enforce use of kernel-supplied time zone information */ unsetenv("TZ"); +#endif + + /* in 4.4BSD, the table can be huge; impose a reasonable limit */ + DtableSize = getdtsize(); + if (DtableSize > 256) + DtableSize = 256; /* ** Be sure we have enough file descriptors. @@ -162,10 +186,20 @@ main(argc, argv, envp) */ i = open("/dev/null", O_RDWR); - while (i >= 0 && i < 2) - i = dup(i); - for (i = getdtablesize(); i > 2; --i) - (void) close(i); + if (fstat(STDIN_FILENO, &stb) < 0) + (void) dup2(i, STDIN_FILENO); + if (fstat(STDOUT_FILENO, &stb) < 0) + (void) dup2(i, STDOUT_FILENO); + if (fstat(STDERR_FILENO, &stb) < 0) + (void) dup2(i, STDERR_FILENO); + (void) close(i); + + i = DtableSize; + while (--i > 0) + { + if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) + (void) close(i); + } errno = 0; #ifdef LOG_MAIL @@ -174,13 +208,6 @@ main(argc, argv, envp) openlog("sendmail", LOG_PID); #endif - /* - ** Set default values for variables. - ** These cannot be in initialized data space. - */ - - setdefaults(); - /* set up the blank envelope */ BlankEnvelope.e_puthdr = putheader; BlankEnvelope.e_putbody = putbody; @@ -189,6 +216,28 @@ main(argc, argv, envp) CurEnv = &BlankEnvelope; STRUCTCOPY(NullAddress, MainEnvelope.e_from); + /* + ** Set default values for variables. + ** These cannot be in initialized data space. + */ + + setdefaults(&BlankEnvelope); + + RealUid = getuid(); + RealGid = getgid(); + + pw = getpwuid(RealUid); + if (pw != NULL) + (void) strcpy(RealUserName, pw->pw_name); + else + (void) sprintf(RealUserName, "Unknown UID %d", RealUid); + + /* our real uid will have to be root -- we will trash this later */ + setuid((uid_t) 0); + + /* Handle any non-getoptable constructions. */ + obsolete(argv); + /* ** Do a quick prescan of the argument list. ** We do this to find out if we can potentially thaw the @@ -196,45 +245,63 @@ main(argc, argv, envp) ** the argument processing applies to this run rather than ** to the run that froze the configuration. */ - - argv[argc] = NULL; - av = argv; nothaw = FALSE; - while ((p = *++av) != NULL) +#if defined(__osf__) || defined(_AIX3) +#define OPTIONS "B:b:C:cd:e:F:f:h:Iimno:p:q:r:sTtvX:x" +#else +#define OPTIONS "B:b:C:cd:e:F:f:h:Iimno:p:q:r:sTtvX:" +#endif + while ((j = getopt(argc, argv, OPTIONS)) != EOF) { - if (strncmp(p, "-C", 2) == 0) + switch (j) { - ConfFile = &p[2]; - if (ConfFile[0] == '\0') - ConfFile = "sendmail.cf"; - (void) setgid(getrgid()); - (void) setuid(getruid()); - nothaw = TRUE; - } - else if (strncmp(p, "-bz", 3) == 0) + case 'b': + if (optarg[0] == 'z' && optarg[1] == '\0') + nothaw = TRUE; + break; + + case 'C': + ConfFile = optarg; + (void) setgid(RealGid); + (void) setuid(RealUid); + safecf = FALSE; nothaw = TRUE; - else if (strncmp(p, "-d", 2) == 0) - { + break; + + case 'd': tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); - tTflag(&p[2]); + tTflag(optarg); setbuf(stdout, (char *) NULL); printf("Version %s\n", Version); + break; } } InChannel = stdin; OutChannel = stdout; +# ifdef FROZENCONFIG if (!nothaw) - readconfig = !thaw(FreezeFile); + readconfig = !thaw(FreezeFile, argv0); +# else + readconfig = TRUE; +# endif - /* reset the environment after the thaw */ - for (i = 0; i < MAXUSERENVIRON && envp[i] != NULL; i++) - UserEnviron[i] = newstr(envp[i]); - UserEnviron[i] = NULL; +# ifdef SETPROCTITLE + /* + ** Move the environment so setproctitle can use the space at + ** the top of memory. + */ + + for (i = j = 0; j < MAXUSERENVIRON && (p = envp[i]) != NULL; i++) + { + if (strncmp(p, "FS=", 3) == 0 || strncmp(p, "LD_", 3) == 0) + continue; + UserEnviron[j++] = newstr(p); + } + UserEnviron[j] = NULL; environ = UserEnviron; -# ifdef SETPROCTITLE /* ** Save start and extent of argv for setproctitle. */ @@ -244,7 +311,7 @@ main(argc, argv, envp) LastArgv = envp[i - 1] + strlen(envp[i - 1]); else LastArgv = argv[argc - 1] + strlen(argv[argc - 1]); -# endif SETPROCTITLE +# endif /* SETPROCTITLE */ if (signal(SIGINT, SIG_IGN) != SIG_IGN) (void) signal(SIGINT, intsig); @@ -252,11 +319,15 @@ main(argc, argv, envp) (void) signal(SIGHUP, intsig); (void) signal(SIGTERM, intsig); (void) signal(SIGPIPE, SIG_IGN); - OldUmask = umask(0); + OldUmask = umask(022); OpMode = MD_DELIVER; - MotherPid = getpid(); FullName = getenv("NAME"); +#ifdef NAMED_BIND + if (tTd(8, 8)) + _res.options |= RES_DEBUG; +#endif + errno = 0; from = NULL; @@ -265,36 +336,67 @@ main(argc, argv, envp) /* initialize some macros, etc. */ initmacros(); - /* hostname */ - av = myhostname(jbuf, sizeof jbuf); - if (jbuf[0] != '\0') + /* version */ + define('v', Version, CurEnv); + } + + /* hostname */ + av = myhostname(jbuf, sizeof jbuf); + if (jbuf[0] != '\0') + { + struct utsname utsname; + + if (tTd(0, 4)) + printf("canonical name: %s\n", jbuf); + p = newstr(jbuf); + define('w', p, CurEnv); + setclass('w', p); + + q = strchr(jbuf, '.'); + if (q != NULL) { - if (tTd(0, 4)) - printf("canonical name: %s\n", jbuf); + *q++ = '\0'; + define('m', q, CurEnv); p = newstr(jbuf); - define('w', p, CurEnv); setclass('w', p); } - while (av != NULL && *av != NULL) + + if (uname(&utsname) >= 0) + p = utsname.nodename; + else { - if (tTd(0, 4)) - printf("\ta.k.a.: %s\n", *av); - setclass('w', *av++); + makelower(jbuf); + p = jbuf; } - - /* version */ - define('v', Version, CurEnv); + if (tTd(0, 4)) + printf("UUCP nodename: %s\n", p); + p = newstr(p); + define('k', p, CurEnv); + setclass('w', p); + } + while (av != NULL && *av != NULL) + { + if (tTd(0, 4)) + printf("\ta.k.a.: %s\n", *av); + setclass('w', *av++); } /* current time */ define('b', arpadate((char *) NULL), CurEnv); + /* + ** Find our real host name for future logging. + */ + + p = getauthinfo(STDIN_FILENO); + define('_', p, CurEnv); + /* ** Crack argv. */ av = argv; - p = rindex(*av, '/'); + p = strrchr(*av, '/'); if (p++ == NULL) p = *av; if (strcmp(p, "newaliases") == 0) @@ -303,16 +405,18 @@ main(argc, argv, envp) OpMode = MD_PRINT; else if (strcmp(p, "smtpd") == 0) OpMode = MD_DAEMON; - while ((p = *++av) != NULL && p[0] == '-') + + optind = 1; + while ((j = getopt(argc, argv, OPTIONS)) != EOF) { - switch (p[1]) + switch (j) { case 'b': /* operations mode */ - switch (p[2]) + switch (j = *optarg) { case MD_DAEMON: # ifdef DAEMON - if (getuid() != 0) { + if (RealUid != 0) { usrerr("Permission denied"); exit (EX_USAGE); } @@ -321,87 +425,82 @@ main(argc, argv, envp) usrerr("Daemon mode not implemented"); ExitStat = EX_USAGE; break; -# endif DAEMON +# endif /* DAEMON */ case MD_SMTP: # ifndef SMTP usrerr("I don't speak SMTP"); ExitStat = EX_USAGE; break; -# endif SMTP - case MD_ARPAFTP: +# endif /* SMTP */ case MD_DELIVER: case MD_VERIFY: case MD_TEST: case MD_INITALIAS: case MD_PRINT: +#ifdef FROZENCONFIG case MD_FREEZE: - OpMode = p[2]; +#endif + OpMode = j; break; +#ifndef FROZENCONFIG + case MD_FREEZE: + usrerr("Frozen configurations unsupported"); + ExitStat = EX_USAGE; + break; +#endif + default: - usrerr("Invalid operation mode %c", p[2]); + usrerr("Invalid operation mode %c", j); ExitStat = EX_USAGE; break; } break; + case 'B': /* body type */ + CurEnv->e_bodytype = newstr(optarg); + break; + case 'C': /* select configuration file (already done) */ + if (RealUid != 0) + auth_warning(CurEnv, + "Processed by %s with -C %s", + RealUserName, optarg); break; case 'd': /* debugging -- redo in case frozen */ tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); - tTflag(&p[2]); + tTflag(optarg); setbuf(stdout, (char *) NULL); -#ifdef NAMED_BIND - _res.options |= RES_DEBUG; -#endif break; case 'f': /* from address */ case 'r': /* obsolete -f flag */ - p += 2; - if (*p == '\0' && ((p = *++av) == NULL || *p == '-')) - { - p = *++av; - if (p == NULL || *p == '-') - { - usrerr("No \"from\" person"); - ExitStat = EX_USAGE; - av--; - break; - } - } if (from != NULL) { usrerr("More than one \"from\" person"); ExitStat = EX_USAGE; break; } - from = newstr(p); + from = newstr(optarg); + if (strcmp(RealUserName, from) != 0) + auth_warning(CurEnv, + "%s set sender to %s using -%c", + RealUserName, from, j); break; case 'F': /* set full name */ - p += 2; - if (*p == '\0' && ((p = *++av) == NULL || *p == '-')) - { - usrerr("Bad -F flag"); - ExitStat = EX_USAGE; - av--; - break; - } - FullName = newstr(p); + FullName = newstr(optarg); break; case 'h': /* hop count */ - p += 2; - if (*p == '\0' && ((p = *++av) == NULL || !isdigit(*p))) + CurEnv->e_hopcount = strtol(optarg, &ep, 10); + if (*ep) { - usrerr("Bad hop count (%s)", p); + usrerr("Bad hop count (%s)", optarg); ExitStat = EX_USAGE; - av--; break; } - CurEnv->e_hopcount = atoi(p); break; case 'n': /* don't alias */ @@ -409,49 +508,102 @@ main(argc, argv, envp) break; case 'o': /* set option */ - setoption(p[2], &p[3], FALSE, TRUE); + setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv); + break; + + case 'p': /* set protocol */ + q = strchr(optarg, ':'); + if (q != NULL) + *q++ = '\0'; + if (*optarg != '\0') + define('r', newstr(optarg), CurEnv); + if (q != NULL && *q != '\0') + define('s', newstr(q), CurEnv); break; case 'q': /* run queue files at intervals */ # ifdef QUEUE - if (getuid() != 0) { - usrerr("Permission denied"); - exit (EX_USAGE); - } (void) unsetenv("HOSTALIASES"); + FullName = NULL; queuemode = TRUE; - QueueIntvl = convtime(&p[2]); -# else QUEUE + switch (optarg[0]) + { + case 'I': + QueueLimitId = newstr(&optarg[1]); + break; + + case 'R': + QueueLimitRecipient = newstr(&optarg[1]); + break; + + case 'S': + QueueLimitSender = newstr(&optarg[1]); + break; + + default: + QueueIntvl = convtime(optarg, 'm'); + break; + } +# else /* QUEUE */ usrerr("I don't know about queues"); ExitStat = EX_USAGE; -# endif QUEUE +# endif /* QUEUE */ break; case 't': /* read recipients from message */ GrabTo = TRUE; break; + case 'X': /* traffic log file */ + setuid(RealUid); + TrafficLogFile = fopen(optarg, "a"); + if (TrafficLogFile == NULL) + { + syserr("cannot open %s", optarg); + break; + } +#ifdef HASSETVBUF + setvbuf(TrafficLogFile, NULL, _IOLBF, BUFSIZ); +#else + setlinebuf(TrafficLogFile); +#endif + break; + /* compatibility flags */ case 'c': /* connect to non-local mailers */ - case 'e': /* error message disposition */ case 'i': /* don't let dot stop me */ case 'm': /* send to me too */ case 'T': /* set timeout interval */ case 'v': /* give blow-by-blow description */ - setoption(p[1], &p[2], FALSE, TRUE); + setoption(j, "T", FALSE, TRUE, CurEnv); + break; + + case 'e': /* error message disposition */ + setoption(j, optarg, FALSE, TRUE, CurEnv); break; case 's': /* save From lines in headers */ - setoption('f', &p[2], FALSE, TRUE); + setoption('f', "T", FALSE, TRUE, CurEnv); break; # ifdef DBM case 'I': /* initialize alias DBM file */ OpMode = MD_INITALIAS; break; -# endif DBM +# endif /* DBM */ + +# if defined(__osf__) || defined(_AIX3) + case 'x': /* random flag that OSF/1 & AIX mailx passes */ + break; +# endif + + default: + ExitStat = EX_USAGE; + finis(); + break; } } + av += optind; /* ** Do basic initialization. @@ -460,55 +612,132 @@ main(argc, argv, envp) */ if (OpMode == MD_FREEZE || readconfig) - readcf(ConfFile); + readcf(ConfFile, safecf, CurEnv); + +#ifdef SYS5TZ + /* Enforce use of local time (null string overrides this) */ + if (TimeZoneSpec == NULL) + unsetenv("TZ"); + else if (TimeZoneSpec[0] != '\0') + { + p = xalloc(strlen(TimeZoneSpec) + 4); + (void) strcpy(p, "TZ="); + (void) strcat(p, TimeZoneSpec); + putenv(p); + } +#endif + + if (ConfigLevel > MAXCONFIGLEVEL) + { + syserr("Warning: .cf version level (%d) exceeds program functionality (%d)", + ConfigLevel, MAXCONFIGLEVEL); + } + + +# ifdef QUEUE + if (queuemode && RealUid != 0) + { + struct stat stbuf; + + /* check to see if we own the queue directory */ + if (stat(QueueDir, &stbuf) < 0) + syserr("main: cannot stat %s", QueueDir); + if (stbuf.st_uid != RealUid) + { + /* nope, really a botch */ + usrerr("Permission denied"); + exit (EX_NOPERM); + } + } +# endif /* QUEUE */ switch (OpMode) { +# ifdef FROZENCONFIG case MD_FREEZE: /* this is critical to avoid forgeries of the frozen config */ - (void) setgid(getgid()); - (void) setuid(getuid()); + (void) setgid(RealGid); + (void) setuid(RealUid); /* freeze the configuration */ freeze(FreezeFile); exit(EX_OK); +# endif case MD_INITALIAS: Verbose = TRUE; break; + + case MD_DAEMON: + /* remove things that don't make sense in daemon mode */ + FullName = NULL; + break; + + case MD_SMTP: + if (RealUid != 0) + auth_warning(CurEnv, + "%s owned process doing -bs", + RealUserName); + break; } /* do heuristic mode adjustment */ if (Verbose) { /* turn off noconnect option */ - setoption('c', "F", TRUE, FALSE); + setoption('c', "F", TRUE, FALSE, CurEnv); /* turn on interactive delivery */ - setoption('d', "", TRUE, FALSE); + setoption('d', "", TRUE, FALSE, CurEnv); + } + + if (ConfigLevel < 3) + { + UseErrorsTo = TRUE; } /* our name for SMTP codes */ - expand("\001j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv); + expand("\201j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv); MyHostName = jbuf; - /* the indices of local and program mailers */ + /* the indices of built-in mailers */ st = stab("local", ST_MAILER, ST_FIND); if (st == NULL) syserr("No local mailer defined"); else LocalMailer = st->s_mailer; + st = stab("prog", ST_MAILER, ST_FIND); if (st == NULL) syserr("No prog mailer defined"); else ProgMailer = st->s_mailer; + st = stab("*file*", ST_MAILER, ST_FIND); + if (st == NULL) + syserr("No *file* mailer defined"); + else + FileMailer = st->s_mailer; + + st = stab("*include*", ST_MAILER, ST_FIND); + if (st == NULL) + syserr("No *include* mailer defined"); + else + InclMailer = st->s_mailer; + + /* operate in queue directory */ if (chdir(QueueDir) < 0) { syserr("cannot chdir(%s)", QueueDir); - exit(EX_SOFTWARE); + ExitStat = EX_SOFTWARE; + } + + /* if we've had errors so far, exit now */ + if (ExitStat != EX_OK && OpMode != MD_TEST) + { + setuid(RealUid); + exit(ExitStat); } /* @@ -522,15 +751,17 @@ main(argc, argv, envp) #ifdef QUEUE dropenvelope(CurEnv); printqueue(); + setuid(RealUid); exit(EX_OK); -#else QUEUE +#else /* QUEUE */ usrerr("No queue to print"); finis(); -#endif QUEUE +#endif /* QUEUE */ case MD_INITALIAS: /* initialize alias database */ - initaliases(AliasFile, TRUE); + initmaps(TRUE, CurEnv); + setuid(RealUid); exit(EX_OK); case MD_DAEMON: @@ -539,7 +770,7 @@ main(argc, argv, envp) default: /* open the alias database */ - initaliases(AliasFile, FALSE); + initmaps(FALSE, CurEnv); break; } @@ -554,14 +785,26 @@ main(argc, argv, envp) if (m == NULL) continue; - printf("mailer %d (%s): P=%s S=%d R=%d M=%ld F=", i, m->m_name, - m->m_mailer, m->m_s_rwset, m->m_r_rwset, - m->m_maxsize); + printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld F=", i, m->m_name, + m->m_mailer, m->m_se_rwset, m->m_sh_rwset, + m->m_re_rwset, m->m_rh_rwset, m->m_maxsize); for (j = '\0'; j <= '\177'; j++) if (bitnset(j, m->m_flags)) (void) putchar(j); printf(" E="); xputs(m->m_eol); + if (m->m_argv != NULL) + { + char **a = m->m_argv; + + printf(" A="); + while (*a != NULL) + { + if (a != m->m_argv) + printf(" "); + xputs(*a++); + } + } printf("\n"); } } @@ -570,7 +813,7 @@ main(argc, argv, envp) ** Switch to the main envelope. */ - CurEnv = newenvelope(&MainEnvelope); + CurEnv = newenvelope(&MainEnvelope, CurEnv); MainEnvelope.e_flags = BlankEnvelope.e_flags; /* @@ -581,42 +824,63 @@ main(argc, argv, envp) { char buf[MAXLINE]; - printf("ADDRESS TEST MODE\nEnter <ruleset> <address>\n"); + if (isatty(fileno(stdin))) + Verbose = TRUE; + + if (Verbose) + { + printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n"); + printf("Enter <ruleset> <address>\n"); + } for (;;) { register char **pvp; char *q; - extern char *DelimChar; + auto char *delimptr; + extern bool invalidaddr(); - printf("> "); + if (Verbose) + printf("> "); (void) fflush(stdout); if (fgets(buf, sizeof buf, stdin) == NULL) finis(); - for (p = buf; isspace(*p); p++) + if (!Verbose) + printf("> %s", buf); + if (buf[0] == '#') + continue; + for (p = buf; isascii(*p) && isspace(*p); p++) continue; q = p; - while (*p != '\0' && !isspace(*p)) + while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; if (*p == '\0') + { + printf("No address!\n"); continue; + } *p = '\0'; + if (invalidaddr(p + 1)) + continue; do { - extern char **prescan(); char pvpbuf[PSBUFSIZE]; - pvp = prescan(++p, ',', pvpbuf); + pvp = prescan(++p, ',', pvpbuf, &delimptr); if (pvp == NULL) continue; - rewrite(pvp, 3); p = q; while (*p != '\0') { - rewrite(pvp, atoi(p)); + int stat; + + stat = rewrite(pvp, atoi(p), CurEnv); + if (stat != EX_OK) + printf("== Ruleset %s status %d\n", + p, stat); while (*p != '\0' && *p++ != ',') continue; } - } while (*(p = DelimChar) != '\0'); + } while (*(p = delimptr) != '\0'); } } @@ -630,7 +894,7 @@ main(argc, argv, envp) runqueue(FALSE); finis(); } -# endif QUEUE +# endif /* QUEUE */ /* ** If a daemon, wait for a request. @@ -643,6 +907,8 @@ main(argc, argv, envp) if (OpMode == MD_DAEMON || QueueIntvl != 0) { + char dtype[200]; + if (!tTd(0, 1)) { /* put us in background */ @@ -652,12 +918,25 @@ main(argc, argv, envp) if (i != 0) exit(0); - /* get our pid right */ - MotherPid = getpid(); - /* disconnect from our controlling tty */ - disconnect(TRUE); + disconnect(TRUE, CurEnv); + } + + dtype[0] = '\0'; + if (OpMode == MD_DAEMON) + strcat(dtype, "+SMTP"); + if (QueueIntvl != 0) + { + strcat(dtype, "+queueing@"); + strcat(dtype, pintvl(QueueIntvl, TRUE)); } + if (tTd(0, 1)) + strcat(dtype, "+debugging"); + + syslog(LOG_INFO, "starting daemon (%s): %s", Version, dtype + 1); +#ifdef XLA + xla_create_file(); +#endif # ifdef QUEUE if (queuemode) @@ -667,17 +946,23 @@ main(argc, argv, envp) for (;;) pause(); } -# endif QUEUE +# endif /* QUEUE */ dropenvelope(CurEnv); #ifdef DAEMON getrequests(); /* at this point we are in a child: reset state */ - OpMode = MD_SMTP; - (void) newenvelope(CurEnv); - openxscript(CurEnv); -#endif DAEMON + (void) newenvelope(CurEnv, CurEnv); + + /* + ** Get authentication data + */ + + p = getauthinfo(fileno(InChannel)); + define('_', p, CurEnv); + +#endif /* DAEMON */ } # ifdef SMTP @@ -687,33 +972,47 @@ main(argc, argv, envp) */ if (OpMode == MD_SMTP) - smtp(); -# endif SMTP + smtp(CurEnv); +# endif /* SMTP */ /* ** Do basic system initialization and set the sender */ - initsys(); - setsender(from); + /* make sendmail immune from process group signals */ +# ifdef _POSIX_JOB_CONTROL + (void) setpgid(0, getpid()); +# else +# ifndef SYSTEM5 + (void) setpgrp(0, getpid()); +# endif +# endif + + initsys(CurEnv); + setsender(from, CurEnv, NULL, FALSE); + if (macvalue('s', CurEnv) == NULL) + define('s', RealHostName, CurEnv); - if (OpMode != MD_ARPAFTP && *av == NULL && !GrabTo) + if (*av == NULL && !GrabTo) { usrerr("Recipient names must be specified"); /* collect body for UUCP return */ if (OpMode != MD_VERIFY) - collect(FALSE); + collect(FALSE, FALSE, CurEnv); finis(); } if (OpMode == MD_VERIFY) - SendMode = SM_VERIFY; + { + CurEnv->e_sendmode = SM_VERIFY; + CurEnv->e_errormode = EM_QUIET; + } /* ** Scan argv and deliver the message to everyone. */ - sendtoargv(av); + sendtoargv(av, CurEnv); /* if we have had errors sofar, arrange a meaningful exit stat */ if (Errors > 0 && ExitStat == EX_OK) @@ -725,7 +1024,7 @@ main(argc, argv, envp) CurEnv->e_to = NULL; if (OpMode != MD_VERIFY || GrabTo) - collect(FALSE); + collect(FALSE, FALSE, CurEnv); errno = 0; /* collect statistics */ @@ -741,11 +1040,17 @@ main(argc, argv, envp) */ CurEnv->e_from.q_flags |= QDONTSEND; + if (tTd(1, 5)) + { + printf("main: QDONTSEND "); + printaddr(&CurEnv->e_from, FALSE); + } CurEnv->e_to = NULL; sendall(CurEnv, SM_DEFAULT); /* - ** All done. + ** All done. + ** Don't send return error message if in VERIFY mode. */ finis(); @@ -772,16 +1077,25 @@ finis() CurEnv->e_to = NULL; dropenvelope(CurEnv); - /* post statistics */ - poststats(StatFile); + /* flush any cached connections */ + mci_flush(TRUE, NULL); + +# ifdef XLA + /* clean up extended load average stuff */ + xla_all_end(); +# endif /* and exit */ # ifdef LOG - if (LogLevel > 11) + if (LogLevel > 78) syslog(LOG_DEBUG, "finis, pid=%d", getpid()); -# endif LOG +# endif /* LOG */ if (ExitStat == EX_TEMPFAIL) ExitStat = EX_OK; + + /* reset uid for process accounting */ + setuid(RealUid); + exit(ExitStat); } /* @@ -805,6 +1119,13 @@ intsig() { FileName = NULL; unlockqueue(CurEnv); +#ifdef XLA + xla_all_end(); +#endif + + /* reset uid for process accounting */ + setuid(RealUid); + exit(EX_OK); } /* @@ -823,26 +1144,25 @@ intsig() ** initializes several macros to be themselves. */ -struct metamac -{ - char metaname; - char metaval; -}; - struct metamac MetaMacros[] = { /* LHS pattern matching characters */ - '*', MATCHZANY, '+', MATCHANY, '-', MATCHONE, '=', MATCHCLASS, - '~', MATCHNCLASS, + '*', MATCHZANY, '+', MATCHANY, '-', MATCHONE, + '=', MATCHCLASS, '~', MATCHNCLASS, /* these are RHS metasymbols */ - '#', CANONNET, '@', CANONHOST, ':', CANONUSER, '>', CALLSUBR, + '#', CANONNET, '@', CANONHOST, ':', CANONUSER, + '>', CALLSUBR, /* the conditional operations */ - '?', CONDIF, '|', CONDELSE, '.', CONDFI, + '?', CONDIF, '|', CONDELSE, '.', CONDFI, - /* and finally the hostname lookup characters */ - '[', HOSTBEGIN, ']', HOSTEND, + /* the hostname lookup characters */ + '[', HOSTBEGIN, ']', HOSTEND, + '(', LOOKUPBEGIN, ')', LOOKUPEND, + + /* miscellaneous control characters */ + '&', MACRODEXPAND, '\0' }; @@ -882,6 +1202,8 @@ initmacros() ** Writes BSS and malloc'ed memory to freezefile */ +# ifdef FROZENCONFIG + union frz { char frzpad[BUFSIZ]; /* insure we are on a BUFSIZ boundary */ @@ -895,13 +1217,21 @@ union frz } frzinfo; }; +#if defined(__hpux) || defined(__alpha) +#define BRK_TYPE int +#define SBRK_TYPE void * +#else +#define BRK_TYPE char * +#define SBRK_TYPE char * +#endif + freeze(freezefile) char *freezefile; { int f; union frz fhdr; + extern SBRK_TYPE sbrk(); extern char edata, end; - extern char *sbrk(); extern char Version[]; if (freezefile == NULL) @@ -939,6 +1269,7 @@ freeze(freezefile) ** ** Parameters: ** freezefile -- the name of the file to thaw from. +** binfile -- the name of the sendmail binary (ok to guess). ** ** Returns: ** TRUE if it successfully read the freeze file. @@ -948,14 +1279,19 @@ freeze(freezefile) ** reads freezefile in to BSS area. */ -thaw(freezefile) +thaw(freezefile, binfile) char *freezefile; + char *binfile; { int f; + register char *p; union frz fhdr; + char hbuf[60]; + struct stat fst, sst; extern char edata, end; extern char Version[]; - extern caddr_t brk(); + extern char **myhostname(); + extern BRK_TYPE brk(); if (freezefile == NULL) return (FALSE); @@ -964,30 +1300,45 @@ thaw(freezefile) f = open(freezefile, 0); if (f < 0) { - syslog(LOG_WARNING, "Cannot open frozen config file %s: %m", - freezefile); errno = 0; return (FALSE); } + if (fstat(f, &fst) < 0 || stat(ConfFile, &sst) < 0 || + fst.st_mtime < sst.st_mtime) + { + syslog(LOG_WARNING, "Freeze file older than config file"); + (void) close(f); + return (FALSE); + } + + if (strchr(binfile, '/') != NULL && stat(binfile, &sst) == 0 && + fst.st_mtime < sst.st_mtime) + { + syslog(LOG_WARNING, "Freeze file older than binary file"); + (void) close(f); + return (FALSE); + } + /* read in the header */ if (read(f, (char *) &fhdr, sizeof fhdr) < sizeof fhdr) { - syserr("Cannot read frozen config file"); + syslog(LOG_WARNING, "Cannot read frozen config file"); (void) close(f); return (FALSE); } - if ( fhdr.frzinfo.frzedata != &edata || + if (fhdr.frzinfo.frzedata != &edata || fhdr.frzinfo.frzend != &end || strcmp(fhdr.frzinfo.frzver, Version) != 0) { + fprintf(stderr, "Wrong version of frozen config file\n"); syslog(LOG_WARNING, "Wrong version of frozen config file"); (void) close(f); return (FALSE); } /* arrange to have enough space */ - if (brk(fhdr.frzinfo.frzbrk) == (caddr_t) -1) + if (brk(fhdr.frzinfo.frzbrk) == (BRK_TYPE) -1) { syserr("Cannot break to %x", fhdr.frzinfo.frzbrk); (void) close(f); @@ -1005,8 +1356,20 @@ thaw(freezefile) } (void) close(f); - return (TRUE); + + /* verify that the host name was correct on the freeze */ + (void) myhostname(hbuf, sizeof hbuf); + p = macvalue('w', CurEnv); + if (p == NULL) + p = ""; + if (strcmp(hbuf, macvalue('w', CurEnv)) == 0) + return (TRUE); + syslog(LOG_WARNING, "Hostname changed since freeze (%s => %s)", + p, hbuf); + return (FALSE); } + +# endif /* FROZENCONFIG */ /* ** DISCONNECT -- remove our connection with any foreground process ** @@ -1025,14 +1388,15 @@ thaw(freezefile) ** the controlling tty. */ -disconnect(fulldrop) +disconnect(fulldrop, e) bool fulldrop; + register ENVELOPE *e; { int fd; if (tTd(52, 1)) - printf("disconnect: In %d Out %d\n", fileno(InChannel), - fileno(OutChannel)); + printf("disconnect: In %d Out %d, e=%x\n", + fileno(InChannel), fileno(OutChannel), e); if (tTd(52, 5)) { printf("don't\n"); @@ -1046,7 +1410,7 @@ disconnect(fulldrop) /* we can't communicate with our caller, so.... */ HoldErrs = TRUE; - ErrorMode = EM_MAIL; + CurEnv->e_errormode = EM_MAIL; Verbose = FALSE; /* all input from /dev/null */ @@ -1063,20 +1427,20 @@ disconnect(fulldrop) (void) fclose(OutChannel); OutChannel = stdout; } - if (CurEnv->e_xfp == NULL) - CurEnv->e_xfp = fopen("/dev/null", "w"); + if (e->e_xfp == NULL) + fd = open("/dev/null", O_WRONLY, 0666); + else + fd = fileno(e->e_xfp); (void) fflush(stdout); - (void) close(1); - (void) close(2); - while ((fd = dup(fileno(CurEnv->e_xfp))) < 2 && fd > 0) - continue; + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (e->e_xfp == NULL) + close(fd); /* drop our controlling TTY completely if possible */ if (fulldrop) { -#if BSD > 43 - daemon(1, 1); -#else + (void) setsid(); #ifdef TIOCNOTTY fd = open("/dev/tty", 2); if (fd >= 0) @@ -1086,14 +1450,90 @@ disconnect(fulldrop) } (void) setpgrp(0, 0); #endif /* TIOCNOTTY */ -#endif /* BSD */ errno = 0; } # ifdef LOG - if (LogLevel > 11) + if (LogLevel > 71) syslog(LOG_DEBUG, "in background, pid=%d", getpid()); -# endif LOG +# endif /* LOG */ errno = 0; } + +static void +obsolete(argv) + char *argv[]; +{ + char *ap; + + while ((ap = *++argv) != NULL) + { + /* Return if "--" or not an option of any form. */ + if (ap[0] != '-' || ap[1] == '-') + return; + + /* If -C doesn't have an argument, use sendmail.cf. */ +#define __DEFPATH "sendmail.cf" + if (ap[1] == 'C' && ap[2] == '\0' && + (argv[1] == NULL || argv[1][0] == '-')) + { + *argv = xalloc(sizeof(__DEFPATH) + 2); + argv[0][0] = '-'; + argv[0][1] = 'C'; + (void)strcpy(&argv[0][2], __DEFPATH); + } + + /* If -q doesn't have an argument, run it once. */ + if (ap[1] == 'q' && ap[2] == '\0' && + (argv[1] == NULL || argv[1][0] == '-')) + *argv = "-q0"; + + /* if -d doesn't have an argument, use 0-99.1 */ + if (ap[1] == 'd' && ap[2] == '\0' && + (argv[1] == NULL || !isdigit(argv[1][0]))) + *argv = "-d0-99.1"; + } +} + /* +** AUTH_WARNING -- specify authorization warning +** +** Parameters: +** e -- the current envelope. +** msg -- the text of the message. +** args -- arguments to the message. +** +** Returns: +** none. +*/ + +void +#ifdef __STDC__ +auth_warning(register ENVELOPE *e, const char *msg, ...) +#else +auth_warning(e, msg, va_alist) + register ENVELOPE *e; + const char *msg; + va_dcl +#endif +{ + char buf[MAXLINE]; + VA_LOCAL_DECL + + if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags)) + { + register char *p; + static char hostbuf[48]; + extern char **myhostname(); + + if (hostbuf[0] == '\0') + (void) myhostname(hostbuf, sizeof hostbuf); + + (void) sprintf(buf, "%s: ", hostbuf); + p = &buf[strlen(buf)]; + VA_START(msg); + vsprintf(p, msg, ap); + VA_END; + addheader("X-Authentication-Warning", buf, e); + } +} diff --git a/usr.sbin/sendmail/src/map.c b/usr.sbin/sendmail/src/map.c new file mode 100644 index 0000000000..bfa2dc03c2 --- /dev/null +++ b/usr.sbin/sendmail/src/map.c @@ -0,0 +1,1192 @@ +/* + * Copyright (c) 1992 Eric P. Allman. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)map.c 8.2 (Berkeley) 7/11/93"; +#endif /* not lint */ + +#include "sendmail.h" + +#ifdef NDBM +#include <ndbm.h> +#endif +#ifdef NEWDB +#include <db.h> +#endif +#ifdef NIS +#include <rpcsvc/ypclnt.h> +#endif + +/* +** MAP.C -- implementations for various map classes. +** +** Each map class implements a series of functions: +** +** bool map_parse(MAP *map, char *args) +** Parse the arguments from the config file. Return TRUE +** if they were ok, FALSE otherwise. Fill in map with the +** values. +** +** char *map_lookup(MAP *map, char *key, char **args, int *pstat) +** Look up the key in the given map. If found, do any +** rewriting the map wants (including "args" if desired) +** and return the value. Set *pstat to the appropriate status +** on error and return NULL. Args will be NULL if called +** from the alias routines, although this should probably +** not be relied upon. It is suggested you call map_rewrite +** to return the results -- it takes care of null termination +** and uses a dynamically expanded buffer as needed. +** +** void map_store(MAP *map, char *key, char *value) +** Store the key:value pair in the map. +** +** bool map_open(MAP *map, int mode) +** Open the map for the indicated mode. Mode should +** be either O_RDONLY or O_RDWR. Return TRUE if it +** was opened successfully, FALSE otherwise. If the open +** failed an the MF_OPTIONAL flag is not set, it should +** also print an error. If the MF_ALIAS bit is set +** and this map class understands the @:@ convention, it +** should call aliaswait() before returning. +** +** void map_close(MAP *map) +** Close the map. +*/ + +#define DBMMODE 0644 + /* +** MAP_PARSEARGS -- parse config line arguments for database lookup +** +** This is a generic version of the map_parse method. +** +** Parameters: +** map -- the map being initialized. +** ap -- a pointer to the args on the config line. +** +** Returns: +** TRUE -- if everything parsed OK. +** FALSE -- otherwise. +** +** Side Effects: +** null terminates the filename; stores it in map +*/ + +bool +map_parseargs(map, ap) + MAP *map; + char *ap; +{ + register char *p = ap; + + map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL; + for (;;) + { + while (isascii(*p) && isspace(*p)) + p++; + if (*p != '-') + break; + switch (*++p) + { + case 'N': + map->map_mflags |= MF_INCLNULL; + map->map_mflags &= ~MF_TRY0NULL; + break; + + case 'O': + map->map_mflags &= ~MF_TRY1NULL; + break; + + case 'o': + map->map_mflags |= MF_OPTIONAL; + break; + + case 'f': + map->map_mflags |= MF_NOFOLDCASE; + break; + + case 'm': + map->map_mflags |= MF_MATCHONLY; + break; + + case 'a': + map->map_app = ++p; + break; + } + while (*p != '\0' && !(isascii(*p) && isspace(*p))) + p++; + if (*p != '\0') + *p++ = '\0'; + } + if (map->map_app != NULL) + map->map_app = newstr(map->map_app); + + if (*p != '\0') + { + map->map_file = p; + while (*p != '\0' && !(isascii(*p) && isspace(*p))) + p++; + if (*p != '\0') + *p++ = '\0'; + map->map_file = newstr(map->map_file); + } + + while (*p != '\0' && isascii(*p) && isspace(*p)) + p++; + if (*p != '\0') + map->map_rebuild = newstr(p); + + if (map->map_file == NULL) + { + syserr("No file name for %s map %s", + map->map_class->map_cname, map->map_mname); + return FALSE; + } + return TRUE; +} + /* +** MAP_REWRITE -- rewrite a database key, interpolating %n indications. +** +** It also adds the map_app string. It can be used as a utility +** in the map_lookup method. +** +** Parameters: +** map -- the map that causes this. +** s -- the string to rewrite, NOT necessarily null terminated. +** slen -- the length of s. +** av -- arguments to interpolate into buf. +** +** Returns: +** Pointer to rewritten result. +** +** Side Effects: +** none. +*/ + +struct rwbuf +{ + int rwb_len; /* size of buffer */ + char *rwb_buf; /* ptr to buffer */ +}; + +struct rwbuf RwBufs[2]; /* buffers for rewriting output */ + +char * +map_rewrite(map, s, slen, av) + register MAP *map; + register char *s; + int slen; + char **av; +{ + register char *bp; + register char c; + char **avp; + register char *ap; + register struct rwbuf *rwb; + int i; + int len; + + if (tTd(39, 1)) + { + printf("map_rewrite(%.*s), av =", slen, s); + if (av == NULL) + printf(" (nullv)"); + else + { + for (avp = av; *avp != NULL; avp++) + printf("\n\t%s", *avp); + } + printf("\n"); + } + + rwb = RwBufs; + if (av == NULL) + rwb++; + + /* count expected size of output (can safely overestimate) */ + i = len = slen; + if (av != NULL) + { + bp = s; + for (i = slen; --i >= 0 && (c = *bp++) != 0; ) + { + if (c != '%') + continue; + if (--i < 0) + break; + c = *bp++; + if (!(isascii(c) && isdigit(c))) + continue; + c -= 0; + for (avp = av; --c >= 0 && *avp != NULL; avp++) + continue; + if (*avp == NULL) + continue; + len += strlen(*avp); + } + } + if (map->map_app != NULL) + len += strlen(map->map_app); + if (rwb->rwb_len < ++len) + { + /* need to malloc additional space */ + rwb->rwb_len = len; + if (rwb->rwb_buf != NULL) + free(rwb->rwb_buf); + rwb->rwb_buf = xalloc(rwb->rwb_len); + } + + bp = rwb->rwb_buf; + if (av == NULL) + { + bcopy(s, bp, slen); + bp += slen; + } + else + { + while (--slen >= 0 && (c = *s++) != '\0') + { + if (c != '%') + { + pushc: + *bp++ = c; + continue; + } + if (--slen < 0 || (c = *s++) == '\0') + c = '%'; + if (c == '%') + goto pushc; + if (!(isascii(c) && isdigit(c))) + { + *bp++ = '%'; + goto pushc; + } + c -= '0'; + for (avp = av; --c >= 0 && *avp != NULL; avp++) + continue; + if (*avp == NULL) + continue; + + /* transliterate argument into output string */ + for (ap = *avp; (c = *ap++) != '\0'; ) + *bp++ = c; + } + } + if (map->map_app != NULL) + strcpy(bp, map->map_app); + else + *bp = '\0'; + if (tTd(39, 1)) + printf("map_rewrite => %s\n", rwb->rwb_buf); + return rwb->rwb_buf; +} + /* +** INITMAPS -- initialize for aliasing +** +** Parameters: +** rebuild -- if TRUE, this rebuilds the cached versions. +** e -- current envelope. +** +** Returns: +** none. +** +** Side Effects: +** initializes aliases: +** if NDBM: opens the database. +** if ~NDBM: reads the aliases into the symbol table. +*/ + +initmaps(rebuild, e) + bool rebuild; + register ENVELOPE *e; +{ + extern void map_init(); + + CurEnv = e; + stabapply(map_init, rebuild); +} + +void +map_init(s, rebuild) + register STAB *s; + int rebuild; +{ + register MAP *map; + + /* has to be a map */ + if (s->s_type != ST_MAP) + return; + + map = &s->s_map; + if (!bitset(MF_VALID, map->map_mflags)) + return; + + if (tTd(38, 2)) + printf("map_init(%s:%s)\n", + map->map_class->map_cname, map->map_file); + + /* if already open, close it (for nested open) */ + if (bitset(MF_OPEN, map->map_mflags)) + { + map->map_class->map_close(map); + map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); + } + + if (rebuild) + { + if (bitset(MF_ALIAS, map->map_mflags) && + bitset(MCF_REBUILDABLE, map->map_class->map_cflags)) + rebuildaliases(map, FALSE); + } + else + { + if (map->map_class->map_open(map, O_RDONLY)) + { + if (tTd(38, 4)) + printf("%s:%s: valid\n", + map->map_class->map_cname, + map->map_file); + map->map_mflags |= MF_OPEN; + } + else if (tTd(38, 4)) + printf("%s:%s: invalid: %s\n", + map->map_class->map_cname, + map->map_file, + errstring(errno)); + } +} + /* +** NDBM modules +*/ + +#ifdef NDBM + +/* +** DBM_MAP_OPEN -- DBM-style map open +*/ + +bool +ndbm_map_open(map, mode) + MAP *map; + int mode; +{ + DBM *dbm; + + if (tTd(38, 2)) + printf("ndbm_map_open(%s, %d)\n", map->map_file, mode); + + if (mode == O_RDWR) + mode |= O_CREAT|O_TRUNC; + + /* open the database */ + dbm = dbm_open(map->map_file, mode, DBMMODE); + if (dbm == NULL) + { + if (!bitset(MF_OPTIONAL, map->map_mflags)) + syserr("Cannot open DBM database %s", map->map_file); + return FALSE; + } + map->map_db1 = (void *) dbm; + if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags)) + aliaswait(map, ".pag"); + return TRUE; +} + + +/* +** DBM_MAP_LOOKUP -- look up a datum in a DBM-type map +*/ + +char * +ndbm_map_lookup(map, name, av, statp) + MAP *map; + char *name; + char **av; + int *statp; +{ + datum key, val; + char keybuf[MAXNAME + 1]; + + if (tTd(38, 20)) + printf("ndbm_map_lookup(%s)\n", name); + + key.dptr = name; + key.dsize = strlen(name); + if (!bitset(MF_NOFOLDCASE, map->map_mflags)) + { + if (key.dsize > sizeof keybuf - 1) + key.dsize = sizeof keybuf - 1; + bcopy(key.dptr, keybuf, key.dsize + 1); + makelower(keybuf); + key.dptr = keybuf; + } + (void) lockfile(dbm_dirfno((DBM *) map->map_db1), map->map_file, LOCK_SH); + val.dptr = NULL; + if (bitset(MF_TRY0NULL, map->map_mflags)) + { + val = dbm_fetch((DBM *) map->map_db1, key); + if (val.dptr != NULL) + map->map_mflags &= ~MF_TRY1NULL; + } + if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags)) + { + key.dsize++; + val = dbm_fetch((DBM *) map->map_db1, key); + if (val.dptr != NULL) + map->map_mflags &= ~MF_TRY0NULL; + } + (void) lockfile(dbm_dirfno((DBM *) map->map_db1), map->map_file, LOCK_UN); + if (val.dptr == NULL) + return NULL; + if (bitset(MF_MATCHONLY, map->map_mflags)) + return map_rewrite(map, name, strlen(name), NULL); + else + return map_rewrite(map, val.dptr, val.dsize, av); +} + + +/* +** DBM_MAP_STORE -- store a datum in the database +*/ + +void +ndbm_map_store(map, lhs, rhs) + register MAP *map; + char *lhs; + char *rhs; +{ + datum key; + datum data; + int stat; + + if (tTd(38, 12)) + printf("ndbm_map_store(%s, %s)\n", lhs, rhs); + + key.dsize = strlen(lhs); + key.dptr = lhs; + + data.dsize = strlen(rhs); + data.dptr = rhs; + + if (bitset(MF_INCLNULL, map->map_mflags)) + { + key.dsize++; + data.dsize++; + } + + stat = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT); + if (stat > 0) + { + usrerr("050 Warning: duplicate alias name %s", lhs); + stat = dbm_store((DBM *) map->map_db1, key, data, DBM_REPLACE); + } + if (stat != 0) + syserr("readaliases: dbm put (%s)", lhs); +} + + +/* +** NDBM_MAP_CLOSE -- close the database +*/ + +void +ndbm_map_close(map) + register MAP *map; +{ + if (bitset(MF_WRITABLE, map->map_mflags)) + { +#ifdef YPCOMPAT + char buf[200]; + + (void) sprintf(buf, "%010ld", curtime()); + ndbm_map_store(map, "YP_LAST_MODIFIED", buf); + + (void) myhostname(buf, sizeof buf); + ndbm_map_store(map, "YP_MASTER_NAME", buf); +#endif + + /* write out the distinguished alias */ + ndbm_map_store(map, "@", "@"); + } + dbm_close((DBM *) map->map_db1); +} + +#endif + /* +** NEWDB (Hash and BTree) Modules +*/ + +#ifdef NEWDB + +/* +** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives. +** +** These do rather bizarre locking. If you can lock on open, +** do that to avoid the condition of opening a database that +** is being rebuilt. If you don't, we'll try to fake it, but +** there will be a race condition. If opening for read-only, +** we immediately release the lock to avoid freezing things up. +** We really ought to hold the lock, but guarantee that we won't +** be pokey about it. That's hard to do. +*/ + +bool +bt_map_open(map, mode) + MAP *map; + int mode; +{ + DB *db; + int i; + int omode; + char buf[MAXNAME]; + + if (tTd(38, 2)) + printf("bt_map_open(%s, %d)\n", map->map_file, mode); + + omode = mode; + if (omode == O_RDWR) + { + omode |= O_CREAT|O_TRUNC; +#if defined(O_EXLOCK) && !defined(LOCKF) + omode |= O_EXLOCK; +# if !defined(OLD_NEWDB) + } + else + { + omode |= O_SHLOCK; +# endif +#endif + } + + (void) strcpy(buf, map->map_file); + i = strlen(buf); + if (i < 3 || strcmp(&buf[i - 3], ".db") != 0) + (void) strcat(buf, ".db"); + db = dbopen(buf, omode, DBMMODE, DB_BTREE, NULL); + if (db == NULL) + { + if (!bitset(MF_OPTIONAL, map->map_mflags)) + syserr("Cannot open BTREE database %s", map->map_file); + return FALSE; + } +#if !defined(OLD_NEWDB) && !defined(LOCKF) +# if !defined(O_EXLOCK) + if (mode == O_RDWR) + (void) lockfile(db->fd(db), map->map_file, LOCK_EX); +# else + if (mode == O_RDONLY) + (void) lockfile(db->fd(db), map->map_file, LOCK_UN); +# endif +#endif + + /* try to make sure that at least the database header is on disk */ + if (mode == O_RDWR) + (void) db->sync(db, 0); + + map->map_db2 = (void *) db; + if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags)) + aliaswait(map, ".db"); + return TRUE; +} + + +/* +** HASH_MAP_INIT -- HASH-style map initialization +*/ + +bool +hash_map_open(map, mode) + MAP *map; + int mode; +{ + DB *db; + int i; + int omode; + char buf[MAXNAME]; + + if (tTd(38, 2)) + printf("hash_map_open(%s, %d)\n", map->map_file, mode); + + omode = mode; + if (omode == O_RDWR) + { + omode |= O_CREAT|O_TRUNC; +#if defined(O_EXLOCK) && !defined(LOCKF) + omode |= O_EXLOCK; +# if !defined(OLD_NEWDB) + } + else + { + omode |= O_SHLOCK; +# endif +#endif + } + + (void) strcpy(buf, map->map_file); + i = strlen(buf); + if (i < 3 || strcmp(&buf[i - 3], ".db") != 0) + (void) strcat(buf, ".db"); + db = dbopen(buf, omode, DBMMODE, DB_HASH, NULL); + if (db == NULL) + { + if (!bitset(MF_OPTIONAL, map->map_mflags)) + syserr("Cannot open HASH database %s", map->map_file); + return FALSE; + } +#if !defined(OLD_NEWDB) && !defined(LOCKF) +# if !defined(O_EXLOCK) + if (mode == O_RDWR) + (void) lockfile(db->fd(db), map->map_file, LOCK_EX); +# else + if (mode == O_RDONLY) + (void) lockfile(db->fd(db), map->map_file, LOCK_UN); +# endif +#endif + + /* try to make sure that at least the database header is on disk */ + if (mode == O_RDWR) + (void) db->sync(db, 0); + + map->map_db2 = (void *) db; + if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags)) + aliaswait(map, ".db"); + return TRUE; +} + + +/* +** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map +*/ + +char * +db_map_lookup(map, name, av, statp) + MAP *map; + char *name; + char **av; + int *statp; +{ + DBT key, val; + register DB *db = (DB *) map->map_db2; + int st; + int saveerrno; + char keybuf[MAXNAME + 1]; + + if (tTd(38, 20)) + printf("db_map_lookup(%s)\n", name); + + key.size = strlen(name); + if (key.size > sizeof keybuf - 1) + key.size = sizeof keybuf - 1; + key.data = keybuf; + bcopy(name, keybuf, key.size + 1); + if (!bitset(MF_NOFOLDCASE, map->map_mflags)) + makelower(keybuf); +#ifndef OLD_NEWDB + (void) lockfile(db->fd(db), map->map_file, LOCK_SH); +#endif + st = 1; + if (bitset(MF_TRY0NULL, map->map_mflags)) + { + st = db->get(db, &key, &val, 0); + if (st == 0) + map->map_mflags &= ~MF_TRY1NULL; + } + if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags)) + { + key.size++; + st = db->get(db, &key, &val, 0); + if (st == 0) + map->map_mflags &= ~MF_TRY0NULL; + } + saveerrno = errno; +#ifndef OLD_NEWDB + (void) lockfile(db->fd(db), map->map_file, LOCK_UN); +#endif + if (st != 0) + { + errno = saveerrno; + if (st < 0) + syserr("db_map_lookup: get (%s)", name); + return NULL; + } + if (bitset(MF_MATCHONLY, map->map_mflags)) + return map_rewrite(map, name, strlen(name), NULL); + else + return map_rewrite(map, val.data, val.size, av); +} + + +/* +** DB_MAP_STORE -- store a datum in the NEWDB database +*/ + +void +db_map_store(map, lhs, rhs) + register MAP *map; + char *lhs; + char *rhs; +{ + int stat; + DBT key; + DBT data; + register DB *db = map->map_db2; + + if (tTd(38, 20)) + printf("db_map_store(%s, %s)\n", lhs, rhs); + + key.size = strlen(lhs); + key.data = lhs; + + data.size = strlen(rhs); + data.data = rhs; + + if (bitset(MF_INCLNULL, map->map_mflags)) + { + key.size++; + data.size++; + } + + stat = db->put(db, &key, &data, R_NOOVERWRITE); + if (stat > 0) + { + usrerr("050 Warning: duplicate alias name %s", lhs); + stat = db->put(db, &key, &data, 0); + } + if (stat != 0) + syserr("readaliases: db put (%s)", lhs); +} + + +/* +** DB_MAP_CLOSE -- add distinguished entries and close the database +*/ + +void +db_map_close(map) + MAP *map; +{ + register DB *db = map->map_db2; + + if (tTd(38, 9)) + printf("db_map_close(%s, %x)\n", map->map_file, map->map_mflags); + + if (bitset(MF_WRITABLE, map->map_mflags)) + { + /* write out the distinguished alias */ + db_map_store(map, "@", "@"); + } + + if (db->close(db) != 0) + syserr("readaliases: db close failure"); +} + +#endif + /* +** NIS Modules +*/ + +# ifdef NIS + +/* +** NIS_MAP_OPEN -- open DBM map +*/ + +bool +nis_map_open(map, mode) + MAP *map; + int mode; +{ + int yperr; + register char *p; + auto char *vp; + auto int vsize; + char *master; + + if (tTd(38, 2)) + printf("nis_map_open(%s)\n", map->map_file); + + if (mode != O_RDONLY) + { + errno = ENODEV; + return FALSE; + } + + p = strchr(map->map_file, '@'); + if (p != NULL) + { + *p++ = '\0'; + if (*p != '\0') + map->map_domain = p; + } + + if (map->map_domain == NULL) + yp_get_default_domain(&map->map_domain); + + if (*map->map_file == '\0') + map->map_file = "mail.aliases"; + + /* check to see if this map actually exists */ + yperr = yp_match(map->map_domain, map->map_file, "@", 1, + &vp, &vsize); + if (tTd(38, 10)) + printf("nis_map_open: yp_match(%s, %s) => %s\n", + map->map_domain, map->map_file, yperr_string(yperr)); + if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY) + return TRUE; + + if (!bitset(MF_OPTIONAL, map->map_mflags)) + syserr("Cannot bind to domain %s: %s", map->map_domain, + yperr_string(yperr)); + + return FALSE; +} + + +/* +** NIS_MAP_LOOKUP -- look up a datum in a NIS map +*/ + +char * +nis_map_lookup(map, name, av, statp) + MAP *map; + char *name; + char **av; + int *statp; +{ + char *vp; + auto int vsize; + int buflen; + int yperr; + char keybuf[MAXNAME + 1]; + + if (tTd(38, 20)) + printf("nis_map_lookup(%s)\n", name); + + buflen = strlen(name); + if (buflen > sizeof keybuf - 1) + buflen = sizeof keybuf - 1; + bcopy(name, keybuf, buflen + 1); + if (!bitset(MF_NOFOLDCASE, map->map_mflags)) + makelower(keybuf); + yperr = YPERR_KEY; + if (bitset(MF_TRY0NULL, map->map_mflags)) + { + yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen, + &vp, &vsize); + if (yperr == 0) + map->map_mflags &= ~MF_TRY1NULL; + } + if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags)) + { + buflen++; + yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen, + &vp, &vsize); + if (yperr == 0) + map->map_mflags &= ~MF_TRY0NULL; + } + if (yperr != 0) + { + if (yperr != YPERR_KEY && yperr != YPERR_BUSY) + map->map_mflags &= ~(MF_VALID|MF_OPEN); + return NULL; + } + if (bitset(MF_MATCHONLY, map->map_mflags)) + return map_rewrite(map, name, strlen(name), NULL); + else + return map_rewrite(map, vp, vsize, av); +} + + +/* +** NIS_MAP_STORE +*/ + +void +nis_map_store(map, lhs, rhs) + MAP *map; + char *lhs; + char *rhs; +{ + /* nothing */ +} + + +/* +** NIS_MAP_CLOSE +*/ + +void +nis_map_close(map) + MAP *map; +{ + /* nothing */ +} + +#endif /* NIS */ + /* +** STAB (Symbol Table) Modules +*/ + + +/* +** STAB_MAP_LOOKUP -- look up alias in symbol table +*/ + +char * +stab_map_lookup(map, name, av, pstat) + register MAP *map; + char *name; + char **av; + int *pstat; +{ + register STAB *s; + + if (tTd(38, 20)) + printf("stab_lookup(%s)\n", name); + + s = stab(name, ST_ALIAS, ST_FIND); + if (s != NULL) + return (s->s_alias); + return (NULL); +} + + +/* +** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild) +*/ + +void +stab_map_store(map, lhs, rhs) + register MAP *map; + char *lhs; + char *rhs; +{ + register STAB *s; + + s = stab(lhs, ST_ALIAS, ST_ENTER); + s->s_alias = newstr(rhs); +} + + +/* +** STAB_MAP_OPEN -- initialize (reads data file) +** +** This is a wierd case -- it is only intended as a fallback for +** aliases. For this reason, opens for write (only during a +** "newaliases") always fails, and opens for read open the +** actual underlying text file instead of the database. +*/ + +bool +stab_map_open(map, mode) + register MAP *map; + int mode; +{ + if (tTd(38, 2)) + printf("stab_map_open(%s)\n", map->map_file); + + if (mode != O_RDONLY) + { + errno = ENODEV; + return FALSE; + } + + return TRUE; +} + + +/* +** STAB_MAP_CLOSE -- close symbol table (???) +*/ + +void +stab_map_close(map) + MAP *map; +{ + /* ignore it */ +} + /* +** Implicit Modules +** +** Tries several types. For back compatibility of aliases. +*/ + + +/* +** IMPL_MAP_LOOKUP -- lookup in best open database +*/ + +char * +impl_map_lookup(map, name, av, pstat) + MAP *map; + char *name; + char **av; + int *pstat; +{ + if (tTd(38, 20)) + printf("impl_map_lookup(%s)\n", name); + +#ifdef NEWDB + if (bitset(MF_IMPL_HASH, map->map_mflags)) + return db_map_lookup(map, name, av, pstat); +#endif +#ifdef NDBM + if (bitset(MF_IMPL_NDBM, map->map_mflags)) + return ndbm_map_lookup(map, name, av, pstat); +#endif + return stab_map_lookup(map, name, av, pstat); +} + +/* +** IMPL_MAP_STORE -- store in open databases +*/ + +void +impl_map_store(map, lhs, rhs) + MAP *map; + char *lhs; + char *rhs; +{ +#ifdef NEWDB + if (bitset(MF_IMPL_HASH, map->map_mflags)) + db_map_store(map, lhs, rhs); +#endif +#ifdef NDBM + if (bitset(MF_IMPL_NDBM, map->map_mflags)) + ndbm_map_store(map, lhs, rhs); +#endif + stab_map_store(map, lhs, rhs); +} + +/* +** IMPL_MAP_OPEN -- implicit database open +*/ + +bool +impl_map_open(map, mode) + MAP *map; + int mode; +{ + struct stat stb; + + if (tTd(38, 2)) + printf("impl_map_open(%s)\n", map->map_file); + + if (stat(map->map_file, &stb) < 0) + { + /* no alias file at all */ + return FALSE; + } + +#ifdef NEWDB + map->map_mflags |= MF_IMPL_HASH; + if (hash_map_open(map, mode)) + { +#if defined(NDBM) && defined(YPCOMPAT) + if (mode == O_RDONLY || access("/var/yp/Makefile", R_OK) != 0) +#endif + return TRUE; + } + else + map->map_mflags &= ~MF_IMPL_HASH; +#endif +#ifdef NDBM + map->map_mflags |= MF_IMPL_NDBM; + if (ndbm_map_open(map, mode)) + { + return TRUE; + } + else + map->map_mflags &= ~MF_IMPL_NDBM; +#endif + +#if !defined(NEWDB) && !defined(NDBM) + if (Verbose) + message("WARNING: cannot open alias database %s", map->map_file); +#endif + + return stab_map_open(map, mode); +} + + +/* +** IMPL_MAP_CLOSE -- close any open database(s) +*/ + +void +impl_map_close(map) + MAP *map; +{ +#ifdef NEWDB + if (bitset(MF_IMPL_HASH, map->map_mflags)) + { + db_map_close(map); + map->map_mflags &= ~MF_IMPL_HASH; + } +#endif + +#ifdef NDBM + if (bitset(MF_IMPL_NDBM, map->map_mflags)) + { + ndbm_map_close(map); + map->map_mflags &= ~MF_IMPL_NDBM; + } +#endif +} + /* +** NULL stubs +*/ + +bool +null_map_open(map, mode) + MAP *map; + int mode; +{ + return TRUE; +} + +void +null_map_close(map) + MAP *map; +{ + return; +} + +void +null_map_store(map, key, val) + MAP *map; + char *key; + char *val; +{ + return; +} diff --git a/usr.sbin/sendmail/src/mci.c b/usr.sbin/sendmail/src/mci.c new file mode 100644 index 0000000000..0d55a665a6 --- /dev/null +++ b/usr.sbin/sendmail/src/mci.c @@ -0,0 +1,319 @@ +/* + * Copyright (c) 1983 Eric P. Allman + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)mci.c 8.2 (Berkeley) 7/11/93"; +#endif /* not lint */ + +#include "sendmail.h" + +/* +** Mail Connection Information (MCI) Caching Module. +** +** There are actually two separate things cached. The first is +** the set of all open connections -- these are stored in a +** (small) list. The second is stored in the symbol table; it +** has the overall status for all hosts, whether or not there +** is a connection open currently. +** +** There should never be too many connections open (since this +** could flood the socket table), nor should a connection be +** allowed to sit idly for too long. +** +** MaxMciCache is the maximum number of open connections that +** will be supported. +** +** MciCacheTimeout is the time (in seconds) that a connection +** is permitted to survive without activity. +** +** We actually try any cached connections by sending a NOOP +** before we use them; if the NOOP fails we close down the +** connection and reopen it. Note that this means that a +** server SMTP that doesn't support NOOP will hose the +** algorithm -- but that doesn't seem too likely. +*/ + +MCI **MciCache; /* the open connection cache */ + /* +** MCI_CACHE -- enter a connection structure into the open connection cache +** +** This may cause something else to be flushed. +** +** Parameters: +** mci -- the connection to cache. +** +** Returns: +** none. +*/ + +mci_cache(mci) + register MCI *mci; +{ + register MCI **mcislot; + extern MCI **mci_scan(); + + if (MaxMciCache <= 0) + { + /* we don't support caching */ + return; + } + + /* + ** Find the best slot. This may cause expired connections + ** to be closed. + */ + + mcislot = mci_scan(mci); + + /* if this is already cached, we are done */ + if (bitset(MCIF_CACHED, mci->mci_flags)) + return; + + /* otherwise we may have to clear the slot */ + if (*mcislot != NULL) + mci_uncache(mcislot, TRUE); + + *mcislot = mci; + mci->mci_flags |= MCIF_CACHED; +} + /* +** MCI_SCAN -- scan the cache, flush junk, and return best slot +** +** Parameters: +** savemci -- never flush this one. Can be null. +** +** Returns: +** The LRU (or empty) slot. +*/ + +MCI ** +mci_scan(savemci) + MCI *savemci; +{ + time_t now; + register MCI **bestmci; + register MCI *mci; + register int i; + + if (MciCache == NULL) + { + /* first call */ + MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache); + bzero((char *) MciCache, MaxMciCache * sizeof *MciCache); + return (&MciCache[0]); + } + + now = curtime(); + bestmci = &MciCache[0]; + for (i = 0; i < MaxMciCache; i++) + { + mci = MciCache[i]; + if (mci == NULL || mci->mci_state == MCIS_CLOSED) + { + bestmci = &MciCache[i]; + continue; + } + if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci) + { + /* connection idle too long -- close it */ + bestmci = &MciCache[i]; + mci_uncache(bestmci, TRUE); + continue; + } + if (*bestmci == NULL) + continue; + if (mci->mci_lastuse < (*bestmci)->mci_lastuse) + bestmci = &MciCache[i]; + } + return bestmci; +} + /* +** MCI_UNCACHE -- remove a connection from a slot. +** +** May close a connection. +** +** Parameters: +** mcislot -- the slot to empty. +** doquit -- if TRUE, send QUIT protocol on this connection. +** if FALSE, we are assumed to be in a forked child; +** all we want to do is close the file(s). +** +** Returns: +** none. +*/ + +mci_uncache(mcislot, doquit) + register MCI **mcislot; + bool doquit; +{ + register MCI *mci; + extern ENVELOPE BlankEnvelope; + + mci = *mcislot; + if (mci == NULL) + return; + *mcislot = NULL; + + if (doquit) + { + message("Closing connection to %s", mci->mci_host); + + mci->mci_flags &= ~MCIF_CACHED; + + /* only uses the envelope to flush the transcript file */ + if (mci->mci_state != MCIS_CLOSED) + smtpquit(mci->mci_mailer, mci, &BlankEnvelope); +#ifdef XLA + xla_host_end(mci->mci_host); +#endif + } + else + { + if (mci->mci_in != NULL) + xfclose(mci->mci_in, "mci_uncache", "mci_in"); + if (mci->mci_out != NULL) + xfclose(mci->mci_out, "mci_uncache", "mci_out"); + mci->mci_in = mci->mci_out = NULL; + mci->mci_state = MCIS_CLOSED; + mci->mci_exitstat = EX_OK; + mci->mci_errno = 0; + mci->mci_flags = 0; + } +} + /* +** MCI_FLUSH -- flush the entire cache +** +** Parameters: +** doquit -- if TRUE, send QUIT protocol. +** if FALSE, just close the connection. +** allbut -- but leave this one open. +** +** Returns: +** none. +*/ + +mci_flush(doquit, allbut) + bool doquit; + MCI *allbut; +{ + register int i; + + if (MciCache == NULL) + return; + + for (i = 0; i < MaxMciCache; i++) + if (allbut != MciCache[i]) + mci_uncache(&MciCache[i], doquit); +} + /* +** MCI_GET -- get information about a particular host +*/ + +MCI * +mci_get(host, m) + char *host; + MAILER *m; +{ + register MCI *mci; + register STAB *s; + +#ifdef DAEMON + extern SOCKADDR CurHostAddr; + + /* clear CurHostAddr so we don't get a bogus address with this name */ + bzero(&CurHostAddr, sizeof CurHostAddr); +#endif DAEMON + + s = stab(host, ST_MCI + m->m_mno, ST_ENTER); + mci = &s->s_mci; + mci->mci_host = s->s_name; + + if (tTd(42, 2)) + { + printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n", + host, m->m_name, mci->mci_state, mci->mci_flags, + mci->mci_exitstat, mci->mci_errno); + } + + if (mci->mci_state == MCIS_OPEN) + { + /* poke the connection to see if it's still alive */ + smtpprobe(mci); + + /* reset the stored state in the event of a timeout */ + if (mci->mci_state != MCIS_OPEN) + { + mci->mci_errno = 0; + mci->mci_exitstat = EX_OK; + mci->mci_state = MCIS_CLOSED; + } + } + + return mci; +} + /* +** MCI_DUMP -- dump the contents of an MCI structure. +** +** Parameters: +** mci -- the MCI structure to dump. +** +** Returns: +** none. +** +** Side Effects: +** none. +*/ + +mci_dump(mci) + register MCI *mci; +{ + extern char *ctime(); + + printf("MCI@%x: ", mci); + if (mci == NULL) + { + printf("NULL\n"); + return; + } + printf("flags=%o, errno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,\n", + mci->mci_flags, mci->mci_errno, mci->mci_herrno, + mci->mci_exitstat, mci->mci_state, mci->mci_pid); + printf("\tmaxsize=%ld, phase=%s, mailer=%s,\n", + mci->mci_maxsize, + mci->mci_phase == NULL ? "NULL" : mci->mci_phase, + mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name); + printf("\thost=%s, lastuse=%s\n", + mci->mci_host == NULL ? "NULL" : mci->mci_host, + ctime(&mci->mci_lastuse)); +} diff --git a/usr.sbin/sendmail/src/newaliases.1 b/usr.sbin/sendmail/src/newaliases.1 index 1d14a01ec1..65cfc5f38e 100644 --- a/usr.sbin/sendmail/src/newaliases.1 +++ b/usr.sbin/sendmail/src/newaliases.1 @@ -1,5 +1,5 @@ -.\" Copyright (c) 1985, 1990 The Regents of the University of California. -.\" All rights reserved. +.\" Copyright (c) 1985, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -29,9 +29,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)newaliases.1 6.7 (Berkeley) 7/30/91 +.\" @(#)newaliases.1 8.1 (Berkeley) 6/7/93 .\" -.Dd July 30, 1991 +.Dd June 7, 1993 .Dt NEWALIASES 1 .Os BSD 4 .Sh NAME diff --git a/usr.sbin/sendmail/src/parseaddr.c b/usr.sbin/sendmail/src/parseaddr.c index 2ee24e07d4..8951cac01c 100644 --- a/usr.sbin/sendmail/src/parseaddr.c +++ b/usr.sbin/sendmail/src/parseaddr.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,7 +33,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)parseaddr.c 5.13 (Berkeley) 6/1/90"; +static char sccsid[] = "@(#)parseaddr.c 8.3 (Berkeley) 7/11/93"; #endif /* not lint */ # include "sendmail.h" @@ -66,6 +66,9 @@ static char sccsid[] = "@(#)parseaddr.c 5.13 (Berkeley) 6/1/90"; ** +1 -- copy everything. ** delim -- the character to terminate the address, passed ** to prescan. +** delimptr -- if non-NULL, set to the location of the +** delim character that was found. +** e -- the envelope that will contain this address. ** ** Returns: ** A pointer to the address descriptor header (`a' if @@ -77,49 +80,69 @@ static char sccsid[] = "@(#)parseaddr.c 5.13 (Berkeley) 6/1/90"; */ /* following delimiters are inherent to the internal algorithms */ -# define DELIMCHARS "\001()<>,;\\\"\r\n" /* word delimiters */ +# define DELIMCHARS "()<>,;\r\n" /* default word delimiters */ ADDRESS * -parseaddr(addr, a, copyf, delim) +parseaddr(addr, a, copyf, delim, delimptr, e) char *addr; register ADDRESS *a; int copyf; - char delim; + int delim; + char **delimptr; + register ENVELOPE *e; { register char **pvp; - register struct mailer *m; + auto char *delimptrbuf; + bool queueup; char pvpbuf[PSBUFSIZE]; - extern char **prescan(); extern ADDRESS *buildaddr(); + extern bool invalidaddr(); /* ** Initialize and prescan address. */ - CurEnv->e_to = addr; + e->e_to = addr; if (tTd(20, 1)) printf("\n--parseaddr(%s)\n", addr); - pvp = prescan(addr, delim, pvpbuf); + if (invalidaddr(addr)) + { + if (tTd(20, 1)) + printf("parseaddr-->bad address\n"); + return NULL; + } + + if (delimptr == NULL) + delimptr = &delimptrbuf; + + pvp = prescan(addr, delim, pvpbuf, delimptr); if (pvp == NULL) + { + if (tTd(20, 1)) + printf("parseaddr-->NULL\n"); return (NULL); + } /* ** Apply rewriting rules. ** Ruleset 0 does basic parsing. It must resolve. */ - rewrite(pvp, 3); - rewrite(pvp, 0); + queueup = FALSE; + if (rewrite(pvp, 3, e) == EX_TEMPFAIL) + queueup = TRUE; + if (rewrite(pvp, 0, e) == EX_TEMPFAIL) + queueup = TRUE; /* ** See if we resolved to a real mailer. */ - if (pvp[0][0] != CANONNET) + if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) { setstat(EX_USAGE); - usrerr("cannot resolve name"); + syserr("554 cannot resolve name %s", addr); return (NULL); } @@ -127,47 +150,32 @@ parseaddr(addr, a, copyf, delim) ** Build canonical address from pvp. */ - a = buildaddr(pvp, a); + a = buildaddr(pvp, a, e); if (a == NULL) return (NULL); - m = a->q_mailer; /* ** Make local copies of the host & user and then ** transport them out. */ - if (copyf > 0) - { - extern char *DelimChar; - char savec = *DelimChar; - - *DelimChar = '\0'; - a->q_paddr = newstr(addr); - *DelimChar = savec; - } - else - a->q_paddr = addr; - - if (a->q_user == NULL) - a->q_user = ""; - if (a->q_host == NULL) - a->q_host = ""; - - if (copyf >= 0) - { - a->q_host = newstr(a->q_host); - if (a->q_user != a->q_paddr) - a->q_user = newstr(a->q_user); - } + allocaddr(a, copyf, addr, *delimptr); /* - ** Convert host name to lower case if requested. - ** User name will be done later. + ** If there was a parsing failure, mark it for queueing. */ - if (!bitnset(M_HST_UPPER, m->m_flags)) - makelower(a->q_host); + if (queueup) + { + char *msg = "Transient parse error -- message queued for future delivery"; + + if (tTd(20, 1)) + printf("parseaddr: queuing message\n"); + message(msg); + if (e->e_message == NULL) + e->e_message = newstr(msg); + a->q_flags |= QQUEUEUP; + } /* ** Compute return value. @@ -182,25 +190,84 @@ parseaddr(addr, a, copyf, delim) return (a); } /* -** LOWERADDR -- map UPPER->lower case on addresses as requested. +** INVALIDADDR -- check for address containing meta-characters ** ** Parameters: -** a -- address to be mapped. +** addr -- the address to check. +** +** Returns: +** TRUE -- if the address has any "wierd" characters +** FALSE -- otherwise. +*/ + +bool +invalidaddr(addr) + register char *addr; +{ + for (; *addr != '\0'; addr++) + { + if ((*addr & 0340) != 0200) + continue; + setstat(EX_USAGE); + usrerr("553 Address contained invalid control characters"); + return TRUE; + } + return FALSE; +} + /* +** ALLOCADDR -- do local allocations of address on demand. +** +** Also lowercases the host name if requested. +** +** Parameters: +** a -- the address to reallocate. +** copyf -- the copy flag (see parseaddr for description). +** paddr -- the printname of the address. +** delimptr -- a pointer to the address delimiter. Must be set. ** ** Returns: ** none. ** ** Side Effects: -** none. +** Copies portions of a into local buffers as requested. */ -loweraddr(a) +allocaddr(a, copyf, paddr, delimptr) register ADDRESS *a; + int copyf; + char *paddr; + char *delimptr; { - register MAILER *m = a->q_mailer; + if (tTd(24, 4)) + printf("allocaddr(copyf=%d, paddr=%s)\n", copyf, paddr); - if (!bitnset(M_USR_UPPER, m->m_flags)) - makelower(a->q_user); + if (copyf > 0 && paddr != NULL) + { + char savec = *delimptr; + + if (savec != '\0') + *delimptr = '\0'; + a->q_paddr = newstr(paddr); + if (savec != '\0') + *delimptr = savec; + } + else + a->q_paddr = paddr; + + if (a->q_user == NULL) + a->q_user = ""; + if (a->q_host == NULL) + a->q_host = ""; + + if (copyf >= 0) + { + a->q_host = newstr(a->q_host); + if (a->q_user != a->q_paddr) + a->q_user = newstr(a->q_user); + } + + if (a->q_paddr == NULL) + a->q_paddr = a->q_user; } /* ** PRESCAN -- Prescan name and make it canonical @@ -225,13 +292,12 @@ loweraddr(a) ** If '\t' then we are reading the .cf file. ** pvpbuf -- place to put the saved text -- note that ** the pointers are static. +** delimptr -- if non-NULL, set to the location of the +** terminating delimiter. ** ** Returns: ** A pointer to a vector of tokens. ** NULL on error. -** -** Side Effects: -** sets DelimChar to point to the character matching 'delim'. */ /* states and character types */ @@ -261,13 +327,12 @@ static short StateTab[NSTATES][NSTATES] = # define NOCHAR -1 /* signal nothing in lookahead token */ -char *DelimChar; /* set to point to the delimiter */ - char ** -prescan(addr, delim, pvpbuf) +prescan(addr, delim, pvpbuf, delimptr) char *addr; char delim; char pvpbuf[]; + char **delimptr; { register char *p; register char *q; @@ -279,6 +344,7 @@ prescan(addr, delim, pvpbuf) char *tok; int state; int newstate; + char *saveto = CurEnv->e_to; static char *av[MAXATOM+1]; extern int errno; @@ -290,10 +356,11 @@ prescan(addr, delim, pvpbuf) cmntcnt = 0; anglecnt = 0; avp = av; - state = OPR; + state = ATM; c = NOCHAR; p = addr; - if (tTd(22, 45)) + CurEnv->e_to = p; + if (tTd(22, 11)) { printf("prescan: "); xputs(p); @@ -307,13 +374,15 @@ prescan(addr, delim, pvpbuf) for (;;) { /* store away any old lookahead character */ - if (c != NOCHAR) + if (c != NOCHAR && !bslashmode) { /* see if there is room */ if (q >= &pvpbuf[PSBUFSIZE - 5]) { - usrerr("Address too long"); - DelimChar = p; + usrerr("553 Address too long"); + if (delimptr != NULL) + *delimptr = p; + CurEnv->e_to = saveto; return (NULL); } @@ -324,8 +393,31 @@ prescan(addr, delim, pvpbuf) /* read a new input character */ c = *p++; if (c == '\0') + { + /* diagnose and patch up bad syntax */ + if (state == QST) + { + usrerr("553 Unbalanced '\"'"); + c = '"'; + } + else if (cmntcnt > 0) + { + usrerr("553 Unbalanced '('"); + c = ')'; + } + else if (anglecnt > 0) + { + c = '>'; + usrerr("553 Unbalanced '<'"); + } + else + break; + + p--; + } + else if (c == delim && anglecnt <= 0 && + cmntcnt <= 0 && state != QST) break; - c &= ~0200; if (tTd(22, 101)) printf("c=%c, s=%d; ", c, state); @@ -334,15 +426,24 @@ prescan(addr, delim, pvpbuf) *q = '\0'; if (bslashmode) { - /* kludge \! for naive users */ - if (c != '!') - c |= 0200; bslashmode = FALSE; + + /* kludge \! for naive users */ + if (cmntcnt > 0) + { + c = NOCHAR; + continue; + } + else if (c != '!' || state == QST) + { + *q++ = '\\'; + continue; + } } - else if (c == '\\') + + if (c == '\\') { bslashmode = TRUE; - c = NOCHAR; } else if (state == QST) { @@ -357,8 +458,10 @@ prescan(addr, delim, pvpbuf) { if (cmntcnt <= 0) { - usrerr("Unbalanced ')'"); - DelimChar = p; + usrerr("553 Unbalanced ')'"); + if (delimptr != NULL) + *delimptr = p; + CurEnv->e_to = saveto; return (NULL); } else @@ -372,13 +475,15 @@ prescan(addr, delim, pvpbuf) { if (anglecnt <= 0) { - usrerr("Unbalanced '>'"); - DelimChar = p; + usrerr("553 Unbalanced '>'"); + if (delimptr != NULL) + *delimptr = p; + CurEnv->e_to = saveto; return (NULL); } anglecnt--; } - else if (delim == ' ' && isspace(c)) + else if (delim == ' ' && isascii(c) && isspace(c)) c = ' '; if (c == NOCHAR) @@ -410,24 +515,28 @@ prescan(addr, delim, pvpbuf) } if (avp >= &av[MAXATOM]) { - syserr("prescan: too many tokens"); - DelimChar = p; + syserr("553 prescan: too many tokens"); + if (delimptr != NULL) + *delimptr = p; + CurEnv->e_to = saveto; return (NULL); } *avp++ = tok; } } while (c != '\0' && (c != delim || anglecnt > 0)); *avp = NULL; - DelimChar = --p; - if (cmntcnt > 0) - usrerr("Unbalanced '('"); - else if (anglecnt > 0) - usrerr("Unbalanced '<'"); - else if (state == QST) - usrerr("Unbalanced '\"'"); - else if (av[0] != NULL) - return (av); - return (NULL); + p--; + if (delimptr != NULL) + *delimptr = p; + if (tTd(22, 12)) + { + printf("prescan==>"); + printav(av); + } + CurEnv->e_to = saveto; + if (av[0] == NULL) + return (NULL); + return (av); } /* ** TOKTYPE -- return token type @@ -443,7 +552,7 @@ prescan(addr, delim, pvpbuf) */ toktype(c) - register char c; + register int c; { static char buf[50]; static bool firstime = TRUE; @@ -451,18 +560,23 @@ toktype(c) if (firstime) { firstime = FALSE; - expand("\001o", buf, &buf[sizeof buf - 1], CurEnv); + expand("\201o", buf, &buf[sizeof buf - 1], CurEnv); (void) strcat(buf, DELIMCHARS); } + c &= 0377; if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS) return (ONE); + if (c == MACRODEXPAND) + return (ONE); if (c == '"') return (QST); + if ((c & 0340) == 0200) + return (OPR); if (!isascii(c)) return (ATM); if (isspace(c) || c == ')') return (SPC); - if (iscntrl(c) || index(buf, c) != NULL) + if (strchr(buf, c) != NULL) return (OPR); return (ATM); } @@ -490,9 +604,12 @@ toktype(c) ** ** Parameters: ** pvp -- pointer to token vector. +** ruleset -- the ruleset to use for rewriting. +** e -- the current envelope. ** ** Returns: -** none. +** A status code. If EX_TEMPFAIL, higher level code should +** attempt recovery. ** ** Side Effects: ** pvp is modified. @@ -502,14 +619,17 @@ struct match { char **first; /* first token matched */ char **last; /* last token matched */ + char **pattern; /* pointer to pattern */ }; # define MAXMATCH 9 /* max params per rewrite */ -rewrite(pvp, ruleset) +int +rewrite(pvp, ruleset, e) char **pvp; int ruleset; + register ENVELOPE *e; { register char *ap; /* address pointer */ register char *rp; /* rewrite pointer */ @@ -517,6 +637,8 @@ rewrite(pvp, ruleset) register char **rvp; /* rewrite vector pointer */ register struct match *mlp; /* cur ptr into mlist */ register struct rewrite *rwr; /* pointer to current rewrite rule */ + int ruleno; /* current rule number */ + int rstat = EX_OK; /* return status */ struct match mlist[MAXMATCH]; /* stores match on LHS */ char *npvp[MAXATOM+1]; /* temporary space for rebuild */ @@ -525,16 +647,24 @@ rewrite(pvp, ruleset) printf("rewrite: ruleset %2d input:", ruleset); printav(pvp); } + if (ruleset < 0 || ruleset >= MAXRWSETS) + { + syserr("554 rewrite: illegal ruleset number %d", ruleset); + return EX_CONFIG; + } if (pvp == NULL) - return; + return EX_USAGE; /* ** Run through the list of rewrite rules, applying ** any that match. */ + ruleno = 1; for (rwr = RewriteRules[ruleset]; rwr != NULL; ) { + int loopcount = 0; + if (tTd(21, 12)) { printf("-----trying rule:"); @@ -545,15 +675,27 @@ rewrite(pvp, ruleset) mlp = mlist; rvp = rwr->r_lhs; avp = pvp; + if (++loopcount > 100) + { + syserr("554 Infinite loop in ruleset %d, rule %d", + ruleset, ruleno); + if (tTd(21, 1)) + { + printf("workspace: "); + printav(pvp); + } + break; + } + while ((ap = *avp) != NULL || *rvp != NULL) { rp = *rvp; if (tTd(21, 35)) { - printf("ap="); - xputs(ap); - printf(", rp="); + printf("ADVANCE rp="); xputs(rp); + printf(", ap="); + xputs(ap); printf("\n"); } if (rp == NULL) @@ -561,33 +703,58 @@ rewrite(pvp, ruleset) /* end-of-pattern before end-of-address */ goto backup; } - if (ap == NULL && *rp != MATCHZANY) + if (ap == NULL && (*rp & 0377) != MATCHZANY && + (*rp & 0377) != MATCHZERO) { - /* end-of-input */ - break; + /* end-of-input with patterns left */ + goto backup; } - switch (*rp) + switch (*rp & 0377) { register STAB *s; + char buf[MAXLINE]; case MATCHCLASS: - case MATCHNCLASS: - /* match any token in (not in) a class */ - s = stab(ap, ST_CLASS, ST_FIND); + /* match any phrase in a class */ + mlp->pattern = rvp; + mlp->first = avp; + extendclass: + ap = *avp; + if (ap == NULL) + goto backup; + mlp->last = avp++; + cataddr(mlp->first, mlp->last, buf, sizeof buf, '\0'); + s = stab(buf, ST_CLASS, ST_FIND); if (s == NULL || !bitnset(rp[1], s->s_class)) { - if (*rp == MATCHCLASS) - goto backup; + if (tTd(21, 36)) + { + printf("EXTEND rp="); + xputs(rp); + printf(", ap="); + xputs(ap); + printf("\n"); + } + goto extendclass; } - else if (*rp == MATCHNCLASS) + if (tTd(21, 36)) + printf("CLMATCH\n"); + mlp++; + break; + + case MATCHNCLASS: + /* match any token not in a class */ + s = stab(ap, ST_CLASS, ST_FIND); + if (s != NULL && bitnset(rp[1], s->s_class)) goto backup; - /* explicit fall-through */ + /* fall through */ case MATCHONE: case MATCHANY: /* match exactly one token */ + mlp->pattern = rvp; mlp->first = avp; mlp->last = avp++; mlp++; @@ -595,11 +762,49 @@ rewrite(pvp, ruleset) case MATCHZANY: /* match zero or more tokens */ + mlp->pattern = rvp; mlp->first = avp; mlp->last = avp - 1; mlp++; break; + case MATCHZERO: + /* match zero tokens */ + break; + + case MACRODEXPAND: + /* + ** Match against run-time macro. + ** This algorithm is broken for the + ** general case (no recursive macros, + ** improper tokenization) but should + ** work for the usual cases. + */ + + ap = macvalue(rp[1], e); + mlp->first = avp; + if (tTd(21, 2)) + printf("rewrite: LHS $&%c => \"%s\"\n", + rp[1], + ap == NULL ? "(NULL)" : ap); + + if (ap == NULL) + break; + while (*ap != '\0') + { + if (*avp == NULL || + strncasecmp(ap, *avp, strlen(*avp)) != 0) + { + /* no match */ + avp = mlp->first; + goto backup; + } + ap += strlen(*avp++); + } + + /* match */ + break; + default: /* must have exact match */ if (strcasecmp(rp, ap)) @@ -612,29 +817,47 @@ rewrite(pvp, ruleset) rvp++; continue; - backup: + backup: /* match failed -- back up */ - while (--rvp >= rwr->r_lhs) + while (--mlp >= mlist) { + rvp = mlp->pattern; rp = *rvp; - if (*rp == MATCHANY || *rp == MATCHZANY) + avp = mlp->last + 1; + ap = *avp; + + if (tTd(21, 36)) + { + printf("BACKUP rp="); + xputs(rp); + printf(", ap="); + xputs(ap); + printf("\n"); + } + + if (ap == NULL) + { + /* run off the end -- back up again */ + continue; + } + if ((*rp & 0377) == MATCHANY || + (*rp & 0377) == MATCHZANY) { /* extend binding and continue */ - avp = ++mlp[-1].last; - avp++; + mlp->last = avp++; rvp++; + mlp++; break; } - avp--; - if (*rp == MATCHONE || *rp == MATCHCLASS || - *rp == MATCHNCLASS) + if ((*rp & 0377) == MATCHCLASS) { - /* back out binding */ - mlp--; + /* extend binding and try again */ + mlp->last = avp; + goto extendclass; } } - if (rvp < rwr->r_lhs) + if (mlp < mlist) { /* total failure to match */ break; @@ -645,11 +868,12 @@ rewrite(pvp, ruleset) ** See if we successfully matched */ - if (rvp < rwr->r_lhs || *rvp != NULL) + if (mlp < mlist || *rvp != NULL) { if (tTd(21, 10)) printf("----- rule fails\n"); rwr = rwr->r_next; + ruleno++; continue; } @@ -661,17 +885,18 @@ rewrite(pvp, ruleset) } rp = *rvp; - if (*rp == CANONUSER) + if ((*rp & 0377) == CANONUSER) { rvp++; rwr = rwr->r_next; + ruleno++; } - else if (*rp == CANONHOST) + else if ((*rp & 0377) == CANONHOST) { rvp++; rwr = NULL; } - else if (*rp == CANONNET) + else if ((*rp & 0377) == CANONNET) rwr = NULL; /* substitute */ @@ -681,14 +906,15 @@ rewrite(pvp, ruleset) register char **pp; rp = *rvp; - if (*rp == MATCHREPL) + if ((*rp & 0377) == MATCHREPL) { /* substitute from LHS */ m = &mlist[rp[1] - '1']; - if (m >= mlp) + if (m < mlist || m >= mlp) { - syserr("rewrite: ruleset %d: replacement out of bounds", ruleset); - return; + syserr("554 rewrite: ruleset %d: replacement $%c out of bounds", + ruleset, rp[1]); + return EX_CONFIG; } if (tTd(21, 15)) { @@ -707,8 +933,8 @@ rewrite(pvp, ruleset) { if (avp >= &npvp[MAXATOM]) { - syserr("rewrite: expansion too long"); - return; + syserr("554 rewrite: expansion too long"); + return EX_DATAERR; } *avp++ = *pp++; } @@ -719,16 +945,27 @@ rewrite(pvp, ruleset) if (avp >= &npvp[MAXATOM]) { toolong: - syserr("rewrite: expansion too long"); - return; + syserr("554 rewrite: expansion too long"); + return EX_DATAERR; + } + if ((*rp & 0377) != MACRODEXPAND) + *avp++ = rp; + else + { + *avp = macvalue(rp[1], e); + if (tTd(21, 2)) + printf("rewrite: RHS $&%c => \"%s\"\n", + rp[1], + *avp == NULL ? "(NULL)" : *avp); + if (*avp != NULL) + avp++; } - *avp++ = rp; } } *avp++ = NULL; /* - ** Check for any hostname lookups. + ** Check for any hostname/keyword lookups. */ for (rvp = npvp; *rvp != NULL; rvp++) @@ -736,49 +973,147 @@ rewrite(pvp, ruleset) char **hbrvp; char **xpvp; int trsize; - char *olddelimchar; + char *replac; + int endtoken; + STAB *map; + char *mapname; + char **key_rvp; + char **arg_rvp; + char **default_rvp; char buf[MAXNAME + 1]; char *pvpb1[MAXATOM + 1]; + char *argvect[10]; char pvpbuf[PSBUFSIZE]; - extern char *DelimChar; - if (**rvp != HOSTBEGIN) + if ((**rvp & 0377) != HOSTBEGIN && + (**rvp & 0377) != LOOKUPBEGIN) continue; /* - ** Got a hostname lookup. + ** Got a hostname/keyword lookup. ** ** This could be optimized fairly easily. */ hbrvp = rvp; + if ((**rvp & 0377) == HOSTBEGIN) + { + endtoken = HOSTEND; + mapname = "host"; + } + else + { + endtoken = LOOKUPEND; + mapname = *++rvp; + } + map = stab(mapname, ST_MAP, ST_FIND); + if (map == NULL) + syserr("554 rewrite: map %s not found", mapname); /* extract the match part */ - while (*++rvp != NULL && **rvp != HOSTEND) - continue; + key_rvp = ++rvp; + default_rvp = NULL; + arg_rvp = argvect; + xpvp = NULL; + replac = pvpbuf; + while (*rvp != NULL && (**rvp & 0377) != endtoken) + { + int nodetype = **rvp & 0377; + + if (nodetype != CANONHOST && nodetype != CANONUSER) + { + rvp++; + continue; + } + + *rvp++ = NULL; + + if (xpvp != NULL) + { + cataddr(xpvp, NULL, replac, + &pvpbuf[sizeof pvpbuf] - replac, + '\0'); + *++arg_rvp = replac; + replac += strlen(replac) + 1; + xpvp = NULL; + } + switch (nodetype) + { + case CANONHOST: + xpvp = rvp; + break; + + case CANONUSER: + default_rvp = rvp; + break; + } + } if (*rvp != NULL) *rvp++ = NULL; + if (xpvp != NULL) + { + cataddr(xpvp, NULL, replac, + &pvpbuf[sizeof pvpbuf] - replac, + '\0'); + *++arg_rvp = replac; + } + *++arg_rvp = NULL; /* save the remainder of the input string */ trsize = (int) (avp - rvp + 1) * sizeof *rvp; bcopy((char *) rvp, (char *) pvpb1, trsize); /* look it up */ - cataddr(++hbrvp, buf, sizeof buf); - maphostname(buf, sizeof buf); - - /* scan the new host name */ - olddelimchar = DelimChar; - xpvp = prescan(buf, '\0', pvpbuf); - DelimChar = olddelimchar; - if (xpvp == NULL) + cataddr(key_rvp, NULL, buf, sizeof buf, '\0'); + argvect[0] = buf; + if (map != NULL && bitset(MF_OPEN, map->s_map.map_mflags)) { - syserr("rewrite: cannot prescan canonical hostname: %s", buf); - return; + auto int stat = EX_OK; + + /* XXX should try to auto-open the map here */ + + if (tTd(60, 1)) + printf("map_lookup(%s, %s) => ", + mapname, buf); + replac = (*map->s_map.map_class->map_lookup)(&map->s_map, + buf, argvect, &stat); + if (tTd(60, 1)) + printf("%s (%d)\n", + replac ? replac : "NOT FOUND", + stat); + + /* should recover if stat == EX_TEMPFAIL */ + if (stat == EX_TEMPFAIL) + rstat = stat; + } + else + replac = NULL; + + /* if no replacement, use default */ + if (replac == NULL && default_rvp != NULL) + { + /* create the default */ + cataddr(default_rvp, NULL, buf, sizeof buf, '\0'); + replac = buf; + } + + if (replac == NULL) + { + xpvp = key_rvp; + } + else + { + /* scan the new replacement */ + xpvp = prescan(replac, '\0', pvpbuf, NULL); + if (xpvp == NULL) + { + /* prescan already printed error */ + return EX_DATAERR; + } } /* append it to the token list */ - for (avp = --hbrvp; *xpvp != NULL; xpvp++) + for (avp = hbrvp; *xpvp != NULL; xpvp++) { *avp++ = newstr(*xpvp); if (avp >= &npvp[MAXATOM]) @@ -797,13 +1132,19 @@ rewrite(pvp, ruleset) ** Check for subroutine calls. */ - if (*npvp != NULL && **npvp == CALLSUBR) + if (*npvp != NULL && (**npvp & 0377) == CALLSUBR) { + int stat; + bcopy((char *) &npvp[2], (char *) pvp, (int) (avp - npvp - 2) * sizeof *avp); if (tTd(21, 3)) printf("-----callsubr %s\n", npvp[1]); - rewrite(pvp, atoi(npvp[1])); + stat = rewrite(pvp, atoi(npvp[1]), e); + if (rstat == EX_OK || stat == EX_TEMPFAIL) + rstat = stat; + if ((**pvp & 0377) == CANONNET) + rwr = NULL; } else { @@ -822,6 +1163,8 @@ rewrite(pvp, ruleset) printf("rewrite: ruleset %2d returns:", ruleset); printav(pvp); } + + return rstat; } /* ** BUILDADDR -- build address from token vector. @@ -830,6 +1173,7 @@ rewrite(pvp, ruleset) ** tv -- token vector. ** a -- pointer to address descriptor to fill. ** If NULL, one will be allocated. +** e -- the current envelope. ** ** Returns: ** NULL if there was an error. @@ -839,91 +1183,181 @@ rewrite(pvp, ruleset) ** fills in 'a' */ +struct errcodes +{ + char *ec_name; /* name of error code */ + int ec_code; /* numeric code */ +} ErrorCodes[] = +{ + "usage", EX_USAGE, + "nouser", EX_NOUSER, + "nohost", EX_NOHOST, + "unavailable", EX_UNAVAILABLE, + "software", EX_SOFTWARE, + "tempfail", EX_TEMPFAIL, + "protocol", EX_PROTOCOL, +#ifdef EX_CONFIG + "config", EX_CONFIG, +#endif + NULL, EX_UNAVAILABLE, +}; + ADDRESS * -buildaddr(tv, a) +buildaddr(tv, a, e) register char **tv; register ADDRESS *a; + register ENVELOPE *e; { - static char buf[MAXNAME]; struct mailer **mp; register struct mailer *m; + char *bp; + int spaceleft; + static char buf[MAXNAME]; if (a == NULL) a = (ADDRESS *) xalloc(sizeof *a); bzero((char *) a, sizeof *a); /* figure out what net/mailer to use */ - if (**tv != CANONNET) + if ((**tv & 0377) != CANONNET) { - syserr("buildaddr: no net"); + syserr("554 buildaddr: no net"); return (NULL); } tv++; - if (!strcasecmp(*tv, "error")) + if (strcasecmp(*tv, "error") == 0) { - if (**++tv == CANONHOST) + if ((**++tv & 0377) == CANONHOST) { - setstat(atoi(*++tv)); + register struct errcodes *ep; + + if (isascii(**++tv) && isdigit(**tv)) + { + setstat(atoi(*tv)); + } + else + { + for (ep = ErrorCodes; ep->ec_name != NULL; ep++) + if (strcasecmp(ep->ec_name, *tv) == 0) + break; + setstat(ep->ec_code); + } tv++; } - if (**tv != CANONUSER) - syserr("buildaddr: error: no user"); - buf[0] = '\0'; - while (*++tv != NULL) - { - if (buf[0] != '\0') - (void) strcat(buf, " "); - (void) strcat(buf, *tv); - } + if ((**tv & 0377) != CANONUSER) + syserr("554 buildaddr: error: no user"); + cataddr(++tv, NULL, buf, sizeof buf, ' '); + stripquotes(buf); usrerr(buf); return (NULL); } + for (mp = Mailer; (m = *mp++) != NULL; ) { - if (!strcasecmp(m->m_name, *tv)) + if (strcasecmp(m->m_name, *tv) == 0) break; } if (m == NULL) { - syserr("buildaddr: unknown mailer %s", *tv); + syserr("554 buildaddr: unknown mailer %s", *tv); return (NULL); } a->q_mailer = m; /* figure out what host (if any) */ tv++; - if (!bitnset(M_LOCAL, m->m_flags)) + if ((**tv & 0377) == CANONHOST) { - if (**tv++ != CANONHOST) + bp = buf; + spaceleft = sizeof buf - 1; + while (*++tv != NULL && (**tv & 0377) != CANONUSER) { - syserr("buildaddr: no host"); - return (NULL); + int i = strlen(*tv); + + if (i > spaceleft) + { + /* out of space for this address */ + if (spaceleft >= 0) + syserr("554 buildaddr: host too long (%.40s...)", + buf); + i = spaceleft; + spaceleft = 0; + } + if (i <= 0) + continue; + bcopy(*tv, bp, i); + bp += i; + spaceleft -= i; } - buf[0] = '\0'; - while (*tv != NULL && **tv != CANONUSER) - (void) strcat(buf, *tv++); + *bp = '\0'; a->q_host = newstr(buf); } else + { + if (!bitnset(M_LOCALMAILER, m->m_flags)) + { + syserr("554 buildaddr: no host"); + return (NULL); + } a->q_host = NULL; + } /* figure out the user */ - if (*tv == NULL || **tv != CANONUSER) + if (*tv == NULL || (**tv & 0377) != CANONUSER) { - syserr("buildaddr: no user"); + syserr("554 buildaddr: no user"); return (NULL); } + tv++; - /* rewrite according recipient mailer rewriting rules */ - rewrite(++tv, 2); - if (m->m_r_rwset > 0) - rewrite(tv, m->m_r_rwset); - rewrite(tv, 4); + /* do special mapping for local mailer */ + if (m == LocalMailer && *tv != NULL) + { + register char *p = *tv; + + if (*p == '"') + p++; + if (*p == '|') + a->q_mailer = m = ProgMailer; + else if (*p == '/') + a->q_mailer = m = FileMailer; + else if (*p == ':') + { + /* may be :include: */ + cataddr(tv, NULL, buf, sizeof buf, '\0'); + stripquotes(buf); + if (strncasecmp(buf, ":include:", 9) == 0) + { + /* if :include:, don't need further rewriting */ + a->q_mailer = m = InclMailer; + a->q_user = &buf[9]; + return (a); + } + } + } + + if (m == LocalMailer && *tv != NULL && strcmp(*tv, "@") == 0) + { + tv++; + a->q_flags |= QNOTREMOTE; + } + + /* do cleanup of final address */ + (void) rewrite(tv, 4, e); /* save the result for the command line/RCPT argument */ - cataddr(tv, buf, sizeof buf); + cataddr(tv, NULL, buf, sizeof buf, '\0'); a->q_user = buf; + /* + ** Do mapping to lower case as requested by mailer + */ + + if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags)) + makelower(a->q_host); + if (!bitnset(M_USR_UPPER, m->m_flags)) + makelower(a->q_user); + return (a); } /* @@ -931,8 +1365,12 @@ buildaddr(tv, a) ** ** Parameters: ** pvp -- parameter vector to rebuild. +** evp -- last parameter to include. Can be NULL to +** use entire pvp. ** buf -- buffer to build the string into. ** sz -- size of buf. +** spacesub -- the space separator character; if null, +** use SpaceSub. ** ** Returns: ** none. @@ -941,16 +1379,21 @@ buildaddr(tv, a) ** Destroys buf. */ -cataddr(pvp, buf, sz) +cataddr(pvp, evp, buf, sz, spacesub) char **pvp; + char **evp; char *buf; register int sz; + char spacesub; { bool oatomtok = FALSE; bool natomtok = FALSE; register int i; register char *p; + if (spacesub == '\0') + spacesub = SpaceSub; + if (pvp == NULL) { (void) strcpy(buf, ""); @@ -962,12 +1405,13 @@ cataddr(pvp, buf, sz) { natomtok = (toktype(**pvp) == ATM); if (oatomtok && natomtok) - *p++ = SpaceSub; + *p++ = spacesub; (void) strcpy(p, *pvp); oatomtok = natomtok; p += i; sz -= i + 1; - pvp++; + if (pvp++ == evp) + break; } *p = '\0'; } @@ -1001,13 +1445,21 @@ sameaddr(a, b) if (strcmp(a->q_user, b->q_user) != 0) return (FALSE); - /* if the mailer ignores hosts, we have succeeded! */ - if (bitnset(M_LOCAL, a->q_mailer->m_flags)) - return (TRUE); + /* if we have good uids for both but the differ, these are different */ + if (bitset(QGOODUID, a->q_flags & b->q_flags) && a->q_uid != b->q_uid) + return (FALSE); /* otherwise compare hosts (but be careful for NULL ptrs) */ + if (a->q_host == b->q_host) + { + /* probably both null pointers */ + return (TRUE); + } if (a->q_host == NULL || b->q_host == NULL) + { + /* only one is a null pointer */ return (FALSE); + } if (strcmp(a->q_host, b->q_host) != 0) return (FALSE); @@ -1032,19 +1484,34 @@ printaddr(a, follow) bool follow; { bool first = TRUE; + register MAILER *m; + MAILER pseudomailer; while (a != NULL) { first = FALSE; printf("%x=", a); (void) fflush(stdout); - printf("%s: mailer %d (%s), host `%s', user `%s', ruser `%s'\n", - a->q_paddr, a->q_mailer->m_mno, a->q_mailer->m_name, - a->q_host, a->q_user, a->q_ruser? a->q_ruser: "<null>"); - printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags, - a->q_alias); - printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home, - a->q_fullname); + + /* find the mailer -- carefully */ + m = a->q_mailer; + if (m == NULL) + { + m = &pseudomailer; + m->m_mno = -1; + m->m_name = "NULL"; + } + + printf("%s:\n\tmailer %d (%s), host `%s', user `%s', ruser `%s'\n", + a->q_paddr, m->m_mno, m->m_name, + a->q_host, a->q_user, + a->q_ruser ? a->q_ruser : "<null>"); + printf("\tnext=%x, flags=%o, alias %x, uid %d, gid %d\n", + a->q_next, a->q_flags, a->q_alias, a->q_uid, a->q_gid); + printf("\towner=%s, home=\"%s\", fullname=\"%s\"\n", + a->q_owner == NULL ? "(none)" : a->q_owner, + a->q_home == NULL ? "(none)" : a->q_home, + a->q_fullname == NULL ? "(none)" : a->q_fullname); if (!follow) return; @@ -1061,10 +1528,9 @@ printaddr(a, follow) ** name -- the name to translate. ** m -- the mailer that we want to do rewriting relative ** to. -** senderaddress -- if set, uses the sender rewriting rules -** rather than the recipient rewriting rules. -** canonical -- if set, strip out any comment information, -** etc. +** flags -- fine tune operations. +** pstat -- pointer to status word. +** e -- the current envelope. ** ** Returns: ** the text string representing this address relative to @@ -1079,27 +1545,33 @@ printaddr(a, follow) */ char * -remotename(name, m, senderaddress, canonical) +remotename(name, m, flags, pstat, e) char *name; struct mailer *m; - bool senderaddress; - bool canonical; + int flags; + int *pstat; + register ENVELOPE *e; { register char **pvp; char *fancy; - extern char *macvalue(); - char *oldg = macvalue('g', CurEnv); + char *oldg = macvalue('g', e); + int rwset; static char buf[MAXNAME]; char lbuf[MAXNAME]; char pvpbuf[PSBUFSIZE]; - extern char **prescan(); extern char *crackaddr(); if (tTd(12, 1)) printf("remotename(%s)\n", name); /* don't do anything if we are tagging it as special */ - if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0) + if (bitset(RF_SENDERADDR, flags)) + rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset + : m->m_se_rwset; + else + rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset + : m->m_re_rwset; + if (rwset < 0) return (name); /* @@ -1107,8 +1579,8 @@ remotename(name, m, senderaddress, canonical) ** This will leave the name as a comment and a $g macro. */ - if (canonical) - fancy = "\001g"; + if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags)) + fancy = "\201g"; else fancy = crackaddr(name); @@ -1120,11 +1592,12 @@ remotename(name, m, senderaddress, canonical) ** domain will be appended. */ - pvp = prescan(name, '\0', pvpbuf); + pvp = prescan(name, '\0', pvpbuf, NULL); if (pvp == NULL) return (name); - rewrite(pvp, 3); - if (CurEnv->e_fromdomain != NULL) + if (rewrite(pvp, 3, e) == EX_TEMPFAIL) + *pstat = EX_TEMPFAIL; + if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL) { /* append from domain to this address */ register char **pxp = pvp; @@ -1135,11 +1608,12 @@ remotename(name, m, senderaddress, canonical) if (*pxp == NULL) { /* no.... append the "@domain" from the sender */ - register char **qxq = CurEnv->e_fromdomain; + register char **qxq = e->e_fromdomain; while ((*pxp++ = *qxq++) != NULL) continue; - rewrite(pvp, 3); + if (rewrite(pvp, 3, e) == EX_TEMPFAIL) + *pstat = EX_TEMPFAIL; } } @@ -1150,17 +1624,20 @@ remotename(name, m, senderaddress, canonical) ** Then run it through any receiving-mailer-specific rulesets. */ - if (senderaddress) + if (bitset(RF_SENDERADDR, flags)) { - rewrite(pvp, 1); - if (m->m_s_rwset > 0) - rewrite(pvp, m->m_s_rwset); + if (rewrite(pvp, 1, e) == EX_TEMPFAIL) + *pstat = EX_TEMPFAIL; } else { - rewrite(pvp, 2); - if (m->m_r_rwset > 0) - rewrite(pvp, m->m_r_rwset); + if (rewrite(pvp, 2, e) == EX_TEMPFAIL) + *pstat = EX_TEMPFAIL; + } + if (rwset > 0) + { + if (rewrite(pvp, rwset, e) == EX_TEMPFAIL) + *pstat = EX_TEMPFAIL; } /* @@ -1170,18 +1647,211 @@ remotename(name, m, senderaddress, canonical) ** may be used as a default to the above rules. */ - rewrite(pvp, 4); + if (rewrite(pvp, 4, e) == EX_TEMPFAIL) + *pstat = EX_TEMPFAIL; /* ** Now restore the comment information we had at the beginning. */ - cataddr(pvp, lbuf, sizeof lbuf); - define('g', lbuf, CurEnv); - expand(fancy, buf, &buf[sizeof buf - 1], CurEnv); - define('g', oldg, CurEnv); + cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0'); + define('g', lbuf, e); + expand(fancy, buf, &buf[sizeof buf - 1], e); + define('g', oldg, e); if (tTd(12, 1)) printf("remotename => `%s'\n", buf); return (buf); } + /* +** MAPLOCALUSER -- run local username through ruleset 5 for final redirection +** +** Parameters: +** a -- the address to map (but just the user name part). +** sendq -- the sendq in which to install any replacement +** addresses. +** +** Returns: +** none. +*/ + +maplocaluser(a, sendq, e) + register ADDRESS *a; + ADDRESS **sendq; + ENVELOPE *e; +{ + register char **pvp; + register ADDRESS *a1 = NULL; + auto char *delimptr; + char pvpbuf[PSBUFSIZE]; + + if (tTd(29, 1)) + { + printf("maplocaluser: "); + printaddr(a, FALSE); + } + pvp = prescan(a->q_user, '\0', pvpbuf, &delimptr); + if (pvp == NULL) + return; + + (void) rewrite(pvp, 5, e); + if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) + return; + + /* if non-null, mailer destination specified -- has it changed? */ + a1 = buildaddr(pvp, NULL, e); + if (a1 == NULL || sameaddr(a, a1)) + return; + + /* mark old address as dead; insert new address */ + a->q_flags |= QDONTSEND; + if (tTd(29, 5)) + { + printf("maplocaluser: QDONTSEND "); + printaddr(a, FALSE); + } + a1->q_alias = a; + allocaddr(a1, 1, NULL, delimptr); + (void) recipient(a1, sendq, e); +} + /* +** DEQUOTE_INIT -- initialize dequote map +** +** This is a no-op. +** +** Parameters: +** map -- the internal map structure. +** args -- arguments. +** +** Returns: +** TRUE. +*/ + +bool +dequote_init(map, args) + MAP *map; + char *args; +{ + register char *p = args; + + for (;;) + { + while (isascii(*p) && isspace(*p)) + p++; + if (*p != '-') + break; + switch (*++p) + { + case 'a': + map->map_app = ++p; + break; + } + while (*p != '\0' && !(isascii(*p) && isspace(*p))) + p++; + if (*p != '\0') + *p = '\0'; + } + if (map->map_app != NULL) + map->map_app = newstr(map->map_app); + + return TRUE; +} + /* +** DEQUOTE_MAP -- unquote an address +** +** Parameters: +** map -- the internal map structure (ignored). +** name -- the name to dequote. +** av -- arguments (ignored). +** statp -- pointer to status out-parameter. +** +** Returns: +** NULL -- if there were no quotes, or if the resulting +** unquoted buffer would not be acceptable to prescan. +** else -- The dequoted buffer. +*/ + +char * +dequote_map(map, name, av, statp) + MAP *map; + char *name; + char **av; + int *statp; +{ + register char *p; + register char *q; + register char c; + int anglecnt; + int cmntcnt; + int quotecnt; + int spacecnt; + bool quotemode; + bool bslashmode; + + anglecnt = 0; + cmntcnt = 0; + quotecnt = 0; + spacecnt = 0; + quotemode = FALSE; + bslashmode = FALSE; + + for (p = q = name; (c = *p++) != '\0'; ) + { + if (bslashmode) + { + bslashmode = FALSE; + *q++ = c; + continue; + } + + switch (c) + { + case '\\': + bslashmode = TRUE; + break; + + case '(': + cmntcnt++; + break; + + case ')': + if (cmntcnt-- <= 0) + return NULL; + break; + + case ' ': + spacecnt++; + break; + } + + if (cmntcnt > 0) + { + *q++ = c; + continue; + } + + switch (c) + { + case '"': + quotemode = !quotemode; + quotecnt++; + continue; + + case '<': + anglecnt++; + break; + + case '>': + if (anglecnt-- <= 0) + return NULL; + break; + } + *q++ = c; + } + + if (anglecnt != 0 || cmntcnt != 0 || bslashmode || + quotemode || quotecnt <= 0 || spacecnt != 0) + return NULL; + *q++ = '\0'; + return name; +} diff --git a/usr.sbin/sendmail/src/pathnames.h b/usr.sbin/sendmail/src/pathnames.h index 588a648390..be9a072617 100644 --- a/usr.sbin/sendmail/src/pathnames.h +++ b/usr.sbin/sendmail/src/pathnames.h @@ -1,6 +1,6 @@ /*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,8 +30,21 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pathnames.h 5.1 (Berkeley) 4/19/90 + * @(#)pathnames.h 8.1 (Berkeley) 6/7/93 */ -#define _PATH_SENDMAILCF "/etc/sendmail.cf"; -#define _PATH_SENDMAILFC "/etc/sendmail.fc"; +#ifndef _PATH_SENDMAILCF +# define _PATH_SENDMAILCF "/etc/sendmail.cf" +#endif + +#ifndef _PATH_SENDMAILFC +# define _PATH_SENDMAILFC "/etc/sendmail.fc" +#endif + +#ifndef _PATH_SENDMAILPID +# ifdef BSD4_4 +# define _PATH_SENDMAILPID "/var/run/sendmail.pid" +# else +# define _PATH_SENDMAILPID "/etc/sendmail.pid" +# endif +#endif diff --git a/usr.sbin/sendmail/src/queue.c b/usr.sbin/sendmail/src/queue.c index 03b226a97c..5b55217774 100644 --- a/usr.sbin/sendmail/src/queue.c +++ b/usr.sbin/sendmail/src/queue.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,18 +36,16 @@ #ifndef lint #ifdef QUEUE -static char sccsid[] = "@(#)queue.c 5.32 (Berkeley) 3/12/91 (with queueing)"; +static char sccsid[] = "@(#)queue.c 8.3 (Berkeley) 7/13/93 (with queueing)"; #else -static char sccsid[] = "@(#)queue.c 5.32 (Berkeley) 3/12/91 (without queueing)"; +static char sccsid[] = "@(#)queue.c 8.3 (Berkeley) 7/13/93 (without queueing)"; #endif #endif /* not lint */ -# include <sys/stat.h> -# include <sys/dir.h> -# include <sys/file.h> # include <signal.h> # include <errno.h> # include <pwd.h> +# include <dirent.h> # ifdef QUEUE @@ -64,7 +62,6 @@ struct work }; typedef struct work WORK; -extern int la; WORK *WorkQ; /* queue of things to be done */ /* @@ -77,50 +74,67 @@ WORK *WorkQ; /* queue of things to be done */ ** announce -- if TRUE, tell when you are queueing up. ** ** Returns: -** locked FILE* to q file +** none. ** ** Side Effects: ** The current request are saved in a control file. +** The queue file is left locked. */ -FILE * queueup(e, queueall, announce) register ENVELOPE *e; bool queueall; bool announce; { char *qf; - char buf[MAXLINE], tf[MAXLINE]; register FILE *tfp; register HDR *h; register ADDRESS *q; + int fd; + int i; + bool newid; + register char *p; MAILER nullmailer; - int fd, ret; + char buf[MAXLINE], tf[MAXLINE]; /* ** Create control file. */ - do { - strcpy(tf, queuename(e, 't')); - fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode); - if (fd < 0) { - if ( errno != EEXIST) { - syserr("queueup: cannot create temp file %s", - tf); - return NULL; - } - } else { - if (flock(fd, LOCK_EX|LOCK_NB) < 0) { - if (errno != EWOULDBLOCK) - syserr("cannot flock(%s)", tf); - close(fd); - fd = -1; + newid = (e->e_id == NULL); + strcpy(tf, queuename(e, 't')); + tfp = e->e_lockfp; + if (tfp == NULL) + newid = FALSE; + if (newid) + { + tfp = e->e_lockfp; + } + else + { + /* get a locked tf file */ + for (i = 100; --i >= 0; ) + { + fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode); + if (fd < 0) + { + if (errno == EEXIST) + continue; +notemp: + syserr("!queueup: cannot create temp file %s", tf); } + + if (lockfile(fd, tf, LOCK_EX|LOCK_NB)) + break; + + close(fd); + sleep(i); } - } while (fd < 0); + if (fd < 0) + goto notemp; - tfp = fdopen(fd, "w"); + tfp = fdopen(fd, "w"); + } if (tTd(40, 1)) printf("queueing %s\n", e->e_id); @@ -137,14 +151,10 @@ queueup(e, queueall, announce) e->e_df = newstr(queuename(e, 'd')); fd = open(e->e_df, O_WRONLY|O_CREAT, FileMode); if (fd < 0) - { - syserr("queueup: cannot create %s", e->e_df); - (void) fclose(tfp); - return NULL; - } + syserr("!queueup: cannot create %s", e->e_df); dfp = fdopen(fd, "w"); - (*e->e_putbody)(dfp, ProgMailer, e); - (void) fclose(dfp); + (*e->e_putbody)(dfp, FileMailer, e, NULL); + (void) xfclose(dfp, "queueup dfp", e->e_id); e->e_putbody = putbody; } @@ -160,33 +170,61 @@ queueup(e, queueall, announce) /* output creation time */ fprintf(tfp, "T%ld\n", e->e_ctime); - /* output name of data file */ + /* output type and name of data file */ + if (e->e_bodytype != NULL) + fprintf(tfp, "B%s\n", e->e_bodytype); fprintf(tfp, "D%s\n", e->e_df); /* message from envelope, if it exists */ if (e->e_message != NULL) fprintf(tfp, "M%s\n", e->e_message); + /* send various flag bits through */ + p = buf; + if (bitset(EF_WARNING, e->e_flags)) + *p++ = 'w'; + if (bitset(EF_RESPONSE, e->e_flags)) + *p++ = 'r'; + *p++ = '\0'; + if (buf[0] != '\0') + fprintf(tfp, "F%s\n", buf); + + /* $r and $s and $_ macro values */ + if ((p = macvalue('r', e)) != NULL) + fprintf(tfp, "$r%s\n", p); + if ((p = macvalue('s', e)) != NULL) + fprintf(tfp, "$s%s\n", p); + if ((p = macvalue('_', e)) != NULL) + fprintf(tfp, "$_%s\n", p); + /* output name of sender */ fprintf(tfp, "S%s\n", e->e_from.q_paddr); + /* output list of error recipients */ + printctladdr(NULL, NULL); + for (q = e->e_errorqueue; q != NULL; q = q->q_next) + { + if (!bitset(QDONTSEND|QBADADDR, q->q_flags)) + { + printctladdr(q, tfp); + fprintf(tfp, "E%s\n", q->q_paddr); + } + } + /* output list of recipient addresses */ for (q = e->e_sendqueue; q != NULL; q = q->q_next) { - if (queueall ? !bitset(QDONTSEND|QSENT, q->q_flags) : - bitset(QQUEUEUP, q->q_flags)) + if (bitset(QQUEUEUP, q->q_flags) || + (queueall && !bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags))) { - char *ctluser, *getctluser(); - - if ((ctluser = getctluser(q)) != NULL) - fprintf(tfp, "C%s\n", ctluser); + printctladdr(q, tfp); fprintf(tfp, "R%s\n", q->q_paddr); if (announce) { e->e_to = q->q_paddr; - message(Arpa_Info, "queued"); - if (LogLevel > 4) - logdelivery("queued"); + message("queued"); + if (LogLevel > 8) + logdelivery(NULL, NULL, "queued", e); e->e_to = NULL; } if (tTd(40, 1)) @@ -197,19 +235,6 @@ queueup(e, queueall, announce) } } - /* output list of error recipients */ - for (q = e->e_errorqueue; q != NULL; q = q->q_next) - { - if (!bitset(QDONTSEND, q->q_flags)) - { - char *ctluser, *getctluser(); - - if ((ctluser = getctluser(q)) != NULL) - fprintf(tfp, "C%s\n", ctluser); - fprintf(tfp, "E%s\n", q->q_paddr); - } - } - /* ** Output headers for this message. ** Expand macros completely here. Queue run will deal with @@ -221,10 +246,11 @@ queueup(e, queueall, announce) */ bzero((char *) &nullmailer, sizeof nullmailer); - nullmailer.m_r_rwset = nullmailer.m_s_rwset = -1; + nullmailer.m_re_rwset = nullmailer.m_rh_rwset = + nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1; nullmailer.m_eol = "\n"; - define('g', "\001f", e); + define('g', "\201f", e); for (h = e->e_header; h != NULL; h = h->h_link) { extern bool bitzerop(); @@ -260,8 +286,18 @@ queueup(e, queueall, announce) } else if (bitset(H_FROM|H_RCPT, h->h_flags)) { - commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags), - &nullmailer); + bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); + FILE *savetrace = TrafficLogFile; + + TrafficLogFile = NULL; + + if (bitset(H_FROM, h->h_flags)) + oldstyle = FALSE; + + commaize(h, h->h_value, tfp, oldstyle, + &nullmailer, e); + + TrafficLogFile = savetrace; } else fprintf(tfp, "%s: %s\n", h->h_field, h->h_value); @@ -271,19 +307,84 @@ queueup(e, queueall, announce) ** Clean up. */ - qf = queuename(e, 'q'); - if (rename(tf, qf) < 0) - syserr("cannot rename(%s, %s), df=%s", tf, qf, e->e_df); + fflush(tfp); + fsync(fileno(tfp)); + if (ferror(tfp)) + { + if (newid) + syserr("!552 Error writing control file %s", tf); + else + syserr("!452 Error writing control file %s", tf); + } + + if (!newid) + { + qf = queuename(e, 'q'); + if (rename(tf, qf) < 0) + syserr("cannot rename(%s, %s), df=%s", tf, qf, e->e_df); + if (e->e_lockfp != NULL) + (void) xfclose(e->e_lockfp, "queueup lockfp", e->e_id); + e->e_lockfp = tfp; + } + else + qf = tf; errno = 0; # ifdef LOG /* save log info */ - if (LogLevel > 15) + if (LogLevel > 79) syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df); -# endif LOG - fflush(tfp); - return tfp; +# endif /* LOG */ + return; } + +printctladdr(a, tfp) + register ADDRESS *a; + FILE *tfp; +{ + char *uname; + register struct passwd *pw; + register ADDRESS *q; + uid_t uid; + static ADDRESS *lastctladdr; + static uid_t lastuid; + + /* initialization */ + if (a == NULL || tfp == NULL) + { + if (lastctladdr != NULL && tfp != NULL) + fprintf(tfp, "C\n"); + lastctladdr = NULL; + lastuid = 0; + return; + } + + /* find the active uid */ + q = getctladdr(a); + if (q == NULL) + uid = 0; + else + uid = q->q_uid; + + /* if a is an alias, use that for printing */ + if (a->q_alias != NULL) + a = a->q_alias; + + /* check to see if this is the same as last time */ + if (lastctladdr != NULL && uid == lastuid && + strcmp(lastctladdr->q_paddr, a->q_paddr) == 0) + return; + lastuid = uid; + lastctladdr = a; + + if (uid == 0 || (pw = getpwuid(uid)) == NULL) + uname = ""; + else + uname = pw->pw_name; + + fprintf(tfp, "C%s:%s\n", uname, a->q_paddr); +} + /* ** RUNQUEUE -- run the jobs in the queue. ** @@ -302,26 +403,28 @@ queueup(e, queueall, announce) ** runs things in the mail queue. */ +ENVELOPE QueueEnvelope; /* the queue run envelope */ + runqueue(forkflag) bool forkflag; { - extern bool shouldqueue(); + register ENVELOPE *e; + extern ENVELOPE BlankEnvelope; /* ** If no work will ever be selected, don't even bother reading ** the queue. */ - la = getla(); /* get load average */ + CurrentLA = getla(); /* get load average */ - if (shouldqueue(-100000000L)) + if (shouldqueue(0L, curtime())) { if (Verbose) printf("Skipping queue run -- load average too high\n"); - - if (forkflag) - return; - finis(); + if (forkflag && QueueIntvl != 0) + (void) setevent(QueueIntvl, runqueue, TRUE); + return; } /* @@ -340,9 +443,9 @@ runqueue(forkflag) /* parent -- pick up intermediate zombie */ #ifndef SIGCHLD (void) waitfor(pid); -#else SIGCHLD +#else /* SIGCHLD */ (void) signal(SIGCHLD, reapchild); -#endif SIGCHLD +#endif /* SIGCHLD */ if (QueueIntvl != 0) (void) setevent(QueueIntvl, runqueue, TRUE); return; @@ -351,17 +454,18 @@ runqueue(forkflag) #ifndef SIGCHLD if (fork() != 0) exit(EX_OK); -#else SIGCHLD +#else /* SIGCHLD */ (void) signal(SIGCHLD, SIG_DFL); -#endif SIGCHLD +#endif /* SIGCHLD */ } setproctitle("running queue: %s", QueueDir); # ifdef LOG - if (LogLevel > 11) - syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid()); -# endif LOG + if (LogLevel > 69) + syslog(LOG_DEBUG, "runqueue %s, pid=%d, forkflag=%d", + QueueDir, getpid(), forkflag); +# endif /* LOG */ /* ** Release any resources used by the daemon code. @@ -369,13 +473,21 @@ runqueue(forkflag) # ifdef DAEMON clrdaemon(); -# endif DAEMON +# endif /* DAEMON */ + + /* + ** Create ourselves an envelope + */ + + CurEnv = &QueueEnvelope; + e = newenvelope(&QueueEnvelope, CurEnv); + e->e_flags = BlankEnvelope.e_flags; /* ** Make sure the alias database is open. */ - initaliases(AliasFile, FALSE); + initmaps(FALSE, e); /* ** Start making passes through the queue. @@ -393,13 +505,27 @@ runqueue(forkflag) WORK *w = WorkQ; WorkQ = WorkQ->w_next; - dowork(w); + + /* + ** Ignore jobs that are too expensive for the moment. + */ + + if (shouldqueue(w->w_pri, w->w_ctime)) + { + if (Verbose) + printf("\nSkipping %s\n", w->w_name + 2); + } + else + { + dowork(w->w_name + 2, ForkQueueRuns, FALSE, e); + } free(w->w_name); free((char *) w); } /* exit without the usual cleanup */ - exit(ExitStat); + e->e_id = NULL; + finis(); } /* ** ORDERQ -- order the work queue. @@ -420,11 +546,13 @@ runqueue(forkflag) # define NEED_P 001 # define NEED_T 002 +# define NEED_R 004 +# define NEED_S 010 orderq(doall) bool doall; { - register struct direct *d; + register struct dirent *d; register WORK *w; DIR *f; register int i; @@ -432,6 +560,17 @@ orderq(doall) int wn = -1; extern workcmpf(); + if (tTd(41, 1)) + { + printf("orderq:\n"); + if (QueueLimitId != NULL) + printf("\tQueueLimitId = %s\n", QueueLimitId); + if (QueueLimitSender != NULL) + printf("\tQueueLimitSender = %s\n", QueueLimitSender); + if (QueueLimitRecipient != NULL) + printf("\tQueueLimitRecipient = %s\n", QueueLimitRecipient); + } + /* clear out old WorkQ */ for (w = WorkQ; w != NULL; ) { @@ -459,14 +598,43 @@ orderq(doall) { FILE *cf; char lbuf[MAXNAME]; + extern bool strcontainedin(); /* is this an interesting entry? */ if (d->d_name[0] != 'q' || d->d_name[1] != 'f') continue; + if (QueueLimitId != NULL && + !strcontainedin(QueueLimitId, d->d_name)) + continue; + + /* + ** Check queue name for plausibility. This handles + ** both old and new type ids. + */ + + i = strlen(d->d_name); + if (i != 9 && i != 10) + { + if (Verbose) + printf("orderq: bogus qf name %s\n", d->d_name); +#ifdef LOG + if (LogLevel > 3) + syslog(LOG_CRIT, "orderq: bogus qf name %s", + d->d_name); +#endif + if (strlen(d->d_name) >= MAXNAME) + d->d_name[MAXNAME - 1] = '\0'; + strcpy(lbuf, d->d_name); + lbuf[0] = 'Q'; + (void) rename(d->d_name, lbuf); + continue; + } + /* yes -- open control file (if not too many files) */ if (++wn >= QUEUESIZE) continue; + cf = fopen(d->d_name, "r"); if (cf == NULL) { @@ -488,9 +656,14 @@ orderq(doall) /* extract useful information */ i = NEED_P | NEED_T; + if (QueueLimitSender != NULL) + i |= NEED_S; + if (QueueLimitRecipient != NULL) + i |= NEED_R; while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) { extern long atol(); + extern bool strcontainedin(); switch (lbuf[0]) { @@ -503,11 +676,24 @@ orderq(doall) w->w_ctime = atol(&lbuf[1]); i &= ~NEED_T; break; + + case 'R': + if (QueueLimitRecipient != NULL && + strcontainedin(QueueLimitRecipient, &lbuf[1])) + i &= ~NEED_R; + break; + + case 'S': + if (QueueLimitSender != NULL && + strcontainedin(QueueLimitSender, &lbuf[1])) + i &= ~NEED_S; + break; } } (void) fclose(cf); - if (!doall && shouldqueue(w->w_pri)) + if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) || + bitset(NEED_R|NEED_S, i)) { /* don't even bother sorting this job in */ wn--; @@ -566,8 +752,8 @@ workcmpf(a, b) register WORK *a; register WORK *b; { - long pa = a->w_pri + a->w_ctime; - long pb = b->w_pri + b->w_ctime; + long pa = a->w_pri; + long pb = b->w_pri; if (pa == pb) return (0); @@ -580,7 +766,13 @@ workcmpf(a, b) ** DOWORK -- do a work request. ** ** Parameters: -** w -- the work request to be satisfied. +** id -- the ID of the job to run. +** forkflag -- if set, run this in background. +** requeueflag -- if set, reinstantiate the queue quickly. +** This is used when expanding aliases in the queue. +** If forkflag is also set, it doesn't wait for the +** child. +** e - the envelope in which to run it. ** ** Returns: ** none. @@ -589,31 +781,23 @@ workcmpf(a, b) ** The work request is satisfied if possible. */ -dowork(w) - register WORK *w; +dowork(id, forkflag, requeueflag, e) + char *id; + bool forkflag; + bool requeueflag; + register ENVELOPE *e; { register int i; - extern bool shouldqueue(); + extern bool readqf(); if (tTd(40, 1)) - printf("dowork: %s pri %ld\n", w->w_name, w->w_pri); - - /* - ** Ignore jobs that are too expensive for the moment. - */ - - if (shouldqueue(w->w_pri)) - { - if (Verbose) - printf("\nSkipping %s\n", w->w_name + 2); - return; - } + printf("dowork(%s)\n", id); /* ** Fork for work. */ - if (ForkQueueRuns) + if (forkflag) { i = fork(); if (i < 0) @@ -629,7 +813,6 @@ dowork(w) if (i == 0) { - FILE *qflock, *readqf(); /* ** CHILD ** Lock the control file to avoid duplicate deliveries. @@ -640,44 +823,46 @@ dowork(w) /* set basic modes, etc. */ (void) alarm(0); - clearenvelope(CurEnv, FALSE); - QueueRun = TRUE; - ErrorMode = EM_MAIL; - CurEnv->e_id = &w->w_name[2]; + clearenvelope(e, FALSE); + e->e_flags |= EF_QUEUERUN; + e->e_errormode = EM_MAIL; + e->e_id = id; # ifdef LOG - if (LogLevel > 11) - syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id, + if (LogLevel > 76) + syslog(LOG_DEBUG, "%s: dowork, pid=%d", e->e_id, getpid()); -# endif LOG +# endif /* LOG */ /* don't use the headers from sendmail.cf... */ - CurEnv->e_header = NULL; + e->e_header = NULL; - /* read the queue control file */ - /* and lock the control file during processing */ - if ((qflock=readqf(CurEnv, TRUE)) == NULL) + /* read the queue control file -- return if locked */ + if (!readqf(e)) { - if (ForkQueueRuns) + if (tTd(40, 4)) + printf("readqf(%s) failed\n", e->e_id); + if (forkflag) exit(EX_OK); else return; } - CurEnv->e_flags |= EF_INQUEUE; - eatheader(CurEnv); + e->e_flags |= EF_INQUEUE; + eatheader(e, requeueflag); + + if (requeueflag) + queueup(e, TRUE, FALSE); /* do the delivery */ - if (!bitset(EF_FATALERRS, CurEnv->e_flags)) - sendall(CurEnv, SM_DELIVER); + sendall(e, SM_DELIVER); /* finish up and exit */ - if (ForkQueueRuns) + if (forkflag) finis(); else - dropenvelope(CurEnv); - fclose(qflock); + dropenvelope(e); } - else + else if (!requeueflag) { /* ** Parent -- pick up results. @@ -692,137 +877,199 @@ dowork(w) ** ** Parameters: ** e -- the envelope of the job to run. -** full -- if set, read in all information. Otherwise just -** read in info needed for a queue print. ** ** Returns: -** FILE * pointing to flock()ed fd so it can be closed -** after the mail is delivered +** TRUE if it successfully read the queue file. +** FALSE otherwise. ** ** Side Effects: -** cf is read and created as the current job, as though -** we had been invoked by argument. +** The queue file is returned locked. */ -FILE * -readqf(e, full) +bool +readqf(e) register ENVELOPE *e; - bool full; { - char *qf; register FILE *qfp; - char buf[MAXFIELD]; - extern char *fgetfolded(); + ADDRESS *ctladdr; + struct stat st; + char *bp; + char qf[20]; + char buf[MAXLINE]; extern long atol(); - int gotctluser = 0; - int fd; + extern ADDRESS *setctluser(); /* ** Read and process the file. */ - qf = queuename(e, 'q'); - qfp = fopen(qf, "r"); + strcpy(qf, queuename(e, 'q')); + qfp = fopen(qf, "r+"); if (qfp == NULL) { + if (tTd(40, 8)) + printf("readqf(%s): fopen failure (%s)\n", + qf, errstring(errno)); if (errno != ENOENT) syserr("readqf: no control file %s", qf); - return NULL; + return FALSE; + } + + /* + ** Check the queue file for plausibility to avoid attacks. + */ + + if (fstat(fileno(qfp), &st) < 0) + { + /* must have been being processed by someone else */ + if (tTd(40, 8)) + printf("readqf(%s): fstat failure (%s)\n", + qf, errstring(errno)); + fclose(qfp); + return FALSE; } - if (flock(fileno(qfp), LOCK_EX|LOCK_NB) < 0) + if (st.st_uid != geteuid() || (st.st_mode & 07777) != FileMode) { # ifdef LOG + if (LogLevel > 0) + { + syslog(LOG_ALERT, "%s: bogus queue file, uid=%d, mode=%o", + e->e_id, st.st_uid, st.st_mode); + } +# endif /* LOG */ + if (tTd(40, 8)) + printf("readqf(%s): bogus file\n", qf); + fclose(qfp); + rename(qf, queuename(e, 'Q')); + return FALSE; + } + + if (!lockfile(fileno(qfp), qf, LOCK_EX|LOCK_NB)) + { /* being processed by another queuer */ + if (tTd(40, 8)) + printf("readqf(%s): locked\n", qf); if (Verbose) - printf("%s: locked\n", CurEnv->e_id); -# endif LOG + printf("%s: locked\n", e->e_id); +# ifdef LOG + if (LogLevel > 19) + syslog(LOG_DEBUG, "%s: locked", e->e_id); +# endif /* LOG */ (void) fclose(qfp); - return NULL; + return FALSE; + } + + if (st.st_size == 0) + { + /* must be a bogus file -- just remove it */ + (void) unlink(qf); + fclose(qfp); + return FALSE; } + /* save this lock */ + e->e_lockfp = qfp; + /* do basic system initialization */ - initsys(); + initsys(e); FileName = qf; LineNumber = 0; - if (Verbose && full) + if (Verbose) printf("\nRunning %s\n", e->e_id); - while (fgetfolded(buf, sizeof buf, qfp) != NULL) + ctladdr = NULL; + while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL) { + register char *p; + struct stat st; + if (tTd(40, 4)) - printf("+++++ %s\n", buf); - switch (buf[0]) + printf("+++++ %s\n", bp); + switch (bp[0]) { case 'C': /* specify controlling user */ - setctluser(&buf[1]); - gotctluser = 1; + ctladdr = setctluser(&bp[1]); break; case 'R': /* specify recipient */ - sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue); + (void) sendtolist(&bp[1], ctladdr, &e->e_sendqueue, e); break; case 'E': /* specify error recipient */ - sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_errorqueue); + (void) sendtolist(&bp[1], ctladdr, &e->e_errorqueue, e); break; case 'H': /* header */ - if (full) - (void) chompheader(&buf[1], FALSE); + (void) chompheader(&bp[1], FALSE, e); break; case 'M': /* message */ - e->e_message = newstr(&buf[1]); + e->e_message = newstr(&bp[1]); break; case 'S': /* sender */ - setsender(newstr(&buf[1])); + setsender(newstr(&bp[1]), e, NULL, TRUE); + break; + + case 'B': /* body type */ + e->e_bodytype = newstr(&bp[1]); break; case 'D': /* data file name */ - if (!full) - break; - e->e_df = newstr(&buf[1]); + e->e_df = newstr(&bp[1]); e->e_dfp = fopen(e->e_df, "r"); if (e->e_dfp == NULL) + { syserr("readqf: cannot open %s", e->e_df); + e->e_msgsize = -1; + } + else if (fstat(fileno(e->e_dfp), &st) >= 0) + e->e_msgsize = st.st_size; break; case 'T': /* init time */ - e->e_ctime = atol(&buf[1]); + e->e_ctime = atol(&bp[1]); break; case 'P': /* message priority */ - e->e_msgpriority = atol(&buf[1]) + WkTimeFact; + e->e_msgpriority = atol(&bp[1]) + WkTimeFact; + break; + + case 'F': /* flag bits */ + for (p = &bp[1]; *p != '\0'; p++) + { + switch (*p) + { + case 'w': /* warning sent */ + e->e_flags |= EF_WARNING; + break; + + case 'r': /* response */ + e->e_flags |= EF_RESPONSE; + break; + } + } + break; + + case '$': /* define macro */ + define(bp[1], newstr(&bp[2]), e); break; case '\0': /* blank line; ignore */ break; default: - syserr("readqf(%s:%d): bad line \"%s\"", e->e_id, - LineNumber, buf); - break; - } - /* - ** The `C' queue file command operates on the next line, - ** so we use "gotctluser" to maintain state as follows: - ** 0 - no controlling user, - ** 1 - controlling user has been set but not used, - ** 2 - controlling user must be used on next iteration. - */ - if (gotctluser == 1) - gotctluser++; - else if (gotctluser == 2) - { - clrctluser(); - gotctluser = 0; + syserr("readqf: bad line \"%s\"", e->e_id, + LineNumber, bp); + fclose(qfp); + rename(qf, queuename(e, 'Q')); + return FALSE; } - } - /* clear controlling user in case we break out prematurely */ - clrctluser(); + if (bp != buf) + free(bp); + } FileName = NULL; @@ -836,7 +1083,7 @@ readqf(e, full) errno = 0; e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE; } - return qfp; + return TRUE; } /* ** PRINTQUEUE -- print out a representation of the mail queue @@ -857,7 +1104,41 @@ printqueue() FILE *f; int nrequests; char buf[MAXLINE]; - char cbuf[MAXLINE]; + + /* + ** Check for permission to print the queue + */ + + if (bitset(PRIV_RESTRMAILQ, PrivacyFlags) && RealUid != 0) + { + struct stat st; +# ifdef NGROUPS + int n; + int gidset[NGROUPS]; +# endif + + if (stat(QueueDir, &st) < 0) + { + syserr("Cannot stat %s", QueueDir); + return; + } +# ifdef NGROUPS + n = getgroups(NGROUPS, gidset); + while (--n >= 0) + { + if (gidset[n] == st.st_gid) + break; + } + if (n < 0) +# else + if (RealGid != st.st_gid) +# endif + { + usrerr("510 You are not permitted to see the queue"); + setstat(EX_NOPERM); + return; + } + } /* ** Read and order the queue. @@ -876,22 +1157,23 @@ printqueue() return; } - la = getla(); /* get load average */ + CurrentLA = getla(); /* get load average */ printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); if (nrequests > QUEUESIZE) printf(", only %d printed", QUEUESIZE); if (Verbose) - printf(")\n--QID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); + printf(")\n--Q-ID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); else - printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); + printf(")\n--Q-ID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); for (w = WorkQ; w != NULL; w = w->w_next) { struct stat st; auto time_t submittime = 0; long dfsize = -1; + int flags = 0; char message[MAXLINE]; - extern bool shouldqueue(); + char bodytype[MAXNAME]; f = fopen(w->w_name, "r"); if (f == NULL) @@ -899,58 +1181,68 @@ printqueue() errno = 0; continue; } - printf("%7s", w->w_name + 2); - if (flock(fileno(f), LOCK_SH|LOCK_NB) < 0) + printf("%8s", w->w_name + 2); + if (!lockfile(fileno(f), w->w_name, LOCK_SH|LOCK_NB)) printf("*"); - else if (shouldqueue(w->w_pri)) + else if (shouldqueue(w->w_pri, w->w_ctime)) printf("X"); else printf(" "); errno = 0; - message[0] = '\0'; - cbuf[0] = '\0'; + message[0] = bodytype[0] = '\0'; while (fgets(buf, sizeof buf, f) != NULL) { + register int i; + register char *p; + fixcrlf(buf, TRUE); switch (buf[0]) { case 'M': /* error message */ - (void) strcpy(message, &buf[1]); + if ((i = strlen(&buf[1])) >= sizeof message) + i = sizeof message - 1; + bcopy(&buf[1], message, i); + message[i] = '\0'; + break; + + case 'B': /* body type */ + if ((i = strlen(&buf[1])) >= sizeof bodytype) + i = sizeof bodytype - 1; + bcopy(&buf[1], bodytype, i); + bodytype[i] = '\0'; break; case 'S': /* sender name */ if (Verbose) - printf("%8ld %10ld %.12s %.38s", dfsize, - w->w_pri, ctime(&submittime) + 4, + printf("%8ld %10ld%c%.12s %.38s", + dfsize, + w->w_pri, + bitset(EF_WARNING, flags) ? '+' : ' ', + ctime(&submittime) + 4, &buf[1]); else printf("%8ld %.16s %.45s", dfsize, ctime(&submittime), &buf[1]); - if (message[0] != '\0') - printf("\n\t\t (%.60s)", message); + if (message[0] != '\0' || bodytype[0] != '\0') + { + printf("\n %10.10s", bodytype); + if (message[0] != '\0') + printf(" (%.60s)", message); + } break; + case 'C': /* controlling user */ - if (strlen(buf) < MAXLINE-3) /* sanity */ - (void) strcat(buf, ") "); - cbuf[0] = cbuf[1] = '('; - (void) strncpy(&cbuf[2], &buf[1], MAXLINE-1); - cbuf[MAXLINE-1] = '\0'; + if (Verbose) + printf("\n\t\t\t\t (---%.34s---)", + &buf[1]); break; case 'R': /* recipient name */ - if (cbuf[0] != '\0') { - /* prepend controlling user to `buf' */ - (void) strncat(cbuf, &buf[1], - MAXLINE-strlen(cbuf)); - cbuf[MAXLINE-1] = '\0'; - (void) strcpy(buf, cbuf); - cbuf[0] = '\0'; - } if (Verbose) - printf("\n\t\t\t\t\t %.38s", &buf[1]); + printf("\n\t\t\t\t\t %.38s", &buf[1]); else - printf("\n\t\t\t\t %.45s", &buf[1]); + printf("\n\t\t\t\t %.45s", &buf[1]); break; case 'T': /* creation time */ @@ -961,6 +1253,17 @@ printqueue() if (stat(&buf[1], &st) >= 0) dfsize = st.st_size; break; + + case 'F': /* flag bits */ + for (p = &buf[1]; *p != '\0'; p++) + { + switch (*p) + { + case 'w': + flags |= EF_WARNING; + break; + } + } } } if (submittime == (time_t) 0) @@ -970,7 +1273,7 @@ printqueue() } } -# endif QUEUE +# endif /* QUEUE */ /* ** QUEUENAME -- build a file name in the queue directory for this envelope. ** @@ -987,20 +1290,23 @@ printqueue() ** a pointer to the new file name (in a static buffer). ** ** Side Effects: -** Will create the qf file if no id code is -** already assigned. This will cause the envelope -** to be modified. +** If no id code is already assigned, queuename will +** assign an id code, create a qf file, and leave a +** locked, open-for-write file pointer in the envelope. */ char * queuename(e, type) register ENVELOPE *e; - char type; + int type; { - static char buf[MAXNAME]; static int pid = -1; - char c1 = 'A'; - char c2 = 'A'; + static char c0; + static char c1; + static char c2; + time_t now; + struct tm *tm; + static char buf[MAXNAME]; if (e->e_id == NULL) { @@ -1011,10 +1317,13 @@ queuename(e, type) { /* new process -- start back at "AA" */ pid = getpid(); + now = curtime(); + tm = localtime(&now); + c0 = 'A' + tm->tm_hour; c1 = 'A'; c2 = 'A' - 1; } - (void) sprintf(qf, "qfAA%05d", pid); + (void) sprintf(qf, "qf%cAA%05d", c0, pid); while (c1 < '~' || c2 < 'Z') { @@ -1025,22 +1334,28 @@ queuename(e, type) c1++; c2 = 'A' - 1; } - qf[2] = c1; - qf[3] = ++c2; + qf[3] = c1; + qf[4] = ++c2; if (tTd(7, 20)) printf("queuename: trying \"%s\"\n", qf); i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode); - if (i < 0) { - if (errno != EEXIST) { - syserr("queuename: Cannot create \"%s\" in \"%s\"", - qf, QueueDir); - exit(EX_UNAVAILABLE); - } - } else { - (void) close(i); + if (i < 0) + { + if (errno == EEXIST) + continue; + syserr("queuename: Cannot create \"%s\" in \"%s\"", + qf, QueueDir); + exit(EX_UNAVAILABLE); + } + if (lockfile(i, qf, LOCK_EX|LOCK_NB)) + { + e->e_lockfp = fdopen(i, "w"); break; } + + /* a reader got the file; abandon it and try again */ + (void) close(i); } if (c1 >= '~' && c2 >= 'Z') { @@ -1053,9 +1368,9 @@ queuename(e, type) if (tTd(7, 1)) printf("queuename: assigned id %s, env=%x\n", e->e_id, e); # ifdef LOG - if (LogLevel > 16) + if (LogLevel > 93) syslog(LOG_DEBUG, "%s: assigned id", e->e_id); -# endif LOG +# endif /* LOG */ } if (type == '\0') @@ -1081,155 +1396,86 @@ queuename(e, type) unlockqueue(e) ENVELOPE *e; { + if (tTd(51, 4)) + printf("unlockqueue(%s)\n", e->e_id); + + /* if there is a lock file in the envelope, close it */ + if (e->e_lockfp != NULL) + xfclose(e->e_lockfp, "unlockqueue", e->e_id); + e->e_lockfp = NULL; + + /* don't create a queue id if we don't already have one */ + if (e->e_id == NULL) + return; + /* remove the transcript */ # ifdef LOG - if (LogLevel > 19) + if (LogLevel > 87) syslog(LOG_DEBUG, "%s: unlock", e->e_id); -# endif LOG - if (!tTd(51, 4)) +# endif /* LOG */ + if (!tTd(51, 104)) xunlink(queuename(e, 'x')); } /* -** GETCTLUSER -- return controlling user if mailing to prog or file +** SETCTLUSER -- create a controlling address ** -** Check for a "|" or "/" at the beginning of the address. If -** found, return a controlling username. +** Create a fake "address" given only a local login name; this is +** used as a "controlling user" for future recipient addresses. ** ** Parameters: -** a - the address to check out +** user -- the user name of the controlling user. ** ** Returns: -** Either NULL, if we werent mailing to a program or file, -** or a controlling user name (possibly in getpwuid's -** static buffer). +** An address descriptor for the controlling user. ** ** Side Effects: ** none. */ -char * -getctluser(a) - ADDRESS *a; +ADDRESS * +setctluser(user) + char *user; { - extern ADDRESS *getctladdr(); + register ADDRESS *a; struct passwd *pw; - char *retstr; + char *p; /* - ** Get unquoted user for file, program or user.name check. - ** N.B. remove this code block to always emit controlling - ** addresses (at the expense of backward compatibility). + ** See if this clears our concept of controlling user. */ - { - char buf[MAXNAME]; - (void) strncpy(buf, a->q_paddr, MAXNAME); - buf[MAXNAME-1] = '\0'; - stripquotes(buf, TRUE); - - if (buf[0] != '|' && buf[0] != '/') - return((char *)NULL); - } - - a = getctladdr(a); /* find controlling address */ - - if (a != NULL && a->q_uid != 0 && (pw = getpwuid(a->q_uid)) != NULL) - retstr = pw->pw_name; - else /* use default user */ - retstr = DefUser; - - if (tTd(40, 5)) - printf("Set controlling user for `%s' to `%s'\n", - (a == NULL)? "<null>": a->q_paddr, retstr); - - return(retstr); -} - /* -** SETCTLUSER - sets `CtlUser' to controlling user -** CLRCTLUSER - clears controlling user (no params, nothing returned) -** -** These routines manipulate `CtlUser'. -** -** Parameters: -** str - controlling user as passed to setctluser() -** -** Returns: -** None. -** -** Side Effects: -** `CtlUser' is changed. -*/ - -static char CtlUser[MAXNAME]; - -setctluser(str) -register char *str; -{ - (void) strncpy(CtlUser, str, MAXNAME); - CtlUser[MAXNAME-1] = '\0'; -} - -clrctluser() -{ - CtlUser[0] = '\0'; -} - - /* -** SETCTLADDR -- create a controlling address -** -** If global variable `CtlUser' is set and we are given a valid -** address, make that address a controlling address; change the -** `q_uid', `q_gid', and `q_ruser' fields and set QGOODUID. -** -** Parameters: -** a - address for which control uid/gid info may apply -** -** Returns: -** None. -** -** Side Effects: -** Fills in uid/gid fields in address and sets QGOODUID -** flag if appropriate. -*/ - -setctladdr(a) - ADDRESS *a; -{ - struct passwd *pw; + if (user == NULL) + user = ""; /* - ** If there is no current controlling user, or we were passed a - ** NULL addr ptr or we already have a controlling user, return. + ** Set up addr fields for controlling user. */ - if (CtlUser[0] == '\0' || a == NULL || a->q_ruser) - return; + a = (ADDRESS *) xalloc(sizeof *a); + bzero((char *) a, sizeof *a); - /* - ** Set up addr fields for controlling user. If `CtlUser' is no - ** longer valid, use the default user/group. - */ - - if ((pw = getpwnam(CtlUser)) != NULL) + p = strchr(user, ':'); + if (p != NULL) + *p++ = '\0'; + if (*user != '\0' && (pw = getpwnam(user)) != NULL) { - if (a->q_home) - free(a->q_home); a->q_home = newstr(pw->pw_dir); a->q_uid = pw->pw_uid; a->q_gid = pw->pw_gid; - a->q_ruser = newstr(CtlUser); + a->q_user = newstr(user); + a->q_flags |= QGOODUID; } else { - a->q_uid = DefUid; - a->q_gid = DefGid; - a->q_ruser = newstr(DefUser); + a->q_user = newstr(DefUser); } - a->q_flags |= QGOODUID; /* flag as a "ctladdr" */ - - if (tTd(40, 5)) - printf("Restored controlling user for `%s' to `%s'\n", - a->q_paddr, a->q_ruser); + a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */ + a->q_mailer = LocalMailer; + if (p == NULL) + a->q_paddr = a->q_user; + else + a->q_paddr = newstr(p); + return a; } diff --git a/usr.sbin/sendmail/src/readcf.c b/usr.sbin/sendmail/src/readcf.c index bd2bad74dd..7af9d5a4c3 100644 --- a/usr.sbin/sendmail/src/readcf.c +++ b/usr.sbin/sendmail/src/readcf.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,10 +33,14 @@ */ #ifndef lint -static char sccsid[] = "@(#)readcf.c 5.22 (Berkeley) 3/12/91"; +static char sccsid[] = "@(#)readcf.c 8.2 (Berkeley) 7/13/93"; #endif /* not lint */ # include "sendmail.h" +#ifdef NAMED_BIND +# include <arpa/nameser.h> +# include <resolv.h> +#endif /* ** READCF -- read control file. @@ -64,9 +68,16 @@ static char sccsid[] = "@(#)readcf.c 5.22 (Berkeley) 3/12/91"; ** Args specify mailer parameters. ** Oxvalue Set option x to value. ** Pname=value Set precedence name to value. +** Vversioncode Version level of configuration syntax. +** Kmapname mapclass arguments.... +** Define keyed lookup of a given class. +** Arguments are class dependent. ** ** Parameters: ** cfname -- control file name. +** safe -- TRUE if this is the system config file; +** FALSE otherwise. +** e -- the main envelope. ** ** Returns: ** none. @@ -75,37 +86,106 @@ static char sccsid[] = "@(#)readcf.c 5.22 (Berkeley) 3/12/91"; ** Builds several internal tables. */ -readcf(cfname) +readcf(cfname, safe, e) char *cfname; + bool safe; + register ENVELOPE *e; { FILE *cf; int ruleset = 0; char *q; - char **pv; struct rewrite *rwp = NULL; + char *bp; + int nfuzzy; char buf[MAXLINE]; register char *p; - extern char **prescan(); extern char **copyplist(); + struct stat statb; char exbuf[MAXLINE]; char pvpbuf[PSBUFSIZE]; - extern char *fgetfolded(); extern char *munchstring(); + extern void makemapentry(); + + FileName = cfname; + LineNumber = 0; cf = fopen(cfname, "r"); if (cf == NULL) { - syserr("cannot open %s", cfname); + syserr("cannot open"); exit(EX_OSFILE); } - FileName = cfname; - LineNumber = 0; - while (fgetfolded(buf, sizeof buf, cf) != NULL) + if (fstat(fileno(cf), &statb) < 0) + { + syserr("cannot fstat"); + exit(EX_OSFILE); + } + + if (!S_ISREG(statb.st_mode)) + { + syserr("not a plain file"); + exit(EX_OSFILE); + } + + if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) + { + if (OpMode == MD_DAEMON || OpMode == MD_FREEZE) + fprintf(stderr, "%s: WARNING: dangerous write permissions\n", + FileName); +#ifdef LOG + if (LogLevel > 0) + syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions", + FileName); +#endif + } + +#ifdef XLA + xla_zero(); +#endif + + while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) { - /* map $ into \001 (ASCII SOH) for macro expansion */ - for (p = buf; *p != '\0'; p++) + if (bp[0] == '#') { + if (bp != buf) + free(bp); + continue; + } + + /* map $ into \201 for macro expansion */ + for (p = bp; *p != '\0'; p++) + { + if (*p == '#' && p > bp && ConfigLevel >= 3) + { + /* this is an on-line comment */ + register char *e; + + switch (*--p & 0377) + { + case MACROEXPAND: + /* it's from $# -- let it go through */ + p++; + break; + + case '\\': + /* it's backslash escaped */ + (void) strcpy(p, p + 1); + break; + + default: + /* delete preceeding white space */ + while (isascii(*p) && isspace(*p) && p > bp) + p--; + if ((e = strchr(++p, '\n')) != NULL) + (void) strcpy(p, e); + else + p[0] = p[1] = '\0'; + break; + } + continue; + } + if (*p != '$') continue; @@ -117,23 +197,23 @@ readcf(cfname) } /* convert to macro expansion character */ - *p = '\001'; + *p = MACROEXPAND; } /* interpret this line */ - switch (buf[0]) + switch (bp[0]) { case '\0': case '#': /* comment */ break; case 'R': /* rewriting rule */ - for (p = &buf[1]; *p != '\0' && *p != '\t'; p++) + for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) continue; if (*p == '\0') { - syserr("invalid rewrite line \"%s\"", buf); + syserr("invalid rewrite line \"%s\"", bp); break; } @@ -152,11 +232,83 @@ readcf(cfname) /* expand and save the LHS */ *p = '\0'; - expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv); - rwp->r_lhs = prescan(exbuf, '\t', pvpbuf); + expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e); + rwp->r_lhs = prescan(exbuf, '\t', pvpbuf, NULL); + nfuzzy = 0; if (rwp->r_lhs != NULL) + { + register char **ap; + rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); + /* count the number of fuzzy matches in LHS */ + for (ap = rwp->r_lhs; *ap != NULL; ap++) + { + char *botch; + + botch = NULL; + switch (**ap & 0377) + { + case MATCHZANY: + case MATCHANY: + case MATCHONE: + case MATCHCLASS: + case MATCHNCLASS: + nfuzzy++; + break; + + case MATCHREPL: + botch = "$0-$9"; + break; + + case CANONNET: + botch = "$#"; + break; + + case CANONUSER: + botch = "$:"; + break; + + case CALLSUBR: + botch = "$>"; + break; + + case CONDIF: + botch = "$?"; + break; + + case CONDELSE: + botch = "$|"; + break; + + case CONDFI: + botch = "$."; + break; + + case HOSTBEGIN: + botch = "$["; + break; + + case HOSTEND: + botch = "$]"; + break; + + case LOOKUPBEGIN: + botch = "$("; + break; + + case LOOKUPEND: + botch = "$)"; + break; + } + if (botch != NULL) + syserr("Inappropriate use of %s on LHS", + botch); + } + } + else + syserr("R line: null LHS"); + /* expand and save the RHS */ while (*++p == '\t') continue; @@ -164,14 +316,62 @@ readcf(cfname) while (*p != '\0' && *p != '\t') p++; *p = '\0'; - expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv); - rwp->r_rhs = prescan(exbuf, '\t', pvpbuf); + expand(q, exbuf, &exbuf[sizeof exbuf], e); + rwp->r_rhs = prescan(exbuf, '\t', pvpbuf, NULL); if (rwp->r_rhs != NULL) + { + register char **ap; + rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); + + /* check no out-of-bounds replacements */ + nfuzzy += '0'; + for (ap = rwp->r_rhs; *ap != NULL; ap++) + { + char *botch; + + botch = NULL; + switch (**ap & 0377) + { + case MATCHREPL: + if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) + { + syserr("replacement $%c out of bounds", + (*ap)[1]); + } + break; + + case MATCHZANY: + botch = "$*"; + break; + + case MATCHANY: + botch = "$+"; + break; + + case MATCHONE: + botch = "$-"; + break; + + case MATCHCLASS: + botch = "$="; + break; + + case MATCHNCLASS: + botch = "$~"; + break; + } + if (botch != NULL) + syserr("Inappropriate use of %s on RHS", + botch); + } + } + else + syserr("R line: null RHS"); break; case 'S': /* select rewriting set */ - ruleset = atoi(&buf[1]); + ruleset = atoi(&bp[1]); if (ruleset >= MAXRWSETS || ruleset < 0) { syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS); @@ -181,58 +381,68 @@ readcf(cfname) break; case 'D': /* macro definition */ - define(buf[1], newstr(munchstring(&buf[2])), CurEnv); + define(bp[1], newstr(munchstring(&bp[2], NULL)), e); break; case 'H': /* required header line */ - (void) chompheader(&buf[1], TRUE); + (void) chompheader(&bp[1], TRUE, e); break; case 'C': /* word class */ - case 'F': /* word class from file */ - /* read list of words from argument or file */ - if (buf[0] == 'F') - { - /* read from file */ - for (p = &buf[2]; *p != '\0' && !isspace(*p); p++) - continue; - if (*p == '\0') - p = "%s"; - else - { - *p = '\0'; - while (isspace(*++p)) - continue; - } - fileclass(buf[1], &buf[2], p); - break; - } - /* scan the list of words and set class for all */ - for (p = &buf[2]; *p != '\0'; ) + for (p = &bp[2]; *p != '\0'; ) { register char *wd; char delim; - while (*p != '\0' && isspace(*p)) + while (*p != '\0' && isascii(*p) && isspace(*p)) p++; wd = p; - while (*p != '\0' && !isspace(*p)) + while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; delim = *p; *p = '\0'; if (wd[0] != '\0') - setclass(buf[1], wd); + { + if (tTd(37, 2)) + printf("setclass(%c, %s)\n", + bp[1], wd); + setclass(bp[1], wd); + } *p = delim; } break; + case 'F': /* word class from file */ + /* read list of words from argument or file */ + /* read from file */ + for (p = &bp[2]; + *p != '\0' && !(isascii(*p) && isspace(*p)); + p++) + continue; + if (*p == '\0') + p = "%s"; + else + { + *p = '\0'; + while (isascii(*++p) && isspace(*p)) + continue; + } + fileclass(bp[1], &bp[2], p, safe); + break; + +#ifdef XLA + case 'L': /* extended load average description */ + xla_init(&bp[1]); + break; +#endif + case 'M': /* define mailer */ - makemailer(&buf[1]); + makemailer(&bp[1]); break; case 'O': /* set option */ - setoption(buf[1], &buf[2], TRUE, FALSE); + setoption(bp[1], &bp[2], safe, FALSE, e); break; case 'P': /* set precedence */ @@ -241,46 +451,51 @@ readcf(cfname) toomany('P', MAXPRIORITIES); break; } - for (p = &buf[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) + for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) continue; if (*p == '\0') goto badline; *p = '\0'; - Priorities[NumPriorities].pri_name = newstr(&buf[1]); + Priorities[NumPriorities].pri_name = newstr(&bp[1]); Priorities[NumPriorities].pri_val = atoi(++p); NumPriorities++; break; case 'T': /* trusted user(s) */ - p = &buf[1]; - while (*p != '\0') - { - while (isspace(*p)) - p++; - q = p; - while (*p != '\0' && !isspace(*p)) - p++; - if (*p != '\0') - *p++ = '\0'; - if (*q == '\0') - continue; - for (pv = TrustedUsers; *pv != NULL; pv++) - continue; - if (pv >= &TrustedUsers[MAXTRUST]) - { - toomany('T', MAXTRUST); - break; - } - *pv = newstr(q); - } + /* this option is obsolete, but will be ignored */ + break; + + case 'V': /* configuration syntax version */ + ConfigLevel = atoi(&bp[1]); + break; + + case 'K': + makemapentry(&bp[1]); break; default: badline: - syserr("unknown control line \"%s\"", buf); + syserr("unknown control line \"%s\"", bp); } + if (bp != buf) + free(bp); } + if (ferror(cf)) + { + syserr("I/O read error", cfname); + exit(EX_OSFILE); + } + fclose(cf); FileName = NULL; + + if (stab("host", ST_MAP, ST_FIND) == NULL) + { + /* user didn't initialize: set up host map */ + strcpy(buf, "host host"); + if (ConfigLevel >= 2) + strcat(buf, " -a."); + makemapentry(buf); + } } /* ** TOOMANY -- signal too many of some option @@ -319,18 +534,35 @@ toomany(id, maxcnt) ** the named class. */ -fileclass(class, filename, fmt) +fileclass(class, filename, fmt, safe) int class; char *filename; char *fmt; + bool safe; { FILE *f; + struct stat stbuf; char buf[MAXLINE]; + if (stat(filename, &stbuf) < 0) + { + syserr("fileclass: cannot stat %s", filename); + return; + } + if (!S_ISREG(stbuf.st_mode)) + { + syserr("fileclass: %s not a regular file", filename); + return; + } + if (!safe && access(filename, R_OK) < 0) + { + syserr("fileclass: access denied on %s", filename); + return; + } f = fopen(filename, "r"); if (f == NULL) { - syserr("cannot open %s", filename); + syserr("fileclass: cannot open %s", filename); return; } @@ -344,9 +576,9 @@ fileclass(class, filename, fmt) if (sscanf(buf, fmt, wordbuf) != 1) continue; p = wordbuf; -# else SCANF +# else /* SCANF */ p = buf; -# endif SCANF +# endif /* SCANF */ /* ** Break up the match into words. @@ -357,14 +589,14 @@ fileclass(class, filename, fmt) register char *q; /* strip leading spaces */ - while (isspace(*p)) + while (isascii(*p) && isspace(*p)) p++; if (*p == '\0') break; /* find the end of the word */ q = p; - while (*p != '\0' && !isspace(*p)) + while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; if (*p != '\0') *p++ = '\0'; @@ -406,20 +638,19 @@ makemailer(line) register STAB *s; int i; char fcode; + auto char *endp; extern int NextMailer; extern char **makeargv(); extern char *munchstring(); - extern char *DelimChar; extern long atol(); /* allocate a mailer and set up defaults */ m = (struct mailer *) xalloc(sizeof *m); bzero((char *) m, sizeof *m); - m->m_mno = NextMailer; m->m_eol = "\n"; /* collect the mailer name */ - for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++) + for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) continue; if (*p != '\0') *p++ = '\0'; @@ -428,7 +659,9 @@ makemailer(line) /* now scan through and assign info from the fields */ while (*p != '\0') { - while (*p != '\0' && (*p == ',' || isspace(*p))) + auto char *delimptr; + + while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) p++; /* p now points to field code */ @@ -437,14 +670,14 @@ makemailer(line) p++; if (*p++ != '=') { - syserr("`=' expected"); + syserr("mailer %s: `=' expected", m->m_name); return; } - while (isspace(*p)) + while (isascii(*p) && isspace(*p)) p++; /* p now points to the field body */ - p = munchstring(p); + p = munchstring(p, &delimptr); /* install the field into the mailer struct */ switch (fcode) @@ -455,21 +688,38 @@ makemailer(line) case 'F': /* flags */ for (; *p != '\0'; p++) - setbitn(*p, m->m_flags); + if (!(isascii(*p) && isspace(*p))) + setbitn(*p, m->m_flags); break; case 'S': /* sender rewriting ruleset */ case 'R': /* recipient rewriting ruleset */ - i = atoi(p); + i = strtol(p, &endp, 10); if (i < 0 || i >= MAXRWSETS) { syserr("invalid rewrite set, %d max", MAXRWSETS); return; } if (fcode == 'S') - m->m_s_rwset = i; + m->m_sh_rwset = m->m_se_rwset = i; else - m->m_r_rwset = i; + m->m_rh_rwset = m->m_re_rwset = i; + + p = endp; + if (*p++ == '/') + { + i = strtol(p, NULL, 10); + if (i < 0 || i >= MAXRWSETS) + { + syserr("invalid rewrite set, %d max", + MAXRWSETS); + return; + } + if (fcode == 'S') + m->m_sh_rwset = i; + else + m->m_rh_rwset = i; + } break; case 'E': /* end of line string */ @@ -483,44 +733,80 @@ makemailer(line) case 'M': /* maximum message size */ m->m_maxsize = atol(p); break; + + case 'L': /* maximum line length */ + m->m_linelimit = atoi(p); + break; + + case 'D': /* working directory */ + m->m_execdir = newstr(p); + break; } - p = DelimChar; + p = delimptr; + } + + /* do some heuristic cleanup for back compatibility */ + if (bitnset(M_LIMITS, m->m_flags)) + { + if (m->m_linelimit == 0) + m->m_linelimit = SMTPLINELIM; + if (ConfigLevel < 2) + setbitn(M_7BITS, m->m_flags); + } + + /* do some rationality checking */ + if (m->m_argv == NULL) + { + syserr("M%s: A= argument required", m->m_name); + return; + } + if (m->m_mailer == NULL) + { + syserr("M%s: P= argument required", m->m_name); + return; } - /* now store the mailer away */ if (NextMailer >= MAXMAILERS) { syserr("too many mailers defined (%d max)", MAXMAILERS); return; } - Mailer[NextMailer++] = m; + s = stab(m->m_name, ST_MAILER, ST_ENTER); - s->s_mailer = m; + if (s->s_mailer != NULL) + { + i = s->s_mailer->m_mno; + free(s->s_mailer); + } + else + { + i = NextMailer++; + } + Mailer[i] = s->s_mailer = m; + m->m_mno = i; } /* ** MUNCHSTRING -- translate a string into internal form. ** ** Parameters: ** p -- the string to munch. +** delimptr -- if non-NULL, set to the pointer of the +** field delimiter character. ** ** Returns: ** the munched string. -** -** Side Effects: -** Sets "DelimChar" to point to the string that caused us -** to stop. */ char * -munchstring(p) +munchstring(p, delimptr) register char *p; + char **delimptr; { register char *q; bool backslash = FALSE; bool quotemode = FALSE; static char buf[MAXLINE]; - extern char *DelimChar; for (q = buf; *p != '\0'; p++) { @@ -561,7 +847,8 @@ munchstring(p) } } - DelimChar = p; + if (delimptr != NULL) + *delimptr = p; *q++ = '\0'; return (buf); } @@ -592,9 +879,9 @@ makeargv(p) while (*p != '\0' && i < MAXPV) { q = p; - while (*p != '\0' && !isspace(*p)) + while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; - while (isspace(*p)) + while (isascii(*p) && isspace(*p)) *p++ = '\0'; argv[i++] = newstr(q); } @@ -651,6 +938,7 @@ printrules() ** reset the user id to avoid security problems. ** sticky -- if set, don't let other setoptions override ** this value. +** e -- the main envelope. ** ** Returns: ** none. @@ -660,20 +948,43 @@ printrules() */ static BITMAP StickyOpt; /* set if option is stuck */ -extern char *NetName; /* name of home (local) network */ -setoption(opt, val, safe, sticky) + +#ifdef NAMED_BIND + +struct resolverflags +{ + char *rf_name; /* name of the flag */ + long rf_bits; /* bits to set/clear */ +} ResolverFlags[] = +{ + "debug", RES_DEBUG, + "aaonly", RES_AAONLY, + "usevc", RES_USEVC, + "primary", RES_PRIMARY, + "igntc", RES_IGNTC, + "recurse", RES_RECURSE, + "defnames", RES_DEFNAMES, + "stayopen", RES_STAYOPEN, + "dnsrch", RES_DNSRCH, + NULL, 0 +}; + +#endif + +setoption(opt, val, safe, sticky, e) char opt; char *val; bool safe; bool sticky; + register ENVELOPE *e; { + register char *p; extern bool atobool(); extern time_t convtime(); extern int QueueLA; extern int RefuseLA; extern bool trusteduser(); - extern char *username(); if (tTd(37, 1)) printf("setoption %c=%s", opt, val); @@ -682,7 +993,7 @@ setoption(opt, val, safe, sticky) ** See if this option is preset for us. */ - if (bitnset(opt, StickyOpt)) + if (!sticky && bitnset(opt, StickyOpt)) { if (tTd(37, 1)) printf(" (ignored)\n"); @@ -693,32 +1004,37 @@ setoption(opt, val, safe, sticky) ** Check to see if this option can be specified by this user. */ - if (!safe && getuid() == 0) + if (!safe && RealUid == 0) safe = TRUE; - if (!safe && index("deiLmorsv", opt) == NULL) + if (!safe && strchr("bdeEijLmoprsvC7", opt) == NULL) { if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) { if (tTd(37, 1)) printf(" (unsafe)"); - if (getuid() != geteuid()) + if (RealUid != geteuid()) { - printf("(Resetting uid)\n"); - (void) setgid(getgid()); - (void) setuid(getuid()); + if (tTd(37, 1)) + printf("(Resetting uid)"); + (void) setgid(RealGid); + (void) setuid(RealUid); } } } - else if (tTd(37, 1)) + if (tTd(37, 1)) printf("\n"); switch (opt) { + case '7': /* force seven-bit input */ + SevenBit = atobool(val); + break; + case 'A': /* set default alias file */ if (val[0] == '\0') - AliasFile = "aliases"; + setalias("aliases"); else - AliasFile = newstr(val); + setalias(val); break; case 'a': /* look N minutes for "@:@" in alias file */ @@ -734,30 +1050,40 @@ setoption(opt, val, safe, sticky) SpaceSub = ' '; break; + case 'b': /* min blocks free on queue fs/max msg size */ + p = strchr(val, '/'); + if (p != NULL) + { + *p++ = '\0'; + MaxMessageSize = atol(p); + } + MinBlocksFree = atol(val); + break; + case 'c': /* don't connect to "expensive" mailers */ NoConnect = atobool(val); break; - case 'C': /* checkpoint after N connections */ - CheckPointLimit = atoi(val); + case 'C': /* checkpoint every N addresses */ + CheckpointInterval = atoi(val); break; case 'd': /* delivery mode */ switch (*val) { case '\0': - SendMode = SM_DELIVER; + e->e_sendmode = SM_DELIVER; break; case SM_QUEUE: /* queue only */ #ifndef QUEUE syserr("need QUEUE to set -odqueue"); -#endif QUEUE +#endif /* QUEUE */ /* fall through..... */ case SM_DELIVER: /* do everything */ case SM_FORK: /* fork after verification */ - SendMode = *val; + e->e_sendmode = *val; break; default: @@ -770,6 +1096,11 @@ setoption(opt, val, safe, sticky) AutoRebuild = atobool(val); break; + case 'E': /* error message header/header file */ + if (*val != '\0') + ErrMsgFile = newstr(val); + break; + case 'e': /* set error processing mode */ switch (*val) { @@ -781,7 +1112,7 @@ setoption(opt, val, safe, sticky) /* fall through... */ case EM_PRINT: /* print errors normally (default) */ - ErrorMode = *val; + e->e_errormode = *val; break; } break; @@ -794,6 +1125,10 @@ setoption(opt, val, safe, sticky) SaveFrom = atobool(val); break; + case 'G': /* match recipients against GECOS field */ + MatchGecos = atobool(val); + break; + case 'g': /* default gid */ DefGid = atoi(val); break; @@ -805,16 +1140,75 @@ setoption(opt, val, safe, sticky) HelpFile = newstr(val); break; + case 'h': /* maximum hop count */ + MaxHopCount = atoi(val); + break; + case 'I': /* use internet domain name server */ - UseNameServer = atobool(val); +#ifdef NAMED_BIND + UseNameServer = TRUE; + for (p = val; *p != 0; ) + { + bool clearmode; + char *q; + struct resolverflags *rfp; + + while (*p == ' ') + p++; + if (*p == '\0') + break; + clearmode = FALSE; + if (*p == '-') + clearmode = TRUE; + else if (*p != '+') + p--; + p++; + q = p; + while (*p != '\0' && !(isascii(*p) && isspace(*p))) + p++; + if (*p != '\0') + *p++ = '\0'; + for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) + { + if (strcasecmp(q, rfp->rf_name) == 0) + break; + } + if (clearmode) + _res.options &= ~rfp->rf_bits; + else + _res.options |= rfp->rf_bits; + } + if (tTd(8, 2)) + printf("_res.options = %x\n", _res.options); +#else + usrerr("name server (I option) specified but BIND not compiled in"); +#endif break; case 'i': /* ignore dot lines in message */ IgnrDot = atobool(val); break; - case 'k': /* checkpoint every N addresses */ - CheckpointInterval = atoi(val); + case 'j': /* send errors in MIME (RFC 1341) format */ + SendMIMEErrors = atobool(val); + break; + + case 'J': /* .forward search path */ + ForwardPath = newstr(val); + break; + + case 'k': /* connection cache size */ + MaxMciCache = atoi(val); + if (MaxMciCache < 0) + MaxMciCache = 0; + break; + + case 'K': /* connection cache timeout */ + MciCacheTimeout = convtime(val, 'm'); + break; + + case 'l': /* use Errors-To: header */ + UseErrorsTo = atobool(val); break; case 'L': /* log level */ @@ -834,11 +1228,11 @@ setoption(opt, val, safe, sticky) CheckAliases = atobool(val); break; -# ifdef DAEMON - case 'N': /* home (local?) network name */ - NetName = newstr(val); + /* 'N' available -- was "net name" */ + + case 'O': /* daemon options */ + setdaemonoptions(val); break; -# endif DAEMON case 'o': /* assume old style headers */ if (atobool(val)) @@ -847,6 +1241,34 @@ setoption(opt, val, safe, sticky) CurEnv->e_flags &= ~EF_OLDSTYLE; break; + case 'p': /* select privacy level */ + p = val; + for (;;) + { + register struct prival *pv; + extern struct prival PrivacyValues[]; + + while (isascii(*p) && (isspace(*p) || ispunct(*p))) + p++; + if (*p == '\0') + break; + val = p; + while (isascii(*p) && isalnum(*p)) + p++; + if (*p != '\0') + *p++ = '\0'; + + for (pv = PrivacyValues; pv->pv_name != NULL; pv++) + { + if (strcasecmp(val, pv->pv_name) == 0) + break; + } + if (pv->pv_name == NULL) + syserr("readcf: Op line: %s unrecognized", val); + PrivacyFlags |= pv->pv_flag; + } + break; + case 'P': /* postmaster copy address for returned mail */ PostMasterCopy = newstr(val); break; @@ -860,10 +1282,16 @@ setoption(opt, val, safe, sticky) QueueDir = "mqueue"; else QueueDir = newstr(val); + if (RealUid != 0 && !safe) + auth_warning(e, "Processed from queue %s", QueueDir); + break; + + case 'R': /* don't prune routes */ + DontPruneRoutes = atobool(val); break; case 'r': /* read timeout */ - ReadTimeout = convtime(val); + settimeouts(val); break; case 'S': /* status file */ @@ -878,10 +1306,21 @@ setoption(opt, val, safe, sticky) break; case 'T': /* queue timeout */ - TimeOut = convtime(val); - /*FALLTHROUGH*/ + p = strchr(val, '/'); + if (p != NULL) + { + *p++ = '\0'; + TimeOuts.to_q_warning = convtime(p, 'd'); + } + TimeOuts.to_q_return = convtime(val, 'h'); + break; case 't': /* time zone name */ + TimeZoneSpec = newstr(val); + break; + + case 'U': /* location of user database */ + UdbSpec = newstr(val); break; case 'u': /* set default uid */ @@ -889,10 +1328,18 @@ setoption(opt, val, safe, sticky) setdefuser(); break; + case 'V': /* fallback MX host */ + FallBackMX = newstr(val); + break; + case 'v': /* run in verbose mode */ Verbose = atobool(val); break; + /* 'w' available -- was "no wildcard MX matching" */ + + /* 'W' available -- was wizard password */ + case 'x': /* load avg at which to auto-queue msgs */ QueueLA = atoi(val); break; @@ -944,6 +1391,177 @@ setclass(class, word) { register STAB *s; + if (tTd(37, 8)) + printf("%s added to class %c\n", word, class); s = stab(word, ST_CLASS, ST_ENTER); setbitn(class, s->s_class); } + /* +** MAKEMAPENTRY -- create a map entry +** +** Parameters: +** line -- the config file line +** +** Returns: +** TRUE if it successfully entered the map entry. +** FALSE otherwise (usually syntax error). +** +** Side Effects: +** Enters the map into the dictionary. +*/ + +void +makemapentry(line) + char *line; +{ + register char *p; + char *mapname; + char *classname; + register STAB *map; + STAB *class; + + for (p = line; isascii(*p) && isspace(*p); p++) + continue; + if (!(isascii(*p) && isalnum(*p))) + { + syserr("readcf: config K line: no map name"); + return; + } + + mapname = p; + while (isascii(*++p) && isalnum(*p)) + continue; + if (*p != '\0') + *p++ = '\0'; + while (isascii(*p) && isspace(*p)) + p++; + if (!(isascii(*p) && isalnum(*p))) + { + syserr("readcf: config K line, map %s: no map class", mapname); + return; + } + classname = p; + while (isascii(*++p) && isalnum(*p)) + continue; + if (*p != '\0') + *p++ = '\0'; + while (isascii(*p) && isspace(*p)) + p++; + + /* look up the class */ + class = stab(classname, ST_MAPCLASS, ST_FIND); + if (class == NULL) + { + syserr("readcf: map %s: class %s not available", mapname, classname); + return; + } + + /* enter the map */ + map = stab(mapname, ST_MAP, ST_ENTER); + map->s_map.map_class = &class->s_mapclass; + map->s_map.map_mname = newstr(mapname); + + if (class->s_mapclass.map_parse(&map->s_map, p)) + map->s_map.map_mflags |= MF_VALID; +} + /* +** SETTIMEOUTS -- parse and set timeout values +** +** Parameters: +** val -- a pointer to the values. If NULL, do initial +** settings. +** +** Returns: +** none. +** +** Side Effects: +** Initializes the TimeOuts structure +*/ + +#define MINUTES * 60 +#define HOUR * 3600 + +settimeouts(val) + register char *val; +{ + register char *p; + extern time_t convtime(); + + if (val == NULL) + { + TimeOuts.to_initial = (time_t) 5 MINUTES; + TimeOuts.to_helo = (time_t) 5 MINUTES; + TimeOuts.to_mail = (time_t) 10 MINUTES; + TimeOuts.to_rcpt = (time_t) 1 HOUR; + TimeOuts.to_datainit = (time_t) 5 MINUTES; + TimeOuts.to_datablock = (time_t) 1 HOUR; + TimeOuts.to_datafinal = (time_t) 1 HOUR; + TimeOuts.to_rset = (time_t) 5 MINUTES; + TimeOuts.to_quit = (time_t) 2 MINUTES; + TimeOuts.to_nextcommand = (time_t) 1 HOUR; + TimeOuts.to_miscshort = (time_t) 2 MINUTES; + return; + } + + for (;; val = p) + { + while (isascii(*val) && isspace(*val)) + val++; + if (*val == '\0') + break; + for (p = val; *p != '\0' && *p != ','; p++) + continue; + if (*p != '\0') + *p++ = '\0'; + + if (isascii(*val) && isdigit(*val)) + { + /* old syntax -- set everything */ + TimeOuts.to_mail = convtime(val, 'm'); + TimeOuts.to_rcpt = TimeOuts.to_mail; + TimeOuts.to_datainit = TimeOuts.to_mail; + TimeOuts.to_datablock = TimeOuts.to_mail; + TimeOuts.to_datafinal = TimeOuts.to_mail; + TimeOuts.to_nextcommand = TimeOuts.to_mail; + continue; + } + else + { + register char *q = strchr(val, '='); + time_t to; + + if (q == NULL) + { + /* syntax error */ + continue; + } + *q++ = '\0'; + to = convtime(q, 'm'); + + if (strcasecmp(val, "initial") == 0) + TimeOuts.to_initial = to; + else if (strcasecmp(val, "mail") == 0) + TimeOuts.to_mail = to; + else if (strcasecmp(val, "rcpt") == 0) + TimeOuts.to_rcpt = to; + else if (strcasecmp(val, "datainit") == 0) + TimeOuts.to_datainit = to; + else if (strcasecmp(val, "datablock") == 0) + TimeOuts.to_datablock = to; + else if (strcasecmp(val, "datafinal") == 0) + TimeOuts.to_datafinal = to; + else if (strcasecmp(val, "command") == 0) + TimeOuts.to_nextcommand = to; + else if (strcasecmp(val, "rset") == 0) + TimeOuts.to_rset = to; + else if (strcasecmp(val, "helo") == 0) + TimeOuts.to_helo = to; + else if (strcasecmp(val, "quit") == 0) + TimeOuts.to_quit = to; + else if (strcasecmp(val, "misc") == 0) + TimeOuts.to_miscshort = to; + else + syserr("settimeouts: invalid timeout %s", val); + } + } +} diff --git a/usr.sbin/sendmail/src/recipient.c b/usr.sbin/sendmail/src/recipient.c index 41634c1815..bcdf5bfb3b 100644 --- a/usr.sbin/sendmail/src/recipient.c +++ b/usr.sbin/sendmail/src/recipient.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,13 +33,11 @@ */ #ifndef lint -static char sccsid[] = "@(#)recipient.c 5.19 (Berkeley) 3/2/91"; +static char sccsid[] = "@(#)recipient.c 8.3 (Berkeley) 7/13/93"; #endif /* not lint */ -# include <sys/types.h> -# include <sys/stat.h> -# include <pwd.h> # include "sendmail.h" +# include <pwd.h> /* ** SENDTOLIST -- Designate a send list. @@ -55,9 +53,10 @@ static char sccsid[] = "@(#)recipient.c 5.19 (Berkeley) 3/2/91"; ** expansion. ** sendq -- a pointer to the head of a queue to put ** these people into. +** e -- the envelope in which to add these recipients. ** ** Returns: -** none +** The number of addresses actually on the list. ** ** Side Effects: ** none. @@ -65,16 +64,17 @@ static char sccsid[] = "@(#)recipient.c 5.19 (Berkeley) 3/2/91"; # define MAXRCRSN 10 -sendtolist(list, ctladdr, sendq) +sendtolist(list, ctladdr, sendq, e) char *list; ADDRESS *ctladdr; ADDRESS **sendq; + register ENVELOPE *e; { register char *p; register ADDRESS *al; /* list of addresses to send to */ bool firstone; /* set on first address sent */ - bool selfref; /* set if this list includes ctladdr */ char delimiter; /* the address delimiter */ + int naddrs; if (tTd(25, 1)) { @@ -84,27 +84,27 @@ sendtolist(list, ctladdr, sendq) /* heuristic to determine old versus new style addresses */ if (ctladdr == NULL && - (index(list, ',') != NULL || index(list, ';') != NULL || - index(list, '<') != NULL || index(list, '(') != NULL)) - CurEnv->e_flags &= ~EF_OLDSTYLE; + (strchr(list, ',') != NULL || strchr(list, ';') != NULL || + strchr(list, '<') != NULL || strchr(list, '(') != NULL)) + e->e_flags &= ~EF_OLDSTYLE; delimiter = ' '; - if (!bitset(EF_OLDSTYLE, CurEnv->e_flags) || ctladdr != NULL) + if (!bitset(EF_OLDSTYLE, e->e_flags) || ctladdr != NULL) delimiter = ','; firstone = TRUE; - selfref = FALSE; al = NULL; + naddrs = 0; for (p = list; *p != '\0'; ) { + auto char *delimptr; register ADDRESS *a; - extern char *DelimChar; /* defined in prescan */ /* parse the address */ - while (isspace(*p) || *p == ',') + while ((isascii(*p) && isspace(*p)) || *p == ',') p++; - a = parseaddr(p, (ADDRESS *) NULL, 1, delimiter); - p = DelimChar; + a = parseaddr(p, (ADDRESS *) NULL, 1, delimiter, &delimptr, e); + p = delimptr; if (a == NULL) continue; a->q_next = al; @@ -115,34 +115,28 @@ sendtolist(list, ctladdr, sendq) (firstone && *p == '\0' && bitset(QPRIMARY, ctladdr->q_flags))) a->q_flags |= QPRIMARY; - /* put on send queue or suppress self-reference */ if (ctladdr != NULL && sameaddr(ctladdr, a)) - selfref = TRUE; - else - al = a; + ctladdr->q_flags |= QSELFREF; + al = a; firstone = FALSE; } - /* if this alias doesn't include itself, delete ctladdr */ - if (!selfref && ctladdr != NULL) - ctladdr->q_flags |= QDONTSEND; - /* arrange to send to everyone on the local send list */ while (al != NULL) { register ADDRESS *a = al; - extern ADDRESS *recipient(); al = a->q_next; - setctladdr(a); - a = recipient(a, sendq); + a = recipient(a, sendq, e); /* arrange to inherit full name */ if (a->q_fullname == NULL && ctladdr != NULL) a->q_fullname = ctladdr->q_fullname; + naddrs++; } - CurEnv->e_to = NULL; + e->e_to = NULL; + return (naddrs); } /* ** RECIPIENT -- Designate a message recipient @@ -154,6 +148,7 @@ sendtolist(list, ctladdr, sendq) ** sendq -- a pointer to the head of a queue to put the ** recipient in. Duplicate supression is done ** in this queue. +** e -- the current envelope. ** ** Returns: ** The actual address in the queue. This will be "a" if @@ -163,22 +158,22 @@ sendtolist(list, ctladdr, sendq) ** none. */ -extern ADDRESS *getctladdr(); - ADDRESS * -recipient(a, sendq) +recipient(a, sendq, e) register ADDRESS *a; register ADDRESS **sendq; + register ENVELOPE *e; { register ADDRESS *q; ADDRESS **pq; register struct mailer *m; register char *p; bool quoted = FALSE; /* set if the addr has a quote bit */ + int findusercount = 0; char buf[MAXNAME]; /* unquoted image of the user name */ - extern bool safefile(); + extern int safefile(); - CurEnv->e_to = a->q_paddr; + e->e_to = a->q_paddr; m = a->q_mailer; errno = 0; if (tTd(26, 1)) @@ -190,7 +185,7 @@ recipient(a, sendq) /* break aliasing loops */ if (AliasLevel > MAXRCRSN) { - usrerr("aliasing/forwarding loop broken"); + usrerr("554 aliasing/forwarding loop broken"); return (a); } @@ -199,31 +194,23 @@ recipient(a, sendq) */ /* set the queue timeout */ - a->q_timeout = TimeOut; - - /* map user & host to lower case if requested on non-aliases */ - if (a->q_alias == NULL) - loweraddr(a); + a->q_timeout = TimeOuts.to_q_return; /* get unquoted user for file, program or user.name check */ (void) strcpy(buf, a->q_user); for (p = buf; *p != '\0' && !quoted; p++) { - if (!isascii(*p) && (*p & 0377) != (SpaceSub & 0377)) + if (*p == '\\') quoted = TRUE; } - stripquotes(buf, TRUE); + stripquotes(buf); - /* do sickly crude mapping for program mailing, etc. */ - if (m == LocalMailer && buf[0] == '|') + /* check for direct mailing to restricted mailers */ + if (a->q_alias == NULL && m == ProgMailer && + !bitset(EF_QUEUERUN, e->e_flags)) { - a->q_mailer = m = ProgMailer; - a->q_user++; - if (a->q_alias == NULL && !QueueRun && !ForceMail) - { - a->q_flags |= QDONTSEND|QBADADDR; - usrerr("Cannot mail directly to programs"); - } + a->q_flags |= QBADADDR; + usrerr("550 Cannot mail directly to programs", m->m_name); } /* @@ -237,17 +224,19 @@ recipient(a, sendq) for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next) { - if (!ForceMail && sameaddr(q, a)) + if (sameaddr(q, a)) { if (tTd(26, 1)) { printf("%s in sendq: ", a->q_paddr); printaddr(q, FALSE); } - if (!bitset(QDONTSEND, a->q_flags)) - message(Arpa_Info, "duplicate suppressed"); if (!bitset(QPRIMARY, q->q_flags)) + { + if (!bitset(QDONTSEND, a->q_flags)) + message("duplicate suppressed"); q->q_flags |= a->q_flags; + } return (q); } } @@ -255,95 +244,181 @@ recipient(a, sendq) /* add address on list */ *pq = a; a->q_next = NULL; - CurEnv->e_nrcpts++; /* - ** Alias the name and handle :include: specs. + ** Alias the name and handle special mailer types. */ - if (m == LocalMailer && !bitset(QDONTSEND, a->q_flags)) + trylocaluser: + if (tTd(29, 7)) + printf("at trylocaluser %s\n", a->q_user); + + if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags)) + return (a); + + if (m == InclMailer) { - if (strncmp(a->q_user, ":include:", 9) == 0) + a->q_flags |= QDONTSEND; + if (a->q_alias == NULL && !bitset(EF_QUEUERUN, e->e_flags)) { - a->q_flags |= QDONTSEND; - if (a->q_alias == NULL && !QueueRun && !ForceMail) + a->q_flags |= QBADADDR; + usrerr("550 Cannot mail directly to :include:s"); + } + else + { + int ret; + + message("including file %s", a->q_user); + ret = include(a->q_user, FALSE, a, sendq, e); + if (transienterror(ret)) { - a->q_flags |= QBADADDR; - usrerr("Cannot mail directly to :include:s"); +#ifdef LOG + if (LogLevel > 2) + syslog(LOG_ERR, "%s: include %s: transient error: %e", + e->e_id, a->q_user, errstring(ret)); +#endif + a->q_flags |= QQUEUEUP|QDONTSEND; + usrerr("451 Cannot open %s: %s", + a->q_user, errstring(ret)); } - else + else if (ret != 0) { - message(Arpa_Info, "including file %s", &a->q_user[9]); - include(&a->q_user[9], " sending", a, sendq); + usrerr("550 Cannot open %s: %s", + a->q_user, errstring(ret)); + a->q_flags |= QBADADDR; } } - else - alias(a, sendq); } + else if (m == FileMailer) + { + struct stat stb; + extern bool writable(); + + p = strrchr(buf, '/'); + /* check if writable or creatable */ + if (a->q_alias == NULL && !bitset(EF_QUEUERUN, e->e_flags)) + { + a->q_flags |= QBADADDR; + usrerr("550 Cannot mail directly to files"); + } + else if ((stat(buf, &stb) >= 0) ? (!writable(&stb)) : + (*p = '\0', safefile(buf, RealUid, TRUE, S_IWRITE|S_IEXEC) != 0)) + { + a->q_flags |= QBADADDR; + giveresponse(EX_CANTCREAT, m, NULL, e); + } + } + + if (m != LocalMailer) + { + if (!bitset(QDONTSEND, a->q_flags)) + e->e_nrcpts++; + return (a); + } + + /* try aliasing */ + alias(a, sendq, e); + +# ifdef USERDB + /* if not aliased, look it up in the user database */ + if (!bitset(QDONTSEND|QNOTREMOTE|QVERIFIED, a->q_flags)) + { + extern int udbexpand(); + extern int errno; + + if (udbexpand(a, sendq, e) == EX_TEMPFAIL) + { + a->q_flags |= QQUEUEUP|QDONTSEND; + if (e->e_message == NULL) + e->e_message = newstr("Deferred: user database error"); +# ifdef LOG + if (LogLevel > 8) + syslog(LOG_INFO, "%s: deferred: udbexpand: %s", + e->e_id, errstring(errno)); +# endif + message("queued (user database error): %s", + errstring(errno)); + e->e_nrcpts++; + return (a); + } + } +# endif + + /* if it was an alias or a UDB expansion, just return now */ + if (bitset(QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags)) + return (a); /* - ** If the user is local and still being sent, verify that - ** the address is good. If it is, try to forward. - ** If the address is already good, we have a forwarding - ** loop. This can be broken by just sending directly to - ** the user (which is probably correct anyway). + ** If we have a level two config file, then pass the name through + ** Ruleset 5 before sending it off. Ruleset 5 has the right + ** to send rewrite it to another mailer. This gives us a hook + ** after local aliasing has been done. */ - if (!bitset(QDONTSEND, a->q_flags) && m == LocalMailer) + if (tTd(29, 5)) { - struct stat stb; - extern bool writable(); + printf("recipient: testing local? cl=%d, rr5=%x\n\t", + ConfigLevel, RewriteRules[5]); + printaddr(a, FALSE); + } + if (!bitset(QNOTREMOTE, a->q_flags) && ConfigLevel >= 2 && + RewriteRules[5] != NULL) + { + maplocaluser(a, sendq, e); + } + + /* + ** If it didn't get rewritten to another mailer, go ahead + ** and deliver it. + */ + + if (!bitset(QDONTSEND|QQUEUEUP, a->q_flags)) + { + auto bool fuzzy; + register struct passwd *pw; + extern struct passwd *finduser(); - /* see if this is to a file */ - if (buf[0] == '/') + /* warning -- finduser may trash buf */ + pw = finduser(buf, &fuzzy); + if (pw == NULL) { - p = rindex(buf, '/'); - /* check if writable or creatable */ - if (a->q_alias == NULL && !QueueRun && !ForceMail) - { - a->q_flags |= QDONTSEND|QBADADDR; - usrerr("Cannot mail directly to files"); - } - else if ((stat(buf, &stb) >= 0) ? (!writable(&stb)) : - (*p = '\0', !safefile(buf, getruid(), S_IWRITE|S_IEXEC))) - { - a->q_flags |= QBADADDR; - giveresponse(EX_CANTCREAT, m, CurEnv); - } + a->q_flags |= QBADADDR; + giveresponse(EX_NOUSER, m, NULL, e); } else { - register struct passwd *pw; - extern struct passwd *finduser(); + char nbuf[MAXNAME]; - /* warning -- finduser may trash buf */ - pw = finduser(buf); - if (pw == NULL) - { - a->q_flags |= QBADADDR; - giveresponse(EX_NOUSER, m, CurEnv); - } - else + if (fuzzy) { - char nbuf[MAXNAME]; - - if (strcmp(a->q_user, pw->pw_name) != 0) + /* name was a fuzzy match */ + a->q_user = newstr(pw->pw_name); + if (findusercount++ > 3) { - a->q_user = newstr(pw->pw_name); - (void) strcpy(buf, pw->pw_name); + a->q_flags |= QBADADDR; + usrerr("554 aliasing/forwarding loop for %s broken", + pw->pw_name); + return (a); } - a->q_home = newstr(pw->pw_dir); - a->q_uid = pw->pw_uid; - a->q_gid = pw->pw_gid; - a->q_flags |= QGOODUID; - buildfname(pw->pw_gecos, pw->pw_name, nbuf); - if (nbuf[0] != '\0') - a->q_fullname = newstr(nbuf); - if (!quoted) - forward(a, sendq); + + /* see if it aliases */ + (void) strcpy(buf, pw->pw_name); + goto trylocaluser; } + a->q_home = newstr(pw->pw_dir); + a->q_uid = pw->pw_uid; + a->q_gid = pw->pw_gid; + a->q_ruser = newstr(pw->pw_name); + a->q_flags |= QGOODUID; + buildfname(pw->pw_gecos, pw->pw_name, nbuf); + if (nbuf[0] != '\0') + a->q_fullname = newstr(nbuf); + if (!quoted) + forward(a, sendq, e); } } + if (!bitset(QDONTSEND, a->q_flags)) + e->e_nrcpts++; return (a); } /* @@ -357,6 +432,9 @@ recipient(a, sendq) ** ** Parameters: ** name -- the name to match against. +** fuzzyp -- an outarg that is set to TRUE if this entry +** was found using the fuzzy matching algorithm; +** set to FALSE otherwise. ** ** Returns: ** A pointer to a pw struct. @@ -367,24 +445,36 @@ recipient(a, sendq) */ struct passwd * -finduser(name) +finduser(name, fuzzyp) char *name; + bool *fuzzyp; { register struct passwd *pw; register char *p; extern struct passwd *getpwent(); extern struct passwd *getpwnam(); - /* map upper => lower case */ - for (p = name; *p != '\0'; p++) - { - if (isascii(*p) && isupper(*p)) - *p = tolower(*p); - } + if (tTd(29, 4)) + printf("finduser(%s): ", name); + + *fuzzyp = FALSE; /* look up this login name using fast path */ if ((pw = getpwnam(name)) != NULL) + { + if (tTd(29, 4)) + printf("found (non-fuzzy)\n"); return (pw); + } + +#ifdef MATCHGECOS + /* see if fuzzy matching allowed */ + if (!MatchGecos) + { + if (tTd(29, 4)) + printf("not found (fuzzy disabled)\n"); + return NULL; + } /* search for a matching full name instead */ for (p = name; *p != '\0'; p++) @@ -398,12 +488,21 @@ finduser(name) char buf[MAXNAME]; buildfname(pw->pw_gecos, pw->pw_name, buf); - if (index(buf, ' ') != NULL && !strcasecmp(buf, name)) + if (strchr(buf, ' ') != NULL && !strcasecmp(buf, name)) { - message(Arpa_Info, "sending to login name %s", pw->pw_name); + if (tTd(29, 4)) + printf("fuzzy matches %s\n", pw->pw_name); + message("sending to login name %s", pw->pw_name); + *fuzzyp = TRUE; return (pw); } } + if (tTd(29, 4)) + printf("no fuzzy match found\n"); +#else + if (tTd(29, 4)) + printf("not found (fuzzy disabled)\n"); +#endif return (NULL); } /* @@ -432,13 +531,14 @@ bool writable(s) register struct stat *s; { - int euid, egid; + uid_t euid; + gid_t egid; int bits; if (bitset(0111, s->st_mode)) return (FALSE); - euid = getruid(); - egid = getrgid(); + euid = RealUid; + egid = RealGid; if (geteuid() == 0) { if (bitset(S_ISUID, s->st_mode)) @@ -463,7 +563,8 @@ writable(s) ** ** Parameters: ** fname -- filename to include. -** msg -- message to print in verbose mode. +** forwarding -- if TRUE, we are reading a .forward file. +** if FALSE, it's a :include: file. ** ctladdr -- address template to use to fill in these ** addresses -- effective user/group id are ** the important things. @@ -471,70 +572,165 @@ writable(s) ** to put these addresses in. ** ** Returns: -** none. +** open error status ** ** Side Effects: ** reads the :include: file and sends to everyone ** listed in that file. */ -include(fname, msg, ctladdr, sendq) +static jmp_buf CtxIncludeTimeout; + +int +include(fname, forwarding, ctladdr, sendq, e) char *fname; - char *msg; + bool forwarding; ADDRESS *ctladdr; ADDRESS **sendq; + ENVELOPE *e; { - char buf[MAXLINE]; register FILE *fp; - char *oldto = CurEnv->e_to; + char *oldto = e->e_to; char *oldfilename = FileName; int oldlinenumber = LineNumber; + register EVENT *ev = NULL; + int nincludes; + int ret; + ADDRESS *ca; + uid_t uid; + char buf[MAXLINE]; + static int includetimeout(); + + if (tTd(27, 2)) + printf("include(%s)\n", fname); + if (tTd(27, 14)) + { + printf("ctladdr "); + printaddr(ctladdr, FALSE); + } + + /* + ** If home directory is remote mounted but server is down, + ** this can hang or give errors; use a timeout to avoid this + */ + + ca = getctladdr(ctladdr); + if (ca == NULL) + uid = 0; + else + uid = ca->q_uid; + + if (setjmp(CtxIncludeTimeout) != 0) + { + ctladdr->q_flags |= QQUEUEUP|QDONTSEND; + errno = 0; + usrerr("451 open timeout on %s", fname); + return ETIMEDOUT; + } + ev = setevent((time_t) 60, includetimeout, 0); + + /* the input file must be marked safe */ + if ((ret = safefile(fname, uid, forwarding, S_IREAD)) != 0) + { + /* don't use this .forward file */ + clrevent(ev); + if (tTd(27, 4)) + printf("include: not safe (uid=%d): %s\n", + uid, errstring(ret)); + return ret; + } fp = fopen(fname, "r"); if (fp == NULL) { - usrerr("Cannot open %s", fname); - return; + int ret = errno; + + clrevent(ev); + return ret; } - if (getctladdr(ctladdr) == NULL) + + if (ca == NULL) { struct stat st; if (fstat(fileno(fp), &st) < 0) + { + int ret = errno; + + clrevent(ev); syserr("Cannot fstat %s!", fname); + return ret; + } ctladdr->q_uid = st.st_uid; ctladdr->q_gid = st.st_gid; ctladdr->q_flags |= QGOODUID; } + clrevent(ev); + + if (bitset(EF_VRFYONLY, e->e_flags)) + { + /* don't do any more now */ + ctladdr->q_flags |= QVERIFIED; + e->e_nrcpts++; + xfclose(fp, "include", fname); + return 0; + } + /* read the file -- each line is a comma-separated list. */ FileName = fname; LineNumber = 0; + ctladdr->q_flags &= ~QSELFREF; + nincludes = 0; while (fgets(buf, sizeof buf, fp) != NULL) { - register char *p = index(buf, '\n'); + register char *p = strchr(buf, '\n'); LineNumber++; if (p != NULL) *p = '\0'; - if (buf[0] == '\0') + if (buf[0] == '#' || buf[0] == '\0') continue; - CurEnv->e_to = oldto; - message(Arpa_Info, "%s to %s", msg, buf); + e->e_to = NULL; + message("%s to %s", + forwarding ? "forwarding" : "sending", buf); +#ifdef LOG + if (forwarding && LogLevel > 9) + syslog(LOG_INFO, "%s: forward %s => %s", + e->e_id, oldto, buf); +#endif + AliasLevel++; - sendtolist(buf, ctladdr, sendq); + nincludes += sendtolist(buf, ctladdr, sendq, e); AliasLevel--; } + if (nincludes > 0 && !bitset(QSELFREF, ctladdr->q_flags)) + { + if (tTd(27, 5)) + { + printf("include: QDONTSEND "); + printaddr(ctladdr, FALSE); + } + ctladdr->q_flags |= QDONTSEND; + } - (void) fclose(fp); + (void) xfclose(fp, "include", fname); FileName = oldfilename; LineNumber = oldlinenumber; + return 0; +} + +static +includetimeout() +{ + longjmp(CtxIncludeTimeout, 1); } /* ** SENDTOARGV -- send to an argument vector. ** ** Parameters: ** argv -- argument vector to send to. +** e -- the current envelope. ** ** Returns: ** none. @@ -544,29 +740,15 @@ include(fname, msg, ctladdr, sendq) ** send queue. */ -sendtoargv(argv) +sendtoargv(argv, e) register char **argv; + register ENVELOPE *e; { register char *p; while ((p = *argv++) != NULL) { - if (argv[0] != NULL && argv[1] != NULL && !strcasecmp(argv[0], "at")) - { - char nbuf[MAXNAME]; - - if (strlen(p) + strlen(argv[1]) + 2 > sizeof nbuf) - usrerr("address overflow"); - else - { - (void) strcpy(nbuf, p); - (void) strcat(nbuf, "@"); - (void) strcat(nbuf, argv[1]); - p = newstr(nbuf); - argv += 2; - } - } - sendtolist(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); + (void) sendtolist(p, (ADDRESS *) NULL, &e->e_sendqueue, e); } } /* diff --git a/usr.sbin/sendmail/src/savemail.c b/usr.sbin/sendmail/src/savemail.c index 1a7fbe2b6f..f47fc4e0e8 100644 --- a/usr.sbin/sendmail/src/savemail.c +++ b/usr.sbin/sendmail/src/savemail.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,10 +33,9 @@ */ #ifndef lint -static char sccsid[] = "@(#)savemail.c 5.14 (Berkeley) 8/29/90"; +static char sccsid[] = "@(#)savemail.c 8.3 (Berkeley) 7/13/93"; #endif /* not lint */ -# include <sys/types.h> # include <pwd.h> # include "sendmail.h" @@ -77,7 +76,7 @@ savemail(e) register struct passwd *pw; register FILE *fp; int state; - auto ADDRESS *q; + auto ADDRESS *q = NULL; char buf[MAXLINE+1]; extern struct passwd *getpwnam(); register char *p; @@ -85,16 +84,18 @@ savemail(e) typedef int (*fnptr)(); if (tTd(6, 1)) - printf("\nsavemail, ErrorMode = %c\n", ErrorMode); + { + printf("\nsavemail, errormode = %c, id = %s\n e_from=", + e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id); + printaddr(&e->e_from, FALSE); + } - if (bitset(EF_RESPONSE, e->e_flags)) - return; - if (e->e_class < 0) + if (e->e_id == NULL) { - message(Arpa_Info, "Dumping junk mail"); + /* can't return a message with no id */ return; } - ForceMail = TRUE; + e->e_flags &= ~EF_FATALERRS; /* @@ -104,9 +105,10 @@ savemail(e) if (e->e_from.q_paddr == NULL) { - if (parseaddr("root", &e->e_from, 0, '\0') == NULL) + e->e_sender = "Postmaster"; + if (parseaddr(e->e_sender, &e->e_from, 0, '\0', NULL, e) == NULL) { - syserr("Cannot parse root!"); + syserr("553 Cannot parse Postmaster!"); ExitStat = EX_SOFTWARE; finis(); } @@ -128,7 +130,7 @@ savemail(e) */ /* determine starting state */ - switch (ErrorMode) + switch (e->e_errormode) { case EM_WRITE: state = ESM_REPORT; @@ -154,11 +156,23 @@ savemail(e) return; default: - syserr("savemail: ErrorMode x%x\n"); + syserr("554 savemail: bogus errormode x%x\n", e->e_errormode); state = ESM_MAIL; break; } + /* if this is already an error response, send to postmaster */ + if (bitset(EF_RESPONSE, e->e_flags)) + { + if (e->e_parent != NULL && + bitset(EF_RESPONSE, e->e_parent->e_flags)) + { + /* got an error sending a response -- can it */ + return; + } + state = ESM_POSTMASTER; + } + while (state != ESM_DONE) { if (tTd(6, 5)) @@ -187,7 +201,7 @@ savemail(e) break; } - expand("\001n", buf, &buf[sizeof buf - 1], e); + expand("\201n", buf, &buf[sizeof buf - 1], e); printf("\r\nMessage from %s...\r\n", buf); printf("Errors occurred while sending mail.\r\n"); if (e->e_xfp != NULL) @@ -208,14 +222,13 @@ savemail(e) while (fgets(buf, sizeof buf, fp) != NULL && !ferror(stdout)) fputs(buf, stdout); - (void) fclose(fp); + (void) xfclose(fp, "savemail transcript", e->e_id); } printf("Original message will be saved in dead.letter.\r\n"); state = ESM_DEADLETTER; break; case ESM_MAIL: - case ESM_POSTMASTER: /* ** If mailing back, do it. ** Throw away all further output. Don't alias, @@ -227,39 +240,62 @@ savemail(e) ** it has already been sent to the sender. */ - if (state == ESM_MAIL) + if (strcmp(e->e_from.q_paddr, "<>") != 0) + (void) sendtolist(e->e_from.q_paddr, + (ADDRESS *) NULL, + &e->e_errorqueue, e); + + /* deliver a cc: to the postmaster if desired */ + if (PostMasterCopy != NULL) { - if (e->e_errorqueue == NULL) - sendtolist(e->e_from.q_paddr, - (ADDRESS *) NULL, - &e->e_errorqueue); - - /* deliver a cc: to the postmaster if desired */ - if (PostMasterCopy != NULL) - sendtolist(PostMasterCopy, - (ADDRESS *) NULL, - &e->e_errorqueue); - q = e->e_errorqueue; + auto ADDRESS *rlist = NULL; + + (void) sendtolist(PostMasterCopy, + (ADDRESS *) NULL, + &rlist, e); + (void) returntosender(e->e_message, + rlist, FALSE, e); } - else + q = e->e_errorqueue; + if (q == NULL) { - if (parseaddr("postmaster", q, 0, '\0') == NULL) - { - syserr("cannot parse postmaster!"); - ExitStat = EX_SOFTWARE; - state = ESM_USRTMP; - break; - } + /* this is an error-error */ + state = ESM_POSTMASTER; + break; + } + if (returntosender(e->e_message, + q, (e->e_class >= 0), e) == 0) + { + state = ESM_DONE; + break; + } + + /* didn't work -- return to postmaster */ + state = ESM_POSTMASTER; + break; + + case ESM_POSTMASTER: + /* + ** Similar to previous case, but to system postmaster. + */ + + q = NULL; + if (sendtolist("postmaster", NULL, &q, e) <= 0) + { + syserr("553 cannot parse postmaster!"); + ExitStat = EX_SOFTWARE; + state = ESM_USRTMP; + break; } - if (returntosender(e->e_message != NULL ? e->e_message : - "Unable to deliver mail", - q, TRUE) == 0) + if (returntosender(e->e_message, + q, (e->e_class >= 0), e) == 0) { state = ESM_DONE; break; } - state = state == ESM_MAIL ? ESM_POSTMASTER : ESM_USRTMP; + /* didn't work -- last resort */ + state = ESM_USRTMP; break; case ESM_DEADLETTER: @@ -282,24 +318,23 @@ savemail(e) } if (p == NULL) { - syserr("Can't return mail to %s", e->e_from.q_paddr); + /* no local directory */ state = ESM_MAIL; break; } if (e->e_dfp != NULL) { - auto ADDRESS *q; bool oldverb = Verbose; /* we have a home directory; open dead.letter */ define('z', p, e); - expand("\001z/dead.letter", buf, &buf[sizeof buf - 1], e); + expand("\201z/dead.letter", buf, &buf[sizeof buf - 1], e); Verbose = TRUE; - message(Arpa_Info, "Saving message in %s", buf); + message("Saving message in %s", buf); Verbose = oldverb; e->e_to = buf; q = NULL; - sendtolist(buf, (ADDRESS *) NULL, &q); + (void) sendtolist(buf, &e->e_from, &q, e); if (deliver(e, q) == 0) state = ESM_DONE; else @@ -317,36 +352,38 @@ savemail(e) ** Log the mail in /usr/tmp/dead.letter. */ - fp = dfopen("/usr/tmp/dead.letter", "a"); + if (e->e_class < 0) + { + state = ESM_DONE; + break; + } + + fp = dfopen("/usr/tmp/dead.letter", + O_WRONLY|O_CREAT|O_APPEND, FileMode); if (fp == NULL) { state = ESM_PANIC; break; } - putfromline(fp, ProgMailer); - (*e->e_puthdr)(fp, ProgMailer, e); - putline("\n", fp, ProgMailer); - (*e->e_putbody)(fp, ProgMailer, e); - putline("\n", fp, ProgMailer); + putfromline(fp, FileMailer, e); + (*e->e_puthdr)(fp, FileMailer, e); + putline("\n", fp, FileMailer); + (*e->e_putbody)(fp, FileMailer, e, NULL); + putline("\n", fp, FileMailer); (void) fflush(fp); state = ferror(fp) ? ESM_PANIC : ESM_DONE; - (void) fclose(fp); + (void) xfclose(fp, "savemail", "/usr/tmp/dead.letter"); break; default: - syserr("savemail: unknown state %d", state); + syserr("554 savemail: unknown state %d", state); /* fall through ... */ case ESM_PANIC: - syserr("savemail: HELP!!!!"); -# ifdef LOG - if (LogLevel >= 1) - syslog(LOG_ALERT, "savemail: HELP!!!!"); -# endif LOG - /* leave the locked queue & transcript files around */ + syserr("554 savemail: cannot save rejected email anywhere"); exit(EX_SOFTWARE); } } @@ -359,6 +396,7 @@ savemail(e) ** returnq -- the queue of people to send the message to. ** sendbody -- if TRUE, also send back the body of the ** message; otherwise just send the header. +** e -- the current envelope. ** ** Returns: ** zero -- if everything went ok. @@ -372,80 +410,119 @@ savemail(e) static bool SendBody; #define MAXRETURNS 6 /* max depth of returning messages */ +#define ERRORFUDGE 100 /* nominal size of error message text */ -returntosender(msg, returnq, sendbody) +returntosender(msg, returnq, sendbody, e) char *msg; ADDRESS *returnq; bool sendbody; + register ENVELOPE *e; { char buf[MAXNAME]; extern putheader(), errbody(); register ENVELOPE *ee; - extern ENVELOPE *newenvelope(); + ENVELOPE *oldcur = CurEnv; ENVELOPE errenvelope; static int returndepth; register ADDRESS *q; + if (returnq == NULL) + return (-1); + + if (msg == NULL) + msg = "Unable to deliver mail"; + if (tTd(6, 1)) { - printf("Return To Sender: msg=\"%s\", depth=%d, CurEnv=%x,\n", - msg, returndepth, CurEnv); - printf("\treturnq="); + printf("Return To Sender: msg=\"%s\", depth=%d, e=%x, returnq=", + msg, returndepth, e); printaddr(returnq, TRUE); } if (++returndepth >= MAXRETURNS) { if (returndepth != MAXRETURNS) - syserr("returntosender: infinite recursion on %s", returnq->q_paddr); + syserr("554 returntosender: infinite recursion on %s", returnq->q_paddr); /* don't "unrecurse" and fake a clean exit */ /* returndepth--; */ return (0); } SendBody = sendbody; - define('g', "\001f", CurEnv); - ee = newenvelope(&errenvelope); - define('a', "\001b", ee); + define('g', e->e_from.q_paddr, e); + ee = newenvelope(&errenvelope, e); + define('a', "\201b", ee); + define('r', "internal", ee); + define('s', "localhost", ee); + define('_', "localhost", ee); ee->e_puthdr = putheader; ee->e_putbody = errbody; ee->e_flags |= EF_RESPONSE; - if (!bitset(EF_OLDSTYLE, CurEnv->e_flags)) + if (!bitset(EF_OLDSTYLE, e->e_flags)) ee->e_flags &= ~EF_OLDSTYLE; ee->e_sendqueue = returnq; + ee->e_msgsize = e->e_msgsize + ERRORFUDGE; openxscript(ee); for (q = returnq; q != NULL; q = q->q_next) { + if (bitset(QBADADDR, q->q_flags)) + continue; + + if (!bitset(QDONTSEND, q->q_flags)) + ee->e_nrcpts++; + + if (!DontPruneRoutes && pruneroute(q->q_paddr)) + parseaddr(q->q_paddr, q, 0, '\0', NULL, e); + if (q->q_alias == NULL) - addheader("to", q->q_paddr, ee); + addheader("To", q->q_paddr, ee); } +# ifdef LOG + if (LogLevel > 5) + syslog(LOG_INFO, "%s: %s: return to sender: %s", + e->e_id, ee->e_id, msg); +# endif + (void) sprintf(buf, "Returned mail: %s", msg); - addheader("subject", buf, ee); + addheader("Subject", buf, ee); + if (SendMIMEErrors) + { + addheader("MIME-Version", "1.0", ee); + (void) sprintf(buf, "%s.%ld/%s", + ee->e_id, curtime(), MyHostName); + ee->e_msgboundary = newstr(buf); + (void) sprintf(buf, "multipart/mixed; boundary=\"%s\"", + ee->e_msgboundary); + addheader("Content-Type", buf, ee); + } /* fake up an address header for the from person */ - expand("\001n", buf, &buf[sizeof buf - 1], CurEnv); - if (parseaddr(buf, &ee->e_from, -1, '\0') == NULL) + expand("\201n", buf, &buf[sizeof buf - 1], e); + if (parseaddr(buf, &ee->e_from, 1, '\0', NULL, e) == NULL) { - syserr("Can't parse myself!"); + syserr("553 Can't parse myself!"); ExitStat = EX_SOFTWARE; returndepth--; return (-1); } - loweraddr(&ee->e_from); + ee->e_sender = ee->e_from.q_paddr; /* push state into submessage */ CurEnv = ee; - define('f', "\001n", ee); + define('f', "\201n", ee); define('x', "Mail Delivery Subsystem", ee); - eatheader(ee); + eatheader(ee, TRUE); + + /* mark statistics */ + markstats(ee, (ADDRESS *) NULL); /* actually deliver the error message */ sendall(ee, SM_DEFAULT); /* restore state */ dropenvelope(ee); - CurEnv = CurEnv->e_parent; + CurEnv = oldcur; returndepth--; /* should check for delivery errors here */ @@ -475,8 +552,87 @@ errbody(fp, m, e) register ENVELOPE *e; { register FILE *xfile; - char buf[MAXLINE]; char *p; + register ADDRESS *q; + bool printheader; + char buf[MAXLINE]; + + if (e->e_parent == NULL) + { + syserr("errbody: null parent"); + putline(" ----- Original message lost -----\n", fp, m); + return; + } + + /* + ** Output MIME header. + */ + + if (e->e_msgboundary != NULL) + { + putline("This is a MIME-encapsulated message", fp, m); + putline("", fp, m); + (void) sprintf(buf, "--%s", e->e_msgboundary); + putline(buf, fp, m); + putline("", fp, m); + } + + /* + ** Output error message header (if specified and available). + */ + + if (ErrMsgFile != NULL) + { + if (*ErrMsgFile == '/') + { + xfile = fopen(ErrMsgFile, "r"); + if (xfile != NULL) + { + while (fgets(buf, sizeof buf, xfile) != NULL) + { + expand(buf, buf, &buf[sizeof buf - 1], e); + putline(buf, fp, m); + } + (void) fclose(xfile); + putline("\n", fp, m); + } + } + else + { + expand(ErrMsgFile, buf, &buf[sizeof buf - 1], e); + putline(buf, fp, m); + putline("", fp, m); + } + } + + /* + ** Output message introduction + */ + + printheader = TRUE; + for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) + { + if (bitset(QBADADDR|QREPORT, q->q_flags)) + { + if (printheader) + { + putline(" ----- The following addresses had delivery problems -----", + fp, m); + printheader = FALSE; + } + if (q->q_alias != NULL) + strcpy(buf, q->q_alias->q_paddr); + else + strcpy(buf, q->q_paddr); + if (bitset(QBADADDR, q->q_flags)) + strcat(buf, " (hard error -- address deleted)"); + else + strcat(buf, " (temporary failure -- will retry)"); + putline(buf, fp, m); + } + } + if (!printheader) + putline("\n", fp, m); /* ** Output transcript of errors @@ -487,16 +643,16 @@ errbody(fp, m, e) if ((xfile = fopen(p, "r")) == NULL) { syserr("Cannot open %s", p); - fprintf(fp, " ----- Transcript of session is unavailable -----\n"); + putline(" ----- Transcript of session is unavailable -----\n", fp, m); } else { - fprintf(fp, " ----- Transcript of session follows -----\n"); + putline(" ----- Transcript of session follows -----\n", fp, m); if (e->e_xfp != NULL) (void) fflush(e->e_xfp); while (fgets(buf, sizeof buf, xfile) != NULL) putline(buf, fp, m); - (void) fclose(xfile); + (void) xfclose(xfile, "errbody xscript", p); } errno = 0; @@ -505,33 +661,44 @@ errbody(fp, m, e) */ if (NoReturn) - fprintf(fp, "\n ----- Return message suppressed -----\n\n"); - else if (e->e_parent->e_dfp != NULL) + SendBody = FALSE; + putline("", fp, m); + if (e->e_parent->e_df != NULL) { if (SendBody) - { - putline("\n", fp, m); putline(" ----- Unsent message follows -----\n", fp, m); - (void) fflush(fp); - putheader(fp, m, e->e_parent); - putline("\n", fp, m); - putbody(fp, m, e->e_parent); - } else + putline(" ----- Message header follows -----\n", fp, m); + (void) fflush(fp); + + if (e->e_msgboundary != NULL) { - putline("\n", fp, m); - putline(" ----- Message header follows -----\n", fp, m); - (void) fflush(fp); - putheader(fp, m, e->e_parent); + putline("", fp, m); + (void) sprintf(buf, "--%s", e->e_msgboundary); + putline(buf, fp, m); + putline("Content-Type: message/rfc822", fp, m); + putline("", fp, m); } + putheader(fp, m, e->e_parent); + putline("", fp, m); + if (SendBody) + putbody(fp, m, e->e_parent, e->e_msgboundary); + else + putline(" ----- Message body suppressed -----", fp, m); } else { - putline("\n", fp, m); putline(" ----- No message was collected -----\n", fp, m); - putline("\n", fp, m); } + if (e->e_msgboundary != NULL) + { + putline("", fp, m); + (void) sprintf(buf, "--%s--", e->e_msgboundary); + putline(buf, fp, m); + } + putline("", fp, m); + /* ** Cleanup and exit */ @@ -539,3 +706,62 @@ errbody(fp, m, e) if (errno != 0) syserr("errbody: I/O error"); } + /* +** PRUNEROUTE -- prune an RFC-822 source route +** +** Trims down a source route to the last internet-registered hop. +** This is encouraged by RFC 1123 section 5.3.3. +** +** Parameters: +** addr -- the address +** +** Returns: +** TRUE -- address was modified +** FALSE -- address could not be pruned +** +** Side Effects: +** modifies addr in-place +*/ + +pruneroute(addr) + char *addr; +{ +#ifdef NAMED_BIND + char *start, *at, *comma; + char c; + int rcode; + char hostbuf[BUFSIZ]; + char *mxhosts[MAXMXHOSTS + 1]; + + /* check to see if this is really a route-addr */ + if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>') + return FALSE; + start = strchr(addr, ':'); + at = strrchr(addr, '@'); + if (start == NULL || at == NULL || at < start) + return FALSE; + + /* slice off the angle brackets */ + strcpy(hostbuf, at + 1); + hostbuf[strlen(hostbuf) - 1] = '\0'; + + while (start) + { + if (getmxrr(hostbuf, mxhosts, FALSE, &rcode) > 0) + { + strcpy(addr + 1, start + 1); + return TRUE; + } + c = *start; + *start = '\0'; + comma = strrchr(addr, ','); + if (comma && comma[1] == '@') + strcpy(hostbuf, comma + 2); + else + comma = 0; + *start = c; + start = comma; + } +#endif + return FALSE; +} diff --git a/usr.sbin/sendmail/src/sendmail.8 b/usr.sbin/sendmail/src/sendmail.8 index 6f51054ab2..c2e0678859 100644 --- a/usr.sbin/sendmail/src/sendmail.8 +++ b/usr.sbin/sendmail/src/sendmail.8 @@ -1,5 +1,5 @@ -.\" Copyright (c) 1988, 1991 The Regents of the University of California. -.\" All rights reserved. +.\" Copyright (c) 1988, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -29,9 +29,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)sendmail.8 6.10 (Berkeley) 8/5/91 +.\" @(#)sendmail.8 8.2 (Berkeley) 7/11/93 .\" -.Dd August 5, 1991 +.Dd July 11, 1993 .Dt SENDMAIL 8 .Os BSD 4 .Sh NAME @@ -80,9 +80,15 @@ expansions, e.g., if `john' sends to `group', and `group' includes `john' in the expansion, then the letter will not be delivered to `john'. -.Pp -Flags are: +.Ss Parameters .Bl -tag -width Fl +.It Fl B Ns Ar type +Set the body type to +.Ar type . +Current legal values +.Li 7BIT +or +.Li 8BITMIME . .It Fl ba Go into .Tn ARPANET @@ -169,6 +175,10 @@ Set option to the specified .Em value . Options are described below. +.It Fl p Ns Ar protocol +Set the name of the protocol used to receive the message. +This can be a simple protocol name such as ``UUCP'' +or a protocol and hostname, such as ``UUCP:ucbvax''. .It Fl q Ns Bq Ar time Processed saved messages in the queue at given intervals. If @@ -217,8 +227,13 @@ receive copies even if listed in the message header. .It Fl v Go into verbose mode. Alias expansions will be announced, etc. +.It Fl X Ar logfile +Log all traffic in and out of mailers in the indicated log file. +This should only be used as a last resort +for debugging mailer bugs. +It will log a lot of data very quickly. .El -.Pp +.Ss Options There are also a number of processing options that may be set. Normally these will only be used by a system administrator. Options may be set either on the command line @@ -226,16 +241,26 @@ using the .Fl o flag or in the configuration file. -These are described in detail in the +This is a partial list; +for a complete list (and details), consult the .%T "Sendmail Installation and Operation Guide" . The options are: .Bl -tag -width Fl .It Li A Ns Ar file Use alternate alias file. +.It Li b Ns Ar nblocks +The minimum number of free blocks needed on the spool filesystem. .It Li c On mailers that are considered ``expensive'' to connect to, don't initiate immediate connection. This requires queueing. +.It Li C Ar N +Checkpoint the queue file after every +.Ar N +successful deliveries (default 10). +This avoids excessive duplicate deliveries +when sending to long mailing lists +interrupted by system crashes. .It Li d Ns Ar x Set the delivery mode to .Ar x . @@ -279,32 +304,40 @@ and if the sender is local to this machine, a copy of the message is appended to the file .Pa dead.letter in the sender's home directory. -.It Li F Ns Ar mode -The mode to use when creating temporary files. .It Li f Save .Tn UNIX Ns \-style From lines at the front of messages. +.It Li G +Match local mail names against the GECOS portion of the password file. .It Li g Ar N The default group id to use when calling mailers. .It Li H Ns Ar file The .Tn SMTP help file. +.It Li h Ar N +The maximum number of times a message is allowed to ``hop'' +before we decide it is in a loop. .It Li i Do not take dots on a line by themselves as a message terminator. -.It Li k Ar N -Checkpoint the queue file after every -.Ar N -successful deliveries (default 10). -This avoids excessive duplicate deliveries -when sending to long mailing lists -interrupted by system crashes. +.It Li j +Send error messages in MIME format. +.It Li K Ns Ar timeout +Set connection cache timeout. +.It Li k Ns Ar N +Set connection cache size. .It Li L Ns Ar n The log level. +.It Li l +Pay attention to the Errors-To: header. .It Li m Send to ``me'' (the sender) also if I am in an alias expansion. +.It Li n +Validate the right hand side of aliases during a +.Xr newaliases 1 +command. .It Li o If set, this message may have old style headers. @@ -315,15 +348,6 @@ If set, an adaptive algorithm is used that will correctly determine the header format in most cases. .It Li Q Ns Ar queuedir Select the directory in which to queue messages. -.It Li r Ns Ar timeout -The timeout on reads; -if none is set, -.Nm sendmail -will wait forever for a mailer. -This option violates the word (if not the intent) of the -.Tn SMTP -specification, -show the timeout should probably be fairly large. .It Li S Ns Ar file Save statistics in the named file. .It Li s @@ -349,22 +373,11 @@ This may not be available if your sendmail does not have the option compiled in. .It Li u Ns Ar N Set the default user id for mailers. -.It Li w -If not set, name server lookups will us a querytype of ANY -to find types -.Dv CNAME , A , -and -.Dv MX , -and will cause all existing records to be cached by our local server. -If there is (might be) a wildcard MX in the local domain -or its parents that are searched, you -.ul -must -set this option, which will use a querytype of -.Dv CNAME -only; -otherwise, it would cause all fully-qualified names -to match as names in the local domain. +.It Li Y +Fork each job during queue runs. +May be convenient on memory-poor machines. +.It Li 7 +Strip incoming messages to seven bits. .El .Pp In aliases, @@ -446,8 +459,7 @@ these values are only approximations. .Bl -tag -width /usr/lib/sendmail.fc -compact .It Pa /etc/aliases raw data for alias names -.It Pa /etc/aliases.pag -.It Pa /etc/aliases.dir +.It Pa /etc/aliases.db data base of alias names .It Pa /etc/sendmail.cf configuration file @@ -459,6 +471,8 @@ help file collected statistics .It Pa /var/spool/mqueue/* temp files +.It Pa /var/run/sendmail.pid +The process id of the daemon .El .Sh SEE ALSO .Xr binmail 1 , diff --git a/usr.sbin/sendmail/src/sendmail.cf b/usr.sbin/sendmail/src/sendmail.cf deleted file mode 100644 index 0252d7ff03..0000000000 --- a/usr.sbin/sendmail/src/sendmail.cf +++ /dev/null @@ -1,634 +0,0 @@ -# Copyright (c) 1983 Eric P. Allman -# Copyright (c) 1988 The Regents of the University of California. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)sendmail.cf 5.2 (Berkeley) 4/23/91 -# - -# built by root on Mon Aug 21 13:47:04 PDT 1989 -# in /usr/src/local/nettables/sendmail.cf/cf on monet.Berkeley.EDU -# -############################################################ -############################################################ -##### -##### SENDMAIL CONFIGURATION FILE -##### -############################################################ -############################################################ - - -################## -# local info # -################## - -# file containing our internet aliases -Fw/etc/sendmail.cw - -# uucp hostnames -DUokeeffe -CUokeeffe - -# local UUCP connections -CV blia -CV ccicpg -CV mjk -CV oxford -CV zulu - - -############################# -### Setup Information ### -############################# - - -###################### -# General Macros # -###################### - -# local domain name -DDBerkeley.EDU - -# Internet relay host -- machines in our domain that are not -# registered with the NIC will be "hidden" behind this relay machine -# with the % kludge, although SMTP delivery will still be performed -# by the sending machine. Someday this will go away. -DAucbvax.Berkeley.EDU - -# UUCP relay host -DRucbvax.Berkeley.EDU - -# csnet relay host -DCrelay.cs.net - -# bitnet relay host -DBjade.Berkeley.EDU - -# my official hostname -Dj$w - - - -############### -# Classes # -############### - -# Internal ("fake") domains that we use in rewriting -CIUUCP BITNET CSNET - - - -############################################################ -############################################################ -##### -##### BERKELEY HOSTS REGISTERED WITH THE NIC -##### -############################################################ -############################################################ - - -CNbach ucbbach -CNbizet ucbbizet -CNcad ucbcad -CNdegas ucbdegas -CNeast ucbeast -CNernie ucbernie -CNesvax ucbesvax -CNjade ucbjade -CNji ucbji -CNmike ucbmike -CNmonet ucbmonet -CNpostgres -CNrenoir ucbrenoir -CNucbarpa -CNucbvax -CNviolet ucbviolet - - -###################### -# Version Number # -###################### - -DZ1.37 - - -###################### -# Special macros # -###################### - -# my name -DnMAILER-DAEMON -# UNIX header format -DlFrom $g $d -# delimiter (operator) characters -Do.:%@!^=/[] -# format of a total name -Dq$g$?x ($x)$. -# SMTP login message -De$j Sendmail $v/$Z ready at $b - -############### -# Options # -############### - -# location of alias file -OA/etc/aliases -# wait up to ten minutes for alias file rebuild -Oa10 -# substitution for space (blank) characters -OB. -# (don't) connect to "expensive" mailers -#Oc -# default delivery mode (deliver in background) -Odbackground -# temporary file mode -OF0600 -# default GID -Og1 -# location of help file -OH/usr/share/misc/sendmail.hf -# log level -OL9 -# default network name -ONARPA -# default messages to old style -Oo -# queue directory -OQ/var/spool/mqueue -# read timeout -- violates protocols -Or2h -# status file -OS/var/log/sendmail.st -# queue up everything before starting transmission -Os -# default timeout interval -OT3d -# time zone names (V6 only) -OtPST,PDT -# default UID -Ou1 -# wizard's password -OW* -# load average at which we just queue messages -Ox8 -# load average at which we refuse connections -OX12 - -########################### -# Message precedences # -########################### - -Pfirst-class=0 -Pspecial-delivery=100 -Pbulk=-60 -Pjunk=-100 - -##################### -# Trusted users # -##################### - -Troot -Tdaemon -Tuucp - -######################### -# Format of headers # -######################### - -H?P?Return-Path: <$g> -HReceived: $?sfrom $s $.by $j ($v/$Z) - id $i; $b -H?D?Resent-Date: $a -H?D?Date: $a -H?F?Resent-From: $q -H?F?From: $q -H?x?Full-Name: $x -HSubject: -# HPosted-Date: $a -# H?l?Received-Date: $b -H?M?Resent-Message-Id: <$t.$i@$j> -H?M?Message-Id: <$t.$i@$j> - - - -########################### -### Rewriting Rules ### -########################### - - -################################ -# Sender Field Pre-rewriting # -################################ -S1 -#R$*<$*>$* $1$2$3 defocus - -################################### -# Recipient Field Pre-rewriting # -################################### -S2 -#R$*<$*>$* $1$2$3 defocus - - - -################################# -# Final Output Post-rewriting # -################################# -S4 - -R@ $@ handle <> error addr - -# resolve numeric addresses to name if possible -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 lookup numeric internet addr - -# externalize local domain info -R$*<$+>$* $1$2$3 defocus -R@$+:@$+:$+ @$1,@$2:$3 <route-addr> canonical - -# UUCP must always be presented in old form -R$+@$-.UUCP $2!$1 u@h.UUCP => h!u - -# delete duplicate local names -R$+%$=w@$=w $1@$w u%host@host => u@host -R$+%$=w@$=w.$D $1@$w u%host@host => u@host - - -########################### -# Name Canonicalization # -########################### -S3 - -# handle "from:<>" special case -R$*<>$* $@@ turn into magic token - -# basic textual canonicalization -- note RFC733 heuristic here -R$*<$*<$*<$+>$*>$*>$* $4 3-level <> nesting -R$*<$*<$+>$*>$* $3 2-level <> nesting -R$*<$+>$* $2 basic RFC821/822 parsing - -# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later -R@$+,$+ @$1:$2 change all "," to ":" - -# localize and dispose of route-based addresses -R@$+:$+ $@$>6<@$1>:$2 handle <route-addr> - -# more miscellaneous cleanup -R$+ $:$>8$1 host dependent cleanup -R$+:$*;@$+ $@$1:$2;@$3 list syntax -R$+:$*; $@$1:$2; list syntax -R$+@$+ $:$1<@$2> focus on domain -R$+<$+@$+> $1$2<@$3> move gaze right -R$+<@$+> $@$>6$1<@$2> already canonical - -# convert old-style addresses to a domain-based address -R$+^$+ $1!$2 convert ^ to ! -R$-!$+ $@$>6$2<@$1.UUCP> resolve uucp names -R$+.$-!$+ $@$>6$3<@$1.$2> domain uucps -R$+!$+ $@$>6$2<@$1.UUCP> uucp subdomains -R$+%$+ $:$>9$1%$2 user%host -R$+<@$+> $@$>6$1<@$2> already canonical -R$-.$+ $@$>6$2<@$1> host.user - - -################################# -# special local conversions # -################################# - -S6 -R$*<@$=w>$* $:$1<@$w>$3 get into u@$w form -R$*<@$=w.$D>$* $:$1<@$w>$3 -R$*<@$=U.UUCP>$* $:$1<@$w>$3 - - -################################ -# Change rightmost % to @. # -################################ - -S9 -R$*%$* $1@$2 First make them all @'s. -R$*@$*@$* $1%$2@$3 Undo all but the last. -R$*@$* $@$1<@$2> Put back the brackets. - - - -################### -### Mailers ### -################### - - -############################################################ -############################################################ -##### -##### Local and Program Mailer specification -##### -############################################################ -############################################################ - -Mlocal, P=/usr/libexec/delivermail, F=lsDFMmn, S=10, R=20, A=mail -r $g -d $u -Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u - -S10 -R@ $n errors to mailer-daemon - - -############################################################ -############################################################ -##### -##### Local Domain SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to remain -##### the local domain. Hence, they can refer to hosts that are -##### not registered in the NIC host table. -##### -############################################################ -############################################################ - -Mtcpld, P=[IPC], F=mDFMueXLC, S=17, R=27, A=IPC $h, E=\r\n - -S17 - -# cleanup forwarding a bit -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 canonicalize -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - - - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.bitnet -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S27 - -# cleanup -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form -R$*%$*<@$w> $:$>9$1%$2 user%localhost@localdomain - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host as user@host.domain -R$- $@$1<@$w> user w/o host -R$+<@$w> $@$1<@$w> this host -R$+<@$=w> $@$1<@$w> or an alias -R$+<@$-> $:$1<@$[$2$]> ask nameserver -R$+<@$w> $@$1<@$w> this host -R$+<@$-> $@$1<@$2.$D> if nameserver fails - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output fake domains as user%fake@relay - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### Internet SMTP Mailer specification -##### -##### Messages processed by this specification are assumed to leave -##### the local domain -- hence, they must be canonical according to -##### RFC822 etc. This means that machines not registered with -##### the NIC must be hidden behind our Internet relay. -##### -############################################################ -############################################################ - -Mtcp, P=[IPC], F=mDFMueXLC, S=14, R=24, A=IPC $h, E=\r\n - -S14 - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# output internal ("fake") domains as "user%host@relay" - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1<@$w> user@host.UUCP - - -S24 - -# put in <> kludge -R$*<$*>$* $1$2$3 defocus -R$* $:$>3$1 now canonical form - -# pass <route-addr>'s through -R<@$+>$* $@<@$[$1$]>$2 resolve <route-addr> - -# map colons to dots everywhere..... -R$*:$* $1.$2 map colons to dots - -# output local host in user@host.domain syntax -R$- $1<@$w> user w/o host -R$+<@$=w> $:$1<@$w> this host -R$+<@$-> $:$1<@$[$2$]> canonicalize into dom -R$+<@$-> $:$1<@$2.$D> if nameserver fails -R$+<@$=N.$D> $@$1<@$2.$D> nic-reg hosts are ok -R$+<@$*.$D> $@$1%$2.$D<@$A> else -> u%h@gateway - -# if not local, and not a "fake" domain, ask the nameserver -R$+<@$+.$~I> $@$1<@$[$2.$3$]> user@host.domain -R$+<@[$+]> $@$1<@[$2]> already ok - -# Hide fake domains behind relays - -R$+<@$+.BITNET> $@$1%$2.BITNET<@$B> user@host.BITNET -R$+<@$+.CSNET> $@$1%$2.CSNET<@$C> user@host.CSNET -R$+<@$+.UUCP> $@$2!$1 user@host.UUCP - - - -############################################################ -############################################################ -##### -##### UUCP Mailer specification -##### -############################################################ -############################################################ - - -Muucp, P=/usr/bin/uux, F=DFMhuU, S=13, R=23, M=100000, - A=uux - -r -z -a$f -gC $h!rmail ($u) - -S13 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$+> $2!$1 uucpize (no @'s in addr) -R$w!$+ $1 strip local name -R$+ $:$U!$1 stick on our host name -R$=U!$-%$- $:$1!$2@$3.$D ucbvax!user@host.domain - -S23 -R$+ $:$>5$1 convert to old style -R$*<@$=w>$* $1<@$w>$2 resolve abbreviations -R$*<@$->$* $1<@$2.$D>$3 resolve abbreviations -R$+<@$w> $U!$1 a!b@here -> here!a!b -R$=U!$+ $2 here!a!b -> a!b -# sanity ... should not happen. -R$=U.$D!$+ $2 strip local name.domain - - -############################################################ -############################################################ -##### -##### Provide Backward Compatibility -##### -############################################################ -############################################################ - -##################################################### -# General code to convert back to old style names # -##################################################### -S5 - -R$+<@$w> $1 strip host -R$+<@$-.UUCP> $2!$1 u@host.UUCP => host!u - - - -##################### -### Rule Zero ### -##################### - - -############################################################ -############################################################ -##### -##### RULESET ZERO PREAMBLE -##### -##### The beginning of ruleset zero is constant through all -##### configurations. -##### -############################################################ -############################################################ - -S0 - -# first make canonical -R$*<$*>$* $1$2$3 defocus -R$+ $:$>3$1 make canonical - -# handle special cases -R$*<@[$+]>$* $:$1<@$[[$2]$]>$3 numeric internet addr -R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec -R$+ $:$>6$1 -R$-<@$w> $#local$:$1 -R@ $#error$:Invalid address handle <> form - -# canonicalize using the nameserver if not internal domain -R$*<@$*.$~I>$* $:$1<@$[$2.$3$]>$4 -R$*<@$->$* $:$1<@$[$2$]>$3 -R$*<@$->$* $:$1<@$2.$D>$3 if nameserver fails - -# now delete the local info -R<@$w>:$* $@$>0$1 @here:... -> ... -R$*<@$w> $@$>0$1 ...@here -> ... - -################################## -# End of ruleset zero preamble # -################################## - - -############################################### -### Machine dependent part of Rule Zero ### -############################################### - - - -# resolve local UUCP connections -R<@$=V.UUCP>:$+ $#uucp$@$1$:$2 @host.UUCP:... -R$+<@$=V.UUCP> $#uucp$@$2$:$1 user@host.UUCP - - - - -# resolve fake top level domains by forwarding to other hosts -R$*<@$+.BITNET>$* $#tcp$@$B$:$1<@$2.BITNET>$3 user@host.BITNET -R$*<@$+.CSNET>$* $#tcp$@$C$:$1<@$2.CSNET>$3 user@host.CSNET - - -# forward non-local UUCP traffic to our UUCP relay -R$*<@$*.UUCP>$* $#tcpld$@$R$:$1<@$2.UUCP> uucp mail - -# resolve SMTP traffic -R$*<@$*.$D>$* $#tcpld$@$2.$D$:$1<@$2.$D>$3 user@host.ourdomain -R$*<@$+>$* $#tcp$@$2$:$1<@$2>$3 user@host.ourdomain - -# remaining names must be local -R$+ $#local$:$1 everything else - diff --git a/usr.sbin/sendmail/src/sendmail.h b/usr.sbin/sendmail/src/sendmail.h index ac757f6712..ef9107eae7 100644 --- a/usr.sbin/sendmail/src/sendmail.h +++ b/usr.sbin/sendmail/src/sendmail.h @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,7 +31,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)sendmail.h 5.17 (Berkeley) 3/12/91 + * @(#)sendmail.h 8.3 (Berkeley) 7/13/93 */ /* @@ -41,31 +41,47 @@ # ifdef _DEFINE # define EXTERN # ifndef lint -static char SmailSccsId[] = "@(#)sendmail.h 5.17 3/12/91"; -# endif lint -# else _DEFINE +static char SmailSccsId[] = "@(#)sendmail.h 8.3 7/13/93"; +# endif +# else /* _DEFINE */ # define EXTERN extern -# endif _DEFINE +# endif /* _DEFINE */ +# include <unistd.h> +# include <stddef.h> +# include <stdlib.h> # include <stdio.h> # include <ctype.h> # include <setjmp.h> +# include <sysexits.h> +# include <string.h> +# include <time.h> +# include <errno.h> + # include "conf.h" # include "useful.h" # ifdef LOG -# include <sys/syslog.h> -# endif LOG +# include <syslog.h> +# endif /* LOG */ # ifdef DAEMON -# ifdef VMUNIX # include <sys/socket.h> +# endif +# ifdef NETINET # include <netinet/in.h> -# endif VMUNIX -# endif DAEMON +# endif +# ifdef NETISO +# include <netiso/iso.h> +# endif +# ifdef NETNS +# include <netns/ns.h> +# endif +# ifdef NETX25 +# include <netccitt/x25.h> +# endif -# define PSBUFSIZE (MAXNAME + MAXATOM) /* size of prescan buffer */ /* @@ -108,12 +124,13 @@ struct address char *q_host; /* host name */ struct mailer *q_mailer; /* mailer to use */ u_short q_flags; /* status flags, see below */ - short q_uid; /* user-id of receiver (if known) */ - short q_gid; /* group-id of receiver (if known) */ + uid_t q_uid; /* user-id of receiver (if known) */ + gid_t q_gid; /* group-id of receiver (if known) */ char *q_home; /* home dir (local mailer only) */ char *q_fullname; /* full name if known */ struct address *q_next; /* chain */ struct address *q_alias; /* address this results from */ + char *q_owner; /* owner of q_alias */ struct address *q_tchain; /* temporary use chain */ time_t q_timeout; /* timeout for this address */ }; @@ -126,6 +143,10 @@ typedef struct address ADDRESS; # define QPRIMARY 000010 /* set from argv */ # define QQUEUEUP 000020 /* queue for later transmission */ # define QSENT 000040 /* has been successfully delivered */ +# define QNOTREMOTE 000100 /* not an address for remote forwarding */ +# define QSELFREF 000200 /* this address references itself */ +# define QVERIFIED 000400 /* verified, but not expanded */ +# define QREPORT 001000 /* report this address in return message */ /* ** Mailer definition structure. ** Every mailer known to the system is declared in this @@ -145,38 +166,57 @@ struct mailer BITMAP m_flags; /* status flags, see below */ short m_mno; /* mailer number internally */ char **m_argv; /* template argument vector */ - short m_s_rwset; /* rewriting set for sender addresses */ - short m_r_rwset; /* rewriting set for recipient addresses */ + short m_sh_rwset; /* rewrite set: sender header addresses */ + short m_se_rwset; /* rewrite set: sender envelope addresses */ + short m_rh_rwset; /* rewrite set: recipient header addresses */ + short m_re_rwset; /* rewrite set: recipient envelope addresses */ char *m_eol; /* end of line string */ long m_maxsize; /* size limit on message to this mailer */ + int m_linelimit; /* max # characters per line */ + char *m_execdir; /* directory to chdir to before execv */ }; typedef struct mailer MAILER; /* bits for m_flags */ +# define M_ESMTP 'a' /* run Extended SMTP protocol */ +# define M_BLANKEND 'b' /* ensure blank line at end of message */ +# define M_NOCOMMENT 'c' /* don't include comment part of address */ # define M_CANONICAL 'C' /* make addresses canonical "u@dom" */ + /* 'D' /* CF: include Date: */ # define M_EXPENSIVE 'e' /* it costs to use this mailer.... */ # define M_ESCFROM 'E' /* escape From lines to >From */ # define M_FOPT 'f' /* mailer takes picky -f flag */ + /* 'F' /* CF: include From: or Resent-From: */ +# define M_NO_NULL_FROM 'g' /* sender of errors should be $g */ # define M_HST_UPPER 'h' /* preserve host case distinction */ + /* 'H' /* UIUC: MAIL11V3: preview headers */ # define M_INTERNAL 'I' /* SMTP to another sendmail site */ -# define M_LOCAL 'l' /* delivery is to this host */ +# define M_LOCALMAILER 'l' /* delivery is to this host */ # define M_LIMITS 'L' /* must enforce SMTP line limits */ # define M_MUSER 'm' /* can handle multiple users at once */ + /* 'M' /* CF: include Message-Id: */ # define M_NHDR 'n' /* don't insert From line */ + /* 'N' /* UIUC: MAIL11V3: DATA returns multi-status */ # define M_FROMPATH 'p' /* use reverse-path in MAIL FROM: */ + /* 'P' /* CF: include Return-Path: */ # define M_ROPT 'r' /* mailer takes picky -r flag */ # define M_SECURE_PORT 'R' /* try to send on a reserved TCP port */ # define M_STRIPQ 's' /* strip quote chars from user/host */ # define M_RESTR 'S' /* must be daemon to execute */ # define M_USR_UPPER 'u' /* preserve user case distinction */ # define M_UGLYUUCP 'U' /* this wants an ugly UUCP from line */ + /* 'V' /* UIUC: !-relativize all addresses */ + /* 'x' /* CF: include Full-Name: */ # define M_XDOT 'X' /* use hidden-dot algorithm */ +# define M_7BITS '7' /* use 7-bit path */ EXTERN MAILER *Mailer[MAXMAILERS+1]; EXTERN MAILER *LocalMailer; /* ptr to local mailer */ EXTERN MAILER *ProgMailer; /* ptr to program mailer */ +EXTERN MAILER *FileMailer; /* ptr to *file* mailer */ +EXTERN MAILER *InclMailer; /* ptr to *include* mailer */ /* ** Header structure. ** This structure is used internally to store header items. @@ -218,6 +258,8 @@ extern struct hdrinfo HdrInfo[]; # define H_TRACE 00200 /* this field contains trace information */ # define H_FROM 00400 /* this is a from-type field */ # define H_VALID 01000 /* this field has a validated value */ +# define H_RECEIPTTO 02000 /* this field has return receipt info */ +# define H_ERRORSTO 04000 /* this field has error address info */ /* ** Envelope structure. ** This structure defines the message itself. There is usually @@ -227,7 +269,9 @@ extern struct hdrinfo HdrInfo[]; ** will have their own envelope. */ -struct envelope +# define ENVELOPE struct envelope + +ENVELOPE { HDR *e_header; /* head of header list */ long e_msgpriority; /* adjusted priority of this message */ @@ -235,6 +279,7 @@ struct envelope char *e_to; /* the target person */ char *e_receiptto; /* return receipt address */ ADDRESS e_from; /* the person it is from */ + char *e_sender; /* e_from.q_paddr w comments stripped */ char **e_fromdomain; /* the domain part of the sender */ ADDRESS *e_sendqueue; /* list of message recipients */ ADDRESS *e_errorqueue; /* the queue for error responses */ @@ -243,20 +288,27 @@ struct envelope short e_class; /* msg class (priority, junk, etc.) */ short e_flags; /* flags, see below */ short e_hopcount; /* number of times processed */ - int (*e_puthdr)(); /* function to put header of message */ - int (*e_putbody)(); /* function to put body of message */ + short e_nsent; /* number of sends since checkpoint */ + short e_sendmode; /* message send mode */ + short e_errormode; /* error return mode */ + int (*e_puthdr)__P((FILE *, MAILER *, ENVELOPE *)); + /* function to put header of message */ + int (*e_putbody)__P((FILE *, MAILER *, ENVELOPE *, char *)); + /* function to put body of message */ struct envelope *e_parent; /* the message this one encloses */ struct envelope *e_sibling; /* the next envelope of interest */ + char *e_bodytype; /* type of message body */ char *e_df; /* location of temp file */ FILE *e_dfp; /* temporary file */ char *e_id; /* code for this entry in queue */ FILE *e_xfp; /* transcript file */ + FILE *e_lockfp; /* the lock file for this message */ char *e_message; /* error message */ + char *e_statmsg; /* stat msg (changes per delivery) */ + char *e_msgboundary; /* MIME-style message part boundary */ char *e_macro[128]; /* macro definitions */ }; -typedef struct envelope ENVELOPE; - /* values for e_flags */ #define EF_OLDSTYLE 000001 /* use spaces (not commas) in hdrs */ #define EF_INQUEUE 000002 /* this message is fully queued */ @@ -267,6 +319,9 @@ typedef struct envelope ENVELOPE; #define EF_KEEPQUEUE 000100 /* keep queue files always */ #define EF_RESPONSE 000200 /* this is an error or return receipt */ #define EF_RESENT 000400 /* this message is being forwarded */ +#define EF_VRFYONLY 001000 /* verify only (don't expand aliases) */ +#define EF_WARNING 002000 /* warning message has been sent */ +#define EF_QUEUERUN 004000 /* this envelope is from queue */ EXTERN ENVELOPE *CurEnv; /* envelope currently being processed */ /* @@ -323,55 +378,178 @@ EXTERN struct rewrite *RewriteRules[MAXRWSETS]; */ /* left hand side items */ -# define MATCHZANY '\020' /* match zero or more tokens */ -# define MATCHANY '\021' /* match one or more tokens */ -# define MATCHONE '\022' /* match exactly one token */ -# define MATCHCLASS '\023' /* match one token in a class */ -# define MATCHNCLASS '\024' /* match anything not in class */ -# define MATCHREPL '\025' /* replacement on RHS for above */ +# define MATCHZANY 0220 /* match zero or more tokens */ +# define MATCHANY 0221 /* match one or more tokens */ +# define MATCHONE 0222 /* match exactly one token */ +# define MATCHCLASS 0223 /* match one token in a class */ +# define MATCHNCLASS 0224 /* match anything not in class */ +# define MATCHREPL 0225 /* replacement on RHS for above */ /* right hand side items */ -# define CANONNET '\026' /* canonical net, next token */ -# define CANONHOST '\027' /* canonical host, next token */ -# define CANONUSER '\030' /* canonical user, next N tokens */ -# define CALLSUBR '\031' /* call another rewriting set */ +# define CANONNET 0226 /* canonical net, next token */ +# define CANONHOST 0227 /* canonical host, next token */ +# define CANONUSER 0230 /* canonical user, next N tokens */ +# define CALLSUBR 0231 /* call another rewriting set */ /* conditionals in macros */ -# define CONDIF '\032' /* conditional if-then */ -# define CONDELSE '\033' /* conditional else */ -# define CONDFI '\034' /* conditional fi */ +# define CONDIF 0232 /* conditional if-then */ +# define CONDELSE 0233 /* conditional else */ +# define CONDFI 0234 /* conditional fi */ /* bracket characters for host name lookup */ -# define HOSTBEGIN '\035' /* hostname lookup begin */ -# define HOSTEND '\036' /* hostname lookup end */ +# define HOSTBEGIN 0235 /* hostname lookup begin */ +# define HOSTEND 0236 /* hostname lookup end */ + +/* bracket characters for generalized lookup */ +# define LOOKUPBEGIN 0205 /* generalized lookup begin */ +# define LOOKUPEND 0206 /* generalized lookup end */ + +/* macro substitution character */ +# define MACROEXPAND 0201 /* macro expansion */ +# define MACRODEXPAND 0202 /* deferred macro expansion */ -/* \001 is also reserved as the macro expansion character */ +/* to make the code clearer */ +# define MATCHZERO CANONHOST + +/* external <==> internal mapping table */ +struct metamac +{ + char metaname; /* external code (after $) */ + char metaval; /* internal code (as above) */ +}; /* -** Information about hosts that we have looked up recently. +** Information about currently open connections to mailers, or to +** hosts that we have looked up recently. +*/ + +# define MCI struct mailer_con_info + +MCI +{ + short mci_flags; /* flag bits, see below */ + short mci_errno; /* error number on last connection */ + short mci_herrno; /* h_errno from last DNS lookup */ + short mci_exitstat; /* exit status from last connection */ + short mci_state; /* SMTP state */ + long mci_maxsize; /* max size this server will accept */ + FILE *mci_in; /* input side of connection */ + FILE *mci_out; /* output side of connection */ + int mci_pid; /* process id of subordinate proc */ + char *mci_phase; /* SMTP phase string */ + struct mailer *mci_mailer; /* ptr to the mailer for this conn */ + char *mci_host; /* host name */ + time_t mci_lastuse; /* last usage time */ +}; + + +/* flag bits */ +#define MCIF_VALID 000001 /* this entry is valid */ +#define MCIF_TEMP 000002 /* don't cache this connection */ +#define MCIF_CACHED 000004 /* currently in open cache */ +#define MCIF_ESMTP 000010 /* this host speaks ESMTP */ +#define MCIF_EXPN 000020 /* EXPN command supported */ +#define MCIF_SIZE 000040 /* SIZE option supported */ +#define MCIF_8BITMIME 000100 /* BODY=8BITMIME supported */ + +/* states */ +#define MCIS_CLOSED 0 /* no traffic on this connection */ +#define MCIS_OPENING 1 /* sending initial protocol */ +#define MCIS_OPEN 2 /* open, initial protocol sent */ +#define MCIS_ACTIVE 3 /* message being sent */ +#define MCIS_QUITING 4 /* running quit protocol */ +#define MCIS_SSD 5 /* service shutting down */ +#define MCIS_ERROR 6 /* I/O error on connection */ + /* +** Name canonification short circuit. ** -** This stuff is 4.2/3bsd specific. +** If the name server for a host is down, the process of trying to +** canonify the name can hang. This is similar to (but alas, not +** identical to) looking up the name for delivery. This stab type +** caches the result of the name server lookup so we don't hang +** multiple times. */ -# ifdef DAEMON -# ifdef VMUNIX +#define NAMECANON struct _namecanon + +NAMECANON +{ + short nc_errno; /* cached errno */ + short nc_herrno; /* cached h_errno */ + short nc_stat; /* cached exit status code */ + short nc_flags; /* flag bits */ + char *nc_cname; /* the canonical name */ +}; + +/* values for nc_flags */ +#define NCF_VALID 0x0001 /* entry valid */ + /* +** Mapping functions +** +** These allow arbitrary mappings in the config file. The idea +** (albeit not the implementation) comes from IDA sendmail. +*/ + +# define MAPCLASS struct _mapclass +# define MAP struct _map + -# define HOSTINFO struct hostinfo +/* +** An actual map. +*/ -HOSTINFO +MAP { - char *ho_name; /* name of this host */ - struct in_addr ho_inaddr; /* internet address */ - short ho_flags; /* flag bits, see below */ - short ho_errno; /* error number on last connection */ - short ho_exitstat; /* exit status from last connection */ + MAPCLASS *map_class; /* the class of this map */ + char *map_mname; /* name of this map */ + int map_mflags; /* flags, see below */ + char *map_file; /* the (nominal) filename */ + void *map_db1; /* the open database ptr */ + void *map_db2; /* an "extra" database pointer */ + char *map_app; /* to append to successful matches */ + char *map_domain; /* the (nominal) NIS domain */ + char *map_rebuild; /* program to run to do auto-rebuild */ }; +/* bit values for map_flags */ +# define MF_VALID 0x0001 /* this entry is valid */ +# define MF_INCLNULL 0x0002 /* include null byte in key */ +# define MF_OPTIONAL 0x0004 /* don't complain if map not found */ +# define MF_NOFOLDCASE 0x0008 /* don't fold case in keys */ +# define MF_MATCHONLY 0x0010 /* don't use the map value */ +# define MF_OPEN 0x0020 /* this entry is open */ +# define MF_WRITABLE 0x0040 /* open for writing */ +# define MF_ALIAS 0x0080 /* this is an alias file */ +# define MF_TRY0NULL 0x0100 /* try with no null byte */ +# define MF_TRY1NULL 0x0200 /* try with the null byte */ +# define MF_IMPL_HASH 0x1000 /* implicit: underlying hash database */ +# define MF_IMPL_NDBM 0x2000 /* implicit: underlying NDBM database */ -/* flag bits */ -#define HOF_VALID 00001 /* this entry is valid */ -# endif VMUNIX -# endif DAEMON +/* +** The class of a map -- essentially the functions to call +*/ + +MAPCLASS +{ + char *map_cname; /* name of this map class */ + char *map_ext; /* extension for database file */ + short map_cflags; /* flag bits, see below */ + bool (*map_parse)__P((MAP *, char *)); + /* argument parsing function */ + char *(*map_lookup)__P((MAP *, char *, char **, int *)); + /* lookup function */ + void (*map_store)__P((MAP *, char *, char *)); + /* store function */ + bool (*map_open)__P((MAP *, int)); + /* open function */ + void (*map_close)__P((MAP *)); + /* close function */ +}; + +/* bit values for map_cflags */ +#define MCF_ALIASOK 0x0001 /* can be used for aliases */ +#define MCF_ALIASONLY 0x0002 /* usable only for aliases */ +#define MCF_REBUILDABLE 0x0004 /* can rebuild alias files */ /* ** Symbol table definitions */ @@ -387,9 +565,11 @@ struct symtab ADDRESS *sv_addr; /* pointer to address header */ MAILER *sv_mailer; /* pointer to mailer */ char *sv_alias; /* alias */ -# ifdef HOSTINFO - HOSTINFO sv_host; /* host information */ -# endif HOSTINFO + MAPCLASS sv_mapclass; /* mapping function class */ + MAP sv_map; /* mapping function */ + char *sv_hostsig; /* host signature */ + MCI sv_mci; /* mailer connection info */ + NAMECANON sv_namecanon; /* canonical name cache */ } s_value; }; @@ -401,15 +581,24 @@ typedef struct symtab STAB; # define ST_ADDRESS 2 /* an address in parsed format */ # define ST_MAILER 3 /* a mailer header */ # define ST_ALIAS 4 /* an alias */ -# define ST_HOST 5 /* host information */ +# define ST_MAPCLASS 5 /* mapping function class */ +# define ST_MAP 6 /* mapping function */ +# define ST_HOSTSIG 7 /* host signature */ +# define ST_NAMECANON 8 /* cached canonical name */ +# define ST_MCI 16 /* mailer connection info (offset) */ # define s_class s_value.sv_class # define s_address s_value.sv_addr # define s_mailer s_value.sv_mailer # define s_alias s_value.sv_alias -# define s_host s_value.sv_host +# define s_mci s_value.sv_mci +# define s_mapclass s_value.sv_mapclass +# define s_hostsig s_value.sv_hostsig +# define s_map s_value.sv_map +# define s_namecanon s_value.sv_namecanon -extern STAB *stab(); +extern STAB *stab __P((char *, int, int)); +extern void stabapply __P((void (*)(STAB *, int), int)); /* opcodes to stab */ # define ST_FIND 0 /* find entry */ @@ -426,7 +615,8 @@ extern STAB *stab(); struct event { time_t ev_time; /* time of the function call */ - int (*ev_func)(); /* function to call */ + int (*ev_func)__P((int)); + /* function to call */ int ev_arg; /* argument to ev_func */ int ev_pid; /* pid that set this event */ struct event *ev_link; /* link to next item */ @@ -454,7 +644,6 @@ EXTERN EVENT *EventQueue; /* head of event queue */ EXTERN char OpMode; /* operation mode, see below */ #define MD_DELIVER 'm' /* be a mail sender */ -#define MD_ARPAFTP 'a' /* old-style arpanet protocols */ #define MD_SMTP 's' /* run SMTP on standard input */ #define MD_DAEMON 'd' /* run as a daemon */ #define MD_VERIFY 'v' /* verify: don't collect or deliver */ @@ -464,8 +653,7 @@ EXTERN char OpMode; /* operation mode, see below */ #define MD_FREEZE 'z' /* freeze the configuration file */ -EXTERN char SendMode; /* send mode, see below */ - +/* values for e_sendmode -- send modes */ #define SM_DELIVER 'i' /* interactive delivery */ #define SM_QUICKD 'j' /* deliver w/o queueing */ #define SM_FORK 'b' /* deliver in background */ @@ -476,25 +664,83 @@ EXTERN char SendMode; /* send mode, see below */ #define SM_DEFAULT '\0' /* unspecified, use SendMode */ -EXTERN char ErrorMode; /* error mode, see below */ - +/* values for e_errormode -- error handling modes */ #define EM_PRINT 'p' /* print errors */ #define EM_MAIL 'm' /* mail back errors */ #define EM_WRITE 'w' /* write back errors */ #define EM_BERKNET 'e' /* special berknet processing */ #define EM_QUIET 'q' /* don't print messages (stat only) */ + /* +** Additional definitions +*/ -/* offset used to issure that the error messages for name server error - * codes are unique. - */ + +/* Offset used to ensure that name server error * codes are unique */ #define MAX_ERRNO 100 + + +/* +** Privacy flags +** These are bit values for the PrivacyFlags word. +*/ + +#define PRIV_PUBLIC 0 /* what have I got to hide? */ +#define PRIV_NEEDMAILHELO 00001 /* insist on HELO for MAIL, at least */ +#define PRIV_NEEDEXPNHELO 00002 /* insist on HELO for EXPN */ +#define PRIV_NEEDVRFYHELO 00004 /* insist on HELO for VRFY */ +#define PRIV_NOEXPN 00010 /* disallow EXPN command entirely */ +#define PRIV_NOVRFY 00020 /* disallow VRFY command entirely */ +#define PRIV_AUTHWARNINGS 00040 /* flag possible authorization probs */ +#define PRIV_RESTRMAILQ 01000 /* restrict mailq command */ +#define PRIV_GOAWAY 00777 /* don't give no info, anyway, anyhow */ + +/* struct defining such things */ +struct prival +{ + char *pv_name; /* name of privacy flag */ + int pv_flag; /* numeric level */ +}; + + +/* +** Flags passed to remotename +*/ + +#define RF_SENDERADDR 0001 /* this is a sender address */ +#define RF_HEADERADDR 0002 /* this is a header address */ +#define RF_CANONICAL 0004 /* strip comment information */ +#define RF_ADDDOMAIN 0010 /* OK to do domain extension */ + + +/* +** Regular UNIX sockaddrs are too small to handle ISO addresses, so +** we are forced to declare a supertype here. +*/ + +union bigsockaddr +{ + struct sockaddr sa; /* general version */ +#ifdef NETINET + struct sockaddr_in sin; /* INET family */ +#endif +#ifdef NETISO + struct sockaddr_iso siso; /* ISO family */ +#endif +#ifdef NETNS + struct sockaddr_ns sns; /* XNS family */ +#endif +#ifdef NETX25 + struct sockaddr_x25 sx25; /* X.25 family */ +#endif +}; + +#define SOCKADDR union bigsockaddr + /* ** Global variables. */ EXTERN bool FromFlag; /* if set, "From" person is explicit */ -EXTERN bool NoAlias; /* if set, don't do any aliasing */ -EXTERN bool ForceMail; /* if set, mail even if already got a copy */ EXTERN bool MeToo; /* send to the sender also */ EXTERN bool IgnrDot; /* don't let dot end messages */ EXTERN bool SaveFrom; /* save leading "From" lines */ @@ -502,64 +748,106 @@ EXTERN bool Verbose; /* set if blow-by-blow desired */ EXTERN bool GrabTo; /* if set, get recipients from msg */ EXTERN bool NoReturn; /* don't return letter to sender */ EXTERN bool SuprErrs; /* set if we are suppressing errors */ -EXTERN bool QueueRun; /* currently running message from the queue */ EXTERN bool HoldErrs; /* only output errors to transcript */ EXTERN bool NoConnect; /* don't connect to non-local mailers */ EXTERN bool SuperSafe; /* be extra careful, even if expensive */ EXTERN bool ForkQueueRuns; /* fork for each job when running the queue */ EXTERN bool AutoRebuild; /* auto-rebuild the alias database as needed */ EXTERN bool CheckAliases; /* parse addresses during newaliases */ +EXTERN bool NoAlias; /* suppress aliasing */ EXTERN bool UseNameServer; /* use internet domain name server */ +EXTERN bool SevenBit; /* force 7-bit data */ EXTERN int SafeAlias; /* minutes to wait until @:@ in alias file */ -EXTERN time_t TimeOut; /* time until timeout */ EXTERN FILE *InChannel; /* input connection */ EXTERN FILE *OutChannel; /* output connection */ -EXTERN int RealUid; /* when Daemon, real uid of caller */ -EXTERN int RealGid; /* when Daemon, real gid of caller */ -EXTERN int DefUid; /* default uid to run as */ +EXTERN uid_t RealUid; /* when Daemon, real uid of caller */ +EXTERN gid_t RealGid; /* when Daemon, real gid of caller */ +EXTERN uid_t DefUid; /* default uid to run as */ +EXTERN gid_t DefGid; /* default gid to run as */ EXTERN char *DefUser; /* default user to run as (from DefUid) */ -EXTERN int DefGid; /* default gid to run as */ EXTERN int OldUmask; /* umask when sendmail starts up */ EXTERN int Errors; /* set if errors (local to single pass) */ EXTERN int ExitStat; /* exit status code */ EXTERN int AliasLevel; /* depth of aliasing */ -EXTERN int MotherPid; /* proc id of parent process */ EXTERN int LineNumber; /* line number in current input */ -EXTERN time_t ReadTimeout; /* timeout on reads */ EXTERN int LogLevel; /* level of logging to perform */ EXTERN int FileMode; /* mode on files */ EXTERN int QueueLA; /* load average starting forced queueing */ EXTERN int RefuseLA; /* load average refusing connections are */ -EXTERN int QueueFactor; /* slope of queue function */ +EXTERN int CurrentLA; /* current load average */ +EXTERN long QueueFactor; /* slope of queue function */ EXTERN time_t QueueIntvl; /* intervals between running the queue */ -EXTERN char *AliasFile; /* location of alias file */ EXTERN char *HelpFile; /* location of SMTP help file */ +EXTERN char *ErrMsgFile; /* file to prepend to all error messages */ EXTERN char *StatFile; /* location of statistics summary */ EXTERN char *QueueDir; /* location of queue directory */ EXTERN char *FileName; /* name to print on error messages */ EXTERN char *SmtpPhase; /* current phase in SMTP processing */ EXTERN char *MyHostName; /* name of this host for SMTP messages */ EXTERN char *RealHostName; /* name of host we are talking to */ -EXTERN struct sockaddr_in RealHostAddr;/* address of host we are talking to */ +EXTERN SOCKADDR RealHostAddr; /* address of host we are talking to */ EXTERN char *CurHostName; /* current host we are dealing with */ EXTERN jmp_buf TopFrame; /* branch-to-top-of-loop-on-error frame */ EXTERN bool QuickAbort; /* .... but only if we want a quick abort */ +EXTERN bool LogUsrErrs; /* syslog user errors (e.g., SMTP RCPT cmd) */ +EXTERN bool SendMIMEErrors; /* send error messages in MIME format */ +EXTERN bool MatchGecos; /* look for user names in gecos field */ +EXTERN bool UseErrorsTo; /* use Errors-To: header (back compat) */ +EXTERN char SpaceSub; /* substitution for <lwsp> */ +EXTERN int PrivacyFlags; /* privacy flags */ extern char *ConfFile; /* location of configuration file [conf.c] */ extern char *FreezeFile; /* location of frozen memory image [conf.c] */ -extern char Arpa_Info[]; /* the reply code for Arpanet info [conf.c] */ +extern char *PidFile; /* location of proc id file [conf.c] */ extern ADDRESS NullAddress; /* a null (template) address [main.c] */ -EXTERN char SpaceSub; /* substitution for <lwsp> */ -EXTERN int WkClassFact; /* multiplier for message class -> priority */ -EXTERN int WkRecipFact; /* multiplier for # of recipients -> priority */ -EXTERN int WkTimeFact; /* priority offset each time this job is run */ -EXTERN int CheckPointLimit; /* deliveries before checkpointing */ -EXTERN int Nmx; /* number of MX RRs */ +EXTERN long WkClassFact; /* multiplier for message class -> priority */ +EXTERN long WkRecipFact; /* multiplier for # of recipients -> priority */ +EXTERN long WkTimeFact; /* priority offset each time this job is run */ +EXTERN char *UdbSpec; /* user database source spec */ +EXTERN int MaxHopCount; /* max # of hops until bounce */ +EXTERN int ConfigLevel; /* config file level */ +EXTERN char *TimeZoneSpec; /* override time zone specification */ +EXTERN char *ForwardPath; /* path to search for .forward files */ +EXTERN long MinBlocksFree; /* min # of blocks free on queue fs */ +EXTERN char *FallBackMX; /* fall back MX host */ +EXTERN long MaxMessageSize; /* advertised max size we will accept */ EXTERN char *PostMasterCopy; /* address to get errs cc's */ -EXTERN char *MxHosts[MAXMXHOSTS+1]; /* for MX RRs */ -EXTERN char *TrustedUsers[MAXTRUST+1]; /* list of trusted users */ -EXTERN char *UserEnviron[MAXUSERENVIRON+1]; /* saved user environment */ EXTERN int CheckpointInterval; /* queue file checkpoint interval */ - /* +EXTERN bool DontPruneRoutes; /* don't prune source routes */ +EXTERN int MaxMciCache; /* maximum entries in MCI cache */ +EXTERN time_t MciCacheTimeout; /* maximum idle time on connections */ +EXTERN char *QueueLimitRecipient; /* limit queue runs to this recipient */ +EXTERN char *QueueLimitSender; /* limit queue runs to this sender */ +EXTERN char *QueueLimitId; /* limit queue runs to this id */ +EXTERN FILE *TrafficLogFile; /* file in which to log all traffic */ + + +/* +** Timeouts +** +** Indicated values are the MINIMUM per RFC 1123 section 5.3.2. +*/ + +EXTERN struct +{ + time_t to_initial; /* initial greeting timeout [5m] */ + time_t to_mail; /* MAIL command [5m] */ + time_t to_rcpt; /* RCPT command [5m] */ + time_t to_datainit; /* DATA initiation [2m] */ + time_t to_datablock; /* DATA block [3m] */ + time_t to_datafinal; /* DATA completion [10m] */ + time_t to_nextcommand; /* next command [5m] */ + /* following timeouts are not mentioned in RFC 1123 */ + time_t to_rset; /* RSET command */ + time_t to_helo; /* HELO command */ + time_t to_quit; /* QUIT command */ + time_t to_miscshort; /* misc short commands (NOOP, VERB, etc) */ + /* following are per message */ + time_t to_q_return; /* queue return timeout */ + time_t to_q_warning; /* queue warning timeout */ +} TimeOuts; + + +/* ** Trace information */ @@ -571,7 +859,6 @@ EXTERN u_char tTdvect[100]; ** Miscellaneous information. */ -# include <sysexits.h> /* @@ -594,11 +881,49 @@ EXTERN u_char tTdvect[100]; ** Declarations of useful functions */ -extern ADDRESS *parseaddr(); -extern char *xalloc(); -extern bool sameaddr(); -extern FILE *dfopen(); -extern EVENT *setevent(); -extern char *sfgets(); -extern char *queuename(); -extern time_t curtime(); +extern ADDRESS *parseaddr __P((char *, ADDRESS *, int, int, char **, ENVELOPE *)); +extern char *xalloc __P((int)); +extern bool sameaddr __P((ADDRESS *, ADDRESS *)); +extern FILE *dfopen __P((char *, int, int)); +extern EVENT *setevent __P((time_t, int(*)(), int)); +extern char *sfgets __P((char *, int, FILE *, time_t, char *)); +extern char *queuename __P((ENVELOPE *, int)); +extern time_t curtime __P(()); +extern bool transienterror __P((int)); +extern const char *errstring __P((int)); +extern void expand __P((char *, char *, char *, ENVELOPE *)); +extern void define __P((int, char *, ENVELOPE *)); +extern char *macvalue __P((int, ENVELOPE *)); +extern char **prescan __P((char *, int, char[], char **)); +extern char *fgetfolded __P((char *, int, FILE *)); +extern ADDRESS *recipient __P((ADDRESS *, ADDRESS **, ENVELOPE *)); +extern ENVELOPE *newenvelope __P((ENVELOPE *, ENVELOPE *)); +extern void dropenvelope __P((ENVELOPE *)); +extern void clearenvelope __P((ENVELOPE *, int)); +extern char *username __P(()); +extern MCI *mci_get __P((char *, MAILER *)); +extern char *pintvl __P((time_t, int)); +extern char *map_rewrite __P((MAP *, char *, int, char **)); +extern ADDRESS *getctladdr __P((ADDRESS *)); +extern char *anynet_ntoa __P((SOCKADDR *)); +extern char *remotename __P((char *, MAILER *, int, int *, ENVELOPE *)); +extern bool shouldqueue __P((long, time_t)); +extern bool lockfile __P((int, char *, int)); +extern char *hostsignature __P((MAILER *, char *, ENVELOPE *)); +extern void openxscript __P((ENVELOPE *)); +extern void closexscript __P((ENVELOPE *)); + +/* ellipsis is a different case though */ +#ifdef __STDC__ +extern void auth_warning(ENVELOPE *, const char *, ...); +extern void syserr(const char *, ...); +extern void usrerr(const char *, ...); +extern void message(const char *, ...); +extern void nmessage(const char *, ...); +#else +extern void auth_warning(); +extern void syserr(); +extern void usrerr(); +extern void message(); +extern void nmessage(); +#endif diff --git a/usr.sbin/sendmail/src/sendmail.hf b/usr.sbin/sendmail/src/sendmail.hf index 293a713c1c..ff208bad7d 100644 --- a/usr.sbin/sendmail/src/sendmail.hf +++ b/usr.sbin/sendmail/src/sendmail.hf @@ -1,21 +1,25 @@ cpyr cpyr Copyright (c) 1983 Eric P. Allman -cpyr Copyright (c) 1988 The Regents of the University of California. -cpyr All rights reserved. +cpyr Copyright (c) 1988, 1993 +cpyr The Regents of the University of California. All rights reserved. cpyr -cpyr @(#)sendmail.hf 4.5 (Berkeley) 4/23/91 +cpyr @(#)sendmail.hf 8.1 (Berkeley) 6/7/93 cpyr smtp Commands: -smtp HELO MAIL RCPT DATA RSET -smtp NOOP QUIT HELP VRFY EXPN +smtp HELO EHLO MAIL RCPT DATA +smtp RSET NOOP QUIT HELP VRFY +smtp EXPN VERB smtp For more info use "HELP <topic>". -smtp To report bugs in the implementation contact sendmail@okeeffe.Berkeley.EDU -smtp For local information contact postmaster at this site. +smtp To report bugs in the implementation send email to +smtp sendmail@CS.Berkeley.EDU. +smtp For local information send email to Postmaster at your site. help HELP [ <topic> ] help The HELP command gives help info. helo HELO <hostname> helo Introduce yourself. I am a boor, so I really don't helo care if you do. +ehlo EHLO <hostname> +ehlo Introduce yourself, and request extended SMTP mode. mail MAIL FROM: <sender> mail Specifies the sender. rcpt RCPT TO: <recipient> @@ -27,11 +31,16 @@ rset RSET rset Resets the system. quit QUIT quit Exit sendmail (SMTP). +verb VERB +verb Go into verbose mode. This sends 0xy responses that are +verb are not RFC821 standard (but should be) They are recognized +verb by humans and other sendmail implementations. vrfy VRFY <recipient> -vrfy Not implemented to protocol. Gives some sexy -vrfy information. +vrfy Verify an address. If you want to see what it aliases +vrfy to, use EXPN instead. expn EXPN <recipient> -expn Same as VRFY in this implementation. +expn Expand an address. If the address indicates a mailing +expn list, return the contents of that list. noop NOOP noop Do nothing. send SEND FROM: <sender> diff --git a/usr.sbin/sendmail/src/srvrsmtp.c b/usr.sbin/sendmail/src/srvrsmtp.c index b33a1ef334..39dabe0a49 100644 --- a/usr.sbin/sendmail/src/srvrsmtp.c +++ b/usr.sbin/sendmail/src/srvrsmtp.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,9 +36,9 @@ #ifndef lint #ifdef SMTP -static char sccsid[] = "@(#)srvrsmtp.c 5.31 (Berkeley) 5/10/91 (with SMTP)"; +static char sccsid[] = "@(#)srvrsmtp.c 8.3 (Berkeley) 7/13/93 (with SMTP)"; #else -static char sccsid[] = "@(#)srvrsmtp.c 5.31 (Berkeley) 5/10/91 (without SMTP)"; +static char sccsid[] = "@(#)srvrsmtp.c 8.3 (Berkeley) 7/13/93 (without SMTP)"; #endif #endif /* not lint */ @@ -74,15 +74,18 @@ struct cmd # define CMDDATA 3 /* data -- send message text */ # define CMDRSET 4 /* rset -- reset state */ # define CMDVRFY 5 /* vrfy -- verify address */ -# define CMDHELP 6 /* help -- give usage info */ +# define CMDEXPN 6 /* expn -- expand address */ # define CMDNOOP 7 /* noop -- do nothing */ # define CMDQUIT 8 /* quit -- close connection and die */ # define CMDHELO 9 /* helo -- be polite */ -# define CMDONEX 10 /* onex -- sending one transaction only */ -# define CMDVERB 11 /* verb -- go into verbose mode */ +# define CMDHELP 10 /* help -- give usage info */ +# define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ +/* non-standard commands */ +# define CMDONEX 16 /* onex -- sending one transaction only */ +# define CMDVERB 17 /* verb -- go into verbose mode */ /* debugging-only commands, only enabled if SMTPDEBUG is defined */ -# define CMDDBGQSHOW 12 /* showq -- show send queue */ -# define CMDDBGDEBUG 13 /* debug -- set debug mode */ +# define CMDDBGQSHOW 24 /* showq -- show send queue */ +# define CMDDBGDEBUG 25 /* debug -- set debug mode */ static struct cmd CmdTab[] = { @@ -91,11 +94,12 @@ static struct cmd CmdTab[] = "data", CMDDATA, "rset", CMDRSET, "vrfy", CMDVRFY, - "expn", CMDVRFY, + "expn", CMDEXPN, "help", CMDHELP, "noop", CMDNOOP, "quit", CMDQUIT, "helo", CMDHELO, + "ehlo", CMDEHLO, "verb", CMDVERB, "onex", CMDONEX, /* @@ -112,68 +116,83 @@ bool OneXact = FALSE; /* one xaction only this run */ #define EX_QUIT 22 /* special code for QUIT command */ -smtp() +smtp(e) + register ENVELOPE *e; { register char *p; register struct cmd *c; char *cmd; static char *skipword(); - bool hasmail; /* mail command received */ auto ADDRESS *vrfyqueue; ADDRESS *a; - char *sendinghost; + bool gotmail; /* mail command received */ + bool gothello; /* helo command received */ + bool vrfy; /* set if this is a vrfy command */ + char *protocol; /* sending protocol */ + char *sendinghost; /* sending hostname */ + long msize; /* approximate maximum message size */ + auto char *delimptr; + char *id; + int nrcpts; /* number of RCPT commands */ + bool doublequeue; char inp[MAXLINE]; - char cmdbuf[100]; + char cmdbuf[MAXLINE]; extern char Version[]; - extern char *macvalue(); - extern ADDRESS *recipient(); extern ENVELOPE BlankEnvelope; - extern ENVELOPE *newenvelope(); - hasmail = FALSE; - if (OutChannel != stdout) + if (fileno(OutChannel) != fileno(stdout)) { /* arrange for debugging output to go to remote host */ - (void) close(1); - (void) dup(fileno(OutChannel)); + (void) dup2(fileno(OutChannel), fileno(stdout)); } - settime(); - if (RealHostName != NULL) - { - CurHostName = RealHostName; - setproctitle("srvrsmtp %s", CurHostName); - } - else - { - /* this must be us!! */ - CurHostName = MyHostName; - } - expand("\001e", inp, &inp[sizeof inp], CurEnv); - message("220", inp); - SmtpPhase = "startup"; - sendinghost = NULL; + settime(e); + CurHostName = RealHostName; + setproctitle("server %s startup", CurHostName); + expand("\201e", inp, &inp[sizeof inp], e); + message("220-%s", inp); + message("220 ESMTP spoken here"); + protocol = NULL; + sendinghost = macvalue('s', e); + gothello = FALSE; + gotmail = FALSE; for (;;) { /* arrange for backout */ if (setjmp(TopFrame) > 0 && InChild) + { + QuickAbort = FALSE; + SuprErrs = TRUE; finis(); + } QuickAbort = FALSE; HoldErrs = FALSE; + LogUsrErrs = FALSE; + e->e_flags &= ~EF_VRFYONLY; /* setup for the read */ - CurEnv->e_to = NULL; + e->e_to = NULL; Errors = 0; (void) fflush(stdout); /* read the input line */ - p = sfgets(inp, sizeof inp, InChannel); + SmtpPhase = "server cmd read"; + setproctitle("server %s cmd read", CurHostName); + p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, + SmtpPhase); /* handle errors */ if (p == NULL) { /* end of file, just die */ - message("421", "%s Lost input channel from %s", + message("421 %s Lost input channel from %s", MyHostName, CurHostName); +#ifdef LOG + if (LogLevel > 1) + syslog(LOG_NOTICE, "lost input channel from %s", + CurHostName); +#endif + if (InChild) + ExitStat = EX_QUIT; finis(); } @@ -181,19 +200,26 @@ smtp() fixcrlf(inp, TRUE); /* echo command to transcript */ - if (CurEnv->e_xfp != NULL) - fprintf(CurEnv->e_xfp, "<<< %s\n", inp); + if (e->e_xfp != NULL) + fprintf(e->e_xfp, "<<< %s\n", inp); + + if (e->e_id == NULL) + setproctitle("%s: %s", CurHostName, inp); + else + setproctitle("%s %s: %s", e->e_id, CurHostName, inp); /* break off command */ - for (p = inp; isspace(*p); p++) + for (p = inp; isascii(*p) && isspace(*p); p++) continue; - cmd = p; - for (cmd = cmdbuf; *p != '\0' && !isspace(*p); ) + cmd = cmdbuf; + while (*p != '\0' && + !(isascii(*p) && isspace(*p)) && + cmd < &cmdbuf[sizeof cmdbuf - 2]) *cmd++ = *p++; *cmd = '\0'; /* throw away leading whitespace */ - while (isspace(*p)) + while (isascii(*p) && isspace(*p)) p++; /* decode command */ @@ -203,131 +229,299 @@ smtp() break; } + /* reset errors */ + errno = 0; + /* process command */ switch (c->cmdcode) { case CMDHELO: /* hello -- introduce yourself */ - SmtpPhase = "HELO"; - setproctitle("%s: %s", CurHostName, inp); - if (!strcasecmp(p, MyHostName)) + case CMDEHLO: /* extended hello */ + if (c->cmdcode == CMDEHLO) { - /* - * didn't know about alias, - * or connected to an echo server - */ - message("553", "%s config error: mail loops back to myself", - MyHostName); - break; + protocol = "ESMTP"; + SmtpPhase = "server EHLO"; } - if (RealHostName != NULL && strcasecmp(p, RealHostName)) + else + { + protocol = "SMTP"; + SmtpPhase = "server HELO"; + } + sendinghost = newstr(p); + if (strcasecmp(p, RealHostName) != 0) { - char hostbuf[MAXNAME]; + auth_warning(e, "Host %s claimed to be %s", + RealHostName, p); + } + p = macvalue('_', e); + if (p == NULL) + p = RealHostName; - (void) sprintf(hostbuf, "%s (%s)", p, RealHostName); - sendinghost = newstr(hostbuf); + gothello = TRUE; + if (c->cmdcode != CMDEHLO) + { + /* print old message and be done with it */ + message("250 %s Hello %s, pleased to meet you", + MyHostName, p); + break; } + + /* print extended message and brag */ + message("250-%s Hello %s, pleased to meet you", + MyHostName, p); + if (!bitset(PRIV_NOEXPN, PrivacyFlags)) + message("250-EXPN"); + if (MaxMessageSize > 0) + message("250-SIZE %ld", MaxMessageSize); else - sendinghost = newstr(p); - message("250", "%s Hello %s, pleased to meet you", - MyHostName, sendinghost); + message("250-SIZE"); + message("250 HELP"); break; case CMDMAIL: /* mail -- designate sender */ - SmtpPhase = "MAIL"; - - /* force a sending host even if no HELO given */ - if (RealHostName != NULL && macvalue('s', CurEnv) == NULL) - sendinghost = RealHostName; + SmtpPhase = "server MAIL"; /* check for validity of this command */ - if (hasmail) + if (!gothello) { - message("503", "Sender already specified"); + /* set sending host to our known value */ + if (sendinghost == NULL) + sendinghost = RealHostName; + + if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) + { + message("503 Polite people say HELO first"); + break; + } + } + if (gotmail) + { + message("503 Sender already specified"); break; } if (InChild) { errno = 0; - syserr("Nested MAIL command"); - exit(0); + syserr("503 Nested MAIL command: MAIL %s", p); + finis(); } /* fork a subprocess to process this command */ - if (runinchild("SMTP-MAIL") > 0) + if (runinchild("SMTP-MAIL", e) > 0) break; - define('s', sendinghost, CurEnv); - define('r', "SMTP", CurEnv); - initsys(); - setproctitle("%s %s: %s", CurEnv->e_id, - CurHostName, inp); + if (!gothello) + { + auth_warning(e, + "Host %s didn't use HELO protocol", + RealHostName); + } + if (protocol == NULL) + protocol = "SMTP"; + define('r', protocol, e); + define('s', sendinghost, e); + initsys(e); + nrcpts = 0; + setproctitle("%s %s: %s", e->e_id, CurHostName, inp); /* child -- go do the processing */ p = skipword(p, "from"); if (p == NULL) break; - setsender(p); - if (Errors == 0) + if (setjmp(TopFrame) > 0) { - message("250", "Sender ok"); - hasmail = TRUE; + /* this failed -- undo work */ + if (InChild) + { + QuickAbort = FALSE; + SuprErrs = TRUE; + e->e_flags &= ~EF_FATALERRS; + finis(); + } + break; } - else if (InChild) - finis(); + QuickAbort = TRUE; + + /* must parse sender first */ + delimptr = NULL; + setsender(p, e, &delimptr, FALSE); + p = delimptr; + if (p != NULL && *p != '\0') + *p++ = '\0'; + + /* now parse ESMTP arguments */ + msize = 0; + for (; p != NULL && *p != '\0'; p++) + { + char *kp; + char *vp; + + /* locate the beginning of the keyword */ + while (isascii(*p) && isspace(*p)) + p++; + if (*p == '\0') + break; + kp = p; + + /* skip to the value portion */ + while (isascii(*p) && isalnum(*p) || *p == '-') + p++; + if (*p == '=') + { + *p++ = '\0'; + vp = p; + + /* skip to the end of the value */ + while (*p != '\0' && *p != ' ' && + !(isascii(*p) && iscntrl(*p)) && + *p != '=') + p++; + } + + if (*p != '\0') + *p++ = '\0'; + + if (tTd(19, 1)) + printf("MAIL: got arg %s=%s\n", kp, + vp == NULL ? "<null>" : vp); + + if (strcasecmp(kp, "size") == 0) + { + if (vp == NULL) + { + usrerr("501 SIZE requires a value"); + /* NOTREACHED */ + } + msize = atol(vp); + } + else if (strcasecmp(kp, "body") == 0) + { + if (vp == NULL) + { + usrerr("501 BODY requires a value"); + /* NOTREACHED */ + } +# ifdef MIME + if (strcasecmp(vp, "8bitmime") == 0) + { + e->e_bodytype = "8BITMIME"; + SevenBit = FALSE; + } + else if (strcasecmp(vp, "7bit") == 0) + { + e->e_bodytype = "7BIT"; + SevenBit = TRUE; + } + else + { + usrerr("501 Unknown BODY type %s", + vp); + } +# endif + } + else + { + usrerr("501 %s parameter unrecognized", kp); + /* NOTREACHED */ + } + } + + if (MaxMessageSize > 0 && msize > MaxMessageSize) + { + usrerr("552 Message size exceeds fixed maximum message size (%ld)", + MaxMessageSize); + /* NOTREACHED */ + } + + if (!enoughspace(msize)) + { + message("452 Insufficient disk space; try again later"); + break; + } + message("250 Sender ok"); + gotmail = TRUE; break; case CMDRCPT: /* rcpt -- designate recipient */ - SmtpPhase = "RCPT"; - setproctitle("%s %s: %s", CurEnv->e_id, - CurHostName, inp); + if (!gotmail) + { + usrerr("503 Need MAIL before RCPT"); + break; + } + SmtpPhase = "server RCPT"; if (setjmp(TopFrame) > 0) { - CurEnv->e_flags &= ~EF_FATALERRS; + e->e_flags &= ~EF_FATALERRS; break; } QuickAbort = TRUE; + LogUsrErrs = TRUE; + + if (e->e_sendmode != SM_DELIVER) + e->e_flags |= EF_VRFYONLY; + p = skipword(p, "to"); if (p == NULL) break; - a = parseaddr(p, (ADDRESS *) NULL, 1, '\0'); + a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e); if (a == NULL) break; a->q_flags |= QPRIMARY; - a = recipient(a, &CurEnv->e_sendqueue); + a = recipient(a, &e->e_sendqueue, e); if (Errors != 0) break; /* no errors during parsing, but might be a duplicate */ - CurEnv->e_to = p; + e->e_to = p; if (!bitset(QBADADDR, a->q_flags)) - message("250", "Recipient ok"); + { + message("250 Recipient ok"); + nrcpts++; + } else { /* punt -- should keep message in ADDRESS.... */ - message("550", "Addressee unknown"); + message("550 Addressee unknown"); } - CurEnv->e_to = NULL; + e->e_to = NULL; break; case CMDDATA: /* data -- text of mail */ - SmtpPhase = "DATA"; - if (!hasmail) + SmtpPhase = "server DATA"; + if (!gotmail) { - message("503", "Need MAIL command"); + message("503 Need MAIL command"); break; } - else if (CurEnv->e_nrcpts <= 0) + else if (e->e_nrcpts <= 0) { - message("503", "Need RCPT (recipient)"); + message("503 Need RCPT (recipient)"); break; } + /* check to see if we need to re-expand aliases */ + /* also reset QBADADDR on already-diagnosted addrs */ + doublequeue = FALSE; + for (a = e->e_sendqueue; a != NULL; a = a->q_next) + { + if (bitset(QVERIFIED, a->q_flags)) + { + /* need to re-expand aliases */ + doublequeue = TRUE; + } + if (bitset(QBADADDR, a->q_flags)) + { + /* make this "go away" */ + a->q_flags |= QDONTSEND; + a->q_flags &= ~QBADADDR; + } + } + /* collect the text of the message */ SmtpPhase = "collect"; - setproctitle("%s %s: %s", CurEnv->e_id, - CurHostName, inp); - collect(TRUE); + collect(TRUE, doublequeue, e); + e->e_flags &= ~EF_FATALERRS; if (Errors != 0) - break; + goto abortmessage; /* ** Arrange to send to everyone. @@ -348,79 +542,131 @@ smtp() */ SmtpPhase = "delivery"; - if (CurEnv->e_nrcpts != 1) + if (nrcpts != 1 && !doublequeue) { HoldErrs = TRUE; - ErrorMode = EM_MAIL; + e->e_errormode = EM_MAIL; } - CurEnv->e_flags &= ~EF_FATALERRS; - CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp); + e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); + id = e->e_id; /* send to all recipients */ - sendall(CurEnv, SM_DEFAULT); - CurEnv->e_to = NULL; + sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); + e->e_to = NULL; /* save statistics */ - markstats(CurEnv, (ADDRESS *) NULL); + markstats(e, (ADDRESS *) NULL); /* issue success if appropriate and reset */ if (Errors == 0 || HoldErrs) - message("250", "Ok"); + message("250 %s Message accepted for delivery", id); + + if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) + { + /* avoid sending back an extra message */ + e->e_flags &= ~EF_FATALERRS; + e->e_flags |= EF_CLRQUEUE; + } else - CurEnv->e_flags &= ~EF_FATALERRS; + { + /* from now on, we have to operate silently */ + HoldErrs = TRUE; + e->e_errormode = EM_MAIL; + /* if we just queued, poke it */ + if (doublequeue && e->e_sendmode != SM_QUEUE) + { + unlockqueue(e); + dowork(id, TRUE, TRUE, e); + e->e_id = NULL; + } + } + + abortmessage: /* if in a child, pop back to our parent */ if (InChild) finis(); /* clean up a bit */ - hasmail = 0; - dropenvelope(CurEnv); - CurEnv = newenvelope(CurEnv); - CurEnv->e_flags = BlankEnvelope.e_flags; + gotmail = FALSE; + dropenvelope(e); + CurEnv = e = newenvelope(e, CurEnv); + e->e_flags = BlankEnvelope.e_flags; break; case CMDRSET: /* rset -- reset state */ - message("250", "Reset state"); + message("250 Reset state"); if (InChild) finis(); + + /* clean up a bit */ + gotmail = FALSE; + dropenvelope(e); + CurEnv = e = newenvelope(e, CurEnv); break; case CMDVRFY: /* vrfy -- verify address */ - if (runinchild("SMTP-VRFY") > 0) + case CMDEXPN: /* expn -- expand address */ + vrfy = c->cmdcode == CMDVRFY; + if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, + PrivacyFlags)) + { + if (vrfy) + message("252 Who's to say?"); + else + message("502 That's none of your business"); break; - setproctitle("%s: %s", CurHostName, inp); + } + else if (!gothello && + bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, + PrivacyFlags)) + { + message("503 I demand that you introduce yourself first"); + break; + } + if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) + break; +#ifdef LOG + if (LogLevel > 5) + syslog(LOG_INFO, "%s: %s", CurHostName, inp); +#endif vrfyqueue = NULL; QuickAbort = TRUE; - sendtolist(p, (ADDRESS *) NULL, &vrfyqueue); + if (vrfy) + e->e_flags |= EF_VRFYONLY; + while (*p != '\0' && isascii(*p) && isspace(*p)) + *p++; + if (*p == '\0') + { + message("501 Argument required"); + Errors++; + } + else + { + (void) sendtolist(p, (ADDRESS *) NULL, + &vrfyqueue, e); + } if (Errors != 0) { if (InChild) finis(); break; } + if (vrfyqueue == NULL) + { + message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); + } while (vrfyqueue != NULL) { register ADDRESS *a = vrfyqueue->q_next; - char *code; while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) a = a->q_next; if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) - { - if (a != NULL) - code = "250-"; - else - code = "250"; - if (vrfyqueue->q_fullname == NULL) - message(code, "<%s>", vrfyqueue->q_paddr); - else - message(code, "%s <%s>", - vrfyqueue->q_fullname, vrfyqueue->q_paddr); - } + printvrfyaddr(vrfyqueue, a == NULL); else if (a == NULL) - message("554", "Self destructive alias loop"); + message("554 Self destructive alias loop"); vrfyqueue = a; } if (InChild) @@ -432,36 +678,46 @@ smtp() break; case CMDNOOP: /* noop -- do nothing */ - message("200", "OK"); + message("200 OK"); break; case CMDQUIT: /* quit -- leave mail */ - message("221", "%s closing connection", MyHostName); + message("221 %s closing connection", MyHostName); + + /* avoid future 050 messages */ + Verbose = FALSE; + if (InChild) ExitStat = EX_QUIT; finis(); case CMDVERB: /* set verbose mode */ + if (bitset(PRIV_NOEXPN, PrivacyFlags)) + { + /* this would give out the same info */ + message("502 Verbose unavailable"); + break; + } Verbose = TRUE; - SendMode = SM_DELIVER; - message("200", "Verbose mode"); + e->e_sendmode = SM_DELIVER; + message("250 Verbose mode"); break; case CMDONEX: /* doing one transaction only */ OneXact = TRUE; - message("200", "Only one transaction"); + message("250 Only one transaction"); break; # ifdef SMTPDEBUG case CMDDBGQSHOW: /* show queues */ printf("Send Queue="); - printaddr(CurEnv->e_sendqueue, TRUE); + printaddr(e->e_sendqueue, TRUE); break; case CMDDBGDEBUG: /* set debug mode */ tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); tTflag(p); - message("200", "Debug set"); + message("200 Debug set"); break; # else /* not SMTPDEBUG */ @@ -469,22 +725,22 @@ smtp() case CMDDBGQSHOW: /* show queues */ case CMDDBGDEBUG: /* set debug mode */ # ifdef LOG - if (RealHostName != NULL && LogLevel > 0) + if (LogLevel > 0) syslog(LOG_NOTICE, - "\"%s\" command from %s (%s)\n", + "\"%s\" command from %s (%s)", c->cmdname, RealHostName, - inet_ntoa(RealHostAddr.sin_addr)); + anynet_ntoa(&RealHostAddr)); # endif /* FALL THROUGH */ # endif /* SMTPDEBUG */ case CMDERROR: /* unknown command */ - message("500", "Command unrecognized"); + message("500 Command unrecognized"); break; default: errno = 0; - syserr("smtp: unknown code %d", c->cmdcode); + syserr("500 smtp: unknown code %d", c->cmdcode); break; } } @@ -512,26 +768,29 @@ skipword(p, w) register char *q; /* find beginning of word */ - while (isspace(*p)) + while (isascii(*p) && isspace(*p)) p++; q = p; /* find end of word */ - while (*p != '\0' && *p != ':' && !isspace(*p)) + while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) p++; - while (isspace(*p)) + while (isascii(*p) && isspace(*p)) *p++ = '\0'; if (*p != ':') { syntax: - message("501", "Syntax error"); + message("501 Syntax error in parameters"); Errors++; return (NULL); } *p++ = '\0'; - while (isspace(*p)) + while (isascii(*p) && isspace(*p)) p++; + if (*p == '\0') + goto syntax; + /* see if the input word matches desired word */ if (strcasecmp(q, w)) goto syntax; @@ -539,6 +798,46 @@ skipword(p, w) return (p); } /* +** PRINTVRFYADDR -- print an entry in the verify queue +** +** Parameters: +** a -- the address to print +** last -- set if this is the last one. +** +** Returns: +** none. +** +** Side Effects: +** Prints the appropriate 250 codes. +*/ + +printvrfyaddr(a, last) + register ADDRESS *a; + bool last; +{ + char fmtbuf[20]; + + strcpy(fmtbuf, "250"); + fmtbuf[3] = last ? ' ' : '-'; + + if (a->q_fullname == NULL) + { + if (strchr(a->q_user, '@') == NULL) + strcpy(&fmtbuf[4], "<%s@%s>"); + else + strcpy(&fmtbuf[4], "<%s>"); + message(fmtbuf, a->q_user, MyHostName); + } + else + { + if (strchr(a->q_user, '@') == NULL) + strcpy(&fmtbuf[4], "%s <%s@%s>"); + else + strcpy(&fmtbuf[4], "%s <%s>"); + message(fmtbuf, a->q_fullname, a->q_user, MyHostName); + } +} + /* ** HELP -- implement the HELP command. ** ** Parameters: @@ -563,7 +862,7 @@ help(topic) { /* no help */ errno = 0; - message("502", "HELP not implemented"); + message("502 HELP not implemented"); return; } @@ -581,21 +880,21 @@ help(topic) { register char *p; - p = index(buf, '\t'); + p = strchr(buf, '\t'); if (p == NULL) p = buf; else p++; fixcrlf(p, TRUE); - message("214-", p); + message("214-%s", p); noinfo = FALSE; } } if (noinfo) - message("504", "HELP topic unknown"); + message("504 HELP topic unknown"); else - message("214", "End of HELP info"); + message("214 End of HELP info"); (void) fclose(hf); } /* @@ -612,8 +911,9 @@ help(topic) ** none. */ -runinchild(label) +runinchild(label, e) char *label; + register ENVELOPE *e; { int childpid; @@ -630,6 +930,7 @@ runinchild(label) auto int st; /* parent -- wait for child to complete */ + setproctitle("server %s child wait", CurHostName); st = waitfor(childpid); if (st == -1) syserr("%s: lost child", label); @@ -645,14 +946,14 @@ runinchild(label) /* child */ InChild = TRUE; QuickAbort = FALSE; - clearenvelope(CurEnv, FALSE); + clearenvelope(e, FALSE); } } /* open alias database */ - initaliases(AliasFile, FALSE); + initmaps(FALSE, e); return (0); } -# endif SMTP +# endif /* SMTP */ diff --git a/usr.sbin/sendmail/src/stab.c b/usr.sbin/sendmail/src/stab.c index aefbabf8bd..07893e5b5d 100644 --- a/usr.sbin/sendmail/src/stab.c +++ b/usr.sbin/sendmail/src/stab.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,7 +33,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)stab.c 5.7 (Berkeley) 6/1/90"; +static char sccsid[] = "@(#)stab.c 8.1 (Berkeley) 6/7/93"; #endif /* not lint */ # include "sendmail.h" @@ -133,3 +133,34 @@ stab(name, type, op) return (s); } + /* +** STABAPPLY -- apply function to all stab entries +** +** Parameters: +** func -- the function to apply. It will be given one +** parameter (the stab entry). +** arg -- an arbitrary argument, passed to func. +** +** Returns: +** none. +*/ + +void +stabapply(func, arg) + void (*func)__P((STAB *, int)); + int arg; +{ + register STAB **shead; + register STAB *s; + + for (shead = SymTab; shead < &SymTab[STABSIZE]; shead++) + { + for (s = *shead; s != NULL; s = s->s_next) + { + if (tTd(38, 90)) + printf("stabapply: trying %d/%s\n", + s->s_type, s->s_name); + func(s, arg); + } + } +} diff --git a/usr.sbin/sendmail/src/stats.c b/usr.sbin/sendmail/src/stats.c index 28e8d502ee..94bead6d34 100644 --- a/usr.sbin/sendmail/src/stats.c +++ b/usr.sbin/sendmail/src/stats.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,7 +33,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)stats.c 5.11 (Berkeley) 6/1/90"; +static char sccsid[] = "@(#)stats.c 8.2 (Berkeley) 7/11/93"; #endif /* not lint */ # include "sendmail.h" @@ -41,6 +41,8 @@ static char sccsid[] = "@(#)stats.c 5.11 (Berkeley) 6/1/90"; struct statistics Stat; +bool GotStats = FALSE; /* set when we have stats to merge */ + #define ONE_K 1000 /* one thousand (twenty-four?) */ #define KBYTES(x) (((x) + (ONE_K - 1)) / ONE_K) /* @@ -57,14 +59,15 @@ markstats(e, to) { Stat.stat_nf[e->e_from.q_mailer->m_mno]++; Stat.stat_bf[e->e_from.q_mailer->m_mno] += - KBYTES(CurEnv->e_msgsize); + KBYTES(e->e_msgsize); } } else { Stat.stat_nt[to->q_mailer->m_mno]++; - Stat.stat_bt[to->q_mailer->m_mno] += KBYTES(CurEnv->e_msgsize); + Stat.stat_bt[to->q_mailer->m_mno] += KBYTES(e->e_msgsize); } + GotStats = TRUE; } /* ** POSTSTATS -- post statistics in the statistics file @@ -86,7 +89,7 @@ poststats(sfile) struct statistics stat; extern off_t lseek(); - if (sfile == NULL) + if (sfile == NULL || !GotStats) return; (void) time(&Stat.stat_itime); @@ -119,4 +122,8 @@ poststats(sfile) (void) lseek(fd, (off_t) 0, 0); (void) write(fd, (char *) &stat, sizeof stat); (void) close(fd); + + /* clear the structure to avoid future disappointment */ + bzero(&Stat, sizeof stat); + GotStats = FALSE; } diff --git a/usr.sbin/sendmail/src/sysexits.c b/usr.sbin/sendmail/src/sysexits.c index 4cc9c3df57..fff3783f91 100644 --- a/usr.sbin/sendmail/src/sysexits.c +++ b/usr.sbin/sendmail/src/sysexits.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,56 +33,35 @@ */ #ifndef lint -static char sccsid[] = "@(#)sysexits.c 5.6 (Berkeley) 6/1/90"; +static char sccsid[] = "@(#)sysexits.c 8.1 (Berkeley) 6/7/93"; #endif /* not lint */ #include <sysexits.h> /* - * SYSEXITS.C -- error messages corresponding to sysexits.h - */ -char *SysExMsg[] = { - /* 64 USAGE */ "500 Bad usage", - /* 65 DATAERR */ "501 Data format error", - /* 66 NOINPUT */ "550 Cannot open input", - /* 67 NOUSER */ "550 User unknown", - /* 68 NOHOST */ "550 Host unknown", - /* 69 UNAVAILABLE */ "554 Service unavailable", - /* 70 SOFTWARE */ "554 Internal error", - /* 71 OSERR */ "451 Operating system error", - /* 72 OSFILE */ "554 System file missing", - /* 73 CANTCREAT */ "550 Can't create output", - /* 74 IOERR */ "451 I/O error", - /* 75 TEMPFAIL */ "250 Deferred", - /* 76 PROTOCOL */ "554 Remote protocol error", - /* 77 NOPERM */ "550 Insufficient permission", - /* 78 CONFIG */ "554 Local configuration error", -}; - -int N_SysEx = sizeof(SysExMsg) / sizeof(SysExMsg[0]); +** SYSEXITS.C -- error messages corresponding to sysexits.h +** +** If the first character of the string is a colon, interpolate +** the current errno after the rest of the string. +*/ -/* - * STATSTRING -- return string corresponding to an error status - * - * Parameters: - * stat -- the status to decode. - * - * Returns: - * The string corresponding to that status - * - * Side Effects: - * none. - */ -char * -statstring(stat) - int stat; +char *SysExMsg[] = { - static char ebuf[50]; + /* 64 USAGE */ " 500 Bad usage", + /* 65 DATAERR */ " 501 Data format error", + /* 66 NOINPUT */ ":550 Cannot open input", + /* 67 NOUSER */ " 550 User unknown", + /* 68 NOHOST */ " 550 Host unknown", + /* 69 UNAVAILABLE */ " 554 Service unavailable", + /* 70 SOFTWARE */ ":554 Internal error", + /* 71 OSERR */ ":451 Operating system error", + /* 72 OSFILE */ ":554 System file missing", + /* 73 CANTCREAT */ ":550 Can't create output", + /* 74 IOERR */ ":451 I/O error", + /* 75 TEMPFAIL */ " 250 Deferred", + /* 76 PROTOCOL */ " 554 Remote protocol error", + /* 77 NOPERM */ ":550 Insufficient permission", + /* 78 CONFIG */ " 554 Local configuration error", +}; - stat -= EX__BASE; - if (stat < 0 || stat >= N_SysEx) { - (void)sprintf(ebuf, "554 Unknown status %d", stat + EX__BASE); - return(ebuf); - } - return(SysExMsg[stat]); -} +int N_SysEx = sizeof(SysExMsg) / sizeof(SysExMsg[0]); diff --git a/usr.sbin/sendmail/src/sysexits.h b/usr.sbin/sendmail/src/sysexits.h new file mode 100644 index 0000000000..464cb11bab --- /dev/null +++ b/usr.sbin/sendmail/src/sysexits.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)sysexits.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _SYSEXITS_H_ +#define _SYSEXITS_H_ + +/* + * SYSEXITS.H -- Exit status codes for system programs. + * + * This include file attempts to categorize possible error + * exit statuses for system programs, notably delivermail + * and the Berkeley network. + * + * Error numbers begin at EX__BASE to reduce the possibility of + * clashing with other exit statuses that random programs may + * already return. The meaning of the codes is approximately + * as follows: + * + * EX_USAGE -- The command was used incorrectly, e.g., with + * the wrong number of arguments, a bad flag, a bad + * syntax in a parameter, or whatever. + * EX_DATAERR -- The input data was incorrect in some way. + * This should only be used for user's data & not + * system files. + * EX_NOINPUT -- An input file (not a system file) did not + * exist or was not readable. This could also include + * errors like "No message" to a mailer (if it cared + * to catch it). + * EX_NOUSER -- The user specified did not exist. This might + * be used for mail addresses or remote logins. + * EX_NOHOST -- The host specified did not exist. This is used + * in mail addresses or network requests. + * EX_UNAVAILABLE -- A service is unavailable. This can occur + * if a support program or file does not exist. This + * can also be used as a catchall message when something + * you wanted to do doesn't work, but you don't know + * why. + * EX_SOFTWARE -- An internal software error has been detected. + * This should be limited to non-operating system related + * errors as possible. + * EX_OSERR -- An operating system error has been detected. + * This is intended to be used for such things as "cannot + * fork", "cannot create pipe", or the like. It includes + * things like getuid returning a user that does not + * exist in the passwd file. + * EX_OSFILE -- Some system file (e.g., /etc/passwd, /etc/utmp, + * etc.) does not exist, cannot be opened, or has some + * sort of error (e.g., syntax error). + * EX_CANTCREAT -- A (user specified) output file cannot be + * created. + * EX_IOERR -- An error occurred while doing I/O on some file. + * EX_TEMPFAIL -- temporary failure, indicating something that + * is not really an error. In sendmail, this means + * that a mailer (e.g.) could not create a connection, + * and the request should be reattempted later. + * EX_PROTOCOL -- the remote system returned something that + * was "not possible" during a protocol exchange. + * EX_NOPERM -- You did not have sufficient permission to + * perform the operation. This is not intended for + * file system problems, which should use NOINPUT or + * CANTCREAT, but rather for higher level permissions. + */ + +#define EX_OK 0 /* successful termination */ + +#define EX__BASE 64 /* base value for error messages */ + +#define EX_USAGE 64 /* command line usage error */ +#define EX_DATAERR 65 /* data format error */ +#define EX_NOINPUT 66 /* cannot open input */ +#define EX_NOUSER 67 /* addressee unknown */ +#define EX_NOHOST 68 /* host name unknown */ +#define EX_UNAVAILABLE 69 /* service unavailable */ +#define EX_SOFTWARE 70 /* internal software error */ +#define EX_OSERR 71 /* system error (e.g., can't fork) */ +#define EX_OSFILE 72 /* critical OS file missing */ +#define EX_CANTCREAT 73 /* can't create (user) output file */ +#define EX_IOERR 74 /* input/output error */ +#define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */ +#define EX_PROTOCOL 76 /* remote error in protocol */ +#define EX_NOPERM 77 /* permission denied */ +#define EX_CONFIG 78 /* configuration error */ + +#define EX__MAX 78 /* maximum listed value */ + +#endif /* !_SYSEXITS_H_ */ diff --git a/usr.sbin/sendmail/src/trace.c b/usr.sbin/sendmail/src/trace.c index 45c7a6c7e3..f27d70b318 100644 --- a/usr.sbin/sendmail/src/trace.c +++ b/usr.sbin/sendmail/src/trace.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,7 +33,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)trace.c 5.6 (Berkeley) 6/1/90"; +static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/7/93"; #endif /* not lint */ # include "sendmail.h" diff --git a/usr.sbin/sendmail/src/udb.c b/usr.sbin/sendmail/src/udb.c new file mode 100644 index 0000000000..d0987d407f --- /dev/null +++ b/usr.sbin/sendmail/src/udb.c @@ -0,0 +1,723 @@ +/* + * Copyright (c) 1983 Eric P. Allman + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "sendmail.h" + +#ifndef lint +#ifdef USERDB +static char sccsid [] = "@(#)udb.c 8.1 (Berkeley) 6/7/93 (with USERDB)"; +#else +static char sccsid [] = "@(#)udb.c 8.1 (Berkeley) 6/7/93 (without USERDB)"; +#endif +#endif + +#ifdef USERDB + +#include <sys/time.h> +#include <errno.h> +#include <netdb.h> +#include <db.h> + +/* +** UDB.C -- interface between sendmail and Berkeley User Data Base. +** +** This depends on the 4.4BSD db package. +*/ + + +struct udbent +{ + char *udb_spec; /* string version of spec */ + int udb_type; /* type of entry */ + char *udb_default; /* default host for outgoing mail */ + union + { + /* type UE_REMOTE -- do remote call for lookup */ + struct + { + struct sockaddr_in _udb_addr; /* address */ + int _udb_timeout; /* timeout */ + } udb_remote; +#define udb_addr udb_u.udb_remote._udb_addr +#define udb_timeout udb_u.udb_remote._udb_timeout + + /* type UE_FORWARD -- forward message to remote */ + struct + { + char *_udb_fwdhost; /* name of forward host */ + } udb_forward; +#define udb_fwdhost udb_u.udb_forward._udb_fwdhost + + /* type UE_FETCH -- lookup in local database */ + struct + { + char *_udb_dbname; /* pathname of database */ + DB *_udb_dbp; /* open database ptr */ + } udb_lookup; +#define udb_dbname udb_u.udb_lookup._udb_dbname +#define udb_dbp udb_u.udb_lookup._udb_dbp + } udb_u; +}; + +#define UDB_EOLIST 0 /* end of list */ +#define UDB_SKIP 1 /* skip this entry */ +#define UDB_REMOTE 2 /* look up in remote database */ +#define UDB_DBFETCH 3 /* look up in local database */ +#define UDB_FORWARD 4 /* forward to remote host */ + +#define MAXUDBENT 10 /* maximum number of UDB entries */ + + +struct option +{ + char *name; + char *val; +}; + /* +** UDBEXPAND -- look up user in database and expand +** +** Parameters: +** a -- address to expand. +** sendq -- pointer to head of sendq to put the expansions in. +** +** Returns: +** EX_TEMPFAIL -- if something "odd" happened -- probably due +** to accessing a file on an NFS server that is down. +** EX_OK -- otherwise. +** +** Side Effects: +** Modifies sendq. +*/ + +int UdbPort = 1616; +int UdbTimeout = 10; + +struct udbent UdbEnts[MAXUDBENT + 1]; +int UdbSock = -1; +bool UdbInitialized = FALSE; + +int +udbexpand(a, sendq, e) + register ADDRESS *a; + ADDRESS **sendq; + register ENVELOPE *e; +{ + int i; + register char *p; + DBT key; + DBT info; + bool breakout; + register struct udbent *up; + int keylen; + int naddrs; + char keybuf[MAXKEY]; + char buf[BUFSIZ]; + + if (tTd(28, 1)) + printf("udbexpand(%s)\n", a->q_paddr); + + /* make certain we are supposed to send to this address */ + if (bitset(QDONTSEND|QVERIFIED, a->q_flags)) + return EX_OK; + e->e_to = a->q_paddr; + + /* on first call, locate the database */ + if (!UdbInitialized) + { + extern int _udbx_init(); + + if (_udbx_init() == EX_TEMPFAIL) + return EX_TEMPFAIL; + } + + /* short circuit the process if no chance of a match */ + if (UdbSpec == NULL || UdbSpec[0] == '\0') + return EX_OK; + + /* if name is too long, assume it won't match */ + if (strlen(a->q_user) > sizeof keybuf - 12) + return EX_OK; + + /* if name begins with a colon, it indicates our metadata */ + if (a->q_user[0] == ':') + return EX_OK; + + /* build actual database key */ + (void) strcpy(keybuf, a->q_user); + (void) strcat(keybuf, ":maildrop"); + keylen = strlen(keybuf); + + breakout = FALSE; + for (up = UdbEnts; !breakout; up++) + { + char *user; + + /* + ** Select action based on entry type. + ** + ** On dropping out of this switch, "class" should + ** explain the type of the data, and "user" should + ** contain the user information. + */ + + switch (up->udb_type) + { + case UDB_DBFETCH: + key.data = keybuf; + key.size = keylen; + if (tTd(28, 80)) + printf("udbexpand: trying %s\n", keybuf); + i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR); + if (i > 0 || info.size <= 0) + { + if (tTd(28, 2)) + printf("udbexpand: no match on %s\n", keybuf); + continue; + } + if (tTd(28, 80)) + printf("udbexpand: match %.*s: %.*s\n", + key.size, key.data, info.size, info.data); + + naddrs = 0; + a->q_flags &= ~QSELFREF; + while (i == 0 && key.size == keylen && + bcmp(key.data, keybuf, keylen) == 0) + { + if (bitset(EF_VRFYONLY, e->e_flags)) + { + a->q_flags |= QVERIFIED; + e->e_nrcpts++; + return EX_OK; + } + + breakout = TRUE; + if (info.size < sizeof buf) + user = buf; + else + user = xalloc(info.size + 1); + bcopy(info.data, user, info.size); + user[info.size] = '\0'; + + message("expanded to %s", user); +#ifdef LOG + if (LogLevel >= 10) + syslog(LOG_INFO, "%s: expand %s => %s", + e->e_id, e->e_to, user); +#endif + AliasLevel++; + naddrs += sendtolist(user, a, sendq, e); + AliasLevel--; + + if (user != buf) + free(user); + + /* get the next record */ + i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT); + } + + /* if nothing ever matched, try next database */ + if (!breakout) + continue; + + if (naddrs > 0 && !bitset(QSELFREF, a->q_flags)) + { + if (tTd(28, 5)) + { + printf("udbexpand: QDONTSEND "); + printaddr(a, FALSE); + } + a->q_flags |= QDONTSEND; + } + if (i < 0) + { + syserr("udbexpand: db-get %.*s stat %d", + key.size, key.data, i); + return EX_TEMPFAIL; + } + + /* + ** If this address has a -request address, reflect + ** it into the envelope. + */ + + (void) strcpy(keybuf, a->q_user); + (void) strcat(keybuf, ":mailsender"); + keylen = strlen(keybuf); + key.data = keybuf; + key.size = keylen; + i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); + if (i != 0 || info.size <= 0) + break; + a->q_owner = xalloc(info.size + 1); + bcopy(info.data, a->q_owner, info.size); + a->q_owner[info.size] = '\0'; + break; + + case UDB_REMOTE: + /* not yet implemented */ + continue; + + case UDB_FORWARD: + if (bitset(EF_VRFYONLY, e->e_flags)) + return EX_OK; + i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1; + if (i < sizeof buf) + user = buf; + else + user = xalloc(i + 1); + (void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost); + message("expanded to %s", user); + a->q_flags &= ~QSELFREF; + AliasLevel++; + naddrs = sendtolist(user, a, sendq, e); + AliasLevel--; + if (naddrs > 0 && !bitset(QSELFREF, a->q_flags)) + { + if (tTd(28, 5)) + { + printf("udbexpand: QDONTSEND "); + printaddr(a, FALSE); + } + a->q_flags |= QDONTSEND; + } + if (user != buf) + free(user); + breakout = TRUE; + break; + + case UDB_EOLIST: + breakout = TRUE; + continue; + + default: + /* unknown entry type */ + continue; + } + } + return EX_OK; +} + /* +** UDBSENDER -- return canonical external name of sender, given local name +** +** Parameters: +** sender -- the name of the sender on the local machine. +** +** Returns: +** The external name for this sender, if derivable from the +** database. +** NULL -- if nothing is changed from the database. +** +** Side Effects: +** none. +*/ + +char * +udbsender(sender) + char *sender; +{ + register char *p; + register struct udbent *up; + int i; + int keylen; + DBT key, info; + char keybuf[MAXKEY]; + + if (tTd(28, 1)) + printf("udbsender(%s)\n", sender); + + if (!UdbInitialized) + { + if (_udbx_init() == EX_TEMPFAIL) + return NULL; + } + + /* short circuit if no spec */ + if (UdbSpec == NULL || UdbSpec[0] == '\0') + return NULL; + + /* long names can never match and are a pain to deal with */ + if (strlen(sender) > sizeof keybuf - 12) + return NULL; + + /* names beginning with colons indicate metadata */ + if (sender[0] == ':') + return NULL; + + /* build database key */ + (void) strcpy(keybuf, sender); + (void) strcat(keybuf, ":mailname"); + keylen = strlen(keybuf); + + for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) + { + /* + ** Select action based on entry type. + */ + + switch (up->udb_type) + { + case UDB_DBFETCH: + key.data = keybuf; + key.size = keylen; + i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); + if (i != 0 || info.size <= 0) + { + if (tTd(28, 2)) + printf("udbsender: no match on %s\n", + keybuf); + continue; + } + + p = xalloc(info.size + 1); + bcopy(info.data, p, info.size); + p[info.size] = '\0'; + if (tTd(28, 1)) + printf("udbsender ==> %s\n", p); + return p; + } + } + + /* + ** Nothing yet. Search again for a default case. But only + ** use it if we also have a forward (:maildrop) pointer already + ** in the database. + */ + + /* build database key */ + (void) strcpy(keybuf, sender); + (void) strcat(keybuf, ":maildrop"); + keylen = strlen(keybuf); + + for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) + { + switch (up->udb_type) + { + case UDB_DBFETCH: + /* get the default case for this database */ + if (up->udb_default == NULL) + { + key.data = ":default:mailname"; + key.size = strlen(key.data); + i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); + if (i != 0 || info.size <= 0) + { + /* no default case */ + up->udb_default = ""; + continue; + } + + /* save the default case */ + up->udb_default = xalloc(info.size + 1); + bcopy(info.data, up->udb_default, info.size); + up->udb_default[info.size] = '\0'; + } + else if (up->udb_default[0] == '\0') + continue; + + /* we have a default case -- verify user:maildrop */ + key.data = keybuf; + key.size = keylen; + i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); + if (i != 0 || info.size <= 0) + { + /* nope -- no aliasing for this user */ + continue; + } + + /* they exist -- build the actual address */ + p = xalloc(strlen(sender) + strlen(up->udb_default) + 2); + (void) strcpy(p, sender); + (void) strcat(p, "@"); + (void) strcat(p, up->udb_default); + if (tTd(28, 1)) + printf("udbsender ==> %s\n", p); + return p; + } + } + + /* still nothing.... too bad */ + return NULL; +} + /* +** _UDBX_INIT -- parse the UDB specification, opening any valid entries. +** +** Parameters: +** none. +** +** Returns: +** EX_TEMPFAIL -- if it appeared it couldn't get hold of a +** database due to a host being down or some similar +** (recoverable) situation. +** EX_OK -- otherwise. +** +** Side Effects: +** Fills in the UdbEnts structure from UdbSpec. +*/ + +#define MAXUDBOPTS 27 + +int +_udbx_init() +{ + register char *p; + int i; + register struct udbent *up; + char buf[BUFSIZ]; + + if (UdbInitialized) + return EX_OK; + +# ifdef UDB_DEFAULT_SPEC + if (UdbSpec == NULL) + UdbSpec = UDB_DEFAULT_SPEC; +# endif + + p = UdbSpec; + up = UdbEnts; + while (p != NULL) + { + char *spec; + auto int rcode; + int nopts; + int nmx; + register struct hostent *h; + char *mxhosts[MAXMXHOSTS + 1]; + struct option opts[MAXUDBOPTS + 1]; + + while (*p == ' ' || *p == '\t' || *p == ',') + p++; + if (*p == '\0') + break; + spec = p; + p = strchr(p, ','); + if (p != NULL) + *p++ = '\0'; + + /* extract options */ + nopts = _udb_parsespec(spec, opts, MAXUDBOPTS); + + /* + ** Decode database specification. + ** + ** In the sendmail tradition, the leading character + ** defines the semantics of the rest of the entry. + ** + ** +hostname -- send a datagram to the udb server + ** on host "hostname" asking for the + ** home mail server for this user. + ** *hostname -- similar to +hostname, except that the + ** hostname is searched as an MX record; + ** resulting hosts are searched as for + ** +mxhostname. If no MX host is found, + ** this is the same as +hostname. + ** @hostname -- forward email to the indicated host. + ** This should be the last in the list, + ** since it always matches the input. + ** /dbname -- search the named database on the local + ** host using the Berkeley db package. + */ + + switch (*spec) + { + case '+': /* search remote database */ + case '*': /* search remote database (expand MX) */ + if (*spec == '*') + { +#ifdef NAMED_BIND + nmx = getmxrr(spec + 1, mxhosts, FALSE, &rcode); +#else + mxhosts[0] = spec + 1; + nmx = 1; + rcode = 0; +#endif + if (tTd(28, 16)) + { + int i; + + printf("getmxrr(%s): %d", spec + 1, nmx); + for (i = 0; i <= nmx; i++) + printf(" %s", mxhosts[i]); + printf("\n"); + } + } + else + { + nmx = 1; + mxhosts[0] = spec + 1; + } + + for (i = 0; i < nmx; i++) + { + h = gethostbyname(mxhosts[i]); + if (h == NULL) + continue; + up->udb_type = UDB_REMOTE; + up->udb_addr.sin_family = h->h_addrtype; + bcopy(h->h_addr_list[0], + (char *) &up->udb_addr.sin_addr, + h->h_length); + up->udb_addr.sin_port = UdbPort; + up->udb_timeout = UdbTimeout; + up++; + } + + /* set up a datagram socket */ + if (UdbSock < 0) + { + UdbSock = socket(AF_INET, SOCK_DGRAM, 0); + (void) fcntl(UdbSock, F_SETFD, 1); + } + break; + + case '@': /* forward to remote host */ + up->udb_type = UDB_FORWARD; + up->udb_fwdhost = spec + 1; + up++; + break; + + case '/': /* look up remote name */ + up->udb_dbname = spec; + errno = 0; + up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL); + if (up->udb_dbp == NULL) + { + if (errno != ENOENT && errno != EACCES) + { +#ifdef LOG + if (LogLevel > 2) + syslog(LOG_ERR, "dbopen(%s): %s", + spec, errstring(errno)); +#endif + up->udb_type = UDB_EOLIST; + goto tempfail; + } + break; + } + up->udb_type = UDB_DBFETCH; + up++; + break; + } + } + up->udb_type = UDB_EOLIST; + + if (tTd(28, 4)) + { + for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) + { + switch (up->udb_type) + { + case UDB_REMOTE: + printf("REMOTE: addr %s, timeo %d\n", + anynet_ntoa((SOCKADDR *) &up->udb_addr), + up->udb_timeout); + break; + + case UDB_DBFETCH: + printf("FETCH: file %s\n", + up->udb_dbname); + break; + + case UDB_FORWARD: + printf("FORWARD: host %s\n", + up->udb_fwdhost); + break; + + default: + printf("UNKNOWN\n"); + break; + } + } + } + + UdbInitialized = TRUE; + errno = 0; + return EX_OK; + + /* + ** On temporary failure, back out anything we've already done + */ + + tempfail: + for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) + { + if (up->udb_type == UDB_DBFETCH) + { + (*up->udb_dbp->close)(up->udb_dbp); + } + } + return EX_TEMPFAIL; +} + +int +_udb_parsespec(udbspec, opt, maxopts) + char *udbspec; + struct option opt[]; + int maxopts; +{ + register char *spec; + register char *spec_end; + register int optnum; + + spec_end = strchr(udbspec, ':'); + for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++) + { + register char *p; + + while (isascii(*spec) && isspace(*spec)) + spec++; + spec_end = strchr(spec, ':'); + if (spec_end != NULL) + *spec_end++ = '\0'; + + opt[optnum].name = spec; + opt[optnum].val = NULL; + p = strchr(spec, '='); + if (p != NULL) + opt[optnum].val = ++p; + } + return optnum; +} + +#else /* not USERDB */ + +int +udbexpand(a, sendq, e) + ADDRESS *a; + ADDRESS **sendq; + ENVELOPE *e; +{ + return EX_OK; +} + +#endif /* USERDB */ diff --git a/usr.sbin/sendmail/src/useful.h b/usr.sbin/sendmail/src/useful.h index 9a22d6a05b..944eb5c962 100644 --- a/usr.sbin/sendmail/src/useful.h +++ b/usr.sbin/sendmail/src/useful.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)useful.h 4.6 (Berkeley) 6/1/90 + * @(#)useful.h 8.1 (Berkeley) 6/7/93 */ # include <sys/types.h> @@ -42,7 +42,7 @@ typedef char bool; # ifndef NULL # define NULL 0 -# endif NULL +# endif /* NULL */ /* bit hacking */ # define bitset(bit, word) (((word) & (bit)) != 0) @@ -51,7 +51,7 @@ typedef char bool; # ifndef max # define max(a, b) ((a) > (b) ? (a) : (b)) # define min(a, b) ((a) < (b) ? (a) : (b)) -# endif max +# endif /* assertions */ # ifndef NASSERT @@ -61,23 +61,13 @@ typedef char bool; fprintf(stderr, "assertion botch: %s:%d: ", __FILE__, __LINE__);\ fprintf(stderr, msg, parm);\ } -# else NASSERT +# else /* NASSERT */ # define ASSERT(expr, msg, parm) -# endif NASSERT +# endif /* NASSERT */ /* sccs id's */ # ifndef lint # define SCCSID(arg) static char SccsId[] = "arg"; -# else lint +# else # define SCCSID(arg) -# endif lint - -/* define the types of some common functions */ -extern char *strcpy(), *strncpy(); -extern char *strcat(), *strncat(); -extern char *malloc(); -extern char *index(), *rindex(); -extern int errno; -extern time_t time(); -extern char *ctime(); -extern char *getenv(); +# endif diff --git a/usr.sbin/sendmail/src/usersmtp.c b/usr.sbin/sendmail/src/usersmtp.c index 1d15c18029..eb62711605 100644 --- a/usr.sbin/sendmail/src/usersmtp.c +++ b/usr.sbin/sendmail/src/usersmtp.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,9 +36,9 @@ #ifndef lint #ifdef SMTP -static char sccsid[] = "@(#)usersmtp.c 5.16 (Berkeley) 3/2/91 (with SMTP)"; +static char sccsid[] = "@(#)usersmtp.c 8.4 (Berkeley) 7/13/93 (with SMTP)"; #else -static char sccsid[] = "@(#)usersmtp.c 5.16 (Berkeley) 3/2/91 (without SMTP)"; +static char sccsid[] = "@(#)usersmtp.c 8.4 (Berkeley) 7/13/93 (without SMTP)"; #endif #endif /* not lint */ @@ -60,16 +60,11 @@ static char sccsid[] = "@(#)usersmtp.c 5.16 (Berkeley) 3/2/91 (without SMTP)"; char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ char SmtpError[MAXLINE] = ""; /* save failure error messages */ -FILE *SmtpOut; /* output file */ -FILE *SmtpIn; /* input file */ int SmtpPid; /* pid of mailer */ -/* following represents the state of the SMTP connection */ -int SmtpState; /* connection state, see below */ - -#define SMTP_CLOSED 0 /* connection is closed */ -#define SMTP_OPEN 1 /* connection is open for business */ -#define SMTP_SSD 2 /* service shutting down */ +#ifdef __STDC__ +extern smtpmessage(char *f, MAILER *m, MCI *mci, ...); +#endif /* ** SMTPINIT -- initialize SMTP. ** @@ -81,67 +76,59 @@ int SmtpState; /* connection state, see below */ ** the mailer. ** ** Returns: -** appropriate exit status -- EX_OK on success. -** If not EX_OK, it should close the connection. +** none. ** ** Side Effects: ** creates connection and sends initial protocol. */ -jmp_buf CtxGreeting; - -smtpinit(m, pvp) +smtpinit(m, mci, e) struct mailer *m; - char **pvp; + register MCI *mci; + ENVELOPE *e; { register int r; - EVENT *gte; - char buf[MAXNAME]; - static int greettimeout(); + register char *p; + extern void esmtp_check(); + extern void helo_options(); + + if (tTd(18, 1)) + { + printf("smtpinit "); + mci_dump(mci); + } /* ** Open the connection to the mailer. */ - if (SmtpState == SMTP_OPEN) - syserr("smtpinit: already open"); - - SmtpIn = SmtpOut = NULL; - SmtpState = SMTP_CLOSED; SmtpError[0] = '\0'; - SmtpPhase = "user open"; - setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase); - SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn); - if (SmtpPid < 0) + CurHostName = mci->mci_host; /* XXX UGLY XXX */ + switch (mci->mci_state) { - if (tTd(18, 1)) - printf("smtpinit: cannot open %s: stat %d errno %d\n", - pvp[0], ExitStat, errno); - if (CurEnv->e_xfp != NULL) - { - register char *p; - extern char *errstring(); - extern char *statstring(); + case MCIS_ACTIVE: + /* need to clear old information */ + smtprset(m, mci, e); + /* fall through */ - if (errno == 0) - { - p = statstring(ExitStat); - fprintf(CurEnv->e_xfp, - "%.3s %s.%s... %s\n", - p, pvp[1], m->m_name, p); - } - else - { - r = errno; - fprintf(CurEnv->e_xfp, - "421 %s.%s... Deferred: %s\n", - pvp[1], m->m_name, errstring(errno)); - errno = r; - } - } - return (ExitStat); + case MCIS_OPEN: + return; + + case MCIS_ERROR: + case MCIS_SSD: + /* shouldn't happen */ + smtpquit(m, mci, e); + /* fall through */ + + case MCIS_CLOSED: + syserr("451 smtpinit: state CLOSED"); + return; + + case MCIS_OPENING: + break; } - SmtpState = SMTP_OPEN; + + mci->mci_state = MCIS_OPENING; /* ** Get the greeting message. @@ -149,31 +136,66 @@ smtpinit(m, pvp) ** happen. */ - if (setjmp(CtxGreeting) != 0) - goto tempfail; - gte = setevent((time_t) 300, greettimeout, 0); - SmtpPhase = "greeting wait"; - setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); - r = reply(m); - clrevent(gte); + SmtpPhase = mci->mci_phase = "client greeting"; + setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); + r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check); if (r < 0 || REPLYTYPE(r) != 2) - goto tempfail; + goto tempfail1; /* ** Send the HELO command. ** My mother taught me to always introduce myself. */ - smtpmessage("HELO %s", m, MyHostName); - SmtpPhase = "HELO wait"; - setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); - r = reply(m); + if (bitnset(M_ESMTP, m->m_flags)) + mci->mci_flags |= MCIF_ESMTP; + +tryhelo: + if (bitset(MCIF_ESMTP, mci->mci_flags)) + { + smtpmessage("EHLO %s", m, mci, MyHostName); + SmtpPhase = mci->mci_phase = "client EHLO"; + } + else + { + smtpmessage("HELO %s", m, mci, MyHostName); + SmtpPhase = mci->mci_phase = "client HELO"; + } + setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); + r = reply(m, mci, e, TimeOuts.to_helo, helo_options); if (r < 0) - goto tempfail; + goto tempfail1; else if (REPLYTYPE(r) == 5) + { + if (bitset(MCIF_ESMTP, mci->mci_flags)) + { + /* try old SMTP instead */ + mci->mci_flags &= ~MCIF_ESMTP; + goto tryhelo; + } goto unavailable; + } else if (REPLYTYPE(r) != 2) - goto tempfail; + goto tempfail1; + + /* + ** Check to see if we actually ended up talking to ourself. + ** This means we didn't know about an alias or MX, or we managed + ** to connect to an echo server. + */ + + p = strchr(&SmtpReplyBuffer[4], ' '); + if (p != NULL) + *p = '\0'; + if (strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) + { + syserr("553 %s config error: mail loops back to myself", + MyHostName); + mci->mci_exitstat = EX_CONFIG; + mci->mci_errno = 0; + smtpquit(m, mci, e); + return; + } /* ** If this is expected to be another sendmail, send some internal @@ -183,65 +205,180 @@ smtpinit(m, pvp) if (bitnset(M_INTERNAL, m->m_flags)) { /* tell it to be verbose */ - smtpmessage("VERB", m); - r = reply(m); + smtpmessage("VERB", m, mci); + r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); if (r < 0) - goto tempfail; + goto tempfail2; + } - /* tell it we will be sending one transaction only */ - smtpmessage("ONEX", m); - r = reply(m); - if (r < 0) - goto tempfail; + mci->mci_state = MCIS_OPEN; + return; + + tempfail1: + tempfail2: + mci->mci_exitstat = EX_TEMPFAIL; + if (mci->mci_errno == 0) + mci->mci_errno = errno; + if (mci->mci_state != MCIS_CLOSED) + smtpquit(m, mci, e); + return; + + unavailable: + mci->mci_exitstat = EX_UNAVAILABLE; + mci->mci_errno = errno; + smtpquit(m, mci, e); + return; +} + /* +** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol +** +** +** Parameters: +** line -- the response line. +** m -- the mailer. +** mci -- the mailer connection info. +** e -- the envelope. +** +** Returns: +** none. +*/ + +void +esmtp_check(line, m, mci, e) + char *line; + MAILER *m; + register MCI *mci; + ENVELOPE *e; +{ + if (strlen(line) < 5) + return; + line += 4; + if (strncmp(line, "ESMTP ", 6) == 0) + mci->mci_flags |= MCIF_ESMTP; +} + /* +** HELO_OPTIONS -- process the options on a HELO line. +** +** Parameters: +** line -- the response line. +** m -- the mailer. +** mci -- the mailer connection info. +** e -- the envelope. +** +** Returns: +** none. +*/ + +void +helo_options(line, m, mci, e) + char *line; + MAILER *m; + register MCI *mci; + ENVELOPE *e; +{ + register char *p; + + if (strlen(line) < 5) + return; + line += 4; + p = strchr(line, ' '); + if (p != NULL) + *p++ = '\0'; + if (strcasecmp(line, "size") == 0) + { + mci->mci_flags |= MCIF_SIZE; + if (p != NULL) + mci->mci_maxsize = atol(p); } + else if (strcasecmp(line, "8bitmime") == 0) + mci->mci_flags |= MCIF_8BITMIME; + else if (strcasecmp(line, "expn") == 0) + mci->mci_flags |= MCIF_EXPN; +} + /* +** SMTPMAILFROM -- send MAIL command +** +** Parameters: +** m -- the mailer. +** mci -- the mailer connection structure. +** e -- the envelope (including the sender to specify). +*/ + +smtpmailfrom(m, mci, e) + struct mailer *m; + MCI *mci; + ENVELOPE *e; +{ + int r; + char buf[MAXNAME]; + char optbuf[MAXLINE]; + + if (tTd(18, 2)) + printf("smtpmailfrom: CurHost=%s\n", CurHostName); + + /* set up appropriate options to include */ + if (bitset(MCIF_SIZE, mci->mci_flags)) + sprintf(optbuf, " SIZE=%ld", e->e_msgsize); + else + strcpy(optbuf, ""); /* ** Send the MAIL command. ** Designates the sender. */ - expand("\001g", buf, &buf[sizeof buf - 1], CurEnv); - if (CurEnv->e_from.q_mailer == LocalMailer || + mci->mci_state = MCIS_ACTIVE; + + if (bitset(EF_RESPONSE, e->e_flags) && + !bitnset(M_NO_NULL_FROM, m->m_flags)) + (void) strcpy(buf, ""); + else + expand("\201g", buf, &buf[sizeof buf - 1], e); + if (e->e_from.q_mailer == LocalMailer || !bitnset(M_FROMPATH, m->m_flags)) { - smtpmessage("MAIL From:<%s>", m, buf); + smtpmessage("MAIL From:<%s>%s", m, mci, buf, optbuf); } else { - smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName, - buf[0] == '@' ? ',' : ':', buf); + smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, + buf[0] == '@' ? ',' : ':', buf, optbuf); } - SmtpPhase = "MAIL wait"; - setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); - r = reply(m); + SmtpPhase = mci->mci_phase = "client MAIL"; + setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); + r = reply(m, mci, e, TimeOuts.to_mail, NULL); if (r < 0 || REPLYTYPE(r) == 4) - goto tempfail; + { + mci->mci_exitstat = EX_TEMPFAIL; + mci->mci_errno = errno; + smtpquit(m, mci, e); + return EX_TEMPFAIL; + } else if (r == 250) - return (EX_OK); + { + mci->mci_exitstat = EX_OK; + return EX_OK; + } else if (r == 552) - goto unavailable; - - /* protocol error -- close up */ - smtpquit(m); - return (EX_PROTOCOL); - - /* signal a temporary failure */ - tempfail: - smtpquit(m); - return (EX_TEMPFAIL); - - /* signal service unavailable */ - unavailable: - smtpquit(m); - return (EX_UNAVAILABLE); -} + { + /* signal service unavailable */ + mci->mci_exitstat = EX_UNAVAILABLE; + smtpquit(m, mci, e); + return EX_UNAVAILABLE; + } +#ifdef LOG + if (LogLevel > 1) + { + syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s", + e->e_id, SmtpReplyBuffer); + } +#endif -static -greettimeout() -{ - /* timeout reading the greeting message */ - longjmp(CtxGreeting, 1); + /* protocol error -- close up */ + smtpquit(m, mci, e); + mci->mci_exitstat = EX_PROTOCOL; + return EX_PROTOCOL; } /* ** SMTPRCPT -- designate recipient. @@ -249,6 +386,8 @@ greettimeout() ** Parameters: ** to -- address of recipient. ** m -- the mailer we are sending to. +** mci -- the connection info for this transaction. +** e -- the envelope for this transaction. ** ** Returns: ** exit status corresponding to recipient status. @@ -257,18 +396,19 @@ greettimeout() ** Sends the mail via SMTP. */ -smtprcpt(to, m) +smtprcpt(to, m, mci, e) ADDRESS *to; register MAILER *m; + MCI *mci; + ENVELOPE *e; { register int r; - extern char *remotename(); - smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE)); + smtpmessage("RCPT To:<%s>", m, mci, to->q_user); - SmtpPhase = "RCPT wait"; - setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); - r = reply(m); + SmtpPhase = mci->mci_phase = "client RCPT"; + setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); + r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); if (r < 0 || REPLYTYPE(r) == 4) return (EX_TEMPFAIL); else if (REPLYTYPE(r) == 2) @@ -277,6 +417,15 @@ smtprcpt(to, m) return (EX_NOUSER); else if (r == 552 || r == 554) return (EX_UNAVAILABLE); + +#ifdef LOG + if (LogLevel > 1) + { + syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s", + e->e_id, SmtpReplyBuffer); + } +#endif + return (EX_PROTOCOL); } /* @@ -293,11 +442,17 @@ smtprcpt(to, m) ** none. */ -smtpdata(m, e) +static jmp_buf CtxDataTimeout; + +smtpdata(m, mci, e) struct mailer *m; + register MCI *mci; register ENVELOPE *e; { register int r; + register EVENT *ev; + time_t timeout; + static int datatimeout(); /* ** Send the data. @@ -308,39 +463,102 @@ smtpdata(m, e) */ /* send the command and check ok to proceed */ - smtpmessage("DATA", m); - SmtpPhase = "DATA wait"; - setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); - r = reply(m); + smtpmessage("DATA", m, mci); + SmtpPhase = mci->mci_phase = "client DATA 354"; + setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); + r = reply(m, mci, e, TimeOuts.to_datainit, NULL); if (r < 0 || REPLYTYPE(r) == 4) + { + smtpquit(m, mci, e); return (EX_TEMPFAIL); + } else if (r == 554) + { + smtprset(m, mci, e); return (EX_UNAVAILABLE); + } else if (r != 354) + { +#ifdef LOG + if (LogLevel > 1) + { + syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s", + e->e_id, SmtpReplyBuffer); + } +#endif + smtprset(m, mci, e); return (EX_PROTOCOL); + } + + /* + ** Set timeout around data writes. Make it at least large + ** enough for DNS timeouts on all recipients plus some fudge + ** factor. The main thing is that it should not be infinite. + */ + + if (setjmp(CtxDataTimeout) != 0) + { + mci->mci_errno = errno; + mci->mci_exitstat = EX_TEMPFAIL; + mci->mci_state = MCIS_ERROR; + syserr("451 timeout writing message to %s", mci->mci_host); + smtpquit(m, mci, e); + return EX_TEMPFAIL; + } + + timeout = e->e_msgsize / 16; + if (timeout < (time_t) 60) + timeout = (time_t) 60; + timeout += e->e_nrcpts * 90; + ev = setevent(timeout, datatimeout, 0); /* now output the actual message */ - (*e->e_puthdr)(SmtpOut, m, CurEnv); - putline("\n", SmtpOut, m); - (*e->e_putbody)(SmtpOut, m, CurEnv); + (*e->e_puthdr)(mci->mci_out, m, e); + putline("\n", mci->mci_out, m); + (*e->e_putbody)(mci->mci_out, m, e, NULL); + + clrevent(ev); /* terminate the message */ - fprintf(SmtpOut, ".%s", m->m_eol); - if (Verbose && !HoldErrs) - nmessage(Arpa_Info, ">>> ."); + fprintf(mci->mci_out, ".%s", m->m_eol); + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%05d >>> .\n", getpid()); + if (Verbose) + nmessage(">>> ."); /* check for the results of the transaction */ - SmtpPhase = "result wait"; - setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); - r = reply(m); - if (r < 0 || REPLYTYPE(r) == 4) + SmtpPhase = mci->mci_phase = "client DATA 250"; + setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); + r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); + if (r < 0) + { + smtpquit(m, mci, e); + return (EX_TEMPFAIL); + } + mci->mci_state = MCIS_OPEN; + e->e_statmsg = newstr(&SmtpReplyBuffer[4]); + if (REPLYTYPE(r) == 4) return (EX_TEMPFAIL); else if (r == 250) return (EX_OK); else if (r == 552 || r == 554) return (EX_UNAVAILABLE); +#ifdef LOG + if (LogLevel > 1) + { + syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s", + e->e_id, SmtpReplyBuffer); + } +#endif return (EX_PROTOCOL); } + + +static int +datatimeout() +{ + longjmp(CtxDataTimeout, 1); +} /* ** SMTPQUIT -- close the SMTP connection. ** @@ -354,40 +572,81 @@ smtpdata(m, e) ** sends the final protocol and closes the connection. */ -smtpquit(m) +smtpquit(m, mci, e) register MAILER *m; + register MCI *mci; + ENVELOPE *e; { int i; - /* if the connection is already closed, don't bother */ - if (SmtpIn == NULL) - return; - - /* send the quit message if not a forced quit */ - if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD) + /* send the quit message if we haven't gotten I/O error */ + if (mci->mci_state != MCIS_ERROR) { - smtpmessage("QUIT", m); - (void) reply(m); - if (SmtpState == SMTP_CLOSED) + SmtpPhase = "client QUIT"; + smtpmessage("QUIT", m, mci); + (void) reply(m, mci, e, TimeOuts.to_quit, NULL); + if (mci->mci_state == MCIS_CLOSED) return; } - /* now actually close the connection */ - (void) fclose(SmtpIn); - (void) fclose(SmtpOut); - SmtpIn = SmtpOut = NULL; - SmtpState = SMTP_CLOSED; - - /* and pick up the zombie */ - i = endmailer(SmtpPid, m->m_argv[0]); + /* now actually close the connection and pick up the zombie */ + i = endmailer(mci, e, m->m_argv); if (i != EX_OK) - syserr("smtpquit %s: stat %d", m->m_argv[0], i); + syserr("451 smtpquit %s: stat %d", m->m_argv[0], i); +} + /* +** SMTPRSET -- send a RSET (reset) command +*/ + +smtprset(m, mci, e) + register MAILER *m; + register MCI *mci; + ENVELOPE *e; +{ + int r; + + SmtpPhase = "client RSET"; + smtpmessage("RSET", m, mci); + r = reply(m, mci, e, TimeOuts.to_rset, NULL); + if (r < 0) + mci->mci_state = MCIS_ERROR; + else if (REPLYTYPE(r) == 2) + { + mci->mci_state = MCIS_OPEN; + return; + } + smtpquit(m, mci, e); +} + /* +** SMTPPROBE -- check the connection state +*/ + +smtpprobe(mci) + register MCI *mci; +{ + int r; + MAILER *m = mci->mci_mailer; + extern ENVELOPE BlankEnvelope; + ENVELOPE *e = &BlankEnvelope; + + SmtpPhase = "client probe"; + smtpmessage("RSET", m, mci); + r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); + if (r < 0 || REPLYTYPE(r) != 2) + smtpquit(m, mci, e); + return r; } /* ** REPLY -- read arpanet reply ** ** Parameters: ** m -- the mailer we are reading the reply from. +** mci -- the mailer connection info structure. +** e -- the current envelope. +** timeout -- the timeout for reads. +** pfunc -- processing function for second and subsequent +** lines of response -- if null, no special +** processing is done. ** ** Returns: ** reply code it reads. @@ -396,10 +655,20 @@ smtpquit(m) ** flushes the mail file. */ -reply(m) +reply(m, mci, e, timeout, pfunc) MAILER *m; + MCI *mci; + ENVELOPE *e; + time_t timeout; + void (*pfunc)(); { - (void) fflush(SmtpOut); + register char *bufp; + register int r; + bool firstline = TRUE; + char junkbuf[MAXLINE]; + + if (mci->mci_out != NULL) + (void) fflush(mci->mci_out); if (tTd(18, 1)) printf("reply\n"); @@ -408,87 +677,120 @@ reply(m) ** Read the input line, being careful not to hang. */ - for (;;) + for (bufp = SmtpReplyBuffer;; bufp = junkbuf) { - register int r; register char *p; + extern time_t curtime(); /* actually do the read */ - if (CurEnv->e_xfp != NULL) - (void) fflush(CurEnv->e_xfp); /* for debugging */ + if (e->e_xfp != NULL) + (void) fflush(e->e_xfp); /* for debugging */ /* if we are in the process of closing just give the code */ - if (SmtpState == SMTP_CLOSED) + if (mci->mci_state == MCIS_CLOSED) return (SMTPCLOSING); + if (mci->mci_out != NULL) + fflush(mci->mci_out); + /* get the line from the other side */ - p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); + p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); + mci->mci_lastuse = curtime(); + if (p == NULL) { + bool oldholderrs; extern char MsgBuf[]; /* err.c */ - extern char Arpa_TSyserr[]; /* conf.c */ /* if the remote end closed early, fake an error */ if (errno == 0) # ifdef ECONNRESET errno = ECONNRESET; -# else ECONNRESET +# else /* ECONNRESET */ errno = EPIPE; -# endif ECONNRESET +# endif /* ECONNRESET */ + + mci->mci_errno = errno; + mci->mci_exitstat = EX_TEMPFAIL; + oldholderrs = HoldErrs; + HoldErrs = TRUE; + usrerr("451 reply: read error from %s", mci->mci_host); - message(Arpa_TSyserr, "reply: read error"); /* if debugging, pause so we can see state */ if (tTd(18, 100)) pause(); -# ifdef LOG - syslog(LOG_INFO, "%s", &MsgBuf[4]); -# endif LOG - SmtpState = SMTP_CLOSED; - smtpquit(m); + mci->mci_state = MCIS_ERROR; + smtpquit(m, mci, e); +#ifdef XDEBUG + { + char wbuf[MAXLINE]; + sprintf(wbuf, "%s... reply(%s) during %s", + e->e_to, mci->mci_host, SmtpPhase); + checkfd012(wbuf); + } +#endif + HoldErrs = oldholderrs; return (-1); } - fixcrlf(SmtpReplyBuffer, TRUE); + fixcrlf(bufp, TRUE); - if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL) + /* EHLO failure is not a real error */ + if (e->e_xfp != NULL && (bufp[0] == '4' || + (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) { /* serious error -- log the previous command */ if (SmtpMsgBuffer[0] != '\0') - fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer); + fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); SmtpMsgBuffer[0] = '\0'; /* now log the message as from the other side */ - fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer); + fprintf(e->e_xfp, "<<< %s\n", bufp); } /* display the input for verbose mode */ - if (Verbose && !HoldErrs) - nmessage(Arpa_Info, "%s", SmtpReplyBuffer); + if (Verbose) + nmessage("050 %s", bufp); + + /* process the line */ + if (pfunc != NULL && !firstline) + (*pfunc)(bufp, m, mci, e); + + firstline = FALSE; /* if continuation is required, we can go on */ - if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) + if (bufp[3] == '-') + continue; + + /* ignore improperly formated input */ + if (!(isascii(bufp[0]) && isdigit(bufp[0]))) continue; /* decode the reply code */ - r = atoi(SmtpReplyBuffer); + r = atoi(bufp); /* extra semantics: 0xx codes are "informational" */ - if (r < 100) - continue; + if (r >= 100) + break; + } - /* reply code 421 is "Service Shutting Down" */ - if (r == SMTPCLOSING && SmtpState != SMTP_SSD) - { - /* send the quit protocol */ - SmtpState = SMTP_SSD; - smtpquit(m); - } + /* + ** Now look at SmtpReplyBuffer -- only care about the first + ** line of the response from here on out. + */ - /* save temporary failure messages for posterity */ - if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') - (void) strcpy(SmtpError, &SmtpReplyBuffer[4]); + /* save temporary failure messages for posterity */ + if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') + (void) strcpy(SmtpError, SmtpReplyBuffer); - return (r); + /* reply code 421 is "Service Shutting Down" */ + if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) + { + /* send the quit protocol */ + mci->mci_state = MCIS_SSD; + smtpquit(m, mci, e); } + + return (r); } /* ** SMTPMESSAGE -- send message to server @@ -502,20 +804,39 @@ reply(m) ** none. ** ** Side Effects: -** writes message to SmtpOut. +** writes message to mci->mci_out. */ /*VARARGS1*/ -smtpmessage(f, m, a, b, c) +#ifdef __STDC__ +smtpmessage(char *f, MAILER *m, MCI *mci, ...) +#else +smtpmessage(f, m, mci, va_alist) char *f; MAILER *m; + MCI *mci; + va_dcl +#endif { - (void) sprintf(SmtpMsgBuffer, f, a, b, c); - if (tTd(18, 1) || (Verbose && !HoldErrs)) - nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); - if (SmtpOut != NULL) - fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, - m == 0 ? "\r\n" : m->m_eol); + VA_LOCAL_DECL + + VA_START(mci); + (void) vsprintf(SmtpMsgBuffer, f, ap); + VA_END; + + if (tTd(18, 1) || Verbose) + nmessage(">>> %s", SmtpMsgBuffer); + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); + if (mci->mci_out != NULL) + { + fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, + m == NULL ? "\r\n" : m->m_eol); + } + else if (tTd(18, 1)) + { + printf("smtpmessage: NULL mci_out\n"); + } } -# endif SMTP +# endif /* SMTP */ diff --git a/usr.sbin/sendmail/src/util.c b/usr.sbin/sendmail/src/util.c index 0aa5469a2f..453a1adc0d 100644 --- a/usr.sbin/sendmail/src/util.c +++ b/usr.sbin/sendmail/src/util.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,17 +33,12 @@ */ #ifndef lint -static char sccsid[] = "@(#)util.c 5.20 (Berkeley) 3/8/91"; +static char sccsid[] = "@(#)util.c 8.3 (Berkeley) 7/13/93"; #endif /* not lint */ -# include <stdio.h> -# include <sys/types.h> -# include <sys/stat.h> -# include <sysexits.h> -# include <errno.h> # include "sendmail.h" - -/* +# include <sysexits.h> + /* ** STRIPQUOTES -- Strip quotes & quote bits from a string. ** ** Runs through a string and strips off unquoted quote @@ -51,8 +46,6 @@ static char sccsid[] = "@(#)util.c 5.20 (Berkeley) 3/8/91"; ** ** Parameters: ** s -- the string to strip. -** qf -- if set, remove actual `` " '' characters -** as well as the quote bits. ** ** Returns: ** none. @@ -64,9 +57,8 @@ static char sccsid[] = "@(#)util.c 5.20 (Berkeley) 3/8/91"; ** deliver */ -stripquotes(s, qf) +stripquotes(s) char *s; - bool qf; { register char *p; register char *q; @@ -75,76 +67,16 @@ stripquotes(s, qf) if (s == NULL) return; - for (p = q = s; (c = *p++) != '\0'; ) - { - if (c != '"' || !qf) - *q++ = c & 0177; - } - *q = '\0'; -} - /* -** QSTRLEN -- give me the string length assuming 0200 bits add a char -** -** Parameters: -** s -- the string to measure. -** -** Reurns: -** The length of s, including space for backslash escapes. -** -** Side Effects: -** none. -*/ - -qstrlen(s) - register char *s; -{ - register int l = 0; - register char c; - - while ((c = *s++) != '\0') - { - if (bitset(0200, c)) - l++; - l++; - } - return (l); -} - /* -** CAPITALIZE -- return a copy of a string, properly capitalized. -** -** Parameters: -** s -- the string to capitalize. -** -** Returns: -** a pointer to a properly capitalized string. -** -** Side Effects: -** none. -*/ - -char * -capitalize(s) - register char *s; -{ - static char buf[50]; - register char *p; - - p = buf; - - for (;;) + p = q = s; + do { - while (!isalpha(*s) && *s != '\0') - *p++ = *s++; - if (*s == '\0') - break; - *p++ = toupper(*s); - s++; - while (isalpha(*s)) - *p++ = *s++; - } - - *p = '\0'; - return (buf); + c = *p++; + if (c == '\\') + c = *p++; + else if (c == '"') + continue; + *q++ = c; + } while (c != '\0'); } /* ** XALLOC -- Allocate memory and bitch wildly on failure. @@ -167,7 +99,6 @@ xalloc(sz) register int sz; { register char *p; - extern char *malloc(); p = malloc((unsigned) sz); if (p == NULL) @@ -222,6 +153,45 @@ copyplist(list, copycont) return (newvp); } /* +** COPYQUEUE -- copy address queue. +** +** This routine is the equivalent of newstr for address queues +** addresses marked with QDONTSEND aren't copied +** +** Parameters: +** addr -- list of address structures to copy. +** +** Returns: +** a copy of 'addr'. +** +** Side Effects: +** none. +*/ + +ADDRESS * +copyqueue(addr) + ADDRESS *addr; +{ + register ADDRESS *newaddr; + ADDRESS *ret; + register ADDRESS **tail = &ret; + + while (addr != NULL) + { + if (!bitset(QDONTSEND, addr->q_flags)) + { + newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS)); + STRUCTCOPY(*addr, *newaddr); + *tail = newaddr; + tail = &newaddr->q_next; + } + addr = addr->q_next; + } + *tail = NULL; + + return ret; +} + /* ** PRINTAV -- print argument vector. ** ** Parameters: @@ -264,7 +234,7 @@ char lower(c) register char c; { - return(isascii(c) && isupper(c) ? tolower(c) : c); + return((isascii(c) && isupper(c)) ? tolower(c) : c); } /* ** XPUTS -- put string doing control escapes. @@ -282,29 +252,67 @@ lower(c) xputs(s) register char *s; { - register char c; + register int c; + register struct metamac *mp; + extern struct metamac MetaMacros[]; if (s == NULL) { printf("<null>"); return; } - (void) putchar('"'); - while ((c = *s++) != '\0') + while ((c = (*s++ & 0377)) != '\0') { if (!isascii(c)) { + if (c == MATCHREPL || c == MACROEXPAND) + { + putchar('$'); + continue; + } + for (mp = MetaMacros; mp->metaname != '\0'; mp++) + { + if ((mp->metaval & 0377) == c) + { + printf("$%c", mp->metaname); + break; + } + } + if (mp->metaname != '\0') + continue; (void) putchar('\\'); c &= 0177; } - if (c < 040 || c >= 0177) + if (isprint(c)) + { + putchar(c); + continue; + } + + /* wasn't a meta-macro -- find another way to print it */ + switch (c) { + case '\0': + continue; + + case '\n': + c = 'n'; + break; + + case '\r': + c = 'r'; + break; + + case '\t': + c = 't'; + break; + + default: (void) putchar('^'); - c ^= 0100; + (void) putchar(c ^ 0100); + continue; } - (void) putchar(c); } - (void) putchar('"'); (void) fflush(stdout); } /* @@ -353,16 +361,30 @@ makelower(p) ** none. */ -buildfname(p, login, buf) - register char *p; +buildfname(gecos, login, buf) + register char *gecos; char *login; char *buf; { + register char *p; register char *bp = buf; + int l; + + if (*gecos == '*') + gecos++; - if (*p == '*') - p++; - while (*p != '\0' && *p != ',' && *p != ';' && *p != '%') + /* find length of final string */ + l = 0; + for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) + { + if (*p == '&') + l += strlen(login); + else + l++; + } + + /* now fill in buf */ + for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) { if (*p == '&') { @@ -370,10 +392,9 @@ buildfname(p, login, buf) *bp = toupper(*bp); while (*bp != '\0') bp++; - p++; } else - *bp++ = *p++; + *bp++ = *p; } *bp = '\0'; } @@ -383,29 +404,81 @@ buildfname(p, login, buf) ** Parameters: ** fn -- filename to check. ** uid -- uid to compare against. +** mustown -- to be safe, this uid must own the file. ** mode -- mode bits that must match. ** ** Returns: -** TRUE if fn exists, is owned by uid, and matches mode. -** FALSE otherwise. +** 0 if fn exists, is owned by uid, and matches mode. +** An errno otherwise. The actual errno is cleared. ** ** Side Effects: ** none. */ -bool -safefile(fn, uid, mode) +#ifndef S_IXOTH +# define S_IXOTH (S_IEXEC >> 6) +#endif + +#ifndef S_IXUSR +# define S_IXUSR (S_IEXEC) +#endif + +int +safefile(fn, uid, mustown, mode) char *fn; - int uid; + uid_t uid; + bool mustown; int mode; { + register char *p; struct stat stbuf; - if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid && - (stbuf.st_mode & mode) == mode) - return (TRUE); + if (tTd(54, 4)) + printf("safefile(%s, %d, %d, %o): ", fn, uid, mustown, mode); errno = 0; - return (FALSE); + + for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/') + { + *p = '\0'; + if (stat(fn, &stbuf) < 0 || + !bitset(stbuf.st_uid == uid ? S_IXUSR : S_IXOTH, + stbuf.st_mode)) + { + int ret = errno; + + if (ret == 0) + ret = EACCES; + if (tTd(54, 4)) + printf("[dir %s] %s\n", fn, errstring(ret)); + *p = '/'; + return ret; + } + } + + if (stat(fn, &stbuf) < 0) + { + int ret = errno; + + if (tTd(54, 4)) + printf("%s\n", errstring(ret)); + + errno = 0; + return ret; + } + if (stbuf.st_uid != uid || uid == 0 || !mustown) + mode >>= 6; + if (tTd(54, 4)) + printf("[uid %d, stat %o] ", stbuf.st_uid, stbuf.st_mode); + if ((stbuf.st_uid == uid || uid == 0 || !mustown) && + (stbuf.st_mode & mode) == mode) + { + if (tTd(54, 4)) + printf("OK\n"); + return 0; + } + if (tTd(54, 4)) + printf("EACCES\n"); + return EACCES; } /* ** FIXCRLF -- fix <CR><LF> in line. @@ -432,7 +505,7 @@ fixcrlf(line, stripnl) { register char *p; - p = index(line, '\n'); + p = strchr(line, '\n'); if (p == NULL) return; if (p > line && p[-1] == '\r') @@ -450,26 +523,66 @@ fixcrlf(line, stripnl) ** whatever), so this tries to get around it. */ +#ifndef O_ACCMODE +# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) +#endif + +struct omodes +{ + int mask; + int mode; + char *farg; +} OpenModes[] = +{ + O_ACCMODE, O_RDONLY, "r", + O_ACCMODE|O_APPEND, O_WRONLY, "w", + O_ACCMODE|O_APPEND, O_WRONLY|O_APPEND, "a", + O_TRUNC, 0, "w+", + O_APPEND, O_APPEND, "a+", + 0, 0, "r+", +}; + FILE * -dfopen(filename, mode) +dfopen(filename, omode, cmode) char *filename; - char *mode; + int omode; + int cmode; { register int tries; - register FILE *fp; + int fd; + register struct omodes *om; + struct stat st; + + for (om = OpenModes; om->mask != 0; om++) + if ((omode & om->mask) == om->mode) + break; for (tries = 0; tries < 10; tries++) { sleep((unsigned) (10 * tries)); errno = 0; - fp = fopen(filename, mode); - if (fp != NULL) + fd = open(filename, omode, cmode); + if (fd >= 0) break; if (errno != ENFILE && errno != EINTR) break; } - errno = 0; - return (fp); + if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode)) + { + int locktype; + + /* lock the file to avoid accidental conflicts */ + if ((omode & O_ACCMODE) != O_RDONLY) + locktype = LOCK_EX; + else + locktype = LOCK_SH; + (void) lockfile(fd, filename, locktype); + errno = 0; + } + if (fd < 0) + return NULL; + else + return fdopen(fd, om->farg); } /* ** PUTLINE -- put a line like fputs obeying SMTP conventions @@ -489,8 +602,6 @@ dfopen(filename, mode) ** output of l to fp. */ -# define SMTPLINELIM 990 /* maximum line length */ - putline(l, fp, m) register char *l; FILE *fp; @@ -500,39 +611,55 @@ putline(l, fp, m) register char svchar; /* strip out 0200 bits -- these can look like TELNET protocol */ - if (bitnset(M_LIMITS, m->m_flags)) + if (bitnset(M_7BITS, m->m_flags)) { - for (p = l; svchar = *p; ++p) - if (svchar & 0200) + for (p = l; (svchar = *p) != '\0'; ++p) + if (bitset(0200, svchar)) *p = svchar &~ 0200; } do { /* find the end of the line */ - p = index(l, '\n'); + p = strchr(l, '\n'); if (p == NULL) p = &l[strlen(l)]; + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%05d >>> ", getpid()); + /* check for line overflow */ - while ((p - l) > SMTPLINELIM && bitnset(M_LIMITS, m->m_flags)) + while (m->m_linelimit > 0 && (p - l) > m->m_linelimit) { - register char *q = &l[SMTPLINELIM - 1]; + register char *q = &l[m->m_linelimit - 1]; svchar = *q; *q = '\0'; if (l[0] == '.' && bitnset(M_XDOT, m->m_flags)) + { (void) putc('.', fp); + if (TrafficLogFile != NULL) + (void) putc('.', TrafficLogFile); + } fputs(l, fp); (void) putc('!', fp); fputs(m->m_eol, fp); + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%s!\n%05d >>> ", + l, getpid()); *q = svchar; l = q; } /* output last part */ if (l[0] == '.' && bitnset(M_XDOT, m->m_flags)) + { (void) putc('.', fp); + if (TrafficLogFile != NULL) + (void) putc('.', TrafficLogFile); + } + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%.*s\n", p - l, l); for ( ; l < p; ++l) (void) putc(*l, fp); fputs(m->m_eol, fp); @@ -559,15 +686,38 @@ xunlink(f) register int i; # ifdef LOG - if (LogLevel > 20) - syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f); -# endif LOG + if (LogLevel > 98) + syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f); +# endif /* LOG */ i = unlink(f); # ifdef LOG - if (i < 0 && LogLevel > 21) + if (i < 0 && LogLevel > 97) syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); -# endif LOG +# endif /* LOG */ +} + /* +** XFCLOSE -- close a file, doing logging as appropriate. +** +** Parameters: +** fp -- file pointer for the file to close +** a, b -- miscellaneous crud to print for debugging +** +** Returns: +** none. +** +** Side Effects: +** fp is closed. +*/ + +xfclose(fp, a, b) + FILE *fp; + char *a, *b; +{ + if (tTd(53, 99)) + printf("xfclose(%x) %s %s\n", fp, a, b); + if (fclose(fp) < 0 && tTd(53, 99)) + printf("xfclose FAILURE: %s\n", errstring(errno)); } /* ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. @@ -576,6 +726,8 @@ xunlink(f) ** buf -- place to put the input line. ** siz -- size of buf. ** fp -- file to read from. +** timeout -- the timeout before error occurs. +** during -- what we are trying to read (for error messages). ** ** Returns: ** NULL on error (including timeout). This will also leave @@ -589,31 +741,37 @@ xunlink(f) static jmp_buf CtxReadTimeout; char * -sfgets(buf, siz, fp) +sfgets(buf, siz, fp, timeout, during) char *buf; int siz; FILE *fp; + time_t timeout; + char *during; { register EVENT *ev = NULL; register char *p; static int readtimeout(); /* set the timeout */ - if (ReadTimeout != 0) + if (timeout != 0) { if (setjmp(CtxReadTimeout) != 0) { # ifdef LOG syslog(LOG_NOTICE, - "timeout waiting for input from %s\n", - RealHostName? RealHostName: "local"); + "timeout waiting for input from %s during %s\n", + CurHostName? CurHostName: "local", during); # endif errno = 0; - usrerr("451 timeout waiting for input"); + usrerr("451 timeout waiting for input during %s", + during); buf[0] = '\0'; +#ifdef XDEBUG + checkfd012(during); +#endif return (NULL); } - ev = setevent((time_t) ReadTimeout, readtimeout, 0); + ev = setevent(timeout, readtimeout, 0); } /* try to read */ @@ -634,10 +792,15 @@ sfgets(buf, siz, fp) if (p == NULL) { buf[0] = '\0'; + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid()); return (NULL); } - for (p = buf; *p != '\0'; p++) - *p &= ~0200; + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf); + if (SevenBit) + for (p = buf; *p != '\0'; p++) + *p &= ~0200; return (buf); } @@ -655,7 +818,9 @@ readtimeout() ** f -- file to read from. ** ** Returns: -** buf on success, NULL on error or EOF. +** input line(s) on success, NULL on error or EOF. +** This will normally be buf -- unless the line is too +** long, when it will be xalloc()ed. ** ** Side Effects: ** buf gets lines from f, with continuation lines (lines @@ -670,6 +835,7 @@ fgetfolded(buf, n, f) FILE *f; { register char *p = buf; + char *bp = buf; register int i; n--; @@ -685,8 +851,26 @@ fgetfolded(buf, n, f) i = '\r'; } } - if (--n > 0) - *p++ = i; + if (--n <= 0) + { + /* allocate new space */ + char *nbp; + int nn; + + nn = (p - bp); + if (nn < MEMCHUNKSIZE) + nn *= 2; + else + nn += MEMCHUNKSIZE; + nbp = xalloc(nn); + bcopy(bp, nbp, p - bp); + p = &nbp[p - bp]; + if (bp != buf) + free(bp); + bp = nbp; + n = nn - (p - bp); + } + *p++ = i; if (i == '\n') { LineNumber++; @@ -694,13 +878,13 @@ fgetfolded(buf, n, f) if (i != EOF) (void) ungetc(i, f); if (i != ' ' && i != '\t') - { - *--p = '\0'; - return (buf); - } + break; } } - return (NULL); + if (p == bp) + return (NULL); + *--p = '\0'; + return (bp); } /* ** CURTIME -- return current time. @@ -743,7 +927,7 @@ bool atobool(s) register char *s; { - if (*s == '\0' || index("tTyY", *s) != NULL) + if (*s == '\0' || strchr("tTyY", *s) != NULL) return (TRUE); return (FALSE); } @@ -850,3 +1034,71 @@ bitzerop(map) return (FALSE); return (TRUE); } + /* +** STRCONTAINEDIN -- tell if one string is contained in another +** +** Parameters: +** a -- possible substring. +** b -- possible superstring. +** +** Returns: +** TRUE if a is contained in b. +** FALSE otherwise. +*/ + +bool +strcontainedin(a, b) + register char *a; + register char *b; +{ + int l; + + l = strlen(a); + for (;;) + { + b = strchr(b, a[0]); + if (b == NULL) + return FALSE; + if (strncmp(a, b, l) == 0) + return TRUE; + b++; + } +} + /* +** CHECKFD012 -- check low numbered file descriptors +** +** File descriptors 0, 1, and 2 should be open at all times. +** This routine verifies that, and fixes it if not true. +** +** Parameters: +** where -- a tag printed if the assertion failed +** +** Returns: +** none +*/ + +checkfd012(where) + char *where; +{ +#ifdef XDEBUG + register int i; + struct stat stbuf; + + for (i = 0; i < 3; i++) + { + if (fstat(i, &stbuf) < 0) + { + /* oops.... */ + int fd; + + syserr("%s: fd %d not open", where, i); + fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666); + if (fd != i) + { + (void) dup2(fd, i); + (void) close(fd); + } + } + } +#endif XDEBUG +} diff --git a/usr.sbin/sendmail/src/version.c b/usr.sbin/sendmail/src/version.c index 6896049acf..b3cb8a5f64 100644 --- a/usr.sbin/sendmail/src/version.c +++ b/usr.sbin/sendmail/src/version.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,7 +33,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)version.c 5.67 (Berkeley) 5/10/91"; +static char sccsid[] = "@(#)version.c 8.3 (Berkeley) 7/13/93"; #endif /* not lint */ -char Version[] = "5.67"; +char Version[] = "8.3"; diff --git a/usr.sbin/sliplogin/Makefile b/usr.sbin/sliplogin/Makefile index e88e6f4a65..0b0bc91ff5 100644 --- a/usr.sbin/sliplogin/Makefile +++ b/usr.sbin/sliplogin/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.1 (Berkeley) 5/11/90 PROG= sliplogin -MAN8= sliplogin.0 +MAN8= sliplogin.8 BINOWN= root BINMODE=4555 diff --git a/usr.sbin/swapinfo/Makefile b/usr.sbin/swapinfo/Makefile index 130d6cfbe1..5bfd9088d6 100644 --- a/usr.sbin/swapinfo/Makefile +++ b/usr.sbin/swapinfo/Makefile @@ -1,7 +1,9 @@ # @(#)Makefile 5.5 (Berkeley) 4/23/91 PROG= swapinfo -SRCS= swapinfo.c devname.c +SRCS= swapinfo.c devname.c getbsize.c +.PATH: ${.CURDIR}/../../bin/df + DPADD= ${LIBMATH} ${LIBUTIL} LDADD= -lm -lutil BINGRP= kmem diff --git a/usr.sbin/swapinfo/swapinfo.c b/usr.sbin/swapinfo/swapinfo.c index 43cf7744b2..55936ded29 100644 --- a/usr.sbin/swapinfo/swapinfo.c +++ b/usr.sbin/swapinfo/swapinfo.c @@ -34,26 +34,43 @@ static struct nlist nl[] = {{"_swapmap"}, /* list of free swap areas */ #define VM_DMMAX 4 {""}}; +char *getbsize __P((char *, int *, long *)); +void usage __P((void)); +int kflag; main (argc, argv) int argc; char **argv; { int i, total_avail, total_free, total_partitions, *by_device, - use_k = 1, /* used as a divisor, so 1 == blocks, 2 == K */ - nswap, nswdev, dmmax; + nswap, nswdev, dmmax, ch; struct swdevt *swdevt; struct rlist head; + static long blocksize; + static int headerlen; + static char *header; + char **save; /* We are trying to be simple here: */ - if (argc > 1) - if (strcmp (argv [1], "-k") == 0) { - use_k = 2; - } else { - fprintf (stderr, "Usage: swapinfo [-k]\n"); - exit (1); + save = argv; + kflag = 0; + while ((ch = getopt(argc, argv, "k")) != EOF) + switch(ch) { + case 'k': + kflag = 1; + break; + case '?': + default: + usage(); } + argv += optind; + + if (!*argv) { + argv = save; + argv[0] = "."; + argv[1] = NULL; + } /* Open up /dev/kmem for reading. */ @@ -154,13 +171,13 @@ char **argv; swapmap = head.rl_next; } + header = getbsize("swapinfo", &headerlen, &blocksize); printf ("%-10s %10s %10s %10s %10s\n", - "Device", use_k == 1 ? "512-blks" : "Kilobytes", - "Used", "Available", "Capacity"); + "Device", header, "Used", "Available", "Capacity"); for (total_avail = total_partitions = i = 0; i < nswdev; i++) { printf ("/dev/%-5s %10d ", devname (swdevt [i].sw_dev, S_IFBLK), - swdevt [i].sw_nblks / use_k); + swdevt [i].sw_nblks / (blocksize/512)); /* * Don't report statistics for partitions which have not @@ -173,8 +190,8 @@ char **argv; total_partitions++; total_avail += swdevt [i].sw_nblks; printf ("%10d %10d %7.0f%%\n", - (swdevt [i].sw_nblks - by_device [i]) / use_k, - by_device [i] / use_k, + (swdevt [i].sw_nblks - by_device [i]) / (blocksize/512), + by_device [i] / (blocksize/512), (double) (swdevt [i].sw_nblks - by_device [i]) / (double) swdevt [i].sw_nblks * 100.0); @@ -188,11 +205,20 @@ char **argv; if (total_partitions > 1) printf ("%-10s %10d %10d %10d %7.0f%%\n", "Total", - total_avail / use_k, - (total_avail - total_free) / use_k, - total_free / use_k, + total_avail / (blocksize/512), + (total_avail - total_free) / (blocksize/512), + total_free / (blocksize/512), (double) (total_avail - total_free) / (double) total_avail * 100.0); exit (0); } + +void +usage() +{ + (void)fprintf(stderr, "usage: swapinfo [-k]\n"); + exit(1); +} + + diff --git a/usr.sbin/syslogd/Makefile b/usr.sbin/syslogd/Makefile index 61730bf4f9..df0d408814 100644 --- a/usr.sbin/syslogd/Makefile +++ b/usr.sbin/syslogd/Makefile @@ -3,8 +3,8 @@ PROG= syslogd SRCS= syslogd.c ttymsg.c .PATH: ${.CURDIR}/../../usr.bin/wall -MAN5= syslog.conf.0 -MAN8= syslogd.0 +MAN5= syslog.conf.5 +MAN8= syslogd.8 LDADD= -lutil .include <bsd.prog.mk> diff --git a/usr.sbin/timed/timed/Makefile b/usr.sbin/timed/timed/Makefile index 7b326bed63..37858e4cdd 100644 --- a/usr.sbin/timed/timed/Makefile +++ b/usr.sbin/timed/timed/Makefile @@ -7,7 +7,11 @@ SRCS= acksend.c candidate.c correct.c master.c networkdelta.c readmsg.c \ slave.c timed.c byteorder.c measure.c cksum.${MACHINE}.c DPADD= ${LIBUTIL} LDADD= -lutil -MAN8= timed.0 +MAN8= timed.8 +CLEANFILES+= cksum.i386.c + +cksum.i386.c: + ln -s $(.CURDIR)/cksum.tahoe.c cksum.i386.c .include "../../Makefile.inc" .include <bsd.prog.mk> diff --git a/usr.sbin/timed/timedc/Makefile b/usr.sbin/timed/timedc/Makefile index a796913c46..8701337507 100644 --- a/usr.sbin/timed/timedc/Makefile +++ b/usr.sbin/timed/timedc/Makefile @@ -2,10 +2,10 @@ PROG= timedc SRCS= cmds.c cmdtab.c timedc.c byteorder.c measure.c cksum.${MACHINE}.c -MAN8= timedc.0 +MAN8= timedc.8 BINOWN= root BINMODE=4555 -.PATH: ${.CURDIR}/../timed +.PATH: ${.CURDIR}/../timed ${.CURDIR}/../timed/obj .include "../../Makefile.inc" .include <bsd.prog.mk> diff --git a/usr.sbin/traceroute/Makefile b/usr.sbin/traceroute/Makefile index ce6557fb0e..54ee2fb406 100644 --- a/usr.sbin/traceroute/Makefile +++ b/usr.sbin/traceroute/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.1 (Berkeley) 5/11/90 PROG= traceroute -MAN8= traceroute.0 +MAN8= traceroute.8 BINOWN= root BINMODE=4555 diff --git a/usr.sbin/trpt/Makefile b/usr.sbin/trpt/Makefile index ce9a571327..c3abf7bc9e 100644 --- a/usr.sbin/trpt/Makefile +++ b/usr.sbin/trpt/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= trpt -MAN8= trpt.0 +MAN8= trpt.8 BINGRP= kmem BINMODE=2555 diff --git a/usr.sbin/trpt/trpt.8 b/usr.sbin/trpt/trpt.8 index f1d0e6b3ca..3082af3c34 100644 --- a/usr.sbin/trpt/trpt.8 +++ b/usr.sbin/trpt/trpt.8 @@ -125,7 +125,7 @@ core file other than the default, the last two arguments may be used to supplant the defaults. .Sh FILES .Bl -tag -width /dev/kmem -compact -.It Pa /vmunix +.It Pa /386bsd .It Pa /dev/kmem .El .Sh SEE ALSO diff --git a/usr.sbin/trsp/Makefile b/usr.sbin/trsp/Makefile index 08d524a5be..9a4a805c8f 100644 --- a/usr.sbin/trsp/Makefile +++ b/usr.sbin/trsp/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.3 (Berkeley) 5/11/90 PROG= trsp -MAN8= trsp.0 +MAN8= trsp.8 BINGRP= kmem BINMODE=2555 diff --git a/usr.sbin/trsp/trsp.8 b/usr.sbin/trsp/trsp.8 index c171616547..278e3f41e2 100644 --- a/usr.sbin/trsp/trsp.8 +++ b/usr.sbin/trsp/trsp.8 @@ -116,7 +116,7 @@ core file other than the default, the last two arguments may be used to supplant the defaults. .Sh FILES .Bl -tag -width /dev/kmem -compact -.It Pa /vmunix +.It Pa /386bsd .It Pa /dev/kmem .El .Sh SEE ALSO diff --git a/usr.sbin/update/Makefile b/usr.sbin/update/Makefile index c065f24a59..5f29640740 100644 --- a/usr.sbin/update/Makefile +++ b/usr.sbin/update/Makefile @@ -1,7 +1,7 @@ # @(#)Makefile 5.5 (Berkeley) 6/29/90 PROG= update -MAN8= update.0 +MAN8= update.8 LDADD= -lutil .include <bsd.prog.mk> diff --git a/usr.sbin/vipw/Makefile b/usr.sbin/vipw/Makefile index 0ca11d1bb1..f0d690684f 100644 --- a/usr.sbin/vipw/Makefile +++ b/usr.sbin/vipw/Makefile @@ -2,6 +2,6 @@ PROG= vipw SRCS= pw_util.c vipw.c -MAN8= vipw.0 +MAN8= vipw.8 .include <bsd.prog.mk> diff --git a/usr.sbin/vipw/vipw.8 b/usr.sbin/vipw/vipw.8 index d82dd617b5..ee41f9f3ed 100644 --- a/usr.sbin/vipw/vipw.8 +++ b/usr.sbin/vipw/vipw.8 @@ -65,7 +65,7 @@ the edit session. Once the information has been verified, .Nm vipw uses -.Xr mkpasswd 8 +.Xr pwd_mkdb 8 to update the user database. This is run in the background, and, at very large sites could take several minutes. Until this update is completed, the password file is unavailable for other updates @@ -85,7 +85,7 @@ will be invoked instead of the default editor .Xr passwd 1 , .Xr passwd 5 , .Xr adduser 8 , -.Xr mkpasswd 8 +.Xr pwd_mkdb 8 .Sh HISTORY The .Nm -- 2.20.1